/amd64 20000000775 sys sys 1371503016 0 /amd64/include 20000000775 sys sys 1371503016 0 /amd64/include/ape 20000000775 sys sys 1369332215 0 /amd64/include/ape/float.h 664 sys sys 1364220488 1820 #ifndef __FLOAT #define __FLOAT /* IEEE, default rounding */ #define FLT_ROUNDS 1 #define FLT_RADIX 2 #define FLT_DIG 6 #define FLT_EPSILON 1.19209290e-07 #define FLT_MANT_DIG 24 #define FLT_MAX 3.40282347e+38 #define FLT_MAX_10_EXP 38 #define FLT_MAX_EXP 128 #define FLT_MIN 1.17549435e-38 #define FLT_MIN_10_EXP -37 #define FLT_MIN_EXP -125 #define DBL_DIG 15 #define DBL_EPSILON 2.2204460492503131e-16 #define DBL_MANT_DIG 53 #define DBL_MAX 1.797693134862315708145e+308 #define DBL_MAX_10_EXP 308 #define DBL_MAX_EXP 1024 #define DBL_MIN 2.225073858507201383090233e-308 #define DBL_MIN_10_EXP -307 #define DBL_MIN_EXP -1021 #define LDBL_MANT_DIG DBL_MANT_DIG #define LDBL_EPSILON DBL_EPSILON #define LDBL_DIG DBL_DIG #define LDBL_MIN_EXP DBL_MIN_EXP #define LDBL_MIN DBL_MIN #define LDBL_MIN_10_EXP DBL_MIN_10_EXP #define LDBL_MAX_EXP DBL_MAX_EXP #define LDBL_MAX DBL_MAX #define LDBL_MAX_10_EXP DBL_MAX_10_EXP typedef union FPdbleword FPdbleword; union FPdbleword { double x; struct { /* little endian */ long lo; long hi; }; }; #ifdef _RESEARCH_SOURCE /* define stuff needed for floating conversion */ #define IEEE_8087 1 #define Sudden_Underflow 1 #endif #ifdef _PLAN9_SOURCE /* MXCSR */ /* fcr */ #define FPFTZ (1<<15) /* amd64 */ #define FPINEX (1<<12) #define FPUNFL (1<<11) #define FPOVFL (1<<10) #define FPZDIV (1<<9) #define FPDNRM (1<<8) /* amd64 */ #define FPINVAL (1<<7) #define FPDAZ (1<<6) /* amd64 */ #define FPRNR (0<<13) #define FPRZ (3<<13) #define FPRPINF (2<<13) #define FPRNINF (1<<13) #define FPRMASK (3<<13) #define FPPEXT 0 #define FPPSGL 0 #define FPPDBL 0 #define FPPMASK 0 /* fsr */ #define FPAINEX (1<<5) #define FPAUNFL (1<<4) #define FPAOVFL (1<<3) #define FPAZDIV (1<<2) #define FPADNRM (1<<1) /* not in plan 9 */ #define FPAINVAL (1<<0) #endif #endif /* __FLOAT */ /amd64/include/ape/inttypes.h 664 bootes sys 1368493469 404 #ifndef _SUSV2_SOURCE #error "inttypes.h is SUSV2" #endif #ifndef _INTTYPES_H_ #define _INTTYPES_H_ 1 typedef char int8_t; typedef short int16_t; typedef int int32_t; typedef long long int64_t; typedef unsigned char uint8_t; typedef unsigned short uint16_t; typedef unsigned int uint32_t; typedef unsigned long long uint64_t; typedef long long intptr_t; typedef unsigned long long uintptr_t; #endif /amd64/include/ape/math.h 664 sys sys 1364220488 2216 #ifndef __MATH #define __MATH #pragma lib "/$M/lib/ape/libap.a" /* a HUGE_VAL appropriate for IEEE double-precision */ /* the correct value, 1.797693134862316e+308, causes a ken overflow */ #define HUGE_VAL 1.79769313486231e+308 #ifdef __cplusplus extern "C" { #endif extern double acos(double); extern double asin(double); extern double atan(double); extern double atan2(double, double); extern double cos(double); extern double hypot(double, double); extern double sin(double); extern double tan(double); extern double cosh(double); extern double sinh(double); extern double tanh(double); extern double exp(double); extern double frexp(double, int *); extern double ldexp(double, int); extern double log(double); extern double log10(double); extern double modf(double, double *); extern double pow(double, double); extern double sqrt(double); extern double ceil(double); extern double fabs(double); extern double floor(double); extern double fmod(double, double); extern double NaN(void); extern int isNaN(double); extern double Inf(int); extern int isInf(double, int); #ifdef _RESEARCH_SOURCE /* does >> treat left operand as unsigned ? */ #define Unsigned_Shifts 1 #define M_E 2.7182818284590452354 /* e */ #define M_LOG2E 1.4426950408889634074 /* log 2e */ #define M_LOG10E 0.43429448190325182765 /* log 10e */ #define M_LN2 0.69314718055994530942 /* log e2 */ #define M_LN10 2.30258509299404568402 /* log e10 */ #define M_PI 3.14159265358979323846 /* pi */ #define M_PI_2 1.57079632679489661923 /* pi/2 */ #define M_PI_4 0.78539816339744830962 /* pi/4 */ #define M_1_PI 0.31830988618379067154 /* 1/pi */ #define M_2_PI 0.63661977236758134308 /* 2/pi */ #define M_2_SQRTPI 1.12837916709551257390 /* 2/sqrt(pi) */ #define M_SQRT2 1.41421356237309504880 /* sqrt(2) */ #define M_SQRT1_2 0.70710678118654752440 /* 1/sqrt(2) */ extern double hypot(double, double); extern double erf(double); extern double erfc(double); extern double j0(double); extern double y0(double); extern double j1(double); extern double y1(double); extern double jn(int, double); extern double yn(int, double); #endif #ifdef __cplusplus } #endif #define isnan(x) isNaN(x) #define isinf(x) isInf(x, 0) #endif /* __MATH */ /amd64/include/ape/stdarg.h 664 sys sys 1364220488 443 #ifndef __STDARG #define __STDARG typedef char *va_list; #define va_start(list, start) list = (sizeof(start)<8 ? (char *)((long long *)&(start)+1) : \ (char *)(&(start)+1)) #define va_end(list) #define va_arg(list, mode)\ ((sizeof(mode) == 1)?\ ((mode*)(list += 8))[-8]:\ (sizeof(mode) == 2)?\ ((mode*)(list += 8))[-4]:\ (sizeof(mode) == 4)?\ ((mode*)(list += 8))[-2]:\ ((mode*)(list += sizeof(mode)))[-1]) #endif /* __STDARG */ /amd64/include/ape/ureg.h 664 sys sys 1364220488 883 #ifndef __UREG_H #define __UREG_H #if !defined(_PLAN9_SOURCE) This header file is an extension to ANSI/POSIX #endif struct Ureg { unsigned long long ax; unsigned long long bx; unsigned long long cx; unsigned long long dx; unsigned long long si; unsigned long long di; unsigned long long bp; unsigned long long r8; unsigned long long r9; unsigned long long r10; unsigned long long r11; unsigned long long r12; unsigned long long r13; unsigned long long r14; unsigned long long r15; unsigned short ds; unsigned short es; unsigned short fs; unsigned short gs; unsigned long long type; unsigned long long error; /* error code (or zero) */ unsigned long long ip; /* pc */ unsigned long long cs; /* old context */ unsigned long long flags; /* old flags */ unsigned long long sp; /* sp */ unsigned long long ss; /* old stack segment */ }; #endif /386 20000000775 sys sys 1367802431 0 /386/include 20000000775 sys sys 1367535560 0 /386/include/ape 20000000775 sys sys 1369332140 0 /386/include/ape/float.h 664 sys sys 969656988 1643 #ifndef __FLOAT #define __FLOAT /* IEEE, default rounding */ #define FLT_ROUNDS 1 #define FLT_RADIX 2 #define FLT_DIG 6 #define FLT_EPSILON 1.19209290e-07 #define FLT_MANT_DIG 24 #define FLT_MAX 3.40282347e+38 #define FLT_MAX_10_EXP 38 #define FLT_MAX_EXP 128 #define FLT_MIN 1.17549435e-38 #define FLT_MIN_10_EXP -37 #define FLT_MIN_EXP -125 #define DBL_DIG 15 #define DBL_EPSILON 2.2204460492503131e-16 #define DBL_MANT_DIG 53 #define DBL_MAX 1.797693134862315708145e+308 #define DBL_MAX_10_EXP 308 #define DBL_MAX_EXP 1024 #define DBL_MIN 2.225073858507201383090233e-308 #define DBL_MIN_10_EXP -307 #define DBL_MIN_EXP -1021 #define LDBL_MANT_DIG DBL_MANT_DIG #define LDBL_EPSILON DBL_EPSILON #define LDBL_DIG DBL_DIG #define LDBL_MIN_EXP DBL_MIN_EXP #define LDBL_MIN DBL_MIN #define LDBL_MIN_10_EXP DBL_MIN_10_EXP #define LDBL_MAX_EXP DBL_MAX_EXP #define LDBL_MAX DBL_MAX #define LDBL_MAX_10_EXP DBL_MAX_10_EXP typedef union FPdbleword FPdbleword; union FPdbleword { double x; struct { /* little endian */ long lo; long hi; }; }; #ifdef _RESEARCH_SOURCE /* define stuff needed for floating conversion */ #define IEEE_8087 1 #define Sudden_Underflow 1 #endif #ifdef _PLAN9_SOURCE /* FCR */ #define FPINEX (1<<5) #define FPOVFL (1<<3) #define FPUNFL ((1<<4)|(1<<1)) #define FPZDIV (1<<2) #define FPRNR (0<<10) #define FPRZ (3<<10) #define FPRPINF (2<<10) #define FPRNINF (1<<10) #define FPRMASK (3<<10) #define FPPEXT (3<<8) #define FPPSGL (0<<8) #define FPPDBL (2<<8) #define FPPMASK (3<<8) /* FSR */ #define FPAINEX FPINEX #define FPAOVFL FPOVFL #define FPAUNFL FPUNFL #define FPAZDIV FPZDIV #endif #endif /* __FLOAT */ /386/include/ape/inttypes.h 664 bootes sys 1368499379 394 #ifndef _SUSV2_SOURCE #error "inttypes.h is SUSV2" #endif #ifndef _INTTYPES_H_ #define _INTTYPES_H_ 1 typedef char int8_t; typedef short int16_t; typedef int int32_t; typedef long long int64_t; typedef unsigned char uint8_t; typedef unsigned short uint16_t; typedef unsigned int uint32_t; typedef unsigned long long uint64_t; typedef long intptr_t; typedef unsigned long uintptr_t; #endif /386/include/ape/math.h 664 sys sys 1362432590 2216 #ifndef __MATH #define __MATH #pragma lib "/$M/lib/ape/libap.a" /* a HUGE_VAL appropriate for IEEE double-precision */ /* the correct value, 1.797693134862316e+308, causes a ken overflow */ #define HUGE_VAL 1.79769313486231e+308 #ifdef __cplusplus extern "C" { #endif extern double acos(double); extern double asin(double); extern double atan(double); extern double atan2(double, double); extern double cos(double); extern double hypot(double, double); extern double sin(double); extern double tan(double); extern double cosh(double); extern double sinh(double); extern double tanh(double); extern double exp(double); extern double frexp(double, int *); extern double ldexp(double, int); extern double log(double); extern double log10(double); extern double modf(double, double *); extern double pow(double, double); extern double sqrt(double); extern double ceil(double); extern double fabs(double); extern double floor(double); extern double fmod(double, double); extern double NaN(void); extern int isNaN(double); extern double Inf(int); extern int isInf(double, int); #ifdef _RESEARCH_SOURCE /* does >> treat left operand as unsigned ? */ #define Unsigned_Shifts 1 #define M_E 2.7182818284590452354 /* e */ #define M_LOG2E 1.4426950408889634074 /* log 2e */ #define M_LOG10E 0.43429448190325182765 /* log 10e */ #define M_LN2 0.69314718055994530942 /* log e2 */ #define M_LN10 2.30258509299404568402 /* log e10 */ #define M_PI 3.14159265358979323846 /* pi */ #define M_PI_2 1.57079632679489661923 /* pi/2 */ #define M_PI_4 0.78539816339744830962 /* pi/4 */ #define M_1_PI 0.31830988618379067154 /* 1/pi */ #define M_2_PI 0.63661977236758134308 /* 2/pi */ #define M_2_SQRTPI 1.12837916709551257390 /* 2/sqrt(pi) */ #define M_SQRT2 1.41421356237309504880 /* sqrt(2) */ #define M_SQRT1_2 0.70710678118654752440 /* 1/sqrt(2) */ extern double hypot(double, double); extern double erf(double); extern double erfc(double); extern double j0(double); extern double y0(double); extern double j1(double); extern double y1(double); extern double jn(int, double); extern double yn(int, double); #endif #ifdef __cplusplus } #endif #define isnan(x) isNaN(x) #define isinf(x) isInf(x, 0) #endif /* __MATH */ /386/include/ape/stdarg.h 664 sys sys 1279226202 403 #ifndef __STDARG #define __STDARG typedef char *va_list; #define va_start(list, start) list = (sizeof(start)<4 ? (char *)((int *)&(start)+1) : \ (char *)(&(start)+1)) #define va_end(list) #define va_arg(list, mode)\ ((sizeof(mode) == 1)?\ ((list += 4), (mode*)list)[-4]:\ (sizeof(mode) == 2)?\ ((list += 4), (mode*)list)[-2]:\ ((list += sizeof(mode)), (mode*)list)[-1]) #endif /* __STDARG */ /386/include/ape/ureg.h 664 sys sys 944946041 812 #ifndef __UREG_H #define __UREG_H #if !defined(_PLAN9_SOURCE) This header file is an extension to ANSI/POSIX #endif struct Ureg { unsigned long di; /* general registers */ unsigned long si; /* ... */ unsigned long bp; /* ... */ unsigned long nsp; unsigned long bx; /* ... */ unsigned long dx; /* ... */ unsigned long cx; /* ... */ unsigned long ax; /* ... */ unsigned long gs; /* data segments */ unsigned long fs; /* ... */ unsigned long es; /* ... */ unsigned long ds; /* ... */ unsigned long trap; /* trap type */ unsigned long ecode; /* error code (or zero) */ unsigned long pc; /* pc */ unsigned long cs; /* old context */ unsigned long flags; /* old flags */ union { unsigned long usp; unsigned long sp; }; unsigned long ss; /* old stack segment */ }; #endif /sys 20000000775 sys sys 1371503015 0 /sys/src 20000000775 sys sys 1364220500 0 /sys/src/ape 20000000775 sys sys 1367862840 0 /sys/src/ape/9src 20000000775 sys sys 1369425056 0 /sys/src/ape/9src/mkfile 664 sys sys 1367613436 123 #include #include typedef struct Mode Mode; struct Mode { char* name; int bit; }; Mode ou[] = { "opost", OPOST, "olcuc", OLCUC, "onlcr", ONLCR, "ocrnl", OCRNL, "onocr", ONOCR, "onlret", ONLRET, "ofill", OFILL, "ofdel", OFDEL, 0 }; Mode in[] = { "brkint", BRKINT, "icrnl", ICRNL, "ignbrk", IGNBRK, "igncr", IGNCR, "ignpar", IGNPAR, "inlcr", INLCR, "inpck", INPCK, "istrip", ISTRIP, "ixoff", IXOFF, "ixon", IXON, "parmrk", PARMRK, 0 }; Mode lo[] = { "echo", ECHO, "echoe", ECHOE, "echok", ECHOK, "echonl", ECHONL, "icanon", ICANON, "iexten", IEXTEN, "isig", ISIG, "noflsh", NOFLSH, "tostop", TOSTOP, 0 }; Mode cc[] = { "eof", VEOF, "eol", VEOL, "erase", VERASE, "intr", VINTR, "kill", VKILL, "min", VMIN, "quit", VQUIT, "susp", VSUSP, "time", VTIME, "start", VSTART, "stop", VSTOP, 0, }; int getmode(int, Termios*); int setmode(int, Termios*); char* ctlchar(char c) { static char buf[10]; if(c == 0x7f) return "DEL"; if(c == 0) return "NUL"; if(c < 32) { buf[0] = '^'; buf[1] = '@'+c; buf[2] = '\0'; return buf; } buf[0] = c; buf[1] = '\0'; return buf; } void showmode(Termios *t) { int i; for(i = 0; cc[i].name; i++) { switch(cc[i].bit) { case VMIN: case VTIME: if(t->cc[i] != 0) print("%s %d ", cc[i].name, t->cc[i]); break; default: print("%s %s ", cc[i].name, ctlchar(t->cc[i])); break; } } print("\n"); for(i = 0; ou[i].name; i++) if(ou[i].bit & t->oflag) print("%s ", ou[i].name); for(i = 0; in[i].name; i++) if(in[i].bit & t->iflag) print("%s ", in[i].name); print("\n"); for(i = 0; lo[i].name; i++) if(lo[i].bit & t->lflag) print("%s ", lo[i].name); print("\n"); } int setreset(char *mode, int *bits, Mode *t) { int i, clr; clr = 0; if(mode[0] == '-') { mode++; clr = 1; } for(i = 0; t[i].name; i++) { if(strcmp(mode, t[i].name) == 0) { if(clr) *bits &= ~t[i].bit; else *bits |= t[i].bit; return 1; } } return 0; } int ccname(char *name) { int i; for(i = 0; cc[i].name; i++) if(strcmp(cc[i].name, name) == 0) return i; return -1; } void main(int argc, char **argv) { Termios t; int i, stdin, wmo, cc; /* Try and get a seek pointer */ stdin = open("/fd/0", ORDWR); if(stdin < 0) stdin = 0; if(getmode(stdin, &t) < 0) { fprint(2, "stty: tiocget %r\n"); exits("1"); } if(argc < 2) { fprint(2, "usage: stty [-a|-g] modes...\n"); exits("1"); } wmo = 0; for(i = 1; i < argc; i++) { if(strcmp(argv[i], "-a") == 0) { showmode(&t); continue; } if(setreset(argv[i], &t.iflag, in)) { wmo++; continue; } if(setreset(argv[i], &t.lflag, lo)) { wmo++; continue; } if(setreset(argv[i], &t.oflag, ou)) { wmo++; continue; } cc = ccname(argv[i]); if(cc != -1 && i+1 < argc) { wmo++; t.cc[cc] = argv[++i][0]; continue; } fprint(2, "stty: bad option/mode %s\n", argv[i]); exits("1"); } if(wmo) { if(setmode(stdin, &t) < 0) { fprint(2, "stty: cant set mode %r\n"); exits("1"); } } exits(0); } int setmode(int fd, Termios *t) { int n, i; char buf[256]; n = sprint(buf, "IOW %4.4ux %4.4ux %4.4ux %4.4ux ", t->iflag, t->oflag, t->cflag, t->lflag); for(i = 0; i < NCCS; i++) n += sprint(buf+n, "%2.2ux ", t->cc[i]); if(seek(fd, -2, 0) != -2) return -1; n = write(fd, buf, n); if(n < 0) return -1; return 0; } /* * Format is: IOR iiii oooo cccc llll xx xx xx xx ... */ int getmode(int fd, Termios *t) { int n; char buf[256]; if(seek(fd, -2, 0) != -2) return -1; n = read(fd, buf, 57); if(n < 0) return -1; t->iflag = strtoul(buf+4, 0, 16); t->oflag = strtoul(buf+9, 0, 16); t->cflag = strtoul(buf+14, 0, 16); t->lflag = strtoul(buf+19, 0, 16); for(n = 0; n < NCCS; n++) t->cc[n] = strtoul(buf+24+(n*3), 0, 16); return 0; } /sys/src/ape/9src/tar.c 664 sys sys 1367613436 2367 /* * Attempt at emulation of Unix tar by calling Plan 9 tar. * * The differences from Plan 9 tar are: * In the absence of an "f" flag, the file /dev/tape is used. * An "f" flag with argument "-" causes use of stdin/stdout * by passing no "f" flag (nor argument) to Plan 9 tar. * By default, the "T" flag is passed to Plan 9 tar. * The "m" flag to this tar inhibits this behavior. */ #include #include void usage(void) { fprint(2, "usage: ape/tar [crtx][vfm] [args...] [file...]\n"); exits("usage"); } void main(int argc, char **argv) { int i, j, verb, vflag, fflag, Tflag, nargc; char *p, *file, **nargv, *cpu, flagbuf[10], execbuf[128]; Waitmsg *w; argv++, argc--; if(argc < 1) usage(); p = argv[0]; argv++, argc--; if(*p == '-') p++; if(strchr("crtx", *p) == nil) usage(); verb = *p++; /* unix defaults */ fflag = 1; file = "/dev/tape"; Tflag = 1; vflag = 0; for(; *p; p++) { switch(*p) { default: usage(); case 'v': vflag = 1; break; case 'f': if(argc <= 0) usage(); fflag = 1; file = argv[0]; argv++, argc--; if(strcmp(file, "-") == 0) { /* * plan9 doesn't know about "-" meaning stdin/stdout, * but it's the default, * so rewrite to not use f flag at all. */ file = nil; fflag = 0; } break; case 'm': Tflag = 0; break; case 'p': /* pretend nothing's wrong */ break; } } nargc = 1 + 1 + fflag + argc + 1; nargv = malloc(sizeof(char*) * nargc); if(nargv == nil) { fprint(2, "ape/tar: out of memory\n"); exits("memory"); } cpu = getenv("cputype"); if(cpu == nil) { fprint(2, "ape/tar: need cputype environment variable set\n"); exits("cputype"); } snprint(execbuf, sizeof execbuf, "/%s/bin/tar", cpu); nargv[0] = "tar"; sprint(flagbuf, "%c%s%s%s", verb, vflag ? "v" : "", Tflag ? "T" : "", fflag ? "f" : ""); nargv[1] = flagbuf; i = 2; if(fflag) nargv[i++] = file; for(j=0; jmsg[0] == '\0') exits(nil); else exits(w->msg); } assert(0); } /sys/src/ape/9src/tty.h 664 sys sys 1367613436 2407 /* input modes */ #define BRKINT 0x001 #define ICRNL 0x002 #define IGNBRK 0x004 #define IGNCR 0x008 #define IGNPAR 0x010 #define INLCR 0x020 #define INPCK 0x040 #define ISTRIP 0x080 #define IXOFF 0x100 #define IXON 0x200 #define PARMRK 0x400 /* output modes */ #define OPOST 0000001 #define OLCUC 0000002 #define ONLCR 0000004 #define OCRNL 0000010 #define ONOCR 0000020 #define ONLRET 0000040 #define OFILL 0000100 #define OFDEL 0000200 #define NLDLY 0000400 #define NL0 0 #define NL1 0000400 #define CRDLY 0003000 #define CR0 0 #define CR1 0001000 #define CR2 0002000 #define CR3 0003000 #define TABDLY 0014000 #define TAB0 0 #define TAB1 0004000 #define TAB2 0010000 #define TAB3 0014000 #define BSDLY 0020000 #define BS0 0 #define BS1 0020000 #define VTDLY 0040000 #define VT0 0 #define VT1 0040000 #define FFDLY 0100000 #define FF0 0 #define FF1 0100000 /* control modes */ #define CLOCAL 0x001 #define CREAD 0x002 #define CSIZE 0x01C #define CS5 0x004 #define CS6 0x008 #define CS7 0x00C #define CS8 0x010 #define CSTOPB 0x020 #define HUPCL 0x040 #define PARENB 0x080 #define PARODD 0x100 /* local modes */ #define ECHO 0x001 #define ECHOE 0x002 #define ECHOK 0x004 #define ECHONL 0x008 #define ICANON 0x010 #define IEXTEN 0x020 #define ISIG 0x040 #define NOFLSH 0x080 #define TOSTOP 0x100 /* control characters */ #define VEOF 0 #define VEOL 1 #define VERASE 2 #define VINTR 3 #define VKILL 4 #define VMIN 5 #define VQUIT 6 #define VSUSP 7 #define VTIME 8 #define VSTART 9 #define VSTOP 10 #define NCCS 11 /* baud rates */ #define B0 0 #define B50 1 #define B75 2 #define B110 3 #define B134 4 #define B150 5 #define B200 6 #define B300 7 #define B600 8 #define B1200 9 #define B1800 10 #define B2400 11 #define B4800 12 #define B9600 13 #define B19200 14 #define B38400 15 #define CESC '\\' #define CINTR 0177 /* DEL */ #define CQUIT 034 /* FS, cntl | */ #define CERASE 010 /* BS */ #define CKILL 025 /* cntl u */ #define CEOF 04 /* cntl d */ #define CSTART 021 /* cntl q */ #define CSTOP 023 /* cntl s */ #define CSWTCH 032 /* cntl z */ #define CEOL 000 #define CNSWTCH 0 /* optional actions for tcsetattr */ #define TCSANOW 1 #define TCSADRAIN 2 #define TCSAFLUSH 3 typedef struct Termios Termios; struct Termios { int iflag; /* input modes */ int oflag; /* output modes */ int cflag; /* control modes */ int lflag; /* local modes */ uchar cc[NCCS]; /* control characters */ }; /sys/src/ape/cmd 20000000775 sys sys 1371506136 0 /sys/src/ape/cmd/README 664 sys sys 1367613436 2953 This is an attempt to make the utilities specified in POSIX 1002.3 available, assuming /$objtype/ape/bin and /lib/rc/ape are bound to /bin before the regular bin directories. Here's a brief description of the status of these commands. EXECUTION ENVIRONMENT UTILITIES awk Plan 9 awk. system() uses rc instead of sh. basename POSIX conforming bc Plan 9 bc. cat Plan 9 cat. no -u option (for byte-at-at-time) cd shell builtins doesn't use $HOME or $CDPATH chgrp Plan 9 chgrp. no -R option (for recursive chgrp). only takes name, not number chmod Plan 9 chmod. no -R option (for recursive chmod). no s (setuid) and X (conditional x) perms. nonstandard a,l perms. chown Always prints 'Permission denied' and fails. cksum not implemented cmp Plan 9 cmp. nonstandard -L option no line number printed; hex instead of octal for bytes comm Plan 9 comm. command not implemented cp Plan 9 cp. no -R and -r (recursive), -i (interactive), -p (preserve) options nonstandard -z option cut not implemented date Plan 9 date. no format option nonstandard -n option dd Plan 9 dd. diff Plan 9 diff. can't have both files directories no -r (recursive) option -c instead of -c and -C for context dirname POSIX conforming echo Plan 9 echo ed Plan 9 ed nonstandard b,wq commands env not implemented expr V10 expr (seems to be like POSIX) false POSIX conforming find not implemented fold not implemented getconf not implemented getopts not implemented grep script calling Plan 9 grep -G s means q, should mean forget nonexistent files nonstandard 1,b,L,q options head not implemented id not implemented join not implemented kill V10 kill no -s signalname, no -l arg ln not implemented locale not implemented localedef not implemented logger not implemented logname not implemented lp Plan 9 lp ls Plan 9 ls mailx not implemented mkdir Plan 9 mkdir mkfifo not implemented mv Plan 9 mv nohup not implemented od not implemented paste not implemented pathchk not implemented pax implemented pr Plan 9 pr printf not implemented pwd Plan 9 pwd read shell builtin rm Plan 9 rm rmdir script no -p option sed v10 sed sh ksh93 -- POSIX compliant sleep Plan 9 sleep sort Plan 9 sort stty POSIX compliant (sort of) tail Plan 9 tail tee Plan 9 tee test Plan 9 test (POSIX compliant); copied as [ touch Plan 9 touch tr Plan 9 tr true POSIX compliant umask noop SOFTWARE DEVELOPMENT UTILITIES (OPTIONAL) ar script to call Plan9 ar, after arg conversion make V10 make strip not implemented C LANGUAGE DEVELOPMENT UTILITIES OPTION c89 script to APE environment cc (also avaiable as cc) lex Plan 9 lex yacc script to Plan 9 yacc General Bugs: The environment variables LANG, LC_ALL, LC_CTYPE, and LC_MESSAGES are ignored. The use of -- as an argument to stop option processing is generally not done. The many 'not implemented' functions will be implemented as scripts using them show up. /sys/src/ape/cmd/basename.c 664 sys sys 1367613436 734 #include #include #include void main(int argc, char **argv) { char *f, *b, *s; int n; if(argc < 2 || argc > 3){ fprintf(stderr, "Usage: basename string [suffix]\n"); exit(1); } s = argv[1]; b = s + strlen(s) - 1; while(b > s && *b == '/') b--; *++b = 0; if(b == s+1 && s[0] == '/') { printf("/"); exit(0); } /* now b is after last char of string, trailing slashes removed */ for(f = b; f >= s; f--) if(*f == '/'){ f++; break; } if(f < s) f = s; /* now f is first char after last remaining slash, or first char */ if(argc == 3){ n = strlen(argv[2]); if(n < b-f && strncmp(b-n, argv[2], n) == 0){ b -= n; *b = 0; } } printf("%s\n", f); exit(0); } /sys/src/ape/cmd/cc.c 664 sys sys 1367613436 7362 #include #include /* POSIX standard c89 standard options: -c, -D name[=val], -E (preprocess to stdout), -g, -L dir, -o outfile, -O, -s, -U name (and operands can have -l lib interspersed) nonstandard but specified options: -S (assembly language left in .s), -Wx,arg1[,arg2...] (pass arg(s) to phase x, where x is p (cpp) 0 (compiler), or l (loader) nonstandard options: -v (echo real commands to stdout as they execute) -A: turn on ANSI prototype warnings */ typedef struct Objtype { char *name; char *cc; char *ld; char *o; } Objtype; Objtype objtype[] = { {"68020", "2c", "2l", "2"}, {"arm", "5c", "5l", "5"}, {"amd64", "6c", "6l", "6"}, {"alpha", "7c", "7l", "7"}, {"386", "8c", "8l", "8"}, {"sparc", "kc", "kl", "k"}, {"power", "qc", "ql", "q"}, {"mips", "vc", "vl", "v"}, }; enum { Nobjs = (sizeof objtype)/(sizeof objtype[0]), Maxlist = 2000, }; typedef struct List { char *strings[Maxlist]; int n; } List; List srcs, objs, cpp, cc, ld, ldargs, srchlibs; int cflag, vflag, Eflag, Sflag, Aflag; char *allos = "2678kqv"; void append(List *, char *); char *changeext(char *, char *); void doexec(char *, List *); void dopipe(char *, List *, char *, List *); void fatal(char *); Objtype *findoty(void); void printlist(List *); char *searchlib(char *, char*); void main(int argc, char *argv[]) { char *s, *suf, *ccpath, *lib; char *oname; int haveoname = 0; int i, cppn, ccn; Objtype *ot; ot = findoty(); oname = "a.out"; append(&cpp, "cpp"); append(&cpp, "-D__STDC__=1"); /* ANSI says so */ append(&cpp, "-D_POSIX_SOURCE="); append(&cpp, "-N"); /* turn off standard includes */ append(&cc, ot->cc); append(&ld, ot->ld); append(&srchlibs, smprint("/%s/lib/ape", ot->name)); while(argc > 0) { ARGBEGIN { case 'c': cflag = 1; break; case 'l': lib = searchlib(ARGF(), ot->name); if(!lib) fprint(2, "cc: can't find library for -l\n"); else append(&objs, lib); break; case 'o': oname = ARGF(); haveoname = 1; if(!oname) fatal("cc: no -o argument"); break; case 'D': case 'I': case 'U': append(&cpp, smprint("-%c%s", ARGC(), ARGF())); break; case 'E': Eflag = 1; cflag = 1; break; case 's': case 'g': break; case 'L': lib = ARGF(); if(!lib) fprint(2, "cc: no -L argument\n"); else append(&srchlibs, lib); break; case 'N': case 'T': case 'w': append(&cc, smprint("-%c", ARGC())); break; case 'O': break; case 'W': s = ARGF(); if(s && s[1]==',') { switch (s[0]) { case 'p': append(&cpp, s+2); break; case '0': append(&cc, s+2); break; case 'l': append(&ldargs, s+2); break; default: fprint(2, "cc: pass letter after -W should be one of p0l; ignored\n"); } } else fprint(2, "cc: bad option after -W; ignored\n"); break; case 'v': vflag = 1; append(&ldargs, "-v"); break; case 'A': Aflag = 1; break; case 'S': Sflag = 1; break; default: fprint(2, "cc: flag -%c ignored\n", ARGC()); break; } ARGEND if(!Aflag) { append(&cc, "-J"); /* old/new decl mixture hack */ append(&cc, "-B"); /* turn off non-prototype warnings */ } if(argc > 0) { s = argv[0]; suf = utfrrune(s, '.'); if(suf) { suf++; if(strcmp(suf, "c") == 0) { append(&srcs, s); append(&objs, changeext(s, "o")); } else if(strcmp(suf, "o") == 0 || strcmp(suf, ot->o) == 0 || strcmp(suf, "a") == 0 || (suf[0] == 'a' && strcmp(suf+1, ot->o) == 0)) { append(&objs, s); } else if(utfrune(allos, suf[0]) != 0) { fprint(2, "cc: argument %s ignored: wrong architecture\n", s); } } } } if(objs.n == 0) fatal("no files to compile or load"); ccpath = smprint("/bin/%s", ot->cc); append(&cpp, smprint("-I/%s/include/ape", ot->name)); append(&cpp, "-I/sys/include/ape"); cppn = cpp.n; ccn = cc.n; for(i = 0; i < srcs.n; i++) { append(&cpp, srcs.strings[i]); if(Eflag) doexec("/bin/cpp", &cpp); else { if(Sflag) append(&cc, "-S"); else { append(&cc, "-o"); if (haveoname && cflag) append(&cc, oname); else append(&cc, changeext(srcs.strings[i], "o")); } dopipe("/bin/cpp", &cpp, ccpath, &cc); } cpp.n = cppn; cc.n = ccn; } if(!cflag) { append(&ld, "-o"); append(&ld, oname); for(i = 0; i < ldargs.n; i++) append(&ld, ldargs.strings[i]); for(i = 0; i < objs.n; i++) append(&ld, objs.strings[i]); append(&ld, smprint("/%s/lib/ape/libap.a", ot->name)); doexec(smprint("/bin/%s", ot->ld), &ld); if(objs.n == 1) remove(objs.strings[0]); } exits(0); } char * searchlib(char *s, char *objtype) { char *l; int i; if(!s) return 0; for(i = srchlibs.n-1; i>=0; i--) { l = smprint("%s/lib%s.a", srchlibs.strings[i], s); if(access(l, 0) >= 0) return l; } if(s[1] == 0) switch(s[0]) { case 'c': l = smprint("/%s/lib/ape/libap.a", objtype); break; case 'm': l = smprint("/%s/lib/ape/libap.a", objtype); break; case 'l': l = smprint("/%s/lib/ape/libl.a", objtype); break; case 'y': l = smprint("/%s/lib/ape/liby.a", objtype); break; default: l = 0; } else l = 0; return l; } void append(List *l, char *s) { if(l->n >= Maxlist-1) fatal("too many arguments"); l->strings[l->n++] = s; l->strings[l->n] = 0; } void doexec(char *c, List *a) { Waitmsg *w; if(vflag) { printlist(a); fprint(2, "\n"); } switch(fork()) { case -1: fatal("fork failed"); case 0: exec(c, a->strings); fatal("exec failed"); } if((w = wait()) == nil) fatal("wait failed"); if(w->msg[0]) fatal(smprint("%s: %s", a->strings[0], w->msg)); free(w); } void dopipe(char *c1, List *a1, char *c2, List *a2) { Waitmsg *w; int pid1, got; int fd[2]; if(vflag) { printlist(a1); fprint(2, " | "); printlist(a2); fprint(2, "\n"); } if(pipe(fd) < 0) fatal("pipe failed"); switch((pid1 = fork())) { case -1: fatal("fork failed"); case 0: dup(fd[0], 0); close(fd[0]); close(fd[1]); exec(c2, a2->strings); fatal("exec failed"); } switch(fork()) { case -1: fatal("fork failed"); case 0: close(0); dup(fd[1], 1); close(fd[0]); close(fd[1]); exec(c1, a1->strings); fatal("exec failed"); } close(fd[0]); close(fd[1]); for(got = 0; got < 2; got++) { if((w = wait()) == nil) fatal("wait failed"); if(w->msg[0]) fatal(smprint("%s: %s", (w->pid == pid1) ? a1->strings[0] : a2->strings[0], w->msg)); free(w); } } Objtype * findoty(void) { char *o; Objtype *oty; o = getenv("objtype"); if(!o) fatal("no $objtype in environment"); for(oty = objtype; oty < &objtype[Nobjs]; oty++) if(strcmp(o, oty->name) == 0) return oty; fatal("unknown $objtype"); return 0; /* shut compiler up */ } void fatal(char *msg) { fprint(2, "cc: %s\n", msg); exits(msg); } /* src ends in .something; return copy of basename with .ext added */ char * changeext(char *src, char *ext) { char *b, *e, *ans; b = utfrrune(src, '/'); if(b) b++; else b = src; e = utfrrune(src, '.'); if(!e) return 0; *e = 0; ans = smprint("%s.%s", b, ext); *e = '.'; return ans; } void printlist(List *l) { int i; for(i = 0; i < l->n; i++) { fprint(2, "%s", l->strings[i]); if(i < l->n - 1) fprint(2, " "); } } /sys/src/ape/cmd/diff 20000000775 sys sys 1371506136 0 /sys/src/ape/cmd/diff/COPYING 664 sys sys 1367613436 17982 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave, Cambridge, MA 02139, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS Appendix: How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy 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 of the License, 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., 675 Mass Ave, Cambridge, MA 02139, USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. /sys/src/ape/cmd/diff/ChangeLog 664 sys sys 1367613436 66704 Sat Oct 1 05:24:19 1994 Paul Eggert * Version 2.7 released. * configure.in (AC_HEADER_SYS_WAIT): Add. (AC_CHECK_HEADERS): Remove sys/wait.h. (AC_CHECK_FUNCS): Add tmpnam. * system.h (, WEXITSTATUS): Use simpler scheme now that HAVE_SYS_WAIT_H is not set on hosts that are incompatible with Posix applications. * util.c (dir_file_pathname): Use filename_lastdirchar not strrchr. * sdiff.c (expand_name): Likewise. (private_tempnam): Use tmpnam if HAVE_TMPNAM; this simplifies porting. (exists, letters): Omit if HAVE_TMPNAM. * diff3.c (read_diff): If STAT_BLOCKSIZE yields zero, adjust it to a more reasonable value. Sat Sep 24 20:36:40 1994 Paul Eggert * sdiff.c (exists, private_tempname): Adopt latest GNU libc algorithm. (private_tempnam): Specialize for sdiff to avoid portability problems. Thu Sep 22 16:47:00 1994 Paul Eggert * configure.in (AC_ARG_PROGRAM): Added. (AC_OUTPUT): Add [date > stamp-h]. * Makefile.in (DEFAULT_EDITOR_PROGRAM, DIFF_PROGRAM, LIBOBJS, NULL_DEVICE, PR_PROGRAM, PROGRAMS): New variables. (check, stamp-h.in, cmp.o, util.o): New targets. (edit_program_name): New variable; replaces old binprefix method. (install, uninstall): Use it. (binprefix): Removed. (distfiles): Add stamp-h.in. (clean): Clean stamp-h. (config.hin, config.h): Use time stamp files. (cmp_o): Add $(LIBOBJS). (install): Install info files from srcdir if they're not in `.'. * cmp.c, io.c (word): Don't define if already defined. * comp.c (main): Use setmode, not open(..., O_BINARY); this gets stdin. Use NULL_DEVICE instead of "/dev/null". (cmp): Use %lu instead of %ld when it is more likely to be right. * diff.h (PR_FILE_NAME): Rename to PR_PROGRAM and move to Makefile.in, util.c. * diff3.c (main): Give proper diagnostic if too many labels were given. (read_diff): Use SYSTEM_QUOTE_ARG. * system.h: : Include if HAVE_STRING_H, too. : Include here. All includers changed. (CTYPE_DOMAIN, ISDIGIT, ISPRINT, ISSPACE, ISUPPER): New macros that work around common problems. (O_BINARY): Remove. (SYSTEM_QUOTE_ARG): New macros. * diff.c: Add comment. * util.c (PR_PROGRAM): Moved here from diff.h. (begin_output): Use SYSTEM_QUOTE_ARG. * io.c (read_files): Set mode to binary before returning 1. * sdiff.c (TMPDIR_ENV): New macro. (DEFAULT_EDITOR_PROGRAM): Renamed from DEFAULT_EDITOR for consistency. (expand_name): Change `isdir' to `is_dir' to avoid theoretical ctype namespace contamination. (main): Use SYSTEM_QUOTE_ARG. (private_tempnam): Don't access "/tmp" directly; use PVT_tmpdir. Tue Sep 13 18:46:43 1994 Paul Eggert * configure.in (AC_FUNC_MEMCHR): Remove. Autoconf didn't adopt this, since we need not worry about an old experimental library where memchr didn't work. (AC_FUNC_MEMCMP): Not needed, since we only test for equality. (AC_REPLACE_FUNCS): Add test for memchr. (AC_CHECK_FUNCS): Check for memchr, not memcpy, since it'll be cached. (AC_CHECK_HEADERS): Add string.h; regex.c uses on some old hosts. * system.h (memcmp): Define in terms of bcmp. Use HAVE_MEMCHR to test for all mem* routines. * Makefile.in (srcs): Remove memcmp.c. We use bcmp if memcmp doesn't work, since we only test for equality. Mon Sep 12 15:52:22 1994 Paul Eggert * configure.in (AC_CONFIG_HEADER): Rename config.h.in to config.hin. (AC_ISC_POSIX, AC_MINIX): Go back to these old names for Autoconf 2. (AC_CHECK_HEADERS): Remove now-redundant check for . (AC_CHECK_FUNCS): Check for strchr. (AC_FUNC_MEMCHR, AC_FUNC_MEMCMP, AC_CHECK_FUNCS): Use special-purpose macros when suitable. * memcmp.c: New file. * Makefile.in (CPPFLAGS, DEFS, CFLAGS, LDFLAGS, prefix, exec_prefix): Default to autoconf-specified strings. (COMPILE): Use the defaults. (srcs): Add memcmp.c. (distfiles): Rename config.h.in->config.hin, install.sh->install-sh. (Makefile, config.h, config.hin, config.status): Rework for compatibility with Autoconf 2. * io.c (binary_file_p): Assume non-broken memchr. * memchr.c: Assume compiler understands void *; otherwise we don't match GCC's internal declaration of memchr. * system.h: Use more modern autoconf approach to standard C headers. * version.c: Include , not "config.h". * diff.c, diff.h (ignore_some_line_changes): New variable; replaces `length_varies'. (line_end_char): Replace with '\n'; it wasn't being used consistently. * io.c (find_and_hash_each_line): Fix inconsistencies with -b -w -i and incomplete lines. Put incomplete lines into their own bucket. This means line_cmp no longer needs line length arguments, and equivalence classes' line lengths no longer need to include \n. Invoke line_cmp only if ignore_some_line_changes. (prepare_text_end): -B no longer ignores missing newlines. (read_files): Allocate another bucket for incomplete lines. * util.c (line_cmp): Now takes just two arguments. No longer optimizes for common case of exact equality; the caller does that optimization now. The caller is changed accordingly. Optimize for the common case of mostly equality. Use isupper+tolower instead of islower+toupper, for consistency. * waitpid.c (waitpid): Fix typo with internal scoping. Thu Sep 8 08:23:15 1994 Paul Eggert * configure.in: Revamp for Autoconf 2. * memchr.c, waitpid.c: New source files for substitute functions. * Makefile.in (diff_o, diff3_o, sdiff_o): Add $(LIBOBJS). (srcs): Add memchr.c, waitpid.c. (distfiles): Add install.sh, memchr.c, waitpid.c, install.sh. * system.h: Use Autoconf 2 style HAVE_DIRENT_H etc. macros for dirs. * dir.c (dir_sort): Prefer NAMLEN (p) to strlen (p->d_name). Change VOID_CLOSEDIR to CLOSEDIR_VOID for Autoconf 2. * sdiff.c, util.c (memchr, waitpid): Remove; use new substitutes. * diff3.c (read_diff): Use new waitpid substitute. * cmp.c, diff.c, diff3.c, sdiff.c (check_stdout, try_help): New fns. (usage): Just print more detailed usage message; let caller exit. * diff.c (option_help): New variable. (filetype): Add Posix.1b file types. Fri Sep 2 16:01:49 1994 Paul Eggert * configure.in: Switch to new autoconf names. Add sys/file.h test. * Makefile.in (distclean): Clean config.cache, config.log (used by new autoconf). * diff.c, diff3.c, (main), sdiff.c (trapsigs): If we'll have children, make sure SIGCHLD isn't ignored. * diff3.c (DIFF_CHUNK_SIZE): Removed. Get size from STAT_BLOCKSIZE. (INT_STRLEN_BOUND): New macro. * ifdef.c (format_group, groups_letter_value): Use * instead of [] in prototypes. * system.h: Include only if HAVE_SYS_FILE_H. (S_IXGRP, S_IXOTH, S_IXUSR): Remove unused macros. * util.c (begin_output): Check fdopen result. The following changes simplify porting to non-Posix environments. * cmp.c, diff.c, diff3.c, sdiff.c, (main): Call initialize_main first. * diff.c (binary_I_O): New variable for --binary option. (main, usage, compare_files): Support --binary option. (compare_files): Use filename_lastdirchar to find last directory char in a file name. * cmp.c (main), diff.c (compare_files), dir.c (compare_names, diff_dirs): Use filename_cmp to compare file names. Use same_file to determine whether two files are the same. * context.c (print_context_label): Check whether ctime yields 0. * diff3.c (read_diff), sdiff.c (cleanup, main, waitpid), util.c (begin_output): Use popen+pclose if !HAVE_FORK. * io.c (sip): If HAVE_SETMODE, test for binary files in O_BINARY mode. * sdiff.c (ck_fdopen): Function removed. (edit): Use system if !HAVE_FORK. (execdiff): Now assumes caller has pushed all args, plus trailing 0. All callers changed. (private_tempnam): Try TMP if TMPDIR isn't defined. Fit temporary filenames into 8.3 limit. * system.h (STAT_BLOCKSIZE): Don't define if already defined. (min, max): Undef if already defined. (filename_cmp, filename_lastdirchar, HAVE_FORK, HAVE_SETMODE, initialize_main O_BINARY, same_file): New macros. Fri Jun 17 11:23:53 1994 David J. MacKenzie (djm@geech.gnu.ai.mit.edu) * Makefile.in (info, dvi, diff.dvi): New targets. (clean): Remove TeX output files. Fri Jun 17 05:37:52 1994 Paul Eggert (eggert@twinsun.com) * cmp.c, io.c (word): Change from typedef to #define, to avoid collision with Unicos 8.0 , which also typedefs `word'. Thu Apr 15 00:53:01 1994 Paul Eggert (eggert@twinsun.com) * diff3.c (scan_diff_line), util.c (print_number_range): Don't rely on promotion to make the old-style parameter type agree with the prototype parameter type; this doesn't work on Apollos running bsd4.3. Mon Jan 3 02:05:51 1994 Paul Eggert (eggert@twinsun.com) * Makefile.in (LDFLAGS): Remove -g. Change all link commands to use both $(CFLAGS) and $(LDFLAGS). Mon Dec 13 12:23:27 1993 Paul Eggert (eggert@twinsun.com) * system.h: Don't assume dirent.h exists just because _POSIX_VERSION is defined. Fri Dec 3 18:39:39 1993 Paul Eggert (eggert@twinsun.com) * diff.c (main): allow -pu. Tue Nov 23 03:51:08 1993 Paul Eggert (eggert@twinsun.com) * Makefile.in (distclean): Remove config.h. Wed Nov 10 00:28:27 1993 Paul Eggert (eggert@twinsun.com) * Version 2.6 released. * analyze.c (too_expensive): New variable, for heuristic to limit the worst-case cost to O(N**1.5 log N) at the price of producing suboptimal output for large inputs with many differences. (diff_2_files): Initialize it. (struct partition): New type. (SNAKE_LIMIT): New macro; merely documents already-used number 20. (diag): New `minimal' arg; all callers changed. Put results into struct partition. Apply `too_expensive' heuristic. Tune. (compareseq): New `minimal' arg; all callers changed. Tune. (shift_boundaries): Improve heuristic to also coalesce adjacent runs of changes more often. * diff.c (long_options, main, usage): Add `--help'. (main): Send version number to stdout, not stderr. (usage): Send usage to stdout, not stderr. (compare_files): Initialize `inf' properly. * io.c (word): Change to `int'; it makes a big difference on x86. (sip, slurp): Put off allocating room to hold the whole file until we have to read the whole file. This wins if the file turns out to be binary. * util.c (xmalloc, xrealloc): "virtual memory" -> "memory" (primes): Omit large primes if INT_MAX is small. * sdiff.c (usage): Send usage to stdout, not stderr. (long_options, main, usage): Add `--help'. (main): Send version number to stdout, not stderr. Exit afterwards. * diff3.c (usage): Send usage to stdout, not stderr. (long_options, main, usage): Add `--help'. (read_diff): Detect integer overflow in buffer size calculations. * cmp.c (word): New type. All uses of `long' for word-at-a-time comparisons changed to `word'. (long_options, main, usage): Add `--help'. (usage): Send usage to stdout, not stderr. (main): Add `-v'. Send version number to stdout, not stderr. * configure.in (AC_HAVE_HEADERS): Add unistd.h; remove AC_UNISTD_H. Mon Sep 27 07:20:24 1993 Paul Eggert (eggert@twinsun.com) * diff.c (add_exclude_file): Cast memchr to (char *) to suppress bogus warnings on some nonstandard hosts. * Makefile.in (cmp): Add version.o. * analyze.c (diff_2_files): Work around memcmp bug with size=0. * cmp.c (main, usage, version_string): Add --version option. * system.h (malloc, realloc): Declare only if !HAVE_STDLIB_H. (memchr): Declare only if !HAVE_MEMCHR. These changes are needed to keep some nonstandard hosts happy. * util.c (memchr): Make first arg char const * to match standard. (xmalloc, xrealloc): Cast malloc, realloc to (VOID *) to suppress bogus warnings on some nonstandard hosts. * diff3.c (xmalloc, xrealloc): Cast malloc, realloc to (VOID *) to suppress bogus warnings on some nonstandard hosts. * sdiff.c (xmalloc, xrealloc): Cast malloc, realloc to (VOID *) to suppress bogus warnings on some nonstandard hosts. (lf_copy, lf_skip, lf_snarf): Cast memchr to (char *) to suppress bogus warnings on some nonstandard hosts. (memchr): Make first arg char const * to match standard. Mon Sep 27 00:23:37 1993 Paul Eggert (eggert@twinsun.com) * Version 2.5 released. * analyze.c (diff_2_files): Work around memcmp bug with size=0. * cmp.c (main, usage, version_string): Add --version option. * Makefile.in (cmp): Add version.o. * diff.c (add_exclude_file): Cast memchr to (char *) to suppress bogus warnings on some nonstandard hosts. * sdiff.c (lf_copy, lf_skip, lf_snarf): Likewise. * diff3.c, sdiff.c, util.c (xmalloc, xrealloc): Cast malloc, realloc to (VOID *) to suppress bogus warnings on some nonstandard hosts. * sdiff.c, util.c (memchr): Make first arg char const * to match standard. * system.h (malloc, realloc): Declare only if !HAVE_STDLIB_H. (memchr): Declare only if !HAVE_MEMCHR. These changes are needed to keep some nonstandard hosts happy. * xmalloc.c: Include always; some nonstandard hosts need it for size_t even if STDC_HEADERS. Sat Sep 18 01:33:07 1993 Paul Eggert (eggert@twinsun.com) * configure.in (AC_STAT_MACROS_BROKEN): Add. * system.h (S_IS{BLK,CHR,DIR,FIFO,REG,SOCK}): Fix defns if STAT_MACROS_BROKEN. * Makefile.in (diff3, sdiff, cmp): Do not link $(ALLOCA). * analyze.c (discard_confusing_lines): Make defn static, like decl. * sdiff.c (xmalloc): Likewise. * ifdef.c (format_group): Ensure isdigit argument isn't < 0. * side.c (print_half_line): Use isprint, since some hosts lack isgraph. * util.c (output_1_line): Likewise. Ensure its argument isn't < 0. (xmalloc, xrealloc): Remove needless casts. * system.h (volatile, const): Define these before including any system headers, so that they're used consistently in all system includes. (getenv, malloc, realloc): Declare even if HAVE_STDLIB_H, since some s don't declare them. (memchr): Likewise for . * cmp.c, diff3.c, diff.h, sdiff.c: Include "system.h" first. * diff.c: Remove redundant "system.h" inclusion. * diff3.c (xmalloc): Now static. (xmalloc, realloc): Remove needless casts. (READNUM): Ensure isdigit argument isn't negative. Wed Sep 14 07:14:15 1993 Paul Eggert (eggert@twinsun.com) * Version 2.4 released. * ifdef.c (scan_char_literal): New function, for new %c'x' and %c'\ooo' format specs. (format_group, print_ifdef_lines): Use it. Remove %0 format spec. * cmp.c (cmp): Don't try to read past end of file; this doesn't work on ttys. * system.h, version.c: #include , not "config.h", to allow configuring in a separate directory when the source directory has already been configured. * Makefile.in (COMPILE): New defn, with proper -I options so that `#include ' works. (.c.o, diff3.o, sdiff.o): Use it. Mon Sep 13 06:45:43 1993 Paul Eggert (eggert@twinsun.com) * diff.c (main, longopts): Add --line-format=FORMAT option. (specify_format): Args no longer const pointers. All callers changed. * ifdef.c: Add support for %?c, %(A=B?T:E), PRINTF_SPECn formats. (struct group): New struct. (print_ifdef_lines): Use it to simplify argument passing. Remove the convention that last arg -1 signifies that the lines from file 2 are the same as the lines from file 1; this convention no longer works, now that line numbers might be printed out, since the line numbers may differ. Add first FILE * argument to output to. All callers changed. Use a faster test for the single-fwrite optimization. (format_group, scan_printf_spec, groups_letter_value): New functions. * diff.h (group_format, line_format): No longer const pointers. (format_ifdef): 1st arg is no longer const pointer. * configure.in: Configure HAVE_LIMITS_H, HAVE_STDLIB_H. * system.h , , : Include only if HAVE_LIMITS_H etc. * system.h (memcmp, memcpy, strchr, strrchr, struct dirent): Prefer these standard names to the traditional names (bcmp, bcpy, index, rindex, struct direct). All callers changed. * system.h (PARAMS, VOID): Define earlier so that malloc decl can use VOID. (STAT_BLOCKSIZE): Simplify ersatz defn; just use 8K. Fri Sep 3 00:21:02 1993 Paul Eggert (eggert@twinsun.com) * diff.c (compare_files): Two files with the same name must be the same file; avoid a needless `stat' in that case. Fri Aug 27 06:59:03 1993 Paul Eggert (eggert@twinsun.com) * Pervasive changes for portability to 64-bit hosts: Add prototypes to function declarations. Use size_t, not int, when needed. * Other pervasive changes: Use `const' more often. Use STD{IN,OUT,ERR}_FILENO instead of [012]. Use 0, not NULL, for portability to broken hosts. * Makefile.in: (srcs, objs, distfiles, cmp): New files cmpbuf.[ch]. (distfiles): New files config.h.in, mkinstalldirs. (.c.o): Add -DHAVE_CONFIG_H. * analyze.c: (diag): Pacify `gcc -Wall' with a useless assignment. (diff_2_files): Use l.c.m., not max, of files' buffer sizes. * cmp.c: Make globals static when possible. (file): Now a 2-element array; replaces `file1' and `file2'. (file_desc, buffer): Likewise, for file[12]_desc and buf[12]. (main): Likewise, for stat_buf[12]. Index these variables with `i'. (ignore_initial): New var. (long_options): Now const. Add `--ignore-initial'. (usage): Sort options and add `--ignore-initial'. (main, cmp): Add `--ignore-initial' support. (main): `cmp - -' now succeeds. When comparing standard input to a file, and using a shortcut (e.g. looking at file sizes or inode numbers), take the lseek offset into account before deciding whether the files are identical. Avoid mentioning `dev_t', `ino_t' for portability to nonstandard hosts. Use l.c.m. of files' buffer sizes, not 8 * 1024. ferror (stdout) does not imply errno has a useful value. If 2nd file is "-", treat it first, in case stdin is closed. (cmp): Always compute `char_number', `smaller' for speed and simplicity. Say `cmp: EOF on input', not `/usr/gnu/bin/cmp: EOF on input', as per Posix.2. (block_compare_and_count): Increment line_number argument. Remove end_char argument; it's always '\n'. All callers changed. Do not assume sizeof(long) == 4; this isn't true on some 64-bit hosts. (block_compare): Minimize differences with block_compare_and_count. (block_read): Coalesce `bp += nread's. (printc): Remove `FILE *' arg; output to stdout. All callers changed. * configure.in: Configure HAVE_SIGACTION, RETSIGTYPE, HAVE_VPRINTF. Configure into config.h. * context.c (print_context_label): Standard input's st_mtime is no longer a special case here, since `compare_files' now sets it to the current time. * diff.c (usage): Sort options. (filetype): New function. (compare_files): Set stdin's st_mtime to be the current time. Leave its name "-" instead of changing it to "Standard Input"; to test whether a file is stdin, we must compare its name to "-" instead of its desc to 0, since if it's closed other file descs may be 0. When comparing standard input to a file, and using a shortcut (e.g. looking at file sizes or inode numbers), take the lseek offset into account before deciding whether the files are identical. Pretend that nonexistent files have the same filetype as existing files. Rename `errorcount' to `failed', since it's boolean. In directory comparisons, if a file is neither a regular file nor a directory, just print its type and the other file's type. * diff.h (Is_space, textchar): Remove. (struct msg, msg_chain, msg_chain_end): Move to util.c. (VOID): Move to system.h. (line_cmp, version_string, change_letter, print_number_range, find_change): New decls. * diff.texi: whitespace -> white space. It now stands for whatever isspace yields. Add --ignore-initial. * diff3.c (VOID): Move to system.h. (version_string): Now char[]. (usage): Sort options. (process_diff): Pacify `gcc -Wall' with a useless assignment. (read_diff): pid is of type pid_t, not int. Use waitpid if available. (output_diff3): Simplify test for `\ No newline at end of file' message. * dir.c (struct dirdata): Rename `files' to `names' to avoid confusion with external struct file_data `files'. * io.c (line_cmp): Move declaration to diff.h. (textchar): Remove. (find_and_hash_each_line): Use locale's definition of white space instead of using one hardwired defn for -b and another for -w. * normal.c (change_letter, print_number_range, find_change): Move decls to diff.h. (print_normal_hunk): Now static. * sdiff.c (SEEK_SET): Move to system.h. (version_string): Now char[], not char*. (private_tempnam): Remove hardcoded limit on temporary file names. (exiterr, perror_fatal, main): When exiting because of a signal, exit with that signal's status. (lf_refill, main, skip_white, edit, interact): Check for signal. (ignore_SIGINT): Renamed from `ignore_signals'. (NUM_SIGS, initial_handler): New macros. (initial_action, signal_received, sigs_trapped): New vars. (catchsig, trapsigs): Use sigaction if possible, since this closes the windows of vulnerability that `signal' has. Use RETSIGTYPE not void. When a signal comes in, just set a global variable; this is safer. (checksigs, untrapsig): New functions. (edit): Pacify `gcc -Wall' with a useless assignment. Respond to each empty line with help, not to every other empty line. (private_tempnam): Remove hardcoded limit on temporary file name length. Don't assume sizeof (pid_t) <= sizeof (int). * system.h: (S_IXOTH, S_IXGRP, S_IXUSR, SEEK_SET, SEEK_CUR, STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO): New macros, if system doesn't define them. (volatile): Don't define if already defined. (PARAMS): New macro. (VOID): Move here from diff.h. * util.c (struct msg, msg_chain, msg_chain_end): Moved here from diff.h. (message5): New function. (pr_pid): New var. (begin_output): Allocate `name' more precisely. Put child pid into pr_pid, so that we can wait for it later. Don't check execl's return value, since any return must be an error. (finish_output): Detect and report output errors. Use waitpid if available. Check pr exit status. (line_cmp): Use locale's definition of white space instead of using one hardwired defn for -b and another for -w. (analyze_cmp): Avoid double negation with `! nontrivial'. Pacify `gcc -Wall' be rewriting for-loop into do-while-loop. (dir_file_pathname): New function. * version.c (version_string): Now char[], not char*. Thu Jul 29 20:44:30 1993 David J. MacKenzie (djm@wookumz.gnu.ai.mit.edu) * Makefile.in (config.status): Run config.status --recheck, not configure, to get the right args passed. Thu Jul 22 10:46:30 1993 Paul Eggert (eggert@twinsun.com) * Makefile.in (dist): Replace `if [ ! TEST ]; then ACTION; fi' with `[ TEST ] || ACTION || exit' so that the containing for-loop exits with proper status for `make'. Thu Jul 8 19:47:22 1993 David J. MacKenzie (djm@goldman.gnu.ai.mit.edu) * Makefile.in (installdirs): New target. (install): Use it. (Makefile, config.status, configure): New targets. Sat Jun 5 23:10:40 1993 Paul Eggert (eggert@twinsun.com) * Makefile.in (dist): Switch from .z to .gz. Wed May 26 17:16:02 1993 Paul Eggert (eggert@twinsun.com) * diff.c (main): Cast args to compare_files, for traditional C. * side.c (print_sdiff_common_lines_print_sdiff_hunk): Likewise. * analyze.c, diff3.c, sdiff.c, util.c: Don't assume NULL is defined properly. Tue May 25 14:54:05 1993 Paul Eggert (eggert@twinsun.com) * analyze.c (diff_2_files): With -q, do not report that files differ if all their differences are ignored. (briefly_report): New function. * diff.h (ignore_some_changes): New variable. * diff.c (compare_files): Don't use the file size shortcut if ignore_some_changes is nonzero, since the file size may differ merely due to ignored changes. (main): Set ignore_some_changes if we might ignore some changes. Remove unsystematic assignment of 0 to static vars. * io.c (read_files): New argument PRETEND_BINARY says whether to pretend the files are binary. * diff3.c (tab_align_flag): New variable, for new -T option. (main, usage, output_diff3): Add support for -T. Sun May 23 15:25:29 1993 Richard Stallman (rms@mole.gnu.ai.mit.edu) * dir.c (dir_sort): Always init `data' to avoid GCC warning. Sat May 22 15:35:02 1993 Paul Eggert (eggert@twinsun.com) * Makefile.in (dist): Change name of package from diff to diffutils. Don't bother to build .Z dist; .z suffices. Fri May 21 16:35:22 1993 Paul Eggert (eggert@twinsun.com) * diff.c: Include "system.h" to get memchr declaration. * system.h (memchr): Declare if !HAVE_MEMCHR, not if !HAVE_MEMCHR && !STDC_HEADERS. Wed May 19 17:43:55 1993 Paul Eggert (eggert@twinsun.com) * Version 2.3 released. Fri Apr 23 17:18:44 1993 Paul Eggert (eggert@twinsun.com) * io.c (find_identical_ends): Do not discard the last HORIZON_LINES lines of the prefix, or the first HORIZON_LINES lines of the suffix. * diff.c (main, longopts, usage): Add --horizon-lines option. * diff3.c (main, process_diff, read_diff): Invoke second diff with --horizon-lines determined by the first diff. * diff.h, diff3.c (horizon_lines): New variable. Mon Mar 22 16:16:00 1993 Roland McGrath (roland@churchy.gnu.ai.mit.edu) * system.h [HAVE_STRING_H || STDC_HEADERS] (bcopy, bcmp, bzero): Don't define if already defined. Fri Mar 5 00:20:16 1993 Richard Stallman (rms@mole.gnu.ai.mit.edu) * diff.c (main): Use NULL in arg to compare_files. Thu Feb 25 15:26:01 1993 Roland McGrath (roland@churchy.gnu.ai.mit.edu) * system.h: Declare memchr #if !HAVE_MEMCHR && !STDC_HEADERS, not #if !HAVE_MEMCHR || !STDC_HEADERS. Mon Feb 22 15:04:46 1993 Richard Stallman (rms@geech.gnu.ai.mit.edu) * io.c (find_identical_ends): Move complicated arg outside GUESS_LINES. Mon Feb 22 12:56:12 1993 Roland McGrath (roland@churchy.gnu.ai.mit.edu) * Makefile.in (.c.o): Add -I$(srcdir); put $(CFLAGS) last before $<. Sat Feb 20 19:18:56 1993 Richard Stallman (rms@mole.gnu.ai.mit.edu) * io.c (binary_file_p): Return zero if file size is zero. Fri Feb 19 17:31:32 1993 Roland McGrath (roland@geech.gnu.ai.mit.edu) * Version 2.2 released. * system.h [HAVE_STRING_H || STDC_HEADERS] (index, rindex): Don't define if already defined. Wed Feb 17 17:08:00 1993 Roland McGrath (roland@churchy.gnu.ai.mit.edu) * Makefile.in (srcs): Remove limits.h. Thu Feb 11 03:36:00 1993 Richard Stallman (rms@mole.gnu.ai.mit.edu) * diff3.c (xmalloc): No longer static. * sdiff.c (edit): Allocate buf dynamically. * dir.c (dir_sort): Handle VOID_CLOSEDIR. Wed Feb 10 00:15:54 1993 Richard Stallman (rms@mole.gnu.ai.mit.edu) * limits.h: File deleted (should never have been there). Tue Feb 9 03:53:22 1993 Richard Stallman (rms@mole.gnu.ai.mit.edu) * Makefile.in (.c.o, diff3.o, sdiff.o): Put $(CFLAGS) last. Wed Feb 3 15:42:10 1993 David J. MacKenzie (djm@goldman.gnu.ai.mit.edu) * system.h: Don't #define const; let configure do it. Mon Feb 1 02:13:23 1993 Paul Eggert (eggert@hal.gnu.ai.mit.edu) * Version 2.1 released. * Makefile.in (dist): Survive ln failures. Create .tar.z (gzipped tar) file as well as .tar.Z (compressed tar) file. Fri Jan 8 22:31:41 1993 Paul Eggert (eggert@twinsun.com) * side.c (print_half_line): When the input position falls outside the column, do not output a tab even if the output position still falls within the column. Mon Dec 21 13:54:36 1992 David J. MacKenzie (djm@kropotkin.gnu.ai.mit.edu) * Makefile.in (.c.o): Add -I. Fri Dec 18 14:08:20 1992 Paul Eggert (eggert@twinsun.com) * configure.in: Add HAVE_FCNTL_H, since system.h uses it. Tue Nov 24 10:06:48 1992 David J. MacKenzie (djm@goldman.gnu.ai.mit.edu) * Makefile.in: Note change from USG to HAVE_STRING_H. Mon Nov 23 18:44:00 1992 Paul Eggert (eggert@twinsun.com) * io.c (find_and_hash_each_line): When running out of lines, double the number of allocated lines, instead of just doubling that number minus the prefix lines. This is more likely to avoid the need for further memory allocation. Wed Nov 18 20:40:28 1992 Paul Eggert (eggert@twinsun.com) * dir.c (dir_sort): Plug memory leak: space holding directory contents was not being reclaimed. Get directory size from struct file_data for initial guess at memory needed. Detect errors when reading and closing directory. (diff_dirs): Pass struct file_data to dir_sort. Finish plugging leak. * diff.c (compare_files): Pass struct file_data to diff_dirs. * io.c (find_and_hash_each_line): Don't assume alloc_lines is nonzero when allocating more lines. Thu Nov 12 16:02:18 1992 Paul Eggert (eggert@twinsun.com) * diff.c (main): Add `-U lines' as an alias for `--unified=lines'. * diff3.c (usage): Add third --label option in example. * util.c (analyze_hunk): Fix test for ignoring blank lines. * configure.in, system.h: Avoid USG; use HAVE_TIME_H etc. instead. Mon Nov 9 05:13:25 1992 Paul Eggert (eggert@twinsun.com) * diff3.c (main, usage): Add -A or --show-all. -m now defaults to -A, not -E. Allow up to three -L options. (output_diff3_edscript, output_diff3_merge): Remove spurious differences between these two functions. Output ||||||| for -A. Distinguish between conflicts and overlaps. (dotlines, undotlines): New functions that output `Ns', not `N,Ns'. (output_diff3_edscript, output_diff3_merge): Use them. * io.c (find_identical_ends): shift_boundaries needs an extra identical line at the end, not at the beginning. * sdiff.c (edit): execvp wants char **, not const char **. Mon Oct 19 04:39:32 1992 Paul Eggert (eggert@twinsun.com) * context.c (print_context_script, find_function): Context line numbers start with - file->prefix_lines, not 0. * io.c (binary_file_p): Undo last change; it was a library bug. Sun Oct 18 00:17:29 1992 Richard Stallman (rms@mole.gnu.ai.mit.edu) * io.c (binary_file_p): Consider empty file as non-binary. Mon Oct 5 05:18:46 1992 Paul Eggert (eggert@twinsun.com) * diff3.c (main, make_3way_diff, using_to_diff3_block): Don't report bogus differences (for one of -mexEX3) just because the file0-file1 diffs don't line up with the file0-file2 diffs. (This is entirely possible since we don't use diff's -n option.) Always compare file1 to file2, so that diff3 sees those changes directly. Typically, file2 is now the common file, not file0. (output_diff3_merge): The input file is file 0, not the common file. (FC, FO): New macros; they replace FILE1, FILE0 for two-way diffs, to distinguish them from three-way diffs. * diff3.c (using_to_diff3_block): Fold repeated code into loops. * diff3.c (make_3way_diff, process_diff): Have the *_end variable point to the next field to be changed, not to the last object allocated; this saves an if-then-else. * diff3.c (process_diff): Use D_NUMLINES instead of its definiens. * diff3.c: Make fns and vars static unless they must be external. Wed Sep 30 09:21:59 1992 Paul Eggert (eggert@twinsun.com) * analyze.c (diff_2_files): OUTPUT_IFDEF is now robust. * diff.h (ROBUST_OUTPUT_STYLE): Likewise. (default_line_format): Remove. All refs removed. * ifdef.c (print_ifdef_lines): Add %L. Optimize %l\n even if user specified it, as opposed to its being the default. Tue Sep 29 19:01:28 1992 Paul Eggert (eggert@twinsun.com) * diff.c (longopts, main): --{old,new,unchanged,changed}--group-format are new options, so that -D is no longer overloaded. Set no_diff_means_no_output if --unchanged-{line,group}-format allows it. * diff.h (enum line_class): New type. (group_format, line_format): Use it to regularize option flags. All refs changed. * ifdef.c (format_ifdef, print_ifdef_lines): %n is no longer a format. Mon Sep 28 04:51:42 1992 Paul Eggert (eggert@twinsun.com) * diff.c (main, usage): Replace --line-prefix with the more general --{old,new,unchanged}-line-format options. * ifdef.c (format_ifdef, print_ifdef_lines): Likewise. * diff.h (line_format): Renamed from line_prefix. All refs changed. * diff.h, ifdef.c (default_line_format): New variable. * util.c (output_1_line): New function. (print_1_line): Use it. * ifdef.c: (format_ifdef, print_ifdef_lines): Add %0 format. Sun Sep 27 05:38:13 1992 Paul Eggert (eggert@twinsun.com) * diff.c (main): Add -E or --line-prefix option. Add -D'=xxx' for common lines. Change default -D< format from copy of -D> format to to -D<%<; similarly for default -D> format. * diff.h (common_format, line_prefix): New variables. * ifdef.c (format_ifdef): New function. (print_ifdef_script, print_ifdef_hunk, print_ifdef_lines): Use it for -D'=xxx', -E. * context.c (find_hunk): Glue together two non-ignorable changes that are exactly CONTEXT * 2 lines apart. This shortens output, removes a behavioral discontinuity at CONTEXT = 0, and is more compatible with traditional diff. * io.c (find_identical_ends): Slurp stdin at most once. * util.c (print_line_line): line_flag is const char *. Thu Sep 24 15:18:07 1992 Paul Eggert (eggert@twinsun.com) * ifdef.c (print_ifdef_lines): New function, which fwrites a sequence of lines all at once for speed. (print_ifdef_script, print_ifdef_hunk): Use it. Thu Sep 24 05:54:14 1992 Paul Eggert (eggert@twinsun.com) * diff.c (main): Support new -D options for if-then-else formats. (specify_format): New function. * diff.h (ifndef_format, ifdef_format, ifnelse_format): New variables. * ifdef.c (print_ifdef_hunk): Use the new variables instead of a hardwired format. * side.c (print_1sdiff_line): Represent incomplete lines on output. (print_sdiff_script): Likewise. Don't print 'q' at end, since that doesn't work with incomplete lines. * sdiff.c (interact): Don't assume diff output ends with 'q' line. * diff.h (ROBUST_OUTPUT_STYLE): OUTPUT_SDIFF is now robust. * sdiff.c (lf_copy, lf_snarf): Use memchr instead of index, to avoid dumping core when files contain null characters. (memchr): New function (if memchr is missing). * io.c (sip): New arg SKIP_TEST to skip test for binary file. (read_files): Don't bother testing second file if first is binary. Thu Sep 17 21:17:49 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) * system.h [!USG && !_POSIX_VERSION]: Protect from conflicting prototype for wait in sys/wait.h. Wed Sep 16 12:32:18 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) * Makefile.in: Include binprefix in -DDIFF_PROGRAM. Tue Sep 15 14:27:25 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) * Version 2.0. Sat Sep 12 01:31:19 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) * util.c, diff.h, system.h [!HAVE_MEMCHR]: Don't use void * and const when declaring memchr replacement. Declare memchr if !STDC_HEADERS && !USG. Thu Sep 10 15:17:32 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) * Makefile.in (uninstall): New target. * diff.c (excluded_filename): Use fnmatch, not wildmat. (usage): Document -x, -X, --exclude, --exclude-from. Makefile.in: Use fnmatch.c, not wildmat.c. Sun Sep 6 23:46:25 1992 Paul Eggert (eggert@twinsun.com) * configure.in: Add HAVE_MEMCHR. * diff.h, util.c: Use it instead of MEMCHR_MISSING. Sun Sep 6 07:25:49 1992 Paul Eggert (eggert@twinsun.com) * diff.h: (struct line_def): Replace this 3-word struct with char *. This uses less memory, particularly for large files with short lines. (struct file_data): New member linbuf_base counts number of lines in common prefix that are not recorded in linbuf; this uses less memory if files are identical or differ only at end. New member buffered_lines counts possibly differing lines. New member valid_lines counts valid data. New member alloc_lines - linbuf_base replaces old linbufsize. linbuf[0] now always points at first differing line. Remove unused members ltran, suffix_lines. Add const where appropriate. (Is_space): New macro, for consistent definition of `white space'. (excluded_filename, memchr, sip, slurp): New declarations. * ed.c (print_ed_hunk): Adjust to diff.h's struct changes. * context.c (pr_context_hunk): Likewise. * ifdef.c (print_ifdef_script): Likewise. * side.c (print_sdiff_script, print_half_line): Likewise. * util.c (analyze_hunk, line_cmp, print_1_line): Likewise. * analyze.c (shift_boundaries): Remove unneeded variable `end' and unnecessary comparisons of `preceding' and `other_preceding' against 0. (diff_2_files): When comparing files byte-by-byte for equality, don't slurp them all in at once; just compare them a buffer at a time. This can win big if they differ early on. Move some code to compare_files to enable this change. Use only one buffer for stdin with `diff - -'. (discard_confusing_lines, diff_2_files): Coalesce malloc/free calls. (build_script): Remove obsolete OUTPUT_RCS code. * diff.c (add_exclude, add_exclude_file, excluded_filename): New fns. (main): Use them for the new --exclude and --exclude-from options. (compare_files): Don't open a file unless it must be read. Treat `diff file file' and `diff file dir' similarly. Move some code here from diff_2_files to enable this. Simplify file vs dir warning. * dir.c (dir_sort): Support new --exclude* options. * io.c (struct equivclass): Put hash code and line length here instead of struct line_def, so that they can be shared. (find_and_hash_each_line): Compute equivalence class as we go, instead of doing it in a separate pass; this thrashes memory less. Make buckets realloc-able, since we can't preallocate them. Record one more line start than there are lines, so that we can compute any line's length by subtracting its start from the next line's, instead of storing the length explicitly. This saves memory. Move prefix-handling code to find_identical_ends; this wins with large prefixes. Use Is_space, not is_space, for consistent treatment of white space. (prepare_text_end): New function. (find_identical_ends): Move slurping here, so it's only done when needed. Work even if the buffers are the same (because of `diff - -'). Compare prefixes a word at a time for speed. (find_equiv_class): Delete; now done by find_and_hash_each_line. (read_files): Don't slurp unless needed. find_equiv_class's work is now folded into find_and_hash_each_line. Don't copy stdin buffer if `diff - -'. Check for running out of primes. (sip, slurp): Split first part of `slurp' into another function `sip'. `sip' sets things up and perhaps reads the first ST_BLKSIZE buffer to see whether the file is binary; `slurp' now just finishes the job. This lets diff_2_files compare binary files lazily. Allocate a one-word sentinel to allow word-at-a-time prefix comparison. Count prefix lines only if needed, only count the first file's prefix. Don't bother to count suffix lines; it's never needed. Set up linbuf[0] to point at first differing line. (binary_file_p): Change test for binary files: if it has a null byte in its first buffer, it's binary. (primes): Add more primes. * util.c (line_cmp): Use bcmp for speed. Use Is_space, not is_space, for consistent treatment of white space. (translate_line_number): Internal line numbers now count from 0 starting after the prefix. (memchr): New function (if memchr is missing). * Makefile.in: Document HAVE_ST_BLKSIZE. Link with wildmat.o. * system.h (STAT_BLOCKSIZE): New macro based on HAVE_ST_BLKSIZE. * configure.in: Add AC_ST_BLKSIZE. * wildmat.c: New file. Fri Sep 4 01:28:51 1992 Richard Stallman (rms@mole.gnu.ai.mit.edu) * sdiff.c (xmalloc): Renamed from ck_malloc. Callers changed. Thu Sep 3 15:28:59 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) * diff.h: Don't declare free, index, rindex. Tue Aug 11 22:18:06 1992 John Gilmore (gnu at cygnus.com) * io.c (binary_file_p): Use heuristic to avoid declaring info files as binary files. Allow about 1.5% non-printing characters (in info's case, ^_). Tue Jul 7 01:09:26 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) * diff.h: Replace function_regexp and ignore_regexp with lists of compiled regexps. * analyze.c, context.c, util.c: Test whether the lists, not the old variables, are empty. * util.c (analyze_hunk), context.c (find_function): Compare lines with the lists of regexps. * diff.c (add_regexp): New function. (main): Use it. * diff3: Add -v --version option. * Makefile.in: Link with version.o. * system.h: New file. * diff.h, cmp.c, diff3.c, sdiff.c: Use it. * diff.h, diff3.c: Include string.h or strings.h, as appropriate. Declare malloc and realloc. * diff3.c (perror_with_exit): Include program name in message. * diff3.c: Lowercase error messages for GNU standards. * sdiff.c [USG || STDC_HEADERS]: Define bcopy in terms of memcpy. * sdiff.c: Use the version number from version.c. * Makefile.in: Link with version.o. * cmp.c error.c xmalloc.c: New files from textutils. * Makefile.in: Add rules for them. * diff.c (longopts): --unidirectional-new-file is like -P, not -N. Rename --file-label to --label (leave old name, but undocumented). * sdiff.c, diff.c (usage): Condense messages and fix some errors. * diff3.c (main, usage): Add long-named options. Fri Jul 3 14:31:18 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) * diff.h, diff3.c, sdiff.c: Change FOO_MISSING macros to HAVE_FOO. Thu Jun 25 16:59:47 1992 David J. MacKenzie (djm@apple-gunkies.gnu.ai.mit.edu) * diff.c: --reversed-ed -> --forward-ed. Wed Feb 26 12:17:32 1992 Paul Eggert (eggert@yata.uucp) * analyze.c, diff.c, diff.h, io.c: For -y, compare even if same file. Fri Feb 14 22:46:38 1992 Richard Stallman (rms@mole.gnu.ai.mit.edu) * io.c, diff3.c, analyze.c: Add extra parentheses. Sun Feb 9 00:22:42 1992 Richard Stallman (rms@mole.gnu.ai.mit.edu) * diff.h (unidirectional_new_file_flag): New variable. * diff.c (main): Set that for -P. (compare_files): Support -P, somewhat like -N. (longopts): Support long name for -P. Sat Jan 4 20:10:34 1992 Paul Eggert (eggert at yata.uucp) * Makefile.in: Distribute diff.info-* too. * README, sdiff.c: version number now matches version.c. * configure: Fix and document vfork test. * ifdef.c: Don't dump core if `diff -Dx f f'. Mon Dec 23 23:36:08 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu) * diff.h, diff3.c, sdiff.c: Change POSIX ifdefs to HAVE_UNISTD_H and _POSIX_VERSION. Wed Dec 18 17:00:31 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu) * Makefile.in (srcs): Add sdiff.c. (tapefiles): Add diff.texi and diff.info. * diff.h, diff3.c, sdiff.c: Use HAVE_VFORK_H instead of VFORK_HEADER and VFORK_WORKS. Tue Dec 17 00:02:59 1991 Paul Eggert (eggert at yata.uucp) * Makefile.in (all): Add diff.info, sdiff. * configure, diff.c, sdiff.c: Prefix long options with `--', not `+'. * diff.c: Regularize option names. * configure: Fix check for vfork. * configure, diff.c, diff.h, diff3.c, sdiff.c: Use Posix definitions when possible. * context.c: Align context with tab if -T is given. Tune. * diff.c, diff.h, side.c: Calculate column widths so that tabs line up. * io.c: Add distinction between white space and printing chars. * side.c: Don't expand tabs unless -t is given. * side.c, util.c: Tab expansion now knows about '\b', '\f', '\r', '\v'. * util.c: -w skips all white space. Remove lint. Tune. * sdiff.c: Support many more diff options, e.g. `-', `sdiff file dir'. Ignore interrupts while the subsidiary editor is in control. Clean up temporary file and kill subsidiary diff if interrupted. Ensure subsidiary diff doesn't ignore SIGPIPE. Don't get confused while waiting for two subprocesses. Don't let buffers overflow. Check for I/O errors. Convert to GNU style. Tune. * sdiff.c, util.c: Don't lose errno. Don't confuse sdiff with messages like `Binary files differ'. * sdiff.c, side.c: Don't assume that common lines are identical. Simplify --sdiff-merge-assist format. Mon Sep 16 16:42:01 1991 Tom Lord (lord at churchy.gnu.ai.mit.edu) * Makefile.in, sdiff.c: introduced sdiff front end to diff. * Makefile.in, analyze.c, diff.c, diff.h, io.c, side.c: Added sdiff-style output format to diff. Mon Aug 26 16:44:55 1991 David J. MacKenzie (djm at pogo.gnu.ai.mit.edu) * Makefile.in, configure: Only put $< in Makefile if using VPATH, because older makes don't understand it. Fri Aug 2 12:22:30 1991 David J. MacKenzie (djm at apple-gunkies) * configure: Create config.status. Remove it and Makefile if interrupted while creating them. Thu Aug 1 22:24:31 1991 David J. MacKenzie (djm at apple-gunkies) * configure: Check for +srcdir etc. arg and look for Makefile.in in that directory. Set VPATH if srcdir is not `.'. * Makefile.in: Get rid of $(archpfx). Tue Jul 30 21:28:44 1991 Richard Stallman (rms at mole.gnu.ai.mit.edu) * Makefile.in (prefix): Renamed from DESTDIR. Wed Jul 24 23:08:56 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu) * diff.h, diff3.c: Rearrange ifdefs to use POSIX, STDC_HEADERS, VFORK_MISSING, DIRENT. This way it works on more systems that aren't pure USG or BSD. Don't not define const if __GNUC__ is defined -- that would break with -traditional. * configure: Check for those features. Wed Jul 10 01:39:23 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu) * configure, Makefile.in: $(INSTALLPROG) -> $(INSTALL). Sat Jul 6 16:39:04 1991 David J. MacKenzie (djm at geech.gnu.ai.mit.edu) * Replace Makefile with configure and Makefile.in. Update README with current compilation instructions. Sat Jul 6 14:03:29 1991 Richard Stallman (rms at mole.gnu.ai.mit.edu) * util.c (setup_output): Just save the args for later use. (begin_output): Do the real work, with the values that were saved. It's safe to call begin_output more than once. Print the special headers for context format here. * analyze.c (diff_2_files): Don't print special headers here. * context.c (pr_context_hunk, pr_unidiff_hunk): Call begin_output. * ed.c (print_ed_hunk, print_forward_ed_hunk, print_rcs_hunk): * normal.c (print_normal_hunk): Likewise. * ifdef.c (print_ifdef_hunk): Likewise. * util.c (finish_output): Don't die if begin_output was not called. Thu Jun 20 23:10:01 1991 David J. MacKenzie (djm at geech.gnu.ai.mit.edu) * Makefile: Add TAGS, distclean, and realclean targets. Set SHELL. Tue Apr 30 13:54:36 1991 Richard Stallman (rms at mole.gnu.ai.mit.edu) * diff.h (TRUE, FALSE): Undefine these before defining. Thu Mar 14 18:27:27 1991 Richard Stallman (rms@mole.ai.mit.edu) * Makefile (objs): Include $(ALLOCA). Sat Mar 9 22:34:03 1991 Richard Stallman (rms at mole.ai.mit.edu) * diff.h: Include regex.h. Thu Feb 28 18:59:53 1991 Richard Stallman (rms at mole.ai.mit.edu) * Makefile (diff3): Link with GNU getopt. Sat Feb 23 12:49:43 1991 Richard Stallman (rms at mole.ai.mit.edu) * io.c (find_equiv_class): Make hash code unsigned before mod. * diff.h (files): Add EXTERN. Sun Jan 13 21:33:01 1991 Richard Stallman (rms at mole.ai.mit.edu) * diff.c: +print option renamed +paginate. Remove +all-text. Mon Jan 7 06:18:01 1991 David J. MacKenzie (djm at geech.ai.mit.edu) * Makefile (dist): New target, replacing diff.tar and diff.tar.Z, to encode version number in distribution directory and tar file names. Sun Jan 6 18:42:23 1991 Michael I Bushnell (mib at geech.ai.mit.edu) * Version 1.15 released. * version.c: Updated from 1.15 alpha to 1.15 * context.c (print_context_number_range, print_unidiff_number_range): Don't print N,M when N=M, print just N instead. * README: Updated for version 1.15. Makefile: Updated for version 1.15. * diff3.c (main): Don't get confused if one of the arguments is a directory. * diff.c (compare_files): Don't get confused if comparing standard input to a directory; print error instead. * analyze.c (diff_2_files), context.c (print_context_header, print_context_script), diff.c (main), diff.h (enum output_style): Tread unidiff as an output style in its own right. This also generates an error when both -u and -c are given. * diff.c (main): Better error messages when regexps are bad. * diff.c (compare_files): Don't assume stdin is opened. * diff3.c (read_diff): Don't assume things about the order of descriptor assignment and closes. * util.c (setup_output): Don't assume things about the order of descriptor assignment and closes. * diff.c (compare_files): Set a flag so that closes don't happen more than once. * diff.c (main): Don't just flush stdout, do a close. That way on broken systems we can still get errors. Mon Dec 24 16:24:17 1990 Richard Stallman (rms at mole.ai.mit.edu) * diff.c (usage): Use = for args of long options. Mon Dec 17 18:19:20 1990 Michael I Bushnell (mib at geech.ai.mit.edu) * context.c (print_context_label): Labels were interchanged badly. * context.c (pr_unidiff_hunk): Changes to deal with files ending in incomplete lines. * util.c (print_1_line): Other half of the changes. Mon Dec 3 14:23:55 1990 Richard Stallman (rms at mole.ai.mit.edu) * diff.c (longopts, usage): unidiff => unified. Wed Nov 7 17:13:08 1990 Richard Stallman (rms at mole.ai.mit.edu) * analyze.c (diff_2_files): No warnings about newlines for -D. * diff.c (pr_unidiff_hunk): Remove ref to output_patch_flag. Tue Oct 23 23:19:18 1990 Richard Stallman (rms at mole.ai.mit.edu) * diff.c (compare_files): For -D, compare even args are same file. * analyze.c (diff_2_files): Likewise. Also, output even if files have no differences. * analyze.c (diff_2_files): Print missing newline messages last. Return 2 if a newline is missing. Print them even if files end with identical text. Mon Oct 22 19:40:09 1990 Richard Stallman (rms at mole.ai.mit.edu) * diff.c (usage): Return 2. Wed Oct 10 20:54:04 1990 Richard Stallman (rms at mole.ai.mit.edu) * diff.c (longopts): Add +new-files. Sun Sep 23 22:49:29 1990 Richard Stallman (rms at mole.ai.mit.edu) * context.c (print_context_script): Handle unidiff_flag. (print_context_header): Likewise. (print_unidiff_number_range, pr_unidiff_hunk): New functions. * diff.c (longopts): Add element for +unidiff. (main): Handle +unidiff and -u. (usage): Mention them. Wed Sep 5 16:33:22 1990 Richard Stallman (rms at mole.ai.mit.edu) * io.c (find_and_hash_each_line): Deal with missing final newline after buffering necessary context lines. Sat Sep 1 16:32:32 1990 Richard Stallman (rms at mole.ai.mit.edu) * io.c (find_identical_ends): ROBUST_OUTPUT_FORMAT test was backward. Thu Aug 23 17:17:20 1990 Richard Stallman (rms at mole.ai.mit.edu) * diff3.c (WIFEXITED): Undef it if WEXITSTATUS is not defined. * context.c (find_function): Don't try to return values. Wed Aug 22 11:54:39 1990 Richard Stallman (rms at mole.ai.mit.edu) * diff.h (O_RDONLY): Define if not defined. Tue Aug 21 13:49:26 1990 Richard Stallman (rms at mole.ai.mit.edu) * Handle -L option. * context.c (print_context_label): New function. (print_context_header): Use that. * diff.c (main): Recognize the option. (usage): Updated. * diff.h (file_label): New variable. * diff3.c (main): Recognize -L instead of -t. * diff3.c (main): Support -m without other option. * diff3.c (WEXITSTATUS, WIFEXITED): Define whenever not defined. * diff3.c (bcopy, index, rindex): Delete definitions; not used. (D_LINENUM, D_LINELEN): Likewise. (struct diff_block): lengths includes newlines. (struct diff3_block): Likewise. (always_text, merge): New variables. (read_diff): Return address of end, not size read. Calls changed. Pass -a to diff if given to diff3. current_chunk_size now an int. Detect error in `pipe'. Check for incomplete line of output here. (scan_diff_line): Don't make scan_ptr + 2 before knowing it is valid. No need to check validity of diff output here. Include newline in length of line. (main): Compute rev_mapping here. Handle -a and -m. Error message if excess -t operands. Error for incompatible options. Error if `-' given more than once. Fix error storing in tag_strings. (output_diff3): REV_MAPPING is now an arg. Call changed. Change syntax of "missing newline" message. Expect length of line to include newline. (output_diff3_edscript): Return just 0 or 1. REV_MAPPING is now an arg. Call changed. (output_diff3_merge): New function. (process_diff): Better error message for bad diff format. (fatal, perror_with_exit): Return status 2. * analyze.c (diff_2_files): Report missing newline in either or both files, if not robust output style. * util.c (setup_output): Detect error from pipe. No need to close stdin. * util.c (print_1_line): Change format of missing-newline msg. Change if statements to switch. * io.c (slurp): Don't mention differences in final newline if -B. * io.c (binary_file_p): Use ISO char set as criterion, not ASCII. * io.c (find_identical_ends): Increase value of BEG0 by 1. Other changes in backwards scan to avoid decrementing pointers before start of array, and set LINES properly. * diff.h (ROBUST_OUTPUT_STYLE): New macro. * io.c (find_identical_ends, find_and_hash_each_line): Use that macro. * diff.h (dup2): Don't define if XENIX. * diff.c (main): Check for write error at end. * context.c (find_function): Don't return a value. Use argument FILE rather than global files. * analyze.c: Add external function declarations. * analyze.c (build_script): Turn off explicit check for final newline. * analyze.c (discard_confusing_lines): Make integers unsigned. Tue Jul 31 21:37:16 1990 Richard Stallman (rms at sugar-bombs.ai.mit.edu) * io.c (find_and_hash_each_line): Correct the criterion for leaving out the newline from the end of the line. Tue May 29 21:28:16 1990 Richard Stallman (rms at sugar-bombs.ai.mit.edu) * dir.c (diff_dirs): Free things only if nonzero. Mon Apr 16 18:31:05 1990 Richard Stallman (rms at sugar-bombs.ai.mit.edu) * diff.h (NDIR_IN_SYS): New macro controls location of ndir.h. * diff3.c (xmalloc, xrealloc): Don't die if size == 0 returns 0. Sun Mar 25 15:58:42 1990 Richard Stallman (rms at sugar-bombs.ai.mit.edu) * analyze.c (discard_confusing_lines): `many' wasn't being used; use it. Cancelling provisionals near start of run must handle already cancelled provisionals. Cancelling subruns of provisionals was cancelling last nonprovisional. Sat Mar 24 14:02:51 1990 Richard Stallman (rms at sugar-bombs.ai.mit.edu) * analyze.c (discard_confusing_lines): Threshold for line occurring many times scales by square root of total lines. Within each run, cancel any long subrun of provisionals. Don't update `provisional' while cancelling provisionals. In big outer loop, handle provisional and nonprovisional separately. Thu Mar 22 16:35:33 1990 Richard Stallman (rms at sugar-bombs.ai.mit.edu) * analyze.c (discard_confusing_lines): The first loops to discard provisionals from ends failed to step. In second such loops, keep discarding all consecutive provisionals. Increase threshold for stopping discarding, and also check for consecutive nondiscardables as separate threshold. Fri Mar 16 00:33:08 1990 Richard Stallman (rms at sugar-bombs.ai.mit.edu) * diff3.c (read_diff): Pass -- as first arg to diff. * diff3.c: Include wait.h or define equivalent macros. (read_diff): Don't use stdio printing error in the inferior. Remember the pid and wait for it. Report failing status. Report failure of vfork. Sun Mar 11 17:10:32 1990 Richard Stallman (rms at sugar-bombs.ai.mit.edu) * diff3.c (main): Accept -t options and pass to output_diff3_edscript. (usage): Mention -t. (read_diff): Use vfork. (vfork): Don't use it on Sparc. * diff.h (vfork): Don't use it on Sparc. Tue Mar 6 22:37:20 1990 Richard Stallman (rms at sugar-bombs.ai.mit.edu) * diff3.c (dup2): Don't define on Xenix. * Makefile: Comments for Xenix. Thu Mar 1 17:19:23 1990 Richard Stallman (rms at sugar-bombs.ai.mit.edu) * analyze.c (diff_2_files): `message' requires three args. Fri Feb 23 10:56:50 1990 David J. MacKenzie (djm at albert.ai.mit.edu) * diff.h, util.c, diff3.c: Change 'void *' to 'VOID *', with VOID defined as void if __STDC__, char if not. Sun Feb 18 20:31:58 1990 David J. MacKenzie (djm at albert.ai.mit.edu) * Makefile: Add rules for getopt.c, getopt1.c, getopt.h. * getopt.c, getopt.h, getopt1.c: New files. * main.c (main, usage): Add long options. * analyze.c (shift_boundaries): Remove unused var 'j_end'. Thu Feb 8 02:43:16 1990 Jim Kingdon (kingdon at pogo.ai.mit.edu) * GNUmakefile: include ../Makerules before Makefile. Fri Feb 2 23:21:38 1990 Richard Stallman (rms at sugar-bombs.ai.mit.edu) * analyze.c (diif_2_files): If -B or -I, don't return 1 if all changes were ignored. Wed Jan 24 20:43:57 1990 Richard Stallman (rms at albert.ai.mit.edu) * diff3.c (fatal): Output to stderr. Thu Jan 11 00:25:56 1990 David J. MacKenzie (djm at hobbes.ai.mit.edu) * diff.c (usage): Mention -v. Wed Jan 10 16:06:38 1990 Richard Stallman (rms at sugar-bombs.ai.mit.edu) * diff3.c (output_diff3_edscript): Return number of overlaps. (main): If have overlaps, exit with status 1. Sun Dec 24 10:29:20 1989 Richard Stallman (rms at sugar-bombs.ai.mit.edu) * io.c (find_equiv_class): Fix typo that came from changing init of B to an assigment. * version.c: New file. * diff.c (main): -v prints version number. * io.c (binary_file_p): Null char implies binary file. Fri Nov 17 23:44:55 1989 Richard Stallman (rms at sugar-bombs.ai.mit.edu) * util.c (print_1_line): Fix off by 1 error. Thu Nov 16 13:51:10 1989 Richard Stallman (rms at sugar-bombs.ai.mit.edu) * util.c (xcalloc): Function deleted. * io.c (slurp): Null-terminate the buffer. * io.c (read_files): Delete unused vars. * io.c (find_equiv_class): Don't index by N if too low. * dir.c (dir_sort): Delete the extra declaration of compare_names. * diff.h: Don't declare xcalloc. Declare some other functions. * analyze.c (shift_boundaries): Test for END at end of range before indexing by it. Fix typo `preceeding' in var names. Sat Nov 11 14:04:16 1989 Richard Stallman (rms at sugar-bombs.ai.mit.edu) * diff3.c (using_to_diff3_block): Delete unused vars. (make_3way_diff, process_diff_control, read_diff, output_diff3): Likewise. Mon Nov 6 18:15:50 EST 1989 Jay Fenlason (hack@ai.mit.edu) * README Fix typo. Fri Nov 3 15:27:47 1989 Richard Stallman (rms at sugar-bombs.ai.mit.edu) * diff.c (usage): Mention -D. * ifdef.c (print_ifdef_hunk): Write comments on #else and #endif. Sun Oct 29 16:41:07 1989 Richard Stallman (rms at sugar-bombs.ai.mit.edu) * diff.c (compare_files): Don't fflush for identical files. Wed Oct 25 17:57:12 1989 Randy Smith (randy at apple-gunkies.ai.mit.edu) * diff3.c (using_to_diff3_block): When defaulting lines from FILE0, only copy up to just under the *lowest* line mentioned in the next diff. * diff3.c (fatal): Add \n to error messages. Wed Oct 25 15:05:49 1989 Richard Stallman (rms at sugar-bombs.ai.mit.edu) * Makefile (tapefiles): Add ChangeLog. Tue Oct 3 00:51:17 1989 Richard Stallman (rms at sugar-bombs.ai.mit.edu) * diff3.c (process_diff, create_diff3_block): Init ->next field. Fri Sep 29 08:16:45 1989 Richard Stallman (rms at sugar-bombs.ai.mit.edu) * util.c (line_cmp): Alter end char of line 2, not line 1. Wed Sep 20 00:12:37 1989 Richard Stallman (rms at sugar-bombs.ai.mit.edu) * Makefile (diff.tar): Expect ln to fail on some files; copy them with cp. Mon Sep 18 02:54:29 1989 Richard Stallman (rms at sugar-bombs.ai.mit.edu) * Handle -D option: * io.c (find_and_hash_each_line): Keep all lines of 1st file. * diff.c (main): Handle -D option. (compare_files): Reject -D if files spec'd are directories. * analyze.c (diff_2_files): Handle OUTPUT_IFDEF case. Fri Sep 1 20:15:50 1989 Richard Stallman (rms at sugar-bombs.ai.mit.edu) * diff.c (option_list): Rename arg VECTOR as OPTIONVEC. Mon Aug 28 17:58:27 1989 Richard Stallman (rms at sugar-bombs.ai.mit.edu) * diff.c (compare_files): Clear entire inf[i].stat. Wed Aug 23 17:48:47 1989 Richard Stallman (rms at apple-gunkies.ai.mit.edu) * io.c (find_identical_ends): Sign was backward determining where to bound the scan for the suffix. Wed Aug 16 12:49:16 1989 Richard Stallman (rms at hobbes.ai.mit.edu) * analyze.c (diff_2_files): If -q, treat all files as binary. * diff.c (main): Detect -q, record in no_details_flag. Sun Jul 30 23:12:00 1989 Richard Stallman (rms at sugar-bombs.ai.mit.edu) * diff.c (usage): New function. (main): Call it. Wed Jul 26 02:02:19 1989 Richard Stallman (rms at sugar-bombs.ai.mit.edu) * diff.c (main): Make -C imply -c. Thu Jul 20 17:57:51 1989 Chris Hanson (cph at kleph) * io.c (find_and_hash_each_line): Bug fix in context handling, introduced by last change. Fri Jul 14 17:39:20 1989 Chris Hanson (cph at kleph) * analyze.c: To make RCS work correctly on files that don't necessarily end in newline, introduce some changes that cause diffs to be sensitive to missing final newline. Because non-RCS modes don't want to be affected by these changes, they are conditional on `output_style == OUTPUT_RCS'. (diff_2_files) [OUTPUT_RCS]: Suppress the "File X missing newline" message. (build_script) [OUTPUT_RCS]: Cause the last line to compare as different if exactly one of the files is missing its final newline. * io.c (find_and_hash_each_line): Bug fix in ignore_space_change mode. Change line's length to include the newline. For OUTPUT_RCS, decrement last line's length if there is no final newline. (find_identical_ends) [OUTPUT_RCS]: If one of the files is missing a final newline, make sure it's not included in either the prefix or suffix. * util.c (print_1_line): Change line output routine to account for line length including the newline. Tue Jun 27 02:35:28 1989 Roland McGrath (roland at hobbes.ai.mit.edu) * Makefile: Inserted $(archpfx) where appropriate. Wed May 17 20:18:43 1989 Richard Stallman (rms at sugar-bombs.ai.mit.edu) * diff3.c [USG]: Include fcntl.h. * diff.h [USG]: New compilation flags HAVE_NDIR, HAVE_DIRECT. Wed Apr 26 15:35:57 1989 Richard Stallman (rms at sugar-bombs.ai.mit.edu) * dir.c (diff_dirs): Two new args, NONEX1 and NONEX2, say to pretend nonex dirs are empty. (dir_sort): New arg NONEX, likewise. * diff.c (compare_files): Pass those args. Sometimes call diff_dirs if subdir exists in just one place. Wed Apr 12 01:10:27 1989 Richard Stallman (rms at sugar-bombs.ai.mit.edu) * io.c (find_identical_ends): Set END0 *after* last char during backward scan for suffix. Sat Apr 8 15:49:49 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) * diff3.c (using_to_diff3_block): Now find high marks in files 1 and 2 through mapping off of the last difference instead of the first. * diff3.c: Many trivial changes to spelling inside comments. Fri Feb 24 12:38:03 1989 Randall Smith (randy at gluteus.ai.mit.edu) * util.c, normal.c, io.c, ed.c, dir.c, diff.h, diff.c, context.c, analyze.c, Makefile: Changed copyright header to conform with new GNU General Public license. * diff3.c: Changed copyright header to conform with new GNU General Public license. * COPYING: Made a hard link to /gp/rms/COPYING. Fri Feb 24 10:01:58 1989 Richard Stallman (rms at sugar-bombs.ai.mit.edu) * io.c (slurp): Leave 2 chars space at end of buffer, not one. (find_identical_ends): Special case if either file is empty; don't try to make a sentinel since could crash. Wed Feb 15 14:24:48 1989 Jay Fenlason (hack at apple-gunkies.ai.mit.edu) * diff3.c (message) Re-wrote routine to avoid using alloca() Wed Feb 15 06:19:14 1989 Richard Stallman (rms at sugar-bombs.ai.mit.edu) * io.c (find_identical_ends): Delete the variable `bytes'. Sun Feb 12 11:50:36 1989 Richard Stallman (rms at sugar-bombs.ai.mit.edu) * io.c (slurp): ->bufsize is nominal amount we have room for; add room for sentinel when calling xmalloc or xrealloc. * io.c (find_identical_ends): Do need overrun check in finding suffix. Fri Feb 10 01:28:15 1989 Richard Stallman (rms at sugar-bombs.ai.mit.edu) * diff.c (main): -C now takes arg to specify context length. Now -p to show C function name--Damned IEEE! Fatal error if context length spec'd twice. * ed.c (print_ed_hunk): Now special treatment only for lines containing precisely a dot and nothing else. Output `..', end the insert, substitute that one line, then resume the insert if nec. * io.c (find_and_hash_lines): When backing up over starting context, don't move past buffer-beg. * io.c (find_identical_ends): Use sentinels to make the loops faster. If files are identical, skip the 2nd loop and return quickly. (slurp): Leave 1 char extra space after each buffer. * analyze.c (diff_2_files): Mention difference in final newlines. Wed Jan 25 22:44:44 1989 Richard Stallman (rms at sugar-bombs.ai.mit.edu) * dir.c (diff_dirs): Use * when calling fcn ptr variable. Sat Dec 17 14:12:06 1988 Richard Stallman (rms at sugar-bombs.ai.mit.edu) * Makefile: New vars INSTALL and LIBS used in some rules; provide default defns plus commented-put defns for sysV. Thu Nov 17 16:42:53 1988 Richard Stallman (rms at sugar-bombs.ai.mit.edu) * dir.c (dir_sort): Open-trouble not fatal; just say # files is -1. (diff_dirs): If dir_sort does that, give up and return 2. * diff.c (compare_files): Don't open directories. Don't close them specially either. Cross-propagate inf[i].dir_p sooner. Sun Nov 13 11:19:36 1988 Richard Stallman (rms at sugar-bombs.ai.mit.edu) * diff.h: Declare index, rindex. * diff.c (compare_files): If comparing foodir with b/f, use foodir/f, not foodir/b/f. * diff.c (compare_files): Don't print "are identical" msg for 2 dirs. Status now 1 if one file is a dir and the other isn't, etc. Thu Nov 3 16:30:24 1988 Randall Smith (randy at gluteus.ai.mit.edu) * Makefile: Added a define for diff3 to define DIFF_PROGRAM. * util.c: Added hack to make sure that perror was not called with a null pointer. * diff.c: Changed S_IFDIR to S_IFMT in masking type of file bits out. * diff3.c: Included USG compatibility defines. * diff.h: Moved sys/file.h into #else USG section (not needed or wanted on System V). * ed.c, analyze.c, context.c: Shortened names to 12 characters for the sake of System V (too simple not to do). Local Variables: mode: indented-text left-margin: 8 version-control: never End: /sys/src/ape/cmd/diff/FREEBSD-upgrade 664 sys sys 1367613436 354 Import of GNU diff 2.7 Original source available as ftp://prep.ai.mit.edu/pub/gnu/diffutils-2.7.tar.gz The following files and directories were removed for this import: Makefile.in INSTALL alloca.c cmp.c diff.info diff.info-1 diff.info-2 diff.info-3 diff.info-4 error.c fnmatch.c fnmatch.h memchr.c mkinstalldirs regex.c regex.h texinfo.tex waitpid.c /sys/src/ape/cmd/diff/NEWS 664 sys sys 1367613436 4827 User-visible changes in version 2.7: * New diff option: --binary (useful only on non-Posix hosts) * diff -b and -w now ignore line incompleteness; -B no longer does this. * cmp -c now uses locale to decide which output characters to quote. * Help and version messages are reorganized. User-visible changes in version 2.6: * New cmp, diff, diff3, sdiff option: --help * A new heuristic for diff greatly reduces the time needed to compare large input files that contain many differences. * Partly as a result, GNU diff's output is not exactly the same as before. Usually it is a bit smaller, but sometimes it is a bit larger. User-visible changes in version 2.5: * New cmp option: -v --version User-visible changes in version 2.4: * New cmp option: --ignore-initial=BYTES * New diff3 option: -T --initial-tab * New diff option: --line-format=FORMAT * New diff group format specifications: [eflmnEFLMN] A printf spec followed by one of the following letters causes the integer corresponding to that letter to be printed according to the printf specification. E.g. `%5df' prints the number of the first line in the group in the old file using the "%5d" format. e: line number just before the group in old file; equals f - 1 f: first line number in group in the old file l: last line number in group in the old file m: line number just after the group in old file; equals l + 1 n: number of lines in group in the old file; equals l - f + 1 E, F, L, M, N: likewise, for lines in the new file %(A=B?T:E) If A equals B then T else E. A and B are each either a decimal constant or a single letter interpreted as above. T and E are arbitrary format strings. This format spec is equivalent to T if A's value equals B's; otherwise it is equivalent to E. For example, `%(N=0?no:%dN) line%(N=1?:s)' is equivalent to `no lines' if N (the number of lines in the group in the the new file) is 0, to `1 line' if N is 1, and to `%dN lines' otherwise. %c'C' where C is a single character, stands for the character C. C may not be a backslash or an apostrophe. E.g. %c':' stands for a colon. %c'\O' where O is a string of 1, 2, or 3 octal digits, stands for the character with octal code O. E.g. %c'\0' stands for a null character. * New diff line format specifications: n The line number, printed with . E.g. `%5dn' prints the line number with a "%5d" format. %c'C' %c'\O' The character C, or with octal code O, as above. * Supported s have the same meaning as with printf, but must match the extended regular expression %-*[0-9]*(\.[0-9]*)?[doxX]. * The format spec %0 introduced in version 2.1 has been removed, since it is incompatible with printf specs like %02d. To represent a null char, use %c'\0' instead. * cmp and diff now conform to Posix.2 (ISO/IEC 9945-2:1993) if the underlying system conforms to Posix: - Some messages' wordings are changed in minor ways. - ``White space'' is now whatever C's `isspace' says it is. - When comparing directories, if `diff' finds a file that is not a regular file or a directory, it reports the file's type instead of diffing it. (As usual, it follows symbolic links first.) - When signaled, sdiff exits with the signal's status, not with status 2. * Now portable to hosts where int, long, pointer, etc. are not all the same size. * `cmp - -' now works like `diff - -'. User-visible changes in version 2.3: * New diff option: --horizon-lines=lines User-visible changes in version 2.1: * New diff options: --{old,new,unchanged}-line-format='format' --{old,new,unchanged,changed}-group-format='format' -U * New diff3 option: -A --show-all * diff3 -m now defaults to -A, not -E. * diff3 now takes up to three -L or --label options, not just two. If just two options are given, they refer to the first two input files, not the first and third input files. * sdiff and diff -y handle incomplete lines. User-visible changes in version 2.0: * Add sdiff and cmp programs. * Add Texinfo documentation. * Add configure script. * Improve diff performance. * New diff options: -x --exclude -X --exclude-from -P --unidirectional-new-file -W --width -y --side-by-side --left-column --sdiff-merge-assist --suppress-common-lines * diff options renamed: --label renamed from --file-label --forward-ed renamed from --reversed-ed --paginate renamed from --print --entire-new-file renamed from --entire-new-files --new-file renamed from --new-files --all-text removed * New diff3 options: -v --version * Add long-named equivalents for other diff3 options. * diff options -F (--show-function-line) and -I (--ignore-matching-lines) can now be given more than once. /sys/src/ape/cmd/diff/README 664 sys sys 1367613436 424 This directory contains the GNU diff, diff3, sdiff, and cmp utilities. Their features are a superset of the Unix features and they are significantly faster. cmp has been moved here from the GNU textutils. See the file COPYING for copying conditions. See the file diff.texi (or diff.info*) for documentation. See the file INSTALL for compilation and installation instructions. Report bugs to bug-gnu-utils@prep.ai.mit.edu /sys/src/ape/cmd/diff/analyze.c 664 sys sys 1367613436 31324 /* Analyze file differences for GNU DIFF. Copyright (C) 1988, 1989, 1992, 1993 Free Software Foundation, Inc. This file is part of GNU DIFF. GNU DIFF 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. GNU DIFF 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 GNU DIFF; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ /* The basic algorithm is described in: "An O(ND) Difference Algorithm and its Variations", Eugene Myers, Algorithmica Vol. 1 No. 2, 1986, pp. 251-266; see especially section 4.2, which describes the variation used below. Unless the --minimal option is specified, this code uses the TOO_EXPENSIVE heuristic, by Paul Eggert, to limit the cost to O(N**1.5 log N) at the price of producing suboptimal output for large inputs with many differences. The basic algorithm was independently discovered as described in: "Algorithms for Approximate String Matching", E. Ukkonen, Information and Control Vol. 64, 1985, pp. 100-118. */ #include "diff.h" #include "cmpbuf.h" extern int no_discards; static int *xvec, *yvec; /* Vectors being compared. */ static int *fdiag; /* Vector, indexed by diagonal, containing 1 + the X coordinate of the point furthest along the given diagonal in the forward search of the edit matrix. */ static int *bdiag; /* Vector, indexed by diagonal, containing the X coordinate of the point furthest along the given diagonal in the backward search of the edit matrix. */ static int too_expensive; /* Edit scripts longer than this are too expensive to compute. */ #define SNAKE_LIMIT 20 /* Snakes bigger than this are considered `big'. */ struct partition { int xmid, ymid; /* Midpoints of this partition. */ int lo_minimal; /* Nonzero if low half will be analyzed minimally. */ int hi_minimal; /* Likewise for high half. */ }; static int diag PARAMS((int, int, int, int, int, struct partition *)); static struct change *add_change PARAMS((int, int, int, int, struct change *)); static struct change *build_reverse_script PARAMS((struct file_data const[])); static struct change *build_script PARAMS((struct file_data const[])); static void briefly_report PARAMS((int, struct file_data const[])); static void compareseq PARAMS((int, int, int, int, int)); static void discard_confusing_lines PARAMS((struct file_data[])); static void shift_boundaries PARAMS((struct file_data[])); /* Find the midpoint of the shortest edit script for a specified portion of the two files. Scan from the beginnings of the files, and simultaneously from the ends, doing a breadth-first search through the space of edit-sequence. When the two searches meet, we have found the midpoint of the shortest edit sequence. If MINIMAL is nonzero, find the minimal edit script regardless of expense. Otherwise, if the search is too expensive, use heuristics to stop the search and report a suboptimal answer. Set PART->(XMID,YMID) to the midpoint (XMID,YMID). The diagonal number XMID - YMID equals the number of inserted lines minus the number of deleted lines (counting only lines before the midpoint). Return the approximate edit cost; this is the total number of lines inserted or deleted (counting only lines before the midpoint), unless a heuristic is used to terminate the search prematurely. Set PART->LEFT_MINIMAL to nonzero iff the minimal edit script for the left half of the partition is known; similarly for PART->RIGHT_MINIMAL. This function assumes that the first lines of the specified portions of the two files do not match, and likewise that the last lines do not match. The caller must trim matching lines from the beginning and end of the portions it is going to specify. If we return the "wrong" partitions, the worst this can do is cause suboptimal diff output. It cannot cause incorrect diff output. */ static int diag (xoff, xlim, yoff, ylim, minimal, part) int xoff, xlim, yoff, ylim, minimal; struct partition *part; { int *const fd = fdiag; /* Give the compiler a chance. */ int *const bd = bdiag; /* Additional help for the compiler. */ int const *const xv = xvec; /* Still more help for the compiler. */ int const *const yv = yvec; /* And more and more . . . */ int const dmin = xoff - ylim; /* Minimum valid diagonal. */ int const dmax = xlim - yoff; /* Maximum valid diagonal. */ int const fmid = xoff - yoff; /* Center diagonal of top-down search. */ int const bmid = xlim - ylim; /* Center diagonal of bottom-up search. */ int fmin = fmid, fmax = fmid; /* Limits of top-down search. */ int bmin = bmid, bmax = bmid; /* Limits of bottom-up search. */ int c; /* Cost. */ int odd = (fmid - bmid) & 1; /* True if southeast corner is on an odd diagonal with respect to the northwest. */ fd[fmid] = xoff; bd[bmid] = xlim; for (c = 1;; ++c) { int d; /* Active diagonal. */ int big_snake = 0; /* Extend the top-down search by an edit step in each diagonal. */ fmin > dmin ? fd[--fmin - 1] = -1 : ++fmin; fmax < dmax ? fd[++fmax + 1] = -1 : --fmax; for (d = fmax; d >= fmin; d -= 2) { int x, y, oldx, tlo = fd[d - 1], thi = fd[d + 1]; if (tlo >= thi) x = tlo + 1; else x = thi; oldx = x; y = x - d; while (x < xlim && y < ylim && xv[x] == yv[y]) ++x, ++y; if (x - oldx > SNAKE_LIMIT) big_snake = 1; fd[d] = x; if (odd && bmin <= d && d <= bmax && bd[d] <= x) { part->xmid = x; part->ymid = y; part->lo_minimal = part->hi_minimal = 1; return 2 * c - 1; } } /* Similarly extend the bottom-up search. */ bmin > dmin ? bd[--bmin - 1] = INT_MAX : ++bmin; bmax < dmax ? bd[++bmax + 1] = INT_MAX : --bmax; for (d = bmax; d >= bmin; d -= 2) { int x, y, oldx, tlo = bd[d - 1], thi = bd[d + 1]; if (tlo < thi) x = tlo; else x = thi - 1; oldx = x; y = x - d; while (x > xoff && y > yoff && xv[x - 1] == yv[y - 1]) --x, --y; if (oldx - x > SNAKE_LIMIT) big_snake = 1; bd[d] = x; if (!odd && fmin <= d && d <= fmax && x <= fd[d]) { part->xmid = x; part->ymid = y; part->lo_minimal = part->hi_minimal = 1; return 2 * c; } } if (minimal) continue; /* Heuristic: check occasionally for a diagonal that has made lots of progress compared with the edit distance. If we have any such, find the one that has made the most progress and return it as if it had succeeded. With this heuristic, for files with a constant small density of changes, the algorithm is linear in the file size. */ if (c > 200 && big_snake && heuristic) { int best; best = 0; for (d = fmax; d >= fmin; d -= 2) { int dd = d - fmid; int x = fd[d]; int y = x - d; int v = (x - xoff) * 2 - dd; if (v > 12 * (c + (dd < 0 ? -dd : dd))) { if (v > best && xoff + SNAKE_LIMIT <= x && x < xlim && yoff + SNAKE_LIMIT <= y && y < ylim) { /* We have a good enough best diagonal; now insist that it end with a significant snake. */ int k; for (k = 1; xv[x - k] == yv[y - k]; k++) if (k == SNAKE_LIMIT) { best = v; part->xmid = x; part->ymid = y; break; } } } } if (best > 0) { part->lo_minimal = 1; part->hi_minimal = 0; return 2 * c - 1; } best = 0; for (d = bmax; d >= bmin; d -= 2) { int dd = d - bmid; int x = bd[d]; int y = x - d; int v = (xlim - x) * 2 + dd; if (v > 12 * (c + (dd < 0 ? -dd : dd))) { if (v > best && xoff < x && x <= xlim - SNAKE_LIMIT && yoff < y && y <= ylim - SNAKE_LIMIT) { /* We have a good enough best diagonal; now insist that it end with a significant snake. */ int k; for (k = 0; xv[x + k] == yv[y + k]; k++) if (k == SNAKE_LIMIT - 1) { best = v; part->xmid = x; part->ymid = y; break; } } } } if (best > 0) { part->lo_minimal = 0; part->hi_minimal = 1; return 2 * c - 1; } } /* Heuristic: if we've gone well beyond the call of duty, give up and report halfway between our best results so far. */ if (c >= too_expensive) { int fxybest, fxbest; int bxybest, bxbest; fxbest = bxbest = 0; /* Pacify `gcc -Wall'. */ /* Find forward diagonal that maximizes X + Y. */ fxybest = -1; for (d = fmax; d >= fmin; d -= 2) { int x = min (fd[d], xlim); int y = x - d; if (ylim < y) x = ylim + d, y = ylim; if (fxybest < x + y) { fxybest = x + y; fxbest = x; } } /* Find backward diagonal that minimizes X + Y. */ bxybest = INT_MAX; for (d = bmax; d >= bmin; d -= 2) { int x = max (xoff, bd[d]); int y = x - d; if (y < yoff) x = yoff + d, y = yoff; if (x + y < bxybest) { bxybest = x + y; bxbest = x; } } /* Use the better of the two diagonals. */ if ((xlim + ylim) - bxybest < fxybest - (xoff + yoff)) { part->xmid = fxbest; part->ymid = fxybest - fxbest; part->lo_minimal = 1; part->hi_minimal = 0; } else { part->xmid = bxbest; part->ymid = bxybest - bxbest; part->lo_minimal = 0; part->hi_minimal = 1; } return 2 * c - 1; } } } /* Compare in detail contiguous subsequences of the two files which are known, as a whole, to match each other. The results are recorded in the vectors files[N].changed_flag, by storing a 1 in the element for each line that is an insertion or deletion. The subsequence of file 0 is [XOFF, XLIM) and likewise for file 1. Note that XLIM, YLIM are exclusive bounds. All line numbers are origin-0 and discarded lines are not counted. If MINIMAL is nonzero, find a minimal difference no matter how expensive it is. */ static void compareseq (xoff, xlim, yoff, ylim, minimal) int xoff, xlim, yoff, ylim, minimal; { int * const xv = xvec; /* Help the compiler. */ int * const yv = yvec; /* Slide down the bottom initial diagonal. */ while (xoff < xlim && yoff < ylim && xv[xoff] == yv[yoff]) ++xoff, ++yoff; /* Slide up the top initial diagonal. */ while (xlim > xoff && ylim > yoff && xv[xlim - 1] == yv[ylim - 1]) --xlim, --ylim; /* Handle simple cases. */ if (xoff == xlim) while (yoff < ylim) files[1].changed_flag[files[1].realindexes[yoff++]] = 1; else if (yoff == ylim) while (xoff < xlim) files[0].changed_flag[files[0].realindexes[xoff++]] = 1; else { int c; struct partition part; /* Find a point of correspondence in the middle of the files. */ c = diag (xoff, xlim, yoff, ylim, minimal, &part); if (c == 1) { /* This should be impossible, because it implies that one of the two subsequences is empty, and that case was handled above without calling `diag'. Let's verify that this is true. */ abort (); #if 0 /* The two subsequences differ by a single insert or delete; record it and we are done. */ if (part.xmid - part.ymid < xoff - yoff) files[1].changed_flag[files[1].realindexes[part.ymid - 1]] = 1; else files[0].changed_flag[files[0].realindexes[part.xmid]] = 1; #endif } else { /* Use the partitions to split this problem into subproblems. */ compareseq (xoff, part.xmid, yoff, part.ymid, part.lo_minimal); compareseq (part.xmid, xlim, part.ymid, ylim, part.hi_minimal); } } } /* Discard lines from one file that have no matches in the other file. A line which is discarded will not be considered by the actual comparison algorithm; it will be as if that line were not in the file. The file's `realindexes' table maps virtual line numbers (which don't count the discarded lines) into real line numbers; this is how the actual comparison algorithm produces results that are comprehensible when the discarded lines are counted. When we discard a line, we also mark it as a deletion or insertion so that it will be printed in the output. */ static void discard_confusing_lines (filevec) struct file_data filevec[]; { unsigned int f, i; char *discarded[2]; int *equiv_count[2]; int *p; /* Allocate our results. */ p = (int *) xmalloc ((filevec[0].buffered_lines + filevec[1].buffered_lines) * (2 * sizeof (int))); for (f = 0; f < 2; f++) { filevec[f].undiscarded = p; p += filevec[f].buffered_lines; filevec[f].realindexes = p; p += filevec[f].buffered_lines; } /* Set up equiv_count[F][I] as the number of lines in file F that fall in equivalence class I. */ p = (int *) xmalloc (filevec[0].equiv_max * (2 * sizeof (int))); equiv_count[0] = p; equiv_count[1] = p + filevec[0].equiv_max; bzero (p, filevec[0].equiv_max * (2 * sizeof (int))); for (i = 0; i < filevec[0].buffered_lines; ++i) ++equiv_count[0][filevec[0].equivs[i]]; for (i = 0; i < filevec[1].buffered_lines; ++i) ++equiv_count[1][filevec[1].equivs[i]]; /* Set up tables of which lines are going to be discarded. */ discarded[0] = xmalloc (sizeof (char) * (filevec[0].buffered_lines + filevec[1].buffered_lines)); discarded[1] = discarded[0] + filevec[0].buffered_lines; bzero (discarded[0], sizeof (char) * (filevec[0].buffered_lines + filevec[1].buffered_lines)); /* Mark to be discarded each line that matches no line of the other file. If a line matches many lines, mark it as provisionally discardable. */ for (f = 0; f < 2; f++) { unsigned int end = filevec[f].buffered_lines; char *discards = discarded[f]; int *counts = equiv_count[1 - f]; int *equivs = filevec[f].equivs; unsigned int many = 5; unsigned int tem = end / 64; /* Multiply MANY by approximate square root of number of lines. That is the threshold for provisionally discardable lines. */ while ((tem = tem >> 2) > 0) many *= 2; for (i = 0; i < end; i++) { int nmatch; if (equivs[i] == 0) continue; nmatch = counts[equivs[i]]; if (nmatch == 0) discards[i] = 1; else if (nmatch > many) discards[i] = 2; } } /* Don't really discard the provisional lines except when they occur in a run of discardables, with nonprovisionals at the beginning and end. */ for (f = 0; f < 2; f++) { unsigned int end = filevec[f].buffered_lines; register char *discards = discarded[f]; for (i = 0; i < end; i++) { /* Cancel provisional discards not in middle of run of discards. */ if (discards[i] == 2) discards[i] = 0; else if (discards[i] != 0) { /* We have found a nonprovisional discard. */ register int j; unsigned int length; unsigned int provisional = 0; /* Find end of this run of discardable lines. Count how many are provisionally discardable. */ for (j = i; j < end; j++) { if (discards[j] == 0) break; if (discards[j] == 2) ++provisional; } /* Cancel provisional discards at end, and shrink the run. */ while (j > i && discards[j - 1] == 2) discards[--j] = 0, --provisional; /* Now we have the length of a run of discardable lines whose first and last are not provisional. */ length = j - i; /* If 1/4 of the lines in the run are provisional, cancel discarding of all provisional lines in the run. */ if (provisional * 4 > length) { while (j > i) if (discards[--j] == 2) discards[j] = 0; } else { register unsigned int consec; unsigned int minimum = 1; unsigned int tem = length / 4; /* MINIMUM is approximate square root of LENGTH/4. A subrun of two or more provisionals can stand when LENGTH is at least 16. A subrun of 4 or more can stand when LENGTH >= 64. */ while ((tem = tem >> 2) > 0) minimum *= 2; minimum++; /* Cancel any subrun of MINIMUM or more provisionals within the larger run. */ for (j = 0, consec = 0; j < length; j++) if (discards[i + j] != 2) consec = 0; else if (minimum == ++consec) /* Back up to start of subrun, to cancel it all. */ j -= consec; else if (minimum < consec) discards[i + j] = 0; /* Scan from beginning of run until we find 3 or more nonprovisionals in a row or until the first nonprovisional at least 8 lines in. Until that point, cancel any provisionals. */ for (j = 0, consec = 0; j < length; j++) { if (j >= 8 && discards[i + j] == 1) break; if (discards[i + j] == 2) consec = 0, discards[i + j] = 0; else if (discards[i + j] == 0) consec = 0; else consec++; if (consec == 3) break; } /* I advances to the last line of the run. */ i += length - 1; /* Same thing, from end. */ for (j = 0, consec = 0; j < length; j++) { if (j >= 8 && discards[i - j] == 1) break; if (discards[i - j] == 2) consec = 0, discards[i - j] = 0; else if (discards[i - j] == 0) consec = 0; else consec++; if (consec == 3) break; } } } } } /* Actually discard the lines. */ for (f = 0; f < 2; f++) { char *discards = discarded[f]; unsigned int end = filevec[f].buffered_lines; unsigned int j = 0; for (i = 0; i < end; ++i) if (no_discards || discards[i] == 0) { filevec[f].undiscarded[j] = filevec[f].equivs[i]; filevec[f].realindexes[j++] = i; } else filevec[f].changed_flag[i] = 1; filevec[f].nondiscarded_lines = j; } free (discarded[0]); free (equiv_count[0]); } /* Adjust inserts/deletes of identical lines to join changes as much as possible. We do something when a run of changed lines include a line at one end and have an excluded, identical line at the other. We are free to choose which identical line is included. `compareseq' usually chooses the one at the beginning, but usually it is cleaner to consider the following identical line to be the "change". */ int inhibit; static void shift_boundaries (filevec) struct file_data filevec[]; { int f; if (inhibit) return; for (f = 0; f < 2; f++) { char *changed = filevec[f].changed_flag; char const *other_changed = filevec[1-f].changed_flag; int const *equivs = filevec[f].equivs; int i = 0; int j = 0; int i_end = filevec[f].buffered_lines; while (1) { int runlength, start, corresponding; /* Scan forwards to find beginning of another run of changes. Also keep track of the corresponding point in the other file. */ while (i < i_end && changed[i] == 0) { while (other_changed[j++]) continue; i++; } if (i == i_end) break; start = i; /* Find the end of this run of changes. */ while (changed[++i]) continue; while (other_changed[j]) j++; do { /* Record the length of this run of changes, so that we can later determine whether the run has grown. */ runlength = i - start; /* Move the changed region back, so long as the previous unchanged line matches the last changed one. This merges with previous changed regions. */ while (start && equivs[start - 1] == equivs[i - 1]) { changed[--start] = 1; changed[--i] = 0; while (changed[start - 1]) start--; while (other_changed[--j]) continue; } /* Set CORRESPONDING to the end of the changed run, at the last point where it corresponds to a changed run in the other file. CORRESPONDING == I_END means no such point has been found. */ corresponding = other_changed[j - 1] ? i : i_end; /* Move the changed region forward, so long as the first changed line matches the following unchanged one. This merges with following changed regions. Do this second, so that if there are no merges, the changed region is moved forward as far as possible. */ while (i != i_end && equivs[start] == equivs[i]) { changed[start++] = 0; changed[i++] = 1; while (changed[i]) i++; while (other_changed[++j]) corresponding = i; } } while (runlength != i - start); /* If possible, move the fully-merged run of changes back to a corresponding run in the other file. */ while (corresponding < i) { changed[--start] = 1; changed[--i] = 0; while (other_changed[--j]) continue; } } } } /* Cons an additional entry onto the front of an edit script OLD. LINE0 and LINE1 are the first affected lines in the two files (origin 0). DELETED is the number of lines deleted here from file 0. INSERTED is the number of lines inserted here in file 1. If DELETED is 0 then LINE0 is the number of the line before which the insertion was done; vice versa for INSERTED and LINE1. */ static struct change * add_change (line0, line1, deleted, inserted, old) int line0, line1, deleted, inserted; struct change *old; { struct change *new = (struct change *) xmalloc (sizeof (struct change)); new->line0 = line0; new->line1 = line1; new->inserted = inserted; new->deleted = deleted; new->link = old; return new; } /* Scan the tables of which lines are inserted and deleted, producing an edit script in reverse order. */ static struct change * build_reverse_script (filevec) struct file_data const filevec[]; { struct change *script = 0; char *changed0 = filevec[0].changed_flag; char *changed1 = filevec[1].changed_flag; int len0 = filevec[0].buffered_lines; int len1 = filevec[1].buffered_lines; /* Note that changedN[len0] does exist, and contains 0. */ int i0 = 0, i1 = 0; while (i0 < len0 || i1 < len1) { if (changed0[i0] || changed1[i1]) { int line0 = i0, line1 = i1; /* Find # lines changed here in each file. */ while (changed0[i0]) ++i0; while (changed1[i1]) ++i1; /* Record this change. */ script = add_change (line0, line1, i0 - line0, i1 - line1, script); } /* We have reached lines in the two files that match each other. */ i0++, i1++; } return script; } /* Scan the tables of which lines are inserted and deleted, producing an edit script in forward order. */ static struct change * build_script (filevec) struct file_data const filevec[]; { struct change *script = 0; char *changed0 = filevec[0].changed_flag; char *changed1 = filevec[1].changed_flag; int i0 = filevec[0].buffered_lines, i1 = filevec[1].buffered_lines; /* Note that changedN[-1] does exist, and contains 0. */ while (i0 >= 0 || i1 >= 0) { if (changed0[i0 - 1] || changed1[i1 - 1]) { int line0 = i0, line1 = i1; /* Find # lines changed here in each file. */ while (changed0[i0 - 1]) --i0; while (changed1[i1 - 1]) --i1; /* Record this change. */ script = add_change (i0, i1, line0 - i0, line1 - i1, script); } /* We have reached lines in the two files that match each other. */ i0--, i1--; } return script; } /* If CHANGES, briefly report that two files differed. */ static void briefly_report (changes, filevec) int changes; struct file_data const filevec[]; { if (changes) message (no_details_flag ? "Files %s and %s differ\n" : "Binary files %s and %s differ\n", filevec[0].name, filevec[1].name); } /* Report the differences of two files. DEPTH is the current directory depth. */ int diff_2_files (filevec, depth) struct file_data filevec[]; int depth; { int diags; int i; struct change *e, *p; struct change *script; int changes; /* If we have detected that either file is binary, compare the two files as binary. This can happen only when the first chunk is read. Also, --brief without any --ignore-* options means we can speed things up by treating the files as binary. */ if (read_files (filevec, no_details_flag & ~ignore_some_changes)) { /* Files with different lengths must be different. */ if (filevec[0].stat.st_size != filevec[1].stat.st_size && (filevec[0].desc < 0 || S_ISREG (filevec[0].stat.st_mode)) && (filevec[1].desc < 0 || S_ISREG (filevec[1].stat.st_mode))) changes = 1; /* Standard input equals itself. */ else if (filevec[0].desc == filevec[1].desc) changes = 0; else /* Scan both files, a buffer at a time, looking for a difference. */ { /* Allocate same-sized buffers for both files. */ size_t buffer_size = buffer_lcm (STAT_BLOCKSIZE (filevec[0].stat), STAT_BLOCKSIZE (filevec[1].stat)); for (i = 0; i < 2; i++) filevec[i].buffer = xrealloc (filevec[i].buffer, buffer_size); for (;; filevec[0].buffered_chars = filevec[1].buffered_chars = 0) { /* Read a buffer's worth from both files. */ for (i = 0; i < 2; i++) if (0 <= filevec[i].desc) while (filevec[i].buffered_chars != buffer_size) { int r = read (filevec[i].desc, filevec[i].buffer + filevec[i].buffered_chars, buffer_size - filevec[i].buffered_chars); if (r == 0) break; if (r < 0) pfatal_with_name (filevec[i].name); filevec[i].buffered_chars += r; } /* If the buffers differ, the files differ. */ if (filevec[0].buffered_chars != filevec[1].buffered_chars || (filevec[0].buffered_chars != 0 && memcmp (filevec[0].buffer, filevec[1].buffer, filevec[0].buffered_chars) != 0)) { changes = 1; break; } /* If we reach end of file, the files are the same. */ if (filevec[0].buffered_chars != buffer_size) { changes = 0; break; } } } briefly_report (changes, filevec); } else { /* Allocate vectors for the results of comparison: a flag for each line of each file, saying whether that line is an insertion or deletion. Allocate an extra element, always zero, at each end of each vector. */ size_t s = filevec[0].buffered_lines + filevec[1].buffered_lines + 4; filevec[0].changed_flag = xmalloc (s); bzero (filevec[0].changed_flag, s); filevec[0].changed_flag++; filevec[1].changed_flag = filevec[0].changed_flag + filevec[0].buffered_lines + 2; /* Some lines are obviously insertions or deletions because they don't match anything. Detect them now, and avoid even thinking about them in the main comparison algorithm. */ discard_confusing_lines (filevec); /* Now do the main comparison algorithm, considering just the undiscarded lines. */ xvec = filevec[0].undiscarded; yvec = filevec[1].undiscarded; diags = filevec[0].nondiscarded_lines + filevec[1].nondiscarded_lines + 3; fdiag = (int *) xmalloc (diags * (2 * sizeof (int))); bdiag = fdiag + diags; fdiag += filevec[1].nondiscarded_lines + 1; bdiag += filevec[1].nondiscarded_lines + 1; /* Set TOO_EXPENSIVE to be approximate square root of input size, bounded below by 256. */ too_expensive = 1; for (i = filevec[0].nondiscarded_lines + filevec[1].nondiscarded_lines; i != 0; i >>= 2) too_expensive <<= 1; too_expensive = max (256, too_expensive); files[0] = filevec[0]; files[1] = filevec[1]; compareseq (0, filevec[0].nondiscarded_lines, 0, filevec[1].nondiscarded_lines, no_discards); free (fdiag - (filevec[1].nondiscarded_lines + 1)); /* Modify the results slightly to make them prettier in cases where that can validly be done. */ shift_boundaries (filevec); /* Get the results of comparison in the form of a chain of `struct change's -- an edit script. */ if (output_style == OUTPUT_ED) script = build_reverse_script (filevec); else script = build_script (filevec); /* Set CHANGES if we had any diffs. If some changes are ignored, we must scan the script to decide. */ if (ignore_blank_lines_flag || ignore_regexp_list) { struct change *next = script; changes = 0; while (next && changes == 0) { struct change *this, *end; int first0, last0, first1, last1, deletes, inserts; /* Find a set of changes that belong together. */ this = next; end = find_change (next); /* Disconnect them from the rest of the changes, making them a hunk, and remember the rest for next iteration. */ next = end->link; end->link = 0; /* Determine whether this hunk is really a difference. */ analyze_hunk (this, &first0, &last0, &first1, &last1, &deletes, &inserts); /* Reconnect the script so it will all be freed properly. */ end->link = next; if (deletes || inserts) changes = 1; } } else changes = (script != 0); if (no_details_flag) briefly_report (changes, filevec); else { if (changes || ! no_diff_means_no_output) { /* Record info for starting up output, to be used if and when we have some output to print. */ setup_output (files[0].name, files[1].name, depth); switch (output_style) { case OUTPUT_CONTEXT: print_context_script (script, 0); break; case OUTPUT_UNIFIED: print_context_script (script, 1); break; case OUTPUT_ED: print_ed_script (script); break; case OUTPUT_FORWARD_ED: pr_forward_ed_script (script); break; case OUTPUT_RCS: print_rcs_script (script); break; case OUTPUT_NORMAL: print_normal_script (script); break; case OUTPUT_IFDEF: print_ifdef_script (script); break; case OUTPUT_SDIFF: print_sdiff_script (script); } finish_output (); } } free (filevec[0].undiscarded); free (filevec[0].changed_flag - 1); for (i = 1; i >= 0; --i) free (filevec[i].equivs); for (i = 0; i < 2; ++i) free (filevec[i].linbuf + filevec[i].linbuf_base); for (e = script; e; e = p) { p = e->link; free (e); } if (! ROBUST_OUTPUT_STYLE (output_style)) for (i = 0; i < 2; ++i) if (filevec[i].missing_newline) { error ("No newline at end of file %s", filevec[i].name, ""); changes = 2; } } if (filevec[0].buffer != filevec[1].buffer) free (filevec[0].buffer); free (filevec[1].buffer); return changes; } /sys/src/ape/cmd/diff/cmpbuf.c 664 sys sys 1367613436 1185 /* Buffer primitives for comparison operations. Copyright (C) 1993 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "system.h" #include "cmpbuf.h" /* Least common multiple of two buffer sizes A and B. */ size_t buffer_lcm (a, b) size_t a, b; { size_t m, n, r; /* Yield reasonable values if buffer sizes are zero. */ if (!a) return b ? b : 8 * 1024; if (!b) return a; /* n = gcd (a, b) */ for (m = a, n = b; (r = m % n) != 0; m = n, n = r) continue; return a/n * b; } /sys/src/ape/cmd/diff/cmpbuf.h 664 sys sys 1367613436 833 /* Buffer primitives for comparison operations. Copyright (C) 1993 Free Software Foundation, Inc. This file is part of GNU DIFF. GNU DIFF 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. GNU DIFF 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 GNU DIFF; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ size_t buffer_lcm PARAMS((size_t, size_t)); /sys/src/ape/cmd/diff/config.h 664 sys sys 1367613436 3406 /* config.h. Generated automatically by configure. */ /* config.hin. Generated automatically from configure.in by autoheader. */ /* Define if using alloca.c. */ /* #undef C_ALLOCA */ /* Define if the closedir function returns void instead of int. */ /* #undef CLOSEDIR_VOID */ /* Define to empty if the keyword does not work. */ /* #undef const */ /* Define to one of _getb67, GETB67, getb67 for Cray-2 and Cray-YMP systems. This function is required for alloca.c support on those systems. */ /* #undef CRAY_STACKSEG_END */ /* Define if you have and it should be used (not on Ultrix). */ /* #undef HAVE_ALLOCA_H */ /* Define if you don't have vprintf but do have _doprnt. */ /* #undef HAVE_DOPRNT */ /* Define if your struct stat has st_blksize. */ /* #define HAVE_ST_BLKSIZE 1 */ /* Define if you have . */ /* #undef HAVE_VFORK_H */ /* Define if you have the vprintf function. */ #define HAVE_VPRINTF 1 /* Define if on MINIX. */ /* #undef _MINIX */ /* Define to `int' if doesn't define. */ /* #undef pid_t */ /* Define if the system does not provide POSIX.1 features except with this defined. */ /* #undef _POSIX_1_SOURCE */ /* 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 /* If using the C implementation of alloca, define if you know the direction of stack growth for your system; otherwise it will be automatically deduced at run-time. STACK_DIRECTION > 0 => grows toward higher addresses STACK_DIRECTION < 0 => grows toward lower addresses STACK_DIRECTION = 0 => direction of growth unknown */ /* #undef STACK_DIRECTION */ /* Define if the `S_IS*' macros in do not work properly. */ /* #undef STAT_MACROS_BROKEN */ /* Define if you have the ANSI C header files. */ #define STDC_HEADERS 1 /* Define if is compatible with Posix applications. */ #define HAVE_SYS_WAIT_H 1 /* Define vfork as fork if vfork does not work. */ /* #undef vfork */ /* Define if you have the dup2 function. */ #define HAVE_DUP2 1 /* Define if you have the memchr function. */ #define HAVE_MEMCHR 1 /* Define if you have the sigaction function. */ #define HAVE_SIGACTION 1 /* Define if you have the strchr function. */ #define HAVE_STRCHR 1 /* Define if you have the strerror function. */ #define HAVE_STRERROR 1 /* Define if you have the tmpnam function. */ #define HAVE_TMPNAM 1 /* Define if you have the header file. */ #define HAVE_DIRENT_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. */ /* #undef HAVE_NDIR_H */ /* 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. */ /* #undef HAVE_SYS_DIR_H */ /* Define if you have the header file. */ #define HAVE_SYS_FILE_H 1 /* Define if you have the header file. */ /* #undef HAVE_SYS_NDIR_H */ /* Define if you have the header file. */ #define HAVE_TIME_H 1 /* Define if you have the header file. */ #define HAVE_UNISTD_H 1 /sys/src/ape/cmd/diff/context.c 664 sys sys 1367613436 13241 /* Context-format output routines for GNU DIFF. Copyright (C) 1988,1989,1991,1992,1993,1994 Free Software Foundation, Inc. This file is part of GNU DIFF. GNU DIFF 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. GNU DIFF 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 GNU DIFF; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "diff.h" static struct change *find_hunk PARAMS((struct change *)); static void find_function PARAMS((struct file_data const *, int, char const **, size_t *)); static void mark_ignorable PARAMS((struct change *)); static void pr_context_hunk PARAMS((struct change *)); static void pr_unidiff_hunk PARAMS((struct change *)); static void print_context_label PARAMS ((char const *, struct file_data *, char const *)); static void print_context_number_range PARAMS((struct file_data const *, int, int)); static void print_unidiff_number_range PARAMS((struct file_data const *, int, int)); /* Last place find_function started searching from. */ static int find_function_last_search; /* The value find_function returned when it started searching there. */ static int find_function_last_match; /* Print a label for a context diff, with a file name and date or a label. */ static void print_context_label (mark, inf, label) char const *mark; struct file_data *inf; char const *label; { if (label) fprintf (outfile, "%s %s\n", mark, label); else { char const *ct = ctime (&inf->stat.st_mtime); if (!ct) ct = "?\n"; /* See Posix.2 section 4.17.6.1.4 for this format. */ fprintf (outfile, "%s %s\t%s", mark, inf->name, ct); } } /* Print a header for a context diff, with the file names and dates. */ void print_context_header (inf, unidiff_flag) struct file_data inf[]; int unidiff_flag; { if (unidiff_flag) { print_context_label ("---", &inf[0], file_label[0]); print_context_label ("+++", &inf[1], file_label[1]); } else { print_context_label ("***", &inf[0], file_label[0]); print_context_label ("---", &inf[1], file_label[1]); } } /* Print an edit script in context format. */ void print_context_script (script, unidiff_flag) struct change *script; int unidiff_flag; { if (ignore_blank_lines_flag || ignore_regexp_list) mark_ignorable (script); else { struct change *e; for (e = script; e; e = e->link) e->ignore = 0; } find_function_last_search = - files[0].prefix_lines; find_function_last_match = find_function_last_search - 1; if (unidiff_flag) print_script (script, find_hunk, pr_unidiff_hunk); else print_script (script, find_hunk, pr_context_hunk); } /* Print a pair of line numbers with a comma, translated for file FILE. If the second number is not greater, use the first in place of it. Args A and B are internal line numbers. We print the translated (real) line numbers. */ static void print_context_number_range (file, a, b) struct file_data const *file; int a, b; { int trans_a, trans_b; translate_range (file, a, b, &trans_a, &trans_b); /* Note: we can have B < A in the case of a range of no lines. In this case, we should print the line number before the range, which is B. */ if (trans_b > trans_a) fprintf (outfile, "%d,%d", trans_a, trans_b); else fprintf (outfile, "%d", trans_b); } /* Print a portion of an edit script in context format. HUNK is the beginning of the portion to be printed. The end is marked by a `link' that has been nulled out. Prints out lines from both files, and precedes each line with the appropriate flag-character. */ static void pr_context_hunk (hunk) struct change *hunk; { int first0, last0, first1, last1, show_from, show_to, i; struct change *next; char const *prefix; char const *function; size_t function_length; FILE *out; /* Determine range of line numbers involved in each file. */ analyze_hunk (hunk, &first0, &last0, &first1, &last1, &show_from, &show_to); if (!show_from && !show_to) return; /* Include a context's width before and after. */ i = - files[0].prefix_lines; first0 = max (first0 - context, i); first1 = max (first1 - context, i); last0 = min (last0 + context, files[0].valid_lines - 1); last1 = min (last1 + context, files[1].valid_lines - 1); /* If desired, find the preceding function definition line in file 0. */ function = 0; if (function_regexp_list) find_function (&files[0], first0, &function, &function_length); begin_output (); out = outfile; /* If we looked for and found a function this is part of, include its name in the header of the diff section. */ fprintf (out, "***************"); if (function) { fprintf (out, " "); fwrite (function, 1, min (function_length - 1, 40), out); } fprintf (out, "\n*** "); print_context_number_range (&files[0], first0, last0); fprintf (out, " ****\n"); if (show_from) { next = hunk; for (i = first0; i <= last0; i++) { /* Skip past changes that apply (in file 0) only to lines before line I. */ while (next && next->line0 + next->deleted <= i) next = next->link; /* Compute the marking for line I. */ prefix = " "; if (next && next->line0 <= i) /* The change NEXT covers this line. If lines were inserted here in file 1, this is "changed". Otherwise it is "deleted". */ prefix = (next->inserted > 0 ? "!" : "-"); print_1_line (prefix, &files[0].linbuf[i]); } } fprintf (out, "--- "); print_context_number_range (&files[1], first1, last1); fprintf (out, " ----\n"); if (show_to) { next = hunk; for (i = first1; i <= last1; i++) { /* Skip past changes that apply (in file 1) only to lines before line I. */ while (next && next->line1 + next->inserted <= i) next = next->link; /* Compute the marking for line I. */ prefix = " "; if (next && next->line1 <= i) /* The change NEXT covers this line. If lines were deleted here in file 0, this is "changed". Otherwise it is "inserted". */ prefix = (next->deleted > 0 ? "!" : "+"); print_1_line (prefix, &files[1].linbuf[i]); } } } /* Print a pair of line numbers with a comma, translated for file FILE. If the second number is smaller, use the first in place of it. If the numbers are equal, print just one number. Args A and B are internal line numbers. We print the translated (real) line numbers. */ static void print_unidiff_number_range (file, a, b) struct file_data const *file; int a, b; { int trans_a, trans_b; translate_range (file, a, b, &trans_a, &trans_b); /* Note: we can have B < A in the case of a range of no lines. In this case, we should print the line number before the range, which is B. */ if (trans_b <= trans_a) fprintf (outfile, trans_b == trans_a ? "%d" : "%d,0", trans_b); else fprintf (outfile, "%d,%d", trans_a, trans_b - trans_a + 1); } /* Print a portion of an edit script in unidiff format. HUNK is the beginning of the portion to be printed. The end is marked by a `link' that has been nulled out. Prints out lines from both files, and precedes each line with the appropriate flag-character. */ static void pr_unidiff_hunk (hunk) struct change *hunk; { int first0, last0, first1, last1, show_from, show_to, i, j, k; struct change *next; char const *function; size_t function_length; FILE *out; /* Determine range of line numbers involved in each file. */ analyze_hunk (hunk, &first0, &last0, &first1, &last1, &show_from, &show_to); if (!show_from && !show_to) return; /* Include a context's width before and after. */ i = - files[0].prefix_lines; first0 = max (first0 - context, i); first1 = max (first1 - context, i); last0 = min (last0 + context, files[0].valid_lines - 1); last1 = min (last1 + context, files[1].valid_lines - 1); /* If desired, find the preceding function definition line in file 0. */ function = 0; if (function_regexp_list) find_function (&files[0], first0, &function, &function_length); begin_output (); out = outfile; fprintf (out, "@@ -"); print_unidiff_number_range (&files[0], first0, last0); fprintf (out, " +"); print_unidiff_number_range (&files[1], first1, last1); fprintf (out, " @@"); /* If we looked for and found a function this is part of, include its name in the header of the diff section. */ if (function) { putc (' ', out); fwrite (function, 1, min (function_length - 1, 40), out); } putc ('\n', out); next = hunk; i = first0; j = first1; while (i <= last0 || j <= last1) { /* If the line isn't a difference, output the context from file 0. */ if (!next || i < next->line0) { putc (tab_align_flag ? '\t' : ' ', out); print_1_line (0, &files[0].linbuf[i++]); j++; } else { /* For each difference, first output the deleted part. */ k = next->deleted; while (k--) { putc ('-', out); if (tab_align_flag) putc ('\t', out); print_1_line (0, &files[0].linbuf[i++]); } /* Then output the inserted part. */ k = next->inserted; while (k--) { putc ('+', out); if (tab_align_flag) putc ('\t', out); print_1_line (0, &files[1].linbuf[j++]); } /* We're done with this hunk, so on to the next! */ next = next->link; } } } /* Scan a (forward-ordered) edit script for the first place that more than 2*CONTEXT unchanged lines appear, and return a pointer to the `struct change' for the last change before those lines. */ static struct change * find_hunk (start) struct change *start; { struct change *prev; int top0, top1; int thresh; do { /* Compute number of first line in each file beyond this changed. */ top0 = start->line0 + start->deleted; top1 = start->line1 + start->inserted; prev = start; start = start->link; /* Threshold distance is 2*CONTEXT between two non-ignorable changes, but only CONTEXT if one is ignorable. */ thresh = ((prev->ignore || (start && start->ignore)) ? context : 2 * context + 1); /* It is not supposed to matter which file we check in the end-test. If it would matter, crash. */ if (start && start->line0 - top0 != start->line1 - top1) abort (); } while (start /* Keep going if less than THRESH lines elapse before the affected line. */ && start->line0 < top0 + thresh); return prev; } /* Set the `ignore' flag properly in each change in SCRIPT. It should be 1 if all the lines inserted or deleted in that change are ignorable lines. */ static void mark_ignorable (script) struct change *script; { while (script) { struct change *next = script->link; int first0, last0, first1, last1, deletes, inserts; /* Turn this change into a hunk: detach it from the others. */ script->link = 0; /* Determine whether this change is ignorable. */ analyze_hunk (script, &first0, &last0, &first1, &last1, &deletes, &inserts); /* Reconnect the chain as before. */ script->link = next; /* If the change is ignorable, mark it. */ script->ignore = (!deletes && !inserts); /* Advance to the following change. */ script = next; } } /* Find the last function-header line in FILE prior to line number LINENUM. This is a line containing a match for the regexp in `function_regexp'. Store the address of the line text into LINEP and the length of the line into LENP. Do not store anything if no function-header is found. */ static void find_function (file, linenum, linep, lenp) struct file_data const *file; int linenum; char const **linep; size_t *lenp; { int i = linenum; int last = find_function_last_search; find_function_last_search = i; while (--i >= last) { /* See if this line is what we want. */ struct regexp_list *r; char const *line = file->linbuf[i]; size_t len = file->linbuf[i + 1] - line; for (r = function_regexp_list; r; r = r->next) if (0 <= re_search (&r->buf, line, len, 0, len, 0)) { *linep = line; *lenp = len; find_function_last_match = i; return; } } /* If we search back to where we started searching the previous time, find the line we found last time. */ if (find_function_last_match >= - file->prefix_lines) { i = find_function_last_match; *linep = file->linbuf[i]; *lenp = file->linbuf[i + 1] - *linep; return; } return; } /sys/src/ape/cmd/diff/diagmeet.note 664 sys sys 1367613436 1069 Here is a comparison matrix which shows a case in which it is possible for the forward and backward scan in `diag' to meet along a nonzero length of diagonal simultaneous (so that bdiag[d] and fdiag[d] are not equal) even though there is no snake on that diagonal at the meeting point. 85 1 1 1 159 1 1 17 1 2 3 4 60 1 2 1 2 2 3 4 71 3 3 4 5 85 4 3 4 5 17 5 4 5 1 6 4 5 6 183 7 5 6 7 10 8 6 7 1 9 6 7 8 12 7 8 9 10 13 10 8 9 10 14 10 9 10 17 10 10 1 10 9 10 1 8 10 10 10 183 8 7 9 9 9 10 7 6 8 9 8 8 1 6 5 7 7 1 5 6 6 1 5 5 5 50 5 4 4 4 1 4 3 3 85 5 4 3 2 2 1 2 1 17 5 4 3 2 1 1 1 1 0 85 1 1 1 159 1 1 17 /sys/src/ape/cmd/diff/diff.c 664 sys sys 1367613436 30831 /* GNU DIFF main routine. Copyright (C) 1988, 1989, 1992, 1993, 1994 Free Software Foundation, Inc. This file is part of GNU DIFF. GNU DIFF 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. GNU DIFF 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 GNU DIFF; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ /* GNU DIFF was written by Mike Haertel, David Hayes, Richard Stallman, Len Tower, and Paul Eggert. */ /* $FreeBSD: src/contrib/diff/diff.c,v 1.3 1999/11/26 02:51:44 obrien Exp $ */ #define GDIFF_MAIN #include "diff.h" #include #include "getopt.h" #ifdef __FreeBSD__ #include #include #else #include "fnmatch.h" #endif #include "prepend_args.h" #ifndef DEFAULT_WIDTH #define DEFAULT_WIDTH 130 #endif #ifndef GUTTER_WIDTH_MINIMUM #define GUTTER_WIDTH_MINIMUM 3 #endif static char const *filetype PARAMS((struct stat const *)); static char *option_list PARAMS((char **, int)); static int add_exclude_file PARAMS((char const *)); static int ck_atoi PARAMS((char const *, int *)); static int compare_files PARAMS((char const *, char const *, char const *, char const *, int)); static int specify_format PARAMS((char **, char *)); static void add_exclude PARAMS((char const *)); static void add_regexp PARAMS((struct regexp_list **, char const *)); static void specify_style PARAMS((enum output_style)); static void try_help PARAMS((char const *)); static void check_stdout PARAMS((void)); static void usage PARAMS((void)); /* Nonzero for -r: if comparing two directories, compare their common subdirectories recursively. */ static int recursive; /* For debugging: don't do discard_confusing_lines. */ int no_discards; #if HAVE_SETMODE /* I/O mode: nonzero only if using binary input/output. */ static int binary_I_O; #endif /* Return a string containing the command options with which diff was invoked. Spaces appear between what were separate ARGV-elements. There is a space at the beginning but none at the end. If there were no options, the result is an empty string. Arguments: OPTIONVEC, a vector containing separate ARGV-elements, and COUNT, the length of that vector. */ static char * option_list (optionvec, count) char **optionvec; /* Was `vector', but that collides on Alliant. */ int count; { int i; size_t length = 0; char *result; for (i = 0; i < count; i++) length += strlen (optionvec[i]) + 1; result = xmalloc (length + 1); result[0] = 0; for (i = 0; i < count; i++) { strcat (result, " "); strcat (result, optionvec[i]); } return result; } /* Convert STR to a positive integer, storing the result in *OUT. If STR is not a valid integer, return -1 (otherwise 0). */ static int ck_atoi (str, out) char const *str; int *out; { char const *p; for (p = str; *p; p++) if (*p < '0' || *p > '9') return -1; *out = atoi (optarg); return 0; } /* Keep track of excluded file name patterns. */ static char const **exclude; static int exclude_alloc, exclude_count; int excluded_filename (f) char const *f; { int i; for (i = 0; i < exclude_count; i++) if (fnmatch (exclude[i], f, 0) == 0) return 1; return 0; } static void add_exclude (pattern) char const *pattern; { if (exclude_alloc <= exclude_count) exclude = (char const **) (exclude_alloc == 0 ? xmalloc ((exclude_alloc = 64) * sizeof (*exclude)) : xrealloc (exclude, (exclude_alloc *= 2) * sizeof (*exclude))); exclude[exclude_count++] = pattern; } static int add_exclude_file (name) char const *name; { struct file_data f; char *p, *q, *lim; f.name = optarg; f.desc = (strcmp (name, "-") == 0 ? STDIN_FILENO : open (name, O_RDONLY, 0)); if (f.desc < 0 || fstat (f.desc, &f.stat) != 0) return -1; sip (&f, 1); slurp (&f); for (p = f.buffer, lim = p + f.buffered_chars; p < lim; p = q) { q = (char *) memchr (p, '\n', lim - p); if (!q) q = lim; *q++ = 0; add_exclude (p); } return close (f.desc); } /* The numbers 129- that appear in the fourth element of some entries tell the big switch in `main' how to process those options. */ static struct option const longopts[] = { {"ignore-blank-lines", 0, 0, 'B'}, {"context", 2, 0, 'C'}, {"ifdef", 1, 0, 'D'}, {"show-function-line", 1, 0, 'F'}, {"speed-large-files", 0, 0, 'H'}, {"ignore-matching-lines", 1, 0, 'I'}, {"label", 1, 0, 'L'}, {"file-label", 1, 0, 'L'}, /* An alias, no longer recommended */ {"new-file", 0, 0, 'N'}, {"entire-new-file", 0, 0, 'N'}, /* An alias, no longer recommended */ {"unidirectional-new-file", 0, 0, 'P'}, {"starting-file", 1, 0, 'S'}, {"initial-tab", 0, 0, 'T'}, {"width", 1, 0, 'W'}, {"text", 0, 0, 'a'}, {"ascii", 0, 0, 'a'}, /* An alias, no longer recommended */ {"ignore-space-change", 0, 0, 'b'}, {"minimal", 0, 0, 'd'}, {"ed", 0, 0, 'e'}, {"forward-ed", 0, 0, 'f'}, {"ignore-case", 0, 0, 'i'}, {"paginate", 0, 0, 'l'}, {"print", 0, 0, 'l'}, /* An alias, no longer recommended */ {"rcs", 0, 0, 'n'}, {"show-c-function", 0, 0, 'p'}, {"brief", 0, 0, 'q'}, {"recursive", 0, 0, 'r'}, {"report-identical-files", 0, 0, 's'}, {"expand-tabs", 0, 0, 't'}, {"version", 0, 0, 'v'}, {"ignore-all-space", 0, 0, 'w'}, {"exclude", 1, 0, 'x'}, {"exclude-from", 1, 0, 'X'}, {"side-by-side", 0, 0, 'y'}, {"unified", 2, 0, 'U'}, {"left-column", 0, 0, 129}, {"suppress-common-lines", 0, 0, 130}, {"sdiff-merge-assist", 0, 0, 131}, {"old-line-format", 1, 0, 132}, {"new-line-format", 1, 0, 133}, {"unchanged-line-format", 1, 0, 134}, {"line-format", 1, 0, 135}, {"old-group-format", 1, 0, 136}, {"new-group-format", 1, 0, 137}, {"unchanged-group-format", 1, 0, 138}, {"changed-group-format", 1, 0, 139}, {"horizon-lines", 1, 0, 140}, {"help", 0, 0, 141}, {"binary", 0, 0, 142}, {0, 0, 0, 0} }; int main (argc, argv) int argc; char *argv[]; { int val; int c; int prev = -1; int width = DEFAULT_WIDTH; int show_c_function = 0; #ifdef __FreeBSD__ setlocale(LC_ALL, ""); #endif /* Do our initializations. */ initialize_main (&argc, &argv); program_name = argv[0]; output_style = OUTPUT_NORMAL; context = -1; prepend_default_options (getenv ("DIFF_OPTIONS"), &argc, &argv); /* Decode the options. */ while ((c = getopt_long (argc, argv, "0123456789abBcC:dD:efF:hHiI:lL:nNopPqrsS:tTuU:vwW:x:X:y", longopts, 0)) != EOF) { switch (c) { /* All digits combine in decimal to specify the context-size. */ case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '0': if (context == -1) context = 0; /* If a context length has already been specified, more digits allowed only if they follow right after the others. Reject two separate runs of digits, or digits after -C. */ else if (prev < '0' || prev > '9') fatal ("context length specified twice"); context = context * 10 + c - '0'; break; case 'a': /* Treat all files as text files; never treat as binary. */ always_text_flag = 1; break; case 'b': /* Ignore changes in amount of white space. */ ignore_space_change_flag = 1; ignore_some_changes = 1; ignore_some_line_changes = 1; break; case 'B': /* Ignore changes affecting only blank lines. */ ignore_blank_lines_flag = 1; ignore_some_changes = 1; break; case 'C': /* +context[=lines] */ case 'U': /* +unified[=lines] */ if (optarg) { if (context >= 0) fatal ("context length specified twice"); if (ck_atoi (optarg, &context)) fatal ("invalid context length argument"); } /* Falls through. */ case 'c': /* Make context-style output. */ specify_style (c == 'U' ? OUTPUT_UNIFIED : OUTPUT_CONTEXT); break; case 'd': /* Don't discard lines. This makes things slower (sometimes much slower) but will find a guaranteed minimal set of changes. */ no_discards = 1; break; case 'D': /* Make merged #ifdef output. */ specify_style (OUTPUT_IFDEF); { int i, err = 0; static char const C_ifdef_group_formats[] = "#ifndef %s\n%%<#endif /* not %s */\n%c#ifdef %s\n%%>#endif /* %s */\n%c%%=%c#ifndef %s\n%%<#else /* %s */\n%%>#endif /* %s */\n"; char *b = xmalloc (sizeof (C_ifdef_group_formats) + 7 * strlen(optarg) - 14 /* 7*"%s" */ - 8 /* 5*"%%" + 3*"%c" */); sprintf (b, C_ifdef_group_formats, optarg, optarg, 0, optarg, optarg, 0, 0, optarg, optarg, optarg); for (i = 0; i < 4; i++) { err |= specify_format (&group_format[i], b); b += strlen (b) + 1; } if (err) error ("conflicting #ifdef formats", 0, 0); } break; case 'e': /* Make output that is a valid `ed' script. */ specify_style (OUTPUT_ED); break; case 'f': /* Make output that looks vaguely like an `ed' script but has changes in the order they appear in the file. */ specify_style (OUTPUT_FORWARD_ED); break; case 'F': /* Show, for each set of changes, the previous line that matches the specified regexp. Currently affects only context-style output. */ add_regexp (&function_regexp_list, optarg); break; case 'h': /* Split the files into chunks of around 1500 lines for faster processing. Usually does not change the result. This currently has no effect. */ break; case 'H': /* Turn on heuristics that speed processing of large files with a small density of changes. */ heuristic = 1; break; case 'i': /* Ignore changes in case. */ ignore_case_flag = 1; ignore_some_changes = 1; ignore_some_line_changes = 1; break; case 'I': /* Ignore changes affecting only lines that match the specified regexp. */ add_regexp (&ignore_regexp_list, optarg); ignore_some_changes = 1; break; case 'l': /* Pass the output through `pr' to paginate it. */ paginate_flag = 1; #if !defined(SIGCHLD) && defined(SIGCLD) #define SIGCHLD SIGCLD #endif #ifdef SIGCHLD /* Pagination requires forking and waiting, and System V fork+wait does not work if SIGCHLD is ignored. */ signal (SIGCHLD, SIG_DFL); #endif break; case 'L': /* Specify file labels for `-c' output headers. */ if (!file_label[0]) file_label[0] = optarg; else if (!file_label[1]) file_label[1] = optarg; else fatal ("too many file label options"); break; case 'n': /* Output RCS-style diffs, like `-f' except that each command specifies the number of lines affected. */ specify_style (OUTPUT_RCS); break; case 'N': /* When comparing directories, if a file appears only in one directory, treat it as present but empty in the other. */ entire_new_file_flag = 1; break; case 'o': /* Output in the old tradition style. */ specify_style (OUTPUT_NORMAL); break; case 'p': /* Make context-style output and show name of last C function. */ show_c_function = 1; add_regexp (&function_regexp_list, "^[_a-zA-Z$]"); break; case 'P': /* When comparing directories, if a file appears only in the second directory of the two, treat it as present but empty in the other. */ unidirectional_new_file_flag = 1; break; case 'q': no_details_flag = 1; break; case 'r': /* When comparing directories, recursively compare any subdirectories found. */ recursive = 1; break; case 's': /* Print a message if the files are the same. */ print_file_same_flag = 1; break; case 'S': /* When comparing directories, start with the specified file name. This is used for resuming an aborted comparison. */ dir_start_file = optarg; break; case 't': /* Expand tabs to spaces in the output so that it preserves the alignment of the input files. */ tab_expand_flag = 1; break; case 'T': /* Use a tab in the output, rather than a space, before the text of an input line, so as to keep the proper alignment in the input line without changing the characters in it. */ tab_align_flag = 1; break; case 'u': /* Output the context diff in unidiff format. */ specify_style (OUTPUT_UNIFIED); break; case 'v': printf ("diff - GNU diffutils version %s\n", version_string); exit (0); case 'w': /* Ignore horizontal white space when comparing lines. */ ignore_all_space_flag = 1; ignore_some_changes = 1; ignore_some_line_changes = 1; break; case 'x': add_exclude (optarg); break; case 'X': if (add_exclude_file (optarg) != 0) pfatal_with_name (optarg); break; case 'y': /* Use side-by-side (sdiff-style) columnar output. */ specify_style (OUTPUT_SDIFF); break; case 'W': /* Set the line width for OUTPUT_SDIFF. */ if (ck_atoi (optarg, &width) || width <= 0) fatal ("column width must be a positive integer"); break; case 129: sdiff_left_only = 1; break; case 130: sdiff_skip_common_lines = 1; break; case 131: /* sdiff-style columns output. */ specify_style (OUTPUT_SDIFF); sdiff_help_sdiff = 1; break; case 132: case 133: case 134: specify_style (OUTPUT_IFDEF); if (specify_format (&line_format[c - 132], optarg) != 0) error ("conflicting line format", 0, 0); break; case 135: specify_style (OUTPUT_IFDEF); { int i, err = 0; for (i = 0; i < sizeof (line_format) / sizeof (*line_format); i++) err |= specify_format (&line_format[i], optarg); if (err) error ("conflicting line format", 0, 0); } break; case 136: case 137: case 138: case 139: specify_style (OUTPUT_IFDEF); if (specify_format (&group_format[c - 136], optarg) != 0) error ("conflicting group format", 0, 0); break; case 140: if (ck_atoi (optarg, &horizon_lines) || horizon_lines < 0) fatal ("horizon must be a nonnegative integer"); break; case 141: usage (); check_stdout (); exit (0); case 142: /* Use binary I/O when reading and writing data. On Posix hosts, this has no effect. */ #if HAVE_SETMODE binary_I_O = 1; setmode (STDOUT_FILENO, O_BINARY); #endif break; default: try_help (0); } prev = c; } if (argc - optind != 2) try_help (argc - optind < 2 ? "missing operand" : "extra operand"); { /* * We maximize first the half line width, and then the gutter width, * according to the following constraints: * 1. Two half lines plus a gutter must fit in a line. * 2. If the half line width is nonzero: * a. The gutter width is at least GUTTER_WIDTH_MINIMUM. * b. If tabs are not expanded to spaces, * a half line plus a gutter is an integral number of tabs, * so that tabs in the right column line up. */ int t = tab_expand_flag ? 1 : TAB_WIDTH; int off = (width + t + GUTTER_WIDTH_MINIMUM) / (2*t) * t; sdiff_half_width = max (0, min (off - GUTTER_WIDTH_MINIMUM, width - off)), sdiff_column2_offset = sdiff_half_width ? off : width; } if (show_c_function && output_style != OUTPUT_UNIFIED) specify_style (OUTPUT_CONTEXT); if (output_style != OUTPUT_CONTEXT && output_style != OUTPUT_UNIFIED) context = 0; else if (context == -1) /* Default amount of context for -c. */ context = 3; if (output_style == OUTPUT_IFDEF) { /* Format arrays are char *, not char const *, because integer formats are temporarily modified. But it is safe to assign a constant like "%=" to a format array, since "%=" does not format any integers. */ int i; for (i = 0; i < sizeof (line_format) / sizeof (*line_format); i++) if (!line_format[i]) line_format[i] = "%l\n"; if (!group_format[OLD]) group_format[OLD] = group_format[UNCHANGED] ? group_format[UNCHANGED] : "%<"; if (!group_format[NEW]) group_format[NEW] = group_format[UNCHANGED] ? group_format[UNCHANGED] : "%>"; if (!group_format[UNCHANGED]) group_format[UNCHANGED] = "%="; if (!group_format[CHANGED]) group_format[CHANGED] = concat (group_format[OLD], group_format[NEW], ""); } no_diff_means_no_output = (output_style == OUTPUT_IFDEF ? (!*group_format[UNCHANGED] || (strcmp (group_format[UNCHANGED], "%=") == 0 && !*line_format[UNCHANGED])) : output_style == OUTPUT_SDIFF ? sdiff_skip_common_lines : 1); switch_string = option_list (argv + 1, optind - 1); val = compare_files (0, argv[optind], 0, argv[optind + 1], 0); /* Print any messages that were saved up for last. */ print_message_queue (); check_stdout (); exit (val); return val; } /* Add the compiled form of regexp PATTERN to REGLIST. */ static void add_regexp (reglist, pattern) struct regexp_list **reglist; char const *pattern; { struct regexp_list *r; char const *m; r = (struct regexp_list *) xmalloc (sizeof (*r)); bzero (r, sizeof (*r)); r->buf.fastmap = xmalloc (256); m = re_compile_pattern (pattern, strlen (pattern), &r->buf); if (m != 0) error ("%s: %s", pattern, m); /* Add to the start of the list, since it's easier than the end. */ r->next = *reglist; *reglist = r; } static void try_help (reason) char const *reason; { if (reason) error ("%s", reason, 0); error ("Try `%s --help' for more information.", program_name, 0); exit (2); } static void check_stdout () { if (ferror (stdout) || fclose (stdout) != 0) fatal ("write error"); } static char const * const option_help[] = { "-i --ignore-case Consider upper- and lower-case to be the same.", "-w --ignore-all-space Ignore all white space.", "-b --ignore-space-change Ignore changes in the amount of white space.", "-B --ignore-blank-lines Ignore changes whose lines are all blank.", "-I RE --ignore-matching-lines=RE Ignore changes whose lines all match RE.", #if HAVE_SETMODE "--binary Read and write data in binary mode.", #endif "-a --text Treat all files as text.\n", "-c -C NUM --context[=NUM] Output NUM (default 2) lines of copied context.", "-u -U NUM --unified[=NUM] Output NUM (default 2) lines of unified context.", " -NUM Use NUM context lines.", " -L LABEL --label LABEL Use LABEL instead of file name.", " -p --show-c-function Show which C function each change is in.", " -F RE --show-function-line=RE Show the most recent line matching RE.", "-q --brief Output only whether files differ.", "-e --ed Output an ed script.", "-n --rcs Output an RCS format diff.", "-y --side-by-side Output in two columns.", " -w NUM --width=NUM Output at most NUM (default 130) characters per line.", " --left-column Output only the left column of common lines.", " --suppress-common-lines Do not output common lines.", "-DNAME --ifdef=NAME Output merged file to show `#ifdef NAME' diffs.", "--GTYPE-group-format=GFMT Similar, but format GTYPE input groups with GFMT.", "--line-format=LFMT Similar, but format all input lines with LFMT.", "--LTYPE-line-format=LFMT Similar, but format LTYPE input lines with LFMT.", " LTYPE is `old', `new', or `unchanged'. GTYPE is LTYPE or `changed'.", " GFMT may contain:", " %< lines from FILE1", " %> lines from FILE2", " %= lines common to FILE1 and FILE2", " %[-][WIDTH][.[PREC]]{doxX}LETTER printf-style spec for LETTER", " LETTERs are as follows for new group, lower case for old group:", " F first line number", " L last line number", " N number of lines = L-F+1", " E F-1", " M L+1", " LFMT may contain:", " %L contents of line", " %l contents of line, excluding any trailing newline", " %[-][WIDTH][.[PREC]]{doxX}n printf-style spec for input line number", " Either GFMT or LFMT may contain:", " %% %", " %c'C' the single character C", " %c'\\OOO' the character with octal code OOO\n", "-l --paginate Pass the output through `pr' to paginate it.", "-t --expand-tabs Expand tabs to spaces in output.", "-T --initial-tab Make tabs line up by prepending a tab.\n", "-r --recursive Recursively compare any subdirectories found.", "-N --new-file Treat absent files as empty.", "-P --unidirectional-new-file Treat absent first files as empty.", "-s --report-identical-files Report when two files are the same.", "-x PAT --exclude=PAT Exclude files that match PAT.", "-X FILE --exclude-from=FILE Exclude files that match any pattern in FILE.", "-S FILE --starting-file=FILE Start with FILE when comparing directories.\n", "--horizon-lines=NUM Keep NUM lines of the common prefix and suffix.", "-d --minimal Try hard to find a smaller set of changes.", "-H --speed-large-files Assume large files and many scattered small changes.\n", "-v --version Output version info.", "--help Output this help.", 0 }; static void usage () { char const * const *p; printf ("Usage: %s [OPTION]... FILE1 FILE2\n\n", program_name); for (p = option_help; *p; p++) printf (" %s\n", *p); printf ("\nIf FILE1 or FILE2 is `-', read standard input.\n"); } static int specify_format (var, value) char **var; char *value; { int err = *var ? strcmp (*var, value) : 0; *var = value; return err; } static void specify_style (style) enum output_style style; { if (output_style != OUTPUT_NORMAL && output_style != style) error ("conflicting specifications of output style", 0, 0); output_style = style; } static char const * filetype (st) struct stat const *st; { /* See Posix.2 section 4.17.6.1.1 and Table 5-1 for these formats. To keep diagnostics grammatical, the returned string must start with a consonant. */ if (S_ISREG (st->st_mode)) { if (st->st_size == 0) return "regular empty file"; /* Posix.2 section 5.14.2 seems to suggest that we must read the file and guess whether it's C, Fortran, etc., but this is somewhat useless and doesn't reflect historical practice. We're allowed to guess wrong, so we don't bother to read the file. */ return "regular file"; } if (S_ISDIR (st->st_mode)) return "directory"; /* other Posix.1 file types */ #ifdef S_ISBLK if (S_ISBLK (st->st_mode)) return "block special file"; #endif #ifdef S_ISCHR if (S_ISCHR (st->st_mode)) return "character special file"; #endif #ifdef S_ISFIFO if (S_ISFIFO (st->st_mode)) return "fifo"; #endif /* other Posix.1b file types */ #ifdef S_TYPEISMQ if (S_TYPEISMQ (st)) return "message queue"; #endif #ifdef S_TYPEISSEM if (S_TYPEISSEM (st)) return "semaphore"; #endif #ifdef S_TYPEISSHM if (S_TYPEISSHM (st)) return "shared memory object"; #endif /* other popular file types */ /* S_ISLNK is impossible with `fstat' and `stat'. */ #ifdef S_ISSOCK if (S_ISSOCK (st->st_mode)) return "socket"; #endif return "weird file"; } /* Compare two files (or dirs) with specified names DIR0/NAME0 and DIR1/NAME1, at level DEPTH in directory recursion. (if DIR0 is 0, then the name is just NAME0, etc.) This is self-contained; it opens the files and closes them. Value is 0 if files are the same, 1 if different, 2 if there is a problem opening them. */ static int compare_files (dir0, name0, dir1, name1, depth) char const *dir0, *dir1; char const *name0, *name1; int depth; { struct file_data inf[2]; register int i; int val; int same_files; int failed = 0; char *free0 = 0, *free1 = 0; /* If this is directory comparison, perhaps we have a file that exists only in one of the directories. If so, just print a message to that effect. */ if (! ((name0 != 0 && name1 != 0) || (unidirectional_new_file_flag && name1 != 0) || entire_new_file_flag)) { char const *name = name0 == 0 ? name1 : name0; char const *dir = name0 == 0 ? dir1 : dir0; message ("Only in %s: %s\n", dir, name); /* Return 1 so that diff_dirs will return 1 ("some files differ"). */ return 1; } bzero (inf, sizeof (inf)); /* Mark any nonexistent file with -1 in the desc field. */ /* Mark unopened files (e.g. directories) with -2. */ inf[0].desc = name0 == 0 ? -1 : -2; inf[1].desc = name1 == 0 ? -1 : -2; /* Now record the full name of each file, including nonexistent ones. */ if (name0 == 0) name0 = name1; if (name1 == 0) name1 = name0; inf[0].name = dir0 == 0 ? name0 : (free0 = dir_file_pathname (dir0, name0)); inf[1].name = dir1 == 0 ? name1 : (free1 = dir_file_pathname (dir1, name1)); /* Stat the files. Record whether they are directories. */ for (i = 0; i <= 1; i++) { if (inf[i].desc != -1) { int stat_result; if (i && filename_cmp (inf[i].name, inf[0].name) == 0) { inf[i].stat = inf[0].stat; stat_result = 0; } else if (strcmp (inf[i].name, "-") == 0) { inf[i].desc = STDIN_FILENO; stat_result = fstat (STDIN_FILENO, &inf[i].stat); if (stat_result == 0 && S_ISREG (inf[i].stat.st_mode)) { off_t pos = lseek (STDIN_FILENO, (off_t) 0, SEEK_CUR); if (pos == -1) stat_result = -1; else { if (pos <= inf[i].stat.st_size) inf[i].stat.st_size -= pos; else inf[i].stat.st_size = 0; /* Posix.2 4.17.6.1.4 requires current time for stdin. */ time (&inf[i].stat.st_mtime); } } } else stat_result = stat (inf[i].name, &inf[i].stat); if (stat_result != 0) { perror_with_name (inf[i].name); failed = 1; } else { inf[i].dir_p = S_ISDIR (inf[i].stat.st_mode) && inf[i].desc != 0; if (inf[1 - i].desc == -1) { inf[1 - i].dir_p = inf[i].dir_p; inf[1 - i].stat.st_mode = inf[i].stat.st_mode; } } } } if (! failed && depth == 0 && inf[0].dir_p != inf[1].dir_p) { /* If one is a directory, and it was specified in the command line, use the file in that dir with the other file's basename. */ int fnm_arg = inf[0].dir_p; int dir_arg = 1 - fnm_arg; char const *fnm = inf[fnm_arg].name; char const *dir = inf[dir_arg].name; char const *p = filename_lastdirchar (fnm); char const *filename = inf[dir_arg].name = dir_file_pathname (dir, p ? p + 1 : fnm); if (strcmp (fnm, "-") == 0) fatal ("can't compare - to a directory"); if (stat (filename, &inf[dir_arg].stat) != 0) { perror_with_name (filename); failed = 1; } else inf[dir_arg].dir_p = S_ISDIR (inf[dir_arg].stat.st_mode); } if (failed) { /* If either file should exist but does not, return 2. */ val = 2; } else if ((same_files = inf[0].desc != -1 && inf[1].desc != -1 && 0 < same_file (&inf[0].stat, &inf[1].stat)) && no_diff_means_no_output) { /* The two named files are actually the same physical file. We know they are identical without actually reading them. */ val = 0; } else if (inf[0].dir_p & inf[1].dir_p) { if (output_style == OUTPUT_IFDEF) fatal ("-D option not supported with directories"); /* If both are directories, compare the files in them. */ if (depth > 0 && !recursive) { /* But don't compare dir contents one level down unless -r was specified. */ message ("Common subdirectories: %s and %s\n", inf[0].name, inf[1].name); val = 0; } else { val = diff_dirs (inf, compare_files, depth); } } else if ((inf[0].dir_p | inf[1].dir_p) || (depth > 0 && (! S_ISREG (inf[0].stat.st_mode) || ! S_ISREG (inf[1].stat.st_mode)))) { /* Perhaps we have a subdirectory that exists only in one directory. If so, just print a message to that effect. */ if (inf[0].desc == -1 || inf[1].desc == -1) { if ((inf[0].dir_p | inf[1].dir_p) && recursive && (entire_new_file_flag || (unidirectional_new_file_flag && inf[0].desc == -1))) val = diff_dirs (inf, compare_files, depth); else { char const *dir = (inf[0].desc == -1) ? dir1 : dir0; /* See Posix.2 section 4.17.6.1.1 for this format. */ message ("Only in %s: %s\n", dir, name0); val = 1; } } else { /* We have two files that are not to be compared. */ /* See Posix.2 section 4.17.6.1.1 for this format. */ message5 ("File %s is a %s while file %s is a %s\n", inf[0].name, filetype (&inf[0].stat), inf[1].name, filetype (&inf[1].stat)); /* This is a difference. */ val = 1; } } else if ((no_details_flag & ~ignore_some_changes) && inf[0].stat.st_size != inf[1].stat.st_size && (inf[0].desc == -1 || S_ISREG (inf[0].stat.st_mode)) && (inf[1].desc == -1 || S_ISREG (inf[1].stat.st_mode))) { message ("Files %s and %s differ\n", inf[0].name, inf[1].name); val = 1; } else { /* Both exist and neither is a directory. */ /* Open the files and record their descriptors. */ if (inf[0].desc == -2) if ((inf[0].desc = open (inf[0].name, O_RDONLY, 0)) < 0) { perror_with_name (inf[0].name); failed = 1; } if (inf[1].desc == -2) if (same_files) inf[1].desc = inf[0].desc; else if ((inf[1].desc = open (inf[1].name, O_RDONLY, 0)) < 0) { perror_with_name (inf[1].name); failed = 1; } #if HAVE_SETMODE if (binary_I_O) for (i = 0; i <= 1; i++) if (0 <= inf[i].desc) setmode (inf[i].desc, O_BINARY); #endif /* Compare the files, if no error was found. */ val = failed ? 2 : diff_2_files (inf, depth); /* Close the file descriptors. */ if (inf[0].desc >= 0 && close (inf[0].desc) != 0) { perror_with_name (inf[0].name); val = 2; } if (inf[1].desc >= 0 && inf[0].desc != inf[1].desc && close (inf[1].desc) != 0) { perror_with_name (inf[1].name); val = 2; } } /* Now the comparison has been done, if no error prevented it, and VAL is the value this function will return. */ if (val == 0 && !inf[0].dir_p) { if (print_file_same_flag) message ("Files %s and %s are identical\n", inf[0].name, inf[1].name); } else fflush (stdout); if (free0) free (free0); if (free1) free (free1); return val; } /sys/src/ape/cmd/diff/diff.h 664 sys sys 1367613436 11767 /* Shared definitions for GNU DIFF Copyright (C) 1988, 89, 91, 92, 93 Free Software Foundation, Inc. This file is part of GNU DIFF. GNU DIFF 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. GNU DIFF 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 GNU DIFF; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "system.h" #include #ifdef __FreeBSD__ #include #else #include "regex.h" #endif #define TAB_WIDTH 8 /* Variables for command line options */ #ifndef GDIFF_MAIN #define EXTERN extern #else #define EXTERN #endif enum output_style { /* Default output style. */ OUTPUT_NORMAL, /* Output the differences with lines of context before and after (-c). */ OUTPUT_CONTEXT, /* Output the differences in a unified context diff format (-u). */ OUTPUT_UNIFIED, /* Output the differences as commands suitable for `ed' (-e). */ OUTPUT_ED, /* Output the diff as a forward ed script (-f). */ OUTPUT_FORWARD_ED, /* Like -f, but output a count of changed lines in each "command" (-n). */ OUTPUT_RCS, /* Output merged #ifdef'd file (-D). */ OUTPUT_IFDEF, /* Output sdiff style (-y). */ OUTPUT_SDIFF }; /* True for output styles that are robust, i.e. can handle a file that ends in a non-newline. */ #define ROBUST_OUTPUT_STYLE(S) ((S) != OUTPUT_ED && (S) != OUTPUT_FORWARD_ED) EXTERN enum output_style output_style; /* Nonzero if output cannot be generated for identical files. */ EXTERN int no_diff_means_no_output; /* Number of lines of context to show in each set of diffs. This is zero when context is not to be shown. */ EXTERN int context; /* Consider all files as text files (-a). Don't interpret codes over 0177 as implying a "binary file". */ EXTERN int always_text_flag; /* Number of lines to keep in identical prefix and suffix. */ EXTERN int horizon_lines; /* Ignore changes in horizontal white space (-b). */ EXTERN int ignore_space_change_flag; /* Ignore all horizontal white space (-w). */ EXTERN int ignore_all_space_flag; /* Ignore changes that affect only blank lines (-B). */ EXTERN int ignore_blank_lines_flag; /* 1 if lines may match even if their contents do not match exactly. This depends on various options. */ EXTERN int ignore_some_line_changes; /* 1 if files may match even if their contents are not byte-for-byte identical. This depends on various options. */ EXTERN int ignore_some_changes; /* Ignore differences in case of letters (-i). */ EXTERN int ignore_case_flag; /* File labels for `-c' output headers (-L). */ EXTERN char *file_label[2]; struct regexp_list { struct re_pattern_buffer buf; struct regexp_list *next; }; /* Regexp to identify function-header lines (-F). */ EXTERN struct regexp_list *function_regexp_list; /* Ignore changes that affect only lines matching this regexp (-I). */ EXTERN struct regexp_list *ignore_regexp_list; /* Say only whether files differ, not how (-q). */ EXTERN int no_details_flag; /* Report files compared that match (-s). Normally nothing is output when that happens. */ EXTERN int print_file_same_flag; /* Output the differences with exactly 8 columns added to each line so that any tabs in the text line up properly (-T). */ EXTERN int tab_align_flag; /* Expand tabs in the output so the text lines up properly despite the characters added to the front of each line (-t). */ EXTERN int tab_expand_flag; /* In directory comparison, specify file to start with (-S). All file names less than this name are ignored. */ EXTERN char *dir_start_file; /* If a file is new (appears in only one dir) include its entire contents (-N). Then `patch' would create the file with appropriate contents. */ EXTERN int entire_new_file_flag; /* If a file is new (appears in only the second dir) include its entire contents (-P). Then `patch' would create the file with appropriate contents. */ EXTERN int unidirectional_new_file_flag; /* Pipe each file's output through pr (-l). */ EXTERN int paginate_flag; enum line_class { /* Lines taken from just the first file. */ OLD, /* Lines taken from just the second file. */ NEW, /* Lines common to both files. */ UNCHANGED, /* A hunk containing both old and new lines (line groups only). */ CHANGED }; /* Line group formats for old, new, unchanged, and changed groups. */ EXTERN char *group_format[CHANGED + 1]; /* Line formats for old, new, and unchanged lines. */ EXTERN char *line_format[UNCHANGED + 1]; /* If using OUTPUT_SDIFF print extra information to help the sdiff filter. */ EXTERN int sdiff_help_sdiff; /* Tell OUTPUT_SDIFF to show only the left version of common lines. */ EXTERN int sdiff_left_only; /* Tell OUTPUT_SDIFF to not show common lines. */ EXTERN int sdiff_skip_common_lines; /* The half line width and column 2 offset for OUTPUT_SDIFF. */ EXTERN unsigned sdiff_half_width; EXTERN unsigned sdiff_column2_offset; /* String containing all the command options diff received, with spaces between and at the beginning but none at the end. If there were no options given, this string is empty. */ EXTERN char * switch_string; /* Nonzero means use heuristics for better speed. */ EXTERN int heuristic; /* Name of program the user invoked (for error messages). */ EXTERN char *program_name; /* The result of comparison is an "edit script": a chain of `struct change'. Each `struct change' represents one place where some lines are deleted and some are inserted. LINE0 and LINE1 are the first affected lines in the two files (origin 0). DELETED is the number of lines deleted here from file 0. INSERTED is the number of lines inserted here in file 1. If DELETED is 0 then LINE0 is the number of the line before which the insertion was done; vice versa for INSERTED and LINE1. */ struct change { struct change *link; /* Previous or next edit command */ int inserted; /* # lines of file 1 changed here. */ int deleted; /* # lines of file 0 changed here. */ int line0; /* Line number of 1st deleted line. */ int line1; /* Line number of 1st inserted line. */ char ignore; /* Flag used in context.c */ }; /* Structures that describe the input files. */ /* Data on one input file being compared. */ struct file_data { int desc; /* File descriptor */ char const *name; /* File name */ struct stat stat; /* File status from fstat() */ int dir_p; /* nonzero if file is a directory */ /* Buffer in which text of file is read. */ char * buffer; /* Allocated size of buffer. */ size_t bufsize; /* Number of valid characters now in the buffer. */ size_t buffered_chars; /* Array of pointers to lines in the file. */ char const **linbuf; /* linbuf_base <= buffered_lines <= valid_lines <= alloc_lines. linebuf[linbuf_base ... buffered_lines - 1] are possibly differing. linebuf[linbuf_base ... valid_lines - 1] contain valid data. linebuf[linbuf_base ... alloc_lines - 1] are allocated. */ int linbuf_base, buffered_lines, valid_lines, alloc_lines; /* Pointer to end of prefix of this file to ignore when hashing. */ char const *prefix_end; /* Count of lines in the prefix. There are this many lines in the file before linbuf[0]. */ int prefix_lines; /* Pointer to start of suffix of this file to ignore when hashing. */ char const *suffix_begin; /* Vector, indexed by line number, containing an equivalence code for each line. It is this vector that is actually compared with that of another file to generate differences. */ int *equivs; /* Vector, like the previous one except that the elements for discarded lines have been squeezed out. */ int *undiscarded; /* Vector mapping virtual line numbers (not counting discarded lines) to real ones (counting those lines). Both are origin-0. */ int *realindexes; /* Total number of nondiscarded lines. */ int nondiscarded_lines; /* Vector, indexed by real origin-0 line number, containing 1 for a line that is an insertion or a deletion. The results of comparison are stored here. */ char *changed_flag; /* 1 if file ends in a line with no final newline. */ int missing_newline; /* 1 more than the maximum equivalence value used for this or its sibling file. */ int equiv_max; }; /* Describe the two files currently being compared. */ EXTERN struct file_data files[2]; /* Stdio stream to output diffs to. */ EXTERN FILE *outfile; /* Declare various functions. */ /* analyze.c */ int diff_2_files PARAMS((struct file_data[], int)); /* context.c */ void print_context_header PARAMS((struct file_data[], int)); void print_context_script PARAMS((struct change *, int)); /* diff.c */ int excluded_filename PARAMS((char const *)); /* dir.c */ int diff_dirs PARAMS((struct file_data const[], int (*) PARAMS((char const *, char const *, char const *, char const *, int)), int)); /* ed.c */ void print_ed_script PARAMS((struct change *)); void pr_forward_ed_script PARAMS((struct change *)); /* ifdef.c */ void print_ifdef_script PARAMS((struct change *)); /* io.c */ int read_files PARAMS((struct file_data[], int)); int sip PARAMS((struct file_data *, int)); void slurp PARAMS((struct file_data *)); /* normal.c */ void print_normal_script PARAMS((struct change *)); /* rcs.c */ void print_rcs_script PARAMS((struct change *)); /* side.c */ void print_sdiff_script PARAMS((struct change *)); /* util.c */ VOID *xmalloc PARAMS((size_t)); VOID *xrealloc PARAMS((VOID *, size_t)); char *concat PARAMS((char const *, char const *, char const *)); char *dir_file_pathname PARAMS((char const *, char const *)); int change_letter PARAMS((int, int)); int line_cmp PARAMS((char const *, char const *)); int translate_line_number PARAMS((struct file_data const *, int)); struct change *find_change PARAMS((struct change *)); struct change *find_reverse_change PARAMS((struct change *)); void analyze_hunk PARAMS((struct change *, int *, int *, int *, int *, int *, int *)); void begin_output PARAMS((void)); void debug_script PARAMS((struct change *)); void error PARAMS((char const *, char const *, char const *)); void fatal PARAMS((char const *)); void finish_output PARAMS((void)); void message PARAMS((char const *, char const *, char const *)); void message5 PARAMS((char const *, char const *, char const *, char const *, char const *)); void output_1_line PARAMS((char const *, char const *, char const *, char const *)); void perror_with_name PARAMS((char const *)); void pfatal_with_name PARAMS((char const *)); void print_1_line PARAMS((char const *, char const * const *)); void print_message_queue PARAMS((void)); void print_number_range PARAMS((int, struct file_data *, int, int)); void print_script PARAMS((struct change *, struct change * (*) PARAMS((struct change *)), void (*) PARAMS((struct change *)))); void setup_output PARAMS((char const *, char const *, int)); void translate_range PARAMS((struct file_data const *, int, int, int *, int *)); /* version.c */ extern char const version_string[]; /sys/src/ape/cmd/diff/diff.texi 664 sys sys 1367613436 150414 \input texinfo @c -*-texinfo-*- @c %**start of header @setfilename diff.info @settitle Comparing and Merging Files @setchapternewpage odd @c %**end of header @ifinfo This file documents the the GNU @code{diff}, @code{diff3}, @code{sdiff}, and @code{cmp} commands for showing the differences between text files and the @code{patch} command for using their output to update files. Copyright (C) 1992, 1993, 1994 Free Software Foundation, Inc. Permission is granted to make and distribute verbatim copies of this manual provided the copyright notice and this permission notice are preserved on all copies. @ignore Permission is granted to process this file through TeX and print the results, provided the printed document carries copying permission notice identical to this one except for the removal of this paragraph (this paragraph not being relevant to the printed manual). @end ignore Permission is granted to copy and distribute modified versions of this manual under the conditions for verbatim copying, provided that the entire resulting derived work is distributed under the terms of a permission notice identical to this one. Permission is granted to copy and distribute translations of this manual into another language, under the above conditions for modified versions, except that this permission notice may be stated in a translation approved by the Foundation. @end ifinfo @titlepage @title Comparing and Merging Files @subtitle @code{diff}, @code{diff3}, @code{sdiff}, @code{cmp}, and @code{patch} @subtitle Edition 1.3, for @code{diff} 2.5 and @code{patch} 2.1 @subtitle September 1993 @author by David MacKenzie, Paul Eggert, and Richard Stallman @page @vskip 0pt plus 1filll Copyright @copyright{} 1992, 1993, 1994 Free Software Foundation, Inc. Permission is granted to make and distribute verbatim copies of this manual provided the copyright notice and this permission notice are preserved on all copies. Permission is granted to copy and distribute modified versions of this manual under the conditions for verbatim copying, provided that the entire resulting derived work is distributed under the terms of a permission notice identical to this one. Permission is granted to copy and distribute translations of this manual into another language, under the above conditions for modified versions, except that this permission notice may be stated in a translation approved by the Foundation. @end titlepage @node Top, , , (dir) @ifinfo This file documents the the GNU @code{diff}, @code{diff3}, @code{sdiff}, and @code{cmp} commands for showing the differences between text files and the @code{patch} command for using their output to update files. This is Edition 1.2, for @code{diff} 2.4 and @code{patch} 2.1. @end ifinfo @menu * Overview:: Preliminary information. * Comparison:: What file comparison means. * Output Formats:: Formats for difference reports. * Comparing Directories:: Comparing files and directories. * Adjusting Output:: Making @code{diff} output prettier. * diff Performance:: Making @code{diff} smarter or faster. * Comparing Three Files:: Formats for three-way difference reports. * diff3 Merging:: Merging from a common ancestor. * Interactive Merging:: Interactive merging with @code{sdiff}. * Merging with patch:: Using @code{patch} to change old files into new ones. * Making Patches:: Tips for making patch distributions. * Invoking cmp:: How to run @code{cmp} and a summary of its options. * Invoking diff:: How to run @code{diff} and a summary of its options. * Invoking diff3:: How to run @code{diff3} and a summary of its options. * Invoking patch:: How to run @code{patch} and a summary of its options. * Invoking sdiff:: How to run @code{sdiff} and a summary of its options. * Incomplete Lines:: Lines that lack trailing newlines. * Projects:: If you think you've found a bug or other shortcoming. * Concept Index:: Index of concepts. @end menu @node Overview, Comparison, , Top @unnumbered Overview @cindex overview of @code{diff} and @code{patch} Computer users often find occasion to ask how two files differ. Perhaps one file is a newer version of the other file. Or maybe the two files started out as identical copies but were changed by different people. You can use the @code{diff} command to show differences between two files, or each corresponding file in two directories. @code{diff} outputs differences between files line by line in any of several formats, selectable by command line options. This set of differences is often called a @dfn{diff} or @dfn{patch}. For files that are identical, @code{diff} normally produces no output; for binary (non-text) files, @code{diff} normally reports only that they are different. You can use the @code{cmp} command to show the offsets and line numbers where two files differ. @code{cmp} can also show all the characters that differ between the two files, side by side. Another way to compare two files character by character is the Emacs command @kbd{M-x compare-windows}. @xref{Other Window, , Other Window, emacs, The GNU Emacs Manual}, for more information on that command. You can use the @code{diff3} command to show differences among three files. When two people have made independent changes to a common original, @code{diff3} can report the differences between the original and the two changed versions, and can produce a merged file that contains both persons' changes together with warnings about conflicts. You can use the @code{sdiff} command to merge two files interactively. You can use the set of differences produced by @code{diff} to distribute updates to text files (such as program source code) to other people. This method is especially useful when the differences are small compared to the complete files. Given @code{diff} output, you can use the @code{patch} program to update, or @dfn{patch}, a copy of the file. If you think of @code{diff} as subtracting one file from another to produce their difference, you can think of @code{patch} as adding the difference to one file to reproduce the other. This manual first concentrates on making diffs, and later shows how to use diffs to update files. GNU @code{diff} was written by Mike Haertel, David Hayes, Richard Stallman, Len Tower, and Paul Eggert. Wayne Davison designed and implemented the unified output format. The basic algorithm is described in ``An O(ND) Difference Algorithm and its Variations'', Eugene W. Myers, @cite{Algorithmica} Vol.@: 1 No.@: 2, 1986, pp.@: 251--266; and in ``A File Comparison Program'', Webb Miller and Eugene W. Myers, @cite{Software---Practice and Experience} Vol.@: 15 No.@: 11, 1985, pp.@: 1025--1040. @c From: "Gene Myers" @c They are about the same basic algorithm; the Algorithmica @c paper gives a rigorous treatment and the sub-algorithm for @c delivering scripts and should be the primary reference, but @c both should be mentioned. The algorithm was independently discovered as described in ``Algorithms for Approximate String Matching'', E. Ukkonen, @cite{Information and Control} Vol.@: 64, 1985, pp.@: 100--118. @c From: "Gene Myers" @c Date: Wed, 29 Sep 1993 08:27:55 MST @c Ukkonen should be given credit for also discovering the algorithm used @c in GNU diff. GNU @code{diff3} was written by Randy Smith. GNU @code{sdiff} was written by Thomas Lord. GNU @code{cmp} was written by Torbjorn Granlund and David MacKenzie. @code{patch} was written mainly by Larry Wall; the GNU enhancements were written mainly by Wayne Davison and David MacKenzie. Parts of this manual are adapted from a manual page written by Larry Wall, with his permission. @node Comparison, Output Formats, Overview, Top @chapter What Comparison Means @cindex introduction There are several ways to think about the differences between two files. One way to think of the differences is as a series of lines that were deleted from, inserted in, or changed in one file to produce the other file. @code{diff} compares two files line by line, finds groups of lines that differ, and reports each group of differing lines. It can report the differing lines in several formats, which have different purposes. GNU @code{diff} can show whether files are different without detailing the differences. It also provides ways to suppress certain kinds of differences that are not important to you. Most commonly, such differences are changes in the amount of white space between words or lines. @code{diff} also provides ways to suppress differences in alphabetic case or in lines that match a regular expression that you provide. These options can accumulate; for example, you can ignore changes in both white space and alphabetic case. Another way to think of the differences between two files is as a sequence of pairs of characters that can be either identical or different. @code{cmp} reports the differences between two files character by character, instead of line by line. As a result, it is more useful than @code{diff} for comparing binary files. For text files, @code{cmp} is useful mainly when you want to know only whether two files are identical. To illustrate the effect that considering changes character by character can have compared with considering them line by line, think of what happens if a single newline character is added to the beginning of a file. If that file is then compared with an otherwise identical file that lacks the newline at the beginning, @code{diff} will report that a blank line has been added to the file, while @code{cmp} will report that almost every character of the two files differs. @code{diff3} normally compares three input files line by line, finds groups of lines that differ, and reports each group of differing lines. Its output is designed to make it easy to inspect two different sets of changes to the same file. @menu * Hunks:: Groups of differing lines. * White Space:: Suppressing differences in white space. * Blank Lines:: Suppressing differences in blank lines. * Case Folding:: Suppressing differences in alphabetic case. * Specified Folding:: Suppressing differences that match regular expressions. * Brief:: Summarizing which files are different. * Binary:: Comparing binary files or forcing text comparisons. @end menu @node Hunks, White Space, , Comparison @section Hunks @cindex hunks When comparing two files, @code{diff} finds sequences of lines common to both files, interspersed with groups of differing lines called @dfn{hunks}. Comparing two identical files yields one sequence of common lines and no hunks, because no lines differ. Comparing two entirely different files yields no common lines and one large hunk that contains all lines of both files. In general, there are many ways to match up lines between two given files. @code{diff} tries to minimize the total hunk size by finding large sequences of common lines interspersed with small hunks of differing lines. For example, suppose the file @file{F} contains the three lines @samp{a}, @samp{b}, @samp{c}, and the file @file{G} contains the same three lines in reverse order @samp{c}, @samp{b}, @samp{a}. If @code{diff} finds the line @samp{c} as common, then the command @samp{diff F G} produces this output: @example 1,2d0 < a < b 3a2,3 > b > a @end example @noindent But if @code{diff} notices the common line @samp{b} instead, it produces this output: @example 1c1 < a --- > c 3c3 < c --- > a @end example @noindent It is also possible to find @samp{a} as the common line. @code{diff} does not always find an optimal matching between the files; it takes shortcuts to run faster. But its output is usually close to the shortest possible. You can adjust this tradeoff with the @samp{--minimal} option (@pxref{diff Performance}). @node White Space, Blank Lines, Hunks, Comparison @section Suppressing Differences in Blank and Tab Spacing @cindex blank and tab difference suppression @cindex tab and blank difference suppression The @samp{-b} and @samp{--ignore-space-change} options ignore white space at line end, and considers all other sequences of one or more white space characters to be equivalent. With these options, @code{diff} considers the following two lines to be equivalent, where @samp{$} denotes the line end: @example Here lyeth muche rychnesse in lytell space. -- John Heywood$ Here lyeth muche rychnesse in lytell space. -- John Heywood $ @end example The @samp{-w} and @samp{--ignore-all-space} options are stronger than @samp{-b}. They ignore difference even if one file has white space where the other file has none. @dfn{White space} characters include tab, newline, vertical tab, form feed, carriage return, and space; some locales may define additional characters to be white space. With these options, @code{diff} considers the following two lines to be equivalent, where @samp{$} denotes the line end and @samp{^M} denotes a carriage return: @example Here lyeth muche rychnesse in lytell space.-- John Heywood$ He relyeth much erychnes seinly tells pace. --John Heywood ^M$ @end example @node Blank Lines, Case Folding, White Space, Comparison @section Suppressing Differences in Blank Lines @cindex blank line difference suppression The @samp{-B} and @samp{--ignore-blank-lines} options ignore insertions or deletions of blank lines. These options normally affect only lines that are completely empty; they do not affect lines that look empty but contain space or tab characters. With these options, for example, a file containing @example 1. A point is that which has no part. 2. A line is breadthless length. -- Euclid, The Elements, I @end example @noindent is considered identical to a file containing @example 1. A point is that which has no part. 2. A line is breadthless length. -- Euclid, The Elements, I @end example @node Case Folding, Specified Folding, Blank Lines, Comparison @section Suppressing Case Differences @cindex case difference suppression GNU @code{diff} can treat lowercase letters as equivalent to their uppercase counterparts, so that, for example, it considers @samp{Funky Stuff}, @samp{funky STUFF}, and @samp{fUNKy stuFf} to all be the same. To request this, use the @samp{-i} or @samp{--ignore-case} option. @node Specified Folding, Brief, Case Folding, Comparison @section Suppressing Lines Matching a Regular Expression @cindex regular expression suppression To ignore insertions and deletions of lines that match a regular expression, use the @samp{-I @var{regexp}} or @samp{--ignore-matching-lines=@var{regexp}} option. You should escape regular expressions that contain shell metacharacters to prevent the shell from expanding them. For example, @samp{diff -I '^[0-9]'} ignores all changes to lines beginning with a digit. However, @samp{-I} only ignores the insertion or deletion of lines that contain the regular expression if every changed line in the hunk---every insertion and every deletion---matches the regular expression. In other words, for each nonignorable change, @code{diff} prints the complete set of changes in its vicinity, including the ignorable ones. You can specify more than one regular expression for lines to ignore by using more than one @samp{-I} option. @code{diff} tries to match each line against each regular expression, starting with the last one given. @node Brief, Binary, Specified Folding, Comparison @section Summarizing Which Files Differ @cindex summarizing which files differ @cindex brief difference reports When you only want to find out whether files are different, and you don't care what the differences are, you can use the summary output format. In this format, instead of showing the differences between the files, @code{diff} simply reports whether files differ. The @samp{-q} and @samp{--brief} options select this output format. This format is especially useful when comparing the contents of two directories. It is also much faster than doing the normal line by line comparisons, because @code{diff} can stop analyzing the files as soon as it knows that there are any differences. You can also get a brief indication of whether two files differ by using @code{cmp}. For files that are identical, @code{cmp} produces no output. When the files differ, by default, @code{cmp} outputs the byte offset and line number where the first difference occurs. You can use the @samp{-s} option to suppress that information, so that @code{cmp} produces no output and reports whether the files differ using only its exit status (@pxref{Invoking cmp}). @c Fix this. Unlike @code{diff}, @code{cmp} cannot compare directories; it can only compare two files. @node Binary, , Brief, Comparison @section Binary Files and Forcing Text Comparisons @cindex binary file diff @cindex text versus binary diff If @code{diff} thinks that either of the two files it is comparing is binary (a non-text file), it normally treats that pair of files much as if the summary output format had been selected (@pxref{Brief}), and reports only that the binary files are different. This is because line by line comparisons are usually not meaningful for binary files. @code{diff} determines whether a file is text or binary by checking the first few bytes in the file; the exact number of bytes is system dependent, but it is typically several thousand. If every character in that part of the file is non-null, @code{diff} considers the file to be text; otherwise it considers the file to be binary. Sometimes you might want to force @code{diff} to consider files to be text. For example, you might be comparing text files that contain null characters; @code{diff} would erroneously decide that those are non-text files. Or you might be comparing documents that are in a format used by a word processing system that uses null characters to indicate special formatting. You can force @code{diff} to consider all files to be text files, and compare them line by line, by using the @samp{-a} or @samp{--text} option. If the files you compare using this option do not in fact contain text, they will probably contain few newline characters, and the @code{diff} output will consist of hunks showing differences between long lines of whatever characters the files contain. You can also force @code{diff} to consider all files to be binary files, and report only whether they differ (but not how). Use the @samp{--brief} option for this. In operating systems that distinguish between text and binary files, @code{diff} normally reads and writes all data as text. Use the @samp{--binary} option to force @code{diff} to read and write binary data instead. This option has no effect on a Posix-compliant system like GNU or traditional Unix. However, many personal computer operating systems represent the end of a line with a carriage return followed by a newline. On such systems, @code{diff} normally ignores these carriage returns on input and generates them at the end of each output line, but with the @samp{--binary} option @code{diff} treats each carriage return as just another input character, and does not generate a carriage return at the end of each output line. This can be useful when dealing with non-text files that are meant to be interchanged with Posix-compliant systems. If you want to compare two files byte by byte, you can use the @code{cmp} program with the @samp{-l} option to show the values of each differing byte in the two files. With GNU @code{cmp}, you can also use the @samp{-c} option to show the ASCII representation of those bytes. @xref{Invoking cmp}, for more information. If @code{diff3} thinks that any of the files it is comparing is binary (a non-text file), it normally reports an error, because such comparisons are usually not useful. @code{diff3} uses the same test as @code{diff} to decide whether a file is binary. As with @code{diff}, if the input files contain a few non-text characters but otherwise are like text files, you can force @code{diff3} to consider all files to be text files and compare them line by line by using the @samp{-a} or @samp{--text} options. @node Output Formats, Comparing Directories, Comparison, Top @chapter @code{diff} Output Formats @cindex output formats @cindex format of @code{diff} output @code{diff} has several mutually exclusive options for output format. The following sections describe each format, illustrating how @code{diff} reports the differences between two sample input files. @menu * Sample diff Input:: Sample @code{diff} input files for examples. * Normal:: Showing differences without surrounding text. * Context:: Showing differences with the surrounding text. * Side by Side:: Showing differences in two columns. * Scripts:: Generating scripts for other programs. * If-then-else:: Merging files with if-then-else. @end menu @node Sample diff Input, Normal, , Output Formats @section Two Sample Input Files @cindex @code{diff} sample input @cindex sample input for @code{diff} Here are two sample files that we will use in numerous examples to illustrate the output of @code{diff} and how various options can change it. This is the file @file{lao}: @example The Way that can be told of is not the eternal Way; The name that can be named is not the eternal name. The Nameless is the origin of Heaven and Earth; The Named is the mother of all things. Therefore let there always be non-being, so we may see their subtlety, And let there always be being, so we may see their outcome. The two are the same, But after they are produced, they have different names. @end example This is the file @file{tzu}: @example The Nameless is the origin of Heaven and Earth; The named is the mother of all things. Therefore let there always be non-being, so we may see their subtlety, And let there always be being, so we may see their outcome. The two are the same, But after they are produced, they have different names. They both may be called deep and profound. Deeper and more profound, The door of all subtleties! @end example In this example, the first hunk contains just the first two lines of @file{lao}, the second hunk contains the fourth line of @file{lao} opposing the second and third lines of @file{tzu}, and the last hunk contains just the last three lines of @file{tzu}. @node Normal, Context, Sample diff Input, Output Formats @section Showing Differences Without Context @cindex normal output format @cindex @samp{<} output format The ``normal'' @code{diff} output format shows each hunk of differences without any surrounding context. Sometimes such output is the clearest way to see how lines have changed, without the clutter of nearby unchanged lines (although you can get similar results with the context or unified formats by using 0 lines of context). However, this format is no longer widely used for sending out patches; for that purpose, the context format (@pxref{Context Format}) and the unified format (@pxref{Unified Format}) are superior. Normal format is the default for compatibility with older versions of @code{diff} and the Posix standard. @menu * Detailed Normal:: A detailed description of normal output format. * Example Normal:: Sample output in the normal format. @end menu @node Detailed Normal, Example Normal, , Normal @subsection Detailed Description of Normal Format The normal output format consists of one or more hunks of differences; each hunk shows one area where the files differ. Normal format hunks look like this: @example @var{change-command} < @var{from-file-line} < @var{from-file-line}@dots{} --- > @var{to-file-line} > @var{to-file-line}@dots{} @end example There are three types of change commands. Each consists of a line number or comma-separated range of lines in the first file, a single character indicating the kind of change to make, and a line number or comma-separated range of lines in the second file. All line numbers are the original line numbers in each file. The types of change commands are: @table @samp @item @var{l}a@var{r} Add the lines in range @var{r} of the second file after line @var{l} of the first file. For example, @samp{8a12,15} means append lines 12--15 of file 2 after line 8 of file 1; or, if changing file 2 into file 1, delete lines 12--15 of file 2. @item @var{f}c@var{t} Replace the lines in range @var{f} of the first file with lines in range @var{t} of the second file. This is like a combined add and delete, but more compact. For example, @samp{5,7c8,10} means change lines 5--7 of file 1 to read as lines 8--10 of file 2; or, if changing file 2 into file 1, change lines 8--10 of file 2 to read as lines 5--7 of file 1. @item @var{r}d@var{l} Delete the lines in range @var{r} from the first file; line @var{l} is where they would have appeared in the second file had they not been deleted. For example, @samp{5,7d3} means delete lines 5--7 of file 1; or, if changing file 2 into file 1, append lines 5--7 of file 1 after line 3 of file 2. @end table @node Example Normal, , Detailed Normal, Normal @subsection An Example of Normal Format Here is the output of the command @samp{diff lao tzu} (@pxref{Sample diff Input}, for the complete contents of the two files). Notice that it shows only the lines that are different between the two files. @example 1,2d0 < The Way that can be told of is not the eternal Way; < The name that can be named is not the eternal name. 4c2,3 < The Named is the mother of all things. --- > The named is the mother of all things. > 11a11,13 > They both may be called deep and profound. > Deeper and more profound, > The door of all subtleties! @end example @node Context, Side by Side, Normal, Output Formats @section Showing Differences in Their Context @cindex context output format @cindex @samp{!} output format Usually, when you are looking at the differences between files, you will also want to see the parts of the files near the lines that differ, to help you understand exactly what has changed. These nearby parts of the files are called the @dfn{context}. GNU @code{diff} provides two output formats that show context around the differing lines: @dfn{context format} and @dfn{unified format}. It can optionally show in which function or section of the file the differing lines are found. If you are distributing new versions of files to other people in the form of @code{diff} output, you should use one of the output formats that show context so that they can apply the diffs even if they have made small changes of their own to the files. @code{patch} can apply the diffs in this case by searching in the files for the lines of context around the differing lines; if those lines are actually a few lines away from where the diff says they are, @code{patch} can adjust the line numbers accordingly and still apply the diff correctly. @xref{Imperfect}, for more information on using @code{patch} to apply imperfect diffs. @menu * Context Format:: An output format that shows surrounding lines. * Unified Format:: A more compact output format that shows context. * Sections:: Showing which sections of the files differences are in. * Alternate Names:: Showing alternate file names in context headers. @end menu @node Context Format, Unified Format, , Context @subsection Context Format The context output format shows several lines of context around the lines that differ. It is the standard format for distributing updates to source code. To select this output format, use the @samp{-C @var{lines}}, @samp{--context@r{[}=@var{lines}@r{]}}, or @samp{-c} option. The argument @var{lines} that some of these options take is the number of lines of context to show. If you do not specify @var{lines}, it defaults to three. For proper operation, @code{patch} typically needs at least two lines of context. @menu * Detailed Context:: A detailed description of the context output format. * Example Context:: Sample output in context format. * Less Context:: Another sample with less context. @end menu @node Detailed Context, Example Context, , Context Format @subsubsection Detailed Description of Context Format The context output format starts with a two-line header, which looks like this: @example *** @var{from-file} @var{from-file-modification-time} --- @var{to-file} @var{to-file-modification time} @end example @noindent You can change the header's content with the @samp{-L @var{label}} or @samp{--label=@var{label}} option; see @ref{Alternate Names}. Next come one or more hunks of differences; each hunk shows one area where the files differ. Context format hunks look like this: @example *************** *** @var{from-file-line-range} **** @var{from-file-line} @var{from-file-line}@dots{} --- @var{to-file-line-range} ---- @var{to-file-line} @var{to-file-line}@dots{} @end example The lines of context around the lines that differ start with two space characters. The lines that differ between the two files start with one of the following indicator characters, followed by a space character: @table @samp @item ! A line that is part of a group of one or more lines that changed between the two files. There is a corresponding group of lines marked with @samp{!} in the part of this hunk for the other file. @item + An ``inserted'' line in the second file that corresponds to nothing in the first file. @item - A ``deleted'' line in the first file that corresponds to nothing in the second file. @end table If all of the changes in a hunk are insertions, the lines of @var{from-file} are omitted. If all of the changes are deletions, the lines of @var{to-file} are omitted. @node Example Context, Less Context, Detailed Context, Context Format @subsubsection An Example of Context Format Here is the output of @samp{diff -c lao tzu} (@pxref{Sample diff Input}, for the complete contents of the two files). Notice that up to three lines that are not different are shown around each line that is different; they are the context lines. Also notice that the first two hunks have run together, because their contents overlap. @example *** lao Sat Jan 26 23:30:39 1991 --- tzu Sat Jan 26 23:30:50 1991 *************** *** 1,7 **** - The Way that can be told of is not the eternal Way; - The name that can be named is not the eternal name. The Nameless is the origin of Heaven and Earth; ! The Named is the mother of all things. Therefore let there always be non-being, so we may see their subtlety, And let there always be being, --- 1,6 ---- The Nameless is the origin of Heaven and Earth; ! The named is the mother of all things. ! Therefore let there always be non-being, so we may see their subtlety, And let there always be being, *************** *** 9,11 **** --- 8,13 ---- The two are the same, But after they are produced, they have different names. + They both may be called deep and profound. + Deeper and more profound, + The door of all subtleties! @end example @node Less Context, , Example Context, Context Format @subsubsection An Example of Context Format with Less Context Here is the output of @samp{diff --context=1 lao tzu} (@pxref{Sample diff Input}, for the complete contents of the two files). Notice that at most one context line is reported here. @example *** lao Sat Jan 26 23:30:39 1991 --- tzu Sat Jan 26 23:30:50 1991 *************** *** 1,5 **** - The Way that can be told of is not the eternal Way; - The name that can be named is not the eternal name. The Nameless is the origin of Heaven and Earth; ! The Named is the mother of all things. Therefore let there always be non-being, --- 1,4 ---- The Nameless is the origin of Heaven and Earth; ! The named is the mother of all things. ! Therefore let there always be non-being, *************** *** 11 **** --- 10,13 ---- they have different names. + They both may be called deep and profound. + Deeper and more profound, + The door of all subtleties! @end example @node Unified Format, Sections, Context Format, Context @subsection Unified Format @cindex unified output format @cindex @samp{+-} output format The unified output format is a variation on the context format that is more compact because it omits redundant context lines. To select this output format, use the @samp{-U @var{lines}}, @samp{--unified@r{[}=@var{lines}@r{]}}, or @samp{-u} option. The argument @var{lines} is the number of lines of context to show. When it is not given, it defaults to three. At present, only GNU @code{diff} can produce this format and only GNU @code{patch} can automatically apply diffs in this format. For proper operation, @code{patch} typically needs at least two lines of context. @menu * Detailed Unified:: A detailed description of unified format. * Example Unified:: Sample output in unified format. @end menu @node Detailed Unified, Example Unified, , Unified Format @subsubsection Detailed Description of Unified Format The unified output format starts with a two-line header, which looks like this: @example --- @var{from-file} @var{from-file-modification-time} +++ @var{to-file} @var{to-file-modification-time} @end example @noindent You can change the header's content with the @samp{-L @var{label}} or @samp{--label=@var{label}} option; see @xref{Alternate Names}. Next come one or more hunks of differences; each hunk shows one area where the files differ. Unified format hunks look like this: @example @@@@ @var{from-file-range} @var{to-file-range} @@@@ @var{line-from-either-file} @var{line-from-either-file}@dots{} @end example The lines common to both files begin with a space character. The lines that actually differ between the two files have one of the following indicator characters in the left column: @table @samp @item + A line was added here to the first file. @item - A line was removed here from the first file. @end table @node Example Unified, , Detailed Unified, Unified Format @subsubsection An Example of Unified Format Here is the output of the command @samp{diff -u lao tzu} (@pxref{Sample diff Input}, for the complete contents of the two files): @example --- lao Sat Jan 26 23:30:39 1991 +++ tzu Sat Jan 26 23:30:50 1991 @@@@ -1,7 +1,6 @@@@ -The Way that can be told of is not the eternal Way; -The name that can be named is not the eternal name. The Nameless is the origin of Heaven and Earth; -The Named is the mother of all things. +The named is the mother of all things. + Therefore let there always be non-being, so we may see their subtlety, And let there always be being, @@@@ -9,3 +8,6 @@@@ The two are the same, But after they are produced, they have different names. +They both may be called deep and profound. +Deeper and more profound, +The door of all subtleties! @end example @node Sections, Alternate Names, Unified Format, Context @subsection Showing Which Sections Differences Are in @cindex headings @cindex section headings Sometimes you might want to know which part of the files each change falls in. If the files are source code, this could mean which function was changed. If the files are documents, it could mean which chapter or appendix was changed. GNU @code{diff} can show this by displaying the nearest section heading line that precedes the differing lines. Which lines are ``section headings'' is determined by a regular expression. @menu * Specified Headings:: Showing headings that match regular expressions. * C Function Headings:: Showing headings of C functions. @end menu @node Specified Headings, C Function Headings, , Sections @subsubsection Showing Lines That Match Regular Expressions @cindex specified headings @cindex regular expression matching headings To show in which sections differences occur for files that are not source code for C or similar languages, use the @samp{-F @var{regexp}} or @samp{--show-function-line=@var{regexp}} option. @code{diff} considers lines that match the argument @var{regexp} to be the beginning of a section of the file. Here are suggested regular expressions for some common languages: @c Please add to this list, e.g. Fortran, Pascal. @table @samp @item ^[A-Za-z_] C, C++, Prolog @item ^( Lisp @item ^@@\(chapter\|appendix\|unnumbered\|chapheading\) Texinfo @end table This option does not automatically select an output format; in order to use it, you must select the context format (@pxref{Context Format}) or unified format (@pxref{Unified Format}). In other output formats it has no effect. The @samp{-F} and @samp{--show-function-line} options find the nearest unchanged line that precedes each hunk of differences and matches the given regular expression. Then they add that line to the end of the line of asterisks in the context format, or to the @samp{@@@@} line in unified format. If no matching line exists, they leave the output for that hunk unchanged. If that line is more than 40 characters long, they output only the first 40 characters. You can specify more than one regular expression for such lines; @code{diff} tries to match each line against each regular expression, starting with the last one given. This means that you can use @samp{-p} and @samp{-F} together, if you wish. @node C Function Headings, , Specified Headings, Sections @subsubsection Showing C Function Headings @cindex C function headings @cindex function headings, C To show in which functions differences occur for C and similar languages, you can use the @samp{-p} or @samp{--show-c-function} option. This option automatically defaults to the context output format (@pxref{Context Format}), with the default number of lines of context. You can override that number with @samp{-C @var{lines}} elsewhere in the command line. You can override both the format and the number with @samp{-U @var{lines}} elsewhere in the command line. The @samp{-p} and @samp{--show-c-function} options are equivalent to @samp{-F'^[_a-zA-Z$]'} if the unified format is specified, otherwise @samp{-c -F'^[_a-zA-Z$]'} (@pxref{Specified Headings}). GNU @code{diff} provides them for the sake of convenience. @node Alternate Names, , Sections, Context @subsection Showing Alternate File Names @cindex alternate file names @cindex file name alternates If you are comparing two files that have meaningless or uninformative names, you might want @code{diff} to show alternate names in the header of the context and unified output formats. To do this, use the @samp{-L @var{label}} or @samp{--label=@var{label}} option. The first time you give this option, its argument replaces the name and date of the first file in the header; the second time, its argument replaces the name and date of the second file. If you give this option more than twice, @code{diff} reports an error. The @samp{-L} option does not affect the file names in the @code{pr} header when the @samp{-l} or @samp{--paginate} option is used (@pxref{Pagination}). Here are the first two lines of the output from @samp{diff -C2 -Loriginal -Lmodified lao tzu}: @example *** original --- modified @end example @node Side by Side, Scripts, Context, Output Formats @section Showing Differences Side by Side @cindex side by side @cindex two-column output @cindex columnar output @code{diff} can produce a side by side difference listing of two files. The files are listed in two columns with a gutter between them. The gutter contains one of the following markers: @table @asis @item white space The corresponding lines are in common. That is, either the lines are identical, or the difference is ignored because of one of the @samp{--ignore} options (@pxref{White Space}). @item @samp{|} The corresponding lines differ, and they are either both complete or both incomplete. @item @samp{<} The files differ and only the first file contains the line. @item @samp{>} The files differ and only the second file contains the line. @item @samp{(} Only the first file contains the line, but the difference is ignored. @item @samp{)} Only the second file contains the line, but the difference is ignored. @item @samp{\} The corresponding lines differ, and only the first line is incomplete. @item @samp{/} The corresponding lines differ, and only the second line is incomplete. @end table Normally, an output line is incomplete if and only if the lines that it contains are incomplete; @xref{Incomplete Lines}. However, when an output line represents two differing lines, one might be incomplete while the other is not. In this case, the output line is complete, but its the gutter is marked @samp{\} if the first line is incomplete, @samp{/} if the second line is. Side by side format is sometimes easiest to read, but it has limitations. It generates much wider output than usual, and truncates lines that are too long to fit. Also, it relies on lining up output more heavily than usual, so its output looks particularly bad if you use varying width fonts, nonstandard tab stops, or nonprinting characters. You can use the @code{sdiff} command to interactively merge side by side differences. @xref{Interactive Merging}, for more information on merging files. @menu * Side by Side Format:: Controlling side by side output format. * Example Side by Side:: Sample side by side output. @end menu @node Side by Side Format, Example Side by Side, , Side by Side @section Controlling Side by Side Format @cindex side by side format The @samp{-y} or @samp{--side-by-side} option selects side by side format. Because side by side output lines contain two input lines, they are wider than usual. They are normally 130 columns, which can fit onto a traditional printer line. You can set the length of output lines with the @samp{-W @var{columns}} or @samp{--width=@var{columns}} option. The output line is split into two halves of equal length, separated by a small gutter to mark differences; the right half is aligned to a tab stop so that tabs line up. Input lines that are too long to fit in half of an output line are truncated for output. The @samp{--left-column} option prints only the left column of two common lines. The @samp{--suppress-common-lines} option suppresses common lines entirely. @node Example Side by Side, , Side by Side Format, Side by Side @subsection An Example of Side by Side Format Here is the output of the command @samp{diff -y -W 72 lao tzu} (@pxref{Sample diff Input}, for the complete contents of the two files). @example The Way that can be told of is n < The name that can be named is no < The Nameless is the origin of He The Nameless is the origin of He The Named is the mother of all t | The named is the mother of all t > Therefore let there always be no Therefore let there always be no so we may see their subtlety, so we may see their subtlety, And let there always be being, And let there always be being, so we may see their outcome. so we may see their outcome. The two are the same, The two are the same, But after they are produced, But after they are produced, they have different names. they have different names. > They both may be called deep and > Deeper and more profound, > The door of all subtleties! @end example @node Scripts, If-then-else, Side by Side, Output Formats @section Making Edit Scripts @cindex script output formats Several output modes produce command scripts for editing @var{from-file} to produce @var{to-file}. @menu * ed Scripts:: Using @code{diff} to produce commands for @code{ed}. * Forward ed:: Making forward @code{ed} scripts. * RCS:: A special @code{diff} output format used by RCS. @end menu @node ed Scripts, Forward ed, , Scripts @subsection @code{ed} Scripts @cindex @code{ed} script output format @code{diff} can produce commands that direct the @code{ed} text editor to change the first file into the second file. Long ago, this was the only output mode that was suitable for editing one file into another automatically; today, with @code{patch}, it is almost obsolete. Use the @samp{-e} or @samp{--ed} option to select this output format. Like the normal format (@pxref{Normal}), this output format does not show any context; unlike the normal format, it does not include the information necessary to apply the diff in reverse (to produce the first file if all you have is the second file and the diff). If the file @file{d} contains the output of @samp{diff -e old new}, then the command @samp{(cat d && echo w) | ed - old} edits @file{old} to make it a copy of @file{new}. More generally, if @file{d1}, @file{d2}, @dots{}, @file{dN} contain the outputs of @samp{diff -e old new1}, @samp{diff -e new1 new2}, @dots{}, @samp{diff -e newN-1 newN}, respectively, then the command @samp{(cat d1 d2 @dots{} dN && echo w) | ed - old} edits @file{old} to make it a copy of @file{newN}. @menu * Detailed ed:: A detailed description of @code{ed} format. * Example ed:: A sample @code{ed} script. @end menu @node Detailed ed, Example ed, , ed Scripts @subsubsection Detailed Description of @code{ed} Format The @code{ed} output format consists of one or more hunks of differences. The changes closest to the ends of the files come first so that commands that change the number of lines do not affect how @code{ed} interprets line numbers in succeeding commands. @code{ed} format hunks look like this: @example @var{change-command} @var{to-file-line} @var{to-file-line}@dots{} . @end example Because @code{ed} uses a single period on a line to indicate the end of input, GNU @code{diff} protects lines of changes that contain a single period on a line by writing two periods instead, then writing a subsequent @code{ed} command to change the two periods into one. The @code{ed} format cannot represent an incomplete line, so if the second file ends in a changed incomplete line, @code{diff} reports an error and then pretends that a newline was appended. There are three types of change commands. Each consists of a line number or comma-separated range of lines in the first file and a single character indicating the kind of change to make. All line numbers are the original line numbers in the file. The types of change commands are: @table @samp @item @var{l}a Add text from the second file after line @var{l} in the first file. For example, @samp{8a} means to add the following lines after line 8 of file 1. @item @var{r}c Replace the lines in range @var{r} in the first file with the following lines. Like a combined add and delete, but more compact. For example, @samp{5,7c} means change lines 5--7 of file 1 to read as the text file 2. @item @var{r}d Delete the lines in range @var{r} from the first file. For example, @samp{5,7d} means delete lines 5--7 of file 1. @end table @node Example ed, , Detailed ed, ed Scripts @subsubsection Example @code{ed} Script Here is the output of @samp{diff -e lao tzu} (@pxref{Sample diff Input}, for the complete contents of the two files): @example 11a They both may be called deep and profound. Deeper and more profound, The door of all subtleties! . 4c The named is the mother of all things. . 1,2d @end example @node Forward ed, RCS, ed Scripts, Scripts @subsection Forward @code{ed} Scripts @cindex forward @code{ed} script output format @code{diff} can produce output that is like an @code{ed} script, but with hunks in forward (front to back) order. The format of the commands is also changed slightly: command characters precede the lines they modify, spaces separate line numbers in ranges, and no attempt is made to disambiguate hunk lines consisting of a single period. Like @code{ed} format, forward @code{ed} format cannot represent incomplete lines. Forward @code{ed} format is not very useful, because neither @code{ed} nor @code{patch} can apply diffs in this format. It exists mainly for compatibility with older versions of @code{diff}. Use the @samp{-f} or @samp{--forward-ed} option to select it. @node RCS, , Forward ed, Scripts @subsection RCS Scripts @cindex RCS script output format The RCS output format is designed specifically for use by the Revision Control System, which is a set of free programs used for organizing different versions and systems of files. Use the @samp{-n} or @samp{--rcs} option to select this output format. It is like the forward @code{ed} format (@pxref{Forward ed}), but it can represent arbitrary changes to the contents of a file because it avoids the forward @code{ed} format's problems with lines consisting of a single period and with incomplete lines. Instead of ending text sections with a line consisting of a single period, each command specifies the number of lines it affects; a combination of the @samp{a} and @samp{d} commands are used instead of @samp{c}. Also, if the second file ends in a changed incomplete line, then the output also ends in an incomplete line. Here is the output of @samp{diff -n lao tzu} (@pxref{Sample diff Input}, for the complete contents of the two files): @example d1 2 d4 1 a4 2 The named is the mother of all things. a11 3 They both may be called deep and profound. Deeper and more profound, The door of all subtleties! @end example @node If-then-else, , Scripts, Output Formats @section Merging Files with If-then-else @cindex merged output format @cindex if-then-else output format @cindex C if-then-else output format @cindex @code{ifdef} output format You can use @code{diff} to merge two files of C source code. The output of @code{diff} in this format contains all the lines of both files. Lines common to both files are output just once; the differing parts are separated by the C preprocessor directives @code{#ifdef @var{name}} or @code{#ifndef @var{name}}, @code{#else}, and @code{#endif}. When compiling the output, you select which version to use by either defining or leaving undefined the macro @var{name}. To merge two files, use @code{diff} with the @samp{-D @var{name}} or @samp{--ifdef=@var{name}} option. The argument @var{name} is the C preprocessor identifier to use in the @code{#ifdef} and @code{#ifndef} directives. For example, if you change an instance of @code{wait (&s)} to @code{waitpid (-1, &s, 0)} and then merge the old and new files with the @samp{--ifdef=HAVE_WAITPID} option, then the affected part of your code might look like this: @example do @{ #ifndef HAVE_WAITPID if ((w = wait (&s)) < 0 && errno != EINTR) #else /* HAVE_WAITPID */ if ((w = waitpid (-1, &s, 0)) < 0 && errno != EINTR) #endif /* HAVE_WAITPID */ return w; @} while (w != child); @end example You can specify formats for languages other than C by using line group formats and line formats, as described in the next sections. @menu * Line Group Formats:: Formats for general if-then-else line groups. * Line Formats:: Formats for each line in a line group. * Detailed If-then-else:: A detailed description of if-then-else format. * Example If-then-else:: Sample if-then-else format output. @end menu @node Line Group Formats, Line Formats, , If-then-else @subsection Line Group Formats @cindex line group formats @cindex formats for if-then-else line groups Line group formats let you specify formats suitable for many applications that allow if-then-else input, including programming languages and text formatting languages. A line group format specifies the output format for a contiguous group of similar lines. For example, the following command compares the TeX files @file{old} and @file{new}, and outputs a merged file in which old regions are surrounded by @samp{\begin@{em@}}-@samp{\end@{em@}} lines, and new regions are surrounded by @samp{\begin@{bf@}}-@samp{\end@{bf@}} lines. @example diff \ --old-group-format='\begin@{em@} %<\end@{em@} ' \ --new-group-format='\begin@{bf@} %>\end@{bf@} ' \ old new @end example The following command is equivalent to the above example, but it is a little more verbose, because it spells out the default line group formats. @example diff \ --old-group-format='\begin@{em@} %<\end@{em@} ' \ --new-group-format='\begin@{bf@} %>\end@{bf@} ' \ --unchanged-group-format='%=' \ --changed-group-format='\begin@{em@} %<\end@{em@} \begin@{bf@} %>\end@{bf@} ' \ old new @end example Here is a more advanced example, which outputs a diff listing with headers containing line numbers in a ``plain English'' style. @example diff \ --unchanged-group-format='' \ --old-group-format='-------- %dn line%(n=1?:s) deleted at %df: %<' \ --new-group-format='-------- %dN line%(N=1?:s) added after %de: %>' \ --changed-group-format='-------- %dn line%(n=1?:s) changed at %df: %<-------- to: %>' \ old new @end example To specify a line group format, use @code{diff} with one of the options listed below. You can specify up to four line group formats, one for each kind of line group. You should quote @var{format}, because it typically contains shell metacharacters. @table @samp @item --old-group-format=@var{format} These line groups are hunks containing only lines from the first file. The default old group format is the same as the changed group format if it is specified; otherwise it is a format that outputs the line group as-is. @item --new-group-format=@var{format} These line groups are hunks containing only lines from the second file. The default new group format is same as the the changed group format if it is specified; otherwise it is a format that outputs the line group as-is. @item --changed-group-format=@var{format} These line groups are hunks containing lines from both files. The default changed group format is the concatenation of the old and new group formats. @item --unchanged-group-format=@var{format} These line groups contain lines common to both files. The default unchanged group format is a format that outputs the line group as-is. @end table In a line group format, ordinary characters represent themselves; conversion specifications start with @samp{%} and have one of the following forms. @table @samp @item %< stands for the lines from the first file, including the trailing newline. Each line is formatted according to the old line format (@pxref{Line Formats}). @item %> stands for the lines from the second file, including the trailing newline. Each line is formatted according to the new line format. @item %= stands for the lines common to both files, including the trailing newline. Each line is formatted according to the unchanged line format. @item %% stands for @samp{%}. @item %c'@var{C}' where @var{C} is a single character, stands for @var{C}. @var{C} may not be a backslash or an apostrophe. For example, @samp{%c':'} stands for a colon, even inside the then-part of an if-then-else format, which a colon would normally terminate. @item %c'\@var{O}' where @var{O} is a string of 1, 2, or 3 octal digits, stands for the character with octal code @var{O}. For example, @samp{%c'\0'} stands for a null character. @item @var{F}@var{n} where @var{F} is a @code{printf} conversion specification and @var{n} is one of the following letters, stands for @var{n}'s value formatted with @var{F}. @table @samp @item e The line number of the line just before the group in the old file. @item f The line number of the first line in the group in the old file; equals @var{e} + 1. @item l The line number of the last line in the group in the old file. @item m The line number of the line just after the group in the old file; equals @var{l} + 1. @item n The number of lines in the group in the old file; equals @var{l} - @var{f} + 1. @item E, F, L, M, N Likewise, for lines in the new file. @end table The @code{printf} conversion specification can be @samp{%d}, @samp{%o}, @samp{%x}, or @samp{%X}, specifying decimal, octal, lower case hexadecimal, or upper case hexadecimal output respectively. After the @samp{%} the following options can appear in sequence: a @samp{-} specifying left-justification; an integer specifying the minimum field width; and a period followed by an optional integer specifying the minimum number of digits. For example, @samp{%5dN} prints the number of new lines in the group in a field of width 5 characters, using the @code{printf} format @code{"%5d"}. @item (@var{A}=@var{B}?@var{T}:@var{E}) If @var{A} equals @var{B} then @var{T} else @var{E}. @var{A} and @var{B} are each either a decimal constant or a single letter interpreted as above. This format spec is equivalent to @var{T} if @var{A}'s value equals @var{B}'s; otherwise it is equivalent to @var{E}. For example, @samp{%(N=0?no:%dN) line%(N=1?:s)} is equivalent to @samp{no lines} if @var{N} (the number of lines in the group in the the new file) is 0, to @samp{1 line} if @var{N} is 1, and to @samp{%dN lines} otherwise. @end table @node Line Formats, Detailed If-then-else, Line Group Formats, If-then-else @subsection Line Formats @cindex line formats Line formats control how each line taken from an input file is output as part of a line group in if-then-else format. For example, the following command outputs text with a one-column change indicator to the left of the text. The first column of output is @samp{-} for deleted lines, @samp{|} for added lines, and a space for unchanged lines. The formats contain newline characters where newlines are desired on output. @example diff \ --old-line-format='-%l ' \ --new-line-format='|%l ' \ --unchanged-line-format=' %l ' \ old new @end example To specify a line format, use one of the following options. You should quote @var{format}, since it often contains shell metacharacters. @table @samp @item --old-line-format=@var{format} formats lines just from the first file. @item --new-line-format=@var{format} formats lines just from the second file. @item --unchanged-line-format=@var{format} formats lines common to both files. @item --line-format=@var{format} formats all lines; in effect, it sets all three above options simultaneously. @end table In a line format, ordinary characters represent themselves; conversion specifications start with @samp{%} and have one of the following forms. @table @samp @item %l stands for the the contents of the line, not counting its trailing newline (if any). This format ignores whether the line is incomplete; @xref{Incomplete Lines}. @item %L stands for the the contents of the line, including its trailing newline (if any). If a line is incomplete, this format preserves its incompleteness. @item %% stands for @samp{%}. @item %c'@var{C}' where @var{C} is a single character, stands for @var{C}. @var{C} may not be a backslash or an apostrophe. For example, @samp{%c':'} stands for a colon. @item %c'\@var{O}' where @var{O} is a string of 1, 2, or 3 octal digits, stands for the character with octal code @var{O}. For example, @samp{%c'\0'} stands for a null character. @item @var{F}n where @var{F} is a @code{printf} conversion specification, stands for the line number formatted with @var{F}. For example, @samp{%.5dn} prints the line number using the @code{printf} format @code{"%.5d"}. @xref{Line Group Formats}, for more about printf conversion specifications. @end table The default line format is @samp{%l} followed by a newline character. If the input contains tab characters and it is important that they line up on output, you should ensure that @samp{%l} or @samp{%L} in a line format is just after a tab stop (e.g.@: by preceding @samp{%l} or @samp{%L} with a tab character), or you should use the @samp{-t} or @samp{--expand-tabs} option. Taken together, the line and line group formats let you specify many different formats. For example, the following command uses a format similar to @code{diff}'s normal format. You can tailor this command to get fine control over @code{diff}'s output. @example diff \ --old-line-format='< %l ' \ --new-line-format='> %l ' \ --old-group-format='%df%(f=l?:,%dl)d%dE %<' \ --new-group-format='%dea%dF%(F=L?:,%dL) %>' \ --changed-group-format='%df%(f=l?:,%dl)c%dF%(F=L?:,%dL) %<--- %>' \ --unchanged-group-format='' \ old new @end example @node Detailed If-then-else, Example If-then-else, Line Formats, If-then-else @subsection Detailed Description of If-then-else Format For lines common to both files, @code{diff} uses the unchanged line group format. For each hunk of differences in the merged output format, if the hunk contains only lines from the first file, @code{diff} uses the old line group format; if the hunk contains only lines from the second file, @code{diff} uses the new group format; otherwise, @code{diff} uses the changed group format. The old, new, and unchanged line formats specify the output format of lines from the first file, lines from the second file, and lines common to both files, respectively. The option @samp{--ifdef=@var{name}} is equivalent to the following sequence of options using shell syntax: @example --old-group-format='#ifndef @var{name} %<#endif /* not @var{name} */ ' \ --new-group-format='#ifdef @var{name} %>#endif /* @var{name} */ ' \ --unchanged-group-format='%=' \ --changed-group-format='#ifndef @var{name} %<#else /* @var{name} */ %>#endif /* @var{name} */ ' @end example You should carefully check the @code{diff} output for proper nesting. For example, when using the the @samp{-D @var{name}} or @samp{--ifdef=@var{name}} option, you should check that if the differing lines contain any of the C preprocessor directives @samp{#ifdef}, @samp{#ifndef}, @samp{#else}, @samp{#elif}, or @samp{#endif}, they are nested properly and match. If they don't, you must make corrections manually. It is a good idea to carefully check the resulting code anyway to make sure that it really does what you want it to; depending on how the input files were produced, the output might contain duplicate or otherwise incorrect code. The @code{patch} @samp{-D @var{name}} option behaves just like the @code{diff} @samp{-D @var{name}} option, except it operates on a file and a diff to produce a merged file; @xref{patch Options}. @node Example If-then-else, , Detailed If-then-else, If-then-else @subsection An Example of If-then-else Format Here is the output of @samp{diff -DTWO lao tzu} (@pxref{Sample diff Input}, for the complete contents of the two files): @example #ifndef TWO The Way that can be told of is not the eternal Way; The name that can be named is not the eternal name. #endif /* not TWO */ The Nameless is the origin of Heaven and Earth; #ifndef TWO The Named is the mother of all things. #else /* TWO */ The named is the mother of all things. #endif /* TWO */ Therefore let there always be non-being, so we may see their subtlety, And let there always be being, so we may see their outcome. The two are the same, But after they are produced, they have different names. #ifdef TWO They both may be called deep and profound. Deeper and more profound, The door of all subtleties! #endif /* TWO */ @end example @node Comparing Directories, Adjusting Output, Output Formats, Top @chapter Comparing Directories You can use @code{diff} to compare some or all of the files in two directory trees. When both file name arguments to @code{diff} are directories, it compares each file that is contained in both directories, examining file names in alphabetical order. Normally @code{diff} is silent about pairs of files that contain no differences, but if you use the @samp{-s} or @samp{--report-identical-files} option, it reports pairs of identical files. Normally @code{diff} reports subdirectories common to both directories without comparing subdirectories' files, but if you use the @samp{-r} or @samp{--recursive} option, it compares every corresponding pair of files in the directory trees, as many levels deep as they go. For file names that are in only one of the directories, @code{diff} normally does not show the contents of the file that exists; it reports only that the file exists in that directory and not in the other. You can make @code{diff} act as though the file existed but was empty in the other directory, so that it outputs the entire contents of the file that actually exists. (It is output as either an insertion or a deletion, depending on whether it is in the first or the second directory given.) To do this, use the @samp{-N} or @samp{--new-file} option. If the older directory contains one or more large files that are not in the newer directory, you can make the patch smaller by using the @samp{-P} or @samp{--unidirectional-new-file} option instead of @samp{-N}. This option is like @samp{-N} except that it only inserts the contents of files that appear in the second directory but not the first (that is, files that were added). At the top of the patch, write instructions for the user applying the patch to remove the files that were deleted before applying the patch. @xref{Making Patches}, for more discussion of making patches for distribution. To ignore some files while comparing directories, use the @samp{-x @var{pattern}} or @samp{--exclude=@var{pattern}} option. This option ignores any files or subdirectories whose base names match the shell pattern @var{pattern}. Unlike in the shell, a period at the start of the base of a file name matches a wildcard at the start of a pattern. You should enclose @var{pattern} in quotes so that the shell does not expand it. For example, the option @samp{-x '*.[ao]'} ignores any file whose name ends with @samp{.a} or @samp{.o}. This option accumulates if you specify it more than once. For example, using the options @samp{-x 'RCS' -x '*,v'} ignores any file or subdirectory whose base name is @samp{RCS} or ends with @samp{,v}. If you need to give this option many times, you can instead put the patterns in a file, one pattern per line, and use the @samp{-X @var{file}} or @samp{--exclude-from=@var{file}} option. If you have been comparing two directories and stopped partway through, later you might want to continue where you left off. You can do this by using the @samp{-S @var{file}} or @samp{--starting-file=@var{file}} option. This compares only the file @var{file} and all alphabetically later files in the topmost directory level. @node Adjusting Output, diff Performance, Comparing Directories, Top @chapter Making @code{diff} Output Prettier @code{diff} provides several ways to adjust the appearance of its output. These adjustments can be applied to any output format. @menu * Tabs:: Preserving the alignment of tabstops. * Pagination:: Page numbering and timestamping @code{diff} output. @end menu @node Tabs, Pagination, , Adjusting Output @section Preserving Tabstop Alignment @cindex tabstop alignment @cindex aligning tabstops The lines of text in some of the @code{diff} output formats are preceded by one or two characters that indicate whether the text is inserted, deleted, or changed. The addition of those characters can cause tabs to move to the next tabstop, throwing off the alignment of columns in the line. GNU @code{diff} provides two ways to make tab-aligned columns line up correctly. The first way is to have @code{diff} convert all tabs into the correct number of spaces before outputting them; select this method with the @samp{-t} or @samp{--expand-tabs} option. @code{diff} assumes that tabstops are set every 8 columns. To use this form of output with @code{patch}, you must give @code{patch} the @samp{-l} or @samp{--ignore-white-space} option (@pxref{Changed White Space}, for more information). The other method for making tabs line up correctly is to add a tab character instead of a space after the indicator character at the beginning of the line. This ensures that all following tab characters are in the same position relative to tabstops that they were in the original files, so that the output is aligned correctly. Its disadvantage is that it can make long lines too long to fit on one line of the screen or the paper. It also does not work with the unified output format, which does not have a space character after the change type indicator character. Select this method with the @samp{-T} or @samp{--initial-tab} option. @node Pagination, , Tabs, Adjusting Output @section Paginating @code{diff} Output @cindex paginating @code{diff} output It can be convenient to have long output page-numbered and time-stamped. The @samp{-l} and @samp{--paginate} options do this by sending the @code{diff} output through the @code{pr} program. Here is what the page header might look like for @samp{diff -lc lao tzu}: @example Mar 11 13:37 1991 diff -lc lao tzu Page 1 @end example @node diff Performance, Comparing Three Files, Adjusting Output, Top @chapter @code{diff} Performance Tradeoffs @cindex performance of @code{diff} GNU @code{diff} runs quite efficiently; however, in some circumstances you can cause it to run faster or produce a more compact set of changes. There are two ways that you can affect the performance of GNU @code{diff} by changing the way it compares files. Performance has more than one dimension. These options improve one aspect of performance at the cost of another, or they improve performance in some cases while hurting it in others. The way that GNU @code{diff} determines which lines have changed always comes up with a near-minimal set of differences. Usually it is good enough for practical purposes. If the @code{diff} output is large, you might want @code{diff} to use a modified algorithm that sometimes produces a smaller set of differences. The @samp{-d} or @samp{--minimal} option does this; however, it can also cause @code{diff} to run more slowly than usual, so it is not the default behavior. When the files you are comparing are large and have small groups of changes scattered throughout them, you can use the @samp{-H} or @samp{--speed-large-files} option to make a different modification to the algorithm that @code{diff} uses. If the input files have a constant small density of changes, this option speeds up the comparisons without changing the output. If not, @code{diff} might produce a larger set of differences; however, the output will still be correct. Normally @code{diff} discards the prefix and suffix that is common to both files before it attempts to find a minimal set of differences. This makes @code{diff} run faster, but occasionally it may produce non-minimal output. The @samp{--horizon-lines=@var{lines}} option prevents @code{diff} from discarding the last @var{lines} lines of the prefix and the first @var{lines} lines of the suffix. This gives @code{diff} further opportunities to find a minimal output. @node Comparing Three Files, diff3 Merging, diff Performance, Top @chapter Comparing Three Files @cindex comparing three files @cindex format of @code{diff3} output Use the program @code{diff3} to compare three files and show any differences among them. (@code{diff3} can also merge files; see @ref{diff3 Merging}). The ``normal'' @code{diff3} output format shows each hunk of differences without surrounding context. Hunks are labeled depending on whether they are two-way or three-way, and lines are annotated by their location in the input files. @xref{Invoking diff3}, for more information on how to run @code{diff3}. @menu * Sample diff3 Input:: Sample @code{diff3} input for examples. * Detailed diff3 Normal:: A detailed description of normal output format. * diff3 Hunks:: The format of normal output format. * Example diff3 Normal:: Sample output in the normal format. @end menu @node Sample diff3 Input, Detailed diff3 Normal, , Comparing Three Files @section A Third Sample Input File @cindex @code{diff3} sample input @cindex sample input for @code{diff3} Here is a third sample file that will be used in examples to illustrate the output of @code{diff3} and how various options can change it. The first two files are the same that we used for @code{diff} (@pxref{Sample diff Input}). This is the third sample file, called @file{tao}: @example The Way that can be told of is not the eternal Way; The name that can be named is not the eternal name. The Nameless is the origin of Heaven and Earth; The named is the mother of all things. Therefore let there always be non-being, so we may see their subtlety, And let there always be being, so we may see their result. The two are the same, But after they are produced, they have different names. -- The Way of Lao-Tzu, tr. Wing-tsit Chan @end example @node Detailed diff3 Normal, diff3 Hunks, Sample diff3 Input, Comparing Three Files @section Detailed Description of @code{diff3} Normal Format Each hunk begins with a line marked @samp{====}. Three-way hunks have plain @samp{====} lines, and two-way hunks have @samp{1}, @samp{2}, or @samp{3} appended to specify which of the three input files differ in that hunk. The hunks contain copies of two or three sets of input lines each preceded by one or two commands identifying where the lines came from. Normally, two spaces precede each copy of an input line to distinguish it from the commands. But with the @samp{-T} or @samp{--initial-tab} option, @code{diff3} uses a tab instead of two spaces; this lines up tabs correctly. @xref{Tabs}, for more information. Commands take the following forms: @table @samp @item @var{file}:@var{l}a This hunk appears after line @var{l} of file @var{file}, and contains no lines in that file. To edit this file to yield the other files, one must append hunk lines taken from the other files. For example, @samp{1:11a} means that the hunk follows line 11 in the first file and contains no lines from that file. @item @var{file}:@var{r}c This hunk contains the lines in the range @var{r} of file @var{file}. The range @var{r} is a comma-separated pair of line numbers, or just one number if the range is a singleton. To edit this file to yield the other files, one must change the specified lines to be the lines taken from the other files. For example, @samp{2:11,13c} means that the hunk contains lines 11 through 13 from the second file. @end table If the last line in a set of input lines is incomplete (@pxref{Incomplete Lines}), it is distinguished on output from a full line by a following line that starts with @samp{\}. @node diff3 Hunks, Example diff3 Normal, Detailed diff3 Normal, Comparing Three Files @section @code{diff3} Hunks @cindex hunks for @code{diff3} @cindex @code{diff3} hunks Groups of lines that differ in two or three of the input files are called @dfn{diff3 hunks}, by analogy with @code{diff} hunks (@pxref{Hunks}). If all three input files differ in a @code{diff3} hunk, the hunk is called a @dfn{three-way hunk}; if just two input files differ, it is a @dfn{two-way hunk}. As with @code{diff}, several solutions are possible. When comparing the files @samp{A}, @samp{B}, and @samp{C}, @code{diff3} normally finds @code{diff3} hunks by merging the two-way hunks output by the two commands @samp{diff A B} and @samp{diff A C}. This does not necessarily minimize the size of the output, but exceptions should be rare. For example, suppose @file{F} contains the three lines @samp{a}, @samp{b}, @samp{f}, @file{G} contains the lines @samp{g}, @samp{b}, @samp{g}, and @file{H} contains the lines @samp{a}, @samp{b}, @samp{h}. @samp{diff3 F G H} might output the following: @example ====2 1:1c 3:1c a 2:1c g ==== 1:3c f 2:3c g 3:3c h @end example @noindent because it found a two-way hunk containing @samp{a} in the first and third files and @samp{g} in the second file, then the single line @samp{b} common to all three files, then a three-way hunk containing the last line of each file. @node Example diff3 Normal, , diff3 Hunks, Comparing Three Files @section An Example of @code{diff3} Normal Format Here is the output of the command @samp{diff3 lao tzu tao} (@pxref{Sample diff3 Input}, for the complete contents of the files). Notice that it shows only the lines that are different among the three files. @example ====2 1:1,2c 3:1,2c The Way that can be told of is not the eternal Way; The name that can be named is not the eternal name. 2:0a ====1 1:4c The Named is the mother of all things. 2:2,3c 3:4,5c The named is the mother of all things. ====3 1:8c 2:7c so we may see their outcome. 3:9c so we may see their result. ==== 1:11a 2:11,13c They both may be called deep and profound. Deeper and more profound, The door of all subtleties! 3:13,14c -- The Way of Lao-Tzu, tr. Wing-tsit Chan @end example @node diff3 Merging, Interactive Merging, Comparing Three Files, Top @chapter Merging From a Common Ancestor @cindex merging from a common ancestor When two people have made changes to copies of the same file, @code{diff3} can produce a merged output that contains both sets of changes together with warnings about conflicts. One might imagine programs with names like @code{diff4} and @code{diff5} to compare more than three files simultaneously, but in practice the need rarely arises. You can use @code{diff3} to merge three or more sets of changes to a file by merging two change sets at a time. @code{diff3} can incorporate changes from two modified versions into a common preceding version. This lets you merge the sets of changes represented by the two newer files. Specify the common ancestor version as the second argument and the two newer versions as the first and third arguments, like this: @example diff3 @var{mine} @var{older} @var{yours} @end example @noindent You can remember the order of the arguments by noting that they are in alphabetical order. @cindex conflict @cindex overlap You can think of this as subtracting @var{older} from @var{yours} and adding the result to @var{mine}, or as merging into @var{mine} the changes that would turn @var{older} into @var{yours}. This merging is well-defined as long as @var{mine} and @var{older} match in the neighborhood of each such change. This fails to be true when all three input files differ or when only @var{older} differs; we call this a @dfn{conflict}. When all three input files differ, we call the conflict an @dfn{overlap}. @code{diff3} gives you several ways to handle overlaps and conflicts. You can omit overlaps or conflicts, or select only overlaps, or mark conflicts with special @samp{<<<<<<<} and @samp{>>>>>>>} lines. @code{diff3} can output the merge results as an @code{ed} script that that can be applied to the first file to yield the merged output. However, it is usually better to have @code{diff3} generate the merged output directly; this bypasses some problems with @code{ed}. @menu * Which Changes:: Selecting changes to incorporate. * Marking Conflicts:: Marking conflicts. * Bypassing ed:: Generating merged output directly. * Merging Incomplete Lines:: How @code{diff3} merges incomplete lines. * Saving the Changed File:: Emulating System V behavior. @end menu @node Which Changes, Marking Conflicts, , diff3 Merging @section Selecting Which Changes to Incorporate @cindex overlapping change, selection of @cindex unmerged change You can select all unmerged changes from @var{older} to @var{yours} for merging into @var{mine} with the @samp{-e} or @samp{--ed} option. You can select only the nonoverlapping unmerged changes with @samp{-3} or @samp{--easy-only}, and you can select only the overlapping changes with @samp{-x} or @samp{--overlap-only}. The @samp{-e}, @samp{-3} and @samp{-x} options select only @dfn{unmerged changes}, i.e.@: changes where @var{mine} and @var{yours} differ; they ignore changes from @var{older} to @var{yours} where @var{mine} and @var{yours} are identical, because they assume that such changes have already been merged. If this assumption is not a safe one, you can use the @samp{-A} or @samp{--show-all} option (@pxref{Marking Conflicts}). Here is the output of the command @code{diff3} with each of these three options (@pxref{Sample diff3 Input}, for the complete contents of the files). Notice that @samp{-e} outputs the union of the disjoint sets of changes output by @samp{-3} and @samp{-x}. Output of @samp{diff3 -e lao tzu tao}: @example 11a -- The Way of Lao-Tzu, tr. Wing-tsit Chan . 8c so we may see their result. . @end example Output of @samp{diff3 -3 lao tzu tao}: @example 8c so we may see their result. . @end example Output of @samp{diff3 -x lao tzu tao}: @example 11a -- The Way of Lao-Tzu, tr. Wing-tsit Chan . @end example @node Marking Conflicts, Bypassing ed, Which Changes, diff3 Merging @section Marking Conflicts @cindex conflict marking @cindex @samp{<<<<<<<} for marking conflicts @code{diff3} can mark conflicts in the merged output by bracketing them with special marker lines. A conflict that comes from two files @var{A} and @var{B} is marked as follows: @example <<<<<<< @var{A} @r{lines from @var{A}} ======= @r{lines from @var{B}} >>>>>>> @var{B} @end example A conflict that comes from three files @var{A}, @var{B} and @var{C} is marked as follows: @example <<<<<<< @var{A} @r{lines from @var{A}} ||||||| @var{B} @r{lines from @var{B}} ======= @r{lines from @var{C}} >>>>>>> @var{C} @end example The @samp{-A} or @samp{--show-all} option acts like the @samp{-e} option, except that it brackets conflicts, and it outputs all changes from @var{older} to @var{yours}, not just the unmerged changes. Thus, given the sample input files (@pxref{Sample diff3 Input}), @samp{diff3 -A lao tzu tao} puts brackets around the conflict where only @file{tzu} differs: @example <<<<<<< tzu ======= The Way that can be told of is not the eternal Way; The name that can be named is not the eternal name. >>>>>>> tao @end example And it outputs the three-way conflict as follows: @example <<<<<<< lao ||||||| tzu They both may be called deep and profound. Deeper and more profound, The door of all subtleties! ======= -- The Way of Lao-Tzu, tr. Wing-tsit Chan >>>>>>> tao @end example The @samp{-E} or @samp{--show-overlap} option outputs less information than the @samp{-A} or @samp{--show-all} option, because it outputs only unmerged changes, and it never outputs the contents of the second file. Thus the @samp{-E} option acts like the @samp{-e} option, except that it brackets the first and third files from three-way overlapping changes. Similarly, @samp{-X} acts like @samp{-x}, except it brackets all its (necessarily overlapping) changes. For example, for the three-way overlapping change above, the @samp{-E} and @samp{-X} options output the following: @example <<<<<<< lao ======= -- The Way of Lao-Tzu, tr. Wing-tsit Chan >>>>>>> tao @end example If you are comparing files that have meaningless or uninformative names, you can use the @samp{-L @var{label}} or @samp{--label=@var{label}} option to show alternate names in the @samp{<<<<<<<}, @samp{|||||||} and @samp{>>>>>>>} brackets. This option can be given up to three times, once for each input file. Thus @samp{diff3 -A -L X -L Y -L Z A B C} acts like @samp{diff3 -A A B C}, except that the output looks like it came from files named @samp{X}, @samp{Y} and @samp{Z} rather than from files named @samp{A}, @samp{B} and @samp{C}. @node Bypassing ed, Merging Incomplete Lines, Marking Conflicts, diff3 Merging @section Generating the Merged Output Directly @cindex merged @code{diff3} format With the @samp{-m} or @samp{--merge} option, @code{diff3} outputs the merged file directly. This is more efficient than using @code{ed} to generate it, and works even with non-text files that @code{ed} would reject. If you specify @samp{-m} without an @code{ed} script option, @samp{-A} (@samp{--show-all}) is assumed. For example, the command @samp{diff3 -m lao tzu tao} (@pxref{Sample diff3 Input} for a copy of the input files) would output the following: @example <<<<<<< tzu ======= The Way that can be told of is not the eternal Way; The name that can be named is not the eternal name. >>>>>>> tao The Nameless is the origin of Heaven and Earth; The Named is the mother of all things. Therefore let there always be non-being, so we may see their subtlety, And let there always be being, so we may see their result. The two are the same, But after they are produced, they have different names. <<<<<<< lao ||||||| tzu They both may be called deep and profound. Deeper and more profound, The door of all subtleties! ======= -- The Way of Lao-Tzu, tr. Wing-tsit Chan >>>>>>> tao @end example @node Merging Incomplete Lines, Saving the Changed File, Bypassing ed, diff3 Merging @section How @code{diff3} Merges Incomplete Lines @cindex incomplete line merging With @samp{-m}, incomplete lines (@pxref{Incomplete Lines}) are simply copied to the output as they are found; if the merged output ends in an conflict and one of the input files ends in an incomplete line, succeeding @samp{|||||||}, @samp{=======} or @samp{>>>>>>>} brackets appear somewhere other than the start of a line because they are appended to the incomplete line. Without @samp{-m}, if an @code{ed} script option is specified and an incomplete line is found, @code{diff3} generates a warning and acts as if a newline had been present. @node Saving the Changed File, , Merging Incomplete Lines, diff3 Merging @section Saving the Changed File @cindex System V @code{diff3} compatibility Traditional Unix @code{diff3} generates an @code{ed} script without the trailing @samp{w} and and @samp{q} commands that save the changes. System V @code{diff3} generates these extra commands. GNU @code{diff3} normally behaves like traditional Unix @code{diff3}, but with the @samp{-i} option it behaves like System V @code{diff3} and appends the @samp{w} and @samp{q} commands. The @samp{-i} option requires one of the @code{ed} script options @samp{-AeExX3}, and is incompatible with the merged output option @samp{-m}. @node Interactive Merging, Merging with patch, diff3 Merging, Top @chapter Interactive Merging with @code{sdiff} @cindex diff merging @cindex interactive merging With @code{sdiff}, you can merge two files interactively based on a side-by-side @samp{-y} format comparison (@pxref{Side by Side}). Use @samp{-o @var{file}} or @samp{--output=@var{file}} to specify where to put the merged text. @xref{Invoking sdiff}, for more details on the options to @code{sdiff}. Another way to merge files interactively is to use the Emacs Lisp package @code{emerge}. @xref{emerge, , emerge, emacs, The GNU Emacs Manual}, for more information. @menu * sdiff Option Summary::Summary of @code{sdiff} options. * Merge Commands:: Merging two files interactively. @end menu @node sdiff Option Summary, Merge Commands, , Interactive Merging @section Specifying @code{diff} Options to @code{sdiff} @cindex @code{sdiff} output format The following @code{sdiff} options have the same meaning as for @code{diff}. @xref{diff Options}, for the use of these options. @example -a -b -d -i -t -v -B -H -I @var{regexp} --ignore-blank-lines --ignore-case --ignore-matching-lines=@var{regexp} --ignore-space-change --left-column --minimal --speed-large-files --suppress-common-lines --expand-tabs --text --version --width=@var{columns} @end example For historical reasons, @code{sdiff} has alternate names for some options. The @samp{-l} option is equivalent to the @samp{--left-column} option, and similarly @samp{-s} is equivalent to @samp{--suppress-common-lines}. The meaning of the @code{sdiff} @samp{-w} and @samp{-W} options is interchanged from that of @code{diff}: with @code{sdiff}, @samp{-w @var{columns}} is equivalent to @samp{--width=@var{columns}}, and @samp{-W} is equivalent to @samp{--ignore-all-space}. @code{sdiff} without the @samp{-o} option is equivalent to @code{diff} with the @samp{-y} or @samp{--side-by-side} option (@pxref{Side by Side}). @node Merge Commands, , sdiff Option Summary, Interactive Merging @section Merge Commands @cindex merge commands @cindex merging interactively Groups of common lines, with a blank gutter, are copied from the first file to the output. After each group of differing lines, @code{sdiff} prompts with @samp{%} and pauses, waiting for one of the following commands. Follow each command with @key{RET}. @table @samp @item e Discard both versions. Invoke a text editor on an empty temporary file, then copy the resulting file to the output. @item eb Concatenate the two versions, edit the result in a temporary file, then copy the edited result to the output. @item el Edit a copy of the left version, then copy the result to the output. @item er Edit a copy of the right version, then copy the result to the output. @item l Copy the left version to the output. @item q Quit. @item r Copy the right version to the output. @item s Silently copy common lines. @item v Verbosely copy common lines. This is the default. @end table The text editor invoked is specified by the @code{EDITOR} environment variable if it is set. The default is system-dependent. @node Merging with patch, Making Patches, Interactive Merging, Top @chapter Merging with @code{patch} @code{patch} takes comparison output produced by @code{diff} and applies the differences to a copy of the original file, producing a patched version. With @code{patch}, you can distribute just the changes to a set of files instead of distributing the entire file set; your correspondents can apply @code{patch} to update their copy of the files with your changes. @code{patch} automatically determines the diff format, skips any leading or trailing headers, and uses the headers to determine which file to patch. This lets your correspondents feed an article or message containing a difference listing directly to @code{patch}. @code{patch} detects and warns about common problems like forward patches. It saves the original version of the files it patches, and saves any patches that it could not apply. It can also maintain a @code{patchlevel.h} file to ensures that your correspondents apply diffs in the proper order. @code{patch} accepts a series of diffs in its standard input, usually separated by headers that specify which file to patch. It applies @code{diff} hunks (@pxref{Hunks}) one by one. If a hunk does not exactly match the original file, @code{patch} uses heuristics to try to patch the file as well as it can. If no approximate match can be found, @code{patch} rejects the hunk and skips to the next hunk. @code{patch} normally replaces each file @var{f} with its new version, saving the original file in @samp{@var{f}.orig}, and putting reject hunks (if any) into @samp{@var{f}.rej}. @xref{Invoking patch}, for detailed information on the options to @code{patch}. @xref{Backups}, for more information on how @code{patch} names backup files. @xref{Rejects}, for more information on where @code{patch} puts reject hunks. @menu * patch Input:: Selecting the type of @code{patch} input. * Imperfect:: Dealing with imperfect patches. * Empty Files:: Removing empty files after patching. * Multiple Patches:: Handling multiple patches in a file specially. * patch Messages:: Messages and questions @code{patch} can produce. @end menu @node patch Input, Imperfect, , Merging with patch @section Selecting the @code{patch} Input Format @cindex @code{patch} input format @code{patch} normally determines which @code{diff} format the patch file uses by examining its contents. For patch files that contain particularly confusing leading text, you might need to use one of the following options to force @code{patch} to interpret the patch file as a certain format of diff. The output formats listed here are the only ones that @code{patch} can understand. @table @samp @item -c @itemx --context context diff. @item -e @itemx --ed @code{ed} script. @item -n @itemx --normal normal diff. @item -u @itemx --unified unified diff. @end table @node Imperfect, Empty Files, patch Input, Merging with patch @section Applying Imperfect Patches @cindex imperfect patch application @code{patch} tries to skip any leading text in the patch file, apply the diff, and then skip any trailing text. Thus you can feed a news article or mail message directly to @code{patch}, and it should work. If the entire diff is indented by a constant amount of white space, @code{patch} automatically ignores the indentation. However, certain other types of imperfect input require user intervention. @menu * Changed White Space:: When tabs and spaces don't match exactly. * Reversed Patches:: Applying reversed patches correctly. * Inexact:: Helping @code{patch} find close matches. @end menu @node Changed White Space, Reversed Patches, , Imperfect @subsection Applying Patches with Changed White Space @cindex white space in patches Sometimes mailers, editors, or other programs change spaces into tabs, or vice versa. If this happens to a patch file or an input file, the files might look the same, but @code{patch} will not be able to match them properly. If this problem occurs, use the @samp{-l} or @samp{--ignore-white-space} option, which makes @code{patch} compare white space loosely so that any sequence of white space in the patch file matches any sequence of white space in the input files. Non-white-space characters must still match exactly. Each line of the context must still match a line in the input file. @node Reversed Patches, Inexact, Changed White Space, Imperfect @subsection Applying Reversed Patches @cindex reversed patches Sometimes people run @code{diff} with the new file first instead of second. This creates a diff that is ``reversed''. To apply such patches, give @code{patch} the @samp{-R} or @samp{--reverse} option. @code{patch} then attempts to swap each hunk around before applying it. Rejects come out in the swapped format. The @samp{-R} option does not work with @code{ed} scripts because there is too little information in them to reconstruct the reverse operation. Often @code{patch} can guess that the patch is reversed. If the first hunk of a patch fails, @code{patch} reverses the hunk to see if it can apply it that way. If it can, @code{patch} asks you if you want to have the @samp{-R} option set; if it can't, @code{patch} continues to apply the patch normally. This method cannot detect a reversed patch if it is a normal diff and the first command is an append (which should have been a delete) since appends always succeed, because a null context matches anywhere. But most patches add or change lines rather than delete them, so most reversed normal diffs begin with a delete, which fails, and @code{patch} notices. If you apply a patch that you have already applied, @code{patch} thinks it is a reversed patch and offers to un-apply the patch. This could be construed as a feature. If you did this inadvertently and you don't want to un-apply the patch, just answer @samp{n} to this offer and to the subsequent ``apply anyway'' question---or type @kbd{C-c} to kill the @code{patch} process. @node Inexact, , Reversed Patches, Imperfect @subsection Helping @code{patch} Find Inexact Matches @cindex inexact patches @cindex fuzz factor when patching For context diffs, and to a lesser extent normal diffs, @code{patch} can detect when the line numbers mentioned in the patch are incorrect, and it attempts to find the correct place to apply each hunk of the patch. As a first guess, it takes the line number mentioned in the hunk, plus or minus any offset used in applying the previous hunk. If that is not the correct place, @code{patch} scans both forward and backward for a set of lines matching the context given in the hunk. First @code{patch} looks for a place where all lines of the context match. If it cannot find such a place, and it is reading a context or unified diff, and the maximum fuzz factor is set to 1 or more, then @code{patch} makes another scan, ignoring the first and last line of context. If that fails, and the maximum fuzz factor is set to 2 or more, it makes another scan, ignoring the first two and last two lines of context are ignored. It continues similarly if the maximum fuzz factor is larger. The @samp{-F @var{lines}} or @samp{--fuzz=@var{lines}} option sets the maximum fuzz factor to @var{lines}. This option only applies to context and unified diffs; it ignores up to @var{lines} lines while looking for the place to install a hunk. Note that a larger fuzz factor increases the odds of making a faulty patch. The default fuzz factor is 2; it may not be set to more than the number of lines of context in the diff, ordinarily 3. If @code{patch} cannot find a place to install a hunk of the patch, it writes the hunk out to a reject file (@pxref{Rejects}, for information on how reject files are named). It writes out rejected hunks in context format no matter what form the input patch is in. If the input is a normal or @code{ed} diff, many of the contexts are simply null. The line numbers on the hunks in the reject file may be different from those in the patch file: they show the approximate location where @code{patch} thinks the failed hunks belong in the new file rather than in the old one. As it completes each hunk, @code{patch} tells you whether the hunk succeeded or failed, and if it failed, on which line (in the new file) @code{patch} thinks the hunk should go. If this is different from the line number specified in the diff, it tells you the offset. A single large offset @emph{may} indicate that @code{patch} installed a hunk in the wrong place. @code{patch} also tells you if it used a fuzz factor to make the match, in which case you should also be slightly suspicious. @code{patch} cannot tell if the line numbers are off in an @code{ed} script, and can only detect wrong line numbers in a normal diff when it finds a change or delete command. It may have the same problem with a context diff using a fuzz factor equal to or greater than the number of lines of context shown in the diff (typically 3). In these cases, you should probably look at a context diff between your original and patched input files to see if the changes make sense. Compiling without errors is a pretty good indication that the patch worked, but not a guarantee. @code{patch} usually produces the correct results, even when it must make many guesses. However, the results are guaranteed only when the patch is applied to an exact copy of the file that the patch was generated from. @node Empty Files, Multiple Patches, Imperfect, Merging with patch @section Removing Empty Files @cindex empty files, removing @cindex removing empty files Sometimes when comparing two directories, the first directory contains a file that the second directory does not. If you give @code{diff} the @samp{-N} or @samp{--new-file} option, it outputs a diff that deletes the contents of this file. By default, @code{patch} leaves an empty file after applying such a diff. The @samp{-E} or @samp{--remove-empty-files} option to @code{patch} deletes output files that are empty after applying the diff. @node Multiple Patches, patch Messages, Empty Files, Merging with patch @section Multiple Patches in a File @cindex multiple patches If the patch file contains more than one patch, @code{patch} tries to apply each of them as if they came from separate patch files. This means that it determines the name of the file to patch for each patch, and that it examines the leading text before each patch for file names and prerequisite revision level (@pxref{Making Patches}, for more on that topic). For the second and subsequent patches in the patch file, you can give options and another original file name by separating their argument lists with a @samp{+}. However, the argument list for a second or subsequent patch may not specify a new patch file, since that does not make sense. For example, to tell @code{patch} to strip the first three slashes from the name of the first patch in the patch file and none from subsequent patches, and to use @file{code.c} as the first input file, you can use: @example patch -p3 code.c + -p0 < patchfile @end example The @samp{-S} or @samp{--skip} option ignores the current patch from the patch file, but continue looking for the next patch in the file. Thus, to ignore the first and third patches in the patch file, you can use: @example patch -S + + -S + < patch file @end example @node patch Messages, , Multiple Patches, Merging with patch @section Messages and Questions from @code{patch} @cindex @code{patch} messages and questions @cindex diagnostics from @code{patch} @cindex messages from @code{patch} @code{patch} can produce a variety of messages, especially if it has trouble decoding its input. In a few situations where it's not sure how to proceed, @code{patch} normally prompts you for more information from the keyboard. There are options to suppress printing non-fatal messages and stopping for keyboard input. The message @samp{Hmm...} indicates that @code{patch} is reading text in the patch file, attempting to determine whether there is a patch in that text, and if so, what kind of patch it is. You can inhibit all terminal output from @code{patch}, unless an error occurs, by using the @samp{-s}, @samp{--quiet}, or @samp{--silent} option. There are two ways you can prevent @code{patch} from asking you any questions. The @samp{-f} or @samp{--force} option assumes that you know what you are doing. It assumes the following: @itemize @bullet @item skip patches that do not contain file names in their headers; @item patch files even though they have the wrong version for the @samp{Prereq:} line in the patch; @item assume that patches are not reversed even if they look like they are. @end itemize The @samp{-t} or @samp{--batch} option is similar to @samp{-f}, in that it suppresses questions, but it makes somewhat different assumptions: @itemize @bullet @item skip patches that do not contain file names in their headers (the same as @samp{-f}); @item skip patches for which the file has the wrong version for the @samp{Prereq:} line in the patch; @item assume that patches are reversed if they look like they are. @end itemize @code{patch} exits with a non-zero status if it creates any reject files. When applying a set of patches in a loop, you should check the exit status, so you don't apply a later patch to a partially patched file. @node Making Patches, Invoking cmp, Merging with patch, Top @chapter Tips for Making Patch Distributions @cindex patch making tips @cindex tips for patch making Here are some things you should keep in mind if you are going to distribute patches for updating a software package. Make sure you have specified the file names correctly, either in a context diff header or with an @samp{Index:} line. If you are patching files in a subdirectory, be sure to tell the patch user to specify a @samp{-p} or @samp{--strip} option as needed. Take care to not send out reversed patches, since these make people wonder whether they have already applied the patch. To save people from partially applying a patch before other patches that should have gone before it, you can make the first patch in the patch file update a file with a name like @file{patchlevel.h} or @file{version.c}, which contains a patch level or version number. If the input file contains the wrong version number, @code{patch} will complain immediately. An even clearer way to prevent this problem is to put a @samp{Prereq:} line before the patch. If the leading text in the patch file contains a line that starts with @samp{Prereq:}, @code{patch} takes the next word from that line (normally a version number) and checks whether the next input file contains that word, preceded and followed by either white space or a newline. If not, @code{patch} prompts you for confirmation before proceeding. This makes it difficult to accidentally apply patches in the wrong order. Since @code{patch} does not handle incomplete lines properly, make sure that all the source files in your program end with a newline whenever you release a version. To create a patch that changes an older version of a package into a newer version, first make a copy of the older version in a scratch directory. Typically you do that by unpacking a @code{tar} or @code{shar} archive of the older version. You might be able to reduce the size of the patch by renaming or removing some files before making the patch. If the older version of the package contains any files that the newer version does not, or if any files have been renamed between the two versions, make a list of @code{rm} and @code{mv} commands for the user to execute in the old version directory before applying the patch. Then run those commands yourself in the scratch directory. If there are any files that you don't need to include in the patch because they can easily be rebuilt from other files (for example, @file{TAGS} and output from @code{yacc} and @code{makeinfo}), replace the versions in the scratch directory with the newer versions, using @code{rm} and @code{ln} or @code{cp}. Now you can create the patch. The de-facto standard @code{diff} format for patch distributions is context format with two lines of context, produced by giving @code{diff} the @samp{-C 2} option. Do not use less than two lines of context, because @code{patch} typically needs at least two lines for proper operation. Give @code{diff} the @samp{-P} option in case the newer version of the package contains any files that the older one does not. Make sure to specify the scratch directory first and the newer directory second. Add to the top of the patch a note telling the user any @code{rm} and @code{mv} commands to run before applying the patch. Then you can remove the scratch directory. @node Invoking cmp, Invoking diff, Making Patches, Top @chapter Invoking @code{cmp} @cindex invoking @code{cmp} @cindex @code{cmp} invocation The @code{cmp} command compares two files, and if they differ, tells the first byte and line number where they differ. Its arguments are as follows: @example cmp @var{options}@dots{} @var{from-file} @r{[}@var{to-file}@var{]} @end example The file name @samp{-} is always the standard input. @code{cmp} also uses the standard input if one file name is omitted. An exit status of 0 means no differences were found, 1 means some differences were found, and 2 means trouble. @menu * cmp Options:: Summary of options to @code{cmp}. @end menu @node cmp Options, , , Invoking cmp @section Options to @code{cmp} @cindex @code{cmp} options @cindex options for @code{cmp} Below is a summary of all of the options that GNU @code{cmp} accepts. Most options have two equivalent names, one of which is a single letter preceded by @samp{-}, and the other of which is a long name preceded by @samp{--}. Multiple single letter options (unless they take an argument) can be combined into a single command line word: @samp{-cl} is equivalent to @samp{-c -l}. @table @samp @item -c Print the differing characters. Display control characters as a @samp{^} followed by a letter of the alphabet and precede characters that have the high bit set with @samp{M-} (which stands for ``meta''). @item --ignore-initial=@var{bytes} Ignore any differences in the the first @var{bytes} bytes of the input files. Treat files with fewer than @var{bytes} bytes as if they are empty. @item -l Print the (decimal) offsets and (octal) values of all differing bytes. @item --print-chars Print the differing characters. Display control characters as a @samp{^} followed by a letter of the alphabet and precede characters that have the high bit set with @samp{M-} (which stands for ``meta''). @item --quiet @itemx -s @itemx --silent Do not print anything; only return an exit status indicating whether the files differ. @item --verbose Print the (decimal) offsets and (octal) values of all differing bytes. @item -v @item --version Output the version number of @code{cmp}. @end table @node Invoking diff, Invoking diff3, Invoking cmp, Top @chapter Invoking @code{diff} @cindex invoking @code{diff} @cindex @code{diff} invocation The format for running the @code{diff} command is: @example diff @var{options}@dots{} @var{from-file} @var{to-file} @end example In the simplest case, @code{diff} compares the contents of the two files @var{from-file} and @var{to-file}. A file name of @samp{-} stands for text read from the standard input. As a special case, @samp{diff - -} compares a copy of standard input to itself. If @var{from-file} is a directory and @var{to-file} is not, @code{diff} compares the file in @var{from-file} whose file name is that of @var{to-file}, and vice versa. The non-directory file must not be @samp{-}. If both @var{from-file} and @var{to-file} are directories, @code{diff} compares corresponding files in both directories, in alphabetical order; this comparison is not recursive unless the @samp{-r} or @samp{--recursive} option is given. @code{diff} never compares the actual contents of a directory as if it were a file. The file that is fully specified may not be standard input, because standard input is nameless and the notion of ``file with the same name'' does not apply. @code{diff} options begin with @samp{-}, so normally @var{from-file} and @var{to-file} may not begin with @samp{-}. However, @samp{--} as an argument by itself treats the remaining arguments as file names even if they begin with @samp{-}. An exit status of 0 means no differences were found, 1 means some differences were found, and 2 means trouble. @menu * diff Options:: Summary of options to @code{diff}. @end menu @node diff Options, , , Invoking diff @section Options to @code{diff} @cindex @code{diff} options @cindex options for @code{diff} Below is a summary of all of the options that GNU @code{diff} accepts. Most options have two equivalent names, one of which is a single letter preceded by @samp{-}, and the other of which is a long name preceded by @samp{--}. Multiple single letter options (unless they take an argument) can be combined into a single command line word: @samp{-ac} is equivalent to @samp{-a -c}. Long named options can be abbreviated to any unique prefix of their name. Brackets ([ and ]) indicate that an option takes an optional argument. @table @samp @item -@var{lines} Show @var{lines} (an integer) lines of context. This option does not specify an output format by itself; it has no effect unless it is combined with @samp{-c} (@pxref{Context Format}) or @samp{-u} (@pxref{Unified Format}). This option is obsolete. For proper operation, @code{patch} typically needs at least two lines of context. @item -a Treat all files as text and compare them line-by-line, even if they do not seem to be text. @xref{Binary}. @item -b Ignore changes in amount of white space. @xref{White Space}. @item -B Ignore changes that just insert or delete blank lines. @xref{Blank Lines}. @item --binary Read and write data in binary mode. @xref{Binary}. @item --brief Report only whether the files differ, not the details of the differences. @xref{Brief}. @item -c Use the context output format. @xref{Context Format}. @item -C @var{lines} @itemx --context@r{[}=@var{lines}@r{]} Use the context output format, showing @var{lines} (an integer) lines of context, or three if @var{lines} is not given. @xref{Context Format}. For proper operation, @code{patch} typically needs at least two lines of context. @item --changed-group-format=@var{format} Use @var{format} to output a line group containing differing lines from both files in if-then-else format. @xref{Line Group Formats}. @item -d Change the algorithm perhaps find a smaller set of changes. This makes @code{diff} slower (sometimes much slower). @xref{diff Performance}. @item -D @var{name} Make merged @samp{#ifdef} format output, conditional on the preprocessor macro @var{name}. @xref{If-then-else}. @item -e @itemx --ed Make output that is a valid @code{ed} script. @xref{ed Scripts}. @item --exclude=@var{pattern} When comparing directories, ignore files and subdirectories whose basenames match @var{pattern}. @xref{Comparing Directories}. @item --exclude-from=@var{file} When comparing directories, ignore files and subdirectories whose basenames match any pattern contained in @var{file}. @xref{Comparing Directories}. @item --expand-tabs Expand tabs to spaces in the output, to preserve the alignment of tabs in the input files. @xref{Tabs}. @item -f Make output that looks vaguely like an @code{ed} script but has changes in the order they appear in the file. @xref{Forward ed}. @item -F @var{regexp} In context and unified format, for each hunk of differences, show some of the last preceding line that matches @var{regexp}. @xref{Specified Headings}. @item --forward-ed Make output that looks vaguely like an @code{ed} script but has changes in the order they appear in the file. @xref{Forward ed}. @item -h This option currently has no effect; it is present for Unix compatibility. @item -H Use heuristics to speed handling of large files that have numerous scattered small changes. @xref{diff Performance}. @item --horizon-lines=@var{lines} Do not discard the last @var{lines} lines of the common prefix and the first @var{lines} lines of the common suffix. @xref{diff Performance}. @item -i Ignore changes in case; consider upper- and lower-case letters equivalent. @xref{Case Folding}. @item -I @var{regexp} Ignore changes that just insert or delete lines that match @var{regexp}. @xref{Specified Folding}. @item --ifdef=@var{name} Make merged if-then-else output using @var{name}. @xref{If-then-else}. @item --ignore-all-space Ignore white space when comparing lines. @xref{White Space}. @item --ignore-blank-lines Ignore changes that just insert or delete blank lines. @xref{Blank Lines}. @item --ignore-case Ignore changes in case; consider upper- and lower-case to be the same. @xref{Case Folding}. @item --ignore-matching-lines=@var{regexp} Ignore changes that just insert or delete lines that match @var{regexp}. @xref{Specified Folding}. @item --ignore-space-change Ignore changes in amount of white space. @xref{White Space}. @item --initial-tab Output a tab rather than a space before the text of a line in normal or context format. This causes the alignment of tabs in the line to look normal. @xref{Tabs}. @item -l Pass the output through @code{pr} to paginate it. @xref{Pagination}. @item -L @var{label} Use @var{label} instead of the file name in the context format (@pxref{Context Format}) and unified format (@pxref{Unified Format}) headers. @xref{RCS}. @item --label=@var{label} Use @var{label} instead of the file name in the context format (@pxref{Context Format}) and unified format (@pxref{Unified Format}) headers. @item --left-column Print only the left column of two common lines in side by side format. @xref{Side by Side Format}. @item --line-format=@var{format} Use @var{format} to output all input lines in if-then-else format. @xref{Line Formats}. @item --minimal Change the algorithm to perhaps find a smaller set of changes. This makes @code{diff} slower (sometimes much slower). @xref{diff Performance}. @item -n Output RCS-format diffs; like @samp{-f} except that each command specifies the number of lines affected. @xref{RCS}. @item -N @itemx --new-file In directory comparison, if a file is found in only one directory, treat it as present but empty in the other directory. @xref{Comparing Directories}. @item --new-group-format=@var{format} Use @var{format} to output a group of lines taken from just the second file in if-then-else format. @xref{Line Group Formats}. @item --new-line-format=@var{format} Use @var{format} to output a line taken from just the second file in if-then-else format. @xref{Line Formats}. @item --old-group-format=@var{format} Use @var{format} to output a group of lines taken from just the first file in if-then-else format. @xref{Line Group Formats}. @item --old-line-format=@var{format} Use @var{format} to output a line taken from just the first file in if-then-else format. @xref{Line Formats}. @item -p Show which C function each change is in. @xref{C Function Headings}. @item -P When comparing directories, if a file appears only in the second directory of the two, treat it as present but empty in the other. @xref{Comparing Directories}. @item --paginate Pass the output through @code{pr} to paginate it. @xref{Pagination}. @item -q Report only whether the files differ, not the details of the differences. @xref{Brief}. @item -r When comparing directories, recursively compare any subdirectories found. @xref{Comparing Directories}. @item --rcs Output RCS-format diffs; like @samp{-f} except that each command specifies the number of lines affected. @xref{RCS}. @item --recursive When comparing directories, recursively compare any subdirectories found. @xref{Comparing Directories}. @item --report-identical-files Report when two files are the same. @xref{Comparing Directories}. @item -s Report when two files are the same. @xref{Comparing Directories}. @item -S @var{file} When comparing directories, start with the file @var{file}. This is used for resuming an aborted comparison. @xref{Comparing Directories}. @item --sdiff-merge-assist Print extra information to help @code{sdiff}. @code{sdiff} uses this option when it runs @code{diff}. This option is not intended for users to use directly. @item --show-c-function Show which C function each change is in. @xref{C Function Headings}. @item --show-function-line=@var{regexp} In context and unified format, for each hunk of differences, show some of the last preceding line that matches @var{regexp}. @xref{Specified Headings}. @item --side-by-side Use the side by side output format. @xref{Side by Side Format}. @item --speed-large-files Use heuristics to speed handling of large files that have numerous scattered small changes. @xref{diff Performance}. @item --starting-file=@var{file} When comparing directories, start with the file @var{file}. This is used for resuming an aborted comparison. @xref{Comparing Directories}. @item --suppress-common-lines Do not print common lines in side by side format. @xref{Side by Side Format}. @item -t Expand tabs to spaces in the output, to preserve the alignment of tabs in the input files. @xref{Tabs}. @item -T Output a tab rather than a space before the text of a line in normal or context format. This causes the alignment of tabs in the line to look normal. @xref{Tabs}. @item --text Treat all files as text and compare them line-by-line, even if they do not appear to be text. @xref{Binary}. @item -u Use the unified output format. @xref{Unified Format}. @item --unchanged-group-format=@var{format} Use @var{format} to output a group of common lines taken from both files in if-then-else format. @xref{Line Group Formats}. @item --unchanged-line-format=@var{format} Use @var{format} to output a line common to both files in if-then-else format. @xref{Line Formats}. @item --unidirectional-new-file When comparing directories, if a file appears only in the second directory of the two, treat it as present but empty in the other. @xref{Comparing Directories}. @item -U @var{lines} @itemx --unified@r{[}=@var{lines}@r{]} Use the unified output format, showing @var{lines} (an integer) lines of context, or three if @var{lines} is not given. @xref{Unified Format}. For proper operation, @code{patch} typically needs at least two lines of context. @item -v @itemx --version Output the version number of @code{diff}. @item -w Ignore white space when comparing lines. @xref{White Space}. @item -W @var{columns} @itemx --width=@var{columns} Use an output width of @var{columns} in side by side format. @xref{Side by Side Format}. @item -x @var{pattern} When comparing directories, ignore files and subdirectories whose basenames match @var{pattern}. @xref{Comparing Directories}. @item -X @var{file} When comparing directories, ignore files and subdirectories whose basenames match any pattern contained in @var{file}. @xref{Comparing Directories}. @item -y Use the side by side output format. @xref{Side by Side Format}. @end table @node Invoking diff3, Invoking patch, Invoking diff, Top @chapter Invoking @code{diff3} @cindex invoking @code{diff3} @cindex @code{diff3} invocation The @code{diff3} command compares three files and outputs descriptions of their differences. Its arguments are as follows: @example diff3 @var{options}@dots{} @var{mine} @var{older} @var{yours} @end example The files to compare are @var{mine}, @var{older}, and @var{yours}. At most one of these three file names may be @samp{-}, which tells @code{diff3} to read the standard input for that file. An exit status of 0 means @code{diff3} was successful, 1 means some conflicts were found, and 2 means trouble. @menu * diff3 Options:: Summary of options to @code{diff3}. @end menu @node diff3 Options, , , Invoking diff3 @section Options to @code{diff3} @cindex @code{diff3} options @cindex options for @code{diff3} Below is a summary of all of the options that GNU @code{diff3} accepts. Multiple single letter options (unless they take an argument) can be combined into a single command line argument. @table @samp @item -a Treat all files as text and compare them line-by-line, even if they do not appear to be text. @xref{Binary}. @item -A Incorporate all changes from @var{older} to @var{yours} into @var{mine}, surrounding all conflicts with bracket lines. @xref{Marking Conflicts}. @item -e Generate an @code{ed} script that incorporates all the changes from @var{older} to @var{yours} into @var{mine}. @xref{Which Changes}. @item -E Like @samp{-e}, except bracket lines from overlapping changes' first and third files. @xref{Marking Conflicts}. With @samp{-e}, an overlapping change looks like this: @example <<<<<<< @var{mine} @r{lines from @var{mine}} ======= @r{lines from @var{yours}} >>>>>>> @var{yours} @end example @item --ed Generate an @code{ed} script that incorporates all the changes from @var{older} to @var{yours} into @var{mine}. @xref{Which Changes}. @item --easy-only Like @samp{-e}, except output only the nonoverlapping changes. @xref{Which Changes}. @item -i Generate @samp{w} and @samp{q} commands at the end of the @code{ed} script for System V compatibility. This option must be combined with one of the @samp{-AeExX3} options, and may not be combined with @samp{-m}. @xref{Saving the Changed File}. @item --initial-tab Output a tab rather than two spaces before the text of a line in normal format. This causes the alignment of tabs in the line to look normal. @xref{Tabs}. @item -L @var{label} @itemx --label=@var{label} Use the label @var{label} for the brackets output by the @samp{-A}, @samp{-E} and @samp{-X} options. This option may be given up to three times, one for each input file. The default labels are the names of the input files. Thus @samp{diff3 -L X -L Y -L Z -m A B C} acts like @samp{diff3 -m A B C}, except that the output looks like it came from files named @samp{X}, @samp{Y} and @samp{Z} rather than from files named @samp{A}, @samp{B} and @samp{C}. @xref{Marking Conflicts}. @item -m @itemx --merge Apply the edit script to the first file and send the result to standard output. Unlike piping the output from @code{diff3} to @code{ed}, this works even for binary files and incomplete lines. @samp{-A} is assumed if no edit script option is specified. @xref{Bypassing ed}. @item --overlap-only Like @samp{-e}, except output only the overlapping changes. @xref{Which Changes}. @item --show-all Incorporate all unmerged changes from @var{older} to @var{yours} into @var{mine}, surrounding all overlapping changes with bracket lines. @xref{Marking Conflicts}. @item --show-overlap Like @samp{-e}, except bracket lines from overlapping changes' first and third files. @xref{Marking Conflicts}. @item -T Output a tab rather than two spaces before the text of a line in normal format. This causes the alignment of tabs in the line to look normal. @xref{Tabs}. @item --text Treat all files as text and compare them line-by-line, even if they do not appear to be text. @xref{Binary}. @item -v @itemx --version Output the version number of @code{diff3}. @item -x Like @samp{-e}, except output only the overlapping changes. @xref{Which Changes}. @item -X Like @samp{-E}, except output only the overlapping changes. In other words, like @samp{-x}, except bracket changes as in @samp{-E}. @xref{Marking Conflicts}. @item -3 Like @samp{-e}, except output only the nonoverlapping changes. @xref{Which Changes}. @end table @node Invoking patch, Invoking sdiff, Invoking diff3, Top @chapter Invoking @code{patch} @cindex invoking @code{patch} @cindex @code{patch} invocation Normally @code{patch} is invoked like this: @example patch <@var{patchfile} @end example The full format for invoking @code{patch} is: @example patch @var{options}@dots{} @r{[}@var{origfile} @r{[}@var{patchfile}@r{]}@r{]} @r{[}+ @var{options}@dots{} @r{[}@var{origfile}@r{]}@r{]}@dots{} @end example If you do not specify @var{patchfile}, or if @var{patchfile} is @samp{-}, @code{patch} reads the patch (that is, the @code{diff} output) from the standard input. You can specify one or more of the original files as @var{orig} arguments; each one and options for interpreting it is separated from the others with a @samp{+}. @xref{Multiple Patches}, for more information. If you do not specify an input file on the command line, @code{patch} tries to figure out from the @dfn{leading text} (any text in the patch that comes before the @code{diff} output) which file to edit. In the header of a context or unified diff, @code{patch} looks in lines beginning with @samp{***}, @samp{---}, or @samp{+++}; among those, it chooses the shortest name of an existing file. Otherwise, if there is an @samp{Index:} line in the leading text, @code{patch} tries to use the file name from that line. If @code{patch} cannot figure out the name of an existing file from the leading text, it prompts you for the name of the file to patch. If the input file does not exist or is read-only, and a suitable RCS or SCCS file exists, @code{patch} attempts to check out or get the file before proceeding. By default, @code{patch} replaces the original input file with the patched version, after renaming the original file into a backup file (@pxref{Backups}, for a description of how @code{patch} names backup files). You can also specify where to put the output with the @samp{-o @var{output-file}} or @samp{--output=@var{output-file}} option. @menu * patch Directories:: Changing directory and stripping directories. * Backups:: Backup file names. * Rejects:: Reject file names. * patch Options:: Summary table of options to @code{patch}. @end menu @node patch Directories, Backups, , Invoking patch @section Applying Patches in Other Directories @cindex directories and patch @cindex patching directories The @samp{-d @var{directory}} or @samp{--directory=@var{directory}} option to @code{patch} makes directory @var{directory} the current directory for interpreting both file names in the patch file, and file names given as arguments to other options (such as @samp{-B} and @samp{-o}). For example, while in a news reading program, you can patch a file in the @file{/usr/src/emacs} directory directly from the article containing the patch like this: @example | patch -d /usr/src/emacs @end example Sometimes the file names given in a patch contain leading directories, but you keep your files in a directory different from the one given in the patch. In those cases, you can use the @samp{-p@r{[}@var{number}@r{]}} or @samp{--strip@r{[}=@var{number}@r{]}} option to set the file name strip count to @var{number}. The strip count tells @code{patch} how many slashes, along with the directory names between them, to strip from the front of file names. @samp{-p} with no @var{number} given is equivalent to @samp{-p0}. By default, @code{patch} strips off all leading directories, leaving just the base file names, except that when a file name given in the patch is a relative file name and all of its leading directories already exist, @code{patch} does not strip off the leading directory. (A @dfn{relative} file name is one that does not start with a slash.) @code{patch} looks for each file (after any slashes have been stripped) in the current directory, or if you used the @samp{-d @var{directory}} option, in that directory. For example, suppose the file name in the patch file is @file{/gnu/src/emacs/etc/NEWS}. Using @samp{-p} or @samp{-p0} gives the entire file name unmodified, @samp{-p1} gives @file{gnu/src/emacs/etc/NEWS} (no leading slash), @samp{-p4} gives @file{etc/NEWS}, and not specifying @samp{-p} at all gives @file{NEWS}. @node Backups, Rejects, patch Directories, Invoking patch @section Backup File Names @cindex backup file names Normally, @code{patch} renames an original input file into a backup file by appending to its name the extension @samp{.orig}, or @samp{~} on systems that do not support long file names. The @samp{-b @var{backup-suffix}} or @samp{--suffix=@var{backup-suffix}} option uses @var{backup-suffix} as the backup extension instead. Alternately, you can specify the extension for backup files with the @code{SIMPLE_BACKUP_SUFFIX} environment variable, which the options override. @code{patch} can also create numbered backup files the way GNU Emacs does. With this method, instead of having a single backup of each file, @code{patch} makes a new backup file name each time it patches a file. For example, the backups of a file named @file{sink} would be called, successively, @file{sink.~1~}, @file{sink.~2~}, @file{sink.~3~}, etc. The @samp{-V @var{backup-style}} or @samp{--version-control=@var{backup-style}} option takes as an argument a method for creating backup file names. You can alternately control the type of backups that @code{patch} makes with the @code{VERSION_CONTROL} environment variable, which the @samp{-V} option overrides. The value of the @code{VERSION_CONTROL} environment variable and the argument to the @samp{-V} option are like the GNU Emacs @code{version-control} variable (@pxref{Backups, emacs, The GNU Emacs Manual}, for more information on backup versions in Emacs). They also recognize synonyms that are more descriptive. The valid values are listed below; unique abbreviations are acceptable. @table @samp @item t @itemx numbered Always make numbered backups. @item nil @itemx existing Make numbered backups of files that already have them, simple backups of the others. This is the default. @item never @itemx simple Always make simple backups. @end table Alternately, you can tell @code{patch} to prepend a prefix, such as a directory name, to produce backup file names. The @samp{-B @var{backup-prefix}} or @samp{--prefix=@var{backup-prefix}} option makes backup files by prepending @var{backup-prefix} to them. If you use this option, @code{patch} ignores any @samp{-b} option that you give. If the backup file already exists, @code{patch} creates a new backup file name by changing the first lowercase letter in the last component of the file name into uppercase. If there are no more lowercase letters in the name, it removes the first character from the name. It repeats this process until it comes up with a backup file name that does not already exist. If you specify the output file with the @samp{-o} option, that file is the one that is backed up, not the input file. @node Rejects, patch Options, Backups, Invoking patch @section Reject File Names @cindex reject file names The names for reject files (files containing patches that @code{patch} could not find a place to apply) are normally the name of the output file with @samp{.rej} appended (or @samp{#} on systems that do not support long file names). Alternatively, you can tell @code{patch} to place all of the rejected patches in a single file. The @samp{-r @var{reject-file}} or @samp{--reject-file=@var{reject-file}} option uses @var{reject-file} as the reject file name. @node patch Options, , Rejects, Invoking patch @section Options to @code{patch} @cindex @code{patch} options @cindex options for @code{patch} Here is a summary of all of the options that @code{patch} accepts. Older versions of @code{patch} do not accept long-named options or the @samp{-t}, @samp{-E}, or @samp{-V} options. Multiple single-letter options that do not take an argument can be combined into a single command line argument (with only one dash). Brackets ([ and ]) indicate that an option takes an optional argument. @table @samp @item -b @var{backup-suffix} Use @var{backup-suffix} as the backup extension instead of @samp{.orig} or @samp{~}. @xref{Backups}. @item -B @var{backup-prefix} Use @var{backup-prefix} as a prefix to the backup file name. If this option is specified, any @samp{-b} option is ignored. @xref{Backups}. @item --batch Do not ask any questions. @xref{patch Messages}. @item -c @itemx --context Interpret the patch file as a context diff. @xref{patch Input}. @item -d @var{directory} @itemx --directory=@var{directory} Makes directory @var{directory} the current directory for interpreting both file names in the patch file, and file names given as arguments to other options. @xref{patch Directories}. @item -D @var{name} Make merged if-then-else output using @var{format}. @xref{If-then-else}. @item --debug=@var{number} Set internal debugging flags. Of interest only to @code{patch} patchers. @item -e @itemx --ed Interpret the patch file as an @code{ed} script. @xref{patch Input}. @item -E Remove output files that are empty after the patches have been applied. @xref{Empty Files}. @item -f Assume that the user knows exactly what he or she is doing, and do not ask any questions. @xref{patch Messages}. @item -F @var{lines} Set the maximum fuzz factor to @var{lines}. @xref{Inexact}. @item --force Assume that the user knows exactly what he or she is doing, and do not ask any questions. @xref{patch Messages}. @item --forward Ignore patches that @code{patch} thinks are reversed or already applied. See also @samp{-R}. @xref{Reversed Patches}. @item --fuzz=@var{lines} Set the maximum fuzz factor to @var{lines}. @xref{Inexact}. @item --help Print a summary of the options that @code{patch} recognizes, then exit. @item --ifdef=@var{name} Make merged if-then-else output using @var{format}. @xref{If-then-else}. @item --ignore-white-space @itemx -l Let any sequence of white space in the patch file match any sequence of white space in the input file. @xref{Changed White Space}. @item -n @itemx --normal Interpret the patch file as a normal diff. @xref{patch Input}. @item -N Ignore patches that @code{patch} thinks are reversed or already applied. See also @samp{-R}. @xref{Reversed Patches}. @item -o @var{output-file} @itemx --output=@var{output-file} Use @var{output-file} as the output file name. @xref{patch Options}. @item -p@r{[}@var{number}@r{]} Set the file name strip count to @var{number}. @xref{patch Directories}. @item --prefix=@var{backup-prefix} Use @var{backup-prefix} as a prefix to the backup file name. If this option is specified, any @samp{-b} option is ignored. @xref{Backups}. @item --quiet Work silently unless an error occurs. @xref{patch Messages}. @item -r @var{reject-file} Use @var{reject-file} as the reject file name. @xref{Rejects}. @item -R Assume that this patch was created with the old and new files swapped. @xref{Reversed Patches}. @item --reject-file=@var{reject-file} Use @var{reject-file} as the reject file name. @xref{Rejects}. @item --remove-empty-files Remove output files that are empty after the patches have been applied. @xref{Empty Files}. @item --reverse Assume that this patch was created with the old and new files swapped. @xref{Reversed Patches}. @item -s Work silently unless an error occurs. @xref{patch Messages}. @item -S Ignore this patch from the patch file, but continue looking for the next patch in the file. @xref{Multiple Patches}. @item --silent Work silently unless an error occurs. @xref{patch Messages}. @item --skip Ignore this patch from the patch file, but continue looking for the next patch in the file. @xref{Multiple Patches}. @item --strip@r{[}=@var{number}@r{]} Set the file name strip count to @var{number}. @xref{patch Directories}. @item --suffix=@var{backup-suffix} Use @var{backup-suffix} as the backup extension instead of @samp{.orig} or @samp{~}. @xref{Backups}. @item -t Do not ask any questions. @xref{patch Messages}. @item -u @itemx --unified Interpret the patch file as a unified diff. @xref{patch Input}. @item -v Output the revision header and patch level of @code{patch}. @item -V @var{backup-style} Select the kind of backups to make. @xref{Backups}. @item --version Output the revision header and patch level of @code{patch}, then exit. @item --version=control=@var{backup-style} Select the kind of backups to make. @xref{Backups}. @item -x @var{number} Set internal debugging flags. Of interest only to @code{patch} patchers. @end table @node Invoking sdiff, Incomplete Lines, Invoking patch, Top @chapter Invoking @code{sdiff} @cindex invoking @code{sdiff} @cindex @code{sdiff} invocation The @code{sdiff} command merges two files and interactively outputs the results. Its arguments are as follows: @example sdiff -o @var{outfile} @var{options}@dots{} @var{from-file} @var{to-file} @end example This merges @var{from-file} with @var{to-file}, with output to @var{outfile}. If @var{from-file} is a directory and @var{to-file} is not, @code{sdiff} compares the file in @var{from-file} whose file name is that of @var{to-file}, and vice versa. @var{from-file} and @var{to-file} may not both be directories. @code{sdiff} options begin with @samp{-}, so normally @var{from-file} and @var{to-file} may not begin with @samp{-}. However, @samp{--} as an argument by itself treats the remaining arguments as file names even if they begin with @samp{-}. You may not use @samp{-} as an input file. An exit status of 0 means no differences were found, 1 means some differences were found, and 2 means trouble. @code{sdiff} without @samp{-o} (or @samp{--output}) produces a side-by-side difference. This usage is obsolete; use @samp{diff --side-by-side} instead. @menu * sdiff Options:: Summary of options to @code{diff}. @end menu @node sdiff Options, , , Invoking sdiff @section Options to @code{sdiff} @cindex @code{sdiff} options @cindex options for @code{sdiff} Below is a summary of all of the options that GNU @code{sdiff} accepts. Each option has two equivalent names, one of which is a single letter preceded by @samp{-}, and the other of which is a long name preceded by @samp{--}. Multiple single letter options (unless they take an argument) can be combined into a single command line argument. Long named options can be abbreviated to any unique prefix of their name. @table @samp @item -a Treat all files as text and compare them line-by-line, even if they do not appear to be text. @xref{Binary}. @item -b Ignore changes in amount of white space. @xref{White Space}. @item -B Ignore changes that just insert or delete blank lines. @xref{Blank Lines}. @item -d Change the algorithm to perhaps find a smaller set of changes. This makes @code{sdiff} slower (sometimes much slower). @xref{diff Performance}. @item -H Use heuristics to speed handling of large files that have numerous scattered small changes. @xref{diff Performance}. @item --expand-tabs Expand tabs to spaces in the output, to preserve the alignment of tabs in the input files. @xref{Tabs}. @item -i Ignore changes in case; consider upper- and lower-case to be the same. @xref{Case Folding}. @item -I @var{regexp} Ignore changes that just insert or delete lines that match @var{regexp}. @xref{Specified Folding}. @item --ignore-all-space Ignore white space when comparing lines. @xref{White Space}. @item --ignore-blank-lines Ignore changes that just insert or delete blank lines. @xref{Blank Lines}. @item --ignore-case Ignore changes in case; consider upper- and lower-case to be the same. @xref{Case Folding}. @item --ignore-matching-lines=@var{regexp} Ignore changes that just insert or delete lines that match @var{regexp}. @xref{Specified Folding}. @item --ignore-space-change Ignore changes in amount of white space. @xref{White Space}. @item -l @itemx --left-column Print only the left column of two common lines. @xref{Side by Side Format}. @item --minimal Change the algorithm to perhaps find a smaller set of changes. This makes @code{sdiff} slower (sometimes much slower). @xref{diff Performance}. @item -o @var{file} @itemx --output=@var{file} Put merged output into @var{file}. This option is required for merging. @item -s @itemx --suppress-common-lines Do not print common lines. @xref{Side by Side Format}. @item --speed-large-files Use heuristics to speed handling of large files that have numerous scattered small changes. @xref{diff Performance}. @item -t Expand tabs to spaces in the output, to preserve the alignment of tabs in the input files. @xref{Tabs}. @item --text Treat all files as text and compare them line-by-line, even if they do not appear to be text. @xref{Binary}. @item -v @itemx --version Output the version number of @code{sdiff}. @item -w @var{columns} @itemx --width=@var{columns} Use an output width of @var{columns}. @xref{Side by Side Format}. Note that for historical reasons, this option is @samp{-W} in @code{diff}, @samp{-w} in @code{sdiff}. @item -W Ignore horizontal white space when comparing lines. @xref{White Space}. Note that for historical reasons, this option is @samp{-w} in @code{diff}, @samp{-W} in @code{sdiff}. @end table @node Incomplete Lines, Projects, Invoking sdiff, Top @chapter Incomplete Lines @cindex incomplete lines @cindex full lines @cindex newline treatment by @code{diff} When an input file ends in a non-newline character, its last line is called an @dfn{incomplete line} because its last character is not a newline. All other lines are called @dfn{full lines} and end in a newline character. Incomplete lines do not match full lines unless differences in white space are ignored (@pxref{White Space}). An incomplete line is normally distinguished on output from a full line by a following line that starts with @samp{\}. However, the RCS format (@pxref{RCS}) outputs the incomplete line as-is, without any trailing newline or following line. The side by side format normally represents incomplete lines as-is, but in some cases uses a @samp{\} or @samp{/} gutter marker; @xref{Side by Side}. The if-then-else line format preserves a line's incompleteness with @samp{%L}, and discards the newline with @samp{%l}; @xref{Line Formats}. Finally, with the @code{ed} and forward @code{ed} output formats (@pxref{Output Formats}) @code{diff} cannot represent an incomplete line, so it pretends there was a newline and reports an error. For example, suppose @file{F} and @file{G} are one-byte files that contain just @samp{f} and @samp{g}, respectively. Then @samp{diff F G} outputs @example 1c1 < f \ No newline at end of file --- > g \ No newline at end of file @end example @noindent (The exact message may differ in non-English locales.) @samp{diff -n F G} outputs the following without a trailing newline: @example d1 1 a1 1 g @end example @samp{diff -e F G} reports two errors and outputs the following: @example 1c g . @end example @node Projects, Concept Index, Incomplete Lines, Top @chapter Future Projects Here are some ideas for improving GNU @code{diff} and @code{patch}. The GNU project has identified some improvements as potential programming projects for volunteers. You can also help by reporting any bugs that you find. If you are a programmer and would like to contribute something to the GNU project, please consider volunteering for one of these projects. If you are seriously contemplating work, please write to @samp{gnu@@prep.ai.mit.edu} to coordinate with other volunteers. @menu * Shortcomings:: Suggested projects for improvements. * Bugs:: Reporting bugs. @end menu @node Shortcomings, Bugs, , Projects @section Suggested Projects for Improving GNU @code{diff} and @code{patch} @cindex projects for directories One should be able to use GNU @code{diff} to generate a patch from any pair of directory trees, and given the patch and a copy of one such tree, use @code{patch} to generate a faithful copy of the other. Unfortunately, some changes to directory trees cannot be expressed using current patch formats; also, @code{patch} does not handle some of the existing formats. These shortcomings motivate the following suggested projects. @menu * Changing Structure:: Handling changes to the directory structure. * Special Files:: Handling symbolic links, device special files, etc. * Unusual File Names:: Handling file names that contain unusual characters. * Arbitrary Limits:: Patching non-text files. * Large Files:: Handling files that do not fit in memory. * Ignoring Changes:: Ignoring certain changes while showing others. @end menu @node Changing Structure, Special Files, , Shortcomings @subsection Handling Changes to the Directory Structure @cindex directory structure changes @code{diff} and @code{patch} do not handle some changes to directory structure. For example, suppose one directory tree contains a directory named @samp{D} with some subsidiary files, and another contains a file with the same name @samp{D}. @samp{diff -r} does not output enough information for @code{patch} to transform the the directory subtree into the file. There should be a way to specify that a file has been deleted without having to include its entire contents in the patch file. There should also be a way to tell @code{patch} that a file was renamed, even if there is no way for @code{diff} to generate such information. These problems can be fixed by extending the @code{diff} output format to represent changes in directory structure, and extending @code{patch} to understand these extensions. @node Special Files, Unusual File Names, Changing Structure, Shortcomings @subsection Files that are Neither Directories Nor Regular Files @cindex special files Some files are neither directories nor regular files: they are unusual files like symbolic links, device special files, named pipes, and sockets. Currently, @code{diff} treats symbolic links like regular files; it treats other special files like regular files if they are specified at the top level, but simply reports their presence when comparing directories. This means that @code{patch} cannot represent changes to such files. For example, if you change which file a symbolic link points to, @code{diff} outputs the difference between the two files, instead of the change to the symbolic link. @c This might not be a good idea; is it wise for root to install devices @c this way? @code{diff} should optionally report changes to special files specially, and @code{patch} should be extended to understand these extensions. @node Unusual File Names, Arbitrary Limits, Special Files, Shortcomings @subsection File Names that Contain Unusual Characters @cindex file names with unusual characters When a file name contains an unusual character like a newline or white space, @samp{diff -r} generates a patch that @code{patch} cannot parse. The problem is with format of @code{diff} output, not just with @code{patch}, because with odd enough file names one can cause @code{diff} to generate a patch that is syntactically correct but patches the wrong files. The format of @code{diff} output should be extended to handle all possible file names. @node Arbitrary Limits, Large Files, Unusual File Names, Shortcomings @subsection Arbitrary Limits @cindex binary file patching GNU @code{diff} can analyze files with arbitrarily long lines and files that end in incomplete lines. However, @code{patch} cannot patch such files. The @code{patch} internal limits on line lengths should be removed, and @code{patch} should be extended to parse @code{diff} reports of incomplete lines. @node Large Files, Ignoring Changes, Arbitrary Limits, Shortcomings @subsection Handling Files that Do Not Fit in Memory @cindex large files @code{diff} operates by reading both files into memory. This method fails if the files are too large, and @code{diff} should have a fallback. One way to do this is to scan the files sequentially to compute hash codes of the lines and put the lines in equivalence classes based only on hash code. Then compare the files normally. This does produce some false matches. Then scan the two files sequentially again, checking each match to see whether it is real. When a match is not real, mark both the ``matching'' lines as changed. Then build an edit script as usual. The output routines would have to be changed to scan the files sequentially looking for the text to print. @node Ignoring Changes,, Large Files, Shortcomings @subsection Ignoring Certain Changes It would be nice to have a feature for specifying two strings, one in @var{from-file} and one in @var{to-file}, which should be considered to match. Thus, if the two strings are @samp{foo} and @samp{bar}, then if two lines differ only in that @samp{foo} in file 1 corresponds to @samp{bar} in file 2, the lines are treated as identical. It is not clear how general this feature can or should be, or what syntax should be used for it. @node Bugs, , Shortcomings, Projects @section Reporting Bugs @cindex bug reports @cindex reporting bugs If you think you have found a bug in GNU @code{cmp}, @code{diff}, @code{diff3}, @code{sdiff}, or @code{patch}, please report it by electronic mail to @samp{bug-gnu-utils@@prep.ai.mit.edu}. Send as precise a description of the problem as you can, including sample input files that produce the bug, if applicable. Because Larry Wall has not released a new version of @code{patch} since mid 1988 and the GNU version of @code{patch} has been changed since then, please send bug reports for @code{patch} by electronic mail to both @samp{bug-gnu-utils@@prep.ai.mit.edu} and @samp{lwall@@netlabs.com}. @node Concept Index, , Projects, Top @unnumbered Concept Index @printindex cp @shortcontents @contents @bye /sys/src/ape/cmd/diff/diff3.c 664 sys sys 1367613436 50145 /* Three way file comparison program (diff3) for Project GNU. Copyright (C) 1988, 1989, 1992, 1993, 1994 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* Written by Randy Smith */ #include "system.h" #include #include #include "getopt.h" extern char const version_string[]; /* * Internal data structures and macros for the diff3 program; includes * data structures for both diff3 diffs and normal diffs. */ /* Different files within a three way diff. */ #define FILE0 0 #define FILE1 1 #define FILE2 2 /* * A three way diff is built from two two-way diffs; the file which * the two two-way diffs share is: */ #define FILEC FILE2 /* * Different files within a two way diff. * FC is the common file, FO the other file. */ #define FO 0 #define FC 1 /* The ranges are indexed by */ #define START 0 #define END 1 enum diff_type { ERROR, /* Should not be used */ ADD, /* Two way diff add */ CHANGE, /* Two way diff change */ DELETE, /* Two way diff delete */ DIFF_ALL, /* All three are different */ DIFF_1ST, /* Only the first is different */ DIFF_2ND, /* Only the second */ DIFF_3RD /* Only the third */ }; /* Two way diff */ struct diff_block { int ranges[2][2]; /* Ranges are inclusive */ char **lines[2]; /* The actual lines (may contain nulls) */ size_t *lengths[2]; /* Line lengths (including newlines, if any) */ struct diff_block *next; }; /* Three way diff */ struct diff3_block { enum diff_type correspond; /* Type of diff */ int ranges[3][2]; /* Ranges are inclusive */ char **lines[3]; /* The actual lines (may contain nulls) */ size_t *lengths[3]; /* Line lengths (including newlines, if any) */ struct diff3_block *next; }; /* * Access the ranges on a diff block. */ #define D_LOWLINE(diff, filenum) \ ((diff)->ranges[filenum][START]) #define D_HIGHLINE(diff, filenum) \ ((diff)->ranges[filenum][END]) #define D_NUMLINES(diff, filenum) \ (D_HIGHLINE (diff, filenum) - D_LOWLINE (diff, filenum) + 1) /* * Access the line numbers in a file in a diff by relative line * numbers (i.e. line number within the diff itself). Note that these * are lvalues and can be used for assignment. */ #define D_RELNUM(diff, filenum, linenum) \ ((diff)->lines[filenum][linenum]) #define D_RELLEN(diff, filenum, linenum) \ ((diff)->lengths[filenum][linenum]) /* * And get at them directly, when that should be necessary. */ #define D_LINEARRAY(diff, filenum) \ ((diff)->lines[filenum]) #define D_LENARRAY(diff, filenum) \ ((diff)->lengths[filenum]) /* * Next block. */ #define D_NEXT(diff) ((diff)->next) /* * Access the type of a diff3 block. */ #define D3_TYPE(diff) ((diff)->correspond) /* * Line mappings based on diffs. The first maps off the top of the * diff, the second off of the bottom. */ #define D_HIGH_MAPLINE(diff, fromfile, tofile, lineno) \ ((lineno) \ - D_HIGHLINE ((diff), (fromfile)) \ + D_HIGHLINE ((diff), (tofile))) #define D_LOW_MAPLINE(diff, fromfile, tofile, lineno) \ ((lineno) \ - D_LOWLINE ((diff), (fromfile)) \ + D_LOWLINE ((diff), (tofile))) /* * General memory allocation function. */ #define ALLOCATE(number, type) \ (type *) xmalloc ((number) * sizeof (type)) /* Options variables for flags set on command line. */ /* If nonzero, treat all files as text files, never as binary. */ static int always_text; /* If nonzero, write out an ed script instead of the standard diff3 format. */ static int edscript; /* If nonzero, in the case of overlapping diffs (type DIFF_ALL), preserve the lines which would normally be deleted from file 1 with a special flagging mechanism. */ static int flagging; /* Number of lines to keep in identical prefix and suffix. */ static int horizon_lines = 10; /* Use a tab to align output lines (-T). */ static int tab_align_flag; /* If nonzero, do not output information for overlapping diffs. */ static int simple_only; /* If nonzero, do not output information for non-overlapping diffs. */ static int overlap_only; /* If nonzero, show information for DIFF_2ND diffs. */ static int show_2nd; /* If nonzero, include `:wq' at the end of the script to write out the file being edited. */ static int finalwrite; /* If nonzero, output a merged file. */ static int merge; static char *program_name; static VOID *xmalloc PARAMS((size_t)); static VOID *xrealloc PARAMS((VOID *, size_t)); static char *read_diff PARAMS((char const *, char const *, char **)); static char *scan_diff_line PARAMS((char *, char **, size_t *, char *, int)); static enum diff_type process_diff_control PARAMS((char **, struct diff_block *)); static int compare_line_list PARAMS((char * const[], size_t const[], char * const[], size_t const[], int)); static int copy_stringlist PARAMS((char * const[], size_t const[], char *[], size_t[], int)); static int dotlines PARAMS((FILE *, struct diff3_block *, int)); static int output_diff3_edscript PARAMS((FILE *, struct diff3_block *, int const[3], int const[3], char const *, char const *, char const *)); static int output_diff3_merge PARAMS((FILE *, FILE *, struct diff3_block *, int const[3], int const[3], char const *, char const *, char const *)); static size_t myread PARAMS((int, char *, size_t)); static struct diff3_block *create_diff3_block PARAMS((int, int, int, int, int, int)); static struct diff3_block *make_3way_diff PARAMS((struct diff_block *, struct diff_block *)); static struct diff3_block *reverse_diff3_blocklist PARAMS((struct diff3_block *)); static struct diff3_block *using_to_diff3_block PARAMS((struct diff_block *[2], struct diff_block *[2], int, int, struct diff3_block const *)); static struct diff_block *process_diff PARAMS((char const *, char const *, struct diff_block **)); static void check_stdout PARAMS((void)); static void fatal PARAMS((char const *)); static void output_diff3 PARAMS((FILE *, struct diff3_block *, int const[3], int const[3])); static void perror_with_exit PARAMS((char const *)); static void try_help PARAMS((char const *)); static void undotlines PARAMS((FILE *, int, int, int)); static void usage PARAMS((void)); static char const diff_program[] = DIFF_PROGRAM; static struct option const longopts[] = { {"text", 0, 0, 'a'}, {"show-all", 0, 0, 'A'}, {"ed", 0, 0, 'e'}, {"show-overlap", 0, 0, 'E'}, {"label", 1, 0, 'L'}, {"merge", 0, 0, 'm'}, {"initial-tab", 0, 0, 'T'}, {"overlap-only", 0, 0, 'x'}, {"easy-only", 0, 0, '3'}, {"version", 0, 0, 'v'}, {"help", 0, 0, 129}, {0, 0, 0, 0} }; /* * Main program. Calls diff twice on two pairs of input files, * combines the two diffs, and outputs them. */ int main (argc, argv) int argc; char **argv; { int c, i; int mapping[3]; int rev_mapping[3]; int incompat = 0; int conflicts_found; struct diff_block *thread0, *thread1, *last_block; struct diff3_block *diff3; int tag_count = 0; char *tag_strings[3]; char *commonname; char **file; struct stat statb; initialize_main (&argc, &argv); program_name = argv[0]; while ((c = getopt_long (argc, argv, "aeimvx3AEL:TX", longopts, 0)) != EOF) { switch (c) { case 'a': always_text = 1; break; case 'A': show_2nd = 1; flagging = 1; incompat++; break; case 'x': overlap_only = 1; incompat++; break; case '3': simple_only = 1; incompat++; break; case 'i': finalwrite = 1; break; case 'm': merge = 1; break; case 'X': overlap_only = 1; /* Falls through */ case 'E': flagging = 1; /* Falls through */ case 'e': incompat++; break; case 'T': tab_align_flag = 1; break; case 'v': printf ("diff3 - GNU diffutils version %s\n", version_string); exit (0); case 129: usage (); check_stdout (); exit (0); case 'L': /* Handle up to three -L options. */ if (tag_count < 3) { tag_strings[tag_count++] = optarg; break; } try_help ("Too many labels were given. The limit is 3."); default: try_help (0); } } edscript = incompat & ~merge; /* -AeExX3 without -m implies ed script. */ show_2nd |= ~incompat & merge; /* -m without -AeExX3 implies -A. */ flagging |= ~incompat & merge; if (incompat > 1 /* Ensure at most one of -AeExX3. */ || finalwrite & merge /* -i -m would rewrite input file. */ || (tag_count && ! flagging)) /* -L requires one of -AEX. */ try_help ("incompatible options"); if (argc - optind != 3) try_help (argc - optind < 3 ? "missing operand" : "extra operand"); file = &argv[optind]; for (i = tag_count; i < 3; i++) tag_strings[i] = file[i]; /* Always compare file1 to file2, even if file2 is "-". This is needed for -mAeExX3. Using the file0 as the common file would produce wrong results, because if the file0-file1 diffs didn't line up with the file0-file2 diffs (which is entirely possible since we don't use diff's -n option), diff3 might report phantom changes from file1 to file2. */ if (strcmp (file[2], "-") == 0) { /* Sigh. We've got standard input as the last arg. We can't call diff twice on stdin. Use the middle arg as the common file instead. */ if (strcmp (file[0], "-") == 0 || strcmp (file[1], "-") == 0) fatal ("`-' specified for more than one input file"); mapping[0] = 0; mapping[1] = 2; mapping[2] = 1; } else { /* Normal, what you'd expect */ mapping[0] = 0; mapping[1] = 1; mapping[2] = 2; } for (i = 0; i < 3; i++) rev_mapping[mapping[i]] = i; for (i = 0; i < 3; i++) if (strcmp (file[i], "-") != 0) { if (stat (file[i], &statb) < 0) perror_with_exit (file[i]); else if (S_ISDIR(statb.st_mode)) { fprintf (stderr, "%s: %s: Is a directory\n", program_name, file[i]); exit (2); } } #if !defined(SIGCHLD) && defined(SIGCLD) #define SIGCHLD SIGCLD #endif #ifdef SIGCHLD /* System V fork+wait does not work if SIGCHLD is ignored. */ signal (SIGCHLD, SIG_DFL); #endif commonname = file[rev_mapping[FILEC]]; thread1 = process_diff (file[rev_mapping[FILE1]], commonname, &last_block); if (thread1) for (i = 0; i < 2; i++) { horizon_lines = max (horizon_lines, D_NUMLINES (thread1, i)); horizon_lines = max (horizon_lines, D_NUMLINES (last_block, i)); } thread0 = process_diff (file[rev_mapping[FILE0]], commonname, &last_block); diff3 = make_3way_diff (thread0, thread1); if (edscript) conflicts_found = output_diff3_edscript (stdout, diff3, mapping, rev_mapping, tag_strings[0], tag_strings[1], tag_strings[2]); else if (merge) { if (! freopen (file[rev_mapping[FILE0]], "r", stdin)) perror_with_exit (file[rev_mapping[FILE0]]); conflicts_found = output_diff3_merge (stdin, stdout, diff3, mapping, rev_mapping, tag_strings[0], tag_strings[1], tag_strings[2]); if (ferror (stdin)) fatal ("read error"); } else { output_diff3 (stdout, diff3, mapping, rev_mapping); conflicts_found = 0; } check_stdout (); exit (conflicts_found); return conflicts_found; } static void try_help (reason) char const *reason; { if (reason) fprintf (stderr, "%s: %s\n", program_name, reason); fprintf (stderr, "%s: Try `%s --help' for more information.\n", program_name, program_name); exit (2); } static void check_stdout () { if (ferror (stdout) || fclose (stdout) != 0) fatal ("write error"); } /* * Explain, patiently and kindly, how to use this program. */ static void usage () { printf ("Usage: %s [OPTION]... MYFILE OLDFILE YOURFILE\n\n", program_name); printf ("%s", "\ -e --ed Output unmerged changes from OLDFILE to YOURFILE into MYFILE.\n\ -E --show-overlap Output unmerged changes, bracketing conflicts.\n\ -A --show-all Output all changes, bracketing conflicts.\n\ -x --overlap-only Output overlapping changes.\n\ -X Output overlapping changes, bracketing them.\n\ -3 --easy-only Output unmerged nonoverlapping changes.\n\n"); printf ("%s", "\ -m --merge Output merged file instead of ed script (default -A).\n\ -L LABEL --label=LABEL Use LABEL instead of file name.\n\ -i Append `w' and `q' commands to ed scripts.\n\ -a --text Treat all files as text.\n\ -T --initial-tab Make tabs line up by prepending a tab.\n\n"); printf ("%s", "\ -v --version Output version info.\n\ --help Output this help.\n\n"); printf ("If a FILE is `-', read standard input.\n"); } /* * Routines that combine the two diffs together into one. The * algorithm used follows: * * File2 is shared in common between the two diffs. * Diff02 is the diff between 0 and 2. * Diff12 is the diff between 1 and 2. * * 1) Find the range for the first block in File2. * a) Take the lowest of the two ranges (in File2) in the two * current blocks (one from each diff) as being the low * water mark. Assign the upper end of this block as * being the high water mark and move the current block up * one. Mark the block just moved over as to be used. * b) Check the next block in the diff that the high water * mark is *not* from. * * *If* the high water mark is above * the low end of the range in that block, * * mark that block as to be used and move the current * block up. Set the high water mark to the max of * the high end of this block and the current. Repeat b. * * 2) Find the corresponding ranges in File0 (from the blocks * in diff02; line per line outside of diffs) and in File1. * Create a diff3_block, reserving space as indicated by the ranges. * * 3) Copy all of the pointers for file2 in. At least for now, * do memcmp's between corresponding strings in the two diffs. * * 4) Copy all of the pointers for file0 and 1 in. Get what you * need from file2 (when there isn't a diff block, it's * identical to file2 within the range between diff blocks). * * 5) If the diff blocks you used came from only one of the two * strings of diffs, then that file (i.e. the one other than * the common file in that diff) is the odd person out. If you used * diff blocks from both sets, check to see if files 0 and 1 match: * * Same number of lines? If so, do a set of memcmp's (if a * memcmp matches; copy the pointer over; it'll be easier later * if you have to do any compares). If they match, 0 & 1 are * the same. If not, all three different. * * Then you do it again, until you run out of blocks. * */ /* * This routine makes a three way diff (chain of diff3_block's) from two * two way diffs (chains of diff_block's). It is assumed that each of * the two diffs passed are onto the same file (i.e. that each of the * diffs were made "to" the same file). The three way diff pointer * returned will have numbering FILE0--the other file in diff02, * FILE1--the other file in diff12, and FILEC--the common file. */ static struct diff3_block * make_3way_diff (thread0, thread1) struct diff_block *thread0, *thread1; { /* * This routine works on the two diffs passed to it as threads. * Thread number 0 is diff02, thread number 1 is diff12. The USING * array is set to the base of the list of blocks to be used to * construct each block of the three way diff; if no blocks from a * particular thread are to be used, that element of the using array * is set to 0. The elements LAST_USING array are set to the last * elements on each of the using lists. * * The HIGH_WATER_MARK is set to the highest line number in the common file * described in any of the diffs in either of the USING lists. The * HIGH_WATER_THREAD names the thread. Similarly the BASE_WATER_MARK * and BASE_WATER_THREAD describe the lowest line number in the common file * described in any of the diffs in either of the USING lists. The * HIGH_WATER_DIFF is the diff from which the HIGH_WATER_MARK was * taken. * * The HIGH_WATER_DIFF should always be equal to LAST_USING * [HIGH_WATER_THREAD]. The OTHER_DIFF is the next diff to check for * higher water, and should always be equal to * CURRENT[HIGH_WATER_THREAD ^ 0x1]. The OTHER_THREAD is the thread * in which the OTHER_DIFF is, and hence should always be equal to * HIGH_WATER_THREAD ^ 0x1. * * The variable LAST_DIFF is kept set to the last diff block produced * by this routine, for line correspondence purposes between that diff * and the one currently being worked on. It is initialized to * ZERO_DIFF before any blocks have been created. */ struct diff_block *using[2], *last_using[2], *current[2]; int high_water_mark; int high_water_thread, base_water_thread, other_thread; struct diff_block *high_water_diff, *other_diff; struct diff3_block *result, *tmpblock, **result_end; struct diff3_block const *last_diff3; static struct diff3_block const zero_diff3; /* Initialization */ result = 0; result_end = &result; current[0] = thread0; current[1] = thread1; last_diff3 = &zero_diff3; /* Sniff up the threads until we reach the end */ while (current[0] || current[1]) { using[0] = using[1] = last_using[0] = last_using[1] = 0; /* Setup low and high water threads, diffs, and marks. */ if (!current[0]) base_water_thread = 1; else if (!current[1]) base_water_thread = 0; else base_water_thread = (D_LOWLINE (current[0], FC) > D_LOWLINE (current[1], FC)); high_water_thread = base_water_thread; high_water_diff = current[high_water_thread]; #if 0 /* low and high waters start off same diff */ base_water_mark = D_LOWLINE (high_water_diff, FC); #endif high_water_mark = D_HIGHLINE (high_water_diff, FC); /* Make the diff you just got info from into the using class */ using[high_water_thread] = last_using[high_water_thread] = high_water_diff; current[high_water_thread] = high_water_diff->next; last_using[high_water_thread]->next = 0; /* And mark the other diff */ other_thread = high_water_thread ^ 0x1; other_diff = current[other_thread]; /* Shuffle up the ladder, checking the other diff to see if it needs to be incorporated. */ while (other_diff && D_LOWLINE (other_diff, FC) <= high_water_mark + 1) { /* Incorporate this diff into the using list. Note that this doesn't take it off the current list */ if (using[other_thread]) last_using[other_thread]->next = other_diff; else using[other_thread] = other_diff; last_using[other_thread] = other_diff; /* Take it off the current list. Note that this following code assumes that other_diff enters it equal to current[high_water_thread ^ 0x1] */ current[other_thread] = current[other_thread]->next; other_diff->next = 0; /* Set the high_water stuff If this comparison is equal, then this is the last pass through this loop; since diff blocks within a given thread cannot overlap, the high_water_mark will be *below* the range_start of either of the next diffs. */ if (high_water_mark < D_HIGHLINE (other_diff, FC)) { high_water_thread ^= 1; high_water_diff = other_diff; high_water_mark = D_HIGHLINE (other_diff, FC); } /* Set the other diff */ other_thread = high_water_thread ^ 0x1; other_diff = current[other_thread]; } /* The using lists contain a list of all of the blocks to be included in this diff3_block. Create it. */ tmpblock = using_to_diff3_block (using, last_using, base_water_thread, high_water_thread, last_diff3); if (!tmpblock) fatal ("internal error: screwup in format of diff blocks"); /* Put it on the list. */ *result_end = tmpblock; result_end = &tmpblock->next; /* Set up corresponding lines correctly. */ last_diff3 = tmpblock; } return result; } /* * using_to_diff3_block: * This routine takes two lists of blocks (from two separate diff * threads) and puts them together into one diff3 block. * It then returns a pointer to this diff3 block or 0 for failure. * * All arguments besides using are for the convenience of the routine; * they could be derived from the using array. * LAST_USING is a pair of pointers to the last blocks in the using * structure. * LOW_THREAD and HIGH_THREAD tell which threads contain the lowest * and highest line numbers for File0. * last_diff3 contains the last diff produced in the calling routine. * This is used for lines mappings which would still be identical to * the state that diff ended in. * * A distinction should be made in this routine between the two diffs * that are part of a normal two diff block, and the three diffs that * are part of a diff3_block. */ static struct diff3_block * using_to_diff3_block (using, last_using, low_thread, high_thread, last_diff3) struct diff_block *using[2], *last_using[2]; int low_thread, high_thread; struct diff3_block const *last_diff3; { int low[2], high[2]; struct diff3_block *result; struct diff_block *ptr; int d, i; /* Find the range in the common file. */ int lowc = D_LOWLINE (using[low_thread], FC); int highc = D_HIGHLINE (last_using[high_thread], FC); /* Find the ranges in the other files. If using[d] is null, that means that the file to which that diff refers is equivalent to the common file over this range. */ for (d = 0; d < 2; d++) if (using[d]) { low[d] = D_LOW_MAPLINE (using[d], FC, FO, lowc); high[d] = D_HIGH_MAPLINE (last_using[d], FC, FO, highc); } else { low[d] = D_HIGH_MAPLINE (last_diff3, FILEC, FILE0 + d, lowc); high[d] = D_HIGH_MAPLINE (last_diff3, FILEC, FILE0 + d, highc); } /* Create a block with the appropriate sizes */ result = create_diff3_block (low[0], high[0], low[1], high[1], lowc, highc); /* Copy information for the common file. Return with a zero if any of the compares failed. */ for (d = 0; d < 2; d++) for (ptr = using[d]; ptr; ptr = D_NEXT (ptr)) { int result_offset = D_LOWLINE (ptr, FC) - lowc; if (!copy_stringlist (D_LINEARRAY (ptr, FC), D_LENARRAY (ptr, FC), D_LINEARRAY (result, FILEC) + result_offset, D_LENARRAY (result, FILEC) + result_offset, D_NUMLINES (ptr, FC))) return 0; } /* Copy information for file d. First deal with anything that might be before the first diff. */ for (d = 0; d < 2; d++) { struct diff_block *u = using[d]; int lo = low[d], hi = high[d]; for (i = 0; i + lo < (u ? D_LOWLINE (u, FO) : hi + 1); i++) { D_RELNUM (result, FILE0 + d, i) = D_RELNUM (result, FILEC, i); D_RELLEN (result, FILE0 + d, i) = D_RELLEN (result, FILEC, i); } for (ptr = u; ptr; ptr = D_NEXT (ptr)) { int result_offset = D_LOWLINE (ptr, FO) - lo; int linec; if (!copy_stringlist (D_LINEARRAY (ptr, FO), D_LENARRAY (ptr, FO), D_LINEARRAY (result, FILE0 + d) + result_offset, D_LENARRAY (result, FILE0 + d) + result_offset, D_NUMLINES (ptr, FO))) return 0; /* Catch the lines between here and the next diff */ linec = D_HIGHLINE (ptr, FC) + 1 - lowc; for (i = D_HIGHLINE (ptr, FO) + 1 - lo; i < (D_NEXT (ptr) ? D_LOWLINE (D_NEXT (ptr), FO) : hi + 1) - lo; i++) { D_RELNUM (result, FILE0 + d, i) = D_RELNUM (result, FILEC, linec); D_RELLEN (result, FILE0 + d, i) = D_RELLEN (result, FILEC, linec); linec++; } } } /* Set correspond */ if (!using[0]) D3_TYPE (result) = DIFF_2ND; else if (!using[1]) D3_TYPE (result) = DIFF_1ST; else { int nl0 = D_NUMLINES (result, FILE0); int nl1 = D_NUMLINES (result, FILE1); if (nl0 != nl1 || !compare_line_list (D_LINEARRAY (result, FILE0), D_LENARRAY (result, FILE0), D_LINEARRAY (result, FILE1), D_LENARRAY (result, FILE1), nl0)) D3_TYPE (result) = DIFF_ALL; else D3_TYPE (result) = DIFF_3RD; } return result; } /* * This routine copies pointers from a list of strings to a different list * of strings. If a spot in the second list is already filled, it * makes sure that it is filled with the same string; if not it * returns 0, the copy incomplete. * Upon successful completion of the copy, it returns 1. */ static int copy_stringlist (fromptrs, fromlengths, toptrs, tolengths, copynum) char * const fromptrs[]; char *toptrs[]; size_t const fromlengths[]; size_t tolengths[]; int copynum; { register char * const *f = fromptrs; register char **t = toptrs; register size_t const *fl = fromlengths; register size_t *tl = tolengths; while (copynum--) { if (*t) { if (*fl != *tl || memcmp (*f, *t, *fl)) return 0; } else { *t = *f ; *tl = *fl; } t++; f++; tl++; fl++; } return 1; } /* * Create a diff3_block, with ranges as specified in the arguments. * Allocate the arrays for the various pointers (and zero them) based * on the arguments passed. Return the block as a result. */ static struct diff3_block * create_diff3_block (low0, high0, low1, high1, low2, high2) register int low0, high0, low1, high1, low2, high2; { struct diff3_block *result = ALLOCATE (1, struct diff3_block); int numlines; D3_TYPE (result) = ERROR; D_NEXT (result) = 0; /* Assign ranges */ D_LOWLINE (result, FILE0) = low0; D_HIGHLINE (result, FILE0) = high0; D_LOWLINE (result, FILE1) = low1; D_HIGHLINE (result, FILE1) = high1; D_LOWLINE (result, FILE2) = low2; D_HIGHLINE (result, FILE2) = high2; /* Allocate and zero space */ numlines = D_NUMLINES (result, FILE0); if (numlines) { D_LINEARRAY (result, FILE0) = ALLOCATE (numlines, char *); D_LENARRAY (result, FILE0) = ALLOCATE (numlines, size_t); bzero (D_LINEARRAY (result, FILE0), (numlines * sizeof (char *))); bzero (D_LENARRAY (result, FILE0), (numlines * sizeof (size_t))); } else { D_LINEARRAY (result, FILE0) = 0; D_LENARRAY (result, FILE0) = 0; } numlines = D_NUMLINES (result, FILE1); if (numlines) { D_LINEARRAY (result, FILE1) = ALLOCATE (numlines, char *); D_LENARRAY (result, FILE1) = ALLOCATE (numlines, size_t); bzero (D_LINEARRAY (result, FILE1), (numlines * sizeof (char *))); bzero (D_LENARRAY (result, FILE1), (numlines * sizeof (size_t))); } else { D_LINEARRAY (result, FILE1) = 0; D_LENARRAY (result, FILE1) = 0; } numlines = D_NUMLINES (result, FILE2); if (numlines) { D_LINEARRAY (result, FILE2) = ALLOCATE (numlines, char *); D_LENARRAY (result, FILE2) = ALLOCATE (numlines, size_t); bzero (D_LINEARRAY (result, FILE2), (numlines * sizeof (char *))); bzero (D_LENARRAY (result, FILE2), (numlines * sizeof (size_t))); } else { D_LINEARRAY (result, FILE2) = 0; D_LENARRAY (result, FILE2) = 0; } /* Return */ return result; } /* * Compare two lists of lines of text. * Return 1 if they are equivalent, 0 if not. */ static int compare_line_list (list1, lengths1, list2, lengths2, nl) char * const list1[], * const list2[]; size_t const lengths1[], lengths2[]; int nl; { char * const *l1 = list1, * const *l2 = list2; size_t const *lgths1 = lengths1, *lgths2 = lengths2; while (nl--) if (!*l1 || !*l2 || *lgths1 != *lgths2++ || memcmp (*l1++, *l2++, *lgths1++)) return 0; return 1; } /* * Routines to input and parse two way diffs. */ extern char **environ; static struct diff_block * process_diff (filea, fileb, last_block) char const *filea, *fileb; struct diff_block **last_block; { char *diff_contents; char *diff_limit; char *scan_diff; enum diff_type dt; int i; struct diff_block *block_list, **block_list_end, *bptr; diff_limit = read_diff (filea, fileb, &diff_contents); scan_diff = diff_contents; block_list_end = &block_list; bptr = 0; /* Pacify `gcc -W'. */ while (scan_diff < diff_limit) { bptr = ALLOCATE (1, struct diff_block); bptr->lines[0] = bptr->lines[1] = 0; bptr->lengths[0] = bptr->lengths[1] = 0; dt = process_diff_control (&scan_diff, bptr); if (dt == ERROR || *scan_diff != '\n') { fprintf (stderr, "%s: diff error: ", program_name); do { putc (*scan_diff, stderr); } while (*scan_diff++ != '\n'); exit (2); } scan_diff++; /* Force appropriate ranges to be null, if necessary */ switch (dt) { case ADD: bptr->ranges[0][0]++; break; case DELETE: bptr->ranges[1][0]++; break; case CHANGE: break; default: fatal ("internal error: invalid diff type in process_diff"); break; } /* Allocate space for the pointers for the lines from filea, and parcel them out among these pointers */ if (dt != ADD) { int numlines = D_NUMLINES (bptr, 0); bptr->lines[0] = ALLOCATE (numlines, char *); bptr->lengths[0] = ALLOCATE (numlines, size_t); for (i = 0; i < numlines; i++) scan_diff = scan_diff_line (scan_diff, &(bptr->lines[0][i]), &(bptr->lengths[0][i]), diff_limit, '<'); } /* Get past the separator for changes */ if (dt == CHANGE) { if (strncmp (scan_diff, "---\n", 4)) fatal ("invalid diff format; invalid change separator"); scan_diff += 4; } /* Allocate space for the pointers for the lines from fileb, and parcel them out among these pointers */ if (dt != DELETE) { int numlines = D_NUMLINES (bptr, 1); bptr->lines[1] = ALLOCATE (numlines, char *); bptr->lengths[1] = ALLOCATE (numlines, size_t); for (i = 0; i < numlines; i++) scan_diff = scan_diff_line (scan_diff, &(bptr->lines[1][i]), &(bptr->lengths[1][i]), diff_limit, '>'); } /* Place this block on the blocklist. */ *block_list_end = bptr; block_list_end = &bptr->next; } *block_list_end = 0; *last_block = bptr; return block_list; } /* * This routine will parse a normal format diff control string. It * returns the type of the diff (ERROR if the format is bad). All of * the other important information is filled into to the structure * pointed to by db, and the string pointer (whose location is passed * to this routine) is updated to point beyond the end of the string * parsed. Note that only the ranges in the diff_block will be set by * this routine. * * If some specific pair of numbers has been reduced to a single * number, then both corresponding numbers in the diff block are set * to that number. In general these numbers are interpetted as ranges * inclusive, unless being used by the ADD or DELETE commands. It is * assumed that these will be special cased in a superior routine. */ static enum diff_type process_diff_control (string, db) char **string; struct diff_block *db; { char *s = *string; int holdnum; enum diff_type type; /* These macros are defined here because they can use variables defined in this function. Don't try this at home kids, we're trained professionals! Also note that SKIPWHITE only recognizes tabs and spaces, and that READNUM can only read positive, integral numbers */ #define SKIPWHITE(s) { while (*s == ' ' || *s == '\t') s++; } #define READNUM(s, num) \ { unsigned char c = *s; if (!ISDIGIT (c)) return ERROR; holdnum = 0; \ do { holdnum = (c - '0' + holdnum * 10); } \ while (ISDIGIT (c = *++s)); (num) = holdnum; } /* Read first set of digits */ SKIPWHITE (s); READNUM (s, db->ranges[0][START]); /* Was that the only digit? */ SKIPWHITE (s); if (*s == ',') { /* Get the next digit */ s++; READNUM (s, db->ranges[0][END]); } else db->ranges[0][END] = db->ranges[0][START]; /* Get the letter */ SKIPWHITE (s); switch (*s) { case 'a': type = ADD; break; case 'c': type = CHANGE; break; case 'd': type = DELETE; break; default: return ERROR; /* Bad format */ } s++; /* Past letter */ /* Read second set of digits */ SKIPWHITE (s); READNUM (s, db->ranges[1][START]); /* Was that the only digit? */ SKIPWHITE (s); if (*s == ',') { /* Get the next digit */ s++; READNUM (s, db->ranges[1][END]); SKIPWHITE (s); /* To move to end */ } else db->ranges[1][END] = db->ranges[1][START]; *string = s; return type; } static char * read_diff (filea, fileb, output_placement) char const *filea, *fileb; char **output_placement; { char *diff_result; size_t bytes, current_chunk_size, total; int fd, wstatus; struct stat pipestat; /* 302 / 1000 is log10(2.0) rounded up. Subtract 1 for the sign bit; add 1 for integer division truncation; add 1 more for a minus sign. */ #define INT_STRLEN_BOUND(type) ((sizeof(type)*CHAR_BIT - 1) * 302 / 1000 + 2) #if HAVE_FORK char const *argv[7]; char horizon_arg[17 + INT_STRLEN_BOUND (int)]; char const **ap; int fds[2]; pid_t pid; ap = argv; *ap++ = diff_program; if (always_text) *ap++ = "-a"; sprintf (horizon_arg, "--horizon-lines=%d", horizon_lines); *ap++ = horizon_arg; *ap++ = "--"; *ap++ = filea; *ap++ = fileb; *ap = 0; if (pipe (fds) != 0) perror_with_exit ("pipe"); pid = fork (); if (pid == 0) { /* Child */ close (fds[0]); if (fds[1] != STDOUT_FILENO) { dup2 (fds[1], STDOUT_FILENO); close (fds[1]); } execve (diff_program, (char **) argv, environ); /* Avoid stdio, because the parent process's buffers are inherited. */ write (STDERR_FILENO, diff_program, strlen (diff_program)); write (STDERR_FILENO, ": not found\n", 12); _exit (2); } if (pid == -1) perror_with_exit ("fork failed"); close (fds[1]); /* Prevent erroneous lack of EOF */ fd = fds[0]; #else /* ! HAVE_FORK */ FILE *fpipe; char *command = xmalloc (sizeof (diff_program) + 30 + INT_STRLEN_BOUND (int) + 4 * (strlen (filea) + strlen (fileb))); char *p; sprintf (command, "%s -a --horizon-lines=%d -- ", diff_program, horizon_lines); p = command + strlen (command); SYSTEM_QUOTE_ARG (p, filea); *p++ = ' '; SYSTEM_QUOTE_ARG (p, fileb); *p = '\0'; fpipe = popen (command, "r"); if (!fpipe) perror_with_exit (command); free (command); fd = fileno (fpipe); #endif /* ! HAVE_FORK */ current_chunk_size = 8 * 1024; if (fstat (fd, &pipestat) == 0) current_chunk_size = max (current_chunk_size, STAT_BLOCKSIZE (pipestat)); diff_result = xmalloc (current_chunk_size); total = 0; do { bytes = myread (fd, diff_result + total, current_chunk_size - total); total += bytes; if (total == current_chunk_size) { if (current_chunk_size < 2 * current_chunk_size) current_chunk_size = 2 * current_chunk_size; else if (current_chunk_size < (size_t) -1) current_chunk_size = (size_t) -1; else fatal ("files are too large to fit into memory"); diff_result = xrealloc (diff_result, (current_chunk_size *= 2)); } } while (bytes); if (total != 0 && diff_result[total-1] != '\n') fatal ("invalid diff format; incomplete last line"); *output_placement = diff_result; #if ! HAVE_FORK wstatus = pclose (fpipe); #else /* HAVE_FORK */ if (close (fd) != 0) perror_with_exit ("pipe close"); if (waitpid (pid, &wstatus, 0) < 0) perror_with_exit ("waitpid failed"); #endif /* HAVE_FORK */ if (! (WIFEXITED (wstatus) && WEXITSTATUS (wstatus) < 2)) fatal ("subsidiary diff failed"); return diff_result + total; } /* * Scan a regular diff line (consisting of > or <, followed by a * space, followed by text (including nulls) up to a newline. * * This next routine began life as a macro and many parameters in it * are used as call-by-reference values. */ static char * scan_diff_line (scan_ptr, set_start, set_length, limit, leadingchar) char *scan_ptr, **set_start; size_t *set_length; char *limit; int leadingchar; { char *line_ptr; if (!(scan_ptr[0] == leadingchar && scan_ptr[1] == ' ')) fatal ("invalid diff format; incorrect leading line chars"); *set_start = line_ptr = scan_ptr + 2; while (*line_ptr++ != '\n') ; /* Include newline if the original line ended in a newline, or if an edit script is being generated. Copy any missing newline message to stderr if an edit script is being generated, because edit scripts cannot handle missing newlines. Return the beginning of the next line. */ *set_length = line_ptr - *set_start; if (line_ptr < limit && *line_ptr == '\\') { if (edscript) fprintf (stderr, "%s:", program_name); else --*set_length; line_ptr++; do { if (edscript) putc (*line_ptr, stderr); } while (*line_ptr++ != '\n'); } return line_ptr; } /* * This routine outputs a three way diff passed as a list of * diff3_block's. * The argument MAPPING is indexed by external file number (in the * argument list) and contains the internal file number (from the * diff passed). This is important because the user expects his * outputs in terms of the argument list number, and the diff passed * may have been done slightly differently (if the last argument * was "-", for example). * REV_MAPPING is the inverse of MAPPING. */ static void output_diff3 (outputfile, diff, mapping, rev_mapping) FILE *outputfile; struct diff3_block *diff; int const mapping[3], rev_mapping[3]; { int i; int oddoneout; char *cp; struct diff3_block *ptr; int line; size_t length; int dontprint; static int skew_increment[3] = { 2, 3, 1 }; /* 0==>2==>1==>3 */ char const *line_prefix = tab_align_flag ? "\t" : " "; for (ptr = diff; ptr; ptr = D_NEXT (ptr)) { char x[2]; switch (ptr->correspond) { case DIFF_ALL: x[0] = '\0'; dontprint = 3; /* Print them all */ oddoneout = 3; /* Nobody's odder than anyone else */ break; case DIFF_1ST: case DIFF_2ND: case DIFF_3RD: oddoneout = rev_mapping[(int) ptr->correspond - (int) DIFF_1ST]; x[0] = oddoneout + '1'; x[1] = '\0'; dontprint = oddoneout==0; break; default: fatal ("internal error: invalid diff type passed to output"); } fprintf (outputfile, "====%s\n", x); /* Go 0, 2, 1 if the first and third outputs are equivalent. */ for (i = 0; i < 3; i = (oddoneout == 1 ? skew_increment[i] : i + 1)) { int realfile = mapping[i]; int lowt = D_LOWLINE (ptr, realfile), hight = D_HIGHLINE (ptr, realfile); fprintf (outputfile, "%d:", i + 1); switch (lowt - hight) { case 1: fprintf (outputfile, "%da\n", lowt - 1); break; case 0: fprintf (outputfile, "%dc\n", lowt); break; default: fprintf (outputfile, "%d,%dc\n", lowt, hight); break; } if (i == dontprint) continue; if (lowt <= hight) { line = 0; do { fprintf (outputfile, line_prefix); cp = D_RELNUM (ptr, realfile, line); length = D_RELLEN (ptr, realfile, line); fwrite (cp, sizeof (char), length, outputfile); } while (++line < hight - lowt + 1); if (cp[length - 1] != '\n') fprintf (outputfile, "\n\\ No newline at end of file\n"); } } } } /* * Output to OUTPUTFILE the lines of B taken from FILENUM. * Double any initial '.'s; yield nonzero if any initial '.'s were doubled. */ static int dotlines (outputfile, b, filenum) FILE *outputfile; struct diff3_block *b; int filenum; { int i; int leading_dot = 0; for (i = 0; i < D_NUMLINES (b, filenum); i++) { char *line = D_RELNUM (b, filenum, i); if (line[0] == '.') { leading_dot = 1; fprintf (outputfile, "."); } fwrite (line, sizeof (char), D_RELLEN (b, filenum, i), outputfile); } return leading_dot; } /* * Output to OUTPUTFILE a '.' line. If LEADING_DOT is nonzero, * also output a command that removes initial '.'s * starting with line START and continuing for NUM lines. */ static void undotlines (outputfile, leading_dot, start, num) FILE *outputfile; int leading_dot, start, num; { fprintf (outputfile, ".\n"); if (leading_dot) if (num == 1) fprintf (outputfile, "%ds/^\\.//\n", start); else fprintf (outputfile, "%d,%ds/^\\.//\n", start, start + num - 1); } /* * This routine outputs a diff3 set of blocks as an ed script. This * script applies the changes between file's 2 & 3 to file 1. It * takes the precise format of the ed script to be output from global * variables set during options processing. Note that it does * destructive things to the set of diff3 blocks it is passed; it * reverses their order (this gets around the problems involved with * changing line numbers in an ed script). * * Note that this routine has the same problem of mapping as the last * one did; the variable MAPPING maps from file number according to * the argument list to file number according to the diff passed. All * files listed below are in terms of the argument list. * REV_MAPPING is the inverse of MAPPING. * * The arguments FILE0, FILE1 and FILE2 are the strings to print * as the names of the three files. These may be the actual names, * or may be the arguments specified with -L. * * Returns 1 if conflicts were found. */ static int output_diff3_edscript (outputfile, diff, mapping, rev_mapping, file0, file1, file2) FILE *outputfile; struct diff3_block *diff; int const mapping[3], rev_mapping[3]; char const *file0, *file1, *file2; { int leading_dot; int conflicts_found = 0, conflict; struct diff3_block *b; for (b = reverse_diff3_blocklist (diff); b; b = b->next) { /* Must do mapping correctly. */ enum diff_type type = ((b->correspond == DIFF_ALL) ? DIFF_ALL : ((enum diff_type) (((int) DIFF_1ST) + rev_mapping[(int) b->correspond - (int) DIFF_1ST]))); /* If we aren't supposed to do this output block, skip it. */ switch (type) { default: continue; case DIFF_2ND: if (!show_2nd) continue; conflict = 1; break; case DIFF_3RD: if (overlap_only) continue; conflict = 0; break; case DIFF_ALL: if (simple_only) continue; conflict = flagging; break; } if (conflict) { conflicts_found = 1; /* Mark end of conflict. */ fprintf (outputfile, "%da\n", D_HIGHLINE (b, mapping[FILE0])); leading_dot = 0; if (type == DIFF_ALL) { if (show_2nd) { /* Append lines from FILE1. */ fprintf (outputfile, "||||||| %s\n", file1); leading_dot = dotlines (outputfile, b, mapping[FILE1]); } /* Append lines from FILE2. */ fprintf (outputfile, "=======\n"); leading_dot |= dotlines (outputfile, b, mapping[FILE2]); } fprintf (outputfile, ">>>>>>> %s\n", file2); undotlines (outputfile, leading_dot, D_HIGHLINE (b, mapping[FILE0]) + 2, (D_NUMLINES (b, mapping[FILE1]) + D_NUMLINES (b, mapping[FILE2]) + 1)); /* Mark start of conflict. */ fprintf (outputfile, "%da\n<<<<<<< %s\n", D_LOWLINE (b, mapping[FILE0]) - 1, type == DIFF_ALL ? file0 : file1); leading_dot = 0; if (type == DIFF_2ND) { /* Prepend lines from FILE1. */ leading_dot = dotlines (outputfile, b, mapping[FILE1]); fprintf (outputfile, "=======\n"); } undotlines (outputfile, leading_dot, D_LOWLINE (b, mapping[FILE0]) + 1, D_NUMLINES (b, mapping[FILE1])); } else if (D_NUMLINES (b, mapping[FILE2]) == 0) /* Write out a delete */ { if (D_NUMLINES (b, mapping[FILE0]) == 1) fprintf (outputfile, "%dd\n", D_LOWLINE (b, mapping[FILE0])); else fprintf (outputfile, "%d,%dd\n", D_LOWLINE (b, mapping[FILE0]), D_HIGHLINE (b, mapping[FILE0])); } else /* Write out an add or change */ { switch (D_NUMLINES (b, mapping[FILE0])) { case 0: fprintf (outputfile, "%da\n", D_HIGHLINE (b, mapping[FILE0])); break; case 1: fprintf (outputfile, "%dc\n", D_HIGHLINE (b, mapping[FILE0])); break; default: fprintf (outputfile, "%d,%dc\n", D_LOWLINE (b, mapping[FILE0]), D_HIGHLINE (b, mapping[FILE0])); break; } undotlines (outputfile, dotlines (outputfile, b, mapping[FILE2]), D_LOWLINE (b, mapping[FILE0]), D_NUMLINES (b, mapping[FILE2])); } } if (finalwrite) fprintf (outputfile, "w\nq\n"); return conflicts_found; } /* * Read from INFILE and output to OUTPUTFILE a set of diff3_ blocks DIFF * as a merged file. This acts like 'ed file0 <[output_diff3_edscript]', * except that it works even for binary data or incomplete lines. * * As before, MAPPING maps from arg list file number to diff file number, * REV_MAPPING is its inverse, * and FILE0, FILE1, and FILE2 are the names of the files. * * Returns 1 if conflicts were found. */ static int output_diff3_merge (infile, outputfile, diff, mapping, rev_mapping, file0, file1, file2) FILE *infile, *outputfile; struct diff3_block *diff; int const mapping[3], rev_mapping[3]; char const *file0, *file1, *file2; { int c, i; int conflicts_found = 0, conflict; struct diff3_block *b; int linesread = 0; for (b = diff; b; b = b->next) { /* Must do mapping correctly. */ enum diff_type type = ((b->correspond == DIFF_ALL) ? DIFF_ALL : ((enum diff_type) (((int) DIFF_1ST) + rev_mapping[(int) b->correspond - (int) DIFF_1ST]))); char const *format_2nd = "<<<<<<< %s\n"; /* If we aren't supposed to do this output block, skip it. */ switch (type) { default: continue; case DIFF_2ND: if (!show_2nd) continue; conflict = 1; break; case DIFF_3RD: if (overlap_only) continue; conflict = 0; break; case DIFF_ALL: if (simple_only) continue; conflict = flagging; format_2nd = "||||||| %s\n"; break; } /* Copy I lines from file 0. */ i = D_LOWLINE (b, FILE0) - linesread - 1; linesread += i; while (0 <= --i) do { c = getc (infile); if (c == EOF) if (ferror (infile)) perror_with_exit ("input file"); else if (feof (infile)) fatal ("input file shrank"); putc (c, outputfile); } while (c != '\n'); if (conflict) { conflicts_found = 1; if (type == DIFF_ALL) { /* Put in lines from FILE0 with bracket. */ fprintf (outputfile, "<<<<<<< %s\n", file0); for (i = 0; i < D_NUMLINES (b, mapping[FILE0]); i++) fwrite (D_RELNUM (b, mapping[FILE0], i), sizeof (char), D_RELLEN (b, mapping[FILE0], i), outputfile); } if (show_2nd) { /* Put in lines from FILE1 with bracket. */ fprintf (outputfile, format_2nd, file1); for (i = 0; i < D_NUMLINES (b, mapping[FILE1]); i++) fwrite (D_RELNUM (b, mapping[FILE1], i), sizeof (char), D_RELLEN (b, mapping[FILE1], i), outputfile); } fprintf (outputfile, "=======\n"); } /* Put in lines from FILE2. */ for (i = 0; i < D_NUMLINES (b, mapping[FILE2]); i++) fwrite (D_RELNUM (b, mapping[FILE2], i), sizeof (char), D_RELLEN (b, mapping[FILE2], i), outputfile); if (conflict) fprintf (outputfile, ">>>>>>> %s\n", file2); /* Skip I lines in file 0. */ i = D_NUMLINES (b, FILE0); linesread += i; while (0 <= --i) while ((c = getc (infile)) != '\n') if (c == EOF) if (ferror (infile)) perror_with_exit ("input file"); else if (feof (infile)) { if (i || b->next) fatal ("input file shrank"); return conflicts_found; } } /* Copy rest of common file. */ while ((c = getc (infile)) != EOF || !(ferror (infile) | feof (infile))) putc (c, outputfile); return conflicts_found; } /* * Reverse the order of the list of diff3 blocks. */ static struct diff3_block * reverse_diff3_blocklist (diff) struct diff3_block *diff; { register struct diff3_block *tmp, *next, *prev; for (tmp = diff, prev = 0; tmp; tmp = next) { next = tmp->next; tmp->next = prev; prev = tmp; } return prev; } static size_t myread (fd, ptr, size) int fd; char *ptr; size_t size; { size_t result = read (fd, ptr, size); if (result == -1) perror_with_exit ("read failed"); return result; } static VOID * xmalloc (size) size_t size; { VOID *result = (VOID *) malloc (size ? size : 1); if (!result) fatal ("memory exhausted"); return result; } static VOID * xrealloc (ptr, size) VOID *ptr; size_t size; { VOID *result = (VOID *) realloc (ptr, size ? size : 1); if (!result) fatal ("memory exhausted"); return result; } static void fatal (string) char const *string; { fprintf (stderr, "%s: %s\n", program_name, string); exit (2); } static void perror_with_exit (string) char const *string; { int e = errno; fprintf (stderr, "%s: ", program_name); errno = e; perror (string); exit (2); } /sys/src/ape/cmd/diff/dir.c 664 sys sys 1367613436 6178 /* Read, sort and compare two directories. Used for GNU DIFF. Copyright (C) 1988, 1989, 1992, 1993, 1994 Free Software Foundation, Inc. This file is part of GNU DIFF. GNU DIFF 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. GNU DIFF 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 GNU DIFF; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "diff.h" /* Read the directory named by DIR and store into DIRDATA a sorted vector of filenames for its contents. DIR->desc == -1 means this directory is known to be nonexistent, so set DIRDATA to an empty vector. Return -1 (setting errno) if error, 0 otherwise. */ struct dirdata { char const **names; /* Sorted names of files in dir, 0-terminated. */ char *data; /* Allocated storage for file names. */ }; static int compare_names PARAMS((void const *, void const *)); static int dir_sort PARAMS((struct file_data const *, struct dirdata *)); static int dir_sort (dir, dirdata) struct file_data const *dir; struct dirdata *dirdata; { register struct dirent *next; register int i; /* Address of block containing the files that are described. */ char const **names; /* Number of files in directory. */ size_t nnames; /* Allocated and used storage for file name data. */ char *data; size_t data_alloc, data_used; dirdata->names = 0; dirdata->data = 0; nnames = 0; data = 0; if (dir->desc != -1) { /* Open the directory and check for errors. */ register DIR *reading = opendir (dir->name); if (!reading) return -1; /* Initialize the table of filenames. */ data_alloc = max (1, (size_t) dir->stat.st_size); data_used = 0; dirdata->data = data = xmalloc (data_alloc); /* Read the directory entries, and insert the subfiles into the `data' table. */ while ((errno = 0, (next = readdir (reading)) != 0)) { char *d_name = next->d_name; size_t d_size = NAMLEN (next) + 1; /* Ignore the files `.' and `..' */ if (d_name[0] == '.' && (d_name[1] == 0 || (d_name[1] == '.' && d_name[2] == 0))) continue; if (excluded_filename (d_name)) continue; while (data_alloc < data_used + d_size) dirdata->data = data = xrealloc (data, data_alloc *= 2); memcpy (data + data_used, d_name, d_size); data_used += d_size; nnames++; } if (errno) { int e = errno; closedir (reading); errno = e; return -1; } #if CLOSEDIR_VOID closedir (reading); #else if (closedir (reading) != 0) return -1; #endif } /* Create the `names' table from the `data' table. */ dirdata->names = names = (char const **) xmalloc (sizeof (char *) * (nnames + 1)); for (i = 0; i < nnames; i++) { names[i] = data; data += strlen (data) + 1; } names[nnames] = 0; /* Sort the table. */ qsort (names, nnames, sizeof (char *), compare_names); return 0; } /* Sort the files now in the table. */ static int compare_names (file1, file2) void const *file1, *file2; { return filename_cmp (* (char const *const *) file1, * (char const *const *) file2); } /* Compare the contents of two directories named in FILEVEC[0] and FILEVEC[1]. This is a top-level routine; it does everything necessary for diff on two directories. FILEVEC[0].desc == -1 says directory FILEVEC[0] doesn't exist, but pretend it is empty. Likewise for FILEVEC[1]. HANDLE_FILE is a caller-provided subroutine called to handle each file. It gets five operands: dir and name (rel to original working dir) of file in dir 0, dir and name pathname of file in dir 1, and the recursion depth. For a file that appears in only one of the dirs, one of the name-args to HANDLE_FILE is zero. DEPTH is the current depth in recursion, used for skipping top-level files by the -S option. Returns the maximum of all the values returned by HANDLE_FILE, or 2 if trouble is encountered in opening files. */ int diff_dirs (filevec, handle_file, depth) struct file_data const filevec[]; int (*handle_file) PARAMS((char const *, char const *, char const *, char const *, int)); int depth; { struct dirdata dirdata[2]; int val = 0; /* Return value. */ int i; /* Get sorted contents of both dirs. */ for (i = 0; i < 2; i++) if (dir_sort (&filevec[i], &dirdata[i]) != 0) { perror_with_name (filevec[i].name); val = 2; } if (val == 0) { register char const * const *names0 = dirdata[0].names; register char const * const *names1 = dirdata[1].names; char const *name0 = filevec[0].name; char const *name1 = filevec[1].name; /* If `-S name' was given, and this is the topmost level of comparison, ignore all file names less than the specified starting name. */ if (dir_start_file && depth == 0) { while (*names0 && filename_cmp (*names0, dir_start_file) < 0) names0++; while (*names1 && filename_cmp (*names1, dir_start_file) < 0) names1++; } /* Loop while files remain in one or both dirs. */ while (*names0 || *names1) { /* Compare next name in dir 0 with next name in dir 1. At the end of a dir, pretend the "next name" in that dir is very large. */ int nameorder = (!*names0 ? 1 : !*names1 ? -1 : filename_cmp (*names0, *names1)); int v1 = (*handle_file) (name0, 0 < nameorder ? 0 : *names0++, name1, nameorder < 0 ? 0 : *names1++, depth + 1); if (v1 > val) val = v1; } } for (i = 0; i < 2; i++) { if (dirdata[i].names) free (dirdata[i].names); if (dirdata[i].data) free (dirdata[i].data); } return val; } /sys/src/ape/cmd/diff/ed.c 664 sys sys 1367613436 5288 /* Output routines for ed-script format. Copyright (C) 1988, 89, 91, 92, 93 Free Software Foundation, Inc. This file is part of GNU DIFF. GNU DIFF 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. GNU DIFF 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 GNU DIFF; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "diff.h" static void print_ed_hunk PARAMS((struct change *)); static void print_rcs_hunk PARAMS((struct change *)); static void pr_forward_ed_hunk PARAMS((struct change *)); /* Print our script as ed commands. */ void print_ed_script (script) struct change *script; { print_script (script, find_reverse_change, print_ed_hunk); } /* Print a hunk of an ed diff */ static void print_ed_hunk (hunk) struct change *hunk; { int f0, l0, f1, l1; int deletes, inserts; #if 0 hunk = flip_script (hunk); #endif #ifdef DEBUG debug_script (hunk); #endif /* Determine range of line numbers involved in each file. */ analyze_hunk (hunk, &f0, &l0, &f1, &l1, &deletes, &inserts); if (!deletes && !inserts) return; begin_output (); /* Print out the line number header for this hunk */ print_number_range (',', &files[0], f0, l0); fprintf (outfile, "%c\n", change_letter (inserts, deletes)); /* Print new/changed lines from second file, if needed */ if (inserts) { int i; int inserting = 1; for (i = f1; i <= l1; i++) { /* Resume the insert, if we stopped. */ if (! inserting) fprintf (outfile, "%da\n", i - f1 + translate_line_number (&files[0], f0) - 1); inserting = 1; /* If the file's line is just a dot, it would confuse `ed'. So output it with a double dot, and set the flag LEADING_DOT so that we will output another ed-command later to change the double dot into a single dot. */ if (files[1].linbuf[i][0] == '.' && files[1].linbuf[i][1] == '\n') { fprintf (outfile, "..\n"); fprintf (outfile, ".\n"); /* Now change that double dot to the desired single dot. */ fprintf (outfile, "%ds/^\\.\\././\n", i - f1 + translate_line_number (&files[0], f0)); inserting = 0; } else /* Line is not `.', so output it unmodified. */ print_1_line ("", &files[1].linbuf[i]); } /* End insert mode, if we are still in it. */ if (inserting) fprintf (outfile, ".\n"); } } /* Print change script in the style of ed commands, but print the changes in the order they appear in the input files, which means that the commands are not truly useful with ed. */ void pr_forward_ed_script (script) struct change *script; { print_script (script, find_change, pr_forward_ed_hunk); } static void pr_forward_ed_hunk (hunk) struct change *hunk; { int i; int f0, l0, f1, l1; int deletes, inserts; /* Determine range of line numbers involved in each file. */ analyze_hunk (hunk, &f0, &l0, &f1, &l1, &deletes, &inserts); if (!deletes && !inserts) return; begin_output (); fprintf (outfile, "%c", change_letter (inserts, deletes)); print_number_range (' ', files, f0, l0); fprintf (outfile, "\n"); /* If deletion only, print just the number range. */ if (!inserts) return; /* For insertion (with or without deletion), print the number range and the lines from file 2. */ for (i = f1; i <= l1; i++) print_1_line ("", &files[1].linbuf[i]); fprintf (outfile, ".\n"); } /* Print in a format somewhat like ed commands except that each insert command states the number of lines it inserts. This format is used for RCS. */ void print_rcs_script (script) struct change *script; { print_script (script, find_change, print_rcs_hunk); } /* Print a hunk of an RCS diff */ static void print_rcs_hunk (hunk) struct change *hunk; { int i; int f0, l0, f1, l1; int deletes, inserts; int tf0, tl0, tf1, tl1; /* Determine range of line numbers involved in each file. */ analyze_hunk (hunk, &f0, &l0, &f1, &l1, &deletes, &inserts); if (!deletes && !inserts) return; begin_output (); translate_range (&files[0], f0, l0, &tf0, &tl0); if (deletes) { fprintf (outfile, "d"); /* For deletion, print just the starting line number from file 0 and the number of lines deleted. */ fprintf (outfile, "%d %d\n", tf0, (tl0 >= tf0 ? tl0 - tf0 + 1 : 1)); } if (inserts) { fprintf (outfile, "a"); /* Take last-line-number from file 0 and # lines from file 1. */ translate_range (&files[1], f1, l1, &tf1, &tl1); fprintf (outfile, "%d %d\n", tl0, (tl1 >= tf1 ? tl1 - tf1 + 1 : 1)); /* Print the inserted lines. */ for (i = f1; i <= l1; i++) print_1_line ("", &files[1].linbuf[i]); } } /sys/src/ape/cmd/diff/fnmatch.c 664 sys sys 1367613436 4105 /* Copyright (C) 1992 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. The GNU C Library 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 Library General Public License for more details. */ /* Modified slightly by Brian Berliner and Jim Blandy for CVS use */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "system.h" /* IGNORE(@ */ /* #include */ /* @) */ #include #include "fnmatch.h" #if !defined(__GNU_LIBRARY__) && !defined(STDC_HEADERS) extern int errno; #endif /* Match STRING against the filename pattern PATTERN, returning zero if it matches, nonzero if not. */ int #if __STDC__ fnmatch (const char *pattern, const char *string, int flags) #else fnmatch (pattern, string, flags) char *pattern; char *string; int flags; #endif { register const char *p = pattern, *n = string; register char c; if ((flags & ~__FNM_FLAGS) != 0) { errno = EINVAL; return -1; } while ((c = *p++) != '\0') { switch (c) { case '?': if (*n == '\0') return FNM_NOMATCH; else if ((flags & FNM_PATHNAME) && *n == '/') return FNM_NOMATCH; else if ((flags & FNM_PERIOD) && *n == '.' && (n == string || ((flags & FNM_PATHNAME) && n[-1] == '/'))) return FNM_NOMATCH; break; case '\\': if (!(flags & FNM_NOESCAPE)) c = *p++; if (FOLD_FN_CHAR (*n) != FOLD_FN_CHAR (c)) return FNM_NOMATCH; break; case '*': if ((flags & FNM_PERIOD) && *n == '.' && (n == string || ((flags & FNM_PATHNAME) && n[-1] == '/'))) return FNM_NOMATCH; for (c = *p++; c == '?' || c == '*'; c = *p++, ++n) if (((flags & FNM_PATHNAME) && *n == '/') || (c == '?' && *n == '\0')) return FNM_NOMATCH; if (c == '\0') return 0; { char c1 = (!(flags & FNM_NOESCAPE) && c == '\\') ? *p : c; for (--p; *n != '\0'; ++n) if ((c == '[' || FOLD_FN_CHAR (*n) == FOLD_FN_CHAR (c1)) && fnmatch(p, n, flags & ~FNM_PERIOD) == 0) return 0; return FNM_NOMATCH; } case '[': { /* Nonzero if the sense of the character class is inverted. */ register int not; if (*n == '\0') return FNM_NOMATCH; if ((flags & FNM_PERIOD) && *n == '.' && (n == string || ((flags & FNM_PATHNAME) && n[-1] == '/'))) return FNM_NOMATCH; not = (*p == '!' || *p == '^'); if (not) ++p; c = *p++; for (;;) { register char cstart = c, cend = c; if (!(flags & FNM_NOESCAPE) && c == '\\') cstart = cend = *p++; if (c == '\0') /* [ (unterminated) loses. */ return FNM_NOMATCH; c = *p++; if ((flags & FNM_PATHNAME) && c == '/') /* [/] can never match. */ return FNM_NOMATCH; if (c == '-' && *p != ']') { cend = *p++; if (!(flags & FNM_NOESCAPE) && cend == '\\') cend = *p++; if (cend == '\0') return FNM_NOMATCH; c = *p++; } if (*n >= cstart && *n <= cend) goto matched; if (c == ']') break; } if (!not) return FNM_NOMATCH; break; matched:; /* Skip the rest of the [...] that already matched. */ while (c != ']') { if (c == '\0') /* [... (unterminated) loses. */ return FNM_NOMATCH; c = *p++; if (!(flags & FNM_NOESCAPE) && c == '\\') /* 1003.2d11 is unclear if this is right. %%% */ ++p; } if (not) return FNM_NOMATCH; } break; default: if (FOLD_FN_CHAR (c) != FOLD_FN_CHAR (*n)) return FNM_NOMATCH; } ++n; } if (*n == '\0') return 0; return FNM_NOMATCH; } /sys/src/ape/cmd/diff/fnmatch.h 664 sys sys 1367613436 1416 /* Copyright (C) 1992 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. The GNU C Library 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 Library General Public License for more details. */ #ifndef _FNMATCH_H #define _FNMATCH_H 1 /* Bits set in the FLAGS argument to `fnmatch'. */ #undef FNM_PATHNAME #define FNM_PATHNAME (1 << 0)/* No wildcard can ever match `/'. */ #undef FNM_NOESCAPE #define FNM_NOESCAPE (1 << 1)/* Backslashes don't quote special chars. */ #undef FNM_PERIOD #define FNM_PERIOD (1 << 2)/* Leading `.' is matched only explicitly. */ #undef __FNM_FLAGS #define __FNM_FLAGS (FNM_PATHNAME|FNM_NOESCAPE|FNM_PERIOD) /* Value returned by `fnmatch' if STRING does not match PATTERN. */ #undef FNM_NOMATCH #define FNM_NOMATCH 1 /* Match STRING against the filename pattern PATTERN, returning zero if it matches, FNM_NOMATCH if not. */ #if __STDC__ extern int fnmatch (const char *pattern, const char *string, int flags); #else extern int fnmatch (); #endif #endif /* fnmatch.h */ /sys/src/ape/cmd/diff/getopt.c 664 sys sys 1367613436 21723 /* Getopt for GNU. NOTE: getopt is now part of the C library, so if you don't know what "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu before changing it! Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94 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, 675 Mass Ave, Cambridge, MA 02139, USA. */ /* This tells Alpha OSF/1 not to define a getopt prototype in . Ditto for AIX 3.2 and . */ #ifndef _NO_PROTO #define _NO_PROTO #endif #ifdef HAVE_CONFIG_H #include #endif #ifndef __STDC__ /* This is a separate conditional since some stdc systems reject `defined (const)'. */ #ifndef const #define const #endif #endif #include /* Comment out all this code if we are using the GNU C Library, and are not actually compiling the library itself. This code is part of the GNU C Library, but also included in many other GNU distributions. Compiling and linking in this code is a waste when using the GNU C library (especially if it is a shared library). Rather than having every GNU program understand `configure --with-gnu-libc' and omit the object files, it is simpler to just do this in the source for each such file. */ #if defined (_LIBC) || !defined (__GNU_LIBRARY__) /* This needs to come after some library #include to get __GNU_LIBRARY__ defined. */ #ifdef __GNU_LIBRARY__ /* Don't include stdlib.h for non-GNU C libraries because some of them contain conflicting prototypes for getopt. */ #include #endif /* GNU C library. */ /* This version of `getopt' appears to the caller like standard Unix `getopt' but it behaves differently for the user, since it allows the user to intersperse the options with the other arguments. As `getopt' works, it permutes the elements of ARGV so that, when it is done, all the options precede everything else. Thus all application programs are extended to handle flexible argument order. Setting the environment variable POSIXLY_CORRECT disables permutation. Then the behavior is completely standard. GNU application programs can use a third alternative mode in which they can distinguish the relative order of options and other arguments. */ #include "getopt.h" /* For communication from `getopt' to the caller. When `getopt' finds an option that takes an argument, the argument value is returned here. Also, when `ordering' is RETURN_IN_ORDER, each non-option ARGV-element is returned here. */ char *optarg = NULL; /* Index in ARGV of the next element to be scanned. This is used for communication to and from the caller and for communication between successive calls to `getopt'. On entry to `getopt', zero means this is the first call; initialize. When `getopt' returns EOF, this is the index of the first of the non-option elements that the caller should itself scan. Otherwise, `optind' communicates from one call to the next how much of ARGV has been scanned so far. */ /* XXX 1003.2 says this must be 1 before any call. */ int optind = 0; /* The next char to be scanned in the option-element in which the last option character we returned was found. This allows us to pick up the scan where we left off. If this is zero, or a null string, it means resume the scan by advancing to the next ARGV-element. */ static char *nextchar; /* Callers store zero here to inhibit the error message for unrecognized options. */ int opterr = 1; /* Set to an option character which was unrecognized. This must be initialized on some systems to avoid linking in the system's own getopt implementation. */ int optopt = '?'; /* Describe how to deal with options that follow non-option ARGV-elements. If the caller did not specify anything, the default is REQUIRE_ORDER if the environment variable POSIXLY_CORRECT is defined, PERMUTE otherwise. REQUIRE_ORDER means don't recognize them as options; stop option processing when the first non-option is seen. This is what Unix does. This mode of operation is selected by either setting the environment variable POSIXLY_CORRECT, or using `+' as the first character of the list of option characters. PERMUTE is the default. We permute the contents of ARGV as we scan, so that eventually all the non-options are at the end. This allows options to be given in any order, even with programs that were not written to expect this. RETURN_IN_ORDER is an option available to programs that were written to expect options and other ARGV-elements in any order and that care about the ordering of the two. We describe each non-option ARGV-element as if it were the argument of an option with character code 1. Using `-' as the first character of the list of option characters selects this mode of operation. The special argument `--' forces an end of option-scanning regardless of the value of `ordering'. In the case of RETURN_IN_ORDER, only `--' can cause `getopt' to return EOF with `optind' != ARGC. */ static enum { REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER } ordering; /* Value of POSIXLY_CORRECT environment variable. */ static char *posixly_correct; #ifdef __GNU_LIBRARY__ /* We want to avoid inclusion of string.h with non-GNU libraries because there are many ways it can cause trouble. On some systems, it contains special magic macros that don't work in GCC. */ #include #define my_index strchr #else /* Avoid depending on library functions or files whose names are inconsistent. */ char *getenv (); static char * my_index (str, chr) const char *str; int chr; { while (*str) { if (*str == chr) return (char *) str; str++; } return 0; } /* If using GCC, we can safely declare strlen this way. If not using GCC, it is ok not to declare it. */ #ifdef __GNUC__ /* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h. That was relevant to code that was here before. */ #ifndef __STDC__ /* gcc with -traditional declares the built-in strlen to return int, and has done so at least since version 2.4.5. -- rms. */ extern int strlen (const char *); #endif /* not __STDC__ */ #endif /* __GNUC__ */ #endif /* not __GNU_LIBRARY__ */ /* Handle permutation of arguments. */ /* Describe the part of ARGV that contains non-options that have been skipped. `first_nonopt' is the index in ARGV of the first of them; `last_nonopt' is the index after the last of them. */ static int first_nonopt; static int last_nonopt; /* Exchange two adjacent subsequences of ARGV. One subsequence is elements [first_nonopt,last_nonopt) which contains all the non-options that have been skipped so far. The other is elements [last_nonopt,optind), which contains all the options processed since those non-options were skipped. `first_nonopt' and `last_nonopt' are relocated so that they describe the new indices of the non-options in ARGV after they are moved. */ static void exchange (argv) char **argv; { int bottom = first_nonopt; int middle = last_nonopt; int top = optind; char *tem; /* Exchange the shorter segment with the far end of the longer segment. That puts the shorter segment into the right place. It leaves the longer segment in the right place overall, but it consists of two parts that need to be swapped next. */ while (top > middle && middle > bottom) { if (top - middle > middle - bottom) { /* Bottom segment is the short one. */ int len = middle - bottom; register int i; /* Swap it with the top part of the top segment. */ for (i = 0; i < len; i++) { tem = argv[bottom + i]; argv[bottom + i] = argv[top - (middle - bottom) + i]; argv[top - (middle - bottom) + i] = tem; } /* Exclude the moved bottom segment from further swapping. */ top -= len; } else { /* Top segment is the short one. */ int len = top - middle; register int i; /* Swap it with the bottom part of the bottom segment. */ for (i = 0; i < len; i++) { tem = argv[bottom + i]; argv[bottom + i] = argv[middle + i]; argv[middle + i] = tem; } /* Exclude the moved top segment from further swapping. */ bottom += len; } } /* Update records for the slots the non-options now occupy. */ first_nonopt += (optind - last_nonopt); last_nonopt = optind; } /* Initialize the internal data when the first call is made. */ static const char * _getopt_initialize (optstring) const char *optstring; { /* Start processing options with ARGV-element 1 (since ARGV-element 0 is the program name); the sequence of previously skipped non-option ARGV-elements is empty. */ first_nonopt = last_nonopt = optind = 1; nextchar = NULL; posixly_correct = getenv ("POSIXLY_CORRECT"); /* Determine how to handle the ordering of options and nonoptions. */ if (optstring[0] == '-') { ordering = RETURN_IN_ORDER; ++optstring; } else if (optstring[0] == '+') { ordering = REQUIRE_ORDER; ++optstring; } else if (posixly_correct != NULL) ordering = REQUIRE_ORDER; else ordering = PERMUTE; return optstring; } /* Scan elements of ARGV (whose length is ARGC) for option characters given in OPTSTRING. If an element of ARGV starts with '-', and is not exactly "-" or "--", then it is an option element. The characters of this element (aside from the initial '-') are option characters. If `getopt' is called repeatedly, it returns successively each of the option characters from each of the option elements. If `getopt' finds another option character, it returns that character, updating `optind' and `nextchar' so that the next call to `getopt' can resume the scan with the following option character or ARGV-element. If there are no more option characters, `getopt' returns `EOF'. Then `optind' is the index in ARGV of the first ARGV-element that is not an option. (The ARGV-elements have been permuted so that those that are not options now come last.) OPTSTRING is a string containing the legitimate option characters. If an option character is seen that is not listed in OPTSTRING, return '?' after printing an error message. If you set `opterr' to zero, the error message is suppressed but we still return '?'. If a char in OPTSTRING is followed by a colon, that means it wants an arg, so the following text in the same ARGV-element, or the text of the following ARGV-element, is returned in `optarg'. Two colons mean an option that wants an optional arg; if there is text in the current ARGV-element, it is returned in `optarg', otherwise `optarg' is set to zero. If OPTSTRING starts with `-' or `+', it requests different methods of handling the non-option ARGV-elements. See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. Long-named options begin with `--' instead of `-'. Their names may be abbreviated as long as the abbreviation is unique or is an exact match for some defined option. If they have an argument, it follows the option name in the same ARGV-element, separated from the option name by a `=', or else the in next ARGV-element. When `getopt' finds a long-named option, it returns 0 if that option's `flag' field is nonzero, the value of the option's `val' field if the `flag' field is zero. The elements of ARGV aren't really const, because we permute them. But we pretend they're const in the prototype to be compatible with other systems. LONGOPTS is a vector of `struct option' terminated by an element containing a name which is zero. LONGIND returns the index in LONGOPT of the long-named option found. It is only valid when a long-named option has been found by the most recent call. If LONG_ONLY is nonzero, '-' as well as '--' can introduce long-named options. */ int _getopt_internal (argc, argv, optstring, longopts, longind, long_only) int argc; char *const *argv; const char *optstring; const struct option *longopts; int *longind; int long_only; { optarg = NULL; if (optind == 0) optstring = _getopt_initialize (optstring); if (nextchar == NULL || *nextchar == '\0') { /* Advance to the next ARGV-element. */ if (ordering == PERMUTE) { /* If we have just processed some options following some non-options, exchange them so that the options come first. */ if (first_nonopt != last_nonopt && last_nonopt != optind) exchange ((char **) argv); else if (last_nonopt != optind) first_nonopt = optind; /* Skip any additional non-options and extend the range of non-options previously skipped. */ while (optind < argc && (argv[optind][0] != '-' || argv[optind][1] == '\0')) optind++; last_nonopt = optind; } /* The special ARGV-element `--' means premature end of options. Skip it like a null option, then exchange with previous non-options as if it were an option, then skip everything else like a non-option. */ if (optind != argc && !strcmp (argv[optind], "--")) { optind++; if (first_nonopt != last_nonopt && last_nonopt != optind) exchange ((char **) argv); else if (first_nonopt == last_nonopt) first_nonopt = optind; last_nonopt = argc; optind = argc; } /* If we have done all the ARGV-elements, stop the scan and back over any non-options that we skipped and permuted. */ if (optind == argc) { /* Set the next-arg-index to point at the non-options that we previously skipped, so the caller will digest them. */ if (first_nonopt != last_nonopt) optind = first_nonopt; return EOF; } /* If we have come to a non-option and did not permute it, either stop the scan or describe it to the caller and pass it by. */ if ((argv[optind][0] != '-' || argv[optind][1] == '\0')) { if (ordering == REQUIRE_ORDER) return EOF; optarg = argv[optind++]; return 1; } /* We have found another option-ARGV-element. Skip the initial punctuation. */ nextchar = (argv[optind] + 1 + (longopts != NULL && argv[optind][1] == '-')); } /* Decode the current option-ARGV-element. */ /* Check whether the ARGV-element is a long option. If long_only and the ARGV-element has the form "-f", where f is a valid short option, don't consider it an abbreviated form of a long option that starts with f. Otherwise there would be no way to give the -f short option. On the other hand, if there's a long option "fubar" and the ARGV-element is "-fu", do consider that an abbreviation of the long option, just like "--fu", and not "-f" with arg "u". This distinction seems to be the most useful approach. */ if (longopts != NULL && (argv[optind][1] == '-' || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1]))))) { char *nameend; const struct option *p; const struct option *pfound = NULL; int exact = 0; int ambig = 0; int indfound; int option_index; for (nameend = nextchar; *nameend && *nameend != '='; nameend++) /* Do nothing. */ ; /* Test all long options for either exact match or abbreviated matches. */ for (p = longopts, option_index = 0; p->name; p++, option_index++) if (!strncmp (p->name, nextchar, nameend - nextchar)) { if (nameend - nextchar == strlen (p->name)) { /* Exact match found. */ pfound = p; indfound = option_index; exact = 1; break; } else if (pfound == NULL) { /* First nonexact match found. */ pfound = p; indfound = option_index; } else /* Second or later nonexact match found. */ ambig = 1; } if (ambig && !exact) { if (opterr) fprintf (stderr, "%s: option `%s' is ambiguous\n", argv[0], argv[optind]); nextchar += strlen (nextchar); optind++; return '?'; } if (pfound != NULL) { option_index = indfound; optind++; if (*nameend) { /* Don't test has_arg with >, because some C compilers don't allow it to be used on enums. */ if (pfound->has_arg) optarg = nameend + 1; else { if (opterr) { if (argv[optind - 1][1] == '-') /* --option */ fprintf (stderr, "%s: option `--%s' doesn't allow an argument\n", argv[0], pfound->name); else /* +option or -option */ fprintf (stderr, "%s: option `%c%s' doesn't allow an argument\n", argv[0], argv[optind - 1][0], pfound->name); } nextchar += strlen (nextchar); return '?'; } } else if (pfound->has_arg == 1) { if (optind < argc) optarg = argv[optind++]; else { if (opterr) fprintf (stderr, "%s: option `%s' requires an argument\n", argv[0], argv[optind - 1]); nextchar += strlen (nextchar); return optstring[0] == ':' ? ':' : '?'; } } nextchar += strlen (nextchar); if (longind != NULL) *longind = option_index; if (pfound->flag) { *(pfound->flag) = pfound->val; return 0; } return pfound->val; } /* Can't find it as a long option. If this is not getopt_long_only, or the option starts with '--' or is not a valid short option, then it's an error. Otherwise interpret it as a short option. */ if (!long_only || argv[optind][1] == '-' || my_index (optstring, *nextchar) == NULL) { if (opterr) { if (argv[optind][1] == '-') /* --option */ fprintf (stderr, "%s: unrecognized option `--%s'\n", argv[0], nextchar); else /* +option or -option */ fprintf (stderr, "%s: unrecognized option `%c%s'\n", argv[0], argv[optind][0], nextchar); } nextchar = (char *) ""; optind++; return '?'; } } /* Look at and handle the next short option-character. */ { char c = *nextchar++; char *temp = my_index (optstring, c); /* Increment `optind' when we start to process its last character. */ if (*nextchar == '\0') ++optind; if (temp == NULL || c == ':') { if (opterr) { if (posixly_correct) /* 1003.2 specifies the format of this message. */ fprintf (stderr, "%s: illegal option -- %c\n", argv[0], c); else fprintf (stderr, "%s: invalid option -- %c\n", argv[0], c); } optopt = c; return '?'; } if (temp[1] == ':') { if (temp[2] == ':') { /* This is an option that accepts an argument optionally. */ if (*nextchar != '\0') { optarg = nextchar; optind++; } else optarg = NULL; nextchar = NULL; } else { /* This is an option that requires an argument. */ if (*nextchar != '\0') { optarg = nextchar; /* If we end this ARGV-element by taking the rest as an arg, we must advance to the next element now. */ optind++; } else if (optind == argc) { if (opterr) { /* 1003.2 specifies the format of this message. */ fprintf (stderr, "%s: option requires an argument -- %c\n", argv[0], c); } optopt = c; if (optstring[0] == ':') c = ':'; else c = '?'; } else /* We already incremented `optind' once; increment it again when taking next ARGV-elt as argument. */ optarg = argv[optind++]; nextchar = NULL; } } return c; } } int getopt (argc, argv, optstring) int argc; char *const *argv; const char *optstring; { return _getopt_internal (argc, argv, optstring, (const struct option *) 0, (int *) 0, 0); } #endif /* _LIBC or not __GNU_LIBRARY__. */ #ifdef TEST /* Compile with -DTEST to make an executable for use in testing the above definition of `getopt'. */ int main (argc, argv) int argc; char **argv; { int c; int digit_optind = 0; while (1) { int this_option_optind = optind ? optind : 1; c = getopt (argc, argv, "abc:d:0123456789"); if (c == EOF) break; switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (digit_optind != 0 && digit_optind != this_option_optind) printf ("digits occur in two different argv-elements.\n"); digit_optind = this_option_optind; printf ("option %c\n", c); break; case 'a': printf ("option a\n"); break; case 'b': printf ("option b\n"); break; case 'c': printf ("option c with value `%s'\n", optarg); break; case '?': break; default: printf ("?? getopt returned character code 0%o ??\n", c); } } if (optind < argc) { printf ("non-option ARGV-elements: "); while (optind < argc) printf ("%s ", argv[optind++]); printf ("\n"); } exit (0); } #endif /* TEST */ /sys/src/ape/cmd/diff/getopt.h 664 sys sys 1367613436 4412 /* Declarations for getopt. Copyright (C) 1989, 1990, 1991, 1992, 1993 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, 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef _GETOPT_H #define _GETOPT_H 1 #ifdef __cplusplus extern "C" { #endif /* For communication from `getopt' to the caller. When `getopt' finds an option that takes an argument, the argument value is returned here. Also, when `ordering' is RETURN_IN_ORDER, each non-option ARGV-element is returned here. */ extern char *optarg; /* Index in ARGV of the next element to be scanned. This is used for communication to and from the caller and for communication between successive calls to `getopt'. On entry to `getopt', zero means this is the first call; initialize. When `getopt' returns EOF, this is the index of the first of the non-option elements that the caller should itself scan. Otherwise, `optind' communicates from one call to the next how much of ARGV has been scanned so far. */ extern int optind; /* Callers store zero here to inhibit the error message `getopt' prints for unrecognized options. */ extern int opterr; /* Set to an option character which was unrecognized. */ extern int optopt; /* Describe the long-named options requested by the application. The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector of `struct option' terminated by an element containing a name which is zero. The field `has_arg' is: no_argument (or 0) if the option does not take an argument, required_argument (or 1) if the option requires an argument, optional_argument (or 2) if the option takes an optional argument. If the field `flag' is not NULL, it points to a variable that is set to the value given in the field `val' when the option is found, but left unchanged if the option is not found. To have a long-named option do something other than set an `int' to a compiled-in constant, such as set a value from `optarg', set the option's `flag' field to zero and its `val' field to a nonzero value (the equivalent single-letter option character, if there is one). For long options that have a zero `flag' field, `getopt' returns the contents of the `val' field. */ struct option { #if __STDC__ const char *name; #else char *name; #endif /* has_arg can't be an enum because some compilers complain about type mismatches in all the code that assumes it is an int. */ int has_arg; int *flag; int val; }; /* Names for the values of the `has_arg' field of `struct option'. */ #define no_argument 0 #define required_argument 1 #define optional_argument 2 #if __STDC__ #if defined(__GNU_LIBRARY__) /* Many other libraries have conflicting prototypes for getopt, with differences in the consts, in stdlib.h. To avoid compilation errors, only prototype getopt for the GNU C library. */ extern int getopt (int argc, char *const *argv, const char *shortopts); #else /* not __GNU_LIBRARY__ */ extern int getopt (); #endif /* not __GNU_LIBRARY__ */ extern int getopt_long (int argc, char *const *argv, const char *shortopts, const struct option *longopts, int *longind); extern int getopt_long_only (int argc, char *const *argv, const char *shortopts, const struct option *longopts, int *longind); /* Internal only. Users should not call this directly. */ extern int _getopt_internal (int argc, char *const *argv, const char *shortopts, const struct option *longopts, int *longind, int long_only); #else /* not __STDC__ */ extern int getopt (); extern int getopt_long (); extern int getopt_long_only (); extern int _getopt_internal (); #endif /* not __STDC__ */ #ifdef __cplusplus } #endif #endif /* _GETOPT_H */ /sys/src/ape/cmd/diff/getopt1.c 664 sys sys 1367613436 4228 /* getopt_long and getopt_long_only entry points for GNU getopt. Copyright (C) 1987, 88, 89, 90, 91, 92, 1993 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, 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifdef HAVE_CONFIG_H #include #endif #include "getopt.h" #ifndef __STDC__ /* This is a separate conditional since some stdc systems reject `defined (const)'. */ #ifndef const #define const #endif #endif #include /* Comment out all this code if we are using the GNU C Library, and are not actually compiling the library itself. This code is part of the GNU C Library, but also included in many other GNU distributions. Compiling and linking in this code is a waste when using the GNU C library (especially if it is a shared library). Rather than having every GNU program understand `configure --with-gnu-libc' and omit the object files, it is simpler to just do this in the source for each such file. */ #if defined (_LIBC) || !defined (__GNU_LIBRARY__) /* This needs to come after some library #include to get __GNU_LIBRARY__ defined. */ #ifdef __GNU_LIBRARY__ #include #else char *getenv (); #endif #ifndef NULL #define NULL 0 #endif int getopt_long (argc, argv, options, long_options, opt_index) int argc; char *const *argv; const char *options; const struct option *long_options; int *opt_index; { return _getopt_internal (argc, argv, options, long_options, opt_index, 0); } /* Like getopt_long, but '-' as well as '--' can indicate a long option. If an option that starts with '-' (not '--') doesn't match a long option, but does match a short option, it is parsed as a short option instead. */ int getopt_long_only (argc, argv, options, long_options, opt_index) int argc; char *const *argv; const char *options; const struct option *long_options; int *opt_index; { return _getopt_internal (argc, argv, options, long_options, opt_index, 1); } #endif /* _LIBC or not __GNU_LIBRARY__. */ #ifdef TEST #include int main (argc, argv) int argc; char **argv; { int c; int digit_optind = 0; while (1) { int this_option_optind = optind ? optind : 1; int option_index = 0; static struct option long_options[] = { {"add", 1, 0, 0}, {"append", 0, 0, 0}, {"delete", 1, 0, 0}, {"verbose", 0, 0, 0}, {"create", 0, 0, 0}, {"file", 1, 0, 0}, {0, 0, 0, 0} }; c = getopt_long (argc, argv, "abc:d:0123456789", long_options, &option_index); if (c == EOF) break; switch (c) { case 0: printf ("option %s", long_options[option_index].name); if (optarg) printf (" with arg %s", optarg); printf ("\n"); break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (digit_optind != 0 && digit_optind != this_option_optind) printf ("digits occur in two different argv-elements.\n"); digit_optind = this_option_optind; printf ("option %c\n", c); break; case 'a': printf ("option a\n"); break; case 'b': printf ("option b\n"); break; case 'c': printf ("option c with value `%s'\n", optarg); break; case 'd': printf ("option d with value `%s'\n", optarg); break; case '?': break; default: printf ("?? getopt returned character code 0%o ??\n", c); } } if (optind < argc) { printf ("non-option ARGV-elements: "); while (optind < argc) printf ("%s ", argv[optind++]); printf ("\n"); } exit (0); } #endif /* TEST */ /sys/src/ape/cmd/diff/ifdef.c 664 sys sys 1367613436 10041 /* #ifdef-format output routines for GNU DIFF. Copyright (C) 1989, 1991, 1992, 1993, 1994 Free Software Foundation, Inc. This file is part of GNU DIFF. GNU DIFF is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to anyone for the consequences of using it or for whether it serves any particular purpose or works at all, unless he says so in writing. Refer to the GNU DIFF General Public License for full details. Everyone is granted permission to copy, modify and redistribute GNU DIFF, but only under the conditions described in the GNU DIFF General Public License. A copy of this license is supposed to have been given to you along with GNU DIFF so you can know your rights and responsibilities. It should be in a file named COPYING. Among other things, the copyright notice and this notice must be preserved on all copies. */ #include "diff.h" struct group { struct file_data const *file; int from, upto; /* start and limit lines for this group of lines */ }; static char *format_group PARAMS((FILE *, char *, int, struct group const *)); static char *scan_char_literal PARAMS((char *, int *)); static char *scan_printf_spec PARAMS((char *)); static int groups_letter_value PARAMS((struct group const *, int)); static void format_ifdef PARAMS((char *, int, int, int, int)); static void print_ifdef_hunk PARAMS((struct change *)); static void print_ifdef_lines PARAMS((FILE *, char *, struct group const *)); static int next_line; /* Print the edit-script SCRIPT as a merged #ifdef file. */ void print_ifdef_script (script) struct change *script; { next_line = - files[0].prefix_lines; print_script (script, find_change, print_ifdef_hunk); if (next_line < files[0].valid_lines) { begin_output (); format_ifdef (group_format[UNCHANGED], next_line, files[0].valid_lines, next_line - files[0].valid_lines + files[1].valid_lines, files[1].valid_lines); } } /* Print a hunk of an ifdef diff. This is a contiguous portion of a complete edit script, describing changes in consecutive lines. */ static void print_ifdef_hunk (hunk) struct change *hunk; { int first0, last0, first1, last1, deletes, inserts; char *format; /* Determine range of line numbers involved in each file. */ analyze_hunk (hunk, &first0, &last0, &first1, &last1, &deletes, &inserts); if (inserts) format = deletes ? group_format[CHANGED] : group_format[NEW]; else if (deletes) format = group_format[OLD]; else return; begin_output (); /* Print lines up to this change. */ if (next_line < first0) format_ifdef (group_format[UNCHANGED], next_line, first0, next_line - first0 + first1, first1); /* Print this change. */ next_line = last0 + 1; format_ifdef (format, first0, next_line, first1, last1 + 1); } /* Print a set of lines according to FORMAT. Lines BEG0 up to END0 are from the first file; lines BEG1 up to END1 are from the second file. */ static void format_ifdef (format, beg0, end0, beg1, end1) char *format; int beg0, end0, beg1, end1; { struct group groups[2]; groups[0].file = &files[0]; groups[0].from = beg0; groups[0].upto = end0; groups[1].file = &files[1]; groups[1].from = beg1; groups[1].upto = end1; format_group (outfile, format, '\0', groups); } /* Print to file OUT a set of lines according to FORMAT. The format ends at the first free instance of ENDCHAR. Yield the address of the terminating character. GROUPS specifies which lines to print. If OUT is zero, do not actually print anything; just scan the format. */ static char * format_group (out, format, endchar, groups) register FILE *out; char *format; int endchar; struct group const *groups; { register char c; register char *f = format; while ((c = *f) != endchar && c != 0) { f++; if (c == '%') { char *spec = f; switch ((c = *f++)) { case '%': break; case '(': /* Print if-then-else format e.g. `%(n=1?thenpart:elsepart)'. */ { int i, value[2]; FILE *thenout, *elseout; for (i = 0; i < 2; i++) { unsigned char f0 = f[0]; if (ISDIGIT (f0)) { value[i] = atoi (f); while (ISDIGIT ((unsigned char) *++f)) continue; } else { value[i] = groups_letter_value (groups, f0); if (value[i] < 0) goto bad_format; f++; } if (*f++ != "=?"[i]) goto bad_format; } if (value[0] == value[1]) thenout = out, elseout = 0; else thenout = 0, elseout = out; f = format_group (thenout, f, ':', groups); if (*f) { f = format_group (elseout, f + 1, ')', groups); if (*f) f++; } } continue; case '<': /* Print lines deleted from first file. */ print_ifdef_lines (out, line_format[OLD], &groups[0]); continue; case '=': /* Print common lines. */ print_ifdef_lines (out, line_format[UNCHANGED], &groups[0]); continue; case '>': /* Print lines inserted from second file. */ print_ifdef_lines (out, line_format[NEW], &groups[1]); continue; default: { int value; char *speclim; f = scan_printf_spec (spec); if (!f) goto bad_format; speclim = f; c = *f++; switch (c) { case '\'': f = scan_char_literal (f, &value); if (!f) goto bad_format; break; default: value = groups_letter_value (groups, c); if (value < 0) goto bad_format; break; } if (out) { /* Temporarily replace e.g. "%3dnx" with "%3d\0x". */ *speclim = 0; fprintf (out, spec - 1, value); /* Undo the temporary replacement. */ *speclim = c; } } continue; bad_format: c = '%'; f = spec; break; } } if (out) putc (c, out); } return f; } /* For the line group pair G, return the number corresponding to LETTER. Return -1 if LETTER is not a group format letter. */ static int groups_letter_value (g, letter) struct group const *g; int letter; { if (ISUPPER (letter)) { g++; letter = tolower (letter); } switch (letter) { case 'e': return translate_line_number (g->file, g->from) - 1; case 'f': return translate_line_number (g->file, g->from); case 'l': return translate_line_number (g->file, g->upto) - 1; case 'm': return translate_line_number (g->file, g->upto); case 'n': return g->upto - g->from; default: return -1; } } /* Print to file OUT, using FORMAT to print the line group GROUP. But do nothing if OUT is zero. */ static void print_ifdef_lines (out, format, group) register FILE *out; char *format; struct group const *group; { struct file_data const *file = group->file; char const * const *linbuf = file->linbuf; int from = group->from, upto = group->upto; if (!out) return; /* If possible, use a single fwrite; it's faster. */ if (!tab_expand_flag && format[0] == '%') { if (format[1] == 'l' && format[2] == '\n' && !format[3]) { fwrite (linbuf[from], sizeof (char), linbuf[upto] + (linbuf[upto][-1] != '\n') - linbuf[from], out); return; } if (format[1] == 'L' && !format[2]) { fwrite (linbuf[from], sizeof (char), linbuf[upto] - linbuf[from], out); return; } } for (; from < upto; from++) { register char c; register char *f = format; while ((c = *f++) != 0) { if (c == '%') { char *spec = f; switch ((c = *f++)) { case '%': break; case 'l': output_1_line (linbuf[from], linbuf[from + 1] - (linbuf[from + 1][-1] == '\n'), 0, 0); continue; case 'L': output_1_line (linbuf[from], linbuf[from + 1], 0, 0); continue; default: { int value; char *speclim; f = scan_printf_spec (spec); if (!f) goto bad_format; speclim = f; c = *f++; switch (c) { case '\'': f = scan_char_literal (f, &value); if (!f) goto bad_format; break; case 'n': value = translate_line_number (file, from); break; default: goto bad_format; } /* Temporarily replace e.g. "%3dnx" with "%3d\0x". */ *speclim = 0; fprintf (out, spec - 1, value); /* Undo the temporary replacement. */ *speclim = c; } continue; bad_format: c = '%'; f = spec; break; } } putc (c, out); } } } /* Scan the character literal represented in the string LIT; LIT points just after the initial apostrophe. Put the literal's value into *INTPTR. Yield the address of the first character after the closing apostrophe, or zero if the literal is ill-formed. */ static char * scan_char_literal (lit, intptr) char *lit; int *intptr; { register char *p = lit; int value, digits; char c = *p++; switch (c) { case 0: case '\'': return 0; case '\\': value = 0; while ((c = *p++) != '\'') { unsigned digit = c - '0'; if (8 <= digit) return 0; value = 8 * value + digit; } digits = p - lit - 2; if (! (1 <= digits && digits <= 3)) return 0; break; default: value = c; if (*p++ != '\'') return 0; break; } *intptr = value; return p; } /* Scan optional printf-style SPEC of the form `-*[0-9]*(.[0-9]*)?[cdoxX]'. Return the address of the character following SPEC, or zero if failure. */ static char * scan_printf_spec (spec) register char *spec; { register unsigned char c; while ((c = *spec++) == '-') continue; while (ISDIGIT (c)) c = *spec++; if (c == '.') while (ISDIGIT (c = *spec++)) continue; switch (c) { case 'c': case 'd': case 'o': case 'x': case 'X': return spec; default: return 0; } } /sys/src/ape/cmd/diff/install-sh 775 sys sys 1367613436 4771 #!/bin/sh # # install - install a program, script, or datafile # This comes from X11R5. # # Calling this script install-sh is preferred over install.sh, to prevent # `make' implicit rules from creating a file called install from it # when there is no Makefile. # # 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}" mkdirprog="${MKDIRPROG-mkdir}" tranformbasename="" transform_arg="" instcmd="$mvprog" chmodcmd="$chmodprog 0755" chowncmd="" chgrpcmd="" stripcmd="" rmcmd="$rmprog -f" mvcmd="$mvprog" src="" dst="" dir_arg="" while [ x"$1" != x ]; do case $1 in -c) instcmd="$cpprog" shift continue;; -d) dir_arg=true 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;; -t=*) transformarg=`echo $1 | sed 's/-t=//'` shift continue;; -b=*) transformbasename=`echo $1 | sed 's/-b=//'` shift continue;; *) if [ x"$src" = x ] then src=$1 else # this colon is to work around a 386BSD /bin/sh bug : dst=$1 fi shift continue;; esac done if [ x"$src" = x ] then echo "install: no input file specified" exit 1 else true fi if [ x"$dir_arg" != x ]; then dst=$src src="" if [ -d $dst ]; then instcmd=: else instcmd=mkdir fi else # Waiting for this to be detected by the "$instcmd $src $dsttmp" command # might cause directories to be created, which would be especially bad # if $src (and thus $dsttmp) contains '*'. if [ -f $src -o -d $src ] then true else echo "install: $src does not exist" exit 1 fi if [ x"$dst" = x ] then echo "install: no destination specified" exit 1 else true 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` else true fi fi ## this sed command emulates the dirname command dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` # Make sure that the destination directory exists. # this part is taken from Noah Friedman's mkinstalldirs script # Skip lots of stat calls in the usual case. if [ ! -d "$dstdir" ]; then defaultIFS=' ' IFS="${IFS-${defaultIFS}}" oIFS="${IFS}" # Some sh's can't handle IFS=/ for some reason. IFS='%' set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` IFS="${oIFS}" pathcomp='' while [ $# -ne 0 ] ; do pathcomp="${pathcomp}${1}" shift if [ ! -d "${pathcomp}" ] ; then $mkdirprog "${pathcomp}" else true fi pathcomp="${pathcomp}/" done fi if [ x"$dir_arg" != x ] then $doit $instcmd $dst && if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi && if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi && if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi && if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi else # If we're going to rename the final executable, determine the name now. if [ x"$transformarg" = x ] then dstfile=`basename $dst` else dstfile=`basename $dst $transformbasename | sed $transformarg`$transformbasename fi # don't allow the sed command to completely eliminate the filename if [ x"$dstfile" = x ] then dstfile=`basename $dst` else true fi # Make a temp file name in the proper directory. dsttmp=$dstdir/#inst.$$# # Move or copy the file name to the temp name $doit $instcmd $src $dsttmp && trap "rm -f ${dsttmp}" 0 && # and set any options; do chmod last to preserve setuid bits # If any of these fail, we abort the whole thing. If we want to # ignore errors from any of these, just make sure not to ignore # errors from the above "$doit $instcmd $src $dsttmp" command. if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi && if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi && if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi && if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi && # Now rename the file to the real destination. $doit $rmcmd -f $dstdir/$dstfile && $doit $mvcmd $dsttmp $dstdir/$dstfile fi && exit 0 /sys/src/ape/cmd/diff/io.c 664 sys sys 1367613436 20341 /* File I/O for GNU DIFF. Copyright (C) 1988, 1989, 1992, 1993, 1994 Free Software Foundation, Inc. This file is part of GNU DIFF. GNU DIFF 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. GNU DIFF 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 GNU DIFF; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "diff.h" /* Rotate a value n bits to the left. */ #define UINT_BIT (sizeof (unsigned) * CHAR_BIT) #define ROL(v, n) ((v) << (n) | (v) >> (UINT_BIT - (n))) /* Given a hash value and a new character, return a new hash value. */ #define HASH(h, c) ((c) + ROL (h, 7)) /* Guess remaining number of lines from number N of lines so far, size S so far, and total size T. */ #define GUESS_LINES(n,s,t) (((t) - (s)) / ((n) < 10 ? 32 : (s) / ((n)-1)) + 5) /* Type used for fast prefix comparison in find_identical_ends. */ #ifndef word #define word int #endif /* Lines are put into equivalence classes (of lines that match in line_cmp). Each equivalence class is represented by one of these structures, but only while the classes are being computed. Afterward, each class is represented by a number. */ struct equivclass { int next; /* Next item in this bucket. */ unsigned hash; /* Hash of lines in this class. */ char const *line; /* A line that fits this class. */ size_t length; /* That line's length, not counting its newline. */ }; /* Hash-table: array of buckets, each being a chain of equivalence classes. buckets[-1] is reserved for incomplete lines. */ static int *buckets; /* Number of buckets in the hash table array, not counting buckets[-1]. */ static int nbuckets; /* Array in which the equivalence classes are allocated. The bucket-chains go through the elements in this array. The number of an equivalence class is its index in this array. */ static struct equivclass *equivs; /* Index of first free element in the array `equivs'. */ static int equivs_index; /* Number of elements allocated in the array `equivs'. */ static int equivs_alloc; static void find_and_hash_each_line PARAMS((struct file_data *)); static void find_identical_ends PARAMS((struct file_data[])); static void prepare_text_end PARAMS((struct file_data *)); /* Check for binary files and compare them for exact identity. */ /* Return 1 if BUF contains a non text character. SIZE is the number of characters in BUF. */ #define binary_file_p(buf, size) (memchr (buf, '\0', size) != 0) /* Get ready to read the current file. Return nonzero if SKIP_TEST is zero, and if it appears to be a binary file. */ int sip (current, skip_test) struct file_data *current; int skip_test; { /* If we have a nonexistent file at this stage, treat it as empty. */ if (current->desc < 0) { /* Leave room for a sentinel. */ current->bufsize = sizeof (word); current->buffer = xmalloc (current->bufsize); } else { current->bufsize = STAT_BLOCKSIZE (current->stat); current->buffer = xmalloc (current->bufsize); if (! skip_test) { /* Check first part of file to see if it's a binary file. */ #if HAVE_SETMODE int oldmode = setmode (current->desc, O_BINARY); #endif size_t n = read (current->desc, current->buffer, current->bufsize); if (n == -1) pfatal_with_name (current->name); current->buffered_chars = n; #if HAVE_SETMODE if (oldmode != O_BINARY) { if (lseek (current->desc, - (off_t) n, SEEK_CUR) == -1) pfatal_with_name (current->name); setmode (current->desc, oldmode); current->buffered_chars = 0; } #endif return binary_file_p (current->buffer, n); } } current->buffered_chars = 0; return 0; } /* Slurp the rest of the current file completely into memory. */ void slurp (current) struct file_data *current; { size_t cc; if (current->desc < 0) /* The file is nonexistent. */ ; else if (S_ISREG (current->stat.st_mode)) { /* It's a regular file; slurp in the rest all at once. */ /* Get the size out of the stat block. Allocate enough room for appended newline and sentinel. */ cc = current->stat.st_size + 1 + sizeof (word); if (current->bufsize < cc) { current->bufsize = cc; current->buffer = xrealloc (current->buffer, cc); } if (current->buffered_chars < current->stat.st_size) { cc = read (current->desc, current->buffer + current->buffered_chars, current->stat.st_size - current->buffered_chars); if (cc == -1) pfatal_with_name (current->name); current->buffered_chars += cc; } } /* It's not a regular file; read it, growing the buffer as needed. */ else if (always_text_flag || current->buffered_chars != 0) { for (;;) { if (current->buffered_chars == current->bufsize) { current->bufsize = current->bufsize * 2; current->buffer = xrealloc (current->buffer, current->bufsize); } cc = read (current->desc, current->buffer + current->buffered_chars, current->bufsize - current->buffered_chars); if (cc == 0) break; if (cc == -1) pfatal_with_name (current->name); current->buffered_chars += cc; } /* Allocate just enough room for appended newline and sentinel. */ current->bufsize = current->buffered_chars + 1 + sizeof (word); current->buffer = xrealloc (current->buffer, current->bufsize); } } /* Split the file into lines, simultaneously computing the equivalence class for each line. */ static void find_and_hash_each_line (current) struct file_data *current; { unsigned h; unsigned char const *p = (unsigned char const *) current->prefix_end; unsigned char c; int i, *bucket; size_t length; /* Cache often-used quantities in local variables to help the compiler. */ char const **linbuf = current->linbuf; int alloc_lines = current->alloc_lines; int line = 0; int linbuf_base = current->linbuf_base; int *cureqs = (int *) xmalloc (alloc_lines * sizeof (int)); struct equivclass *eqs = equivs; int eqs_index = equivs_index; int eqs_alloc = equivs_alloc; char const *suffix_begin = current->suffix_begin; char const *bufend = current->buffer + current->buffered_chars; int use_line_cmp = ignore_some_line_changes; while ((char const *) p < suffix_begin) { char const *ip = (char const *) p; /* Compute the equivalence class for this line. */ h = 0; /* Hash this line until we find a newline. */ if (ignore_case_flag) { if (ignore_all_space_flag) while ((c = *p++) != '\n') { if (! ISSPACE (c)) h = HASH (h, ISUPPER (c) ? tolower (c) : c); } else if (ignore_space_change_flag) while ((c = *p++) != '\n') { if (ISSPACE (c)) { for (;;) { c = *p++; if (!ISSPACE (c)) break; if (c == '\n') goto hashing_done; } h = HASH (h, ' '); } /* C is now the first non-space. */ h = HASH (h, ISUPPER (c) ? tolower (c) : c); } else while ((c = *p++) != '\n') h = HASH (h, ISUPPER (c) ? tolower (c) : c); } else { if (ignore_all_space_flag) while ((c = *p++) != '\n') { if (! ISSPACE (c)) h = HASH (h, c); } else if (ignore_space_change_flag) while ((c = *p++) != '\n') { if (ISSPACE (c)) { for (;;) { c = *p++; if (!ISSPACE (c)) break; if (c == '\n') goto hashing_done; } h = HASH (h, ' '); } /* C is now the first non-space. */ h = HASH (h, c); } else while ((c = *p++) != '\n') h = HASH (h, c); } hashing_done:; bucket = &buckets[h % nbuckets]; length = (char const *) p - ip - 1; if ((char const *) p == bufend && current->missing_newline && ROBUST_OUTPUT_STYLE (output_style)) { /* This line is incomplete. If this is significant, put the line into bucket[-1]. */ if (! (ignore_space_change_flag | ignore_all_space_flag)) bucket = &buckets[-1]; /* Omit the inserted newline when computing linbuf later. */ p--; bufend = suffix_begin = (char const *) p; } for (i = *bucket; ; i = eqs[i].next) if (!i) { /* Create a new equivalence class in this bucket. */ i = eqs_index++; if (i == eqs_alloc) eqs = (struct equivclass *) xrealloc (eqs, (eqs_alloc*=2) * sizeof(*eqs)); eqs[i].next = *bucket; eqs[i].hash = h; eqs[i].line = ip; eqs[i].length = length; *bucket = i; break; } else if (eqs[i].hash == h) { char const *eqline = eqs[i].line; /* Reuse existing equivalence class if the lines are identical. This detects the common case of exact identity faster than complete comparison would. */ if (eqs[i].length == length && memcmp (eqline, ip, length) == 0) break; /* Reuse existing class if line_cmp reports the lines equal. */ if (use_line_cmp && line_cmp (eqline, ip) == 0) break; } /* Maybe increase the size of the line table. */ if (line == alloc_lines) { /* Double (alloc_lines - linbuf_base) by adding to alloc_lines. */ alloc_lines = 2 * alloc_lines - linbuf_base; cureqs = (int *) xrealloc (cureqs, alloc_lines * sizeof (*cureqs)); linbuf = (char const **) xrealloc (linbuf + linbuf_base, (alloc_lines - linbuf_base) * sizeof (*linbuf)) - linbuf_base; } linbuf[line] = ip; cureqs[line] = i; ++line; } current->buffered_lines = line; for (i = 0; ; i++) { /* Record the line start for lines in the suffix that we care about. Record one more line start than lines, so that we can compute the length of any buffered line. */ if (line == alloc_lines) { /* Double (alloc_lines - linbuf_base) by adding to alloc_lines. */ alloc_lines = 2 * alloc_lines - linbuf_base; linbuf = (char const **) xrealloc (linbuf + linbuf_base, (alloc_lines - linbuf_base) * sizeof (*linbuf)) - linbuf_base; } linbuf[line] = (char const *) p; if ((char const *) p == bufend) break; if (context <= i && no_diff_means_no_output) break; line++; while (*p++ != '\n') ; } /* Done with cache in local variables. */ current->linbuf = linbuf; current->valid_lines = line; current->alloc_lines = alloc_lines; current->equivs = cureqs; equivs = eqs; equivs_alloc = eqs_alloc; equivs_index = eqs_index; } /* Prepare the end of the text. Make sure it's initialized. Make sure text ends in a newline, but remember that we had to add one. */ static void prepare_text_end (current) struct file_data *current; { size_t buffered_chars = current->buffered_chars; char *p = current->buffer; if (buffered_chars == 0 || p[buffered_chars - 1] == '\n') current->missing_newline = 0; else { p[buffered_chars++] = '\n'; current->buffered_chars = buffered_chars; current->missing_newline = 1; } /* Don't use uninitialized storage when planting or using sentinels. */ if (p) bzero (p + buffered_chars, sizeof (word)); } /* Given a vector of two file_data objects, find the identical prefixes and suffixes of each object. */ static void find_identical_ends (filevec) struct file_data filevec[]; { word *w0, *w1; char *p0, *p1, *buffer0, *buffer1; char const *end0, *beg0; char const **linbuf0, **linbuf1; int i, lines; size_t n0, n1, tem; int alloc_lines0, alloc_lines1; int buffered_prefix, prefix_count, prefix_mask; slurp (&filevec[0]); if (filevec[0].desc != filevec[1].desc) slurp (&filevec[1]); else { filevec[1].buffer = filevec[0].buffer; filevec[1].bufsize = filevec[0].bufsize; filevec[1].buffered_chars = filevec[0].buffered_chars; } for (i = 0; i < 2; i++) prepare_text_end (&filevec[i]); /* Find identical prefix. */ p0 = buffer0 = filevec[0].buffer; p1 = buffer1 = filevec[1].buffer; n0 = filevec[0].buffered_chars; n1 = filevec[1].buffered_chars; if (p0 == p1) /* The buffers are the same; sentinels won't work. */ p0 = p1 += n1; else { /* Insert end sentinels, in this case characters that are guaranteed to make the equality test false, and thus terminate the loop. */ if (n0 < n1) p0[n0] = ~p1[n0]; else p1[n1] = ~p0[n1]; /* Loop until first mismatch, or to the sentinel characters. */ /* Compare a word at a time for speed. */ w0 = (word *) p0; w1 = (word *) p1; while (*w0++ == *w1++) ; --w0, --w1; /* Do the last few bytes of comparison a byte at a time. */ p0 = (char *) w0; p1 = (char *) w1; while (*p0++ == *p1++) ; --p0, --p1; /* Don't mistakenly count missing newline as part of prefix. */ if (ROBUST_OUTPUT_STYLE (output_style) && (buffer0 + n0 - filevec[0].missing_newline < p0) != (buffer1 + n1 - filevec[1].missing_newline < p1)) --p0, --p1; } /* Now P0 and P1 point at the first nonmatching characters. */ /* Skip back to last line-beginning in the prefix, and then discard up to HORIZON_LINES lines from the prefix. */ i = horizon_lines; while (p0 != buffer0 && (p0[-1] != '\n' || i--)) --p0, --p1; /* Record the prefix. */ filevec[0].prefix_end = p0; filevec[1].prefix_end = p1; /* Find identical suffix. */ /* P0 and P1 point beyond the last chars not yet compared. */ p0 = buffer0 + n0; p1 = buffer1 + n1; if (! ROBUST_OUTPUT_STYLE (output_style) || filevec[0].missing_newline == filevec[1].missing_newline) { end0 = p0; /* Addr of last char in file 0. */ /* Get value of P0 at which we should stop scanning backward: this is when either P0 or P1 points just past the last char of the identical prefix. */ beg0 = filevec[0].prefix_end + (n0 < n1 ? 0 : n0 - n1); /* Scan back until chars don't match or we reach that point. */ while (p0 != beg0) if (*--p0 != *--p1) { /* Point at the first char of the matching suffix. */ ++p0, ++p1; beg0 = p0; break; } /* Are we at a line-beginning in both files? If not, add the rest of this line to the main body. Discard up to HORIZON_LINES lines from the identical suffix. Also, discard one extra line, because shift_boundaries may need it. */ i = horizon_lines + !((buffer0 == p0 || p0[-1] == '\n') && (buffer1 == p1 || p1[-1] == '\n')); while (i-- && p0 != end0) while (*p0++ != '\n') ; p1 += p0 - beg0; } /* Record the suffix. */ filevec[0].suffix_begin = p0; filevec[1].suffix_begin = p1; /* Calculate number of lines of prefix to save. prefix_count == 0 means save the whole prefix; we need this with for options like -D that output the whole file. We also need it for options like -F that output some preceding line; at least we will need to find the last few lines, but since we don't know how many, it's easiest to find them all. Otherwise, prefix_count != 0. Save just prefix_count lines at start of the line buffer; they'll be moved to the proper location later. Handle 1 more line than the context says (because we count 1 too many), rounded up to the next power of 2 to speed index computation. */ if (no_diff_means_no_output && ! function_regexp_list) { for (prefix_count = 1; prefix_count < context + 1; prefix_count *= 2) ; prefix_mask = prefix_count - 1; alloc_lines0 = prefix_count + GUESS_LINES (0, 0, p0 - filevec[0].prefix_end) + context; } else { prefix_count = 0; prefix_mask = ~0; alloc_lines0 = GUESS_LINES (0, 0, n0); } lines = 0; linbuf0 = (char const **) xmalloc (alloc_lines0 * sizeof (*linbuf0)); /* If the prefix is needed, find the prefix lines. */ if (! (no_diff_means_no_output && filevec[0].prefix_end == p0 && filevec[1].prefix_end == p1)) { p0 = buffer0; end0 = filevec[0].prefix_end; while (p0 != end0) { int l = lines++ & prefix_mask; if (l == alloc_lines0) linbuf0 = (char const **) xrealloc (linbuf0, (alloc_lines0 *= 2) * sizeof(*linbuf0)); linbuf0[l] = p0; while (*p0++ != '\n') ; } } buffered_prefix = prefix_count && context < lines ? context : lines; /* Allocate line buffer 1. */ tem = prefix_count ? filevec[1].suffix_begin - buffer1 : n1; alloc_lines1 = (buffered_prefix + GUESS_LINES (lines, filevec[1].prefix_end - buffer1, tem) + context); linbuf1 = (char const **) xmalloc (alloc_lines1 * sizeof (*linbuf1)); if (buffered_prefix != lines) { /* Rotate prefix lines to proper location. */ for (i = 0; i < buffered_prefix; i++) linbuf1[i] = linbuf0[(lines - context + i) & prefix_mask]; for (i = 0; i < buffered_prefix; i++) linbuf0[i] = linbuf1[i]; } /* Initialize line buffer 1 from line buffer 0. */ for (i = 0; i < buffered_prefix; i++) linbuf1[i] = linbuf0[i] - buffer0 + buffer1; /* Record the line buffer, adjusted so that linbuf*[0] points at the first differing line. */ filevec[0].linbuf = linbuf0 + buffered_prefix; filevec[1].linbuf = linbuf1 + buffered_prefix; filevec[0].linbuf_base = filevec[1].linbuf_base = - buffered_prefix; filevec[0].alloc_lines = alloc_lines0 - buffered_prefix; filevec[1].alloc_lines = alloc_lines1 - buffered_prefix; filevec[0].prefix_lines = filevec[1].prefix_lines = lines; } /* Largest primes less than some power of two, for nbuckets. Values range from useful to preposterous. If one of these numbers isn't prime after all, don't blame it on me, blame it on primes (6) . . . */ static int const primes[] = { 509, 1021, 2039, 4093, 8191, 16381, 32749, #if 32767 < INT_MAX 65521, 131071, 262139, 524287, 1048573, 2097143, 4194301, 8388593, 16777213, 33554393, 67108859, /* Preposterously large . . . */ 134217689, 268435399, 536870909, 1073741789, 2147483647, #endif 0 }; /* Given a vector of two file_data objects, read the file associated with each one, and build the table of equivalence classes. Return 1 if either file appears to be a binary file. If PRETEND_BINARY is nonzero, pretend they are binary regardless. */ int read_files (filevec, pretend_binary) struct file_data filevec[]; int pretend_binary; { int i; int skip_test = always_text_flag | pretend_binary; int appears_binary = pretend_binary | sip (&filevec[0], skip_test); if (filevec[0].desc != filevec[1].desc) appears_binary |= sip (&filevec[1], skip_test | appears_binary); else { filevec[1].buffer = filevec[0].buffer; filevec[1].bufsize = filevec[0].bufsize; filevec[1].buffered_chars = filevec[0].buffered_chars; } if (appears_binary) { #if HAVE_SETMODE setmode (filevec[0].desc, O_BINARY); setmode (filevec[1].desc, O_BINARY); #endif return 1; } find_identical_ends (filevec); equivs_alloc = filevec[0].alloc_lines + filevec[1].alloc_lines + 1; equivs = (struct equivclass *) xmalloc (equivs_alloc * sizeof (struct equivclass)); /* Equivalence class 0 is permanently safe for lines that were not hashed. Real equivalence classes start at 1. */ equivs_index = 1; for (i = 0; primes[i] < equivs_alloc / 3; i++) if (! primes[i]) abort (); nbuckets = primes[i]; buckets = (int *) xmalloc ((nbuckets + 1) * sizeof (*buckets)); bzero (buckets++, (nbuckets + 1) * sizeof (*buckets)); for (i = 0; i < 2; i++) find_and_hash_each_line (&filevec[i]); filevec[0].equiv_max = filevec[1].equiv_max = equivs_index; free (equivs); free (buckets - 1); return 0; } /sys/src/ape/cmd/diff/mkfile 664 sys sys 1367613436 557 ", &files[1].linbuf[i]); } /sys/src/ape/cmd/diff/prepend_args.c 664 sys sys 1367613436 2538 /* prepend_args.c - utilility programs for manpiulating argv[] Copyright (C) 1999 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* $FreeBSD: src/contrib/diff/prepend_args.c,v 1.1 1999/11/26 02:51:44 obrien Exp $ */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include "system.h" #include "prepend_args.h" #include "diff.h" /* Find the white-space-separated options specified by OPTIONS, and using BUF to store copies of these options, set ARGV[0], ARGV[1], etc. to the option copies. Return the number N of options found. Do not set ARGV[N] to NULL. If ARGV is NULL, do not store ARGV[0] etc. Backslash can be used to escape whitespace (and backslashes). */ static int prepend_args (options, buf, argv) char const *options; char *buf; char **argv; { char const *o = options; char *b = buf; int n = 0; for (;;) { while (ISSPACE ((unsigned char) *o)) o++; if (!*o) return n; if (argv) argv[n] = b; n++; do if ((*b++ = *o++) == '\\' && *o) b[-1] = *o++; while (*o && ! ISSPACE ((unsigned char) *o)); *b++ = '\0'; } } /* Prepend the whitespace-separated options in OPTIONS to the argument vector of a main program with argument count *PARGC and argument vector *PARGV. */ void prepend_default_options (options, pargc, pargv) char const *options; int *pargc; char ***pargv; { if (options) { char *buf = xmalloc (strlen (options) + 1); int prepended = prepend_args (options, buf, (char **) NULL); int argc = *pargc; char * const *argv = *pargv; char **pp = (char **) xmalloc ((prepended + argc + 1) * sizeof *pp); *pargc = prepended + argc; *pargv = pp; *pp++ = *argv++; pp += prepend_args (options, buf, pp); while ((*pp++ = *argv++)) continue; } } /sys/src/ape/cmd/diff/prepend_args.h 664 sys sys 1367613436 979 /* prepend_args.h - utilility programs for manpiulating argv[] Copyright (C) 1999 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* $FreeBSD: src/contrib/diff/prepend_args.h,v 1.1 1999/11/26 02:51:44 obrien Exp $ */ void prepend_default_options PARAMS ((char const *, int *, char ***)); /sys/src/ape/cmd/diff/regex.c 664 sys sys 1367613436 186345 /* Extended regular expression matching and search library, version 0.12. (Implements POSIX draft P10003.2/D11.2, except for internationalization features.) Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* AIX requires this to be the first thing in the file. */ #if defined (_AIX) && !defined (REGEX_MALLOC) #pragma alloca #endif #undef _GNU_SOURCE #define _GNU_SOURCE #ifdef emacs /* Converts the pointer to the char to BEG-based offset from the start. */ #define PTR_TO_OFFSET(d) \ POS_AS_IN_BUFFER (MATCHING_IN_FIRST_STRING \ ? (d) - string1 : (d) - (string2 - size1)) #define POS_AS_IN_BUFFER(p) ((p) + (NILP (re_match_object) || BUFFERP (re_match_object))) #else #define PTR_TO_OFFSET(d) 0 #endif #include "config.h" /* We need this for `regex.h', and perhaps for the Emacs include files. */ #include /* This is for other GNU distributions with internationalized messages. */ #if HAVE_LIBINTL_H || defined (_LIBC) # include #else # define gettext(msgid) (msgid) #endif #ifndef gettext_noop /* This define is so xgettext can find the internationalizable strings. */ #define gettext_noop(String) String #endif /* The `emacs' switch turns on certain matching commands that make sense only in Emacs. */ #ifdef emacs #include "lisp.h" #include "buffer.h" /* Make syntax table lookup grant data in gl_state. */ #define SYNTAX_ENTRY_VIA_PROPERTY #include "syntax.h" #include "charset.h" #include "category.h" #define malloc xmalloc #define realloc xrealloc #define free xfree #else /* not emacs */ /* If we are not linking with Emacs proper, we can't use the relocating allocator even if config.h says that we can. */ #undef REL_ALLOC #if defined (STDC_HEADERS) || defined (_LIBC) #include #else char *malloc (); char *realloc (); #endif /* When used in Emacs's lib-src, we need to get bzero and bcopy somehow. If nothing else has been done, use the method below. */ #ifdef INHIBIT_STRING_HEADER #if !(defined (HAVE_BZERO) && defined (HAVE_BCOPY)) #if !defined (bzero) && !defined (bcopy) #undef INHIBIT_STRING_HEADER #endif #endif #endif /* This is the normal way of making sure we have a bcopy and a bzero. This is used in most programs--a few other programs avoid this by defining INHIBIT_STRING_HEADER. */ #ifndef INHIBIT_STRING_HEADER #if defined (HAVE_STRING_H) || defined (STDC_HEADERS) || defined (_LIBC) #include #ifndef bcmp #define bcmp(s1, s2, n) memcmp ((s1), (s2), (n)) #endif #ifndef bcopy #define bcopy(s, d, n) memcpy ((d), (s), (n)) #endif #ifndef bzero #define bzero(s, n) memset ((s), 0, (n)) #endif #else #include #endif #endif /* Define the syntax stuff for \<, \>, etc. */ /* This must be nonzero for the wordchar and notwordchar pattern commands in re_match_2. */ #ifndef Sword #define Sword 1 #endif #ifdef SWITCH_ENUM_BUG #define SWITCH_ENUM_CAST(x) ((int)(x)) #else #define SWITCH_ENUM_CAST(x) (x) #endif #ifdef SYNTAX_TABLE extern char *re_syntax_table; #else /* not SYNTAX_TABLE */ /* How many characters in the character set. */ #define CHAR_SET_SIZE 256 static char re_syntax_table[CHAR_SET_SIZE]; static void init_syntax_once () { register int c; static int done = 0; if (done) return; bzero (re_syntax_table, sizeof re_syntax_table); for (c = 'a'; c <= 'z'; c++) re_syntax_table[c] = Sword; for (c = 'A'; c <= 'Z'; c++) re_syntax_table[c] = Sword; for (c = '0'; c <= '9'; c++) re_syntax_table[c] = Sword; re_syntax_table['_'] = Sword; done = 1; } #endif /* not SYNTAX_TABLE */ #define SYNTAX(c) re_syntax_table[c] /* Dummy macros for non-Emacs environments. */ #define BASE_LEADING_CODE_P(c) (0) #define WORD_BOUNDARY_P(c1, c2) (0) #define CHAR_HEAD_P(p) (1) #define SINGLE_BYTE_CHAR_P(c) (1) #define SAME_CHARSET_P(c1, c2) (1) #define MULTIBYTE_FORM_LENGTH(p, s) (1) #define STRING_CHAR(p, s) (*(p)) #define STRING_CHAR_AND_LENGTH(p, s, actual_len) ((actual_len) = 1, *(p)) #define GET_CHAR_AFTER_2(c, p, str1, end1, str2, end2) \ (c = ((p) == (end1) ? *(str2) : *(p))) #define GET_CHAR_BEFORE_2(c, p, str1, end1, str2, end2) \ (c = ((p) == (str2) ? *((end1) - 1) : *((p) - 1))) #endif /* not emacs */ /* Get the interface, including the syntax bits. */ #include "regex.h" /* isalpha etc. are used for the character classes. */ #include /* Jim Meyering writes: "... Some ctype macros are valid only for character codes that isascii says are ASCII (SGI's IRIX-4.0.5 is one such system --when using /bin/cc or gcc but without giving an ansi option). So, all ctype uses should be through macros like ISPRINT... If STDC_HEADERS is defined, then autoconf has verified that the ctype macros don't need to be guarded with references to isascii. ... Defining isascii to 1 should let any compiler worth its salt eliminate the && through constant folding." */ #if defined (STDC_HEADERS) || (!defined (isascii) && !defined (HAVE_ISASCII)) #define ISASCII(c) 1 #else #define ISASCII(c) isascii(c) #endif #ifdef isblank #define ISBLANK(c) (ISASCII (c) && isblank (c)) #else #define ISBLANK(c) ((c) == ' ' || (c) == '\t') #endif #ifdef isgraph #define ISGRAPH(c) (ISASCII (c) && isgraph (c)) #else #define ISGRAPH(c) (ISASCII (c) && isprint (c) && !isspace (c)) #endif #define ISPRINT(c) (ISASCII (c) && isprint (c)) #define ISDIGIT(c) (ISASCII (c) && isdigit (c)) #define ISALNUM(c) (ISASCII (c) && isalnum (c)) #define ISALPHA(c) (ISASCII (c) && isalpha (c)) #define ISCNTRL(c) (ISASCII (c) && iscntrl (c)) #define ISLOWER(c) (ISASCII (c) && islower (c)) #define ISPUNCT(c) (ISASCII (c) && ispunct (c)) #define ISSPACE(c) (ISASCII (c) && isspace (c)) #define ISUPPER(c) (ISASCII (c) && isupper (c)) #define ISXDIGIT(c) (ISASCII (c) && isxdigit (c)) #ifndef NULL #define NULL (void *)0 #endif /* We remove any previous definition of `SIGN_EXTEND_CHAR', since ours (we hope) works properly with all combinations of machines, compilers, `char' and `unsigned char' argument types. (Per Bothner suggested the basic approach.) */ #undef SIGN_EXTEND_CHAR #if __STDC__ #define SIGN_EXTEND_CHAR(c) ((signed char) (c)) #else /* not __STDC__ */ /* As in Harbison and Steele. */ #define SIGN_EXTEND_CHAR(c) ((((unsigned char) (c)) ^ 128) - 128) #endif /* Should we use malloc or alloca? If REGEX_MALLOC is not defined, we use `alloca' instead of `malloc'. This is because using malloc in re_search* or re_match* could cause memory leaks when C-g is used in Emacs; also, malloc is slower and causes storage fragmentation. On the other hand, malloc is more portable, and easier to debug. Because we sometimes use alloca, some routines have to be macros, not functions -- `alloca'-allocated space disappears at the end of the function it is called in. */ #ifdef REGEX_MALLOC #define REGEX_ALLOCATE malloc #define REGEX_REALLOCATE(source, osize, nsize) realloc (source, nsize) #define REGEX_FREE free #else /* not REGEX_MALLOC */ /* Emacs already defines alloca, sometimes. */ #ifndef alloca /* Make alloca work the best possible way. */ #ifdef __GNUC__ #define alloca __builtin_alloca #else /* not __GNUC__ */ #if HAVE_ALLOCA_H #include #else /* not __GNUC__ or HAVE_ALLOCA_H */ #if 0 /* It is a bad idea to declare alloca. We always cast the result. */ #ifndef _AIX /* Already did AIX, up at the top. */ char *alloca (); #endif /* not _AIX */ #endif #endif /* not HAVE_ALLOCA_H */ #endif /* not __GNUC__ */ #endif /* not alloca */ #define REGEX_ALLOCATE alloca /* Assumes a `char *destination' variable. */ #define REGEX_REALLOCATE(source, osize, nsize) \ (destination = (char *) alloca (nsize), \ bcopy (source, destination, osize), \ destination) /* No need to do anything to free, after alloca. */ #define REGEX_FREE(arg) ((void)0) /* Do nothing! But inhibit gcc warning. */ #endif /* not REGEX_MALLOC */ /* Define how to allocate the failure stack. */ #if defined (REL_ALLOC) && defined (REGEX_MALLOC) #define REGEX_ALLOCATE_STACK(size) \ r_alloc (&failure_stack_ptr, (size)) #define REGEX_REALLOCATE_STACK(source, osize, nsize) \ r_re_alloc (&failure_stack_ptr, (nsize)) #define REGEX_FREE_STACK(ptr) \ r_alloc_free (&failure_stack_ptr) #else /* not using relocating allocator */ #ifdef REGEX_MALLOC #define REGEX_ALLOCATE_STACK malloc #define REGEX_REALLOCATE_STACK(source, osize, nsize) realloc (source, nsize) #define REGEX_FREE_STACK free #else /* not REGEX_MALLOC */ #define REGEX_ALLOCATE_STACK alloca #define REGEX_REALLOCATE_STACK(source, osize, nsize) \ REGEX_REALLOCATE (source, osize, nsize) /* No need to explicitly free anything. */ #define REGEX_FREE_STACK(arg) #endif /* not REGEX_MALLOC */ #endif /* not using relocating allocator */ /* True if `size1' is non-NULL and PTR is pointing anywhere inside `string1' or just past its end. This works if PTR is NULL, which is a good thing. */ #define FIRST_STRING_P(ptr) \ (size1 && string1 <= (ptr) && (ptr) <= string1 + size1) /* (Re)Allocate N items of type T using malloc, or fail. */ #define TALLOC(n, t) ((t *) malloc ((n) * sizeof (t))) #define RETALLOC(addr, n, t) ((addr) = (t *) realloc (addr, (n) * sizeof (t))) #define RETALLOC_IF(addr, n, t) \ if (addr) RETALLOC((addr), (n), t); else (addr) = TALLOC ((n), t) #define REGEX_TALLOC(n, t) ((t *) REGEX_ALLOCATE ((n) * sizeof (t))) #define BYTEWIDTH 8 /* In bits. */ #define STREQ(s1, s2) ((strcmp (s1, s2) == 0)) #undef MAX #undef MIN #define MAX(a, b) ((a) > (b) ? (a) : (b)) #define MIN(a, b) ((a) < (b) ? (a) : (b)) typedef char boolean; #define false 0 #define true 1 static int re_match_2_internal (); /* These are the command codes that appear in compiled regular expressions. Some opcodes are followed by argument bytes. A command code can specify any interpretation whatsoever for its arguments. Zero bytes may appear in the compiled regular expression. */ typedef enum { no_op = 0, /* Succeed right away--no more backtracking. */ succeed, /* Followed by one byte giving n, then by n literal bytes. */ exactn, /* Matches any (more or less) character. */ anychar, /* Matches any one char belonging to specified set. First following byte is number of bitmap bytes. Then come bytes for a bitmap saying which chars are in. Bits in each byte are ordered low-bit-first. A character is in the set if its bit is 1. A character too large to have a bit in the map is automatically not in the set. */ charset, /* Same parameters as charset, but match any character that is not one of those specified. */ charset_not, /* Start remembering the text that is matched, for storing in a register. Followed by one byte with the register number, in the range 0 to one less than the pattern buffer's re_nsub field. Then followed by one byte with the number of groups inner to this one. (This last has to be part of the start_memory only because we need it in the on_failure_jump of re_match_2.) */ start_memory, /* Stop remembering the text that is matched and store it in a memory register. Followed by one byte with the register number, in the range 0 to one less than `re_nsub' in the pattern buffer, and one byte with the number of inner groups, just like `start_memory'. (We need the number of inner groups here because we don't have any easy way of finding the corresponding start_memory when we're at a stop_memory.) */ stop_memory, /* Match a duplicate of something remembered. Followed by one byte containing the register number. */ duplicate, /* Fail unless at beginning of line. */ begline, /* Fail unless at end of line. */ endline, /* Succeeds if at beginning of buffer (if emacs) or at beginning of string to be matched (if not). */ begbuf, /* Analogously, for end of buffer/string. */ endbuf, /* Followed by two byte relative address to which to jump. */ jump, /* Same as jump, but marks the end of an alternative. */ jump_past_alt, /* Followed by two-byte relative address of place to resume at in case of failure. */ on_failure_jump, /* Like on_failure_jump, but pushes a placeholder instead of the current string position when executed. */ on_failure_keep_string_jump, /* Throw away latest failure point and then jump to following two-byte relative address. */ pop_failure_jump, /* Change to pop_failure_jump if know won't have to backtrack to match; otherwise change to jump. This is used to jump back to the beginning of a repeat. If what follows this jump clearly won't match what the repeat does, such that we can be sure that there is no use backtracking out of repetitions already matched, then we change it to a pop_failure_jump. Followed by two-byte address. */ maybe_pop_jump, /* Jump to following two-byte address, and push a dummy failure point. This failure point will be thrown away if an attempt is made to use it for a failure. A `+' construct makes this before the first repeat. Also used as an intermediary kind of jump when compiling an alternative. */ dummy_failure_jump, /* Push a dummy failure point and continue. Used at the end of alternatives. */ push_dummy_failure, /* Followed by two-byte relative address and two-byte number n. After matching N times, jump to the address upon failure. */ succeed_n, /* Followed by two-byte relative address, and two-byte number n. Jump to the address N times, then fail. */ jump_n, /* Set the following two-byte relative address to the subsequent two-byte number. The address *includes* the two bytes of number. */ set_number_at, wordchar, /* Matches any word-constituent character. */ notwordchar, /* Matches any char that is not a word-constituent. */ wordbeg, /* Succeeds if at word beginning. */ wordend, /* Succeeds if at word end. */ wordbound, /* Succeeds if at a word boundary. */ notwordbound /* Succeeds if not at a word boundary. */ #ifdef emacs ,before_dot, /* Succeeds if before point. */ at_dot, /* Succeeds if at point. */ after_dot, /* Succeeds if after point. */ /* Matches any character whose syntax is specified. Followed by a byte which contains a syntax code, e.g., Sword. */ syntaxspec, /* Matches any character whose syntax is not that specified. */ notsyntaxspec, /* Matches any character whose category-set contains the specified category. The operator is followed by a byte which contains a category code (mnemonic ASCII character). */ categoryspec, /* Matches any character whose category-set does not contain the specified category. The operator is followed by a byte which contains the category code (mnemonic ASCII character). */ notcategoryspec #endif /* emacs */ } re_opcode_t; /* Common operations on the compiled pattern. */ /* Store NUMBER in two contiguous bytes starting at DESTINATION. */ #define STORE_NUMBER(destination, number) \ do { \ (destination)[0] = (number) & 0377; \ (destination)[1] = (number) >> 8; \ } while (0) /* Same as STORE_NUMBER, except increment DESTINATION to the byte after where the number is stored. Therefore, DESTINATION must be an lvalue. */ #define STORE_NUMBER_AND_INCR(destination, number) \ do { \ STORE_NUMBER (destination, number); \ (destination) += 2; \ } while (0) /* Put into DESTINATION a number stored in two contiguous bytes starting at SOURCE. */ #define EXTRACT_NUMBER(destination, source) \ do { \ (destination) = *(source) & 0377; \ (destination) += SIGN_EXTEND_CHAR (*((source) + 1)) << 8; \ } while (0) #ifdef DEBUG static void extract_number (dest, source) int *dest; unsigned char *source; { int temp = SIGN_EXTEND_CHAR (*(source + 1)); *dest = *source & 0377; *dest += temp << 8; } #ifndef EXTRACT_MACROS /* To debug the macros. */ #undef EXTRACT_NUMBER #define EXTRACT_NUMBER(dest, src) extract_number (&dest, src) #endif /* not EXTRACT_MACROS */ #endif /* DEBUG */ /* Same as EXTRACT_NUMBER, except increment SOURCE to after the number. SOURCE must be an lvalue. */ #define EXTRACT_NUMBER_AND_INCR(destination, source) \ do { \ EXTRACT_NUMBER (destination, source); \ (source) += 2; \ } while (0) #ifdef DEBUG static void extract_number_and_incr (destination, source) int *destination; unsigned char **source; { extract_number (destination, *source); *source += 2; } #ifndef EXTRACT_MACROS #undef EXTRACT_NUMBER_AND_INCR #define EXTRACT_NUMBER_AND_INCR(dest, src) \ extract_number_and_incr (&dest, &src) #endif /* not EXTRACT_MACROS */ #endif /* DEBUG */ /* Store a multibyte character in three contiguous bytes starting DESTINATION, and increment DESTINATION to the byte after where the character is stored. Therefore, DESTINATION must be an lvalue. */ #define STORE_CHARACTER_AND_INCR(destination, character) \ do { \ (destination)[0] = (character) & 0377; \ (destination)[1] = ((character) >> 8) & 0377; \ (destination)[2] = (character) >> 16; \ (destination) += 3; \ } while (0) /* Put into DESTINATION a character stored in three contiguous bytes starting at SOURCE. */ #define EXTRACT_CHARACTER(destination, source) \ do { \ (destination) = ((source)[0] \ | ((source)[1] << 8) \ | ((source)[2] << 16)); \ } while (0) /* Macros for charset. */ /* Size of bitmap of charset P in bytes. P is a start of charset, i.e. *P is (re_opcode_t) charset or (re_opcode_t) charset_not. */ #define CHARSET_BITMAP_SIZE(p) ((p)[1] & 0x7F) /* Nonzero if charset P has range table. */ #define CHARSET_RANGE_TABLE_EXISTS_P(p) ((p)[1] & 0x80) /* Return the address of range table of charset P. But not the start of table itself, but the before where the number of ranges is stored. `2 +' means to skip re_opcode_t and size of bitmap. */ #define CHARSET_RANGE_TABLE(p) (&(p)[2 + CHARSET_BITMAP_SIZE (p)]) /* Test if C is listed in the bitmap of charset P. */ #define CHARSET_LOOKUP_BITMAP(p, c) \ ((c) < CHARSET_BITMAP_SIZE (p) * BYTEWIDTH \ && (p)[2 + (c) / BYTEWIDTH] & (1 << ((c) % BYTEWIDTH))) /* Return the address of end of RANGE_TABLE. COUNT is number of ranges (which is a pair of (start, end)) in the RANGE_TABLE. `* 2' is start of range and end of range. `* 3' is size of each start and end. */ #define CHARSET_RANGE_TABLE_END(range_table, count) \ ((range_table) + (count) * 2 * 3) /* Test if C is in RANGE_TABLE. A flag NOT is negated if C is in. COUNT is number of ranges in RANGE_TABLE. */ #define CHARSET_LOOKUP_RANGE_TABLE_RAW(not, c, range_table, count) \ do \ { \ int range_start, range_end; \ unsigned char *p; \ unsigned char *range_table_end \ = CHARSET_RANGE_TABLE_END ((range_table), (count)); \ \ for (p = (range_table); p < range_table_end; p += 2 * 3) \ { \ EXTRACT_CHARACTER (range_start, p); \ EXTRACT_CHARACTER (range_end, p + 3); \ \ if (range_start <= (c) && (c) <= range_end) \ { \ (not) = !(not); \ break; \ } \ } \ } \ while (0) /* Test if C is in range table of CHARSET. The flag NOT is negated if C is listed in it. */ #define CHARSET_LOOKUP_RANGE_TABLE(not, c, charset) \ do \ { \ /* Number of ranges in range table. */ \ int count; \ unsigned char *range_table = CHARSET_RANGE_TABLE (charset); \ \ EXTRACT_NUMBER_AND_INCR (count, range_table); \ CHARSET_LOOKUP_RANGE_TABLE_RAW ((not), (c), range_table, count); \ } \ while (0) /* If DEBUG is defined, Regex prints many voluminous messages about what it is doing (if the variable `debug' is nonzero). If linked with the main program in `iregex.c', you can enter patterns and strings interactively. And if linked with the main program in `main.c' and the other test files, you can run the already-written tests. */ #ifdef DEBUG /* We use standard I/O for debugging. */ #include /* It is useful to test things that ``must'' be true when debugging. */ #include static int debug = 0; #define DEBUG_STATEMENT(e) e #define DEBUG_PRINT1(x) if (debug) printf (x) #define DEBUG_PRINT2(x1, x2) if (debug) printf (x1, x2) #define DEBUG_PRINT3(x1, x2, x3) if (debug) printf (x1, x2, x3) #define DEBUG_PRINT4(x1, x2, x3, x4) if (debug) printf (x1, x2, x3, x4) #define DEBUG_PRINT_COMPILED_PATTERN(p, s, e) \ if (debug) print_partial_compiled_pattern (s, e) #define DEBUG_PRINT_DOUBLE_STRING(w, s1, sz1, s2, sz2) \ if (debug) print_double_string (w, s1, sz1, s2, sz2) /* Print the fastmap in human-readable form. */ void print_fastmap (fastmap) char *fastmap; { unsigned was_a_range = 0; unsigned i = 0; while (i < (1 << BYTEWIDTH)) { if (fastmap[i++]) { was_a_range = 0; putchar (i - 1); while (i < (1 << BYTEWIDTH) && fastmap[i]) { was_a_range = 1; i++; } if (was_a_range) { printf ("-"); putchar (i - 1); } } } putchar ('\n'); } /* Print a compiled pattern string in human-readable form, starting at the START pointer into it and ending just before the pointer END. */ void print_partial_compiled_pattern (start, end) unsigned char *start; unsigned char *end; { int mcnt, mcnt2; unsigned char *p = start; unsigned char *pend = end; if (start == NULL) { printf ("(null)\n"); return; } /* Loop over pattern commands. */ while (p < pend) { printf ("%d:\t", p - start); switch ((re_opcode_t) *p++) { case no_op: printf ("/no_op"); break; case exactn: mcnt = *p++; printf ("/exactn/%d", mcnt); do { putchar ('/'); putchar (*p++); } while (--mcnt); break; case start_memory: mcnt = *p++; printf ("/start_memory/%d/%d", mcnt, *p++); break; case stop_memory: mcnt = *p++; printf ("/stop_memory/%d/%d", mcnt, *p++); break; case duplicate: printf ("/duplicate/%d", *p++); break; case anychar: printf ("/anychar"); break; case charset: case charset_not: { register int c, last = -100; register int in_range = 0; printf ("/charset [%s", (re_opcode_t) *(p - 1) == charset_not ? "^" : ""); assert (p + *p < pend); for (c = 0; c < 256; c++) if (c / 8 < *p && (p[1 + (c/8)] & (1 << (c % 8)))) { /* Are we starting a range? */ if (last + 1 == c && ! in_range) { putchar ('-'); in_range = 1; } /* Have we broken a range? */ else if (last + 1 != c && in_range) { putchar (last); in_range = 0; } if (! in_range) putchar (c); last = c; } if (in_range) putchar (last); putchar (']'); p += 1 + *p; } break; case begline: printf ("/begline"); break; case endline: printf ("/endline"); break; case on_failure_jump: extract_number_and_incr (&mcnt, &p); printf ("/on_failure_jump to %d", p + mcnt - start); break; case on_failure_keep_string_jump: extract_number_and_incr (&mcnt, &p); printf ("/on_failure_keep_string_jump to %d", p + mcnt - start); break; case dummy_failure_jump: extract_number_and_incr (&mcnt, &p); printf ("/dummy_failure_jump to %d", p + mcnt - start); break; case push_dummy_failure: printf ("/push_dummy_failure"); break; case maybe_pop_jump: extract_number_and_incr (&mcnt, &p); printf ("/maybe_pop_jump to %d", p + mcnt - start); break; case pop_failure_jump: extract_number_and_incr (&mcnt, &p); printf ("/pop_failure_jump to %d", p + mcnt - start); break; case jump_past_alt: extract_number_and_incr (&mcnt, &p); printf ("/jump_past_alt to %d", p + mcnt - start); break; case jump: extract_number_and_incr (&mcnt, &p); printf ("/jump to %d", p + mcnt - start); break; case succeed_n: extract_number_and_incr (&mcnt, &p); extract_number_and_incr (&mcnt2, &p); printf ("/succeed_n to %d, %d times", p + mcnt - start, mcnt2); break; case jump_n: extract_number_and_incr (&mcnt, &p); extract_number_and_incr (&mcnt2, &p); printf ("/jump_n to %d, %d times", p + mcnt - start, mcnt2); break; case set_number_at: extract_number_and_incr (&mcnt, &p); extract_number_and_incr (&mcnt2, &p); printf ("/set_number_at location %d to %d", p + mcnt - start, mcnt2); break; case wordbound: printf ("/wordbound"); break; case notwordbound: printf ("/notwordbound"); break; case wordbeg: printf ("/wordbeg"); break; case wordend: printf ("/wordend"); #ifdef emacs case before_dot: printf ("/before_dot"); break; case at_dot: printf ("/at_dot"); break; case after_dot: printf ("/after_dot"); break; case syntaxspec: printf ("/syntaxspec"); mcnt = *p++; printf ("/%d", mcnt); break; case notsyntaxspec: printf ("/notsyntaxspec"); mcnt = *p++; printf ("/%d", mcnt); break; #endif /* emacs */ case wordchar: printf ("/wordchar"); break; case notwordchar: printf ("/notwordchar"); break; case begbuf: printf ("/begbuf"); break; case endbuf: printf ("/endbuf"); break; default: printf ("?%d", *(p-1)); } putchar ('\n'); } printf ("%d:\tend of pattern.\n", p - start); } void print_compiled_pattern (bufp) struct re_pattern_buffer *bufp; { unsigned char *buffer = bufp->buffer; print_partial_compiled_pattern (buffer, buffer + bufp->used); printf ("%d bytes used/%d bytes allocated.\n", bufp->used, bufp->allocated); if (bufp->fastmap_accurate && bufp->fastmap) { printf ("fastmap: "); print_fastmap (bufp->fastmap); } printf ("re_nsub: %d\t", bufp->re_nsub); printf ("regs_alloc: %d\t", bufp->regs_allocated); printf ("can_be_null: %d\t", bufp->can_be_null); printf ("newline_anchor: %d\n", bufp->newline_anchor); printf ("no_sub: %d\t", bufp->no_sub); printf ("not_bol: %d\t", bufp->not_bol); printf ("not_eol: %d\t", bufp->not_eol); printf ("syntax: %d\n", bufp->syntax); /* Perhaps we should print the translate table? */ } void print_double_string (where, string1, size1, string2, size2) const char *where; const char *string1; const char *string2; int size1; int size2; { unsigned this_char; if (where == NULL) printf ("(null)"); else { if (FIRST_STRING_P (where)) { for (this_char = where - string1; this_char < size1; this_char++) putchar (string1[this_char]); where = string2; } for (this_char = where - string2; this_char < size2; this_char++) putchar (string2[this_char]); } } #else /* not DEBUG */ #undef assert #define assert(e) #define DEBUG_STATEMENT(e) #define DEBUG_PRINT1(x) #define DEBUG_PRINT2(x1, x2) #define DEBUG_PRINT3(x1, x2, x3) #define DEBUG_PRINT4(x1, x2, x3, x4) #define DEBUG_PRINT_COMPILED_PATTERN(p, s, e) #define DEBUG_PRINT_DOUBLE_STRING(w, s1, sz1, s2, sz2) #endif /* not DEBUG */ /* Set by `re_set_syntax' to the current regexp syntax to recognize. Can also be assigned to arbitrarily: each pattern buffer stores its own syntax, so it can be changed between regex compilations. */ /* This has no initializer because initialized variables in Emacs become read-only after dumping. */ reg_syntax_t re_syntax_options; /* Specify the precise syntax of regexps for compilation. This provides for compatibility for various utilities which historically have different, incompatible syntaxes. The argument SYNTAX is a bit mask comprised of the various bits defined in regex.h. We return the old syntax. */ reg_syntax_t re_set_syntax (syntax) reg_syntax_t syntax; { reg_syntax_t ret = re_syntax_options; re_syntax_options = syntax; return ret; } /* This table gives an error message for each of the error codes listed in regex.h. Obviously the order here has to be same as there. POSIX doesn't require that we do anything for REG_NOERROR, but why not be nice? */ static const char *re_error_msgid[] = { gettext_noop ("Success"), /* REG_NOERROR */ gettext_noop ("No match"), /* REG_NOMATCH */ gettext_noop ("Invalid regular expression"), /* REG_BADPAT */ gettext_noop ("Invalid collation character"), /* REG_ECOLLATE */ gettext_noop ("Invalid character class name"), /* REG_ECTYPE */ gettext_noop ("Trailing backslash"), /* REG_EESCAPE */ gettext_noop ("Invalid back reference"), /* REG_ESUBREG */ gettext_noop ("Unmatched [ or [^"), /* REG_EBRACK */ gettext_noop ("Unmatched ( or \\("), /* REG_EPAREN */ gettext_noop ("Unmatched \\{"), /* REG_EBRACE */ gettext_noop ("Invalid content of \\{\\}"), /* REG_BADBR */ gettext_noop ("Invalid range end"), /* REG_ERANGE */ gettext_noop ("Memory exhausted"), /* REG_ESPACE */ gettext_noop ("Invalid preceding regular expression"), /* REG_BADRPT */ gettext_noop ("Premature end of regular expression"), /* REG_EEND */ gettext_noop ("Regular expression too big"), /* REG_ESIZE */ gettext_noop ("Unmatched ) or \\)"), /* REG_ERPAREN */ }; /* Avoiding alloca during matching, to placate r_alloc. */ /* Define MATCH_MAY_ALLOCATE unless we need to make sure that the searching and matching functions should not call alloca. On some systems, alloca is implemented in terms of malloc, and if we're using the relocating allocator routines, then malloc could cause a relocation, which might (if the strings being searched are in the ralloc heap) shift the data out from underneath the regexp routines. Here's another reason to avoid allocation: Emacs processes input from X in a signal handler; processing X input may call malloc; if input arrives while a matching routine is calling malloc, then we're scrod. But Emacs can't just block input while calling matching routines; then we don't notice interrupts when they come in. So, Emacs blocks input around all regexp calls except the matching calls, which it leaves unprotected, in the faith that they will not malloc. */ /* Normally, this is fine. */ #define MATCH_MAY_ALLOCATE /* When using GNU C, we are not REALLY using the C alloca, no matter what config.h may say. So don't take precautions for it. */ #ifdef __GNUC__ #undef C_ALLOCA #endif /* The match routines may not allocate if (1) they would do it with malloc and (2) it's not safe for them to use malloc. Note that if REL_ALLOC is defined, matching would not use malloc for the failure stack, but we would still use it for the register vectors; so REL_ALLOC should not affect this. */ #if (defined (C_ALLOCA) || defined (REGEX_MALLOC)) && defined (emacs) #undef MATCH_MAY_ALLOCATE #endif /* Failure stack declarations and macros; both re_compile_fastmap and re_match_2 use a failure stack. These have to be macros because of REGEX_ALLOCATE_STACK. */ /* Approximate number of failure points for which to initially allocate space when matching. If this number is exceeded, we allocate more space, so it is not a hard limit. */ #ifndef INIT_FAILURE_ALLOC #define INIT_FAILURE_ALLOC 20 #endif /* Roughly the maximum number of failure points on the stack. Would be exactly that if always used TYPICAL_FAILURE_SIZE items each time we failed. This is a variable only so users of regex can assign to it; we never change it ourselves. */ #if defined (MATCH_MAY_ALLOCATE) /* Note that 4400 is enough to cause a crash on Alpha OSF/1, whose default stack limit is 2mb. In order for a larger value to work reliably, you have to try to make it accord with the process stack limit. */ int re_max_failures = 40000; #else int re_max_failures = 4000; #endif union fail_stack_elt { unsigned char *pointer; int integer; }; typedef union fail_stack_elt fail_stack_elt_t; typedef struct { fail_stack_elt_t *stack; unsigned size; unsigned avail; /* Offset of next open position. */ } fail_stack_type; #define FAIL_STACK_EMPTY() (fail_stack.avail == 0) #define FAIL_STACK_PTR_EMPTY() (fail_stack_ptr->avail == 0) #define FAIL_STACK_FULL() (fail_stack.avail == fail_stack.size) /* Define macros to initialize and free the failure stack. Do `return -2' if the alloc fails. */ #ifdef MATCH_MAY_ALLOCATE #define INIT_FAIL_STACK() \ do { \ fail_stack.stack = (fail_stack_elt_t *) \ REGEX_ALLOCATE_STACK (INIT_FAILURE_ALLOC * TYPICAL_FAILURE_SIZE \ * sizeof (fail_stack_elt_t)); \ \ if (fail_stack.stack == NULL) \ return -2; \ \ fail_stack.size = INIT_FAILURE_ALLOC; \ fail_stack.avail = 0; \ } while (0) #define RESET_FAIL_STACK() REGEX_FREE_STACK (fail_stack.stack) #else #define INIT_FAIL_STACK() \ do { \ fail_stack.avail = 0; \ } while (0) #define RESET_FAIL_STACK() #endif /* Double the size of FAIL_STACK, up to a limit which allows approximately `re_max_failures' items. Return 1 if succeeds, and 0 if either ran out of memory allocating space for it or it was already too large. REGEX_REALLOCATE_STACK requires `destination' be declared. */ /* Factor to increase the failure stack size by when we increase it. This used to be 2, but 2 was too wasteful because the old discarded stacks added up to as much space were as ultimate, maximum-size stack. */ #define FAIL_STACK_GROWTH_FACTOR 4 #define GROW_FAIL_STACK(fail_stack) \ (((fail_stack).size * sizeof (fail_stack_elt_t) \ >= re_max_failures * TYPICAL_FAILURE_SIZE) \ ? 0 \ : ((fail_stack).stack \ = (fail_stack_elt_t *) \ REGEX_REALLOCATE_STACK ((fail_stack).stack, \ (fail_stack).size * sizeof (fail_stack_elt_t), \ MIN (re_max_failures * TYPICAL_FAILURE_SIZE, \ ((fail_stack).size * sizeof (fail_stack_elt_t) \ * FAIL_STACK_GROWTH_FACTOR))), \ \ (fail_stack).stack == NULL \ ? 0 \ : ((fail_stack).size \ = (MIN (re_max_failures * TYPICAL_FAILURE_SIZE, \ ((fail_stack).size * sizeof (fail_stack_elt_t) \ * FAIL_STACK_GROWTH_FACTOR)) \ / sizeof (fail_stack_elt_t)), \ 1))) /* Push pointer POINTER on FAIL_STACK. Return 1 if was able to do so and 0 if ran out of memory allocating space to do so. */ #define PUSH_PATTERN_OP(POINTER, FAIL_STACK) \ ((FAIL_STACK_FULL () \ && !GROW_FAIL_STACK (FAIL_STACK)) \ ? 0 \ : ((FAIL_STACK).stack[(FAIL_STACK).avail++].pointer = POINTER, \ 1)) /* Push a pointer value onto the failure stack. Assumes the variable `fail_stack'. Probably should only be called from within `PUSH_FAILURE_POINT'. */ #define PUSH_FAILURE_POINTER(item) \ fail_stack.stack[fail_stack.avail++].pointer = (unsigned char *) (item) /* This pushes an integer-valued item onto the failure stack. Assumes the variable `fail_stack'. Probably should only be called from within `PUSH_FAILURE_POINT'. */ #define PUSH_FAILURE_INT(item) \ fail_stack.stack[fail_stack.avail++].integer = (item) /* Push a fail_stack_elt_t value onto the failure stack. Assumes the variable `fail_stack'. Probably should only be called from within `PUSH_FAILURE_POINT'. */ #define PUSH_FAILURE_ELT(item) \ fail_stack.stack[fail_stack.avail++] = (item) /* These three POP... operations complement the three PUSH... operations. All assume that `fail_stack' is nonempty. */ #define POP_FAILURE_POINTER() fail_stack.stack[--fail_stack.avail].pointer #define POP_FAILURE_INT() fail_stack.stack[--fail_stack.avail].integer #define POP_FAILURE_ELT() fail_stack.stack[--fail_stack.avail] /* Used to omit pushing failure point id's when we're not debugging. */ #ifdef DEBUG #define DEBUG_PUSH PUSH_FAILURE_INT #define DEBUG_POP(item_addr) *(item_addr) = POP_FAILURE_INT () #else #define DEBUG_PUSH(item) #define DEBUG_POP(item_addr) #endif /* Push the information about the state we will need if we ever fail back to it. Requires variables fail_stack, regstart, regend, reg_info, and num_regs be declared. GROW_FAIL_STACK requires `destination' be declared. Does `return FAILURE_CODE' if runs out of memory. */ #define PUSH_FAILURE_POINT(pattern_place, string_place, failure_code) \ do { \ char *destination; \ /* Must be int, so when we don't save any registers, the arithmetic \ of 0 + -1 isn't done as unsigned. */ \ int this_reg; \ \ DEBUG_STATEMENT (failure_id++); \ DEBUG_STATEMENT (nfailure_points_pushed++); \ DEBUG_PRINT2 ("\nPUSH_FAILURE_POINT #%u:\n", failure_id); \ DEBUG_PRINT2 (" Before push, next avail: %d\n", (fail_stack).avail);\ DEBUG_PRINT2 (" size: %d\n", (fail_stack).size);\ \ DEBUG_PRINT2 (" slots needed: %d\n", NUM_FAILURE_ITEMS); \ DEBUG_PRINT2 (" available: %d\n", REMAINING_AVAIL_SLOTS); \ \ /* Ensure we have enough space allocated for what we will push. */ \ while (REMAINING_AVAIL_SLOTS < NUM_FAILURE_ITEMS) \ { \ if (!GROW_FAIL_STACK (fail_stack)) \ return failure_code; \ \ DEBUG_PRINT2 ("\n Doubled stack; size now: %d\n", \ (fail_stack).size); \ DEBUG_PRINT2 (" slots available: %d\n", REMAINING_AVAIL_SLOTS);\ } \ \ /* Push the info, starting with the registers. */ \ DEBUG_PRINT1 ("\n"); \ \ if (1) \ for (this_reg = lowest_active_reg; this_reg <= highest_active_reg; \ this_reg++) \ { \ DEBUG_PRINT2 (" Pushing reg: %d\n", this_reg); \ DEBUG_STATEMENT (num_regs_pushed++); \ \ DEBUG_PRINT2 (" start: 0x%x\n", regstart[this_reg]); \ PUSH_FAILURE_POINTER (regstart[this_reg]); \ \ DEBUG_PRINT2 (" end: 0x%x\n", regend[this_reg]); \ PUSH_FAILURE_POINTER (regend[this_reg]); \ \ DEBUG_PRINT2 (" info: 0x%x\n ", reg_info[this_reg]); \ DEBUG_PRINT2 (" match_null=%d", \ REG_MATCH_NULL_STRING_P (reg_info[this_reg])); \ DEBUG_PRINT2 (" active=%d", IS_ACTIVE (reg_info[this_reg])); \ DEBUG_PRINT2 (" matched_something=%d", \ MATCHED_SOMETHING (reg_info[this_reg])); \ DEBUG_PRINT2 (" ever_matched=%d", \ EVER_MATCHED_SOMETHING (reg_info[this_reg])); \ DEBUG_PRINT1 ("\n"); \ PUSH_FAILURE_ELT (reg_info[this_reg].word); \ } \ \ DEBUG_PRINT2 (" Pushing low active reg: %d\n", lowest_active_reg);\ PUSH_FAILURE_INT (lowest_active_reg); \ \ DEBUG_PRINT2 (" Pushing high active reg: %d\n", highest_active_reg);\ PUSH_FAILURE_INT (highest_active_reg); \ \ DEBUG_PRINT2 (" Pushing pattern 0x%x: ", pattern_place); \ DEBUG_PRINT_COMPILED_PATTERN (bufp, pattern_place, pend); \ PUSH_FAILURE_POINTER (pattern_place); \ \ DEBUG_PRINT2 (" Pushing string 0x%x: `", string_place); \ DEBUG_PRINT_DOUBLE_STRING (string_place, string1, size1, string2, \ size2); \ DEBUG_PRINT1 ("'\n"); \ PUSH_FAILURE_POINTER (string_place); \ \ DEBUG_PRINT2 (" Pushing failure id: %u\n", failure_id); \ DEBUG_PUSH (failure_id); \ } while (0) /* This is the number of items that are pushed and popped on the stack for each register. */ #define NUM_REG_ITEMS 3 /* Individual items aside from the registers. */ #ifdef DEBUG #define NUM_NONREG_ITEMS 5 /* Includes failure point id. */ #else #define NUM_NONREG_ITEMS 4 #endif /* Estimate the size of data pushed by a typical failure stack entry. An estimate is all we need, because all we use this for is to choose a limit for how big to make the failure stack. */ #define TYPICAL_FAILURE_SIZE 20 /* This is how many items we actually use for a failure point. It depends on the regexp. */ #define NUM_FAILURE_ITEMS \ (((0 \ ? 0 : highest_active_reg - lowest_active_reg + 1) \ * NUM_REG_ITEMS) \ + NUM_NONREG_ITEMS) /* How many items can still be added to the stack without overflowing it. */ #define REMAINING_AVAIL_SLOTS ((fail_stack).size - (fail_stack).avail) /* Pops what PUSH_FAIL_STACK pushes. We restore into the parameters, all of which should be lvalues: STR -- the saved data position. PAT -- the saved pattern position. LOW_REG, HIGH_REG -- the highest and lowest active registers. REGSTART, REGEND -- arrays of string positions. REG_INFO -- array of information about each subexpression. Also assumes the variables `fail_stack' and (if debugging), `bufp', `pend', `string1', `size1', `string2', and `size2'. */ #define POP_FAILURE_POINT(str, pat, low_reg, high_reg, regstart, regend, reg_info)\ { \ DEBUG_STATEMENT (fail_stack_elt_t failure_id;) \ int this_reg; \ const unsigned char *string_temp; \ \ assert (!FAIL_STACK_EMPTY ()); \ \ /* Remove failure points and point to how many regs pushed. */ \ DEBUG_PRINT1 ("POP_FAILURE_POINT:\n"); \ DEBUG_PRINT2 (" Before pop, next avail: %d\n", fail_stack.avail); \ DEBUG_PRINT2 (" size: %d\n", fail_stack.size); \ \ assert (fail_stack.avail >= NUM_NONREG_ITEMS); \ \ DEBUG_POP (&failure_id); \ DEBUG_PRINT2 (" Popping failure id: %u\n", failure_id); \ \ /* If the saved string location is NULL, it came from an \ on_failure_keep_string_jump opcode, and we want to throw away the \ saved NULL, thus retaining our current position in the string. */ \ string_temp = POP_FAILURE_POINTER (); \ if (string_temp != NULL) \ str = (const char *) string_temp; \ \ DEBUG_PRINT2 (" Popping string 0x%x: `", str); \ DEBUG_PRINT_DOUBLE_STRING (str, string1, size1, string2, size2); \ DEBUG_PRINT1 ("'\n"); \ \ pat = (unsigned char *) POP_FAILURE_POINTER (); \ DEBUG_PRINT2 (" Popping pattern 0x%x: ", pat); \ DEBUG_PRINT_COMPILED_PATTERN (bufp, pat, pend); \ \ /* Restore register info. */ \ high_reg = (unsigned) POP_FAILURE_INT (); \ DEBUG_PRINT2 (" Popping high active reg: %d\n", high_reg); \ \ low_reg = (unsigned) POP_FAILURE_INT (); \ DEBUG_PRINT2 (" Popping low active reg: %d\n", low_reg); \ \ if (1) \ for (this_reg = high_reg; this_reg >= low_reg; this_reg--) \ { \ DEBUG_PRINT2 (" Popping reg: %d\n", this_reg); \ \ reg_info[this_reg].word = POP_FAILURE_ELT (); \ DEBUG_PRINT2 (" info: 0x%x\n", reg_info[this_reg]); \ \ regend[this_reg] = (const char *) POP_FAILURE_POINTER (); \ DEBUG_PRINT2 (" end: 0x%x\n", regend[this_reg]); \ \ regstart[this_reg] = (const char *) POP_FAILURE_POINTER (); \ DEBUG_PRINT2 (" start: 0x%x\n", regstart[this_reg]); \ } \ else \ { \ for (this_reg = highest_active_reg; this_reg > high_reg; this_reg--) \ { \ reg_info[this_reg].word.integer = 0; \ regend[this_reg] = 0; \ regstart[this_reg] = 0; \ } \ highest_active_reg = high_reg; \ } \ \ set_regs_matched_done = 0; \ DEBUG_STATEMENT (nfailure_points_popped++); \ } /* POP_FAILURE_POINT */ /* Structure for per-register (a.k.a. per-group) information. Other register information, such as the starting and ending positions (which are addresses), and the list of inner groups (which is a bits list) are maintained in separate variables. We are making a (strictly speaking) nonportable assumption here: that the compiler will pack our bit fields into something that fits into the type of `word', i.e., is something that fits into one item on the failure stack. */ typedef union { fail_stack_elt_t word; struct { /* This field is one if this group can match the empty string, zero if not. If not yet determined, `MATCH_NULL_UNSET_VALUE'. */ #define MATCH_NULL_UNSET_VALUE 3 unsigned match_null_string_p : 2; unsigned is_active : 1; unsigned matched_something : 1; unsigned ever_matched_something : 1; } bits; } register_info_type; #define REG_MATCH_NULL_STRING_P(R) ((R).bits.match_null_string_p) #define IS_ACTIVE(R) ((R).bits.is_active) #define MATCHED_SOMETHING(R) ((R).bits.matched_something) #define EVER_MATCHED_SOMETHING(R) ((R).bits.ever_matched_something) /* Call this when have matched a real character; it sets `matched' flags for the subexpressions which we are currently inside. Also records that those subexprs have matched. */ #define SET_REGS_MATCHED() \ do \ { \ if (!set_regs_matched_done) \ { \ unsigned r; \ set_regs_matched_done = 1; \ for (r = lowest_active_reg; r <= highest_active_reg; r++) \ { \ MATCHED_SOMETHING (reg_info[r]) \ = EVER_MATCHED_SOMETHING (reg_info[r]) \ = 1; \ } \ } \ } \ while (0) /* Registers are set to a sentinel when they haven't yet matched. */ static char reg_unset_dummy; #define REG_UNSET_VALUE (®_unset_dummy) #define REG_UNSET(e) ((e) == REG_UNSET_VALUE) /* Subroutine declarations and macros for regex_compile. */ static void store_op1 (), store_op2 (); static void insert_op1 (), insert_op2 (); static boolean at_begline_loc_p (), at_endline_loc_p (); static boolean group_in_compile_stack (); static reg_errcode_t compile_range (); /* Fetch the next character in the uncompiled pattern---translating it if necessary. Also cast from a signed character in the constant string passed to us by the user to an unsigned char that we can use as an array index (in, e.g., `translate'). */ #ifndef PATFETCH #define PATFETCH(c) \ do {if (p == pend) return REG_EEND; \ c = (unsigned char) *p++; \ if (RE_TRANSLATE_P (translate)) c = RE_TRANSLATE (translate, c); \ } while (0) #endif /* Fetch the next character in the uncompiled pattern, with no translation. */ #define PATFETCH_RAW(c) \ do {if (p == pend) return REG_EEND; \ c = (unsigned char) *p++; \ } while (0) /* Go backwards one character in the pattern. */ #define PATUNFETCH p-- /* If `translate' is non-null, return translate[D], else just D. We cast the subscript to translate because some data is declared as `char *', to avoid warnings when a string constant is passed. But when we use a character as a subscript we must make it unsigned. */ #ifndef TRANSLATE #define TRANSLATE(d) \ (RE_TRANSLATE_P (translate) \ ? (unsigned) RE_TRANSLATE (translate, (unsigned) (d)) : (d)) #endif /* Macros for outputting the compiled pattern into `buffer'. */ /* If the buffer isn't allocated when it comes in, use this. */ #define INIT_BUF_SIZE 32 /* Make sure we have at least N more bytes of space in buffer. */ #define GET_BUFFER_SPACE(n) \ while (b - bufp->buffer + (n) > bufp->allocated) \ EXTEND_BUFFER () /* Make sure we have one more byte of buffer space and then add C to it. */ #define BUF_PUSH(c) \ do { \ GET_BUFFER_SPACE (1); \ *b++ = (unsigned char) (c); \ } while (0) /* Ensure we have two more bytes of buffer space and then append C1 and C2. */ #define BUF_PUSH_2(c1, c2) \ do { \ GET_BUFFER_SPACE (2); \ *b++ = (unsigned char) (c1); \ *b++ = (unsigned char) (c2); \ } while (0) /* As with BUF_PUSH_2, except for three bytes. */ #define BUF_PUSH_3(c1, c2, c3) \ do { \ GET_BUFFER_SPACE (3); \ *b++ = (unsigned char) (c1); \ *b++ = (unsigned char) (c2); \ *b++ = (unsigned char) (c3); \ } while (0) /* Store a jump with opcode OP at LOC to location TO. We store a relative address offset by the three bytes the jump itself occupies. */ #define STORE_JUMP(op, loc, to) \ store_op1 (op, loc, (to) - (loc) - 3) /* Likewise, for a two-argument jump. */ #define STORE_JUMP2(op, loc, to, arg) \ store_op2 (op, loc, (to) - (loc) - 3, arg) /* Like `STORE_JUMP', but for inserting. Assume `b' is the buffer end. */ #define INSERT_JUMP(op, loc, to) \ insert_op1 (op, loc, (to) - (loc) - 3, b) /* Like `STORE_JUMP2', but for inserting. Assume `b' is the buffer end. */ #define INSERT_JUMP2(op, loc, to, arg) \ insert_op2 (op, loc, (to) - (loc) - 3, arg, b) /* This is not an arbitrary limit: the arguments which represent offsets into the pattern are two bytes long. So if 2^16 bytes turns out to be too small, many things would have to change. */ #define MAX_BUF_SIZE (1L << 16) /* Extend the buffer by twice its current size via realloc and reset the pointers that pointed into the old block to point to the correct places in the new one. If extending the buffer results in it being larger than MAX_BUF_SIZE, then flag memory exhausted. */ #define EXTEND_BUFFER() \ do { \ unsigned char *old_buffer = bufp->buffer; \ if (bufp->allocated == MAX_BUF_SIZE) \ return REG_ESIZE; \ bufp->allocated <<= 1; \ if (bufp->allocated > MAX_BUF_SIZE) \ bufp->allocated = MAX_BUF_SIZE; \ bufp->buffer = (unsigned char *) realloc (bufp->buffer, bufp->allocated);\ if (bufp->buffer == NULL) \ return REG_ESPACE; \ /* If the buffer moved, move all the pointers into it. */ \ if (old_buffer != bufp->buffer) \ { \ b = (b - old_buffer) + bufp->buffer; \ begalt = (begalt - old_buffer) + bufp->buffer; \ if (fixup_alt_jump) \ fixup_alt_jump = (fixup_alt_jump - old_buffer) + bufp->buffer;\ if (laststart) \ laststart = (laststart - old_buffer) + bufp->buffer; \ if (pending_exact) \ pending_exact = (pending_exact - old_buffer) + bufp->buffer; \ } \ } while (0) /* Since we have one byte reserved for the register number argument to {start,stop}_memory, the maximum number of groups we can report things about is what fits in that byte. */ #define MAX_REGNUM 255 /* But patterns can have more than `MAX_REGNUM' registers. We just ignore the excess. */ typedef unsigned regnum_t; /* Macros for the compile stack. */ /* Since offsets can go either forwards or backwards, this type needs to be able to hold values from -(MAX_BUF_SIZE - 1) to MAX_BUF_SIZE - 1. */ typedef int pattern_offset_t; typedef struct { pattern_offset_t begalt_offset; pattern_offset_t fixup_alt_jump; pattern_offset_t inner_group_offset; pattern_offset_t laststart_offset; regnum_t regnum; } compile_stack_elt_t; typedef struct { compile_stack_elt_t *stack; unsigned size; unsigned avail; /* Offset of next open position. */ } compile_stack_type; #define INIT_COMPILE_STACK_SIZE 32 #define COMPILE_STACK_EMPTY (compile_stack.avail == 0) #define COMPILE_STACK_FULL (compile_stack.avail == compile_stack.size) /* The next available element. */ #define COMPILE_STACK_TOP (compile_stack.stack[compile_stack.avail]) /* Structure to manage work area for range table. */ struct range_table_work_area { int *table; /* actual work area. */ int allocated; /* allocated size for work area in bytes. */ int used; /* actually used size in words. */ }; /* Make sure that WORK_AREA can hold more N multibyte characters. */ #define EXTEND_RANGE_TABLE_WORK_AREA(work_area, n) \ do { \ if (((work_area).used + (n)) * sizeof (int) > (work_area).allocated) \ { \ (work_area).allocated += 16 * sizeof (int); \ if ((work_area).table) \ (work_area).table \ = (int *) realloc ((work_area).table, (work_area).allocated); \ else \ (work_area).table \ = (int *) malloc ((work_area).allocated); \ if ((work_area).table == 0) \ FREE_STACK_RETURN (REG_ESPACE); \ } \ } while (0) /* Set a range (RANGE_START, RANGE_END) to WORK_AREA. */ #define SET_RANGE_TABLE_WORK_AREA(work_area, range_start, range_end) \ do { \ EXTEND_RANGE_TABLE_WORK_AREA ((work_area), 2); \ (work_area).table[(work_area).used++] = (range_start); \ (work_area).table[(work_area).used++] = (range_end); \ } while (0) /* Free allocated memory for WORK_AREA. */ #define FREE_RANGE_TABLE_WORK_AREA(work_area) \ do { \ if ((work_area).table) \ free ((work_area).table); \ } while (0) #define CLEAR_RANGE_TABLE_WORK_USED(work_area) ((work_area).used = 0) #define RANGE_TABLE_WORK_USED(work_area) ((work_area).used) #define RANGE_TABLE_WORK_ELT(work_area, i) ((work_area).table[i]) /* Set the bit for character C in a list. */ #define SET_LIST_BIT(c) \ (b[((unsigned char) (c)) / BYTEWIDTH] \ |= 1 << (((unsigned char) c) % BYTEWIDTH)) /* Get the next unsigned number in the uncompiled pattern. */ #define GET_UNSIGNED_NUMBER(num) \ { if (p != pend) \ { \ PATFETCH (c); \ while (ISDIGIT (c)) \ { \ if (num < 0) \ num = 0; \ num = num * 10 + c - '0'; \ if (p == pend) \ break; \ PATFETCH (c); \ } \ } \ } #define CHAR_CLASS_MAX_LENGTH 6 /* Namely, `xdigit'. */ #define IS_CHAR_CLASS(string) \ (STREQ (string, "alpha") || STREQ (string, "upper") \ || STREQ (string, "lower") || STREQ (string, "digit") \ || STREQ (string, "alnum") || STREQ (string, "xdigit") \ || STREQ (string, "space") || STREQ (string, "print") \ || STREQ (string, "punct") || STREQ (string, "graph") \ || STREQ (string, "cntrl") || STREQ (string, "blank")) #ifndef MATCH_MAY_ALLOCATE /* If we cannot allocate large objects within re_match_2_internal, we make the fail stack and register vectors global. The fail stack, we grow to the maximum size when a regexp is compiled. The register vectors, we adjust in size each time we compile a regexp, according to the number of registers it needs. */ static fail_stack_type fail_stack; /* Size with which the following vectors are currently allocated. That is so we can make them bigger as needed, but never make them smaller. */ static int regs_allocated_size; static const char ** regstart, ** regend; static const char ** old_regstart, ** old_regend; static const char **best_regstart, **best_regend; static register_info_type *reg_info; static const char **reg_dummy; static register_info_type *reg_info_dummy; /* Make the register vectors big enough for NUM_REGS registers, but don't make them smaller. */ static regex_grow_registers (num_regs) int num_regs; { if (num_regs > regs_allocated_size) { RETALLOC_IF (regstart, num_regs, const char *); RETALLOC_IF (regend, num_regs, const char *); RETALLOC_IF (old_regstart, num_regs, const char *); RETALLOC_IF (old_regend, num_regs, const char *); RETALLOC_IF (best_regstart, num_regs, const char *); RETALLOC_IF (best_regend, num_regs, const char *); RETALLOC_IF (reg_info, num_regs, register_info_type); RETALLOC_IF (reg_dummy, num_regs, const char *); RETALLOC_IF (reg_info_dummy, num_regs, register_info_type); regs_allocated_size = num_regs; } } #endif /* not MATCH_MAY_ALLOCATE */ /* `regex_compile' compiles PATTERN (of length SIZE) according to SYNTAX. Returns one of error codes defined in `regex.h', or zero for success. Assumes the `allocated' (and perhaps `buffer') and `translate' fields are set in BUFP on entry. If it succeeds, results are put in BUFP (if it returns an error, the contents of BUFP are undefined): `buffer' is the compiled pattern; `syntax' is set to SYNTAX; `used' is set to the length of the compiled pattern; `fastmap_accurate' is zero; `re_nsub' is the number of subexpressions in PATTERN; `not_bol' and `not_eol' are zero; The `fastmap' and `newline_anchor' fields are neither examined nor set. */ /* Return, freeing storage we allocated. */ #define FREE_STACK_RETURN(value) \ do { \ FREE_RANGE_TABLE_WORK_AREA (range_table_work); \ free (compile_stack.stack); \ return value; \ } while (0) static reg_errcode_t regex_compile (pattern, size, syntax, bufp) const char *pattern; int size; reg_syntax_t syntax; struct re_pattern_buffer *bufp; { /* We fetch characters from PATTERN here. Even though PATTERN is `char *' (i.e., signed), we declare these variables as unsigned, so they can be reliably used as array indices. */ register unsigned int c, c1; /* A random temporary spot in PATTERN. */ const char *p1; /* Points to the end of the buffer, where we should append. */ register unsigned char *b; /* Keeps track of unclosed groups. */ compile_stack_type compile_stack; /* Points to the current (ending) position in the pattern. */ #ifdef AIX /* `const' makes AIX compiler fail. */ char *p = pattern; #else const char *p = pattern; #endif const char *pend = pattern + size; /* How to translate the characters in the pattern. */ RE_TRANSLATE_TYPE translate = bufp->translate; /* Address of the count-byte of the most recently inserted `exactn' command. This makes it possible to tell if a new exact-match character can be added to that command or if the character requires a new `exactn' command. */ unsigned char *pending_exact = 0; /* Address of start of the most recently finished expression. This tells, e.g., postfix * where to find the start of its operand. Reset at the beginning of groups and alternatives. */ unsigned char *laststart = 0; /* Address of beginning of regexp, or inside of last group. */ unsigned char *begalt; /* Place in the uncompiled pattern (i.e., the {) to which to go back if the interval is invalid. */ const char *beg_interval; /* Address of the place where a forward jump should go to the end of the containing expression. Each alternative of an `or' -- except the last -- ends with a forward jump of this sort. */ unsigned char *fixup_alt_jump = 0; /* Counts open-groups as they are encountered. Remembered for the matching close-group on the compile stack, so the same register number is put in the stop_memory as the start_memory. */ regnum_t regnum = 0; /* Work area for range table of charset. */ struct range_table_work_area range_table_work; #ifdef DEBUG DEBUG_PRINT1 ("\nCompiling pattern: "); if (debug) { unsigned debug_count; for (debug_count = 0; debug_count < size; debug_count++) putchar (pattern[debug_count]); putchar ('\n'); } #endif /* DEBUG */ /* Initialize the compile stack. */ compile_stack.stack = TALLOC (INIT_COMPILE_STACK_SIZE, compile_stack_elt_t); if (compile_stack.stack == NULL) return REG_ESPACE; compile_stack.size = INIT_COMPILE_STACK_SIZE; compile_stack.avail = 0; range_table_work.table = 0; range_table_work.allocated = 0; /* Initialize the pattern buffer. */ bufp->syntax = syntax; bufp->fastmap_accurate = 0; bufp->not_bol = bufp->not_eol = 0; /* Set `used' to zero, so that if we return an error, the pattern printer (for debugging) will think there's no pattern. We reset it at the end. */ bufp->used = 0; /* Always count groups, whether or not bufp->no_sub is set. */ bufp->re_nsub = 0; #ifdef emacs /* bufp->multibyte is set before regex_compile is called, so don't alter it. */ #else /* not emacs */ /* Nothing is recognized as a multibyte character. */ bufp->multibyte = 0; #endif #if !defined (emacs) && !defined (SYNTAX_TABLE) /* Initialize the syntax table. */ init_syntax_once (); #endif if (bufp->allocated == 0) { if (bufp->buffer) { /* If zero allocated, but buffer is non-null, try to realloc enough space. This loses if buffer's address is bogus, but that is the user's responsibility. */ RETALLOC (bufp->buffer, INIT_BUF_SIZE, unsigned char); } else { /* Caller did not allocate a buffer. Do it for them. */ bufp->buffer = TALLOC (INIT_BUF_SIZE, unsigned char); } if (!bufp->buffer) FREE_STACK_RETURN (REG_ESPACE); bufp->allocated = INIT_BUF_SIZE; } begalt = b = bufp->buffer; /* Loop through the uncompiled pattern until we're at the end. */ while (p != pend) { PATFETCH (c); switch (c) { case '^': { if ( /* If at start of pattern, it's an operator. */ p == pattern + 1 /* If context independent, it's an operator. */ || syntax & RE_CONTEXT_INDEP_ANCHORS /* Otherwise, depends on what's come before. */ || at_begline_loc_p (pattern, p, syntax)) BUF_PUSH (begline); else goto normal_char; } break; case '$': { if ( /* If at end of pattern, it's an operator. */ p == pend /* If context independent, it's an operator. */ || syntax & RE_CONTEXT_INDEP_ANCHORS /* Otherwise, depends on what's next. */ || at_endline_loc_p (p, pend, syntax)) BUF_PUSH (endline); else goto normal_char; } break; case '+': case '?': if ((syntax & RE_BK_PLUS_QM) || (syntax & RE_LIMITED_OPS)) goto normal_char; handle_plus: case '*': /* If there is no previous pattern... */ if (!laststart) { if (syntax & RE_CONTEXT_INVALID_OPS) FREE_STACK_RETURN (REG_BADRPT); else if (!(syntax & RE_CONTEXT_INDEP_OPS)) goto normal_char; } { /* Are we optimizing this jump? */ boolean keep_string_p = false; /* 1 means zero (many) matches is allowed. */ char zero_times_ok = 0, many_times_ok = 0; /* If there is a sequence of repetition chars, collapse it down to just one (the right one). We can't combine interval operators with these because of, e.g., `a{2}*', which should only match an even number of `a's. */ for (;;) { zero_times_ok |= c != '+'; many_times_ok |= c != '?'; if (p == pend) break; PATFETCH (c); if (c == '*' || (!(syntax & RE_BK_PLUS_QM) && (c == '+' || c == '?'))) ; else if (syntax & RE_BK_PLUS_QM && c == '\\') { if (p == pend) FREE_STACK_RETURN (REG_EESCAPE); PATFETCH (c1); if (!(c1 == '+' || c1 == '?')) { PATUNFETCH; PATUNFETCH; break; } c = c1; } else { PATUNFETCH; break; } /* If we get here, we found another repeat character. */ } /* Star, etc. applied to an empty pattern is equivalent to an empty pattern. */ if (!laststart) break; /* Now we know whether or not zero matches is allowed and also whether or not two or more matches is allowed. */ if (many_times_ok) { /* More than one repetition is allowed, so put in at the end a backward relative jump from `b' to before the next jump we're going to put in below (which jumps from laststart to after this jump). But if we are at the `*' in the exact sequence `.*\n', insert an unconditional jump backwards to the ., instead of the beginning of the loop. This way we only push a failure point once, instead of every time through the loop. */ assert (p - 1 > pattern); /* Allocate the space for the jump. */ GET_BUFFER_SPACE (3); /* We know we are not at the first character of the pattern, because laststart was nonzero. And we've already incremented `p', by the way, to be the character after the `*'. Do we have to do something analogous here for null bytes, because of RE_DOT_NOT_NULL? */ if (TRANSLATE ((unsigned char)*(p - 2)) == TRANSLATE ('.') && zero_times_ok && p < pend && TRANSLATE ((unsigned char)*p) == TRANSLATE ('\n') && !(syntax & RE_DOT_NEWLINE)) { /* We have .*\n. */ STORE_JUMP (jump, b, laststart); keep_string_p = true; } else /* Anything else. */ STORE_JUMP (maybe_pop_jump, b, laststart - 3); /* We've added more stuff to the buffer. */ b += 3; } /* On failure, jump from laststart to b + 3, which will be the end of the buffer after this jump is inserted. */ GET_BUFFER_SPACE (3); INSERT_JUMP (keep_string_p ? on_failure_keep_string_jump : on_failure_jump, laststart, b + 3); pending_exact = 0; b += 3; if (!zero_times_ok) { /* At least one repetition is required, so insert a `dummy_failure_jump' before the initial `on_failure_jump' instruction of the loop. This effects a skip over that instruction the first time we hit that loop. */ GET_BUFFER_SPACE (3); INSERT_JUMP (dummy_failure_jump, laststart, laststart + 6); b += 3; } } break; case '.': laststart = b; BUF_PUSH (anychar); break; case '[': { CLEAR_RANGE_TABLE_WORK_USED (range_table_work); if (p == pend) FREE_STACK_RETURN (REG_EBRACK); /* Ensure that we have enough space to push a charset: the opcode, the length count, and the bitset; 34 bytes in all. */ GET_BUFFER_SPACE (34); laststart = b; /* We test `*p == '^' twice, instead of using an if statement, so we only need one BUF_PUSH. */ BUF_PUSH (*p == '^' ? charset_not : charset); if (*p == '^') p++; /* Remember the first position in the bracket expression. */ p1 = p; /* Push the number of bytes in the bitmap. */ BUF_PUSH ((1 << BYTEWIDTH) / BYTEWIDTH); /* Clear the whole map. */ bzero (b, (1 << BYTEWIDTH) / BYTEWIDTH); /* charset_not matches newline according to a syntax bit. */ if ((re_opcode_t) b[-2] == charset_not && (syntax & RE_HAT_LISTS_NOT_NEWLINE)) SET_LIST_BIT ('\n'); /* Read in characters and ranges, setting map bits. */ for (;;) { int len; boolean escaped_char = false; if (p == pend) FREE_STACK_RETURN (REG_EBRACK); PATFETCH (c); /* \ might escape characters inside [...] and [^...]. */ if ((syntax & RE_BACKSLASH_ESCAPE_IN_LISTS) && c == '\\') { if (p == pend) FREE_STACK_RETURN (REG_EESCAPE); PATFETCH (c); escaped_char = true; } else { /* Could be the end of the bracket expression. If it's not (i.e., when the bracket expression is `[]' so far), the ']' character bit gets set way below. */ if (c == ']' && p != p1 + 1) break; } /* If C indicates start of multibyte char, get the actual character code in C, and set the pattern pointer P to the next character boundary. */ if (bufp->multibyte && BASE_LEADING_CODE_P (c)) { PATUNFETCH; c = STRING_CHAR_AND_LENGTH (p, pend - p, len); p += len; } /* What should we do for the character which is greater than 0x7F, but not BASE_LEADING_CODE_P? XXX */ /* See if we're at the beginning of a possible character class. */ else if (!escaped_char && syntax & RE_CHAR_CLASSES && c == '[' && *p == ':') { /* Leave room for the null. */ char str[CHAR_CLASS_MAX_LENGTH + 1]; PATFETCH (c); c1 = 0; /* If pattern is `[[:'. */ if (p == pend) FREE_STACK_RETURN (REG_EBRACK); for (;;) { PATFETCH (c); if (c == ':' || c == ']' || p == pend || c1 == CHAR_CLASS_MAX_LENGTH) break; str[c1++] = c; } str[c1] = '\0'; /* If isn't a word bracketed by `[:' and `:]': undo the ending character, the letters, and leave the leading `:' and `[' (but set bits for them). */ if (c == ':' && *p == ']') { int ch; boolean is_alnum = STREQ (str, "alnum"); boolean is_alpha = STREQ (str, "alpha"); boolean is_blank = STREQ (str, "blank"); boolean is_cntrl = STREQ (str, "cntrl"); boolean is_digit = STREQ (str, "digit"); boolean is_graph = STREQ (str, "graph"); boolean is_lower = STREQ (str, "lower"); boolean is_print = STREQ (str, "print"); boolean is_punct = STREQ (str, "punct"); boolean is_space = STREQ (str, "space"); boolean is_upper = STREQ (str, "upper"); boolean is_xdigit = STREQ (str, "xdigit"); if (!IS_CHAR_CLASS (str)) FREE_STACK_RETURN (REG_ECTYPE); /* Throw away the ] at the end of the character class. */ PATFETCH (c); if (p == pend) FREE_STACK_RETURN (REG_EBRACK); for (ch = 0; ch < 1 << BYTEWIDTH; ch++) { int translated = TRANSLATE (ch); /* This was split into 3 if's to avoid an arbitrary limit in some compiler. */ if ( (is_alnum && ISALNUM (ch)) || (is_alpha && ISALPHA (ch)) || (is_blank && ISBLANK (ch)) || (is_cntrl && ISCNTRL (ch))) SET_LIST_BIT (translated); if ( (is_digit && ISDIGIT (ch)) || (is_graph && ISGRAPH (ch)) || (is_lower && ISLOWER (ch)) || (is_print && ISPRINT (ch))) SET_LIST_BIT (translated); if ( (is_punct && ISPUNCT (ch)) || (is_space && ISSPACE (ch)) || (is_upper && ISUPPER (ch)) || (is_xdigit && ISXDIGIT (ch))) SET_LIST_BIT (translated); } /* Repeat the loop. */ continue; } else { c1++; while (c1--) PATUNFETCH; SET_LIST_BIT ('['); /* Because the `:' may starts the range, we can't simply set bit and repeat the loop. Instead, just set it to C and handle below. */ c = ':'; } } if (p < pend && p[0] == '-' && p[1] != ']') { /* Discard the `-'. */ PATFETCH (c1); /* Fetch the character which ends the range. */ PATFETCH (c1); if (bufp->multibyte && BASE_LEADING_CODE_P (c1)) { PATUNFETCH; c1 = STRING_CHAR_AND_LENGTH (p, pend - p, len); p += len; } if (SINGLE_BYTE_CHAR_P (c) && ! SINGLE_BYTE_CHAR_P (c1)) { /* Handle a range such as \177-\377 in multibyte mode. Split that into two ranges,, the low one ending at 0237, and the high one starting at ...040. */ int c1_base = (c1 & ~0177) | 040; SET_RANGE_TABLE_WORK_AREA (range_table_work, c, c1); c1 = 0237; } else if (!SAME_CHARSET_P (c, c1)) FREE_STACK_RETURN (REG_ERANGE); } else /* Range from C to C. */ c1 = c; /* Set the range ... */ if (SINGLE_BYTE_CHAR_P (c)) /* ... into bitmap. */ { unsigned this_char; int range_start = c, range_end = c1; /* If the start is after the end, the range is empty. */ if (range_start > range_end) { if (syntax & RE_NO_EMPTY_RANGES) FREE_STACK_RETURN (REG_ERANGE); /* Else, repeat the loop. */ } else { for (this_char = range_start; this_char <= range_end; this_char++) SET_LIST_BIT (TRANSLATE (this_char)); } } else /* ... into range table. */ SET_RANGE_TABLE_WORK_AREA (range_table_work, c, c1); } /* Discard any (non)matching list bytes that are all 0 at the end of the map. Decrease the map-length byte too. */ while ((int) b[-1] > 0 && b[b[-1] - 1] == 0) b[-1]--; b += b[-1]; /* Build real range table from work area. */ if (RANGE_TABLE_WORK_USED (range_table_work)) { int i; int used = RANGE_TABLE_WORK_USED (range_table_work); /* Allocate space for COUNT + RANGE_TABLE. Needs two bytes for COUNT and three bytes for each character. */ GET_BUFFER_SPACE (2 + used * 3); /* Indicate the existence of range table. */ laststart[1] |= 0x80; STORE_NUMBER_AND_INCR (b, used / 2); for (i = 0; i < used; i++) STORE_CHARACTER_AND_INCR (b, RANGE_TABLE_WORK_ELT (range_table_work, i)); } } break; case '(': if (syntax & RE_NO_BK_PARENS) goto handle_open; else goto normal_char; case ')': if (syntax & RE_NO_BK_PARENS) goto handle_close; else goto normal_char; case '\n': if (syntax & RE_NEWLINE_ALT) goto handle_alt; else goto normal_char; case '|': if (syntax & RE_NO_BK_VBAR) goto handle_alt; else goto normal_char; case '{': if (syntax & RE_INTERVALS && syntax & RE_NO_BK_BRACES) goto handle_interval; else goto normal_char; case '\\': if (p == pend) FREE_STACK_RETURN (REG_EESCAPE); /* Do not translate the character after the \, so that we can distinguish, e.g., \B from \b, even if we normally would translate, e.g., B to b. */ PATFETCH_RAW (c); switch (c) { case '(': if (syntax & RE_NO_BK_PARENS) goto normal_backslash; handle_open: bufp->re_nsub++; regnum++; if (COMPILE_STACK_FULL) { RETALLOC (compile_stack.stack, compile_stack.size << 1, compile_stack_elt_t); if (compile_stack.stack == NULL) return REG_ESPACE; compile_stack.size <<= 1; } /* These are the values to restore when we hit end of this group. They are all relative offsets, so that if the whole pattern moves because of realloc, they will still be valid. */ COMPILE_STACK_TOP.begalt_offset = begalt - bufp->buffer; COMPILE_STACK_TOP.fixup_alt_jump = fixup_alt_jump ? fixup_alt_jump - bufp->buffer + 1 : 0; COMPILE_STACK_TOP.laststart_offset = b - bufp->buffer; COMPILE_STACK_TOP.regnum = regnum; /* We will eventually replace the 0 with the number of groups inner to this one. But do not push a start_memory for groups beyond the last one we can represent in the compiled pattern. */ if (regnum <= MAX_REGNUM) { COMPILE_STACK_TOP.inner_group_offset = b - bufp->buffer + 2; BUF_PUSH_3 (start_memory, regnum, 0); } compile_stack.avail++; fixup_alt_jump = 0; laststart = 0; begalt = b; /* If we've reached MAX_REGNUM groups, then this open won't actually generate any code, so we'll have to clear pending_exact explicitly. */ pending_exact = 0; break; case ')': if (syntax & RE_NO_BK_PARENS) goto normal_backslash; if (COMPILE_STACK_EMPTY) if (syntax & RE_UNMATCHED_RIGHT_PAREN_ORD) goto normal_backslash; else FREE_STACK_RETURN (REG_ERPAREN); handle_close: if (fixup_alt_jump) { /* Push a dummy failure point at the end of the alternative for a possible future `pop_failure_jump' to pop. See comments at `push_dummy_failure' in `re_match_2'. */ BUF_PUSH (push_dummy_failure); /* We allocated space for this jump when we assigned to `fixup_alt_jump', in the `handle_alt' case below. */ STORE_JUMP (jump_past_alt, fixup_alt_jump, b - 1); } /* See similar code for backslashed left paren above. */ if (COMPILE_STACK_EMPTY) if (syntax & RE_UNMATCHED_RIGHT_PAREN_ORD) goto normal_char; else FREE_STACK_RETURN (REG_ERPAREN); /* Since we just checked for an empty stack above, this ``can't happen''. */ assert (compile_stack.avail != 0); { /* We don't just want to restore into `regnum', because later groups should continue to be numbered higher, as in `(ab)c(de)' -- the second group is #2. */ regnum_t this_group_regnum; compile_stack.avail--; begalt = bufp->buffer + COMPILE_STACK_TOP.begalt_offset; fixup_alt_jump = COMPILE_STACK_TOP.fixup_alt_jump ? bufp->buffer + COMPILE_STACK_TOP.fixup_alt_jump - 1 : 0; laststart = bufp->buffer + COMPILE_STACK_TOP.laststart_offset; this_group_regnum = COMPILE_STACK_TOP.regnum; /* If we've reached MAX_REGNUM groups, then this open won't actually generate any code, so we'll have to clear pending_exact explicitly. */ pending_exact = 0; /* We're at the end of the group, so now we know how many groups were inside this one. */ if (this_group_regnum <= MAX_REGNUM) { unsigned char *inner_group_loc = bufp->buffer + COMPILE_STACK_TOP.inner_group_offset; *inner_group_loc = regnum - this_group_regnum; BUF_PUSH_3 (stop_memory, this_group_regnum, regnum - this_group_regnum); } } break; case '|': /* `\|'. */ if (syntax & RE_LIMITED_OPS || syntax & RE_NO_BK_VBAR) goto normal_backslash; handle_alt: if (syntax & RE_LIMITED_OPS) goto normal_char; /* Insert before the previous alternative a jump which jumps to this alternative if the former fails. */ GET_BUFFER_SPACE (3); INSERT_JUMP (on_failure_jump, begalt, b + 6); pending_exact = 0; b += 3; /* The alternative before this one has a jump after it which gets executed if it gets matched. Adjust that jump so it will jump to this alternative's analogous jump (put in below, which in turn will jump to the next (if any) alternative's such jump, etc.). The last such jump jumps to the correct final destination. A picture: _____ _____ | | | | | v | v a | b | c If we are at `b', then fixup_alt_jump right now points to a three-byte space after `a'. We'll put in the jump, set fixup_alt_jump to right after `b', and leave behind three bytes which we'll fill in when we get to after `c'. */ if (fixup_alt_jump) STORE_JUMP (jump_past_alt, fixup_alt_jump, b); /* Mark and leave space for a jump after this alternative, to be filled in later either by next alternative or when know we're at the end of a series of alternatives. */ fixup_alt_jump = b; GET_BUFFER_SPACE (3); b += 3; laststart = 0; begalt = b; break; case '{': /* If \{ is a literal. */ if (!(syntax & RE_INTERVALS) /* If we're at `\{' and it's not the open-interval operator. */ || ((syntax & RE_INTERVALS) && (syntax & RE_NO_BK_BRACES)) || (p - 2 == pattern && p == pend)) goto normal_backslash; handle_interval: { /* If got here, then the syntax allows intervals. */ /* At least (most) this many matches must be made. */ int lower_bound = -1, upper_bound = -1; beg_interval = p - 1; if (p == pend) { if (syntax & RE_NO_BK_BRACES) goto unfetch_interval; else FREE_STACK_RETURN (REG_EBRACE); } GET_UNSIGNED_NUMBER (lower_bound); if (c == ',') { GET_UNSIGNED_NUMBER (upper_bound); if (upper_bound < 0) upper_bound = RE_DUP_MAX; } else /* Interval such as `{1}' => match exactly once. */ upper_bound = lower_bound; if (lower_bound < 0 || upper_bound > RE_DUP_MAX || lower_bound > upper_bound) { if (syntax & RE_NO_BK_BRACES) goto unfetch_interval; else FREE_STACK_RETURN (REG_BADBR); } if (!(syntax & RE_NO_BK_BRACES)) { if (c != '\\') FREE_STACK_RETURN (REG_EBRACE); PATFETCH (c); } if (c != '}') { if (syntax & RE_NO_BK_BRACES) goto unfetch_interval; else FREE_STACK_RETURN (REG_BADBR); } /* We just parsed a valid interval. */ /* If it's invalid to have no preceding re. */ if (!laststart) { if (syntax & RE_CONTEXT_INVALID_OPS) FREE_STACK_RETURN (REG_BADRPT); else if (syntax & RE_CONTEXT_INDEP_OPS) laststart = b; else goto unfetch_interval; } /* If the upper bound is zero, don't want to succeed at all; jump from `laststart' to `b + 3', which will be the end of the buffer after we insert the jump. */ if (upper_bound == 0) { GET_BUFFER_SPACE (3); INSERT_JUMP (jump, laststart, b + 3); b += 3; } /* Otherwise, we have a nontrivial interval. When we're all done, the pattern will look like: set_number_at set_number_at succeed_n jump_n (The upper bound and `jump_n' are omitted if `upper_bound' is 1, though.) */ else { /* If the upper bound is > 1, we need to insert more at the end of the loop. */ unsigned nbytes = 10 + (upper_bound > 1) * 10; GET_BUFFER_SPACE (nbytes); /* Initialize lower bound of the `succeed_n', even though it will be set during matching by its attendant `set_number_at' (inserted next), because `re_compile_fastmap' needs to know. Jump to the `jump_n' we might insert below. */ INSERT_JUMP2 (succeed_n, laststart, b + 5 + (upper_bound > 1) * 5, lower_bound); b += 5; /* Code to initialize the lower bound. Insert before the `succeed_n'. The `5' is the last two bytes of this `set_number_at', plus 3 bytes of the following `succeed_n'. */ insert_op2 (set_number_at, laststart, 5, lower_bound, b); b += 5; if (upper_bound > 1) { /* More than one repetition is allowed, so append a backward jump to the `succeed_n' that starts this interval. When we've reached this during matching, we'll have matched the interval once, so jump back only `upper_bound - 1' times. */ STORE_JUMP2 (jump_n, b, laststart + 5, upper_bound - 1); b += 5; /* The location we want to set is the second parameter of the `jump_n'; that is `b-2' as an absolute address. `laststart' will be the `set_number_at' we're about to insert; `laststart+3' the number to set, the source for the relative address. But we are inserting into the middle of the pattern -- so everything is getting moved up by 5. Conclusion: (b - 2) - (laststart + 3) + 5, i.e., b - laststart. We insert this at the beginning of the loop so that if we fail during matching, we'll reinitialize the bounds. */ insert_op2 (set_number_at, laststart, b - laststart, upper_bound - 1, b); b += 5; } } pending_exact = 0; beg_interval = NULL; } break; unfetch_interval: /* If an invalid interval, match the characters as literals. */ assert (beg_interval); p = beg_interval; beg_interval = NULL; /* normal_char and normal_backslash need `c'. */ PATFETCH (c); if (!(syntax & RE_NO_BK_BRACES)) { if (p > pattern && p[-1] == '\\') goto normal_backslash; } goto normal_char; #ifdef emacs /* There is no way to specify the before_dot and after_dot operators. rms says this is ok. --karl */ case '=': BUF_PUSH (at_dot); break; case 's': laststart = b; PATFETCH (c); BUF_PUSH_2 (syntaxspec, syntax_spec_code[c]); break; case 'S': laststart = b; PATFETCH (c); BUF_PUSH_2 (notsyntaxspec, syntax_spec_code[c]); break; case 'c': laststart = b; PATFETCH_RAW (c); BUF_PUSH_2 (categoryspec, c); break; case 'C': laststart = b; PATFETCH_RAW (c); BUF_PUSH_2 (notcategoryspec, c); break; #endif /* emacs */ case 'w': laststart = b; BUF_PUSH (wordchar); break; case 'W': laststart = b; BUF_PUSH (notwordchar); break; case '<': BUF_PUSH (wordbeg); break; case '>': BUF_PUSH (wordend); break; case 'b': BUF_PUSH (wordbound); break; case 'B': BUF_PUSH (notwordbound); break; case '`': BUF_PUSH (begbuf); break; case '\'': BUF_PUSH (endbuf); break; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (syntax & RE_NO_BK_REFS) goto normal_char; c1 = c - '0'; if (c1 > regnum) FREE_STACK_RETURN (REG_ESUBREG); /* Can't back reference to a subexpression if inside of it. */ if (group_in_compile_stack (compile_stack, c1)) goto normal_char; laststart = b; BUF_PUSH_2 (duplicate, c1); break; case '+': case '?': if (syntax & RE_BK_PLUS_QM) goto handle_plus; else goto normal_backslash; default: normal_backslash: /* You might think it would be useful for \ to mean not to translate; but if we don't translate it it will never match anything. */ c = TRANSLATE (c); goto normal_char; } break; default: /* Expects the character in `c'. */ normal_char: p1 = p - 1; /* P1 points the head of C. */ #ifdef emacs if (bufp->multibyte) { c = STRING_CHAR (p1, pend - p1); c = TRANSLATE (c); /* Set P to the next character boundary. */ p += MULTIBYTE_FORM_LENGTH (p1, pend - p1) - 1; } #endif /* If no exactn currently being built. */ if (!pending_exact /* If last exactn not at current position. */ || pending_exact + *pending_exact + 1 != b /* We have only one byte following the exactn for the count. */ || *pending_exact >= (1 << BYTEWIDTH) - (p - p1) /* If followed by a repetition operator. */ || (p != pend && (*p == '*' || *p == '^')) || ((syntax & RE_BK_PLUS_QM) ? p + 1 < pend && *p == '\\' && (p[1] == '+' || p[1] == '?') : p != pend && (*p == '+' || *p == '?')) || ((syntax & RE_INTERVALS) && ((syntax & RE_NO_BK_BRACES) ? p != pend && *p == '{' : p + 1 < pend && p[0] == '\\' && p[1] == '{'))) { /* Start building a new exactn. */ laststart = b; BUF_PUSH_2 (exactn, 0); pending_exact = b - 1; } #ifdef emacs if (! SINGLE_BYTE_CHAR_P (c)) { unsigned char work[4], *str; int i = CHAR_STRING (c, work, str); int j; for (j = 0; j < i; j++) { BUF_PUSH (str[j]); (*pending_exact)++; } } else #endif { BUF_PUSH (c); (*pending_exact)++; } break; } /* switch (c) */ } /* while p != pend */ /* Through the pattern now. */ if (fixup_alt_jump) STORE_JUMP (jump_past_alt, fixup_alt_jump, b); if (!COMPILE_STACK_EMPTY) FREE_STACK_RETURN (REG_EPAREN); /* If we don't want backtracking, force success the first time we reach the end of the compiled pattern. */ if (syntax & RE_NO_POSIX_BACKTRACKING) BUF_PUSH (succeed); free (compile_stack.stack); /* We have succeeded; set the length of the buffer. */ bufp->used = b - bufp->buffer; #ifdef DEBUG if (debug) { DEBUG_PRINT1 ("\nCompiled pattern: \n"); print_compiled_pattern (bufp); } #endif /* DEBUG */ #ifndef MATCH_MAY_ALLOCATE /* Initialize the failure stack to the largest possible stack. This isn't necessary unless we're trying to avoid calling alloca in the search and match routines. */ { int num_regs = bufp->re_nsub + 1; if (fail_stack.size < re_max_failures * TYPICAL_FAILURE_SIZE) { fail_stack.size = re_max_failures * TYPICAL_FAILURE_SIZE; #ifdef emacs if (! fail_stack.stack) fail_stack.stack = (fail_stack_elt_t *) xmalloc (fail_stack.size * sizeof (fail_stack_elt_t)); else fail_stack.stack = (fail_stack_elt_t *) xrealloc (fail_stack.stack, (fail_stack.size * sizeof (fail_stack_elt_t))); #else /* not emacs */ if (! fail_stack.stack) fail_stack.stack = (fail_stack_elt_t *) malloc (fail_stack.size * sizeof (fail_stack_elt_t)); else fail_stack.stack = (fail_stack_elt_t *) realloc (fail_stack.stack, (fail_stack.size * sizeof (fail_stack_elt_t))); #endif /* not emacs */ } regex_grow_registers (num_regs); } #endif /* not MATCH_MAY_ALLOCATE */ return REG_NOERROR; } /* regex_compile */ /* Subroutines for `regex_compile'. */ /* Store OP at LOC followed by two-byte integer parameter ARG. */ static void store_op1 (op, loc, arg) re_opcode_t op; unsigned char *loc; int arg; { *loc = (unsigned char) op; STORE_NUMBER (loc + 1, arg); } /* Like `store_op1', but for two two-byte parameters ARG1 and ARG2. */ static void store_op2 (op, loc, arg1, arg2) re_opcode_t op; unsigned char *loc; int arg1, arg2; { *loc = (unsigned char) op; STORE_NUMBER (loc + 1, arg1); STORE_NUMBER (loc + 3, arg2); } /* Copy the bytes from LOC to END to open up three bytes of space at LOC for OP followed by two-byte integer parameter ARG. */ static void insert_op1 (op, loc, arg, end) re_opcode_t op; unsigned char *loc; int arg; unsigned char *end; { register unsigned char *pfrom = end; register unsigned char *pto = end + 3; while (pfrom != loc) *--pto = *--pfrom; store_op1 (op, loc, arg); } /* Like `insert_op1', but for two two-byte parameters ARG1 and ARG2. */ static void insert_op2 (op, loc, arg1, arg2, end) re_opcode_t op; unsigned char *loc; int arg1, arg2; unsigned char *end; { register unsigned char *pfrom = end; register unsigned char *pto = end + 5; while (pfrom != loc) *--pto = *--pfrom; store_op2 (op, loc, arg1, arg2); } /* P points to just after a ^ in PATTERN. Return true if that ^ comes after an alternative or a begin-subexpression. We assume there is at least one character before the ^. */ static boolean at_begline_loc_p (pattern, p, syntax) const char *pattern, *p; reg_syntax_t syntax; { const char *prev = p - 2; boolean prev_prev_backslash = prev > pattern && prev[-1] == '\\'; return /* After a subexpression? */ (*prev == '(' && (syntax & RE_NO_BK_PARENS || prev_prev_backslash)) /* After an alternative? */ || (*prev == '|' && (syntax & RE_NO_BK_VBAR || prev_prev_backslash)); } /* The dual of at_begline_loc_p. This one is for $. We assume there is at least one character after the $, i.e., `P < PEND'. */ static boolean at_endline_loc_p (p, pend, syntax) const char *p, *pend; int syntax; { const char *next = p; boolean next_backslash = *next == '\\'; const char *next_next = p + 1 < pend ? p + 1 : 0; return /* Before a subexpression? */ (syntax & RE_NO_BK_PARENS ? *next == ')' : next_backslash && next_next && *next_next == ')') /* Before an alternative? */ || (syntax & RE_NO_BK_VBAR ? *next == '|' : next_backslash && next_next && *next_next == '|'); } /* Returns true if REGNUM is in one of COMPILE_STACK's elements and false if it's not. */ static boolean group_in_compile_stack (compile_stack, regnum) compile_stack_type compile_stack; regnum_t regnum; { int this_element; for (this_element = compile_stack.avail - 1; this_element >= 0; this_element--) if (compile_stack.stack[this_element].regnum == regnum) return true; return false; } /* re_compile_fastmap computes a ``fastmap'' for the compiled pattern in BUFP. A fastmap records which of the (1 << BYTEWIDTH) possible characters can start a string that matches the pattern. This fastmap is used by re_search to skip quickly over impossible starting points. The caller must supply the address of a (1 << BYTEWIDTH)-byte data area as BUFP->fastmap. We set the `fastmap', `fastmap_accurate', and `can_be_null' fields in the pattern buffer. Returns 0 if we succeed, -2 if an internal error. */ int re_compile_fastmap (bufp) struct re_pattern_buffer *bufp; { int i, j, k; #ifdef MATCH_MAY_ALLOCATE fail_stack_type fail_stack; #endif #ifndef REGEX_MALLOC char *destination; #endif /* We don't push any register information onto the failure stack. */ unsigned num_regs = 0; register char *fastmap = bufp->fastmap; unsigned char *pattern = bufp->buffer; unsigned long size = bufp->used; unsigned char *p = pattern; register unsigned char *pend = pattern + size; /* This holds the pointer to the failure stack, when it is allocated relocatably. */ fail_stack_elt_t *failure_stack_ptr; /* Assume that each path through the pattern can be null until proven otherwise. We set this false at the bottom of switch statement, to which we get only if a particular path doesn't match the empty string. */ boolean path_can_be_null = true; /* We aren't doing a `succeed_n' to begin with. */ boolean succeed_n_p = false; /* If all elements for base leading-codes in fastmap is set, this flag is set true. */ boolean match_any_multibyte_characters = false; /* Maximum code of simple (single byte) character. */ int simple_char_max; assert (fastmap != NULL && p != NULL); INIT_FAIL_STACK (); bzero (fastmap, 1 << BYTEWIDTH); /* Assume nothing's valid. */ bufp->fastmap_accurate = 1; /* It will be when we're done. */ bufp->can_be_null = 0; while (1) { if (p == pend || *p == succeed) { /* We have reached the (effective) end of pattern. */ if (!FAIL_STACK_EMPTY ()) { bufp->can_be_null |= path_can_be_null; /* Reset for next path. */ path_can_be_null = true; p = fail_stack.stack[--fail_stack.avail].pointer; continue; } else break; } /* We should never be about to go beyond the end of the pattern. */ assert (p < pend); switch (SWITCH_ENUM_CAST ((re_opcode_t) *p++)) { /* I guess the idea here is to simply not bother with a fastmap if a backreference is used, since it's too hard to figure out the fastmap for the corresponding group. Setting `can_be_null' stops `re_search_2' from using the fastmap, so that is all we do. */ case duplicate: bufp->can_be_null = 1; goto done; /* Following are the cases which match a character. These end with `break'. */ case exactn: fastmap[p[1]] = 1; break; #ifndef emacs case charset: for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--) if (p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH))) fastmap[j] = 1; break; case charset_not: /* Chars beyond end of map must be allowed. */ for (j = *p * BYTEWIDTH; j < (1 << BYTEWIDTH); j++) fastmap[j] = 1; for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--) if (!(p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH)))) fastmap[j] = 1; break; case wordchar: for (j = 0; j < (1 << BYTEWIDTH); j++) if (SYNTAX (j) == Sword) fastmap[j] = 1; break; case notwordchar: for (j = 0; j < (1 << BYTEWIDTH); j++) if (SYNTAX (j) != Sword) fastmap[j] = 1; break; #else /* emacs */ case charset: for (j = CHARSET_BITMAP_SIZE (&p[-1]) * BYTEWIDTH - 1, p++; j >= 0; j--) if (p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH))) fastmap[j] = 1; if (CHARSET_RANGE_TABLE_EXISTS_P (&p[-2]) && match_any_multibyte_characters == false) { /* Set fastmap[I] 1 where I is a base leading code of each multibyte character in the range table. */ int c, count; /* Make P points the range table. */ p += CHARSET_BITMAP_SIZE (&p[-2]); /* Extract the number of ranges in range table into COUNT. */ EXTRACT_NUMBER_AND_INCR (count, p); for (; count > 0; count--, p += 2 * 3) /* XXX */ { /* Extract the start of each range. */ EXTRACT_CHARACTER (c, p); j = CHAR_CHARSET (c); fastmap[CHARSET_LEADING_CODE_BASE (j)] = 1; } } break; case charset_not: /* Chars beyond end of bitmap are possible matches. All the single-byte codes can occur in multibyte buffers. So any that are not listed in the charset are possible matches, even in multibyte buffers. */ simple_char_max = (1 << BYTEWIDTH); for (j = CHARSET_BITMAP_SIZE (&p[-1]) * BYTEWIDTH; j < simple_char_max; j++) fastmap[j] = 1; for (j = CHARSET_BITMAP_SIZE (&p[-1]) * BYTEWIDTH - 1, p++; j >= 0; j--) if (!(p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH)))) fastmap[j] = 1; if (bufp->multibyte) /* Any character set can possibly contain a character which doesn't match the specified set of characters. */ { set_fastmap_for_multibyte_characters: if (match_any_multibyte_characters == false) { for (j = 0x80; j < 0xA0; j++) /* XXX */ if (BASE_LEADING_CODE_P (j)) fastmap[j] = 1; match_any_multibyte_characters = true; } } break; case wordchar: /* All the single-byte codes can occur in multibyte buffers, and they may have word syntax. So do consider them. */ simple_char_max = (1 << BYTEWIDTH); for (j = 0; j < simple_char_max; j++) if (SYNTAX (j) == Sword) fastmap[j] = 1; if (bufp->multibyte) /* Any character set can possibly contain a character whose syntax is `Sword'. */ goto set_fastmap_for_multibyte_characters; break; case notwordchar: /* All the single-byte codes can occur in multibyte buffers, and they may not have word syntax. So do consider them. */ simple_char_max = (1 << BYTEWIDTH); for (j = 0; j < simple_char_max; j++) if (SYNTAX (j) != Sword) fastmap[j] = 1; if (bufp->multibyte) /* Any character set can possibly contain a character whose syntax is not `Sword'. */ goto set_fastmap_for_multibyte_characters; break; #endif case anychar: { int fastmap_newline = fastmap['\n']; /* `.' matches anything, except perhaps newline. Even in a multibyte buffer, it should match any conceivable byte value for the fastmap. */ if (bufp->multibyte) match_any_multibyte_characters = true; simple_char_max = (1 << BYTEWIDTH); for (j = 0; j < simple_char_max; j++) fastmap[j] = 1; /* ... except perhaps newline. */ if (!(bufp->syntax & RE_DOT_NEWLINE)) fastmap['\n'] = fastmap_newline; /* Return if we have already set `can_be_null'; if we have, then the fastmap is irrelevant. Something's wrong here. */ else if (bufp->can_be_null) goto done; /* Otherwise, have to check alternative paths. */ break; } #ifdef emacs case wordbound: case notwordbound: case wordbeg: case wordend: case notsyntaxspec: case syntaxspec: /* This match depends on text properties. These end with aborting optimizations. */ bufp->can_be_null = 1; goto done; #if 0 k = *p++; simple_char_max = bufp->multibyte ? 0x80 : (1 << BYTEWIDTH); for (j = 0; j < simple_char_max; j++) if (SYNTAX (j) == (enum syntaxcode) k) fastmap[j] = 1; if (bufp->multibyte) /* Any character set can possibly contain a character whose syntax is K. */ goto set_fastmap_for_multibyte_characters; break; case notsyntaxspec: k = *p++; simple_char_max = bufp->multibyte ? 0x80 : (1 << BYTEWIDTH); for (j = 0; j < simple_char_max; j++) if (SYNTAX (j) != (enum syntaxcode) k) fastmap[j] = 1; if (bufp->multibyte) /* Any character set can possibly contain a character whose syntax is not K. */ goto set_fastmap_for_multibyte_characters; break; #endif case categoryspec: k = *p++; simple_char_max = (1 << BYTEWIDTH); for (j = 0; j < simple_char_max; j++) if (CHAR_HAS_CATEGORY (j, k)) fastmap[j] = 1; if (bufp->multibyte) /* Any character set can possibly contain a character whose category is K. */ goto set_fastmap_for_multibyte_characters; break; case notcategoryspec: k = *p++; simple_char_max = (1 << BYTEWIDTH); for (j = 0; j < simple_char_max; j++) if (!CHAR_HAS_CATEGORY (j, k)) fastmap[j] = 1; if (bufp->multibyte) /* Any character set can possibly contain a character whose category is not K. */ goto set_fastmap_for_multibyte_characters; break; /* All cases after this match the empty string. These end with `continue'. */ case before_dot: case at_dot: case after_dot: continue; #endif /* emacs */ case no_op: case begline: case endline: case begbuf: case endbuf: #ifndef emacs case wordbound: case notwordbound: case wordbeg: case wordend: #endif case push_dummy_failure: continue; case jump_n: case pop_failure_jump: case maybe_pop_jump: case jump: case jump_past_alt: case dummy_failure_jump: EXTRACT_NUMBER_AND_INCR (j, p); p += j; if (j > 0) continue; /* Jump backward implies we just went through the body of a loop and matched nothing. Opcode jumped to should be `on_failure_jump' or `succeed_n'. Just treat it like an ordinary jump. For a * loop, it has pushed its failure point already; if so, discard that as redundant. */ if ((re_opcode_t) *p != on_failure_jump && (re_opcode_t) *p != succeed_n) continue; p++; EXTRACT_NUMBER_AND_INCR (j, p); p += j; /* If what's on the stack is where we are now, pop it. */ if (!FAIL_STACK_EMPTY () && fail_stack.stack[fail_stack.avail - 1].pointer == p) fail_stack.avail--; continue; case on_failure_jump: case on_failure_keep_string_jump: handle_on_failure_jump: EXTRACT_NUMBER_AND_INCR (j, p); /* For some patterns, e.g., `(a?)?', `p+j' here points to the end of the pattern. We don't want to push such a point, since when we restore it above, entering the switch will increment `p' past the end of the pattern. We don't need to push such a point since we obviously won't find any more fastmap entries beyond `pend'. Such a pattern can match the null string, though. */ if (p + j < pend) { if (!PUSH_PATTERN_OP (p + j, fail_stack)) { RESET_FAIL_STACK (); return -2; } } else bufp->can_be_null = 1; if (succeed_n_p) { EXTRACT_NUMBER_AND_INCR (k, p); /* Skip the n. */ succeed_n_p = false; } continue; case succeed_n: /* Get to the number of times to succeed. */ p += 2; /* Increment p past the n for when k != 0. */ EXTRACT_NUMBER_AND_INCR (k, p); if (k == 0) { p -= 4; succeed_n_p = true; /* Spaghetti code alert. */ goto handle_on_failure_jump; } continue; case set_number_at: p += 4; continue; case start_memory: case stop_memory: p += 2; continue; default: abort (); /* We have listed all the cases. */ } /* switch *p++ */ /* Getting here means we have found the possible starting characters for one path of the pattern -- and that the empty string does not match. We need not follow this path further. Instead, look at the next alternative (remembered on the stack), or quit if no more. The test at the top of the loop does these things. */ path_can_be_null = false; p = pend; } /* while p */ /* Set `can_be_null' for the last path (also the first path, if the pattern is empty). */ bufp->can_be_null |= path_can_be_null; done: RESET_FAIL_STACK (); return 0; } /* re_compile_fastmap */ /* Set REGS to hold NUM_REGS registers, storing them in STARTS and ENDS. Subsequent matches using PATTERN_BUFFER and REGS will use this memory for recording register information. STARTS and ENDS must be allocated using the malloc library routine, and must each be at least NUM_REGS * sizeof (regoff_t) bytes long. If NUM_REGS == 0, then subsequent matches should allocate their own register data. Unless this function is called, the first search or match using PATTERN_BUFFER will allocate its own register data, without freeing the old data. */ void re_set_registers (bufp, regs, num_regs, starts, ends) struct re_pattern_buffer *bufp; struct re_registers *regs; unsigned num_regs; regoff_t *starts, *ends; { if (num_regs) { bufp->regs_allocated = REGS_REALLOCATE; regs->num_regs = num_regs; regs->start = starts; regs->end = ends; } else { bufp->regs_allocated = REGS_UNALLOCATED; regs->num_regs = 0; regs->start = regs->end = (regoff_t *) 0; } } /* Searching routines. */ /* Like re_search_2, below, but only one string is specified, and doesn't let you say where to stop matching. */ int re_search (bufp, string, size, startpos, range, regs) struct re_pattern_buffer *bufp; const char *string; int size, startpos, range; struct re_registers *regs; { return re_search_2 (bufp, NULL, 0, string, size, startpos, range, regs, size); } /* End address of virtual concatenation of string. */ #define STOP_ADDR_VSTRING(P) \ (((P) >= size1 ? string2 + size2 : string1 + size1)) /* Address of POS in the concatenation of virtual string. */ #define POS_ADDR_VSTRING(POS) \ (((POS) >= size1 ? string2 - size1 : string1) + (POS)) /* Using the compiled pattern in BUFP->buffer, first tries to match the virtual concatenation of STRING1 and STRING2, starting first at index STARTPOS, then at STARTPOS + 1, and so on. STRING1 and STRING2 have length SIZE1 and SIZE2, respectively. RANGE is how far to scan while trying to match. RANGE = 0 means try only at STARTPOS; in general, the last start tried is STARTPOS + RANGE. In REGS, return the indices of the virtual concatenation of STRING1 and STRING2 that matched the entire BUFP->buffer and its contained subexpressions. Do not consider matching one past the index STOP in the virtual concatenation of STRING1 and STRING2. We return either the position in the strings at which the match was found, -1 if no match, or -2 if error (such as failure stack overflow). */ int re_search_2 (bufp, string1, size1, string2, size2, startpos, range, regs, stop) struct re_pattern_buffer *bufp; const char *string1, *string2; int size1, size2; int startpos; int range; struct re_registers *regs; int stop; { int val; register char *fastmap = bufp->fastmap; register RE_TRANSLATE_TYPE translate = bufp->translate; int total_size = size1 + size2; int endpos = startpos + range; int anchored_start = 0; /* Nonzero if we have to concern multibyte character. */ int multibyte = bufp->multibyte; /* Check for out-of-range STARTPOS. */ if (startpos < 0 || startpos > total_size) return -1; /* Fix up RANGE if it might eventually take us outside the virtual concatenation of STRING1 and STRING2. Make sure we won't move STARTPOS below 0 or above TOTAL_SIZE. */ if (endpos < 0) range = 0 - startpos; else if (endpos > total_size) range = total_size - startpos; /* If the search isn't to be a backwards one, don't waste time in a search for a pattern anchored at beginning of buffer. */ if (bufp->used > 0 && (re_opcode_t) bufp->buffer[0] == begbuf && range > 0) { if (startpos > 0) return -1; else range = 0; } #ifdef emacs /* In a forward search for something that starts with \=. don't keep searching past point. */ if (bufp->used > 0 && (re_opcode_t) bufp->buffer[0] == at_dot && range > 0) { range = PT_BYTE - BEGV_BYTE - startpos; if (range < 0) return -1; } #endif /* emacs */ /* Update the fastmap now if not correct already. */ if (fastmap && !bufp->fastmap_accurate) if (re_compile_fastmap (bufp) == -2) return -2; /* See whether the pattern is anchored. */ if (bufp->buffer[0] == begline) anchored_start = 1; #ifdef emacs gl_state.object = re_match_object; { int adjpos = NILP (re_match_object) || BUFFERP (re_match_object); int charpos = SYNTAX_TABLE_BYTE_TO_CHAR (startpos + adjpos); SETUP_SYNTAX_TABLE_FOR_OBJECT (re_match_object, charpos, 1); } #endif /* Loop through the string, looking for a place to start matching. */ for (;;) { /* If the pattern is anchored, skip quickly past places we cannot match. We don't bother to treat startpos == 0 specially because that case doesn't repeat. */ if (anchored_start && startpos > 0) { if (! (bufp->newline_anchor && ((startpos <= size1 ? string1[startpos - 1] : string2[startpos - size1 - 1]) == '\n'))) goto advance; } /* If a fastmap is supplied, skip quickly over characters that cannot be the start of a match. If the pattern can match the null string, however, we don't need to skip characters; we want the first null string. */ if (fastmap && startpos < total_size && !bufp->can_be_null) { register const char *d; register unsigned int buf_ch; d = POS_ADDR_VSTRING (startpos); if (range > 0) /* Searching forwards. */ { register int lim = 0; int irange = range; if (startpos < size1 && startpos + range >= size1) lim = range - (size1 - startpos); /* Written out as an if-else to avoid testing `translate' inside the loop. */ if (RE_TRANSLATE_P (translate)) { if (multibyte) while (range > lim) { int buf_charlen; buf_ch = STRING_CHAR_AND_LENGTH (d, range - lim, buf_charlen); buf_ch = RE_TRANSLATE (translate, buf_ch); if (buf_ch >= 0400 || fastmap[buf_ch]) break; range -= buf_charlen; d += buf_charlen; } else while (range > lim && !fastmap[(unsigned char) RE_TRANSLATE (translate, (unsigned char) *d)]) { d++; range--; } } else while (range > lim && !fastmap[(unsigned char) *d]) { d++; range--; } startpos += irange - range; } else /* Searching backwards. */ { int room = (size1 == 0 || startpos >= size1 ? size2 + size1 - startpos : size1 - startpos); buf_ch = STRING_CHAR (d, room); if (RE_TRANSLATE_P (translate)) buf_ch = RE_TRANSLATE (translate, buf_ch); if (! (buf_ch >= 0400 || fastmap[buf_ch])) goto advance; } } /* If can't match the null string, and that's all we have left, fail. */ if (range >= 0 && startpos == total_size && fastmap && !bufp->can_be_null) return -1; val = re_match_2_internal (bufp, string1, size1, string2, size2, startpos, regs, stop); #ifndef REGEX_MALLOC #ifdef C_ALLOCA alloca (0); #endif #endif if (val >= 0) return startpos; if (val == -2) return -2; advance: if (!range) break; else if (range > 0) { /* Update STARTPOS to the next character boundary. */ if (multibyte) { const unsigned char *p = (const unsigned char *) POS_ADDR_VSTRING (startpos); const unsigned char *pend = (const unsigned char *) STOP_ADDR_VSTRING (startpos); int len = MULTIBYTE_FORM_LENGTH (p, pend - p); range -= len; if (range < 0) break; startpos += len; } else { range--; startpos++; } } else { range++; startpos--; /* Update STARTPOS to the previous character boundary. */ if (multibyte) { const unsigned char *p = (const unsigned char *) POS_ADDR_VSTRING (startpos); int len = 0; /* Find the head of multibyte form. */ while (!CHAR_HEAD_P (*p)) p--, len++; /* Adjust it. */ #if 0 /* XXX */ if (MULTIBYTE_FORM_LENGTH (p, len + 1) != (len + 1)) ; else #endif { range += len; if (range > 0) break; startpos -= len; } } } } return -1; } /* re_search_2 */ /* Declarations and macros for re_match_2. */ static int bcmp_translate (); static boolean alt_match_null_string_p (), common_op_match_null_string_p (), group_match_null_string_p (); /* This converts PTR, a pointer into one of the search strings `string1' and `string2' into an offset from the beginning of that string. */ #define POINTER_TO_OFFSET(ptr) \ (FIRST_STRING_P (ptr) \ ? ((regoff_t) ((ptr) - string1)) \ : ((regoff_t) ((ptr) - string2 + size1))) /* Macros for dealing with the split strings in re_match_2. */ #define MATCHING_IN_FIRST_STRING (dend == end_match_1) /* Call before fetching a character with *d. This switches over to string2 if necessary. */ #define PREFETCH() \ while (d == dend) \ { \ /* End of string2 => fail. */ \ if (dend == end_match_2) \ goto fail; \ /* End of string1 => advance to string2. */ \ d = string2; \ dend = end_match_2; \ } /* Test if at very beginning or at very end of the virtual concatenation of `string1' and `string2'. If only one string, it's `string2'. */ #define AT_STRINGS_BEG(d) ((d) == (size1 ? string1 : string2) || !size2) #define AT_STRINGS_END(d) ((d) == end2) /* Test if D points to a character which is word-constituent. We have two special cases to check for: if past the end of string1, look at the first character in string2; and if before the beginning of string2, look at the last character in string1. */ #define WORDCHAR_P(d) \ (SYNTAX ((d) == end1 ? *string2 \ : (d) == string2 - 1 ? *(end1 - 1) : *(d)) \ == Sword) /* Disabled due to a compiler bug -- see comment at case wordbound */ /* The comment at case wordbound is following one, but we don't use AT_WORD_BOUNDARY anymore to support multibyte form. The DEC Alpha C compiler 3.x generates incorrect code for the test WORDCHAR_P (d - 1) != WORDCHAR_P (d) in the expansion of AT_WORD_BOUNDARY, so this code is disabled. Expanding the macro and introducing temporary variables works around the bug. */ #if 0 /* Test if the character before D and the one at D differ with respect to being word-constituent. */ #define AT_WORD_BOUNDARY(d) \ (AT_STRINGS_BEG (d) || AT_STRINGS_END (d) \ || WORDCHAR_P (d - 1) != WORDCHAR_P (d)) #endif /* Free everything we malloc. */ #ifdef MATCH_MAY_ALLOCATE #define FREE_VAR(var) if (var) { REGEX_FREE (var); var = NULL; } else #define FREE_VARIABLES() \ do { \ REGEX_FREE_STACK (fail_stack.stack); \ FREE_VAR (regstart); \ FREE_VAR (regend); \ FREE_VAR (old_regstart); \ FREE_VAR (old_regend); \ FREE_VAR (best_regstart); \ FREE_VAR (best_regend); \ FREE_VAR (reg_info); \ FREE_VAR (reg_dummy); \ FREE_VAR (reg_info_dummy); \ } while (0) #else #define FREE_VARIABLES() ((void)0) /* Do nothing! But inhibit gcc warning. */ #endif /* not MATCH_MAY_ALLOCATE */ /* These values must meet several constraints. They must not be valid register values; since we have a limit of 255 registers (because we use only one byte in the pattern for the register number), we can use numbers larger than 255. They must differ by 1, because of NUM_FAILURE_ITEMS above. And the value for the lowest register must be larger than the value for the highest register, so we do not try to actually save any registers when none are active. */ #define NO_HIGHEST_ACTIVE_REG (1 << BYTEWIDTH) #define NO_LOWEST_ACTIVE_REG (NO_HIGHEST_ACTIVE_REG + 1) /* Matching routines. */ #ifndef emacs /* Emacs never uses this. */ /* re_match is like re_match_2 except it takes only a single string. */ int re_match (bufp, string, size, pos, regs) struct re_pattern_buffer *bufp; const char *string; int size, pos; struct re_registers *regs; { int result = re_match_2_internal (bufp, NULL, 0, string, size, pos, regs, size); #ifndef REGEX_MALLOC /* CVS */ #ifdef C_ALLOCA /* CVS */ alloca (0); #endif /* CVS */ #endif /* CVS */ return result; } #endif /* not emacs */ #ifdef emacs /* In Emacs, this is the string or buffer in which we are matching. It is used for looking up syntax properties. */ Lisp_Object re_match_object; #endif /* re_match_2 matches the compiled pattern in BUFP against the the (virtual) concatenation of STRING1 and STRING2 (of length SIZE1 and SIZE2, respectively). We start matching at POS, and stop matching at STOP. If REGS is non-null and the `no_sub' field of BUFP is nonzero, we store offsets for the substring each group matched in REGS. See the documentation for exactly how many groups we fill. We return -1 if no match, -2 if an internal error (such as the failure stack overflowing). Otherwise, we return the length of the matched substring. */ int re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) struct re_pattern_buffer *bufp; const char *string1, *string2; int size1, size2; int pos; struct re_registers *regs; int stop; { int result; #ifdef emacs int charpos; int adjpos = NILP (re_match_object) || BUFFERP (re_match_object); gl_state.object = re_match_object; charpos = SYNTAX_TABLE_BYTE_TO_CHAR (pos + adjpos); SETUP_SYNTAX_TABLE_FOR_OBJECT (re_match_object, charpos, 1); #endif result = re_match_2_internal (bufp, string1, size1, string2, size2, pos, regs, stop); #ifndef REGEX_MALLOC /* CVS */ #ifdef C_ALLOCA /* CVS */ alloca (0); #endif /* CVS */ #endif /* CVS */ return result; } /* This is a separate function so that we can force an alloca cleanup afterwards. */ static int re_match_2_internal (bufp, string1, size1, string2, size2, pos, regs, stop) struct re_pattern_buffer *bufp; const char *string1, *string2; int size1, size2; int pos; struct re_registers *regs; int stop; { /* General temporaries. */ int mcnt; unsigned char *p1; /* Just past the end of the corresponding string. */ const char *end1, *end2; /* Pointers into string1 and string2, just past the last characters in each to consider matching. */ const char *end_match_1, *end_match_2; /* Where we are in the data, and the end of the current string. */ const char *d, *dend; /* Where we are in the pattern, and the end of the pattern. */ unsigned char *p = bufp->buffer; register unsigned char *pend = p + bufp->used; /* Mark the opcode just after a start_memory, so we can test for an empty subpattern when we get to the stop_memory. */ unsigned char *just_past_start_mem = 0; /* We use this to map every character in the string. */ RE_TRANSLATE_TYPE translate = bufp->translate; /* Nonzero if we have to concern multibyte character. */ int multibyte = bufp->multibyte; /* Failure point stack. Each place that can handle a failure further down the line pushes a failure point on this stack. It consists of restart, regend, and reg_info for all registers corresponding to the subexpressions we're currently inside, plus the number of such registers, and, finally, two char *'s. The first char * is where to resume scanning the pattern; the second one is where to resume scanning the strings. If the latter is zero, the failure point is a ``dummy''; if a failure happens and the failure point is a dummy, it gets discarded and the next next one is tried. */ #ifdef MATCH_MAY_ALLOCATE /* otherwise, this is global. */ fail_stack_type fail_stack; #endif #ifdef DEBUG static unsigned failure_id = 0; unsigned nfailure_points_pushed = 0, nfailure_points_popped = 0; #endif /* This holds the pointer to the failure stack, when it is allocated relocatably. */ fail_stack_elt_t *failure_stack_ptr; /* We fill all the registers internally, independent of what we return, for use in backreferences. The number here includes an element for register zero. */ unsigned num_regs = bufp->re_nsub + 1; /* The currently active registers. */ unsigned lowest_active_reg = NO_LOWEST_ACTIVE_REG; unsigned highest_active_reg = NO_HIGHEST_ACTIVE_REG; /* Information on the contents of registers. These are pointers into the input strings; they record just what was matched (on this attempt) by a subexpression part of the pattern, that is, the regnum-th regstart pointer points to where in the pattern we began matching and the regnum-th regend points to right after where we stopped matching the regnum-th subexpression. (The zeroth register keeps track of what the whole pattern matches.) */ #ifdef MATCH_MAY_ALLOCATE /* otherwise, these are global. */ const char **regstart, **regend; #endif /* If a group that's operated upon by a repetition operator fails to match anything, then the register for its start will need to be restored because it will have been set to wherever in the string we are when we last see its open-group operator. Similarly for a register's end. */ #ifdef MATCH_MAY_ALLOCATE /* otherwise, these are global. */ const char **old_regstart, **old_regend; #endif /* The is_active field of reg_info helps us keep track of which (possibly nested) subexpressions we are currently in. The matched_something field of reg_info[reg_num] helps us tell whether or not we have matched any of the pattern so far this time through the reg_num-th subexpression. These two fields get reset each time through any loop their register is in. */ #ifdef MATCH_MAY_ALLOCATE /* otherwise, this is global. */ register_info_type *reg_info; #endif /* The following record the register info as found in the above variables when we find a match better than any we've seen before. This happens as we backtrack through the failure points, which in turn happens only if we have not yet matched the entire string. */ unsigned best_regs_set = false; #ifdef MATCH_MAY_ALLOCATE /* otherwise, these are global. */ const char **best_regstart, **best_regend; #endif /* Logically, this is `best_regend[0]'. But we don't want to have to allocate space for that if we're not allocating space for anything else (see below). Also, we never need info about register 0 for any of the other register vectors, and it seems rather a kludge to treat `best_regend' differently than the rest. So we keep track of the end of the best match so far in a separate variable. We initialize this to NULL so that when we backtrack the first time and need to test it, it's not garbage. */ const char *match_end = NULL; /* This helps SET_REGS_MATCHED avoid doing redundant work. */ int set_regs_matched_done = 0; /* Used when we pop values we don't care about. */ #ifdef MATCH_MAY_ALLOCATE /* otherwise, these are global. */ const char **reg_dummy; register_info_type *reg_info_dummy; #endif #ifdef DEBUG /* Counts the total number of registers pushed. */ unsigned num_regs_pushed = 0; #endif DEBUG_PRINT1 ("\n\nEntering re_match_2.\n"); INIT_FAIL_STACK (); #ifdef MATCH_MAY_ALLOCATE /* Do not bother to initialize all the register variables if there are no groups in the pattern, as it takes a fair amount of time. If there are groups, we include space for register 0 (the whole pattern), even though we never use it, since it simplifies the array indexing. We should fix this. */ if (bufp->re_nsub) { regstart = REGEX_TALLOC (num_regs, const char *); regend = REGEX_TALLOC (num_regs, const char *); old_regstart = REGEX_TALLOC (num_regs, const char *); old_regend = REGEX_TALLOC (num_regs, const char *); best_regstart = REGEX_TALLOC (num_regs, const char *); best_regend = REGEX_TALLOC (num_regs, const char *); reg_info = REGEX_TALLOC (num_regs, register_info_type); reg_dummy = REGEX_TALLOC (num_regs, const char *); reg_info_dummy = REGEX_TALLOC (num_regs, register_info_type); if (!(regstart && regend && old_regstart && old_regend && reg_info && best_regstart && best_regend && reg_dummy && reg_info_dummy)) { FREE_VARIABLES (); return -2; } } else { /* We must initialize all our variables to NULL, so that `FREE_VARIABLES' doesn't try to free them. */ regstart = regend = old_regstart = old_regend = best_regstart = best_regend = reg_dummy = NULL; reg_info = reg_info_dummy = (register_info_type *) NULL; } #endif /* MATCH_MAY_ALLOCATE */ /* The starting position is bogus. */ if (pos < 0 || pos > size1 + size2) { FREE_VARIABLES (); return -1; } /* Initialize subexpression text positions to -1 to mark ones that no start_memory/stop_memory has been seen for. Also initialize the register information struct. */ for (mcnt = 1; mcnt < num_regs; mcnt++) { regstart[mcnt] = regend[mcnt] = old_regstart[mcnt] = old_regend[mcnt] = REG_UNSET_VALUE; REG_MATCH_NULL_STRING_P (reg_info[mcnt]) = MATCH_NULL_UNSET_VALUE; IS_ACTIVE (reg_info[mcnt]) = 0; MATCHED_SOMETHING (reg_info[mcnt]) = 0; EVER_MATCHED_SOMETHING (reg_info[mcnt]) = 0; } /* We move `string1' into `string2' if the latter's empty -- but not if `string1' is null. */ if (size2 == 0 && string1 != NULL) { string2 = string1; size2 = size1; string1 = 0; size1 = 0; } end1 = string1 + size1; end2 = string2 + size2; /* Compute where to stop matching, within the two strings. */ if (stop <= size1) { end_match_1 = string1 + stop; end_match_2 = string2; } else { end_match_1 = end1; end_match_2 = string2 + stop - size1; } /* `p' scans through the pattern as `d' scans through the data. `dend' is the end of the input string that `d' points within. `d' is advanced into the following input string whenever necessary, but this happens before fetching; therefore, at the beginning of the loop, `d' can be pointing at the end of a string, but it cannot equal `string2'. */ if (size1 > 0 && pos <= size1) { d = string1 + pos; dend = end_match_1; } else { d = string2 + pos - size1; dend = end_match_2; } DEBUG_PRINT1 ("The compiled pattern is: "); DEBUG_PRINT_COMPILED_PATTERN (bufp, p, pend); DEBUG_PRINT1 ("The string to match is: `"); DEBUG_PRINT_DOUBLE_STRING (d, string1, size1, string2, size2); DEBUG_PRINT1 ("'\n"); /* This loops over pattern commands. It exits by returning from the function if the match is complete, or it drops through if the match fails at this starting point in the input data. */ for (;;) { DEBUG_PRINT2 ("\n0x%x: ", p); if (p == pend) { /* End of pattern means we might have succeeded. */ DEBUG_PRINT1 ("end of pattern ... "); /* If we haven't matched the entire string, and we want the longest match, try backtracking. */ if (d != end_match_2) { /* 1 if this match ends in the same string (string1 or string2) as the best previous match. */ boolean same_str_p = (FIRST_STRING_P (match_end) == MATCHING_IN_FIRST_STRING); /* 1 if this match is the best seen so far. */ boolean best_match_p; /* AIX compiler got confused when this was combined with the previous declaration. */ if (same_str_p) best_match_p = d > match_end; else best_match_p = !MATCHING_IN_FIRST_STRING; DEBUG_PRINT1 ("backtracking.\n"); if (!FAIL_STACK_EMPTY ()) { /* More failure points to try. */ /* If exceeds best match so far, save it. */ if (!best_regs_set || best_match_p) { best_regs_set = true; match_end = d; DEBUG_PRINT1 ("\nSAVING match as best so far.\n"); for (mcnt = 1; mcnt < num_regs; mcnt++) { best_regstart[mcnt] = regstart[mcnt]; best_regend[mcnt] = regend[mcnt]; } } goto fail; } /* If no failure points, don't restore garbage. And if last match is real best match, don't restore second best one. */ else if (best_regs_set && !best_match_p) { restore_best_regs: /* Restore best match. It may happen that `dend == end_match_1' while the restored d is in string2. For example, the pattern `x.*y.*z' against the strings `x-' and `y-z-', if the two strings are not consecutive in memory. */ DEBUG_PRINT1 ("Restoring best registers.\n"); d = match_end; dend = ((d >= string1 && d <= end1) ? end_match_1 : end_match_2); for (mcnt = 1; mcnt < num_regs; mcnt++) { regstart[mcnt] = best_regstart[mcnt]; regend[mcnt] = best_regend[mcnt]; } } } /* d != end_match_2 */ succeed_label: DEBUG_PRINT1 ("Accepting match.\n"); /* If caller wants register contents data back, do it. */ if (regs && !bufp->no_sub) { /* Have the register data arrays been allocated? */ if (bufp->regs_allocated == REGS_UNALLOCATED) { /* No. So allocate them with malloc. We need one extra element beyond `num_regs' for the `-1' marker GNU code uses. */ regs->num_regs = MAX (RE_NREGS, num_regs + 1); regs->start = TALLOC (regs->num_regs, regoff_t); regs->end = TALLOC (regs->num_regs, regoff_t); if (regs->start == NULL || regs->end == NULL) { FREE_VARIABLES (); return -2; } bufp->regs_allocated = REGS_REALLOCATE; } else if (bufp->regs_allocated == REGS_REALLOCATE) { /* Yes. If we need more elements than were already allocated, reallocate them. If we need fewer, just leave it alone. */ if (regs->num_regs < num_regs + 1) { regs->num_regs = num_regs + 1; RETALLOC (regs->start, regs->num_regs, regoff_t); RETALLOC (regs->end, regs->num_regs, regoff_t); if (regs->start == NULL || regs->end == NULL) { FREE_VARIABLES (); return -2; } } } else { /* These braces fend off a "empty body in an else-statement" warning under GCC when assert expands to nothing. */ assert (bufp->regs_allocated == REGS_FIXED); } /* Convert the pointer data in `regstart' and `regend' to indices. Register zero has to be set differently, since we haven't kept track of any info for it. */ if (regs->num_regs > 0) { regs->start[0] = pos; regs->end[0] = (MATCHING_IN_FIRST_STRING ? ((regoff_t) (d - string1)) : ((regoff_t) (d - string2 + size1))); } /* Go through the first `min (num_regs, regs->num_regs)' registers, since that is all we initialized. */ for (mcnt = 1; mcnt < MIN (num_regs, regs->num_regs); mcnt++) { if (REG_UNSET (regstart[mcnt]) || REG_UNSET (regend[mcnt])) regs->start[mcnt] = regs->end[mcnt] = -1; else { regs->start[mcnt] = (regoff_t) POINTER_TO_OFFSET (regstart[mcnt]); regs->end[mcnt] = (regoff_t) POINTER_TO_OFFSET (regend[mcnt]); } } /* If the regs structure we return has more elements than were in the pattern, set the extra elements to -1. If we (re)allocated the registers, this is the case, because we always allocate enough to have at least one -1 at the end. */ for (mcnt = num_regs; mcnt < regs->num_regs; mcnt++) regs->start[mcnt] = regs->end[mcnt] = -1; } /* regs && !bufp->no_sub */ DEBUG_PRINT4 ("%u failure points pushed, %u popped (%u remain).\n", nfailure_points_pushed, nfailure_points_popped, nfailure_points_pushed - nfailure_points_popped); DEBUG_PRINT2 ("%u registers pushed.\n", num_regs_pushed); mcnt = d - pos - (MATCHING_IN_FIRST_STRING ? string1 : string2 - size1); DEBUG_PRINT2 ("Returning %d from re_match_2.\n", mcnt); FREE_VARIABLES (); return mcnt; } /* Otherwise match next pattern command. */ switch (SWITCH_ENUM_CAST ((re_opcode_t) *p++)) { /* Ignore these. Used to ignore the n of succeed_n's which currently have n == 0. */ case no_op: DEBUG_PRINT1 ("EXECUTING no_op.\n"); break; case succeed: DEBUG_PRINT1 ("EXECUTING succeed.\n"); goto succeed_label; /* Match the next n pattern characters exactly. The following byte in the pattern defines n, and the n bytes after that are the characters to match. */ case exactn: mcnt = *p++; DEBUG_PRINT2 ("EXECUTING exactn %d.\n", mcnt); /* This is written out as an if-else so we don't waste time testing `translate' inside the loop. */ if (RE_TRANSLATE_P (translate)) { #ifdef emacs if (multibyte) do { int pat_charlen, buf_charlen; unsigned int pat_ch, buf_ch; PREFETCH (); pat_ch = STRING_CHAR_AND_LENGTH (p, pend - p, pat_charlen); buf_ch = STRING_CHAR_AND_LENGTH (d, dend - d, buf_charlen); if (RE_TRANSLATE (translate, buf_ch) != pat_ch) goto fail; p += pat_charlen; d += buf_charlen; mcnt -= pat_charlen; } while (mcnt > 0); else #endif /* not emacs */ do { PREFETCH (); if ((unsigned char) RE_TRANSLATE (translate, (unsigned char) *d) != (unsigned char) *p++) goto fail; d++; } while (--mcnt); } else { do { PREFETCH (); if (*d++ != (char) *p++) goto fail; } while (--mcnt); } SET_REGS_MATCHED (); break; /* Match any character except possibly a newline or a null. */ case anychar: { int buf_charlen; unsigned int buf_ch; DEBUG_PRINT1 ("EXECUTING anychar.\n"); PREFETCH (); #ifdef emacs if (multibyte) buf_ch = STRING_CHAR_AND_LENGTH (d, dend - d, buf_charlen); else #endif /* not emacs */ { buf_ch = (unsigned char) *d; buf_charlen = 1; } buf_ch = TRANSLATE (buf_ch); if ((!(bufp->syntax & RE_DOT_NEWLINE) && buf_ch == '\n') || ((bufp->syntax & RE_DOT_NOT_NULL) && buf_ch == '\000')) goto fail; SET_REGS_MATCHED (); DEBUG_PRINT2 (" Matched `%d'.\n", *d); d += buf_charlen; } break; case charset: case charset_not: { register unsigned int c; boolean not = (re_opcode_t) *(p - 1) == charset_not; int len; /* Start of actual range_table, or end of bitmap if there is no range table. */ unsigned char *range_table; /* Nonzero if there is range table. */ int range_table_exists; /* Number of ranges of range table. Not in bytes. */ int count; DEBUG_PRINT2 ("EXECUTING charset%s.\n", not ? "_not" : ""); PREFETCH (); c = (unsigned char) *d; range_table = CHARSET_RANGE_TABLE (&p[-1]); /* Past the bitmap. */ range_table_exists = CHARSET_RANGE_TABLE_EXISTS_P (&p[-1]); if (range_table_exists) EXTRACT_NUMBER_AND_INCR (count, range_table); else count = 0; if (multibyte && BASE_LEADING_CODE_P (c)) c = STRING_CHAR_AND_LENGTH (d, dend - d, len); if (SINGLE_BYTE_CHAR_P (c)) { /* Lookup bitmap. */ c = TRANSLATE (c); /* The character to match. */ len = 1; /* Cast to `unsigned' instead of `unsigned char' in case the bit list is a full 32 bytes long. */ if (c < (unsigned) (CHARSET_BITMAP_SIZE (&p[-1]) * BYTEWIDTH) && p[1 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH))) not = !not; } else if (range_table_exists) CHARSET_LOOKUP_RANGE_TABLE_RAW (not, c, range_table, count); p = CHARSET_RANGE_TABLE_END (range_table, count); if (!not) goto fail; SET_REGS_MATCHED (); d += len; break; } /* The beginning of a group is represented by start_memory. The arguments are the register number in the next byte, and the number of groups inner to this one in the next. The text matched within the group is recorded (in the internal registers data structure) under the register number. */ case start_memory: DEBUG_PRINT3 ("EXECUTING start_memory %d (%d):\n", *p, p[1]); /* Find out if this group can match the empty string. */ p1 = p; /* To send to group_match_null_string_p. */ if (REG_MATCH_NULL_STRING_P (reg_info[*p]) == MATCH_NULL_UNSET_VALUE) REG_MATCH_NULL_STRING_P (reg_info[*p]) = group_match_null_string_p (&p1, pend, reg_info); /* Save the position in the string where we were the last time we were at this open-group operator in case the group is operated upon by a repetition operator, e.g., with `(a*)*b' against `ab'; then we want to ignore where we are now in the string in case this attempt to match fails. */ old_regstart[*p] = REG_MATCH_NULL_STRING_P (reg_info[*p]) ? REG_UNSET (regstart[*p]) ? d : regstart[*p] : regstart[*p]; DEBUG_PRINT2 (" old_regstart: %d\n", POINTER_TO_OFFSET (old_regstart[*p])); regstart[*p] = d; DEBUG_PRINT2 (" regstart: %d\n", POINTER_TO_OFFSET (regstart[*p])); IS_ACTIVE (reg_info[*p]) = 1; MATCHED_SOMETHING (reg_info[*p]) = 0; /* Clear this whenever we change the register activity status. */ set_regs_matched_done = 0; /* This is the new highest active register. */ highest_active_reg = *p; /* If nothing was active before, this is the new lowest active register. */ if (lowest_active_reg == NO_LOWEST_ACTIVE_REG) lowest_active_reg = *p; /* Move past the register number and inner group count. */ p += 2; just_past_start_mem = p; break; /* The stop_memory opcode represents the end of a group. Its arguments are the same as start_memory's: the register number, and the number of inner groups. */ case stop_memory: DEBUG_PRINT3 ("EXECUTING stop_memory %d (%d):\n", *p, p[1]); /* We need to save the string position the last time we were at this close-group operator in case the group is operated upon by a repetition operator, e.g., with `((a*)*(b*)*)*' against `aba'; then we want to ignore where we are now in the string in case this attempt to match fails. */ old_regend[*p] = REG_MATCH_NULL_STRING_P (reg_info[*p]) ? REG_UNSET (regend[*p]) ? d : regend[*p] : regend[*p]; DEBUG_PRINT2 (" old_regend: %d\n", POINTER_TO_OFFSET (old_regend[*p])); regend[*p] = d; DEBUG_PRINT2 (" regend: %d\n", POINTER_TO_OFFSET (regend[*p])); /* This register isn't active anymore. */ IS_ACTIVE (reg_info[*p]) = 0; /* Clear this whenever we change the register activity status. */ set_regs_matched_done = 0; /* If this was the only register active, nothing is active anymore. */ if (lowest_active_reg == highest_active_reg) { lowest_active_reg = NO_LOWEST_ACTIVE_REG; highest_active_reg = NO_HIGHEST_ACTIVE_REG; } else { /* We must scan for the new highest active register, since it isn't necessarily one less than now: consider (a(b)c(d(e)f)g). When group 3 ends, after the f), the new highest active register is 1. */ unsigned char r = *p - 1; while (r > 0 && !IS_ACTIVE (reg_info[r])) r--; /* If we end up at register zero, that means that we saved the registers as the result of an `on_failure_jump', not a `start_memory', and we jumped to past the innermost `stop_memory'. For example, in ((.)*) we save registers 1 and 2 as a result of the *, but when we pop back to the second ), we are at the stop_memory 1. Thus, nothing is active. */ if (r == 0) { lowest_active_reg = NO_LOWEST_ACTIVE_REG; highest_active_reg = NO_HIGHEST_ACTIVE_REG; } else highest_active_reg = r; } /* If just failed to match something this time around with a group that's operated on by a repetition operator, try to force exit from the ``loop'', and restore the register information for this group that we had before trying this last match. */ if ((!MATCHED_SOMETHING (reg_info[*p]) || just_past_start_mem == p - 1) && (p + 2) < pend) { boolean is_a_jump_n = false; p1 = p + 2; mcnt = 0; switch ((re_opcode_t) *p1++) { case jump_n: is_a_jump_n = true; case pop_failure_jump: case maybe_pop_jump: case jump: case dummy_failure_jump: EXTRACT_NUMBER_AND_INCR (mcnt, p1); if (is_a_jump_n) p1 += 2; break; default: /* do nothing */ ; } p1 += mcnt; /* If the next operation is a jump backwards in the pattern to an on_failure_jump right before the start_memory corresponding to this stop_memory, exit from the loop by forcing a failure after pushing on the stack the on_failure_jump's jump in the pattern, and d. */ if (mcnt < 0 && (re_opcode_t) *p1 == on_failure_jump && (re_opcode_t) p1[3] == start_memory && p1[4] == *p) { /* If this group ever matched anything, then restore what its registers were before trying this last failed match, e.g., with `(a*)*b' against `ab' for regstart[1], and, e.g., with `((a*)*(b*)*)*' against `aba' for regend[3]. Also restore the registers for inner groups for, e.g., `((a*)(b*))*' against `aba' (register 3 would otherwise get trashed). */ if (EVER_MATCHED_SOMETHING (reg_info[*p])) { unsigned r; EVER_MATCHED_SOMETHING (reg_info[*p]) = 0; /* Restore this and inner groups' (if any) registers. */ for (r = *p; r < *p + *(p + 1); r++) { regstart[r] = old_regstart[r]; /* xx why this test? */ if (old_regend[r] >= regstart[r]) regend[r] = old_regend[r]; } } p1++; EXTRACT_NUMBER_AND_INCR (mcnt, p1); PUSH_FAILURE_POINT (p1 + mcnt, d, -2); goto fail; } } /* Move past the register number and the inner group count. */ p += 2; break; /* \ has been turned into a `duplicate' command which is followed by the numeric value of as the register number. */ case duplicate: { register const char *d2, *dend2; int regno = *p++; /* Get which register to match against. */ DEBUG_PRINT2 ("EXECUTING duplicate %d.\n", regno); /* Can't back reference a group which we've never matched. */ if (REG_UNSET (regstart[regno]) || REG_UNSET (regend[regno])) goto fail; /* Where in input to try to start matching. */ d2 = regstart[regno]; /* Where to stop matching; if both the place to start and the place to stop matching are in the same string, then set to the place to stop, otherwise, for now have to use the end of the first string. */ dend2 = ((FIRST_STRING_P (regstart[regno]) == FIRST_STRING_P (regend[regno])) ? regend[regno] : end_match_1); for (;;) { /* If necessary, advance to next segment in register contents. */ while (d2 == dend2) { if (dend2 == end_match_2) break; if (dend2 == regend[regno]) break; /* End of string1 => advance to string2. */ d2 = string2; dend2 = regend[regno]; } /* At end of register contents => success */ if (d2 == dend2) break; /* If necessary, advance to next segment in data. */ PREFETCH (); /* How many characters left in this segment to match. */ mcnt = dend - d; /* Want how many consecutive characters we can match in one shot, so, if necessary, adjust the count. */ if (mcnt > dend2 - d2) mcnt = dend2 - d2; /* Compare that many; failure if mismatch, else move past them. */ if (RE_TRANSLATE_P (translate) ? bcmp_translate (d, d2, mcnt, translate) : bcmp (d, d2, mcnt)) goto fail; d += mcnt, d2 += mcnt; /* Do this because we've match some characters. */ SET_REGS_MATCHED (); } } break; /* begline matches the empty string at the beginning of the string (unless `not_bol' is set in `bufp'), and, if `newline_anchor' is set, after newlines. */ case begline: DEBUG_PRINT1 ("EXECUTING begline.\n"); if (AT_STRINGS_BEG (d)) { if (!bufp->not_bol) break; } else if (d[-1] == '\n' && bufp->newline_anchor) { break; } /* In all other cases, we fail. */ goto fail; /* endline is the dual of begline. */ case endline: DEBUG_PRINT1 ("EXECUTING endline.\n"); if (AT_STRINGS_END (d)) { if (!bufp->not_eol) break; } /* We have to ``prefetch'' the next character. */ else if ((d == end1 ? *string2 : *d) == '\n' && bufp->newline_anchor) { break; } goto fail; /* Match at the very beginning of the data. */ case begbuf: DEBUG_PRINT1 ("EXECUTING begbuf.\n"); if (AT_STRINGS_BEG (d)) break; goto fail; /* Match at the very end of the data. */ case endbuf: DEBUG_PRINT1 ("EXECUTING endbuf.\n"); if (AT_STRINGS_END (d)) break; goto fail; /* on_failure_keep_string_jump is used to optimize `.*\n'. It pushes NULL as the value for the string on the stack. Then `pop_failure_point' will keep the current value for the string, instead of restoring it. To see why, consider matching `foo\nbar' against `.*\n'. The .* matches the foo; then the . fails against the \n. But the next thing we want to do is match the \n against the \n; if we restored the string value, we would be back at the foo. Because this is used only in specific cases, we don't need to check all the things that `on_failure_jump' does, to make sure the right things get saved on the stack. Hence we don't share its code. The only reason to push anything on the stack at all is that otherwise we would have to change `anychar's code to do something besides goto fail in this case; that seems worse than this. */ case on_failure_keep_string_jump: DEBUG_PRINT1 ("EXECUTING on_failure_keep_string_jump"); EXTRACT_NUMBER_AND_INCR (mcnt, p); DEBUG_PRINT3 (" %d (to 0x%x):\n", mcnt, p + mcnt); PUSH_FAILURE_POINT (p + mcnt, NULL, -2); break; /* Uses of on_failure_jump: Each alternative starts with an on_failure_jump that points to the beginning of the next alternative. Each alternative except the last ends with a jump that in effect jumps past the rest of the alternatives. (They really jump to the ending jump of the following alternative, because tensioning these jumps is a hassle.) Repeats start with an on_failure_jump that points past both the repetition text and either the following jump or pop_failure_jump back to this on_failure_jump. */ case on_failure_jump: on_failure: DEBUG_PRINT1 ("EXECUTING on_failure_jump"); #if defined (WINDOWSNT) && defined (emacs) QUIT; #endif EXTRACT_NUMBER_AND_INCR (mcnt, p); DEBUG_PRINT3 (" %d (to 0x%x)", mcnt, p + mcnt); /* If this on_failure_jump comes right before a group (i.e., the original * applied to a group), save the information for that group and all inner ones, so that if we fail back to this point, the group's information will be correct. For example, in \(a*\)*\1, we need the preceding group, and in \(zz\(a*\)b*\)\2, we need the inner group. */ /* We can't use `p' to check ahead because we push a failure point to `p + mcnt' after we do this. */ p1 = p; /* We need to skip no_op's before we look for the start_memory in case this on_failure_jump is happening as the result of a completed succeed_n, as in \(a\)\{1,3\}b\1 against aba. */ while (p1 < pend && (re_opcode_t) *p1 == no_op) p1++; if (p1 < pend && (re_opcode_t) *p1 == start_memory) { /* We have a new highest active register now. This will get reset at the start_memory we are about to get to, but we will have saved all the registers relevant to this repetition op, as described above. */ highest_active_reg = *(p1 + 1) + *(p1 + 2); if (lowest_active_reg == NO_LOWEST_ACTIVE_REG) lowest_active_reg = *(p1 + 1); } DEBUG_PRINT1 (":\n"); PUSH_FAILURE_POINT (p + mcnt, d, -2); break; /* A smart repeat ends with `maybe_pop_jump'. We change it to either `pop_failure_jump' or `jump'. */ case maybe_pop_jump: #if defined (WINDOWSNT) && defined (emacs) QUIT; #endif EXTRACT_NUMBER_AND_INCR (mcnt, p); DEBUG_PRINT2 ("EXECUTING maybe_pop_jump %d.\n", mcnt); { register unsigned char *p2 = p; /* Compare the beginning of the repeat with what in the pattern follows its end. If we can establish that there is nothing that they would both match, i.e., that we would have to backtrack because of (as in, e.g., `a*a') then we can change to pop_failure_jump, because we'll never have to backtrack. This is not true in the case of alternatives: in `(a|ab)*' we do need to backtrack to the `ab' alternative (e.g., if the string was `ab'). But instead of trying to detect that here, the alternative has put on a dummy failure point which is what we will end up popping. */ /* Skip over open/close-group commands. If what follows this loop is a ...+ construct, look at what begins its body, since we will have to match at least one of that. */ while (1) { if (p2 + 2 < pend && ((re_opcode_t) *p2 == stop_memory || (re_opcode_t) *p2 == start_memory)) p2 += 3; else if (p2 + 6 < pend && (re_opcode_t) *p2 == dummy_failure_jump) p2 += 6; else break; } p1 = p + mcnt; /* p1[0] ... p1[2] are the `on_failure_jump' corresponding to the `maybe_finalize_jump' of this case. Examine what follows. */ /* If we're at the end of the pattern, we can change. */ if (p2 == pend) { /* Consider what happens when matching ":\(.*\)" against ":/". I don't really understand this code yet. */ p[-3] = (unsigned char) pop_failure_jump; DEBUG_PRINT1 (" End of pattern: change to `pop_failure_jump'.\n"); } else if ((re_opcode_t) *p2 == exactn || (bufp->newline_anchor && (re_opcode_t) *p2 == endline)) { register unsigned int c = *p2 == (unsigned char) endline ? '\n' : p2[2]; if ((re_opcode_t) p1[3] == exactn) { if (!(multibyte /* && (c != '\n') */ && BASE_LEADING_CODE_P (c)) ? c != p1[5] : (STRING_CHAR (&p2[2], pend - &p2[2]) != STRING_CHAR (&p1[5], pend - &p1[5]))) { p[-3] = (unsigned char) pop_failure_jump; DEBUG_PRINT3 (" %c != %c => pop_failure_jump.\n", c, p1[5]); } } else if ((re_opcode_t) p1[3] == charset || (re_opcode_t) p1[3] == charset_not) { int not = (re_opcode_t) p1[3] == charset_not; if (multibyte /* && (c != '\n') */ && BASE_LEADING_CODE_P (c)) c = STRING_CHAR (&p2[2], pend - &p2[2]); /* Test if C is listed in charset (or charset_not) at `&p1[3]'. */ if (SINGLE_BYTE_CHAR_P (c)) { if (c < CHARSET_BITMAP_SIZE (&p1[3]) * BYTEWIDTH && p1[5 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH))) not = !not; } else if (CHARSET_RANGE_TABLE_EXISTS_P (&p1[3])) CHARSET_LOOKUP_RANGE_TABLE (not, c, &p1[3]); /* `not' is equal to 1 if c would match, which means that we can't change to pop_failure_jump. */ if (!not) { p[-3] = (unsigned char) pop_failure_jump; DEBUG_PRINT1 (" No match => pop_failure_jump.\n"); } } } else if ((re_opcode_t) *p2 == charset) { if ((re_opcode_t) p1[3] == exactn) { register unsigned int c = p1[5]; int not = 0; if (multibyte && BASE_LEADING_CODE_P (c)) c = STRING_CHAR (&p1[5], pend - &p1[5]); /* Test if C is listed in charset at `p2'. */ if (SINGLE_BYTE_CHAR_P (c)) { if (c < CHARSET_BITMAP_SIZE (p2) * BYTEWIDTH && (p2[2 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH)))) not = !not; } else if (CHARSET_RANGE_TABLE_EXISTS_P (p2)) CHARSET_LOOKUP_RANGE_TABLE (not, c, p2); if (!not) { p[-3] = (unsigned char) pop_failure_jump; DEBUG_PRINT1 (" No match => pop_failure_jump.\n"); } } /* It is hard to list up all the character in charset P2 if it includes multibyte character. Give up in such case. */ else if (!multibyte || !CHARSET_RANGE_TABLE_EXISTS_P (p2)) { /* Now, we are sure that P2 has no range table. So, for the size of bitmap in P2, `p2[1]' is enough. But P1 may have range table, so the size of bitmap table of P1 is extracted by using macro `CHARSET_BITMAP_SIZE'. Since we know that all the character listed in P2 is ASCII, it is enough to test only bitmap table of P1. */ if ((re_opcode_t) p1[3] == charset_not) { int idx; /* We win if the charset_not inside the loop lists every character listed in the charset after. */ for (idx = 0; idx < (int) p2[1]; idx++) if (! (p2[2 + idx] == 0 || (idx < CHARSET_BITMAP_SIZE (&p1[3]) && ((p2[2 + idx] & ~ p1[5 + idx]) == 0)))) break; if (idx == p2[1]) { p[-3] = (unsigned char) pop_failure_jump; DEBUG_PRINT1 (" No match => pop_failure_jump.\n"); } } else if ((re_opcode_t) p1[3] == charset) { int idx; /* We win if the charset inside the loop has no overlap with the one after the loop. */ for (idx = 0; (idx < (int) p2[1] && idx < CHARSET_BITMAP_SIZE (&p1[3])); idx++) if ((p2[2 + idx] & p1[5 + idx]) != 0) break; if (idx == p2[1] || idx == CHARSET_BITMAP_SIZE (&p1[3])) { p[-3] = (unsigned char) pop_failure_jump; DEBUG_PRINT1 (" No match => pop_failure_jump.\n"); } } } } } p -= 2; /* Point at relative address again. */ if ((re_opcode_t) p[-1] != pop_failure_jump) { p[-1] = (unsigned char) jump; DEBUG_PRINT1 (" Match => jump.\n"); goto unconditional_jump; } /* Note fall through. */ /* The end of a simple repeat has a pop_failure_jump back to its matching on_failure_jump, where the latter will push a failure point. The pop_failure_jump takes off failure points put on by this pop_failure_jump's matching on_failure_jump; we got through the pattern to here from the matching on_failure_jump, so didn't fail. */ case pop_failure_jump: { /* We need to pass separate storage for the lowest and highest registers, even though we don't care about the actual values. Otherwise, we will restore only one register from the stack, since lowest will == highest in `pop_failure_point'. */ unsigned dummy_low_reg, dummy_high_reg; unsigned char *pdummy; const char *sdummy; DEBUG_PRINT1 ("EXECUTING pop_failure_jump.\n"); POP_FAILURE_POINT (sdummy, pdummy, dummy_low_reg, dummy_high_reg, reg_dummy, reg_dummy, reg_info_dummy); } /* Note fall through. */ /* Unconditionally jump (without popping any failure points). */ case jump: unconditional_jump: #if defined (WINDOWSNT) && defined (emacs) QUIT; #endif EXTRACT_NUMBER_AND_INCR (mcnt, p); /* Get the amount to jump. */ DEBUG_PRINT2 ("EXECUTING jump %d ", mcnt); p += mcnt; /* Do the jump. */ DEBUG_PRINT2 ("(to 0x%x).\n", p); break; /* We need this opcode so we can detect where alternatives end in `group_match_null_string_p' et al. */ case jump_past_alt: DEBUG_PRINT1 ("EXECUTING jump_past_alt.\n"); goto unconditional_jump; /* Normally, the on_failure_jump pushes a failure point, which then gets popped at pop_failure_jump. We will end up at pop_failure_jump, also, and with a pattern of, say, `a+', we are skipping over the on_failure_jump, so we have to push something meaningless for pop_failure_jump to pop. */ case dummy_failure_jump: DEBUG_PRINT1 ("EXECUTING dummy_failure_jump.\n"); /* It doesn't matter what we push for the string here. What the code at `fail' tests is the value for the pattern. */ PUSH_FAILURE_POINT (0, 0, -2); goto unconditional_jump; /* At the end of an alternative, we need to push a dummy failure point in case we are followed by a `pop_failure_jump', because we don't want the failure point for the alternative to be popped. For example, matching `(a|ab)*' against `aab' requires that we match the `ab' alternative. */ case push_dummy_failure: DEBUG_PRINT1 ("EXECUTING push_dummy_failure.\n"); /* See comments just above at `dummy_failure_jump' about the two zeroes. */ PUSH_FAILURE_POINT (0, 0, -2); break; /* Have to succeed matching what follows at least n times. After that, handle like `on_failure_jump'. */ case succeed_n: EXTRACT_NUMBER (mcnt, p + 2); DEBUG_PRINT2 ("EXECUTING succeed_n %d.\n", mcnt); assert (mcnt >= 0); /* Originally, this is how many times we HAVE to succeed. */ if (mcnt > 0) { mcnt--; p += 2; STORE_NUMBER_AND_INCR (p, mcnt); DEBUG_PRINT3 (" Setting 0x%x to %d.\n", p, mcnt); } else if (mcnt == 0) { DEBUG_PRINT2 (" Setting two bytes from 0x%x to no_op.\n", p+2); p[2] = (unsigned char) no_op; p[3] = (unsigned char) no_op; goto on_failure; } break; case jump_n: EXTRACT_NUMBER (mcnt, p + 2); DEBUG_PRINT2 ("EXECUTING jump_n %d.\n", mcnt); /* Originally, this is how many times we CAN jump. */ if (mcnt) { mcnt--; STORE_NUMBER (p + 2, mcnt); goto unconditional_jump; } /* If don't have to jump any more, skip over the rest of command. */ else p += 4; break; case set_number_at: { DEBUG_PRINT1 ("EXECUTING set_number_at.\n"); EXTRACT_NUMBER_AND_INCR (mcnt, p); p1 = p + mcnt; EXTRACT_NUMBER_AND_INCR (mcnt, p); DEBUG_PRINT3 (" Setting 0x%x to %d.\n", p1, mcnt); STORE_NUMBER (p1, mcnt); break; } case wordbound: DEBUG_PRINT1 ("EXECUTING wordbound.\n"); /* We SUCCEED in one of the following cases: */ /* Case 1: D is at the beginning or the end of string. */ if (AT_STRINGS_BEG (d) || AT_STRINGS_END (d)) break; else { /* C1 is the character before D, S1 is the syntax of C1, C2 is the character at D, and S2 is the syntax of C2. */ int c1, c2, s1, s2; int pos1 = PTR_TO_OFFSET (d - 1); int charpos; GET_CHAR_BEFORE_2 (c1, d, string1, end1, string2, end2); GET_CHAR_AFTER_2 (c2, d, string1, end1, string2, end2); #ifdef emacs charpos = SYNTAX_TABLE_BYTE_TO_CHAR (pos1); UPDATE_SYNTAX_TABLE (charpos); #endif s1 = SYNTAX (c1); #ifdef emacs UPDATE_SYNTAX_TABLE_FORWARD (charpos + 1); #endif s2 = SYNTAX (c2); if (/* Case 2: Only one of S1 and S2 is Sword. */ ((s1 == Sword) != (s2 == Sword)) /* Case 3: Both of S1 and S2 are Sword, and macro WORD_BOUNDARY_P (C1, C2) returns nonzero. */ || ((s1 == Sword) && WORD_BOUNDARY_P (c1, c2))) break; } goto fail; case notwordbound: DEBUG_PRINT1 ("EXECUTING notwordbound.\n"); /* We FAIL in one of the following cases: */ /* Case 1: D is at the beginning or the end of string. */ if (AT_STRINGS_BEG (d) || AT_STRINGS_END (d)) goto fail; else { /* C1 is the character before D, S1 is the syntax of C1, C2 is the character at D, and S2 is the syntax of C2. */ int c1, c2, s1, s2; int pos1 = PTR_TO_OFFSET (d - 1); int charpos; GET_CHAR_BEFORE_2 (c1, d, string1, end1, string2, end2); GET_CHAR_AFTER_2 (c2, d, string1, end1, string2, end2); #ifdef emacs charpos = SYNTAX_TABLE_BYTE_TO_CHAR (pos1); UPDATE_SYNTAX_TABLE (charpos); #endif s1 = SYNTAX (c1); #ifdef emacs UPDATE_SYNTAX_TABLE_FORWARD (charpos + 1); #endif s2 = SYNTAX (c2); if (/* Case 2: Only one of S1 and S2 is Sword. */ ((s1 == Sword) != (s2 == Sword)) /* Case 3: Both of S1 and S2 are Sword, and macro WORD_BOUNDARY_P (C1, C2) returns nonzero. */ || ((s1 == Sword) && WORD_BOUNDARY_P (c1, c2))) goto fail; } break; case wordbeg: DEBUG_PRINT1 ("EXECUTING wordbeg.\n"); /* We FAIL in one of the following cases: */ /* Case 1: D is at the end of string. */ if (AT_STRINGS_END (d)) goto fail; else { /* C1 is the character before D, S1 is the syntax of C1, C2 is the character at D, and S2 is the syntax of C2. */ int c1, c2, s1, s2; int pos1 = PTR_TO_OFFSET (d); int charpos; GET_CHAR_AFTER_2 (c2, d, string1, end1, string2, end2); #ifdef emacs charpos = SYNTAX_TABLE_BYTE_TO_CHAR (pos1); UPDATE_SYNTAX_TABLE (charpos); #endif s2 = SYNTAX (c2); /* Case 2: S2 is not Sword. */ if (s2 != Sword) goto fail; /* Case 3: D is not at the beginning of string ... */ if (!AT_STRINGS_BEG (d)) { GET_CHAR_BEFORE_2 (c1, d, string1, end1, string2, end2); #ifdef emacs UPDATE_SYNTAX_TABLE_BACKWARD (charpos - 1); #endif s1 = SYNTAX (c1); /* ... and S1 is Sword, and WORD_BOUNDARY_P (C1, C2) returns 0. */ if ((s1 == Sword) && !WORD_BOUNDARY_P (c1, c2)) goto fail; } } break; case wordend: DEBUG_PRINT1 ("EXECUTING wordend.\n"); /* We FAIL in one of the following cases: */ /* Case 1: D is at the beginning of string. */ if (AT_STRINGS_BEG (d)) goto fail; else { /* C1 is the character before D, S1 is the syntax of C1, C2 is the character at D, and S2 is the syntax of C2. */ int c1, c2, s1, s2; int pos1 = PTR_TO_OFFSET (d); int charpos; GET_CHAR_BEFORE_2 (c1, d, string1, end1, string2, end2); #ifdef emacs charpos = SYNTAX_TABLE_BYTE_TO_CHAR (pos1 - 1); UPDATE_SYNTAX_TABLE (charpos); #endif s1 = SYNTAX (c1); /* Case 2: S1 is not Sword. */ if (s1 != Sword) goto fail; /* Case 3: D is not at the end of string ... */ if (!AT_STRINGS_END (d)) { GET_CHAR_AFTER_2 (c2, d, string1, end1, string2, end2); #ifdef emacs UPDATE_SYNTAX_TABLE_FORWARD (charpos); #endif s2 = SYNTAX (c2); /* ... and S2 is Sword, and WORD_BOUNDARY_P (C1, C2) returns 0. */ if ((s2 == Sword) && !WORD_BOUNDARY_P (c1, c2)) goto fail; } } break; #ifdef emacs case before_dot: DEBUG_PRINT1 ("EXECUTING before_dot.\n"); if (PTR_BYTE_POS ((unsigned char *) d) >= PT_BYTE) goto fail; break; case at_dot: DEBUG_PRINT1 ("EXECUTING at_dot.\n"); if (PTR_BYTE_POS ((unsigned char *) d) != PT_BYTE) goto fail; break; case after_dot: DEBUG_PRINT1 ("EXECUTING after_dot.\n"); if (PTR_BYTE_POS ((unsigned char *) d) <= PT_BYTE) goto fail; break; case syntaxspec: DEBUG_PRINT2 ("EXECUTING syntaxspec %d.\n", mcnt); mcnt = *p++; goto matchsyntax; case wordchar: DEBUG_PRINT1 ("EXECUTING Emacs wordchar.\n"); mcnt = (int) Sword; matchsyntax: PREFETCH (); #ifdef emacs { int pos1 = SYNTAX_TABLE_BYTE_TO_CHAR (PTR_TO_OFFSET (d)); UPDATE_SYNTAX_TABLE (pos1); } #endif { int c, len; if (multibyte) /* we must concern about multibyte form, ... */ c = STRING_CHAR_AND_LENGTH (d, dend - d, len); else /* everything should be handled as ASCII, even though it looks like multibyte form. */ c = *d, len = 1; if (SYNTAX (c) != (enum syntaxcode) mcnt) goto fail; d += len; } SET_REGS_MATCHED (); break; case notsyntaxspec: DEBUG_PRINT2 ("EXECUTING notsyntaxspec %d.\n", mcnt); mcnt = *p++; goto matchnotsyntax; case notwordchar: DEBUG_PRINT1 ("EXECUTING Emacs notwordchar.\n"); mcnt = (int) Sword; matchnotsyntax: PREFETCH (); #ifdef emacs { int pos1 = SYNTAX_TABLE_BYTE_TO_CHAR (PTR_TO_OFFSET (d)); UPDATE_SYNTAX_TABLE (pos1); } #endif { int c, len; if (multibyte) c = STRING_CHAR_AND_LENGTH (d, dend - d, len); else c = *d, len = 1; if (SYNTAX (c) == (enum syntaxcode) mcnt) goto fail; d += len; } SET_REGS_MATCHED (); break; case categoryspec: DEBUG_PRINT2 ("EXECUTING categoryspec %d.\n", *p); mcnt = *p++; PREFETCH (); { int c, len; if (multibyte) c = STRING_CHAR_AND_LENGTH (d, dend - d, len); else c = *d, len = 1; if (!CHAR_HAS_CATEGORY (c, mcnt)) goto fail; d += len; } SET_REGS_MATCHED (); break; case notcategoryspec: DEBUG_PRINT2 ("EXECUTING notcategoryspec %d.\n", *p); mcnt = *p++; PREFETCH (); { int c, len; if (multibyte) c = STRING_CHAR_AND_LENGTH (d, dend - d, len); else c = *d, len = 1; if (CHAR_HAS_CATEGORY (c, mcnt)) goto fail; d += len; } SET_REGS_MATCHED (); break; #else /* not emacs */ case wordchar: DEBUG_PRINT1 ("EXECUTING non-Emacs wordchar.\n"); PREFETCH (); if (!WORDCHAR_P (d)) goto fail; SET_REGS_MATCHED (); d++; break; case notwordchar: DEBUG_PRINT1 ("EXECUTING non-Emacs notwordchar.\n"); PREFETCH (); if (WORDCHAR_P (d)) goto fail; SET_REGS_MATCHED (); d++; break; #endif /* not emacs */ default: abort (); } continue; /* Successfully executed one pattern command; keep going. */ /* We goto here if a matching operation fails. */ fail: #if defined (WINDOWSNT) && defined (emacs) QUIT; #endif if (!FAIL_STACK_EMPTY ()) { /* A restart point is known. Restore to that state. */ DEBUG_PRINT1 ("\nFAIL:\n"); POP_FAILURE_POINT (d, p, lowest_active_reg, highest_active_reg, regstart, regend, reg_info); /* If this failure point is a dummy, try the next one. */ if (!p) goto fail; /* If we failed to the end of the pattern, don't examine *p. */ assert (p <= pend); if (p < pend) { boolean is_a_jump_n = false; /* If failed to a backwards jump that's part of a repetition loop, need to pop this failure point and use the next one. */ switch ((re_opcode_t) *p) { case jump_n: is_a_jump_n = true; case maybe_pop_jump: case pop_failure_jump: case jump: p1 = p + 1; EXTRACT_NUMBER_AND_INCR (mcnt, p1); p1 += mcnt; if ((is_a_jump_n && (re_opcode_t) *p1 == succeed_n) || (!is_a_jump_n && (re_opcode_t) *p1 == on_failure_jump)) goto fail; break; default: /* do nothing */ ; } } if (d >= string1 && d <= end1) dend = end_match_1; } else break; /* Matching at this starting point really fails. */ } /* for (;;) */ if (best_regs_set) goto restore_best_regs; FREE_VARIABLES (); return -1; /* Failure to match. */ } /* re_match_2 */ /* Subroutine definitions for re_match_2. */ /* We are passed P pointing to a register number after a start_memory. Return true if the pattern up to the corresponding stop_memory can match the empty string, and false otherwise. If we find the matching stop_memory, sets P to point to one past its number. Otherwise, sets P to an undefined byte less than or equal to END. We don't handle duplicates properly (yet). */ static boolean group_match_null_string_p (p, end, reg_info) unsigned char **p, *end; register_info_type *reg_info; { int mcnt; /* Point to after the args to the start_memory. */ unsigned char *p1 = *p + 2; while (p1 < end) { /* Skip over opcodes that can match nothing, and return true or false, as appropriate, when we get to one that can't, or to the matching stop_memory. */ switch ((re_opcode_t) *p1) { /* Could be either a loop or a series of alternatives. */ case on_failure_jump: p1++; EXTRACT_NUMBER_AND_INCR (mcnt, p1); /* If the next operation is not a jump backwards in the pattern. */ if (mcnt >= 0) { /* Go through the on_failure_jumps of the alternatives, seeing if any of the alternatives cannot match nothing. The last alternative starts with only a jump, whereas the rest start with on_failure_jump and end with a jump, e.g., here is the pattern for `a|b|c': /on_failure_jump/0/6/exactn/1/a/jump_past_alt/0/6 /on_failure_jump/0/6/exactn/1/b/jump_past_alt/0/3 /exactn/1/c So, we have to first go through the first (n-1) alternatives and then deal with the last one separately. */ /* Deal with the first (n-1) alternatives, which start with an on_failure_jump (see above) that jumps to right past a jump_past_alt. */ while ((re_opcode_t) p1[mcnt-3] == jump_past_alt) { /* `mcnt' holds how many bytes long the alternative is, including the ending `jump_past_alt' and its number. */ if (!alt_match_null_string_p (p1, p1 + mcnt - 3, reg_info)) return false; /* Move to right after this alternative, including the jump_past_alt. */ p1 += mcnt; /* Break if it's the beginning of an n-th alternative that doesn't begin with an on_failure_jump. */ if ((re_opcode_t) *p1 != on_failure_jump) break; /* Still have to check that it's not an n-th alternative that starts with an on_failure_jump. */ p1++; EXTRACT_NUMBER_AND_INCR (mcnt, p1); if ((re_opcode_t) p1[mcnt-3] != jump_past_alt) { /* Get to the beginning of the n-th alternative. */ p1 -= 3; break; } } /* Deal with the last alternative: go back and get number of the `jump_past_alt' just before it. `mcnt' contains the length of the alternative. */ EXTRACT_NUMBER (mcnt, p1 - 2); if (!alt_match_null_string_p (p1, p1 + mcnt, reg_info)) return false; p1 += mcnt; /* Get past the n-th alternative. */ } /* if mcnt > 0 */ break; case stop_memory: assert (p1[1] == **p); *p = p1 + 2; return true; default: if (!common_op_match_null_string_p (&p1, end, reg_info)) return false; } } /* while p1 < end */ return false; } /* group_match_null_string_p */ /* Similar to group_match_null_string_p, but doesn't deal with alternatives: It expects P to be the first byte of a single alternative and END one byte past the last. The alternative can contain groups. */ static boolean alt_match_null_string_p (p, end, reg_info) unsigned char *p, *end; register_info_type *reg_info; { int mcnt; unsigned char *p1 = p; while (p1 < end) { /* Skip over opcodes that can match nothing, and break when we get to one that can't. */ switch ((re_opcode_t) *p1) { /* It's a loop. */ case on_failure_jump: p1++; EXTRACT_NUMBER_AND_INCR (mcnt, p1); p1 += mcnt; break; default: if (!common_op_match_null_string_p (&p1, end, reg_info)) return false; } } /* while p1 < end */ return true; } /* alt_match_null_string_p */ /* Deals with the ops common to group_match_null_string_p and alt_match_null_string_p. Sets P to one after the op and its arguments, if any. */ static boolean common_op_match_null_string_p (p, end, reg_info) unsigned char **p, *end; register_info_type *reg_info; { int mcnt; boolean ret; int reg_no; unsigned char *p1 = *p; switch ((re_opcode_t) *p1++) { case no_op: case begline: case endline: case begbuf: case endbuf: case wordbeg: case wordend: case wordbound: case notwordbound: #ifdef emacs case before_dot: case at_dot: case after_dot: #endif break; case start_memory: reg_no = *p1; assert (reg_no > 0 && reg_no <= MAX_REGNUM); ret = group_match_null_string_p (&p1, end, reg_info); /* Have to set this here in case we're checking a group which contains a group and a back reference to it. */ if (REG_MATCH_NULL_STRING_P (reg_info[reg_no]) == MATCH_NULL_UNSET_VALUE) REG_MATCH_NULL_STRING_P (reg_info[reg_no]) = ret; if (!ret) return false; break; /* If this is an optimized succeed_n for zero times, make the jump. */ case jump: EXTRACT_NUMBER_AND_INCR (mcnt, p1); if (mcnt >= 0) p1 += mcnt; else return false; break; case succeed_n: /* Get to the number of times to succeed. */ p1 += 2; EXTRACT_NUMBER_AND_INCR (mcnt, p1); if (mcnt == 0) { p1 -= 4; EXTRACT_NUMBER_AND_INCR (mcnt, p1); p1 += mcnt; } else return false; break; case duplicate: if (!REG_MATCH_NULL_STRING_P (reg_info[*p1])) return false; break; case set_number_at: p1 += 4; default: /* All other opcodes mean we cannot match the empty string. */ return false; } *p = p1; return true; } /* common_op_match_null_string_p */ /* Return zero if TRANSLATE[S1] and TRANSLATE[S2] are identical for LEN bytes; nonzero otherwise. */ static int bcmp_translate (s1, s2, len, translate) unsigned char *s1, *s2; register int len; RE_TRANSLATE_TYPE translate; { register unsigned char *p1 = s1, *p2 = s2; unsigned char *p1_end = s1 + len; unsigned char *p2_end = s2 + len; while (p1 != p1_end && p2 != p2_end) { int p1_charlen, p2_charlen; int p1_ch, p2_ch; p1_ch = STRING_CHAR_AND_LENGTH (p1, p1_end - p1, p1_charlen); p2_ch = STRING_CHAR_AND_LENGTH (p2, p2_end - p2, p2_charlen); if (RE_TRANSLATE (translate, p1_ch) != RE_TRANSLATE (translate, p2_ch)) return 1; p1 += p1_charlen, p2 += p2_charlen; } if (p1 != p1_end || p2 != p2_end) return 1; return 0; } /* Entry points for GNU code. */ /* re_compile_pattern is the GNU regular expression compiler: it compiles PATTERN (of length SIZE) and puts the result in BUFP. Returns 0 if the pattern was valid, otherwise an error string. Assumes the `allocated' (and perhaps `buffer') and `translate' fields are set in BUFP on entry. We call regex_compile to do the actual compilation. */ const char * re_compile_pattern (pattern, length, bufp) const char *pattern; int length; struct re_pattern_buffer *bufp; { reg_errcode_t ret; /* GNU code is written to assume at least RE_NREGS registers will be set (and at least one extra will be -1). */ bufp->regs_allocated = REGS_UNALLOCATED; /* And GNU code determines whether or not to get register information by passing null for the REGS argument to re_match, etc., not by setting no_sub. */ bufp->no_sub = 0; /* Match anchors at newline. */ bufp->newline_anchor = 1; ret = regex_compile (pattern, length, re_syntax_options, bufp); if (!ret) return NULL; return gettext (re_error_msgid[(int) ret]); } /* Entry points compatible with 4.2 BSD regex library. We don't define them unless specifically requested. */ #if defined (_REGEX_RE_COMP) || defined (_LIBC) /* BSD has one and only one pattern buffer. */ static struct re_pattern_buffer re_comp_buf; char * #ifdef _LIBC /* Make these definitions weak in libc, so POSIX programs can redefine these names if they don't use our functions, and still use regcomp/regexec below without link errors. */ weak_function #endif re_comp (s) const char *s; { reg_errcode_t ret; if (!s) { if (!re_comp_buf.buffer) return gettext ("No previous regular expression"); return 0; } if (!re_comp_buf.buffer) { re_comp_buf.buffer = (unsigned char *) malloc (200); if (re_comp_buf.buffer == NULL) /* CVS: Yes, we're discarding `const' here if !HAVE_LIBINTL. */ return (char *) gettext (re_error_msgid[(int) REG_ESPACE]); re_comp_buf.allocated = 200; re_comp_buf.fastmap = (char *) malloc (1 << BYTEWIDTH); if (re_comp_buf.fastmap == NULL) /* CVS: Yes, we're discarding `const' here if !HAVE_LIBINTL. */ return (char *) gettext (re_error_msgid[(int) REG_ESPACE]); } /* Since `re_exec' always passes NULL for the `regs' argument, we don't need to initialize the pattern buffer fields which affect it. */ /* Match anchors at newlines. */ re_comp_buf.newline_anchor = 1; ret = regex_compile (s, strlen (s), re_syntax_options, &re_comp_buf); if (!ret) return NULL; /* Yes, we're discarding `const' here if !HAVE_LIBINTL. */ return (char *) gettext (re_error_msgid[(int) ret]); } int #ifdef _LIBC weak_function #endif re_exec (s) const char *s; { const int len = strlen (s); return 0 <= re_search (&re_comp_buf, s, len, 0, len, (struct re_registers *) 0); } #endif /* _REGEX_RE_COMP */ /* POSIX.2 functions. Don't define these for Emacs. */ #ifndef emacs /* regcomp takes a regular expression as a string and compiles it. PREG is a regex_t *. We do not expect any fields to be initialized, since POSIX says we shouldn't. Thus, we set `buffer' to the compiled pattern; `used' to the length of the compiled pattern; `syntax' to RE_SYNTAX_POSIX_EXTENDED if the REG_EXTENDED bit in CFLAGS is set; otherwise, to RE_SYNTAX_POSIX_BASIC; `newline_anchor' to REG_NEWLINE being set in CFLAGS; `fastmap' and `fastmap_accurate' to zero; `re_nsub' to the number of subexpressions in PATTERN. PATTERN is the address of the pattern string. CFLAGS is a series of bits which affect compilation. If REG_EXTENDED is set, we use POSIX extended syntax; otherwise, we use POSIX basic syntax. If REG_NEWLINE is set, then . and [^...] don't match newline. Also, regexec will try a match beginning after every newline. If REG_ICASE is set, then we considers upper- and lowercase versions of letters to be equivalent when matching. If REG_NOSUB is set, then when PREG is passed to regexec, that routine will report only success or failure, and nothing about the registers. It returns 0 if it succeeds, nonzero if it doesn't. (See regex.h for the return codes and their meanings.) */ int regcomp (preg, pattern, cflags) regex_t *preg; const char *pattern; int cflags; { reg_errcode_t ret; unsigned syntax = (cflags & REG_EXTENDED) ? RE_SYNTAX_POSIX_EXTENDED : RE_SYNTAX_POSIX_BASIC; /* regex_compile will allocate the space for the compiled pattern. */ preg->buffer = 0; preg->allocated = 0; preg->used = 0; /* Don't bother to use a fastmap when searching. This simplifies the REG_NEWLINE case: if we used a fastmap, we'd have to put all the characters after newlines into the fastmap. This way, we just try every character. */ preg->fastmap = 0; if (cflags & REG_ICASE) { unsigned i; preg->translate = (RE_TRANSLATE_TYPE) malloc (CHAR_SET_SIZE * sizeof (*(RE_TRANSLATE_TYPE)0)); if (preg->translate == NULL) return (int) REG_ESPACE; /* Map uppercase characters to corresponding lowercase ones. */ for (i = 0; i < CHAR_SET_SIZE; i++) preg->translate[i] = ISUPPER (i) ? tolower (i) : i; } else preg->translate = NULL; /* If REG_NEWLINE is set, newlines are treated differently. */ if (cflags & REG_NEWLINE) { /* REG_NEWLINE implies neither . nor [^...] match newline. */ syntax &= ~RE_DOT_NEWLINE; syntax |= RE_HAT_LISTS_NOT_NEWLINE; /* It also changes the matching behavior. */ preg->newline_anchor = 1; } else preg->newline_anchor = 0; preg->no_sub = !!(cflags & REG_NOSUB); /* POSIX says a null character in the pattern terminates it, so we can use strlen here in compiling the pattern. */ ret = regex_compile (pattern, strlen (pattern), syntax, preg); /* POSIX doesn't distinguish between an unmatched open-group and an unmatched close-group: both are REG_EPAREN. */ if (ret == REG_ERPAREN) ret = REG_EPAREN; return (int) ret; } /* regexec searches for a given pattern, specified by PREG, in the string STRING. If NMATCH is zero or REG_NOSUB was set in the cflags argument to `regcomp', we ignore PMATCH. Otherwise, we assume PMATCH has at least NMATCH elements, and we set them to the offsets of the corresponding matched substrings. EFLAGS specifies `execution flags' which affect matching: if REG_NOTBOL is set, then ^ does not match at the beginning of the string; if REG_NOTEOL is set, then $ does not match at the end. We return 0 if we find a match and REG_NOMATCH if not. */ int regexec (preg, string, nmatch, pmatch, eflags) const regex_t *preg; const char *string; size_t nmatch; regmatch_t pmatch[]; int eflags; { int ret; struct re_registers regs; regex_t private_preg; int len = strlen (string); boolean want_reg_info = !preg->no_sub && nmatch > 0; private_preg = *preg; private_preg.not_bol = !!(eflags & REG_NOTBOL); private_preg.not_eol = !!(eflags & REG_NOTEOL); /* The user has told us exactly how many registers to return information about, via `nmatch'. We have to pass that on to the matching routines. */ private_preg.regs_allocated = REGS_FIXED; if (want_reg_info) { regs.num_regs = nmatch; regs.start = TALLOC (nmatch, regoff_t); regs.end = TALLOC (nmatch, regoff_t); if (regs.start == NULL || regs.end == NULL) return (int) REG_NOMATCH; } /* Perform the searching operation. */ ret = re_search (&private_preg, string, len, /* start: */ 0, /* range: */ len, want_reg_info ? ®s : (struct re_registers *) 0); /* Copy the register information to the POSIX structure. */ if (want_reg_info) { if (ret >= 0) { unsigned r; for (r = 0; r < nmatch; r++) { pmatch[r].rm_so = regs.start[r]; pmatch[r].rm_eo = regs.end[r]; } } /* If we needed the temporary register info, free the space now. */ free (regs.start); free (regs.end); } /* We want zero return to mean success, unlike `re_search'. */ return ret >= 0 ? (int) REG_NOERROR : (int) REG_NOMATCH; } /* Returns a message corresponding to an error code, ERRCODE, returned from either regcomp or regexec. We don't use PREG here. */ size_t regerror (errcode, preg, errbuf, errbuf_size) int errcode; const regex_t *preg; char *errbuf; size_t errbuf_size; { const char *msg; size_t msg_size; if (errcode < 0 || errcode >= (sizeof (re_error_msgid) / sizeof (re_error_msgid[0]))) /* Only error codes returned by the rest of the code should be passed to this routine. If we are given anything else, or if other regex code generates an invalid error code, then the program has a bug. Dump core so we can fix it. */ abort (); msg = gettext (re_error_msgid[errcode]); msg_size = strlen (msg) + 1; /* Includes the null. */ if (errbuf_size != 0) { if (msg_size > errbuf_size) { strncpy (errbuf, msg, errbuf_size - 1); errbuf[errbuf_size - 1] = 0; } else strcpy (errbuf, msg); } return msg_size; } /* Free dynamically allocated space used by PREG. */ void regfree (preg) regex_t *preg; { if (preg->buffer != NULL) free (preg->buffer); preg->buffer = NULL; preg->allocated = 0; preg->used = 0; if (preg->fastmap != NULL) free (preg->fastmap); preg->fastmap = NULL; preg->fastmap_accurate = 0; if (preg->translate != NULL) free (preg->translate); preg->translate = NULL; } #endif /* not emacs */ /sys/src/ape/cmd/diff/regex.h 664 sys sys 1367613436 19353 /* Definitions for data structures and routines for the regular expression library, version 0.12. Copyright (C) 1985, 89, 90, 91, 92, 93, 95 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __REGEXP_LIBRARY_H__ #define __REGEXP_LIBRARY_H__ /* POSIX says that must be included (by the caller) before . */ #if !defined (_POSIX_C_SOURCE) && !defined (_POSIX_SOURCE) && defined (VMS) /* VMS doesn't have `size_t' in , even though POSIX says it should be there. */ #include #endif /* The following bits are used to determine the regexp syntax we recognize. The set/not-set meanings are chosen so that Emacs syntax remains the value 0. The bits are given in alphabetical order, and the definitions shifted by one from the previous bit; thus, when we add or remove a bit, only one other definition need change. */ typedef unsigned reg_syntax_t; /* If this bit is not set, then \ inside a bracket expression is literal. If set, then such a \ quotes the following character. */ #define RE_BACKSLASH_ESCAPE_IN_LISTS (1) /* If this bit is not set, then + and ? are operators, and \+ and \? are literals. If set, then \+ and \? are operators and + and ? are literals. */ #define RE_BK_PLUS_QM (RE_BACKSLASH_ESCAPE_IN_LISTS << 1) /* If this bit is set, then character classes are supported. They are: [:alpha:], [:upper:], [:lower:], [:digit:], [:alnum:], [:xdigit:], [:space:], [:print:], [:punct:], [:graph:], and [:cntrl:]. If not set, then character classes are not supported. */ #define RE_CHAR_CLASSES (RE_BK_PLUS_QM << 1) /* If this bit is set, then ^ and $ are always anchors (outside bracket expressions, of course). If this bit is not set, then it depends: ^ is an anchor if it is at the beginning of a regular expression or after an open-group or an alternation operator; $ is an anchor if it is at the end of a regular expression, or before a close-group or an alternation operator. This bit could be (re)combined with RE_CONTEXT_INDEP_OPS, because POSIX draft 11.2 says that * etc. in leading positions is undefined. We already implemented a previous draft which made those constructs invalid, though, so we haven't changed the code back. */ #define RE_CONTEXT_INDEP_ANCHORS (RE_CHAR_CLASSES << 1) /* If this bit is set, then special characters are always special regardless of where they are in the pattern. If this bit is not set, then special characters are special only in some contexts; otherwise they are ordinary. Specifically, * + ? and intervals are only special when not after the beginning, open-group, or alternation operator. */ #define RE_CONTEXT_INDEP_OPS (RE_CONTEXT_INDEP_ANCHORS << 1) /* If this bit is set, then *, +, ?, and { cannot be first in an re or immediately after an alternation or begin-group operator. */ #define RE_CONTEXT_INVALID_OPS (RE_CONTEXT_INDEP_OPS << 1) /* If this bit is set, then . matches newline. If not set, then it doesn't. */ #define RE_DOT_NEWLINE (RE_CONTEXT_INVALID_OPS << 1) /* If this bit is set, then . doesn't match NUL. If not set, then it does. */ #define RE_DOT_NOT_NULL (RE_DOT_NEWLINE << 1) /* If this bit is set, nonmatching lists [^...] do not match newline. If not set, they do. */ #define RE_HAT_LISTS_NOT_NEWLINE (RE_DOT_NOT_NULL << 1) /* If this bit is set, either \{...\} or {...} defines an interval, depending on RE_NO_BK_BRACES. If not set, \{, \}, {, and } are literals. */ #define RE_INTERVALS (RE_HAT_LISTS_NOT_NEWLINE << 1) /* If this bit is set, +, ? and | aren't recognized as operators. If not set, they are. */ #define RE_LIMITED_OPS (RE_INTERVALS << 1) /* If this bit is set, newline is an alternation operator. If not set, newline is literal. */ #define RE_NEWLINE_ALT (RE_LIMITED_OPS << 1) /* If this bit is set, then `{...}' defines an interval, and \{ and \} are literals. If not set, then `\{...\}' defines an interval. */ #define RE_NO_BK_BRACES (RE_NEWLINE_ALT << 1) /* If this bit is set, (...) defines a group, and \( and \) are literals. If not set, \(...\) defines a group, and ( and ) are literals. */ #define RE_NO_BK_PARENS (RE_NO_BK_BRACES << 1) /* If this bit is set, then \ matches . If not set, then \ is a back-reference. */ #define RE_NO_BK_REFS (RE_NO_BK_PARENS << 1) /* If this bit is set, then | is an alternation operator, and \| is literal. If not set, then \| is an alternation operator, and | is literal. */ #define RE_NO_BK_VBAR (RE_NO_BK_REFS << 1) /* If this bit is set, then an ending range point collating higher than the starting range point, as in [z-a], is invalid. If not set, then when ending range point collates higher than the starting range point, the range is ignored. */ #define RE_NO_EMPTY_RANGES (RE_NO_BK_VBAR << 1) /* If this bit is set, then an unmatched ) is ordinary. If not set, then an unmatched ) is invalid. */ #define RE_UNMATCHED_RIGHT_PAREN_ORD (RE_NO_EMPTY_RANGES << 1) /* If this bit is set, succeed as soon as we match the whole pattern, without further backtracking. */ #define RE_NO_POSIX_BACKTRACKING (RE_UNMATCHED_RIGHT_PAREN_ORD << 1) /* This global variable defines the particular regexp syntax to use (for some interfaces). When a regexp is compiled, the syntax used is stored in the pattern buffer, so changing this does not affect already-compiled regexps. */ extern reg_syntax_t re_syntax_options; #ifdef emacs /* In Emacs, this is the string or buffer in which we are matching. It is used for looking up syntax properties. */ extern Lisp_Object re_match_object; #endif /* Define combinations of the above bits for the standard possibilities. (The [[[ comments delimit what gets put into the Texinfo file, so don't delete them!) */ /* [[[begin syntaxes]]] */ #define RE_SYNTAX_EMACS 0 #define RE_SYNTAX_AWK \ (RE_BACKSLASH_ESCAPE_IN_LISTS | RE_DOT_NOT_NULL \ | RE_NO_BK_PARENS | RE_NO_BK_REFS \ | RE_NO_BK_VBAR | RE_NO_EMPTY_RANGES \ | RE_UNMATCHED_RIGHT_PAREN_ORD) #define RE_SYNTAX_POSIX_AWK \ (RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS) #define RE_SYNTAX_GREP \ (RE_BK_PLUS_QM | RE_CHAR_CLASSES \ | RE_HAT_LISTS_NOT_NEWLINE | RE_INTERVALS \ | RE_NEWLINE_ALT) #define RE_SYNTAX_EGREP \ (RE_CHAR_CLASSES | RE_CONTEXT_INDEP_ANCHORS \ | RE_CONTEXT_INDEP_OPS | RE_HAT_LISTS_NOT_NEWLINE \ | RE_NEWLINE_ALT | RE_NO_BK_PARENS \ | RE_NO_BK_VBAR) #define RE_SYNTAX_POSIX_EGREP \ (RE_SYNTAX_EGREP | RE_INTERVALS | RE_NO_BK_BRACES) /* P1003.2/D11.2, section 4.20.7.1, lines 5078ff. */ #define RE_SYNTAX_ED RE_SYNTAX_POSIX_BASIC #define RE_SYNTAX_SED RE_SYNTAX_POSIX_BASIC /* Syntax bits common to both basic and extended POSIX regex syntax. */ #define _RE_SYNTAX_POSIX_COMMON \ (RE_CHAR_CLASSES | RE_DOT_NEWLINE | RE_DOT_NOT_NULL \ | RE_INTERVALS | RE_NO_EMPTY_RANGES) #define RE_SYNTAX_POSIX_BASIC \ (_RE_SYNTAX_POSIX_COMMON | RE_BK_PLUS_QM) /* Differs from ..._POSIX_BASIC only in that RE_BK_PLUS_QM becomes RE_LIMITED_OPS, i.e., \? \+ \| are not recognized. Actually, this isn't minimal, since other operators, such as \`, aren't disabled. */ #define RE_SYNTAX_POSIX_MINIMAL_BASIC \ (_RE_SYNTAX_POSIX_COMMON | RE_LIMITED_OPS) #define RE_SYNTAX_POSIX_EXTENDED \ (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS \ | RE_CONTEXT_INDEP_OPS | RE_NO_BK_BRACES \ | RE_NO_BK_PARENS | RE_NO_BK_VBAR \ | RE_UNMATCHED_RIGHT_PAREN_ORD) /* Differs from ..._POSIX_EXTENDED in that RE_CONTEXT_INVALID_OPS replaces RE_CONTEXT_INDEP_OPS and RE_NO_BK_REFS is added. */ #define RE_SYNTAX_POSIX_MINIMAL_EXTENDED \ (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS \ | RE_CONTEXT_INVALID_OPS | RE_NO_BK_BRACES \ | RE_NO_BK_PARENS | RE_NO_BK_REFS \ | RE_NO_BK_VBAR | RE_UNMATCHED_RIGHT_PAREN_ORD) /* [[[end syntaxes]]] */ /* Maximum number of duplicates an interval can allow. Some systems (erroneously) define this in other header files, but we want our value, so remove any previous define. */ #ifdef RE_DUP_MAX #undef RE_DUP_MAX #endif #define RE_DUP_MAX ((1 << 15) - 1) /* POSIX `cflags' bits (i.e., information for `regcomp'). */ /* If this bit is set, then use extended regular expression syntax. If not set, then use basic regular expression syntax. */ #define REG_EXTENDED 1 /* If this bit is set, then ignore case when matching. If not set, then case is significant. */ #define REG_ICASE (REG_EXTENDED << 1) /* If this bit is set, then anchors do not match at newline characters in the string. If not set, then anchors do match at newlines. */ #define REG_NEWLINE (REG_ICASE << 1) /* If this bit is set, then report only success or fail in regexec. If not set, then returns differ between not matching and errors. */ #define REG_NOSUB (REG_NEWLINE << 1) /* POSIX `eflags' bits (i.e., information for regexec). */ /* If this bit is set, then the beginning-of-line operator doesn't match the beginning of the string (presumably because it's not the beginning of a line). If not set, then the beginning-of-line operator does match the beginning of the string. */ #define REG_NOTBOL 1 /* Like REG_NOTBOL, except for the end-of-line. */ #define REG_NOTEOL (1 << 1) /* If any error codes are removed, changed, or added, update the `re_error_msg' table in regex.c. */ typedef enum { REG_NOERROR = 0, /* Success. */ REG_NOMATCH, /* Didn't find a match (for regexec). */ /* POSIX regcomp return error codes. (In the order listed in the standard.) */ REG_BADPAT, /* Invalid pattern. */ REG_ECOLLATE, /* Not implemented. */ REG_ECTYPE, /* Invalid character class name. */ REG_EESCAPE, /* Trailing backslash. */ REG_ESUBREG, /* Invalid back reference. */ REG_EBRACK, /* Unmatched left bracket. */ REG_EPAREN, /* Parenthesis imbalance. */ REG_EBRACE, /* Unmatched \{. */ REG_BADBR, /* Invalid contents of \{\}. */ REG_ERANGE, /* Invalid range end. */ REG_ESPACE, /* Ran out of memory. */ REG_BADRPT, /* No preceding re for repetition op. */ /* Error codes we've added. */ REG_EEND, /* Premature end. */ REG_ESIZE, /* Compiled pattern bigger than 2^16 bytes. */ REG_ERPAREN /* Unmatched ) or \); not returned from regcomp. */ } reg_errcode_t; /* This data structure represents a compiled pattern. Before calling the pattern compiler, the fields `buffer', `allocated', `fastmap', `translate', and `no_sub' can be set. After the pattern has been compiled, the `re_nsub' field is available. All other fields are private to the regex routines. */ #ifndef RE_TRANSLATE_TYPE #define RE_TRANSLATE_TYPE char * #define RE_TRANSLATE(TBL, C) ((TBL)[C]) #define RE_TRANSLATE_P(TBL) (TBL) #endif struct re_pattern_buffer { /* [[[begin pattern_buffer]]] */ /* Space that holds the compiled pattern. It is declared as `unsigned char *' because its elements are sometimes used as array indexes. */ unsigned char *buffer; /* Number of bytes to which `buffer' points. */ unsigned long allocated; /* Number of bytes actually used in `buffer'. */ unsigned long used; /* Syntax setting with which the pattern was compiled. */ reg_syntax_t syntax; /* Pointer to a fastmap, if any, otherwise zero. re_search uses the fastmap, if there is one, to skip over impossible starting points for matches. */ char *fastmap; /* Either a translate table to apply to all characters before comparing them, or zero for no translation. The translation is applied to a pattern when it is compiled and to a string when it is matched. */ RE_TRANSLATE_TYPE translate; /* Number of subexpressions found by the compiler. */ size_t re_nsub; /* Zero if this pattern cannot match the empty string, one else. Well, in truth it's used only in `re_search_2', to see whether or not we should use the fastmap, so we don't set this absolutely perfectly; see `re_compile_fastmap' (the `duplicate' case). */ unsigned can_be_null : 1; /* If REGS_UNALLOCATED, allocate space in the `regs' structure for `max (RE_NREGS, re_nsub + 1)' groups. If REGS_REALLOCATE, reallocate space if necessary. If REGS_FIXED, use what's there. */ #define REGS_UNALLOCATED 0 #define REGS_REALLOCATE 1 #define REGS_FIXED 2 unsigned regs_allocated : 2; /* Set to zero when `regex_compile' compiles a pattern; set to one by `re_compile_fastmap' if it updates the fastmap. */ unsigned fastmap_accurate : 1; /* If set, `re_match_2' does not return information about subexpressions. */ unsigned no_sub : 1; /* If set, a beginning-of-line anchor doesn't match at the beginning of the string. */ unsigned not_bol : 1; /* Similarly for an end-of-line anchor. */ unsigned not_eol : 1; /* If true, an anchor at a newline matches. */ unsigned newline_anchor : 1; /* If true, multi-byte form in the `buffer' should be recognized as a multibyte character. */ unsigned multibyte : 1; /* [[[end pattern_buffer]]] */ }; typedef struct re_pattern_buffer regex_t; /* Type for byte offsets within the string. POSIX mandates this. */ typedef int regoff_t; /* This is the structure we store register match data in. See regex.texinfo for a full description of what registers match. */ struct re_registers { unsigned num_regs; regoff_t *start; regoff_t *end; }; /* If `regs_allocated' is REGS_UNALLOCATED in the pattern buffer, `re_match_2' returns information about at least this many registers the first time a `regs' structure is passed. */ #ifndef RE_NREGS #define RE_NREGS 30 #endif /* POSIX specification for registers. Aside from the different names than `re_registers', POSIX uses an array of structures, instead of a structure of arrays. */ typedef struct { regoff_t rm_so; /* Byte offset from string's start to substring's start. */ regoff_t rm_eo; /* Byte offset from string's start to substring's end. */ } regmatch_t; /* Declarations for routines. */ /* To avoid duplicating every routine declaration -- once with a prototype (if we are ANSI), and once without (if we aren't) -- we use the following macro to declare argument types. This unfortunately clutters up the declarations a bit, but I think it's worth it. */ #if __STDC__ #define _RE_ARGS(args) args #else /* not __STDC__ */ #define _RE_ARGS(args) () #endif /* not __STDC__ */ /* Sets the current default syntax to SYNTAX, and return the old syntax. You can also simply assign to the `re_syntax_options' variable. */ extern reg_syntax_t re_set_syntax _RE_ARGS ((reg_syntax_t syntax)); /* Compile the regular expression PATTERN, with length LENGTH and syntax given by the global `re_syntax_options', into the buffer BUFFER. Return NULL if successful, and an error string if not. */ extern const char *re_compile_pattern _RE_ARGS ((const char *pattern, int length, struct re_pattern_buffer *buffer)); /* Compile a fastmap for the compiled pattern in BUFFER; used to accelerate searches. Return 0 if successful and -2 if was an internal error. */ extern int re_compile_fastmap _RE_ARGS ((struct re_pattern_buffer *buffer)); /* Search in the string STRING (with length LENGTH) for the pattern compiled into BUFFER. Start searching at position START, for RANGE characters. Return the starting position of the match, -1 for no match, or -2 for an internal error. Also return register information in REGS (if REGS and BUFFER->no_sub are nonzero). */ extern int re_search _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string, int length, int start, int range, struct re_registers *regs)); /* Like `re_search', but search in the concatenation of STRING1 and STRING2. Also, stop searching at index START + STOP. */ extern int re_search_2 _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string1, int length1, const char *string2, int length2, int start, int range, struct re_registers *regs, int stop)); /* Like `re_search', but return how many characters in STRING the regexp in BUFFER matched, starting at position START. */ extern int re_match _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string, int length, int start, struct re_registers *regs)); /* Relates to `re_match' as `re_search_2' relates to `re_search'. */ extern int re_match_2 _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string1, int length1, const char *string2, int length2, int start, struct re_registers *regs, int stop)); /* Set REGS to hold NUM_REGS registers, storing them in STARTS and ENDS. Subsequent matches using BUFFER and REGS will use this memory for recording register information. STARTS and ENDS must be allocated with malloc, and must each be at least `NUM_REGS * sizeof (regoff_t)' bytes long. If NUM_REGS == 0, then subsequent matches should allocate their own register data. Unless this function is called, the first search or match using PATTERN_BUFFER will allocate its own register data, without freeing the old data. */ extern void re_set_registers _RE_ARGS ((struct re_pattern_buffer *buffer, struct re_registers *regs, unsigned num_regs, regoff_t *starts, regoff_t *ends)); #ifdef _REGEX_RE_COMP /* 4.2 bsd compatibility. */ /* CVS: don't use prototypes: they may conflict with system headers. */ extern char *re_comp _RE_ARGS (()); extern int re_exec _RE_ARGS (()); #endif /* POSIX compatibility. */ extern int regcomp _RE_ARGS ((regex_t *preg, const char *pattern, int cflags)); extern int regexec _RE_ARGS ((const regex_t *preg, const char *string, size_t nmatch, regmatch_t pmatch[], int eflags)); extern size_t regerror _RE_ARGS ((int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size)); extern void regfree _RE_ARGS ((regex_t *preg)); #endif /* not __REGEXP_LIBRARY_H__ */ /* Local variables: make-backup-files: t version-control: t trim-versions-without-asking: nil End: */ /sys/src/ape/cmd/diff/sdiff.c 664 sys sys 1367613436 23417 /* SDIFF -- interactive merge front end to diff Copyright (C) 1992, 1993, 1994 Free Software Foundation, Inc. This file is part of GNU DIFF. GNU DIFF 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. GNU DIFF 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 GNU DIFF; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ /* GNU SDIFF was written by Thomas Lord. */ #include __FBSDID("$FreeBSD: src/contrib/diff/sdiff.c,v 1.1.1.1.12.1 2002/01/28 01:26:35 nectar Exp $"); #include "system.h" #include #include #include "getopt.h" /* Size of chunks read from files which must be parsed into lines. */ #define SDIFF_BUFSIZE ((size_t) 65536) /* Default name of the diff program */ #ifndef DIFF_PROGRAM #define DIFF_PROGRAM "/usr/bin/diff" #endif /* Users' editor of nonchoice */ #ifndef DEFAULT_EDITOR_PROGRAM #define DEFAULT_EDITOR_PROGRAM "ed" #endif extern char version_string[]; static char const *program_name; static char const *diffbin = DIFF_PROGRAM; static char const *edbin = DEFAULT_EDITOR_PROGRAM; static char const **diffargv; static char *tmpname; static int volatile tmpmade; #if HAVE_FORK static pid_t volatile diffpid; #endif struct line_filter; static FILE *ck_fopen PARAMS((char const *, char const *)); static RETSIGTYPE catchsig PARAMS((int)); static VOID *xmalloc PARAMS((size_t)); static char const *expand_name PARAMS((char *, int, char const *)); static int edit PARAMS((struct line_filter *, int, struct line_filter *, int, FILE*)); static int interact PARAMS((struct line_filter *, struct line_filter *, struct line_filter *, FILE*)); static int lf_snarf PARAMS((struct line_filter *, char *, size_t)); static int skip_white PARAMS((void)); static size_t ck_fread PARAMS((char *, size_t, FILE *)); static size_t lf_refill PARAMS((struct line_filter *)); static void checksigs PARAMS((void)); static void ck_fclose PARAMS((FILE *)); static void ck_fflush PARAMS((FILE *)); static void ck_fwrite PARAMS((char const *, size_t, FILE *)); static void cleanup PARAMS((void)); static void diffarg PARAMS((char const *)); static void execdiff PARAMS((void)); static void exiterr PARAMS((void)); static void fatal PARAMS((char const *)); static void flush_line PARAMS((void)); static void give_help PARAMS((void)); static void lf_copy PARAMS((struct line_filter *, int, FILE *)); static void lf_init PARAMS((struct line_filter *, FILE *)); static void lf_skip PARAMS((struct line_filter *, int)); static void perror_fatal PARAMS((char const *)); static void trapsigs PARAMS((void)); static void try_help PARAMS((char const *)); static void untrapsig PARAMS((int)); static void usage PARAMS((void)); static int diraccess PARAMS((char const *)); /* Options: */ /* name of output file if -o spec'd */ static char *out_file; /* do not print common lines if true, set by -s option */ static int suppress_common_flag; static struct option const longopts[] = { {"ignore-blank-lines", 0, 0, 'B'}, {"speed-large-files", 0, 0, 'H'}, {"ignore-matching-lines", 1, 0, 'I'}, {"ignore-all-space", 0, 0, 'W'}, /* swap W and w for historical reasons */ {"text", 0, 0, 'a'}, {"ignore-space-change", 0, 0, 'b'}, {"minimal", 0, 0, 'd'}, {"ignore-case", 0, 0, 'i'}, {"left-column", 0, 0, 'l'}, {"output", 1, 0, 'o'}, {"suppress-common-lines", 0, 0, 's'}, {"expand-tabs", 0, 0, 't'}, {"width", 1, 0, 'w'}, {"version", 0, 0, 'v'}, {"help", 0, 0, 129}, {0, 0, 0, 0} }; static void try_help (reason) char const *reason; { if (reason) fprintf (stderr, "%s: %s\n", program_name, reason); fprintf (stderr, "%s: Try `%s --help' for more information.\n", program_name, program_name); exit (2); } static void usage () { printf ("Usage: %s [OPTIONS]... FILE1 FILE2\n\n", program_name); printf ("%s", "\ -o FILE --output=FILE Operate interactively, sending output to FILE.\n\n"); printf ("%s", "\ -i --ignore-case Consider upper- and lower-case to be the same.\n\ -W --ignore-all-space Ignore all white space.\n\ -b --ignore-space-change Ignore changes in the amount of white space.\n\ -B --ignore-blank-lines Ignore changes whose lines are all blank.\n\ -I RE --ignore-matching-lines=RE Ignore changes whose lines all match RE.\n\ -a --text Treat all files as text.\n\n"); printf ("%s", "\ -w NUM --width=NUM Output at most NUM (default 130) characters per line.\n\ -l --left-column Output only the left column of common lines.\n\ -s --suppress-common-lines Do not output common lines.\n\n"); printf ("\ -t --expand-tabs Expand tabs to spaces in output.\n\n"); printf ("%s", "\ -d --minimal Try hard to find a smaller set of changes.\n\ -H --speed-large-files Assume large files and many scattered small changes.\n\n"); printf ("%s", "\ -v --version Output version info.\n\ --help Output this help.\n\n\ If FILE1 or FILE2 is `-', read standard input.\n"); } static void cleanup () { #if HAVE_FORK if (0 < diffpid) kill (diffpid, SIGPIPE); #endif if (tmpmade) unlink (tmpname); } static void exiterr () { cleanup (); untrapsig (0); checksigs (); exit (2); } static void fatal (msg) char const *msg; { fprintf (stderr, "%s: %s\n", program_name, msg); exiterr (); } static void perror_fatal (msg) char const *msg; { int e = errno; checksigs (); fprintf (stderr, "%s: ", program_name); errno = e; perror (msg); exiterr (); } /* malloc freely or DIE! */ static VOID * xmalloc (size) size_t size; { VOID *r = (VOID *) malloc (size); if (!r) fatal ("memory exhausted"); return r; } static FILE * ck_fopen (fname, type) char const *fname, *type; { FILE *r = fopen (fname, type); if (!r) perror_fatal (fname); return r; } static void ck_fclose (f) FILE *f; { if (fclose (f)) perror_fatal ("input/output error"); } static size_t ck_fread (buf, size, f) char *buf; size_t size; FILE *f; { size_t r = fread (buf, sizeof (char), size, f); if (r == 0 && ferror (f)) perror_fatal ("input error"); return r; } static void ck_fwrite (buf, size, f) char const *buf; size_t size; FILE *f; { if (fwrite (buf, sizeof (char), size, f) != size) perror_fatal ("output error"); } static void ck_fflush (f) FILE *f; { if (fflush (f) != 0) perror_fatal ("output error"); } static char const * expand_name (name, is_dir, other_name) char *name; int is_dir; char const *other_name; { if (strcmp (name, "-") == 0) fatal ("cannot interactively merge standard input"); if (!is_dir) return name; else { /* Yield NAME/BASE, where BASE is OTHER_NAME's basename. */ char const *p = filename_lastdirchar (other_name); char const *base = p ? p+1 : other_name; size_t namelen = strlen (name), baselen = strlen (base); char *r = xmalloc (namelen + baselen + 2); memcpy (r, name, namelen); r[namelen] = '/'; memcpy (r + namelen + 1, base, baselen + 1); return r; } } struct line_filter { FILE *infile; char *bufpos; char *buffer; char *buflim; }; static void lf_init (lf, infile) struct line_filter *lf; FILE *infile; { lf->infile = infile; lf->bufpos = lf->buffer = lf->buflim = xmalloc (SDIFF_BUFSIZE + 1); lf->buflim[0] = '\n'; } /* Fill an exhausted line_filter buffer from its INFILE */ static size_t lf_refill (lf) struct line_filter *lf; { size_t s = ck_fread (lf->buffer, SDIFF_BUFSIZE, lf->infile); lf->bufpos = lf->buffer; lf->buflim = lf->buffer + s; lf->buflim[0] = '\n'; checksigs (); return s; } /* Advance LINES on LF's infile, copying lines to OUTFILE */ static void lf_copy (lf, lines, outfile) struct line_filter *lf; int lines; FILE *outfile; { char *start = lf->bufpos; while (lines) { lf->bufpos = (char *) memchr (lf->bufpos, '\n', lf->buflim - lf->bufpos); if (! lf->bufpos) { ck_fwrite (start, lf->buflim - start, outfile); if (! lf_refill (lf)) return; start = lf->bufpos; } else { --lines; ++lf->bufpos; } } ck_fwrite (start, lf->bufpos - start, outfile); } /* Advance LINES on LF's infile without doing output */ static void lf_skip (lf, lines) struct line_filter *lf; int lines; { while (lines) { lf->bufpos = (char *) memchr (lf->bufpos, '\n', lf->buflim - lf->bufpos); if (! lf->bufpos) { if (! lf_refill (lf)) break; } else { --lines; ++lf->bufpos; } } } /* Snarf a line into a buffer. Return EOF if EOF, 0 if error, 1 if OK. */ static int lf_snarf (lf, buffer, bufsize) struct line_filter *lf; char *buffer; size_t bufsize; { char *start = lf->bufpos; for (;;) { char *next = (char *) memchr (start, '\n', lf->buflim + 1 - start); size_t s = next - start; if (bufsize <= s) return 0; memcpy (buffer, start, s); if (next < lf->buflim) { buffer[s] = 0; lf->bufpos = next + 1; return 1; } if (! lf_refill (lf)) return s ? 0 : EOF; buffer += s; bufsize -= s; start = next; } } int main (argc, argv) int argc; char *argv[]; { int opt; char *editor; char *differ; initialize_main (&argc, &argv); program_name = argv[0]; editor = getenv ("EDITOR"); if (editor) edbin = editor; differ = getenv ("DIFF"); if (differ) diffbin = differ; diffarg ("diff"); /* parse command line args */ while ((opt = getopt_long (argc, argv, "abBdHiI:lo:stvw:W", longopts, 0)) != EOF) { switch (opt) { case 'a': diffarg ("-a"); break; case 'b': diffarg ("-b"); break; case 'B': diffarg ("-B"); break; case 'd': diffarg ("-d"); break; case 'H': diffarg ("-H"); break; case 'i': diffarg ("-i"); break; case 'I': diffarg ("-I"); diffarg (optarg); break; case 'l': diffarg ("--left-column"); break; case 'o': out_file = optarg; break; case 's': suppress_common_flag = 1; break; case 't': diffarg ("-t"); break; case 'v': printf ("sdiff - GNU diffutils version %s\n", version_string); exit (0); case 'w': diffarg ("-W"); diffarg (optarg); break; case 'W': diffarg ("-w"); break; case 129: usage (); if (ferror (stdout) || fclose (stdout) != 0) fatal ("write error"); exit (0); default: try_help (0); } } if (argc - optind != 2) try_help (argc - optind < 2 ? "missing operand" : "extra operand"); if (! out_file) { /* easy case: diff does everything for us */ if (suppress_common_flag) diffarg ("--suppress-common-lines"); diffarg ("-y"); diffarg ("--"); diffarg (argv[optind]); diffarg (argv[optind + 1]); diffarg (0); execdiff (); } else { FILE *left, *right, *out, *diffout; int interact_ok; struct line_filter lfilt; struct line_filter rfilt; struct line_filter diff_filt; int leftdir = diraccess (argv[optind]); int rightdir = diraccess (argv[optind + 1]); if (leftdir && rightdir) fatal ("both files to be compared are directories"); left = ck_fopen (expand_name (argv[optind], leftdir, argv[optind + 1]), "r"); ; right = ck_fopen (expand_name (argv[optind + 1], rightdir, argv[optind]), "r"); out = ck_fopen (out_file, "w"); diffarg ("--sdiff-merge-assist"); diffarg ("--"); diffarg (argv[optind]); diffarg (argv[optind + 1]); diffarg (0); trapsigs (); #if ! HAVE_FORK { size_t cmdsize = 1; char *p, *command; int i; for (i = 0; diffargv[i]; i++) cmdsize += 4 * strlen (diffargv[i]) + 3; command = p = xmalloc (cmdsize); for (i = 0; diffargv[i]; i++) { char const *a = diffargv[i]; SYSTEM_QUOTE_ARG (p, a); *p++ = ' '; } p[-1] = '\0'; diffout = popen (command, "r"); if (!diffout) perror_fatal (command); free (command); } #else /* HAVE_FORK */ { int diff_fds[2]; if (pipe (diff_fds) != 0) perror_fatal ("pipe"); diffpid = fork (); if (diffpid < 0) perror_fatal ("fork failed"); if (!diffpid) { signal (SIGINT, SIG_IGN); /* in case user interrupts editor */ signal (SIGPIPE, SIG_DFL); close (diff_fds[0]); if (diff_fds[1] != STDOUT_FILENO) { dup2 (diff_fds[1], STDOUT_FILENO); close (diff_fds[1]); } execdiff (); } close (diff_fds[1]); diffout = fdopen (diff_fds[0], "r"); if (!diffout) perror_fatal ("fdopen"); } #endif /* HAVE_FORK */ lf_init (&diff_filt, diffout); lf_init (&lfilt, left); lf_init (&rfilt, right); interact_ok = interact (&diff_filt, &lfilt, &rfilt, out); ck_fclose (left); ck_fclose (right); ck_fclose (out); { int wstatus; #if ! HAVE_FORK wstatus = pclose (diffout); #else ck_fclose (diffout); while (waitpid (diffpid, &wstatus, 0) < 0) if (errno == EINTR) checksigs (); else perror_fatal ("wait failed"); diffpid = 0; #endif if (tmpmade) { unlink (tmpname); tmpmade = 0; } if (! interact_ok) exiterr (); if (! (WIFEXITED (wstatus) && WEXITSTATUS (wstatus) < 2)) fatal ("Subsidiary diff failed"); untrapsig (0); checksigs (); exit (WEXITSTATUS (wstatus)); } } return 0; /* Fool -Wall . . . */ } static void diffarg (a) char const *a; { static unsigned diffargs, diffargsmax; if (diffargs == diffargsmax) { if (! diffargsmax) { diffargv = (char const **) xmalloc (sizeof (char)); diffargsmax = 8; } diffargsmax *= 2; diffargv = (char const **) realloc (diffargv, diffargsmax * sizeof (char const *)); if (! diffargv) fatal ("out of memory"); } diffargv[diffargs++] = a; } static void execdiff () { execvp (diffbin, (char **) diffargv); write (STDERR_FILENO, diffbin, strlen (diffbin)); write (STDERR_FILENO, ": not found\n", 12); _exit (2); } /* Signal handling */ #define NUM_SIGS (sizeof (sigs) / sizeof (*sigs)) static int const sigs[] = { #ifdef SIGHUP SIGHUP, #endif #ifdef SIGQUIT SIGQUIT, #endif #ifdef SIGTERM SIGTERM, #endif #ifdef SIGXCPU SIGXCPU, #endif #ifdef SIGXFSZ SIGXFSZ, #endif SIGINT, SIGPIPE }; /* Prefer `sigaction' if it is available, since `signal' can lose signals. */ #if HAVE_SIGACTION static struct sigaction initial_action[NUM_SIGS]; #define initial_handler(i) (initial_action[i].sa_handler) #else static RETSIGTYPE (*initial_action[NUM_SIGS]) (); #define initial_handler(i) (initial_action[i]) #endif static int volatile ignore_SIGINT; static int volatile signal_received; static int sigs_trapped; static RETSIGTYPE catchsig (s) int s; { #if ! HAVE_SIGACTION signal (s, SIG_IGN); #endif if (! (s == SIGINT && ignore_SIGINT)) signal_received = s; } static void trapsigs () { int i; #if HAVE_SIGACTION struct sigaction catchaction; bzero (&catchaction, sizeof (catchaction)); catchaction.sa_handler = catchsig; #ifdef SA_INTERRUPT /* Non-Posix BSD-style systems like SunOS 4.1.x need this so that `read' calls are interrupted properly. */ catchaction.sa_flags = SA_INTERRUPT; #endif sigemptyset (&catchaction.sa_mask); for (i = 0; i < NUM_SIGS; i++) sigaddset (&catchaction.sa_mask, sigs[i]); for (i = 0; i < NUM_SIGS; i++) { sigaction (sigs[i], 0, &initial_action[i]); if (initial_handler (i) != SIG_IGN && sigaction (sigs[i], &catchaction, 0) != 0) fatal ("signal error"); } #else /* ! HAVE_SIGACTION */ for (i = 0; i < NUM_SIGS; i++) { initial_action[i] = signal (sigs[i], SIG_IGN); if (initial_handler (i) != SIG_IGN && signal (sigs[i], catchsig) != SIG_IGN) fatal ("signal error"); } #endif /* ! HAVE_SIGACTION */ #if !defined(SIGCHLD) && defined(SIGCLD) #define SIGCHLD SIGCLD #endif #ifdef SIGCHLD /* System V fork+wait does not work if SIGCHLD is ignored. */ signal (SIGCHLD, SIG_DFL); #endif sigs_trapped = 1; } /* Untrap signal S, or all trapped signals if S is zero. */ static void untrapsig (s) int s; { int i; if (sigs_trapped) for (i = 0; i < NUM_SIGS; i++) if ((!s || sigs[i] == s) && initial_handler (i) != SIG_IGN) #if HAVE_SIGACTION sigaction (sigs[i], &initial_action[i], 0); #else signal (sigs[i], initial_action[i]); #endif } /* Exit if a signal has been received. */ static void checksigs () { int s = signal_received; if (s) { cleanup (); /* Yield an exit status indicating that a signal was received. */ untrapsig (s); kill (getpid (), s); /* That didn't work, so exit with error status. */ exit (2); } } static void give_help () { fprintf (stderr,"l:\tuse the left version\n"); fprintf (stderr,"r:\tuse the right version\n"); fprintf (stderr,"e l:\tedit then use the left version\n"); fprintf (stderr,"e r:\tedit then use the right version\n"); fprintf (stderr,"e b:\tedit then use the left and right versions concatenated\n"); fprintf (stderr,"e:\tedit a new version\n"); fprintf (stderr,"s:\tsilently include common lines\n"); fprintf (stderr,"v:\tverbosely include common lines\n"); fprintf (stderr,"q:\tquit\n"); } static int skip_white () { int c; for (;;) { c = getchar (); if (!ISSPACE (c) || c == '\n') break; checksigs (); } if (ferror (stdin)) perror_fatal ("input error"); return c; } static void flush_line () { int c; while ((c = getchar ()) != '\n' && c != EOF) ; if (ferror (stdin)) perror_fatal ("input error"); } /* interpret an edit command */ static int edit (left, lenl, right, lenr, outfile) struct line_filter *left; int lenl; struct line_filter *right; int lenr; FILE *outfile; { for (;;) { int cmd0, cmd1; int gotcmd = 0; cmd1 = 0; /* Pacify `gcc -W'. */ while (!gotcmd) { if (putchar ('%') != '%') perror_fatal ("output error"); ck_fflush (stdout); cmd0 = skip_white (); switch (cmd0) { case 'l': case 'r': case 's': case 'v': case 'q': if (skip_white () != '\n') { give_help (); flush_line (); continue; } gotcmd = 1; break; case 'e': cmd1 = skip_white (); switch (cmd1) { case 'l': case 'r': case 'b': if (skip_white () != '\n') { give_help (); flush_line (); continue; } gotcmd = 1; break; case '\n': gotcmd = 1; break; default: give_help (); flush_line (); continue; } break; case EOF: if (feof (stdin)) { gotcmd = 1; cmd0 = 'q'; break; } /* falls through */ default: flush_line (); /* falls through */ case '\n': give_help (); continue; } } switch (cmd0) { case 'l': lf_copy (left, lenl, outfile); lf_skip (right, lenr); return 1; case 'r': lf_copy (right, lenr, outfile); lf_skip (left, lenl); return 1; case 's': suppress_common_flag = 1; break; case 'v': suppress_common_flag = 0; break; case 'q': return 0; case 'e': { int tfd; FILE *tmp; if (tmpmade) { unlink (tmpname); tmpmade = 0; free (tmpname); } asprintf (&tmpname, "%s/sdiff.XXXXXX", getenv("TMPDIR") ?: P_tmpdir); if (tmpname == NULL) perror_fatal ("temporary file name"); tfd = mkstemp(tmpname); if (tfd == -1) perror_fatal ("temporary file name"); tmp = fdopen (tfd, "w+"); if (tmp == NULL) perror_fatal ("temporary file name"); tmpmade = 1; if (cmd1 == 'l' || cmd1 == 'b') lf_copy (left, lenl, tmp); else lf_skip (left, lenl); if (cmd1 == 'r' || cmd1 == 'b') lf_copy (right, lenr, tmp); else lf_skip (right, lenr); ck_fflush (tmp); { int wstatus; #if ! HAVE_FORK char *command = xmalloc (strlen (edbin) + strlen (tmpname) + 2); sprintf (command, "%s %s", edbin, tmpname); wstatus = system (command); free (command); #else /* HAVE_FORK */ pid_t pid; ignore_SIGINT = 1; checksigs (); pid = fork (); if (pid == 0) { char const *argv[3]; int i = 0; argv[i++] = edbin; argv[i++] = tmpname; argv[i++] = 0; execvp (edbin, (char **) argv); write (STDERR_FILENO, edbin, strlen (edbin)); write (STDERR_FILENO, ": not found\n", 12); _exit (1); } if (pid < 0) perror_fatal ("fork failed"); while (waitpid (pid, &wstatus, 0) < 0) if (errno == EINTR) checksigs (); else perror_fatal ("wait failed"); ignore_SIGINT = 0; #endif /* HAVE_FORK */ if (wstatus != 0) fatal ("Subsidiary editor failed"); } if (fseek (tmp, 0L, SEEK_SET) != 0) perror_fatal ("fseek"); { /* SDIFF_BUFSIZE is too big for a local var in some compilers, so we allocate it dynamically. */ char *buf = xmalloc (SDIFF_BUFSIZE); size_t size; while ((size = ck_fread (buf, SDIFF_BUFSIZE, tmp)) != 0) { checksigs (); ck_fwrite (buf, size, outfile); } ck_fclose (tmp); free (buf); } return 1; } default: give_help (); break; } } } /* Alternately reveal bursts of diff output and handle user commands. */ static int interact (diff, left, right, outfile) struct line_filter *diff; struct line_filter *left; struct line_filter *right; FILE *outfile; { for (;;) { char diff_help[256]; int snarfed = lf_snarf (diff, diff_help, sizeof (diff_help)); if (snarfed <= 0) return snarfed; checksigs (); switch (diff_help[0]) { case ' ': puts (diff_help + 1); break; case 'i': { int lenl = atoi (diff_help + 1), lenr, lenmax; char *p = strchr (diff_help, ','); if (!p) fatal (diff_help); lenr = atoi (p + 1); lenmax = max (lenl, lenr); if (suppress_common_flag) lf_skip (diff, lenmax); else lf_copy (diff, lenmax, stdout); lf_copy (left, lenl, outfile); lf_skip (right, lenr); break; } case 'c': { int lenl = atoi (diff_help + 1), lenr; char *p = strchr (diff_help, ','); if (!p) fatal (diff_help); lenr = atoi (p + 1); lf_copy (diff, max (lenl, lenr), stdout); if (! edit (left, lenl, right, lenr, outfile)) return 0; break; } default: fatal (diff_help); break; } } } /* temporary lossage: this is torn from gnu libc */ /* Return nonzero if DIR is an existing directory. */ static int diraccess (dir) char const *dir; { struct stat buf; return stat (dir, &buf) == 0 && S_ISDIR (buf.st_mode); } /sys/src/ape/cmd/diff/side.c 664 sys sys 1367613436 7012 /* sdiff-format output routines for GNU DIFF. Copyright (C) 1991, 1992, 1993 Free Software Foundation, Inc. This file is part of GNU DIFF. GNU DIFF is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to anyone for the consequences of using it or for whether it serves any particular purpose or works at all, unless he says so in writing. Refer to the GNU DIFF General Public License for full details. Everyone is granted permission to copy, modify and redistribute GNU DIFF, but only under the conditions described in the GNU DIFF General Public License. A copy of this license is supposed to have been given to you along with GNU DIFF so you can know your rights and responsibilities. It should be in a file named COPYING. Among other things, the copyright notice and this notice must be preserved on all copies. */ #include "diff.h" static unsigned print_half_line PARAMS((char const * const *, unsigned, unsigned)); static unsigned tab_from_to PARAMS((unsigned, unsigned)); static void print_1sdiff_line PARAMS((char const * const *, int, char const * const *)); static void print_sdiff_common_lines PARAMS((int, int)); static void print_sdiff_hunk PARAMS((struct change *)); /* Next line number to be printed in the two input files. */ static int next0, next1; /* Print the edit-script SCRIPT as a sdiff style output. */ void print_sdiff_script (script) struct change *script; { begin_output (); next0 = next1 = - files[0].prefix_lines; print_script (script, find_change, print_sdiff_hunk); print_sdiff_common_lines (files[0].valid_lines, files[1].valid_lines); } /* Tab from column FROM to column TO, where FROM <= TO. Yield TO. */ static unsigned tab_from_to (from, to) unsigned from, to; { FILE *out = outfile; unsigned tab; if (! tab_expand_flag) for (tab = from + TAB_WIDTH - from % TAB_WIDTH; tab <= to; tab += TAB_WIDTH) { putc ('\t', out); from = tab; } while (from++ < to) putc (' ', out); return to; } /* * Print the text for half an sdiff line. This means truncate to width * observing tabs, and trim a trailing newline. Returns the last column * written (not the number of chars). */ static unsigned print_half_line (line, indent, out_bound) char const * const *line; unsigned indent, out_bound; { FILE *out = outfile; register unsigned in_position = 0, out_position = 0; register char const *text_pointer = line[0], *text_limit = line[1]; while (text_pointer < text_limit) { register unsigned char c = *text_pointer++; switch (c) { case '\t': { unsigned spaces = TAB_WIDTH - in_position % TAB_WIDTH; if (in_position == out_position) { unsigned tabstop = out_position + spaces; if (tab_expand_flag) { if (out_bound < tabstop) tabstop = out_bound; for (; out_position < tabstop; out_position++) putc (' ', out); } else if (tabstop < out_bound) { out_position = tabstop; putc (c, out); } } in_position += spaces; } break; case '\r': { putc (c, out); tab_from_to (0, indent); in_position = out_position = 0; } break; case '\b': if (in_position != 0 && --in_position < out_bound) if (out_position <= in_position) /* Add spaces to make up for suppressed tab past out_bound. */ for (; out_position < in_position; out_position++) putc (' ', out); else { out_position = in_position; putc (c, out); } break; case '\f': case '\v': control_char: if (in_position < out_bound) putc (c, out); break; default: if (! ISPRINT (c)) goto control_char; /* falls through */ case ' ': if (in_position++ < out_bound) { out_position = in_position; putc (c, out); } break; case '\n': return out_position; } } return out_position; } /* * Print side by side lines with a separator in the middle. * 0 parameters are taken to indicate white space text. * Blank lines that can easily be caught are reduced to a single newline. */ static void print_1sdiff_line (left, sep, right) char const * const *left; int sep; char const * const *right; { FILE *out = outfile; unsigned hw = sdiff_half_width, c2o = sdiff_column2_offset; unsigned col = 0; int put_newline = 0; if (left) { if (left[1][-1] == '\n') put_newline = 1; col = print_half_line (left, 0, hw); } if (sep != ' ') { col = tab_from_to (col, (hw + c2o - 1) / 2) + 1; if (sep == '|' && put_newline != (right[1][-1] == '\n')) sep = put_newline ? '/' : '\\'; putc (sep, out); } if (right) { if (right[1][-1] == '\n') put_newline = 1; if (**right != '\n') { col = tab_from_to (col, c2o); print_half_line (right, col, hw); } } if (put_newline) putc ('\n', out); } /* Print lines common to both files in side-by-side format. */ static void print_sdiff_common_lines (limit0, limit1) int limit0, limit1; { int i0 = next0, i1 = next1; if (! sdiff_skip_common_lines && (i0 != limit0 || i1 != limit1)) { if (sdiff_help_sdiff) fprintf (outfile, "i%d,%d\n", limit0 - i0, limit1 - i1); if (! sdiff_left_only) { while (i0 != limit0 && i1 != limit1) print_1sdiff_line (&files[0].linbuf[i0++], ' ', &files[1].linbuf[i1++]); while (i1 != limit1) print_1sdiff_line (0, ')', &files[1].linbuf[i1++]); } while (i0 != limit0) print_1sdiff_line (&files[0].linbuf[i0++], '(', 0); } next0 = limit0; next1 = limit1; } /* Print a hunk of an sdiff diff. This is a contiguous portion of a complete edit script, describing changes in consecutive lines. */ static void print_sdiff_hunk (hunk) struct change *hunk; { int first0, last0, first1, last1, deletes, inserts; register int i, j; /* Determine range of line numbers involved in each file. */ analyze_hunk (hunk, &first0, &last0, &first1, &last1, &deletes, &inserts); if (!deletes && !inserts) return; /* Print out lines up to this change. */ print_sdiff_common_lines (first0, first1); if (sdiff_help_sdiff) fprintf (outfile, "c%d,%d\n", last0 - first0 + 1, last1 - first1 + 1); /* Print ``xxx | xxx '' lines */ if (inserts && deletes) { for (i = first0, j = first1; i <= last0 && j <= last1; ++i, ++j) print_1sdiff_line (&files[0].linbuf[i], '|', &files[1].linbuf[j]); deletes = i <= last0; inserts = j <= last1; next0 = first0 = i; next1 = first1 = j; } /* Print `` > xxx '' lines */ if (inserts) { for (j = first1; j <= last1; ++j) print_1sdiff_line (0, '>', &files[1].linbuf[j]); next1 = j; } /* Print ``xxx < '' lines */ if (deletes) { for (i = first0; i <= last0; ++i) print_1sdiff_line (&files[0].linbuf[i], '<', 0); next0 = i; } } /sys/src/ape/cmd/diff/system.h 664 sys sys 1367613436 5775 /* System dependent declarations. Copyright (C) 1988, 1989, 1992, 1993, 1994 Free Software Foundation, Inc. This file is part of GNU DIFF. GNU DIFF 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. GNU DIFF 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 GNU DIFF; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ /* We must define `volatile' and `const' first (the latter inside config.h), so that they're used consistently in all system includes. */ #if !__STDC__ #ifndef volatile #define volatile #endif #endif #include #include #include #if __STDC__ #define PARAMS(args) args #define VOID void #else #define PARAMS(args) () #define VOID char #endif #if STAT_MACROS_BROKEN #undef S_ISBLK #undef S_ISCHR #undef S_ISDIR #undef S_ISFIFO #undef S_ISREG #undef S_ISSOCK #endif #ifndef S_ISDIR #define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) #endif #ifndef S_ISREG #define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) #endif #if !defined(S_ISBLK) && defined(S_IFBLK) #define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK) #endif #if !defined(S_ISCHR) && defined(S_IFCHR) #define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR) #endif #if !defined(S_ISFIFO) && defined(S_IFFIFO) #define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFFIFO) #endif #if !defined(S_ISSOCK) && defined(S_IFSOCK) #define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK) #endif #if HAVE_UNISTD_H #include #endif #ifndef SEEK_SET #define SEEK_SET 0 #endif #ifndef SEEK_CUR #define SEEK_CUR 1 #endif #ifndef STDIN_FILENO #define STDIN_FILENO 0 #endif #ifndef STDOUT_FILENO #define STDOUT_FILENO 1 #endif #ifndef STDERR_FILENO #define STDERR_FILENO 2 #endif #if HAVE_TIME_H #include #else #include #endif #if HAVE_FCNTL_H #include #else #if HAVE_SYS_FILE_H #include #endif #endif #if !HAVE_DUP2 #define dup2(f,t) (close (t), fcntl (f,F_DUPFD,t)) #endif #ifndef O_RDONLY #define O_RDONLY 0 #endif #if HAVE_SYS_WAIT_H #include #endif #ifndef WEXITSTATUS #define WEXITSTATUS(stat_val) ((unsigned) (stat_val) >> 8) #endif #ifndef WIFEXITED #define WIFEXITED(stat_val) (((stat_val) & 255) == 0) #endif #ifndef STAT_BLOCKSIZE #if HAVE_ST_BLKSIZE #define STAT_BLOCKSIZE(s) (s).st_blksize #else #define STAT_BLOCKSIZE(s) (8 * 1024) #endif #endif #if HAVE_DIRENT_H # include # define NAMLEN(dirent) strlen((dirent)->d_name) #else # define dirent direct # define NAMLEN(dirent) ((dirent)->d_namlen) # if HAVE_SYS_NDIR_H # include # endif # if HAVE_SYS_DIR_H # include # endif # if HAVE_NDIR_H # include # endif #endif #if HAVE_VFORK_H #include #endif #if HAVE_STDLIB_H #include #else VOID *malloc (); VOID *realloc (); #endif #ifndef getenv char *getenv (); #endif #if HAVE_LIMITS_H #include #endif #ifndef INT_MAX #define INT_MAX 2147483647 #endif #ifndef CHAR_BIT #define CHAR_BIT 8 #endif #if STDC_HEADERS || HAVE_STRING_H # include # ifndef bzero # define bzero(s, n) memset (s, 0, n) # endif #else # if !HAVE_STRCHR # define strchr index # define strrchr rindex # endif char *strchr (), *strrchr (); # if !HAVE_MEMCHR # define memcmp(s1, s2, n) bcmp (s1, s2, n) # define memcpy(d, s, n) bcopy (s, d, n) void *memchr (); # endif #endif #include /* CTYPE_DOMAIN (C) is nonzero if the unsigned char C can safely be given as an argument to macros like `isspace'. */ #if STDC_HEADERS #define CTYPE_DOMAIN(c) 1 #else #define CTYPE_DOMAIN(c) ((unsigned) (c) <= 0177) #endif #ifndef ISPRINT #define ISPRINT(c) (CTYPE_DOMAIN (c) && isprint (c)) #endif #ifndef ISSPACE #define ISSPACE(c) (CTYPE_DOMAIN (c) && isspace (c)) #endif #ifndef ISUPPER #define ISUPPER(c) (CTYPE_DOMAIN (c) && isupper (c)) #endif #ifndef ISDIGIT #define ISDIGIT(c) ((unsigned) (c) - '0' <= 9) #endif #include #if !STDC_HEADERS extern int errno; #endif #ifdef min #undef min #endif #ifdef max #undef max #endif #define min(a,b) ((a) <= (b) ? (a) : (b)) #define max(a,b) ((a) >= (b) ? (a) : (b)) /* This section contains Posix-compliant defaults for macros that are meant to be overridden by hand in config.h as needed. */ #ifndef filename_cmp #define filename_cmp(a, b) strcmp (a, b) #endif #ifndef filename_lastdirchar #define filename_lastdirchar(filename) strrchr (filename, '/') #endif #ifndef HAVE_FORK #define HAVE_FORK 1 #endif #ifndef HAVE_SETMODE #define HAVE_SETMODE 0 #endif #ifndef initialize_main #define initialize_main(argcp, argvp) #endif /* Do struct stat *S, *T describe the same file? Answer -1 if unknown. */ #ifndef same_file /* #define same_file(s,t) ((s)->st_ino==(t)->st_ino && (s)->st_dev==(t)->st_dev) */ #define same_file(s,t) 0 #endif /* Place into Q a quoted version of A suitable for `popen' or `system', incrementing Q and junking A. Do not increment Q by more than 4 * strlen (A) + 2. */ #ifndef SYSTEM_QUOTE_ARG #define SYSTEM_QUOTE_ARG(q, a) \ { \ *(q)++ = '\''; \ for (; *(a); *(q)++ = *(a)++) \ if (*(a) == '\'') \ { \ *(q)++ = '\''; \ *(q)++ = '\\'; \ *(q)++ = '\''; \ } \ *(q)++ = '\''; \ } #endif #ifndef FOLD_FN_CHAR #define FOLD_FN_CHAR(c) (c) #define fnfold(filename) (filename) #define fncmp strcmp #endif /sys/src/ape/cmd/diff/util.c 664 sys sys 1367613436 18335 /* Support routines for GNU DIFF. Copyright (C) 1988, 1989, 1992, 1993, 1994 Free Software Foundation, Inc. This file is part of GNU DIFF. GNU DIFF 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. GNU DIFF 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 GNU DIFF; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ /* $FreeBSD: src/contrib/diff/util.c,v 1.2.6.2 2000/09/20 02:24:32 jkh Exp $ */ #include "diff.h" #ifndef PR_PROGRAM #define PR_PROGRAM "/bin/pr" #endif /* Queue up one-line messages to be printed at the end, when -l is specified. Each message is recorded with a `struct msg'. */ struct msg { struct msg *next; char const *format; char const *arg1; char const *arg2; char const *arg3; char const *arg4; }; /* Head of the chain of queues messages. */ static struct msg *msg_chain; /* Tail of the chain of queues messages. */ static struct msg **msg_chain_end = &msg_chain; /* Use when a system call returns non-zero status. TEXT should normally be the file name. */ void perror_with_name (text) char const *text; { int e = errno; fprintf (stderr, "%s: ", program_name); errno = e; perror (text); } /* Use when a system call returns non-zero status and that is fatal. */ void pfatal_with_name (text) char const *text; { int e = errno; print_message_queue (); fprintf (stderr, "%s: ", program_name); errno = e; perror (text); exit (2); } /* Print an error message from the format-string FORMAT with args ARG1 and ARG2. */ void error (format, arg, arg1) char const *format, *arg, *arg1; { fprintf (stderr, "%s: ", program_name); fprintf (stderr, format, arg, arg1); fprintf (stderr, "\n"); } /* Print an error message containing the string TEXT, then exit. */ void fatal (m) char const *m; { print_message_queue (); error ("%s", m, 0); exit (2); } /* Like printf, except if -l in effect then save the message and print later. This is used for things like "binary files differ" and "Only in ...". */ void message (format, arg1, arg2) char const *format, *arg1, *arg2; { message5 (format, arg1, arg2, 0, 0); } void message5 (format, arg1, arg2, arg3, arg4) char const *format, *arg1, *arg2, *arg3, *arg4; { if (paginate_flag) { struct msg *new = (struct msg *) xmalloc (sizeof (struct msg)); new->format = format; new->arg1 = concat (arg1, "", ""); new->arg2 = concat (arg2, "", ""); new->arg3 = arg3 ? concat (arg3, "", "") : 0; new->arg4 = arg4 ? concat (arg4, "", "") : 0; new->next = 0; *msg_chain_end = new; msg_chain_end = &new->next; } else { if (sdiff_help_sdiff) putchar (' '); printf (format, arg1, arg2, arg3, arg4); } } /* Output all the messages that were saved up by calls to `message'. */ void print_message_queue () { struct msg *m; for (m = msg_chain; m; m = m->next) printf (m->format, m->arg1, m->arg2, m->arg3, m->arg4); } /* Call before outputting the results of comparing files NAME0 and NAME1 to set up OUTFILE, the stdio stream for the output to go to. Usually, OUTFILE is just stdout. But when -l was specified we fork off a `pr' and make OUTFILE a pipe to it. `pr' then outputs to our stdout. */ static char const *current_name0; static char const *current_name1; static int current_depth; void setup_output (name0, name1, depth) char const *name0, *name1; int depth; { current_name0 = name0; current_name1 = name1; current_depth = depth; outfile = 0; } #if HAVE_FORK static pid_t pr_pid; #endif void begin_output () { char *name; if (outfile != 0) return; /* Construct the header of this piece of diff. */ name = xmalloc (strlen (current_name0) + strlen (current_name1) + strlen (switch_string) + 7); /* Posix.2 section 4.17.6.1.1 specifies this format. But there is a bug in the first printing (IEEE Std 1003.2-1992 p 251 l 3304): it says that we must print only the last component of the pathnames. This requirement is silly and does not match historical practice. */ sprintf (name, "diff%s %s %s", switch_string, current_name0, current_name1); if (paginate_flag) { /* Make OUTFILE a pipe to a subsidiary `pr'. */ #if HAVE_FORK int pipes[2]; if (pipe (pipes) != 0) pfatal_with_name ("pipe"); fflush (stdout); pr_pid = fork (); if (pr_pid < 0) pfatal_with_name ("vfork"); if (pr_pid == 0) { close (pipes[1]); if (pipes[0] != STDIN_FILENO) { if (dup2 (pipes[0], STDIN_FILENO) < 0) pfatal_with_name ("dup2"); close (pipes[0]); } #ifdef __FreeBSD__ execl (PR_PROGRAM, PR_PROGRAM, "-F", "-h", name, 0); #else execl (PR_PROGRAM, PR_PROGRAM, "-f", "-h", name, 0); #endif pfatal_with_name (PR_PROGRAM); } else { close (pipes[0]); outfile = fdopen (pipes[1], "w"); if (!outfile) pfatal_with_name ("fdopen"); } #else /* ! HAVE_FORK */ char *command = xmalloc (4 * strlen (name) + strlen (PR_PROGRAM) + 10); char *p; char const *a = name; sprintf (command, "%s -f -h ", PR_PROGRAM); p = command + strlen (command); SYSTEM_QUOTE_ARG (p, a); *p = 0; outfile = popen (command, "w"); if (!outfile) pfatal_with_name (command); free (command); #endif /* ! HAVE_FORK */ } else { /* If -l was not specified, output the diff straight to `stdout'. */ outfile = stdout; /* If handling multiple files (because scanning a directory), print which files the following output is about. */ if (current_depth > 0) printf ("%s\n", name); } free (name); /* A special header is needed at the beginning of context output. */ switch (output_style) { case OUTPUT_CONTEXT: print_context_header (files, 0); break; case OUTPUT_UNIFIED: print_context_header (files, 1); break; default: break; } } /* Call after the end of output of diffs for one file. Close OUTFILE and get rid of the `pr' subfork. */ void finish_output () { if (outfile != 0 && outfile != stdout) { int wstatus; if (ferror (outfile)) fatal ("write error"); #if ! HAVE_FORK wstatus = pclose (outfile); #else /* HAVE_FORK */ if (fclose (outfile) != 0) pfatal_with_name ("write error"); if (waitpid (pr_pid, &wstatus, 0) < 0) pfatal_with_name ("waitpid"); #endif /* HAVE_FORK */ if (wstatus != 0) fatal ("subsidiary pr failed"); } outfile = 0; } /* Compare two lines (typically one from each input file) according to the command line options. For efficiency, this is invoked only when the lines do not match exactly but an option like -i might cause us to ignore the difference. Return nonzero if the lines differ. */ int line_cmp (s1, s2) char const *s1, *s2; { register unsigned char const *t1 = (unsigned char const *) s1; register unsigned char const *t2 = (unsigned char const *) s2; while (1) { register unsigned char c1 = *t1++; register unsigned char c2 = *t2++; /* Test for exact char equality first, since it's a common case. */ if (c1 != c2) { /* Ignore horizontal white space if -b or -w is specified. */ if (ignore_all_space_flag) { /* For -w, just skip past any white space. */ while (ISSPACE (c1) && c1 != '\n') c1 = *t1++; while (ISSPACE (c2) && c2 != '\n') c2 = *t2++; } else if (ignore_space_change_flag) { /* For -b, advance past any sequence of white space in line 1 and consider it just one Space, or nothing at all if it is at the end of the line. */ if (ISSPACE (c1)) { while (c1 != '\n') { c1 = *t1++; if (! ISSPACE (c1)) { --t1; c1 = ' '; break; } } } /* Likewise for line 2. */ if (ISSPACE (c2)) { while (c2 != '\n') { c2 = *t2++; if (! ISSPACE (c2)) { --t2; c2 = ' '; break; } } } if (c1 != c2) { /* If we went too far when doing the simple test for equality, go back to the first non-white-space character in both sides and try again. */ if (c2 == ' ' && c1 != '\n' && (unsigned char const *) s1 + 1 < t1 && ISSPACE(t1[-2])) { --t1; continue; } if (c1 == ' ' && c2 != '\n' && (unsigned char const *) s2 + 1 < t2 && ISSPACE(t2[-2])) { --t2; continue; } } } /* Lowercase all letters if -i is specified. */ if (ignore_case_flag) { if (ISUPPER (c1)) c1 = tolower (c1); if (ISUPPER (c2)) c2 = tolower (c2); } if (c1 != c2) break; } if (c1 == '\n') return 0; } return (1); } /* Find the consecutive changes at the start of the script START. Return the last link before the first gap. */ struct change * find_change (start) struct change *start; { return start; } struct change * find_reverse_change (start) struct change *start; { return start; } /* Divide SCRIPT into pieces by calling HUNKFUN and print each piece with PRINTFUN. Both functions take one arg, an edit script. HUNKFUN is called with the tail of the script and returns the last link that belongs together with the start of the tail. PRINTFUN takes a subscript which belongs together (with a null link at the end) and prints it. */ void print_script (script, hunkfun, printfun) struct change *script; struct change * (*hunkfun) PARAMS((struct change *)); void (*printfun) PARAMS((struct change *)); { struct change *next = script; while (next) { struct change *this, *end; /* Find a set of changes that belong together. */ this = next; end = (*hunkfun) (next); /* Disconnect them from the rest of the changes, making them a hunk, and remember the rest for next iteration. */ next = end->link; end->link = 0; #ifdef DEBUG debug_script (this); #endif /* Print this hunk. */ (*printfun) (this); /* Reconnect the script so it will all be freed properly. */ end->link = next; } } /* Print the text of a single line LINE, flagging it with the characters in LINE_FLAG (which say whether the line is inserted, deleted, changed, etc.). */ void print_1_line (line_flag, line) char const *line_flag; char const * const *line; { char const *text = line[0], *limit = line[1]; /* Help the compiler. */ FILE *out = outfile; /* Help the compiler some more. */ char const *flag_format = 0; /* If -T was specified, use a Tab between the line-flag and the text. Otherwise use a Space (as Unix diff does). Print neither space nor tab if line-flags are empty. */ if (line_flag && *line_flag) { flag_format = tab_align_flag ? "%s\t" : "%s "; fprintf (out, flag_format, line_flag); } output_1_line (text, limit, flag_format, line_flag); if ((!line_flag || line_flag[0]) && limit[-1] != '\n') fputc ('\n', out); } /* Output a line from TEXT up to LIMIT. Without -t, output verbatim. With -t, expand white space characters to spaces, and if FLAG_FORMAT is nonzero, output it with argument LINE_FLAG after every internal carriage return, so that tab stops continue to line up. */ void output_1_line (text, limit, flag_format, line_flag) char const *text, *limit, *flag_format, *line_flag; { if (!tab_expand_flag) fwrite (text, sizeof (char), limit - text, outfile); else { register FILE *out = outfile; register unsigned char c; register char const *t = text; register unsigned column = 0; while (t < limit) switch ((c = *t++)) { case '\t': { unsigned spaces = TAB_WIDTH - column % TAB_WIDTH; column += spaces; do putc (' ', out); while (--spaces); } break; case '\r': putc (c, out); if (flag_format && t < limit && *t != '\n') fprintf (out, flag_format, line_flag); column = 0; break; case '\b': if (column == 0) continue; column--; putc (c, out); break; default: if (ISPRINT (c)) column++; putc (c, out); break; } } } int change_letter (inserts, deletes) int inserts, deletes; { if (!inserts) return 'd'; else if (!deletes) return 'a'; else return 'c'; } /* Translate an internal line number (an index into diff's table of lines) into an actual line number in the input file. The internal line number is LNUM. FILE points to the data on the file. Internal line numbers count from 0 starting after the prefix. Actual line numbers count from 1 within the entire file. */ int translate_line_number (file, lnum) struct file_data const *file; int lnum; { return lnum + file->prefix_lines + 1; } void translate_range (file, a, b, aptr, bptr) struct file_data const *file; int a, b; int *aptr, *bptr; { *aptr = translate_line_number (file, a - 1) + 1; *bptr = translate_line_number (file, b + 1) - 1; } /* Print a pair of line numbers with SEPCHAR, translated for file FILE. If the two numbers are identical, print just one number. Args A and B are internal line numbers. We print the translated (real) line numbers. */ void print_number_range (sepchar, file, a, b) int sepchar; struct file_data *file; int a, b; { int trans_a, trans_b; translate_range (file, a, b, &trans_a, &trans_b); /* Note: we can have B < A in the case of a range of no lines. In this case, we should print the line number before the range, which is B. */ if (trans_b > trans_a) fprintf (outfile, "%d%c%d", trans_a, sepchar, trans_b); else fprintf (outfile, "%d", trans_b); } /* Look at a hunk of edit script and report the range of lines in each file that it applies to. HUNK is the start of the hunk, which is a chain of `struct change'. The first and last line numbers of file 0 are stored in *FIRST0 and *LAST0, and likewise for file 1 in *FIRST1 and *LAST1. Note that these are internal line numbers that count from 0. If no lines from file 0 are deleted, then FIRST0 is LAST0+1. Also set *DELETES nonzero if any lines of file 0 are deleted and set *INSERTS nonzero if any lines of file 1 are inserted. If only ignorable lines are inserted or deleted, both are set to 0. */ void analyze_hunk (hunk, first0, last0, first1, last1, deletes, inserts) struct change *hunk; int *first0, *last0, *first1, *last1; int *deletes, *inserts; { int l0, l1, show_from, show_to; int i; int trivial = ignore_blank_lines_flag || ignore_regexp_list; struct change *next; show_from = show_to = 0; *first0 = hunk->line0; *first1 = hunk->line1; next = hunk; do { l0 = next->line0 + next->deleted - 1; l1 = next->line1 + next->inserted - 1; show_from += next->deleted; show_to += next->inserted; for (i = next->line0; i <= l0 && trivial; i++) if (!ignore_blank_lines_flag || files[0].linbuf[i][0] != '\n') { struct regexp_list *r; char const *line = files[0].linbuf[i]; int len = files[0].linbuf[i + 1] - line; for (r = ignore_regexp_list; r; r = r->next) if (0 <= re_search (&r->buf, line, len, 0, len, 0)) break; /* Found a match. Ignore this line. */ /* If we got all the way through the regexp list without finding a match, then it's nontrivial. */ if (!r) trivial = 0; } for (i = next->line1; i <= l1 && trivial; i++) if (!ignore_blank_lines_flag || files[1].linbuf[i][0] != '\n') { struct regexp_list *r; char const *line = files[1].linbuf[i]; int len = files[1].linbuf[i + 1] - line; for (r = ignore_regexp_list; r; r = r->next) if (0 <= re_search (&r->buf, line, len, 0, len, 0)) break; /* Found a match. Ignore this line. */ /* If we got all the way through the regexp list without finding a match, then it's nontrivial. */ if (!r) trivial = 0; } } while ((next = next->link) != 0); *last0 = l0; *last1 = l1; /* If all inserted or deleted lines are ignorable, tell the caller to ignore this hunk. */ if (trivial) show_from = show_to = 0; *deletes = show_from; *inserts = show_to; } /* malloc a block of memory, with fatal error message if we can't do it. */ VOID * xmalloc (size) size_t size; { register VOID *value; if (size == 0) size = 1; value = (VOID *) malloc (size); if (!value) fatal ("memory exhausted"); return value; } /* realloc a block of memory, with fatal error message if we can't do it. */ VOID * xrealloc (old, size) VOID *old; size_t size; { register VOID *value; if (size == 0) size = 1; value = (VOID *) realloc (old, size); if (!value) fatal ("memory exhausted"); return value; } /* Concatenate three strings, returning a newly malloc'd string. */ char * concat (s1, s2, s3) char const *s1, *s2, *s3; { size_t len = strlen (s1) + strlen (s2) + strlen (s3); char *new = xmalloc (len + 1); sprintf (new, "%s%s%s", s1, s2, s3); return new; } /* Yield the newly malloc'd pathname of the file in DIR whose filename is FILE. */ char * dir_file_pathname (dir, file) char const *dir, *file; { char const *p = filename_lastdirchar (dir); return concat (dir, "/" + (p && !p[1]), file); } void debug_script (sp) struct change *sp; { fflush (stdout); for (; sp; sp = sp->link) fprintf (stderr, "%3d %3d delete %d insert %d\n", sp->line0, sp->line1, sp->deleted, sp->inserted); fflush (stderr); } /sys/src/ape/cmd/diff/version.c 664 sys sys 1367613436 94 /* Version number of GNU diff. */ #include "config.h" char const version_string[] = "2.7"; /sys/src/ape/cmd/diff/xmalloc.c 664 sys sys 1367613436 1828 /* xmalloc.c -- malloc with out of memory checking Copyright (C) 1990, 1991, 1993 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifdef HAVE_CONFIG_H #include #endif #if __STDC__ #define VOID void #else #define VOID char #endif #include #if STDC_HEADERS #include #else VOID *malloc (); VOID *realloc (); void free (); #endif #if __STDC__ && defined (HAVE_VPRINTF) void error (int, int, char const *, ...); #else void error (); #endif /* Allocate N bytes of memory dynamically, with error checking. */ VOID * xmalloc (n) size_t n; { VOID *p; p = malloc (n); if (p == 0) /* Must exit with 2 for `cmp'. */ error (2, 0, "memory exhausted"); return p; } /* Change the size of an allocated block of memory P to N bytes, with error checking. If P is NULL, run xmalloc. If N is 0, run free and return NULL. */ VOID * xrealloc (p, n) VOID *p; size_t n; { if (p == 0) return xmalloc (n); if (n == 0) { free (p); return 0; } p = realloc (p, n); if (p == 0) /* Must exit with 2 for `cmp'. */ error (2, 0, "memory exhausted"); return p; } /sys/src/ape/cmd/dirname.c 664 sys sys 1369258163 531 #include #include #include main(int argc, char **argv) { char *f, *s; if(argc != 2){ fprintf(stderr, "Usage: dirname string\n"); exit(1); } s = argv[1]; f = s + strlen(s) - 1; while(f > s && *f == '/') f--; *++f = 0; /* now f is after last char of string, trailing slashes removed */ for(; f >= s; f--) if(*f == '/'){ f++; break; } if(f < s) { *s = '.'; s[1] = 0; } else { --f; while(f > s && *f == '/') f--; f[1] = 0; } printf("%s\n", s); return 0; } /sys/src/ape/cmd/expr 20000000775 sys sys 1371506136 0 /sys/src/ape/cmd/expr/expr.y 664 sys sys 1367613436 5394 /* Yacc productions for "expr" command: */ %token OR AND ADD SUBT MULT DIV REM EQ GT GEQ LT LEQ NEQ %token A_STRING SUBSTR LENGTH INDEX NOARG MATCH /* operators listed below in increasing precedence: */ %left OR %left AND %left EQ LT GT GEQ LEQ NEQ %left ADD SUBT %left MULT DIV REM %left MCH %left MATCH %left SUBSTR %left LENGTH INDEX %{ #define YYSTYPE charp typedef char *charp; %} %% /* a single `expression' is evaluated and printed: */ expression: expr NOARG = { prt(1, $1); exit((!strcmp($1,"0")||!strcmp($1,"\0"))? 1: 0); } ; expr: '(' expr ')' = { $$ = $2; } | expr OR expr = { $$ = conj(OR, $1, $3); } | expr AND expr = { $$ = conj(AND, $1, $3); } | expr EQ expr = { $$ = rel(EQ, $1, $3); } | expr GT expr = { $$ = rel(GT, $1, $3); } | expr GEQ expr = { $$ = rel(GEQ, $1, $3); } | expr LT expr = { $$ = rel(LT, $1, $3); } | expr LEQ expr = { $$ = rel(LEQ, $1, $3); } | expr NEQ expr = { $$ = rel(NEQ, $1, $3); } | expr ADD expr = { $$ = arith(ADD, $1, $3); } | expr SUBT expr = { $$ = arith(SUBT, $1, $3); } | expr MULT expr = { $$ = arith(MULT, $1, $3); } | expr DIV expr = { $$ = arith(DIV, $1, $3); } | expr REM expr = { $$ = arith(REM, $1, $3); } | expr MCH expr = { $$ = match($1, $3); } | MATCH expr expr = { $$ = match($2, $3); } | SUBSTR expr expr expr = { $$ = substr($2, $3, $4); } | LENGTH expr = { $$ = length($2); } | INDEX expr expr = { $$ = index($2, $3); } | A_STRING ; %% /* expression command */ #include /* get rid of yacc debug printf's */ #define printf #define ESIZE 512 #define error(c) errxx(c) #define EQL(x,y) !strcmp(x,y) long atol(); char *ltoa(); char **Av; int Ac; int Argi; char Mstring[1][128]; char *malloc(); extern int nbra; int yyparse(void); main(argc, argv) char **argv; { Ac = argc; Argi = 1; Av = argv; yyparse(); } char *operator[] = { "|", "&", "+", "-", "*", "/", "%", ":", "=", "==", "<", "<=", ">", ">=", "!=", "match", "substr", "length", "index", "\0" }; int op[] = { OR, AND, ADD, SUBT, MULT, DIV, REM, MCH, EQ, EQ, LT, LEQ, GT, GEQ, NEQ, MATCH, SUBSTR, LENGTH, INDEX }; yylex() { register char *p; register i; if(Argi >= Ac) return NOARG; p = Av[Argi++]; if(*p == '(' || *p == ')') return (int)*p; for(i = 0; *operator[i]; ++i) if(EQL(operator[i], p)) return op[i]; yylval = p; return A_STRING; } char *rel(op, r1, r2) register char *r1, *r2; { register i; if(ematch(r1, "-\\{0,1\\}[0-9]*$") && ematch(r2, "-\\{0,1\\}[0-9]*$")) i = atol(r1) - atol(r2); else i = strcmp(r1, r2); switch(op) { case EQ: i = i==0; break; case GT: i = i>0; break; case GEQ: i = i>=0; break; case LT: i = i<0; break; case LEQ: i = i<=0; break; case NEQ: i = i!=0; break; } return i? "1": "0"; } char *arith(op, r1, r2) char *r1, *r2; { long i1, i2; register char *rv; if(!(ematch(r1, "-\\{0,1\\}[0-9]*$") && ematch(r2, "-\\{0,1\\}[0-9]*$"))) yyerror("non-numeric argument"); i1 = atol(r1); i2 = atol(r2); switch(op) { case ADD: i1 = i1 + i2; break; case SUBT: i1 = i1 - i2; break; case MULT: i1 = i1 * i2; break; case DIV: i1 = i1 / i2; break; case REM: i1 = i1 % i2; break; } rv = malloc(16); strcpy(rv, ltoa(i1)); return rv; } char *conj(op, r1, r2) char *r1, *r2; { register char *rv; switch(op) { case OR: if(EQL(r1, "0") || EQL(r1, "")) if(EQL(r2, "0") || EQL(r2, "")) rv = "0"; else rv = r2; else rv = r1; break; case AND: if(EQL(r1, "0") || EQL(r1, "")) rv = "0"; else if(EQL(r2, "0") || EQL(r2, "")) rv = "0"; else rv = r1; break; } return rv; } char *substr(v, s, w) char *v, *s, *w; { register si, wi; register char *res; si = atol(s); wi = atol(w); while(--si) if(*v) ++v; res = v; while(wi--) if(*v) ++v; *v = '\0'; return res; } char *length(s) register char *s; { register i = 0; register char *rv; while(*s++) ++i; rv = malloc(8); strcpy(rv, ltoa((long)i)); return rv; } char *index(s, t) char *s, *t; { register i, j; register char *rv; for(i = 0; s[i] ; ++i) for(j = 0; t[j] ; ++j) if(s[i]==t[j]) { strcpy(rv=malloc(8), ltoa((long)++i)); return rv; } return "0"; } char *match(s, p) { register char *rv; strcpy(rv=malloc(8), ltoa((long)ematch(s, p))); if(nbra) { rv = malloc(strlen(Mstring[0])+1); strcpy(rv, Mstring[0]); } return rv; } #define INIT register char *sp = instring; #define GETC() (*sp++) #define PEEKC() (*sp) #define UNGETC(c) (--sp) #define RETURN(c) return #define ERROR(c) errxx(c) ematch(s, p) char *s; register char *p; { static char expbuf[ESIZE]; char *compile(); register num; extern char *braslist[], *braelist[], *loc2; compile(p, expbuf, &expbuf[ESIZE], 0); if(nbra > 1) yyerror("Too many '\\('s"); if(advance(s, expbuf)) { if(nbra == 1) { p = braslist[0]; num = braelist[0] - p; strncpy(Mstring[0], p, num); Mstring[0][num] = '\0'; } return(loc2-s); } return(0); } errxx(c) { yyerror("RE error"); } #include "regexp.h" yyerror(s) { write(2, "expr: ", 6); prt(2, s); exit(2); } prt(fd, s) char *s; { write(fd, s, strlen(s)); write(fd, "\n", 1); } char *ltoa(l) long l; { static char str[20]; register char *sp = &str[18]; register i; register neg = 0; if(l < 0) ++neg, l *= -1; str[19] = '\0'; do { i = l % 10; *sp-- = '0' + i; l /= 10; } while(l); if(neg) *sp-- = '-'; return ++sp; } /sys/src/ape/cmd/expr/mkfile 664 sys sys 1367613436 144 APE=/sys/src/ape <$APE/config TARG=expr OFILES=y.tab.$O YFILES=expr.y HFILES=regexp.h BIN=$APEBIN > 3] |= bittab[c & 07] #define ISTHERE(c) (ep[c >> 3] & bittab[c & 07]) char *braslist[NBRA]; char *braelist[NBRA]; int nbra, ebra; char *loc1, *loc2, *locs; int sed; int circf; int low; int size; char bittab[] = { 1, 2, 4, 8, 16, 32, 64, 128 }; char * compile(instring, ep, endbuf, seof) register char *ep; char *instring, *endbuf; { INIT /* Dependent declarations and initializations */ register c; register eof = seof; char *lastep = instring; int cclcnt; char bracket[NBRA], *bracketp; int closed; char neg; int lc; int i, cflg; lastep = 0; if((c = GETC()) == eof) { if(*ep == 0 && !sed) ERROR(41); RETURN(ep); } bracketp = bracket; circf = closed = nbra = ebra = 0; if (c == '^') circf++; else UNGETC(c); for (;;) { if (ep >= endbuf) ERROR(50); if((c = GETC()) != '*' && ((c != '\\') || (PEEKC() != '{'))) lastep = ep; if (c == eof) { *ep++ = CEOF; RETURN(ep); } switch (c) { case '.': *ep++ = CDOT; continue; case '\n': ERROR(36); case '*': if (lastep==0 || *lastep==CBRA || *lastep==CKET) goto defchar; *lastep |= STAR; continue; case '$': if(PEEKC() != eof) goto defchar; *ep++ = CDOL; continue; case '[': if(&ep[17] >= endbuf) ERROR(50); *ep++ = CCL; lc = 0; for(i = 0; i < 16; i++) ep[i] = 0; neg = 0; if((c = GETC()) == '^') { neg = 1; c = GETC(); } do { if(c == '\0' || c == '\n') ERROR(49); if(c == '-' && lc != 0) { if ((c = GETC()) == ']') { PLACE('-'); break; } while(lc < c) { PLACE(lc); lc++; } } lc = c; PLACE(c); } while((c = GETC()) != ']'); if(neg) { for(cclcnt = 0; cclcnt < 16; cclcnt++) ep[cclcnt] ^= -1; ep[0] &= 0376; } ep += 16; continue; case '\\': switch(c = GETC()) { case '(': if(nbra >= NBRA) ERROR(43); *bracketp++ = nbra; *ep++ = CBRA; *ep++ = nbra++; continue; case ')': if(bracketp <= bracket || ++ebra != nbra) ERROR(42); *ep++ = CKET; *ep++ = *--bracketp; closed++; continue; case '{': if(lastep == (char *) (0)) goto defchar; *lastep |= RNGE; cflg = 0; nlim: c = GETC(); i = 0; do { if ('0' <= c && c <= '9') i = 10 * i + c - '0'; else ERROR(16); } while(((c = GETC()) != '\\') && (c != ',')); if (i > 255) ERROR(11); *ep++ = i; if (c == ',') { if(cflg++) ERROR(44); if((c = GETC()) == '\\') *ep++ = 255; else { UNGETC(c); goto nlim; /* get 2'nd number */ } } if(GETC() != '}') ERROR(45); if(!cflg) /* one number */ *ep++ = i; else if((ep[-1] & 0377) < (ep[-2] & 0377)) ERROR(46); continue; case '\n': ERROR(36); case 'n': c = '\n'; goto defchar; default: if(c >= '1' && c <= '9') { if((c -= '1') >= closed) ERROR(25); *ep++ = CBACK; *ep++ = c; continue; } } /* Drop through to default to use \ to turn off special chars */ defchar: default: lastep = ep; *ep++ = CCHR; *ep++ = c; } } } step(p1, p2) register char *p1, *p2; { register c; if (circf) { loc1 = p1; return(advance(p1, p2)); } /* fast check for first character */ if (*p2==CCHR) { c = p2[1]; do { if (*p1 != c) continue; if (advance(p1, p2)) { loc1 = p1; return(1); } } while (*p1++); return(0); } /* regular algorithm */ do { if (advance(p1, p2)) { loc1 = p1; return(1); } } while (*p1++); return(0); } advance(lp, ep) register char *lp, *ep; { register char *curlp; char c; char *bbeg; int ct; for (;;) switch (*ep++) { case CCHR: if (*ep++ == *lp++) continue; return(0); case CDOT: if (*lp++) continue; return(0); case CDOL: if (*lp==0) continue; return(0); case CEOF: loc2 = lp; return(1); case CCL: c = *lp++ & 0177; if(ISTHERE(c)) { ep += 16; continue; } return(0); case CBRA: braslist[*ep++] = lp; continue; case CKET: braelist[*ep++] = lp; continue; case CCHR|RNGE: c = *ep++; getrnge(ep); while(low--) if(*lp++ != c) return(0); curlp = lp; while(size--) if(*lp++ != c) break; if(size < 0) lp++; ep += 2; goto star; case CDOT|RNGE: getrnge(ep); while(low--) if(*lp++ == '\0') return(0); curlp = lp; while(size--) if(*lp++ == '\0') break; if(size < 0) lp++; ep += 2; goto star; case CCL|RNGE: getrnge(ep + 16); while(low--) { c = *lp++ & 0177; if(!ISTHERE(c)) return(0); } curlp = lp; while(size--) { c = *lp++ & 0177; if(!ISTHERE(c)) break; } if(size < 0) lp++; ep += 18; /* 16 + 2 */ goto star; case CBACK: bbeg = braslist[*ep]; ct = braelist[*ep++] - bbeg; if(ecmp(bbeg, lp, ct)) { lp += ct; continue; } return(0); case CBACK|STAR: bbeg = braslist[*ep]; ct = braelist[*ep++] - bbeg; curlp = lp; while(ecmp(bbeg, lp, ct)) lp += ct; while(lp >= curlp) { if(advance(lp, ep)) return(1); lp -= ct; } return(0); case CDOT|STAR: curlp = lp; while (*lp++); goto star; case CCHR|STAR: curlp = lp; while (*lp++ == *ep); ep++; goto star; case CCL|STAR: curlp = lp; do { c = *lp++ & 0177; } while(ISTHERE(c)); ep += 16; goto star; star: do { if(--lp == locs) break; if (advance(lp, ep)) return(1); } while (lp > curlp); return(0); } } getrnge(str) register char *str; { low = *str++ & 0377; size = *str == 255 ? 20000 : (*str &0377) - low; } ecmp(a, b, count) register char *a, *b; register count; { while(count--) if(*a++ != *b++) return(0); return(1); } /sys/src/ape/cmd/kill.c 664 sys sys 1371506091 1594 #include #include #include #include #include #include #include #define NSIG SIGUSR2 char *signm[NSIG+1] = { 0, "SIGHUP", "SIGINT", "SIGQUIT", "SIGILL", "SIGABRT", "SIGFPE", "SIGKILL", /* 1-7 */ "SIGSEGV", "SIGPIPE", "SIGALRM", "SIGTERM", "SIGUR1", "SIGUSR2", /* 8-13 */ }; main(int argc, char **argv) { int signo, res; int errlev; errlev = 0; if (argc <= 1) { usage: fprintf(stderr, "usage: kill [ -sig ] pid ...\n"); fprintf(stderr, "for a list of signals: kill -l\n"); exit(2); } if (*argv[1] == '-') { if (argv[1][1] == 'l') { int i = 0; for (signo = 1; signo <= NSIG; signo++) if (signm[signo]) { printf("%s ", signm[signo]); if (++i%8 == 0) printf("\n"); } if(i%8 !=0) printf("\n"); exit(0); } else if (isdigit(argv[1][1])) { signo = atoi(argv[1]+1); if (signo < 0 || signo > NSIG) { fprintf(stderr, "kill: %s: number out of range\n", argv[1]); exit(1); } } else { char *name = argv[1]+1; for (signo = 1; signo <= NSIG; signo++) if (signm[signo] && ( !strcmp(signm[signo], name)|| !strcmp(signm[signo]+3, name))) goto foundsig; fprintf(stderr, "kill: %s: unknown signal; kill -l lists signals\n", name); exit(1); foundsig: ; } argc--; argv++; } else signo = SIGTERM; argv++; while (argc > 1) { if ((**argv<'0' || **argv>'9') && **argv!='-') goto usage; res = kill(atoi(*argv), signo); if (res<0) { perror("kill"); } argc--; argv++; } return(errlev); } /sys/src/ape/cmd/make 20000000775 sys sys 1371506136 0 /sys/src/ape/cmd/make/defs.h 664 sys sys 1369259436 4225 /* defs 4.2 85/10/28 */ #define _POSIX_SOURCE #define _RESEARCH_SOURCE #include #include #include #include #include #include #include #include #include #include #ifndef SHELLCOM #define SHELLCOM "/bin/sh" #endif typedef char flag; /* represent a few bit flag */ #define NO 0 #define YES 1 #define equal(a,b) (! strcmp(a,b)) #define HASHSIZE 1021 #define NLEFTS 512 #define NCHARS 500 #define NINTS 250 #define INMAX 20000 #define OUTMAX 20000 #define QBUFMAX 20000 #define MAXDIR 10 #define MAXPROC 100 #define MAXINCLUDE 17 #define PROCLIMIT 3 #define ALLDEPS 1 #define SOMEDEPS 2 #define META 01 #define TERMINAL 02 extern char funny[128]; #define ALLOC(x) (struct x *) ckalloc(sizeof(struct x)) #define CHNULL (char *) NULL extern void (*sigivalue)(int); extern void (*sigqvalue)(int); extern int dbgflag; extern int prtrflag; extern int silflag; extern int noexflag; extern int keepgoing; extern int noruleflag; extern int touchflag; extern int questflag; extern int oldflag; extern int ndocoms; extern int ignerr; extern int okdel; extern int forceshell; extern int inarglist; extern char **envpp; /* points to slot in environment vector */ extern char *prompt; extern int nopdir; typedef struct nameblock *nameblkp; typedef struct depblock *depblkp; typedef struct lineblock *lineblkp; typedef struct chain *chainp; struct nameblock { nameblkp nxtnameblock; char *namep; lineblkp linep; flag done; flag septype; flag isarch; flag isdir; time_t modtime; }; extern nameblkp mainname; extern nameblkp firstname; extern nameblkp *hashtab; extern int nhashed; extern int hashsize; extern int hashthresh; struct lineblock { lineblkp nxtlineblock; struct depblock *depp; struct shblock *shp; }; extern lineblkp sufflist; struct depblock { depblkp nxtdepblock; nameblkp depname; char nowait; } ; struct shblock { struct shblock *nxtshblock; char *shbp; }; struct varblock { struct varblock *nxtvarblock; char *varname; char *varval; char **export; flag noreset; flag used; }; extern struct varblock *firstvar; struct pattern { struct pattern *nxtpattern; char *patval; }; extern struct pattern *firstpat; struct dirhd { struct dirhd *nxtdirhd; time_t dirtime; int dirok; DIR * dirfc; char *dirn; }; extern struct dirhd *firstod; struct chain { chainp nextp; char *datap; }; struct wild { struct wild *next; lineblkp linep; char *left; char *right; int llen; int rlen; int totlen; }; typedef struct wild *wildp; extern wildp firstwild; extern wildp lastwild; /* date for processes */ extern int proclimit; /* maximum spawned processes allowed alive at one time */ extern int proclive; /* number of spawned processes awaited */ extern int nproc; /* next slot in process stack to use */ extern struct process { int pid; flag nohalt; flag nowait; flag done; } procstack[ ]; extern void intrupt(int); extern void enbint(void (*)(int)); extern int doname(nameblkp, int, time_t *, int); extern int docom(struct shblock *, int, int); extern int dosys(char *, int, int, char *); extern int waitstack(int); extern void touch(int, char*); extern time_t exists(char *); extern time_t prestime(void); extern depblkp srchdir(char*, int, depblkp); extern time_t lookarch(char *); extern void dirsrch(char *); extern void baddirs(void); extern nameblkp srchname(char *); extern nameblkp makename(char *); extern int hasparen(char *); extern void newhash(int); extern nameblkp chkname(char *); extern char *copys(char *); extern char *concat(char *, char *, char *); extern int suffix(char *, char *, char *); extern int *ckalloc(int); extern char *subst(char *, char *); extern void setvar(char *, char *, int); extern void set3var(char *, char *); extern int eqsign(char *); extern struct varblock *varptr(char *); extern int dynmacro(char *); extern void fatal1(char *, char *); extern void fatal(char *); extern chainp appendq(chainp, char *); extern char *mkqlist(chainp, char *); extern wildp iswild(char *); extern char *wildmatch(wildp, char *, int); extern char *wildsub(char *, char *); extern int parse(char *); extern int yylex(void); /sys/src/ape/cmd/make/doname.c 664 sys sys 1369259657 7781 #include "defs.h" static int docom1(char *, int, int, int, int); static void expand(depblkp); /* BASIC PROCEDURE. RECURSIVE. */ /* p->done = 0 don't know what to do yet p->done = 1 file in process of being updated p->done = 2 file already exists in current state p->done = 3 file make failed */ int doname(nameblkp p, int reclevel, time_t *tval, int nowait) { int errstat; int okdel1; int didwork; int len; time_t td, td1, tdep, ptime, ptime1; depblkp q; depblkp qtemp, suffp, suffp1; nameblkp p1, p2; struct shblock *implcom, *explcom; lineblkp lp; lineblkp lp1, lp2; char sourcename[100], prefix[100], temp[100], concsuff[20]; char *stem; char *pnamep, *p1namep; chainp allchain, qchain; char qbuf[QBUFMAX], tgsbuf[QBUFMAX]; wildp wp; int nproc1; char *lastslash, *s; if(p == 0) { *tval = 0; return 0; } if(dbgflag) { printf("doname(%s,%d)\n",p->namep,reclevel); fflush(stdout); } if(p->done > 0) { *tval = p->modtime; return (p->done == 3); } errstat = 0; tdep = 0; implcom = 0; explcom = 0; ptime = exists(p->namep); ptime1 = 0; didwork = NO; p->done = 1; /* avoid infinite loops */ nproc1 = nproc; /* current depth of process stack */ qchain = NULL; allchain = NULL; /* define values of Bradford's $$@ and $$/ macros */ for(s = lastslash = p->namep; *s; ++s) if(*s == '/') lastslash = s; setvar("$@", p->namep, YES); setvar("$/", lastslash, YES); /* expand any names that have embedded metacharacters */ for(lp = p->linep ; lp ; lp = lp->nxtlineblock) for(q = lp->depp ; q ; q=qtemp ) { qtemp = q->nxtdepblock; expand(q); } /* make sure all dependents are up to date */ for(lp = p->linep ; lp ; lp = lp->nxtlineblock) { td = 0; for(q = lp->depp ; q ; q = q->nxtdepblock) if(q->depname) { errstat += doname(q->depname, reclevel+1, &td1, q->nowait); if(dbgflag) printf("TIME(%s)=%ld\n",q->depname->namep, td1); if(td1 > td) td = td1; if(ptime < td1) qchain = appendq(qchain, q->depname->namep); allchain = appendq(allchain, q->depname->namep); } if(p->septype == SOMEDEPS) { if(lp->shp) if( ptimedepp==0) { okdel1 = okdel; okdel = NO; set3var("@", p->namep); setvar("?", mkqlist(qchain,qbuf), YES); setvar("^", mkqlist(allchain,tgsbuf), YES); qchain = NULL; if( !questflag ) errstat += docom(lp->shp, nowait, nproc1); set3var("@", CHNULL); okdel = okdel1; ptime1 = prestime(); didwork = YES; } } else { if(lp->shp != 0) { if(explcom) fprintf(stderr, "Too many command lines for `%s'\n", p->namep); else explcom = lp->shp; } if(td > tdep) tdep = td; } } /* Look for implicit dependents, using suffix rules */ for(lp = sufflist ; lp ; lp = lp->nxtlineblock) for(suffp = lp->depp ; suffp ; suffp = suffp->nxtdepblock) { pnamep = suffp->depname->namep; if(suffix(p->namep , pnamep , prefix)) { srchdir(concat(prefix,"*",temp), NO, (depblkp) NULL); for(lp1 = sufflist ; lp1 ; lp1 = lp1->nxtlineblock) for(suffp1=lp1->depp; suffp1 ; suffp1 = suffp1->nxtdepblock) { p1namep = suffp1->depname->namep; if( (p1=srchname(concat(p1namep, pnamep ,concsuff))) && (p2=srchname(concat(prefix, p1namep ,sourcename))) ) { errstat += doname(p2, reclevel+1, &td, NO); if(ptime < td) qchain = appendq(qchain, p2->namep); if(dbgflag) printf("TIME(%s)=%ld\n", p2->namep, td); if(td > tdep) tdep = td; set3var("*", prefix); set3var("<", copys(sourcename)); for(lp2=p1->linep ; lp2 ; lp2 = lp2->nxtlineblock) if(implcom = lp2->shp) break; goto endloop; } } } } /* Look for implicit dependents, using pattern matching rules */ len = strlen(p->namep); for(wp = firstwild ; wp ; wp = wp->next) if(stem = wildmatch(wp, p->namep, len) ) { lp = wp->linep; for(q = lp->depp; q; q = q->nxtdepblock) { if(dbgflag>1 && q->depname) fprintf(stderr,"check dep of %s on %s\n", p->namep, wildsub(q->depname->namep,stem)); if(q->depname && ! chkname(wildsub(q->depname->namep,stem))) break; } if(q) /* some name not found, go to next line */ continue; for(q = lp->depp; q; q = q->nxtdepblock) { nameblkp tamep; if(q->depname == NULL) continue; tamep = srchname( wildsub(q->depname->namep,stem)); /*TEMP fprintf(stderr,"check dep %s on %s =>%s\n",p->namep,q->depname->namep,tamep->namep);*/ /*TEMP*/if(dbgflag) printf("%s depends on %s. stem=%s\n", p->namep,tamep->namep, stem); errstat += doname(tamep, reclevel+1, &td, q->nowait); if(ptime < td) qchain = appendq(qchain, tamep->namep); allchain = appendq(allchain, tamep->namep); if(dbgflag) printf("TIME(%s)=%ld\n", tamep->namep, td); if(td > tdep) tdep = td; set3var("<", copys(tamep->namep) ); } set3var("*", stem); setvar("%", stem, YES); implcom = lp->shp; goto endloop; } endloop: if(errstat==0 && (ptime0 ? tdep : prestime() ); USED(ptime); set3var("@", p->namep); setvar("?", mkqlist(qchain,qbuf), YES); setvar("^", mkqlist(allchain,tgsbuf), YES); if(explcom) errstat += docom(explcom, nowait, nproc1); else if(implcom) errstat += docom(implcom, nowait, nproc1); else if(p->septype == 0) if(p1=srchname(".DEFAULT")) { set3var("<", p->namep); for(lp2 = p1->linep ; lp2 ; lp2 = lp2->nxtlineblock) if(implcom = lp2->shp) { errstat += docom(implcom, nowait,nproc1); break; } } else if(keepgoing) { printf("Don't know how to make %s\n", p->namep); ++errstat; } else fatal1(" Don't know how to make %s", p->namep); set3var("@", CHNULL); if(noexflag || nowait || (ptime = exists(p->namep)) == 0 ) ptime = prestime(); } else if(errstat!=0 && reclevel==0) printf("`%s' not remade because of errors\n", p->namep); else if(!questflag && reclevel==0 && didwork==NO) printf("`%s' is up to date.\n", p->namep); if(questflag && reclevel==0) exit(ndocoms>0 ? -1 : 0); p->done = (errstat ? 3 : 2); if(ptime1 > ptime) ptime = ptime1; p->modtime = ptime; *tval = ptime; return errstat; } docom(struct shblock *q, int nowait, int nproc1) { char *s; int ign, nopr, doit; char string[OUTMAX]; ++ndocoms; if(questflag) return NO; if(touchflag) { s = varptr("@")->varval; if(!silflag) printf("touch(%s)\n", s); if(!noexflag) touch(YES, s); return NO; } if(nproc1 < nproc) waitstack(nproc1); for( ; q ; q = q->nxtshblock ) { subst(q->shbp,string); ign = ignerr; nopr = NO; doit = NO; for(s = string ; ; ++s) { switch(*s) { case '-': ign = YES; continue; case '@': nopr = YES; continue; case '+': doit = YES; continue; default: break; } break; } if( docom1(s, ign, nopr, doit||!noexflag, nowait&&!q->nxtshblock) && !ign) return YES; } return NO; } static int docom1(char *comstring, int nohalt, int noprint, int doit, int nowait) { int status; char *prefix; if(comstring[0] == '\0') return 0; if(!silflag && (!noprint || !doit) ) prefix = doit ? prompt : "" ; else prefix = CHNULL; if(dynmacro(comstring) || !doit) { if(prefix) { fputs(prefix, stdout); puts(comstring); /* with a newline */ fflush(stdout); } return 0; } status = dosys(comstring, nohalt, nowait, prefix); baddirs(); /* directories may have changed */ return status; } /* If there are any Shell meta characters in the name, expand into a list, after searching directory */ static void expand(depblkp q) { char *s; char *s1; depblkp p; s1 = q->depname->namep; for(s=s1 ; ;) switch(*s++) { case '\0': return; case '*': case '?': case '[': if( p = srchdir(s1 , YES, q->nxtdepblock) ) { q->nxtdepblock = p; q->depname = 0; } return; } } /sys/src/ape/cmd/make/dosys.c 664 sys sys 1367613436 4785 #include "defs.h" #include #include #include #include static int metas(char *); static int waitproc(int *); static int doshell(char *, int); static int doexec(char *); int dosys(char *comstring, int nohalt, int nowait, char *prefix) { int status; struct process *procp; /* make sure there is room in the process stack */ if(nproc >= MAXPROC) waitstack(MAXPROC-1); /* make sure fewer than proclimit processes are running */ while(proclive >= proclimit) { enbint(SIG_IGN); waitproc(&status); enbint(intrupt); } if(prefix) { fputs(prefix, stdout); fputs(comstring, stdout); } procp = procstack + nproc; procp->pid = (forceshell || metas(comstring) ) ? doshell(comstring,nohalt) : doexec(comstring); if(procp->pid == -1) fatal("fork failed"); procstack[nproc].nohalt = nohalt; procstack[nproc].nowait = nowait; procstack[nproc].done = NO; ++proclive; ++nproc; if(nowait) { printf(" &%d\n", procp->pid); fflush(stdout); return 0; } if(prefix) { putchar('\n'); fflush(stdout); } return waitstack(nproc-1); } static int metas(char *s) /* Are there are any Shell meta-characters? */ { char c; while( (funny[c = *s++] & META) == 0 ) ; return( c ); } static void doclose(void) /* Close open directory files before exec'ing */ { struct dirhd *od; for (od = firstod; od; od = od->nxtdirhd) if(od->dirfc) closedir(od->dirfc); } /* wait till none of the processes in the stack starting at k is live */ int waitstack(int k) { int npending, status, totstatus; int i; totstatus = 0; npending = 0; for(i=k ; i 1) printf("waitstack(%d)\n", k); while(npending>0 && proclive>0) { if(waitproc(&status) >= k) --npending; totstatus |= status; } if(nproc > k) nproc = k; enbint(intrupt); return totstatus; } static int waitproc(int *statp) { pid_t pid; int status; int i; struct process *procp; char junk[50]; static int inwait = NO; if(inwait) /* avoid infinite recursions on errors */ return MAXPROC; inwait = YES; pid = wait(&status); if(dbgflag > 1) fprintf(stderr, "process %d done, status = %d\n", pid, status); if(pid == -1) { if(errno == ECHILD) /* multiple deaths, no problem */ { if(proclive) { for(i=0, procp=procstack; idone = YES; proclive = nproc = 0; } return MAXPROC; } fatal("bad wait code"); } for(i=0, procp=procstack; ipid == pid) { --proclive; procp->done = YES; if(status) { if(procp->nowait) printf("%d: ", pid); if( WEXITSTATUS(status) ) printf("*** Error code %d", WEXITSTATUS(status) ); else printf("*** Termination code %d", WTERMSIG(status)); printf(procp->nohalt ? "(ignored)\n" : "\n"); fflush(stdout); if(!keepgoing && !procp->nohalt) fatal(CHNULL); } *statp = status; inwait = NO; return i; } sprintf(junk, "spurious return from process %d", pid); fatal(junk); /*NOTREACHED*/ return -1; } static int doshell(char *comstring, int nohalt) { pid_t pid; if((pid = fork()) == 0) { enbint(SIG_DFL); doclose(); execl(SHELLCOM, "sh", (nohalt ? "-c" : "-ce"), comstring, NULL); fatal("Couldn't load Shell"); } return pid; } static int doexec(char *str) { char *t, *tend; char **argv; char **p; int nargs; pid_t pid; while( *str==' ' || *str=='\t' ) ++str; if( *str == '\0' ) return(-1); /* no command */ nargs = 1; for(t = str ; *t ; ) { ++nargs; while(*t!=' ' && *t!='\t' && *t!='\0') ++t; if(*t) /* replace first white space with \0, skip rest */ for( *t++ = '\0' ; *t==' ' || *t=='\t' ; ++t) ; } /* now allocate args array, copy pointer to start of each string, then terminate array with a null */ p = argv = (char **) ckalloc(nargs*sizeof(char *)); tend = t; for(t = str ; t #include #define NAMESPERBLOCK 32 /* DEFAULT RULES FOR POSIX */ char *dfltmacro[] = { ".SUFFIXES : .o .c .y .l .a .sh .f", "MAKE=make", "AR=ar", "ARFLAGS=rv", "YACC=yacc", "YFLAGS=", "LEX=lex", "LFLAGS=", "LDFLAGS=", "CC=c89", "CFLAGS=-O", "FC=fort77", "FFLAGS=-O 1", 0 }; char *dfltpat[] = { "%.o : %.c", "\t$(CC) $(CFLAGS) -c $<", "%.o : %.y", "\t$(YACC) $(YFLAGS) $<", "\t$(CC) $(CFLAGS) -c y.tab.c", "\trm y.tab.c", "\tmv y.tab.o $@", "%.o : %.l", "\t$(LEX) $(LFLAGS) $<", "\t$(CC) $(CFLAGS) -c lex.yy.c", "\trm lex.yy.c", "\tmv lex.yy.o $@", "%.c : %.y", "\t$(YACC) $(YFLAGS) $<", "\tmv y.tab.c $@", "%.c : %.l", "\t$(LEX) $(LFLAGS) $<", "\tmv lex.yy.c $@", "% : %.o", "\t$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<", "% : %.c", "\t$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<", 0 }; char *dfltsuff[] = { ".SUFFIXES : .o .c .y .l .a .sh .f", ".c.o :", "\t$(CC) $(CFLAGS) -c $<", ".f.o :", "\t$(FC) $(FFLAGS) -c $<", ".y.o :", "\t$(YACC) $(YFLAGS) $<", "\t$(CC) $(CFLAGS) -c y.tab.c", "\trm -f y.tab.c", "\tmv y.tab.o $@", ".l.o :", "\t$(LEX) $(LFLAGS) $<", "\t$(CC) $(CFLAGS) -c lex.yy.c", "\trm -f lex.yy.c", "\tmv lex.yy.o $@", ".y.c :", "\t$(YACC) $(YFLAGS) $<", "\tmv y.tab.c $@", ".l.c :", "\t$(LEX) $(LFLAGS) $<", "\tmv lex.yy.c $@", ".c.a:", "\t$(CC) -c $(CFLAGS) $<", "\t$(AR) $(ARFLAGS) $@ $*.o", "\trm -f $*.o", ".f.a:", "\t$(FC) -c $(FFLAGS) $<", "\t$(AR) $(ARFLAGS) $@ $*.o", "\trm -f $*.o", ".c:", "\t$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<", ".f:", "\t$(FC) $(FFLAGS) $(LDFLAGS) -o $@ $<", ".sh:", "\tcp $< $@", "\tchmod a+x $@", 0 }; static struct dirhd *opdir(char *, int); static void cldir(struct dirhd *, int); static int amatch(char *, char *); static int umatch(char *, char *); static void clarch(void); static int openarch(char *); static int getarch(void); time_t exists(char *filename) { struct stat buf; char *s; for(s = filename ; *s!='\0' && *s!='(' && *s!=')' ; ++s) ; if(*s != '\0') return lookarch(filename); if(stat(filename,&buf) < 0) return 0; else return buf.st_mtime; } time_t prestime(void) { time_t t; time(&t); return t; } static char nmtemp[MAXNAMLEN+1]; /* guarantees a null after the name */ static char *tempend = nmtemp + MAXNAMLEN; depblkp srchdir(char *pat, int mkchain, depblkp nextdbl) { DIR *dirf; struct dirhd *dirptr; char *dirname, *dirpref, *endir, *filepat, *p, temp[100]; char fullname[100]; nameblkp q; depblkp thisdbl; struct pattern *patp; struct dirent *dptr; thisdbl = 0; if(mkchain == NO) for(patp=firstpat ; patp ; patp = patp->nxtpattern) if(equal(pat, patp->patval)) return 0; patp = ALLOC(pattern); patp->nxtpattern = firstpat; firstpat = patp; patp->patval = copys(pat); endir = 0; for(p=pat; *p!='\0'; ++p) if(*p=='/') endir = p; if(endir==0) { dirname = "."; dirpref = ""; filepat = pat; } else { dirname = pat; *endir = '\0'; dirpref = concat(dirname, "/", temp); filepat = endir+1; } dirptr = opdir(dirname,YES); dirf = dirptr->dirfc; for( dptr = readdir(dirf) ; dptr ; dptr = readdir(dirf) ) { char *p1, *p2; p1 = dptr->d_name; p2 = nmtemp; while( (p2nxtdepblock = nextdbl; thisdbl->depname = q; nextdbl = thisdbl; } } } if(endir) *endir = '/'; cldir(dirptr, YES); return thisdbl; } static struct dirhd * opdir(char *dirname, int stopifbad) { struct dirhd *od; for(od = firstod; od; od = od->nxtdirhd) if(equal(dirname, od->dirn) ) break; if(od == NULL) { ++nopdir; od = ALLOC(dirhd); od->nxtdirhd = firstod; firstod = od; od->dirn = copys(dirname); } if(od->dirfc==NULL && (od->dirfc = opendir(dirname)) == NULL && stopifbad) { fprintf(stderr, "Directory %s: ", dirname); fatal("Cannot open"); } return od; } static void cldir(struct dirhd *dp, int used) { if(nopdir >= MAXDIR) { closedir(dp->dirfc); dp->dirfc = NULL; } else if(used) rewinddir(dp->dirfc); /* start over at the beginning */ } /* stolen from glob through find */ static int amatch(char *s, char *p) { int cc, scc, k; int c, lc; scc = *s; lc = 077777; switch (c = *p) { case '[': k = 0; while (cc = *++p) { switch (cc) { case ']': if (k) return amatch(++s, ++p); else return 0; case '-': k |= (lc <= scc) & (scc <= (cc=p[1]) ) ; } if (scc==(lc=cc)) k++; } return 0; case '?': caseq: if(scc) return amatch(++s, ++p); return 0; case '*': return umatch(s, ++p); case 0: return !scc; } if (c==scc) goto caseq; return 0; } static int umatch(char *s, char *p) { if(*p==0) return 1; while(*s) if (amatch(s++,p)) return 1; return 0; } #ifdef METERFILE #include int meteron = 0; /* default: metering off */ extern void meter(char *file) { time_t tvec; char *p; FILE * mout; struct passwd *pwd; if(file==0 || meteron==0) return; pwd = getpwuid(getuid()); time(&tvec); if( mout = fopen(file,"a") ) { p = ctime(&tvec); p[16] = '\0'; fprintf(mout, "User %s, %s\n", pwd->pw_name, p+4); fclose(mout); } } #endif /* look inside archives for notation a(b) a(b) is file member b in archive a */ static long arflen; static long arfdate; static char arfname[16]; FILE *arfd; long int arpos, arlen; time_t lookarch(char *filename) { char *p, *q, *send, s[15], pad; int nc; for(p = filename; *p!= '(' ; ++p) ; *p = '\0'; if( ! openarch(filename) ) { *p = '('; return 0L; } *p++ = '('; nc = 14; pad = ' '; send = s + nc; for( q = s ; qisarch = YES; arflen = 0; return YES; } static int getarch(void) { struct ar_hdr arhead; arpos += (arflen + 1) & ~1L; /* round archived file length up to even */ if(arpos >= arlen) return 0; fseek(arfd, arpos, 0); fread( (char *) &arhead, sizeof(arhead), 1, arfd); arpos += sizeof(arhead); arflen = atol(arhead.ar_size); arfdate = atol(arhead.ar_date); strncpy(arfname, arhead.ar_name, sizeof(arhead.ar_name)); return 1; } /* find the directory containing name. read it into the hash table if it hasn't been used before or if if might have changed since last reference */ void dirsrch(char *name) { DIR *dirf; struct dirhd *dirp; time_t dirt, objt; int dirused, hasparen; char *dirname, *lastslash; char *fullname, *filepart, *fileend, *s; struct dirent *dptr; lastslash = NULL; hasparen = NO; for(s=name; *s; ++s) if(*s == '/') lastslash = s; else if(*s=='(' || *s==')') hasparen = YES; if(hasparen) { if(objt = lookarch(name)) makename(name)->modtime = objt; return; } if(lastslash) { dirname = name; *lastslash = '\0'; } else dirname = "."; dirused = NO; dirp = opdir(dirname, NO); dirf = dirp->dirfc; if(dirp->dirok || !dirf) goto ret; dirt = exists(dirname); if(dirp->dirtime == dirt) goto ret; dirp->dirok = YES; dirp->dirtime = dirt; dirused = YES; /* allocate buffer to hold full file name */ if(lastslash) { fullname = (char *) ckalloc(strlen(dirname)+MAXNAMLEN+2); concat(dirname, "/", fullname); filepart = fullname + strlen(fullname); } else filepart = fullname = (char *) ckalloc(MAXNAMLEN+1); fileend = filepart + MAXNAMLEN; *fileend = '\0'; for(dptr = readdir(dirf) ; dptr ; dptr = readdir(dirf) ) { char *p1, *p2; p1 = dptr->d_name; p2 = filepart; while( (p2nxtdirhd) od->dirok = NO; } /sys/src/ape/cmd/make/gram.y 664 sys sys 1367613436 7379 %{#include "defs.h" %} %term NAME SHELLINE START MACRODEF COLON DOUBLECOLON GREATER AMPER AMPERAMPER %union { struct shblock *yshblock; depblkp ydepblock; nameblkp ynameblock; } %type SHELLINE, shlist, shellist %type NAME, namelist %type deplist, dlist %% %{ struct depblock *pp; static struct shblock *prevshp; static struct nameblock *lefts[NLEFTS]; struct nameblock *leftp; static int nlefts; struct lineblock *lp, *lpp; static struct depblock *prevdep; static int sepc; static int allnowait; static struct fstack { FILE *fin; char *fname; int lineno; } filestack[MAXINCLUDE]; static int ninclude = 0; %} file: | file comline ; comline: START | MACRODEF | START namelist deplist shellist = { while( --nlefts >= 0) { wildp wp; leftp = lefts[nlefts]; if(wp = iswild(leftp->namep)) { leftp->septype = SOMEDEPS; if(lastwild) lastwild->next = wp; else firstwild = wp; lastwild = wp; } if(leftp->septype == 0) leftp->septype = sepc; else if(leftp->septype != sepc) { if(! wp) fprintf(stderr, "Inconsistent rules lines for `%s'\n", leftp->namep); } else if(sepc==ALLDEPS && leftp->namep[0]!='.' && $4!=0) { for(lp=leftp->linep; lp->nxtlineblock; lp=lp->nxtlineblock) if(lp->shp) fprintf(stderr, "Multiple rules lines for `%s'\n", leftp->namep); } lp = ALLOC(lineblock); lp->nxtlineblock = NULL; lp->depp = $3; lp->shp = $4; if(wp) wp->linep = lp; if(equal(leftp->namep, ".SUFFIXES") && $3==0) leftp->linep = 0; else if(leftp->linep == 0) leftp->linep = lp; else { for(lpp = leftp->linep; lpp->nxtlineblock; lpp = lpp->nxtlineblock) ; if(sepc==ALLDEPS && leftp->namep[0]=='.') lpp->shp = 0; lpp->nxtlineblock = lp; } } } | error ; namelist: NAME = { lefts[0] = $1; nlefts = 1; } | namelist NAME = { lefts[nlefts++] = $2; if(nlefts>=NLEFTS) fatal("Too many lefts"); } ; deplist: { char junk[100]; sprintf(junk, "%s:%d", filestack[ninclude-1].fname, yylineno); fatal1("Must be a separator on rules line %s", junk); } | dlist ; dlist: sepchar = { prevdep = 0; $$ = 0; allnowait = NO; } | sepchar AMPER = { prevdep = 0; $$ = 0; allnowait = YES; } | dlist NAME = { pp = ALLOC(depblock); pp->nxtdepblock = NULL; pp->depname = $2; pp->nowait = allnowait; if(prevdep == 0) $$ = pp; else prevdep->nxtdepblock = pp; prevdep = pp; } | dlist AMPER = { if(prevdep) prevdep->nowait = YES; } | dlist AMPERAMPER ; sepchar: COLON = { sepc = ALLDEPS; } | DOUBLECOLON = { sepc = SOMEDEPS; } ; shellist: = {$$ = 0; } | shlist = { $$ = $1; } ; shlist: SHELLINE = { $$ = $1; prevshp = $1; } | shlist SHELLINE = { $$ = $1; prevshp->nxtshblock = $2; prevshp = $2; } ; %% static char *zznextc; /* null if need another line; otherwise points to next char */ static int yylineno; static FILE * fin; static int retsh(char *); static int nextlin(void); static int isinclude(char *); int yyparse(void); int parse(char *name) { FILE *stream; if(name == CHNULL) { stream = NULL; name = "(builtin-rules)"; } else if(equal(name, "-")) { stream = stdin; name = "(stdin)"; } else if( (stream = fopen(name, "r")) == NULL) return NO; filestack[0].fname = copys(name); ninclude = 1; fin = stream; yylineno = 0; zznextc = 0; if( yyparse() ) fatal("Description file error"); if(fin) fclose(fin); return YES; } int yylex(void) { char *p; char *q; char word[INMAX]; if(! zznextc ) return nextlin() ; while( isspace(*zznextc) ) ++zznextc; switch(*zznextc) { case '\0': return nextlin() ; case '|': if(zznextc[1]==':') { zznextc += 2; return DOUBLECOLON; } break; case ':': if(*++zznextc == ':') { ++zznextc; return DOUBLECOLON; } return COLON; case '>': ++zznextc; return GREATER; case '&': if(*++zznextc == '&') { ++zznextc; return AMPERAMPER; } return AMPER; case ';': return retsh(zznextc) ; } p = zznextc; q = word; while( ! ( funny[*p] & TERMINAL) ) *q++ = *p++; if(p != zznextc) { *q = '\0'; if((yylval.ynameblock=srchname(word))==0) yylval.ynameblock = makename(word); zznextc = p; return NAME; } else { char junk[100]; sprintf(junk, "Bad character %c (octal %o), line %d of file %s", *zznextc, *zznextc, yylineno, filestack[ninclude-1].fname); fatal(junk); } return 0; /* never executed */ } static int retsh(char *q) { register char *p; struct shblock *sp; for(p=q+1 ; *p==' '||*p=='\t' ; ++p) ; sp = ALLOC(shblock); sp->nxtshblock = NULL; sp->shbp = (fin ? copys(p) : p ); yylval.yshblock = sp; zznextc = 0; return SHELLINE; } static int nextlin(void) { static char yytext[INMAX]; static char *yytextl = yytext+INMAX; char *text, templin[INMAX]; char c; char *p, *t; char lastch, *lastchp; extern char **linesptr; int incom; int kc; again: incom = NO; zznextc = 0; if(fin == NULL) { if( (text = *linesptr++) == 0) return 0; ++yylineno; } else { for(p = text = yytext ; p 1) { register struct fstack *stp; fclose(fin); --ninclude; stp = filestack + ninclude; fin = stp->fin; yylineno = stp->lineno; free(stp->fname); goto again; } return 0; } fatal("line too long"); } endloop: if((c = text[0]) == '\t') return retsh(text) ; if(isalpha(c) || isdigit(c) || c==' ' || c=='.'|| c=='_') for(p=text+1; *p!='\0'; ) if(*p == ':') break; else if(*p++ == '=') { eqsign(text); return MACRODEF; } /* substitute for macros on dependency line up to the semicolon if any */ for(t = yytext ; *t!='\0' && *t!=';' ; ++t) ; lastchp = t; lastch = *t; *t = '\0'; /* replace the semi with a null so subst will stop */ subst(yytext, templin); /* Substitute for macros on dependency lines */ if(lastch) /* copy the stuff after the semicolon */ { *lastchp = lastch; strcat(templin, lastchp); } strcpy(yytext, templin); /* process include files after macro substitution */ if(strncmp(text, "include", 7) == 0) { if (isinclude(text+7)) goto again; } for(p = zznextc = text ; *p ; ++p ) if(*p!=' ' && *p!='\t') return START; goto again; } static int isinclude(char *s) { char *t; struct fstack *p; for(t=s; *t==' ' || *t=='\t' ; ++t) ; if(t == s) return NO; for(s = t; *s!='\n' && *s!='#' && *s!='\0' ; ++s) if(*s == ':') return NO; *s = '\0'; if(ninclude >= MAXINCLUDE) fatal("include depth exceeded"); p = filestack + ninclude; p->fin = fin; p->lineno = yylineno; p->fname = copys(t); if( (fin = fopen(t, "r")) == NULL) fatal1("Cannot open include file %s", t); yylineno = 0; ++ninclude; return YES; } int yyerror(char *s, ...) { char buf[100]; sprintf(buf, "line %d of file %s: %s", yylineno, filestack[ninclude-1].fname, s); fatal(buf); return 0; } /sys/src/ape/cmd/make/ident.c 664 sys sys 1367613436 4910 char *xxxvers = "\n@(#) MAKE. VERSION 2.78 22 MAY 1986\n" ; static char *sccsid = "@(#)ident.c 8th Edition (Bell Labs) 85/10/28"; /* 2.1 4/24/76 Base version 2.2 4/26/76 Error found by SRB in overriding pattern rules; corrected gram.y 2.3 4/27/76 Further correction for overriding pattern rules; corrected doname.c 2.4 Removed .CLEAR name, added .IGNORE. A .SUFFIXES rule without dependents clears the list 2.5 Stripped output 2.6 Changed doshell to accomodate new shell. 2.7 Following SRB's sugestion, added ${...} as alternate macro name 2.8 Defined macros AS and DTGEN in files.c. 2.9 Put in a fix to prevent removal of files upon interrupt in a :: rule. 2.10 Fixed bugs involving messages for :: and closing standard input 2.11 Changed time test from <= to < (equal times are considered in sync) 2.12 Installed -t flag (touch and update time of files rather than issue commands) Fixed bug in dosys 2.13 Fixed lex.c to allow sharps (#) in commands 2.14 Added .DEFAULT rule 2.15 Changed to I/O System (stdio.h) 2.16 Removed references to double floats and macro HAVELONGS; committed to use of long ints for times. 2.17 Corrected metacharacter list in dosys.c. 2.18 Miscellaneous fixes 2.19 Updated files.c to use include file stat.h 2.20 Added -q flag for Mike Lesk 2.21 Added AWK rules and .w suffix to files.c 2.22 Added colon to the list of metacharacters 2.23 Macro substitutions on dependency lines. Redid argument and macro setting. Close files before exec'ing. Print > at beginning of command lines. No printing of commands beginnng with @. 2.24 Parametrized propt sequence in doname.c (4/1/77) 2.25 Added $? facility 2.26 Fixed bug in macro expansion 2.27 Repaired interrupt handling 2.28 Repaired bug in -n 2.29 Repaired bug in file closing and $? string creation 2.30 Repaired bug in grammar about command lines 2.31 Added -k flag, modified doname.c and defs 2.32 Made "keepgoing" the default, added -S flag, changed handling of funny characters internally 2.3 Small fixups to interrupt and quit handling. Changed default back to -k. 2.34 Added .PRECIOUS rule for interrupts 2.35 Added references to include files (due to TLL) 2.36 Fixed bug in lex.c so = permitted in rules on :; line 2.37 Miscellaneous code cleanups 2.38 Sleep one second after each touch in -t mode 2.39 Extended string[] declaration in doname.c 2.40 Permit recursive macro references 2.41 Separated YYLMAX into INMAX and OUTMAX macros, specifying longest input and output lines respectively. 2.42 Fixed bug involving :: lines without dependents 2.43 Main name is first name that contains a slash or doesn't begin with a dot 2.44 Fixed bug involving $$ on command line 2.45 Changed files.c to put .f before .e, .r and to use f77 instead of fc. 2.46 Changed dosys.c to eliminate copying and to call execvp. 2.47 Changed files.c to add ".out" suffix and rules. 2.48 Changed misc.c to permit tabs preceding = in macro definition 2.49 Added reference to . Removed -lS references from files.c 2.50 General cleanup to reduce lint messages. (changes in declarations and in uses of variables) 2.51 Further cleanup making use of new Yacc features. 2.52 2.53 Changed handling of "touch" 2.54 Fixed bug involving comments in lexical analyzer. 2.55 Ignore commands that begin with a # are comments. 2.56 Added = to list of META characters (to permit shell commands) 2.57 Changed lookarch and getobj to fix bugs. 2.58 Fixed interrupt handling. 2.59 Changed references to sprintf to accomodate new function definition Also fixed extern declarations. 2.60 Limited the number of open directories. 2.61 Added code to handle archives with ascii headers. 2.62 Joe Condon Fixes to archive formats 2.63 Pattern Matching (%) stuff. 2.64 Reinstalled $(TGS) as $^ from other version 2.65 Installed dynamic macros ( := commands). 2.66 Sped up pattern matching code 2.67 Changed pattern matching code to permit multiple dependents 2.68 Added + (do it despite -n) prefix to command lines. Fixed bug involving metacharacter expansions on dependency lines. 2.69 Added & to dependency lines and new background process spawning 2.70 Added Bradford's macros: $/, $@, *D, *F, = argc-1) fatal("No description argument after -f flag"); if( ! rddescf(argv[i+1]) ) fatal1("Cannot open %s", argv[i+1]); argv[i+1] = 0; ++descset; break; case 'i': ignerr = YES; *mkflagp++ = 'i'; break; case 'k': keepgoing = YES; *mkflagp++ = 'k'; break; case 'n': noexflag = YES; *mkflagp++ = 'n'; break; case 'N': oldflag = NO; *mkflagp++ = 'N'; break; case 'p': prtrflag = YES; break; case 'P': if(isdigit(argv[i][j+1])) { proclimit = argv[i][++j] - '0'; if(proclimit < 1) proclimit = 1; } else fatal("illegal proclimit parameter"); *mkflagp++ = 'P'; *mkflagp++ = argv[i][j]; break; case 'q': questflag = YES; *mkflagp++ = 'q'; break; case 'r': noruleflag = YES; *mkflagp++ = 'r'; break; case 's': silflag = YES; *mkflagp++ = 's'; break; case 'S': keepgoing = NO; *mkflagp++ = 'S'; break; case 't': touchflag = YES; *mkflagp++ = 't'; break; case 'z': forceshell = YES; *mkflagp++ = 'z'; break; default: onechar[0] = c; /* to make lint happy */ fatal1("Unknown flag argument %s", onechar); } argv[i] = NULL; } if(mkflagp > makeflags+1) setvar("MAKEFLAGS", makeflags, NO); if( !descset ) if( !rddescf("makefile") && !rddescf("Makefile") && (exists(s = "s.makefile") || exists(s = "s.Makefile")) ) { char junk[20]; concat("get ", s, junk); dosys(junk, NO, NO, junk); rddescf(s+2); unlink(s+2); } if(envlast) loadenv(); if(!noruleflag && !oldflag) rdarray(dfltpat); if(prtrflag) printdesc(NO); if( srchname(".IGNORE") ) ignerr = YES; if( srchname(".SILENT") ) silflag = YES; if( srchname(".OLDFLAG") ) oldflag = YES; if( p=srchname(".SUFFIXES") ) sufflist = p->linep; if( !sufflist && !firstwild) fprintf(stderr,"No suffix or %% pattern list.\n"); /* if(sufflist && !oldflag) fprintf(stderr, "Suffix lists are old-fashioned. Use %% patterns\n); */ sigivalue = signal(SIGINT, SIG_IGN); sigqvalue = signal(SIGQUIT, SIG_IGN); enbint(intrupt); nfargs = 0; for(i=1; ivarval) && exists(p)>0 && !isprecious(p) ) { fprintf(stderr, "\n*** %s removed.", p); remove(p); } fprintf(stderr, "\n"); exit(2); } static int isprecious(char *p) { lineblkp lp; depblkp dp; nameblkp np; if(np = srchname(".PRECIOUS")) for(lp = np->linep ; lp ; lp = lp->nxtlineblock) for(dp = lp->depp ; dp ; dp = dp->nxtdepblock) if(equal(p, dp->depname->namep)) return YES; return NO; } void enbint(void (*k)(int)) { if(sigivalue == 0) signal(SIGINT,k); if(sigqvalue == 0) signal(SIGQUIT,k); } static int rddescf(char *descfile) { static int firstrd = YES; /* read and parse description */ if(firstrd) { firstrd = NO; if( !noruleflag ) { rdarray(dfltmacro); if(oldflag) rdarray(dfltsuff); } if(!envlast) loadenv(); } return parse(descfile); } static void rdarray(char **s) { linesptr = s; parse(CHNULL); } static void loadenv(void) { for(envpp = environ ; *envpp ; ++envpp) eqsign(*envpp); envpp = NULL; } static void printdesc(int prntflag) { nameblkp p; depblkp dp; struct varblock *vp; struct dirhd *od; struct shblock *sp; lineblkp lp; if(prntflag) { printf("Open directories:\n"); for (od = firstod; od; od = od->nxtdirhd) printf("\t%s\n", od->dirn); } if(firstvar != 0) printf("Macros:\n"); for(vp = firstvar; vp ; vp = vp->nxtvarblock) printf("\t%s = %s\n" , vp->varname , vp->varval ? vp->varval : "(null)"); for(p = firstname; p; p = p->nxtnameblock) { printf("\n\n%s",p->namep); if(p->linep != 0) printf(":"); if(prntflag) printf(" done=%d",p->done); if(p==mainname) printf(" (MAIN NAME)"); for(lp = p->linep ; lp ; lp = lp->nxtlineblock) { if( dp = lp->depp ) { printf("\n depends on:"); for(; dp ; dp = dp->nxtdepblock) if(dp->depname != 0) printf(" %s ", dp->depname->namep); } if(sp = lp->shp) { printf("\n commands:\n"); for( ; sp ; sp = sp->nxtshblock) printf("\t%s\n", sp->shbp); } } } printf("\n"); fflush(stdout); } /sys/src/ape/cmd/make/misc.c 664 sys sys 1367613436 7186 #include "defs.h" static int hasslash(char *); static int haspercent(char *); static void rehash(void); /* simple linear hash. hash function is sum of characters mod hash table size. */ static int hashloc(char *s) { int i; int hashval; char *t; hashval = 0; for(t=s; *t!='\0' ; ++t) hashval += *t; hashval %= hashsize; for(i=hashval; hashtab[i]!=0 && !equal(s,hashtab[i]->namep); i = i >= hashsize-1 ? 0 : i+1) ; return i; } nameblkp srchname(char *s) { return hashtab[hashloc(s)] ; } nameblkp makename(char *s) { nameblkp p; if(nhashed > hashthresh) rehash(); ++nhashed; hashtab[hashloc(s)] = p = ALLOC(nameblock); p->nxtnameblock = firstname; p->namep = copys(s); /* make a fresh copy of the string s */ /* p->linep = 0; p->done = 0; p->septype = 0; p->modtime = 0; */ firstname = p; if(mainname==NULL && !haspercent(s) && (*s!='.' || hasslash(s)) ) mainname = p; return p; } static int hasslash(char *s) { for( ; *s ; ++s) if(*s == '/') return YES; return NO; } static int haspercent(char *s) { for( ; *s ; ++s) if(*s == '%') return YES; return NO; } int hasparen(char *s) { for( ; *s ; ++s) if(*s == '(') return YES; return NO; } static void rehash(void) { nameblkp *ohash; nameblkp p, *hp, *endohash; hp = ohash = hashtab; endohash = hashtab + hashsize; newhash(2*hashsize); while( hpnamep)] = p; free( (char *) ohash); } void newhash(int newsize) { hashsize = newsize; hashtab = (nameblkp *) ckalloc(hashsize * sizeof(nameblkp)); hashthresh = (2*hashsize)/3; } nameblkp chkname(char *s) { nameblkp p; time_t k; /*TEMP NEW */ if(hasparen(s)) { k = lookarch(s); /*TEMP fprintf(stderr, "chkname(%s): look=%d\n", s, k); */ if(k == 0) return NULL; } if(p = srchname(s)) return p; dirsrch(s); return srchname(s); } char * copys(char *s) { char *t; if( (t = malloc( strlen(s)+1 ) ) == NULL) fatal("out of memory"); strcpy(t, s); return t; } char * concat(char *a, char *b, char *c) /* c = concatenation of a and b */ { char *t; t = c; while(*t = *a++) t++; while(*t++ = *b++); return c; } int suffix(char *a, char *b, char *p) /* is b the suffix of a? if so, set p = prefix */ { char *a0,*b0; a0 = a; b0 = b; while(*a++); while(*b++); if( (a-a0) < (b-b0) ) return 0; while(b>b0) if(*--a != *--b) return 0; while(a0 100) fatal("infinitely recursive macro?"); if(a) while(*a) { if(*a!='$' || a[1]=='\0' || *++a=='$') /* if a non-macro character copy it. if $$ or $\0, copy $ */ *b++ = *a++; else { s = vname; if( *a=='(' || *a=='{' ) { closer = ( *a=='(' ? ')' : '}'); ++a; while(*a == ' ') ++a; while(*a!=' ' && *a!=closer && *a!='\0') *s++ = *a++; while(*a!=closer && *a!='\0') ++a; if(*a == closer) ++a; } else *s++ = *a++; *s = '\0'; if( (vbp = varptr(vname)) ->varval != 0) { b = subst(vbp->varval, b); vbp->used = YES; } } } *b = '\0'; --depth; return b; } void setvar(char *v, char *s, int dyn) { struct varblock *p; p = varptr(v); if( ! p->noreset ) { p->varval = s; p->noreset = inarglist; if(p->used && !dyn) fprintf(stderr, "Warning: %s changed after being used\n",v); if(p->export) { /* change string pointed to by environment to new v=s */ char *t; int lenv; lenv = strlen(v); *(p->export) = t = (char *) ckalloc(lenv + strlen(s) + 2); strcpy(t,v); t[lenv] = '='; strcpy(t+lenv+1, s); } else p->export = envpp; } } /* for setting Bradford's *D and *F family of macros whens setting * etc */ void set3var(char *macro, char *value) { char *s; char macjunk[8], *lastslash, *dirpart, *filepart; setvar(macro, value, YES); if(value == CHNULL) dirpart = filepart = CHNULL; else { lastslash = CHNULL; for(s = value; *s; ++s) if(*s == '/') lastslash = s; if(lastslash) { dirpart = copys(value); filepart = dirpart + (lastslash-value); filepart[-1] = '\0'; } else { dirpart = ""; filepart = value; } } setvar(concat(macro, "D", macjunk), dirpart, YES); setvar(concat(macro, "F", macjunk), filepart, YES); } int eqsign(char *a) /*look for arguments with equal signs but not colons */ { char *s, *t; char c; while(*a == ' ') ++a; for(s=a ; *s!='\0' && *s!=':' ; ++s) if(*s == '=') { for(t = a ; *t!='=' && *t!=' ' && *t!='\t' ; ++t ); c = *t; *t = '\0'; for(++s; *s==' ' || *s=='\t' ; ++s); setvar(a, copys(s), NO); *t = c; return YES; } return NO; } struct varblock * varptr(char *v) { struct varblock *vp; /* for compatibility, $(TGS) = $^ */ if(equal(v, "TGS") ) v = "^"; for(vp = firstvar; vp ; vp = vp->nxtvarblock) if(equal(v , vp->varname)) return vp; vp = ALLOC(varblock); vp->nxtvarblock = firstvar; firstvar = vp; vp->varname = copys(v); vp->varval = 0; return vp; } int dynmacro(char *line) { char *s; char endc, *endp; if(!isalpha(line[0])) return NO; for(s=line+1 ; *s && (isalpha(*s) | isdigit(*s)) ; ++s) ; endp = s; while( isspace(*s) ) ++s; if(s[0]!=':' || s[1]!='=') return NO; endc = *endp; *endp = '\0'; setvar(line, copys(s+2), YES); *endp = endc; return YES; } void fatal1(char *s, char *t) { char buf[100]; sprintf(buf, s, t); fatal(buf); } void fatal(char *s) { fflush(stdout); if(s) fprintf(stderr, "Make: %s. Stop.\n", s); else fprintf(stderr, "\nStop.\n"); waitstack(0); exit(1); } /* appends to the chain for $? and $^ */ chainp appendq(chainp head, char *tail) { chainp p, q; p = ALLOC(chain); p->datap = tail; if(head) { for(q = head ; q->nextp ; q = q->nextp) ; q->nextp = p; return head; } else return p; } /* builds the value for $? and $^ */ char * mkqlist(chainp p, char *qbuf) { char *qbufp, *s; if(p == NULL) return ""; qbufp = qbuf; for( ; p ; p = p->nextp) { s = p->datap; if(qbufp+strlen(s) > &qbuf[QBUFMAX-3]) { fprintf(stderr, "$? list too long\n"); break; } while (*s) *qbufp++ = *s++; *qbufp++ = ' '; } *--qbufp = '\0'; return qbuf; } wildp iswild(char *name) { char *s; wildp p; for(s=name; *s; ++s) if(*s == '%') { p = ALLOC(wild); *s = '\0'; p->left = copys(name); *s = '%'; p->right = copys(s+1); p->llen = strlen(p->left); p->rlen = strlen(p->right); p->totlen = p->llen + p->rlen; return p; } return NULL; } char * wildmatch(wildp p, char *name, int len) { char *stem; char *s; char c; if(len < p->totlen || strncmp(name, p->left, p->llen) || strncmp(s = name+len-p->rlen, p->right, p->rlen) ) return CHNULL; /*TEMP fprintf(stderr, "wildmatch(%s)=%s%%%s)\n", name,p->left,p->right); */ c = *s; *s = '\0'; stem = copys(name + p->llen); *s = c; return stem; } /* substitute stem for any % marks */ char * wildsub(char *pat, char *stem) { static char temp[100]; char *s, *t; s = temp; for(; *pat; ++pat) if(*pat == '%') for(t = stem ; *t; ) *s++ = *t++; else *s++ = *pat; *s = '\0'; return temp; } /sys/src/ape/cmd/make/mkfile 664 sys sys 1369259712 387 APE=/sys/src/ape <$APE/config TARG=make OFILES=ident.$O\ main.$O\ doname.$O\ dosys.$O\ gram.$O\ misc.$O\ files.$O\ HFILES=defs.h YFILES=gram.y BIN=$APEBIN $makefile"); # The Contents of the MAKEFILE ... print MAKEFILE <<\EOF; # Test comment vs semicolon parsing and line continuation target: # this ; is just a comment \ @echo This is within a comment. @echo There should be no errors for this makefile. EOF # END of Contents of MAKEFILE close(MAKEFILE); &run_make_with_options($makefile,"",&get_logfile); # Create the answer to what should be produced by this Makefile $answer = "There should be no errors for this makefile.\n"; # COMPARE RESULTS &compare_output($answer,&get_logfile(1)) /sys/src/ape/cmd/make-3.79/tests/scripts/features/conditionals 644 bootes sys 1367613436 1207 # -*-perl-*- $description = "Check GNU make conditionals."; $details = "Attempt various different flavors of GNU make conditionals."; open(MAKEFILE,"> $makefile"); # The Contents of the MAKEFILE ... print MAKEFILE <<'EOMAKE'; objects = foo.obj arg1 = first arg2 = second arg3 = third arg4 = cc arg5 = second all: ifeq ($(arg1),$(arg2)) @echo arg1 equals arg2 else @echo arg1 NOT equal arg2 endif ifeq '$(arg2)' "$(arg5)" @echo arg2 equals arg5 else @echo arg2 NOT equal arg5 endif ifneq '$(arg3)' '$(arg4)' @echo arg3 NOT equal arg4 else @echo arg3 equal arg4 endif ifndef undefined @echo variable is undefined else @echo variable undefined is defined endif ifdef arg4 @echo arg4 is defined else @echo arg4 is NOT defined endif EOMAKE close(MAKEFILE); &run_make_with_options($makefile,"",&get_logfile,0); # Create the answer to what should be produced by this Makefile $answer = "arg1 NOT equal arg2 arg2 equals arg5 arg3 NOT equal arg4 variable is undefined arg4 is defined "; # COMPARE RESULTS &compare_output($answer,&get_logfile(1)); # This tells the test driver that the perl test script executed properly. 1; /sys/src/ape/cmd/make-3.79/tests/scripts/features/default_names 644 bootes sys 1367613436 1635 # -*-perl-*- $description = "This script tests to make sure that Make looks for default makefiles in the correct order (GNUmakefile,makefile,Makefile)"; # Create a makefile called "GNUmakefile" $makefile = "GNUmakefile"; open(MAKEFILE,"> $makefile"); print MAKEFILE "FIRST: ; \@echo It chose GNUmakefile\n"; close(MAKEFILE); # DOS/WIN32 platforms preserve case, but Makefile is the same file as makefile. # Just test what we can here (avoid Makefile versus makefile test). # if ($port_type eq 'UNIX') { # Create another makefile called "makefile" open(MAKEFILE,"> makefile"); print MAKEFILE "SECOND: ; \@echo It chose makefile\n"; close(MAKEFILE); } # Create another makefile called "Makefile" open(MAKEFILE,"> Makefile"); print MAKEFILE "THIRD: ; \@echo It chose Makefile\n"; close(MAKEFILE); &run_make_with_options("","",&get_logfile); # Create the answer to what should be produced by this Makefile $answer = "It chose GNUmakefile\n"; # COMPARE RESULTS &compare_output($answer,&get_logfile(1)) || &error("abort"); unlink $makefile; # DOS/WIN32 platforms preserve case, but Makefile is the same file as makefile. # Just test what we can here (avoid Makefile versus makefile test). # if ($port_type eq 'UNIX') { $answer = "It chose makefile\n"; &run_make_with_options("","",&get_logfile); &compare_output($answer,&get_logfile(1)) || &error("abort"); unlink "makefile"; } $answer = "It chose Makefile\n"; &run_make_with_options("","",&get_logfile); &compare_output($answer,&get_logfile(1)) || &error("abort"); unlink "Makefile"; /sys/src/ape/cmd/make-3.79/tests/scripts/features/double_colon 644 bootes sys 1367613436 2976 # -*-perl-*- $description = "Test handling of double-colon rules."; $details = "\ We test these features: - Multiple commands for the same (double-colon) target - Different prerequisites for targets: only out-of-date ones are rebuilt. - Double-colon targets that aren't the goal target. Then we do the same thing for parallel builds: double-colon targets should always be built serially."; # The Contents of the MAKEFILE ... open(MAKEFILE,"> $makefile"); print MAKEFILE <<'EOF'; all: baz foo:: f1.h ; @echo foo FIRST foo:: f2.h ; @echo foo SECOND bar:: ; @echo aaa; sleep 1; echo aaa done bar:: ; @echo bbb baz:: ; @echo aaa baz:: ; @echo bbb biz:: ; @echo aaa biz:: two ; @echo bbb two: ; @echo two f1.h f2.h: ; @echo $@ d :: ; @echo ok d :: d ; @echo oops EOF close(MAKEFILE); # TEST 0: A simple double-colon rule that isn't the goal target. &run_make_with_options($makefile, "all", &get_logfile, 0); $answer = "aaa\nbbb\n"; &compare_output($answer, &get_logfile(1)); # TEST 1: As above, in parallel &run_make_with_options($makefile, "-j10 all", &get_logfile, 0); $answer = "aaa\nbbb\n"; &compare_output($answer, &get_logfile(1)); # TEST 2: A simple double-colon rule that is the goal target &run_make_with_options($makefile, "bar", &get_logfile, 0); $answer = "aaa\naaa done\nbbb\n"; &compare_output($answer, &get_logfile(1)); # TEST 3: As above, in parallel &run_make_with_options($makefile, "-j10 bar", &get_logfile, 0); $answer = "aaa\naaa done\nbbb\n"; &compare_output($answer, &get_logfile(1)); # TEST 4: Each double-colon rule is supposed to be run individually &touch('f2.h'); &touch('foo'); &run_make_with_options($makefile, "foo", &get_logfile, 0); $answer = "f1.h\nfoo FIRST\n"; &compare_output($answer, &get_logfile(1)); # TEST 5: Again, in parallel. &run_make_with_options($makefile, "-j10 foo", &get_logfile, 0); $answer = "f1.h\nfoo FIRST\n"; &compare_output($answer, &get_logfile(1)); # TEST 6: Each double-colon rule is supposed to be run individually &touch('f1.h'); unlink('f2.h'); &touch('foo'); &run_make_with_options($makefile, "foo", &get_logfile, 0); $answer = "f2.h\nfoo SECOND\n"; &compare_output($answer, &get_logfile(1)); # TEST 7: Again, in parallel. &run_make_with_options($makefile, "-j10 foo", &get_logfile, 0); $answer = "f2.h\nfoo SECOND\n"; &compare_output($answer, &get_logfile(1)); # TEST 8: Test circular dependency check; PR/1671 &run_make_with_options($makefile, "d", &get_logfile, 0); $answer = "ok\n$make_name: Circular d <- d dependency dropped.\noops\n"; &compare_output($answer, &get_logfile(1)); # TEST 8: I don't grok why this is different than the above, but it is... # # Hmm... further testing indicates this might be timing-dependent? # #&run_make_with_options($makefile, "-j10 biz", &get_logfile, 0); #$answer = "aaa\ntwo\nbbb\n"; #&compare_output($answer, &get_logfile(1)); unlink('foo','f1.h','f2.h'); 1; /sys/src/ape/cmd/make-3.79/tests/scripts/features/echoing 644 bootes sys 1367613436 2900 $description = "The following test creates a makefile to test command \n" ."echoing. It tests that when a command line starts with \n" ."a '\@', the echoing of that line is suppressed. It also \n" ."tests the -n option which tells make to ONLY echo the \n" ."commands and no execution happens. In this case, even \n" ."the commands with '\@' are printed. Lastly, it tests the \n" ."-s flag which tells make to prevent all echoing, as if \n" ."all commands started with a '\@'."; $details = "This test is similar to the 'clean' test except that a '\@' has\n" ."been placed in front of the delete command line. Four tests \n" ."are run here. First, make is run normally and the first echo\n" ."command should be executed. In this case there is no '\@' so \n" ."we should expect make to display the command AND display the \n" ."echoed message. Secondly, make is run with the clean target, \n" ."but since there is a '\@' at the beginning of the command, we\n" ."expect no output; just the deletion of a file which we check \n" ."for. Third, we give the clean target again except this time\n" ."we give make the -n option. We now expect the command to be \n" ."displayed but not to be executed. In this case we need only \n" ."to check the output since an error message would be displayed\n" ."if it actually tried to run the delete command again and the \n" ."file didn't exist. Lastly, we run the first test again with \n" ."the -s option and check that make did not echo the echo \n" ."command before printing the message."; $example = "EXAMPLE_FILE"; open(MAKEFILE,"> $makefile"); # The Contents of the MAKEFILE ... print MAKEFILE "all: \n"; print MAKEFILE "\techo This makefile did not clean the dir... good\n"; print MAKEFILE "clean: \n"; print MAKEFILE "\t\@$delete_command $example\n"; # END of Contents of MAKEFILE close(MAKEFILE); &touch($example); # TEST #1 # ------- &run_make_with_options($makefile,"",&get_logfile,0); $answer = "echo This makefile did not clean the dir... good\n" ."This makefile did not clean the dir... good\n"; &compare_output($answer,&get_logfile(1)); # TEST #2 # ------- &run_make_with_options($makefile,"clean",&get_logfile,0); $answer = ""; &compare_output($answer,&get_logfile(1)); if (-f $example) { $test_passed = 0; } # TEST #3 # ------- &run_make_with_options($makefile,"-n clean",&get_logfile,0); $answer = "$delete_command $example\n"; &compare_output($answer,&get_logfile(1)); # TEST #4 # ------- &run_make_with_options($makefile,"-s",&get_logfile,0); $answer = "This makefile did not clean the dir... good\n"; &compare_output($answer,&get_logfile(1)); 1; /sys/src/ape/cmd/make-3.79/tests/scripts/features/errors 644 bootes sys 1367613436 2433 $description = "The following tests the -i option and the '-' in front of \n" ."commands to test that make ignores errors in these commands\n" ."and continues processing."; $details = "This test runs two makes. The first runs on a target with a \n" ."command that has a '-' in front of it (and a command that is \n" ."intended to fail) and then a delete command after that is \n" ."intended to succeed. If make ignores the failure of the first\n" ."command as it is supposed to, then the second command should \n" ."delete a file and this is what we check for. The second make\n" ."that is run in this test is identical except that the make \n" ."command is given with the -i option instead of the '-' in \n" ."front of the command. They should run the same. "; if ($vos) { $delete_command = "delete_file"; } else { $delete_command = "rm"; } open(MAKEFILE,"> $makefile"); # The Contents of the MAKEFILE ... print MAKEFILE "clean:\n" ."\t-$delete_command cleanit\n" ."\t$delete_command foo\n" ."clean2: \n" ."\t$delete_command cleanit\n" ."\t$delete_command foo\n"; # END of Contents of MAKEFILE close(MAKEFILE); &touch("foo"); unlink("cleanit"); $cleanit_error = `sh -c "$delete_command cleanit 2>&1"`; $delete_error_code = $? >> 8; # TEST #1 # ------- $answer = "$delete_command cleanit\n" . $cleanit_error ."$make_name: [clean] Error $delete_error_code (ignored)\n" ."$delete_command foo\n"; &run_make_with_options($makefile,"",&get_logfile); # The output for this on VOS is too hard to replicate, so we only check it # on unix. if (!$vos) { &compare_output($answer,&get_logfile(1)); } # If make acted as planned, it should ignore the error from the first # command in the target and execute the second which deletes the file "foo" # This file, therefore, should not exist if the test PASSES. if (-f "foo") { $test_passed = 0; } &touch("foo"); # TEST #2 # ------- $answer = "$delete_command cleanit\n" . $cleanit_error ."$make_name: [clean2] Error $delete_error_code (ignored)\n" ."$delete_command foo\n"; &run_make_with_options($makefile,"clean2 -i",&get_logfile); if (!$vos) { &compare_output($answer,&get_logfile(1)); } if (-f "foo") { $test_passed = 0; } 1; /sys/src/ape/cmd/make-3.79/tests/scripts/features/escape 644 bootes sys 1367613436 1477 # -*-perl-*- $description = "Test various types of escaping in makefiles."; $details = "\ Make sure that escaping of `:' works in target names. Also make sure escaping of whitespace works in target names"; open(MAKEFILE,"> $makefile"); print MAKEFILE <<'EOF'; $(path)foo : ; @echo cp $^ $@ foo\ bar: ; @echo 'touch "$@"' EOF close(MAKEFILE); # TEST 1 &run_make_with_options($makefile, "", &get_logfile); $answer = "cp foo\n"; &compare_output($answer,&get_logfile(1)); # TEST 2: This one should fail, since the ":" is unquoted. &run_make_with_options($makefile, "path=p:", &get_logfile, 512); $answer = "$makefile:1: *** target pattern contains no `%'. Stop.\n"; &compare_output($answer,&get_logfile(1)); # TEST 3: This one should work, since we escape the ":". &run_make_with_options($makefile, "'path=p\\:'", &get_logfile, 0); $answer = "cp p:foo\n"; &compare_output($answer,&get_logfile(1)); # TEST 4: This one should fail, since the escape char is escaped. &run_make_with_options($makefile, "'path=p\\\\:'", &get_logfile, 512); $answer = "$makefile:1: *** target pattern contains no `%'. Stop.\n"; &compare_output($answer,&get_logfile(1)); # TEST 5: This one should work &run_make_with_options($makefile, "'foo bar'", &get_logfile, 0); $answer = "touch \"foo bar\"\n"; &compare_output($answer,&get_logfile(1)); # This tells the test driver that the perl test script executed properly. 1; /sys/src/ape/cmd/make-3.79/tests/scripts/features/include 644 bootes sys 1367613436 1922 # -*-mode: perl; rm-trailing-spaces: nil-*- $description = "Test various forms of the GNU make `include' command."; $details = "Test include, -include, sinclude and various regressions involving them. Test extra whitespace at the end of the include, multiple -includes and sincludes (should not give an error) and make sure that errors are reported for targets that were also -included."; $makefile2 = &get_tmpfile; open(MAKEFILE,"> $makefile"); # The contents of the Makefile ... print MAKEFILE < $makefile2"); print MAKEFILE "ANOTHER: ; \@echo This is another included makefile\n"; close(MAKEFILE); # Create the answer to what should be produced by this Makefile &run_make_with_options($makefile, "all", &get_logfile); $answer = "There should be no errors for this makefile.\n"; &compare_output($answer, &get_logfile(1)); &run_make_with_options($makefile, "ANOTHER", &get_logfile); $answer = "This is another included makefile\n"; &compare_output($answer, &get_logfile(1)); # Try to build the "error" target; this will fail since we don't know # how to create makeit.mk, but we should also get a message (even though # the -include suppressed it during the makefile read phase, we should # see one during the makefile run phase). # The fix to this caused more problems than the error, so I removed it. # pds -- 22 Jan 2000 #&run_make_with_options($makefile, "error", &get_logfile, 512); #$answer = "$make_name: *** No rule to make target `makeit.mk', needed by `error'.\n"; #&compare_output($answer, &get_logfile(1)); 1; /sys/src/ape/cmd/make-3.79/tests/scripts/features/mult_rules 644 bootes sys 1367613436 1771 $description = "\ The following test creates a makefile to test the presence of multiple rules for one target. One file can be the target of several rules if at most one rule has commands; the other rules can only have dependencies."; $details = "\ The makefile created in this test contains two hardcoded rules for foo.o and bar.o. It then gives another multiple target rule with the same names as above but adding more dependencies. Additionally, another variable extradeps is listed as a dependency but is defined to be null. It can however be defined on the make command line as extradeps=extra.h which adds yet another dependency to the targets."; open(MAKEFILE,"> $makefile"); # The Contents of the MAKEFILE ... print MAKEFILE < $makefile"); # The Contents of the MAKEFILE ... print MAKEFILE "bigoutput littleoutput: test.h\n"; print MAKEFILE "\t\@echo I am \$(subst output,,\$@)\n"; # END of Contents of MAKEFILE close(MAKEFILE); &touch("test.h"); &run_make_with_options($makefile,"bigoutput",&get_logfile); # Create the answer to what should be produced by this Makefile $answer = "I am big\n"; &compare_output($answer,&get_logfile(1)); &run_make_with_options($makefile,"littleoutput",&get_logfile); $answer = "I am little\n"; &compare_output($answer,&get_logfile(1)); unlink "test.h"; 1; /sys/src/ape/cmd/make-3.79/tests/scripts/features/override 644 bootes sys 1367613436 681 $description = "The following test creates a makefile to ..."; $details = ""; open(MAKEFILE,"> $makefile"); # The Contents of the MAKEFILE ... print MAKEFILE "override define foo\n" ."\@echo First comes the definition.\n" ."\@echo Then comes the override.\n" ."endef\n" ."all: \n" ."\t\$(foo)\n"; # END of Contents of MAKEFILE close(MAKEFILE); &run_make_with_options($makefile,"foo=Hello",&get_logfile); # Create the answer to what should be produced by this Makefile $answer = "First comes the definition.\n" ."Then comes the override.\n"; &compare_output($answer,&get_logfile(1)); 1; /sys/src/ape/cmd/make-3.79/tests/scripts/features/parallelism 644 bootes sys 1367613436 2747 # -*-perl-*- $description = "Test parallelism (-j) option."; $details = "This test creates a makefile with two double-colon default rules. The first rule has a series of sleep and echo commands intended to run in series. The second and third have just an echo statement. When make is called in this test, it is given the -j option with a value of 4. This tells make that it may start up to four jobs simultaneously. In this case, since the first command is a sleep command, the output of the second and third commands will appear before the first if indeed make is running all of these commands in parallel."; if (!$parallel_jobs) { return -1; } if ($vos) { $delete_command = "delete_file -no_ask"; $sleep_command = "sleep -seconds"; } else { $delete_command = "rm -f"; $sleep_command = "sleep"; } open(MAKEFILE,"> $makefile"); print MAKEFILE <<"EOF"; all : def_1 def_2 def_3 def_1 : ; \@echo ONE; $sleep_command 3 ; echo TWO def_2 : ; \@$sleep_command 2 ; echo THREE def_3 : ; \@$sleep_command 1 ; echo FOUR EOF close(MAKEFILE); &run_make_with_options($makefile, "-j 4", &get_logfile); $answer = "ONE\nFOUR\nTHREE\nTWO\n"; &compare_output($answer, &get_logfile(1)); # Test parallelism with included files. Here we sleep/echo while # building the included files, to test that they are being built in # parallel. $makefile2 = &get_tmpfile; open(MAKEFILE,"> $makefile2"); print MAKEFILE <<"EOF"; all: 1 2; \@echo success -include 1.inc 2.inc 1.inc: ; \@echo ONE.inc; $sleep_command 2; echo TWO.inc; echo "1: ; \@echo ONE; $sleep_command 2; echo TWO" > \$\@ 2.inc: ; \@$sleep_command 1; echo THREE.inc; echo "2: ; \@$sleep_command 1; echo THREE" > \$\@ EOF close(MAKEFILE); &run_make_with_options("$makefile2", "-j 4", &get_logfile); $answer = "ONE.inc\nTHREE.inc\nTWO.inc\nONE\nTHREE\nTWO\nsuccess\n"; &compare_output($answer, &get_logfile(1)); unlink('1.inc', '2.inc'); # Test parallelism with included files--this time recurse first and make # sure the jobserver works. $makefile3 = &get_tmpfile; open(MAKEFILE,"> $makefile3"); print MAKEFILE <<"EOF"; recurse: ; \@\$(MAKE) --no-print-directory -f $makefile3 INC=yes all all: 1 2; \@echo success INC = no ifeq (\$(INC),yes) -include 1.inc 2.inc endif 1.inc: ; \@echo ONE.inc; $sleep_command 2; echo TWO.inc; echo "1: ; \@echo ONE; $sleep_command 2; echo TWO" > \$\@ 2.inc: ; \@$sleep_command 1; echo THREE.inc; echo "2: ; \@$sleep_command 1; echo THREE" > \$\@ EOF close(MAKEFILE); &run_make_with_options("$makefile3", "-j 4", &get_logfile); $answer = "ONE.inc\nTHREE.inc\nTWO.inc\nONE\nTHREE\nTWO\nsuccess\n"; &compare_output($answer, &get_logfile(1)); unlink('1.inc', '2.inc'); 1; /sys/src/ape/cmd/make-3.79/tests/scripts/features/patspecific_vars 644 bootes sys 1367613436 1296 # -*-perl-*- $description = "Test pattern-specific variable settings."; $details = "\ Create a makefile containing various flavors of pattern-specific variable settings, override and non-override, and using various variable expansion rules, semicolon interference, etc."; open(MAKEFILE,"> $makefile"); print MAKEFILE <<'EOF'; all: one.x two.x three.x FOO = foo BAR = bar BAZ = baz thr% : override BAZ = three t%.x: BAR = four %.x: BAR = two %.x: override BAZ = three one.x: override FOO = one one.x two.x three.x: ; @echo $(FOO) $(BAR) $(BAZ) four.x: baz ; @echo $(FOO) $(BAR) $(BAZ) baz: ; @echo $(FOO) $(BAR) $(BAZ) EOF close(MAKEFILE); # TEST #1 -- basics &run_make_with_options($makefile, "", &get_logfile); $answer = "one two three\nfoo four baz\nfoo bar three\n"; &compare_output($answer,&get_logfile(1)); # TEST #2 -- try the override feature &run_make_with_options($makefile, "BAZ=five", &get_logfile); $answer = "one two three\nfoo four five\nfoo bar three\n"; &compare_output($answer,&get_logfile(1)); # TEST #3 -- make sure patterns are inherited properly &run_make_with_options($makefile, "four.x", &get_logfile); $answer = "foo two three\nfoo two three\n"; &compare_output($answer,&get_logfile(1)); 1; /sys/src/ape/cmd/make-3.79/tests/scripts/features/quoting 644 bootes sys 1367613436 714 # -*-perl-*- $description = "The following test creates a makefile to test using \n" . "quotes within makefiles."; open(MAKEFILE,"> $makefile"); # The Contents of the MAKEFILE ... print MAKEFILE <<'EOM'; SHELL = /bin/sh TEXFONTS = NICEFONT DEFINES = -DDEFAULT_TFM_PATH=\".:$(TEXFONTS)\" test: ; @"echo" 'DEFINES = $(DEFINES)' EOM # END of Contents of MAKEFILE close(MAKEFILE); &run_make_with_options($makefile,"",&get_logfile); # Create the answer to what should be produced by this Makefile $answer = 'DEFINES = -DDEFAULT_TFM_PATH=\".:NICEFONT\"' . "\n"; # COMPARE RESULTS &compare_output($answer,&get_logfile(1)); 1; /sys/src/ape/cmd/make-3.79/tests/scripts/features/recursion 644 bootes sys 1367613436 1826 # -*-perl-*- $description = "The following test creates a makefile to ...\n"; $details = "DETAILS"; open(MAKEFILE,"> $makefile"); # The Contents of the MAKEFILE ... print MAKEFILE "all: \n" ."\t\$(MAKE) -f $makefile foo \n" ."foo: \n" ."\t\@echo \$(MAKE) \n" ."\t\@echo MAKELEVEL = \$(MAKELEVEL)\n" ."\t\$(MAKE) -f $makefile last \n" ."last: \n" ."\t\@echo \$(MAKE) \n" ."\t\@echo MAKELEVEL = \$(MAKELEVEL) \n" ."\t\@echo THE END\n"; # END of Contents of MAKEFILE close(MAKEFILE); if ($vos) { $answer = "$make_name: Entering directory \`$pwd\'\n" ."make 'CFLAGS=-O' -f $makefile foo \n" ."make CFLAGS=-O\n" ."MAKELEVEL = 0\n" ."make 'CFLAGS=-O' -f $makefile last \n" ."make CFLAGS=-O\n" ."MAKELEVEL = 0\n" ."THE END\n" ."$make_name: Leaving directory `$pwd'\n"; } else { $answer = "$make_name: Entering directory `$pwd'\n" ."$mkpath -f $makefile foo \n" ."${make_name}[1]: Entering directory `$pwd'\n" ."$mkpath\n" ."MAKELEVEL = 1\n" ."$mkpath -f $makefile last \n" ."${make_name}[2]: Entering directory `$pwd'\n" ."$mkpath\n" ."MAKELEVEL = 2\n" ."THE END\n" ."${make_name}[2]: Leaving directory `$pwd'\n" ."${make_name}[1]: Leaving directory `$pwd'\n" ."$make_name: Leaving directory `$pwd'\n"; } $mkoptions = "CFLAGS=-O -w"; $mkoptions .= " -j 2" if ($parallel_jobs); &run_make_with_options($makefile,$mkoptions,&get_logfile,0); &compare_output($answer,&get_logfile(1)); 1; /sys/src/ape/cmd/make-3.79/tests/scripts/features/reinvoke 644 bootes sys 1367613436 2099 # -*-mode: perl-*- $description = "Test GNU make's auto-reinvocation feature."; $details = "\ If the makefile or one it includes can be rebuilt then it is, and make is reinvoked. We create a rule to rebuild the makefile from a temp file, then touch the temp file to make it newer than the makefile."; $makefile2 = &get_tmpfile; $makefile_orig = &get_tmpfile; open(MAKEFILE,"> $makefile"); print MAKEFILE <> \$\@ include $makefile2 EOM close(MAKEFILE); &touch($makefile2); # Sleep 2 seconds for DOS/Windows FAT volumes which have 2-second # granularity of file times. sleep(2); &touch("$makefile_orig"); &run_make_with_options($makefile, "", &get_logfile, 0); # Create the answer to what should be produced by this Makefile $answer = "rebuilding $makefile2.\nrebuilding $makefile.\nrunning rules.\n"; &compare_output($answer,&get_logfile(1)) && unlink "$makefile_orig"; # In this test we create an included file that's out-of-date, but then # the rule doesn't update it. Make shouldn't re-exec. $makefile3 = &get_tmpfile; open(MAKEFILE, "> $makefile3"); print MAKEFILE <<'EOM'; SHELL = /bin/sh all: ; @echo hello a : b ; echo >> $@ b : c ; [ -f $@ ] || echo >> $@ c: ; echo >> $@ include $(F) EOM close(MAKEFILE); &touch('b'); &touch('a'); sleep(2); &touch('c'); # First try with the file that's not updated "once removed" from the # file we're including. &run_make_with_options($makefile3, "F=a", &get_logfile, 0); $answer = "[ -f b ] || echo >> b\nhello\n"; &compare_output($answer,&get_logfile(1)); # Now try with the file we're not updating being the actual file we're # including: this and the previous one test different parts of the code. &run_make_with_options($makefile3, "F=b", &get_logfile, 0); $answer = "[ -f b ] || echo >> b\nhello\n"; &compare_output($answer,&get_logfile(1)); unlink('a','b','c'); # This tells the test driver that the perl test script executed properly. 1; /sys/src/ape/cmd/make-3.79/tests/scripts/features/statipattrules 644 bootes sys 1367613436 1675 # -*-perl-*- $description = "Test handling of static pattern rules."; $details = "\ The makefile created in this test has three targets. The filter command is used to get those target names ending in .o and statically creates a compile command with the target name and the target name with .c. It also does the same thing for another target filtered with .elc and creates a command to emacs a .el file"; open(MAKEFILE,"> $makefile"); print MAKEFILE <<'EOF'; files = foo.elc bar.o lose.o $(filter %.o,$(files)): %.o: %.c ; @echo CC -c $(CFLAGS) $< -o $@ $(filter %.elc,$(files)): %.elc: %.el ; @echo emacs $< EOF close(MAKEFILE); &touch('bar.c', 'lose.c'); # TEST #1 # ------- &run_make_with_options($makefile, '', &get_logfile); $answer = "CC -c bar.c -o bar.o\n"; &compare_output($answer, &get_logfile(1)); # TEST #2 # ------- &run_make_with_options($makefile, 'lose.o', &get_logfile); $answer = "CC -c lose.c -o lose.o\n"; &compare_output($answer, &get_logfile(1)); # TEST #3 # ------- &touch("foo.el"); &run_make_with_options($makefile, 'foo.elc', &get_logfile); $answer = "emacs foo.el\n"; &compare_output($answer, &get_logfile(1)); unlink('foo.el', 'bar.c', 'lose.c'); # TEST #4 -- PR/1670: don't core dump on invalid static pattern rules # ------- $makefile2 = &get_tmpfile; open(MAKEFILE, "> $makefile2"); print MAKEFILE "foo: foo%: % ; \@echo $@\n"; close(MAKEFILE); &run_make_with_options($makefile2, '', &get_logfile, 512); $answer = "$makefile2:1: *** target `foo' leaves prerequisite pattern empty. Stop.\n"; &compare_output($answer, &get_logfile(1)); 1; /sys/src/ape/cmd/make-3.79/tests/scripts/features/targetvars 644 bootes sys 1367613436 3784 # -*-perl-*- $description = "Test target-specific variable settings."; $details = "\ Create a makefile containing various flavors of target-specific variable values, override and non-override, and using various variable expansion rules, semicolon interference, etc."; open(MAKEFILE,"> $makefile"); print MAKEFILE <<'EOF'; SHELL = /bin/sh export FOO = foo export BAR = bar one: override FOO = one one two: ; @echo $(FOO) $(BAR) two: BAR = two three: ; BAR=1000 @echo $(FOO) $(BAR) # Some things that shouldn't be target vars funk : override funk : override adelic adelic override : ; echo $@ # Test per-target recursive variables four:FOO=x four:VAR$(FOO)=ok four: ; @echo '$(FOO) $(VAR$(FOO)) $(VAR) $(VARx)' five:FOO=x five six : VAR$(FOO)=good five six: ;@echo '$(FOO) $(VAR$(FOO)) $(VAR) $(VARx) $(VARfoo)' # Test per-target variable inheritance seven: eight seven eight: ; @echo $@: $(FOO) $(BAR) seven: BAR = seven seven: FOO = seven eight: BAR = eight # Test the export keyword with per-target variables nine: ; @echo $(FOO) $(BAR) $$FOO $$BAR nine: FOO = wallace # Test = escaping EQ = = ten: one\=two ten: one \= two ten one$(EQ)two $(EQ):;@echo $@ .PHONY: one two three four five six seven eight nine ten $(EQ) one$(EQ)two # Test target-specific vars with pattern/suffix rules QVAR = qvar RVAR = = %.q : ; @echo $(QVAR) $(RVAR) foo.q : RVAR += rvar # Target-specific vars with multiple LHS pattern rules %.r %.s %.t: ; @echo $(QVAR) $(RVAR) $(SVAR) $(TVAR) foo.r : RVAR += rvar foo.t : TVAR := $(QVAR) EOF close(MAKEFILE); # TEST #1 &run_make_with_options($makefile, "one two three", &get_logfile); $answer = "one bar\nfoo two\nBAR=1000\nfoo bar\n"; &compare_output($answer,&get_logfile(1)); # TEST #2 &run_make_with_options($makefile, "one two FOO=1 BAR=2", &get_logfile); $answer = "one 2\n1 2\n"; &compare_output($answer,&get_logfile(1)); # TEST #3 &run_make_with_options($makefile, "four", &get_logfile); $answer = "x ok ok\n"; &compare_output($answer,&get_logfile(1)); # TEST #4 &run_make_with_options($makefile, "seven", &get_logfile); $answer = "eight: seven eight\nseven: seven seven\n"; &compare_output($answer,&get_logfile(1)); # TEST #5 &run_make_with_options($makefile, "nine", &get_logfile); $answer = "wallace bar wallace bar\n"; &compare_output($answer,&get_logfile(1)); # TEST #6 &run_make_with_options($makefile, "ten", &get_logfile); $answer = "one=two\none bar\n=\nfoo two\nten\n"; &compare_output($answer,&get_logfile(1)); # TEST #6 &run_make_with_options($makefile, "foo.q bar.q", &get_logfile); $answer = "qvar = rvar\nqvar =\n"; &compare_output($answer,&get_logfile(1)); # TEST #7 &run_make_with_options($makefile, "foo.t bar.s", &get_logfile); $answer = "qvar = qvar\nqvar =\n"; &compare_output($answer,&get_logfile(1)); # TEST #8 # For PR/1378: Target-specific vars don't inherit correctly $makefile2 = &get_tmpfile; open(MAKEFILE,"> $makefile2"); print MAKEFILE <<'EOF'; foo: FOO = foo bar: BAR = bar foo: bar bar: baz baz: ; @echo $(FOO) $(BAR) EOF close(MAKEFILE); &run_make_with_options("$makefile2", "", &get_logfile); $answer = "foo bar\n"; &compare_output($answer, &get_logfile(1)); # TEST #9 # For PR/1380: Using += assignment in target-specific variables sometimes fails $makefile3 = &get_tmpfile; open(MAKEFILE,"> $makefile3"); print MAKEFILE <<'EOF'; .PHONY: all one all: FOO += baz all: one; @echo $(FOO) FOO = bar one: FOO += biz one: ; @echo $(FOO) EOF close(MAKEFILE); &run_make_with_options("$makefile3", "", &get_logfile); $answer = "bar baz biz\nbar baz\n"; &compare_output($answer, &get_logfile(1)); &run_make_with_options("$makefile3", "one", &get_logfile); $answer = "bar biz\n"; &compare_output($answer, &get_logfile(1)); 1; /sys/src/ape/cmd/make-3.79/tests/scripts/features/varnesting 644 bootes sys 1367613436 624 $description = "The following test creates a makefile to ..."; $details = ""; open(MAKEFILE,"> $makefile"); # The Contents of the MAKEFILE ... print MAKEFILE "x = variable1\n" ."variable2 := Hello\n" ."y = \$(subst 1,2,\$(x))\n" ."z = y\n" ."a := \$(\$(\$(z)))\n" ."all: \n" ."\t\@echo \$(a)\n"; # END of Contents of MAKEFILE close(MAKEFILE); &run_make_with_options($makefile,"",&get_logfile); # Create the answer to what should be produced by this Makefile $answer = "Hello\n"; &compare_output($answer,&get_logfile(1)); 1; /sys/src/ape/cmd/make-3.79/tests/scripts/features/vpath 644 bootes sys 1367613436 2491 $description = "The following test creates a makefile to test the \n" ."vpath directive which allows you to specify a search \n" ."path for a particular class of filenames, those that\n" ."match a particular pattern."; $details = "This tests the vpath directive by specifying search directories\n" ."for one class of filenames with the form: vpath pattern directories" ."\nIn this test, we specify the working directory for all files\n" ."that end in c or h. We also test the variables $@ (which gives\n" ."target name) and $^ (which is a list of all dependencies \n" ."including the directories in which they were found). It also\n" ."uses the function firstword used to extract just the first\n" ."dependency from the entire list."; open(MAKEFILE,"> $makefile"); # The Contents of the MAKEFILE ... print MAKEFILE "vpath %.c foo\n"; print MAKEFILE "vpath %.c $workdir\n"; print MAKEFILE "vpath %.h $workdir\n"; print MAKEFILE "objects = main.o kbd.o commands.o display.o insert.o\n"; print MAKEFILE "edit: \$(objects)\n"; print MAKEFILE "\t\@echo cc -o \$@ \$^\n"; print MAKEFILE "main.o : main.c defs.h\n"; print MAKEFILE "\t\@echo cc -c \$(firstword \$^)\n"; print MAKEFILE "kbd.o : kbd.c defs.h command.h\n"; print MAKEFILE "\t\@echo cc -c kbd.c\n"; print MAKEFILE "commands.o : command.c defs.h command.h\n"; print MAKEFILE "\t\@echo cc -c commands.c\n"; print MAKEFILE "display.o : display.c defs.h buffer.h\n"; print MAKEFILE "\t\@echo cc -c display.c\n"; print MAKEFILE "insert.o : insert.c defs.h buffer.h\n"; print MAKEFILE "\t\@echo cc -c insert.c\n"; # END of Contents of MAKEFILE close(MAKEFILE); @files_to_touch = ("$workdir${pathsep}main.c","$workdir${pathsep}defs.h", "$workdir${pathsep}kbd.c","$workdir${pathsep}command.h", "$workdir${pathsep}commands.c","$workdir${pathsep}display.c", "$workdir${pathsep}buffer.h","$workdir${pathsep}insert.c", "$workdir${pathsep}command.c"); &touch(@files_to_touch); &run_make_with_options($makefile,"",&get_logfile); # Create the answer to what should be produced by this Makefile $answer = "cc -c $workdir${pathsep}main.c\ncc -c kbd.c\ncc -c commands.c\n" ."cc -c display.c\n" ."cc -c insert.c\ncc -o edit main.o kbd.o commands.o display.o " ."insert.o\n"; if (&compare_output($answer,&get_logfile(1))) { unlink @files_to_touch; } 1; /sys/src/ape/cmd/make-3.79/tests/scripts/features/vpath2 644 bootes sys 1367613436 1247 $description = "This is part 2 in a series to test the vpath directive\n" ."It tests the three forms of the directive:\n" ." vpath pattern directive\n" ." vpath pattern (clears path associated with pattern)\n" ." vpath (clears all paths specified with vpath)\n"; $details = "This test simply adds many search paths using various vpath\n" ."directive forms and clears them afterwards. It has a simple\n" ."rule to print a message at the end to confirm that the makefile\n" ."ran with no errors.\n"; open(MAKEFILE,"> $makefile"); # The Contents of the MAKEFILE ... print MAKEFILE "VPATH = $workdir:$sourcedir\n"; print MAKEFILE "vpath %.c foo\n"; print MAKEFILE "vpath %.c $workdir\n"; print MAKEFILE "vpath %.c $sourcedir\n"; print MAKEFILE "vpath %.h $workdir\n"; print MAKEFILE "vpath %.c\n"; print MAKEFILE "vpath\n"; print MAKEFILE "all:\n"; print MAKEFILE "\t\@echo ALL IS WELL\n"; # END of Contents of MAKEFILE close(MAKEFILE); &run_make_with_options($makefile,"",&get_logfile); # Create the answer to what should be produced by this Makefile $answer = "ALL IS WELL\n"; &compare_output($answer,&get_logfile(1)); 1; /sys/src/ape/cmd/make-3.79/tests/scripts/features/vpathgpath 644 bootes sys 1367613436 1069 # -*-perl-*- $description = "Tests VPATH+/GPATH functionality."; $details = ""; $VP = "$workdir$pathsep"; open(MAKEFILE,"> $makefile"); # The Contents of the MAKEFILE ... print MAKEFILE "VPATH = $VP\n"; print MAKEFILE <<'EOMAKE'; GPATH = $(VPATH) .SUFFIXES: .a .b .c .d .PHONY: general rename notarget intermediate %.a: %.b: %.c: %.d: %.a : %.b ; cat $^ > $@ %.b : %.c ; cat $^ > $@ %.c :: %.d ; cat $^ > $@ # General testing info: general: foo.b foo.b: foo.c bar.c EOMAKE close(MAKEFILE); @touchedfiles = (); sub touchfiles { foreach (@_) { ($f = $_) =~ s,VP/,$VP,g; &touch($f); push(@touchedfiles, $f); sleep(1); } } # Run the general-case test &touchfiles("VP/foo.d", "VP/bar.d", "VP/foo.c", "VP/bar.c", "foo.b", "bar.d"); &run_make_with_options($makefile,"general",&get_logfile()); push(@touchedfiles, "bar.c"); $answer = "$make_name: Nothing to be done for `general'.\n"; &compare_output($answer,&get_logfile(1)); unlink(@touchedfiles) unless $keep; 1; /sys/src/ape/cmd/make-3.79/tests/scripts/features/vpathplus 644 bootes sys 1367613436 2357 # -*-perl-*- $description = "Tests the new VPATH+ functionality added in 3.76."; $details = ""; $VP = "$workdir$pathsep"; open(MAKEFILE,"> $makefile"); # The Contents of the MAKEFILE ... print MAKEFILE "VPATH = $VP\n"; print MAKEFILE <<'EOMAKE'; SHELL = /bin/sh .SUFFIXES: .a .b .c .d .PHONY: general rename notarget intermediate %.a: %.b: %.c: %.d: %.a : %.b cat $^ > $@ %.b : %.c cat $^ > $@ 2>/dev/null || exit 1 %.c :: %.d cat $^ > $@ # General testing info: general: foo.b foo.b: foo.c bar.c # Rename testing info: rename: $(VPATH)/foo.c foo.d # Target not made testing info: notarget: notarget.b notarget.c: notarget.d -@echo "not creating $@ from $^" # Intermediate files: intermediate: inter.a EOMAKE close(MAKEFILE); @touchedfiles = (); sub touchfiles { foreach (@_) { sleep($wtime); ($f = $_) =~ s,VP/,$VP,g; &touch($f); push(@touchedfiles, $f); } } # Run the general-case test &touchfiles("VP/foo.d", "VP/bar.d", "VP/foo.c", "VP/bar.c", "foo.b", "bar.d"); &run_make_with_options($makefile,"general",&get_logfile); push(@touchedfiles, "bar.c"); $answer = "cat bar.d > bar.c cat ${VP}foo.c bar.c > foo.b 2>/dev/null || exit 1 "; &compare_output($answer,&get_logfile(1)); # Test rules that don't make the target correctly &touchfiles("VP/notarget.c", "notarget.b", "notarget.d"); &run_make_with_options($makefile,"notarget",&get_logfile,512); $answer = "not creating notarget.c from notarget.d cat notarget.c > notarget.b 2>/dev/null || exit 1 $make_name: *** [notarget.b] Error 1 "; &compare_output($answer,&get_logfile(1)); # Test intermediate file handling (part 1) &touchfiles("VP/inter.d"); &run_make_with_options($makefile,"intermediate",&get_logfile); push(@touchedfiles, "inter.a", "inter.b"); $answer = "cat ${VP}inter.d > inter.c cat inter.c > inter.b 2>/dev/null || exit 1 cat inter.b > inter.a rm inter.b inter.c "; &compare_output($answer,&get_logfile(1)); # Test intermediate file handling (part 2) &touchfiles("VP/inter.b", "VP/inter.d"); &run_make_with_options($makefile,"intermediate",&get_logfile); $answer = "cat ${VP}inter.d > inter.c cat inter.c > inter.b 2>/dev/null || exit 1 cat inter.b > inter.a rm inter.c "; &compare_output($answer,&get_logfile(1)); unlink @touchedfiles unless $keep; 1; /sys/src/ape/cmd/make-3.79/tests/scripts/functions 20000000755 bootes sys 1367862839 0 /sys/src/ape/cmd/make-3.79/tests/scripts/functions/addprefix 644 bootes sys 1367613436 1156 $description = "The following test creates a makefile to test the addprefix " ."function."; $details = ""; # IF YOU NEED >1 MAKEFILE FOR THIS TEST, USE &get_tmpfile; TO GET # THE NAME OF THE MAKEFILE. THIS INSURES CONSISTENCY AND KEEPS TRACK OF # HOW MANY MAKEFILES EXIST FOR EASY DELETION AT THE END. # EXAMPLE: $makefile2 = &get_tmpfile; open(MAKEFILE,"> $makefile"); # The Contents of the MAKEFILE ... print MAKEFILE "string := \$(addprefix src${pathsep},a.b.z.foo hacks) \n" ."all: \n" ."\t\@echo \$(string) \n"; # END of Contents of MAKEFILE close(MAKEFILE); &run_make_with_options($makefile,"",&get_logfile,0); # Create the answer to what should be produced by this Makefile $answer = "src${pathsep}a.b.z.foo src${pathsep}hacks\n"; # COMPARE RESULTS # In this call to compare output, you should use the call &get_logfile(1) # to send the name of the last logfile created. You may also use # the special call &get_logfile(1) which returns the same as &get_logfile(1). &compare_output($answer,&get_logfile(1)); # This tells the test driver that the perl test script executed properly. 1; /sys/src/ape/cmd/make-3.79/tests/scripts/functions/addsuffix 644 bootes sys 1367613436 1149 $description = "The following test creates a makefile to test the addsuffix " ."function."; $details = ""; # IF YOU NEED >1 MAKEFILE FOR THIS TEST, USE &get_tmpfile; TO GET # THE NAME OF THE MAKEFILE. THIS INSURES CONSISTENCY AND KEEPS TRACK OF # HOW MANY MAKEFILES EXIST FOR EASY DELETION AT THE END. # EXAMPLE: $makefile2 = &get_tmpfile; open(MAKEFILE,"> $makefile"); # The Contents of the MAKEFILE ... print MAKEFILE "string := \$(addsuffix .c,src${pathsep}a.b.z.foo hacks) \n" ."all: \n" ."\t\@echo \$(string) \n"; # END of Contents of MAKEFILE close(MAKEFILE); &run_make_with_options($makefile,"",&get_logfile,0); # Create the answer to what should be produced by this Makefile $answer = "src${pathsep}a.b.z.foo.c hacks.c\n"; # COMPARE RESULTS # In this call to compare output, you should use the call &get_logfile(1) # to send the name of the last logfile created. You may also use # the special call &get_logfile(1) which returns the same as &get_logfile(1). &compare_output($answer,&get_logfile(1)); # This tells the test driver that the perl test script executed properly. 1; /sys/src/ape/cmd/make-3.79/tests/scripts/functions/basename 644 bootes sys 1367613436 1280 $description = "The following test creates a makefile to test the suffix " ."function."; $details = ""; # IF YOU NEED >1 MAKEFILE FOR THIS TEST, USE &get_tmpfile; TO GET # THE NAME OF THE MAKEFILE. THIS INSURES CONSISTENCY AND KEEPS TRACK OF # HOW MANY MAKEFILES EXIST FOR EASY DELETION AT THE END. # EXAMPLE: $makefile2 = &get_tmpfile; open(MAKEFILE,"> $makefile"); # The Contents of the MAKEFILE ... print MAKEFILE "string := \$(basename src${pathsep}a.b.z.foo.c src${pathsep}hacks src.bar${pathsep}a.b.z.foo.c src.bar${pathsep}hacks hacks) \n" ."all: \n" ."\t\@echo \$(string) \n"; # END of Contents of MAKEFILE close(MAKEFILE); &run_make_with_options($makefile,"",&get_logfile,0); # Create the answer to what should be produced by this Makefile $answer = "src${pathsep}a.b.z.foo src${pathsep}hacks src.bar${pathsep}a.b.z.foo src.bar${pathsep}hacks hacks\n"; # COMPARE RESULTS # In this call to compare output, you should use the call &get_logfile(1) # to send the name of the last logfile created. You may also use # the special call &get_logfile(1) which returns the same as &get_logfile(1). &compare_output($answer,&get_logfile(1)); # This tells the test driver that the perl test script executed properly. 1; /sys/src/ape/cmd/make-3.79/tests/scripts/functions/call 644 bootes sys 1367613436 1524 # -*-perl-*- $description = "Test the call function.\n"; $details = "Try various uses of call and ensure they all give the correct results.\n"; open(MAKEFILE, "> $makefile"); # The Contents of the MAKEFILE ... print MAKEFILE <<'EOMAKE'; # Simple, just reverse two things # reverse = $2 $1 # A complex `map' function, using recursive `call'. # map = $(foreach a,$2,$(call $1,$a)) # Test using a builtin; this is silly as it's simpler to do without call # my-notdir = $(call notdir,$(1)) # Test using non-expanded builtins # my-foreach = $(foreach $(1),$(2),$(3)) my-if = $(if $(1),$(2),$(3)) # Test recursive invocations of call with different arguments # one = $(1) $(2) $(3) two = $(call one,$(1),foo,$(2)) all: ; @echo '$(call reverse,bar,foo)'; \ echo '$(call map,origin,MAKE reverse map)'; \ echo '$(call my-notdir,a/b c/d e/f)'; \ echo '$(call my-foreach)'; \ echo '$(call my-foreach,a,,,)'; \ echo '$(call my-if,a,b,c)'; \ echo '$(call two,bar,baz)' EOMAKE # These won't work until/unless PR/1527 is resolved. # echo '$(call my-foreach,a,x y z,$(a)$(a))'; \ # echo '$(call my-if,,$(warning don't print this),ok)' # # $answer = "xx yy zz\nok\n"; # END of Contents of MAKEFILE close(MAKEFILE); &run_make_with_options($makefile, "", &get_logfile); $answer = "foo bar\ndefault file file\nb d f\n\n\nb\nbar foo baz\n"; &compare_output($answer, &get_logfile(1)); 1; /sys/src/ape/cmd/make-3.79/tests/scripts/functions/dir 644 bootes sys 1367613436 1123 $description = "The following test creates a makefile to test the dir " ."function."; $details = ""; # IF YOU NEED >1 MAKEFILE FOR THIS TEST, USE &get_tmpfile; TO GET # THE NAME OF THE MAKEFILE. THIS INSURES CONSISTENCY AND KEEPS TRACK OF # HOW MANY MAKEFILES EXIST FOR EASY DELETION AT THE END. # EXAMPLE: $makefile2 = &get_tmpfile; open(MAKEFILE,"> $makefile"); # The Contents of the MAKEFILE ... print MAKEFILE "string := \$(dir src${pathsep}foo.c hacks) \n" ."all: \n" ."\t\@echo \$(string) \n"; # END of Contents of MAKEFILE close(MAKEFILE); &run_make_with_options($makefile,"",&get_logfile,0); # Create the answer to what should be produced by this Makefile $answer = "src${pathsep} .${pathsep}\n"; # COMPARE RESULTS # In this call to compare output, you should use the call &get_logfile(1) # to send the name of the last logfile created. You may also use # the special call &get_logfile(1) which returns the same as &get_logfile(1). &compare_output($answer,&get_logfile(1)); # This tells the test driver that the perl test script executed properly. 1; /sys/src/ape/cmd/make-3.79/tests/scripts/functions/error 644 bootes sys 1367613436 1284 $description = "\ The following test creates a makefile to test the error function."; $details = ""; open(MAKEFILE,"> $makefile"); print MAKEFILE <<'EOF'; ifdef ERROR1 $(error error is $(ERROR1)) endif ifdef ERROR2 $(error error is $(ERROR2)) endif ifdef ERROR3 all: some; @echo $(error error is $(ERROR3)) endif ifdef ERROR4 all: some; @echo error is $(ERROR4) @echo $(error error is $(ERROR4)) endif some: ; @echo Some stuff EOF close(MAKEFILE); # Test #1 &run_make_with_options($makefile, "ERROR1=yes", &get_logfile, 512); $answer = "$makefile:2: *** error is yes. Stop.\n"; &compare_output($answer,&get_logfile(1)); # Test #2 &run_make_with_options($makefile, "ERROR2=no", &get_logfile, 512); $answer = "$makefile:6: *** error is no. Stop.\n"; &compare_output($answer,&get_logfile(1)); # Test #3 &run_make_with_options($makefile, "ERROR3=maybe", &get_logfile, 512); $answer = "Some stuff\n$makefile:10: *** error is maybe. Stop.\n"; &compare_output($answer,&get_logfile(1)); # Test #4 &run_make_with_options($makefile, "ERROR4=definitely", &get_logfile, 512); $answer = "Some stuff\n$makefile:14: *** error is definitely. Stop.\n"; &compare_output($answer,&get_logfile(1)); # This tells the test driver that the perl test script executed properly. 1; /sys/src/ape/cmd/make-3.79/tests/scripts/functions/filter-out 644 bootes sys 1367613436 1213 $description = "The following test creates a makefile to test static \n" ."pattern rules. Static pattern rules are rules which \n" ."specify multiple targets and construct the dependency \n" ."names for each target based on the target name. "; $details = "The makefile created in this test has three targets. The \n" ."filter command is used to get those target names ending in \n" .".o and statically creates a compile command with the target\n" ."name and the target name with .c. It also does the same thing\n" ."for another target filtered with .elc and creates a command\n" ."to emacs a .el file"; open(MAKEFILE,"> $makefile"); # The Contents of the MAKEFILE ... print MAKEFILE "files := \$(filter-out %.o, foo.elc bar.o lose.o) \n" ."all: \n" ."\t\@echo \$(files) \n"; # END of Contents of MAKEFILE close(MAKEFILE); &run_make_with_options($makefile, "", &get_logfile, 0); # Create the answer to what should be produced by this Makefile $answer = "foo.elc\n"; &compare_output($answer,&get_logfile(1)); 1; /sys/src/ape/cmd/make-3.79/tests/scripts/functions/findstring 644 bootes sys 1367613436 1177 $description = "The following test creates a makefile to test the findstring " ."function."; $details = ""; # IF YOU NEED >1 MAKEFILE FOR THIS TEST, USE &get_tmpfile; TO GET # THE NAME OF THE MAKEFILE. THIS INSURES CONSISTENCY AND KEEPS TRACK OF # HOW MANY MAKEFILES EXIST FOR EASY DELETION AT THE END. # EXAMPLE: $makefile2 = &get_tmpfile; open(MAKEFILE,"> $makefile"); # The Contents of the MAKEFILE ... print MAKEFILE "string := \$(findstring port, reporter)\n" ."all: \n" ."\t\@echo \$(string) \n"; # END of Contents of MAKEFILE close(MAKEFILE); &run_make_with_options($makefile, "", &get_logfile, 0); # Create the answer to what should be produced by this Makefile $answer = "port\n"; # COMPARE RESULTS # In this call to compare output, you should use the call &get_logfile(1) # to send the name of the last logfile created. You may also use # the special call &get_logfile(1) which returns the same as &get_logfile(1). &compare_output($answer,&get_logfile(1)); # This tells the test driver that the perl test script executed properly. 1; /sys/src/ape/cmd/make-3.79/tests/scripts/functions/foreach 644 bootes sys 1367613436 1725 # -*-perl-*- # Updated 6.16.93 variable "MAKE" is default was environment override # For make 3.63 and above $description = "The following test creates a makefile to verify test the foreach function."; $details = "This is a test of the foreach function in gnu make. This function starts with a space separated list of names and a variable. Each name in the list is subsituted into the variable and the given text evaluated. The general form of the command is $(foreach var,$list,$text). Several types of foreach loops are tested\n"; open(MAKEFILE,"> $makefile"); # The Contents of the MAKEFILE ... # On WIN32 systems, the user's path is found in %Path% ($Path) # $pathvar = (($port_type eq 'Windows') ? "Path" : "PATH"); print MAKEFILE < $makefile"); print MAKEFILE <1 MAKEFILE FOR THIS TEST, USE &get_tmpfile; TO GET # THE NAME OF THE MAKEFILE. THIS INSURES CONSISTENCY AND KEEPS TRACK OF # HOW MANY MAKEFILES EXIST FOR EASY DELETION AT THE END. # EXAMPLE: $makefile2 = &get_tmpfile; open(MAKEFILE,"> $makefile"); # The Contents of the MAKEFILE ... print MAKEFILE "string := \$(join a b c,foo hacks .pl1) \n" ."all: \n" ."\t\@echo \$(string) \n"; # END of Contents of MAKEFILE close(MAKEFILE); &run_make_with_options($makefile,"",&get_logfile,0); # Create the answer to what should be produced by this Makefile $answer = "afoo bhacks c.pl1\n"; # COMPARE RESULTS # In this call to compare output, you should use the call &get_logfile(1) # to send the name of the last logfile created. You may also use # the special call &get_logfile(1) which returns the same as &get_logfile(1). &compare_output($answer,&get_logfile(1)); # This tells the test driver that the perl test script executed properly. 1; /sys/src/ape/cmd/make-3.79/tests/scripts/functions/notdir 644 bootes sys 1367613436 1125 $description = "The following test creates a makefile to test the notdir " ."function."; $details = ""; # IF YOU NEED >1 MAKEFILE FOR THIS TEST, USE &get_tmpfile; TO GET # THE NAME OF THE MAKEFILE. THIS INSURES CONSISTENCY AND KEEPS TRACK OF # HOW MANY MAKEFILES EXIST FOR EASY DELETION AT THE END. # EXAMPLE: $makefile2 = &get_tmpfile; open(MAKEFILE,"> $makefile"); # The Contents of the MAKEFILE ... print MAKEFILE "string := \$(notdir ${pathsep}src${pathsep}foo.c hacks) \n" ."all: \n" ."\t\@echo \$(string) \n"; # END of Contents of MAKEFILE close(MAKEFILE); &run_make_with_options($makefile,"",&get_logfile,0); # Create the answer to what should be produced by this Makefile $answer = "foo.c hacks\n"; # COMPARE RESULTS # In this call to compare output, you should use the call &get_logfile(1) # to send the name of the last logfile created. You may also use # the special call &get_logfile(1) which returns the same as &get_logfile(1). &compare_output($answer,&get_logfile(1)); # This tells the test driver that the perl test script executed properly. 1; /sys/src/ape/cmd/make-3.79/tests/scripts/functions/origin 644 bootes sys 1367613436 1781 # -*-perl-*- $description = "Test the origin function."; $details = "This is a test of the origin function in gnu make. This function will report on where a variable was defined per the following list: 'undefined' never defined 'default' default definition 'environment' environment var without -e 'environment override' environment var with -e 'file' defined in makefile 'command line' defined on the command line 'override' defined by override in makefile 'automatic' Automatic variable\n"; # On WIN32 systems, HOME is meaningless. SystemRoot should be defined # though. With DJGPP, HOME is not guaranteed to be defined. Use DJDIR # instead. # $homevar = (($port_type eq 'Windows') ? "SystemRoot" : (($port_type eq 'DOS') ? "DJDIR" : "HOME")); open(MAKEFILE,"> $makefile"); print MAKEFILE < $makefile"); # The Contents of the MAKEFILE ... print MAKEFILE "foo := moon_light days \n" ."foo1:= jazz\n" ."bar := captured \n" ."bar2 = boy end, has rise A midnight \n" ."bar3:= \$(foo)\n" ."s1 := _by\n" ."s2 := _and_a\n" ."t1 := \$(addsuffix \$(s1), \$(bar) )\n" ."t2 := \$(addsuffix \$(s2), \$(foo1) )\n" ."t3 := \$(t2) \$(t2) \$(t2) \$(t2) \$(t2) \$(t2) \$(t2) \$(t2) \$(t2) \$(t2) \n" ."t4 := \$(t3) \$(t3) \$(t3) \$(t3) \$(t3) \$(t3) \$(t3) \$(t3) \$(t3) \$(t3) \n" ."t5 := \$(t4) \$(t4) \$(t4) \$(t4) \$(t4) \$(t4) \$(t4) \$(t4) \$(t4) \$(t4) \n" ."t6 := \$(t5) \$(t5) \$(t5) \$(t5) \$(t5) \$(t5) \$(t5) \$(t5) \$(t5) \$(t5) \n" ."t7 := \$(t6) \$(t6) \$(t6) \n" ."p1 := \$(addprefix \$(foo1), \$(s2) )\n" ."blank:= \n" ."all:\n" ."\t\@echo \$(sort \$(bar2) \$(foo) \$(addsuffix \$(s1), \$(bar) ) \$(t2) \$(bar2) \$(bar3))\n" ."\t\@echo \$(sort \$(blank) \$(foo) \$(bar2) \$(t1) \$(p1) )\n" ."\t\@echo \$(sort \$(foo) \$(bar2) \$(t1) \$(t4) \$(t5) \$(t7) \$(t6) )\n"; # END of Contents of MAKEFILE close(MAKEFILE); &run_make_with_options($makefile,"",&get_logfile); # Create the answer to what should be produced by this Makefile $answer = "A boy captured_by days end, has jazz_and_a midnight moon_light rise\n" ."A boy captured_by days end, has jazz_and_a midnight moon_light rise\n" ."A boy captured_by days end, has jazz_and_a midnight moon_light rise\n"; &compare_output($answer,&get_logfile(1)); 1; /sys/src/ape/cmd/make-3.79/tests/scripts/functions/strip 644 bootes sys 1367613436 1608 # -*-perl-*- $description = "The following test creates a makefile to verify the ability of make to strip white space from lists of object.\n"; $details = "The make file is built with a list of objects that contain white space These are then run through the strip command to remove it. This is then verified by echoing the result.\n"; open(MAKEFILE,"> $makefile"); # The Contents of the MAKEFILE ... print MAKEFILE <<'EOMAKE'; TEST1 := "Is this TERMINAL fun? What makes you believe is this terminal fun? JAPAN is a WONDERFUL planet -- I wonder if we will ever reach their level of COMPARATIVE SHOPPING..." E := TEST2 := $E try this and this $E define TEST3 and these test out some blank lines endef .PHONY: all all: @echo '$(strip $(TEST1) )' @echo '$(strip $(TEST2) )' @echo '$(strip $(TEST3) )' EOMAKE # END of Contents of MAKEFILE close(MAKEFILE); &run_make_with_options($makefile,"",&get_logfile); # Create the answer to what should be produced by this Makefile $answer = "\"Is this TERMINAL fun? What makes you believe is this terminal fun? JAPAN is a WONDERFUL planet -- I wonder if we will ever reach their level of COMPARATIVE SHOPPING...\" try this and this and these test out some blank lines "; &compare_output($answer,&get_logfile(1)); 1; /sys/src/ape/cmd/make-3.79/tests/scripts/functions/substitution 644 bootes sys 1367613436 752 $description = "The following test creates a makefile to ..."; $details = ""; open(MAKEFILE,"> $makefile"); # The Contents of the MAKEFILE ... print MAKEFILE "foo := a.o b.o c.o\n" ."bar := \$(foo:.o=.c)\n" ."bar2:= \$(foo:%.o=%.c)\n" ."bar3:= \$(patsubst %.c,%.o,x.c.c bar.c)\n" ."all:\n" ."\t\@echo \$(bar)\n" ."\t\@echo \$(bar2)\n" ."\t\@echo \$(bar3)\n"; # END of Contents of MAKEFILE close(MAKEFILE); &run_make_with_options($makefile,"",&get_logfile); # Create the answer to what should be produced by this Makefile $answer = "a.c b.c c.c\n" ."a.c b.c c.c\n" ."x.c.o bar.o\n"; &compare_output($answer,&get_logfile(1)); 1; /sys/src/ape/cmd/make-3.79/tests/scripts/functions/suffix 644 bootes sys 1367613436 2087 $description = "The following test creates a makefile to test the suffix\n" ."function. \n"; $details = "The suffix function will return the string following the last _._\n" ."the list provided. It will provide all of the unique suffixes found\n" ."in the list. The long strings are sorted to remove duplicates.\n"; # IF YOU NEED >1 MAKEFILE FOR THIS TEST, USE &get_tmpfile; TO GET # THE NAME OF THE MAKEFILE. THIS INSURES CONSISTENCY AND KEEPS TRACK OF # HOW MANY MAKEFILES EXIST FOR EASY DELETION AT THE END. # EXAMPLE: $makefile2 = &get_tmpfile; open(MAKEFILE,"> $makefile"); # The Contents of the MAKEFILE ... print MAKEFILE "string := word.pl general_test2.pl1 FORCE.pl word.pl3 generic_test.perl /tmp.c/bar foo.baz/bar.c MAKEFILES_variable.c\n" ."string2 := \$(string) \$(string) \$(string) \$(string) \$(string) \$(string) \$(string)\n" ."string3 := \$(string2) \$(string2) \$(string2) \$(string2) \$(string2) \$(string2) \$(string2)\n" ."string4 := \$(string3) \$(string3) \$(string3) \$(string3) \$(string3) \$(string3) \$(string3)\n" ."all: \n" ."\t\@echo \$(suffix \$(string)) \n" ."\t\@echo \$(sort \$(suffix \$(string4))) \n" ."\t\@echo \$(suffix \$(string) a.out) \n" ."\t\@echo \$(sort \$(suffix \$(string3))) \n"; # END of Contents of MAKEFILE close(MAKEFILE); &run_make_with_options($makefile,"",&get_logfile,0); # Create the answer to what should be produced by this Makefile # COMPARE RESULTS $answer = ".pl .pl1 .pl .pl3 .perl .c .c\n" .".c .perl .pl .pl1 .pl3\n" .".pl .pl1 .pl .pl3 .perl .c .c .out\n" .".c .perl .pl .pl1 .pl3\n"; # In this call to compare output, you should use the call &get_logfile(1) # to send the name of the last logfile created. You may also use # the special call &get_logfile(1) which returns the same as &get_logfile(1). &compare_output($answer,&get_logfile(1)); # This tells the test driver that the perl test script executed properly. 1; /sys/src/ape/cmd/make-3.79/tests/scripts/functions/warning 644 bootes sys 1367613436 1310 $description = "\ The following test creates a makefile to test the warning function."; $details = ""; open(MAKEFILE,"> $makefile"); print MAKEFILE <<'EOF'; ifdef WARNING1 $(warning warning is $(WARNING1)) endif ifdef WARNING2 $(warning warning is $(WARNING2)) endif ifdef WARNING3 all: some; @echo hi $(warning warning is $(WARNING3)) endif ifdef WARNING4 all: some; @echo hi @echo there $(warning warning is $(WARNING4)) endif some: ; @echo Some stuff EOF close(MAKEFILE); # Test #1 &run_make_with_options($makefile, "WARNING1=yes", &get_logfile, 0); $answer = "$makefile:2: warning is yes\nSome stuff\n"; &compare_output($answer,&get_logfile(1)); # Test #2 &run_make_with_options($makefile, "WARNING2=no", &get_logfile, 0); $answer = "$makefile:6: warning is no\nSome stuff\n"; &compare_output($answer,&get_logfile(1)); # Test #3 &run_make_with_options($makefile, "WARNING3=maybe", &get_logfile, 0); $answer = "Some stuff\n$makefile:10: warning is maybe\nhi\n"; &compare_output($answer,&get_logfile(1)); # Test #4 &run_make_with_options($makefile, "WARNING4=definitely", &get_logfile, 0); $answer = "Some stuff\n$makefile:14: warning is definitely\nhi\nthere\n"; &compare_output($answer,&get_logfile(1)); # This tells the test driver that the perl test script executed properly. 1; /sys/src/ape/cmd/make-3.79/tests/scripts/functions/wildcard 644 bootes sys 1367613436 2171 # -*-perl-*- $description = "The following test creates a makefile to test wildcard\n" ."expansions and the ability to put a command on the same\n" ."line as the target name separated by a semi-colon."; $details = "This test creates 4 files by the names of 1.example, \n" ."two.example and 3.example. We execute three tests. The first\n" ."executes the print1 target which tests the '*' wildcard by \n" ."echoing all filenames by the name of '*.example'. The second\n" ."test echo's all files which match '?.example' and \n" ."[a-z0-9].example. Lastly we clean up all of the files using\n" ."the '*' wildcard as in the first test"; if ($vos) { $delete_command = "delete_file -no_ask"; } else { $delete_command = "rm"; } open(MAKEFILE,"> $makefile"); # The Contents of the MAKEFILE ... print MAKEFILE < $makefile"); # The Contents of the MAKEFILE ... print MAKEFILE <<'EOF'; string := word.pl general_test2.pl FORCE.pl word.pl generic_test.perl MAKEFILES_variable.pl string2 := $(string) $(string) $(string) $(string) $(string) $(string) $(string) string3 := $(string2) $(string2) $(string2) $(string2) $(string2) $(string2) $(string2) string4 := $(string3) $(string3) $(string3) $(string3) $(string3) $(string3) $(string3) all: @echo $(words $(string)) @echo $(words $(string4)) @echo $(word 1, $(string)) @echo $(word 100, $(string)) @echo $(word 1, $(string)) @echo $(word 1000, $(string3)) @echo $(wordlist 3, 4, $(string)) @echo $(wordlist 4, 3, $(string)) @echo $(wordlist 1, 6, $(string)) @echo $(wordlist 5, 7, $(string)) @echo $(wordlist 100, 110, $(string)) @echo $(wordlist 7, 10, $(string2)) EOF # END of Contents of MAKEFILE close(MAKEFILE); &run_make_with_options($makefile, "", &get_logfile); # Create the answer to what should be produced by this Makefile # COMPARE RESULTS $answer = "6\n" ."2058\n" ."word.pl\n" ."\n" ."word.pl\n" ."\n" ."FORCE.pl word.pl\n" ."\n" ."word.pl general_test2.pl FORCE.pl word.pl generic_test.perl MAKEFILES_variable.pl\n" ."generic_test.perl MAKEFILES_variable.pl\n" ."\n" ."word.pl general_test2.pl FORCE.pl word.pl\n" ; # In this call to compare output, you should use the call &get_logfile(1) # to send the name of the last logfile created. You may also use # the special call &get_logfile(1) which returns the same as &get_logfile(1). &compare_output($answer, &get_logfile(1)); # This tells the test driver that the perl test script executed properly. 1; /sys/src/ape/cmd/make-3.79/tests/scripts/misc 20000000755 bootes sys 1367862839 0 /sys/src/ape/cmd/make-3.79/tests/scripts/misc/general1 644 bootes sys 1367613436 1534 # -*-perl-*- $description = "The following test creates a makefile to test the simple functionality of make. It mimics the rebuilding of a product with dependencies. It also tests the simple definition of VPATH."; open(MAKEFILE,"> $makefile"); print MAKEFILE < $makefile"); # The contents of the Makefile ... print MAKEFILE < $makefile"); # The contents of the Makefile ... print MAKEFILE < $makefile"); # The Contents of the MAKEFILE ... print MAKEFILE "all: \n"; # END of Contents of MAKEFILE close(MAKEFILE); &run_make_with_options($makefile,"-v",&get_logfile,0); # This tells the test driver that the perl test script executed properly. 1; /sys/src/ape/cmd/make-3.79/tests/scripts/options 20000000755 bootes sys 1367862839 0 /sys/src/ape/cmd/make-3.79/tests/scripts/options/dash-C 644 bootes sys 1367613436 1410 $description = "The following test creates a makefile to test the -C dir \n" ."option in make. This option tells make to change to \n" ."directory dir before reading the makefile."; $details = "This test is similar to the clean test except that this test\n" ."creates the file to delete in the work directory instead of\n" ."the current directory. Make is called from another directory\n" ."using the -C workdir option so that it can both find the \n" ."makefile and the file to delete in the work directory. "; $example = $workdir . $pathsep . "EXAMPLE_FILE"; open(MAKEFILE,"> $makefile"); # The Contents of the MAKEFILE ... print MAKEFILE "all: \n"; print MAKEFILE "\t\@echo This makefile did not clean the dir ... good\n"; print MAKEFILE "clean: \n"; print MAKEFILE "\t$delete_command EXAMPLE_FILE\n"; # END of Contents of MAKEFILE close(MAKEFILE); &touch($example); &run_make_with_options("${testname}.mk", "-C $workdir clean", &get_logfile); chdir $workdir; $wpath = &get_this_pwd; chdir $pwd; # Create the answer to what should be produced by this Makefile $answer = "$make_name: Entering directory `$wpath'\n" . "$delete_command EXAMPLE_FILE\n" . "$make_name: Leaving directory `$wpath'\n"; &compare_output($answer,&get_logfile(1)); if (-f $example) { $test_passed = 0; } 1; /sys/src/ape/cmd/make-3.79/tests/scripts/options/dash-I 644 bootes sys 1367613436 1536 $description ="The following test creates a makefile to test the -I option."; $details = "\ This test tests the -I option by including a filename in another directory and giving make that directory name under -I in the command line. Without this option, the make would fail to find the included file. It also checks to make sure that the -I option gets passed to recursive makes."; $makefile2 = &get_tmpfile; open(MAKEFILE,"> $makefile"); # The Contents of the MAKEFILE ... $mf2 = substr ($makefile2, index ($makefile2, $pathsep) + 1); print MAKEFILE < $makefile2"); print MAKEFILE < $makefile"); print MAKEFILE <<'EOF'; GOOGLE = bazzle all:; @echo "$(GOOGLE)" EOF close(MAKEFILE); &run_make_with_options($makefile, '-e' ,&get_logfile); $answer = "boggle\n"; &compare_output($answer,&get_logfile(1)); 1; /sys/src/ape/cmd/make-3.79/tests/scripts/options/dash-f 644 bootes sys 1367613436 2585 $description = "The following test tests that if you specify greater \n" ."than one '-f makefilename' on the command line, \n" ."that make concatenates them. This test creates three \n" ."makefiles and specifies all of them with the -f option \n" ."on the command line. To make sure they were concatenated, \n" ."we then call make with the rules from the concatenated \n" ."makefiles one at a time. Finally, it calls all three \n" ."rules in one call to make and checks that the output\n" ."is in the correct order."; $makefile2 = &get_tmpfile; $makefile3 = &get_tmpfile; open(MAKEFILE,"> $makefile"); # The Contents of the MAKEFILE ... print MAKEFILE "all: \n"; print MAKEFILE "\t\@echo This is the output from the original makefile\n"; # END of Contents of MAKEFILE close(MAKEFILE); # Create a second makefile open(MAKEFILE,"> $makefile2"); print MAKEFILE "TWO: \n"; print MAKEFILE "\t\@echo This is the output from makefile 2\n"; close(MAKEFILE); # Create a third makefile open(MAKEFILE,"> $makefile3"); print MAKEFILE "THREE: \n"; print MAKEFILE "\t\@echo This is the output from makefile 3\n"; close(MAKEFILE); # Create the answer to what should be produced by this Makefile $answer = "This is the output from the original makefile\n"; # Run make to catch the default rule &run_make_with_options($makefile,"-f $makefile2 -f $makefile3",&get_logfile,0); &compare_output($answer,&get_logfile(1)); # Run Make again with the rule from the second makefile: TWO $answer = "This is the output from makefile 2\n"; &run_make_with_options($makefile,"-f $makefile2 -f $makefile3 TWO",&get_logfile,0); &compare_output($answer,&get_logfile(1)); # Run Make again with the rule from the third makefile: THREE $answer = "This is the output from makefile 3\n"; &run_make_with_options($makefile, "-f $makefile2 -f $makefile3 THREE", &get_logfile, 0); &compare_output($answer,&get_logfile(1)); # Run Make again with ALL three rules in the order 2 1 3 to make sure # that all rules are executed in the proper order $answer = "This is the output from makefile 2\n"; $answer .= "This is the output from the original makefile\n"; $answer .= "This is the output from makefile 3\n"; &run_make_with_options($makefile, "-f $makefile2 -f $makefile3 TWO all THREE", &get_logfile, 0); &compare_output($answer,&get_logfile(1)); /sys/src/ape/cmd/make-3.79/tests/scripts/options/dash-k 644 bootes sys 1367613436 2474 # -*-perl-*- $description = "Test the make -k (don't stop on error) option.\n"; $details = "\ The makefile created in this test is a simulation of building a small product. However, the trick to this one is that one of the dependencies of the main target does not exist. Without the -k option, make would fail immediately and not build any part of the target. What we are looking for here, is that make builds the rest of the dependencies even though it knows that at the end it will fail to rebuild the main target."; open(MAKEFILE,"> $makefile"); # The Contents of the MAKEFILE ... print MAKEFILE < $makefile2"); print MAKEFILE <<'EOF'; .SUFFIXES: all: exe1 exe2; @echo making $@ exe1 exe2: lib; @echo cp $^ $@ lib: foo.o; @echo cp $^ $@ foo.o: ; exit 1 EOF close(MAKEFILE); &run_make_with_options($makefile2, "-k", &get_logfile, $error_code); $answer = "exit 1 $make_name: *** [foo.o] Error 1 $make_name: Target `all' not remade because of errors.\n"; &compare_output($answer, &get_logfile(1)); 1; /sys/src/ape/cmd/make-3.79/tests/scripts/options/dash-l 644 bootes sys 1367613436 1482 # -*-perl-*- # Date: Tue, 11 Aug 1992 09:34:26 -0400 # From: pds@lemming.webo.dg.com (Paul D. Smith) $description = "Test load balancing (-l) option."; $details = "\ This test creates a makefile where all depends on three rules which contain the same body. Each rule checks for the existence of a temporary file; if it exists an error is generated. If it doesn't exist then it is created, the rule sleeps, then deletes the temp file again. Thus if any of the rules are run in parallel the test will fail. When make is called in this test, it is given the -l option with a value of 0.0001. This ensures that the load will be above this number and make will therefore decide that it cannot run more than one job even though -j 4 was also specified on the command line."; open(MAKEFILE,"> $makefile"); # The Contents of the MAKEFILE ... print MAKEFILE <<'EOF'; SHELL = /bin/sh define test if [ ! -f test-file ]; then \ echo >> test-file; sleep 2; rm -f test-file; \ else \ echo $@ FAILED; \ fi endef all : ONE TWO THREE ONE : ; @$(test) TWO : ; @$(test) THREE : ; @$(test) EOF # END of Contents of MAKEFILE close(MAKEFILE); $mkoptions = "-l 0.0001"; $mkoptions .= " -j 4" if ($parallel_jobs); &run_make_with_options($makefile, $mkoptions, &get_logfile); $slurp = &read_file_into_string (&get_logfile(1)); if ($slurp !~ /cannot enforce load limit/) { &compare_output("", &get_logfile(1)); } 1; /sys/src/ape/cmd/make-3.79/tests/scripts/options/dash-n 644 bootes sys 1367613436 1750 # -*-perl-*- $description = "Test the -n option.\n"; $details = "Try various uses of -n and ensure they all give the correct results.\n"; open(MAKEFILE, "> $makefile"); # The Contents of the MAKEFILE ... print MAKEFILE <<'EOMAKE'; final: intermediate ; echo >> $@ intermediate: orig ; echo >> $@ EOMAKE close(MAKEFILE); &touch('orig'); # TEST 0 &run_make_with_options($makefile, "", &get_logfile); $answer = "echo >> intermediate\necho >> final\n"; &compare_output($answer, &get_logfile(1)); # TEST 1 &run_make_with_options($makefile, "-Worig -n", &get_logfile); $answer = "echo >> intermediate\necho >> final\n"; &compare_output($answer, &get_logfile(1)); unlink('orig', 'intermediate', 'final'); # We consider the actual updated timestamp of targets with all # recursive commands, even with -n. $makefile2 = &get_tmpfile; open(MAKEFILE, "> $makefile2"); print MAKEFILE <<'EOF'; .SUFFIXES: BAR = # nothing FOO = +$(BAR) a: b; echo > $@ b: c; $(FOO) EOF close(MAKEFILE); &touch('b'); # Sometimes, on my Solaris 2.5.1 box with a NetApp filesystem NFS-mounted, # just touching b first then a isn't good enough: the nsec field in the # stat result shows b is _newer_ than a once every 5 or 6 tries!!! I've # no idea what this is about, but that's why there's a sleep(1) here... sleep(1); &touch('a'); sleep(1); &touch('c'); # TEST 2 &run_make_with_options($makefile2, "", &get_logfile); $answer = "$make_name: `a' is up to date.\n"; &compare_output($answer, &get_logfile(1)); # TEST 3 &run_make_with_options($makefile2, "-n", &get_logfile); $answer = "$make_name: `a' is up to date.\n"; &compare_output($answer, &get_logfile(1)); unlink('a', 'b', 'c'); 1; /sys/src/ape/cmd/make-3.79/tests/scripts/targets 20000000755 bootes sys 1367862839 0 /sys/src/ape/cmd/make-3.79/tests/scripts/targets/DEFAULT 644 bootes sys 1367613436 1459 $description = "The following test creates a makefile to override part\n" ."of one Makefile with Another Makefile with the .DEFAULT\n" ."rule."; $details = "This tests the use of the .DEFAULT special target to say that \n" ."to remake any target that cannot be made fram the information\n" ."in the containing makefile, make should look in another makefile\n" ."This test gives this makefile the target bar which is not \n" ."defined here but passes the target bar on to another makefile\n" ."which does have the target bar defined.\n"; $makefile2 = &get_tmpfile; open(MAKEFILE,"> $makefile"); # The Contents of the MAKEFILE ... print MAKEFILE "foo:\n"; print MAKEFILE "\t\@echo Executing rule FOO\n\n"; print MAKEFILE ".DEFAULT:\n"; print MAKEFILE "\t\@\$(MAKE) -f $makefile2 \$\@ \n"; # END of Contents of MAKEFILE close(MAKEFILE); open(MAKEFILE,"> $makefile2"); print MAKEFILE "bar:\n"; print MAKEFILE "\t\@echo Executing rule BAR\n\n"; close(MAKEFILE); &run_make_with_options($makefile,'bar',&get_logfile); # Create the answer to what should be produced by this Makefile $answer = "${make_name}[1]: Entering directory `$pwd'\n" . "Executing rule BAR\n" . "${make_name}[1]: Leaving directory `$pwd'\n"; # COMPARE RESULTS &compare_output($answer,&get_logfile(1)); # This tells the test driver that the perl test script executed properly. 1; /sys/src/ape/cmd/make-3.79/tests/scripts/targets/FORCE 644 bootes sys 1367613436 839 $description = "The following tests rules without Commands or Dependencies."; $details = "If the rule ...\n"; if ($vos) { $delete_command = "delete_file"; } else { $delete_command = "rm"; } open(MAKEFILE,"> $makefile"); # The Contents of the MAKEFILE ... print MAKEFILE ".IGNORE :\n"; print MAKEFILE "clean: FORCE\n"; print MAKEFILE "\t$delete_command clean\n"; print MAKEFILE "FORCE:\n"; # END of Contents of MAKEFILE close(MAKEFILE); # Create a file named "clean". This is the same name as the target clean # and tricks the target into thinking that it is up to date. (Unless you # use the .PHONY target. &touch("clean"); $answer = "$delete_command clean\n"; &run_make_with_options($makefile,"clean",&get_logfile); &compare_output($answer,&get_logfile(1)); if (-f $example) { $test_passed = 0; } 1; /sys/src/ape/cmd/make-3.79/tests/scripts/targets/INTERMEDIATE 644 bootes sys 1367613436 2827 # -*-perl-*- $description = "Test the behaviour of the .INTERMEDIATE target."; $details = "\ Test the behavior of the .INTERMEDIATE special target. Create a makefile where a file would not normally be considered intermediate, then specify it as .INTERMEDIATE. Build and ensure it's deleted properly. Rebuild to ensure that it's not created if it doesn't exist but doesn't need to be built. Change the original and ensure that the intermediate file and the ultimate target are both rebuilt, and that the intermediate file is again deleted. Try this with implicit rules and explicit rules: both should work.\n"; open(MAKEFILE,"> $makefile"); print MAKEFILE <<'EOF'; .INTERMEDIATE: foo.e bar.e # Implicit rule test %.d : %.e ; cp $< $@ %.e : %.f ; cp $< $@ foo.d: foo.e # Explicit rule test foo.c: foo.e bar.e; cat $^ > $@ EOF close(MAKEFILE); # TEST #0 &touch('foo.f'); &touch('bar.f'); sleep($wtime); &run_make_with_options($makefile,'foo.d',&get_logfile); $answer = "cp foo.f foo.e\ncp foo.e foo.d\nrm foo.e\n"; &compare_output($answer, &get_logfile(1)); # TEST #1 &run_make_with_options($makefile,'foo.d',&get_logfile); $answer = "$make_name: `foo.d' is up to date.\n"; &compare_output($answer, &get_logfile(1)); # TEST #2 sleep($wtime); &touch('foo.f'); &run_make_with_options($makefile,'foo.d',&get_logfile); $answer = "cp foo.f foo.e\ncp foo.e foo.d\nrm foo.e\n"; &compare_output($answer, &get_logfile(1)); # TEST #3 &run_make_with_options($makefile,'foo.c',&get_logfile); $answer = "cp foo.f foo.e\ncp bar.f bar.e\ncat foo.e bar.e > foo.c\nrm foo.e bar.e\n"; &compare_output($answer, &get_logfile(1)); # TEST #4 &run_make_with_options($makefile,'foo.c',&get_logfile); $answer = "$make_name: `foo.c' is up to date.\n"; &compare_output($answer, &get_logfile(1)); # TEST #5 sleep($wtime); &touch('foo.f'); &run_make_with_options($makefile,'foo.c',&get_logfile); $answer = "cp foo.f foo.e\ncp bar.f bar.e\ncat foo.e bar.e > foo.c\nrm foo.e bar.e\n"; &compare_output($answer, &get_logfile(1)); # TEST #6 -- added for PR/1669: don't remove files mentioned on the cmd line. &run_make_with_options($makefile,'foo.e',&get_logfile); $answer = "cp foo.f foo.e\n"; &compare_output($answer, &get_logfile(1)); unlink('foo.f', 'foo.e', 'foo.d', 'foo.c', 'bar.f', 'bar.e', 'bar.d', 'bar.c'); # TEST #7 -- added for PR/1423 $makefile2 = &get_tmpfile; open(MAKEFILE, "> $makefile2"); print MAKEFILE <<'EOF'; all: foo foo.a: ; touch $@ %: %.a ; touch $@ .INTERMEDIATE: foo.a EOF close(MAKEFILE); &run_make_with_options($makefile2, '-R', &get_logfile); $answer = "touch foo.a\ntouch foo\nrm foo.a\n"; &compare_output($answer, &get_logfile(1)); unlink('foo'); # This tells the test driver that the perl test script executed properly. 1; /sys/src/ape/cmd/make-3.79/tests/scripts/targets/PHONY 644 bootes sys 1367613436 1427 $description = "The following tests the use of a PHONY target. It makes\n" ."sure that the rules under a target get executed even if\n" ."a filename of the same name of the target exists in the\n" ."directory.\n"; $details = "This makefile in this test declares the target clean to be a \n" ."PHONY target. We then create a file named \"clean\" in the \n" ."directory. Although this file exists, the rule under the target\n" ."clean should still execute because of it's phony status."; if ($vos) { $delete_command = "delete_file"; } else { $delete_command = "rm"; } $example = "EXAMPLE_FILE"; open(MAKEFILE,"> $makefile"); # The Contents of the MAKEFILE ... print MAKEFILE ".PHONY : clean \n"; print MAKEFILE "all: \n"; print MAKEFILE "\t\@echo This makefile did not clean the dir ... good\n"; print MAKEFILE "clean: \n"; print MAKEFILE "\t$delete_command $example clean\n"; # END of Contents of MAKEFILE close(MAKEFILE); &touch($example); # Create a file named "clean". This is the same name as the target clean # and tricks the target into thinking that it is up to date. (Unless you # use the .PHONY target. &touch("clean"); $answer = "$delete_command $example clean\n"; &run_make_with_options($makefile,"clean",&get_logfile); &compare_output($answer,&get_logfile(1)); if (-f $example) { $test_passed = 0; } 1; /sys/src/ape/cmd/make-3.79/tests/scripts/targets/SECONDARY 644 bootes sys 1367613436 2027 #! -*-perl-*- $description = "Test the behaviour of the .SECONDARY target."; $details = "\ Test the behavior of the .SECONDARY special target. Create a makefile where a file would not normally be considered intermediate, then specify it as .SECONDARY. Build and note that it's not automatically deleted. Delete the file. Rebuild to ensure that it's not created if it doesn't exist but doesn't need to be built. Change the original and ensure that the secondary file and the ultimate target are both rebuilt, and that the secondary file is not deleted. Try this with implicit rules and explicit rules: both should work.\n"; open(MAKEFILE,"> $makefile"); print MAKEFILE <<'EOF'; .SECONDARY: foo.e # Implicit rule test %.d : %.e ; cp $< $@ %.e : %.f ; cp $< $@ foo.d: foo.e # Explicit rule test foo.c: foo.e ; cp $< $@ EOF close(MAKEFILE); # TEST #1 &touch('foo.f'); &run_make_with_options($makefile,'foo.d',&get_logfile); $answer = "cp foo.f foo.e\ncp foo.e foo.d\n"; &compare_output($answer, &get_logfile(1)); # TEST #2 unlink('foo.e'); &run_make_with_options($makefile,'foo.d',&get_logfile); $answer = "$make_name: `foo.d' is up to date.\n"; &compare_output($answer, &get_logfile(1)); # TEST #3 sleep($wtime); &touch('foo.f'); &run_make_with_options($makefile,'foo.d',&get_logfile); $answer = "cp foo.f foo.e\ncp foo.e foo.d\n"; &compare_output($answer, &get_logfile(1)); # TEST #4 &run_make_with_options($makefile,'foo.c',&get_logfile); $answer = "cp foo.e foo.c\n"; &compare_output($answer, &get_logfile(1)); # TEST #5 unlink('foo.e'); &run_make_with_options($makefile,'foo.c',&get_logfile); $answer = "$make_name: `foo.c' is up to date.\n"; &compare_output($answer, &get_logfile(1)); # TEST #6 sleep($wtime); &touch('foo.f'); &run_make_with_options($makefile,'foo.c',&get_logfile); $answer = "cp foo.f foo.e\ncp foo.e foo.c\n"; &compare_output($answer, &get_logfile(1)); unlink('foo.f', 'foo.e', 'foo.d', 'foo.c'); # This tells the test driver that the perl test script executed properly. 1; /sys/src/ape/cmd/make-3.79/tests/scripts/targets/SILENT 644 bootes sys 1367613436 894 $description = "The following tests the special target .SILENT. By simply\n" ."mentioning this as a target, it tells make not to print\n" ."commands before executing them."; $details = "This test is the same as the clean test except that it should\n" ."not echo its command before deleting the specified file.\n"; if ($vos) { $delete_command = "delete_file"; } else { $delete_command = "rm"; } $example = "EXAMPLE_FILE"; open(MAKEFILE,"> $makefile"); # The Contents of the MAKEFILE ... print MAKEFILE ".SILENT : clean\n"; print MAKEFILE "clean: \n"; print MAKEFILE "\t$delete_command EXAMPLE_FILE\n"; # END of Contents of MAKEFILE close(MAKEFILE); &touch($example); $answer = ""; &run_make_with_options($makefile,"clean",&get_logfile,0); &compare_output($answer,&get_logfile(1)); if (-f $example) { $test_passed = 0; } 1; /sys/src/ape/cmd/make-3.79/tests/scripts/targets/clean 644 bootes sys 1367613436 1139 # -*-perl-*- $description = "The following test creates a makefile to delete a \n" ."file in the directory. It tests to see if make will \n" ."NOT execute the command unless the rule is given in \n" ."the make command line."; $example = "EXAMPLE_FILE"; open(MAKEFILE,"> $makefile"); # The Contents of the MAKEFILE ... print MAKEFILE "all: \n"; print MAKEFILE "\t\@echo This makefile did not clean the dir... good\n"; print MAKEFILE "clean: \n"; print MAKEFILE "\t$delete_command EXAMPLE_FILE\n"; # END of Contents of MAKEFILE close(MAKEFILE); &touch($example); &run_make_with_options($makefile,"",&get_logfile,0); # Create the answer to what should be produced by this Makefile $answer = "This makefile did not clean the dir... good\n"; &compare_output($answer,&get_logfile(1)) || &error ("abort"); $answer = "$delete_command $example\n"; &run_make_with_options($makefile,"clean",&get_logfile,0); &compare_output($answer,&get_logfile(1)) || &error ("abort"); if (-f $example) { $test_passed = 0; } 1; /sys/src/ape/cmd/make-3.79/tests/scripts/test_template 644 bootes sys 1367613436 2420 $description = "The following test creates a makefile to ... "; $details = ""; # IF YOU NEED >1 MAKEFILE FOR THIS TEST, USE &get_tmpfile; TO GET # THE NAME OF THE MAKEFILE. THIS INSURES CONSISTENCY AND KEEPS TRACK OF # HOW MANY MAKEFILES EXIST FOR EASY DELETION AT THE END. # EXAMPLE: $makefile2 = &get_tmpfile; open(MAKEFILE,"> $makefile"); # The Contents of the MAKEFILE ... print MAKEFILE " \n"; # END of Contents of MAKEFILE close(MAKEFILE); # Run make. You may specify a makefile, but if you don't want to, just # insert "" where $make_filename is now. You may also specify specific # options to run make with, but you also don't have to. (Insert "" where it # says ), The last field in this subroutine call # is the code which is returned from make. If you think that make should # execute with no errors, you may OPTIONALLY put 0; Otherwise put the # error code that you expect back from make for this test. # Every time you run make, you just need to say &get_logfile and that # subroutine will get a new logfile name for you in incrementing order # according to how many times you call it within ONE test. It is # reset to 0 at the beginning of every new test script. &run_make_with_options($makefile, "", &get_logfile, 0); # THE REST OF THIS FILE DEPENDS HIGHLY ON WHAT KIND OF TEST YOU ARE # CREATING, SO IT WILL VARY. BASICALLY, YOU MAY INSERT ANYTHING YOU # WISH AT THIS POINT TO SEE IF THE TEST WORKED OK. IF THERE ARE # ADDITIONAL TESTS BESIDES &compare_output, AND IT FAILES, YOU # MUST *** SET $test_passed = 0 !!! *** # Create the answer to what should be produced by this Makefile $answer = ""; # COMPARE RESULTS # In this call to compare output, you should use the call &get_logfile(1) # to send the name of the last logfile created. You may also use # the special call &get_logfile(1) which returns the same as &get_logfile(1). &compare_output($answer,&get_logfile(1)); # If you wish to &error ("abort ") if the compare fails, then add a "|| &error ("abort ")" to the # end of the previous line. # This tells the test driver that the perl test script executed properly. 1; /sys/src/ape/cmd/make-3.79/tests/scripts/variables 20000000755 bootes sys 1367862839 0 /sys/src/ape/cmd/make-3.79/tests/scripts/variables/CURDIR 644 bootes sys 1367613436 447 # -*-perl-*- $description = "This tests the CURDIR varaible."; $details = "Echo CURDIR both with and without -C. Also ensure overrides work."; open(MAKEFILE,"> $makefile"); print MAKEFILE "all: ; \@echo \$(CURDIR)\n"; close(MAKEFILE); # TEST #1 # ------- &run_make_with_options($makefile,"",&get_logfile); $answer = "$pwd\n"; &compare_output($answer,&get_logfile(1)); 1; /sys/src/ape/cmd/make-3.79/tests/scripts/variables/MAKE 644 bootes sys 1367613436 871 $description = "The following test creates a makefile to test MAKE \n" ."(very generic)"; $details = "DETAILS"; open(MAKEFILE,"> $makefile"); # The Contents of the MAKEFILE ... print MAKEFILE "TMP := \$(MAKE)\n"; print MAKEFILE "MAKE := \$(subst X=\$(X),,\$(MAKE))\n\n"; print MAKEFILE "all:\n"; print MAKEFILE "\t\@echo \$(TMP)\n"; print MAKEFILE "\t\$(MAKE) -f $makefile foo\n\n"; print MAKEFILE "foo:\n"; print MAKEFILE "\t\@echo \$(MAKE)\n"; # END of Contents of MAKEFILE close(MAKEFILE); # Create the answer to what should be produced by this Makefile $answer = "$mkpath\n$mkpath -f $makefile foo\n" . "${make_name}[1]: Entering directory `$pwd'\n" . "$mkpath\n${make_name}[1]: Leaving directory `$pwd'\n"; &run_make_with_options($makefile,"",&get_logfile,0); &delete("foo"); # COMPARE RESULTS &compare_output($answer,&get_logfile(1)); 1; /sys/src/ape/cmd/make-3.79/tests/scripts/variables/MAKECMDGOALS 644 bootes sys 1367613436 1130 # -*-perl-*- $description = "Test the MAKECMDGOALS variable."; $details = "\ We construct a makefile with various targets, all of which print out \$(MAKECMDGOALS), then call it different ways."; open(MAKEFILE,"> $makefile"); print MAKEFILE "\ .DEFAULT all: \@echo \$(MAKECMDGOALS) "; close(MAKEFILE); # TEST #1 &run_make_with_options($makefile, "", &get_logfile, 0); $answer = "\n"; &compare_output($answer,&get_logfile(1)); # TEST #2 &run_make_with_options($makefile, "all", &get_logfile, 0); $answer = "all\n"; &compare_output($answer,&get_logfile(1)); # TEST #3 &run_make_with_options($makefile, "foo bar baz yaz", &get_logfile, 0); $answer = "foo bar baz yaz\nfoo bar baz yaz\nfoo bar baz yaz\nfoo bar baz yaz\n"; &compare_output($answer,&get_logfile(1)); # This tells the test driver that the perl test script executed properly. 1; /sys/src/ape/cmd/make-3.79/tests/scripts/variables/MAKEFILES 644 bootes sys 1367613436 739 # -*-perl-*- $description = "Test the MAKEFILES variable."; $makefile2 = &get_tmpfile; $makefile3 = &get_tmpfile; open(MAKEFILE,"> $makefile"); print MAKEFILE 'all: ; @echo DEFAULT RULE: M2=$(M2) M3=$(M3)', "\n"; close(MAKEFILE); open(MAKEFILE,"> $makefile2"); print MAKEFILE < $makefile3"); print MAKEFILE < $makefile"); # The Contents of the MAKEFILE ... print MAKEFILE < $makefile"); # The Contents of the MAKEFILE ... print MAKEFILE <<'EOF'; foo = $(bar) bar = ${ugh} ugh = Hello all: multi ; @echo $(foo) multi: ; $(multi) x := foo y := $(x) bar x := later nullstring := space := $(nullstring) $(nullstring) next: ; @echo $x$(space)$y define multi @echo hi @echo there endef ifdef BOGUS define @echo error endef endif EOF # END of Contents of MAKEFILE close(MAKEFILE); # TEST #1 # ------- &run_make_with_options($makefile, "", &get_logfile); $answer = "hi\nthere\nHello\n"; &compare_output($answer, &get_logfile(1)); # TEST #2 # ------- &run_make_with_options($makefile, "next", &get_logfile); $answer = "later foo bar\n"; &compare_output($answer, &get_logfile(1)); # TEST #3 # ------- &run_make_with_options($makefile, "BOGUS=true", &get_logfile, 512); $answer = "$makefile:23: *** empty variable name. Stop.\n"; &compare_output($answer, &get_logfile(1)); 1; /sys/src/ape/cmd/make-3.79/w32 20000000775 bootes sys 1367862839 0 /sys/src/ape/cmd/make-3.79/w32/compat 20000000755 bootes sys 1367862839 0 /sys/src/ape/cmd/make-3.79/w32/compat/dirent.c 644 bootes sys 1367613436 3795 #include #include #include #include #include #include "dirent.h" DIR* opendir(const char* pDirName) { struct stat sb; DIR* pDir; char* pEndDirName; int nBufferLen; /* sanity checks */ if (!pDirName) { errno = EINVAL; return NULL; } if (stat(pDirName, &sb) != 0) { errno = ENOENT; return NULL; } if ((sb.st_mode & S_IFMT) != S_IFDIR) { errno = ENOTDIR; return NULL; } /* allocate a DIR structure to return */ pDir = (DIR *) malloc(sizeof (DIR)); if (!pDir) return NULL; /* input directory name length */ nBufferLen = strlen(pDirName); /* copy input directory name to DIR buffer */ strcpy(pDir->dir_pDirectoryName, pDirName); /* point to end of the copied directory name */ pEndDirName = &pDir->dir_pDirectoryName[nBufferLen - 1]; /* if directory name did not end in '/' or '\', add '/' */ if ((*pEndDirName != '/') && (*pEndDirName != '\\')) { pEndDirName++; *pEndDirName = '/'; } /* now append the wildcard character to the buffer */ pEndDirName++; *pEndDirName = '*'; pEndDirName++; *pEndDirName = '\0'; /* other values defaulted */ pDir->dir_nNumFiles = 0; pDir->dir_hDirHandle = INVALID_HANDLE_VALUE; pDir->dir_ulCookie = __DIRENT_COOKIE; return pDir; } void closedir(DIR *pDir) { /* got a valid pointer? */ if (!pDir) { errno = EINVAL; return; } /* sanity check that this is a DIR pointer */ if (pDir->dir_ulCookie != __DIRENT_COOKIE) { errno = EINVAL; return; } /* close the WINDOWS32 directory handle */ if (pDir->dir_hDirHandle != INVALID_HANDLE_VALUE) FindClose(pDir->dir_hDirHandle); free(pDir); return; } struct dirent * readdir(DIR* pDir) { WIN32_FIND_DATA wfdFindData; if (!pDir) { errno = EINVAL; return NULL; } /* sanity check that this is a DIR pointer */ if (pDir->dir_ulCookie != __DIRENT_COOKIE) { errno = EINVAL; return NULL; } if (pDir->dir_nNumFiles == 0) { pDir->dir_hDirHandle = FindFirstFile(pDir->dir_pDirectoryName, &wfdFindData); if (pDir->dir_hDirHandle == INVALID_HANDLE_VALUE) return NULL; } else if (!FindNextFile(pDir->dir_hDirHandle, &wfdFindData)) return NULL; /* bump count for next call to readdir() or telldir() */ pDir->dir_nNumFiles++; /* fill in struct dirent values */ pDir->dir_sdReturn.d_ino = -1; strcpy(pDir->dir_sdReturn.d_name, wfdFindData.cFileName); return &pDir->dir_sdReturn; } void rewinddir(DIR* pDir) { if (!pDir) { errno = EINVAL; return; } /* sanity check that this is a DIR pointer */ if (pDir->dir_ulCookie != __DIRENT_COOKIE) { errno = EINVAL; return; } /* close the WINDOWS32 directory handle */ if (pDir->dir_hDirHandle != INVALID_HANDLE_VALUE) if (!FindClose(pDir->dir_hDirHandle)) errno = EBADF; /* reset members which control readdir() */ pDir->dir_hDirHandle = INVALID_HANDLE_VALUE; pDir->dir_nNumFiles = 0; return; } int telldir(DIR* pDir) { if (!pDir) { errno = EINVAL; return -1; } /* sanity check that this is a DIR pointer */ if (pDir->dir_ulCookie != __DIRENT_COOKIE) { errno = EINVAL; return -1; } /* return number of times readdir() called */ return pDir->dir_nNumFiles; } void seekdir(DIR* pDir, long nPosition) { if (!pDir) return; /* sanity check that this is a DIR pointer */ if (pDir->dir_ulCookie != __DIRENT_COOKIE) return; /* go back to beginning of directory */ rewinddir(pDir); /* loop until we have found position we care about */ for (--nPosition; nPosition && readdir(pDir); nPosition--); /* flag invalid nPosition value */ if (nPosition) errno = EINVAL; return; } /sys/src/ape/cmd/make-3.79/w32/include 20000000755 bootes sys 1367862839 0 /sys/src/ape/cmd/make-3.79/w32/include/dirent.h 644 bootes sys 1367613436 678 #ifndef _DIRENT_H #define _DIRENT_H #include #include #include #include #ifndef NAME_MAX #define NAME_MAX 255 #endif #define __DIRENT_COOKIE 0xfefeabab struct dirent { ino_t d_ino; /* unused - no equivalent on WINDOWS32 */ char d_name[NAME_MAX+1]; }; typedef struct dir_struct { ULONG dir_ulCookie; HANDLE dir_hDirHandle; DWORD dir_nNumFiles; char dir_pDirectoryName[NAME_MAX+1]; struct dirent dir_sdReturn; } DIR; DIR *opendir(const char *); struct dirent *readdir(DIR *); void rewinddir(DIR *); void closedir(DIR *); int telldir(DIR *); void seekdir(DIR *, long); #endif /sys/src/ape/cmd/make-3.79/w32/include/pathstuff.h 644 bootes sys 1367613436 280 #ifndef _PATHSTUFF_H #define _PATHSTUFF_H extern char * convert_Path_to_windows32(char *Path, char to_delim); extern char * convert_vpath_to_windows32(char *Path, char to_delim); extern char * w32ify(char *file, int resolve); extern char * getcwd_fs(char *buf, int len); #endif /sys/src/ape/cmd/make-3.79/w32/include/sub_proc.h 644 bootes sys 1367613436 1540 #ifndef SUB_PROC_H #define SUB_PROC_H /* * Component Name: * * $Date: 1997/08/27 20:34:23 $ * * $Source: /home/cvs/make/w32/include/sub_proc.h,v $ * * $Revision: 1.4 $ */ /* $Id: sub_proc.h,v 1.4 1997/08/27 20:34:23 psmith Exp $ */ #ifdef WINDOWS32 #define EXTERN_DECL(entry, args) extern entry args #define VOID_DECL void EXTERN_DECL(HANDLE process_init, (VOID_DECL)); EXTERN_DECL(HANDLE process_init_fd, (HANDLE stdinh, HANDLE stdouth, HANDLE stderrh)); EXTERN_DECL(long process_begin, (HANDLE proc, char **argv, char **envp, char *exec_path, char *as_user)); EXTERN_DECL(long process_pipe_io, (HANDLE proc, char *stdin_data, int stdin_data_len)); EXTERN_DECL(long process_file_io, (HANDLE proc)); EXTERN_DECL(void process_cleanup, (HANDLE proc)); EXTERN_DECL(HANDLE process_wait_for_any, (VOID_DECL)); EXTERN_DECL(void process_register, (HANDLE proc)); EXTERN_DECL(HANDLE process_easy, (char** argv, char** env)); EXTERN_DECL(BOOL process_kill, (HANDLE proc, int signal)); /* support routines */ EXTERN_DECL(long process_errno, (HANDLE proc)); EXTERN_DECL(long process_last_err, (HANDLE proc)); EXTERN_DECL(long process_exit_code, (HANDLE proc)); EXTERN_DECL(long process_signal, (HANDLE proc)); EXTERN_DECL(char * process_outbuf, (HANDLE proc)); EXTERN_DECL(char * process_errbuf, (HANDLE proc)); EXTERN_DECL(int process_outcnt, (HANDLE proc)); EXTERN_DECL(int process_errcnt, (HANDLE proc)); EXTERN_DECL(void process_pipes, (HANDLE proc, int pipes[3])); #endif #endif /sys/src/ape/cmd/make-3.79/w32/include/w32err.h 644 bootes sys 1367613436 212 #ifndef _W32ERR_H_ #define _W32ERR_H_ #ifndef EXTERN_DECL #define EXTERN_DECL(entry, args) entry args #endif EXTERN_DECL(char * map_windows32_error_to_string, (DWORD error)); #endif /* !_W32ERR_H */ /sys/src/ape/cmd/make-3.79/w32/subproc 20000000755 bootes sys 1367862839 0 /sys/src/ape/cmd/make-3.79/w32/subproc/NMakefile 644 bootes sys 1367613436 1935 # NOTE: If you have no `make' program at all to process this makefile, run # `build.bat' instead. # # Copyright (C) 1988,89,91,92,93,94,95,96,97 Free Software Foundation, Inc # This file is part of GNU Make. # # GNU Make 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. # # GNU Make 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 GNU Make; see the file COPYING. If not, write to # the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. # # NMakefile for GNU Make (subproc library) # LIB = lib CC = cl OUTDIR=. MAKEFILE=NMakefile CFLAGS_any = /nologo /MT /W3 /GX /Z7 /YX /D WIN32 /D WINDOWS32 /D _WINDOWS -I. -I../include -I../../ CFLAGS_debug = $(CFLAGS_any) /Od /D _DEBUG /FR.\WinDebug\ /Fp.\WinDebug\subproc.pch /Fo.\WinDebug/ CFLAGS_release = $(CFLAGS_any) /O2 /FR.\WinRel\ /Fp.\WinRel\subproc.pch /Fo.\WinRel/ all: Release Debug Release: $(MAKE) /f $(MAKEFILE) OUTDIR=WinRel CFLAGS="$(CFLAGS_release)" WinRel/subproc.lib Debug: $(MAKE) /f $(MAKEFILE) OUTDIR=WinDebug CFLAGS="$(CFLAGS_debug)" WinDebug/subproc.lib clean: rmdir /s /q WinRel WinDebug erase *.pdb $(OUTDIR): if not exist .\$@\nul mkdir .\$@ OBJS = $(OUTDIR)/misc.obj $(OUTDIR)/w32err.obj $(OUTDIR)/sub_proc.obj $(OUTDIR)/subproc.lib: $(OUTDIR) $(OBJS) $(LIB) -out:$@ @<< $(OBJS) << .c{$(OUTDIR)}.obj: $(CC) $(CFLAGS) /c $< $(OUTDIR)/misc.obj: misc.c proc.h $(OUTDIR)/sub_proc.obj: sub_proc.c ../include/sub_proc.h ../include/w32err.h proc.h $(OUTDIR)/w32err.obj: w32err.c ../include/w32err.h /sys/src/ape/cmd/make-3.79/w32/subproc/build.bat 644 bootes sys 1367613436 1325 if not exist .\WinDebug\nul mkdir .\WinDebug cl.exe /nologo /MT /W3 /GX /Z7 /YX /Od /I .. /I . /I ../include /D WIN32 /D WINDOWS32 /D _DEBUG /D _WINDOWS /FR.\WinDebug/ /Fp.\WinDebug/subproc.pch /Fo.\WinDebug/ /c misc.c cl.exe /nologo /MT /W3 /GX /Z7 /YX /Od /I .. /I . /I ../include /I ../.. /D WIN32 /D WINDOWS32 /D _DEBUG /D _WINDOWS /FR.\WinDebug/ /Fp.\WinDebug/subproc.pch /Fo.\WinDebug/ /c sub_proc.c cl.exe /nologo /MT /W3 /GX /Z7 /YX /Od /I .. /I . /I ../include /D WIN32 /D WINDOWS32 /D _DEBUG /D _WINDOWS /FR.\WinDebug/ /Fp.\WinDebug/subproc.pch /Fo.\WinDebug/ /c w32err.c lib.exe /NOLOGO /OUT:.\WinDebug\subproc.lib .\WinDebug/misc.obj .\WinDebug/sub_proc.obj .\WinDebug/w32err.obj if not exist .\WinRel\nul mkdir .\WinRel cl.exe /nologo /MT /W3 /GX /YX /O2 /I ../include /D WIN32 /D WINDOWS32 /D NDEBUG /D _WINDOWS /FR.\WinRel/ /Fp.\WinRel/subproc.pch /Fo.\WinRel/ /c misc.c cl.exe /nologo /MT /W3 /GX /YX /O2 /I ../include /I ../.. /D WIN32 /D WINDOWS32 /D NDEBUG /D _WINDOWS /FR.\WinRel/ /Fp.\WinRel/subproc.pch /Fo.\WinRel/ /c sub_proc.c cl.exe /nologo /MT /W3 /GX /YX /O2 /I ../include /D WIN32 /D WINDOWS32 /D NDEBUG /D _WINDOWS /FR.\WinRel/ /Fp.\WinRel/subproc.pch /Fo.\WinRel/ /c w32err.c lib.exe /NOLOGO /OUT:.\WinRel\subproc.lib .\WinRel/misc.obj .\WinRel/sub_proc.obj .\WinRel/w32err.obj /sys/src/ape/cmd/make-3.79/w32/subproc/misc.c 644 bootes sys 1367613436 1311 #include #include #include #include #include "proc.h" /* * Description: Convert a NULL string terminated UNIX environment block to * an environment block suitable for a windows32 system call * * Returns: TRUE= success, FALSE=fail * * Notes/Dependencies: the environment block is sorted in case-insensitive * order, is double-null terminated, and is a char *, not a char ** */ int _cdecl compare(const void *a1, const void *a2) { return _stricoll(*((char**)a1),*((char**)a2)); } bool_t arr2envblk(char **arr, char **envblk_out) { char **tmp; int size_needed; int arrcnt; char *ptr; arrcnt = 0; while (arr[arrcnt]) { arrcnt++; } tmp = (char**) calloc(arrcnt + 1, sizeof(char *)); if (!tmp) { return FALSE; } arrcnt = 0; size_needed = 0; while (arr[arrcnt]) { tmp[arrcnt] = arr[arrcnt]; size_needed += strlen(arr[arrcnt]) + 1; arrcnt++; } size_needed++; qsort((void *) tmp, (size_t) arrcnt, sizeof (char*), compare); ptr = *envblk_out = calloc(size_needed, 1); if (!ptr) { free(tmp); return FALSE; } arrcnt = 0; while (tmp[arrcnt]) { strcpy(ptr, tmp[arrcnt]); ptr += strlen(tmp[arrcnt]) + 1; arrcnt++; } free(tmp); return TRUE; } /sys/src/ape/cmd/make-3.79/w32/subproc/proc.h 644 bootes sys 1367613436 214 #ifndef _PROC_H #define _PROC_H typedef int bool_t; #define E_SCALL 101 #define E_IO 102 #define E_NO_MEM 103 #define E_FORK 104 extern bool_t arr2envblk(char **arr, char **envblk_out); #endif /sys/src/ape/cmd/make-3.79/w32/subproc/sub_proc.c 644 bootes sys 1367613436 27121 #include #include #include /* for msvc _beginthreadex, _endthreadex */ #include #include "sub_proc.h" #include "proc.h" #include "w32err.h" #include "config.h" static char *make_command_line(char *shell_name, char *exec_path, char **argv); extern int debug_flag; /* from make */ typedef struct sub_process_t { int sv_stdin[2]; int sv_stdout[2]; int sv_stderr[2]; int using_pipes; char *inp; DWORD incnt; char * volatile outp; volatile DWORD outcnt; char * volatile errp; volatile DWORD errcnt; int pid; int exit_code; int signal; long last_err; long lerrno; } sub_process; /* keep track of children so we can implement a waitpid-like routine */ static sub_process *proc_array[256]; static int proc_index = 0; static int fake_exits_pending = 0; /* * When a process has been waited for, adjust the wait state * array so that we don't wait for it again */ static void process_adjust_wait_state(sub_process* pproc) { int i; if (!proc_index) return; for (i = 0; i < proc_index; i++) if (proc_array[i]->pid == pproc->pid) break; if (i < proc_index) { proc_index--; if (i != proc_index) memmove(&proc_array[i], &proc_array[i+1], (proc_index-i) * sizeof(sub_process*)); proc_array[proc_index] = NULL; } } /* * Waits for any of the registered child processes to finish. */ static sub_process * process_wait_for_any_private(void) { HANDLE handles[256]; DWORD retval, which; int i; if (!proc_index) return NULL; /* build array of handles to wait for */ for (i = 0; i < proc_index; i++) { handles[i] = (HANDLE) proc_array[i]->pid; if (fake_exits_pending && proc_array[i]->exit_code) break; } /* wait for someone to exit */ if (!fake_exits_pending) { retval = WaitForMultipleObjects(proc_index, handles, FALSE, INFINITE); which = retval - WAIT_OBJECT_0; } else { fake_exits_pending--; retval = !WAIT_FAILED; which = i; } /* return pointer to process */ if (retval != WAIT_FAILED) { sub_process* pproc = proc_array[which]; process_adjust_wait_state(pproc); return pproc; } else return NULL; } /* * Terminate a process. */ BOOL process_kill(HANDLE proc, int signal) { sub_process* pproc = (sub_process*) proc; pproc->signal = signal; return (TerminateProcess((HANDLE) pproc->pid, signal)); } /* * Use this function to register processes you wish to wait for by * calling process_file_io(NULL) or process_wait_any(). This must be done * because it is possible for callers of this library to reuse the same * handle for multiple processes launches :-( */ void process_register(HANDLE proc) { proc_array[proc_index++] = (sub_process *) proc; } /* * Public function which works kind of like waitpid(). Wait for any * of the children to die and return results. To call this function, * you must do 1 of things: * * x = process_easy(...); * * or * * x = process_init_fd(); * process_register(x); * * or * * x = process_init(); * process_register(x); * * You must NOT then call process_pipe_io() because this function is * not capable of handling automatic notification of any child * death. */ HANDLE process_wait_for_any(void) { sub_process* pproc = process_wait_for_any_private(); if (!pproc) return NULL; else { /* * Ouch! can't tell caller if this fails directly. Caller * will have to use process_last_err() */ (void) process_file_io(pproc); return ((HANDLE) pproc); } } long process_errno(HANDLE proc) { return (((sub_process *)proc)->lerrno); } long process_signal(HANDLE proc) { return (((sub_process *)proc)->signal); } long process_last_err(HANDLE proc) { return (((sub_process *)proc)->last_err); } long process_exit_code(HANDLE proc) { return (((sub_process *)proc)->exit_code); } char * process_outbuf(HANDLE proc) { return (((sub_process *)proc)->outp); } char * process_errbuf(HANDLE proc) { return (((sub_process *)proc)->errp); } int process_outcnt(HANDLE proc) { return (((sub_process *)proc)->outcnt); } int process_errcnt(HANDLE proc) { return (((sub_process *)proc)->errcnt); } void process_pipes(HANDLE proc, int pipes[3]) { pipes[0] = ((sub_process *)proc)->sv_stdin[0]; pipes[1] = ((sub_process *)proc)->sv_stdout[0]; pipes[2] = ((sub_process *)proc)->sv_stderr[0]; return; } HANDLE process_init() { sub_process *pproc; /* * open file descriptors for attaching stdin/stdout/sterr */ HANDLE stdin_pipes[2]; HANDLE stdout_pipes[2]; HANDLE stderr_pipes[2]; SECURITY_ATTRIBUTES inherit; BYTE sd[SECURITY_DESCRIPTOR_MIN_LENGTH]; pproc = malloc(sizeof(*pproc)); memset(pproc, 0, sizeof(*pproc)); /* We can't use NULL for lpSecurityDescriptor because that uses the default security descriptor of the calling process. Instead we use a security descriptor with no DACL. This allows nonrestricted access to the associated objects. */ if (!InitializeSecurityDescriptor((PSECURITY_DESCRIPTOR)(&sd), SECURITY_DESCRIPTOR_REVISION)) { pproc->last_err = GetLastError(); pproc->lerrno = E_SCALL; return((HANDLE)pproc); } inherit.nLength = sizeof(inherit); inherit.lpSecurityDescriptor = (PSECURITY_DESCRIPTOR)(&sd); inherit.bInheritHandle = TRUE; // By convention, parent gets pipe[0], and child gets pipe[1] // This means the READ side of stdin pipe goes into pipe[1] // and the WRITE side of the stdout and stderr pipes go into pipe[1] if (CreatePipe( &stdin_pipes[1], &stdin_pipes[0], &inherit, 0) == FALSE || CreatePipe( &stdout_pipes[0], &stdout_pipes[1], &inherit, 0) == FALSE || CreatePipe( &stderr_pipes[0], &stderr_pipes[1], &inherit, 0) == FALSE) { pproc->last_err = GetLastError(); pproc->lerrno = E_SCALL; return((HANDLE)pproc); } // // Mark the parent sides of the pipes as non-inheritable // if (SetHandleInformation(stdin_pipes[0], HANDLE_FLAG_INHERIT, 0) == FALSE || SetHandleInformation(stdout_pipes[0], HANDLE_FLAG_INHERIT, 0) == FALSE || SetHandleInformation(stderr_pipes[0], HANDLE_FLAG_INHERIT, 0) == FALSE) { pproc->last_err = GetLastError(); pproc->lerrno = E_SCALL; return((HANDLE)pproc); } pproc->sv_stdin[0] = (int) stdin_pipes[0]; pproc->sv_stdin[1] = (int) stdin_pipes[1]; pproc->sv_stdout[0] = (int) stdout_pipes[0]; pproc->sv_stdout[1] = (int) stdout_pipes[1]; pproc->sv_stderr[0] = (int) stderr_pipes[0]; pproc->sv_stderr[1] = (int) stderr_pipes[1]; pproc->using_pipes = 1; pproc->lerrno = 0; return((HANDLE)pproc); } HANDLE process_init_fd(HANDLE stdinh, HANDLE stdouth, HANDLE stderrh) { sub_process *pproc; pproc = malloc(sizeof(*pproc)); memset(pproc, 0, sizeof(*pproc)); /* * Just pass the provided file handles to the 'child side' of the * pipe, bypassing pipes altogether. */ pproc->sv_stdin[1] = (int) stdinh; pproc->sv_stdout[1] = (int) stdouth; pproc->sv_stderr[1] = (int) stderrh; pproc->last_err = pproc->lerrno = 0; return((HANDLE)pproc); } static HANDLE find_file(char *exec_path, LPOFSTRUCT file_info) { HANDLE exec_handle; char *fname; char *ext; fname = malloc(strlen(exec_path) + 5); strcpy(fname, exec_path); ext = fname + strlen(fname); strcpy(ext, ".exe"); if ((exec_handle = (HANDLE)OpenFile(fname, file_info, OF_READ | OF_SHARE_COMPAT)) != (HANDLE)HFILE_ERROR) { free(fname); return(exec_handle); } strcpy(ext, ".cmd"); if ((exec_handle = (HANDLE)OpenFile(fname, file_info, OF_READ | OF_SHARE_COMPAT)) != (HANDLE)HFILE_ERROR) { free(fname); return(exec_handle); } strcpy(ext, ".bat"); if ((exec_handle = (HANDLE)OpenFile(fname, file_info, OF_READ | OF_SHARE_COMPAT)) != (HANDLE)HFILE_ERROR) { free(fname); return(exec_handle); } /* should .com come before this case? */ if ((exec_handle = (HANDLE)OpenFile(exec_path, file_info, OF_READ | OF_SHARE_COMPAT)) != (HANDLE)HFILE_ERROR) { free(fname); return(exec_handle); } strcpy(ext, ".com"); if ((exec_handle = (HANDLE)OpenFile(fname, file_info, OF_READ | OF_SHARE_COMPAT)) != (HANDLE)HFILE_ERROR) { free(fname); return(exec_handle); } free(fname); return(exec_handle); } /* * Description: Create the child process to be helped * * Returns: * * Notes/Dependencies: */ long process_begin( HANDLE proc, char **argv, char **envp, char *exec_path, char *as_user) { sub_process *pproc = (sub_process *)proc; char *shell_name = 0; int file_not_found=0; HANDLE exec_handle; char buf[256]; DWORD bytes_returned; DWORD flags; char *command_line; STARTUPINFO startInfo; PROCESS_INFORMATION procInfo; char *envblk=NULL; OFSTRUCT file_info; /* * Shell script detection... if the exec_path starts with #! then * we want to exec shell-script-name exec-path, not just exec-path * NT doesn't recognize #!/bin/sh or #!/etc/Tivoli/bin/perl. We do not * hard-code the path to the shell or perl or whatever: Instead, we * assume it's in the path somewhere (generally, the NT tools * bin directory) * We use OpenFile here because it is capable of searching the Path. */ exec_handle = find_file(exec_path, &file_info); /* * If we couldn't open the file, just assume that Windows32 will be able * to find and execute it. */ if (exec_handle == (HANDLE)HFILE_ERROR) { file_not_found++; } else { /* Attempt to read the first line of the file */ if (ReadFile( exec_handle, buf, sizeof(buf) - 1, /* leave room for trailing NULL */ &bytes_returned, 0) == FALSE || bytes_returned < 2) { pproc->last_err = GetLastError(); pproc->lerrno = E_IO; CloseHandle(exec_handle); return(-1); } if (buf[0] == '#' && buf[1] == '!') { /* * This is a shell script... Change the command line from * exec_path args to shell_name exec_path args */ char *p; /* Make sure buf is NULL terminated */ buf[bytes_returned] = 0; /* * Depending on the file system type, etc. the first line * of the shell script may end with newline or newline-carriage-return * Whatever it ends with, cut it off. */ p= strchr(buf, '\n'); if (p) *p = 0; p = strchr(buf, '\r'); if (p) *p = 0; /* * Find base name of shell */ shell_name = strrchr( buf, '/'); if (shell_name) { shell_name++; } else { shell_name = &buf[2];/* skipping "#!" */ } } CloseHandle(exec_handle); } flags = 0; if (file_not_found) command_line = make_command_line( shell_name, exec_path, argv); else command_line = make_command_line( shell_name, file_info.szPathName, argv); if ( command_line == NULL ) { pproc->last_err = 0; pproc->lerrno = E_NO_MEM; return(-1); } if (envp) { if (arr2envblk(envp, &envblk) ==FALSE) { pproc->last_err = 0; pproc->lerrno = E_NO_MEM; free( command_line ); return(-1); } } if ((shell_name) || (file_not_found)) { exec_path = 0; /* Search for the program in %Path% */ } else { exec_path = file_info.szPathName; } /* * Set up inherited stdin, stdout, stderr for child */ GetStartupInfo(&startInfo); startInfo.dwFlags = STARTF_USESTDHANDLES; startInfo.lpReserved = 0; startInfo.cbReserved2 = 0; startInfo.lpReserved2 = 0; startInfo.lpTitle = shell_name ? shell_name : exec_path; startInfo.hStdInput = (HANDLE)pproc->sv_stdin[1]; startInfo.hStdOutput = (HANDLE)pproc->sv_stdout[1]; startInfo.hStdError = (HANDLE)pproc->sv_stderr[1]; if (as_user) { if (envblk) free(envblk); return -1; } else { if (debug_flag) printf("CreateProcess(%s,%s,...)\n", exec_path ? exec_path : "NULL", command_line ? command_line : "NULL"); if (CreateProcess( exec_path, command_line, NULL, 0, /* default security attributes for thread */ TRUE, /* inherit handles (e.g. helper pipes, oserv socket) */ flags, envblk, 0, /* default starting directory */ &startInfo, &procInfo) == FALSE) { pproc->last_err = GetLastError(); pproc->lerrno = E_FORK; fprintf(stderr, "process_begin: CreateProcess(%s, %s, ...) failed.\n", exec_path, command_line); if (envblk) free(envblk); free( command_line ); return(-1); } } pproc->pid = (int)procInfo.hProcess; /* Close the thread handle -- we'll just watch the process */ CloseHandle(procInfo.hThread); /* Close the halves of the pipes we don't need */ if (pproc->sv_stdin) { CloseHandle((HANDLE)pproc->sv_stdin[1]); (HANDLE)pproc->sv_stdin[1] = 0; } if (pproc->sv_stdout) { CloseHandle((HANDLE)pproc->sv_stdout[1]); (HANDLE)pproc->sv_stdout[1] = 0; } if (pproc->sv_stderr) { CloseHandle((HANDLE)pproc->sv_stderr[1]); (HANDLE)pproc->sv_stderr[1] = 0; } free( command_line ); if (envblk) free(envblk); pproc->lerrno=0; return 0; } static DWORD proc_stdin_thread(sub_process *pproc) { DWORD in_done; for (;;) { if (WriteFile( (HANDLE) pproc->sv_stdin[0], pproc->inp, pproc->incnt, &in_done, NULL) == FALSE) _endthreadex(0); // This if should never be true for anonymous pipes, but gives // us a chance to change I/O mechanisms later if (in_done < pproc->incnt) { pproc->incnt -= in_done; pproc->inp += in_done; } else { _endthreadex(0); } } return 0; // for compiler warnings only.. not reached } static DWORD proc_stdout_thread(sub_process *pproc) { DWORD bufsize = 1024; char c; DWORD nread; pproc->outp = malloc(bufsize); if (pproc->outp == NULL) _endthreadex(0); pproc->outcnt = 0; for (;;) { if (ReadFile( (HANDLE)pproc->sv_stdout[0], &c, 1, &nread, NULL) == FALSE) { /* map_windows32_error_to_string(GetLastError());*/ _endthreadex(0); } if (nread == 0) _endthreadex(0); if (pproc->outcnt + nread > bufsize) { bufsize += nread + 512; pproc->outp = realloc(pproc->outp, bufsize); if (pproc->outp == NULL) { pproc->outcnt = 0; _endthreadex(0); } } pproc->outp[pproc->outcnt++] = c; } return 0; } static DWORD proc_stderr_thread(sub_process *pproc) { DWORD bufsize = 1024; char c; DWORD nread; pproc->errp = malloc(bufsize); if (pproc->errp == NULL) _endthreadex(0); pproc->errcnt = 0; for (;;) { if (ReadFile( (HANDLE)pproc->sv_stderr[0], &c, 1, &nread, NULL) == FALSE) { map_windows32_error_to_string(GetLastError()); _endthreadex(0); } if (nread == 0) _endthreadex(0); if (pproc->errcnt + nread > bufsize) { bufsize += nread + 512; pproc->errp = realloc(pproc->errp, bufsize); if (pproc->errp == NULL) { pproc->errcnt = 0; _endthreadex(0); } } pproc->errp[pproc->errcnt++] = c; } return 0; } /* * Purpose: collects output from child process and returns results * * Description: * * Returns: * * Notes/Dependencies: */ long process_pipe_io( HANDLE proc, char *stdin_data, int stdin_data_len) { sub_process *pproc = (sub_process *)proc; bool_t stdin_eof = FALSE, stdout_eof = FALSE, stderr_eof = FALSE; HANDLE childhand = (HANDLE) pproc->pid; HANDLE tStdin, tStdout, tStderr; DWORD dwStdin, dwStdout, dwStderr; HANDLE wait_list[4]; DWORD wait_count; DWORD wait_return; HANDLE ready_hand; bool_t child_dead = FALSE; /* * Create stdin thread, if needed */ pproc->inp = stdin_data; pproc->incnt = stdin_data_len; if (!pproc->inp) { stdin_eof = TRUE; CloseHandle((HANDLE)pproc->sv_stdin[0]); (HANDLE)pproc->sv_stdin[0] = 0; } else { tStdin = (HANDLE) _beginthreadex( 0, 1024, (unsigned (__stdcall *) (void *))proc_stdin_thread, pproc, 0, (unsigned int *) &dwStdin); if (tStdin == 0) { pproc->last_err = GetLastError(); pproc->lerrno = E_SCALL; goto done; } } /* * Assume child will produce stdout and stderr */ tStdout = (HANDLE) _beginthreadex( 0, 1024, (unsigned (__stdcall *) (void *))proc_stdout_thread, pproc, 0, (unsigned int *) &dwStdout); tStderr = (HANDLE) _beginthreadex( 0, 1024, (unsigned (__stdcall *) (void *))proc_stderr_thread, pproc, 0, (unsigned int *) &dwStderr); if (tStdout == 0 || tStderr == 0) { pproc->last_err = GetLastError(); pproc->lerrno = E_SCALL; goto done; } /* * Wait for all I/O to finish and for the child process to exit */ while (!stdin_eof || !stdout_eof || !stderr_eof || !child_dead) { wait_count = 0; if (!stdin_eof) { wait_list[wait_count++] = tStdin; } if (!stdout_eof) { wait_list[wait_count++] = tStdout; } if (!stderr_eof) { wait_list[wait_count++] = tStderr; } if (!child_dead) { wait_list[wait_count++] = childhand; } wait_return = WaitForMultipleObjects(wait_count, wait_list, FALSE, /* don't wait for all: one ready will do */ child_dead? 1000 :INFINITE); /* after the child dies, subthreads have one second to collect all remaining output */ if (wait_return == WAIT_FAILED) { /* map_windows32_error_to_string(GetLastError());*/ pproc->last_err = GetLastError(); pproc->lerrno = E_SCALL; goto done; } ready_hand = wait_list[wait_return - WAIT_OBJECT_0]; if (ready_hand == tStdin) { CloseHandle((HANDLE)pproc->sv_stdin[0]); (HANDLE)pproc->sv_stdin[0] = 0; CloseHandle(tStdin); tStdin = 0; stdin_eof = TRUE; } else if (ready_hand == tStdout) { CloseHandle((HANDLE)pproc->sv_stdout[0]); (HANDLE)pproc->sv_stdout[0] = 0; CloseHandle(tStdout); tStdout = 0; stdout_eof = TRUE; } else if (ready_hand == tStderr) { CloseHandle((HANDLE)pproc->sv_stderr[0]); (HANDLE)pproc->sv_stderr[0] = 0; CloseHandle(tStderr); tStderr = 0; stderr_eof = TRUE; } else if (ready_hand == childhand) { if (GetExitCodeProcess(childhand, &pproc->exit_code) == FALSE) { pproc->last_err = GetLastError(); pproc->lerrno = E_SCALL; goto done; } child_dead = TRUE; } else { /* ?? Got back a handle we didn't query ?? */ pproc->last_err = 0; pproc->lerrno = E_FAIL; goto done; } } done: if (tStdin != 0) CloseHandle(tStdin); if (tStdout != 0) CloseHandle(tStdout); if (tStderr != 0) CloseHandle(tStderr); if (pproc->lerrno) return(-1); else return(0); } /* * Purpose: collects output from child process and returns results * * Description: * * Returns: * * Notes/Dependencies: */ long process_file_io( HANDLE proc) { sub_process *pproc; HANDLE childhand; DWORD wait_return; if (proc == NULL) pproc = process_wait_for_any_private(); else pproc = (sub_process *)proc; /* some sort of internal error */ if (!pproc) return -1; childhand = (HANDLE) pproc->pid; /* * This function is poorly named, and could also be used just to wait * for child death if you're doing your own pipe I/O. If that is * the case, close the pipe handles here. */ if (pproc->sv_stdin[0]) { CloseHandle((HANDLE)pproc->sv_stdin[0]); pproc->sv_stdin[0] = 0; } if (pproc->sv_stdout[0]) { CloseHandle((HANDLE)pproc->sv_stdout[0]); pproc->sv_stdout[0] = 0; } if (pproc->sv_stderr[0]) { CloseHandle((HANDLE)pproc->sv_stderr[0]); pproc->sv_stderr[0] = 0; } /* * Wait for the child process to exit */ wait_return = WaitForSingleObject(childhand, INFINITE); if (wait_return != WAIT_OBJECT_0) { /* map_windows32_error_to_string(GetLastError());*/ pproc->last_err = GetLastError(); pproc->lerrno = E_SCALL; goto done2; } if (GetExitCodeProcess(childhand, &pproc->exit_code) == FALSE) { pproc->last_err = GetLastError(); pproc->lerrno = E_SCALL; } done2: if (pproc->lerrno) return(-1); else return(0); } /* * Description: Clean up any leftover handles, etc. It is up to the * caller to manage and free the input, ouput, and stderr buffers. */ void process_cleanup( HANDLE proc) { sub_process *pproc = (sub_process *)proc; int i; if (pproc->using_pipes) { for (i= 0; i <= 1; i++) { if ((HANDLE)pproc->sv_stdin[i]) CloseHandle((HANDLE)pproc->sv_stdin[i]); if ((HANDLE)pproc->sv_stdout[i]) CloseHandle((HANDLE)pproc->sv_stdout[i]); if ((HANDLE)pproc->sv_stderr[i]) CloseHandle((HANDLE)pproc->sv_stderr[i]); } } if ((HANDLE)pproc->pid) CloseHandle((HANDLE)pproc->pid); free(pproc); } /* * Description: * Create a command line buffer to pass to CreateProcess * * Returns: the buffer or NULL for failure * Shell case: sh_name a:/full/path/to/script argv[1] argv[2] ... * Otherwise: argv[0] argv[1] argv[2] ... * * Notes/Dependencies: * CreateProcess does not take an argv, so this command creates a * command line for the executable. */ static char * make_command_line( char *shell_name, char *full_exec_path, char **argv) { int argc = 0; char** argvi; int* enclose_in_quotes = NULL; int* enclose_in_quotes_i; unsigned int bytes_required = 0; char* command_line; char* command_line_i; int cygwin_mode = 0; /* HAVE_CYGWIN_SHELL */ int have_sh = 0; /* HAVE_CYGWIN_SHELL */ #ifdef HAVE_CYGWIN_SHELL have_sh = (shell_name != NULL || strstr(full_exec_path, "sh.exe")); cygwin_mode = 1; #endif if (shell_name && full_exec_path) { bytes_required = strlen(shell_name) + 1 + strlen(full_exec_path); /* * Skip argv[0] if any, when shell_name is given. */ if (*argv) argv++; /* * Add one for the intervening space. */ if (*argv) bytes_required++; } argvi = argv; while (*(argvi++)) argc++; if (argc) { enclose_in_quotes = (int*) calloc(1, argc * sizeof(int)); if (!enclose_in_quotes) { return NULL; } } /* We have to make one pass through each argv[i] to see if we need * to enclose it in ", so we might as well figure out how much * memory we'll need on the same pass. */ argvi = argv; enclose_in_quotes_i = enclose_in_quotes; while(*argvi) { char* p = *argvi; unsigned int backslash_count = 0; /* * We have to enclose empty arguments in ". */ if (!(*p)) *enclose_in_quotes_i = 1; while(*p) { switch (*p) { case '\"': /* * We have to insert a backslash for each " * and each \ that precedes the ". */ bytes_required += (backslash_count + 1); backslash_count = 0; break; #if !defined(HAVE_MKS_SHELL) && !defined(HAVE_CYGWIN_SHELL) case '\\': backslash_count++; break; #endif /* * At one time we set *enclose_in_quotes_i for '*' or '?' to suppress * wildcard expansion in programs linked with MSVC's SETARGV.OBJ so * that argv in always equals argv out. This was removed. Say you have * such a program named glob.exe. You enter * glob '*' * at the sh command prompt. Obviously the intent is to make glob do the * wildcarding instead of sh. If we set *enclose_in_quotes_i for '*' or '?', * then the command line that glob would see would be * glob "*" * and the _setargv in SETARGV.OBJ would _not_ expand the *. */ case ' ': case '\t': *enclose_in_quotes_i = 1; /* fall through */ default: backslash_count = 0; break; } /* * Add one for each character in argv[i]. */ bytes_required++; p++; } if (*enclose_in_quotes_i) { /* * Add one for each enclosing ", * and one for each \ that precedes the * closing ". */ bytes_required += (backslash_count + 2); } /* * Add one for the intervening space. */ if (*(++argvi)) bytes_required++; enclose_in_quotes_i++; } /* * Add one for the terminating NULL. */ bytes_required++; command_line = (char*) malloc(bytes_required); if (!command_line) { if (enclose_in_quotes) free(enclose_in_quotes); return NULL; } command_line_i = command_line; if (shell_name && full_exec_path) { while(*shell_name) { *(command_line_i++) = *(shell_name++); } *(command_line_i++) = ' '; while(*full_exec_path) { *(command_line_i++) = *(full_exec_path++); } if (*argv) { *(command_line_i++) = ' '; } } argvi = argv; enclose_in_quotes_i = enclose_in_quotes; while(*argvi) { char* p = *argvi; unsigned int backslash_count = 0; if (*enclose_in_quotes_i) { *(command_line_i++) = '\"'; } while(*p) { if (*p == '\"') { if (cygwin_mode && have_sh) { /* HAVE_CYGWIN_SHELL */ /* instead of a \", cygwin likes "" */ *(command_line_i++) = '\"'; } else { /* * We have to insert a backslash for the " * and each \ that precedes the ". */ backslash_count++; while(backslash_count) { *(command_line_i++) = '\\'; backslash_count--; }; } #if !defined(HAVE_MKS_SHELL) && !defined(HAVE_CYGWIN_SHELL) } else if (*p == '\\') { backslash_count++; } else { backslash_count = 0; #endif } /* * Copy the character. */ *(command_line_i++) = *(p++); } if (*enclose_in_quotes_i) { #if !defined(HAVE_MKS_SHELL) && !defined(HAVE_CYGWIN_SHELL) /* * Add one \ for each \ that precedes the * closing ". */ while(backslash_count--) { *(command_line_i++) = '\\'; }; #endif *(command_line_i++) = '\"'; } /* * Append an intervening space. */ if (*(++argvi)) { *(command_line_i++) = ' '; } enclose_in_quotes_i++; } /* * Append the terminating NULL. */ *command_line_i = '\0'; if (enclose_in_quotes) free(enclose_in_quotes); return command_line; } /* * Description: Given an argv and optional envp, launch the process * using the default stdin, stdout, and stderr handles. * Also, register process so that process_wait_for_any_private() * can be used via process_file_io(NULL) or * process_wait_for_any(). * * Returns: * * Notes/Dependencies: */ HANDLE process_easy( char **argv, char **envp) { HANDLE hIn; HANDLE hOut; HANDLE hErr; HANDLE hProcess; if (DuplicateHandle(GetCurrentProcess(), GetStdHandle(STD_INPUT_HANDLE), GetCurrentProcess(), &hIn, 0, TRUE, DUPLICATE_SAME_ACCESS) == FALSE) { fprintf(stderr, "process_easy: DuplicateHandle(In) failed (e=%d)\n", GetLastError()); return INVALID_HANDLE_VALUE; } if (DuplicateHandle(GetCurrentProcess(), GetStdHandle(STD_OUTPUT_HANDLE), GetCurrentProcess(), &hOut, 0, TRUE, DUPLICATE_SAME_ACCESS) == FALSE) { fprintf(stderr, "process_easy: DuplicateHandle(Out) failed (e=%d)\n", GetLastError()); return INVALID_HANDLE_VALUE; } if (DuplicateHandle(GetCurrentProcess(), GetStdHandle(STD_ERROR_HANDLE), GetCurrentProcess(), &hErr, 0, TRUE, DUPLICATE_SAME_ACCESS) == FALSE) { fprintf(stderr, "process_easy: DuplicateHandle(Err) failed (e=%d)\n", GetLastError()); return INVALID_HANDLE_VALUE; } hProcess = process_init_fd(hIn, hOut, hErr); if (process_begin(hProcess, argv, envp, argv[0], NULL)) { fake_exits_pending++; ((sub_process*) hProcess)->exit_code = process_last_err(hProcess); /* close up unused handles */ CloseHandle(hIn); CloseHandle(hOut); CloseHandle(hErr); } process_register(hProcess); return hProcess; } /sys/src/ape/cmd/make-3.79/w32/subproc/w32err.c 644 bootes sys 1367613436 1264 #include #include "w32err.h" /* * Description: the windows32 version of perror() * * Returns: a pointer to a static error * * Notes/Dependencies: I got this from * comp.os.ms-windows.programmer.win32 */ char * map_windows32_error_to_string (DWORD ercode) { /* __declspec (thread) necessary if you will use multiple threads */ __declspec (thread) static char szMessageBuffer[128]; /* Fill message buffer with a default message in * case FormatMessage fails */ wsprintf (szMessageBuffer, "Error %ld", ercode); /* * Special code for winsock error handling. */ if (ercode > WSABASEERR) { HMODULE hModule = GetModuleHandle("wsock32"); if (hModule != NULL) { FormatMessage(FORMAT_MESSAGE_FROM_HMODULE, hModule, ercode, LANG_NEUTRAL, szMessageBuffer, sizeof(szMessageBuffer), NULL); FreeLibrary(hModule); } } else { /* * Default system message handling */ FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, ercode, LANG_NEUTRAL, szMessageBuffer, sizeof(szMessageBuffer), NULL); } return szMessageBuffer; } /sys/src/ape/cmd/mkfile 664 sys sys 1369258140 616 APE=/sys/src/ape <$APE/config TARG=basename\ cc\ dirname\ kill\ uname DIRS=\ diff\ expr\ make\ patch\ pdksh\ sed\ BIN=$APEBIN Copyright (C) 19yy 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. /sys/src/ape/cmd/patch/ChangeLog 664 sys sys 1367613436 66042 1997-08-31 Paul Eggert * configure.in (VERSION): Version 2.5 released. 1997-07-21 Paul Eggert * configure.in (VERSION): Bump to 2.4.4. * pch.c (there_is_another_patch), NEWS: Report an error if the patch input contains garbage but no patches. * pch.c (open_patch_file): Check for patch file too long (i.e., its size doesn't fit in a `long', and LFS isn't available). * inp.c (plan_a): Cast malloc return value, in case malloc returns char *. 1997-07-16 Paul Eggert * configure.in (VERSION): Bump to 2.4.3. * NEWS, patch.man, pch.c (intuit_diff_type, get_line, pget_line): Now demangles RFC 934 encapsulation. * pch.c (p_rfc934_nesting): New var. * pch.c (intuit_diff_type): Don't bother to check file names carefully if we're going to return NO_DIFF. * inp.c (plan_a): Count the number of lines before allocating pointer-to-line buffer; this reduces memory requirements considerably (roughly by a factor of 5 on 32-bit hosts). Decrease `size' only when read unexpectedly reports EOF. (i_buffer): New var. (too_many_lines): New fn. (re_input): Free i_buffer if using plan A. Free buffers unconditionally; they can't be zero. * inp.c (plan_a, plan_b): Check for overflow of line counter. * pch.c (malformed), util.h (memory_fatal, read_fatal, write_fatal): Declare as noreturn. 1997-07-10 Paul Eggert * configure.in (VERSION): Bump to 2.4.2. * util.c (ok_to_reverse), NEWS: The default answer is now `n'; this is better for Emacs. * Makefile.in (dist): Use cp -p, not ln; some hosts do the wrong thing with ln if the source is a symbolic link. * patch.man: Fix typo: -y -> -Y. 1997-07-05 Paul Eggert * configure.in (VERSION): Bump to 2.4.1. * patch.c: (main, get_some_switches), NEWS, patch.man: Version control is now independent of whether backups are made. * patch.c (option_help): Put version control options together. (get_some_switches): With CVS 1.9 hack, treat -b foo like -b -z foo, not just -z foo. This change is needed due to recent change in -z. * backupfile.c (find_backup_file_name): backup_type == none causes undefined behavior; this undoes the previous change to this file. * patch.c (locate_hunk): Fix bug when locating context diff hunks near end of file with nonzero fuzz. * util.c (move_file): Don't assume that ENOENT is reported when both ENOENT and EXDEV apply; this isn't true with DJGPP, and Posix doesn't require it. * pch.c (there_is_another_patch): Suggest -p when we can't intuit a file. 1997-06-19 Paul Eggert * configure.in (VERSION): Version 2.4 released. * NEWS: Patch is now verbose when patches do not match exactly. 1997-06-17 Paul Eggert * pc/djgpp/configure.sed (config.h): Remove redundant $(srcdir). * configure.in (VERSION): Bump to 2.3.9. * patch.c (main): By default, warn about hunks that succeed with nonzero offset. * patch.man: Add LC_ALL=C advice for making patches. * pc/djgpp/configure.sed (config.h): Fix paths to dependent files. 1997-06-17 Paul Eggert * configure.in (VERSION): Bump to 2.3.8. * pch.c (open_patch_file): Test stdin for fseekability. (intuit_diff_type): Missing context diff headers are now warnings, not errors; some people use patches with them (e.g. when retrying rejects). * patch.c (struct outstate): New type, collecting together some output state vars. (apply_hunk, copy_till, spew_output, init_output): Use it. Keep track of whether some output has been generated. (backup_if_mismatch): New var. (ofp): Remove, in favor of local struct outstate vars. (main): Use struct outstate. Initialize backup_if_mismatch to be the inverse of posixly_correct. Keep track of whether mismatches occur, and use this to implement backup_if_mismatch. Report files that are not empty after patching, but should be. (longopts, option_help, get_some_switches): New options --backup-if-mismatch, --no-backup-if-mismatch. (get_some_switches): -B, -Y, -z no longer set backup_type. * backupfile.c (find_backup_file_name): Treat backup_type == none like simple. * Makefile.in (CONFIG_HDRS): Remove var; no longer needed by djgpp port. (DISTFILES_PC_DJGPP): Rename pc/djgpp/config.sed to pc/djgpp/configure.sed; remove pc/djgpp/config.h in favor of new file that edits it, called pc/djgpp/config.sed. * pc/djgpp/configure.bat: Rename config.sed to configure.sed. * pc/djgpp/configure.sed (CONFIG_HDRS): Remove. (config.h): Add rule to build this from config.hin and pc/djgpp/config.sed. * pc/djgpp/config.sed: Convert from .h file to .sed script that generates .h file. * NEWS: Describe --backup-if-mismatch, --no-backup-if-mismatch. * patch.man: Describe new options --backup-if-mismatch, --no-backup-if-mismatch and their ramifications. Use unreadable backup to represent nonexistent file. 1997-06-12 Paul Eggert * configure.in (VERSION): Bump to 2.3.7. (AC_CHECK_FUNCS): Add `raise'. * Makefile.in (inp.o): No longer depends on quotearg.h. * common.h (outfile): New decl (was private var named `output'). (invc): New decl. (GENERIC_OBJECT): Renamed from VOID. (NULL_DEVICE, TTY_DEVICE): New macros. * patch.c (output): Remove; renamed to `outfile' and moved to common.h. (main): `failed' is count, not boolean. Say "Skipping patch." when deciding to skip patch. (get_some_switches): Set invc when setting inname. * inp.c: Do not include . (SCCSPREFIX, GET, GET_LOCKED, SCCSDIFF1, SCCSDIFF2, SCCSDIFF3, RCSSUFFIX, CHECKOUT, CHECKOUT_LOCKED, RCSDIFF1, RCSDIFF2): Move to util.c. (get_input_file): Invoke new functions version_controller and version_get to simplify this code. (plan_b): "/dev/tty" -> NULL_DEVICE * pch.h (pch_timestamp): New decl. * pch.c (p_timestamp): New var; takes over from global timestamp array. (pch_timestamp): New function to export p_timestamp. (there_is_another_patch): Use blander wording when you can't intuit the file name. Say "Skipping patch." when deciding to skip patch. (intuit_diff_type): Look for version-controlled but nonexistent files when intuiting file names; set invc accordingly. Ignore Index: line if either old or new line is present, and if POSIXLY_CORRECT is not set. (do_ed_script): Flush stdout before invoking popen, since it may send output to stdout. * util.h (version_controller, version_get): New decls. * util.c: Include earlier. (raise): New macro, if ! HAVE_RAISE. (move_file): Create empty unreadable file when backing up a nonexistent file. (DEV_NULL): New constant. (SCCSPREFIX, GET. GET_LOCKED, SCCSDIFF1, SCCSDIFF2, RCSSUFFIX, CHECKOUT, CHECKOUT_LOCKED, RCSDIFF1): Moved here from inp.c. (version_controller, version_get): New functions. (ask): Look only at /dev/tty for answers; and when standard output is not a terminal and ! posixly_correct, don't even look there. Remove unnecessary fflushes of stdout. (ok_to_reverse): Say "Skipping patch." when deciding to skip patch.. (sigs): SIGPIPE might not be defined. (exit_with_signal): Use `raise' instead of `kill'. (systemic): fflush stdout before invoking subsidiary command. * patch.man: Document recent changes. Add "COMPATIBILITY ISSUES" section. * NEWS: New COMPATIBILITY ISSUES for man page. Changed verbosity when fuzz is found. File name intuition is changed, again. Backups are made unreadable when the file did not exist. * pc/djgpp/config.h (HAVE_STRUCT_UTIMBUF): Define. (HAVE_RAISE): New macro. (HAVE_UTIME_H): Define. (TZ_is_unset): Do not define; it's not a serious problem with `patch' to have TZ be unset in DOS. 1997-06-08 Paul Eggert * configure.in (VERSION): Bump to 2.3.6. (AC_CHECK_HEADERS): Add utime.h. * acconfig.h, configure.in, pc/djgpp/config.h (HAVE_STRUCT_UTIMBUF): New macro. * pc/djgpp/config.h (HAVE_UTIME_H, TZ_is_unset): New macros. * NEWS, patch.man: Describe new -Z, -T options, new numeric option for -G, retired -G, and more verbose default behavior with fuzz. * pch.c (intuit_diff_type): Record times reported for files in headers. Remove head_says_nonexistent[x], since it's now equivalent to !timestamp[x]. * util.h (fetchname): Change argument head_says_nonexistent to timestamp. * util.c: #include for TM_LOCAL_ZONE. Don't include since common.h now includes it. (ok_to_reverse): noreverse and batch cases now output regardless of verbosity. (fetchname): Change argument head_says_nonexistent to pstamp, and store header timestamp into *pstamp. If -T or -Z option is given, match time stamps more precisely. (ask): Remove unnecessary close of ttyfd. When there is no terminal at all, output a newline to make the output look nicer. After reporting EOF, flush stdout; when an input error, report the error type. * inp.c (get_input_file): Ask user whether to get file if patch_get is negative. * Makefile.in (clean): Don't clean */*.o; clean core* and *core. 1997-06-04 Paul Eggert * configure.in (VERSION): Bump to 2.3.5. * util.c (ok_to_reverse): Be less chatty if verbosity is SILENT and we don't have to ask the user. If force is nonzero, apply the patch anyway. * pch.c (there_is_another_patch): Before skipping rest of patch, skip to the patch start, so that another_hunk can skip it properly. (intuit_diff_type): Slight wording change for missing headers, to regularize with other diagnostics. Fix off-by-one error when setting p_input_line when scanning the first hunk to check for deleted files. 1997-06-03 Paul Eggert * configure.in (VERSION): Bump to 2.3.4. * NEWS: Now matches more generously against nonexistent or empty files. * pch.c (there_is_another_patch): Move warning about not being able to intuit file names here from skip_to. (intuit_diff_type): Fatal error if we find a headless unified or context diff. * util.c (ask): Null-terminate buffer properly even if it grew. (fetchname): No need to test for null first argument. 1997-06-02 Paul Eggert * configure.in (VERSION): Bump to 2.3.3. * pch.c (p_says_nonexistent, pch_says_nonexistent): Is now 1 for empty, 2 for nonexistent. (intuit_diff_type): Set p_says_nonexistent according to new meaning. Treat empty files like nonexistent files when reversing. (skip_to): Output better diagnostic when we can't intuit a file name. * patch.c (main): Count bytes, not lines, when testing whether a file is empty, since it may contain only non-newline chars. pch_says_nonexistent now returns 2 for nonexistent files. 1997-06-01 Paul Eggert * configure.in (VERSION): Bump to 2.3.2. * pch.c (open_patch_file): Fix bug when computing size of patch read from a pipe. 1997-05-30 Paul Eggert * configure.in (VERSION): Bump to 2.3.1. * Makefile.in (transform, patch_name): New vars, for proper implementation of AC_ARG_PROGRAM. (install, uninstall): Use them. (install-strip): New rule. * pc/djgpp/config.sed (program_transform_name): Set to empty. 1997-05-30 Paul Eggert * configure.in (VERSION), NEWS: Version 2.3 released. * patch.man: Fix two font typos. * util.c (doprogram): Fix misspelled decl. 1997-05-26 Paul Eggert * configure.in (VERSION): Bump to 2.2.93. * pch.c (open_patch_file): Fatal error if binary_transput and stdin is a tty. * pc/djgpp/config.sed (chdirsaf.c): Use sed instead of cp, since cp might not be installed. * pc/djgpp/configure.bat: Prepend %srcdir% to pathname of config.sed, for crosscompiles. 1997-05-25 Paul Eggert * configure.in (VERSION): Bump to 2.2.92. (D_INO_IN_DIRENT): New macro. * pc/djgpp/config.h, acconfig.h (D_INO_IN_DIRENT): New macro. * backupfile.c (REAL_DIR_ENTRY): Depend on D_INO_IN_DIRENT, not _POSIX_VERSION. * addext.c (addext): Adjust slen when adjusting s for DOS 8.3 limit. Do not use xxx.h -> xxxh~ hack. * util.c: (move_file): Avoid makedirs test when possible even if FILESYSTEM_PREFIX_LEN (p) is nonzero. Don't play case-changing tricks to come up with backup file name; it's not portable to case-insensitive file systems. * common.h (ISLOWER): Remove. * inp.c (scan_input): Don't use Plan A if (debug & 16). * patch.c (shortopts): Add -g, -G. (longopts): --help now maps to 132, not 'h', to avoid confusion. (get_some_switches): Likewise. Don't invoke setmode on input if --binary; wait until needed. Don't ever invoke setmode on stdout. * pch.c (open_patch_file): Setmode stdin to binary if binary_transput. * patch.man: Fix documentation of backup file name to match behavior. Add advice for ordering of patches of derived files. Add /dev/tty to list of files used. * README: Adjust instructions for building on DOS. * pc/djgpp/README: Remove tentative wording. * NEWS: The DOS port is now tested. Backup file names are no longer computed by switching case. * pc/chdirsaf.c (ERANGE): Include to define it. (restore_wd): chdir unconditionally. (chdir_safer): Invoke atexit successfully at most once. * pc/djgpp/config.sed: Use chdirsaf.o, not pc/chdirsaf.o. Replace CONFIG_HDRS, don't append. Use $(srcdir) in CONFIG_STATUS. Don't apply $(SHELL) to $(CONFIG_STATUS). Append rules for chdirsaf.o, chdirsaf.c; clean chdirsaf.c at the end. * pc/djgpp/configure.bat: Append CR to each line; DOS needs this. Don't use | as sed s delimiter; DOS can't handle it. 1997-05-21 Paul Eggert * configure.in (VERSION): Bump to 2.2.91. * pch.c (another_hunk): Fix bug with computing size of prefix and suffix context with ordinary context diffs. Report malformed patch if a unified diff has nothing but context. * inp.c (get_input_file): Use patch_get, not backup_type, to decide whether to get from RCS or SCCS. Use the word `get' in diagnostics. * patch.c (main): Initialize patch_get from PATCH_GET. Omit DEFAULT_VERSION_CONTROL hook; it just leads to nonstandarization. (longopts, option_help, get_some_switches): Add support for -g, -G. (option_help): Add bug report address. * common.h (patch_get): New decl. * patch.man: Add -g and -G options; use `get' instead of `check out'. Add PATCH_GET. Recommend -Naur instead of -raNU2 for diff. * NEWS: Describe -g, -G, PATCH_GET. * version.c (copyright_string): Use only most recent copyright year, as per GNU standards. * Makefile.in (DISTFILES_PC): Remove pc/quotearg.c. * pc/djgpp/config.sed: Remove unnecessary hooks for quotearg and SHELL. 1997-05-18 Paul Eggert * configure.in (VERSION): Increase to 2.2.9. (AC_TYPE_MODE_T): Add. * pch.h (another_hunk): New parameter REV. * pch.c (hunkmax): Now of type LINENUM. (malformed): Add decl. (there_is_another_patch): Skip inname-detection if skip_rest_of_patch. (intuit_diff_type): To determine whether file appears to have been deleted, look at replacement, not pattern. If there is a mismatch between existence of file and whether the patch claims to change whether the file exists, ask whether to reverse the patch. (another_hunk): New parameter REV specifying whether to reverse the hunk. All callers changed. (do_ed_script): Add assertion to ensure input file exists. * util.h (create_file): New function. (copy_file): Now takes mode, not struct stat. (makedirs): No longer exported. (move_file): Now takes mode, not struct stat. * util.c (makedirs): No longer exported. (move_file): Accept mode of destination, not struct stat. All callers changed. Quote file names in diagnostics. Create parent dir of destination if necessary. Don't use ENOTDIR. Don't unlink source; it will be unlinked later. Unlink destination if FROM is zero. (create_file): New function. (copy_file): Accept mode of destination, not struct stat. All callers changed. Use create_file to create file. (ok_to_reverse): Moved here from patch.c. Now accepts format and args; all callers changed. (mkdir): 2nd arg is now mode_t, for better compatibility. (replace_slashes): Ignore slashes at the end of the filename. * common.h (noreverse): New decl. (ok_to_reverse): Remove decl. * patch.c (noreverse): Now extern. (main): New environment var PATCH_VERSION_CONTROL overrides VERSION_CONTROL. Don't assert(hunk) if we're skipping the patch; we may not have any hunks. When removing a file, back it up if backups are desired. Don't chmod output file if input file did not exist. chmod rej file to input file's mode minus executable bits. (locate_hunk): Go back to old way of a single fuzz parameter, but handle it more precisely: context diffs with partial contexts can only match file ends, since the partial context can occur only at the start or end of file. All callers changed. (create_output_file): Use create_file to create files. (ok_to_reverse): Move to util.c. * inp.c (scan_input, get_input_file): Quote file names in diagnostics. (get_input_file): Set inerrno if it's not already set. Don't create file; it's now the caller's responsibility. (plan_b): Use /dev/null if input size is zero, since it might not exist. Use create_file to create temporary file. * NEWS: Add PATCH_VERSION_CONTROL; DOS port is untested. * pc/djgpp/config.h: Add comment for mode_t. * pc/djgpp/README: Note that it's not tested. * patch.man: PATCH_VERSION_CONTROL overrides VERSION_CONTROL. 1997-05-15 Paul Eggert * configure.in: Add AC_PREREQ(2.12). (VERSION): Bump to 2.2.8. (ed_PROGRAM): Rename from ED_PROGRAM. * pch.c (prefix_components): Support DOS file names better. Fix typo that caused fn to almost always yield 0. * util.c (, ): Include. (move_file, copy_file): Add support for DOS filenames. Preserve mode of input files when creating temp files. Add binary file support. (doprogram, rmdir): New functions. (mkdir): Use doprogram. (replace_slashes): Add support for DOS filenames. (removedirs): New function. (init_time)): New function. (initial_time): New var. (fetchname): Add support for deleted files, DOS filenames. * basename.c (FILESYSTEM_PREFIX_LEN, ISSLASH): New macros, for DOS port. (base_name): Use them. * addext.c (HAVE_DOS_FILE_NAMES): New macro. : Include if HAVE_LIMITS_H. (addext): Handle hosts with DOS file name limits. * common.h (LONG_MIN): New macro. (FILESYSTEM_PREFIX_LEN, ISSLASH): New macros, for DOS port. (ok_to_create_file): Remove. (reverse): Now int. (ok_to_reverse): New function decl. (O_WRONLY, _O_BINARY, O_BINARY, O_CREAT, O_TRUNC): New macros. (binary_transput): New var decl. * Makefile.in (ed_PROGRAM): Renamed from ED_PROGRAM. (CONFIG_HDRS, CONFIG_STATUS): New vars. (SRCS): Add maketime.c, partime.c. (OBJS): Likewise. (HDRS): Add maketime.h, partime.h. (DISTFILES_PC, DISTFILES_PC_DJGPP): New vars. (Makefile, config.status): Use CONFIG_STATUS, not config.status. (clean): Remove */*.o. (dist): Add pc and pc/djgpp subdirectories. ($(OBJS)): Depend on $(CONFIG_HDRS) instead of config.h. (maketime.o, partime.o): New rules. (util.o): Depend on maketime.h. * patch.c (main): Call init_time. Add DEFAULT_VERSION_CONTROL hook for people who prefer the old ways. Build temp file names before we might invoke cleanup. Add support for deleted files and clean up the patch-swapping code a bit. Delete empty ancestors of deleted files. When creating temporaries, use file modes of original files. (longopts, get_some_switches): New option --binary. (get_some_switches): Report non-errno errors with `fatal', not `pfatal'. (create_output_file): New function, which preserves modes of original files and supports binary transput. (init_output, init_reject): Use it. (ok_to_reverse): New function. (TMPDIR): New macro. (make_temp): Use $TMPDIR, $TMP, $TEMP, or TMPDIR, whichever comes first. * pch.c (p_says_nonexistent): New var. (open_patch_file): Add binary transput support. Apply stat to file names retrieved from user. Reject them if they don't exist. (intuit_diff_type): Add support for deleting files. Don't treat trivial directories any differently. Avoid stating the same file twice in common case of context diffs. (prefix_components): Don't treat trivial directories any differently. Add support for DOS filenames. (pch_says_nonexistent): New function. (do_ed_script): Preserve mode of input files when creating temp files. Add support for binary transput. * pch.h (pch_says_nonexistent): New decl. * util.h (replace_slashes): No longer exported. (fetchname): Add support for deleted files. (copy_file, move_file): Add support for preserving file modes. (init_time, removedirs): New functions. * argmatch.c: Converge with fileutils. * backupfile.c: Converge with fileutils. (find_backup_file_name): Treat .~N~ suffix just like any other suffix when handling file names that are too long. * inp.c: In messages, put quotes around file names and spaces around "--". (get_input_file): Allow files to be deleted. Do the expense of makedirs only if we can't create the file. (plan_a, plan_b): Add support for binary transput. * pc/chdirsaf.c, pc/djgpp/README, pc/djgpp/config.h, pc/djgpp/config.sed, pc/djgpp/configure.bat, pc/quotearg.c: New file. * NEWS: New methods for removing files; adjust file name intuition again. Add description of MS-DOS and MS-Windows ports. * patch.man: Simplify file name intuition slightly (no distinction for trivial dirs). Add --binary. Describe how files and directories are deleted. Suggest diff -a. Include caveats about what context diffs cannot represent. 1997-05-06 Paul Eggert * configure.in (VERSION): Now 2.2.7. (CPPFLAGS, LDFLAGS, LIBS): If the user has not set any of these vars, prefer support for large files if available. * common.h (_LARGEFILE_SOURCE): Define. (file_offset): New typedef. (file_seek, file_tell): New macros. * patch.c (main): Remove empty files by default unless POSIXLY_CORRECT is set. * util.c, util.h (Fseek): Use file_offset instead of long, for portability to large-file hosts. * pch.c: (p_base, p_start, next_intuit_at, skip_to, open_patch_file, intuit_diff_type, another_hunk, incomplete_line, do_ed_script): Use file_offset instead of long, for portability to large-file hosts. (prefix_components): Renamed from path_name_components; count only nontrivial prefix components, and take a 2nd EXISTING arg. (existing_prefix_components): Remove; subsumed by prefix_components. (intuit_diff_type): When creating files, try for the creation of the fewest directories. * configure.in (VERSION): Now 2.2.6. * pch.c (existing_prefix_components): New function. (intuit_diff_type): When creating a file, use a name whose existing directory prefix contains the most nontrivial path name components. (best_name): Don't check for null 2nd arg. * util.h (replace_slashes): New decl. * util.c (replace_slashes): Now external. (fetchname): Don't assume chars are nonnegative. * patch.man: When creating a file, use a name whose existing directory prefix contains the most nontrivial path name components. Add advice for creating patches and applying them. 1997-05-06 Paul Eggert * configure.in (VERSION): Now 2.2.6. * pch.c (existing_prefix_components): New function. (intuit_diff_type): When creating a file, use a name whose existing directory prefix contains the most nontrivial path name components. (best_name): Don't check for null 2nd arg. * util.h (replace_slashes): New decl. * util.c (replace_slashes): Now external. (fetchname): Don't assume chars are nonnegative. * patch.man: Describe above change to pch.c. Add advice for creating patches and applying them. 1997-05-05 Paul Eggert * configure.in (VERSION): Update to 2.2.5. * quotearg.h, quotearg.c: New files. * Makefile.in (SRCS, OBJS, HDRS): Mention new files. (inp.o, util.o): Now depends on quotearg.h. (quotearg.o): New makefile rule. * common.h (posixly_correct): New var. * patch.c (main): Initialize it. If ! posixly_correct, default backup type is now `existing'. SIMPLE_BACKUP_SUFFIX no longer affects backup type. (backup): Remove var. * util.h: (countdirs): Remove. (systemic): New decl. * util.c (move_file): Try making the parent directory of TO if backup prefix or suffix contain a slash. (ask): Remove arbitrary limit on size of result. (systemic): New function. (mkdir): Work even if arg contains shell metacharacters. (replace_slashes): Return 0 if none were replaced. Don't replace slash after . or .. since it's redundant. (countdirs): Remove. (makedirs): Ignore mkdir failures. * NEWS, patch.man: More POSIXLY_CORRECT adjustments. Describe new rules for how file names are intuited. 1997-04-17 Paul Eggert * configure.in (VERSION): Version 2.2 released. * Makefile.in (config.hin): Remove before building; we always want the timestamp updated. * inp.c (get_input_file): Look for RCS files only if backup_type == numbered_existing. * NEWS, patch.man: Remove mention of never-implemented -V rcs and -V sccs options. * patch.man: `pathname' -> `file name' Correct the description of how file names are found in diff headers. Clarify the distinction between ordinary and unified context diffs. 1997-04-13 Paul Eggert * configure.in (VERSION): Update to 2.1.7. * patch.c (numeric_optarg): New function. (get_some_switches): Use it. * pch.c (intuit_diff_type): When creating a file, prefer a name whose existing dir prefix is the longest. * util.h (countdirs): New function. * util.c (replace_slashes, countdirs): New functions. (makedirs): Use replace_slashes, to be more like countdirs. * patch.man: Explain -pN vs -p N. Recommend --new-file. Explain possible incompatibility with strip count. 1997-04-10 Paul Eggert * configure.in (VERSION): Bump to 2.1.6. (AC_CHECK_HEADERS): Remove stdlib.h (i.e. remove HAVE_STDLIB_H). * Makefile.in: (HDRS, patchlevel.h, TAGS, distclean, maintainer-clean): Don't distribute patchlevel.h; let the user do it. This works around some obscure (possibly nonexistent?) `make' bugs. * common.h (program_name): extern, not XTERN. (): Include if STDC_HEADERS, not if HAVE_STDLIB_H. (atol, getenv, malloc, realloc): Don't worry whether they're #defined. * patch.c (get_some_switches): Add special hack for backwards compatibility with CVS 1.9. (-B, -Y, -z): Now set backup_type = simple. * NEWS: Fix misspellings; minor reformatting. * README: Report POSIX.2 compliance. 1997-04-06 Paul Eggert Move all old RCS $Log entries into ChangeLog. #include all files with < >, not " ". * addext.c, argmatch.c, argmatch.h, memchr.c, install-sh: New files. * EXTERN.h, INTERN.h: Removed. * config.hin: Renamed from config.h.in. * acconfig.h (NODIR): Remove. (HAVE_MEMCHR): Add. * configure.in (AC_ARG_PROGRAM, AC_PROG_MAKE_SET, HAVE_MEMCHR): Add. (AC_CHECK_HEADERS): Replaces obsolescent AC_HAVE_HEADERS. Add stdlib.h, string.h, unistd.h, varargs.h. Delete obsolete call to AC_UNISTD_H. (AC_CONFIG_HEADER): Rename config.h.in to config.hin. (AC_C_CONST): Replaces obsolescent AC_CONST. (AC_CHECK_FUNC): Check for getopt_long; define LIBOBJS and substitute for it accordingly. (AC_CHECK_FUNCS): Replaces obsolescent AC_HAVE_FUNCS. Add _doprintf, isascii, mktemp, sigaction, sigprocmask, sigsetmask. Remove strerror. (AC_FUNC_CLOSEDIR_VOID, AC_FUNC_VPRINTF): Add. (AC_HEADER_DIRENT): Replaces obsolescent AC_DIR_HEADER. (AC_HEADER_STDC): Replaces obsolescent AC_STDC_HEADERS. (AC_SYS_LONG_FILE_NAMES): Replaces obsolescent AC_LONG_FILE_NAMES. (AC_TYPE_OFF_T): Replaces obsolescent AC_OFF_T. (AC_TYPE_SIGNAL): Replaces obsolescent AC_RETSIGTYPE. (AC_TYPE_SIZE_T): Replaces obsolescent AC_SIZE_T. (AC_XENIX_DIR): Remove. (ED_PROGRAM): New var. (NODIR): Remove. (PACKAGE, VERSION): New vars; substitute them with AC_SUBST. * Makefile.in: Conform to current GNU build standards. Redo dependencies. Use library getopt_long if available. Use `&&' instead of `;' inside shell commands where applicable; GNU make requires this. Use double-colon rules for actions that do not build files. (@SET_MAKE@): Added. (CFLAGS, LDFLAGS, prefix, exec_prefix): Base on @ versions of symbols. (COMPILE, CPPFLAGS, DEFS, ED_PROGRAM, LIBOBJS, LIBSRCS, PACKAGE, VERSION): New symbols. (SRCS, OBJS, HDRS, MISC): Add new files. (man1dir): Renamed from mandir. (man1ext): Renamed from manext. (patch): Put -o first. (install): Use $(transform) to allow program to be renamed by configure. (patchlevel.h): Build from $(VERSION). (dist): Get version number from $(VERSION) and package name from $(PACKAGE). (TAGS): Scan $(HDRS). (maintainer-clean): Renamed from realclean. Remove patchlevel.h. * backupfile.h (simple_backup_suffix): Now const *. (find_backup_file_name, base_name, get_version): Args are now const *. (base_name): New decl. * backupfile.c (): Include only if HAVE_CONFIG_H. (): Include. (): Include if HAVE_STRING_H, not if STDC_HEADERS. (): Include if !HAVE_STRING_H. (): Do not include. (): Redo include as per current autoconf standards. (): Include if HAVE_LIMITS_H. Define CHAR_BIT if not defined. (NLENGTH): Now returns size_t. (CLOSEDIR, INT_STRLEN_BOUND): New macros. (ISDIGIT): Use faster method. (find_backup_file_name): No longer depends on NODIR. Remove redundant code. (make_version_name): Remove; do it more portably. (max_backup_version): Args are now const *. (version_number): Simplify digit checking. (basename, concat, dirname): Remove. (argmatch, invalid_arg): Move to argmatch.c. Simplify test for ambiguous args. When reporting an error, use program_name not "patch". (addext): Move to addext.c. Treat all negative values from pathconf like -1. Always use long extension if it fits, even if the filesystem does not support long file names. (backup_types): Now const. * common.h, inp.h (XTERN): Renamed from EXT to avoid collision with errno.h reserved name space. * common.h (DEBUGGING): Now an integer; default is 1. (enum diff): New type. (diff_type): Use it instead of small integers. (CONTEXT_DIFF, NORMAL_DIFF, ED_DIFF, NEW_CONTEXT_DIFF, UNI_DIFF): Now enumerated values instead of macros. (NO_DIFF): New enumerated value (used instead of 0). (volatile): Default to the empty string if __STDC__ is not defined. (): Do not include. (Chmod, Close, Fclose, Fflush, Fputc, Signal, Sprintf, Strcat, Strcpy, Unlink, Write): Remove these macros; casts to void are not needed for GNU coding standards. (INITHUNKMAX): Move to pch.c. (malloc, realloc, INT_MIN, MAXLINELEN, strNE, strnNE, Reg1, Reg2, Reg3, Reg4, Reg5, Reg6, Reg7, Reg8, Reg9, Reg10, Reg11, Reg12, Reg13, Reg14, Reg15, Reg16): Remove these macros. (S_IXOTH, S_IWOTH, S_IROTH, S_IXGRP, S_IWGRP, S_IRGRP, S_IXUSR, S_IWUSR, S_IRUSR, O_RDONLY, O_RDWR): Define these macros, if not defined. (CTYPE_DOMAIN, ISLOWER, ISSPACE, ISDIGIT, PARAMS): New macros. (instat): Renamed from filestat; used for input file now. (bufsize, using_plan_a, debug, strippath): Not statically initialized. (debug): #define to 0 if not DEBUGGING, so that users of `debug' no longer need to be surrounded by `#if DEBUGGING'. (out_of_mem, filec, filearg, outname, toutkeep, trejkeep): Remove. (inname, inerrno, dry_run, origbase): New variables. (origprae): Now const*. (TMPOUTNAME, TMPINNAME, TMPPATNAME): Now const*volatile. (verbosity): New variable; subsumes `verbose'. (DEFAULT_VERBOSITY, SILENT, VERBOSE): Values in a new enum. (verbose): Removed. (VOID): Use `#ifdef __STDC__' instead of`#if __STDC__', for consistency elsewhere. (__attribute__): New macro (empty if not a recent GCC). (fatal_exit): Renamed from my_exit. (errno): Don't define if STDC_HEADERS. (): Include if either STDC_HEADERS or HAVE_STRING_H. (memcmp, memcpy): Define if !STDC_HEADERS && !HAVE_STRING_H && !HAVE_MEMCHR. (): Include if HAVE_STDLIB_H, not if STDC_HEADERS. (atol, getenv, malloc, realloc, lseek): Declare only if not defined as a macro. (popen, strcpy, strcat, mktemp): Do not declare. (lseek): Declare to yield off_t, not long. (): Include only if HAVE_FCNTL_H. * inp.h (get_input_file): New decl. * inp.c (SCCSPREFIX, GET, GET_LOCKED, SCCSDIFF, RCSSUFFIX, CHECKOUT, CHECKOUT_LOCKED, RCSDIFF): Moved here from common.h. (i_ptr): Now char const **. (i_size): Remove. (TIBUFSIZE_MINIMUM): Define only if not already defined. (plan_a, plan_b): Arg is now const *. (report_revision): Declare before use. It's now the caller's responsibility to test whether revision is 0. (scan_input, report_revision, get_input_file): Be less chatty unless --verbose. (get_input_file): New function, split off from plan_a. Reuse file status gotten by pch if possible. Allow for dry run. Use POSIX bits for creat, not number. Check for creation and close failure, and use fstat not stat. Use memcpy not strncpy. (plan_a): Rewrite for speed. Caller now assigns result to using_plan_a. Don't bother reading empty files; during dry runs they might not exist. Use ISSPACE, not isspace. (plan_b): Allow for dry runs. Use ISSPACE, and handle sign extension correctly on arg. Use POSIX symbol for open arg. * patch.c (backup, output, patchname, program_name): New vars. (last_frozen_line): Moved here from inp.h. (TMPREJNAME): Moved here from common.h. (optind_last): Removed. (do_defines, if_defined, not_defined, else_defined, end_defined): Now char const. Prepend with \n (except for not_defined) to allow for files ending in non-newline. (Argv): Now char*const*. (main, get_some_switches): Exit status 0 means success, 1 means hunks were rejected, 2 means trouble. (main, locate_hunk, patch_match): Keep track of patch prefix context separately from suffix context; this fixes several bugs. (main): Initialize bufsize, strippath. Be less chatty unless --verbose. No more NODIR; always have version control available. Require environment variables to be nonempty to have effect. Add support for --dry-run, --output, --verbose. Invoke get_input_file first, before deciding among do_ed_script, plan_a, or plan_b. Clear ofp after closing it, to keep discipline that ofp is either 0 or open, to avoid file descriptor leaks. Conversely, rejfp doesn't need this trick since static analysis is enough to show when it needs to be closed. Don't allow file-creation patches to be applied to existing files. Misordered hunks are now not fatal errors; just go on to the next file. It's a fatal error to fall back on plan B when --output is given, since the moving hand has writ. Add support for binary files. Check for I/O errors. chmod output file ourselves, rather than letting move_file do it; this saves global state. Use better grammar when outputting hunks messages, e.g. avoid `1 hunks'. (main, reinitialize_almost_everything): Remove support for multiple file arguments. Move get_some_switches call from reinitialize_almost_everything to main. (reinitialize_almost_everything): No need to reinitialize things that are no longer global variables, e.g. outname. (shortopts): Remove leading "-"; it's no longer important to return options and arguments in order. '-b' no longer takes operand. -p's operand is no longer optional. Add -i, -Y, -z. Remove -S. (longopts): --suffix is now pared with -z, not -b. --backup now means -b. Add --input, --basename-prefix, --dry-run, --verbose. Remove --skip. --strip's operand is now required. (option_help): New variable. Use style of current coding standards. Change to match current option set. (usage): Use it. (get_some_switches): Get all switches, since `+' is defunct. New options -i, -Y, -z, --verbose, --dry-run. Option -S removed. -b now means backup (backup_type == simple), not simple_backup_suffix. -B now implies backup, and requires nonempty operand. -D no longer requires first char of argument to be an identifier. `-o -' is now disallowed (formerly output to regular file named "-"). -p operand is now required. -v no longer needs to cleanup (no temp files can exist at that point). -V now implies backup. Set inname, patchname from file name arguments, if any; do not set filearg. It's now an error if extra operands are given. (abort_junk): Check for write errors in reject file. (apply_hunk, copy_till): Return error flag, so that failure to apply out-of-order hunk is no longer fatal. (apply_hunk): New arg after_newline, for patching files not ending in newline. Cache ofp for speed. Check for write errors. (OUTSIDE, IN_IFNDEF, IN_IFDEF, IN_ELSE): Now part of an enumerated type instead of being #defined to small integers. Change while-do to do-while when copying !-part for R_do_defines, since condition is always true the first time through the loop. (init_output, init_reject): Arg is now const *. (copy_till, spew_output): Do not insert ``missing'' newlines; propagate them via new after_newline argument. (spew_output): Nothing to copy if last_frozen_line == input lines. Do not close (ofp) if it's null. (dump_line): Remove. (similar): Ignore presence or absence of trailing newlines. Check for only ' ' or '\t', not isspace (as per POSIX.2). (make_temp): Use tmpnam if mktemp is not available. (cleanup): New function. (fatal_exit): Use it. Renamed from my_exit. Take signal to exit with, not exit status (which is now always 2). * pch.h, pch.c (pch_prefix_context, pch_suffix_context): New fns replacing pch_context. (another_hunk): Now yields int, not bool; -1 means out of memory. Now takes difftype as argument. (pch_write_line): Now returns boolean indicating whether we're after a newline just after the write, for supporting non-text files. * pch.c (isdigit): Remove; use ISDIGIT instead. (INITHUNKMAX): Moved here from common.h. (p_context): Removed. We need to keep track of the pre- and post- context separately, in: (p_prefix_context, p_suffix_context): New variables. (bestguess): Remove. (open_patch_file): Arg is now char const *. Copy file a buffer at a time, not a char at a time, for speed. (grow_hunkmax): Now returns success indicator. (there_is_another_patch, skip_to, another_hunk, do_ed_script): Be less chatty unless --verbose. (there_is_another_patch): Avoid infinite loop if user input keeps yielding EOF. (intuit_diff_type): New returns enum diff, not int. Strip paths as they're being fetched. Set ok_to_create_file correctly even if patch is reversed. Set up file names correctly with unidiff output. Use algorithm specified by POSIX 1003.2b/D11 to deduce name of file to patch, with the exception of patches that can create files. (skip_to): Be verbose if !inname, since we're about to ask the user for a file name and the context will help the user choose. (another_hunk): Keep context as LINENUM, not int. If the replacement is missing, calculate its context correctly. Don't assume input ends in newline. Keep track of patch prefix context separately from suffix context; this fixes several bugs. Don't assume blank lines got chopped if the replacement is missing. Report poorly-formed hunks instead of aborting. Do not use strcpy on overlapping strings; it's not portable. Work even if lines are incomplete. Fix bugs associated with context-less context hunks, particularly when patching in reverse. (pget_line): Now takes just 1 arg; instead of second arg, just examine using_plan_a global. Return -1 if we ran out of memory. (do_ed_script): Now takes output FILE * argument. Take name of editor from ED_PROGRAM instead of hardwiring /bin/ed. Don't bother unlinking TMPOUTNAME. Check for popen failure. Flush pipe to check for output errors. If ofp is nonzero, copy result to it, instead of trying to move the result. * util.h, util.c (say1, say2, say3, say4, fatal1, fatal2, fatal3, fatal4, pfatal1, pfatal2, pfatal3, pfatal4, ask1, ask2, ask3, ask4): Remove; replaced with following. (ask, say, fatal, pfatal): New stdarg functions. (fetchname): Remove last, `assume_exists' parameter. (savebuf, savestr, move_file, copy_file): Args are now const *. (exit_with_signal): New function, for proper process status if a signal is received as per POSIX.2. (basename): Rename to `base_name' and move to backupfile. * util.c (): Include here, not in common.h. (vararg_start): New macro. (va_dcl, va_start, va_arg, va_end): Define if neither nor are available. (SIGCHLD): Define to SIGCLD if SIGCLD is defined and SIGCHLD isn't. (private_strerror): Remove. (move_file): Remove option of moving to stdout. Add support for -Y, -z. Don't assume chars in file name are nonnegative. Use copy_file if rename fails due to EXDEV; report failure if rename fails for any other reason. (copy_file, makedirs): Use POSIX symbols for permissions. (copy_file): Open source before destination. (remove_prefix): New function. (vfprintf): New function, if !HAVE_VPRINTF. (afatal, apfatal, zfatal, zpfatal, errnum): Remove. (fatal, pfatal, say): New functions that use stdarg. All callers changed. (zask): Renamed from `ask'. Now uses stdarg. Output to stdout, and read from /dev/tty, or if that cannot be opened, from stderr, stdout, stdin, whichever is first a tty. Print "EOF" when an EOF is read. Do not echo input. (sigs): New array. (sigset_t, sigemptyset, sigmask, sigaddset, sigismember, SIG_BLOCK, SIG_UNBLOCK, SIG_SETMASK, sigprocmask, sigblock, sigsetmask): Define substitutes if not available. (initial_signal_mask, signals_to_block): New vars. (fatal_exit_handler): New function, if !HAVE_SIGACTION. (set_signals, ignore_signals): Use sigaction and sigprocmask style signal-handling if possible; it doesn't lose signals. (set_signals): Default SIGCHLD to work around SysV fork+wait bug. (mkdir): First arg is now const *. (makedirs): Handle multiple adjacent slashes correctly. (fetchname): Do not worry about whether the file exists (that is now the caller's responsibility). Treat a sequence of one or more slashes like one slash. Do not unstrip leading directories if they all exist and if no -p option was given; POSIX doesn't allow this. (memcmp): Remove (now a macro in common.h). * version.c (copyright_string, free_software_msgid, authorship_msgid): New constants. (version): Use them. Use program_name instead of hardwiring it. * patch.man: Generate date from RCS Id. Rewrite to match the above changes. Fri Jul 30 02:02:51 1993 Paul Eggert (eggert@twinsun.com) * configure.in (AC_HAVE_FUNCS): Add mkdir. * common.h (Chmod, Fputc, Write, VOID): New macros. (malloc, realloc): Yield `VOID *', not `char *'. * util.h (makedirs): Omit `striplast' argument. Remove `aask'. * inp.c (plan_a): Remove fixed internal buffer. Remove lint. * util.c (set_signals, ignore_signals): Trap SIGTERM, too. (makedirs): Removed fixed internal buffer. Omit `striplast' argument. (mkdir): New function, if !HAVE_MKDIR. (fetchname): Remove fixed internal buffer. Remove lint from various functions. * patch.c, pch.c: Remove lint. Thu Jul 29 20:52:07 1993 David J. MacKenzie (djm@wookumz.gnu.ai.mit.edu) * Makefile.in (config.status): Run config.status --recheck, not configure, to get the right args passed. Thu Jul 29 07:46:16 1993 Paul Eggert (eggert@twinsun.com) * The following changes remove all remaining fixed limits on memory, and fix bugs in patch's handling of null bytes and files that do not end in newline. `Patch' now works on binary files. * backupfile.c (find_backup_file_name): Don't dump core if malloc fails. * EXTERN.h, INTERN.h (EXITING): New macro. * backupfile.[ch], patch.c, pch.c: Add PARAMS to function declarations. * common.h (bool): Change to int, so ANSI C prototype promotion works. (CANVARARG): Remove varargs hack; it wasn't portable. (filearg): Now a pointer, not an array, so that it can be reallocated. (GET*, SCCSDIFF, CHECKOUT*, RCSDIFF): Quote operands to commands. (my_exit): Declare here. (BUFFERSIZE, Ctl, filemode, Fseek, Fstat, Lseek, MAXFILEC, MAXHUNKSIZE, Mktemp, myuid, Null, Nullch, Nullfp, Nulline, Pclose, VOIDUSED): Remove. All invokers changed. (Argc, Argv, *define[sd], last_offset, maxfuzz, noreverse, ofp, optind_last, rejfp, rejname): No longer externally visible; all definers changed. (INT_MAX, INT_MIN, STD*_FILENO, SEEK_SET): Define if the underlying system doesn't. Include for this. * configure.in: Add limits.h, memcmp. Delete getline. * inp.c (tibufsize): New variable; buffers grow as needed. (TIBUFSIZE_MINIMUM): New macro. (report_revision): New function. (plan_a): Do not search patch as a big string, since that fails if it contains null bytes. Prepend `./' to filenames starting with `-', for RCS and SCCS. If file does not match default RCS/SCCS version, go ahead and patch it anyway; warn about the problem but do not report a fatal error. (plan_b): Do not use a fixed buffer to read lines; read byte by byte instead, so that the lines can be arbitrarily long. Do not search lines as strings, since they may contain null bytes. (plan_a, plan_b): Report I/O errors. * inp.c, inp.h (rev_in_string): Remove. (ifetch): Yield size of line too, since strlen no longer applies. (plan_a, plan_b): No longer exported. * patch.c (abort_hunk, apply_hunk, patch_match, similar): Lines may contain NUL and need not end in newline. (copy_till, dump_line): Insert newline if appending after partial line. All invokers changed. (main, get_some_switches, apply_hunk): Allocate *_define[ds], filearg, rejname dynamically. (make_temp): New function. (main): Use it. (main, spew_output, dump_line) Check for I/O errors. * pch.c (open_patch_file): Don't copy stdin to a temporary file if it's a regular file, since we can seek on it directly. (open_patch_file, skip_to, another_hunk): The patch file may contain NULs. (another_hunk): The patch file may contain lines starting with '\', which means the preceding line lacked a trailing newline. (pgetline): Rename to pget_line. (get_line, incomplete_line, pch_write_line): New functions. (pch_line_len): Return size_t, not short; lines may be very long. (do_ed_script): Check for I/O errors. Allow scripts to contain 'i' and 's' commands, too. * pch.h (pfp, grow_hunkmax, intuit_diff_type, next_intuit_at, skip_to, pfetch, pgetline): No longer exported. (pch_write_line): New declaration. (getline): Removed. * util.c (move_file, fetchname): Use private stat buffer, so that filestat isn't lost. Check for I/O errors. (savestr): Use savebuf. (zask): Use STD*_FILENO instead of 0, 1, 2. (fetchname): strip_leading defaults to INT_MAX instead of 957 (!). (memcmp): Define if !HAVE_MEMCMP. * util.c, util.h (say*, fatal*, pfatal*, ask*): Delete; these pseudo-varargs functions weren't ANSI C. Replace by macros that invoke [fs]printf directly, and invoke new functions [az]{say,fatal,pfatal,ask} before and after. (savebuf, read_fatal, write_fatal, memory_fatal, Fseek): New functions. (fatal*): Output trailing newline after message. All invokers changed. * version.c (version): Don't exit. * Makefile.in (SRCS): Remove getline.c. Thu Jul 22 15:24:24 1993 David J. MacKenzie (djm@goldman.gnu.ai.mit.edu) * EXTERN.h, INTERN.h (PARAMS): Define. * backupfile.h, common.h, inp.h, pch.h, util.h: Use. * backupfile.c: Include EXTERN.h. Wed Jul 21 13:14:05 1993 David J. MacKenzie (djm@goldman.gnu.ai.mit.edu) * getline.c: New file. * configure.in: Check for getline (GNU libc has it). * pch.c: Use it instead of fgets. (pgetline): Renamed from pgets. Change callers. * pch.h: Change decl. * pch.c (pgets): Tab adjusts by 8 - (indent % 8), not % 7. Be consistent with similar code in pch.c::intuit_diff_type. * common.h (MEM): Typedef removed. inp.c, pch.c, util.c: Use size_t instead of MEM. inp.c, pch.c: Use off_t. configure.in: Add AC_SIZE_T and AC_OFF_T. * common.h: Make buf a pointer and add a bufsize variable. * util.c, pch.c, inp.c: Replace sizeof buf with bufsize. * patch.c: malloc buf to bufsize bytes. Tue Jul 20 20:40:03 1993 Paul Eggert (eggert@twinsun.com) * common.h (BUFFERSIZE): Grow it to 8k too, just in case. (buf): Turn `buf' back into an array; making it a pointer broke things seriously. * patch.c (main): Likewise. Tue Jul 20 20:02:40 1993 David J. MacKenzie (djm@goldman.gnu.ai.mit.edu) * Move Reg[1-16] and CANVARARG decls from config.h.in to common.h. * acconfig.h: New file. * Makefile (HDRS): Add it. Tue Jul 20 16:35:27 1993 Paul Eggert (eggert@twinsun.com) * Makefile.in: Remove alloca.[co]; getopt no longer needs it. * configure.in (AC_ALLOCA): Remove. * util.c (set_signals, ignore_signals): Do nothing if SIGHUP and SIGINT aren't defined. Tue Jul 20 17:59:56 1993 David J. MacKenzie (djm@goldman.gnu.ai.mit.edu) * patch.c (main): Call xmalloc, not malloc. xmalloc buf. * common.h: Declare xmalloc. Make buf a pointer, not an array. * util.c (xmalloc): Call fatal1, not fatal. * common.h [MAXLINELEN]: Bump from 1k to 8k. Thu Jul 8 19:56:16 1993 David J. MacKenzie (djm@goldman.gnu.ai.mit.edu) * Makefile.in (installdirs): New target. (install): Use it. (Makefile, config.status, configure): New targets. Wed Jul 7 13:25:40 1993 David J. MacKenzie (djm@goldman.gnu.ai.mit.edu) * patch.c (get_some_switches, longopts): Recognize --help option, and call usage. (usage): New function. Fri Jun 25 07:49:45 1993 Paul Eggert (eggert@twinsun.com) * backupfile.c (find_backup_file_name): Don't use .orig if numbered_existing with no existing numbered backup. (addext): Don't use ext if !HAVE_LONG_FILE_NAMES, even if it would fit. This matches patch's historical behavior. (simple_backup_suffix): Default to ".orig". * patch.c (main): Just use that default. Tue Jun 15 22:32:14 1993 Paul Eggert (eggert@twinsun.com) * config.h.in (HAVE_ALLOCA_H): This #undef was missing. * Makefile.in (info, check, installcheck): New rules. Sun Jun 13 14:31:29 1993 Paul Eggert (eggert@twinsun.com) * config.h.in (index, rindex): Remove unused macro definitions; they get in the way when porting to AIX. * config.h.in, configure.in (HAVE_STRING_H): Remove unused defn. Thu Jun 10 21:13:47 1993 Paul Eggert (eggert@twinsun.com) * patchlevel.h: PATCH_VERSION 2.1. (The name `patch-2.0.12g12' is too long for traditional Unix.) * patchlevel.h (PATCH_VERSION): Renamed from PATCHLEVEL. Now contains the entire patch version number. * version.c (version): Use it. Wed Jun 9 21:43:23 1993 Paul Eggert (eggert@twinsun.com) * common.h: Remove declarations of index and rindex. * backupfile.c: Likewise. (addext, basename, dirname): Avoid rindex. Tue Jun 8 15:24:14 1993 Paul Eggert (eggert@twinsun.com) * inp.c (plan_a): Check that RCS and working files are not the same. This check is needed on hosts that do not report file name length limits and have short limits. Sat Jun 5 22:56:07 1993 Paul Eggert (eggert@twinsun.com) * Makefile.in (.c.o): Put $(CFLAGS) after other options. (dist): Switch from .z to .gz. Wed Jun 2 10:37:15 1993 Paul Eggert (eggert@twinsun.com) * backupfile.c (find_backup_file_name): Initialize copy of file name properly. Mon May 31 21:55:21 1993 Paul Eggert (eggert@twinsun.com) * patchlevel.h: Patch level 12g11. * pch.c (p_Char): Renamed from p_char, which is a system type in Tex XD88's . * backupfile.c: Include "config.h" first, so that `const' is treated consistently in system headers. Mon May 31 16:06:23 1993 Paul Eggert (eggert@twinsun.com) * patchlevel.h: Patch level 12g10. * configure.in: Add AC_CONST. * config.h.in: Add `const'. * Makefile.in (.c.o): Add -DHAVE_CONFIG_H. (getopt.o getopt1.o): Depend on config.h. * util.c (xmalloc): New function; alloca.c needs this. Mon May 31 00:49:40 1993 Paul Eggert (eggert@twinsun.com) * patchlevel.h: PATCHLEVEL 12g9. * backupfile.c, backupfile.h (addext): New function. It uses pathconf(), if available, to determine maximum file name length. * patch.c (main): Use it for reject file name. * common.h (ORIGEXT): Moved to patch.c. * config.h.in (HAVE_PATHCONF): New macro. * configure.in: Define it. * Makefile.in (dist): Use gzip, not compress. Sat May 29 09:42:18 1993 Paul Eggert (eggert@twinsun.com) * patch.c (main): Use pathconf to decide reject file name. * common.h (REJEXT): Remove. * inp.c (plan_a): Don't lock the checked-out file if `patch -o' redirected the output elsewhere. * common.h (CHECKOUT_LOCKED, GET_LOCKED): New macros. GET and CHECKOUT now just checkout unlocked copies. Fri May 28 08:44:50 1993 Paul Eggert (eggert@twinsun.com) * backupfile.c (basename): Define even if NODIR isn't defined. * patch.c (main): Ask just once to apply a reversed patch. Tue Nov 24 08:09:04 1992 David J. MacKenzie (djm@goldman.gnu.ai.mit.edu) * config.h.in, common.h: Use HAVE_FCNTL_H and HAVE_STRING_H instead of USG. * backupfile.c: Use SYSDIR and NDIR instead of USG. Define direct as dirent, not vice-versa. Wed Sep 16 17:11:48 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) * patch.c (get_some_switches): optc should be int, not char. Tue Sep 15 00:36:46 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) * patchlevel.h: PATCHLEVEL 12g8. Mon Sep 14 22:01:23 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) * Makefile.in: Add uninstall target. * util.c (fatal, pfatal): Add some asterisks to make fatal messages stand out more. Tue Aug 25 22:13:36 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) * patch.c (main, get_some_switches), common.h, inp.c (plan_a, plan_b), pch.c (there_is_another_patch): Add -t --batch option, similar to -f --force. Mon Jul 27 11:27:07 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) * common.h: Define SCCSDIFF and RCSDIFF. * inp.c (plan_a): Use them to make sure it's safe to check out the default RCS or SCCS version. From Paul Eggert. Mon Jul 20 14:10:32 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) * util.h: Declare basename. * inp.c (plan_a), util.c (fetchname): Use it to isolate the leading path when testing for RCS and SCCS files. Fri Jul 10 16:03:23 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) * util.c (makedirs): Only make the directories that don't exist. From chip@tct.com (Chip Salzenberg). Wed Jul 8 01:20:56 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) * patch.c (main): Open ofp after checking for ed script. Close ofp and rejfp before trying plan B. From epang@sfu.ca (Eugene Pang). * util.c (fatal, pfatal): Print "patch: " before message. * pch.c, inp.c, patch.c, util.c: Remove "patch: " from the callers that had it. * common.h (myuid): New variable. * patch.c (main): Initialize it. * inp.c (myuid): Function removed. (plan_a): Use the variable, not the function. * patch.c: Add back -E --remove-empty-files option. Tue Jul 7 23:19:28 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) * inp.c (myuid): New function. (plan_a): Call it. Optimize stat calls. Be smarter about detecting checked out RCS and SCCS files. From Paul Eggert (eggert@twinsun.com). * inp.c, util.c, patch.c: Don't bother checking for stat() > 0. Mon Jul 6 13:01:52 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) * util.c (move_file): Use rename instead of link and copying. * util.c (pfatal): New function. * util.h: Declare it and pfatal[1-4] macros. * various files: Use it instead of fatal where appropriate. * common.h, patch.c: Replace Arg[cv]_last with optind_last. * patch.c (main, get_some_switches): Use getopt_long. Update usage message. (nextarg): Function removed. * Rename FLEXFILENAMES to HAVE_LONG_FILE_NAMES, VOIDSIG to RETSIGTYPE. * backupfile.c, common.h: Use STDC header files if available. backupfile.h: Declare get_version. * COPYING, COPYING.LIB, INSTALL, Makefile.in, alloca.c, config.h.in, configure, configure.in, getopt.[ch], getopt1.c, rename.c: New files. * Configure, MANIFEST, Makefile.SH, config.H, config.h.SH, malloc.c: Files removed. * version.c (version): Don't print the RCS stuff, since we're not updating it regularly. * patchlevel.h: PATCHLEVEL 12u7. * Makefile.SH (dist): New target. Makedist: File removed. * inp.c (plan_a): Check whether the user can write to the file, not whether anyone can write to the file. Sat Jul 4 00:06:58 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) * inp.c (plan_a): Try to check out read-only files from RCS or SCCS. * util.c (move_file): If backing up by linking fails, try copying. From cek@sdc.boeing.com (Conrad Kimball). * patch.c (get_some_switches): Eliminate -E option; always remove empty output files. * util.c (fetchname): Only undo slash removal for relative paths if -p was not given. * Makefile.sh: Add mostlyclean target. Fri Jul 3 23:48:14 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) * util.c (fetchname): Accept whitespace between `Index:' and filename. Also plug a small memory leak for diffs against /dev/null. From eggert@twinsun.com (Paul Eggert). * common.h: Don't define TRUE and FALSE if already defined. From phk@data.fls.dk (Poul-Henning Kamp). Wed Apr 29 10:19:33 1992 David J. MacKenzie (djm@churchy.gnu.ai.mit.edu) * backupfile.c (get_version): Exit if given a bad backup type. Fri Mar 27 09:57:14 1992 Karl Berry (karl at hayley) * common.h (S_ISDIR, S_ISREG): define these. * inp.c (plan_a): use S_ISREG, not S_IFREG. * util.c (fetchname): use S_ISDIR, not S_IFDIR. Mon Mar 16 14:10:42 1992 David J. MacKenzie (djm@wookumz.gnu.ai.mit.edu) * patchlevel.h: PATCHLEVEL 12u6. Sat Mar 14 13:13:29 1992 David J. MacKenzie (djm at frob.eng.umd.edu) * Configure, config.h.SH: Check for directory header and unistd.h. * patch.c (main): If -E was given and output file is empty after patching, remove it. (get_some_switches): Recognize -E option. * patch.c (copy_till): Make garbled output an error, not a warning that doesn't change the exit status. * common.h: Protect against system declarations of malloc and realloc. * Makedist: Add backupfile.[ch]. * Configure: Look for C library where NeXT and SVR4 put it. Look in /usr/ucb after /bin and /usr/bin for utilities, and look in /usr/ccs/bin, to make SVR4 happier. Recognize m68k predefine. * util.c (fetchname): Test of stat return value was backward. From csss@scheme.cs.ubc.ca. * version.c (version): Exit with status 0, not 1. * Makefile.SH: Add backupfile.[cho]. * patch.c (main): Initialize backup file generation. (get_some_switches): Add -V option. * common.h, util,c, patch.c: Replace origext with simple_backup_suffix. * util.c (move_file): Use find_backup_file_name. Tue Dec 3 11:27:16 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu) * patchlevel.h: PATCHLEVEL 12u5. * Makefile.SH: Change clean, distclean, and realclean targets a little so they agree with the GNU coding standards. Add Makefile to addedbyconf, so distclean removes it. * Configure: Recognize Domain/OS C library in /lib/libc. From mmuegel@mot.com (Michael S. Muegel). * pch.c: Fixes from Wayne Davison: Patch now accepts no-context context diffs that are specified with an assumed one line hunk (e.g. "*** 10 ****"). Fixed a bug in both context and unified diff processing that would put a zero-context hunk in the wrong place (one line too soon). Fixed a minor problem with p_max in unified diffs where it would set p_max to hunkmax unnecessarily (the only adverse effect was to not supply empty lines at eof by assuming they were truncated). Tue Jul 2 03:25:51 1991 David J. MacKenzie (djm at geech.gnu.ai.mit.edu) * Configure: Check for signal declaration in /usr/include/sys/signal.h as well as /usr/include/signal.h. * Configure, common.h, config.h.SH: Comment out the sprintf declaration and tests to determine its return value type. It conflicts with ANSI C systems' prototypes in stdio.h and the return value of sprintf is never used anyway -- it's always cast to void. Thu Jun 27 13:05:32 1991 David J. MacKenzie (djm at churchy.gnu.ai.mit.edu) * patchlevel.h: PATCHLEVEL 12u4. Thu Feb 21 15:18:14 1991 David J. MacKenzie (djm at geech.ai.mit.edu) * pch.c (another_hunk): Fix off by 1 error. From iverson@xstor.com (Tim Iverson). Sun Jan 20 20:18:58 1991 David J. MacKenzie (djm at geech.ai.mit.edu) * Makefile.SH (all): Don't make a dummy `all' file. * patchlevel.h: PATCHLEVEL 12u3. * patch.c (nextarg): New function. (get_some_switches): Use it, to prevent dereferencing a null pointer if an option that takes an arg is not given one (is last on the command line). From Paul Eggert. * pch.c (another_hunk): Fix from Wayne Davison to recognize single-line hunks in unified diffs (with a single line number instead of a range). * inp.c (rev_in_string): Don't use `s' before defining it. From Wayne Davison. Mon Jan 7 06:25:11 1991 David J. MacKenzie (djm at geech.ai.mit.edu) * patchlevel.h: PATCHLEVEL 12u2. * pch.c (intuit_diff_type): Recognize `+++' in diff headers, for unified diff format. From unidiff patch 1. Mon Dec 3 00:14:25 1990 David J. MacKenzie (djm at albert.ai.mit.edu) * patch.c (get_some_switches): Make the usage message more informative. Sun Dec 2 23:20:18 1990 David J. MacKenzie (djm at albert.ai.mit.edu) * Configure: When checking for C preprocessor, look for 'abc.*xyz' instead of 'abc.xyz', so ANSI C preprocessors work. * Apply fix for -D from ksb@mentor.cc.purdue.edu (Kevin Braunsdorf). 1990-05-01 Wayne Davison * patch.c, pch.c: unidiff support added Wed Mar 7 23:47:25 1990 Jim Kingdon (kingdon at pogo.ai.mit.edu) * pch.c: Call malformed instead of goto malformed (just allows easier debugging). Tue Jan 23 21:27:00 1990 Jim Kingdon (kingdon at pogo.ai.mit.edu) * common.h (TMP*NAME): Make these char *, not char []. patch.c (main): Use TMPDIR (if present) to set TMP*NAME. common.h: Declare getenv. Sun Dec 17 17:29:48 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) * patch.c (reverse_flag_specified): New variable. (get_some_switches, reinitialize_almost_everything): Use it. 1988-06-22 Larry Wall patch12: * common.h: sprintf was declared wrong * patch.c: rindex() wasn't declared * patch.man: now avoids Bell System Logo 1988-06-03 Larry Wall patch10: * common.h: support for shorter extensions. * inp.c: made a little smarter about sccs files * patch.c: exit code improved. better support for non-flexfilenames. * patch.man: -B switch was contributed. * pch.c: Can now find patches in shar scripts. Hunks that swapped and then swapped back could core dump. 1987-06-04 Larry Wall * pch.c: pch_swap didn't swap p_bfake and p_efake. 1987-02-16 Larry Wall * patch.c: Short replacement caused spurious "Out of sync" message. 1987-01-30 Larry Wall * patch.c: Improved diagnostic on sync error. Moved do_ed_script() to pch.c. * pch.c: Improved responses to mangled patches. * pch.h: Added do_ed_script(). 1987-01-05 Larry Wall * pch.c: New-style context diffs caused double call to free(). 1986-11-21 Larry Wall * patch.c: Fuzz factor caused offset of installed lines. 1986-11-14 Larry Wall * pch.c: Fixed problem where a long pattern wouldn't grow the hunk. Also restored p_input_line when backtracking so error messages are right. 1986-11-03 Larry Wall * pch.c: New-style delete triggers spurious assertion error. 1986-10-29 Larry Wall * patch.c: Backwards search could terminate prematurely. * pch.c: Could falsely report new-style context diff. 1986-09-17 Larry Wall * common.h, inp.c, inp.h, patch.c, patch.man, pch.c, pch.h, util.h, version.c, version.h: Baseline for netwide release. 1986-08-01 Larry Wall * patch.c: Fixes for machines that can't vararg. Added fuzz factor. Generalized -p. General cleanup. Changed some %d's to %ld's. Linted. * patch.man: Documented -v, -p, -F. Added notes to patch senders. 1985-08-15 van%ucbmonet@berkeley Changes for 4.3bsd diff -c. 1985-03-26 Larry Wall * patch.c: Frozen. * patch.man: Frozen. 1985-03-12 Larry Wall * patch.c: Now checks for normalness of file to patch. Check i_ptr and i_womp to make sure they aren't null before freeing. Also allow ed output to be suppressed. Changed pfp->_file to fileno(pfp). Added -p option from jromine@uci-750a. Added -D (#ifdef) option from joe@fluke. * patch.man: Documented -p, -D. 1984-12-06 Larry Wall * patch.c: Made smarter about SCCS subdirectories. 1984-12-05 Larry Wall * patch.c: Added -l switch to do loose string comparison. * patch.man: Added -l switch, and noted bistability bug. 1984-12-04 Larry Wall Branch for sdcrdcf changes. * patch.c: Failed hunk count not reset on multiple patch file. * patch.man: Baseline version. 1984-11-29 Larry Wall * patch.c: Linted. Identifiers uniquified. Fixed i_ptr malloc() bug. Fixed multiple calls to mktemp(). Will now work on machines that can only read 32767 chars. Added -R option for diffs with new and old swapped. Various cosmetic changes. 1984-11-09 Larry Wall * patch.c: Initial revision Local Variables: mode: indented-text left-margin: 8 version-control: never end: /sys/src/ape/cmd/patch/FREEBSD-upgrade 664 sys sys 1367613436 1113 This directory contains the virgin patch source on the vendor branch. Do not under any circumstances commit new versions onto the mainline, new versions or official-patch versions must be imported. To prepare a new patch dist for import, extract it into a fresh directory and remove the following files (and any others that are non-FreeBSD specific): memchr.c mkinstalldirs pc/* rename.c The only other change that was made to the original tarball was to rename patch.man to patch.1. patch has RCS Id, Name and Header tags. It needs to be imported with -ko. It is imported from it's top level directory something like this: cvs -n import -ko src/contrib/patch FSF patch_ The -n option is "don't do anything" so you can see what is about to happen first. Remove it when it looks ok. The initial import was done with: cvs import -ko src/contrib/patch FSF patch_2_4 When new versions are imported, cvs will give instructions on how to merge the local and vendor changes when/if conflicts arise.. steve@freebsd.org - 29 June 1997 Current local changes: - Make patch(1) compile -Wall clean. /sys/src/ape/cmd/patch/INSTALL 664 sys sys 1367613436 7832 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. To suppress all normal output, redirect it to `/dev/null' (any error messages will still be shown). `--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. /sys/src/ape/cmd/patch/Makefile.in 664 sys sys 1367613436 4495 # Makefile for GNU patch. # Copyright 1993, 1997 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; see the file COPYING. # If not, write to the Free Software Foundation, # 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #### Start of system configuration section. #### srcdir = @srcdir@ VPATH = @srcdir@ @SET_MAKE@ CC = @CC@ ed_PROGRAM = @ed_PROGRAM@ INSTALL = @INSTALL@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_DATA = @INSTALL_DATA@ transform = @program_transform_name@ CFLAGS = @CFLAGS@ CPPFLAGS = @CPPFLAGS@ DEFS = @DEFS@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ PACKAGE = @PACKAGE@ VERSION = @VERSION@ prefix = @prefix@ exec_prefix = @exec_prefix@ bindir = $(exec_prefix)/bin # Where to put the manual pages. man1dir = $(prefix)/man/man1 # Extension (including `.') for the manual page filenames. man1ext = .1 # Hook for nonstandard builds. CONFIG_STATUS = config.status #### End of system configuration section. #### SHELL = /bin/sh LIBSRCS = getopt.c getopt1.c memchr.c rename.c SRCS = addext.c argmatch.c backupfile.c basename.c inp.c maketime.c \ partime.c patch.c pch.c quotearg.c util.c version.c $(LIBSRCS) OBJS = addext.o argmatch.o backupfile.o basename.o inp.o maketime.o \ partime.o patch.o pch.o quotearg.o util.o version.o $(LIBOBJS) HDRS = argmatch.h backupfile.h common.h getopt.h \ inp.h maketime.h partime.h pch.h quotearg.h util.h version.h MISC = COPYING ChangeLog INSTALL Makefile.in NEWS README \ acconfig.h config.hin configure configure.in \ install-sh mkinstalldirs patch.man DISTFILES = $(MISC) $(SRCS) $(HDRS) DISTFILES_PC = pc/chdirsaf.c DISTFILES_PC_DJGPP = pc/djgpp/README pc/djgpp/config.sed \ pc/djgpp/configure.bat pc/djgpp/configure.sed patch_name = `echo patch | sed '$(transform)'` all:: patch info:: check:: installcheck:: COMPILE = $(CC) -c $(CPPFLAGS) $(DEFS) -Ded_PROGRAM=\"$(ed_PROGRAM)\" \ -I. -I$(srcdir) $(CFLAGS) .c.o: $(COMPILE) $< patch: $(OBJS) $(CC) -o $@ $(CFLAGS) $(LDFLAGS) $(OBJS) $(LIBS) install:: all installdirs $(INSTALL_PROGRAM) patch $(bindir)/$(patch_name) -$(INSTALL_DATA) $(srcdir)/patch.man $(man1dir)/$(patch_name)$(man1ext) installdirs:: $(SHELL) $(srcdir)/mkinstalldirs $(bindir) $(man1dir) install-strip:: $(MAKE) INSTALL_PROGRAM='$(INSTALL_PROGRAM) -s' install uninstall:: rm -f $(bindir)/$(patch_name) $(man1dir)/$(patch_name)$(man1ext) Makefile: Makefile.in $(CONFIG_STATUS) $(SHELL) $(CONFIG_STATUS) config.status: configure $(SHELL) $(CONFIG_STATUS) --recheck configure: configure.in cd $(srcdir) && autoconf config.hin: configure.in acconfig.h cd $(srcdir) && rm -f config.hin && autoheader patchlevel.h: Makefile echo '#define PATCH_VERSION "$(VERSION)"' >patchlevel.h TAGS: $(HDRS) patchlevel.h $(SRCS) etags $(HDRS) patchlevel.h $(SRCS) clean:: rm -f patch core* *core *.o mostlyclean:: clean distclean:: clean rm -f Makefile config.cache config.log config.status config.h rm -f patchlevel.h maintainer-clean:: @echo "This command is intended for maintainers to use;" @echo "rebuilding the deleted files requires special tools." $(MAKE) distclean rm -f TAGS PV = $(PACKAGE)-$(VERSION) dist:: $(DISTFILES) $(DISTFILES_PC) $(DISTFILES_PC_DJGPP) rm -rf $(PV) mkdir $(PV) $(PV)/pc $(PV)/pc/djgpp cp -p $(DISTFILES) $(PV) cp -p $(DISTFILES_PC) $(PV)/pc cp -p $(DISTFILES_PC_DJGPP) $(PV)/pc/djgpp tar -chf - $(PV) | gzip -9 >$(PV).tar.gz rm -rf $(PV) $(OBJS): config.h addext.o: backupfile.h argmatch.o: argmatch.h backupfile.o: argmatch.h backupfile.h basename.o: backupfile.h getopt.o getopt1.o: getopt.h maketime.o: maketime.h partime.h inp.o: backupfile.h common.h inp.h pch.h util.h partime.o: partime.h patch.o: argmatch.h backupfile.h common.h getopt.h inp.h pch.h util.h version.h pch.o: common.h inp.h pch.h util.h quotearg.o: quotearg.h util.o: backupfile.h common.h maketime.h partime.h quotearg.h util.h version.h version.o: common.h patchlevel.h util.h version.h /sys/src/ape/cmd/patch/NEWS 664 sys sys 1367613436 7498 Known problems: * The diffutils 2.7 documentation for `patch' is obsolete; this should be fixed in diffutils 2.8. Until then, see `patch --help' or `man patch'. Changes in version 2.5: * Version control is now independent of whether backups are made. The -V or --version-control option and the VERSION_CONTROL and PATCH_VERSION_CONTROL environment variables no longer affect whether backups are made; they affect only the names of the backup files. * When asking the user whether to reverse a patch, the default answer is now `no' instead of `yes'. * `patch' can now recognize context diffs that have been encapsulated by prepending "- " to lines beginning with "-" (as per Internet RFC 934). * `patch' now reports an error if the input contains garbage and no patches. Changes in version 2.4: * New options: -Z or --set-utc sets times of patched files, assuming diff uses UTC (GMT). -T or --set-time is similar, assuming local time (not recommended). --backup-if-mismatch makes a backup if the patch does not match exactly --no-backup-if-mismatch makes a backup only if otherwise requested * The default is now --backup-if-mismatch unless POSIXLY_CORRECT is set. * The -B or --prefix, -Y or --basename-prefix, and -z or --suffix options no longer affect whether backups are made (as they did in patch 2.2 and 2.3); they now merely specify the file names used when simple backups are made. * When patching a nonexistent file and making backups, an empty backup file is now made (just as with traditional patch); but the backup file is unreadable, as a way of indicating that it represents a nonexistent file. * `patch' now matches against empty and nonexistent files more generously. A patch against an empty file applies to a nonexistent file, and vice versa. * -g or --get and PATCH_GET now have a numeric value that specifies whether `patch' is getting files. If the value is positive, working files are gotten from RCS or SCCS files; if zero, `patch' ignores RCS and SCCS and working files are not gotten; and if negative, `patch' asks the user whether to get each file. The default is normally negative, but it is zero if POSIXLY_CORRECT is set. * The -G or --no-get option introduced in GNU patch 2.3 has been removed; use -g0 instead. * The method used to intuit names of files to be patched is changed again: `Index:' lines are normally ignored for context diffs, and RCS and SCCS files are normally looked for when files do not exist. The complete new method is described in the man page. * By default, `patch' is now more verbose when patches do not match exactly. * The manual page has a new COMPATIBILITY ISSUES section. Changes in version 2.3: * Unless the POSIXLY_CORRECT environment variable is set: - `patch' now distinguishes more accurately between empty and nonexistent files if the input is a context diff. A file is assumed to not exist if its context diff header suggests that it is empty, and if the header timestamp looks like it might be equivalent to 1970-01-01 00:00:00 UTC. - Files that ``become nonexistent'' after patching are now removed. When a file is removed, any empty ancestor directories are also removed. * Files are now automatically gotten from RCS and SCCS if the -g or --get option is specified. (The -G or --no-get option, also introduced in 2.3, was withdrawn in 2.4.) * If the PATCH_VERSION_CONTROL environment variable is set, it overrides the VERSION_CONTROL environment variable. * The method used to intuit names of files to be patched is changed. (It was further revised in 2.4; see above.) * The new --binary option makes `patch' read and write files in binary mode. This option has no effect on POSIX-compliant hosts; it is useful only in on operating systems like DOS that distinguish between text and binary I/O. * The environment variables TMP and TEMP are consulted for the name of the temporary directory if TMPDIR is not set. * A port to MS-DOS and MS-Windows is available; see the `pc' directory. * Backup file names are no longer ever computed by uppercasing characters, since this isn't portable to systems with case-insensitive file names. Changes in version 2.2: * Arbitrary limits removed (e.g. line length, file name length). * On POSIX.1-compliant hosts, you can now patch binary files using the output of GNU `diff -a'. * New options: --dry-run --help --verbose -i FILE or --input=FILE -Y PREF or --basename-prefix=PREF * patch is now quieter by default; use --verbose for the old chatty behavior. * Patch now complies better with POSIX.2 if your host complies with POSIX.1. Therefore: - By default, no backups are made. (But this was changed again in patch 2.4; see above.) - The simple backup file name for F defaults to F.orig regardless of whether the file system supports long file names, and F~ is used only if F.orig is too long for that particular file. - Similarly for the reject file names F.rej and F#. Also: - The pseudo-option `+' has been withdrawn. - -b is equivalent to --version-control=simple; `-z SUFF' has the meaning that `-b SUFF' used to. - Names of files to be patched are taken first from *** line and then from --- line of context diffs; then from Index: line; /dev/tty is consulted if none of the above files exist. However, if the patch appears to create a file, the file does not have to exist: instead, the first name with the longest existing directory prefix is taken. (These rules were changed again in patch 2.3 and 2.4; see above.) - Exit status 0 means success, 1 means hunks were rejected, 2 means trouble. - `-l' ignores changes only in spaces and tabs, not in other white space. - If no `-p' option is given, `-pINFINITY' is assumed, instead of trying to guess the proper value. - `-p' now requires an operand; use `-p 0' to get the effect of the old plain `-p' option. - `-p' treats two or more adjacent slashes as if it were one slash. - The TERM signal is caught. - New option `-i F' reads patch from F instead of stdin. * The `patch' options and build procedure conform to current GNU standards. For example, the `--version' option now outputs copyright information. * When the patch is creating a file, but a nonempty file of that name already exists, `patch' now asks for confirmation before patching. * RCS is used only if the version control method is `existing' and there is already an RCS file. Similarly for SCCS. (But this was changed again in patch 2.3 and 2.4; see above.) * Copyright notices have been clarified. Every file in this version of `patch' can be distributed under the GNU General Public License. See README for details. Changes in version 2.1: * A few more portability bugs have been fixed. The version number has been changed from 2.0.12g11 to 2.1, because the name `patch-2.0.12g10' was too long for traditional Unix file systems. Versions 2.0.12g9 through 2.0.12g11 fix various portability bugs. Changes in version 2.0.12g8: * Start of the 12g series, with a GNU-style configure script and long-named options. * Added the -t --batch option, similar to -f. * Improved detection of files that are locked under RCS or SCCS. * Reinstate the -E option to remove output files that are empty after being patched. * Print the system error message when system calls fail. * Fixed various bugs and portability problems. /sys/src/ape/cmd/patch/README 664 sys sys 1367613436 2385 This version of `patch' has many changes made by the Free Software Foundation. They add support for: * handling arbitrary binary data and large files * the unified context diff format that GNU diff can produce * making GNU Emacs-style backup files * improved interaction with RCS and SCCS * the GNU conventions for option parsing and configuring and compilation. * better POSIX.2 compliance They also fix some bugs. See the NEWS and ChangeLog files for details. Tutorial-style documentation for patch is included in the GNU diffutils package. Unfortunately, the diffutils 2.7 documentation for `patch' is obsolete; this should be fixed in diffutils 2.8. In the mean time, see `patch --help', or consult the man page in this distribution. For GNU and Unix build and installation instructions, see the file INSTALL. For MS-DOS using DJGPP tools, see the file pc/djgpp/README. For other systems, copy config.hin to config.h and change #undef statements in it to #define as appropriate for your system, and copy Makefile.in to Makefile and set the variables that are enclosed in @ signs as appropriate for your system. Please send bug reports for this version of patch to bug-gnu-utils@prep.ai.mit.edu. The Free Software Foundation is distributing this version of patch independently because as of this writing, Larry Wall has not released a new version of patch since mid-1988. We have heard that he has been too busy working on other things, like Perl. He has graciously agreed to let GNU `patch' be distributed under the terms of the GNU General Public License. ------ Copyright 1984, 1985, 1986, 1987, 1988 Larry Wall Copyright 1989, 1990, 1991, 1992, 1993, 1997 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 file; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. /sys/src/ape/cmd/patch/acconfig.h 664 sys sys 1367613436 452 /* Local acconfig.h for autoheader. Descriptive text for the C preprocessor macros that the patch configure.in can define. autoheader copies the comments into config.hin. */ /* Define if there is a member named d_ino in the struct describing directory headers. */ #undef D_INO_IN_DIRENT /* Define if memchr works. */ #undef HAVE_MEMCHR /* Define if `struct utimbuf' is declared -- usually in . */ #undef HAVE_STRUCT_UTIMBUF /sys/src/ape/cmd/patch/addext.c 664 sys sys 1367613436 2605 /* addext.c -- add an extension to a file name Copyright (C) 1990, 1997 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; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Written by David MacKenzie and Paul Eggert */ #if HAVE_CONFIG_H # include #endif #ifndef HAVE_DOS_FILE_NAMES #define HAVE_DOS_FILE_NAMES 0 #endif #ifndef HAVE_LONG_FILE_NAMES #define HAVE_LONG_FILE_NAMES 0 #endif #include #if HAVE_LIMITS_H # include #endif #ifndef _POSIX_NAME_MAX #define _POSIX_NAME_MAX 14 #endif #include #if HAVE_STRING_H # include #else # include #endif #if HAVE_UNISTD_H # include #endif /* Append to FILENAME the extension EXT, unless the result would be too long, in which case just append the character E. */ void addext (filename, ext, e) char *filename; char const *ext; int e; { char *s = base_name (filename); size_t slen = strlen (s), extlen = strlen (ext); long slen_max = -1; #if HAVE_PATHCONF && defined _PC_NAME_MAX if (slen + extlen <= _POSIX_NAME_MAX && ! HAVE_DOS_FILE_NAMES) /* The file name is so short there's no need to call pathconf. */ slen_max = _POSIX_NAME_MAX; else if (s == filename) slen_max = pathconf (".", _PC_NAME_MAX); else { char c = *s; *s = 0; slen_max = pathconf (filename, _PC_NAME_MAX); *s = c; } #endif if (slen_max < 0) slen_max = HAVE_LONG_FILE_NAMES ? 255 : 14; if (HAVE_DOS_FILE_NAMES && slen_max <= 12) { /* Live within DOS's 8.3 limit. */ char *dot = strchr (s, '.'); if (dot) { slen -= dot + 1 - s; s = dot + 1; slen_max = 3; } else slen_max = 8; extlen = 9; /* Don't use EXT. */ } if (slen + extlen <= slen_max) strcpy (s + slen, ext); else { if (slen_max <= slen) slen = slen_max - 1; s[slen] = e; s[slen + 1] = 0; } } /sys/src/ape/cmd/patch/argmatch.c 664 sys sys 1367613436 2652 /* argmatch.c -- find a match for a string in an array Copyright (C) 1990, 1997 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; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Written by David MacKenzie */ #if HAVE_CONFIG_H # include #endif #include #include #include #if HAVE_STRING_H # include #else # include #endif /* If ARG is an unambiguous match for an element of the null-terminated array OPTLIST, return the index in OPTLIST of the matched element, else -1 if it does not match any element or -2 if it is ambiguous (is a prefix of more than one element). */ int argmatch (arg, optlist) const char *arg; const char *const *optlist; { int i; /* Temporary index in OPTLIST. */ size_t arglen; /* Length of ARG. */ int matchind = -1; /* Index of first nonexact match. */ int ambiguous = 0; /* If nonzero, multiple nonexact match(es). */ arglen = strlen (arg); /* Test all elements for either exact match or abbreviated matches. */ for (i = 0; optlist[i]; i++) { if (!strncmp (optlist[i], arg, arglen)) { if (strlen (optlist[i]) == arglen) /* Exact match found. */ return i; else if (matchind == -1) /* First nonexact match found. */ matchind = i; else /* Second nonexact match found. */ ambiguous = 1; } } if (ambiguous) return -2; else return matchind; } /* Error reporting for argmatch. KIND is a description of the type of entity that was being matched. VALUE is the invalid value that was given. PROBLEM is the return value from argmatch. */ void invalid_arg (kind, value, problem) const char *kind; const char *value; int problem; { fprintf (stderr, "%s: ", program_name); if (problem == -1) fprintf (stderr, "invalid"); else /* Assume -2. */ fprintf (stderr, "ambiguous"); fprintf (stderr, " %s `%s'\n", kind, value); } /sys/src/ape/cmd/patch/argmatch.h 664 sys sys 1367613436 357 /* argmatch.h -- declarations for matching arguments against option lists */ #if defined __STDC__ || __GNUC__ # define __ARGMATCH_P(args) args #else # define __ARGMATCH_P(args) () #endif int argmatch __ARGMATCH_P ((const char *, const char * const *)); void invalid_arg __ARGMATCH_P ((const char *, const char *, int)); extern char const program_name[]; /sys/src/ape/cmd/patch/backupfile.c 664 sys sys 1367613436 6739 /* backupfile.c -- make Emacs style backup file names Copyright (C) 1990,1991,1992,1993,1995,1997 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; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Written by David MacKenzie . Some algorithms adapted from GNU Emacs. */ #if HAVE_CONFIG_H # include #endif #include #include #include #include #if HAVE_STRING_H # include #else # include #endif #if HAVE_DIRENT_H # include # define NLENGTH(direct) strlen ((direct)->d_name) #else # define dirent direct # define NLENGTH(direct) ((size_t) (direct)->d_namlen) # if HAVE_SYS_NDIR_H # include # endif # if HAVE_SYS_DIR_H # include # endif # if HAVE_NDIR_H # include # endif #endif #if CLOSEDIR_VOID /* Fake a return value. */ # define CLOSEDIR(d) (closedir (d), 0) #else # define CLOSEDIR(d) closedir (d) #endif #if STDC_HEADERS # include #else char *malloc (); #endif #if HAVE_DIRENT_H || HAVE_NDIR_H || HAVE_SYS_DIR_H || HAVE_SYS_NDIR_H # define HAVE_DIR 1 #else # define HAVE_DIR 0 #endif #if HAVE_LIMITS_H # include #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 + 2) /* ISDIGIT differs from isdigit, as follows: - Its arg may be any int or unsigned int; it need not be an unsigned char. - It's guaranteed to evaluate its argument exactly once. - It's typically faster. Posix 1003.2-1992 section 2.5.2.1 page 50 lines 1556-1558 says that only '0' through '9' are digits. Prefer ISDIGIT to isdigit unless it's important to use the locale's definition of `digit' even when the host does not conform to Posix. */ #define ISDIGIT(c) ((unsigned) (c) - '0' <= 9) #if D_INO_IN_DIRENT # define REAL_DIR_ENTRY(dp) ((dp)->d_ino != 0) #else # define REAL_DIR_ENTRY(dp) 1 #endif /* Which type of backup file names are generated. */ enum backup_type backup_type = none; /* The extension added to file names to produce a simple (as opposed to numbered) backup file name. */ const char *simple_backup_suffix = ".orig"; static int max_backup_version __BACKUPFILE_P ((const char *, const char *)); static int version_number __BACKUPFILE_P ((const char *, const char *, size_t)); /* Return the name of the new backup file for file FILE, allocated with malloc. Return 0 if out of memory. FILE must not end with a '/' unless it is the root directory. Do not call this function if backup_type == none. */ char * find_backup_file_name (file) const char *file; { size_t backup_suffix_size_max; size_t file_len = strlen (file); size_t numbered_suffix_size_max = INT_STRLEN_BOUND (int) + 4; char *s; const char *suffix = simple_backup_suffix; /* Allow room for simple or `.~N~' backups. */ backup_suffix_size_max = strlen (simple_backup_suffix) + 1; if (HAVE_DIR && backup_suffix_size_max < numbered_suffix_size_max) backup_suffix_size_max = numbered_suffix_size_max; s = malloc (file_len + backup_suffix_size_max + numbered_suffix_size_max); if (s) { strcpy (s, file); #if HAVE_DIR if (backup_type != simple) { int highest_backup; size_t dir_len = base_name (s) - s; strcpy (s + dir_len, "."); highest_backup = max_backup_version (file + dir_len, s); if (! (backup_type == numbered_existing && highest_backup == 0)) { char *numbered_suffix = s + (file_len + backup_suffix_size_max); sprintf (numbered_suffix, ".~%d~", highest_backup + 1); suffix = numbered_suffix; } strcpy (s, file); } #endif /* HAVE_DIR */ addext (s, suffix, '~'); } return s; } #if HAVE_DIR /* Return the number of the highest-numbered backup file for file FILE in directory DIR. If there are no numbered backups of FILE in DIR, or an error occurs reading DIR, return 0. */ static int max_backup_version (file, dir) const char *file; const char *dir; { DIR *dirp; struct dirent *dp; int highest_version; int this_version; size_t file_name_length; dirp = opendir (dir); if (!dirp) return 0; highest_version = 0; file_name_length = strlen (file); while ((dp = readdir (dirp)) != 0) { if (!REAL_DIR_ENTRY (dp) || NLENGTH (dp) < file_name_length + 4) continue; this_version = version_number (file, dp->d_name, file_name_length); if (this_version > highest_version) highest_version = this_version; } if (CLOSEDIR (dirp)) return 0; return highest_version; } /* If BACKUP is a numbered backup of BASE, return its version number; otherwise return 0. BASE_LENGTH is the length of BASE. */ static int version_number (base, backup, base_length) const char *base; const char *backup; size_t base_length; { int version; const char *p; version = 0; if (strncmp (base, backup, base_length) == 0 && backup[base_length] == '.' && backup[base_length + 1] == '~') { for (p = &backup[base_length + 2]; ISDIGIT (*p); ++p) version = version * 10 + *p - '0'; if (p[0] != '~' || p[1]) version = 0; } return version; } #endif /* HAVE_DIR */ static const char * const backup_args[] = { "never", "simple", "nil", "existing", "t", "numbered", 0 }; static const enum backup_type backup_types[] = { simple, simple, numbered_existing, numbered_existing, numbered, numbered }; /* Return the type of backup indicated by VERSION. Unique abbreviations are accepted. */ enum backup_type get_version (version) const char *version; { int i; if (version == 0 || *version == 0) return numbered_existing; i = argmatch (version, backup_args); if (i < 0) { invalid_arg ("version control type", version, i); exit (2); } return backup_types[i]; } /sys/src/ape/cmd/patch/backupfile.h 664 sys sys 1367613436 1662 /* backupfile.h -- declarations for making Emacs style backup file names Copyright (C) 1990, 1991, 1992, 1997 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; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* When to make backup files. */ enum backup_type { /* Never make backups. */ none, /* Make simple backups of every file. */ simple, /* Make numbered backups of files that already have numbered backups, and simple backups of the others. */ numbered_existing, /* Make numbered backups of every file. */ numbered }; extern enum backup_type backup_type; extern char const *simple_backup_suffix; #ifndef __BACKUPFILE_P # if defined __STDC__ || __GNUC__ # define __BACKUPFILE_P(args) args # else # define __BACKUPFILE_P(args) () # endif #endif char *base_name __BACKUPFILE_P ((char const *)); char *find_backup_file_name __BACKUPFILE_P ((char const *)); enum backup_type get_version __BACKUPFILE_P ((char const *)); void addext __BACKUPFILE_P ((char *, char const *, int)); /sys/src/ape/cmd/patch/basename.c 664 sys sys 1367613436 675 /* basename.c -- return the last element in a path */ #if HAVE_CONFIG_H # include #endif #include #ifndef FILESYSTEM_PREFIX_LEN #define FILESYSTEM_PREFIX_LEN(f) 0 #endif #ifndef ISSLASH #define ISSLASH(c) ((c) == '/') #endif /* In general, we can't use the builtin `basename' function if available, since it has different meanings in different environments. In some environments the builtin `basename' modifies its argument. */ char * base_name (name) char const *name; { char const *base = name += FILESYSTEM_PREFIX_LEN (name); for (; *name; name++) if (ISSLASH (*name)) base = name + 1; return (char *) base; } /sys/src/ape/cmd/patch/common.h 664 sys sys 1367613436 6153 /* common definitions for `patch' */ /* $Id: common.h,v 1.18 1997/06/13 06:28:37 eggert Exp $ */ /* Copyright 1986, 1988 Larry Wall Copyright 1990, 1991, 1992, 1993, 1997 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; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef DEBUGGING #define DEBUGGING 1 #endif /* We must define `volatile' and `const' first (the latter inside config.h), so that they're used consistently in all system includes. */ #ifndef __STDC__ # ifndef volatile # define volatile # endif #endif /* Enable support for fseeko and ftello on hosts where it is available but is turned off by default. This must be defined before any system file is included. */ #define _LARGEFILE_SOURCE 1 #include #include #include #include #include #include #if ! defined S_ISDIR && defined S_IFDIR # define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) #endif #if ! defined S_ISREG && defined S_IFREG # define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) #endif #ifndef S_IXOTH #define S_IXOTH 1 #endif #ifndef S_IWOTH #define S_IWOTH 2 #endif #ifndef S_IROTH #define S_IROTH 4 #endif #ifndef S_IXGRP #define S_IXGRP (S_IXOTH << 3) #endif #ifndef S_IWGRP #define S_IWGRP (S_IWOTH << 3) #endif #ifndef S_IRGRP #define S_IRGRP (S_IROTH << 3) #endif #ifndef S_IXUSR #define S_IXUSR (S_IXOTH << 6) #endif #ifndef S_IWUSR #define S_IWUSR (S_IWOTH << 6) #endif #ifndef S_IRUSR #define S_IRUSR (S_IROTH << 6) #endif #if HAVE_LIMITS_H # include #endif #ifndef INT_MAX #define INT_MAX 2147483647 #endif #ifndef LONG_MIN #define LONG_MIN (-1-2147483647L) #endif #include /* CTYPE_DOMAIN (C) is nonzero if the unsigned char C can safely be given as an argument to macros like `isspace'. */ #if STDC_HEADERS #define CTYPE_DOMAIN(c) 1 #else #define CTYPE_DOMAIN(c) ((unsigned) (c) <= 0177) #endif #ifndef ISSPACE #define ISSPACE(c) (CTYPE_DOMAIN (c) && isspace (c)) #endif #ifndef ISDIGIT #define ISDIGIT(c) ((unsigned) (c) - '0' <= 9) #endif #ifndef FILESYSTEM_PREFIX_LEN #define FILESYSTEM_PREFIX_LEN(f) 0 #endif #ifndef ISSLASH #define ISSLASH(c) ((c) == '/') #endif /* constants */ /* AIX predefines these. */ #ifdef TRUE #undef TRUE #endif #ifdef FALSE #undef FALSE #endif #define TRUE 1 #define FALSE 0 /* handy definitions */ #define strEQ(s1,s2) (!strcmp(s1, s2)) #define strnEQ(s1,s2,l) (!strncmp(s1, s2, l)) /* typedefs */ typedef int bool; /* must promote to itself */ typedef long LINENUM; /* must be signed */ /* globals */ extern char const program_name[]; XTERN char *buf; /* general purpose buffer */ XTERN size_t bufsize; /* allocated size of buf */ XTERN bool using_plan_a; /* try to keep everything in memory */ XTERN char *inname; XTERN char *outfile; XTERN int inerrno; XTERN int invc; XTERN struct stat instat; XTERN bool dry_run; XTERN bool posixly_correct; XTERN char const *origprae; XTERN char const *origbase; XTERN char const * volatile TMPOUTNAME; XTERN char const * volatile TMPINNAME; XTERN char const * volatile TMPPATNAME; #ifdef DEBUGGING XTERN int debug; #else # define debug 0 #endif XTERN bool force; XTERN bool batch; XTERN bool noreverse; XTERN int reverse; XTERN enum { DEFAULT_VERBOSITY, SILENT, VERBOSE } verbosity; XTERN bool skip_rest_of_patch; XTERN int strippath; XTERN bool canonicalize; XTERN int patch_get; XTERN int set_time; XTERN int set_utc; enum diff { NO_DIFF, CONTEXT_DIFF, NORMAL_DIFF, ED_DIFF, NEW_CONTEXT_DIFF, UNI_DIFF }; XTERN enum diff diff_type; XTERN char *revision; /* prerequisite revision, if any */ #ifdef __STDC__ # define GENERIC_OBJECT void #else # define GENERIC_OBJECT char #endif #if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 6) || __STRICT_ANSI__ # define __attribute__(x) #endif #ifndef PARAMS # ifdef __STDC__ # define PARAMS(args) args # else # define PARAMS(args) () # endif #endif GENERIC_OBJECT *xmalloc PARAMS ((size_t)); void fatal_exit PARAMS ((int)) __attribute__ ((noreturn)); #include #if !STDC_HEADERS && !defined errno extern int errno; #endif #if STDC_HEADERS || HAVE_STRING_H # include #else # if !HAVE_MEMCHR # define memcmp(s1, s2, n) bcmp (s1, s2, n) # define memcpy(d, s, n) bcopy (s, d, n) GENERIC_OBJECT *memchr (); # endif #endif #if STDC_HEADERS # include #else long atol (); char *getenv (); GENERIC_OBJECT *malloc (); GENERIC_OBJECT *realloc (); #endif #if HAVE_UNISTD_H # include #endif #ifndef lseek off_t lseek (); #endif #ifndef SEEK_SET #define SEEK_SET 0 #endif #ifndef STDIN_FILENO #define STDIN_FILENO 0 #endif #ifndef STDOUT_FILENO #define STDOUT_FILENO 1 #endif #ifndef STDERR_FILENO #define STDERR_FILENO 2 #endif #if _LFS_LARGEFILE typedef off_t file_offset; # define file_seek fseeko # define file_tell ftello #else typedef long file_offset; # define file_seek fseek # define file_tell ftell #endif #if HAVE_FCNTL_H # include #endif #ifndef O_RDONLY #define O_RDONLY 0 #endif #ifndef O_WRONLY #define O_WRONLY 1 #endif #ifndef O_RDWR #define O_RDWR 2 #endif #ifndef _O_BINARY #define _O_BINARY 0 #endif #ifndef O_BINARY #define O_BINARY _O_BINARY #endif #ifndef O_CREAT #define O_CREAT 0 #endif #ifndef O_TRUNC #define O_TRUNC 0 #endif #if HAVE_SETMODE XTERN int binary_transput; /* O_BINARY if binary i/o is desired */ #else # define binary_transput 0 #endif #ifndef NULL_DEVICE #define NULL_DEVICE "/dev/null" #endif #ifndef TTY_DEVICE #define TTY_DEVICE "/dev/tty" #endif /sys/src/ape/cmd/patch/config.h 664 sys sys 1367613436 3428 /* config.h. Generated automatically by configure. */ /* config.hin. Generated automatically from configure.in by autoheader. */ /* Define if using alloca.c. */ /* #undef C_ALLOCA */ /* Define if the closedir function returns void instead of int. */ /* #undef CLOSEDIR_VOID */ /* Define to empty if the keyword does not work. */ /* #undef const */ /* Define to one of _getb67, GETB67, getb67 for Cray-2 and Cray-YMP systems. This function is required for alloca.c support on those systems. */ /* #undef CRAY_STACKSEG_END */ /* Define if you have and it should be used (not on Ultrix). */ /* #undef HAVE_ALLOCA_H */ /* Define if you don't have vprintf but do have _doprnt. */ /* #undef HAVE_DOPRNT */ /* Define if your struct stat has st_blksize. */ /* #define HAVE_ST_BLKSIZE 1 */ /* Define if you have . */ /* #undef HAVE_VFORK_H */ /* Define if you have the vprintf function. */ #define HAVE_VPRINTF 1 /* Define if on MINIX. */ /* #undef _MINIX */ /* Define to `int' if doesn't define. */ /* #undef pid_t */ /* Define if the system does not provide POSIX.1 features except with this defined. */ /* #undef _POSIX_1_SOURCE */ /* 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 /* If using the C implementation of alloca, define if you know the direction of stack growth for your system; otherwise it will be automatically deduced at run-time. STACK_DIRECTION > 0 => grows toward higher addresses STACK_DIRECTION < 0 => grows toward lower addresses STACK_DIRECTION = 0 => direction of growth unknown */ /* #undef STACK_DIRECTION */ /* Define if the `S_IS*' macros in do not work properly. */ /* #undef STAT_MACROS_BROKEN */ /* Define if you have the ANSI C header files. */ #define STDC_HEADERS 1 /* Define if is compatible with Posix applications. */ #define HAVE_SYS_WAIT_H 1 /* Define vfork as fork if vfork does not work. */ /* #undef vfork */ /* Define if you have the dup2 function. */ #define HAVE_DUP2 1 /* Define if you have the memchr function. */ #define HAVE_MEMCHR 1 /* Define if you have the sigaction function. */ #define HAVE_SIGACTION 1 /* Define if you have the strchr function. */ #define HAVE_STRCHR 1 /* Define if you have the strerror function. */ #define HAVE_STRERROR 1 /* Define if you have the tmpnam function. */ #define HAVE_TMPNAM 1 /* Define if you have the header file. */ #define HAVE_DIRENT_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. */ /* #undef HAVE_NDIR_H */ /* 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. */ /* #undef HAVE_SYS_DIR_H */ /* Define if you have the header file. */ #define HAVE_SYS_FILE_H 1 /* Define if you have the header file. */ /* #undef HAVE_SYS_NDIR_H */ /* 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 HAVE_MKDIR 1 /sys/src/ape/cmd/patch/config.hin 664 sys sys 1367613436 3406 /* config.h. Generated automatically by configure. */ /* config.hin. Generated automatically from configure.in by autoheader. */ /* Define if using alloca.c. */ /* #undef C_ALLOCA */ /* Define if the closedir function returns void instead of int. */ /* #undef CLOSEDIR_VOID */ /* Define to empty if the keyword does not work. */ /* #undef const */ /* Define to one of _getb67, GETB67, getb67 for Cray-2 and Cray-YMP systems. This function is required for alloca.c support on those systems. */ /* #undef CRAY_STACKSEG_END */ /* Define if you have and it should be used (not on Ultrix). */ /* #undef HAVE_ALLOCA_H */ /* Define if you don't have vprintf but do have _doprnt. */ /* #undef HAVE_DOPRNT */ /* Define if your struct stat has st_blksize. */ /* #define HAVE_ST_BLKSIZE 1 */ /* Define if you have . */ /* #undef HAVE_VFORK_H */ /* Define if you have the vprintf function. */ #define HAVE_VPRINTF 1 /* Define if on MINIX. */ /* #undef _MINIX */ /* Define to `int' if doesn't define. */ /* #undef pid_t */ /* Define if the system does not provide POSIX.1 features except with this defined. */ /* #undef _POSIX_1_SOURCE */ /* 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 /* If using the C implementation of alloca, define if you know the direction of stack growth for your system; otherwise it will be automatically deduced at run-time. STACK_DIRECTION > 0 => grows toward higher addresses STACK_DIRECTION < 0 => grows toward lower addresses STACK_DIRECTION = 0 => direction of growth unknown */ /* #undef STACK_DIRECTION */ /* Define if the `S_IS*' macros in do not work properly. */ /* #undef STAT_MACROS_BROKEN */ /* Define if you have the ANSI C header files. */ #define STDC_HEADERS 1 /* Define if is compatible with Posix applications. */ #define HAVE_SYS_WAIT_H 1 /* Define vfork as fork if vfork does not work. */ /* #undef vfork */ /* Define if you have the dup2 function. */ #define HAVE_DUP2 1 /* Define if you have the memchr function. */ #define HAVE_MEMCHR 1 /* Define if you have the sigaction function. */ #define HAVE_SIGACTION 1 /* Define if you have the strchr function. */ #define HAVE_STRCHR 1 /* Define if you have the strerror function. */ #define HAVE_STRERROR 1 /* Define if you have the tmpnam function. */ #define HAVE_TMPNAM 1 /* Define if you have the header file. */ #define HAVE_DIRENT_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. */ /* #undef HAVE_NDIR_H */ /* 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. */ /* #undef HAVE_SYS_DIR_H */ /* Define if you have the header file. */ #define HAVE_SYS_FILE_H 1 /* Define if you have the header file. */ /* #undef HAVE_SYS_NDIR_H */ /* Define if you have the header file. */ #define HAVE_TIME_H 1 /* Define if you have the header file. */ #define HAVE_UNISTD_H 1 /sys/src/ape/cmd/patch/configure 775 sys sys 1367613436 70943 #! /bin/sh # Guess values for system-dependent variables and create Makefiles. # Generated automatically using autoconf version 2.12 # Copyright (C) 1992, 93, 94, 95, 96 Free Software Foundation, Inc. # # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. # Defaults: ac_help= ac_default_prefix=/usr/local # Any additions from configure.in: # Initialize some variables set by options. # The variables have the same names as the options, with # dashes changed to underlines. build=NONE cache_file=./config.cache exec_prefix=NONE host=NONE no_create= nonopt=NONE no_recursion= prefix=NONE program_prefix=NONE program_suffix=NONE program_transform_name=s,x,x, silent= site= srcdir= target=NONE verbose= x_includes=NONE x_libraries=NONE bindir='${exec_prefix}/bin' sbindir='${exec_prefix}/sbin' libexecdir='${exec_prefix}/libexec' datadir='${prefix}/share' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' libdir='${exec_prefix}/lib' includedir='${prefix}/include' oldincludedir='/usr/include' infodir='${prefix}/info' mandir='${prefix}/man' # Initialize some other variables. subdirs= MFLAGS= MAKEFLAGS= # Maximum number of lines to put in a shell here document. ac_max_here_lines=12 ac_prev= 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=`echo "$ac_option" | sed 's/[-_a-zA-Z0-9]*=//'` ;; *) ac_optarg= ;; esac # Accept the important Cygnus configure options, so we can diagnose typos. case "$ac_option" in -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 ;; -build=* | --build=* | --buil=* | --bui=* | --bu=*) build="$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" ;; -datadir | --datadir | --datadi | --datad | --data | --dat | --da) ac_prev=datadir ;; -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \ | --da=*) datadir="$ac_optarg" ;; -disable-* | --disable-*) ac_feature=`echo $ac_option|sed -e 's/-*disable-//'` # Reject names that are not valid shell variable names. if test -n "`echo $ac_feature| sed 's/[-a-zA-Z0-9_]//g'`"; then { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; } fi ac_feature=`echo $ac_feature| sed 's/-/_/g'` eval "enable_${ac_feature}=no" ;; -enable-* | --enable-*) ac_feature=`echo $ac_option|sed -e 's/-*enable-//' -e 's/=.*//'` # Reject names that are not valid shell variable names. if test -n "`echo $ac_feature| sed 's/[-_a-zA-Z0-9]//g'`"; then { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; } fi ac_feature=`echo $ac_feature| sed 's/-/_/g'` case "$ac_option" in *=*) ;; *) ac_optarg=yes ;; esac eval "enable_${ac_feature}='$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) # 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 << EOF Usage: configure [options] [host] Options: [defaults in brackets after descriptions] Configuration: --cache-file=FILE cache test results in FILE --help print this message --no-create do not create output files --quiet, --silent do not print \`checking...' messages --version print the version of autoconf that created configure Directory and file names: --prefix=PREFIX install architecture-independent files in PREFIX [$ac_default_prefix] --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX [same as prefix] --bindir=DIR user executables in DIR [EPREFIX/bin] --sbindir=DIR system admin executables in DIR [EPREFIX/sbin] --libexecdir=DIR program executables in DIR [EPREFIX/libexec] --datadir=DIR read-only architecture-independent data in DIR [PREFIX/share] --sysconfdir=DIR read-only single-machine data in DIR [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data in DIR [PREFIX/com] --localstatedir=DIR modifiable single-machine data in DIR [PREFIX/var] --libdir=DIR object code libraries in DIR [EPREFIX/lib] --includedir=DIR C header files in DIR [PREFIX/include] --oldincludedir=DIR C header files for non-gcc in DIR [/usr/include] --infodir=DIR info documentation in DIR [PREFIX/info] --mandir=DIR man documentation in DIR [PREFIX/man] --srcdir=DIR find the sources in DIR [configure dir or ..] --program-prefix=PREFIX prepend PREFIX to installed program names --program-suffix=SUFFIX append SUFFIX to installed program names --program-transform-name=PROGRAM run sed PROGRAM on installed program names EOF cat << EOF Host type: --build=BUILD configure for building on BUILD [BUILD=HOST] --host=HOST configure for HOST [guessed] --target=TARGET configure for TARGET [TARGET=HOST] Features and packages: --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) --enable-FEATURE[=ARG] include FEATURE [ARG=yes] --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) --x-includes=DIR X include files are in DIR --x-libraries=DIR X library files are in DIR EOF if test -n "$ac_help"; then echo "--enable and --with options recognized:$ac_help" fi exit 0 ;; -host | --host | --hos | --ho) ac_prev=host ;; -host=* | --host=* | --hos=* | --ho=*) host="$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" ;; -localstatedir | --localstatedir | --localstatedi | --localstated \ | --localstate | --localstat | --localsta | --localst \ | --locals | --local | --loca | --loc | --lo) ac_prev=localstatedir ;; -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ | --localstate=* | --localstat=* | --localsta=* | --localst=* \ | --locals=* | --local=* | --loca=* | --loc=* | --lo=*) 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) 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" ;; -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 ;; -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) target="$ac_optarg" ;; -v | -verbose | --verbose | --verbos | --verbo | --verb) verbose=yes ;; -version | --version | --versio | --versi | --vers) echo "configure generated by autoconf version 2.12" exit 0 ;; -with-* | --with-*) ac_package=`echo $ac_option|sed -e 's/-*with-//' -e 's/=.*//'` # Reject names that are not valid shell variable names. if test -n "`echo $ac_package| sed 's/[-_a-zA-Z0-9]//g'`"; then { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; } fi ac_package=`echo $ac_package| sed 's/-/_/g'` case "$ac_option" in *=*) ;; *) ac_optarg=yes ;; esac eval "with_${ac_package}='$ac_optarg'" ;; -without-* | --without-*) ac_package=`echo $ac_option|sed -e 's/-*without-//'` # Reject names that are not valid shell variable names. if test -n "`echo $ac_package| sed 's/[-a-zA-Z0-9_]//g'`"; then { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; } fi ac_package=`echo $ac_package| sed 's/-/_/g'` eval "with_${ac_package}=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" ;; -*) { echo "configure: error: $ac_option: invalid option; use --help to show usage" 1>&2; exit 1; } ;; *) if test -n "`echo $ac_option| sed 's/[-a-z0-9.]//g'`"; then echo "configure: warning: $ac_option: invalid host type" 1>&2 fi if test "x$nonopt" != xNONE; then { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; } fi nonopt="$ac_option" ;; esac done if test -n "$ac_prev"; then { echo "configure: error: missing argument to --`echo $ac_prev | sed 's/_/-/g'`" 1>&2; exit 1; } fi trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15 # File descriptor usage: # 0 standard input # 1 file creation # 2 errors and warnings # 3 some systems may open it to /dev/tty # 4 used on the Kubota Titan # 6 checking for... messages and results # 5 compiler messages saved in config.log if test "$silent" = yes; then exec 6>/dev/null else exec 6>&1 fi exec 5>./config.log echo "\ This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. " 1>&5 # Strip out --no-create and --no-recursion so they do not pile up. # Also quote any args containing shell metacharacters. ac_configure_args= for ac_arg do case "$ac_arg" in -no-create | --no-create | --no-creat | --no-crea | --no-cre \ | --no-cr | --no-c) ;; -no-recursion | --no-recursion | --no-recursio | --no-recursi \ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) ;; *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?]*) ac_configure_args="$ac_configure_args '$ac_arg'" ;; *) ac_configure_args="$ac_configure_args $ac_arg" ;; esac done # NLS nuisances. # Only set these to C if already set. These must not be set unconditionally # because not all systems understand e.g. LANG=C (notably SCO). # Fixing LC_MESSAGES prevents Solaris sh from translating var values in `set'! # Non-C LC_CTYPE values break the ctype check. if test "${LANG+set}" = set; then LANG=C; export LANG; fi if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi if test "${LC_MESSAGES+set}" = set; then LC_MESSAGES=C; export LC_MESSAGES; fi if test "${LC_CTYPE+set}" = set; then LC_CTYPE=C; export LC_CTYPE; fi # confdefs.h avoids OS command line length limits that DEFS can exceed. rm -rf conftest* confdefs.h # AIX cpp loses on an empty file, so make sure it contains at least a newline. echo > confdefs.h # A filename unique to this package, relative to the directory that # configure is in, which we can look for to find out if srcdir is correct. ac_unique_file=patch.c # 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 its parent. ac_prog=$0 ac_confdir=`echo $ac_prog|sed 's%/[^/][^/]*$%%'` test "x$ac_confdir" = "x$ac_prog" && ac_confdir=. 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 if test "$ac_srcdir_defaulted" = yes; then { echo "configure: error: can not find sources in $ac_confdir or .." 1>&2; exit 1; } else { echo "configure: error: can not find sources in $srcdir" 1>&2; exit 1; } fi fi srcdir=`echo "${srcdir}" | sed 's%\([^/]\)/*$%\1%'` # Prefer explicitly selected file to automatically selected ones. if test -z "$CONFIG_SITE"; then if test "x$prefix" != xNONE; then CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site" else CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" fi fi for ac_site_file in $CONFIG_SITE; do if test -r "$ac_site_file"; then echo "loading site script $ac_site_file" . "$ac_site_file" fi done if test -r "$cache_file"; then echo "loading cache $cache_file" . $cache_file else echo "creating cache $cache_file" > $cache_file fi ac_ext=c # CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. ac_cpp='$CPP $CPPFLAGS' ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' ac_link='${CC-cc} -o conftest $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' cross_compiling=$ac_cv_prog_cc_cross if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then # Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu. if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then ac_n= ac_c=' ' ac_t=' ' else ac_n=-n ac_c= ac_t= fi else ac_n= ac_c='\c' ac_t= fi if test "$program_transform_name" = s,x,x,; then program_transform_name= else # Double any \ or $. echo might interpret backslashes. cat <<\EOF_SED > conftestsed s,\\,\\\\,g; s,\$,$$,g EOF_SED program_transform_name="`echo $program_transform_name|sed -f conftestsed`" rm -f conftestsed fi test "$program_prefix" != NONE && program_transform_name="s,^,${program_prefix},; $program_transform_name" # Use a double $ so make ignores it. test "$program_suffix" != NONE && program_transform_name="s,\$\$,${program_suffix},; $program_transform_name" # sed with no file args requires a program. test "$program_transform_name" = "" && program_transform_name="s,x,x," PACKAGE=patch VERSION=2.5 # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 echo "configure:551: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:" for ac_dir in $PATH; do test -z "$ac_dir" && ac_dir=. if test -f $ac_dir/$ac_word; then ac_cv_prog_CC="gcc" break fi done IFS="$ac_save_ifs" fi fi CC="$ac_cv_prog_CC" if test -n "$CC"; then echo "$ac_t""$CC" 1>&6 else echo "$ac_t""no" 1>&6 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 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 echo "configure:580: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:" ac_prog_rejected=no for ac_dir in $PATH; do test -z "$ac_dir" && ac_dir=. if test -f $ac_dir/$ac_word; then if test "$ac_dir/$ac_word" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue fi ac_cv_prog_CC="cc" break fi done IFS="$ac_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 $# -gt 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 set dummy "$ac_dir/$ac_word" "$@" shift ac_cv_prog_CC="$@" fi fi fi fi CC="$ac_cv_prog_CC" if test -n "$CC"; then echo "$ac_t""$CC" 1>&6 else echo "$ac_t""no" 1>&6 fi test -z "$CC" && { echo "configure: error: no acceptable cc found in \$PATH" 1>&2; exit 1; } fi echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works""... $ac_c" 1>&6 echo "configure:628: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5 ac_ext=c # CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. ac_cpp='$CPP $CPPFLAGS' ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' ac_link='${CC-cc} -o conftest $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' cross_compiling=$ac_cv_prog_cc_cross cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then ac_cv_prog_cc_works=yes # If we can't run a trivial program, we are probably using a cross compiler. if (./conftest; exit) 2>/dev/null; then ac_cv_prog_cc_cross=no else ac_cv_prog_cc_cross=yes fi else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 ac_cv_prog_cc_works=no fi rm -fr conftest* echo "$ac_t""$ac_cv_prog_cc_works" 1>&6 if test $ac_cv_prog_cc_works = no; then { echo "configure: error: installation or configuration problem: C compiler cannot create executables." 1>&2; exit 1; } fi echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6 echo "configure:662: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5 echo "$ac_t""$ac_cv_prog_cc_cross" 1>&6 cross_compiling=$ac_cv_prog_cc_cross echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6 echo "configure:667: checking whether we are using GNU C" >&5 if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.c <&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then ac_cv_prog_gcc=yes else ac_cv_prog_gcc=no fi fi echo "$ac_t""$ac_cv_prog_gcc" 1>&6 if test $ac_cv_prog_gcc = yes; then GCC=yes ac_test_CFLAGS="${CFLAGS+set}" ac_save_CFLAGS="$CFLAGS" CFLAGS= echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6 echo "configure:691: checking whether ${CC-cc} accepts -g" >&5 if eval "test \"`echo '$''{'ac_cv_prog_cc_g'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else echo 'void f(){}' > conftest.c if test -z "`${CC-cc} -g -c conftest.c 2>&1`"; then ac_cv_prog_cc_g=yes else ac_cv_prog_cc_g=no fi rm -f conftest* fi echo "$ac_t""$ac_cv_prog_cc_g" 1>&6 if test "$ac_test_CFLAGS" = set; then CFLAGS="$ac_save_CFLAGS" elif test $ac_cv_prog_cc_g = yes; then CFLAGS="-g -O2" else CFLAGS="-O2" fi else GCC= test "${CFLAGS+set}" = set || CFLAGS="-g" fi echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&6 echo "configure:719: checking how to run the C preprocessor" >&5 # On Suns, sometimes $CPP names a directory. if test -n "$CPP" && test -d "$CPP"; then CPP= fi if test -z "$CPP"; then if eval "test \"`echo '$''{'ac_cv_prog_CPP'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else # This must be in double quotes, not single quotes, because CPP may get # substituted into the Makefile and "${CC-cc}" will confuse make. CPP="${CC-cc} -E" # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. cat > conftest.$ac_ext < Syntax Error EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" { (eval echo configure:740: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out` if test -z "$ac_err"; then : else echo "$ac_err" >&5 echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* CPP="${CC-cc} -E -traditional-cpp" cat > conftest.$ac_ext < Syntax Error EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" { (eval echo configure:757: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out` if test -z "$ac_err"; then : else echo "$ac_err" >&5 echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* CPP=/lib/cpp fi rm -f conftest* fi rm -f conftest* ac_cv_prog_CPP="$CPP" fi CPP="$ac_cv_prog_CPP" else ac_cv_prog_CPP="$CPP" fi echo "$ac_t""$CPP" 1>&6 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 fi done if test -z "$ac_aux_dir"; then { echo "configure: error: can not find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." 1>&2; exit 1; } fi ac_config_guess=$ac_aux_dir/config.guess ac_config_sub=$ac_aux_dir/config.sub ac_configure=$ac_aux_dir/configure # This should be Cygnus configure. # 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 # AFS /usr/afsws/bin/install, which mishandles nonexistent args # SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" # ./install, which can be erroneously created by make from ./install.sh. echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6 echo "configure:809: checking for a BSD compatible install" >&5 if test -z "$INSTALL"; then if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else IFS="${IFS= }"; ac_save_IFS="$IFS"; IFS="${IFS}:" for ac_dir in $PATH; do # Account for people who put trailing slashes in PATH elements. case "$ac_dir/" in /|./|.//|/etc/*|/usr/sbin/*|/usr/etc/*|/sbin/*|/usr/afsws/bin/*|/usr/ucb/*) ;; *) # OSF1 and SCO ODT 3.0 have their own names for install. for ac_prog in ginstall installbsd scoinst install; do if test -f $ac_dir/$ac_prog; then if test $ac_prog = install && grep dspmsg $ac_dir/$ac_prog >/dev/null 2>&1; then # AIX install. It has an incompatible calling convention. # OSF/1 installbsd also uses dspmsg, but is usable. : else ac_cv_path_install="$ac_dir/$ac_prog -c" break 2 fi fi done ;; esac done IFS="$ac_save_IFS" 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. We don't cache a # path for INSTALL within a source directory, because that will # break other packages using the cache if that directory is # removed, or if the path is relative. INSTALL="$ac_install_sh" fi fi echo "$ac_t""$INSTALL" 1>&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_DATA" && INSTALL_DATA='${INSTALL} -m 644' echo $ac_n "checking whether ${MAKE-make} sets \${MAKE}""... $ac_c" 1>&6 echo "configure:859: checking whether ${MAKE-make} sets \${MAKE}" >&5 set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_prog_make_${ac_make}_set'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftestmake <<\EOF all: @echo 'ac_maketemp="${MAKE}"' EOF # GNU make sometimes prints "make[1]: Entering...", which would confuse us. eval `${MAKE-make} -f conftestmake 2>/dev/null | grep temp=` if test -n "$ac_maketemp"; then eval ac_cv_prog_make_${ac_make}_set=yes else eval ac_cv_prog_make_${ac_make}_set=no fi rm -f conftestmake fi if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then echo "$ac_t""yes" 1>&6 SET_MAKE= else echo "$ac_t""no" 1>&6 SET_MAKE="MAKE=${MAKE-make}" fi # Use ed_PROGRAM, not ED_PROGRAM, # because reserves symbols starting with `E'. # Extract the first word of "ed", so it can be a program name with args. set dummy ed; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 echo "configure:890: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_path_ed_PROGRAM'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else case "$ed_PROGRAM" in /*) ac_cv_path_ed_PROGRAM="$ed_PROGRAM" # Let the user override the test with a path. ;; *) IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:" for ac_dir in $PATH; do test -z "$ac_dir" && ac_dir=. if test -f $ac_dir/$ac_word; then ac_cv_path_ed_PROGRAM="$ac_dir/$ac_word" break fi done IFS="$ac_save_ifs" test -z "$ac_cv_path_ed_PROGRAM" && ac_cv_path_ed_PROGRAM="ed" ;; esac fi ed_PROGRAM="$ac_cv_path_ed_PROGRAM" if test -n "$ed_PROGRAM"; then echo "$ac_t""$ed_PROGRAM" 1>&6 else echo "$ac_t""no" 1>&6 fi # If available, prefer support for large files unless the user specified # one of the CPPFLAGS, LDFLAGS, or LIBS variables. echo $ac_n "checking whether large file support needs explicit enabling""... $ac_c" 1>&6 echo "configure:923: checking whether large file support needs explicit enabling" >&5 ac_getconfs='' ac_result=yes ac_set='' ac_shellvars='CPPFLAGS LDFLAGS LIBS' for ac_shellvar in $ac_shellvars; do case $ac_shellvar in CPPFLAGS) ac_lfsvar=LFS_CFLAGS ;; *) ac_lfsvar=LFS_$ac_shellvar ;; esac eval test '"${'$ac_shellvar'+set}"' = set && ac_set=$ac_shellvar (getconf $ac_lfsvar) >/dev/null 2>&1 || { ac_result=no; break; } ac_getconf=`getconf $ac_lfsvar` ac_getconfs=$ac_getconfs$ac_getconf eval ac_test_$ac_shellvar=\$ac_getconf done case "$ac_result$ac_getconfs" in yes) ac_result=no ;; esac case "$ac_result$ac_set" in yes?*) ac_result="yes, but $ac_set is already set, so use its settings" esac echo "$ac_t""$ac_result" 1>&6 case $ac_result in yes) for ac_shellvar in $ac_shellvars; do eval $ac_shellvar=\$ac_test_$ac_shellvar done ;; esac echo $ac_n "checking for AIX""... $ac_c" 1>&6 echo "configure:954: checking for AIX" >&5 cat > conftest.$ac_ext <&5 | egrep "yes" >/dev/null 2>&1; then rm -rf conftest* echo "$ac_t""yes" 1>&6; cat >> confdefs.h <<\EOF #define _ALL_SOURCE 1 EOF else rm -rf conftest* echo "$ac_t""no" 1>&6 fi rm -f conftest* ac_safe=`echo "minix/config.h" | sed 'y%./+-%__p_%'` echo $ac_n "checking for minix/config.h""... $ac_c" 1>&6 echo "configure:979: checking for minix/config.h" >&5 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" { (eval echo configure:989: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out` if test -z "$ac_err"; then rm -rf conftest* eval "ac_cv_header_$ac_safe=yes" else echo "$ac_err" >&5 echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_header_$ac_safe=no" fi rm -f conftest* fi if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then echo "$ac_t""yes" 1>&6 MINIX=yes else echo "$ac_t""no" 1>&6 MINIX= fi if test "$MINIX" = yes; then cat >> confdefs.h <<\EOF #define _POSIX_SOURCE 1 EOF cat >> confdefs.h <<\EOF #define _POSIX_1_SOURCE 2 EOF cat >> confdefs.h <<\EOF #define _MINIX 1 EOF fi echo $ac_n "checking for POSIXized ISC""... $ac_c" 1>&6 echo "configure:1027: checking for POSIXized ISC" >&5 if test -d /etc/conf/kconfig.d && grep _POSIX_VERSION /usr/include/sys/unistd.h >/dev/null 2>&1 then echo "$ac_t""yes" 1>&6 ISC=yes # If later tests want to check for ISC. cat >> confdefs.h <<\EOF #define _POSIX_SOURCE 1 EOF if test "$GCC" = yes; then CC="$CC -posix" else CC="$CC -Xp" fi else echo "$ac_t""no" 1>&6 ISC= fi echo $ac_n "checking for working const""... $ac_c" 1>&6 echo "configure:1049: checking for working const" >&5 if eval "test \"`echo '$''{'ac_cv_c_const'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <j = 5; } { /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ const int foo = 10; } ; return 0; } EOF if { (eval echo configure:1103: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* ac_cv_c_const=yes else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* ac_cv_c_const=no fi rm -f conftest* fi echo "$ac_t""$ac_cv_c_const" 1>&6 if test $ac_cv_c_const = no; then cat >> confdefs.h <<\EOF #define const EOF fi ac_header_dirent=no for ac_hdr in dirent.h sys/ndir.h sys/dir.h ndir.h do ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` echo $ac_n "checking for $ac_hdr that defines DIR""... $ac_c" 1>&6 echo "configure:1129: checking for $ac_hdr that defines DIR" >&5 if eval "test \"`echo '$''{'ac_cv_header_dirent_$ac_safe'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < #include <$ac_hdr> int main() { DIR *dirp = 0; ; return 0; } EOF if { (eval echo configure:1142: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* eval "ac_cv_header_dirent_$ac_safe=yes" else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_header_dirent_$ac_safe=no" fi rm -f conftest* fi if eval "test \"`echo '$ac_cv_header_dirent_'$ac_safe`\" = yes"; then echo "$ac_t""yes" 1>&6 ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` cat >> confdefs.h <&6 fi done # Two versions of opendir et al. are in -ldir and -lx on SCO Xenix. if test $ac_header_dirent = dirent.h; then echo $ac_n "checking for opendir in -ldir""... $ac_c" 1>&6 echo "configure:1167: checking for opendir in -ldir" >&5 ac_lib_var=`echo dir'_'opendir | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else ac_save_LIBS="$LIBS" LIBS="-ldir $LIBS" cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=no" fi rm -f conftest* LIBS="$ac_save_LIBS" fi if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then echo "$ac_t""yes" 1>&6 LIBS="$LIBS -ldir" else echo "$ac_t""no" 1>&6 fi else echo $ac_n "checking for opendir in -lx""... $ac_c" 1>&6 echo "configure:1208: checking for opendir in -lx" >&5 ac_lib_var=`echo x'_'opendir | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else ac_save_LIBS="$LIBS" LIBS="-lx $LIBS" cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=no" fi rm -f conftest* LIBS="$ac_save_LIBS" fi if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then echo "$ac_t""yes" 1>&6 LIBS="$LIBS -lx" else echo "$ac_t""no" 1>&6 fi fi echo $ac_n "checking for ANSI C header files""... $ac_c" 1>&6 echo "configure:1250: checking for ANSI C header files" >&5 if eval "test \"`echo '$''{'ac_cv_header_stdc'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < #include #include #include EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" { (eval echo configure:1263: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out` if test -z "$ac_err"; then rm -rf conftest* ac_cv_header_stdc=yes else echo "$ac_err" >&5 echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* ac_cv_header_stdc=no fi rm -f conftest* if test $ac_cv_header_stdc = yes; then # SunOS 4.x string.h does not declare mem*, contrary to ANSI. cat > conftest.$ac_ext < EOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | egrep "memchr" >/dev/null 2>&1; then : else rm -rf conftest* 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 > conftest.$ac_ext < EOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | egrep "free" >/dev/null 2>&1; then : else rm -rf conftest* 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 > conftest.$ac_ext < #define ISLOWER(c) ('a' <= (c) && (c) <= 'z') #define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) #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)) exit(2); exit (0); } EOF if { (eval echo configure:1330: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null then : else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -fr conftest* ac_cv_header_stdc=no fi rm -fr conftest* fi fi fi echo "$ac_t""$ac_cv_header_stdc" 1>&6 if test $ac_cv_header_stdc = yes; then cat >> confdefs.h <<\EOF #define STDC_HEADERS 1 EOF fi for ac_hdr in fcntl.h limits.h string.h unistd.h utime.h varargs.h do ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 echo "configure:1357: checking for $ac_hdr" >&5 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" { (eval echo configure:1367: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out` if test -z "$ac_err"; then rm -rf conftest* eval "ac_cv_header_$ac_safe=yes" else echo "$ac_err" >&5 echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_header_$ac_safe=no" fi rm -f conftest* fi if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then echo "$ac_t""yes" 1>&6 ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` cat >> confdefs.h <&6 fi done echo $ac_n "checking for mode_t""... $ac_c" 1>&6 echo "configure:1395: checking for mode_t" >&5 if eval "test \"`echo '$''{'ac_cv_type_mode_t'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < #if STDC_HEADERS #include #include #endif EOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | egrep "mode_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then rm -rf conftest* ac_cv_type_mode_t=yes else rm -rf conftest* ac_cv_type_mode_t=no fi rm -f conftest* fi echo "$ac_t""$ac_cv_type_mode_t" 1>&6 if test $ac_cv_type_mode_t = no; then cat >> confdefs.h <<\EOF #define mode_t int EOF fi echo $ac_n "checking for off_t""... $ac_c" 1>&6 echo "configure:1428: checking for off_t" >&5 if eval "test \"`echo '$''{'ac_cv_type_off_t'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < #if STDC_HEADERS #include #include #endif EOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | egrep "off_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then rm -rf conftest* ac_cv_type_off_t=yes else rm -rf conftest* ac_cv_type_off_t=no fi rm -f conftest* fi echo "$ac_t""$ac_cv_type_off_t" 1>&6 if test $ac_cv_type_off_t = no; then cat >> confdefs.h <<\EOF #define off_t long EOF fi echo $ac_n "checking return type of signal handlers""... $ac_c" 1>&6 echo "configure:1461: checking return type of signal handlers" >&5 if eval "test \"`echo '$''{'ac_cv_type_signal'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < #include #ifdef signal #undef signal #endif #ifdef __cplusplus extern "C" void (*signal (int, void (*)(int)))(int); #else void (*signal ()) (); #endif int main() { int i; ; return 0; } EOF if { (eval echo configure:1483: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* ac_cv_type_signal=void else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* ac_cv_type_signal=int fi rm -f conftest* fi echo "$ac_t""$ac_cv_type_signal" 1>&6 cat >> confdefs.h <&6 echo "configure:1502: checking for size_t" >&5 if eval "test \"`echo '$''{'ac_cv_type_size_t'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < #if STDC_HEADERS #include #include #endif EOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | egrep "size_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then rm -rf conftest* ac_cv_type_size_t=yes else rm -rf conftest* ac_cv_type_size_t=no fi rm -f conftest* fi echo "$ac_t""$ac_cv_type_size_t" 1>&6 if test $ac_cv_type_size_t = no; then cat >> confdefs.h <<\EOF #define size_t unsigned EOF fi echo $ac_n "checking for struct utimbuf""... $ac_c" 1>&6 echo "configure:1536: checking for struct utimbuf" >&5 if eval "test \"`echo '$''{'patch_cv_sys_struct_utimbuf'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < #if HAVE_UTIME_H #include #endif int main() { static struct utimbuf x; x.actime = x.modtime; ; return 0; } EOF if { (eval echo configure:1551: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* patch_cv_sys_struct_utimbuf=yes else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* patch_cv_sys_struct_utimbuf=no fi rm -f conftest* fi echo "$ac_t""$patch_cv_sys_struct_utimbuf" 1>&6 if test $patch_cv_sys_struct_utimbuf = yes; then cat >> confdefs.h <<\EOF #define HAVE_STRUCT_UTIMBUF 1 EOF fi # Check for NetBSD 1.0 bug, where memchr(..., 0) returns nonzero. echo $ac_n "checking for working memchr""... $ac_c" 1>&6 echo "configure:1573: checking for working memchr" >&5 if eval "test \"`echo '$''{'ac_cv_func_memchr'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else if test "$cross_compiling" = yes; then echo "configure: warning: We are cross-compiling so we assume memchr does not work." 1>&2 ac_cv_func_memchr=no else cat > conftest.$ac_ext < main () { exit (memchr ("", 0, 0) != 0 || memchr ("", 1, 0) != 0); } EOF if { (eval echo configure:1587: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null then ac_cv_func_memchr=yes else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -fr conftest* ac_cv_func_memchr=no fi rm -fr conftest* fi fi echo "$ac_t""$ac_cv_func_memchr" 1>&6 if test $ac_cv_func_memchr = yes; then cat >> confdefs.h <<\EOF #define HAVE_MEMCHR 1 EOF fi echo $ac_n "checking for getopt_long""... $ac_c" 1>&6 echo "configure:1609: checking for getopt_long" >&5 if eval "test \"`echo '$''{'ac_cv_func_getopt_long'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < /* Override any gcc2 internal prototype to avoid an error. */ /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char getopt_long(); int main() { /* 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_getopt_long) || defined (__stub___getopt_long) choke me #else getopt_long(); #endif ; return 0; } EOF if { (eval echo configure:1637: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then rm -rf conftest* eval "ac_cv_func_getopt_long=yes" else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_func_getopt_long=no" fi rm -f conftest* fi if eval "test \"`echo '$ac_cv_func_'getopt_long`\" = yes"; then echo "$ac_t""yes" 1>&6 : else echo "$ac_t""no" 1>&6 LIBOBJS="$LIBOBJS getopt1.o getopt.o" fi for ac_func in _doprintf isascii memcmp mkdir mktemp pathconf raise sigaction sigprocmask sigsetmask do echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 echo "configure:1661: checking for $ac_func" >&5 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < /* Override any gcc2 internal prototype to avoid an error. */ /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char $ac_func(); int main() { /* 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_$ac_func) || defined (__stub___$ac_func) choke me #else $ac_func(); #endif ; return 0; } EOF if { (eval echo configure:1689: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then rm -rf conftest* eval "ac_cv_func_$ac_func=yes" else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_func_$ac_func=no" fi rm -f conftest* fi if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then echo "$ac_t""yes" 1>&6 ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` cat >> confdefs.h <&6 fi done for ac_func in memchr rename do echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 echo "configure:1716: checking for $ac_func" >&5 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < /* Override any gcc2 internal prototype to avoid an error. */ /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char $ac_func(); int main() { /* 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_$ac_func) || defined (__stub___$ac_func) choke me #else $ac_func(); #endif ; return 0; } EOF if { (eval echo configure:1744: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then rm -rf conftest* eval "ac_cv_func_$ac_func=yes" else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_func_$ac_func=no" fi rm -f conftest* fi if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then echo "$ac_t""yes" 1>&6 ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` cat >> confdefs.h <&6 LIBOBJS="$LIBOBJS ${ac_func}.o" fi done echo $ac_n "checking whether closedir returns void""... $ac_c" 1>&6 echo "configure:1771: checking whether closedir returns void" >&5 if eval "test \"`echo '$''{'ac_cv_func_closedir_void'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else if test "$cross_compiling" = yes; then ac_cv_func_closedir_void=yes else cat > conftest.$ac_ext < #include <$ac_header_dirent> int closedir(); main() { exit(closedir(opendir(".")) != 0); } EOF if { (eval echo configure:1785: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null then ac_cv_func_closedir_void=no else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -fr conftest* ac_cv_func_closedir_void=yes fi rm -fr conftest* fi fi echo "$ac_t""$ac_cv_func_closedir_void" 1>&6 if test $ac_cv_func_closedir_void = yes; then cat >> confdefs.h <<\EOF #define CLOSEDIR_VOID 1 EOF fi echo $ac_n "checking for vprintf""... $ac_c" 1>&6 echo "configure:1808: checking for vprintf" >&5 if eval "test \"`echo '$''{'ac_cv_func_vprintf'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < /* Override any gcc2 internal prototype to avoid an error. */ /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char vprintf(); int main() { /* 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_vprintf) || defined (__stub___vprintf) choke me #else vprintf(); #endif ; return 0; } EOF if { (eval echo configure:1836: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then rm -rf conftest* eval "ac_cv_func_vprintf=yes" else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_func_vprintf=no" fi rm -f conftest* fi if eval "test \"`echo '$ac_cv_func_'vprintf`\" = yes"; then echo "$ac_t""yes" 1>&6 cat >> confdefs.h <<\EOF #define HAVE_VPRINTF 1 EOF else echo "$ac_t""no" 1>&6 fi if test "$ac_cv_func_vprintf" != yes; then echo $ac_n "checking for _doprnt""... $ac_c" 1>&6 echo "configure:1860: checking for _doprnt" >&5 if eval "test \"`echo '$''{'ac_cv_func__doprnt'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < /* Override any gcc2 internal prototype to avoid an error. */ /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char _doprnt(); int main() { /* 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__doprnt) || defined (__stub____doprnt) choke me #else _doprnt(); #endif ; return 0; } EOF if { (eval echo configure:1888: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then rm -rf conftest* eval "ac_cv_func__doprnt=yes" else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_func__doprnt=no" fi rm -f conftest* fi if eval "test \"`echo '$ac_cv_func_'_doprnt`\" = yes"; then echo "$ac_t""yes" 1>&6 cat >> confdefs.h <<\EOF #define HAVE_DOPRNT 1 EOF else echo "$ac_t""no" 1>&6 fi fi echo $ac_n "checking for long file names""... $ac_c" 1>&6 echo "configure:1914: checking for long file names" >&5 if eval "test \"`echo '$''{'ac_cv_sys_long_file_names'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else ac_cv_sys_long_file_names=yes # Test for long file names in all the places we know might matter: # . the current directory, where building will happen # $prefix/lib where we will be installing things # $exec_prefix/lib likewise # eval it to expand exec_prefix. # $TMPDIR if set, where it might want to write temporary files # if $TMPDIR is not set: # /tmp where it might want to write temporary files # /var/tmp likewise # /usr/tmp likewise if test -n "$TMPDIR" && test -d "$TMPDIR" && test -w "$TMPDIR"; then ac_tmpdirs="$TMPDIR" else ac_tmpdirs='/tmp /var/tmp /usr/tmp' fi for ac_dir in . $ac_tmpdirs `eval echo $prefix/lib $exec_prefix/lib` ; do test -d $ac_dir || continue test -w $ac_dir || continue # It is less confusing to not echo anything here. (echo 1 > $ac_dir/conftest9012345) 2>/dev/null (echo 2 > $ac_dir/conftest9012346) 2>/dev/null val=`cat $ac_dir/conftest9012345 2>/dev/null` if test ! -f $ac_dir/conftest9012345 || test "$val" != 1; then ac_cv_sys_long_file_names=no rm -f $ac_dir/conftest9012345 $ac_dir/conftest9012346 2>/dev/null break fi rm -f $ac_dir/conftest9012345 $ac_dir/conftest9012346 2>/dev/null done fi echo "$ac_t""$ac_cv_sys_long_file_names" 1>&6 if test $ac_cv_sys_long_file_names = yes; then cat >> confdefs.h <<\EOF #define HAVE_LONG_FILE_NAMES 1 EOF fi echo $ac_n "checking for d_ino member in directory struct""... $ac_c" 1>&6 echo "configure:1959: checking for d_ino member in directory struct" >&5 if eval "test \"`echo '$''{'patch_cv_sys_d_ino_in_dirent'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < #if HAVE_DIRENT_H # include #else # define dirent direct # if HAVE_SYS_NDIR_H # include # endif # ifdef HAVE_SYS_DIR_H # include # endif # ifdef HAVE_NDIR_H # include # endif #endif int main() { struct dirent dp; dp.d_ino = 0; ; return 0; } EOF if { (eval echo configure:1987: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then rm -rf conftest* patch_cv_sys_d_ino_in_dirent=yes else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* patch_cv_sys_d_ino_in_dirent=no fi rm -f conftest* fi echo "$ac_t""$patch_cv_sys_d_ino_in_dirent" 1>&6 if test $patch_cv_sys_d_ino_in_dirent = yes; then cat >> confdefs.h <<\EOF #define D_INO_IN_DIRENT 1 EOF fi trap '' 1 2 15 cat > confcache <<\EOF # 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. It is not useful on other systems. # If it contains results you don't want to keep, you may remove or edit it. # # By default, configure uses ./config.cache as the cache file, # creating it if it does not exist already. You can give configure # the --cache-file=FILE option to use a different cache file; that is # what configure does when it calls configure scripts in # subdirectories, so they share the cache. # Giving --cache-file=/dev/null disables caching, for debugging configure. # config.status only pays attention to the cache file if you give it the # --recheck option to rerun configure. # EOF # The following way of writing the cache mishandles newlines in values, # but we know of no workaround that is simple, portable, and efficient. # So, don't put newlines in cache variables' values. # 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. (set) 2>&1 | case `(ac_space=' '; set | grep ac_space) 2>&1` in *ac_space=\ *) # `set' does not quote correctly, so add quotes (double-quote substitution # turns \\\\ into \\, and sed turns \\ into \). sed -n \ -e "s/'/'\\\\''/g" \ -e "s/^\\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\\)=\\(.*\\)/\\1=\${\\1='\\2'}/p" ;; *) # `set' quotes correctly as required by POSIX, so do not add quotes. sed -n -e 's/^\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\)=\(.*\)/\1=${\1=\2}/p' ;; esac >> confcache if cmp -s $cache_file confcache; then : else if test -w $cache_file; then echo "updating cache $cache_file" cat confcache > $cache_file else echo "not updating unwritable cache $cache_file" fi fi rm -f confcache trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15 test "x$prefix" = xNONE && prefix=$ac_default_prefix # Let make expand exec_prefix. test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' # Any assignment to VPATH causes Sun make to only execute # the first set of double-colon rules, so remove it if not needed. # If there is a colon in the path, we need to keep it. if test "x$srcdir" = x.; then ac_vpsub='/^[ ]*VPATH[ ]*=[^:]*$/d' fi trap 'rm -f $CONFIG_STATUS conftest*; exit 1' 1 2 15 DEFS=-DHAVE_CONFIG_H # Without the "./", some shells look in PATH for config.status. : ${CONFIG_STATUS=./config.status} echo creating $CONFIG_STATUS rm -f $CONFIG_STATUS cat > $CONFIG_STATUS </dev/null | sed 1q`: # # $0 $ac_configure_args # # Compiler output produced by configure, useful for debugging # configure, is in ./config.log if it exists. ac_cs_usage="Usage: $CONFIG_STATUS [--recheck] [--version] [--help]" for ac_option do case "\$ac_option" in -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) echo "running \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion" exec \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion ;; -version | --version | --versio | --versi | --vers | --ver | --ve | --v) echo "$CONFIG_STATUS generated by autoconf version 2.12" exit 0 ;; -help | --help | --hel | --he | --h) echo "\$ac_cs_usage"; exit 0 ;; *) echo "\$ac_cs_usage"; exit 1 ;; esac done ac_given_srcdir=$srcdir ac_given_INSTALL="$INSTALL" trap 'rm -fr `echo "Makefile config.h:config.hin" | sed "s/:[^ ]*//g"` conftest*; exit 1' 1 2 15 EOF cat >> $CONFIG_STATUS < conftest.subs <<\\CEOF $ac_vpsub $extrasub s%@CFLAGS@%$CFLAGS%g s%@CPPFLAGS@%$CPPFLAGS%g s%@CXXFLAGS@%$CXXFLAGS%g s%@DEFS@%$DEFS%g s%@LDFLAGS@%$LDFLAGS%g s%@LIBS@%$LIBS%g s%@exec_prefix@%$exec_prefix%g s%@prefix@%$prefix%g s%@program_transform_name@%$program_transform_name%g s%@bindir@%$bindir%g s%@sbindir@%$sbindir%g s%@libexecdir@%$libexecdir%g s%@datadir@%$datadir%g s%@sysconfdir@%$sysconfdir%g s%@sharedstatedir@%$sharedstatedir%g s%@localstatedir@%$localstatedir%g s%@libdir@%$libdir%g s%@includedir@%$includedir%g s%@oldincludedir@%$oldincludedir%g s%@infodir@%$infodir%g s%@mandir@%$mandir%g s%@PACKAGE@%$PACKAGE%g s%@VERSION@%$VERSION%g s%@CC@%$CC%g s%@CPP@%$CPP%g s%@INSTALL_PROGRAM@%$INSTALL_PROGRAM%g s%@INSTALL_DATA@%$INSTALL_DATA%g s%@SET_MAKE@%$SET_MAKE%g s%@ed_PROGRAM@%$ed_PROGRAM%g s%@LIBOBJS@%$LIBOBJS%g CEOF EOF cat >> $CONFIG_STATUS <<\EOF # Split the substitutions into bite-sized pieces for seds with # small command number limits, like on Digital OSF/1 and HP-UX. ac_max_sed_cmds=90 # Maximum number of lines to put in a sed script. ac_file=1 # Number of current file. ac_beg=1 # First line for current file. ac_end=$ac_max_sed_cmds # Line after last line for current file. ac_more_lines=: ac_sed_cmds="" while $ac_more_lines; do if test $ac_beg -gt 1; then sed "1,${ac_beg}d; ${ac_end}q" conftest.subs > conftest.s$ac_file else sed "${ac_end}q" conftest.subs > conftest.s$ac_file fi if test ! -s conftest.s$ac_file; then ac_more_lines=false rm -f conftest.s$ac_file else if test -z "$ac_sed_cmds"; then ac_sed_cmds="sed -f conftest.s$ac_file" else ac_sed_cmds="$ac_sed_cmds | sed -f conftest.s$ac_file" fi ac_file=`expr $ac_file + 1` ac_beg=$ac_end ac_end=`expr $ac_end + $ac_max_sed_cmds` fi done if test -z "$ac_sed_cmds"; then ac_sed_cmds=cat fi EOF cat >> $CONFIG_STATUS <> $CONFIG_STATUS <<\EOF for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". case "$ac_file" in *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'` ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;; *) ac_file_in="${ac_file}.in" ;; esac # Adjust a relative srcdir, top_srcdir, and INSTALL for subdirectories. # Remove last slash and all that follows it. Not all systems have dirname. ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'` if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then # The file is in a subdirectory. test ! -d "$ac_dir" && mkdir "$ac_dir" ac_dir_suffix="/`echo $ac_dir|sed 's%^\./%%'`" # A "../" for each directory in $ac_dir_suffix. ac_dots=`echo $ac_dir_suffix|sed 's%/[^/]*%../%g'` else ac_dir_suffix= ac_dots= fi case "$ac_given_srcdir" in .) srcdir=. if test -z "$ac_dots"; then top_srcdir=. else top_srcdir=`echo $ac_dots|sed 's%/$%%'`; fi ;; /*) srcdir="$ac_given_srcdir$ac_dir_suffix"; top_srcdir="$ac_given_srcdir" ;; *) # Relative path. srcdir="$ac_dots$ac_given_srcdir$ac_dir_suffix" top_srcdir="$ac_dots$ac_given_srcdir" ;; esac case "$ac_given_INSTALL" in [/$]*) INSTALL="$ac_given_INSTALL" ;; *) INSTALL="$ac_dots$ac_given_INSTALL" ;; esac echo creating "$ac_file" rm -f "$ac_file" configure_input="Generated automatically from `echo $ac_file_in|sed 's%.*/%%'` by configure." case "$ac_file" in *Makefile*) ac_comsub="1i\\ # $configure_input" ;; *) ac_comsub= ;; esac ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"` sed -e "$ac_comsub s%@configure_input@%$configure_input%g s%@srcdir@%$srcdir%g s%@top_srcdir@%$top_srcdir%g s%@INSTALL@%$INSTALL%g " $ac_file_inputs | (eval "$ac_sed_cmds") > $ac_file fi; done rm -f conftest.s* # These sed commands are passed to sed as "A NAME B NAME C VALUE D", where # NAME is the cpp macro being defined and VALUE is the value it is being given. # # ac_d sets the value in "#define NAME VALUE" lines. ac_dA='s%^\([ ]*\)#\([ ]*define[ ][ ]*\)' ac_dB='\([ ][ ]*\)[^ ]*%\1#\2' ac_dC='\3' ac_dD='%g' # ac_u turns "#undef NAME" with trailing blanks into "#define NAME VALUE". ac_uA='s%^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)' ac_uB='\([ ]\)%\1#\2define\3' ac_uC=' ' ac_uD='\4%g' # ac_e turns "#undef NAME" without trailing blanks into "#define NAME VALUE". ac_eA='s%^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)' ac_eB='$%\1#\2define\3' ac_eC=' ' ac_eD='%g' if test "${CONFIG_HEADERS+set}" != set; then EOF cat >> $CONFIG_STATUS <> $CONFIG_STATUS <<\EOF fi for ac_file in .. $CONFIG_HEADERS; do if test "x$ac_file" != x..; then # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". case "$ac_file" in *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'` ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;; *) ac_file_in="${ac_file}.in" ;; esac echo creating $ac_file rm -f conftest.frag conftest.in conftest.out ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"` cat $ac_file_inputs > conftest.in EOF # Transform confdefs.h into a sed script conftest.vals that substitutes # the proper values into config.h.in to produce config.h. And first: # Protect against being on the right side of a sed subst in config.status. # Protect against being in an unquoted here document in config.status. rm -f conftest.vals cat > conftest.hdr <<\EOF s/[\\&%]/\\&/g s%[\\$`]%\\&%g s%#define \([A-Za-z_][A-Za-z0-9_]*\) *\(.*\)%${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD}%gp s%ac_d%ac_u%gp s%ac_u%ac_e%gp EOF sed -n -f conftest.hdr confdefs.h > conftest.vals rm -f conftest.hdr # This sed command replaces #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. cat >> conftest.vals <<\EOF s%^[ ]*#[ ]*undef[ ][ ]*[a-zA-Z_][a-zA-Z_0-9]*%/* & */% EOF # Break up conftest.vals because some shells have a limit on # the size of here documents, and old seds have small limits too. rm -f conftest.tail while : do ac_lines=`grep -c . conftest.vals` # grep -c gives empty output for an empty file on some AIX systems. if test -z "$ac_lines" || test "$ac_lines" -eq 0; then break; fi # Write a limited-size here document to conftest.frag. echo ' cat > conftest.frag <> $CONFIG_STATUS sed ${ac_max_here_lines}q conftest.vals >> $CONFIG_STATUS echo 'CEOF sed -f conftest.frag conftest.in > conftest.out rm -f conftest.in mv conftest.out conftest.in ' >> $CONFIG_STATUS sed 1,${ac_max_here_lines}d conftest.vals > conftest.tail rm -f conftest.vals mv conftest.tail conftest.vals done rm -f conftest.vals cat >> $CONFIG_STATUS <<\EOF rm -f conftest.frag conftest.h echo "/* $ac_file. Generated automatically by configure. */" > conftest.h cat conftest.in >> conftest.h rm -f conftest.in if cmp -s $ac_file conftest.h 2>/dev/null; then echo "$ac_file is unchanged" rm -f conftest.h else # Remove last slash and all that follows it. Not all systems have dirname. ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'` if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then # The file is in a subdirectory. test ! -d "$ac_dir" && mkdir "$ac_dir" fi rm -f $ac_file mv conftest.h $ac_file fi fi; done EOF cat >> $CONFIG_STATUS <> $CONFIG_STATUS <<\EOF exit 0 EOF chmod +x $CONFIG_STATUS rm -fr confdefs* $ac_clean_files test "$no_create" = yes || ${CONFIG_SHELL-/bin/sh} $CONFIG_STATUS || exit 1 /sys/src/ape/cmd/patch/configure.in 664 sys sys 1367613436 3545 # Configure `patch'. # Copyright 1993, 1997 Free Software Foundation, Inc. dnl Process this file with autoconf to produce a configure script. AC_PREREQ(2.12) AC_INIT(patch.c) AC_CONFIG_HEADER(config.h:config.hin) AC_ARG_PROGRAM PACKAGE=patch VERSION=2.5 AC_SUBST(PACKAGE) AC_SUBST(VERSION) AC_PROG_CC AC_PROG_CPP AC_PROG_INSTALL AC_PROG_MAKE_SET # Use ed_PROGRAM, not ED_PROGRAM, # because reserves symbols starting with `E'. AC_PATH_PROG(ed_PROGRAM, ed, ed) # If available, prefer support for large files unless the user specified # one of the CPPFLAGS, LDFLAGS, or LIBS variables. AC_MSG_CHECKING(whether large file support needs explicit enabling) ac_getconfs='' ac_result=yes ac_set='' ac_shellvars='CPPFLAGS LDFLAGS LIBS' for ac_shellvar in $ac_shellvars; do case $ac_shellvar in CPPFLAGS) ac_lfsvar=LFS_CFLAGS ;; *) ac_lfsvar=LFS_$ac_shellvar ;; esac eval test '"${'$ac_shellvar'+set}"' = set && ac_set=$ac_shellvar (getconf $ac_lfsvar) >/dev/null 2>&1 || { ac_result=no; break; } ac_getconf=`getconf $ac_lfsvar` ac_getconfs=$ac_getconfs$ac_getconf eval ac_test_$ac_shellvar=\$ac_getconf done case "$ac_result$ac_getconfs" in yes) ac_result=no ;; esac case "$ac_result$ac_set" in yes?*) ac_result="yes, but $ac_set is already set, so use its settings" esac AC_MSG_RESULT($ac_result) case $ac_result in yes) for ac_shellvar in $ac_shellvars; do eval $ac_shellvar=\$ac_test_$ac_shellvar done ;; esac AC_AIX AC_MINIX AC_ISC_POSIX AC_C_CONST AC_HEADER_DIRENT AC_HEADER_STDC AC_CHECK_HEADERS(fcntl.h limits.h string.h unistd.h utime.h varargs.h) AC_TYPE_MODE_T AC_TYPE_OFF_T AC_TYPE_SIGNAL AC_TYPE_SIZE_T dnl Some systems have utime.h but don't declare the struct anywhere. AC_MSG_CHECKING(for struct utimbuf) AC_CACHE_VAL(patch_cv_sys_struct_utimbuf, [AC_TRY_COMPILE([#include #if HAVE_UTIME_H #include #endif], [static struct utimbuf x; x.actime = x.modtime;], patch_cv_sys_struct_utimbuf=yes, patch_cv_sys_struct_utimbuf=no)]) AC_MSG_RESULT($patch_cv_sys_struct_utimbuf) if test $patch_cv_sys_struct_utimbuf = yes; then AC_DEFINE(HAVE_STRUCT_UTIMBUF) fi # Check for NetBSD 1.0 bug, where memchr(..., 0) returns nonzero. AC_MSG_CHECKING(for working memchr) AC_CACHE_VAL(ac_cv_func_memchr, [AC_TRY_RUN([#include main () { exit (memchr ("", 0, 0) != 0 || memchr ("", 1, 0) != 0); }], ac_cv_func_memchr=yes, ac_cv_func_memchr=no, AC_MSG_WARN([We are cross-compiling so we assume memchr does not work.]) ac_cv_func_memchr=no)])dnl AC_MSG_RESULT($ac_cv_func_memchr) if test $ac_cv_func_memchr = yes; then AC_DEFINE(HAVE_MEMCHR) fi AC_CHECK_FUNC(getopt_long, , [LIBOBJS="$LIBOBJS getopt1.o getopt.o"]) AC_SUBST(LIBOBJS) AC_CHECK_FUNCS(_doprintf isascii memcmp mkdir mktemp pathconf raise sigaction sigprocmask sigsetmask) AC_REPLACE_FUNCS(memchr rename) AC_FUNC_CLOSEDIR_VOID AC_FUNC_VPRINTF AC_SYS_LONG_FILE_NAMES AC_MSG_CHECKING([for d_ino member in directory struct]) AC_CACHE_VAL(patch_cv_sys_d_ino_in_dirent, [AC_TRY_LINK([ #include #if HAVE_DIRENT_H # include #else # define dirent direct # if HAVE_SYS_NDIR_H # include # endif # ifdef HAVE_SYS_DIR_H # include # endif # ifdef HAVE_NDIR_H # include # endif #endif ], [struct dirent dp; dp.d_ino = 0;], patch_cv_sys_d_ino_in_dirent=yes, patch_cv_sys_d_ino_in_dirent=no)]) AC_MSG_RESULT($patch_cv_sys_d_ino_in_dirent) if test $patch_cv_sys_d_ino_in_dirent = yes; then AC_DEFINE(D_INO_IN_DIRENT) fi AC_OUTPUT(Makefile) /sys/src/ape/cmd/patch/getopt.c 664 sys sys 1367613436 30164 /* Getopt for GNU. NOTE: getopt is now part of the C library, so if you don't know what "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu before changing it! Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97 Free Software Foundation, Inc. NOTE: The canonical source of this file is maintained with the GNU C Library. Bugs can be reported to bug-glibc@prep.ai.mit.edu. 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* This tells Alpha OSF/1 not to define a getopt prototype in . Ditto for AIX 3.2 and . */ #ifndef _NO_PROTO #define _NO_PROTO #endif #ifdef HAVE_CONFIG_H #include #endif #if !defined (__STDC__) || !__STDC__ /* This is a separate conditional since some stdc systems reject `defined (const)'. */ #ifndef const #define const #endif #endif #include #include /* Comment out all this code if we are using the GNU C Library, and are not actually compiling the library itself. This code is part of the GNU C Library, but also included in many other GNU distributions. Compiling and linking in this code is a waste when using the GNU C library (especially if it is a shared library). Rather than having every GNU program understand `configure --with-gnu-libc' and omit the object files, it is simpler to just do this in the source for each such file. */ #define GETOPT_INTERFACE_VERSION 2 #if !defined (_LIBC) && defined (__GLIBC__) && __GLIBC__ >= 2 #include #if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION #define ELIDE_CODE #endif #endif #ifndef ELIDE_CODE /* This needs to come after some library #include to get __GNU_LIBRARY__ defined. */ #ifdef __GNU_LIBRARY__ /* Don't include stdlib.h for non-GNU C libraries because some of them contain conflicting prototypes for getopt. */ #include #include #endif /* GNU C library. */ #ifdef VMS #include #if HAVE_STRING_H - 0 #include #endif #endif #if defined (WIN32) && !defined (__CYGWIN32__) /* It's not Unix, really. See? Capital letters. */ #include #define getpid() GetCurrentProcessId() #endif #ifndef _ /* This is for other GNU distributions with internationalized messages. When compiling libc, the _ macro is predefined. */ #ifdef HAVE_LIBINTL_H # include # define _(msgid) gettext (msgid) #else # define _(msgid) (msgid) #endif #endif /* This version of `getopt' appears to the caller like standard Unix `getopt' but it behaves differently for the user, since it allows the user to intersperse the options with the other arguments. As `getopt' works, it permutes the elements of ARGV so that, when it is done, all the options precede everything else. Thus all application programs are extended to handle flexible argument order. Setting the environment variable POSIXLY_CORRECT disables permutation. Then the behavior is completely standard. GNU application programs can use a third alternative mode in which they can distinguish the relative order of options and other arguments. */ #include "getopt.h" /* For communication from `getopt' to the caller. When `getopt' finds an option that takes an argument, the argument value is returned here. Also, when `ordering' is RETURN_IN_ORDER, each non-option ARGV-element is returned here. */ char *optarg = NULL; /* Index in ARGV of the next element to be scanned. This is used for communication to and from the caller and for communication between successive calls to `getopt'. On entry to `getopt', zero means this is the first call; initialize. When `getopt' returns -1, this is the index of the first of the non-option elements that the caller should itself scan. Otherwise, `optind' communicates from one call to the next how much of ARGV has been scanned so far. */ /* 1003.2 says this must be 1 before any call. */ int optind = 1; /* Formerly, initialization of getopt depended on optind==0, which causes problems with re-calling getopt as programs generally don't know that. */ int __getopt_initialized = 0; /* The next char to be scanned in the option-element in which the last option character we returned was found. This allows us to pick up the scan where we left off. If this is zero, or a null string, it means resume the scan by advancing to the next ARGV-element. */ static char *nextchar; /* Callers store zero here to inhibit the error message for unrecognized options. */ int opterr = 1; /* Set to an option character which was unrecognized. This must be initialized on some systems to avoid linking in the system's own getopt implementation. */ int optopt = '?'; /* Describe how to deal with options that follow non-option ARGV-elements. If the caller did not specify anything, the default is REQUIRE_ORDER if the environment variable POSIXLY_CORRECT is defined, PERMUTE otherwise. REQUIRE_ORDER means don't recognize them as options; stop option processing when the first non-option is seen. This is what Unix does. This mode of operation is selected by either setting the environment variable POSIXLY_CORRECT, or using `+' as the first character of the list of option characters. PERMUTE is the default. We permute the contents of ARGV as we scan, so that eventually all the non-options are at the end. This allows options to be given in any order, even with programs that were not written to expect this. RETURN_IN_ORDER is an option available to programs that were written to expect options and other ARGV-elements in any order and that care about the ordering of the two. We describe each non-option ARGV-element as if it were the argument of an option with character code 1. Using `-' as the first character of the list of option characters selects this mode of operation. The special argument `--' forces an end of option-scanning regardless of the value of `ordering'. In the case of RETURN_IN_ORDER, only `--' can cause `getopt' to return -1 with `optind' != ARGC. */ static enum { REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER } ordering; /* Value of POSIXLY_CORRECT environment variable. */ static char *posixly_correct; #ifdef __GNU_LIBRARY__ /* We want to avoid inclusion of string.h with non-GNU libraries because there are many ways it can cause trouble. On some systems, it contains special magic macros that don't work in GCC. */ #include #define my_index strchr #else /* Avoid depending on library functions or files whose names are inconsistent. */ char *getenv (); static char * my_index (str, chr) const char *str; int chr; { while (*str) { if (*str == chr) return (char *) str; str++; } return 0; } /* If using GCC, we can safely declare strlen this way. If not using GCC, it is ok not to declare it. */ #ifdef __GNUC__ /* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h. That was relevant to code that was here before. */ #if !defined (__STDC__) || !__STDC__ /* gcc with -traditional declares the built-in strlen to return int, and has done so at least since version 2.4.5. -- rms. */ extern int strlen (const char *); #endif /* not __STDC__ */ #endif /* __GNUC__ */ #endif /* not __GNU_LIBRARY__ */ /* Handle permutation of arguments. */ /* Describe the part of ARGV that contains non-options that have been skipped. `first_nonopt' is the index in ARGV of the first of them; `last_nonopt' is the index after the last of them. */ static int first_nonopt; static int last_nonopt; #ifdef _LIBC /* Bash 2.0 gives us an environment variable containing flags indicating ARGV elements that should not be considered arguments. */ /* Defined in getopt_init.c */ extern char *__getopt_nonoption_flags; static int nonoption_flags_max_len; static int nonoption_flags_len; static int original_argc; static char *const *original_argv; extern pid_t __libc_pid; /* Make sure the environment variable bash 2.0 puts in the environment is valid for the getopt call we must make sure that the ARGV passed to getopt is that one passed to the process. */ static void __attribute__ ((unused)) store_args_and_env (int argc, char *const *argv) { /* XXX This is no good solution. We should rather copy the args so that we can compare them later. But we must not use malloc(3). */ original_argc = argc; original_argv = argv; } text_set_element (__libc_subinit, store_args_and_env); # define SWAP_FLAGS(ch1, ch2) \ if (nonoption_flags_len > 0) \ { \ char __tmp = __getopt_nonoption_flags[ch1]; \ __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \ __getopt_nonoption_flags[ch2] = __tmp; \ } #else /* !_LIBC */ # define SWAP_FLAGS(ch1, ch2) #endif /* _LIBC */ /* Exchange two adjacent subsequences of ARGV. One subsequence is elements [first_nonopt,last_nonopt) which contains all the non-options that have been skipped so far. The other is elements [last_nonopt,optind), which contains all the options processed since those non-options were skipped. `first_nonopt' and `last_nonopt' are relocated so that they describe the new indices of the non-options in ARGV after they are moved. */ #if defined (__STDC__) && __STDC__ static void exchange (char **); #endif static void exchange (argv) char **argv; { int bottom = first_nonopt; int middle = last_nonopt; int top = optind; char *tem; /* Exchange the shorter segment with the far end of the longer segment. That puts the shorter segment into the right place. It leaves the longer segment in the right place overall, but it consists of two parts that need to be swapped next. */ #ifdef _LIBC /* First make sure the handling of the `__getopt_nonoption_flags' string can work normally. Our top argument must be in the range of the string. */ if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len) { /* We must extend the array. The user plays games with us and presents new arguments. */ char *new_str = malloc (top + 1); if (new_str == NULL) nonoption_flags_len = nonoption_flags_max_len = 0; else { memcpy (new_str, __getopt_nonoption_flags, nonoption_flags_max_len); memset (&new_str[nonoption_flags_max_len], '\0', top + 1 - nonoption_flags_max_len); nonoption_flags_max_len = top + 1; __getopt_nonoption_flags = new_str; } } #endif while (top > middle && middle > bottom) { if (top - middle > middle - bottom) { /* Bottom segment is the short one. */ int len = middle - bottom; register int i; /* Swap it with the top part of the top segment. */ for (i = 0; i < len; i++) { tem = argv[bottom + i]; argv[bottom + i] = argv[top - (middle - bottom) + i]; argv[top - (middle - bottom) + i] = tem; SWAP_FLAGS (bottom + i, top - (middle - bottom) + i); } /* Exclude the moved bottom segment from further swapping. */ top -= len; } else { /* Top segment is the short one. */ int len = top - middle; register int i; /* Swap it with the bottom part of the bottom segment. */ for (i = 0; i < len; i++) { tem = argv[bottom + i]; argv[bottom + i] = argv[middle + i]; argv[middle + i] = tem; SWAP_FLAGS (bottom + i, middle + i); } /* Exclude the moved top segment from further swapping. */ bottom += len; } } /* Update records for the slots the non-options now occupy. */ first_nonopt += (optind - last_nonopt); last_nonopt = optind; } /* Initialize the internal data when the first call is made. */ #if defined (__STDC__) && __STDC__ static const char *_getopt_initialize (int, char *const *, const char *); #endif static const char * _getopt_initialize (argc, argv, optstring) int argc; char *const *argv; const char *optstring; { /* Start processing options with ARGV-element 1 (since ARGV-element 0 is the program name); the sequence of previously skipped non-option ARGV-elements is empty. */ first_nonopt = last_nonopt = optind; nextchar = NULL; posixly_correct = getenv ("POSIXLY_CORRECT"); /* Determine how to handle the ordering of options and nonoptions. */ if (optstring[0] == '-') { ordering = RETURN_IN_ORDER; ++optstring; } else if (optstring[0] == '+') { ordering = REQUIRE_ORDER; ++optstring; } else if (posixly_correct != NULL) ordering = REQUIRE_ORDER; else ordering = PERMUTE; #ifdef _LIBC if (posixly_correct == NULL && argc == original_argc && argv == original_argv) { if (nonoption_flags_max_len == 0) { if (__getopt_nonoption_flags == NULL || __getopt_nonoption_flags[0] == '\0') nonoption_flags_max_len = -1; else { const char *orig_str = __getopt_nonoption_flags; int len = nonoption_flags_max_len = strlen (orig_str); if (nonoption_flags_max_len < argc) nonoption_flags_max_len = argc; __getopt_nonoption_flags = (char *) malloc (nonoption_flags_max_len); if (__getopt_nonoption_flags == NULL) nonoption_flags_max_len = -1; else { memcpy (__getopt_nonoption_flags, orig_str, len); memset (&__getopt_nonoption_flags[len], '\0', nonoption_flags_max_len - len); } } } nonoption_flags_len = nonoption_flags_max_len; } else nonoption_flags_len = 0; #endif return optstring; } /* Scan elements of ARGV (whose length is ARGC) for option characters given in OPTSTRING. If an element of ARGV starts with '-', and is not exactly "-" or "--", then it is an option element. The characters of this element (aside from the initial '-') are option characters. If `getopt' is called repeatedly, it returns successively each of the option characters from each of the option elements. If `getopt' finds another option character, it returns that character, updating `optind' and `nextchar' so that the next call to `getopt' can resume the scan with the following option character or ARGV-element. If there are no more option characters, `getopt' returns -1. Then `optind' is the index in ARGV of the first ARGV-element that is not an option. (The ARGV-elements have been permuted so that those that are not options now come last.) OPTSTRING is a string containing the legitimate option characters. If an option character is seen that is not listed in OPTSTRING, return '?' after printing an error message. If you set `opterr' to zero, the error message is suppressed but we still return '?'. If a char in OPTSTRING is followed by a colon, that means it wants an arg, so the following text in the same ARGV-element, or the text of the following ARGV-element, is returned in `optarg'. Two colons mean an option that wants an optional arg; if there is text in the current ARGV-element, it is returned in `optarg', otherwise `optarg' is set to zero. If OPTSTRING starts with `-' or `+', it requests different methods of handling the non-option ARGV-elements. See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. Long-named options begin with `--' instead of `-'. Their names may be abbreviated as long as the abbreviation is unique or is an exact match for some defined option. If they have an argument, it follows the option name in the same ARGV-element, separated from the option name by a `=', or else the in next ARGV-element. When `getopt' finds a long-named option, it returns 0 if that option's `flag' field is nonzero, the value of the option's `val' field if the `flag' field is zero. The elements of ARGV aren't really const, because we permute them. But we pretend they're const in the prototype to be compatible with other systems. LONGOPTS is a vector of `struct option' terminated by an element containing a name which is zero. LONGIND returns the index in LONGOPT of the long-named option found. It is only valid when a long-named option has been found by the most recent call. If LONG_ONLY is nonzero, '-' as well as '--' can introduce long-named options. */ int _getopt_internal (argc, argv, optstring, longopts, longind, long_only) int argc; char *const *argv; const char *optstring; const struct option *longopts; int *longind; int long_only; { optarg = NULL; if (optind == 0 || !__getopt_initialized) { if (optind == 0) optind = 1; /* Don't scan ARGV[0], the program name. */ optstring = _getopt_initialize (argc, argv, optstring); __getopt_initialized = 1; } /* Test whether ARGV[optind] points to a non-option argument. Either it does not have option syntax, or there is an environment flag from the shell indicating it is not an option. The later information is only used when the used in the GNU libc. */ #ifdef _LIBC #define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0' \ || (optind < nonoption_flags_len \ && __getopt_nonoption_flags[optind] == '1')) #else #define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0') #endif if (nextchar == NULL || *nextchar == '\0') { /* Advance to the next ARGV-element. */ /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been moved back by the user (who may also have changed the arguments). */ if (last_nonopt > optind) last_nonopt = optind; if (first_nonopt > optind) first_nonopt = optind; if (ordering == PERMUTE) { /* If we have just processed some options following some non-options, exchange them so that the options come first. */ if (first_nonopt != last_nonopt && last_nonopt != optind) exchange ((char **) argv); else if (last_nonopt != optind) first_nonopt = optind; /* Skip any additional non-options and extend the range of non-options previously skipped. */ while (optind < argc && NONOPTION_P) optind++; last_nonopt = optind; } /* The special ARGV-element `--' means premature end of options. Skip it like a null option, then exchange with previous non-options as if it were an option, then skip everything else like a non-option. */ if (optind != argc && !strcmp (argv[optind], "--")) { optind++; if (first_nonopt != last_nonopt && last_nonopt != optind) exchange ((char **) argv); else if (first_nonopt == last_nonopt) first_nonopt = optind; last_nonopt = argc; optind = argc; } /* If we have done all the ARGV-elements, stop the scan and back over any non-options that we skipped and permuted. */ if (optind == argc) { /* Set the next-arg-index to point at the non-options that we previously skipped, so the caller will digest them. */ if (first_nonopt != last_nonopt) optind = first_nonopt; return -1; } /* If we have come to a non-option and did not permute it, either stop the scan or describe it to the caller and pass it by. */ if (NONOPTION_P) { if (ordering == REQUIRE_ORDER) return -1; optarg = argv[optind++]; return 1; } /* We have found another option-ARGV-element. Skip the initial punctuation. */ nextchar = (argv[optind] + 1 + (longopts != NULL && argv[optind][1] == '-')); } /* Decode the current option-ARGV-element. */ /* Check whether the ARGV-element is a long option. If long_only and the ARGV-element has the form "-f", where f is a valid short option, don't consider it an abbreviated form of a long option that starts with f. Otherwise there would be no way to give the -f short option. On the other hand, if there's a long option "fubar" and the ARGV-element is "-fu", do consider that an abbreviation of the long option, just like "--fu", and not "-f" with arg "u". This distinction seems to be the most useful approach. */ if (longopts != NULL && (argv[optind][1] == '-' || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1]))))) { char *nameend; const struct option *p; const struct option *pfound = NULL; int exact = 0; int ambig = 0; int indfound = -1; int option_index; for (nameend = nextchar; *nameend && *nameend != '='; nameend++) /* Do nothing. */ ; /* Test all long options for either exact match or abbreviated matches. */ for (p = longopts, option_index = 0; p->name; p++, option_index++) if (!strncmp (p->name, nextchar, nameend - nextchar)) { if ((unsigned int) (nameend - nextchar) == (unsigned int) strlen (p->name)) { /* Exact match found. */ pfound = p; indfound = option_index; exact = 1; break; } else if (pfound == NULL) { /* First nonexact match found. */ pfound = p; indfound = option_index; } else /* Second or later nonexact match found. */ ambig = 1; } if (ambig && !exact) { if (opterr) fprintf (stderr, _("%s: option `%s' is ambiguous\n"), argv[0], argv[optind]); nextchar += strlen (nextchar); optind++; optopt = 0; return '?'; } if (pfound != NULL) { option_index = indfound; optind++; if (*nameend) { /* Don't test has_arg with >, because some C compilers don't allow it to be used on enums. */ if (pfound->has_arg) optarg = nameend + 1; else { if (opterr) if (argv[optind - 1][1] == '-') /* --option */ fprintf (stderr, _("%s: option `--%s' doesn't allow an argument\n"), argv[0], pfound->name); else /* +option or -option */ fprintf (stderr, _("%s: option `%c%s' doesn't allow an argument\n"), argv[0], argv[optind - 1][0], pfound->name); nextchar += strlen (nextchar); optopt = pfound->val; return '?'; } } else if (pfound->has_arg == 1) { if (optind < argc) optarg = argv[optind++]; else { if (opterr) fprintf (stderr, _("%s: option `%s' requires an argument\n"), argv[0], argv[optind - 1]); nextchar += strlen (nextchar); optopt = pfound->val; return optstring[0] == ':' ? ':' : '?'; } } nextchar += strlen (nextchar); if (longind != NULL) *longind = option_index; if (pfound->flag) { *(pfound->flag) = pfound->val; return 0; } return pfound->val; } /* Can't find it as a long option. If this is not getopt_long_only, or the option starts with '--' or is not a valid short option, then it's an error. Otherwise interpret it as a short option. */ if (!long_only || argv[optind][1] == '-' || my_index (optstring, *nextchar) == NULL) { if (opterr) { if (argv[optind][1] == '-') /* --option */ fprintf (stderr, _("%s: unrecognized option `--%s'\n"), argv[0], nextchar); else /* +option or -option */ fprintf (stderr, _("%s: unrecognized option `%c%s'\n"), argv[0], argv[optind][0], nextchar); } nextchar = (char *) ""; optind++; optopt = 0; return '?'; } } /* Look at and handle the next short option-character. */ { char c = *nextchar++; char *temp = my_index (optstring, c); /* Increment `optind' when we start to process its last character. */ if (*nextchar == '\0') ++optind; if (temp == NULL || c == ':') { if (opterr) { if (posixly_correct) /* 1003.2 specifies the format of this message. */ fprintf (stderr, _("%s: illegal option -- %c\n"), argv[0], c); else fprintf (stderr, _("%s: invalid option -- %c\n"), argv[0], c); } optopt = c; return '?'; } /* Convenience. Treat POSIX -W foo same as long option --foo */ if (temp[0] == 'W' && temp[1] == ';') { char *nameend; const struct option *p; const struct option *pfound = NULL; int exact = 0; int ambig = 0; int indfound = 0; int option_index; /* This is an option that requires an argument. */ if (*nextchar != '\0') { optarg = nextchar; /* If we end this ARGV-element by taking the rest as an arg, we must advance to the next element now. */ optind++; } else if (optind == argc) { if (opterr) { /* 1003.2 specifies the format of this message. */ fprintf (stderr, _("%s: option requires an argument -- %c\n"), argv[0], c); } optopt = c; if (optstring[0] == ':') c = ':'; else c = '?'; return c; } else /* We already incremented `optind' once; increment it again when taking next ARGV-elt as argument. */ optarg = argv[optind++]; /* optarg is now the argument, see if it's in the table of longopts. */ for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++) /* Do nothing. */ ; /* Test all long options for either exact match or abbreviated matches. */ for (p = longopts, option_index = 0; p->name; p++, option_index++) if (!strncmp (p->name, nextchar, nameend - nextchar)) { if ((unsigned int) (nameend - nextchar) == strlen (p->name)) { /* Exact match found. */ pfound = p; indfound = option_index; exact = 1; break; } else if (pfound == NULL) { /* First nonexact match found. */ pfound = p; indfound = option_index; } else /* Second or later nonexact match found. */ ambig = 1; } if (ambig && !exact) { if (opterr) fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"), argv[0], argv[optind]); nextchar += strlen (nextchar); optind++; return '?'; } if (pfound != NULL) { option_index = indfound; if (*nameend) { /* Don't test has_arg with >, because some C compilers don't allow it to be used on enums. */ if (pfound->has_arg) optarg = nameend + 1; else { if (opterr) fprintf (stderr, _("\ %s: option `-W %s' doesn't allow an argument\n"), argv[0], pfound->name); nextchar += strlen (nextchar); return '?'; } } else if (pfound->has_arg == 1) { if (optind < argc) optarg = argv[optind++]; else { if (opterr) fprintf (stderr, _("%s: option `%s' requires an argument\n"), argv[0], argv[optind - 1]); nextchar += strlen (nextchar); return optstring[0] == ':' ? ':' : '?'; } } nextchar += strlen (nextchar); if (longind != NULL) *longind = option_index; if (pfound->flag) { *(pfound->flag) = pfound->val; return 0; } return pfound->val; } nextchar = NULL; return 'W'; /* Let the application handle it. */ } if (temp[1] == ':') { if (temp[2] == ':') { /* This is an option that accepts an argument optionally. */ if (*nextchar != '\0') { optarg = nextchar; optind++; } else optarg = NULL; nextchar = NULL; } else { /* This is an option that requires an argument. */ if (*nextchar != '\0') { optarg = nextchar; /* If we end this ARGV-element by taking the rest as an arg, we must advance to the next element now. */ optind++; } else if (optind == argc) { if (opterr) { /* 1003.2 specifies the format of this message. */ fprintf (stderr, _("%s: option requires an argument -- %c\n"), argv[0], c); } optopt = c; if (optstring[0] == ':') c = ':'; else c = '?'; } else /* We already incremented `optind' once; increment it again when taking next ARGV-elt as argument. */ optarg = argv[optind++]; nextchar = NULL; } } return c; } } int getopt (argc, argv, optstring) int argc; char *const *argv; const char *optstring; { return _getopt_internal (argc, argv, optstring, (const struct option *) 0, (int *) 0, 0); } #endif /* Not ELIDE_CODE. */ #ifdef TEST /* Compile with -DTEST to make an executable for use in testing the above definition of `getopt'. */ int main (argc, argv) int argc; char **argv; { int c; int digit_optind = 0; while (1) { int this_option_optind = optind ? optind : 1; c = getopt (argc, argv, "abc:d:0123456789"); if (c == -1) break; switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (digit_optind != 0 && digit_optind != this_option_optind) printf ("digits occur in two different argv-elements.\n"); digit_optind = this_option_optind; printf ("option %c\n", c); break; case 'a': printf ("option a\n"); break; case 'b': printf ("option b\n"); break; case 'c': printf ("option c with value `%s'\n", optarg); break; case '?': break; default: printf ("?? getopt returned character code 0%o ??\n", c); } } if (optind < argc) { printf ("non-option ARGV-elements: "); while (optind < argc) printf ("%s ", argv[optind++]); printf ("\n"); } exit (0); } #endif /* TEST */ /sys/src/ape/cmd/patch/getopt.h 664 sys sys 1367613436 4558 /* Declarations for getopt. Copyright (C) 1989,90,91,92,93,94,96,97 Free Software Foundation, Inc. NOTE: The canonical source of this file is maintained with the GNU C Library. Bugs can be reported to bug-glibc@prep.ai.mit.edu. 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef _GETOPT_H #define _GETOPT_H 1 #ifdef __cplusplus extern "C" { #endif /* For communication from `getopt' to the caller. When `getopt' finds an option that takes an argument, the argument value is returned here. Also, when `ordering' is RETURN_IN_ORDER, each non-option ARGV-element is returned here. */ extern char *optarg; /* Index in ARGV of the next element to be scanned. This is used for communication to and from the caller and for communication between successive calls to `getopt'. On entry to `getopt', zero means this is the first call; initialize. When `getopt' returns -1, this is the index of the first of the non-option elements that the caller should itself scan. Otherwise, `optind' communicates from one call to the next how much of ARGV has been scanned so far. */ extern int optind; /* Callers store zero here to inhibit the error message `getopt' prints for unrecognized options. */ extern int opterr; /* Set to an option character which was unrecognized. */ extern int optopt; /* Describe the long-named options requested by the application. The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector of `struct option' terminated by an element containing a name which is zero. The field `has_arg' is: no_argument (or 0) if the option does not take an argument, required_argument (or 1) if the option requires an argument, optional_argument (or 2) if the option takes an optional argument. If the field `flag' is not NULL, it points to a variable that is set to the value given in the field `val' when the option is found, but left unchanged if the option is not found. To have a long-named option do something other than set an `int' to a compiled-in constant, such as set a value from `optarg', set the option's `flag' field to zero and its `val' field to a nonzero value (the equivalent single-letter option character, if there is one). For long options that have a zero `flag' field, `getopt' returns the contents of the `val' field. */ struct option { #if defined (__STDC__) && __STDC__ const char *name; #else char *name; #endif /* has_arg can't be an enum because some compilers complain about type mismatches in all the code that assumes it is an int. */ int has_arg; int *flag; int val; }; /* Names for the values of the `has_arg' field of `struct option'. */ #define no_argument 0 #define required_argument 1 #define optional_argument 2 #if defined (__STDC__) && __STDC__ #ifdef __GNU_LIBRARY__ /* Many other libraries have conflicting prototypes for getopt, with differences in the consts, in stdlib.h. To avoid compilation errors, only prototype getopt for the GNU C library. */ extern int getopt (int argc, char *const *argv, const char *shortopts); #else /* not __GNU_LIBRARY__ */ extern int getopt (); #endif /* __GNU_LIBRARY__ */ extern int getopt_long (int argc, char *const *argv, const char *shortopts, const struct option *longopts, int *longind); extern int getopt_long_only (int argc, char *const *argv, const char *shortopts, const struct option *longopts, int *longind); /* Internal only. Users should not call this directly. */ extern int _getopt_internal (int argc, char *const *argv, const char *shortopts, const struct option *longopts, int *longind, int long_only); #else /* not __STDC__ */ extern int getopt (); extern int getopt_long (); extern int getopt_long_only (); extern int _getopt_internal (); #endif /* __STDC__ */ #ifdef __cplusplus } #endif #endif /* _GETOPT_H */ /sys/src/ape/cmd/patch/getopt1.c 664 sys sys 1367613436 4518 /* getopt_long and getopt_long_only entry points for GNU getopt. Copyright (C) 1987,88,89,90,91,92,93,94,96,97 Free Software Foundation, Inc. NOTE: The canonical source of this file is maintained with the GNU C Library. Bugs can be reported to bug-glibc@prep.ai.mit.edu. 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifdef HAVE_CONFIG_H #include #endif #include "getopt.h" #if !defined (__STDC__) || !__STDC__ /* This is a separate conditional since some stdc systems reject `defined (const)'. */ #ifndef const #define const #endif #endif #include /* Comment out all this code if we are using the GNU C Library, and are not actually compiling the library itself. This code is part of the GNU C Library, but also included in many other GNU distributions. Compiling and linking in this code is a waste when using the GNU C library (especially if it is a shared library). Rather than having every GNU program understand `configure --with-gnu-libc' and omit the object files, it is simpler to just do this in the source for each such file. */ #define GETOPT_INTERFACE_VERSION 2 #if !defined (_LIBC) && defined (__GLIBC__) && __GLIBC__ >= 2 #include #if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION #define ELIDE_CODE #endif #endif #ifndef ELIDE_CODE /* This needs to come after some library #include to get __GNU_LIBRARY__ defined. */ #ifdef __GNU_LIBRARY__ #include #endif #ifndef NULL #define NULL 0 #endif int getopt_long (argc, argv, options, long_options, opt_index) int argc; char *const *argv; const char *options; const struct option *long_options; int *opt_index; { return _getopt_internal (argc, argv, options, long_options, opt_index, 0); } /* Like getopt_long, but '-' as well as '--' can indicate a long option. If an option that starts with '-' (not '--') doesn't match a long option, but does match a short option, it is parsed as a short option instead. */ int getopt_long_only (argc, argv, options, long_options, opt_index) int argc; char *const *argv; const char *options; const struct option *long_options; int *opt_index; { return _getopt_internal (argc, argv, options, long_options, opt_index, 1); } #endif /* Not ELIDE_CODE. */ #ifdef TEST #include int main (argc, argv) int argc; char **argv; { int c; int digit_optind = 0; while (1) { int this_option_optind = optind ? optind : 1; int option_index = 0; static struct option long_options[] = { {"add", 1, 0, 0}, {"append", 0, 0, 0}, {"delete", 1, 0, 0}, {"verbose", 0, 0, 0}, {"create", 0, 0, 0}, {"file", 1, 0, 0}, {0, 0, 0, 0} }; c = getopt_long (argc, argv, "abc:d:0123456789", long_options, &option_index); if (c == -1) break; switch (c) { case 0: printf ("option %s", long_options[option_index].name); if (optarg) printf (" with arg %s", optarg); printf ("\n"); break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (digit_optind != 0 && digit_optind != this_option_optind) printf ("digits occur in two different argv-elements.\n"); digit_optind = this_option_optind; printf ("option %c\n", c); break; case 'a': printf ("option a\n"); break; case 'b': printf ("option b\n"); break; case 'c': printf ("option c with value `%s'\n", optarg); break; case 'd': printf ("option d with value `%s'\n", optarg); break; case '?': break; default: printf ("?? getopt returned character code 0%o ??\n", c); } } if (optind < argc) { printf ("non-option ARGV-elements: "); while (optind < argc) printf ("%s ", argv[optind++]); printf ("\n"); } exit (0); } #endif /* TEST */ /sys/src/ape/cmd/patch/inp.c 664 sys sys 1367613436 11253 /* inputting files to be patched */ /* $Id: inp.c,v 1.18 1997/07/21 17:59:46 eggert Exp $ */ /* Copyright 1986, 1988 Larry Wall Copyright 1991, 1992, 1993, 1997 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; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #define XTERN extern #include #include #include #include #undef XTERN #define XTERN #include /* Input-file-with-indexable-lines abstract type */ static char *i_buffer; /* plan A buffer */ static char const **i_ptr; /* pointers to lines in plan A buffer */ static size_t tibufsize; /* size of plan b buffers */ #ifndef TIBUFSIZE_MINIMUM #define TIBUFSIZE_MINIMUM (8 * 1024) /* minimum value for tibufsize */ #endif static int tifd = -1; /* plan b virtual string array */ static char *tibuf[2]; /* plan b buffers */ static LINENUM tiline[2] = {-1, -1}; /* 1st line in each buffer */ static LINENUM lines_per_buf; /* how many lines per buffer */ static size_t tireclen; /* length of records in tmp file */ static size_t last_line_size; /* size of last input line */ static bool plan_a PARAMS ((char const *));/* yield FALSE if memory runs out */ static void plan_b PARAMS ((char const *)); static void report_revision PARAMS ((int)); static void too_many_lines PARAMS ((char const *)) __attribute__((noreturn)); /* New patch--prepare to edit another file. */ void re_input() { if (using_plan_a) { free (i_buffer); free (i_ptr); } else { close (tifd); tifd = -1; free(tibuf[0]); tibuf[0] = 0; tiline[0] = tiline[1] = -1; tireclen = 0; } } /* Construct the line index, somehow or other. */ void scan_input(filename) char *filename; { using_plan_a = ! (debug & 16) && plan_a (filename); if (!using_plan_a) plan_b(filename); switch (verbosity) { case SILENT: break; case VERBOSE: say ("Patching file `%s' using Plan %s...\n", filename, using_plan_a ? "A" : "B"); break; case DEFAULT_VERBOSITY: say ("patching file `%s'\n", filename); break; } } /* Report whether a desired revision was found. */ static void report_revision (found_revision) int found_revision; { if (found_revision) { if (verbosity == VERBOSE) say ("Good. This file appears to be the %s version.\n", revision); } else if (force) { if (verbosity != SILENT) say ("Warning: this file doesn't appear to be the %s version -- patching anyway.\n", revision); } else if (batch) { fatal ("This file doesn't appear to be the %s version -- aborting.", revision); } else { ask ("This file doesn't appear to be the %s version -- patch anyway? [n] ", revision); if (*buf != 'y') fatal ("aborted"); } } static void too_many_lines (filename) char const *filename; { fatal ("File `%s' has too many lines.", filename); } void get_input_file (filename, outname) char const *filename; char const *outname; { int elsewhere = strcmp (filename, outname); char const *cs; char *diffbuf; char *getbuf; if (inerrno == -1) inerrno = stat (inname, &instat) == 0 ? 0 : errno; /* Perhaps look for RCS or SCCS versions. */ if (patch_get && invc != 0 && (inerrno || (! elsewhere && (/* No one can write to it. */ (instat.st_mode & (S_IWUSR|S_IWGRP|S_IWOTH)) == 0 /* Only the owner (who's not me) can write to it. */ || ((instat.st_mode & (S_IWGRP|S_IWOTH)) == 0 && instat.st_uid != geteuid ())))) && (invc = !! (cs = (version_controller (filename, elsewhere, inerrno ? (struct stat *) 0 : &instat, &getbuf, &diffbuf))))) { if (!inerrno) { if (!elsewhere && (instat.st_mode & (S_IWUSR|S_IWGRP|S_IWOTH)) != 0) /* Somebody can write to it. */ fatal ("file `%s' seems to be locked by somebody else under %s", filename, cs); /* It might be checked out unlocked. See if it's safe to check out the default version locked. */ if (verbosity == VERBOSE) say ("Comparing file `%s' to default %s version...\n", filename, cs); if (systemic (diffbuf) != 0) { say ("warning: patching file `%s', which does not match default %s version\n", filename, cs); cs = 0; } } if (cs && version_get (filename, cs, ! inerrno, elsewhere, getbuf, &instat)) inerrno = 0; free (getbuf); free (diffbuf); } else if (inerrno && !pch_says_nonexistent (reverse)) { errno = inerrno; pfatal ("can't find file `%s'", filename); } if (inerrno) { instat.st_mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH; instat.st_size = 0; } else if (! S_ISREG (instat.st_mode)) fatal ("`%s' is not a regular file -- can't patch", filename); } /* Try keeping everything in memory. */ static bool plan_a(filename) char const *filename; { register char const *s; register char const *lim; register char const **ptr; register char *buffer; register LINENUM iline; size_t size = instat.st_size; /* Fail if the file size doesn't fit in a size_t, or if storage isn't available. */ if (! (size == instat.st_size && (buffer = malloc (size ? size : (size_t) 1)))) return FALSE; /* Read the input file, but don't bother reading it if it's empty. When creating files, the files do not actually exist. */ if (size) { int ifd = open (filename, O_RDONLY|binary_transput); size_t buffered = 0, n; if (ifd < 0) pfatal ("can't open file `%s'", filename); while (size - buffered != 0) { n = read (ifd, buffer + buffered, size - buffered); if (n == 0) { /* Some non-POSIX hosts exaggerate st_size in text mode; or the file may have shrunk! */ size = buffered; break; } if (n == (size_t) -1) { /* Perhaps size is too large for this host. */ close (ifd); free (buffer); return FALSE; } buffered += n; } if (close (ifd) != 0) read_fatal (); } /* Scan the buffer and build array of pointers to lines. */ lim = buffer + size; iline = 3; /* 1 unused, 1 for SOF, 1 for EOF if last line is incomplete */ for (s = buffer; (s = (char *) memchr (s, '\n', lim - s)); s++) if (++iline < 0) too_many_lines (filename); if (! (iline == (size_t) iline && (size_t) iline * sizeof *ptr / sizeof *ptr == (size_t) iline && (ptr = (char const **) malloc ((size_t) iline * sizeof *ptr)))) { free (buffer); return FALSE; } iline = 0; for (s = buffer; ; s++) { ptr[++iline] = s; if (! (s = (char *) memchr (s, '\n', lim - s))) break; } if (size && lim[-1] != '\n') ptr[++iline] = lim; input_lines = iline - 1; if (revision) { char const *rev = revision; int rev0 = rev[0]; int found_revision = 0; size_t revlen = strlen (rev); if (revlen <= size) { char const *limrev = lim - revlen; for (s = buffer; (s = (char *) memchr (s, rev0, limrev - s)); s++) if (memcmp (s, rev, revlen) == 0 && (s == buffer || ISSPACE ((unsigned char) s[-1])) && (s + 1 == limrev || ISSPACE ((unsigned char) s[revlen]))) { found_revision = 1; break; } } report_revision (found_revision); } /* Plan A will work. */ i_buffer = buffer; i_ptr = ptr; return TRUE; } /* Keep (virtually) nothing in memory. */ static void plan_b(filename) char const *filename; { register FILE *ifp; register int c; register size_t len; register size_t maxlen; register int found_revision; register size_t i; register char const *rev; register size_t revlen; register LINENUM line = 1; if (instat.st_size == 0) filename = NULL_DEVICE; if (! (ifp = fopen (filename, binary_transput ? "rb" : "r"))) pfatal ("can't open file `%s'", filename); tifd = create_file (TMPINNAME, O_RDWR | O_BINARY, (mode_t) 0); i = 0; len = 0; maxlen = 1; rev = revision; found_revision = !rev; revlen = rev ? strlen (rev) : 0; while ((c = getc (ifp)) != EOF) { len++; if (c == '\n') { if (++line < 0) too_many_lines (filename); if (maxlen < len) maxlen = len; len = 0; } if (!found_revision) { if (i == revlen) { found_revision = ISSPACE ((unsigned char) c); i = (size_t) -1; } else if (i != (size_t) -1) i = rev[i]==c ? i + 1 : (size_t) -1; if (i == (size_t) -1 && ISSPACE ((unsigned char) c)) i = 0; } } if (revision) report_revision (found_revision); Fseek (ifp, (off_t) 0, SEEK_SET); /* rewind file */ for (tibufsize = TIBUFSIZE_MINIMUM; tibufsize < maxlen; tibufsize <<= 1) continue; lines_per_buf = tibufsize / maxlen; tireclen = maxlen; tibuf[0] = xmalloc (2 * tibufsize); tibuf[1] = tibuf[0] + tibufsize; for (line = 1; ; line++) { char *p = tibuf[0] + maxlen * (line % lines_per_buf); char const *p0 = p; if (! (line % lines_per_buf)) /* new block */ if (write (tifd, tibuf[0], tibufsize) != tibufsize) write_fatal (); if ((c = getc (ifp)) == EOF) break; for (;;) { *p++ = c; if (c == '\n') { last_line_size = p - p0; break; } if ((c = getc (ifp)) == EOF) { last_line_size = p - p0; line++; goto EOF_reached; } } } EOF_reached: if (ferror (ifp) || fclose (ifp) != 0) read_fatal (); if (line % lines_per_buf != 0) if (write (tifd, tibuf[0], tibufsize) != tibufsize) write_fatal (); input_lines = line - 1; } /* Fetch a line from the input file. */ char const * ifetch (line, whichbuf, psize) register LINENUM line; int whichbuf; /* ignored when file in memory */ size_t *psize; { register char const *q; register char const *p; if (line < 1 || line > input_lines) { *psize = 0; return ""; } if (using_plan_a) { p = i_ptr[line]; *psize = i_ptr[line + 1] - p; return p; } else { LINENUM offline = line % lines_per_buf; LINENUM baseline = line - offline; if (tiline[0] == baseline) whichbuf = 0; else if (tiline[1] == baseline) whichbuf = 1; else { tiline[whichbuf] = baseline; if (lseek (tifd, (off_t) (baseline/lines_per_buf * tibufsize), SEEK_SET) == -1 || read (tifd, tibuf[whichbuf], tibufsize) < 0) read_fatal (); } p = tibuf[whichbuf] + (tireclen*offline); if (line == input_lines) *psize = last_line_size; else { for (q = p; *q++ != '\n'; ) continue; *psize = q - p; } return p; } } /sys/src/ape/cmd/patch/inp.h 664 sys sys 1367613436 340 /* inputting files to be patched */ /* $Id: inp.h,v 1.4 1997/04/07 01:07:00 eggert Exp $ */ XTERN LINENUM input_lines; /* how long is input file in lines */ char const *ifetch PARAMS ((LINENUM, int, size_t *)); void get_input_file PARAMS ((char const *, char const *)); void re_input PARAMS ((void)); void scan_input PARAMS ((char *)); /sys/src/ape/cmd/patch/install-sh 775 sys sys 1367613436 5490 #! /bin/sh # # install - install a program, script, or datafile # This comes from X11R5 (mit/util/scripts/install.sh). # # Copyright 1991 by the Massachusetts Institute of Technology # # Permission to use, copy, modify, distribute, and sell this software and its # documentation for any purpose is hereby granted without fee, provided that # the above copyright notice appear in all copies and that both that # copyright notice and this permission notice appear in supporting # documentation, and that the name of M.I.T. not be used in advertising or # publicity pertaining to distribution of the software without specific, # written prior permission. M.I.T. makes no representations about the # suitability of this software for any purpose. It is provided "as is" # without express or implied warranty. # # Calling this script install-sh is preferred over install.sh, to prevent # `make' implicit rules from creating a file called install from it # when there is no Makefile. # # 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}" mkdirprog="${MKDIRPROG-mkdir}" transformbasename="" transform_arg="" instcmd="$mvprog" chmodcmd="$chmodprog 0755" chowncmd="" chgrpcmd="" stripcmd="" rmcmd="$rmprog -f" mvcmd="$mvprog" src="" dst="" dir_arg="" while [ x"$1" != x ]; do case $1 in -c) instcmd="$cpprog" shift continue;; -d) dir_arg=true 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;; -t=*) transformarg=`echo $1 | sed 's/-t=//'` shift continue;; -b=*) transformbasename=`echo $1 | sed 's/-b=//'` shift continue;; *) if [ x"$src" = x ] then src=$1 else # this colon is to work around a 386BSD /bin/sh bug : dst=$1 fi shift continue;; esac done if [ x"$src" = x ] then echo "install: no input file specified" exit 1 else true fi if [ x"$dir_arg" != x ]; then dst=$src src="" if [ -d $dst ]; then instcmd=: else instcmd=mkdir fi else # Waiting for this to be detected by the "$instcmd $src $dsttmp" command # might cause directories to be created, which would be especially bad # if $src (and thus $dsttmp) contains '*'. if [ -f $src -o -d $src ] then true else echo "install: $src does not exist" exit 1 fi if [ x"$dst" = x ] then echo "install: no destination specified" exit 1 else true 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` else true fi fi ## this sed command emulates the dirname command dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` # Make sure that the destination directory exists. # this part is taken from Noah Friedman's mkinstalldirs script # Skip lots of stat calls in the usual case. if [ ! -d "$dstdir" ]; then defaultIFS=' ' IFS="${IFS-${defaultIFS}}" oIFS="${IFS}" # Some sh's can't handle IFS=/ for some reason. IFS='%' set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` IFS="${oIFS}" pathcomp='' while [ $# -ne 0 ] ; do pathcomp="${pathcomp}${1}" shift if [ ! -d "${pathcomp}" ] ; then $mkdirprog "${pathcomp}" else true fi pathcomp="${pathcomp}/" done fi if [ x"$dir_arg" != x ] then $doit $instcmd $dst && if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi && if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi && if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi && if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi else # If we're going to rename the final executable, determine the name now. if [ x"$transformarg" = x ] then dstfile=`basename $dst` else dstfile=`basename $dst $transformbasename | sed $transformarg`$transformbasename fi # don't allow the sed command to completely eliminate the filename if [ x"$dstfile" = x ] then dstfile=`basename $dst` else true fi # Make a temp file name in the proper directory. dsttmp=$dstdir/#inst.$$# # Move or copy the file name to the temp name $doit $instcmd $src $dsttmp && trap "rm -f ${dsttmp}" 0 && # and set any options; do chmod last to preserve setuid bits # If any of these fail, we abort the whole thing. If we want to # ignore errors from any of these, just make sure not to ignore # errors from the above "$doit $instcmd $src $dsttmp" command. if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi && if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi && if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi && if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi && # Now rename the file to the real destination. $doit $rmcmd -f $dstdir/$dstfile && $doit $mvcmd $dsttmp $dstdir/$dstfile fi && exit 0 /sys/src/ape/cmd/patch/maketime.c 664 sys sys 1367613436 10415 /* Convert struct partime into time_t. */ /* Copyright 1992, 1993, 1994, 1995, 1997 Paul Eggert Distributed under license by the Free Software Foundation, Inc. This file is part of RCS. RCS 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. RCS 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 RCS; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. Report problems and direct all questions to: rcs-bugs@cs.purdue.edu */ #if has_conf_h # include #else # if HAVE_CONFIG_H # include # else # ifndef __STDC__ # define const # endif # endif /* MIPS RISCOS4.52 defines time_t in not . */ # include # if HAVE_LIMITS_H # include # endif # ifndef LONG_MIN # define LONG_MIN (-1-2147483647L) # endif # if STDC_HEADERS # include # endif # include # ifdef __STDC__ # define P(x) x # else # define P(x) () # endif #endif #include #include char const maketId[] = "$Id: maketime.c,v 5.15 1997/06/17 16:54:36 eggert Exp $"; static int isleap P ((int)); static int month_days P ((struct tm const *)); static time_t maketime P ((struct partime const *, time_t)); /* For maximum portability, use only localtime and gmtime. Make no assumptions about the time_t epoch or the range of time_t values. Avoid mktime because it's not universal and because there's no easy, portable way for mktime to yield the inverse of gmtime. */ #define TM_YEAR_ORIGIN 1900 static int isleap (y) int y; { return (y & 3) == 0 && (y % 100 != 0 || y % 400 == 0); } /* days in year before start of months 0-12 */ static int const month_yday[] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }; /* Yield the number of days in TM's month. */ static int month_days (tm) struct tm const *tm; { int m = tm->tm_mon; return (month_yday[m + 1] - month_yday[m] + (m == 1 && isleap (tm->tm_year + TM_YEAR_ORIGIN))); } /* Convert UNIXTIME to struct tm form. Use gmtime if available and if !LOCALZONE, localtime otherwise. */ struct tm * time2tm (unixtime, localzone) time_t unixtime; int localzone; { struct tm *tm; #ifdef TZ_is_unset static char const *TZ; if (!TZ && !(TZ = getenv ("TZ"))) TZ_is_unset ("The TZ environment variable is not set; please set it to your timezone"); #endif if (localzone || !(tm = gmtime (&unixtime))) tm = localtime (&unixtime); return tm; } /* Yield A - B, measured in seconds. */ time_t difftm (a, b) struct tm const *a; struct tm const *b; { int ay = a->tm_year + (TM_YEAR_ORIGIN - 1); int by = b->tm_year + (TM_YEAR_ORIGIN - 1); int ac = ay / 100 - (ay % 100 < 0); int bc = by / 100 - (by % 100 < 0); int difference_in_day_of_year = a->tm_yday - b->tm_yday; int intervening_leap_days = (((ay >> 2) - (by >> 2)) - (ac - bc) + ((ac >> 2) - (bc >> 2))); time_t difference_in_years = ay - by; time_t difference_in_days = (difference_in_years * 365 + (intervening_leap_days + difference_in_day_of_year)); return (((((difference_in_days * 24 + (a->tm_hour - b->tm_hour)) * 60) + (a->tm_min - b->tm_min)) * 60) + (a->tm_sec - b->tm_sec)); } /* * Adjust time T by adding SECONDS. SECONDS must be at most 24 hours' worth. * Adjust only T's year, mon, mday, hour, min and sec members; * plus adjust wday if it is defined. */ void adjzone (t, seconds) register struct tm *t; long seconds; { /* * This code can be off by a second if SECONDS is not a multiple of 60, * if T is local time, and if a leap second happens during this minute. * But this bug has never occurred, and most likely will not ever occur. * Liberia, the last country for which SECONDS % 60 was nonzero, * switched to UTC in May 1972; the first leap second was in June 1972. */ int leap_second = t->tm_sec == 60; long sec = seconds + (t->tm_sec - leap_second); if (sec < 0) { if ((t->tm_min -= (59 - sec) / 60) < 0) { if ((t->tm_hour -= (59 - t->tm_min) / 60) < 0) { t->tm_hour += 24; if (TM_DEFINED (t->tm_wday) && --t->tm_wday < 0) t->tm_wday = 6; if (--t->tm_mday <= 0) { if (--t->tm_mon < 0) { --t->tm_year; t->tm_mon = 11; } t->tm_mday = month_days (t); } } t->tm_min += 24 * 60; } sec += 24L * 60 * 60; } else if (60 <= (t->tm_min += sec / 60)) if (24 <= (t->tm_hour += t->tm_min / 60)) { t->tm_hour -= 24; if (TM_DEFINED (t->tm_wday) && ++t->tm_wday == 7) t->tm_wday = 0; if (month_days (t) < ++t->tm_mday) { if (11 < ++t->tm_mon) { ++t->tm_year; t->tm_mon = 0; } t->tm_mday = 1; } } t->tm_min %= 60; t->tm_sec = (int) (sec % 60) + leap_second; } /* * Convert TM to time_t, using localtime if LOCALZONE and gmtime otherwise. * Use only TM's year, mon, mday, hour, min, and sec members. * Ignore TM's old tm_yday and tm_wday, but fill in their correct values. * Yield -1 on failure (e.g. a member out of range). * Posix 1003.1-1990 doesn't allow leap seconds, but some implementations * have them anyway, so allow them if localtime/gmtime does. */ time_t tm2time (tm, localzone) struct tm *tm; int localzone; { /* Cache the most recent t,tm pairs; 1 for gmtime, 1 for localtime. */ static time_t t_cache[2]; static struct tm tm_cache[2]; time_t d, gt; struct tm const *gtm; /* * The maximum number of iterations should be enough to handle any * combinations of leap seconds, time zone rule changes, and solar time. * 4 is probably enough; we use a bigger number just to be safe. */ int remaining_tries = 8; /* Avoid subscript errors. */ if (12 <= (unsigned) tm->tm_mon) return -1; tm->tm_yday = month_yday[tm->tm_mon] + tm->tm_mday - (tm->tm_mon < 2 || !isleap (tm->tm_year + TM_YEAR_ORIGIN)); /* Make a first guess. */ gt = t_cache[localzone]; gtm = gt ? &tm_cache[localzone] : time2tm (gt, localzone); /* Repeatedly use the error from the guess to improve the guess. */ while ((d = difftm (tm, gtm)) != 0) { if (--remaining_tries == 0) return -1; gt += d; gtm = time2tm (gt, localzone); } /* * Check that the guess actually matches; * overflow can cause difftm to yield 0 even on differing times, * or tm may have members out of range (e.g. bad leap seconds). */ #define TM_DIFFER(a,b) \ ( \ ((a)->tm_year ^ (b)->tm_year) | \ ((a)->tm_mon ^ (b)->tm_mon) | \ ((a)->tm_mday ^ (b)->tm_mday) | \ ((a)->tm_hour ^ (b)->tm_hour) | \ ((a)->tm_min ^ (b)->tm_min) | \ ((a)->tm_sec ^ (b)->tm_sec) \ ) if (TM_DIFFER (tm, gtm)) { /* * If gt is a leap second, try gt+1; if it is one greater than * a leap second, try gt-1; otherwise, it doesn't matter. * Leap seconds always fall at month end. */ int yd = tm->tm_year - gtm->tm_year; gt += yd + (yd ? 0 : tm->tm_mon - gtm->tm_mon); gtm = time2tm (gt, localzone); if (TM_DIFFER (tm, gtm)) return -1; } t_cache[localzone] = gt; tm_cache[localzone] = *gtm; tm->tm_wday = gtm->tm_wday; return gt; } /* * Check *PT and convert it to time_t. * If it is incompletely specified, use DEFAULT_TIME to fill it out. * Use localtime if PT->zone is the special value TM_LOCAL_ZONE. * Yield -1 on failure. * ISO 8601 day-of-year and week numbers are not yet supported. */ static time_t maketime (pt, default_time) struct partime const *pt; time_t default_time; { int localzone, wday; struct tm tm; struct tm *tm0 = 0; time_t r; tm0 = 0; /* Keep gcc -Wall happy. */ localzone = pt->zone == TM_LOCAL_ZONE; tm = pt->tm; if (TM_DEFINED (pt->ymodulus) || !TM_DEFINED (tm.tm_year)) { /* Get tm corresponding to default time. */ tm0 = time2tm (default_time, localzone); if (!localzone) adjzone (tm0, pt->zone); } if (TM_DEFINED (pt->ymodulus)) tm.tm_year += (tm0->tm_year + TM_YEAR_ORIGIN) / pt->ymodulus * pt->ymodulus; else if (!TM_DEFINED (tm.tm_year)) { /* Set default year, month, day from current time. */ tm.tm_year = tm0->tm_year + TM_YEAR_ORIGIN; if (!TM_DEFINED (tm.tm_mon)) { tm.tm_mon = tm0->tm_mon; if (!TM_DEFINED (tm.tm_mday)) tm.tm_mday = tm0->tm_mday; } } /* Convert from partime year (Gregorian) to Posix year. */ tm.tm_year -= TM_YEAR_ORIGIN; /* Set remaining default fields to be their minimum values. */ if (!TM_DEFINED (tm.tm_mon)) tm.tm_mon = 0; if (!TM_DEFINED (tm.tm_mday)) tm.tm_mday = 1; if (!TM_DEFINED (tm.tm_hour)) tm.tm_hour = 0; if (!TM_DEFINED (tm.tm_min)) tm.tm_min = 0; if (!TM_DEFINED (tm.tm_sec)) tm.tm_sec = 0; if (!localzone) adjzone (&tm, -pt->zone); wday = tm.tm_wday; /* Convert and fill in the rest of the tm. */ r = tm2time (&tm, localzone); /* Check weekday. */ if (r != -1 && TM_DEFINED (wday) && wday != tm.tm_wday) return -1; return r; } /* Parse a free-format date in SOURCE, yielding a Unix format time. */ time_t str2time (source, default_time, default_zone) char const *source; time_t default_time; long default_zone; { struct partime pt; if (*partime (source, &pt)) return -1; if (pt.zone == TM_UNDEFINED_ZONE) pt.zone = default_zone; return maketime (&pt, default_time); } #if TEST #include int main (argc, argv) int argc; char **argv; { time_t default_time = time ((time_t *) 0); long default_zone = argv[1] ? atol (argv[1]) : 0; char buf[1000]; while (fgets (buf, sizeof (buf), stdin)) { time_t t = str2time (buf, default_time, default_zone); printf ("%s", asctime (gmtime (&t))); } return 0; } #endif /sys/src/ape/cmd/patch/maketime.h 664 sys sys 1367613436 1356 /* Yield time_t from struct partime yielded by partime. */ /* Copyright 1993, 1994, 1995 Paul Eggert Distributed under license by the Free Software Foundation, Inc. This file is part of RCS. RCS 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. RCS 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 RCS; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. Report problems and direct all questions to: rcs-bugs@cs.purdue.edu */ #if defined __STDC__ || has_prototypes # define __MAKETIME_P(x) x #else # define __MAKETIME_P(x) () #endif struct tm *time2tm __MAKETIME_P ((time_t, int)); time_t difftm __MAKETIME_P ((struct tm const *, struct tm const *)); time_t str2time __MAKETIME_P ((char const *, time_t, long)); time_t tm2time __MAKETIME_P ((struct tm *, int)); void adjzone __MAKETIME_P ((struct tm *, long)); /sys/src/ape/cmd/patch/mkfile 664 sys sys 1367613436 487 #else # if HAVE_CONFIG_H # include # else # ifndef __STDC__ # define const # endif # endif # if HAVE_LIMITS_H # include # endif # ifndef LONG_MIN # define LONG_MIN (-1-2147483647L) # endif # if STDC_HEADERS # include # endif # include # ifdef __STDC__ # define P(x) x # else # define P(x) () # endif #endif #include #if STDC_HEADERS # define CTYPE_DOMAIN(c) 1 #else # define CTYPE_DOMAIN(c) ((unsigned) (c) <= 0177) #endif #define ISALNUM(c) (CTYPE_DOMAIN (c) && isalnum (c)) #define ISALPHA(c) (CTYPE_DOMAIN (c) && isalpha (c)) #define ISSPACE(c) (CTYPE_DOMAIN (c) && isspace (c)) #define ISUPPER(c) (CTYPE_DOMAIN (c) && isupper (c)) #define ISDIGIT(c) ((unsigned) (c) - '0' <= 9) #include char const partimeId[] = "$Id: partime.c,v 5.16 1997/05/19 06:33:53 eggert Exp $"; /* Lookup tables for names of months, weekdays, time zones. */ #define NAME_LENGTH_MAXIMUM 4 struct name_val { char name[NAME_LENGTH_MAXIMUM]; int val; }; static char const *parse_decimal P ((char const *, int, int, int, int, int *, int *)); static char const *parse_fixed P ((char const *, int, int *)); static char const *parse_pattern_letter P ((char const *, int, struct partime *)); static char const *parse_prefix P ((char const *, struct partime *, int *)); static char const *parse_ranged P ((char const *, int, int, int, int *)); static int lookup P ((char const *, struct name_val const[])); static int merge_partime P ((struct partime *, struct partime const *)); static void undefine P ((struct partime *)); static struct name_val const month_names[] = { {"jan", 0}, {"feb", 1}, {"mar", 2}, {"apr", 3}, {"may", 4}, {"jun", 5}, {"jul", 6}, {"aug", 7}, {"sep", 8}, {"oct", 9}, {"nov", 10}, {"dec", 11}, {"", TM_UNDEFINED} }; static struct name_val const weekday_names[] = { {"sun", 0}, {"mon", 1}, {"tue", 2}, {"wed", 3}, {"thu", 4}, {"fri", 5}, {"sat", 6}, {"", TM_UNDEFINED} }; #define hr60nonnegative(t) ((t)/100 * 60 + (t)%100) #define hr60(t) ((t)<0 ? -hr60nonnegative(-(t)) : hr60nonnegative(t)) #define zs(t,s) {s, hr60(t)} #define zd(t,s,d) zs(t, s), zs((t)+100, d) static struct name_val const zone_names[] = { zs (-1000, "hst"), /* Hawaii */ zd (-1000, "hast", "hadt"), /* Hawaii-Aleutian */ zd (- 900, "akst", "akdt"), /* Alaska */ zd (- 800, "pst" , "pdt" ), /* Pacific */ zd (- 700, "mst" , "mdt" ), /* Mountain */ zd (- 600, "cst" , "cdt" ), /* Central */ zd (- 500, "est" , "edt" ), /* Eastern */ zd (- 400, "ast" , "adt" ), /* Atlantic */ zd (- 330, "nst" , "ndt" ), /* Newfoundland */ zs ( 000, "utc" ), /* Coordinated Universal */ zs ( 000, "uct" ), /* " */ zs ( 000, "cut" ), /* " */ zs ( 000, "ut"), /* Universal */ zs ( 000, "z"), /* Zulu (required by ISO 8601) */ zd ( 000, "gmt" , "bst" ), /* Greenwich Mean, British Summer */ zd ( 000, "wet" , "west"), /* Western European */ zd ( 100, "cet" , "cest"), /* Central European */ zd ( 100, "met" , "mest"), /* Middle European (bug in old tz versions) */ zd ( 100, "mez" , "mesz"), /* Mittel-Europaeische Zeit */ zd ( 200, "eet" , "eest"), /* Eastern European */ zs ( 530, "ist" ), /* India */ zd ( 900, "jst" , "jdt" ), /* Japan */ zd ( 900, "kst" , "kdt" ), /* Korea */ zd ( 1200, "nzst", "nzdt"), /* New Zealand */ {"lt", 1}, #if 0 /* The following names are duplicates or are not well attested. There are lots more where these came from. */ zs (-1100, "sst" ), /* Samoan */ zd (- 900, "yst" , "ydt" ), /* Yukon - name is no longer used */ zd (- 500, "ast" , "adt" ), /* Acre */ zd (- 400, "wst" , "wdt" ), /* Western Brazil */ zd (- 400, "cst" , "cdt" ), /* Chile */ zd (- 200, "fst" , "fdt" ), /* Fernando de Noronha */ zs ( 000, "wat" ), /* West African */ zs ( 100, "cat" ), /* Central African */ zs ( 200, "sat" ), /* South African */ zd ( 200, "ist" , "idt" ), /* Israel */ zs ( 300, "eat" ), /* East African */ zd ( 300, "msk" , "msd" ), /* Moscow */ zd ( 330, "ist" , "idt" ), /* Iran */ zs ( 800, "hkt" ), /* Hong Kong */ zs ( 800, "sgt" ), /* Singapore */ zd ( 800, "cst" , "cdt" ), /* China */ zd ( 800, "wst" , "wst" ), /* Western Australia */ zd ( 930, "cst" , "cst" ), /* Central Australia */ zs ( 1000, "gst" ), /* Guam */ zd ( 1000, "est" , "est" ), /* Eastern Australia */ #endif {"", -1} }; /* Look for a prefix of S in TABLE, returning val for first matching entry. */ static int lookup (s, table) char const *s; struct name_val const table[]; { int j; char buf[NAME_LENGTH_MAXIMUM]; for (j = 0; j < NAME_LENGTH_MAXIMUM; j++) { unsigned char c = *s++; if (! ISALPHA (c)) { buf[j] = '\0'; break; } buf[j] = ISUPPER (c) ? tolower (c) : c; } for (;; table++) for (j = 0; ; j++) if (j == NAME_LENGTH_MAXIMUM || ! table[0].name[j]) return table[0].val; else if (buf[j] != table[0].name[j]) break; } /* Set *T to ``undefined'' values. */ static void undefine (t) struct partime *t; { t->tm.tm_sec = t->tm.tm_min = t->tm.tm_hour = t->tm.tm_mday = t->tm.tm_mon = t->tm.tm_year = t->tm.tm_wday = t->tm.tm_yday = t->ymodulus = t->yweek = TM_UNDEFINED; t->zone = TM_UNDEFINED_ZONE; } /* Array of patterns to look for in a date string. Order is important: we look for the first matching pattern whose values do not contradict values that we already know about. See `parse_pattern_letter' below for the meaning of the pattern codes. */ static char const *const patterns[] = { /* These traditional patterns must come first, to prevent an ISO 8601 format from misinterpreting their prefixes. */ "E_n_y", "x", /* RFC 822 */ "E_n", "n_E", "n", "t:m:s_A", "t:m_A", "t_A", /* traditional */ "y/N/D$", /* traditional RCS */ /* ISO 8601:1988 formats, generalized a bit. */ "y-N-D$", "4ND$", "Y-N$", "RND$", "-R=N$", "-R$", "--N=D$", "N=DT", "--N$", "---D$", "DT", "Y-d$", "4d$", "R=d$", "-d$", "dT", "y-W-X", "yWX", "y=W", "-r-W-X", "r-W-XT", "-rWX", "rWXT", "-W=X", "W=XT", "-W", "-w-X", "w-XT", "---X$", "XT", "4$", "T", "h:m:s$", "hms$", "h:m$", "hm$", "h$", "-m:s$", "-ms$", "-m$", "--s$", "Y", "Z", 0 }; /* Parse an initial prefix of STR, setting *T accordingly. Return the first character after the prefix, or 0 if it couldn't be parsed. Start with pattern *PI; if success, set *PI to the next pattern to try. Set *PI to -1 if we know there are no more patterns to try; if *PI is initially negative, give up immediately. */ static char const * parse_prefix (str, t, pi) char const *str; struct partime *t; int *pi; { int i = *pi; char const *pat; unsigned char c; if (i < 0) return 0; /* Remove initial noise. */ while (! ISALNUM (c = *str) && c != '-' && c != '+') { if (! c) { undefine (t); *pi = -1; return str; } str++; } /* Try a pattern until one succeeds. */ while ((pat = patterns[i++]) != 0) { char const *s = str; undefine (t); do { if (! (c = *pat++)) { *pi = i; return s; } } while ((s = parse_pattern_letter (s, c, t)) != 0); } return 0; } /* Parse an initial prefix of S of length DIGITS; it must be a number. Store the parsed number into *RES. Return the first character after the prefix, or 0 if it wasn't parsed. */ static char const * parse_fixed (s, digits, res) char const *s; int digits, *res; { int n = 0; char const *lim = s + digits; while (s < lim) { unsigned d = *s++ - '0'; if (9 < d) return 0; n = 10 * n + d; } *res = n; return s; } /* Parse an initial prefix of S of length DIGITS; it must be a number in the range LO through HI. Store the parsed number into *RES. Return the first character after the prefix, or 0 if it wasn't parsed. */ static char const * parse_ranged (s, digits, lo, hi, res) char const *s; int digits, lo, hi, *res; { s = parse_fixed (s, digits, res); return s && lo <= *res && *res <= hi ? s : 0; } /* Parse an initial prefix of S of length DIGITS; it must be a number in the range LO through HI and it may be followed by a fraction to be computed using RESOLUTION. Store the parsed number into *RES; store the fraction times RESOLUTION, rounded to the nearest integer, into *FRES. Return the first character after the prefix, or 0 if it wasn't parsed. */ static char const * parse_decimal (s, digits, lo, hi, resolution, res, fres) char const *s; int digits, lo, hi, resolution, *res, *fres; { s = parse_fixed (s, digits, res); if (s && lo <= *res && *res <= hi) { int f = 0; if ((s[0] == ',' || s[0] == '.') && ISDIGIT (s[1])) { char const *s1 = ++s; int num10 = 0, denom10 = 10, product; while (ISDIGIT (*++s)) { int d = denom10 * 10; if (d / 10 != denom10) return 0; /* overflow */ denom10 = d; } s = parse_fixed (s1, (int) (s - s1), &num10); product = num10 * resolution; f = (product + (denom10 >> 1)) / denom10; f -= f & (product % denom10 == denom10 >> 1); /* round to even */ if (f < 0 || product/resolution != num10) return 0; /* overflow */ } *fres = f; return s; } return 0; } /* Parse an initial prefix of S; it must denote a time zone. Set *ZONE to the number of seconds east of GMT, or to TM_LOCAL_ZONE if it is the local time zone. Return the first character after the prefix, or 0 if it wasn't parsed. */ char * parzone (s, zone) char const *s; long *zone; { char sign; int hh, mm, ss; int minutesEastOfUTC; long offset, z; /* The formats are LT, n, n DST, nDST, no, o where n is a time zone name and o is a time zone offset of the form [-+]hh[:mm[:ss]]. */ switch (*s) { case '-': case '+': z = 0; break; default: minutesEastOfUTC = lookup (s, zone_names); if (minutesEastOfUTC == -1) return 0; /* Don't bother to check rest of spelling. */ while (ISALPHA ((unsigned char) *s)) s++; /* Don't modify LT. */ if (minutesEastOfUTC == 1) { *zone = TM_LOCAL_ZONE; return (char *) s; } z = minutesEastOfUTC * 60L; /* Look for trailing " DST". */ if ((s[-1] == 'T' || s[-1] == 't') && (s[-2] == 'S' || s[-2] == 's') && (s[-3] == 'D' || s[-3] == 'd')) goto trailing_dst; while (ISSPACE ((unsigned char) *s)) s++; if ((s[0] == 'D' || s[0] == 'd') && (s[1] == 'S' || s[1] == 's') && (s[2] == 'T' || s[2] == 't')) { s += 3; trailing_dst: *zone = z + 60*60; return (char *) s; } switch (*s) { case '-': case '+': break; default: *zone = z; return (char *) s; } break; } sign = *s++; if (! (s = parse_ranged (s, 2, 0, 23, &hh))) return 0; mm = ss = 0; if (*s == ':') s++; if (ISDIGIT (*s)) { if (! (s = parse_ranged (s, 2, 0, 59, &mm))) return 0; if (*s == ':' && s[-3] == ':' && ISDIGIT (s[1]) && ! (s = parse_ranged (s + 1, 2, 0, 59, &ss))) return 0; } if (ISDIGIT (*s)) return 0; offset = (hh * 60 + mm) * 60L + ss; *zone = z + (sign == '-' ? -offset : offset); /* ?? Are fractions allowed here? If so, they're not implemented. */ return (char *) s; } /* Parse an initial prefix of S, matching the pattern whose code is C. Set *T accordingly. Return the first character after the prefix, or 0 if it wasn't parsed. */ static char const * parse_pattern_letter (s, c, t) char const *s; int c; struct partime *t; { switch (c) { case '$': /* The next character must be a non-digit. */ if (ISDIGIT (*s)) return 0; break; case '-': case '/': case ':': /* These characters stand for themselves. */ if (*s++ != c) return 0; break; case '4': /* 4-digit year */ s = parse_fixed (s, 4, &t->tm.tm_year); break; case '=': /* optional '-' */ s += *s == '-'; break; case 'A': /* AM or PM */ /* This matches the regular expression [AaPp][Mm]?. It must not be followed by a letter or digit; otherwise it would match prefixes of strings like "PST". */ switch (*s++) { case 'A': case 'a': if (t->tm.tm_hour == 12) t->tm.tm_hour = 0; break; case 'P': case 'p': if (t->tm.tm_hour != 12) t->tm.tm_hour += 12; break; default: return 0; } switch (*s) { case 'M': case 'm': s++; break; } if (ISALNUM ((unsigned char) *s)) return 0; break; case 'D': /* day of month [01-31] */ s = parse_ranged (s, 2, 1, 31, &t->tm.tm_mday); break; case 'd': /* day of year [001-366] */ s = parse_ranged (s, 3, 1, 366, &t->tm.tm_yday); t->tm.tm_yday--; break; case 'E': /* extended day of month [1-9, 01-31] */ s = parse_ranged (s, (ISDIGIT (s[0]) && ISDIGIT (s[1])) + 1, 1, 31, &t->tm.tm_mday); break; case 'h': /* hour [00-23 followed by optional fraction] */ { int frac; s = parse_decimal (s, 2, 0, 23, 60 * 60, &t->tm.tm_hour, &frac); t->tm.tm_min = frac / 60; t->tm.tm_sec = frac % 60; } break; case 'm': /* minute [00-59 followed by optional fraction] */ s = parse_decimal (s, 2, 0, 59, 60, &t->tm.tm_min, &t->tm.tm_sec); break; case 'n': /* month name [e.g. "Jan"] */ if (! TM_DEFINED (t->tm.tm_mon = lookup (s, month_names))) return 0; /* Don't bother to check rest of spelling. */ while (ISALPHA ((unsigned char) *s)) s++; break; case 'N': /* month [01-12] */ s = parse_ranged (s, 2, 1, 12, &t->tm.tm_mon); t->tm.tm_mon--; break; case 'r': /* year % 10 (remainder in origin-0 decade) [0-9] */ s = parse_fixed (s, 1, &t->tm.tm_year); t->ymodulus = 10; break; case_R: case 'R': /* year % 100 (remainder in origin-0 century) [00-99] */ s = parse_fixed (s, 2, &t->tm.tm_year); t->ymodulus = 100; break; case 's': /* second [00-60 followed by optional fraction] */ { int frac; s = parse_decimal (s, 2, 0, 60, 1, &t->tm.tm_sec, &frac); t->tm.tm_sec += frac; } break; case 'T': /* 'T' or 't' */ switch (*s++) { case 'T': case 't': break; default: return 0; } break; case 't': /* traditional hour [1-9 or 01-12] */ s = parse_ranged (s, (ISDIGIT (s[0]) && ISDIGIT (s[1])) + 1, 1, 12, &t->tm.tm_hour); break; case 'w': /* 'W' or 'w' only (stands for current week) */ switch (*s++) { case 'W': case 'w': break; default: return 0; } break; case 'W': /* 'W' or 'w', followed by a week of year [00-53] */ switch (*s++) { case 'W': case 'w': break; default: return 0; } s = parse_ranged (s, 2, 0, 53, &t->yweek); break; case 'X': /* weekday (1=Mon ... 7=Sun) [1-7] */ s = parse_ranged (s, 1, 1, 7, &t->tm.tm_wday); t->tm.tm_wday--; break; case 'x': /* weekday name [e.g. "Sun"] */ if (! TM_DEFINED (t->tm.tm_wday = lookup (s, weekday_names))) return 0; /* Don't bother to check rest of spelling. */ while (ISALPHA ((unsigned char) *s)) s++; break; case 'y': /* either R or Y */ if (ISDIGIT (s[0]) && ISDIGIT (s[1]) && ! ISDIGIT (s[2])) goto case_R; /* fall into */ case 'Y': /* year in full [4 or more digits] */ { int len = 0; while (ISDIGIT (s[len])) len++; if (len < 4) return 0; s = parse_fixed (s, len, &t->tm.tm_year); } break; case 'Z': /* time zone */ s = parzone (s, &t->zone); break; case '_': /* possibly empty sequence of non-alphanumerics */ while (! ISALNUM ((unsigned char) *s) && *s) s++; break; default: /* bad pattern */ return 0; } return s; } /* If there is no conflict, merge into *T the additional information in *U and return 0. Otherwise do nothing and return -1. */ static int merge_partime (t, u) struct partime *t; struct partime const *u; { # define conflict(a,b) ((a) != (b) && TM_DEFINED (a) && TM_DEFINED (b)) if (conflict (t->tm.tm_sec, u->tm.tm_sec) || conflict (t->tm.tm_min, u->tm.tm_min) || conflict (t->tm.tm_hour, u->tm.tm_hour) || conflict (t->tm.tm_mday, u->tm.tm_mday) || conflict (t->tm.tm_mon, u->tm.tm_mon) || conflict (t->tm.tm_year, u->tm.tm_year) || conflict (t->tm.tm_wday, u->tm.tm_yday) || conflict (t->ymodulus, u->ymodulus) || conflict (t->yweek, u->yweek) || (t->zone != u->zone && t->zone != TM_UNDEFINED_ZONE && u->zone != TM_UNDEFINED_ZONE)) return -1; # undef conflict # define merge_(a,b) if (TM_DEFINED (b)) (a) = (b); merge_ (t->tm.tm_sec, u->tm.tm_sec) merge_ (t->tm.tm_min, u->tm.tm_min) merge_ (t->tm.tm_hour, u->tm.tm_hour) merge_ (t->tm.tm_mday, u->tm.tm_mday) merge_ (t->tm.tm_mon, u->tm.tm_mon) merge_ (t->tm.tm_year, u->tm.tm_year) merge_ (t->tm.tm_wday, u->tm.tm_yday) merge_ (t->ymodulus, u->ymodulus) merge_ (t->yweek, u->yweek) # undef merge_ if (u->zone != TM_UNDEFINED_ZONE) t->zone = u->zone; return 0; } /* Parse a date/time prefix of S, putting the parsed result into *T. Return the first character after the prefix. The prefix may contain no useful information; in that case, *T will contain only undefined values. */ char * partime (s, t) char const *s; struct partime *t; { struct partime p; undefine (t); while (*s) { int i = 0; char const *s1; do { if (! (s1 = parse_prefix (s, &p, &i))) return (char *) s; } while (merge_partime (t, &p) != 0); s = s1; } return (char *) s; } /sys/src/ape/cmd/patch/partime.h 664 sys sys 1367613436 2136 /* Parse a string, yielding a struct partime that describes it. */ /* Copyright 1993, 1994, 1995, 1997 Paul Eggert Distributed under license by the Free Software Foundation, Inc. This file is part of RCS. RCS 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. RCS 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 RCS; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. Report problems and direct all questions to: rcs-bugs@cs.purdue.edu */ #define TM_UNDEFINED (-1) #define TM_DEFINED(x) (0 <= (x)) /* #include if you want to use these symbols. */ #define TM_LOCAL_ZONE LONG_MIN #define TM_UNDEFINED_ZONE (LONG_MIN + 1) struct partime { /* This structure describes the parsed time. Only the following tm_* values in it are used: sec, min, hour, mday, mon, year, wday, yday. If TM_UNDEFINED (value), the parser never found the value. The tm_year field is the actual year, not the year - 1900; but see ymodulus below. */ struct tm tm; /* If !TM_UNDEFINED (ymodulus), then tm.tm_year is actually modulo ymodulus. */ int ymodulus; /* Week of year, ISO 8601 style. If TM_UNDEFINED (yweek), the parser never found yweek. Weeks start on Mondays. Week 1 includes Jan 4. */ int yweek; /* Seconds east of UTC; or TM_LOCAL_ZONE or TM_UNDEFINED_ZONE. */ long zone; }; #if defined __STDC__ || has_prototypes # define __PARTIME_P(x) x #else # define __PARTIME_P(x) () #endif char *partime __PARTIME_P ((char const *, struct partime *)); char *parzone __PARTIME_P ((char const *, long *)); /sys/src/ape/cmd/patch/patch.1 664 sys sys 1367613436 29906 .\" patch man page .de Id .ds Dt \\$4 .. .Id $Id: patch.man,v 1.23 1997/07/16 12:26:36 eggert Exp $ .ds = \-\^\- .de Sp .if t .sp .3 .if n .sp .. .TH PATCH 1 \*(Dt GNU .ta 3n .SH NAME patch \- apply a diff file to an original .SH SYNOPSIS .B patch .RI [ options ] .RI [ originalfile .RI [ patchfile ]] .Sp but usually just .Sp .BI "patch \-p" "num" .BI < patchfile .SH DESCRIPTION .B patch takes a patch file .I patchfile containing a difference listing produced by the .B diff program and applies those differences to one or more original files, producing patched versions. Normally the patched versions are put in place of the originals. Backups can be made; see the .B \-b or .B \*=backup option. The names of the files to be patched are usually taken from the patch file, but if there's just one file to be patched it can specified on the command line as .IR originalfile . .PP Upon startup, patch attempts to determine the type of the diff listing, unless overruled by a \fB\-c\fP (\fB\*=context\fP), \fB\-e\fP (\fB\*=ed\fP), \fB\-n\fP (\fB\*=normal\fP), or \fB\-u\fP (\fB\*=unified\fP) option. Context diffs (old-style, new-style, and unified) and normal diffs are applied by the .B patch program itself, while .B ed diffs are simply fed to the .BR ed (1) editor via a pipe. .PP .B patch tries to skip any leading garbage, apply the diff, and then skip any trailing garbage. Thus you could feed an article or message containing a diff listing to .BR patch , and it should work. If the entire diff is indented by a consistent amount, or if a context diff is encapsulated one or more times by prepending "\fB\- \fP" to lines starting with "\fB\-\fP" as specified by Internet RFC 934, this is taken into account. .PP With context diffs, and to a lesser extent with normal diffs, .B patch can detect when the line numbers mentioned in the patch are incorrect, and attempts to find the correct place to apply each hunk of the patch. As a first guess, it takes the line number mentioned for the hunk, plus or minus any offset used in applying the previous hunk. If that is not the correct place, .B patch scans both forwards and backwards for a set of lines matching the context given in the hunk. First .B patch looks for a place where all lines of the context match. If no such place is found, and it's a context diff, and the maximum fuzz factor is set to 1 or more, then another scan takes place ignoring the first and last line of context. If that fails, and the maximum fuzz factor is set to 2 or more, the first two and last two lines of context are ignored, and another scan is made. (The default maximum fuzz factor is 2.) If .B patch cannot find a place to install that hunk of the patch, it puts the hunk out to a reject file, which normally is the name of the output file plus a .B \&.rej suffix, or .B # if .B \&.rej would generate a file name that is too long (if even appending the single character .B # makes the file name too long, then .B # replaces the file name's last character). (The rejected hunk comes out in ordinary context diff form regardless of the input patch's form. If the input was a normal diff, many of the contexts are simply null.) The line numbers on the hunks in the reject file may be different than in the patch file: they reflect the approximate location patch thinks the failed hunks belong in the new file rather than the old one. .PP As each hunk is completed, you are told if the hunk failed, and if so which line (in the new file) .B patch thought the hunk should go on. If the hunk is installed at a different line from the line number specified in the diff you are told the offset. A single large offset .I may indicate that a hunk was installed in the wrong place. You are also told if a fuzz factor was used to make the match, in which case you should also be slightly suspicious. If the .B \*=verbose option is given, you are also told about hunks that match exactly. .PP If no original file .I origfile is specified on the command line, .B patch tries to figure out from the leading garbage what the name of the file to edit is, using the following rules. .TP 3 .B " \(bu" If the header is that of a context diff, .B patch takes the old and new file names in the header. Any .B /dev/null names are ignored. .TP .B " \(bu" If there is an .B Index:\& line in the leading garbage and if either the old and new names are both absent or the .B POSIXLY_CORRECT environment variable is set, .B patch takes the name in the .B Index:\& line. .TP .B " \(bu" For the purpose of the following rules, the names are considered to be in the order (old, new, index), regardless of the order that they appear in the header. .TP .B " \(bu" If some of the named files exist, .B patch uses the first name if the .B POSIXLY_CORRECT environment variable is set, and the best name otherwise. .TP .B " \(bu" If .B patch is not ignoring \s-1RCS\s0 and \s-1SCCS\s0 (see the .BI "\-g\ " num or .BI \*=get= num option), and no named files exist but an \s-1RCS\s0 or \s-1SCCS\s0 master is found, .B patch uses the first named file with an \s-1RCS\s0 or \s-1SCCS\s0 master. .TP .B " \(bu" If no named files exist, no \s-1RCS\s0 or \s-1SCCS\s0 master was found, some names are given, .B POSIXLY_CORRECT is not set, and the patch appears to create a file, .B patch uses the best name requiring the creation of the fewest directories. .TP .B " \(bu" If no file name results from the above heuristics, you are asked for the name of the file to patch. .LP To determine the .I best of a nonempty list of file names, .B patch first takes all the names with the fewest path name components; of those, it then takes all the names with the shortest basename; of those, it then takes all the shortest names; finally, it takes the first remaining name. .PP Additionally, if the leading garbage contains a .B Prereq:\& line, .B patch takes the first word from the prerequisites line (normally a version number) and checks the original file to see if that word can be found. If not, .B patch asks for confirmation before proceeding. .PP The upshot of all this is that you should be able to say, while in a news interface, something like the following: .Sp \fB| patch \-d /usr/src/local/blurfl\fP .Sp and patch a file in the .B blurfl directory directly from the article containing the patch. .PP If the patch file contains more than one patch, .B patch tries to apply each of them as if they came from separate patch files. This means, among other things, that it is assumed that the name of the file to patch must be determined for each diff listing, and that the garbage before each diff listing contains interesting things such as file names and revision level, as mentioned previously. .SH OPTIONS .TP 3 \fB\-b\fP or \fB\*=backup\fP Make backup files. That is, when patching a file, rename or copy the original instead of removing it. When backing up a file that does not exist, an empty, unreadable backup file is created as a placeholder to represent the nonexistent file. See the .B \-V or .B \*=version\-control option for details about how backup file names are determined. .TP .B \*=backup\-if\-mismatch Back up a file if the patch does not match the file exactly and if backups are not otherwise requested. This is the default unless the .B POSIXLY_CORRECT environment variable is set. .TP .B \*=no\-backup\-if\-mismatch Do not back up a file if the patch does not match the file exactly and if backups are not otherwise requested. This is the default if the .B POSIXLY_CORRECT environment variable is set. .TP \fB\-B\fP \fIpref\fP or \fB\*=prefix=\fP\fIpref\fP Prefix .I pref to a file name when generating its simple backup file name. For example, with .B "\-B\ /junk/" the simple backup file name for .B src/patch/util.c is .BR /junk/src/patch/util.c . .TP \fB\*=binary\fP Read and write all files in binary mode, except for standard output and .BR /dev/tty . This option has no effect on \s-1POSIX\s0-compliant systems. On systems like \s-1DOS\s0 where this option makes a difference, the patch should be generated by .BR "diff\ \-a\ \*=binary" . .TP \fB\-c\fP or \fB\*=context\fP Interpret the patch file as a ordinary context diff. .TP \fB\-d\fP \fIdir\fP or \fB\*=directory=\fP\fIdir\fP Change to the directory .I dir immediately, before doing anything else. .TP \fB\-D\fP \fIdefine\fP or \fB\*=ifdef=\fP\fIdefine\fP Use the .BR #ifdef " .\|.\|. " #endif construct to mark changes, with .I define as the differentiating symbol. .TP .B "\*=dry\-run" Print the results of applying the patches without actually changing any files. .TP \fB\-e\fP or \fB\*=ed\fP Interpret the patch file as an .B ed script. .TP \fB\-E\fP or \fB\*=remove\-empty\-files\fP Remove output files that are empty after the patches have been applied. Normally this option is unnecessary, since .B patch can examine the time stamps on the header to determine whether a file should exist after patching. However, if the input is not a context diff or if the .B POSIXLY_CORRECT environment variable is set, .B patch does not remove empty patched files unless this option is given. When .B patch removes a file, it also attempts to remove any empty ancestor directories. .TP \fB\-f\fP or \fB\*=force\fP Assume that the user knows exactly what he or she is doing, and do not ask any questions. Skip patches whose headers do not say which file is to be patched; patch files even though they have the wrong version for the .B Prereq:\& line in the patch; and assume that patches are not reversed even if they look like they are. This option does not suppress commentary; use .B \-s for that. .TP \fB\-F\fP \fInum\fP or \fB\*=fuzz=\fP\fInum\fP Set the maximum fuzz factor. This option only applies to diffs that have context, and causes .B patch to ignore up to that many lines in looking for places to install a hunk. Note that a larger fuzz factor increases the odds of a faulty patch. The default fuzz factor is 2, and it may not be set to more than the number of lines of context in the context diff, ordinarily 3. .TP \fB\-g\fP \fInum\fP or \fB\*=get=\fP\fInum\fP This option controls .BR patch 's actions when a file is under \s-1RCS\s0 or \s-1SCCS\s0 control, and does not exist or is read-only and matches the default version. If .I num is positive, .B patch gets (or checks out) the file from the revision control system; if zero, .B patch ignores \s-1RCS\s0 and \s-1SCCS\s0 and does not get the file; and if negative, .B patch asks the user whether to get the file. The default value of this option is given by the value of the .B PATCH_GET environment variable if it is set; if not, the default value is zero if .B POSIXLY_CORRECT is set, negative otherwise. .TP .B "\*=help" Print a summary of options and exit. .TP \fB\-i\fP \fIpatchfile\fP or \fB\*=input=\fP\fIpatchfile\fP Read the patch from .IR patchfile . If .I patchfile is .BR \- , read from standard input, the default. .TP \fB\-l\fP or \fB\*=ignore\-whitespace\fP Match patterns loosely, in case tabs or spaces have been munged in your files. Any sequence of one or more blanks in the patch file matches any sequence in the original file, and sequences of blanks at the ends of lines are ignored. Normal characters must still match exactly. Each line of the context must still match a line in the original file. .TP \fB\-n\fP or \fB\*=normal\fP Interpret the patch file as a normal diff. .TP \fB\-N\fP or \fB\*=forward\fP Ignore patches that seem to be reversed or already applied. See also .BR \-R . .TP \fB\-o\fP \fIoutfile\fP or \fB\*=output=\fP\fIoutfile\fP Send output to .I outfile instead of patching files in place. .TP \fB\-p\fP\fInum\fP or \fB\*=strip\fP\fB=\fP\fInum\fP Strip the smallest prefix containing .I num leading slashes from each file name found in the patch file. A sequence of one or more adjacent slashes is counted as a single slash. This controls how file names found in the patch file are treated, in case you keep your files in a different directory than the person who sent out the patch. For example, supposing the file name in the patch file was .Sp \fB/u/howard/src/blurfl/blurfl.c\fP .Sp setting .B \-p0 gives the entire file name unmodified, .B \-p1 gives .Sp \fBu/howard/src/blurfl/blurfl.c\fP .Sp without the leading slash, .B \-p4 gives .Sp \fBblurfl/blurfl.c\fP .Sp and not specifying .B \-p at all just gives you \fBblurfl.c\fP. Whatever you end up with is looked for either in the current directory, or the directory specified by the .B \-d option. .TP \fB\-r\fP \fIrejectfile\fP or \fB\*=reject\-file=\fP\fIrejectfile\fP Put rejects into .I rejectfile instead of the default .B \&.rej file. .TP \fB\-R\fP or \fB\*=reverse\fP Assume that this patch was created with the old and new files swapped. (Yes, I'm afraid that does happen occasionally, human nature being what it is.) .B patch attempts to swap each hunk around before applying it. Rejects come out in the swapped format. The .B \-R option does not work with .B ed diff scripts because there is too little information to reconstruct the reverse operation. .Sp If the first hunk of a patch fails, .B patch reverses the hunk to see if it can be applied that way. If it can, you are asked if you want to have the .B \-R option set. If it can't, the patch continues to be applied normally. (Note: this method cannot detect a reversed patch if it is a normal diff and if the first command is an append (i.e. it should have been a delete) since appends always succeed, due to the fact that a null context matches anywhere. Luckily, most patches add or change lines rather than delete them, so most reversed normal diffs begin with a delete, which fails, triggering the heuristic.) .TP \fB\-s\fP or \fB\*=silent\fP or \fB\*=quiet\fP Work silently, unless an error occurs. .TP \fB\-t\fP or \fB\*=batch\fP Suppress questions like .BR \-f , but make some different assumptions: skip patches whose headers do not contain file names (the same as \fB\-f\fP); skip patches for which the file has the wrong version for the .B Prereq:\& line in the patch; and assume that patches are reversed if they look like they are. .TP \fB\-T\fP or \fB\*=set\-time\fP Set the modification and access times of patched files from time stamps given in context diff headers, assuming that the context diff headers use local time. This option is not recommended, because patches using local time cannot easily be used by people in other time zones, and because local time stamps are ambiguous when local clocks move backwards during daylight-saving time adjustments. Instead of using this option, generate patches with \s-1UTC\s0 and use the .B \-Z or .B \*=set\-utc option instead. .TP \fB\-u\fP or \fB\*=unified\fP Interpret the patch file as a unified context diff. .TP \fB\-v\fP or \fB\*=version\fP Print out .BR patch 's revision header and patch level, and exit. .TP \fB\-V\fP \fImethod\fP or \fB\*=version\-control=\fP\fImethod\fP Use .I method to determine backup file names. The method can also be given by the .B PATCH_VERSION_CONTROL (or, if that's not set, the .BR VERSION_CONTROL ) environment variable, which is overridden by this option. The method does not affect whether backup files are made; it affects only the names of any backup files that are made. .Sp The value of .I method is like the \s-1GNU\s0 Emacs `version-control' variable; .B patch also recognizes synonyms that are more descriptive. The valid values for .I method are (unique abbreviations are accepted): .RS .TP 3 \fBexisting\fP or \fBnil\fP Make numbered backups of files that already have them, otherwise simple backups. This is the default. .TP \fBnumbered\fP or \fBt\fP Make numbered backups. The numbered backup file name for .I F is .IB F .~ N ~ where .I N is the version number. .TP \fBsimple\fP or \fBnever\fP Make simple backups. The .B \-B or .BR \*=prefix , .B \-Y or .BR \*=basename\-prefix , and .B \-z or .BR \*=suffix options specify the simple backup file name. If none of these options are given, then a simple backup suffix is used; it is the value of the .B SIMPLE_BACKUP_SUFFIX environment variable if set, and is .B \&.orig otherwise. .PP With numbered or simple backups, if the backup file name is too long, the backup suffix .B ~ is used instead; if even appending .B ~ would make the name too long, then .B ~ replaces the last character of the file name. .RE .TP \fB\*=verbose\fP Output extra information about the work being done. .TP \fB\-x\fP \fInum\fP or \fB\*=debug=\fP\fInum\fP Set internal debugging flags of interest only to .B patch patchers. .TP \fB\-Y\fP \fIpref\fP or \fB\*=basename\-prefix=\fP\fIpref\fP Prefix .I pref to the basename of a file name when generating its simple backup file name. For example, with .B "\-Y\ .del/" the simple backup file name for .B src/patch/util.c is .BR src/patch/.del/util.c . .TP \fB\-z\fP \fIsuffix\fP or \fB\*=suffix=\fP\fIsuffix\fP Use .I suffix as the simple backup suffix. For example, with .B "\-z\ -" the simple backup file name for .B src/patch/util.c is .BR src/patch/util.c- . The backup suffix may also be specified by the .B SIMPLE_BACKUP_SUFFIX environment variable, which is overridden by this option. .TP \fB\-Z\fP or \fB\*=set\-utc\fP Set the modification and access times of patched files from time stamps given in context diff headers, assuming that the context diff headers use Coordinated Universal Time (\s-1UTC\s0, often known as \s-1GMT\s0). Also see the .B \-T or .B \*=set\-time option. .Sp The .B \-Z or .B \*=set\-utc and .B \-T or .B \*=set\-time options normally refrain from setting a file's time if the file's original time does not match the time given in the patch header, or if its contents do not match the patch exactly. However, if the .B \-f or .B \*=force option is given, the file time is set regardless. .Sp Due to the limitations of .B diff output format, these options cannot update the times of files whose contents have not changed. Also, if you use these options, you should remove (e.g. with .BR "make\ clean" ) all files that depend on the patched files, so that later invocations of .B make do not get confused by the patched files' times. .SH ENVIRONMENT .TP 3 \fBPATCH_GET\fP This specifies whether .B patch gets missing or read-only files from \s-1RCS\s0 or \s-1SCCS\s0 by default; see the .B \-g or .B \*=get option. .TP .B POSIXLY_CORRECT If set, .B patch conforms more strictly to the \s-1POSIX\s0 standard: it takes the first existing file from the list (old, new, index) when intuiting file names from diff headers, it does not remove files that are empty after patching, it does not ask whether to get files from \s-1RCS\s0 or \s-1SCCS\s0, it requires that all options precede the files in the command line, and it does not backup files when there is a mismatch. .TP .B SIMPLE_BACKUP_SUFFIX Extension to use for simple backup file names instead of .BR \&.orig . .TP \fBTMPDIR\fP, \fBTMP\fP, \fBTEMP\fP Directory to put temporary files in; .B patch uses the first environment variable in this list that is set. If none are set, the default is system-dependent; it is normally .B /tmp on Unix hosts. .TP \fBVERSION_CONTROL\fP or \fBPATCH_VERSION_CONTROL\fP Selects version control style; see the .B \-v or .B \*=version\-control option. .SH FILES .TP 3 .IB $TMPDIR "/p\(**" temporary files .TP .B /dev/tty controlling terminal; used to get answers to questions asked of the user .SH "SEE ALSO" .BR diff (1), .BR ed (1) .Sp Marshall T. Rose and Einar A. Stefferud, Proposed Standard for Message Encapsulation, Internet RFC 934 (1985-01). .SH "NOTES FOR PATCH SENDERS" There are several things you should bear in mind if you are going to be sending out patches. .PP Create your patch systematically. A good method is the command .BI "diff\ \-Naur\ " "old\ new" where .I old and .I new identify the old and new directories. The names .I old and .I new should not contain any slashes. The .B diff command's headers should have dates and times in Universal Time using traditional Unix format, so that patch recipients can use the .B \-Z or .B \*=set\-utc option. Here is an example command, using Bourne shell syntax: .Sp \fBLC_ALL=C TZ=UTC0 diff \-Naur gcc\-2.7 gcc\-2.8\fP .PP Tell your recipients how to apply the patch by telling them which directory to .B cd to, and which .B patch options to use. The option string .B "\-Np1" is recommended. Test your procedure by pretending to be a recipient and applying your patch to a copy of the original files. .PP You can save people a lot of grief by keeping a .B patchlevel.h file which is patched to increment the patch level as the first diff in the patch file you send out. If you put a .B Prereq:\& line in with the patch, it won't let them apply patches out of order without some warning. .PP You can create a file by sending out a diff that compares .B /dev/null or an empty file dated the Epoch (1970-01-01 00:00:00 \s-1UTC\s0) to the file you want to create. This only works if the file you want to create doesn't exist already in the target directory. Conversely, you can remove a file by sending out a context diff that compares the file to be deleted with an empty file dated the Epoch. The file will be removed unless the .B POSIXLY_CORRECT environment variable is set and the .B \-E or .B \*=remove\-empty\-files option is not given. An easy way to generate patches that create and remove files is to use \s-1GNU\s0 .BR diff 's .B \-N or .B \*=new\-file option. .PP If the recipient is supposed to use the .BI \-p N option, do not send output that looks like this: .Sp .ft B .ne 3 diff \-Naur v2.0.29/prog/README prog/README .br \-\^\-\^\- v2.0.29/prog/README Mon Mar 10 15:13:12 1997 .br +\^+\^+ prog/README Mon Mar 17 14:58:22 1997 .ft .Sp because the two file names have different numbers of slashes, and different versions of .B patch interpret the file names differently. To avoid confusion, send output that looks like this instead: .Sp .ft B .ne 3 diff \-Naur v2.0.29/prog/README v2.0.30/prog/README .br \-\^\-\^\- v2.0.29/prog/README Mon Mar 10 15:13:12 1997 .br +\^+\^+ v2.0.30/prog/README Mon Mar 17 14:58:22 1997 .ft .Sp .PP Avoid sending patches that compare backup file names like .BR README.orig , since this might confuse .B patch into patching a backup file instead of the real file. Instead, send patches that compare the same base file names in different directories, e.g.\& .B old/README and .BR new/README . .PP Take care not to send out reversed patches, since it makes people wonder whether they already applied the patch. .PP Try not to have your patch modify derived files (e.g. the file .B configure where there is a line .B "configure: configure.in" in your makefile), since the recipient should be able to regenerate the derived files anyway. If you must send diffs of derived files, generate the diffs using \s-1UTC\s0, have the recipients apply the patch with the .B \-Z or .B \*=set\-utc option, and have them remove any unpatched files that depend on patched files (e.g. with .BR "make\ clean" ). .PP While you may be able to get away with putting 582 diff listings into one file, it may be wiser to group related patches into separate files in case something goes haywire. .SH DIAGNOSTICS Diagnostics generally indicate that .B patch couldn't parse your patch file. .PP If the .B \*=verbose option is given, the message .B Hmm.\|.\|.\& indicates that there is unprocessed text in the patch file and that .B patch is attempting to intuit whether there is a patch in that text and, if so, what kind of patch it is. .PP .BR patch 's exit status is 0 if all hunks are applied successfully, 1 if some hunks cannot be applied, and 2 if there is more serious trouble. When applying a set of patches in a loop it behooves you to check this exit status so you don't apply a later patch to a partially patched file. .SH CAVEATS Context diffs cannot reliably represent the creation or deletion of empty files, empty directories, or special files such as symbolic links. Nor can they represent changes to file metadata like ownership, permissions, or whether one file is a hard link to another. If changes like these are also required, separate instructions (e.g. a shell script) to accomplish them should accompany the patch. .PP .B patch cannot tell if the line numbers are off in an .B ed script, and can detect bad line numbers in a normal diff only when it finds a change or deletion. A context diff using fuzz factor 3 may have the same problem. Until a suitable interactive interface is added, you should probably do a context diff in these cases to see if the changes made sense. Of course, compiling without errors is a pretty good indication that the patch worked, but not always. .PP .B patch usually produces the correct results, even when it has to do a lot of guessing. However, the results are guaranteed to be correct only when the patch is applied to exactly the same version of the file that the patch was generated from. .SH "COMPATIBILITY ISSUES" The \s-1POSIX\s0 standard specifies behavior that differs from .BR patch 's traditional behavior. You should be aware of these differences if you must interoperate with .B patch versions 2.1 and earlier, which are not \s-1POSIX\s0-compliant. .TP 3 .B " \(bu" In traditional .BR patch , the .B \-p option's operand was optional, and a bare .B \-p was equivalent to .BR \-p0. The .B \-p option now requires an operand, and .B "\-p\ 0" is now equivalent to .BR \-p0 . For maximum compatibility, use options like .B \-p0 and .BR \-p1 . .Sp Also, traditional .B patch simply counted slashes when stripping path prefixes; .B patch now counts pathname components. That is, a sequence of one or more adjacent slashes now counts as a single slash. For maximum portability, avoid sending patches containing .B // in file names. .TP .B " \(bu" In traditional .BR patch , backups were enabled by default. This behavior is now enabled with the .B \-b or .B \*=backup option. .Sp Conversely, in \s-1POSIX\s0 .BR patch , backups are never made, even when there is a mismatch. In \s-1GNU\s0 .BR patch , this behavior is enabled with the .B \*=no\-backup\-if\-mismatch option or by setting the .B POSIXLY_CORRECT environment variable. .Sp The .BI \-b "\ suffix" option of traditional .B patch is equivalent to the .BI "\-b\ \-z" "\ suffix" options of \s-1GNU\s0 .BR patch . .TP .B " \(bu" Traditional .B patch used a complicated (and incompletely documented) method to intuit the name of the file to be patched from the patch header. This method was not \s-1POSIX\s0-compliant, and had a few gotchas. Now .B patch uses a different, equally complicated (but better documented) method that is optionally \s-1POSIX\s0-compliant; we hope it has fewer gotchas. The two methods are compatible if the file names in the context diff header and the .B Index:\& line are all identical after prefix-stripping. Your patch is normally compatible if each header's file names all contain the same number of slashes. .TP .B " \(bu" When traditional .B patch asked the user a question, it sent the question to standard error and looked for an answer from the first file in the following list that was a terminal: standard error, standard output, .BR /dev/tty , and standard input. Now .B patch sends questions to standard output and gets answers from .BR /dev/tty . Defaults for some answers have been changed so that .B patch never goes into an infinite loop when using default answers. .TP .B " \(bu" Traditional .B patch exited with a status value that counted the number of bad hunks, or with status 1 if there was real trouble. Now .B patch exits with status 1 if some hunks failed, or with 2 if there was real trouble. .TP .B " \(bu" Limit yourself to the following options when sending instructions meant to be executed by anyone running \s-1GNU\s0 .BR patch , traditional .BR patch , or a \s-1POSIX\s0-compliant .BR patch . Spaces are significant in the following list, and operands are required. .Sp .nf .in +3 .ne 11 .B \-c .BI \-d " dir" .BI \-D " define" .B \-e .B \-l .B \-n .B \-N .BI \-o " outfile" .BI \-p num .B \-R .BI \-r " rejectfile" .in .fi .SH BUGS .B patch could be smarter about partial matches, excessively deviant offsets and swapped code, but that would take an extra pass. .PP If code has been duplicated (for instance with \fB#ifdef OLDCODE\fP .\|.\|. \fB#else .\|.\|. #endif\fP), .B patch is incapable of patching both versions, and, if it works at all, will likely patch the wrong one, and tell you that it succeeded to boot. .PP If you apply a patch you've already applied, .B patch thinks it is a reversed patch, and offers to un-apply the patch. This could be construed as a feature. .SH COPYING Copyright .if t \(co 1984, 1985, 1986, 1988 Larry Wall. .br Copyright .if t \(co 1997 Free Software Foundation, Inc. .PP Permission is granted to make and distribute verbatim copies of this manual provided the copyright notice and this permission notice are preserved on all copies. .PP Permission is granted to copy and distribute modified versions of this manual under the conditions for verbatim copying, provided that the entire resulting derived work is distributed under the terms of a permission notice identical to this one. .PP Permission is granted to copy and distribute translations of this manual into another language, under the above conditions for modified versions, except that this permission notice may be included in translations approved by the copyright holders instead of in the original English. .SH AUTHORS Larry Wall wrote the original version of .BR patch . Paul Eggert removed .BR patch 's arbitrary limits; added support for binary files, setting file times, and deleting files; and made it conform better to \s-1POSIX\s0. Other contributors include Wayne Davison, who added unidiff support, and David MacKenzie, who added configuration and backup support. /sys/src/ape/cmd/patch/patch.c 664 sys sys 1367613436 33892 /* patch - a program to apply diffs to original files */ /* $Id: patch.c,v 1.23 1997/07/05 10:32:23 eggert Exp $ */ /* Copyright 1984, 1985, 1986, 1987, 1988 Larry Wall Copyright 1989, 1990, 1991, 1992, 1993, 1997 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; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #define XTERN #include #undef XTERN #define XTERN extern #include #include #include #include #include #include #include #if HAVE_UTIME_H # include #endif /* Some nonstandard hosts don't declare this structure even in . */ #if ! HAVE_STRUCT_UTIMBUF struct utimbuf { time_t actime; time_t modtime; }; #endif /* Output stream state. */ struct outstate { FILE *ofp; int after_newline; int zero_output; }; /* procedures */ static FILE *create_output_file PARAMS ((char const *)); static LINENUM locate_hunk PARAMS ((LINENUM)); static bool apply_hunk PARAMS ((struct outstate *, LINENUM)); static bool copy_till PARAMS ((struct outstate *, LINENUM)); static bool patch_match PARAMS ((LINENUM, LINENUM, LINENUM, LINENUM)); static bool similar PARAMS ((char const *, size_t, char const *, size_t)); static bool spew_output PARAMS ((struct outstate *)); static char const *make_temp PARAMS ((int)); static int numeric_string PARAMS ((char const *, int, char const *)); static void abort_hunk PARAMS ((void)); static void cleanup PARAMS ((void)); static void get_some_switches PARAMS ((void)); static void init_output PARAMS ((char const *, struct outstate *)); static void init_reject PARAMS ((char const *)); static void reinitialize_almost_everything PARAMS ((void)); static void usage PARAMS ((FILE *, int)) __attribute__((noreturn)); static int make_backups; static int backup_if_mismatch; static char const *version_control; static int remove_empty_files; /* TRUE if -R was specified on command line. */ static int reverse_flag_specified; /* how many input lines have been irretractably output */ static LINENUM last_frozen_line; static char const *do_defines; /* symbol to patch using ifdef, ifndef, etc. */ static char const if_defined[] = "\n#ifdef %s\n"; static char const not_defined[] = "#ifndef %s\n"; static char const else_defined[] = "\n#else\n"; static char const end_defined[] = "\n#endif /* %s */\n"; static int Argc; static char * const *Argv; static FILE *rejfp; /* reject file pointer */ static char const *patchname; static char *rejname; static char const * volatile TMPREJNAME; static LINENUM last_offset; static LINENUM maxfuzz = 2; static char serrbuf[BUFSIZ]; char const program_name[] = "patch"; /* Apply a set of diffs as appropriate. */ int main PARAMS ((int, char **)); int main(argc,argv) int argc; char **argv; { char const *val; bool somefailed = FALSE; struct outstate outstate; init_time (); setbuf(stderr, serrbuf); bufsize = 8 * 1024; buf = xmalloc (bufsize); strippath = INT_MAX; posixly_correct = getenv ("POSIXLY_CORRECT") != 0; backup_if_mismatch = ! posixly_correct; patch_get = ((val = getenv ("PATCH_GET")) ? numeric_string (val, 1, "PATCH_GET value") : posixly_correct - 1); { char const *v = getenv ("SIMPLE_BACKUP_SUFFIX"); if (v && *v) simple_backup_suffix = v; } version_control = getenv ("PATCH_VERSION_CONTROL"); if (! version_control) version_control = getenv ("VERSION_CONTROL"); /* Cons up the names of the global temporary files. Do this before `cleanup' can possibly be called (e.g. by `pfatal'). */ TMPOUTNAME = make_temp ('o'); TMPINNAME = make_temp ('i'); TMPREJNAME = make_temp ('r'); TMPPATNAME = make_temp ('p'); /* parse switches */ Argc = argc; Argv = argv; get_some_switches(); if (make_backups | backup_if_mismatch) backup_type = get_version (version_control); init_output (outfile, &outstate); /* Make sure we clean up in case of disaster. */ set_signals(0); for ( open_patch_file (patchname); there_is_another_patch(); reinitialize_almost_everything() ) { /* for each patch in patch file */ int hunk = 0; int failed = 0; int mismatch = 0; char *outname = outfile ? outfile : inname; if (!skip_rest_of_patch) get_input_file (inname, outname); if (diff_type == ED_DIFF) { outstate.zero_output = 0; if (! dry_run) { do_ed_script (outstate.ofp); if (! outfile) { struct stat statbuf; if (stat (TMPOUTNAME, &statbuf) != 0) pfatal ("%s", TMPOUTNAME); outstate.zero_output = statbuf.st_size == 0; } } } else { int got_hunk; int apply_anyway = 0; /* initialize the patched file */ if (! skip_rest_of_patch && ! outfile) init_output (TMPOUTNAME, &outstate); /* initialize reject file */ init_reject(TMPREJNAME); /* find out where all the lines are */ if (!skip_rest_of_patch) scan_input (inname); /* from here on, open no standard i/o files, because malloc */ /* might misfire and we can't catch it easily */ /* apply each hunk of patch */ while (0 < (got_hunk = another_hunk (diff_type, reverse))) { LINENUM where = 0; /* Pacify `gcc -Wall'. */ LINENUM newwhere; LINENUM fuzz = 0; LINENUM prefix_context = pch_prefix_context (); LINENUM suffix_context = pch_suffix_context (); LINENUM context = (prefix_context < suffix_context ? suffix_context : prefix_context); LINENUM mymaxfuzz = (maxfuzz < context ? maxfuzz : context); hunk++; if (!skip_rest_of_patch) { do { where = locate_hunk(fuzz); if (! where || fuzz || last_offset) mismatch = 1; if (hunk == 1 && ! where && ! (force | apply_anyway) && reverse == reverse_flag_specified) { /* dwim for reversed patch? */ if (!pch_swap()) { say ( "Not enough memory to try swapped hunk! Assuming unswapped.\n"); continue; } /* Try again. */ where = locate_hunk (fuzz); if (where && (ok_to_reverse ("%s patch detected!", (reverse ? "Unreversed" : "Reversed (or previously applied)")))) reverse ^= 1; else { /* Put it back to normal. */ if (! pch_swap ()) fatal ("lost hunk on alloc error!"); if (where) { apply_anyway = 1; fuzz--; /* Undo `++fuzz' below. */ where = 0; } } } } while (!skip_rest_of_patch && !where && ++fuzz <= mymaxfuzz); if (skip_rest_of_patch) { /* just got decided */ if (outstate.ofp && ! outfile) { fclose (outstate.ofp); outstate.ofp = 0; } } } newwhere = pch_newfirst() + last_offset; if (skip_rest_of_patch) { abort_hunk(); failed++; if (verbosity == VERBOSE) say ("Hunk #%d ignored at %ld.\n", hunk, newwhere); } else if (!where || (where == 1 && pch_says_nonexistent (reverse) && instat.st_size)) { if (where) say ("Patch attempted to create file `%s', which already exists.\n", inname); abort_hunk(); failed++; if (verbosity != SILENT) say ("Hunk #%d FAILED at %ld.\n", hunk, newwhere); } else if (! apply_hunk (&outstate, where)) { abort_hunk (); failed++; if (verbosity != SILENT) say ("Hunk #%d FAILED at %ld.\n", hunk, newwhere); } else { if (verbosity == VERBOSE || (verbosity != SILENT && (fuzz || last_offset))) { say ("Hunk #%d succeeded at %ld", hunk, newwhere); if (fuzz) say (" with fuzz %ld", fuzz); if (last_offset) say (" (offset %ld line%s)", last_offset, last_offset==1?"":"s"); say (".\n"); } } } if (got_hunk < 0 && using_plan_a) { if (outfile) fatal ("out of memory using Plan A"); say ("\n\nRan out of memory using Plan A -- trying again...\n\n"); if (outstate.ofp) { fclose (outstate.ofp); outstate.ofp = 0; } fclose (rejfp); continue; } /* finish spewing out the new file */ if (!skip_rest_of_patch) { assert (hunk); if (! spew_output (&outstate)) { say ("Skipping patch.\n"); skip_rest_of_patch = TRUE; } } } /* and put the output where desired */ ignore_signals (); if (! skip_rest_of_patch && ! outfile) { if (outstate.zero_output && (remove_empty_files || (pch_says_nonexistent (reverse ^ 1) == 2 && ! posixly_correct))) { if (verbosity == VERBOSE) say ("Removing file `%s'%s.\n", outname, dry_run ? " and any empty ancestor directories" : ""); if (! dry_run) { move_file ((char *) 0, outname, (mode_t) 0, (make_backups || (backup_if_mismatch && (mismatch | failed)))); removedirs (outname); } } else { if (! outstate.zero_output && pch_says_nonexistent (reverse ^ 1)) { mismatch = 1; if (verbosity != SILENT) say ("File `%s' is not empty after patch, as expected.\n", outname); } if (! dry_run) { time_t t; move_file (TMPOUTNAME, outname, instat.st_mode, (make_backups || (backup_if_mismatch && (mismatch | failed)))); if ((set_time | set_utc) && (t = pch_timestamp (reverse ^ 1)) != (time_t) -1) { struct utimbuf utimbuf; utimbuf.actime = utimbuf.modtime = t; if (! force && ! inerrno && ! pch_says_nonexistent (reverse) && (t = pch_timestamp (reverse)) != (time_t) -1 && t != instat.st_mtime) say ("not setting time of file `%s' (time mismatch)\n", outname); else if (! force && (mismatch | failed)) say ("not setting time of file `%s' (contents mismatch)\n", outname); else if (utime (outname, &utimbuf) != 0) pfatal ("can't set timestamp on file `%s'", outname); } if (! inerrno && chmod (outname, instat.st_mode) != 0) pfatal ("can't set permissions on file `%s'", outname); } } } if (diff_type != ED_DIFF) { if (fclose (rejfp) != 0) write_fatal (); if (failed) { somefailed = TRUE; say ("%d out of %d hunk%s %s", failed, hunk, "s" + (hunk == 1), skip_rest_of_patch ? "ignored" : "FAILED"); if (outname) { char *rej = rejname; if (!rejname) { rej = xmalloc (strlen (outname) + 5); strcpy (rej, outname); addext (rej, ".rej", '#'); } say (" -- saving rejects to %s", rej); if (! dry_run) { move_file (TMPREJNAME, rej, instat.st_mode, FALSE); if (! inerrno && (chmod (rej, (instat.st_mode & ~(S_IXUSR|S_IXGRP|S_IXOTH))) != 0)) pfatal ("can't set permissions on file `%s'", rej); } if (!rejname) free (rej); } say ("\n"); } } set_signals (1); } if (outstate.ofp && (ferror (outstate.ofp) || fclose (outstate.ofp) != 0)) write_fatal (); cleanup (); if (somefailed) exit (1); return 0; } /* Prepare to find the next patch to do in the patch file. */ static void reinitialize_almost_everything() { re_patch(); re_input(); input_lines = 0; last_frozen_line = 0; if (inname) { free (inname); inname = 0; } last_offset = 0; diff_type = NO_DIFF; if (revision) { free(revision); revision = 0; } reverse = reverse_flag_specified; skip_rest_of_patch = FALSE; } static char const shortopts[] = "bB:cd:D:eEfF:g:i:lnNo:p:r:RstTuvV:x:Y:z:Z"; static struct option const longopts[] = { {"backup", no_argument, NULL, 'b'}, {"prefix", required_argument, NULL, 'B'}, {"context", no_argument, NULL, 'c'}, {"directory", required_argument, NULL, 'd'}, {"ifdef", required_argument, NULL, 'D'}, {"ed", no_argument, NULL, 'e'}, {"remove-empty-files", no_argument, NULL, 'E'}, {"force", no_argument, NULL, 'f'}, {"fuzz", required_argument, NULL, 'F'}, {"get", no_argument, NULL, 'g'}, {"input", required_argument, NULL, 'i'}, {"ignore-whitespace", no_argument, NULL, 'l'}, {"normal", no_argument, NULL, 'n'}, {"forward", no_argument, NULL, 'N'}, {"output", required_argument, NULL, 'o'}, {"strip", required_argument, NULL, 'p'}, {"reject-file", required_argument, NULL, 'r'}, {"reverse", no_argument, NULL, 'R'}, {"quiet", no_argument, NULL, 's'}, {"silent", no_argument, NULL, 's'}, {"batch", no_argument, NULL, 't'}, {"set-time", no_argument, NULL, 'T'}, {"unified", no_argument, NULL, 'u'}, {"version", no_argument, NULL, 'v'}, {"version-control", required_argument, NULL, 'V'}, {"debug", required_argument, NULL, 'x'}, {"basename-prefix", required_argument, NULL, 'Y'}, {"suffix", required_argument, NULL, 'z'}, {"set-utc", no_argument, NULL, 'Z'}, {"dry-run", no_argument, NULL, 129}, {"verbose", no_argument, NULL, 130}, {"binary", no_argument, NULL, 131}, {"help", no_argument, NULL, 132}, {"backup-if-mismatch", no_argument, NULL, 133}, {"no-backup-if-mismatch", no_argument, NULL, 134}, {NULL, no_argument, NULL, 0} }; static char const *const option_help[] = { "Input options:", "", " -p NUM --strip=NUM Strip NUM leading components from file names.", " -F LINES --fuzz LINES Set the fuzz factor to LINES for inexact matching.", " -l --ignore-whitespace Ignore white space changes between patch and input.", "", " -c --context Interpret the patch as a context difference.", " -e --ed Interpret the patch as an ed script.", " -n --normal Interpret the patch as a normal difference.", " -u --unified Interpret the patch as a unified difference.", "", " -N --forward Ignore patches that appear to be reversed or already applied.", " -R --reverse Assume patches were created with old and new files swapped.", "", " -i PATCHFILE --input=PATCHFILE Read patch from PATCHFILE instead of stdin.", "", "Output options:", "", " -o FILE --output=FILE Output patched files to FILE.", " -r FILE --reject-file=FILE Output rejects to FILE.", "", " -D NAME --ifdef=NAME Make merged if-then-else output using NAME.", " -E --remove-empty-files Remove output files that are empty after patching.", "", " -Z --set-utc Set times of patched files, assuming diff uses UTC (GMT).", " -T --set-time Likewise, assuming local time.", "", "Backup and version control options:", "", " -b --backup Back up the original contents of each file.", " --backup-if-mismatch Back up if the patch does not match exactly.", " --no-backup-if-mismatch Back up mismatches only if otherwise requested.", "", " -V STYLE --version-control=STYLE Use STYLE version control.", " STYLE is either 'simple', 'numbered', or 'existing'.", " -B PREFIX --prefix=PREFIX Prepend PREFIX to backup file names.", " -Y PREFIX --basename-prefix=PREFIX Prepend PREFIX to backup file basenames.", " -z SUFFIX --suffix=SUFFIX Append SUFFIX to backup file names.", "", " -g NUM --get=NUM Get files from RCS or SCCS if positive; ask if negative.", "", "Miscellaneous options:", "", " -t --batch Ask no questions; skip bad-Prereq patches; assume reversed.", " -f --force Like -t, but ignore bad-Prereq patches, and assume unreversed.", " -s --quiet --silent Work silently unless an error occurs.", " --verbose Output extra information about the work being done.", " --dry-run Do not actually change any files; just print what would happen.", "", " -d DIR --directory=DIR Change the working directory to DIR first.", #if HAVE_SETMODE " --binary Read and write data in binary mode.", #else " --binary Read and write data in binary mode (no effect on this platform).", #endif "", " -v --version Output version info.", " --help Output this help.", "", "Report bugs to .", 0 }; static void usage (stream, status) FILE *stream; int status; { char const * const *p; if (status != 0) { fprintf (stream, "%s: Try `%s --help' for more information.\n", program_name, Argv[0]); } else { fprintf (stream, "Usage: %s [OPTION]... [ORIGFILE [PATCHFILE]]\n\n", Argv[0]); for (p = option_help; *p; p++) fprintf (stream, "%s\n", *p); } exit (status); } /* Process switches and filenames. */ static void get_some_switches() { register int optc; if (rejname) free (rejname); rejname = 0; if (optind == Argc) return; while ((optc = getopt_long (Argc, Argv, shortopts, longopts, (int *) 0)) != -1) { switch (optc) { case 'b': make_backups = 1; /* Special hack for backward compatibility with CVS 1.9. If the last 4 args are `-b SUFFIX ORIGFILE PATCHFILE', treat `-b' as if it were `-b -z'. */ if (Argc - optind == 3 && strcmp (Argv[optind - 1], "-b") == 0 && ! (Argv[optind + 0][0] == '-' && Argv[optind + 0][1]) && ! (Argv[optind + 1][0] == '-' && Argv[optind + 1][1]) && ! (Argv[optind + 2][0] == '-' && Argv[optind + 2][1])) { optarg = Argv[optind++]; if (verbosity != SILENT) say ("warning: the `-b %s' option is obsolete; use `-b -z %s' instead\n", optarg, optarg); goto case_z; } break; case 'B': if (!*optarg) fatal ("backup prefix is empty"); origprae = savestr (optarg); break; case 'c': diff_type = CONTEXT_DIFF; break; case 'd': if (chdir(optarg) < 0) pfatal ("can't change directory to `%s'", optarg); break; case 'D': do_defines = savestr (optarg); break; case 'e': diff_type = ED_DIFF; break; case 'E': remove_empty_files = TRUE; break; case 'f': force = TRUE; break; case 'F': maxfuzz = numeric_string (optarg, 0, "fuzz factor"); break; case 'g': patch_get = numeric_string (optarg, 1, "get option value"); break; case 'i': patchname = savestr (optarg); break; case 'l': canonicalize = TRUE; break; case 'n': diff_type = NORMAL_DIFF; break; case 'N': noreverse = TRUE; break; case 'o': if (strcmp (optarg, "-") == 0) fatal ("can't output patches to standard output"); outfile = savestr (optarg); break; case 'p': strippath = numeric_string (optarg, 0, "strip count"); break; case 'r': rejname = savestr (optarg); break; case 'R': reverse = 1; reverse_flag_specified = 1; break; case 's': verbosity = SILENT; break; case 't': batch = TRUE; break; case 'T': set_time = 1; break; case 'u': diff_type = UNI_DIFF; break; case 'v': version(); exit (0); break; case 'V': version_control = optarg; break; #if DEBUGGING case 'x': debug = numeric_string (optarg, 1, "debugging option"); break; #endif case 'Y': if (!*optarg) fatal ("backup basename prefix is empty"); origbase = savestr (optarg); break; case 'z': case_z: if (!*optarg) fatal ("backup suffix is empty"); simple_backup_suffix = savestr (optarg); break; case 'Z': set_utc = 1; break; case 129: dry_run = TRUE; break; case 130: verbosity = VERBOSE; break; case 131: #if HAVE_SETMODE binary_transput = O_BINARY; #endif break; case 132: usage (stdout, 0); case 133: backup_if_mismatch = 1; break; case 134: backup_if_mismatch = 0; break; default: usage (stderr, 2); } } /* Process any filename args. */ if (optind < Argc) { inname = savestr (Argv[optind++]); invc = -1; if (optind < Argc) { patchname = savestr (Argv[optind++]); if (optind < Argc) { fprintf (stderr, "%s: extra operand `%s'\n", program_name, Argv[optind]); usage (stderr, 2); } } } } /* Handle STRING (possibly negative if NEGATIVE_ALLOWED is nonzero) of type ARGTYPE_MSGID by converting it to an integer, returning the result. */ static int numeric_string (string, negative_allowed, argtype_msgid) char const *string; int negative_allowed; char const *argtype_msgid; { int value = 0; char const *p = string; int sign = *p == '-' ? -1 : 1; p += *p == '-' || *p == '+'; do { int v10 = value * 10; int digit = *p - '0'; int signed_digit = sign * digit; int next_value = v10 + signed_digit; if (9 < (unsigned) digit) fatal ("%s `%s' is not a number", argtype_msgid, string); if (v10 / 10 != value || (next_value < v10) != (signed_digit < 0)) fatal ("%s `%s' is too large", argtype_msgid, string); value = next_value; } while (*++p); if (value < 0 && ! negative_allowed) fatal ("%s `%s' is negative", argtype_msgid, string); return value; } /* Attempt to find the right place to apply this hunk of patch. */ static LINENUM locate_hunk(fuzz) LINENUM fuzz; { register LINENUM first_guess = pch_first () + last_offset; register LINENUM offset; LINENUM pat_lines = pch_ptrn_lines(); LINENUM prefix_context = pch_prefix_context (); LINENUM suffix_context = pch_suffix_context (); LINENUM context = (prefix_context < suffix_context ? suffix_context : prefix_context); LINENUM prefix_fuzz = fuzz + prefix_context - context; LINENUM suffix_fuzz = fuzz + suffix_context - context; LINENUM max_where = input_lines - (pat_lines - suffix_fuzz) + 1; LINENUM min_where = last_frozen_line + 1 - (prefix_context - prefix_fuzz); LINENUM max_pos_offset = max_where - first_guess; LINENUM max_neg_offset = first_guess - min_where; LINENUM max_offset = (max_pos_offset < max_neg_offset ? max_neg_offset : max_pos_offset); if (!pat_lines) /* null range matches always */ return first_guess; /* Do not try lines <= 0. */ if (first_guess <= max_neg_offset) max_neg_offset = first_guess - 1; if (prefix_fuzz < 0) { /* Can only match start of file. */ if (suffix_fuzz < 0) /* Can only match entire file. */ if (pat_lines != input_lines || prefix_context < last_frozen_line) return 0; offset = 1 - first_guess; if (last_frozen_line <= prefix_context && offset <= max_pos_offset && patch_match (first_guess, offset, (LINENUM) 0, suffix_fuzz)) { last_offset = offset; return first_guess + offset; } else return 0; } if (suffix_fuzz < 0) { /* Can only match end of file. */ offset = first_guess - (input_lines - pat_lines + 1); if (offset <= max_neg_offset && patch_match (first_guess, -offset, prefix_fuzz, (LINENUM) 0)) { last_offset = - offset; return first_guess - offset; } else return 0; } for (offset = 0; offset <= max_offset; offset++) { if (offset <= max_pos_offset && patch_match (first_guess, offset, prefix_fuzz, suffix_fuzz)) { if (debug & 1) say ("Offset changing from %ld to %ld\n", last_offset, offset); last_offset = offset; return first_guess+offset; } if (0 < offset && offset <= max_neg_offset && patch_match (first_guess, -offset, prefix_fuzz, suffix_fuzz)) { if (debug & 1) say ("Offset changing from %ld to %ld\n", last_offset, -offset); last_offset = -offset; return first_guess-offset; } } return 0; } /* We did not find the pattern, dump out the hunk so they can handle it. */ static void abort_hunk() { register LINENUM i; register LINENUM pat_end = pch_end (); /* add in last_offset to guess the same as the previous successful hunk */ LINENUM oldfirst = pch_first() + last_offset; LINENUM newfirst = pch_newfirst() + last_offset; LINENUM oldlast = oldfirst + pch_ptrn_lines() - 1; LINENUM newlast = newfirst + pch_repl_lines() - 1; char const *stars = (int) NEW_CONTEXT_DIFF <= (int) diff_type ? " ****" : ""; char const *minuses = (int) NEW_CONTEXT_DIFF <= (int) diff_type ? " ----" : " -----"; fprintf(rejfp, "***************\n"); for (i=0; i<=pat_end; i++) { switch (pch_char(i)) { case '*': if (oldlast < oldfirst) fprintf(rejfp, "*** 0%s\n", stars); else if (oldlast == oldfirst) fprintf(rejfp, "*** %ld%s\n", oldfirst, stars); else fprintf(rejfp, "*** %ld,%ld%s\n", oldfirst, oldlast, stars); break; case '=': if (newlast < newfirst) fprintf(rejfp, "--- 0%s\n", minuses); else if (newlast == newfirst) fprintf(rejfp, "--- %ld%s\n", newfirst, minuses); else fprintf(rejfp, "--- %ld,%ld%s\n", newfirst, newlast, minuses); break; case ' ': case '-': case '+': case '!': fprintf (rejfp, "%c ", pch_char (i)); /* fall into */ case '\n': pch_write_line (i, rejfp); break; default: fatal ("fatal internal error in abort_hunk"); } if (ferror (rejfp)) write_fatal (); } } /* We found where to apply it (we hope), so do it. */ static bool apply_hunk (outstate, where) struct outstate *outstate; LINENUM where; { register LINENUM old = 1; register LINENUM lastline = pch_ptrn_lines (); register LINENUM new = lastline+1; register enum {OUTSIDE, IN_IFNDEF, IN_IFDEF, IN_ELSE} def_state = OUTSIDE; register char const *R_do_defines = do_defines; register LINENUM pat_end = pch_end (); register FILE *fp = outstate->ofp; where--; while (pch_char(new) == '=' || pch_char(new) == '\n') new++; while (old <= lastline) { if (pch_char(old) == '-') { assert (outstate->after_newline); if (! copy_till (outstate, where + old - 1)) return FALSE; if (R_do_defines) { if (def_state == OUTSIDE) { fprintf (fp, outstate->after_newline + if_defined, R_do_defines); def_state = IN_IFNDEF; } else if (def_state == IN_IFDEF) { fprintf (fp, outstate->after_newline + else_defined); def_state = IN_ELSE; } if (ferror (fp)) write_fatal (); outstate->after_newline = pch_write_line (old, fp); outstate->zero_output = 0; } last_frozen_line++; old++; } else if (new > pat_end) { break; } else if (pch_char(new) == '+') { if (! copy_till (outstate, where + old - 1)) return FALSE; if (R_do_defines) { if (def_state == IN_IFNDEF) { fprintf (fp, outstate->after_newline + else_defined); def_state = IN_ELSE; } else if (def_state == OUTSIDE) { fprintf (fp, outstate->after_newline + if_defined, R_do_defines); def_state = IN_IFDEF; } if (ferror (fp)) write_fatal (); } outstate->after_newline = pch_write_line (new, fp); outstate->zero_output = 0; new++; } else if (pch_char(new) != pch_char(old)) { if (debug & 1) say ("oldchar = '%c', newchar = '%c'\n", pch_char (old), pch_char (new)); fatal ("Out-of-sync patch, lines %ld,%ld -- mangled text or line numbers, maybe?", pch_hunk_beg() + old, pch_hunk_beg() + new); } else if (pch_char(new) == '!') { assert (outstate->after_newline); if (! copy_till (outstate, where + old - 1)) return FALSE; assert (outstate->after_newline); if (R_do_defines) { fprintf (fp, not_defined, R_do_defines); if (ferror (fp)) write_fatal (); def_state = IN_IFNDEF; } do { if (R_do_defines) { outstate->after_newline = pch_write_line (old, fp); } last_frozen_line++; old++; } while (pch_char (old) == '!'); if (R_do_defines) { fprintf (fp, outstate->after_newline + else_defined); if (ferror (fp)) write_fatal (); def_state = IN_ELSE; } do { outstate->after_newline = pch_write_line (new, fp); new++; } while (pch_char (new) == '!'); outstate->zero_output = 0; } else { assert(pch_char(new) == ' '); old++; new++; if (R_do_defines && def_state != OUTSIDE) { fprintf (fp, outstate->after_newline + end_defined, R_do_defines); if (ferror (fp)) write_fatal (); outstate->after_newline = 1; def_state = OUTSIDE; } } } if (new <= pat_end && pch_char(new) == '+') { if (! copy_till (outstate, where + old - 1)) return FALSE; if (R_do_defines) { if (def_state == OUTSIDE) { fprintf (fp, outstate->after_newline + if_defined, R_do_defines); def_state = IN_IFDEF; } else if (def_state == IN_IFNDEF) { fprintf (fp, outstate->after_newline + else_defined); def_state = IN_ELSE; } if (ferror (fp)) write_fatal (); outstate->zero_output = 0; } do { if (! outstate->after_newline && putc ('\n', fp) == EOF) write_fatal (); outstate->after_newline = pch_write_line (new, fp); outstate->zero_output = 0; new++; } while (new <= pat_end && pch_char (new) == '+'); } if (R_do_defines && def_state != OUTSIDE) { fprintf (fp, outstate->after_newline + end_defined, R_do_defines); if (ferror (fp)) write_fatal (); outstate->after_newline = 1; } return TRUE; } /* Create an output file. */ static FILE * create_output_file (name) char const *name; { int fd = create_file (name, O_WRONLY | binary_transput, instat.st_mode); FILE *f = fdopen (fd, binary_transput ? "wb" : "w"); if (! f) pfatal ("can't create `%s'", name); return f; } /* Open the new file. */ static void init_output (name, outstate) char const *name; struct outstate *outstate; { outstate->ofp = name ? create_output_file (name) : (FILE *) 0; outstate->after_newline = 1; outstate->zero_output = 1; } /* Open a file to put hunks we can't locate. */ static void init_reject(name) char const *name; { rejfp = create_output_file (name); } /* Copy input file to output, up to wherever hunk is to be applied. */ static bool copy_till (outstate, lastline) register struct outstate *outstate; register LINENUM lastline; { register LINENUM R_last_frozen_line = last_frozen_line; register FILE *fp = outstate->ofp; register char const *s; size_t size; if (R_last_frozen_line > lastline) { say ("misordered hunks! output would be garbled\n"); return FALSE; } while (R_last_frozen_line < lastline) { s = ifetch (++R_last_frozen_line, 0, &size); if (size) { if ((! outstate->after_newline && putc ('\n', fp) == EOF) || ! fwrite (s, sizeof *s, size, fp)) write_fatal (); outstate->after_newline = s[size - 1] == '\n'; outstate->zero_output = 0; } } last_frozen_line = R_last_frozen_line; return TRUE; } /* Finish copying the input file to the output file. */ static bool spew_output (outstate) struct outstate *outstate; { if (debug & 256) say ("il=%ld lfl=%ld\n", input_lines, last_frozen_line); if (last_frozen_line < input_lines) if (! copy_till (outstate, input_lines)) return FALSE; if (outstate->ofp && ! outfile) { if (fclose (outstate->ofp) != 0) write_fatal (); outstate->ofp = 0; } return TRUE; } /* Does the patch pattern match at line base+offset? */ static bool patch_match (base, offset, prefix_fuzz, suffix_fuzz) LINENUM base; LINENUM offset; LINENUM prefix_fuzz; LINENUM suffix_fuzz; { register LINENUM pline = 1 + prefix_fuzz; register LINENUM iline; register LINENUM pat_lines = pch_ptrn_lines () - suffix_fuzz; size_t size; register char const *p; for (iline=base+offset+prefix_fuzz; pline <= pat_lines; pline++,iline++) { p = ifetch (iline, offset >= 0, &size); if (canonicalize) { if (!similar(p, size, pfetch(pline), pch_line_len(pline) )) return FALSE; } else if (size != pch_line_len (pline) || memcmp (p, pfetch (pline), size) != 0) return FALSE; } return TRUE; } /* Do two lines match with canonicalized white space? */ static bool similar (a, alen, b, blen) register char const *a; register size_t alen; register char const *b; register size_t blen; { /* Ignore presence or absence of trailing newlines. */ alen -= alen && a[alen - 1] == '\n'; blen -= blen && b[blen - 1] == '\n'; for (;;) { if (!blen || (*b == ' ' || *b == '\t')) { while (blen && (*b == ' ' || *b == '\t')) b++, blen--; if (alen) { if (!(*a == ' ' || *a == '\t')) return FALSE; do a++, alen--; while (alen && (*a == ' ' || *a == '\t')); } if (!alen || !blen) return alen == blen; } else if (!alen || *a++ != *b++) return FALSE; else alen--, blen--; } } /* Make a temporary file. */ #if HAVE_MKTEMP char *mktemp PARAMS ((char *)); #endif #ifndef TMPDIR #define TMPDIR "/tmp" #endif static char const * make_temp (letter) int letter; { char *r; #if HAVE_MKTEMP char const *tmpdir = getenv ("TMPDIR"); /* Unix tradition */ if (!tmpdir) tmpdir = getenv ("TMP"); /* DOS tradition */ if (!tmpdir) tmpdir = getenv ("TEMP"); /* another DOS tradition */ if (!tmpdir) tmpdir = TMPDIR; r = xmalloc (strlen (tmpdir) + 10); sprintf (r, "%s/p%cXXXXXX", tmpdir, letter); mktemp (r); if (!*r) pfatal ("mktemp"); #else r = xmalloc (L_tmpnam); if (! (tmpnam (r) == r && *r)) pfatal ("tmpnam"); #endif return r; } /* Fatal exit with cleanup. */ void fatal_exit (sig) int sig; { cleanup (); if (sig) exit_with_signal (sig); exit (2); } static void cleanup () { unlink (TMPINNAME); unlink (TMPOUTNAME); unlink (TMPPATNAME); unlink (TMPREJNAME); } /sys/src/ape/cmd/patch/patchlevel.h 664 sys sys 1367613436 28 #define PATCH_VERSION "2.1" /sys/src/ape/cmd/patch/pch.c 664 sys sys 1367613436 47529 /* reading patches */ /* $Id: pch.c,v 1.26 1997/07/21 17:59:46 eggert Exp $ */ /* Copyright 1986, 1987, 1988 Larry Wall Copyright 1990, 1991, 1992, 1993, 1997 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; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #define XTERN extern #include #include #include #include #undef XTERN #define XTERN #include #define INITHUNKMAX 125 /* initial dynamic allocation size */ /* Patch (diff listing) abstract type. */ static FILE *pfp; /* patch file pointer */ static int p_says_nonexistent[2]; /* [0] for old file, [1] for new; value is 0 for nonempty, 1 for empty, 2 for nonexistent */ static int p_rfc934_nesting; /* RFC 934 nesting level */ static time_t p_timestamp[2]; /* timestamps in patch headers */ static off_t p_filesize; /* size of the patch file */ static LINENUM p_first; /* 1st line number */ static LINENUM p_newfirst; /* 1st line number of replacement */ static LINENUM p_ptrn_lines; /* # lines in pattern */ static LINENUM p_repl_lines; /* # lines in replacement text */ static LINENUM p_end = -1; /* last line in hunk */ static LINENUM p_max; /* max allowed value of p_end */ static LINENUM p_prefix_context; /* # of prefix context lines */ static LINENUM p_suffix_context; /* # of suffix context lines */ static LINENUM p_input_line; /* current line # from patch file */ static char **p_line; /* the text of the hunk */ static size_t *p_len; /* line length including \n if any */ static char *p_Char; /* +, -, and ! */ static LINENUM hunkmax = INITHUNKMAX; /* size of above arrays */ static int p_indent; /* indent to patch */ static file_offset p_base; /* where to intuit this time */ static LINENUM p_bline; /* line # of p_base */ static file_offset p_start; /* where intuit found a patch */ static LINENUM p_sline; /* and the line number for it */ static LINENUM p_hunk_beg; /* line number of current hunk */ static LINENUM p_efake = -1; /* end of faked up lines--don't free */ static LINENUM p_bfake = -1; /* beg of faked up lines */ enum nametype { OLD, NEW, INDEX, NONE }; static enum diff intuit_diff_type PARAMS ((void)); static enum nametype best_name PARAMS ((char * const *, int const *)); static int prefix_components PARAMS ((char *, int)); static size_t pget_line PARAMS ((int, int)); static size_t get_line PARAMS ((void)); static bool incomplete_line PARAMS ((void)); static bool grow_hunkmax PARAMS ((void)); static void malformed PARAMS ((void)) __attribute__ ((noreturn)); static void next_intuit_at PARAMS ((file_offset, LINENUM)); static void skip_to PARAMS ((file_offset, LINENUM)); /* Prepare to look for the next patch in the patch file. */ void re_patch() { p_first = 0; p_newfirst = 0; p_ptrn_lines = 0; p_repl_lines = 0; p_end = -1; p_max = 0; p_indent = 0; } /* Open the patch file at the beginning of time. */ void open_patch_file(filename) char const *filename; { file_offset file_pos = 0; struct stat st; if (!filename || !*filename || strEQ (filename, "-")) { file_offset stdin_pos; #if HAVE_SETMODE if (binary_transput) { if (isatty (STDIN_FILENO)) fatal ("cannot read binary data from tty on this platform"); setmode (STDIN_FILENO, O_BINARY); } #endif if (fstat (STDIN_FILENO, &st) != 0) pfatal ("fstat"); if (S_ISREG (st.st_mode) && (stdin_pos = file_tell (stdin)) != -1) { pfp = stdin; file_pos = stdin_pos; } else { size_t charsread; pfp = fopen (TMPPATNAME, "w+b"); if (!pfp) pfatal ("can't create `%s'", TMPPATNAME); for (st.st_size = 0; (charsread = fread (buf, 1, bufsize, stdin)) != 0; st.st_size += charsread) if (fwrite (buf, 1, charsread, pfp) != charsread) write_fatal (); if (ferror (stdin) || fclose (stdin) != 0) read_fatal (); if (fflush (pfp) != 0 || file_seek (pfp, (file_offset) 0, SEEK_SET) != 0) write_fatal (); } } else { pfp = fopen (filename, binary_transput ? "rb" : "r"); if (!pfp) pfatal ("can't open patch file `%s'", filename); if (fstat (fileno (pfp), &st) != 0) pfatal ("fstat"); } p_filesize = st.st_size; if (p_filesize != (file_offset) p_filesize) fatal ("patch file is too long"); next_intuit_at (file_pos, (LINENUM) 1); set_hunkmax(); } /* Make sure our dynamically realloced tables are malloced to begin with. */ void set_hunkmax() { if (!p_line) p_line = (char **) malloc (hunkmax * sizeof *p_line); if (!p_len) p_len = (size_t *) malloc (hunkmax * sizeof *p_len); if (!p_Char) p_Char = malloc (hunkmax * sizeof *p_Char); } /* Enlarge the arrays containing the current hunk of patch. */ static bool grow_hunkmax() { hunkmax *= 2; assert (p_line && p_len && p_Char); if ((p_line = (char **) realloc (p_line, hunkmax * sizeof (*p_line))) && (p_len = (size_t *) realloc (p_len, hunkmax * sizeof (*p_len))) && (p_Char = realloc (p_Char, hunkmax * sizeof (*p_Char)))) return TRUE; if (!using_plan_a) memory_fatal (); /* Don't free previous values of p_line etc., since some broken implementations free them for us. Whatever is null will be allocated again from within plan_a (), of all places. */ return FALSE; } /* True if the remainder of the patch file contains a diff of some sort. */ bool there_is_another_patch() { if (p_base != 0 && p_base >= p_filesize) { if (verbosity == VERBOSE) say ("done\n"); return FALSE; } if (verbosity == VERBOSE) say ("Hmm..."); diff_type = intuit_diff_type(); if (diff_type == NO_DIFF) { if (verbosity == VERBOSE) say (p_base ? " Ignoring the trailing garbage.\ndone\n" : " I can't seem to find a patch in there anywhere.\n"); if (! p_base && p_filesize) fatal ("Only garbage was found in the patch input."); return FALSE; } if (skip_rest_of_patch) { Fseek (pfp, p_start, SEEK_SET); p_input_line = p_sline - 1; return TRUE; } if (verbosity == VERBOSE) say (" %sooks like %s to me...\n", (p_base == 0 ? "L" : "The next patch l"), diff_type == UNI_DIFF ? "a unified diff" : diff_type == CONTEXT_DIFF ? "a context diff" : diff_type == NEW_CONTEXT_DIFF ? "a new-style context diff" : diff_type == NORMAL_DIFF ? "a normal diff" : "an ed script" ); if (verbosity != SILENT) { if (p_indent) say ("(Patch is indented %d space%s.)\n", p_indent, p_indent==1?"":"s"); if (! inname) { say ("can't find file to patch at input line %ld\n", p_sline); say (strippath == INT_MAX ? "Perhaps you should have used the -p or --strip option?\n" : "Perhaps you used the wrong -p or --strip option?\n"); } } skip_to(p_start,p_sline); while (!inname) { if (force || batch) { say ("No file to patch. Skipping patch.\n"); skip_rest_of_patch = TRUE; return TRUE; } ask ("File to patch: "); inname = fetchname (buf, 0, (time_t *) 0); if (inname) { if (stat (inname, &instat) == 0) { inerrno = 0; invc = -1; } else { perror (inname); free (inname); inname = 0; } } if (!inname) { ask ("Skip this patch? [y] "); if (*buf != 'n') { if (verbosity != SILENT) say ("Skipping patch.\n"); skip_rest_of_patch = TRUE; return TRUE; } } } return TRUE; } /* Determine what kind of diff is in the remaining part of the patch file. */ static enum diff intuit_diff_type() { register char *s; register char *t; register int indent; register file_offset this_line = 0; register file_offset previous_line; register file_offset first_command_line = -1; LINENUM fcl_line = 0; /* Pacify `gcc -W'. */ register bool last_line_was_command = FALSE; register bool this_is_a_command = FALSE; register bool stars_last_line = FALSE; register bool stars_this_line = FALSE; enum nametype i; char *name[3]; struct stat st[3]; int stat_errno[3]; int version_controlled[3]; register enum diff retval; name[OLD] = name[NEW] = name[INDEX] = 0; version_controlled[OLD] = -1; version_controlled[NEW] = -1; version_controlled[INDEX] = -1; p_rfc934_nesting = 0; p_timestamp[OLD] = p_timestamp[NEW] = (time_t) -1; p_says_nonexistent[OLD] = p_says_nonexistent[NEW] = 0; Fseek (pfp, p_base, SEEK_SET); p_input_line = p_bline - 1; for (;;) { previous_line = this_line; last_line_was_command = this_is_a_command; stars_last_line = stars_this_line; this_line = file_tell (pfp); indent = 0; if (! pget_line (0, 0)) { if (first_command_line >= 0) { /* nothing but deletes!? */ p_start = first_command_line; p_sline = fcl_line; retval = ED_DIFF; goto scan_exit; } else { p_start = this_line; p_sline = p_input_line; return NO_DIFF; } } for (s = buf; *s == ' ' || *s == '\t' || *s == 'X'; s++) { if (*s == '\t') indent = (indent + 8) & ~7; else indent++; } for (t = s; ISDIGIT (*t) || *t == ','; t++) continue; this_is_a_command = (ISDIGIT (*s) && (*t == 'd' || *t == 'c' || *t == 'a') ); if (first_command_line < 0 && this_is_a_command) { first_command_line = this_line; fcl_line = p_input_line; p_indent = indent; /* assume this for now */ } if (!stars_last_line && strnEQ(s, "*** ", 4)) name[OLD] = fetchname (s+4, strippath, &p_timestamp[OLD]); else if (strnEQ(s, "+++ ", 4)) /* Swap with NEW below. */ name[OLD] = fetchname (s+4, strippath, &p_timestamp[OLD]); else if (strnEQ(s, "Index:", 6)) name[INDEX] = fetchname (s+6, strippath, (time_t *) 0); else if (strnEQ(s, "Prereq:", 7)) { for (t = s + 7; ISSPACE ((unsigned char) *t); t++) continue; revision = t; for (t = revision; *t && !ISSPACE ((unsigned char) *t); t++) continue; if (t == revision) revision = 0; else { char oldc = *t; *t = '\0'; revision = savestr (revision); *t = oldc; } } else { for (t = s; t[0] == '-' && t[1] == ' '; t += 2) continue; if (strnEQ(t, "--- ", 4)) { time_t timestamp = (time_t) -1; name[NEW] = fetchname (t+4, strippath, ×tamp); if (timestamp != (time_t) -1) { p_timestamp[NEW] = timestamp; p_rfc934_nesting = (t - s) >> 1; } } } if ((diff_type == NO_DIFF || diff_type == ED_DIFF) && first_command_line >= 0 && strEQ(s, ".\n") ) { p_indent = indent; p_start = first_command_line; p_sline = fcl_line; retval = ED_DIFF; goto scan_exit; } if ((diff_type == NO_DIFF || diff_type == UNI_DIFF) && strnEQ(s, "@@ -", 4)) { /* `name' and `p_timestamp' are backwards; swap them. */ time_t ti = p_timestamp[OLD]; p_timestamp[OLD] = p_timestamp[NEW]; p_timestamp[NEW] = ti; t = name[OLD]; name[OLD] = name[NEW]; name[NEW] = t; s += 4; if (! atol (s)) p_says_nonexistent[OLD] = 1 + ! p_timestamp[OLD]; while (*s != ' ' && *s != '\n') s++; while (*s == ' ') s++; if (! atol (s)) p_says_nonexistent[NEW] = 1 + ! p_timestamp[NEW]; p_indent = indent; p_start = this_line; p_sline = p_input_line; retval = UNI_DIFF; if (! ((name[OLD] || ! p_timestamp[OLD]) && (name[NEW] || ! p_timestamp[NEW]))) say ("missing header for unified diff at line %ld of patch\n", p_sline); goto scan_exit; } stars_this_line = strnEQ(s, "********", 8); if ((diff_type == NO_DIFF || diff_type == CONTEXT_DIFF || diff_type == NEW_CONTEXT_DIFF) && stars_last_line && strnEQ (s, "*** ", 4)) { s += 4; if (! atol (s)) p_says_nonexistent[OLD] = 1 + ! p_timestamp[OLD]; /* if this is a new context diff the character just before */ /* the newline is a '*'. */ while (*s != '\n') s++; p_indent = indent; p_start = previous_line; p_sline = p_input_line - 1; retval = (*(s-1) == '*' ? NEW_CONTEXT_DIFF : CONTEXT_DIFF); { /* Scan the first hunk to see whether the file contents appear to have been deleted. */ file_offset saved_p_base = p_base; LINENUM saved_p_bline = p_bline; Fseek (pfp, previous_line, SEEK_SET); p_input_line -= 2; if (another_hunk (retval, 0) && ! p_repl_lines && p_newfirst == 1) p_says_nonexistent[NEW] = 1 + ! p_timestamp[NEW]; next_intuit_at (saved_p_base, saved_p_bline); } if (! ((name[OLD] || ! p_timestamp[OLD]) && (name[NEW] || ! p_timestamp[NEW]))) say ("missing header for context diff at line %ld of patch\n", p_sline); goto scan_exit; } if ((diff_type == NO_DIFF || diff_type == NORMAL_DIFF) && last_line_was_command && (strnEQ(s, "< ", 2) || strnEQ(s, "> ", 2)) ) { p_start = previous_line; p_sline = p_input_line - 1; p_indent = indent; retval = NORMAL_DIFF; goto scan_exit; } } scan_exit: /* To intuit `inname', the name of the file to patch, use the algorithm specified by POSIX 1003.2b/D11 section 5.22.7.2 (with some modifications if posixly_correct is zero): - Take the old and new names from the context header if present, and take the index name from the `Index:' line if present and if either the old and new names are both absent or posixly_correct is nonzero. Consider the file names to be in the order (old, new, index). - If some named files exist, use the first one if posixly_correct is nonzero, the best one otherwise. - If patch_get is nonzero, and no named files exist, but an RCS or SCCS master file exists, use the first named file with an RCS or SCCS master. - If no named files exist, no RCS or SCCS master was found, some names are given, posixly_correct is zero, and the patch appears to create a file, then use the best name requiring the creation of the fewest directories. - Otherwise, report failure by setting `inname' to 0; this causes our invoker to ask the user for a file name. */ i = NONE; if (!inname) { enum nametype i0 = NONE; if (! posixly_correct && (name[OLD] || name[NEW]) && name[INDEX]) { free (name[INDEX]); name[INDEX] = 0; } for (i = OLD; i <= INDEX; i++) if (name[i]) { if (i0 != NONE && strcmp (name[i0], name[i]) == 0) { /* It's the same name as before; reuse stat results. */ stat_errno[i] = stat_errno[i0]; if (! stat_errno[i]) st[i] = st[i0]; } else if (stat (name[i], &st[i]) != 0) stat_errno[i] = errno; else { stat_errno[i] = 0; if (posixly_correct) break; } i0 = i; } if (! posixly_correct) { i = best_name (name, stat_errno); if (i == NONE && patch_get) { enum nametype nope = NONE; for (i = OLD; i <= INDEX; i++) if (name[i]) { char const *cs; char *getbuf; char *diffbuf; int readonly = outfile && strcmp (outfile, name[i]) != 0; if (nope == NONE || strcmp (name[nope], name[i]) != 0) { cs = (version_controller (name[i], readonly, (struct stat *) 0, &getbuf, &diffbuf)); version_controlled[i] = !! cs; if (cs) { if (version_get (name[i], cs, 0, readonly, getbuf, &st[i])) stat_errno[i] = 0; else version_controlled[i] = 0; free (getbuf); free (diffbuf); if (! stat_errno[i]) break; } } nope = i; } } if (p_says_nonexistent[reverse ^ (i == NONE || st[i].st_size == 0)]) { assert (i0 != NONE); if (ok_to_reverse ("The next patch%s would %s the file `%s',\nwhich %s!", reverse ? ", when reversed," : "", (i == NONE ? "delete" : st[i].st_size == 0 ? "empty out" : "create"), name[i == NONE || st[i].st_size == 0 ? i0 : i], (i == NONE ? "does not exist" : st[i].st_size == 0 ? "is already empty" : "already exists"))) reverse ^= 1; } if (i == NONE && p_says_nonexistent[reverse]) { int newdirs[3]; int newdirs_min = INT_MAX; int distance_from_minimum[3]; for (i = OLD; i <= INDEX; i++) if (name[i]) { newdirs[i] = (prefix_components (name[i], 0) - prefix_components (name[i], 1)); if (newdirs[i] < newdirs_min) newdirs_min = newdirs[i]; } for (i = OLD; i <= INDEX; i++) if (name[i]) distance_from_minimum[i] = newdirs[i] - newdirs_min; i = best_name (name, distance_from_minimum); } } } if (i == NONE) inerrno = -1; else { inname = name[i]; name[i] = 0; inerrno = stat_errno[i]; invc = version_controlled[i]; instat = st[i]; } for (i = OLD; i <= INDEX; i++) if (name[i]) free (name[i]); return retval; } /* Count the path name components in FILENAME's prefix. If CHECKDIRS is nonzero, count only existing directories. */ static int prefix_components (filename, checkdirs) char *filename; int checkdirs; { int count = 0; struct stat stat_buf; int stat_result; char *f = filename + FILESYSTEM_PREFIX_LEN (filename); if (*f) while (*++f) if (ISSLASH (f[0]) && ! ISSLASH (f[-1])) { if (checkdirs) { *f = '\0'; stat_result = stat (filename, &stat_buf); *f = '/'; if (! (stat_result == 0 && S_ISDIR (stat_buf.st_mode))) break; } count++; } return count; } /* Return the index of the best of NAME[OLD], NAME[NEW], and NAME[INDEX]. Ignore null names, and ignore NAME[i] if IGNORE[i] is nonzero. Return NONE if all names are ignored. */ static enum nametype best_name (name, ignore) char *const *name; int const *ignore; { enum nametype i; int components[3]; int components_min = INT_MAX; size_t basename_len[3]; size_t basename_len_min = (size_t) -1; size_t len[3]; size_t len_min = (size_t) -1; for (i = OLD; i <= INDEX; i++) if (name[i] && !ignore[i]) { /* Take the names with the fewest prefix components. */ components[i] = prefix_components (name[i], 0); if (components_min < components[i]) continue; components_min = components[i]; /* Of those, take the names with the shortest basename. */ basename_len[i] = strlen (base_name (name[i])); if (basename_len_min < basename_len[i]) continue; basename_len_min = basename_len[i]; /* Of those, take the shortest names. */ len[i] = strlen (name[i]); if (len_min < len[i]) continue; len_min = len[i]; } /* Of those, take the first name. */ for (i = OLD; i <= INDEX; i++) if (name[i] && !ignore[i] && components[i] == components_min && basename_len[i] == basename_len_min && len[i] == len_min) break; return i; } /* Remember where this patch ends so we know where to start up again. */ static void next_intuit_at(file_pos,file_line) file_offset file_pos; LINENUM file_line; { p_base = file_pos; p_bline = file_line; } /* Basically a verbose fseek() to the actual diff listing. */ static void skip_to(file_pos,file_line) file_offset file_pos; LINENUM file_line; { register FILE *i = pfp; register FILE *o = stdout; register int c; assert(p_base <= file_pos); if ((verbosity == VERBOSE || !inname) && p_base < file_pos) { Fseek (i, p_base, SEEK_SET); say ("The text leading up to this was:\n--------------------------\n"); while (file_tell (i) < file_pos) { putc ('|', o); do { if ((c = getc (i)) == EOF) read_fatal (); putc (c, o); } while (c != '\n'); } say ("--------------------------\n"); } else Fseek (i, file_pos, SEEK_SET); p_input_line = file_line - 1; } /* Make this a function for better debugging. */ static void malformed () { fatal ("malformed patch at line %ld: %s", p_input_line, buf); /* about as informative as "Syntax error" in C */ } /* 1 if there is more of the current diff listing to process; 0 if not; -1 if ran out of memory. */ int another_hunk (difftype, rev) enum diff difftype; int rev; { register char *s; register LINENUM context = 0; register size_t chars_read; while (p_end >= 0) { if (p_end == p_efake) p_end = p_bfake; /* don't free twice */ else free(p_line[p_end]); p_end--; } assert(p_end == -1); p_efake = -1; p_max = hunkmax; /* gets reduced when --- found */ if (difftype == CONTEXT_DIFF || difftype == NEW_CONTEXT_DIFF) { file_offset line_beginning = file_tell (pfp); /* file pos of the current line */ LINENUM repl_beginning = 0; /* index of --- line */ register LINENUM fillcnt = 0; /* #lines of missing ptrn or repl */ register LINENUM fillsrc; /* index of first line to copy */ register LINENUM filldst; /* index of first missing line */ bool ptrn_spaces_eaten = FALSE; /* ptrn was slightly misformed */ bool some_context = FALSE; /* (perhaps internal) context seen */ register bool repl_could_be_missing = TRUE; bool repl_missing = FALSE; /* we are now backtracking */ file_offset repl_backtrack_position = 0; /* file pos of first repl line */ LINENUM repl_patch_line; /* input line number for same */ LINENUM repl_context; /* context for same */ LINENUM ptrn_prefix_context = -1; /* lines in pattern prefix context */ LINENUM ptrn_suffix_context = -1; /* lines in pattern suffix context */ LINENUM repl_prefix_context = -1; /* lines in replac. prefix context */ register LINENUM ptrn_copiable = 0; /* # of copiable lines in ptrn */ /* Pacify `gcc -Wall'. */ fillsrc = filldst = repl_patch_line = repl_context = 0; chars_read = get_line (); if (chars_read == (size_t) -1 || chars_read <= 8 || strncmp (buf, "********", 8) != 0) { next_intuit_at(line_beginning,p_input_line); return chars_read == (size_t) -1 ? -1 : 0; } p_hunk_beg = p_input_line + 1; while (p_end < p_max) { chars_read = get_line (); if (chars_read == (size_t) -1) return -1; if (!chars_read) { if (repl_beginning && repl_could_be_missing) { repl_missing = TRUE; goto hunk_done; } if (p_max - p_end < 4) { strcpy (buf, " \n"); /* assume blank lines got chopped */ chars_read = 3; } else { fatal ("unexpected end of file in patch"); } } p_end++; if (p_end == hunkmax) fatal ("unterminated hunk starting at line %ld; giving up at line %ld: %s", pch_hunk_beg (), p_input_line, buf); assert(p_end < hunkmax); p_Char[p_end] = *buf; p_len[p_end] = 0; p_line[p_end] = 0; switch (*buf) { case '*': if (strnEQ(buf, "********", 8)) { if (repl_beginning && repl_could_be_missing) { repl_missing = TRUE; goto hunk_done; } else fatal ("unexpected end of hunk at line %ld", p_input_line); } if (p_end != 0) { if (repl_beginning && repl_could_be_missing) { repl_missing = TRUE; goto hunk_done; } fatal ("unexpected `***' at line %ld: %s", p_input_line, buf); } context = 0; p_len[p_end] = strlen (buf); if (! (p_line[p_end] = savestr (buf))) { p_end--; return -1; } for (s = buf; *s && !ISDIGIT (*s); s++) continue; if (!*s) malformed (); if (strnEQ(s,"0,0",3)) remove_prefix (s, 2); p_first = (LINENUM) atol(s); while (ISDIGIT (*s)) s++; if (*s == ',') { while (*s && !ISDIGIT (*s)) s++; if (!*s) malformed (); p_ptrn_lines = ((LINENUM)atol(s)) - p_first + 1; } else if (p_first) p_ptrn_lines = 1; else { p_ptrn_lines = 0; p_first = 1; } p_max = p_ptrn_lines + 6; /* we need this much at least */ while (p_max >= hunkmax) if (! grow_hunkmax ()) return -1; p_max = hunkmax; break; case '-': if (buf[1] != '-') goto change_line; if (ptrn_prefix_context == -1) ptrn_prefix_context = context; ptrn_suffix_context = context; if (repl_beginning || (p_end != p_ptrn_lines + 1 + (p_Char[p_end - 1] == '\n'))) { if (p_end == 1) { /* `Old' lines were omitted. Set up to fill them in from `new' context lines. */ p_end = p_ptrn_lines + 1; ptrn_prefix_context = ptrn_suffix_context = -1; fillsrc = p_end + 1; filldst = 1; fillcnt = p_ptrn_lines; } else if (! repl_beginning) fatal ("%s `---' at line %ld; check line numbers at line %ld", (p_end <= p_ptrn_lines ? "Premature" : "Overdue"), p_input_line, p_hunk_beg); else if (! repl_could_be_missing) fatal ("duplicate `---' at line %ld; check line numbers at line %ld", p_input_line, p_hunk_beg + repl_beginning); else { repl_missing = TRUE; goto hunk_done; } } repl_beginning = p_end; repl_backtrack_position = file_tell (pfp); repl_patch_line = p_input_line; repl_context = context; p_len[p_end] = strlen (buf); if (! (p_line[p_end] = savestr (buf))) { p_end--; return -1; } p_Char[p_end] = '='; for (s = buf; *s && ! ISDIGIT (*s); s++) continue; if (!*s) malformed (); p_newfirst = (LINENUM) atol (s); while (ISDIGIT (*s)) s++; if (*s == ',') { do { if (!*++s) malformed (); } while (! ISDIGIT (*s)); p_repl_lines = (LINENUM) atol (s) - p_newfirst + 1; } else if (p_newfirst) p_repl_lines = 1; else { p_repl_lines = 0; p_newfirst = 1; } p_max = p_repl_lines + p_end; while (p_max >= hunkmax) if (! grow_hunkmax ()) return -1; if (p_repl_lines != ptrn_copiable && (p_prefix_context != 0 || context != 0 || p_repl_lines != 1)) repl_could_be_missing = FALSE; context = 0; break; case '+': case '!': repl_could_be_missing = FALSE; change_line: s = buf + 1; chars_read--; if (*s == '\n' && canonicalize) { strcpy (s, " \n"); chars_read = 2; } if (*s == ' ' || *s == '\t') { s++; chars_read--; } else if (repl_beginning && repl_could_be_missing) { repl_missing = TRUE; goto hunk_done; } if (! repl_beginning) { if (ptrn_prefix_context == -1) ptrn_prefix_context = context; } else { if (repl_prefix_context == -1) repl_prefix_context = context; } chars_read -= (1 < chars_read && p_end == (repl_beginning ? p_max : p_ptrn_lines) && incomplete_line ()); p_len[p_end] = chars_read; if (! (p_line[p_end] = savebuf (s, chars_read))) { p_end--; return -1; } context = 0; break; case '\t': case '\n': /* assume spaces got eaten */ s = buf; if (*buf == '\t') { s++; chars_read--; } if (repl_beginning && repl_could_be_missing && (!ptrn_spaces_eaten || difftype == NEW_CONTEXT_DIFF) ) { repl_missing = TRUE; goto hunk_done; } chars_read -= (1 < chars_read && p_end == (repl_beginning ? p_max : p_ptrn_lines) && incomplete_line ()); p_len[p_end] = chars_read; if (! (p_line[p_end] = savebuf (buf, chars_read))) { p_end--; return -1; } if (p_end != p_ptrn_lines + 1) { ptrn_spaces_eaten |= (repl_beginning != 0); some_context = TRUE; context++; if (!repl_beginning) ptrn_copiable++; p_Char[p_end] = ' '; } break; case ' ': s = buf + 1; chars_read--; if (*s == '\n' && canonicalize) { strcpy (s, "\n"); chars_read = 2; } if (*s == ' ' || *s == '\t') { s++; chars_read--; } else if (repl_beginning && repl_could_be_missing) { repl_missing = TRUE; goto hunk_done; } some_context = TRUE; context++; if (!repl_beginning) ptrn_copiable++; chars_read -= (1 < chars_read && p_end == (repl_beginning ? p_max : p_ptrn_lines) && incomplete_line ()); p_len[p_end] = chars_read; if (! (p_line[p_end] = savebuf (buf + 2, chars_read))) { p_end--; return -1; } break; default: if (repl_beginning && repl_could_be_missing) { repl_missing = TRUE; goto hunk_done; } malformed (); } } hunk_done: if (p_end >=0 && !repl_beginning) fatal ("no `---' found in patch at line %ld", pch_hunk_beg ()); if (repl_missing) { /* reset state back to just after --- */ p_input_line = repl_patch_line; context = repl_context; for (p_end--; p_end > repl_beginning; p_end--) free(p_line[p_end]); Fseek (pfp, repl_backtrack_position, SEEK_SET); /* redundant 'new' context lines were omitted - set */ /* up to fill them in from the old file context */ fillsrc = 1; filldst = repl_beginning+1; fillcnt = p_repl_lines; p_end = p_max; } else if (!some_context && fillcnt == 1) { /* the first hunk was a null hunk with no context */ /* and we were expecting one line -- fix it up. */ while (filldst < p_end) { p_line[filldst] = p_line[filldst+1]; p_Char[filldst] = p_Char[filldst+1]; p_len[filldst] = p_len[filldst+1]; filldst++; } #if 0 repl_beginning--; /* this doesn't need to be fixed */ #endif p_end--; p_first++; /* do append rather than insert */ fillcnt = 0; p_ptrn_lines = 0; } p_prefix_context = ((repl_prefix_context == -1 || (ptrn_prefix_context != -1 && ptrn_prefix_context < repl_prefix_context)) ? ptrn_prefix_context : repl_prefix_context); p_suffix_context = ((ptrn_suffix_context != -1 && ptrn_suffix_context < context) ? ptrn_suffix_context : context); assert (p_prefix_context != -1 && p_suffix_context != -1); if (difftype == CONTEXT_DIFF && (fillcnt || (p_first > 1 && p_prefix_context + p_suffix_context < ptrn_copiable))) { if (verbosity == VERBOSE) say ("%s\n%s\n%s\n", "(Fascinating -- this is really a new-style context diff but without", "the telltale extra asterisks on the *** line that usually indicate", "the new style...)"); diff_type = difftype = NEW_CONTEXT_DIFF; } /* if there were omitted context lines, fill them in now */ if (fillcnt) { p_bfake = filldst; /* remember where not to free() */ p_efake = filldst + fillcnt - 1; while (fillcnt-- > 0) { while (fillsrc <= p_end && fillsrc != repl_beginning && p_Char[fillsrc] != ' ') fillsrc++; if (p_end < fillsrc || fillsrc == repl_beginning) fatal ("replacement text or line numbers mangled in hunk at line %ld", p_hunk_beg); p_line[filldst] = p_line[fillsrc]; p_Char[filldst] = p_Char[fillsrc]; p_len[filldst] = p_len[fillsrc]; fillsrc++; filldst++; } while (fillsrc <= p_end && fillsrc != repl_beginning) { if (p_Char[fillsrc] == ' ') fatal ("replacement text or line numbers mangled in hunk at line %ld", p_hunk_beg); fillsrc++; } if (debug & 64) printf("fillsrc %ld, filldst %ld, rb %ld, e+1 %ld\n", fillsrc,filldst,repl_beginning,p_end+1); assert(fillsrc==p_end+1 || fillsrc==repl_beginning); assert(filldst==p_end+1 || filldst==repl_beginning); } } else if (difftype == UNI_DIFF) { file_offset line_beginning = file_tell (pfp); /* file pos of the current line */ register LINENUM fillsrc; /* index of old lines */ register LINENUM filldst; /* index of new lines */ char ch = '\0'; chars_read = get_line (); if (chars_read == (size_t) -1 || chars_read <= 4 || strncmp (buf, "@@ -", 4) != 0) { next_intuit_at(line_beginning,p_input_line); return chars_read == (size_t) -1 ? -1 : 0; } s = buf+4; if (!*s) malformed (); p_first = (LINENUM) atol(s); while (ISDIGIT (*s)) s++; if (*s == ',') { p_ptrn_lines = (LINENUM) atol(++s); while (ISDIGIT (*s)) s++; } else p_ptrn_lines = 1; if (*s == ' ') s++; if (*s != '+' || !*++s) malformed (); p_newfirst = (LINENUM) atol(s); while (ISDIGIT (*s)) s++; if (*s == ',') { p_repl_lines = (LINENUM) atol(++s); while (ISDIGIT (*s)) s++; } else p_repl_lines = 1; if (*s == ' ') s++; if (*s != '@') malformed (); if (!p_ptrn_lines) p_first++; /* do append rather than insert */ if (!p_repl_lines) p_newfirst++; p_max = p_ptrn_lines + p_repl_lines + 1; while (p_max >= hunkmax) if (! grow_hunkmax ()) return -1; fillsrc = 1; filldst = fillsrc + p_ptrn_lines; p_end = filldst + p_repl_lines; sprintf (buf,"*** %ld,%ld ****\n",p_first,p_first + p_ptrn_lines - 1); p_len[0] = strlen (buf); if (! (p_line[0] = savestr (buf))) { p_end = -1; return -1; } p_Char[0] = '*'; sprintf (buf,"--- %ld,%ld ----\n",p_newfirst,p_newfirst+p_repl_lines-1); p_len[filldst] = strlen (buf); if (! (p_line[filldst] = savestr (buf))) { p_end = 0; return -1; } p_Char[filldst++] = '='; p_prefix_context = -1; p_hunk_beg = p_input_line + 1; while (fillsrc <= p_ptrn_lines || filldst <= p_end) { chars_read = get_line (); if (!chars_read) { if (p_max - filldst < 3) { strcpy (buf, " \n"); /* assume blank lines got chopped */ chars_read = 2; } else { fatal ("unexpected end of file in patch"); } } if (chars_read == (size_t) -1) s = 0; else if (*buf == '\t' || *buf == '\n') { ch = ' '; /* assume the space got eaten */ s = savebuf (buf, chars_read); } else { ch = *buf; s = savebuf (buf+1, --chars_read); } if (!s) { while (--filldst > p_ptrn_lines) free(p_line[filldst]); p_end = fillsrc-1; return -1; } switch (ch) { case '-': if (fillsrc > p_ptrn_lines) { free(s); p_end = filldst-1; malformed (); } chars_read -= fillsrc == p_ptrn_lines && incomplete_line (); p_Char[fillsrc] = ch; p_line[fillsrc] = s; p_len[fillsrc++] = chars_read; break; case '=': ch = ' '; /* FALL THROUGH */ case ' ': if (fillsrc > p_ptrn_lines) { free(s); while (--filldst > p_ptrn_lines) free(p_line[filldst]); p_end = fillsrc-1; malformed (); } context++; chars_read -= fillsrc == p_ptrn_lines && incomplete_line (); p_Char[fillsrc] = ch; p_line[fillsrc] = s; p_len[fillsrc++] = chars_read; s = savebuf (s, chars_read); if (!s) { while (--filldst > p_ptrn_lines) free(p_line[filldst]); p_end = fillsrc-1; return -1; } /* FALL THROUGH */ case '+': if (filldst > p_end) { free(s); while (--filldst > p_ptrn_lines) free(p_line[filldst]); p_end = fillsrc-1; malformed (); } chars_read -= filldst == p_end && incomplete_line (); p_Char[filldst] = ch; p_line[filldst] = s; p_len[filldst++] = chars_read; break; default: p_end = filldst; malformed (); } if (ch != ' ') { if (p_prefix_context == -1) p_prefix_context = context; context = 0; } }/* while */ if (p_prefix_context == -1) malformed (); p_suffix_context = context; } else { /* normal diff--fake it up */ char hunk_type; register int i; LINENUM min, max; file_offset line_beginning = file_tell (pfp); p_prefix_context = p_suffix_context = 0; chars_read = get_line (); if (chars_read == (size_t) -1 || !chars_read || !ISDIGIT (*buf)) { next_intuit_at(line_beginning,p_input_line); return chars_read == (size_t) -1 ? -1 : 0; } p_first = (LINENUM)atol(buf); for (s = buf; ISDIGIT (*s); s++) continue; if (*s == ',') { p_ptrn_lines = (LINENUM)atol(++s) - p_first + 1; while (ISDIGIT (*s)) s++; } else p_ptrn_lines = (*s != 'a'); hunk_type = *s; if (hunk_type == 'a') p_first++; /* do append rather than insert */ min = (LINENUM)atol(++s); while (ISDIGIT (*s)) s++; if (*s == ',') max = (LINENUM)atol(++s); else max = min; if (hunk_type == 'd') min++; p_end = p_ptrn_lines + 1 + max - min + 1; while (p_end >= hunkmax) if (! grow_hunkmax ()) { p_end = -1; return -1; } p_newfirst = min; p_repl_lines = max - min + 1; sprintf (buf, "*** %ld,%ld\n", p_first, p_first + p_ptrn_lines - 1); p_len[0] = strlen (buf); if (! (p_line[0] = savestr (buf))) { p_end = -1; return -1; } p_Char[0] = '*'; for (i=1; i<=p_ptrn_lines; i++) { chars_read = get_line (); if (chars_read == (size_t) -1) { p_end = i - 1; return -1; } if (!chars_read) fatal ("unexpected end of file in patch at line %ld", p_input_line); if (buf[0] != '<' || (buf[1] != ' ' && buf[1] != '\t')) fatal ("`<' expected at line %ld of patch", p_input_line); chars_read -= 2 + (i == p_ptrn_lines && incomplete_line ()); p_len[i] = chars_read; if (! (p_line[i] = savebuf (buf + 2, chars_read))) { p_end = i-1; return -1; } p_Char[i] = '-'; } if (hunk_type == 'c') { chars_read = get_line (); if (chars_read == (size_t) -1) { p_end = i - 1; return -1; } if (! chars_read) fatal ("unexpected end of file in patch at line %ld", p_input_line); if (*buf != '-') fatal ("`---' expected at line %ld of patch", p_input_line); } sprintf (buf, "--- %ld,%ld\n", min, max); p_len[i] = strlen (buf); if (! (p_line[i] = savestr (buf))) { p_end = i-1; return -1; } p_Char[i] = '='; for (i++; i<=p_end; i++) { chars_read = get_line (); if (chars_read == (size_t) -1) { p_end = i - 1; return -1; } if (!chars_read) fatal ("unexpected end of file in patch at line %ld", p_input_line); if (buf[0] != '>' || (buf[1] != ' ' && buf[1] != '\t')) fatal ("`>' expected at line %ld of patch", p_input_line); chars_read -= 2 + (i == p_end && incomplete_line ()); p_len[i] = chars_read; if (! (p_line[i] = savebuf (buf + 2, chars_read))) { p_end = i-1; return -1; } p_Char[i] = '+'; } } if (rev) /* backwards patch? */ if (!pch_swap()) say ("Not enough memory to swap next hunk!\n"); if (debug & 2) { LINENUM i; char special; for (i=0; i <= p_end; i++) { if (i == p_ptrn_lines) special = '^'; else special = ' '; fprintf (stderr, "%3ld %c %c ", i, p_Char[i], special); pch_write_line (i, stderr); fflush (stderr); } } if (p_end+1 < hunkmax) /* paranoia reigns supreme... */ p_Char[p_end+1] = '^'; /* add a stopper for apply_hunk */ return 1; } static size_t get_line () { return pget_line (p_indent, p_rfc934_nesting); } /* Input a line from the patch file, worrying about indentation. Strip up to INDENT characters' worth of leading indentation. Then remove up to RFC934_NESTING instances of leading "- ". Ignore any partial lines at end of input, but warn about them. Succeed if a line was read; it is terminated by "\n\0" for convenience. Return the number of characters read, including '\n' but not '\0'. Return -1 if we ran out of memory. */ static size_t pget_line (indent, rfc934_nesting) int indent; int rfc934_nesting; { register FILE *fp = pfp; register int c; register int i = 0; register char *b; register size_t s; for (;;) { c = getc (fp); if (c == EOF) { if (ferror (fp)) read_fatal (); return 0; } if (indent <= i) break; if (c == ' ' || c == 'X') i++; else if (c == '\t') i = (i + 8) & ~7; else break; } i = 0; b = buf; while (c == '-' && 0 <= --rfc934_nesting) { c = getc (fp); if (c == EOF) goto patch_ends_in_middle_of_line; if (c != ' ') { i = 1; b[0] = '-'; break; } c = getc (fp); if (c == EOF) goto patch_ends_in_middle_of_line; } s = bufsize; for (;;) { if (i == s - 1) { s *= 2; b = realloc (b, s); if (!b) { if (!using_plan_a) memory_fatal (); return (size_t) -1; } buf = b; bufsize = s; } b[i++] = c; if (c == '\n') break; c = getc (fp); if (c == EOF) goto patch_ends_in_middle_of_line; } b[i] = '\0'; p_input_line++; return i; patch_ends_in_middle_of_line: if (ferror (fp)) read_fatal (); say ("patch unexpectedly ends in middle of line\n"); return 0; } static bool incomplete_line () { register FILE *fp = pfp; register int c; register file_offset line_beginning = file_tell (fp); if (getc (fp) == '\\') { while ((c = getc (fp)) != '\n' && c != EOF) continue; return TRUE; } else { /* We don't trust ungetc. */ Fseek (pfp, line_beginning, SEEK_SET); return FALSE; } } /* Reverse the old and new portions of the current hunk. */ bool pch_swap() { char **tp_line; /* the text of the hunk */ size_t *tp_len; /* length of each line */ char *tp_char; /* +, -, and ! */ register LINENUM i; register LINENUM n; bool blankline = FALSE; register char *s; i = p_first; p_first = p_newfirst; p_newfirst = i; /* make a scratch copy */ tp_line = p_line; tp_len = p_len; tp_char = p_Char; p_line = 0; /* force set_hunkmax to allocate again */ p_len = 0; p_Char = 0; set_hunkmax(); if (!p_line || !p_len || !p_Char) { if (p_line) free (p_line); p_line = tp_line; if (p_len) free (p_len); p_len = tp_len; if (p_Char) free (p_Char); p_Char = tp_char; return FALSE; /* not enough memory to swap hunk! */ } /* now turn the new into the old */ i = p_ptrn_lines + 1; if (tp_char[i] == '\n') { /* account for possible blank line */ blankline = TRUE; i++; } if (p_efake >= 0) { /* fix non-freeable ptr range */ if (p_efake <= i) n = p_end - i + 1; else n = -i; p_efake += n; p_bfake += n; } for (n=0; i <= p_end; i++,n++) { p_line[n] = tp_line[i]; p_Char[n] = tp_char[i]; if (p_Char[n] == '+') p_Char[n] = '-'; p_len[n] = tp_len[i]; } if (blankline) { i = p_ptrn_lines + 1; p_line[n] = tp_line[i]; p_Char[n] = tp_char[i]; p_len[n] = tp_len[i]; n++; } assert(p_Char[0] == '='); p_Char[0] = '*'; for (s=p_line[0]; *s; s++) if (*s == '-') *s = '*'; /* now turn the old into the new */ assert(tp_char[0] == '*'); tp_char[0] = '='; for (s=tp_line[0]; *s; s++) if (*s == '*') *s = '-'; for (i=0; n <= p_end; i++,n++) { p_line[n] = tp_line[i]; p_Char[n] = tp_char[i]; if (p_Char[n] == '-') p_Char[n] = '+'; p_len[n] = tp_len[i]; } assert(i == p_ptrn_lines + 1); i = p_ptrn_lines; p_ptrn_lines = p_repl_lines; p_repl_lines = i; if (tp_line) free (tp_line); if (tp_len) free (tp_len); if (tp_char) free (tp_char); return TRUE; } /* Return whether file WHICH (0 = old, 1 = new) appears to nonexistent. Return 1 for empty, 2 for nonexistent. */ bool pch_says_nonexistent (which) int which; { return p_says_nonexistent[which]; } /* Return timestamp of patch header for file WHICH (0 = old, 1 = new), or -1 if there was no timestamp or an error in the timestamp. */ time_t pch_timestamp (which) int which; { return p_timestamp[which]; } /* Return the specified line position in the old file of the old context. */ LINENUM pch_first() { return p_first; } /* Return the number of lines of old context. */ LINENUM pch_ptrn_lines() { return p_ptrn_lines; } /* Return the probable line position in the new file of the first line. */ LINENUM pch_newfirst() { return p_newfirst; } /* Return the number of lines in the replacement text including context. */ LINENUM pch_repl_lines() { return p_repl_lines; } /* Return the number of lines in the whole hunk. */ LINENUM pch_end() { return p_end; } /* Return the number of context lines before the first changed line. */ LINENUM pch_prefix_context () { return p_prefix_context; } /* Return the number of context lines after the last changed line. */ LINENUM pch_suffix_context () { return p_suffix_context; } /* Return the length of a particular patch line. */ size_t pch_line_len(line) LINENUM line; { return p_len[line]; } /* Return the control character (+, -, *, !, etc) for a patch line. */ char pch_char(line) LINENUM line; { return p_Char[line]; } /* Return a pointer to a particular patch line. */ char * pfetch(line) LINENUM line; { return p_line[line]; } /* Output a patch line. */ bool pch_write_line (line, file) LINENUM line; FILE *file; { bool after_newline = p_line[line][p_len[line] - 1] == '\n'; if (! fwrite (p_line[line], sizeof (*p_line[line]), p_len[line], file)) write_fatal (); return after_newline; } /* Return where in the patch file this hunk began, for error messages. */ LINENUM pch_hunk_beg() { return p_hunk_beg; } /* Apply an ed script by feeding ed itself. */ void do_ed_script (ofp) FILE *ofp; { static char const ed_program[] = ed_PROGRAM; register char *t; register file_offset beginning_of_this_line; register bool this_line_is_command = FALSE; register FILE *pipefp = 0; register size_t chars_read; if (!skip_rest_of_patch) { assert (! inerrno); copy_file (inname, TMPOUTNAME, instat.st_mode); sprintf (buf, "%s %s%s", ed_program, verbosity == VERBOSE ? "" : "- ", TMPOUTNAME); fflush (stdout); pipefp = popen(buf, binary_transput ? "wb" : "w"); if (!pipefp) pfatal ("can't open pipe to `%s'", buf); } for (;;) { beginning_of_this_line = file_tell (pfp); chars_read = get_line (); if (! chars_read) { next_intuit_at(beginning_of_this_line,p_input_line); break; } for (t = buf; ISDIGIT (*t) || *t == ','; t++) continue; this_line_is_command = (ISDIGIT (*buf) && (*t == 'd' || *t == 'c' || *t == 'a' || *t == 'i' || *t == 's') ); if (this_line_is_command) { if (pipefp) if (! fwrite (buf, sizeof *buf, chars_read, pipefp)) write_fatal (); if (*t != 'd' && *t != 's') { while ((chars_read = get_line ()) != 0) { if (pipefp) if (! fwrite (buf, sizeof *buf, chars_read, pipefp)) write_fatal (); if (chars_read == 2 && strEQ (buf, ".\n")) break; } } } else { next_intuit_at(beginning_of_this_line,p_input_line); break; } } if (!pipefp) return; if (fwrite ("w\nq\n", sizeof (char), (size_t) 4, pipefp) == 0 || fflush (pipefp) != 0) write_fatal (); if (pclose (pipefp) != 0) fatal ("%s FAILED", ed_program); if (ofp) { FILE *ifp = fopen (TMPOUTNAME, binary_transput ? "rb" : "r"); int c; if (!ifp) pfatal ("can't open `%s'", TMPOUTNAME); while ((c = getc (ifp)) != EOF) if (putc (c, ofp) == EOF) write_fatal (); if (ferror (ifp) || fclose (ifp) != 0) read_fatal (); } } /sys/src/ape/cmd/patch/pch.h 664 sys sys 1367613436 893 /* reading patches */ /* $Id: pch.h,v 1.8 1997/06/13 06:28:37 eggert Exp $ */ LINENUM pch_end PARAMS ((void)); LINENUM pch_first PARAMS ((void)); LINENUM pch_hunk_beg PARAMS ((void)); LINENUM pch_newfirst PARAMS ((void)); LINENUM pch_prefix_context PARAMS ((void)); LINENUM pch_ptrn_lines PARAMS ((void)); LINENUM pch_repl_lines PARAMS ((void)); LINENUM pch_suffix_context PARAMS ((void)); bool pch_swap PARAMS ((void)); bool pch_write_line PARAMS ((LINENUM, FILE *)); bool there_is_another_patch PARAMS ((void)); char *pfetch PARAMS ((LINENUM)); char pch_char PARAMS ((LINENUM)); int another_hunk PARAMS ((enum diff, int)); int pch_says_nonexistent PARAMS ((int)); size_t pch_line_len PARAMS ((LINENUM)); time_t pch_timestamp PARAMS ((int)); void do_ed_script PARAMS ((FILE *)); void open_patch_file PARAMS ((char const *)); void re_patch PARAMS ((void)); void set_hunkmax PARAMS ((void)); /sys/src/ape/cmd/patch/quotearg.c 664 sys sys 1367613436 2844 /* Shell command argument quoting. Copyright (C) 1994, 1995, 1997 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; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Written by Paul Eggert */ #if HAVE_CONFIG_H # include #endif #include #include /* Place into QUOTED a quoted version of ARG suitable for `system'. Return the length of the resulting string (which is not null-terminated). If QUOTED is null, return the length without any side effects. */ size_t quote_system_arg (quoted, arg) char *quoted; char const *arg; { char const *a; size_t len = 0; /* Scan ARG, copying it to QUOTED if QUOTED is not null, looking for shell metacharacters. */ for (a = arg; ; a++) { char c = *a; switch (c) { case 0: /* ARG has no shell metacharacters. */ return len; case '=': if (*arg == '-') break; /* Fall through. */ case '\t': case '\n': case ' ': case '!': case '"': case '#': case '$': case '%': case '&': case '\'': case '(': case ')': case '*': case ';': case '<': case '>': case '?': case '[': case '\\': case '^': case '`': case '|': case '~': { /* ARG has a shell metacharacter. Start over, quoting it this time. */ len = 0; c = *arg++; /* If ARG is an option, quote just its argument. This is not necessary, but it looks nicer. */ if (c == '-' && arg < a) { c = *arg++; if (quoted) { quoted[len] = '-'; quoted[len + 1] = c; } len += 2; if (c == '-') while (arg < a) { c = *arg++; if (quoted) quoted[len] = c; len++; if (c == '=') break; } c = *arg++; } if (quoted) quoted[len] = '\''; len++; for (; c; c = *arg++) { if (c == '\'') { if (quoted) { quoted[len] = '\''; quoted[len + 1] = '\\'; quoted[len + 2] = '\''; } len += 3; } if (quoted) quoted[len] = c; len++; } if (quoted) quoted[len] = '\''; return len + 1; } } if (quoted) quoted[len] = c; len++; } } /sys/src/ape/cmd/patch/quotearg.h 664 sys sys 1367613436 234 /* quote.h -- declarations for quoting system arguments */ #if defined __STDC__ || __GNUC__ # define __QUOTEARG_P(args) args #else # define __QUOTEARG_P(args) () #endif size_t quote_system_arg __QUOTEARG_P ((char *, char const *)); /sys/src/ape/cmd/patch/util.c 664 sys sys 1367613436 24520 /* utility functions for `patch' */ /* $Id: util.c,v 1.24 1997/07/10 08:16:12 eggert Exp $ */ /* Copyright 1986 Larry Wall Copyright 1992, 1993, 1997 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; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #define XTERN extern #include #include #include #include #undef XTERN #define XTERN #include #include #include #include #if !defined SIGCHLD && defined SIGCLD #define SIGCHLD SIGCLD #endif #if ! HAVE_RAISE # define raise(sig) kill (getpid (), sig) #endif #ifdef __STDC__ # include # define vararg_start va_start #else # define vararg_start(ap,p) va_start (ap) # if HAVE_VARARGS_H # include # else typedef char *va_list; # define va_dcl int va_alist; # define va_start(ap) ((ap) = (va_list) &va_alist) # define va_arg(ap, t) (((t *) ((ap) += sizeof (t))) [-1]) # define va_end(ap) # endif #endif static void makedirs PARAMS ((char *)); /* Move a file FROM to TO, renaming it if possible and copying it if necessary. If we must create TO, use MODE to create it. If FROM is null, remove TO (ignoring FROMSTAT). Back up TO if BACKUP is nonzero. */ #ifdef __STDC__ /* If mode_t doesn't promote to itself, we can't use old-style definition. */ void move_file (char const *from, char *to, mode_t mode, int backup) #else void move_file (from, to, mode, backup) char const *from; char *to; mode_t mode; int backup; #endif { struct stat to_st; int to_errno = ! backup ? -1 : stat (to, &to_st) == 0 ? 0 : errno; if (backup) { int try_makedirs_errno = 0; char *bakname; if (origprae || origbase) { char const *p = origprae ? origprae : ""; char const *b = origbase ? origbase : ""; char const *o = base_name (to); size_t plen = strlen (p); size_t tlen = o - to; size_t blen = strlen (b); size_t osize = strlen (o) + 1; bakname = xmalloc (plen + tlen + blen + osize); memcpy (bakname, p, plen); memcpy (bakname + plen, to, tlen); memcpy (bakname + plen + tlen, b, blen); memcpy (bakname + plen + tlen + blen, o, osize); for (p += FILESYSTEM_PREFIX_LEN (p); *p; p++) if (ISSLASH (*p)) { try_makedirs_errno = ENOENT; break; } } else { bakname = find_backup_file_name (to); if (!bakname) memory_fatal (); } if (to_errno) { int fd; if (debug & 4) say ("creating empty unreadable file `%s'\n", bakname); try_makedirs_errno = ENOENT; unlink (bakname); while ((fd = creat (bakname, 0)) < 0) { if (errno != try_makedirs_errno) pfatal ("can't create file `%s'", bakname); makedirs (bakname); try_makedirs_errno = 0; } if (close (fd) != 0) pfatal ("can't close `%s'", bakname); } else { if (debug & 4) say ("renaming `%s' to `%s'\n", to, bakname); while (rename (to, bakname) != 0) { if (errno != try_makedirs_errno) pfatal ("can't rename `%s' to `%s'", to, bakname); makedirs (bakname); try_makedirs_errno = 0; } } free (bakname); } if (from) { if (debug & 4) say ("renaming `%s' to `%s'\n", from, to); if (rename (from, to) != 0) { int to_dir_known_to_exist = 0; if (errno == ENOENT && (to_errno == -1 || to_errno == ENOENT)) { makedirs (to); to_dir_known_to_exist = 1; if (rename (from, to) == 0) return; } if (errno == EXDEV) { if (! backup) { if (unlink (to) == 0) to_dir_known_to_exist = 1; else if (errno != ENOENT) pfatal ("can't remove `%s'", to); } if (! to_dir_known_to_exist) makedirs (to); copy_file (from, to, mode); return; } pfatal ("can't rename `%s' to `%s'", from, to); } } else if (! backup) { if (debug & 4) say ("removing `%s'\n", to); if (unlink (to) != 0) pfatal ("can't remove `%s'", to); } } /* Create FILE with OPEN_FLAGS, and with MODE adjusted so that we can read and write the file and that the file is not executable. Return the file descriptor. */ #ifdef __STDC__ /* If mode_t doesn't promote to itself, we can't use old-style definition. */ int create_file (char const *file, int open_flags, mode_t mode) #else int create_file (file, open_flags, mode) char const *file; int open_flags; mode_t mode; #endif { int fd; mode |= S_IRUSR | S_IWUSR; mode &= ~ (S_IXUSR | S_IXGRP | S_IXOTH); if (! (O_CREAT && O_TRUNC)) close (creat (file, mode)); fd = open (file, O_CREAT | O_TRUNC | open_flags, mode); if (fd < 0) pfatal ("can't create `%s'", file); return fd; } /* Copy a file. */ #ifdef __STDC__ /* If mode_t doesn't promote to itself, we can't use old-style definition. */ void copy_file (char const *from, char const *to, mode_t mode) #else void copy_file (from, to, mode) char const *from; char const *to; mode_t mode; #endif { int tofd; int fromfd; size_t i; if ((fromfd = open (from, O_RDONLY | O_BINARY)) < 0) pfatal ("can't reopen `%s'", from); tofd = create_file (to, O_WRONLY | O_BINARY, mode); while ((i = read (fromfd, buf, bufsize)) != 0) { if (i == -1) read_fatal (); if (write (tofd, buf, i) != i) write_fatal (); } if (close (fromfd) != 0) read_fatal (); if (close (tofd) != 0) write_fatal (); } static char const DEV_NULL[] = NULL_DEVICE; static char const SCCSPREFIX[] = "s."; static char const GET[] = "get "; static char const GET_LOCKED[] = "get -e "; static char const SCCSDIFF1[] = "get -p "; static char const SCCSDIFF2[] = "|diff - %s"; static char const RCSSUFFIX[] = ",v"; static char const CHECKOUT[] = "co %s"; static char const CHECKOUT_LOCKED[] = "co -l %s"; static char const RCSDIFF1[] = "rcsdiff %s"; /* Return "RCS" if FILENAME is controlled by RCS, "SCCS" if it is controlled by SCCS, and 0 otherwise. READONLY is nonzero if we desire only readonly access to FILENAME. FILESTAT describes FILENAME's status or is 0 if FILENAME does not exist. If successful and if GETBUF is nonzero, set *GETBUF to a command that gets the file; similarly for DIFFBUF and a command to diff the file. *GETBUF and *DIFFBUF must be freed by the caller. */ char const * version_controller (filename, readonly, filestat, getbuf, diffbuf) char const *filename; int readonly; struct stat const *filestat; char **getbuf; char **diffbuf; { struct stat cstat; char const *filebase = base_name (filename); char const *dotslash = *filename == '-' ? "./" : ""; size_t dir_len = filebase - filename; size_t filenamelen = strlen (filename); size_t maxfixlen = sizeof "SCCS/" - 1 + sizeof SCCSPREFIX - 1; size_t maxtrysize = filenamelen + maxfixlen + 1; size_t quotelen = quote_system_arg (0, filename); size_t maxgetsize = sizeof GET_LOCKED + quotelen + maxfixlen; size_t maxdiffsize = (sizeof SCCSDIFF1 + sizeof SCCSDIFF2 + sizeof DEV_NULL - 1 + 2 * quotelen + maxfixlen); char *trybuf = xmalloc (maxtrysize); char const *r = 0; strcpy (trybuf, filename); #define try1(f,a1) (sprintf (trybuf + dir_len, f, a1), stat (trybuf, &cstat) == 0) #define try2(f,a1,a2) (sprintf (trybuf + dir_len, f, a1,a2), stat (trybuf, &cstat) == 0) /* Check that RCS file is not working file. Some hosts don't report file name length errors. */ if ((try2 ("RCS/%s%s", filebase, RCSSUFFIX) || try1 ("RCS/%s", filebase) || try2 ("%s%s", filebase, RCSSUFFIX)) && ! (filestat && filestat->st_dev == cstat.st_dev && filestat->st_ino == cstat.st_ino)) { if (getbuf) { char *p = *getbuf = xmalloc (maxgetsize); sprintf (p, readonly ? CHECKOUT : CHECKOUT_LOCKED, dotslash); p += strlen (p); p += quote_system_arg (p, filename); *p = '\0'; } if (diffbuf) { char *p = *diffbuf = xmalloc (maxdiffsize); sprintf (p, RCSDIFF1, dotslash); p += strlen (p); p += quote_system_arg (p, filename); *p++ = '>'; strcpy (p, DEV_NULL); } r = "RCS"; } else if (try2 ("SCCS/%s%s", SCCSPREFIX, filebase) || try2 ("%s%s", SCCSPREFIX, filebase)) { if (getbuf) { char *p = *getbuf = xmalloc (maxgetsize); sprintf (p, readonly ? GET : GET_LOCKED); p += strlen (p); p += quote_system_arg (p, trybuf); *p = '\0'; } if (diffbuf) { char *p = *diffbuf = xmalloc (maxdiffsize); strcpy (p, SCCSDIFF1); p += sizeof SCCSDIFF1 - 1; p += quote_system_arg (p, trybuf); sprintf (p, SCCSDIFF2, dotslash); p += strlen (p); p += quote_system_arg (p, filename); *p++ = '>'; strcpy (p, DEV_NULL); } r = "SCCS"; } free (trybuf); return r; } /* Get FILENAME from version control system CS. The file already exists if EXISTS is nonzero. Only readonly access is needed if READONLY is nonzero. Use the command GETBUF to actually get the named file. Store the resulting file status into *FILESTAT. Return nonzero if successful. */ int version_get (filename, cs, exists, readonly, getbuf, filestat) char const *filename; char const *cs; int exists; int readonly; char const *getbuf; struct stat *filestat; { if (patch_get < 0) { ask ("Get file `%s' from %s%s? [y] ", filename, cs, readonly ? "" : " with lock"); if (*buf == 'n') return 0; } if (dry_run) { if (! exists) fatal ("can't do dry run on nonexistent version-controlled file `%s'; invoke `%s' and try again", filename, getbuf); } else { if (verbosity == VERBOSE) say ("Getting file `%s' from %s%s...\n", filename, cs, readonly ? "" : " with lock"); if (systemic (getbuf) != 0) fatal ("can't get file `%s' from %s", filename, cs); if (stat (filename, filestat) != 0) pfatal ("%s", filename); } return 1; } /* Allocate a unique area for a string. */ char * savebuf (s, size) register char const *s; register size_t size; { register char *rv; assert (s && size); rv = malloc (size); if (! rv) { if (! using_plan_a) memory_fatal (); } else memcpy (rv, s, size); return rv; } char * savestr(s) char const *s; { return savebuf (s, strlen (s) + 1); } void remove_prefix (p, prefixlen) char *p; size_t prefixlen; { char const *s = p + prefixlen; while ((*p++ = *s++)) continue; } #if !HAVE_VPRINTF #define vfprintf my_vfprintf static int vfprintf PARAMS ((FILE *, char const *, va_list)); static int vfprintf (stream, format, args) FILE *stream; char const *format; va_list args; { #if !HAVE_DOPRNT && HAVE__DOPRINTF # define _doprnt _doprintf #endif #if HAVE_DOPRNT || HAVE__DOPRINTF _doprnt (format, args, stream); return ferror (stream) ? -1 : 0; #else int *a = (int *) args; return fprintf (stream, format, a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9]); #endif } #endif /* !HAVE_VPRINTF */ /* Terminal output, pun intended. */ #ifdef __STDC__ void fatal (char const *format, ...) #else /*VARARGS1*/ void fatal (format, va_alist) char const *format; va_dcl #endif { va_list args; fprintf (stderr, "%s: **** ", program_name); vararg_start (args, format); vfprintf (stderr, format, args); va_end (args); putc ('\n', stderr); fflush (stderr); fatal_exit (0); } void memory_fatal () { fatal ("out of memory"); } void read_fatal () { pfatal ("read error"); } void write_fatal () { pfatal ("write error"); } /* Say something from patch, something from the system, then silence . . . */ #ifdef __STDC__ void pfatal (char const *format, ...) #else /*VARARGS1*/ void pfatal (format, va_alist) char const *format; va_dcl #endif { int errnum = errno; va_list args; fprintf (stderr, "%s: **** ", program_name); vararg_start (args, format); vfprintf (stderr, format, args); va_end (args); fflush (stderr); /* perror bypasses stdio on some hosts. */ errno = errnum; perror (" "); fflush (stderr); fatal_exit (0); } /* Tell the user something. */ #ifdef __STDC__ void say (char const *format, ...) #else /*VARARGS1*/ void say (format, va_alist) char const *format; va_dcl #endif { va_list args; vararg_start (args, format); vfprintf (stdout, format, args); va_end (args); fflush (stdout); } /* Get a response from the user, somehow or other. */ #ifdef __STDC__ void ask (char const *format, ...) #else /*VARARGS1*/ void ask (format, va_alist) char const *format; va_dcl #endif { static int ttyfd = -2; int r; va_list args; vararg_start (args, format); vfprintf (stdout, format, args); va_end (args); fflush (stdout); if (ttyfd == -2) { /* If standard output is not a tty, don't bother opening /dev/tty, since it's unlikely that stdout will be seen by the tty user. The isatty test also works around a bug in GNU Emacs 19.34 under Linux which makes a call-process `patch' hang when it reads from /dev/tty. POSIX.2 requires that we read /dev/tty, though. */ ttyfd = (posixly_correct || isatty (STDOUT_FILENO) ? open (TTY_DEVICE, O_RDONLY) : -1); } if (ttyfd < 0) { /* No terminal at all -- default it. */ printf ("\n"); buf[0] = '\n'; buf[1] = '\0'; } else { size_t s = 0; while ((r = read (ttyfd, buf + s, bufsize - 1 - s)) == bufsize - 1 - s && buf[bufsize - 2] != '\n') { s = bufsize - 1; bufsize *= 2; buf = realloc (buf, bufsize); if (!buf) memory_fatal (); } if (r == 0) printf ("EOF\n"); else if (r < 0) { perror ("tty read"); fflush (stderr); close (ttyfd); ttyfd = -1; r = 0; } buf[s + r] = '\0'; } } /* Return nonzero if it OK to reverse a patch. */ #ifdef __STDC__ int ok_to_reverse (char const *format, ...) #else ok_to_reverse (format, va_alist) char const *format; va_dcl #endif { int r = 0; if (noreverse || ! (force && verbosity == SILENT)) { va_list args; vararg_start (args, format); vfprintf (stdout, format, args); va_end (args); } if (noreverse) { printf (" Skipping patch.\n"); skip_rest_of_patch = TRUE; r = 0; } else if (force) { if (verbosity != SILENT) printf (" Applying it anyway.\n"); r = 0; } else if (batch) { say (reverse ? " Ignoring -R.\n" : " Assuming -R.\n"); r = 1; } else { ask (reverse ? " Ignore -R? [n] " : " Assume -R? [n] "); r = *buf == 'y'; if (! r) { ask ("Apply anyway? [n] "); if (*buf != 'y') { if (verbosity != SILENT) say ("Skipping patch.\n"); skip_rest_of_patch = TRUE; } } } return r; } /* How to handle certain events when not in a critical region. */ #define NUM_SIGS (sizeof (sigs) / sizeof (*sigs)) static int const sigs[] = { #ifdef SIGHUP SIGHUP, #endif #ifdef SIGPIPE SIGPIPE, #endif #ifdef SIGTERM SIGTERM, #endif #ifdef SIGXCPU SIGXCPU, #endif #ifdef SIGXFSZ SIGXFSZ, #endif SIGINT }; #if !HAVE_SIGPROCMASK #define sigset_t int #define sigemptyset(s) (*(s) = 0) #ifndef sigmask #define sigmask(sig) (1 << ((sig) - 1)) #endif #define sigaddset(s, sig) (*(s) |= sigmask (sig)) #define sigismember(s, sig) ((*(s) & sigmask (sig)) != 0) #ifndef SIG_BLOCK #define SIG_BLOCK 0 #endif #ifndef SIG_UNBLOCK #define SIG_UNBLOCK (SIG_BLOCK + 1) #endif #ifndef SIG_SETMASK #define SIG_SETMASK (SIG_BLOCK + 2) #endif #define sigprocmask(how, n, o) \ ((how) == SIG_BLOCK \ ? ((o) ? *(o) = sigblock (*(n)) : sigblock (*(n))) \ : (how) == SIG_UNBLOCK \ ? sigsetmask (((o) ? *(o) = sigblock (0) : sigblock (0)) & ~*(n)) \ : (o ? *(o) = sigsetmask (*(n)) : sigsetmask (*(n)))) #if !HAVE_SIGSETMASK #define sigblock(mask) 0 #define sigsetmask(mask) 0 #endif #endif static sigset_t initial_signal_mask; static sigset_t signals_to_block; #if ! HAVE_SIGACTION static RETSIGTYPE fatal_exit_handler PARAMS ((int)) __attribute__ ((noreturn)); static RETSIGTYPE fatal_exit_handler (sig) int sig; { signal (sig, SIG_IGN); fatal_exit (sig); } #endif void set_signals(reset) int reset; { int i; #if HAVE_SIGACTION struct sigaction initial_act, fatal_act; fatal_act.sa_handler = fatal_exit; sigemptyset (&fatal_act.sa_mask); fatal_act.sa_flags = 0; #define setup_handler(sig) sigaction (sig, &fatal_act, (struct sigaction *) 0) #else #define setup_handler(sig) signal (sig, fatal_exit_handler) #endif if (!reset) { #ifdef SIGCHLD /* System V fork+wait does not work if SIGCHLD is ignored. */ signal (SIGCHLD, SIG_DFL); #endif sigemptyset (&signals_to_block); for (i = 0; i < NUM_SIGS; i++) { int ignoring_signal; #if HAVE_SIGACTION if (sigaction (sigs[i], (struct sigaction *) 0, &initial_act) != 0) continue; ignoring_signal = initial_act.sa_handler == SIG_IGN; #else ignoring_signal = signal (sigs[i], SIG_IGN) == SIG_IGN; #endif if (! ignoring_signal) { sigaddset (&signals_to_block, sigs[i]); setup_handler (sigs[i]); } } } else { /* Undo the effect of ignore_signals. */ #if HAVE_SIGPROCMASK || HAVE_SIGSETMASK sigprocmask (SIG_SETMASK, &initial_signal_mask, (sigset_t *) 0); #else for (i = 0; i < NUM_SIGS; i++) if (sigismember (&signals_to_block, sigs[i])) setup_handler (sigs[i]); #endif } } /* How to handle certain events when in a critical region. */ void ignore_signals() { #if HAVE_SIGPROCMASK || HAVE_SIGSETMASK sigprocmask (SIG_BLOCK, &signals_to_block, &initial_signal_mask); #else int i; for (i = 0; i < NUM_SIGS; i++) if (sigismember (&signals_to_block, sigs[i])) signal (sigs[i], SIG_IGN); #endif } void exit_with_signal (sig) int sig; { sigset_t s; signal (sig, SIG_DFL); sigemptyset (&s); sigaddset (&s, sig); sigprocmask (SIG_UNBLOCK, &s, (sigset_t *) 0); raise (sig); exit (2); } int systemic (command) char const *command; { if (debug & 8) say ("+ %s\n", command); fflush (stdout); return system (command); } #if !HAVE_MKDIR /* These mkdir and rmdir substitutes are good enough for `patch'; they are not general emulators. */ static int doprogram PARAMS ((char const *, char const *)); static int mkdir PARAMS ((char const *, mode_t)); static int rmdir PARAMS ((char const *)); static int doprogram (program, arg) char const *program; char const *arg; { int result; static char const DISCARD_OUTPUT[] = " 2>/dev/null"; size_t program_len = strlen (program); char *cmd = xmalloc (program_len + 1 + quote_system_arg (0, arg) + sizeof DISCARD_OUTPUT); char *p = cmd; strcpy (p, program); p += program_len; *p++ = ' '; p += quote_system_arg (p, arg); strcpy (p, DISCARD_OUTPUT); result = systemic (cmd); free (cmd); return result; } #ifdef __STDC__ /* If mode_t doesn't promote to itself, we can't use old-style definition. */ static int mkdir (char const *path, mode_t mode) #else static int mkdir (path, mode) char const *path; mode_t mode; /* ignored */ #endif { return doprogram ("mkdir", path); } static int rmdir (path) char const *path; { int result = doprogram ("rmdir", path); errno = EEXIST; return result; } #endif /* Replace '/' with '\0' in FILENAME if it marks a place that needs testing for the existence of directory. Return the address of the last location replaced, or 0 if none were replaced. */ static char *replace_slashes PARAMS ((char *)); static char * replace_slashes (filename) char *filename; { char *f; char *last_location_replaced = 0; char const *component_start; for (f = filename + FILESYSTEM_PREFIX_LEN (filename); ISSLASH (*f); f++) continue; component_start = f; for (; *f; f++) if (ISSLASH (*f)) { char *slash = f; /* Treat multiple slashes as if they were one slash. */ while (ISSLASH (f[1])) f++; /* Ignore slashes at the end of the path. */ if (! f[1]) break; /* "." and ".." need not be tested. */ if (! (slash - component_start <= 2 && component_start[0] == '.' && slash[-1] == '.')) { *slash = '\0'; last_location_replaced = slash; } component_start = f + 1; } return last_location_replaced; } /* Make sure we'll have the directories to create a file. Ignore the last element of `filename'. */ static void makedirs (filename) register char *filename; { register char *f; register char *flim = replace_slashes (filename); if (flim) { /* Create any missing directories, replacing NULs by '/'s. Ignore errors. We may have to keep going even after an EEXIST, since the path may contain ".."s; and when there is an EEXIST failure the system may return some other error number. Any problems will eventually be reported when we create the file. */ for (f = filename; f <= flim; f++) if (!*f) { mkdir (filename, S_IRUSR|S_IWUSR|S_IXUSR |S_IRGRP|S_IWGRP|S_IXGRP |S_IROTH|S_IWOTH|S_IXOTH); *f = '/'; } } } /* Remove empty ancestor directories of FILENAME. Ignore errors, since the path may contain ".."s, and when there is an EEXIST failure the system may return some other error number. */ void removedirs (filename) char *filename; { size_t i; for (i = strlen (filename); i != 0; i--) if (ISSLASH (filename[i]) && ! (ISSLASH (filename[i - 1]) || (filename[i - 1] == '.' && (i == 1 || ISSLASH (filename[i - 2]) || (filename[i - 2] == '.' && (i == 2 || ISSLASH (filename[i - 3]))))))) { filename[i] = '\0'; if (rmdir (filename) == 0 && verbosity == VERBOSE) say ("Removed empty directory `%s'.\n", filename); filename[i] = '/'; } } static time_t initial_time; void init_time () { time (&initial_time); } /* Make filenames more reasonable. */ char * fetchname (at, strip_leading, pstamp) char *at; int strip_leading; time_t *pstamp; { char *name; register char *t; int sleading = strip_leading; time_t stamp = (time_t) -1; while (ISSPACE ((unsigned char) *at)) at++; if (debug & 128) say ("fetchname %s %d\n", at, strip_leading); name = at; /* Strip off up to `sleading' leading slashes and null terminate. */ for (t = at; *t; t++) { if (ISSLASH (*t)) { while (ISSLASH (t[1])) t++; if (--sleading >= 0) name = t+1; } else if (ISSPACE ((unsigned char) *t)) { if (set_time | set_utc) stamp = str2time (t, initial_time, set_utc ? 0L : TM_LOCAL_ZONE); else { /* The head says the file is nonexistent if the timestamp is the epoch; but the listed time is local time, not UTC, and POSIX.1 allows local time to be 24 hours away from UTC. So match any time within 24 hours of the epoch. Use a default time zone 24 hours behind UTC so that any non-zoned time within 24 hours of the epoch is valid. */ stamp = str2time (t, initial_time, -24L * 60 * 60); if (0 <= stamp && stamp <= 2 * 24L * 60 * 60) stamp = 0; } *t = '\0'; break; } } if (!*name) return 0; /* Allow files to be created by diffing against /dev/null. */ if (strcmp (at, "/dev/null") == 0) { if (pstamp) *pstamp = 0; return 0; } if (pstamp) *pstamp = stamp; return savestr (name); } GENERIC_OBJECT * xmalloc (size) size_t size; { register GENERIC_OBJECT *p = malloc (size); if (!p) memory_fatal (); return p; } void Fseek (stream, offset, ptrname) FILE *stream; file_offset offset; int ptrname; { if (file_seek (stream, offset, ptrname) != 0) pfatal ("fseek"); } /sys/src/ape/cmd/patch/util.h 664 sys sys 1367613436 1553 /* utility functions for `patch' */ /* $Id: util.h,v 1.15 1997/07/16 12:26:36 eggert Exp $ */ int ok_to_reverse PARAMS ((char const *, ...)) __attribute__ ((format (printf, 1, 2))); void ask PARAMS ((char const *, ...)) __attribute__ ((format (printf, 1, 2))); void say PARAMS ((char const *, ...)) __attribute__ ((format (printf, 1, 2))); void fatal PARAMS ((char const *, ...)) __attribute__ ((noreturn, format (printf, 1, 2))); void pfatal PARAMS ((char const *, ...)) __attribute__ ((noreturn, format (printf, 1, 2))); char *fetchname PARAMS ((char *, int, time_t *)); char *savebuf PARAMS ((char const *, size_t)); char *savestr PARAMS ((char const *)); char const *version_controller PARAMS ((char const *, int, struct stat const *, char **, char **)); int version_get PARAMS ((char const *, char const *, int, int, char const *, struct stat *)); int create_file PARAMS ((char const *, int, mode_t)); int systemic PARAMS ((char const *)); void Fseek PARAMS ((FILE *, file_offset, int)); void copy_file PARAMS ((char const *, char const *, mode_t)); void exit_with_signal PARAMS ((int)) __attribute__ ((noreturn)); void ignore_signals PARAMS ((void)); void init_time PARAMS ((void)); void memory_fatal PARAMS ((void)) __attribute__ ((noreturn)); void move_file PARAMS ((char const *, char *, mode_t, int)); void read_fatal PARAMS ((void)) __attribute__ ((noreturn)); void remove_prefix PARAMS ((char *, size_t)); void removedirs PARAMS ((char *)); void set_signals PARAMS ((int)); void write_fatal PARAMS ((void)) __attribute__ ((noreturn)); /sys/src/ape/cmd/patch/version.c 664 sys sys 1367613436 869 /* Print the version number. */ /* $Id: version.c,v 1.5 1997/05/21 18:29:20 eggert Exp $ */ #define XTERN extern #include #undef XTERN #define XTERN #include #include static char const copyright_string[] = "\ Copyright 1988 Larry Wall\n\ Copyright 1997 Free Software Foundation, Inc."; static char const free_software_msgid[] = "\ This program comes with NO WARRANTY, to the extent permitted by law.\n\ You may redistribute copies of this program\n\ under the terms of the GNU General Public License.\n\ For more information about these matters, see the file named COPYING."; static char const authorship_msgid[] = "\ written by Larry Wall with lots o' patches by Paul Eggert"; void version() { printf ("%s %s\n%s\n\n%s\n\n%s\n", program_name, PATCH_VERSION, copyright_string, free_software_msgid, authorship_msgid); } /sys/src/ape/cmd/patch/version.h 664 sys sys 1367613436 125 /* Print the version number. */ /* $Id: version.h,v 1.3 1997/04/07 01:07:00 eggert Exp $ */ void version PARAMS ((void)); /sys/src/ape/cmd/pax 20000000775 sys sys 1371505601 0 /sys/src/ape/cmd/pax/Makefile 664 sys sys 1367613436 3654 # # PAX - read and write POSIX conformant tar and cpio archives # # Written by Mark H. Colburn (mark@jhereg.mn.org) # # $Id: Makefile,v 1.2 89/02/12 10:08:59 mark Exp $ # # # CONFIGURATION SECTION # # The following defines may need to be changed for each system which PAX # is installed on. Please review these settings before installing on your # system. # # You should define _POSIX_SOURCE if you are running on a POSIX system. This # include has to be in the command line because it has to appear before any # include file is included in the source. For most systems in use today, # it should be left blank. # # POSIX= -D_POSIX_SOURCE POSIX= # # Set CFLAGS to whatever makes your C compiler happy. Be sure to include # the definition of $(POSIX) in the flag. # CFLAGS = -O $(POSIX) CC = cc # # Set LIBS to any additional libraries that you need linked in with pax. # LIBS= # # Set LFLAGS to whatever makes your linker happy # #LDFLAGS = -s LDFLAGS = # # Set COPY to the name of the command to use to copy pax to cpio and # tar. Usually it is 'ln'. # COPY=ln # # Set LINTFLAGS to whatever makes your implementation of lint happy. If # you don't undef __STDC__ and you have an ANSI C compiler, lint will choke # on the function prototypes present in func.h. # LINTFLAGS = -U__STDC__ $(POSIX) # # BINDIR - points to the directory in which you want the final pax, tar and # cpio binaries installed in. # BINDIR = /usr/local/bin # # MANDIR - specify the directory in which the man pages will be installed # MAN5 = /usr/man/man5 MAN1 = /usr/man/man1 MAN5EXT = 5 MAN1EXT = 1 # # There are three different ways to get POSIX or BSD conformant directory # access routines: 1) they are installed in your system library, 2) you # are using Doug Gwyn's dirent library (/usr/lib/libdirent.a), or 3) you # need the source for the dirent package. Based on that, pick one of the # following three options: # # 1. Pick the first dirent line and make sure that config.h is defined # correctly for your version of directory access routines. THIS IS # THE LINE WHICH SHOULD BE USED FOR BSD SYSTEMS. # 2. Chose the second dirent line which used a library at link time. You # may need to change the name of the library to match your system. # 3. If you need #3, then you must copy everything in the subdirectory dirent # to this directory and choose the DIROBJ lines. Please note that this # version of dirent has been modified to work as a stand-alone. # DIRENT= #DIRENT= -ldirent #DIROBJ= paxdir.o # # END CONFIGURATION SECTION # # Nothing beyond this point should need to be changed. # SHELL = /bin/sh MISC = Makefile pax.1 tar.5 cpio.5 README PATCHLEVEL HEADERS= config.h func.h limits.h port.h pax.h SOURCE= pax.c append.c buffer.c cpio.c create.c extract.c fileio.c\ link.c list.c mem.c namelist.c names.c pass.c pathname.c\ port.c regexp.c replace.c tar.c ttyio.c warn.c wildmat.c OBJECT= pax.o append.o buffer.o cpio.o create.o extract.o fileio.o\ link.o list.o mem.o namelist.o names.o pass.o pathname.o\ port.o regexp.o replace.o tar.o ttyio.o warn.o wildmat.o $(DIROBJ) PROGS = pax tar cpio PMAN1 = pax.1 tar.1 PMAN5 = pax.5 tar.5 all: $(PROGS) install: $(PROGS) strip pax cp pax $(BINDIR) chmod 755 $(BINDIR)/pax ln $(BINDIR)/pax $(BINDIR)/tar ln $(BINDIR)/pax $(BINDIR)/cpio cp $(PMAN1) $(MAN1) # cp $(PMAN5) $(MAN5) clean: rm -f $(OBJECT) rm -f $(PROGS) a.out *.BAK *.bak lint: lint $(LINTFLAGS) $(SOURCE) pax : $(OBJECT) $(CC) $(CFLAGS) $(LDFLAGS) -o pax $(OBJECT) $(DIRENT) $(LIBS) tar: pax rm -f tar $(COPY) pax tar cpio: pax rm -f cpio $(COPY) pax cpio $(OBJECT): $(HEADERS) /sys/src/ape/cmd/pax/PATCHLEVEL 664 sys sys 1367613436 64 Patchlevel 1 $Id: PATCHLEVEL,v 1.2 89/02/12 10:09:03 mark Exp $ /sys/src/ape/cmd/pax/README 664 sys sys 1367613436 4965 PAX - Portable Archive Interchange Copyright (C) 1989 Mark H. Colburn All Rights Reserved. Introduction This is version 1.2 of Pax, an archiving utility. Pax is an archiving utility that reads and writes tar and cpio formats, both the traditional ones and the extended formats specified in IEEE 1003.1. It handles multi-volume archives and automatically determines the format of an archive while reading it. Three user interfaces are supported: tar, cpio, and pax. The pax interface was designed by IEEE 1003.2 as a compromise in the chronic controversy over which of tar or cpio is best. The USENIX Association provided some support for the initial implementation of this product. As a result, the Pax utility is being distributed free of charge and may be redistributed by others in either source or binary form. (See the liscensing section for restrictions) The source for Pax has been posted to comp.sources.unix on USENET and will also be available by anonymous FTP on the Internet from uunet.uu.net, moon.src.honeywell.com and from ucb-arpa.berkeley.edu. The source to Pax is also available via anonymous UUCP from jhereg.mn.org, the author's home machine and possibly other sites. The source for Pax will continue to change as long as the definition of the utility is modified by the 1003.2 working group. (For example, there are a number of changes in Draft 8 which will be incorporated as soon as Draft 8 is available). Additional modifications will be made based on user input, such as request for support of additional archive formats, etc. Patches and new releases will be made as new functionality is added or problems are diagnosed and fixed. Installation In order to install Pax, you must first edit the Makefile and the config.h file according to the directions in each of the files. These two files provide the configuration information for most commonly available machines. Please be sure to read through all the directions in each of these files before attempting to compile Pax. Portability Pax is intended to run on as many systems as possible. If you have problems getting Pax to compile or run on your system, please let me know so that the source or the installation procedure can be modified. Pax has been tested and appears to run correctly on the following machines: Machine Operating System/Release --------------------------------------------------- Altos 586 System III (2.3) AT&T UNIX PC System V.2 (Release 3.51) Convergent S/320 CTIX/68k 6.1, UNIX SysV 3.1 Cray 2 UNICOS Encore CC 02.00.r088 HP 9000 HP/UX 6.0.1 IBM PC/AT Microport SV/AT V2.4 Mac II A/UX 1.0 NCR Tower System V.2 Pyramid AT&T and Berkeley universe Sequent Symetry Dynix 3.0 SGI Iris 4D/60G UNIX 3.0 SGI Iris 4D/70G UNIX 3.0 SCO Xenix 386 2.3.2 SCO Unix 386 3.2 Sun 2 SunOS 3.4 Sun 2 SunOS 3.5 Sun 3 SunOS 3.4 Sun 3 SunOS 3.5 Sun 3 SunOS 4.0 Sun 4 SunOS 4.0 VAX 8750 BSD 4.3 (Mt. Xinu) VAX 8650 BSD 4.3 (Mt. Xinu) VAX 780 BSD 4.3 (Berkeley) --------------------------------------------------- In future releases, the source will be moving toward ANSI C and POSIX compatibility. This should allow for portability over any system supporting both ANSI and POSIX. In addition, POSIX/ANSI portability library routines will be developed which will allow the code to run on the standard machines available now. Credit Where Credit is Due Parts of the code which makes up Pax were gleaned from a number of different sources: the directory access routines in paxdir.h are modified copies of Doug Gwyn's dirent library; the regular expression matching routines in regexp.c are from Henry Spencer, some of the tar archive routines were initially written by John Gilmore for his PDTAR; and finally afio, written by Mark Brukhartz at Lachman Associates, was the basis for the buffering schemes used in pax. Licensing Copyright (c) 1989 Mark H. Colburn. All rights reserved. Redistribution and use in source and binary forms are permitted provided that the above copyright notice is duplicated in all such forms and that any documentation, advertising materials, and other materials related to such distribution and use acknowledge that the software was developed by Mark H. Colburn and sponsored by The USENIX Association. THE 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. Please report any bug or problems to: Mark Colburn Minnetech Consulting, Inc. 117 Mackubin St., Suite 1 St. Paul MN 55102 mark@jhereg.MN.ORG /sys/src/ape/cmd/pax/append.c 664 sys sys 1367613436 2365 /* $Source: /u/mark/src/pax/RCS/append.c,v $ * * $Revision: 1.2 $ * * append.c - append to a tape archive. * * DESCRIPTION * * Routines to allow appending of archives * * AUTHORS * * Mark H. Colburn, NAPS International (mark@jhereg.mn.org) * * * Sponsored by The USENIX Association for public distribution. * * Copyright (c) 1989 Mark H. Colburn. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that the above copyright notice is duplicated in all such * forms and that any documentation, advertising materials, and other * materials related to such distribution and use acknowledge that the * software was developed * by Mark H. Colburn and sponsored by The * USENIX Association. * * 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. * * $Log: append.c,v $ * Revision 1.2 89/02/12 10:03:58 mark * 1.2 release fixes * * Revision 1.1 88/12/23 18:02:00 mark * Initial revision * */ #ifndef lint static char *ident = "$Id: append.c,v 1.2 89/02/12 10:03:58 mark Exp $"; static char *copyright = "Copyright (c) 1989 Mark H. Colburn.\nAll rights reserved.\n"; #endif /* ! lint */ /* Headers */ #include "pax.h" /* append_archive - main loop for appending to a tar archive * * DESCRIPTION * * Append_archive reads an archive until the end of the archive is * reached once the archive is reached, the buffers are reset and the * create_archive function is called to handle the actual writing of * the appended archive data. This is quite similar to the * read_archive function, however, it does not do all the processing. */ #ifdef __STDC__ void append_archive(void) #else void append_archive() #endif { Stat sb; char name[PATH_MAX + 1]; name[0] = '\0'; while (get_header(name, &sb) == 0) { if (((ar_format == TAR) ? buf_skip(ROUNDUP((OFFSET) sb.sb_size, BLOCKSIZE)) : buf_skip((OFFSET) sb.sb_size)) < 0) { warn(name, "File data is corrupt"); } } /* we have now gotten to the end of the archive... */ /* reset the buffer now that we have read the entire archive */ bufend = bufidx = bufstart; create_archive(); } /sys/src/ape/cmd/pax/buffer.c 664 sys sys 1367613436 17429 /* $Source: /u/mark/src/pax/RCS/buffer.c,v $ * * $Revision: 1.2 $ * * buffer.c - Buffer management functions * * DESCRIPTION * * These functions handle buffer manipulations for the archiving * formats. Functions are provided to get memory for buffers, * flush buffers, read and write buffers and de-allocate buffers. * Several housekeeping functions are provided as well to get some * information about how full buffers are, etc. * * AUTHOR * * Mark H. Colburn, NAPS International (mark@jhereg.mn.org) * * Sponsored by The USENIX Association for public distribution. * * Copyright (c) 1989 Mark H. Colburn. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that the above copyright notice is duplicated in all such * forms and that any documentation, advertising materials, and other * materials related to such distribution and use acknowledge that the * software was developed * by Mark H. Colburn and sponsored by The * USENIX Association. * * 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. * * $Log: buffer.c,v $ * Revision 1.2 89/02/12 10:04:02 mark * 1.2 release fixes * * Revision 1.1 88/12/23 18:02:01 mark * Initial revision * */ #ifndef lint static char *ident = "$Id: buffer.c,v 1.2 89/02/12 10:04:02 mark Exp $"; static char *copyright = "Copyright (c) 1989 Mark H. Colburn.\nAll rights reserved.\n"; #endif /* ! lint */ /* Headers */ #include "pax.h" /* Function Prototypes */ #ifdef __STDC__ static int ar_write(int, char *, uint); static void buf_pad(OFFSET); static int indata(int, OFFSET, char *); static void outflush(void); static void buf_use(uint); static int buf_in_avail(char **, uint *); static uint buf_out_avail(char **); #else /* !__STDC__ */ static int ar_write(); static void buf_pad(); static int indata(); static void outflush(); static void buf_use(); static int buf_in_avail(); static uint buf_out_avail(); #endif /* __STDC__ */ /* inentry - install a single archive entry * * DESCRIPTION * * Inentry reads an archive entry from the archive file and writes it * out the the named file. If we are in PASS mode during archive * processing, the pass() function is called, otherwise we will * extract from the archive file. * * Inentry actaully calls indata to process the actual data to the * file. * * PARAMETERS * * char *name - name of the file to extract from the archive * Stat *asb - stat block of the file to be extracted from the * archive. * * RETURNS * * Returns zero if successful, -1 otherwise. */ #ifdef __STDC__ int inentry(char *name, Stat *asb) #else int inentry(name, asb) char *name; Stat *asb; #endif { Link *linkp; int ifd; int ofd; time_t tstamp[2]; if ((ofd = openout(name, asb, linkp = linkfrom(name, asb), 0)) > 0) { if (asb->sb_size || linkp == (Link *)NULL || linkp->l_size == 0) { close(indata(ofd, asb->sb_size, name)); } else if ((ifd = open(linkp->l_path->p_name, O_RDONLY)) < 0) { warn(linkp->l_path->p_name, strerror()); } else { passdata(linkp->l_path->p_name, ifd, name, ofd); close(ifd); close(ofd); } } else { return(buf_skip((OFFSET) asb->sb_size) >= 0); } tstamp[0] = (!f_pass && f_access_time) ? asb->sb_atime : time((time_t *) 0); tstamp[1] = f_mtime ? asb->sb_mtime : time((time_t *) 0); utime(name, tstamp); return (0); } /* outdata - write archive data * * DESCRIPTION * * Outdata transfers data from the named file to the archive buffer. * It knows about the file padding which is required by tar, but no * by cpio. Outdata continues after file read errors, padding with * null characters if neccessary. Closes the input file descriptor * when finished. * * PARAMETERS * * int fd - file descriptor of file to read data from * char *name - name of file * OFFSET size - size of the file * */ #ifdef __STDC__ void outdata(int fd, char *name, OFFSET size) #else void outdata(fd, name, size) int fd; char *name; OFFSET size; #endif { uint chunk; int got; int oops; uint avail; int pad; char *buf; oops = got = 0; if (pad = (size % BLOCKSIZE)) { pad = (BLOCKSIZE - pad); } while (size) { avail = buf_out_avail(&buf); size -= (chunk = size < avail ? (uint) size : avail); if (oops == 0 && (got = read(fd, buf, (unsigned int) chunk)) < 0) { oops = -1; warn(name, strerror()); got = 0; } if (got < chunk) { if (oops == 0) { oops = -1; } warn(name, "Early EOF"); while (got < chunk) { buf[got++] = '\0'; } } buf_use(chunk); } close(fd); if (ar_format == TAR) { buf_pad((OFFSET) pad); } } /* write_eot - write the end of archive record(s) * * DESCRIPTION * * Write out an End-Of-Tape record. We actually zero at least one * record, through the end of the block. Old tar writes garbage after * two zeroed records -- and PDtar used to. */ #ifdef __STDC__ void write_eot(void) #else void write_eot() #endif { OFFSET pad; char header[M_STRLEN + H_STRLEN + 1]; if (ar_format == TAR) { /* write out two zero blocks for trailer */ pad = 2 * BLOCKSIZE; } else { if (pad = (total + M_STRLEN + H_STRLEN + TRAILZ) % BLOCKSIZE) { pad = BLOCKSIZE - pad; } strcpy(header, M_ASCII); sprintf(header + M_STRLEN, H_PRINT, 0, 0, 0, 0, 0, 1, 0, (time_t) 0, TRAILZ, pad); outwrite(header, M_STRLEN + H_STRLEN); outwrite(TRAILER, TRAILZ); } buf_pad((OFFSET) pad); outflush(); } /* outwrite - write archive data * * DESCRIPTION * * Writes out data in the archive buffer to the archive file. The * buffer index and the total byte count are incremented by the number * of data bytes written. * * PARAMETERS * * char *idx - pointer to data to write * uint len - length of the data to write */ #ifdef __STDC__ void outwrite(char *idx, uint len) #else void outwrite(idx, len) char *idx; /* pointer to data to write */ uint len; /* length of data to write */ #endif { uint have; uint want; char *endx; endx = idx + len; while (want = endx - idx) { if (bufend - bufidx < 0) { fatal("Buffer overlow in out_write\n"); } if ((have = bufend - bufidx) == 0) { outflush(); } if (have > want) { have = want; } memcpy(bufidx, idx, (int) have); bufidx += have; idx += have; total += have; } } /* passdata - copy data to one file * * DESCRIPTION * * Copies a file from one place to another. Doesn't believe in input * file descriptor zero (see description of kludge in openin() comments). * Closes the provided output file descriptor. * * PARAMETERS * * char *from - input file name (old file) * int ifd - input file descriptor * char *to - output file name (new file) * int ofd - output file descriptor */ #ifdef __STDC__ void passdata(char *from, int ifd, char *to, int ofd) #else void passdata(from, ifd, to, ofd) char *from; int ifd; char *to; int ofd; #endif { int got; int sparse; char block[BUFSIZ]; if (ifd) { lseek(ifd, (OFFSET) 0, 0); sparse = 0; while ((got = read(ifd, block, sizeof(block))) > 0 && (sparse = ar_write(ofd, block, (uint) got)) >= 0) { total += got; } if (got) { warn(got < 0 ? from : to, strerror()); } else if (sparse > 0 && (lseek(ofd, (OFFSET)(-sparse), 1) < 0 || write(ofd, block, (uint) sparse) != sparse)) { warn(to, strerror()); } } close(ofd); } /* buf_allocate - get space for the I/O buffer * * DESCRIPTION * * buf_allocate allocates an I/O buffer using malloc. The resulting * buffer is used for all data buffering throughout the program. * Buf_allocate must be called prior to any use of the buffer or any * of the buffering calls. * * PARAMETERS * * int size - size of the I/O buffer to request * * ERRORS * * If an invalid size is given for a buffer or if a buffer of the * required size cannot be allocated, then the function prints out an * error message and returns a non-zero exit status to the calling * process, terminating the program. * */ #ifdef __STDC__ void buf_allocate(OFFSET size) #else void buf_allocate(size) OFFSET size; #endif { if (size <= 0) { fatal("invalid value for blocksize"); } if ((bufstart = malloc((unsigned) size)) == (char *)NULL) { fatal("Cannot allocate I/O buffer"); } bufend = bufidx = bufstart; bufend += size; } /* buf_skip - skip input archive data * * DESCRIPTION * * Buf_skip skips past archive data. It is used when the length of * the archive data is known, and we do not wish to process the data. * * PARAMETERS * * OFFSET len - number of bytes to skip * * RETURNS * * Returns zero under normal circumstances, -1 if unreadable data is * encountered. */ #ifdef __STDC__ int buf_skip(OFFSET len) #else int buf_skip(len) OFFSET len; #endif { uint chunk; int corrupt = 0; while (len) { if (bufend - bufidx < 0) { fatal("Buffer overlow in buf_skip\n"); } while ((chunk = bufend - bufidx) == 0) { corrupt |= ar_read(); } if (chunk > len) { chunk = len; } bufidx += chunk; len -= chunk; total += chunk; } return (corrupt); } /* buf_read - read a given number of characters from the input archive * * DESCRIPTION * * Reads len number of characters from the input archive and * stores them in the buffer pointed at by dst. * * PARAMETERS * * char *dst - pointer to buffer to store data into * uint len - length of data to read * * RETURNS * * Returns zero with valid data, -1 if unreadable portions were * replaced by null characters. */ #ifdef __STDC__ int buf_read(char *dst, uint len) #else int buf_read(dst, len) char *dst; uint len; #endif { int have; int want; int corrupt = 0; char *endx = dst + len; while (want = endx - dst) { if (bufend - bufidx < 0) { fatal("Buffer overlow in buf_read\n"); } while ((have = bufend - bufidx) == 0) { have = 0; corrupt |= ar_read(); } if (have > want) { have = want; } memcpy(dst, bufidx, have); bufidx += have; dst += have; total += have; } return (corrupt); } /* indata - install data from an archive * * DESCRIPTION * * Indata writes size bytes of data from the archive buffer to the output * file specified by fd. The filename which is being written, pointed * to by name is provided only for diagnostics. * * PARAMETERS * * int fd - output file descriptor * OFFSET size - number of bytes to write to output file * char *name - name of file which corresponds to fd * * RETURNS * * Returns given file descriptor. */ #ifdef __STDC__ static int indata(int fd, OFFSET size, char *name) #else static int indata(fd, size, name) int fd; OFFSET size; char *name; #endif { uint chunk; char *oops; int sparse; int corrupt; char *buf; uint avail; corrupt = sparse = 0; oops = (char *)NULL; while (size) { corrupt |= buf_in_avail(&buf, &avail); size -= (chunk = size < avail ? (uint) size : avail); if (oops == (char *)NULL && (sparse = ar_write(fd, buf, chunk)) < 0) { oops = strerror(); } buf_use(chunk); } if (corrupt) { warn(name, "Corrupt archive data"); } if (oops) { warn(name, oops); } else if (sparse > 0 && (lseek(fd, (OFFSET) - 1, 1) < 0 || write(fd, "", 1) != 1)) { warn(name, strerror()); } return (fd); } /* outflush - flush the output buffer * * DESCRIPTION * * The output buffer is written, if there is anything in it, to the * archive file. */ #ifdef __STDC__ static void outflush(void) #else static void outflush() #endif { char *buf; int got; uint len; /* if (bufidx - buf > 0) */ for (buf = bufstart; len = bufidx - buf;) { if ((got = write(archivefd, buf, MIN(len, blocksize))) > 0) { buf += got; } else if (got < 0) { next(AR_WRITE); } } bufend = (bufidx = bufstart) + blocksize; } /* ar_read - fill the archive buffer * * DESCRIPTION * * Remembers mid-buffer read failures and reports them the next time * through. Replaces unreadable data with null characters. Resets * the buffer pointers as appropriate. * * RETURNS * * Returns zero with valid data, -1 otherwise. */ #ifdef __STDC__ int ar_read(void) #else int ar_read() #endif { int got; static int failed; bufend = bufidx = bufstart; if (!failed) { if (areof) { if (total == 0) { fatal("No input"); } else { next(AR_READ); } } while (!failed && !areof && bufstart + blocksize - bufend >= blocksize) { if ((got = read(archivefd, bufend, (unsigned int) blocksize)) > 0) { bufend += got; } else if (got < 0) { failed = -1; warnarch(strerror(), (OFFSET) 0 - (bufend - bufidx)); } else { ++areof; } } } if (failed && bufend == bufstart) { failed = 0; for (got = 0; got < blocksize; ++got) { *bufend++ = '\0'; } return (-1); } return (0); } /* ar_write - write a filesystem block * * DESCRIPTION * * Writes len bytes of data data from the specified buffer to the * specified file. Seeks past sparse blocks. * * PARAMETERS * * int fd - file to write to * char *buf - buffer to get data from * uint len - number of bytes to transfer * * RETURNS * * Returns 0 if the block was written, the given length for a sparse * block or -1 if unsuccessful. */ #ifdef __STDC__ static int ar_write(int fd, char *buf, uint len) #else static int ar_write(fd, buf, len) int fd; char *buf; uint len; #endif { char *bidx; char *bend; bend = (bidx = buf) + len; while (bidx < bend) { if (*bidx++) { return (write(fd, buf, len) == len ? 0 : -1); } } return (lseek(fd, (OFFSET) len, 1) < 0 ? -1 : len); } /* buf_pad - pad the archive buffer * * DESCRIPTION * * Buf_pad writes len zero bytes to the archive buffer in order to * pad it. * * PARAMETERS * * OFFSET pad - number of zero bytes to pad * */ #ifdef __STDC__ static void buf_pad(OFFSET pad) #else static void buf_pad(pad) OFFSET pad; #endif { int idx; int have; while (pad) { if ((have = bufend - bufidx) > pad) { have = pad; } for (idx = 0; idx < have; ++idx) { *bufidx++ = '\0'; } total += have; pad -= have; if (bufend - bufidx == 0) { outflush(); } } } /* buf_use - allocate buffer space * * DESCRIPTION * * Buf_use marks space in the buffer as being used; advancing both the * buffer index (bufidx) and the total byte count (total). * * PARAMETERS * * uint len - Amount of space to allocate in the buffer */ #ifdef __STDC__ static void buf_use(uint len) #else static void buf_use(len) uint len; #endif { bufidx += len; total += len; } /* buf_in_avail - index available input data within the buffer * * DESCRIPTION * * Buf_in_avail fills the archive buffer, and points the bufp * parameter at the start of the data. The lenp parameter is * modified to contain the number of bytes which were read. * * PARAMETERS * * char **bufp - pointer to the buffer to read data into * uint *lenp - pointer to the number of bytes which were read * (returned to the caller) * * RETURNS * * Stores a pointer to the data and its length in given locations. * Returns zero with valid data, -1 if unreadable portions were * replaced with nulls. * * ERRORS * * If an error occurs in ar_read, the error code is returned to the * calling function. * */ #ifdef __STDC__ static int buf_in_avail(char **bufp, uint *lenp) #else static int buf_in_avail(bufp, lenp) char **bufp; uint *lenp; #endif { uint have; int corrupt = 0; while ((have = bufend - bufidx) == 0) { corrupt |= ar_read(); } *bufp = bufidx; *lenp = have; return (corrupt); } /* buf_out_avail - index buffer space for archive output * * DESCRIPTION * * Stores a buffer pointer at a given location. Returns the number * of bytes available. * * PARAMETERS * * char **bufp - pointer to the buffer which is to be stored * * RETURNS * * The number of bytes which are available in the buffer. * */ #ifdef __STDC__ static uint buf_out_avail(char **bufp) #else static uint buf_out_avail(bufp) char **bufp; #endif { int have; if (bufend - bufidx < 0) { fatal("Buffer overlow in buf_out_avail\n"); } if ((have = bufend - bufidx) == 0) { outflush(); } *bufp = bufidx; return (have); } /sys/src/ape/cmd/pax/config.h 664 sys sys 1367613436 5367 /* $Source: /u/mark/src/pax/RCS/config.h,v $ * * $Revision: 1.2 $ * * config.h - configuration options for PAX * * DESCRIPTION * * This file contains a number of configurable parameters for the * PAX software. This files should be edited prior to makeing the * package. * * AUTHOR * * Mark H. Colburn, NAPS International (mark@jhereg.mn.org) * * Sponsored by The USENIX Association for public distribution. * * Copyright (c) 1989 Mark H. Colburn. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that the above copyright notice and this paragraph are * duplicated in all such forms and that any documentation, * advertising materials, and other materials related to such * distribution and use acknowledge that the software was developed * by Mark H. Colburn and sponsored by The USENIX Association. * * 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. */ #ifndef _PAX_CONFIG_H #define _PAX_CONFIG_H /* Defines */ /* XENIX_286 (SCO ugh, Xenix system V(?) 286, USG with changes... * You will get a warning about DIRSIZ being redefined, ignore it, * complain to SCO about include files that are messed up or send * mail to doug@lentni.UUCP, who can provide some patches to fix * your include files. * * Defining XENIX_286 will automatically define USG. * */ /* #define XENIX_286 /* Running on a XENIX 286 system */ /* * USG - USG (Unix System V) specific modifications * * Define USG if you are running Unix System V or some similar variant */ #define USG /* Running on a USG System */ /* * BSD - BSD (Berkely) specific modifications * * Define BSD if you are running some version of BSD Unix */ /* #define BSD /* Running on a BSD System */ /* * DEF_AR_FILE - tar only (required) * * DEF_AR_FILE should contain the full pathname of your favorite archive * device. Normally this would be a tape drive, but it may be a disk drive * on those systems that don't have tape drives. */ #define DEF_AR_FILE "-" /* The default archive on your system */ /* * TTY - device which interactive queries should be directed to (required) * * This is the device to which interactive queries will be sent to and * received from. On most unix systems, this should be /dev/tty, however, on * some systems, such as MS-DOS, it my need to be different (e.g. "con:"). */ /* #define TTY "/dev/tty" /* for most versions of UNIX */ /* #define TTY "con:" /* For MS-DOS */ #define TTY "/dev/cons" /* for Plan 9 */ /* * PAXDIR - if you do not have directory access routines * * Define PAXDIR if you do not have Doug Gwyn's dirent package installed * as a system library or you wish to use the version supplied with PAX. * * NOTE: DO NOT DEFINE THIS IF YOU HAVE BERKELEY DIRECTORY ACCESS ROUTINES. */ /* #define PAXDIR /* use paxdir.h paxdir.c */ /* * DIRENT - directory access routines (required) * * If you have Doug Gwyn's dirent package installed, either as a system * library, or are using the paxdir.c and paxdir.h routines which come with * PAX, then define dirent. * * NOTE: DO NOT DEFINE THIS IF YOU HAVE BERKELEY DIRECTORY ACCESS ROUTINES. */ #define DIRENT /* use POSIX compatible directory routines */ /* * OFFSET - compiler dependent offset type * * OFFSET is the type which is returned by lseek(). It is different on * some systems. Most define it to be off_t, but some define it to be long. */ #define OFFSET off_t /* for most BSD, USG and other systems */ /* #define OFFSET long /* for most of the rest of them... */ /* * VOID - compiler support for VOID types * * If your system does not support void, then this should be defined to * int, otherwise, it should be left undefined. * * For ANSI Systems this should always be blank. */ #ifndef __STDC__ /* #define void int /* for system which do support void */ #endif /* * SIG_T - return type for the signal routine * * Some systems have signal defines to return an int *, other return a * void *. Please choose the correct value for your system. */ #define SIG_T void /* signal defined as "void (*signal)()" */ /* #define SIG_T int /* signal defined as "int (*signal)()" */ /* * STRCSPN - use the strcspn function included with pax * * Some systems do not have the strcspn() function in their C libraries. * For those system define STRCSPN and the one provided in regexp.c will * be used. */ /* #define STRCSPN /* implementation does not have strcspn() */ /* * STRERROR - use the strerror function included with pax * * Non-Ansi systems do not have the strerror() function in their C libraries. * For those system define STRERROR and the one provided in misc.c will * be used instead. */ /* #define STRERROR /* implementation does not have strerror() */ /* /* * END OF CONFIGURATION SECTION * * Nothing beyond this point should need to be changed */ #ifdef BSD #ifdef USG #include "You must first edit config.h and Makefile to configure pax." #endif #endif /* * Do a little sanity checking */ #ifdef PAXDIR # ifndef DIRENT # define DIRENT # endif #endif #ifdef XENIX_286 # define USG #endif /* XENIX_286 */ #endif /* _PAX_CONFIG_H */ #ifndef __STDC__ #define __STDC__ #endif /sys/src/ape/cmd/pax/cpio.1 664 sys sys 1367613436 6231 .\" $Id: cpio.1,v 1.2 89/02/12 10:08:42 mark Exp $ .TH CPIO 1 "USENIX Association" "" .SH NAME cpio \- copy file archives in and out .SH SYNOPSIS .B cpio .BR \-o [ Bacv ] .br .B cpio .BR \-i [ Bcdfmrtuv ] .RI [ pattern... ] .br .B cpio .BR \-p [ adlmruv ] .I directory .SH DESCRIPTION The .B cpio utility produces and reads files in the format specified by the .B cpio .B "Archive/Interchange File Format" specified in .IR "IEEE Std. 1003.1-1988" . .PP The .B "cpio -i" (copy in) utility extracts files from the standard input, which is assumed to be the product of a previous .B "cpio -o" . Only files with names that match .I patterns are selected. Multiple .I patterns may be specified and if no .I patterns are specified, the default for .I patterns is \*, selecting all files. The extracted files are conditionally created and copied into the current directory, and possibly any levels below, based upon the options described below and the permissions of the files will be those of the previous .B "cpio -o" . The owner and group of the files will be that of the current user unless the user has appropriate privileges, which causes .B cpio to retains the owner and group of the files of the previous .B "cpio -o" . .PP The .B "cpio -p" (pass) utility reads the standard input to obtain a list of path names of files that are conditionally created and copied into the destination .I directory based upon the options described below. .PP If an error is detected, the cause is reported and the .B cpio utility will continue to copy other files. .B cpio will skip over any unrecognized files which it encounters in the archive. .PP The following restrictions apply to the .B cpio utility: .IP 1 .25i Pathnames are restricted to 256 characters. .IP 2 .25i Appropriate privileges are required to copy special files. .IP 3 .25i Blocks are reported in 512-byte quantities. .SS Options The following options are available: .TP .5i .B \-B Input/output is to be blocked 5120 bytes to the record. Can only be used with .B "cpio -o" or .B "cpio -i" for data that is directed to or from character special files. .TP .5i .B \-a Reset access times of input files after they have been copied. When the .B \-l option is also specified, the linked files do not have their access times reset. Can only be used with .B "cpio -o" or .B "cpio -i" . .TP .5i .B \-c Write header information in ASCII character for for portability. Can only be used with .B "cpio -i" or .B "cpio -o" . Note that this option should always be used to write portable files. .TP .5i .B \-d Creates directories as needed. Can only be used with .B "cpio -i" or .B "cpio -p" . .TP .5i .B \-f Copy in all files except those in .I patterns . Can only be used with .B "cpio -i" . .TP .5i .B \-l Whenever possible, link files rather than copying them. Can only be used with .B "cpio -p" . .TP .5i .B \-m Retain previous modification times. This option is ineffective on directories that are being copied. Can only be used with .B "cpio -i" or .B "cpio -p" . .TP .5i .B \-r Interactively rename files. The user is asked whether to rename .I pattern each invocation. Read and write permissions for .B "/dev/tty" are required for this option. If the user types a null line, the file is skipped. Should only be used with .B "cpio -i" or .B "cpio -o" . .TP .5i .B \-t Print a table of contents of the input. No files are created. Can only be used with .B "cpio -i" . .TP .5i .B \-u Copy files unconditionally; usually an older file will not replace a new file with the same name. Can only be used with .B "cpio -i" or .B "cpio -p" . .TP .5i .B \-v Verbose: cause the names of the affected files to be printed. Can only be used with .B "cpio -i" . Provides a detailed listing when used with the .B \-t option. .SS Operands The following operands are available: .TP 1i .I patterns Simple regular expressions given in the name-generating notation of the shell. .TP 1i .I directory The destination directory. .SS "Exit Status" The .B cpio utility exits with one of the following values: .TP .5i 0 All input files were copied. .TP .5i 2 The utility encountered errors in copying or accessing files or directories. An error will be reported for nonexistent files or directories, or permissions that do not allow the user to access the source or target files. .SS It is important to use the .B "-depth" option of the .B find utility to generate pathnames for .B cpio . This eliminates problems .B cpio could have trying to create files under read-only directories. .PP The following command: .RS ls | cpio -o > ../newfile .RE copies out the files listed by the .B ls utility and redirects them to the file .B newfile . .PP The following command: .RS cat newfile | cpio -id "memo/al" "memo/b*" .RE uses the output file .B newfile from the .B "cpio -o" utility, takes those files that match the patterns .B "memo/al" and .B "memo/b*" , creates the directories below the current directory, and places the files in the appropriate directories. .PP The command .RS find . -depth -print | cpio -pdlmv newdir .RE takes the file names piped to it from the .B find utility and copies or links those files to another directory named .B newdir , while retaining the modification time. .SH FILES .TP 1i /dev/tty used to prompt the user for information when the .B \-i or .B \-r options are specified. .SH "SEE ALSO" find(1), pax(1), tar(1), cpio(5), tar(5) .SH COPYRIGHT Copyright (c) 1989 Mark H. Colburn. .br All rights reserved. .PP Redistribution and use in source and binary forms are permitted provided that the above copyright notice is duplicated in all such forms and that any documentation, advertising materials, and other materials related to such distribution and use acknowledge that the software was developed by Mark H. Colburn and sponsored by The USENIX Association. .PP THE 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. .SH AUTHOR Mark H. Colburn .br NAPS International .br 117 Mackubin Street, Suite 1 .br St. Paul, MN 55102 .br mark@jhereg.MN.ORG .sp 2 Sponsored by .B "The USENIX Association" for public distribution. /sys/src/ape/cmd/pax/cpio.c 664 sys sys 1367613436 4543 /* $Source: /u/mark/src/pax/RCS/cpio.c,v $ * * $Revision: 1.2 $ * * cpio.c - Cpio specific functions for archive handling * * DESCRIPTION * * These function provide a cpio conformant interface to the pax * program. * * AUTHOR * * Mark H. Colburn, NAPS International (mark@jhereg.mn.org) * * Sponsored by The USENIX Association for public distribution. * * Copyright (c) 1989 Mark H. Colburn. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that the above copyright notice is duplicated in all such * forms and that any documentation, advertising materials, and other * materials related to such distribution and use acknowledge that the * software was developed * by Mark H. Colburn and sponsored by The * USENIX Association. * * 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. * * $Log: cpio.c,v $ * Revision 1.2 89/02/12 10:04:13 mark * 1.2 release fixes * * Revision 1.1 88/12/23 18:02:05 mark * Initial revision * */ #ifndef lint static char *ident = "$Id: cpio.c,v 1.2 89/02/12 10:04:13 mark Exp $"; static char *copyright = "Copyright (c) 1989 Mark H. Colburn.\nAll rights reserved.\n"; #endif /* ! lint */ /* Headers */ #include "pax.h" /* Function Prototypes */ #ifdef __STDC__ static void usage(void); #else /* !__STDC__ */ static void usage(); #endif /* __STDC__ */ /* do_cpio - handle cpio format archives * * DESCRIPTION * * Do_cpio provides a standard CPIO interface to the PAX program. All * of the standard cpio flags are available, and the behavior of the * program mimics traditonal cpio. * * PARAMETERS * * int argc - command line argument count * char **argv - pointer to command line arguments * * RETURNS * * Nothing. */ #ifdef __STDC__ int do_cpio(int argc, char **argv) #else int do_cpio(argc, argv) int argc; char **argv; #endif { int c; char *dirname; Stat st; /* default input/output file for CPIO is STDIN/STDOUT */ ar_file = "-"; names_from_stdin = 1; /* set up the flags to reflect the default CPIO inteface. */ blocksize = BLOCKSIZE; ar_interface = CPIO; ar_format = CPIO; msgfile=stderr; while ((c = getopt(argc, argv, "D:Bacdfilmoprtuv")) != EOF) { switch (c) { case 'i': f_extract = 1; break; case 'o': f_create = 1; break; case 'p': f_pass = 1; dirname = argv[--argc]; /* check to make sure that the argument is a directory */ if (LSTAT(dirname, &st) < 0) { fatal(strerror()); } if ((st.sb_mode & S_IFMT) != S_IFDIR) { fatal("Not a directory"); } break; case 'B': blocksize = BLOCK; break; case 'a': f_access_time = 1; break; case 'c': break; case 'D': ar_file = optarg; break; case 'd': f_dir_create = 1; break; case 'f': f_reverse_match = 1; break; case 'l': f_link = 1; break; case 'm': f_mtime = 1; break; case 'r': f_interactive = 1; break; case 't': f_list = 1; break; case 'u': f_unconditional = 1; break; case 'v': f_verbose = 1; break; default: usage(); } } if (f_create + f_pass + f_extract != 1) { usage(); } if (!f_pass) { buf_allocate((OFFSET) blocksize); } if (f_extract) { open_archive(AR_READ); /* Open for reading */ read_archive(); } else if (f_create) { open_archive(AR_WRITE); create_archive(); } else if (f_pass) { pass(dirname); } /* print out the total block count transfered */ fprintf(stderr, "%ld Blocks\n", ROUNDUP(total, BLOCKSIZE) / BLOCKSIZE); exit(0); /* NOTREACHED */ } /* usage - print a helpful message and exit * * DESCRIPTION * * Usage prints out the usage message for the CPIO interface and then * exits with a non-zero termination status. This is used when a user * has provided non-existant or incompatible command line arguments. * * RETURNS * * Returns an exit status of 1 to the parent process. * */ #ifdef __STDC__ static void usage(void) #else static void usage() #endif { fprintf(stderr, "Usage: %s -o[Bacv]\n", myname); fprintf(stderr, " %s -i[Bcdmrtuvf] [pattern...]\n", myname); fprintf(stderr, " %s -p[adlmruv] directory\n", myname); exit(1); } /sys/src/ape/cmd/pax/create.c 664 sys sys 1367613436 8079 /* $Source: /u/mark/src/pax/RCS/create.c,v $ * * $Revision: 1.3 $ * * create.c - Create a tape archive. * * DESCRIPTION * * These functions are used to create/write and archive from an set of * named files. * * AUTHOR * * Mark H. Colburn, NAPS International (mark@jhereg.mn.org) * * Sponsored by The USENIX Association for public distribution. * * Copyright (c) 1989 Mark H. Colburn. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that the above copyright notice is duplicated in all such * forms and that any documentation, advertising materials, and other * materials related to such distribution and use acknowledge that the * software was developed * by Mark H. Colburn and sponsored by The * USENIX Association. * * 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. * * $Log: create.c,v $ * Revision 1.3 89/02/12 10:29:37 mark * Fixed misspelling of Replstr * * Revision 1.2 89/02/12 10:04:17 mark * 1.2 release fixes * * Revision 1.1 88/12/23 18:02:06 mark * Initial revision * */ #ifndef lint static char *ident = "$Id: create.c,v 1.3 89/02/12 10:29:37 mark Exp Locker: mark $"; static char *copyright = "Copyright (c) 1989 Mark H. Colburn.\nAll rights reserved.\n"; #endif /* ! lint */ /* Headers */ #include "pax.h" /* Function Prototypes */ #ifdef __STDC__ static void writetar(char *, Stat *); static void writecpio(char *, Stat *); static char tartype(int); #else /* !__STDC__ */ static void writetar(); static void writecpio(); static char tartype(); #endif /* __STDC__ */ /* create_archive - create a tar archive. * * DESCRIPTION * * Create_archive is used as an entry point to both create and append * archives. Create archive goes through the files specified by the * user and writes each one to the archive if it can. Create_archive * knows how to write both cpio and tar headers and the padding which * is needed for each type of archive. * * RETURNS * * Always returns 0 */ #ifdef __STDC__ int create_archive(void) #else int create_archive() #endif { char name[PATH_MAX + 1]; Stat sb; int fd; while (name_next(name, &sb) != -1) { if ((fd = openin(name, &sb)) < 0) { /* FIXME: pax wants to exit here??? */ continue; } if (rplhead != (Replstr *)NULL) { rpl_name(name); if (strlen(name) == 0) { continue; } } if (get_disposition("add", name) || get_newname(name, sizeof(name))) { /* skip file... */ if (fd) { close(fd); } continue; } if (!f_link && sb.sb_nlink > 1) { if (islink(name, &sb)) { sb.sb_size = 0; } linkto(name, &sb); } if (ar_format == TAR) { writetar(name, &sb); } else { writecpio(name, &sb); } if (fd) { outdata(fd, name, sb.sb_size); } if (f_verbose) { print_entry(name, &sb); } } write_eot(); close_archive(); return (0); } /* writetar - write a header block for a tar file * * DESCRIPTION * * Make a header block for the file name whose stat info is in st. * Return header pointer for success, NULL if the name is too long. * * The tar header block is structured as follows: * * FIELD NAME OFFSET SIZE * -------------|---------------|------ * name 0 100 * mode 100 8 * uid 108 8 * gid 116 8 * size 124 12 * mtime 136 12 * chksum 148 8 * typeflag 156 1 * linkname 157 100 * magic 257 6 * version 263 2 * uname 265 32 * gname 297 32 * devmajor 329 8 * devminor 337 8 * prefix 345 155 * * PARAMETERS * * char *name - name of file to create a header block for * Stat *asb - pointer to the stat structure for the named file * */ #ifdef __STDC__ static void writetar(char *name, Stat *asb) #else static void writetar(name, asb) char *name; Stat *asb; #endif { char *p; char *prefix = (char *)NULL; int i; int sum; char hdr[BLOCKSIZE]; Link *from; memset(hdr, 0, BLOCKSIZE); if (strlen(name) > 255) { warn(name, "name too long"); return; } /* * If the pathname is longer than TNAMLEN, but less than 255, then * we can split it up into the prefix and the filename. */ if (strlen(name) > 100) { prefix = name; name += 155; while (name > prefix && *name != '/') { name--; } /* no slash found....hmmm.... */ if (name == prefix) { warn(prefix, "Name too long"); return; } *name++ = '\0'; } #ifdef S_IFLNK if ((asb->sb_mode & S_IFMT) == S_IFLNK) { strcpy(&hdr[157], asb->sb_link); asb->sb_size = 0; } #endif strcpy(hdr, name); sprintf(&hdr[100], "%06o \0", asb->sb_mode & ~S_IFMT); sprintf(&hdr[108], "%06o \0", asb->sb_uid); sprintf(&hdr[116], "%06o \0", asb->sb_gid); sprintf(&hdr[124], "%011lo ", (long) asb->sb_size); sprintf(&hdr[136], "%011lo ", (long) asb->sb_mtime); strncpy(&hdr[148], " ", 8); hdr[156] = tartype(asb->sb_mode); if (asb->sb_nlink > 1 && (from = linkfrom(name, asb)) != (Link *)NULL) { strcpy(&hdr[157], from->l_name); hdr[156] = LNKTYPE; } strcpy(&hdr[257], TMAGIC); strncpy(&hdr[263], TVERSION, 2); strcpy(&hdr[265], finduname((int) asb->sb_uid)); strcpy(&hdr[297], findgname((int) asb->sb_gid)); sprintf(&hdr[329], "%06o \0", major(asb->sb_rdev)); sprintf(&hdr[337], "%06o \0", minor(asb->sb_rdev)); if (prefix != (char *)NULL) { strncpy(&hdr[345], prefix, 155); } /* Calculate the checksum */ sum = 0; p = hdr; for (i = 0; i < 500; i++) { sum += 0xFF & *p++; } /* Fill in the checksum field. */ sprintf(&hdr[148], "%06o \0", sum); outwrite(hdr, BLOCKSIZE); } /* tartype - return tar file type from file mode * * DESCRIPTION * * tartype returns the character which represents the type of file * indicated by "mode". * * PARAMETERS * * int mode - file mode from a stat block * * RETURNS * * The character which represents the particular file type in the * ustar standard headers. */ #ifdef __STDC__ static char tartype(int mode) #else static char tartype(mode) int mode; #endif { switch (mode & S_IFMT) { #ifdef S_IFCTG case S_IFCTG: return(CONTTYPE); #endif case S_IFDIR: return (DIRTYPE); #ifdef S_IFLNK case S_IFLNK: return (SYMTYPE); #endif #ifdef S_IFFIFO case S_IFIFO: return (FIFOTYPE); #endif #ifdef S_IFCHR case S_IFCHR: return (CHRTYPE); #endif #ifdef S_IFBLK case S_IFBLK: return (BLKTYPE); #endif default: return (REGTYPE); } } /* writecpio - write a cpio archive header * * DESCRIPTION * * Writes a new CPIO style archive header for the file specified. * * PARAMETERS * * char *name - name of file to create a header block for * Stat *asb - pointer to the stat structure for the named file */ #ifdef __STDC__ static void writecpio(char *name, Stat *asb) #else static void writecpio(name, asb) char *name; Stat *asb; #endif { uint namelen; char header[M_STRLEN + H_STRLEN + 1]; namelen = (uint) strlen(name) + 1; strcpy(header, M_ASCII); sprintf(header + M_STRLEN, "%06o%06o%06o%06o%06o", USH(asb->sb_dev), USH(asb->sb_ino), USH(asb->sb_mode), USH(asb->sb_uid), USH(asb->sb_gid)); sprintf(header + M_STRLEN + 30, "%06o%06o%011lo%06o%011lo", #ifdef _POSIX_SOURCE USH(asb->sb_nlink), USH(0), #else USH(asb->sb_nlink), USH(asb->sb_rdev), #endif f_mtime ? asb->sb_mtime : time((time_t *) 0), namelen, asb->sb_size); outwrite(header, M_STRLEN + H_STRLEN); outwrite(name, namelen); #ifdef S_IFLNK if ((asb->sb_mode & S_IFMT) == S_IFLNK) { outwrite(asb->sb_link, (uint) asb->sb_size); } #endif /* S_IFLNK */ } /sys/src/ape/cmd/pax/extract.c 664 sys sys 1367613436 14691 /* $Source: /u/mark/src/pax/RCS/extract.c,v $ * * $Revision: 1.3 $ * * extract.c - Extract files from a tar archive. * * DESCRIPTION * * AUTHOR * * Mark H. Colburn, NAPS International (mark@jhereg.mn.org) * * Sponsored by The USENIX Association for public distribution. * * Copyright (c) 1989 Mark H. Colburn. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that the above copyright notice is duplicated in all such * forms and that any documentation, advertising materials, and other * materials related to such distribution and use acknowledge that the * software was developed * by Mark H. Colburn and sponsored by The * USENIX Association. * * 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. * * $Log: extract.c,v $ * Revision 1.3 89/02/12 10:29:43 mark * Fixed misspelling of Replstr * * Revision 1.2 89/02/12 10:04:24 mark * 1.2 release fixes * * Revision 1.1 88/12/23 18:02:07 mark * Initial revision * */ #ifndef lint static char *ident = "$Id: extract.c,v 1.3 89/02/12 10:29:43 mark Exp Locker: mark $"; static char *copyright = "Copyright (c) 1989 Mark H. Colburn.\nAll rights reserved.\n"; #endif /* ! lint */ /* Headers */ #include "pax.h" /* Defines */ /* * Swap bytes. */ #define SWAB(n) ((((ushort)(n) >> 8) & 0xff) | (((ushort)(n) << 8) & 0xff00)) /* Function Prototypes */ #ifdef __STDC__ static int inbinary(char *, char *, Stat *); static int inascii(char *, char *, Stat *); static int inswab(char *, char *, Stat *); static int readtar(char *, Stat *); static int readcpio(char *, Stat *); #else /* !__STDC__ */ static int inbinary(); static int inascii(); static int inswab(); static int readtar(); static int readcpio(); #endif /* __STDC__ */ /* read_archive - read in an archive * * DESCRIPTION * * Read_archive is the central entry point for reading archives. * Read_archive determines the proper archive functions to call * based upon the archive type being processed. * * RETURNS * */ #ifdef __STDC__ int read_archive(void) #else int read_archive() #endif { Stat sb; char name[PATH_MAX + 1]; int match; int pad; name_gather(); /* get names from command line */ name[0] = '\0'; while (get_header(name, &sb) == 0) { match = name_match(name) ^ f_reverse_match; if (f_list) { /* only wanted a table of contents */ if (match) { print_entry(name, &sb); } if (((ar_format == TAR) ? buf_skip(ROUNDUP((OFFSET) sb.sb_size, BLOCKSIZE)) : buf_skip((OFFSET) sb.sb_size)) < 0) { warn(name, "File data is corrupt"); } } else if (match) { if (rplhead != (Replstr *)NULL) { rpl_name(name); if (strlen(name) == 0) { continue; } } if (get_disposition("extract", name) || get_newname(name, sizeof(name))) { /* skip file... */ if (((ar_format == TAR) ? buf_skip(ROUNDUP((OFFSET) sb.sb_size, BLOCKSIZE)) : buf_skip((OFFSET) sb.sb_size)) < 0) { warn(name, "File data is corrupt"); } continue; } if (inentry(name, &sb) < 0) { warn(name, "File data is corrupt"); } if (f_verbose) { print_entry(name, &sb); } if (ar_format == TAR && sb.sb_nlink > 1) { /* * This kludge makes sure that the link table is cleared * before attempting to process any other links. */ if (sb.sb_nlink > 1) { linkfrom(name, &sb); } } if (ar_format == TAR && (pad = sb.sb_size % BLOCKSIZE) != 0) { pad = BLOCKSIZE - pad; buf_skip((OFFSET) pad); } } else { if (((ar_format == TAR) ? buf_skip(ROUNDUP((OFFSET) sb.sb_size, BLOCKSIZE)) : buf_skip((OFFSET) sb.sb_size)) < 0) { warn(name, "File data is corrupt"); } } } close_archive(); } /* get_header - figures which type of header needs to be read. * * DESCRIPTION * * This is merely a single entry point for the two types of archive * headers which are supported. The correct header is selected * depending on the archive type. * * PARAMETERS * * char *name - name of the file (passed to header routine) * Stat *asb - Stat block for the file (passed to header routine) * * RETURNS * * Returns the value which was returned by the proper header * function. */ #ifdef __STDC__ int get_header(char *name, Stat *asb) #else int get_header(name, asb) char *name; Stat *asb; #endif { if (ar_format == TAR) { return(readtar(name, asb)); } else { return(readcpio(name, asb)); } } /* readtar - read a tar header * * DESCRIPTION * * Tar_head read a tar format header from the archive. The name * and asb parameters are modified as appropriate for the file listed * in the header. Name is assumed to be a pointer to an array of * at least PATH_MAX bytes. * * PARAMETERS * * char *name - name of the file for which the header is * for. This is modified and passed back to * the caller. * Stat *asb - Stat block for the file for which the header * is for. The fields of the stat structure are * extracted from the archive header. This is * also passed back to the caller. * * RETURNS * * Returns 0 if a valid header was found, or -1 if EOF is * encountered. */ #ifdef __STDC__ static int readtar(char *name, Stat *asb) #else static int readtar(name, asb) char *name; Stat *asb; #endif { int status = 3; /* Initial status at start of archive */ static int prev_status; for (;;) { prev_status = status; status = read_header(name, asb); switch (status) { case 1: /* Valid header */ return(0); case 0: /* Invalid header */ switch (prev_status) { case 3: /* Error on first record */ warn(ar_file, "This doesn't look like a tar archive"); /* FALLTHRU */ case 2: /* Error after record of zeroes */ case 1: /* Error after header rec */ warn(ar_file, "Skipping to next file..."); /* FALLTHRU */ default: case 0: /* Error after error */ break; } break; case 2: /* Record of zeroes */ case EOF: /* End of archive */ default: return(-1); } } } /* readcpio - read a CPIO header * * DESCRIPTION * * Read in a cpio header. Understands how to determine and read ASCII, * binary and byte-swapped binary headers. Quietly translates * old-fashioned binary cpio headers (and arranges to skip the possible * alignment byte). Returns zero if successful, -1 upon archive trailer. * * PARAMETERS * * char *name - name of the file for which the header is * for. This is modified and passed back to * the caller. * Stat *asb - Stat block for the file for which the header * is for. The fields of the stat structure are * extracted from the archive header. This is * also passed back to the caller. * * RETURNS * * Returns 0 if a valid header was found, or -1 if EOF is * encountered. */ #ifdef __STDC__ static int readcpio(char *name, Stat *asb) #else static int readcpio(name, asb) char *name; Stat *asb; #endif { OFFSET skipped; char magic[M_STRLEN]; static int align; if (align > 0) { buf_skip((OFFSET) align); } align = 0; for (;;) { buf_read(magic, M_STRLEN); skipped = 0; while ((align = inascii(magic, name, asb)) < 0 && (align = inbinary(magic, name, asb)) < 0 && (align = inswab(magic, name, asb)) < 0) { if (++skipped == 1) { if (total - sizeof(magic) == 0) { fatal("Unrecognizable archive"); } warnarch("Bad magic number", (OFFSET) sizeof(magic)); if (name[0]) { warn(name, "May be corrupt"); } } memcpy(magic, magic + 1, sizeof(magic) - 1); buf_read(magic + sizeof(magic) - 1, 1); } if (skipped) { warnarch("Apparently resynchronized", (OFFSET) sizeof(magic)); warn(name, "Continuing"); } if (strcmp(name, TRAILER) == 0) { return (-1); } if (nameopt(name) >= 0) { break; } buf_skip((OFFSET) asb->sb_size + align); } #ifdef S_IFLNK if ((asb->sb_mode & S_IFMT) == S_IFLNK) { if (buf_read(asb->sb_link, (uint) asb->sb_size) < 0) { warn(name, "Corrupt symbolic link"); return (readcpio(name, asb)); } asb->sb_link[asb->sb_size] = '\0'; asb->sb_size = 0; } #endif /* S_IFLNK */ /* destroy absolute pathnames for security reasons */ if (name[0] == '/') { if (name[1]) { while (name[0] = name[1]) { ++name; } } else { name[0] = '.'; } } asb->sb_atime = asb->sb_ctime = asb->sb_mtime; if (asb->sb_nlink > 1) { linkto(name, asb); } return (0); } /* inswab - read a reversed by order binary header * * DESCRIPTIONS * * Reads a byte-swapped CPIO binary archive header * * PARMAMETERS * * char *magic - magic number to match * char *name - name of the file which is stored in the header. * (modified and passed back to caller). * Stat *asb - stat block for the file (modified and passed back * to the caller). * * * RETURNS * * Returns the number of trailing alignment bytes to skip; -1 if * unsuccessful. * */ #ifdef __STDC__ static int inswab(char *magic, char *name, Stat *asb) #else static int inswab(magic, name, asb) char *magic; char *name; Stat *asb; #endif { ushort namesize; uint namefull; Binary binary; if (*((ushort *) magic) != SWAB(M_BINARY)) { return (-1); } memcpy((char *) &binary, magic + sizeof(ushort), M_STRLEN - sizeof(ushort)); if (buf_read((char *) &binary + M_STRLEN - sizeof(ushort), sizeof(binary) - (M_STRLEN - sizeof(ushort))) < 0) { warnarch("Corrupt swapped header", (OFFSET) sizeof(binary) - (M_STRLEN - sizeof(ushort))); return (-1); } asb->sb_dev = (dev_t) SWAB(binary.b_dev); asb->sb_ino = (ino_t) SWAB(binary.b_ino); asb->sb_mode = SWAB(binary.b_mode); asb->sb_uid = SWAB(binary.b_uid); asb->sb_gid = SWAB(binary.b_gid); asb->sb_nlink = SWAB(binary.b_nlink); #ifndef _POSIX_SOURCE asb->sb_rdev = (dev_t) SWAB(binary.b_rdev); #endif asb->sb_mtime = SWAB(binary.b_mtime[0]) << 16 | SWAB(binary.b_mtime[1]); asb->sb_size = SWAB(binary.b_size[0]) << 16 | SWAB(binary.b_size[1]); if ((namesize = SWAB(binary.b_name)) == 0 || namesize >= PATH_MAX) { warnarch("Bad swapped pathname length", (OFFSET) sizeof(binary) - (M_STRLEN - sizeof(ushort))); return (-1); } if (buf_read(name, namefull = namesize + namesize % 2) < 0) { warnarch("Corrupt swapped pathname", (OFFSET) namefull); return (-1); } if (name[namesize - 1] != '\0') { warnarch("Bad swapped pathname", (OFFSET) namefull); return (-1); } return (asb->sb_size % 2); } /* inascii - read in an ASCII cpio header * * DESCRIPTION * * Reads an ASCII format cpio header * * PARAMETERS * * char *magic - magic number to match * char *name - name of the file which is stored in the header. * (modified and passed back to caller). * Stat *asb - stat block for the file (modified and passed back * to the caller). * * RETURNS * * Returns zero if successful; -1 otherwise. Assumes that the entire * magic number has been read. */ #ifdef __STDC__ static int inascii(char *magic, char *name, Stat *asb) #else static int inascii(magic, name, asb) char *magic; char *name; Stat *asb; #endif { uint namelen; char header[H_STRLEN + 1]; #ifdef _POSIX_SOURCE dev_t dummyrdev; #endif if (strncmp(magic, M_ASCII, M_STRLEN) != 0) { return (-1); } if (buf_read(header, H_STRLEN) < 0) { warnarch("Corrupt ASCII header", (OFFSET) H_STRLEN); return (-1); } header[H_STRLEN] = '\0'; if (sscanf(header, H_SCAN, &asb->sb_dev, &asb->sb_ino, &asb->sb_mode, &asb->sb_uid, #ifdef _POSIX_SOURCE &asb->sb_gid, &asb->sb_nlink, &dummyrdev, #else &asb->sb_gid, &asb->sb_nlink, &asb->sb_rdev, #endif &asb->sb_mtime, &namelen, &asb->sb_size) != H_COUNT) { warnarch("Bad ASCII header", (OFFSET) H_STRLEN); return (-1); } if (namelen == 0 || namelen >= PATH_MAX) { warnarch("Bad ASCII pathname length", (OFFSET) H_STRLEN); return (-1); } if (buf_read(name, namelen) < 0) { warnarch("Corrupt ASCII pathname", (OFFSET) namelen); return (-1); } if (name[namelen - 1] != '\0') { warnarch("Bad ASCII pathname", (OFFSET) namelen); return (-1); } return (0); } /* inbinary - read a binary header * * DESCRIPTION * * Reads a CPIO format binary header. * * PARAMETERS * * char *magic - magic number to match * char *name - name of the file which is stored in the header. * (modified and passed back to caller). * Stat *asb - stat block for the file (modified and passed back * to the caller). * * RETURNS * * Returns the number of trailing alignment bytes to skip; -1 if * unsuccessful. */ #ifdef __STDC__ static int inbinary(char *magic, char *name, Stat *asb) #else static int inbinary(magic, name, asb) char *magic; char *name; Stat *asb; #endif { uint namefull; Binary binary; if (*((ushort *) magic) != M_BINARY) { return (-1); } memcpy((char *) &binary, magic + sizeof(ushort), M_STRLEN - sizeof(ushort)); if (buf_read((char *) &binary + M_STRLEN - sizeof(ushort), sizeof(binary) - (M_STRLEN - sizeof(ushort))) < 0) { warnarch("Corrupt binary header", (OFFSET) sizeof(binary) - (M_STRLEN - sizeof(ushort))); return (-1); } asb->sb_dev = binary.b_dev; asb->sb_ino = binary.b_ino; asb->sb_mode = binary.b_mode; asb->sb_uid = binary.b_uid; asb->sb_gid = binary.b_gid; asb->sb_nlink = binary.b_nlink; #ifndef _POSIX_SOURCE asb->sb_rdev = binary.b_rdev; #endif asb->sb_mtime = binary.b_mtime[0] << 16 | binary.b_mtime[1]; asb->sb_size = binary.b_size[0] << 16 | binary.b_size[1]; if (binary.b_name == 0 || binary.b_name >= PATH_MAX) { warnarch("Bad binary pathname length", (OFFSET) sizeof(binary) - (M_STRLEN - sizeof(ushort))); return (-1); } if (buf_read(name, namefull = binary.b_name + binary.b_name % 2) < 0) { warnarch("Corrupt binary pathname", (OFFSET) namefull); return (-1); } if (name[binary.b_name - 1] != '\0') { warnarch("Bad binary pathname", (OFFSET) namefull); return (-1); } return (asb->sb_size % 2); } /sys/src/ape/cmd/pax/fileio.c 664 sys sys 1367613436 10875 /* $Source: /u/mark/src/pax/RCS/fileio.c,v $ * * $Revision: 1.2 $ * * fileio.c - file I/O functions for all archive interfaces * * DESCRIPTION * * These function all do I/O of some form or another. They are * grouped here mainly for convienence. * * AUTHOR * * Mark H. Colburn, NAPS International (mark@jhereg.mn.org) * * Sponsored by The USENIX Association for public distribution. * * Copyright (c) 1989 Mark H. Colburn. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that the above copyright notice is duplicated in all such * forms and that any documentation, advertising materials, and other * materials related to such distribution and use acknowledge that the * software was developed * by Mark H. Colburn and sponsored by The * USENIX Association. * * 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. * * $Log: fileio.c,v $ * Revision 1.2 89/02/12 10:04:31 mark * 1.2 release fixes * * Revision 1.1 88/12/23 18:02:09 mark * Initial revision * */ #ifndef lint static char *ident = "$Id: fileio.c,v 1.2 89/02/12 10:04:31 mark Exp $"; static char *copyright = "Copyright (c) 1989 Mark H. Colburn.\nAll rights reserved.\n"; #endif /* ! lint */ /* Headers */ #include "pax.h" /* open_archive - open an archive file. * * DESCRIPTION * * Open_archive will open an archive file for reading or writing, * setting the proper file mode, depending on the "mode" passed to * it. All buffer pointers are reset according to the mode * specified. * * PARAMETERS * * int mode - specifies whether we are reading or writing. * * RETURNS * * Returns a zero if successfull, or -1 if an error occured during * the open. */ #ifdef __STDC__ int open_archive(int mode) #else int open_archive(mode) int mode; #endif { if (ar_file[0] == '-' && ar_file[1] == '\0') { if (mode == AR_READ) { archivefd = STDIN; bufend = bufidx = bufstart; } else { archivefd = STDOUT; } } else if (mode == AR_READ) { archivefd = open(ar_file, O_RDONLY | O_BINARY); bufend = bufidx = bufstart; /* set up for initial read */ } else if (mode == AR_WRITE) { archivefd = open(ar_file, O_WRONLY|O_TRUNC|O_CREAT|O_BINARY, 0666); } else if (mode == AR_APPEND) { archivefd = open(ar_file, O_RDWR | O_BINARY, 0666); bufend = bufidx = bufstart; /* set up for initial read */ } if (archivefd < 0) { warnarch(strerror(), (OFFSET) 0); return (-1); } ++arvolume; return (0); } /* close_archive - close the archive file * * DESCRIPTION * * Closes the current archive and resets the archive end of file * marker. */ #ifdef __STDC__ void close_archive(void) #else void close_archive() #endif { if (archivefd != STDIN && archivefd != STDOUT) { close(archivefd); } areof = 0; } /* openout - open an output file * * DESCRIPTION * * Openo opens the named file for output. The file mode and type are * set based on the values stored in the stat structure for the file. * If the file is a special file, then no data will be written, the * file/directory/Fifo, etc., will just be created. Appropriate * permission may be required to create special files. * * PARAMETERS * * char *name - The name of the file to create * Stat *asb - Stat structure for the file * Link *linkp; - pointer to link chain for this file * int ispass - true if we are operating in "pass" mode * * RETURNS * * Returns the output file descriptor, 0 if no data is required or -1 * if unsuccessful. Note that UNIX open() will never return 0 because * the standard input is in use. */ #ifdef __STDC__ int openout(char *name, Stat *asb, Link *linkp, int ispass) #else int openout(name, asb, linkp, ispass) char *name; Stat *asb; Link *linkp; int ispass; #endif { int exists; int fd; ushort perm; ushort operm = 0; Stat osb; #ifdef S_IFLNK int ssize; char sname[PATH_MAX + 1]; #endif /* S_IFLNK */ if (exists = (LSTAT(name, &osb) == 0)) { if (ispass && osb.sb_ino == asb->sb_ino && osb.sb_dev == asb->sb_dev) { warn(name, "Same file"); return (-1); } else if ((osb.sb_mode & S_IFMT) == (asb->sb_mode & S_IFMT)) { operm = osb.sb_mode & S_IPERM; } else if (REMOVE(name, &osb) < 0) { warn(name, strerror()); return (-1); } else { exists = 0; } } if (linkp) { if (exists) { if (asb->sb_ino == osb.sb_ino && asb->sb_dev == osb.sb_dev) { return (0); } else if (unlink(name) < 0) { warn(name, strerror()); return (-1); } else { exists = 0; } } if (link(linkp->l_name, name) != 0) { if (errno == ENOENT) { if (f_dir_create) { if (dirneed(name) != 0 || link(linkp->l_name, name) != 0) { warn(name, strerror()); return (-1); } } else { warn(name, "Directories are not being created (-d option)"); } return(0); } else if (errno != EXDEV) { warn(name, strerror()); return (-1); } } else { return(0); } } perm = asb->sb_mode & S_IPERM; switch (asb->sb_mode & S_IFMT) { case S_IFBLK: case S_IFCHR: #ifdef _POSIX_SOURCE warn(name, "Can't create special files"); return (-1); #else fd = 0; if (exists) { if (asb->sb_rdev == osb.sb_rdev) { if (perm != operm && chmod(name, (int) perm) < 0) { warn(name, strerror()); return (-1); } else { break; } } else if (REMOVE(name, &osb) < 0) { warn(name, strerror()); return (-1); } else { exists = 0; } } if (mknod(name, (int) asb->sb_mode, (int) asb->sb_rdev) < 0) { if (errno == ENOENT) { if (f_dir_create) { if (dirneed(name) < 0 || mknod(name, (int) asb->sb_mode, (int) asb->sb_rdev) < 0) { warn(name, strerror()); return (-1); } } else { warn(name, "Directories are not being created (-d option)"); } } else { warn(name, strerror()); return (-1); } } return(0); #endif /* _POSIX_SOURCE */ break; case S_IFDIR: if (exists) { if (perm != operm && chmod(name, (int) perm) < 0) { warn(name, strerror()); return (-1); } } else if (f_dir_create) { if (dirmake(name, asb) < 0 || dirneed(name) < 0) { warn(name, strerror()); return (-1); } } else { warn(name, "Directories are not being created (-d option)"); } return (0); #ifndef _POSIX_SOURCE #ifdef S_IFIFO case S_IFIFO: fd = 0; if (exists) { if (perm != operm && chmod(name, (int) perm) < 0) { warn(name, strerror()); return (-1); } } else if (mknod(name, (int) asb->sb_mode, 0) < 0) { if (errno == ENOENT) { if (f_dir_create) { if (dirneed(name) < 0 || mknod(name, (int) asb->sb_mode, 0) < 0) { warn(name, strerror()); return (-1); } } else { warn(name, "Directories are not being created (-d option)"); } } else { warn(name, strerror()); return (-1); } } return(0); break; #endif /* S_IFIFO */ #endif /* _POSIX_SOURCE */ #ifdef S_IFLNK case S_IFLNK: if (exists) { if ((ssize = readlink(name, sname, sizeof(sname))) < 0) { warn(name, strerror()); return (-1); } else if (strncmp(sname, asb->sb_link, ssize) == 0) { return (0); } else if (REMOVE(name, &osb) < 0) { warn(name, strerror()); return (-1); } else { exists = 0; } } if (symlink(asb->sb_link, name) < 0) { if (errno == ENOENT) { if (f_dir_create) { if (dirneed(name) < 0 || symlink(asb->sb_link, name) < 0) { warn(name, strerror()); return (-1); } } else { warn(name, "Directories are not being created (-d option)"); } } else { warn(name, strerror()); return (-1); } } return (0); /* Can't chown()/chmod() a symbolic link */ #endif /* S_IFLNK */ case S_IFREG: if (exists) { if (!f_unconditional && osb.sb_mtime > asb->sb_mtime) { warn(name, "Newer file exists"); return (-1); } else if (unlink(name) < 0) { warn(name, strerror()); return (-1); } else { exists = 0; } } if ((fd = creat(name, (int) perm)) < 0) { if (errno == ENOENT) { if (f_dir_create) { if (dirneed(name) < 0 || (fd = creat(name, (int) perm)) < 0) { warn(name, strerror()); return (-1); } } else { /* * the file requires a directory which does not exist * and which the user does not want created, so skip * the file... */ warn(name, "Directories are not being created (-d option)"); return(0); } } else { warn(name, strerror()); return (-1); } } break; default: warn(name, "Unknown filetype"); return (-1); } if (f_owner) { if (!exists || asb->sb_uid != osb.sb_uid || asb->sb_gid != osb.sb_gid) { chown(name, (int) asb->sb_uid, (int) asb->sb_gid); } } return (fd); } /* openin - open the next input file * * DESCRIPTION * * Openi will attempt to open the next file for input. If the file is * a special file, such as a directory, FIFO, link, character- or * block-special file, then the file size field of the stat structure * is zeroed to make sure that no data is written out for the file. * If the file is a special file, then a file descriptor of 0 is * returned to the caller, which is handled specially. If the file * is a regular file, then the file is opened and a file descriptor * to the open file is returned to the caller. * * PARAMETERS * * char *name - pointer to the name of the file to open * Stat *asb - pointer to the stat block for the file to open * * RETURNS * * Returns a file descriptor, 0 if no data exists, or -1 at EOF. This * kludge works because standard input is in use, preventing open() from * returning zero. */ #ifdef __STDC__ int openin(char *name, Stat *asb) #else int openin(name, asb) char *name; /* name of file to open */ Stat *asb; /* pointer to stat structure for file */ #endif { int fd; switch (asb->sb_mode & S_IFMT) { case S_IFDIR: asb->sb_nlink = 1; asb->sb_size = 0; return (0); #ifdef S_IFLNK case S_IFLNK: if ((asb->sb_size = readlink(name, asb->sb_link, sizeof(asb->sb_link) - 1)) < 0) { warn(name, strerror()); return(0); } asb->sb_link[asb->sb_size] = '\0'; return (0); #endif /* S_IFLNK */ case S_IFREG: if (asb->sb_size == 0) { return (0); } if ((fd = open(name, O_RDONLY | O_BINARY)) < 0) { warn(name, strerror()); } return (fd); default: asb->sb_size = 0; return (0); } } /sys/src/ape/cmd/pax/func.h 664 sys sys 1367613436 5091 /* $Source: /u/mark/src/pax/RCS/func.h,v $ * * $Revision: 1.3 $ * * func.h - function type and argument declarations * * DESCRIPTION * * This file contains function delcarations in both ANSI style * (function prototypes) and traditional style. * * AUTHOR * * Mark H. Colburn, NAPS International (mark@jhereg.mn.org) * * Sponsored by The USENIX Association for public distribution. * * Copyright (c) 1989 Mark H. Colburn. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that the above copyright notice and this paragraph are * duplicated in all such forms and that any documentation, * advertising materials, and other materials related to such * distribution and use acknowledge that the software was developed * by Mark H. Colburn and sponsored by The USENIX Association. * * 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. */ #ifndef _PAX_FUNC_H #define _PAX_FUNC_H /* Function Prototypes */ #ifdef __STDC__ extern Link *linkfrom(char *, Stat *); extern Link *linkto(char *, Stat *); extern char *mem_get(uint); extern char *mem_str(char *); extern char *strerror(void); extern int ar_read(void); extern int buf_read(char *, uint); extern int buf_skip(OFFSET); extern int create_archive(void); extern int dirneed(char *); extern int read_archive(void); extern int inentry(char *, Stat *); extern int lineget(FILE *, char *); extern int name_match(char *); extern int name_next(char *, Stat *); extern int nameopt(char *); extern int open_archive(int); extern int open_tty(void); extern int openin(char *, Stat *); extern int openout(char *, Stat *, Link *, int); extern int pass(char *); extern int passitem(char *, Stat *, int, char *); extern int read_header(char *, Stat *); extern int wildmat(char *, char *); extern void buf_allocate(OFFSET); extern void close_archive(void); extern void fatal(char *); extern void name_gather(void); extern void name_init(int, char **); extern void names_notfound(void); extern void next(int); extern int nextask(char *, char *, int); extern void outdata(int, char *, OFFSET); extern void outwrite(char *, uint); extern void passdata(char *, int, char *, int); extern void print_entry(char *, Stat *); extern void warn(); extern void warnarch(char *, OFFSET); extern void write_eot(void); extern void get_archive_type(void); extern struct group *getgrgid(); extern struct group *getgrnam(); extern struct passwd *getpwuid(); extern char *getenv(char *); extern SIG_T (*signal())(); extern Link *islink(char *, Stat *); extern char *finduname(int); extern char *findgname(int); extern int findgid(char *gname); extern char *malloc(); #else /* !__STDC__ */ extern Link *linkfrom(); extern Link *linkto(); extern char *mem_get(); extern char *mem_str(); extern char *strerror(); extern int ar_read(); extern int buf_read(); extern int buf_skip(); extern int create_archive(); extern int dirneed(); extern int read_archive(); extern int inentry(); extern int lineget(); extern int name_match(); extern int name_next(); extern int nameopt(); extern int open_archive(); extern int open_tty(); extern int openin(); extern int openout(); extern int pass(); extern int passitem(); extern int read_header(); extern int wildmat(); extern void buf_allocate(); extern void close_archive(); extern void fatal(); extern void name_gather(); extern void name_init(); extern void names_notfound(); extern void next(); extern int nextask(); extern void outdata(); extern void outwrite(); extern void passdata(); extern void print_entry(); extern void warn(); extern void warnarch(); extern void write_eot(); extern void get_archive_type(); extern char *getenv(); extern char *malloc(); extern char *strcat(); extern char *strcpy(); extern char *strncpy(); extern SIG_T (*signal())(); extern OFFSET lseek(); extern struct group *getgrgid(); extern struct group *getgrnam(); extern struct passwd *getpwuid(); extern struct tm *localtime(); extern time_t time(); extern uint sleep(); extern void _exit(); extern void exit(); extern void free(); extern Link *islink(); extern char *finduname(); extern char *findgname(); extern int findgid(); #endif /* __STDC__ */ #endif /* _PAX_FUNC_H */ /sys/src/ape/cmd/pax/limits.h 664 sys sys 1367613436 2765 /* $Source: /u/mark/src/pax/RCS/limits.h,v $ * * $Revision: 1.2 $ * * limits.h - POSIX compatible defnitions for some of * * DESCRIPTION * * We need to include if this system is being compiled with an * ANSI standard C compiler, or if we are running on a POSIX confomrming * system. If the manifest constant _POSIX_SOURCE is not defined when * is included, then none of the POSIX constants are defined * and we need to define them here. It's a bit wierd, but it works. * * These values where taken from the IEEE P1003.1 standard, draft 12. * All of the values below are the MINIMUM values allowed by the standard. * Not all values are used by the PAX program, but they are included for * completeness, and for support of future enhancements. Please see * section 2.9 of the draft standard for more information on the following * constants. * * AUTHOR * * Mark H. Colburn, NAPS International (mark@jhereg.mn.org) * * Sponsored by The USENIX Association for public distribution. * * Copyright (c) 1989 Mark H. Colburn. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that the above copyright notice and this paragraph are * duplicated in all such forms and that any documentation, * advertising materials, and other materials related to such * distribution and use acknowledge that the software was developed * by Mark H. Colburn and sponsored by The USENIX Association. * * 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. */ #ifndef _PAX_LIMITS_H #define _PAX_LIMITS_H /* Headers */ #if defined(__STDC__) || defined(_POSIX_SOURCE) # include #endif /* Defines */ #ifndef _POSIX_SOURCE #define MAX_INPUT 256 /* Max numbef of bytes in terminal input */ #define NGROUPS_MAX 1 /* Max number of suplemental group id's */ #define PASS_MAX 8 /* Max number of bytes in a password */ #define PID_MAX 30000 /* Max value for a process ID */ #define UID_MAX 32000 /* Max value for a user or group ID */ #define ARG_MAX 4096 /* Nax number of bytes passed to exec */ #define CHILD_MAX 6 /* Max number of simultaneous processes */ #define MAX_CANON 256 /* Max numbef of bytes in a cononical queue */ #define OPEN_MAX 16 /* Nax number of open files per process */ #define NAME_MAX 14 /* Max number of bytes in a file name */ #define PATH_MAX 255 /* Max number of bytes in pathname */ #define LINK_MAX 8 /* Max value of a file's link count */ #define PIPE_BUF 512 /* Max number of bytes for pipe reads */ #endif /* _POSIX_SOURCE */ #endif /* _PAX_LIMITS_H */ /sys/src/ape/cmd/pax/link.c 664 sys sys 1367613436 7658 /* $Source: /u/mark/src/pax/RCS/link.c,v $ * * $Revision: 1.2 $ * * link.c - functions for handling multiple file links * * DESCRIPTION * * These function manage the link chains which are used to keep track * of outstanding links during archive reading and writing. * * AUTHOR * * Mark H. Colburn, NAPS International (mark@jhereg.mn.org) * * Sponsored by The USENIX Association for public distribution. * * Copyright (c) 1989 Mark H. Colburn. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that the above copyright notice is duplicated in all such * forms and that any documentation, advertising materials, and other * materials related to such distribution and use acknowledge that the * software was developed * by Mark H. Colburn and sponsored by The * USENIX Association. * * 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. * * $Log: link.c,v $ * Revision 1.2 89/02/12 10:04:38 mark * 1.2 release fixes * * Revision 1.1 88/12/23 18:02:12 mark * Initial revision * */ #ifndef lint static char *ident = "$Id: link.c,v 1.2 89/02/12 10:04:38 mark Exp $"; static char *copyright = "Copyright (c) 1989 Mark H. Colburn.\nAll rights reserved.\n"; #endif /* ! lint */ /* Headers */ #include "pax.h" /* Defines */ /* * Address link information base. */ #define LINKHASH(ino) (linkbase + (ino) % NEL(linkbase)) /* * Number of array elements. */ #define NEL(a) (sizeof(a) / sizeof(*(a))) /* Internal Identifiers */ static Link *linkbase[256]; /* Unresolved link information */ /* linkfrom - find a file to link from * * DESCRIPTION * * Linkfrom searches the link chain to see if there is a file in the * link chain which has the same inode number as the file specified * by the stat block pointed at by asb. If a file is found, the * name is returned to the caller, otherwise a NULL is returned. * * PARAMETERS * * char *name - name of the file which we are attempting * to find a link for * Stat *asb - stat structure of file to find a link to * * RETURNS * * Returns a pointer to a link structure, or NULL if unsuccessful. * */ #ifdef __STDC__ Link *linkfrom(char *name, Stat *asb) #else Link *linkfrom(name, asb) char *name; Stat *asb; #endif { Link *linkp; Link *linknext; Path *path; Path *pathnext; Link **abase; for (linkp = *(abase = LINKHASH(asb->sb_ino)); linkp; linkp = linknext) { if (linkp->l_nlink == 0) { if (linkp->l_name) { free((char *) linkp->l_name); } if (linknext = linkp->l_forw) { linknext->l_back = linkp->l_back; } if (linkp->l_back) { linkp->l_back->l_forw = linkp->l_forw; } free((char *) linkp); *abase = (Link *)NULL; } else if (linkp->l_ino == asb->sb_ino && linkp->l_dev == asb->sb_dev) { /* * check to see if a file with the name "name" exists in the * chain of files which we have for this particular link */ for (path = linkp->l_path; path; path = pathnext) { if (strcmp(path->p_name, name) == 0) { --linkp->l_nlink; if (path->p_name) { free(path->p_name); } if (pathnext = path->p_forw) { pathnext->p_back = path->p_back; } if (path->p_back) { path->p_back->p_forw = pathnext; } if (linkp->l_path == path) { linkp->l_path = pathnext; } free(path); return (linkp); } pathnext = path->p_forw; } return((Link *)NULL); } else { linknext = linkp->l_forw; } } return ((Link *)NULL); } /* islink - determine whether a given file really a link * * DESCRIPTION * * Islink searches the link chain to see if there is a file in the * link chain which has the same inode number as the file specified * by the stat block pointed at by asb. If a file is found, a * non-zero value is returned to the caller, otherwise a 0 is * returned. * * PARAMETERS * * char *name - name of file to check to see if it is link. * Stat *asb - stat structure of file to find a link to * * RETURNS * * Returns a pointer to a link structure, or NULL if unsuccessful. * */ #ifdef __STDC__ Link *islink(char *name, Stat *asb) #else Link *islink(name, asb) char *name; Stat *asb; #endif { Link *linkp; Link *linknext; for (linkp = *(LINKHASH(asb->sb_ino)); linkp; linkp = linknext) { if (linkp->l_ino == asb->sb_ino && linkp->l_dev == asb->sb_dev) { if (strcmp(name, linkp->l_name) == 0) { return ((Link *)NULL); } return (linkp); } else { linknext = linkp->l_forw; } } return ((Link *)NULL); } /* linkto - remember a file with outstanding links * * DESCRIPTION * * Linkto adds the specified file to the link chain. Any subsequent * calls to linkfrom which have the same inode will match the file * just entered. If not enough space is available to make the link * then the item is not added to the link chain, and a NULL is * returned to the calling function. * * PARAMETERS * * char *name - name of file to remember * Stat *asb - pointer to stat structure of file to remember * * RETURNS * * Returns a pointer to the associated link structure, or NULL when * linking is not possible. * */ #ifdef __STDC__ Link *linkto(char *name, Stat *asb) #else Link *linkto(name, asb) char *name; Stat *asb; #endif { Link *linkp; Link *linknext; Path *path; Link **abase; for (linkp = *(LINKHASH(asb->sb_ino)); linkp; linkp = linknext) { if (linkp->l_ino == asb->sb_ino && linkp->l_dev == asb->sb_dev) { if ((path = (Path *) mem_get(sizeof(Path))) == (Path *)NULL || (path->p_name = mem_str(name)) == (char *)NULL) { return((Link *)NULL); } if (path->p_forw = linkp->l_path) { if (linkp->l_path->p_forw) { linkp->l_path->p_forw->p_back = path; } } else { linkp->l_path = path; } path->p_back = (Path *)NULL; return(linkp); } else { linknext = linkp->l_forw; } } /* * This is a brand new link, for which there is no other information */ if ((asb->sb_mode & S_IFMT) == S_IFDIR || (linkp = (Link *) mem_get(sizeof(Link))) == (Link *)NULL || (linkp->l_name = mem_str(name)) == (char *)NULL) { return ((Link *)NULL); } linkp->l_dev = asb->sb_dev; linkp->l_ino = asb->sb_ino; linkp->l_nlink = asb->sb_nlink - 1; linkp->l_size = asb->sb_size; linkp->l_path = (Path *)NULL; if (linkp->l_forw = *(abase = LINKHASH(asb->sb_ino))) { linkp->l_forw->l_back = linkp; } else { *abase = linkp; } linkp->l_back = (Link *)NULL; return (linkp); } /* linkleft - complain about files with unseen links * * DESCRIPTION * * Linksleft scans through the link chain to see if there were any * files which have outstanding links that were not processed by the * archive. For each file in the link chain for which there was not * a file, and error message is printed. */ #ifdef __STDC__ void linkleft(void) #else void linkleft() #endif { Link *lp; Link **base; for (base = linkbase; base < linkbase + NEL(linkbase); ++base) { for (lp = *base; lp; lp = lp->l_forw) { if (lp->l_nlink) { warn(lp->l_path->p_name, "Unseen link(s)"); } } } } /sys/src/ape/cmd/pax/list.c 664 sys sys 1367613436 16312 /* $Source: /u/mark/src/pax/RCS/list.c,v $ * * $Revision: 1.2 $ * * list.c - List all files on an archive * * DESCRIPTION * * These function are needed to support archive table of contents and * verbose mode during extraction and creation of achives. * * AUTHOR * * Mark H. Colburn, NAPS International (mark@jhereg.mn.org) * * Sponsored by The USENIX Association for public distribution. * * Copyright (c) 1989 Mark H. Colburn. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that the above copyright notice is duplicated in all such * forms and that any documentation, advertising materials, and other * materials related to such distribution and use acknowledge that the * software was developed * by Mark H. Colburn and sponsored by The * USENIX Association. * * 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. * * $Log: list.c,v $ * Revision 1.2 89/02/12 10:04:43 mark * 1.2 release fixes * * Revision 1.1 88/12/23 18:02:14 mark * Initial revision * */ #ifndef lint static char *ident = "$Id: list.c,v 1.2 89/02/12 10:04:43 mark Exp $"; static char *copyright = "Copyright (c) 1989 Mark H. Colburn.\nAll rights reserved.\n"; #endif /* ! lint */ /* Headers */ #include "pax.h" /* Defines */ /* * isodigit returns non zero iff argument is an octal digit, zero otherwise */ #define ISODIGIT(c) (((c) >= '0') && ((c) <= '7')) /* Function Prototypes */ #ifdef __STDC__ static void cpio_entry(char *, Stat *); static void tar_entry(char *, Stat *); static void pax_entry(char *, Stat *); static void print_mode(ushort); static long from_oct(int digs, char *where); #else /* !__STDC__ */ static void cpio_entry(); static void tar_entry(); static void pax_entry(); static void print_mode(); static long from_oct(); #endif /* __STDC__ */ /* Internal Identifiers */ static char *monnames[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; /* read_header - read a header record * * DESCRIPTION * * Read a record that's supposed to be a header record. Return its * address in "head", and if it is good, the file's size in * asb->sb_size. Decode things from a file header record into a "Stat". * Also set "head_standard" to !=0 or ==0 depending whether header record * is "Unix Standard" tar format or regular old tar format. * * PARAMETERS * * char *name - pointer which will contain name of file * Stat *asb - pointer which will contain stat info * * RETURNS * * Return 1 for success, 0 if the checksum is bad, EOF on eof, 2 for a * record full of zeros (EOF marker). */ #ifdef __STDC__ int read_header(char *name, Stat *asb) #else int read_header(name, asb) char *name; Stat *asb; #endif { int i; long sum; long recsum; Link *link; char *p; char hdrbuf[BLOCKSIZE]; memset((char *)asb, 0, sizeof(Stat)); /* read the header from the buffer */ if (buf_read(hdrbuf, BLOCKSIZE) != 0) { return (EOF); } strcpy(name, hdrbuf); recsum = from_oct(8, &hdrbuf[148]); sum = 0; p = hdrbuf; for (i = 0 ; i < 500; i++) { /* * We can't use unsigned char here because of old compilers, e.g. V7. */ sum += 0xFF & *p++; } /* Adjust checksum to count the "chksum" field as blanks. */ for (i = 0; i < 8; i++) { sum -= 0xFF & hdrbuf[148 + i]; } sum += ' ' * 8; if (sum == 8 * ' ') { /* * This is a zeroed record...whole record is 0's except for the 8 * blanks we faked for the checksum field. */ return (2); } if (sum == recsum) { /* * Good record. Decode file size and return. */ if (hdrbuf[156] != LNKTYPE) { asb->sb_size = from_oct(1 + 12, &hdrbuf[124]); } asb->sb_mtime = from_oct(1 + 12, &hdrbuf[136]); asb->sb_mode = from_oct(8, &hdrbuf[100]); if (strcmp(&hdrbuf[257], TMAGIC) == 0) { /* Unix Standard tar archive */ head_standard = 1; #ifdef NONAMES asb->sb_uid = from_oct(8, &hdrbuf[108]); asb->sb_gid = from_oct(8, &hdrbuf[116]); #else asb->sb_uid = finduid(&hdrbuf[265]); asb->sb_gid = findgid(&hdrbuf[297]); #endif switch (hdrbuf[156]) { case BLKTYPE: case CHRTYPE: #ifndef _POSIX_SOURCE asb->sb_rdev = makedev(from_oct(8, &hdrbuf[329]), from_oct(8, &hdrbuf[337])); #endif break; default: /* do nothing... */ break; } } else { /* Old fashioned tar archive */ head_standard = 0; asb->sb_uid = from_oct(8, &hdrbuf[108]); asb->sb_gid = from_oct(8, &hdrbuf[116]); } switch (hdrbuf[156]) { case REGTYPE: case AREGTYPE: /* * Berkeley tar stores directories as regular files with a * trailing / */ if (name[strlen(name) - 1] == '/') { name[strlen(name) - 1] = '\0'; asb->sb_mode |= S_IFDIR; } else { asb->sb_mode |= S_IFREG; } break; case LNKTYPE: asb->sb_nlink = 2; linkto(&hdrbuf[157], asb); linkto(name, asb); asb->sb_mode |= S_IFREG; break; case BLKTYPE: asb->sb_mode |= S_IFBLK; break; case CHRTYPE: asb->sb_mode |= S_IFCHR; break; case DIRTYPE: asb->sb_mode |= S_IFDIR; break; #ifdef S_IFLNK case SYMTYPE: asb->sb_mode |= S_IFLNK; strcpy(asb->sb_link, &hdrbuf[157]); break; #endif #ifdef S_IFIFO case FIFOTYPE: asb->sb_mode |= S_IFIFO; break; #endif #ifdef S_IFCTG case CONTTYPE: asb->sb_mode |= S_IFCTG; break; #endif } return (1); } return (0); } /* print_entry - print a single table-of-contents entry * * DESCRIPTION * * Print_entry prints a single line of file information. The format * of the line is the same as that used by the LS command. For some * archive formats, various fields may not make any sense, such as * the link count on tar archives. No error checking is done for bad * or invalid data. * * PARAMETERS * * char *name - pointer to name to print an entry for * Stat *asb - pointer to the stat structure for the file */ #ifdef __STDC__ void print_entry(char *name, Stat *asb) #else void print_entry(name, asb) char *name; Stat *asb; #endif { switch (ar_interface) { case TAR: tar_entry(name, asb); break; case CPIO: cpio_entry(name, asb); break; case PAX: pax_entry(name, asb); break; } } /* cpio_entry - print a verbose cpio-style entry * * DESCRIPTION * * Print_entry prints a single line of file information. The format * of the line is the same as that used by the traditional cpio * command. No error checking is done for bad or invalid data. * * PARAMETERS * * char *name - pointer to name to print an entry for * Stat *asb - pointer to the stat structure for the file */ #ifdef __STDC__ static void cpio_entry(char *name, Stat *asb) #else static void cpio_entry(name, asb) char *name; Stat *asb; #endif { struct tm *atm; Link *from; struct passwd *pwp; struct group *grp; if (f_list && f_verbose) { fprintf(msgfile, "%-7o", asb->sb_mode); atm = localtime(&asb->sb_mtime); if (pwp = getpwuid((int) USH(asb->sb_uid))) { fprintf(msgfile, "%-6s", pwp->pw_name); } else { fprintf(msgfile, "%-6u", USH(asb->sb_uid)); } fprintf(msgfile,"%7ld %3s %2d %02d:%02d:%02d %4d ", asb->sb_size, monnames[atm->tm_mon], atm->tm_mday, atm->tm_hour, atm->tm_min, atm->tm_sec, atm->tm_year + 1900); } fprintf(msgfile, "%s", name); if ((asb->sb_nlink > 1) && (from = islink(name, asb))) { fprintf(msgfile, " linked to %s", from->l_name); } #ifdef S_IFLNK if ((asb->sb_mode & S_IFMT) == S_IFLNK) { fprintf(msgfile, " symbolic link to %s", asb->sb_link); } #endif /* S_IFLNK */ putc('\n', msgfile); } /* tar_entry - print a tar verbose mode entry * * DESCRIPTION * * Print_entry prints a single line of tar file information. The format * of the line is the same as that produced by the traditional tar * command. No error checking is done for bad or invalid data. * * PARAMETERS * * char *name - pointer to name to print an entry for * Stat *asb - pointer to the stat structure for the file */ #ifdef __STDC__ static void tar_entry(char *name, Stat *asb) #else static void tar_entry(name, asb) char *name; Stat *asb; #endif { struct tm *atm; int i; int mode; char *symnam = "NULL"; Link *link; if ((mode = asb->sb_mode & S_IFMT) == S_IFDIR) { return; /* don't print directories */ } if (f_extract) { switch (mode) { #ifdef S_IFLNK case S_IFLNK: /* This file is a symbolic link */ i = readlink(name, symnam, PATH_MAX - 1); if (i < 0) { /* Could not find symbolic link */ warn("can't read symbolic link", strerror()); } else { /* Found symbolic link filename */ symnam[i] = '\0'; fprintf(msgfile, "x %s symbolic link to %s\n", name, symnam); } break; #endif case S_IFREG: /* It is a link or a file */ if ((asb->sb_nlink > 1) && (link = islink(name, asb))) { fprintf(msgfile, "%s linked to %s\n", name, link->l_name); } else { fprintf(msgfile, "x %s, %ld bytes, %d tape blocks\n", name, asb->sb_size, ROUNDUP(asb->sb_size, BLOCKSIZE) / BLOCKSIZE); } } } else if (f_append || f_create) { switch (mode) { #ifdef S_IFLNK case S_IFLNK: /* This file is a symbolic link */ i = readlink(name, symnam, PATH_MAX - 1); if (i < 0) { /* Could not find symbolic link */ warn("can't read symbolic link", strerror()); } else { /* Found symbolic link filename */ symnam[i] = '\0'; fprintf(msgfile, "a %s symbolic link to %s\n", name, symnam); } break; #endif case S_IFREG: /* It is a link or a file */ fprintf(msgfile, "a %s ", name); if ((asb->sb_nlink > 1) && (link = islink(name, asb))) { fprintf(msgfile, "link to %s\n", link->l_name); } else { fprintf(msgfile, "%ld Blocks\n", ROUNDUP(asb->sb_size, BLOCKSIZE) / BLOCKSIZE); } break; } } else if (f_list) { if (f_verbose) { atm = localtime(&asb->sb_mtime); print_mode(asb->sb_mode); fprintf(msgfile," %d/%d %6d %3s %2d %02d:%02d %4d %s", asb->sb_uid, asb->sb_gid, asb->sb_size, monnames[atm->tm_mon], atm->tm_mday, atm->tm_hour, atm->tm_min, atm->tm_year + 1900, name); } else { fprintf(msgfile, "%s", name); } switch (mode) { #ifdef S_IFLNK case S_IFLNK: /* This file is a symbolic link */ i = readlink(name, symnam, PATH_MAX - 1); if (i < 0) { /* Could not find symbolic link */ warn("can't read symbolic link", strerror()); } else { /* Found symbolic link filename */ symnam[i] = '\0'; fprintf(msgfile, " symbolic link to %s", symnam); } break; #endif case S_IFREG: /* It is a link or a file */ if ((asb->sb_nlink > 1) && (link = islink(name, asb))) { fprintf(msgfile, " linked to %s", link->l_name); } break; /* Do not print out directories */ } fputc('\n', msgfile); } else { fprintf(msgfile, "? %s %ld blocks\n", name, ROUNDUP(asb->sb_size, BLOCKSIZE) / BLOCKSIZE); } } /* pax_entry - print a verbose cpio-style entry * * DESCRIPTION * * Print_entry prints a single line of file information. The format * of the line is the same as that used by the LS command. * No error checking is done for bad or invalid data. * * PARAMETERS * * char *name - pointer to name to print an entry for * Stat *asb - pointer to the stat structure for the file */ #ifdef __STDC__ static void pax_entry(char *name, Stat *asb) #else static void pax_entry(name, asb) char *name; Stat *asb; #endif { struct tm *atm; Link *from; struct passwd *pwp; struct group *grp; if (f_list && f_verbose) { print_mode(asb->sb_mode); fprintf(msgfile, " %2d", asb->sb_nlink); atm = localtime(&asb->sb_mtime); if (pwp = getpwuid((int) USH(asb->sb_uid))) { fprintf(msgfile, " %-8s", pwp->pw_name); } else { fprintf(msgfile, " %-8u", USH(asb->sb_uid)); } if (grp = getgrgid((int) USH(asb->sb_gid))) { fprintf(msgfile, " %-8s", grp->gr_name); } else { fprintf(msgfile, " %-8u", USH(asb->sb_gid)); } switch (asb->sb_mode & S_IFMT) { case S_IFBLK: case S_IFCHR: fprintf(msgfile, "\t%3d, %3d", major(asb->sb_rdev), minor(asb->sb_rdev)); break; case S_IFREG: fprintf(msgfile, "\t%8ld", asb->sb_size); break; default: fprintf(msgfile, "\t "); } fprintf(msgfile," %3s %2d %02d:%02d ", monnames[atm->tm_mon], atm->tm_mday, atm->tm_hour, atm->tm_min); } fprintf(msgfile, "%s", name); if ((asb->sb_nlink > 1) && (from = islink(name, asb))) { fprintf(msgfile, " == %s", from->l_name); } #ifdef S_IFLNK if ((asb->sb_mode & S_IFMT) == S_IFLNK) { fprintf(msgfile, " -> %s", asb->sb_link); } #endif /* S_IFLNK */ putc('\n', msgfile); } /* print_mode - fancy file mode display * * DESCRIPTION * * Print_mode displays a numeric file mode in the standard unix * representation, ala ls (-rwxrwxrwx). No error checking is done * for bad mode combinations. FIFOS, sybmbolic links, sticky bits, * block- and character-special devices are supported if supported * by the hosting implementation. * * PARAMETERS * * ushort mode - The integer representation of the mode to print. */ #ifdef __STDC__ static void print_mode(ushort mode) #else static void print_mode(mode) ushort mode; #endif { /* Tar does not print the leading identifier... */ if (ar_interface != TAR) { switch (mode & S_IFMT) { case S_IFDIR: putc('d', msgfile); break; #ifdef S_IFLNK case S_IFLNK: putc('l', msgfile); break; #endif /* S_IFLNK */ case S_IFBLK: putc('b', msgfile); break; case S_IFCHR: putc('c', msgfile); break; #ifdef S_IFIFO case S_IFIFO: putc('p', msgfile); break; #endif /* S_IFIFO */ case S_IFREG: default: putc('-', msgfile); break; } } putc(mode & 0400 ? 'r' : '-', msgfile); putc(mode & 0200 ? 'w' : '-', msgfile); putc(mode & 0100 ? mode & 04000 ? 's' : 'x' : mode & 04000 ? 'S' : '-', msgfile); putc(mode & 0040 ? 'r' : '-', msgfile); putc(mode & 0020 ? 'w' : '-', msgfile); putc(mode & 0010 ? mode & 02000 ? 's' : 'x' : mode & 02000 ? 'S' : '-', msgfile); putc(mode & 0004 ? 'r' : '-', msgfile); putc(mode & 0002 ? 'w' : '-', msgfile); putc(mode & 0001 ? mode & 01000 ? 't' : 'x' : mode & 01000 ? 'T' : '-', msgfile); } /* from_oct - quick and dirty octal conversion * * DESCRIPTION * * From_oct will convert an ASCII representation of an octal number * to the numeric representation. The number of characters to convert * is given by the parameter "digs". If there are less numbers than * specified by "digs", then the routine returns -1. * * PARAMETERS * * int digs - Number to of digits to convert * char *where - Character representation of octal number * * RETURNS * * The value of the octal number represented by the first digs * characters of the string where. Result is -1 if the field * is invalid (all blank, or nonoctal). * * ERRORS * * If the field is all blank, then the value returned is -1. * */ #ifdef __STDC__ static long from_oct(int digs, char *where) #else static long from_oct(digs, where) int digs; /* number of characters to convert */ char *where; /* character representation of octal number */ #endif { long value; while (isspace(*where)) { /* Skip spaces */ where++; if (--digs <= 0) { return(-1); /* All blank field */ } } value = 0; while (digs > 0 && ISODIGIT(*where)) { /* Scan til nonoctal */ value = (value << 3) | (*where++ - '0'); --digs; } if (digs > 0 && *where && !isspace(*where)) { return(-1); /* Ended on non-space/nul */ } return(value); } /sys/src/ape/cmd/pax/mem.c 664 sys sys 1367613436 3278 /* $Source: /u/mark/src/pax/RCS/mem.c,v $ * * $Revision: 1.2 $ * * mem.c - memory allocation and manipulation functions * * DESCRIPTION * * These routines are provided for higher level handling of the UNIX * memory allocation functions. * * AUTHOR * * Mark H. Colburn, NAPS International (mark@jhereg.mn.org) * * * Sponsored by The USENIX Association for public distribution. * * Copyright (c) 1989 Mark H. Colburn. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that the above copyright notice is duplicated in all such * forms and that any documentation, advertising materials, and other * materials related to such distribution and use acknowledge that the * software was developed * by Mark H. Colburn and sponsored by The * USENIX Association. * * 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. * * $Log: mem.c,v $ * Revision 1.2 89/02/12 10:04:53 mark * 1.2 release fixes * * Revision 1.1 88/12/23 18:02:17 mark * Initial revision * */ #ifndef lint static char *ident = "$Id: mem.c,v 1.2 89/02/12 10:04:53 mark Exp $"; static char *copyright = "Copyright (c) 1989 Mark H. Colburn.\nAll rights reserved.\n"; #endif /* ! lint */ /* Headers */ #include "pax.h" /* mem_get - allocate memory * * DESCRIPTION * * Mem_get attempts to allocate a block of memory using the malloc * function call. In the event that the memory is not available, * mem_get will display an "Out of memory" message for the user * the first time it encounters the an out of memory situation. * Subsequent calls to mem_get may fail, but no message will be * printed. * * PARAMETERS * * uint len - The amount of memory to allocate * * RETURNS * * Normally returns the pointer to the newly allocated memory. If * an error occurs, NULL is returned, and an error message is * printed. * * ERRORS * * ENOMEM No memory is available */ #ifdef __STDC__ char *mem_get(uint len) #else char *mem_get(len) uint len; /* amount of memory to get */ #endif { char *mem; static short outofmem = 0; if ((mem = (char *)malloc(len)) == (char *)NULL && !outofmem) { outofmem++; warn("mem_get()", "Out of memory"); } return (mem); } /* mem_str - duplicate a string into dynamic memory * * DESCRIPTION * * Mem_str attempts to make a copy of string. It allocates space for * the string, and if the allocation was successfull, copies the old * string into the newly allocated space. * * PARAMETERS * * char *str - string to make a copy of * * RETURNS * * Normally returns a pointer to a new string at least as large * as strlen(str) + 1, which contains a copy of the the data * passed in str, plus a null terminator. Returns (char *)NULL * if enough memory to make a copy of str is not available. */ #ifdef __STDC__ char *mem_str(char *str) #else char *mem_str(str) char *str; /* string to make a copy of */ #endif { char *mem; if (mem = mem_get((uint) strlen(str) + 1)) { strcpy(mem, str); } return (mem); } /sys/src/ape/cmd/pax/mkfile 664 sys sys 1369258816 476 APE=/sys/src/ape <$APE/config TARG=pax OFILES=pax.$O\ append.$O\ buffer.$O\ cpio.$O\ create.$O\ extract.$O\ fileio.$O\ link.$O\ list.$O\ mem.$O\ namelist.$O\ names.$O\ pass.$O\ pathname.$O\ port.$O\ regexp.$O\ replace.$O\ tar.$O\ ttyio.$O\ warn.$O\ wildmat.$O\ HFILES=config.h\ func.h\ limits.h\ port.h\ pax.h\ BIN=$APEBIN next = (struct nm_list *)NULL; p->length = i; strncpy(p->name, name, i); p->name[i] = '\0'; /* Null term */ p->found = 0; p->firstch = isalpha(name[0]); if (strchr(name, '*') || strchr(name, '[') || strchr(name, '?')) { p->re = 1; } if (namelast) { namelast->next = p; } namelast = p; if (!namelist) { namelist = p; } } /* name_match - match a name from an archive with a name from the namelist * * DESCRIPTION * * Name_match attempts to find a name pointed at by p in the namelist. * If no namelist is available, then all filenames passed in are * assumed to match the filename criteria. Name_match knows how to * match names with regular expressions, etc. * * PARAMETERS * * char *p - the name to match * * RETURNS * * Returns 1 if the name is in the namelist, or no name list is * available, otherwise returns 0 * */ #ifdef __STDC__ int name_match(char *p) #else int name_match(p) char *p; #endif { struct nm_list *nlp; int len; if ((nlp = namelist) == 0) {/* Empty namelist is easy */ return (1); } len = strlen(p); for (; nlp != 0; nlp = nlp->next) { /* If first chars don't match, quick skip */ if (nlp->firstch && nlp->name[0] != p[0]) { continue; } /* Regular expressions */ if (nlp->re) { if (wildmat(nlp->name, p)) { nlp->found = 1; /* Remember it matched */ return (1); /* We got a match */ } continue; } /* Plain Old Strings */ if (nlp->length <= len /* Archive len >= specified */ && (p[nlp->length] == '\0' || p[nlp->length] == '/') && strncmp(p, nlp->name, nlp->length) == 0) { /* Name compare */ nlp->found = 1; /* Remember it matched */ return (1); /* We got a match */ } } return (0); } /* names_notfound - print names of files in namelist that were not found * * DESCRIPTION * * Names_notfound scans through the namelist for any files which were * named, but for which a matching file was not processed by the * archive. Each of the files is listed on the standard error. * */ #ifdef __STDC__ void names_notfound(void) #else void names_notfound() #endif { struct nm_list *nlp; for (nlp = namelist; nlp != 0; nlp = nlp->next) { if (!nlp->found) { fprintf(stderr, "%s: %s not found in archive\n", myname, nlp->name); } free(nlp); } namelist = (struct nm_list *)NULL; namelast = (struct nm_list *)NULL; } /* name_init - set up to gather file names * * DESCRIPTION * * Name_init sets up the namelist pointers so that we may access the * command line arguments. At least the first item of the command * line (argv[0]) is assumed to be stripped off, prior to the * name_init call. * * PARAMETERS * * int argc - number of items in argc * char **argv - pointer to the command line arguments */ #ifdef __STDC__ void name_init(int argc, char **argv) #else void name_init(argc, argv) int argc; char **argv; #endif { /* Get file names from argv, after options. */ n_argc = argc; n_argv = argv; } /* name_next - get the next name from argv or the name file. * * DESCRIPTION * * Name next finds the next name which is to be processed in the * archive. If the named file is a directory, then the directory * is recursively traversed for additional file names. Directory * names and locations within the directory are kept track of by * using a directory stack. See the pushdir/popdir function for * more details. * * The names come from argv, after options or from the standard input. * * PARAMETERS * * name - a pointer to a buffer of at least MAX_PATH + 1 bytes long; * statbuf - a pointer to a stat structure * * RETURNS * * Returns -1 if there are no names left, (e.g. EOF), otherwise returns * 0 */ #ifdef __STDC__ int name_next(char *name, Stat *statbuf) #else int name_next(name, statbuf) char *name; Stat *statbuf; #endif { int err = -1; static int in_subdir = 0; static DIR *dirp; struct dirent *d; static struct dirinfo *curr_dir; int len; do { if (names_from_stdin) { if (lineget(stdin, name) < 0) { return (-1); } if (nameopt(name) < 0) { continue; } } else { if (in_subdir) { if ((d = readdir(dirp)) != (struct dirent *)NULL) { /* Skip . and .. */ if (strcmp(d->d_name, ".") == 0 || strcmp(d->d_name, "..") == 0) { continue; } if (strlen(d->d_name) + strlen(curr_dir->dirname) >= PATH_MAX) { warn("name too long", d->d_name); continue; } strcpy(name, curr_dir->dirname); strcat(name, d->d_name); } else { closedir(dirp); in_subdir--; curr_dir = popdir(); if (in_subdir) { errno = 0; if ((dirp=opendir(curr_dir->dirname)) == (DIR *)NULL) { warn(curr_dir->dirname, "error opening directory (1)"); in_subdir--; } seekdir(dirp, curr_dir->where); } continue; } } else if (optind >= n_argc) { return (-1); } else { strcpy(name, n_argv[optind++]); } } if ((err = LSTAT(name, statbuf)) < 0) { warn(name, strerror()); continue; } if (!names_from_stdin && (statbuf->sb_mode & S_IFMT) == S_IFDIR) { if (in_subdir) { curr_dir->where = telldir(dirp); pushdir(curr_dir); closedir(dirp); } in_subdir++; /* Build new prototype name */ if ((curr_dir = (struct dirinfo *) mem_get(sizeof(struct dirinfo))) == (struct dirinfo *)NULL) { exit(2); } strcpy(curr_dir->dirname, name); len = strlen(curr_dir->dirname); while (len >= 1 && curr_dir->dirname[len - 1] == '/') { len--; /* Delete trailing slashes */ } curr_dir->dirname[len++] = '/'; /* Now add exactly one back */ curr_dir->dirname[len] = '\0';/* Make sure null-terminated */ curr_dir->where = 0; errno = 0; do { if ((dirp = opendir(curr_dir->dirname)) == (DIR *)NULL) { warn(curr_dir->dirname, "error opening directory (2)"); if (in_subdir > 1) { curr_dir = popdir(); } in_subdir--; err = -1; continue; } else { seekdir(dirp, curr_dir->where); } } while (in_subdir && (! dirp)); } } while (err < 0); return (0); } /* name_gather - gather names in a list for scanning. * * DESCRIPTION * * Name_gather takes names from the command line and adds them to * the name list. * * FIXME * * We could hash the names if we really care about speed here. */ #ifdef __STDC__ void name_gather(void) #else void name_gather() #endif { while (optind < n_argc) { add_name(n_argv[optind++]); } } /* pushdir - pushes a directory name on the directory stack * * DESCRIPTION * * The pushdir function puses the directory structure which is pointed * to by "info" onto a stack for later processing. The information * may be retrieved later with a call to popdir(). * * PARAMETERS * * dirinfo *info - pointer to directory structure to save */ #ifdef __STDC__ static void pushdir(struct dirinfo *info) #else static void pushdir(info) struct dirinfo *info; #endif { if (stack_head == (struct dirinfo *)NULL) { stack_head = info; stack_head->next = (struct dirinfo *)NULL; } else { info->next = stack_head; stack_head = info; } } /* popdir - pop a directory structure off the directory stack. * * DESCRIPTION * * The popdir function pops the most recently pushed directory * structure off of the directory stack and returns it to the calling * function. * * RETURNS * * Returns a pointer to the most recently pushed directory structure * or NULL if the stack is empty. */ #ifdef __STDC__ static struct dirinfo *popdir(void) #else static struct dirinfo *popdir() #endif { struct dirinfo *tmp; if (stack_head == (struct dirinfo *)NULL) { return((struct dirinfo *)NULL); } else { tmp = stack_head; stack_head = stack_head->next; } return(tmp); } /sys/src/ape/cmd/pax/names.c 664 sys sys 1367613436 5101 /* $Source: /u/mark/src/pax/RCS/names.c,v $ * * $Revision: 1.2 $ * * names.c - Look up user and/or group names. * * DESCRIPTION * * These functions support UID and GID name lookup. The results are * cached to improve performance. * * AUTHOR * * Mark H. Colburn, NAPS International (mark@jhereg.mn.org) * * Sponsored by The USENIX Association for public distribution. * * Copyright (c) 1989 Mark H. Colburn. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that the above copyright notice is duplicated in all such * forms and that any documentation, advertising materials, and other * materials related to such distribution and use acknowledge that the * software was developed * by Mark H. Colburn and sponsored by The * USENIX Association. * * 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. * * $Log: names.c,v $ * Revision 1.2 89/02/12 10:05:05 mark * 1.2 release fixes * * Revision 1.1 88/12/23 18:02:19 mark * Initial revision * */ #ifndef lint static char *ident = "$Id: names.c,v 1.2 89/02/12 10:05:05 mark Exp $"; static char *copyright = "Copyright (c) 1989 Mark H. Colburn.\nAll rights reserved.\n"; #endif /* ! lint */ /* Headers */ #include "pax.h" /* Defines */ #define myuid ( my_uid < 0? (my_uid = getuid()): my_uid ) #define mygid ( my_gid < 0? (my_gid = getgid()): my_gid ) /* Internal Identifiers */ static int saveuid = -993; static char saveuname[TUNMLEN]; static int my_uid = -993; static int savegid = -993; static char savegname[TGNMLEN]; static int my_gid = -993; /* finduname - find a user or group name from a uid or gid * * DESCRIPTION * * Look up a user name from a uid/gid, maintaining a cache. * * PARAMETERS * * char uname[] - name (to be returned to user) * int uuid - id of name to find * * * RETURNS * * Returns a name which is associated with the user id given. If there * is not name which corresponds to the user-id given, then a pointer * to a string of zero length is returned. * * FIXME * * 1. for now it's a one-entry cache. * 2. The "-993" is to reduce the chance of a hit on the first lookup. */ #ifdef __STDC__ char *finduname(int uuid) #else char *finduname(uuid) int uuid; #endif { struct passwd *pw; if (uuid != saveuid) { saveuid = uuid; saveuname[0] = '\0'; pw = getpwuid(uuid); if (pw) { strncpy(saveuname, pw->pw_name, TUNMLEN); } } return(saveuname); } /* finduid - get the uid for a given user name * * DESCRIPTION * * This does just the opposit of finduname. Given a user name it * finds the corresponding UID for that name. * * PARAMETERS * * char uname[] - username to find a UID for * * RETURNS * * The UID which corresponds to the uname given, if any. If no UID * could be found, then the UID which corrsponds the user running the * program is returned. * */ #ifdef __STDC__ int finduid(char *uname) #else int finduid(uname) char *uname; #endif { struct passwd *pw; extern struct passwd *getpwnam(); if (uname[0] != saveuname[0]/* Quick test w/o proc call */ ||0 != strncmp(uname, saveuname, TUNMLEN)) { strncpy(saveuname, uname, TUNMLEN); pw = getpwnam(uname); if (pw) { saveuid = pw->pw_uid; } else { saveuid = myuid; } } return (saveuid); } /* findgname - look up a group name from a gid * * DESCRIPTION * * Look up a group name from a gid, maintaining a cache. * * * PARAMETERS * * int ggid - goupid of group to find * * RETURNS * * A string which is associated with the group ID given. If no name * can be found, a string of zero length is returned. */ #ifdef __STDC__ char *findgname(int ggid) #else char *findgname(ggid) int ggid; #endif { struct group *gr; if (ggid != savegid) { savegid = ggid; savegname[0] = '\0'; #ifndef _POSIX_SOURCE setgrent(); #endif gr = getgrgid(ggid); if (gr) { strncpy(savegname, gr->gr_name, TGNMLEN); } } return(savegname); } /* findgid - get the gid for a given group name * * DESCRIPTION * * This does just the opposit of finduname. Given a group name it * finds the corresponding GID for that name. * * PARAMETERS * * char uname[] - groupname to find a GID for * * RETURNS * * The GID which corresponds to the uname given, if any. If no GID * could be found, then the GID which corrsponds the group running the * program is returned. * */ #ifdef __STDC__ int findgid(char *gname) #else int findgid(gname) char *gname; #endif { struct group *gr; /* Quick test w/o proc call */ if (gname[0] != savegname[0] || strncmp(gname, savegname, TUNMLEN) != 0) { strncpy(savegname, gname, TUNMLEN); gr = getgrnam(gname); if (gr) { savegid = gr->gr_gid; } else { savegid = mygid; } } return (savegid); } /sys/src/ape/cmd/pax/pass.c 664 sys sys 1367613436 3640 /* $Source: /u/mark/src/pax/RCS/pass.c,v $ * * $Revision: 1.3 $ * * pass.c - handle the pass option of cpio * * DESCRIPTION * * These functions implement the pass options in PAX. The pass option * copies files from one directory hierarchy to another. * * AUTHOR * * Mark H. Colburn, NAPS International (mark@jhereg.mn.org) * * Sponsored by The USENIX Association for public distribution. * * Copyright (c) 1989 Mark H. Colburn. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that the above copyright notice is duplicated in all such * forms and that any documentation, advertising materials, and other * materials related to such distribution and use acknowledge that the * software was developed * by Mark H. Colburn and sponsored by The * USENIX Association. * * 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. * * $Log: pass.c,v $ * Revision 1.3 89/02/12 10:29:51 mark * Fixed misspelling of Replstr * * Revision 1.2 89/02/12 10:05:09 mark * 1.2 release fixes * * Revision 1.1 88/12/23 18:02:20 mark * Initial revision * */ #ifndef lint static char *ident = "$Id: pass.c,v 1.3 89/02/12 10:29:51 mark Exp $"; static char *copyright = "Copyright (c) 1989 Mark H. Colburn.\nAll rights reserved.\n"; #endif /* ! lint */ /* Headers */ #include "pax.h" /* pass - copy within the filesystem * * DESCRIPTION * * Pass copies the named files from the current directory hierarchy to * the directory pointed to by dirname. * * PARAMETERS * * char *dirname - name of directory to copy named files to. * */ #ifdef __STDC__ int pass(char *dirname) #else int pass(dirname) char *dirname; #endif { char name[PATH_MAX + 1]; int fd; Stat sb; while (name_next(name, &sb) >= 0 && (fd = openin(name, &sb)) >= 0) { if (rplhead != (Replstr *)NULL) { rpl_name(name); } if (get_disposition("pass", name) || get_newname(name, sizeof(name))) { /* skip file... */ if (fd) { close(fd); } continue; } if (passitem(name, &sb, fd, dirname)) { close(fd); } if (f_verbose) { fprintf(stderr, "%s/%s\n", dirname, name); } } } /* passitem - copy one file * * DESCRIPTION * * Passitem copies a specific file to the named directory * * PARAMETERS * * char *from - the name of the file to open * Stat *asb - the stat block associated with the file to copy * int ifd - the input file descriptor for the file to copy * char *dir - the directory to copy it to * * RETURNS * * Returns given input file descriptor or -1 if an error occurs. * * ERRORS */ #ifdef __STDC__ int passitem(char *from, Stat *asb, int ifd, char *dir) #else int passitem(from, asb, ifd, dir) char *from; Stat *asb; int ifd; char *dir; #endif { int ofd; time_t tstamp[2]; char to[PATH_MAX + 1]; if (nameopt(strcat(strcat(strcpy(to, dir), "/"), from)) < 0) { return (-1); } if (asb->sb_nlink > 1) { linkto(to, asb); } if (f_link && islink(from, asb) == (Link *)NULL) { linkto(from, asb); } if ((ofd = openout(to, asb, islink(to, asb), 1)) < 0) { return (-1); } if (ofd > 0) { passdata(from, ifd, to, ofd); } tstamp[0] = asb->sb_atime; tstamp[1] = f_mtime ? asb->sb_mtime : time((time_t *) 0); utime(to, tstamp); return (ifd); } /sys/src/ape/cmd/pax/pathname.c 664 sys sys 1367613436 5060 /* $Source: /u/mark/src/pax/RCS/pathname.c,v $ * * $Revision: 1.2 $ * * pathname.c - directory/pathname support functions * * DESCRIPTION * * These functions provide directory/pathname support for PAX * * AUTHOR * * Mark H. Colburn, NAPS International (mark@jhereg.mn.org) * * Sponsored by The USENIX Association for public distribution. * * Copyright (c) 1989 Mark H. Colburn. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that the above copyright notice is duplicated in all such * forms and that any documentation, advertising materials, and other * materials related to such distribution and use acknowledge that the * software was developed * by Mark H. Colburn and sponsored by The * USENIX Association. * * 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. * * $Log: pathname.c,v $ * Revision 1.2 89/02/12 10:05:13 mark * 1.2 release fixes * * Revision 1.1 88/12/23 18:02:21 mark * Initial revision * */ #ifndef lint static char *ident = "$Id: pathname.c,v 1.2 89/02/12 10:05:13 mark Exp $"; static char *copyright = "Copyright (c) 1989 Mark H. Colburn.\nAll rights reserved.\n"; #endif /* ! lint */ /* Headers */ #include "pax.h" /* dirneed - checks for the existance of directories and possibly create * * DESCRIPTION * * Dirneed checks to see if a directory of the name pointed to by name * exists. If the directory does exist, then dirneed returns 0. If * the directory does not exist and the f_dir_create flag is set, * then dirneed will create the needed directory, recursively creating * any needed intermediate directory. * * If f_dir_create is not set, then no directories will be created * and a value of -1 will be returned if the directory does not * exist. * * PARAMETERS * * name - name of the directory to create * * RETURNS * * Returns a 0 if the creation of the directory succeeded or if the * directory already existed. If the f_dir_create flag was not set * and the named directory does not exist, or the directory creation * failed, a -1 will be returned to the calling routine. */ #ifdef __STDC__ int dirneed(char *name) #else int dirneed(name) char *name; #endif { char *cp; char *last; int ok; static Stat sb; last = (char *)NULL; for (cp = name; *cp;) { if (*cp++ == '/') { last = cp; } } if (last == (char *)NULL) { return (STAT(".", &sb)); } *--last = '\0'; ok = STAT(*name ? name : ".", &sb) == 0 ? ((sb.sb_mode & S_IFMT) == S_IFDIR) : (f_dir_create && dirneed(name) == 0 && dirmake(name, &sb) == 0); *last = '/'; return (ok ? 0 : -1); } /* nameopt - optimize a pathname * * DESCRIPTION * * Confused by "/.." twistiness. Returns the number of final * pathname elements (zero for "/" or ".") or -1 if unsuccessful. * * PARAMETERS * * char *begin - name of the path to optimize * * RETURNS * * Returns 0 if successful, non-zero otherwise. * */ #ifdef __STDC__ int nameopt(char *begin) #else int nameopt(begin) char *begin; #endif { char *name; char *item; int idx; int absolute; char *element[PATHELEM]; absolute = (*(name = begin) == '/'); idx = 0; for (;;) { if (idx == PATHELEM) { warn(begin, "Too many elements"); return (-1); } while (*name == '/') { ++name; } if (*name == '\0') { break; } element[idx] = item = name; while (*name && *name != '/') { ++name; } if (*name) { *name++ = '\0'; } if (strcmp(item, "..") == 0) { if (idx == 0) { if (!absolute) { ++idx; } } else if (strcmp(element[idx - 1], "..") == 0) { ++idx; } else { --idx; } } else if (strcmp(item, ".") != 0) { ++idx; } } if (idx == 0) { element[idx++] = absolute ? "" : "."; } element[idx] = (char *)NULL; name = begin; if (absolute) { *name++ = '/'; } for (idx = 0; item = element[idx]; ++idx, *name++ = '/') { while (*item) { *name++ = *item++; } } *--name = '\0'; return (idx); } /* dirmake - make a directory * * DESCRIPTION * * Dirmake makes a directory with the appropritate permissions. * * PARAMETERS * * char *name - Name of directory make * Stat *asb - Stat structure of directory to make * * RETURNS * * Returns zero if successful, -1 otherwise. * */ #ifdef __STDC__ int dirmake(char *name, Stat *asb) #else int dirmake(name, asb) char *name; Stat *asb; #endif { if (mkdir(name, (int) (asb->sb_mode & S_IPOPN)) < 0) { return (-1); } if (asb->sb_mode & S_IPEXE) { chmod(name, (int) (asb->sb_mode & S_IPERM)); } if (f_owner) { chown(name, (int) asb->sb_uid, (int) asb->sb_gid); } return (0); } /sys/src/ape/cmd/pax/pax.1 664 sys sys 1367613436 13626 .\" $Id: pax.1,v 1.2 89/02/12 10:08:47 mark Exp $ .TH PAX 1 "USENIX Association" "" .SH NAME pax \- portable archive exchange .SH SYNOPSIS .TP \w'\fBpax\fR\ 'u .B pax .RB [ \-cimopuvy ] .RI "[\fB\-f\fR" " archive" ] .RI "[\fB\-s\fR" " replstr" ] .RI "[\fB\-t\fR" " device" ] .RI [ pattern... ] .TP \w'\fBpax\ \-r\fR\ 'u .B "pax\ \-r" .RB [ \-cimnopuvy ] .RI "[\fB\-f\fR" " archive" ] .RI "[\fB\-s\fR" " replstr" ] .RI "[\fB\-t\fR" " device" ] .RI [ pattern... ] .TP \w'\fBpax\ \-w\fR\ 'u .B "pax\ \-w" .RB [ \-adimuvy ] .RI "[\fB\-b\fR" " blocking" ] .RI "[\fB\-f\fR" " archive" ] .RI "[\fB\-s\fR" " replstr" ] .RI "[\fB\-t\fR" " device" ] .RI "[\fB\-x\fR" " format" ] .RI [ pathname... ] .TP \w'\fBpax\ \-rw\fR\ 'u .B "pax\ \-rw" .RB [ \-ilmopuvy ] .RI "[\fB\-s\fR" " replstr" ] .RI [ pathname... ] directory .SH DESCRIPTION .I Pax reads and writes archive files which conform to the .B "Archive/Interchange File Format" specified in .IR "IEEE Std. 1003.1-1988" . .I Pax can also read, but not write, a number of other file formats in addition to those specified in the .B "Archive/Interchange File Format" description. Support for these traditional file formats, such as V7 .I "tar" and System V binary .I "cpio" format archives, is provided for backward compatibility and to maximize portability. .PP .I Pax will also support traditional .I cpio and System V .I tar interfaces if invoked with the name "cpio" or "tar" respectively. See the .I cpio(1) or .I tar(1) manual pages for more details. .PP Combinations of the .B \-r and .B \-w command line arguments specify whether .I pax will read, write or list the contents of the specified archive, or move the specified files to another directory. .PP The command line arguments are: .TP .5i .B \-w writes the files and directories specified by .I pathname operands to the standard output together with the pathname and status information prescribed by the archive format used. A directory .I pathname operand refers to the files and (recursively) subdirectories of that directory. If no .I pathname operands are given, then the standard input is read to get a list of pathnames to copy, one pathname per line. In this case, only those pathnames appearing on the standard input are copied. .TP .5i .B \-r .I Pax reads an archive file from the standard input. Only files with names that match any of the .I pattern operands are selected for extraction. The selected files are conditionally created and copied relative to the current directory tree, subject to the options described below. By default, the owner and group of selected files will be that of the invoking process, and the permissions and modification times will be the sames as those in the archive. .RS .5i .PP The supported archive formats are automatically detected on input. The default output format is .IR ustar , but may be overridden by the .B \-x .I format option described below. .RE .TP .5i .B \-rw .I Pax reads the files and directories named in the .I pathname operands and copies them to the destination .IR directory . A directory .I pathname operand refers to the files and (recursively) subdirectories of that directory. If no .I pathname operands are given, the standard input is read to get a list of pathnames to copy, one pathname per line. In this case, only those pathnames appearing on the standard input are copied. The directory named by the .I directory operand must exist and have the proper permissions before the copy can occur. .PP If neither the .BR \-r " or " \-w options are given, then .I pax will list the contents of the specified archive. In this mode, .I pax lists normal files one per line, hard link pathnames as .sp .RS 1i .IR pathname " == " linkname .RE .sp and symbolic link pathnames (if supported by the implementation) as .sp .RS 1i .IR pathname " -> " linkname .RE .sp where .I pathname is the name of the file being extracted, and .I linkname is the name of a file which appeared earlier in the archive. .PP If the .B \-v option is specified, then .I pax list normal pathnames in the same format used by the .I ls utility with the .B \-l option. Hard links are shown as .sp .RS 1i .IR "" " == " linkname .RE .sp and symbolic links (if supported) are shown as .sp .RS 1i .IR "" " -> " linkname .RE .sp .PP .I Pax is capable of reading and writing archives which span multiple physical volumes. Upon detecting an end of medium on an archive which is not yet completed, .I pax will prompt the user for the next volume of the archive and will allow the user to specify the location of the next volume. .SS Options The following options are available: .TP 1i .B \-a The files specified by .I pathname are appended to the specified archive. .TP 1i .BI \-b " blocking" Block the output at .I blocking bytes per write to the archive file. A .B k suffix multiplies .I blocking by 1024, a .B b suffix multiplies .I blocking by 512 and a .B m suffix multiplies .I blocking by 1048576 (1 megabyte). For machines with 16-bit int's (VAXen, XENIX-286, etc.), the maximum buffer size is 32k-1. If not specified, .I blocking is automatically determined on input and is ignored for .B \-rw. .TP 1i .B \-c Complement the match sense of the .I pattern operands. .TP 1i .B \-d Intermediate directories not explicitly listed in the archive are not created. This option is ignored unless the .B \-r option is specified. .TP 1i .BI \-f " archive" The .I archive option specifies the pathname of the input or output archive, overriding the default of standard input for .B \-r or standard output for .BR \-w . .TP 1i .B \-i Interactively rename files. Substitutions specified by .B \-s options (described below) are performed before requesting the new file name from the user. A file is skipped if an empty line is entered and .I pax exits with an exit status of 0 if .B EOF is encountered. .TP 1i .B \-l Files are linked rather than copied when possible. .TP 1i .B \-m File modification times are not retained. .TP 1i .B \-n When .B \-r is specified, but .B \-w is not, the .I pattern arguments are treated as ordinary file names. Only the first occurrence of each of these files in the input archive is read. The .B pax utility exits with a zero exit status after all files in the list have been read. If one or more files in the list is not found, .B pax writes a diagnostic to standard error for each of the files and exits with a non-zero exit status. the file names are compared before any of the .BR \-i , .BR \-s , or .B \-y options are applied. .TP 1i .B \-o Restore file ownership as specified in the archive. The invoking process must have appropriate privileges to accomplish this. .TP 1i .B \-p Preserve the access time of the input files after they have been copied. .TP 1i .BI \-s " replstr" File names are modified according to the substitution expression using the syntax of .I "ed(1)" as shown: .sp .RS 2i -s /\fIold\fR/\fInew\fR/\fB[\fRgp\fB]\fR .RE .RS 1i .PP Any non null character may be used as a delimiter (a / is used here as an example). Multiple .B \-s expressions may be specified; the expressions are applied in the order specified terminating with the first successful substitution. The optional trailing .B p causes successful mappings to be listed on standard error. The optional trailing .B g causes the .I old expression to be replaced each time it occurs in the source string. Files that substitute to an empty string are ignored both on input and output. .RE .TP 1i .BI \-t " device" The .I device option argument is an implementation-defined identifier that names the input or output archive device, overriding the default of standard input for .B \-r and standard output for .BR \-w . .TP 1i .B \-u Copy each file only if it is newer than a pre-existing file with the same name. This implies .BR \-a . .TP 1i .B \-v List file names as they are encountered. Produces a verbose table of contents listing on the standard output when both .B \-r and .B \-w are omitted, otherwise the file names are printed to standard error as they are encountered in the archive. .TP 1i .BI \-x " format" Specifies the output archive .IR format . The input format, which must be one of the following, is automatically determined when the .B \-r option is used. The supported formats are: .RS 1i .TP 0.75i .I cpio The extended .I CPIO interchange format specified in .B "Extended CPIO Format" in .I "IEEE Std. 1003.1-1988." .TP 0.75i .I ustar The extended .I TAR interchange format specified in .B "Extended TAR Format" in .I "IEEE Std. 1003.1-1988." This is the default archive format. .RE .TP 1i .B \-y Interactively prompt for the disposition of each file. Substitutions specified by .B \-s options (described above) are performed before prompting the user for disposition. .B EOF or an input line starting with the character .B q caused .I pax to exit. Otherwise, an input line starting with anything other than .B y causes the file to be ignored. This option cannot be used in conjunction with the .B \-i option. .PP Only the last of multiple .B \-f or .B \-t options take effect. .PP When writing to an archive, the standard input is used as a list of pathnames if no .I pathname operands are specified. The format is one pathname per line. Otherwise, the standard input is the archive file, which is formatted according to one of the specifications in .B "Archive/Interchange File format" in .IR "IEEE Std. 1003.1-1988" , or some other implementation-defined format. .PP The user ID and group ID of the process, together with the appropriate privileges, affect the ability of .I pax to restore ownership and permissions attributes of the archived files. (See .I "format-reading utility" in .B "Archive/Interchange File Format" in .IR "IEEE Std. 1003.1-1988" ".)" .PP The options .BR \-a , .BR \-c , .BR \-d , .BR \-i , .BR \-l , .BR \-p , .BR \-t , .BR \-u , and .BR \-y are provided for functional compatibility with the historical .I cpio and .I tar utilities. The option defaults were chosen based on the most common usage of these options, therefore, some of the options have meanings different than those of the historical commands. .SS Operands The following operands are available: .TP 1i .I directory The destination directory pathname for copies when both the .B \-r and .B \-w options are specified. The directory must exist and be writable before the copy or and error results. .TP 1i .I pathname A file whose contents are used instead of the files named on the standard input. When a directory is named, all of its files and (recursively) subdirectories are copied as well. .TP 1i .IR pattern A .I pattern is given in the standard shell pattern matching notation. The default if no .I pattern is specified is .BR * \, which selects all files. .SH EXAMPLES The following command .sp .RS 1i pax \-w \-f /dev/rmt0 \. .RE .sp copies the contents of the current directory to tape drive 0. .PP The commands .sp .RS 1i .RI mkdir " newdir" .br .RI cd " olddir" .br .RI "pax -rw . " newdir .RE .sp copies the contents of .I olddir to .I newdir . .PP The command .sp .RS 1i pax \-r \-s ',//*usr//*,,' -f pax.out .RE .sp reads the archive .B pax.out with all files rooted in "/usr" in the archive extracted relative to the current directory. .SH FILES .TP 1i /dev/tty used to prompt the user for information when the .BR \-i " or " \-y options are specified. .SH "SEE ALSO" cpio(1), find(1), tar(1), cpio(5), tar(5) .SH DIAGNOSTICS .I Pax will terminate immediately, without processing any additional files on the command line or in the archive. .SH "EXIT CODES" .I Pax will exit with one of the following values: .IP 0 .5i All files in the archive were processed successfully. .IP ">0" .5i .I Pax aborted due to errors encountered during operation. .SH BUGS Special permissions may be required to copy or extract special files. .PP Device, user ID, and group ID numbers larger than 65535 cause additional header records to be output. These records are ignored by some historical version of .I "cpio(1)" and .IR "tar(1)" . .PP The archive formats described in .B "Archive/Interchange File Format" have certain restrictions that have been carried over from historical usage. For example, there are restrictions on the length of pathnames stored in the archive. .PP When getting an "ls -l" style listing on .I tar format archives, link counts are listed as zero since the .I ustar archive format does not keep link count information. .PP On 16 bit architectures, the largest buffer size is 32k-1. This is due, in part, to using integers in the buffer allocation schemes, however, on many of these machines, it is not possible to allocate blocks of memory larger than 32k. .SH COPYRIGHT Copyright (c) 1989 Mark H. Colburn. .br All rights reserved. .PP Redistribution and use in source and binary forms are permitted provided that the above copyright notice is duplicated in all such forms and that any documentation, advertising materials, and other materials related to such distribution and use acknowledge that the software was developed by Mark H. Colburn and sponsored by The USENIX Association. .PP THE 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. .SH AUTHOR Mark H. Colburn .br Minnetech Consulting, Inc. .br 117 Mackubin Street, Suite 1 .br St. Paul, MN 55102 .br mark@jhereg.MN.ORG .sp 2 Sponsored by .B "The USENIX Association" for public distribution. /sys/src/ape/cmd/pax/pax.c 664 sys sys 1367613436 13061 /* $Source: /u/mark/src/pax/RCS/pax.c,v $ * * $Revision: 1.2 $ * * DESCRIPTION * * Pax is the archiver described in IEEE P1003.2. It is an archiver * which understands both tar and cpio archives and has a new interface. * * SYNOPSIS * * pax -[cimopuvy] [-f archive] [-s replstr] [-t device] [pattern...] * pax -r [-cimopuvy] [-f archive] [-s replstr] [-t device] [pattern...] * pax -w [-adimuvy] [-b blocking] [-f archive] [-s replstr]...] * [-t device][-x format][pathname...] * pax -r -w [-ilmopuvy][-s replstr][pathname...] directory * * DESCRIPTION * * PAX - POSIX conforming tar and cpio archive handler. This * program implements POSIX conformant versions of tar, cpio and pax * archive handlers for UNIX. These handlers have defined befined * by the IEEE P1003.2 commitee. * * COMPILATION * * A number of different compile time configuration options are * available, please see the Makefile and config.h for more details. * * AUTHOR * * Mark H. Colburn, NAPS International (mark@jhereg.mn.org) * * * Sponsored by The USENIX Association for public distribution. * * Copyright (c) 1989 Mark H. Colburn. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that the above copyright notice is duplicated in all such * forms and that any documentation, advertising materials, and other * materials related to such distribution and use acknowledge that the * software was developed * by Mark H. Colburn and sponsored by The * USENIX Association. * * 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. * * $Log: pax.c,v $ * Revision 1.2 89/02/12 10:05:17 mark * 1.2 release fixes * * Revision 1.1 88/12/23 18:02:23 mark * Initial revision * */ #ifndef lint static char *ident = "$Id: pax.c,v 1.2 89/02/12 10:05:17 mark Exp $"; static char *copyright = "Copyright (c) 1989 Mark H. Colburn.\nAll rights reserved.\n"; #endif /* ! lint */ /* Headers */ #define NO_EXTERN #include "pax.h" /* Globally Available Identifiers */ char *ar_file; /* File containing name of archive */ char *bufend; /* End of data within archive buffer */ char *bufstart; /* Archive buffer */ char *bufidx; /* Archive buffer index */ char *myname; /* name of executable (argv[0]) */ char **n_argv; /* Argv used by name routines */ int n_argc; /* Argc used by name routines */ int archivefd; /* Archive file descriptor */ int blocking; /* Size of each block, in records */ int gid; /* Group ID */ int head_standard; /* true if archive is POSIX format */ int ar_interface; /* defines interface we are using */ int ar_format; /* defines current archve format */ int mask; /* File creation mask */ int ttyf; /* For interactive queries */ int uid; /* User ID */ int names_from_stdin; /* names for files are from stdin */ OFFSET total; /* Total number of bytes transferred */ short f_access_time; /* Reset access times of input files */ short areof; /* End of input volume reached */ short f_dir_create; /* Create missing directories */ short f_append; /* Add named files to end of archive */ short f_create; /* create a new archive */ short f_extract; /* Extract named files from archive */ short f_follow_links; /* follow symbolic links */ short f_interactive; /* Interactivly extract files */ short f_linksleft; /* Report on unresolved links */ short f_list; /* List files on the archive */ short f_modified; /* Don't restore modification times */ short f_verbose; /* Turn on verbose mode */ short f_link; /* link files where possible */ short f_owner; /* extract files as the user */ short f_pass; /* pass files between directories */ short f_newer; /* append files to archive if newer */ short f_disposition; /* ask for file disposition */ short f_reverse_match; /* Reverse sense of pattern match */ short f_mtime; /* Retain file modification time */ short f_unconditional; /* Copy unconditionally */ time_t now = 0; /* Current time */ uint arvolume; /* Volume number */ uint blocksize = BLOCKSIZE; /* Archive block size */ FILE *msgfile; /* message outpu file stdout/stderr */ Replstr *rplhead = (Replstr *)NULL; /* head of replstr list */ Replstr *rpltail; /* pointer to tail of replstr list */ /* Function Prototypes */ #ifdef __STDC__ static void usage(void); static OFFSET pax_optsize(char *); #else /* !__STDC__ */ static void usage(); static OFFSET pax_optsize(); #endif /* __STDC__ */ /* main - main routine for handling all archive formats. * * DESCRIPTION * * Set up globals and call the proper interface as specified by the user. * * PARAMETERS * * int argc - count of user supplied arguments * char **argv - user supplied arguments * * RETURNS * * Returns an exit code of 0 to the parent process. */ #ifdef __STDC__ int main(int argc, char **argv) #else int main(argc, argv) int argc; char **argv; #endif { /* strip the pathname off of the name of the executable */ if ((myname = strrchr(argv[0], '/')) != (char *)NULL) { myname++; } else { myname = argv[0]; } /* set upt for collecting other command line arguments */ name_init(argc, argv); /* get all our necessary information */ mask = umask(0); uid = getuid(); gid = getgid(); now = time((time_t *) 0); /* open terminal for interactive queries */ ttyf = open_tty(); if (strcmp(myname, "tar")==0) { do_tar(argc, argv); } else if (strcmp(myname, "cpio")==0) { do_cpio(argc, argv); } else { do_pax(argc, argv); } exit(0); /* NOTREACHED */ } /* do_pax - provide a PAX conformant user interface for archive handling * * DESCRIPTION * * Process the command line parameters given, doing some minimal sanity * checking, and then launch the specified archiving functions. * * PARAMETERS * * int ac - A count of arguments in av. Should be passed argc * from main * char **av - A pointer to an argument list. Should be passed * argv from main * * RETURNS * * Normally returns 0. If an error occurs, -1 is returned * and state is set to reflect the error. * */ #ifdef __STDC__ int do_pax(int ac, char **av) #else int do_pax(ac, av) int ac; /* argument counter */ char **av; /* arguments */ #endif { int c; char *dirname; Stat st; /* default input/output file for PAX is STDIN/STDOUT */ ar_file = "-"; /* * set up the flags to reflect the default pax inteface. Unfortunately * the pax interface has several options which are completely opposite * of the tar and/or cpio interfaces... */ f_unconditional = 1; f_mtime = 1; f_dir_create = 1; f_list = 1; blocksize = 0; blocking = 0; ar_interface = PAX; ar_format = TAR; /* default interface if none given for -w */ msgfile=stdout; while ((c = getopt(ac, av, "ab:cdf:ilmoprs:t:uvwx:y")) != EOF) { switch (c) { case 'a': f_append = 1; f_list = 0; break; case 'b': if ((blocksize = pax_optsize(optarg)) == 0) { fatal("Bad block size"); } break; case 'c': f_reverse_match = 1; break; case 'd': f_dir_create = 0; break; case 'f': if (blocksize == 0) { blocking = 1; blocksize = 1 * BLOCKSIZE; } ar_file = optarg; break; case 'i': f_interactive = 1; break; case 'l': f_link = 1; break; case 'm': f_mtime = 0; break; case 'o': f_owner = 1; break; case 'p': f_access_time = 1; break; case 'r': if (f_create) { f_create = 0; f_pass = 1; } else { f_list = 0; f_extract = 1; } msgfile=stderr; break; case 's': add_replstr(optarg); break; case 't': if (blocksize == 0) { blocking = 1; blocksize = 10 * BLOCKSIZE; } ar_file = optarg; break; case 'u': f_unconditional = 1; break; case 'v': f_verbose = 1; break; case 'w': if (f_extract) { f_extract = 0; f_pass = 1; } else { f_list = 0; f_create = 1; } msgfile=stderr; break; case 'x': if (strcmp(optarg, "ustar") == 0) { ar_format = TAR; } else if (strcmp(optarg, "cpio") == 0) { ar_format = CPIO; } else { usage(); } break; case 'y': f_disposition = 1; break; default: usage(); } } if (blocksize == 0) { blocking = 1; blocksize = blocking * BLOCKSIZE; } buf_allocate((OFFSET) blocksize); if (f_extract || f_list) { open_archive(AR_READ); get_archive_type(); read_archive(); } else if (f_create) { if (optind >= n_argc) { names_from_stdin++; /* args from stdin */ } open_archive(AR_WRITE); create_archive(); } else if (f_append) { open_archive(AR_APPEND); get_archive_type(); append_archive(); } else if (f_pass && optind < n_argc) { dirname = n_argv[--n_argc]; if (LSTAT(dirname, &st) < 0) { fatal(strerror()); } if ((st.sb_mode & S_IFMT) != S_IFDIR) { fatal("Not a directory"); } if (optind >= n_argc) { names_from_stdin++; /* args from stdin */ } pass(dirname); } else { usage(); } return (0); } /* get_archive_type - determine input archive type from archive header * * DESCRIPTION * * reads the first block of the archive and determines the archive * type from the data. If the archive type cannot be determined, * processing stops, and a 1 is returned to the caller. If verbose * mode is on, then the archive type will be printed on the standard * error device as it is determined. * * FIXME * * be able to understand TAR and CPIO magic numbers */ #ifdef __STDC__ void get_archive_type(void) #else void get_archive_type() #endif { if (ar_read() != 0) { fatal("Unable to determine archive type."); } if (strncmp(bufstart, "070707", 6) == 0) { ar_format = CPIO; if (f_verbose) { fputs("CPIO format archive\n", stderr); } } else if (strncmp(&bufstart[257], "ustar", 5) == 0) { ar_format = TAR; if (f_verbose) { fputs("USTAR format archive\n", stderr); } } else { ar_format = TAR; } } /* pax_optsize - interpret a size argument * * DESCRIPTION * * Recognizes suffixes for blocks (512-bytes), k-bytes and megabytes. * Also handles simple expressions containing '+' for addition. * * PARAMETERS * * char *str - A pointer to the string to interpret * * RETURNS * * Normally returns the value represented by the expression in the * the string. * * ERRORS * * If the string cannot be interpretted, the program will fail, since * the buffering will be incorrect. * */ #ifdef __STDC__ static OFFSET pax_optsize(char *str) #else static OFFSET pax_optsize(str) char *str; /* pointer to string to interpret */ #endif { char *idx; OFFSET number; /* temporary storage for current number */ OFFSET result; /* cumulative total to be returned to caller */ result = 0; idx = str; for (;;) { number = 0; while (*idx >= '0' && *idx <= '9') number = number * 10 + *idx++ - '0'; switch (*idx++) { case 'b': result += number * 512L; continue; case 'k': result += number * 1024L; continue; case 'm': result += number * 1024L * 1024L; continue; case '+': result += number; continue; case '\0': result += number; break; default: break; } break; } if (*--idx) { fatal("Unrecognizable value"); } return (result); } /* usage - print a helpful message and exit * * DESCRIPTION * * Usage prints out the usage message for the PAX interface and then * exits with a non-zero termination status. This is used when a user * has provided non-existant or incompatible command line arguments. * * RETURNS * * Returns an exit status of 1 to the parent process. * */ #ifdef __STDC__ static void usage(void) #else static void usage() #endif { fprintf(stderr, "Usage: %s -[cimopuvy] [-f archive] [-s replstr] [-t device] [pattern...]\n", myname); fprintf(stderr, " %s -r [-cimopuvy] [-f archive] [-s replstr] [-t device] [pattern...]\n", myname); fprintf(stderr, " %s -w [-adimuvy] [-b blocking] [-f archive] [-s replstr]\n [-t device] [-x format] [pathname...]\n", myname); fprintf(stderr, " %s -r -w [-ilmopuvy] [-s replstr] [pathname...] directory\n", myname); exit(1); } /sys/src/ape/cmd/pax/pax.h 664 sys sys 1367613436 10247 /* $Source: /u/mark/src/pax/RCS/pax.h,v $ * * $Revision: 1.2 $ * * pax.h - defnitions for entire program * * DESCRIPTION * * This file contains most all of the definitions required by the PAX * software. This header is included in every source file. * * AUTHOR * * Mark H. Colburn, NAPS International (mark@jhereg.mn.org) * * Sponsored by The USENIX Association for public distribution. * * Copyright (c) 1989 Mark H. Colburn. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that the above copyright notice and this paragraph are * duplicated in all such forms and that any documentation, * advertising materials, and other materials related to such * distribution and use acknowledge that the software was developed * by Mark H. Colburn and sponsored by The USENIX Association. * * 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. */ #ifndef _PAX_H #define _PAX_H /* Headers */ #include "config.h" #include "limits.h" #include #include #include #include #include #ifndef _POSIX_SOURCE #include #endif #include #include "regexp.h" #if defined(DIRENT) || defined(_POSIX_SOURCE) # ifdef PAXDIR # include "paxdir.h" # else # include # endif #else # ifdef hpux # include # else # ifdef XENIX_286 # include # else XENIX_286 # include # endif XENIX_286 # endif /* hpux */ # define dirent direct #endif #ifdef _POSIX_SOURCE #define major(x) 0 #define minor(x) 0 #else #ifndef major # include #endif /* major */ #endif #ifdef SYSTIME # include #else /* SYSTIME */ # include #endif /* SYSTIME */ #ifndef V7 # include #endif #ifdef XENIX # include #endif #ifdef XENIX_286 #include #endif XENIX_286 #include #include #ifndef XENIX_286 #ifndef _POSIX_SOURCE #include #endif #endif /* XENIX_286 */ #ifdef _POSIX_SOURCE #include #include #endif /* Defines */ #define STDIN 0 /* Standard input file descriptor */ #define STDOUT 1 /* Standard output file descriptor */ /* * Open modes; there is no with v7 UNIX and other versions of * UNIX may not have all of these defined... */ #ifndef O_RDONLY # define O_RDONLY 0 #endif #ifndef O_WRONLY # define O_WRONLY 1 #endif #ifndef O_RDWR # define O_WRONLY 2 #endif #ifndef O_BINARY # define O_BINARY 0 #endif #ifndef NULL # define NULL 0 #endif #define TMAGIC "ustar" /* ustar and a null */ #define TMAGLEN 6 #define TVERSION "00" /* 00 and no null */ #define TVERSLEN 2 /* Values used in typeflag field */ #define REGTYPE '0' /* Regular File */ #define AREGTYPE '\0' /* Regular File */ #define LNKTYPE '1' /* Link */ #define SYMTYPE '2' /* Reserved */ #define CHRTYPE '3' /* Character Special File */ #define BLKTYPE '4' /* Block Special File */ #define DIRTYPE '5' /* Directory */ #define FIFOTYPE '6' /* FIFO */ #define CONTTYPE '7' /* Reserved */ #define BLOCKSIZE 512 /* all output is padded to 512 bytes */ #define uint unsigned int /* Not always in types.h */ #define ushort unsigned short /* Not always in types.h */ #define BLOCK 5120 /* Default archive block size */ #define H_COUNT 10 /* Number of items in ASCII header */ #define H_PRINT "%06o%06o%06o%06o%06o%06o%06o%011lo%06o%011lo" #define H_SCAN "%6ho%6ho%6ho%6ho%6ho%6ho%6ho%11lo%6o%11lo" #define H_STRLEN 70 /* ASCII header string length */ #define M_ASCII "070707" /* ASCII magic number */ #define M_BINARY 070707 /* Binary magic number */ #define M_STRLEN 6 /* ASCII magic number length */ #define PATHELEM 256 /* Pathname element count limit */ #define S_IFSHF 12 /* File type shift (shb in stat.h) */ #define S_IPERM 07777 /* File permission bits (shb in stat.h) */ #define S_IPEXE 07000 /* Special execution bits (shb in stat.h) */ #define S_IPOPN 0777 /* Open access bits (shb in stat.h) */ #ifdef _POSIX_SOURCE /* hack: depend on knowing bits out of stat.h for S_ISREG, etc. */ #define S_IFMT 0170000 #define S_IFREG 0100000 #define S_IFDIR 0040000 #define S_IFCHR 0020000 #define S_IFBLK 0060000 #define S_IFIFO 0010000 #endif /* * Trailer pathnames. All must be of the same length. */ #define TRAILER "TRAILER!!!" /* Archive trailer (cpio compatible) */ #define TRAILZ 11 /* Trailer pathname length (including null) */ #include "port.h" #define TAR 1 #define CPIO 2 #define PAX 3 #define AR_READ 0 #define AR_WRITE 1 #define AR_EXTRACT 2 #define AR_APPEND 4 /* * Header block on tape. */ #define NAMSIZ 100 #define PFIXSIZ 155 #define TUNMLEN 32 #define TGNMLEN 32 /* The checksum field is filled with this while the checksum is computed. */ #define CHKBLANKS " " /* 8 blanks, no null */ /* * Exit codes from the "tar" program */ #define EX_SUCCESS 0 /* success! */ #define EX_ARGSBAD 1 /* invalid args */ #define EX_BADFILE 2 /* invalid filename */ #define EX_BADARCH 3 /* bad archive */ #define EX_SYSTEM 4 /* system gave unexpected error */ #define ROUNDUP(a,b) (((a) % (b)) == 0 ? (a) : ((a) + ((b) - ((a) % (b))))) /* * Mininum value. */ #define MIN(a, b) (((a) < (b)) ? (a) : (b)) /* * Remove a file or directory. */ #ifdef _POSIX_SOURCE #define REMOVE(name, asb) \ (S_ISDIR((asb)->sb_mode)? rmdir(name) : unlink(name)) #else #define REMOVE(name, asb) \ (((asb)->sb_mode & S_IFMT) == S_IFDIR ? rmdir(name) : unlink(name)) #endif /* * Cast and reduce to unsigned short. */ #define USH(n) (((ushort) (n)) & 0177777) /* Type Definitions */ /* * Binary archive header (obsolete). */ typedef struct { short b_dev; /* Device code */ ushort b_ino; /* Inode number */ ushort b_mode; /* Type and permissions */ ushort b_uid; /* Owner */ ushort b_gid; /* Group */ short b_nlink; /* Number of links */ short b_rdev; /* Real device */ ushort b_mtime[2]; /* Modification time (hi/lo) */ ushort b_name; /* Length of pathname (with null) */ ushort b_size[2]; /* Length of data */ } Binary; /* * File status with symbolic links. Kludged to hold symbolic link pathname * within structure. */ typedef struct { struct stat sb_stat; char sb_link[PATH_MAX + 1]; } Stat; #define STAT(name, asb) stat(name, &(asb)->sb_stat) #define FSTAT(fd, asb) fstat(fd, &(asb)->sb_stat) #define sb_dev sb_stat.st_dev #define sb_ino sb_stat.st_ino #define sb_mode sb_stat.st_mode #define sb_nlink sb_stat.st_nlink #define sb_uid sb_stat.st_uid #define sb_gid sb_stat.st_gid #define sb_rdev sb_stat.st_rdev #define sb_size sb_stat.st_size #define sb_atime sb_stat.st_atime #define sb_mtime sb_stat.st_mtime #define sb_ctime sb_stat.st_ctime #ifdef S_IFLNK # define LSTAT(name, asb) lstat(name, &(asb)->sb_stat) # define sb_blksize sb_stat.st_blksize # define sb_blocks sb_stat.st_blocks #else /* S_IFLNK */ /* * File status without symbolic links. */ # define LSTAT(name, asb) stat(name, &(asb)->sb_stat) #endif /* S_IFLNK */ /* * Hard link sources. One or more are chained from each link structure. */ typedef struct name { struct name *p_forw; /* Forward chain (terminated) */ struct name *p_back; /* Backward chain (circular) */ char *p_name; /* Pathname to link from */ } Path; /* * File linking information. One entry exists for each unique file with with * outstanding hard links. */ typedef struct link { struct link *l_forw; /* Forward chain (terminated) */ struct link *l_back; /* Backward chain (terminated) */ dev_t l_dev; /* Device */ ino_t l_ino; /* Inode */ ushort l_nlink; /* Unresolved link count */ OFFSET l_size; /* Length */ char *l_name; /* pathname to link from */ Path *l_path; /* Pathname which link to l_name */ } Link; /* * Structure for ed-style replacement strings (-s option). */ typedef struct replstr { regexp *comp; /* compiled regular expression */ char *replace; /* replacement string */ char print; /* >0 if we are to print replacement */ char global; /* >0 if we are to replace globally */ struct replstr *next; /* pointer to next record */ } Replstr; /* * This has to be included here to insure that all of the type * delcarations are declared for the prototypes. */ #ifndef STRERROR /* boofheads have a different interface than standard, so rename */ #define strerror xstrerror #endif #include "func.h" #ifndef NO_EXTERN /* Globally Available Identifiers */ extern char *ar_file; extern char *bufend; extern char *bufstart; extern char *bufidx; extern char *myname; extern int archivefd; extern int blocking; extern uint blocksize; extern int gid; extern int head_standard; extern int ar_interface; extern int ar_format; extern int mask; extern int ttyf; extern int uid; extern OFFSET total; extern short areof; extern short f_append; extern short f_create; extern short f_extract; extern short f_follow_links; extern short f_interactive; extern short f_linksleft; extern short f_list; extern short f_modified; extern short f_verbose; extern short f_link; extern short f_owner; extern short f_access_time; extern short f_pass; extern short f_pass; extern short f_disposition; extern short f_reverse_match; extern short f_mtime; extern short f_dir_create; extern short f_unconditional; extern short f_newer; extern time_t now; extern uint arvolume; extern int names_from_stdin; extern Replstr *rplhead; extern Replstr *rpltail; extern char **n_argv; extern int n_argc; extern FILE *msgfile; #endif /* NO_EXTERN */ extern char *optarg; extern int optind; #ifndef _POSIX_SOURCE extern int sys_nerr; extern char *sys_errlist[]; #endif extern int errno; #endif /* _PAX_H */ /sys/src/ape/cmd/pax/paxdir.c 664 sys sys 1367613436 17106 /* opendir -- open a directory stream last edit: 16-Jun-1987 D A Gwyn */ #include #include #include #include "paxdir.h" #ifdef BSD_SYSV /* -- definitions for 4.2,4.3BSD directories last edit: 25-Apr-1987 D A Gwyn A directory consists of some number of blocks of DIRBLKSIZ bytes each, where DIRBLKSIZ is chosen such that it can be transferred to disk in a single atomic operation (e.g., 512 bytes on most machines). Each DIRBLKSIZ-byte block contains some number of directory entry structures, which are of variable length. Each directory entry has the beginning of a (struct direct) at the front of it, containing its filesystem-unique ident number, the length of the entry, and the length of the name contained in the entry. These are followed by the NUL- terminated name padded to a (long) boundary with 0 bytes. The maximum length of a name in a directory is MAXNAMELEN. The macro DIRSIZ(dp) gives the amount of space required to represent a directory entry. Free space in a directory is represented by entries that have dp->d_reclen > DIRSIZ(dp). All DIRBLKSIZ bytes in a directory block are claimed by the directory entries; this usually results in the last entry in a directory having a large dp->d_reclen. When entries are deleted from a directory, the space is returned to the previous entry in the same directory block by increasing its dp->d_reclen. If the first entry of a directory block is free, then its dp->d_fileno is set to 0; entries other than the first in a directory do not normally have dp->d_fileno set to 0. prerequisite: */ #if defined(accel) || defined(sun) || defined(vax) #define DIRBLKSIZ 512 /* size of directory block */ #else #ifdef alliant #define DIRBLKSIZ 4096 /* size of directory block */ #else #ifdef gould #define DIRBLKSIZ 1024 /* size of directory block */ #else #ifdef ns32000 /* Dynix System V */ #define DIRBLKSIZ 2600 /* size of directory block */ #else /* be conservative; multiple blocks are okay * but fractions are not */ #define DIRBLKSIZ 4096 /* size of directory block */ #endif #endif #endif #endif #define MAXNAMELEN 255 /* maximum filename length */ /* NOTE: not MAXNAMLEN, which has been preempted by SVR3 */ struct direct { /* data from read()/_getdirentries() */ unsigned long d_fileno; /* unique ident of entry */ unsigned short d_reclen; /* length of this record */ unsigned short d_namlen; /* length of string in d_name */ char d_name[MAXNAMELEN + 1]; /* NUL-terminated filename */ }; /* The DIRSIZ macro gives the minimum record length which will hold the directory entry. This requires the amount of space in a (struct direct) without the d_name field, plus enough space for the name with a terminating NUL character, rounded up to a (long) boundary. (Note that Berkeley didn't properly compensate for struct padding, but we nevertheless have to use the same size as the actual system.) */ #define DIRSIZ( dp ) ((sizeof(struct direct) - (MAXNAMELEN+1) \ + sizeof(long) + (dp)->d_namlen) \ / sizeof(long) * sizeof(long)) #else #include #ifdef SYSV3 #undef MAXNAMLEN /* avoid conflict with SVR3 */ #endif /* Good thing we don't need to use the DIRSIZ() macro! */ #ifdef d_ino /* 4.3BSD/NFS using d_fileno */ #undef d_ino /* (not absolutely necessary) */ #else #define d_fileno d_ino /* (struct direct) member */ #endif #endif #ifdef UNK #ifndef UFS #include "***** ERROR ***** UNK applies only to UFS" /* One could do something similar for getdirentries(), but I didn't bother. */ #endif #include #endif #if defined(UFS) + defined(BFS) + defined(NFS) != 1 /* sanity check */ #include "***** ERROR ***** exactly one of UFS, BFS, or NFS must be defined" #endif #ifdef UFS #define RecLen( dp ) (sizeof(struct direct)) /* fixed-length entries */ #else /* BFS || NFS */ #define RecLen( dp ) ((dp)->d_reclen) /* variable-length entries */ #endif #ifdef NFS #ifdef BSD_SYSV #define getdirentries _getdirentries /* package hides this system call */ #endif extern int getdirentries(); static long dummy; /* getdirentries() needs basep */ #define GetBlock( fd, buf, n ) getdirentries( fd, buf, (unsigned)n, &dummy ) #else /* UFS || BFS */ #ifdef BSD_SYSV #define read _read /* avoid emulation overhead */ #endif extern int read(); #define GetBlock( fd, buf, n ) read( fd, buf, (unsigned)n ) #endif #ifdef UNK extern int _getdents(); /* actual system call */ #endif extern char *strncpy(); extern int fstat(); extern OFFSET lseek(); extern int errno; #ifndef DIRBLKSIZ #define DIRBLKSIZ 4096 /* directory file read buffer size */ #endif #ifndef NULL #define NULL 0 #endif #ifndef SEEK_CUR #define SEEK_CUR 1 #endif #ifndef S_ISDIR /* macro to test for directory file */ #define S_ISDIR( mode ) (((mode) & S_IFMT) == S_IFDIR) #endif #ifndef SEEK_CUR #define SEEK_CUR 1 #endif #ifdef BSD_SYSV #define open _open /* avoid emulation overhead */ #endif extern int getdents(); /* SVR3 system call, or emulation */ typedef char *pointer; /* (void *) if you have it */ extern void free(); extern pointer malloc(); extern int open(), close(), fstat(); extern int errno; extern OFFSET lseek(); #ifndef SEEK_SET #define SEEK_SET 0 #endif typedef int bool; /* Boolean data type */ #define false 0 #define true 1 #ifndef NULL #define NULL 0 #endif #ifndef O_RDONLY #define O_RDONLY 0 #endif #ifndef S_ISDIR /* macro to test for directory file */ #define S_ISDIR( mode ) (((mode) & S_IFMT) == S_IFDIR) #endif #ifdef __STDC__ DIR *opendir(char *dirname) #else DIR *opendir(dirname) char *dirname; /* name of directory */ #endif { register DIR *dirp; /* -> malloc'ed storage */ register int fd; /* file descriptor for read */ struct stat sbuf; /* result of fstat() */ if ((fd = open(dirname, O_RDONLY)) < 0) return ((DIR *)NULL); /* errno set by open() */ if (fstat(fd, &sbuf) != 0 || !S_ISDIR(sbuf.st_mode)) { close(fd); errno = ENOTDIR; return ((DIR *)NULL); /* not a directory */ } if ((dirp = (DIR *) malloc(sizeof(DIR))) == (DIR *)NULL || (dirp->dd_buf = (char *) malloc((unsigned) DIRBUF)) == (char *)NULL ) { register int serrno = errno; /* errno set to ENOMEM by sbrk() */ if (dirp != (DIR *)NULL) free((pointer) dirp); close(fd); errno = serrno; return ((DIR *)NULL); /* not enough memory */ } dirp->dd_fd = fd; dirp->dd_loc = dirp->dd_size = 0; /* refill needed */ return dirp; } /* * closedir -- close a directory stream * * last edit: 11-Nov-1988 D A Gwyn */ #ifdef __STDC__ int closedir(register DIR *dirp) #else int closedir(dirp) register DIR *dirp; /* stream from opendir() */ #endif { register int fd; if ( dirp == (DIR *)NULL || dirp->dd_buf == (char *)NULL ) { errno = EFAULT; return -1; /* invalid pointer */ } fd = dirp->dd_fd; /* bug fix thanks to R. Salz */ free( (pointer)dirp->dd_buf ); free( (pointer)dirp ); return close( fd ); } /* readdir -- read next entry from a directory stream last edit: 25-Apr-1987 D A Gwyn */ #ifdef __STDC__ struct dirent *readdir(register DIR *dirp) #else struct dirent *readdir(dirp) register DIR *dirp; /* stream from opendir() */ #endif { register struct dirent *dp; /* -> directory data */ if (dirp == (DIR *)NULL || dirp->dd_buf == (char *)NULL) { errno = EFAULT; return (struct dirent *)NULL; /* invalid pointer */ } do { if (dirp->dd_loc >= dirp->dd_size) /* empty or obsolete */ dirp->dd_loc = dirp->dd_size = 0; if (dirp->dd_size == 0 /* need to refill buffer */ && (dirp->dd_size = getdents(dirp->dd_fd, dirp->dd_buf, (unsigned) DIRBUF) ) <= 0 ) return ((struct dirent *)NULL); /* EOF or error */ dp = (struct dirent *) & dirp->dd_buf[dirp->dd_loc]; dirp->dd_loc += dp->d_reclen; } while (dp->d_ino == 0L); /* don't rely on getdents() */ return dp; } /* seekdir -- reposition a directory stream last edit: 24-May-1987 D A Gwyn An unsuccessful seekdir() will in general alter the current directory position; beware. NOTE: 4.nBSD directory compaction makes seekdir() & telldir() practically impossible to do right. Avoid using them! */ #ifdef __STDC__ void seekdir(register DIR *dirp, register OFFSET loc) #else void seekdir(dirp, loc) register DIR *dirp; /* stream from opendir() */ register OFFSET loc; /* position from telldir() */ #endif { register bool rewind; /* "start over when stymied" flag */ if (dirp == (DIR *)NULL || dirp->dd_buf == (char *)NULL) { errno = EFAULT; return; /* invalid pointer */ } /* * A (struct dirent)'s d_off is an invented quantity on 4.nBSD * NFS-supporting systems, so it is not safe to lseek() to it. */ /* Monotonicity of d_off is heavily exploited in the following. */ /* * This algorithm is tuned for modest directory sizes. For huge * directories, it might be more efficient to read blocks until the first * d_off is too large, then back up one block, or even to use binary * search on the directory blocks. I doubt that the extra code for that * would be worthwhile. */ if (dirp->dd_loc >= dirp->dd_size /* invalid index */ || ((struct dirent *) & dirp->dd_buf[dirp->dd_loc])->d_off > loc /* too far along in buffer */ ) dirp->dd_loc = 0; /* reset to beginning of buffer */ /* else save time by starting at current dirp->dd_loc */ for (rewind = true;;) { register struct dirent *dp; /* See whether the matching entry is in the current buffer. */ if ((dirp->dd_loc < dirp->dd_size /* valid index */ || readdir(dirp) != (struct dirent *)NULL /* next buffer read */ && (dirp->dd_loc = 0, true) /* beginning of buffer set */ ) && (dp = (struct dirent *) & dirp->dd_buf[dirp->dd_loc])->d_off <= loc /* match possible in this buffer */ ) { for ( /* dp initialized above */ ; (char *) dp < &dirp->dd_buf[dirp->dd_size]; dp = (struct dirent *) ((char *) dp + dp->d_reclen) ) if (dp->d_off == loc) { /* found it! */ dirp->dd_loc = (char *) dp - dirp->dd_buf; return; } rewind = false; /* no point in backing up later */ dirp->dd_loc = dirp->dd_size; /* set end of buffer */ } else /* whole buffer past matching entry */ if (!rewind) { /* no point in searching * further */ errno = EINVAL; return; /* no entry at specified loc */ } else { /* rewind directory and start over */ rewind = false; /* but only once! */ dirp->dd_loc = dirp->dd_size = 0; if (lseek(dirp->dd_fd, (OFFSET) 0, SEEK_SET) != 0 ) return; /* errno already set (EBADF) */ if (loc == 0) return; /* save time */ } } } /* telldir - report directory stream position * * DESCRIPTION * * Returns the offset of the next directory entry in the * directory associated with dirp. * * NOTE: 4.nBSD directory compaction makes seekdir() & telldir() * practically impossible to do right. Avoid using them! * * PARAMETERS * * DIR *dirp - stream from opendir() * * RETURNS * * Return offset of next entry */ #ifdef __STDC__ OFFSET telldir(DIR *dirp) #else OFFSET telldir(dirp) DIR *dirp; /* stream from opendir() */ #endif { if (dirp == (DIR *)NULL || dirp->dd_buf == (char *)NULL) { errno = EFAULT; return -1; /* invalid pointer */ } if (dirp->dd_loc < dirp->dd_size) /* valid index */ return ((struct dirent *) & dirp->dd_buf[dirp->dd_loc])->d_off; else /* beginning of next directory block */ return lseek(dirp->dd_fd, (OFFSET) 0, SEEK_CUR); } #ifdef UFS /* The following routine is necessary to handle DIRSIZ-long entry names. Thanks to Richard Todd for pointing this out. */ /* return # chars in embedded name */ #ifdef __STDC__ static int NameLen(char *name) #else static int NameLen(name) char *name; /* -> name embedded in struct direct */ #endif { register char *s; /* -> name[.] */ register char *stop = &name[DIRSIZ]; /* -> past end of name field */ for (s = &name[1]; /* (empty names are impossible) */ *s != '\0' /* not NUL terminator */ && ++s < stop; /* < DIRSIZ characters scanned */ ); return s - name; /* # valid characters in name */ } #else /* BFS || NFS */ extern int strlen(); #define NameLen( name ) strlen( name ) /* names are always NUL-terminated */ #endif #ifdef UNK static enum { maybe, no, yes } state = maybe; /* sig_catch - used to catch signals * * DESCRIPTION * * Used to catch signals. */ /*ARGSUSED*/ #ifdef __STDC__ static void sig_catch(int sig) #else static void sig_catch(sig) int sig; /* must be SIGSYS */ #endif { state = no; /* attempted _getdents() faulted */ } #endif /* getdents - get directory entries * * DESCRIPTION * * Gets directory entries from the filesystem in an implemenation * defined way. * * PARAMETERS * * int fildes - directory file descriptor * char *buf - where to put the (struct dirent)s * unsigned nbyte - size of buf[] * * RETURNS * * Returns number of bytes read; 0 on EOF, -1 on error */ #ifdef __STDC__ int getdents(int fildes, char *buf, unsigned nbyte) #else int getdents(fildes, buf, nbyte) int fildes; /* directory file descriptor */ char *buf; /* where to put the (struct dirent)s */ unsigned nbyte; /* size of buf[] */ #endif { int serrno; /* entry errno */ OFFSET offset; /* initial directory file offset */ struct stat statb; /* fstat() info */ union { /* directory file block buffer */ #ifdef UFS char dblk[DIRBLKSIZ + 1]; #else char dblk[DIRBLKSIZ]; #endif struct direct dummy; /* just for alignment */ } u; /* (avoids having to malloc()) */ register struct direct *dp; /* -> u.dblk[.] */ register struct dirent *bp; /* -> buf[.] */ #ifdef UNK switch (state) { SIG_T (*shdlr)(); /* entry SIGSYS handler */ register int retval; /* return from _getdents() if any */ case yes: /* _getdents() is known to work */ return _getdents(fildes, buf, nbyte); case maybe: /* first time only */ shdlr = signal(SIGSYS, sig_catch); retval = _getdents(fildes, buf, nbyte); /* try it */ signal(SIGSYS, shdlr); if (state == maybe) { /* SIGSYS did not occur */ state = yes; /* so _getdents() must have worked */ return retval; } /* else fall through into emulation */ /* case no: /* fall through into emulation */ } #endif if (buf == (char *)NULL #ifdef ATT_SPEC || (unsigned long) buf % sizeof(long) != 0 /* ugh */ #endif ) { errno = EFAULT; /* invalid pointer */ return -1; } if (fstat(fildes, &statb) != 0) { return -1; /* errno set by fstat() */ } if (!S_ISDIR(statb.st_mode)) { errno = ENOTDIR; /* not a directory */ return -1; } if ((offset = lseek(fildes, (OFFSET) 0, SEEK_CUR)) < 0) { return -1; /* errno set by lseek() */ } #ifdef BFS /* no telling what remote hosts do */ if ((unsigned long) offset % DIRBLKSIZ != 0) { errno = ENOENT; /* file pointer probably misaligned */ return -1; } #endif serrno = errno; /* save entry errno */ for (bp = (struct dirent *) buf; bp == (struct dirent *) buf;) { /* convert next directory block */ int size; do { size = GetBlock(fildes, u.dblk, DIRBLKSIZ); } while (size == -1 && errno == EINTR); if (size <= 0) { return size; /* EOF or error (EBADF) */ } for (dp = (struct direct *) u.dblk; (char *) dp < &u.dblk[size]; dp = (struct direct *) ((char *) dp + RecLen(dp)) ) { #ifndef UFS if (dp->d_reclen <= 0) { errno = EIO; /* corrupted directory */ return -1; } #endif if (dp->d_fileno != 0) { /* non-empty; copy to user buffer */ register int reclen = DIRENTSIZ(NameLen(dp->d_name)); if ((char *) bp + reclen > &buf[nbyte]) { errno = EINVAL; return -1; /* buf too small */ } bp->d_ino = dp->d_fileno; bp->d_off = offset + ((char *) dp - u.dblk); bp->d_reclen = reclen; { #ifdef UFS /* Is the following kludge ugly? You bet. */ register char save = dp->d_name[DIRSIZ]; /* save original data */ dp->d_name[DIRSIZ] = '\0'; /* ensure NUL termination */ #endif /* adds NUL padding */ strncpy(bp->d_name, dp->d_name, reclen - DIRENTBASESIZ); #ifdef UFS dp->d_name[DIRSIZ] = save; /* restore original data */ #endif } bp = (struct dirent *) ((char *) bp + reclen); } } #ifndef BFS /* 4.2BSD screwed up; fixed in 4.3BSD */ if ((char *) dp > &u.dblk[size]) { errno = EIO; /* corrupted directory */ return -1; } #endif } errno = serrno; /* restore entry errno */ return (char *) bp - buf; /* return # bytes read */ } /sys/src/ape/cmd/pax/paxdir.h 664 sys sys 1367613436 1562 /* -- definitions for SVR3 directory access routines last edit: 25-Apr-1987 D A Gwyn Prerequisite: */ #ifndef _PAX_DIRENT_H #define _PAX_DIRENT_H #include "config.h" #ifdef USG #define UFS #else #ifdef BSD #define BFS #endif #endif struct dirent { /* data from getdents()/readdir() */ long d_ino; /* inode number of entry */ off_t d_off; /* offset of disk directory entry */ unsigned short d_reclen; /* length of this record */ char d_name[1]; /* name of file (non-POSIX) */ }; typedef struct { int dd_fd; /* file descriptor */ int dd_loc; /* offset in block */ int dd_size; /* amount of valid data */ char *dd_buf; /* -> directory block */ } DIR; /* stream data from opendir() */ /* * The following nonportable ugliness could have been avoided by defining * DIRENTSIZ and DIRENTBASESIZ to also have (struct dirent *) arguments. */ #define DIRENTBASESIZ (((struct dirent *)0)->d_name \ - (char *)&((struct dirent *)0)->d_ino) #define DIRENTSIZ( namlen ) ((DIRENTBASESIZ + sizeof(long) + (namlen)) \ / sizeof(long) * sizeof(long)) #define MAXNAMLEN 512 /* maximum filename length */ #ifndef NAME_MAX #define NAME_MAX (MAXNAMLEN - 1) /* DAG -- added for POSIX */ #endif #define DIRBUF 8192 /* buffer size for fs-indep. dirs */ /* must in general be larger than the filesystem buffer size */ extern DIR *opendir(); extern struct dirent *readdir(); extern OFFSET telldir(); extern void seekdir(); extern int closedir(); #endif /* _PAX_DIRENT_H */ /sys/src/ape/cmd/pax/port.c 664 sys sys 1367613436 4870 /* $Source: /u/mark/src/pax/RCS/port.c,v $ * * $Revision: 1.2 $ * * port.c - These are routines not available in all environments. * * DESCRIPTION * * The routines contained in this file are provided for portability to * other versions of UNIX or other operating systems (e.g. MSDOS). * Not all systems have the same functions or the same semantics, * these routines attempt to bridge the gap as much as possible. * * AUTHOR * * Mark H. Colburn, NAPS International (mark@jhereg.mn.org) * John Gilmore (gnu@hoptoad) * * Sponsored by The USENIX Association for public distribution. * * Copyright (c) 1989 Mark H. Colburn. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that the above copyright notice is duplicated in all such * forms and that any documentation, advertising materials, and other * materials related to such distribution and use acknowledge that the * software was developed * by Mark H. Colburn and sponsored by The * USENIX Association. * * 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. * * $Log: port.c,v $ * Revision 1.2 89/02/12 10:05:35 mark * 1.2 release fixes * * Revision 1.1 88/12/23 18:02:29 mark * Initial revision * */ #ifndef lint static char *ident = "$Id: port.c,v 1.2 89/02/12 10:05:35 mark Exp $"; static char *copyright = "Copyright (c) 1989 Mark H. Colburn.\nAll rights reserved.\n"; #endif /* ! lint */ /* Headers */ #include "pax.h" /* * Some computers are not so crass as to align themselves into the BSD or USG * camps. If a system supplies all of the routines we fake here, add it to * the list in the #if !defined()'s below and it'll all be skipped. */ #if !defined(mc300) && !defined(mc500) && !defined(mc700) && !defined(BSD) && !defined(_POSIX_SOURCE) /* mkdir - make a directory * * DESCRIPTION * * Mkdir will make a directory of the name "dpath" with a mode of * "dmode". This is consistent with the BSD mkdir() function and the * P1003.1 definitions of MKDIR. * * PARAMETERS * * dpath - name of directory to create * dmode - mode of the directory * * RETURNS * * Returns 0 if the directory was successfully created, otherwise a * non-zero return value will be passed back to the calling function * and the value of errno should reflect the error. */ #ifdef __STDC__ int mkdir(char *dpath, int dmode) #else int mkdir(dpath, dmode) char *dpath; int dmode; #endif { int cpid, status; Stat statbuf; extern int errno; if (STAT(dpath, &statbuf) == 0) { errno = EEXIST; /* Stat worked, so it already exists */ return (-1); } /* If stat fails for a reason other than non-existence, return error */ if (errno != ENOENT) return (-1); switch (cpid = fork()) { case -1: /* Error in fork() */ return (-1); /* Errno is set already */ case 0: /* Child process */ status = umask(0); /* Get current umask */ status = umask(status | (0777 & ~dmode)); /* Set for mkdir */ execl("/bin/mkdir", "mkdir", dpath, (char *) 0); _exit(-1); /* Can't exec /bin/mkdir */ default: /* Parent process */ while (cpid != wait(&status)) { /* Wait for child to finish */ } } if (TERM_SIGNAL(status) != 0 || TERM_VALUE(status) != 0) { errno = EIO; /* We don't know why, but */ return (-1); /* /bin/mkdir failed */ } return (0); } /* rmdir - remove a directory * * DESCRIPTION * * Rmdir will remove the directory specified by "dpath". It is * consistent with the BSD and POSIX rmdir functions. * * PARAMETERS * * dpath - name of directory to remove * * RETURNS * * Returns 0 if the directory was successfully deleted, otherwise a * non-zero return value will be passed back to the calling function * and the value of errno should reflect the error. */ #ifdef __STDC__ int rmdir(char *dpath) #else int rmdir(dpath) char *dpath; #endif { int cpid, status; Stat statbuf; extern int errno; /* check to see if it exists */ if (STAT(dpath, &statbuf) == -1) { return (-1); } switch (cpid = fork()) { case -1: /* Error in fork() */ return (-1); /* Errno is set already */ case 0: /* Child process */ execl("/bin/rmdir", "rmdir", dpath, (char *) 0); _exit(-1); /* Can't exec /bin/rmdir */ default: /* Parent process */ while (cpid != wait(&status)) { /* Wait for child to finish */ } } if (TERM_SIGNAL(status) != 0 || TERM_VALUE(status) != 0) { errno = EIO; /* We don't know why, but */ return (-1); /* /bin/rmdir failed */ } return (0); } #endif /* MASSCOMP, BSD */ /sys/src/ape/cmd/pax/port.h 664 sys sys 1367613436 2862 /* $Source: /u/mark/src/pax/RCS/port.h,v $ * * $Revision: 1.2 $ * * port.h - defnitions for portability library * * DESCRIPTION * * Header for maintaing portablilty across operating system and * version boundries. For the most part, this file contains * definitions which map functions which have the same functionality * but different names on different systems, to have the same name. * * AUTHORS * * Mark H. Colburn, NAPS International (mark@jhereg.mn.org) * John Gilmore (gnu@hoptoad) * * Sponsored by The USENIX Association for public distribution. * * Copyright (c) 1989 Mark H. Colburn. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that the above copyright notice and this paragraph are * duplicated in all such forms and that any documentation, * advertising materials, and other materials related to such * distribution and use acknowledge that the software was developed * by Mark H. Colburn and sponsored by The USENIX Association. * * 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. */ #ifndef _PAX_PORT_H #define _PAX_PORT_H /* * Everybody does wait() differently. There seem to be no definitions for * this in V7 (e.g. you are supposed to shift and mask things out using * constant shifts and masks.) In order to provide the functionality, here * are some non standard but portable macros. Don't change to a "union wait" * based approach -- the ordering of the elements of the struct depends on the * byte-sex of the machine. */ #define TERM_SIGNAL(status) ((status) & 0x7F) #define TERM_COREDUMP(status) (((status) & 0x80) != 0) #define TERM_VALUE(status) ((status) >> 8) /* * String library emulation definitions for the different variants of UNIX */ #if defined(USG) # include #ifndef _POSIX_SOURCE # include #endif #else /* USG */ /* * The following functions are defined here since func.h has no idea which * of the functions will actually be used. */ # ifdef __STDC__ extern char *rindex(char *, char); extern char *index(char *, char); extern char *bcopy(char *, char *, unsigned int); extern char *bzero(char *, unsigned int); extern char *strcat(char *, char *); extern char *strcpy(char *, char *); # else /* !__STDC__ */ extern char *rindex(); extern char *index(); extern char *bcopy(); extern char *bzero(); extern char *strcat(); extern char *strcpy(); # endif /* __STDC__ */ /* * Map ANSI C compatible functions to V7 functions */ # define memcpy(a,b,n) bcopy((b),(a),(n)) # define memset(a,b,n) bzero((a),(n)) # define strrchr(s,c) rindex(s,c) # define strchr(s,c) index(s,c) #endif /* USG */ #endif /* _PAX_PORT_H */ /sys/src/ape/cmd/pax/regexp.c 664 sys sys 1367613436 31134 /* $Source: /u/mark/src/pax/RCS/regexp.c,v $ * * $Revision: 1.2 $ * * regexp.c - regular expression matching * * DESCRIPTION * * Underneath the reformatting and comment blocks which were added to * make it consistent with the rest of the code, you will find a * modified version of Henry Specer's regular expression library. * Henry's functions were modified to provide the minimal regular * expression matching, as required by P1003. Henry's code was * copyrighted, and copy of the copyright message and restrictions * are provided, verbatim, below: * * Copyright (c) 1986 by University of Toronto. * Written by Henry Spencer. Not derived from licensed software. * * Permission is granted to anyone to use this software for any * purpose on any computer system, and to redistribute it freely, * subject to the following restrictions: * * 1. The author is not responsible for the consequences of use of * this software, no matter how awful, even if they arise * from defects in it. * * 2. The origin of this software must not be misrepresented, either * by explicit claim or by omission. * * 3. Altered versions must be plainly marked as such, and must not * be misrepresented as being the original software. * * Beware that some of this code is subtly aware of the way operator * precedence is structured in regular expressions. Serious changes in * regular-expression syntax might require a total rethink. * * AUTHORS * * Mark H. Colburn, NAPS International (mark@jhereg.mn.org) * Henry Spencer, University of Torronto (henry@utzoo.edu) * * Sponsored by The USENIX Association for public distribution. * * $Log: regexp.c,v $ * Revision 1.2 89/02/12 10:05:39 mark * 1.2 release fixes * * Revision 1.1 88/12/23 18:02:32 mark * Initial revision * */ /* Headers */ #include "pax.h" #ifndef lint static char *Ident = "$Id: regexp.c,v 1.2 89/02/12 10:05:39 mark Exp $"; #endif /* * The "internal use only" fields in regexp.h are present to pass info from * compile to execute that permits the execute phase to run lots faster on * simple cases. They are: * * regstart char that must begin a match; '\0' if none obvious * reganch is the match anchored (at beginning-of-line only)? * regmust string (pointer into program) that match must include, or NULL * regmlen length of regmust string * * Regstart and reganch permit very fast decisions on suitable starting points * for a match, cutting down the work a lot. Regmust permits fast rejection * of lines that cannot possibly match. The regmust tests are costly enough * that regcomp() supplies a regmust only if the r.e. contains something * potentially expensive (at present, the only such thing detected is * or + * at the start of the r.e., which can involve a lot of backup). Regmlen is * supplied because the test in regexec() needs it and regcomp() is computing * it anyway. */ /* * Structure for regexp "program". This is essentially a linear encoding * of a nondeterministic finite-state machine (aka syntax charts or * "railroad normal form" in parsing technology). Each node is an opcode * plus a "nxt" pointer, possibly plus an operand. "Nxt" pointers of * all nodes except BRANCH implement concatenation; a "nxt" pointer with * a BRANCH on both ends of it is connecting two alternatives. (Here we * have one of the subtle syntax dependencies: an individual BRANCH (as * opposed to a collection of them) is never concatenated with anything * because of operator precedence.) The operand of some types of node is * a literal string; for others, it is a node leading into a sub-FSM. In * particular, the operand of a BRANCH node is the first node of the branch. * (NB this is *not* a tree structure: the tail of the branch connects * to the thing following the set of BRANCHes.) The opcodes are: */ /* definition number opnd? meaning */ #define END 0 /* no End of program. */ #define BOL 1 /* no Match "" at beginning of line. */ #define EOL 2 /* no Match "" at end of line. */ #define ANY 3 /* no Match any one character. */ #define ANYOF 4 /* str Match any character in this string. */ #define ANYBUT 5 /* str Match any character not in this * string. */ #define BRANCH 6 /* node Match this alternative, or the * nxt... */ #define BACK 7 /* no Match "", "nxt" ptr points backward. */ #define EXACTLY 8 /* str Match this string. */ #define NOTHING 9 /* no Match empty string. */ #define STAR 10 /* node Match this (simple) thing 0 or more * times. */ #define OPEN 20 /* no Mark this point in input as start of * #n. */ /* OPEN+1 is number 1, etc. */ #define CLOSE 30 /* no Analogous to OPEN. */ /* * Opcode notes: * * BRANCH The set of branches constituting a single choice are hooked * together with their "nxt" pointers, since precedence prevents * anything being concatenated to any individual branch. The * "nxt" pointer of the last BRANCH in a choice points to the * thing following the whole choice. This is also where the * final "nxt" pointer of each individual branch points; each * branch starts with the operand node of a BRANCH node. * * BACK Normal "nxt" pointers all implicitly point forward; BACK * exists to make loop structures possible. * * STAR complex '*', are implemented as circular BRANCH structures * using BACK. Simple cases (one character per match) are * implemented with STAR for speed and to minimize recursive * plunges. * * OPEN,CLOSE ...are numbered at compile time. */ /* * A node is one char of opcode followed by two chars of "nxt" pointer. * "Nxt" pointers are stored as two 8-bit pieces, high order first. The * value is a positive offset from the opcode of the node containing it. * An operand, if any, simply follows the node. (Note that much of the * code generation knows about this implicit relationship.) * * Using two bytes for the "nxt" pointer is vast overkill for most things, * but allows patterns to get big without disasters. */ #define OP(p) (*(p)) #define NEXT(p) (((*((p)+1)&0377)<<8) + (*((p)+2)&0377)) #define OPERAND(p) ((p) + 3) /* * Utility definitions. */ #define FAIL(m) { regerror(m); return(NULL); } #define ISMULT(c) ((c) == '*') #define META "^$.[()|*\\" #ifndef CHARBITS #define UCHARAT(p) ((int)*(unsigned char *)(p)) #else #define UCHARAT(p) ((int)*(p)&CHARBITS) #endif /* * Flags to be passed up and down. */ #define HASWIDTH 01 /* Known never to match null string. */ #define SIMPLE 02 /* Simple enough to be STAR operand. */ #define SPSTART 04 /* Starts with * */ #define WORST 0 /* Worst case. */ /* * Global work variables for regcomp(). */ static char *regparse; /* Input-scan pointer. */ static int regnpar; /* () count. */ static char regdummy; static char *regcode; /* Code-emit pointer; ®dummy = don't. */ static long regsize; /* Code size. */ /* * Forward declarations for regcomp()'s friends. */ #ifndef STATIC #define STATIC static #endif STATIC char *reg(); STATIC char *regbranch(); STATIC char *regpiece(); STATIC char *regatom(); STATIC char *regnode(); STATIC char *regnext(); STATIC void regc(); STATIC void reginsert(); STATIC void regtail(); STATIC void regoptail(); #ifdef STRCSPN STATIC int strcspn(); #endif /* - regcomp - compile a regular expression into internal code * * We can't allocate space until we know how big the compiled form will be, * but we can't compile it (and thus know how big it is) until we've got a * place to put the code. So we cheat: we compile it twice, once with code * generation turned off and size counting turned on, and once "for real". * This also means that we don't allocate space until we are sure that the * thing really will compile successfully, and we never have to move the * code and thus invalidate pointers into it. (Note that it has to be in * one piece because free() must be able to free it all.) * * Beware that the optimization-preparation code in here knows about some * of the structure of the compiled regexp. */ regexp *regcomp(exp) char *exp; { register regexp *r; register char *scan; register char *longest; register int len; int flags; extern char *malloc(); if (exp == (char *)NULL) FAIL("NULL argument"); /* First pass: determine size, legality. */ regparse = exp; regnpar = 1; regsize = 0L; regcode = ®dummy; regc(MAGIC); if (reg(0, &flags) == (char *)NULL) return ((regexp *)NULL); /* Small enough for pointer-storage convention? */ if (regsize >= 32767L) /* Probably could be 65535L. */ FAIL("regexp too big"); /* Allocate space. */ r = (regexp *) malloc(sizeof(regexp) + (unsigned) regsize); if (r == (regexp *) NULL) FAIL("out of space"); /* Second pass: emit code. */ regparse = exp; regnpar = 1; regcode = r->program; regc(MAGIC); if (reg(0, &flags) == NULL) return ((regexp *) NULL); /* Dig out information for optimizations. */ r->regstart = '\0'; /* Worst-case defaults. */ r->reganch = 0; r->regmust = NULL; r->regmlen = 0; scan = r->program + 1; /* First BRANCH. */ if (OP(regnext(scan)) == END) { /* Only one top-level choice. */ scan = OPERAND(scan); /* Starting-point info. */ if (OP(scan) == EXACTLY) r->regstart = *OPERAND(scan); else if (OP(scan) == BOL) r->reganch++; /* * If there's something expensive in the r.e., find the longest * literal string that must appear and make it the regmust. Resolve * ties in favor of later strings, since the regstart check works * with the beginning of the r.e. and avoiding duplication * strengthens checking. Not a strong reason, but sufficient in the * absence of others. */ if (flags & SPSTART) { longest = NULL; len = 0; for (; scan != NULL; scan = regnext(scan)) if (OP(scan) == EXACTLY && strlen(OPERAND(scan)) >= len) { longest = OPERAND(scan); len = strlen(OPERAND(scan)); } r->regmust = longest; r->regmlen = len; } } return (r); } /* - reg - regular expression, i.e. main body or parenthesized thing * * Caller must absorb opening parenthesis. * * Combining parenthesis handling with the base level of regular expression * is a trifle forced, but the need to tie the tails of the branches to what * follows makes it hard to avoid. */ static char *reg(paren, flagp) int paren; /* Parenthesized? */ int *flagp; { register char *ret; register char *br; register char *ender; register int parno; int flags; *flagp = HASWIDTH; /* Tentatively. */ /* Make an OPEN node, if parenthesized. */ if (paren) { if (regnpar >= NSUBEXP) FAIL("too many ()"); parno = regnpar; regnpar++; ret = regnode(OPEN + parno); } else ret = (char *)NULL; /* Pick up the branches, linking them together. */ br = regbranch(&flags); if (br == (char *)NULL) return ((char *)NULL); if (ret != (char *)NULL) regtail(ret, br); /* OPEN -> first. */ else ret = br; if (!(flags & HASWIDTH)) *flagp &= ~HASWIDTH; *flagp |= flags & SPSTART; while (*regparse == '|') { regparse++; br = regbranch(&flags); if (br == (char *)NULL) return ((char *)NULL); regtail(ret, br); /* BRANCH -> BRANCH. */ if (!(flags & HASWIDTH)) *flagp &= ~HASWIDTH; *flagp |= flags & SPSTART; } /* Make a closing node, and hook it on the end. */ ender = regnode((paren) ? CLOSE + parno : END); regtail(ret, ender); /* Hook the tails of the branches to the closing node. */ for (br = ret; br != (char *)NULL; br = regnext(br)) regoptail(br, ender); /* Check for proper termination. */ if (paren && *regparse++ != ')') { FAIL("unmatched ()"); } else if (!paren && *regparse != '\0') { if (*regparse == ')') { FAIL("unmatched ()"); } else FAIL("junk on end");/* "Can't happen". */ /* NOTREACHED */ } return (ret); } /* - regbranch - one alternative of an | operator * * Implements the concatenation operator. */ static char *regbranch(flagp) int *flagp; { register char *ret; register char *chain; register char *latest; int flags; *flagp = WORST; /* Tentatively. */ ret = regnode(BRANCH); chain = (char *)NULL; while (*regparse != '\0' && *regparse != '|' && *regparse != ')') { latest = regpiece(&flags); if (latest == (char *)NULL) return ((char *)NULL); *flagp |= flags & HASWIDTH; if (chain == (char *)NULL) /* First piece. */ *flagp |= flags & SPSTART; else regtail(chain, latest); chain = latest; } if (chain == (char *)NULL) /* Loop ran zero times. */ regnode(NOTHING); return (ret); } /* - regpiece - something followed by possible [*] * * Note that the branching code sequence used for * is somewhat optimized: * they use the same NOTHING node as both the endmarker for their branch * list and the body of the last branch. It might seem that this node could * be dispensed with entirely, but the endmarker role is not redundant. */ static char *regpiece(flagp) int *flagp; { register char *ret; register char op; register char *nxt; int flags; ret = regatom(&flags); if (ret == (char *)NULL) return ((char *)NULL); op = *regparse; if (!ISMULT(op)) { *flagp = flags; return (ret); } if (!(flags & HASWIDTH)) FAIL("* operand could be empty"); *flagp = (WORST | SPSTART); if (op == '*' && (flags & SIMPLE)) reginsert(STAR, ret); else if (op == '*') { /* Emit x* as (x&|), where & means "self". */ reginsert(BRANCH, ret); /* Either x */ regoptail(ret, regnode(BACK)); /* and loop */ regoptail(ret, ret); /* back */ regtail(ret, regnode(BRANCH)); /* or */ regtail(ret, regnode(NOTHING)); /* null. */ } regparse++; if (ISMULT(*regparse)) FAIL("nested *"); return (ret); } /* - regatom - the lowest level * * Optimization: gobbles an entire sequence of ordinary characters so that * it can turn them into a single node, which is smaller to store and * faster to run. Backslashed characters are exceptions, each becoming a * separate node; the code is simpler that way and it's not worth fixing. */ static char *regatom(flagp) int *flagp; { register char *ret; int flags; *flagp = WORST; /* Tentatively. */ switch (*regparse++) { case '^': ret = regnode(BOL); break; case '$': ret = regnode(EOL); break; case '.': ret = regnode(ANY); *flagp |= HASWIDTH | SIMPLE; break; case '[':{ register int class; register int classend; if (*regparse == '^') { /* Complement of range. */ ret = regnode(ANYBUT); regparse++; } else ret = regnode(ANYOF); if (*regparse == ']' || *regparse == '-') regc(*regparse++); while (*regparse != '\0' && *regparse != ']') { if (*regparse == '-') { regparse++; if (*regparse == ']' || *regparse == '\0') regc('-'); else { class = UCHARAT(regparse - 2) + 1; classend = UCHARAT(regparse); if (class > classend + 1) FAIL("invalid [] range"); for (; class <= classend; class++) regc(class); regparse++; } } else regc(*regparse++); } regc('\0'); if (*regparse != ']') FAIL("unmatched []"); regparse++; *flagp |= HASWIDTH | SIMPLE; } break; case '(': ret = reg(1, &flags); if (ret == (char *)NULL) return ((char *)NULL); *flagp |= flags & (HASWIDTH | SPSTART); break; case '\0': case '|': case ')': FAIL("internal urp"); /* Supposed to be caught earlier. */ break; case '*': FAIL("* follows nothing"); break; case '\\': if (*regparse == '\0') FAIL("trailing \\"); ret = regnode(EXACTLY); regc(*regparse++); regc('\0'); *flagp |= HASWIDTH | SIMPLE; break; default:{ register int len; register char ender; regparse--; len = strcspn(regparse, META); if (len <= 0) FAIL("internal disaster"); ender = *(regparse + len); if (len > 1 && ISMULT(ender)) len--; /* Back off clear of * operand. */ *flagp |= HASWIDTH; if (len == 1) *flagp |= SIMPLE; ret = regnode(EXACTLY); while (len > 0) { regc(*regparse++); len--; } regc('\0'); } break; } return (ret); } /* - regnode - emit a node */ static char *regnode(op) char op; { register char *ret; register char *ptr; ret = regcode; if (ret == ®dummy) { regsize += 3; return (ret); } ptr = ret; *ptr++ = op; *ptr++ = '\0'; /* Null "nxt" pointer. */ *ptr++ = '\0'; regcode = ptr; return (ret); } /* - regc - emit (if appropriate) a byte of code */ static void regc(b) char b; { if (regcode != ®dummy) *regcode++ = b; else regsize++; } /* - reginsert - insert an operator in front of already-emitted operand * * Means relocating the operand. */ static void reginsert(op, opnd) char op; char *opnd; { register char *src; register char *dst; register char *place; if (regcode == ®dummy) { regsize += 3; return; } src = regcode; regcode += 3; dst = regcode; while (src > opnd) *--dst = *--src; place = opnd; /* Op node, where operand used to be. */ *place++ = op; *place++ = '\0'; *place++ = '\0'; } /* - regtail - set the next-pointer at the end of a node chain */ static void regtail(p, val) char *p; char *val; { register char *scan; register char *temp; register int offset; if (p == ®dummy) return; /* Find last node. */ scan = p; for (;;) { temp = regnext(scan); if (temp == (char *)NULL) break; scan = temp; } if (OP(scan) == BACK) offset = scan - val; else offset = val - scan; *(scan + 1) = (offset >> 8) & 0377; *(scan + 2) = offset & 0377; } /* - regoptail - regtail on operand of first argument; nop if operandless */ static void regoptail(p, val) char *p; char *val; { /* "Operandless" and "op != BRANCH" are synonymous in practice. */ if (p == (char *)NULL || p == ®dummy || OP(p) != BRANCH) return; regtail(OPERAND(p), val); } /* * regexec and friends */ /* * Global work variables for regexec(). */ static char *reginput; /* String-input pointer. */ static char *regbol; /* Beginning of input, for ^ check. */ static char **regstartp; /* Pointer to startp array. */ static char **regendp; /* Ditto for endp. */ /* * Forwards. */ STATIC int regtry(); STATIC int regmatch(); STATIC int regrepeat(); #ifdef DEBUG int regnarrate = 0; void regdump(); STATIC char *regprop(); #endif /* - regexec - match a regexp against a string */ int regexec(prog, string) register regexp *prog; register char *string; { register char *s; /* Be paranoid... */ if (prog == (regexp *)NULL || string == (char *)NULL) { regerror("NULL parameter"); return (0); } /* Check validity of program. */ if (UCHARAT(prog->program) != MAGIC) { regerror("corrupted program"); return (0); } /* If there is a "must appear" string, look for it. */ if (prog->regmust != (char *)NULL) { s = string; while ((s = strchr(s, prog->regmust[0])) != (char *)NULL) { if (strncmp(s, prog->regmust, prog->regmlen) == 0) break; /* Found it. */ s++; } if (s == (char *)NULL) /* Not present. */ return (0); } /* Mark beginning of line for ^ . */ regbol = string; /* Simplest case: anchored match need be tried only once. */ if (prog->reganch) return (regtry(prog, string)); /* Messy cases: unanchored match. */ s = string; if (prog->regstart != '\0') /* We know what char it must start with. */ while ((s = strchr(s, prog->regstart)) != (char *)NULL) { if (regtry(prog, s)) return (1); s++; } else /* We don't -- general case. */ do { if (regtry(prog, s)) return (1); } while (*s++ != '\0'); /* Failure. */ return (0); } /* - regtry - try match at specific point */ #ifdef __STDC__ static int regtry(regexp *prog, char *string) #else static int regtry(prog, string) regexp *prog; char *string; #endif { register int i; register char **sp; register char **ep; reginput = string; regstartp = prog->startp; regendp = prog->endp; sp = prog->startp; ep = prog->endp; for (i = NSUBEXP; i > 0; i--) { *sp++ = (char *)NULL; *ep++ = (char *)NULL; } if (regmatch(prog->program + 1)) { prog->startp[0] = string; prog->endp[0] = reginput; return (1); } else return (0); } /* - regmatch - main matching routine * * Conceptually the strategy is simple: check to see whether the current * node matches, call self recursively to see whether the rest matches, * and then act accordingly. In practice we make some effort to avoid * recursion, in particular by going through "ordinary" nodes (that don't * need to know whether the rest of the match failed) by a loop instead of * by recursion. */ #ifdef __STDC__ static int regmatch(char *prog) #else static int regmatch(prog) char *prog; #endif { register char *scan; /* Current node. */ char *nxt; /* nxt node. */ scan = prog; #ifdef DEBUG if (scan != (char *)NULL && regnarrate) fprintf(stderr, "%s(\n", regprop(scan)); #endif while (scan != (char *)NULL) { #ifdef DEBUG if (regnarrate) fprintf(stderr, "%s...\n", regprop(scan)); #endif nxt = regnext(scan); switch (OP(scan)) { case BOL: if (reginput != regbol) return (0); break; case EOL: if (*reginput != '\0') return (0); break; case ANY: if (*reginput == '\0') return (0); reginput++; break; case EXACTLY:{ register int len; register char *opnd; opnd = OPERAND(scan); /* Inline the first character, for speed. */ if (*opnd != *reginput) return (0); len = strlen(opnd); if (len > 1 && strncmp(opnd, reginput, len) != 0) return (0); reginput += len; } break; case ANYOF: if (*reginput == '\0' || strchr(OPERAND(scan), *reginput) == (char *)NULL) return (0); reginput++; break; case ANYBUT: if (*reginput == '\0' || strchr(OPERAND(scan), *reginput) != (char *)NULL) return (0); reginput++; break; case NOTHING: break; case BACK: break; case OPEN + 1: case OPEN + 2: case OPEN + 3: case OPEN + 4: case OPEN + 5: case OPEN + 6: case OPEN + 7: case OPEN + 8: case OPEN + 9:{ register int no; register char *save; no = OP(scan) - OPEN; save = reginput; if (regmatch(nxt)) { /* * Don't set startp if some later invocation of the same * parentheses already has. */ if (regstartp[no] == (char *)NULL) regstartp[no] = save; return (1); } else return (0); } break; case CLOSE + 1: case CLOSE + 2: case CLOSE + 3: case CLOSE + 4: case CLOSE + 5: case CLOSE + 6: case CLOSE + 7: case CLOSE + 8: case CLOSE + 9:{ register int no; register char *save; no = OP(scan) - CLOSE; save = reginput; if (regmatch(nxt)) { /* * Don't set endp if some later invocation of the same * parentheses already has. */ if (regendp[no] == (char *)NULL) regendp[no] = save; return (1); } else return (0); } break; case BRANCH:{ register char *save; if (OP(nxt) != BRANCH) /* No choice. */ nxt = OPERAND(scan); /* Avoid recursion. */ else { do { save = reginput; if (regmatch(OPERAND(scan))) return (1); reginput = save; scan = regnext(scan); } while (scan != (char *)NULL && OP(scan) == BRANCH); return (0); /* NOTREACHED */ } } break; case STAR:{ register char nextch; register int no; register char *save; register int minimum; /* * Lookahead to avoid useless match attempts when we know * what character comes next. */ nextch = '\0'; if (OP(nxt) == EXACTLY) nextch = *OPERAND(nxt); minimum = (OP(scan) == STAR) ? 0 : 1; save = reginput; no = regrepeat(OPERAND(scan)); while (no >= minimum) { /* If it could work, try it. */ if (nextch == '\0' || *reginput == nextch) if (regmatch(nxt)) return (1); /* Couldn't or didn't -- back up. */ no--; reginput = save + no; } return (0); } break; case END: return (1); /* Success! */ break; default: regerror("memory corruption"); return (0); break; } scan = nxt; } /* * We get here only if there's trouble -- normally "case END" is the * terminating point. */ regerror("corrupted pointers"); return (0); } /* - regrepeat - repeatedly match something simple, report how many */ #ifdef __STDC__ static int regrepeat(char *p) #else static int regrepeat(p) char *p; #endif { register int count = 0; register char *scan; register char *opnd; scan = reginput; opnd = OPERAND(p); switch (OP(p)) { case ANY: count = strlen(scan); scan += count; break; case EXACTLY: while (*opnd == *scan) { count++; scan++; } break; case ANYOF: while (*scan != '\0' && strchr(opnd, *scan) != (char *)NULL) { count++; scan++; } break; case ANYBUT: while (*scan != '\0' && strchr(opnd, *scan) == (char *)NULL) { count++; scan++; } break; default: /* Oh dear. Called inappropriately. */ regerror("internal foulup"); count = 0; /* Best compromise. */ break; } reginput = scan; return (count); } /* - regnext - dig the "nxt" pointer out of a node */ #ifdef __STDC__ static char *regnext(register char *p) #else static char *regnext(p) register char *p; #endif { register int offset; if (p == ®dummy) return ((char *)NULL); offset = NEXT(p); if (offset == 0) return ((char *)NULL); if (OP(p) == BACK) return (p - offset); else return (p + offset); } #ifdef DEBUG STATIC char *regprop(); /* - regdump - dump a regexp onto stdout in vaguely comprehensible form */ #ifdef __STDC__ void regdump(regexp *r) #else void regdump(r) regexp *r; #endif { register char *s; register char op = EXACTLY; /* Arbitrary non-END op. */ register char *nxt; extern char *strchr(); s = r->program + 1; while (op != END) { /* While that wasn't END last time... */ op = OP(s); printf("%2d%s", s - r->program, regprop(s)); /* Where, what. */ nxt = regnext(s); if (nxt == (char *)NULL) /* nxt ptr. */ printf("(0)"); else printf("(%d)", (s - r->program) + (nxt - s)); s += 3; if (op == ANYOF || op == ANYBUT || op == EXACTLY) { /* Literal string, where present. */ while (*s != '\0') { putchar(*s); s++; } s++; } putchar('\n'); } /* Header fields of interest. */ if (r->regstart != '\0') printf("start `%c' ", r->regstart); if (r->reganch) printf("anchored "); if (r->regmust != (char *)NULL) printf("must have \"%s\"", r->regmust); printf("\n"); } /* - regprop - printable representation of opcode */ #ifdef __STDC__ static char *regprop(char *op) #else static char *regprop(op) char *op; #endif { register char *p; static char buf[50]; strcpy(buf, ":"); switch (OP(op)) { case BOL: p = "BOL"; break; case EOL: p = "EOL"; break; case ANY: p = "ANY"; break; case ANYOF: p = "ANYOF"; break; case ANYBUT: p = "ANYBUT"; break; case BRANCH: p = "BRANCH"; break; case EXACTLY: p = "EXACTLY"; break; case NOTHING: p = "NOTHING"; break; case BACK: p = "BACK"; break; case END: p = "END"; break; case OPEN + 1: case OPEN + 2: case OPEN + 3: case OPEN + 4: case OPEN + 5: case OPEN + 6: case OPEN + 7: case OPEN + 8: case OPEN + 9: sprintf(buf + strlen(buf), "OPEN%d", OP(op) - OPEN); p = (char *)NULL; break; case CLOSE + 1: case CLOSE + 2: case CLOSE + 3: case CLOSE + 4: case CLOSE + 5: case CLOSE + 6: case CLOSE + 7: case CLOSE + 8: case CLOSE + 9: sprintf(buf + strlen(buf), "CLOSE%d", OP(op) - CLOSE); p = (char *)NULL; break; case STAR: p = "STAR"; break; default: regerror("corrupted opcode"); break; } if (p != (char *)NULL) strcat(buf, p); return (buf); } #endif /* * The following is provided for those people who do not have strcspn() in * their C libraries. They should get off their butts and do something * about it; at least one public-domain implementation of those (highly * useful) string routines has been published on Usenet. */ #ifdef STRCSPN /* * strcspn - find length of initial segment of s1 consisting entirely * of characters not from s2 */ #ifdef __STDC__ static int strcspn(char *s1, char *s2) #else static int strcspn(s1, s2) char *s1; char *s2; #endif { register char *scan1; register char *scan2; register int count; count = 0; for (scan1 = s1; *scan1 != '\0'; scan1++) { for (scan2 = s2; *scan2 != '\0';) /* ++ moved down. */ if (*scan1 == *scan2++) return (count); count++; } return (count); } #endif /* - regsub - perform substitutions after a regexp match */ #ifdef __STDC__ void regsub(regexp *prog, char *source, char *dest) #else void regsub(prog, source, dest) regexp *prog; char *source; char *dest; #endif { register char *src; register char *dst; register char c; register int no; register int len; extern char *strncpy(); if (prog == (regexp *)NULL || source == (char *)NULL || dest == (char *)NULL) { regerror("NULL parm to regsub"); return; } if (UCHARAT(prog->program) != MAGIC) { regerror("damaged regexp fed to regsub"); return; } src = source; dst = dest; while ((c = *src++) != '\0') { if (c == '&') no = 0; else if (c == '\\' && '0' <= *src && *src <= '9') no = *src++ - '0'; else no = -1; if (no < 0) { /* Ordinary character. */ if (c == '\\' && (*src == '\\' || *src == '&')) c = *src++; *dst++ = c; } else if (prog->startp[no] != (char *)NULL && prog->endp[no] != (char *)NULL) { len = prog->endp[no] - prog->startp[no]; strncpy(dst, prog->startp[no], len); dst += len; if (len != 0 && *(dst - 1) == '\0') { /* strncpy hit NUL. */ regerror("damaged match string"); return; } } } *dst++ = '\0'; } #ifdef __STDC__ void regerror(char *s) #else void regerror(s) char *s; #endif { fprintf(stderr, "regexp(3): %s", s); exit(1); } /sys/src/ape/cmd/pax/regexp.h 664 sys sys 1367613436 803 /* * Definitions etc. for regexp(3) routines. * * Caveat: this is V8 regexp(3) [actually, a reimplementation thereof], * not the System V one. */ #ifndef _PAX_REGEXP_H #define _PAX_REGEXP_H #define NSUBEXP 10 typedef struct regexp { char *startp[NSUBEXP]; char *endp[NSUBEXP]; char regstart; /* Internal use only. */ char reganch; /* Internal use only. */ char *regmust; /* Internal use only. */ int regmlen; /* Internal use only. */ char program[1]; /* Unwarranted chumminess with compiler. */ } regexp; /* * The first byte of the regexp internal "program" is actually this magic * number; the start node begins in the second byte. */ #define MAGIC 0234 extern regexp *regcomp(); extern int regexec(); extern void regsub(); extern void regerror(); #endif /* _PAX_REGEXP_H */ /sys/src/ape/cmd/pax/replace.c 664 sys sys 1367613436 6614 /* $Source: /u/mark/src/pax/RCS/replace.c,v $ * * $Revision: 1.2 $ * * replace.c - regular expression pattern replacement functions * * DESCRIPTION * * These routines provide for regular expression file name replacement * as required by pax. * * AUTHORS * * Mark H. Colburn, NAPS International * * Sponsored by The USENIX Association for public distribution. * * Copyright (c) 1989 Mark H. Colburn. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that the above copyright notice is duplicated in all such * forms and that any documentation, advertising materials, and other * materials related to such distribution and use acknowledge that the * software was developed * by Mark H. Colburn and sponsored by The * USENIX Association. * * 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. * * $Log: replace.c,v $ * Revision 1.2 89/02/12 10:05:59 mark * 1.2 release fixes * * Revision 1.1 88/12/23 18:02:36 mark * Initial revision * */ #ifndef lint static char *ident = "$Id: replace.c,v 1.2 89/02/12 10:05:59 mark Exp $"; static char *copyright = "Copyright (c) 1989 Mark H. Colburn.\nAll rights reserved.\n"; #endif /* not lint */ /* Headers */ #include "pax.h" /* add_replstr - add a replacement string to the replacement string list * * DESCRIPTION * * Add_replstr adds a replacement string to the replacement string * list which is applied each time a file is about to be processed. * * PARAMETERS * * char *pattern - A regular expression which is to be parsed */ #ifdef __STDC__ void add_replstr(char *pattern) #else void add_replstr(pattern) char *pattern; #endif { char *p; char sep; Replstr *rptr; int len; if ((len = strlen(pattern)) < 4) { warn("Replacement string not added", "Malformed substitution syntax"); return; } if ((rptr = (Replstr *) malloc(sizeof(Replstr))) == (Replstr *)NULL) { warn("Replacement string not added", "No space"); return; } /* First character is the delimeter... */ sep = *pattern; /* Get trailing g and/or p */ p = pattern + len - 1; while (*p != sep) { if (*p == 'g') { rptr->global = 1; } else if (*p == 'p') { rptr->print = 1; } else { warn(p, "Invalid RE modifier"); } p--; } if (*p != sep) { warn("Replacement string not added", "Bad delimeters"); free(rptr); return; } /* strip off leading and trailing delimeter */ *p = '\0'; pattern++; /* find the separating '/' in the pattern */ p = pattern; while (*p) { if (*p == sep) { break; } if (*p == '\\' && *(p + 1) != '\0') { p++; } p++; } if (*p != sep) { warn("Replacement string not added", "Bad delimeters"); free(rptr); return; } *p++ = '\0'; /* * Now pattern points to 'old' and p points to 'new' and both are '\0' * terminated */ if ((rptr->comp = regcomp(pattern)) == (regexp *)NULL) { warn("Replacement string not added", "Invalid RE"); free(rptr); return; } rptr->replace = p; rptr->next = (Replstr *)NULL; if (rplhead == (Replstr *)NULL) { rplhead = rptr; rpltail = rptr; } else { rpltail->next = rptr; rpltail = rptr; } } /* rpl_name - possibly replace a name with a regular expression * * DESCRIPTION * * The string name is searched for in the list of regular expression * substituions. If the string matches any of the regular expressions * then the string is modified as specified by the user. * * PARAMETERS * * char *name - name to search for and possibly modify */ #ifdef __STDC__ void rpl_name(char *name) #else void rpl_name(name) char *name; #endif { int found = 0; int ret; Replstr *rptr; char buff[PATH_MAX + 1]; char buff1[PATH_MAX + 1]; char buff2[PATH_MAX + 1]; char *p; char *b; strcpy(buff, name); for (rptr = rplhead; !found && rptr != (Replstr *)NULL; rptr = rptr->next) { do { if ((ret = regexec(rptr->comp, buff)) != 0) { p = buff; b = buff1; while (p < rptr->comp->startp[0]) { *b++ = *p++; } p = rptr->replace; while (*p) { *b++ = *p++; } strcpy(b, rptr->comp->endp[0]); found = 1; regsub(rptr->comp, buff1, buff2); strcpy(buff, buff2); } } while (ret && rptr->global); if (found) { if (rptr->print) { fprintf(stderr, "%s >> %s\n", name, buff); } strcpy(name, buff); } } } /* get_disposition - get a file disposition * * DESCRIPTION * * Get a file disposition from the user. If the user enters 'y' * the the file is processed, anything else and the file is ignored. * If the user enters EOF, then the PAX exits with a non-zero return * status. * * PARAMETERS * * char *mode - string signifying the action to be taken on file * char *name - the name of the file * * RETURNS * * Returns 1 if the file should be processed, 0 if it should not. */ #ifdef __STDC__ int get_disposition(char *mode, char *name) #else int get_disposition(mode, name) char *mode; char *name; #endif { char ans[2]; char buf[PATH_MAX + 10]; if (f_disposition) { sprintf(buf, "%s %s? ", mode, name); if (nextask(buf, ans, sizeof(ans)) == -1 || ans[0] == 'q') { exit(0); } if (strlen(ans) == 0 || ans[0] != 'y') { return(1); } } return(0); } /* get_newname - prompt the user for a new filename * * DESCRIPTION * * The user is prompted with the name of the file which is currently * being processed. The user may choose to rename the file by * entering the new file name after the prompt; the user may press * carriage-return/newline, which will skip the file or the user may * type an 'EOF' character, which will cause the program to stop. * * PARAMETERS * * char *name - filename, possibly modified by user * int size - size of allowable new filename * * RETURNS * * Returns 0 if successfull, or -1 if an error occurred. * */ #ifdef __STDC__ int get_newname(char *name, int size) #else int get_newname(name, size) char *name; int size; #endif { char buf[PATH_MAX + 10]; if (f_interactive) { sprintf(buf, "rename %s? ", name); if (nextask(buf, name, size) == -1) { exit(0); } if (strlen(name) == 0) { return(1); } } return(0); } /sys/src/ape/cmd/pax/tar.1 664 sys sys 1367613436 4671 .\" $Id: tar.1,v 1.2 89/02/12 10:08:55 mark Exp $ .TH TAR 1 "USENIX Association" "" .SH NAME tar \- process tape archives .SH SYNOPSIS .B tar .BR \-c [ bfvw ] .I device .I block .I filename... .br .B tar .BR \-r [ bvw ] .I device .I block .RI [ filename... ] .br .B tar .BR \-t [ fv ] .I device .br .B tar .BR \-u [ bvw ] .I device .I block .br .B tar .BR \-x [ flmovw ] .I device .RI [ filename... ] .SH DESCRIPTION .I Tar reads and writes archive files which conform to the .B "Archive/Interchange File Format" specified in .IR "IEEE Std. 1003.1-1988" . .SS Options The following options are available: .TP 1i .B \-c Creates a new archive; writing begins at the beginning of the archive, instead of after the last file. .TP 1i .B \-r Writes names files to the end of the archive. .TP 1i .B \-t Lists the names of all of the files in the archive. .TP 1i .B \-u Causes named files to be added to the archive if they are not already there, or have been modified since last written into the archive. This implies the .B \-r option. .TP 1i .B \-x Extracts named files from the archive. If a named file matches a directory whose contents had been written onto the archive, that directory is recursively extracted. If a named file in the archive does not exist on the system, the file is create with the same mode as the one in the archive, except that the set-user-id and get-group-id modes are not set unless the user has appropriate privileges. .PP If the files exist, their modes are not changed except as described above. The owner, group and modification time are restored if possible. If no .I filename argument is given, the entire contents of the archive is extracted. Note that if several files with the same name are in the archive, the last one will overwrite all earlier ones. .TP 1i .B \-b Causes .I tar to use the next argument on the command line as the blocking factor for tape records. The default is 1; the maximum is 20. This option should only be used with raw magnetic tape archives. Normally, the block size is determined automatically when reading tapes. .TP 1i .B \-f Causes .I tar to use the next argument on the command line as the name of the archive instead of the default, which is usually a tape drive. If .B - is specified as a filename .I tar writes to the standard output or reads from the standard input, whichever is appropriate for the options given. Thus, .I tar can be used as the head or tail of a pipeline. .TP 1i .B \-l Tells .I tar to report if it cannot resolve all of the links to the files being archived. If .B \-l is not specified, no error messages are written to the standard output. This modifier is only valid with the .BR \-c , .B \-r and .BR \-u options. .TP 1i .B \-m Tells .I tar not to restore the modification times. The modification time of the file will be the time of extraction. This modifier is invalid with th .B \-t option. .TP 1i .B \-o Causes extracted files to take on the user and group identifier of the user running the program rather than those on the archive. This modifier is only valid with the .B \-x option. .TP 1i .B \-v Causes .I tar to operate verbosely. Usually, .I tar does its work silently, but the .B v modifier causes it to print the name of each file it processes, preceded by the option letter. With the .B \-t option, .B v gives more information about the archive entries than just the name. .TP 1i .B \-w Causes .I tar to print the action to be taken, followed by the name of the file, and then wait for the user's confirmation. If a word beginning with .B y is given, the action is performed. Any other input means "no". This modifier is invalid with the .B \-t option. .SH FILES .TP 1i /dev/tty used to prompt the user for information when the .BR \-i " or " \-y options are specified. .SH "SEE ALSO" cpio(1), dd(1), find(1), pax(1), cpio(5), tar(5) .SH COPYRIGHT Copyright (c) 1989 Mark H. Colburn. .br All rights reserved. .PP Redistribution and use in source and binary forms are permitted provided that the above copyright notice is duplicated in all such forms and that any documentation, advertising materials, and other materials related to such distribution and use acknowledge that the software was developed by Mark H. Colburn and sponsored by The USENIX Association. .PP THE 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. .SH AUTHOR Mark H. Colburn .br NAPS International .br 117 Mackubin Street, Suite 1 .br St. Paul, MN 55102 .br mark@jhereg.MN.ORG .sp 2 Sponsored by .B "The USENIX Association" for public distribution. /sys/src/ape/cmd/pax/tar.c 664 sys sys 1367613436 7949 /* $Source: /u/mark/src/pax/RCS/tar.c,v $ * * $Revision: 1.2 $ * * tar.c - tar specific functions for archive handling * * DESCRIPTION * * These routines provide a tar conforming interface to the pax * program. * * AUTHOR * * Mark H. Colburn, NAPS International (mark@jhereg.mn.org) * * Sponsored by The USENIX Association for public distribution. * * Copyright (c) 1989 Mark H. Colburn. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that the above copyright notice is duplicated in all such * forms and that any documentation, advertising materials, and other * materials related to such distribution and use acknowledge that the * software was developed by Mark H. Colburn and sponsored by The * USENIX Association. * * 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. * * $Log: tar.c,v $ * Revision 1.2 89/02/12 10:06:05 mark * 1.2 release fixes * * Revision 1.1 88/12/23 18:02:38 mark * Initial revision * */ #ifndef lint static char *ident = "$Id: tar.c,v 1.2 89/02/12 10:06:05 mark Exp $"; static char *copyright ="Copyright (c) 1989 Mark H. Colburn.\nAll rights reserved."; #endif /* not lint */ /* Headers */ #include "pax.h" /* Defines */ #define DEF_BLOCKING 20 /* default blocking factor for extract */ /* Function Prototypes */ #ifdef __STDC__ static int taropt(int , char **, char *); static void usage(void); #else /* !__STDC__ */ static int taropt(); static void usage(); #endif /* __STDC__ */ /* do_tar - main routine for tar. * * DESCRIPTION * * Provides a tar interface to the PAX program. All tar standard * command line options are supported. * * PARAMETERS * * int argc - argument count (argc from main) * char **argv - argument list (argv from main) * * RETURNS * * zero */ #ifdef __STDC__ int do_tar(int argc, char **argv) #else int do_tar(argc, argv) int argc; /* argument count (argc from main) */ char **argv; /* argument list (argv from main) */ #endif { int c; /* Option letter */ /* Set default option values */ names_from_stdin = 0; ar_file = getenv("TAPE"); /* From environment, or */ if (ar_file == 0) { ar_file = DEF_AR_FILE; /* From Makefile */ } /* * set up the flags to reflect the default pax inteface. Unfortunately * the pax interface has several options which are completely opposite * of the tar and/or cpio interfaces... */ f_unconditional = 1; f_mtime = 1; f_dir_create = 1; blocking = 0; ar_interface = TAR; ar_format = TAR; msgfile=stderr; /* Parse options */ while ((c = taropt(argc, argv, "b:cf:hlmortuvwx")) != EOF) { switch (c) { case 'b': /* specify blocking factor */ /* * FIXME - we should use a conversion routine that does * some kind of reasonable error checking, but... */ blocking = atoi(optarg); break; case 'c': /* create a new archive */ f_create = 1; break; case 'f': /* specify input/output file */ ar_file = optarg; break; case 'h': f_follow_links = 1; /* follow symbolic links */ break; case 'l': /* report unresolved links */ f_linksleft = 1; break; case 'm': /* don't restore modification times */ f_modified = 1; break; case 'o': /* take on user's group rather than * archives */ break; case 'r': /* named files are appended to archive */ f_append = 1; break; case 't': f_list = 1; /* list files in archive */ break; case 'u': /* named files are added to archive */ f_newer = 1; break; case 'v': /* verbose mode */ f_verbose = 1; break; case 'w': /* user interactive mode */ f_disposition = 1; break; case 'x': /* named files are extracted from archive */ f_extract = 1; break; case '?': usage(); exit(EX_ARGSBAD); } } /* check command line argument sanity */ if (f_create + f_extract + f_list + f_append + f_newer != 1) { (void) fprintf(stderr, "%s: you must specify exactly one of the c, t, r, u or x options\n", myname); usage(); exit(EX_ARGSBAD); } /* set the blocking factor, if not set by the user */ if (blocking == 0) { #ifdef USG if (f_extract || f_list) { blocking = DEF_BLOCKING; fprintf(stderr, "Tar: blocksize = %d\n", blocking); } else { blocking = 1; } #else /* !USG */ blocking = 20; #endif /* USG */ } blocksize = blocking * BLOCKSIZE; buf_allocate((OFFSET) blocksize); if (f_create) { open_archive(AR_WRITE); create_archive(); /* create the archive */ } else if (f_extract) { open_archive(AR_READ); read_archive(); /* extract files from archive */ } else if (f_list) { open_archive(AR_READ); read_archive(); /* read and list contents of archive */ } else if (f_append) { open_archive(AR_APPEND); append_archive(); /* append files to archive */ } if (f_linksleft) { linkleft(); /* report any unresolved links */ } return (0); } /* taropt - tar specific getopt * * DESCRIPTION * * Plug-compatible replacement for getopt() for parsing tar-like * arguments. If the first argument begins with "-", it uses getopt; * otherwise, it uses the old rules used by tar, dump, and ps. * * PARAMETERS * * int argc - argument count (argc from main) * char **argv - argument list (argv from main) * char *optstring - sring which describes allowable options * * RETURNS * * Returns the next option character in the option string(s). If the * option requires an argument and an argument was given, the argument * is pointed to by "optarg". If no option character was found, * returns an EOF. * */ #ifdef __STDC__ static int taropt(int argc, char **argv, char *optstring) #else static int taropt(argc, argv, optstring) int argc; char **argv; char *optstring; #endif { extern char *optarg; /* Points to next arg */ extern int optind; /* Global argv index */ static char *key; /* Points to next keyletter */ static char use_getopt; /* !=0 if argv[1][0] was '-' */ char c; char *place; optarg = (char *)NULL; if (key == (char *)NULL) { /* First time */ if (argc < 2) return EOF; key = argv[1]; if (*key == '-') use_getopt++; else optind = 2; } if (use_getopt) { return getopt(argc, argv, optstring); } c = *key++; if (c == '\0') { key--; return EOF; } place = strchr(optstring, c); if (place == (char *)NULL || c == ':') { fprintf(stderr, "%s: unknown option %c\n", argv[0], c); return ('?'); } place++; if (*place == ':') { if (optind < argc) { optarg = argv[optind]; optind++; } else { fprintf(stderr, "%s: %c argument missing\n", argv[0], c); return ('?'); } } return (c); } /* usage - print a helpful message and exit * * DESCRIPTION * * Usage prints out the usage message for the TAR interface and then * exits with a non-zero termination status. This is used when a user * has provided non-existant or incompatible command line arguments. * * RETURNS * * Returns an exit status of 1 to the parent process. * */ #ifdef __STDC__ static void usage(void) #else static void usage() #endif { fprintf(stderr, "Usage: %s -c[bfvw] device block filename..\n", myname); fprintf(stderr, " %s -r[bvw] device block [filename...]\n", myname); fprintf(stderr, " %s -t[vf] device\n", myname); fprintf(stderr, " %s -u[bvw] device block [filename...]\n", myname); fprintf(stderr, " %s -x[flmovw] device [filename...]\n", myname); exit(1); } /sys/src/ape/cmd/pax/ttyio.c 664 sys sys 1367613436 6386 /* $Source: /u/mark/src/pax/RCS/ttyio.c,v $ * * $Revision: 1.2 $ * * ttyio.c - Terminal/Console I/O functions for all archive interfaces * * DESCRIPTION * * These routines provide a consistent, general purpose interface to * the user via the users terminal, if it is available to the * process. * * AUTHOR * * Mark H. Colburn, NAPS International (mark@jhereg.mn.org) * * Sponsored by The USENIX Association for public distribution. * * Copyright (c) 1989 Mark H. Colburn. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that the above copyright notice is duplicated in all such * forms and that any documentation, advertising materials, and other * materials related to such distribution and use acknowledge that the * software was developed * by Mark H. Colburn and sponsored by The * USENIX Association. * * 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. * * $Log: ttyio.c,v $ * Revision 1.2 89/02/12 10:06:11 mark * 1.2 release fixes * * Revision 1.1 88/12/23 18:02:39 mark * Initial revision * */ #ifndef lint static char *ident = "$Id: ttyio.c,v 1.2 89/02/12 10:06:11 mark Exp $"; static char *copyright = "Copyright (c) 1989 Mark H. Colburn.\nAll rights reserved.\n"; #endif /* ! lint */ /* Headers */ #include "pax.h" /* open_tty - open the terminal for interactive queries * * DESCRIPTION * * Assumes that background processes ignore interrupts and that the * open() or the isatty() will fail for processes which are not * attached to terminals. Returns a file descriptor or -1 if * unsuccessful. * * RETURNS * * Returns a file descriptor which can be used to read and write * directly to the user's terminal, or -1 on failure. * * ERRORS * * If SIGINT cannot be ignored, or the open fails, or the newly opened * terminal device is not a tty, then open_tty will return a -1 to the * caller. */ #ifdef __STDC__ int open_tty(void) #else int open_tty() #endif { int fd; /* file descriptor for terminal */ SIG_T (*intr)(); /* used to restore interupts if signal fails */ if ((intr = signal(SIGINT, SIG_IGN)) == SIG_IGN) { return (-1); } signal(SIGINT, intr); if ((fd = open(TTY, O_RDWR)) < 0) { return (-1); } if (isatty(fd)) { return (fd); } close(fd); return (-1); } /* nextask - ask a question and get a response * * DESCRIPTION * * Give the user a prompt and wait for their response. The prompt, * located in "msg" is printed, then the user is allowed to type * a response to the message. The first "limit" characters of the * user response is stored in "answer". * * Nextask ignores spaces and tabs. * * PARAMETERS * * char *msg - Message to display for user * char *answer - Pointer to user's response to question * int limit - Limit of length for user's response * * RETURNS * * Returns the number of characters in the user response to the * calling function. If an EOF was encountered, a -1 is returned to * the calling function. If an error occured which causes the read * to return with a value of -1, then the function will return a * non-zero return status to the calling process, and abort * execution. */ #ifdef __STDC__ int nextask(char *msg, char *answer, int limit) #else int nextask(msg, answer, limit) char *msg; /* message to display for user */ char *answer; /* pointer to user's response to question */ int limit; /* limit of length for user's response */ #endif { int idx; /* index into answer for character input */ int got; /* number of characters read */ char c; /* character read */ if (ttyf < 0) { fatal("/dev/tty Unavailable"); } write(ttyf, msg, (uint) strlen(msg)); idx = 0; while ((got = read(ttyf, &c, 1)) == 1) { if (c == '\n') { break; } else if (c == ' ' || c == '\t') { continue; } else if (idx < limit - 1) { answer[idx++] = c; } } if (got == 0) { /* got an EOF */ return(-1); } if (got < 0) { fatal(strerror()); } answer[idx] = '\0'; return(0); } /* lineget - get a line from a given stream * * DESCRIPTION * * Get a line of input for the stream named by "stream". The data on * the stream is put into the buffer "buf". * * PARAMETERS * * FILE *stream - Stream to get input from * char *buf - Buffer to put input into * * RETURNS * * Returns 0 if successful, -1 at EOF. */ #ifdef __STDC__ int lineget(FILE *stream, char *buf) #else int lineget(stream, buf) FILE *stream; /* stream to get input from */ char *buf; /* buffer to put input into */ #endif { int c; for (;;) { if ((c = getc(stream)) == EOF) { return (-1); } if (c == '\n') { break; } *buf++ = c; } *buf = '\0'; return (0); } /* next - Advance to the next archive volume. * * DESCRIPTION * * Prompts the user to replace the backup medium with a new volume * when the old one is full. There are some cases, such as when * archiving to a file on a hard disk, that the message can be a * little surprising. Assumes that background processes ignore * interrupts and that the open() or the isatty() will fail for * processes which are not attached to terminals. Returns a file * descriptor or -1 if unsuccessful. * * PARAMETERS * * int mode - mode of archive (READ, WRITE, PASS) */ #ifdef __STDC__ void next(int mode) #else void next(mode) int mode; /* mode of archive (READ, WRITE, PASS) */ #endif { char msg[200]; /* buffer for message display */ char answer[20]; /* buffer for user's answer */ int ret; close_archive(); sprintf(msg, "%s: Ready for volume %u\n%s: Type \"go\" when ready to proceed (or \"quit\" to abort): \07", myname, arvolume + 1, myname); for (;;) { ret = nextask(msg, answer, sizeof(answer)); if (ret == -1 || strcmp(answer, "quit") == 0) { fatal("Aborted"); } if (strcmp(answer, "go") == 0 && open_archive(mode) == 0) { break; } } warnarch("Continuing", (OFFSET) 0); } /sys/src/ape/cmd/pax/warn.c 664 sys sys 1367613436 5605 /* $Source: /u/mark/src/pax/RCS/warn.c,v $ * * $Revision: 1.2 $ * * warn.c - miscellaneous user warning routines * * DESCRIPTION * * These routines provide the user with various forms of warning * and informational messages. * * AUTHOR * * Mark H. Colburn, NAPS International (mark@jhereg.mn.org) * * Sponsored by The USENIX Association for public distribution. * * Copyright (c) 1989 Mark H. Colburn. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that the above copyright notice is duplicated in all such * forms and that any documentation, advertising materials, and other * materials related to such distribution and use acknowledge that the * software was developed * by Mark H. Colburn and sponsored by The * USENIX Association. * * 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. * * $Log: warn.c,v $ * Revision 1.2 89/02/12 10:06:15 mark * 1.2 release fixes * * Revision 1.1 88/12/23 18:02:40 mark * Initial revision * */ #ifndef lint static char *ident = "$Id: warn.c,v 1.2 89/02/12 10:06:15 mark Exp $"; static char *copyright = "Copyright (c) 1989 Mark H. Colburn.\nAll rights reserved.\n"; #endif /* ! lint */ /* Headers */ #include "pax.h" /* Function Prototypes */ #ifdef __STDC__ static void prsize(FILE *, OFFSET); #else /* !__STDC__ */ static void prsize(); #endif /* __STDC__ */ /* warnarch - print an archive-related warning message and offset * * DESCRIPTION * * Present the user with an error message and an archive offset at * which the error occured. This can be useful for diagnosing or * fixing damaged archives. * * PARAMETERS * * char *msg - A message string to be printed for the user. * OFFSET adjust - An adjustment which is added to the current * archive position to tell the user exactly where * the error occurred. */ #ifdef __STDC__ void warnarch(char *msg, OFFSET adjust) #else void warnarch(msg, adjust) char *msg; OFFSET adjust; #endif { fprintf(stderr, "%s: [offset ", myname); prsize(stderr, total - adjust); fprintf(stderr, "]: %s\n", msg); } /* strerror - return pointer to appropriate system error message * * DESCRIPTION * * Get an error message string which is appropriate for the setting * of the errno variable. * * RETURNS * * Returns a pointer to a string which has an appropriate error * message for the present value of errno. The error message * strings are taken from sys_errlist[] where appropriate. If an * appropriate message is not available in sys_errlist, then a * pointer to the string "Unknown error (errno )" is * returned instead. */ #ifdef __STDC__ char *strerror(void) #else char *strerror() #endif { #ifdef _POSIX_SOURCE #undef strerror return (strerror(errno)); #else static char msg[40]; /* used for "Unknown error" messages */ if (errno > 0 && errno < sys_nerr) { return (sys_errlist[errno]); } sprintf(msg, "Unknown error (errno %d)", errno); return (msg); #endif } /* prsize - print a file offset on a file stream * * DESCRIPTION * * Prints a file offset to a specific file stream. The file offset is * of the form "%dm+%dk+%d", where the number preceeding the "m" and * the "k" stand for the number of Megabytes and the number of * Kilobytes, respectivley, which have been processed so far. * * PARAMETERS * * FILE *stream - Stream which is to be used for output * OFFSET size - Current archive position to be printed on the output * stream in the form: "%dm+%dk+%d". * */ #ifdef __STDC__ static void prsize(FILE *stream, OFFSET size) #else static void prsize(stream, size) FILE *stream; /* stream which is used for output */ OFFSET size; /* current archive position to be printed */ #endif { OFFSET n; if (n = (size / (1024L * 1024L))) { fprintf(stream, "%ldm+", n); size -= n * 1024L * 1024L; } if (n = (size / 1024L)) { fprintf(stream, "%ldk+", n); size -= n * 1024L; } fprintf(stream, "%ld", size); } /* fatal - print fatal message and exit * * DESCRIPTION * * Fatal prints the program's name along with an error message, then * exits the program with a non-zero return code. * * PARAMETERS * * char *why - description of reason for termination * * RETURNS * * Returns an exit code of 1 to the parent process. */ #ifdef __STDC__ void fatal(char *why) #else void fatal(why) char *why; /* description of reason for termination */ #endif { fprintf(stderr, "%s: %s\n", myname, why); exit(1); } /* warn - print a warning message * * DESCRIPTION * * Print an error message listing the program name, the actual error * which occurred and an informational message as to why the error * occurred on the standard error device. The standard error is * flushed after the error is printed to assure that the user gets * the message in a timely fasion. * * PARAMETERS * * char *what - Pointer to string describing what failed. * char *why - Pointer to string describing why did it failed. */ #ifdef __STDC__ void warn(char *what, char *why) #else void warn(what, why) char *what; /* message as to what the error was */ char *why; /* explanation why the error occurred */ #endif { fprintf(stderr, "%s: %s : %s\n", myname, what, why); fflush(stderr); } /sys/src/ape/cmd/pax/wildmat.c 664 sys sys 1367613436 5025 /* $Source: /u/mark/src/pax/RCS/wildmat.c,v $ * * $Revision: 1.2 $ * * wildmat.c - simple regular expression pattern matching routines * * DESCRIPTION * * These routines provide simple UNIX style regular expression matching. * They were originally written by Rich Salz, the comp.sources.unix * moderator for inclusion in some of his software. These routines * were released into the public domain and used by John Gilmore in * USTAR. * * AUTHORS * * Mark H. Colburn, NAPS International (mark@jhereg.mn.org) * John Gilmore (gnu@hoptoad) * Rich Salz (rs@uunet.uu.net) * * * Sponsored by The USENIX Association for public distribution. * * Copyright (c) 1989 Mark H. Colburn. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that the above copyright notice is duplicated in all such * forms and that any documentation, advertising materials, and other * materials related to such distribution and use acknowledge that the * software was developed * by Mark H. Colburn and sponsored by The * USENIX Association. * * 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. * * $Log: wildmat.c,v $ * Revision 1.2 89/02/12 10:06:20 mark * 1.2 release fixes * * Revision 1.1 88/12/23 18:02:41 mark * Initial revision * */ #ifndef lint static char *ident = "$Id: wildmat.c,v 1.2 89/02/12 10:06:20 mark Exp $"; static char *copyright = "Copyright (c) 1989 Mark H. Colburn.\nAll rights reserved.\n"; #endif /* ! lint */ /* Headers */ #include "pax.h" /* Function Prototypes */ #ifdef __STDC__ static int star(char *, char *); #else /* !__STDC__ */ static int star(); #endif /* __STDC__ */ /* * star - handle trailing * in a regular expression * * DESCRIPTION * * Star is used to match filename expansions containing a trailing * asterisk ('*'). Star call wildmat() to determine if the substring * passed to it is matches the regular expression. * * PARAMETERS * * char *source - The source string which is to be compared to the * regular expression pattern. * char *pattern - The regular expression which we are supposed to * match to. * * RETURNS * * Returns non-zero if the entire source string is completely matched by * the regular expression pattern, returns 0 otherwise. This is used to * see if *'s in a pattern matched the entire source string. * */ #ifdef __STDC__ static int star(char *source, char *pattern) #else static int star(source, pattern) char *source; /* source operand */ char *pattern; /* regular expression to match */ #endif { while (!wildmat(pattern, source)) { if (*++source == '\0') { return (0); } } return (1); } /* * wildmat - match a regular expression * * DESCRIPTION * * Wildmat attempts to match the string pointed to by source to the * regular expression pointed to by pattern. The subset of regular * expression syntax which is supported is defined by POSIX P1003.2 * FILENAME EXPANSION rules. * * PARAMETERS * * char *pattern - The regular expression which we are supposed to * match to. * char *source - The source string which is to be compared to the * regular expression pattern. * * RETURNS * * Returns non-zero if the source string matches the regular expression * pattern specified, returns 0 otherwise. * */ #ifdef __STDC__ int wildmat(char *pattern, char *source) #else int wildmat(pattern, source) char *pattern; /* regular expression to match */ char *source; /* source operand */ #endif { int last; /* last character matched */ int matched; /* !0 if a match occurred */ int reverse; /* !0 if sense of match is reversed */ for (; *pattern; source++, pattern++) { switch (*pattern) { case '\\': /* Literal match with following character */ pattern++; /* FALLTHRU */ default: if (*source != *pattern) { return (0); } continue; case '?': /* Match anything. */ if (*source == '\0') { return (0); } continue; case '*': /* Trailing star matches everything. */ return (*++pattern ? star(source, pattern) : 1); case '[': /* [^....] means inverse character class. */ if (reverse = pattern[1] == '^') { pattern++; } for (last = 0400, matched = 0; *++pattern && *pattern != ']'; last = *pattern) { /* This next line requires a good C compiler. */ if (*pattern == '-' ? *source <= *++pattern && *source >= last : *source == *pattern) { matched = 1; } } if (matched == reverse) { return (0); } continue; } } /* * For "tar" use, matches that end at a slash also work. --hoptoad!gnu */ return (*source == '\0' || *source == '/'); } /sys/src/ape/cmd/pdksh 20000000775 sys sys 1371506136 0 /sys/src/ape/cmd/pdksh/README 664 sys sys 1367613436 9492 Last updated Jul '99 for pdksh-5.2.14. (check ftp://ftp.cs.mun.ca:/pub/pdksh/ or http://www.cs.mun.ca/~michael/pdksh/ for new versions/patches) PD-ksh is a mostly complete AT&T ksh look-alike (see NOTES file for a list of things not supported). Work is mostly finished to make it fully compatible with both POSIX and AT&T ksh (when the two don't conflict). Since pdksh is free and compiles and runs on most common unix systems, it is very useful in creating a consistent user interface across multiple machines. For example, in the CS dept. of MUN, pdksh is installed on a variety of machines including Suns, HPs, DecStations, pcs running Linux, etc., and is the login shell of ~5200 users. PDksh is currently being maintained by Michael Rendell (michael@cs.mun.ca), who took over from Simon J. Gerraty (sjg@zen.void.oz.au) at the later's suggestion. A short list of things that have been added since the last public pdksh release (4.9) are auto-configuration, arrays, $(( .. )), [[ .. ]], variable attributes, co-processes, extended file globbing, many POSIXisms and many bug fixes. See the NEWS and ChangeLog files for other features added and bugs fixed. Note that pdksh is provided AS IS, with NO WARRANTY, either expressed or implied. Also note that although the bulk of the code in pdksh is in the public domain, some files are copyrighten (but freely distributable) and subject to certain conditions (eg, don't remove copyright, document any changes, etc.). See the LEGAL file for details. If you would like to be notified via email of new releases as they become available, send mail to pdksh-request@cs.mun.ca with subject "send release notifications" (or "don't send release notifications" to stop them). Files of interest: NEWS short list of noticeable changes in various versions. CONTRIBUTORS short history of pdksh, people who contributed, etc. NOTES lists of known bugs in pdksh, at&t ksh, and posix. PROJECTS list of things that need to be done in pdksh. BUG-REPORTS list of recently reported bugs that have been fixed and all reported bugs that haven't been fixed. LEGAL A file detailing legal issues concerning pdksh. etc/* system profile and kshrc files used by Simon J. Gerraty. misc/README* readme files from previous versions. misc/Changes* changelog files from previous versions. os2/* files and info needed to compile ksh on os/2. tests/* pdksh's regression testing system. Compiling/Installing: The quick way: ./configure make make check # optional make install # will install /usr/local/bin/ksh # and /usr/local/man/man1/ksh.1 [add path-to-installed-pdksh to /etc/shells] The more detailed description: * run "configure --help | your-favorite-pager" and look at the --enable-* and --disable-* options (they are at the end). Select any you options you wish to enable/disable (most people can skip this step). * run configure: this is a GNU autoconf configure script that will generate a Makefile and a config.h. Some of the useful options to configure are: --prefix=PATH indicates the directory tree under which the binary and man page are installed (ie, PATH/bin/ksh and PATH/man/man1/ksh.1). The default prefix is /usr/local. --exec-prefix=PATH overrides --prefix for machine dependent files (ie, the ksh binary) --program-prefix=pd install binary and man page as pdksh and pdksh.1 --verbose show what is being defined as script runs Note that you don't have to build in the source directory. To build in a separate directory, do something like: $ mkdir objs $ cd objs $ ../configure --verbose .... $ make See the file INSTALL for a more complete description of configure and its generic options (ksh specific options are documented in the --help output) * miscellaneous configuration notes: * If your make doesn't understand VPATH, you must compile in the source directory. * On DecStations, MIPS and SONY machines with older C compilers that can't handle "int * volatile x", you should use gcc or turn off optimization. The problem is configure defines volatile to nothing since the compiler can't handle it properly, but the compiler does optimizations that the volatile is meant to prevent. So. Use gcc. * On MIPS RISC/os 5.0 systems, sysv environment, is messed up - it defines sigset_t, but not any of the rest of the posix signals (the sigset_t typedef should be in the ifdef KERNEL section) - also doesn't have waitpid() or wait3(). Things compile up ok in the svr4 environment, but it dumps core in __start (perhaps our system doesn't have the full svr4 environ?). Try compiling in the bsd43 environ instead (still not perfect - see BUG-REPORTS file), using gcc - cc has problems with macro expansions in the argument of a macro (in this case, the ARGS macro). * On TitanOS (Stardent/Titan), use `CC="cc -43" configure ...'. When configure finishes, edit config.h, undef HAVE_DIRENT_H and define HAVE_SYS_DIR_H (the dirent.h header file is broken). * On Linux (red hat distribution), check that /dev/tty has mode 0666 (not mode 0644). If it has the wrong permissions, ksh will print warnings about not being able to do job control. * on NeXT machines (3.2, probably other releases), the siglist.out file won't be generated correctly if you try to use the system's compiler (it has a broken cc -E and strange header files). There are two ways to make it work: 1) if you have gcc, use it (for everything). Alternatively, force configure to use it for CPP, i.e., use CPP="gcc -E" configure ... 2) Force configure to use some extra CPPFLAGS, using CPPFLAGS="XXX" configure ... where XXX is obtained from running "cc -v YYY.c" on some C file. Look at the options passed to cpp (there are lots of them...) and replace the XXX above with them. Make sure you do a "make distclean" (or "rm config.cache") if you re-run configure with a difference CPP or CPPFLAGS. Also note that if you are building multiple arch binaries, you will have to specify both CC and CPP. * run make: everything should compile and link without problems. * run make check: this fires up a perl script that checks for some known and some fixed bugs. The script prints pass/fail for tests it expected to pass/fail, and PASS/FAIL for tests it expected to fail/pass. If you don't have perl, or if your perl doesn't work (most common problem is the .ph header files are missing or broken), you can run ENV= path-to-pdksh-executable misc/Bugs path-to-pdksh-executable instead. * run make install: this installs ksh (in /usr/local/bin/ksh by default, or where ever you told configure to put things). * add path-to-installed-pdksh to /etc/shells if it's not already there. This is only needed if you intend to use pdksh as a login shell (things like ftp won't allow users to connect in if their shell isn't in this file). The following is a list of machines that pdksh is reported to work on: -/PC Linux 1.x,2.x -/PC NetBSD 0.9a -/PC BSDI 1.1 -/PC FreeBSD 2.x, 3.x -/PC OpenBSD -/PC Interactive/Sunsoft 3.0.1 and 4.1 (note that problems have been reported with isc3.2 - see the BUG-REPORTS file) -/PC OS/2 Commadore/Amiga NetBSD 1.0 Dec/alpha OSF/1 v2.x, v3.x Dec/alpha NetBSD 1.1B Dec/pmax Ultrix 4.2 Dec/vax Ultrix 2.2 (not tested recently :-)) Dec/vax 4.3BSD+NFS (MtXinu) (not tested recently :-)) HP/pa HP-UX 9.01 IBM/RS/6000 AIX 3.2.5 MIPS/m120 RISC/os 5.0 (bsd43 environ) NeXT NeXTStep 3.2 SGI/IRIX 6.2 Sun/sun4 SunOS 4.1.3, 4.1.4 Sun/sun4 Solaris 2.x Sun/sun386i SunOS 4.0.2 Sun/sun3 SunOS 4.0.3, 4.1.1_U1 Stardent/TitanOS 4.2 Newer versions of pdksh may be available from ftp://ftp.cs.mun.ca:/pub/pdksh/ you may want to check for one if you run into any problems, as the problem may already be fixed (you can get new release notifications automatically - see above). The file pdksh-unstable-XXX.tar.gz has the very latest version which may not compile (it is generated automatically when changes are detected in the main source repository) - it is for those who want to follow changes as they are made. You can send bug reports, fixes, and enhancements to pdksh@cs.mun.ca (please don't assume I will see bug reports that are posted to some newsgroup or mailing list - I probably won't). If you are reporting a bug (with or without a fix), please include * the version of pdksh you are using (see version.c, or, if you are running pdksh, try echo $KSH_VERSION), * the machine, operating system and compiler you are using, * and a description of how to repeat the bug (a small shell script that demonstrates the bug is best). as well as the following, if relevant (if you aren't sure, include them) * what options you are using (both configure options and set -o options) * the output of configure, with the verbose flag (eg, make distclean; ./configure --verbose) * the contents of config.log (this is created by the configure script) * if you are using gcc (the GNU C compiler), which version it is. BTW, THE MOST FREQUENTLY REPORTED BUG IS echo hi | read a; echo $a # Does not print hi I'm aware of this and there is no need to report it. Michael Rendell, michael@cs.mun.ca /sys/src/ape/cmd/pdksh/README.Plan9 664 sys sys 1367613436 792 This is a Plan 9 port of pdksh version 5.2.14, a public domain implementation of the Korn shell. The entirety of the code is in the public domain. There were two non-public domain pieces (sigact.[ch] and aclocal.m4) but they are gone. This has not been significantly tested under Plan 9 for lack (not want) of a complete Perl distribution with which to run the test suite. THIS IS NOT A COMPLETE PDKSH DISTRIBUTION. This is only the source code required to compile and run the shell under Plan 9. For revision history and any other information, look at the web page mentioned in the README file. Russ Cox March 2000 12 June 2000: rsc: Added -I flag so you can bind pdksh over rc for use in mk, a dubious justification. 31 Dec 2002: rsc: Killed all ifdefs. /sys/src/ape/cmd/pdksh/alloc.c 664 sys sys 1367613436 13935 /* * area-based allocation built on malloc/free */ #include "sh.h" # if DEBUG_ALLOC void acheck ARGS((Area *ap)); # define ACHECK(ap) acheck(ap) # else /* DEBUG_ALLOC */ # define ACHECK(ap) # endif /* DEBUG_ALLOC */ #define ICELLS 200 /* number of Cells in small Block */ typedef union Cell Cell; typedef struct Block Block; /* * The Cells in a Block are organized as a set of objects. * Each object (pointed to by dp) begins with the block it is in * (dp-2)->block, then has a size in (dp-1)->size, which is * followed with "size" data Cells. Free objects are * linked together via dp->next. */ #define NOBJECT_FIELDS 2 /* the block and size `fields' */ union Cell { size_t size; Cell *next; Block *block; struct {int _;} junk; /* alignment */ double djunk; /* alignment */ }; struct Block { Block *next; /* list of Blocks in Area */ Block *prev; /* previous block in list */ Cell *freelist; /* object free list */ Cell *last; /* &b.cell[size] */ Cell cell [1]; /* [size] Cells for allocation */ }; static Block aempty = {&aempty, &aempty, aempty.cell, aempty.cell}; static void ablockfree ARGS((Block *bp, Area *ap)); static void *asplit ARGS((Area *ap, Block *bp, Cell *fp, Cell *fpp, int cells)); /* create empty Area */ Area * ainit(ap) register Area *ap; { ap->freelist = &aempty; ACHECK(ap); return ap; } /* free all object in Area */ void afreeall(ap) register Area *ap; { register Block *bp; register Block *tmp; ACHECK(ap); bp = ap->freelist; if (bp != NULL && bp != &aempty) { do { tmp = bp; bp = bp->next; free((void*)tmp); } while (bp != ap->freelist); ap->freelist = &aempty; } ACHECK(ap); } /* allocate object from Area */ void * alloc(size, ap) size_t size; register Area *ap; { int cells, acells; Block *bp = 0; Cell *fp = 0, *fpp = 0; ACHECK(ap); if (size <= 0) aerror(ap, "allocate bad size"); cells = (unsigned)(size + sizeof(Cell) - 1) / sizeof(Cell); /* allocate at least this many cells */ acells = cells + NOBJECT_FIELDS; /* * Only attempt to track small objects - let malloc deal * with larger objects. (this way we don't have to deal with * coalescing memory, or with releasing it to the system) */ if (cells <= ICELLS) { /* find free Cell large enough */ for (bp = ap->freelist; ; bp = bp->next) { for (fpp = NULL, fp = bp->freelist; fp != bp->last; fpp = fp, fp = fp->next) { if ((fp-1)->size >= cells) goto Found; } /* wrapped around Block list, create new Block */ if (bp->next == ap->freelist) { bp = 0; break; } } /* Not much free space left? Allocate a big object this time */ acells += ICELLS; } if (bp == 0) { bp = (Block*) malloc(offsetof(Block, cell[acells])); if (bp == NULL) aerror(ap, "cannot allocate"); if (ap->freelist == &aempty) { ap->freelist = bp->next = bp->prev = bp; } else { bp->next = ap->freelist->next; ap->freelist->next->prev = bp; ap->freelist->next = bp; bp->prev = ap->freelist; } bp->last = bp->cell + acells; /* initial free list */ fp = bp->freelist = bp->cell + NOBJECT_FIELDS; (fp-1)->size = acells - NOBJECT_FIELDS; (fp-2)->block = bp; fp->next = bp->last; fpp = NULL; } Found: return asplit(ap, bp, fp, fpp, cells); } /* Do the work of splitting an object into allocated and (possibly) unallocated * objects. Returns the `allocated' object. */ static void * asplit(ap, bp, fp, fpp, cells) Area *ap; Block *bp; Cell *fp; Cell *fpp; int cells; { Cell *dp = fp; /* allocated object */ int split = (fp-1)->size - cells; ACHECK(ap); if (split < 0) aerror(ap, "allocated object too small"); if (split <= NOBJECT_FIELDS) { /* allocate all */ fp = fp->next; } else { /* allocate head, free tail */ Cell *next = fp->next; /* needed, as cells may be 0 */ ap->freelist = bp; /* next time, start looking for space here */ (fp-1)->size = cells; fp += cells + NOBJECT_FIELDS; (fp-1)->size = split - NOBJECT_FIELDS; (fp-2)->block = bp; fp->next = next; } if (fpp == NULL) bp->freelist = fp; else fpp->next = fp; ACHECK(ap); return (void*) dp; } /* change size of object -- like realloc */ void * aresize(ptr, size, ap) register void *ptr; size_t size; Area *ap; { int cells; Cell *dp = (Cell*) ptr; int oldcells = dp ? (dp-1)->size : 0; ACHECK(ap); if (size <= 0) aerror(ap, "allocate bad size"); /* New size (in cells) */ cells = (unsigned)(size - 1) / sizeof(Cell) + 1; /* Is this a large object? If so, let malloc deal with it * directly (unless we are crossing the ICELLS border, in * which case the alloc/free below handles it - this should * cut down on fragmentation, and will also keep the code * working (as it assumes size < ICELLS means it is not * a `large object'). */ if (oldcells > ICELLS && cells > ICELLS) { Block *bp = (dp-2)->block; Block *nbp; /* Saved in case realloc fails.. */ Block *next = bp->next, *prev = bp->prev; if (bp->freelist != bp->last) aerror(ap, "allocation resizing free pointer"); nbp = realloc((void *) bp, offsetof(Block, cell[cells + NOBJECT_FIELDS])); if (!nbp) { /* Have to clean up... */ /* NOTE: If this code changes, similar changes may be * needed in ablockfree(). */ if (next == bp) /* only block */ ap->freelist = &aempty; else { next->prev = prev; prev->next = next; if (ap->freelist == bp) ap->freelist = next; } aerror(ap, "cannot re-allocate"); } /* If location changed, keep pointers straight... */ if (nbp != bp) { if (next == bp) /* only one block */ nbp->next = nbp->prev = nbp; else { next->prev = nbp; prev->next = nbp; } if (ap->freelist == bp) ap->freelist = nbp; dp = nbp->cell + NOBJECT_FIELDS; (dp-2)->block = nbp; } (dp-1)->size = cells; nbp->last = nbp->cell + cells + NOBJECT_FIELDS; nbp->freelist = nbp->last; ACHECK(ap); return (void*) dp; } /* Check if we can just grow this cell * (need to check that cells < ICELLS so we don't make an * object a `large' - that would mess everything up). */ if (dp && cells > oldcells && cells <= ICELLS) { Cell *fp, *fpp; Block *bp = (dp-2)->block; int need = cells - oldcells - NOBJECT_FIELDS; /* XXX if we had a flag in an object indicating * if the object was free/allocated, we could * avoid this loop (perhaps) */ for (fpp = NULL, fp = bp->freelist; fp != bp->last && dp + oldcells + NOBJECT_FIELDS <= fp ; fpp = fp, fp = fp->next) { if (dp + oldcells + NOBJECT_FIELDS == fp && (fp-1)->size >= need) { Cell *np = asplit(ap, bp, fp, fpp, need); /* May get more than we need here */ (dp-1)->size += (np-1)->size + NOBJECT_FIELDS; ACHECK(ap); return ptr; } } } /* Check if we can just shrink this cell * (if oldcells > ICELLS, this is a large object and we leave * it to malloc...) * Note: this also handles cells == oldcells (a no-op). */ if (dp && cells <= oldcells && oldcells <= ICELLS) { int split; split = oldcells - cells; if (split <= NOBJECT_FIELDS) /* cannot split */ ; else { /* shrink head, free tail */ Block *bp = (dp-2)->block; (dp-1)->size = cells; dp += cells + NOBJECT_FIELDS; (dp-1)->size = split - NOBJECT_FIELDS; (dp-2)->block = bp; afree((void*)dp, ap); } /* ACHECK() done in afree() */ return ptr; } /* Have to do it the hard way... */ ptr = alloc(size, ap); if (dp != NULL) { size_t s = (dp-1)->size * sizeof(Cell); if (s > size) s = size; memcpy(ptr, dp, s); afree((void *) dp, ap); } /* ACHECK() done in alloc()/afree() */ return ptr; } void afree(ptr, ap) void *ptr; register Area *ap; { register Block *bp; register Cell *fp, *fpp; register Cell *dp = (Cell*)ptr; ACHECK(ap); if (ptr == 0) aerror(ap, "freeing null pointer"); bp = (dp-2)->block; /* If this is a large object, just free it up... */ /* Release object... */ if ((dp-1)->size > ICELLS) { ablockfree(bp, ap); ACHECK(ap); return; } if (dp < &bp->cell[NOBJECT_FIELDS] || dp >= bp->last) aerror(ap, "freeing memory outside of block (corrupted?)"); /* find position in free list */ /* XXX if we had prev/next pointers for objects, this loop could go */ for (fpp = NULL, fp = bp->freelist; fp < dp; fpp = fp, fp = fp->next) ; if (fp == dp) aerror(ap, "freeing free object"); /* join object with next */ if (dp + (dp-1)->size == fp-NOBJECT_FIELDS) { /* adjacent */ (dp-1)->size += (fp-1)->size + NOBJECT_FIELDS; dp->next = fp->next; } else /* non-adjacent */ dp->next = fp; /* join previous with object */ if (fpp == NULL) bp->freelist = dp; else if (fpp + (fpp-1)->size == dp-NOBJECT_FIELDS) { /* adjacent */ (fpp-1)->size += (dp-1)->size + NOBJECT_FIELDS; fpp->next = dp->next; } else /* non-adjacent */ fpp->next = dp; /* If whole block is free (and we have some other blocks * around), release this block back to the system... */ if (bp->next != bp && bp->freelist == bp->cell + NOBJECT_FIELDS && bp->freelist + (bp->freelist-1)->size == bp->last /* XXX and the other block has some free memory? */ ) ablockfree(bp, ap); ACHECK(ap); } static void ablockfree(bp, ap) Block *bp; Area *ap; { /* NOTE: If this code changes, similar changes may be * needed in alloc() (where realloc fails). */ if (bp->next == bp) /* only block */ ap->freelist = &aempty; else { bp->next->prev = bp->prev; bp->prev->next = bp->next; if (ap->freelist == bp) ap->freelist = bp->next; } free((void*) bp); } # if DEBUG_ALLOC void acheck(ap) Area *ap; { Block *bp, *bpp; Cell *dp, *dptmp, *fp; int ok = 1; int isfree; static int disabled; if (disabled) return; if (!ap) { disabled = 1; aerror(ap, "acheck: null area pointer"); } bp = ap->freelist; if (!bp) { disabled = 1; aerror(ap, "acheck: null area freelist"); } /* Nothing to check... */ if (bp == &aempty) return; bpp = ap->freelist->prev; while (1) { if (bp->prev != bpp) { shellf("acheck: bp->prev != previous\n"); ok = 0; } fp = bp->freelist; for (dp = &bp->cell[NOBJECT_FIELDS]; dp != bp->last; ) { if ((dp-2)->block != bp) { shellf("acheck: fragment's block is wrong\n"); ok = 0; } isfree = dp == fp; if ((dp-1)->size == 0 && isfree) { shellf("acheck: 0 size frag\n"); ok = 0; } if ((dp-1)->size > ICELLS && !isfree && (dp != &bp->cell[NOBJECT_FIELDS] || dp + (dp-1)->size != bp->last)) { shellf("acheck: big cell doesn't make up whole block\n"); ok = 0; } if (isfree) { if (dp->next <= dp) { shellf("acheck: free fragment's next <= self\n"); ok = 0; } if (dp->next > bp->last) { shellf("acheck: free fragment's next > last\n"); ok = 0; } fp = dp->next; } dptmp = dp + (dp-1)->size; if (dptmp > bp->last) { shellf("acheck: next frag out of range\n"); ok = 0; break; } else if (dptmp != bp->last) { dptmp += NOBJECT_FIELDS; if (dptmp > bp->last) { shellf("acheck: next frag just out of range\n"); ok = 0; break; } } if (isfree && dptmp == fp && dptmp != bp->last) { shellf("acheck: adjacent free frags\n"); ok = 0; } else if (dptmp > fp) { shellf("acheck: free frag list messed up\n"); ok = 0; } dp = dptmp; } bpp = bp; bp = bp->next; if (bp == ap->freelist) break; } if (!ok) { disabled = 1; aerror(ap, "acheck failed"); } } void aprint(ap, ptr, size) register Area *ap; void *ptr; size_t size; { Block *bp; if (!ap) shellf("aprint: null area pointer\n"); else if (!(bp = ap->freelist)) shellf("aprint: null area freelist\n"); else if (bp == &aempty) shellf("aprint: area is empty\n"); else { int i; Cell *dp, *fp; Block *bpp; bpp = ap->freelist->prev; for (i = 0; ; i++) { if (ptr) { void *eptr = (void *) (((char *) ptr) + size); /* print block only if it overlaps ptr/size */ if (!((ptr >= (void *) bp && ptr <= (void *) bp->last) || (eptr >= (void *) bp && eptr <= (void *) bp->last))) continue; shellf("aprint: overlap of 0x%p .. 0x%p\n", ptr, eptr); } if (bp->prev != bpp || bp->next->prev != bp) shellf( "aprint: BAD prev pointer: bp %p, bp->prev %p, bp->next %p, bpp=%p\n", bp, bp->prev, bp->next, bpp); shellf("aprint: block %2d (p=%p,%p,n=%p): 0x%p .. 0x%p (%ld)\n", i, bp->prev, bp, bp->next, bp->cell, bp->last, (long) ((char *) bp->last - (char *) bp->cell)); fp = bp->freelist; if (bp->last <= bp->cell + NOBJECT_FIELDS) shellf( "aprint: BAD bp->last too small: %p <= %p\n", bp->last, bp->cell + NOBJECT_FIELDS); if (bp->freelist < bp->cell + NOBJECT_FIELDS || bp->freelist > bp->last) shellf( "aprint: BAD bp->freelist %p out of range: %p .. %p\n", bp->freelist, bp->cell + NOBJECT_FIELDS, bp->last); for (dp = bp->cell; dp != bp->last ; ) { dp += NOBJECT_FIELDS; shellf( "aprint: 0x%p .. 0x%p (%ld) %s\n", (dp-NOBJECT_FIELDS), (dp-NOBJECT_FIELDS) + (dp-1)->size + NOBJECT_FIELDS, (long) ((dp-1)->size + NOBJECT_FIELDS) * sizeof(Cell), dp == fp ? "free" : "allocated"); if ((dp-2)->block != bp) shellf( "aprint: BAD dp->block %p != bp %p\n", (dp-2)->block, bp); if (dp > bp->last) shellf( "aprint: BAD dp gone past block: %p > %p\n", dp, bp->last); if (dp > fp) shellf( "aprint: BAD dp gone past free: %p > %p\n", dp, fp); if (dp == fp) { fp = fp->next; if (fp < dp || fp > bp->last) shellf( "aprint: BAD free object %p out of range: %p .. %p\n", fp, dp, bp->last); } dp += (dp-1)->size; } bpp = bp; bp = bp->next; if (bp == ap->freelist) break; } } } #endif /sys/src/ape/cmd/pdksh/c_ksh.c 664 sys sys 1367613436 32057 /* * built-in Korn commands: c_* */ #include "sh.h" #include "ksh_stat.h" #include #ifdef __CYGWIN__ #include #endif /* __CYGWIN__ */ int c_cd(wp) char **wp; { int optc; int physical = Flag(FPHYSICAL); int cdnode; /* was a node from cdpath added in? */ int printpath = 0; /* print where we cd'd? */ int rval; struct tbl *pwd_s, *oldpwd_s; XString xs; char *xp; char *dir, *try, *pwd; int phys_path; char *cdpath; while ((optc = ksh_getopt(wp, &builtin_opt, "LP")) != EOF) switch (optc) { case 'L': physical = 0; break; case 'P': physical = 1; break; case '?': return 1; } wp += builtin_opt.optind; if (Flag(FRESTRICTED)) { bi_errorf("restricted shell - can't cd"); return 1; } pwd_s = global("PWD"); oldpwd_s = global("OLDPWD"); if (!wp[0]) { /* No arguments - go home */ if ((dir = str_val(global("HOME"))) == null) { bi_errorf("no home directory (HOME not set)"); return 1; } } else if (!wp[1]) { /* One argument: - or dir */ dir = wp[0]; if (strcmp(dir, "-") == 0) { dir = str_val(oldpwd_s); if (dir == null) { bi_errorf("no OLDPWD"); return 1; } printpath++; } } else if (!wp[2]) { /* Two arguments - substitute arg1 in PWD for arg2 */ int ilen, olen, nlen, elen; char *cp; if (!current_wd[0]) { bi_errorf("don't know current directory"); return 1; } /* substitue arg1 for arg2 in current path. * if the first substitution fails because the cd fails * we could try to find another substitution. For now * we don't */ if ((cp = strstr(current_wd, wp[0])) == (char *) 0) { bi_errorf("bad substitution"); return 1; } ilen = cp - current_wd; olen = strlen(wp[0]); nlen = strlen(wp[1]); elen = strlen(current_wd + ilen + olen) + 1; dir = alloc(ilen + nlen + elen, ATEMP); memcpy(dir, current_wd, ilen); memcpy(dir + ilen, wp[1], nlen); memcpy(dir + ilen + nlen, current_wd + ilen + olen, elen); printpath++; } else { bi_errorf("too many arguments"); return 1; } Xinit(xs, xp, PATH, ATEMP); /* xp will have a bogus value after make_path() - set it to 0 * so that if it's used, it will cause a dump */ xp = (char *) 0; cdpath = str_val(global("CDPATH")); do { cdnode = make_path(current_wd, dir, &cdpath, &xs, &phys_path); #ifdef S_ISLNK if (physical) rval = chdir(try = Xstring(xs, xp) + phys_path); else #endif /* S_ISLNK */ { simplify_path(Xstring(xs, xp)); rval = chdir(try = Xstring(xs, xp)); } } while (rval < 0 && cdpath != (char *) 0); if (rval < 0) { if (cdnode) bi_errorf("%s: bad directory", dir); else bi_errorf("%s - %s", try, strerror(errno)); return 1; } /* Clear out tracked aliases with relative paths */ flushcom(0); /* Set OLDPWD (note: unsetting OLDPWD does not disable this * setting in at&t ksh) */ if (current_wd[0]) /* Ignore failure (happens if readonly or integer) */ setstr(oldpwd_s, current_wd, KSH_RETURN_ERROR); if (!ISABSPATH(Xstring(xs, xp))) { #ifdef OS2 /* simplify_path() doesn't know about os/2's drive contexts, * so it can't set current_wd when changing to a:foo. * Handle this by calling getcwd()... */ pwd = ksh_get_wd((char *) 0, 0); #else /* OS2 */ pwd = (char *) 0; #endif /* OS2 */ } else #ifdef S_ISLNK if (!physical || !(pwd = get_phys_path(Xstring(xs, xp)))) #endif /* S_ISLNK */ pwd = Xstring(xs, xp); /* Set PWD */ if (pwd) { #ifdef __CYGWIN__ char ptmp[PATH]; /* larger than MAX_PATH */ cygwin_conv_to_full_posix_path(pwd, ptmp); #else /* __CYGWIN__ */ char *ptmp = pwd; #endif /* __CYGWIN__ */ set_current_wd(ptmp); /* Ignore failure (happens if readonly or integer) */ setstr(pwd_s, ptmp, KSH_RETURN_ERROR); } else { set_current_wd(null); pwd = Xstring(xs, xp); /* XXX unset $PWD? */ } if (printpath || cdnode) shprintf("%s\n", pwd); return 0; } int c_pwd(wp) char **wp; { int optc; int physical = Flag(FPHYSICAL); char *p; while ((optc = ksh_getopt(wp, &builtin_opt, "LP")) != EOF) switch (optc) { case 'L': physical = 0; break; case 'P': physical = 1; break; case '?': return 1; } wp += builtin_opt.optind; if (wp[0]) { bi_errorf("too many arguments"); return 1; } #ifdef S_ISLNK p = current_wd[0] ? (physical ? get_phys_path(current_wd) : current_wd) : (char *) 0; #else /* S_ISLNK */ p = current_wd[0] ? current_wd : (char *) 0; #endif /* S_ISLNK */ if (p && eaccess(p, R_OK) < 0) p = (char *) 0; if (!p) { p = ksh_get_wd((char *) 0, 0); if (!p) { bi_errorf("can't get current directory - %s", strerror(errno)); return 1; } } shprintf("%s\n", p); return 0; } int c_print(wp) char **wp; { #define PO_NL BIT(0) /* print newline */ #define PO_EXPAND BIT(1) /* expand backslash sequences */ #define PO_PMINUSMINUS BIT(2) /* print a -- argument */ #define PO_HIST BIT(3) /* print to history instead of stdout */ #define PO_COPROC BIT(4) /* printing to coprocess: block SIGPIPE */ #define PO_FSLASH BIT(5) /* swap slash for backslash (for os2 ) */ int fd = 1; int flags = PO_EXPAND|PO_NL; char *s; const char *emsg; XString xs; char *xp; if (wp[0][0] == 'e') { /* echo command */ int nflags = flags; /* A compromise between sysV and BSD echo commands: * escape sequences are enabled by default, and * -n, -e and -E are recognized if they appear * in arguments with no illegal options (ie, echo -nq * will print -nq). * Different from sysV echo since options are recognized, * different from BSD echo since escape sequences are enabled * by default. */ wp += 1; while ((s = *wp) && *s == '-' && s[1]) { while (*++s) if (*s == 'n') nflags &= ~PO_NL; else if (*s == 'e') nflags |= PO_EXPAND; else if (*s == 'E') nflags &= ~PO_EXPAND; else /* bad option: don't use nflags, print * argument */ break; if (*s) break; wp++; flags = nflags; } } else { int optc; #if OS2 const char *options = "Rnpfrsu,"; /* added f flag */ #else const char *options = "Rnprsu,"; #endif while ((optc = ksh_getopt(wp, &builtin_opt, options)) != EOF) switch (optc) { case 'R': /* fake BSD echo command */ flags |= PO_PMINUSMINUS; flags &= ~PO_EXPAND; options = "ne"; break; case 'e': flags |= PO_EXPAND; break; #ifdef OS2 case 'f': flags |= PO_FSLASH; break; #endif case 'n': flags &= ~PO_NL; break; #ifdef KSH case 'p': if ((fd = coproc_getfd(W_OK, &emsg)) < 0) { bi_errorf("-p: %s", emsg); return 1; } break; #endif /* KSH */ case 'r': flags &= ~PO_EXPAND; break; case 's': flags |= PO_HIST; break; case 'u': if (!*(s = builtin_opt.optarg)) fd = 0; else if ((fd = check_fd(s, W_OK, &emsg)) < 0) { bi_errorf("-u: %s: %s", s, emsg); return 1; } break; case '?': return 1; } if (!(builtin_opt.info & GI_MINUSMINUS)) { /* treat a lone - like -- */ if (wp[builtin_opt.optind] && strcmp(wp[builtin_opt.optind], "-") == 0) builtin_opt.optind++; } else if (flags & PO_PMINUSMINUS) builtin_opt.optind--; wp += builtin_opt.optind; } Xinit(xs, xp, 128, ATEMP); while (*wp != NULL) { register int c; s = *wp; while ((c = *s++) != '\0') { Xcheck(xs, xp); #ifdef OS2 if ((flags & PO_FSLASH) && c == '\\') if (*s == '\\') *s++; else c = '/'; #endif /* OS2 */ if ((flags & PO_EXPAND) && c == '\\') { int i; switch ((c = *s++)) { /* Oddly enough, \007 seems more portable than * \a (due to HP-UX cc, Ultrix cc, old pcc's, * etc.). */ case 'a': c = '\007'; break; case 'b': c = '\b'; break; case 'c': flags &= ~PO_NL; continue; /* AT&T brain damage */ case 'f': c = '\f'; break; case 'n': c = '\n'; break; case 'r': c = '\r'; break; case 't': c = '\t'; break; case 'v': c = 0x0B; break; case '0': /* Look for an octal number: can have * three digits (not counting the * leading 0). Truely burnt. */ c = 0; for (i = 0; i < 3; i++) { if (*s >= '0' && *s <= '7') c = c*8 + *s++ - '0'; else break; } break; case '\0': s--; c = '\\'; break; case '\\': break; default: Xput(xs, xp, '\\'); } } Xput(xs, xp, c); } if (*++wp != NULL) Xput(xs, xp, ' '); } if (flags & PO_NL) Xput(xs, xp, '\n'); if (flags & PO_HIST) { Xput(xs, xp, '\0'); source->line++; histsave(source->line, Xstring(xs, xp), 1); Xfree(xs, xp); } else { int n, len = Xlength(xs, xp); #ifdef KSH int UNINITIALIZED(opipe); /* Ensure we aren't killed by a SIGPIPE while writing to * a coprocess. at&t ksh doesn't seem to do this (seems * to just check that the co-process is alive, which is * not enough). */ if (coproc.write >= 0 && coproc.write == fd) { flags |= PO_COPROC; opipe = block_pipe(); } #endif /* KSH */ for (s = Xstring(xs, xp); len > 0; ) { n = write(fd, s, len); if (n < 0) { #ifdef KSH if (flags & PO_COPROC) restore_pipe(opipe); #endif /* KSH */ if (errno == EINTR) { /* allow user to ^C out */ intrcheck(); #ifdef KSH if (flags & PO_COPROC) opipe = block_pipe(); #endif /* KSH */ continue; } #ifdef KSH /* This doesn't really make sense - could * break scripts (print -p generates * error message). *if (errno == EPIPE) * coproc_write_close(fd); */ #endif /* KSH */ return 1; } s += n; len -= n; } #ifdef KSH if (flags & PO_COPROC) restore_pipe(opipe); #endif /* KSH */ } return 0; } int c_whence(wp) char **wp; { struct tbl *tp; char *id; int pflag = 0, vflag = 0, Vflag = 0; int ret = 0; int optc; int iam_whence = wp[0][0] == 'w'; int fcflags; const char *options = iam_whence ? "pv" : "pvV"; while ((optc = ksh_getopt(wp, &builtin_opt, options)) != EOF) switch (optc) { case 'p': pflag = 1; break; case 'v': vflag = 1; break; case 'V': Vflag = 1; break; case '?': return 1; } wp += builtin_opt.optind; fcflags = FC_BI | FC_PATH | FC_FUNC; if (!iam_whence) { /* Note that -p on its own is deal with in comexec() */ if (pflag) fcflags |= FC_DEFPATH; /* Convert command options to whence options - note that * command -pV uses a different path search than whence -v * or whence -pv. This should be considered a feature. */ vflag = Vflag; } if (pflag) fcflags &= ~(FC_BI | FC_FUNC); while ((vflag || ret == 0) && (id = *wp++) != NULL) { tp = NULL; if ((iam_whence || vflag) && !pflag) tp = tsearch(&keywords, id, hash(id)); if (!tp && !pflag) { tp = tsearch(&aliases, id, hash(id)); if (tp && !(tp->flag & ISSET)) tp = NULL; } if (!tp) tp = findcom(id, fcflags); if (vflag || (tp->type != CALIAS && tp->type != CEXEC && tp->type != CTALIAS)) shprintf("%s", id); switch (tp->type) { case CKEYWD: if (vflag) shprintf(" is a reserved word"); break; case CALIAS: if (vflag) shprintf(" is an %salias for ", (tp->flag & EXPORT) ? "exported " : null); if (!iam_whence && !vflag) shprintf("alias %s=", id); print_value_quoted(tp->val.s); break; case CFUNC: if (vflag) { shprintf(" is a"); if (tp->flag & EXPORT) shprintf("n exported"); if (tp->flag & TRACE) shprintf(" traced"); if (!(tp->flag & ISSET)) { shprintf(" undefined"); if (tp->u.fpath) shprintf(" (autoload from %s)", tp->u.fpath); } shprintf(" function"); } break; case CSHELL: if (vflag) shprintf(" is a%s shell builtin", (tp->flag & SPEC_BI) ? " special" : null); break; case CTALIAS: case CEXEC: if (tp->flag & ISSET) { if (vflag) { shprintf(" is "); if (tp->type == CTALIAS) shprintf( "a tracked %salias for ", (tp->flag & EXPORT) ? "exported " : null); } shprintf("%s", tp->val.s); } else { if (vflag) shprintf(" not found"); ret = 1; } break; default: shprintf("%s is *GOK*", id); break; } if (vflag || !ret) shprintf(newline); } return ret; } /* Deal with command -vV - command -p dealt with in comexec() */ int c_command(wp) char **wp; { /* Let c_whence do the work. Note that c_command() must be * a distinct function from c_whence() (tested in comexec()). */ return c_whence(wp); } /* typeset, export, and readonly */ int c_typeset(wp) char **wp; { struct block *l = e->loc; struct tbl *vp, **p; Tflag fset = 0, fclr = 0; int thing = 0, func = 0, local = 0; const char *options = "L#R#UZ#fi#lprtux"; /* see comment below */ char *fieldstr, *basestr; int field, base; int optc; Tflag flag; int pflag = 0; switch (**wp) { case 'e': /* export */ fset |= EXPORT; options = "p"; break; case 'r': /* readonly */ fset |= RDONLY; options = "p"; break; case 's': /* set */ /* called with 'typeset -' */ break; case 't': /* typeset */ local = 1; break; } fieldstr = basestr = (char *) 0; builtin_opt.flags |= GF_PLUSOPT; /* at&t ksh seems to have 0-9 as options, which are multiplied * to get a number that is used with -L, -R, -Z or -i (eg, -1R2 * sets right justify in a field of 12). This allows options * to be grouped in an order (eg, -Lu12), but disallows -i8 -L3 and * does not allow the number to be specified as a separate argument * Here, the number must follow the RLZi option, but is optional * (see the # kludge in ksh_getopt()). */ while ((optc = ksh_getopt(wp, &builtin_opt, options)) != EOF) { flag = 0; switch (optc) { case 'L': flag = LJUST; fieldstr = builtin_opt.optarg; break; case 'R': flag = RJUST; fieldstr = builtin_opt.optarg; break; case 'U': /* at&t ksh uses u, but this conflicts with * upper/lower case. If this option is changed, * need to change the -U below as well */ flag = INT_U; break; case 'Z': flag = ZEROFIL; fieldstr = builtin_opt.optarg; break; case 'f': func = 1; break; case 'i': flag = INTEGER; basestr = builtin_opt.optarg; break; case 'l': flag = LCASEV; break; case 'p': /* posix export/readonly -p flag. * typset -p is the same as typeset (in pdksh); * here for compatability with ksh93. */ pflag = 1; break; case 'r': flag = RDONLY; break; case 't': flag = TRACE; break; case 'u': flag = UCASEV_AL; /* upper case / autoload */ break; case 'x': flag = EXPORT; break; case '?': return 1; } if (builtin_opt.info & GI_PLUS) { fclr |= flag; fset &= ~flag; thing = '+'; } else { fset |= flag; fclr &= ~flag; thing = '-'; } } field = 0; if (fieldstr && !bi_getn(fieldstr, &field)) return 1; base = 0; if (basestr && !bi_getn(basestr, &base)) return 1; if (!(builtin_opt.info & GI_MINUSMINUS) && wp[builtin_opt.optind] && (wp[builtin_opt.optind][0] == '-' || wp[builtin_opt.optind][0] == '+') && wp[builtin_opt.optind][1] == '\0') { thing = wp[builtin_opt.optind][0]; builtin_opt.optind++; } if (func && ((fset|fclr) & ~(TRACE|UCASEV_AL|EXPORT))) { bi_errorf("only -t, -u and -x options may be used with -f"); return 1; } if (wp[builtin_opt.optind]) { /* Take care of exclusions. * At this point, flags in fset are cleared in fclr and vise * versa. This property should be preserved. */ if (fset & LCASEV) /* LCASEV has priority over UCASEV_AL */ fset &= ~UCASEV_AL; if (fset & LJUST) /* LJUST has priority over RJUST */ fset &= ~RJUST; if ((fset & (ZEROFIL|LJUST)) == ZEROFIL) { /* -Z implies -ZR */ fset |= RJUST; fclr &= ~RJUST; } /* Setting these attributes clears the others, unless they * are also set in this command */ if (fset & (LJUST|RJUST|ZEROFIL|UCASEV_AL|LCASEV|INTEGER |INT_U|INT_L)) fclr |= ~fset & (LJUST|RJUST|ZEROFIL|UCASEV_AL|LCASEV|INTEGER |INT_U|INT_L); } /* set variables and attributes */ if (wp[builtin_opt.optind]) { int i; int rval = 0; struct tbl *f; if (local && !func) fset |= LOCAL; for (i = builtin_opt.optind; wp[i]; i++) { if (func) { f = findfunc(wp[i], hash(wp[i]), (fset&UCASEV_AL) ? TRUE : FALSE); if (!f) { /* at&t ksh does ++rval: bogus */ rval = 1; continue; } if (fset | fclr) { f->flag |= fset; f->flag &= ~fclr; } else fptreef(shl_stdout, 0, f->flag & FKSH ? "function %s %T\n" : "%s() %T\n" , wp[i], f->val.t); } else if (!typeset(wp[i], fset, fclr, field, base)) { bi_errorf("%s: not identifier", wp[i]); return 1; } } return rval; } /* list variables and attributes */ flag = fset | fclr; /* no difference at this point.. */ if (func) { for (l = e->loc; l; l = l->next) { for (p = tsort(&l->funs); (vp = *p++); ) { if (flag && (vp->flag & flag) == 0) continue; if (thing == '-') fptreef(shl_stdout, 0, vp->flag & FKSH ? "function %s %T\n" : "%s() %T\n", vp->name, vp->val.t); else shprintf("%s\n", vp->name); } } } else { for (l = e->loc; l; l = l->next) { for (p = tsort(&l->vars); (vp = *p++); ) { struct tbl *tvp; int any_set = 0; /* * See if the parameter is set (for arrays, if any * element is set). */ for (tvp = vp; tvp; tvp = tvp->u.array) if (tvp->flag & ISSET) { any_set = 1; break; } /* * Check attributes - note that all array elements * have (should have?) the same attributes, so checking * the first is sufficient. * * Report an unset param only if the user has * explicitly given it some attribute (like export); * otherwise, after "echo $FOO", we would report FOO... */ if (!any_set && !(vp->flag & USERATTRIB)) continue; if (flag && (vp->flag & flag) == 0) continue; for (; vp; vp = vp->u.array) { /* Ignore array elements that aren't set unless there * are no set elements, in which case the first is * reported on */ if ((vp->flag&ARRAY) && any_set && !(vp->flag & ISSET)) continue; /* no arguments */ if (thing == 0 && flag == 0) { /* at&t ksh prints things like export, integer, * leftadj, zerofill, etc., but POSIX says must * be suitable for re-entry... */ shprintf("typeset "); if ((vp->flag&INTEGER)) shprintf("-i "); if ((vp->flag&EXPORT)) shprintf("-x "); if ((vp->flag&RDONLY)) shprintf("-r "); if ((vp->flag&TRACE)) shprintf("-t "); if ((vp->flag&LJUST)) shprintf("-L%d ", vp->u2.field); if ((vp->flag&RJUST)) shprintf("-R%d ", vp->u2.field); if ((vp->flag&ZEROFIL)) shprintf("-Z "); if ((vp->flag&LCASEV)) shprintf("-l "); if ((vp->flag&UCASEV_AL)) shprintf("-u "); if ((vp->flag&INT_U)) shprintf("-U "); shprintf("%s\n", vp->name); if (vp->flag&ARRAY) break; } else { if (pflag) shprintf("%s ", (flag & EXPORT) ? "export" : "readonly"); if ((vp->flag&ARRAY) && any_set) shprintf("%s[%d]", vp->name, vp->index); else shprintf("%s", vp->name); if (thing == '-' && (vp->flag&ISSET)) { char *s = str_val(vp); shprintf("="); /* at&t ksh can't have justified integers.. */ if ((vp->flag & (INTEGER|LJUST|RJUST)) == INTEGER) shprintf("%s", s); else print_value_quoted(s); } shprintf(newline); } /* Only report first `element' of an array with * no set elements. */ if (!any_set) break; } } } } return 0; } int c_alias(wp) char **wp; { struct table *t = &aliases; int rv = 0, rflag = 0, tflag, Uflag = 0, pflag = 0; int prefix = 0; Tflag xflag = 0; int optc; builtin_opt.flags |= GF_PLUSOPT; while ((optc = ksh_getopt(wp, &builtin_opt, "dprtUx")) != EOF) { prefix = builtin_opt.info & GI_PLUS ? '+' : '-'; switch (optc) { case 'd': t = &homedirs; break; case 'p': pflag = 1; break; case 'r': rflag = 1; break; case 't': t = &taliases; break; case 'U': /* kludge for tracked alias initialization * (don't do a path search, just make an entry) */ Uflag = 1; break; case 'x': xflag = EXPORT; break; case '?': return 1; } } wp += builtin_opt.optind; if (!(builtin_opt.info & GI_MINUSMINUS) && *wp && (wp[0][0] == '-' || wp[0][0] == '+') && wp[0][1] == '\0') { prefix = wp[0][0]; wp++; } tflag = t == &taliases; /* "hash -r" means reset all the tracked aliases.. */ if (rflag) { static const char *const args[] = { "unalias", "-ta", (const char *) 0 }; if (!tflag || *wp) { shprintf( "alias: -r flag can only be used with -t and without arguments\n"); return 1; } ksh_getopt_reset(&builtin_opt, GF_ERROR); return c_unalias((char **) args); } if (*wp == NULL) { struct tbl *ap, **p; for (p = tsort(t); (ap = *p++) != NULL; ) if ((ap->flag & (ISSET|xflag)) == (ISSET|xflag)) { if (pflag) shf_puts("alias ", shl_stdout); shf_puts(ap->name, shl_stdout); if (prefix != '+') { shf_putc('=', shl_stdout); print_value_quoted(ap->val.s); } shprintf(newline); } } for (; *wp != NULL; wp++) { char *alias = *wp; char *val = strchr(alias, '='); char *newval; struct tbl *ap; int h; if (val) alias = str_nsave(alias, val++ - alias, ATEMP); h = hash(alias); if (val == NULL && !tflag && !xflag) { ap = tsearch(t, alias, h); if (ap != NULL && (ap->flag&ISSET)) { if (pflag) shf_puts("alias ", shl_stdout); shf_puts(ap->name, shl_stdout); if (prefix != '+') { shf_putc('=', shl_stdout); print_value_quoted(ap->val.s); } shprintf(newline); } else { shprintf("%s alias not found\n", alias); rv = 1; } continue; } ap = tenter(t, alias, h); ap->type = tflag ? CTALIAS : CALIAS; /* Are we setting the value or just some flags? */ if ((val && !tflag) || (!val && tflag && !Uflag)) { if (ap->flag&ALLOC) { ap->flag &= ~(ALLOC|ISSET); afree((void*)ap->val.s, APERM); } /* ignore values for -t (at&t ksh does this) */ newval = tflag ? search(alias, path, X_OK, (int *) 0) : val; if (newval) { ap->val.s = str_save(newval, APERM); ap->flag |= ALLOC|ISSET; } else ap->flag &= ~ISSET; } ap->flag |= DEFINED; if (prefix == '+') ap->flag &= ~xflag; else ap->flag |= xflag; if (val) afree(alias, ATEMP); } return rv; } int c_unalias(wp) char **wp; { register struct table *t = &aliases; register struct tbl *ap; int rv = 0, all = 0; int optc; while ((optc = ksh_getopt(wp, &builtin_opt, "adt")) != EOF) switch (optc) { case 'a': all = 1; break; case 'd': t = &homedirs; break; case 't': t = &taliases; break; case '?': return 1; } wp += builtin_opt.optind; for (; *wp != NULL; wp++) { ap = tsearch(t, *wp, hash(*wp)); if (ap == NULL) { rv = 1; /* POSIX */ continue; } if (ap->flag&ALLOC) { ap->flag &= ~(ALLOC|ISSET); afree((void*)ap->val.s, APERM); } ap->flag &= ~(DEFINED|ISSET|EXPORT); } if (all) { struct tstate ts; for (twalk(&ts, t); (ap = tnext(&ts)); ) { if (ap->flag&ALLOC) { ap->flag &= ~(ALLOC|ISSET); afree((void*)ap->val.s, APERM); } ap->flag &= ~(DEFINED|ISSET|EXPORT); } } return rv; } #ifdef KSH int c_let(wp) char **wp; { int rv = 1; long val; if (wp[1] == (char *) 0) /* at&t ksh does this */ bi_errorf("no arguments"); else for (wp++; *wp; wp++) if (!evaluate(*wp, &val, KSH_RETURN_ERROR)) { rv = 2; /* distinguish error from zero result */ break; } else rv = val == 0; return rv; } #endif /* KSH */ int c_jobs(wp) char **wp; { int optc; int flag = 0; int nflag = 0; int rv = 0; while ((optc = ksh_getopt(wp, &builtin_opt, "lpnz")) != EOF) switch (optc) { case 'l': flag = 1; break; case 'p': flag = 2; break; case 'n': nflag = 1; break; case 'z': /* debugging: print zombies */ nflag = -1; break; case '?': return 1; } wp += builtin_opt.optind; if (!*wp) if (j_jobs((char *) 0, flag, nflag)) rv = 1; else for (; *wp; wp++) if (j_jobs(*wp, flag, nflag)) rv = 1; return rv; } #ifdef JOBS int c_fgbg(wp) char **wp; { int bg = strcmp(*wp, "bg") == 0; int UNINITIALIZED(rv); if (!Flag(FMONITOR)) { bi_errorf("job control not enabled"); return 1; } if (ksh_getopt(wp, &builtin_opt, null) == '?') return 1; wp += builtin_opt.optind; if (*wp) for (; *wp; wp++) rv = j_resume(*wp, bg); else rv = j_resume("%%", bg); /* POSIX says fg shall return 0 (unless an error occurs). * at&t ksh returns the exit value of the job... */ return (bg || Flag(FPOSIX)) ? 0 : rv; } #endif struct kill_info { int num_width; int name_width; }; static char *kill_fmt_entry ARGS((void *arg, int i, char *buf, int buflen)); /* format a single kill item */ static char * kill_fmt_entry(arg, i, buf, buflen) void *arg; int i; char *buf; int buflen; { struct kill_info *ki = (struct kill_info *) arg; i++; if (sigtraps[i].name) shf_snprintf(buf, buflen, "%*d %*s %s", ki->num_width, i, ki->name_width, sigtraps[i].name, sigtraps[i].mess); else shf_snprintf(buf, buflen, "%*d %*d %s", ki->num_width, i, ki->name_width, sigtraps[i].signal, sigtraps[i].mess); return buf; } int c_kill(wp) char **wp; { Trap *t = (Trap *) 0; char *p; int lflag = 0; int i, n, rv, sig; /* assume old style options if -digits or -UPPERCASE */ if ((p = wp[1]) && *p == '-' && (digit(p[1]) || isupper(p[1]))) { if (!(t = gettrap(p + 1, TRUE))) { bi_errorf("bad signal `%s'", p + 1); return 1; } i = (wp[2] && strcmp(wp[2], "--") == 0) ? 3 : 2; } else { int optc; while ((optc = ksh_getopt(wp, &builtin_opt, "ls:")) != EOF) switch (optc) { case 'l': lflag = 1; break; case 's': if (!(t = gettrap(builtin_opt.optarg, TRUE))) { bi_errorf("bad signal `%s'", builtin_opt.optarg); return 1; } case '?': return 1; } i = builtin_opt.optind; } if ((lflag && t) || (!wp[i] && !lflag)) { shf_fprintf(shl_out, "Usage: kill [ -s signame | -signum | -signame ] {pid|job}...\n\ kill -l [exit_status]\n" ); bi_errorf(null); return 1; } if (lflag) { if (wp[i]) { for (; wp[i]; i++) { if (!bi_getn(wp[i], &n)) return 1; if (n > 128 && n < 128 + SIGNALS) n -= 128; if (n > 0 && n < SIGNALS && sigtraps[n].name) shprintf("%s\n", sigtraps[n].name); else shprintf("%d\n", n); } } else if (Flag(FPOSIX)) { p = null; for (i = 1; i < SIGNALS; i++, p = space) if (sigtraps[i].name) shprintf("%s%s", p, sigtraps[i].name); shprintf(newline); } else { int w, i; int mess_width; struct kill_info ki; for (i = SIGNALS, ki.num_width = 1; i >= 10; i /= 10) ki.num_width++; ki.name_width = mess_width = 0; for (i = 0; i < SIGNALS; i++) { w = sigtraps[i].name ? strlen(sigtraps[i].name) : ki.num_width; if (w > ki.name_width) ki.name_width = w; w = strlen(sigtraps[i].mess); if (w > mess_width) mess_width = w; } print_columns(shl_stdout, SIGNALS - 1, kill_fmt_entry, (void *) &ki, ki.num_width + ki.name_width + mess_width + 3); } return 0; } rv = 0; sig = t ? t->signal : SIGTERM; for (; (p = wp[i]); i++) { if (*p == '%') { if (j_kill(p, sig)) rv = 1; } else if (!getn(p, &n)) { bi_errorf("%s: arguments must be jobs or process ids", p); rv = 1; } else { /* use killpg if < -1 since -1 does special things for * some non-killpg-endowed kills */ if ((n < -1 ? killpg(-n, sig) : kill(n, sig)) < 0) { bi_errorf("%s: %s", p, strerror(errno)); rv = 1; } } } return rv; } void getopts_reset(val) int val; { if (val >= 1) { ksh_getopt_reset(&user_opt, GF_NONAME | (Flag(FPOSIX) ? 0 : GF_PLUSOPT)); user_opt.optind = user_opt.uoptind = val; } } int c_getopts(wp) char **wp; { int argc; const char *options; const char *var; int optc; int ret; char buf[3]; struct tbl *vq, *voptarg; if (ksh_getopt(wp, &builtin_opt, null) == '?') return 1; wp += builtin_opt.optind; options = *wp++; if (!options) { bi_errorf("missing options argument"); return 1; } var = *wp++; if (!var) { bi_errorf("missing name argument"); return 1; } if (!*var || *skip_varname(var, TRUE)) { bi_errorf("%s: is not an identifier", var); return 1; } if (e->loc->next == (struct block *) 0) { internal_errorf(0, "c_getopts: no argv"); return 1; } /* Which arguments are we parsing... */ if (*wp == (char *) 0) wp = e->loc->next->argv; else *--wp = e->loc->next->argv[0]; /* Check that our saved state won't cause a core dump... */ for (argc = 0; wp[argc]; argc++) ; if (user_opt.optind > argc || (user_opt.p != 0 && user_opt.p > strlen(wp[user_opt.optind - 1]))) { bi_errorf("arguments changed since last call"); return 1; } user_opt.optarg = (char *) 0; optc = ksh_getopt(wp, &user_opt, options); if (optc >= 0 && optc != '?' && (user_opt.info & GI_PLUS)) { buf[0] = '+'; buf[1] = optc; buf[2] = '\0'; } else { /* POSIX says var is set to ? at end-of-options, at&t ksh * sets it to null - we go with POSIX... */ buf[0] = optc < 0 ? '?' : optc; buf[1] = '\0'; } /* at&t ksh does not change OPTIND if it was an unknown option. * Scripts counting on this are prone to break... (ie, don't count * on this staying). */ if (optc != '?') { user_opt.uoptind = user_opt.optind; } voptarg = global("OPTARG"); voptarg->flag &= ~RDONLY; /* at&t ksh clears ro and int */ /* Paranoia: ensure no bizarre results. */ if (voptarg->flag & INTEGER) typeset("OPTARG", 0, INTEGER, 0, 0); if (user_opt.optarg == (char *) 0) unset(voptarg, 0); else /* This can't fail (have cleared readonly/integer) */ setstr(voptarg, user_opt.optarg, KSH_RETURN_ERROR); ret = 0; vq = global(var); /* Error message already printed (integer, readonly) */ if (!setstr(vq, buf, KSH_RETURN_ERROR)) ret = 1; if (Flag(FEXPORT)) typeset(var, EXPORT, 0, 0, 0); return optc < 0 ? 1 : ret; } #ifdef EMACS int c_bind(wp) char **wp; { int rv = 0, macro = 0, list = 0; register char *cp; int optc; while ((optc = ksh_getopt(wp, &builtin_opt, "lm")) != EOF) switch (optc) { case 'l': list = 1; break; case 'm': macro = 1; break; case '?': return 1; } wp += builtin_opt.optind; if (*wp == NULL) /* list all */ rv = x_bind((char*)NULL, (char*)NULL, 0, list); for (; *wp != NULL; wp++) { cp = strchr(*wp, '='); if (cp != NULL) *cp++ = '\0'; if (x_bind(*wp, cp, macro, 0)) rv = 1; } return rv; } #endif /* A leading = means assignments before command are kept; * a leading * means a POSIX special builtin; * a leading + means a POSIX regular builtin * (* and + should not be combined). */ const struct builtin kshbuiltins [] = { {"+alias", c_alias}, /* no =: at&t manual wrong */ {"+cd", c_cd}, {"+command", c_command}, {"echo", c_print}, {"*=export", c_typeset}, #ifdef HISTORY {"+fc", c_fc}, #endif /* HISTORY */ {"+getopts", c_getopts}, {"+jobs", c_jobs}, {"+kill", c_kill}, #ifdef KSH {"let", c_let}, #endif /* KSH */ {"print", c_print}, {"pwd", c_pwd}, {"*=readonly", c_typeset}, {"=typeset", c_typeset}, {"+unalias", c_unalias}, {"whence", c_whence}, #ifdef JOBS {"+bg", c_fgbg}, {"+fg", c_fgbg}, #endif #ifdef EMACS {"bind", c_bind}, #endif {NULL, NULL} }; /sys/src/ape/cmd/pdksh/c_sh.c 664 sys sys 1367613436 19716 /* * built-in Bourne commands */ #include "sh.h" #include "ksh_stat.h" /* umask() */ #include "ksh_time.h" #include "ksh_times.h" static char *clocktos ARGS((clock_t t)); /* :, false and true */ int c_label(wp) char **wp; { return wp[0][0] == 'f' ? 1 : 0; } int c_shift(wp) char **wp; { register struct block *l = e->loc; register int n; long val; char *arg; if (ksh_getopt(wp, &builtin_opt, null) == '?') return 1; arg = wp[builtin_opt.optind]; if (arg) { evaluate(arg, &val, KSH_UNWIND_ERROR); n = val; } else n = 1; if (n < 0) { bi_errorf("%s: bad number", arg); return (1); } if (l->argc < n) { bi_errorf("nothing to shift"); return (1); } l->argv[n] = l->argv[0]; l->argv += n; l->argc -= n; return 0; } int c_umask(wp) char **wp; { register int i; register char *cp; int symbolic = 0; int old_umask; int optc; while ((optc = ksh_getopt(wp, &builtin_opt, "S")) != EOF) switch (optc) { case 'S': symbolic = 1; break; case '?': return 1; } cp = wp[builtin_opt.optind]; if (cp == NULL) { old_umask = umask(0); umask(old_umask); if (symbolic) { char buf[18]; int j; old_umask = ~old_umask; cp = buf; for (i = 0; i < 3; i++) { *cp++ = "ugo"[i]; *cp++ = '='; for (j = 0; j < 3; j++) if (old_umask & (1 << (8 - (3*i + j)))) *cp++ = "rwx"[j]; *cp++ = ','; } cp[-1] = '\0'; shprintf("%s\n", buf); } else shprintf("%#3.3o\n", old_umask); } else { int new_umask; if (digit(*cp)) { for (new_umask = 0; *cp >= '0' && *cp <= '7'; cp++) new_umask = new_umask * 8 + (*cp - '0'); if (*cp) { bi_errorf("bad number"); return 1; } } else { /* symbolic format */ int positions, new_val; char op; old_umask = umask(0); umask(old_umask); /* in case of error */ old_umask = ~old_umask; new_umask = old_umask; positions = 0; while (*cp) { while (*cp && strchr("augo", *cp)) switch (*cp++) { case 'a': positions |= 0111; break; case 'u': positions |= 0100; break; case 'g': positions |= 0010; break; case 'o': positions |= 0001; break; } if (!positions) positions = 0111; /* default is a */ if (!strchr("=+-", op = *cp)) break; cp++; new_val = 0; while (*cp && strchr("rwxugoXs", *cp)) switch (*cp++) { case 'r': new_val |= 04; break; case 'w': new_val |= 02; break; case 'x': new_val |= 01; break; case 'u': new_val |= old_umask >> 6; break; case 'g': new_val |= old_umask >> 3; break; case 'o': new_val |= old_umask >> 0; break; case 'X': if (old_umask & 0111) new_val |= 01; break; case 's': /* ignored */ break; } new_val = (new_val & 07) * positions; switch (op) { case '-': new_umask &= ~new_val; break; case '=': new_umask = new_val | (new_umask & ~(positions * 07)); break; case '+': new_umask |= new_val; } if (*cp == ',') { positions = 0; cp++; } else if (!strchr("=+-", *cp)) break; } if (*cp) { bi_errorf("bad mask"); return 1; } new_umask = ~new_umask; } umask(new_umask); } return 0; } int c_dot(wp) char **wp; { char *file, *cp; char **argv; int argc; int i; int err; if (ksh_getopt(wp, &builtin_opt, null) == '?') return 1; if ((cp = wp[builtin_opt.optind]) == NULL) return 0; file = search(cp, path, R_OK, &err); if (file == NULL) { bi_errorf("%s: %s", cp, err ? strerror(err) : "not found"); return 1; } /* Set positional parameters? */ if (wp[builtin_opt.optind + 1]) { argv = wp + builtin_opt.optind; argv[0] = e->loc->argv[0]; /* preserve $0 */ for (argc = 0; argv[argc + 1]; argc++) ; } else { argc = 0; argv = (char **) 0; } i = include(file, argc, argv, 0); if (i < 0) { /* should not happen */ bi_errorf("%s: %s", cp, strerror(errno)); return 1; } return i; } int c_wait(wp) char **wp; { int UNINITIALIZED(rv); int sig; if (ksh_getopt(wp, &builtin_opt, null) == '?') return 1; wp += builtin_opt.optind; if (*wp == (char *) 0) { while (waitfor((char *) 0, &sig) >= 0) ; rv = sig; } else { for (; *wp; wp++) rv = waitfor(*wp, &sig); if (rv < 0) rv = sig ? sig : 127; /* magic exit code: bad job-id */ } return rv; } int c_read(wp) char **wp; { register int c = 0; int expand = 1, history = 0; int expanding; int ecode = 0; register char *cp; int fd = 0; struct shf *shf; int optc; const char *emsg; XString cs, xs; struct tbl *vp; char UNINITIALIZED(*xp); while ((optc = ksh_getopt(wp, &builtin_opt, "prsu,")) != EOF) switch (optc) { #ifdef KSH case 'p': if ((fd = coproc_getfd(R_OK, &emsg)) < 0) { bi_errorf("-p: %s", emsg); return 1; } break; #endif /* KSH */ case 'r': expand = 0; break; case 's': history = 1; break; case 'u': if (!*(cp = builtin_opt.optarg)) fd = 0; else if ((fd = check_fd(cp, R_OK, &emsg)) < 0) { bi_errorf("-u: %s: %s", cp, emsg); return 1; } break; case '?': return 1; } wp += builtin_opt.optind; if (*wp == NULL) *--wp = "REPLY"; /* Since we can't necessarily seek backwards on non-regular files, * don't buffer them so we can't read too much. */ shf = shf_reopen(fd, SHF_RD | SHF_INTERRUPT | can_seek(fd), shl_spare); if ((cp = strchr(*wp, '?')) != NULL) { *cp = 0; if (isatty(fd)) { /* at&t ksh says it prints prompt on fd if it's open * for writing and is a tty, but it doesn't do it * (it also doesn't check the interactive flag, * as is indicated in the Kornshell book). */ shellf("%s", cp+1); } } #ifdef KSH /* If we are reading from the co-process for the first time, * make sure the other side of the pipe is closed first. This allows * the detection of eof. * * This is not compatiable with at&t ksh... the fd is kept so another * coproc can be started with same ouput, however, this means eof * can't be detected... This is why it is closed here. * If this call is removed, remove the eof check below, too. * coproc_readw_close(fd); */ #endif /* KSH */ if (history) Xinit(xs, xp, 128, ATEMP); expanding = 0; Xinit(cs, cp, 128, ATEMP); for (; *wp != NULL; wp++) { for (cp = Xstring(cs, cp); ; ) { if (c == '\n' || c == EOF) break; while (1) { c = shf_getc(shf); if (c == '\0' #ifdef OS2 || c == '\r' #endif /* OS2 */ ) continue; if (c == EOF && shf_error(shf) && shf_errno(shf) == EINTR) { /* Was the offending signal one that * would normally kill a process? * If so, pretend the read was killed. */ ecode = fatal_trap_check(); /* non fatal (eg, CHLD), carry on */ if (!ecode) { shf_clearerr(shf); continue; } } break; } if (history) { Xcheck(xs, xp); Xput(xs, xp, c); } Xcheck(cs, cp); if (expanding) { expanding = 0; if (c == '\n') { c = 0; if (Flag(FTALKING_I) && isatty(fd)) { /* set prompt in case this is * called from .profile or $ENV */ set_prompt(PS2, (Source *) 0); pprompt(prompt, 0); } } else if (c != EOF) Xput(cs, cp, c); continue; } if (expand && c == '\\') { expanding = 1; continue; } if (c == '\n' || c == EOF) break; if (ctype(c, C_IFS)) { if (Xlength(cs, cp) == 0 && ctype(c, C_IFSWS)) continue; if (wp[1]) break; } Xput(cs, cp, c); } /* strip trailing IFS white space from last variable */ if (!wp[1]) while (Xlength(cs, cp) && ctype(cp[-1], C_IFS) && ctype(cp[-1], C_IFSWS)) cp--; Xput(cs, cp, '\0'); vp = global(*wp); /* Must be done before setting export. */ if (vp->flag & RDONLY) { shf_flush(shf); bi_errorf("%s is read only", *wp); return 1; } if (Flag(FEXPORT)) typeset(*wp, EXPORT, 0, 0, 0); if (!setstr(vp, Xstring(cs, cp), KSH_RETURN_ERROR)) { shf_flush(shf); return 1; } } shf_flush(shf); if (history) { Xput(xs, xp, '\0'); source->line++; histsave(source->line, Xstring(xs, xp), 1); Xfree(xs, xp); } #ifdef KSH /* if this is the co-process fd, close the file descriptor * (can get eof if and only if all processes are have died, ie, * coproc.njobs is 0 and the pipe is closed). */ if (c == EOF && !ecode) coproc_read_close(fd); #endif /* KSH */ return ecode ? ecode : c == EOF; } int c_eval(wp) char **wp; { register struct source *s; if (ksh_getopt(wp, &builtin_opt, null) == '?') return 1; s = pushs(SWORDS, ATEMP); s->u.strv = wp + builtin_opt.optind; if (!Flag(FPOSIX)) { /* * Handle case where the command is empty due to failed * command substitution, eg, eval "$(false)". * In this case, shell() will not set/change exstat (because * compiled tree is empty), so will use this value. * subst_exstat is cleared in execute(), so should be 0 if * there were no substitutions. * * A strict reading of POSIX says we don't do this (though * it is traditionally done). [from 1003.2-1992] * 3.9.1: Simple Commands * ... If there is a command name, execution shall * continue as described in 3.9.1.1. If there * is no command name, but the command contained a command * substitution, the command shall complete with the exit * status of the last command substitution * 3.9.1.1: Command Search and Execution * ...(1)...(a) If the command name matches the name of * a special built-in utility, that special built-in * utility shall be invoked. * 3.14.5: Eval * ... If there are no arguments, or only null arguments, * eval shall return an exit status of zero. */ exstat = subst_exstat; } return shell(s, FALSE); } int c_trap(wp) char **wp; { int i; char *s; register Trap *p; if (ksh_getopt(wp, &builtin_opt, null) == '?') return 1; wp += builtin_opt.optind; if (*wp == NULL) { int anydfl = 0; for (p = sigtraps, i = SIGNALS+1; --i >= 0; p++) { if (p->trap == NULL) anydfl = 1; else { shprintf("trap -- "); print_value_quoted(p->trap); shprintf(" %s\n", p->name); } } #if 0 /* this is ugly and not clear POSIX needs it */ /* POSIX may need this so output of trap can be saved and * used to restore trap conditions */ if (anydfl) { shprintf("trap -- -"); for (p = sigtraps, i = SIGNALS+1; --i >= 0; p++) if (p->trap == NULL && p->name) shprintf(" %s", p->name); shprintf(newline); } #endif return 0; } /* * Use case sensitive lookup for first arg so the * command 'exit' isn't confused with the pseudo-signal * 'EXIT'. */ s = (gettrap(*wp, FALSE) == NULL) ? *wp++ : NULL; /* get command */ if (s != NULL && s[0] == '-' && s[1] == '\0') s = NULL; /* set/clear traps */ while (*wp != NULL) { p = gettrap(*wp++, TRUE); if (p == NULL) { bi_errorf("bad signal %s", wp[-1]); return 1; } settrap(p, s); } return 0; } int c_exitreturn(wp) char **wp; { int how = LEXIT; int n; char *arg; if (ksh_getopt(wp, &builtin_opt, null) == '?') return 1; arg = wp[builtin_opt.optind]; if (arg) { if (!getn(arg, &n)) { exstat = 1; warningf(TRUE, "%s: bad number", arg); } else exstat = n; } if (wp[0][0] == 'r') { /* return */ struct env *ep; /* need to tell if this is exit or return so trap exit will * work right (POSIX) */ for (ep = e; ep; ep = ep->oenv) if (STOP_RETURN(ep->type)) { how = LRETURN; break; } } if (how == LEXIT && !really_exit && j_stopped_running()) { really_exit = 1; how = LSHELL; } quitenv(); /* get rid of any i/o redirections */ unwind(how); /*NOTREACHED*/ return 0; } int c_brkcont(wp) char **wp; { int n, quit; struct env *ep, *last_ep = (struct env *) 0; char *arg; if (ksh_getopt(wp, &builtin_opt, null) == '?') return 1; arg = wp[builtin_opt.optind]; if (!arg) n = 1; else if (!bi_getn(arg, &n)) return 1; quit = n; if (quit <= 0) { /* at&t ksh does this for non-interactive shells only - weird */ bi_errorf("%s: bad value", arg); return 1; } /* Stop at E_NONE, E_PARSE, E_FUNC, or E_INCL */ for (ep = e; ep && !STOP_BRKCONT(ep->type); ep = ep->oenv) if (ep->type == E_LOOP) { if (--quit == 0) break; ep->flags |= EF_BRKCONT_PASS; last_ep = ep; } if (quit) { /* at&t ksh doesn't print a message - just does what it * can. We print a message 'cause it helps in debugging * scripts, but don't generate an error (ie, keep going). */ if (n == quit) { warningf(TRUE, "%s: cannot %s", wp[0], wp[0]); return 0; } /* POSIX says if n is too big, the last enclosing loop * shall be used. Doesn't say to print an error but we * do anyway 'cause the user messed up. */ last_ep->flags &= ~EF_BRKCONT_PASS; warningf(TRUE, "%s: can only %s %d level(s)", wp[0], wp[0], n - quit); } unwind(*wp[0] == 'b' ? LBREAK : LCONTIN); /*NOTREACHED*/ return 0; } int c_set(wp) char **wp; { int argi, setargs; struct block *l = e->loc; register char **owp = wp; if (wp[1] == NULL) { static const char *const args [] = { "set", "-", NULL }; return c_typeset((char **) args); } argi = parse_args(wp, OF_SET, &setargs); if (argi < 0) return 1; /* set $# and $* */ if (setargs) { owp = wp += argi - 1; wp[0] = l->argv[0]; /* save $0 */ while (*++wp != NULL) *wp = str_save(*wp, &l->area); l->argc = wp - owp - 1; l->argv = (char **) alloc(sizeofN(char *, l->argc+2), &l->area); for (wp = l->argv; (*wp++ = *owp++) != NULL; ) ; } /* POSIX says set exit status is 0, but old scripts that use * getopt(1), use the construct: set -- `getopt ab:c "$@"` * which assumes the exit value set will be that of the `` * (subst_exstat is cleared in execute() so that it will be 0 * if there are no command substitutions). */ return Flag(FPOSIX) ? 0 : subst_exstat; } int c_unset(wp) char **wp; { register char *id; int optc, unset_var = 1; int ret = 0; while ((optc = ksh_getopt(wp, &builtin_opt, "fv")) != EOF) switch (optc) { case 'f': unset_var = 0; break; case 'v': unset_var = 1; break; case '?': return 1; } wp += builtin_opt.optind; for (; (id = *wp) != NULL; wp++) if (unset_var) { /* unset variable */ struct tbl *vp = global(id); if (!(vp->flag & ISSET)) ret = 1; if ((vp->flag&RDONLY)) { bi_errorf("%s is read only", vp->name); return 1; } unset(vp, strchr(id, '[') ? 1 : 0); } else { /* unset function */ if (define(id, (struct op *) NULL)) ret = 1; } return ret; } int c_times(wp) char **wp; { struct tms all; (void) ksh_times(&all); shprintf("Shell: %8ss user ", clocktos(all.tms_utime)); shprintf("%8ss system\n", clocktos(all.tms_stime)); shprintf("Kids: %8ss user ", clocktos(all.tms_cutime)); shprintf("%8ss system\n", clocktos(all.tms_cstime)); return 0; } /* * time pipeline (really a statement, not a built-in command) */ int timex(t, f) struct op *t; int f; { #define TF_NOARGS BIT(0) #define TF_NOREAL BIT(1) /* don't report real time */ #define TF_POSIX BIT(2) /* report in posix format */ int rv = 0; struct tms t0, t1, tms; clock_t t0t, t1t = 0; int tf = 0; extern clock_t j_usrtime, j_systime; /* computed by j_wait */ char opts[1]; t0t = ksh_times(&t0); if (t->left) { /* * Two ways of getting cpu usage of a command: just use t0 * and t1 (which will get cpu usage from other jobs that * finish while we are executing t->left), or get the * cpu usage of t->left. at&t ksh does the former, while * pdksh tries to do the later (the j_usrtime hack doesn't * really work as it only counts the last job). */ j_usrtime = j_systime = 0; if (t->left->type == TCOM) t->left->str = opts; opts[0] = 0; rv = execute(t->left, f | XTIME); tf |= opts[0]; t1t = ksh_times(&t1); } else tf = TF_NOARGS; if (tf & TF_NOARGS) { /* ksh93 - report shell times (shell+kids) */ tf |= TF_NOREAL; tms.tms_utime = t0.tms_utime + t0.tms_cutime; tms.tms_stime = t0.tms_stime + t0.tms_cstime; } else { tms.tms_utime = t1.tms_utime - t0.tms_utime + j_usrtime; tms.tms_stime = t1.tms_stime - t0.tms_stime + j_systime; } if (!(tf & TF_NOREAL)) shf_fprintf(shl_out, tf & TF_POSIX ? "real %8s\n" : "%8ss real ", clocktos(t1t - t0t)); shf_fprintf(shl_out, tf & TF_POSIX ? "user %8s\n" : "%8ss user ", clocktos(tms.tms_utime)); shf_fprintf(shl_out, tf & TF_POSIX ? "sys %8s\n" : "%8ss system\n", clocktos(tms.tms_stime)); shf_flush(shl_out); return rv; } void timex_hook(t, app) struct op *t; char ** volatile *app; { char **wp = *app; int optc; int i, j; Getopt opt; ksh_getopt_reset(&opt, 0); opt.optind = 0; /* start at the start */ while ((optc = ksh_getopt(wp, &opt, ":p")) != EOF) switch (optc) { case 'p': t->str[0] |= TF_POSIX; break; case '?': errorf("time: -%s unknown option", opt.optarg); case ':': errorf("time: -%s requires an argument", opt.optarg); } /* Copy command words down over options. */ if (opt.optind != 0) { for (i = 0; i < opt.optind; i++) afree(wp[i], ATEMP); for (i = 0, j = opt.optind; (wp[i] = wp[j]); i++, j++) ; } if (!wp[0]) t->str[0] |= TF_NOARGS; *app = wp; } static char * clocktos(t) clock_t t; { static char temp[22]; /* enough for 64 bit clock_t */ register int i; register char *cp = temp + sizeof(temp); /* note: posix says must use max precision, ie, if clk_tck is * 1000, must print 3 places after decimal (if non-zero, else 1). */ if (CLK_TCK != 100) /* convert to 1/100'ths */ t = (t < 1000000000/CLK_TCK) ? (t * 100) / CLK_TCK : (t / CLK_TCK) * 100; *--cp = '\0'; for (i = -2; i <= 0 || t > 0; i++) { if (i == 0) *--cp = '.'; *--cp = '0' + (char)(t%10); t /= 10; } return cp; } /* exec with no args - args case is taken care of in comexec() */ int c_exec(wp) char ** wp; { int i; /* make sure redirects stay in place */ if (e->savefd != NULL) { for (i = 0; i < NUFILE; i++) { if (e->savefd[i] > 0) close(e->savefd[i]); /* * For ksh keep anything > 2 private, * for sh, let them be (POSIX says what * happens is unspecified and the bourne shell * keeps them open). */ #ifdef KSH if (i > 2 && e->savefd[i]) fd_clexec(i); #endif /* KSH */ } e->savefd = NULL; } return 0; } /* dummy function, special case in comexec() */ int c_builtin(wp) char ** wp; { return 0; } extern int c_test ARGS((char **wp)); /* in c_test.c */ extern int c_ulimit ARGS((char **wp)); /* in c_ulimit.c */ /* A leading = means assignments before command are kept; * a leading * means a POSIX special builtin; * a leading + means a POSIX regular builtin * (* and + should not be combined). */ const struct builtin shbuiltins [] = { {"*=.", c_dot}, {"*=:", c_label}, {"[", c_test}, {"*=break", c_brkcont}, {"=builtin", c_builtin}, {"*=continue", c_brkcont}, {"*=eval", c_eval}, {"*=exec", c_exec}, {"*=exit", c_exitreturn}, {"+false", c_label}, {"*=return", c_exitreturn}, {"*=set", c_set}, {"*=shift", c_shift}, {"=times", c_times}, {"*=trap", c_trap}, {"+=wait", c_wait}, {"+read", c_read}, {"test", c_test}, {"+true", c_label}, {"ulimit", c_ulimit}, {"+umask", c_umask}, {"*=unset", c_unset}, #ifdef OS2 /* In OS2, the first line of a file can be "extproc name", which * tells the command interpreter (cmd.exe) to use name to execute * the file. For this to be useful, ksh must ignore commands * starting with extproc and this does the trick... */ {"extproc", c_label}, #endif /* OS2 */ {NULL, NULL} }; /sys/src/ape/cmd/pdksh/c_test.c 664 sys sys 1367613436 15173 /* * test(1); version 7-like -- author Erik Baalbergen * modified by Eric Gisin to be used as built-in. * modified by Arnold Robbins to add SVR3 compatibility * (-x -c -b -p -u -g -k) plus Korn's -L -nt -ot -ef and new -S (socket). * modified by Michael Rendell to add Korn's [[ .. ]] expressions. * modified by J.T. Conklin to add POSIX compatibility. */ #include "sh.h" #include "ksh_stat.h" #include "c_test.h" /* test(1) accepts the following grammar: oexpr ::= aexpr | aexpr "-o" oexpr ; aexpr ::= nexpr | nexpr "-a" aexpr ; nexpr ::= primary | "!" nexpr ; primary ::= unary-operator operand | operand binary-operator operand | operand | "(" oexpr ")" ; unary-operator ::= "-a"|"-r"|"-w"|"-x"|"-e"|"-f"|"-d"|"-c"|"-b"|"-p"| "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"| "-L"|"-h"|"-S"|"-H"; binary-operator ::= "="|"=="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"| "-nt"|"-ot"|"-ef"| "<"|">" # rules used for [[ .. ]] expressions ; operand ::= */ #define T_ERR_EXIT 2 /* POSIX says > 1 for errors */ struct t_op { char op_text[4]; Test_op op_num; }; static const struct t_op u_ops [] = { {"-a", TO_FILAXST }, {"-b", TO_FILBDEV }, {"-c", TO_FILCDEV }, {"-d", TO_FILID }, {"-e", TO_FILEXST }, {"-f", TO_FILREG }, {"-G", TO_FILGID }, {"-g", TO_FILSETG }, {"-h", TO_FILSYM }, {"-H", TO_FILCDF }, {"-k", TO_FILSTCK }, {"-L", TO_FILSYM }, {"-n", TO_STNZE }, {"-O", TO_FILUID }, {"-o", TO_OPTION }, {"-p", TO_FILFIFO }, {"-r", TO_FILRD }, {"-s", TO_FILGZ }, {"-S", TO_FILSOCK }, {"-t", TO_FILTT }, {"-u", TO_FILSETU }, {"-w", TO_FILWR }, {"-x", TO_FILEX }, {"-z", TO_STZER }, {"", TO_NONOP } }; static const struct t_op b_ops [] = { {"=", TO_STEQL }, #ifdef KSH {"==", TO_STEQL }, #endif /* KSH */ {"!=", TO_STNEQ }, {"<", TO_STLT }, {">", TO_STGT }, {"-eq", TO_INTEQ }, {"-ne", TO_INTNE }, {"-gt", TO_INTGT }, {"-ge", TO_INTGE }, {"-lt", TO_INTLT }, {"-le", TO_INTLE }, {"-ef", TO_FILEQ }, {"-nt", TO_FILNT }, {"-ot", TO_FILOT }, {"", TO_NONOP } }; static int test_stat ARGS((const char *path, struct stat *statb)); static int test_eaccess ARGS((const char *path, int mode)); static int test_oexpr ARGS((Test_env *te, int do_eval)); static int test_aexpr ARGS((Test_env *te, int do_eval)); static int test_nexpr ARGS((Test_env *te, int do_eval)); static int test_primary ARGS((Test_env *te, int do_eval)); static int ptest_isa ARGS((Test_env *te, Test_meta meta)); static const char *ptest_getopnd ARGS((Test_env *te, Test_op op, int do_eval)); static int ptest_eval ARGS((Test_env *te, Test_op op, const char *opnd1, const char *opnd2, int do_eval)); static void ptest_error ARGS((Test_env *te, int offset, const char *msg)); int c_test(wp) char **wp; { int argc; int res; Test_env te; te.flags = 0; te.isa = ptest_isa; te.getopnd = ptest_getopnd; te.eval = ptest_eval; te.error = ptest_error; for (argc = 0; wp[argc]; argc++) ; if (strcmp(wp[0], "[") == 0) { if (strcmp(wp[--argc], "]") != 0) { bi_errorf("missing ]"); return T_ERR_EXIT; } } te.pos.wp = wp + 1; te.wp_end = wp + argc; /* * Handle the special cases from POSIX.2, section 4.62.4. * Implementation of all the rules isn't necessary since * our parser does the right thing for the ommited steps. */ if (argc <= 5) { char **owp = wp; int invert = 0; Test_op op; const char *opnd1, *opnd2; while (--argc >= 0) { if ((*te.isa)(&te, TM_END)) return !0; if (argc == 3) { opnd1 = (*te.getopnd)(&te, TO_NONOP, 1); if ((op = (Test_op) (*te.isa)(&te, TM_BINOP))) { opnd2 = (*te.getopnd)(&te, op, 1); res = (*te.eval)(&te, op, opnd1, opnd2, 1); if (te.flags & TEF_ERROR) return T_ERR_EXIT; if (invert & 1) res = !res; return !res; } /* back up to opnd1 */ te.pos.wp--; } if (argc == 1) { opnd1 = (*te.getopnd)(&te, TO_NONOP, 1); /* Historically, -t by itself test if fd 1 * is a file descriptor, but POSIX says its * a string test... */ if (!Flag(FPOSIX) && strcmp(opnd1, "-t") == 0) break; res = (*te.eval)(&te, TO_STNZE, opnd1, (char *) 0, 1); if (invert & 1) res = !res; return !res; } if ((*te.isa)(&te, TM_NOT)) { invert++; } else break; } te.pos.wp = owp + 1; } return test_parse(&te); } /* * Generic test routines. */ Test_op test_isop(te, meta, s) Test_env *te; Test_meta meta; const char *s; { char sc1; const struct t_op *otab; otab = meta == TM_UNOP ? u_ops : b_ops; if (*s) { sc1 = s[1]; for (; otab->op_text[0]; otab++) if (sc1 == otab->op_text[1] && strcmp(s, otab->op_text) == 0 && ((te->flags & TEF_DBRACKET) || (otab->op_num != TO_STLT && otab->op_num != TO_STGT))) return otab->op_num; } return TO_NONOP; } int test_eval(te, op, opnd1, opnd2, do_eval) Test_env *te; Test_op op; const char *opnd1; const char *opnd2; int do_eval; { int res; int not; struct stat b1, b2; if (!do_eval) return 0; switch ((int) op) { /* * Unary Operators */ case TO_STNZE: /* -n */ return *opnd1 != '\0'; case TO_STZER: /* -z */ return *opnd1 == '\0'; case TO_OPTION: /* -o */ if ((not = *opnd1 == '!')) opnd1++; if ((res = option(opnd1)) < 0) res = 0; else { res = Flag(res); if (not) res = !res; } return res; case TO_FILRD: /* -r */ return test_eaccess(opnd1, R_OK) == 0; case TO_FILWR: /* -w */ return test_eaccess(opnd1, W_OK) == 0; case TO_FILEX: /* -x */ return test_eaccess(opnd1, X_OK) == 0; case TO_FILAXST: /* -a */ return test_stat(opnd1, &b1) == 0; case TO_FILEXST: /* -e */ /* at&t ksh does not appear to do the /dev/fd/ thing for * this (unless the os itself handles it) */ return stat(opnd1, &b1) == 0; case TO_FILREG: /* -r */ return test_stat(opnd1, &b1) == 0 && S_ISREG(b1.st_mode); case TO_FILID: /* -d */ return test_stat(opnd1, &b1) == 0 && S_ISDIR(b1.st_mode); case TO_FILCDEV: /* -c */ #ifdef S_ISCHR return test_stat(opnd1, &b1) == 0 && S_ISCHR(b1.st_mode); #else return 0; #endif case TO_FILBDEV: /* -b */ #ifdef S_ISBLK return test_stat(opnd1, &b1) == 0 && S_ISBLK(b1.st_mode); #else return 0; #endif case TO_FILFIFO: /* -p */ #ifdef S_ISFIFO return test_stat(opnd1, &b1) == 0 && S_ISFIFO(b1.st_mode); #else return 0; #endif case TO_FILSYM: /* -h -L */ #ifdef S_ISLNK return lstat(opnd1, &b1) == 0 && S_ISLNK(b1.st_mode); #else return 0; #endif case TO_FILSOCK: /* -S */ #ifdef S_ISSOCK return test_stat(opnd1, &b1) == 0 && S_ISSOCK(b1.st_mode); #else return 0; #endif case TO_FILCDF:/* -H HP context dependent files (directories) */ #ifdef S_ISCDF { /* Append a + to filename and check to see if result is a * setuid directory. CDF stuff in general is hookey, since * it breaks for the following sequence: echo hi > foo+; * mkdir foo; echo bye > foo/default; chmod u+s foo * (foo+ refers to the file with hi in it, there is no way * to get at the file with bye in it - please correct me if * I'm wrong about this). */ int len = strlen(opnd1); char *p = str_nsave(opnd1, len + 1, ATEMP); p[len++] = '+'; p[len] = '\0'; return stat(p, &b1) == 0 && S_ISCDF(b1.st_mode); } #else return 0; #endif case TO_FILSETU: /* -u */ #ifdef S_ISUID return test_stat(opnd1, &b1) == 0 && (b1.st_mode & S_ISUID) == S_ISUID; #else return 0; #endif case TO_FILSETG: /* -g */ #ifdef S_ISGID return test_stat(opnd1, &b1) == 0 && (b1.st_mode & S_ISGID) == S_ISGID; #else return 0; #endif case TO_FILSTCK: /* -k */ return test_stat(opnd1, &b1) == 0 && (b1.st_mode & S_ISVTX) == S_ISVTX; case TO_FILGZ: /* -s */ return test_stat(opnd1, &b1) == 0 && b1.st_size > 0L; case TO_FILTT: /* -t */ if (opnd1 && !bi_getn(opnd1, &res)) { te->flags |= TEF_ERROR; res = 0; } else { /* generate error if in FPOSIX mode? */ res = isatty(opnd1 ? res : 0); } return res; case TO_FILUID: /* -O */ return test_stat(opnd1, &b1) == 0 && b1.st_uid == ksheuid; case TO_FILGID: /* -G */ return test_stat(opnd1, &b1) == 0 && b1.st_gid == getegid(); /* * Binary Operators */ case TO_STEQL: /* = */ if (te->flags & TEF_DBRACKET) return gmatch(opnd1, opnd2, FALSE); return strcmp(opnd1, opnd2) == 0; case TO_STNEQ: /* != */ if (te->flags & TEF_DBRACKET) return !gmatch(opnd1, opnd2, FALSE); return strcmp(opnd1, opnd2) != 0; case TO_STLT: /* < */ return strcmp(opnd1, opnd2) < 0; case TO_STGT: /* > */ return strcmp(opnd1, opnd2) > 0; case TO_INTEQ: /* -eq */ case TO_INTNE: /* -ne */ case TO_INTGE: /* -ge */ case TO_INTGT: /* -gt */ case TO_INTLE: /* -le */ case TO_INTLT: /* -lt */ { long v1, v2; if (!evaluate(opnd1, &v1, KSH_RETURN_ERROR) || !evaluate(opnd2, &v2, KSH_RETURN_ERROR)) { /* error already printed.. */ te->flags |= TEF_ERROR; return 1; } switch ((int) op) { case TO_INTEQ: return v1 == v2; case TO_INTNE: return v1 != v2; case TO_INTGE: return v1 >= v2; case TO_INTGT: return v1 > v2; case TO_INTLE: return v1 <= v2; case TO_INTLT: return v1 < v2; } } case TO_FILNT: /* -nt */ { int s2; /* ksh88/ksh93 succeed if file2 can't be stated * (subtly different from `does not exist'). */ return stat(opnd1, &b1) == 0 && (((s2 = stat(opnd2, &b2)) == 0 && b1.st_mtime > b2.st_mtime) || s2 < 0); } case TO_FILOT: /* -ot */ { int s1; /* ksh88/ksh93 succeed if file1 can't be stated * (subtly different from `does not exist'). */ return stat(opnd2, &b2) == 0 && (((s1 = stat(opnd1, &b1)) == 0 && b1.st_mtime < b2.st_mtime) || s1 < 0); } case TO_FILEQ: /* -ef */ return stat (opnd1, &b1) == 0 && stat (opnd2, &b2) == 0 && b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino; } (*te->error)(te, 0, "internal error: unknown op"); return 1; } /* Nasty kludge to handle Korn's bizarre /dev/fd hack */ static int test_stat(path, statb) const char *path; struct stat *statb; { #if !defined(HAVE_DEV_FD) int fd; if (strncmp(path, "/dev/fd/", 8) == 0 && getn(path + 8, &fd)) return fstat(fd, statb); #endif /* !HAVE_DEV_FD */ return stat(path, statb); } /* Routine to handle Korn's /dev/fd hack, and to deal with X_OK on * non-directories when running as root. */ static int test_eaccess(path, mode) const char *path; int mode; { int res; #if !defined(HAVE_DEV_FD) int fd; /* Note: doesn't handle //dev/fd, etc.. (this is ok) */ if (strncmp(path, "/dev/fd/", 8) == 0 && getn(path + 8, &fd)) { int flags; if ((flags = fcntl(fd, F_GETFL, 0)) < 0 || (mode & X_OK) || ((mode & W_OK) && (flags & O_ACCMODE) == O_RDONLY) || ((mode & R_OK) && (flags & O_ACCMODE) == O_WRONLY)) return -1; return 0; } #endif /* !HAVE_DEV_FD */ /* On most (all?) unixes, access() says everything is executable for * root - avoid this on files by using stat(). */ if ((mode & X_OK) && ksheuid == 0) { struct stat statb; if (stat(path, &statb) < 0) res = -1; else if (S_ISDIR(statb.st_mode)) res = 0; else res = (statb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) ? 0 : -1; /* Need to check other permissions? If so, use access() as * this will deal with root on NFS. */ if (res == 0 && (mode & (R_OK|W_OK))) res = eaccess(path, mode); } else res = eaccess(path, mode); return res; } int test_parse(te) Test_env *te; { int res; res = test_oexpr(te, 1); if (!(te->flags & TEF_ERROR) && !(*te->isa)(te, TM_END)) (*te->error)(te, 0, "unexpected operator/operand"); return (te->flags & TEF_ERROR) ? T_ERR_EXIT : !res; } static int test_oexpr(te, do_eval) Test_env *te; int do_eval; { int res; res = test_aexpr(te, do_eval); if (res) do_eval = 0; if (!(te->flags & TEF_ERROR) && (*te->isa)(te, TM_OR)) return test_oexpr(te, do_eval) || res; return res; } static int test_aexpr(te, do_eval) Test_env *te; int do_eval; { int res; res = test_nexpr(te, do_eval); if (!res) do_eval = 0; if (!(te->flags & TEF_ERROR) && (*te->isa)(te, TM_AND)) return test_aexpr(te, do_eval) && res; return res; } static int test_nexpr(te, do_eval) Test_env *te; int do_eval; { if (!(te->flags & TEF_ERROR) && (*te->isa)(te, TM_NOT)) return !test_nexpr(te, do_eval); return test_primary(te, do_eval); } static int test_primary(te, do_eval) Test_env *te; int do_eval; { const char *opnd1, *opnd2; int res; Test_op op; if (te->flags & TEF_ERROR) return 0; if ((*te->isa)(te, TM_OPAREN)) { res = test_oexpr(te, do_eval); if (te->flags & TEF_ERROR) return 0; if (!(*te->isa)(te, TM_CPAREN)) { (*te->error)(te, 0, "missing closing paren"); return 0; } return res; } if ((op = (Test_op) (*te->isa)(te, TM_UNOP))) { /* unary expression */ opnd1 = (*te->getopnd)(te, op, do_eval); if (!opnd1) { (*te->error)(te, -1, "missing argument"); return 0; } return (*te->eval)(te, op, opnd1, (const char *) 0, do_eval); } opnd1 = (*te->getopnd)(te, TO_NONOP, do_eval); if (!opnd1) { (*te->error)(te, 0, "expression expected"); return 0; } if ((op = (Test_op) (*te->isa)(te, TM_BINOP))) { /* binary expression */ opnd2 = (*te->getopnd)(te, op, do_eval); if (!opnd2) { (*te->error)(te, -1, "missing second argument"); return 0; } return (*te->eval)(te, op, opnd1, opnd2, do_eval); } if (te->flags & TEF_DBRACKET) { (*te->error)(te, -1, "missing expression operator"); return 0; } return (*te->eval)(te, TO_STNZE, opnd1, (const char *) 0, do_eval); } /* * Plain test (test and [ .. ]) specific routines. */ /* Test if the current token is a whatever. Accepts the current token if * it is. Returns 0 if it is not, non-zero if it is (in the case of * TM_UNOP and TM_BINOP, the returned value is a Test_op). */ static int ptest_isa(te, meta) Test_env *te; Test_meta meta; { /* Order important - indexed by Test_meta values */ static const char *const tokens[] = { "-o", "-a", "!", "(", ")" }; int ret; if (te->pos.wp >= te->wp_end) return meta == TM_END; if (meta == TM_UNOP || meta == TM_BINOP) ret = (int) test_isop(te, meta, *te->pos.wp); else if (meta == TM_END) ret = 0; else ret = strcmp(*te->pos.wp, tokens[(int) meta]) == 0; /* Accept the token? */ if (ret) te->pos.wp++; return ret; } static const char * ptest_getopnd(te, op, do_eval) Test_env *te; Test_op op; int do_eval; { if (te->pos.wp >= te->wp_end) return op == TO_FILTT ? "1" : (const char *) 0; return *te->pos.wp++; } static int ptest_eval(te, op, opnd1, opnd2, do_eval) Test_env *te; Test_op op; const char *opnd1; const char *opnd2; int do_eval; { return test_eval(te, op, opnd1, opnd2, do_eval); } static void ptest_error(te, offset, msg) Test_env *te; int offset; const char *msg; { const char *op = te->pos.wp + offset >= te->wp_end ? (const char *) 0 : te->pos.wp[offset]; te->flags |= TEF_ERROR; if (op) bi_errorf("%s: %s", op, msg); else bi_errorf("%s", msg); } /sys/src/ape/cmd/pdksh/c_test.h 664 sys sys 1367613436 1812 /* Various types of operations. Keeping things grouped nicely * (unary,binary) makes switch() statements more efficeint. */ enum Test_op { TO_NONOP = 0, /* non-operator */ /* unary operators */ TO_STNZE, TO_STZER, TO_OPTION, TO_FILAXST, TO_FILEXST, TO_FILREG, TO_FILBDEV, TO_FILCDEV, TO_FILSYM, TO_FILFIFO, TO_FILSOCK, TO_FILCDF, TO_FILID, TO_FILGID, TO_FILSETG, TO_FILSTCK, TO_FILUID, TO_FILRD, TO_FILGZ, TO_FILTT, TO_FILSETU, TO_FILWR, TO_FILEX, /* binary operators */ TO_STEQL, TO_STNEQ, TO_STLT, TO_STGT, TO_INTEQ, TO_INTNE, TO_INTGT, TO_INTGE, TO_INTLT, TO_INTLE, TO_FILEQ, TO_FILNT, TO_FILOT }; typedef enum Test_op Test_op; /* Used by Test_env.isa() (order important - used to index *_tokens[] arrays) */ enum Test_meta { TM_OR, /* -o or || */ TM_AND, /* -a or && */ TM_NOT, /* ! */ TM_OPAREN, /* ( */ TM_CPAREN, /* ) */ TM_UNOP, /* unary operator */ TM_BINOP, /* binary operator */ TM_END /* end of input */ }; typedef enum Test_meta Test_meta; #define TEF_ERROR BIT(0) /* set if we've hit an error */ #define TEF_DBRACKET BIT(1) /* set if [[ .. ]] test */ typedef struct test_env Test_env; struct test_env { int flags; /* TEF_* */ union { char **wp; /* used by ptest_* */ XPtrV *av; /* used by dbtestp_* */ } pos; char **wp_end; /* used by ptest_* */ int (*isa) ARGS((Test_env *te, Test_meta meta)); const char *(*getopnd) ARGS((Test_env *te, Test_op op, int do_eval)); int (*eval) ARGS((Test_env *te, Test_op op, const char *opnd1, const char *opnd2, int do_eval)); void (*error) ARGS((Test_env *te, int offset, const char *msg)); }; Test_op test_isop ARGS((Test_env *te, Test_meta meta, const char *s)); int test_eval ARGS((Test_env *te, Test_op op, const char *opnd1, const char *opnd2, int do_eval)); int test_parse ARGS((Test_env *te)); /sys/src/ape/cmd/pdksh/c_ulimit.c 664 sys sys 1367613436 6775 /* ulimit -- handle "ulimit" builtin Reworked to use getrusage() and ulimit() at once (as needed on some schizophenic systems, eg, HP-UX 9.01), made argument parsing conform to at&t ksh, added autoconf support. Michael Rendell, May, '94 Eric Gisin, September 1988 Adapted to PD KornShell. Removed AT&T code. last edit: 06-Jun-1987 D A Gwyn This started out as the BRL UNIX System V system call emulation for 4.nBSD, and was later extended by Doug Kingston to handle the extended 4.nBSD resource limits. It now includes the code that was originally under case SYSULIMIT in source file "xec.c". */ #include "sh.h" #include "ksh_time.h" #ifdef HAVE_SYS_RESOURCE_H # include #endif /* HAVE_SYS_RESOURCE_H */ #ifdef HAVE_ULIMIT_H # include #else /* HAVE_ULIMIT_H */ # ifdef HAVE_ULIMIT extern long ulimit(); # endif /* HAVE_ULIMIT */ #endif /* HAVE_ULIMIT_H */ #define SOFT 0x1 #define HARD 0x2 #ifdef RLIM_INFINITY # define KSH_RLIM_INFINITY RLIM_INFINITY #else # define KSH_RLIM_INFINITY ((rlim_t) 1 << (sizeof(rlim_t) * 8 - 1) - 1) #endif /* RLIM_INFINITY */ int c_ulimit(wp) char **wp; { static const struct limits { const char *name; enum { RLIMIT, ULIMIT } which; int gcmd; /* get command */ int scmd; /* set command (or -1, if no set command) */ int factor; /* multiply by to get rlim_{cur,max} values */ char option; } limits[] = { /* Do not use options -H, -S or -a */ #ifdef RLIMIT_CPU { "time(cpu-seconds)", RLIMIT, RLIMIT_CPU, RLIMIT_CPU, 1, 't' }, #endif #ifdef RLIMIT_FSIZE { "file(blocks)", RLIMIT, RLIMIT_FSIZE, RLIMIT_FSIZE, 512, 'f' }, #else /* RLIMIT_FSIZE */ # ifdef UL_GETFSIZE /* x/open */ { "file(blocks)", ULIMIT, UL_GETFSIZE, UL_SETFSIZE, 1, 'f' }, # else /* UL_GETFSIZE */ # ifdef UL_GFILLIM /* svr4/xenix */ { "file(blocks)", ULIMIT, UL_GFILLIM, UL_SFILLIM, 1, 'f' }, # else /* UL_GFILLIM */ { "file(blocks)", ULIMIT, 1, 2, 1, 'f' }, # endif /* UL_GFILLIM */ # endif /* UL_GETFSIZE */ #endif /* RLIMIT_FSIZE */ #ifdef RLIMIT_CORE { "coredump(blocks)", RLIMIT, RLIMIT_CORE, RLIMIT_CORE, 512, 'c' }, #endif #ifdef RLIMIT_DATA { "data(kbytes)", RLIMIT, RLIMIT_DATA, RLIMIT_DATA, 1024, 'd' }, #endif #ifdef RLIMIT_STACK { "stack(kbytes)", RLIMIT, RLIMIT_STACK, RLIMIT_STACK, 1024, 's' }, #endif #ifdef RLIMIT_MEMLOCK { "lockedmem(kbytes)", RLIMIT, RLIMIT_MEMLOCK, RLIMIT_MEMLOCK, 1024, 'l' }, #endif #ifdef RLIMIT_RSS { "memory(kbytes)", RLIMIT, RLIMIT_RSS, RLIMIT_RSS, 1024, 'm' }, #endif #ifdef RLIMIT_NOFILE { "nofiles(descriptors)", RLIMIT, RLIMIT_NOFILE, RLIMIT_NOFILE, 1, 'n' }, #else /* RLIMIT_NOFILE */ # ifdef UL_GDESLIM /* svr4/xenix */ { "nofiles(descriptors)", ULIMIT, UL_GDESLIM, -1, 1, 'n' }, # endif /* UL_GDESLIM */ #endif /* RLIMIT_NOFILE */ #ifdef RLIMIT_NPROC { "processes", RLIMIT, RLIMIT_NPROC, RLIMIT_NPROC, 1, 'p' }, #endif #ifdef RLIMIT_VMEM { "vmemory(kbytes)", RLIMIT, RLIMIT_VMEM, RLIMIT_VMEM, 1024, 'v' }, #else /* RLIMIT_VMEM */ /* These are not quite right - really should subtract etext or something */ # ifdef UL_GMEMLIM /* svr4/xenix */ { "vmemory(maxaddr)", ULIMIT, UL_GMEMLIM, -1, 1, 'v' }, # else /* UL_GMEMLIM */ # ifdef UL_GETBREAK /* osf/1 */ { "vmemory(maxaddr)", ULIMIT, UL_GETBREAK, -1, 1, 'v' }, # else /* UL_GETBREAK */ # ifdef UL_GETMAXBRK /* hpux */ { "vmemory(maxaddr)", ULIMIT, UL_GETMAXBRK, -1, 1, 'v' }, # endif /* UL_GETMAXBRK */ # endif /* UL_GETBREAK */ # endif /* UL_GMEMLIM */ #endif /* RLIMIT_VMEM */ #ifdef RLIMIT_SWAP { "swap(kbytes)", RLIMIT_SWAP, RLIMIT_SWAP, 1024, 'w' }, #endif { (char *) 0 } }; static char options[3 + NELEM(limits)]; rlim_t UNINITIALIZED(val); int how = SOFT | HARD; const struct limits *l; int set, all = 0; int optc, what; #ifdef HAVE_SETRLIMIT struct rlimit limit; #endif /* HAVE_SETRLIMIT */ if (!options[0]) { /* build options string on first call - yuck */ char *p = options; *p++ = 'H'; *p++ = 'S'; *p++ = 'a'; for (l = limits; l->name; l++) *p++ = l->option; *p = '\0'; } what = 'f'; while ((optc = ksh_getopt(wp, &builtin_opt, options)) != EOF) switch (optc) { case 'H': how = HARD; break; case 'S': how = SOFT; break; case 'a': all = 1; break; case '?': return 1; default: what = optc; } for (l = limits; l->name && l->option != what; l++) ; if (!l->name) { internal_errorf(0, "ulimit: %c", what); return 1; } wp += builtin_opt.optind; set = *wp ? 1 : 0; if (set) { if (all || wp[1]) { bi_errorf("too many arguments"); return 1; } if (strcmp(wp[0], "unlimited") == 0) val = KSH_RLIM_INFINITY; else { long rval; if (!evaluate(wp[0], &rval, KSH_RETURN_ERROR)) return 1; /* Avoid problems caused by typos that * evaluate misses due to evaluating unset * parameters to 0... * If this causes problems, will have to * add parameter to evaluate() to control * if unset params are 0 or an error. */ if (!rval && !digit(wp[0][0])) { bi_errorf("invalid limit: %s", wp[0]); return 1; } val = rval * l->factor; } } if (all) { for (l = limits; l->name; l++) { #ifdef HAVE_SETRLIMIT if (l->which == RLIMIT) { getrlimit(l->gcmd, &limit); if (how & SOFT) val = limit.rlim_cur; else if (how & HARD) val = limit.rlim_max; } else #endif /* HAVE_SETRLIMIT */ #ifdef HAVE_ULIMIT { val = ulimit(l->gcmd, (rlim_t) 0); } #else /* HAVE_ULIMIT */ ; #endif /* HAVE_ULIMIT */ shprintf("%-20s ", l->name); #ifdef RLIM_INFINITY if (val == RLIM_INFINITY) shprintf("unlimited\n"); else #endif /* RLIM_INFINITY */ { val /= l->factor; shprintf("%ld\n", (long) val); } } return 0; } #ifdef HAVE_SETRLIMIT if (l->which == RLIMIT) { getrlimit(l->gcmd, &limit); if (set) { if (how & SOFT) limit.rlim_cur = val; if (how & HARD) limit.rlim_max = val; if (setrlimit(l->scmd, &limit) < 0) { if (errno == EPERM) bi_errorf("exceeds allowable limit"); else bi_errorf("bad limit: %s", strerror(errno)); return 1; } } else { if (how & SOFT) val = limit.rlim_cur; else if (how & HARD) val = limit.rlim_max; } } else #endif /* HAVE_SETRLIMIT */ #ifdef HAVE_ULIMIT { if (set) { if (l->scmd == -1) { bi_errorf("can't change limit"); return 1; } else if (ulimit(l->scmd, val) < 0) { bi_errorf("bad limit: %s", strerror(errno)); return 1; } } else val = ulimit(l->gcmd, (rlim_t) 0); } #else /* HAVE_ULIMIT */ ; #endif /* HAVE_ULIMIT */ if (!set) { #ifdef RLIM_INFINITY if (val == RLIM_INFINITY) shprintf("unlimited\n"); else #endif /* RLIM_INFINITY */ { val /= l->factor; shprintf("%ld\n", (long) val); } } return 0; } /sys/src/ape/cmd/pdksh/conf-end.h 664 sys sys 1367613436 1735 /* * End of configuration stuff for PD ksh. * * RCSid: $Id$ */ #if defined(EMACS) || defined(VI) # define EDIT #else # undef EDIT #endif /* Editing implies history */ #if defined(EDIT) && !defined(HISTORY) # define HISTORY #endif /* EDIT */ /* * if you don't have mmap() you can't use Peter Collinson's history * mechanism. If that is the case, then define EASY_HISTORY */ #if defined(HISTORY) && (!defined(COMPLEX_HISTORY) || !defined(HAVE_MMAP) || !defined(HAVE_FLOCK)) # undef COMPLEX_HISTORY # define EASY_HISTORY /* sjg's trivial history file */ #endif /* Can we safely catch sigchld and wait for processes? */ #if (defined(HAVE_WAITPID) || defined(HAVE_WAIT3)) \ && (defined(POSIX_SIGNALS) || defined(BSD42_SIGNALS)) # define JOB_SIGS #endif #if !defined(JOB_SIGS) || !(defined(POSIX_PGRP) || defined(BSD_PGRP)) # undef JOBS /* if no JOB_SIGS, no job control support */ #endif /* pdksh assumes system calls return EINTR if a signal happened (this so * the signal handler doesn't have to longjmp()). I don't know if this * happens (or can be made to happen) with sigset() et. al. (the bsd41 signal * routines), so, the autoconf stuff checks what they do and defines * SIGNALS_DONT_INTERRUPT if signals don't interrupt read(). * If SIGNALS_DONT_INTERRUPT isn't defined and your compiler chokes on this, * delete the hash in front of the error (and file a bug report). */ #ifdef SIGNALS_DONT_INTERRUPT # error pdksh needs interruptable system calls. #endif /* SIGNALS_DONT_INTERRUPT */ #ifdef HAVE_GCC_FUNC_ATTR # define GCC_FUNC_ATTR(x) __attribute__((x)) # define GCC_FUNC_ATTR2(x,y) __attribute__((x,y)) #else # define GCC_FUNC_ATTR(x) # define GCC_FUNC_ATTR2(x,y) #endif /* HAVE_GCC_FUNC_ATTR */ /sys/src/ape/cmd/pdksh/config.h 664 sys sys 1367613436 10283 /* config.h. Generated automatically by configure. */ /* * This file, acconfig.h, which is a part of pdksh (the public domain ksh), * is placed in the public domain. It comes with no licence, warranty * or guarantee of any kind (i.e., at your own risk). */ #ifndef CONFIG_H #define CONFIG_H #define PLAN9 /* Define if on AIX 3. System headers sometimes define this. We just want to avoid a redefinition error message. */ #ifndef _ALL_SOURCE /* #undef _ALL_SOURCE */ #endif /* Define if the closedir function returns void instead of int. */ /* #undef CLOSEDIR_VOID */ /* Define to empty if the keyword does not work. */ /* #define const */ /* Define to `int' if doesn't define. */ /* #define gid_t int */ /* Define if you have a working `mmap' system call. */ /* #undef HAVE_MMAP */ /* Define if your struct stat has st_rdev. */ /* #undef HAVE_ST_RDEV */ /* Define if you have that is POSIX.1 compatible. */ #define HAVE_SYS_WAIT_H 1 /* Define if you have . */ #define HAVE_UNISTD_H /* Define if on MINIX. */ /* #define _MINIX 1 */ /* Define to `int' if doesn't define. */ /* #define mode_t short */ /* Define to `long' if doesn't define. */ /* #define off_t long */ /* Define to `int' if doesn't define. */ /* #define pid_t int */ /* Define if the system does not provide POSIX.1 features except with this defined. */ #define _POSIX_1_SOURCE 2 /* Define if you need to in order for stat and other things to work. */ #undef _POSIX_SOURCE #define _POSIX_SOURCE 1 /* Define as the return type of signal handlers (int or void). */ #define RETSIGTYPE void /* Define if the `S_IS*' macros in do not work properly. */ /* #undef STAT_MACROS_BROKEN */ /* Define if `sys_siglist' is declared by . */ /* #undef SYS_SIGLIST_DECLARED */ /* Define if you can safely include both and . */ #define TIME_WITH_SYS_TIME /* Define to `int' if doesn't define. */ /* #define uid_t int */ /* Define if the closedir function returns void instead of int. */ /* #undef VOID_CLOSEDIR */ /* Define if your kernal doesn't handle scripts starting with #! */ /* #undef SHARPBANG */ /* Define if dup2() preserves the close-on-exec flag (ultrix does this) */ #define DUP2_BROKEN 1 /* Define as the return value of signal handlers (0 or ). */ #define RETSIGVAL /* Define if you have posix signal routines (sigaction(), et. al.) */ /* #undef POSIX_SIGNALS */ /* Define if you have BSD4.2 signal routines (sigsetmask(), et. al.) */ /* #undef BSD42_SIGNALS */ /* Define if you have BSD4.1 signal routines (sigset(), et. al.) */ /* #undef BSD41_SIGNALS */ /* Define if you have v7 signal routines (signal(), signal reset on delivery) */ /* #define V7_SIGNALS 1 */ /* Define to use the fake posix signal routines (sigact.[ch]) */ /* #define USE_FAKE_SIGACT 1 */ /* Define if signals don't interrupt read() */ /* #undef SIGNALS_DONT_INTERRUPT */ /* Define if you have bsd versions of the setpgrp() and getpgrp() routines */ /* #undef BSD_PGRP */ /* Define if you have POSIX versions of the setpgid() and getpgrp() routines */ /* #undef POSIX_PGRP */ /* Define if you have sysV versions of the setpgrp() and getpgrp() routines */ /* #undef SYSV_PGRP */ /* Define if you don't have setpgrp(), setpgid() or getpgrp() routines */ #define NO_PGRP 1 /* Define to char if your compiler doesn't like the void keyword */ /* #define void char */ #undef void /* Define to nothing if compiler doesn't like the volatile keyword */ #define volatile /* Define if C compiler groks function prototypes */ #define HAVE_PROTOTYPES /* Define if C compiler groks __attribute__((...)) (const, noreturn, format) */ /* #undef HAVE_GCC_FUNC_ATTR */ /* Define to 32-bit signed integer type if doesn't define */ #define clock_t long /* Define to the type of struct rlimit fields if the rlim_t type is missing */ #define rlim_t long /* Define if time() is declared in */ #define TIME_DECLARED /* Define to `unsigned' if doesn't define */ /* #define sigset_t unsigned */ /* Define if sys_errlist[] and sys_nerr are in the C library */ /* #undef HAVE_SYS_ERRLIST */ #define _BSD_EXTENSION #define HAVE_SYS_ERRLIST /* Define if sys_errlist[] and sys_nerr are defined in */ #define SYS_ERRLIST_DECLARED /* Define if sys_siglist[] is in the C library */ /* #undef HAVE_SYS_SIGLIST */ /* Define if you have a sane header file */ #define HAVE_TERMIOS_H /* Define if you have a memset() function in your C library */ #define HAVE_MEMSET /* Define if you have a memmove() function in your C library */ #define HAVE_MEMMOVE /* Define if you have a bcopy() function in your C library */ /* #undef HAVE_BCOPY */ /* Define if you have a lstat() function in your C library */ #define HAVE_LSTAT /* Define if you have a sane header file */ /* #define HAVE_TERMIO_H 1 */ /* Define if you don't have times() or if it always returns 0 */ /* #define TIMES_BROKEN 1 */ /* Define if opendir() will open non-directory files */ /* #define OPENDIR_DOES_NONDIR 1 */ /* Define if the pgrp of setpgrp() can't be the pid of a zombie process */ /* #undef NEED_PGRP_SYNC */ /* Define if you arg running SCO unix */ /* #undef OS_SCO */ /* Define if you arg running ISC unix */ /* #undef OS_ISC */ /* Define if you arg running OS2 with the EMX library */ /* #undef OS2 */ /* Define if you have a POSIX.1 compatiable */ #define POSIX_SYS_WAIT /* Define if your OS maps references to /dev/fd/n to file descriptor n */ /* #undef HAVE_DEV_FD */ /* Define if your C library's getwd/getcwd function dumps core in unreadable * directories. */ /* #undef HPUX_GETWD_BUG */ /* Default PATH (see comments in configure.in for more details) */ #define DEFAULT_PATH "/bin:." /* Include ksh features? (see comments in configure.in for more details) */ #define KSH 1 /* Include emacs editing? (see comments in configure.in for more details) */ /* #define EMACS 0 */ /* Include vi editing? (see comments in configure.in for more details) */ /* #define VI 0 */ /* Include job control? (see comments in configure.in for more details) */ /* #define JOBS 0 */ /* Include brace-expansion? (see comments in configure.in for more details) */ /* #define BRACE_EXPAND 0 */ /* Include any history? (see comments in configure.in for more details) */ /* #define HISTORY 0 */ /* Include complex history? (see comments in configure.in for more details) */ /* #undef COMPLEX_HISTORY */ /* Strict POSIX behaviour? (see comments in configure.in for more details) */ #define POSIXLY_CORRECT /* Specify default $ENV? (see comments in configure.in for more details) */ /* #undef DEFAULT_ENV */ /* Include shl(1) support? (see comments in configure.in for more details) */ /* #undef SWTCH */ /* Include game-of-life? (see comments in configure.in for more details) */ /* #undef SILLY */ /* The number of bytes in a int. */ #define SIZEOF_INT sizeof(int) /* The number of bytes in a long. */ #define SIZEOF_LONG sizeof(long) /* Define if you have the _setjmp function. */ /* #undef HAVE__SETJMP */ /* Define if you have the confstr function. */ /* #undef HAVE_CONFSTR */ /* Define if you have the dup2 function. */ #define HAVE_DUP2 /* Define if you have the flock function. */ /* #undef HAVE_FLOCK */ /* Define if you have the getcwd function. */ #define HAVE_GETCWD /* Define if you have the getgroups function. */ #define HAVE_GETGROUPS /* Define if you have the getpagesize function. */ /* #undef HAVE_GETPAGESIZE */ /* Define if you have the getrusage function. */ /* #undef HAVE_GETRUSAGE */ /* Define if you have the getwd function. */ /* #undef HAVE_GETWD */ /* Define if you have the killpg function. */ /* #undef HAVE_KILLPG */ /* Define if you have the nice function. */ /* #undef HAVE_NICE */ /* Define if you have the setrlimit function. */ /* #undef HAVE_SETRLIMIT */ /* Define if you have the sigsetjmp function. */ #define HAVE_SIGSETJMP /* Define if you have the strcasecmp function. */ /* #undef HAVE_STRCASECMP */ /* Define if you have the strerror function. */ #define HAVE_STRERROR /* Define if you have the strstr function. */ #define HAVE_STRSTR /* Define if you have the sysconf function. */ /* #undef HAVE_SYSCONF */ /* Define if you have the tcsetpgrp function. */ #define HAVE_TCSETPGRP /* Define if you have the ulimit function. */ /* #undef HAVE_ULIMIT */ /* Define if you have the valloc function. */ /* #undef HAVE_VALLOC */ /* Define if you have the wait3 function. */ /* #undef HAVE_WAIT3 */ /* Define if you have the waitpid function. */ #define HAVE_WAITPID 1 /* Define if you have the header file. */ #define HAVE_DIRENT_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_MEMORY_H 1 */ /* Define if you have the header file. */ /* #undef HAVE_NDIR_H */ /* Define if you have the header file. */ /* #define HAVE_PATHS_H 1 */ /* Define if you have the header file. */ #define HAVE_STDDEF_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. */ /* #undef HAVE_SYS_DIR_H */ /* Define if you have the header file. */ /* #undef HAVE_SYS_NDIR_H */ /* Define if you have the header file. */ #define HAVE_SYS_PARAM_H 1 /* Define if you have the header file. */ #define HAVE_SYS_RESOURCE_H 1 /* Define if you have the header file. */ #define HAVE_SYS_TIME_H 1 /* Define if you have the header file. */ #define HAVE_SYS_WAIT_H 1 /* Define if you have the header file. */ /* #define HAVE_ULIMIT_H 1 */ /* Define if you have the header file. */ /* #define HAVE_VALUES_H 1 */ /* Need to use a separate file to keep the configure script from commenting * out the undefs.... */ #include "conf-end.h" #endif /* CONFIG_H */ /sys/src/ape/cmd/pdksh/crap.c 664 bootes sys 1367613436 1397 #include "../plan9/lib.h" #include "../plan9/sys9.h" #include #include /* A stack to hold pcs when signals nest */ #define MAXSIGSTACK 20 typedef struct Pcstack Pcstack; static struct Pcstack { int sig; void (*hdlr)(int, char*, Ureg*); unsigned long restorepc; Ureg *u; } pcstack[MAXSIGSTACK]; static int nstack = 0; static void notecont(Ureg*, char*); void _notetramp(int sig, void (*hdlr)(int, char*, Ureg*), Ureg *u) { Pcstack *p; if(nstack >= MAXSIGSTACK) _NOTED(1); /* nesting too deep; just do system default */ p = &pcstack[nstack]; p->restorepc = u->pc; p->sig = sig; p->hdlr = hdlr; p->u = u; nstack++; u->pc = (unsigned long) notecont; _NOTED(2); /* NSAVE: clear note but hold state */ } static void notecont(Ureg *u, char *s) { Pcstack *p; void(*f)(int, char*, Ureg*); p = &pcstack[nstack-1]; f = p->hdlr; u->pc = p->restorepc; nstack--; (*f)(p->sig, s, u); _NOTED(3); /* NRSTR */ } #define JMPBUFPC 1 #define JMPBUFSP 0 extern sigset_t _psigblocked; void siglongjmp(sigjmp_buf j, int ret) { struct Ureg *u; write(1,"_", 1); if(j[0]) _psigblocked = j[1]; if(nstack == 0 || pcstack[nstack-1].u->sp > j[2+JMPBUFSP]) longjmp(j+2, ret); write(1,"!", 1); u = pcstack[nstack-1].u; nstack--; u->ax = ret; if(ret == 0) u->ax = 1; u->pc = j[2+JMPBUFPC]; u->sp = j[2+JMPBUFSP] + 4; write(1,"?", 1); _NOTED(3); /* NRSTR */ } /sys/src/ape/cmd/pdksh/eval.c 664 sys sys 1367613436 31469 /* * Expansion - quoting, separation, substitution, globbing */ #include "sh.h" #include #include "ksh_dir.h" #include "ksh_stat.h" /* * string expansion * * first pass: quoting, IFS separation, ~, ${}, $() and $(()) substitution. * second pass: alternation ({,}), filename expansion (*?[]). */ /* expansion generator state */ typedef struct Expand { /* int type; */ /* see expand() */ const char *str; /* string */ union { const char **strv;/* string[] */ struct shf *shf;/* file */ } u; /* source */ struct tbl *var; /* variable in ${var..} */ short split; /* split "$@" / call waitlast $() */ } Expand; #define XBASE 0 /* scanning original */ #define XSUB 1 /* expanding ${} string */ #define XARGSEP 2 /* ifs0 between "$*" */ #define XARG 3 /* expanding $*, $@ */ #define XCOM 4 /* expanding $() */ #define XNULLSUB 5 /* "$@" when $# is 0 (don't generate word) */ /* States used for field splitting */ #define IFS_WORD 0 /* word has chars (or quotes) */ #define IFS_WS 1 /* have seen IFS white-space */ #define IFS_NWS 2 /* have seen IFS non-white-space */ static int varsub ARGS((Expand *xp, char *sp, char *word, int *stypep, int *slenp)); static int comsub ARGS((Expand *xp, char *cp)); static char *trimsub ARGS((char *str, char *pat, int how)); static void glob ARGS((char *cp, XPtrV *wp, int markdirs)); static void globit ARGS((XString *xs, char **xpp, char *sp, XPtrV *wp, int check)); static char *maybe_expand_tilde ARGS((char *p, XString *dsp, char **dpp, int isassign)); static char *tilde ARGS((char *acp)); static char *homedir ARGS((char *name)); #ifdef BRACE_EXPAND static void alt_expand ARGS((XPtrV *wp, char *start, char *exp_start, char *end, int fdo)); #endif /* compile and expand word */ char * substitute(cp, f) const char *cp; int f; { struct source *s, *sold; sold = source; s = pushs(SWSTR, ATEMP); s->start = s->str = cp; source = s; if (yylex(ONEWORD) != LWORD) internal_errorf(1, "substitute"); source = sold; afree(s, ATEMP); return evalstr(yylval.cp, f); } /* * expand arg-list */ char ** eval(ap, f) register char **ap; int f; { XPtrV w; if (*ap == NULL) return ap; XPinit(w, 32); XPput(w, NULL); /* space for shell name */ #ifdef SHARPBANG XPput(w, NULL); /* and space for one arg */ #endif while (*ap != NULL) expand(*ap++, &w, f); XPput(w, NULL); #ifdef SHARPBANG return (char **) XPclose(w) + 2; #else return (char **) XPclose(w) + 1; #endif } /* * expand string */ char * evalstr(cp, f) char *cp; int f; { XPtrV w; XPinit(w, 1); expand(cp, &w, f); cp = (XPsize(w) == 0) ? null : (char*) *XPptrv(w); XPfree(w); return cp; } /* * expand string - return only one component * used from iosetup to expand redirection files */ char * evalonestr(cp, f) register char *cp; int f; { XPtrV w; XPinit(w, 1); expand(cp, &w, f); switch (XPsize(w)) { case 0: cp = null; break; case 1: cp = (char*) *XPptrv(w); break; default: cp = evalstr(cp, f&~DOGLOB); break; } XPfree(w); return cp; } /* for nested substitution: ${var:=$var2} */ typedef struct SubType { short stype; /* [=+-?%#] action after expanded word */ short base; /* begin position of expanded word */ short f; /* saved value of f (DOPAT, etc) */ struct tbl *var; /* variable for ${var..} */ short quote; /* saved value of quote (for ${..[%#]..}) */ struct SubType *prev; /* old type */ struct SubType *next; /* poped type (to avoid re-allocating) */ } SubType; void expand(cp, wp, f) char *cp; /* input word */ register XPtrV *wp; /* output words */ int f; /* DO* flags */ { register int UNINITIALIZED(c); register int type; /* expansion type */ register int quote = 0; /* quoted */ XString ds; /* destination string */ register char *dp, *sp; /* dest., source */ int fdo, word; /* second pass flags; have word */ int doblank; /* field spliting of parameter/command subst */ Expand x; /* expansion variables */ SubType st_head, *st; int UNINITIALIZED(newlines); /* For trailing newlines in COMSUB */ int saw_eq, tilde_ok; int make_magic; if (cp == NULL) internal_errorf(1, "expand(NULL)"); /* for alias, readonly, set, typeset commands */ if ((f & DOVACHECK) && is_wdvarassign(cp)) { f &= ~(DOVACHECK|DOBLANK|DOGLOB|DOTILDE); f |= DOASNTILDE; } if (Flag(FNOGLOB)) f &= ~DOGLOB; if (Flag(FMARKDIRS)) f |= DOMARKDIRS; #ifdef BRACE_EXPAND if (Flag(FBRACEEXPAND) && (f & DOGLOB)) f |= DOBRACE_; #endif /* BRACE_EXPAND */ Xinit(ds, dp, 128, ATEMP); /* init dest. string */ type = XBASE; sp = cp; fdo = 0; saw_eq = 0; tilde_ok = (f & (DOTILDE|DOASNTILDE)) ? 1 : 0; /* must be 1/0 */ doblank = 0; make_magic = 0; word = (f&DOBLANK) ? IFS_WS : IFS_WORD; st_head.next = (SubType *) 0; st = &st_head; while (1) { Xcheck(ds, dp); switch (type) { case XBASE: /* original prefixed string */ c = *sp++; switch (c) { case EOS: c = 0; break; case CHAR: c = *sp++; break; case QCHAR: quote |= 2; /* temporary quote */ c = *sp++; break; case OQUOTE: word = IFS_WORD; tilde_ok = 0; quote = 1; continue; case CQUOTE: quote = 0; continue; case COMSUB: tilde_ok = 0; if (f & DONTRUNCOMMAND) { word = IFS_WORD; *dp++ = '$'; *dp++ = '('; while (*sp != '\0') { Xcheck(ds, dp); *dp++ = *sp++; } *dp++ = ')'; } else { type = comsub(&x, sp); if (type == XCOM && (f&DOBLANK)) doblank++; sp = strchr(sp, 0) + 1; newlines = 0; } continue; case EXPRSUB: word = IFS_WORD; tilde_ok = 0; if (f & DONTRUNCOMMAND) { *dp++ = '$'; *dp++ = '('; *dp++ = '('; while (*sp != '\0') { Xcheck(ds, dp); *dp++ = *sp++; } *dp++ = ')'; *dp++ = ')'; } else { struct tbl v; char *p; v.flag = DEFINED|ISSET|INTEGER; v.type = 10; /* not default */ v.name[0] = '\0'; v_evaluate(&v, substitute(sp, 0), KSH_UNWIND_ERROR); sp = strchr(sp, 0) + 1; for (p = str_val(&v); *p; ) { Xcheck(ds, dp); *dp++ = *p++; } } continue; case OSUBST: /* ${{#}var{:}[=+-?#%]word} */ /* format is: * OSUBST [{x] plain-variable-part \0 * compiled-word-part CSUBST [}x] * This is were all syntax checking gets done... */ { char *varname = ++sp; /* skip the { or x (}) */ int stype; int slen; sp = strchr(sp, '\0') + 1; /* skip variable */ type = varsub(&x, varname, sp, &stype, &slen); if (type < 0) { char endc; char *str, *end; end = (char *) wdscan(sp, CSUBST); /* ({) the } or x is already skipped */ endc = *end; *end = EOS; str = snptreef((char *) 0, 64, "%S", varname - 1); *end = endc; errorf("%s: bad substitution", str); } if (f&DOBLANK) doblank++; tilde_ok = 0; if (type == XBASE) { /* expand? */ if (!st->next) { SubType *newst; newst = (SubType *) alloc( sizeof(SubType), ATEMP); newst->next = (SubType *) 0; newst->prev = st; st->next = newst; } st = st->next; st->stype = stype; st->base = Xsavepos(ds, dp); st->f = f; st->var = x.var; st->quote = quote; /* skip qualifier(s) */ if (stype) sp += slen; switch (stype & 0x7f) { case '#': case '%': /* ! DOBLANK,DOBRACE_,DOTILDE */ f = DOPAT | (f&DONTRUNCOMMAND) | DOTEMP_; quote = 0; /* Prepend open pattern (so | * in a trim will work as * expected) */ *dp++ = MAGIC; *dp++ = '@' + 0x80; break; case '=': /* Enabling tilde expansion * after :'s here is * non-standard ksh, but is * consistent with rules for * other assignments. Not * sure what POSIX thinks of * this. * Not doing tilde expansion * for integer variables is a * non-POSIX thing - makes * sense though, since ~ is * a arithmetic operator. */ if (!(x.var->flag & INTEGER)) f |= DOASNTILDE|DOTILDE; f |= DOTEMP_; /* These will be done after the * value has been assigned. */ f &= ~(DOBLANK|DOGLOB|DOBRACE_); tilde_ok = 1; break; case '?': f &= ~DOBLANK; f |= DOTEMP_; /* fall through */ default: /* Enable tilde expansion */ tilde_ok = 1; f |= DOTILDE; } } else /* skip word */ sp = (char *) wdscan(sp, CSUBST); continue; } case CSUBST: /* only get here if expanding word */ sp++; /* ({) skip the } or x */ tilde_ok = 0; /* in case of ${unset:-} */ *dp = '\0'; quote = st->quote; f = st->f; if (f&DOBLANK) doblank--; switch (st->stype&0x7f) { case '#': case '%': /* Append end-pattern */ *dp++ = MAGIC; *dp++ = ')'; *dp = '\0'; dp = Xrestpos(ds, dp, st->base); /* Must use st->var since calling * global would break things * like x[i+=1]. */ x.str = trimsub(str_val(st->var), dp, st->stype); type = XSUB; if (f&DOBLANK) doblank++; st = st->prev; continue; case '=': /* Restore our position and substitute * the value of st->var (may not be * the assigned value in the presence * of integer/right-adj/etc attributes). */ dp = Xrestpos(ds, dp, st->base); /* Must use st->var since calling * global would cause with things * like x[i+=1] to be evaluated twice. */ /* Note: not exported by FEXPORT * in at&t ksh. */ /* XXX POSIX says readonly is only * fatal for special builtins (setstr * does readonly check). */ setstr(st->var, debunk( (char *) alloc(strlen(dp) + 1, ATEMP), dp), KSH_UNWIND_ERROR); x.str = str_val(st->var); type = XSUB; if (f&DOBLANK) doblank++; st = st->prev; continue; case '?': { char *s = Xrestpos(ds, dp, st->base); errorf("%s: %s", st->var->name, dp == s ? "parameter null or not set" : (debunk(s, s), s)); } } st = st->prev; type = XBASE; continue; case OPAT: /* open pattern: *(foo|bar) */ /* Next char is the type of pattern */ make_magic = 1; c = *sp++ + 0x80; break; case SPAT: /* pattern seperator (|) */ make_magic = 1; c = '|'; break; case CPAT: /* close pattern */ make_magic = 1; c = /*(*/ ')'; break; } break; case XNULLSUB: /* Special case for "$@" (and "${foo[@]}") - no * word is generated if $# is 0 (unless there is * other stuff inside the quotes). */ type = XBASE; if (f&DOBLANK) { doblank--; /* not really correct: x=; "$x$@" should * generate a null argument and * set A; "${@:+}" shouldn't. */ if (dp == Xstring(ds, dp)) word = IFS_WS; } continue; case XSUB: if ((c = *x.str++) == 0) { type = XBASE; if (f&DOBLANK) doblank--; continue; } break; case XARGSEP: type = XARG; quote = 1; case XARG: if ((c = *x.str++) == '\0') { /* force null words to be created so * set -- '' 2 ''; foo "$@" will do * the right thing */ if (quote && x.split) word = IFS_WORD; if ((x.str = *x.u.strv++) == NULL) { type = XBASE; if (f&DOBLANK) doblank--; continue; } c = ifs0; if (c == 0) { if (quote && !x.split) continue; c = ' '; } if (quote && x.split) { /* terminate word for "$@" */ type = XARGSEP; quote = 0; } } break; case XCOM: if (newlines) { /* Spit out saved nl's */ c = '\n'; --newlines; } else { while ((c = shf_getc(x.u.shf)) == 0 || c == '\n') if (c == '\n') newlines++; /* Save newlines */ if (newlines && c != EOF) { shf_ungetc(c, x.u.shf); c = '\n'; --newlines; } } if (c == EOF) { newlines = 0; shf_close(x.u.shf); if (x.split) subst_exstat = waitlast(); type = XBASE; if (f&DOBLANK) doblank--; continue; } break; } /* check for end of word or IFS separation */ if (c == 0 || (!quote && (f & DOBLANK) && doblank && !make_magic && ctype(c, C_IFS))) { /* How words are broken up: * | value of c * word | ws nws 0 * ----------------------------------- * IFS_WORD w/WS w/NWS w * IFS_WS -/WS w/NWS - * IFS_NWS -/NWS w/NWS w * (w means generate a word) * Note that IFS_NWS/0 generates a word (at&t ksh * doesn't do this, but POSIX does). */ if (word == IFS_WORD || (!ctype(c, C_IFSWS) && (c || word == IFS_NWS))) { char *p; *dp++ = '\0'; p = Xclose(ds, dp); #ifdef BRACE_EXPAND if (fdo & DOBRACE_) /* also does globbing */ alt_expand(wp, p, p, p + Xlength(ds, (dp - 1)), fdo | (f & DOMARKDIRS)); else #endif /* BRACE_EXPAND */ if (fdo & DOGLOB) glob(p, wp, f & DOMARKDIRS); else if ((f & DOPAT) || !(fdo & DOMAGIC_)) XPput(*wp, p); else XPput(*wp, debunk(p, p)); fdo = 0; saw_eq = 0; tilde_ok = (f & (DOTILDE|DOASNTILDE)) ? 1 : 0; if (c != 0) Xinit(ds, dp, 128, ATEMP); } if (c == 0) return; if (word != IFS_NWS) word = ctype(c, C_IFSWS) ? IFS_WS : IFS_NWS; } else { /* age tilde_ok info - ~ code tests second bit */ tilde_ok <<= 1; /* mark any special second pass chars */ if (!quote) switch (c) { case '[': case NOT: case '-': case ']': /* For character classes - doesn't hurt * to have magic !,-,]'s outside of * [...] expressions. */ if (f & (DOPAT | DOGLOB)) { fdo |= DOMAGIC_; if (c == '[') fdo |= f & DOGLOB; *dp++ = MAGIC; } break; case '*': case '?': if (f & (DOPAT | DOGLOB)) { fdo |= DOMAGIC_ | (f & DOGLOB); *dp++ = MAGIC; } break; #ifdef BRACE_EXPAND case OBRACE: case ',': case CBRACE: if ((f & DOBRACE_) && (c == OBRACE || (fdo & DOBRACE_))) { fdo |= DOBRACE_|DOMAGIC_; *dp++ = MAGIC; } break; #endif /* BRACE_EXPAND */ case '=': /* Note first unquoted = for ~ */ if (!(f & DOTEMP_) && !saw_eq) { saw_eq = 1; tilde_ok = 1; } break; case PATHSEP: /* : */ /* Note unquoted : for ~ */ if (!(f & DOTEMP_) && (f & DOASNTILDE)) tilde_ok = 1; break; case '~': /* tilde_ok is reset whenever * any of ' " $( $(( ${ } are seen. * Note that tilde_ok must be preserved * through the sequence ${A=a=}~ */ if (type == XBASE && (f & (DOTILDE|DOASNTILDE)) && (tilde_ok & 2)) { char *p, *dp_x; dp_x = dp; p = maybe_expand_tilde(sp, &ds, &dp_x, f & DOASNTILDE); if (p) { if (dp != dp_x) word = IFS_WORD; dp = dp_x; sp = p; continue; } } break; } else quote &= ~2; /* undo temporary */ if (make_magic) { make_magic = 0; fdo |= DOMAGIC_ | (f & DOGLOB); *dp++ = MAGIC; } else if (ISMAGIC(c)) { fdo |= DOMAGIC_; *dp++ = MAGIC; } *dp++ = c; /* save output char */ word = IFS_WORD; } } } /* * Prepare to generate the string returned by ${} substitution. */ static int varsub(xp, sp, word, stypep, slenp) Expand *xp; char *sp; char *word; int *stypep; /* becomes qualifier type */ int *slenp; /* " " len (=, :=, etc.) valid iff *stypep != 0 */ { int c; int state; /* next state: XBASE, XARG, XSUB, XNULLSUB */ int stype; /* substitution type */ int slen; char *p; struct tbl *vp; if (sp[0] == '\0') /* Bad variable name */ return -1; xp->var = (struct tbl *) 0; /* ${#var}, string length or array size */ if (sp[0] == '#' && (c = sp[1]) != '\0') { int zero_ok = 0; /* Can't have any modifiers for ${#...} */ if (*word != CSUBST) return -1; sp++; /* Check for size of array */ if ((p=strchr(sp,'[')) && (p[1]=='*'||p[1]=='@') && p[2]==']') { int n = 0; int max = 0; vp = global(arrayname(sp)); if (vp->flag & (ISSET|ARRAY)) zero_ok = 1; for (; vp; vp = vp->u.array) if (vp->flag & ISSET) { max = vp->index + 1; n++; } c = n; /* ksh88/ksh93 go for number, not max index */ } else if (c == '*' || c == '@') c = e->loc->argc; else { p = str_val(global(sp)); zero_ok = p != null; c = strlen(p); } if (Flag(FNOUNSET) && c == 0 && !zero_ok) errorf("%s: parameter not set", sp); *stypep = 0; /* unqualified variable/string substitution */ xp->str = str_save(ulton((unsigned long)c, 10), ATEMP); return XSUB; } /* Check for qualifiers in word part */ stype = 0; c = word[slen = 0] == CHAR ? word[1] : 0; if (c == ':') { slen += 2; stype = 0x80; c = word[slen + 0] == CHAR ? word[slen + 1] : 0; } if (ctype(c, C_SUBOP1)) { slen += 2; stype |= c; } else if (ctype(c, C_SUBOP2)) { /* Note: ksh88 allows :%, :%%, etc */ slen += 2; stype = c; if (word[slen + 0] == CHAR && c == word[slen + 1]) { stype |= 0x80; slen += 2; } } else if (stype) /* : is not ok */ return -1; if (!stype && *word != CSUBST) return -1; *stypep = stype; *slenp = slen; c = sp[0]; if (c == '*' || c == '@') { switch (stype & 0x7f) { case '=': /* can't assign to a vector */ case '%': /* can't trim a vector (yet) */ case '#': return -1; } if (e->loc->argc == 0) { xp->str = null; state = c == '@' ? XNULLSUB : XSUB; } else { xp->u.strv = (const char **) e->loc->argv + 1; xp->str = *xp->u.strv++; xp->split = c == '@'; /* $@ */ state = XARG; } } else { if ((p=strchr(sp,'[')) && (p[1]=='*'||p[1]=='@') && p[2]==']') { XPtrV wv; switch (stype & 0x7f) { case '=': /* can't assign to a vector */ case '%': /* can't trim a vector (yet) */ case '#': return -1; } XPinit(wv, 32); vp = global(arrayname(sp)); for (; vp; vp = vp->u.array) { if (!(vp->flag&ISSET)) continue; XPput(wv, str_val(vp)); } if (XPsize(wv) == 0) { xp->str = null; state = p[1] == '@' ? XNULLSUB : XSUB; XPfree(wv); } else { XPput(wv, 0); xp->u.strv = (const char **) XPptrv(wv); xp->str = *xp->u.strv++; xp->split = p[1] == '@'; /* ${foo[@]} */ state = XARG; } } else { /* Can't assign things like $! or $1 */ if ((stype & 0x7f) == '=' && (ctype(*sp, C_VAR1) || digit(*sp))) return -1; xp->var = global(sp); xp->str = str_val(xp->var); state = XSUB; } } c = stype&0x7f; /* test the compiler's code generator */ if (ctype(c, C_SUBOP2) || (((stype&0x80) ? *xp->str=='\0' : xp->str==null) ? /* undef? */ c == '=' || c == '-' || c == '?' : c == '+')) state = XBASE; /* expand word instead of variable value */ if (Flag(FNOUNSET) && xp->str == null && (ctype(c, C_SUBOP2) || (state != XBASE && c != '+'))) errorf("%s: parameter not set", sp); return state; } /* * Run the command in $(...) and read its output. */ static int comsub(xp, cp) register Expand *xp; char *cp; { Source *s, *sold; register struct op *t; struct shf *shf; s = pushs(SSTRING, ATEMP); s->start = s->str = cp; sold = source; t = compile(s); source = sold; if (t == NULL) return XBASE; if (t != NULL && t->type == TCOM && /* $(args == NULL && *t->vars == NULL && t->ioact != NULL) { register struct ioword *io = *t->ioact; char *name; if ((io->flag&IOTYPE) != IOREAD) errorf("funny $() command: %s", snptreef((char *) 0, 32, "%R", io)); shf = shf_open(name = evalstr(io->name, DOTILDE), O_RDONLY, 0, SHF_MAPHI|SHF_CLEXEC); if (shf == NULL) errorf("%s: cannot open $() input", name); xp->split = 0; /* no waitlast() */ } else { int ofd1, pv[2]; openpipe(pv); shf = shf_fdopen(pv[0], SHF_RD, (struct shf *) 0); ofd1 = savefd(1, 0); /* fd 1 may be closed... */ ksh_dup2(pv[1], 1, FALSE); close(pv[1]); execute(t, XFORK|XXCOM|XPIPEO); restfd(1, ofd1); startlast(); xp->split = 1; /* waitlast() */ } xp->u.shf = shf; return XCOM; } /* * perform #pattern and %pattern substitution in ${} */ static char * trimsub(str, pat, how) register char *str; char *pat; int how; { register char *end = strchr(str, 0); register char *p, c; switch (how&0xff) { /* UCHAR_MAX maybe? */ case '#': /* shortest at begining */ for (p = str; p <= end; p++) { c = *p; *p = '\0'; if (gmatch(str, pat, FALSE)) { *p = c; return p; } *p = c; } break; case '#'|0x80: /* longest match at begining */ for (p = end; p >= str; p--) { c = *p; *p = '\0'; if (gmatch(str, pat, FALSE)) { *p = c; return p; } *p = c; } break; case '%': /* shortest match at end */ for (p = end; p >= str; p--) { if (gmatch(p, pat, FALSE)) return str_nsave(str, p - str, ATEMP); } break; case '%'|0x80: /* longest match at end */ for (p = str; p <= end; p++) { if (gmatch(p, pat, FALSE)) return str_nsave(str, p - str, ATEMP); } break; } return str; /* no match, return string */ } /* * glob * Name derived from V6's /etc/glob, the program that expanded filenames. */ /* XXX cp not const 'cause slashes are temporarily replaced with nulls... */ static void glob(cp, wp, markdirs) char *cp; register XPtrV *wp; int markdirs; { int oldsize = XPsize(*wp); if (glob_str(cp, wp, markdirs) == 0) XPput(*wp, debunk(cp, cp)); else qsortp(XPptrv(*wp) + oldsize, (size_t)(XPsize(*wp) - oldsize), xstrcmp); } #define GF_NONE 0 #define GF_EXCHECK BIT(0) /* do existance check on file */ #define GF_GLOBBED BIT(1) /* some globbing has been done */ #define GF_MARKDIR BIT(2) /* add trailing / to directories */ /* Apply file globbing to cp and store the matching files in wp. Returns * the number of matches found. */ int glob_str(cp, wp, markdirs) char *cp; XPtrV *wp; int markdirs; { int oldsize = XPsize(*wp); XString xs; char *xp; Xinit(xs, xp, 256, ATEMP); globit(&xs, &xp, cp, wp, markdirs ? GF_MARKDIR : GF_NONE); Xfree(xs, xp); return XPsize(*wp) - oldsize; } static void globit(xs, xpp, sp, wp, check) XString *xs; /* dest string */ char **xpp; /* ptr to dest end */ char *sp; /* source path */ register XPtrV *wp; /* output list */ int check; /* GF_* flags */ { register char *np; /* next source component */ char *xp = *xpp; char *se; char odirsep; /* This to allow long expansions to be interrupted */ intrcheck(); if (sp == NULL) { /* end of source path */ /* We only need to check if the file exists if a pattern * is followed by a non-pattern (eg, foo*x/bar; no check * is needed for foo* since the match must exist) or if * any patterns were expanded and the markdirs option is set. * Symlinks make things a bit tricky... */ if ((check & GF_EXCHECK) || ((check & GF_MARKDIR) && (check & GF_GLOBBED))) { #define stat_check() (stat_done ? stat_done : \ (stat_done = stat(Xstring(*xs, xp), &statb) < 0 \ ? -1 : 1)) struct stat lstatb, statb; int stat_done = 0; /* -1: failed, 1 ok */ if (lstat(Xstring(*xs, xp), &lstatb) < 0) return; /* special case for systems which strip trailing * slashes from regular files (eg, /etc/passwd/). * SunOS 4.1.3 does this... */ if ((check & GF_EXCHECK) && xp > Xstring(*xs, xp) && ISDIRSEP(xp[-1]) && !S_ISDIR(lstatb.st_mode) #ifdef S_ISLNK && (!S_ISLNK(lstatb.st_mode) || stat_check() < 0 || !S_ISDIR(statb.st_mode)) #endif /* S_ISLNK */ ) return; /* Possibly tack on a trailing / if there isn't already * one and if the file is a directory or a symlink to a * directory */ if (((check & GF_MARKDIR) && (check & GF_GLOBBED)) && xp > Xstring(*xs, xp) && !ISDIRSEP(xp[-1]) && (S_ISDIR(lstatb.st_mode) #ifdef S_ISLNK || (S_ISLNK(lstatb.st_mode) && stat_check() > 0 && S_ISDIR(statb.st_mode)) #endif /* S_ISLNK */ )) { *xp++ = DIRSEP; *xp = '\0'; } } #ifdef OS2 /* Done this way to avoid bug in gcc 2.7.2... */ /* Ugly kludge required for command * completion - see how search_access() * is implemented for OS/2... */ # define KLUDGE_VAL 4 #else /* OS2 */ # define KLUDGE_VAL 0 #endif /* OS2 */ XPput(*wp, str_nsave(Xstring(*xs, xp), Xlength(*xs, xp) + KLUDGE_VAL, ATEMP)); return; } if (xp > Xstring(*xs, xp)) *xp++ = DIRSEP; while (ISDIRSEP(*sp)) { Xcheck(*xs, xp); *xp++ = *sp++; } np = ksh_strchr_dirsep(sp); if (np != NULL) { se = np; odirsep = *np; /* don't assume DIRSEP, can be multiple kinds */ *np++ = '\0'; } else { odirsep = '\0'; /* keep gcc quiet */ se = sp + strlen(sp); } /* Check if sp needs globbing - done to avoid pattern checks for strings * containing MAGIC characters, open ['s without the matching close ], * etc. (otherwise opendir() will be called which may fail because the * directory isn't readable - if no globbing is needed, only execute * permission should be required (as per POSIX)). */ if (!has_globbing(sp, se)) { XcheckN(*xs, xp, se - sp + 1); debunk(xp, sp); xp += strlen(xp); *xpp = xp; globit(xs, xpp, np, wp, check); } else { DIR *dirp; struct dirent *d; char *name; int len; int prefix_len; /* xp = *xpp; copy_non_glob() may have re-alloc'd xs */ *xp = '\0'; prefix_len = Xlength(*xs, xp); dirp = ksh_opendir(prefix_len ? Xstring(*xs, xp) : "."); if (dirp == NULL) goto Nodir; while ((d = readdir(dirp)) != NULL) { name = d->d_name; if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) continue; /* always ignore . and .. */ if ((*name == '.' && *sp != '.') || !gmatch(name, sp, TRUE)) continue; len = NLENGTH(d) + 1; XcheckN(*xs, xp, len); memcpy(xp, name, len); *xpp = xp + len - 1; globit(xs, xpp, np, wp, (check & GF_MARKDIR) | GF_GLOBBED | (np ? GF_EXCHECK : GF_NONE)); xp = Xstring(*xs, xp) + prefix_len; } closedir(dirp); Nodir:; } if (np != NULL) *--np = odirsep; } #if 0 /* Check if p contains something that needs globbing; if it does, 0 is * returned; if not, p is copied into xs/xp after stripping any MAGICs */ static int copy_non_glob ARGS((XString *xs, char **xpp, char *p)); static int copy_non_glob(xs, xpp, p) XString *xs; char **xpp; char *p; { char *xp; int len = strlen(p); XcheckN(*xs, *xpp, len); xp = *xpp; for (; *p; p++) { if (ISMAGIC(*p)) { int c = *++p; if (c == '*' || c == '?') return 0; if (*p == '[') { char *q = p + 1; if (ISMAGIC(*q) && q[1] == NOT) q += 2; if (ISMAGIC(*q) && q[1] == ']') q += 2; for (; *q; q++) if (ISMAGIC(*q) && *++q == ']') return 0; /* pass a literal [ through */ } /* must be a MAGIC-MAGIC, or MAGIC-!, MAGIC--, etc. */ } *xp++ = *p; } *xp = '\0'; *xpp = xp; return 1; } #endif /* 0 */ /* remove MAGIC from string */ char * debunk(dp, sp) char *dp; const char *sp; { char *d, *s; if ((s = strchr(sp, MAGIC))) { memcpy(dp, sp, s - sp); for (d = dp + (s - sp); *s; s++) if (!ISMAGIC(*s) || !(*++s & 0x80) || !strchr("*+?@! ", *s & 0x7f)) *d++ = *s; else { /* extended pattern operators: *+?@! */ if ((*s & 0x7f) != ' ') *d++ = *s & 0x7f; *d++ = '('; } *d = '\0'; } else if (dp != sp) strcpy(dp, sp); return dp; } /* Check if p is an unquoted name, possibly followed by a / or :. If so * puts the expanded version in *dcp,dp and returns a pointer in p just * past the name, otherwise returns 0. */ static char * maybe_expand_tilde(p, dsp, dpp, isassign) char *p; XString *dsp; char **dpp; int isassign; { XString ts; char *dp = *dpp; char *tp, *r; Xinit(ts, tp, 16, ATEMP); /* : only for DOASNTILDE form */ while (p[0] == CHAR && !ISDIRSEP(p[1]) && (!isassign || p[1] != PATHSEP)) { Xcheck(ts, tp); *tp++ = p[1]; p += 2; } *tp = '\0'; r = (p[0] == EOS || p[0] == CHAR || p[0] == CSUBST) ? tilde(Xstring(ts, tp)) : (char *) 0; Xfree(ts, tp); if (r) { while (*r) { Xcheck(*dsp, dp); if (ISMAGIC(*r)) *dp++ = MAGIC; *dp++ = *r++; } *dpp = dp; r = p; } return r; } /* * tilde expansion * * based on a version by Arnold Robbins */ static char * tilde(cp) char *cp; { char *dp; if (cp[0] == '\0') dp = str_val(global("HOME")); else if (cp[0] == '+' && cp[1] == '\0') dp = str_val(global("PWD")); else if (cp[0] == '-' && cp[1] == '\0') dp = str_val(global("OLDPWD")); else dp = homedir(cp); /* If HOME, PWD or OLDPWD are not set, don't expand ~ */ if (dp == null) dp = (char *) 0; return dp; } /* * map userid to user's home directory. * note that 4.3's getpw adds more than 6K to the shell, * and the YP version probably adds much more. * we might consider our own version of getpwnam() to keep the size down. */ static char * homedir(name) char *name; { register struct tbl *ap; ap = tenter(&homedirs, name, hash(name)); if (!(ap->flag & ISSET)) { #ifdef OS2 /* No usernames in OS2 - punt */ return NULL; #else /* OS2 */ struct passwd *pw; pw = getpwnam(name); if (pw == NULL) return NULL; ap->val.s = str_save(pw->pw_dir, APERM); ap->flag |= DEFINED|ISSET|ALLOC; #endif /* OS2 */ } return ap->val.s; } #ifdef BRACE_EXPAND static void alt_expand(wp, start, exp_start, end, fdo) XPtrV *wp; char *start, *exp_start; char *end; int fdo; { int UNINITIALIZED(count); char *brace_start, *brace_end, *UNINITIALIZED(comma); char *field_start; char *p; /* search for open brace */ for (p = exp_start; (p = strchr(p, MAGIC)) && p[1] != OBRACE; p += 2) ; brace_start = p; /* find matching close brace, if any */ if (p) { comma = (char *) 0; count = 1; for (p += 2; *p && count; p++) { if (ISMAGIC(*p)) { if (*++p == OBRACE) count++; else if (*p == CBRACE) --count; else if (*p == ',' && count == 1) comma = p; } } } /* no valid expansions... */ if (!p || count != 0) { /* Note that given a{{b,c} we do not expand anything (this is * what at&t ksh does. This may be changed to do the {b,c} * expansion. } */ if (fdo & DOGLOB) glob(start, wp, fdo & DOMARKDIRS); else XPput(*wp, debunk(start, start)); return; } brace_end = p; if (!comma) { alt_expand(wp, start, brace_end, end, fdo); return; } /* expand expression */ field_start = brace_start + 2; count = 1; for (p = brace_start + 2; p != brace_end; p++) { if (ISMAGIC(*p)) { if (*++p == OBRACE) count++; else if ((*p == CBRACE && --count == 0) || (*p == ',' && count == 1)) { char *new; int l1, l2, l3; l1 = brace_start - start; l2 = (p - 1) - field_start; l3 = end - brace_end; new = (char *) alloc(l1 + l2 + l3 + 1, ATEMP); memcpy(new, start, l1); memcpy(new + l1, field_start, l2); memcpy(new + l1 + l2, brace_end, l3); new[l1 + l2 + l3] = '\0'; alt_expand(wp, new, new + l1, new + l1 + l2 + l3, fdo); field_start = p + 1; } } } return; } #endif /* BRACE_EXPAND */ /sys/src/ape/cmd/pdksh/exec.c 664 sys sys 1367613436 38927 /* * execute command tree */ #include "sh.h" #include "c_test.h" #include #include "ksh_stat.h" /* Does ps4 get parameter substitutions done? */ #ifdef KSH # define PS4_SUBSTITUTE(s) substitute((s), 0) #else # define PS4_SUBSTITUTE(s) (s) #endif /* KSH */ static int comexec ARGS((struct op *t, struct tbl *volatile tp, char **ap, int volatile flags)); static void scriptexec ARGS((struct op *tp, char **ap)); static int call_builtin ARGS((struct tbl *tp, char **wp)); static int iosetup ARGS((struct ioword *iop, struct tbl *tp)); static int herein ARGS((const char *content, int sub)); #ifdef KSH static char *do_selectargs ARGS((char **ap, bool_t print_menu)); #endif /* KSH */ #ifdef KSH static int dbteste_isa ARGS((Test_env *te, Test_meta meta)); static const char *dbteste_getopnd ARGS((Test_env *te, Test_op op, int do_eval)); static int dbteste_eval ARGS((Test_env *te, Test_op op, const char *opnd1, const char *opnd2, int do_eval)); static void dbteste_error ARGS((Test_env *te, int offset, const char *msg)); #endif /* KSH */ #ifdef OS2 static int search_access1 ARGS((const char *path, int mode, int *errnop)); #endif /* OS2 */ /* * handle systems that don't have F_SETFD */ #ifndef F_SETFD # ifndef MAXFD # define MAXFD 64 # endif /* a bit field would be smaller, but this will work */ static char clexec_tab[MAXFD+1]; #endif /* * we now use this function always. */ int fd_clexec(fd) int fd; { #ifndef F_SETFD if (fd >= 0 && fd < sizeof(clexec_tab)) { clexec_tab[fd] = 1; return 0; } return -1; #else return fcntl(fd, F_SETFD, 1); #endif } /* * execute command tree */ int execute(t, flags) struct op * volatile t; volatile int flags; /* if XEXEC don't fork */ { int i; volatile int rv = 0; int pv[2]; char ** volatile ap; char *s, *cp; struct ioword **iowp; struct tbl *tp = NULL; if (t == NULL) return 0; /* Is this the end of a pipeline? If so, we want to evaluate the * command arguments bool_t eval_done = FALSE; if ((flags&XFORK) && !(flags&XEXEC) && (flags&XPCLOSE)) { eval_done = TRUE; tp = eval_execute_args(t, &ap); } */ if ((flags&XFORK) && !(flags&XEXEC) && t->type != TPIPE) return exchild(t, flags & ~XTIME, -1); /* run in sub-process */ newenv(E_EXEC); if (trap) runtraps(0); if (t->type == TCOM) { /* Clear subst_exstat before argument expansion. Used by * null commands (see comexec() and c_eval()) and by c_set(). */ subst_exstat = 0; current_lineno = t->lineno; /* for $LINENO */ /* POSIX says expand command words first, then redirections, * and assignments last.. */ ap = eval(t->args, t->u.evalflags | DOBLANK | DOGLOB | DOTILDE); if (flags & XTIME) /* Allow option parsing (bizarre, but POSIX) */ timex_hook(t, &ap); if (Flag(FXTRACE) && ap[0]) { shf_fprintf(shl_out, "%s", PS4_SUBSTITUTE(str_val(global("PS4")))); for (i = 0; ap[i]; i++) shf_fprintf(shl_out, "%s%s", ap[i], ap[i + 1] ? space : newline); shf_flush(shl_out); } if (ap[0]) tp = findcom(ap[0], FC_BI|FC_FUNC); } flags &= ~XTIME; if (t->ioact != NULL || t->type == TPIPE || t->type == TCOPROC) { e->savefd = (short *) alloc(sizeofN(short, NUFILE), ATEMP); /* initialize to not redirected */ memset(e->savefd, 0, sizeofN(short, NUFILE)); } /* do redirection, to be restored in quitenv() */ if (t->ioact != NULL) for (iowp = t->ioact; *iowp != NULL; iowp++) { if (iosetup(*iowp, tp) < 0) { exstat = rv = 1; /* Redirection failures for special commands * cause (non-interactive) shell to exit. */ if (tp && tp->type == CSHELL && (tp->flag & SPEC_BI)) errorf(null); /* Deal with FERREXIT, quitenv(), etc. */ goto Break; } } switch(t->type) { case TCOM: rv = comexec(t, tp, ap, flags); break; case TPAREN: rv = execute(t->left, flags|XFORK); break; case TPIPE: flags |= XFORK; flags &= ~XEXEC; e->savefd[0] = savefd(0, 0); (void) ksh_dup2(e->savefd[0], 0, FALSE); /* stdin of first */ e->savefd[1] = savefd(1, 0); while (t->type == TPIPE) { openpipe(pv); (void) ksh_dup2(pv[1], 1, FALSE); /* stdout of curr */ /* Let exchild() close pv[0] in child * (if this isn't done, commands like * (: ; cat /etc/termcap) | sleep 1 * will hang forever). */ exchild(t->left, flags|XPIPEO|XCCLOSE, pv[0]); (void) ksh_dup2(pv[0], 0, FALSE); /* stdin of next */ closepipe(pv); flags |= XPIPEI; t = t->right; } restfd(1, e->savefd[1]); /* stdout of last */ e->savefd[1] = 0; /* no need to re-restore this */ /* Let exchild() close 0 in parent, after fork, before wait */ i = exchild(t, flags|XPCLOSE, 0); if (!(flags&XBGND) && !(flags&XXCOM)) rv = i; break; case TLIST: while (t->type == TLIST) { execute(t->left, flags & XERROK); t = t->right; } rv = execute(t, flags & XERROK); break; #ifdef KSH case TCOPROC: { # ifdef JOB_SIGS sigset_t omask; # endif /* JOB_SIGS */ # ifdef JOB_SIGS /* Block sigchild as we are using things changed in the * signal handler */ sigprocmask(SIG_BLOCK, &sm_sigchld, &omask); e->type = E_ERRH; i = ksh_sigsetjmp(e->jbuf, 0); if (i) { sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0); quitenv(); unwind(i); /*NOTREACHED*/ } # endif /* JOB_SIGS */ /* Already have a (live) co-process? */ if (coproc.job && coproc.write >= 0) errorf("coprocess already exists"); /* Can we re-use the existing co-process pipe? */ coproc_cleanup(TRUE); /* do this before opening pipes, in case these fail */ e->savefd[0] = savefd(0, 0); e->savefd[1] = savefd(1, 0); openpipe(pv); ksh_dup2(pv[0], 0, FALSE); close(pv[0]); coproc.write = pv[1]; coproc.job = (void *) 0; if (coproc.readw >= 0) ksh_dup2(coproc.readw, 1, FALSE); else { openpipe(pv); coproc.read = pv[0]; ksh_dup2(pv[1], 1, FALSE); coproc.readw = pv[1]; /* closed before first read */ coproc.njobs = 0; /* create new coprocess id */ ++coproc.id; } # ifdef JOB_SIGS sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0); e->type = E_EXEC; /* no more need for error handler */ # endif /* JOB_SIGS */ /* exchild() closes coproc.* in child after fork, * will also increment coproc.njobs when the * job is actually created. */ flags &= ~XEXEC; exchild(t->left, flags|XBGND|XFORK|XCOPROC|XCCLOSE, coproc.readw); break; } #endif /* KSH */ case TASYNC: /* XXX non-optimal, I think - "(foo &)", forks for (), * forks again for async... parent should optimize * this to "foo &"... */ rv = execute(t->left, (flags&~XEXEC)|XBGND|XFORK); break; case TOR: case TAND: rv = execute(t->left, XERROK); if (t->right != NULL && (rv == 0) == (t->type == TAND)) rv = execute(t->right, flags & XERROK); else flags |= XERROK; break; case TBANG: rv = !execute(t->right, XERROK); break; #ifdef KSH case TDBRACKET: { Test_env te; te.flags = TEF_DBRACKET; te.pos.wp = t->args; te.isa = dbteste_isa; te.getopnd = dbteste_getopnd; te.eval = dbteste_eval; te.error = dbteste_error; rv = test_parse(&te); break; } #endif /* KSH */ case TFOR: #ifdef KSH case TSELECT: { volatile bool_t is_first = TRUE; #endif /* KSH */ ap = (t->vars != NULL) ? eval(t->vars, DOBLANK|DOGLOB|DOTILDE) : e->loc->argv + 1; e->type = E_LOOP; while (1) { i = ksh_sigsetjmp(e->jbuf, 0); if (!i) break; if ((e->flags&EF_BRKCONT_PASS) || (i != LBREAK && i != LCONTIN)) { quitenv(); unwind(i); } else if (i == LBREAK) { rv = 0; goto Break; } } rv = 0; /* in case of a continue */ if (t->type == TFOR) { while (*ap != NULL) { setstr(global(t->str), *ap++, KSH_UNWIND_ERROR); rv = execute(t->left, flags & XERROK); } } #ifdef KSH else { /* TSELECT */ for (;;) { if (!(cp = do_selectargs(ap, is_first))) { rv = 1; break; } is_first = FALSE; setstr(global(t->str), cp, KSH_UNWIND_ERROR); rv = execute(t->left, flags & XERROK); } } } #endif /* KSH */ break; case TWHILE: case TUNTIL: e->type = E_LOOP; while (1) { i = ksh_sigsetjmp(e->jbuf, 0); if (!i) break; if ((e->flags&EF_BRKCONT_PASS) || (i != LBREAK && i != LCONTIN)) { quitenv(); unwind(i); } else if (i == LBREAK) { rv = 0; goto Break; } } rv = 0; /* in case of a continue */ while ((execute(t->left, XERROK) == 0) == (t->type == TWHILE)) rv = execute(t->right, flags & XERROK); break; case TIF: case TELIF: if (t->right == NULL) break; /* should be error */ rv = execute(t->left, XERROK) == 0 ? execute(t->right->left, flags & XERROK) : execute(t->right->right, flags & XERROK); break; case TCASE: cp = evalstr(t->str, DOTILDE); for (t = t->left; t != NULL && t->type == TPAT; t = t->right) for (ap = t->vars; *ap; ap++) if ((s = evalstr(*ap, DOTILDE|DOPAT)) && gmatch(cp, s, FALSE)) goto Found; break; Found: rv = execute(t->left, flags & XERROK); break; case TBRACE: rv = execute(t->left, flags & XERROK); break; case TFUNCT: rv = define(t->str, t); break; case TTIME: /* Clear XEXEC so nested execute() call doesn't exit * (allows "ls -l | time grep foo"). */ rv = timex(t, flags & ~XEXEC); break; case TEXEC: /* an eval'd TCOM */ s = t->args[0]; ap = makenv(); #ifndef F_SETFD for (i = 0; i < sizeof(clexec_tab); i++) if (clexec_tab[i]) { close(i); clexec_tab[i] = 0; } #endif restoresigs(); cleanup_proc_env(); /* XINTACT bit is for OS2 */ ksh_execve(t->str, t->args, ap, (flags & XINTACT) ? 1 : 0); if (errno == ENOEXEC) scriptexec(t, ap); else errorf("%s: %s", s, strerror(errno)); } Break: exstat = rv; quitenv(); /* restores IO */ if ((flags&XEXEC)) unwind(LEXIT); /* exit child */ if (rv != 0 && !(flags & XERROK)) { if (Flag(FERREXIT)) unwind(LERROR); trapsig(SIGERR_); } return rv; } /* * execute simple command */ static int comexec(t, tp, ap, flags) struct op *t; struct tbl *volatile tp; register char **ap; int volatile flags; { int i; int rv = 0; register char *cp; register char **lastp; static struct op texec; /* Must be static (XXX but why?) */ int type_flags; int keepasn_ok; int fcflags = FC_BI|FC_FUNC|FC_PATH; #ifdef KSH /* snag the last argument for $_ XXX not the same as at&t ksh, * which only seems to set $_ after a newline (but not in * functions/dot scripts, but in interactive and scipt) - * perhaps save last arg here and set it in shell()?. */ if (Flag(FTALKING) && *(lastp = ap)) { while (*++lastp) ; /* setstr() can't fail here */ setstr(typeset("_", LOCAL, 0, INTEGER, 0), *--lastp, KSH_RETURN_ERROR); } #endif /* KSH */ /* Deal with the shell builtins builtin, exec and command since * they can be followed by other commands. This must be done before * we know if we should create a local block, which must be done * before we can do a path search (in case the assignments change * PATH). * Odd cases: * FOO=bar exec > /dev/null FOO is kept but not exported * FOO=bar exec foobar FOO is exported * FOO=bar command exec > /dev/null FOO is neither kept nor exported * FOO=bar command FOO is neither kept nor exported * PATH=... foobar use new PATH in foobar search */ keepasn_ok = 1; while (tp && tp->type == CSHELL) { fcflags = FC_BI|FC_FUNC|FC_PATH;/* undo effects of command */ if (tp->val.f == c_builtin) { if ((cp = *++ap) == NULL) { tp = NULL; break; } tp = findcom(cp, FC_BI); if (tp == NULL) errorf("builtin: %s: not a builtin", cp); continue; } else if (tp->val.f == c_exec) { if (ap[1] == NULL) break; ap++; flags |= XEXEC; } else if (tp->val.f == c_command) { int optc, saw_p = 0; /* Ugly dealing with options in two places (here and * in c_command(), but such is life) */ ksh_getopt_reset(&builtin_opt, 0); while ((optc = ksh_getopt(ap, &builtin_opt, ":p")) == 'p') saw_p = 1; if (optc != EOF) break; /* command -vV or something */ /* don't look for functions */ fcflags = FC_BI|FC_PATH; if (saw_p) { if (Flag(FRESTRICTED)) { warningf(TRUE, "command -p: restricted"); rv = 1; goto Leave; } fcflags |= FC_DEFPATH; } ap += builtin_opt.optind; /* POSIX says special builtins lose their status * if accessed using command. */ keepasn_ok = 0; if (!ap[0]) { /* ensure command with no args exits with 0 */ subst_exstat = 0; break; } } else break; tp = findcom(ap[0], fcflags & (FC_BI|FC_FUNC)); } if (keepasn_ok && (!ap[0] || (tp && (tp->flag & KEEPASN)))) type_flags = 0; else { /* create new variable/function block */ newblock(); /* ksh functions don't keep assignments, POSIX functions do. */ if (keepasn_ok && tp && tp->type == CFUNC && !(tp->flag & FKSH)) type_flags = 0; else type_flags = LOCAL|LOCAL_COPY|EXPORT; } if (Flag(FEXPORT)) type_flags |= EXPORT; for (i = 0; t->vars[i]; i++) { cp = evalstr(t->vars[i], DOASNTILDE); if (Flag(FXTRACE)) { if (i == 0) shf_fprintf(shl_out, "%s", PS4_SUBSTITUTE(str_val(global("PS4")))); shf_fprintf(shl_out, "%s%s", cp, t->vars[i + 1] ? space : newline); if (!t->vars[i + 1]) shf_flush(shl_out); } typeset(cp, type_flags, 0, 0, 0); } if ((cp = *ap) == NULL) { rv = subst_exstat; goto Leave; } else if (!tp) { if (Flag(FRESTRICTED) && ksh_strchr_dirsep(cp)) { warningf(TRUE, "%s: restricted", cp); rv = 1; goto Leave; } tp = findcom(cp, fcflags); } switch (tp->type) { case CSHELL: /* shell built-in */ rv = call_builtin(tp, ap); break; case CFUNC: /* function call */ { volatile int old_xflag; volatile Tflag old_inuse; const char *volatile old_kshname; if (!(tp->flag & ISSET)) { struct tbl *ftp; if (!tp->u.fpath) { if (tp->u2.errno_) { warningf(TRUE, "%s: can't find function definition file - %s", cp, strerror(tp->u2.errno_)); rv = 126; } else { warningf(TRUE, "%s: can't find function definition file", cp); rv = 127; } break; } if (include(tp->u.fpath, 0, (char **) 0, 0) < 0) { warningf(TRUE, "%s: can't open function definition file %s - %s", cp, tp->u.fpath, strerror(errno)); rv = 127; break; } if (!(ftp = findfunc(cp, hash(cp), FALSE)) || !(ftp->flag & ISSET)) { warningf(TRUE, "%s: function not defined by %s", cp, tp->u.fpath); rv = 127; break; } tp = ftp; } /* ksh functions set $0 to function name, POSIX functions leave * $0 unchanged. */ old_kshname = kshname; if (tp->flag & FKSH) kshname = ap[0]; else ap[0] = (char *) kshname; e->loc->argv = ap; for (i = 0; *ap++ != NULL; i++) ; e->loc->argc = i - 1; /* ksh-style functions handle getopts sanely, * bourne/posix functions are insane... */ if (tp->flag & FKSH) { e->loc->flags |= BF_DOGETOPTS; e->loc->getopts_state = user_opt; getopts_reset(1); } old_xflag = Flag(FXTRACE); Flag(FXTRACE) = tp->flag & TRACE ? TRUE : FALSE; old_inuse = tp->flag & FINUSE; tp->flag |= FINUSE; e->type = E_FUNC; i = ksh_sigsetjmp(e->jbuf, 0); if (i == 0) { /* seems odd to pass XERROK here, but at&t ksh does */ exstat = execute(tp->val.t, flags & XERROK); i = LRETURN; } kshname = old_kshname; Flag(FXTRACE) = old_xflag; tp->flag = (tp->flag & ~FINUSE) | old_inuse; /* Were we deleted while executing? If so, free the execution * tree. todo: Unfortunately, the table entry is never re-used * until the lookup table is expanded. */ if ((tp->flag & (FDELETE|FINUSE)) == FDELETE) { if (tp->flag & ALLOC) { tp->flag &= ~ALLOC; tfree(tp->val.t, tp->areap); } tp->flag = 0; } switch (i) { case LRETURN: case LERROR: rv = exstat; break; case LINTR: case LEXIT: case LLEAVE: case LSHELL: quitenv(); unwind(i); /*NOTREACHED*/ default: quitenv(); internal_errorf(1, "CFUNC %d", i); } break; } case CEXEC: /* executable command */ case CTALIAS: /* tracked alias */ if (!(tp->flag&ISSET)) { /* errno_ will be set if the named command was found * but could not be executed (permissions, no execute * bit, directory, etc). Print out a (hopefully) * useful error message and set the exit status to 126. */ if (tp->u2.errno_) { warningf(TRUE, "%s: cannot execute - %s", cp, strerror(tp->u2.errno_)); rv = 126; /* POSIX */ } else { warningf(TRUE, "%s: not found", cp); rv = 127; } break; } #ifdef KSH /* set $_ to program's full path */ /* setstr() can't fail here */ setstr(typeset("_", LOCAL|EXPORT, 0, INTEGER, 0), tp->val.s, KSH_RETURN_ERROR); #endif /* KSH */ if (flags&XEXEC) { j_exit(); if (!(flags&XBGND) || Flag(FMONITOR)) { setexecsig(&sigtraps[SIGINT], SS_RESTORE_ORIG); setexecsig(&sigtraps[SIGQUIT], SS_RESTORE_ORIG); } } /* to fork we set up a TEXEC node and call execute */ texec.type = TEXEC; texec.left = t; /* for tprint */ texec.str = tp->val.s; texec.args = ap; rv = exchild(&texec, flags, -1); break; } Leave: if (flags & XEXEC) { exstat = rv; unwind(LLEAVE); } return rv; } static void scriptexec(tp, ap) register struct op *tp; register char **ap; { char *shell; shell = str_val(global(EXECSHELL_STR)); if (shell && *shell) shell = search(shell, path, X_OK, (int *) 0); if (!shell || !*shell) shell = EXECSHELL; *tp->args-- = tp->str; #ifdef SHARPBANG { char buf[LINE]; register char *cp; register int fd, n; buf[0] = '\0'; if ((fd = open(tp->str, O_RDONLY)) >= 0) { if ((n = read(fd, buf, LINE - 1)) > 0) buf[n] = '\0'; (void) close(fd); } if ((buf[0] == '#' && buf[1] == '!' && (cp = &buf[2])) # ifdef OS2 || (strncmp(buf, "extproc", 7) == 0 && isspace(buf[7]) && (cp = &buf[7])) # endif /* OS2 */ ) { while (*cp && (*cp == ' ' || *cp == '\t')) cp++; if (*cp && *cp != '\n') { char *a0 = cp, *a1 = (char *) 0; # ifdef OS2 char *a2 = cp; # endif /* OS2 */ while (*cp && *cp != '\n' && *cp != ' ' && *cp != '\t') { # ifdef OS2 /* Allow shell search without prepended path * if shell with / in pathname cannot be found. * Use / explicitly so \ can be used if explicit * needs to be forced. */ if (*cp == '/') a2 = cp + 1; # endif /* OS2 */ cp++; } if (*cp && *cp != '\n') { *cp++ = '\0'; while (*cp && (*cp == ' ' || *cp == '\t')) cp++; if (*cp && *cp != '\n') { a1 = cp; /* all one argument */ while (*cp && *cp != '\n') cp++; } } if (*cp == '\n') { *cp = '\0'; if (a1) *tp->args-- = a1; # ifdef OS2 if (a0 != a2) { char *tmp_a0 = str_nsave(a0, strlen(a0) + 5, ATEMP); if (search_access(tmp_a0, X_OK, (int *) 0)) a0 = a2; afree(tmp_a0, ATEMP); } # endif /* OS2 */ shell = a0; } } # ifdef OS2 } else { /* Use ksh documented shell default if present * else use OS2_SHELL which is assumed to need * the /c option and '\' as dir separater. */ char *p = shell; shell = str_val(global("EXECSHELL")); if (shell && *shell) shell = search(shell, path, X_OK, (int *) 0); if (!shell || !*shell) { shell = p; *tp->args-- = "/c"; for (p = tp->str; *p; p++) if (*p == '/') *p = '\\'; } # endif /* OS2 */ } } #endif /* SHARPBANG */ *tp->args = shell; ksh_execve(tp->args[0], tp->args, ap, 0); /* report both the program that was run and the bogus shell */ errorf("%s: %s: %s", tp->str, shell, strerror(errno)); } int shcomexec(wp) register char **wp; { register struct tbl *tp; tp = tsearch(&builtins, *wp, hash(*wp)); if (tp == NULL) internal_errorf(1, "shcomexec: %s", *wp); return call_builtin(tp, wp); } /* * Search function tables for a function. If create set, a table entry * is created if none is found. */ struct tbl * findfunc(name, h, create) const char *name; unsigned int h; int create; { struct block *l; struct tbl *tp = (struct tbl *) 0; for (l = e->loc; l; l = l->next) { tp = tsearch(&l->funs, name, h); if (tp) break; if (!l->next && create) { tp = tenter(&l->funs, name, h); tp->flag = DEFINED; tp->type = CFUNC; tp->val.t = (struct op *) 0; break; } } return tp; } /* * define function. Returns 1 if function is being undefined (t == 0) and * function did not exist, returns 0 otherwise. */ int define(name, t) const char *name; struct op *t; { struct tbl *tp; int was_set = 0; while (1) { tp = findfunc(name, hash(name), TRUE); if (tp->flag & ISSET) was_set = 1; /* If this function is currently being executed, we zap this * table entry so findfunc() won't see it */ if (tp->flag & FINUSE) { tp->name[0] = '\0'; tp->flag &= ~DEFINED; /* ensure it won't be found */ tp->flag |= FDELETE; } else break; } if (tp->flag & ALLOC) { tp->flag &= ~(ISSET|ALLOC); tfree(tp->val.t, tp->areap); } if (t == NULL) { /* undefine */ tdelete(tp); return was_set ? 0 : 1; } tp->val.t = tcopy(t->left, tp->areap); tp->flag |= (ISSET|ALLOC); if (t->u.ksh_func) tp->flag |= FKSH; return 0; } /* * add builtin */ void builtin(name, func) const char *name; int (*func) ARGS((char **)); { register struct tbl *tp; Tflag flag; /* see if any flags should be set for this builtin */ for (flag = 0; ; name++) { if (*name == '=') /* command does variable assignment */ flag |= KEEPASN; else if (*name == '*') /* POSIX special builtin */ flag |= SPEC_BI; else if (*name == '+') /* POSIX regular builtin */ flag |= REG_BI; else break; } tp = tenter(&builtins, name, hash(name)); tp->flag = DEFINED | flag; tp->type = CSHELL; tp->val.f = func; } /* * find command * either function, hashed command, or built-in (in that order) */ struct tbl * findcom(name, flags) const char *name; int flags; /* FC_* */ { static struct tbl temp; unsigned int h = hash(name); struct tbl *tp = NULL, *tbi; int insert = Flag(FTRACKALL); /* insert if not found */ char *fpath; /* for function autoloading */ char *npath; if (ksh_strchr_dirsep(name) != NULL) { insert = 0; /* prevent FPATH search below */ flags &= ~FC_FUNC; goto Search; } tbi = (flags & FC_BI) ? tsearch(&builtins, name, h) : NULL; /* POSIX says special builtins first, then functions, then * POSIX regular builtins, then search path... */ if ((flags & FC_SPECBI) && tbi && (tbi->flag & SPEC_BI)) tp = tbi; if (!tp && (flags & FC_FUNC)) { tp = findfunc(name, h, FALSE); if (tp && !(tp->flag & ISSET)) { if ((fpath = str_val(global("FPATH"))) == null) { tp->u.fpath = (char *) 0; tp->u2.errno_ = 0; } else tp->u.fpath = search(name, fpath, R_OK, &tp->u2.errno_); } } if (!tp && (flags & FC_REGBI) && tbi && (tbi->flag & REG_BI)) tp = tbi; /* todo: posix says non-special/non-regular builtins must * be triggered by some user-controllable means like a * special directory in PATH. Requires modifications to * the search() function. Tracked aliases should be * modified to allow tracking of builtin commands. * This should be under control of the FPOSIX flag. * If this is changed, also change c_whence... */ if (!tp && (flags & FC_UNREGBI) && tbi) tp = tbi; if (!tp && (flags & FC_PATH) && !(flags & FC_DEFPATH)) { tp = tsearch(&taliases, name, h); if (tp && (tp->flag & ISSET) && eaccess(tp->val.s, X_OK) != 0) { if (tp->flag & ALLOC) { tp->flag &= ~ALLOC; afree(tp->val.s, APERM); } tp->flag &= ~ISSET; } } Search: if ((!tp || (tp->type == CTALIAS && !(tp->flag&ISSET))) && (flags & FC_PATH)) { if (!tp) { if (insert && !(flags & FC_DEFPATH)) { tp = tenter(&taliases, name, h); tp->type = CTALIAS; } else { tp = &temp; tp->type = CEXEC; } tp->flag = DEFINED; /* make ~ISSET */ } npath = search(name, flags & FC_DEFPATH ? def_path : path, X_OK, &tp->u2.errno_); if (npath) { tp->val.s = tp == &temp ? npath : str_save(npath, APERM); tp->flag |= ISSET|ALLOC; } else if ((flags & FC_FUNC) && (fpath = str_val(global("FPATH"))) != null && (npath = search(name, fpath, R_OK, &tp->u2.errno_)) != (char *) 0) { /* An undocumented feature of at&t ksh is that it * searches FPATH if a command is not found, even * if the command hasn't been set up as an autoloaded * function (ie, no typeset -uf). */ tp = &temp; tp->type = CFUNC; tp->flag = DEFINED; /* make ~ISSET */ tp->u.fpath = npath; } } return tp; } /* * flush executable commands with relative paths */ void flushcom(all) int all; /* just relative or all */ { struct tbl *tp; struct tstate ts; for (twalk(&ts, &taliases); (tp = tnext(&ts)) != NULL; ) if ((tp->flag&ISSET) && (all || !ISDIRSEP(tp->val.s[0]))) { if (tp->flag&ALLOC) { tp->flag &= ~(ALLOC|ISSET); afree(tp->val.s, APERM); } tp->flag &= ~ISSET; } } /* Check if path is something we want to find. Returns -1 for failure. */ int search_access(path, mode, errnop) const char *path; int mode; int *errnop; /* set if candidate found, but not suitable */ { #ifndef OS2 int ret, err = 0; struct stat statb; if (stat(path, &statb) < 0) return -1; ret = eaccess(path, mode); if (ret < 0) err = errno; /* File exists, but we can't access it */ else if (mode == X_OK && (!S_ISREG(statb.st_mode) /* This 'cause access() says root can execute everything */ || !(statb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)))) { ret = -1; err = S_ISDIR(statb.st_mode) ? EISDIR : EACCES; } if (err && errnop && !*errnop) *errnop = err; return ret; #else /* !OS2 */ /* * NOTE: ASSUMES path can be modified and has enough room at the * end of the string for a suffix (ie, 4 extra characters). * Certain code knows this (eg, eval.c(globit()), * exec.c(search())). */ static char *xsuffixes[] = { ".ksh", ".exe", ".", ".sh", ".cmd", ".com", ".bat", (char *) 0 }; static char *rsuffixes[] = { ".ksh", ".", ".sh", ".cmd", ".bat", (char *) 0 }; int i; char *mpath = (char *) path; char *tp = mpath + strlen(mpath); char *p; char **sfx; /* If a suffix has been specified, check if it is one of the * suffixes that indicate the file is executable - if so, change * the access test to R_OK... * This code assumes OS/2 files can have only one suffix... */ if ((p = strrchr((p = ksh_strrchr_dirsep(mpath)) ? p : mpath, '.'))) { if (mode == X_OK) mode = R_OK; return search_access1(mpath, mode, errnop); } /* Try appending the various suffixes. Different suffixes for * read and execute 'cause we don't want to read an executable... */ sfx = mode == R_OK ? rsuffixes : xsuffixes; for (i = 0; sfx[i]; i++) { strcpy(tp, p = sfx[i]); if (search_access1(mpath, R_OK, errnop) == 0) return 0; *tp = '\0'; } return -1; #endif /* !OS2 */ } #ifdef OS2 static int search_access1(path, mode, errnop) const char *path; int mode; int *errnop; /* set if candidate found, but not suitable */ { int ret, err = 0; struct stat statb; if (stat(path, &statb) < 0) return -1; ret = eaccess(path, mode); if (ret < 0) err = errno; /* File exists, but we can't access it */ else if (!S_ISREG(statb.st_mode)) { ret = -1; err = S_ISDIR(statb.st_mode) ? EISDIR : EACCES; } if (err && errnop && !*errnop) *errnop = err; return ret; } #endif /* OS2 */ /* * search for command with PATH */ char * search(name, path, mode, errnop) const char *name; const char *path; int mode; /* R_OK or X_OK */ int *errnop; /* set if candidate found, but not suitable */ { const char *sp, *p; char *xp; XString xs; int namelen; if (errnop) *errnop = 0; #ifdef OS2 /* Xinit() allocates 8 additional bytes, so appended suffixes won't * overflow the memory. */ namelen = strlen(name) + 1; Xinit(xs, xp, namelen, ATEMP); memcpy(Xstring(xs, xp), name, namelen); if (ksh_strchr_dirsep(name)) { if (search_access(Xstring(xs, xp), mode, errnop) >= 0) return Xstring(xs, xp); /* not Xclose() - see above */ Xfree(xs, xp); return NULL; } /* Look in current context always. (os2 style) */ if (search_access(Xstring(xs, xp), mode, errnop) == 0) return Xstring(xs, xp); /* not Xclose() - xp may be wrong */ #else /* OS2 */ if (ksh_strchr_dirsep(name)) { if (search_access(name, mode, errnop) == 0) return (char *) name; return NULL; } namelen = strlen(name) + 1; Xinit(xs, xp, 128, ATEMP); #endif /* OS2 */ sp = path; while (sp != NULL) { xp = Xstring(xs, xp); if (!(p = strchr(sp, PATHSEP))) p = sp + strlen(sp); if (p != sp) { XcheckN(xs, xp, p - sp); memcpy(xp, sp, p - sp); xp += p - sp; *xp++ = DIRSEP; } sp = p; XcheckN(xs, xp, namelen); memcpy(xp, name, namelen); if (search_access(Xstring(xs, xp), mode, errnop) == 0) #ifdef OS2 return Xstring(xs, xp); /* Not Xclose() - see above */ #else /* OS2 */ return Xclose(xs, xp + namelen); #endif /* OS2 */ if (*sp++ == '\0') sp = NULL; } Xfree(xs, xp); return NULL; } static int call_builtin(tp, wp) struct tbl *tp; char **wp; { int rv; builtin_argv0 = wp[0]; builtin_flag = tp->flag; shf_reopen(1, SHF_WR, shl_stdout); shl_stdout_ok = 1; ksh_getopt_reset(&builtin_opt, GF_ERROR); rv = (*tp->val.f)(wp); shf_flush(shl_stdout); shl_stdout_ok = 0; builtin_flag = 0; builtin_argv0 = (char *) 0; return rv; } /* * set up redirection, saving old fd's in e->savefd */ static int iosetup(iop, tp) register struct ioword *iop; struct tbl *tp; { register int u = -1; char *cp = iop->name; int iotype = iop->flag & IOTYPE; int do_open = 1, do_close = 0, UNINITIALIZED(flags); struct ioword iotmp; struct stat statb; if (iotype != IOHERE) cp = evalonestr(cp, DOTILDE|(Flag(FTALKING_I) ? DOGLOB : 0)); /* Used for tracing and error messages to print expanded cp */ iotmp = *iop; iotmp.name = (iotype == IOHERE) ? (char *) 0 : cp; iotmp.flag |= IONAMEXP; if (Flag(FXTRACE)) shellf("%s%s\n", PS4_SUBSTITUTE(str_val(global("PS4"))), snptreef((char *) 0, 32, "%R", &iotmp)); switch (iotype) { case IOREAD: flags = O_RDONLY; break; case IOCAT: flags = O_WRONLY | O_APPEND | O_CREAT; break; case IOWRITE: flags = O_WRONLY | O_CREAT | O_TRUNC; /* The stat() is here to allow redirections to * things like /dev/null without error. */ if (Flag(FNOCLOBBER) && !(iop->flag & IOCLOB) && (stat(cp, &statb) < 0 || S_ISREG(statb.st_mode))) flags |= O_EXCL; break; case IORDWR: flags = O_RDWR | O_CREAT; break; case IOHERE: do_open = 0; /* herein() returns -2 if error has been printed */ u = herein(iop->heredoc, iop->flag & IOEVAL); /* cp may have wrong name */ break; case IODUP: { const char *emsg; do_open = 0; if (*cp == '-' && !cp[1]) { u = 1009; /* prevent error return below */ do_close = 1; } else if ((u = check_fd(cp, X_OK | ((iop->flag & IORDUP) ? R_OK : W_OK), &emsg)) < 0) { warningf(TRUE, "%s: %s", snptreef((char *) 0, 32, "%R", &iotmp), emsg); return -1; } break; } } if (do_open) { if (Flag(FRESTRICTED) && (flags & O_CREAT)) { warningf(TRUE, "%s: restricted", cp); return -1; } u = open(cp, flags, 0666); #ifdef OS2 if (u < 0 && strcmp(cp, "/dev/null") == 0) u = open("nul", flags, 0666); #endif /* OS2 */ } if (u < 0) { /* herein() may already have printed message */ if (u == -1) warningf(TRUE, "cannot %s %s: %s", iotype == IODUP ? "dup" : (iotype == IOREAD || iotype == IOHERE) ? "open" : "create", cp, strerror(errno)); return -1; } /* Do not save if it has already been redirected (i.e. "cat >x >y"). */ if (e->savefd[iop->unit] == 0) /* c_exec() assumes e->savefd[fd] set for any redirections. * Ask savefd() not to close iop->unit - allows error messages * to be seen if iop->unit is 2; also means we can't lose * the fd (eg, both dup2 below and dup2 in restfd() failing). */ e->savefd[iop->unit] = savefd(iop->unit, 1); if (do_close) close(iop->unit); else if (u != iop->unit) { if (ksh_dup2(u, iop->unit, TRUE) < 0) { warningf(TRUE, "could not finish (dup) redirection %s: %s", snptreef((char *) 0, 32, "%R", &iotmp), strerror(errno)); if (iotype != IODUP) close(u); return -1; } if (iotype != IODUP) close(u); #ifdef KSH /* Touching any co-process fd in an empty exec * causes the shell to close its copies */ else if (tp && tp->type == CSHELL && tp->val.f == c_exec) { if (iop->flag & IORDUP) /* possible exec <&p */ coproc_read_close(u); else /* possible exec >&p */ coproc_write_close(u); } #endif /* KSH */ } if (u == 2) /* Clear any write errors */ shf_reopen(2, SHF_WR, shl_out); return 0; } /* * open here document temp file. * if unquoted here, expand here temp file into second temp file. */ static int herein(content, sub) const char *content; int sub; { volatile int fd = -1; struct source *s, *volatile osource; struct shf *volatile shf; struct temp *h; int i; /* ksh -c 'cat << EOF' can cause this... */ if (content == (char *) 0) { warningf(TRUE, "here document missing"); return -2; /* special to iosetup(): don't print error */ } /* Create temp file to hold content (done before newenv so temp * doesn't get removed too soon). */ h = maketemp(ATEMP, TT_HEREDOC_EXP, &e->temps); if (!(shf = h->shf) || (fd = open(h->name, O_RDONLY, 0)) < 0) { warningf(TRUE, "can't %s temporary file %s: %s", !shf ? "create" : "open", h->name, strerror(errno)); if (shf) shf_close(shf); return -2 /* special to iosetup(): don't print error */; } osource = source; newenv(E_ERRH); i = ksh_sigsetjmp(e->jbuf, 0); if (i) { source = osource; quitenv(); shf_close(shf); /* after quitenv */ close(fd); return -2; /* special to iosetup(): don't print error */ } if (sub) { /* Do substitutions on the content of heredoc */ s = pushs(SSTRING, ATEMP); s->start = s->str = content; source = s; if (yylex(ONEWORD) != LWORD) internal_errorf(1, "herein: yylex"); source = osource; shf_puts(evalstr(yylval.cp, 0), shf); } else shf_puts(content, shf); quitenv(); if (shf_close(shf) == EOF) { close(fd); warningf(TRUE, "error writing %s: %s", h->name, strerror(errno)); return -2; /* special to iosetup(): don't print error */ } return fd; } #ifdef KSH /* * ksh special - the select command processing section * print the args in column form - assuming that we can */ static char * do_selectargs(ap, print_menu) register char **ap; bool_t print_menu; { static const char *const read_args[] = { "read", "-r", "REPLY", (char *) 0 }; char *s; int i, argct; for (argct = 0; ap[argct]; argct++) ; while (1) { /* Menu is printed if * - this is the first time around the select loop * - the user enters a blank line * - the REPLY parameter is empty */ if (print_menu || !*str_val(global("REPLY"))) pr_menu(ap); shellf("%s", str_val(global("PS3"))); if (call_builtin(findcom("read", FC_BI), (char **) read_args)) return (char *) 0; s = str_val(global("REPLY")); if (*s) { i = atoi(s); return (i >= 1 && i <= argct) ? ap[i - 1] : null; } print_menu = 1; } } struct select_menu_info { char *const *args; int arg_width; int num_width; } info; static char *select_fmt_entry ARGS((void *arg, int i, char *buf, int buflen)); /* format a single select menu item */ static char * select_fmt_entry(arg, i, buf, buflen) void *arg; int i; char *buf; int buflen; { struct select_menu_info *smi = (struct select_menu_info *) arg; shf_snprintf(buf, buflen, "%*d) %s", smi->num_width, i + 1, smi->args[i]); return buf; } /* * print a select style menu */ int pr_menu(ap) char *const *ap; { struct select_menu_info smi; char *const *pp; int nwidth, dwidth; int i, n; /* Width/column calculations were done once and saved, but this * means select can't be used recursively so we re-calculate each * time (could save in a structure that is returned, but its probably * not worth the bother). */ /* * get dimensions of the list */ for (n = 0, nwidth = 0, pp = ap; *pp; n++, pp++) { i = strlen(*pp); nwidth = (i > nwidth) ? i : nwidth; } /* * we will print an index of the form * %d) * in front of each entry * get the max width of this */ for (i = n, dwidth = 1; i >= 10; i /= 10) dwidth++; smi.args = ap; smi.arg_width = nwidth; smi.num_width = dwidth; print_columns(shl_out, n, select_fmt_entry, (void *) &smi, dwidth + nwidth + 2); return n; } #endif /* KSH */ #ifdef KSH /* * [[ ... ]] evaluation routines */ extern const char *const dbtest_tokens[]; extern const char db_close[]; /* Test if the current token is a whatever. Accepts the current token if * it is. Returns 0 if it is not, non-zero if it is (in the case of * TM_UNOP and TM_BINOP, the returned value is a Test_op). */ static int dbteste_isa(te, meta) Test_env *te; Test_meta meta; { int ret = 0; int uqword; char *p; if (!*te->pos.wp) return meta == TM_END; /* unquoted word? */ for (p = *te->pos.wp; *p == CHAR; p += 2) ; uqword = *p == EOS; if (meta == TM_UNOP || meta == TM_BINOP) { if (uqword) { char buf[8]; /* longer than the longest operator */ char *q = buf; for (p = *te->pos.wp; *p == CHAR && q < &buf[sizeof(buf) - 1]; p += 2) *q++ = p[1]; *q = '\0'; ret = (int) test_isop(te, meta, buf); } } else if (meta == TM_END) ret = 0; else ret = uqword && strcmp(*te->pos.wp, dbtest_tokens[(int) meta]) == 0; /* Accept the token? */ if (ret) te->pos.wp++; return ret; } static const char * dbteste_getopnd(te, op, do_eval) Test_env *te; Test_op op; int do_eval; { char *s = *te->pos.wp; if (!s) return (char *) 0; te->pos.wp++; if (!do_eval) return null; if (op == TO_STEQL || op == TO_STNEQ) s = evalstr(s, DOTILDE | DOPAT); else s = evalstr(s, DOTILDE); return s; } static int dbteste_eval(te, op, opnd1, opnd2, do_eval) Test_env *te; Test_op op; const char *opnd1; const char *opnd2; int do_eval; { return test_eval(te, op, opnd1, opnd2, do_eval); } static void dbteste_error(te, offset, msg) Test_env *te; int offset; const char *msg; { te->flags |= TEF_ERROR; internal_errorf(0, "dbteste_error: %s (offset %d)", msg, offset); } #endif /* KSH */ /sys/src/ape/cmd/pdksh/expand.h 664 sys sys 1367613436 2799 /* * Expanding strings */ /* $Id$ */ #define X_EXTRA 8 /* this many extra bytes in X string */ #if 0 /* Usage */ XString xs; char *xp; Xinit(xs, xp, 128, ATEMP); /* allocate initial string */ while ((c = generate()) { Xcheck(xs, xp); /* expand string if neccessary */ Xput(xs, xp, c); /* add character */ } return Xclose(xs, xp); /* resize string */ /* * NOTE: * The Xcheck and Xinit macros have a magic + X_EXTRA in the lengths. * This is so that you can put up to X_EXTRA characters in a XString * before calling Xcheck. (See yylex in lex.c) */ #endif /* 0 */ typedef struct XString { char *end, *beg; /* end, begin of string */ size_t len; /* length */ Area *areap; /* area to allocate/free from */ } XString; typedef char * XStringP; /* initialize expandable string */ #define Xinit(xs, xp, length, area) do { \ (xs).len = length; \ (xs).areap = (area); \ (xs).beg = alloc((xs).len + X_EXTRA, (xs).areap); \ (xs).end = (xs).beg + (xs).len; \ xp = (xs).beg; \ } while (0) /* stuff char into string */ #define Xput(xs, xp, c) (*xp++ = (c)) /* check if there are at least n bytes left */ #define XcheckN(xs, xp, n) do { \ int more = ((xp) + (n)) - (xs).end; \ if (more > 0) \ xp = Xcheck_grow_(&xs, xp, more); \ } while (0) /* check for overflow, expand string */ #define Xcheck(xs, xp) XcheckN(xs, xp, 1) /* free string */ #define Xfree(xs, xp) afree((void*) (xs).beg, (xs).areap) /* close, return string */ #define Xclose(xs, xp) (char*) aresize((void*)(xs).beg, \ (size_t)((xp) - (xs).beg), (xs).areap) /* begin of string */ #define Xstring(xs, xp) ((xs).beg) #define Xnleft(xs, xp) ((xs).end - (xp)) /* may be less than 0 */ #define Xlength(xs, xp) ((xp) - (xs).beg) #define Xsize(xs, xp) ((xs).end - (xs).beg) #define Xsavepos(xs, xp) ((xp) - (xs).beg) #define Xrestpos(xs, xp, n) ((xs).beg + (n)) char * Xcheck_grow_ ARGS((XString *xsp, char *xp, int more)); /* * expandable vector of generic pointers */ typedef struct XPtrV { void **cur; /* next avail pointer */ void **beg, **end; /* begin, end of vector */ } XPtrV; #define XPinit(x, n) do { \ register void **vp__; \ vp__ = (void**) alloc(sizeofN(void*, n), ATEMP); \ (x).cur = (x).beg = vp__; \ (x).end = vp__ + n; \ } while (0) #define XPput(x, p) do { \ if ((x).cur >= (x).end) { \ int n = XPsize(x); \ (x).beg = (void**) aresize((void*) (x).beg, \ sizeofN(void*, n*2), ATEMP); \ (x).cur = (x).beg + n; \ (x).end = (x).cur + n; \ } \ *(x).cur++ = (p); \ } while (0) #define XPptrv(x) ((x).beg) #define XPsize(x) ((x).cur - (x).beg) #define XPclose(x) (void**) aresize((void*)(x).beg, \ sizeofN(void*, XPsize(x)), ATEMP) #define XPfree(x) afree((void*) (x).beg, ATEMP) /sys/src/ape/cmd/pdksh/expr.c 664 sys sys 1367613436 13332 /* * Korn expression evaluation */ /* * todo: better error handling: if in builtin, should be builtin error, etc. */ #include "sh.h" #include /* The order of these enums is constrained by the order of opinfo[] */ enum token { /* some (long) unary operators */ O_PLUSPLUS = 0, O_MINUSMINUS, /* binary operators */ O_EQ, O_NE, /* assignments are assumed to be in range O_ASN .. O_BORASN */ O_ASN, O_TIMESASN, O_DIVASN, O_MODASN, O_PLUSASN, O_MINUSASN, O_LSHIFTASN, O_RSHIFTASN, O_BANDASN, O_BXORASN, O_BORASN, O_LSHIFT, O_RSHIFT, O_LE, O_GE, O_LT, O_GT, O_LAND, O_LOR, O_TIMES, O_DIV, O_MOD, O_PLUS, O_MINUS, O_BAND, O_BXOR, O_BOR, O_TERN, O_COMMA, /* things after this aren't used as binary operators */ /* unary that are not also binaries */ O_BNOT, O_LNOT, /* misc */ OPEN_PAREN, CLOSE_PAREN, CTERN, /* things that don't appear in the opinfo[] table */ VAR, LIT, END, BAD }; #define IS_BINOP(op) (((int)op) >= (int)O_EQ && ((int)op) <= (int)O_COMMA) #define IS_ASSIGNOP(op) ((int)(op) >= (int)O_ASN && (int)(op) <= (int)O_BORASN) enum prec { P_PRIMARY = 0, /* VAR, LIT, (), ~ ! - + */ P_MULT, /* * / % */ P_ADD, /* + - */ P_SHIFT, /* << >> */ P_RELATION, /* < <= > >= */ P_EQUALITY, /* == != */ P_BAND, /* & */ P_BXOR, /* ^ */ P_BOR, /* | */ P_LAND, /* && */ P_LOR, /* || */ P_TERN, /* ?: */ P_ASSIGN, /* = *= /= %= += -= <<= >>= &= ^= |= */ P_COMMA /* , */ }; #define MAX_PREC P_COMMA struct opinfo { char name[4]; int len; /* name length */ enum prec prec; /* precidence: lower is higher */ }; /* Tokens in this table must be ordered so the longest are first * (eg, += before +). If you change something, change the order * of enum token too. */ static const struct opinfo opinfo[] = { { "++", 2, P_PRIMARY }, /* before + */ { "--", 2, P_PRIMARY }, /* before - */ { "==", 2, P_EQUALITY }, /* before = */ { "!=", 2, P_EQUALITY }, /* before ! */ { "=", 1, P_ASSIGN }, /* keep assigns in a block */ { "*=", 2, P_ASSIGN }, { "/=", 2, P_ASSIGN }, { "%=", 2, P_ASSIGN }, { "+=", 2, P_ASSIGN }, { "-=", 2, P_ASSIGN }, { "<<=", 3, P_ASSIGN }, { ">>=", 3, P_ASSIGN }, { "&=", 2, P_ASSIGN }, { "^=", 2, P_ASSIGN }, { "|=", 2, P_ASSIGN }, { "<<", 2, P_SHIFT }, { ">>", 2, P_SHIFT }, { "<=", 2, P_RELATION }, { ">=", 2, P_RELATION }, { "<", 1, P_RELATION }, { ">", 1, P_RELATION }, { "&&", 2, P_LAND }, { "||", 2, P_LOR }, { "*", 1, P_MULT }, { "/", 1, P_MULT }, { "%", 1, P_MULT }, { "+", 1, P_ADD }, { "-", 1, P_ADD }, { "&", 1, P_BAND }, { "^", 1, P_BXOR }, { "|", 1, P_BOR }, { "?", 1, P_TERN }, { ",", 1, P_COMMA }, { "~", 1, P_PRIMARY }, { "!", 1, P_PRIMARY }, { "(", 1, P_PRIMARY }, { ")", 1, P_PRIMARY }, { ":", 1, P_PRIMARY }, { "", 0, P_PRIMARY } /* end of table */ }; typedef struct expr_state Expr_state; struct expr_state { const char *expression; /* expression being evaluated */ const char *tokp; /* lexical position */ enum token tok; /* token from token() */ int noassign; /* don't do assigns (for ?:,&&,||) */ struct tbl *val; /* value from token() */ struct tbl *evaling; /* variable that is being recursively * expanded (EXPRINEVAL flag set) */ }; enum error_type { ET_UNEXPECTED, ET_BADLIT, ET_RECURSIVE, ET_LVALUE, ET_RDONLY, ET_STR }; static void evalerr ARGS((Expr_state *es, enum error_type type, const char *str)) GCC_FUNC_ATTR(noreturn); static struct tbl *evalexpr ARGS((Expr_state *es, enum prec prec)); static void token ARGS((Expr_state *es)); static struct tbl *do_ppmm ARGS((Expr_state *es, enum token op, struct tbl *vasn, bool_t is_prefix)); static void assign_check ARGS((Expr_state *es, enum token op, struct tbl *vasn)); static struct tbl *tempvar ARGS((void)); static struct tbl *intvar ARGS((Expr_state *es, struct tbl *vp)); /* * parse and evalute expression */ int evaluate(expr, rval, error_ok) const char *expr; long *rval; int error_ok; { struct tbl v; int ret; v.flag = DEFINED|INTEGER; v.type = 0; ret = v_evaluate(&v, expr, error_ok); *rval = v.val.i; return ret; } /* * parse and evalute expression, storing result in vp. */ int v_evaluate(vp, expr, error_ok) struct tbl *vp; const char *expr; volatile int error_ok; { struct tbl *v; Expr_state curstate; Expr_state * const es = &curstate; int i; /* save state to allow recursive calls */ curstate.expression = curstate.tokp = expr; curstate.noassign = 0; curstate.evaling = (struct tbl *) 0; newenv(E_ERRH); i = ksh_sigsetjmp(e->jbuf, 0); if (i) { /* Clear EXPRINEVAL in of any variables we were playing with */ if (curstate.evaling) curstate.evaling->flag &= ~EXPRINEVAL; quitenv(); if (i == LAEXPR) { if (error_ok == KSH_RETURN_ERROR) return 0; errorf(null); } unwind(i); /*NOTREACHED*/ } token(es); #if 1 /* ifdef-out to disallow empty expressions to be treated as 0 */ if (es->tok == END) { es->tok = LIT; es->val = tempvar(); } #endif /* 0 */ v = intvar(es, evalexpr(es, MAX_PREC)); if (es->tok != END) evalerr(es, ET_UNEXPECTED, (char *) 0); if (vp->flag & INTEGER) setint_v(vp, v); else /* can fail if readony */ setstr(vp, str_val(v), error_ok); quitenv(); return 1; } static void evalerr(es, type, str) Expr_state *es; enum error_type type; const char *str; { char tbuf[2]; const char *s; switch (type) { case ET_UNEXPECTED: switch (es->tok) { case VAR: s = es->val->name; break; case LIT: s = str_val(es->val); break; case END: s = "end of expression"; break; case BAD: tbuf[0] = *es->tokp; tbuf[1] = '\0'; s = tbuf; break; default: s = opinfo[(int)es->tok].name; } warningf(TRUE, "%s: unexpected `%s'", es->expression, s); break; case ET_BADLIT: warningf(TRUE, "%s: bad number `%s'", es->expression, str); break; case ET_RECURSIVE: warningf(TRUE, "%s: expression recurses on parameter `%s'", es->expression, str); break; case ET_LVALUE: warningf(TRUE, "%s: %s requires lvalue", es->expression, str); break; case ET_RDONLY: warningf(TRUE, "%s: %s applied to read only variable", es->expression, str); break; default: /* keep gcc happy */ case ET_STR: warningf(TRUE, "%s: %s", es->expression, str); break; } unwind(LAEXPR); } static struct tbl * evalexpr(es, prec) Expr_state *es; enum prec prec; { struct tbl *vl, UNINITIALIZED(*vr), *vasn; enum token op; long UNINITIALIZED(res); if (prec == P_PRIMARY) { op = es->tok; if (op == O_BNOT || op == O_LNOT || op == O_MINUS || op == O_PLUS) { token(es); vl = intvar(es, evalexpr(es, P_PRIMARY)); if (op == O_BNOT) vl->val.i = ~vl->val.i; else if (op == O_LNOT) vl->val.i = !vl->val.i; else if (op == O_MINUS) vl->val.i = -vl->val.i; /* op == O_PLUS is a no-op */ } else if (op == OPEN_PAREN) { token(es); vl = evalexpr(es, MAX_PREC); if (es->tok != CLOSE_PAREN) evalerr(es, ET_STR, "missing )"); token(es); } else if (op == O_PLUSPLUS || op == O_MINUSMINUS) { token(es); vl = do_ppmm(es, op, es->val, TRUE); token(es); } else if (op == VAR || op == LIT) { vl = es->val; token(es); } else { evalerr(es, ET_UNEXPECTED, (char *) 0); /*NOTREACHED*/ } if (es->tok == O_PLUSPLUS || es->tok == O_MINUSMINUS) { vl = do_ppmm(es, es->tok, vl, FALSE); token(es); } return vl; } vl = evalexpr(es, ((int) prec) - 1); for (op = es->tok; IS_BINOP(op) && opinfo[(int) op].prec == prec; op = es->tok) { token(es); vasn = vl; if (op != O_ASN) /* vl may not have a value yet */ vl = intvar(es, vl); if (IS_ASSIGNOP(op)) { assign_check(es, op, vasn); vr = intvar(es, evalexpr(es, P_ASSIGN)); } else if (op != O_TERN && op != O_LAND && op != O_LOR) vr = intvar(es, evalexpr(es, ((int) prec) - 1)); if ((op == O_DIV || op == O_MOD || op == O_DIVASN || op == O_MODASN) && vr->val.i == 0) { if (es->noassign) vr->val.i = 1; else evalerr(es, ET_STR, "zero divisor"); } switch ((int) op) { case O_TIMES: case O_TIMESASN: res = vl->val.i * vr->val.i; break; case O_DIV: case O_DIVASN: res = vl->val.i / vr->val.i; break; case O_MOD: case O_MODASN: res = vl->val.i % vr->val.i; break; case O_PLUS: case O_PLUSASN: res = vl->val.i + vr->val.i; break; case O_MINUS: case O_MINUSASN: res = vl->val.i - vr->val.i; break; case O_LSHIFT: case O_LSHIFTASN: res = vl->val.i << vr->val.i; break; case O_RSHIFT: case O_RSHIFTASN: res = vl->val.i >> vr->val.i; break; case O_LT: res = vl->val.i < vr->val.i; break; case O_LE: res = vl->val.i <= vr->val.i; break; case O_GT: res = vl->val.i > vr->val.i; break; case O_GE: res = vl->val.i >= vr->val.i; break; case O_EQ: res = vl->val.i == vr->val.i; break; case O_NE: res = vl->val.i != vr->val.i; break; case O_BAND: case O_BANDASN: res = vl->val.i & vr->val.i; break; case O_BXOR: case O_BXORASN: res = vl->val.i ^ vr->val.i; break; case O_BOR: case O_BORASN: res = vl->val.i | vr->val.i; break; case O_LAND: if (!vl->val.i) es->noassign++; vr = intvar(es, evalexpr(es, ((int) prec) - 1)); res = vl->val.i && vr->val.i; if (!vl->val.i) es->noassign--; break; case O_LOR: if (vl->val.i) es->noassign++; vr = intvar(es, evalexpr(es, ((int) prec) - 1)); res = vl->val.i || vr->val.i; if (vl->val.i) es->noassign--; break; case O_TERN: { int e = vl->val.i != 0; if (!e) es->noassign++; vl = evalexpr(es, MAX_PREC); if (!e) es->noassign--; if (es->tok != CTERN) evalerr(es, ET_STR, "missing :"); token(es); if (e) es->noassign++; vr = evalexpr(es, P_TERN); if (e) es->noassign--; vl = e ? vl : vr; } break; case O_ASN: res = vr->val.i; break; case O_COMMA: res = vr->val.i; break; } if (IS_ASSIGNOP(op)) { vr->val.i = res; if (vasn->flag & INTEGER) setint_v(vasn, vr); else setint(vasn, res); vl = vr; } else if (op != O_TERN) vl->val.i = res; } return vl; } static void token(es) Expr_state *es; { const char *cp; int c; char *tvar; /* skip white space */ for (cp = es->tokp; (c = *cp), isspace(c); cp++) ; es->tokp = cp; if (c == '\0') es->tok = END; else if (letter(c)) { for (; letnum(c); c = *cp) cp++; if (c == '[') { int len; len = array_ref_len(cp); if (len == 0) evalerr(es, ET_STR, "missing ]"); cp += len; } #ifdef KSH else if (c == '(' /*)*/ ) { /* todo: add math functions (all take single argument): * abs acos asin atan cos cosh exp int log sin sinh sqrt * tan tanh */ ; } #endif /* KSH */ if (es->noassign) { es->val = tempvar(); es->val->flag |= EXPRLVALUE; } else { tvar = str_nsave(es->tokp, cp - es->tokp, ATEMP); es->val = global(tvar); afree(tvar, ATEMP); } es->tok = VAR; } else if (digit(c)) { for (; c != '_' && (letnum(c) || c == '#'); c = *cp++) ; tvar = str_nsave(es->tokp, --cp - es->tokp, ATEMP); es->val = tempvar(); es->val->flag &= ~INTEGER; es->val->type = 0; es->val->val.s = tvar; if (setint_v(es->val, es->val) == NULL) evalerr(es, ET_BADLIT, tvar); afree(tvar, ATEMP); es->tok = LIT; } else { int i, n0; for (i = 0; (n0 = opinfo[i].name[0]); i++) if (c == n0 && strncmp(cp, opinfo[i].name, opinfo[i].len) == 0) { es->tok = (enum token) i; cp += opinfo[i].len; break; } if (!n0) es->tok = BAD; } es->tokp = cp; } /* Do a ++ or -- operation */ static struct tbl * do_ppmm(es, op, vasn, is_prefix) Expr_state *es; enum token op; struct tbl *vasn; bool_t is_prefix; { struct tbl *vl; int oval; assign_check(es, op, vasn); vl = intvar(es, vasn); oval = op == O_PLUSPLUS ? vl->val.i++ : vl->val.i--; if (vasn->flag & INTEGER) setint_v(vasn, vl); else setint(vasn, vl->val.i); if (!is_prefix) /* undo the inc/dec */ vl->val.i = oval; return vl; } static void assign_check(es, op, vasn) Expr_state *es; enum token op; struct tbl *vasn; { if (vasn->name[0] == '\0' && !(vasn->flag & EXPRLVALUE)) evalerr(es, ET_LVALUE, opinfo[(int) op].name); else if (vasn->flag & RDONLY) evalerr(es, ET_RDONLY, opinfo[(int) op].name); } static struct tbl * tempvar() { register struct tbl *vp; vp = (struct tbl*) alloc(sizeof(struct tbl), ATEMP); vp->flag = ISSET|INTEGER; vp->type = 0; vp->areap = ATEMP; vp->val.i = 0; vp->name[0] = '\0'; return vp; } /* cast (string) variable to temporary integer variable */ static struct tbl * intvar(es, vp) Expr_state *es; struct tbl *vp; { struct tbl *vq; /* try to avoid replacing a temp var with another temp var */ if (vp->name[0] == '\0' && (vp->flag & (ISSET|INTEGER|EXPRLVALUE)) == (ISSET|INTEGER)) return vp; vq = tempvar(); if (setint_v(vq, vp) == NULL) { if (vp->flag & EXPRINEVAL) evalerr(es, ET_RECURSIVE, vp->name); es->evaling = vp; vp->flag |= EXPRINEVAL; v_evaluate(vq, str_val(vp), KSH_UNWIND_ERROR); vp->flag &= ~EXPRINEVAL; es->evaling = (struct tbl *) 0; } return vq; } /sys/src/ape/cmd/pdksh/history.c 664 sys sys 1367613436 24063 /* * command history * * only implements in-memory history. */ /* * This file contains * a) the original in-memory history mechanism * b) a simple file saving history mechanism done by sjg@zen * define EASY_HISTORY to get this * c) a more complicated mechanism done by pc@hillside.co.uk * that more closely follows the real ksh way of doing * things. You need to have the mmap system call for this * to work on your system */ #include "sh.h" #include "ksh_stat.h" #ifdef HISTORY # ifdef EASY_HISTORY # ifndef HISTFILE # ifdef OS2 # define HISTFILE "history.ksh" # else /* OS2 */ # define HISTFILE ".pdksh_history" # endif /* OS2 */ # endif # else /* Defines and includes for the complicated case */ # include # include /* * variables for handling the data file */ static int histfd; static int hsize; static int hist_count_lines ARGS((unsigned char *, int)); static int hist_shrink ARGS((unsigned char *, int)); static unsigned char *hist_skip_back ARGS((unsigned char *,int *,int)); static void histload ARGS((Source *, unsigned char *, int)); static void histinsert ARGS((Source *, int, unsigned char *)); static void writehistfile ARGS((int, char *)); static int sprinkle ARGS((int)); # ifdef MAP_FILE # define MAP_FLAGS (MAP_FILE|MAP_PRIVATE) # else # define MAP_FLAGS MAP_PRIVATE # endif # endif /* of EASY_HISTORY */ static int hist_execute ARGS((char *cmd)); static int hist_replace ARGS((char **hp, const char *pat, const char *rep, int global)); static char **hist_get ARGS((const char *str, int approx, int allow_cur)); static char **hist_get_newest ARGS((int allow_cur)); static char **hist_get_oldest ARGS(()); static void histbackup ARGS((void)); static char **current; /* current postition in history[] */ static int curpos; /* current index in history[] */ static char *hname; /* current name of history file */ static int hstarted; /* set after hist_init() called */ static Source *hist_source; int c_fc(wp) char **wp; { struct shf *shf; struct temp UNINITIALIZED(*tf); char *p, *editor = (char *) 0; int gflag = 0, lflag = 0, nflag = 0, sflag = 0, rflag = 0; int optc; char *first = (char *) 0, *last = (char *) 0; char **hfirst, **hlast, **hp; while ((optc = ksh_getopt(wp, &builtin_opt, "e:glnrs0,1,2,3,4,5,6,7,8,9,")) != EOF) switch (optc) { case 'e': p = builtin_opt.optarg; if (strcmp(p, "-") == 0) sflag++; else { editor = str_nsave(p, strlen(p) + 4, ATEMP); strcat(editor, " $_"); } break; case 'g': /* non-at&t ksh */ gflag++; break; case 'l': lflag++; break; case 'n': nflag++; break; case 'r': rflag++; break; case 's': /* posix version of -e - */ sflag++; break; /* kludge city - accept -num as -- -num (kind of) */ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': p = shf_smprintf("-%c%s", optc, builtin_opt.optarg); if (!first) first = p; else if (!last) last = p; else { bi_errorf("too many arguments"); return 1; } break; case '?': return 1; } wp += builtin_opt.optind; /* Substitute and execute command */ if (sflag) { char *pat = (char *) 0, *rep = (char *) 0; if (editor || lflag || nflag || rflag) { bi_errorf("can't use -e, -l, -n, -r with -s (-e -)"); return 1; } /* Check for pattern replacement argument */ if (*wp && **wp && (p = strchr(*wp + 1, '='))) { pat = str_save(*wp, ATEMP); p = pat + (p - *wp); *p++ = '\0'; rep = p; wp++; } /* Check for search prefix */ if (!first && (first = *wp)) wp++; if (last || *wp) { bi_errorf("too many arguments"); return 1; } hp = first ? hist_get(first, FALSE, FALSE) : hist_get_newest(FALSE); if (!hp) return 1; return hist_replace(hp, pat, rep, gflag); } if (editor && (lflag || nflag)) { bi_errorf("can't use -l, -n with -e"); return 1; } if (!first && (first = *wp)) wp++; if (!last && (last = *wp)) wp++; if (*wp) { bi_errorf("too many arguments"); return 1; } if (!first) { hfirst = lflag ? hist_get("-16", TRUE, TRUE) : hist_get_newest(FALSE); if (!hfirst) return 1; /* can't fail if hfirst didn't fail */ hlast = hist_get_newest(FALSE); } else { /* POSIX says not an error if first/last out of bounds * when range is specified; at&t ksh and pdksh allow out of * bounds for -l as well. */ hfirst = hist_get(first, (lflag || last) ? TRUE : FALSE, lflag ? TRUE : FALSE); if (!hfirst) return 1; hlast = last ? hist_get(last, TRUE, lflag ? TRUE : FALSE) : (lflag ? hist_get_newest(FALSE) : hfirst); if (!hlast) return 1; } if (hfirst > hlast) { char **temp; temp = hfirst; hfirst = hlast; hlast = temp; rflag = !rflag; /* POSIX */ } /* List history */ if (lflag) { char *s, *t; const char *nfmt = nflag ? "\t" : "%d\t"; for (hp = rflag ? hlast : hfirst; hp >= hfirst && hp <= hlast; hp += rflag ? -1 : 1) { shf_fprintf(shl_stdout, nfmt, hist_source->line - (int) (histptr - hp)); /* print multi-line commands correctly */ for (s = *hp; (t = strchr(s, '\n')); s = t) shf_fprintf(shl_stdout, "%.*s\t", ++t - s, s); shf_fprintf(shl_stdout, "%s\n", s); } shf_flush(shl_stdout); return 0; } /* Run editor on selected lines, then run resulting commands */ tf = maketemp(ATEMP, TT_HIST_EDIT, &e->temps); if (!(shf = tf->shf)) { bi_errorf("cannot create temp file %s - %s", tf->name, strerror(errno)); return 1; } for (hp = rflag ? hlast : hfirst; hp >= hfirst && hp <= hlast; hp += rflag ? -1 : 1) shf_fprintf(shf, "%s\n", *hp); if (shf_close(shf) == EOF) { bi_errorf("error writing temporary file - %s", strerror(errno)); return 1; } /* Ignore setstr errors here (arbitrary) */ setstr(local("_", FALSE), tf->name, KSH_RETURN_ERROR); /* XXX: source should not get trashed by this.. */ { Source *sold = source; int ret; ret = command(editor ? editor : "${FCEDIT:-/bin/ed} $_"); source = sold; if (ret) return ret; } { struct stat statb; XString xs; char *xp; int n; if (!(shf = shf_open(tf->name, O_RDONLY, 0, 0))) { bi_errorf("cannot open temp file %s", tf->name); return 1; } n = fstat(shf_fileno(shf), &statb) < 0 ? 128 : statb.st_size + 1; Xinit(xs, xp, n, hist_source->areap); while ((n = shf_read(xp, Xnleft(xs, xp), shf)) > 0) { xp += n; if (Xnleft(xs, xp) <= 0) XcheckN(xs, xp, Xlength(xs, xp)); } if (n < 0) { bi_errorf("error reading temp file %s - %s", tf->name, strerror(shf_errno(shf))); shf_close(shf); return 1; } shf_close(shf); *xp = '\0'; strip_nuls(Xstring(xs, xp), Xlength(xs, xp)); return hist_execute(Xstring(xs, xp)); } } /* Save cmd in history, execute cmd (cmd gets trashed) */ static int hist_execute(cmd) char *cmd; { Source *sold; int ret; char *p, *q; histbackup(); for (p = cmd; p; p = q) { if ((q = strchr(p, '\n'))) { *q++ = '\0'; /* kill the newline */ if (!*q) /* ignore trailing newline */ q = (char *) 0; } #ifdef EASY_HISTORY if (p != cmd) histappend(p, TRUE); else #endif /* EASY_HISTORY */ histsave(++(hist_source->line), p, 1); shellf("%s\n", p); /* POSIX doesn't say this is done... */ if ((p = q)) /* restore \n (trailing \n not restored) */ q[-1] = '\n'; } /* Commands are executed here instead of pushing them onto the * input 'cause posix says the redirection and variable assignments * in * X=y fc -e - 42 2> /dev/null * are to effect the repeated commands environment. */ /* XXX: source should not get trashed by this.. */ sold = source; ret = command(cmd); source = sold; return ret; } static int hist_replace(hp, pat, rep, global) char **hp; const char *pat; const char *rep; int global; { char *line; if (!pat) line = str_save(*hp, ATEMP); else { char *s, *s1; int pat_len = strlen(pat); int rep_len = strlen(rep); int len; XString xs; char *xp; int any_subst = 0; Xinit(xs, xp, 128, ATEMP); for (s = *hp; (s1 = strstr(s, pat)) && (!any_subst || global) ; s = s1 + pat_len) { any_subst = 1; len = s1 - s; XcheckN(xs, xp, len + rep_len); memcpy(xp, s, len); /* first part */ xp += len; memcpy(xp, rep, rep_len); /* replacement */ xp += rep_len; } if (!any_subst) { bi_errorf("substitution failed"); return 1; } len = strlen(s) + 1; XcheckN(xs, xp, len); memcpy(xp, s, len); xp += len; line = Xclose(xs, xp); } return hist_execute(line); } /* * get pointer to history given pattern * pattern is a number or string */ static char ** hist_get(str, approx, allow_cur) const char *str; int approx; int allow_cur; { char **hp = (char **) 0; int n; if (getn(str, &n)) { hp = histptr + (n < 0 ? n : (n - hist_source->line)); if (hp < history) { if (approx) hp = hist_get_oldest(); else { bi_errorf("%s: not in history", str); hp = (char **) 0; } } else if (hp > histptr) { if (approx) hp = hist_get_newest(allow_cur); else { bi_errorf("%s: not in history", str); hp = (char **) 0; } } else if (!allow_cur && hp == histptr) { bi_errorf("%s: invalid range", str); hp = (char **) 0; } } else { int anchored = *str == '?' ? (++str, 0) : 1; /* the -1 is to avoid the current fc command */ n = findhist(histptr - history - 1, 0, str, anchored); if (n < 0) { bi_errorf("%s: not in history", str); hp = (char **) 0; } else hp = &history[n]; } return hp; } /* Return a pointer to the newest command in the history */ static char ** hist_get_newest(allow_cur) int allow_cur; { if (histptr < history || (!allow_cur && histptr == history)) { bi_errorf("no history (yet)"); return (char **) 0; } if (allow_cur) return histptr; return histptr - 1; } /* Return a pointer to the newest command in the history */ static char ** hist_get_oldest() { if (histptr <= history) { bi_errorf("no history (yet)"); return (char **) 0; } return history; } /******************************/ /* Back up over last histsave */ /******************************/ static void histbackup() { static int last_line = -1; if (histptr >= history && last_line != hist_source->line) { hist_source->line--; afree((void*)*histptr, APERM); histptr--; last_line = hist_source->line; } } /* * Return the current position. */ char ** histpos() { return current; } int histN() { return curpos; } int histnum(n) int n; { int last = histptr - history; if (n < 0 || n >= last) { current = histptr; curpos = last; return last; } else { current = &history[n]; curpos = n; return n; } } /* * This will become unecessary if hist_get is modified to allow * searching from positions other than the end, and in either * direction. */ int findhist(start, fwd, str, anchored) int start; int fwd; const char *str; int anchored; { char **hp; int maxhist = histptr - history; int incr = fwd ? 1 : -1; int len = strlen(str); if (start < 0 || start >= maxhist) start = maxhist; hp = &history[start]; for (; hp >= history && hp <= histptr; hp += incr) if ((anchored && strncmp(*hp, str, len) == 0) || (!anchored && strstr(*hp, str))) return hp - history; return -1; } /* * set history * this means reallocating the dataspace */ void sethistsize(n) int n; { if (n > 0 && n != histsize) { int cursize = histptr - history; /* save most recent history */ if (n < cursize) { memmove(history, histptr - n, n * sizeof(char *)); cursize = n; } history = (char **)aresize(history, n*sizeof(char *), APERM); histsize = n; histptr = history + cursize; } } /* * set history file * This can mean reloading/resetting/starting history file * maintenance */ void sethistfile(name) const char *name; { /* if not started then nothing to do */ if (hstarted == 0) return; /* if the name is the same as the name we have */ if (hname && strcmp(hname, name) == 0) return; /* * its a new name - possibly */ # ifdef EASY_HISTORY if (hname) { afree(hname, APERM); hname = NULL; } # else if (histfd) { /* yes the file is open */ (void) close(histfd); histfd = 0; hsize = 0; afree(hname, APERM); hname = NULL; /* let's reset the history */ histptr = history - 1; hist_source->line = 0; } # endif hist_init(hist_source); } /* * initialise the history vector */ void init_histvec() { if (history == (char **)NULL) { histsize = HISTORYSIZE; history = (char **)alloc(histsize*sizeof (char *), APERM); histptr = history - 1; } } # ifdef EASY_HISTORY /* * save command in history */ void histsave(lno, cmd, dowrite) int lno; /* ignored (compatibility with COMPLEX_HISTORY) */ const char *cmd; int dowrite; /* ignored (compatibility with COMPLEX_HISTORY) */ { register char **hp = histptr; char *cp; if (++hp >= history + histsize) { /* remove oldest command */ afree((void*)history[0], APERM); memmove(history, history + 1, sizeof(history[0]) * (histsize - 1)); hp = &history[histsize - 1]; } *hp = str_save(cmd, APERM); /* trash trailing newline but allow imbedded newlines */ cp = *hp + strlen(*hp); if (cp > *hp && cp[-1] == '\n') cp[-1] = '\0'; histptr = hp; } /* * Append an entry to the last saved command. Used for multiline * commands */ void histappend(cmd, nl_separate) const char *cmd; int nl_separate; { int hlen, clen; char *p; hlen = strlen(*histptr); clen = strlen(cmd); if (clen > 0 && cmd[clen-1] == '\n') clen--; p = *histptr = (char *) aresize(*histptr, hlen + clen + 2, APERM); p += hlen; if (nl_separate) *p++ = '\n'; memcpy(p, cmd, clen); p[clen] = '\0'; } /* * 92-04-25 * A simple history file implementation. * At present we only save the history when we exit. * This can cause problems when there are multiple shells are * running under the same user-id. The last shell to exit gets * to save its history. */ void hist_init(s) Source *s; { char *f; FILE *fh; if (Flag(FTALKING) == 0) return; hstarted = 1; hist_source = s; if ((f = str_val(global("HISTFILE"))) == NULL || *f == '\0') { # if 1 /* Don't use history file unless the user asks for it */ hname = NULL; return; # else char *home = str_val(global("HOME")); int len; if (home == NULL) home = null; f = HISTFILE; hname = alloc(len = strlen(home) + strlen(f) + 2, APERM); shf_snprintf(hname, len, "%s/%s", home, f); # endif } else hname = str_save(f, APERM); if ((fh = fopen(hname, "r"))) { int pos = 0, nread = 0; int contin = 0; /* continuation of previous command */ char *end; char hline[LINE + 1]; while (1) { if (pos >= nread) { pos = 0; nread = fread(hline, 1, LINE, fh); if (nread <= 0) break; hline[nread] = '\0'; } end = strchr(hline + pos, 0); /* will always succeed */ if (contin) histappend(hline + pos, 0); else { hist_source->line++; histsave(0, hline + pos, 0); } pos = end - hline + 1; contin = end == &hline[nread]; } fclose(fh); } } /* * save our history. * We check that we do not have more than we are allowed. * If the history file is read-only we do nothing. * Handy for having all shells start with a useful history set. */ void hist_finish() { static int once; FILE *fh; register int i; register char **hp; if (once++) return; /* check how many we have */ i = histptr - history; if (i >= histsize) hp = &histptr[-histsize]; else hp = history; if (hname && (fh = fopen(hname, "w"))) { for (i = 0; hp + i <= histptr && hp[i]; i++) fprintf(fh, "%s%c", hp[i], '\0'); fclose(fh); } } # else /* EASY_HISTORY */ /* * Routines added by Peter Collinson BSDI(Europe)/Hillside Systems to * a) permit HISTSIZE to control number of lines of history stored * b) maintain a physical history file * * It turns out that there is a lot of ghastly hackery here */ /* * save command in history */ void histsave(lno, cmd, dowrite) int lno; const char *cmd; int dowrite; { register char **hp; char *c, *cp; c = str_save(cmd, APERM); if ((cp = strchr(c, '\n')) != NULL) *cp = '\0'; if (histfd && dowrite) writehistfile(lno, c); hp = histptr; if (++hp >= history + histsize) { /* remove oldest command */ afree((void*)*history, APERM); for (hp = history; hp < history + histsize - 1; hp++) hp[0] = hp[1]; } *hp = c; histptr = hp; } /* * Write history data to a file nominated by HISTFILE * if HISTFILE is unset then history still happens, but * the data is not written to a file * All copies of ksh looking at the file will maintain the * same history. This is ksh behaviour. * * This stuff uses mmap() * if your system ain't got it - then you'll have to undef HISTORYFILE */ /* * Open a history file * Format is: * Bytes 1, 2: HMAGIC - just to check that we are dealing with * the correct object * Then follows a number of stored commands * Each command is * */ # define HMAGIC1 0xab # define HMAGIC2 0xcd # define COMMAND 0xff void hist_init(s) Source *s; { unsigned char *base; int lines; int fd; if (Flag(FTALKING) == 0) return; hstarted = 1; hist_source = s; hname = str_val(global("HISTFILE")); if (hname == NULL) return; hname = str_save(hname, APERM); retry: /* we have a file and are interactive */ if ((fd = open(hname, O_RDWR|O_CREAT|O_APPEND, 0600)) < 0) return; histfd = savefd(fd, 0); (void) flock(histfd, LOCK_EX); hsize = lseek(histfd, 0L, SEEK_END); if (hsize == 0) { /* add magic */ if (sprinkle(histfd)) { hist_finish(); return; } } else if (hsize > 0) { /* * we have some data */ base = (unsigned char *)mmap(0, hsize, PROT_READ, MAP_FLAGS, histfd, 0); /* * check on its validity */ if ((int)base == -1 || *base != HMAGIC1 || base[1] != HMAGIC2) { if ((int)base != -1) munmap((caddr_t)base, hsize); hist_finish(); unlink(hname); goto retry; } if (hsize > 2) { lines = hist_count_lines(base+2, hsize-2); if (lines > histsize) { /* we need to make the file smaller */ if (hist_shrink(base, hsize)) unlink(hname); munmap((caddr_t)base, hsize); hist_finish(); goto retry; } } histload(hist_source, base+2, hsize-2); munmap((caddr_t)base, hsize); } (void) flock(histfd, LOCK_UN); hsize = lseek(histfd, 0L, SEEK_END); } typedef enum state { shdr, /* expecting a header */ sline, /* looking for a null byte to end the line */ sn1, /* bytes 1 to 4 of a line no */ sn2, sn3, sn4, } State; static int hist_count_lines(base, bytes) register unsigned char *base; register int bytes; { State state = shdr; register lines = 0; while (bytes--) { switch (state) { case shdr: if (*base == COMMAND) state = sn1; break; case sn1: state = sn2; break; case sn2: state = sn3; break; case sn3: state = sn4; break; case sn4: state = sline; break; case sline: if (*base == '\0') lines++, state = shdr; } base++; } return lines; } /* * Shrink the history file to histsize lines */ static int hist_shrink(oldbase, oldbytes) unsigned char *oldbase; int oldbytes; { int fd; char nfile[1024]; struct stat statb; unsigned char *nbase = oldbase; int nbytes = oldbytes; nbase = hist_skip_back(nbase, &nbytes, histsize); if (nbase == NULL) return 1; if (nbase == oldbase) return 0; /* * create temp file */ (void) shf_snprintf(nfile, sizeof(nfile), "%s.%d", hname, procpid); if ((fd = creat(nfile, 0600)) < 0) return 1; if (sprinkle(fd)) { close(fd); unlink(nfile); return 1; } if (write(fd, nbase, nbytes) != nbytes) { close(fd); unlink(nfile); return 1; } /* * worry about who owns this file */ if (fstat(histfd, &statb) >= 0) fchown(fd, statb.st_uid, statb.st_gid); close(fd); /* * rename */ if (rename(nfile, hname) < 0) return 1; return 0; } /* * find a pointer to the data `no' back from the end of the file * return the pointer and the number of bytes left */ static unsigned char * hist_skip_back(base, bytes, no) unsigned char *base; int *bytes; int no; { register int lines = 0; register unsigned char *ep; for (ep = base + *bytes; --ep > base; ) { /* this doesn't really work: the 4 byte line number that is * encoded after the COMMAND byte can itself contain the * COMMAND byte.... */ for (; ep > base && *ep != COMMAND; ep--) ; if (ep == base) break; if (++lines == no) { *bytes = *bytes - ((char *)ep - (char *)base); return ep; } } return NULL; } /* * load the history structure from the stored data */ static void histload(s, base, bytes) Source *s; register unsigned char *base; register int bytes; { State state; int lno; unsigned char *line; for (state = shdr; bytes-- > 0; base++) { switch (state) { case shdr: if (*base == COMMAND) state = sn1; break; case sn1: lno = (((*base)&0xff)<<24); state = sn2; break; case sn2: lno |= (((*base)&0xff)<<16); state = sn3; break; case sn3: lno |= (((*base)&0xff)<<8); state = sn4; break; case sn4: lno |= (*base)&0xff; line = base+1; state = sline; break; case sline: if (*base == '\0') { /* worry about line numbers */ if (histptr >= history && lno-1 != s->line) { /* a replacement ? */ histinsert(s, lno, line); } else { s->line = lno; histsave(lno, (char *)line, 0); } state = shdr; } } } } /* * Insert a line into the history at a specified number */ static void histinsert(s, lno, line) Source *s; int lno; unsigned char *line; { register char **hp; if (lno >= s->line-(histptr-history) && lno <= s->line) { hp = &histptr[lno-s->line]; if (*hp) afree((void*)*hp, APERM); *hp = str_save((char *)line, APERM); } } /* * write a command to the end of the history file * This *MAY* seem easy but it's also necessary to check * that the history file has not changed in size. * If it has - then some other shell has written to it * and we should read those commands to update our history */ static void writehistfile(lno, cmd) int lno; char *cmd; { int sizenow; unsigned char *base; unsigned char *new; int bytes; char hdr[5]; (void) flock(histfd, LOCK_EX); sizenow = lseek(histfd, 0L, SEEK_END); if (sizenow != hsize) { /* * Things have changed */ if (sizenow > hsize) { /* someone has added some lines */ bytes = sizenow - hsize; base = (unsigned char *)mmap(0, sizenow, PROT_READ, MAP_FLAGS, histfd, 0); if ((int)base == -1) goto bad; new = base + hsize; if (*new != COMMAND) { munmap((caddr_t)base, sizenow); goto bad; } hist_source->line--; histload(hist_source, new, bytes); hist_source->line++; lno = hist_source->line; munmap((caddr_t)base, sizenow); hsize = sizenow; } else { /* it has shrunk */ /* but to what? */ /* we'll give up for now */ goto bad; } } /* * we can write our bit now */ hdr[0] = COMMAND; hdr[1] = (lno>>24)&0xff; hdr[2] = (lno>>16)&0xff; hdr[3] = (lno>>8)&0xff; hdr[4] = lno&0xff; (void) write(histfd, hdr, 5); (void) write(histfd, cmd, strlen(cmd)+1); hsize = lseek(histfd, 0L, SEEK_END); (void) flock(histfd, LOCK_UN); return; bad: hist_finish(); } void hist_finish() { (void) flock(histfd, LOCK_UN); (void) close(histfd); histfd = 0; } /* * add magic to the history file */ static int sprinkle(fd) int fd; { static char mag[] = { HMAGIC1, HMAGIC2 }; return(write(fd, mag, 2) != 2); } # endif #else /* HISTORY */ /* No history to be compiled in: dummy routines to avoid lots more ifdefs */ void init_histvec() { } void hist_init(s) Source *s; { } void hist_finish() { } void histsave(lno, cmd, dowrite) int lno; const char *cmd; int dowrite; { errorf("history not enabled"); } #endif /* HISTORY */ /sys/src/ape/cmd/pdksh/io.c 664 sys sys 1367613436 10758 /* * shell buffered IO and formatted output */ #include #include "sh.h" #include "ksh_stat.h" static int initio_done; /* * formatted output functions */ /* A shell error occured (eg, syntax error, etc.) */ void #ifdef HAVE_PROTOTYPES errorf(const char *fmt, ...) #else errorf(fmt, va_alist) const char *fmt; va_dcl #endif { va_list va; shl_stdout_ok = 0; /* debugging: note that stdout not valid */ exstat = 1; if (*fmt) { error_prefix(TRUE); SH_VA_START(va, fmt); shf_vfprintf(shl_out, fmt, va); va_end(va); shf_putchar('\n', shl_out); } shf_flush(shl_out); unwind(LERROR); } /* like errorf(), but no unwind is done */ void #ifdef HAVE_PROTOTYPES warningf(int fileline, const char *fmt, ...) #else warningf(fileline, fmt, va_alist) int fileline; const char *fmt; va_dcl #endif { va_list va; error_prefix(fileline); SH_VA_START(va, fmt); shf_vfprintf(shl_out, fmt, va); va_end(va); shf_putchar('\n', shl_out); shf_flush(shl_out); } /* Used by built-in utilities to prefix shell and utility name to message * (also unwinds environments for special builtins). */ void #ifdef HAVE_PROTOTYPES bi_errorf(const char *fmt, ...) #else bi_errorf(fmt, va_alist) const char *fmt; va_dcl #endif { va_list va; shl_stdout_ok = 0; /* debugging: note that stdout not valid */ exstat = 1; if (*fmt) { error_prefix(TRUE); /* not set when main() calls parse_args() */ if (builtin_argv0) shf_fprintf(shl_out, "%s: ", builtin_argv0); SH_VA_START(va, fmt); shf_vfprintf(shl_out, fmt, va); va_end(va); shf_putchar('\n', shl_out); } shf_flush(shl_out); /* POSIX special builtins and ksh special builtins cause * non-interactive shells to exit. * XXX odd use of KEEPASN; also may not want LERROR here */ if ((builtin_flag & SPEC_BI) || (Flag(FPOSIX) && (builtin_flag & KEEPASN))) { builtin_argv0 = (char *) 0; unwind(LERROR); } } /* Called when something that shouldn't happen does */ void #ifdef HAVE_PROTOTYPES internal_errorf(int jump, const char *fmt, ...) #else internal_errorf(jump, fmt, va_alist) int jump; const char *fmt; va_dcl #endif { va_list va; error_prefix(TRUE); shf_fprintf(shl_out, "internal error: "); SH_VA_START(va, fmt); shf_vfprintf(shl_out, fmt, va); va_end(va); shf_putchar('\n', shl_out); shf_flush(shl_out); if (jump) unwind(LERROR); } /* used by error reporting functions to print "ksh: .kshrc[25]: " */ void error_prefix(fileline) int fileline; { /* Avoid foo: foo[2]: ... */ if (!fileline || !source || !source->file || strcmp(source->file, kshname) != 0) shf_fprintf(shl_out, "%s: ", kshname + (*kshname == '-')); if (fileline && source && source->file != NULL) { shf_fprintf(shl_out, "%s[%d]: ", source->file, source->errline > 0 ? source->errline : source->line); source->errline = 0; } } /* printf to shl_out (stderr) with flush */ void #ifdef HAVE_PROTOTYPES shellf(const char *fmt, ...) #else shellf(fmt, va_alist) const char *fmt; va_dcl #endif { va_list va; if (!initio_done) /* shl_out may not be set up yet... */ return; SH_VA_START(va, fmt); shf_vfprintf(shl_out, fmt, va); va_end(va); shf_flush(shl_out); } /* printf to shl_stdout (stdout) */ void #ifdef HAVE_PROTOTYPES shprintf(const char *fmt, ...) #else shprintf(fmt, va_alist) const char *fmt; va_dcl #endif { va_list va; if (!shl_stdout_ok) internal_errorf(1, "shl_stdout not valid"); SH_VA_START(va, fmt); shf_vfprintf(shl_stdout, fmt, va); va_end(va); } #ifdef KSH_DEBUG static struct shf *kshdebug_shf; void kshdebug_init_() { if (kshdebug_shf) shf_close(kshdebug_shf); kshdebug_shf = shf_open("/tmp/ksh-debug.log", O_WRONLY|O_APPEND|O_CREAT, 0600, SHF_WR|SHF_MAPHI); if (kshdebug_shf) { shf_fprintf(kshdebug_shf, "\nNew shell[pid %d]\n", getpid()); shf_flush(kshdebug_shf); } } /* print to debugging log */ void # ifdef HAVE_PROTOTYPES kshdebug_printf_(const char *fmt, ...) # else kshdebug_printf_(fmt, va_alist) const char *fmt; va_dcl # endif { va_list va; if (!kshdebug_shf) return; SH_VA_START(va, fmt); shf_fprintf(kshdebug_shf, "[%d] ", getpid()); shf_vfprintf(kshdebug_shf, fmt, va); va_end(va); shf_flush(kshdebug_shf); } void kshdebug_dump_(str, mem, nbytes) const char *str; const void *mem; int nbytes; { int i, j; int nprow = 16; if (!kshdebug_shf) return; shf_fprintf(kshdebug_shf, "[%d] %s:\n", getpid(), str); for (i = 0; i < nbytes; i += nprow) { char c = '\t'; for (j = 0; j < nprow && i + j < nbytes; j++) { shf_fprintf(kshdebug_shf, "%c%02x", c, ((const unsigned char *) mem)[i + j]); c = ' '; } shf_fprintf(kshdebug_shf, "\n"); } shf_flush(kshdebug_shf); } #endif /* KSH_DEBUG */ /* test if we can seek backwards fd (returns 0 or SHF_UNBUF) */ int can_seek(fd) int fd; { struct stat statb; return fstat(fd, &statb) == 0 && !S_ISREG(statb.st_mode) ? SHF_UNBUF : 0; } struct shf shf_iob[3]; void initio() { shf_fdopen(1, SHF_WR, shl_stdout); /* force buffer allocation */ shf_fdopen(2, SHF_WR, shl_out); shf_fdopen(2, SHF_WR, shl_spare); /* force buffer allocation */ initio_done = 1; kshdebug_init(); } /* A dup2() with error checking */ int ksh_dup2(ofd, nfd, errok) int ofd; int nfd; int errok; { int ret = dup2(ofd, nfd); if (ret < 0 && errno != EBADF && !errok) errorf("too many files open in shell"); #ifdef DUP2_BROKEN /* Ultrix systems like to preserve the close-on-exec flag */ if (ret >= 0) (void) fcntl(nfd, F_SETFD, 0); #endif /* DUP2_BROKEN */ return ret; } /* * move fd from user space (0<=fd<10) to shell space (fd>=10), * set close-on-exec flag. */ int savefd(fd, noclose) int fd; int noclose; { int nfd; if (fd < FDBASE) { nfd = ksh_dupbase(fd, FDBASE); if (nfd < 0) if (errno == EBADF) return -1; else errorf("too many files open in shell"); if (!noclose) close(fd); } else nfd = fd; fd_clexec(nfd); return nfd; } void restfd(fd, ofd) int fd, ofd; { if (fd == 2) shf_flush(&shf_iob[fd]); if (ofd < 0) /* original fd closed */ close(fd); else { ksh_dup2(ofd, fd, TRUE); /* XXX: what to do if this fails? */ close(ofd); } } void openpipe(pv) register int *pv; { if (pipe(pv) < 0) errorf("can't create pipe - try again"); pv[0] = savefd(pv[0], 0); pv[1] = savefd(pv[1], 0); } void closepipe(pv) register int *pv; { close(pv[0]); close(pv[1]); } /* Called by iosetup() (deals with 2>&4, etc.), c_read, c_print to turn * a string (the X in 2>&X, read -uX, print -uX) into a file descriptor. */ int check_fd(name, mode, emsgp) char *name; int mode; const char **emsgp; { int fd, fl; if (isdigit(name[0]) && !name[1]) { fd = name[0] - '0'; if ((fl = fcntl(fd = name[0] - '0', F_GETFL, 0)) < 0) { if (emsgp) *emsgp = "bad file descriptor"; return -1; } fl &= O_ACCMODE; #ifdef OS2 if (mode == W_OK ) { if (setmode(fd, O_TEXT) == -1) { if (emsgp) *emsgp = "couldn't set write mode"; return -1; } } else if (mode == R_OK) if (setmode(fd, O_BINARY) == -1) { if (emsgp) *emsgp = "couldn't set read mode"; return -1; } #else /* OS2 */ /* X_OK is a kludge to disable this check for dups (x<&1): * historical shells never did this check (XXX don't know what * posix has to say). */ if (!(mode & X_OK) && fl != O_RDWR && (((mode & R_OK) && fl != O_RDONLY) || ((mode & W_OK) && fl != O_WRONLY))) { if (emsgp) *emsgp = (fl == O_WRONLY) ? "fd not open for reading" : "fd not open for writing"; return -1; } #endif /* OS2 */ return fd; } #ifdef KSH else if (name[0] == 'p' && !name[1]) return coproc_getfd(mode, emsgp); #endif /* KSH */ if (emsgp) *emsgp = "illegal file descriptor name"; return -1; } #ifdef KSH /* Called once from main */ void coproc_init() { coproc.read = coproc.readw = coproc.write = -1; coproc.njobs = 0; coproc.id = 0; } /* Called by c_read() when eof is read - close fd if it is the co-process fd */ void coproc_read_close(fd) int fd; { if (coproc.read >= 0 && fd == coproc.read) { coproc_readw_close(fd); close(coproc.read); coproc.read = -1; } } /* Called by c_read() and by iosetup() to close the other side of the * read pipe, so reads will actually terminate. */ void coproc_readw_close(fd) int fd; { if (coproc.readw >= 0 && coproc.read >= 0 && fd == coproc.read) { close(coproc.readw); coproc.readw = -1; } } /* Called by c_print when a write to a fd fails with EPIPE and by iosetup * when co-process input is dup'd */ void coproc_write_close(fd) int fd; { if (coproc.write >= 0 && fd == coproc.write) { close(coproc.write); coproc.write = -1; } } /* Called to check for existance of/value of the co-process file descriptor. * (Used by check_fd() and by c_read/c_print to deal with -p option). */ int coproc_getfd(mode, emsgp) int mode; const char **emsgp; { int fd = (mode & R_OK) ? coproc.read : coproc.write; if (fd >= 0) return fd; if (emsgp) *emsgp = "no coprocess"; return -1; } /* called to close file descriptors related to the coprocess (if any) * Should be called with SIGCHLD blocked. */ void coproc_cleanup(reuse) int reuse; { /* This to allow co-processes to share output pipe */ if (!reuse || coproc.readw < 0 || coproc.read < 0) { if (coproc.read >= 0) { close(coproc.read); coproc.read = -1; } if (coproc.readw >= 0) { close(coproc.readw); coproc.readw = -1; } } if (coproc.write >= 0) { close(coproc.write); coproc.write = -1; } } #endif /* KSH */ /* * temporary files */ struct temp * maketemp(ap, type, tlist) Area *ap; Temp_type type; struct temp **tlist; { static unsigned int inc; struct temp *tp; int len; int fd; char *path; const char *dir; dir = tmpdir ? tmpdir : "/tmp"; /* The 20 + 20 is a paranoid worst case for pid/inc */ len = strlen(dir) + 3 + 20 + 20 + 1; tp = (struct temp *) alloc(sizeof(struct temp) + len, ap); tp->name = path = (char *) &tp[1]; tp->shf = (struct shf *) 0; tp->type = type; while (1) { /* Note that temp files need to fit 8.3 DOS limits */ shf_snprintf(path, len, "%s/sh%05u.%03x", dir, (unsigned) procpid, inc++); /* Mode 0600 to be paranoid, O_TRUNC in case O_EXCL isn't * really there. */ fd = open(path, O_RDWR|O_CREAT|O_EXCL|O_TRUNC, 0600); if (fd >= 0) { tp->shf = shf_fdopen(fd, SHF_WR, (struct shf *) 0); break; } if (errno != EINTR #ifdef EEXIST && errno != EEXIST #endif /* EEXIST */ #ifdef EISDIR && errno != EISDIR #endif /* EISDIR */ ) /* Error must be printed by caller: don't know here if * errorf() or bi_errorf() should be used. */ break; } tp->next = NULL; tp->pid = procpid; tp->next = *tlist; *tlist = tp; return tp; } /sys/src/ape/cmd/pdksh/jobs.c 664 sys sys 1367613436 45491 /* * Process and job control */ /* * Reworked/Rewritten version of Eric Gisin's/Ron Natalie's code by * Larry Bouzane (larry@cs.mun.ca) and hacked again by * Michael Rendell (michael@cs.mun.ca) * * The interface to the rest of the shell should probably be changed * to allow use of vfork() when available but that would be way too much * work :) * * Notes regarding the copious ifdefs: * - JOB_SIGS is independent of JOBS - it is defined if there are modern * signal and wait routines available. This is prefered, even when * JOBS is not defined, since the shell will not otherwise notice when * background jobs die until the shell waits for a foreground process * to die. * - TTY_PGRP defined iff JOBS is defined - defined if there are tty * process groups * - NEED_PGRP_SYNC defined iff JOBS is defined - see comment below */ #include "sh.h" #include "ksh_stat.h" #include "ksh_wait.h" #include "ksh_times.h" #include "tty.h" /* Start of system configuration stuff */ /* We keep CHILD_MAX zombie processes around (exact value isn't critical) */ #ifndef CHILD_MAX # if defined(HAVE_SYSCONF) && defined(_SC_CHILD_MAX) # define CHILD_MAX sysconf(_SC_CHILD_MAX) # else /* _SC_CHILD_MAX */ # ifdef _POSIX_CHILD_MAX # define CHILD_MAX ((_POSIX_CHILD_MAX) * 2) # else /* _POSIX_CHILD_MAX */ # define CHILD_MAX 20 # endif /* _POSIX_CHILD_MAX */ # endif /* _SC_CHILD_MAX */ #endif /* !CHILD_MAX */ #ifdef JOBS # if defined(HAVE_TCSETPGRP) || defined(TIOCSPGRP) # define TTY_PGRP # endif # ifdef BSD_PGRP # define setpgid setpgrp # define getpgID() getpgrp(0) # else # define getpgID() getpgrp() # endif # if defined(TTY_PGRP) && !defined(HAVE_TCSETPGRP) int tcsetpgrp ARGS((int fd, pid_t grp)); int tcgetpgrp ARGS((int fd)); int tcsetpgrp(fd, grp) int fd; pid_t grp; { return ioctl(fd, TIOCSPGRP, &grp); } int tcgetpgrp(fd) int fd; { int r, grp; if ((r = ioctl(fd, TIOCGPGRP, &grp)) < 0) return r; return grp; } # endif /* !HAVE_TCSETPGRP && TIOCSPGRP */ #else /* JOBS */ /* These so we can use ifdef xxx instead of if defined(JOBS) && defined(xxx) */ # undef TTY_PGRP # undef NEED_PGRP_SYNC #endif /* JOBS */ /* End of system configuration stuff */ /* Order important! */ #define PRUNNING 0 #define PEXITED 1 #define PSIGNALLED 2 #define PSTOPPED 3 typedef struct proc Proc; struct proc { Proc *next; /* next process in pipeline (if any) */ int state; WAIT_T status; /* wait status */ pid_t pid; /* process id */ char command[48]; /* process command string */ }; /* Notify/print flag - j_print() argument */ #define JP_NONE 0 /* don't print anything */ #define JP_SHORT 1 /* print signals processes were killed by */ #define JP_MEDIUM 2 /* print [job-num] -/+ command */ #define JP_LONG 3 /* print [job-num] -/+ pid command */ #define JP_PGRP 4 /* print pgrp */ /* put_job() flags */ #define PJ_ON_FRONT 0 /* at very front */ #define PJ_PAST_STOPPED 1 /* just past any stopped jobs */ /* Job.flags values */ #define JF_STARTED 0x001 /* set when all processes in job are started */ #define JF_WAITING 0x002 /* set if j_waitj() is waiting on job */ #define JF_W_ASYNCNOTIFY 0x004 /* set if waiting and async notification ok */ #define JF_XXCOM 0x008 /* set for `command` jobs */ #define JF_FG 0x010 /* running in foreground (also has tty pgrp) */ #define JF_SAVEDTTY 0x020 /* j->ttystate is valid */ #define JF_CHANGED 0x040 /* process has changed state */ #define JF_KNOWN 0x080 /* $! referenced */ #define JF_ZOMBIE 0x100 /* known, unwaited process */ #define JF_REMOVE 0x200 /* flaged for removal (j_jobs()/j_noityf()) */ #define JF_USETTYMODE 0x400 /* tty mode saved if process exits normally */ #define JF_SAVEDTTYPGRP 0x800 /* j->saved_ttypgrp is valid */ typedef struct job Job; struct job { Job *next; /* next job in list */ int job; /* job number: %n */ int flags; /* see JF_* */ int state; /* job state */ int status; /* exit status of last process */ pid_t pgrp; /* process group of job */ pid_t ppid; /* pid of process that forked job */ INT32 age; /* number of jobs started */ clock_t systime; /* system time used by job */ clock_t usrtime; /* user time used by job */ Proc *proc_list; /* process list */ Proc *last_proc; /* last process in list */ #ifdef KSH Coproc_id coproc_id; /* 0 or id of coprocess output pipe */ #endif /* KSH */ #ifdef TTY_PGRP TTY_state ttystate; /* saved tty state for stopped jobs */ pid_t saved_ttypgrp; /* saved tty process group for stopped jobs */ #endif /* TTY_PGRP */ }; /* Flags for j_waitj() */ #define JW_NONE 0x00 #define JW_INTERRUPT 0x01 /* ^C will stop the wait */ #define JW_ASYNCNOTIFY 0x02 /* asynchronous notification during wait ok */ #define JW_STOPPEDWAIT 0x04 /* wait even if job stopped */ /* Error codes for j_lookup() */ #define JL_OK 0 #define JL_NOSUCH 1 /* no such job */ #define JL_AMBIG 2 /* %foo or %?foo is ambiguous */ #define JL_INVALID 3 /* non-pid, non-% job id */ static const char *const lookup_msgs[] = { null, "no such job", "ambiguous", "argument must be %job or process id", (char *) 0 }; clock_t j_systime, j_usrtime; /* user and system time of last j_waitjed job */ static Job *job_list; /* job list */ static Job *last_job; static Job *async_job; static pid_t async_pid; static int nzombie; /* # of zombies owned by this process */ static INT32 njobs; /* # of jobs started */ static int child_max; /* CHILD_MAX */ #ifdef JOB_SIGS /* held_sigchld is set if sigchld occurs before a job is completely started */ static int held_sigchld; #endif /* JOB_SIGS */ #ifdef JOBS static struct shf *shl_j; #endif /* JOBS */ #ifdef NEED_PGRP_SYNC /* On some systems, the kernel doesn't count zombie processes when checking * if a process group is valid, which can cause problems in creating the * pipeline "cmd1 | cmd2": if cmd1 can die (and go into the zombie state) * before cmd2 is started, the kernel doesn't allow the setpgid() for cmd2 * to succeed. Solution is to create a pipe between the parent and the first * process; the first process doesn't do anything until the pipe is closed * and the parent doesn't close the pipe until all the processes are started. */ static int j_sync_pipe[2]; static int j_sync_open; #endif /* NEED_PGRP_SYNC */ #ifdef TTY_PGRP static int ttypgrp_ok; /* set if can use tty pgrps */ static pid_t restore_ttypgrp = -1; static pid_t our_pgrp; static int const tt_sigs[] = { SIGTSTP, SIGTTIN, SIGTTOU }; #endif /* TTY_PGRP */ static void j_set_async ARGS((Job *j)); static void j_startjob ARGS((Job *j)); static int j_waitj ARGS((Job *j, int flags, const char *where)); static RETSIGTYPE j_sigchld ARGS((int sig)); static void j_print ARGS((Job *j, int how, struct shf *shf)); static Job *j_lookup ARGS((const char *cp, int *ecodep)); static Job *new_job ARGS((void)); static Proc *new_proc ARGS((void)); static void check_job ARGS((Job *j)); static void put_job ARGS((Job *j, int where)); static void remove_job ARGS((Job *j, const char *where)); static void kill_job ARGS((Job *j)); static void fill_command ARGS((char *c, int len, struct op *t)); /* initialize job control */ void j_init(mflagset) int mflagset; { child_max = CHILD_MAX; /* so syscon() isn't always being called */ #ifdef JOB_SIGS sigemptyset(&sm_default); sigprocmask(SIG_SETMASK, &sm_default, (sigset_t *) 0); sigemptyset(&sm_sigchld); sigaddset(&sm_sigchld, SIGCHLD); setsig(&sigtraps[SIGCHLD], j_sigchld, SS_RESTORE_ORIG|SS_FORCE|SS_SHTRAP); #else /* JOB_SIGS */ /* Make sure SIGCHLD isn't ignored - can do odd things under SYSV */ setsig(&sigtraps[SIGCHLD], SIG_DFL, SS_RESTORE_ORIG|SS_FORCE); #endif /* JOB_SIGS */ #ifdef JOBS if (!mflagset && Flag(FTALKING)) Flag(FMONITOR) = 1; /* shl_j is used to do asynchronous notification (used in * an interrupt handler, so need a distinct shf) */ shl_j = shf_fdopen(2, SHF_WR, (struct shf *) 0); # ifdef TTY_PGRP if (Flag(FMONITOR) || Flag(FTALKING)) { int i; /* the TF_SHELL_USES test is a kludge that lets us know if * if the signals have been changed by the shell. */ for (i = NELEM(tt_sigs); --i >= 0; ) { sigtraps[tt_sigs[i]].flags |= TF_SHELL_USES; /* j_change() sets this to SS_RESTORE_DFL if FMONITOR */ setsig(&sigtraps[tt_sigs[i]], SIG_IGN, SS_RESTORE_IGN|SS_FORCE); } } # endif /* TTY_PGRP */ /* j_change() calls tty_init() */ if (Flag(FMONITOR)) j_change(); else #endif /* JOBS */ if (Flag(FTALKING)) tty_init(TRUE); } /* job cleanup before shell exit */ void j_exit() { /* kill stopped, and possibly running, jobs */ Job *j; int killed = 0; for (j = job_list; j != (Job *) 0; j = j->next) { if (j->ppid == procpid && (j->state == PSTOPPED || (j->state == PRUNNING && ((j->flags & JF_FG) || (Flag(FLOGIN) && !Flag(FNOHUP) && procpid == kshpid))))) { killed = 1; killpg(j->pgrp, SIGHUP); #ifdef JOBS if (j->state == PSTOPPED) killpg(j->pgrp, SIGCONT); #endif /* JOBS */ } } if (killed) sleep(1); j_notify(); #ifdef JOBS # ifdef TTY_PGRP if (kshpid == procpid && restore_ttypgrp >= 0) { /* Need to restore the tty pgrp to what it was when the * shell started up, so that the process that started us * will be able to access the tty when we are done. * Also need to restore our process group in case we are * about to do an exec so that both our parent and the * process we are to become will be able to access the tty. */ tcsetpgrp(tty_fd, restore_ttypgrp); setpgid(0, restore_ttypgrp); } # endif /* TTY_PGRP */ if (Flag(FMONITOR)) { Flag(FMONITOR) = 0; j_change(); } #endif /* JOBS */ } #ifdef JOBS /* turn job control on or off according to Flag(FMONITOR) */ void j_change() { int i; if (Flag(FMONITOR)) { /* Don't call get_tty() 'til we own the tty process group */ tty_init(FALSE); # ifdef TTY_PGRP /* no controlling tty, no SIGT* */ ttypgrp_ok = tty_fd >= 0 && tty_devtty; if (ttypgrp_ok && (our_pgrp = getpgID()) < 0) { warningf(FALSE, "j_init: getpgrp() failed: %s", strerror(errno)); ttypgrp_ok = 0; } if (ttypgrp_ok) { setsig(&sigtraps[SIGTTIN], SIG_DFL, SS_RESTORE_ORIG|SS_FORCE); /* wait to be given tty (POSIX.1, B.2, job control) */ while (1) { pid_t ttypgrp; if ((ttypgrp = tcgetpgrp(tty_fd)) < 0) { warningf(FALSE, "j_init: tcgetpgrp() failed: %s", strerror(errno)); ttypgrp_ok = 0; break; } if (ttypgrp == our_pgrp) break; kill(0, SIGTTIN); } } for (i = NELEM(tt_sigs); --i >= 0; ) setsig(&sigtraps[tt_sigs[i]], SIG_IGN, SS_RESTORE_DFL|SS_FORCE); if (ttypgrp_ok && our_pgrp != kshpid) { if (setpgid(0, kshpid) < 0) { warningf(FALSE, "j_init: setpgid() failed: %s", strerror(errno)); ttypgrp_ok = 0; } else { if (tcsetpgrp(tty_fd, kshpid) < 0) { warningf(FALSE, "j_init: tcsetpgrp() failed: %s", strerror(errno)); ttypgrp_ok = 0; } else restore_ttypgrp = our_pgrp; our_pgrp = kshpid; } } # if defined(NTTYDISC) && defined(TIOCSETD) && !defined(HAVE_TERMIOS_H) && !defined(HAVE_TERMIO_H) if (ttypgrp_ok) { int ldisc = NTTYDISC; if (ioctl(tty_fd, TIOCSETD, &ldisc) < 0) warningf(FALSE, "j_init: can't set new line discipline: %s", strerror(errno)); } # endif /* NTTYDISC && TIOCSETD */ if (!ttypgrp_ok) warningf(FALSE, "warning: won't have full job control"); # endif /* TTY_PGRP */ if (tty_fd >= 0) get_tty(tty_fd, &tty_state); } else { # ifdef TTY_PGRP ttypgrp_ok = 0; if (Flag(FTALKING)) for (i = NELEM(tt_sigs); --i >= 0; ) setsig(&sigtraps[tt_sigs[i]], SIG_IGN, SS_RESTORE_IGN|SS_FORCE); else for (i = NELEM(tt_sigs); --i >= 0; ) { if (sigtraps[tt_sigs[i]].flags & (TF_ORIG_IGN |TF_ORIG_DFL)) setsig(&sigtraps[tt_sigs[i]], (sigtraps[tt_sigs[i]].flags & TF_ORIG_IGN) ? SIG_IGN : SIG_DFL, SS_RESTORE_ORIG|SS_FORCE); } # endif /* TTY_PGRP */ if (!Flag(FTALKING)) tty_close(); } } #endif /* JOBS */ /* execute tree in child subprocess */ int exchild(t, flags, close_fd) struct op *t; int flags; int close_fd; /* used if XPCLOSE or XCCLOSE */ { static Proc *last_proc; /* for pipelines */ int i; #ifdef JOB_SIGS sigset_t omask; #endif /* JOB_SIGS */ Proc *p; Job *j; int rv = 0; int forksleep; int ischild; if (flags & XEXEC) /* Clear XFORK|XPCLOSE|XCCLOSE|XCOPROC|XPIPEO|XPIPEI|XXCOM|XBGND * (also done in another execute() below) */ return execute(t, flags & (XEXEC | XERROK)); #ifdef JOB_SIGS /* no SIGCHLD's while messing with job and process lists */ sigprocmask(SIG_BLOCK, &sm_sigchld, &omask); #endif /* JOB_SIGS */ p = new_proc(); p->next = (Proc *) 0; p->state = PRUNNING; WSTATUS(p->status) = 0; p->pid = 0; /* link process into jobs list */ if (flags&XPIPEI) { /* continuing with a pipe */ if (!last_job) internal_errorf(1, "exchild: XPIPEI and no last_job - pid %d", (int) procpid); j = last_job; last_proc->next = p; last_proc = p; } else { #ifdef NEED_PGRP_SYNC if (j_sync_open) { /* should never happen */ j_sync_open = 0; closepipe(j_sync_pipe); } /* don't do the sync pipe business if there is no pipeline */ if (flags & XPIPEO) { openpipe(j_sync_pipe); j_sync_open = 1; } #endif /* NEED_PGRP_SYNC */ j = new_job(); /* fills in j->job */ /* we don't consider XXCOM's foreground since they don't get * tty process group and we don't save or restore tty modes. */ j->flags = (flags & XXCOM) ? JF_XXCOM : ((flags & XBGND) ? 0 : (JF_FG|JF_USETTYMODE)); j->usrtime = j->systime = 0; j->state = PRUNNING; j->pgrp = 0; j->ppid = procpid; j->age = ++njobs; j->proc_list = p; #ifdef KSH j->coproc_id = 0; #endif /* KSH */ last_job = j; last_proc = p; put_job(j, PJ_PAST_STOPPED); } fill_command(p->command, sizeof(p->command), t); /* create child process */ forksleep = 1; while ((i = fork()) < 0 && errno == EAGAIN && forksleep < 32) { if (intrsig) /* allow user to ^C out... */ break; sleep(forksleep); forksleep <<= 1; } if (i < 0) { kill_job(j); remove_job(j, "fork failed"); #ifdef NEED_PGRP_SYNC if (j_sync_open) { closepipe(j_sync_pipe); j_sync_open = 0; } #endif /* NEED_PGRP_SYNC */ #ifdef JOB_SIGS sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0); #endif /* JOB_SIGS */ errorf("cannot fork - try again"); } ischild = i == 0; if (ischild) p->pid = procpid = getpid(); else p->pid = i; #ifdef JOBS /* job control set up */ if (Flag(FMONITOR) && !(flags&XXCOM)) { int dotty = 0; # ifdef NEED_PGRP_SYNC int first_child_sync = 0; # endif /* NEED_PGRP_SYNC */ # ifdef NEED_PGRP_SYNC if (j_sync_open) { /* * The Parent closes 0, keeps 1 open 'til the whole * pipeline is started. The First child closes 1, * keeps 0 open (reads from it). The remaining * children just have to close 1 (parent has already * closeed 0). */ if (j->pgrp == 0) { /* First process */ close(j_sync_pipe[ischild]); j_sync_pipe[ischild] = -1; first_child_sync = ischild; } else if (ischild) { j_sync_open = 0; closepipe(j_sync_pipe); } } # endif /* NEED_PGRP_SYNC */ if (j->pgrp == 0) { /* First process */ j->pgrp = p->pid; dotty = 1; } /* set pgrp in both parent and child to deal with race * condition */ setpgid(p->pid, j->pgrp); # ifdef TTY_PGRP /* YYY: should this be if (ttypgrp_ok && ischild && !(flags&XBGND)) tcsetpgrp(tty_fd, j->pgrp); instead? (see also YYY below) */ if (ttypgrp_ok && dotty && !(flags & XBGND)) tcsetpgrp(tty_fd, j->pgrp); # endif /* TTY_PGRP */ # ifdef NEED_PGRP_SYNC if (first_child_sync) { char c; while (read(j_sync_pipe[0], &c, 1) == -1 && errno == EINTR) ; close(j_sync_pipe[0]); j_sync_open = 0; } # endif /* NEED_PGRP_SYNC */ } #endif /* JOBS */ /* used to close pipe input fd */ if (close_fd >= 0 && (((flags & XPCLOSE) && !ischild) || ((flags & XCCLOSE) && ischild))) close(close_fd); if (ischild) { /* child */ #ifdef KSH /* Do this before restoring signal */ if (flags & XCOPROC) coproc_cleanup(FALSE); #endif /* KSH */ #ifdef JOB_SIGS sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0); #endif /* JOB_SIGS */ cleanup_parents_env(); #ifdef TTY_PGRP /* If FMONITOR or FTALKING is set, these signals are ignored, * if neither FMONITOR nor FTALKING are set, the signals have * their inherited values. */ if (Flag(FMONITOR) && !(flags & XXCOM)) { for (i = NELEM(tt_sigs); --i >= 0; ) setsig(&sigtraps[tt_sigs[i]], SIG_DFL, SS_RESTORE_DFL|SS_FORCE); } #endif /* TTY_PGRP */ #ifdef HAVE_NICE if (Flag(FBGNICE) && (flags & XBGND)) nice(4); #endif /* HAVE_NICE */ if ((flags & XBGND) && !Flag(FMONITOR)) { setsig(&sigtraps[SIGINT], SIG_IGN, SS_RESTORE_IGN|SS_FORCE); setsig(&sigtraps[SIGQUIT], SIG_IGN, SS_RESTORE_IGN|SS_FORCE); if (!(flags & (XPIPEI | XCOPROC))) { int fd = open("/dev/null", 0); (void) ksh_dup2(fd, 0, TRUE); close(fd); } } remove_job(j, "child"); /* in case of `jobs` command */ nzombie = 0; #ifdef JOBS ttypgrp_ok = 0; Flag(FMONITOR) = 0; #endif /* JOBS */ Flag(FTALKING) = 0; #ifdef OS2 if (tty_fd >= 0) flags |= XINTACT; #endif /* OS2 */ tty_close(); cleartraps(); execute(t, (flags & XERROK) | XEXEC); /* no return */ internal_errorf(0, "exchild: execute() returned"); unwind(LLEAVE); /* NOTREACHED */ } /* shell (parent) stuff */ /* Ensure next child gets a (slightly) different $RANDOM sequence */ change_random(); if (!(flags & XPIPEO)) { /* last process in a job */ #ifdef TTY_PGRP /* YYY: Is this needed? (see also YYY above) if (Flag(FMONITOR) && !(flags&(XXCOM|XBGND))) tcsetpgrp(tty_fd, j->pgrp); */ #endif /* TTY_PGRP */ j_startjob(j); #ifdef KSH if (flags & XCOPROC) { j->coproc_id = coproc.id; coproc.njobs++; /* n jobs using co-process output */ coproc.job = (void *) j; /* j using co-process input */ } #endif /* KSH */ if (flags & XBGND) { j_set_async(j); if (Flag(FTALKING)) { shf_fprintf(shl_out, "[%d]", j->job); for (p = j->proc_list; p; p = p->next) shf_fprintf(shl_out, " %d", p->pid); shf_putchar('\n', shl_out); shf_flush(shl_out); } } else rv = j_waitj(j, JW_NONE, "jw:last proc"); } #ifdef JOB_SIGS sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0); #endif /* JOB_SIGS */ return rv; } /* start the last job: only used for `command` jobs */ void startlast() { #ifdef JOB_SIGS sigset_t omask; sigprocmask(SIG_BLOCK, &sm_sigchld, &omask); #endif /* JOB_SIGS */ if (last_job) { /* no need to report error - waitlast() will do it */ /* ensure it isn't removed by check_job() */ last_job->flags |= JF_WAITING; j_startjob(last_job); } #ifdef JOB_SIGS sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0); #endif /* JOB_SIGS */ } /* wait for last job: only used for `command` jobs */ int waitlast() { int rv; Job *j; #ifdef JOB_SIGS sigset_t omask; sigprocmask(SIG_BLOCK, &sm_sigchld, &omask); #endif /* JOB_SIGS */ j = last_job; if (!j || !(j->flags & JF_STARTED)) { if (!j) warningf(TRUE, "waitlast: no last job"); else internal_errorf(0, "waitlast: not started"); #ifdef JOB_SIGS sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0); #endif /* JOB_SIGS */ return 125; /* not so arbitrary, non-zero value */ } rv = j_waitj(j, JW_NONE, "jw:waitlast"); #ifdef JOB_SIGS sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0); #endif /* JOB_SIGS */ return rv; } /* wait for child, interruptable. */ int waitfor(cp, sigp) const char *cp; int *sigp; { int rv; Job *j; int ecode; int flags = JW_INTERRUPT|JW_ASYNCNOTIFY; #ifdef JOB_SIGS sigset_t omask; sigprocmask(SIG_BLOCK, &sm_sigchld, &omask); #endif /* JOB_SIGS */ *sigp = 0; if (cp == (char *) 0) { /* wait for an unspecified job - always returns 0, so * don't have to worry about exited/signaled jobs */ for (j = job_list; j; j = j->next) /* at&t ksh will wait for stopped jobs - we don't */ if (j->ppid == procpid && j->state == PRUNNING) break; if (!j) { #ifdef JOB_SIGS sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0); #endif /* JOB_SIGS */ return -1; } } else if ((j = j_lookup(cp, &ecode))) { /* don't report normal job completion */ flags &= ~JW_ASYNCNOTIFY; if (j->ppid != procpid) { #ifdef JOB_SIGS sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0); #endif /* JOB_SIGS */ return -1; } } else { #ifdef JOB_SIGS sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0); #endif /* JOB_SIGS */ if (ecode != JL_NOSUCH) bi_errorf("%s: %s", cp, lookup_msgs[ecode]); return -1; } /* at&t ksh will wait for stopped jobs - we don't */ rv = j_waitj(j, flags, "jw:waitfor"); #ifdef JOB_SIGS sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0); #endif /* JOB_SIGS */ if (rv < 0) /* we were interrupted */ *sigp = 128 + -rv; return rv; } /* kill (built-in) a job */ int j_kill(cp, sig) const char *cp; int sig; { Job *j; Proc *p; int rv = 0; int ecode; #ifdef JOB_SIGS sigset_t omask; sigprocmask(SIG_BLOCK, &sm_sigchld, &omask); #endif /* JOB_SIGS */ if ((j = j_lookup(cp, &ecode)) == (Job *) 0) { #ifdef JOB_SIGS sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0); #endif /* JOB_SIGS */ bi_errorf("%s: %s", cp, lookup_msgs[ecode]); return 1; } if (j->pgrp == 0) { /* started when !Flag(FMONITOR) */ for (p=j->proc_list; p != (Proc *) 0; p = p->next) if (kill(p->pid, sig) < 0) { bi_errorf("%s: %s", cp, strerror(errno)); rv = 1; } } else { #ifdef JOBS if (j->state == PSTOPPED && (sig == SIGTERM || sig == SIGHUP)) (void) killpg(j->pgrp, SIGCONT); #endif /* JOBS */ if (killpg(j->pgrp, sig) < 0) { bi_errorf("%s: %s", cp, strerror(errno)); rv = 1; } } #ifdef JOB_SIGS sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0); #endif /* JOB_SIGS */ return rv; } #ifdef JOBS /* fg and bg built-ins: called only if Flag(FMONITOR) set */ int j_resume(cp, bg) const char *cp; int bg; { Job *j; Proc *p; int ecode; int running; int rv = 0; sigset_t omask; sigprocmask(SIG_BLOCK, &sm_sigchld, &omask); if ((j = j_lookup(cp, &ecode)) == (Job *) 0) { sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0); bi_errorf("%s: %s", cp, lookup_msgs[ecode]); return 1; } if (j->pgrp == 0) { sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0); bi_errorf("job not job-controlled"); return 1; } if (bg) shprintf("[%d] ", j->job); running = 0; for (p = j->proc_list; p != (Proc *) 0; p = p->next) { if (p->state == PSTOPPED) { p->state = PRUNNING; WSTATUS(p->status) = 0; running = 1; } shprintf("%s%s", p->command, p->next ? "| " : null); } shprintf(newline); shf_flush(shl_stdout); if (running) j->state = PRUNNING; put_job(j, PJ_PAST_STOPPED); if (bg) j_set_async(j); else { # ifdef TTY_PGRP /* attach tty to job */ if (j->state == PRUNNING) { if (ttypgrp_ok && (j->flags & JF_SAVEDTTY)) { set_tty(tty_fd, &j->ttystate, TF_NONE); } /* See comment in j_waitj regarding saved_ttypgrp. */ if (ttypgrp_ok && tcsetpgrp(tty_fd, (j->flags & JF_SAVEDTTYPGRP) ? j->saved_ttypgrp : j->pgrp) < 0) { if (j->flags & JF_SAVEDTTY) { set_tty(tty_fd, &tty_state, TF_NONE); } sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0); bi_errorf("1st tcsetpgrp(%d, %d) failed: %s", tty_fd, (int) ((j->flags & JF_SAVEDTTYPGRP) ? j->saved_ttypgrp : j->pgrp), strerror(errno)); return 1; } } # endif /* TTY_PGRP */ j->flags |= JF_FG; j->flags &= ~JF_KNOWN; if (j == async_job) async_job = (Job *) 0; } if (j->state == PRUNNING && killpg(j->pgrp, SIGCONT) < 0) { int err = errno; if (!bg) { j->flags &= ~JF_FG; # ifdef TTY_PGRP if (ttypgrp_ok && (j->flags & JF_SAVEDTTY)) { set_tty(tty_fd, &tty_state, TF_NONE); } if (ttypgrp_ok && tcsetpgrp(tty_fd, our_pgrp) < 0) { warningf(TRUE, "fg: 2nd tcsetpgrp(%d, %d) failed: %s", tty_fd, (int) our_pgrp, strerror(errno)); } # endif /* TTY_PGRP */ } sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0); bi_errorf("cannot continue job %s: %s", cp, strerror(err)); return 1; } if (!bg) { # ifdef TTY_PGRP if (ttypgrp_ok) { j->flags &= ~(JF_SAVEDTTY | JF_SAVEDTTYPGRP); } # endif /* TTY_PGRP */ rv = j_waitj(j, JW_NONE, "jw:resume"); } sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0); return rv; } #endif /* JOBS */ /* are there any running or stopped jobs ? */ int j_stopped_running() { Job *j; int which = 0; for (j = job_list; j != (Job *) 0; j = j->next) { #ifdef JOBS if (j->ppid == procpid && j->state == PSTOPPED) which |= 1; #endif /* JOBS */ if (Flag(FLOGIN) && !Flag(FNOHUP) && procpid == kshpid && j->ppid == procpid && j->state == PRUNNING) which |= 2; } if (which) { shellf("You have %s%s%s jobs\n", which & 1 ? "stopped" : "", which == 3 ? " and " : "", which & 2 ? "running" : ""); return 1; } return 0; } /* list jobs for jobs built-in */ int j_jobs(cp, slp, nflag) const char *cp; int slp; /* 0: short, 1: long, 2: pgrp */ int nflag; { Job *j, *tmp; int how; int zflag = 0; #ifdef JOB_SIGS sigset_t omask; sigprocmask(SIG_BLOCK, &sm_sigchld, &omask); #endif /* JOB_SIGS */ if (nflag < 0) { /* kludge: print zombies */ nflag = 0; zflag = 1; } if (cp) { int ecode; if ((j = j_lookup(cp, &ecode)) == (Job *) 0) { #ifdef JOB_SIGS sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0); #endif /* JOB_SIGS */ bi_errorf("%s: %s", cp, lookup_msgs[ecode]); return 1; } } else j = job_list; how = slp == 0 ? JP_MEDIUM : (slp == 1 ? JP_LONG : JP_PGRP); for (; j; j = j->next) { if ((!(j->flags & JF_ZOMBIE) || zflag) && (!nflag || (j->flags & JF_CHANGED))) { j_print(j, how, shl_stdout); if (j->state == PEXITED || j->state == PSIGNALLED) j->flags |= JF_REMOVE; } if (cp) break; } /* Remove jobs after printing so there won't be multiple + or - jobs */ for (j = job_list; j; j = tmp) { tmp = j->next; if (j->flags & JF_REMOVE) remove_job(j, "jobs"); } #ifdef JOB_SIGS sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0); #endif /* JOB_SIGS */ return 0; } /* list jobs for top-level notification */ void j_notify() { Job *j, *tmp; #ifdef JOB_SIGS sigset_t omask; sigprocmask(SIG_BLOCK, &sm_sigchld, &omask); #endif /* JOB_SIGS */ for (j = job_list; j; j = j->next) { #ifdef JOBS if (Flag(FMONITOR) && (j->flags & JF_CHANGED)) j_print(j, JP_MEDIUM, shl_out); #endif /* JOBS */ /* Remove job after doing reports so there aren't * multiple +/- jobs. */ if (j->state == PEXITED || j->state == PSIGNALLED) j->flags |= JF_REMOVE; } for (j = job_list; j; j = tmp) { tmp = j->next; if (j->flags & JF_REMOVE) remove_job(j, "notify"); } shf_flush(shl_out); #ifdef JOB_SIGS sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0); #endif /* JOB_SIGS */ } /* Return pid of last process in last asynchornous job */ pid_t j_async() { #ifdef JOB_SIGS sigset_t omask; sigprocmask(SIG_BLOCK, &sm_sigchld, &omask); #endif /* JOB_SIGS */ if (async_job) async_job->flags |= JF_KNOWN; #ifdef JOB_SIGS sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0); #endif /* JOB_SIGS */ return async_pid; } /* Make j the last async process * * If jobs are compiled in then this routine expects sigchld to be blocked. */ static void j_set_async(j) Job *j; { Job *jl, *oldest; if (async_job && (async_job->flags & (JF_KNOWN|JF_ZOMBIE)) == JF_ZOMBIE) remove_job(async_job, "async"); if (!(j->flags & JF_STARTED)) { internal_errorf(0, "j_async: job not started"); return; } async_job = j; async_pid = j->last_proc->pid; while (nzombie > child_max) { oldest = (Job *) 0; for (jl = job_list; jl; jl = jl->next) if (jl != async_job && (jl->flags & JF_ZOMBIE) && (!oldest || jl->age < oldest->age)) oldest = jl; if (!oldest) { /* XXX debugging */ if (!(async_job->flags & JF_ZOMBIE) || nzombie != 1) { internal_errorf(0, "j_async: bad nzombie (%d)", nzombie); nzombie = 0; } break; } remove_job(oldest, "zombie"); } } /* Start a job: set STARTED, check for held signals and set j->last_proc * * If jobs are compiled in then this routine expects sigchld to be blocked. */ static void j_startjob(j) Job *j; { Proc *p; j->flags |= JF_STARTED; for (p = j->proc_list; p->next; p = p->next) ; j->last_proc = p; #ifdef NEED_PGRP_SYNC if (j_sync_open) { j_sync_open = 0; closepipe(j_sync_pipe); } #endif /* NEED_PGRP_SYNC */ #ifdef JOB_SIGS if (held_sigchld) { held_sigchld = 0; /* Don't call j_sigchld() as it may remove job... */ kill(procpid, SIGCHLD); } #endif /* JOB_SIGS */ } /* * wait for job to complete or change state * * If jobs are compiled in then this routine expects sigchld to be blocked. */ static int j_waitj(j, flags, where) Job *j; int flags; /* see JW_* */ const char *where; { int rv; /* * No auto-notify on the job we are waiting on. */ j->flags |= JF_WAITING; if (flags & JW_ASYNCNOTIFY) j->flags |= JF_W_ASYNCNOTIFY; if (!Flag(FMONITOR)) flags |= JW_STOPPEDWAIT; while ((volatile int) j->state == PRUNNING || ((flags & JW_STOPPEDWAIT) && (volatile int) j->state == PSTOPPED)) { #ifdef JOB_SIGS sigsuspend(&sm_default); #else /* JOB_SIGS */ j_sigchld(SIGCHLD); #endif /* JOB_SIGS */ if (fatal_trap) { int oldf = j->flags & (JF_WAITING|JF_W_ASYNCNOTIFY); j->flags &= ~(JF_WAITING|JF_W_ASYNCNOTIFY); runtraps(TF_FATAL); j->flags |= oldf; /* not reached... */ } if ((flags & JW_INTERRUPT) && (rv = trap_pending())) { j->flags &= ~(JF_WAITING|JF_W_ASYNCNOTIFY); return -rv; } } j->flags &= ~(JF_WAITING|JF_W_ASYNCNOTIFY); if (j->flags & JF_FG) { WAIT_T status; j->flags &= ~JF_FG; #ifdef TTY_PGRP if (Flag(FMONITOR) && ttypgrp_ok && j->pgrp) { /* * Save the tty's current pgrp so it can be restored * when the job is foregrounded. This is to * deal with things like the GNU su which does * a fork/exec instead of an exec (the fork means * the execed shell gets a different pid from its * pgrp, so naturally it sets its pgrp and gets hosed * when it gets forgrounded by the parent shell, which * has restored the tty's pgrp to that of the su * process). */ if (j->state == PSTOPPED && (j->saved_ttypgrp = tcgetpgrp(tty_fd)) >= 0) j->flags |= JF_SAVEDTTYPGRP; if (tcsetpgrp(tty_fd, our_pgrp) < 0) { warningf(TRUE, "j_waitj: tcsetpgrp(%d, %d) failed: %s", tty_fd, (int) our_pgrp, strerror(errno)); } if (j->state == PSTOPPED) { j->flags |= JF_SAVEDTTY; get_tty(tty_fd, &j->ttystate); } } #endif /* TTY_PGRP */ if (tty_fd >= 0) { /* Only restore tty settings if job was originally * started in the foreground. Problems can be * caused by things like `more foobar &' which will * typically get and save the shell's vi/emacs tty * settings before setting up the tty for itself; * when more exits, it restores the `original' * settings, and things go down hill from there... */ if (j->state == PEXITED && j->status == 0 && (j->flags & JF_USETTYMODE)) { get_tty(tty_fd, &tty_state); } else { set_tty(tty_fd, &tty_state, (j->state == PEXITED) ? 0 : TF_MIPSKLUDGE); /* Don't use tty mode if job is stopped and * later restarted and exits. Consider * the sequence: * vi foo (stopped) * ... * stty something * ... * fg (vi; ZZ) * mode should be that of the stty, not what * was before the vi started. */ if (j->state == PSTOPPED) j->flags &= ~JF_USETTYMODE; } } #ifdef JOBS /* If it looks like user hit ^C to kill a job, pretend we got * one too to break out of for loops, etc. (at&t ksh does this * even when not monitoring, but this doesn't make sense since * a tty generated ^C goes to the whole process group) */ status = j->last_proc->status; if (Flag(FMONITOR) && j->state == PSIGNALLED && WIFSIGNALED(status) && (sigtraps[WTERMSIG(status)].flags & TF_TTY_INTR)) trapsig(WTERMSIG(status)); #endif /* JOBS */ } j_usrtime = j->usrtime; j_systime = j->systime; rv = j->status; if (!(flags & JW_ASYNCNOTIFY) && (!Flag(FMONITOR) || j->state != PSTOPPED)) { j_print(j, JP_SHORT, shl_out); shf_flush(shl_out); } if (j->state != PSTOPPED && (!Flag(FMONITOR) || !(flags & JW_ASYNCNOTIFY))) remove_job(j, where); return rv; } /* SIGCHLD handler to reap children and update job states * * If jobs are compiled in then this routine expects sigchld to be blocked. */ static RETSIGTYPE j_sigchld(sig) int sig; { int errno_ = errno; Job *j; Proc UNINITIALIZED(*p); int pid; WAIT_T status; struct tms t0, t1; #ifdef JOB_SIGS /* Don't wait for any processes if a job is partially started. * This is so we don't do away with the process group leader * before all the processes in a pipe line are started (so the * setpgid() won't fail) */ for (j = job_list; j; j = j->next) if (j->ppid == procpid && !(j->flags & JF_STARTED)) { held_sigchld = 1; return RETSIGVAL; } #endif /* JOB_SIGS */ ksh_times(&t0); do { #ifdef JOB_SIGS pid = ksh_waitpid(-1, &status, (WNOHANG|WUNTRACED)); #else /* JOB_SIGS */ pid = wait(&status); #endif /* JOB_SIGS */ if (pid <= 0) /* return if would block (0) ... */ break; /* ... or no children or interrupted (-1) */ ksh_times(&t1); /* find job and process structures for this pid */ for (j = job_list; j != (Job *) 0; j = j->next) for (p = j->proc_list; p != (Proc *) 0; p = p->next) if (p->pid == pid) goto found; found: if (j == (Job *) 0) { /* Can occur if process has kids, then execs shell warningf(TRUE, "bad process waited for (pid = %d)", pid); */ t0 = t1; continue; } j->usrtime += t1.tms_cutime - t0.tms_cutime; j->systime += t1.tms_cstime - t0.tms_cstime; t0 = t1; p->status = status; #ifdef JOBS if (WIFSTOPPED(status)) p->state = PSTOPPED; else #endif /* JOBS */ if (WIFSIGNALED(status)) p->state = PSIGNALLED; else p->state = PEXITED; check_job(j); /* check to see if entire job is done */ } #ifdef JOB_SIGS while (1); #else /* JOB_SIGS */ while (0); #endif /* JOB_SIGS */ errno = errno_; return RETSIGVAL; } /* * Called only when a process in j has exited/stopped (ie, called only * from j_sigchld()). If no processes are running, the job status * and state are updated, asynchronous job notification is done and, * if unneeded, the job is removed. * * If jobs are compiled in then this routine expects sigchld to be blocked. */ static void check_job(j) Job *j; { int jstate; Proc *p; /* XXX debugging (nasty - interrupt routine using shl_out) */ if (!(j->flags & JF_STARTED)) { internal_errorf(0, "check_job: job started (flags 0x%x)", j->flags); return; } jstate = PRUNNING; for (p=j->proc_list; p != (Proc *) 0; p = p->next) { if (p->state == PRUNNING) return; /* some processes still running */ if (p->state > jstate) jstate = p->state; } j->state = jstate; switch (j->last_proc->state) { case PEXITED: j->status = WEXITSTATUS(j->last_proc->status); break; case PSIGNALLED: j->status = 128 + WTERMSIG(j->last_proc->status); break; default: j->status = 0; break; } #ifdef KSH /* Note when co-process dies: can't be done in j_wait() nor * remove_job() since neither may be called for non-interactive * shells. */ if (j->state == PEXITED || j->state == PSIGNALLED) { /* No need to keep co-process input any more * (at leasst, this is what ksh93d thinks) */ if (coproc.job == j) { coproc.job = (void *) 0; /* XXX would be nice to get the closes out of here * so they aren't done in the signal handler. * Would mean a check in coproc_getfd() to * do "if job == 0 && write >= 0, close write". */ coproc_write_close(coproc.write); } /* Do we need to keep the output? */ if (j->coproc_id && j->coproc_id == coproc.id && --coproc.njobs == 0) coproc_readw_close(coproc.read); } #endif /* KSH */ j->flags |= JF_CHANGED; #ifdef JOBS if (Flag(FMONITOR) && !(j->flags & JF_XXCOM)) { /* Only put stopped jobs at the front to avoid confusing * the user (don't want finished jobs effecting %+ or %-) */ if (j->state == PSTOPPED) put_job(j, PJ_ON_FRONT); if (Flag(FNOTIFY) && (j->flags & (JF_WAITING|JF_W_ASYNCNOTIFY)) != JF_WAITING) { /* Look for the real file descriptor 2 */ { struct env *ep; int fd = 2; for (ep = e; ep; ep = ep->oenv) if (ep->savefd && ep->savefd[2]) fd = ep->savefd[2]; shf_reopen(fd, SHF_WR, shl_j); } /* Can't call j_notify() as it removes jobs. The job * must stay in the job list as j_waitj() may be * running with this job. */ j_print(j, JP_MEDIUM, shl_j); shf_flush(shl_j); if (!(j->flags & JF_WAITING) && j->state != PSTOPPED) remove_job(j, "notify"); } } #endif /* JOBS */ if (!Flag(FMONITOR) && !(j->flags & (JF_WAITING|JF_FG)) && j->state != PSTOPPED) { if (j == async_job || (j->flags & JF_KNOWN)) { j->flags |= JF_ZOMBIE; j->job = -1; nzombie++; } else remove_job(j, "checkjob"); } } /* * Print job status in either short, medium or long format. * * If jobs are compiled in then this routine expects sigchld to be blocked. */ static void j_print(j, how, shf) Job *j; int how; struct shf *shf; { Proc *p; int state; WAIT_T status; int coredumped; char jobchar = ' '; char buf[64]; const char *filler; int output = 0; if (how == JP_PGRP) { /* POSIX doesn't say what to do it there is no process * group leader (ie, !FMONITOR). We arbitrarily return * last pid (which is what $! returns). */ shf_fprintf(shf, "%d\n", j->pgrp ? j->pgrp : (j->last_proc ? j->last_proc->pid : 0)); return; } j->flags &= ~JF_CHANGED; filler = j->job > 10 ? "\n " : "\n "; if (j == job_list) jobchar = '+'; else if (j == job_list->next) jobchar = '-'; for (p = j->proc_list; p != (Proc *) 0;) { coredumped = 0; switch (p->state) { case PRUNNING: strcpy(buf, "Running"); break; case PSTOPPED: strcpy(buf, sigtraps[WSTOPSIG(p->status)].mess); break; case PEXITED: if (how == JP_SHORT) buf[0] = '\0'; else if (WEXITSTATUS(p->status) == 0) strcpy(buf, "Done"); else shf_snprintf(buf, sizeof(buf), "Done (%d)", WEXITSTATUS(p->status)); break; case PSIGNALLED: if (WIFCORED(p->status)) coredumped = 1; /* kludge for not reporting `normal termination signals' * (ie, SIGINT, SIGPIPE) */ if (how == JP_SHORT && !coredumped && (WTERMSIG(p->status) == SIGINT || WTERMSIG(p->status) == SIGPIPE)) { buf[0] = '\0'; } else strcpy(buf, sigtraps[WTERMSIG(p->status)].mess); break; } if (how != JP_SHORT) if (p == j->proc_list) shf_fprintf(shf, "[%d] %c ", j->job, jobchar); else shf_fprintf(shf, "%s", filler); if (how == JP_LONG) shf_fprintf(shf, "%5d ", p->pid); if (how == JP_SHORT) { if (buf[0]) { output = 1; shf_fprintf(shf, "%s%s ", buf, coredumped ? " (core dumped)" : null); } } else { output = 1; shf_fprintf(shf, "%-20s %s%s%s", buf, p->command, p->next ? "|" : null, coredumped ? " (core dumped)" : null); } state = p->state; status = p->status; p = p->next; while (p && p->state == state && WSTATUS(p->status) == WSTATUS(status)) { if (how == JP_LONG) shf_fprintf(shf, "%s%5d %-20s %s%s", filler, p->pid, space, p->command, p->next ? "|" : null); else if (how == JP_MEDIUM) shf_fprintf(shf, " %s%s", p->command, p->next ? "|" : null); p = p->next; } } if (output) shf_fprintf(shf, newline); } /* Convert % sequence to job * * If jobs are compiled in then this routine expects sigchld to be blocked. */ static Job * j_lookup(cp, ecodep) const char *cp; int *ecodep; { Job *j, *last_match; Proc *p; int len, job = 0; if (digit(*cp)) { job = atoi(cp); /* Look for last_proc->pid (what $! returns) first... */ for (j = job_list; j != (Job *) 0; j = j->next) if (j->last_proc && j->last_proc->pid == job) return j; /* ...then look for process group (this is non-POSIX), * but should not break anything (so FPOSIX isn't used). */ for (j = job_list; j != (Job *) 0; j = j->next) if (j->pgrp && j->pgrp == job) return j; if (ecodep) *ecodep = JL_NOSUCH; return (Job *) 0; } if (*cp != '%') { if (ecodep) *ecodep = JL_INVALID; return (Job *) 0; } switch (*++cp) { case '\0': /* non-standard */ case '+': case '%': if (job_list != (Job *) 0) return job_list; break; case '-': if (job_list != (Job *) 0 && job_list->next) return job_list->next; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': job = atoi(cp); for (j = job_list; j != (Job *) 0; j = j->next) if (j->job == job) return j; break; case '?': /* %?string */ last_match = (Job *) 0; for (j = job_list; j != (Job *) 0; j = j->next) for (p = j->proc_list; p != (Proc *) 0; p = p->next) if (strstr(p->command, cp+1) != (char *) 0) { if (last_match) { if (ecodep) *ecodep = JL_AMBIG; return (Job *) 0; } last_match = j; } if (last_match) return last_match; break; default: /* %string */ len = strlen(cp); last_match = (Job *) 0; for (j = job_list; j != (Job *) 0; j = j->next) if (strncmp(cp, j->proc_list->command, len) == 0) { if (last_match) { if (ecodep) *ecodep = JL_AMBIG; return (Job *) 0; } last_match = j; } if (last_match) return last_match; break; } if (ecodep) *ecodep = JL_NOSUCH; return (Job *) 0; } static Job *free_jobs; static Proc *free_procs; /* allocate a new job and fill in the job number. * * If jobs are compiled in then this routine expects sigchld to be blocked. */ static Job * new_job() { int i; Job *newj, *j; if (free_jobs != (Job *) 0) { newj = free_jobs; free_jobs = free_jobs->next; } else newj = (Job *) alloc(sizeof(Job), APERM); /* brute force method */ for (i = 1; ; i++) { for (j = job_list; j && j->job != i; j = j->next) ; if (j == (Job *) 0) break; } newj->job = i; return newj; } /* Allocate new process strut * * If jobs are compiled in then this routine expects sigchld to be blocked. */ static Proc * new_proc() { Proc *p; if (free_procs != (Proc *) 0) { p = free_procs; free_procs = free_procs->next; } else p = (Proc *) alloc(sizeof(Proc), APERM); return p; } /* Take job out of job_list and put old structures into free list. * Keeps nzombies, last_job and async_job up to date. * * If jobs are compiled in then this routine expects sigchld to be blocked. */ static void remove_job(j, where) Job *j; const char *where; { Proc *p, *tmp; Job **prev, *curr; prev = &job_list; curr = *prev; for (; curr != (Job *) 0 && curr != j; prev = &curr->next, curr = *prev) ; if (curr != j) { internal_errorf(0, "remove_job: job not found (%s)", where); return; } *prev = curr->next; /* free up proc structures */ for (p = j->proc_list; p != (Proc *) 0; ) { tmp = p; p = p->next; tmp->next = free_procs; free_procs = tmp; } if ((j->flags & JF_ZOMBIE) && j->ppid == procpid) --nzombie; j->next = free_jobs; free_jobs = j; if (j == last_job) last_job = (Job *) 0; if (j == async_job) async_job = (Job *) 0; } /* put j in a particular location (taking it out job_list if it is there * already) * * If jobs are compiled in then this routine expects sigchld to be blocked. */ static void put_job(j, where) Job *j; int where; { Job **prev, *curr; /* Remove job from list (if there) */ prev = &job_list; curr = job_list; for (; curr && curr != j; prev = &curr->next, curr = *prev) ; if (curr == j) *prev = curr->next; switch (where) { case PJ_ON_FRONT: j->next = job_list; job_list = j; break; case PJ_PAST_STOPPED: prev = &job_list; curr = job_list; for (; curr && curr->state == PSTOPPED; prev = &curr->next, curr = *prev) ; j->next = curr; *prev = j; break; } } /* nuke a job (called when unable to start full job). * * If jobs are compiled in then this routine expects sigchld to be blocked. */ static void kill_job(j) Job *j; { Proc *p; for (p = j->proc_list; p != (Proc *) 0; p = p->next) if (p->pid != 0) (void) kill(p->pid, SIGKILL); } /* put a more useful name on a process than snptreef does (in certain cases) */ static void fill_command(c, len, t) char *c; int len; struct op *t; { int alen; char **ap; if (t->type == TEXEC || t->type == TCOM) { /* Causes problems when set -u is in effect, can also cause problems when array indices evaluated (may have side effects, eg, assignment, incr, etc.) if (t->type == TCOM) ap = eval(t->args, DOBLANK|DONTRUNCOMMAND); else */ ap = t->args; --len; /* save room for the null */ while (len > 0 && *ap != (char *) 0) { alen = strlen(*ap); if (alen > len) alen = len; memcpy(c, *ap, alen); c += alen; len -= alen; if (len > 0) { *c++ = ' '; len--; } ap++; } *c = '\0'; } else snptreef(c, len, "%T", t); } /sys/src/ape/cmd/pdksh/ksh_dir.h 664 sys sys 1367613436 653 /* Wrapper around the ugly dir includes/ifdefs */ /* $Id$ */ #if defined(HAVE_DIRENT_H) # include # define NLENGTH(dirent) (strlen(dirent->d_name)) #else # define dirent direct # define NLENGTH(dirent) (dirent->d_namlen) # ifdef HAVE_SYS_NDIR_H # include # endif /* HAVE_SYS_NDIR_H */ # ifdef HAVE_SYS_DIR_H # include # endif /* HAVE_SYSDIR_H */ # ifdef HAVE_NDIR_H # include # endif /* HAVE_NDIR_H */ #endif /* HAVE_DIRENT_H */ #ifdef OPENDIR_DOES_NONDIR extern DIR *ksh_opendir ARGS((const char *d)); #else /* OPENDIR_DOES_NONDIR */ # define ksh_opendir(d) opendir(d) #endif /* OPENDIR_DOES_NONDIR */ /sys/src/ape/cmd/pdksh/ksh_limval.h 664 sys sys 1367613436 448 /* Wrapper around the values.h/limits.h includes/ifdefs */ /* $Id$ */ #ifdef HAVE_VALUES_H # include #endif /* HAVE_VALUES_H */ /* limits.h is included in sh.h */ #ifndef DMAXEXP # define DMAXEXP 128 /* should be big enough */ #endif #ifndef BITSPERBYTE # ifdef CHAR_BIT # define BITSPERBYTE CHAR_BIT # else # define BITSPERBYTE 8 /* probably true.. */ # endif #endif #ifndef BITS # define BITS(t) (BITSPERBYTE * sizeof(t)) #endif /sys/src/ape/cmd/pdksh/ksh_stat.h 664 sys sys 1367613437 1619 /* Wrapper around the ugly sys/stat includes/ifdefs */ /* $Id$ */ /* assumes already included */ #include #ifndef HAVE_LSTAT # define lstat(path, buf) stat(path, buf) #endif /* HAVE_LSTAT */ #ifdef STAT_MACROS_BROKEN # undef S_ISREG # undef S_ISDIR # undef S_ISCHR # undef S_ISBLK # undef S_ISFIFO # undef S_ISSOCK # undef S_ISLNK #endif /* STAT_MACROS_BROKEN */ #if !defined(S_ISREG) && defined(S_IFREG) # define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) #endif /* S_ISREG */ #if !defined(S_ISDIR) && defined(S_IFDIR) # define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) #endif /* S_ISDIR */ #if !defined(S_ISCHR) && defined(S_IFCHR) # define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) #endif /* S_ISCHR */ #if !defined(S_ISBLK) && defined(S_IFBLK) # define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK) #endif /* S_ISBLK */ #if !defined(S_ISFIFO) && defined(S_IFIFO) # define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO) #endif /* S_ISFIFO */ #if !defined(S_ISLNK) && defined(S_IFLNK) # define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) #endif /* S_ISLNK */ #if !defined(S_ISSOCK) && defined(S_IFSOCK) # define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK) #endif /* S_ISSOCK */ #if !defined(S_ISCDF) && defined(S_CDF) # define S_ISCDF(m) (S_ISDIR(m) && ((m) & S_CDF)) #endif /* S_ISSOCK */ #ifndef S_ISVTX # define S_ISVTX 01000 /* sticky bit */ #endif /* S_ISVTX */ #ifndef S_IXUSR # define S_IXUSR 00100 /* user execute bit */ #endif /* S_IXUSR */ #ifndef S_IXGRP # define S_IXGRP 00010 /* user execute bit */ #endif /* S_IXGRP */ #ifndef S_IXOTH # define S_IXOTH 00001 /* user execute bit */ #endif /* S_IXOTH */ /sys/src/ape/cmd/pdksh/ksh_time.h 664 sys sys 1367613437 478 #ifndef KSH_TIME_H # define KSH_TIME_H /* Wrapper around the ugly time.h,sys/time.h includes/ifdefs */ /* $Id$ */ #ifdef TIME_WITH_SYS_TIME # include # include #else /* TIME_WITH_SYS_TIME */ # ifdef HAVE_SYS_TIME_H # include # else # include # endif #endif /* TIME_WITH_SYS_TIME */ #ifndef TIME_DECLARED extern time_t time ARGS((time_t *)); #endif #ifndef CLK_TCK # define CLK_TCK 60 /* 60HZ */ #endif #endif /* KSH_TIME_H */ /sys/src/ape/cmd/pdksh/ksh_times.h 664 sys sys 1367613437 413 #ifndef KSH_TIMES_H # define KSH_TIMES_H /* Needed for clock_t on some systems (ie, NeXT in non-posix mode) */ #include "ksh_time.h" #include #ifdef TIMES_BROKEN extern clock_t ksh_times ARGS((struct tms *)); #else /* TIMES_BROKEN */ # define ksh_times times #endif /* TIMES_BROKEN */ #ifdef HAVE_TIMES extern clock_t times ARGS((struct tms *)); #endif /* HAVE_TIMES */ #endif /* KSH_TIMES_H */ /sys/src/ape/cmd/pdksh/ksh_wait.h 664 sys sys 1367613437 1180 /* Wrapper around the ugly sys/wait includes/ifdefs */ /* $Id$ */ #ifdef HAVE_SYS_WAIT_H # include #endif #ifndef POSIX_SYS_WAIT /* Get rid of system macros (which probably use union wait) */ # undef WIFCORED # undef WIFEXITED # undef WEXITSTATUS # undef WIFSIGNALED # undef WTERMSIG # undef WIFSTOPPED # undef WSTOPSIG #endif /* POSIX_SYS_WAIT */ typedef int WAIT_T; #ifndef WIFCORED # define WIFCORED(s) ((s) & 0x80) #endif #define WSTATUS(s) (s) #ifndef WIFEXITED # define WIFEXITED(s) (((s) & 0xff) == 0) #endif #ifndef WEXITSTATUS # define WEXITSTATUS(s) (((s) >> 8) & 0xff) #endif #ifndef WIFSIGNALED # define WIFSIGNALED(s) (((s) & 0xff) != 0 && ((s) & 0xff) != 0x7f) #endif #ifndef WTERMSIG # define WTERMSIG(s) ((s) & 0x7f) #endif #ifndef WIFSTOPPED # define WIFSTOPPED(s) (((s) & 0xff) == 0x7f) #endif #ifndef WSTOPSIG # define WSTOPSIG(s) (((s) >> 8) & 0xff) #endif #if !defined(HAVE_WAITPID) && defined(HAVE_WAIT3) /* always used with p == -1 */ # define ksh_waitpid(p, s, o) wait3((s), (o), (struct rusage *) 0) #else /* !HAVE_WAITPID && HAVE_WAIT3 */ # define ksh_waitpid(p, s, o) waitpid((p), (s), (o)) #endif /* !HAVE_WAITPID && HAVE_WAIT3 */ /sys/src/ape/cmd/pdksh/lex.c 664 sys sys 1367613437 29385 /* * lexical analysis and source input */ #include "sh.h" #include /* Structure to keep track of the lexing state and the various pieces of info * needed for each particular state. */ typedef struct lex_state Lex_state; struct lex_state { int ls_state; union { /* $(...) */ struct scsparen_info { int nparen; /* count open parenthesis */ int csstate; /* XXX remove */ #define ls_scsparen ls_info.u_scsparen } u_scsparen; /* $((...)) */ struct sasparen_info { int nparen; /* count open parenthesis */ int start; /* marks start of $(( in output str */ #define ls_sasparen ls_info.u_sasparen } u_sasparen; /* ((...)) */ struct sletparen_info { int nparen; /* count open parenthesis */ #define ls_sletparen ls_info.u_sletparen } u_sletparen; /* `...` */ struct sbquote_info { int indquotes; /* true if in double quotes: "`...`" */ #define ls_sbquote ls_info.u_sbquote } u_sbquote; Lex_state *base; /* used to point to next state block */ } ls_info; }; typedef struct State_info State_info; struct State_info { Lex_state *base; Lex_state *end; }; static void readhere ARGS((struct ioword *iop)); static int getsc__ ARGS((void)); static void getsc_line ARGS((Source *s)); static int getsc_bn ARGS((void)); static char *get_brace_var ARGS((XString *wsp, char *wp)); static int arraysub ARGS((char **strp)); static const char *ungetsc ARGS((int c)); static void gethere ARGS((void)); static Lex_state *push_state_ ARGS((State_info *si, Lex_state *old_end)); static Lex_state *pop_state_ ARGS((State_info *si, Lex_state *old_end)); static int backslash_skip; static int ignore_backslash_newline; /* optimized getsc_bn() */ #define getsc() (*source->str != '\0' && *source->str != '\\' \ && !backslash_skip ? *source->str++ : getsc_bn()) /* optimized getsc__() */ #define getsc_() ((*source->str != '\0') ? *source->str++ : getsc__()) #define STATE_BSIZE 32 #define PUSH_STATE(s) do { \ if (++statep == state_info.end) \ statep = push_state_(&state_info, statep); \ state = statep->ls_state = (s); \ } while (0) #define POP_STATE() do { \ if (--statep == state_info.base) \ statep = pop_state_(&state_info, statep); \ state = statep->ls_state; \ } while (0) /* * Lexical analyzer * * tokens are not regular expressions, they are LL(1). * for example, "${var:-${PWD}}", and "$(size $(whence ksh))". * hence the state stack. */ int yylex(cf) int cf; { Lex_state states[STATE_BSIZE], *statep; State_info state_info; register int c, state; XString ws; /* expandable output word */ register char *wp; /* output word pointer */ char *sp, *dp; int c2; Again: states[0].ls_state = -1; states[0].ls_info.base = (Lex_state *) 0; statep = &states[1]; state_info.base = states; state_info.end = &states[STATE_BSIZE]; Xinit(ws, wp, 64, ATEMP); backslash_skip = 0; ignore_backslash_newline = 0; if (cf&ONEWORD) state = SWORD; #ifdef KSH else if (cf&LETEXPR) { *wp++ = OQUOTE; /* enclose arguments in (double) quotes */ state = SLETPAREN; statep->ls_sletparen.nparen = 0; } #endif /* KSH */ else { /* normal lexing */ state = (cf & HEREDELIM) ? SHEREDELIM : SBASE; while ((c = getsc()) == ' ' || c == '\t') ; if (c == '#') { ignore_backslash_newline++; while ((c = getsc()) != '\0' && c != '\n') ; ignore_backslash_newline--; } ungetsc(c); } if (source->flags & SF_ALIAS) { /* trailing ' ' in alias definition */ source->flags &= ~SF_ALIAS; /* In POSIX mode, a trailing space only counts if we are * parsing a simple command */ if (!Flag(FPOSIX) || (cf & CMDWORD)) cf |= ALIAS; } /* Initial state: one of SBASE SHEREDELIM SWORD SASPAREN */ statep->ls_state = state; /* collect non-special or quoted characters to form word */ while (!((c = getsc()) == 0 || ((state == SBASE || state == SHEREDELIM) && ctype(c, C_LEX1)))) { Xcheck(ws, wp); switch (state) { case SBASE: if (c == '[' && (cf & (VARASN|ARRAYVAR))) { *wp = EOS; /* temporary */ if (is_wdvarname(Xstring(ws, wp), FALSE)) { char *p, *tmp; if (arraysub(&tmp)) { *wp++ = CHAR; *wp++ = c; for (p = tmp; *p; ) { Xcheck(ws, wp); *wp++ = CHAR; *wp++ = *p++; } afree(tmp, ATEMP); break; } else { Source *s; s = pushs(SREREAD, source->areap); s->start = s->str = s->u.freeme = tmp; s->next = source; source = s; } } *wp++ = CHAR; *wp++ = c; break; } /* fall through.. */ Sbase1: /* includes *(...|...) pattern (*+?@!) */ #ifdef KSH if (c == '*' || c == '@' || c == '+' || c == '?' || c == '!') { c2 = getsc(); if (c2 == '(' /*)*/ ) { *wp++ = OPAT; *wp++ = c; PUSH_STATE(SPATTERN); break; } ungetsc(c2); } #endif /* KSH */ /* fall through.. */ Sbase2: /* doesn't include *(...|...) pattern (*+?@!) */ switch (c) { case '\\': c = getsc(); #ifdef OS2 if (isalnum(c)) { *wp++ = CHAR, *wp++ = '\\'; *wp++ = CHAR, *wp++ = c; } else #endif if (c) /* trailing \ is lost */ *wp++ = QCHAR, *wp++ = c; break; case '\'': *wp++ = OQUOTE; ignore_backslash_newline++; PUSH_STATE(SSQUOTE); break; case '"': *wp++ = OQUOTE; PUSH_STATE(SDQUOTE); break; default: goto Subst; } break; Subst: switch (c) { case '\\': c = getsc(); switch (c) { case '"': case '\\': case '$': case '`': *wp++ = QCHAR, *wp++ = c; break; default: Xcheck(ws, wp); if (c) { /* trailing \ is lost */ *wp++ = CHAR, *wp++ = '\\'; *wp++ = CHAR, *wp++ = c; } break; } break; case '$': c = getsc(); if (c == '(') /*)*/ { c = getsc(); if (c == '(') /*)*/ { PUSH_STATE(SASPAREN); statep->ls_sasparen.nparen = 2; statep->ls_sasparen.start = Xsavepos(ws, wp); *wp++ = EXPRSUB; } else { ungetsc(c); PUSH_STATE(SCSPAREN); statep->ls_scsparen.nparen = 1; statep->ls_scsparen.csstate = 0; *wp++ = COMSUB; } } else if (c == '{') /*}*/ { *wp++ = OSUBST; *wp++ = '{'; /*}*/ wp = get_brace_var(&ws, wp); c = getsc(); /* allow :# and :% (ksh88 compat) */ if (c == ':') { *wp++ = CHAR, *wp++ = c; c = getsc(); } /* If this is a trim operation, * treat (,|,) specially in STBRACE. */ if (c == '#' || c == '%') { ungetsc(c); PUSH_STATE(STBRACE); } else { ungetsc(c); PUSH_STATE(SBRACE); } } else if (ctype(c, C_ALPHA)) { *wp++ = OSUBST; *wp++ = 'X'; do { Xcheck(ws, wp); *wp++ = c; c = getsc(); } while (ctype(c, C_ALPHA|C_DIGIT)); *wp++ = '\0'; *wp++ = CSUBST; *wp++ = 'X'; ungetsc(c); } else if (ctype(c, C_DIGIT|C_VAR1)) { Xcheck(ws, wp); *wp++ = OSUBST; *wp++ = 'X'; *wp++ = c; *wp++ = '\0'; *wp++ = CSUBST; *wp++ = 'X'; } else { *wp++ = CHAR, *wp++ = '$'; ungetsc(c); } break; case '`': PUSH_STATE(SBQUOTE); *wp++ = COMSUB; /* Need to know if we are inside double quotes * since sh/at&t-ksh translate the \" to " in * "`..\"..`". * This is not done in posix mode (section * 3.2.3, Double Quotes: "The backquote shall * retain its special meaning introducing the * other form of command substitution (see * 3.6.3). The portion of the quoted string * from the initial backquote and the * characters up to the next backquote that * is not preceded by a backslash (having * escape characters removed) defines that * command whose output replaces `...` when * the word is expanded." * Section 3.6.3, Command Substitution: * "Within the backquoted style of command * substitution, backslash shall retain its * literal meaning, except when followed by * $ ` \."). */ statep->ls_sbquote.indquotes = 0; if (!Flag(FPOSIX)) { Lex_state *s = statep; Lex_state *base = state_info.base; while (1) { for (; s != base; s--) { if (s->ls_state == SDQUOTE) { statep->ls_sbquote.indquotes = 1; break; } } if (s != base) break; if (!(s = s->ls_info.base)) break; base = s-- - STATE_BSIZE; } } break; default: *wp++ = CHAR, *wp++ = c; } break; case SSQUOTE: if (c == '\'') { POP_STATE(); *wp++ = CQUOTE; ignore_backslash_newline--; } else *wp++ = QCHAR, *wp++ = c; break; case SDQUOTE: if (c == '"') { POP_STATE(); *wp++ = CQUOTE; } else goto Subst; break; case SCSPAREN: /* $( .. ) */ /* todo: deal with $(...) quoting properly * kludge to partly fake quoting inside $(..): doesn't * really work because nested $(..) or ${..} inside * double quotes aren't dealt with. */ switch (statep->ls_scsparen.csstate) { case 0: /* normal */ switch (c) { case '(': statep->ls_scsparen.nparen++; break; case ')': statep->ls_scsparen.nparen--; break; case '\\': statep->ls_scsparen.csstate = 1; break; case '"': statep->ls_scsparen.csstate = 2; break; case '\'': statep->ls_scsparen.csstate = 4; ignore_backslash_newline++; break; } break; case 1: /* backslash in normal mode */ case 3: /* backslash in double quotes */ --statep->ls_scsparen.csstate; break; case 2: /* double quotes */ if (c == '"') statep->ls_scsparen.csstate = 0; else if (c == '\\') statep->ls_scsparen.csstate = 3; break; case 4: /* single quotes */ if (c == '\'') { statep->ls_scsparen.csstate = 0; ignore_backslash_newline--; } break; } if (statep->ls_scsparen.nparen == 0) { POP_STATE(); *wp++ = 0; /* end of COMSUB */ } else *wp++ = c; break; case SASPAREN: /* $(( .. )) */ /* todo: deal with $((...); (...)) properly */ /* XXX should nest using existing state machine * (embed "..", $(...), etc.) */ if (c == '(') statep->ls_sasparen.nparen++; else if (c == ')') { statep->ls_sasparen.nparen--; if (statep->ls_sasparen.nparen == 1) { /*(*/ if ((c2 = getsc()) == ')') { POP_STATE(); *wp++ = 0; /* end of EXPRSUB */ break; } else { char *s; ungetsc(c2); /* mismatched parenthesis - * assume we were really * parsing a $(..) expression */ s = Xrestpos(ws, wp, statep->ls_sasparen.start); memmove(s + 1, s, wp - s); *s++ = COMSUB; *s = '('; /*)*/ wp++; statep->ls_scsparen.nparen = 1; statep->ls_scsparen.csstate = 0; state = statep->ls_state = SCSPAREN; } } } *wp++ = c; break; case SBRACE: /*{*/ if (c == '}') { POP_STATE(); *wp++ = CSUBST; *wp++ = /*{*/ '}'; } else goto Sbase1; break; case STBRACE: /* Same as SBRACE, except (,|,) treated specially */ /*{*/ if (c == '}') { POP_STATE(); *wp++ = CSUBST; *wp++ = /*{*/ '}'; } else if (c == '|') { *wp++ = SPAT; } else if (c == '(') { *wp++ = OPAT; *wp++ = ' '; /* simile for @ */ PUSH_STATE(SPATTERN); } else goto Sbase1; break; case SBQUOTE: if (c == '`') { *wp++ = 0; POP_STATE(); } else if (c == '\\') { switch (c = getsc()) { case '\\': case '$': case '`': *wp++ = c; break; case '"': if (statep->ls_sbquote.indquotes) { *wp++ = c; break; } /* fall through.. */ default: if (c) { /* trailing \ is lost */ *wp++ = '\\'; *wp++ = c; } break; } } else *wp++ = c; break; case SWORD: /* ONEWORD */ goto Subst; #ifdef KSH case SLETPAREN: /* LETEXPR: (( ... )) */ /*(*/ if (c == ')') { if (statep->ls_sletparen.nparen > 0) --statep->ls_sletparen.nparen; /*(*/ else if ((c2 = getsc()) == ')') { c = 0; *wp++ = CQUOTE; goto Done; } else ungetsc(c2); } else if (c == '(') /* parenthesis inside quotes and backslashes * are lost, but at&t ksh doesn't count them * either */ ++statep->ls_sletparen.nparen; goto Sbase2; #endif /* KSH */ case SHEREDELIM: /* <<,<<- delimiter */ /* XXX chuck this state (and the next) - use * the existing states ($ and \`..` should be * stripped of their specialness after the * fact). */ /* here delimiters need a special case since * $ and `..` are not to be treated specially */ if (c == '\\') { c = getsc(); if (c) { /* trailing \ is lost */ *wp++ = QCHAR; *wp++ = c; } } else if (c == '\'') { PUSH_STATE(SSQUOTE); *wp++ = OQUOTE; ignore_backslash_newline++; } else if (c == '"') { state = statep->ls_state = SHEREDQUOTE; *wp++ = OQUOTE; } else { *wp++ = CHAR; *wp++ = c; } break; case SHEREDQUOTE: /* " in <<,<<- delimiter */ if (c == '"') { *wp++ = CQUOTE; state = statep->ls_state = SHEREDELIM; } else { if (c == '\\') { switch (c = getsc()) { case '\\': case '"': case '$': case '`': break; default: if (c) { /* trailing \ lost */ *wp++ = CHAR; *wp++ = '\\'; } break; } } *wp++ = CHAR; *wp++ = c; } break; case SPATTERN: /* in *(...|...) pattern (*+?@!) */ if ( /*(*/ c == ')') { *wp++ = CPAT; POP_STATE(); } else if (c == '|') { *wp++ = SPAT; } else if (c == '(') { *wp++ = OPAT; *wp++ = ' '; /* simile for @ */ PUSH_STATE(SPATTERN); } else goto Sbase1; break; } } Done: Xcheck(ws, wp); if (statep != &states[1]) /* XXX figure out what is missing */ yyerror("no closing quote\n"); /* This done to avoid tests for SHEREDELIM wherever SBASE tested */ if (state == SHEREDELIM) state = SBASE; dp = Xstring(ws, wp); if ((c == '<' || c == '>') && state == SBASE && ((c2 = Xlength(ws, wp)) == 0 || (c2 == 2 && dp[0] == CHAR && digit(dp[1])))) { struct ioword *iop = (struct ioword *) alloc(sizeof(*iop), ATEMP); if (c2 == 2) iop->unit = dp[1] - '0'; else iop->unit = c == '>'; /* 0 for <, 1 for > */ c2 = getsc(); /* <<, >>, <> are ok, >< is not */ if (c == c2 || (c == '<' && c2 == '>')) { iop->flag = c == c2 ? (c == '>' ? IOCAT : IOHERE) : IORDWR; if (iop->flag == IOHERE) if ((c2 = getsc()) == '-') iop->flag |= IOSKIP; else ungetsc(c2); } else if (c2 == '&') iop->flag = IODUP | (c == '<' ? IORDUP : 0); else { iop->flag = c == '>' ? IOWRITE : IOREAD; if (c == '>' && c2 == '|') iop->flag |= IOCLOB; else ungetsc(c2); } iop->name = (char *) 0; iop->delim = (char *) 0; iop->heredoc = (char *) 0; Xfree(ws, wp); /* free word */ yylval.iop = iop; return REDIR; } if (wp == dp && state == SBASE) { Xfree(ws, wp); /* free word */ /* no word, process LEX1 character */ switch (c) { default: return c; case '|': case '&': case ';': if ((c2 = getsc()) == c) c = (c == ';') ? BREAK : (c == '|') ? LOGOR : (c == '&') ? LOGAND : YYERRCODE; #ifdef KSH else if (c == '|' && c2 == '&') c = COPROC; #endif /* KSH */ else ungetsc(c2); return c; case '\n': gethere(); if (cf & CONTIN) goto Again; return c; case '(': /*)*/ #ifdef KSH if ((c2 = getsc()) == '(') /*)*/ /* XXX need to handle ((...); (...)) */ c = MDPAREN; else ungetsc(c2); #endif /* KSH */ return c; /*(*/ case ')': return c; } } *wp++ = EOS; /* terminate word */ yylval.cp = Xclose(ws, wp); if (state == SWORD #ifdef KSH || state == SLETPAREN #endif /* KSH */ ) /* ONEWORD? */ return LWORD; ungetsc(c); /* unget terminator */ /* copy word to unprefixed string ident */ for (sp = yylval.cp, dp = ident; dp < ident+IDENT && (c = *sp++) == CHAR; ) *dp++ = *sp++; /* Make sure the ident array stays '\0' paded */ memset(dp, 0, (ident+IDENT) - dp + 1); if (c != EOS) *ident = '\0'; /* word is not unquoted */ if (*ident != '\0' && (cf&(KEYWORD|ALIAS))) { struct tbl *p; int h = hash(ident); /* { */ if ((cf & KEYWORD) && (p = tsearch(&keywords, ident, h)) && (!(cf & ESACONLY) || p->val.i == ESAC || p->val.i == '}')) { afree(yylval.cp, ATEMP); return p->val.i; } if ((cf & ALIAS) && (p = tsearch(&aliases, ident, h)) && (p->flag & ISSET)) { register Source *s; for (s = source; s->type == SALIAS; s = s->next) if (s->u.tblp == p) return LWORD; /* push alias expansion */ s = pushs(SALIAS, source->areap); s->start = s->str = p->val.s; s->u.tblp = p; s->next = source; source = s; afree(yylval.cp, ATEMP); goto Again; } } return LWORD; } static void gethere() { register struct ioword **p; for (p = heres; p < herep; p++) readhere(*p); herep = heres; } /* * read "<delim, 0); if (!(iop->flag & IOEVAL)) ignore_backslash_newline++; Xinit(xs, xp, 256, ATEMP); for (;;) { eofp = eof; skiptabs = iop->flag & IOSKIP; xpos = Xsavepos(xs, xp); while ((c = getsc()) != 0) { if (skiptabs) { if (c == '\t') continue; skiptabs = 0; } if (c != *eofp) break; Xcheck(xs, xp); Xput(xs, xp, c); eofp++; } /* Allow EOF here so commands with out trailing newlines * will work (eg, ksh -c '...', $(...), etc). */ if (*eofp == '\0' && (c == 0 || c == '\n')) { xp = Xrestpos(xs, xp, xpos); break; } ungetsc(c); while ((c = getsc()) != '\n') { if (c == 0) yyerror("here document `%s' unclosed\n", eof); Xcheck(xs, xp); Xput(xs, xp, c); } Xcheck(xs, xp); Xput(xs, xp, c); } Xput(xs, xp, '\0'); iop->heredoc = Xclose(xs, xp); if (!(iop->flag & IOEVAL)) ignore_backslash_newline--; } void #ifdef HAVE_PROTOTYPES yyerror(const char *fmt, ...) #else yyerror(fmt, va_alist) const char *fmt; va_dcl #endif { va_list va; /* pop aliases and re-reads */ while (source->type == SALIAS || source->type == SREREAD) source = source->next; source->str = null; /* zap pending input */ error_prefix(TRUE); SH_VA_START(va, fmt); shf_vfprintf(shl_out, fmt, va); va_end(va); errorf(null); } /* * input for yylex with alias expansion */ Source * pushs(type, areap) int type; Area *areap; { register Source *s; s = (Source *) alloc(sizeof(Source), areap); s->type = type; s->str = null; s->start = NULL; s->line = 0; s->errline = 0; s->file = NULL; s->flags = 0; s->next = NULL; s->areap = areap; if (type == SFILE || type == SSTDIN) { char *dummy; Xinit(s->xs, dummy, 256, s->areap); } else memset(&s->xs, 0, sizeof(s->xs)); return s; } static int getsc__() { register Source *s = source; register int c; while ((c = *s->str++) == 0) { s->str = NULL; /* return 0 for EOF by default */ switch (s->type) { case SEOF: s->str = null; return 0; case SSTDIN: case SFILE: getsc_line(s); break; case SWSTR: break; case SSTRING: break; case SWORDS: s->start = s->str = *s->u.strv++; s->type = SWORDSEP; break; case SWORDSEP: if (*s->u.strv == NULL) { s->start = s->str = newline; s->type = SEOF; } else { s->start = s->str = space; s->type = SWORDS; } break; case SALIAS: if (s->flags & SF_ALIASEND) { /* pass on an unused SF_ALIAS flag */ source = s->next; source->flags |= s->flags & SF_ALIAS; s = source; } else if (*s->u.tblp->val.s && isspace(strchr(s->u.tblp->val.s, 0)[-1])) { source = s = s->next; /* pop source stack */ /* Note that this alias ended with a space, * enabling alias expansion on the following * word. */ s->flags |= SF_ALIAS; } else { /* At this point, we need to keep the current * alias in the source list so recursive * aliases can be detected and we also need * to return the next character. Do this * by temporarily popping the alias to get * the next character and then put it back * in the source list with the SF_ALIASEND * flag set. */ source = s->next; /* pop source stack */ source->flags |= s->flags & SF_ALIAS; c = getsc__(); if (c) { s->flags |= SF_ALIASEND; s->ugbuf[0] = c; s->ugbuf[1] = '\0'; s->start = s->str = s->ugbuf; s->next = source; source = s; } else { s = source; /* avoid reading eof twice */ s->str = NULL; break; } } continue; case SREREAD: if (s->start != s->ugbuf) /* yuck */ afree(s->u.freeme, ATEMP); source = s = s->next; continue; } if (s->str == NULL) { s->type = SEOF; s->start = s->str = null; return '\0'; } if (s->flags & SF_ECHO) { shf_puts(s->str, shl_out); shf_flush(shl_out); } } return c; } static void getsc_line(s) Source *s; { char *xp = Xstring(s->xs, xp); int interactive = Flag(FTALKING) && s->type == SSTDIN; int have_tty = interactive && (s->flags & SF_TTY); /* Done here to ensure nothing odd happens when a timeout occurs */ XcheckN(s->xs, xp, LINE); *xp = '\0'; s->start = s->str = xp; #ifdef KSH if (have_tty && ksh_tmout) { ksh_tmout_state = TMOUT_READING; alarm(ksh_tmout); } #endif /* KSH */ #ifdef EDIT if (have_tty && (0 # ifdef VI || Flag(FVI) # endif /* VI */ # ifdef EMACS || Flag(FEMACS) || Flag(FGMACS) # endif /* EMACS */ )) { int nread; nread = x_read(xp, LINE); if (nread < 0) /* read error */ nread = 0; xp[nread] = '\0'; xp += nread; } else #endif /* EDIT */ { if (interactive) { pprompt(prompt, 0); } else s->line++; while (1) { char *p = shf_getse(xp, Xnleft(s->xs, xp), s->u.shf); if (!p && shf_error(s->u.shf) && shf_errno(s->u.shf) == EINTR) { shf_clearerr(s->u.shf); if (trap) runtraps(0); continue; } if (!p || (xp = p, xp[-1] == '\n')) break; /* double buffer size */ xp++; /* move past null so doubling works... */ XcheckN(s->xs, xp, Xlength(s->xs, xp)); xp--; /* ...and move back again */ } /* flush any unwanted input so other programs/builtins * can read it. Not very optimal, but less error prone * than flushing else where, dealing with redirections, * etc.. * todo: reduce size of shf buffer (~128?) if SSTDIN */ if (s->type == SSTDIN) shf_flush(s->u.shf); } /* XXX: temporary kludge to restore source after a * trap may have been executed. */ source = s; #ifdef KSH if (have_tty && ksh_tmout) { ksh_tmout_state = TMOUT_EXECUTING; alarm(0); } #endif /* KSH */ s->start = s->str = Xstring(s->xs, xp); strip_nuls(Xstring(s->xs, xp), Xlength(s->xs, xp)); /* Note: if input is all nulls, this is not eof */ if (Xlength(s->xs, xp) == 0) { /* EOF */ if (s->type == SFILE) shf_fdclose(s->u.shf); s->str = NULL; } else if (interactive) { #ifdef HISTORY char *p = Xstring(s->xs, xp); if (cur_prompt == PS1) while (*p && ctype(*p, C_IFS) && ctype(*p, C_IFSWS)) p++; if (*p) { # ifdef EASY_HISTORY if (cur_prompt == PS2) histappend(Xstring(s->xs, xp), 1); else # endif /* EASY_HISTORY */ { s->line++; histsave(s->line, s->str, 1); } } #endif /* HISTORY */ } if (interactive) set_prompt(PS2, (Source *) 0); } void set_prompt(to, s) int to; Source *s; { cur_prompt = to; switch (to) { case PS1: /* command */ #ifdef KSH /* Substitute ! and !! here, before substitutions are done * so ! in expanded variables are not expanded. * NOTE: this is not what at&t ksh does (it does it after * substitutions, POSIX doesn't say which is to be done. */ { struct shf *shf; char *ps1; Area *saved_atemp; ps1 = str_val(global("PS1")); shf = shf_sopen((char *) 0, strlen(ps1) * 2, SHF_WR | SHF_DYNAMIC, (struct shf *) 0); while (*ps1) { if (*ps1 != '!' || *++ps1 == '!') shf_putchar(*ps1++, shf); else shf_fprintf(shf, "%d", s ? s->line + 1 : 0); } ps1 = shf_sclose(shf); saved_atemp = ATEMP; newenv(E_ERRH); if (ksh_sigsetjmp(e->jbuf, 0)) { prompt = safe_prompt; /* Don't print an error - assume it has already * been printed. Reason is we may have forked * to run a command and the child may be * unwinding its stack through this code as it * exits. */ } else prompt = str_save(substitute(ps1, 0), saved_atemp); quitenv(); } #else /* KSH */ prompt = str_val(global("PS1")); #endif /* KSH */ break; case PS2: /* command continuation */ prompt = str_val(global("PS2")); break; } } /* See also related routine, promptlen() in edit.c */ void pprompt(cp, ntruncate) const char *cp; int ntruncate; { #if 0 char nbuf[32]; int c; while (*cp != 0) { if (*cp != '!') c = *cp++; else if (*++cp == '!') c = *cp++; else { int len; char *p; shf_snprintf(p = nbuf, sizeof(nbuf), "%d", source->line + 1); len = strlen(nbuf); if (ntruncate) { if (ntruncate >= len) { ntruncate -= len; continue; } p += ntruncate; len -= ntruncate; ntruncate = 0; } shf_write(p, len, shl_out); continue; } if (ntruncate) --ntruncate; else shf_putc(c, shl_out); } #endif /* 0 */ shf_puts(cp + ntruncate, shl_out); shf_flush(shl_out); } /* Read the variable part of a ${...} expression (ie, up to but not including * the :[-+?=#%] or close-brace. */ static char * get_brace_var(wsp, wp) XString *wsp; char *wp; { enum parse_state { PS_INITIAL, PS_SAW_HASH, PS_IDENT, PS_NUMBER, PS_VAR1, PS_END } state; char c; state = PS_INITIAL; while (1) { c = getsc(); /* State machine to figure out where the variable part ends. */ switch (state) { case PS_INITIAL: if (c == '#') { state = PS_SAW_HASH; break; } /* fall through.. */ case PS_SAW_HASH: if (letter(c)) state = PS_IDENT; else if (digit(c)) state = PS_NUMBER; else if (ctype(c, C_VAR1)) state = PS_VAR1; else state = PS_END; break; case PS_IDENT: if (!letnum(c)) { state = PS_END; if (c == '[') { char *tmp, *p; if (!arraysub(&tmp)) yyerror("missing ]\n"); *wp++ = c; for (p = tmp; *p; ) { Xcheck(*wsp, wp); *wp++ = *p++; } afree(tmp, ATEMP); c = getsc(); /* the ] */ } } break; case PS_NUMBER: if (!digit(c)) state = PS_END; break; case PS_VAR1: state = PS_END; break; case PS_END: /* keep gcc happy */ break; } if (state == PS_END) { *wp++ = '\0'; /* end of variable part */ ungetsc(c); break; } Xcheck(*wsp, wp); *wp++ = c; } return wp; } /* * Save an array subscript - returns true if matching bracket found, false * if eof or newline was found. * (Returned string double null terminated) */ static int arraysub(strp) char **strp; { XString ws; char *wp; char c; int depth = 1; /* we are just past the initial [ */ Xinit(ws, wp, 32, ATEMP); do { c = getsc(); Xcheck(ws, wp); *wp++ = c; if (c == '[') depth++; else if (c == ']') depth--; } while (depth > 0 && c && c != '\n'); *wp++ = '\0'; *strp = Xclose(ws, wp); return depth == 0 ? 1 : 0; } /* Unget a char: handles case when we are already at the start of the buffer */ static const char * ungetsc(c) int c; { if (backslash_skip) backslash_skip--; /* Don't unget eof... */ if (source->str == null && c == '\0') return source->str; if (source->str > source->start) source->str--; else { Source *s; s = pushs(SREREAD, source->areap); s->ugbuf[0] = c; s->ugbuf[1] = '\0'; s->start = s->str = s->ugbuf; s->next = source; source = s; } return source->str; } /* Called to get a char that isn't a \newline sequence. */ static int getsc_bn ARGS((void)) { int c, c2; if (ignore_backslash_newline) return getsc_(); if (backslash_skip == 1) { backslash_skip = 2; return getsc_(); } backslash_skip = 0; while (1) { c = getsc_(); if (c == '\\') { if ((c2 = getsc_()) == '\n') /* ignore the \newline; get the next char... */ continue; ungetsc(c2); backslash_skip = 1; } return c; } } static Lex_state * push_state_(si, old_end) State_info *si; Lex_state *old_end; { Lex_state *new = alloc(sizeof(Lex_state) * STATE_BSIZE, ATEMP); new[0].ls_info.base = old_end; si->base = &new[0]; si->end = &new[STATE_BSIZE]; return &new[1]; } static Lex_state * pop_state_(si, old_end) State_info *si; Lex_state *old_end; { Lex_state *old_base = si->base; si->base = old_end->ls_info.base - STATE_BSIZE; si->end = old_end->ls_info.base; afree(old_base, ATEMP); return si->base + STATE_BSIZE - 1;; } /sys/src/ape/cmd/pdksh/lex.h 664 sys sys 1367613437 3823 /* * Source input, lexer and parser */ /* $Id: lex.h,v 1.4 1994/05/31 13:34:34 michael Exp $ */ #define IDENT 64 typedef struct source Source; struct source { const char *str; /* input pointer */ int type; /* input type */ const char *start; /* start of current buffer */ union { char **strv; /* string [] */ struct shf *shf; /* shell file */ struct tbl *tblp; /* alias (SALIAS) */ char *freeme; /* also for SREREAD */ } u; char ugbuf[2]; /* buffer for ungetsc() (SREREAD) and * alias (SALIAS) */ int line; /* line number */ int errline; /* line the error occured on (0 if not set) */ const char *file; /* input file name */ int flags; /* SF_* */ Area *areap; XString xs; /* input buffer */ Source *next; /* stacked source */ }; /* Source.type values */ #define SEOF 0 /* input EOF */ #define SFILE 1 /* file input */ #define SSTDIN 2 /* read stdin */ #define SSTRING 3 /* string */ #define SWSTR 4 /* string without \n */ #define SWORDS 5 /* string[] */ #define SWORDSEP 6 /* string[] seperator */ #define SALIAS 7 /* alias expansion */ #define SREREAD 8 /* read ahead to be re-scanned */ /* Source.flags values */ #define SF_ECHO BIT(0) /* echo input to shlout */ #define SF_ALIAS BIT(1) /* faking space at end of alias */ #define SF_ALIASEND BIT(2) /* faking space at end of alias */ #define SF_TTY BIT(3) /* type == SSTDIN & it is a tty */ /* * states while lexing word */ #define SBASE 0 /* outside any lexical constructs */ #define SWORD 1 /* implicit quoting for substitute() */ #ifdef KSH #define SLETPAREN 2 /* inside (( )), implicit quoting */ #endif /* KSH */ #define SSQUOTE 3 /* inside '' */ #define SDQUOTE 4 /* inside "" */ #define SBRACE 5 /* inside ${} */ #define SCSPAREN 6 /* inside $() */ #define SBQUOTE 7 /* inside `` */ #define SASPAREN 8 /* inside $(( )) */ #define SHEREDELIM 9 /* parsing <<,<<- delimiter */ #define SHEREDQUOTE 10 /* parsing " in <<,<<- delimiter */ #define SPATTERN 11 /* parsing *(...|...) pattern (*+?@!) */ #define STBRACE 12 /* parsing ${..[#%]..} */ typedef union { int i; char *cp; char **wp; struct op *o; struct ioword *iop; } YYSTYPE; /* If something is added here, add it to tokentab[] in syn.c as well */ #define LWORD 256 #define LOGAND 257 /* && */ #define LOGOR 258 /* || */ #define BREAK 259 /* ;; */ #define IF 260 #define THEN 261 #define ELSE 262 #define ELIF 263 #define FI 264 #define CASE 265 #define ESAC 266 #define FOR 267 #define SELECT 268 #define WHILE 269 #define UNTIL 270 #define DO 271 #define DONE 272 #define IN 273 #define FUNCTION 274 #define TIME 275 #define REDIR 276 #ifdef KSH #define MDPAREN 277 /* (( )) */ #endif /* KSH */ #define BANG 278 /* ! */ #define DBRACKET 279 /* [[ .. ]] */ #define COPROC 280 /* |& */ #define YYERRCODE 300 /* flags to yylex */ #define CONTIN BIT(0) /* skip new lines to complete command */ #define ONEWORD BIT(1) /* single word for substitute() */ #define ALIAS BIT(2) /* recognize alias */ #define KEYWORD BIT(3) /* recognize keywords */ #define LETEXPR BIT(4) /* get expression inside (( )) */ #define VARASN BIT(5) /* check for var=word */ #define ARRAYVAR BIT(6) /* parse x[1 & 2] as one word */ #define ESACONLY BIT(7) /* only accept esac keyword */ #define CMDWORD BIT(8) /* parsing simple command (alias related) */ #define HEREDELIM BIT(9) /* parsing <<,<<- delimiter */ #define HERES 10 /* max << in line */ EXTERN Source *source; /* yyparse/yylex source */ EXTERN YYSTYPE yylval; /* result from yylex */ EXTERN struct ioword *heres [HERES], **herep; EXTERN char ident [IDENT+1]; #ifdef HISTORY # define HISTORYSIZE 128 /* size of saved history */ EXTERN char **history; /* saved commands */ EXTERN char **histptr; /* last history item */ EXTERN int histsize; /* history size */ #endif /* HISTORY */ /sys/src/ape/cmd/pdksh/mail.c 664 sys sys 1367613437 4050 /* * Mailbox checking code by Robert J. Gibson, adapted for PD ksh by * John R. MacMillan */ #include "config.h" #ifdef KSH #include "sh.h" #include "ksh_stat.h" #include "ksh_time.h" #define MBMESSAGE "you have mail in $_" typedef struct mbox { struct mbox *mb_next; /* next mbox in list */ char *mb_path; /* path to mail file */ char *mb_msg; /* to announce arrival of new mail */ time_t mb_mtime; /* mtime of mail file */ } mbox_t; /* * $MAILPATH is a linked list of mboxes. $MAIL is a treated as a * special case of $MAILPATH, where the list has only one node. The * same list is used for both since they are exclusive. */ static mbox_t *mplist; static mbox_t mbox; static time_t mlastchkd; /* when mail was last checked */ static time_t mailcheck_interval; static void munset ARGS((mbox_t *mlist)); /* free mlist and mval */ static mbox_t * mballoc ARGS((char *p, char *m)); /* allocate a new mbox */ static void mprintit ARGS((mbox_t *mbp)); void mcheck() { register mbox_t *mbp; time_t now; struct tbl *vp; struct stat stbuf; now = time((time_t *) 0); if (mlastchkd == 0) mlastchkd = now; if (now - mlastchkd >= mailcheck_interval) { mlastchkd = now; if (mplist) mbp = mplist; else if ((vp = global("MAIL")) && (vp->flag & ISSET)) mbp = &mbox; else mbp = NULL; while (mbp) { if (mbp->mb_path && stat(mbp->mb_path, &stbuf) == 0 && S_ISREG(stbuf.st_mode)) { if (stbuf.st_size && mbp->mb_mtime != stbuf.st_mtime && stbuf.st_atime <= stbuf.st_mtime) mprintit(mbp); mbp->mb_mtime = stbuf.st_mtime; } else { /* * Some mail readers remove the mail * file if all mail is read. If file * does not exist, assume this is the * case and set mtime to zero. */ mbp->mb_mtime = 0; } mbp = mbp->mb_next; } } } void mcset(interval) long interval; { mailcheck_interval = interval; } void mbset(p) register char *p; { struct stat stbuf; if (mbox.mb_msg) afree((void *)mbox.mb_msg, APERM); if (mbox.mb_path) afree((void *)mbox.mb_path, APERM); /* Save a copy to protect from export (which munges the string) */ mbox.mb_path = str_save(p, APERM); mbox.mb_msg = NULL; if (p && stat(p, &stbuf) == 0 && S_ISREG(stbuf.st_mode)) mbox.mb_mtime = stbuf.st_mtime; else mbox.mb_mtime = 0; } void mpset(mptoparse) register char *mptoparse; { register mbox_t *mbp; register char *mpath, *mmsg, *mval; char *p; munset( mplist ); mplist = NULL; mval = str_save(mptoparse, APERM); while (mval) { mpath = mval; if ((mval = strchr(mval, PATHSEP)) != NULL) { *mval = '\0', mval++; } /* POSIX/bourne-shell say file%message */ for (p = mpath; (mmsg = strchr(p, '%')); ) { /* a literal percent? (POSIXism) */ if (mmsg[-1] == '\\') { /* use memmove() to avoid overlap problems */ memmove(mmsg - 1, mmsg, strlen(mmsg) + 1); p = mmsg + 1; continue; } break; } /* at&t ksh says file?message */ if (!mmsg && !Flag(FPOSIX)) mmsg = strchr(mpath, '?'); if (mmsg) { *mmsg = '\0'; mmsg++; } mbp = mballoc(mpath, mmsg); mbp->mb_next = mplist; mplist = mbp; } } static void munset(mlist) register mbox_t *mlist; { register mbox_t *mbp; while (mlist != NULL) { mbp = mlist; mlist = mbp->mb_next; if (!mlist) afree((void *)mbp->mb_path, APERM); afree((void *)mbp, APERM); } } static mbox_t * mballoc(p, m) char *p; char *m; { struct stat stbuf; register mbox_t *mbp; mbp = (mbox_t *)alloc(sizeof(mbox_t), APERM); mbp->mb_next = NULL; mbp->mb_path = p; mbp->mb_msg = m; if (stat(mbp->mb_path, &stbuf) == 0 && S_ISREG(stbuf.st_mode)) mbp->mb_mtime = stbuf.st_mtime; else mbp->mb_mtime = 0; return(mbp); } static void mprintit( mbp ) mbox_t *mbp; { struct tbl *vp; /* Ignore setstr errors here (arbitrary) */ setstr((vp = local("_", FALSE)), mbp->mb_path, KSH_RETURN_ERROR); shellf("%s\n", substitute(mbp->mb_msg ? mbp->mb_msg : MBMESSAGE, 0)); unset(vp, 0); } #endif /* KSH */ /sys/src/ape/cmd/pdksh/main.c 664 sys sys 1367613437 19342 /* * startup, main loop, enviroments and error handling */ #define EXTERN /* define EXTERNs in sh.h */ #include "sh.h" #include "ksh_stat.h" #include "ksh_time.h" extern char **environ; /* * global data */ static void reclaim ARGS((void)); static void remove_temps ARGS((struct temp *tp)); static int is_restricted ARGS((char *name)); /* * shell initialization */ static const char initifs[] = "IFS= \t\n"; static const char initsubs[] = "${PS2=> } ${PS3=#? } ${PS4=+ }"; static const char version_param[] = #ifdef KSH "KSH_VERSION" #else /* KSH */ "SH_VERSION" #endif /* KSH */ ; static const char *const initcoms [] = { "typeset", "-x", "SHELL", "PATH", "HOME", NULL, "typeset", "-r", version_param, NULL, "typeset", "-i", "PPID", NULL, "typeset", "-i", "OPTIND=1", NULL, #ifdef KSH "eval", "typeset -i RANDOM MAILCHECK=\"${MAILCHECK-600}\" SECONDS=\"${SECONDS-0}\" TMOUT=\"${TMOUT-0}\"", NULL, #endif /* KSH */ "alias", /* Standard ksh aliases */ "hash=alias -t", /* not "alias -t --": hash -r needs to work */ "type=whence -v", #ifdef JOBS "stop=kill -STOP", "suspend=kill -STOP $$", #endif #ifdef KSH "autoload=typeset -fu", "functions=typeset -f", # ifdef HISTORY "history=fc -l", # endif /* HISTORY */ "integer=typeset -i", "nohup=nohup ", "local=typeset", "r=fc -e -", #endif /* KSH */ #ifdef KSH /* Aliases that are builtin commands in at&t */ "login=exec login", "newgrp=exec newgrp", #endif /* KSH */ NULL, /* this is what at&t ksh seems to track, with the addition of emacs */ "alias", "-tU", "cat", "cc", "chmod", "cp", "date", "ed", "emacs", "grep", "ls", "mail", "make", "mv", "pr", "rm", "sed", "sh", "vi", "who", NULL, #ifdef EXTRA_INITCOMS EXTRA_INITCOMS, NULL, #endif /* EXTRA_INITCOMS */ NULL }; int main(argc, argv) int argc; register char **argv; { register int i; int argi; Source *s; struct block *l; int restricted, errexit; char **wp; struct env env; pid_t ppid; #ifdef MEM_DEBUG chmem_set_defaults("ct", 1); /* chmem_push("+c", 1); */ #endif /* MEM_DEBUG */ #ifdef OS2 setmode (0, O_BINARY); setmode (1, O_TEXT); #endif /* make sure argv[] is sane */ if (!*argv) { static const char *empty_argv[] = { "pdksh", (char *) 0 }; argv = (char **) empty_argv; argc = 1; } kshname = *argv; ainit(&aperm); /* initialize permanent Area */ /* set up base enviroment */ memset(&env, 0, sizeof(env)); env.type = E_NONE; ainit(&env.area); e = &env; newblock(); /* set up global l->vars and l->funs */ /* Do this first so output routines (eg, errorf, shellf) can work */ initio(); initvar(); initctypes(); inittraps(); #ifdef KSH coproc_init(); #endif /* KSH */ /* set up variable and command dictionaries */ tinit(&taliases, APERM, 0); tinit(&aliases, APERM, 0); tinit(&homedirs, APERM, 0); /* define shell keywords */ initkeywords(); /* define built-in commands */ tinit(&builtins, APERM, 64); /* must be 2^n (currently 40 builtins) */ for (i = 0; shbuiltins[i].name != NULL; i++) builtin(shbuiltins[i].name, shbuiltins[i].func); for (i = 0; kshbuiltins[i].name != NULL; i++) builtin(kshbuiltins[i].name, kshbuiltins[i].func); init_histvec(); def_path = DEFAULT__PATH; #if defined(HAVE_CONFSTR) && defined(_CS_PATH) { size_t len = confstr(_CS_PATH, (char *) 0, 0); char *new; if (len > 0) { confstr(_CS_PATH, new = alloc(len + 1, APERM), len + 1); def_path = new; } } #endif /* HAVE_CONFSTR && _CS_PATH */ /* Set PATH to def_path (will set the path global variable). * (import of environment below will probably change this setting). */ { struct tbl *vp = global("PATH"); /* setstr can't fail here */ setstr(vp, def_path, KSH_RETURN_ERROR); } /* Turn on nohup by default for how - will change to off * by default once people are aware of its existance * (at&t ksh does not have a nohup option - it always sends * the hup). */ Flag(FNOHUP) = 1; /* Turn on brace expansion by default. At&t ksh's that have * alternation always have it on. BUT, posix doesn't have * brace expansion, so set this before setting up FPOSIX * (change_flag() clears FBRACEEXPAND when FPOSIX is set). */ #ifdef BRACE_EXPAND Flag(FBRACEEXPAND) = 1; #endif /* BRACE_EXPAND */ /* set posix flag just before environment so that it will have * exactly the same effect as the POSIXLY_CORRECT environment * variable. If this needs to be done sooner to ensure correct posix * operation, an initial scan of the environment will also have * done sooner. */ #ifdef POSIXLY_CORRECT change_flag(FPOSIX, OF_SPECIAL, 1); #endif /* POSIXLY_CORRECT */ /* import enviroment */ if (environ != NULL) for (wp = environ; *wp != NULL; wp++) typeset(*wp, IMPORT|EXPORT, 0, 0, 0); kshpid = procpid = getpid(); typeset(initifs, 0, 0, 0, 0); /* for security */ /* assign default shell variable values */ substitute(initsubs, 0); /* Figure out the current working directory and set $PWD */ { struct stat s_pwd, s_dot; struct tbl *pwd_v = global("PWD"); char *pwd = str_val(pwd_v); char *pwdx = pwd; /* Try to use existing $PWD if it is valid */ if (!ISABSPATH(pwd) || stat(pwd, &s_pwd) < 0 || stat(".", &s_dot) < 0 || s_pwd.st_dev != s_dot.st_dev || s_pwd.st_ino != s_dot.st_ino) pwdx = (char *) 0; set_current_wd(pwdx); if (current_wd[0]) simplify_path(current_wd); /* Only set pwd if we know where we are or if it had a * bogus value */ if (current_wd[0] || pwd != null) /* setstr can't fail here */ setstr(pwd_v, current_wd, KSH_RETURN_ERROR); } ppid = getppid(); setint(global("PPID"), (long) ppid); #ifdef KSH setint(global("RANDOM"), (long) (time((time_t *)0) * kshpid * ppid)); #endif /* KSH */ /* setstr can't fail here */ setstr(global(version_param), ksh_version, KSH_RETURN_ERROR); /* execute initialization statements */ for (wp = (char**) initcoms; *wp != NULL; wp++) { shcomexec(wp); for (; *wp != NULL; wp++) ; } ksheuid = geteuid(); safe_prompt = ksheuid ? "$ " : "# "; { struct tbl *vp = global("PS1"); /* Set PS1 if it isn't set, or we are root and prompt doesn't * contain a #. */ if (!(vp->flag & ISSET) || (!ksheuid && !strchr(str_val(vp), '#'))) /* setstr can't fail here */ setstr(vp, safe_prompt, KSH_RETURN_ERROR); } /* Set this before parsing arguments */ Flag(FPRIVILEGED) = getuid() != ksheuid || getgid() != getegid(); /* this to note if monitor is set on command line (see below) */ Flag(FMONITOR) = 127; argi = parse_args(argv, OF_CMDLINE, (int *) 0); if (argi < 0) exit(1); if (Flag(FCOMMAND)) { s = pushs(SSTRING, ATEMP); if (!(s->start = s->str = argv[argi++])) errorf("-c requires an argument"); if (argv[argi]) kshname = argv[argi++]; } else if (argi < argc && !Flag(FSTDIN)) { s = pushs(SFILE, ATEMP); #ifdef OS2 /* a bug in os2 extproc shell processing doesn't * pass full pathnames so we have to search for it. * This changes the behavior of 'ksh arg' to search * the users search path but it can't be helped. */ s->file = search(argv[argi++], path, R_OK, (int *) 0); if (!s->file || !*s->file) s->file = argv[argi - 1]; #else s->file = argv[argi++]; #endif /* OS2 */ s->u.shf = shf_open(s->file, O_RDONLY, 0, SHF_MAPHI|SHF_CLEXEC); if (s->u.shf == NULL) { exstat = 127; /* POSIX */ errorf("%s: %s", s->file, strerror(errno)); } kshname = s->file; } else { Flag(FSTDIN) = 1; s = pushs(SSTDIN, ATEMP); s->file = ""; s->u.shf = shf_fdopen(0, SHF_RD | can_seek(0), (struct shf *) 0); if (!Flag(FNOTTALKING) && isatty(0) && isatty(2)) { Flag(FTALKING) = Flag(FTALKING_I) = 1; /* The following only if isatty(0) */ s->flags |= SF_TTY; s->u.shf->flags |= SHF_INTERRUPT; s->file = (char *) 0; } } /* This bizarreness is mandated by POSIX */ { struct stat s_stdin; if (fstat(0, &s_stdin) >= 0 && S_ISCHR(s_stdin.st_mode)) reset_nonblock(0); } /* initialize job control */ i = Flag(FMONITOR) != 127; Flag(FMONITOR) = 0; j_init(i); #ifdef EDIT /* Do this after j_init(), as tty_fd is not initialized 'til then */ if (Flag(FTALKING)) x_init(); #endif l = e->loc; l->argv = &argv[argi - 1]; l->argc = argc - argi; l->argv[0] = (char *) kshname; getopts_reset(1); /* Disable during .profile/ENV reading */ restricted = Flag(FRESTRICTED); Flag(FRESTRICTED) = 0; errexit = Flag(FERREXIT); Flag(FERREXIT) = 0; /* Do this before profile/$ENV so that if it causes problems in them, * user will know why things broke. */ if (!current_wd[0] && Flag(FTALKING)) warningf(FALSE, "Cannot determine current working directory"); if (Flag(FLOGIN)) { #ifdef OS2 char *profile; /* Try to find a profile - first see if $INIT has a value, * then try /etc/profile.ksh, then c:/usr/etc/profile.ksh. */ if (!Flag(FPRIVILEGED) && strcmp(profile = substitute("$INIT/profile.ksh", 0), "/profile.ksh")) include(profile, 0, (char **) 0, 1); else if (include("/etc/profile.ksh", 0, (char **) 0, 1) < 0) include("c:/usr/etc/profile.ksh", 0, (char **) 0, 1); if (!Flag(FPRIVILEGED)) include(substitute("$HOME/profile.ksh", 0), 0, (char **) 0, 1); #else /* OS2 */ include(KSH_SYSTEM_PROFILE, 0, (char **) 0, 1); if (!Flag(FPRIVILEGED)) include(substitute("$HOME/.profile", 0), 0, (char **) 0, 1); #endif /* OS2 */ } if (Flag(FPRIVILEGED)) include("/etc/suid_profile", 0, (char **) 0, 1); else { char *env_file; #ifndef KSH if (!Flag(FPOSIX)) env_file = null; else #endif /* !KSH */ /* include $ENV */ env_file = str_val(global("ENV")); #ifdef DEFAULT_ENV /* If env isn't set, include default environment */ if (env_file == null) env_file = DEFAULT_ENV; #endif /* DEFAULT_ENV */ env_file = substitute(env_file, DOTILDE); if (*env_file != '\0') include(env_file, 0, (char **) 0, 1); #ifdef OS2 else if (Flag(FTALKING)) include(substitute("$HOME/kshrc.ksh", 0), 0, (char **) 0, 1); #endif /* OS2 */ } if (is_restricted(argv[0]) || is_restricted(str_val(global("SHELL")))) restricted = 1; if (restricted) { static const char *const restr_com[] = { "typeset", "-r", "PATH", "ENV", "SHELL", (char *) 0 }; shcomexec((char **) restr_com); /* After typeset command... */ Flag(FRESTRICTED) = 1; } if (errexit) Flag(FERREXIT) = 1; if (Flag(FTALKING)) { hist_init(s); #ifdef KSH alarm_init(); #endif /* KSH */ } else Flag(FTRACKALL) = 1; /* set after ENV */ shell(s, TRUE); /* doesn't return */ return 0; } int include(name, argc, argv, intr_ok) const char *name; int argc; char **argv; int intr_ok; { register Source *volatile s = NULL; Source *volatile sold; struct shf *shf; char **volatile old_argv; volatile int old_argc; int i; shf = shf_open(name, O_RDONLY, 0, SHF_MAPHI|SHF_CLEXEC); if (shf == NULL) return -1; if (argv) { old_argv = e->loc->argv; old_argc = e->loc->argc; } else { old_argv = (char **) 0; old_argc = 0; } sold = source; newenv(E_INCL); i = ksh_sigsetjmp(e->jbuf, 0); if (i) { source = sold; if (s) /* Do this before quitenv(), which frees the memory */ shf_close(s->u.shf); quitenv(); if (old_argv) { e->loc->argv = old_argv; e->loc->argc = old_argc; } switch (i) { case LRETURN: case LERROR: return exstat & 0xff; /* see below */ case LINTR: /* intr_ok is set if we are including .profile or $ENV. * If user ^C's out, we don't want to kill the shell... */ if (intr_ok && (exstat - 128) != SIGTERM) return 1; /* fall through... */ case LEXIT: case LLEAVE: case LSHELL: unwind(i); /*NOREACHED*/ default: internal_errorf(1, "include: %d", i); /*NOREACHED*/ } } if (argv) { e->loc->argv = argv; e->loc->argc = argc; } s = pushs(SFILE, ATEMP); s->u.shf = shf; s->file = str_save(name, ATEMP); i = shell(s, FALSE); source = sold; shf_close(s->u.shf); quitenv(); if (old_argv) { e->loc->argv = old_argv; e->loc->argc = old_argc; } return i & 0xff; /* & 0xff to ensure value not -1 */ } int command(comm) const char *comm; { register Source *s; s = pushs(SSTRING, ATEMP); s->start = s->str = comm; return shell(s, FALSE); } /* * run the commands from the input source, returning status. */ int shell(s, toplevel) Source *volatile s; /* input source */ int volatile toplevel; { struct op *t; volatile int wastty = s->flags & SF_TTY; volatile int attempts = 13; volatile int interactive = Flag(FTALKING) && toplevel; int i; newenv(E_PARSE); if (interactive) really_exit = 0; i = ksh_sigsetjmp(e->jbuf, 0); if (i) { s->start = s->str = null; switch (i) { case LINTR: /* we get here if SIGINT not caught or ignored */ case LERROR: case LSHELL: if (interactive) { if (i == LINTR) shellf(newline); /* Reset any eof that was read as part of a * multiline command. */ if (Flag(FIGNOREEOF) && s->type == SEOF && wastty) s->type = SSTDIN; /* Used by exit command to get back to * top level shell. Kind of strange since * interactive is set if we are reading from * a tty, but to have stopped jobs, one only * needs FMONITOR set (not FTALKING/SF_TTY)... */ break; } /* fall through... */ case LEXIT: case LLEAVE: case LRETURN: quitenv(); unwind(i); /* keep on going */ /*NOREACHED*/ default: quitenv(); internal_errorf(1, "shell: %d", i); /*NOREACHED*/ } } while (1) { if (trap) runtraps(0); if (s->next == NULL) if (Flag(FVERBOSE)) s->flags |= SF_ECHO; else s->flags &= ~SF_ECHO; if (interactive) { j_notify(); #ifdef KSH mcheck(); #endif /* KSH */ set_prompt(PS1, s); } t = compile(s); if (t != NULL && t->type == TEOF) { if (wastty && Flag(FIGNOREEOF) && --attempts > 0) { shellf("Use `exit' to leave ksh\n"); s->type = SSTDIN; } else if (wastty && !really_exit && j_stopped_running()) { really_exit = 1; s->type = SSTDIN; } else { /* this for POSIX, which says EXIT traps * shall be taken in the environment * immediately after the last command * executed. */ if (toplevel) unwind(LEXIT); break; } } if (t && (!Flag(FNOEXEC) || (s->flags & SF_TTY))) exstat = execute(t, 0); if (t != NULL && t->type != TEOF && interactive && really_exit) really_exit = 0; reclaim(); } quitenv(); return exstat; } /* return to closest error handler or shell(), exit if none found */ void unwind(i) int i; { /* ordering for EXIT vs ERR is a bit odd (this is what at&t ksh does) */ if (i == LEXIT || (Flag(FERREXIT) && (i == LERROR || i == LINTR) && sigtraps[SIGEXIT_].trap)) { runtrap(&sigtraps[SIGEXIT_]); i = LLEAVE; } else if (Flag(FERREXIT) && (i == LERROR || i == LINTR)) { runtrap(&sigtraps[SIGERR_]); i = LLEAVE; } while (1) { switch (e->type) { case E_PARSE: case E_FUNC: case E_INCL: case E_LOOP: case E_ERRH: ksh_siglongjmp(e->jbuf, i); /*NOTREACHED*/ case E_NONE: if (i == LINTR) e->flags |= EF_FAKE_SIGDIE; /* Fall through... */ default: quitenv(); } } } void newenv(type) int type; { register struct env *ep; ep = (struct env *) alloc(sizeof(*ep), ATEMP); ep->type = type; ep->flags = 0; ainit(&ep->area); ep->loc = e->loc; ep->savefd = NULL; ep->oenv = e; ep->temps = NULL; e = ep; } void quitenv() { register struct env *ep = e; register int fd; if (ep->oenv && ep->oenv->loc != ep->loc) popblock(); if (ep->savefd != NULL) { for (fd = 0; fd < NUFILE; fd++) /* if ep->savefd[fd] < 0, means fd was closed */ if (ep->savefd[fd]) restfd(fd, ep->savefd[fd]); if (ep->savefd[2]) /* Clear any write errors */ shf_reopen(2, SHF_WR, shl_out); } reclaim(); /* Bottom of the stack. * Either main shell is exiting or cleanup_parents_env() was called. */ if (ep->oenv == NULL) { if (ep->type == E_NONE) { /* Main shell exiting? */ if (Flag(FTALKING)) hist_finish(); j_exit(); if (ep->flags & EF_FAKE_SIGDIE) { int sig = exstat - 128; /* ham up our death a bit (at&t ksh * only seems to do this for SIGTERM) * Don't do it for SIGQUIT, since we'd * dump a core.. */ if (sig == SIGINT || sig == SIGTERM) { setsig(&sigtraps[sig], SIG_DFL, SS_RESTORE_CURR|SS_FORCE); kill(0, sig); } } #ifdef MEM_DEBUG chmem_allfree(); #endif /* MEM_DEBUG */ } exit(exstat); } e = e->oenv; afree(ep, ATEMP); } /* Called after a fork to cleanup stuff left over from parents environment */ void cleanup_parents_env() { struct env *ep; int fd; /* Don't clean up temporary files - parent will probably need them. * Also, can't easily reclaim memory since variables, etc. could be * anywyere. */ /* close all file descriptors hiding in savefd */ for (ep = e; ep; ep = ep->oenv) { if (ep->savefd) { for (fd = 0; fd < NUFILE; fd++) if (ep->savefd[fd] > 0) close(ep->savefd[fd]); afree(ep->savefd, &ep->area); ep->savefd = (short *) 0; } } e->oenv = (struct env *) 0; } /* Called just before an execve cleanup stuff temporary files */ void cleanup_proc_env() { struct env *ep; for (ep = e; ep; ep = ep->oenv) remove_temps(ep->temps); } /* remove temp files and free ATEMP Area */ static void reclaim() { remove_temps(e->temps); e->temps = NULL; afreeall(&e->area); } static void remove_temps(tp) struct temp *tp; { #ifdef OS2 static struct temp *delayed_remove; struct temp *t, **tprev; if (delayed_remove) { for (tprev = &delayed_remove, t = delayed_remove; t; t = *tprev) /* No need to check t->pid here... */ if (unlink(t->name) >= 0 || errno == ENOENT) { *tprev = t->next; afree(t, APERM); } else tprev = &t->next; } #endif /* OS2 */ for (; tp != NULL; tp = tp->next) if (tp->pid == procpid) { #ifdef OS2 /* OS/2 (and dos) do not allow files that are currently * open to be removed, so we cache it away for future * removal. * XXX should only do this if errno * is Efile-still-open-can't-remove * (but I don't know what that is...) */ if (unlink(tp->name) < 0 && errno != ENOENT) { t = (struct temp *) alloc( sizeof(struct temp) + strlen(tp->name) + 1, APERM); memset(t, 0, sizeof(struct temp)); t->name = (char *) &t[1]; strcpy(t->name, tp->name); t->next = delayed_remove; delayed_remove = t; } #else /* OS2 */ unlink(tp->name); #endif /* OS2 */ } } /* Returns true if name refers to a restricted shell */ static int is_restricted(name) char *name; { char *p; /* this silly function prevents you running a command called runconf.sh. */ /* we don't care about restricted shells, which aren't very restricted anyway */ /* and introduce a false sense of security */ return 0; #ifdef dumbidea if ((p = ksh_strrchr_dirsep(name))) name = p; /* accepts rsh, rksh, rpdksh, pdrksh, etc. */ return (p = strchr(name, 'r')) && strstr(p, "sh"); #endif } void aerror(ap, msg) Area *ap; const char *msg; { internal_errorf(1, "alloc: %s", msg); errorf(null); /* this is never executed - keeps gcc quiet */ /*NOTREACHED*/ } /sys/src/ape/cmd/pdksh/misc.c 664 sys sys 1367613437 28368 /* * Miscellaneous functions */ #include "sh.h" #include /* for FILECHCONV */ #ifdef HAVE_LIMITS_H # include #endif #ifndef UCHAR_MAX # define UCHAR_MAX 0xFF #endif short ctypes [UCHAR_MAX+1]; /* type bits for unsigned char */ static int do_gmatch ARGS((const unsigned char *s, const unsigned char *p, const unsigned char *se, const unsigned char *pe, int isfile)); static const unsigned char *cclass ARGS((const unsigned char *p, int sub)); /* * Fast character classes */ void setctypes(s, t) register const char *s; register int t; { register int i; if (t & C_IFS) { for (i = 0; i < UCHAR_MAX+1; i++) ctypes[i] &= ~C_IFS; ctypes[0] |= C_IFS; /* include \0 in C_IFS */ } while (*s != 0) ctypes[(unsigned char) *s++] |= t; } void initctypes() { register int c; for (c = 'a'; c <= 'z'; c++) ctypes[c] |= C_ALPHA; for (c = 'A'; c <= 'Z'; c++) ctypes[c] |= C_ALPHA; ctypes['_'] |= C_ALPHA; setctypes("0123456789", C_DIGIT); setctypes(" \t\n|&;<>()", C_LEX1); /* \0 added automatically */ setctypes("*@#!$-?", C_VAR1); setctypes(" \t\n", C_IFSWS); setctypes("=-+?", C_SUBOP1); setctypes("#%", C_SUBOP2); setctypes(" \n\t\"#$&'()*;<>?[\\`|", C_QUOTE); } /* convert unsigned long to base N string */ char * ulton(n, base) register unsigned long n; int base; { register char *p; static char buf [20]; p = &buf[sizeof(buf)]; *--p = '\0'; do { *--p = "0123456789ABCDEF"[n%base]; n /= base; } while (n != 0); return p; } char * str_save(s, ap) register const char *s; Area *ap; { return s ? strcpy((char*) alloc((size_t)strlen(s)+1, ap), s) : NULL; } /* Allocate a string of size n+1 and copy upto n characters from the possibly * null terminated string s into it. Always returns a null terminated string * (unless n < 0). */ char * str_nsave(s, n, ap) register const char *s; int n; Area *ap; { char *ns; if (n < 0) return 0; ns = alloc(n + 1, ap); ns[0] = '\0'; return strncat(ns, s, n); } /* called from expand.h:XcheckN() to grow buffer */ char * Xcheck_grow_(xsp, xp, more) XString *xsp; char *xp; int more; { char *old_beg = xsp->beg; xsp->len += more > xsp->len ? more : xsp->len; xsp->beg = aresize(xsp->beg, xsp->len + 8, xsp->areap); xsp->end = xsp->beg + xsp->len; return xsp->beg + (xp - old_beg); } const struct option options[] = { /* Special cases (see parse_args()): -A, -o, -s. * Options are sorted by their longnames - the order of these * entries MUST match the order of sh_flag F* enumerations in sh.h. */ { "allexport", 'a', OF_ANY }, #ifdef BRACE_EXPAND { "braceexpand", 0, OF_ANY }, /* non-standard */ #endif { "bgnice", 0, OF_ANY }, { (char *) 0, 'c', OF_CMDLINE }, { "errexit", 'e', OF_ANY }, { "ignoreeof", 0, OF_ANY }, { "interactive",'i', OF_CMDLINE }, { "keyword", 'k', OF_ANY }, { "login", 'l', OF_CMDLINE }, { "markdirs", 'X', OF_ANY }, #ifdef JOBS { "monitor", 'm', OF_ANY }, #else /* JOBS */ { (char *) 0, 'm', 0 }, /* so FMONITOR not ifdef'd */ #endif /* JOBS */ { "noclobber", 'C', OF_ANY }, { "noexec", 'n', OF_ANY }, { "noglob", 'f', OF_ANY }, { "nohup", 0, OF_ANY }, { "nointeractive", 'I', OF_CMDLINE }, { "nolog", 0, OF_ANY }, /* no effect */ #ifdef JOBS { "notify", 'b', OF_ANY }, #endif /* JOBS */ { "nounset", 'u', OF_ANY }, { "physical", 0, OF_ANY }, /* non-standard */ { "posix", 0, OF_ANY }, /* non-standard */ { "privileged", 'p', OF_ANY }, { "restricted", 'r', OF_CMDLINE }, { "stdin", 's', OF_CMDLINE }, /* pseudo non-standard */ { "trackall", 'h', OF_ANY }, { "verbose", 'v', OF_ANY }, { "xtrace", 'x', OF_ANY }, /* Anonymous flags: used internally by shell only * (not visable to user) */ { (char *) 0, 0, OF_INTERNAL }, /* FTALKING_I */ }; /* * translate -o option into F* constant (also used for test -o option) */ int option(n) const char *n; { int i; for (i = 0; i < NELEM(options); i++) if (options[i].name && strcmp(options[i].name, n) == 0) return i; return -1; } struct options_info { int opt_width; struct { const char *name; int flag; } opts[NELEM(options)]; }; static char *options_fmt_entry ARGS((void *arg, int i, char *buf, int buflen)); static void printoptions ARGS((int verbose)); /* format a single select menu item */ static char * options_fmt_entry(arg, i, buf, buflen) void *arg; int i; char *buf; int buflen; { struct options_info *oi = (struct options_info *) arg; shf_snprintf(buf, buflen, "%-*s %s", oi->opt_width, oi->opts[i].name, Flag(oi->opts[i].flag) ? "on" : "off"); return buf; } static void printoptions(verbose) int verbose; { int i; if (verbose) { struct options_info oi; int n, len; /* verbose version */ shprintf("Current option settings\n"); for (i = n = oi.opt_width = 0; i < NELEM(options); i++) if (options[i].name) { len = strlen(options[i].name); oi.opts[n].name = options[i].name; oi.opts[n++].flag = i; if (len > oi.opt_width) oi.opt_width = len; } print_columns(shl_stdout, n, options_fmt_entry, &oi, oi.opt_width + 5); } else { /* short version ala ksh93 */ shprintf("set"); for (i = 0; i < NELEM(options); i++) if (Flag(i) && options[i].name) shprintf(" -o %s", options[i].name); shprintf(newline); } } char * getoptions() { int i; char m[(int) FNFLAGS + 1]; register char *cp = m; for (i = 0; i < NELEM(options); i++) if (options[i].c && Flag(i)) *cp++ = options[i].c; *cp = 0; return str_save(m, ATEMP); } /* change a Flag(*) value; takes care of special actions */ void change_flag(f, what, newval) enum sh_flag f; /* flag to change */ int what; /* what is changing the flag (command line vs set) */ int newval; { int oldval; oldval = Flag(f); Flag(f) = newval; /* Turning off -p? */ if (f == FPRIVILEGED && oldval && !newval) { setuid(ksheuid = getuid()); setgid(getgid()); } else if (f == FPOSIX && newval) { #ifdef BRACE_EXPAND Flag(FBRACEEXPAND) = 0 #endif /* BRACE_EXPAND */ ; } /* Changing interactive flag? */ if (f == FTALKING) { if ((what == OF_CMDLINE || what == OF_SET) && procpid == kshpid) Flag(FTALKING_I) = newval; } else if(f == FNOTTALKING) { if ((what == OF_CMDLINE || what == OF_SET) && procpid == kshpid) Flag(FTALKING_I) = !newval; } } /* parse command line & set command arguments. returns the index of * non-option arguments, -1 if there is an error. */ int parse_args(argv, what, setargsp) char **argv; int what; /* OF_CMDLINE or OF_SET */ int *setargsp; { static char cmd_opts[NELEM(options) + 3]; /* o:\0 */ static char set_opts[NELEM(options) + 5]; /* Ao;s\0 */ char *opts; char *array = (char *) 0; Getopt go; int i, optc, set, sortargs = 0, arrayset = 0; /* First call? Build option strings... */ if (cmd_opts[0] == '\0') { char *p, *q; strcpy(cmd_opts, "o:"); /* see cmd_opts[] declaration */ p = cmd_opts + strlen(cmd_opts); strcpy(set_opts, "A:o;s"); /* see set_opts[] declaration */ q = set_opts + strlen(set_opts); for (i = 0; i < NELEM(options); i++) { if (options[i].c) { if (options[i].flags & OF_CMDLINE) *p++ = options[i].c; if (options[i].flags & OF_SET) *q++ = options[i].c; } } *p = '\0'; *q = '\0'; } if (what == OF_CMDLINE) { char *p; /* Set FLOGIN before parsing options so user can clear * flag using +l. */ Flag(FLOGIN) = (argv[0][0] == '-' || ((p = ksh_strrchr_dirsep(argv[0])) && *++p == '-')); opts = cmd_opts; } else opts = set_opts; ksh_getopt_reset(&go, GF_ERROR|GF_PLUSOPT); while ((optc = ksh_getopt(argv, &go, opts)) != EOF) { set = (go.info & GI_PLUS) ? 0 : 1; switch (optc) { case 'A': arrayset = set ? 1 : -1; array = go.optarg; break; case 'o': if (go.optarg == (char *) 0) { /* lone -o: print options * * Note that on the command line, -o requires * an option (ie, can't get here if what is * OF_CMDLINE). */ printoptions(set); break; } i = option(go.optarg); if (i >= 0 && set == Flag(i)) /* Don't check the context if the flag * isn't changing - makes "set -o interactive" * work if you're already interactive. Needed * if the output of "set +o" is to be used. */ ; else if (i >= 0 && (options[i].flags & what)) change_flag((enum sh_flag) i, what, set); else { bi_errorf("%s: bad option", go.optarg); return -1; } break; case '?': return -1; default: /* -s: sort positional params (at&t ksh stupidity) */ if (what == OF_SET && optc == 's') { sortargs = 1; break; } for (i = 0; i < NELEM(options); i++) if (optc == options[i].c && (what & options[i].flags)) { change_flag((enum sh_flag) i, what, set); break; } if (i == NELEM(options)) { internal_errorf(1, "parse_args: `%c'", optc); return -1; /* not reached */ } } } if (!(go.info & GI_MINUSMINUS) && argv[go.optind] && (argv[go.optind][0] == '-' || argv[go.optind][0] == '+') && argv[go.optind][1] == '\0') { /* lone - clears -v and -x flags */ if (argv[go.optind][0] == '-' && !Flag(FPOSIX)) Flag(FVERBOSE) = Flag(FXTRACE) = 0; /* set skips lone - or + option */ go.optind++; } if (setargsp) /* -- means set $#/$* even if there are no arguments */ *setargsp = !arrayset && ((go.info & GI_MINUSMINUS) || argv[go.optind]); if (arrayset && (!*array || *skip_varname(array, FALSE))) { bi_errorf("%s: is not an identifier", array); return -1; } if (sortargs) { for (i = go.optind; argv[i]; i++) ; qsortp((void **) &argv[go.optind], (size_t) (i - go.optind), xstrcmp); } if (arrayset) { set_array(array, arrayset, argv + go.optind); for (; argv[go.optind]; go.optind++) ; } return go.optind; } /* parse a decimal number: returns 0 if string isn't a number, 1 otherwise */ int getn(as, ai) const char *as; int *ai; { const char *s; register int n; int sawdigit = 0; s = as; if (*s == '-' || *s == '+') s++; for (n = 0; digit(*s); s++, sawdigit = 1) n = n * 10 + (*s - '0'); *ai = (*as == '-') ? -n : n; if (*s || !sawdigit) return 0; return 1; } /* getn() that prints error */ int bi_getn(as, ai) const char *as; int *ai; { int rv = getn(as, ai); if (!rv) bi_errorf("%s: bad number", as); return rv; } /* -------- gmatch.c -------- */ /* * int gmatch(string, pattern) * char *string, *pattern; * * Match a pattern as in sh(1). * pattern character are prefixed with MAGIC by expand. */ int gmatch(s, p, isfile) const char *s, *p; int isfile; { const char *se, *pe; if (s == NULL || p == NULL) return 0; se = s + strlen(s); pe = p + strlen(p); /* isfile is false iff no syntax check has been done on * the pattern. If check fails, just to a strcmp(). */ if (!isfile && !has_globbing(p, pe)) { int len = pe - p + 1; char tbuf[64]; char *t = len <= sizeof(tbuf) ? tbuf : (char *) alloc(len, ATEMP); debunk(t, p); return !strcmp(t, s); } return do_gmatch((const unsigned char *) s, (const unsigned char *) se, (const unsigned char *) p, (const unsigned char *) pe, isfile); } /* Returns if p is a syntacticly correct globbing pattern, false * if it contains no pattern characters or if there is a syntax error. * Syntax errors are: * - [ with no closing ] * - imballenced $(...) expression * - [...] and *(...) not nested (eg, [a$(b|]c), *(a[b|c]d)) */ /*XXX - if no magic, if dest given, copy to dst return ? - if magic && (no globbing || syntax error) debunk to dst return ? - return ? */ int has_globbing(xp, xpe) const char *xp, *xpe; { const unsigned char *p = (const unsigned char *) xp; const unsigned char *pe = (const unsigned char *) xpe; int c; int nest = 0, bnest = 0; int saw_glob = 0; int in_bracket = 0; /* inside [...] */ for (; p < pe; p++) { if (!ISMAGIC(*p)) continue; if ((c = *++p) == '*' || c == '?') saw_glob = 1; else if (c == '[') { if (!in_bracket) { saw_glob = 1; in_bracket = 1; if (ISMAGIC(p[1]) && p[2] == NOT) p += 2; if (ISMAGIC(p[1]) && p[2] == ']') p += 2; } /* XXX Do we need to check ranges here? POSIX Q */ } else if (c == ']') { if (in_bracket) { if (bnest) /* [a*(b]) */ return 0; in_bracket = 0; } } else if ((c & 0x80) && strchr("*+?@! ", c & 0x7f)) { saw_glob = 1; if (in_bracket) bnest++; else nest++; } else if (c == '|') { if (in_bracket && !bnest) /* *(a[foo|bar]) */ return 0; } else if (c == /*(*/ ')') { if (in_bracket) { if (!bnest--) /* *(a[b)c] */ return 0; } else if (nest) nest--; } /* else must be a MAGIC-MAGIC, or MAGIC-!, MAGIC--, MAGIC-] MAGIC-{, MAGIC-,, MAGIC-} */ } return saw_glob && !in_bracket && !nest; } /* Function must return either 0 or 1 (assumed by code for 0x80|'!') */ static int do_gmatch(s, se, p, pe, isfile) const unsigned char *s, *p; const unsigned char *se, *pe; int isfile; { register int sc, pc; const unsigned char *prest, *psub, *pnext; const unsigned char *srest; if (s == NULL || p == NULL) return 0; while (p < pe) { pc = *p++; sc = s < se ? *s : '\0'; s++; if (isfile) { sc = FILECHCONV(sc); pc = FILECHCONV(pc); } if (!ISMAGIC(pc)) { if (sc != pc) return 0; continue; } switch (*p++) { case '[': if (sc == 0 || (p = cclass(p, sc)) == NULL) return 0; break; case '?': if (sc == 0) return 0; break; case '*': if (p == pe) return 1; s--; do { if (do_gmatch(s, se, p, pe, isfile)) return 1; } while (s++ < se); return 0; /* * [*+?@!](pattern|pattern|..) * * Not ifdef'd KSH as this is needed for ${..%..}, etc. */ case 0x80|'+': /* matches one or more times */ case 0x80|'*': /* matches zero or more times */ if (!(prest = pat_scan(p, pe, 0))) return 0; s--; /* take care of zero matches */ if (p[-1] == (0x80 | '*') && do_gmatch(s, se, prest, pe, isfile)) return 1; for (psub = p; ; psub = pnext) { pnext = pat_scan(psub, pe, 1); for (srest = s; srest <= se; srest++) { if (do_gmatch(s, srest, psub, pnext - 2, isfile) && (do_gmatch(srest, se, prest, pe, isfile) || (s != srest && do_gmatch(srest, se, p - 2, pe, isfile)))) return 1; } if (pnext == prest) break; } return 0; case 0x80|'?': /* matches zero or once */ case 0x80|'@': /* matches one of the patterns */ case 0x80|' ': /* simile for @ */ if (!(prest = pat_scan(p, pe, 0))) return 0; s--; /* Take care of zero matches */ if (p[-1] == (0x80 | '?') && do_gmatch(s, se, prest, pe, isfile)) return 1; for (psub = p; ; psub = pnext) { pnext = pat_scan(psub, pe, 1); srest = prest == pe ? se : s; for (; srest <= se; srest++) { if (do_gmatch(s, srest, psub, pnext - 2, isfile) && do_gmatch(srest, se, prest, pe, isfile)) return 1; } if (pnext == prest) break; } return 0; case 0x80|'!': /* matches none of the patterns */ if (!(prest = pat_scan(p, pe, 0))) return 0; s--; for (srest = s; srest <= se; srest++) { int matched = 0; for (psub = p; ; psub = pnext) { pnext = pat_scan(psub, pe, 1); if (do_gmatch(s, srest, psub, pnext - 2, isfile)) { matched = 1; break; } if (pnext == prest) break; } if (!matched && do_gmatch(srest, se, prest, pe, isfile)) return 1; } return 0; default: if (sc != p[-1]) return 0; break; } } return s == se; } static const unsigned char * cclass(p, sub) const unsigned char *p; register int sub; { register int c, d, not, found = 0; const unsigned char *orig_p = p; if ((not = (ISMAGIC(*p) && *++p == NOT))) p++; do { c = *p++; if (ISMAGIC(c)) { c = *p++; if ((c & 0x80) && !ISMAGIC(c)) { c &= 0x7f;/* extended pattern matching: *+?@! */ /* XXX the ( char isn't handled as part of [] */ if (c == ' ') /* simile for @: plain (..) */ c = '(' /*)*/; } } if (c == '\0') /* No closing ] - act as if the opening [ was quoted */ return sub == '[' ? orig_p : NULL; if (ISMAGIC(p[0]) && p[1] == '-' && (!ISMAGIC(p[2]) || p[3] != ']')) { p += 2; /* MAGIC- */ d = *p++; if (ISMAGIC(d)) { d = *p++; if ((d & 0x80) && !ISMAGIC(d)) d &= 0x7f; } /* POSIX says this is an invalid expression */ if (c > d) return NULL; } else d = c; if (c == sub || (c <= sub && sub <= d)) found = 1; } while (!(ISMAGIC(p[0]) && p[1] == ']')); return (found != not) ? p+2 : NULL; } /* Look for next ) or | (if match_sep) in *(foo|bar) pattern */ const unsigned char * pat_scan(p, pe, match_sep) const unsigned char *p; const unsigned char *pe; int match_sep; { int nest = 0; for (; p < pe; p++) { if (!ISMAGIC(*p)) continue; if ((*++p == /*(*/ ')' && nest-- == 0) || (*p == '|' && match_sep && nest == 0)) return ++p; if ((*p & 0x80) && strchr("*+?@! ", *p & 0x7f)) nest++; } return (const unsigned char *) 0; } /* -------- qsort.c -------- */ /* * quick sort of array of generic pointers to objects. */ static void qsort1 ARGS((void **base, void **lim, int (*f)(void *, void *))); void qsortp(base, n, f) void **base; /* base address */ size_t n; /* elements */ int (*f) ARGS((void *, void *)); /* compare function */ { qsort1(base, base + n, f); } #define swap2(a, b) {\ register void *t; t = *(a); *(a) = *(b); *(b) = t;\ } #define swap3(a, b, c) {\ register void *t; t = *(a); *(a) = *(c); *(c) = *(b); *(b) = t;\ } static void qsort1(base, lim, f) void **base, **lim; int (*f) ARGS((void *, void *)); { register void **i, **j; register void **lptr, **hptr; size_t n; int c; top: n = (lim - base) / 2; if (n == 0) return; hptr = lptr = base+n; i = base; j = lim - 1; for (;;) { if (i < lptr) { if ((c = (*f)(*i, *lptr)) == 0) { lptr --; swap2(i, lptr); continue; } if (c < 0) { i += 1; continue; } } begin: if (j > hptr) { if ((c = (*f)(*hptr, *j)) == 0) { hptr ++; swap2(hptr, j); goto begin; } if (c > 0) { if (i == lptr) { hptr ++; swap3(i, hptr, j); i = lptr += 1; goto begin; } swap2(i, j); j -= 1; i += 1; continue; } j -= 1; goto begin; } if (i == lptr) { if (lptr-base >= lim-hptr) { qsort1(hptr+1, lim, f); lim = lptr; } else { qsort1(base, lptr, f); base = hptr+1; } goto top; } lptr -= 1; swap3(j, lptr, i); j = hptr -= 1; } } int xstrcmp(p1, p2) void *p1, *p2; { return (strcmp((char *)p1, (char *)p2)); } /* Initialize a Getopt structure */ void ksh_getopt_reset(go, flags) Getopt *go; int flags; { go->optind = 1; go->optarg = (char *) 0; go->p = 0; go->flags = flags; go->info = 0; go->buf[1] = '\0'; } /* getopt() used for shell built-in commands, the getopts command, and * command line options. * A leading ':' in options means don't print errors, instead return '?' * or ':' and set go->optarg to the offending option character. * If GF_ERROR is set (and option doesn't start with :), errors result in * a call to bi_errorf(). * * Non-standard features: * - ';' is like ':' in options, except the argument is optional * (if it isn't present, optarg is set to 0). * Used for 'set -o'. * - ',' is like ':' in options, except the argument always immediately * follows the option character (optarg is set to the null string if * the option is missing). * Used for 'read -u2', 'print -u2' and fc -40. * - '#' is like ':' in options, expect that the argument is optional * and must start with a digit. If the argument doesn't start with a * digit, it is assumed to be missing and normal option processing * continues (optarg is set to 0 if the option is missing). * Used for 'typeset -LZ4'. * - accepts +c as well as -c IF the GF_PLUSOPT flag is present. If an * option starting with + is accepted, the GI_PLUS flag will be set * in go->info. */ int ksh_getopt(argv, go, options) char **argv; Getopt *go; const char *options; { char c; char *o; if (go->p == 0 || (c = argv[go->optind - 1][go->p]) == '\0') { char *arg = argv[go->optind], flag = arg ? *arg : '\0'; go->p = 1; if (flag == '-' && arg[1] == '-' && arg[2] == '\0') { go->optind++; go->p = 0; go->info |= GI_MINUSMINUS; return EOF; } if (arg == (char *) 0 || ((flag != '-' ) /* neither a - nor a + (if + allowed) */ && (!(go->flags & GF_PLUSOPT) || flag != '+')) || (c = arg[1]) == '\0') { go->p = 0; return EOF; } go->optind++; go->info &= ~(GI_MINUS|GI_PLUS); go->info |= flag == '-' ? GI_MINUS : GI_PLUS; } go->p++; if (c == '?' || c == ':' || c == ';' || c == ',' || c == '#' || !(o = strchr(options, c))) { if (options[0] == ':') { go->buf[0] = c; go->optarg = go->buf; } else { warningf(TRUE, "%s%s-%c: unknown option", (go->flags & GF_NONAME) ? "" : argv[0], (go->flags & GF_NONAME) ? "" : ": ", c); if (go->flags & GF_ERROR) bi_errorf(null); } return '?'; } /* : means argument must be present, may be part of option argument * or the next argument * ; same as : but argument may be missing * , means argument is part of option argument, and may be null. */ if (*++o == ':' || *o == ';') { if (argv[go->optind - 1][go->p]) go->optarg = argv[go->optind - 1] + go->p; else if (argv[go->optind]) go->optarg = argv[go->optind++]; else if (*o == ';') go->optarg = (char *) 0; else { if (options[0] == ':') { go->buf[0] = c; go->optarg = go->buf; return ':'; } warningf(TRUE, "%s%s-`%c' requires argument", (go->flags & GF_NONAME) ? "" : argv[0], (go->flags & GF_NONAME) ? "" : ": ", c); if (go->flags & GF_ERROR) bi_errorf(null); return '?'; } go->p = 0; } else if (*o == ',') { /* argument is attatched to option character, even if null */ go->optarg = argv[go->optind - 1] + go->p; go->p = 0; } else if (*o == '#') { /* argument is optional and may be attatched or unattatched * but must start with a digit. optarg is set to 0 if the * argument is missing. */ if (argv[go->optind - 1][go->p]) { if (digit(argv[go->optind - 1][go->p])) { go->optarg = argv[go->optind - 1] + go->p; go->p = 0; } else go->optarg = (char *) 0;; } else { if (argv[go->optind] && digit(argv[go->optind][0])) { go->optarg = argv[go->optind++]; go->p = 0; } else go->optarg = (char *) 0;; } } return c; } /* print variable/alias value using necessary quotes * (POSIX says they should be suitable for re-entry...) * No trailing newline is printed. */ void print_value_quoted(s) const char *s; { const char *p; int inquote = 0; /* Test if any quotes are needed */ for (p = s; *p; p++) if (ctype(*p, C_QUOTE)) break; if (!*p) { shprintf("%s", s); return; } for (p = s; *p; p++) { if (*p == '\'') { shprintf("'\\'" + 1 - inquote); inquote = 0; } else { if (!inquote) { shprintf("'"); inquote = 1; } shf_putc(*p, shl_stdout); } } if (inquote) shprintf("'"); } /* Print things in columns and rows - func() is called to format the ith * element */ void print_columns(shf, n, func, arg, max_width) struct shf *shf; int n; char *(*func) ARGS((void *, int, char *, int)); void *arg; int max_width; { char *str = (char *) alloc(max_width + 1, ATEMP); int i; int r, c; int rows, cols; int nspace; /* max_width + 1 for the space. Note that no space * is printed after the last column to avoid problems * with terminals that have auto-wrap. */ cols = x_cols / (max_width + 1); if (!cols) cols = 1; rows = (n + cols - 1) / cols; if (n && cols > rows) { int tmp = rows; rows = cols; cols = tmp; if (rows > n) rows = n; } nspace = (x_cols - max_width * cols) / cols; if (nspace <= 0) nspace = 1; for (r = 0; r < rows; r++) { for (c = 0; c < cols; c++) { i = c * rows + r; if (i < n) { shf_fprintf(shf, "%-*s", max_width, (*func)(arg, i, str, max_width + 1)); if (c + 1 < cols) shf_fprintf(shf, "%*s", nspace, null); } } shf_putchar('\n', shf); } afree(str, ATEMP); } /* Strip any nul bytes from buf - returns new length (nbytes - # of nuls) */ int strip_nuls(buf, nbytes) char *buf; int nbytes; { char *dst; /* nbytes check because some systems (older freebsd's) have a buggy * memchr() */ if (nbytes && (dst = memchr(buf, '\0', nbytes))) { char *end = buf + nbytes; char *p, *q; for (p = dst; p < end; p = q) { /* skip a block of nulls */ while (++p < end && *p == '\0') ; /* find end of non-null block */ if (!(q = memchr(p, '\0', end - p))) q = end; memmove(dst, p, q - p); dst += q - p; } *dst = '\0'; return dst - buf; } return nbytes; } /* Copy at most dsize-1 bytes from src to dst, ensuring dst is null terminated. * Returns dst. */ char * str_zcpy(dst, src, dsize) char *dst; const char *src; int dsize; { if (dsize > 0) { int len = strlen(src); if (len >= dsize) len = dsize - 1; memcpy(dst, src, len); dst[len] = '\0'; } return dst; } /* Like read(2), but if read fails due to non-blocking flag, resets flag * and restarts read. */ int blocking_read(fd, buf, nbytes) int fd; char *buf; int nbytes; { int ret; int tried_reset = 0; while ((ret = read(fd, buf, nbytes)) < 0) { if (!tried_reset && (errno == EAGAIN #ifdef EWOULDBLOCK || errno == EWOULDBLOCK #endif /* EWOULDBLOCK */ )) { int oerrno = errno; if (reset_nonblock(fd) > 0) { tried_reset = 1; continue; } errno = oerrno; } break; } return ret; } /* Reset the non-blocking flag on the specified file descriptor. * Returns -1 if there was an error, 0 if non-blocking wasn't set, * 1 if it was. */ int reset_nonblock(fd) int fd; { int flags; int blocking_flags; if ((flags = fcntl(fd, F_GETFL, 0)) < 0) return -1; /* With luck, the C compiler will reduce this to a constant */ blocking_flags = 0; #ifdef O_NONBLOCK blocking_flags |= O_NONBLOCK; #endif /* O_NONBLOCK */ #ifdef O_NDELAY blocking_flags |= O_NDELAY; #else /* O_NDELAY */ # ifndef O_NONBLOCK blocking_flags |= FNDELAY; /* hope this exists... */ # endif /* O_NONBLOCK */ #endif /* O_NDELAY */ if (!(flags & blocking_flags)) return 0; flags &= ~blocking_flags; if (fcntl(fd, F_SETFL, flags) < 0) return -1; return 1; } #ifdef HAVE_SYS_PARAM_H # include #endif /* HAVE_SYS_PARAM_H */ #ifndef MAXPATHLEN # define MAXPATHLEN PATH #endif /* MAXPATHLEN */ #ifdef HPUX_GETWD_BUG # include "ksh_dir.h" /* * Work around bug in hpux 10.x C library - getwd/getcwd dump core * if current directory is not readable. Done in macro 'cause code * is needed in GETWD and GETCWD cases. */ # define HPUX_GETWD_BUG_CODE \ { \ DIR *d = ksh_opendir("."); \ if (!d) \ return (char *) 0; \ closedir(d); \ } #else /* HPUX_GETWD_BUG */ # define HPUX_GETWD_BUG_CODE #endif /* HPUX_GETWD_BUG */ /* Like getcwd(), except bsize is ignored if buf is 0 (MAXPATHLEN is used) */ char * ksh_get_wd(buf, bsize) char *buf; int bsize; { #ifdef HAVE_GETCWD char *b; char *ret; /* Before memory allocated */ HPUX_GETWD_BUG_CODE /* Assume getcwd() available */ if (!buf) { bsize = MAXPATHLEN; b = alloc(MAXPATHLEN + 1, ATEMP); } else b = buf; ret = getcwd(b, bsize); if (!buf) { if (ret) ret = aresize(b, strlen(b) + 1, ATEMP); else afree(b, ATEMP); } return ret; #else /* HAVE_GETCWD */ extern char *getwd ARGS((char *)); char *b; int len; /* Before memory allocated */ HPUX_GETWD_BUG_CODE if (buf && bsize > MAXPATHLEN) b = buf; else b = alloc(MAXPATHLEN + 1, ATEMP); if (!getwd(b)) { errno = EACCES; if (b != buf) afree(b, ATEMP); return (char *) 0; } len = strlen(b) + 1; if (!buf) b = aresize(b, len, ATEMP); else if (buf != b) { if (len > bsize) { errno = ERANGE; return (char *) 0; } memcpy(buf, b, len); afree(b, ATEMP); b = buf; } return b; #endif /* HAVE_GETCWD */ } /sys/src/ape/cmd/pdksh/mkfile 664 sys sys 1367613437 549 start && !ISDIRSEP(*cur)) ; t += 2; continue; } } if (cur != very_start) *cur++ = DIRSEP; /* find/copy next component of pathname */ while (*t && !ISDIRSEP(*t)) *cur++ = *t++; } } void set_current_wd(path) char *path; { int len; char *p = path; if (!p && !(p = ksh_get_wd((char *) 0, 0))) p = null; len = strlen(p) + 1; if (len > current_wd_size) current_wd = aresize(current_wd, current_wd_size = len, APERM); memcpy(current_wd, p, len); if (p != path && p != null) afree(p, ATEMP); } #ifdef S_ISLNK char * get_phys_path(path) const char *path; { XString xs; char *xp; Xinit(xs, xp, strlen(path) + 1, ATEMP); xp = do_phys_path(&xs, xp, path); if (!xp) return (char *) 0; if (Xlength(xs, xp) == 0) Xput(xs, xp, DIRSEP); Xput(xs, xp, '\0'); return Xclose(xs, xp); } static char * do_phys_path(xsp, xp, path) XString *xsp; char *xp; const char *path; { const char *p, *q; int len, llen; int savepos; char lbuf[PATH]; Xcheck(*xsp, xp); for (p = path; p; p = q) { while (ISDIRSEP(*p)) p++; if (!*p) break; len = (q = ksh_strchr_dirsep(p)) ? q - p : strlen(p); if (len == 1 && p[0] == '.') continue; if (len == 2 && p[0] == '.' && p[1] == '.') { while (xp > Xstring(*xsp, xp)) { xp--; if (ISDIRSEP(*xp)) break; } continue; } savepos = Xsavepos(*xsp, xp); Xput(*xsp, xp, DIRSEP); XcheckN(*xsp, xp, len + 1); memcpy(xp, p, len); xp += len; *xp = '\0'; llen = readlink(Xstring(*xsp, xp), lbuf, sizeof(lbuf) - 1); if (llen < 0) { /* EINVAL means it wasn't a symlink... */ if (errno != EINVAL) return (char *) 0; continue; } lbuf[llen] = '\0'; /* If absolute path, start from scratch.. */ xp = ISABSPATH(lbuf) ? Xstring(*xsp, xp) : Xrestpos(*xsp, xp, savepos); if (!(xp = do_phys_path(xsp, xp, lbuf))) return (char *) 0; } return xp; } #endif /* S_ISLNK */ #ifdef TEST main(argc, argv) { int rv; char *cp, cdpath[256], pwd[256], file[256], result[256]; printf("enter CDPATH: "); gets(cdpath); printf("enter PWD: "); gets(pwd); while (1) { if (printf("Enter file: "), gets(file) == 0) return 0; cp = cdpath; do { rv = make_path(pwd, file, &cp, result, sizeof(result)); printf("make_path returns (%d), \"%s\" ", rv, result); simplify_path(result); printf("(simpifies to \"%s\")\n", result); } while (cp); } } #endif /* TEST */ /sys/src/ape/cmd/pdksh/proto.h 664 sys sys 1367613437 12016 /* * prototypes for PD-KSH * originally generated using "cproto.c 3.5 92/04/11 19:28:01 cthuang " * $Id: proto.h,v 1.3 1994/05/19 18:32:40 michael Exp michael $ */ /* alloc.c */ Area * ainit ARGS((Area *ap)); void afreeall ARGS((Area *ap)); void * alloc ARGS((size_t size, Area *ap)); void * aresize ARGS((void *ptr, size_t size, Area *ap)); void afree ARGS((void *ptr, Area *ap)); /* c_ksh.c */ int c_hash ARGS((char **wp)); int c_cd ARGS((char **wp)); int c_pwd ARGS((char **wp)); int c_print ARGS((char **wp)); int c_whence ARGS((char **wp)); int c_command ARGS((char **wp)); int c_typeset ARGS((char **wp)); int c_alias ARGS((char **wp)); int c_unalias ARGS((char **wp)); int c_let ARGS((char **wp)); int c_jobs ARGS((char **wp)); int c_fgbg ARGS((char **wp)); int c_kill ARGS((char **wp)); void getopts_reset ARGS((int val)); int c_getopts ARGS((char **wp)); int c_bind ARGS((char **wp)); /* c_sh.c */ int c_label ARGS((char **wp)); int c_shift ARGS((char **wp)); int c_umask ARGS((char **wp)); int c_dot ARGS((char **wp)); int c_wait ARGS((char **wp)); int c_read ARGS((char **wp)); int c_eval ARGS((char **wp)); int c_trap ARGS((char **wp)); int c_brkcont ARGS((char **wp)); int c_exitreturn ARGS((char **wp)); int c_set ARGS((char **wp)); int c_unset ARGS((char **wp)); int c_ulimit ARGS((char **wp)); int c_times ARGS((char **wp)); int timex ARGS((struct op *t, int f)); void timex_hook ARGS((struct op *t, char ** volatile *app)); int c_exec ARGS((char **wp)); int c_builtin ARGS((char **wp)); /* c_test.c */ int c_test ARGS((char **wp)); /* edit.c: most prototypes in edit.h */ void x_init ARGS((void)); int x_read ARGS((char *buf, size_t len)); void set_editmode ARGS((const char *ed)); /* emacs.c: most prototypes in edit.h */ int x_bind ARGS((const char *a1, const char *a2, int macro, int list)); /* eval.c */ char * substitute ARGS((const char *cp, int f)); char ** eval ARGS((char **ap, int f)); char * evalstr ARGS((char *cp, int f)); char * evalonestr ARGS((char *cp, int f)); char *debunk ARGS((char *dp, const char *sp)); void expand ARGS((char *cp, XPtrV *wp, int f)); int glob_str ARGS((char *cp, XPtrV *wp, int markdirs)); /* exec.c */ int fd_clexec ARGS((int fd)); int execute ARGS((struct op * volatile t, volatile int flags)); int shcomexec ARGS((char **wp)); struct tbl * findfunc ARGS((const char *name, unsigned int h, int create)); int define ARGS((const char *name, struct op *t)); void builtin ARGS((const char *name, int (*func)(char **))); struct tbl * findcom ARGS((const char *name, int flags)); void flushcom ARGS((int all)); char * search ARGS((const char *name, const char *path, int mode, int *errnop)); int search_access ARGS((const char *path, int mode, int *errnop)); int pr_menu ARGS((char *const *ap)); /* expr.c */ int evaluate ARGS((const char *expr, long *rval, int error_ok)); int v_evaluate ARGS((struct tbl *vp, const char *expr, volatile int error_ok)); /* history.c */ void init_histvec ARGS((void)); void hist_init ARGS((Source *s)); void hist_finish ARGS((void)); void histsave ARGS((int lno, const char *cmd, int dowrite)); #ifdef HISTORY int c_fc ARGS((register char **wp)); void sethistsize ARGS((int n)); void sethistfile ARGS((const char *name)); # ifdef EASY_HISTORY void histappend ARGS((const char *cmd, int nl_separate)); # endif char ** histpos ARGS((void)); int histN ARGS((void)); int histnum ARGS((int n)); int findhist ARGS((int start, int fwd, const char *str, int anchored)); #endif /* HISTORY */ /* io.c */ void errorf ARGS((const char *fmt, ...)) GCC_FUNC_ATTR2(noreturn, format(printf, 1, 2)); void warningf ARGS((int fileline, const char *fmt, ...)) GCC_FUNC_ATTR(format(printf, 2, 3)); void bi_errorf ARGS((const char *fmt, ...)) GCC_FUNC_ATTR(format(printf, 1, 2)); void internal_errorf ARGS((int jump, const char *fmt, ...)) GCC_FUNC_ATTR(format(printf, 2, 3)); void error_prefix ARGS((int fileline)); void shellf ARGS((const char *fmt, ...)) GCC_FUNC_ATTR(format(printf, 1, 2)); void shprintf ARGS((const char *fmt, ...)) GCC_FUNC_ATTR(format(printf, 1, 2)); #ifdef KSH_DEBUG void kshdebug_init_ ARGS((void)); void kshdebug_printf_ ARGS((const char *fmt, ...)) GCC_FUNC_ATTR(format(printf, 1, 2)); void kshdebug_dump_ ARGS((const char *str, const void *mem, int nbytes)); #endif /* KSH_DEBUG */ int can_seek ARGS((int fd)); void initio ARGS((void)); int ksh_dup2 ARGS((int ofd, int nfd, int errok)); int savefd ARGS((int fd, int noclose)); void restfd ARGS((int fd, int ofd)); void openpipe ARGS((int *pv)); void closepipe ARGS((int *pv)); int check_fd ARGS((char *name, int mode, const char **emsgp)); #ifdef KSH void coproc_init ARGS((void)); void coproc_read_close ARGS((int fd)); void coproc_readw_close ARGS((int fd)); void coproc_write_close ARGS((int fd)); int coproc_getfd ARGS((int mode, const char **emsgp)); void coproc_cleanup ARGS((int reuse)); #endif /* KSH */ struct temp *maketemp ARGS((Area *ap, Temp_type type, struct temp **tlist)); /* jobs.c */ void j_init ARGS((int mflagset)); void j_exit ARGS((void)); void j_change ARGS((void)); int exchild ARGS((struct op *t, int flags, int close_fd)); void startlast ARGS((void)); int waitlast ARGS((void)); int waitfor ARGS((const char *cp, int *sigp)); int j_kill ARGS((const char *cp, int sig)); int j_resume ARGS((const char *cp, int bg)); int j_jobs ARGS((const char *cp, int slp, int nflag)); void j_notify ARGS((void)); pid_t j_async ARGS((void)); int j_stopped_running ARGS((void)); /* lex.c */ int yylex ARGS((int cf)); void yyerror ARGS((const char *fmt, ...)) GCC_FUNC_ATTR2(noreturn, format(printf, 1, 2)); Source * pushs ARGS((int type, Area *areap)); void set_prompt ARGS((int to, Source *s)); void pprompt ARGS((const char *cp, int ntruncate)); /* mail.c */ #ifdef KSH void mcheck ARGS((void)); void mcset ARGS((long interval)); void mbset ARGS((char *p)); void mpset ARGS((char *mptoparse)); #endif /* KSH */ /* main.c */ int include ARGS((const char *name, int argc, char **argv, int intr_ok)); int command ARGS((const char *comm)); int shell ARGS((Source *volatile s, int volatile toplevel)); void unwind ARGS((int i)) GCC_FUNC_ATTR(noreturn); void newenv ARGS((int type)); void quitenv ARGS((void)); void cleanup_parents_env ARGS((void)); void cleanup_proc_env ARGS((void)); void aerror ARGS((Area *ap, const char *msg)) GCC_FUNC_ATTR(noreturn); /* misc.c */ void setctypes ARGS((const char *s, int t)); void initctypes ARGS((void)); char * ulton ARGS((unsigned long n, int base)); char * str_save ARGS((const char *s, Area *ap)); char * str_nsave ARGS((const char *s, int n, Area *ap)); int option ARGS((const char *n)); char * getoptions ARGS((void)); void change_flag ARGS((enum sh_flag f, int what, int newval)); int parse_args ARGS((char **argv, int what, int *setargsp)); int getn ARGS((const char *as, int *ai)); int bi_getn ARGS((const char *as, int *ai)); char * strerror ARGS((int i)); int gmatch ARGS((const char *s, const char *p, int isfile)); int has_globbing ARGS((const char *xp, const char *xpe)); const unsigned char *pat_scan ARGS((const unsigned char *p, const unsigned char *pe, int match_sep)); void qsortp ARGS((void **base, size_t n, int (*f)(void *, void *))); int xstrcmp ARGS((void *p1, void *p2)); void ksh_getopt_reset ARGS((Getopt *go, int)); int ksh_getopt ARGS((char **argv, Getopt *go, const char *options)); void print_value_quoted ARGS((const char *s)); void print_columns ARGS((struct shf *shf, int n, char *(*func)(void *, int, char *, int), void *arg, int max_width)); int strip_nuls ARGS((char *buf, int nbytes)); char *str_zcpy ARGS((char *dst, const char *src, int dsize)); int blocking_read ARGS((int fd, char *buf, int nbytes)); int reset_nonblock ARGS((int fd)); char *ksh_get_wd ARGS((char *buf, int bsize)); /* path.c */ int make_path ARGS((const char *cwd, const char *file, char **pathlist, XString *xsp, int *phys_pathp)); void simplify_path ARGS((char *path)); char *get_phys_path ARGS((const char *path)); void set_current_wd ARGS((char *path)); /* syn.c */ void initkeywords ARGS((void)); struct op * compile ARGS((Source *s)); /* table.c */ unsigned int hash ARGS((const char *n)); void tinit ARGS((struct table *tp, Area *ap, int tsize)); struct tbl * tsearch ARGS((struct table *tp, const char *n, unsigned int h)); struct tbl * tenter ARGS((struct table *tp, const char *n, unsigned int h)); void tdelete ARGS((struct tbl *p)); void twalk ARGS((struct tstate *ts, struct table *tp)); struct tbl * tnext ARGS((struct tstate *ts)); struct tbl ** tsort ARGS((struct table *tp)); /* trace.c */ /* trap.c */ void inittraps ARGS((void)); #ifdef KSH void alarm_init ARGS((void)); #endif /* KSH */ Trap * gettrap ARGS((const char *name, int igncase)); RETSIGTYPE trapsig ARGS((int i)); void intrcheck ARGS((void)); int fatal_trap_check ARGS((void)); int trap_pending ARGS((void)); void runtraps ARGS((int intr)); void runtrap ARGS((Trap *p)); void cleartraps ARGS((void)); void restoresigs ARGS((void)); void settrap ARGS((Trap *p, char *s)); int block_pipe ARGS((void)); void restore_pipe ARGS((int restore_dfl)); int setsig ARGS((Trap *p, handler_t f, int flags)); void setexecsig ARGS((Trap *p, int restore)); /* tree.c */ int fptreef ARGS((struct shf *f, int indent, const char *fmt, ...)); char * snptreef ARGS((char *s, int n, const char *fmt, ...)); struct op * tcopy ARGS((struct op *t, Area *ap)); char * wdcopy ARGS((const char *wp, Area *ap)); char * wdscan ARGS((const char *wp, int c)); char * wdstrip ARGS((const char *wp)); void tfree ARGS((struct op *t, Area *ap)); /* var.c */ void newblock ARGS((void)); void popblock ARGS((void)); void initvar ARGS((void)); struct tbl * global ARGS((const char *n)); struct tbl * local ARGS((const char *n, bool_t copy)); char * str_val ARGS((struct tbl *vp)); long intval ARGS((struct tbl *vp)); int setstr ARGS((struct tbl *vq, const char *s, int error_ok)); struct tbl *setint_v ARGS((struct tbl *vq, struct tbl *vp)); void setint ARGS((struct tbl *vq, long n)); int getint ARGS((struct tbl *vp, long *nump)); struct tbl * typeset ARGS((const char *var, Tflag set, Tflag clr, int field, int base)); void unset ARGS((struct tbl *vp, int array_ref)); char * skip_varname ARGS((const char *s, int aok)); char *skip_wdvarname ARGS((const char *s, int aok)); int is_wdvarname ARGS((const char *s, int aok)); int is_wdvarassign ARGS((const char *s)); char ** makenv ARGS((void)); void change_random ARGS((void)); int array_ref_len ARGS((const char *cp)); char * arrayname ARGS((const char *str)); void set_array ARGS((const char *var, int reset, char **vals)); /* version.c */ /* vi.c: see edit.h */ /* Hack to avoid billions of compile warnings on SunOS 4.1.x */ #if defined(MUN) && defined(sun) && !defined(__svr4__) extern void bcopy ARGS((const void *src, void *dst, size_t size)); extern int fclose ARGS((FILE *fp)); extern int fprintf ARGS((FILE *fp, const char *fmt, ...)); extern int fread ARGS((void *buf, int size, int num, FILE *fp)); extern int ioctl ARGS((int fd, int request, void *arg)); extern int killpg ARGS((int pgrp, int sig)); extern int nice ARGS((int n)); extern int readlink ARGS((const char *path, char *buf, int bufsize)); extern int setpgrp ARGS((int pid, int pgrp)); extern int strcasecmp ARGS((const char *s1, const char *s2)); extern int tolower ARGS((int)); extern int toupper ARGS((int)); /* Include files aren't included yet */ extern int getrlimit ARGS(( /* int resource, struct rlimit *rpl */ )); extern int getrusage ARGS(( /* int who, struct rusage *rusage */ )); extern int gettimeofday ARGS(( /* struct timeval *tv, struct timezone *tz */ )); extern int setrlimit ARGS(( /* int resource, struct rlimit *rlp */ )); extern int lstat ARGS(( /* const char *path, struct stat *buf */ )); #endif /sys/src/ape/cmd/pdksh/sh.h 664 sys sys 1367613437 22835 /* * Public Domain Bourne/Korn shell */ /* $Id: sh.h,v 1.2 1994/05/19 18:32:40 michael Exp michael $ */ #include "config.h" /* system and option configuration info */ #ifdef HAVE_PROTOTYPES # define ARGS(args) args /* prototype declaration */ #else # define ARGS(args) () /* K&R declaration */ #endif /* Start of common headers */ #include #include #include #ifdef HAVE_STDDEF_H # include #endif #ifdef HAVE_STDLIB_H # include #else /* just a useful subset of what stdlib.h would have */ extern char * getenv ARGS((const char *)); extern void * malloc ARGS((size_t)); extern void * realloc ARGS((void *, size_t)); extern int free ARGS((void *)); extern int exit ARGS((int)); extern int rand ARGS((void)); extern void srand ARGS((unsigned int)); extern int atoi ARGS((const char *)); #endif /* HAVE_STDLIB_H */ #ifdef HAVE_UNISTD_H # include #else /* just a useful subset of what unistd.h would have */ extern int access ARGS((const char *, int)); extern int open ARGS((const char *, int, ...)); extern int creat ARGS((const char *, mode_t)); extern int read ARGS((int, char *, unsigned)); extern int write ARGS((int, const char *, unsigned)); extern off_t lseek ARGS((int, off_t, int)); extern int close ARGS((int)); extern int pipe ARGS((int [])); extern int dup2 ARGS((int, int)); extern int unlink ARGS((const char *)); extern int fork ARGS((void)); extern int execve ARGS((const char *, char * const[], char * const[])); extern int chdir ARGS((const char *)); extern int kill ARGS((pid_t, int)); extern char *getcwd(); /* no ARGS here - differs on different machines */ extern int geteuid ARGS((void)); extern int readlink ARGS((const char *, char *, int)); extern int getegid ARGS((void)); extern int getpid ARGS((void)); extern int getppid ARGS((void)); extern unsigned int sleep ARGS((unsigned int)); extern int isatty ARGS((int)); # ifdef POSIX_PGRP extern int getpgrp ARGS((void)); extern int setpgid ARGS((pid_t, pid_t)); # endif /* POSIX_PGRP */ # ifdef BSD_PGRP extern int getpgrp ARGS((pid_t)); extern int setpgrp ARGS((pid_t, pid_t)); # endif /* BSD_PGRP */ # ifdef SYSV_PGRP extern int getpgrp ARGS((void)); extern int setpgrp ARGS((void)); # endif /* SYSV_PGRP */ #endif /* HAVE_UNISTD_H */ #ifdef HAVE_STRING_H # include #else # include # define strchr index # define strrchr rindex #endif /* HAVE_STRING_H */ #ifndef HAVE_STRSTR char *strstr ARGS((const char *s, const char *p)); #endif /* HAVE_STRSTR */ #ifndef HAVE_STRCASECMP int strcasecmp ARGS((const char *s1, const char *s2)); int strncasecmp ARGS((const char *s1, const char *s2, int n)); #endif /* HAVE_STRCASECMP */ #ifdef HAVE_MEMORY_H # include #endif #ifndef HAVE_MEMSET # define memcpy(d, s, n) bcopy(s, d, n) # define memcmp(s1, s2, n) bcmp(s1, s2, n) void *memset ARGS((void *d, int c, size_t n)); #endif /* HAVE_MEMSET */ #ifndef HAVE_MEMMOVE # ifdef HAVE_BCOPY # define memmove(d, s, n) bcopy(s, d, n) # else void *memmove ARGS((void *d, const void *s, size_t n)); # endif #endif /* HAVE_MEMMOVE */ #ifdef HAVE_PROTOTYPES # include # define SH_VA_START(va, argn) va_start(va, argn) #else # include # define SH_VA_START(va, argn) va_start(va) #endif /* HAVE_PROTOTYPES */ #include extern int errno; #ifdef HAVE_FCNTL_H # include #else # include #endif /* HAVE_FCNTL_H */ #ifndef O_ACCMODE # define O_ACCMODE (O_RDONLY|O_WRONLY|O_RDWR) #endif /* !O_ACCMODE */ #ifndef F_OK /* access() arguments */ # define F_OK 0 # define X_OK 1 # define W_OK 2 # define R_OK 4 #endif /* !F_OK */ #ifndef SEEK_SET # ifdef L_SET # define SEEK_SET L_SET # define SEEK_CUR L_INCR # define SEEK_END L_XTND # else /* L_SET */ # define SEEK_SET 0 # define SEEK_CUR 1 # define SEEK_END 2 # endif /* L_SET */ #endif /* !SEEK_SET */ /* Some machines (eg, FreeBSD 1.1.5) define CLK_TCK in limits.h * (ksh_limval.h assumes limits has been included, if available) */ #ifdef HAVE_LIMITS_H # include #endif /* HAVE_LIMITS_H */ #include #ifdef NSIG # define SIGNALS NSIG #else # ifdef _MINIX # define SIGNALS (_NSIG+1) /* _NSIG is # of signals used, excluding 0. */ # else # ifdef _SIGMAX /* QNX */ # define SIGNALS _SIGMAX # else /* _SIGMAX */ # define SIGNALS 32 # endif /* _SIGMAX */ # endif /* _MINIX */ #endif /* NSIG */ #ifndef SIGCHLD # define SIGCHLD SIGCLD #endif /* struct sigaction.sa_flags is set to KSH_SA_FLAGS. Used to ensure * system calls are interrupted */ #ifdef SA_INTERRUPT # define KSH_SA_FLAGS SA_INTERRUPT #else /* SA_INTERRUPT */ # define KSH_SA_FLAGS 0 #endif /* SA_INTERRUPT */ typedef RETSIGTYPE (*handler_t) ARGS((int)); /* signal handler */ #ifdef USE_FAKE_SIGACT # include "sigact.h" /* use sjg's fake sigaction() */ #endif #ifdef HAVE_PATHS_H # include #endif /* HAVE_PATHS_H */ #ifdef _PATH_DEFPATH # define DEFAULT__PATH _PATH_DEFPATH #else /* _PATH_DEFPATH */ # define DEFAULT__PATH DEFAULT_PATH #endif /* _PATH_DEFPATH */ #ifndef offsetof # define offsetof(type,id) ((size_t)&((type*)NULL)->id) #endif #ifndef HAVE_KILLPG # define killpg(p, s) kill(-(p), (s)) #endif /* !HAVE_KILLPG */ /* Special cases for execve(2) */ #ifdef OS2 extern int ksh_execve(char *cmd, char **args, char **env, int flags); #else /* OS2 */ # if defined(OS_ISC) && defined(_POSIX_SOURCE) /* Kludge for ISC 3.2 (and other versions?) so programs will run correctly. */ # define ksh_execve(p, av, ev, flags) \ do { \ __setostype(0); \ execve(p, av, ev); \ __setostype(1); \ } while (0) # else /* OS_ISC && _POSIX */ # define ksh_execve(p, av, ev, flags) execve(p, av, ev) # endif /* OS_ISC && _POSIX */ #endif /* OS2 */ /* this is a hang-over from older versions of the os2 port */ #define ksh_dupbase(fd, base) fcntl(fd, F_DUPFD, base) #ifdef HAVE_SIGSETJMP # define ksh_sigsetjmp(env,sm) sigsetjmp((env), (sm)) # define ksh_siglongjmp(env,v) siglongjmp((env), (v)) # define ksh_jmp_buf sigjmp_buf #else /* HAVE_SIGSETJMP */ # ifdef HAVE__SETJMP # define ksh_sigsetjmp(env,sm) _setjmp(env) # define ksh_siglongjmp(env,v) _longjmp((env), (v)) # else /* HAVE__SETJMP */ # define ksh_sigsetjmp(env,sm) setjmp(env) # define ksh_siglongjmp(env,v) longjmp((env), (v)) # endif /* HAVE__SETJMP */ # define ksh_jmp_buf jmp_buf #endif /* HAVE_SIGSETJMP */ #ifndef HAVE_DUP2 extern int dup2 ARGS((int, int)); #endif /* !HAVE_DUP2 */ /* Find a integer type that is at least 32 bits (or die) - SIZEOF_* defined * by autoconf (assumes an 8 bit byte, but I'm not concerned). * NOTE: INT32 may end up being more than 32 bits. */ #ifdef __OLD__ #if SIZEOF_INT >= 4 # define INT32 long /* #else SIZEOF_INT */ # if SIZEOF_LONG >= 4 # define INT32 long # else /* SIZEOF_LONG */ #error cannot find 32 bit type... # endif /* SIZEOF_LONG */ #endif /* SIZEOF_INT */ #endif #define INT32 long /* end of common headers */ /* Stop gcc and lint from complaining about possibly uninitialized variables */ #if defined(__GNUC__) || defined(lint) # define UNINITIALIZED(var) var = 0 #else # define UNINITIALIZED(var) var #endif /* GNUC || lint */ /* some useful #defines */ #ifdef EXTERN # define I__(i) = i #else # define I__(i) # define EXTERN extern # define EXTERN_DEFINED #endif #ifdef OS2 # define inDOS() (!(_emx_env & 0x200)) #endif #ifndef EXECSHELL /* shell to exec scripts (see also $SHELL initialization in main.c) */ # ifdef OS2 # define EXECSHELL (inDOS() ? "c:\\command.com" : "c:\\os2\\cmd.exe") # define EXECSHELL_STR (inDOS() ? "COMSPEC" : "OS2_SHELL") # else /* OS2 */ # define EXECSHELL "/bin/sh" # define EXECSHELL_STR "EXECSHELL" # endif /* OS2 */ #endif /* ISABSPATH() means path is fully and completely specified, * ISROOTEDPATH() means a .. as the first component is a no-op, * ISRELPATH() means $PWD can be tacked on to get an absolute path. * * OS Path ISABSPATH ISROOTEDPATH ISRELPATH * unix /foo yes yes no * unix foo no no yes * unix ../foo no no yes * os2+cyg a:/foo yes yes no * os2+cyg a:foo no no no * os2+cyg /foo no yes no * os2+cyg foo no no yes * os2+cyg ../foo no no yes * cyg //foo yes yes no */ #ifdef OS2 # define PATHSEP ';' # define DIRSEP '/' /* even though \ is native */ # define DIRSEPSTR "\\" # define ISDIRSEP(c) ((c) == '\\' || (c) == '/') # define ISABSPATH(s) (((s)[0] && (s)[1] == ':' && ISDIRSEP((s)[2]))) # define ISROOTEDPATH(s) (ISDIRSEP((s)[0]) || ISABSPATH(s)) # define ISRELPATH(s) (!(s)[0] || ((s)[1] != ':' && !ISDIRSEP((s)[0]))) # define FILECHCONV(c) (isascii(c) && isupper(c) ? tolower(c) : c) # define FILECMP(s1, s2) stricmp(s1, s2) # define FILENCMP(s1, s2, n) strnicmp(s1, s2, n) extern char *ksh_strchr_dirsep(const char *path); extern char *ksh_strrchr_dirsep(const char *path); # define chdir _chdir2 # define getcwd _getcwd2 #else # define PATHSEP ':' # define DIRSEP '/' # define DIRSEPSTR "/" # define ISDIRSEP(c) ((c) == '/') #ifdef __CYGWIN__ # define ISABSPATH(s) \ (((s)[0] && (s)[1] == ':' && ISDIRSEP((s)[2])) || ISDIRSEP((s)[0])) # define ISRELPATH(s) (!(s)[0] || ((s)[1] != ':' && !ISDIRSEP((s)[0]))) #else /* __CYGWIN__ */ # define ISABSPATH(s) ISDIRSEP((s)[0]) # define ISRELPATH(s) (!ISABSPATH(s)) #endif /* __CYGWIN__ */ # define ISROOTEDPATH(s) ISABSPATH(s) # define FILECHCONV(c) c # define FILECMP(s1, s2) strcmp(s1, s2) # define FILENCMP(s1, s2, n) strncmp(s1, s2, n) # define ksh_strchr_dirsep(p) strchr(p, DIRSEP) # define ksh_strrchr_dirsep(p) strrchr(p, DIRSEP) #endif typedef int bool_t; #define FALSE 0 #define TRUE 1 #define NELEM(a) (sizeof(a) / sizeof((a)[0])) #define sizeofN(type, n) (sizeof(type) * (n)) #define BIT(i) (1<<(i)) /* define bit in flag */ /* Table flag type - needs > 16 and < 32 bits */ typedef INT32 Tflag; #define NUFILE 10 /* Number of user-accessible files */ #define FDBASE 10 /* First file usable by Shell */ /* you're not going to run setuid shell scripts, are you? */ #define eaccess(path, mode) access(path, mode) /* Make MAGIC a char that might be printed to make bugs more obvious, but * not a char that is used often. Also, can't use the high bit as it causes * portability problems (calling strchr(x, 0x80|'x') is error prone). */ #define MAGIC (7)/* prefix for *?[!{,} during expand */ #define ISMAGIC(c) ((unsigned char)(c) == MAGIC) #define NOT '!' /* might use ^ (ie, [!...] vs [^..]) */ #define LINE 1024 /* input line size */ #define PATH 1024 /* pathname size (todo: PATH_MAX/pathconf()) */ #define ARRAYMAX 1023 /* max array index */ EXTERN const char *kshname; /* $0 */ EXTERN pid_t kshpid; /* $$, shell pid */ EXTERN pid_t procpid; /* pid of executing process */ EXTERN int ksheuid; /* effective uid of shell */ EXTERN int exstat; /* exit status */ EXTERN int subst_exstat; /* exit status of last $(..)/`..` */ EXTERN const char *safe_prompt; /* safe prompt if PS1 substitution fails */ /* * Area-based allocation built on malloc/free */ typedef struct Area { struct Block *freelist; /* free list */ } Area; EXTERN Area aperm; /* permanent object space */ #define APERM &aperm #define ATEMP &e->area #ifdef MEM_DEBUG # include "chmem.h" /* a debugging front end for malloc et. al. */ #endif /* MEM_DEBUG */ #ifdef KSH_DEBUG # define kshdebug_init() kshdebug_init_() # define kshdebug_printf(a) kshdebug_printf_ a # define kshdebug_dump(a) kshdebug_dump_ a #else /* KSH_DEBUG */ # define kshdebug_init() # define kshdebug_printf(a) # define kshdebug_dump(a) #endif /* KSH_DEBUG */ /* * parsing & execution environment */ EXTERN struct env { short type; /* enviroment type - see below */ short flags; /* EF_* */ Area area; /* temporary allocation area */ struct block *loc; /* local variables and functions */ short *savefd; /* original redirected fd's */ struct env *oenv; /* link to previous enviroment */ ksh_jmp_buf jbuf; /* long jump back to env creator */ struct temp *temps; /* temp files */ } *e; /* struct env.type values */ #define E_NONE 0 /* dummy enviroment */ #define E_PARSE 1 /* parsing command # */ #define E_FUNC 2 /* executing function # */ #define E_INCL 3 /* including a file via . # */ #define E_EXEC 4 /* executing command tree */ #define E_LOOP 5 /* executing for/while # */ #define E_ERRH 6 /* general error handler # */ /* # indicates env has valid jbuf (see unwind()) */ /* struct env.flag values */ #define EF_FUNC_PARSE BIT(0) /* function being parsed */ #define EF_BRKCONT_PASS BIT(1) /* set if E_LOOP must pass break/continue on */ #define EF_FAKE_SIGDIE BIT(2) /* hack to get info from unwind to quitenv */ /* Do breaks/continues stop at env type e? */ #define STOP_BRKCONT(t) ((t) == E_NONE || (t) == E_PARSE \ || (t) == E_FUNC || (t) == E_INCL) /* Do returns stop at env type e? */ #define STOP_RETURN(t) ((t) == E_FUNC || (t) == E_INCL) /* values for ksh_siglongjmp(e->jbuf, 0) */ #define LRETURN 1 /* return statement */ #define LEXIT 2 /* exit statement */ #define LERROR 3 /* errorf() called */ #define LLEAVE 4 /* untrappable exit/error */ #define LINTR 5 /* ^C noticed */ #define LBREAK 6 /* break statement */ #define LCONTIN 7 /* continue statement */ #define LSHELL 8 /* return to interactive shell() */ #define LAEXPR 9 /* error in arithmetic expression */ /* option processing */ #define OF_CMDLINE 0x01 /* command line */ #define OF_SET 0x02 /* set builtin */ #define OF_SPECIAL 0x04 /* a special variable changing */ #define OF_INTERNAL 0x08 /* set internally by shell */ #define OF_ANY (OF_CMDLINE | OF_SET | OF_SPECIAL | OF_INTERNAL) struct option { const char *name; /* long name of option */ char c; /* character flag (if any) */ short flags; /* OF_* */ }; extern const struct option options[]; /* * flags (the order of these enums MUST match the order in misc.c(options[])) */ enum sh_flag { FEXPORT = 0, /* -a: export all */ #ifdef BRACE_EXPAND FBRACEEXPAND, /* enable {} globbing */ #endif FBGNICE, /* bgnice */ FCOMMAND, /* -c: (invocation) execute specified command */ #ifdef EMACS FEMACS, /* emacs command editing */ #endif FERREXIT, /* -e: quit on error */ #ifdef EMACS FGMACS, /* gmacs command editing */ #endif FIGNOREEOF, /* eof does not exit */ FTALKING, /* -i: interactive */ FKEYWORD, /* -k: name=value anywere */ FLOGIN, /* -l: a login shell */ FMARKDIRS, /* mark dirs with / in file name completion */ FMONITOR, /* -m: job control monitoring */ FNOCLOBBER, /* -C: don't overwrite existing files */ FNOEXEC, /* -n: don't execute any commands */ FNOGLOB, /* -f: don't do file globbing */ FNOHUP, /* -H: don't kill running jobs when login shell exits */ FNOTTALKING, /* -I: don't be interactive */ FNOLOG, /* don't save functions in history (ignored) */ #ifdef JOBS FNOTIFY, /* -b: asynchronous job completion notification */ #endif FNOUNSET, /* -u: using an unset var is an error */ FPHYSICAL, /* -o physical: don't do logical cd's/pwd's */ FPOSIX, /* -o posix: be posixly correct */ FPRIVILEGED, /* -p: use suid_profile */ FRESTRICTED, /* -r: restricted shell */ FSTDIN, /* -s: (invocation) parse stdin */ FTRACKALL, /* -h: create tracked aliases for all commands */ FVERBOSE, /* -v: echo input */ #ifdef VI FVI, /* vi command editing */ FVIRAW, /* always read in raw mode (ignored) */ FVISHOW8, /* display chars with 8th bit set as is (versus M-) */ FVITABCOMPLETE, /* enable tab as file name completion char */ FVIESCCOMPLETE, /* enable ESC as file name completion in command mode */ #endif FXTRACE, /* -x: execution trace */ FTALKING_I, /* (internal): initial shell was interactive */ FNFLAGS /* (place holder: how many flags are there) */ }; #define Flag(f) (shell_flags[(int) (f)]) EXTERN char shell_flags [FNFLAGS]; EXTERN char null [] I__(""); /* null value for variable */ EXTERN char space [] I__(" "); EXTERN char newline [] I__("\n"); EXTERN char slash [] I__("/"); enum temp_type { TT_HEREDOC_EXP, /* expanded heredoc */ TT_HIST_EDIT /* temp file used for history editing (fc -e) */ }; typedef enum temp_type Temp_type; /* temp/heredoc files. The file is removed when the struct is freed. */ struct temp { struct temp *next; struct shf *shf; int pid; /* pid of process parsed here-doc */ Temp_type type; char *name; }; /* * stdio and our IO routines */ #define shl_spare (&shf_iob[0]) /* for c_read()/c_print() */ #define shl_stdout (&shf_iob[1]) #define shl_out (&shf_iob[2]) EXTERN int shl_stdout_ok; /* * trap handlers */ typedef struct trap { int signal; /* signal number */ const char *name; /* short name */ const char *mess; /* descriptive name */ char *trap; /* trap command */ int volatile set; /* trap pending */ int flags; /* TF_* */ handler_t cursig; /* current handler (valid if TF_ORIG_* set) */ handler_t shtrap; /* shell signal handler */ } Trap; /* values for Trap.flags */ #define TF_SHELL_USES BIT(0) /* shell uses signal, user can't change */ #define TF_USER_SET BIT(1) /* user has (tried to) set trap */ #define TF_ORIG_IGN BIT(2) /* original action was SIG_IGN */ #define TF_ORIG_DFL BIT(3) /* original action was SIG_DFL */ #define TF_EXEC_IGN BIT(4) /* restore SIG_IGN just before exec */ #define TF_EXEC_DFL BIT(5) /* restore SIG_DFL just before exec */ #define TF_DFL_INTR BIT(6) /* when received, default action is LINTR */ #define TF_TTY_INTR BIT(7) /* tty generated signal (see j_waitj) */ #define TF_CHANGED BIT(8) /* used by runtrap() to detect trap changes */ #define TF_FATAL BIT(9) /* causes termination if not trapped */ /* values for setsig()/setexecsig() flags argument */ #define SS_RESTORE_MASK 0x3 /* how to restore a signal before an exec() */ #define SS_RESTORE_CURR 0 /* leave current handler in place */ #define SS_RESTORE_ORIG 1 /* restore original handler */ #define SS_RESTORE_DFL 2 /* restore to SIG_DFL */ #define SS_RESTORE_IGN 3 /* restore to SIG_IGN */ #define SS_FORCE BIT(3) /* set signal even if original signal ignored */ #define SS_USER BIT(4) /* user is doing the set (ie, trap command) */ #define SS_SHTRAP BIT(5) /* trap for internal use (CHLD,ALRM,WINCH) */ #define SIGEXIT_ 0 /* for trap EXIT */ #define SIGERR_ SIGNALS /* for trap ERR */ EXTERN int volatile trap; /* traps pending? */ EXTERN int volatile intrsig; /* pending trap interrupts executing command */ EXTERN int volatile fatal_trap;/* received a fatal signal */ #ifndef FROM_TRAP_C /* Kludge to avoid bogus re-declaration of sigtraps[] error on AIX 3.2.5 */ extern Trap sigtraps[SIGNALS+1]; #endif /* !FROM_TRAP_C */ #ifdef KSH /* * TMOUT support */ /* values for ksh_tmout_state */ enum tmout_enum { TMOUT_EXECUTING = 0, /* executing commands */ TMOUT_READING, /* waiting for input */ TMOUT_LEAVING /* have timed out */ }; EXTERN unsigned int ksh_tmout; EXTERN enum tmout_enum ksh_tmout_state I__(TMOUT_EXECUTING); #endif /* KSH */ /* For "You have stopped jobs" message */ EXTERN int really_exit; /* * fast character classes */ #define C_ALPHA BIT(0) /* a-z_A-Z */ #define C_DIGIT BIT(1) /* 0-9 */ #define C_LEX1 BIT(2) /* \0 \t\n|&;<>() */ #define C_VAR1 BIT(3) /* *@#!$-? */ #define C_IFSWS BIT(4) /* \t \n (IFS white space) */ #define C_SUBOP1 BIT(5) /* "=-+?" */ #define C_SUBOP2 BIT(6) /* "#%" */ #define C_IFS BIT(7) /* $IFS */ #define C_QUOTE BIT(8) /* \n\t"#$&'()*;<>?[\`| (needing quoting) */ extern short ctypes []; #define ctype(c, t) !!(ctypes[(unsigned char)(c)]&(t)) #define letter(c) ctype(c, C_ALPHA) #define digit(c) ctype(c, C_DIGIT) #define letnum(c) ctype(c, C_ALPHA|C_DIGIT) EXTERN int ifs0 I__(' '); /* for "$*" */ /* Argument parsing for built-in commands and getopts command */ /* Values for Getopt.flags */ #define GF_ERROR BIT(0) /* call errorf() if there is an error */ #define GF_PLUSOPT BIT(1) /* allow +c as an option */ #define GF_NONAME BIT(2) /* don't print argv[0] in errors */ /* Values for Getopt.info */ #define GI_MINUS BIT(0) /* an option started with -... */ #define GI_PLUS BIT(1) /* an option started with +... */ #define GI_MINUSMINUS BIT(2) /* arguments were ended with -- */ typedef struct { int optind; int uoptind;/* what user sees in $OPTIND */ char *optarg; int flags; /* see GF_* */ int info; /* see GI_* */ unsigned int p; /* 0 or index into argv[optind - 1] */ char buf[2]; /* for bad option OPTARG value */ } Getopt; EXTERN Getopt builtin_opt; /* for shell builtin commands */ EXTERN Getopt user_opt; /* parsing state for getopts builtin command */ #ifdef KSH /* This for co-processes */ typedef INT32 Coproc_id; /* something that won't (realisticly) wrap */ struct coproc { int read; /* pipe from co-process's stdout */ int readw; /* other side of read (saved temporarily) */ int write; /* pipe to co-process's stdin */ Coproc_id id; /* id of current output pipe */ int njobs; /* number of live jobs using output pipe */ void *job; /* 0 or job of co-process using input pipe */ }; EXTERN struct coproc coproc; #endif /* KSH */ /* Used in jobs.c and by coprocess stuff in exec.c */ #ifdef JOB_SIGS EXTERN sigset_t sm_default, sm_sigchld; #endif /* JOB_SIGS */ extern const char ksh_version[]; /* name of called builtin function (used by error functions) */ EXTERN char *builtin_argv0; EXTERN Tflag builtin_flag; /* flags of called builtin (SPEC_BI, etc.) */ /* current working directory, and size of memory allocated for same */ EXTERN char *current_wd; EXTERN int current_wd_size; #ifdef EDIT /* Minimium required space to work with on a line - if the prompt leaves less * space than this on a line, the prompt is truncated. */ # define MIN_EDIT_SPACE 7 /* Minimium allowed value for x_cols: 2 for prompt, 3 for " < " at end of line */ # define MIN_COLS (2 + MIN_EDIT_SPACE + 3) EXTERN int x_cols I__(80); /* tty columns */ #else # define x_cols 80 /* for pr_menu(exec.c) */ #endif /* These to avoid bracket matching problems */ #define OPAREN '(' #define CPAREN ')' #define OBRACK '[' #define CBRACK ']' #define OBRACE '{' #define CBRACE '}' /* Determine the location of the system (common) profile */ #ifndef KSH_SYSTEM_PROFILE # ifdef __NeXT # define KSH_SYSTEM_PROFILE "/etc/profile.std" # else /* __NeXT */ # define KSH_SYSTEM_PROFILE "/etc/profile" # endif /* __NeXT */ #endif /* KSH_SYSTEM_PROFILE */ /* Used by v_evaluate() and setstr() to control action when error occurs */ #define KSH_UNWIND_ERROR 0 /* unwind the stack (longjmp) */ #define KSH_RETURN_ERROR 1 /* return 1/0 for success/failure */ #include "shf.h" #include "table.h" #include "tree.h" #include "expand.h" #include "lex.h" #include "proto.h" /* be sure not to interfere with anyone else's idea about EXTERN */ #ifdef EXTERN_DEFINED # undef EXTERN_DEFINED # undef EXTERN #endif #undef I__ /sys/src/ape/cmd/pdksh/shf.c 664 sys sys 1367613437 27625 /* * Shell file I/O routines */ #include "sh.h" #include "ksh_stat.h" #include "ksh_limval.h" /* flags to shf_emptybuf() */ #define EB_READSW 0x01 /* about to switch to reading */ #define EB_GROW 0x02 /* grow buffer if necessary (STRING+DYNAMIC) */ /* * Replacement stdio routines. Stdio is too flakey on too many machines * to be useful when you have multiple processes using the same underlying * file descriptors. */ static int shf_fillbuf ARGS((struct shf *shf)); static int shf_emptybuf ARGS((struct shf *shf, int flags)); /* Open a file. First three args are for open(), last arg is flags for * this package. Returns NULL if file could not be opened, or if a dup * fails. */ struct shf * shf_open(name, oflags, mode, sflags) const char *name; int oflags; int mode; int sflags; { struct shf *shf; int bsize = sflags & SHF_UNBUF ? (sflags & SHF_RD ? 1 : 0) : SHF_BSIZE; int fd; /* Done before open so if alloca fails, fd won't be lost. */ shf = (struct shf *) alloc(sizeof(struct shf) + bsize, ATEMP); shf->areap = ATEMP; shf->buf = (unsigned char *) &shf[1]; shf->bsize = bsize; shf->flags = SHF_ALLOCS; /* Rest filled in by reopen. */ fd = open(name, oflags, mode); if (fd < 0) { afree(shf, shf->areap); return NULL; } if ((sflags & SHF_MAPHI) && fd < FDBASE) { int nfd; nfd = ksh_dupbase(fd, FDBASE); close(fd); if (nfd < 0) { afree(shf, shf->areap); return NULL; } fd = nfd; } sflags &= ~SHF_ACCMODE; sflags |= (oflags & O_ACCMODE) == O_RDONLY ? SHF_RD : ((oflags & O_ACCMODE) == O_WRONLY ? SHF_WR : SHF_RDWR); return shf_reopen(fd, sflags, shf); } /* Set up the shf structure for a file descriptor. Doesn't fail. */ struct shf * shf_fdopen(fd, sflags, shf) int fd; int sflags; struct shf *shf; { int bsize = sflags & SHF_UNBUF ? (sflags & SHF_RD ? 1 : 0) : SHF_BSIZE; /* use fcntl() to figure out correct read/write flags */ if (sflags & SHF_GETFL) { int flags = fcntl(fd, F_GETFL, 0); if (flags < 0) /* will get an error on first read/write */ sflags |= SHF_RDWR; else switch (flags & O_ACCMODE) { case O_RDONLY: sflags |= SHF_RD; break; case O_WRONLY: sflags |= SHF_WR; break; case O_RDWR: sflags |= SHF_RDWR; break; } } if (!(sflags & (SHF_RD | SHF_WR))) internal_errorf(1, "shf_fdopen: missing read/write"); if (shf) { if (bsize) { shf->buf = (unsigned char *) alloc(bsize, ATEMP); sflags |= SHF_ALLOCB; } else shf->buf = (unsigned char *) 0; } else { shf = (struct shf *) alloc(sizeof(struct shf) + bsize, ATEMP); shf->buf = (unsigned char *) &shf[1]; sflags |= SHF_ALLOCS; } shf->areap = ATEMP; shf->fd = fd; shf->rp = shf->wp = shf->buf; shf->rnleft = 0; shf->rbsize = bsize; shf->wnleft = 0; /* force call to shf_emptybuf() */ shf->wbsize = sflags & SHF_UNBUF ? 0 : bsize; shf->flags = sflags; shf->errno_ = 0; shf->bsize = bsize; if (sflags & SHF_CLEXEC) fd_clexec(fd); return shf; } /* Set up an existing shf (and buffer) to use the given fd */ struct shf * shf_reopen(fd, sflags, shf) int fd; int sflags; struct shf *shf; { int bsize = sflags & SHF_UNBUF ? (sflags & SHF_RD ? 1 : 0) : SHF_BSIZE; /* use fcntl() to figure out correct read/write flags */ if (sflags & SHF_GETFL) { int flags = fcntl(fd, F_GETFL, 0); if (flags < 0) /* will get an error on first read/write */ sflags |= SHF_RDWR; else switch (flags & O_ACCMODE) { case O_RDONLY: sflags |= SHF_RD; break; case O_WRONLY: sflags |= SHF_WR; break; case O_RDWR: sflags |= SHF_RDWR; break; } } if (!(sflags & (SHF_RD | SHF_WR))) internal_errorf(1, "shf_reopen: missing read/write"); if (!shf || !shf->buf || shf->bsize < bsize) internal_errorf(1, "shf_reopen: bad shf/buf/bsize"); /* assumes shf->buf and shf->bsize already set up */ shf->fd = fd; shf->rp = shf->wp = shf->buf; shf->rnleft = 0; shf->rbsize = bsize; shf->wnleft = 0; /* force call to shf_emptybuf() */ shf->wbsize = sflags & SHF_UNBUF ? 0 : bsize; shf->flags = (shf->flags & (SHF_ALLOCS | SHF_ALLOCB)) | sflags; shf->errno_ = 0; if (sflags & SHF_CLEXEC) fd_clexec(fd); return shf; } /* Open a string for reading or writing. If reading, bsize is the number * of bytes that can be read. If writing, bsize is the maximum number of * bytes that can be written. If shf is not null, it is filled in and * returned, if it is null, shf is allocated. If writing and buf is null * and SHF_DYNAMIC is set, the buffer is allocated (if bsize > 0, it is * used for the initial size). Doesn't fail. * When writing, a byte is reserved for a trailing null - see shf_sclose(). */ struct shf * shf_sopen(buf, bsize, sflags, shf) char *buf; int bsize; int sflags; struct shf *shf; { /* can't have a read+write string */ if (!(sflags & (SHF_RD | SHF_WR)) || (sflags & (SHF_RD | SHF_WR)) == (SHF_RD | SHF_WR)) internal_errorf(1, "shf_sopen: flags 0x%x", sflags); if (!shf) { shf = (struct shf *) alloc(sizeof(struct shf), ATEMP); sflags |= SHF_ALLOCS; } shf->areap = ATEMP; if (!buf && (sflags & SHF_WR) && (sflags & SHF_DYNAMIC)) { if (bsize <= 0) bsize = 64; sflags |= SHF_ALLOCB; buf = alloc(bsize, shf->areap); } shf->fd = -1; shf->buf = shf->rp = shf->wp = (unsigned char *) buf; shf->rnleft = bsize; shf->rbsize = bsize; shf->wnleft = bsize - 1; /* space for a '\0' */ shf->wbsize = bsize; shf->flags = sflags | SHF_STRING; shf->errno_ = 0; shf->bsize = bsize; return shf; } /* Flush and close file descriptor, free the shf structure */ int shf_close(shf) struct shf *shf; { int ret = 0; if (shf->fd >= 0) { ret = shf_flush(shf); if (close(shf->fd) < 0) ret = EOF; } if (shf->flags & SHF_ALLOCS) afree(shf, shf->areap); else if (shf->flags & SHF_ALLOCB) afree(shf->buf, shf->areap); return ret; } /* Flush and close file descriptor, don't free file structure */ int shf_fdclose(shf) struct shf *shf; { int ret = 0; if (shf->fd >= 0) { ret = shf_flush(shf); if (close(shf->fd) < 0) ret = EOF; shf->rnleft = 0; shf->rp = shf->buf; shf->wnleft = 0; shf->fd = -1; } return ret; } /* Close a string - if it was opened for writing, it is null terminated; * returns a pointer to the string and frees shf if it was allocated * (does not free string if it was allocated). */ char * shf_sclose(shf) struct shf *shf; { unsigned char *s = shf->buf; /* null terminate */ if (shf->flags & SHF_WR) { shf->wnleft++; shf_putc('\0', shf); } if (shf->flags & SHF_ALLOCS) afree(shf, shf->areap); return (char *) s; } /* Flush and free file structure, don't close file descriptor */ int shf_finish(shf) struct shf *shf; { int ret = 0; if (shf->fd >= 0) ret = shf_flush(shf); if (shf->flags & SHF_ALLOCS) afree(shf, shf->areap); else if (shf->flags & SHF_ALLOCB) afree(shf->buf, shf->areap); return ret; } /* Un-read what has been read but not examined, or write what has been * buffered. Returns 0 for success, EOF for (write) error. */ int shf_flush(shf) struct shf *shf; { if (shf->flags & SHF_STRING) return (shf->flags & SHF_WR) ? EOF : 0; if (shf->fd < 0) internal_errorf(1, "shf_flush: no fd"); if (shf->flags & SHF_ERROR) { errno = shf->errno_; return EOF; } if (shf->flags & SHF_READING) { shf->flags &= ~(SHF_EOF | SHF_READING); if (shf->rnleft > 0) { lseek(shf->fd, (off_t) -shf->rnleft, 1); shf->rnleft = 0; shf->rp = shf->buf; } return 0; } else if (shf->flags & SHF_WRITING) return shf_emptybuf(shf, 0); return 0; } /* Write out any buffered data. If currently reading, flushes the read * buffer. Returns 0 for success, EOF for (write) error. */ static int shf_emptybuf(shf, flags) struct shf *shf; int flags; { int ret = 0; if (!(shf->flags & SHF_STRING) && shf->fd < 0) internal_errorf(1, "shf_emptybuf: no fd"); if (shf->flags & SHF_ERROR) { errno = shf->errno_; return EOF; } if (shf->flags & SHF_READING) { if (flags & EB_READSW) /* doesn't happen */ return 0; ret = shf_flush(shf); shf->flags &= ~SHF_READING; } if (shf->flags & SHF_STRING) { unsigned char *nbuf; /* Note that we assume SHF_ALLOCS is not set if SHF_ALLOCB * is set... (changing the shf pointer could cause problems) */ if (!(flags & EB_GROW) || !(shf->flags & SHF_DYNAMIC) || !(shf->flags & SHF_ALLOCB)) return EOF; /* allocate more space for buffer */ nbuf = (unsigned char *) aresize(shf->buf, shf->wbsize * 2, shf->areap); shf->rp = nbuf + (shf->rp - shf->buf); shf->wp = nbuf + (shf->wp - shf->buf); shf->rbsize += shf->wbsize; shf->wbsize += shf->wbsize; shf->wnleft += shf->wbsize; shf->wbsize *= 2; shf->buf = nbuf; } else { if (shf->flags & SHF_WRITING) { int ntowrite = shf->wp - shf->buf; unsigned char *buf = shf->buf; int n; while (ntowrite > 0) { n = write(shf->fd, buf, ntowrite); if (n < 0) { if (errno == EINTR && !(shf->flags & SHF_INTERRUPT)) continue; shf->flags |= SHF_ERROR; shf->errno_ = errno; shf->wnleft = 0; if (buf != shf->buf) { /* allow a second flush * to work */ memmove(shf->buf, buf, ntowrite); shf->wp = shf->buf + ntowrite; } return EOF; } buf += n; ntowrite -= n; } if (flags & EB_READSW) { shf->wp = shf->buf; shf->wnleft = 0; shf->flags &= ~SHF_WRITING; return 0; } } shf->wp = shf->buf; shf->wnleft = shf->wbsize; } shf->flags |= SHF_WRITING; return ret; } /* Fill up a read buffer. Returns EOF for a read error, 0 otherwise. */ static int shf_fillbuf(shf) struct shf *shf; { if (shf->flags & SHF_STRING) return 0; if (shf->fd < 0) internal_errorf(1, "shf_fillbuf: no fd"); if (shf->flags & (SHF_EOF | SHF_ERROR)) { if (shf->flags & SHF_ERROR) errno = shf->errno_; return EOF; } if ((shf->flags & SHF_WRITING) && shf_emptybuf(shf, EB_READSW) == EOF) return EOF; shf->flags |= SHF_READING; shf->rp = shf->buf; while (1) { shf->rnleft = blocking_read(shf->fd, (char *) shf->buf, shf->rbsize); if (shf->rnleft < 0 && errno == EINTR && !(shf->flags & SHF_INTERRUPT)) continue; break; } if (shf->rnleft <= 0) { if (shf->rnleft < 0) { shf->flags |= SHF_ERROR; shf->errno_ = errno; shf->rnleft = 0; shf->rp = shf->buf; return EOF; } shf->flags |= SHF_EOF; } return 0; } /* Seek to a new position in the file. If writing, flushes the buffer * first. If reading, optimizes small relative seeks that stay inside the * buffer. Returns 0 for success, EOF otherwise. */ int shf_seek(shf, where, from) struct shf *shf; off_t where; int from; { if (shf->fd < 0) { errno = EINVAL; return EOF; } if (shf->flags & SHF_ERROR) { errno = shf->errno_; return EOF; } if ((shf->flags & SHF_WRITING) && shf_emptybuf(shf, EB_READSW) == EOF) return EOF; if (shf->flags & SHF_READING) { if (from == SEEK_CUR && (where < 0 ? -where >= shf->rbsize - shf->rnleft : where < shf->rnleft)) { shf->rnleft -= where; shf->rp += where; return 0; } shf->rnleft = 0; shf->rp = shf->buf; } shf->flags &= ~(SHF_EOF | SHF_READING | SHF_WRITING); if (lseek(shf->fd, where, from) < 0) { shf->errno_ = errno; shf->flags |= SHF_ERROR; return EOF; } return 0; } /* Read a buffer from shf. Returns the number of bytes read into buf, * if no bytes were read, returns 0 if end of file was seen, EOF if * a read error occurred. */ int shf_read(buf, bsize, shf) char *buf; int bsize; struct shf *shf; { int orig_bsize = bsize; int ncopy; if (!(shf->flags & SHF_RD)) internal_errorf(1, "shf_read: flags %x", shf->flags); if (bsize <= 0) internal_errorf(1, "shf_read: bsize %d", bsize); while (bsize > 0) { if (shf->rnleft == 0 && (shf_fillbuf(shf) == EOF || shf->rnleft == 0)) break; ncopy = shf->rnleft; if (ncopy > bsize) ncopy = bsize; memcpy(buf, shf->rp, ncopy); buf += ncopy; bsize -= ncopy; shf->rp += ncopy; shf->rnleft -= ncopy; } /* Note: fread(3S) returns 0 for errors - this doesn't */ return orig_bsize == bsize ? (shf_error(shf) ? EOF : 0) : orig_bsize - bsize; } /* Read up to a newline or EOF. The newline is put in buf; buf is always * null terminated. Returns NULL on read error or if nothing was read before * end of file, returns a pointer to the null byte in buf otherwise. */ char * shf_getse(buf, bsize, shf) char *buf; int bsize; struct shf *shf; { unsigned char *end; int ncopy; char *orig_buf = buf; if (!(shf->flags & SHF_RD)) internal_errorf(1, "shf_getse: flags %x", shf->flags); if (bsize <= 0) return (char *) 0; --bsize; /* save room for null */ do { if (shf->rnleft == 0) { if (shf_fillbuf(shf) == EOF) return NULL; if (shf->rnleft == 0) { *buf = '\0'; return buf == orig_buf ? NULL : buf; } } end = (unsigned char *) memchr((char *) shf->rp, '\n', shf->rnleft); ncopy = end ? end - shf->rp + 1 : shf->rnleft; if (ncopy > bsize) ncopy = bsize; memcpy(buf, (char *) shf->rp, ncopy); shf->rp += ncopy; shf->rnleft -= ncopy; buf += ncopy; bsize -= ncopy; #ifdef OS2 if (end && buf > orig_buf + 1 && buf[-2] == '\r') { buf--; bsize++; buf[-1] = '\n'; } #endif } while (!end && bsize); *buf = '\0'; return buf; } /* Returns the char read. Returns EOF for error and end of file. */ int shf_getchar(shf) struct shf *shf; { if (!(shf->flags & SHF_RD)) internal_errorf(1, "shf_getchar: flags %x", shf->flags); if (shf->rnleft == 0 && (shf_fillbuf(shf) == EOF || shf->rnleft == 0)) return EOF; --shf->rnleft; return *shf->rp++; } /* Put a character back in the input stream. Returns the character if * successful, EOF if there is no room. */ int shf_ungetc(c, shf) int c; struct shf *shf; { if (!(shf->flags & SHF_RD)) internal_errorf(1, "shf_ungetc: flags %x", shf->flags); if ((shf->flags & SHF_ERROR) || c == EOF || (shf->rp == shf->buf && shf->rnleft)) return EOF; if ((shf->flags & SHF_WRITING) && shf_emptybuf(shf, EB_READSW) == EOF) return EOF; if (shf->rp == shf->buf) shf->rp = shf->buf + shf->rbsize; if (shf->flags & SHF_STRING) { /* Can unget what was read, but not something different - we * don't want to modify a string. */ if (shf->rp[-1] != c) return EOF; shf->flags &= ~SHF_EOF; shf->rp--; shf->rnleft++; return c; } shf->flags &= ~SHF_EOF; *--(shf->rp) = c; shf->rnleft++; return c; } /* Write a character. Returns the character if successful, EOF if * the char could not be written. */ int shf_putchar(c, shf) int c; struct shf *shf; { if (!(shf->flags & SHF_WR)) internal_errorf(1, "shf_putchar: flags %x", shf->flags); if (c == EOF) return EOF; if (shf->flags & SHF_UNBUF) { char cc = c; int n; if (shf->fd < 0) internal_errorf(1, "shf_putchar: no fd"); if (shf->flags & SHF_ERROR) { errno = shf->errno_; return EOF; } while ((n = write(shf->fd, &cc, 1)) != 1) if (n < 0) { if (errno == EINTR && !(shf->flags & SHF_INTERRUPT)) continue; shf->flags |= SHF_ERROR; shf->errno_ = errno; return EOF; } } else { /* Flush deals with strings and sticky errors */ if (shf->wnleft == 0 && shf_emptybuf(shf, EB_GROW) == EOF) return EOF; shf->wnleft--; *shf->wp++ = c; } return c; } /* Write a string. Returns the length of the string if successful, EOF if * the string could not be written. */ int shf_puts(s, shf) const char *s; struct shf *shf; { if (!s) return EOF; return shf_write(s, strlen(s), shf); } /* Write a buffer. Returns nbytes if successful, EOF if there is an error. */ int shf_write(buf, nbytes, shf) const char *buf; int nbytes; struct shf *shf; { int orig_nbytes = nbytes; int n; int ncopy; if (!(shf->flags & SHF_WR)) internal_errorf(1, "shf_write: flags %x", shf->flags); if (nbytes < 0) internal_errorf(1, "shf_write: nbytes %d", nbytes); /* Don't buffer if buffer is empty and we're writting a large amount. */ if ((ncopy = shf->wnleft) && (shf->wp != shf->buf || nbytes < shf->wnleft)) { if (ncopy > nbytes) ncopy = nbytes; memcpy(shf->wp, buf, ncopy); nbytes -= ncopy; buf += ncopy; shf->wp += ncopy; shf->wnleft -= ncopy; } if (nbytes > 0) { /* Flush deals with strings and sticky errors */ if (shf_emptybuf(shf, EB_GROW) == EOF) return EOF; if (nbytes > shf->wbsize) { ncopy = nbytes; if (shf->wbsize) ncopy -= nbytes % shf->wbsize; nbytes -= ncopy; while (ncopy > 0) { n = write(shf->fd, buf, ncopy); if (n < 0) { if (errno == EINTR && !(shf->flags & SHF_INTERRUPT)) continue; shf->flags |= SHF_ERROR; shf->errno_ = errno; shf->wnleft = 0; /* Note: fwrite(3S) returns 0 for * errors - this doesn't */ return EOF; } buf += n; ncopy -= n; } } if (nbytes > 0) { memcpy(shf->wp, buf, nbytes); shf->wp += nbytes; shf->wnleft -= nbytes; } } return orig_nbytes; } int #ifdef HAVE_PROTOTYPES shf_fprintf(struct shf *shf, const char *fmt, ...) #else shf_fprintf(shf, fmt, va_alist) struct shf *shf; const char *fmt; va_dcl #endif { va_list args; int n; SH_VA_START(args, fmt); n = shf_vfprintf(shf, fmt, args); va_end(args); return n; } int #ifdef HAVE_PROTOTYPES shf_snprintf(char *buf, int bsize, const char *fmt, ...) #else shf_snprintf(buf, bsize, fmt, va_alist) char *buf; int bsize; const char *fmt; va_dcl #endif { struct shf shf; va_list args; int n; if (!buf || bsize <= 0) internal_errorf(1, "shf_snprintf: buf %lx, bsize %d", (long) buf, bsize); shf_sopen(buf, bsize, SHF_WR, &shf); SH_VA_START(args, fmt); n = shf_vfprintf(&shf, fmt, args); va_end(args); shf_sclose(&shf); /* null terminates */ return n; } char * #ifdef HAVE_PROTOTYPES shf_smprintf(const char *fmt, ...) #else shf_smprintf(fmt, va_alist) char *fmt; va_dcl #endif { struct shf shf; va_list args; shf_sopen((char *) 0, 0, SHF_WR|SHF_DYNAMIC, &shf); SH_VA_START(args, fmt); shf_vfprintf(&shf, fmt, args); va_end(args); return shf_sclose(&shf); /* null terminates */ } #undef FP /* if you want floating point stuff */ #define BUF_SIZE 128 #define FPBUF_SIZE (DMAXEXP+16)/* this must be > * MAX(DMAXEXP, log10(pow(2, DSIGNIF))) * + ceil(log10(DMAXEXP)) + 8 (I think). * Since this is hard to express as a * constant, just use a large buffer. */ /* * What kinda of machine we on? Hopefully the C compiler will optimize * this out... * * For shorts, we want sign extend for %d but not for %[oxu] - on 16 bit * machines it don't matter. Assmumes C compiler has converted shorts to * ints before pushing them. */ #define POP_INT(f, s, a) (((f) & FL_LONG) ? \ va_arg((a), unsigned long) \ : \ (sizeof(int) < sizeof(long) ? \ ((s) ? \ (long) va_arg((a), int) \ : \ va_arg((a), unsigned)) \ : \ va_arg((a), unsigned))) #define ABIGNUM 32000 /* big numer that will fit in a short */ #define LOG2_10 3.321928094887362347870319429 /* log base 2 of 10 */ #define FL_HASH 0x001 /* `#' seen */ #define FL_PLUS 0x002 /* `+' seen */ #define FL_RIGHT 0x004 /* `-' seen */ #define FL_BLANK 0x008 /* ` ' seen */ #define FL_SHORT 0x010 /* `h' seen */ #define FL_LONG 0x020 /* `l' seen */ #define FL_ZERO 0x040 /* `0' seen */ #define FL_DOT 0x080 /* '.' seen */ #define FL_UPPER 0x100 /* format character was uppercase */ #define FL_NUMBER 0x200 /* a number was formated %[douxefg] */ #ifdef FP #include static double my_ceil(d) double d; { double i; return d - modf(d, &i) + (d < 0 ? -1 : 1); } #endif /* FP */ int shf_vfprintf(shf, fmt, args) struct shf *shf; const char *fmt; va_list args; { char c, *s; int UNINITIALIZED(tmp); int field, precision; int len; int flags; unsigned long lnum; /* %#o produces the longest output */ char numbuf[(BITS(long) + 2) / 3 + 1]; /* this stuff for dealing with the buffer */ int nwritten = 0; #ifdef FP /* should be in * extern double frexp(); */ extern char *ecvt(); double fpnum; int expo, decpt; char style; char fpbuf[FPBUF_SIZE]; #endif /* FP */ if (!fmt) return 0; while ((c = *fmt++)) { if (c != '%') { shf_putc(c, shf); nwritten++; continue; } /* * This will accept flags/fields in any order - not * just the order specified in printf(3), but this is * the way _doprnt() seems to work (on bsd and sysV). * The only resriction is that the format character must * come last :-). */ flags = field = precision = 0; for ( ; (c = *fmt++) ; ) { switch (c) { case '#': flags |= FL_HASH; continue; case '+': flags |= FL_PLUS; continue; case '-': flags |= FL_RIGHT; continue; case ' ': flags |= FL_BLANK; continue; case '0': if (!(flags & FL_DOT)) flags |= FL_ZERO; continue; case '.': flags |= FL_DOT; precision = 0; continue; case '*': tmp = va_arg(args, int); if (flags & FL_DOT) precision = tmp; else if ((field = tmp) < 0) { field = -field; flags |= FL_RIGHT; } continue; case 'l': flags |= FL_LONG; continue; case 'h': flags |= FL_SHORT; continue; } if (digit(c)) { tmp = c - '0'; while (c = *fmt++, digit(c)) tmp = tmp * 10 + c - '0'; --fmt; if (tmp < 0) /* overflow? */ tmp = 0; if (flags & FL_DOT) precision = tmp; else field = tmp; continue; } break; } if (precision < 0) precision = 0; if (!c) /* nasty format */ break; if (c >= 'A' && c <= 'Z') { flags |= FL_UPPER; c = c - 'A' + 'a'; } switch (c) { case 'p': /* pointer */ flags &= ~(FL_LONG | FL_SHORT); if (sizeof(char *) > sizeof(int)) flags |= FL_LONG; /* hope it fits.. */ /* aaahhh... */ case 'd': case 'i': case 'o': case 'u': case 'x': flags |= FL_NUMBER; s = &numbuf[sizeof(numbuf)]; lnum = POP_INT(flags, c == 'd', args); switch (c) { case 'd': case 'i': if (0 > (long) lnum) lnum = - (long) lnum, tmp = 1; else tmp = 0; /* aaahhhh..... */ case 'u': do { *--s = lnum % 10 + '0'; lnum /= 10; } while (lnum); if (c != 'u') { if (tmp) *--s = '-'; else if (flags & FL_PLUS) *--s = '+'; else if (flags & FL_BLANK) *--s = ' '; } break; case 'o': do { *--s = (lnum & 0x7) + '0'; lnum >>= 3; } while (lnum); if ((flags & FL_HASH) && *s != '0') *--s = '0'; break; case 'p': case 'x': { const char *digits = (flags & FL_UPPER) ? "0123456789ABCDEF" : "0123456789abcdef"; do { *--s = digits[lnum & 0xf]; lnum >>= 4; } while (lnum); if (flags & FL_HASH) { *--s = (flags & FL_UPPER) ? 'X' : 'x'; *--s = '0'; } } } len = &numbuf[sizeof(numbuf)] - s; if (flags & FL_DOT) { if (precision > len) { field = precision; flags |= FL_ZERO; } else precision = len; /* no loss */ } break; #ifdef FP case 'e': case 'g': case 'f': { char *p; /* * This could proabably be done better, * but it seems to work. Note that gcvt() * is not used, as you cannot tell it to * not strip the zeros. */ flags |= FL_NUMBER; if (!(flags & FL_DOT)) precision = 6; /* default */ /* * Assumes doubles are pushed on * the stack. If this is not so, then * FL_LONG/FL_SHORT should be checked. */ fpnum = va_arg(args, double); s = fpbuf; style = c; /* * This is the same as * expo = ceil(log10(fpnum)) * but doesn't need -lm. This is an * aproximation as expo is rounded up. */ (void) frexp(fpnum, &expo); expo = my_ceil(expo / LOG2_10); if (expo < 0) expo = 0; p = ecvt(fpnum, precision + 1 + expo, &decpt, &tmp); if (c == 'g') { if (decpt < -4 || decpt > precision) style = 'e'; else style = 'f'; if (decpt > 0 && (precision -= decpt) < 0) precision = 0; } if (tmp) *s++ = '-'; else if (flags & FL_PLUS) *s++ = '+'; else if (flags & FL_BLANK) *s++ = ' '; if (style == 'e') *s++ = *p++; else { if (decpt > 0) { /* Overflow check - should * never have this problem. */ if (decpt > &fpbuf[sizeof(fpbuf)] - s - 8) decpt = &fpbuf[sizeof(fpbuf)] - s - 8; (void) memcpy(s, p, decpt); s += decpt; p += decpt; } else *s++ = '0'; } /* print the fraction? */ if (precision > 0) { *s++ = '.'; /* Overflow check - should * never have this problem. */ if (precision > &fpbuf[sizeof(fpbuf)] - s - 7) precision = &fpbuf[sizeof(fpbuf)] - s - 7; for (tmp = decpt; tmp++ < 0 && precision > 0 ; precision--) *s++ = '0'; tmp = strlen(p); if (precision > tmp) precision = tmp; /* Overflow check - should * never have this problem. */ if (precision > &fpbuf[sizeof(fpbuf)] - s - 7) precision = &fpbuf[sizeof(fpbuf)] - s - 7; (void) memcpy(s, p, precision); s += precision; /* * `g' format strips trailing * zeros after the decimal. */ if (c == 'g' && !(flags & FL_HASH)) { while (*--s == '0') ; if (*s != '.') s++; } } else if (flags & FL_HASH) *s++ = '.'; if (style == 'e') { *s++ = (flags & FL_UPPER) ? 'E' : 'e'; if (--decpt >= 0) *s++ = '+'; else { *s++ = '-'; decpt = -decpt; } p = &numbuf[sizeof(numbuf)]; for (tmp = 0; tmp < 2 || decpt ; tmp++) { *--p = '0' + decpt % 10; decpt /= 10; } tmp = &numbuf[sizeof(numbuf)] - p; (void) memcpy(s, p, tmp); s += tmp; } len = s - fpbuf; s = fpbuf; precision = len; break; } #endif /* FP */ case 's': if (!(s = va_arg(args, char *))) s = "(null %s)"; len = strlen(s); break; case 'c': flags &= ~FL_DOT; numbuf[0] = va_arg(args, int); s = numbuf; len = 1; break; case '%': default: numbuf[0] = c; s = numbuf; len = 1; break; } /* * At this point s should point to a string that is * to be formatted, and len should be the length of the * string. */ if (!(flags & FL_DOT) || len < precision) precision = len; if (field > precision) { field -= precision; if (!(flags & FL_RIGHT)) { field = -field; /* skip past sign or 0x when padding with 0 */ if ((flags & FL_ZERO) && (flags & FL_NUMBER)) { if (*s == '+' || *s == '-' || *s ==' ') { shf_putc(*s, shf); s++; precision--; nwritten++; } else if (*s == '0') { shf_putc(*s, shf); s++; nwritten++; if (--precision > 0 && (*s | 0x20) == 'x') { shf_putc(*s, shf); s++; precision--; nwritten++; } } c = '0'; } else c = flags & FL_ZERO ? '0' : ' '; if (field < 0) { nwritten += -field; for ( ; field < 0 ; field++) shf_putc(c, shf); } } else c = ' '; } else field = 0; if (precision > 0) { nwritten += precision; for ( ; precision-- > 0 ; s++) shf_putc(*s, shf); } if (field > 0) { nwritten += field; for ( ; field > 0 ; --field) shf_putc(c, shf); } } return shf_error(shf) ? EOF : nwritten; } /sys/src/ape/cmd/pdksh/shf.h 664 sys sys 1367613437 3524 #ifndef SHF_H # define SHF_H /* * Shell file I/O routines */ /* $Id$ */ #define SHF_BSIZE 512 #define shf_fileno(shf) ((shf)->fd) #define shf_setfileno(shf,nfd) ((shf)->fd = (nfd)) #define shf_getc(shf) ((shf)->rnleft > 0 ? (shf)->rnleft--, *(shf)->rp++ : \ shf_getchar(shf)) #define shf_putc(c, shf) ((shf)->wnleft == 0 ? shf_putchar((c), (shf)) \ : ((shf)->wnleft--, *(shf)->wp++ = (c))) #define shf_eof(shf) ((shf)->flags & SHF_EOF) #define shf_error(shf) ((shf)->flags & SHF_ERROR) #define shf_errno(shf) ((shf)->errno_) #define shf_clearerr(shf) ((shf)->flags &= ~(SHF_EOF | SHF_ERROR)) /* Flags passed to shf_*open() */ #define SHF_RD 0x0001 #define SHF_WR 0x0002 #define SHF_RDWR (SHF_RD|SHF_WR) #define SHF_ACCMODE 0x0003 /* mask */ #define SHF_GETFL 0x0004 /* use fcntl() to figure RD/WR flags */ #define SHF_UNBUF 0x0008 /* unbuffered I/O */ #define SHF_CLEXEC 0x0010 /* set close on exec flag */ #define SHF_MAPHI 0x0020 /* make fd > FDBASE (and close orig) * (shf_open() only) */ #define SHF_DYNAMIC 0x0040 /* string: increase buffer as needed */ #define SHF_INTERRUPT 0x0080 /* EINTR in read/write causes error */ /* Flags used internally */ #define SHF_STRING 0x0100 /* a string, not a file */ #define SHF_ALLOCS 0x0200 /* shf and shf->buf were alloc()ed */ #define SHF_ALLOCB 0x0400 /* shf->buf was alloc()ed */ #define SHF_ERROR 0x0800 /* read()/write() error */ #define SHF_EOF 0x1000 /* read eof (sticky) */ #define SHF_READING 0x2000 /* currently reading: rnleft,rp valid */ #define SHF_WRITING 0x4000 /* currently writing: wnleft,wp valid */ struct shf { int flags; /* see SHF_* */ unsigned char *rp; /* read: current position in buffer */ int rbsize; /* size of buffer (1 if SHF_UNBUF) */ int rnleft; /* read: how much data left in buffer */ unsigned char *wp; /* write: current position in buffer */ int wbsize; /* size of buffer (0 if SHF_UNBUF) */ int wnleft; /* write: how much space left in buffer */ unsigned char *buf; /* buffer */ int fd; /* file descriptor */ int errno_; /* saved value of errno after error */ int bsize; /* actual size of buf */ Area *areap; /* area shf/buf were allocated in */ }; extern struct shf shf_iob[]; struct shf *shf_open ARGS((const char *name, int oflags, int mode, int sflags)); struct shf *shf_fdopen ARGS((int fd, int sflags, struct shf *shf)); struct shf *shf_reopen ARGS((int fd, int sflags, struct shf *shf)); struct shf *shf_sopen ARGS((char *buf, int bsize, int sflags, struct shf *shf)); int shf_close ARGS((struct shf *shf)); int shf_fdclose ARGS((struct shf *shf)); char *shf_sclose ARGS((struct shf *shf)); int shf_finish ARGS((struct shf *shf)); int shf_flush ARGS((struct shf *shf)); int shf_seek ARGS((struct shf *shf, off_t where, int from)); int shf_read ARGS((char *buf, int bsize, struct shf *shf)); char *shf_getse ARGS((char *buf, int bsize, struct shf *shf)); int shf_getchar ARGS((struct shf *shf)); int shf_ungetc ARGS((int c, struct shf *shf)); int shf_putchar ARGS((int c, struct shf *shf)); int shf_puts ARGS((const char *s, struct shf *shf)); int shf_write ARGS((const char *buf, int nbytes, struct shf *shf)); int shf_fprintf ARGS((struct shf *shf, const char *fmt, ...)); int shf_snprintf ARGS((char *buf, int bsize, const char *fmt, ...)); char *shf_smprintf ARGS((const char *fmt, ...)); int shf_vfprintf ARGS((struct shf *, const char *fmt, va_list args)); #endif /* SHF_H */ /sys/src/ape/cmd/pdksh/syn.c 664 sys sys 1367613437 20384 /* * shell parser (C version) */ #include "sh.h" #include "c_test.h" struct nesting_state { int start_token; /* token than began nesting (eg, FOR) */ int start_line; /* line nesting began on */ }; static void yyparse ARGS((void)); static struct op *pipeline ARGS((int cf)); static struct op *andor ARGS((void)); static struct op *c_list ARGS((int multi)); static struct ioword *synio ARGS((int cf)); static void musthave ARGS((int c, int cf)); static struct op *nested ARGS((int type, int smark, int emark)); static struct op *get_command ARGS((int cf)); static struct op *dogroup ARGS((void)); static struct op *thenpart ARGS((void)); static struct op *elsepart ARGS((void)); static struct op *caselist ARGS((void)); static struct op *casepart ARGS((int endtok)); static struct op *function_body ARGS((char *name, int ksh_func)); static char ** wordlist ARGS((void)); static struct op *block ARGS((int type, struct op *t1, struct op *t2, char **wp)); static struct op *newtp ARGS((int type)); static void syntaxerr ARGS((const char *what)) GCC_FUNC_ATTR(noreturn); static void nesting_push ARGS((struct nesting_state *save, int tok)); static void nesting_pop ARGS((struct nesting_state *saved)); static int assign_command ARGS((char *s)); static int inalias ARGS((struct source *s)); #ifdef KSH static int dbtestp_isa ARGS((Test_env *te, Test_meta meta)); static const char *dbtestp_getopnd ARGS((Test_env *te, Test_op op, int do_eval)); static int dbtestp_eval ARGS((Test_env *te, Test_op op, const char *opnd1, const char *opnd2, int do_eval)); static void dbtestp_error ARGS((Test_env *te, int offset, const char *msg)); #endif /* KSH */ static struct op *outtree; /* yyparse output */ static struct nesting_state nesting; /* \n changed to ; */ static int reject; /* token(cf) gets symbol again */ static int symbol; /* yylex value */ #define REJECT (reject = 1) #define ACCEPT (reject = 0) #define token(cf) \ ((reject) ? (ACCEPT, symbol) : (symbol = yylex(cf))) #define tpeek(cf) \ ((reject) ? (symbol) : (REJECT, symbol = yylex(cf))) static void yyparse() { int c; ACCEPT; outtree = c_list(source->type == SSTRING); c = tpeek(0); if (c == 0 && !outtree) outtree = newtp(TEOF); else if (c != '\n' && c != 0) syntaxerr((char *) 0); } static struct op * pipeline(cf) int cf; { register struct op *t, *p, *tl = NULL; t = get_command(cf); if (t != NULL) { while (token(0) == '|') { if ((p = get_command(CONTIN)) == NULL) syntaxerr((char *) 0); if (tl == NULL) t = tl = block(TPIPE, t, p, NOWORDS); else tl = tl->right = block(TPIPE, tl->right, p, NOWORDS); } REJECT; } return (t); } static struct op * andor() { register struct op *t, *p; register int c; t = pipeline(0); if (t != NULL) { while ((c = token(0)) == LOGAND || c == LOGOR) { if ((p = pipeline(CONTIN)) == NULL) syntaxerr((char *) 0); t = block(c == LOGAND? TAND: TOR, t, p, NOWORDS); } REJECT; } return (t); } static struct op * c_list(multi) int multi; { register struct op *t = NULL, *p, *tl = NULL; register int c; int have_sep; while (1) { p = andor(); /* Token has always been read/rejected at this point, so * we don't worry about what flags to pass token() */ c = token(0); have_sep = 1; if (c == '\n' && (multi || inalias(source))) { if (!p) /* ignore blank lines */ continue; } else if (!p) break; else if (c == '&' || c == COPROC) p = block(c == '&' ? TASYNC : TCOPROC, p, NOBLOCK, NOWORDS); else if (c != ';') have_sep = 0; if (!t) t = p; else if (!tl) t = tl = block(TLIST, t, p, NOWORDS); else tl = tl->right = block(TLIST, tl->right, p, NOWORDS); if (!have_sep) break; } REJECT; return t; } static struct ioword * synio(cf) int cf; { register struct ioword *iop; int ishere; if (tpeek(cf) != REDIR) return NULL; ACCEPT; iop = yylval.iop; ishere = (iop->flag&IOTYPE) == IOHERE; musthave(LWORD, ishere ? HEREDELIM : 0); if (ishere) { iop->delim = yylval.cp; if (*ident != 0) /* unquoted */ iop->flag |= IOEVAL; if (herep >= &heres[HERES]) yyerror("too many <<'s\n"); *herep++ = iop; } else iop->name = yylval.cp; return iop; } static void musthave(c, cf) int c, cf; { if ((token(cf)) != c) syntaxerr((char *) 0); } static struct op * nested(type, smark, emark) int type, smark, emark; { register struct op *t; struct nesting_state old_nesting; nesting_push(&old_nesting, smark); t = c_list(TRUE); musthave(emark, KEYWORD|ALIAS); nesting_pop(&old_nesting); return (block(type, t, NOBLOCK, NOWORDS)); } static struct op * get_command(cf) int cf; { register struct op *t; register int c, iopn = 0, syniocf; struct ioword *iop, **iops; XPtrV args, vars; struct nesting_state old_nesting; iops = (struct ioword **) alloc(sizeofN(struct ioword *, NUFILE+1), ATEMP); XPinit(args, 16); XPinit(vars, 16); syniocf = KEYWORD|ALIAS; switch (c = token(cf|KEYWORD|ALIAS|VARASN)) { default: REJECT; afree((void*) iops, ATEMP); XPfree(args); XPfree(vars); return NULL; /* empty line */ case LWORD: case REDIR: REJECT; syniocf &= ~(KEYWORD|ALIAS); t = newtp(TCOM); t->lineno = source->line; while (1) { cf = (t->u.evalflags ? ARRAYVAR : 0) | (XPsize(args) == 0 ? ALIAS|VARASN : CMDWORD); switch (tpeek(cf)) { case REDIR: if (iopn >= NUFILE) yyerror("too many redirections\n"); iops[iopn++] = synio(cf); break; case LWORD: ACCEPT; /* the iopn == 0 and XPsize(vars) == 0 are * dubious but at&t ksh acts this way */ if (iopn == 0 && XPsize(vars) == 0 && XPsize(args) == 0 && assign_command(ident)) t->u.evalflags = DOVACHECK; if ((XPsize(args) == 0 || Flag(FKEYWORD)) && is_wdvarassign(yylval.cp)) XPput(vars, yylval.cp); else XPput(args, yylval.cp); break; case '(': /* Check for "> foo (echo hi)", which at&t ksh * allows (not POSIX, but not disallowed) */ afree(t, ATEMP); if (XPsize(args) == 0 && XPsize(vars) == 0) { ACCEPT; goto Subshell; } /* Must be a function */ if (iopn != 0 || XPsize(args) != 1 || XPsize(vars) != 0) syntaxerr((char *) 0); ACCEPT; /*(*/ musthave(')', 0); t = function_body(XPptrv(args)[0], FALSE); goto Leave; default: goto Leave; } } Leave: break; Subshell: case '(': t = nested(TPAREN, '(', ')'); break; case '{': /*}*/ t = nested(TBRACE, '{', '}'); break; #ifdef KSH case MDPAREN: { static const char let_cmd[] = { CHAR, 'l', CHAR, 'e', CHAR, 't', EOS }; /* Leave KEYWORD in syniocf (allow if (( 1 )) then ...) */ t = newtp(TCOM); t->lineno = source->line; ACCEPT; XPput(args, wdcopy(let_cmd, ATEMP)); musthave(LWORD,LETEXPR); XPput(args, yylval.cp); break; } #endif /* KSH */ #ifdef KSH case DBRACKET: /* [[ .. ]] */ /* Leave KEYWORD in syniocf (allow if [[ -n 1 ]] then ...) */ t = newtp(TDBRACKET); ACCEPT; { Test_env te; te.flags = TEF_DBRACKET; te.pos.av = &args; te.isa = dbtestp_isa; te.getopnd = dbtestp_getopnd; te.eval = dbtestp_eval; te.error = dbtestp_error; test_parse(&te); } break; #endif /* KSH */ case FOR: case SELECT: t = newtp((c == FOR) ? TFOR : TSELECT); musthave(LWORD, ARRAYVAR); if (!is_wdvarname(yylval.cp, TRUE)) yyerror("%s: bad identifier\n", c == FOR ? "for" : "select"); t->str = str_save(ident, ATEMP); nesting_push(&old_nesting, c); t->vars = wordlist(); t->left = dogroup(); nesting_pop(&old_nesting); break; case WHILE: case UNTIL: nesting_push(&old_nesting, c); t = newtp((c == WHILE) ? TWHILE : TUNTIL); t->left = c_list(TRUE); t->right = dogroup(); nesting_pop(&old_nesting); break; case CASE: t = newtp(TCASE); musthave(LWORD, 0); t->str = yylval.cp; nesting_push(&old_nesting, c); t->left = caselist(); nesting_pop(&old_nesting); break; case IF: nesting_push(&old_nesting, c); t = newtp(TIF); t->left = c_list(TRUE); t->right = thenpart(); musthave(FI, KEYWORD|ALIAS); nesting_pop(&old_nesting); break; case BANG: syniocf &= ~(KEYWORD|ALIAS); t = pipeline(0); if (t == (struct op *) 0) syntaxerr((char *) 0); t = block(TBANG, NOBLOCK, t, NOWORDS); break; case TIME: syniocf &= ~(KEYWORD|ALIAS); t = pipeline(0); t = block(TTIME, t, NOBLOCK, NOWORDS); break; case FUNCTION: musthave(LWORD, 0); t = function_body(yylval.cp, TRUE); break; } while ((iop = synio(syniocf)) != NULL) { if (iopn >= NUFILE) yyerror("too many redirections\n"); iops[iopn++] = iop; } if (iopn == 0) { afree((void*) iops, ATEMP); t->ioact = NULL; } else { iops[iopn++] = NULL; iops = (struct ioword **) aresize((void*) iops, sizeofN(struct ioword *, iopn), ATEMP); t->ioact = iops; } if (t->type == TCOM || t->type == TDBRACKET) { XPput(args, NULL); t->args = (char **) XPclose(args); XPput(vars, NULL); t->vars = (char **) XPclose(vars); } else { XPfree(args); XPfree(vars); } return t; } static struct op * dogroup() { register int c; register struct op *list; c = token(CONTIN|KEYWORD|ALIAS); /* A {...} can be used instead of do...done for for/select loops * but not for while/until loops - we don't need to check if it * is a while loop because it would have been parsed as part of * the conditional command list... */ if (c == DO) c = DONE; else if (c == '{') c = '}'; else syntaxerr((char *) 0); list = c_list(TRUE); musthave(c, KEYWORD|ALIAS); return list; } static struct op * thenpart() { register struct op *t; musthave(THEN, KEYWORD|ALIAS); t = newtp(0); t->left = c_list(TRUE); if (t->left == NULL) syntaxerr((char *) 0); t->right = elsepart(); return (t); } static struct op * elsepart() { register struct op *t; switch (token(KEYWORD|ALIAS|VARASN)) { case ELSE: if ((t = c_list(TRUE)) == NULL) syntaxerr((char *) 0); return (t); case ELIF: t = newtp(TELIF); t->left = c_list(TRUE); t->right = thenpart(); return (t); default: REJECT; } return NULL; } static struct op * caselist() { register struct op *t, *tl; int c; c = token(CONTIN|KEYWORD|ALIAS); /* A {...} can be used instead of in...esac for case statements */ if (c == IN) c = ESAC; else if (c == '{') c = '}'; else syntaxerr((char *) 0); t = tl = NULL; while ((tpeek(CONTIN|KEYWORD|ESACONLY)) != c) { /* no ALIAS here */ struct op *tc = casepart(c); if (tl == NULL) t = tl = tc, tl->right = NULL; else tl->right = tc, tl = tc; } musthave(c, KEYWORD|ALIAS); return (t); } static struct op * casepart(endtok) int endtok; { register struct op *t; register int c; XPtrV ptns; XPinit(ptns, 16); t = newtp(TPAT); c = token(CONTIN|KEYWORD); /* no ALIAS here */ if (c != '(') REJECT; do { musthave(LWORD, 0); XPput(ptns, yylval.cp); } while ((c = token(0)) == '|'); REJECT; XPput(ptns, NULL); t->vars = (char **) XPclose(ptns); musthave(')', 0); t->left = c_list(TRUE); /* Note: Posix requires the ;; */ if ((tpeek(CONTIN|KEYWORD|ALIAS)) != endtok) musthave(BREAK, CONTIN|KEYWORD|ALIAS); return (t); } static struct op * function_body(name, ksh_func) char *name; int ksh_func; /* function foo { ... } vs foo() { .. } */ { char *sname, *p; struct op *t; int old_func_parse; sname = wdstrip(name); /* Check for valid characters in name. posix and ksh93 say only * allow [a-zA-Z_0-9] but this allows more as old pdksh's have * allowed more (the following were never allowed: * nul space nl tab $ ' " \ ` ( ) & | ; = < > * C_QUOTE covers all but = and adds # [ ? *) */ for (p = sname; *p; p++) if (ctype(*p, C_QUOTE) || *p == '=') yyerror("%s: invalid function name\n", sname); t = newtp(TFUNCT); t->str = sname; t->u.ksh_func = ksh_func; t->lineno = source->line; /* Note that POSIX allows only compound statements after foo(), sh and * at&t ksh allow any command, go with the later since it shouldn't * break anything. However, for function foo, at&t ksh only accepts * an open-brace. */ if (ksh_func) { musthave('{', CONTIN|KEYWORD|ALIAS); /* } */ REJECT; } old_func_parse = e->flags & EF_FUNC_PARSE; e->flags |= EF_FUNC_PARSE; if ((t->left = get_command(CONTIN)) == (struct op *) 0) { /* * Probably something like foo() followed by eof or ;. * This is accepted by sh and ksh88. * To make "typset -f foo" work reliably (so its output can * be used as input), we pretend there is a colon here. */ t->left = newtp(TCOM); t->left->args = (char **) alloc(sizeof(char *) * 2, ATEMP); t->left->args[0] = alloc(sizeof(char) * 3, ATEMP); t->left->args[0][0] = CHAR; t->left->args[0][1] = ':'; t->left->args[0][2] = EOS; t->left->args[1] = (char *) 0; t->left->vars = (char **) alloc(sizeof(char *), ATEMP); t->left->vars[0] = (char *) 0; t->left->lineno = 1; } if (!old_func_parse) e->flags &= ~EF_FUNC_PARSE; return t; } static char ** wordlist() { register int c; XPtrV args; XPinit(args, 16); /* Posix does not do alias expansion here... */ if ((c = token(CONTIN|KEYWORD|ALIAS)) != IN) { if (c != ';') /* non-POSIX, but at&t ksh accepts a ; here */ REJECT; return NULL; } while ((c = token(0)) == LWORD) XPput(args, yylval.cp); if (c != '\n' && c != ';') syntaxerr((char *) 0); if (XPsize(args) == 0) { XPfree(args); return NULL; } else { XPput(args, NULL); return (char **) XPclose(args); } } /* * supporting functions */ static struct op * block(type, t1, t2, wp) int type; struct op *t1, *t2; char **wp; { register struct op *t; t = newtp(type); t->left = t1; t->right = t2; t->vars = wp; return (t); } const struct tokeninfo { const char *name; short val; short reserved; } tokentab[] = { /* Reserved words */ { "if", IF, TRUE }, { "then", THEN, TRUE }, { "else", ELSE, TRUE }, { "elif", ELIF, TRUE }, { "fi", FI, TRUE }, { "case", CASE, TRUE }, { "esac", ESAC, TRUE }, { "for", FOR, TRUE }, #ifdef KSH { "select", SELECT, TRUE }, #endif /* KSH */ { "while", WHILE, TRUE }, { "until", UNTIL, TRUE }, { "do", DO, TRUE }, { "done", DONE, TRUE }, { "in", IN, TRUE }, { "function", FUNCTION, TRUE }, { "time", TIME, TRUE }, { "{", '{', TRUE }, { "}", '}', TRUE }, { "!", BANG, TRUE }, #ifdef KSH { "[[", DBRACKET, TRUE }, #endif /* KSH */ /* Lexical tokens (0[EOF], LWORD and REDIR handled specially) */ { "&&", LOGAND, FALSE }, { "||", LOGOR, FALSE }, { ";;", BREAK, FALSE }, #ifdef KSH { "((", MDPAREN, FALSE }, { "|&", COPROC, FALSE }, #endif /* KSH */ /* and some special cases... */ { "newline", '\n', FALSE }, { 0 } }; void initkeywords() { register struct tokeninfo const *tt; register struct tbl *p; tinit(&keywords, APERM, 32); /* must be 2^n (currently 20 keywords) */ for (tt = tokentab; tt->name; tt++) { if (tt->reserved) { p = tenter(&keywords, tt->name, hash(tt->name)); p->flag |= DEFINED|ISSET; p->type = CKEYWD; p->val.i = tt->val; } } } static void syntaxerr(what) const char *what; { char redir[6]; /* 2<<- is the longest redirection, I think */ const char *s; struct tokeninfo const *tt; int c; if (!what) what = "unexpected"; REJECT; c = token(0); Again: switch (c) { case 0: if (nesting.start_token) { c = nesting.start_token; source->errline = nesting.start_line; what = "unmatched"; goto Again; } /* don't quote the EOF */ yyerror("syntax error: unexpected EOF\n"); /*NOTREACHED*/ case LWORD: s = snptreef((char *) 0, 32, "%S", yylval.cp); break; case REDIR: s = snptreef(redir, sizeof(redir), "%R", yylval.iop); break; default: for (tt = tokentab; tt->name; tt++) if (tt->val == c) break; if (tt->name) s = tt->name; else { if (c > 0 && c < 256) { redir[0] = c; redir[1] = '\0'; } else shf_snprintf(redir, sizeof(redir), "?%d", c); s = redir; } } yyerror("syntax error: `%s' %s\n", s, what); } static void nesting_push(save, tok) struct nesting_state *save; int tok; { *save = nesting; nesting.start_token = tok; nesting.start_line = source->line; } static void nesting_pop(saved) struct nesting_state *saved; { nesting = *saved; } static struct op * newtp(type) int type; { register struct op *t; t = (struct op *) alloc(sizeof(*t), ATEMP); t->type = type; t->u.evalflags = 0; t->args = t->vars = NULL; t->ioact = NULL; t->left = t->right = NULL; t->str = NULL; return (t); } struct op * compile(s) Source *s; { nesting.start_token = 0; nesting.start_line = 0; herep = heres; source = s; yyparse(); return outtree; } /* This kludge exists to take care of sh/at&t ksh oddity in which * the arguments of alias/export/readonly/typeset have no field * splitting, file globbing, or (normal) tilde expansion done. * at&t ksh seems to do something similar to this since * $ touch a=a; typeset a=[ab]; echo "$a" * a=[ab] * $ x=typeset; $x a=[ab]; echo "$a" * a=a * $ */ static int assign_command(s) char *s; { char c = *s; if (Flag(FPOSIX) || !*s) return 0; return (c == 'a' && strcmp(s, "alias") == 0) || (c == 'e' && strcmp(s, "export") == 0) || (c == 'r' && strcmp(s, "readonly") == 0) || (c == 't' && strcmp(s, "typeset") == 0); } /* Check if we are in the middle of reading an alias */ static int inalias(s) struct source *s; { for (; s && s->type == SALIAS; s = s->next) if (!(s->flags & SF_ALIASEND)) return 1; return 0; } #ifdef KSH /* Order important - indexed by Test_meta values * Note that ||, &&, ( and ) can't appear in as unquoted strings * in normal shell input, so these can be interpreted unambiguously * in the evaluation pass. */ static const char dbtest_or[] = { CHAR, '|', CHAR, '|', EOS }; static const char dbtest_and[] = { CHAR, '&', CHAR, '&', EOS }; static const char dbtest_not[] = { CHAR, '!', EOS }; static const char dbtest_oparen[] = { CHAR, '(', EOS }; static const char dbtest_cparen[] = { CHAR, ')', EOS }; const char *const dbtest_tokens[] = { dbtest_or, dbtest_and, dbtest_not, dbtest_oparen, dbtest_cparen }; const char db_close[] = { CHAR, ']', CHAR, ']', EOS }; const char db_lthan[] = { CHAR, '<', EOS }; const char db_gthan[] = { CHAR, '>', EOS }; /* Test if the current token is a whatever. Accepts the current token if * it is. Returns 0 if it is not, non-zero if it is (in the case of * TM_UNOP and TM_BINOP, the returned value is a Test_op). */ static int dbtestp_isa(te, meta) Test_env *te; Test_meta meta; { int c = tpeek(ARRAYVAR | (meta == TM_BINOP ? 0 : CONTIN)); int uqword = 0; char *save = (char *) 0; int ret = 0; /* unquoted word? */ uqword = c == LWORD && *ident; if (meta == TM_OR) ret = c == LOGOR; else if (meta == TM_AND) ret = c == LOGAND; else if (meta == TM_NOT) ret = uqword && strcmp(yylval.cp, dbtest_tokens[(int) TM_NOT]) == 0; else if (meta == TM_OPAREN) ret = c == '(' /*)*/; else if (meta == TM_CPAREN) ret = c == /*(*/ ')'; else if (meta == TM_UNOP || meta == TM_BINOP) { if (meta == TM_BINOP && c == REDIR && (yylval.iop->flag == IOREAD || yylval.iop->flag == IOWRITE)) { ret = 1; save = wdcopy(yylval.iop->flag == IOREAD ? db_lthan : db_gthan, ATEMP); } else if (uqword && (ret = (int) test_isop(te, meta, ident))) save = yylval.cp; } else /* meta == TM_END */ ret = uqword && strcmp(yylval.cp, db_close) == 0; if (ret) { ACCEPT; if (meta != TM_END) { if (!save) save = wdcopy(dbtest_tokens[(int) meta], ATEMP); XPput(*te->pos.av, save); } } return ret; } static const char * dbtestp_getopnd(te, op, do_eval) Test_env *te; Test_op op; int do_eval; { int c = tpeek(ARRAYVAR); if (c != LWORD) return (const char *) 0; ACCEPT; XPput(*te->pos.av, yylval.cp); return null; } static int dbtestp_eval(te, op, opnd1, opnd2, do_eval) Test_env *te; Test_op op; const char *opnd1; const char *opnd2; int do_eval; { return 1; } static void dbtestp_error(te, offset, msg) Test_env *te; int offset; const char *msg; { te->flags |= TEF_ERROR; if (offset < 0) { REJECT; /* Kludgy to say the least... */ symbol = LWORD; yylval.cp = *(XPptrv(*te->pos.av) + XPsize(*te->pos.av) + offset); } syntaxerr(msg); } #endif /* KSH */ /sys/src/ape/cmd/pdksh/table.c 664 sys sys 1367613437 4768 /* * dynamic hashed associative table for commands and variables */ #include "sh.h" #define INIT_TBLS 8 /* initial table size (power of 2) */ static void texpand ARGS((struct table *tp, int nsize)); static int tnamecmp ARGS((void *p1, void *p2)); unsigned int hash(n) register const char * n; { register unsigned int h = 0; while (*n != '\0') h = 2*h + *n++; return h * 32821; /* scatter bits */ } void tinit(tp, ap, tsize) register struct table *tp; register Area *ap; int tsize; { tp->areap = ap; tp->tbls = NULL; tp->size = tp->nfree = 0; if (tsize) texpand(tp, tsize); } static void texpand(tp, nsize) register struct table *tp; int nsize; { register int i; register struct tbl *tblp, **p; register struct tbl **ntblp, **otblp = tp->tbls; int osize = tp->size; ntblp = (struct tbl**) alloc(sizeofN(struct tbl *, nsize), tp->areap); for (i = 0; i < nsize; i++) ntblp[i] = NULL; tp->size = nsize; tp->nfree = 8*nsize/10; /* table can get 80% full */ tp->tbls = ntblp; if (otblp == NULL) return; for (i = 0; i < osize; i++) if ((tblp = otblp[i]) != NULL) if ((tblp->flag&DEFINED)) { for (p = &ntblp[hash(tblp->name) & (tp->size-1)]; *p != NULL; p--) if (p == ntblp) /* wrap */ p += tp->size; *p = tblp; tp->nfree--; } else if (!(tblp->flag & FINUSE)) { afree((void*)tblp, tp->areap); } afree((void*)otblp, tp->areap); } struct tbl * tsearch(tp, n, h) register struct table *tp; /* table */ register const char *n; /* name to enter */ unsigned int h; /* hash(n) */ { register struct tbl **pp, *p; if (tp->size == 0) return NULL; /* search for name in hashed table */ for (pp = &tp->tbls[h & (tp->size-1)]; (p = *pp) != NULL; pp--) { if (*p->name == *n && strcmp(p->name, n) == 0 && (p->flag&DEFINED)) return p; if (pp == tp->tbls) /* wrap */ pp += tp->size; } return NULL; } struct tbl * tenter(tp, n, h) register struct table *tp; /* table */ register const char *n; /* name to enter */ unsigned int h; /* hash(n) */ { register struct tbl **pp, *p; register int len; if (tp->size == 0) texpand(tp, INIT_TBLS); Search: /* search for name in hashed table */ for (pp = &tp->tbls[h & (tp->size-1)]; (p = *pp) != NULL; pp--) { if (*p->name == *n && strcmp(p->name, n) == 0) return p; /* found */ if (pp == tp->tbls) /* wrap */ pp += tp->size; } if (tp->nfree <= 0) { /* too full */ texpand(tp, 2*tp->size); goto Search; } /* create new tbl entry */ len = strlen(n) + 1; p = (struct tbl *) alloc(offsetof(struct tbl, name[0]) + len, tp->areap); p->flag = 0; p->type = 0; p->areap = tp->areap; p->u2.field = 0; p->u.array = (struct tbl *)0; memcpy(p->name, n, len); /* enter in tp->tbls */ tp->nfree--; *pp = p; return p; } void tdelete(p) register struct tbl *p; { p->flag = 0; } void twalk(ts, tp) struct tstate *ts; struct table *tp; { ts->left = tp->size; ts->next = tp->tbls; } struct tbl * tnext(ts) struct tstate *ts; { while (--ts->left >= 0) { struct tbl *p = *ts->next++; if (p != NULL && (p->flag&DEFINED)) return p; } return NULL; } static int tnamecmp(p1, p2) void *p1, *p2; { return strcmp(((struct tbl *)p1)->name, ((struct tbl *)p2)->name); } struct tbl ** tsort(tp) register struct table *tp; { register int i; register struct tbl **p, **sp, **dp; p = (struct tbl **)alloc(sizeofN(struct tbl *, tp->size+1), ATEMP); sp = tp->tbls; /* source */ dp = p; /* dest */ for (i = 0; i < tp->size; i++) if ((*dp = *sp++) != NULL && (((*dp)->flag&DEFINED) || ((*dp)->flag&ARRAY))) dp++; i = dp - p; qsortp((void**)p, (size_t)i, tnamecmp); p[i] = NULL; return p; } #ifdef PERF_DEBUG /* performance debugging */ void tprintinfo ARGS((struct table *tp)); void tprintinfo(tp) struct table *tp; { struct tbl *te; char *n; unsigned int h; int ncmp; int totncmp = 0, maxncmp = 0; int nentries = 0; struct tstate ts; shellf("table size %d, nfree %d\n", tp->size, tp->nfree); shellf(" Ncmp name\n"); twalk(&ts, tp); while ((te = tnext(&ts))) { register struct tbl **pp, *p; h = hash(n = te->name); ncmp = 0; /* taken from tsearch() and added counter */ for (pp = &tp->tbls[h & (tp->size-1)]; (p = *pp); pp--) { ncmp++; if (*p->name == *n && strcmp(p->name, n) == 0 && (p->flag&DEFINED)) break; /* return p; */ if (pp == tp->tbls) /* wrap */ pp += tp->size; } shellf(" %4d %s\n", ncmp, n); totncmp += ncmp; nentries++; if (ncmp > maxncmp) maxncmp = ncmp; } if (nentries) shellf(" %d entries, worst ncmp %d, avg ncmp %d.%02d\n", nentries, maxncmp, totncmp / nentries, (totncmp % nentries) * 100 / nentries); } #endif /* PERF_DEBUG */ /sys/src/ape/cmd/pdksh/table.h 664 sys sys 1367613437 5935 /* $Id: table.h,v 1.3 1994/05/31 13:34:34 michael Exp $ */ /* * generic hashed associative table for commands and variables. */ struct table { Area *areap; /* area to allocate entries */ short size, nfree; /* hash size (always 2^^n), free entries */ struct tbl **tbls; /* hashed table items */ }; struct tbl { /* table item */ Tflag flag; /* flags */ int type; /* command type (see below), base (if INTEGER), * or offset from val.s of value (if EXPORT) */ Area *areap; /* area to allocate from */ union { char *s; /* string */ long i; /* integer */ int (*f) ARGS((char **)); /* int function */ struct op *t; /* "function" tree */ } val; /* value */ int index; /* index for an array */ union { int field; /* field with for -L/-R/-Z */ int errno_; /* CEXEC/CTALIAS */ } u2; union { struct tbl *array; /* array values */ char *fpath; /* temporary path to undef function */ } u; char name[4]; /* name -- variable length */ }; /* common flag bits */ #define ALLOC BIT(0) /* val.s has been allocated */ #define DEFINED BIT(1) /* is defined in block */ #define ISSET BIT(2) /* has value, vp->val.[si] */ #define EXPORT BIT(3) /* exported variable/function */ #define TRACE BIT(4) /* var: user flagged, func: execution tracing */ /* (start non-common flags at 8) */ /* flag bits used for variables */ #define SPECIAL BIT(8) /* PATH, IFS, SECONDS, etc */ #define INTEGER BIT(9) /* val.i contains integer value */ #define RDONLY BIT(10) /* read-only variable */ #define LOCAL BIT(11) /* for local typeset() */ #define ARRAY BIT(13) /* array */ #define LJUST BIT(14) /* left justify */ #define RJUST BIT(15) /* right justify */ #define ZEROFIL BIT(16) /* 0 filled if RJUSTIFY, strip 0s if LJUSTIFY */ #define LCASEV BIT(17) /* convert to lower case */ #define UCASEV_AL BIT(18)/* convert to upper case / autoload function */ #define INT_U BIT(19) /* unsigned integer */ #define INT_L BIT(20) /* long integer (no-op) */ #define IMPORT BIT(21) /* flag to typeset(): no arrays, must have = */ #define LOCAL_COPY BIT(22) /* with LOCAL - copy attrs from existing var */ #define EXPRINEVAL BIT(23) /* contents currently being evaluated */ #define EXPRLVALUE BIT(24) /* useable as lvalue (temp flag) */ /* flag bits used for taliases/builtins/aliases/keywords/functions */ #define KEEPASN BIT(8) /* keep command assignments (eg, var=x cmd) */ #define FINUSE BIT(9) /* function being executed */ #define FDELETE BIT(10) /* function deleted while it was executing */ #define FKSH BIT(11) /* function defined with function x (vs x()) */ #define SPEC_BI BIT(12) /* a POSIX special builtin */ #define REG_BI BIT(13) /* a POSIX regular builtin */ /* Attributes that can be set by the user (used to decide if an unset param * should be repoted by set/typeset). Does not include ARRAY or LOCAL. */ #define USERATTRIB (EXPORT|INTEGER|RDONLY|LJUST|RJUST|ZEROFIL\ |LCASEV|UCASEV_AL|INT_U|INT_L) /* command types */ #define CNONE 0 /* undefined */ #define CSHELL 1 /* built-in */ #define CFUNC 2 /* function */ #define CEXEC 4 /* executable command */ #define CALIAS 5 /* alias */ #define CKEYWD 6 /* keyword */ #define CTALIAS 7 /* tracked alias */ /* Flags for findcom()/comexec() */ #define FC_SPECBI BIT(0) /* special builtin */ #define FC_FUNC BIT(1) /* function builtin */ #define FC_REGBI BIT(2) /* regular builtin */ #define FC_UNREGBI BIT(3) /* un-regular builtin (!special,!regular) */ #define FC_BI (FC_SPECBI|FC_REGBI|FC_UNREGBI) #define FC_PATH BIT(4) /* do path search */ #define FC_DEFPATH BIT(5) /* use default path in path search */ #define AF_ARGV_ALLOC 0x1 /* argv[] array allocated */ #define AF_ARGS_ALLOCED 0x2 /* argument strings allocated */ #define AI_ARGV(a, i) ((i) == 0 ? (a).argv[0] : (a).argv[(i) - (a).skip]) #define AI_ARGC(a) ((a).argc_ - (a).skip) /* Argument info. Used for $#, $* for shell, functions, includes, etc. */ struct arg_info { int flags; /* AF_* */ char **argv; int argc_; int skip; /* first arg is argv[0], second is argv[1 + skip] */ }; /* * activation record for function blocks */ struct block { Area area; /* area to allocate things */ /*struct arg_info argi;*/ char **argv; int argc; int flags; /* see BF_* */ struct table vars; /* local variables */ struct table funs; /* local functions */ Getopt getopts_state; #if 1 char * error; /* error handler */ char * exit; /* exit handler */ #else Trap error, exit; #endif struct block *next; /* enclosing block */ }; /* Values for struct block.flags */ #define BF_DOGETOPTS BIT(0) /* save/restore getopts state */ /* * Used by twalk() and tnext() routines. */ struct tstate { int left; struct tbl **next; }; EXTERN struct table taliases; /* tracked aliases */ EXTERN struct table builtins; /* built-in commands */ EXTERN struct table aliases; /* aliases */ EXTERN struct table keywords; /* keywords */ EXTERN struct table homedirs; /* homedir() cache */ struct builtin { const char *name; int (*func) ARGS((char **)); }; /* these really are externs! Look in table.c for them */ extern const struct builtin shbuiltins [], kshbuiltins []; /* var spec values */ #define V_NONE 0 #define V_PATH 1 #define V_IFS 2 #define V_SECONDS 3 #define V_OPTIND 4 #define V_MAIL 5 #define V_MAILPATH 6 #define V_MAILCHECK 7 #define V_RANDOM 8 #define V_HISTSIZE 9 #define V_HISTFILE 10 #define V_VISUAL 11 #define V_EDITOR 12 #define V_COLUMNS 13 #define V_POSIXLY_CORRECT 14 #define V_TMOUT 15 #define V_TMPDIR 16 #define V_LINENO 17 /* values for set_prompt() */ #define PS1 0 /* command */ #define PS2 1 /* command continuation */ EXTERN char *path; /* copy of either PATH or def_path */ EXTERN const char *def_path; /* path to use if PATH not set */ EXTERN char *tmpdir; /* TMPDIR value */ EXTERN const char *prompt; EXTERN int cur_prompt; /* PS1 or PS2 */ EXTERN int current_lineno; /* LINENO value */ /sys/src/ape/cmd/pdksh/trap.c 664 sys sys 1367613437 10495 /* * signal handling */ /* Kludge to avoid bogus re-declaration of sigtraps[] error on AIX 3.2.5 */ #define FROM_TRAP_C #include "sh.h" /* Table is indexed by signal number * * The script siglist.sh generates siglist.out, which is a sorted, complete * list of signals */ Trap sigtraps[SIGNALS+1] = { { SIGEXIT_, "EXIT", "Signal 0" }, { 1 , "HUP", "Hangup" }, { 2 , "INT", "Interrupt" }, { 3 , "QUIT", "Quit" }, { 4 , "ILL", "Illegal instruction" }, { 5 , "ABRT", "Abort" }, { 6 , "FPE", "Floating point exception" }, { 7 , "KILL", "Killed" }, { 8 , "SEGV", "Memory fault" }, { 9 , "PIPE", "Broken pipe" }, { 10 , "ALRM", "Alarm clock" }, { 11 , "TERM", "Terminated" }, { 12 , "USR1", "User defined signal 1" }, { 13 , "USR2", "User defined signal 2" }, { 14 , "BUS", "Bus error" }, { 15 , "CHLD", "Child exited" }, { 16 , "CONT", "Continued" }, { 17 , "STOP", "Stopped (signal)" }, { 18 , "TSTP", "Stopped" }, { 19 , "TTIN", "Stopped (tty input)" }, { 20 , "TTOU", "Stopped (tty output)" }, { SIGERR_, "ERR", "Error handler" }, }; static struct sigaction Sigact_ign, Sigact_trap; void inittraps() { #ifdef HAVE_SYS_SIGLIST # ifndef SYS_SIGLIST_DECLARED extern char *sys_siglist[]; # endif int i; /* Use system description, if available, for unknown signals... */ for (i = 0; i < NSIG; i++) if (!sigtraps[i].name && sys_siglist[i] && sys_siglist[i][0]) sigtraps[i].mess = sys_siglist[i]; #endif /* HAVE_SYS_SIGLIST */ sigemptyset(&Sigact_ign.sa_mask); Sigact_ign.sa_flags = KSH_SA_FLAGS; Sigact_ign.sa_handler = SIG_IGN; Sigact_trap = Sigact_ign; Sigact_trap.sa_handler = trapsig; sigtraps[SIGINT].flags |= TF_DFL_INTR | TF_TTY_INTR; sigtraps[SIGQUIT].flags |= TF_DFL_INTR | TF_TTY_INTR; sigtraps[SIGTERM].flags |= TF_DFL_INTR;/* not fatal for interactive */ sigtraps[SIGHUP].flags |= TF_FATAL; sigtraps[SIGCHLD].flags |= TF_SHELL_USES; /* these are always caught so we can clean up any temproary files. */ setsig(&sigtraps[SIGINT], trapsig, SS_RESTORE_ORIG); setsig(&sigtraps[SIGQUIT], trapsig, SS_RESTORE_ORIG); setsig(&sigtraps[SIGTERM], trapsig, SS_RESTORE_ORIG); setsig(&sigtraps[SIGHUP], trapsig, SS_RESTORE_ORIG); } #ifdef KSH static RETSIGTYPE alarm_catcher ARGS((int sig)); void alarm_init() { sigtraps[SIGALRM].flags |= TF_SHELL_USES; setsig(&sigtraps[SIGALRM], alarm_catcher, SS_RESTORE_ORIG|SS_FORCE|SS_SHTRAP); } static RETSIGTYPE alarm_catcher(sig) int sig; { if (ksh_tmout_state == TMOUT_READING) { int left = alarm(0); if (left == 0) { ksh_tmout_state = TMOUT_LEAVING; intrsig = 1; } else alarm(left); } return RETSIGVAL; } #endif /* KSH */ Trap * gettrap(name, igncase) const char *name; int igncase; { int i; register Trap *p; if (digit(*name)) { int n; if (getn(name, &n) && 0 <= n && n < SIGNALS) return &sigtraps[n]; return NULL; } if (strncasecmp(name, "sig", 3) == 0) name += 3; for (p = sigtraps, i = SIGNALS+1; --i >= 0; p++) if (p->name && (igncase ? strcasecmp(p->name, name) == 0 : strcmp(p->name, name) == 0)) return p; return NULL; } /* * trap signal handler */ RETSIGTYPE trapsig(i) int i; { Trap *p = &sigtraps[i]; trap = p->set = 1; if (p->flags & TF_DFL_INTR) intrsig = 1; if ((p->flags & TF_FATAL) && !p->trap) { fatal_trap = 1; intrsig = 1; } if (p->shtrap) (*p->shtrap)(i); #ifdef V7_SIGNALS if (sigtraps[i].cursig == trapsig) /* this for SIGCHLD,SIGALRM */ sigaction(i, &Sigact_trap, (struct sigaction *) 0); #endif /* V7_SIGNALS */ return RETSIGVAL; } /* called when we want to allow the user to ^C out of something - won't * work if user has trapped SIGINT. */ void intrcheck() { if (intrsig) runtraps(TF_DFL_INTR|TF_FATAL); } /* called after EINTR to check if a signal with normally causes process * termination has been received. */ int fatal_trap_check() { int i; Trap *p; /* todo: should check if signal is fatal, not the TF_DFL_INTR flag */ for (p = sigtraps, i = SIGNALS+1; --i >= 0; p++) if (p->set && (p->flags & (TF_DFL_INTR|TF_FATAL))) /* return value is used as an exit code */ return 128 + p->signal; return 0; } /* Returns the signal number of any pending traps: ie, a signal which has * occured for which a trap has been set or for which the TF_DFL_INTR flag * is set. */ int trap_pending() { int i; Trap *p; for (p = sigtraps, i = SIGNALS+1; --i >= 0; p++) if (p->set && ((p->trap && p->trap[0]) || ((p->flags & (TF_DFL_INTR|TF_FATAL)) && !p->trap))) return p->signal; return 0; } /* * run any pending traps. If intr is set, only run traps that * can interrupt commands. */ void runtraps(flag) int flag; { int i; register Trap *p; #ifdef KSH if (ksh_tmout_state == TMOUT_LEAVING) { ksh_tmout_state = TMOUT_EXECUTING; warningf(FALSE, "timed out waiting for input"); unwind(LEXIT); } else /* XXX: this means the alarm will have no effect if a trap * is caught after the alarm() was started...not good. */ ksh_tmout_state = TMOUT_EXECUTING; #endif /* KSH */ if (!flag) trap = 0; if (flag & TF_DFL_INTR) intrsig = 0; if (flag & TF_FATAL) fatal_trap = 0; for (p = sigtraps, i = SIGNALS+1; --i >= 0; p++) if (p->set && (!flag || ((p->flags & flag) && p->trap == (char *) 0))) runtrap(p); } void runtrap(p) Trap *p; { int i = p->signal; char *trapstr = p->trap; int oexstat; int UNINITIALIZED(old_changed); p->set = 0; if (trapstr == (char *) 0) { /* SIG_DFL */ if (p->flags & TF_FATAL) { /* eg, SIGHUP */ exstat = 128 + i; unwind(LLEAVE); } if (p->flags & TF_DFL_INTR) { /* eg, SIGINT, SIGQUIT, SIGTERM, etc. */ exstat = 128 + i; unwind(LINTR); } return; } if (trapstr[0] == '\0') /* SIG_IGN */ return; if (i == SIGEXIT_ || i == SIGERR_) { /* avoid recursion on these */ old_changed = p->flags & TF_CHANGED; p->flags &= ~TF_CHANGED; p->trap = (char *) 0; } oexstat = exstat; /* Note: trapstr is fully parsed before anything is executed, thus * no problem with afree(p->trap) in settrap() while still in use. */ command(trapstr); exstat = oexstat; if (i == SIGEXIT_ || i == SIGERR_) { if (p->flags & TF_CHANGED) /* don't clear TF_CHANGED */ afree(trapstr, APERM); else p->trap = trapstr; p->flags |= old_changed; } } /* clear pending traps and reset user's trap handlers; used after fork(2) */ void cleartraps() { int i; Trap *p; trap = 0; intrsig = 0; fatal_trap = 0; for (i = SIGNALS+1, p = sigtraps; --i >= 0; p++) { p->set = 0; if ((p->flags & TF_USER_SET) && (p->trap && p->trap[0])) settrap(p, (char *) 0); } } /* restore signals just before an exec(2) */ void restoresigs() { int i; Trap *p; for (i = SIGNALS+1, p = sigtraps; --i >= 0; p++) if (p->flags & (TF_EXEC_IGN|TF_EXEC_DFL)) setsig(p, (p->flags & TF_EXEC_IGN) ? SIG_IGN : SIG_DFL, SS_RESTORE_CURR|SS_FORCE); } void settrap(p, s) Trap *p; char *s; { handler_t f; if (p->trap) afree(p->trap, APERM); p->trap = str_save(s, APERM); /* handles s == 0 */ p->flags |= TF_CHANGED; f = !s ? SIG_DFL : s[0] ? trapsig : SIG_IGN; p->flags |= TF_USER_SET; if ((p->flags & (TF_DFL_INTR|TF_FATAL)) && f == SIG_DFL) f = trapsig; else if (p->flags & TF_SHELL_USES) { if (!(p->flags & TF_ORIG_IGN) || Flag(FTALKING)) { /* do what user wants at exec time */ p->flags &= ~(TF_EXEC_IGN|TF_EXEC_DFL); if (f == SIG_IGN) p->flags |= TF_EXEC_IGN; else p->flags |= TF_EXEC_DFL; } /* assumes handler already set to what shell wants it * (normally trapsig, but could be j_sigchld() or SIG_IGN) */ return; } /* todo: should we let user know signal is ignored? how? */ setsig(p, f, SS_RESTORE_CURR|SS_USER); } /* Called by c_print() when writing to a co-process to ensure SIGPIPE won't * kill shell (unless user catches it and exits) */ int block_pipe() { int restore_dfl = 0; Trap *p = &sigtraps[SIGPIPE]; if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL))) { setsig(p, SIG_IGN, SS_RESTORE_CURR); if (p->flags & TF_ORIG_DFL) restore_dfl = 1; } else if (p->cursig == SIG_DFL) { setsig(p, SIG_IGN, SS_RESTORE_CURR); restore_dfl = 1; /* restore to SIG_DFL */ } return restore_dfl; } /* Called by c_print() to undo whatever block_pipe() did */ void restore_pipe(restore_dfl) int restore_dfl; { if (restore_dfl) setsig(&sigtraps[SIGPIPE], SIG_DFL, SS_RESTORE_CURR); } /* Set action for a signal. Action may not be set if original * action was SIG_IGN, depending on the value of flags and * FTALKING. */ int setsig(p, f, flags) Trap *p; handler_t f; int flags; { struct sigaction sigact; if (p->signal == SIGEXIT_ || p->signal == SIGERR_) return 1; /* First time setting this signal? If so, get and note the current * setting. */ if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL))) { sigaction(p->signal, &Sigact_ign, &sigact); p->flags |= sigact.sa_handler == SIG_IGN ? TF_ORIG_IGN : TF_ORIG_DFL; p->cursig = SIG_IGN; } /* Generally, an ignored signal stays ignored, except if * - the user of an interactive shell wants to change it * - the shell wants for force a change */ if ((p->flags & TF_ORIG_IGN) && !(flags & SS_FORCE) && (!(flags & SS_USER) || !Flag(FTALKING))) return 0; setexecsig(p, flags & SS_RESTORE_MASK); /* This is here 'cause there should be a way of clearing shtraps, but * don't know if this is a sane way of doing it. At the moment, * all users of shtrap are lifetime users (SIGCHLD, SIGALRM, SIGWINCH). */ if (!(flags & SS_USER)) p->shtrap = (handler_t) 0; if (flags & SS_SHTRAP) { p->shtrap = f; f = trapsig; } if (p->cursig != f) { p->cursig = f; sigemptyset(&sigact.sa_mask); sigact.sa_flags = KSH_SA_FLAGS; sigact.sa_handler = f; sigaction(p->signal, &sigact, (struct sigaction *) 0); } return 1; } /* control what signal is set to before an exec() */ void setexecsig(p, restore) Trap *p; int restore; { /* XXX debugging */ if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL))) internal_errorf(1, "setexecsig: unset signal %d(%s)", p->signal, p->name); /* restore original value for exec'd kids */ p->flags &= ~(TF_EXEC_IGN|TF_EXEC_DFL); switch (restore & SS_RESTORE_MASK) { case SS_RESTORE_CURR: /* leave things as they currently are */ break; case SS_RESTORE_ORIG: p->flags |= p->flags & TF_ORIG_IGN ? TF_EXEC_IGN : TF_EXEC_DFL; break; case SS_RESTORE_DFL: p->flags |= TF_EXEC_DFL; break; case SS_RESTORE_IGN: p->flags |= TF_EXEC_IGN; break; } } /sys/src/ape/cmd/pdksh/tree.c 664 sys sys 1367613437 15142 /* * command tree climbing */ #include "sh.h" #define INDENT 4 #define tputc(c, shf) shf_putchar(c, shf); static void ptree ARGS((struct op *t, int indent, struct shf *f)); static void pioact ARGS((struct shf *f, int indent, struct ioword *iop)); static void tputC ARGS((int c, struct shf *shf)); static void tputS ARGS((char *wp, struct shf *shf)); static void vfptreef ARGS((struct shf *shf, int indent, const char *fmt, va_list va)); static struct ioword **iocopy ARGS((struct ioword **iow, Area *ap)); static void iofree ARGS((struct ioword **iow, Area *ap)); /* * print a command tree */ static void ptree(t, indent, shf) register struct op *t; int indent; register struct shf *shf; { register char **w; struct ioword **ioact; struct op *t1; Chain: if (t == NULL) return; switch (t->type) { case TCOM: if (t->vars) for (w = t->vars; *w != NULL; ) fptreef(shf, indent, "%S ", *w++); else fptreef(shf, indent, "#no-vars# "); if (t->args) for (w = t->args; *w != NULL; ) fptreef(shf, indent, "%S ", *w++); else fptreef(shf, indent, "#no-args# "); break; case TEXEC: #if 0 /* ?not useful - can't be called? */ /* Print original vars */ if (t->left->vars) for (w = t->left->vars; *w != NULL; ) fptreef(shf, indent, "%S ", *w++); else fptreef(shf, indent, "#no-vars# "); /* Print expanded vars */ if (t->args) for (w = t->args; *w != NULL; ) fptreef(shf, indent, "%s ", *w++); else fptreef(shf, indent, "#no-args# "); /* Print original io */ t = t->left; #else t = t->left; goto Chain; #endif case TPAREN: fptreef(shf, indent + 2, "( %T) ", t->left); break; case TPIPE: fptreef(shf, indent, "%T| ", t->left); t = t->right; goto Chain; case TLIST: fptreef(shf, indent, "%T%;", t->left); t = t->right; goto Chain; case TOR: case TAND: fptreef(shf, indent, "%T%s %T", t->left, (t->type==TOR) ? "||" : "&&", t->right); break; case TBANG: fptreef(shf, indent, "! "); t = t->right; goto Chain; case TDBRACKET: { int i; fptreef(shf, indent, "[["); for (i = 0; t->args[i]; i++) fptreef(shf, indent, " %S", t->args[i]); fptreef(shf, indent, " ]] "); break; } #ifdef KSH case TSELECT: fptreef(shf, indent, "select %s ", t->str); /* fall through */ #endif /* KSH */ case TFOR: if (t->type == TFOR) fptreef(shf, indent, "for %s ", t->str); if (t->vars != NULL) { fptreef(shf, indent, "in "); for (w = t->vars; *w; ) fptreef(shf, indent, "%S ", *w++); fptreef(shf, indent, "%;"); } fptreef(shf, indent + INDENT, "do%N%T", t->left); fptreef(shf, indent, "%;done "); break; case TCASE: fptreef(shf, indent, "case %S in", t->str); for (t1 = t->left; t1 != NULL; t1 = t1->right) { fptreef(shf, indent, "%N("); for (w = t1->vars; *w != NULL; w++) fptreef(shf, indent, "%S%c", *w, (w[1] != NULL) ? '|' : ')'); fptreef(shf, indent + INDENT, "%;%T%N;;", t1->left); } fptreef(shf, indent, "%Nesac "); break; case TIF: case TELIF: /* 3 == strlen("if ") */ fptreef(shf, indent + 3, "if %T", t->left); for (;;) { t = t->right; if (t->left != NULL) { fptreef(shf, indent, "%;"); fptreef(shf, indent + INDENT, "then%N%T", t->left); } if (t->right == NULL || t->right->type != TELIF) break; t = t->right; fptreef(shf, indent, "%;"); /* 5 == strlen("elif ") */ fptreef(shf, indent + 5, "elif %T", t->left); } if (t->right != NULL) { fptreef(shf, indent, "%;"); fptreef(shf, indent + INDENT, "else%;%T", t->right); } fptreef(shf, indent, "%;fi "); break; case TWHILE: case TUNTIL: /* 6 == strlen("while"/"until") */ fptreef(shf, indent + 6, "%s %T", (t->type==TWHILE) ? "while" : "until", t->left); fptreef(shf, indent, "%;do"); fptreef(shf, indent + INDENT, "%;%T", t->right); fptreef(shf, indent, "%;done "); break; case TBRACE: fptreef(shf, indent + INDENT, "{%;%T", t->left); fptreef(shf, indent, "%;} "); break; case TCOPROC: fptreef(shf, indent, "%T|& ", t->left); break; case TASYNC: fptreef(shf, indent, "%T& ", t->left); break; case TFUNCT: fptreef(shf, indent, t->u.ksh_func ? "function %s %T" : "%s() %T", t->str, t->left); break; case TTIME: fptreef(shf, indent, "time %T", t->left); break; default: fptreef(shf, indent, ""); break; } if ((ioact = t->ioact) != NULL) { int need_nl = 0; while (*ioact != NULL) pioact(shf, indent, *ioact++); /* Print here documents after everything else... */ for (ioact = t->ioact; *ioact != NULL; ) { struct ioword *iop = *ioact++; /* heredoc is 0 when tracing (set -x) */ if ((iop->flag & IOTYPE) == IOHERE && iop->heredoc) { tputc('\n', shf); shf_puts(iop->heredoc, shf); fptreef(shf, indent, "%s", evalstr(iop->delim, 0)); need_nl = 1; } } /* Last delimiter must be followed by a newline (this often * leads to an extra blank line, but its not worth worrying * about) */ if (need_nl) tputc('\n', shf); } } static void pioact(shf, indent, iop) register struct shf *shf; int indent; register struct ioword *iop; { int flag = iop->flag; int type = flag & IOTYPE; int expected; expected = (type == IOREAD || type == IORDWR || type == IOHERE) ? 0 : (type == IOCAT || type == IOWRITE) ? 1 : (type == IODUP && (iop->unit == !(flag & IORDUP))) ? iop->unit : iop->unit + 1; if (iop->unit != expected) tputc('0' + iop->unit, shf); switch (type) { case IOREAD: fptreef(shf, indent, "< "); break; case IOHERE: if (flag&IOSKIP) fptreef(shf, indent, "<<- "); else fptreef(shf, indent, "<< "); break; case IOCAT: fptreef(shf, indent, ">> "); break; case IOWRITE: if (flag&IOCLOB) fptreef(shf, indent, ">| "); else fptreef(shf, indent, "> "); break; case IORDWR: fptreef(shf, indent, "<> "); break; case IODUP: if (flag & IORDUP) fptreef(shf, indent, "<&"); else fptreef(shf, indent, ">&"); break; } /* name/delim are 0 when printing syntax errors */ if (type == IOHERE) { if (iop->delim) fptreef(shf, indent, "%S ", iop->delim); } else if (iop->name) fptreef(shf, indent, (iop->flag & IONAMEXP) ? "%s " : "%S ", iop->name); } /* * variants of fputc, fputs for ptreef and snptreef */ static void tputC(c, shf) register int c; register struct shf *shf; { if ((c&0x60) == 0) { /* C0|C1 */ tputc((c&0x80) ? '$' : '^', shf); tputc(((c&0x7F)|0x40), shf); } else if ((c&0x7F) == 0x7F) { /* DEL */ tputc((c&0x80) ? '$' : '^', shf); tputc('?', shf); } else tputc(c, shf); } static void tputS(wp, shf) register char *wp; register struct shf *shf; { register int c, quoted=0; /* problems: * `...` -> $(...) * 'foo' -> "foo" * could change encoding to: * OQUOTE ["'] ... CQUOTE ["'] * COMSUB [(`] ...\0 (handle $ ` \ and maybe " in `...` case) */ while (1) switch ((c = *wp++)) { case EOS: return; case CHAR: tputC(*wp++, shf); break; case QCHAR: c = *wp++; if (!quoted || (c == '"' || c == '`' || c == '$')) tputc('\\', shf); tputC(c, shf); break; case COMSUB: tputc('$', shf); tputc('(', shf); while (*wp != 0) tputC(*wp++, shf); tputc(')', shf); wp++; break; case EXPRSUB: tputc('$', shf); tputc('(', shf); tputc('(', shf); while (*wp != 0) tputC(*wp++, shf); tputc(')', shf); tputc(')', shf); wp++; break; case OQUOTE: quoted = 1; tputc('"', shf); break; case CQUOTE: quoted = 0; tputc('"', shf); break; case OSUBST: tputc('$', shf); if (*wp++ == '{') tputc('{', shf); while ((c = *wp++) != 0) tputC(c, shf); break; case CSUBST: if (*wp++ == '}') tputc('}', shf); break; #ifdef KSH case OPAT: tputc(*wp++, shf); tputc('(', shf); break; case SPAT: tputc('|', shf); break; case CPAT: tputc(')', shf); break; #endif /* KSH */ } } /* * this is the _only_ way to reliably handle * variable args with an ANSI compiler */ /* VARARGS */ int #ifdef HAVE_PROTOTYPES fptreef(struct shf *shf, int indent, const char *fmt, ...) #else fptreef(shf, indent, fmt, va_alist) struct shf *shf; int indent; const char *fmt; va_dcl #endif { va_list va; SH_VA_START(va, fmt); vfptreef(shf, indent, fmt, va); va_end(va); return 0; } /* VARARGS */ char * #ifdef HAVE_PROTOTYPES snptreef(char *s, int n, const char *fmt, ...) #else snptreef(s, n, fmt, va_alist) char *s; int n; const char *fmt; va_dcl #endif { va_list va; struct shf shf; shf_sopen(s, n, SHF_WR | (s ? 0 : SHF_DYNAMIC), &shf); SH_VA_START(va, fmt); vfptreef(&shf, 0, fmt, va); va_end(va); return shf_sclose(&shf); /* null terminates */ } static void vfptreef(shf, indent, fmt, va) register struct shf *shf; int indent; const char *fmt; register va_list va; { register int c; while ((c = *fmt++)) if (c == '%') { register long n; register char *p; int neg; switch ((c = *fmt++)) { case 'c': tputc(va_arg(va, int), shf); break; case 's': p = va_arg(va, char *); while (*p) tputc(*p++, shf); break; case 'S': /* word */ p = va_arg(va, char *); tputS(p, shf); break; case 'd': case 'u': /* decimal */ n = (c == 'd') ? va_arg(va, int) : va_arg(va, unsigned int); neg = c=='d' && n<0; p = ulton((neg) ? -n : n, 10); if (neg) *--p = '-'; while (*p) tputc(*p++, shf); break; case 'T': /* format tree */ ptree(va_arg(va, struct op *), indent, shf); break; case ';': /* newline or ; */ case 'N': /* newline or space */ if (shf->flags & SHF_STRING) { if (c == ';') tputc(';', shf); tputc(' ', shf); } else { int i; tputc('\n', shf); for (i = indent; i >= 8; i -= 8) tputc('\t', shf); for (; i > 0; --i) tputc(' ', shf); } break; case 'R': pioact(shf, indent, va_arg(va, struct ioword *)); break; default: tputc(c, shf); break; } } else tputc(c, shf); } /* * copy tree (for function definition) */ struct op * tcopy(t, ap) register struct op *t; Area *ap; { register struct op *r; register char **tw, **rw; if (t == NULL) return NULL; r = (struct op *) alloc(sizeof(struct op), ap); r->type = t->type; r->u.evalflags = t->u.evalflags; r->str = t->type == TCASE ? wdcopy(t->str, ap) : str_save(t->str, ap); if (t->vars == NULL) r->vars = NULL; else { for (tw = t->vars; *tw++ != NULL; ) ; rw = r->vars = (char **) alloc((int)(tw - t->vars) * sizeof(*tw), ap); for (tw = t->vars; *tw != NULL; ) *rw++ = wdcopy(*tw++, ap); *rw = NULL; } if (t->args == NULL) r->args = NULL; else { for (tw = t->args; *tw++ != NULL; ) ; rw = r->args = (char **) alloc((int)(tw - t->args) * sizeof(*tw), ap); for (tw = t->args; *tw != NULL; ) *rw++ = wdcopy(*tw++, ap); *rw = NULL; } r->ioact = (t->ioact == NULL) ? NULL : iocopy(t->ioact, ap); r->left = tcopy(t->left, ap); r->right = tcopy(t->right, ap); r->lineno = t->lineno; return r; } char * wdcopy(wp, ap) const char *wp; Area *ap; { size_t len = wdscan(wp, EOS) - wp; return memcpy(alloc(len, ap), wp, len); } /* return the position of prefix c in wp plus 1 */ char * wdscan(wp, c) register const char *wp; register int c; { register int nest = 0; while (1) switch (*wp++) { case EOS: return (char *) wp; case CHAR: case QCHAR: wp++; break; case COMSUB: case EXPRSUB: while (*wp++ != 0) ; break; case OQUOTE: case CQUOTE: break; case OSUBST: nest++; while (*wp++ != '\0') ; break; case CSUBST: wp++; if (c == CSUBST && nest == 0) return (char *) wp; nest--; break; #ifdef KSH case OPAT: nest++; wp++; break; case SPAT: case CPAT: if (c == wp[-1] && nest == 0) return (char *) wp; if (wp[-1] == CPAT) nest--; break; #endif /* KSH */ default: internal_errorf(0, "wdscan: unknown char 0x%x (carrying on)", wp[-1]); } } /* return a copy of wp without any of the mark up characters and * with quote characters (" ' \) stripped. * (string is allocated from ATEMP) */ char * wdstrip(wp) const char *wp; { struct shf shf; int c; shf_sopen((char *) 0, 32, SHF_WR | SHF_DYNAMIC, &shf); /* problems: * `...` -> $(...) * x${foo:-"hi"} -> x${foo:-hi} * x${foo:-'hi'} -> x${foo:-hi} */ while (1) switch ((c = *wp++)) { case EOS: return shf_sclose(&shf); /* null terminates */ case CHAR: case QCHAR: shf_putchar(*wp++, &shf); break; case COMSUB: shf_putchar('$', &shf); shf_putchar('(', &shf); while (*wp != 0) shf_putchar(*wp++, &shf); shf_putchar(')', &shf); break; case EXPRSUB: shf_putchar('$', &shf); shf_putchar('(', &shf); shf_putchar('(', &shf); while (*wp != 0) shf_putchar(*wp++, &shf); shf_putchar(')', &shf); shf_putchar(')', &shf); break; case OQUOTE: break; case CQUOTE: break; case OSUBST: shf_putchar('$', &shf); if (*wp++ == '{') shf_putchar('{', &shf); while ((c = *wp++) != 0) shf_putchar(c, &shf); break; case CSUBST: if (*wp++ == '}') shf_putchar('}', &shf); break; #ifdef KSH case OPAT: shf_putchar(*wp++, &shf); shf_putchar('(', &shf); break; case SPAT: shf_putchar('|', &shf); break; case CPAT: shf_putchar(')', &shf); break; #endif /* KSH */ } } static struct ioword ** iocopy(iow, ap) register struct ioword **iow; Area *ap; { register struct ioword **ior; register int i; for (ior = iow; *ior++ != NULL; ) ; ior = (struct ioword **) alloc((int)(ior - iow) * sizeof(*ior), ap); for (i = 0; iow[i] != NULL; i++) { register struct ioword *p, *q; p = iow[i]; q = (struct ioword *) alloc(sizeof(*p), ap); ior[i] = q; *q = *p; if (p->name != (char *) 0) q->name = wdcopy(p->name, ap); if (p->delim != (char *) 0) q->delim = wdcopy(p->delim, ap); if (p->heredoc != (char *) 0) q->heredoc = str_save(p->heredoc, ap); } ior[i] = NULL; return ior; } /* * free tree (for function definition) */ void tfree(t, ap) register struct op *t; Area *ap; { register char **w; if (t == NULL) return; if (t->str != NULL) afree((void*)t->str, ap); if (t->vars != NULL) { for (w = t->vars; *w != NULL; w++) afree((void*)*w, ap); afree((void*)t->vars, ap); } if (t->args != NULL) { for (w = t->args; *w != NULL; w++) afree((void*)*w, ap); afree((void*)t->args, ap); } if (t->ioact != NULL) iofree(t->ioact, ap); tfree(t->left, ap); tfree(t->right, ap); afree((void*)t, ap); } static void iofree(iow, ap) struct ioword **iow; Area *ap; { register struct ioword **iop; register struct ioword *p; for (iop = iow; (p = *iop++) != NULL; ) { if (p->name != NULL) afree((void*)p->name, ap); if (p->delim != NULL) afree((void*)p->delim, ap); if (p->heredoc != NULL) afree((void*)p->heredoc, ap); afree((void*)p, ap); } } /sys/src/ape/cmd/pdksh/tree.h 664 sys sys 1367613437 5028 /* * command trees for compile/execute */ /* $Id: tree.h,v 1.3 1994/05/31 13:34:34 michael Exp $ */ #define NOBLOCK ((struct op *)NULL) #define NOWORD ((char *)NULL) #define NOWORDS ((char **)NULL) /* * Description of a command or an operation on commands. */ struct op { short type; /* operation type, see below */ union { /* WARNING: newtp(), tcopy() use evalflags = 0 to clear union */ short evalflags; /* TCOM: arg expansion eval() flags */ short ksh_func; /* TFUNC: function x (vs x()) */ } u; char **args; /* arguments to a command */ char **vars; /* variable assignments */ struct ioword **ioact; /* IO actions (eg, < > >>) */ struct op *left, *right; /* descendents */ char *str; /* word for case; identifier for for, * select, and functions; * path to execute for TEXEC; * time hook for TCOM. */ int lineno; /* TCOM/TFUNC: LINENO for this */ }; /* Tree.type values */ #define TEOF 0 #define TCOM 1 /* command */ #define TPAREN 2 /* (c-list) */ #define TPIPE 3 /* a | b */ #define TLIST 4 /* a ; b */ #define TOR 5 /* || */ #define TAND 6 /* && */ #define TBANG 7 /* ! */ #define TDBRACKET 8 /* [[ .. ]] */ #define TFOR 9 #define TSELECT 10 #define TCASE 11 #define TIF 12 #define TWHILE 13 #define TUNTIL 14 #define TELIF 15 #define TPAT 16 /* pattern in case */ #define TBRACE 17 /* {c-list} */ #define TASYNC 18 /* c & */ #define TFUNCT 19 /* function name { command; } */ #define TTIME 20 /* time pipeline */ #define TEXEC 21 /* fork/exec eval'd TCOM */ #define TCOPROC 22 /* coprocess |& */ /* * prefix codes for words in command tree */ #define EOS 0 /* end of string */ #define CHAR 1 /* unquoted character */ #define QCHAR 2 /* quoted character */ #define COMSUB 3 /* $() substitution (0 terminated) */ #define EXPRSUB 4 /* $(()) substitution (0 terminated) */ #define OQUOTE 5 /* opening " or ' */ #define CQUOTE 6 /* closing " or ' */ #define OSUBST 7 /* opening ${ subst (followed by { or X) */ #define CSUBST 8 /* closing } of above (followed by } or X) */ #define OPAT 9 /* open pattern: *(, @(, etc. */ #define SPAT 10 /* separate pattern: | */ #define CPAT 11 /* close pattern: ) */ /* * IO redirection */ struct ioword { int unit; /* unit affected */ int flag; /* action (below) */ char *name; /* file name (unused if heredoc) */ char *delim; /* delimiter for <<,<<- */ char *heredoc;/* content of heredoc */ }; /* ioword.flag - type of redirection */ #define IOTYPE 0xF /* type: bits 0:3 */ #define IOREAD 0x1 /* < */ #define IOWRITE 0x2 /* > */ #define IORDWR 0x3 /* <>: todo */ #define IOHERE 0x4 /* << (here file) */ #define IOCAT 0x5 /* >> */ #define IODUP 0x6 /* <&/>& */ #define IOEVAL BIT(4) /* expand in << */ #define IOSKIP BIT(5) /* <<-, skip ^\t* */ #define IOCLOB BIT(6) /* >|, override -o noclobber */ #define IORDUP BIT(7) /* x<&y (as opposed to x>&y) */ #define IONAMEXP BIT(8) /* name has been expanded */ /* execute/exchild flags */ #define XEXEC BIT(0) /* execute without forking */ #define XFORK BIT(1) /* fork before executing */ #define XBGND BIT(2) /* command & */ #define XPIPEI BIT(3) /* input is pipe */ #define XPIPEO BIT(4) /* output is pipe */ #define XPIPE (XPIPEI|XPIPEO) /* member of pipe */ #define XXCOM BIT(5) /* `...` command */ #define XPCLOSE BIT(6) /* exchild: close close_fd in parent */ #define XCCLOSE BIT(7) /* exchild: close close_fd in child */ #define XERROK BIT(8) /* non-zero exit ok (for set -e) */ #define XCOPROC BIT(9) /* starting a co-process */ #define XTIME BIT(10) /* timeing TCOM command */ #define XINTACT BIT(11) /* OS2: proc started from interactive session */ /* * flags to control expansion of words (assumed by t->evalflags to fit * in a short) */ #define DOBLANK BIT(0) /* perform blank interpretation */ #define DOGLOB BIT(1) /* expand [?* */ #define DOPAT BIT(2) /* quote *?[ */ #define DOTILDE BIT(3) /* normal ~ expansion (first char) */ #define DONTRUNCOMMAND BIT(4) /* do not run $(command) things */ #define DOASNTILDE BIT(5) /* assignment ~ expansion (after =, :) */ #define DOBRACE_ BIT(6) /* used by expand(): do brace expansion */ #define DOMAGIC_ BIT(7) /* used by expand(): string contains MAGIC */ #define DOTEMP_ BIT(8) /* ditto : in word part of ${..[%#=?]..} */ #define DOVACHECK BIT(9) /* var assign check (for typeset, set, etc) */ #define DOMARKDIRS BIT(10) /* force markdirs behaviour */ /* * The arguments of [[ .. ]] expressions are kept in t->args[] and flags * indicating how the arguments have been munged are kept in t->vars[]. * The contents of t->vars[] are stuffed strings (so they can be treated * like all other t->vars[]) in which the second character is the one that * is examined. The DB_* defines are the values for these second characters. */ #define DB_NORM 1 /* normal argument */ #define DB_OR 2 /* || -> -o conversion */ #define DB_AND 3 /* && -> -a conversion */ #define DB_BE 4 /* an inserted -BE */ #define DB_PAT 5 /* a pattern argument */ /sys/src/ape/cmd/pdksh/tty.c 664 sys sys 1367613437 4015 #include "sh.h" #include "ksh_stat.h" #define EXTERN #include "tty.h" #undef EXTERN int get_tty(fd, ts) int fd; TTY_state *ts; { int ret; # ifdef HAVE_TERMIOS_H ret = tcgetattr(fd, ts); # else /* HAVE_TERIOS_H */ # ifdef HAVE_TERMIO_H ret = ioctl(fd, TCGETA, ts); # else /* HAVE_TERMIO_H */ ret = ioctl(fd, TIOCGETP, &ts->sgttyb); # ifdef TIOCGATC if (ioctl(fd, TIOCGATC, &ts->lchars) < 0) ret = -1; # else if (ioctl(fd, TIOCGETC, &ts->tchars) < 0) ret = -1; # ifdef TIOCGLTC if (ioctl(fd, TIOCGLTC, &ts->ltchars) < 0) ret = -1; # endif /* TIOCGLTC */ # endif /* TIOCGATC */ # endif /* HAVE_TERMIO_H */ # endif /* HAVE_TERIOS_H */ return ret; } int set_tty(fd, ts, flags) int fd; TTY_state *ts; int flags; { int ret = 0; # ifdef HAVE_TERMIOS_H ret = tcsetattr(fd, TCSADRAIN, ts); # else /* HAVE_TERIOS_H */ # ifdef HAVE_TERMIO_H # ifndef TCSETAW /* e.g. Cray-2 */ /* first wait for output to drain */ # ifdef TCSBRK if (ioctl(tty_fd, TCSBRK, 1) < 0) ret = -1; # else /* the following kludge is minimally intrusive, but sometimes fails */ if (flags & TF_WAIT) sleep((unsigned)1); /* fake it */ # endif # endif /* !TCSETAW */ # if defined(_BSD_SYSV) || !defined(TCSETAW) /* _BSD_SYSV must force TIOCSETN instead of TIOCSETP (preserve type-ahead) */ if (ioctl(tty_fd, TCSETA, ts) < 0) ret = -1; # else if (ioctl(tty_fd, TCSETAW, ts) < 0) ret = -1; # endif # else /* HAVE_TERMIO_H */ # if defined(__mips) && (defined(_SYSTYPE_BSD43) || defined(__SYSTYPE_BSD43)) /* Under RISC/os 5.00, bsd43 environment, after a tty driver * generated interrupt (eg, INTR, TSTP), all output to tty is * lost until a SETP is done (there must be a better way of * doing this...). */ if (flags & TF_MIPSKLUDGE) ret = ioctl(fd, TIOCSETP, &ts->sgttyb); else # endif /* _SYSTYPE_BSD43 */ ret = ioctl(fd, TIOCSETN, &ts->sgttyb); # ifdef TIOCGATC if (ioctl(fd, TIOCSATC, &ts->lchars) < 0) ret = -1; # else if (ioctl(fd, TIOCSETC, &ts->tchars) < 0) ret = -1; # ifdef TIOCGLTC if (ioctl(fd, TIOCSLTC, &ts->ltchars) < 0) ret = -1; # endif /* TIOCGLTC */ # endif /* TIOCGATC */ # endif /* HAVE_TERMIO_H */ # endif /* HAVE_TERIOS_H */ return ret; } /* Initialize tty_fd. Used for saving/reseting tty modes upon * foreground job completion and for setting up tty process group. */ void tty_init(init_ttystate) int init_ttystate; { int do_close = 1; int tfd; if (tty_fd >= 0) { close(tty_fd); tty_fd = -1; } tty_devtty = 1; /* SCO can't job control on /dev/tty, so don't try... */ #if !defined(__SCO__) && !defined(PLAN9) if ((tfd = open("/dev/tty", O_RDWR, 0)) < 0) { #ifdef __NeXT /* rlogin on NeXT boxes does not set up the controlling tty, * so force it to be done here... */ { extern char *ttyname ARGS((int)); char *s = ttyname(isatty(2) ? 2 : 0); int fd; if (s && (fd = open(s, O_RDWR, 0)) >= 0) { close(fd); tfd = open("/dev/tty", O_RDWR, 0); } } #endif /* __NeXT */ /* X11R5 xterm on mips doesn't set controlling tty properly - temporary hack */ # if !defined(__mips) || !(defined(_SYSTYPE_BSD43) || defined(__SYSTYPE_BSD43)) if (tfd < 0) { tty_devtty = 0; warningf(FALSE, "No controlling tty (open /dev/tty: %s)", strerror(errno)); } # endif /* __mips */ } #else /* !__SCO__ */ tfd = -1; #endif /* __SCO__ */ if (tfd < 0) { do_close = 0; if (isatty(0)) tfd = 0; else if (isatty(2)) tfd = 2; else { warningf(FALSE, "Can't find tty file descriptor"); return; } } if ((tty_fd = ksh_dupbase(tfd, FDBASE)) < 0) { warningf(FALSE, "j_ttyinit: dup of tty fd failed: %s", strerror(errno)); } else if (fd_clexec(tty_fd) < 0) { warningf(FALSE, "j_ttyinit: can't set close-on-exec flag: %s", strerror(errno)); close(tty_fd); tty_fd = -1; } else if (init_ttystate) get_tty(tty_fd, &tty_state); if (do_close) close(tfd); } void tty_close() { if (tty_fd >= 0) { close(tty_fd); tty_fd = -1; } } /sys/src/ape/cmd/pdksh/tty.h 664 sys sys 1367613437 3065 /* tty.h -- centralized definitions for a variety of terminal interfaces created by DPK, Oct. 1986 Rearranged to work with autoconf, added TTY_state, get_tty/set_tty Michael Rendell, May '94 last edit: 30-Jul-1987 D A Gwyn */ /* $Id$ */ /* some useful #defines */ #ifdef EXTERN # define I__(i) = i #else # define I__(i) # define EXTERN extern # define EXTERN_DEFINED #endif /* Don't know of a system on which including sys/ioctl.h with termios.h * causes problems. If there is one, these lines need to be deleted and * aclocal.m4 needs to have stuff un-commented. */ #ifdef SYS_IOCTL_WITH_TERMIOS # define SYS_IOCTL_WITH_TERMIOS #endif /* SYS_IOCTL_WITH_TERMIOS */ #ifdef SYS_IOCTL_WITH_TERMIO # define SYS_IOCTL_WITH_TERMIO #endif /* SYS_IOCTL_WITH_TERMIO */ #ifdef HAVE_TERMIOS_H # include # ifdef SYS_IOCTL_WITH_TERMIOS # if !(defined(sun) && !defined(__svr4__)) /* too many warnings on sunos */ /* Need to include sys/ioctl.h on some systems to get the TIOCGWINSZ * stuff (eg, digital unix). */ # include # endif /* !(sun && !__svr4__) */ # endif /* SYS_IOCTL_WITH_TERMIOS */ typedef struct termios TTY_state; #else # ifdef HAVE_TERMIO_H # include # ifdef SYS_IOCTL_WITH_TERMIO # include /* see comment above in termios stuff */ # endif /* SYS_IOCTL_WITH_TERMIO */ # if _BSD_SYSV /* BRL UNIX System V emulation */ # ifndef NTTYDISC # define TIOCGETD _IOR( 't', 0, int ) # define TIOCSETD _IOW( 't', 1, int ) # define NTTYDISC 2 # endif # ifndef TIOCSTI # define TIOCSTI _IOW( 't', 114, char ) # endif # ifndef TIOCSPGRP # define TIOCSPGRP _IOW( 't', 118, int ) # endif # endif /* _BSD_SYSV */ typedef struct termio TTY_state; # else /* HAVE_TERMIO_H */ /* Assume BSD tty stuff. Uses TIOCGETP, TIOCSETN; uses TIOCGATC/TIOCSATC if * available, otherwise it uses TIOCGETC/TIOCSETC (also uses TIOCGLTC/TIOCSLTC * if available) */ # ifdef _MINIX # include # define TIOCSETN TIOCSETP # else # include # endif typedef struct { struct sgttyb sgttyb; # ifdef TIOCGATC struct lchars lchars; # else /* TIOCGATC */ struct tchars tchars; # ifdef TIOCGLTC struct ltchars ltchars; # endif /* TIOCGLTC */ # endif /* TIOCGATC */ } TTY_state; # endif /* HAVE_TERMIO_H */ #endif /* HAVE_TERMIOS_H */ /* Flags for set_tty() */ #define TF_NONE 0x00 #define TF_WAIT 0x01 /* drain output, even it requires sleep() */ #define TF_MIPSKLUDGE 0x02 /* kludge to unwedge RISC/os 5.0 tty driver */ EXTERN int tty_fd I__(-1); /* dup'd tty file descriptor */ EXTERN int tty_devtty; /* true if tty_fd is from /dev/tty */ EXTERN TTY_state tty_state; /* saved tty state */ extern int get_tty ARGS((int fd, TTY_state *ts)); extern int set_tty ARGS((int fd, TTY_state *ts, int flags)); extern void tty_init ARGS((int init_ttystate)); extern void tty_close ARGS((void)); /* be sure not to interfere with anyone else's idea about EXTERN */ #ifdef EXTERN_DEFINED # undef EXTERN_DEFINED # undef EXTERN #endif #undef I__ /sys/src/ape/cmd/pdksh/var.c 664 sys sys 1367613437 27320 #include "sh.h" #include "ksh_time.h" #include "ksh_limval.h" #include "ksh_stat.h" #include /* * Variables * * WARNING: unreadable code, needs a rewrite * * if (flag&INTEGER), val.i contains integer value, and type contains base. * otherwise, (val.s + type) contains string value. * if (flag&EXPORT), val.s contains "name=value" for E-Z exporting. */ static struct tbl vtemp; static struct table specials; static char *formatstr ARGS((struct tbl *vp, const char *s)); static void export ARGS((struct tbl *vp, const char *val)); static int special ARGS((const char *name)); static void unspecial ARGS((const char *name)); static void getspec ARGS((struct tbl *vp)); static void setspec ARGS((struct tbl *vp)); static void unsetspec ARGS((struct tbl *vp)); static struct tbl *arraysearch ARGS((struct tbl *, int)); /* * create a new block for function calls and simple commands * assume caller has allocated and set up e->loc */ void newblock() { register struct block *l; static char *const empty[] = {null}; l = (struct block *) alloc(sizeof(struct block), ATEMP); l->flags = 0; ainit(&l->area); /* todo: could use e->area (l->area => l->areap) */ if (!e->loc) { l->argc = 0; l->argv = (char **) empty; } else { l->argc = e->loc->argc; l->argv = e->loc->argv; } l->exit = l->error = NULL; tinit(&l->vars, &l->area, 0); tinit(&l->funs, &l->area, 0); l->next = e->loc; e->loc = l; } /* * pop a block handling special variables */ void popblock() { register struct block *l = e->loc; register struct tbl *vp, **vpp = l->vars.tbls, *vq; register int i; e->loc = l->next; /* pop block */ for (i = l->vars.size; --i >= 0; ) if ((vp = *vpp++) != NULL && (vp->flag&SPECIAL)) if ((vq = global(vp->name))->flag & ISSET) setspec(vq); else unsetspec(vq); if (l->flags & BF_DOGETOPTS) user_opt = l->getopts_state; afreeall(&l->area); afree(l, ATEMP); } /* called by main() to initialize variable data structures */ void initvar() { static const struct { const char *name; int v; } names[] = { { "COLUMNS", V_COLUMNS }, { "IFS", V_IFS }, { "OPTIND", V_OPTIND }, { "PATH", V_PATH }, { "POSIXLY_CORRECT", V_POSIXLY_CORRECT }, { "TMPDIR", V_TMPDIR }, #ifdef HISTORY { "HISTFILE", V_HISTFILE }, { "HISTSIZE", V_HISTSIZE }, #endif /* HISTORY */ #ifdef EDIT { "EDITOR", V_EDITOR }, { "VISUAL", V_VISUAL }, #endif /* EDIT */ #ifdef KSH { "MAIL", V_MAIL }, { "MAILCHECK", V_MAILCHECK }, { "MAILPATH", V_MAILPATH }, { "RANDOM", V_RANDOM }, { "SECONDS", V_SECONDS }, { "TMOUT", V_TMOUT }, #endif /* KSH */ { "LINENO", V_LINENO }, { (char *) 0, 0 } }; int i; struct tbl *tp; tinit(&specials, APERM, 32); /* must be 2^n (currently 17 specials) */ for (i = 0; names[i].name; i++) { tp = tenter(&specials, names[i].name, hash(names[i].name)); tp->flag = DEFINED|ISSET; tp->type = names[i].v; } } /* Used to calculate an array index for global()/local(). Sets *arrayp to * non-zero if this is an array, sets *valp to the array index, returns * the basename of the array. */ const char * array_index_calc(const char *n, bool_t *arrayp, int *valp) { const char *p; int len; *arrayp = FALSE; p = skip_varname(n, FALSE); if (p != n && *p == '[' && (len = array_ref_len(p))) { char *sub, *tmp; long rval; /* Calculate the value of the subscript */ *arrayp = TRUE; tmp = str_nsave(p+1, len-2, ATEMP); sub = substitute(tmp, 0); afree(tmp, ATEMP); n = str_nsave(n, p - n, ATEMP); evaluate(sub, &rval, KSH_UNWIND_ERROR); if (rval < 0 || rval > ARRAYMAX) errorf("%s: subscript out of range", n); *valp = rval; afree(sub, ATEMP); } return n; } /* * Search for variable, if not found create globally. */ struct tbl * global(n) register const char *n; { register struct block *l = e->loc; register struct tbl *vp; register int c; unsigned h; bool_t array; int val; /* Check to see if this is an array */ n = array_index_calc(n, &array, &val); h = hash(n); c = n[0]; if (!letter(c)) { if (array) errorf("bad substitution"); vp = &vtemp; vp->flag = DEFINED; vp->type = 0; vp->areap = ATEMP; *vp->name = c; if (digit(c)) { for (c = 0; digit(*n); n++) c = c*10 + *n-'0'; if (c <= l->argc) /* setstr can't fail here */ setstr(vp, l->argv[c], KSH_RETURN_ERROR); vp->flag |= RDONLY; return vp; } vp->flag |= RDONLY; if (n[1] != '\0') return vp; vp->flag |= ISSET|INTEGER; switch (c) { case '$': vp->val.i = kshpid; break; case '!': /* If no job, expand to nothing */ if ((vp->val.i = j_async()) == 0) vp->flag &= ~(ISSET|INTEGER); break; case '?': vp->val.i = exstat; break; case '#': vp->val.i = l->argc; break; case '-': vp->flag &= ~INTEGER; vp->val.s = getoptions(); break; default: vp->flag &= ~(ISSET|INTEGER); } return vp; } for (l = e->loc; ; l = l->next) { vp = tsearch(&l->vars, n, h); if (vp != NULL) if (array) return arraysearch(vp, val); else return vp; if (l->next == NULL) break; } vp = tenter(&l->vars, n, h); if (array) vp = arraysearch(vp, val); vp->flag |= DEFINED; if (special(n)) vp->flag |= SPECIAL; return vp; } /* * Search for local variable, if not found create locally. */ struct tbl * local(n, copy) register const char *n; bool_t copy; { register struct block *l = e->loc; register struct tbl *vp; unsigned h; bool_t array; int val; /* Check to see if this is an array */ n = array_index_calc(n, &array, &val); h = hash(n); if (!letter(*n)) { vp = &vtemp; vp->flag = DEFINED|RDONLY; vp->type = 0; vp->areap = ATEMP; return vp; } vp = tenter(&l->vars, n, h); if (copy && !(vp->flag & DEFINED)) { struct block *ll = l; struct tbl *vq = (struct tbl *) 0; while ((ll = ll->next) && !(vq = tsearch(&ll->vars, n, h))) ; if (vq) { vp->flag |= vq->flag & (EXPORT|INTEGER|RDONLY |LJUST|RJUST|ZEROFIL |LCASEV|UCASEV_AL|INT_U|INT_L); if (vq->flag & INTEGER) vp->type = vq->type; vp->u2.field = vq->u2.field; } } if (array) vp = arraysearch(vp, val); vp->flag |= DEFINED; if (special(n)) vp->flag |= SPECIAL; return vp; } /* get variable string value */ char * str_val(vp) register struct tbl *vp; { char *s; if ((vp->flag&SPECIAL)) getspec(vp); if (!(vp->flag&ISSET)) s = null; /* special to dollar() */ else if (!(vp->flag&INTEGER)) /* string source */ s = vp->val.s + vp->type; else { /* integer source */ /* worst case number length is when base=2, so use BITS(long) */ /* minus base # number null */ static char strbuf[1 + 2 + 1 + BITS(long) + 1]; const char *digits = (vp->flag & UCASEV_AL) ? "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" : "0123456789abcdefghijklmnopqrstuvwxyz"; register unsigned long n; register int base; s = strbuf + sizeof(strbuf); if (vp->flag & INT_U) n = (unsigned long) vp->val.i; else n = (vp->val.i < 0) ? -vp->val.i : vp->val.i; base = (vp->type == 0) ? 10 : vp->type; *--s = '\0'; do { *--s = digits[n % base]; n /= base; } while (n != 0); if (base != 10) { *--s = '#'; *--s = digits[base % 10]; if (base >= 10) *--s = digits[base / 10]; } if (!(vp->flag & INT_U) && vp->val.i < 0) *--s = '-'; if (vp->flag & (RJUST|LJUST)) /* case already dealt with */ s = formatstr(vp, s); } return s; } /* get variable integer value, with error checking */ long intval(vp) register struct tbl *vp; { long num; int base; base = getint(vp, &num); if (base == -1) /* XXX check calls - is error here ok by POSIX? */ errorf("%s: bad number", str_val(vp)); return num; } /* set variable to string value */ int setstr(vq, s, error_ok) register struct tbl *vq; const char *s; int error_ok; { if (vq->flag & RDONLY) { warningf(TRUE, "%s: is read only", vq->name); if (!error_ok) errorf(null); return 0; } if (!(vq->flag&INTEGER)) { /* string dest */ if ((vq->flag&ALLOC)) { /* debugging */ if (s >= vq->val.s && s <= vq->val.s + strlen(vq->val.s)) internal_errorf(TRUE, "setstr: %s=%s: assigning to self", vq->name, s); afree((void*)vq->val.s, vq->areap); } vq->flag &= ~(ISSET|ALLOC); vq->type = 0; if (s && (vq->flag & (UCASEV_AL|LCASEV|LJUST|RJUST))) s = formatstr(vq, s); if ((vq->flag&EXPORT)) export(vq, s); else { vq->val.s = str_save(s, vq->areap); if (vq->val.s) /* don't lie */ vq->flag |= ALLOC; } } else /* integer dest */ if (!v_evaluate(vq, s, error_ok)) return 0; vq->flag |= ISSET; if ((vq->flag&SPECIAL)) setspec(vq); return 1; } /* set variable to integer */ void setint(vq, n) register struct tbl *vq; long n; { if (!(vq->flag&INTEGER)) { register struct tbl *vp = &vtemp; vp->flag = (ISSET|INTEGER); vp->type = 0; vp->areap = ATEMP; vp->val.i = n; /* setstr can't fail here */ setstr(vq, str_val(vp), KSH_RETURN_ERROR); } else vq->val.i = n; vq->flag |= ISSET; if ((vq->flag&SPECIAL)) setspec(vq); } int getint(vp, nump) struct tbl *vp; long *nump; { register char *s; register int c; int base, neg; int have_base = 0; long num; if (vp->flag&SPECIAL) getspec(vp); /* XXX is it possible for ISSET to be set and val.s to be 0? */ if (!(vp->flag&ISSET) || (!(vp->flag&INTEGER) && vp->val.s == NULL)) return -1; if (vp->flag&INTEGER) { *nump = vp->val.i; return vp->type; } s = vp->val.s + vp->type; if (s == NULL) /* redundent given initial test */ s = null; base = 10; num = 0; neg = 0; for (c = *s++; c ; c = *s++) { if (c == '-') { neg++; } else if (c == '#') { base = (int) num; if (have_base || base < 2 || base > 36) return -1; num = 0; have_base = 1; } else if (letnum(c)) { if (isdigit(c)) c -= '0'; else if (islower(c)) c -= 'a' - 10; /* todo: assumes ascii */ else if (isupper(c)) c -= 'A' - 10; /* todo: assumes ascii */ else c = -1; /* _: force error */ if (c < 0 || c >= base) return -1; num = num * base + c; } else return -1; } if (neg) num = -num; *nump = num; return base; } /* convert variable vq to integer variable, setting its value from vp * (vq and vp may be the same) */ struct tbl * setint_v(vq, vp) register struct tbl *vq, *vp; { int base; long num; if ((base = getint(vp, &num)) == -1) return NULL; if (!(vq->flag & INTEGER) && (vq->flag & ALLOC)) { vq->flag &= ~ALLOC; afree(vq->val.s, vq->areap); } vq->val.i = num; if (vq->type == 0) /* default base */ vq->type = base; vq->flag |= ISSET|INTEGER; if (vq->flag&SPECIAL) setspec(vq); return vq; } static char * formatstr(vp, s) struct tbl *vp; const char *s; { int olen, nlen; char *p, *q; olen = strlen(s); if (vp->flag & (RJUST|LJUST)) { if (!vp->u2.field) /* default field width */ vp->u2.field = olen; nlen = vp->u2.field; } else nlen = olen; p = (char *) alloc(nlen + 1, ATEMP); if (vp->flag & (RJUST|LJUST)) { int slen; if (vp->flag & RJUST) { const char *q = s + olen; /* strip trailing spaces (at&t ksh uses q[-1] == ' ') */ while (q > s && isspace(q[-1])) --q; slen = q - s; if (slen > vp->u2.field) { s += slen - vp->u2.field; slen = vp->u2.field; } shf_snprintf(p, nlen + 1, ((vp->flag & ZEROFIL) && digit(*s)) ? "%0*s%.*s" : "%*s%.*s", vp->u2.field - slen, null, slen, s); } else { /* strip leading spaces/zeros */ while (isspace(*s)) s++; if (vp->flag & ZEROFIL) while (*s == '0') s++; shf_snprintf(p, nlen + 1, "%-*.*s", vp->u2.field, vp->u2.field, s); } } else memcpy(p, s, olen + 1); if (vp->flag & UCASEV_AL) { for (q = p; *q; q++) if (islower(*q)) *q = toupper(*q); } else if (vp->flag & LCASEV) { for (q = p; *q; q++) if (isupper(*q)) *q = tolower(*q); } return p; } /* * make vp->val.s be "name=value" for quick exporting. */ static void export(vp, val) register struct tbl *vp; const char *val; { register char *xp; char *op = (vp->flag&ALLOC) ? vp->val.s : NULL; int namelen = strlen(vp->name); int vallen = strlen(val) + 1; vp->flag |= ALLOC; xp = (char*)alloc(namelen + 1 + vallen, vp->areap); memcpy(vp->val.s = xp, vp->name, namelen); xp += namelen; *xp++ = '='; vp->type = xp - vp->val.s; /* offset to value */ memcpy(xp, val, vallen); if (op != NULL) afree((void*)op, vp->areap); } /* * lookup variable (according to (set&LOCAL)), * set its attributes (INTEGER, RDONLY, EXPORT, TRACE, LJUST, RJUST, ZEROFIL, * LCASEV, UCASEV_AL), and optionally set its value if an assignment. */ struct tbl * typeset(var, set, clr, field, base) register const char *var; Tflag clr, set; int field, base; { register struct tbl *vp; struct tbl *vpbase, *t; char *tvar; const char *val; /* check for valid variable name, search for value */ val = skip_varname(var, FALSE); if (val == var) return NULL; if (*val == '[') { int len; len = array_ref_len(val); if (len == 0) return NULL; /* IMPORT is only used when the shell starts up and is * setting up its environment. Allow only simple array * references at this time since parameter/command substitution * is preformed on the [expression], which would be a major * security hole. */ if (set & IMPORT) { int i; for (i = 1; i < len - 1; i++) if (!digit(val[i])) return NULL; } val += len; } if (*val == '=') tvar = str_nsave(var, val++ - var, ATEMP); else { /* Importing from original envirnment: must have an = */ if (set & IMPORT) return NULL; tvar = (char *) var; val = NULL; } /* Prevent typeset from creating a local PATH/ENV/SHELL */ if (Flag(FRESTRICTED) && (strcmp(tvar, "PATH") == 0 || strcmp(tvar, "ENV") == 0 || strcmp(tvar, "SHELL") == 0)) errorf("%s: restricted", tvar); vp = (set&LOCAL) ? local(tvar, (set & LOCAL_COPY) ? TRUE : FALSE) : global(tvar); set &= ~(LOCAL|LOCAL_COPY); vpbase = (vp->flag & ARRAY) ? global(arrayname(var)) : vp; /* only allow export flag to be set. at&t ksh allows any attribute to * be changed, which means it can be truncated or modified * (-L/-R/-Z/-i). */ if ((vpbase->flag&RDONLY) && (val || clr || (set & ~EXPORT))) /* XXX check calls - is error here ok by POSIX? */ errorf("%s: is read only", tvar); if (val) afree(tvar, ATEMP); /* most calls are with set/clr == 0 */ if (set | clr) { int ok = 1; /* XXX if x[0] isn't set, there will be problems: need to have * one copy of attributes for arrays... */ for (t = vpbase; t; t = t->u.array) { int fake_assign; char UNINITIALIZED(*s); char UNINITIALIZED(*free_me); fake_assign = (t->flag & ISSET) && (!val || t != vp) && ((set & (UCASEV_AL|LCASEV|LJUST|RJUST|ZEROFIL)) || ((t->flag & INTEGER) && (clr & INTEGER)) || (!(t->flag & INTEGER) && (set & INTEGER))); if (fake_assign) { if (t->flag & INTEGER) { s = str_val(t); free_me = (char *) 0; } else { s = t->val.s + t->type; free_me = (t->flag & ALLOC) ? t->val.s : (char *) 0; } t->flag &= ~ALLOC; } if (!(t->flag & INTEGER) && (set & INTEGER)) { t->type = 0; t->flag &= ~ALLOC; } t->flag = (t->flag | set) & ~clr; /* Don't change base if assignment is to be done, * in case assignment fails. */ if ((set & INTEGER) && base > 0 && (!val || t != vp)) t->type = base; if (set & (LJUST|RJUST|ZEROFIL)) t->u2.field = field; if (fake_assign) { if (!setstr(t, s, KSH_RETURN_ERROR)) { /* Somewhat arbitrary action here: * zap contents of varibale, but keep * the flag settings. */ ok = 0; if (t->flag & INTEGER) t->flag &= ~ISSET; else { if (t->flag & ALLOC) afree((void*) t->val.s, t->areap); t->flag &= ~(ISSET|ALLOC); t->type = 0; } } if (free_me) afree((void *) free_me, t->areap); } } if (!ok) errorf(null); } if (val != NULL) { if (vp->flag&INTEGER) { /* do not zero base before assignment */ setstr(vp, val, KSH_UNWIND_ERROR); /* Done after assignment to override default */ if (base > 0) vp->type = base; } else /* setstr can't fail (readonly check already done) */ setstr(vp, val, KSH_RETURN_ERROR); } /* only x[0] is ever exported, so use vpbase */ if ((vpbase->flag&EXPORT) && !(vpbase->flag&INTEGER) && vpbase->type == 0) export(vpbase, (vpbase->flag&ISSET) ? vpbase->val.s : null); return vp; } /* Unset a variable. array_ref is set if there was an array reference in * the name lookup (eg, x[2]). */ void unset(vp, array_ref) register struct tbl *vp; int array_ref; { if (vp->flag & ALLOC) afree((void*)vp->val.s, vp->areap); if ((vp->flag & ARRAY) && !array_ref) { struct tbl *a, *tmp; /* Free up entire array */ for (a = vp->u.array; a; ) { tmp = a; a = a->u.array; if (tmp->flag & ALLOC) afree((void *) tmp->val.s, tmp->areap); afree(tmp, tmp->areap); } vp->u.array = (struct tbl *) 0; } /* If foo[0] is being unset, the remainder of the array is kept... */ vp->flag &= SPECIAL | (array_ref ? ARRAY|DEFINED : 0); if (vp->flag & SPECIAL) unsetspec(vp); /* responsible for `unspecial'ing var */ } /* return a pointer to the first char past a legal variable name (returns the * argument if there is no legal name, returns * a pointer to the terminating * null if whole string is legal). */ char * skip_varname(s, aok) const char *s; int aok; { int alen; if (s && letter(*s)) { while (*++s && letnum(*s)) ; if (aok && *s == '[' && (alen = array_ref_len(s))) s += alen; } return (char *) s; } /* Return a pointer to the first character past any legal variable name. */ char * skip_wdvarname(s, aok) const char *s; int aok; /* skip array de-reference? */ { if (s[0] == CHAR && letter(s[1])) { do s += 2; while (s[0] == CHAR && letnum(s[1])); if (aok && s[0] == CHAR && s[1] == '[') { /* skip possible array de-reference */ const char *p = s; char c; int depth = 0; while (1) { if (p[0] != CHAR) break; c = p[1]; p += 2; if (c == '[') depth++; else if (c == ']' && --depth == 0) { s = p; break; } } } } return (char *) s; } /* Check if coded string s is a variable name */ int is_wdvarname(s, aok) const char *s; int aok; { char *p = skip_wdvarname(s, aok); return p != s && p[0] == EOS; } /* Check if coded string s is a variable assignment */ int is_wdvarassign(s) const char *s; { char *p = skip_wdvarname(s, TRUE); return p != s && p[0] == CHAR && p[1] == '='; } /* * Make the exported environment from the exported names in the dictionary. */ char ** makenv() { struct block *l = e->loc; XPtrV env; register struct tbl *vp, **vpp; register int i; XPinit(env, 64); for (l = e->loc; l != NULL; l = l->next) for (vpp = l->vars.tbls, i = l->vars.size; --i >= 0; ) if ((vp = *vpp++) != NULL && (vp->flag&(ISSET|EXPORT)) == (ISSET|EXPORT)) { register struct block *l2; register struct tbl *vp2; unsigned h = hash(vp->name); /* unexport any redefined instances */ for (l2 = l->next; l2 != NULL; l2 = l2->next) { vp2 = tsearch(&l2->vars, vp->name, h); if (vp2 != NULL) vp2->flag &= ~EXPORT; } if ((vp->flag&INTEGER)) { /* integer to string */ char *val; val = str_val(vp); vp->flag &= ~(INTEGER|RDONLY); /* setstr can't fail here */ setstr(vp, val, KSH_RETURN_ERROR); } XPput(env, vp->val.s); } XPput(env, NULL); return (char **) XPclose(env); } /* * Called after a fork in parent to bump the random number generator. * Done to ensure children will not get the same random number sequence * if the parent doesn't use $RANDOM. */ void change_random() { rand(); } /* * handle special variables with side effects - PATH, SECONDS. */ /* Test if name is a special parameter */ static int special(name) register const char * name; { register struct tbl *tp; tp = tsearch(&specials, name, hash(name)); return tp && (tp->flag & ISSET) ? tp->type : V_NONE; } /* Make a variable non-special */ static void unspecial(name) register const char * name; { register struct tbl *tp; tp = tsearch(&specials, name, hash(name)); if (tp) tdelete(tp); } #ifdef KSH static time_t seconds; /* time SECONDS last set */ #endif /* KSH */ static int user_lineno; /* what user set $LINENO to */ static void getspec(vp) register struct tbl *vp; { switch (special(vp->name)) { #ifdef KSH case V_SECONDS: vp->flag &= ~SPECIAL; /* On start up the value of SECONDS is used before seconds * has been set - don't do anything in this case * (see initcoms[] in main.c). */ if (vp->flag & ISSET) setint(vp, (long) (time((time_t *)0) - seconds)); vp->flag |= SPECIAL; break; case V_RANDOM: vp->flag &= ~SPECIAL; setint(vp, (long) (rand() & 0x7fff)); vp->flag |= SPECIAL; break; #endif /* KSH */ #ifdef HISTORY case V_HISTSIZE: vp->flag &= ~SPECIAL; setint(vp, (long) histsize); vp->flag |= SPECIAL; break; #endif /* HISTORY */ case V_OPTIND: vp->flag &= ~SPECIAL; setint(vp, (long) user_opt.uoptind); vp->flag |= SPECIAL; break; case V_LINENO: vp->flag &= ~SPECIAL; setint(vp, (long) current_lineno + user_lineno); vp->flag |= SPECIAL; break; } } static void setspec(vp) register struct tbl *vp; { char *s; switch (special(vp->name)) { case V_PATH: if (path) afree(path, APERM); path = str_save(str_val(vp), APERM); flushcom(1); /* clear tracked aliases */ break; case V_IFS: setctypes(s = str_val(vp), C_IFS); ifs0 = *s; break; case V_OPTIND: vp->flag &= ~SPECIAL; getopts_reset((int) intval(vp)); vp->flag |= SPECIAL; break; case V_POSIXLY_CORRECT: change_flag(FPOSIX, OF_SPECIAL, 1); break; case V_TMPDIR: if (tmpdir) { afree(tmpdir, APERM); tmpdir = (char *) 0; } /* Use tmpdir iff it is an absolute path, is writable and * searchable and is a directory... */ { struct stat statb; s = str_val(vp); if (ISABSPATH(s) && eaccess(s, W_OK|X_OK) == 0 && stat(s, &statb) == 0 && S_ISDIR(statb.st_mode)) tmpdir = str_save(s, APERM); } break; #ifdef HISTORY case V_HISTSIZE: vp->flag &= ~SPECIAL; sethistsize((int) intval(vp)); vp->flag |= SPECIAL; break; case V_HISTFILE: sethistfile(str_val(vp)); break; #endif /* HISTORY */ #ifdef EDIT case V_VISUAL: set_editmode(str_val(vp)); break; case V_EDITOR: if (!(global("VISUAL")->flag & ISSET)) set_editmode(str_val(vp)); break; case V_COLUMNS: if ((x_cols = intval(vp)) <= MIN_COLS) x_cols = MIN_COLS; break; #endif /* EDIT */ #ifdef KSH case V_MAIL: mbset(str_val(vp)); break; case V_MAILPATH: mpset(str_val(vp)); break; case V_MAILCHECK: vp->flag &= ~SPECIAL; mcset(intval(vp)); vp->flag |= SPECIAL; break; case V_RANDOM: vp->flag &= ~SPECIAL; srand((unsigned int)intval(vp)); vp->flag |= SPECIAL; break; case V_SECONDS: vp->flag &= ~SPECIAL; seconds = time((time_t*) 0) - intval(vp); vp->flag |= SPECIAL; break; case V_TMOUT: /* at&t ksh seems to do this (only listen if integer) */ if (vp->flag & INTEGER) ksh_tmout = vp->val.i >= 0 ? vp->val.i : 0; break; #endif /* KSH */ case V_LINENO: vp->flag &= ~SPECIAL; /* The -1 is because line numbering starts at 1. */ user_lineno = (unsigned int) intval(vp) - current_lineno - 1; vp->flag |= SPECIAL; break; } } static void unsetspec(vp) register struct tbl *vp; { switch (special(vp->name)) { case V_PATH: if (path) afree(path, APERM); path = str_save(def_path, APERM); flushcom(1); /* clear tracked aliases */ break; case V_IFS: setctypes(" \t\n", C_IFS); ifs0 = ' '; break; case V_TMPDIR: /* should not become unspecial */ if (tmpdir) { afree(tmpdir, APERM); tmpdir = (char *) 0; } break; #ifdef KSH case V_MAIL: mbset((char *) 0); break; case V_MAILPATH: mpset((char *) 0); break; #endif /* KSH */ case V_LINENO: #ifdef KSH case V_MAILCHECK: /* at&t ksh leaves previous value in place */ case V_RANDOM: case V_SECONDS: case V_TMOUT: /* at&t ksh leaves previous value in place */ #endif /* KSH */ unspecial(vp->name); break; /* at&t ksh man page says OPTIND, OPTARG and _ lose special meaning, * but OPTARG does not (still set by getopts) and _ is also still * set in various places. * Don't know what at&t does for: * MAIL, MAILPATH, HISTSIZE, HISTFILE, * Unsetting these in at&t ksh does not loose the `specialness': * no effect: IFS, COLUMNS, PATH, TMPDIR, * VISUAL, EDITOR, * pdkshisms: no effect: * POSIXLY_CORRECT (use set +o posix instead) */ } } /* * Search for (and possibly create) a table entry starting with * vp, indexed by val. */ static struct tbl * arraysearch(vp, val) struct tbl *vp; int val; { struct tbl *prev, *curr, *new; vp->flag |= ARRAY|DEFINED; /* The table entry is always [0] */ if (val == 0) { vp->index = 0; return vp; } prev = vp; curr = vp->u.array; while (curr && curr->index < val) { prev = curr; curr = curr->u.array; } if (curr && curr->index == val) { if (curr->flag&ISSET) return curr; else new = curr; } else new = (struct tbl *)alloc(sizeof(struct tbl)+strlen(vp->name)+1, vp->areap); strcpy(new->name, vp->name); new->flag = vp->flag & ~(ALLOC|DEFINED|ISSET|SPECIAL); new->type = vp->type; new->areap = vp->areap; new->u2.field = vp->u2.field; new->index = val; if (curr != new) { /* not reusing old array entry */ prev->u.array = new; new->u.array = curr; } return new; } /* Return the length of an array reference (eg, [1+2]) - cp is assumed * to point to the open bracket. Returns 0 if there is no matching closing * bracket. */ int array_ref_len(cp) const char *cp; { const char *s = cp; int c; int depth = 0; while ((c = *s++) && (c != ']' || --depth)) if (c == '[') depth++; if (!c) return 0; return s - cp; } /* * Make a copy of the base of an array name */ char * arrayname(str) const char *str; { const char *p; if ((p = strchr(str, '[')) == 0) /* Shouldn't happen, but why worry? */ return (char *) str; return str_nsave(str, p - str, ATEMP); } /* Set (or overwrite, if !reset) the array variable var to the values in vals. */ void set_array(var, reset, vals) const char *var; int reset; char **vals; { struct tbl *vp, *vq; int i; /* to get local array, use "typeset foo; set -A foo" */ vp = global(var); /* Note: at&t ksh allows set -A but not set +A of a read-only var */ if ((vp->flag&RDONLY)) errorf("%s: is read only", var); /* This code is quite non-optimal */ if (reset > 0) /* trash existing values and attributes */ unset(vp, 0); /* todo: would be nice for assignment to completely succeed or * completely fail. Only really effects integer arrays: * evaluation of some of vals[] may fail... */ for (i = 0; vals[i]; i++) { vq = arraysearch(vp, i); /* would be nice to deal with errors here... (see above) */ setstr(vq, vals[i], KSH_RETURN_ERROR); } } /sys/src/ape/cmd/pdksh/version.c 664 sys sys 1367613437 129 /* * value of $KSH_VERSION (or $SH_VERSION) */ #include "sh.h" const char ksh_version [] = "@(#)PD KSH v5.2.14 99/07/13.2"; /sys/src/ape/cmd/psh.rc 775 sys sys 1367613437 34 #!/bin/rc exec /rc/bin/ape/psh $* /sys/src/ape/cmd/sed 20000000775 sys sys 1371506136 0 /sys/src/ape/cmd/sed/mkfile 664 sys sys 1369258447 140 APE=/sys/src/ape <$APE/config TARG=sed OFILES=sed0.$O\ sed1.$O HFILES=sed.h BIN=$APEBIN #include #include #include "sed.h" struct label *labtab = ltab; char CGMES[] = "sed: Command garbled: %s\n"; char TMMES[] = "sed: Too much text: %s\n"; char LTL[] = "sed: Label too long: %s\n"; char AD0MES[] = "sed: No addresses allowed: %s\n"; char AD1MES[] = "sed: Only one address allowed: %s\n"; uchar bittab[] = { 1, 2, 4, 8, 16, 32, 64, 128 }; void main(int argc, char **argv) { eargc = argc; eargv = (uchar**)argv; badp = &bad; aptr = abuf; hspend = holdsp; lab = labtab + 1; /* 0 reserved for end-pointer */ rep = ptrspace; rep->r1.ad1 = respace; lbend = &linebuf[LBSIZE]; hend = &holdsp[LBSIZE]; lcomend = &genbuf[64]; ptrend = &ptrspace[PTRSIZE]; reend = &respace[RESIZE]; labend = &labtab[LABSIZE]; lnum = 0; pending = 0; depth = 0; spend = linebuf; hspend = holdsp; fcode[0] = stdout; nfiles = 1; lastre = NULL; if(eargc == 1) exit(0); while (--eargc > 0 && (++eargv)[0][0] == '-') switch (eargv[0][1]) { case 'n': nflag++; continue; case 'f': if(eargc-- <= 0) exit(2); if((fin = fopen((char*)(*++eargv), "r")) == NULL) { fprintf(stderr, "sed: Cannot open pattern-file: %s\n", *eargv); exit(2); } fcomp(); fclose(fin); continue; case 'e': eflag++; fcomp(); eflag = 0; continue; case 'g': gflag++; continue; default: fprintf(stderr, "sed: Unknown flag: %c\n", eargv[0][1]); continue; } if(compfl == 0) { eargv--; eargc++; eflag++; fcomp(); eargv++; eargc--; eflag = 0; } if(depth) { fprintf(stderr, "sed: Too many {'s\n"); exit(2); } labtab->address = rep; dechain(); /* abort(); /*DEBUG*/ if(eargc <= 0) execute((uchar *)NULL); else while(--eargc >= 0) { execute(*eargv++); } fclose(stdout); exit(0); } void fcomp(void) { uchar *p, *op, *tp; uchar *address(uchar*); union reptr *pt, *pt1; int i; struct label *lpt; compfl = 1; op = lastre; if(rline(linebuf) < 0) { lastre = op; return; } if(*linebuf == '#') { if(linebuf[1] == 'n') nflag = 1; } else { cp = linebuf; goto comploop; } for(;;) { if(rline(linebuf) < 0) break; cp = linebuf; comploop: /* fprintf(stdout, "cp: %s\n", cp); /*DEBUG*/ while(*cp == ' ' || *cp == '\t') cp++; if(*cp == '\0' || *cp == '#') continue; if(*cp == ';') { cp++; goto comploop; } p = address(rep->r1.ad1); if(p == badp) { fprintf(stderr, CGMES, linebuf); exit(2); } if(p == 0) { p = rep->r1.ad1; rep->r1.ad1 = 0; } else { if(p == rep->r1.ad1) { if(op) rep->r1.ad1 = op; else { fprintf(stderr, "sed: First RE may not be null\n"); exit(2); } } if(*rep->r1.ad1 != CLNUM && *rep->r1.ad1 != CEND) op = rep->r1.ad1; if(*cp == ',' || *cp == ';') { cp++; if((rep->r1.ad2 = p) > reend) { fprintf(stderr, TMMES, linebuf); exit(2); } p = address(rep->r1.ad2); if(p == badp || p == 0) { fprintf(stderr, CGMES, linebuf); exit(2); } if(p == rep->r1.ad2) rep->r1.ad2 = op; else{ if(*rep->r1.ad2 != CLNUM && *rep->r1.ad2 != CEND) op = rep->r1.ad2; } } else rep->r1.ad2 = 0; } if(p > reend) { fprintf(stderr, "sed: Too much text: %s\n", linebuf); exit(2); } while(*cp == ' ' || *cp == '\t') cp++; swit: switch(*cp++) { default: /*fprintf(stderr, "cp = %d; *cp = %o\n", cp - linebuf, *cp);*/ fprintf(stderr, "sed: Unrecognized command: %s\n", linebuf); exit(2); case '!': rep->r1.negfl = 1; goto swit; case '{': rep->r1.command = BCOM; rep->r1.negfl = !(rep->r1.negfl); cmpend[depth++] = &rep->r2.lb1; if(++rep >= ptrend) { fprintf(stderr, "sed: Too many commands: %s\n", linebuf); exit(2); } rep->r1.ad1 = p; if(*cp == '\0') continue; goto comploop; case '}': if(rep->r1.ad1) { fprintf(stderr, AD0MES, linebuf); exit(2); } if(--depth < 0) { fprintf(stderr, "sed: Too many }'s\n"); exit(2); } *cmpend[depth] = rep; rep->r1.ad1 = p; if(*cp == 0) continue; goto comploop; case '=': rep->r1.command = EQCOM; if(rep->r1.ad2) { fprintf(stderr, AD1MES, linebuf); exit(2); } break; case ':': if(rep->r1.ad1) { fprintf(stderr, AD0MES, linebuf); exit(2); } while(*cp++ == ' '); cp--; tp = lab->asc; while((*tp = *cp++) && *tp != ';') if(++tp >= &(lab->asc[8])) { fprintf(stderr, LTL, linebuf); exit(2); } *tp = '\0'; if(*lab->asc == 0) { fprintf(stderr, CGMES, linebuf); exit(2); } if(lpt = search(lab)) { if(lpt->address) { fprintf(stderr, "sed: Duplicate labels: %s\n", linebuf); exit(2); } } else { lab->chain = 0; lpt = lab; if(++lab >= labend) { fprintf(stderr, "sed: Too many labels: %s\n", linebuf); exit(2); } } lpt->address = rep; rep->r1.ad1 = p; continue; case 'a': rep->r1.command = ACOM; if(rep->r1.ad2) { fprintf(stderr, AD1MES, linebuf); exit(2); } if(*cp == '\\') cp++; if(*cp++ != '\n') { fprintf(stderr, CGMES, linebuf); exit(2); } rep->r1.re1 = p; p = text(rep->r1.re1); break; case 'c': rep->r1.command = CCOM; if(*cp == '\\') cp++; if(*cp++ != ('\n')) { fprintf(stderr, CGMES, linebuf); exit(2); } rep->r1.re1 = p; p = text(rep->r1.re1); break; case 'i': rep->r1.command = ICOM; if(rep->r1.ad2) { fprintf(stderr, AD1MES, linebuf); exit(2); } if(*cp == '\\') cp++; if(*cp++ != ('\n')) { fprintf(stderr, CGMES, linebuf); exit(2); } rep->r1.re1 = p; p = text(rep->r1.re1); break; case 'g': rep->r1.command = GCOM; break; case 'G': rep->r1.command = CGCOM; break; case 'h': rep->r1.command = HCOM; break; case 'H': rep->r1.command = CHCOM; break; case 't': rep->r1.command = TCOM; goto jtcommon; case 'b': rep->r1.command = BCOM; jtcommon: while(*cp++ == ' '); cp--; if(*cp == '\0') { if(pt = labtab->chain) { while(pt1 = pt->r2.lb1) pt = pt1; pt->r2.lb1 = rep; } else labtab->chain = rep; break; } tp = lab->asc; while((*tp = *cp++) && *tp != ';') if(++tp >= &(lab->asc[8])) { fprintf(stderr, LTL, linebuf); exit(2); } cp--; *tp = '\0'; if(*lab->asc == 0) { fprintf(stderr, CGMES, linebuf); exit(2); } if(lpt = search(lab)) { if(lpt->address) { rep->r2.lb1 = lpt->address; } else { pt = lpt->chain; while(pt1 = pt->r2.lb1) pt = pt1; pt->r2.lb1 = rep; } } else { lab->chain = rep; lab->address = 0; if(++lab >= labend) { fprintf(stderr, "sed: Too many labels: %s\n", linebuf); exit(2); } } break; case 'n': rep->r1.command = NCOM; break; case 'N': rep->r1.command = CNCOM; break; case 'p': rep->r1.command = PCOM; break; case 'P': rep->r1.command = CPCOM; break; case 'r': rep->r1.command = RCOM; if(rep->r1.ad2) { fprintf(stderr, AD1MES, linebuf); exit(2); } if(*cp++ != ' ') { fprintf(stderr, CGMES, linebuf); exit(2); } rep->r1.re1 = p; p = text(rep->r1.re1); break; case 'd': rep->r1.command = DCOM; break; case 'D': rep->r1.command = CDCOM; rep->r2.lb1 = ptrspace; break; case 'q': rep->r1.command = QCOM; if(rep->r1.ad2) { fprintf(stderr, AD1MES, linebuf); exit(2); } break; case 'l': rep->r1.command = LCOM; break; case 's': rep->r1.command = SCOM; seof = *cp++; rep->r1.re1 = p; p = compile(rep->r1.re1); if(p == badp) { fprintf(stderr, CGMES, linebuf); exit(2); } if(p == rep->r1.re1) { if(op == NULL) { fprintf(stderr, "sed: First RE may not be null.\n"); exit(2); } rep->r1.re1 = op; } else { op = rep->r1.re1; } if((rep->r1.rhs = p) > reend) { fprintf(stderr, TMMES, linebuf); exit(2); } if((p = compsub(rep->r1.rhs)) == badp) { fprintf(stderr, CGMES, linebuf); exit(2); } if(*cp == 'g') { cp++; rep->r1.gfl++; } else if(gflag) rep->r1.gfl++; if(*cp == 'p') { cp++; rep->r1.pfl = 1; } if(*cp == 'P') { cp++; rep->r1.pfl = 2; } if(*cp == 'w') { cp++; if(*cp++ != ' ') { fprintf(stderr, CGMES, linebuf); exit(2); } if(nfiles >= MAXFILES) { fprintf(stderr, "sed: Too many files in w commands 1 \n"); exit(2); } text((uchar*)fname[nfiles]); for(i = nfiles - 1; i >= 0; i--) if(cmp((uchar*)fname[nfiles],(uchar*)fname[i]) == 0) { rep->r1.fcode = fcode[i]; goto done; } if((rep->r1.fcode = fopen(fname[nfiles], "w")) == NULL) { fprintf(stderr, "sed: Cannot open %s\n", fname[nfiles]); exit(2); } fcode[nfiles++] = rep->r1.fcode; } break; case 'w': rep->r1.command = WCOM; if(*cp++ != ' ') { fprintf(stderr, CGMES, linebuf); exit(2); } if(nfiles >= MAXFILES){ fprintf(stderr, "sed: Too many files in w commands 2 \n"); fprintf(stderr, "nfiles = %d; MAXF = %d\n", nfiles, MAXFILES); exit(2); } text((uchar*)fname[nfiles]); for(i = nfiles - 1; i >= 0; i--) if(cmp((uchar*)fname[nfiles], (uchar*)fname[i]) == 0) { rep->r1.fcode = fcode[i]; goto done; } if((rep->r1.fcode = fopen(fname[nfiles], "w")) == NULL) { fprintf(stderr, "sed: Cannot create %s\n", fname[nfiles]); exit(2); } fcode[nfiles++] = rep->r1.fcode; break; case 'x': rep->r1.command = XCOM; break; case 'y': rep->r1.command = YCOM; seof = *cp++; rep->r1.re1 = p; p = ycomp(rep->r1.re1); if(p == badp) { fprintf(stderr, CGMES, linebuf); exit(2); } if(p > reend) { fprintf(stderr, TMMES, linebuf); exit(2); } break; } done: if(++rep >= ptrend) { fprintf(stderr, "sed: Too many commands, last: %s\n", linebuf); exit(2); } rep->r1.ad1 = p; if(*cp++ != '\0') { if(cp[-1] == ';') goto comploop; fprintf(stderr, CGMES, linebuf); exit(2); } } } uchar * compsub(uchar *rhsbuf) { uchar *p, *q, *r; p = rhsbuf; q = cp; for(;;) { if((*p = *q++) == '\\') { *++p = *q++; if(*p >= '1' && *p <= '9' && *p > numbra + '0') return(badp); if(*p == 'n') *--p = '\n'; } else if(*p == seof) { *p++ = '\0'; cp = q; return(p); } if(*p++ == '\0') { return(badp); } } } uchar * compile(uchar *expbuf) { int c; uchar *ep, *sp; uchar neg; uchar *lastep, *cstart; int cclcnt; int closed; uchar bracket[NBRA], *bracketp; if(*cp == seof) { cp++; return(expbuf); } ep = expbuf; lastep = 0; bracketp = bracket; closed = numbra = 0; sp = cp; if (*sp == '^') { *ep++ = 1; sp++; } else { *ep++ = 0; } for (;;) { if (ep >= reend) { cp = sp; return(badp); } if((c = *sp++) == seof) { if(bracketp != bracket) { cp = sp; return(badp); } cp = sp; *ep++ = CEOF; return(ep); } if(c != '*') lastep = ep; switch (c) { case '\\': if((c = *sp++) == '(') { if(numbra >= NBRA) { cp = sp; return(badp); } *bracketp++ = numbra; *ep++ = CBRA; *ep++ = numbra++; continue; } if(c == ')') { if(bracketp <= bracket) { cp = sp; return(badp); } *ep++ = CKET; *ep++ = *--bracketp; closed++; continue; } if(c >= '1' && c <= '9') { if((c -= '1') >= closed) return(badp); *ep++ = CBACK; *ep++ = c; continue; } if(c == '\n') { cp = sp; return(badp); } if(c == 'n') { c = '\n'; } goto defchar; case '\0': case '\n': cp = sp; return(badp); case '.': *ep++ = CDOT; continue; case '*': if (lastep == 0) goto defchar; if(*lastep == CKET) { cp = sp; return(badp); } *lastep |= STAR; continue; case '$': if (*sp != seof) goto defchar; *ep++ = CDOL; continue; case '[': if(&ep[33] >= reend) { fprintf(stderr, "sed: RE too long: %s\n", linebuf); exit(2); } *ep++ = CCL; neg = 0; if((c = *sp++) == '^') { neg = 1; c = *sp++; } cstart = sp; do { if(c == '\0') { fprintf(stderr, CGMES, linebuf); exit(2); } if (c=='-' && sp>cstart && *sp!=']') { for (c = sp[-2]; c<*sp; c++) ep[c>>3] |= bittab[c&07]; } if(c == '\\') { switch(c = *sp++) { case 'n': c = '\n'; break; } } ep[c >> 3] |= bittab[c & 07]; } while((c = *sp++) != ']'); if(neg) for(cclcnt = 0; cclcnt < 32; cclcnt++) ep[cclcnt] ^= -1; ep[0] &= 0376; ep += 32; continue; defchar: default: *ep++ = CCHR; *ep++ = c; } } } int rline(uchar *lbuf) { uchar *p, *q; int t; static uchar *saveq; p = lbuf - 1; if(eflag) { if(eflag > 0) { eflag = -1; if(eargc-- <= 0) exit(2); q = *++eargv; while(*++p = *q++) { if(*p == '\\') { if((*++p = *q++) == '\0') { saveq = 0; return(-1); } else continue; } if(*p == '\n') { *p = '\0'; saveq = q; return(1); } } saveq = 0; return(1); } if((q = saveq) == 0) return(-1); while(*++p = *q++) { if(*p == '\\') { if((*++p = *q++) == '0') { saveq = 0; return(-1); } else continue; } if(*p == '\n') { *p = '\0'; saveq = q; return(1); } } saveq = 0; return(1); } while((t = getc(fin)) != EOF) { *++p = t; if(*p == '\\') { t = getc(fin); *++p = t; } else if(*p == '\n') { *p = '\0'; return(1); } } *++p = '\0'; return(-1); } uchar * address(uchar *expbuf) { uchar *rcp; long lno; if(*cp == '$') { cp++; *expbuf++ = CEND; *expbuf++ = CEOF; return(expbuf); } if(*cp == '/') { seof = '/'; cp++; return(compile(expbuf)); } rcp = cp; lno = 0; while(*rcp >= '0' && *rcp <= '9') lno = lno*10 + *rcp++ - '0'; if(rcp > cp) { if(!lno){ fprintf(stderr, "sed: line number 0 is illegal\n"); exit(2); } *expbuf++ = CLNUM; *expbuf++ = lno; *expbuf++ = lno >> 8; *expbuf++ = lno >> 16; *expbuf++ = lno >> 24; *expbuf++ = CEOF; cp = rcp; return(expbuf); } return(0); } int cmp(uchar *a, uchar *b) { uchar *ra, *rb; ra = a - 1; rb = b - 1; while(*++ra == *++rb) if(*ra == '\0') return(0); return(1); } uchar * text(uchar *textbuf) { uchar *p, *q; p = textbuf; q = cp; while(*q == '\t' || *q == ' ') q++; for(;;) { if((*p = *q++) == '\\') *p = *q++; if(*p == '\0') { cp = --q; return(++p); } if(*p == '\n') { while(*q == '\t' || *q == ' ') q++; } p++; } } struct label * search(struct label *ptr) { struct label *rp; rp = labtab; while(rp < ptr) { if(cmp(rp->asc, ptr->asc) == 0) return(rp); rp++; } return(0); } void dechain(void) { struct label *lptr; union reptr *rptr, *trptr; for(lptr = labtab; lptr < lab; lptr++) { if(lptr->address == 0) { fprintf(stderr, "sed: Undefined label: %s\n", lptr->asc); exit(2); } if(lptr->chain) { rptr = lptr->chain; while(trptr = rptr->r2.lb1) { rptr->r2.lb1 = lptr->address; rptr = trptr; } rptr->r2.lb1 = lptr->address; } } } uchar * ycomp(uchar *expbuf) { uchar *ep, *tsp; int c; uchar *sp; ep = expbuf; sp = cp; for(tsp = cp; *tsp != seof; tsp++) { if(*tsp == '\\') tsp++; if(*tsp == '\n' || *tsp == '\0') return(badp); } tsp++; while((c = *sp++) != seof) { if(c == '\\' && *sp == 'n') { sp++; c = '\n'; } if((ep[c] = *tsp++) == '\\' && *tsp == 'n') { ep[c] = '\n'; tsp++; } if(ep[c] == seof || ep[c] == '\0') return(badp); } if(*tsp != seof) return(badp); cp = ++tsp; for(c = 0; c<0400; c++) if(ep[c] == 0) ep[c] = c; return(ep + 0400); } /sys/src/ape/cmd/sed/sed1.c 664 sys sys 1367613437 10841 #include #include #include #include #include #include "sed.h" #define Read(f, buf, n) (fflush(stdout), read(f, buf, n)) void execute(uchar *file) { uchar *p1, *p2; union reptr *ipc; int c; long l; uchar *execp; if (file) { if ((f = open((char*)file, O_RDONLY)) < 0) { fprintf(stderr, "sed: Can't open %s\n", file); } } else f = 0; ebp = ibuf; cbp = ibuf; if(pending) { ipc = pending; pending = 0; goto yes; } for(;;) { if((execp = gline(linebuf)) == badp) { close(f); return; } spend = execp; for(ipc = ptrspace; ipc->r1.command; ) { p1 = ipc->r1.ad1; p2 = ipc->r1.ad2; if(p1) { if(ipc->r1.inar) { if(*p2 == CEND) { p1 = 0; } else if(*p2 == CLNUM) { l = p2[1]&0377 | ((p2[2]&0377)<<8) | ((p2[3]&0377)<<16) | ((p2[4]&0377)<<24); if(lnum > l) { ipc->r1.inar = 0; if(ipc->r1.negfl) goto yes; ipc++; continue; } if(lnum == l) { ipc->r1.inar = 0; } } else if(match(p2, 0)) { ipc->r1.inar = 0; } } else if(*p1 == CEND) { if(!dolflag) { if(ipc->r1.negfl) goto yes; ipc++; continue; } } else if(*p1 == CLNUM) { l = p1[1]&0377 | ((p1[2]&0377)<<8) | ((p1[3]&0377)<<16) | ((p1[4]&0377)<<24); if(lnum != l) { if(ipc->r1.negfl) goto yes; ipc++; continue; } if(p2) ipc->r1.inar = 1; } else if(match(p1, 0)) { if(p2) ipc->r1.inar = 1; } else { if(ipc->r1.negfl) goto yes; ipc++; continue; } } if(ipc->r1.negfl) { ipc++; continue; } yes: command(ipc); if(delflag) break; if(jflag) { jflag = 0; if((ipc = ipc->r2.lb1) == 0) { ipc = ptrspace; break; } } else ipc++; } if(!nflag && !delflag) { for(p1 = linebuf; p1 < spend; p1++) putc(*p1, stdout); putc('\n', stdout); } if(aptr > abuf) { arout(); } delflag = 0; } } int match(uchar *expbuf, int gf) { uchar *p1, *p2; int c; if(gf) { if(*expbuf) return(0); p1 = linebuf; p2 = genbuf; while(*p1++ = *p2++); locs = p1 = loc2; } else { p1 = linebuf; locs = 0; } p2 = expbuf; if(*p2++) { loc1 = p1; if(*p2 == CCHR && p2[1] != *p1) return(0); return(advance(p1, p2)); } /* fast check for first character */ if(*p2 == CCHR) { c = p2[1]; do { if(*p1 != c) continue; if(advance(p1, p2)) { loc1 = p1; return(1); } } while(*p1++); return(0); } do { if(advance(p1, p2)) { loc1 = p1; return(1); } } while(*p1++); return(0); } int advance(uchar *alp, uchar *aep) { uchar *lp, *ep, *curlp; uchar c; uchar *bbeg; int ct; /*fprintf(stderr, "*lp = %c, %o\n*ep = %c, %o\n", *lp, *lp, *ep, *ep); /*DEBUG*/ lp = alp; ep = aep; for (;;) switch (*ep++) { case CCHR: if (*ep++ == *lp++) continue; return(0); case CDOT: if (*lp++) continue; return(0); case CNL: case CDOL: if (*lp == 0) continue; return(0); case CEOF: loc2 = lp; return(1); case CCL: c = *lp++; if(ep[c>>3] & bittab[c & 07]) { ep += 32; continue; } return(0); case CBRA: braslist[*ep++] = lp; continue; case CKET: braelist[*ep++] = lp; continue; case CBACK: bbeg = braslist[*ep]; ct = braelist[*ep++] - bbeg; if(ecmp(bbeg, lp, ct)) { lp += ct; continue; } return(0); case CBACK|STAR: bbeg = braslist[*ep]; ct = braelist[*ep++] - bbeg; curlp = lp; while(ecmp(bbeg, lp, ct)) lp += ct; while(lp >= curlp) { if(advance(lp, ep)) return(1); lp -= ct; } return(0); case CDOT|STAR: curlp = lp; while (*lp++); goto star; case CCHR|STAR: curlp = lp; while (*lp++ == *ep); ep++; goto star; case CCL|STAR: curlp = lp; do { c = *lp++; } while(ep[c>>3] & bittab[c & 07]); ep += 32; goto star; star: if(--lp == curlp) { continue; } if(*ep == CCHR) { c = ep[1]; do { if(*lp != c) continue; if(advance(lp, ep)) return(1); } while(lp-- > curlp); return(0); } if(*ep == CBACK) { c = *(braslist[ep[1]]); do { if(*lp != c) continue; if(advance(lp, ep)) return(1); } while(lp-- > curlp); return(0); } do { if(lp == locs) break; if (advance(lp, ep)) return(1); } while (lp-- > curlp); return(0); default: fprintf(stderr, "sed: RE botch, %o\n", *--ep); exit(1); } } int substitute(union reptr *ipc) { uchar *oloc2; if(match(ipc->r1.re1, 0)) { sflag = 1; if(!ipc->r1.gfl) { dosub(ipc->r1.rhs); return(1); } oloc2 = NULL; do { if(oloc2 == loc2) { loc2++; continue; } else { dosub(ipc->r1.rhs); if(*loc2 == 0) break; oloc2 = loc2; } } while(match(ipc->r1.re1, 1)); return(1); } return(0); } void dosub(uchar *rhsbuf) { uchar *lp, *sp, *rp; int c; lp = linebuf; sp = genbuf; rp = rhsbuf; while (lp < loc1) *sp++ = *lp++; while(c = *rp++) { if (c == '\\') { c = *rp++; if (c >= '1' && c < NBRA+'1') { sp = place(sp, braslist[c-'1'], braelist[c-'1']); continue; } } else if(c == '&') { sp = place(sp, loc1, loc2); continue; } *sp++ = c; if (sp >= &genbuf[LBSIZE]) fprintf(stderr, "sed: Output line too long.\n"); } lp = loc2; loc2 = sp - genbuf + linebuf; while (*sp++ = *lp++) if (sp >= &genbuf[LBSIZE]) { fprintf(stderr, "sed: Output line too long.\n"); } lp = linebuf; sp = genbuf; while (*lp++ = *sp++); spend = lp-1; } uchar * place(uchar *asp, uchar *al1, uchar *al2) { uchar *sp, *l1, *l2; sp = asp; l1 = al1; l2 = al2; while (l1 < l2) { *sp++ = *l1++; if (sp >= &genbuf[LBSIZE]) fprintf(stderr, "sed: Output line too long.\n"); } return(sp); } void command(union reptr *ipc) { int i; uchar *p1, *p2; uchar *execp; switch(ipc->r1.command) { case ACOM: *aptr++ = ipc; if(aptr >= &abuf[ABUFSIZE]) { fprintf(stderr, "sed: Too many appends after line %ld\n", lnum); } *aptr = 0; break; case CCOM: delflag = 1; if(!ipc->r1.inar || dolflag) { for(p1 = ipc->r1.re1; *p1; ) putc(*p1++, stdout); putc('\n', stdout); } break; case DCOM: delflag++; break; case CDCOM: p1 = p2 = linebuf; while(*p1 != '\n') { if(*p1++ == 0) { delflag++; return; } } p1++; while(*p2++ = *p1++); spend = p2-1; jflag++; break; case EQCOM: fprintf(stdout, "%ld\n", lnum); break; case GCOM: p1 = linebuf; p2 = holdsp; while(*p1++ = *p2++); spend = p1-1; break; case CGCOM: *spend++ = '\n'; p1 = spend; p2 = holdsp; while(*p1++ = *p2++) if(p1 >= lbend) break; spend = p1-1; break; case HCOM: p1 = holdsp; p2 = linebuf; while(*p1++ = *p2++); hspend = p1-1; break; case CHCOM: *hspend++ = '\n'; p1 = hspend; p2 = linebuf; while(*p1++ = *p2++) if(p1 >= hend) break; hspend = p1-1; break; case ICOM: for(p1 = ipc->r1.re1; *p1; ) putc(*p1++, stdout); putc('\n', stdout); break; case BCOM: jflag = 1; break; case LCOM: p1 = linebuf; p2 = genbuf; while(*p1) { p2 = lformat(*p1++ & 0377, p2); if(p2>lcomend && *p1) { *p2 = 0; fprintf(stdout, "%s\\\n", genbuf); p2 = genbuf; } } if(p2>genbuf && (p1[-1]==' '||p1[-1]=='\n')) p2 = lformat('\n', p2); *p2 = 0; fprintf(stdout, "%s\n", genbuf); break; case NCOM: if(!nflag) { for(p1 = linebuf; p1 < spend; p1++) putc(*p1, stdout); putc('\n', stdout); } if(aptr > abuf) arout(); if((execp = gline(linebuf)) == badp) { pending = ipc; delflag = 1; break; } spend = execp; break; case CNCOM: if(aptr > abuf) arout(); *spend++ = '\n'; if((execp = gline(spend)) == badp) { pending = ipc; delflag = 1; break; } spend = execp; break; case PCOM: for(p1 = linebuf; p1 < spend; p1++) putc(*p1, stdout); putc('\n', stdout); break; case CPCOM: cpcom: for(p1 = linebuf; *p1 != '\n' && *p1 != '\0'; ) putc(*p1++, stdout); putc('\n', stdout); break; case QCOM: if(!nflag) { for(p1 = linebuf; p1 < spend; p1++) putc(*p1, stdout); putc('\n', stdout); } if(aptr > abuf) arout(); fclose(stdout); lseek(f,(long)(cbp-ebp),2); exit(0); case RCOM: *aptr++ = ipc; if(aptr >= &abuf[ABUFSIZE]) fprintf(stderr, "sed: Too many reads after line%ld\n", lnum); *aptr = 0; break; case SCOM: i = substitute(ipc); if(ipc->r1.pfl && i) if(ipc->r1.pfl == 1) { for(p1 = linebuf; p1 < spend; p1++) putc(*p1, stdout); putc('\n', stdout); } else goto cpcom; if(i && ipc->r1.fcode) goto wcom; break; case TCOM: if(sflag == 0) break; sflag = 0; jflag = 1; break; wcom: case WCOM: fprintf(ipc->r1.fcode, "%s\n", linebuf); fflush(ipc->r1.fcode); break; case XCOM: p1 = linebuf; p2 = genbuf; while(*p2++ = *p1++); p1 = holdsp; p2 = linebuf; while(*p2++ = *p1++); spend = p2 - 1; p1 = genbuf; p2 = holdsp; while(*p2++ = *p1++); hspend = p2 - 1; break; case YCOM: p1 = linebuf; p2 = ipc->r1.re1; while(*p1 = p2[*p1]) p1++; break; } } uchar * gline(uchar *addr) { uchar *p1, *p2; int c; sflag = 0; p1 = addr; p2 = cbp; for (;;) { if (p2 >= ebp) { if ((c = Read(f, ibuf, 512)) <= 0) { return(badp); } p2 = ibuf; ebp = ibuf+c; } if ((c = *p2++) == '\n') { if(p2 >= ebp) { if((c = Read(f, ibuf, 512)) <= 0) { close(f); if(eargc == 0) dolflag = 1; } p2 = ibuf; ebp = ibuf + c; } break; } if(c) if(p1 < lbend) *p1++ = c; } lnum++; *p1 = 0; cbp = p2; return(p1); } int ecmp(uchar *a, uchar *b, int count) { while(count--) if(*a++ != *b++) return(0); return(1); } void arout(void) { uchar *p1; FILE *fi; uchar c; int t; aptr = abuf - 1; while(*++aptr) { if((*aptr)->r1.command == ACOM) { for(p1 = (*aptr)->r1.re1; *p1; ) putc(*p1++, stdout); putc('\n', stdout); } else { if((fi = fopen((char*)((*aptr)->r1.re1), "r")) == NULL) continue; while((t = getc(fi)) != EOF) { c = t; putc(c, stdout); } fclose(fi); } } aptr = abuf; *aptr = 0; } uchar * lformat(int c, uchar *p) { int trans = c=='\b'? 'b': c=='\t'? 't': c=='\n'? 'n': c=='\v'? 'v': c=='\f'? 'f': c=='\r'? 'r': c=='\\'? '\\': 0; if(trans) { *p++ = '\\'; *p++ = trans; } else if(c<040 || c>=0177) { *p++ = '\\'; *p++ = ((c>>6)&07) + '0'; *p++ = ((c>>3)&07) + '0'; *p++ = (c&07) + '0'; } else *p++ = c; return p; } /sys/src/ape/cmd/uname.c 664 sys sys 1369258378 1363 #include #include #include #define ARGBEGIN for((argv0||(argv0=*argv)),argv++,argc--;\ argv[0] && argv[0][0]=='-' && argv[0][1];\ argc--, argv++) {\ char *_args, *_argt;\ int _argc;\ _args = &argv[0][1];\ if(_args[0]=='-' && _args[1]==0){\ argc--; argv++; break;\ }\ while(_argc = *_args++)\ switch(_argc) #define ARGEND SET(_argt);USED(_argt);USED(_argc);USED(_args);}USED(argv);USED(argc); #define ARGF() (_argt=_args, _args="",\ (*_argt? _argt: argv[1]? (argc--, *++argv): 0)) #define EARGF(x) (_argt=_args, _args="",\ (*_argt? _argt: argv[1]? (argc--, *++argv): ((x), abort(), (char*)0))) #define ARGC() _argc char *argv0; static int started; static void prword(char *w) { if (started) fputs(" ", stdout); else started = 1; fputs(w, stdout); } void main(int argc, char **argv) { struct utsname u; uname(&u); if(argc == 1){ printf("%s\n", u.sysname); exit(0); } ARGBEGIN { case 'a': prword(u.sysname); prword(u.nodename); prword(u.release); prword(u.version); prword(u.machine); break; case 'm': prword(u.machine); break; case 'n': prword(u.nodename); break; case 'r': prword(u.release); break; case 's': prword(u.sysname); break; case 'v': prword(u.version); break; } ARGEND printf("\n"); exit(0); } /sys/src/ape/config 664 sys sys 1367613437 573 # global include file for the APE environment < /$objtype/mkfile # gives (compiler loader extension ranliber) APEBIN=/$objtype/bin/ape # where installed ape binaries go APELIB=/rc/bin/ape # where helper programs go CC=pcc # compiler (must be ansi) LD=pcc # loader CFLAGS= # global defaults FAMILY=plan9 AR=ar # manipulating libraries RANLIB=echo # for updating libraries INSTALL=$APELIB/install # install script INSOWNER=() # default installation parameter INSGROUP=bin # default installation parameter INSMODE=775 # default installation parameter /sys/src/ape/lib 20000000775 sys sys 1368464304 0 /sys/src/ape/lib/9 20000000775 sys sys 1369861527 0 /sys/src/ape/lib/9/386 20000000775 sys sys 1367862839 0 /sys/src/ape/lib/9/386/getcallerpc.s 664 sys sys 1367613437 65 TEXT getcallerpc(SB), $0 MOVL v+0(FP), AX MOVL -4(AX), AX RET /sys/src/ape/lib/9/386/getfcr.s 664 sys sys 1367613437 256 TEXT setfcr(SB), $0 MOVL p+0(FP),AX XORB $0x3f,AX PUSHW AX WAIT FLDCW 0(SP) POPW AX RET TEXT getfcr(SB), $0 PUSHW AX WAIT FSTCW 0(SP) POPW AX XORB $0x3f,AX RET TEXT getfsr(SB), $0 WAIT FSTSW AX RET TEXT setfsr(SB), $0 WAIT FCLEX RET /sys/src/ape/lib/9/68020 20000000775 sys sys 1367862839 0 /sys/src/ape/lib/9/68020/getcallerpc.s 664 sys sys 1367613437 50 TEXT getcallerpc(SB), $0 MOVL (a+0(FP)), R0 RTS /sys/src/ape/lib/9/68020/getfcr.s 664 sys sys 1367613437 229 TEXT getfsr(SB), $0 MOVL $0, R0 MOVL FPSR, R0 RTS TEXT setfsr(SB), $0 MOVL new+0(FP), R1 MOVL R1, FPSR RTS TEXT getfcr(SB), $0 MOVL $0, R0 MOVL FPCR, R0 RTS TEXT setfcr(SB), $0 MOVL new+0(FP), R1 MOVL R1, FPCR RTS /sys/src/ape/lib/9/alpha 20000000775 sys sys 1367862839 0 /sys/src/ape/lib/9/alpha/getcallerpc.s 664 sys sys 1367613437 48 TEXT getcallerpc(SB), $-8 MOVL 0(SP), R0 RET /sys/src/ape/lib/9/alpha/getfcr.s 644 sys sys 1367613437 816 #define EXCB WORD $0x60000400 /* until 7a/7l catch up */ TEXT getfsr(SB), $8 EXCB MOVT FPCR, F0 EXCB MOVT F0, tmp-8(SP) MOVL tmp-4(SP), R1 MOVQ $0x01e00000, R2 AND R2, R1, R0 RET TEXT setfsr(SB), $8 MOVQ $0x01e00000, R2 EXCB MOVT FPCR, F0 EXCB MOVT F0, tmp-8(SP) MOVL tmp-4(SP), R1 ANDNOT R2, R1, R3 AND R2, R0, R4 OR R3, R4, R5 MOVL R5, tmp-4(SP) MOVT tmp-8(SP), F0 EXCB MOVT F0, FPCR EXCB RET TEXT getfcr(SB), $8 EXCB MOVT FPCR, F0 EXCB MOVT F0, tmp-8(SP) MOVL tmp-4(SP), R1 MOVQ $0x700c0000, R2 AND R2, R1, R0 XOR R2, R0 RET TEXT setfcr(SB), $8 MOVQ $0x700c0000, R2 XOR R2, R0 EXCB MOVT FPCR, F0 EXCB MOVT F0, tmp-8(SP) MOVL tmp-4(SP), R1 ANDNOT R2, R1, R3 AND R2, R0, R4 OR R3, R4, R5 MOVL R5, tmp-4(SP) MOVT tmp-8(SP), F0 EXCB MOVT F0, FPCR EXCB RET /sys/src/ape/lib/9/amd64 20000000775 sys sys 1367862839 0 /sys/src/ape/lib/9/amd64/getcallerpc.s 664 sys sys 1367613437 49 TEXT getcallerpc(SB), $0 MOVQ -8(RARG), AX RET /sys/src/ape/lib/9/amd64/getfcr.s 664 sys sys 1367613437 584 TEXT setfcr(SB), $4 XORL $(0x3F<<7),RARG /* bits are cleared in csr to enable them */ ANDL $0xFFC0, RARG /* just the fcr bits */ WAIT /* is this needed? */ STMXCSR 0(SP) MOVL 0(SP), AX ANDL $~0x3F, AX ORL RARG, AX MOVL AX, 0(SP) LDMXCSR 0(SP) RET TEXT getfcr(SB), $4 WAIT STMXCSR 0(SP) MOVWLZX 0(SP), AX ANDL $0xFFC0, AX XORL $(0x3F<<7),AX RET TEXT getfsr(SB), $4 WAIT STMXCSR 0(SP) MOVL 0(SP), AX ANDL $0x3F, AX RET TEXT setfsr(SB), $4 ANDL $0x3F, RARG WAIT STMXCSR 0(SP) MOVL 0(SP), AX ANDL $~0x3F, AX ORL RARG, AX MOVL AX, 0(SP) LDMXCSR 0(SP) RET /sys/src/ape/lib/9/arm 20000000775 sys sys 1367862839 0 /sys/src/ape/lib/9/arm/getcallerpc.s 664 sys sys 1367613437 48 TEXT getcallerpc(SB), $-4 MOVW 0(R13), R0 RET /sys/src/ape/lib/9/arm/getfcr.s 664 sys sys 1367613437 164 TEXT setfcr(SB), $4 MOVW R0, FPCR RET TEXT getfcr(SB), $4 MOVW FPCR, R0 RET TEXT getfsr(SB), $0 MOVW FPSR, R0 RET TEXT setfsr(SB), $0 MOVW R0, FPSR RET /sys/src/ape/lib/9/bind.c 664 sys sys 1367613437 135 #include extern int _BIND(char*, char*, int); int bind(char *name, char *old, int flag) { return _BIND(name, old, flag); } /sys/src/ape/lib/9/errstr.c 664 sys sys 1367613437 134 #include extern int _ERRSTR(char*, unsigned int); int errstr(char *err, unsigned int nerr) { return _ERRSTR(err, nerr); } /sys/src/ape/lib/9/libc.h 664 bootes sys 1369844459 4805 #ifndef _LIBC_H_ #define _LIBC_H_ 1 #define _LOCK_EXTENSION #define _QLOCK_EXTENSION #define _BSD_EXTENSION #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define nelem(x) (sizeof(x)/sizeof((x)[0])) extern int tokenize(char*, char**, int); typedef struct Qid { uvlong path; ulong vers; uchar type; } Qid; typedef struct Dir { /* system-modified data */ ushort type; /* server type */ uint dev; /* server subtype */ /* file data */ Qid qid; /* unique id from server */ ulong mode; /* permissions */ ulong atime; /* last read time */ ulong mtime; /* last write time */ vlong length; /* file length: see */ char *name; /* last element of path */ char *uid; /* owner name */ char *gid; /* group name */ char *muid; /* last modifier name */ } Dir; uint _convM2D(uchar*, uint, Dir*, char*); uint _convD2M(Dir*, uchar*, uint); Dir *_dirstat(char*); int _dirwstat(char*, Dir*); Dir *_dirfstat(int); int _dirfwstat(int, Dir*); long _dirread(int, Dir**); long _dirreadall(int, Dir**); void _nulldir(Dir*); uint _sizeD2M(Dir*); typedef struct Waitmsg { int pid; /* of loved one */ unsigned long time[3]; /* of loved one & descendants */ char *msg; } Waitmsg; extern int _AWAIT(char*, int); extern int _ALARM(unsigned long); extern int _BIND(const char*, const char*, int); extern int _CHDIR(const char*); extern int _CLOSE(int); extern int _CREATE(char*, int, unsigned long); extern int _DUP(int, int); extern int _ERRSTR(char*, unsigned int); extern int _EXEC(char*, char*[]); extern void _EXITS(char *); extern int _FD2PATH(int, char*, int); extern int _FAUTH(int, char*); extern int _FSESSION(int, char*, int); extern int _FSTAT(int, unsigned char*, int); extern int _FWSTAT(int, unsigned char*, int); extern int _MOUNT(int, int, const char*, int, const char*); extern int _NOTED(int); extern int _NOTIFY(int(*)(void*, char*)); extern int _OPEN(const char*, int); extern int _PIPE(int*); extern long _PREAD(int, void*, long, long long); extern long _PWRITE(int, void*, long, long long); extern long _READ(int, void*, long); extern int _REMOVE(const char*); extern void* _RENDEZVOUS(void*, void*); extern int _RFORK(int); extern void* _SEGATTACH(int, char*, void*, unsigned long); extern void* _SEGBRK(void*, void*); extern int _SEGDETACH(void*); extern int _SEGFLUSH(void*, unsigned long); extern int _SEGFREE(void*, unsigned long); extern long long _SEEK(int, long long, int); extern int _SLEEP(long); extern int _STAT(const char*, unsigned char*, int); extern Waitmsg* _WAIT(void); extern long _WRITE(int, const void*, long); extern int _WSTAT(const char*, unsigned char*, int); extern long _READN(int, void*, long); extern int _IOUNIT(int); extern void *_MALLOCZ(int, int); /* not a syscall */ #define dirstat _dirstat #define dirfstat _dirfstat #define OREAD 0 #define OWRITE 1 #define ORDWR 2 #define OEXEC 3 /* execute, == read but check execute permission */ #define OTRUNC 16 /* or'ed in (except for exec), truncate file first */ #define OCEXEC 32 /* or'ed in, close on exec */ #define ORCLOSE 64 /* or'ed in, remove on close */ #define OEXCL 0x1000 /* or'ed in, exclusive use (create only) */ #define AREAD 4 #define AWRITE 2 #define AEXEC 1 #define AEXIST 0 #ifdef thisisallgoingtoendintears #define open _OPEN #define close _CLOSE #define read _READ #define write _WRITE #define create _CREATE #define pread _PREAD #define seek _SEEK #endif /* we don't have fauth(), so let this slide */ #define seek(fd, off, dir) lseek(fd, off, dir) #define fauth _FAUTH /* neither iounit */ #define iounit _IOUNIT #define wait _WAIT #define create(file, omode, perm) open(file, (omode) |O_CREAT | O_TRUNC, perm) #define readn _READN #define mallocz _MALLOCZ #define _exits(s) _exit(s && *(char*)s ? 1 : 0) #define exits(s) exit(s && *(char*)s ? 1 : 0) /* assume being called as in event.c */ #define postnote(x, pid, msg) kill(pid, SIGTERM) #define atnotify(x, y) signal(SIGTERM, NULL) #define ERRMAX 128 extern void setmalloctag(void*, ulong); extern ulong getcallerpc(void*); /* Used in libsec.h and not picked up in earlier type definitions */ typedef unsigned int u32int; typedef unsigned long long u64int; int dec16(uchar *, int, char *, int); int enc16(char *, int, uchar *, int); int dec32(uchar *, int, char *, int); int enc32(char *, int, uchar *, int); int dec64(uchar *, int, char *, int); int enc64(char *, int, uchar *, int); int decrypt(void*, void*, int); int encrypt(void*, void*, int); extern vlong nsec(void); extern void sysfatal(char*, ...); extern ulong truerand(void); /* uses /dev/random */ #endif /* _LIBC_H_ */ /sys/src/ape/lib/9/mips 20000000775 sys sys 1367862839 0 /sys/src/ape/lib/9/mips/getcallerpc.s 664 sys sys 1367613437 47 TEXT getcallerpc(SB), $0 MOVW 0(SP), R1 RET /sys/src/ape/lib/9/mips/getfcr.s 664 sys sys 1367613437 167 TEXT getfsr(SB), $0 MOVW FCR31, R1 RET TEXT setfsr(SB), $0 MOVW R1, FCR31 RET TEXT getfcr(SB), $0 MOVW FCR31, R1 RET TEXT setfcr(SB), $0 MOVW R1, FCR31 RET /sys/src/ape/lib/9/mkfile 664 sys sys 1369839295 1080 APE=/sys/src/ape <$APE/config LIB=/$objtype/lib/ape/lib9.a OFILES=errstr.$O\ # bind.$O\ crypt.$O\ getcallerpc.$O\ getfcr.$O\ mount.$O\ nsec.$O\ rendezvous.$O\ rfork.$O\ segattach.$O\ segdetach.$O\ segflush.$O\ segfree.$O\ setmalloctag.$O\ sysfatal.$O\ tokenize.$O\ truerand.$O\ u16.$O\ u32.$O\ u64.$O\ unmount.$O\ # wait.$O\ extern int _MOUNT(int, int, char*, int, char*); int mount(int fd, int afd, char *old, int flag, char *aname) { return _MOUNT(fd, afd, old, flag, aname); } /sys/src/ape/lib/9/nsec.c 664 bootes sys 1369185886 396 #include #include "libc.h" static uvlong border = 0x0001020304050607ull; static uvlong getbe(uchar *t, int w) { uint i; uvlong r; r = 0; for(i = 0; i < w; i++) r = r<<8 | t[i]; return r; } vlong nsec(void) { uchar b[8]; int fd; vlong v; fd = _OPEN("/dev/bintime", OREAD); if(fd != -1 && _PREAD(fd, b, 8, 0) == 8) v = getbe(b, 8); else v = 0; _CLOSE(fd); return v; } /sys/src/ape/lib/9/power 20000000775 sys sys 1367862839 0 /sys/src/ape/lib/9/power/getcallerpc.s 664 sys sys 1367613437 51 TEXT getcallerpc(SB), $-4 MOVW 0(R1), R3 RETURN /sys/src/ape/lib/9/power/getfcr.s 664 sys sys 1367613437 352 TEXT getfcr(SB), $8 MOVFL FPSCR, F3 FMOVD F3, f-8(SP) MOVW -4(SP), R3 RETURN TEXT getfsr(SB), $8 MOVFL FPSCR, F3 FMOVD F3, f-8(SP) MOVW -4(SP), R3 RETURN TEXT setfcr(SB), $8 SYNC MOVW R3, -4(SP) FMOVD -8(SP), F3 MOVFL F3, FPSCR ISYNC RETURN TEXT setfsr(SB), $8 SYNC MOVW R3, -4(SP) FMOVD -8(SP), F3 MOVFL F3, FPSCR ISYNC RETURN /sys/src/ape/lib/9/rendezvous.c 664 sys sys 1368232723 138 #include extern void* _RENDEZVOUS(void*, void*); void* rendezvous(void* tag, void* value) { return _RENDEZVOUS(tag, value); } /sys/src/ape/lib/9/rfork.c 664 sys sys 1367613437 92 #include extern int _RFORK(int); int rfork(int flags) { return _RFORK(flags); } /sys/src/ape/lib/9/segattach.c 664 sys sys 1368232624 194 #include extern void* _SEGATTACH(int, char*, void*, unsigned long); void* segattach(int attr, char *class, void *va, unsigned long len) { return _SEGATTACH(attr, class, va, len); } /sys/src/ape/lib/9/segbrk.c 664 sys sys 1368232623 126 #include extern int _SEGBRK(void*, void*); void* segbrk(void *saddr, void *addr) { return _SEGBRK(saddr, addr); } /sys/src/ape/lib/9/segdetach.c 664 sys sys 1367613437 108 #include extern int _SEGDETACH(void *); int segdetach(void *addr) { return _SEGDETACH(addr); } /sys/src/ape/lib/9/segflush.c 664 sys sys 1367613437 138 #include extern int _SEGFLUSH(void*, unsigned long); int segflush(void *va, unsigned long len) { return _SEGFLUSH(va, len); } /sys/src/ape/lib/9/segfree.c 664 sys sys 1367613437 136 #include extern int _SEGFREE(void*, unsigned long); int segfree(void *va, unsigned long len) { return _SEGFREE(va, len); } /sys/src/ape/lib/9/setmalloctag.c 664 sys sys 1367613437 44 void setmalloctag(void*, unsigned long) { } /sys/src/ape/lib/9/sparc 20000000775 sys sys 1367862839 0 /sys/src/ape/lib/9/sparc/getcallerpc.s 664 sys sys 1364220490 49 TEXT getcallerpc(SB), $0 MOVW 0(R1), R7 RETURN /sys/src/ape/lib/9/sparc/getfcr.s 664 sys sys 1367613437 335 TEXT getfsr(SB), $0 SUB $4, R1 MOVW FSR, (R1) MOVW (R1), R7 ADD $4, R1 RETURN TEXT setfsr(SB), $0 SUB $4, R1 MOVW R7, (R1) MOVW (R1), FSR ADD $4, R1 RETURN TEXT setfcr(SB), $0 SUB $4, R1 MOVW R7, (R1) MOVW (R1), FSR ADD $4, R1 RETURN TEXT getfcr(SB), $0 SUB $4, R1 MOVW FSR, (R1) MOVW (R1), R7 ADD $4, R1 RETURN /sys/src/ape/lib/9/unmount.c 664 sys sys 1367613437 123 #include extern int _UNMOUNT(char*, char*); int unmount(char *name, char *old) { return _UNMOUNT(name, old); } /sys/src/ape/lib/9/wait.c 664 bootes sys 1369156478 547 #include #include "libc.h" Waitmsg* wait(void) { int n, l; char buf[512], *fld[5]; Waitmsg *w; n = _AWAIT(buf, sizeof buf-1); if(n < 0) return nil; buf[n] = '\0'; if(tokenize(buf, fld, nelem(fld)) != nelem(fld)){ werrstr("couldn't parse wait message"); return nil; } l = strlen(fld[4])+1; w = malloc(sizeof(Waitmsg)+l); if(w == nil) return nil; w->pid = atoi(fld[0]); w->time[0] = atoi(fld[1]); w->time[1] = atoi(fld[2]); w->time[2] = atoi(fld[3]); w->msg = (char*)&w[1]; memmove(w->msg, fld[4], l); return w; } /sys/src/ape/lib/ap 20000000775 sys sys 1369167180 0 /sys/src/ape/lib/ap/386 20000000775 sys sys 1367862839 0 /sys/src/ape/lib/ap/386/cycles.s 664 sys sys 1367613437 208 #define RDTSC BYTE $0x0F; BYTE $0x31 TEXT _cycles(SB),1,$0 /* time stamp counter; cycles since power up */ RDTSC MOVL vlong+0(FP), CX /* &vlong */ MOVL AX, 0(CX) /* lo */ MOVL DX, 4(CX) /* hi */ RET /sys/src/ape/lib/ap/386/lock.c 664 sys sys 1367613437 255 #define _LOCK_EXTENSION #include "../plan9/sys9.h" #include int tas(int*); void lock(Lock *lk) { while(tas(&lk->val)) _SLEEP(0); } int canlock(Lock *lk) { if(tas(&lk->val)) return 0; return 1; } void unlock(Lock *lk) { lk->val = 0; } /sys/src/ape/lib/ap/386/main9.s 664 sys sys 1367613437 212 TEXT _main(SB), 1, $12 CALL _envsetup(SB) MOVL inargc-4(FP), AX MOVL AX, 0(SP) LEAL inargv+0(FP), AX MOVL AX, 4(SP) MOVL environ(SB), AX MOVL AX, 8(SP) CALL main(SB) MOVL AX, 0(SP) CALL exit(SB) RET /sys/src/ape/lib/ap/386/main9p.s 664 sys sys 1367613437 771 #define NPRIVATES 16 GLOBL _tos(SB), $4 GLOBL _privates(SB), $4 GLOBL _nprivates(SB), $4 TEXT _mainp(SB), 1, $(3*4+NPRIVATES*4) /* _tos = arg */ MOVL AX, _tos(SB) LEAL 8(SP), AX MOVL AX, _privates(SB) MOVL $NPRIVATES, _nprivates(SB) /* _profmain(); */ CALL _profmain(SB) /* _tos->prof.pp = _tos->prof.next; */ MOVL _tos+0(SB),DX MOVL 4(DX),CX MOVL CX,(DX) CALL _envsetup(SB) /* main(argc, argv, environ); */ MOVL inargc-4(FP), AX MOVL AX, 0(SP) LEAL inargv+0(FP), AX MOVL AX, 4(SP) MOVL environ(SB), AX MOVL AX, 8(SP) CALL main(SB) loop: MOVL AX, 0(SP) CALL exit(SB) MOVL $_profin(SB), AX /* force loading of profile */ MOVL $0, AX JMP loop TEXT _savearg(SB), 1, $0 RET TEXT _callpc(SB), 1, $0 MOVL argp+0(FP), AX MOVL 4(AX), AX RET /sys/src/ape/lib/ap/386/memchr.s 664 sys sys 1367613437 233 TEXT memchr(SB),$0 MOVL n+8(FP), CX CMPL CX, $0 JEQ none MOVL p+0(FP), DI MOVBLZX c+4(FP), AX CLD /* * SCASB is memchr instruction */ REPN; SCASB JEQ found none: MOVL $0, AX RET found: MOVL DI, AX SUBL $1, AX RET /sys/src/ape/lib/ap/386/memcmp.s 664 sys sys 1367613437 452 TEXT memcmp(SB),$0 MOVL n+8(FP), BX CMPL BX, $0 JEQ none MOVL p1+0(FP), DI MOVL p2+4(FP), SI CLD /* * first by longs */ MOVL BX, CX SHRL $2, CX REP; CMPSL JNE found /* * then by bytes */ ANDL $3, BX MOVL BX, CX REP; CMPSB JNE found1 none: MOVL $0, AX RET /* * if long found, * back up and look by bytes */ found: MOVL $4, CX SUBL CX, DI SUBL CX, SI REP; CMPSB found1: JLS lt MOVL $-1, AX RET lt: MOVL $1, AX RET /sys/src/ape/lib/ap/386/memcpy.s 664 sys sys 1367613437 649 TEXT memcpy(SB), $0 MOVL p1+0(FP), DI MOVL p2+4(FP), SI MOVL n+8(FP), BX CMPL BX, $0 JGE ok MOVL $0, SI ok: CLD /* * check and set for backwards */ CMPL SI, DI JLS back /* * copy whole longs */ MOVL BX, CX SHRL $2, CX REP; MOVSL /* * copy the rest, by bytes */ ANDL $3, BX MOVL BX, CX REP; MOVSB MOVL p+0(FP),AX RET /* * whole thing backwards has * adjusted addresses */ back: ADDL BX, DI ADDL BX, SI SUBL $4, DI SUBL $4, SI STD /* * copy whole longs */ MOVL BX, CX SHRL $2, CX ANDL $3, BX REP; MOVSL /* * copy the rest, by bytes */ ADDL $3, DI ADDL $3, SI MOVL BX, CX REP; MOVSB MOVL p+0(FP),AX RET /sys/src/ape/lib/ap/386/memmove.s 664 sys sys 1367613437 650 TEXT memmove(SB), $0 MOVL p1+0(FP), DI MOVL p2+4(FP), SI MOVL n+8(FP), BX CMPL BX, $0 JGE ok MOVL $0, SI ok: CLD /* * check and set for backwards */ CMPL SI, DI JLS back /* * copy whole longs */ MOVL BX, CX SHRL $2, CX REP; MOVSL /* * copy the rest, by bytes */ ANDL $3, BX MOVL BX, CX REP; MOVSB MOVL p+0(FP),AX RET /* * whole thing backwards has * adjusted addresses */ back: ADDL BX, DI ADDL BX, SI SUBL $4, DI SUBL $4, SI STD /* * copy whole longs */ MOVL BX, CX SHRL $2, CX ANDL $3, BX REP; MOVSL /* * copy the rest, by bytes */ ADDL $3, DI ADDL $3, SI MOVL BX, CX REP; MOVSB MOVL p+0(FP),AX RET /sys/src/ape/lib/ap/386/memset.s 664 sys sys 1367613437 396 TEXT memset(SB),$0 CLD MOVL p+0(FP), DI MOVBLZX c+4(FP), AX MOVL n+8(FP), BX /* * if not enough bytes, just copy */ CMPL BX, $9 JLS c3 /* * build word in AX */ MOVB AL, AH MOVL AX, CX SHLL $16, CX ORL CX, AX /* * copy whole longs */ c1: MOVL BX, CX SHRL $2, CX ANDL $3, BX REP; STOSL /* * copy the rest, by bytes */ c3: MOVL BX, CX REP; STOSB ret: MOVL p+0(FP),AX RET /sys/src/ape/lib/ap/386/mkfile 664 sys sys 1367613437 347 APE=/sys/src/ape <$APE/config LIB=/$objtype/lib/ape/libap.a OFILES=\ cycles.$O\ lock.$O\ main9.$O\ main9p.$O\ memchr.$O\ memcmp.$O\ memcpy.$O\ memmove.$O\ memset.$O\ notetramp.$O\ setjmp.$O\ strcat.$O\ strchr.$O\ strcpy.$O\ strlen.$O\ tas.$O\ vlop.$O\ vlrt.$O\ #include /* A stack to hold pcs when signals nest */ #define MAXSIGSTACK 20 typedef struct Pcstack Pcstack; static struct Pcstack { int sig; void (*hdlr)(int, char*, Ureg*); unsigned long restorepc; Ureg *u; } pcstack[MAXSIGSTACK]; static int nstack = 0; static void notecont(Ureg*, char*); void _notetramp(int sig, void (*hdlr)(int, char*, Ureg*), Ureg *u) { Pcstack *p; if(nstack >= MAXSIGSTACK) _NOTED(1); /* nesting too deep; just do system default */ p = &pcstack[nstack]; p->restorepc = u->pc; p->sig = sig; p->hdlr = hdlr; p->u = u; nstack++; u->pc = (unsigned long) notecont; _NOTED(2); /* NSAVE: clear note but hold state */ } static void notecont(Ureg *u, char *s) { Pcstack *p; void(*f)(int, char*, Ureg*); p = &pcstack[nstack-1]; f = p->hdlr; u->pc = p->restorepc; nstack--; (*f)(p->sig, s, u); _NOTED(3); /* NRSTR */ } #define JMPBUFPC 1 #define JMPBUFSP 0 extern sigset_t _psigblocked; void siglongjmp(sigjmp_buf j, int ret) { struct Ureg *u; if(j[0]) _psigblocked = j[1]; if(nstack == 0 || pcstack[nstack-1].u->sp > j[2+JMPBUFSP]) longjmp(j+2, ret); u = pcstack[nstack-1].u; nstack--; u->ax = ret; if(ret == 0) u->ax = 1; u->pc = j[2+JMPBUFPC]; u->sp = j[2+JMPBUFSP] + 4; _NOTED(3); /* NRSTR */ } /sys/src/ape/lib/ap/386/setjmp.s 664 sys sys 1367613437 661 TEXT longjmp(SB), $0 MOVL r+4(FP), AX CMPL AX, $0 JNE ok /* ansi: "longjmp(0) => longjmp(1)" */ MOVL $1, AX /* bless their pointed heads */ ok: MOVL l+0(FP), BX MOVL 0(BX), SP /* restore sp */ MOVL 4(BX), BX /* put return pc on the stack */ MOVL BX, 0(SP) RET TEXT setjmp(SB), $0 MOVL l+0(FP), AX MOVL SP, 0(AX) /* store sp */ MOVL 0(SP), BX /* store return pc */ MOVL BX, 4(AX) MOVL $0, AX /* return 0 */ RET TEXT sigsetjmp(SB), $0 MOVL buf+0(FP), AX MOVL savemask+4(FP),BX MOVL BX,0(AX) MOVL $_psigblocked(SB),4(AX) MOVL SP, 8(AX) /* store sp */ MOVL 0(SP), BX /* store return pc */ MOVL BX, 12(AX) MOVL $0, AX /* return 0 */ RET /sys/src/ape/lib/ap/386/strcat.s 664 sys sys 1367613437 449 TEXT strcat(SB),$0 MOVL $0, AX MOVL $-1, CX CLD /* * find length of second string */ MOVL p2+4(FP), DI REPN; SCASB MOVL DI, BX SUBL p2+4(FP), BX /* * find end of first string */ MOVL p1+0(FP), DI REPN; SCASB /* * copy the memory */ SUBL $1, DI MOVL p2+4(FP), SI /* * copy whole longs */ MOVL BX, CX SHRL $2, CX REP; MOVSL /* * copy the rest, by bytes */ ANDL $3, BX MOVL BX, CX REP; MOVSB MOVL p1+0(FP), AX RET /sys/src/ape/lib/ap/386/strchr.s 664 sys sys 1367613437 338 TEXT strchr(SB), $0 MOVL s+0(FP), DI MOVB c+4(FP), AX CMPB AX, $0 JEQ l2 /**/ /* * char is not null */ l1: MOVB (DI), BX CMPB BX, $0 JEQ ret0 ADDL $1, DI CMPB AX, BX JNE l1 MOVL DI, AX SUBL $1, AX RET /* * char is null */ l2: MOVL $-1, CX CLD REPN; SCASB MOVL DI, AX SUBL $1, AX RET ret0: MOVL $0, AX RET /sys/src/ape/lib/ap/386/strcpy.s 664 sys sys 1367613437 382 TEXT strcpy(SB),$0 MOVL $0, AX MOVL $-1, CX CLD /* * find end of second string */ MOVL p2+4(FP), DI REPN; SCASB MOVL DI, BX SUBL p2+4(FP), BX /* * copy the memory */ MOVL p1+0(FP), DI MOVL p2+4(FP), SI /* * copy whole longs */ MOVL BX, CX SHRL $2, CX REP; MOVSL /* * copy the rest, by bytes */ ANDL $3, BX MOVL BX, CX REP; MOVSB MOVL p1+0(FP), AX RET /sys/src/ape/lib/ap/386/strlen.s 664 sys sys 1367613437 168 TEXT strlen(SB),$0 MOVL $0, AX MOVL $-1, CX CLD /* * look for end of string */ MOVL p+0(FP), DI REPN; SCASB MOVL DI, AX SUBL p+0(FP), AX SUBL $1, AX RET /sys/src/ape/lib/ap/386/tas.s 664 sys sys 1367613437 74 TEXT tas(SB),$0 MOVL $0xdeadead,AX MOVL l+0(FP),BX XCHGL AX,(BX) RET /sys/src/ape/lib/ap/386/vlop.s 664 sys sys 1367613437 648 TEXT _mulv(SB), $0 MOVL r+0(FP), CX MOVL a+4(FP), AX MULL b+12(FP) MOVL AX, 0(CX) MOVL DX, BX MOVL a+4(FP), AX MULL b+16(FP) ADDL AX, BX MOVL a+8(FP), AX MULL b+12(FP) ADDL AX, BX MOVL BX, 4(CX) RET TEXT _mul64by32(SB), $0 MOVL r+0(FP), CX MOVL a+4(FP), AX MULL b+12(FP) MOVL AX, 0(CX) MOVL DX, BX MOVL a+8(FP), AX MULL b+12(FP) ADDL AX, BX MOVL BX, 4(CX) RET TEXT _div64by32(SB), $0 MOVL r+12(FP), CX MOVL a+0(FP), AX MOVL a+4(FP), DX DIVL b+8(FP) MOVL DX, 0(CX) RET TEXT _addv(SB), $0 MOVL r+0(FP), CX MOVL a+4(FP), AX MOVL a+8(FP), BX ADDL b+12(FP), AX ADCL b+16(FP), BX MOVL AX, 0(CX) MOVL BX, 4(CX) RET /sys/src/ape/lib/ap/386/vlrt.c 664 sys sys 1367613437 8668 typedef unsigned long ulong; typedef unsigned int uint; typedef unsigned short ushort; typedef unsigned char uchar; typedef signed char schar; #define SIGN(n) (1UL<<(n-1)) typedef struct Vlong Vlong; struct Vlong { union { struct { ulong lo; ulong hi; }; struct { ushort lols; ushort loms; ushort hils; ushort hims; }; }; }; void abort(void); void _subv(Vlong *r, Vlong a, Vlong b) { ulong lo, hi; lo = a.lo - b.lo; hi = a.hi - b.hi; if(lo > a.lo) hi--; r->lo = lo; r->hi = hi; } void _d2v(Vlong *y, double d) { union { double d; struct Vlong; } x; ulong xhi, xlo, ylo, yhi; int sh; x.d = d; xhi = (x.hi & 0xfffff) | 0x100000; xlo = x.lo; sh = 1075 - ((x.hi >> 20) & 0x7ff); ylo = 0; yhi = 0; if(sh >= 0) { /* v = (hi||lo) >> sh */ if(sh < 32) { if(sh == 0) { ylo = xlo; yhi = xhi; } else { ylo = (xlo >> sh) | (xhi << (32-sh)); yhi = xhi >> sh; } } else { if(sh == 32) { ylo = xhi; } else if(sh < 64) { ylo = xhi >> (sh-32); } } } else { /* v = (hi||lo) << -sh */ sh = -sh; if(sh <= 10) { ylo = xlo << sh; yhi = (xhi << sh) | (xlo >> (32-sh)); } else { /* overflow */ yhi = d; /* causes something awful */ } } if(x.hi & SIGN(32)) { if(ylo != 0) { ylo = -ylo; yhi = ~yhi; } else yhi = -yhi; } y->hi = yhi; y->lo = ylo; } void _f2v(Vlong *y, float f) { _d2v(y, f); } double _v2d(Vlong x) { if(x.hi & SIGN(32)) { if(x.lo) { x.lo = -x.lo; x.hi = ~x.hi; } else x.hi = -x.hi; return -((long)x.hi*4294967296. + x.lo); } return (long)x.hi*4294967296. + x.lo; } float _v2f(Vlong x) { return _v2d(x); } ulong _div64by32(Vlong, ulong, ulong*); void _mul64by32(Vlong*, Vlong, ulong); static void dodiv(Vlong num, Vlong den, Vlong *qp, Vlong *rp) { ulong n; Vlong x, q, r; if(den.hi > num.hi || (den.hi == num.hi && den.lo > num.lo)){ if(qp) { qp->hi = 0; qp->lo = 0; } if(rp) { rp->hi = num.hi; rp->lo = num.lo; } return; } if(den.hi != 0){ q.hi = 0; n = num.hi/den.hi; _mul64by32(&x, den, n); if(x.hi > num.hi || (x.hi == num.hi && x.lo > num.lo)){ n--; _mul64by32(&x, den, n); } q.lo = n; _subv(&r, num, x); } else { if(num.hi >= den.lo){ q.hi = n = num.hi/den.lo; num.hi -= den.lo*n; } else { q.hi = 0; } q.lo = _div64by32(num, den.lo, &r.lo); r.hi = 0; } if(qp) { qp->lo = q.lo; qp->hi = q.hi; } if(rp) { rp->lo = r.lo; rp->hi = r.hi; } } void _divvu(Vlong *q, Vlong n, Vlong d) { if(n.hi == 0 && d.hi == 0) { q->hi = 0; q->lo = n.lo / d.lo; return; } dodiv(n, d, q, 0); } void _modvu(Vlong *r, Vlong n, Vlong d) { if(n.hi == 0 && d.hi == 0) { r->hi = 0; r->lo = n.lo % d.lo; return; } dodiv(n, d, 0, r); } static void vneg(Vlong *v) { if(v->lo == 0) { v->hi = -v->hi; return; } v->lo = -v->lo; v->hi = ~v->hi; } void _divv(Vlong *q, Vlong n, Vlong d) { long nneg, dneg; if(n.hi == (((long)n.lo)>>31) && d.hi == (((long)d.lo)>>31)) { q->lo = (long)n.lo / (long)d.lo; q->hi = ((long)q->lo) >> 31; return; } nneg = n.hi >> 31; if(nneg) vneg(&n); dneg = d.hi >> 31; if(dneg) vneg(&d); dodiv(n, d, q, 0); if(nneg != dneg) vneg(q); } void _modv(Vlong *r, Vlong n, Vlong d) { long nneg, dneg; if(n.hi == (((long)n.lo)>>31) && d.hi == (((long)d.lo)>>31)) { r->lo = (long)n.lo % (long)d.lo; r->hi = ((long)r->lo) >> 31; return; } nneg = n.hi >> 31; if(nneg) vneg(&n); dneg = d.hi >> 31; if(dneg) vneg(&d); dodiv(n, d, 0, r); if(nneg) vneg(r); } void _rshav(Vlong *r, Vlong a, int b) { long t; t = a.hi; if(b >= 32) { r->hi = t>>31; if(b >= 64) { /* this is illegal re C standard */ r->lo = t>>31; return; } r->lo = t >> (b-32); return; } if(b <= 0) { r->hi = t; r->lo = a.lo; return; } r->hi = t >> b; r->lo = (t << (32-b)) | (a.lo >> b); } void _rshlv(Vlong *r, Vlong a, int b) { ulong t; t = a.hi; if(b >= 32) { r->hi = 0; if(b >= 64) { /* this is illegal re C standard */ r->lo = 0; return; } r->lo = t >> (b-32); return; } if(b <= 0) { r->hi = t; r->lo = a.lo; return; } r->hi = t >> b; r->lo = (t << (32-b)) | (a.lo >> b); } void _lshv(Vlong *r, Vlong a, int b) { ulong t; t = a.lo; if(b >= 32) { r->lo = 0; if(b >= 64) { /* this is illegal re C standard */ r->hi = 0; return; } r->hi = t << (b-32); return; } if(b <= 0) { r->lo = t; r->hi = a.hi; return; } r->lo = t << b; r->hi = (t >> (32-b)) | (a.hi << b); } void _andv(Vlong *r, Vlong a, Vlong b) { r->hi = a.hi & b.hi; r->lo = a.lo & b.lo; } void _orv(Vlong *r, Vlong a, Vlong b) { r->hi = a.hi | b.hi; r->lo = a.lo | b.lo; } void _xorv(Vlong *r, Vlong a, Vlong b) { r->hi = a.hi ^ b.hi; r->lo = a.lo ^ b.lo; } void _vpp(Vlong *l, Vlong *r) { l->hi = r->hi; l->lo = r->lo; r->lo++; if(r->lo == 0) r->hi++; } void _vmm(Vlong *l, Vlong *r) { l->hi = r->hi; l->lo = r->lo; if(r->lo == 0) r->hi--; r->lo--; } void _ppv(Vlong *l, Vlong *r) { r->lo++; if(r->lo == 0) r->hi++; l->hi = r->hi; l->lo = r->lo; } void _mmv(Vlong *l, Vlong *r) { if(r->lo == 0) r->hi--; r->lo--; l->hi = r->hi; l->lo = r->lo; } void _vasop(Vlong *ret, void *lv, void fn(Vlong*, Vlong, Vlong), int type, Vlong rv) { Vlong t, u; u.lo = 0; u.hi = 0; switch(type) { default: abort(); break; case 1: /* schar */ t.lo = *(schar*)lv; t.hi = t.lo >> 31; fn(&u, t, rv); *(schar*)lv = u.lo; break; case 2: /* uchar */ t.lo = *(uchar*)lv; t.hi = 0; fn(&u, t, rv); *(uchar*)lv = u.lo; break; case 3: /* short */ t.lo = *(short*)lv; t.hi = t.lo >> 31; fn(&u, t, rv); *(short*)lv = u.lo; break; case 4: /* ushort */ t.lo = *(ushort*)lv; t.hi = 0; fn(&u, t, rv); *(ushort*)lv = u.lo; break; case 9: /* int */ t.lo = *(int*)lv; t.hi = t.lo >> 31; fn(&u, t, rv); *(int*)lv = u.lo; break; case 10: /* uint */ t.lo = *(uint*)lv; t.hi = 0; fn(&u, t, rv); *(uint*)lv = u.lo; break; case 5: /* long */ t.lo = *(long*)lv; t.hi = t.lo >> 31; fn(&u, t, rv); *(long*)lv = u.lo; break; case 6: /* ulong */ t.lo = *(ulong*)lv; t.hi = 0; fn(&u, t, rv); *(ulong*)lv = u.lo; break; case 7: /* vlong */ case 8: /* uvlong */ fn(&u, *(Vlong*)lv, rv); *(Vlong*)lv = u; break; } *ret = u; } void _p2v(Vlong *ret, void *p) { long t; t = (ulong)p; ret->lo = t; ret->hi = 0; } void _sl2v(Vlong *ret, long sl) { long t; t = sl; ret->lo = t; ret->hi = t >> 31; } void _ul2v(Vlong *ret, ulong ul) { long t; t = ul; ret->lo = t; ret->hi = 0; } void _si2v(Vlong *ret, int si) { long t; t = si; ret->lo = t; ret->hi = t >> 31; } void _ui2v(Vlong *ret, uint ui) { long t; t = ui; ret->lo = t; ret->hi = 0; } void _sh2v(Vlong *ret, long sh) { long t; t = (sh << 16) >> 16; ret->lo = t; ret->hi = t >> 31; } void _uh2v(Vlong *ret, ulong ul) { long t; t = ul & 0xffff; ret->lo = t; ret->hi = 0; } void _sc2v(Vlong *ret, long uc) { long t; t = (uc << 24) >> 24; ret->lo = t; ret->hi = t >> 31; } void _uc2v(Vlong *ret, ulong ul) { long t; t = ul & 0xff; ret->lo = t; ret->hi = 0; } long _v2sc(Vlong rv) { long t; t = rv.lo & 0xff; return (t << 24) >> 24; } long _v2uc(Vlong rv) { return rv.lo & 0xff; } long _v2sh(Vlong rv) { long t; t = rv.lo & 0xffff; return (t << 16) >> 16; } long _v2uh(Vlong rv) { return rv.lo & 0xffff; } long _v2sl(Vlong rv) { return rv.lo; } long _v2ul(Vlong rv) { return rv.lo; } long _v2si(Vlong rv) { return rv.lo; } long _v2ui(Vlong rv) { return rv.lo; } int _testv(Vlong rv) { return rv.lo || rv.hi; } int _eqv(Vlong lv, Vlong rv) { return lv.lo == rv.lo && lv.hi == rv.hi; } int _nev(Vlong lv, Vlong rv) { return lv.lo != rv.lo || lv.hi != rv.hi; } int _ltv(Vlong lv, Vlong rv) { return (long)lv.hi < (long)rv.hi || (lv.hi == rv.hi && lv.lo < rv.lo); } int _lev(Vlong lv, Vlong rv) { return (long)lv.hi < (long)rv.hi || (lv.hi == rv.hi && lv.lo <= rv.lo); } int _gtv(Vlong lv, Vlong rv) { return (long)lv.hi > (long)rv.hi || (lv.hi == rv.hi && lv.lo > rv.lo); } int _gev(Vlong lv, Vlong rv) { return (long)lv.hi > (long)rv.hi || (lv.hi == rv.hi && lv.lo >= rv.lo); } int _lov(Vlong lv, Vlong rv) { return lv.hi < rv.hi || (lv.hi == rv.hi && lv.lo < rv.lo); } int _lsv(Vlong lv, Vlong rv) { return lv.hi < rv.hi || (lv.hi == rv.hi && lv.lo <= rv.lo); } int _hiv(Vlong lv, Vlong rv) { return lv.hi > rv.hi || (lv.hi == rv.hi && lv.lo > rv.lo); } int _hsv(Vlong lv, Vlong rv) { return lv.hi > rv.hi || (lv.hi == rv.hi && lv.lo >= rv.lo); } /sys/src/ape/lib/ap/68020 20000000775 sys sys 1367862839 0 /sys/src/ape/lib/ap/68020/cycles.c 664 sys sys 1367613437 49 void _cycles(unsigned long long *u) { *u = 0; } /sys/src/ape/lib/ap/68020/lock.c 664 sys sys 1367613437 255 #define _LOCK_EXTENSION #include "../plan9/sys9.h" #include int tas(int*); void lock(Lock *lk) { while(tas(&lk->val)) _SLEEP(0); } int canlock(Lock *lk) { if(tas(&lk->val)) return 0; return 1; } void unlock(Lock *lk) { lk->val = 0; } /sys/src/ape/lib/ap/68020/main9.s 664 sys sys 1367613437 152 TEXT _main(SB), 1, $16 MOVL $a6base(SB), A6 PEA inargv+0(FP) MOVL inargc-4(FP), TOS BSR _envsetup(SB) BSR main(SB) MOVL R0,TOS BSR exit(SB) RTS /sys/src/ape/lib/ap/68020/main9p.s 664 sys sys 1367613437 739 #define NPRIVATES 16 GLOBL _tos(SB), $4 GLOBL _privates(SB), $4 GLOBL _nprivates(SB), $4 TEXT _mainp(SB), 1, $(3*4+NPRIVATES*4) MOVL $a6base(SB), A6 /* _tos = arg */ MOVL R0, _tos(SB) /* return value of sys exec!! */ LEA private+8(SP), _privates(SB) MOVL $NPRIVATES, _nprivates(SB) /* _profmain(); */ BSR _profmain(SB) /* _tos->prof.pp = _tos->prof.next; */ MOVL _tos+0(SB),A1 MOVL 4(A1),(A1) BSR _envsetup(SB) /* main(argc, argv, environ); */ MOVL environ(SB), TOS PEA inargv+0(FP) MOVL inargc-4(FP), TOS BSR main(SB) loop: MOVL R0,TOS BSR exit(SB) LEA _profin(SB), A0 /* force loading of profile */ BRA loop TEXT _savearg(SB), 1, $0 RTS TEXT _callpc(SB), 1, $0 MOVL argp+0(FP), A0 MOVL 4(A0), R0 RTS /sys/src/ape/lib/ap/68020/memchr.s 664 sys sys 1367613437 170 TEXT memchr(SB),$0 MOVL n+8(FP),R0 BEQ ret MOVL s1+0(FP),A1 MOVL c+4(FP),R1 l1: CMPB R1,(A1)+ BEQ eq SUBL $1,R0 BNE l1 RTS eq: MOVL A1,R0 SUBL $1,R0 ret: RTS /sys/src/ape/lib/ap/68020/memcmp.s 664 sys sys 1367613437 196 TEXT memcmp(SB),$0 MOVL n+8(FP),R0 BEQ ret MOVL s1+0(FP),A2 MOVL s2+4(FP),A1 l1: CMPB (A1)+,(A2)+ BNE neq SUBL $1,R0 BNE l1 RTS neq: BCS gtr MOVL $-1,R0 RTS gtr: MOVL $1,R0 ret: RTS /sys/src/ape/lib/ap/68020/memcpy.s 664 sys sys 1367613437 1209 TEXT memcpy(SB), $0 MOVL n+8(FP),R0 BEQ return BGT ok MOVL 0, R0 ok: MOVL s1+0(FP),A2 MOVL s2+4(FP),A1 CMPL A2,A1 BHI back /* * speed depends on source allignment * destination allignment is secondary * byte-at-a-time foreward copy to * get source (A1) alligned. */ f1: MOVL A1, R1 ANDL $3, R1 BEQ f2 SUBL $1, R0 BLT return MOVB (A1)+, (A2)+ BRA f1 /* * quad-long-at-a-time forward copy */ f2: SUBL $16, R0 BLT f3 MOVL (A1)+, (A2)+ MOVL (A1)+, (A2)+ MOVL (A1)+, (A2)+ MOVL (A1)+, (A2)+ BRA f2 /* * cleanup byte-at-a-time */ f3: ADDL $15, R0 BLT return f4: MOVB (A1)+, (A2)+ SUBL $1, R0 BGE f4 BRA return return: MOVL s1+0(FP),R0 RTS /* * everything the same, but * copy backwards */ back: ADDL R0, A1 ADDL R0, A2 /* * byte-at-a-time backward copy to * get source (A1) alligned. */ b1: MOVL A1, R1 ANDL $3, R1 BEQ b2 SUBL $1, R0 BLT return MOVB -(A1), -(A2) BRA b1 /* * quad-long-at-a-time backward copy */ b2: SUBL $16, R0 BLT b3 MOVL -(A1), -(A2) MOVL -(A1), -(A2) MOVL -(A1), -(A2) MOVL -(A1), -(A2) BRA b2 /* * cleanup byte-at-a-time backward */ b3: ADDL $15, R0 BLT return b4: MOVB -(A1), -(A2) SUBL $1, R0 BGE b4 BRA return /sys/src/ape/lib/ap/68020/memmove.s 664 sys sys 1367613437 1216 TEXT memmove(SB), $0 move: MOVL n+8(FP),R0 BEQ return BGT ok MOVL 0, R0 ok: MOVL s1+0(FP),A2 MOVL s2+4(FP),A1 CMPL A2,A1 BHI back /* * speed depends on source allignment * destination allignment is secondary * byte-at-a-time foreward copy to * get source (A1) alligned. */ f1: MOVL A1, R1 ANDL $3, R1 BEQ f2 SUBL $1, R0 BLT return MOVB (A1)+, (A2)+ BRA f1 /* * quad-long-at-a-time forward copy */ f2: SUBL $16, R0 BLT f3 MOVL (A1)+, (A2)+ MOVL (A1)+, (A2)+ MOVL (A1)+, (A2)+ MOVL (A1)+, (A2)+ BRA f2 /* * cleanup byte-at-a-time */ f3: ADDL $15, R0 BLT return f4: MOVB (A1)+, (A2)+ SUBL $1, R0 BGE f4 BRA return return: MOVL s1+0(FP),R0 RTS /* * everything the same, but * copy backwards */ back: ADDL R0, A1 ADDL R0, A2 /* * byte-at-a-time backward copy to * get source (A1) alligned. */ b1: MOVL A1, R1 ANDL $3, R1 BEQ b2 SUBL $1, R0 BLT return MOVB -(A1), -(A2) BRA b1 /* * quad-long-at-a-time backward copy */ b2: SUBL $16, R0 BLT b3 MOVL -(A1), -(A2) MOVL -(A1), -(A2) MOVL -(A1), -(A2) MOVL -(A1), -(A2) BRA b2 /* * cleanup byte-at-a-time backward */ b3: ADDL $15, R0 BLT return b4: MOVB -(A1), -(A2) SUBL $1, R0 BGE b4 BRA return /sys/src/ape/lib/ap/68020/memset.s 664 sys sys 1367613437 558 TEXT memset(SB), $0 MOVL n+8(FP), R0 BLE return MOVL s1+0(FP), A1 CLRL R1 MOVB c+7(FP), R1 BEQ l1 /* * create 4 replicated copies * of the byte in R1 */ MOVL R1, R2 ASLL $8, R2 ORL R2, R1 MOVL R1, R2 SWAP R2 ORL R2, R1 /* * quad-long-at-a-time set * destination allignment is not * very important. */ l1: SUBL $16, R0 BLT l2 MOVL R1, (A1)+ MOVL R1, (A1)+ MOVL R1, (A1)+ MOVL R1, (A1)+ BRA l1 /* * cleanup byte-at-a-time */ l2: ADDL $15, R0 BLT return l3: MOVB R1, (A1)+ SUBL $1, R0 BGE l3 return: MOVL s1+0(FP),R0 RTS /sys/src/ape/lib/ap/68020/mkfile 664 sys sys 1367613437 359 APE=/sys/src/ape <$APE/config LIB=/$objtype/lib/ape/libap.a OFILES=\ cycles.$O\ lock.$O\ main9.$O\ main9p.$O\ memchr.$O\ memcmp.$O\ memcpy.$O\ memmove.$O\ memset.$O\ notetramp.$O\ setjmp.$O\ strcat.$O\ strchr.$O\ strcmp.$O\ strcpy.$O\ strlen.$O\ tas.$O\ vlop.$O\ vlrt.$O\ #include /* A stack to hold pcs when signals nest */ #define MAXSIGSTACK 20 typedef struct Pcstack Pcstack; static struct Pcstack { int sig; void (*hdlr)(int, char*, Ureg*); unsigned long restorepc; Ureg *u; } pcstack[MAXSIGSTACK]; static int nstack = 0; static void notecont(Ureg*, char*); void _notetramp(int sig, void (*hdlr)(int, char*, Ureg*), Ureg *u) { Pcstack *p; if(nstack >= MAXSIGSTACK) _NOTED(1); /* nesting too deep; just do system default */ p = &pcstack[nstack]; p->restorepc = u->pc; p->sig = sig; p->hdlr = hdlr; p->u = u; nstack++; u->pc = (unsigned long) notecont; _NOTED(2); /* NSAVE: clear note but hold state */ } static void notecont(Ureg *u, char *s) { Pcstack *p; void(*f)(int, char*, Ureg*); p = &pcstack[nstack-1]; f = p->hdlr; u->pc = p->restorepc; nstack--; (*f)(p->sig, s, u); _NOTED(3); /* NRSTR */ } #define JMPBUFPC 1 #define JMPBUFSP 0 extern sigset_t _psigblocked; void siglongjmp(sigjmp_buf j, int ret) { struct Ureg *u; if(j[0]) _psigblocked = j[1]; if(nstack == 0 || pcstack[nstack-1].u->sp > j[2+JMPBUFSP]) longjmp(j+2, ret); u = pcstack[nstack-1].u; nstack--; u->r0 = ret; if(ret == 0) u->r0 = 1; u->pc = j[2+JMPBUFPC]; u->sp = j[2+JMPBUFSP] + 4; _NOTED(3); /* NRSTR */ } /sys/src/ape/lib/ap/68020/setjmp.s 664 sys sys 1367613437 461 TEXT setjmp(SB), 1, $0 MOVL b+0(FP), A0 MOVL A7, (A0)+ MOVL (A7), (A0) CLRL R0 RTS TEXT sigsetjmp(SB), 1, $0 MOVL b+0(FP), A0 MOVW savemask+4(FP), R1 MOVW R1, (A0)+ MOVW $_psigblocked(SB), R1 MOVW R1, (A0)+ MOVL A7, (A0)+ MOVL (A7), (A0) CLRL R0 RTS TEXT longjmp(SB), 1, $0 MOVL b+0(FP), A0 MOVL r+4(FP), R0 BNE ok /* ansi: "longjmp(0) => longjmp(1)" */ MOVL $1, R0 /* bless their pointed heads */ ok: MOVL (A0)+, A7 MOVL (A0), (A7) RTS /sys/src/ape/lib/ap/68020/strcat.s 664 sys sys 1367613437 175 TEXT strcat(SB), $0 MOVL s1+0(FP), A2 MOVL s2+4(FP), A1 l1: TSTB (A2)+ BNE l1 MOVB (A1)+, -1(A2) BEQ done l2: MOVB (A1)+, (A2)+ BNE l2 done: MOVL s1+0(FP), R0 RTS /sys/src/ape/lib/ap/68020/strchr.s 664 sys sys 1367613437 232 TEXT strchr(SB), $0 MOVL s+0(FP), A0 MOVB c+7(FP), R2 BEQ null l: MOVB (A0)+, R1 BEQ out CMPB R1, R2 BNE l MOVL A0, R0 ADDL $-1, R0 RTS out: CLRL R0 RTS null: TSTB (A0)+ BNE null MOVL A0, R0 ADDL $-1, R0 RTS /sys/src/ape/lib/ap/68020/strcmp.s 664 sys sys 1367613437 203 TEXT strcmp(SB), $0 MOVL s1+0(FP), A2 MOVL s2+4(FP), A1 l1: MOVB (A1)+, R0 BEQ end CMPB R0, (A2)+ BEQ l1 BCS gtr MOVL $-1, R0 RTS gtr: MOVL $1, R0 RTS end: TSTB (A2) BNE gtr CLRL R0 RTS /sys/src/ape/lib/ap/68020/strcpy.s 664 sys sys 1367613437 116 TEXT strcpy(SB), $0 MOVL s1+0(FP), A2 MOVL s2+4(FP), A1 l1: MOVB (A1)+, (A2)+ BNE l1 MOVL s1+0(FP), R0 RTS /sys/src/ape/lib/ap/68020/strlen.s 664 sys sys 1367613437 157 TEXT strlen(SB), $0 MOVL s+0(FP), A1 TSTB (A1)+ BEQ null MOVL A1, A2 l1: TSTB (A1)+ BNE l1 SUBL A2, A1 MOVL A1, R0 RTS null: MOVL $0, R0 RTS /sys/src/ape/lib/ap/68020/tas.s 664 sys sys 1367613437 95 TEXT tas(SB), $0 MOVL $0, R0 MOVL a+0(FP), A0 TAS (A0) BEQ tas_1 MOVL $1, R0 tas_1: RTS /sys/src/ape/lib/ap/68020/vlop.s 664 sys sys 1367613437 295 TEXT _mulv(SB), $0 MOVL r+0(FP), A0 MOVL a+8(FP), R0 WORD $0x4c2f WORD $0x0401 WORD $0x0014 /* * MULUL b+16(FP), R0:R1 * philw made me do it! */ MOVL a+4(FP), R2 MULUL b+16(FP), R2 ADDL R2, R1 MOVL a+8(FP), R2 MULUL b+12(FP), R2 ADDL R2, R1 MOVL R1, (A0)+ MOVL R0, (A0) RTS /sys/src/ape/lib/ap/68020/vlrt.c 664 sys sys 1367613437 9109 typedef unsigned long ulong; typedef unsigned int uint; typedef unsigned short ushort; typedef unsigned char uchar; typedef signed char schar; #define SIGN(n) (1UL<<(n-1)) typedef struct Vlong Vlong; struct Vlong { union { struct { ulong hi; ulong lo; }; struct { ushort hims; ushort hils; ushort loms; ushort lols; }; }; }; void abort(void); void _addv(Vlong *r, Vlong a, Vlong b) { ulong lo, hi; lo = a.lo + b.lo; hi = a.hi + b.hi; if(lo < a.lo) hi++; r->lo = lo; r->hi = hi; } void _subv(Vlong *r, Vlong a, Vlong b) { ulong lo, hi; lo = a.lo - b.lo; hi = a.hi - b.hi; if(lo > a.lo) hi--; r->lo = lo; r->hi = hi; } void _d2v(Vlong *y, double d) { union { double d; struct Vlong; } x; ulong xhi, xlo, ylo, yhi; int sh; x.d = d; xhi = (x.hi & 0xfffff) | 0x100000; xlo = x.lo; sh = 1075 - ((x.hi >> 20) & 0x7ff); ylo = 0; yhi = 0; if(sh >= 0) { /* v = (hi||lo) >> sh */ if(sh < 32) { if(sh == 0) { ylo = xlo; yhi = xhi; } else { ylo = (xlo >> sh) | (xhi << (32-sh)); yhi = xhi >> sh; } } else { if(sh == 32) { ylo = xhi; } else if(sh < 64) { ylo = xhi >> (sh-32); } } } else { /* v = (hi||lo) << -sh */ sh = -sh; if(sh <= 10) { ylo = xlo << sh; yhi = (xhi << sh) | (xlo >> (32-sh)); } else { /* overflow */ yhi = d; /* causes something awful */ } } if(x.hi & SIGN(32)) { if(ylo != 0) { ylo = -ylo; yhi = ~yhi; } else yhi = -yhi; } y->hi = yhi; y->lo = ylo; } void _f2v(Vlong *y, float f) { _d2v(y, f); } double _v2d(Vlong x) { if(x.hi & SIGN(32)) { if(x.lo) { x.lo = -x.lo; x.hi = ~x.hi; } else x.hi = -x.hi; return -((long)x.hi*4294967296. + x.lo); } return (long)x.hi*4294967296. + x.lo; } float _v2f(Vlong x) { return _v2d(x); } static void dodiv(Vlong num, Vlong den, Vlong *q, Vlong *r) { ulong numlo, numhi, denhi, denlo, quohi, quolo, t; int i; numhi = num.hi; numlo = num.lo; denhi = den.hi; denlo = den.lo; /* * get a divide by zero */ if(denlo==0 && denhi==0) { numlo = numlo / denlo; } /* * set up the divisor and find the number of iterations needed */ if(numhi >= SIGN(32)) { quohi = SIGN(32); quolo = 0; } else { quohi = numhi; quolo = numlo; } i = 0; while(denhi < quohi || (denhi == quohi && denlo < quolo)) { denhi = (denhi<<1) | (denlo>>31); denlo <<= 1; i++; } quohi = 0; quolo = 0; for(; i >= 0; i--) { quohi = (quohi<<1) | (quolo>>31); quolo <<= 1; if(numhi > denhi || (numhi == denhi && numlo >= denlo)) { t = numlo; numlo -= denlo; if(numlo > t) numhi--; numhi -= denhi; quolo |= 1; } denlo = (denlo>>1) | (denhi<<31); denhi >>= 1; } if(q) { q->lo = quolo; q->hi = quohi; } if(r) { r->lo = numlo; r->hi = numhi; } } void _divvu(Vlong *q, Vlong n, Vlong d) { if(n.hi == 0 && d.hi == 0) { q->hi = 0; q->lo = n.lo / d.lo; return; } dodiv(n, d, q, 0); } void _modvu(Vlong *r, Vlong n, Vlong d) { if(n.hi == 0 && d.hi == 0) { r->hi = 0; r->lo = n.lo % d.lo; return; } dodiv(n, d, 0, r); } static void vneg(Vlong *v) { if(v->lo == 0) { v->hi = -v->hi; return; } v->lo = -v->lo; v->hi = ~v->hi; } void _divv(Vlong *q, Vlong n, Vlong d) { long nneg, dneg; if(n.hi == (((long)n.lo)>>31) && d.hi == (((long)d.lo)>>31)) { q->lo = (long)n.lo / (long)d.lo; q->hi = ((long)q->lo) >> 31; return; } nneg = n.hi >> 31; if(nneg) vneg(&n); dneg = d.hi >> 31; if(dneg) vneg(&d); dodiv(n, d, q, 0); if(nneg != dneg) vneg(q); } void _modv(Vlong *r, Vlong n, Vlong d) { long nneg, dneg; if(n.hi == (((long)n.lo)>>31) && d.hi == (((long)d.lo)>>31)) { r->lo = (long)n.lo % (long)d.lo; r->hi = ((long)r->lo) >> 31; return; } nneg = n.hi >> 31; if(nneg) vneg(&n); dneg = d.hi >> 31; if(dneg) vneg(&d); dodiv(n, d, 0, r); if(nneg) vneg(r); } void _rshav(Vlong *r, Vlong a, int b) { long t; t = a.hi; if(b >= 32) { r->hi = t>>31; if(b >= 64) { /* this is illegal re C standard */ r->lo = t>>31; return; } r->lo = t >> (b-32); return; } if(b <= 0) { r->hi = t; r->lo = a.lo; return; } r->hi = t >> b; r->lo = (t << (32-b)) | (a.lo >> b); } void _rshlv(Vlong *r, Vlong a, int b) { ulong t; t = a.hi; if(b >= 32) { r->hi = 0; if(b >= 64) { /* this is illegal re C standard */ r->lo = 0; return; } r->lo = t >> (b-32); return; } if(b <= 0) { r->hi = t; r->lo = a.lo; return; } r->hi = t >> b; r->lo = (t << (32-b)) | (a.lo >> b); } void _lshv(Vlong *r, Vlong a, int b) { ulong t; t = a.lo; if(b >= 32) { r->lo = 0; if(b >= 64) { /* this is illegal re C standard */ r->hi = 0; return; } r->hi = t << (b-32); return; } if(b <= 0) { r->lo = t; r->hi = a.hi; return; } r->lo = t << b; r->hi = (t >> (32-b)) | (a.hi << b); } void _andv(Vlong *r, Vlong a, Vlong b) { r->hi = a.hi & b.hi; r->lo = a.lo & b.lo; } void _orv(Vlong *r, Vlong a, Vlong b) { r->hi = a.hi | b.hi; r->lo = a.lo | b.lo; } void _xorv(Vlong *r, Vlong a, Vlong b) { r->hi = a.hi ^ b.hi; r->lo = a.lo ^ b.lo; } void _negv(Vlong *r, Vlong a) { if(a.lo == 0) { r->hi = -a.hi; r->lo = 0; return; } r->hi = ~a.hi; r->lo = -a.lo; } void _vpp(Vlong *l, Vlong *r) { l->hi = r->hi; l->lo = r->lo; r->lo++; if(r->lo == 0) r->hi++; } void _vmm(Vlong *l, Vlong *r) { l->hi = r->hi; l->lo = r->lo; if(r->lo == 0) r->hi--; r->lo--; } void _ppv(Vlong *l, Vlong *r) { r->lo++; if(r->lo == 0) r->hi++; l->hi = r->hi; l->lo = r->lo; } void _mmv(Vlong *l, Vlong *r) { if(r->lo == 0) r->hi--; r->lo--; l->hi = r->hi; l->lo = r->lo; } void _vasop(Vlong *ret, void *lv, void fn(Vlong*, Vlong, Vlong), int type, Vlong rv) { Vlong t, u; u = *ret; switch(type) { default: abort(); break; case 1: /* schar */ t.lo = *(schar*)lv; t.hi = t.lo >> 31; fn(&u, t, rv); *(schar*)lv = u.lo; break; case 2: /* uchar */ t.lo = *(uchar*)lv; t.hi = 0; fn(&u, t, rv); *(uchar*)lv = u.lo; break; case 3: /* short */ t.lo = *(short*)lv; t.hi = t.lo >> 31; fn(&u, t, rv); *(short*)lv = u.lo; break; case 4: /* ushort */ t.lo = *(ushort*)lv; t.hi = 0; fn(&u, t, rv); *(ushort*)lv = u.lo; break; case 9: /* int */ t.lo = *(int*)lv; t.hi = t.lo >> 31; fn(&u, t, rv); *(int*)lv = u.lo; break; case 10: /* uint */ t.lo = *(uint*)lv; t.hi = 0; fn(&u, t, rv); *(uint*)lv = u.lo; break; case 5: /* long */ t.lo = *(long*)lv; t.hi = t.lo >> 31; fn(&u, t, rv); *(long*)lv = u.lo; break; case 6: /* ulong */ t.lo = *(ulong*)lv; t.hi = 0; fn(&u, t, rv); *(ulong*)lv = u.lo; break; case 7: /* vlong */ case 8: /* uvlong */ fn(&u, *(Vlong*)lv, rv); *(Vlong*)lv = u; break; } *ret = u; } void _p2v(Vlong *ret, void *p) { long t; t = (ulong)p; ret->lo = t; ret->hi = 0; } void _sl2v(Vlong *ret, long sl) { long t; t = sl; ret->lo = t; ret->hi = t >> 31; } void _ul2v(Vlong *ret, ulong ul) { long t; t = ul; ret->lo = t; ret->hi = 0; } void _si2v(Vlong *ret, int si) { long t; t = si; ret->lo = t; ret->hi = t >> 31; } void _ui2v(Vlong *ret, uint ui) { long t; t = ui; ret->lo = t; ret->hi = 0; } void _sh2v(Vlong *ret, long sh) { long t; t = (sh << 16) >> 16; ret->lo = t; ret->hi = t >> 31; } void _uh2v(Vlong *ret, ulong ul) { long t; t = ul & 0xffff; ret->lo = t; ret->hi = 0; } void _sc2v(Vlong *ret, long uc) { long t; t = (uc << 24) >> 24; ret->lo = t; ret->hi = t >> 31; } void _uc2v(Vlong *ret, ulong ul) { long t; t = ul & 0xff; ret->lo = t; ret->hi = 0; } long _v2sc(Vlong rv) { long t; t = rv.lo & 0xff; return (t << 24) >> 24; } long _v2uc(Vlong rv) { return rv.lo & 0xff; } long _v2sh(Vlong rv) { long t; t = rv.lo & 0xffff; return (t << 16) >> 16; } long _v2uh(Vlong rv) { return rv.lo & 0xffff; } long _v2sl(Vlong rv) { return rv.lo; } long _v2ul(Vlong rv) { return rv.lo; } long _v2si(Vlong rv) { return rv.lo; } long _v2ui(Vlong rv) { return rv.lo; } int _testv(Vlong rv) { return rv.lo || rv.hi; } int _eqv(Vlong lv, Vlong rv) { return lv.lo == rv.lo && lv.hi == rv.hi; } int _nev(Vlong lv, Vlong rv) { return lv.lo != rv.lo || lv.hi != rv.hi; } int _ltv(Vlong lv, Vlong rv) { return (long)lv.hi < (long)rv.hi || (lv.hi == rv.hi && lv.lo < rv.lo); } int _lev(Vlong lv, Vlong rv) { return (long)lv.hi < (long)rv.hi || (lv.hi == rv.hi && lv.lo <= rv.lo); } int _gtv(Vlong lv, Vlong rv) { return (long)lv.hi > (long)rv.hi || (lv.hi == rv.hi && lv.lo > rv.lo); } int _gev(Vlong lv, Vlong rv) { return (long)lv.hi > (long)rv.hi || (lv.hi == rv.hi && lv.lo >= rv.lo); } int _lov(Vlong lv, Vlong rv) { return lv.hi < rv.hi || (lv.hi == rv.hi && lv.lo < rv.lo); } int _lsv(Vlong lv, Vlong rv) { return lv.hi < rv.hi || (lv.hi == rv.hi && lv.lo <= rv.lo); } int _hiv(Vlong lv, Vlong rv) { return lv.hi > rv.hi || (lv.hi == rv.hi && lv.lo > rv.lo); } int _hsv(Vlong lv, Vlong rv) { return lv.hi > rv.hi || (lv.hi == rv.hi && lv.lo >= rv.lo); } /sys/src/ape/lib/ap/alpha 20000000775 sys sys 1367862839 0 /sys/src/ape/lib/ap/alpha/_seek.c 664 sys sys 1367613437 169 extern long __SEEK(long long*, int, long long, int); long long _SEEK(int fd, long long o, int p) { long long l; if(__SEEK(&l, fd, o, p) < 0) l = -1; return l; } /sys/src/ape/lib/ap/alpha/cycles.c 664 sys sys 1367613437 49 void _cycles(unsigned long long *u) { *u = 0; } /sys/src/ape/lib/ap/alpha/divl.s 664 sys sys 1367613437 3127 /* * ulong * _udiv(ulong num, ulong den) * { * int i; * ulong quo; * * if(den == 0) * *(ulong*)-1 = 0; * quo = num; * if(quo > 1<<(32-1)) * quo = 1<<(32-1); * for(i=0; den=0; i--) { * quo <<= 1; * if(num >= den) { * num -= den; * quo |= 1; * } * den >>= 1; * } * return quo::num; * } */ #define NOPROF 1 /* * calling sequence: * num: 8(R30) * den: 12(R30) * returns * quo: 8(R30) * rem: 12(R30) */ TEXT _udivmodl(SB), NOPROF, $-8 MOVQ $-1, R11 SLLQ $31, R11 /* (1<<31) in canonical form */ MOVL 8(R30), R23 /* numerator */ MOVL 12(R30), R10 /* denominator */ BNE R10, udm20 MOVQ R31, -1(R31) /* fault -- divide by zero; todo: use gentrap? */ udm20: MOVQ R23, R12 BGE R12, udm34 MOVQ R11, R12 udm34: MOVQ R31, R11 udm38: CMPUGE R10, R12, R24 BNE R24, udm54 SLLL $1, R10 ADDQ $1, R11 JMP udm38 udm54: MOVQ R31, R12 udm58: BLT R11, udm8c SLLL $1, R12 CMPUGE R23, R10, R24 BEQ R24, udm7c SUBL R10, R23 OR $1, R12 udm7c: SRLL $1, R10 SUBQ $1, R11 JMP udm58 udm8c: MOVL R12, 8(R30) /* quotient */ MOVL R23, 12(R30) /* remainder */ RET /* * save working registers * and bring in num/den parameters */ TEXT _unsargl(SB), NOPROF, $-8 MOVQ R10, 24(R30) MOVQ R11, 32(R30) MOVQ R12, 40(R30) MOVQ R23, 48(R30) MOVQ R24, 56(R30) MOVL R27, 8(R30) MOVL 72(R30), R27 MOVL R27, 12(R30) RET /* * save working registers * and bring in absolute value * of num/den parameters */ TEXT _absargl(SB), NOPROF, $-8 MOVQ R10, 24(R30) MOVQ R11, 32(R30) MOVQ R12, 40(R30) MOVQ R23, 48(R30) MOVQ R24, 56(R30) MOVL R27, 64(R30) BGE R27, ab1 SUBL R27, R31, R27 ab1: MOVL R27, 8(R30) /* numerator */ MOVL 72(R30), R27 BGE R27, ab2 SUBL R27, R31, R27 ab2: MOVL R27, 12(R30) /* denominator */ RET /* * restore registers and * return to original caller * answer is in R27 */ TEXT _retargl(SB), NOPROF, $-8 MOVQ 24(R30), R10 MOVQ 32(R30), R11 MOVQ 40(R30), R12 MOVQ 48(R30), R23 MOVQ 56(R30), R24 MOVL 0(R30), R26 ADDQ $64, R30 RET /* back to main sequence */ TEXT _divl(SB), NOPROF, $-8 SUBQ $64, R30 /* 5 reg save, 2 parameters, link */ MOVL R26, 0(R30) JSR _absargl(SB) JSR _udivmodl(SB) MOVL 8(R30), R27 MOVL 64(R30), R10 /* clean up the sign */ MOVL 72(R30), R11 XOR R11, R10 BGE R10, div1 SUBL R27, R31, R27 div1: JSR _retargl(SB) RET /* not executed */ TEXT _divlu(SB), NOPROF, $-8 SUBQ $64, R30 /* 5 reg save, 2 parameters, link */ MOVL R26, 0(R30) JSR _unsargl(SB) JSR _udivmodl(SB) MOVL 8(R30), R27 JSR _retargl(SB) RET /* not executed */ TEXT _modl(SB), NOPROF, $-8 SUBQ $64, R30 /* 5 reg save, 2 parameters, link */ MOVL R26, 0(R30) JSR _absargl(SB) JSR _udivmodl(SB) MOVL 12(R30), R27 MOVL 64(R30), R10 /* clean up the sign */ BGE R10, div2 SUBL R27, R31, R27 div2: JSR _retargl(SB) RET /* not executed */ TEXT _modlu(SB), NOPROF, $-8 SUBQ $64, R30 /* 5 reg save, 2 parameters, link */ MOVL R26, 0(R30) JSR _unsargl(SB) JSR _udivmodl(SB) MOVL 12(R30), R27 JSR _retargl(SB) RET /* not executed */ /sys/src/ape/lib/ap/alpha/divq.s 664 sys sys 1367613437 3147 /* * uvlong * _udiv(uvlong num, uvlong den) * { * int i; * uvlong quo; * * if(den == 0) * *(ulong*)-1 = 0; * quo = num; * if(quo > 1<<(64-1)) * quo = 1<<(64-1); * for(i=0; den=0; i--) { * quo <<= 1; * if(num >= den) { * num -= den; * quo |= 1; * } * den >>= 1; * } * return quo::num; * } */ #define NOPROF 1 /* * calling sequence: * num: 8(R30) * den: 16(R30) * returns * quo: 8(R30) * rem: 16(R30) */ TEXT _udivmodq(SB), NOPROF, $-8 MOVQ $1, R11 SLLQ $63, R11 MOVQ 8(R30), R23 /* numerator */ MOVQ 16(R30), R10 /* denominator */ BNE R10, udm20 MOVQ R31, -1(R31) /* fault -- divide by zero; todo: use gentrap? */ udm20: MOVQ R23, R12 BGE R12, udm34 MOVQ R11, R12 udm34: MOVQ R31, R11 udm38: CMPUGE R10, R12, R24 BNE R24, udm54 SLLQ $1, R10 ADDQ $1, R11 JMP udm38 udm54: MOVQ R31, R12 udm58: BLT R11, udm8c SLLQ $1, R12 CMPUGE R23, R10, R24 BEQ R24, udm7c SUBQ R10, R23 OR $1, R12 udm7c: SRLQ $1, R10 SUBQ $1, R11 JMP udm58 udm8c: MOVQ R12, 8(R30) /* quotient */ MOVQ R23, 16(R30) /* remainder */ RET /* * save working registers * and bring in num/den parameters */ TEXT _unsargq(SB), NOPROF, $-8 MOVQ R10, 24(R30) MOVQ R11, 32(R30) MOVQ R12, 40(R30) MOVQ R23, 48(R30) MOVQ R24, 56(R30) MOVQ R27, 8(R30) MOVQ 72(R30), R27 MOVQ R27, 16(R30) MOVQ (R30), R10 /* debug */ RET /* * save working registers * and bring in absolute value * of num/den parameters */ TEXT _absargq(SB), NOPROF, $-8 MOVQ R10, 24(R30) MOVQ R11, 32(R30) MOVQ R12, 40(R30) MOVQ R23, 48(R30) MOVQ R24, 56(R30) MOVQ R27, 64(R30) BGE R27, ab1 SUBQ R27, R31, R27 ab1: MOVQ R27, 8(R30) /* numerator */ MOVQ 72(R30), R27 BGE R27, ab2 SUBQ R27, R31, R27 ab2: MOVQ R27, 16(R30) /* denominator */ MOVQ (R30), R10 /* debug */ RET /* * restore registers and * return to original caller * answer is in R27 */ TEXT _retargq(SB), NOPROF, $-8 MOVQ 24(R30), R10 MOVQ 32(R30), R11 MOVQ 40(R30), R12 MOVQ 48(R30), R23 MOVQ 56(R30), R24 MOVL 0(R30), R26 ADDQ $64, R30 RET /* back to main sequence */ TEXT _divq(SB), NOPROF, $-8 SUBQ $64, R30 /* 5 reg save, 2 parameters, link */ MOVQ R26, 0(R30) JSR _absargq(SB) JSR _udivmodq(SB) MOVQ 8(R30), R27 MOVQ 64(R30), R10 /* clean up the sign */ MOVQ 72(R30), R11 XOR R11, R10 BGE R10, div1 SUBQ R27, R31, R27 div1: JSR _retargq(SB) RET /* not executed */ TEXT _divqu(SB), NOPROF, $-8 SUBQ $64, R30 /* 5 reg save, 2 parameters, link */ MOVQ R26, 0(R30) JSR _unsargq(SB) JSR _udivmodq(SB) MOVQ 8(R30), R27 JSR _retargq(SB) RET /* not executed */ TEXT _modq(SB), NOPROF, $-8 SUBQ $64, R30 /* 5 reg save, 2 parameters, link */ MOVQ R26, 0(R30) JSR _absargq(SB) JSR _udivmodq(SB) MOVQ 16(R30), R27 MOVQ 64(R30), R10 /* clean up the sign */ BGE R10, div2 SUBQ R27, R31, R27 div2: JSR _retargq(SB) RET /* not executed */ TEXT _modqu(SB), NOPROF, $-8 SUBQ $64, R30 /* 5 reg save, 2 parameters, link */ MOVQ R26, 0(R30) JSR _unsargq(SB) JSR _udivmodq(SB) MOVQ 16(R30), R27 JSR _retargq(SB) RET /* not executed */ /sys/src/ape/lib/ap/alpha/getfcr.s 664 sys sys 1367613437 409 TEXT getfsr(SB), $8 TRAPB MOVT FPCR, F0 TRAPB MOVT F0, tmp-8(SP) MOVL tmp-4(SP), R0 RET TEXT setfsr(SB), $8 SLLQ $32, R0 MOVQ R0, tmp-8(SP) MOVT tmp-8(SP), F0 TRAPB MOVT F0, FPCR TRAPB RET TEXT getfcr(SB), $8 TRAPB MOVT FPCR, F0 TRAPB MOVT F0, tmp-8(SP) MOVL tmp-4(SP), R0 RET TEXT setfcr(SB), $8 SLLQ $32, R0 MOVQ R0, tmp-8(SP) MOVT tmp-8(SP), F0 TRAPB MOVT F0, FPCR TRAPB RET /sys/src/ape/lib/ap/alpha/lock.c 664 sys sys 1367613437 255 #define _LOCK_EXTENSION #include "../plan9/sys9.h" #include int tas(int*); void lock(Lock *lk) { while(tas(&lk->val)) _SLEEP(0); } int canlock(Lock *lk) { if(tas(&lk->val)) return 0; return 1; } void unlock(Lock *lk) { lk->val = 0; } /sys/src/ape/lib/ap/alpha/main9.s 664 sys sys 1367613437 311 TEXT _main(SB), 1, $16 MOVQ $setSB(SB), R29 JSR _envsetup(SB) MOVL inargc-8(FP), R0 MOVL $inargv-4(FP), R1 MOVL R0, 8(R30) MOVL R1, 12(R30) JSR main(SB) loop: MOVL R0, 8(R30) JSR exit(SB) MOVQ $_divq(SB), R31 /* force loading of divq */ MOVQ $_divl(SB), R31 /* force loading of divl */ JMP loop /sys/src/ape/lib/ap/alpha/main9p.s 664 sys sys 1367613437 888 #define NPRIVATES 16 GLOBL _tos(SB), $4 GLOBL _privates(SB), $4 GLOBL _nprivates(SB), $4 TEXT _mainp(SB), 1, $(3*4+NPRIVATES*4) MOVQ $setSB(SB), R29 /* _tos = arg */ MOVL R0, _tos(SB) MOVQ $8(SP), R1 MOVL R1, _privates(SB) MOVQ $NPRIVATES, R1 MOVL R1, _nprivates(SB) /* _profmain(); */ JSR _profmain(SB) /* _tos->prof.pp = _tos->prof.next; */ MOVL _tos+0(SB), R1 MOVL 4(R1), R2 MOVL R2, 0(R1) JSR _envsetup(SB) /* main(argc, argv, environ); */ MOVL inargc-4(FP), R0 MOVL $inargv+0(FP), R1 MOVL environ(SB), R2 MOVL R0, 8(R30) MOVL R1, 12(R30) MOVL R2, 16(R30) JSR main(SB) loop: MOVL R0, 8(R30) JSR exit(SB) MOVQ $_divq(SB), R31 /* force loading of divq */ MOVQ $_divl(SB), R31 /* force loading of divl */ MOVQ $_profin(SB), R31 /* force loading of profile */ JMP loop TEXT _savearg(SB), 1, $0 RET TEXT _callpc(SB), 1, $0 MOVL argp-8(FP), R0 RET /sys/src/ape/lib/ap/alpha/memcpy.c 664 sys sys 1367613437 102 #include void* memcpy(void *a1, const void *a2, size_t n) { return memmove(a1, a2, n); } /sys/src/ape/lib/ap/alpha/memmove.s 664 sys sys 1367613437 2936 #define QUAD 8 #define ALIGN 64 #define BLOCK 64 TEXT memmove(SB), $0 MOVL from+4(FP), R7 MOVL n+8(FP), R10 MOVQ R0, R6 CMPUGE R7, R0, R5 BNE R5, _forward MOVQ R6, R8 /* end to address */ ADDL R10, R6, R6 /* to+n */ ADDL R10, R7, R7 /* from+n */ CMPUGE $ALIGN, R10, R1 /* need at least ALIGN bytes */ BNE R1, _b1tail _balign: AND $(ALIGN-1), R6, R1 BEQ R1, _baligned MOVBU -1(R7), R2 ADDL $-1, R6, R6 MOVB R2, (R6) ADDL $-1, R7, R7 JMP _balign _baligned: AND $(QUAD-1), R7, R1 /* is the source quad-aligned */ BNE R1, _bunaligned ADDL $(BLOCK-1), R8, R9 _bblock: CMPUGE R9, R6, R1 BNE R1, _b8tail MOVQ -64(R7), R22 MOVQ -56(R7), R23 MOVQ -48(R7), R24 MOVQ -40(R7), R25 MOVQ -32(R7), R2 MOVQ -24(R7), R3 MOVQ -16(R7), R4 MOVQ -8(R7), R5 SUBL $64, R6, R6 SUBL $64, R7, R7 MOVQ R22, (R6) MOVQ R23, 8(R6) MOVQ R24, 16(R6) MOVQ R25, 24(R6) MOVQ R2, 32(R6) MOVQ R3, 40(R6) MOVQ R4, 48(R6) MOVQ R5, 56(R6) JMP _bblock _b8tail: ADDL $(QUAD-1), R8, R9 _b8block: CMPUGE R9, R6, R1 BNE R1, _b1tail MOVQ -8(R7), R2 SUBL $8, R6 MOVQ R2, (R6) SUBL $8, R7 JMP _b8block _b1tail: CMPUGE R8, R6, R1 BNE R1, _ret MOVBU -1(R7), R2 SUBL $1, R6, R6 MOVB R2, (R6) SUBL $1, R7, R7 JMP _b1tail _ret: RET _bunaligned: ADDL $(16-1), R8, R9 _bu8block: CMPUGE R9, R6, R1 BNE R1, _b1tail MOVQU -16(R7), R4 MOVQU -8(R7), R3 MOVQU (R7), R2 SUBL $16, R6 EXTQH R7, R2, R2 EXTQL R7, R3, R5 OR R5, R2, R11 EXTQH R7, R3, R3 EXTQL R7, R4, R4 OR R3, R4, R13 MOVQ R11, 8(R6) MOVQ R13, (R6) SUBL $16, R7 JMP _bu8block _forward: ADDL R10, R6, R8 /* end to address */ CMPUGE $ALIGN, R10, R1 /* need at least ALIGN bytes */ BNE R1, _f1tail _falign: AND $(ALIGN-1), R6, R1 BEQ R1, _faligned MOVBU (R7), R2 ADDL $1, R6, R6 ADDL $1, R7, R7 MOVB R2, -1(R6) JMP _falign _faligned: AND $(QUAD-1), R7, R1 /* is the source quad-aligned */ BNE R1, _funaligned SUBL $(BLOCK-1), R8, R9 _fblock: CMPUGT R9, R6, R1 BEQ R1, _f8tail MOVQ (R7), R2 MOVQ 8(R7), R3 MOVQ 16(R7), R4 MOVQ 24(R7), R5 MOVQ 32(R7), R22 MOVQ 40(R7), R23 MOVQ 48(R7), R24 MOVQ 56(R7), R25 ADDL $64, R6, R6 ADDL $64, R7, R7 MOVQ R2, -64(R6) MOVQ R3, -56(R6) MOVQ R4, -48(R6) MOVQ R5, -40(R6) MOVQ R22, -32(R6) MOVQ R23, -24(R6) MOVQ R24, -16(R6) MOVQ R25, -8(R6) JMP _fblock _f8tail: SUBL $(QUAD-1), R8, R9 _f8block: CMPUGT R9, R6, R1 BEQ R1, _f1tail MOVQ (R7), R2 ADDL $8, R6 ADDL $8, R7 MOVQ R2, -8(R6) JMP _f8block _f1tail: CMPUGT R8, R6, R1 BEQ R1, _fret MOVBU (R7), R2 ADDL $1, R6, R6 ADDL $1, R7, R7 MOVB R2, -1(R6) JMP _f1tail _fret: RET _funaligned: SUBL $(16-1), R8, R9 _fu8block: CMPUGT R9, R6, R1 BEQ R1, _f1tail MOVQU (R7), R2 MOVQU 8(R7), R3 MOVQU 16(R7), R4 EXTQL R7, R2, R2 EXTQH R7, R3, R5 OR R5, R2, R11 EXTQL R7, R3, R3 MOVQ R11, (R6) EXTQH R7, R4, R4 OR R3, R4, R11 MOVQ R11, 8(R6) ADDL $16, R6 ADDL $16, R7 JMP _fu8block /sys/src/ape/lib/ap/alpha/memset.s 664 sys sys 1367613437 844 TEXT memset(SB), $0 MOVL R0, R6 MOVBU data+4(FP), R2 MOVL n+8(FP), R10 ADDL R10, R0, R8 CMPUGE $8, R10, R1 /* need at least 8 bytes */ BNE R1, _1loop SLLQ $8, R2, R1 /* replicate the byte */ OR R1, R2 SLLQ $16, R2, R1 OR R1, R2 SLLQ $32, R2, R1 OR R1, R2 _align: AND $(8-1), R6, R1 BEQ R1, _aligned MOVB R2, (R6) ADDL $1, R6, R6 JMP _align _aligned: SUBL $(64-1), R8, R9 /* end pointer minus slop */ _64loop: CMPUGT R9, R6, R1 BEQ R1, _8tail MOVQ R2, (R6) MOVQ R2, 8(R6) MOVQ R2, 16(R6) MOVQ R2, 24(R6) MOVQ R2, 32(R6) MOVQ R2, 40(R6) MOVQ R2, 48(R6) MOVQ R2, 56(R6) ADDL $64, R6, R6 JMP _64loop _8tail: SUBL $(8-1), R8, R9 _8loop: CMPUGT R9, R6, R1 BEQ R1, _1loop MOVQ R2, (R6) ADDL $8, R6 JMP _8loop _1loop: CMPUGT R8, R6, R1 BEQ R1, _ret MOVB R2, (R6) ADDL $1, R6 JMP _1loop _ret: RET /sys/src/ape/lib/ap/alpha/mkfile 664 sys sys 1367613437 298 APE=/sys/src/ape <$APE/config LIB=/$objtype/lib/ape/libap.a OFILES=\ _seek.$O\ cycles.$O\ divl.$O\ divq.$O\ getfcr.$O\ lock.$O\ main9.$O\ main9p.$O\ memcpy.$O\ memmove.$O\ memset.$O\ notetramp.$O\ setjmp.$O\ tas.$O\ #include /* A stack to hold pcs when signals nest */ #define MAXSIGSTACK 20 typedef struct Pcstack Pcstack; static struct Pcstack { int sig; void (*hdlr)(int, char*, Ureg*); unsigned long restorepc; Ureg *u; } pcstack[MAXSIGSTACK]; static int nstack = 0; static void notecont(Ureg*, char*); void _notetramp(int sig, void (*hdlr)(int, char*, Ureg*), Ureg *u) { Pcstack *p; if(nstack >= MAXSIGSTACK) _NOTED(1); /* nesting too deep; just do system default */ p = &pcstack[nstack]; p->restorepc = u->pc; p->sig = sig; p->hdlr = hdlr; p->u = u; nstack++; u->pc = (unsigned long) notecont; _NOTED(2); /* NSAVE: clear note but hold state */ } static void notecont(Ureg *u, char *s) { Pcstack *p; void(*f)(int, char*, Ureg*); p = &pcstack[nstack-1]; f = p->hdlr; u->pc = p->restorepc; nstack--; (*f)(p->sig, s, u); _NOTED(3); /* NRSTR */ } #define JMPBUFPC 1 #define JMPBUFSP 0 extern sigset_t _psigblocked; void siglongjmp(sigjmp_buf j, int ret) { struct Ureg *u; if(j[0]) _psigblocked = j[1]; if(nstack == 0 || pcstack[nstack-1].u->sp > j[2+JMPBUFSP]) longjmp(j+2, ret); u = pcstack[nstack-1].u; nstack--; u->r0 = ret; if(ret == 0) u->r0 = 1; u->pc = j[2+JMPBUFPC]; u->sp = j[2+JMPBUFSP]; _NOTED(3); /* NRSTR */ } /sys/src/ape/lib/ap/alpha/setjmp.s 664 sys sys 1367613437 437 TEXT setjmp(SB), 1, $-8 MOVL R30, (R0) MOVL R26, 4(R0) MOVQ $0, R0 RET TEXT sigsetjmp(SB), 1, $-8 MOVL savemask+4(FP), R3 MOVL R3, 0(R0) MOVL $_psigblocked(SB), R3 MOVL R3, 4(R0) MOVL R30, 8(R0) MOVL R26, 12(R0) MOVQ $0, R0 RET TEXT longjmp(SB), 1, $-8 MOVL r+4(FP), R3 BNE R3, ok /* ansi: "longjmp(0) => longjmp(1)" */ MOVQ $1, R3 /* bless their pointed heads */ ok: MOVL (R0), R30 MOVL 4(R0), R26 MOVL R3, R0 RET /sys/src/ape/lib/ap/alpha/tas.s 664 sys sys 1367613437 195 TEXT tas(SB), $-8 MOVQ R0, R1 /* l */ tas1: MOVLL (R1), R0 /* l->key */ BNE R0, tas2 MOVQ $1, R2 MOVLC R2, (R1) /* l->key = 1 */ BEQ R2, tas1 /* write failed, try again? */ tas2: RET /sys/src/ape/lib/ap/amd64 20000000775 sys sys 1369861527 0 /sys/src/ape/lib/ap/amd64/_seek.c 664 sys sys 1367613437 168 extern long __SEEK(long long*, int, long long, int); long long _SEEK(int fd, long long o, int p) { long long l; if(__SEEK(&l, fd, o, p) < 0) l = -1; return l; } /sys/src/ape/lib/ap/amd64/cycles.s 664 sys sys 1367613437 143 TEXT _cycles(SB),1,$0 /* time stamp counter; cycles since power up */ RDTSC MOVL AX, 0(RARG) /* lo */ MOVL DX, 4(RARG) /* hi */ RET /sys/src/ape/lib/ap/amd64/lock.c 664 sys sys 1367613437 255 #define _LOCK_EXTENSION #include "../plan9/sys9.h" #include int tas(int*); void lock(Lock *lk) { while(tas(&lk->val)) _SLEEP(0); } int canlock(Lock *lk) { if(tas(&lk->val)) return 0; return 1; } void unlock(Lock *lk) { lk->val = 0; } /sys/src/ape/lib/ap/amd64/main9.s 664 sys sys 1367613437 201 TEXT _main(SB), 1, $(3*8) CALL _envsetup(SB) MOVL inargc-8(FP), RARG LEAQ inargv+0(FP), AX MOVQ AX, 8(SP) MOVQ environ(SB), AX MOVQ AX, 16(SP) CALL main(SB) MOVQ AX, RARG CALL exit(SB) RET /sys/src/ape/lib/ap/amd64/main9p.s 664 sys sys 1367613437 739 #define NPRIVATES 16 GLOBL _tos(SB), $8 GLOBL _privates(SB), $8 GLOBL _nprivates(SB), $8 TEXT _mainp(SB), 1, $(3*8+NPRIVATES*8) /* _tos = arg */ MOVQ AX, _tos(SB) LEAQ 8(SP), AX MOVQ AX, _privates(SB) MOVQ $NPRIVATES, _nprivates(SB) /* _profmain(); */ CALL _profmain(SB) /* _tos->prof.pp = _tos->prof.next; */ MOVQ _tos+0(SB),DX MOVQ 4(DX),CX MOVQ CX,(DX) CALL _envsetup(SB) /* main(argc, argv, environ); */ MOVL inargc-8(FP), RARG LEAQ inargv+0(FP), AX MOVQ AX, 8(SP) MOVQ environ(SB), AX MOVQ AX, 16(SP) CALL main(SB) loop: MOVL AX, RARG CALL exit(SB) MOVQ $_profin(SB), AX /* force loading of profile */ MOVL $0, AX JMP loop TEXT _savearg(SB), 1, $0 RET TEXT _callpc(SB), 1, $0 MOVQ 8(RARG), AX RET /sys/src/ape/lib/ap/amd64/mkfile 664 sys sys 1367613437 267 APE=/sys/src/ape objtype=amd64 <$APE/config LIB=/$objtype/lib/ape/libap.a OFILES=\ _seek.$O\ cycles.$O\ lock.$O\ main9.$O\ main9p.$O\ notetramp.$O\ setjmp.$O\ strchr.$O\ strlen.$O\ tas.$O\ #include /* A stack to hold pcs when signals nest */ #define MAXSIGSTACK 20 typedef struct Pcstack Pcstack; static struct Pcstack { int sig; void (*hdlr)(int, char*, Ureg*); unsigned long long restorepc; Ureg *u; } pcstack[MAXSIGSTACK]; static int nstack = 0; static void notecont(Ureg*, char*); void _notetramp(int sig, void (*hdlr)(int, char*, Ureg*), Ureg *u) { Pcstack *p; if(nstack >= MAXSIGSTACK) _NOTED(1); /* nesting too deep; just do system default */ p = &pcstack[nstack]; p->restorepc = u->ip; p->sig = sig; p->hdlr = hdlr; p->u = u; nstack++; u->ip = (unsigned long long) notecont; _NOTED(2); /* NSAVE: clear note but hold state */ } static void notecont(Ureg *u, char *s) { Pcstack *p; void(*f)(int, char*, Ureg*); p = &pcstack[nstack-1]; f = p->hdlr; u->ip = p->restorepc; nstack--; (*f)(p->sig, s, u); _NOTED(3); /* NRSTR */ } #define JMPBUFPC 1 #define JMPBUFSP 0 extern sigset_t _psigblocked; typedef struct { sigset_t set; sigset_t blocked; unsigned long long jmpbuf[2]; } sigjmp_buf_amd64; void siglongjmp(sigjmp_buf j, int ret) { struct Ureg *u; sigjmp_buf_amd64 *jb; jb = (sigjmp_buf_amd64*)j; if(jb->set) _psigblocked = jb->blocked; if(nstack == 0 || pcstack[nstack-1].u->sp > jb->jmpbuf[JMPBUFSP]) longjmp((void*)jb->jmpbuf, ret); u = pcstack[nstack-1].u; nstack--; u->ax = ret; if(ret == 0) u->ax = 1; u->ip = jb->jmpbuf[JMPBUFPC]; u->sp = jb->jmpbuf[JMPBUFSP] + 8; _NOTED(3); /* NRSTR */ } /sys/src/ape/lib/ap/amd64/setjmp.s 664 sys sys 1367613437 624 TEXT longjmp(SB), $0 MOVL r+8(FP), AX CMPL AX, $0 JNE ok /* ansi: "longjmp(0) => longjmp(1)" */ MOVL $1, AX /* bless their pointed heads */ ok: MOVQ 0(RARG), SP /* restore sp */ MOVQ 8(RARG), BX /* put return pc on the stack */ MOVQ BX, 0(SP) RET TEXT setjmp(SB), $0 MOVQ SP, 0(RARG) /* store sp */ MOVQ 0(SP), BX /* store return pc */ MOVQ BX, 8(RARG) MOVL $0, AX /* return 0 */ RET TEXT sigsetjmp(SB), $0 MOVL savemask+8(FP), BX MOVL BX, 0(RARG) MOVL $_psigblocked(SB), 4(RARG) MOVQ SP, 8(RARG) /* store sp */ MOVQ 0(SP), BX /* store return pc */ MOVQ BX, 16(RARG) MOVL $0, AX /* return 0 */ RET /sys/src/ape/lib/ap/amd64/strchr.s 664 sys sys 1367613437 335 TEXT strchr(SB), $0 MOVQ RARG, DI MOVB c+8(FP), AX CMPB AX, $0 JEQ l2 /**/ /* * char is not null */ l1: MOVB (DI), BX CMPB BX, $0 JEQ ret0 ADDQ $1, DI CMPB AX, BX JNE l1 MOVQ DI, AX SUBQ $1, AX RET /* * char is null */ l2: MOVQ $-1, CX CLD REPN; SCASB MOVQ DI, AX SUBQ $1, AX RET ret0: MOVQ $0, AX RET /sys/src/ape/lib/ap/amd64/strlen.s 664 sys sys 1367613437 162 TEXT strlen(SB),$0 MOVL $0, AX MOVQ $-1, CX CLD /* * look for end of string */ MOVQ RARG, DI REPN; SCASB MOVQ DI, AX SUBQ RARG, AX SUBQ $1, AX RET /sys/src/ape/lib/ap/amd64/tas.s 664 sys sys 1367613437 59 TEXT tas(SB),$0 MOVL $0xdeadead,AX XCHGL AX,(RARG) RET /sys/src/ape/lib/ap/arm 20000000775 sys sys 1367862839 0 /sys/src/ape/lib/ap/arm/cycles.c 664 sys sys 1367613437 49 void _cycles(unsigned long long *u) { *u = 0; } /sys/src/ape/lib/ap/arm/div.s 664 sys sys 1367613437 1639 Q = 0 N = 1 D = 2 CC = 3 TMP = 11 TEXT save<>(SB), 1, $0 MOVW R(Q), 0(FP) MOVW R(N), 4(FP) MOVW R(D), 8(FP) MOVW R(CC), 12(FP) MOVW R(TMP), R(Q) /* numerator */ MOVW 20(FP), R(D) /* denominator */ CMP $0, R(D) BNE s1 MOVW -1(R(D)), R(TMP) /* divide by zero fault */ s1: RET TEXT rest<>(SB), 1, $0 MOVW 0(FP), R(Q) MOVW 4(FP), R(N) MOVW 8(FP), R(D) MOVW 12(FP), R(CC) /* * return to caller * of rest<> */ MOVW 0(R13), R14 ADD $20, R13 B (R14) TEXT div<>(SB), 1, $0 MOVW $32, R(CC) /* * skip zeros 8-at-a-time */ e1: AND.S $(0xff<<24),R(Q), R(N) BNE e2 SLL $8, R(Q) SUB.S $8, R(CC) BNE e1 RET e2: MOVW $0, R(N) loop: /* * shift R(N||Q) left one */ SLL $1, R(N) CMP $0, R(Q) ORR.LT $1, R(N) SLL $1, R(Q) /* * compare numerator to denominator * if less, subtract and set quotent bit */ CMP R(D), R(N) ORR.HS $1, R(Q) SUB.HS R(D), R(N) SUB.S $1, R(CC) BNE loop RET TEXT _div(SB), 1, $16 BL save<>(SB) CMP $0, R(Q) BGE d1 RSB $0, R(Q), R(Q) CMP $0, R(D) BGE d2 RSB $0, R(D), R(D) d0: BL div<>(SB) /* none/both neg */ MOVW R(Q), R(TMP) B out d1: CMP $0, R(D) BGE d0 RSB $0, R(D), R(D) d2: BL div<>(SB) /* one neg */ RSB $0, R(Q), R(TMP) B out TEXT _mod(SB), 1, $16 BL save<>(SB) CMP $0, R(D) RSB.LT $0, R(D), R(D) CMP $0, R(Q) BGE m1 RSB $0, R(Q), R(Q) BL div<>(SB) /* neg numerator */ RSB $0, R(N), R(TMP) B out m1: BL div<>(SB) /* pos numerator */ MOVW R(N), R(TMP) B out TEXT _divu(SB), 1, $16 BL save<>(SB) BL div<>(SB) MOVW R(Q), R(TMP) B out TEXT _modu(SB), 1, $16 BL save<>(SB) BL div<>(SB) MOVW R(N), R(TMP) B out out: BL rest<>(SB) B out /sys/src/ape/lib/ap/arm/getfcr.s 664 sys sys 1367613437 164 TEXT setfcr(SB), $4 MOVW R0, FPCR RET TEXT getfcr(SB), $4 MOVW FPCR, R0 RET TEXT getfsr(SB), $0 MOVW FPSR, R0 RET TEXT setfsr(SB), $0 MOVW R0, FPSR RET /sys/src/ape/lib/ap/arm/lock.c 664 sys sys 1367613437 255 #define _LOCK_EXTENSION #include "../plan9/sys9.h" #include int tas(int*); void lock(Lock *lk) { while(tas(&lk->val)) _SLEEP(0); } int canlock(Lock *lk) { if(tas(&lk->val)) return 0; return 1; } void unlock(Lock *lk) { lk->val = 0; } /sys/src/ape/lib/ap/arm/main9.s 664 sys sys 1367613437 262 arg=0 sp=13 sb=12 TEXT _main(SB), 1, $16 MOVW $setR12(SB), R(sb) BL _envsetup(SB) MOVW $inargv+0(FP), R(arg) MOVW R(arg), 8(R(sp)) MOVW inargc-4(FP), R(arg) MOVW R(arg), 4(R(sp)) BL main(SB) loop: MOVW R(arg), 4(R(sp)) BL exit(SB) BL _div(SB) B loop /sys/src/ape/lib/ap/arm/main9p.s 664 sys sys 1367613437 906 arg=0 sp=13 sb=12 #define NPRIVATES 16 GLOBL _tos(SB), $4 GLOBL _privates(SB), $4 GLOBL _nprivates(SB), $4 TEXT _mainp(SB), 1, $(3*4+NPRIVATES*4) MOVW $setR12(SB), R(sb) /* _tos = arg */ MOVW R(arg), _tos(SB) MOVW $private+8(SP), R1 MOVW R1, _privates(SB) MOVW $NPRIVATES, R1 MOVW R1, _nprivates(SB) /* _profmain(); */ BL _profmain(SB) /* _tos->prof.pp = _tos->prof.next; */ MOVW _tos+0(SB),R1 MOVW 4(R1), R2 MOVW R2, 0(R1) BL _envsetup(SB) /* main(argc, argv, environ); */ MOVW $inargv+0(FP), R(arg) MOVW R(arg), 8(R(sp)) MOVW inargc-4(FP), R(arg) MOVW R(arg), 4(R(sp)) MOVW environ(SB), R(arg) MOVW R(arg), 8(R(sp)) BL main(SB) loop: MOVW R(arg), 4(R(sp)) BL exit(SB) MOVW $_div(SB), R(arg) /* force loading of div */ MOVW $_profin(SB), R(arg) /* force loading of profile */ B loop TEXT _savearg(SB), 1, $0 RET TEXT _callpc(SB), 1, $0 MOVW argp-4(FP), R(arg) RET /sys/src/ape/lib/ap/arm/memmove.s 664 sys sys 1367613437 4198 TS = 0 TE = 1 FROM = 2 N = 3 TMP = 3 /* N and TMP don't overlap */ TMP1 = 4 TEXT memcpy(SB), $-4 B _memmove TEXT memmove(SB), $-4 _memmove: MOVW R(TS), to+0(FP) /* need to save for return value */ MOVW from+4(FP), R(FROM) MOVW n+8(FP), R(N) ADD R(N), R(TS), R(TE) /* to end pointer */ CMP R(FROM), R(TS) BLS _forward _back: ADD R(N), R(FROM) /* from end pointer */ CMP $4, R(N) /* need at least 4 bytes to copy */ BLT _b1tail _b4align: /* align destination on 4 */ AND.S $3, R(TE), R(TMP) BEQ _b4aligned MOVBU.W -1(R(FROM)), R(TMP) /* pre-indexed */ MOVBU.W R(TMP), -1(R(TE)) /* pre-indexed */ B _b4align _b4aligned: /* is source now aligned? */ AND.S $3, R(FROM), R(TMP) BNE _bunaligned ADD $31, R(TS), R(TMP) /* do 32-byte chunks if possible */ _b32loop: CMP R(TMP), R(TE) BLS _b4tail MOVM.DB.W (R(FROM)), [R4-R7] MOVM.DB.W [R4-R7], (R(TE)) MOVM.DB.W (R(FROM)), [R4-R7] MOVM.DB.W [R4-R7], (R(TE)) B _b32loop _b4tail: /* do remaining words if possible */ ADD $3, R(TS), R(TMP) _b4loop: CMP R(TMP), R(TE) BLS _b1tail MOVW.W -4(R(FROM)), R(TMP1) /* pre-indexed */ MOVW.W R(TMP1), -4(R(TE)) /* pre-indexed */ B _b4loop _b1tail: /* remaining bytes */ CMP R(TE), R(TS) BEQ _return MOVBU.W -1(R(FROM)), R(TMP) /* pre-indexed */ MOVBU.W R(TMP), -1(R(TE)) /* pre-indexed */ B _b1tail _forward: CMP $4, R(N) /* need at least 4 bytes to copy */ BLT _f1tail _f4align: /* align destination on 4 */ AND.S $3, R(TS), R(TMP) BEQ _f4aligned MOVBU.P 1(R(FROM)), R(TMP) /* implicit write back */ MOVBU.P R(TMP), 1(R(TS)) /* implicit write back */ B _f4align _f4aligned: /* is source now aligned? */ AND.S $3, R(FROM), R(TMP) BNE _funaligned SUB $31, R(TE), R(TMP) /* do 32-byte chunks if possible */ _f32loop: CMP R(TMP), R(TS) BHS _f4tail MOVM.IA.W (R(FROM)), [R4-R7] MOVM.IA.W [R4-R7], (R(TS)) MOVM.IA.W (R(FROM)), [R4-R7] MOVM.IA.W [R4-R7], (R(TS)) B _f32loop _f4tail: SUB $3, R(TE), R(TMP) /* do remaining words if possible */ _f4loop: CMP R(TMP), R(TS) BHS _f1tail MOVW.P 4(R(FROM)), R(TMP1) /* implicit write back */ MOVW.P R4, 4(R(TS)) /* implicit write back */ B _f4loop _f1tail: CMP R(TS), R(TE) BEQ _return MOVBU.P 1(R(FROM)), R(TMP) /* implicit write back */ MOVBU.P R(TMP), 1(R(TS)) /* implicit write back */ B _f1tail _return: MOVW to+0(FP), R0 RET RSHIFT = 4 LSHIFT = 5 OFFSET = 11 BR0 = 6 BW0 = 7 BR1 = 7 BW1 = 8 _bunaligned: CMP $2, R(TMP) /* is R(TMP) < 2 ? */ MOVW.LT $8, R(RSHIFT) /* (R(n)<<24)|(R(n-1)>>8) */ MOVW.LT $24, R(LSHIFT) MOVW.LT $1, R(OFFSET) MOVW.EQ $16, R(RSHIFT) /* (R(n)<<16)|(R(n-1)>>16) */ MOVW.EQ $16, R(LSHIFT) MOVW.EQ $2, R(OFFSET) MOVW.GT $24, R(RSHIFT) /* (R(n)<<8)|(R(n-1)>>24) */ MOVW.GT $8, R(LSHIFT) MOVW.GT $3, R(OFFSET) ADD $8, R(TS), R(TMP) /* do 8-byte chunks if possible */ CMP R(TMP), R(TE) BLS _b1tail BIC $3, R(FROM) /* align source */ MOVW (R(FROM)), R(BR0) /* prime first block register */ _bu8loop: CMP R(TMP), R(TE) BLS _bu1tail MOVW R(BR0)<>R(RSHIFT), R(BW1) MOVW R(BR1)<>R(RSHIFT), R(BW0) MOVM.DB.W [R(BW0)-R(BW1)], (R(TE)) B _bu8loop _bu1tail: ADD R(OFFSET), R(FROM) B _b1tail RSHIFT = 4 LSHIFT = 5 OFFSET = 11 FW0 = 6 FR0 = 7 FW1 = 7 FR1 = 8 _funaligned: CMP $2, R(TMP) MOVW.LT $8, R(RSHIFT) /* (R(n+1)<<24)|(R(n)>>8) */ MOVW.LT $24, R(LSHIFT) MOVW.LT $3, R(OFFSET) MOVW.EQ $16, R(RSHIFT) /* (R(n+1)<<16)|(R(n)>>16) */ MOVW.EQ $16, R(LSHIFT) MOVW.EQ $2, R(OFFSET) MOVW.GT $24, R(RSHIFT) /* (R(n+1)<<8)|(R(n)>>24) */ MOVW.GT $8, R(LSHIFT) MOVW.GT $1, R(OFFSET) SUB $8, R(TE), R(TMP) /* do 8-byte chunks if possible */ CMP R(TMP), R(TS) BHS _f1tail BIC $3, R(FROM) /* align source */ MOVW.P 4(R(FROM)), R(FR1) /* prime last block register, implicit write back */ _fu8loop: CMP R(TMP), R(TS) BHS _fu1tail MOVW R(FR1)>>R(RSHIFT), R(FW0) MOVM.IA.W (R(FROM)), [R(FR0)-R(FR1)] ORR R(FR0)<>R(RSHIFT), R(FW1) ORR R(FR1)< #include /* A stack to hold pcs when signals nest */ #define MAXSIGSTACK 20 typedef struct Pcstack Pcstack; static struct Pcstack { int sig; void (*hdlr)(int, char*, Ureg*); unsigned long restorepc; Ureg *u; } pcstack[MAXSIGSTACK]; static int nstack = 0; static void notecont(Ureg*, char*); void _notetramp(int sig, void (*hdlr)(int, char*, Ureg*), Ureg *u) { Pcstack *p; if(nstack >= MAXSIGSTACK) _NOTED(1); /* nesting too deep; just do system default */ p = &pcstack[nstack]; p->restorepc = u->pc; p->sig = sig; p->hdlr = hdlr; p->u = u; nstack++; u->pc = (unsigned long) notecont; _NOTED(2); /* NSAVE: clear note but hold state */ } static void notecont(Ureg *u, char *s) { Pcstack *p; void(*f)(int, char*, Ureg*); p = &pcstack[nstack-1]; f = p->hdlr; u->pc = p->restorepc; nstack--; (*f)(p->sig, s, u); _NOTED(3); /* NRSTR */ } #define JMPBUFPC 1 #define JMPBUFSP 0 extern sigset_t _psigblocked; void siglongjmp(sigjmp_buf j, int ret) { struct Ureg *u; if(j[0]) _psigblocked = j[1]; if(nstack == 0 || pcstack[nstack-1].u->sp > j[2+JMPBUFSP]) longjmp(j+2, ret); u = pcstack[nstack-1].u; nstack--; u->r0 = ret; if(ret == 0) u->r0 = 1; u->pc = j[2+JMPBUFPC]; u->sp = j[2+JMPBUFSP]; _NOTED(3); /* NRSTR */ } /sys/src/ape/lib/ap/arm/setjmp.s 664 sys sys 1367613437 587 arg=0 link=14 sp=13 TEXT setjmp(SB), 1, $-4 MOVW R(sp), (R(arg+0)) MOVW R(link), 4(R(arg+0)) MOVW $0, R0 RET TEXT sigsetjmp(SB), 1, $-4 MOVW savemask+4(FP), R(arg+2) MOVW R(arg+2), 0(R(arg+0)) MOVW $_psigblocked(SB), R(arg+2) MOVW R2, 4(R(arg+0)) MOVW R(sp), 8(R(arg+0)) MOVW R(link), 12(R(arg+0)) MOVW $0, R(arg+0) RET TEXT longjmp(SB), 1, $-4 MOVW r+4(FP), R(arg+2) CMP $0, R(arg+2) BNE ok /* ansi: "longjmp(0) => longjmp(1)" */ MOVW $1, R(arg+2) /* bless their pointed heads */ ok: MOVW (R(arg+0)), R(sp) MOVW 4(R(arg+0)), R(link) MOVW R(arg+2), R(arg+0) RET /sys/src/ape/lib/ap/arm/strchr.s 664 sys sys 1367613437 841 TEXT strchr(SB), $-4 MOVBU c+4(FP), R1 CMP $0, R1 BEQ _null _strchr: /* not looking for a null, byte at a time */ MOVBU.P 1(R0), R2 CMP R1, R2 BEQ _sub1 CMP $0, R2 BNE _strchr _return0: /* character not found in string, return 0 */ MOVW $0, R0 RET _null: /* looking for null, align */ AND.S $3, R0, R2 BEQ _aligned MOVBU.P 1(R0), R4 CMP $0, R4 BEQ _sub1 B _null _aligned: MOVW $0xFF, R3 /* mask */ _loop: MOVW.P 4(R0), R4 /* 4 at a time */ TST R4, R3 /* AND.S R2, R3, Rx */ TST.NE R4>>8, R3 TST.NE R4>>16, R3 TST.NE R4>>24, R3 BNE _loop TST R4, R3 /* its somewhere, find it and correct */ BEQ _sub4 TST R4>>8, R3 BEQ _sub3 TST R4>>16, R3 BEQ _sub2 _sub1: /* compensate for pointer increment */ SUB $1, R0 RET _sub2: SUB $2, R0 RET _sub3: SUB $3, R0 RET _sub4: SUB $4, R0 RET /sys/src/ape/lib/ap/arm/strcmp.s 664 sys sys 1367613437 1010 TEXT strcmp(SB), $-4 MOVW R0, R1 MOVW s2+4(FP), R2 MOVW $0xFF, R3 /* mask */ _align: /* align s1 on 4 */ TST $3, R1 BEQ _aligned MOVBU.P 1(R1), R4 /* implicit write back */ MOVBU.P 1(R2), R8 /* implicit write back */ SUB.S R8, R4, R0 BNE _return CMP $0, R4 BEQ _return B _align _aligned: /* is s2 now aligned? */ TST $3, R2 BNE _unaligned _aloop: MOVW.P 4(R1), R5 /* 4 at a time */ MOVW.P 4(R2), R7 AND R5, R3, R4 AND R7, R3, R8 SUB.S R8, R4, R0 BNE _return CMP $0, R4 BEQ _return AND R5>>8, R3, R4 AND R7>>8, R3, R8 SUB.S R8, R4, R0 BNE _return CMP $0, R4 BEQ _return AND R5>>16, R3, R4 AND R7>>16, R3, R8 SUB.S R8, R4, R0 BNE _return CMP $0, R4 BEQ _return AND R5>>24, R3, R4 AND R7>>24, R3, R8 SUB.S R8, R4, R0 BNE _return CMP $0, R4 BEQ _return B _aloop _return: RET _unaligned: MOVBU.P 1(R1), R4 /* implicit write back */ MOVBU.P 1(R2), R8 /* implicit write back */ SUB.S R8, R4, R0 BNE _return CMP $0, R4 BEQ _return B _unaligned /sys/src/ape/lib/ap/arm/strcpy.s 664 sys sys 1367613437 885 TEXT strcpy(SB), $-4 MOVW R0, to+0(FP) /* need to save for return value */ MOVW from+4(FP), R1 MOVW $0xFF, R2 /* mask */ salign: /* align source on 4 */ AND.S $3, R1, R3 BEQ dalign MOVBU.P 1(R1), R3 /* implicit write back */ TST R3, R2 MOVBU.P R3, 1(R0) /* implicit write back */ BNE salign B return dalign: /* is destination now aligned? */ AND.S $3, R0, R3 BNE uloop aloop: MOVW.P 4(R1), R4 /* read 4, write 4 */ TST R4, R2 /* AND.S R3, R2, Rx */ TST.NE R4>>8, R2 TST.NE R4>>16, R2 TST.NE R4>>24, R2 BEQ tail MOVW.P R4, 4(R0) B aloop uloop: MOVW.P 4(R1), R4 /* read 4, write 1,1,1,1 */ tail: AND.S R4, R2, R3 MOVBU.NE.P R3, 1(R0) AND.NE.S R4>>8, R2, R3 MOVBU.NE.P R3, 1(R0) AND.NE.S R4>>16, R2, R3 MOVBU.NE.P R3, 1(R0) AND.NE.S R4>>24, R2, R3 MOVBU.P R3, 1(R0) BNE uloop B return return: MOVW to+0(FP), R0 RET /sys/src/ape/lib/ap/arm/tas.s 664 sys sys 1367613437 61 TEXT tas(SB), $-4 MOVW R0,R1 MOVW $1,R0 SWPW R0,(R1) RET /sys/src/ape/lib/ap/arm/vlop.s 664 sys sys 1367613437 262 TEXT _mulv(SB), $0 MOVW 4(FP),R8 /* l0 */ MOVW 8(FP),R11 /* h0 */ MOVW 12(FP),R4 /* l1 */ MOVW 16(FP),R5 /* h1 */ MULLU R8,R4,(R6, R7) /* l0*l1 */ MUL R8,R5,R5 /* l0*h1 */ MUL R11,R4,R4 /* h0*l1 */ ADD R4,R6 ADD R5,R6 MOVW R6,4(R0) MOVW R7,0(R0) RET /sys/src/ape/lib/ap/arm/vlrt.c 664 sys sys 1367613437 8947 typedef unsigned long ulong; typedef unsigned int uint; typedef unsigned short ushort; typedef unsigned char uchar; typedef signed char schar; #define SIGN(n) (1UL<<(n-1)) typedef struct Vlong Vlong; struct Vlong { ulong lo; ulong hi; }; void abort(void); /* needed by profiler; can't be profiled */ #pragma profile off void _addv(Vlong *r, Vlong a, Vlong b) { ulong lo, hi; lo = a.lo + b.lo; hi = a.hi + b.hi; if(lo < a.lo) hi++; r->lo = lo; r->hi = hi; } void _subv(Vlong *r, Vlong a, Vlong b) { ulong lo, hi; lo = a.lo - b.lo; hi = a.hi - b.hi; if(lo > a.lo) hi--; r->lo = lo; r->hi = hi; } #pragma profile on void _d2v(Vlong *y, double d) { union { double d; struct Vlong; } x; ulong xhi, xlo, ylo, yhi; int sh; x.d = d; xhi = (x.hi & 0xfffff) | 0x100000; xlo = x.lo; sh = 1075 - ((x.hi >> 20) & 0x7ff); ylo = 0; yhi = 0; if(sh >= 0) { /* v = (hi||lo) >> sh */ if(sh < 32) { if(sh == 0) { ylo = xlo; yhi = xhi; } else { ylo = (xlo >> sh) | (xhi << (32-sh)); yhi = xhi >> sh; } } else { if(sh == 32) { ylo = xhi; } else if(sh < 64) { ylo = xhi >> (sh-32); } } } else { /* v = (hi||lo) << -sh */ sh = -sh; if(sh <= 10) { ylo = xlo << sh; yhi = (xhi << sh) | (xlo >> (32-sh)); } else { /* overflow */ yhi = d; /* causes something awful */ } } if(x.hi & SIGN(32)) { if(ylo != 0) { ylo = -ylo; yhi = ~yhi; } else yhi = -yhi; } y->hi = yhi; y->lo = ylo; } void _f2v(Vlong *y, float f) { _d2v(y, f); } double _v2d(Vlong x) { if(x.hi & SIGN(32)) { if(x.lo) { x.lo = -x.lo; x.hi = ~x.hi; } else x.hi = -x.hi; return -((long)x.hi*4294967296. + x.lo); } return (long)x.hi*4294967296. + x.lo; } float _v2f(Vlong x) { return _v2d(x); } static void dodiv(Vlong num, Vlong den, Vlong *q, Vlong *r) { ulong numlo, numhi, denhi, denlo, quohi, quolo, t; int i; numhi = num.hi; numlo = num.lo; denhi = den.hi; denlo = den.lo; /* * get a divide by zero */ if(denlo==0 && denhi==0) { numlo = numlo / denlo; } /* * set up the divisor and find the number of iterations needed */ if(numhi >= SIGN(32)) { quohi = SIGN(32); quolo = 0; } else { quohi = numhi; quolo = numlo; } i = 0; while(denhi < quohi || (denhi == quohi && denlo < quolo)) { denhi = (denhi<<1) | (denlo>>31); denlo <<= 1; i++; } quohi = 0; quolo = 0; for(; i >= 0; i--) { quohi = (quohi<<1) | (quolo>>31); quolo <<= 1; if(numhi > denhi || (numhi == denhi && numlo >= denlo)) { t = numlo; numlo -= denlo; if(numlo > t) numhi--; numhi -= denhi; quolo |= 1; } denlo = (denlo>>1) | (denhi<<31); denhi >>= 1; } if(q) { q->lo = quolo; q->hi = quohi; } if(r) { r->lo = numlo; r->hi = numhi; } } void _divvu(Vlong *q, Vlong n, Vlong d) { if(n.hi == 0 && d.hi == 0) { q->hi = 0; q->lo = n.lo / d.lo; return; } dodiv(n, d, q, 0); } void _modvu(Vlong *r, Vlong n, Vlong d) { if(n.hi == 0 && d.hi == 0) { r->hi = 0; r->lo = n.lo % d.lo; return; } dodiv(n, d, 0, r); } static void vneg(Vlong *v) { if(v->lo == 0) { v->hi = -v->hi; return; } v->lo = -v->lo; v->hi = ~v->hi; } void _divv(Vlong *q, Vlong n, Vlong d) { long nneg, dneg; if(n.hi == (((long)n.lo)>>31) && d.hi == (((long)d.lo)>>31)) { q->lo = (long)n.lo / (long)d.lo; q->hi = ((long)q->lo) >> 31; return; } nneg = n.hi >> 31; if(nneg) vneg(&n); dneg = d.hi >> 31; if(dneg) vneg(&d); dodiv(n, d, q, 0); if(nneg != dneg) vneg(q); } void _modv(Vlong *r, Vlong n, Vlong d) { long nneg, dneg; if(n.hi == (((long)n.lo)>>31) && d.hi == (((long)d.lo)>>31)) { r->lo = (long)n.lo % (long)d.lo; r->hi = ((long)r->lo) >> 31; return; } nneg = n.hi >> 31; if(nneg) vneg(&n); dneg = d.hi >> 31; if(dneg) vneg(&d); dodiv(n, d, 0, r); if(nneg) vneg(r); } void _rshav(Vlong *r, Vlong a, int b) { long t; t = a.hi; if(b >= 32) { r->hi = t>>31; if(b >= 64) { /* this is illegal re C standard */ r->lo = t>>31; return; } r->lo = t >> (b-32); return; } if(b <= 0) { r->hi = t; r->lo = a.lo; return; } r->hi = t >> b; r->lo = (t << (32-b)) | (a.lo >> b); } void _rshlv(Vlong *r, Vlong a, int b) { ulong t; t = a.hi; if(b >= 32) { r->hi = 0; if(b >= 64) { /* this is illegal re C standard */ r->lo = 0; return; } r->lo = t >> (b-32); return; } if(b <= 0) { r->hi = t; r->lo = a.lo; return; } r->hi = t >> b; r->lo = (t << (32-b)) | (a.lo >> b); } void _lshv(Vlong *r, Vlong a, int b) { ulong t; t = a.lo; if(b >= 32) { r->lo = 0; if(b >= 64) { /* this is illegal re C standard */ r->hi = 0; return; } r->hi = t << (b-32); return; } if(b <= 0) { r->lo = t; r->hi = a.hi; return; } r->lo = t << b; r->hi = (t >> (32-b)) | (a.hi << b); } void _andv(Vlong *r, Vlong a, Vlong b) { r->hi = a.hi & b.hi; r->lo = a.lo & b.lo; } void _orv(Vlong *r, Vlong a, Vlong b) { r->hi = a.hi | b.hi; r->lo = a.lo | b.lo; } void _xorv(Vlong *r, Vlong a, Vlong b) { r->hi = a.hi ^ b.hi; r->lo = a.lo ^ b.lo; } void _vpp(Vlong *l, Vlong *r) { l->hi = r->hi; l->lo = r->lo; r->lo++; if(r->lo == 0) r->hi++; } void _vmm(Vlong *l, Vlong *r) { l->hi = r->hi; l->lo = r->lo; if(r->lo == 0) r->hi--; r->lo--; } void _ppv(Vlong *l, Vlong *r) { r->lo++; if(r->lo == 0) r->hi++; l->hi = r->hi; l->lo = r->lo; } void _mmv(Vlong *l, Vlong *r) { if(r->lo == 0) r->hi--; r->lo--; l->hi = r->hi; l->lo = r->lo; } void _vasop(Vlong *ret, void *lv, void fn(Vlong*, Vlong, Vlong), int type, Vlong rv) { Vlong t, u; u = *ret; switch(type) { default: abort(); break; case 1: /* schar */ t.lo = *(schar*)lv; t.hi = t.lo >> 31; fn(&u, t, rv); *(schar*)lv = u.lo; break; case 2: /* uchar */ t.lo = *(uchar*)lv; t.hi = 0; fn(&u, t, rv); *(uchar*)lv = u.lo; break; case 3: /* short */ t.lo = *(short*)lv; t.hi = t.lo >> 31; fn(&u, t, rv); *(short*)lv = u.lo; break; case 4: /* ushort */ t.lo = *(ushort*)lv; t.hi = 0; fn(&u, t, rv); *(ushort*)lv = u.lo; break; case 9: /* int */ t.lo = *(int*)lv; t.hi = t.lo >> 31; fn(&u, t, rv); *(int*)lv = u.lo; break; case 10: /* uint */ t.lo = *(uint*)lv; t.hi = 0; fn(&u, t, rv); *(uint*)lv = u.lo; break; case 5: /* long */ t.lo = *(long*)lv; t.hi = t.lo >> 31; fn(&u, t, rv); *(long*)lv = u.lo; break; case 6: /* ulong */ t.lo = *(ulong*)lv; t.hi = 0; fn(&u, t, rv); *(ulong*)lv = u.lo; break; case 7: /* vlong */ case 8: /* uvlong */ fn(&u, *(Vlong*)lv, rv); *(Vlong*)lv = u; break; } *ret = u; } void _p2v(Vlong *ret, void *p) { long t; t = (ulong)p; ret->lo = t; ret->hi = 0; } void _sl2v(Vlong *ret, long sl) { long t; t = sl; ret->lo = t; ret->hi = t >> 31; } void _ul2v(Vlong *ret, ulong ul) { long t; t = ul; ret->lo = t; ret->hi = 0; } void _si2v(Vlong *ret, int si) { long t; t = si; ret->lo = t; ret->hi = t >> 31; } void _ui2v(Vlong *ret, uint ui) { long t; t = ui; ret->lo = t; ret->hi = 0; } void _sh2v(Vlong *ret, long sh) { long t; t = (sh << 16) >> 16; ret->lo = t; ret->hi = t >> 31; } void _uh2v(Vlong *ret, ulong ul) { long t; t = ul & 0xffff; ret->lo = t; ret->hi = 0; } void _sc2v(Vlong *ret, long uc) { long t; t = (uc << 24) >> 24; ret->lo = t; ret->hi = t >> 31; } void _uc2v(Vlong *ret, ulong ul) { long t; t = ul & 0xff; ret->lo = t; ret->hi = 0; } long _v2sc(Vlong rv) { long t; t = rv.lo & 0xff; return (t << 24) >> 24; } long _v2uc(Vlong rv) { return rv.lo & 0xff; } long _v2sh(Vlong rv) { long t; t = rv.lo & 0xffff; return (t << 16) >> 16; } long _v2uh(Vlong rv) { return rv.lo & 0xffff; } long _v2sl(Vlong rv) { return rv.lo; } long _v2ul(Vlong rv) { return rv.lo; } long _v2si(Vlong rv) { return rv.lo; } long _v2ui(Vlong rv) { return rv.lo; } int _testv(Vlong rv) { return rv.lo || rv.hi; } int _eqv(Vlong lv, Vlong rv) { return lv.lo == rv.lo && lv.hi == rv.hi; } int _nev(Vlong lv, Vlong rv) { return lv.lo != rv.lo || lv.hi != rv.hi; } int _ltv(Vlong lv, Vlong rv) { return (long)lv.hi < (long)rv.hi || (lv.hi == rv.hi && lv.lo < rv.lo); } int _lev(Vlong lv, Vlong rv) { return (long)lv.hi < (long)rv.hi || (lv.hi == rv.hi && lv.lo <= rv.lo); } int _gtv(Vlong lv, Vlong rv) { return (long)lv.hi > (long)rv.hi || (lv.hi == rv.hi && lv.lo > rv.lo); } int _gev(Vlong lv, Vlong rv) { return (long)lv.hi > (long)rv.hi || (lv.hi == rv.hi && lv.lo >= rv.lo); } int _lov(Vlong lv, Vlong rv) { return lv.hi < rv.hi || (lv.hi == rv.hi && lv.lo < rv.lo); } int _lsv(Vlong lv, Vlong rv) { return lv.hi < rv.hi || (lv.hi == rv.hi && lv.lo <= rv.lo); } int _hiv(Vlong lv, Vlong rv) { return lv.hi > rv.hi || (lv.hi == rv.hi && lv.lo > rv.lo); } int _hsv(Vlong lv, Vlong rv) { return lv.hi > rv.hi || (lv.hi == rv.hi && lv.lo >= rv.lo); } /sys/src/ape/lib/ap/extraobjs 775 sys sys 1367613437 1356 #!/bin/rc switch($objtype){ case mips echo G_strcat.$O 9_strchr.$O 9_strcmp.$O 9_strcpy.$O G_strlen.$O\ G_strncmp.$O G_strrchr.$O 9_memchr.$O 9_memcmp.$O 9_memmove.$O\ 9_memset.$O 9_getfcr.$O 9_vlop.$O 9_vlrt.$O case 68020 echo 9_strcat.$O 9_strchr.$O 9_strcmp.$O 9_strcpy.$O 9_strlen.$O\ G_strncmp.$O G_strrchr.$O 9_memchr.$O 9_memcmp.$O 9_memcpy.$O\ 9_memmove.$O 9_memset.$O 9_vlop.$O 9_vlrt.$O case 386 echo 9_strcat.$O 9_strchr.$O G_strcmp.$O 9_strcpy.$O 9_strlen.$O\ G_strncmp.$O G_strrchr.$O 9_memchr.$O 9_memcmp.$O 9_memcpy.$O\ 9_memmove.$O 9_memset.$O 9_vlop.$O 9_vlrt.$O case arm echo 9_strcat.$O 9_strchr.$O G_strcmp.$O 9_strcpy.$O 9_strlen.$O\ G_strncmp.$O G_strrchr.$O 9_memchr.$O 9_memcmp.$O\ 9_memmove.$O 9_memset.$O 9_vlop.$O 9_vlrt.$O 9_div.$O case sparc echo G_strcat.$O 9_strchr.$O 9_strcmp.$O 9_strcpy.$O G_strlen.$O\ G_strncmp.$O G_strrchr.$O 9_memchr.$O 9_memcmp.$O 9_memmove.$O\ 9_memset.$O 9_muldiv.$O 9_vlop.$O 9_vlrt.$O case alpha echo G_strcat.$O G_strchr.$O G_strcmp.$O G_strcpy.$O G_strlen.$O\ G_strncmp.$O G_strrchr.$O G_memchr.$O G_memcmp.$O 9_memmove.$O\ 9_memset.$O 9_memcpy.$O 9_getfcr.$O 9_divl.$O 9_divq.$O case power echo G_strcat.$O G_strchr.$O G_strcmp.$O G_strcpy.$O G_strlen.$O\ G_strncmp.$O G_strrchr.$O G_memchr.$O 9_memcmp.$O 9_memmove.$O\ 9_memset.$O 9_getfcr.$O 9_vlop.$O 9_vlrt.$O } /sys/src/ape/lib/ap/gen 20000000775 sys sys 1369861527 0 /sys/src/ape/lib/ap/gen/_assert.c 664 sys sys 1367613437 347 #include #include #include void _assert(char *f, unsigned line) { char buf[20], *p, *s = &buf[20]; write(2, "assertion failed: file ", 23); for(p = f; *p; p++) continue; write(2, f, p-f); write(2, ":", 7); *--s = '\n'; do *--s = line%10 + '0'; while (line /= 10); write(2, s, &buf[20] - s); abort(); } /sys/src/ape/lib/ap/gen/abort.c 664 sys sys 1367613437 111 #include #include #include void abort(void) { kill(getpid(), SIGABRT); } /sys/src/ape/lib/ap/gen/abs.c 664 sys sys 1367613437 133 #include int abs(int a) { if(a < 0) return -a; return a; } long labs(long a) { if(a < 0) return -a; return a; } /sys/src/ape/lib/ap/gen/atof.c 664 sys sys 1367613437 84 #include double atof(const char *s) { return(strtod(s, (char **)0)); } /sys/src/ape/lib/ap/gen/atoi.c 664 sys sys 1367613437 85 #include int atoi(const char *s) { return(strtol(s, (char **)0, 10)); } /sys/src/ape/lib/ap/gen/atol.c 664 sys sys 1367613437 86 #include long atol(const char *s) { return(strtol(s, (char **)0, 10)); } /sys/src/ape/lib/ap/gen/atoll.c 664 sys sys 1367613437 93 #include long long atoll(const char *s) { return(strtoll(s, (char **)0, 10)); } /sys/src/ape/lib/ap/gen/bsearch.c 664 sys sys 1367613437 423 #include #include void* bsearch(const void* key, const void* base, size_t nmemb, size_t size, int (*compar)(const void*, const void*)) { long i, bot, top, new; void *p; bot = 0; top = bot + nmemb - 1; while(bot <= top){ new = (top + bot)/2; p = (char *)base+new*size; i = (*compar)(key, p); if(i == 0) return p; if(i > 0) bot = new + 1; else top = new - 1; } return 0; } /sys/src/ape/lib/ap/gen/calloc.c 664 sys sys 1367613437 180 #include #include void * calloc(size_t nmemb, size_t size) { void *mp; nmemb = nmemb*size; if(mp = malloc(nmemb)) memset(mp, 0, nmemb); return(mp); } /sys/src/ape/lib/ap/gen/clock.c 664 sys sys 1367613437 167 #include #include clock_t clock(void) { struct tms t; if(times(&t) == (clock_t)-1) return (clock_t)-1; return t.tms_utime+t.tms_stime; } /sys/src/ape/lib/ap/gen/ctype.c 664 sys sys 1367613437 2400 #include unsigned char _ctype[] = { /* 0 1 2 3 */ /* 4 5 6 7 */ /* 0*/ _IScntrl, _IScntrl, _IScntrl, _IScntrl, _IScntrl, _IScntrl, _IScntrl, _IScntrl, /* 10*/ _IScntrl, _ISspace|_IScntrl, _ISspace|_IScntrl, _ISspace|_IScntrl, _ISspace|_IScntrl, _ISspace|_IScntrl, _IScntrl, _IScntrl, /* 20*/ _IScntrl, _IScntrl, _IScntrl, _IScntrl, _IScntrl, _IScntrl, _IScntrl, _IScntrl, /* 30*/ _IScntrl, _IScntrl, _IScntrl, _IScntrl, _IScntrl, _IScntrl, _IScntrl, _IScntrl, /* 40*/ _ISspace|_ISblank, _ISpunct, _ISpunct, _ISpunct, _ISpunct, _ISpunct, _ISpunct, _ISpunct, /* 50*/ _ISpunct, _ISpunct, _ISpunct, _ISpunct, _ISpunct, _ISpunct, _ISpunct, _ISpunct, /* 60*/ _ISdigit|_ISxdigit, _ISdigit|_ISxdigit, _ISdigit|_ISxdigit, _ISdigit|_ISxdigit, _ISdigit|_ISxdigit, _ISdigit|_ISxdigit, _ISdigit|_ISxdigit, _ISdigit|_ISxdigit, /* 70*/ _ISdigit|_ISxdigit, _ISdigit|_ISxdigit, _ISpunct, _ISpunct, _ISpunct, _ISpunct, _ISpunct, _ISpunct, /*100*/ _ISpunct, _ISupper|_ISxdigit, _ISupper|_ISxdigit, _ISupper|_ISxdigit, _ISupper|_ISxdigit, _ISupper|_ISxdigit, _ISupper|_ISxdigit, _ISupper, /*110*/ _ISupper, _ISupper, _ISupper, _ISupper, _ISupper, _ISupper, _ISupper, _ISupper, /*120*/ _ISupper, _ISupper, _ISupper, _ISupper, _ISupper, _ISupper, _ISupper, _ISupper, /*130*/ _ISupper, _ISupper, _ISupper, _ISpunct, _ISpunct, _ISpunct, _ISpunct, _ISpunct, /*140*/ _ISpunct, _ISlower|_ISxdigit, _ISlower|_ISxdigit, _ISlower|_ISxdigit, _ISlower|_ISxdigit, _ISlower|_ISxdigit, _ISlower|_ISxdigit, _ISlower, /*150*/ _ISlower, _ISlower, _ISlower, _ISlower, _ISlower, _ISlower, _ISlower, _ISlower, /*160*/ _ISlower, _ISlower, _ISlower, _ISlower, _ISlower, _ISlower, _ISlower, _ISlower, /*170*/ _ISlower, _ISlower, _ISlower, _ISpunct, _ISpunct, _ISpunct, _ISpunct, _IScntrl, /*200*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; /sys/src/ape/lib/ap/gen/difftime.c 664 sys sys 1367613437 148 #include /* Difference in seconds between two calendar times */ double difftime(time_t t1, time_t t0) { return (double)t1-(double)t0; } /sys/src/ape/lib/ap/gen/div.c 664 sys sys 1367613437 170 #include div_t div(int numer, int denom) { div_t ans; ans.quot=numer/denom; /* assumes division truncates */ ans.rem=numer-ans.quot*denom; return ans; } /sys/src/ape/lib/ap/gen/getenv.c 664 sys sys 1369166818 279 #include extern char **environ; char * getenv(const char *name) { char **p = environ; char *s1, *s2; while (*p != NULL){ for(s1 = (char *)name, s2 = *p++; *s1 == *s2; s1++, s2++) continue; if(*s1 == '\0' && *s2 == '=') return s2+1; } return NULL ; } /sys/src/ape/lib/ap/gen/isalnum.c 664 sys sys 1367613437 869 #include #undef isalnum #undef isalpha #undef iscntrl #undef isdigit #undef isgraph #undef islower #undef isprint #undef ispunct #undef isspace #undef isupper #undef isxdigit int isalnum(int c){ return (_ctype+1)[c]&(_ISupper|_ISlower|_ISdigit); } int isalpha(int c){ return (_ctype+1)[c]&(_ISupper|_ISlower); } int iscntrl(int c){ return (_ctype+1)[c]&_IScntrl; } int isdigit(int c){ return (_ctype+1)[c]&_ISdigit; } int isgraph(int c){ return (_ctype+1)[c]&(_ISpunct|_ISupper|_ISlower|_ISdigit); } int islower(int c){ return (_ctype+1)[c]&_ISlower; } int isprint(int c){ return (_ctype+1)[c]&(_ISpunct|_ISupper|_ISlower|_ISdigit|_ISblank); } int ispunct(int c){ return (_ctype+1)[c]&_ISpunct; } int isspace(int c){ return (_ctype+1)[c]&_ISspace; } int isupper(int c){ return (_ctype+1)[c]&_ISupper; } int isxdigit(int c){ return (_ctype+1)[c]&_ISxdigit; } /sys/src/ape/lib/ap/gen/itoa.c 664 sys sys 1367613437 85 #include int itoa(const char *s) { return(strtol(s, (char **)0, 10)); } /sys/src/ape/lib/ap/gen/itol.c 664 sys sys 1367613437 86 #include long itol(const char *s) { return(strtol(s, (char **)0, 10)); } /sys/src/ape/lib/ap/gen/ldiv.c 664 sys sys 1367613437 149 #include ldiv_t ldiv(long int numer, long int denom) { ldiv_t ans; ans.quot=numer/denom; ans.rem=numer-ans.quot*denom; return ans; } /sys/src/ape/lib/ap/gen/mbwc.c 664 sys sys 1369166818 2811 #include #include /* * Use the FSS-UTF transformation proposed by posix. * We define 7 byte types: * T0 0xxxxxxx 7 free bits * Tx 10xxxxxx 6 free bits * T1 110xxxxx 5 free bits * T2 1110xxxx 4 free bits * T3 11110xxx 3 free bits * * Encoding is as follows. * From hex Thru hex Sequence Bits * 00000000 0000007F T1 7 * 00000080 000007FF T2 Tx 11 * 00000800 0000FFFF T3 Tx Tx 16 * 00010000 0010FFFF T4 Tx Tx Tx 20 (and change) */ int mblen(const char *s, size_t n) { return mbtowc(0, s, n); } int mbtowc(wchar_t *pwc, const char *s, size_t n) { int c, c1, c2, c3; long l; if(!s) return 0; if(n < 1) goto bad; c = s[0] & 0xff; if((c & 0x80) == 0x00) { if(pwc) *pwc = c; if(c == 0) return 0; return 1; } if(n < 2) goto bad; c1 = (s[1] ^ 0x80) & 0xff; if((c1 & 0xC0) != 0x00) goto bad; if((c & 0xE0) == 0xC0) { l = ((c << 6) | c1) & 0x7FF; if(l < 0x080) goto bad; if(pwc) *pwc = l; return 2; } if(n < 3) goto bad; c2 = (s[2] ^ 0x80) & 0xff; if((c2 & 0xC0) != 0x00) goto bad; if((c & 0xF0) == 0xE0) { l = ((((c << 6) | c1) << 6) | c2) & 0xFFFF; if(l < 0x0800) goto bad; if(pwc) *pwc = l; return 3; } if(n < 4) goto bad; if(UTFmax >= 4) { c3 = (s[3] ^ 0x80) & 0xff; if(c3 & 0xC0) goto bad; if(c < 0xf8) { l = ((((((c << 6) | c1) << 6) | c2) << 6) | c3) & 0x3fffff; if(l <= 0x10000) goto bad; if(l > Runemax) goto bad; if(pwc) *pwc = l; return 4; } } /* * bad decoding */ bad: return -1; } int wctomb(char *s, wchar_t wchar) { long c; if(!s) return 0; c = wchar; if(c > Runemax) c = Runeerror; if(c < 0x80) { s[0] = c; return 1; } if(c < 0x800) { s[0] = 0xC0 | (c >> 6); s[1] = 0x80 | (c & 0x3F); return 2; } if(c < 0x10000){ s[0] = 0xE0 | (c >> 12); s[1] = 0x80 | ((c >> 6) & 0x3F); s[2] = 0x80 | (c & 0x3F); return 3; } s[0] = 0xf0 | c >> 18; s[1] = 0x80 | (c >> 12) & 0x3F; s[2] = 0x80 | (c >> 6) & 0x3F; s[3] = 0x80 | (c & 0x3F); return 4; } size_t mbstowcs(wchar_t *pwcs, const char *s, size_t n) { int i, d, c; for(i=0; i < n; i++) { c = *s & 0xff; if(c < 0x80) { *pwcs = c; if(c == 0) break; s++; } else { d = mbtowc(pwcs, s, UTFmax); if(d <= 0) return (size_t)((d<0) ? -1 : i); s += d; } pwcs++; } return i; } size_t wcstombs(char *s, const wchar_t *pwcs, size_t n) { int i, d; long c; char *p, *pe; char buf[UTFmax]; p = s; pe = p+n-UTFmax; while(p < pe) { c = *pwcs++; if(c < 0x80) *p++ = c; else p += wctomb(p, c); if(c == 0) return p-s; } while(p < pe+UTFmax) { c = *pwcs++; d = wctomb(buf, c); if(p+d <= pe+UTFmax) { for(i = 0; i < d; i++) p[i] = buf[i]; p += d; } if(c == 0) break; } return p-s; } /sys/src/ape/lib/ap/gen/memccpy.c 664 sys sys 1367613437 212 #include void* memccpy(void *a1, void *a2, int c, size_t n) { unsigned char *s1, *s2; s1 = a1; s2 = a2; c &= 0xFF; while(n > 0) { if((*s1++ = *s2++) == c) return s1; n--; } return 0; } /sys/src/ape/lib/ap/gen/memchr.c 664 sys sys 1369166818 199 #include void* memchr(const void *ap, int c, size_t n) { const unsigned char *sp; sp = ap; c &= 0xFF; while(n > 0) { if(*sp++ == c) return (void*)(sp-1); n--; } return 0; } /sys/src/ape/lib/ap/gen/memcmp.c 664 sys sys 1369166818 268 #include int memcmp(const void *a1, const void *a2, size_t n) { const char *s1, *s2; unsigned c1, c2; s1 = a1; s2 = a2; while(n > 0) { c1 = *s1++; c2 = *s2++; if(c1 != c2) { if(c1 > c2) return 1; return -1; } n--; } return 0; } /sys/src/ape/lib/ap/gen/memmove.c 664 sys sys 1369166818 440 #include void* memmove(void *a1, const void *a2, size_t n) { char *s1, *s2; extern void abort(void); if((long)n < 0) abort(); if(a1 > a2) goto back; s1 = a1; s2 = (char*)a2; while(n > 0) { *s1++ = *s2++; n--; } return a1; back: s1 = (char*)a1 + n; s2 = (char*)a2 + n; while(n > 0) { *--s1 = *--s2; n--; } return a1; } void* memcpy(void *a1, const void *a2, size_t n) { return memmove(a1, a2, n); } /sys/src/ape/lib/ap/gen/memset.c 664 sys sys 1367613437 135 #include void* memset(void *ap, int c, size_t n) { char *p; p = ap; while(n > 0) { *p++ = c; n--; } return ap; } /sys/src/ape/lib/ap/gen/mkfile 664 sys sys 1369185886 852 APE=/sys/src/ape <$APE/config LIB=/$objtype/lib/ape/libap.a ALLOFILES=\ _assert.$O\ abort.$O\ abs.$O\ atof.$O\ atoi.$O\ atol.$O\ atoll.$O\ bsearch.$O\ calloc.$O\ clock.$O\ ctype.$O\ difftime.$O\ div.$O\ getenv.$O\ isalnum.$O\ itoa.$O\ itol.$O\ ldiv.$O\ mbwc.$O\ memccpy.$O\ memchr.$O\ memcmp.$O\ memmove.$O\ memset.$O\ mktime.$O\ qsort.$O\ raise.$O\ rand.$O\ strcat.$O\ strchr.$O\ strcmp.$O\ strcoll.$O\ strcpy.$O\ strcspn.$O\ strdup.$O\ strftime.$O\ strlen.$O\ strncat.$O\ strncmp.$O\ strncpy.$O\ strpbrk.$O\ strrchr.$O\ strspn.$O\ strstr.$O\ strtod.$O\ strtok.$O\ strtol.$O\ strtoll.$O\ strtoul.$O\ strtoull.$O\ strxfrm.$O\ toupper.$O\ # cull things in the per-machine directories from this list OFILES= `{rc ./reduce $O $objtype $ALLOFILES} /* * BUG: Doesn't do leap years in full glory, * or calendar changes. In 2038 the sign bit * will be needed in time_t, but we say it * can't be represented. */ static int dysize(int y) { y += 1900; /* arg is a tm_year, number of years since 1900 */ if((y%4) == 0 && ((y%100) !=0 || (y%400) == 0)) return 366; return 365; } static int dmsize(int m, int y) { static char sizes[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; if(m == 1) return (dysize(y)==366)? 29 : 28; else return sizes[m]; } /* Reduce *v to [0, mult), adding 1 to *next for every mult * subtracted from *v, and return 1 if reduction worked (no overflow) */ static int reduce(int *v, int *next, int mult) { int oldnext; while(*v < 0){ *v += mult; oldnext = *next; --*next; if(!(*next < oldnext)) return 0; } while(*v >= mult){ *v -= mult; oldnext = *next; ++*next; if(!(*next > oldnext)) return 0; } return 1; } static int jan1(int yr) { int y, d; y = yr+1900; d = (4+y+(y+3)/4-(y-1701)/100+(y-1601)/400+3)%7; return d; } time_t mktime(struct tm *t) { time_t a; int i, d; struct tm *ptm; if(!(reduce(&t->tm_sec, &t->tm_min, 60) && reduce(&t->tm_min, &t->tm_hour, 60) && reduce(&t->tm_hour, &t->tm_mday, 24) && reduce(&t->tm_mon, &t->tm_year, 12))) return -1; while(t->tm_mday < 1){ if(--t->tm_mon == -1){ t->tm_mon = 11; t->tm_year--; } t->tm_mday += dmsize(t->tm_mon, t->tm_year); } while(t->tm_mday > dmsize(t->tm_mon, t->tm_year)){ t->tm_mday -= dmsize(t->tm_mon, t->tm_year); if(++t->tm_mon == 12){ t->tm_mon = 0; t->tm_year++; } } a = t->tm_sec + 60*t->tm_min + 3600*t->tm_hour; t->tm_yday = t->tm_mday-1; for(i=0; itm_mon; i++) t->tm_yday += dmsize(i, t->tm_year); a += t->tm_yday*86400L; if(t->tm_year < 70){ for(i=t->tm_year; i<70; i++) if((a -= dysize(i)*86400L) < 0) return -1; }else if(t->tm_year > 70){ for(i=70; itm_year; i++) if((a += dysize(i)*86400L) < 0) return -1; } /* * Now a is number of seconds past Jan 1 1970. * Convert to GMT. */ ptm = gmtime(&a); d = ptm->tm_hour; ptm = localtime(&a); d -= ptm->tm_hour; if(d < 0) d += 24; if(t->tm_isdst == 0 && ptm->tm_isdst) d--; if(t->tm_isdst > 0 && !ptm->tm_isdst) d++; a += d*3600; t->tm_wday = (jan1(t->tm_year)+t->tm_yday)%7; return a; } /sys/src/ape/lib/ap/gen/qsort.c 664 sys sys 1369773416 2913 /* qsort -- qsort interface implemented by faster quicksort */ #define _SUSV2_SOURCE #include #include #define SWAPINIT(a, es) swaptype = \ ((uintptr_t)a - 0) % sizeof(long) || es % sizeof(long) ? 2 : \ es == sizeof(long) ? 0 : 1; #define swapcode(TYPE, parmi, parmj, n) { \ long i = (n) / (int) sizeof(TYPE); \ TYPE *pi = (TYPE *) (parmi); \ TYPE *pj = (TYPE *) (parmj); \ do { \ TYPE t = *pi; \ *pi++ = *pj; \ *pj++ = t; \ } while (--i > 0); \ } static void swapfunc(char *a, char *b, int n, int swaptype) { if (swaptype <= 1) swapcode(long, a, b, n) else swapcode(char, a, b, n) } #define swap(a, b) { \ if (swaptype == 0) { \ long t = * (long *) (a); \ * (long *) (a) = * (long *) (b); \ * (long *) (b) = t; \ } else \ swapfunc(a, b, es, swaptype); \ } #define vecswap(a, b, n) { if (n > 0) swapfunc(a, b, n*es, swaptype); } static char *med3func(char *a, char *b, char *c, int (*cmp)(const void *, const void *)) { return cmp(a, b) < 0 ? (cmp(b, c) < 0 ? b : (cmp(a, c) < 0 ? c : a ) ) : (cmp(b, c) > 0 ? b : (cmp(a, c) < 0 ? a : c ) ); } #define med3(a, b, c) med3func(a, b, c, cmp) void qsort(void *va, size_t n, size_t es, int (*cmp)(const void *, const void *)) { char *a, *pa, *pb, *pc, *pd, *pl, *pm, *pn; int r, swaptype, na, nb, nc, nd, d; a = va; SWAPINIT(a, es); if (n < 7) { /* Insertion sort on small arrays */ for (pm = a + es; pm < a + n*es; pm += es) for (pl = pm; pl > a && cmp(pl-es, pl) > 0; pl -= es) swap(pl, pl-es); return; } pm = a + (n/2) * es; if (n > 7) { pl = a; pn = a + (n-1) * es; if (n > 40) { /* On big arrays, pseudomedian of 9 */ d = (n/8) * es; pl = med3(pl, pl+d, pl+2*d); pm = med3(pm-d, pm, pm+d); pn = med3(pn-2*d, pn-d, pn); } pm = med3(pl, pm, pn); /* On medium arrays, median of 3 */ } swap(a, pm); /* On tiny arrays, partition around middle */ pa = pb = a + es; pc = pd = pn = a + (n-1)*es; for (;;) { while (pb <= pc && (r = cmp(pb, a)) <= 0) { if (r == 0) { swap(pa, pb); pa += es; } pb += es; } while (pb <= pc && (r = cmp(pc, a)) >= 0) { if (r == 0) { swap(pc, pd); pd -= es; } pc -= es; } if (pb > pc) break; swap(pb, pc); pb += es; pc -= es; } na = (pa - a) / es; nb = (pb - pa) / es; nc = (pd - pc) / es; nd = (pn - pd) / es; if (na < nb) { vecswap(a, a + nb*es, na); } else { vecswap(a, a + na*es, nb); } if (nc < nd) { vecswap(pb, pb + nd*es, nc); } else { vecswap(pb, pb + nc*es, nd); } if (nb > 1) qsort(a, nb, es, cmp); if (nc > 1) qsort(a + (n-nc) * es, nc, es, cmp); } /sys/src/ape/lib/ap/gen/raise.c 664 sys sys 1367613437 185 /* not a posix function, but implemented with posix kill, getpid */ #include #include #include int raise(int sig) { return kill(getpid(), sig); } /sys/src/ape/lib/ap/gen/rand.c 664 sys sys 1369166818 1109 #include /* * algorithm by * D. P. Mitchell & J. A. Reeds */ #define LEN 607 #define TAP 273 #define MASK 0x7fffffffL #define A 48271 #define M 2147483647 #define Q 44488 #define R 3399 typedef unsigned long ulong; static ulong rng_vec[LEN]; static ulong* rng_tap = rng_vec; static ulong* rng_feed = 0; void srand(unsigned int seed) { long lo, hi, x; int i; rng_tap = rng_vec; rng_feed = rng_vec+LEN-TAP; seed = seed%M; // if(seed < 0) // seed += M; if(seed == 0) seed = 89482311; x = seed; /* * Initialize by x[n+1] = 48271 * x[n] mod (2**31 - 1) */ for(i = -20; i < LEN; i++) { hi = x / Q; lo = x % Q; x = A*lo - R*hi; if(x < 0) x += M; if(i >= 0) rng_vec[i] = x; } } static long lrand(void) { ulong x; rng_tap--; if(rng_tap < rng_vec) { if(rng_feed == 0) { srand(1); rng_tap--; } rng_tap += LEN; } rng_feed--; if(rng_feed < rng_vec) rng_feed += LEN; x = (*rng_feed + *rng_tap) & MASK; *rng_feed = x; return x; } int rand(void) { return lrand() & 0x7fff; } /sys/src/ape/lib/ap/gen/reduce 664 sys sys 1367613437 313 O=$1 shift objtype=$1 shift ls -p ../$objtype/*.[cs] >[2]/dev/null | sed 's/..$//;s/^/^/' > /tmp/reduce.$pid # # if empty directory, just return the input files # if (! ~ $status '|') { echo $* rm /tmp/reduce.$pid exit 0 } echo $* | tr ' ' \012 | grep -v -f /tmp/reduce.$pid | tr \012 ' ' rm /tmp/reduce.$pid /sys/src/ape/lib/ap/gen/strcat.c 664 sys sys 1367613437 105 #include char* strcat(char *s1, const char *s2) { strcpy(strchr(s1, 0), s2); return s1; } /sys/src/ape/lib/ap/gen/strchr.c 664 sys sys 1367613437 180 #include char* strchr(const char *s, int c) { char c1; if(c == 0) { while(*s++) ; return s-1; } while(c1 = *s++) if(c1 == c) return s-1; return 0; } /sys/src/ape/lib/ap/gen/strcmp.c 664 sys sys 1367613437 220 #include int strcmp(const char *s1, const char *s2) { unsigned c1, c2; for(;;) { c1 = *s1++; c2 = *s2++; if(c1 != c2) { if(c1 > c2) return 1; return -1; } if(c1 == 0) return 0; } } /sys/src/ape/lib/ap/gen/strcoll.c 664 sys sys 1367613437 163 #include int strcoll(const char *s1, const char *s2) { /* BUG: supposed to pay attention to LC_COLLATE of current locale */ return strcmp(s1, s2); } /sys/src/ape/lib/ap/gen/strcpy.c 664 sys sys 1367613437 355 #include #define N 10000 static void* pmemccpy(void *a1, void *a2, int c, size_t n) { char *s1, *s2; s1 = a1; s2 = a2; while(n > 0) { if((*s1++ = *s2++) == c) return s1; n--; } return 0; } char* strcpy(char *s1, const char *s2) { char *os1; os1 = s1; while(!pmemccpy(s1, s2, 0, N)) { s1 += N; s2 += N; } return os1; } /sys/src/ape/lib/ap/gen/strcspn.c 664 sys sys 1369166818 280 #include #define N 256 size_t strcspn(const char *s, const char *b) { char map[N]; const char *os; memset(map, 0, N); for(;;) { map[*(unsigned char*)b] = 1; if(*b++ == 0) break; } os = s; while(map[*(unsigned char*)s++] == 0) ; return s - os - 1; } /sys/src/ape/lib/ap/gen/strdup.c 664 sys sys 1367613437 182 #include #include #include char* strdup(char *p) { int n; char *np; n = strlen(p)+1; np = malloc(n); if(np) memmove(np, p, n); return np; } /sys/src/ape/lib/ap/gen/strftime.c 664 sys sys 1367613437 4030 #include #include static char *awday[7] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; static char *wday[7] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}; static char *amon[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; static char *mon[12] = {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}; static char *ampm[2] = {"AM", "PM"}; static char *tz[2] = {"EST", "EDT"}; static int jan1(int); static char *strval(char *, char *, char **, int, int); static char *dval(char *, char *, int, int); size_t strftime(char *s, size_t maxsize, const char *format, const struct tm *t) { char *sp, *se, *fp; int i; sp = s; se = s+maxsize; for(fp=(char *)format; *fp && sptm_wday, 7); break; case 'A': sp = strval(sp, se, wday, t->tm_wday, 7); break; case 'b': sp = strval(sp, se, amon, t->tm_mon, 12); break; case 'B': sp = strval(sp, se, mon, t->tm_mon, 12); break; case 'c': sp += strftime(sp, se-sp, "%a %b %d %H:%M:%S %Y", t); break; case 'd': sp = dval(sp, se, t->tm_mday, 2); break; case 'H': sp = dval(sp, se, t->tm_hour, 2); break; case 'I': i = t->tm_hour; if(i == 0) i = 12; else if(i > 12) i -= 12; sp = dval(sp, se, i, 2); break; case 'j': sp = dval(sp, se, t->tm_yday+1, 3); break; case 'm': sp = dval(sp, se, t->tm_mon+1, 2); break; case 'M': sp = dval(sp, se, t->tm_min, 2); break; case 'p': i = (t->tm_hour < 12)? 0 : 1; sp = strval(sp, se, ampm, i, 2); break; case 'S': sp = dval(sp, se, t->tm_sec, 2); break; case 'U': i = 7-jan1(t->tm_year); if(i == 7) i = 0; /* Now i is yday number of first sunday in year */ if(t->tm_yday < i) i = 0; else i = (t->tm_yday-i)/7 + 1; sp = dval(sp, se, i, 2); break; case 'w': sp = dval(sp, se, t->tm_wday, 1); break; case 'W': i = 8-jan1(t->tm_year); if(i >= 7) i -= 7; /* Now i is yday number of first monday in year */ if(t->tm_yday < i) i = 0; else i = (t->tm_yday-i)/7 + 1; sp = dval(sp, se, i, 2); break; case 'x': sp += strftime(sp, se-sp, "%a %b %d, %Y", t); break; case 'X': sp += strftime(sp, se-sp, "%H:%M:%S", t); break; case 'y': sp = dval(sp, se, t->tm_year%100, 2); break; case 'Y': sp = dval(sp, se, t->tm_year+1900, 4); break; case 'Z': /* hack for now: assume eastern time zone */ i = t->tm_isdst? 1 : 0; sp = strval(sp, se, tz, i, 2); break; case 0: fp--; /* stop loop after next fp incr */ break; default: *sp++ = *fp; } } if(*fp) sp = s; /* format string didn't end: no room for conversion */ if(sp=alen){ *start = '?'; return start+1; } n = strlen(array[index]); if(n > end-start) n = end-start; memcpy(start, array[index], n); return start+n; } static char * dval(char *start, char *end, int val, int width) { char *p; if(val<0 || end-start=start){ *p-- = val%10 + '0'; val /= 10; } if(val>0) *start = '*'; return start+width; } /* * return day of the week * of jan 1 of given year */ static int jan1(int yr) { int y, d; /* * normal gregorian calendar * one extra day per four years */ y = yr+1900; d = 4+y+(y+3)/4; /* * julian calendar * regular gregorian * less three days per 400 */ if(y > 1800) { d -= (y-1701)/100; d += (y-1601)/400; } /* * great calendar changeover instant */ if(y > 1752) d += 3; return(d%7); } /sys/src/ape/lib/ap/gen/strlen.c 664 sys sys 1367613437 81 #include size_t strlen(const char *s) { return strchr(s, 0) - s; } /sys/src/ape/lib/ap/gen/strncat.c 664 sys sys 1367613437 224 #include char* strncat(char *s1, const char *s2, size_t n) { char *os1; long nn; os1 = s1; nn = n; while(*s1++) ; s1--; while(*s1++ = *s2++) if(--nn < 0) { s1[-1] = 0; break; } return os1; } /sys/src/ape/lib/ap/gen/strncmp.c 664 sys sys 1367613437 272 #include int strncmp(const char *s1, const char *s2, size_t n) { unsigned c1, c2; long nn; nn = n; while(nn > 0) { c1 = *s1++; c2 = *s2++; nn--; if(c1 != c2) { if(c1 > c2) return 1; return -1; } if(c1 == 0) break; } return 0; } /sys/src/ape/lib/ap/gen/strncpy.c 664 sys sys 1367613437 225 #include char* strncpy(char *s1, const char *s2, size_t n) { int i; char *os1; os1 = s1; for(i = 0; i < n; i++) if((*s1++ = *s2++) == 0) { while(++i < n) *s1++ = 0; return os1; } return os1; } /sys/src/ape/lib/ap/gen/strpbrk.c 664 sys sys 1369166818 240 #include #define N 256 char* strpbrk(const char *s, const char *b) { char map[N]; memset(map, 0, N); for(;;) { map[*b] = 1; if(*b++ == 0) break; } while(map[*s++] == 0) ; if(*--s) return (char*)s; return 0; } /sys/src/ape/lib/ap/gen/strrchr.c 664 sys sys 1369166818 175 #include char* strrchr(const char *s, int c) { const char *r; if(c == 0) return strchr(s, 0); r = 0; while(s = strchr(s, c)) r = s++; return (char*)r; } /sys/src/ape/lib/ap/gen/strspn.c 664 sys sys 1369166818 249 #include #define N 256 size_t strspn(const char *s, const char *b) { char map[N]; const char *os; memset(map, 0, N); while(*b) map[*(unsigned char *)b++] = 1; os = s; while(map[*(unsigned char *)s++]) ; return s - os - 1; } /sys/src/ape/lib/ap/gen/strstr.c 664 sys sys 1369166818 396 #include /* Return pointer to first occurrence of s2 in s1, NULL if none */ char *strstr(const char *s1, const char *s2) { char *p, *pa, *pb; int c0, c; c0 = *s2; if(c0 == 0) return (char*)s1; s2++; for(p=strchr(s1, c0); p; p=strchr(p+1, c0)) { pa = p; for(pb=(char*)s2;; pb++) { c = *pb; if(c == 0) return p; if(c != *++pa) break; } } return 0; } /sys/src/ape/lib/ap/gen/strtod.c 664 sys sys 1367613437 1434 #include #include /* * bug: should detect overflow, set errno = ERANGE, and return +/- HUGE_VAL */ double strtod(const char *cp, char **endptr) { double num, dem; extern double pow10(int); int neg, eneg, dig, predig, exp, c; const char *p; p = cp; num = 0; neg = 0; dig = 0; predig = 0; exp = 0; eneg = 0; c = *p++; while(c == ' ' || c == '\t' || c == '\n' || c == '\f' || c == '\v' || c == '\r') c = *p++; if(c == '-' || c == '+'){ if(c == '-') neg = 1; c = *p++; } while(c >= '0' && c <= '9'){ num = num*10 + c-'0'; predig++; c = *p++; } if(c == '.') c = *p++; while(c >= '0' && c <= '9'){ num = num*10 + c-'0'; dig++; c = *p++; } if(dig+predig == 0){ if(endptr) *endptr = (char *)cp; return 0.0; } if(c == 'e' || c == 'E'){ c = *p++; if(c == '-' || c == '+'){ if(c == '-'){ dig = -dig; eneg = 1; } c = *p++; } while(c >= '0' && c <= '9'){ exp = exp*10 + c-'0'; c = *p++; } } exp -= dig; if(exp < 0){ exp = -exp; eneg = !eneg; } dem = pow10(exp); if(dem==HUGE_VAL) num = eneg? 0.0 : HUGE_VAL; else if(dem==0) num = eneg? HUGE_VAL : 0.0; else if(eneg) num /= dem; else num *= dem; if(neg) num = -num; if(endptr){ *endptr = (char *)--p; /* * Fix cases like 2.3e+ */ while(p > cp){ c = *--p; if(c!='-' && c!='+' && c!='e' && c!='E') break; (*endptr)--; } } return num; } /sys/src/ape/lib/ap/gen/strtok.c 664 bootes sys 1212357942 505 #include #define N 256 char* strtok_r(char *s, const char *b, char **last) { char map[N], *os; memset(map, 0, N); while(*b) map[*(unsigned char*)b++] = 1; if(s == 0) s = *last; while(map[*(unsigned char*)s++]) ; if(*--s == 0) return 0; os = s; while(map[*(unsigned char*)s] == 0) if(*s++ == 0) { *last = s-1; return os; } *s++ = 0; *last = s; return os; } char* strtok(char *s, const char *b) { static char *under_rock; return strtok_r(s, b, &under_rock); } /sys/src/ape/lib/ap/gen/strtol.c 664 sys sys 1367613437 1223 #include #include #include long strtol(const char *nptr, char **endptr, int base) { const char *p; long n, nn; int c, ovfl, v, neg, ndig; p = nptr; neg = 0; n = 0; ndig = 0; ovfl = 0; /* * White space */ for(;;p++){ switch(*p){ case ' ': case '\t': case '\n': case '\f': case '\r': case '\v': continue; } break; } /* * Sign */ if(*p=='-' || *p=='+') if(*p++ == '-') neg = 1; /* * Base */ if(base==0){ if(*p != '0') base = 10; else{ base = 8; if(p[1]=='x' || p[1]=='X'){ p += 2; base = 16; } } }else if(base==16 && *p=='0'){ if(p[1]=='x' || p[1]=='X') p += 2; }else if(base<0 || 36= base) break; nn = n*base + v; if(nn < n) ovfl = 1; n = nn; } Return: if(ndig == 0) p = nptr; if(endptr) *endptr = (char *)p; if(ovfl){ errno = ERANGE; if(neg) return LONG_MIN; return LONG_MAX; } if(neg) return -n; return n; } /sys/src/ape/lib/ap/gen/strtoll.c 664 sys sys 1367613437 1326 #include #include #include #define VLONG_MAX ~(1LL<<63) #define VLONG_MIN (1LL<<63) long long strtoll(char *nptr, char **endptr, int base) { char *p; long long n, nn, m; int c, ovfl, v, neg, ndig; p = nptr; neg = 0; n = 0; ndig = 0; ovfl = 0; /* * White space */ for(;; p++) { switch(*p) { case ' ': case '\t': case '\n': case '\f': case '\r': case '\v': continue; } break; } /* * Sign */ if(*p=='-' || *p=='+') if(*p++ == '-') neg = 1; /* * Base */ if(base==0){ base = 10; if(*p == '0') { base = 8; if(p[1]=='x' || p[1]=='X') { p += 2; base = 16; } } } else if(base==16 && *p=='0') { if(p[1]=='x' || p[1]=='X') p += 2; } else if(base<0 || 36= base) break; if(n > m) ovfl = 1; nn = n*base + v; if(nn < n) ovfl = 1; n = nn; } Return: if(ndig == 0) p = nptr; if(endptr) *endptr = p; if(ovfl){ errno = ERANGE; if(neg) return VLONG_MIN; return VLONG_MAX; } if(neg) return -n; return n; } /sys/src/ape/lib/ap/gen/strtoul.c 664 sys sys 1367613437 1220 #include #include #include unsigned long strtoul(const char *nptr, char **endptr, int base) { const char *p; unsigned long n, nn; int c, ovfl, neg, v, ndig; p = (char*)nptr; neg = 0; n = 0; ndig = 0; ovfl = 0; /* * White space */ for(;;p++){ switch(*p){ case ' ': case '\t': case '\n': case '\f': case '\r': case '\v': continue; } break; } /* * Sign */ if(*p=='-' || *p=='+') if(*p++ == '-') neg = 1; /* * Base */ if(base==0){ if(*p != '0') base = 10; else{ base = 8; if(p[1]=='x' || p[1]=='X'){ p += 2; base = 16; } } }else if(base==16 && *p=='0'){ if(p[1]=='x' || p[1]=='X') p += 2; }else if(base<0 || 36= base) break; nn = n*base + v; if(nn < n) ovfl = 1; n = nn; } Return: if(ndig == 0) p = nptr; if(endptr) *endptr = (char *)p; if(ovfl){ errno = ERANGE; return ULONG_MAX; } if(neg) return -n; return n; } /sys/src/ape/lib/ap/gen/strtoull.c 664 sys sys 1367613437 1322 #include #include #include #define UVLONG_MAX (1LL<<63) unsigned long long strtoull(char *nptr, char **endptr, int base) { char *p; unsigned long long n, nn, m; int c, ovfl, v, neg, ndig; p = nptr; neg = 0; n = 0; ndig = 0; ovfl = 0; /* * White space */ for(;; p++) { switch(*p) { case ' ': case '\t': case '\n': case '\f': case '\r': case '\v': continue; } break; } /* * Sign */ if(*p == '-' || *p == '+') if(*p++ == '-') neg = 1; /* * Base */ if(base == 0) { base = 10; if(*p == '0') { base = 8; if(p[1] == 'x' || p[1] == 'X'){ p += 2; base = 16; } } } else if(base == 16 && *p == '0') { if(p[1] == 'x' || p[1] == 'X') p += 2; } else if(base < 0 || 36 < base) goto Return; /* * Non-empty sequence of digits */ m = UVLONG_MAX/base; for(;; p++,ndig++) { c = *p; v = base; if('0' <= c && c <= '9') v = c - '0'; else if('a' <= c && c <= 'z') v = c - 'a' + 10; else if('A' <= c && c <= 'Z') v = c - 'A' + 10; if(v >= base) break; if(n > m) ovfl = 1; nn = n*base + v; if(nn < n) ovfl = 1; n = nn; } Return: if(ndig == 0) p = nptr; if(endptr) *endptr = p; if(ovfl){ errno = ERANGE; return UVLONG_MAX; } if(neg) return -n; return n; } /sys/src/ape/lib/ap/gen/strxfrm.c 664 sys sys 1367613437 308 #include size_t strxfrm(char *s1, const char *s2, size_t n) { /* * BUG: supposed to transform s2 to a canonical form * so that strcmp can be used instead of strcoll, but * our strcoll just uses strcmp. */ size_t xn = strlen(s2); if(n > xn) n = xn; memcpy(s1, s2, n); return xn; } /sys/src/ape/lib/ap/gen/toupper.c 664 sys sys 1367613437 175 #include toupper(int c) { if(c < 'a' || c > 'z') return c; return (c-'a'+'A'); } tolower(int c) { if(c < 'A' || c > 'Z') return c; return (c-'A'+'a'); } /sys/src/ape/lib/ap/math 20000000775 sys sys 1369861527 0 /sys/src/ape/lib/ap/math/asin.c 664 sys sys 1367613437 647 /* asin(arg) and acos(arg) return the arcsin, arccos, respectively of their arguments. Arctan is called after appropriate range reduction. */ #include #include static double pio2 = 1.570796326794896619231e0; double asin(double arg) { double temp; int sign; sign = 0; if(arg < 0) { arg = -arg; sign++; } if(arg > 1) { errno = EDOM; return 0; } temp = sqrt(1 - arg*arg); if(arg > 0.7) temp = pio2 - atan(temp/arg); else temp = atan(arg/temp); if(sign) temp = -temp; return temp; } double acos(double arg) { if(arg > 1 || arg < -1) { errno = EDOM; return 0; } return pio2 - asin(arg); } /sys/src/ape/lib/ap/math/atan.c 664 sys sys 1367613437 1786 /* floating-point arctangent atan returns the value of the arctangent of its argument in the range [-pi/2,pi/2]. atan2 returns the arctangent of arg1/arg2 in the range [-pi,pi]. there are no error returns. coefficients are #5077 from Hart & Cheney. (19.56D) */ #include #define sq2p1 2.414213562373095048802e0 #define sq2m1 .414213562373095048802e0 #define pio2 1.570796326794896619231e0 #define pio4 .785398163397448309615e0 #define p4 .161536412982230228262e2 #define p3 .26842548195503973794141e3 #define p2 .11530293515404850115428136e4 #define p1 .178040631643319697105464587e4 #define p0 .89678597403663861959987488e3 #define q4 .5895697050844462222791e2 #define q3 .536265374031215315104235e3 #define q2 .16667838148816337184521798e4 #define q1 .207933497444540981287275926e4 #define q0 .89678597403663861962481162e3 /* xatan evaluates a series valid in the range [-0.414...,+0.414...]. */ static double xatan(double arg) { double argsq, value; /* get denormalized add in following if range arg**10 is much smaller than q1, so check for that case */ if(-.01 < arg && arg < .01) value = p0/q0; else { argsq = arg*arg; value = ((((p4*argsq + p3)*argsq + p2)*argsq + p1)*argsq + p0); value = value/(((((argsq + q4)*argsq + q3)*argsq + q2)*argsq + q1)*argsq + q0); } return value*arg; } /* satan reduces its argument (known to be positive) to the range [0,0.414...] and calls xatan. */ static double satan(double arg) { if(arg < sq2m1) return xatan(arg); if(arg > sq2p1) return pio2 - xatan(1.0/arg); return pio4 + xatan((arg-1.0)/(arg+1.0)); } /* atan makes its argument positive and calls the inner routine satan. */ double atan(double arg) { if(arg > 0) return satan(arg); return -satan(-arg); } /sys/src/ape/lib/ap/math/atan2.c 664 sys sys 1367613437 492 #include #include /* atan2 discovers what quadrant the angle is in and calls atan. */ #define pio2 1.5707963267948966192313217 #define pi 3.1415926535897932384626434; double atan2(double arg1, double arg2) { if(arg1 == 0.0 && arg2 == 0.0){ errno = EDOM; return 0.0; } if(arg1+arg2 == arg1) { if(arg1 >= 0) return pio2; return -pio2; } arg1 = atan(arg1/arg2); if(arg2 < 0) { if(arg1 <= 0) return arg1 + pi; return arg1 - pi; } return arg1; } /sys/src/ape/lib/ap/math/erf.c 664 sys sys 1367613437 2356 #include #include /* C program for floating point error function erf(x) returns the error function of its argument erfc(x) returns 1 - erf(x) erf(x) is defined by ${2 over sqrt(pi)} int from 0 to x e sup {-t sup 2} dt$ the entry for erfc is provided because of the extreme loss of relative accuracy if erf(x) is called for large x and the result subtracted from 1. (e.g. for x= 10, 12 places are lost). There are no error returns. Calls exp. Coefficients for large x are #5667 from Hart & Cheney (18.72D). */ #define M 7 #define N 9 static double torp = 1.1283791670955125738961589031; static double p1[] = { 0.804373630960840172832162e5, 0.740407142710151470082064e4, 0.301782788536507577809226e4, 0.380140318123903008244444e2, 0.143383842191748205576712e2, -.288805137207594084924010e0, 0.007547728033418631287834e0, }; static double q1[] = { 0.804373630960840172826266e5, 0.342165257924628539769006e5, 0.637960017324428279487120e4, 0.658070155459240506326937e3, 0.380190713951939403753468e2, 0.100000000000000000000000e1, 0.0, }; static double p2[] = { 0.18263348842295112592168999e4, 0.28980293292167655611275846e4, 0.2320439590251635247384768711e4, 0.1143262070703886173606073338e4, 0.3685196154710010637133875746e3, 0.7708161730368428609781633646e2, 0.9675807882987265400604202961e1, 0.5641877825507397413087057563e0, 0.0, }; static double q2[] = { 0.18263348842295112595576438e4, 0.495882756472114071495438422e4, 0.60895424232724435504633068e4, 0.4429612803883682726711528526e4, 0.2094384367789539593790281779e4, 0.6617361207107653469211984771e3, 0.1371255960500622202878443578e3, 0.1714980943627607849376131193e2, 1.0, }; double erfc(double); double erf(double arg) { int sign; double argsq; double d, n; int i; errno = 0; sign = 1; if(arg < 0) { arg = -arg; sign = -1; } if(arg < 0.5) { argsq = arg*arg; for(n=0,d=0,i=M-1; i>=0; i--) { n = n*argsq + p1[i]; d = d*argsq + q1[i]; } return sign*torp*arg*n/d; } if(arg >= 10) return sign; return sign*(1 - erfc(arg)); } double erfc(double arg) { double n, d; int i; errno = 0; if(arg < 0) return 2 - erfc(-arg); if(arg < 0.5) return 1 - erf(arg); if(arg >= 10) return 0; for(n=0,d=0,i=N-1; i>=0; i--) { n = n*arg + p2[i]; d = d*arg + q2[i]; } return exp(-arg*arg)*n/d; } /sys/src/ape/lib/ap/math/exp.c 664 sys sys 1367613437 964 /* exp returns the exponential function of its floating-point argument. The coefficients are #1069 from Hart and Cheney. (22.35D) */ #include #include #define p0 .2080384346694663001443843411e7 #define p1 .3028697169744036299076048876e5 #define p2 .6061485330061080841615584556e2 #define q0 .6002720360238832528230907598e7 #define q1 .3277251518082914423057964422e6 #define q2 .1749287689093076403844945335e4 #define log2e 1.4426950408889634073599247 #define sqrt2 1.4142135623730950488016887 #define maxf 10000 double exp(double arg) { double fract, temp1, temp2, xsq; int ent; if(arg == 0) return 1; if(arg < -maxf) { errno = ERANGE; return 0; } if(arg > maxf) { errno = ERANGE; return HUGE_VAL; } arg *= log2e; ent = floor(arg); fract = (arg-ent) - 0.5; xsq = fract*fract; temp1 = ((p2*xsq+p1)*xsq+p0)*fract; temp2 = ((xsq+q2)*xsq+q1)*xsq + q0; return ldexp(sqrt2*(temp2+temp1)/(temp2-temp1), ent); } /sys/src/ape/lib/ap/math/fabs.c 664 sys sys 1367613437 89 #include double fabs(double arg) { if(arg < 0) return -arg; return arg; } /sys/src/ape/lib/ap/math/floor.c 664 sys sys 1367613437 290 #include /* * floor and ceil-- greatest integer <= arg * (resp least >=) */ double floor(double d) { double fract; if(d < 0) { fract = modf(-d, &d); if(fract != 0.0) d += 1; d = -d; } else modf(d, &d); return d; } double ceil(double d) { return -floor(-d); } /sys/src/ape/lib/ap/math/fmod.c 664 sys sys 1367613437 428 /* floating-point mod function without infinity or NaN checking */ #include double fmod (double x, double y) { int sign = 0, yexp; double r, yfr; if (y == 0) return 0; if (y < 0) y = -y; yfr = frexp (y, &yexp); if (x < 0) { sign = 1; r = -x; } else r = x; while (r >= y) { int rexp; double rfr = frexp (r, &rexp); r -= ldexp (y, rexp - yexp - (rfr < yfr)); } if (sign) r = -r; return r; } /sys/src/ape/lib/ap/math/gamma.c 664 sys sys 1367613437 1955 #include #include /* C program for floating point log gamma function gamma(x) computes the log of the absolute value of the gamma function. The sign of the gamma function is returned in the external quantity signgam. The coefficients for expansion around zero are #5243 from Hart & Cheney; for expansion around infinity they are #5404. Calls log and sin. */ int signgam; static double goobie = 0.9189385332046727417803297; static double pi = 3.1415926535897932384626434; #define M 6 #define N 8 static double p1[] = { 0.83333333333333101837e-1, -.277777777735865004e-2, 0.793650576493454e-3, -.5951896861197e-3, 0.83645878922e-3, -.1633436431e-2, }; static double p2[] = { -.42353689509744089647e5, -.20886861789269887364e5, -.87627102978521489560e4, -.20085274013072791214e4, -.43933044406002567613e3, -.50108693752970953015e2, -.67449507245925289918e1, 0.0, }; static double q2[] = { -.42353689509744090010e5, -.29803853309256649932e4, 0.99403074150827709015e4, -.15286072737795220248e4, -.49902852662143904834e3, 0.18949823415702801641e3, -.23081551524580124562e2, 0.10000000000000000000e1, }; static double asym(double arg) { double n, argsq; int i; argsq = 1 / (arg*arg); n = 0; for(i=M-1; i>=0; i--) n = n*argsq + p1[i]; return (arg-.5)*log(arg) - arg + goobie + n/arg; } static double pos(double arg) { double n, d, s; int i; if(arg < 2) return pos(arg+1)/arg; if(arg > 3) return (arg-1)*pos(arg-1); s = arg - 2; n = 0; d = 0; for(i=N-1; i>=0; i--){ n = n*s + p2[i]; d = d*s + q2[i]; } return n/d; } static double neg(double arg) { double temp; arg = -arg; temp = sin(pi*arg); if(temp == 0) { errno = EDOM; return HUGE_VAL; } if(temp < 0) temp = -temp; else signgam = -1; return -log(arg*pos(arg)*temp/pi); } double gamma(double arg) { signgam = 1; if(arg <= 0) return neg(arg); if(arg > 8) return asym(arg); return log(pos(arg)); } /sys/src/ape/lib/ap/math/hypot.c 664 sys sys 1367613437 325 #include /* * sqrt(a^2 + b^2) * (but carefully) */ double hypot(double a, double b) { double t; if(a < 0) a = -a; if(b < 0) b = -b; if(a > b) { t = a; a = b; b = t; } if(b == 0) return 0; a /= b; /* * pathological overflow possible * in the next line. */ return b * sqrt(1 + a*a); } /sys/src/ape/lib/ap/math/j0.c 664 sys sys 1367613437 4216 #include #include /* floating point Bessel's function of the first and second kinds of order zero j0(x) returns the value of J0(x) for all real values of x. There are no error returns. Calls sin, cos, sqrt. There is a niggling bug in J0 which causes errors up to 2e-16 for x in the interval [-8,8]. The bug is caused by an inappropriate order of summation of the series. rhm will fix it someday. Coefficients are from Hart & Cheney. #5849 (19.22D) #6549 (19.25D) #6949 (19.41D) y0(x) returns the value of Y0(x) for positive real values of x. For x<=0, error number EDOM is set and a large negative value is returned. Calls sin, cos, sqrt, log, j0. The values of Y0 have not been checked to more than ten places. Coefficients are from Hart & Cheney. #6245 (18.78D) #6549 (19.25D) #6949 (19.41D) */ static double pzero, qzero; static double tpi = .6366197723675813430755350535e0; static double pio4 = .7853981633974483096156608458e0; static double p1[] = { 0.4933787251794133561816813446e21, -.1179157629107610536038440800e21, 0.6382059341072356562289432465e19, -.1367620353088171386865416609e18, 0.1434354939140344111664316553e16, -.8085222034853793871199468171e13, 0.2507158285536881945555156435e11, -.4050412371833132706360663322e8, 0.2685786856980014981415848441e5, }; static double q1[] = { 0.4933787251794133562113278438e21, 0.5428918384092285160200195092e19, 0.3024635616709462698627330784e17, 0.1127756739679798507056031594e15, 0.3123043114941213172572469442e12, 0.6699987672982239671814028660e9, 0.1114636098462985378182402543e7, 0.1363063652328970604442810507e4, 1.0 }; static double p2[] = { 0.5393485083869438325262122897e7, 0.1233238476817638145232406055e8, 0.8413041456550439208464315611e7, 0.2016135283049983642487182349e7, 0.1539826532623911470917825993e6, 0.2485271928957404011288128951e4, 0.0, }; static double q2[] = { 0.5393485083869438325560444960e7, 0.1233831022786324960844856182e8, 0.8426449050629797331554404810e7, 0.2025066801570134013891035236e7, 0.1560017276940030940592769933e6, 0.2615700736920839685159081813e4, 1.0, }; static double p3[] = { -.3984617357595222463506790588e4, -.1038141698748464093880530341e5, -.8239066313485606568803548860e4, -.2365956170779108192723612816e4, -.2262630641933704113967255053e3, -.4887199395841261531199129300e1, 0.0, }; static double q3[] = { 0.2550155108860942382983170882e6, 0.6667454239319826986004038103e6, 0.5332913634216897168722255057e6, 0.1560213206679291652539287109e6, 0.1570489191515395519392882766e5, 0.4087714673983499223402830260e3, 1.0, }; static double p4[] = { -.2750286678629109583701933175e20, 0.6587473275719554925999402049e20, -.5247065581112764941297350814e19, 0.1375624316399344078571335453e18, -.1648605817185729473122082537e16, 0.1025520859686394284509167421e14, -.3436371222979040378171030138e11, 0.5915213465686889654273830069e8, -.4137035497933148554125235152e5, }; static double q4[] = { 0.3726458838986165881989980e21, 0.4192417043410839973904769661e19, 0.2392883043499781857439356652e17, 0.9162038034075185262489147968e14, 0.2613065755041081249568482092e12, 0.5795122640700729537480087915e9, 0.1001702641288906265666651753e7, 0.1282452772478993804176329391e4, 1.0, }; static void asympt(double arg) { double zsq, n, d; int i; zsq = 64 / (arg*arg); for(n=0,d=0,i=6;i>=0;i--) { n = n*zsq + p2[i]; d = d*zsq + q2[i]; } pzero = n/d; for(n=0,d=0,i=6;i>=0;i--) { n = n*zsq + p3[i]; d = d*zsq + q3[i]; } qzero = (8/arg)*(n/d); } double j0(double arg) { double argsq, n, d; int i; if(arg < 0) arg = -arg; if(arg > 8) { asympt(arg); n = arg - pio4; return sqrt(tpi/arg)*(pzero*cos(n) - qzero*sin(n)); } argsq = arg*arg; for(n=0,d=0,i=8;i>=0;i--) { n = n*argsq + p1[i]; d = d*argsq + q1[i]; } return n/d; } double y0(double arg) { double argsq, n, d; int i; errno = 0; if(arg <= 0) { errno = EDOM; return(-HUGE_VAL); } if(arg > 8) { asympt(arg); n = arg - pio4; return sqrt(tpi/arg)*(pzero*sin(n) + qzero*cos(n)); } argsq = arg*arg; for(n=0,d=0,i=8;i>=0;i--) { n = n*argsq + p4[i]; d = d*argsq + q4[i]; } return n/d + tpi*j0(arg)*log(arg); } /sys/src/ape/lib/ap/math/j1.c 664 sys sys 1367613437 4284 #include #include /* floating point Bessel's function of the first and second kinds of order one j1(x) returns the value of J1(x) for all real values of x. There are no error returns. Calls sin, cos, sqrt. There is a niggling bug in J1 which causes errors up to 2e-16 for x in the interval [-8,8]. The bug is caused by an inappropriate order of summation of the series. rhm will fix it someday. Coefficients are from Hart & Cheney. #6050 (20.98D) #6750 (19.19D) #7150 (19.35D) y1(x) returns the value of Y1(x) for positive real values of x. For x<=0, error number EDOM is set and a large negative value is returned. Calls sin, cos, sqrt, log, j1. The values of Y1 have not been checked to more than ten places. Coefficients are from Hart & Cheney. #6447 (22.18D) #6750 (19.19D) #7150 (19.35D) */ static double pzero, qzero; static double tpi = .6366197723675813430755350535e0; static double pio4 = .7853981633974483096156608458e0; static double p1[] = { 0.581199354001606143928050809e21, -.6672106568924916298020941484e20, 0.2316433580634002297931815435e19, -.3588817569910106050743641413e17, 0.2908795263834775409737601689e15, -.1322983480332126453125473247e13, 0.3413234182301700539091292655e10, -.4695753530642995859767162166e7, 0.2701122710892323414856790990e4, }; static double q1[] = { 0.1162398708003212287858529400e22, 0.1185770712190320999837113348e20, 0.6092061398917521746105196863e17, 0.2081661221307607351240184229e15, 0.5243710262167649715406728642e12, 0.1013863514358673989967045588e10, 0.1501793594998585505921097578e7, 0.1606931573481487801970916749e4, 1.0, }; static double p2[] = { -.4435757816794127857114720794e7, -.9942246505077641195658377899e7, -.6603373248364939109255245434e7, -.1523529351181137383255105722e7, -.1098240554345934672737413139e6, -.1611616644324610116477412898e4, 0.0, }; static double q2[] = { -.4435757816794127856828016962e7, -.9934124389934585658967556309e7, -.6585339479723087072826915069e7, -.1511809506634160881644546358e7, -.1072638599110382011903063867e6, -.1455009440190496182453565068e4, 1.0, }; static double p3[] = { 0.3322091340985722351859704442e5, 0.8514516067533570196555001171e5, 0.6617883658127083517939992166e5, 0.1849426287322386679652009819e5, 0.1706375429020768002061283546e4, 0.3526513384663603218592175580e2, 0.0, }; static double q3[] = { 0.7087128194102874357377502472e6, 0.1819458042243997298924553839e7, 0.1419460669603720892855755253e7, 0.4002944358226697511708610813e6, 0.3789022974577220264142952256e5, 0.8638367769604990967475517183e3, 1.0, }; static double p4[] = { -.9963753424306922225996744354e23, 0.2655473831434854326894248968e23, -.1212297555414509577913561535e22, 0.2193107339917797592111427556e20, -.1965887462722140658820322248e18, 0.9569930239921683481121552788e15, -.2580681702194450950541426399e13, 0.3639488548124002058278999428e10, -.2108847540133123652824139923e7, 0.0, }; static double q4[] = { 0.5082067366941243245314424152e24, 0.5435310377188854170800653097e22, 0.2954987935897148674290758119e20, 0.1082258259408819552553850180e18, 0.2976632125647276729292742282e15, 0.6465340881265275571961681500e12, 0.1128686837169442121732366891e10, 0.1563282754899580604737366452e7, 0.1612361029677000859332072312e4, 1.0, }; static void asympt(double arg) { double zsq, n, d; int i; zsq = 64/(arg*arg); for(n=0,d=0,i=6;i>=0;i--) { n = n*zsq + p2[i]; d = d*zsq + q2[i]; } pzero = n/d; for(n=0,d=0,i=6;i>=0;i--) { n = n*zsq + p3[i]; d = d*zsq + q3[i]; } qzero = (8/arg)*(n/d); } double j1(double arg) { double xsq, n, d, x; int i; x = arg; if(x < 0) x = -x; if(x > 8) { asympt(x); n = x - 3*pio4; n = sqrt(tpi/x)*(pzero*cos(n) - qzero*sin(n)); if(arg < 0) n = -n; return n; } xsq = x*x; for(n=0,d=0,i=8;i>=0;i--) { n = n*xsq + p1[i]; d = d*xsq + q1[i]; } return arg*n/d; } double y1(double arg) { double xsq, n, d, x; int i; errno = 0; x = arg; if(x <= 0) { errno = EDOM; return -HUGE_VAL; } if(x > 8) { asympt(x); n = x - 3*pio4; return sqrt(tpi/x)*(pzero*sin(n) + qzero*cos(n)); } xsq = x*x; for(n=0,d=0,i=9;i>=0;i--) { n = n*xsq + p4[i]; d = d*xsq + q4[i]; } return x*n/d + tpi*(j1(x)*log(x)-1/x); } /sys/src/ape/lib/ap/math/jn.c 664 sys sys 1367613437 1751 #include #include /* floating point Bessel's function of the first and second kinds and of integer order. int n; double x; jn(n,x); returns the value of Jn(x) for all integer values of n and all real values of x. There are no error returns. Calls j0, j1. For n=0, j0(x) is called, for n=1, j1(x) is called, for nx, a continued fraction approximation to j(n,x)/j(n-1,x) is evaluated and then backward recursion is used starting from a supposed value for j(n,x). The resulting value of j(0,x) is compared with the actual value to correct the supposed value of j(n,x). yn(n,x) is similar in all respects, except that forward recursion is used for all values of n>1. */ double j0(double); double j1(double); double y0(double); double y1(double); double jn(int n, double x) { int i; double a, b, temp; double xsq, t; if(n < 0) { n = -n; x = -x; } if(n == 0) return j0(x); if(n == 1) return j1(x); if(x == 0) return 0; if(n > x) goto recurs; a = j0(x); b = j1(x); for(i=1; in; i--) t = xsq/(2*i - t); t = x/(2*n-t); a = t; b = 1; for(i=n-1; i>0; i--) { temp = b; b = (2*i/x)*b - a; a = temp; } return t*j0(x)/b; } double yn(int n, double x) { int i; int sign; double a, b, temp; if (x <= 0) { errno = EDOM; return -HUGE_VAL; } sign = 1; if(n < 0) { n = -n; if(n%2 == 1) sign = -1; } if(n == 0) return y0(x); if(n == 1) return sign*y1(x); a = y0(x); b = y1(x); for(i=1; i #include #define log2 0.693147180559945309e0 #define ln10o1 .4342944819032518276511 #define sqrto2 0.707106781186547524e0 #define p0 -.240139179559210510e2 #define p1 0.309572928215376501e2 #define p2 -.963769093377840513e1 #define p3 0.421087371217979714e0 #define q0 -.120069589779605255e2 #define q1 0.194809660700889731e2 #define q2 -.891110902798312337e1 double log(double arg) { double x, z, zsq, temp; int exp; if(arg <= 0) { errno = (arg==0)? ERANGE : EDOM; return -HUGE_VAL; } x = frexp(arg, &exp); while(x < 0.5) { x *= 2; exp--; } if(x < sqrto2) { x *= 2; exp--; } z = (x-1) / (x+1); zsq = z*z; temp = ((p3*zsq + p2)*zsq + p1)*zsq + p0; temp = temp/(((zsq + q2)*zsq + q1)*zsq + q0); temp = temp*z + exp*log2; return temp; } double log10(double arg) { if(arg <= 0) { errno = (arg==0)? ERANGE : EDOM; return -HUGE_VAL; } return log(arg) * ln10o1; } /sys/src/ape/lib/ap/math/mkfile 664 sys sys 1369773844 305 APE=/sys/src/ape <$APE/config LIB=/$objtype/lib/ape/libap.a OFILES=\ asin.$O\ atan.$O\ atan2.$O\ erf.$O\ exp.$O\ fabs.$O\ floor.$O\ fmod.$O\ gamma.$O\ hypot.$O\ j0.$O\ j1.$O\ jn.$O\ log.$O\ pow.$O\ sin.$O\ sinh.$O\ tan.$O\ tanh.$O\ #include /* modf suitable for IEEE double-precision */ #define MASK 0x7ffL #define SIGN 0x80000000 #define SHIFT 20 #define BIAS 1022L typedef union { double d; struct { long ms; long ls; } i; } Cheat; double modf(double d, double *ip) { Cheat x; int e; if(-1 < d && d < 1) { *ip = 0; return d; } x.d = d; x.i.ms &= ~SIGN; e = (x.i.ms >> SHIFT) & MASK; if(e == MASK || e == 0){ errno = EDOM; *ip = (d > 0)? HUGE_VAL : -HUGE_VAL; return 0; } e -= BIAS; if(e <= SHIFT+1) { x.i.ms &= ~(0x1fffffL >> e); x.i.ls = 0; } else if(e <= SHIFT+33) x.i.ls &= ~(0x7fffffffL >> (e-SHIFT-2)); if(d > 0){ *ip = x.d; return d - x.d; }else{ *ip = -x.d; return d + x.d; } } /sys/src/ape/lib/ap/math/pow.c 664 sys sys 1367613437 987 #include #include #include double pow(double x, double y) /* return x ^ y (exponentiation) */ { double xy, y1, ye; long i; int ex, ey, flip; if(y == 0.0) return 1.0; flip = 0; if(y < 0.){ y = -y; flip = 1; } y1 = modf(y, &ye); if(y1 != 0.0){ if(x <= 0.) goto zreturn; if(y1 > 0.5) { y1 -= 1.; ye += 1.; } xy = exp(y1 * log(x)); }else xy = 1.0; if(ye > LONG_MAX){ if(x <= 0.){ zreturn: if(x || flip) errno = EDOM; return 0.; } if(flip){ if(y == .5) return 1./sqrt(x); y = -y; }else if(y == .5) return sqrt(x); return exp(y * log(x)); } x = frexp(x, &ex); ey = 0; i = ye; if(i) for(;;){ if(i & 1){ xy *= x; ey += ex; } i >>= 1; if(i == 0) break; x *= x; ex <<= 1; if(x < .5){ x += x; ex -= 1; } } if(flip){ xy = 1. / xy; ey = -ey; } errno = 0; x = ldexp(xy, ey); if(errno && ey < 0) { errno = 0; x = 0.; } return x; } /sys/src/ape/lib/ap/math/sin.c 664 sys sys 1367613437 1234 /* C program for floating point sin/cos. Calls modf. There are no error exits. Coefficients are #3370 from Hart & Cheney (18.80D). */ #include #define PIO2 1.570796326794896619231e0 #define p0 .1357884097877375669092680e8 #define p1 -.4942908100902844161158627e7 #define p2 .4401030535375266501944918e6 #define p3 -.1384727249982452873054457e5 #define p4 .1459688406665768722226959e3 #define q0 .8644558652922534429915149e7 #define q1 .4081792252343299749395779e6 #define q2 .9463096101538208180571257e4 #define q3 .1326534908786136358911494e3 static double sinus(double arg, int quad) { double e, f, ysq, x, y, temp1, temp2; int k; x = arg; if(x < 0) { x = -x; quad += 2; } x *= 1/PIO2; /* underflow? */ if(x > 32764) { y = modf(x, &e); e += quad; modf(0.25*e, &f); quad = e - 4*f; } else { k = x; y = x - k; quad += k; quad &= 3; } if(quad & 1) y = 1-y; if(quad > 1) y = -y; ysq = y*y; temp1 = ((((p4*ysq+p3)*ysq+p2)*ysq+p1)*ysq+p0)*y; temp2 = ((((ysq+q3)*ysq+q2)*ysq+q1)*ysq+q0); return temp1/temp2; } double cos(double arg) { if(arg < 0) arg = -arg; return sinus(arg, 1); } double sin(double arg) { return sinus(arg, 0); } /sys/src/ape/lib/ap/math/sinh.c 664 sys sys 1367613437 1460 /* sinh(arg) returns the hyperbolic sine of its floating- point argument. The exponential function is called for arguments greater in magnitude than 0.5. A series is used for arguments smaller in magnitude than 0.5. The coefficients are #2029 from Hart & Cheney. (20.36D) cosh(arg) is computed from the exponential function for all arguments. */ #include #include static double p0 = -0.6307673640497716991184787251e+6; static double p1 = -0.8991272022039509355398013511e+5; static double p2 = -0.2894211355989563807284660366e+4; static double p3 = -0.2630563213397497062819489e+2; static double q0 = -0.6307673640497716991212077277e+6; static double q1 = 0.1521517378790019070696485176e+5; static double q2 = -0.173678953558233699533450911e+3; double sinh(double arg) { double temp, argsq; int sign; sign = 1; if(arg < 0) { arg = - arg; sign = -1; } if(arg > 21) { if(arg >= HUGE_VAL){ errno = ERANGE; temp = HUGE_VAL; } else temp = exp(arg)/2; if(sign > 0) return temp; else return -temp; } if(arg > 0.5) return sign*(exp(arg) - exp(-arg))/2; argsq = arg*arg; temp = (((p3*argsq+p2)*argsq+p1)*argsq+p0)*arg; temp /= (((argsq+q2)*argsq+q1)*argsq+q0); return sign*temp; } double cosh(double arg) { if(arg < 0) arg = - arg; if(arg > 21) { if(arg >= HUGE_VAL){ errno = ERANGE; return HUGE_VAL; } else return(exp(arg)/2); } return (exp(arg) + exp(-arg))/2; } /sys/src/ape/lib/ap/math/tan.c 664 sys sys 1367613437 1295 /* floating point tangent A series is used after range reduction. Coefficients are #4285 from Hart & Cheney. (19.74D) */ #include #include static double invpi = 1.27323954473516268; static double p0 = -0.1306820264754825668269611177e+5; static double p1 = 0.1055970901714953193602353981e+4; static double p2 = -0.1550685653483266376941705728e+2; static double p3 = 0.3422554387241003435328470489e-1; static double p4 = 0.3386638642677172096076369e-4; static double q0 = -0.1663895238947119001851464661e+5; static double q1 = 0.4765751362916483698926655581e+4; static double q2 = -0.1555033164031709966900124574e+3; double tan(double arg) { double sign, temp, e, x, xsq; int flag, i; flag = 0; sign = 1; if(arg < 0){ arg = -arg; sign = -1; } arg = arg*invpi; /* overflow? */ x = modf(arg, &e); i = e; switch(i%4) { case 1: x = 1 - x; flag = 1; break; case 2: sign = - sign; flag = 1; break; case 3: x = 1 - x; sign = - sign; break; case 0: break; } xsq = x*x; temp = ((((p4*xsq+p3)*xsq+p2)*xsq+p1)*xsq+p0)*x; temp = temp/(((xsq+q2)*xsq+q1)*xsq+q0); if(flag == 1) { if(temp == 0) { errno = EDOM; if (sign > 0) return HUGE_VAL; return -HUGE_VAL; } temp = 1/temp; } return sign*temp; } /sys/src/ape/lib/ap/math/tanh.c 664 sys sys 1367613437 374 #include /* tanh(arg) computes the hyperbolic tangent of its floating point argument. sinh and cosh are called except for large arguments, which would cause overflow improperly. */ double tanh(double arg) { if(arg < 0) { arg = -arg; if(arg > 21) return -1; return -sinh(arg)/cosh(arg); } if(arg > 21) return 1; return sinh(arg)/cosh(arg); } /sys/src/ape/lib/ap/mips 20000000775 sys sys 1368235096 0 /sys/src/ape/lib/ap/mips/cycles.c 664 sys sys 1367613437 49 void _cycles(unsigned long long *u) { *u = 0; } /sys/src/ape/lib/ap/mips/getfcr.s 664 sys sys 1367613437 167 TEXT getfsr(SB), $0 MOVW FCR31, R1 RET TEXT setfsr(SB), $0 MOVW R1, FCR31 RET TEXT getfcr(SB), $0 MOVW FCR31, R1 RET TEXT setfcr(SB), $0 MOVW R1, FCR31 RET /sys/src/ape/lib/ap/mips/lock.c 664 sys sys 1368232573 2440 #define _LOCK_EXTENSION #include #include #include "../plan9/sys9.h" #include enum { Pagesize = 4096, Semperpg = Pagesize/(16*sizeof(unsigned int)), Lockaddr = 0x60000000, POWER = 0x320, MAGNUM = 0x330, MAGNUMII = 0x340, R4K = 0x500, }; static int arch; extern int C_3ktas(int*); extern int C_4ktas(int*); extern int C_fcr0(void); static void lockinit(void) { void *v; if(arch != 0) return; /* allow multiple calls */ arch = C_fcr0(); switch(arch) { case POWER: v = (void*)Lockaddr; if(segattach(SG_CEXEC, "lock", v, Pagesize) == (void*)-1) { arch = MAGNUM; break; } memset((void*)Lockaddr, 0, Pagesize); break; case MAGNUM: case MAGNUMII: case R4K: break; default: abort(); } } void lock(Lock *lk) { int *hwsem; int hash; retry: switch(arch) { case 0: lockinit(); goto retry; case MAGNUM: case MAGNUMII: while(C_3ktas(&lk->val)) _SLEEP(0); return; case R4K: for(;;){ while(lk->val) ; if(C_4ktas(&lk->val) == 0) return; } break; case POWER: /* Use low order lock bits to generate hash */ hash = ((int)lk/sizeof(int)) & (Semperpg-1); hwsem = (int*)Lockaddr+hash; for(;;) { if((*hwsem & 1) == 0) { if(lk->val) *hwsem = 0; else { lk->val = 1; *hwsem = 0; return; } } while(lk->val) ; } } } int canlock(Lock *lk) { int *hwsem; int hash; retry: switch(arch) { case 0: lockinit(); goto retry; case MAGNUM: case MAGNUMII: if(C_3ktas(&lk->val)) return 0; return 1; case R4K: if(C_4ktas(&lk->val)) return 0; return 1; case POWER: /* Use low order lock bits to generate hash */ hash = ((int)lk/sizeof(int)) & (Semperpg-1); hwsem = (int*)Lockaddr+hash; if((*hwsem & 1) == 0) { if(lk->val) *hwsem = 0; else { lk->val = 1; *hwsem = 0; return 1; } } return 0; default: return 0; } } void unlock(Lock *lk) { lk->val = 0; } int tas(int *p) { int *hwsem; int hash; retry: switch(arch) { case 0: lockinit(); goto retry; case MAGNUM: case MAGNUMII: return C_3ktas(p); case R4K: return C_4ktas(p); case POWER: /* Use low order lock bits to generate hash */ hash = ((int)p/sizeof(int)) & (Semperpg-1); hwsem = (int*)Lockaddr+hash; if((*hwsem & 1) == 0) { if(*p) *hwsem = 0; else { *p = 1; *hwsem = 0; return 0; } } return 1; default: return 0; } } /sys/src/ape/lib/ap/mips/main9.s 664 sys sys 1367613437 205 TEXT _main(SB), $16 MOVW $setR30(SB), R30 JAL _envsetup(SB) MOVW inargc-4(FP), R1 MOVW $inargv+0(FP), R2 MOVW R1, 4(R29) MOVW R2, 8(R29) JAL main(SB) loop: MOVW R1, 4(R29) JAL exit(SB) JMP loop /sys/src/ape/lib/ap/mips/main9p.s 664 sys sys 1367613437 889 #define NPRIVATES 16 GLOBL _tos(SB), $4 GLOBL _privates(SB), $4 GLOBL _nprivates(SB), $4 TEXT _mainp(SB), 1, $(3*4+NPRIVATES*4) MOVW $setR30(SB), R30 /* _tos = arg */ MOVW R1, _tos(SB) /* MOVW $0,FCR31 NOR R0,R0 MOVD $0.5, F26 SUBD F26, F26, F24 ADDD F26, F26, F28 ADDD F28, F28, F30 */ MOVW $8(SP), R1 MOVW R1, _privates(SB) MOVW $NPRIVATES, R1 MOVW R1, _nprivates(SB) /* _profmain(); */ JAL _profmain(SB) /* _tos->prof.pp = _tos->prof.next; */ MOVW _tos+0(SB),R1 MOVW 4(R1),R2 MOVW R2,(R1) JAL _envsetup(SB) /* main(argc, argv, environ); */ MOVW inargc-4(FP), R1 MOVW $inargv+0(FP), R2 MOVW environ(SB), R3 MOVW R1, 4(R29) MOVW R2, 8(R29) MOVW R3, 12(R29) JAL main(SB) loop: MOVW R1, 4(R29) JAL exit(SB) MOVW $_profin(SB), R0 /* force loading of profile */ JMP loop TEXT _savearg(SB), 1, $0 RET TEXT _callpc(SB), 1, $0 MOVW argp-4(FP), R1 RET /sys/src/ape/lib/ap/mips/memchr.s 664 sys sys 1367613437 425 TEXT memchr(SB), $0 MOVW R1, 0(FP) MOVW n+8(FP), R1 MOVW s1+0(FP), R2 MOVBU c+7(FP), R3 ADDU R1, R2, R6 AND $(~1), R1, R5 ADDU R2, R5 BEQ R2, R5, lt2 l1: MOVBU 0(R2), R4 MOVBU 1(R2), R7 BEQ R3, R4, eq0 ADDU $2, R2 BEQ R3, R7, eq BNE R2, R5, l1 lt2: BEQ R2, R6, zret l2: MOVBU (R2), R4 ADDU $1, R2 BEQ R3, R4, eq BNE R2, R6, l2 zret: MOVW R0, R1 RET eq0: MOVW R2, R1 RET eq: SUBU $1,R2, R1 RET /sys/src/ape/lib/ap/mips/memcmp.s 664 sys sys 1367613437 1678 TEXT memcmp(SB), $0 MOVW R1, 0(FP) /* * performance: * alligned about 1.0us/call and 17.4mb/sec * unalligned is about 3.1mb/sec */ MOVW n+8(FP), R3 /* R3 is count */ MOVW s1+0(FP), R4 /* R4 is pointer1 */ MOVW s2+4(FP), R5 /* R5 is pointer2 */ ADDU R3,R4, R6 /* R6 is end pointer1 */ /* * if not at least 4 chars, * dont even mess around. * 3 chars to guarantee any * rounding up to a word * boundary and 4 characters * to get at least maybe one * full word cmp. */ SGT $4,R3, R1 BNE R1, out /* * test if both pointers * are similarly word alligned */ XOR R4,R5, R1 AND $3, R1 BNE R1, out /* * byte at a time to word allign */ l1: AND $3,R4, R1 BEQ R1, l2 MOVB 0(R4), R8 MOVB 0(R5), R9 ADDU $1, R4 BNE R8,R9, ne ADDU $1, R5 JMP l1 /* * turn R3 into end pointer1-15 * cmp 16 at a time while theres room */ l2: ADDU $-15,R6, R3 l3: SGTU R3,R4, R1 BEQ R1, l4 MOVW 0(R4), R8 MOVW 0(R5), R9 MOVW 4(R4), R10 BNE R8,R9, ne MOVW 4(R5), R11 MOVW 8(R4), R8 BNE R10,R11, ne1 MOVW 8(R5), R9 MOVW 12(R4), R10 BNE R8,R9, ne MOVW 12(R5), R11 ADDU $16, R4 BNE R10,R11, ne1 BNE R8,R9, ne ADDU $16, R5 JMP l3 /* * turn R3 into end pointer1-3 * cmp 4 at a time while theres room */ l4: ADDU $-3,R6, R3 l5: SGTU R3,R4, R1 BEQ R1, out MOVW 0(R4), R8 MOVW 0(R5), R9 ADDU $4, R4 BNE R8,R9, ne /* only works because big endian */ ADDU $4, R5 JMP l5 /* * last loop, cmp byte at a time */ out: SGTU R6,R4, R1 BEQ R1, ret MOVB 0(R4), R8 MOVB 0(R5), R9 ADDU $1, R4 BNE R8,R9, ne ADDU $1, R5 JMP out ne1: SGTU R10,R11, R1 BNE R1, ret MOVW $-1,R1 RET ne: SGTU R8,R9, R1 BNE R1, ret MOVW $-1,R1 ret: RET END /sys/src/ape/lib/ap/mips/memmove.s 664 sys sys 1367613437 2341 TEXT memmove(SB), $0 JMP move TEXT memcpy(SB), $0 move: MOVW R1, s1+0(FP) MOVW n+8(FP), R3 /* R3 is count */ MOVW R1, R4 /* R4 is to-pointer */ SGT R0, R3, R5 BEQ R5, ok MOVW (R0), R0 /* abort if negative count */ ok: MOVW s2+4(FP), R5 /* R5 is from-pointer */ ADDU R3,R5, R7 /* R7 is end from-pointer */ ADDU R3,R4, R6 /* R6 is end to-pointer */ /* * easiest test is copy backwards if * destination string has higher mem address */ SGT $4,R3, R2 SGTU R4,R5, R1 BNE R1, back /* * if not at least 4 chars, * dont even mess around. * 3 chars to guarantee any * rounding up to a word * boundary and 4 characters * to get at least maybe one * full word store. */ BNE R2, fout /* * test if both pointers * are similarly word aligned */ XOR R4,R5, R1 AND $3, R1 BNE R1, fout /* * byte at a time to word align */ f1: AND $3,R4, R1 BEQ R1, f2 MOVB 0(R5), R8 ADDU $1, R5 MOVB R8, 0(R4) ADDU $1, R4 JMP f1 /* * turn R3 into to-end pointer-15 * copy 16 at a time while theres room. * R6 is smaller than R7 -- * there are problems if R7 is 0. */ f2: ADDU $-15,R6, R3 f3: SGTU R3,R4, R1 BEQ R1, f4 MOVW 0(R5), R8 MOVW 4(R5), R9 MOVW R8, 0(R4) MOVW 8(R5), R8 MOVW R9, 4(R4) MOVW 12(R5), R9 ADDU $16, R5 MOVW R8, 8(R4) MOVW R9, 12(R4) ADDU $16, R4 JMP f3 /* * turn R3 into to-end pointer-3 * copy 4 at a time while theres room */ f4: ADDU $-3,R6, R3 f5: SGTU R3,R4, R1 BEQ R1, fout MOVW 0(R5), R8 ADDU $4, R5 MOVW R8, 0(R4) ADDU $4, R4 JMP f5 /* * last loop, copy byte at a time */ fout: BEQ R7,R5, ret MOVB 0(R5), R8 ADDU $1, R5 MOVB R8, 0(R4) ADDU $1, R4 JMP fout /* * whole thing repeated for backwards */ back: BNE R2, bout XOR R6,R7, R1 AND $3, R1 BNE R1, bout b1: AND $3,R7, R1 BEQ R1, b2 MOVB -1(R7), R8 ADDU $-1, R7 MOVB R8, -1(R6) ADDU $-1, R6 JMP b1 b2: ADDU $15,R5, R3 b3: SGTU R7,R3, R1 BEQ R1, b4 MOVW -4(R7), R8 MOVW -8(R7), R9 MOVW R8, -4(R6) MOVW -12(R7), R8 MOVW R9, -8(R6) MOVW -16(R7), R9 ADDU $-16, R7 MOVW R8, -12(R6) MOVW R9, -16(R6) ADDU $-16, R6 JMP b3 b4: ADDU $3,R5, R3 b5: SGTU R7,R3, R1 BEQ R1, bout MOVW -4(R7), R8 ADDU $-4, R7 MOVW R8, -4(R6) ADDU $-4, R6 JMP b5 bout: BEQ R7,R5, ret MOVB -1(R7), R8 ADDU $-1, R7 MOVB R8, -1(R6) ADDU $-1, R6 JMP bout ret: MOVW s1+0(FP), R1 RET END /sys/src/ape/lib/ap/mips/memset.s 664 sys sys 1367613437 1270 TEXT memset(SB),$12 MOVW R1, 0(FP) /* * performance: * about 1us/call and 28mb/sec */ MOVW n+8(FP), R3 /* R3 is count */ MOVW p+0(FP), R4 /* R4 is pointer */ MOVW c+4(FP), R5 /* R5 is char */ ADDU R3,R4, R6 /* R6 is end pointer */ /* * if not at least 4 chars, * dont even mess around. * 3 chars to guarantee any * rounding up to a word * boundary and 4 characters * to get at least maybe one * full word store. */ SGT $4,R3, R1 BNE R1, out /* * turn R5 into a word of characters */ AND $0xff, R5 SLL $8,R5, R1 OR R1, R5 SLL $16,R5, R1 OR R1, R5 /* * store one byte at a time until pointer * is alligned on a word boundary */ l1: AND $3,R4, R1 BEQ R1, l2 MOVB R5, 0(R4) ADDU $1, R4 JMP l1 /* * turn R3 into end pointer-15 * store 16 at a time while theres room */ l2: ADDU $-15,R6, R3 l3: SGTU R3,R4, R1 BEQ R1, l4 MOVW R5, 0(R4) MOVW R5, 4(R4) ADDU $16, R4 MOVW R5, -8(R4) MOVW R5, -4(R4) JMP l3 /* * turn R3 into end pointer-3 * store 4 at a time while theres room */ l4: ADDU $-3,R6, R3 l5: SGTU R3,R4, R1 BEQ R1, out MOVW R5, 0(R4) ADDU $4, R4 JMP l5 /* * last loop, store byte at a time */ out: SGTU R6,R4 ,R1 BEQ R1, ret MOVB R5, 0(R4) ADDU $1, R4 JMP out ret: MOVW s1+0(FP), R1 RET END /sys/src/ape/lib/ap/mips/mkfile 664 sys sys 1367613437 335 APE=/sys/src/ape <$APE/config LIB=/$objtype/lib/ape/libap.a OFILES=\ cycles.$O\ getfcr.$O\ lock.$O\ main9.$O\ main9p.$O\ memchr.$O\ memcmp.$O\ memmove.$O\ memset.$O\ notetramp.$O\ setjmp.$O\ strchr.$O\ strcmp.$O\ strcpy.$O\ tas.$O\ vlop.$O\ vlrt.$O\ #include /* A stack to hold pcs when signals nest */ #define MAXSIGSTACK 20 typedef struct Pcstack Pcstack; static struct Pcstack { int sig; void (*hdlr)(int, char*, Ureg*); unsigned long restorepc; Ureg *u; } pcstack[MAXSIGSTACK]; static int nstack = 0; static void notecont(Ureg*, char*); void _notetramp(int sig, void (*hdlr)(int, char*, Ureg*), Ureg *u) { Pcstack *p; if(nstack >= MAXSIGSTACK) _NOTED(1); /* nesting too deep; just do system default */ p = &pcstack[nstack]; p->restorepc = u->pc; p->sig = sig; p->hdlr = hdlr; p->u = u; nstack++; u->pc = (unsigned long) notecont; _NOTED(2); /* NSAVE: clear note but hold state */ } static void notecont(Ureg *u, char *s) { Pcstack *p; void(*f)(int, char*, Ureg*); p = &pcstack[nstack-1]; f = p->hdlr; u->pc = p->restorepc; nstack--; (*f)(p->sig, s, u); _NOTED(3); /* NRSTR */ } #define JMPBUFPC 1 #define JMPBUFSP 0 extern sigset_t _psigblocked; void siglongjmp(sigjmp_buf j, int ret) { struct Ureg *u; if(j[0]) _psigblocked = j[1]; if(nstack == 0 || pcstack[nstack-1].u->sp > j[2+JMPBUFSP]) longjmp(j+2, ret); u = pcstack[nstack-1].u; nstack--; u->r1 = ret; if(ret == 0) u->r1 = 1; u->pc = j[2+JMPBUFPC]; u->sp = j[2+JMPBUFSP]; _NOTED(3); /* NRSTR */ } /sys/src/ape/lib/ap/mips/setjmp.s 664 sys sys 1367613437 437 TEXT setjmp(SB), 1, $-4 MOVW R29, (R1) MOVW R31, 4(R1) MOVW $0, R1 RET TEXT sigsetjmp(SB), 1, $-4 MOVW savemask+4(FP), R2 MOVW R2, 0(R1) MOVW $_psigblocked(SB), R2 MOVW R2, 4(R1) MOVW R29, 8(R1) MOVW R31, 12(R1) MOVW $0, R1 RET TEXT longjmp(SB), 1, $-4 MOVW r+4(FP), R3 BNE R3, ok /* ansi: "longjmp(0) => longjmp(1)" */ MOVW $1, R3 /* bless their pointed heads */ ok: MOVW (R1), R29 MOVW 4(R1), R31 MOVW R3, R1 RET /sys/src/ape/lib/ap/mips/strchr.s 664 sys sys 1367613437 637 TEXT strchr(SB), $0 MOVW R1, 0(FP) MOVB c+7(FP), R4 MOVW s+0(FP), R3 BEQ R4, l2 /* * char is not null */ l1: MOVB (R3), R1 ADDU $1, R3 BEQ R1, ret BNE R1,R4, l1 JMP rm1 /* * char is null * align to word */ l2: AND $3,R3, R1 BEQ R1, l3 MOVB (R3), R1 ADDU $1, R3 BNE R1, l2 JMP rm1 l3: MOVW $0xff000000, R6 MOVW $0x00ff0000, R7 l4: MOVW (R3), R5 ADDU $4, R3 AND R6,R5, R1 AND R7,R5, R2 BEQ R1, b0 AND $0xff00,R5, R1 BEQ R2, b1 AND $0xff,R5, R2 BEQ R1, b2 BNE R2, l4 rm1: ADDU $-1,R3, R1 JMP ret b2: ADDU $-2,R3, R1 JMP ret b1: ADDU $-3,R3, R1 JMP ret b0: ADDU $-4,R3, R1 JMP ret ret: RET /sys/src/ape/lib/ap/mips/strcmp.s 664 sys sys 1367613437 213 TEXT strcmp(SB), $0 MOVW s2+4(FP), R2 l1: MOVB (R2), R3 MOVB (R1), R4 ADDU $1, R1 BEQ R3, end ADDU $1, R2 BEQ R3, R4, l1 SGTU R4, R3, R1 BNE R1, ret MOVW $-1, R1 RET end: SGTU R4, R3, R1 ret: RET /sys/src/ape/lib/ap/mips/strcpy.s 664 sys sys 1367613437 1202 TEXT strcpy(SB), $0 MOVW s2+4(FP),R2 /* R2 is from pointer */ MOVW R1, R3 /* R3 is to pointer */ /* * align 'from' pointer */ l1: AND $3, R2, R5 ADDU $1, R2 BEQ R5, l2 MOVB -1(R2), R5 ADDU $1, R3 MOVB R5, -1(R3) BNE R5, l1 RET /* * test if 'to' is also alligned */ l2: AND $3,R3, R5 BEQ R5, l4 /* * copy 4 at a time, 'to' not aligned */ l3: MOVW -1(R2), R4 ADD $4, R2 ADD $4, R3 SRL $24,R4, R5 MOVB R5, -4(R3) BEQ R5, out SRL $16,R4, R5 AND $0xff, R5 MOVB R5, -3(R3) BEQ R5, out SRL $8,R4, R5 AND $0xff, R5 MOVB R5, -2(R3) BEQ R5, out AND $0xff,R4, R5 MOVB R5, -1(R3) BNE R5, l3 out: RET /* * word at a time both aligned */ l4: MOVW $0xff000000, R7 MOVW $0x00ff0000, R8 l5: ADDU $4, R3 MOVW -1(R2), R4 /* fetch */ ADDU $4, R2 AND R7,R4, R5 /* is it byte 0 */ AND R8,R4, R6 /* is it byte 1 */ BEQ R5, b0 AND $0xff00,R4, R5 /* is it byte 2 */ BEQ R6, b1 AND $0xff,R4, R6 /* is it byte 3 */ BEQ R5, b2 MOVW R4, -4(R3) /* store */ BNE R6, l5 JMP out b0: MOVB $0, -4(R3) JMP out b1: SRL $24, R4 MOVB R4, -4(R3) MOVB $0, -3(R3) JMP out b2: SRL $24,R4, R5 MOVB R5, -4(R3) SRL $16, R4 MOVB R4, -3(R3) MOVB $0, -2(R3) JMP out /sys/src/ape/lib/ap/mips/tas.s 664 sys sys 1367613437 494 /* * magnum user level lock code */ #define LL(base, rt) WORD $((060<<26)|((base)<<21)|((rt)<<16)) #define SC(base, rt) WORD $((070<<26)|((base)<<21)|((rt)<<16)) #define NOOP WORD $0x27 #define COP3 WORD $(023<<26) TEXT C_3ktas(SB),$0 MOVW R1, R21 btas: MOVW R21, R1 MOVB R0, 1(R1) NOOP COP3 BLTZ R1, btas RET TEXT C_4ktas(SB), $0 MOVW R1, R2 /* address of key */ tas1: MOVW $1, R3 LL(2, 1) NOOP SC(2, 3) NOOP BEQ R3, tas1 RET TEXT C_fcr0(SB), $0 MOVW FCR0, R1 RET /sys/src/ape/lib/ap/mips/vlop.s 664 sys sys 1367613437 239 TEXT _mulv(SB), $0 MOVW 8(FP), R2 MOVW 4(FP), R3 MOVW 16(FP), R4 MOVW 12(FP), R5 MULU R4, R2 MOVW LO, R6 MOVW HI, R7 MULU R3, R4 MOVW LO, R8 ADDU R8, R7 MULU R2, R5 MOVW LO, R8 ADDU R8, R7 MOVW R6, 4(R1) MOVW R7, 0(R1) RET /sys/src/ape/lib/ap/mips/vlrt.c 664 sys sys 1367613437 9001 typedef unsigned long ulong; typedef unsigned int uint; typedef unsigned short ushort; typedef unsigned char uchar; typedef signed char schar; #define SIGN(n) (1UL<<(n-1)) typedef struct Vlong Vlong; struct Vlong { union { struct { ulong hi; ulong lo; }; struct { ushort hims; ushort hils; ushort loms; ushort lols; }; }; }; void abort(void); void _addv(Vlong *r, Vlong a, Vlong b) { ulong lo, hi; lo = a.lo + b.lo; hi = a.hi + b.hi; if(lo < a.lo) hi++; r->lo = lo; r->hi = hi; } void _subv(Vlong *r, Vlong a, Vlong b) { ulong lo, hi; lo = a.lo - b.lo; hi = a.hi - b.hi; if(lo > a.lo) hi--; r->lo = lo; r->hi = hi; } void _d2v(Vlong *y, double d) { union { double d; struct Vlong; } x; ulong xhi, xlo, ylo, yhi; int sh; x.d = d; xhi = (x.hi & 0xfffff) | 0x100000; xlo = x.lo; sh = 1075 - ((x.hi >> 20) & 0x7ff); ylo = 0; yhi = 0; if(sh >= 0) { /* v = (hi||lo) >> sh */ if(sh < 32) { if(sh == 0) { ylo = xlo; yhi = xhi; } else { ylo = (xlo >> sh) | (xhi << (32-sh)); yhi = xhi >> sh; } } else { if(sh == 32) { ylo = xhi; } else if(sh < 64) { ylo = xhi >> (sh-32); } } } else { /* v = (hi||lo) << -sh */ sh = -sh; if(sh <= 10) { ylo = xlo << sh; yhi = (xhi << sh) | (xlo >> (32-sh)); } else { /* overflow */ yhi = d; /* causes something awful */ } } if(x.hi & SIGN(32)) { if(ylo != 0) { ylo = -ylo; yhi = ~yhi; } else yhi = -yhi; } y->hi = yhi; y->lo = ylo; } void _f2v(Vlong *y, float f) { _d2v(y, f); } double _v2d(Vlong x) { if(x.hi & SIGN(32)) { if(x.lo) { x.lo = -x.lo; x.hi = ~x.hi; } else x.hi = -x.hi; return -((long)x.hi*4294967296. + x.lo); } return (long)x.hi*4294967296. + x.lo; } float _v2f(Vlong x) { return _v2d(x); } static void dodiv(Vlong num, Vlong den, Vlong *qp, Vlong *rp) { ulong numlo, numhi, denhi, denlo, quohi, quolo, t; int i; numhi = num.hi; numlo = num.lo; denhi = den.hi; denlo = den.lo; /* * get a divide by zero */ if(denlo==0 && denhi==0) { numlo = numlo / denlo; } /* * set up the divisor and find the number of iterations needed */ if(numhi >= SIGN(32)) { quohi = SIGN(32); quolo = 0; } else { quohi = numhi; quolo = numlo; } i = 0; while(denhi < quohi || (denhi == quohi && denlo < quolo)) { denhi = (denhi<<1) | (denlo>>31); denlo <<= 1; i++; } quohi = 0; quolo = 0; for(; i >= 0; i--) { quohi = (quohi<<1) | (quolo>>31); quolo <<= 1; if(numhi > denhi || (numhi == denhi && numlo >= denlo)) { t = numlo; numlo -= denlo; if(numlo > t) numhi--; numhi -= denhi; quolo |= 1; } denlo = (denlo>>1) | (denhi<<31); denhi >>= 1; } if(qp) { qp->lo = quolo; qp->hi = quohi; } if(rp) { rp->lo = numlo; rp->hi = numhi; } } void _divvu(Vlong *q, Vlong n, Vlong d) { if(n.hi == 0 && d.hi == 0) { q->hi = 0; q->lo = n.lo / d.lo; return; } dodiv(n, d, q, 0); } void _modvu(Vlong *r, Vlong n, Vlong d) { if(n.hi == 0 && d.hi == 0) { r->hi = 0; r->lo = n.lo % d.lo; return; } dodiv(n, d, 0, r); } static void vneg(Vlong *v) { if(v->lo == 0) { v->hi = -v->hi; return; } v->lo = -v->lo; v->hi = ~v->hi; } void _divv(Vlong *q, Vlong n, Vlong d) { long nneg, dneg; if(n.hi == (((long)n.lo)>>31) && d.hi == (((long)d.lo)>>31)) { q->lo = (long)n.lo / (long)d.lo; q->hi = ((long)q->lo) >> 31; return; } nneg = n.hi >> 31; if(nneg) vneg(&n); dneg = d.hi >> 31; if(dneg) vneg(&d); dodiv(n, d, q, 0); if(nneg != dneg) vneg(q); } void _modv(Vlong *r, Vlong n, Vlong d) { long nneg, dneg; if(n.hi == (((long)n.lo)>>31) && d.hi == (((long)d.lo)>>31)) { r->lo = (long)n.lo % (long)d.lo; r->hi = ((long)r->lo) >> 31; return; } nneg = n.hi >> 31; if(nneg) vneg(&n); dneg = d.hi >> 31; if(dneg) vneg(&d); dodiv(n, d, 0, r); if(nneg) vneg(r); } void _rshav(Vlong *r, Vlong a, int b) { long t; t = a.hi; if(b >= 32) { r->hi = t>>31; if(b >= 64) { /* this is illegal re C standard */ r->lo = t>>31; return; } r->lo = t >> (b-32); return; } if(b <= 0) { r->hi = t; r->lo = a.lo; return; } r->hi = t >> b; r->lo = (t << (32-b)) | (a.lo >> b); } void _rshlv(Vlong *r, Vlong a, int b) { ulong t; t = a.hi; if(b >= 32) { r->hi = 0; if(b >= 64) { /* this is illegal re C standard */ r->lo = 0; return; } r->lo = t >> (b-32); return; } if(b <= 0) { r->hi = t; r->lo = a.lo; return; } r->hi = t >> b; r->lo = (t << (32-b)) | (a.lo >> b); } void _lshv(Vlong *r, Vlong a, int b) { ulong t; t = a.lo; if(b >= 32) { r->lo = 0; if(b >= 64) { /* this is illegal re C standard */ r->hi = 0; return; } r->hi = t << (b-32); return; } if(b <= 0) { r->lo = t; r->hi = a.hi; return; } r->lo = t << b; r->hi = (t >> (32-b)) | (a.hi << b); } void _andv(Vlong *r, Vlong a, Vlong b) { r->hi = a.hi & b.hi; r->lo = a.lo & b.lo; } void _orv(Vlong *r, Vlong a, Vlong b) { r->hi = a.hi | b.hi; r->lo = a.lo | b.lo; } void _xorv(Vlong *r, Vlong a, Vlong b) { r->hi = a.hi ^ b.hi; r->lo = a.lo ^ b.lo; } void _vpp(Vlong *l, Vlong *r) { l->hi = r->hi; l->lo = r->lo; r->lo++; if(r->lo == 0) r->hi++; } void _vmm(Vlong *l, Vlong *r) { l->hi = r->hi; l->lo = r->lo; if(r->lo == 0) r->hi--; r->lo--; } void _ppv(Vlong *l, Vlong *r) { r->lo++; if(r->lo == 0) r->hi++; l->hi = r->hi; l->lo = r->lo; } void _mmv(Vlong *l, Vlong *r) { if(r->lo == 0) r->hi--; r->lo--; l->hi = r->hi; l->lo = r->lo; } void _vasop(Vlong *ret, void *lv, void fn(Vlong*, Vlong, Vlong), int type, Vlong rv) { Vlong t, u; u.lo = 0; u.hi = 0; switch(type) { default: abort(); break; case 1: /* schar */ t.lo = *(schar*)lv; t.hi = t.lo >> 31; fn(&u, t, rv); *(schar*)lv = u.lo; break; case 2: /* uchar */ t.lo = *(uchar*)lv; t.hi = 0; fn(&u, t, rv); *(uchar*)lv = u.lo; break; case 3: /* short */ t.lo = *(short*)lv; t.hi = t.lo >> 31; fn(&u, t, rv); *(short*)lv = u.lo; break; case 4: /* ushort */ t.lo = *(ushort*)lv; t.hi = 0; fn(&u, t, rv); *(ushort*)lv = u.lo; break; case 9: /* int */ t.lo = *(int*)lv; t.hi = t.lo >> 31; fn(&u, t, rv); *(int*)lv = u.lo; break; case 10: /* uint */ t.lo = *(uint*)lv; t.hi = 0; fn(&u, t, rv); *(uint*)lv = u.lo; break; case 5: /* long */ t.lo = *(long*)lv; t.hi = t.lo >> 31; fn(&u, t, rv); *(long*)lv = u.lo; break; case 6: /* ulong */ t.lo = *(ulong*)lv; t.hi = 0; fn(&u, t, rv); *(ulong*)lv = u.lo; break; case 7: /* vlong */ case 8: /* uvlong */ fn(&u, *(Vlong*)lv, rv); *(Vlong*)lv = u; break; } *ret = u; } void _p2v(Vlong *ret, void *p) { long t; t = (ulong)p; ret->lo = t; ret->hi = 0; } void _sl2v(Vlong *ret, long sl) { long t; t = sl; ret->lo = t; ret->hi = t >> 31; } void _ul2v(Vlong *ret, ulong ul) { long t; t = ul; ret->lo = t; ret->hi = 0; } void _si2v(Vlong *ret, int si) { long t; t = si; ret->lo = t; ret->hi = t >> 31; } void _ui2v(Vlong *ret, uint ui) { long t; t = ui; ret->lo = t; ret->hi = 0; } void _sh2v(Vlong *ret, long sh) { long t; t = (sh << 16) >> 16; ret->lo = t; ret->hi = t >> 31; } void _uh2v(Vlong *ret, ulong ul) { long t; t = ul & 0xffff; ret->lo = t; ret->hi = 0; } void _sc2v(Vlong *ret, long uc) { long t; t = (uc << 24) >> 24; ret->lo = t; ret->hi = t >> 31; } void _uc2v(Vlong *ret, ulong ul) { long t; t = ul & 0xff; ret->lo = t; ret->hi = 0; } long _v2sc(Vlong rv) { long t; t = rv.lo & 0xff; return (t << 24) >> 24; } long _v2uc(Vlong rv) { return rv.lo & 0xff; } long _v2sh(Vlong rv) { long t; t = rv.lo & 0xffff; return (t << 16) >> 16; } long _v2uh(Vlong rv) { return rv.lo & 0xffff; } long _v2sl(Vlong rv) { return rv.lo; } long _v2ul(Vlong rv) { return rv.lo; } long _v2si(Vlong rv) { return rv.lo; } long _v2ui(Vlong rv) { return rv.lo; } int _testv(Vlong rv) { return rv.lo || rv.hi; } int _eqv(Vlong lv, Vlong rv) { return lv.lo == rv.lo && lv.hi == rv.hi; } int _nev(Vlong lv, Vlong rv) { return lv.lo != rv.lo || lv.hi != rv.hi; } int _ltv(Vlong lv, Vlong rv) { return (long)lv.hi < (long)rv.hi || (lv.hi == rv.hi && lv.lo < rv.lo); } int _lev(Vlong lv, Vlong rv) { return (long)lv.hi < (long)rv.hi || (lv.hi == rv.hi && lv.lo <= rv.lo); } int _gtv(Vlong lv, Vlong rv) { return (long)lv.hi > (long)rv.hi || (lv.hi == rv.hi && lv.lo > rv.lo); } int _gev(Vlong lv, Vlong rv) { return (long)lv.hi > (long)rv.hi || (lv.hi == rv.hi && lv.lo >= rv.lo); } int _lov(Vlong lv, Vlong rv) { return lv.hi < rv.hi || (lv.hi == rv.hi && lv.lo < rv.lo); } int _lsv(Vlong lv, Vlong rv) { return lv.hi < rv.hi || (lv.hi == rv.hi && lv.lo <= rv.lo); } int _hiv(Vlong lv, Vlong rv) { return lv.hi > rv.hi || (lv.hi == rv.hi && lv.lo > rv.lo); } int _hsv(Vlong lv, Vlong rv) { return lv.hi > rv.hi || (lv.hi == rv.hi && lv.lo >= rv.lo); } /sys/src/ape/lib/ap/mkfile 664 sys sys 1367613437 316 APE=/sys/src/ape <$APE/config DIRS=gen math plan9 posix stdio syscall default:V: all install all:V: for(i in $DIRS $objtype)@{ echo $i cd $i mk $MKFLAGS $target } installall:V: for(objtype in $CPUS) mk $MKFLAGS install clean nuke:V: for(i in $DIRS $CPUS)@{ echo $i cd $i mk $MKFLAGS $target } /sys/src/ape/lib/ap/plan9 20000000775 sys sys 1369861527 0 /sys/src/ape/lib/ap/plan9/9iounit.c 664 sys sys 1369166818 529 #include "lib.h" #include #include #include #include "sys9.h" #include "dir.h" /* * Format: 3 r M 4 (0000000000457def 11 00) 8192 512 /rc/lib/rcmain */ int _IOUNIT(int fd) { int i, cfd; char buf[128], *args[10]; snprint(buf, sizeof buf, "#d/%dctl", fd); cfd = _OPEN(buf, OREAD); if(cfd < 0) return 0; i = _READ(cfd, buf, sizeof buf-1); _CLOSE(cfd); if(i <= 0) return 0; buf[i] = '\0'; if(getfields(buf, args, 10, 1, " \t") != 10) return 0; return atoi(args[7]); } /sys/src/ape/lib/ap/plan9/9mallocz.c 664 sys sys 1367613437 148 #include #include void* _MALLOCZ(int n, int clr) { void *v; v = malloc(n); if(v && clr) memset(v, 0, n); return v; } /sys/src/ape/lib/ap/plan9/9read.c 664 sys sys 1367613437 169 #include "lib.h" #include #include #include "sys9.h" #include "dir.h" long _READ(int fd, void *buf, long n) { return _PREAD(fd, buf, n, -1LL); } /sys/src/ape/lib/ap/plan9/9readn.c 664 sys sys 1367613437 221 #include "sys9.h" long _READN(int f, void *av, long n) { char *a; long m, t; a = av; t = 0; while(t < n){ m = _READ(f, a+t, n-t); if(m <= 0){ if(t == 0) return m; break; } t += m; } return t; } /sys/src/ape/lib/ap/plan9/9wait.c 664 sys sys 1367613437 1517 #include "lib.h" #include #include #include #include "sys9.h" #include "dir.h" static char qsep[] = " \t\r\n"; static char* qtoken(char *s) { int quoting; char *t; quoting = 0; t = s; /* s is output string, t is input string */ while(*t!='\0' && (quoting || strchr(qsep, *t)==nil)){ if(*t != '\''){ *s++ = *t++; continue; } /* *t is a quote */ if(!quoting){ quoting = 1; t++; continue; } /* quoting and we're on a quote */ if(t[1] != '\''){ /* end of quoted section; absorb closing quote */ t++; quoting = 0; continue; } /* doubled quote; fold one quote into two */ t++; *s++ = *t++; } if(*s != '\0'){ *s = '\0'; if(t == s) t++; } return t; } static int tokenize(char *s, char **args, int maxargs) { int nargs; for(nargs=0; nargspid = atoi(fld[0]); w->time[0] = atoi(fld[1]); w->time[1] = atoi(fld[2]); w->time[2] = atoi(fld[3]); w->msg = (char*)&w[1]; memmove(w->msg, fld[4], l); return w; } /sys/src/ape/lib/ap/plan9/9write.c 664 sys sys 1367613437 171 #include "lib.h" #include #include #include "sys9.h" #include "dir.h" long _WRITE(int fd, void *buf, long n) { return _PWRITE(fd, buf, n, -1LL); } /sys/src/ape/lib/ap/plan9/_dirconv.c 664 sys sys 1367613437 1419 #include "lib.h" #include #include "sys9.h" #include "dir.h" #define CHAR(x) *p++ = f->x #define SHORT(x) p[0] = f->x; p[1] = f->x>>8; p += 2 #define LONG(x) p[0] = f->x; p[1] = f->x>>8; p[2] = f->x>>16; p[3] = f->x>>24; p += 4 #define VLONG(x) p[0] = f->x; p[1] = f->x>>8; p[2] = f->x>>16; p[3] = f->x>>24;\ p[4] = 0; p[5] = 0; p[6] = 0; p[7] = 0; p += 8 #define STRING(x,n) memcpy(p, f->x, n); p += n int convD2M(Dir *f, char *ap) { unsigned char *p; p = (unsigned char*)ap; STRING(name, sizeof(f->name)); STRING(uid, sizeof(f->uid)); STRING(gid, sizeof(f->gid)); LONG(qid.path); LONG(qid.vers); LONG(mode); LONG(atime); LONG(mtime); VLONG(length); SHORT(type); SHORT(dev); return p - (unsigned char*)ap; } #undef CHAR #undef SHORT #undef LONG #undef VLONG #undef STRING #define CHAR(x) f->x = *p++ #define SHORT(x) f->x = (p[0] | (p[1]<<8)); p += 2 #define LONG(x) f->x = (p[0] | (p[1]<<8) |\ (p[2]<<16) | (p[3]<<24)); p += 4 #define VLONG(x) f->x = (p[0] | (p[1]<<8) |\ (p[2]<<16) | (p[3]<<24)); p += 8 #define STRING(x,n) memcpy(f->x, p, n); p += n int convM2D(char *ap, Dir *f) { unsigned char *p; p = (unsigned char*)ap; STRING(name, sizeof(f->name)); STRING(uid, sizeof(f->uid)); STRING(gid, sizeof(f->gid)); LONG(qid.path); LONG(qid.vers); LONG(mode); LONG(atime); LONG(mtime); VLONG(length); SHORT(type); SHORT(dev); return p - (unsigned char*)ap; } /sys/src/ape/lib/ap/plan9/_envsetup.c 664 sys sys 1369166818 2192 #include "lib.h" #include #include #include #include #include #include #include #include "sys9.h" #include "dir.h" /* * Called before main to initialize environ. * Some plan9 environment variables * have 0 bytes in them (notably $path); * we change them to 1's (and execve changes back) * * Also, register the note handler. */ char **environ; int errno; unsigned long _clock; static void fdsetup(char *, char *); static void sigsetup(char *, char *); enum { Envhunk=7000, }; void _envsetup(void) { int dfd; int n, nd, m, i, j, f; int psize, cnt; int nohandle; int fdinited; char *ps, *p, *name; char **pp; Dir *d9, *d9a; nohandle = 0; fdinited = 0; cnt = 0; dfd = _OPEN("/env", 0); if(dfd < 0) { static char **emptyenvp = 0; environ = emptyenvp; return; } ps = p = malloc(Envhunk); psize = Envhunk; nd = _dirreadall(dfd, &d9a); _CLOSE(dfd); for(j=0; jname); m = d9->length; i = p - ps; if(i+n+1+m+1 > psize) { psize += (n+m+2 < Envhunk)? Envhunk : n+m+2; ps = realloc(ps, psize); p = ps + i; } memcpy(p, d9->name, n); p[n] = '='; name = malloc(n+6); strcpy(name, "/env/"); strcpy(name+5, d9->name); f = _OPEN(name, O_RDONLY); free(name); if(f < 0 || _READ(f, p+n+1, m) != m) m = 0; _CLOSE(f); if(p[n+m] == 0) m--; for(i=0; iname, "_fdinfo") == 0) { _fdinit(p+n+1, p+n+1+m); fdinited = 1; } else if(strcmp(d9->name, "_sighdlr") == 0) sigsetup(p+n+1, p+n+1+m); else if(strcmp(d9->name, "nohandle") == 0) nohandle = 1; p += n+m+2; cnt++; } free(d9a); if(!fdinited) _fdinit(0, 0); environ = pp = malloc((1+cnt)*sizeof(char *)); p = ps; for(i = 0; i < cnt; i++) { *pp++ = p; p = memchr(p, 0, ps+psize-p); if (!p) break; p++; } *pp = 0; if(!nohandle) _NOTIFY(_notehandler); } static void sigsetup(char *s, char *se) { int sig; char *e; while(s < se){ sig = strtoul(s, &e, 10); if(s == e) break; s = e; if(sig <= MAXSIG) _sighdlr[sig] = SIG_IGN; } } /sys/src/ape/lib/ap/plan9/_errno.c 664 sys sys 1367613437 3899 #include "lib.h" #include "sys9.h" #include #include /* make this global, so programs can look at it if errno is unilluminating */ /* see also: ../stdio/strerror.c, with errno-> string mapping */ char _plan9err[ERRMAX]; static struct errmap { int errno; char *ename; } map[] = { /* from /sys/src/9/port/errstr.h */ {EINVAL, "inconsistent mount"}, {EINVAL, "not mounted"}, {EINVAL, "not in union"}, {EIO, "mount rpc error"}, {EIO, "mounted device shut down"}, {EPERM, "mounted directory forbids creation"}, {ENOENT, "does not exist"}, {ENXIO, "unknown device in # filename"}, {ENOTDIR, "not a directory"}, {EISDIR, "file is a directory"}, {EINVAL, "bad character in file name"}, {EINVAL, "file name syntax"}, {EPERM, "permission denied"}, {EPERM, "inappropriate use of fd"}, {EINVAL, "bad arg in system call"}, {EBUSY, "device or object already in use"}, {EIO, "i/o error"}, {EIO, "read or write too large"}, {EIO, "read or write too small"}, {EADDRINUSE, "network port not available"}, {ESHUTDOWN, "write to hungup stream"}, {ESHUTDOWN, "i/o on hungup channel"}, {EINVAL, "bad process or channel control request"}, {EBUSY, "no free devices"}, {ESRCH, "process exited"}, {ECHILD, "no living children"}, {EIO, "i/o error in demand load"}, {ENOMEM, "virtual memory allocation failed"}, {EBADF, "fd out of range or not open"}, {EMFILE, "no free file descriptors"}, {ESPIPE, "seek on a stream"}, {ENOEXEC, "exec header invalid"}, {ETIMEDOUT, "connection timed out"}, {ECONNREFUSED, "connection refused"}, {ECONNREFUSED, "connection in use"}, {EINTR, "interrupted"}, {ENOMEM, "kernel allocate failed"}, {EINVAL, "segments overlap"}, {EIO, "i/o count too small"}, {EGREG, "ken has left the building"}, {EINVAL, "bad attach specifier"}, /* from exhausted() calls in kernel */ {ENFILE, "no free file descriptors"}, {EBUSY, "no free mount devices"}, {EBUSY, "no free mount rpc buffer"}, {EBUSY, "no free segments"}, {ENOMEM, "no free memory"}, {ENOBUFS, "no free Blocks"}, {EBUSY, "no free routes"}, /* from ken */ {EINVAL, "attach -- bad specifier"}, {EBADF, "unknown fid"}, {EINVAL, "bad character in directory name"}, {EBADF, "read/write -- on non open fid"}, {EIO, "read/write -- count too big"}, {EIO, "phase error -- directory entry not allocated"}, {EIO, "phase error -- qid does not match"}, {EACCES, "access permission denied"}, {ENOENT, "directory entry not found"}, {EINVAL, "open/create -- unknown mode"}, {ENOTDIR, "walk -- in a non-directory"}, {ENOTDIR, "create -- in a non-directory"}, {EIO, "phase error -- cannot happen"}, {EEXIST, "create -- file exists"}, {EINVAL, "create -- . and .. illegal names"}, {ENOTEMPTY, "directory not empty"}, {EINVAL, "attach -- privileged user"}, {EPERM, "wstat -- not owner"}, {EPERM, "wstat -- not in group"}, {EINVAL, "create/wstat -- bad character in file name"}, {EBUSY, "walk -- too many (system wide)"}, {EROFS, "file system read only"}, {ENOSPC, "file system full"}, {EINVAL, "read/write -- offset negative"}, {EBUSY, "open/create -- file is locked"}, {EBUSY, "close/read/write -- lock is broken"}, /* from sockets */ {ENOTSOCK, "not a socket"}, {EPROTONOSUPPORT, "protocol not supported"}, {ECONNREFUSED, "connection refused"}, {EAFNOSUPPORT, "address family not supported"}, {ENOBUFS, "insufficient buffer space"}, {EOPNOTSUPP, "operation not supported"}, {EADDRINUSE, "address in use"}, {EGREG, "unnamed error message"}, }; #define NERRMAP (sizeof(map)/sizeof(struct errmap)) /* convert last system call error to an errno */ void _syserrno(void) { int i; if(_ERRSTR(_plan9err, sizeof _plan9err) < 0) errno = EINVAL; else{ for(i = 0; i < NERRMAP; i++) if(strstr(_plan9err, map[i].ename) != 0) break; _ERRSTR(_plan9err, sizeof _plan9err); errno = (i < NERRMAP)? map[i].errno : EINVAL; } } /sys/src/ape/lib/ap/plan9/_exit.c 664 sys sys 1369166818 859 #include "lib.h" #include "sys9.h" #include #include #include #include int _finishing = 0; int _sessleader = 0; static char exitstatus[ERRMAX]; void _exit(int status) { _finish(status, 0); } void _finish(int status, char *term) { char *cp; if(_finishing) _EXITS(exitstatus); _finishing = 1; if(status){ cp = _ultoa(exitstatus, status & 0xFF); *cp = 0; }else if(term){ strncpy(exitstatus, term, ERRMAX); exitstatus[ERRMAX-1] = '\0'; } if(_sessleader) kill(0, SIGTERM); _EXITS(exitstatus); } /* emulate: return p+sprintf(p, "%uld", v) */ #define IDIGIT 15 char * _ultoa(char *p, unsigned long v) { char s[IDIGIT]; int n, i; s[IDIGIT-1] = 0; for(i = IDIGIT-2; i; i--){ n = v % 10; s[i] = n + '0'; v = v / 10; if(v == 0) break; } strcpy(p, s+i); return p + (IDIGIT-1-i); } /sys/src/ape/lib/ap/plan9/_fcall.c 664 sys sys 1367613437 6036 #include #include "sys9.h" #include "lib.h" #include "dir.h" #include "fcall.h" typedef unsigned char uchar; #define CHAR(x) *p++ = f->x #define SHORT(x) p[0] = f->x; p[1] = f->x>>8; p += 2 #define LONG(x) p[0] = f->x; p[1] = f->x>>8; p[2] = f->x>>16; p[3] = f->x>>24; p += 4 #define VLONG(x) p[0] = f->x; p[1] = f->x>>8; p[2] = f->x>>16; p[3] = f->x>>24;\ p[4] = 0; p[5] = 0; p[6] = 0; p[7] = 0; p += 8 #define STRING(x,n) memcpy(p, f->x, n); p += n int convS2M(Fcall *f, char *ap) { uchar *p; p = (uchar*)ap; CHAR(type); SHORT(tag); switch(f->type) { default: return 0; case Tosession: case Tnop: break; case Tsession: STRING(chal, sizeof(f->chal)); break; case Tflush: SHORT(oldtag); break; case Tattach: SHORT(fid); STRING(uname, sizeof(f->uname)); STRING(aname, sizeof(f->aname)); STRING(ticket, sizeof(f->ticket)); STRING(auth, sizeof(f->auth)); break; case Toattach: SHORT(fid); STRING(uname, sizeof(f->uname)); STRING(aname, sizeof(f->aname)); STRING(ticket, NAMELEN); break; case Tauth: SHORT(fid); STRING(uname, sizeof(f->uname)); STRING(ticket, 8+NAMELEN); break; case Tclone: SHORT(fid); SHORT(newfid); break; case Twalk: SHORT(fid); STRING(name, sizeof(f->name)); break; case Topen: SHORT(fid); CHAR(mode); break; case Tcreate: SHORT(fid); STRING(name, sizeof(f->name)); LONG(perm); CHAR(mode); break; case Tread: SHORT(fid); VLONG(offset); SHORT(count); break; case Twrite: SHORT(fid); VLONG(offset); SHORT(count); p++; /* pad(1) */ STRING(data, f->count); break; case Tclunk: SHORT(fid); break; case Tremove: SHORT(fid); break; case Tstat: SHORT(fid); break; case Twstat: SHORT(fid); STRING(stat, sizeof(f->stat)); break; case Tclwalk: SHORT(fid); SHORT(newfid); STRING(name, sizeof(f->name)); break; /* */ case Rosession: case Rnop: break; case Rsession: STRING(chal, sizeof(f->chal)); STRING(authid, sizeof(f->authid)); STRING(authdom, sizeof(f->authdom)); break; case Rerror: STRING(ename, sizeof(f->ename)); break; case Rflush: break; case Rattach: SHORT(fid); LONG(qid.path); LONG(qid.vers); STRING(rauth, sizeof(f->rauth)); break; case Roattach: SHORT(fid); LONG(qid.path); LONG(qid.vers); break; case Rauth: SHORT(fid); STRING(ticket, 8+8+7+7); break; case Rclone: SHORT(fid); break; case Rwalk: case Rclwalk: SHORT(fid); LONG(qid.path); LONG(qid.vers); break; case Ropen: SHORT(fid); LONG(qid.path); LONG(qid.vers); break; case Rcreate: SHORT(fid); LONG(qid.path); LONG(qid.vers); break; case Rread: SHORT(fid); SHORT(count); p++; /* pad(1) */ STRING(data, f->count); break; case Rwrite: SHORT(fid); SHORT(count); break; case Rclunk: SHORT(fid); break; case Rremove: SHORT(fid); break; case Rstat: SHORT(fid); STRING(stat, sizeof(f->stat)); break; case Rwstat: SHORT(fid); break; } return p - (uchar*)ap; } #undef CHAR #undef SHORT #undef LONG #undef VLONG #undef STRING #define CHAR(x) f->x = *p++ #define SHORT(x) f->x = (p[0] | (p[1]<<8)); p += 2 #define LONG(x) f->x = (p[0] | (p[1]<<8) |\ (p[2]<<16) | (p[3]<<24)); p += 4 #define VLONG(x) f->x = (p[0] | (p[1]<<8) |\ (p[2]<<16) | (p[3]<<24)); p += 8 #define STRING(x,n) memcpy(f->x, p, n); p += n int convM2S(char *ap, Fcall *f, int n) { uchar *p; p = (uchar*)ap; CHAR(type); SHORT(tag); switch(f->type) { default: return 0; case Tnop: case Tosession: break; case Tsession: STRING(chal, sizeof(f->chal)); break; case Tflush: SHORT(oldtag); break; case Tattach: SHORT(fid); STRING(uname, sizeof(f->uname)); STRING(aname, sizeof(f->aname)); STRING(ticket, sizeof(f->ticket)); STRING(auth, sizeof(f->auth)); break; case Toattach: SHORT(fid); STRING(uname, sizeof(f->uname)); STRING(aname, sizeof(f->aname)); STRING(ticket, NAMELEN); break; case Tauth: SHORT(fid); STRING(uname, sizeof(f->uname)); STRING(ticket, 8+NAMELEN); break; case Tclone: SHORT(fid); SHORT(newfid); break; case Twalk: SHORT(fid); STRING(name, sizeof(f->name)); break; case Topen: SHORT(fid); CHAR(mode); break; case Tcreate: SHORT(fid); STRING(name, sizeof(f->name)); LONG(perm); CHAR(mode); break; case Tread: SHORT(fid); VLONG(offset); SHORT(count); break; case Twrite: SHORT(fid); VLONG(offset); SHORT(count); p++; /* pad(1) */ f->data = (char*)p; p += f->count; break; case Tclunk: SHORT(fid); break; case Tremove: SHORT(fid); break; case Tstat: SHORT(fid); break; case Twstat: SHORT(fid); STRING(stat, sizeof(f->stat)); break; case Tclwalk: SHORT(fid); SHORT(newfid); STRING(name, sizeof(f->name)); break; /* */ case Rnop: case Rosession: break; case Rsession: STRING(chal, sizeof(f->chal)); STRING(authid, sizeof(f->authid)); STRING(authdom, sizeof(f->authdom)); break; case Rerror: STRING(ename, sizeof(f->ename)); break; case Rflush: break; case Rattach: SHORT(fid); LONG(qid.path); LONG(qid.vers); STRING(rauth, sizeof(f->rauth)); break; case Roattach: SHORT(fid); LONG(qid.path); LONG(qid.vers); break; case Rauth: SHORT(fid); STRING(ticket, 8+8+7+7); break; case Rclone: SHORT(fid); break; case Rwalk: case Rclwalk: SHORT(fid); LONG(qid.path); LONG(qid.vers); break; case Ropen: SHORT(fid); LONG(qid.path); LONG(qid.vers); break; case Rcreate: SHORT(fid); LONG(qid.path); LONG(qid.vers); break; case Rread: SHORT(fid); SHORT(count); p++; /* pad(1) */ f->data = (char*)p; p += f->count; break; case Rwrite: SHORT(fid); SHORT(count); break; case Rclunk: SHORT(fid); break; case Rremove: SHORT(fid); break; case Rstat: SHORT(fid); STRING(stat, sizeof(f->stat)); break; case Rwstat: SHORT(fid); break; } if((uchar*)ap+n == p) return n; return 0; } /sys/src/ape/lib/ap/plan9/_fdinfo.c 664 sys sys 1369166818 2856 #define _BSDTIME_EXTENSION #include "lib.h" #include #include #include "sys9.h" #include extern int errno; Fdinfo _fdinfo[OPEN_MAX]; /* called from _envsetup, either with the value of the environment variable _fdinfo (from s to se-1), or with s==0 if there was no _fdinfo */ static void defaultfdinit(void) { int i; Fdinfo *fi; for(i = 0; i <= 2; i++) { fi = &_fdinfo[i]; fi->flags = FD_ISOPEN; fi->oflags = (i == 0)? O_RDONLY : O_WRONLY; if(_isatty(i)) fi->flags |= FD_ISTTY; } } static int readprocfdinit(void) { /* construct info from /proc/$pid/fd */ char buf[8192]; Fdinfo *fi; int fd, pfd, pid, n, tot, m; char *s, *nexts; memset(buf, 0, sizeof buf); pfd = _OPEN("#c/pid", 0); if(pfd < 0) return -1; if(_PREAD(pfd, buf, 100, 0) < 0){ _CLOSE(pfd); return -1; } _CLOSE(pfd); pid = strtoul(buf, 0, 10); strcpy(buf, "#p/"); _ultoa(buf+3, pid); strcat(buf, "/fd"); pfd = _OPEN(buf, 0); if(pfd < 0) return -1; memset(buf, 0, sizeof buf); tot = 0; for(;;){ n = _PREAD(pfd, buf+tot, sizeof buf-tot, tot); if(n <= 0) break; tot += n; } _CLOSE(pfd); if(n < 0) return -1; buf[sizeof buf-1] = '\0'; s = strchr(buf, '\n'); /* skip current directory */ if(s == 0) return -1; s++; m = 0; for(; s && *s; s=nexts){ nexts = strchr(s, '\n'); if(nexts) *nexts++ = '\0'; errno = 0; fd = strtoul(s, &s, 10); if(errno != 0) return -1; if(fd >= OPEN_MAX) continue; if(fd == pfd) continue; fi = &_fdinfo[fd]; fi->flags = FD_ISOPEN; while(*s == ' ' || *s == '\t') s++; if(*s == 'r'){ m |= 1; s++; } if(*s == 'w'){ m |= 2; } if(m==1) fi->oflags = O_RDONLY; else if(m==2) fi->oflags = O_WRONLY; else fi->oflags = O_RDWR; if(strlen(s) >= 9 && strcmp(s+strlen(s)-9, "/dev/cons") == 0) fi->flags |= FD_ISTTY; } return 0; } static void sfdinit(int usedproc, char *s, char *se) { Fdinfo *fi; unsigned long fd, fl, ofl; char *e; while(s < se){ fd = strtoul(s, &e, 10); if(s == e) break; s = e; fl = strtoul(s, &e, 10); if(s == e) break; s = e; ofl = strtoul(s, &e, 10); if(s == e) break; s = e; if(fd < OPEN_MAX){ fi = &_fdinfo[fd]; if(usedproc && !(fi->flags&FD_ISOPEN)) continue; /* should probably ignore all of $_fdinit */ fi->flags = fl; fi->oflags = ofl; if(_isatty(fd)) fi->flags |= FD_ISTTY; } } } void _fdinit(char *s, char *se) { int i, usedproc; Fdinfo *fi; struct stat sbuf; usedproc = 0; if(readprocfdinit() == 0) usedproc = 1; else _WRITE(2, "FAILED\n", 7); if(s) sfdinit(usedproc, s, se); if(!s && !usedproc) defaultfdinit(); for(i = 0; i < OPEN_MAX; i++) { fi = &_fdinfo[i]; if(fi->flags&FD_ISOPEN){ if(fstat(i, &sbuf) >= 0) { fi->uid = sbuf.st_uid; fi->gid = sbuf.st_gid; } } } } /sys/src/ape/lib/ap/plan9/_getpw.c 664 sys sys 1369166818 3305 #include "lib.h" #include #include #include #include #include #include #include "sys9.h" #include "dir.h" /* * Search /adm/users for line with second field == *pname (if * not NULL), else with first field == *pnum. Return non-zero * if found, and fill in *pnum, *pname, and *plist to fields * 1, 2, and 4 */ enum {NAMEMAX = 20, MEMOMAX = 40 }; static char *admusers = "/adm/users"; /* we hold a fixed-length memo list of past lookups, and use a move-to-front strategy to organize the list */ typedef struct Memo { char name[NAMEMAX]; int num; char *glist; } Memo; static Memo *memo[MEMOMAX]; static int nmemo = 0; int _getpw(int *pnum, char **pname, char **plist) { Dir *d; int f, n, i, j, matchnum, m, matched; char *eline, *f1, *f2, *f3, *f4; Memo *mem; static char *au = NULL; vlong length; if(!pname) return 0; if(au == NULL){ d = _dirstat(admusers); if(d == nil) return 0; length = d->length; free(d); if((au = (char *)malloc(length+2)) == NULL) return 0; f = open(admusers, O_RDONLY); if(f < 0) return 0; n = read(f, au, length); if(n < 0) return 0; au[n] = 0; } matchnum = (*pname == NULL); matched = 0; mem = (void*)0; /* try using memo */ for(i = 0; inum == *pnum); else matched = (strcmp(mem->name, *pname) == 0); if(matched) { break; } } if(!matched) for(f1 = au, eline = au; !matched && *eline; f1 = eline+1){ eline = strchr(f1, '\n'); if(!eline) eline = strchr(f1, 0); if(*f1 == '#' || *f1 == '\n') continue; n = eline-f1; f2 = memchr(f1, ':', n); if(!f2) continue; f2++; f3 = memchr(f2, ':', n-(f2-f1)); if(!f3) continue; f3++; f4 = memchr(f3, ':', n-(f3-f1)); if(!f4) continue; f4++; if(matchnum) matched = (atoi(f1) == *pnum); else{ int length; length = f3-f2-1; matched = length==strlen(*pname) && memcmp(*pname, f2, length)==0; } if(matched){ /* allocate and fill in a Memo structure */ mem = (Memo*)malloc(sizeof(struct Memo)); if(!mem) return 0; m = (f3-f2)-1; if(m > NAMEMAX-1) m = NAMEMAX-1; memcpy(mem->name, f2, m); mem->name[m] = 0; mem->num = atoi(f1); m = n-(f4-f1); if(m > 0){ mem->glist = (char*)malloc(m+1); if(mem->glist) { memcpy(mem->glist, f4, m); mem->glist[m] = 0; } } else mem->glist = 0; /* prepare for following move-to-front */ if(nmemo == MEMOMAX) { free(memo[nmemo-1]); i = nmemo-1; } else { i = nmemo++; } } } if(matched) { if(matchnum) *pname = mem->name; else *pnum = mem->num; if(plist) *plist = mem->glist; if(i > 0) { /* make room at front */ for(j = i; j > 0; j--) memo[j] = memo[j-1]; } memo[0] = mem; return 1; } return 0; } char ** _grpmems(char *list) { char **v; char *p; static char *holdvec[200]; static char holdlist[1000]; p = list; v = holdvec; if(p) { strncpy(holdlist, list, sizeof(holdlist)); while(v< &holdvec[sizeof(holdvec)]-1 && *p){ *v++ = p; p = strchr(p, ','); if(p){ p++; *p = 0; }else break; } } *v = 0; return holdvec; } /sys/src/ape/lib/ap/plan9/_nap.c 664 sys sys 1367613437 266 #include "lib.h" #include #include #include "sys9.h" /* * This is an extension to POSIX */ unsigned int _nap(unsigned int millisecs) { time_t t0, t1; t0 = time(0); if(_SLEEP(millisecs) < 0){ t1 = time(0); return t1-t0; } return 0; } /sys/src/ape/lib/ap/plan9/access.c 664 sys sys 1369166818 901 #include "lib.h" #include #include #include #include #include #include #include "sys9.h" #include "dir.h" int access(const char *name, int mode) { int fd; Dir *db; struct stat st; static char omode[] = { 0, 3, 1, 2, 0, 2, 2, 2 }; char tname[1024]; if(mode == 0){ db = _dirstat(name); if(db == nil){ _syserrno(); return -1; } free(db); return 0; } fd = open(name, omode[mode&7]); if(fd >= 0){ close(fd); return 0; } else if(stat(name, &st)==0 && S_ISDIR(st.st_mode)){ if(mode & (R_OK|X_OK)){ fd = open(name, O_RDONLY); if(fd < 0) return -1; close(fd); } if(mode & W_OK){ strncpy(tname, name, sizeof(tname)-9); strcat(tname, "/_AcChAcK"); fd = creat(tname, 0666); if(fd < 0) return -1; close(fd); _REMOVE(tname); } return 0; } return -1; } /sys/src/ape/lib/ap/plan9/acid.c 664 sys sys 1367613437 729 /* include struct defs to get acid library cpp -I/sys/include/ape -I/$objtype/include/ape -I./include acid.c > t.c vc -a t.c > acidlib */ #define _POSIX_SOURCE 1 #define _BSD_EXTENSION 1 #define _LOCK_EXTENSION #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* #include "lib.h" buf.c below */ /* #include "sys9.h" buf.c below */ #include "_buf.c" #include "dir.h" #include "fcall.h" /sys/src/ape/lib/ap/plan9/acidlib 664 sys sys 1367613437 10866 sizeof_1_ = 8; aggr _1_ { 'D' 0 quot; 'D' 4 rem; }; defn _1_(addr) { complex _1_ addr; print(" quot ", addr.quot, "\n"); print(" rem ", addr.rem, "\n"); }; sizeof_2_ = 8; aggr _2_ { 'D' 0 quot; 'D' 4 rem; }; defn _2_(addr) { complex _2_ addr; print(" quot ", addr.quot, "\n"); print(" rem ", addr.rem, "\n"); }; sizeofsigaction = 12; aggr sigaction { 'X' 0 sa_handler; 'D' 4 sa_mask; 'D' 8 sa_flags; }; defn sigaction(addr) { complex sigaction addr; print(" sa_handler ", addr.sa_handler\X, "\n"); print(" sa_mask ", addr.sa_mask, "\n"); print(" sa_flags ", addr.sa_flags, "\n"); }; sizeof_3_ = 32; aggr _3_ { 'D' 0 fd; 'C' 4 flags; 'C' 5 state; 'X' 8 buf; 'X' 12 rp; 'X' 16 wp; 'X' 20 lp; 'U' 24 bufl; 'a' 28 unbuf; }; defn _3_(addr) { complex _3_ addr; print(" fd ", addr.fd, "\n"); print(" flags ", addr.flags, "\n"); print(" state ", addr.state, "\n"); print(" buf ", addr.buf\X, "\n"); print(" rp ", addr.rp\X, "\n"); print(" wp ", addr.wp\X, "\n"); print(" lp ", addr.lp\X, "\n"); print(" bufl ", addr.bufl, "\n"); print(" unbuf ", addr.unbuf, "\n"); }; sizeof_4_ = 4; aggr _4_ { 'D' 0 val; }; defn _4_(addr) { complex _4_ addr; print(" val ", addr.val, "\n"); }; sizeoftimeval = 8; aggr timeval { 'D' 0 tv_sec; 'D' 4 tv_usec; }; defn timeval(addr) { complex timeval addr; print(" tv_sec ", addr.tv_sec, "\n"); print(" tv_usec ", addr.tv_usec, "\n"); }; sizeoftimezone = 8; aggr timezone { 'D' 0 tz_minuteswest; 'D' 4 tz_dsttime; }; defn timezone(addr) { complex timezone addr; print(" tz_minuteswest ", addr.tz_minuteswest, "\n"); print(" tz_dsttime ", addr.tz_dsttime, "\n"); }; sizeoffd_set = 12; aggr fd_set { 'a' 0 fds_bits; }; defn fd_set(addr) { complex fd_set addr; mem(addr, "3X"); }; sizeofstat = 28; aggr stat { 'u' 0 st_dev; 'u' 2 st_ino; 'u' 4 st_mode; 'd' 6 st_nlink; 'd' 8 st_uid; 'd' 10 st_gid; 'D' 12 st_size; 'D' 16 st_atime; 'D' 20 st_mtime; 'D' 24 st_ctime; }; defn stat(addr) { complex stat addr; print(" st_dev ", addr.st_dev, "\n"); print(" st_ino ", addr.st_ino, "\n"); print(" st_mode ", addr.st_mode, "\n"); print(" st_nlink ", addr.st_nlink, "\n"); print(" st_uid ", addr.st_uid, "\n"); print(" st_gid ", addr.st_gid, "\n"); print(" st_size ", addr.st_size, "\n"); print(" st_atime ", addr.st_atime, "\n"); print(" st_mtime ", addr.st_mtime, "\n"); print(" st_ctime ", addr.st_ctime, "\n"); }; sizeofflock = 16; aggr flock { 'd' 0 l_type; 'd' 2 l_whence; 'D' 4 l_start; 'D' 8 l_len; 'D' 12 l_pid; }; defn flock(addr) { complex flock addr; print(" l_type ", addr.l_type, "\n"); print(" l_whence ", addr.l_whence, "\n"); print(" l_start ", addr.l_start, "\n"); print(" l_len ", addr.l_len, "\n"); print(" l_pid ", addr.l_pid, "\n"); }; sizeofdirent = 28; aggr dirent { 'a' 0 d_name; }; defn dirent(addr) { complex dirent addr; print(" d_name ", addr.d_name, "\n"); }; sizeof_dirdesc = 16; aggr _dirdesc { 'D' 0 dd_fd; 'D' 4 dd_loc; 'D' 8 dd_size; 'X' 12 dd_buf; }; defn _dirdesc(addr) { complex _dirdesc addr; print(" dd_fd ", addr.dd_fd, "\n"); print(" dd_loc ", addr.dd_loc, "\n"); print(" dd_size ", addr.dd_size, "\n"); print(" dd_buf ", addr.dd_buf\X, "\n"); }; sizeoftermios = 28; aggr termios { 'U' 0 c_iflag; 'U' 4 c_oflag; 'U' 8 c_cflag; 'U' 12 c_lflag; 'a' 16 c_cc; }; defn termios(addr) { complex termios addr; print(" c_iflag ", addr.c_iflag, "\n"); print(" c_oflag ", addr.c_oflag, "\n"); print(" c_cflag ", addr.c_cflag, "\n"); print(" c_lflag ", addr.c_lflag, "\n"); print(" c_cc ", addr.c_cc, "\n"); }; sizeofutsname = 20; aggr utsname { 'X' 0 sysname; 'X' 4 nodename; 'X' 8 release; 'X' 12 version; 'X' 16 machine; }; defn utsname(addr) { complex utsname addr; print(" sysname ", addr.sysname\X, "\n"); print(" nodename ", addr.nodename\X, "\n"); print(" release ", addr.release\X, "\n"); print(" version ", addr.version\X, "\n"); print(" machine ", addr.machine\X, "\n"); }; sizeofMuxbuf = 16400; aggr Muxbuf { 'D' 0 n; 'X' 4 putnext; 'X' 8 getnext; 'b' 12 fd; 'b' 13 eof; 'b' 14 roomwait; 'b' 15 datawait; 'a' 16 data; }; defn Muxbuf(addr) { complex Muxbuf addr; print(" n ", addr.n, "\n"); print(" putnext ", addr.putnext\X, "\n"); print(" getnext ", addr.getnext\X, "\n"); print(" fd ", addr.fd, "\n"); print(" eof ", addr.eof, "\n"); print(" roomwait ", addr.roomwait, "\n"); print(" datawait ", addr.datawait, "\n"); print(" data ", addr.data, "\n"); }; sizeofFdinfo = 16; aggr Fdinfo { 'U' 0 flags; 'U' 4 oflags; 'X' 8 name; 'A' Muxbuf 12 buf; }; defn Fdinfo(addr) { complex Fdinfo addr; print(" flags ", addr.flags, "\n"); print(" oflags ", addr.oflags, "\n"); print(" name ", addr.name\X, "\n"); print(" buf ", addr.buf\X, "\n"); }; sizeofWaitmsg = 112; aggr Waitmsg { 'a' 0 pid; 'a' 12 time; 'a' 48 msg; }; defn Waitmsg(addr) { complex Waitmsg addr; print(" pid ", addr.pid, "\n"); print(" time ", addr.time, "\n"); print(" msg ", addr.msg, "\n"); }; sizeof_5_ = 8; aggr _5_ { 'D' 0 hlength; 'D' 4 length; }; defn _5_(addr) { complex _5_ addr; print(" hlength ", addr.hlength, "\n"); print(" length ", addr.length, "\n"); }; sizeof_6_ = 8; aggr _6_ { 'a' 0 clength; 'D' 0 vlength; { 'D' 0 hlength; 'D' 4 length; }; }; defn _6_(addr) { complex _6_ addr; print(" clength ", addr.clength, "\n"); print(" vlength ", addr.vlength, "\n"); print("_5_ {\n"); _5_(addr+0); print("}\n"); }; sizeofQid = 8; aggr Qid { 'U' 0 path; 'U' 4 vers; }; defn Qid(addr) { complex Qid addr; print(" path ", addr.path, "\n"); print(" vers ", addr.vers, "\n"); }; sizeofDir = 116; aggr Dir { 'a' 0 name; 'a' 28 uid; 'a' 56 gid; Qid 84 qid; 'U' 92 mode; 'D' 96 atime; 'D' 100 mtime; { 'a' 104 clength; 'D' 104 vlength; { 'D' 104 hlength; 'D' 108 length; }; }; 'd' 112 type; 'd' 114 dev; }; defn Dir(addr) { complex Dir addr; print(" name ", addr.name, "\n"); print(" uid ", addr.uid, "\n"); print(" gid ", addr.gid, "\n"); print("Qid qid {\n"); Qid(addr.qid); print("}\n"); print(" mode ", addr.mode, "\n"); print(" atime ", addr.atime, "\n"); print(" mtime ", addr.mtime, "\n"); print("_6_ {\n"); _6_(addr+104); print("}\n"); print(" type ", addr.type, "\n"); print(" dev ", addr.dev, "\n"); }; sizeof_7_ = 28; aggr _7_ { 'u' 0 oldtag; Qid 4 qid; 'a' 12 rauth; }; defn _7_(addr) { complex _7_ addr; print(" oldtag ", addr.oldtag, "\n"); print("Qid qid {\n"); Qid(addr.qid); print("}\n"); print(" rauth ", addr.rauth, "\n"); }; sizeof_8_ = 144; aggr _8_ { 'a' 0 uname; 'a' 28 aname; 'a' 56 ticket; 'a' 128 auth; }; defn _8_(addr) { complex _8_ addr; print(" uname ", addr.uname, "\n"); print(" aname ", addr.aname, "\n"); print(" ticket ", addr.ticket, "\n"); print(" auth ", addr.auth, "\n"); }; sizeof_9_ = 148; aggr _9_ { 'a' 0 ename; 'a' 64 authid; 'a' 92 authdom; 'a' 140 chal; }; defn _9_(addr) { complex _9_ addr; print(" ename ", addr.ename, "\n"); print(" authid ", addr.authid, "\n"); print(" authdom ", addr.authdom, "\n"); print(" chal ", addr.chal, "\n"); }; sizeof_10_ = 36; aggr _10_ { 'D' 0 perm; 'd' 4 newfid; 'a' 6 name; 'C' 34 mode; }; defn _10_(addr) { complex _10_ addr; print(" perm ", addr.perm, "\n"); print(" newfid ", addr.newfid, "\n"); print(" name ", addr.name, "\n"); print(" mode ", addr.mode, "\n"); }; sizeof_11_ = 12; aggr _11_ { 'D' 0 offset; 'D' 4 count; 'X' 8 data; }; defn _11_(addr) { complex _11_ addr; print(" offset ", addr.offset, "\n"); print(" count ", addr.count, "\n"); print(" data ", addr.data\X, "\n"); }; sizeof_12_ = 116; aggr _12_ { 'a' 0 stat; }; defn _12_(addr) { complex _12_ addr; print(" stat ", addr.stat, "\n"); }; sizeof_13_ = 148; aggr _13_ { { 'u' 0 oldtag; Qid 4 qid; 'a' 12 rauth; }; { 'a' 0 uname; 'a' 28 aname; 'a' 56 ticket; 'a' 128 auth; }; { 'a' 0 ename; 'a' 64 authid; 'a' 92 authdom; 'a' 140 chal; }; { 'D' 0 perm; 'd' 4 newfid; 'a' 6 name; 'C' 34 mode; }; { 'D' 0 offset; 'D' 4 count; 'X' 8 data; }; { 'a' 0 stat; }; }; defn _13_(addr) { complex _13_ addr; print("_7_ {\n"); _7_(addr+0); print("}\n"); print("_8_ {\n"); _8_(addr+0); print("}\n"); print("_9_ {\n"); _9_(addr+0); print("}\n"); print("_10_ {\n"); _10_(addr+0); print("}\n"); print("_11_ {\n"); _11_(addr+0); print("}\n"); print("_12_ {\n"); _12_(addr+0); print("}\n"); }; sizeofFcall = 156; aggr Fcall { 'C' 0 type; 'd' 2 fid; 'u' 4 tag; { { 'u' 8 oldtag; Qid 12 qid; 'a' 20 rauth; }; { 'a' 8 uname; 'a' 36 aname; 'a' 64 ticket; 'a' 136 auth; }; { 'a' 8 ename; 'a' 72 authid; 'a' 100 authdom; 'a' 148 chal; }; { 'D' 8 perm; 'd' 12 newfid; 'a' 14 name; 'C' 42 mode; }; { 'D' 8 offset; 'D' 12 count; 'X' 16 data; }; { 'a' 8 stat; }; }; }; defn Fcall(addr) { complex Fcall addr; print(" type ", addr.type, "\n"); print(" fid ", addr.fid, "\n"); print(" tag ", addr.tag, "\n"); print("_13_ {\n"); _13_(addr+8); print("}\n"); }; sizeofMuxbuf = 16400; aggr Muxbuf { 'D' 0 n; 'X' 4 putnext; 'X' 8 getnext; 'b' 12 fd; 'b' 13 eof; 'b' 14 roomwait; 'b' 15 datawait; 'a' 16 data; }; defn Muxbuf(addr) { complex Muxbuf addr; print(" n ", addr.n, "\n"); print(" putnext ", addr.putnext\X, "\n"); print(" getnext ", addr.getnext\X, "\n"); print(" fd ", addr.fd, "\n"); print(" eof ", addr.eof, "\n"); print(" roomwait ", addr.roomwait, "\n"); print(" datawait ", addr.datawait, "\n"); print(" data ", addr.data, "\n"); }; sizeofFdinfo = 16; aggr Fdinfo { 'U' 0 flags; 'U' 4 oflags; 'X' 8 name; 'A' Muxbuf 12 buf; }; defn Fdinfo(addr) { complex Fdinfo addr; print(" flags ", addr.flags, "\n"); print(" oflags ", addr.oflags, "\n"); print(" name ", addr.name\X, "\n"); print(" buf ", addr.buf\X, "\n"); }; sizeofWaitmsg = 112; aggr Waitmsg { 'a' 0 pid; 'a' 12 time; 'a' 48 msg; }; defn Waitmsg(addr) { complex Waitmsg addr; print(" pid ", addr.pid, "\n"); print(" time ", addr.time, "\n"); print(" msg ", addr.msg, "\n"); }; sizeofMuxseg = 65640; aggr Muxseg { _4_ 0 lock; 'D' 4 curfds; 'D' 8 selwait; 'D' 12 waittime; fd_set 16 rwant; fd_set 28 ewant; 'a' 40 bufs; }; defn Muxseg(addr) { complex Muxseg addr; print("_4_ lock {\n"); _4_(addr.lock); print("}\n"); print(" curfds ", addr.curfds, "\n"); print(" selwait ", addr.selwait, "\n"); print(" waittime ", addr.waittime, "\n"); print("fd_set rwant {\n"); fd_set(addr.rwant); print("}\n"); print("fd_set ewant {\n"); fd_set(addr.ewant); print("}\n"); print(" bufs ", addr.bufs, "\n"); }; complex Muxseg mux; complex Fdinfo _startbuf:f; complex Muxbuf _startbuf:b; complex Muxbuf _copyproc:b; complex Muxbuf _readbuf:b; complex fd_set select:rfds; complex fd_set select:wfds; complex fd_set select:efds; complex timeval select:timeout; complex Fdinfo select:f; complex Muxbuf select:b; /sys/src/ape/lib/ap/plan9/alarm.c 664 sys sys 1367613437 127 #include "lib.h" #include #include "sys9.h" unsigned int alarm(unsigned seconds) { return _ALARM(seconds*1000); } /sys/src/ape/lib/ap/plan9/brk.c 664 sys sys 1369166818 554 #include "lib.h" #include #include #include "sys9.h" char end[]; static char *bloc = { end }; extern int _BRK_(void*); enum { Round = 7 }; char * brk(char *p) { uintptr_t bl; bl = ((uintptr_t)p + Round) & ~Round; if(_BRK_((void*)bl) < 0){ errno = ENOMEM; return (char *)-1; } bloc = (char*)bl; return 0; } void * sbrk(unsigned long n) { uintptr_t bl; bl = ((uintptr_t)bloc + Round) & ~Round; if(_BRK_((void *)(bloc+n)) < 0){ errno = ENOMEM; return (void *)-1; } bloc = (char*)bl + n; return (void*)bl; } /sys/src/ape/lib/ap/plan9/buf.prom 664 sys sys 1367613437 6783 #define NBUFS 2 #define READMAX 2 #define BUFSIZ 2*READMAX #define EOF 255 #define TIMEOUT 254 #define FILEMAXLEN 20 byte n[NBUFS]; byte ntotal[NBUFS]; byte putnext[NBUFS]; byte getnext[NBUFS]; bool eof[NBUFS]; bool roomwait[NBUFS]; bool datawait[NBUFS]; byte rwant; /* use one big data array to simulate 2-d array */ #define bufstart(slot) (slot*BUFSIZ) #define bufend(slot) ((slot+1)*BUFSIZ) /* bit data[BUFSIZ*NBUFS]; */ bool selwait; /* bool hastimeout; */ #define get 0 #define release 1 chan lock = [0] of { bit }; chan lockkill = [0] of { bit }; chan sel = [0] of { byte }; chan selcall = [0] of { byte }; chan selans = [0] of { byte, byte }; chan selkill = [0] of { bit }; chan readcall = [0] of { byte, byte }; chan readans = [0] of { byte }; chan readkill = [0] of { bit }; chan croom[NBUFS] = [0] of { bit }; chan cdata[NBUFS] = [0] of { bit }; proctype Lockrendez() { do :: lock!get -> lock?release :: lockkill?release -> break od } proctype Copy(byte fd) { byte num; bit b; do :: 1 -> /* make sure there's room */ lock?get; if :: (BUFSIZ-putnext[fd]) < READMAX -> if :: getnext[fd] == putnext[fd] -> getnext[fd] = 0; putnext[fd] = 0; lock!release :: getnext[fd] != putnext[fd] -> roomwait[fd] = 1; lock!release; croom[fd]?b fi :: (BUFSIZ-putnext[fd]) >= READMAX -> lock!release fi; /* simulate read into data buf at putnext */ if :: ntotal[fd] > FILEMAXLEN -> num = EOF :: ntotal[fd] <= FILEMAXLEN -> if :: num = 1 :: num = READMAX :: num = EOF fi fi; /* here is where data transfer would happen */ lock?get; if :: num == EOF -> eof[fd] = 1; /* printf("Copy %d got eof\n", fd);/**/ if :: datawait[fd] -> datawait[fd] = 0; lock!release; cdata[fd]!1 :: !datawait[fd] && (rwant & (1< selwait = 0; lock!release; sel!fd :: !datawait[fd] && !((rwant & (1< lock!release fi; break :: num != EOF -> /* printf("Copy %d putting %d in; old putnext=%d, old n=%d\n", fd, num, putnext[fd], n[fd]); /* */ putnext[fd] = putnext[fd] + num; n[fd] = n[fd] + num; ntotal[fd] = ntotal[fd] + num; assert(n[fd] > 0); if :: datawait[fd] -> datawait[fd] = 0; lock!release; cdata[fd]!1 :: !datawait[fd] && (rwant & (1< selwait = 0; lock!release; sel!fd :: !datawait[fd] && !((rwant & (1< lock!release fi fi; od } proctype Read() { byte ngot; byte fd; byte nwant; bit b; do :: readcall?fd,nwant -> if :: eof[fd] && n[fd] == 0 -> readans!EOF :: !(eof[fd] && n[fd] == 0) -> lock?get; ngot = putnext[fd] - getnext[fd]; /* printf("Reading %d, want %d: ngot = %d - %d, n = %d\n", fd, nwant, putnext[fd], getnext[fd], n[fd]); /* */ if :: ngot == 0 -> if :: eof[fd] -> skip :: !eof[fd] -> /* sleep until there's data */ datawait[fd] = 1; /* printf("Read sleeping\n"); /* */ lock!release; cdata[fd]?b; lock?get; ngot = putnext[fd] - getnext[fd]; /* printf("Read awoke, ngot = %d\n", ngot); /**/ fi :: ngot != 0 -> skip fi; if :: ngot > nwant -> ngot = nwant :: ngot <= nwant -> skip fi; /* here would take ngot elements from data, from getnext[fd] ... */ getnext[fd] = getnext[fd] + ngot; assert(n[fd] >= ngot); n[fd] = n[fd] - ngot; if :: ngot == 0 -> assert(eof[fd]); ngot = EOF :: ngot != 0 -> skip fi; if :: getnext[fd] == putnext[fd] && roomwait[fd] -> getnext[fd] = 0; putnext[fd] = 0; roomwait[fd] = 0; lock!release; croom[fd]!0 :: getnext[fd] != putnext[fd] || !roomwait[fd] -> lock!release fi; readans!ngot fi :: readkill?b -> break od } proctype Select() { byte num; byte i; byte fd; byte r; bit b; do :: selcall?r -> /* printf("Select called, r=%d\n", r); /**/ i = 0; do :: i < NBUFS -> if :: r & (1< if :: eof[i] && n[i] == 0 -> /* printf("Select got eof on %d\n", i);/**/ num = EOF; r = i; goto donesel :: !eof[i] || n[i] != 0 -> skip fi :: !(r & (1< skip fi; i = i+1 :: i >= NBUFS -> break od; num = 0; lock?get; rwant = 0; i = 0; do :: i < NBUFS -> if :: r & (1< if :: n[i] > 0 || eof[i] -> /* printf("Select found %d has n==%d\n", i, n[i]); /**/ num = num+1 :: n[i] == 0 && !eof[i] -> /* printf("Select asks to wait for %d\n", i); /**/ r = r &(~(1< skip fi; i = i+1 :: i >= NBUFS -> break od; if :: num > 0 || rwant == 0 -> rwant = 0; lock!release; :: num == 0 && rwant != 0 -> selwait = 1; lock!release; /* printf("Select sleeps\n"); /**/ sel?fd; /* printf("Select wakes up, fd=%d\n", fd); /**/ if :: fd != TIMEOUT -> if :: (rwant & (1< 0) -> r = r | (1< num = 0 fi :: fd == TIMEOUT -> skip fi; rwant = 0 fi; donesel: selans!num,r :: selkill?b -> break od } /* This routine is written knowing NBUFS == 2 in several places */ proctype User() { byte ndone; byte i; byte rw; byte num; byte nwant; byte fd; bool goteof[NBUFS]; ndone = 0; do :: ndone == NBUFS -> break :: ndone < NBUFS -> if :: 1-> /* maybe use Read */ /* printf("User trying to read. Current goteofs: %d %d\n", goteof[0], goteof[1]); /**/ /* randomly pick fd 0 or 1 from non-eof ones */ if :: !goteof[0] -> fd = 0 :: !goteof[1] -> fd = 1 fi; if :: nwant = 1 :: nwant = READMAX fi; readcall!fd,nwant; readans?num; if :: num == EOF -> goteof[fd] = 1; ndone = ndone + 1 :: num != EOF -> assert(num != 0) fi :: 1-> /* printf("User trying to select. Current goteofs: %d %d\n", goteof[0], goteof[1]); /**/ /* maybe use Select, then Read */ /* randomly set the "i want" bit for non-eof fds */ if :: !goteof[0] && !goteof[1] -> rw = (1<<0) | (1<<1) :: !goteof[0] -> rw = (1<<0) :: !goteof[1] -> rw = (1<<1) fi; selcall!rw; selans?i,rw; if :: i == EOF -> goteof[rw] = 1; ndone = ndone + 1 :: i != EOF -> /* this next statement knows NBUFS == 2 ! */ if :: rw & (1<<0) -> fd = 0 :: rw & (1<<1) -> fd = 1 :: rw == 0 -> fd = EOF fi; if :: nwant = 1 :: nwant = READMAX fi; if :: fd != EOF -> readcall!fd,nwant; readans?num; assert(num != 0) :: fd == EOF -> skip fi fi fi od; lockkill!release; selkill!release; readkill!release } init { byte i; atomic { run Lockrendez(); i = 0; do :: i < NBUFS -> run Copy(i); i = i+1 :: i >= NBUFS -> break od; run Select(); run Read(); run User() } } /sys/src/ape/lib/ap/plan9/cfgetospeed.c 664 sys sys 1369166818 261 #include speed_t cfgetospeed(const struct termios *) { return B0; } int cfsetospeed(struct termios *, speed_t) { return 0; } speed_t cfgetispeed(const struct termios *) { return B0; } int cfsetispeed(struct termios *, speed_t) { return 0; } /sys/src/ape/lib/ap/plan9/chdir.c 664 sys sys 1367613437 147 #include "lib.h" #include #include "sys9.h" int chdir(const char *f) { int n; n = _CHDIR(f); if(n < 0) _syserrno(); return n; } /sys/src/ape/lib/ap/plan9/chmod.c 664 sys sys 1367613437 649 #include "lib.h" #include #include #include "sys9.h" #include "dir.h" static int seterrno(void) { _syserrno(); return -1; } int chmod(const char *path, mode_t mode) { Dir d, *dir; dir = _dirstat(path); if(dir == nil) return seterrno(); _nulldir(&d); d.mode = (dir->mode & ~0777) | (mode & 0777); free(dir); if(_dirwstat(path, &d) < 0) return seterrno(); return 0; } int fchmod(int fd, mode_t mode) { Dir d, *dir; dir = _dirfstat(fd); if(dir == nil) return seterrno(); _nulldir(&d); d.mode = (dir->mode & ~0777) | (mode & 0777); free(dir); if(_dirfwstat(fd, &d) < 0) return seterrno(); return 0; } /sys/src/ape/lib/ap/plan9/chown.c 664 sys sys 1367613437 557 #include "lib.h" #include "sys9.h" #include #include #include #include #include #include #include "dir.h" int chown(const char *path, uid_t owner, gid_t group) { int num; Dir d; _nulldir(&d); /* find owner, group */ d.uid = nil; num = owner; if(!_getpw(&num, &d.uid, 0)) { errno = EINVAL; return -1; } d.gid = nil; num = group; if(!_getpw(&num, &d.gid, 0)) { errno = EINVAL; return -1; } if(_dirwstat(path, &d) < 0){ _syserrno(); return -1; } return 0; } /sys/src/ape/lib/ap/plan9/close.c 664 sys sys 1367613437 545 #include "lib.h" #include #include #include #include "sys9.h" int close(int d) { int n; Fdinfo *f; n = -1; f = &_fdinfo[d]; if(d<0 || d>=OPEN_MAX || !(f->flags&FD_ISOPEN)) errno = EBADF; else{ if(f->flags&(FD_BUFFERED|FD_BUFFEREDX)) { if(f->flags&FD_BUFFERED) _closebuf(d); f->flags &= ~FD_BUFFERED; } n = _CLOSE(d); if(n < 0) _syserrno(); _fdinfo[d].flags = 0; _fdinfo[d].oflags = 0; if(_fdinfo[d].name){ free(_fdinfo[d].name); _fdinfo[d].name = 0; } } return n; } /sys/src/ape/lib/ap/plan9/convD2M.c 664 sys sys 1367613437 1376 #include "lib.h" #include #include "sys9.h" #include "dir.h" uint _convD2M(Dir *d, uchar *buf, uint nbuf) { uchar *p, *ebuf; char *sv[4]; int i, ns, nsv[4], ss; if(nbuf < BIT16SZ) return 0; p = buf; ebuf = buf + nbuf; sv[0] = d->name; sv[1] = d->uid; sv[2] = d->gid; sv[3] = d->muid; ns = 0; for(i = 0; i < 4; i++){ nsv[i] = strlen(sv[i]); ns += nsv[i]; } ss = STATFIXLEN + ns; /* set size befor erroring, so user can know how much is needed */ /* note that length excludes count field itself */ PBIT16(p, ss-BIT16SZ); p += BIT16SZ; if(ss > nbuf) return BIT16SZ; PBIT16(p, d->type); p += BIT16SZ; PBIT32(p, d->dev); p += BIT32SZ; PBIT8(p, d->qid.type); p += BIT8SZ; PBIT32(p, d->qid.vers); p += BIT32SZ; PBIT64(p, d->qid.path); p += BIT64SZ; PBIT32(p, d->mode); p += BIT32SZ; PBIT32(p, d->atime); p += BIT32SZ; PBIT32(p, d->mtime); p += BIT32SZ; PBIT64(p, d->length); p += BIT64SZ; for(i = 0; i < 4; i++){ ns = nsv[i]; if(p + ns + BIT16SZ > ebuf) return 0; PBIT16(p, ns); p += BIT16SZ; memmove(p, sv[i], ns); p += ns; } if(ss != p - buf) return 0; return p - buf; } uint _sizeD2M(Dir *d) { char *sv[4]; int i, ns; sv[0] = d->name; sv[1] = d->uid; sv[2] = d->gid; sv[3] = d->muid; ns = 0; for(i = 0; i < 4; i++) if(sv[i]) ns += strlen(sv[i]); return STATFIXLEN + ns; } /sys/src/ape/lib/ap/plan9/convM2D.c 664 sys sys 1367613437 1178 #include "lib.h" #include #include "sys9.h" #include "dir.h" #define nil ((void*)0) static char nullstring[] = ""; uint _convM2D(uchar *buf, uint nbuf, Dir *d, char *strs) { uchar *p, *ebuf; char *sv[4]; int i, ns, nsv[4]; p = buf; ebuf = buf + nbuf; p += BIT16SZ; /* ignore size */ d->type = GBIT16(p); p += BIT16SZ; d->dev = GBIT32(p); p += BIT32SZ; d->qid.type = GBIT8(p); p += BIT8SZ; d->qid.vers = GBIT32(p); p += BIT32SZ; d->qid.path = GBIT64(p); p += BIT64SZ; d->mode = GBIT32(p); p += BIT32SZ; d->atime = GBIT32(p); p += BIT32SZ; d->mtime = GBIT32(p); p += BIT32SZ; d->length = GBIT64(p); p += BIT64SZ; d->name = nil; d->uid = nil; d->gid = nil; d->muid = nil; for(i = 0; i < 4; i++){ if(p + BIT16SZ > ebuf) return 0; ns = GBIT16(p); p += BIT16SZ; if(p + ns > ebuf) return 0; if(strs){ nsv[i] = ns; sv[i] = strs; memmove(strs, p, ns); strs += ns; *strs++ = '\0'; } p += ns; } if(strs){ d->name = sv[0]; d->uid = sv[1]; d->gid = sv[2]; d->muid = sv[3]; }else{ d->name = nullstring; d->uid = nullstring; d->gid = nullstring; d->muid = nullstring; } return p - buf; } /sys/src/ape/lib/ap/plan9/creat.c 664 sys sys 1367613437 226 #include "lib.h" #include #include int creat(const char *name, mode_t mode) { int n; n = open(name, O_WRONLY | O_CREAT | O_TRUNC, mode); /* no need to _syserrno; open did it already */ return n; } /sys/src/ape/lib/ap/plan9/ctermid.c 664 sys sys 1367613437 253 #include #include #include char * ctermid(char *s) { static char buf[L_ctermid]; if(s == 0) s = buf; strncpy(s, "/dev/cons", sizeof buf); return(s); } char * ctermid_r(char *s) { return s ? ctermid(s) : NULL; } /sys/src/ape/lib/ap/plan9/ctime.c 664 sys sys 1369166818 5231 /* * This routine converts time as follows. * The epoch is 0000 Jan 1 1970 GMT. * The argument time is in seconds since then. * The localtime(t) entry returns a pointer to an array * containing * * seconds (0-59) * minutes (0-59) * hours (0-23) * day of month (1-31) * month (0-11) * year-1970 * weekday (0-6, Sun is 0) * day of the year * daylight savings flag * * The routine gets the daylight savings time from the environment. * * asctime(tvec)) * where tvec is produced by localtime * returns a ptr to a character string * that has the ascii time in the form * * \\ * Thu Jan 01 00:00:00 1970n0 * 01234567890123456789012345 * 0 1 2 * * ctime(t) just calls localtime, then asctime. */ #include #include #include #include #include #include static char dmsize[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; /* * The following table is used for 1974 and 1975 and * gives the day number of the first day after the Sunday of the * change. */ static int dysize(int); static void ct_numb(char*, int); static void readtimezone(void); static int rd_name(char**, char*); static int rd_long(char**, long*); #define TZSIZE 150 static struct { char stname[4]; char dlname[4]; long stdiff; long dldiff; long dlpairs[TZSIZE]; } timezone; char* ctime(const time_t *t) { return asctime(localtime(t)); } struct tm* gmtime_r(const time_t *timp, struct tm *result) { int d0, d1; long hms, day; time_t tim; tim = *timp; /* * break initial number into days */ hms = tim % 86400L; day = tim / 86400L; if(hms < 0) { hms += 86400L; day -= 1; } /* * generate hours:minutes:seconds */ result->tm_sec = hms % 60; d1 = hms / 60; result->tm_min = d1 % 60; d1 /= 60; result->tm_hour = d1; /* * day is the day number. * generate day of the week. * The addend is 4 mod 7 (1/1/1970 was Thursday) */ result->tm_wday = (day + 7340036L) % 7; /* * year number */ if(day >= 0) for(d1 = 70; day >= dysize(d1); d1++) day -= dysize(d1); else for (d1 = 70; day < 0; d1--) day += dysize(d1-1); result->tm_year = d1; result->tm_yday = d0 = day; /* * generate month */ if(dysize(d1) == 366) dmsize[1] = 29; for(d1 = 0; d0 >= dmsize[d1]; d1++) d0 -= dmsize[d1]; dmsize[1] = 28; result->tm_mday = d0 + 1; result->tm_mon = d1; result->tm_isdst = 0; return result; } struct tm* gmtime(const time_t *timp) { static struct tm xtime; return gmtime_r(timp, &xtime); } struct tm* localtime_r(const time_t *timp, struct tm *result) { struct tm *ct; time_t t, tim; long *p; int dlflag; tim = *timp; if(timezone.stname[0] == 0) readtimezone(); t = tim + timezone.stdiff; dlflag = 0; for(p = timezone.dlpairs; *p; p += 2) if(t >= p[0]) if(t < p[1]) { t = tim + timezone.dldiff; dlflag++; break; } ct = gmtime_r(&t, result); ct->tm_isdst = dlflag; return ct; } struct tm* localtime(const time_t *timp) { static struct tm xtime; return localtime_r(timp, &xtime); } char* asctime_r(const struct tm *t, char *buf) { char *ncp; strcpy(buf, "Thu Jan 01 00:00:00 1970\n"); ncp = &"SunMonTueWedThuFriSat"[t->tm_wday*3]; buf[0] = *ncp++; buf[1] = *ncp++; buf[2] = *ncp; ncp = &"JanFebMarAprMayJunJulAugSepOctNovDec"[t->tm_mon*3]; buf[4] = *ncp++; buf[5] = *ncp++; buf[6] = *ncp; ct_numb(buf+8, t->tm_mday); ct_numb(buf+11, t->tm_hour+100); ct_numb(buf+14, t->tm_min+100); ct_numb(buf+17, t->tm_sec+100); if(t->tm_year >= 100) { buf[20] = '2'; buf[21] = '0'; } ct_numb(buf+22, t->tm_year+100); return buf; } char* asctime(const struct tm *t) { static char cbuf[30]; return asctime_r(t, cbuf); } static dysize(int y) { if((y%4) == 0) return 366; return 365; } static void ct_numb(char *cp, int n) { cp[0] = ' '; if(n >= 10) cp[0] = (n/10)%10 + '0'; cp[1] = n%10 + '0'; } static void readtimezone(void) { char buf[TZSIZE*11+30], *p; int i; memset(buf, 0, sizeof(buf)); i = open("/env/timezone", 0); if(i < 0) goto error; if(read(i, buf, sizeof(buf)) >= sizeof(buf)) goto error; close(i); p = buf; if(rd_name(&p, timezone.stname)) goto error; if(rd_long(&p, &timezone.stdiff)) goto error; if(rd_name(&p, timezone.dlname)) goto error; if(rd_long(&p, &timezone.dldiff)) goto error; for(i=0; i '9') return 1; l = l*10 + c-'0'; c = *(*f)++; } if(s) l = -l; *p = l; return 0; } /sys/src/ape/lib/ap/plan9/cuserid.c 664 sys sys 1367613437 343 #include #include #include /* * BUG: supposed to be for effective uid, * but plan9 doesn't have that concept */ char * cuserid(char *s) { char *logname; static char buf[L_cuserid]; if((logname = getlogin()) == NULL) return(NULL); if(s == 0) s = buf; strncpy(s, logname, sizeof buf); return(s); } /sys/src/ape/lib/ap/plan9/dir.h 664 sys sys 1367613437 2092 typedef long long vlong; typedef unsigned long long uvlong; typedef unsigned char uchar; typedef unsigned short ushort; typedef unsigned int uint; typedef unsigned long ulong; #define GBIT8(p) ((p)[0]) #define GBIT16(p) ((p)[0]|((p)[1]<<8)) #define GBIT32(p) ((p)[0]|((p)[1]<<8)|((p)[2]<<16)|((p)[3]<<24)) #define GBIT64(p) ((vlong)((p)[0]|((p)[1]<<8)|((p)[2]<<16)|((p)[3]<<24)) |\ ((vlong)((p)[4]|((p)[5]<<8)|((p)[6]<<16)|((p)[7]<<24)) << 32)) #define PBIT8(p,v) (p)[0]=(v) #define PBIT16(p,v) (p)[0]=(v);(p)[1]=(v)>>8 #define PBIT32(p,v) (p)[0]=(v);(p)[1]=(v)>>8;(p)[2]=(v)>>16;(p)[3]=(v)>>24 #define PBIT64(p,v) (p)[0]=(v);(p)[1]=(v)>>8;(p)[2]=(v)>>16;(p)[3]=(v)>>24;\ (p)[4]=(v)>>32;(p)[5]=(v)>>40;(p)[6]=(v)>>48;(p)[7]=(v)>>56 #define BIT8SZ 1 #define BIT16SZ 2 #define BIT32SZ 4 #define BIT64SZ 8 #define QIDSZ (BIT8SZ+BIT32SZ+BIT64SZ) /* STATFIXLEN includes leading 16-bit count */ /* The count, however, excludes itself; total size is BIT16SZ+count */ #define STATFIXLEN (BIT16SZ+QIDSZ+5*BIT16SZ+4*BIT32SZ+1*BIT64SZ) /* amount of fixed length data in a stat buffer */ typedef union { char clength[8]; vlong vlength; struct { long hlength; long length; }; } Length; typedef struct Qid { uvlong path; ulong vers; uchar type; } Qid; typedef struct Dir { /* system-modified data */ ushort type; /* server type */ uint dev; /* server subtype */ /* file data */ Qid qid; /* unique id from server */ ulong mode; /* permissions */ ulong atime; /* last read time */ ulong mtime; /* last write time */ vlong length; /* file length: see */ char *name; /* last element of path */ char *uid; /* owner name */ char *gid; /* group name */ char *muid; /* last modifier name */ } Dir; void _dirtostat(struct stat *, Dir*, Fdinfo*); uint _convM2D(uchar*, uint, Dir*, char*); uint _convD2M(Dir*, uchar*, uint); Dir *_dirstat(char*); int _dirwstat(char*, Dir*); Dir *_dirfstat(int); int _dirfwstat(int, Dir*); long _dirread(int, Dir**); long _dirreadall(int, Dir**); void _nulldir(Dir*); uint _sizeD2M(Dir*); #ifndef nil #define nil ((void*)0) #endif /sys/src/ape/lib/ap/plan9/dirread.c 664 sys sys 1367613437 1659 #include "lib.h" #include #include #include "sys9.h" #include "dir.h" static int statcheck(uchar *buf, uint nbuf) { uchar *ebuf; int i; ebuf = buf + nbuf; buf += STATFIXLEN - 4 * BIT16SZ; for(i = 0; i < 4; i++){ if(buf + BIT16SZ > ebuf) return -1; buf += BIT16SZ + GBIT16(buf); } if(buf != ebuf) return -1; return 0; } static long dirpackage(uchar *buf, long ts, Dir **d) { char *s; long ss, i, n, nn, m; if(ts == 0){ *d = nil; return 0; } /* * first find number of all stats, check they look like stats, & size all associated strings */ ss = 0; n = 0; for(i = 0; i < ts; i += m){ m = BIT16SZ + GBIT16(&buf[i]); if(statcheck(&buf[i], m) < 0) break; ss += m; n++; } if(i != ts) return -1; *d = malloc(n * sizeof(Dir) + ss); if(*d == nil) return -1; /* * then convert all buffers */ s = (char*)*d + n * sizeof(Dir); nn = 0; for(i = 0; i < ts; i += m){ m = BIT16SZ + GBIT16((uchar*)&buf[i]); if(nn >= n || _convM2D(&buf[i], m, *d + nn, s) != m){ free(*d); return -1; } nn++; s += m; } return nn; } long _dirread(int fd, Dir **d) { uchar *buf; long ts; buf = malloc(DIRMAX); if(buf == nil) return -1; ts = _READ(fd, buf, DIRMAX); if(ts >= 0) ts = dirpackage(buf, ts, d); free(buf); return ts; } long _dirreadall(int fd, Dir **d) { uchar *buf, *nbuf; long n, ts; buf = nil; ts = 0; for(;;){ nbuf = realloc(buf, ts+DIRMAX); if(nbuf == nil){ free(buf); return -1; } buf = nbuf; n = _READ(fd, buf+ts, DIRMAX); if(n <= 0) break; ts += n; } if(ts >= 0) ts = dirpackage(buf, ts, d); free(buf); return ts; } /sys/src/ape/lib/ap/plan9/dirstat.c 664 sys sys 1367613437 1723 #include "lib.h" #include #include #include "sys9.h" #include "dir.h" enum { DIRSIZE = STATFIXLEN + 16 * 4 /* enough for encoded stat buf + some reasonable strings */ }; Dir* _dirstat(char *name) { Dir *d; uchar *buf; int n, nd, i; nd = DIRSIZE; for(i=0; i<2; i++){ /* should work by the second try */ d = malloc(sizeof(Dir) + BIT16SZ +nd); if(d == nil) return nil; buf = (uchar*)&d[1]; n = _STAT(name, buf, BIT16SZ+nd); if(n < BIT16SZ){ free(d); return nil; } nd = GBIT16((uchar*)buf); /* size needed to store whole stat buffer */ if(nd <= n){ _convM2D(buf, n, d, (char*)&d[1]); return d; } /* else sizeof(Dir)+BIT16SZ+nd is plenty */ free(d); } return nil; } int _dirwstat(char *name, Dir *d) { uchar *buf; int r; r = _sizeD2M(d); buf = malloc(r); if(buf == nil) return -1; _convD2M(d, buf, r); r = _WSTAT(name, buf, r); free(buf); return r; } Dir* _dirfstat(int fd) { Dir *d; uchar *buf; int n, nd, i; nd = DIRSIZE; for(i=0; i<2; i++){ /* should work by the second try */ d = malloc(sizeof(Dir) + nd); if(d == nil) return nil; buf = (uchar*)&d[1]; n = _FSTAT(fd, buf, nd); if(n < BIT16SZ){ free(d); return nil; } nd = GBIT16(buf); /* size needed to store whole stat buffer */ if(nd <= n){ _convM2D(buf, n, d, (char*)&d[1]); return d; } /* else sizeof(Dir)+nd is plenty */ free(d); } return nil; } int _dirfwstat(int fd, Dir *d) { uchar *buf; int r; r = _sizeD2M(d); buf = malloc(r); if(buf == nil) return -1; _convD2M(d, buf, r); r = _FWSTAT(fd, buf, r); free(buf); return r; } void _nulldir(Dir *d) { memset(d, ~0, sizeof(Dir)); d->name = d->uid = d->gid = d->muid = ""; } /sys/src/ape/lib/ap/plan9/dirtostat.c 664 sys sys 1367613437 1081 #include "lib.h" #include #include #include "sys9.h" #include "dir.h" /* fi is non-null if there is an fd associated with s */ void _dirtostat(struct stat *s, Dir *d, Fdinfo *fi) { int num; char *nam; s->st_dev = (d->type<<8)|(d->dev&0xFF); s->st_ino = d->qid.path; s->st_mode = d->mode&0777; if(fi && (fi->flags&FD_ISTTY)) s->st_mode |= S_IFCHR; else if(d->mode & 0x80000000) s->st_mode |= S_IFDIR; else if(d->type == '|' || d->type == 's') s->st_mode |= S_IFIFO; else if(d->type != 'M') s->st_mode |= S_IFCHR; else s->st_mode |= S_IFREG; s->st_nlink = 1; s->st_uid = 1; s->st_gid = 1; if(fi && (fi->flags&FD_BUFFERED)) s->st_size = fi->buf->n; else s->st_size = d->length; s->st_atime = d->atime; s->st_mtime = d->mtime; s->st_ctime = d->mtime; if(fi && fi->uid != -2){ s->st_uid = fi->uid; s->st_gid = fi->gid; } else { nam = d->uid; if(_getpw(&num, &nam, 0)) s->st_uid = num; nam = d->gid; if(_getpw(&num, &nam, 0)) s->st_gid = num; if(fi){ fi->uid = s->st_uid; fi->gid = s->st_gid; } } } /sys/src/ape/lib/ap/plan9/dup.c 664 sys sys 1369166818 330 #include "lib.h" #include #include int dup(int oldd) { return fcntl(oldd, F_DUPFD, 0); } int dup2(int oldd, int newd) { if(newd < 0 || newd >= OPEN_MAX){ errno = EBADF; return -1; } if(oldd == newd && _fdinfo[newd].flags&FD_ISOPEN) return newd; close(newd); return fcntl(oldd, F_DUPFD, newd); } /sys/src/ape/lib/ap/plan9/execl.c 664 sys sys 1367613437 138 #include extern char **environ; int execl(const char *name, const char *arg0, ...) { return execve(name, &arg0, environ); } /sys/src/ape/lib/ap/plan9/execle.c 664 sys sys 1369166818 183 #include int execle(const char *name, const char *arg0, const char*, ...) { char *p; for(p=(char *)(&name)+1; *p; ) p++; return execve(name, &arg0, (char **)p+1); } /sys/src/ape/lib/ap/plan9/execlp.c 664 sys sys 1367613437 419 #include #include #include /* * BUG: instead of looking at PATH env variable, * just try prepending /bin/ if name fails... */ extern char **environ; int execlp(const char *name, const char *arg0, ...) { int n; char buf[PATH_MAX]; if((n=execve(name, &arg0, environ)) < 0){ strcpy(buf, "/bin/"); strcpy(buf+5, name); n = execve(buf, &name+1, environ); } return n; } /sys/src/ape/lib/ap/plan9/execv.c 664 sys sys 1367613437 134 #include extern char **environ; int execv(const char *name, const char *argv[]) { return execve(name, argv, environ); } /sys/src/ape/lib/ap/plan9/execve.c 664 sys sys 1369166818 2132 #include "lib.h" #include #include #include #include #include "sys9.h" extern char **environ; int execve(const char *name, const char *argv[], const char *envp[]) { int n, f, i; char **e, *ss, *se; Fdinfo *fi; unsigned long flags; char nam[256+5]; char buf[1000]; _RFORK(RFCENVG); /* * To pass _fdinfo[] across exec, put lines like * fd flags oflags * in $_fdinfo (for open fd's) */ f = _CREATE("#e/_fdinfo", OWRITE, 0666); ss = buf; for(n = 0; nflags; if(flags&FD_CLOEXEC){ _CLOSE(n); fi->flags = 0; fi->oflags = 0; }else if(flags&FD_ISOPEN){ ss = _ultoa(ss, n); *ss++ = ' '; ss = _ultoa(ss, flags); *ss++ = ' '; ss = _ultoa(ss, fi->oflags); *ss++ = '\n'; if(ss-buf < sizeof(buf)-50){ _WRITE(f, buf, ss-buf); ss = buf; } } } if(ss > buf) _WRITE(f, buf, ss-buf); _CLOSE(f); /* * To pass _sighdlr[] across exec, set $_sighdlr * to list of blank separated fd's that have * SIG_IGN (the rest will be SIG_DFL). * We write the variable, even if no signals * are ignored, in case the current value of the * variable ignored some. */ f = _CREATE("/env/_sighdlr", OWRITE, 0666); if(f >= 0){ ss = buf; for(i = 0; i <=MAXSIG && ss < &buf[sizeof(buf)]-5; i++) { if(_sighdlr[i] == SIG_IGN) { ss = _ultoa(ss, i); *ss++ = ' '; } } _WRITE(f, buf, ss-buf); _CLOSE(f); } if(envp){ strcpy(nam, "/env/"); for(e = (char**)envp; (ss = *e); e++) { se = strchr(ss, '='); if(!se || ss==se) continue; /* what is name? value? */ n = se-ss; if(n >= sizeof(nam)-5) n = sizeof(nam)-(5 + 1); memcpy(nam+5, ss, n); nam[5+n] = 0; f = _CREATE(nam, OWRITE, 0666); if(f < 0) continue; se++; /* past = */ n = strlen(se); /* temporarily decode nulls (see _envsetup()) */ for(i=0; i < n; i++) if(se[i] == 1) se[i] = 0; _WRITE(f, se, n); /* put nulls back */ for(i=0; i < n; i++) if(se[i] == 0) se[i] = 1; _CLOSE(f); } } n = _EXEC(name, argv); _syserrno(); return n; } /sys/src/ape/lib/ap/plan9/execvp.c 664 sys sys 1367613437 411 #include #include #include extern char **environ; /* * BUG: instead of looking at PATH env variable, * just try prepending /bin/ if name fails... */ int execvp(const char *name, const char **argv) { int n; char buf[PATH_MAX]; if((n=execve(name, argv, environ)) < 0){ strcpy(buf, "/bin/"); strcpy(buf+5, name); n = execve(buf, argv, environ); } return n; } /sys/src/ape/lib/ap/plan9/fcall.h 664 sys sys 1367613437 2266 typedef struct Fcall Fcall; /* see /sys/include/auth.h */ enum { DOMLEN= 48, /* length of an authentication domain name */ DESKEYLEN= 7, /* length of a des key for encrypt/decrypt */ CHALLEN= 8, /* length of a challenge */ NETCHLEN= 16, /* max network challenge length */ CONFIGLEN= 14, KEYDBLEN= NAMELEN+DESKEYLEN+4+2 }; #define TICKETLEN (CHALLEN+2*NAMELEN+DESKEYLEN+1) #define AUTHENTLEN (CHALLEN+4+1) struct Fcall { char type; short fid; unsigned short tag; union { struct { unsigned short oldtag; /* T-Flush */ Qid qid; /* R-Attach, R-Walk, R-Open, R-Create */ char rauth[AUTHENTLEN]; /* Rattach */ }; struct { char uname[NAMELEN]; /* T-Attach */ char aname[NAMELEN]; /* T-Attach */ char ticket[TICKETLEN]; /* T-Attach */ char auth[AUTHENTLEN];/* T-Attach */ }; struct { char ename[ERRLEN]; /* R-Error */ char authid[NAMELEN]; /* R-session */ char authdom[DOMLEN]; /* R-session */ char chal[CHALLEN]; /* T-session/R-session */ }; struct { long perm; /* T-Create */ short newfid; /* T-Clone, T-Clwalk */ char name[NAMELEN]; /* T-Walk, T-Clwalk, T-Create */ char mode; /* T-Create, T-Open */ }; struct { long offset; /* T-Read, T-Write */ long count; /* T-Read, T-Write, R-Read */ char *data; /* T-Write, R-Read */ }; struct { char stat[DIRLEN]; /* T-Wstat, R-Stat */ }; }; }; #define MAXFDATA 8192 #define MAXMSG 160 /* max header sans data */ #define NOTAG 0xFFFF /* Dummy tag */ enum { Tmux = 48, Rmux, /* illegal */ Tnop = 50, Rnop, Tosession = 52, /* illegal */ Rosession, /* illegal */ Terror = 54, /* illegal */ Rerror, Tflush = 56, Rflush, Toattach = 58, /* illegal */ Roattach, /* illegal */ Tclone = 60, Rclone, Twalk = 62, Rwalk, Topen = 64, Ropen, Tcreate = 66, Rcreate, Tread = 68, Rread, Twrite = 70, Rwrite, Tclunk = 72, Rclunk, Tremove = 74, Rremove, Tstat = 76, Rstat, Twstat = 78, Rwstat, Tclwalk = 80, Rclwalk, Tauth = 82, /* illegal */ Rauth, /* illegal */ Tsession = 84, Rsession, Tattach = 86, Rattach, }; int convM2S(char*, Fcall*, int); int convS2M(Fcall*, char*); int convM2D(char*, Dir*); int convD2M(Dir*, char*); char* getS(int, char*, Fcall*, long*); /sys/src/ape/lib/ap/plan9/fcntl.c 664 sys sys 1367613437 1495 #include "lib.h" #include #include #include #include "sys9.h" /* * BUG: advisory locking not implemented */ #define OFL (O_ACCMODE|O_NONBLOCK|O_APPEND) int fcntl(int fd, int cmd, ...) { int arg, i, ans, err; Fdinfo *fi, *fans; va_list va; unsigned long oflags; err = 0; ans = 0; va_start(va, cmd); arg = va_arg(va, int); va_end(va); fi = &_fdinfo[fd]; if(fd<0 || fd>=OPEN_MAX || !(fi->flags&FD_ISOPEN)) err = EBADF; else switch(cmd){ case F_DUPFD: if(fi->flags&(FD_BUFFERED|FD_BUFFEREDX)){ err = EGREG; /* dup of buffered fd not implemented */ break; } oflags = fi->oflags; for(i = (arg>0)? arg : 0; iflags = fi->flags&~FD_CLOEXEC; fans->oflags = oflags; fans->uid = fi->uid; fans->gid = fi->gid; } } break; case F_GETFD: ans = fi->flags&FD_CLOEXEC; break; case F_SETFD: fi->flags = (fi->flags&~FD_CLOEXEC)|(arg&FD_CLOEXEC); break; case F_GETFL: ans = fi->oflags&OFL; break; case F_SETFL: fi->oflags = (fi->oflags&~OFL)|(arg&OFL); break; case F_GETLK: case F_SETLK: case F_SETLKW: err = EINVAL; break; } if(err){ errno = err; ans = -1; } return ans; } /sys/src/ape/lib/ap/plan9/fork.c 664 sys sys 1367613437 228 #include "lib.h" #include #include #include "sys9.h" pid_t fork(void) { int n; n = _RFORK(RFENVG|RFFDG|RFPROC); if(n < 0) _syserrno(); if(n == 0) { _detachbuf(); _sessleader = 0; } return n; } /sys/src/ape/lib/ap/plan9/frexp.c 664 sys sys 1367613437 1196 #include #include #define _RESEARCH_SOURCE #include #define MASK 0x7ffL #define SHIFT 20 #define BIAS 1022L #define SIG 52 double frexp(double d, int *ep) { FPdbleword x, a; *ep = 0; if(isNaN(d) || isInf(d, 0) || d == 0) return d; x.x = d; a.x = fabs(d); if((a.hi >> SHIFT) == 0){ /* normalize subnormal numbers */ x.x = (double)(1ULL<> SHIFT) & MASK) - BIAS; x.hi &= ~(MASK << SHIFT); x.hi |= BIAS << SHIFT; return x.x; } double ldexp(double d, int e) { FPdbleword x; if(d == 0) return 0; x.x = d; e += (x.hi >> SHIFT) & MASK; if(e <= 0) return 0; if(e >= MASK){ errno = ERANGE; if(d < 0) return -HUGE_VAL; return HUGE_VAL; } x.hi &= ~(MASK << SHIFT); x.hi |= (long)e << SHIFT; return x.x; } double modf(double d, double *ip) { double f; FPdbleword x; int e; if(d < 1) { if(d < 0) { f = modf(-d, ip); *ip = -*ip; return -f; } *ip = 0; return d; } x.x = d; e = ((x.hi >> SHIFT) & MASK) - BIAS; if(e <= SHIFT+1) { x.hi &= ~(0x1fffffL >> e); x.lo = 0; } else if(e <= SHIFT+33) x.lo &= ~(0x7fffffffL >> (e-SHIFT-2)); *ip = x.x; return d - x.x; } /sys/src/ape/lib/ap/plan9/fstat.c 664 sys sys 1367613437 284 #include "lib.h" #include #include #include #include "sys9.h" #include "dir.h" int fstat(int fd, struct stat *buf) { Dir *d; if((d = _dirfstat(fd)) == nil){ _syserrno(); return -1; } _dirtostat(buf, d, &_fdinfo[fd]); free(d); return 0; } /sys/src/ape/lib/ap/plan9/fsync.c 664 sys sys 1369166818 125 #include #include #include int fsync(int fd) { USED(fd); errno = EINVAL; return -1; } /sys/src/ape/lib/ap/plan9/ftruncate.c 664 sys sys 1367613437 303 #include "lib.h" #include #include #include #include "dir.h" int ftruncate(int fd, off_t length) { Dir d; if(length < 0){ errno = EINVAL; return -1; } _nulldir(&d); d.length = length; if(_dirfwstat(fd, &d) < 0){ _syserrno(); return -1; } return 0; } /sys/src/ape/lib/ap/plan9/getcwd.c 664 sys sys 1367613437 468 #include "lib.h" #include #include #include #include #include #include #include "sys9.h" #include "dir.h" char* getcwd(char *buf, size_t len) { int fd; fd = _OPEN(".", OREAD); if(fd < 0) { errno = EACCES; return 0; } if(_FD2PATH(fd, buf, len) < 0) { errno = EIO; _CLOSE(fd); return 0; } _CLOSE(fd); /* RSC: is this necessary? */ if(buf[0] == '\0') strcpy(buf, "/"); return buf; } /sys/src/ape/lib/ap/plan9/getgid.c 664 sys sys 1367613437 306 #include #include #include /* * BUG: assumes group that is same as user name * is the one wanted (plan 9 has no "current group") */ gid_t getgid(void) { struct group *g; g = getgrnam(getlogin()); return g? g->gr_gid : 1; } gid_t getegid(void) { return getgid(); } /sys/src/ape/lib/ap/plan9/getgrgid.c 664 sys sys 1367613437 405 #include #include extern int _getpw(int *, char **, char **); extern char **_grpmems(char *); static struct group holdgroup; struct group * getgrgid(gid_t gid) { int num; char *nam, *mem; num = gid; nam = 0; mem = 0; if(_getpw(&num, &nam, &mem)){ holdgroup.gr_name = nam; holdgroup.gr_gid = num; holdgroup.gr_mem = _grpmems(mem); return &holdgroup; } return NULL; } /sys/src/ape/lib/ap/plan9/getgrnam.c 664 sys sys 1369166818 420 #include #include extern int _getpw(int *, char **, char **); extern char **_grpmems(char *); static struct group holdgroup; struct group * getgrnam(const char *name) { int num; char *nam, *mem; num = 0; nam = (char*)name; mem = 0; if(_getpw(&num, &nam, &mem)){ holdgroup.gr_name = nam; holdgroup.gr_gid = num; holdgroup.gr_mem = _grpmems(mem); return &holdgroup; } return NULL; } /sys/src/ape/lib/ap/plan9/getgroups.c 664 sys sys 1369166818 169 #include #include #include int getgroups(int gidsize, gid_t grouplist[]) { USED(gidsize, grouplist); errno = EINVAL; return -1; } /sys/src/ape/lib/ap/plan9/getlogin.c 664 sys sys 1367613437 392 #include #include #include #include #include char * getlogin_r(char *buf, int len) { int f, n; f = open("/dev/user", O_RDONLY); if(f < 0) return 0; n = read(f, buf, len); buf[len-1] = 0; close(f); return (n>=0)? buf : 0; } char * getlogin(void) { static char buf[NAME_MAX+1]; return getlogin_r(buf, sizeof buf); } /sys/src/ape/lib/ap/plan9/getpgrp.c 664 sys sys 1367613437 442 #include #include #include #include #include #include #include #include "sys9.h" #include "lib.h" pid_t getpgrp(void) { int n, f, pid; char pgrpbuf[15], fname[30]; pid = getpid(); sprintf(fname, "/proc/%d/noteid", pid); f = open(fname, 0); n = read(f, pgrpbuf, sizeof pgrpbuf); if(n < 0) _syserrno(); else n = atoi(pgrpbuf); close(f); return n; } /sys/src/ape/lib/ap/plan9/getpid.c 664 sys sys 1367613437 308 #include "lib.h" #include #include #include #include #include "sys9.h" pid_t getpid(void) { int n, f; char pidbuf[15]; f = _OPEN("#c/pid", 0); n = _READ(f, pidbuf, sizeof pidbuf); if(n < 0) _syserrno(); else n = atoi(pidbuf); _CLOSE(f); return n; } /sys/src/ape/lib/ap/plan9/getppid.c 664 sys sys 1367613437 339 #include #include #include #include #include #include #include "sys9.h" pid_t getppid(void) { int n, f; char ppidbuf[15]; f = open("#c/ppid", 0); n = read(f, ppidbuf, sizeof ppidbuf); if(n < 0) errno = EINVAL; else n = atoi(ppidbuf); close(f); return n; } /sys/src/ape/lib/ap/plan9/getpwnam.c 664 sys sys 1369166818 514 #include "lib.h" #include #include #include static struct passwd holdpw; static char dirbuf[40] = "/usr/"; static char *rc = "/bin/rc"; struct passwd * getpwnam(const char *name) { int num; char *nam, *mem; num = 0; nam = (char*)name; mem = 0; if(_getpw(&num, &nam, &mem)){ holdpw.pw_name = nam; holdpw.pw_uid = num; holdpw.pw_gid = num; strncpy(dirbuf+5, nam, sizeof(dirbuf)-6); holdpw.pw_dir = dirbuf; holdpw.pw_shell = rc; return &holdpw; } return NULL; } /sys/src/ape/lib/ap/plan9/getpwuid.c 664 sys sys 1367613437 527 #include #include #include extern int _getpw(int *, char **, char **); static struct passwd holdpw; static char dirbuf[40] = "/usr/"; static char *rc = "/bin/rc"; struct passwd * getpwuid(uid_t uid) { int num; char *nam, *mem; num = uid; nam = 0; mem = 0; if(_getpw(&num, &nam, &mem)){ holdpw.pw_name = nam; holdpw.pw_uid = num; holdpw.pw_gid = num; strncpy(dirbuf+5, nam, sizeof(dirbuf)-6); holdpw.pw_dir = dirbuf; holdpw.pw_shell = rc; return &holdpw; } return NULL; } /sys/src/ape/lib/ap/plan9/getuid.c 664 sys sys 1367613437 199 #include #include #include uid_t getuid(void) { struct passwd *p; p = getpwnam(getlogin()); return p? p->pw_uid : 1; } uid_t geteuid(void) { return getuid(); } /sys/src/ape/lib/ap/plan9/isatty.c 664 sys sys 1369187647 467 #include "lib.h" #include #include #include #include "sys9.h" #include "dir.h" int _isatty(int fd) { char buf[64]; if(_FD2PATH(fd, buf, sizeof buf) < 0) return 0; /* might be /mnt/term/dev/cons */ return strlen(buf) >= 9 && strcmp(buf+strlen(buf)-9, "/dev/cons") == 0; } /* The FD_ISTTY flag is set via _isatty in _fdsetup or open */ int isatty(int fd) { if(_fdinfo[fd].flags&FD_ISTTY) return 1; else return 0; } /sys/src/ape/lib/ap/plan9/kill.c 664 sys sys 1367613437 912 #include "lib.h" #include #include #include #include #include #include static int note(int pid, char *msg, char *fmt) { int f; char pname[50]; sprintf(pname, fmt, pid); f = open(pname, O_WRONLY); if(f < 0){ errno = ESRCH; return -1; } if(msg != 0 && write(f, msg, strlen(msg)) < 0){ close(f); errno = EPERM; return -1; } close(f); return 0; } int kill(pid_t pid, int sig) { char *msg; int sid, r, mpid; if(sig == 0) msg = 0; else { msg = _sigstring(sig); if(msg == 0) { errno = EINVAL; return -1; } } if(pid < 0) { sid = getpgrp(); mpid = getpid(); if(setpgid(mpid, -pid) == 0) { r = note(mpid, msg, "/proc/%d/notepg"); setpgid(mpid, sid); } else { r = -1; } } else if(pid == 0) r = note(getpid(), msg, "/proc/%d/notepg"); else r = note(pid, msg, "/proc/%d/note"); return r; } /sys/src/ape/lib/ap/plan9/lib.h 664 sys sys 1369166818 2072 #include #include #include #include typedef struct Ureg Ureg; /* mux buf for selecting (see _buf.c) */ enum { READMAX = 8192, /* read at most this much with _READ */ PERFDMAX = 2*READMAX, /* stop _READing an fd when it has this much */ INITBUFS = 90, /* allow enough room for this many PERFDMAX */ }; typedef struct Muxbuf { int n; /* # unprocessed chars in buf */ unsigned char* putnext; /* place for copy process to put next data */ unsigned char* getnext; /* place for parent process to get next data */ char fd; /* fd for which this is a buffer */ unsigned char eof; /* true if eof after current data exhausted */ unsigned char roomwait; /* true if copy process is waiting for room */ unsigned char datawait; /* true if parent process is waiting for data */ int copypid; /* pid of copyproc */ unsigned char data[PERFDMAX]; } Muxbuf; /* be sure to change _fdinfo[] init in _fdinfo if you change this */ typedef struct Fdinfo{ unsigned long flags; unsigned long oflags; uid_t uid; gid_t gid; char *name; /* * the following is used if flags&FD_BUFFERED */ Muxbuf *buf; /* holds buffered data and state */ } Fdinfo; /* #define FD_CLOEXEC 1 is in fcntl.h */ #define FD_ISOPEN 0x2 #define FD_BUFFERED 0x4 #define FD_BUFFEREDX 0x8 #define FD_ISTTY 0x20 #define MAXSIG SIGUSR2 extern Fdinfo _fdinfo[]; extern int _finishing; extern int _sessleader; extern void (*_sighdlr[])(int, char*, Ureg*); extern char *_sigstring(int); extern int _stringsig(char *); extern long _psigblocked; extern int _startbuf(int); extern int _selbuf(int); extern void _closebuf(int); extern int _readbuf(int, void*, int, int); extern void _detachbuf(void); extern void _finish(int, char *); extern char *_ultoa(char *, unsigned long); extern int _notehandler(void *, char *); extern void _notetramp(int, void (*)(int, char*, Ureg*), Ureg*); extern void _syserrno(void); extern int _getpw(int *, char **, char **); extern int _isatty(int); extern void _fdinit(char*, char*); void checkbug(char *, int); /sys/src/ape/lib/ap/plan9/link.c 664 sys sys 1369166818 158 #include #include /* * BUG: LINK_MAX==1 isn't really allowed */ int link(const char *, const char *) { errno = EMLINK; return -1; } /sys/src/ape/lib/ap/plan9/lseek.c 664 sys sys 1367613437 358 #include "lib.h" #include #include #include "sys9.h" /* * BUG: errno mapping */ off_t lseek(int d, off_t offset, int whence) { long long n; int flags; flags = _fdinfo[d].flags; if(flags&(FD_BUFFERED|FD_BUFFEREDX|FD_ISTTY)) { errno = ESPIPE; return -1; } n = _SEEK(d, offset, whence); if(n < 0) _syserrno(); return n; } /sys/src/ape/lib/ap/plan9/malloc.c 664 sys sys 1367613437 2168 #include #include #include #include typedef unsigned int uint; enum { MAGIC = 0xbada110c, MAX2SIZE = 32, CUTOFF = 12, }; typedef struct Bucket Bucket; struct Bucket { int size; int magic; Bucket *next; int pad; char data[1]; }; typedef struct Arena Arena; struct Arena { Bucket *btab[MAX2SIZE]; Lock; }; static Arena arena; #define datoff ((intptr_t)((Bucket*)0)->data) #define nil ((void*)0) extern void *sbrk(unsigned long); void* malloc(size_t size) { uint next; int pow, n; Bucket *bp, *nbp; for(pow = 1; pow < MAX2SIZE; pow++) { if(size <= (1<next; unlock(&arena); if(bp->magic != 0) abort(); bp->magic = MAGIC; return bp->data; } size = sizeof(Bucket)+(1<next = (Bucket*)next; nbp->size = pow; nbp = nbp->next; } nbp->size = pow; } else { bp = sbrk(size); if((intptr_t)bp == -1){ unlock(&arena); return nil; } } unlock(&arena); bp->size = pow; bp->magic = MAGIC; return bp->data; } void free(void *ptr) { Bucket *bp, **l; if(ptr == nil) return; /* Find the start of the structure */ bp = (Bucket*)((intptr_t)ptr - datoff); if(bp->magic != MAGIC) abort(); bp->magic = 0; l = &arena.btab[bp->size]; lock(&arena); bp->next = *l; *l = bp; unlock(&arena); } void* realloc(void *ptr, size_t n) { void *new; uint osize; Bucket *bp; if(ptr == nil) return malloc(n); /* Find the start of the structure */ bp = (Bucket*)((intptr_t)ptr - datoff); if(bp->magic != MAGIC) abort(); /* enough space in this bucket */ osize = 1<size; if(osize >= n) return ptr; new = malloc(n); if(new == nil) return nil; memmove(new, ptr, osize); free(ptr); return new; } /sys/src/ape/lib/ap/plan9/mkdir.c 664 sys sys 1367613437 353 #include "lib.h" #include #include #include "sys9.h" /* * BUG: errno mapping */ int mkdir(const char *name, mode_t mode) { int n; struct stat st; if(stat(name, &st)==0) { errno = EEXIST; return -1; } n = _CREATE(name, 0, 0x80000000|(mode&0777)); if(n < 0) _syserrno(); else{ _CLOSE(n); n = 0; } return n; } /sys/src/ape/lib/ap/plan9/mkfile 664 sys sys 1369185886 1407 APE=/sys/src/ape <$APE/config LIB=/$objtype/lib/ape/libap.a OFILES=\ _buf.$O\ _dirconv.$O\ _envsetup.$O\ _errno.$O\ _exit.$O\ _fdinfo.$O\ _getpw.$O\ _nap.$O\ 9mallocz.$O\ 9iounit.$O\ 9read.$O\ 9readn.$O\ 9wait.$O\ 9write.$O\ access.$O\ alarm.$O\ brk.$O\ cfgetospeed.$O\ chdir.$O\ chmod.$O\ chown.$O\ close.$O\ convM2D.$O\ convD2M.$O\ copysign.$O\ creat.$O\ ctermid.$O\ ctime.$O\ cuserid.$O\ dirread.$O\ dirstat.$O\ dirtostat.$O\ dup.$O\ execl.$O\ execle.$O\ execlp.$O\ execv.$O\ execve.$O\ execvp.$O\ fcntl.$O\ fork.$O\ frexp.$O\ fstat.$O\ fsync.$O\ ftruncate.$O\ getcwd.$O\ getgid.$O\ getgrgid.$O\ getgrnam.$O\ getgroups.$O\ getlogin.$O\ getpgrp.$O\ getpid.$O\ getppid.$O\ getpwnam.$O\ getpwuid.$O\ getuid.$O\ isatty.$O\ kill.$O\ link.$O\ lseek.$O\ malloc.$O\ mkdir.$O\ nan.$O\ open.$O\ opendir.$O\ pause.$O\ pipe.$O\ profile.$O\ qlock.$O\ read.$O\ rename.$O\ rmdir.$O\ setgid.$O\ setpgid.$O\ setsid.$O\ setuid.$O\ signal.$O\ sigpending.$O\ sigprocmask.$O\ sigsuspend.$O\ sleep.$O\ sqrt.$O\ stat.$O\ system.$O\ tcgetattr.$O\ time.$O\ times.$O\ tmpfile.$O\ ttyname.$O\ umask.$O\ uname.$O\ unlink.$O\ utime.$O\ wait.$O\ write.$O\ UPDATE=\ mkfile\ /386/lib/ape/libap.a\ ${OFILES:%.$O=%.c}\ #include #define NANEXP (2047<<20) #define NANMASK (2047<<20) #define NANSIGN (1<<31) double NaN(void) { FPdbleword a; a.hi = NANEXP; a.lo = 1; return a.x; } int isNaN(double d) { FPdbleword a; a.x = d; if((a.hi & NANMASK) != NANEXP) return 0; return !isInf(d, 0); } double Inf(int sign) { FPdbleword a; a.hi = NANEXP; a.lo = 0; if(sign < 0) a.hi |= NANSIGN; return a.x; } int isInf(double d, int sign) { FPdbleword a; a.x = d; if(a.lo != 0) return 0; if(a.hi == NANEXP) return sign >= 0; if(a.hi == (NANEXP|NANSIGN)) return sign <= 0; return 0; } /sys/src/ape/lib/ap/plan9/open.c 664 sys sys 1369166818 1069 #include #include #include #include #include #include "lib.h" #include #include "sys9.h" /* * O_NOCTTY has no effect */ int open(const char *path, int flags, ...) { int n; long f; int mode; Fdinfo *fi; va_list va; f = flags&O_ACCMODE; if(flags&O_CREAT){ if(access(path, 0) >= 0){ if(flags&O_EXCL){ errno = EEXIST; return -1; }else{ if((flags&O_TRUNC)&&(flags&(O_RDWR|O_WRONLY))) f |= 16; n = _OPEN(path, f); } }else{ va_start(va, flags); mode = va_arg(va, int); va_end(va); n = _CREATE(path, f, mode&0777); } if(n < 0) _syserrno(); }else{ if((flags&O_TRUNC)&&(flags&(O_RDWR|O_WRONLY))) f |= 16; n = _OPEN(path, f); if(n < 0) _syserrno(); } if(n >= 0){ fi = &_fdinfo[n]; fi->flags = FD_ISOPEN; fi->oflags = flags&(O_ACCMODE|O_NONBLOCK|O_APPEND); fi->uid = -2; fi->gid = -2; fi->name = malloc(strlen(path)+1); if(fi->name) strcpy(fi->name, path); if(fi->oflags&O_APPEND) _SEEK(n, 0, 2); } return n; } /sys/src/ape/lib/ap/plan9/opendir.c 664 sys sys 1369166818 1968 #include "lib.h" #include #include #include #include #include #include #include "sys9.h" #include "dir.h" #define DBLOCKSIZE 20 DIR * opendir(const char *filename) { int f; DIR *d; struct stat sb; Dir *d9; if((d9 = _dirstat(filename)) == nil){ _syserrno(); return NULL; } _dirtostat(&sb, d9, 0); free(d9); if(S_ISDIR(sb.st_mode) == 0) { errno = ENOTDIR; return NULL; } f = open(filename, O_RDONLY); if(f < 0){ _syserrno(); return NULL; } _fdinfo[f].flags |= FD_CLOEXEC; d = (DIR *)malloc(sizeof(DIR) + DBLOCKSIZE*sizeof(struct dirent)); if(!d){ errno = ENOMEM; return NULL; } d->dd_buf = (char *)d + sizeof(DIR); d->dd_fd = f; d->dd_loc = 0; d->dd_size = 0; d->dirs = nil; d->dirsize = 0; d->dirloc = 0; return d; } int closedir(DIR *d) { if(!d){ errno = EBADF; return -1; } if(close(d->dd_fd) < 0) return -1; free(d->dirs); free(d); return 0; } void rewinddir(DIR *d) { if(!d) return; d->dd_loc = 0; d->dd_size = 0; d->dirsize = 0; d->dirloc = 0; free(d->dirs); d->dirs = nil; if(_SEEK(d->dd_fd, 0, 0) < 0){ _syserrno(); return; } } struct dirent * readdir(DIR *d) { int i; struct dirent *dr; Dir *dirs; if(!d){ errno = EBADF; return NULL; } if(d->dd_loc >= d->dd_size){ if(d->dirloc >= d->dirsize){ free(d->dirs); d->dirs = NULL; d->dirsize = _dirread(d->dd_fd, &d->dirs); d->dirloc = 0; } if(d->dirsize < 0) { /* malloc or read failed in _dirread? */ free(d->dirs); d->dirs = NULL; } if(d->dirs == NULL) return NULL; dr = (struct dirent *)d->dd_buf; dirs = d->dirs; for(i=0; idirloc < d->dirsize; i++){ strncpy(dr[i].d_name, dirs[d->dirloc++].name, MAXNAMLEN); dr[i].d_name[MAXNAMLEN] = 0; } d->dd_loc = 0; d->dd_size = i*sizeof(struct dirent); } dr = (struct dirent*)(d->dd_buf+d->dd_loc); d->dd_loc += sizeof(struct dirent); return dr; } /sys/src/ape/lib/ap/plan9/pause.c 664 sys sys 1369166818 169 #include "lib.h" #include #include #include "sys9.h" int pause(void) { for(;;) if(_SLEEP(1000*1000) < 0){ errno = EINTR; return -1; } } /sys/src/ape/lib/ap/plan9/pipe.c 664 sys sys 1367613437 476 #include #include "lib.h" #include "sys9.h" int pipe(int fildes[2]) { Fdinfo *fi; int i; if(!fildes){ errno = EFAULT; return -1; } if(_PIPE(fildes) < 0) _syserrno(); else if(fildes[0] < 0 || fildes[0]>=OPEN_MAX || fildes[1] < 0 || fildes[1]>=OPEN_MAX) { errno = EMFILE; return -1; } for(i = 0; i <=1; i++) { fi = &_fdinfo[fildes[i]]; fi->flags = FD_ISOPEN; fi->oflags = O_RDWR; fi->uid = 0; /* none */ fi->gid = 0; } return 0; } /sys/src/ape/lib/ap/plan9/profile.c 664 sys sys 1367613437 6602 #include #include #include #include #include #include #include #include "sys9.h" enum { Profoff, /* No profiling */ Profuser, /* Measure user time only (default) */ Profkernel, /* Measure user + kernel time */ Proftime, /* Measure total time */ Profsample, /* Use clock interrupt to sample (default when there is no cycle counter) */ }; /* what */ typedef long long vlong; typedef unsigned long ulong; typedef unsigned long long uvlong; typedef unsigned long uint; #include "/sys/include/tos.h" extern void* sbrk(ulong); extern long _callpc(void**); extern long _savearg(void); extern void _cycles(uvlong*); /* 64-bit value of the cycle counter if there is one, 0 if there isn't */ static ulong khz; static ulong perr; static int havecycles; typedef struct Plink Plink; struct Plink { Plink *old; Plink *down; Plink *link; long pc; long count; vlong time; }; #pragma profile off ulong _profin(void) { void *dummy; long pc; Plink *pp, *p; ulong arg; vlong t; arg = _savearg(); pc = _callpc(&dummy); pp = _tos->prof.pp; if(pp == 0 || (_tos->prof.pid && _tos->pid != _tos->prof.pid)) return arg; for(p=pp->down; p; p=p->link) if(p->pc == pc) goto out; p = _tos->prof.next + 1; if(p >= _tos->prof.last){ _tos->prof.pp = 0; perr++; return arg; } _tos->prof.next = p; p->link = pp->down; pp->down = p; p->pc = pc; p->old = pp; p->down = 0; p->count = 0; p->time = 0LL; out: _tos->prof.pp = p; p->count++; switch(_tos->prof.what){ case Profkernel: p->time = p->time - _tos->pcycles; goto proftime; case Profuser: /* Add kernel cycles on proc entry */ p->time = p->time + _tos->kcycles; /* fall through */ case Proftime: proftime: /* Subtract cycle counter on proc entry */ _cycles((uvlong*)&t); p->time = p->time - t; break; case Profsample: p->time = p->time - _tos->clock; break; } return arg; /* disgusting linkage */ } ulong _profout(void) { Plink *p; ulong arg; vlong t; arg = _savearg(); p = _tos->prof.pp; if (p == NULL || (_tos->prof.pid != 0 && _tos->pid != _tos->prof.pid)) return arg; /* Not our process */ switch(_tos->prof.what){ case Profkernel: /* Add proc cycles on proc entry */ p->time = p->time + _tos->pcycles; goto proftime; case Profuser: /* Subtract kernel cycles on proc entry */ p->time = p->time - _tos->kcycles; /* fall through */ case Proftime: proftime: /* Add cycle counter on proc entry */ _cycles((uvlong*)&t); p->time = p->time + t; break; case Profsample: p->time = p->time + _tos->clock; break; } _tos->prof.pp = p->old; return arg; } /* stdio may not be ready for us yet */ static void err(char *fmt, ...) { int fd; va_list arg; char buf[128]; if((fd = open("/dev/cons", OWRITE)) == -1) return; va_start(arg, fmt); /* * C99 now requires *snprintf to return the number of characters * that *would* have been emitted, had there been room for them, * or a negative value on an `encoding error'. Arrgh! */ vsnprintf(buf, sizeof buf, fmt, arg); va_end(arg); write(fd, buf, strlen(buf)); close(fd); } void _profdump(void) { int f; long n; Plink *p; char *vp; char filename[64]; if (_tos->prof.what == 0) return; /* No profiling */ if (_tos->prof.pid != 0 && _tos->pid != _tos->prof.pid) return; /* Not our process */ if(perr) err("%lud Prof errors\n", perr); _tos->prof.pp = NULL; if (_tos->prof.pid) snprintf(filename, sizeof filename - 1, "prof.%ld", _tos->prof.pid); else snprintf(filename, sizeof filename - 1, "prof.out"); f = creat(filename, 0666); if(f < 0) { err("%s: cannot create - %s\n", filename, strerror(errno)); return; } _tos->prof.pid = ~0; /* make sure data gets dumped once */ switch(_tos->prof.what){ case Profkernel: _cycles((uvlong*)&_tos->prof.first->time); _tos->prof.first->time = _tos->prof.first->time + _tos->pcycles; break; case Profuser: _cycles((uvlong*)&_tos->prof.first->time); _tos->prof.first->time = _tos->prof.first->time - _tos->kcycles; break; case Proftime: _cycles((uvlong*)&_tos->prof.first->time); break; case Profsample: _tos->prof.first->time = _tos->clock; break; } vp = (char*)_tos->prof.first; for(p = _tos->prof.first; p <= _tos->prof.next; p++) { /* * short down */ n = 0xffff; if(p->down) n = p->down - _tos->prof.first; vp[0] = n>>8; vp[1] = n; /* * short right */ n = 0xffff; if(p->link) n = p->link - _tos->prof.first; vp[2] = n>>8; vp[3] = n; vp += 4; /* * long pc */ n = p->pc; vp[0] = n>>24; vp[1] = n>>16; vp[2] = n>>8; vp[3] = n; vp += 4; /* * long count */ n = p->count; vp[0] = n>>24; vp[1] = n>>16; vp[2] = n>>8; vp[3] = n; vp += 4; /* * vlong time */ if (havecycles){ n = (vlong)(p->time / (vlong)khz); }else n = p->time; vp[0] = n>>24; vp[1] = n>>16; vp[2] = n>>8; vp[3] = n; vp += 4; } write(f, (char*)_tos->prof.first, vp - (char*)_tos->prof.first); close(f); } void _profinit(int entries, int what) { if (_tos->prof.what == 0) return; /* Profiling not linked in */ _tos->prof.pp = NULL; _tos->prof.first = calloc(entries*sizeof(Plink),1); _tos->prof.last = _tos->prof.first + entries; _tos->prof.next = _tos->prof.first; _tos->prof.pid = _tos->pid; _tos->prof.what = what; _tos->clock = 1; } void _profmain(void) { char ename[50]; int n, f; n = 2000; if (_tos->cyclefreq != 0LL){ khz = _tos->cyclefreq / 1000; /* Report times in milliseconds */ havecycles = 1; } f = open("/env/profsize", OREAD); if(f >= 0) { memset(ename, 0, sizeof(ename)); read(f, ename, sizeof(ename)-1); close(f); n = atol(ename); } _tos->prof.what = Profuser; f = open("/env/proftype", OREAD); if(f >= 0) { memset(ename, 0, sizeof(ename)); read(f, ename, sizeof(ename)-1); close(f); if (strcmp(ename, "user") == 0) _tos->prof.what = Profuser; else if (strcmp(ename, "kernel") == 0) _tos->prof.what = Profkernel; else if (strcmp(ename, "elapsed") == 0 || strcmp(ename, "time") == 0) _tos->prof.what = Proftime; else if (strcmp(ename, "sample") == 0) _tos->prof.what = Profsample; } _tos->prof.first = sbrk(n*sizeof(Plink)); _tos->prof.last = sbrk(0); _tos->prof.next = _tos->prof.first; _tos->prof.pp = NULL; _tos->prof.pid = _tos->pid; atexit(_profdump); _tos->clock = 1; } void prof(void (*fn)(void*), void *arg, int entries, int what) { _profinit(entries, what); _tos->prof.pp = _tos->prof.next; fn(arg); _profdump(); } #pragma profile on /sys/src/ape/lib/ap/plan9/qlock.c 664 sys sys 1368211174 5198 #define _LOCK_EXTENSION #define _QLOCK_EXTENSION #define _RESEARCH_SOURCE #include #include #include #include #include "sys9.h" #define rendezvous _RENDEZVOUS #define _rendezvousp rendezvous #define _tas tas #define nelem(x) (sizeof(x)/sizeof((x)[0])) static struct { QLp *p; QLp x[1024]; } ql = { ql.x }; enum { Queuing, QueuingR, QueuingW, Sleeping, }; /* find a free shared memory location to queue ourselves in */ static QLp* getqlp(void) { QLp *p, *op; op = ql.p; for(p = op+1; ; p++){ if(p == &ql.x[nelem(ql.x)]) p = ql.x; if(p == op) abort(); if(_tas(&(p->inuse)) == 0){ ql.p = p; p->next = nil; break; } } return p; } void qlock(QLock *q) { QLp *p, *mp; lock(&q->lock); if(!q->locked){ q->locked = 1; unlock(&q->lock); return; } /* chain into waiting list */ mp = getqlp(); p = q->tail; if(p == nil) q->head = mp; else p->next = mp; q->tail = mp; mp->state = Queuing; unlock(&q->lock); /* wait */ while((*_rendezvousp)(mp, (void*)1) == (void*)~0) ; mp->inuse = 0; } void qunlock(QLock *q) { QLp *p; lock(&q->lock); if (q->locked == 0) abort(); p = q->head; if(p != nil){ /* wakeup head waiting process */ q->head = p->next; if(q->head == nil) q->tail = nil; unlock(&q->lock); while((*_rendezvousp)(p, (void*)0x12345) == (void*)~0) ; return; } q->locked = 0; unlock(&q->lock); } int canqlock(QLock *q) { if(!canlock(&q->lock)) return 0; if(!q->locked){ q->locked = 1; unlock(&q->lock); return 1; } unlock(&q->lock); return 0; } #if 0 void rlock(RWLock *q) { QLp *p, *mp; lock(&q->lock); if(q->writer == 0 && q->head == nil){ /* no writer, go for it */ q->readers++; unlock(&q->lock); return; } mp = getqlp(); p = q->tail; if(p == 0) q->head = mp; else p->next = mp; q->tail = mp; mp->next = nil; mp->state = QueuingR; unlock(&q->lock); /* wait in kernel */ while((*_rendezvousp)(mp, (void*)1) == (void*)~0) ; mp->inuse = 0; } int canrlock(RWLock *q) { lock(&q->lock); if (q->writer == 0 && q->head == nil) { /* no writer; go for it */ q->readers++; unlock(&q->lock); return 1; } unlock(&q->lock); return 0; } void runlock(RWLock *q) { QLp *p; lock(&q->lock); if(q->readers <= 0) abort(); p = q->head; if(--(q->readers) > 0 || p == nil){ unlock(&q->lock); return; } /* start waiting writer */ if(p->state != QueuingW) abort(); q->head = p->next; if(q->head == 0) q->tail = 0; q->writer = 1; unlock(&q->lock); /* wakeup waiter */ while((*_rendezvousp)(p, 0) == (void*)~0) ; } void wlock(RWLock *q) { QLp *p, *mp; lock(&q->lock); if(q->readers == 0 && q->writer == 0){ /* noone waiting, go for it */ q->writer = 1; unlock(&q->lock); return; } /* wait */ p = q->tail; mp = getqlp(); if(p == nil) q->head = mp; else p->next = mp; q->tail = mp; mp->next = nil; mp->state = QueuingW; unlock(&q->lock); /* wait in kernel */ while((*_rendezvousp)(mp, (void*)1) == (void*)~0) ; mp->inuse = 0; } int canwlock(RWLock *q) { lock(&q->lock); if (q->readers == 0 && q->writer == 0) { /* no one waiting; go for it */ q->writer = 1; unlock(&q->lock); return 1; } unlock(&q->lock); return 0; } void wunlock(RWLock *q) { QLp *p; lock(&q->lock); if(q->writer == 0) abort(); p = q->head; if(p == nil){ q->writer = 0; unlock(&q->lock); return; } if(p->state == QueuingW){ /* start waiting writer */ q->head = p->next; if(q->head == nil) q->tail = nil; unlock(&q->lock); while((*_rendezvousp)(p, 0) == (void*)~0) ; return; } if(p->state != QueuingR) abort(); /* wake waiting readers */ while(q->head != nil && q->head->state == QueuingR){ p = q->head; q->head = p->next; q->readers++; while((*_rendezvousp)(p, 0) == (void*)~0) ; } if(q->head == nil) q->tail = nil; q->writer = 0; unlock(&q->lock); } void rsleep(Rendez *r) { QLp *t, *me; if(!r->l) abort(); lock(&r->l->lock); /* we should hold the qlock */ if(!r->l->locked) abort(); /* add ourselves to the wait list */ me = getqlp(); me->state = Sleeping; if(r->head == nil) r->head = me; else r->tail->next = me; me->next = nil; r->tail = me; /* pass the qlock to the next guy */ t = r->l->head; if(t){ r->l->head = t->next; if(r->l->head == nil) r->l->tail = nil; unlock(&r->l->lock); while((*_rendezvousp)(t, (void*)0x12345) == (void*)~0) ; }else{ r->l->locked = 0; unlock(&r->l->lock); } /* wait for a wakeup */ while((*_rendezvousp)(me, (void*)1) == (void*)~0) ; me->inuse = 0; } int rwakeup(Rendez *r) { QLp *t; /* * take off wait and put on front of queue * put on front so guys that have been waiting will not get starved */ if(!r->l) abort(); lock(&r->l->lock); if(!r->l->locked) abort(); t = r->head; if(t == nil){ unlock(&r->l->lock); return 0; } r->head = t->next; if(r->head == nil) r->tail = nil; t->next = r->l->head; r->l->head = t; if(r->l->tail == nil) r->l->tail = t; t->state = Queuing; unlock(&r->l->lock); return 1; } int rwakeupall(Rendez *r) { int i; for(i=0; rwakeup(r); i++) ; return i; } #endif /sys/src/ape/lib/ap/plan9/read.c 664 sys sys 1369843551 891 #include #include #include #include #include "lib.h" #include "sys9.h" #include ssize_t pread(int fd, void *buf, size_t nbytes, off_t offset) { int n, noblock, isbuf; Fdinfo *f; if(fd<0 || fd>=OPEN_MAX || !((f = &_fdinfo[fd])->flags&FD_ISOPEN)){ errno = EBADF; return -1; } if(nbytes <= 0) return 0; if(buf == 0){ errno = EFAULT; return -1; } noblock = f->oflags&O_NONBLOCK; isbuf = f->flags&(FD_BUFFERED|FD_BUFFEREDX); if(noblock || isbuf){ if(f->flags&FD_BUFFEREDX) { errno = EIO; return -1; } if(!isbuf) { if(_startbuf(fd) != 0) { errno = EIO; return -1; } } n = _readbuf(fd, buf, nbytes, noblock); }else{ n = _PREAD(fd, buf, nbytes, offset); if(n < 0) _syserrno(); } return n; } ssize_t read(int fd, void *buf, size_t nbytes) { return pread(fd, buf, nbytes, -1ll); } /sys/src/ape/lib/ap/plan9/rename.c 664 sys sys 1367613437 1288 #include "lib.h" #include #include #include #include #include #include "sys9.h" #include "dir.h" int rename(const char *from, const char *to) { int n; const char *f, *t; Dir *d, nd; if(access(to, 0) >= 0){ if(_REMOVE(to) < 0){ _syserrno(); return -1; } } if((d = _dirstat(to)) != nil){ free(d); errno = EEXIST; return -1; } if((d = _dirstat(from)) == nil){ _syserrno(); return -1; } f = strrchr(from, '/'); t = strrchr(to, '/'); f = f? f+1 : from; t = t? t+1 : to; n = 0; if(f-from==t-to && strncmp(from, to, f-from)==0){ /* from and to are in same directory (we miss some cases) */ strlen(t); _nulldir(&nd); nd.name = (char*)t; if(_dirwstat(from, &nd) < 0){ _syserrno(); n = -1; } }else{ /* different directories: have to copy */ int ffd, tfd; char buf[8192]; tfd = -1; if((ffd = _OPEN(from, 0)) < 0 || (tfd = _CREATE(to, 1, d->mode)) < 0){ _CLOSE(ffd); _syserrno(); n = -1; } while(n>=0 && (n = _READ(ffd, buf, 8192)) > 0) if(_WRITE(tfd, buf, n) != n){ _syserrno(); n = -1; } _CLOSE(ffd); _CLOSE(tfd); if(n>0) n = 0; if(n == 0) { if(_REMOVE(from) < 0){ _syserrno(); return -1; } } } free(d); return n; } /sys/src/ape/lib/ap/plan9/rmdir.c 664 sys sys 1367613437 188 #include "lib.h" #include #include #include "sys9.h" int rmdir(const char *path) { int n; n = 0; if(_REMOVE(path) < 0) { _syserrno(); n = -1; } return n; } /sys/src/ape/lib/ap/plan9/_buf.c 664 bootes sys 1369166818 9682 #define _BSDTIME_EXTENSION #define _LOCK_EXTENSION #include "lib.h" #include #include #include #include #include #include #include #include #include #include #include #include "sys9.h" typedef struct Muxseg { Lock lock; /* for mutual exclusion access to buffer variables */ int curfds; /* number of fds currently buffered */ int selwait; /* true if selecting process is waiting */ int waittime; /* time for timer process to wait */ fd_set rwant; /* fd's that select wants to read */ fd_set ewant; /* fd's that select wants to know eof info on */ Muxbuf bufs[INITBUFS]; /* can grow, via segbrk() */ } Muxseg; static Muxseg *mux = 0; /* shared memory segment */ /* _muxsid and _killmuxsid are known in libbsd's listen.c */ int _muxsid = -1; /* group id of copy processes */ static int _mainpid = -1; static int timerpid = -1; /* pid of a timer process */ void _killmuxsid(void); static void _copyproc(int, Muxbuf*); static void _timerproc(void); static void _resettimer(void); static int copynotehandler(void *, char *); /* assume FD_SETSIZE is 96 */ #define FD_ANYSET(p) ((p)->fds_bits[0] || (p)->fds_bits[1] || (p)->fds_bits[2]) /* * Start making fd read-buffered: make the shared segment, if necessary, * allocate a slot (index into mux->bufs), and fork a child to read the fd * and write into the slot-indexed buffer. * Return -1 if we can't do it. */ int _startbuf(int fd) { long i, slot; int pid; Fdinfo *f; Muxbuf *b; if(mux == 0){ _RFORK(RFREND); mux = (Muxseg*)_SEGATTACH(0, "shared", 0, sizeof(Muxseg)); if(mux == (void*)-1){ _syserrno(); return -1; } /* segattach has returned zeroed memory */ atexit(_killmuxsid); } if(fd == -1) return 0; lock(&mux->lock); slot = mux->curfds++; if(mux->curfds > INITBUFS) { if(_SEGBRK(mux, mux->bufs+mux->curfds) == (void*)-1){ _syserrno(); unlock(&mux->lock); return -1; } } f = &_fdinfo[fd]; b = &mux->bufs[slot]; b->n = 0; b->putnext = b->data; b->getnext = b->data; b->eof = 0; b->fd = fd; if(_mainpid == -1) _mainpid = getpid(); if((pid = _RFORK(RFFDG|RFPROC|RFNOWAIT)) == 0){ /* copy process ... */ if(_muxsid == -1) { _RFORK(RFNOTEG); _muxsid = getpgrp(); } else setpgid(getpid(), _muxsid); _NOTIFY(copynotehandler); for(i=0; icopypid = pid; f->buf = b; f->flags |= FD_BUFFERED; unlock(&mux->lock); _muxsid = (uintptr_t)_RENDEZVOUS((void*)0x101, 0); /* leave fd open in parent so system doesn't reuse it */ return 0; } /* * The given buffered fd is being closed. * Set the fd field in the shared buffer to -1 to tell copyproc * to exit, and kill the copyproc. */ void _closebuf(int fd) { Muxbuf *b; b = _fdinfo[fd].buf; if(!b) return; lock(&mux->lock); b->fd = -1; unlock(&mux->lock); kill(b->copypid, SIGKILL); } /* child copy procs execute this until eof */ static void _copyproc(int fd, Muxbuf *b) { unsigned char *e; int n; int nzeros; e = &b->data[PERFDMAX]; for(;;) { /* make sure there's room */ lock(&mux->lock); if(e - b->putnext < READMAX) { if(b->getnext == b->putnext) { b->getnext = b->putnext = b->data; unlock(&mux->lock); } else { /* sleep until there's room */ b->roomwait = 1; unlock(&mux->lock); _RENDEZVOUS(&b->roomwait, 0); } } else unlock(&mux->lock); /* * A Zero-length _READ might mean a zero-length write * happened, or it might mean eof; try several times to * disambiguate (posix read() discards 0-length messages) */ nzeros = 0; do { n = _READ(fd, b->putnext, READMAX); if(b->fd == -1) { _exit(0); /* we've been closed */ } } while(n == 0 && ++nzeros < 3); lock(&mux->lock); if(n <= 0) { b->eof = 1; if(mux->selwait && FD_ISSET(fd, &mux->ewant)) { mux->selwait = 0; unlock(&mux->lock); _RENDEZVOUS(&mux->selwait, (void*)fd); } else if(b->datawait) { b->datawait = 0; unlock(&mux->lock); _RENDEZVOUS(&b->datawait, 0); } else if(mux->selwait && FD_ISSET(fd, &mux->rwant)) { mux->selwait = 0; unlock(&mux->lock); _RENDEZVOUS(&mux->selwait, (void*)fd); } else unlock(&mux->lock); _exit(0); } else { b->putnext += n; b->n += n; if(b->n > 0) { /* parent process cannot be both in datawait and selwait */ if(b->datawait) { b->datawait = 0; unlock(&mux->lock); /* wake up _bufreading process */ _RENDEZVOUS(&b->datawait, 0); } else if(mux->selwait && FD_ISSET(fd, &mux->rwant)) { mux->selwait = 0; unlock(&mux->lock); /* wake up selecting process */ _RENDEZVOUS(&mux->selwait, (void*)fd); } else unlock(&mux->lock); } else unlock(&mux->lock); } } } /* like read(), for a buffered fd; extra arg noblock says don't wait for data if true */ int _readbuf(int fd, void *addr, int nwant, int noblock) { Muxbuf *b; int ngot; b = _fdinfo[fd].buf; if(b->eof && b->n == 0) { goteof: return 0; } if(b->n == 0 && noblock) { errno = EAGAIN; return -1; } /* make sure there's data */ lock(&mux->lock); ngot = b->putnext - b->getnext; if(ngot == 0) { /* maybe EOF just happened */ if(b->eof) { unlock(&mux->lock); goto goteof; } /* sleep until there's data */ b->datawait = 1; unlock(&mux->lock); _RENDEZVOUS(&b->datawait, 0); lock(&mux->lock); ngot = b->putnext - b->getnext; } if(ngot == 0) { unlock(&mux->lock); goto goteof; } if(ngot > nwant) ngot = nwant; memcpy(addr, b->getnext, ngot); b->getnext += ngot; b->n -= ngot; if(b->getnext == b->putnext && b->roomwait) { b->getnext = b->putnext = b->data; b->roomwait = 0; unlock(&mux->lock); /* wake up copy process */ _RENDEZVOUS(&b->roomwait, 0); } else unlock(&mux->lock); return ngot; } int select(int nfds, fd_set *rfds, fd_set *wfds, fd_set *efds, struct timeval *timeout) { int n, i, t, slots, fd, err; Fdinfo *f; Muxbuf *b; if(timeout) t = timeout->tv_sec*1000 + (timeout->tv_usec+999)/1000; else t = -1; if(!((rfds && FD_ANYSET(rfds)) || (wfds && FD_ANYSET(wfds)) || (efds && FD_ANYSET(efds)))) { /* no requested fds */ if(t > 0) _SLEEP(t); return 0; } _startbuf(-1); /* make sure all requested rfds and efds are buffered */ if(nfds >= OPEN_MAX) nfds = OPEN_MAX; for(i = 0; i < nfds; i++) if((rfds && FD_ISSET(i, rfds)) || (efds && FD_ISSET(i, efds))){ f = &_fdinfo[i]; if(!(f->flags&FD_BUFFERED)) if(_startbuf(i) != 0) return -1; } /* check wfds; for now, we'll say they are all ready */ n = 0; if(wfds && FD_ANYSET(wfds)){ for(i = 0; ilock); slots = mux->curfds; FD_ZERO(&mux->rwant); FD_ZERO(&mux->ewant); for(i = 0; ibufs[i]; fd = b->fd; if(fd == -1) continue; err = 0; if(efds && FD_ISSET(fd, efds)) { if(b->eof && b->n == 0){ err = 1; n++; }else{ FD_CLR(fd, efds); FD_SET(fd, &mux->ewant); } } if(rfds && FD_ISSET(fd, rfds)) { if(!err && (b->n > 0 || b->eof)) n++; else{ FD_CLR(fd, rfds); FD_SET(fd, &mux->rwant); } } } if(n || !(FD_ANYSET(&mux->rwant) || FD_ANYSET(&mux->ewant)) || t == 0) { FD_ZERO(&mux->rwant); FD_ZERO(&mux->ewant); unlock(&mux->lock); return n; } if(timeout) { mux->waittime = t; if(timerpid == -1) _timerproc(); else _resettimer(); } mux->selwait = 1; unlock(&mux->lock); fd = (uintptr_t)_RENDEZVOUS(&mux->selwait, 0); if(fd >= 0) { b = _fdinfo[fd].buf; if(FD_ISSET(fd, &mux->rwant)) { FD_SET(fd, rfds); n = 1; } else if(FD_ISSET(fd, &mux->ewant) && b->eof && b->n == 0) { FD_SET(fd, efds); n = 1; } } FD_ZERO(&mux->rwant); FD_ZERO(&mux->ewant); return n; } static int timerreset; static int timerpid; static void alarmed(int) { timerreset = 1; } /* a little over an hour */ #define LONGWAIT 4000001 static void _killtimerproc(void) { if(timerpid > 0) kill(timerpid, SIGKILL); } static void _timerproc(void) { int i; if((timerpid = _RFORK(RFFDG|RFPROC|RFNOWAIT)) == 0){ /* timer process */ setpgid(getpid(), _muxsid); signal(SIGALRM, alarmed); for(i=0; iwaittime); if(timerreset) { timerreset = 0; } else { lock(&mux->lock); if(mux->selwait && mux->waittime != LONGWAIT) { mux->selwait = 0; mux->waittime = LONGWAIT; unlock(&mux->lock); _RENDEZVOUS(&mux->selwait, (void*)-2); } else { mux->waittime = LONGWAIT; unlock(&mux->lock); } } } } atexit(_killtimerproc); /* parent process continues */ while(_RENDEZVOUS((void*)0x103, 0) == (void*)~0) ; } static void _resettimer(void) { kill(timerpid, SIGALRM); } void _killmuxsid(void) { if(_muxsid != -1 && (_mainpid == getpid() || _mainpid == -1)) kill(-_muxsid,SIGTERM); } /* call this on fork(), because reading a BUFFERED fd won't work in child */ void _detachbuf(void) { int i; Fdinfo *f; if(mux == 0) return; _SEGDETACH(mux); for(i = 0; i < OPEN_MAX; i++){ f = &_fdinfo[i]; if(f->flags&FD_BUFFERED) f->flags = (f->flags&~FD_BUFFERED) | FD_BUFFEREDX; /* mark 'poisoned' */ } mux = 0; _muxsid = -1; _mainpid = -1; timerpid = -1; } static int copynotehandler(void*, char*) { if(_finishing) _finish(0, 0); _NOTED(1); return 0; } /sys/src/ape/lib/ap/plan9/copysign.c 664 bootes sys 1367613437 234 #include #include #define _RESEARCH_SOURCE #include #define SIGN (1<<31) double copysign(double x, double y) { FPdbleword a, b; a.x = x; b.x = y; a.hi &= ~SIGN; a.hi |= b.hi & SIGN; return a.x; } /sys/src/ape/lib/ap/plan9/setgid.c 664 sys sys 1367613437 157 #include #include #include /* * BUG: never works */ int setgid(gid_t gid) { USED(gid); errno = EPERM; return -1; } /sys/src/ape/lib/ap/plan9/setpgid.c 664 sys sys 1367613437 463 #include "lib.h" #include #include #include #include "sys9.h" int setpgid(pid_t pid, pid_t pgid) { int n, f; char buf[50], fname[30]; if(pid == 0) pid = getpid(); if(pgid == 0) pgid = getpgrp(); sprintf(fname, "/proc/%d/noteid", pid); f = open(fname, 1); if(f < 0) { errno = ESRCH; return -1; } n = sprintf(buf, "%d", pgid); n = write(f, buf, n); if(n < 0) _syserrno(); else n = 0; close(f); return n; } /sys/src/ape/lib/ap/plan9/setsid.c 664 sys sys 1367613437 181 #include "lib.h" #include #include "sys9.h" pid_t setsid(void) { if(_RFORK(RFNAMEG|RFNOTEG) < 0){ _syserrno(); return -1; } _sessleader = 1; return getpgrp(); } /sys/src/ape/lib/ap/plan9/setuid.c 664 sys sys 1367613437 157 #include #include #include /* * BUG: never works */ int setuid(uid_t uid) { USED(uid); errno = EPERM; return -1; } /sys/src/ape/lib/ap/plan9/signal.c 664 sys sys 1369185886 2925 #include "lib.h" #include "sys9.h" #include #include #include #include extern sigset_t _psigblocked; static struct { char *msg; /* just check prefix */ int num; } sigtab[] = { {"hangup", SIGHUP}, {"interrupt", SIGINT}, {"quit", SIGQUIT}, {"alarm", SIGALRM}, {"sys: trap: illegal instruction", SIGILL}, {"sys: trap: reserved instruction", SIGILL}, {"sys: trap: reserved", SIGILL}, {"sys: trap: arithmetic overflow", SIGFPE}, {"abort", SIGABRT}, {"sys: fp:", SIGFPE}, {"exit", SIGKILL}, {"die", SIGKILL}, {"kill", SIGKILL}, {"sys: trap: bus error", SIGSEGV}, {"sys: trap: address error", SIGSEGV}, {"sys: trap: TLB", SIGSEGV}, {"sys: write on closed pipe", SIGPIPE}, {"alarm", SIGALRM}, {"term", SIGTERM}, {"usr1", SIGUSR1}, {"usr2", SIGUSR2}, }; #define NSIGTAB ((sizeof sigtab)/(sizeof (sigtab[0]))) void (*_sighdlr[MAXSIG+1])(int, char*, Ureg*); /* 0 initialized: SIG_DFL */ /* consider moving to */ typedef void (*sighandler_t)(int); typedef void (*isighandler_t)(int, char*, Ureg*); void (*signal(int sig, void (*func)(int)))(int) { sighandler_t oldf; if(sig <= 0 || sig > MAXSIG){ errno = EINVAL; return SIG_ERR; } oldf = (sighandler_t)_sighdlr[sig]; if(sig == SIGKILL) return oldf; /* can't catch or ignore SIGKILL */ _sighdlr[sig] = (isighandler_t)func; return oldf; } /* BAD CODE - see /sys/src/ape/lib/ap/$objtype/setjmp.s for real code int sigsetjmp(sigjmp_buf buf, int savemask) { int r; buf[0] = savemask; buf[1] = _psigblocked; return setjmp(&buf[2]); } */ /* * BUG: improper handling of process signal mask */ int sigaction(int sig, struct sigaction *act, struct sigaction *oact) { if(sig <= 0 || sig > MAXSIG || sig == SIGKILL){ errno = EINVAL; return -1; } if(oact){ oact->sa_handler = _sighdlr[sig]; oact->sa_mask = _psigblocked; oact->sa_flags = 0; } if(act){ _sighdlr[sig] = act->sa_handler; } return 0; } /* this is registered in _envsetup */ int _notehandler(void *v, char *msg) { int i; void(*f)(int, char*, Ureg*); Ureg *u; extern void _doatexits(void); /* in stdio/exit.c */ u = v; if(_finishing) _finish(0, 0); for(i = 0; i /* * BUG: don't keep track of these */ int sigpending(sigset_t *set) { *set = 0; return 0; } /sys/src/ape/lib/ap/plan9/sigprocmask.c 664 sys sys 1367613437 373 #include "lib.h" #include #include sigset_t _psigblocked; int sigprocmask(int how, sigset_t *set, sigset_t *oset) { if(oset) *oset = _psigblocked; if(how==SIG_BLOCK) _psigblocked |= *set; else if(how==SIG_UNBLOCK) _psigblocked &= ~*set; else if(how==SIG_SETMASK) _psigblocked = *set; else{ errno = EINVAL; return -1; } return 0; } /sys/src/ape/lib/ap/plan9/sigsuspend.c 664 sys sys 1369166818 144 #include #include /* * BUG: doesn't work */ int sigsuspend(sigset_t *set) { USED(set); errno = EINVAL; return -1; } /sys/src/ape/lib/ap/plan9/sleep.c 664 sys sys 1367613437 222 #include "lib.h" #include #include #include "sys9.h" unsigned int sleep(unsigned int secs) { time_t t0, t1; t0 = time(0); if(_SLEEP(secs*1000) < 0){ t1 = time(0); return t1-t0; } return 0; } /sys/src/ape/lib/ap/plan9/sqrt.c 664 sys sys 1367613437 759 /* sqrt returns the square root of its floating point argument. Newton's method. calls frexp */ #include #include #define _RESEARCH_SOURCE #include double sqrt(double arg) { double x, temp; int exp, i; if(isInf(arg, 1)) return arg; if(arg <= 0) { if(arg < 0) errno = EDOM; return 0; } x = frexp(arg, &exp); while(x < 0.5) { x *= 2; exp--; } /* * NOTE * this wont work on 1's comp */ if(exp & 1) { x *= 2; exp--; } temp = 0.5 * (1.0+x); while(exp > 60) { temp *= (1L<<30); exp -= 60; } while(exp < -60) { temp /= (1L<<30); exp += 60; } if(exp >= 0) temp *= 1L << (exp/2); else temp /= 1L << (-exp/2); for(i=0; i<=4; i++) temp = 0.5*(temp + arg/temp); return temp; } /sys/src/ape/lib/ap/plan9/stat.c 664 sys sys 1367613437 284 #include "lib.h" #include #include #include #include "sys9.h" #include "dir.h" int stat(const char *path, struct stat *buf) { Dir *d; if((d = _dirstat(path)) == nil){ _syserrno(); return -1; } _dirtostat(buf, d, 0); free(d); return 0; } /sys/src/ape/lib/ap/plan9/sys9.h 664 sys sys 1369185886 4005 typedef struct Waitmsg { int pid; /* of loved one */ unsigned long time[3]; /* of loved one & descendants */ char *msg; } Waitmsg; #define STATMAX 65535U /* max length of machine-independent stat structure */ #define DIRMAX (sizeof(Dir)+STATMAX) /* max length of Dir structure */ #define ERRMAX 128 /* max length of error string */ #define MORDER 0x0003 /* mask for bits defining order of mounting */ #define MREPL 0x0000 /* mount replaces object */ #define MBEFORE 0x0001 /* mount goes before others in union directory */ #define MAFTER 0x0002 /* mount goes after others in union directory */ #define MCREATE 0x0004 /* permit creation in mounted directory */ #define MCACHE 0x0010 /* cache some data */ #define MMASK 0x0007 /* all bits on */ #define OREAD 0 /* open for read */ #define OWRITE 1 /* write */ #define ORDWR 2 /* read and write */ #define OEXEC 3 /* execute, == read but check execute permission */ #define OTRUNC 16 /* or'ed in (except for exec), truncate file first */ #define OCEXEC 32 /* or'ed in, close on exec */ #define ORCLOSE 64 /* or'ed in, remove on close */ #define OEXCL 0x1000 /* or'ed in, exclusive use (create only) */ #define AEXIST 0 /* accessible: exists */ #define AEXEC 1 /* execute access */ #define AWRITE 2 /* write access */ #define AREAD 4 /* read access */ /* Segattch */ #define SG_RONLY 0040 /* read only */ #define SG_CEXEC 0100 /* detach on exec */ #define NCONT 0 /* continue after note */ #define NDFLT 1 /* terminate after note */ #define NSAVE 2 /* clear note but hold state */ #define NRSTR 3 /* restore saved state */ /* bits in Qid.type */ #define QTDIR 0x80 /* type bit for directories */ #define QTAPPEND 0x40 /* type bit for append only files */ #define QTEXCL 0x20 /* type bit for exclusive use files */ #define QTMOUNT 0x10 /* type bit for mounted channel */ #define QTFILE 0x00 /* plain file */ /* bits in Dir.mode */ #define DMDIR 0x80000000 /* mode bit for directories */ #define DMAPPEND 0x40000000 /* mode bit for append only files */ #define DMEXCL 0x20000000 /* mode bit for exclusive use files */ #define DMMOUNT 0x10000000 /* mode bit for mounted channel */ #define DMREAD 0x4 /* mode bit for read permission */ #define DMWRITE 0x2 /* mode bit for write permission */ #define DMEXEC 0x1 /* mode bit for execute permission */ /* rfork */ enum { RFNAMEG = (1<<0), RFENVG = (1<<1), RFFDG = (1<<2), RFNOTEG = (1<<3), RFPROC = (1<<4), RFMEM = (1<<5), RFNOWAIT = (1<<6), RFCNAMEG = (1<<10), RFCENVG = (1<<11), RFCFDG = (1<<12), RFREND = (1<<13), RFNOMNT = (1<<14) }; extern int _AWAIT(char*, int); extern int _ALARM(unsigned long); extern int _BIND(const char*, const char*, int); extern int _CHDIR(const char*); extern int _CLOSE(int); extern int _CREATE(char*, int, unsigned long); extern int _DUP(int, int); extern int _ERRSTR(char*, unsigned int); extern int _EXEC(char*, char*[]); extern void _EXITS(char *); extern int _FD2PATH(int, char*, int); extern int _FAUTH(int, char*); extern int _FSESSION(int, char*, int); extern int _FSTAT(int, unsigned char*, int); extern int _FWSTAT(int, unsigned char*, int); extern int _MOUNT(int, int, const char*, int, const char*); extern int _NOTED(int); extern int _NOTIFY(int(*)(void*, char*)); extern int _OPEN(const char*, int); extern int _PIPE(int*); extern long _PREAD(int, void*, long, long long); extern long _PWRITE(int, void*, long, long long); extern long _READ(int, void*, long); extern int _REMOVE(const char*); extern void* _RENDEZVOUS(void*, void*); extern int _RFORK(int); extern void* _SEGATTACH(int, char*, void*, unsigned long); extern void* _SEGBRK(void*, void*); extern int _SEGDETACH(void*); extern int _SEGFLUSH(void*, unsigned long); extern int _SEGFREE(void*, unsigned long); extern long long _SEEK(int, long long, int); extern int _SLEEP(long); extern int _STAT(const char*, unsigned char*, int); extern Waitmsg* _WAIT(void); extern long _WRITE(int, const void*, long); extern int _WSTAT(const char*, unsigned char*, int); /sys/src/ape/lib/ap/plan9/system.c 664 sys sys 1367613437 598 #include "lib.h" #include #include #include #include int system(const char *s) { int w, status; pid_t pid; char cmd[30], *oty; oty = getenv("cputype"); if(!oty) return -1; if(!s) return 1; /* a command interpreter is available */ pid = fork(); snprintf(cmd, sizeof cmd, "/%s/bin/ape/sh", oty); if(pid == 0) { execl(cmd, "sh", "-c", s, NULL); _exit(1); } if(pid < 0){ _syserrno(); return -1; } for(;;) { w = wait(&status); if(w == -1 || w == pid) break; } if(w == -1){ _syserrno(); return w; } return status; } /sys/src/ape/lib/ap/plan9/tcgetattr.c 664 sys sys 1367613437 3100 #include #include #include #include #include #include #include #include #include "sys9.h" #include "lib.h" #include "dir.h" #define CESC '\\' #define CINTR 0177 /* DEL */ #define CQUIT 034 /* FS, cntl | */ #define CERASE 010 /* BS */ #define CKILL 025 /* cntl u */ #define CEOF 04 /* cntl d */ #define CSTART 021 /* cntl q */ #define CSTOP 023 /* cntl s */ #define CSWTCH 032 /* cntl z */ #define CEOL 000 #define CNSWTCH 0 static int isptty(int fd) { Dir *d; int rv; if((d = _dirfstat(fd)) == nil) return 0; rv = (strncmp(d->name, "ptty", 4) == 0); free(d); return rv; } int tcgetattr(int fd, struct termios *t) { int n; char buf[60]; if(!isptty(fd)) { if(isatty(fd)) { /* If there is no emulation return sensible defaults */ t->c_iflag = ISTRIP|ICRNL|IXON|IXOFF; t->c_oflag = OPOST|TAB3|ONLCR; t->c_cflag = B9600; t->c_lflag = ISIG|ICANON|ECHO|ECHOE|ECHOK; t->c_cc[VINTR] = CINTR; t->c_cc[VQUIT] = CQUIT; t->c_cc[VERASE] = CERASE; t->c_cc[VKILL] = CKILL; t->c_cc[VEOF] = CEOF; t->c_cc[VEOL] = CEOL; t->c_cc[VSTART] = CSTART; t->c_cc[VSTOP] = CSTOP; return 0; } else { errno = ENOTTY; return -1; } } if(_SEEK(fd, -2, 0) != -2) { _syserrno(); return -1; } n = _READ(fd, buf, 57); if(n < 0) { _syserrno(); return -1; } t->c_iflag = strtoul(buf+4, 0, 16); t->c_oflag = strtoul(buf+9, 0, 16); t->c_cflag = strtoul(buf+14, 0, 16); t->c_lflag = strtoul(buf+19, 0, 16); for(n = 0; n < NCCS; n++) t->c_cc[n] = strtoul(buf+24+(n*3), 0, 16); return 0; } /* BUG: ignores optional actions */ int tcsetattr(int fd, int optactions, const struct termios *t) { int n, i; char buf[100]; USED(optactions); if(!isptty(fd)) { if(!isatty(fd)) { errno = ENOTTY; return -1; } else return 0; } n = sprintf(buf, "IOW %4.4x %4.4x %4.4x %4.4x ", t->c_iflag, t->c_oflag, t->c_cflag, t->c_lflag); for(i = 0; i < NCCS; i++) n += sprintf(buf+n, "%2.2x ", t->c_cc[i]); if(_SEEK(fd, -2, 0) != -2) { _syserrno(); return -1; } n = _WRITE(fd, buf, n); if(n < 0) { _syserrno(); return -1; } return 0; } int tcsetpgrp(int fd, pid_t pgrpid) { int n; char buf[30]; if(!isptty(fd)) { if(!isatty(fd)) { errno = ENOTTY; return -1; } else return 0; } n = sprintf(buf, "IOW note %d", pgrpid); if(_SEEK(fd, -2, 0) != -2) { _syserrno(); return -1; } n = _WRITE(fd, buf, n); if(n < 0) { _syserrno(); return -1; } return 0; } pid_t tcgetpgrp(int fd) { int n; pid_t pgrp; char buf[100]; if(!isptty(fd)) { errno = ENOTTY; return -1; } if(_SEEK(fd, -2, 0) != -2) { _syserrno(); return -1; } n = _READ(fd, buf, sizeof(buf)); if(n < 0) { _syserrno(); return -1; } pgrp = atoi(buf+24+(NCCS*3)); return pgrp; } /* should do a better job here */ int tcdrain(int) { errno = ENOTTY; return -1; } int tcflush(int, int) { errno = ENOTTY; return -1; } int tcflow(int, int) { errno = ENOTTY; return -1; } int tcsendbreak(int) { errno = ENOTTY; return -1; } /sys/src/ape/lib/ap/plan9/time.c 664 sys sys 1367613437 380 #include #include #include #include #include #include #include #include "sys9.h" time_t time(time_t *tp) { char b[20]; int f; time_t t; memset(b, 0, sizeof(b)); f = _OPEN("/dev/time", 0); if(f >= 0) { _PREAD(f, b, sizeof(b), 0); _CLOSE(f); } t = atol(b); if(tp) *tp = t; return t; } /sys/src/ape/lib/ap/plan9/times.c 664 sys sys 1367613437 730 #include #include #include #include #include #include #include #include static char* skip(char *p) { while(*p == ' ') p++; while(*p != ' ' && *p != 0) p++; return p; } clock_t times(struct tms *buf) { char b[200], *p; int f; unsigned long r; memset(b, 0, sizeof(b)); f = open("/dev/cputime", O_RDONLY); if(f >= 0) { lseek(f, SEEK_SET, 0); read(f, b, sizeof(b)); close(f); } p = b; if(buf) buf->tms_utime = atol(p); p = skip(p); if(buf) buf->tms_stime = atol(p); p = skip(p); r = atol(p); if(buf){ p = skip(p); buf->tms_cutime = atol(p); p = skip(p); buf->tms_cstime = atol(p); } return r; } /sys/src/ape/lib/ap/plan9/tmpfile.c 664 sys sys 1367613437 744 #include #include #include #include "sys9.h" #undef OPEN #include "../stdio/iolib.h" #include "lib.h" #include "dir.h" FILE * tmpfile(void){ FILE *f; static char name[]="/tmp/tf0000000000000"; char *p; int n; for(f=_IO_stream;f!=&_IO_stream[FOPEN_MAX];f++) if(f->state==CLOSED) break; if(f==&_IO_stream[FOPEN_MAX]) return NULL; while(access(name, 0) >= 0){ p = name+7; while(*p == '9') *p++ = '0'; if(*p == '\0') return NULL; ++*p; } n = _CREATE(name, 64|2, 0777); /* remove-on-close */ if(n==-1){ _syserrno(); return NULL; } _fdinfo[n].flags = FD_ISOPEN; _fdinfo[n].oflags = 2; f->fd=n; f->flags=0; f->state=OPEN; f->buf=0; f->rp=0; f->wp=0; f->lp=0; return f; } /sys/src/ape/lib/ap/plan9/ttyname.c 664 sys sys 1367613437 133 #include #include char * ttyname(int fd) { static char buf[100]; sprintf(buf, "/fd/%d", fd); return buf; } /sys/src/ape/lib/ap/plan9/umask.c 664 sys sys 1369166818 175 #include #include #include /* * No such concept in plan9, but supposed to be always successful */ mode_t umask(mode_t) { return 0; } /sys/src/ape/lib/ap/plan9/uname.c 664 sys sys 1367613437 520 #include #include #include int uname(struct utsname *n) { n->sysname = getenv("osname"); if(!n->sysname) n->sysname = "Plan9"; n->nodename = getenv("sysname"); if(!n->nodename){ n->nodename = getenv("site"); if(!n->nodename) n->nodename = "?"; } n->release = "4"; /* edition */ n->version = "0"; n->machine = getenv("cputype"); if(!n->machine) n->machine = "?"; if(strcmp(n->machine, "386") == 0) n->machine = "i386"; /* for gnu configure */ return 0; } /sys/src/ape/lib/ap/plan9/unlink.c 664 sys sys 1367613437 1481 #include "lib.h" #include #include #include #include #include #include "sys9.h" #include "dir.h" /* * BUG: errno mapping */ int unlink(const char *path) { int n, i, fd; long long nn; Dir *db1, *db2, nd; Fdinfo *f; char *p, newname[PATH_MAX], newelem[32]; /* if the file is already open, make it close-on-exec (and rename to qid) */ if((db1 = _dirstat(path)) == nil) { _syserrno(); return -1; } for(i=0, f = _fdinfo;i < OPEN_MAX; i++, f++) { if((f->flags&FD_ISOPEN) && (db2=_dirfstat(i)) != nil) { if(db1->qid.path == db2->qid.path && db1->qid.vers == db2->qid.vers && db1->type == db2->type && db1->dev == db2->dev) { sprintf(newelem, "%8.8lx%8.8lx", (ulong)(db2->qid.path>>32), (ulong)db2->qid.path); _nulldir(&nd); nd.name = newelem; if(_dirfwstat(i, &nd) < 0) p = (char*)path; else { p = strrchr(path, '/'); if(p == 0) p = newelem; else { memmove(newname, path, p-path); newname[p-path] = '/'; strcpy(newname+(p-path)+1, newelem); p = newname; } } /* reopen remove on close */ fd = _OPEN(p, 64|(f->oflags)); if(fd < 0){ free(db2); continue; } nn = _SEEK(i, 0, 1); if(nn < 0) nn = 0; _SEEK(fd, nn, 0); _DUP(fd, i); _CLOSE(fd); free(db1); return 0; } free(db2); } } if((n = _REMOVE(path)) < 0) _syserrno(); free(db1); return n; } /sys/src/ape/lib/ap/plan9/utime.c 664 sys sys 1367613437 469 #include "lib.h" #include #include #include #include #include #include "sys9.h" #include "dir.h" int utime(const char *path, const struct utimbuf *times) { int n; Dir nd; time_t curt; _nulldir(&nd); if(times == 0) { curt = time(0); nd.atime = curt; nd.mtime = curt; } else { nd.atime = times->actime; nd.mtime = times->modtime; } n = _dirwstat(path, &nd); if(n < 0) _syserrno(); return n; } /sys/src/ape/lib/ap/plan9/wait.c 664 sys sys 1367613437 2378 #include "lib.h" #include #include #include #include #include #include #include #include #include #include "sys9.h" #include "dir.h" /* * status not yet collected for processes that have exited */ typedef struct Waited Waited; struct Waited { Waitmsg* msg; Waited* next; }; static Waited *wd; static Waitmsg * lookpid(int pid) { Waited **wl, *w; Waitmsg *msg; for(wl = &wd; (w = *wl) != nil; wl = &w->next) if(pid <= 0 || w->msg->pid == pid){ msg = w->msg; *wl = w->next; free(w); return msg; } return 0; } static void addpid(Waitmsg *msg) { Waited *w; w = malloc(sizeof(*w)); if(w == nil){ /* lost it; what can we do? */ free(msg); return; } w->msg = msg; w->next = wd; wd = w; } static int waitstatus(Waitmsg *w) { int r, t; char *bp, *ep; r = 0; t = 0; if(w->msg[0]){ /* message is 'prog pid:string' */ bp = w->msg; while(*bp){ if(*bp++ == ':') break; } if(*bp == 0) bp = w->msg; r = strtol(bp, &ep, 10); if(*ep == 0){ if(r < 0 || r >= 256) r = 1; }else{ t = _stringsig(bp); if(t == 0) r = 1; } } return (r<<8) | t; } static void waitresource(struct rusage *ru, Waitmsg *w) { memset(ru, 0, sizeof(*ru)); ru->ru_utime.tv_sec = w->time[0]/1000; ru->ru_utime.tv_usec = (w->time[0]%1000)*1000; ru->ru_stime.tv_sec = w->time[1]/1000; ru->ru_stime.tv_usec = (w->time[1]%1000)*1000; } pid_t wait(int *status) { return wait4(-1, status, 0, nil); } pid_t waitpid(pid_t wpid, int *status, int options) { return wait4(wpid, status, options, nil); } pid_t wait3(int *status, int options, struct rusage *res) { return wait4(-1, status, options, res); } pid_t wait4(pid_t wpid, int *status, int options, struct rusage *res) { char pname[50]; Dir *d; Waitmsg *w; w = lookpid(wpid); if(w == nil){ if(options & WNOHANG){ snprintf(pname, sizeof(pname), "/proc/%d/wait", getpid()); d = _dirstat(pname); if(d != nil && d->length == 0){ free(d); return 0; } free(d); } for(;;){ w = _WAIT(); if(w == nil){ _syserrno(); return -1; } if(wpid <= 0 || w->pid == wpid) break; addpid(w); } } if(res != nil) waitresource(res, w); if(status != nil) *status = waitstatus(w); wpid = w->pid; free(w); return wpid; } /sys/src/ape/lib/ap/plan9/write.c 664 sys sys 1369842310 493 #include #include #include #include "lib.h" #include "sys9.h" ssize_t pwrite(int fd, const void *buf, size_t nbytes, off_t off) { int n; if(fd<0 || fd>=OPEN_MAX || !(_fdinfo[fd].flags&FD_ISOPEN)){ errno = EBADF; return -1; } if(_fdinfo[fd].oflags&O_APPEND) _SEEK(fd, 0, 2); n = _PWRITE(fd, buf, nbytes, off); if(n < 0) _syserrno(); return n; } ssize_t write(int fd, const void *buf, size_t nbytes) { return pwrite(fd, buf, nbytes, -1ll); } /sys/src/ape/lib/ap/posix 20000000775 sys sys 1369861527 0 /sys/src/ape/lib/ap/posix/getgrent.c 664 sys sys 1369842338 944 #include #include #include #define CL ':' #define CM ',' #define NL '\n' #define MAXGRP 100 static char GROUP[] = "/etc/group"; static FILE *grf = NULL; static char line[BUFSIZ+1]; static struct group group; static char *gr_mem[MAXGRP]; void setgrent(void) { if( !grf ) grf = fopen( GROUP, "r" ); else rewind( grf ); } void endgrent(void) { if( grf ){ fclose( grf ); grf = NULL; } } static char * grskip(char *p, int c) { while( *p && *p != c ) ++p; if( *p ) *p++ = 0; return( p ); } struct group * getgrent(void) { char *p, **q; if( !grf && !(grf = fopen( GROUP, "r" )) ) return(NULL); if( !(p = fgets( line, BUFSIZ, grf )) ) return(NULL); group.gr_name = p; p = grskip(p,CL); /* passwd */ group.gr_gid = atoi(p = grskip(p,CL)); group.gr_mem = gr_mem; p = grskip(p,CL); grskip(p,NL); q = gr_mem; while( *p ){ *q++ = p; p = grskip(p,CM); } *q = NULL; return( &group ); } /sys/src/ape/lib/ap/posix/getpwent.c 664 sys sys 1369843146 1026 #include #include #include static char PASSWD[] = "/etc/passwd"; static FILE *pwf = NULL; static char line[BUFSIZ+2]; static struct passwd passwd; void setpwent(void) { if( pwf == NULL ) pwf = fopen( PASSWD, "r" ); else rewind( pwf ); } void endpwent(void) { if( pwf != NULL ){ fclose( pwf ); pwf = NULL; } } static char * pwskip(char *p) { while( *p && *p != ':' && *p != '\n' ) ++p; if( *p ) *p++ = 0; else p[1] = 0; return(p); } struct passwd * pwdecode(char *p) { passwd.pw_name = p; p = pwskip(p); p = pwskip(p); /* passwd */ passwd.pw_uid = atoi(p); p = pwskip(p); passwd.pw_gid = atoi(p); p = pwskip(p); /* comment */ p = pwskip(p); /* gecos */ passwd.pw_dir = p; p = pwskip(p); passwd.pw_shell = p; p = pwskip(p); USED(p); return(&passwd); } struct passwd * getpwent(void) { char *p; if (pwf == NULL) { if( (pwf = fopen( PASSWD, "r" )) == NULL ) return(0); } p = fgets(line, BUFSIZ, pwf); if (p==NULL) return(0); return pwdecode (p); } /sys/src/ape/lib/ap/posix/locale.c 664 sys sys 1367613437 1442 #include #include #include static struct lconv Clocale = { ".", /* decimal_point */ "", /* thousands_sep */ "", /* grouping */ "", /* int_curr_symbol */ "", /* currency_symbol */ "", /* mon_decimal_point */ "", /* mon_thousands_sep */ "", /* mon_grouping */ "", /* positive_sign */ "", /* negative_sign */ CHAR_MAX, /* int_frac_digits */ CHAR_MAX, /* frac_digits */ CHAR_MAX, /* p_cs_precedes */ CHAR_MAX, /* p_sep_by_space */ CHAR_MAX, /* n_cs_precedes */ CHAR_MAX, /* n_sep_by_space */ CHAR_MAX, /* p_sign_posn */ CHAR_MAX, /* n_sign_posn */ }; static char *localename[2] = {"C", ""}; static short catlocale[6] = {0, 0, 0, 0, 0, 0}; /* indices into localename for categories LC_ALL, LC_COLLATE, etc. */ #define ASIZE(a) (sizeof(a)/sizeof(a[0])) char * setlocale(int category, const char *locale) { int c, i; if(category < 0 || category >= ASIZE(catlocale)) return 0; if(!locale) return localename[catlocale[category]]; for(c=0; c= ASIZE(localename)) return 0; catlocale[category] = c; if(category == LC_ALL) for(i=0; i #include #include int mkfifo(char *path, mode_t mode) { USED(path, mode); errno = 0; return -1; } /sys/src/ape/lib/ap/posix/mkfile 664 sys sys 1369840942 225 APE=/sys/src/ape <$APE/config LIB=/$objtype/lib/ape/libap.a OFILES=\ getgrent.$O\ getpwent.$O\ locale.$O\ mkfifo.$O\ pathconf.$O\ sigset.$O\ sysconf.$O\ tzset.$O\ #include #include #include long pathconf(const char *path, int name) { USED(path); switch(name) { case _PC_LINK_MAX: return LINK_MAX; case _PC_MAX_CANON: return MAX_CANON; case _PC_MAX_INPUT: return MAX_INPUT; case _PC_NAME_MAX: return NAME_MAX; case _PC_PATH_MAX: return PATH_MAX; case _PC_PIPE_BUF: return PIPE_BUF; case _PC_CHOWN_RESTRICTED: #ifdef _POSIX_CHOWN_RESTRICTED return _POSIX_CHOWN_RESTRICTED; #else return -1; #endif case _PC_NO_TRUNC: #ifdef _POSIX_NO_TRUNC return _POSIX_NO_TRUNC; #else return -1; #endif case _PC_VDISABLE: #ifdef _POSIX_VDISABLE return _POSIX_VDISABLE; #else return -1; #endif } errno = EINVAL; return -1; } long fpathconf(int fd, int name) { USED(fd); return pathconf(0, name); } /sys/src/ape/lib/ap/posix/sigset.c 664 sys sys 1367613437 892 #include #include /* * sigsets are 32-bit longs. if the 2<<(i-1) bit is on, * the signal #define'd as i in signal.h is inluded. */ static sigset_t stdsigs = SIGHUP|SIGINT|SIGQUIT|SIGILL|SIGABRT|SIGFPE|SIGKILL| SIGSEGV|SIGPIPE|SIGALRM|SIGTERM|SIGUSR1|SIGUSR2; #define BITSIG(s) (2<<(s)) int sigemptyset(sigset_t *set) { *set = 0; return 0; } int sigfillset(sigset_t *set) { *set = stdsigs; return 0; } int sigaddset(sigset_t *set, int signo) { int b; b = BITSIG(signo); if(!(b&stdsigs)){ errno = EINVAL; return -1; } *set |= b; return 0; } int sigdelset(sigset_t *set, int signo) { int b; b = BITSIG(signo); if(!(b&stdsigs)){ errno = EINVAL; return -1; } *set &= ~b; return 0; } int sigismember(sigset_t *set, int signo) { int b; b = BITSIG(signo); if(!(b&stdsigs)){ errno = EINVAL; return -1; } return (b&*set)? 1 : 0; } /sys/src/ape/lib/ap/posix/sysconf.c 664 sys sys 1367613437 695 #include #include #include #include #include #include long sysconf(int name) { switch(name) { case _SC_ARG_MAX: return ARG_MAX; case _SC_CHILD_MAX: return CHILD_MAX; case _SC_CLK_TCK: return CLOCKS_PER_SEC; case _SC_NGROUPS_MAX: return NGROUPS_MAX; case _SC_OPEN_MAX: return OPEN_MAX; case _SC_JOB_CONTROL: #ifdef _POSIX_JOB_CONTROL return _POSIX_JOB_CONTROL; #else return -1; #endif case _SC_SAVED_IDS: #ifdef _POSIX_SAVED_IDS return _POSIX_SAVED_IDS; #else return -1; #endif case _SC_VERSION: return _POSIX_VERSION; case _SC_LOGIN_NAME_MAX: return L_cuserid; } errno = EINVAL; return -1; } /sys/src/ape/lib/ap/posix/tzset.c 664 sys sys 1369166818 2806 #include #include #include #include #include #include #include #define TZFILE "/etc/TZ" static char TZ[128]; static char std[32] = "GMT0"; static char dst[32]; char *tzname[2] = { std, dst }; time_t tzoffset, tzdstoffset; int tzdst = 0; static int offset(char *env, time_t *off) { int n, sign; size_t len, retlen; retlen = 0; sign = 1; /* * strictly, no sign is allowed in the 'time' part of the * dst start/stop rules, but who cares? */ if (*env == '-' || *env == '+') { if (*env++ == '-') sign = -1; retlen++; } if ((len = strspn(env, ":0123456789")) == 0) return 0; retlen += len; for (*off = 0; len && isdigit(*env); len--) /* hours */ *off = *off*10 + (*env++ - '0')*60*60; if (len) { if (*env++ != ':') return 0; len--; } for (n = 0; len && isdigit(*env); len--) /* minutes */ n = n*10 + (*env++ - '0')*60; *off += n; if (len) { if (*env++ != ':') return 0; len--; } for (n = 0; len && isdigit(*env); len--) /* seconds */ n = n*10 + (*env++ - '0'); *off = (*off + n)*sign; return retlen; } /* * TZ=stdoffset[dst[offset][,start[/time],end[/time]]] */ void tzset(void) { char *env, *p, envbuf[128]; int fd, i; size_t len, retlen; time_t off; /* * get the TZ environment variable and check for validity. * the implementation-defined manner for dealing with the * leading ':' format is to reject it. * if it's ok, stash a copy away for quick comparison next time. */ if ((env = getenv("TZ")) == 0) { if ((fd = open(TZFILE, O_RDONLY)) == -1) return; if (read(fd, envbuf, sizeof(envbuf)-1) == -1) { close(fd); return; } close(fd); for (i = 0; i < sizeof(envbuf); i++) { if (envbuf[i] != '\n') continue; envbuf[i] = '\0'; break; } env = envbuf; } if (strcmp(env, TZ) == 0) return; if (*env == 0 || *env == ':') return; strncpy(TZ, env, sizeof(TZ)-1); TZ[sizeof(TZ)-1] = 0; /* * get the 'std' string. * before committing, check there is a valid offset. */ if ((len = strcspn(env, ":0123456789,-+")) == 0) return; if ((retlen = offset(env+len, &off)) == 0) return; for (p = std, i = len+retlen; i; i--) *p++ = *env++; *p = 0; tzoffset = -off; /* * get the 'dst' string (if any). */ if (*env == 0 || (len = strcspn(env, ":0123456789,-+")) == 0) return; for (p = dst; len; len--) *p++ = *env++; *p = 0; /* * optional dst offset. * default is one hour. */ tzdst = 1; if (retlen = offset(env+len, &off)) { tzdstoffset = -off; env += retlen; } else tzdstoffset = tzoffset + 60*60; /* * optional rule(s) for start/end of dst. */ if (*env == 0 || *env != ',' || *(env+1) == 0) return; env++; /* * we could go on... * but why bother? */ USED(env); } /sys/src/ape/lib/ap/power 20000000775 sys sys 1369168294 0 /sys/src/ape/lib/ap/power/cycles.s 664 sys sys 1367613437 323 #define TBRL 268 #define TBRU 269 /* Time base Upper/Lower (Reading) */ /* * time stamp counter; _cycles since power up * Runs at fasthz/4 cycles per second (m->clkin>>3) */ TEXT _cycles(SB),1,$0 loop: MOVW SPR(TBRU),R7 MOVW SPR(TBRL),R8 MOVW SPR(TBRU),R5 CMP R5,R7 BNE loop MOVW R7,0(R3) MOVW R8,4(R3) RETURN /sys/src/ape/lib/ap/power/getfcr.s 664 sys sys 1367613437 352 TEXT getfcr(SB), $8 MOVFL FPSCR, F3 FMOVD F3, f-8(SP) MOVW -4(SP), R3 RETURN TEXT getfsr(SB), $8 MOVFL FPSCR, F3 FMOVD F3, f-8(SP) MOVW -4(SP), R3 RETURN TEXT setfcr(SB), $8 SYNC MOVW R3, -4(SP) FMOVD -8(SP), F3 MOVFL F3, FPSCR ISYNC RETURN TEXT setfsr(SB), $8 SYNC MOVW R3, -4(SP) FMOVD -8(SP), F3 MOVFL F3, FPSCR ISYNC RETURN /sys/src/ape/lib/ap/power/lock.c 664 sys sys 1367613437 569 #include "../plan9/lib.h" #include "../plan9/sys9.h" #define _LOCK_EXTENSION #include int tas(int*); void lock(Lock *lk) { int i; /* once fast */ if(!tas(&lk->val)) return; /* a thousand times pretty fast */ for(i=0; i<1000; i++){ if(!tas(&lk->val)) return; _SLEEP(0); } /* now nice and slow */ for(i=0; i<1000; i++){ if(!tas(&lk->val)) return; _SLEEP(100); } /* take your time */ while(tas(&lk->val)) _SLEEP(1000); } int canlock(Lock *lk) { if(tas(&lk->val)) return 0; return 1; } void unlock(Lock *lk) { lk->val = 0; } /sys/src/ape/lib/ap/power/main9.s 664 sys sys 1367613437 201 TEXT _main(SB), 1, $16 MOVW $setSB(SB), R2 BL _envsetup(SB) MOVW inargc-4(FP), R3 MOVW $inargv+0(FP), R4 MOVW R3, 4(R1) MOVW R4, 8(R1) BL main(SB) loop: MOVW R3, 4(R1) BL exit(SB) BR loop /sys/src/ape/lib/ap/power/main9p.s 664 sys sys 1367613437 774 #define NPRIVATES 16 GLOBL _tos(SB), $4 GLOBL _privates(SB), $4 GLOBL _nprivates(SB), $4 TEXT _mainp(SB), 1, $(3*4+NPRIVATES*4) MOVW $setSB(SB), R2 /* _tos = arg */ MOVW R3, _tos(SB) MOVW $8(SP), R1 MOVW R1, _privates(SB) MOVW $NPRIVATES, R1 MOVW R1, _nprivates(SB) /* _profmain(); */ BL _envsetup(SB) /* _tos->prof.pp = _tos->prof.next; */ MOVW _tos+0(SB),R1 MOVW 4(R1),R2 MOVW R2,(R1) /* main(argc, argv, environ); */ MOVW inargc-4(FP), R3 MOVW $inargv+0(FP), R4 MOVW environ(SB), R5 MOVW R3, 4(R1) MOVW R4, 8(R1) MOVW R5, 12(R1) BL main(SB) loop: MOVW R3, 4(R1) BL exit(SB) MOVW $_profin(SB), R4 /* force loading of profile */ BR loop TEXT _savearg(SB), 1, $0 RETURN TEXT _callpc(SB), 1, $0 MOVW argp+0(FP), R3 MOVW 4(R3), R3 RETURN /sys/src/ape/lib/ap/power/memcmp.s 664 sys sys 1367613437 1493 TEXT memcmp(SB), $0 #define BDNZ BC 16,0, MOVW R3, s1+0(FP) /* R3 is pointer1 */ /* * performance: * 67mb/sec aligned; 16mb/sec unaligned */ MOVW n+8(FP), R4 /* R4 is count */ MOVW s2+4(FP), R5 /* R5 is pointer2 */ /* * let LSW do the work for 4 characters or less; aligned and unaligned */ CMP R4, $0 BLE eq CMP R4, $4 BLE out XOR R3, R5, R9 ANDCC $3, R9 BNE l4 /* pointers misaligned; use LSW loop */ /* * do enough bytes to align pointers */ ANDCC $3,R3, R9 BEQ l2 SUBC R9, $4, R9 MOVW R9, XER LSW (R3), R10 ADD R9, R3 LSW (R5), R14 ADD R9, R5 SUB R9, R4 CMPU R10, R14 BNE ne /* * compare 16 at a time */ l2: SRAWCC $4, R4, R9 BLE l4 MOVW R9, CTR SUB $4, R3 SUB $4, R5 l3: MOVWU 4(R3), R10 MOVWU 4(R5), R12 MOVWU 4(R3), R11 MOVWU 4(R5), R13 CMPU R10, R12 BNE ne MOVWU 4(R3), R10 MOVWU 4(R5), R12 CMPU R11, R13 BNE ne MOVWU 4(R3), R11 MOVWU 4(R5), R13 CMPU R10, R12 BNE ne CMPU R11, R13 BNE ne BDNZ l3 ADD $4, R3 ADD $4, R5 RLWNMCC $0, R4, $15, R4 /* residue */ BEQ eq /* * do remaining words with LSW; also does unaligned case */ l4: SRAWCC $2, R4, R9 BLE out MOVW R9, CTR l5: LSW (R3), $4, R10 ADD $4, R3 LSW (R5), $4, R11 ADD $4, R5 CMPU R10, R11 BNE ne BDNZ l5 RLWNMCC $0, R4, $3, R4 /* residue */ BEQ eq /* * do remaining bytes with final LSW */ out: MOVW R4, XER LSW (R3), R10 LSW (R5), R11 CMPU R10, R11 BNE ne eq: MOVW $0, R3 RETURN ne: MOVW $1, R3 BGE ret MOVW $-1,R3 ret: RETURN END /sys/src/ape/lib/ap/power/memmove.s 664 sys sys 1367613437 2479 #define BDNZ BC 16,0, TEXT memmove(SB), $0 BR move TEXT memcpy(SB), $0 move: /* * performance: * (tba) */ MOVW R3, s1+0(FP) MOVW n+8(FP), R9 /* R9 is count */ MOVW R3, R10 /* R10 is to-pointer */ CMP R9, $0 BEQ ret BLT trap MOVW s2+4(FP), R11 /* R11 is from-pointer */ /* * if no more than 16 bytes, just use one lsw/stsw */ CMP R9, $16 BLE fout ADD R9,R11, R13 /* R13 is end from-pointer */ ADD R9,R10, R12 /* R12 is end to-pointer */ /* * easiest test is copy backwards if * destination string has higher mem address */ CMPU R10, R11 BGT back /* * test if both pointers * are similarly word aligned */ XOR R10,R11, R7 ANDCC $3,R7 BNE fbad /* * move a few bytes to align pointers */ ANDCC $3,R10,R7 BEQ f2 SUBC R7, $4, R7 SUB R7, R9 MOVW R7, XER LSW (R11), R16 ADD R7, R11 STSW R16, (R10) ADD R7, R10 /* * turn R14 into doubleword count * copy 16 bytes at a time while there's room. */ f2: SRAWCC $4, R9, R14 BLE fout MOVW R14, CTR SUB $4, R11 SUB $4, R10 f3: MOVWU 4(R11), R16 MOVWU R16, 4(R10) MOVWU 4(R11), R17 MOVWU R17, 4(R10) MOVWU 4(R11), R16 MOVWU R16, 4(R10) MOVWU 4(R11), R17 MOVWU R17, 4(R10) BDNZ f3 RLWNMCC $0, R9, $15, R9 /* residue */ BEQ ret ADD $4, R11 ADD $4, R10 /* * move up to 16 bytes through R16 .. R19; aligned and unaligned */ fout: MOVW R9, XER LSW (R11), R16 STSW R16, (R10) BR ret /* * loop for unaligned copy, then copy up to 15 remaining bytes */ fbad: SRAWCC $4, R9, R14 BLE f6 MOVW R14, CTR f5: LSW (R11), $16, R16 ADD $16, R11 STSW R16, $16, (R10) ADD $16, R10 BDNZ f5 RLWNMCC $0, R9, $15, R9 /* residue */ BEQ ret f6: MOVW R9, XER LSW (R11), R16 STSW R16, (R10) BR ret /* * whole thing repeated for backwards */ back: CMP R9, $4 BLT bout XOR R12,R13, R7 ANDCC $3,R7 BNE bout b1: ANDCC $3,R13, R7 BEQ b2 MOVBZU -1(R13), R16 MOVBZU R16, -1(R12) SUB $1, R9 BR b1 b2: SRAWCC $4, R9, R14 BLE b4 MOVW R14, CTR b3: MOVWU -4(R13), R16 MOVWU R16, -4(R12) MOVWU -4(R13), R17 MOVWU R17, -4(R12) MOVWU -4(R13), R16 MOVWU R16, -4(R12) MOVWU -4(R13), R17 MOVWU R17, -4(R12) BDNZ b3 RLWNMCC $0, R9, $15, R9 /* residue */ BEQ ret b4: SRAWCC $2, R9, R14 BLE bout MOVW R14, CTR b5: MOVWU -4(R13), R16 MOVWU R16, -4(R12) BDNZ b5 RLWNMCC $0, R9, $3, R9 /* residue */ BEQ ret bout: CMPU R13, R11 BLE ret MOVBZU -1(R13), R16 MOVBZU R16, -1(R12) BR bout trap: MOVW $0, R0 MOVW 0(R0), R0 ret: MOVW s1+0(FP), R3 RETURN /sys/src/ape/lib/ap/power/memset.s 664 sys sys 1367613437 1211 TEXT memset(SB),$0 #define BDNZ BC 16,0, MOVW R3, p+0(FP) /* R3 is pointer */ /* * performance: * about 100mbytes/sec (8k blocks) on a 603/105 without L2 cache * drops to 40mbytes/sec (10k blocks) and 28mbytes/sec with 32k blocks */ MOVW n+8(FP), R4 /* R4 is count */ CMP R4, $0 BLE ret MOVW c+4(FP), R5 /* R5 is char */ /* * create 16 copies of c in R5 .. R8 */ RLWNM $0, R5, $0xff, R5 RLWMI $8, R5, $0xff00, R5 RLWMI $16, R5, $0xffff0000, R5 MOVW R5, R6 MOVW R5, R7 MOVW R5, R8 /* * let STSW do the work for 16 characters or less; aligned and unaligned */ CMP R4, $16 BLE out /* * store enough bytes to align pointer */ ANDCC $7,R3, R9 BEQ l2 SUBC R9, $8, R9 MOVW R9, XER STSW R5, (R3) ADD R9, R3 SUB R9, R4 /* * store 16 at a time while there's room * STSW was used here originally, but it's `completion serialised' */ l2: SRAWCC $4, R4, R9 BLE out MOVW R9, CTR l3: MOVW R5, 0(R3) ADD $8, R3, R10 MOVW R6, 4(R3) MOVW R7, 0(R10) ADD $8, R10, R3 MOVW R8, 4(R10) BDNZ l3 RLWNMCC $0, R4, $15, R4 /* residue */ BEQ ret /* * store up to 16 bytes from R5 .. R8; aligned and unaligned */ out: MOVW R4, XER STSW R5, (R3) ret: MOVW 0(FP), R3 RETURN END /sys/src/ape/lib/ap/power/mkfile 664 sys sys 1367613437 299 APE=/sys/src/ape <$APE/config LIB=/$objtype/lib/ape/libap.a OFILES=\ cycles.$O\ getfcr.$O\ lock.$O\ main9.$O\ main9p.$O\ memcmp.$O\ memmove.$O\ memset.$O\ notetramp.$O\ setjmp.$O\ strcmp.$O\ tas.$O\ vlop.$O\ vlrt.$O\ #include /* A stack to hold pcs when signals nest */ #define MAXSIGSTACK 20 typedef struct Pcstack Pcstack; static struct Pcstack { int sig; void (*hdlr)(int, char*, Ureg*); unsigned long restorepc; Ureg *u; } pcstack[MAXSIGSTACK]; static int nstack = 0; static void notecont(Ureg*, char*); void _notetramp(int sig, void (*hdlr)(int, char*, Ureg*), Ureg *u) { Pcstack *p; if(nstack >= MAXSIGSTACK) _NOTED(1); /* nesting too deep; just do system default */ p = &pcstack[nstack]; p->restorepc = u->pc; p->sig = sig; p->hdlr = hdlr; p->u = u; nstack++; u->pc = (unsigned long) notecont; _NOTED(2); /* NSAVE: clear note but hold state */ } static void notecont(Ureg *u, char *s) { Pcstack *p; void(*f)(int, char*, Ureg*); p = &pcstack[nstack-1]; f = p->hdlr; u->pc = p->restorepc; nstack--; (*f)(p->sig, s, u); _NOTED(3); /* NRSTR */ } #define JMPBUFPC 1 #define JMPBUFSP 0 extern sigset_t _psigblocked; void siglongjmp(sigjmp_buf j, int ret) { struct Ureg *u; if(j[0]) _psigblocked = j[1]; if(nstack == 0 || pcstack[nstack-1].u->sp > j[2+JMPBUFSP]) longjmp(j+2, ret); u = pcstack[nstack-1].u; nstack--; u->r3 = ret; if(ret == 0) u->r3 = 1; u->pc = j[2+JMPBUFPC]; u->sp = j[2+JMPBUFSP]; _NOTED(3); /* NRSTR */ } /sys/src/ape/lib/ap/power/setjmp.s 664 sys sys 1367613437 649 TEXT setjmp(SB), 1, $-4 MOVW LR, R4 MOVW R1, (R3) MOVW R4, 4(R3) MOVW $0, R3 RETURN TEXT sigsetjmp(SB), 1, $-4 MOVW savemask+4(FP), R4 MOVW R4, 0(R3) MOVW $_psigblocked(SB), R4 MOVW R4, 4(R3) MOVW LR, R4 MOVW R1, 8(R3) MOVW R4, 12(R3) MOVW $0, R3 RETURN TEXT longjmp(SB), 1, $-4 MOVW R3, R4 MOVW r+4(FP), R3 CMP R3, $0 BNE ok /* ansi: "longjmp(0) => longjmp(1)" */ MOVW $1, R3 /* bless their pointed heads */ ok: MOVW (R4), R1 MOVW 4(R4), R4 MOVW R4, LR BR (LR) /* * trampoline functions because the kernel smashes r1 * in the uregs given to notejmp */ TEXT __noterestore(SB), 1, $-4 MOVW R4, R3 MOVW R5, LR BR (LR) /sys/src/ape/lib/ap/power/strcmp.s 664 sys sys 1367613437 219 TEXT strcmp(SB), $0 MOVW s2+4(FP), R4 SUB $1, R3 SUB $1, R4 l1: MOVBZU 1(R3), R5 MOVBZU 1(R4), R6 CMP R5, R6 BNE ne CMP R5, $0 BNE l1 MOVW $0, R3 RETURN ne: MOVW $1, R3 BGT ret MOVW $-1, R3 ret: RETURN /sys/src/ape/lib/ap/power/tas.s 664 sys sys 1369166818 231 TEXT tas(SB), $0 SYNC MOVW R3, R4 MOVW $0xdeaddead,R5 tas1: DCBF (R4) /* fix for 603x bug */ LWAR (R4), R3 CMP R3, $0 BNE tas0 DCBT (R4) /* fix 405 errata cpu_210 */ STWCCC R5, (R4) BNE tas1 tas0: SYNC ISYNC RETURN /sys/src/ape/lib/ap/power/vlop.s 664 sys sys 1369166818 3748 #define BDNZ BC 16,0, /* * 64/64 division adapted from powerpc compiler writer's handbook * * (R3:R4) = (R3:R4) / (R5:R6) (64b) = (64b / 64b) * quo dvd dvs * * Remainder is left in R7:R8 * * Code comment notation: * msw = most-significant (high-order) word, i.e. bits 0..31 * lsw = least-significant (low-order) word, i.e. bits 32..63 * LZ = Leading Zeroes * SD = Significant Digits * * R3:R4 = dvd (input dividend); quo (output quotient) * R5:R6 = dvs (input divisor) * * R7:R8 = tmp; rem (output remainder) */ TEXT _divu64(SB), $0 MOVW a+0(FP), R3 MOVW a+4(FP), R4 MOVW b+8(FP), R5 MOVW b+12(FP), R6 /* count the number of leading 0s in the dividend */ CMP R3, $0 /* dvd.msw == 0? R3, */ CNTLZW R3, R11 /* R11 = dvd.msw.LZ */ CNTLZW R4, R9 /* R9 = dvd.lsw.LZ */ BNE lab1 /* if(dvd.msw != 0) dvd.LZ = dvd.msw.LZ */ ADD $32, R9, R11 /* dvd.LZ = dvd.lsw.LZ + 32 */ lab1: /* count the number of leading 0s in the divisor */ CMP R5, $0 /* dvd.msw == 0? */ CNTLZW R5, R9 /* R9 = dvs.msw.LZ */ CNTLZW R6, R10 /* R10 = dvs.lsw.LZ */ BNE lab2 /* if(dvs.msw != 0) dvs.LZ = dvs.msw.LZ */ ADD $32, R10, R9 /* dvs.LZ = dvs.lsw.LZ + 32 */ lab2: /* determine shift amounts to minimize the number of iterations */ CMP R11, R9 /* compare dvd.LZ to dvs.LZ */ SUBC R11, $64, R10 /* R10 = dvd.SD */ BGT lab9 /* if(dvs > dvd) quotient = 0 */ ADD $1, R9 /* ++dvs.LZ (or --dvs.SD) */ SUBC R9, $64, R9 /* R9 = dvs.SD */ ADD R9, R11 /* (dvd.LZ + dvs.SD) = left shift of dvd for */ /* initial dvd */ SUB R9, R10, R9 /* (dvd.SD - dvs.SD) = right shift of dvd for */ /* initial tmp */ MOVW R9, CTR /* number of iterations = dvd.SD - dvs.SD */ /* R7:R8 = R3:R4 >> R9 */ CMP R9, $32 ADD $-32, R9, R7 BLT lab3 /* if(R9 < 32) jump to lab3 */ SRW R7, R3, R8 /* tmp.lsw = dvd.msw >> (R9 - 32) */ MOVW $0, R7 /* tmp.msw = 0 */ BR lab4 lab3: SRW R9, R4, R8 /* R8 = dvd.lsw >> R9 */ SUBC R9, $32, R7 SLW R7, R3, R7 /* R7 = dvd.msw << 32 - R9 */ OR R7, R8 /* tmp.lsw = R8 | R7 */ SRW R9, R3, R7 /* tmp.msw = dvd.msw >> R9 */ lab4: /* R3:R4 = R3:R4 << R11 */ CMP R11,$32 ADDC $-32, R11, R9 BLT lab5 /* (R11 < 32)? */ SLW R9, R4, R3 /* dvd.msw = dvs.lsw << R9 */ MOVW $0, R4 /* dvd.lsw = 0 */ BR lab6 lab5: SLW R11, R3 /* R3 = dvd.msw << R11 */ SUBC R11, $32, R9 SRW R9, R4, R9 /* R9 = dvd.lsw >> 32 - R11 */ OR R9, R3 /* dvd.msw = R3 | R9 */ SLW R11, R4 /* dvd.lsw = dvd.lsw << R11 */ lab6: /* restoring division shift and subtract loop */ MOVW $-1, R10 ADDC $0, R7 /* clear carry bit before loop starts */ lab7: /* tmp:dvd is considered one large register */ /* each portion is shifted left 1 bit by adding it to itself */ /* adde sums the carry from the previous and creates a new carry */ ADDE R4,R4 /* shift dvd.lsw left 1 bit */ ADDE R3,R3 /* shift dvd.msw to left 1 bit */ ADDE R8,R8 /* shift tmp.lsw to left 1 bit */ ADDE R7,R7 /* shift tmp.msw to left 1 bit */ SUBC R6, R8, R11 /* tmp.lsw - dvs.lsw */ SUBECC R5, R7, R9 /* tmp.msw - dvs.msw */ BLT lab8 /* if(result < 0) clear carry bit */ MOVW R11, R8 /* move lsw */ MOVW R9, R7 /* move msw */ ADDC $1, R10, R11 /* set carry bit */ lab8: BDNZ lab7 ADDE R4,R4 /* quo.lsw (lsb = CA) */ ADDE R3,R3 /* quo.msw (lsb from lsw) */ lab10: MOVW qp+16(FP), R9 MOVW rp+20(FP), R10 CMP R9, $0 BEQ lab11 MOVW R3, 0(R9) MOVW R4, 4(R9) lab11: CMP R10, $0 BEQ lab12 MOVW R7, 0(R10) MOVW R8, 4(R10) lab12: RETURN lab9: /* Quotient is 0 (dvs > dvd) */ MOVW R4, R8 /* rmd.lsw = dvd.lsw */ MOVW R3, R7 /* rmd.msw = dvd.msw */ MOVW $0, R4 /* dvd.lsw = 0 */ MOVW $0, R3 /* dvd.msw = 0 */ BR lab10 /sys/src/ape/lib/ap/power/vlrt.c 664 sys sys 1369166818 3663 typedef unsigned long ulong; typedef unsigned int uint; typedef unsigned short ushort; typedef unsigned char uchar; typedef signed char schar; #define SIGN(n) (1UL<<(n-1)) typedef struct Vlong Vlong; struct Vlong { ulong hi; ulong lo; }; void abort(void); void _divu64(Vlong, Vlong, Vlong*, Vlong*); void _d2v(Vlong *y, double d) { union { double d; Vlong; } x; ulong xhi, xlo, ylo, yhi; int sh; x.d = d; xhi = (x.hi & 0xfffff) | 0x100000; xlo = x.lo; sh = 1075 - ((x.hi >> 20) & 0x7ff); ylo = 0; yhi = 0; if(sh >= 0) { /* v = (hi||lo) >> sh */ if(sh < 32) { if(sh == 0) { ylo = xlo; yhi = xhi; } else { ylo = (xlo >> sh) | (xhi << (32-sh)); yhi = xhi >> sh; } } else { if(sh == 32) { ylo = xhi; } else if(sh < 64) { ylo = xhi >> (sh-32); } } } else { /* v = (hi||lo) << -sh */ sh = -sh; if(sh <= 10) { ylo = xlo << sh; yhi = (xhi << sh) | (xlo >> (32-sh)); } else { /* overflow */ yhi = d; /* causes something awful */ } } if(x.hi & SIGN(32)) { if(ylo != 0) { ylo = -ylo; yhi = ~yhi; } else yhi = -yhi; } y->hi = yhi; y->lo = ylo; } void _f2v(Vlong *y, float f) { _d2v(y, f); } double _v2d(Vlong x) { if(x.hi & SIGN(32)) { if(x.lo) { x.lo = -x.lo; x.hi = ~x.hi; } else x.hi = -x.hi; return -((long)x.hi*4294967296. + x.lo); } return (long)x.hi*4294967296. + x.lo; } float _v2f(Vlong x) { return _v2d(x); } void _divvu(Vlong *q, Vlong n, Vlong d) { if(n.hi == 0 && d.hi == 0) { q->hi = 0; q->lo = n.lo / d.lo; return; } _divu64(n, d, q, 0); } void _modvu(Vlong *r, Vlong n, Vlong d) { if(n.hi == 0 && d.hi == 0) { r->hi = 0; r->lo = n.lo % d.lo; return; } _divu64(n, d, 0, r); } static void vneg(Vlong *v) { if(v->lo == 0) { v->hi = -v->hi; return; } v->lo = -v->lo; v->hi = ~v->hi; } void _divv(Vlong *q, Vlong n, Vlong d) { long nneg, dneg; if(n.hi == (((long)n.lo)>>31) && d.hi == (((long)d.lo)>>31)) { q->lo = (long)n.lo / (long)d.lo; q->hi = ((long)q->lo) >> 31; return; } nneg = n.hi >> 31; if(nneg) vneg(&n); dneg = d.hi >> 31; if(dneg) vneg(&d); _divu64(n, d, q, 0); if(nneg != dneg) vneg(q); } void _modv(Vlong *r, Vlong n, Vlong d) { long nneg, dneg; if(n.hi == (((long)n.lo)>>31) && d.hi == (((long)d.lo)>>31)) { r->lo = (long)n.lo % (long)d.lo; r->hi = ((long)r->lo) >> 31; return; } nneg = n.hi >> 31; if(nneg) vneg(&n); dneg = d.hi >> 31; if(dneg) vneg(&d); _divu64(n, d, 0, r); if(nneg) vneg(r); } void _vasop(Vlong *ret, void *lv, void fn(Vlong*, Vlong, Vlong), int type, Vlong rv) { Vlong t, u; u = *ret; switch(type) { default: abort(); break; case 1: /* schar */ t.lo = *(schar*)lv; t.hi = t.lo >> 31; fn(&u, t, rv); *(schar*)lv = u.lo; break; case 2: /* uchar */ t.lo = *(uchar*)lv; t.hi = 0; fn(&u, t, rv); *(uchar*)lv = u.lo; break; case 3: /* short */ t.lo = *(short*)lv; t.hi = t.lo >> 31; fn(&u, t, rv); *(short*)lv = u.lo; break; case 4: /* ushort */ t.lo = *(ushort*)lv; t.hi = 0; fn(&u, t, rv); *(ushort*)lv = u.lo; break; case 9: /* int */ t.lo = *(int*)lv; t.hi = t.lo >> 31; fn(&u, t, rv); *(int*)lv = u.lo; break; case 10: /* uint */ t.lo = *(uint*)lv; t.hi = 0; fn(&u, t, rv); *(uint*)lv = u.lo; break; case 5: /* long */ t.lo = *(long*)lv; t.hi = t.lo >> 31; fn(&u, t, rv); *(long*)lv = u.lo; break; case 6: /* ulong */ t.lo = *(ulong*)lv; t.hi = 0; fn(&u, t, rv); *(ulong*)lv = u.lo; break; case 7: /* vlong */ case 8: /* uvlong */ fn(&u, *(Vlong*)lv, rv); *(Vlong*)lv = u; break; } *ret = u; } /sys/src/ape/lib/ap/sparc 20000000775 sys sys 1367862839 0 /sys/src/ape/lib/ap/sparc/cycles.c 664 sys sys 1367613437 49 void _cycles(unsigned long long *u) { *u = 0; } /sys/src/ape/lib/ap/sparc/lock.c 664 sys sys 1367613437 255 #define _LOCK_EXTENSION #include "../plan9/sys9.h" #include int tas(int*); void lock(Lock *lk) { while(tas(&lk->val)) _SLEEP(0); } int canlock(Lock *lk) { if(tas(&lk->val)) return 0; return 1; } void unlock(Lock *lk) { lk->val = 0; } /sys/src/ape/lib/ap/sparc/main9.s 664 sys sys 1367613437 223 TEXT _main(SB), $16 MOVW $setSB(SB), R2 JMPL _envsetup(SB) MOVW inargc-4(FP), R7 MOVW $inargv+0(FP), R8 MOVW R7, 4(R1) MOVW R8, 8(R1) JMPL main(SB) loop: MOVW R7, 4(R1) JMPL exit(SB) MOVW $_mul(SB),R7 JMP loop /sys/src/ape/lib/ap/sparc/main9p.s 664 sys sys 1367613437 914 #define NPRIVATES 16 GLOBL _tos(SB), $4 GLOBL _privates(SB), $4 GLOBL _nprivates(SB), $4 TEXT _mainp(SB), 1, $(3*4+NPRIVATES*4) MOVW $setSB(SB), R2 /* _tos = arg */ MOVW R7, _tos(SB) /* MOVW _fpsr+0(SB), FSR FMOVD $0.5, F26 FSUBD F26, F26, F24 FADDD F26, F26, F28 FADDD F28, F28, F30 */ MOVW $8(SP), R1 MOVW R1, _privates(SB) MOVW $NPRIVATES, R1 MOVW R1, _nprivates(SB) /* _profmain(); */ JMPL _profmain(SB) /* _tos->prof.pp = _tos->prof.next; */ MOVW _tos+0(SB),R7 MOVW 4(R7),R8 MOVW R8,(R7) JMPL _envsetup(SB) /* main(argc, argv, environ); */ MOVW inargc-4(FP), R7 MOVW $inargv+0(FP), R8 MOVW environ(SB), R9 MOVW R8, 8(R1) MOVW R9, 12(R1) JMPL main(SB) loop: JMPL exit(SB) MOVW $_mul(SB), R0 /* force loading of muldiv */ MOVW $_profin(SB), R0 /* force loading of profile */ JMP loop TEXT _savearg(SB), 1, $0 RETURN TEXT _callpc(SB), 1, $0 MOVW argp-4(FP), R7 RETURN /sys/src/ape/lib/ap/sparc/memchr.s 664 sys sys 1367613437 275 TEXT memchr(SB), $0 MOVW R7, 0(FP) MOVW n+8(FP), R7 SUBCC R0,R7, R0 BE ret MOVW s1+0(FP), R8 MOVBU c+7(FP), R9 ADD R7,R8, R11 l1: MOVBU (R8), R10 SUBCC R9,R10, R0 ADD $1, R8 BE eq SUBCC R8,R11, R0 BNE l1 MOVW R0, R7 RETURN eq: SUB $1,R8, R7 ret: RETURN /sys/src/ape/lib/ap/sparc/memcmp.s 664 sys sys 1367613437 1715 #define Bxx BE TEXT memcmp(SB), $0 /* * performance: * (tba) */ MOVW R7, 0(FP) MOVW n+8(FP), R9 /* R9 is count */ MOVW s1+0(FP), R10 /* R10 is pointer1 */ MOVW s2+4(FP), R11 /* R11 is pointer2 */ ADD R9,R10, R12 /* R12 is end pointer1 */ /* * if not at least 4 chars, * dont even mess around. * 3 chars to guarantee any * rounding up to a word * boundary and 4 characters * to get at least maybe one * full word cmp. */ SUBCC $4,R9, R0 BL out /* * test if both pointers * are similarly word alligned */ XOR R10,R11, R7 ANDCC $3,R7, R0 BNE out /* * byte at a time to word allign */ l1: ANDCC $3,R10, R0 BE l2 MOVB 0(R10), R16 MOVB 0(R11), R17 ADD $1, R10 SUBCC R16,R17, R0 BNE ne ADD $1, R11 JMP l1 /* * turn R9 into end pointer1-15 * cmp 16 at a time while theres room */ l2: SUB $15,R12, R9 l3: SUBCC R10,R9, R0 BLEU l4 MOVW 0(R10), R16 MOVW 0(R11), R17 MOVW 4(R10), R18 SUBCC R16,R17, R0 BNE ne MOVW 4(R11), R19 MOVW 8(R10), R16 SUBCC R18,R19, R0 BNE ne MOVW 8(R11), R17 MOVW 12(R10), R18 SUBCC R16,R17, R0 BNE ne MOVW 12(R11), R19 ADD $16, R10 SUBCC R18,R19, R0 BNE ne SUBCC R16,R17, R0 BNE ne ADD $16, R11 JMP l3 /* * turn R9 into end pointer1-3 * cmp 4 at a time while theres room */ l4: SUB $3,R12, R9 l5: SUBCC R10,R9, R0 BLEU out MOVW 0(R10), R16 MOVW 0(R11), R17 ADD $4, R10 SUBCC R16,R17, R0 /* only works because big endian */ BNE ne ADD $4, R11 JMP l5 /* * last loop, cmp byte at a time */ out: SUBCC R10,R12, R0 BE zero MOVB 0(R10), R16 MOVB 0(R11), R17 ADD $1, R10 SUBCC R16,R17, R0 BNE ne ADD $1, R11 JMP out ne: BG plus MOVW $1, R7 RETURN plus: MOVW $-1, R7 RETURN zero: MOVW R0, R7 RETURN /sys/src/ape/lib/ap/sparc/memmove.s 664 sys sys 1367613437 2430 TEXT memmove(SB), $0 JMP move TEXT memcpy(SB), $0 move: /* * performance: * (tba) */ MOVW R7, s1+0(FP) MOVW n+8(FP), R9 /* R9 is count */ MOVW R7, R10 /* R10 is to-pointer */ SUBCC R0,R9, R0 BGE ok MOVW 0(R0), R0 ok: MOVW s2+4(FP), R11 /* R11 is from-pointer */ ADD R9,R11, R13 /* R13 is end from-pointer */ ADD R9,R10, R12 /* R12 is end to-pointer */ /* * easiest test is copy backwards if * destination string has higher mem address */ SUBCC R11,R10, R0 BGU back /* * if not at least 4 chars, * dont even mess around. * 3 chars to guarantee any * rounding up to a word * boundary and 4 characters * to get at least maybe one * full word store. */ SUBCC $4,R9, R0 BL fout /* * test if both pointers * are similarly word aligned */ XOR R10,R11, R7 ANDCC $3,R7, R0 BNE fout /* * byte at a time to word align */ f1: ANDCC $3,R10, R0 BE f2 MOVB 0(R11), R16 ADD $1, R11 MOVB R16, 0(R10) ADD $1, R10 JMP f1 /* * turn R9 into to-end pointer-15 * copy 16 at a time while theres room. * R12 is smaller than R13 -- * there are problems if R13 is 0. */ f2: SUB $15,R12, R9 f3: SUBCC R10,R9, R0 BLEU f4 MOVW 0(R11), R16 MOVW 4(R11), R17 MOVW R16, 0(R10) MOVW 8(R11), R16 MOVW R17, 4(R10) MOVW 12(R11), R17 ADD $16, R11 MOVW R16, 8(R10) MOVW R17, 12(R10) ADD $16, R10 JMP f3 /* * turn R9 into to-end pointer-3 * copy 4 at a time while theres room */ f4: SUB $3,R12, R9 f5: SUBCC R10,R9, R0 BLEU fout MOVW 0(R11), R16 ADD $4, R11 MOVW R16, 0(R10) ADD $4, R10 JMP f5 /* * last loop, copy byte at a time */ fout: SUBCC R11,R13, R0 BLEU ret MOVB 0(R11), R16 ADD $1, R11 MOVB R16, 0(R10) ADD $1, R10 JMP fout /* * whole thing repeated for backwards */ back: SUBCC $4,R9, R0 BL bout XOR R12,R13, R7 ANDCC $3,R7, R0 BNE bout b1: ANDCC $3,R13, R0 BE b2 MOVB -1(R13), R16 SUB $1, R13 MOVB R16, -1(R12) SUB $1, R12 JMP b1 b2: ADD $15,R11, R9 b3: SUBCC R9,R13, R0 BLEU b4 MOVW -4(R13), R16 MOVW -8(R13), R17 MOVW R16, -4(R12) MOVW -12(R13), R16 MOVW R17, -8(R12) MOVW -16(R13), R17 SUB $16, R13 MOVW R16, -12(R12) MOVW R17, -16(R12) SUB $16, R12 JMP b3 b4: ADD $3,R11, R9 b5: SUBCC R9,R13, R0 BLEU bout MOVW -4(R13), R16 SUB $4, R13 MOVW R16, -4(R12) SUB $4, R12 JMP b5 bout: SUBCC R11,R13, R0 BLEU ret MOVB -1(R13), R16 SUB $1, R13 MOVB R16, -1(R12) SUB $1, R12 JMP bout ret: MOVW s1+0(FP), R7 RETURN /sys/src/ape/lib/ap/sparc/memset.s 664 sys sys 1367613437 1282 TEXT memset(SB),$0 /* * performance: * (tba) */ MOVW R7, 0(FP) MOVW n+8(FP), R9 /* R9 is count */ MOVW p+0(FP), R10 /* R10 is pointer */ MOVW c+4(FP), R11 /* R11 is char */ ADD R9,R10, R12 /* R12 is end pointer */ /* * if not at least 4 chars, * dont even mess around. * 3 chars to guarantee any * rounding up to a word * boundary and 4 characters * to get at least maybe one * full word store. */ SUBCC $4,R9, R0 BL out /* * turn R11 into a word of characters */ AND $0xff, R11 SLL $8,R11, R7 OR R7, R11 SLL $16,R11, R7 OR R7, R11 /* * store one byte at a time until pointer * is alligned on a word boundary */ l1: ANDCC $3,R10, R0 BE l2 MOVB R11, 0(R10) ADD $1, R10 JMP l1 /* * turn R9 into end pointer-15 * store 16 at a time while theres room */ l2: ADD $-15,R12, R9 SUBCC R10,R9, R0 BLEU l4 l3: MOVW R11, 0(R10) MOVW R11, 4(R10) ADD $16, R10 SUBCC R10,R9, R0 MOVW R11, -8(R10) MOVW R11, -4(R10) BGU l3 /* * turn R9 into end pointer-3 * store 4 at a time while theres room */ l4: ADD $-3,R12, R9 l5: SUBCC R10,R9, R0 BLEU out MOVW R11, 0(R10) ADD $4, R10 JMP l5 /* * last loop, store byte at a time */ out: SUBCC R10,R12, R0 BLEU ret MOVB R11, 0(R10) ADD $1, R10 JMP out ret: MOVW s1+0(FP), R7 RETURN /sys/src/ape/lib/ap/sparc/mkfile 664 sys sys 1367613437 335 APE=/sys/src/ape <$APE/config LIB=/$objtype/lib/ape/libap.a OFILES=\ cycles.$O\ lock.$O\ main9.$O\ main9p.$O\ memchr.$O\ memcmp.$O\ memmove.$O\ memset.$O\ muldiv.$O\ notetramp.$O\ setjmp.$O\ strchr.$O\ strcmp.$O\ strcpy.$O\ tas.$O\ vlop.$O\ vlrt.$O\ 1<<(32-1)) * quo = 1<<(32-1); * for(i=0; den=0; i--) { * quo <<= 1; * if(num >= den) { * num -= den; * quo |= 1; * } * den >>= 1; * } * return quo::num; * } */ #define NOPROF 1 /* * calling sequence: * num: 4(R1) * den: 8(R1) * returns * quo: 4(R1) * rem: 8(R1) */ TEXT _udivmod(SB), NOPROF, $-4 MOVW $(1<<31), R11 MOVW 4(R1), R13 /* numerator */ MOVW 8(R1), R10 /* denominator */ CMP R10, R0 BNE udm20 MOVW R0, -1(R0) /* fault -- divide by zero */ udm20: MOVW R13, R12 CMP R13, R11 BLEU udm34 MOVW R11, R12 udm34: MOVW R0, R11 udm38: CMP R10, R12 BCC udm54 SLL $1, R10 ADD $1, R11 BA udm38 udm54: MOVW R0, R12 udm58: CMP R11, R0 BL udm8c SLL $1, R12 CMP R13, R10 BCS udm7c SUB R10, R13 OR $1, R12 udm7c: SRL $1, R10 SUB $1, R11 BA udm58 udm8c: MOVW R12, 4(R1) /* quotent */ MOVW R13, 8(R1) /* remainder */ JMPL 8(R15) /* * save working registers * and bring in num/den parameters */ TEXT _unsarg(SB), NOPROF, $-4 MOVW R10, 12(R1) MOVW R11, 16(R1) MOVW R12, 20(R1) MOVW R13, 24(R1) MOVW R14, 4(R1) MOVW 32(R1), R14 MOVW R14, 8(R1) JMPL 8(R15) /* * save working registers * and bring in absolute value * of num/den parameters */ TEXT _absarg(SB), NOPROF, $-4 MOVW R10, 12(R1) MOVW R11, 16(R1) MOVW R12, 20(R1) MOVW R13, 24(R1) MOVW R14, 28(R1) CMP R14, R0 BGE ab1 SUB R14, R0, R14 ab1: MOVW R14, 4(R1) /* numerator */ MOVW 32(R1), R14 CMP R14, R0 BGE ab2 SUB R14, R0, R14 ab2: MOVW R14, 8(R1) /* denominator */ JMPL 8(R15) /* * restore registers and * return to original caller * answer is in R14 */ TEXT _retarg(SB), NOPROF, $-4 MOVW 12(R1), R10 MOVW 16(R1), R11 MOVW 20(R1), R12 MOVW 24(R1), R13 MOVW 0(R1), R15 ADD $28, R1 JMP 8(R15) /* back to main sequence */ /* * calling sequence * num: R14 * den: 8(R1) * returns * quo: R14 */ TEXT _div(SB), NOPROF, $-4 SUB $28, R1 /* 4 reg save, 2 parameters, link */ MOVW R15, 0(R1) JMPL _absarg(SB) JMPL _udivmod(SB) MOVW 4(R1), R14 MOVW 28(R1), R10 /* clean up the sign */ MOVW 32(R1), R11 XORCC R11, R10, R0 BGE div1 SUB R14, R0, R14 div1: JMPL _retarg(SB) JMP 8(R15) /* not executed */ /* * calling sequence * num: R14 * den: 8(R1) * returns * quo: R14 */ TEXT _divl(SB), NOPROF, $-4 SUB $((4+2+1)*4), R1 /* 4 reg save, 2 parameters, link */ MOVW R15, 0(R1) JMPL _unsarg(SB) JMPL _udivmod(SB) MOVW 4(R1), R14 JMPL _retarg(SB) JMP 8(R15) /* not executed */ /* * calling sequence * num: R14 * den: 8(R1) * returns * rem: R14 */ TEXT _mod(SB), NOPROF, $-4 SUB $28, R1 /* 4 reg save, 2 parameters, link */ MOVW R15, 0(R1) JMPL _absarg(SB) JMPL _udivmod(SB) MOVW 8(R1), R14 MOVW 28(R1), R10 /* clean up the sign */ CMP R10, R0 BGE mod1 SUB R14, R0, R14 mod1: JMPL _retarg(SB) JMP 8(R15) /* not executed */ /* * calling sequence * num: R14 * den: 8(R1) * returns * rem: R14 */ TEXT _modl(SB), NOPROF, $-4 SUB $28, R1 /* 4 reg save, 2 parameters, link */ MOVW R15, 0(R1) JMPL _unsarg(SB) JMPL _udivmod(SB) MOVW 8(R1), R14 JMPL _retarg(SB) JMP 8(R15) /* not executed */ /* * special calling sequence: * arg1 in R14 * arg2 in 4(R1), will save R9 * nothing in 0(R1), will save R8 * result in R14 */ TEXT _mul+0(SB), NOPROF, $-4 /* * exchange stack and registers */ MOVW R8, 0(R1) MOVW 4(R1), R8 MOVW R9, 4(R1) CMP R14, R8 BLE mul1 MOVW R14, R9 MOVW R8, R14 MOVW R9, R8 mul1: MOVW R14, Y ANDNCC $0xFFF, R14, R0 BE mul_shortway ANDCC R0, R0, R9 /* zero partial product and clear N and V cond's */ /* long multiply */ MULSCC R8, R9, R9 /* 0 */ MULSCC R8, R9, R9 /* 1 */ MULSCC R8, R9, R9 /* 2 */ MULSCC R8, R9, R9 /* 3 */ MULSCC R8, R9, R9 /* 4 */ MULSCC R8, R9, R9 /* 5 */ MULSCC R8, R9, R9 /* 6 */ MULSCC R8, R9, R9 /* 7 */ MULSCC R8, R9, R9 /* 8 */ MULSCC R8, R9, R9 /* 9 */ MULSCC R8, R9, R9 /* 10 */ MULSCC R8, R9, R9 /* 11 */ MULSCC R8, R9, R9 /* 12 */ MULSCC R8, R9, R9 /* 13 */ MULSCC R8, R9, R9 /* 14 */ MULSCC R8, R9, R9 /* 15 */ MULSCC R8, R9, R9 /* 16 */ MULSCC R8, R9, R9 /* 17 */ MULSCC R8, R9, R9 /* 18 */ MULSCC R8, R9, R9 /* 19 */ MULSCC R8, R9, R9 /* 20 */ MULSCC R8, R9, R9 /* 21 */ MULSCC R8, R9, R9 /* 22 */ MULSCC R8, R9, R9 /* 23 */ MULSCC R8, R9, R9 /* 24 */ MULSCC R8, R9, R9 /* 25 */ MULSCC R8, R9, R9 /* 26 */ MULSCC R8, R9, R9 /* 27 */ MULSCC R8, R9, R9 /* 28 */ MULSCC R8, R9, R9 /* 29 */ MULSCC R8, R9, R9 /* 30 */ MULSCC R8, R9, R9 /* 31 */ MULSCC R0, R9, R9 /* 32; shift only */ MOVW Y, R14 /* get low part */ BA mul_return mul_shortway: ANDCC R0, R0, R9 /* zero partial product and clear N and V cond's */ MULSCC R8, R9, R9 /* 0 */ MULSCC R8, R9, R9 /* 1 */ MULSCC R8, R9, R9 /* 2 */ MULSCC R8, R9, R9 /* 3 */ MULSCC R8, R9, R9 /* 4 */ MULSCC R8, R9, R9 /* 5 */ MULSCC R8, R9, R9 /* 6 */ MULSCC R8, R9, R9 /* 7 */ MULSCC R8, R9, R9 /* 8 */ MULSCC R8, R9, R9 /* 9 */ MULSCC R8, R9, R9 /* 10 */ MULSCC R8, R9, R9 /* 11 */ MULSCC R0, R9, R9 /* 12; shift only */ MOVW Y, R8 SLL $12, R9 SRL $20, R8 OR R8, R9, R14 mul_return: MOVW 0(R1), R8 MOVW 4(R1), R9 JMP 8(R15) /sys/src/ape/lib/ap/sparc/notetramp.c 664 sys sys 1367613437 1578 #include "../plan9/lib.h" #include "../plan9/sys9.h" #include #include /* A stack to hold pcs when signals nest */ #define MAXSIGSTACK 20 typedef struct Pcstack Pcstack; static struct Pcstack { int sig; void (*hdlr)(int, char*, Ureg*); unsigned long restorepc; unsigned long restorenpc; Ureg *u; } pcstack[MAXSIGSTACK]; static int nstack = 0; static void notecont(Ureg*, char*); void _notetramp(int sig, void (*hdlr)(int, char*, Ureg*), Ureg *u) { Pcstack *p; if(nstack >= MAXSIGSTACK) _NOTED(1); /* nesting too deep; just do system default */ p = &pcstack[nstack]; p->restorepc = u->pc; p->restorenpc = u->npc; p->sig = sig; p->hdlr = hdlr; p->u = u; nstack++; u->pc = (unsigned long) notecont; u->npc = u->pc+4; _NOTED(2); /* NSAVE: clear note but hold state */ } static void notecont(Ureg *u, char *s) { Pcstack *p; void(*f)(int, char*, Ureg*); p = &pcstack[nstack-1]; f = p->hdlr; u->pc = p->restorepc; u->npc = p->restorenpc; nstack--; (*f)(p->sig, s, u); _NOTED(3); /* NRSTR */ } int __noterestore(void); #define JMPBUFPC 1 #define JMPBUFSP 0 #define JMPBUFDPC (-8) extern sigset_t _psigblocked; void siglongjmp(sigjmp_buf j, int ret) { struct Ureg *u; if(j[0]) _psigblocked = j[1]; if(nstack == 0 || pcstack[nstack-1].u->sp > j[2+JMPBUFSP]) longjmp(j+2, ret); u = pcstack[nstack-1].u; nstack--; u->r8 = ret; if(ret == 0) u->r8 = 1; u->r9 = j[JMPBUFPC] - JMPBUFDPC; u->pc = (unsigned long)__noterestore; u->npc = (unsigned long)__noterestore + 4; u->sp = j[JMPBUFSP]; _NOTED(3); /* NRSTR */ } /sys/src/ape/lib/ap/sparc/setjmp.s 664 sys sys 1367613437 600 TEXT setjmp(SB), 1, $0 MOVW R1, (R7) MOVW R15, 4(R7) MOVW $0, R7 RETURN TEXT sigsetjmp(SB), 1, $0 MOVW savemask+4(FP), R8 MOVW R8, 0(R7) MOVW $_psigblocked(SB), R8 MOVW R8, 4(R7) MOVW R1, 8(R7) MOVW R15, 12(R7) MOVW $0, R7 RETURN TEXT longjmp(SB), 1, $0 MOVW R7, R8 MOVW r+4(FP), R7 CMP R7, R0 BNE ok /* ansi: "longjmp(0) => longjmp(1)" */ MOVW $1, R7 /* bless their pointed heads */ ok: MOVW (R8), R1 MOVW 4(R8), R15 RETURN /* * trampoline functions because the kernel smashes r7 * in the uregs given to notejmp */ TEXT __noterestore(SB), 1, $-4 MOVW R8, R7 JMP (R9) /sys/src/ape/lib/ap/sparc/strchr.s 664 sys sys 1367613437 722 TEXT strchr(SB), $0 MOVW R7, 0(FP) MOVB c+7(FP), R10 MOVW s+0(FP), R9 SUBCC R0,R10, R0 BE l2 /* * char is not null */ l1: MOVB (R9), R7 ADD $1, R9 SUBCC R0,R7, R0 BE ret SUBCC R7,R10, R0 BNE l1 JMP rm1 /* * char is null * align to word */ l2: ANDCC $3,R9, R0 BE l3 MOVB (R9), R7 ADD $1, R9 SUBCC R0,R7, R0 BNE l2 JMP rm1 /* * develop byte masks */ l3: MOVW $0xff, R17 SLL $8,R17, R16 SLL $16,R17, R13 SLL $24,R17, R12 l4: MOVW (R9), R11 ADD $4, R9 ANDCC R12,R11, R0 BE b0 ANDCC R13,R11, R0 BE b1 ANDCC R16,R11, R0 BE b2 ANDCC R17,R11, R0 BNE l4 rm1: SUB $1,R9, R7 JMP ret b2: SUB $2,R9, R7 JMP ret b1: SUB $3,R9, R7 JMP ret b0: SUB $4,R9, R7 JMP ret ret: RETURN /sys/src/ape/lib/ap/sparc/strcmp.s 664 sys sys 1367613437 230 TEXT strcmp(SB), $0 MOVW s2+4(FP), R10 l1: MOVB 0(R7), R8 MOVB 0(R10), R9 ADD $1, R7 ADD $1, R10 CMP R8, R9 BNE l2 CMP R8, $0 BNE l1 MOVW R0, R7 RETURN l2: BLEU l3 MOVW $1, R7 RETURN l3: MOVW $-1, R7 RETURN /sys/src/ape/lib/ap/sparc/strcpy.s 664 sys sys 1367613437 1115 TEXT strcpy(SB), $0 MOVW R7, 0(FP) MOVW s1+0(FP), R9 /* R9 is to pointer */ MOVW s2+4(FP), R10 /* R10 is from pointer */ /* * test if both pointers * are similarly word aligned */ XOR R9,R10, R7 ANDCC $3,R7, R0 BNE una /* * make byte masks */ MOVW $0xff, R17 SLL $8,R17, R16 SLL $16,R17, R13 SLL $24,R17, R12 /* * byte at a time to word align */ al1: ANDCC $3,R10, R0 BE al2 MOVB (R10), R11 ADD $1, R10 MOVB R11, (R9) ADD $1, R9 SUBCC R0,R11, R0 BNE al1 JMP out /* * word at a time */ al2: ADD $4, R9 MOVW (R10), R11 /* fetch */ ADD $4, R10 ANDCC R12,R11, R0 /* is it byte 0 */ BE b0 ANDCC R13,R11, R0 /* is it byte 1 */ BE b1 ANDCC R16,R11, R0 /* is it byte 2 */ BE b2 MOVW R11, -4(R9) /* store */ ANDCC R17,R11, R0 /* is it byte 3 */ BNE al2 JMP out b0: MOVB R0, -4(R9) JMP out b1: SRL $24, R11 MOVB R11, -4(R9) MOVB R0, -3(R9) JMP out b2: SRL $24,R11, R7 MOVB R7, -4(R9) SRL $16, R11 MOVB R11, -3(R9) MOVB R0, -2(R9) JMP out una: MOVB (R10), R11 ADD $1, R10 MOVB R11, (R9) ADD $1, R9 SUBCC R0,R11, R0 BNE una out: MOVW s1+0(FP),R7 RETURN /sys/src/ape/lib/ap/sparc/tas.s 664 sys sys 1367613437 66 /* * tas uses LDSTUB */ TEXT tas(SB),$-4 TAS (R7),R7 RETURN /sys/src/ape/lib/ap/sparc/vlop.s 664 sys sys 1367613437 2423 TEXT _mulv(SB), $0 MOVW u1+8(FP), R8 MOVW u2+16(FP), R13 MOVW R13, R16 /* save low parts for later */ MOVW R8, R12 /* * unsigned 32x32 => 64 multiply */ CMP R13, R8 BLE mul1 MOVW R12, R13 MOVW R16, R8 mul1: MOVW R13, Y ANDNCC $0xFFF, R13, R0 BE mul_shortway ANDCC R0, R0, R9 /* zero partial product and clear N and V cond's */ /* long multiply */ MULSCC R8, R9, R9 /* 0 */ MULSCC R8, R9, R9 /* 1 */ MULSCC R8, R9, R9 /* 2 */ MULSCC R8, R9, R9 /* 3 */ MULSCC R8, R9, R9 /* 4 */ MULSCC R8, R9, R9 /* 5 */ MULSCC R8, R9, R9 /* 6 */ MULSCC R8, R9, R9 /* 7 */ MULSCC R8, R9, R9 /* 8 */ MULSCC R8, R9, R9 /* 9 */ MULSCC R8, R9, R9 /* 10 */ MULSCC R8, R9, R9 /* 11 */ MULSCC R8, R9, R9 /* 12 */ MULSCC R8, R9, R9 /* 13 */ MULSCC R8, R9, R9 /* 14 */ MULSCC R8, R9, R9 /* 15 */ MULSCC R8, R9, R9 /* 16 */ MULSCC R8, R9, R9 /* 17 */ MULSCC R8, R9, R9 /* 18 */ MULSCC R8, R9, R9 /* 19 */ MULSCC R8, R9, R9 /* 20 */ MULSCC R8, R9, R9 /* 21 */ MULSCC R8, R9, R9 /* 22 */ MULSCC R8, R9, R9 /* 23 */ MULSCC R8, R9, R9 /* 24 */ MULSCC R8, R9, R9 /* 25 */ MULSCC R8, R9, R9 /* 26 */ MULSCC R8, R9, R9 /* 27 */ MULSCC R8, R9, R9 /* 28 */ MULSCC R8, R9, R9 /* 29 */ MULSCC R8, R9, R9 /* 30 */ MULSCC R8, R9, R9 /* 31 */ MULSCC R0, R9, R9 /* 32; shift only; r9 is high part */ /* * need to correct top word if top bit set */ CMP R8, R0 BGE mul_tstlow ADD R13, R9 /* adjust the high parts */ mul_tstlow: MOVW Y, R13 /* get low part */ BA mul_done mul_shortway: ANDCC R0, R0, R9 /* zero partial product and clear N and V cond's */ MULSCC R8, R9, R9 /* 0 */ MULSCC R8, R9, R9 /* 1 */ MULSCC R8, R9, R9 /* 2 */ MULSCC R8, R9, R9 /* 3 */ MULSCC R8, R9, R9 /* 4 */ MULSCC R8, R9, R9 /* 5 */ MULSCC R8, R9, R9 /* 6 */ MULSCC R8, R9, R9 /* 7 */ MULSCC R8, R9, R9 /* 8 */ MULSCC R8, R9, R9 /* 9 */ MULSCC R8, R9, R9 /* 10 */ MULSCC R8, R9, R9 /* 11 */ MULSCC R0, R9, R9 /* 12; shift only; r9 is high part */ MOVW Y, R8 /* make low part of partial low part & high part */ SLL $12, R9, R13 SRL $20, R8 OR R8, R13 SRA $20, R9 /* high part */ mul_done: /* * mul by high halves if needed */ MOVW R13, 4(R7) MOVW u2+12(FP), R11 CMP R11, R0 BE nomul1 MUL R11, R12 ADD R12, R9 nomul1: MOVW u1+4(FP), R11 CMP R11, R0 BE nomul2 MUL R11, R16 ADD R16, R9 nomul2: MOVW R9, 0(R7) RETURN /sys/src/ape/lib/ap/sparc/vlrt.c 664 sys sys 1367613437 8982 typedef unsigned long ulong; typedef unsigned int uint; typedef unsigned short ushort; typedef unsigned char uchar; typedef signed char schar; #define SIGN(n) (1UL<<(n-1)) typedef struct Vlong Vlong; struct Vlong { union { struct { ulong hi; ulong lo; }; struct { ushort hims; ushort hils; ushort loms; ushort lols; }; }; }; void abort(void); void _addv(Vlong *r, Vlong a, Vlong b) { ulong lo, hi; lo = a.lo + b.lo; hi = a.hi + b.hi; if(lo < a.lo) hi++; r->lo = lo; r->hi = hi; } void _subv(Vlong *r, Vlong a, Vlong b) { ulong lo, hi; lo = a.lo - b.lo; hi = a.hi - b.hi; if(lo > a.lo) hi--; r->lo = lo; r->hi = hi; } void _d2v(Vlong *y, double d) { union { double d; struct Vlong; } x; ulong xhi, xlo, ylo, yhi; int sh; x.d = d; xhi = (x.hi & 0xfffff) | 0x100000; xlo = x.lo; sh = 1075 - ((x.hi >> 20) & 0x7ff); ylo = 0; yhi = 0; if(sh >= 0) { /* v = (hi||lo) >> sh */ if(sh < 32) { if(sh == 0) { ylo = xlo; yhi = xhi; } else { ylo = (xlo >> sh) | (xhi << (32-sh)); yhi = xhi >> sh; } } else { if(sh == 32) { ylo = xhi; } else if(sh < 64) { ylo = xhi >> (sh-32); } } } else { /* v = (hi||lo) << -sh */ sh = -sh; if(sh <= 10) { ylo = xlo << sh; yhi = (xhi << sh) | (xlo >> (32-sh)); } else { /* overflow */ yhi = d; /* causes something awful */ } } if(x.hi & SIGN(32)) { if(ylo != 0) { ylo = -ylo; yhi = ~yhi; } else yhi = -yhi; } y->hi = yhi; y->lo = ylo; } void _f2v(Vlong *y, float f) { _d2v(y, f); } double _v2d(Vlong x) { if(x.hi & SIGN(32)) { if(x.lo) { x.lo = -x.lo; x.hi = ~x.hi; } else x.hi = -x.hi; return -((long)x.hi*4294967296. + x.lo); } return (long)x.hi*4294967296. + x.lo; } float _v2f(Vlong x) { return _v2d(x); } static void dodiv(Vlong num, Vlong den, Vlong *q, Vlong *r) { ulong numlo, numhi, denhi, denlo, quohi, quolo, t; int i; numhi = num.hi; numlo = num.lo; denhi = den.hi; denlo = den.lo; /* * get a divide by zero */ if(denlo==0 && denhi==0) { numlo = numlo / denlo; } /* * set up the divisor and find the number of iterations needed */ if(numhi >= SIGN(32)) { quohi = SIGN(32); quolo = 0; } else { quohi = numhi; quolo = numlo; } i = 0; while(denhi < quohi || (denhi == quohi && denlo < quolo)) { denhi = (denhi<<1) | (denlo>>31); denlo <<= 1; i++; } quohi = 0; quolo = 0; for(; i >= 0; i--) { quohi = (quohi<<1) | (quolo>>31); quolo <<= 1; if(numhi > denhi || (numhi == denhi && numlo >= denlo)) { t = numlo; numlo -= denlo; if(numlo > t) numhi--; numhi -= denhi; quolo |= 1; } denlo = (denlo>>1) | (denhi<<31); denhi >>= 1; } if(q) { q->lo = quolo; q->hi = quohi; } if(r) { r->lo = numlo; r->hi = numhi; } } void _divvu(Vlong *q, Vlong n, Vlong d) { if(n.hi == 0 && d.hi == 0) { q->hi = 0; q->lo = n.lo / d.lo; return; } dodiv(n, d, q, 0); } void _modvu(Vlong *r, Vlong n, Vlong d) { if(n.hi == 0 && d.hi == 0) { r->hi = 0; r->lo = n.lo % d.lo; return; } dodiv(n, d, 0, r); } static void vneg(Vlong *v) { if(v->lo == 0) { v->hi = -v->hi; return; } v->lo = -v->lo; v->hi = ~v->hi; } void _divv(Vlong *q, Vlong n, Vlong d) { long nneg, dneg; if(n.hi == (((long)n.lo)>>31) && d.hi == (((long)d.lo)>>31)) { q->lo = (long)n.lo / (long)d.lo; q->hi = ((long)q->lo) >> 31; return; } nneg = n.hi >> 31; if(nneg) vneg(&n); dneg = d.hi >> 31; if(dneg) vneg(&d); dodiv(n, d, q, 0); if(nneg != dneg) vneg(q); } void _modv(Vlong *r, Vlong n, Vlong d) { long nneg, dneg; if(n.hi == (((long)n.lo)>>31) && d.hi == (((long)d.lo)>>31)) { r->lo = (long)n.lo % (long)d.lo; r->hi = ((long)r->lo) >> 31; return; } nneg = n.hi >> 31; if(nneg) vneg(&n); dneg = d.hi >> 31; if(dneg) vneg(&d); dodiv(n, d, 0, r); if(nneg) vneg(r); } void _rshav(Vlong *r, Vlong a, int b) { long t; t = a.hi; if(b >= 32) { r->hi = t>>31; if(b >= 64) { /* this is illegal re C standard */ r->lo = t>>31; return; } r->lo = t >> (b-32); return; } if(b <= 0) { r->hi = t; r->lo = a.lo; return; } r->hi = t >> b; r->lo = (t << (32-b)) | (a.lo >> b); } void _rshlv(Vlong *r, Vlong a, int b) { ulong t; t = a.hi; if(b >= 32) { r->hi = 0; if(b >= 64) { /* this is illegal re C standard */ r->lo = 0; return; } r->lo = t >> (b-32); return; } if(b <= 0) { r->hi = t; r->lo = a.lo; return; } r->hi = t >> b; r->lo = (t << (32-b)) | (a.lo >> b); } void _lshv(Vlong *r, Vlong a, int b) { ulong t; t = a.lo; if(b >= 32) { r->lo = 0; if(b >= 64) { /* this is illegal re C standard */ r->hi = 0; return; } r->hi = t << (b-32); return; } if(b <= 0) { r->lo = t; r->hi = a.hi; return; } r->lo = t << b; r->hi = (t >> (32-b)) | (a.hi << b); } void _andv(Vlong *r, Vlong a, Vlong b) { r->hi = a.hi & b.hi; r->lo = a.lo & b.lo; } void _orv(Vlong *r, Vlong a, Vlong b) { r->hi = a.hi | b.hi; r->lo = a.lo | b.lo; } void _xorv(Vlong *r, Vlong a, Vlong b) { r->hi = a.hi ^ b.hi; r->lo = a.lo ^ b.lo; } void _vpp(Vlong *l, Vlong *r) { l->hi = r->hi; l->lo = r->lo; r->lo++; if(r->lo == 0) r->hi++; } void _vmm(Vlong *l, Vlong *r) { l->hi = r->hi; l->lo = r->lo; if(r->lo == 0) r->hi--; r->lo--; } void _ppv(Vlong *l, Vlong *r) { r->lo++; if(r->lo == 0) r->hi++; l->hi = r->hi; l->lo = r->lo; } void _mmv(Vlong *l, Vlong *r) { if(r->lo == 0) r->hi--; r->lo--; l->hi = r->hi; l->lo = r->lo; } void _vasop(Vlong *ret, void *lv, void fn(Vlong*, Vlong, Vlong), int type, Vlong rv) { Vlong t, u; u = *ret; switch(type) { default: abort(); break; case 1: /* schar */ t.lo = *(schar*)lv; t.hi = t.lo >> 31; fn(&u, t, rv); *(schar*)lv = u.lo; break; case 2: /* uchar */ t.lo = *(uchar*)lv; t.hi = 0; fn(&u, t, rv); *(uchar*)lv = u.lo; break; case 3: /* short */ t.lo = *(short*)lv; t.hi = t.lo >> 31; fn(&u, t, rv); *(short*)lv = u.lo; break; case 4: /* ushort */ t.lo = *(ushort*)lv; t.hi = 0; fn(&u, t, rv); *(ushort*)lv = u.lo; break; case 9: /* int */ t.lo = *(int*)lv; t.hi = t.lo >> 31; fn(&u, t, rv); *(int*)lv = u.lo; break; case 10: /* uint */ t.lo = *(uint*)lv; t.hi = 0; fn(&u, t, rv); *(uint*)lv = u.lo; break; case 5: /* long */ t.lo = *(long*)lv; t.hi = t.lo >> 31; fn(&u, t, rv); *(long*)lv = u.lo; break; case 6: /* ulong */ t.lo = *(ulong*)lv; t.hi = 0; fn(&u, t, rv); *(ulong*)lv = u.lo; break; case 7: /* vlong */ case 8: /* uvlong */ fn(&u, *(Vlong*)lv, rv); *(Vlong*)lv = u; break; } *ret = u; } void _p2v(Vlong *ret, void *p) { long t; t = (ulong)p; ret->lo = t; ret->hi = 0; } void _sl2v(Vlong *ret, long sl) { long t; t = sl; ret->lo = t; ret->hi = t >> 31; } void _ul2v(Vlong *ret, ulong ul) { long t; t = ul; ret->lo = t; ret->hi = 0; } void _si2v(Vlong *ret, int si) { long t; t = si; ret->lo = t; ret->hi = t >> 31; } void _ui2v(Vlong *ret, uint ui) { long t; t = ui; ret->lo = t; ret->hi = 0; } void _sh2v(Vlong *ret, long sh) { long t; t = (sh << 16) >> 16; ret->lo = t; ret->hi = t >> 31; } void _uh2v(Vlong *ret, ulong ul) { long t; t = ul & 0xffff; ret->lo = t; ret->hi = 0; } void _sc2v(Vlong *ret, long uc) { long t; t = (uc << 24) >> 24; ret->lo = t; ret->hi = t >> 31; } void _uc2v(Vlong *ret, ulong ul) { long t; t = ul & 0xff; ret->lo = t; ret->hi = 0; } long _v2sc(Vlong rv) { long t; t = rv.lo & 0xff; return (t << 24) >> 24; } long _v2uc(Vlong rv) { return rv.lo & 0xff; } long _v2sh(Vlong rv) { long t; t = rv.lo & 0xffff; return (t << 16) >> 16; } long _v2uh(Vlong rv) { return rv.lo & 0xffff; } long _v2sl(Vlong rv) { return rv.lo; } long _v2ul(Vlong rv) { return rv.lo; } long _v2si(Vlong rv) { return rv.lo; } long _v2ui(Vlong rv) { return rv.lo; } int _testv(Vlong rv) { return rv.lo || rv.hi; } int _eqv(Vlong lv, Vlong rv) { return lv.lo == rv.lo && lv.hi == rv.hi; } int _nev(Vlong lv, Vlong rv) { return lv.lo != rv.lo || lv.hi != rv.hi; } int _ltv(Vlong lv, Vlong rv) { return (long)lv.hi < (long)rv.hi || (lv.hi == rv.hi && lv.lo < rv.lo); } int _lev(Vlong lv, Vlong rv) { return (long)lv.hi < (long)rv.hi || (lv.hi == rv.hi && lv.lo <= rv.lo); } int _gtv(Vlong lv, Vlong rv) { return (long)lv.hi > (long)rv.hi || (lv.hi == rv.hi && lv.lo > rv.lo); } int _gev(Vlong lv, Vlong rv) { return (long)lv.hi > (long)rv.hi || (lv.hi == rv.hi && lv.lo >= rv.lo); } int _lov(Vlong lv, Vlong rv) { return lv.hi < rv.hi || (lv.hi == rv.hi && lv.lo < rv.lo); } int _lsv(Vlong lv, Vlong rv) { return lv.hi < rv.hi || (lv.hi == rv.hi && lv.lo <= rv.lo); } int _hiv(Vlong lv, Vlong rv) { return lv.hi > rv.hi || (lv.hi == rv.hi && lv.lo > rv.lo); } int _hsv(Vlong lv, Vlong rv) { return lv.hi > rv.hi || (lv.hi == rv.hi && lv.lo >= rv.lo); } /sys/src/ape/lib/ap/stdio 20000000775 sys sys 1369861527 0 /sys/src/ape/lib/ap/stdio/_IO_getc.c 664 sys sys 1367613437 591 /* * pANS stdio -- _IO_getc */ #include "iolib.h" int _IO_getc(FILE *f) { int cnt, n; switch(f->state){ default: /* CLOSED, WR, ERR, EOF */ return EOF; case OPEN: if(_IO_setvbuf(f) != 0) return EOF; case RDWR: case RD: if(f->flags&STRING) return EOF; if(f->buf == f->unbuf) n = 1; else n = f->bufl; cnt=read(f->fd, f->buf, n); if(f->state == CLOSED) return EOF; switch(cnt){ case -1: f->state=ERR; return EOF; case 0: f->state=END; return EOF; default: f->state=RD; f->rp=f->buf; f->wp=f->buf+cnt; return (*f->rp++)&_IO_CHMASK; } } } /sys/src/ape/lib/ap/stdio/_IO_newfile.c 444 bootes sys 1367613437 385 /* * pANS stdio -- fopen */ #include "iolib.h" #define _PLAN9_SOURCE #include FILE *_IO_newfile(void) { static FILE *fx=0; static Lock fl; FILE *f; int i; lock(&fl); for(i=0; i= &_IO_stream[FOPEN_MAX]) fx=_IO_stream; if(fx->state==CLOSED) break; } f = fx; unlock(&fl); if(f->state!=CLOSED) return NULL; return f; } /sys/src/ape/lib/ap/stdio/_IO_putc.c 664 sys sys 1367613437 1916 /* * pANS stdio -- _IO_putc, _IO_cleanup */ #include "iolib.h" void _IO_cleanup(void){ fflush(NULL); } /* * Look this over for simplification */ int _IO_putc(int c, FILE *f){ int cnt; static int first=1; switch(f->state){ case RD: f->state=ERR; case ERR: case CLOSED: return EOF; case OPEN: if(_IO_setvbuf(f)!=0) return EOF; /* fall through */ case RDWR: case END: f->rp=f->buf+f->bufl; if(f->flags&LINEBUF){ f->wp=f->rp; f->lp=f->buf; } else f->wp=f->buf; break; } if(first){ atexit(_IO_cleanup); first=0; } if(f->flags&STRING){ f->rp=f->buf+f->bufl; if(f->wp==f->rp){ if(f->flags&BALLOC) f->buf=realloc(f->buf, f->bufl+BUFSIZ); else{ f->state=ERR; return EOF; } if(f->buf==NULL){ f->state=ERR; return EOF; } f->rp=f->buf+f->bufl; f->bufl+=BUFSIZ; } *f->wp++=c; } else if(f->flags&LINEBUF){ if(f->lp==f->rp){ cnt=f->lp-f->buf; if(f->flags&APPEND) lseek(f->fd, 0L, SEEK_END); if(cnt!=0 && write(f->fd, f->buf, cnt)!=cnt){ f->state=ERR; return EOF; } f->lp=f->buf; } *f->lp++=c; if(c=='\n'){ cnt=f->lp-f->buf; if(f->flags&APPEND) lseek(f->fd, 0L, SEEK_END); if(cnt!=0 && write(f->fd, f->buf, cnt)!=cnt){ f->state=ERR; return EOF; } f->lp=f->buf; } } else if(f->buf==f->unbuf){ f->unbuf[0]=c; if(f->flags&APPEND) lseek(f->fd, 0L, SEEK_END); if(write(f->fd, f->buf, 1)!=1){ f->state=ERR; return EOF; } } else{ if(f->wp==f->rp){ cnt=f->wp-f->buf; if(f->flags&APPEND) lseek(f->fd, 0L, SEEK_END); if(cnt!=0 && write(f->fd, f->buf, cnt)!=cnt){ f->state=ERR; return EOF; } f->wp=f->buf; f->rp=f->buf+f->bufl; } *f->wp++=c; } f->state=WR; /* * Make sure EOF looks different from putc(-1) * Should be able to cast to unsigned char, but * there's a vc bug preventing that from working */ return c&0xff; } /sys/src/ape/lib/ap/stdio/_dtoa.c 664 sys sys 1367613437 15559 #include "fconv.h" static int quorem(Bigint *, Bigint *); /* dtoa for IEEE arithmetic (dmg): convert double to ASCII string. * * Inspired by "How to Print Floating-Point Numbers Accurately" by * Guy L. Steele, Jr. and Jon L. White [Proc. ACM SIGPLAN '90, pp. 92-101]. * * Modifications: * 1. Rather than iterating, we use a simple numeric overestimate * to determine k = floor(log10(d)). We scale relevant * quantities using O(log2(k)) rather than O(k) multiplications. * 2. For some modes > 2 (corresponding to ecvt and fcvt), we don't * try to generate digits strictly left to right. Instead, we * compute with fewer bits and propagate the carry if necessary * when rounding the final digit up. This is often faster. * 3. Under the assumption that input will be rounded nearest, * mode 0 renders 1e23 as 1e23 rather than 9.999999999999999e22. * That is, we allow equality in stopping tests when the * round-nearest rule will give the same floating-point value * as would satisfaction of the stopping test with strict * inequality. * 4. We remove common factors of powers of 2 from relevant * quantities. * 5. When converting floating-point integers less than 1e16, * we use floating-point arithmetic rather than resorting * to multiple-precision integers. * 6. When asked to produce fewer than 15 digits, we first try * to get by with floating-point arithmetic; we resort to * multiple-precision integer arithmetic only if we cannot * guarantee that the floating-point calculation has given * the correctly rounded result. For k requested digits and * "uniformly" distributed input, the probability is * something like 10^(k-15) that we must resort to the long * calculation. */ char * _dtoa(double darg, int mode, int ndigits, int *decpt, int *sign, char **rve) { /* Arguments ndigits, decpt, sign are similar to those of ecvt and fcvt; trailing zeros are suppressed from the returned string. If not null, *rve is set to point to the end of the return value. If d is +-Infinity or NaN, then *decpt is set to 9999. mode: 0 ==> shortest string that yields d when read in and rounded to nearest. 1 ==> like 0, but with Steele & White stopping rule; e.g. with IEEE P754 arithmetic , mode 0 gives 1e23 whereas mode 1 gives 9.999999999999999e22. 2 ==> max(1,ndigits) significant digits. This gives a return value similar to that of ecvt, except that trailing zeros are suppressed. 3 ==> through ndigits past the decimal point. This gives a return value similar to that from fcvt, except that trailing zeros are suppressed, and ndigits can be negative. 4-9 should give the same return values as 2-3, i.e., 4 <= mode <= 9 ==> same return as mode 2 + (mode & 1). These modes are mainly for debugging; often they run slower but sometimes faster than modes 2-3. 4,5,8,9 ==> left-to-right digit generation. 6-9 ==> don't try fast floating-point estimate (if applicable). Values of mode other than 0-9 are treated as mode 0. Sufficient space is allocated to the return value to hold the suppressed trailing zeros. */ int bbits, b2, b5, be, dig, i, ieps, ilim, ilim0, ilim1, j, j1, k, k0, k_check, leftright, m2, m5, s2, s5, spec_case, try_quick; long L; #ifndef Sudden_Underflow int denorm; unsigned long x; #endif Bigint *b, *b1, *delta, *mlo, *mhi, *S; double ds; Dul d2, eps; char *s, *s0; static Bigint *result; static int result_k; Dul d; d.d = darg; if (result) { result->k = result_k; result->maxwds = 1 << result_k; Bfree(result); result = 0; } if (word0(d) & Sign_bit) { /* set sign for everything, including 0's and NaNs */ *sign = 1; word0(d) &= ~Sign_bit; /* clear sign bit */ } else *sign = 0; #if defined(IEEE_Arith) + defined(VAX) #ifdef IEEE_Arith if ((word0(d) & Exp_mask) == Exp_mask) #else if (word0(d) == 0x8000) #endif { /* Infinity or NaN */ *decpt = 9999; s = #ifdef IEEE_Arith !word1(d) && !(word0(d) & 0xfffff) ? "Infinity" : #endif "NaN"; if (rve) *rve = #ifdef IEEE_Arith s[3] ? s + 8 : #endif s + 3; return s; } #endif #ifdef IBM d.d += 0; /* normalize */ #endif if (!d.d) { *decpt = 1; s = "0"; if (rve) *rve = s + 1; return s; } b = d2b(d.d, &be, &bbits); #ifdef Sudden_Underflow i = (int)(word0(d) >> Exp_shift1 & (Exp_mask>>Exp_shift1)); #else if (i = (int)(word0(d) >> Exp_shift1 & (Exp_mask>>Exp_shift1))) { #endif d2.d = d.d; word0(d2) &= Frac_mask1; word0(d2) |= Exp_11; #ifdef IBM if (j = 11 - hi0bits(word0(d2) & Frac_mask)) d2.d /= 1 << j; #endif /* log(x) ~=~ log(1.5) + (x-1.5)/1.5 * log10(x) = log(x) / log(10) * ~=~ log(1.5)/log(10) + (x-1.5)/(1.5*log(10)) * log10(d) = (i-Bias)*log(2)/log(10) + log10(d2) * * This suggests computing an approximation k to log10(d) by * * k = (i - Bias)*0.301029995663981 * + ( (d2-1.5)*0.289529654602168 + 0.176091259055681 ); * * We want k to be too large rather than too small. * The error in the first-order Taylor series approximation * is in our favor, so we just round up the constant enough * to compensate for any error in the multiplication of * (i - Bias) by 0.301029995663981; since |i - Bias| <= 1077, * and 1077 * 0.30103 * 2^-52 ~=~ 7.2e-14, * adding 1e-13 to the constant term more than suffices. * Hence we adjust the constant term to 0.1760912590558. * (We could get a more accurate k by invoking log10, * but this is probably not worthwhile.) */ i -= Bias; #ifdef IBM i <<= 2; i += j; #endif #ifndef Sudden_Underflow denorm = 0; } else { /* d is denormalized */ i = bbits + be + (Bias + (P-1) - 1); x = i > 32 ? word0(d) << 64 - i | word1(d) >> i - 32 : word1(d) << 32 - i; d2.d = x; word0(d2) -= 31*Exp_msk1; /* adjust exponent */ i -= (Bias + (P-1) - 1) + 1; denorm = 1; } #endif ds = (d2.d-1.5)*0.289529654602168 + 0.1760912590558 + i*0.301029995663981; k = floor(ds); k_check = 1; if (k >= 0 && k <= Ten_pmax) { if (d.d < tens[k]) k--; k_check = 0; } j = bbits - i - 1; if (j >= 0) { b2 = 0; s2 = j; } else { b2 = -j; s2 = 0; } if (k >= 0) { b5 = 0; s5 = k; s2 += k; } else { b2 -= k; b5 = -k; s5 = 0; } if (mode < 0 || mode > 9) mode = 0; try_quick = 1; if (mode > 5) { mode -= 4; try_quick = 0; } leftright = 1; switch(mode) { case 0: case 1: ilim = ilim1 = -1; i = 18; ndigits = 0; break; case 2: leftright = 0; /* no break */ case 4: if (ndigits <= 0) ndigits = 1; ilim = ilim1 = i = ndigits; break; case 3: leftright = 0; /* no break */ case 5: i = ndigits + k + 1; ilim = i; ilim1 = i - 1; if (i <= 0) i = 1; } j = sizeof(unsigned long); for(result_k = 0; sizeof(Bigint) - sizeof(unsigned long) + j <= i; j <<= 1) result_k++; result = Balloc(result_k); s = s0 = (char *)result; if (ilim >= 0 && ilim <= Quick_max && try_quick) { /* Try to get by with floating-point arithmetic. */ i = 0; d2.d = d.d; k0 = k; ilim0 = ilim; ieps = 2; /* conservative */ if (k > 0) { ds = tens[k&0xf]; j = k >> 4; if (j & Bletch) { /* prevent overflows */ j &= Bletch - 1; d.d /= bigtens[n_bigtens-1]; ieps++; } for(; j; j >>= 1, i++) if (j & 1) { ieps++; ds *= bigtens[i]; } d.d /= ds; } else if (j1 = -k) { d.d *= tens[j1 & 0xf]; for(j = j1 >> 4; j; j >>= 1, i++) if (j & 1) { ieps++; d.d *= bigtens[i]; } } if (k_check && d.d < 1. && ilim > 0) { if (ilim1 <= 0) goto fast_failed; ilim = ilim1; k--; d.d *= 10.; ieps++; } eps.d = ieps*d.d + 7.; word0(eps) -= (P-1)*Exp_msk1; if (ilim == 0) { S = mhi = 0; d.d -= 5.; if (d.d > eps.d) goto one_digit; if (d.d < -eps.d) goto no_digits; goto fast_failed; } #ifndef No_leftright if (leftright) { /* Use Steele & White method of only * generating digits needed. */ eps.d = 0.5/tens[ilim-1] - eps.d; for(i = 0;;) { L = floor(d.d); d.d -= L; *s++ = '0' + (int)L; if (d.d < eps.d) goto ret1; if (1. - d.d < eps.d) goto bump_up; if (++i >= ilim) break; eps.d *= 10.; d.d *= 10.; } } else { #endif /* Generate ilim digits, then fix them up. */ eps.d *= tens[ilim-1]; for(i = 1;; i++, d.d *= 10.) { L = floor(d.d); d.d -= L; *s++ = '0' + (int)L; if (i == ilim) { if (d.d > 0.5 + eps.d) goto bump_up; else if (d.d < 0.5 - eps.d) { while(*--s == '0'); s++; goto ret1; } break; } } #ifndef No_leftright } #endif fast_failed: s = s0; d.d = d2.d; k = k0; ilim = ilim0; } /* Do we have a "small" integer? */ if (be >= 0 && k <= Int_max) { /* Yes. */ ds = tens[k]; if (ndigits < 0 && ilim <= 0) { S = mhi = 0; if (ilim < 0 || d.d <= 5*ds) goto no_digits; goto one_digit; } for(i = 1;; i++) { L = floor(d.d / ds); d.d -= L*ds; #ifdef Check_FLT_ROUNDS /* If FLT_ROUNDS == 2, L will usually be high by 1 */ if (d.d < 0) { L--; d.d += ds; } #endif *s++ = '0' + (int)L; if (i == ilim) { d.d += d.d; if (d.d > ds || d.d == ds && L & 1) { bump_up: while(*--s == '9') if (s == s0) { k++; *s = '0'; break; } ++*s++; } break; } d.d *= 10.; if (d.d == 0.) break; } goto ret1; } m2 = b2; m5 = b5; mhi = mlo = 0; if (leftright) { if (mode < 2) { i = #ifndef Sudden_Underflow denorm ? be + (Bias + (P-1) - 1 + 1) : #endif #ifdef IBM 1 + 4*P - 3 - bbits + ((bbits + be - 1) & 3); #else 1 + P - bbits; #endif } else { j = ilim - 1; if (m5 >= j) m5 -= j; else { s5 += j -= m5; b5 += j; m5 = 0; } if ((i = ilim) < 0) { m2 -= i; i = 0; } } b2 += i; s2 += i; mhi = i2b(1); } if (m2 > 0 && s2 > 0) { i = m2 < s2 ? m2 : s2; b2 -= i; m2 -= i; s2 -= i; } if (b5 > 0) { if (leftright) { if (m5 > 0) { mhi = pow5mult(mhi, m5); b1 = mult(mhi, b); Bfree(b); b = b1; } if (j = b5 - m5) b = pow5mult(b, j); } else b = pow5mult(b, b5); } S = i2b(1); if (s5 > 0) S = pow5mult(S, s5); /* Check for special case that d is a normalized power of 2. */ if (mode < 2) { if (!word1(d) && !(word0(d) & Bndry_mask) #ifndef Sudden_Underflow && word0(d) & Exp_mask #endif ) { /* The special case */ b2 += Log2P; s2 += Log2P; spec_case = 1; } else spec_case = 0; } /* Arrange for convenient computation of quotients: * shift left if necessary so divisor has 4 leading 0 bits. * * Perhaps we should just compute leading 28 bits of S once * and for all and pass them and a shift to quorem, so it * can do shifts and ors to compute the numerator for q. */ #ifdef Pack_32 if (i = ((s5 ? 32 - hi0bits(S->x[S->wds-1]) : 1) + s2) & 0x1f) i = 32 - i; #else if (i = ((s5 ? 32 - hi0bits(S->x[S->wds-1]) : 1) + s2) & 0xf) i = 16 - i; #endif if (i > 4) { i -= 4; b2 += i; m2 += i; s2 += i; } else if (i < 4) { i += 28; b2 += i; m2 += i; s2 += i; } if (b2 > 0) b = lshift(b, b2); if (s2 > 0) S = lshift(S, s2); if (k_check) { if (cmp(b,S) < 0) { k--; b = multadd(b, 10, 0); /* we botched the k estimate */ if (leftright) mhi = multadd(mhi, 10, 0); ilim = ilim1; } } if (ilim <= 0 && mode > 2) { if (ilim < 0 || cmp(b,S = multadd(S,5,0)) <= 0) { /* no digits, fcvt style */ no_digits: k = -1 - ndigits; goto ret; } one_digit: *s++ = '1'; k++; goto ret; } if (leftright) { if (m2 > 0) mhi = lshift(mhi, m2); /* Compute mlo -- check for special case * that d is a normalized power of 2. */ mlo = mhi; if (spec_case) { mhi = Balloc(mhi->k); Bcopy(mhi, mlo); mhi = lshift(mhi, Log2P); } for(i = 1;;i++) { dig = quorem(b,S) + '0'; /* Do we yet have the shortest decimal string * that will round to d? */ j = cmp(b, mlo); delta = diff(S, mhi); j1 = delta->sign ? 1 : cmp(b, delta); Bfree(delta); #ifndef ROUND_BIASED if (j1 == 0 && !mode && !(word1(d) & 1)) { if (dig == '9') goto round_9_up; if (j > 0) dig++; *s++ = dig; goto ret; } #endif if (j < 0 || j == 0 && !mode #ifndef ROUND_BIASED && !(word1(d) & 1) #endif ) { if (j1 > 0) { b = lshift(b, 1); j1 = cmp(b, S); if ((j1 > 0 || j1 == 0 && dig & 1) && dig++ == '9') goto round_9_up; } *s++ = dig; goto ret; } if (j1 > 0) { if (dig == '9') { /* possible if i == 1 */ round_9_up: *s++ = '9'; goto roundoff; } *s++ = dig + 1; goto ret; } *s++ = dig; if (i == ilim) break; b = multadd(b, 10, 0); if (mlo == mhi) mlo = mhi = multadd(mhi, 10, 0); else { mlo = multadd(mlo, 10, 0); mhi = multadd(mhi, 10, 0); } } } else for(i = 1;; i++) { *s++ = dig = quorem(b,S) + '0'; if (i >= ilim) break; b = multadd(b, 10, 0); } /* Round off last digit */ b = lshift(b, 1); j = cmp(b, S); if (j > 0 || j == 0 && dig & 1) { roundoff: while(*--s == '9') if (s == s0) { k++; *s++ = '1'; goto ret; } ++*s++; } else { while(*--s == '0'); s++; } ret: Bfree(S); if (mhi) { if (mlo && mlo != mhi) Bfree(mlo); Bfree(mhi); } ret1: Bfree(b); *s = 0; *decpt = k + 1; if (rve) *rve = s; return s0; } static int quorem(Bigint *b, Bigint *S) { int n; long borrow, y; unsigned long carry, q, ys; unsigned long *bx, *bxe, *sx, *sxe; #ifdef Pack_32 long z; unsigned long si, zs; #endif n = S->wds; #ifdef DEBUG /*debug*/ if (b->wds > n) /*debug*/ Bug("oversize b in quorem"); #endif if (b->wds < n) return 0; sx = S->x; sxe = sx + --n; bx = b->x; bxe = bx + n; q = *bxe / (*sxe + 1); /* ensure q <= true quotient */ #ifdef DEBUG /*debug*/ if (q > 9) /*debug*/ Bug("oversized quotient in quorem"); #endif if (q) { borrow = 0; carry = 0; do { #ifdef Pack_32 si = *sx++; ys = (si & 0xffff) * q + carry; zs = (si >> 16) * q + (ys >> 16); carry = zs >> 16; y = (*bx & 0xffff) - (ys & 0xffff) + borrow; borrow = y >> 16; Sign_Extend(borrow, y); z = (*bx >> 16) - (zs & 0xffff) + borrow; borrow = z >> 16; Sign_Extend(borrow, z); Storeinc(bx, z, y); #else ys = *sx++ * q + carry; carry = ys >> 16; y = *bx - (ys & 0xffff) + borrow; borrow = y >> 16; Sign_Extend(borrow, y); *bx++ = y & 0xffff; #endif } while(sx <= sxe); if (!*bxe) { bx = b->x; while(--bxe > bx && !*bxe) --n; b->wds = n; } } if (cmp(b, S) >= 0) { q++; borrow = 0; carry = 0; bx = b->x; sx = S->x; do { #ifdef Pack_32 si = *sx++; ys = (si & 0xffff) + carry; zs = (si >> 16) + (ys >> 16); carry = zs >> 16; y = (*bx & 0xffff) - (ys & 0xffff) + borrow; borrow = y >> 16; Sign_Extend(borrow, y); z = (*bx >> 16) - (zs & 0xffff) + borrow; borrow = z >> 16; Sign_Extend(borrow, z); Storeinc(bx, z, y); #else ys = *sx++ + carry; carry = ys >> 16; y = *bx - (ys & 0xffff) + borrow; borrow = y >> 16; Sign_Extend(borrow, y); *bx++ = y & 0xffff; #endif } while(sx <= sxe); bx = b->x; bxe = bx + n; if (!*bxe) { while(--bxe > bx && !*bxe) --n; b->wds = n; } } return q; } /sys/src/ape/lib/ap/stdio/_fconv.c 664 sys sys 1369843182 8979 /* Common routines for _dtoa and strtod */ #include "fconv.h" #ifdef DEBUG #include #define Bug(x) {fprintf(stderr, "%s\n", x); exit(1);} #endif double _tens[] = { 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22 #ifdef VAX , 1e23, 1e24 #endif }; #ifdef IEEE_Arith double _bigtens[] = { 1e16, 1e32, 1e64, 1e128, 1e256 }; double _tinytens[] = { 1e-16, 1e-32, 1e-64, 1e-128, 1e-256 }; #else #ifdef IBM double _bigtens[] = { 1e16, 1e32, 1e64 }; double _tinytens[] = { 1e-16, 1e-32, 1e-64 }; #else double _bigtens[] = { 1e16, 1e32 }; double _tinytens[] = { 1e-16, 1e-32 }; #endif #endif static Bigint *freelist[Kmax+1]; Bigint * _Balloc(int k) { int x; Bigint *rv; if (rv = freelist[k]) { freelist[k] = rv->next; } else { x = 1 << k; rv = (Bigint *)malloc(sizeof(Bigint) + (x-1)*sizeof(long)); rv->k = k; rv->maxwds = x; } rv->sign = rv->wds = 0; return rv; } void _Bfree(Bigint *v) { if (v) { v->next = freelist[v->k]; freelist[v->k] = v; } } Bigint * _multadd(Bigint *b, int m, int a) /* multiply by m and add a */ { int i, wds; unsigned long *x, y; #ifdef Pack_32 unsigned long xi, z; #endif Bigint *b1; wds = b->wds; x = b->x; i = 0; do { #ifdef Pack_32 xi = *x; y = (xi & 0xffff) * m + a; z = (xi >> 16) * m + (y >> 16); a = (int)(z >> 16); *x++ = (z << 16) + (y & 0xffff); #else y = *x * m + a; a = (int)(y >> 16); *x++ = y & 0xffff; #endif } while(++i < wds); if (a) { if (wds >= b->maxwds) { b1 = Balloc(b->k+1); Bcopy(b1, b); Bfree(b); b = b1; } b->x[wds++] = a; b->wds = wds; } return b; } int _hi0bits(unsigned long x) { int k = 0; if (!(x & 0xffff0000)) { k = 16; x <<= 16; } if (!(x & 0xff000000)) { k += 8; x <<= 8; } if (!(x & 0xf0000000)) { k += 4; x <<= 4; } if (!(x & 0xc0000000)) { k += 2; x <<= 2; } if (!(x & 0x80000000)) { k++; if (!(x & 0x40000000)) return 32; } return k; } static int lo0bits(unsigned long *y) { int k; unsigned long x = *y; if (x & 7) { if (x & 1) return 0; if (x & 2) { *y = x >> 1; return 1; } *y = x >> 2; return 2; } k = 0; if (!(x & 0xffff)) { k = 16; x >>= 16; } if (!(x & 0xff)) { k += 8; x >>= 8; } if (!(x & 0xf)) { k += 4; x >>= 4; } if (!(x & 0x3)) { k += 2; x >>= 2; } if (!(x & 1)) { k++; x >>= 1; if (!x & 1) return 32; } *y = x; return k; } Bigint * _i2b(int i) { Bigint *b; b = Balloc(1); b->x[0] = i; b->wds = 1; return b; } Bigint * _mult(Bigint *a, Bigint *b) { Bigint *c; int k, wa, wb, wc; unsigned long carry, y, z; unsigned long *x, *xa, *xae, *xb, *xbe, *xc, *xc0; #ifdef Pack_32 unsigned long z2; #endif if (a->wds < b->wds) { c = a; a = b; b = c; } k = a->k; wa = a->wds; wb = b->wds; wc = wa + wb; if (wc > a->maxwds) k++; c = Balloc(k); for(x = c->x, xa = x + wc; x < xa; x++) *x = 0; xa = a->x; xae = xa + wa; xb = b->x; xbe = xb + wb; xc0 = c->x; #ifdef Pack_32 for(; xb < xbe; xb++, xc0++) { if (y = *xb & 0xffff) { x = xa; xc = xc0; carry = 0; do { z = (*x & 0xffff) * y + (*xc & 0xffff) + carry; carry = z >> 16; z2 = (*x++ >> 16) * y + (*xc >> 16) + carry; carry = z2 >> 16; Storeinc(xc, z2, z); } while(x < xae); *xc = carry; } if (y = *xb >> 16) { x = xa; xc = xc0; carry = 0; z2 = *xc; do { z = (*x & 0xffff) * y + (*xc >> 16) + carry; carry = z >> 16; Storeinc(xc, z, z2); z2 = (*x++ >> 16) * y + (*xc & 0xffff) + carry; carry = z2 >> 16; } while(x < xae); *xc = z2; } } #else for(; xb < xbe; xc0++) { if (y = *xb++) { x = xa; xc = xc0; carry = 0; do { z = *x++ * y + *xc + carry; carry = z >> 16; *xc++ = z & 0xffff; } while(x < xae); *xc = carry; } } #endif for(xc0 = c->x, xc = xc0 + wc; wc > 0 && !*--xc; --wc) ; c->wds = wc; return c; } static Bigint *p5s; Bigint * _pow5mult(Bigint *b, int k) { Bigint *b1, *p5, *p51; int i; static int p05[3] = { 5, 25, 125 }; if (i = k & 3) b = multadd(b, p05[i-1], 0); if (!(k >>= 2)) return b; if (!(p5 = p5s)) { /* first time */ p5 = p5s = i2b(625); p5->next = 0; } for(;;) { if (k & 1) { b1 = mult(b, p5); Bfree(b); b = b1; } if (!(k >>= 1)) break; if (!(p51 = p5->next)) { p51 = p5->next = mult(p5,p5); p51->next = 0; } p5 = p51; } return b; } Bigint * _lshift(Bigint *b, int k) { int i, k1, n, n1; Bigint *b1; unsigned long *x, *x1, *xe, z; #ifdef Pack_32 n = k >> 5; #else n = k >> 4; #endif k1 = b->k; n1 = n + b->wds + 1; for(i = b->maxwds; n1 > i; i <<= 1) k1++; b1 = Balloc(k1); x1 = b1->x; for(i = 0; i < n; i++) *x1++ = 0; x = b->x; xe = x + b->wds; #ifdef Pack_32 if (k &= 0x1f) { k1 = 32 - k; z = 0; do { *x1++ = *x << k | z; z = *x++ >> k1; } while(x < xe); if (*x1 = z) ++n1; } #else if (k &= 0xf) { k1 = 16 - k; z = 0; do { *x1++ = *x << k & 0xffff | z; z = *x++ >> k1; } while(x < xe); if (*x1 = z) ++n1; } #endif else do *x1++ = *x++; while(x < xe); b1->wds = n1 - 1; Bfree(b); return b1; } int _cmp(Bigint *a, Bigint *b) { unsigned long *xa, *xa0, *xb, *xb0; int i, j; i = a->wds; j = b->wds; #ifdef DEBUG if (i > 1 && !a->x[i-1]) Bug("cmp called with a->x[a->wds-1] == 0"); if (j > 1 && !b->x[j-1]) Bug("cmp called with b->x[b->wds-1] == 0"); #endif if (i -= j) return i; xa0 = a->x; xa = xa0 + j; xb0 = b->x; xb = xb0 + j; for(;;) { if (*--xa != *--xb) return *xa < *xb ? -1 : 1; if (xa <= xa0) break; } return 0; } Bigint * _diff(Bigint *a, Bigint *b) { Bigint *c; int i, wa, wb; long borrow, y; /* We need signed shifts here. */ unsigned long *xa, *xae, *xb, *xbe, *xc; #ifdef Pack_32 long z; #endif i = cmp(a,b); if (!i) { c = Balloc(0); c->wds = 1; c->x[0] = 0; return c; } if (i < 0) { c = a; a = b; b = c; i = 1; } else i = 0; c = Balloc(a->k); c->sign = i; wa = a->wds; xa = a->x; xae = xa + wa; wb = b->wds; xb = b->x; xbe = xb + wb; xc = c->x; borrow = 0; #ifdef Pack_32 do { y = (*xa & 0xffff) - (*xb & 0xffff) + borrow; borrow = y >> 16; Sign_Extend(borrow, y); z = (*xa++ >> 16) - (*xb++ >> 16) + borrow; borrow = z >> 16; Sign_Extend(borrow, z); Storeinc(xc, z, y); } while(xb < xbe); while(xa < xae) { y = (*xa & 0xffff) + borrow; borrow = y >> 16; Sign_Extend(borrow, y); z = (*xa++ >> 16) + borrow; borrow = z >> 16; Sign_Extend(borrow, z); Storeinc(xc, z, y); } #else do { y = *xa++ - *xb++ + borrow; borrow = y >> 16; Sign_Extend(borrow, y); *xc++ = y & 0xffff; } while(xb < xbe); while(xa < xae) { y = *xa++ + borrow; borrow = y >> 16; Sign_Extend(borrow, y); *xc++ = y & 0xffff; } #endif while(!*--xc) wa--; c->wds = wa; return c; } Bigint * _d2b(double darg, int *e, int *bits) { Bigint *b; int de, i, k; unsigned long *x, y, z; Dul d; #ifdef VAX unsigned long d0, d1; d.d = darg; d0 = word0(d) >> 16 | word0(d) << 16; d1 = word1(d) >> 16 | word1(d) << 16; #else d.d = darg; #define d0 word0(d) #define d1 word1(d) #endif #ifdef Pack_32 b = Balloc(1); #else b = Balloc(2); #endif x = b->x; z = d0 & Frac_mask; d0 &= 0x7fffffff; /* clear sign bit, which we ignore */ #ifdef Sudden_Underflow de = (int)(d0 >> Exp_shift); #ifndef IBM z |= Exp_msk11; #endif #else if (de = (int)(d0 >> Exp_shift)) z |= Exp_msk1; #endif #ifdef Pack_32 if (y = d1) { if (k = lo0bits(&y)) { x[0] = y | z << 32 - k; z >>= k; } else x[0] = y; i = b->wds = (x[1] = z) ? 2 : 1; } else { #ifdef DEBUG if (!z) Bug("Zero passed to d2b"); #endif k = lo0bits(&z); x[0] = z; i = b->wds = 1; k += 32; } #else if (y = d1) { if (k = lo0bits(&y)) if (k >= 16) { x[0] = y | z << 32 - k & 0xffff; x[1] = z >> k - 16 & 0xffff; x[2] = z >> k; i = 2; } else { x[0] = y & 0xffff; x[1] = y >> 16 | z << 16 - k & 0xffff; x[2] = z >> k & 0xffff; x[3] = z >> k+16; i = 3; } else { x[0] = y & 0xffff; x[1] = y >> 16; x[2] = z & 0xffff; x[3] = z >> 16; i = 3; } } else { #ifdef DEBUG if (!z) Bug("Zero passed to d2b"); #endif k = lo0bits(&z); if (k >= 16) { x[0] = z; i = 0; } else { x[0] = z & 0xffff; x[1] = z >> 16; i = 1; } k += 32; } while(!x[i]) --i; b->wds = i + 1; #endif #ifndef Sudden_Underflow if (de) { #endif #ifdef IBM *e = (de - Bias - (P-1) << 2) + k; *bits = 4*P + 8 - k - hi0bits(word0(d) & Frac_mask); #else *e = de - Bias - (P-1) + k; *bits = P - k; #endif #ifndef Sudden_Underflow } else { *e = de - Bias - (P-1) + 1 + k; #ifdef Pack_32 *bits = 32*i - hi0bits(x[i-1]); #else *bits = (i+2)*16 - hi0bits(x[i]); #endif } #endif return b; } #undef d0 #undef d1 /sys/src/ape/lib/ap/stdio/atexit.c 664 sys sys 1369841159 230 #include #include extern void (*_atexitfns[ATEXIT_MAX])(void); int atexit(void (*f)(void)) { int i; for(i=0; istate){ case ERR: f->state=f->buf?RDWR:OPEN; break; case END: f->state=RDWR; break; } } /sys/src/ape/lib/ap/stdio/exit.c 664 sys sys 1369841299 343 #include #include void (*_atexitfns[ATEXIT_MAX])(void); void _doatexits(void) { int i; void (*f)(void); for(i = ATEXIT_MAX-1; i >= 0; i--) if(_atexitfns[i]){ f = _atexitfns[i]; _atexitfns[i] = NULL; /* self defense against bozos */ (*f)(); } } void exit(int status) { _doatexits(); _exit(status); } /sys/src/ape/lib/ap/stdio/fclose.c 664 sys sys 1367613437 459 /* * pANS stdio -- fclose */ #include "iolib.h" int fclose(FILE *f){ int d, error=0; char *p; if(!f) return EOF; if(f->state==CLOSED) return EOF; if(fflush(f)==EOF) error=EOF; if(f->flags&BALLOC){ if((p = f->buf)!=0){ f->buf = 0; f->wp = 0; f->rp = 0; f->lp = 0; free(p); } } if(!(f->flags&STRING)){ if((d = f->fd)>=0){ f->fd = -1; if(close(d) < 0) error = EOF; } } f->state=CLOSED; f->flags=0; return error; } /sys/src/ape/lib/ap/stdio/fconv.h 664 sys sys 1367613437 6538 /**************************************************************** The author of this software (_dtoa, strtod) is David M. Gay. Please send bug reports to David M. Gay Bell Laboratories, Room 2C-463 600 Mountain Avenue Murray Hill, NJ 07974-2070 U.S.A. dmg@research.bell-labs.com */ #include #include #define _RESEARCH_SOURCE #include #include #include #define CONST const /* * #define IEEE_8087 for IEEE-arithmetic machines where the least * significant byte has the lowest address. * #define IEEE_MC68k for IEEE-arithmetic machines where the most * significant byte has the lowest address. * #define Sudden_Underflow for IEEE-format machines without gradual * underflow (i.e., that flush to zero on underflow). * #define IBM for IBM mainframe-style floating-point arithmetic. * #define VAX for VAX-style floating-point arithmetic. * #define Unsigned_Shifts if >> does treats its left operand as unsigned. * #define No_leftright to omit left-right logic in fast floating-point * computation of dtoa. * #define Check_FLT_ROUNDS if FLT_ROUNDS can assume the values 2 or 3. * #define RND_PRODQUOT to use rnd_prod and rnd_quot (assembly routines * that use extended-precision instructions to compute rounded * products and quotients) with IBM. * #define ROUND_BIASED for IEEE-format with biased rounding. * #define Inaccurate_Divide for IEEE-format with correctly rounded * products but inaccurate quotients, e.g., for Intel i860. * #define Just_16 to store 16 bits per 32-bit long when doing high-precision * integer arithmetic. Whether this speeds things up or slows things * down depends on the machine and the number being converted. */ #if defined(IEEE_8087) + defined(IEEE_MC68k) + defined(VAX) + defined(IBM) != 1 Exactly one of IEEE_8087, IEEE_MC68k, VAX, or IBM should be defined. #endif typedef union { double d; unsigned long ul[2]; } Dul; #ifdef IEEE_8087 #define word0(x) ((x).ul[1]) #define word1(x) ((x).ul[0]) #else #define word0(x) ((x).ul[0]) #define word1(x) ((x).ul[1]) #endif #ifdef Unsigned_Shifts #define Sign_Extend(a,b) if (b < 0) a |= 0xffff0000; #else #define Sign_Extend(a,b) /*no-op*/ #endif /* The following definition of Storeinc is appropriate for MIPS processors. * An alternative that might be better on some machines is * #define Storeinc(a,b,c) (*a++ = b << 16 | c & 0xffff) */ #if defined(IEEE_8087) + defined(VAX) #define Storeinc(a,b,c) (((unsigned short *)a)[1] = (unsigned short)b, \ ((unsigned short *)a)[0] = (unsigned short)c, a++) #else #define Storeinc(a,b,c) (((unsigned short *)a)[0] = (unsigned short)b, \ ((unsigned short *)a)[1] = (unsigned short)c, a++) #endif /* #define P DBL_MANT_DIG */ /* Ten_pmax = floor(P*log(2)/log(5)) */ /* Bletch = (highest power of 2 < DBL_MAX_10_EXP) / 16 */ /* Quick_max = floor((P-1)*log(FLT_RADIX)/log(10) - 1) */ /* Int_max = floor(P*log(FLT_RADIX)/log(10) - 1) */ #if defined(IEEE_8087) + defined(IEEE_MC68k) #define Exp_shift 20 #define Exp_shift1 20 #define Exp_msk1 0x100000 #define Exp_msk11 0x100000 #define Exp_mask 0x7ff00000 #define P 53 #define Bias 1023 #define IEEE_Arith #define Emin (-1022) #define Exp_1 0x3ff00000 #define Exp_11 0x3ff00000 #define Ebits 11 #define Frac_mask 0xfffff #define Frac_mask1 0xfffff #define Ten_pmax 22 #define Bletch 0x10 #define Bndry_mask 0xfffff #define Bndry_mask1 0xfffff #define LSB 1 #define Sign_bit 0x80000000 #define Log2P 1 #define Tiny0 0 #define Tiny1 1 #define Quick_max 14 #define Int_max 14 #define Infinite(x) (word0(x) == 0x7ff00000) /* sufficient test for here */ #else #undef Sudden_Underflow #define Sudden_Underflow #ifdef IBM #define Exp_shift 24 #define Exp_shift1 24 #define Exp_msk1 0x1000000 #define Exp_msk11 0x1000000 #define Exp_mask 0x7f000000 #define P 14 #define Bias 65 #define Exp_1 0x41000000 #define Exp_11 0x41000000 #define Ebits 8 /* exponent has 7 bits, but 8 is the right value in b2d */ #define Frac_mask 0xffffff #define Frac_mask1 0xffffff #define Bletch 4 #define Ten_pmax 22 #define Bndry_mask 0xefffff #define Bndry_mask1 0xffffff #define LSB 1 #define Sign_bit 0x80000000 #define Log2P 4 #define Tiny0 0x100000 #define Tiny1 0 #define Quick_max 14 #define Int_max 15 #else /* VAX */ #define Exp_shift 23 #define Exp_shift1 7 #define Exp_msk1 0x80 #define Exp_msk11 0x800000 #define Exp_mask 0x7f80 #define P 56 #define Bias 129 #define Exp_1 0x40800000 #define Exp_11 0x4080 #define Ebits 8 #define Frac_mask 0x7fffff #define Frac_mask1 0xffff007f #define Ten_pmax 24 #define Bletch 2 #define Bndry_mask 0xffff007f #define Bndry_mask1 0xffff007f #define LSB 0x10000 #define Sign_bit 0x8000 #define Log2P 1 #define Tiny0 0x80 #define Tiny1 0 #define Quick_max 15 #define Int_max 15 #endif #endif #ifndef IEEE_Arith #define ROUND_BIASED #endif #define Big0 (Frac_mask1 | Exp_msk1*(DBL_MAX_EXP+Bias-1)) #define Big1 0xffffffff #ifndef Just_16 /* When Pack_32 is not defined, we store 16 bits per 32-bit long. * This makes some inner loops simpler and sometimes saves work * during multiplications, but it often seems to make things slightly * slower. Hence the default is now to store 32 bits per long. */ #ifndef Pack_32 #define Pack_32 #endif #endif #define Kmax 15 struct Bigint { struct Bigint *next; int k, maxwds, sign, wds; unsigned long x[1]; }; typedef struct Bigint Bigint; /* This routines shouldn't be visible externally */ extern Bigint *_Balloc(int); extern void _Bfree(Bigint *); extern Bigint *_multadd(Bigint *, int, int); extern int _hi0bits(unsigned long); extern Bigint *_mult(Bigint *, Bigint *); extern Bigint *_pow5mult(Bigint *, int); extern Bigint *_lshift(Bigint *, int); extern int _cmp(Bigint *, Bigint *); extern Bigint *_diff(Bigint *, Bigint *); extern Bigint *_d2b(double, int *, int *); extern Bigint *_i2b(int); extern double _tens[], _bigtens[], _tinytens[]; #ifdef IEEE_Arith #define n_bigtens 5 #else #ifdef IBM #define n_bigtens 3 #else #define n_bigtens 2 #endif #endif #define Balloc(x) _Balloc(x) #define Bfree(x) _Bfree(x) #define Bcopy(x,y) memcpy((char *)&x->sign, (char *)&y->sign, \ y->wds*sizeof(long) + 2*sizeof(int)) #define multadd(x,y,z) _multadd(x,y,z) #define hi0bits(x) _hi0bits(x) #define i2b(x) _i2b(x) #define mult(x,y) _mult(x,y) #define pow5mult(x,y) _pow5mult(x,y) #define lshift(x,y) _lshift(x,y) #define cmp(x,y) _cmp(x,y) #define diff(x,y) _diff(x,y) #define d2b(x,y,z) _d2b(x,y,z) #define tens _tens #define bigtens _bigtens #define tinytens _tinytens /sys/src/ape/lib/ap/stdio/fdopen.c 664 sys sys 1367613437 794 /* * Posix stdio -- fdopen */ #include "iolib.h" /* * Open the named file with the given mode, using the given FILE * Legal modes are given below, `additional characters may follow these sequences': * r rb open to read * w wb open to write, truncating * a ab open to write positioned at eof, creating if non-existant * r+ r+b rb+ open to read and write, creating if non-existant * w+ w+b wb+ open to read and write, truncating * a+ a+b ab+ open to read and write, positioned at eof, creating if non-existant. */ FILE *fdopen(const int fd, const char *mode){ FILE *f; if((f = _IO_newfile()) == NULL) return NULL; f->fd=fd; if(mode[0]=='a') lseek(f->fd, 0L, 2); if(f->fd==-1) return NULL; f->flags=0; f->state=OPEN; f->buf=0; f->rp=0; f->wp=0; f->lp=0; return f; } /sys/src/ape/lib/ap/stdio/feof.c 664 sys sys 1367613437 92 /* * pANS stdio -- feof */ #include "iolib.h" int feof(FILE *f){ return f->state==END; } /sys/src/ape/lib/ap/stdio/ferror.c 664 sys sys 1367613437 96 /* * pANS stdio -- ferror */ #include "iolib.h" int ferror(FILE *f){ return f->state==ERR; } /sys/src/ape/lib/ap/stdio/fflush.c 664 sys sys 1367613437 592 /* * pANS stdio -- fflush */ #include "iolib.h" int fflush(FILE *f){ int error, cnt; if(f==NULL){ error=0; for(f=_IO_stream;f!=&_IO_stream[FOPEN_MAX];f++) if(f->state==WR && fflush(f)==EOF) error=EOF; return error; } if(f->flags&STRING) return EOF; switch(f->state){ default: /* OPEN RDWR EOF RD */ return 0; case CLOSED: case ERR: return EOF; case WR: cnt=(f->flags&LINEBUF?f->lp:f->wp)-f->buf; if(cnt && write(f->fd, f->buf, cnt)!=cnt){ if(f->state != CLOSED) f->state = ERR; return EOF; } f->rp=f->wp=f->buf; f->state=RDWR; return 0; } } /sys/src/ape/lib/ap/stdio/fgetc.c 664 sys sys 1367613437 88 /* * pANS stdio -- fgetc */ #include "iolib.h" int fgetc(FILE *f){ return getc(f); } /sys/src/ape/lib/ap/stdio/fgetpos.c 664 sys sys 1367613437 127 /* * pANS stdio -- fgetpos */ #include "iolib.h" int fgetpos(FILE *f, fpos_t *pos){ *pos=ftell(f); return *pos==-1?-1:0; } /sys/src/ape/lib/ap/stdio/fgets.c 664 sys sys 1367613437 263 /* * pANS stdio -- fgets */ #include "iolib.h" char *fgets(char *as, int n, FILE *f){ int c=0; char *s=as; while(n>1 && (c=getc(f))!=EOF){ *s++=c; --n; if(c=='\n') break; } if(c==EOF && s==as || ferror(f)) return NULL; if(n) *s='\0'; return as; } /sys/src/ape/lib/ap/stdio/fileno.c 664 sys sys 1367613437 122 /* * Posix stdio -- fileno */ #include "iolib.h" int fileno(FILE *f){ if(f==NULL) return -1; else return f->fd; } /sys/src/ape/lib/ap/stdio/fopen.c 664 sys sys 1367613437 192 /* * pANS stdio -- fopen */ #include "iolib.h" FILE *fopen(const char *name, const char *mode){ FILE *f; if((f = _IO_newfile()) == NULL) return NULL; return freopen(name, mode, f); } /sys/src/ape/lib/ap/stdio/fprintf.c 664 sys sys 1367613437 195 /* * pANS stdio -- fprintf */ #include "iolib.h" int fprintf(FILE *f, const char *fmt, ...){ int n; va_list args; va_start(args, fmt); n=vfprintf(f, fmt, args); va_end(args); return n; } /sys/src/ape/lib/ap/stdio/fputc.c 664 sys sys 1367613437 154 /* * pANS stdio -- fputc */ #include "iolib.h" int fputc(int c, FILE *f){ return putc(c, f); /* This can be made more fair to _IOLBF-mode streams */ } /sys/src/ape/lib/ap/stdio/fputs.c 664 sys sys 1367613437 137 /* * pANS stdio -- fputs */ #include "iolib.h" int fputs(const char *s, FILE *f){ while(*s) putc(*s++, f); return ferror(f)?EOF:0; } /sys/src/ape/lib/ap/stdio/fread.c 664 sys sys 1367613437 711 /* * pANS stdio -- fread */ #include "iolib.h" #include #define BIGN (BUFSIZ/2) size_t fread(void *p, size_t recl, size_t nrec, FILE *f){ char *s; int n, d, c; s=(char *)p; n=recl*nrec; while(n>0 && f->state!=CLOSED){ d=f->wp-f->rp; if(d>0){ if(d>n) d=n; memcpy(s, f->rp, d); f->rp+=d; }else{ if(f->buf==f->unbuf || (n >= BIGN && f->state==RD && !(f->flags&STRING))){ d=read(f->fd, s, n); if(d<=0){ if(f->state!=CLOSED) f->state=(d==0)?END:ERR; goto ret; } }else{ c=_IO_getc(f); if(c==EOF) goto ret; *s=c; d=1; } } s+=d; n-=d; } ret: if(recl) return (s-(char*)p)/recl; else return s-(char*)p; } /sys/src/ape/lib/ap/stdio/freopen.c 664 sys sys 1367613437 1517 /* * pANS stdio -- freopen */ #include "iolib.h" /* * Open the named file with the given mode, using the given FILE * Legal modes are given below, `additional characters may follow these sequences': * r rb open to read * w wb open to write, truncating * a ab open to write positioned at eof, creating if non-existant * r+ r+b rb+ open to read and write, creating if non-existant * w+ w+b wb+ open to read and write, truncating * a+ a+b ab+ open to read and write, positioned at eof, creating if non-existant. */ FILE *freopen(const char *name, const char *mode, FILE *f){ int m; if(f->state!=CLOSED){ fclose(f); /* premature; fall through and see what happens */ /* f->state=OPEN; */ } m = *mode++; if(m == 0) return NULL; if(*mode == 'b') mode++; switch(m){ default: return NULL; case 'r': f->fd=open(name, (*mode == '+'? O_RDWR: O_RDONLY)); break; case 'w': f->fd=creat(name, 0666); /* implicitly O_WRONLY */ /* for O_RDWR, have to creat, close, open */ if(*mode == '+' && f->fd >= 0) { close(f->fd); f->fd=open(name, O_RDWR); } break; case 'a': f->fd=open(name, (*mode == '+'? O_RDWR: O_WRONLY)); if(f->fd<0) { f->fd=creat(name, 0666); /* for O_RDWR, have to creat, close, open */ if(*mode == '+' && f->fd >= 0) { close(f->fd); f->fd=open(name, O_RDWR); } } lseek(f->fd, 0L, 2); break; } if(f->fd==-1) return NULL; f->flags=(mode[0]=='a')? APPEND : 0; f->state=OPEN; f->buf=0; f->rp=0; f->wp=0; f->lp=0; return f; } /sys/src/ape/lib/ap/stdio/fscanf.c 664 sys sys 1367613437 192 /* * pANS stdio -- fscanf */ #include "iolib.h" int fscanf(FILE *f, const char *fmt, ...){ int n; va_list args; va_start(args, fmt); n=vfscanf(f, fmt, args); va_end(args); return n; } /sys/src/ape/lib/ap/stdio/fseek.c 664 sys sys 1367613437 411 /* * pANS stdio -- fseek */ #include "iolib.h" int fseek(FILE *f, long offs, int type){ switch(f->state){ case ERR: case CLOSED: return -1; case WR: fflush(f); break; case RD: if(type==1 && f->buf!=f->unbuf) offs-=f->wp-f->rp; break; } if(f->flags&STRING || lseek(f->fd, offs, type)==-1) return -1; if(f->state==RD) f->rp=f->wp=f->buf; if(f->state!=OPEN) f->state=RDWR; return 0; } /sys/src/ape/lib/ap/stdio/fseeko.c 664 sys sys 1367613437 414 /* * pANS stdio -- fseeko */ #include "iolib.h" int fseeko(FILE *f, off_t offs, int type){ switch(f->state){ case ERR: case CLOSED: return -1; case WR: fflush(f); break; case RD: if(type==1 && f->buf!=f->unbuf) offs-=f->wp-f->rp; break; } if(f->flags&STRING || lseek(f->fd, offs, type)==-1) return -1; if(f->state==RD) f->rp=f->wp=f->buf; if(f->state!=OPEN) f->state=RDWR; return 0; } /sys/src/ape/lib/ap/stdio/fsetpos.c 664 sys sys 1367613437 128 /* * pANS stdio -- fsetpos */ #include "iolib.h" int fsetpos(FILE *f, const fpos_t *pos){ return fseek(f, *pos, SEEK_SET); } /sys/src/ape/lib/ap/stdio/ftell.c 664 sys sys 1367613437 307 /* * pANS stdio -- ftell */ #include "iolib.h" long ftell(FILE *f){ long seekp=lseek(f->fd, 0L, 1); if(seekp<0) return -1; /* enter error state? */ switch(f->state){ default: return seekp; case RD: return seekp-(f->wp-f->rp); case WR: return (f->flags&LINEBUF?f->lp:f->wp)-f->buf+seekp; } } /sys/src/ape/lib/ap/stdio/ftello.c 664 sys sys 1367613437 311 /* * pANS stdio -- ftello */ #include "iolib.h" off_t ftello(FILE *f){ off_t seekp=lseek(f->fd, 0L, 1); if(seekp<0) return -1; /* enter error state? */ switch(f->state){ default: return seekp; case RD: return seekp-(f->wp-f->rp); case WR: return (f->flags&LINEBUF?f->lp:f->wp)-f->buf+seekp; } } /sys/src/ape/lib/ap/stdio/ftoa.c 664 sys sys 1367613437 951 #include #include double pow10(int); #define NDIG 18 #define NFTOA (NDIG+4) /* * convert floating to ascii. ftoa returns an integer e such that * f=g*10**e, with .1<=|g|<1 (e=0 when g==0) and puts an ascii * representation of g in the buffer pointed to by bp. bp[0] will * be '+' or '-', and bp[1] to bp[NFTOA-2] * will be appropriate digits of g. bp[NFTOA-1] will be '\0' */ int ftoa(double f, char *bp){ int e, e1, e2, i; double digit, g, p; if(f>=0) *bp++='+'; else{ f=-f; *bp++='-'; } /* find e such that f==0 or 1<=f*pow10(e)<10, and set f=f*pow10(e) */ if(f==0.) e=1; else{ frexp(f, &e); e=-e*30103/100000; /* split in 2 pieces to guard against overflow in extreme cases */ e1=e/2; e2=e-e1; p=f*pow10(e2); while((g=p*pow10(e1))<1.) e1++; while((g=p*pow10(e1))>=10.) --e1; e=e1+e2; f=g; } for(i=0;i!=NDIG;i++){ f=modf(f, &digit)*10.; *bp++=digit+'0'; } *bp='\0'; return 1-e; } /sys/src/ape/lib/ap/stdio/fwrite.c 664 sys sys 1367613437 986 /* * pANS stdio -- fwrite */ #include "iolib.h" #include #define BIGN (BUFSIZ/2) size_t fwrite(const void *p, size_t recl, size_t nrec, FILE *f){ char *s; int n, d; s=(char *)p; n=recl*nrec; while(n>0 && f->state!=CLOSED){ d=f->rp-f->wp; if(d>0){ if(d>n) d=n; memcpy(f->wp, s, d); f->wp+=d; }else{ if(f->buf==f->unbuf || (n>=BIGN && f->state==WR && !(f->flags&(STRING|LINEBUF)))){ d=f->wp-f->buf; if(d>0){ if(f->flags&APPEND) lseek(f->fd, 0L, SEEK_END); if(write(f->fd, f->buf, d)!=d){ if(f->state!=CLOSED) f->state=ERR; goto ret; } f->wp=f->rp=f->buf; } if(f->flags&APPEND) lseek(f->fd, 0L, SEEK_END); d=write(f->fd, s, n); if(d<=0){ if(f->state!=CLOSED) f->state=ERR; goto ret; } }else{ if(_IO_putc(*s, f)==EOF) goto ret; d=1; } } s+=d; n-=d; } ret: if(recl) return (s-(char*)p)/recl; else return s-(char*)p; } /sys/src/ape/lib/ap/stdio/getc.c 664 sys sys 1367613437 99 /* * pANS stdio -- getc */ #include "iolib.h" #undef getc int getc(FILE *f){ return fgetc(f); } /sys/src/ape/lib/ap/stdio/getchar.c 664 sys sys 1367613437 109 /* * pANS stdio -- getchar */ #include "iolib.h" #undef getchar int getchar(void){ return fgetc(stdin); } /sys/src/ape/lib/ap/stdio/gets.c 664 sys sys 1367613437 261 /* * pANS stdio -- gets */ #include "iolib.h" char *gets(char *as){ #ifdef secure stdin->flags|=ERR; return NULL; #else char *s=as; int c; while((c=getchar())!='\n' && c!=EOF) *s++=c; if(c!=EOF || s!=as) *s='\0'; else return NULL; return as; #endif } /sys/src/ape/lib/ap/stdio/iolib.h 664 sys sys 1367613437 1467 /* * pANS stdio -- definitions * The following names are defined in the pANS: * FILE fpos_t _IOFBF _IOLBF _IONBF * BUFSIZ EOF FOPEN_MAX FILENAME_MAX L_tmpnam * SEEK_CUR SEEK_END SEEK_SET TMP_MAX stderr * stdin stdout remove rename tmpfile * tmpnam fclose fflush fopen freopen * setbuf setvbuf fprintf fscanf printf * scanf sprintf sscanf vfprintf vprintf * vsprintf fgetc fgets fputc fputs * getc getchar gets putc putchar * puts ungetc fread fwrite fgetpos * fseek fsetpos ftell rewind clearerr * feof ferror perror */ #include #include #include #include #include /* * Flag bits */ #define BALLOC 1 /* did stdio malloc fd->buf? */ #define LINEBUF 2 /* is stream line buffered? */ #define STRING 4 /* output to string, instead of file */ #define APPEND 8 /* append mode output */ /* * States */ #define CLOSED 0 /* file not open */ #define OPEN 1 /* file open, but no I/O buffer allocated yet */ #define RDWR 2 /* open, buffer allocated, ok to read or write */ #define RD 3 /* open, buffer allocated, ok to read but not write */ #define WR 4 /* open, buffer allocated, ok to write but not read */ #define ERR 5 /* open, but an uncleared error occurred */ #define END 6 /* open, but at eof */ char *strerror(int errno); int _IO_setvbuf(FILE *); FILE *_IO_sopenr(const char*); FILE *_IO_sopenw(void); char *_IO_sclose(FILE *); FILE *_IO_newfile(void); /sys/src/ape/lib/ap/stdio/mkfile 664 sys sys 1369841346 893 APE=/sys/src/ape <$APE/config LIB=/$objtype/lib/ape/libap.a OFILES=\ _IO_newfile.$O\ _IO_getc.$O\ _IO_putc.$O\ _dtoa.$O\ _fconv.$O\ clearerr.$O\ atexit.$O\ exit.$O\ fclose.$O\ fdopen.$O\ feof.$O\ ferror.$O\ fflush.$O\ fgetc.$O\ fgetpos.$O\ fgets.$O\ fileno.$O\ fopen.$O\ fprintf.$O\ fputc.$O\ fputs.$O\ fread.$O\ freopen.$O\ fscanf.$O\ fseek.$O\ fseeko.$O\ fsetpos.$O\ ftell.$O\ ftello.$O\ ftoa.$O\ fwrite.$O\ getc.$O\ getchar.$O\ gets.$O\ perror.$O\ pow10.$O\ printf.$O\ putc.$O\ putchar.$O\ puts.$O\ remove.$O\ rewind.$O\ scanf.$O\ sclose.$O\ setbuf.$O\ setvbuf.$O\ snprintf.$O\ sopenr.$O\ sopenw.$O\ sprintf.$O\ sscanf.$O\ stdio.$O\ strerror.$O\ strtod.$O\ tmpnam.$O\ ungetc.$O\ vfprintf.$O\ vfscanf.$O\ vprintf.$O\ vsprintf.$O\ vsnprintf.$O\ #include #include static long tab[] = { 1L, 10L, 100L, 1000L, 10000L, 100000L, 1000000L, 10000000L }; double pow10(int n) { int m; if(n > DBL_MAX_10_EXP){ errno = ERANGE; return HUGE_VAL; } if(n < DBL_MIN_10_EXP){ errno = ERANGE; return 0.0; } if(n < 0) return 1/pow10(-n); if(n < sizeof(tab)/sizeof(tab[0])) return tab[n]; m = n/2; return pow10(m) * pow10(n-m); } /sys/src/ape/lib/ap/stdio/printf.c 664 sys sys 1367613437 189 /* * pANS stdio -- printf */ #include "iolib.h" int printf(const char *fmt, ...){ int n; va_list args; va_start(args, fmt); n=vfprintf(stdout, fmt, args); va_end(args); return n; } /sys/src/ape/lib/ap/stdio/putc.c 664 sys sys 1367613437 109 /* * pANS stdio -- putc */ #include "iolib.h" #undef putc int putc(int c, FILE *f){ return fputc(c, f); } /sys/src/ape/lib/ap/stdio/putchar.c 664 sys sys 1367613437 114 /* * pANS stdio -- getchar */ #include "iolib.h" #undef putchar int putchar(int c){ return fputc(c, stdout); } /sys/src/ape/lib/ap/stdio/puts.c 664 sys sys 1367613437 139 /* * pANS stdio -- puts */ #include "iolib.h" int puts(const char *s){ fputs(s, stdout); putchar('\n'); return ferror(stdin)?EOF:0; } /sys/src/ape/lib/ap/stdio/rdline.c 664 sys sys 1367613437 1253 /* * pANS stdio -- rdline * This is not a pANS routine. */ #include "iolib.h" #include char *rdline(FILE *f, char **ep){ int cnt; char *nlp, *vp; switch(f->state){ default: /* CLOSED, WR, ERR, EOF */ return NULL; case OPEN: if(_IO_setvbuf(f)!=0) return NULL; case RDWR: f->state=RD; case RD: if(f->bufl==0){ /* Called by a comedian! */ f->state=ERR; return NULL; } vp=f->rp; for(;;){ /* * Look for a newline. * If none found, slide the partial line to the beginning * of the buffer, read some more and keep looking. */ nlp=memchr(f->rp, '\n', f->wp-f->rp); if(nlp!=0) break; if(f->flags&STRING){ f->rp=f->wp; if(ep) *ep=f->wp; return vp; } if(f->rp!=f->buf){ memmove(f->buf, f->rp, f->wp-f->rp); f->wp-=f->rp-f->buf; f->rp=f->buf; vp=f->rp; } cnt=f->bufl-(f->wp-f->buf); if(cnt==0){ /* no room left */ nlp=f->wp-1; break; } cnt=read(f->fd, f->wp, cnt); if(f->state==CLOSED) return NULL; if(cnt==-1){ f->state=ERR; return NULL; } if(cnt==0){ /* is this ok? */ f->state=EOF; return NULL; } f->rp=f->wp; f->wp+=cnt; } *nlp='\0'; f->rp=nlp+1; if(ep) *ep=nlp; return vp; } } /sys/src/ape/lib/ap/stdio/remove.c 664 sys sys 1367613437 106 /* * pANS stdio -- remove */ #include "iolib.h" int remove(const char *f){ return unlink((char *)f); } /sys/src/ape/lib/ap/stdio/rename.c 664 sys sys 1367613437 225 /* * pANS stdio -- rename */ #include "iolib.h" int rename(const char *old, const char *new){ if(link((char *)old, (char *)new)<0) return -1; if(unlink((char *)old)<0){ unlink((char *)new); return -1; } return 0; } /sys/src/ape/lib/ap/stdio/rewind.c 664 sys sys 1367613437 99 /* * pANS stdio -- rewind */ #include "iolib.h" void rewind(FILE *f){ fseek(f, 0L, SEEK_SET); } /sys/src/ape/lib/ap/stdio/scanf.c 664 sys sys 1367613437 185 /* * pANS stdio -- scanf */ #include "iolib.h" int scanf(const char *fmt, ...){ int n; va_list args; va_start(args, fmt); n=vfscanf(stdin, fmt, args); va_end(args); return n; } /sys/src/ape/lib/ap/stdio/sclose.c 664 sys sys 1367613437 575 /* * pANS stdio -- sclose */ #include "iolib.h" #include char *_IO_sclose(FILE *f){ switch(f->state){ default: /* ERR CLOSED */ if(f->buf && f->flags&BALLOC) free(f->buf); f->state=CLOSED; f->flags=0; return NULL; case OPEN: f->buf=malloc(1); f->buf[0]='\0'; break; case RD: case END: f->flags=0; break; case RDWR: case WR: if(f->wp==f->rp){ if(f->flags&BALLOC) f->buf=realloc(f->buf, f->bufl+1); if(f->buf==NULL) return NULL; } *f->wp='\0'; f->flags=0; break; } f->state=CLOSED; f->flags=0; return f->buf; } /sys/src/ape/lib/ap/stdio/setbuf.c 664 sys sys 1367613437 281 /* * pANS stdio -- setbuf */ #include "iolib.h" void setbuf(FILE *f, char *buf){ if(f->state==OPEN){ if(buf) f->bufl=BUFSIZ; else{ buf=f->unbuf; f->bufl=0; } f->rp=f->wp=f->lp=f->buf=buf; f->state=RDWR; } /* else error, but there's no way to report it */ } /sys/src/ape/lib/ap/stdio/setvbuf.c 664 sys sys 1367613437 679 /* * pANS stdio -- setvbuf */ #include "iolib.h" #include int setvbuf(FILE *f, char *buf, int mode, size_t size){ if(f->state!=OPEN){ f->state=ERR; return -1; } f->state=RDWR; switch(mode){ case _IOLBF: f->flags|=LINEBUF; case _IOFBF: if(buf==0){ buf=malloc(size); if(buf==0){ f->state=ERR; return -1; } f->flags|=BALLOC; } f->bufl=size; break; case _IONBF: buf=f->unbuf; f->bufl=0; break; } f->rp=f->wp=f->lp=f->buf=buf; f->state=RDWR; return 0; } int _IO_setvbuf(FILE *f){ if(f==stderr || (f==stdout && isatty(1))) return setvbuf(f, (char *)0, _IOLBF, BUFSIZ); return setvbuf(f, (char *)0, _IOFBF, BUFSIZ); } /sys/src/ape/lib/ap/stdio/snprintf.c 664 sys sys 1367613437 341 /* * pANS stdio -- sprintf */ #define _C99_SNPRINTF_EXTENSION #include "iolib.h" int snprintf(char *buf, size_t nbuf, const char *fmt, ...){ int n; va_list args; FILE *f=_IO_sopenw(); if(f==NULL) return 0; setvbuf(f, buf, _IOFBF, nbuf); va_start(args, fmt); n=vfprintf(f, fmt, args); va_end(args); _IO_sclose(f); return n; } /sys/src/ape/lib/ap/stdio/sopenr.c 664 sys sys 1367613437 316 /* * pANS stdio -- sopenr */ #include "iolib.h" #include FILE *_IO_sopenr(const char *s){ FILE *f; if((f=_IO_newfile())==NULL) return NULL; f->buf=f->rp=(char *)s; /* what an annoyance const is */ f->bufl=strlen(s); f->wp=f->buf+f->bufl; f->state=RD; f->flags=STRING; f->fd=-1; return f; } /sys/src/ape/lib/ap/stdio/sopenw.c 664 sys sys 1367613437 211 /* * pANS stdio -- sopenw */ #include "iolib.h" FILE *_IO_sopenw(void){ FILE *f; if((f=_IO_newfile())==NULL) return NULL; f->buf=f->rp=f->wp=0; f->state=OPEN; f->flags=STRING; f->fd=-1; return f; } /sys/src/ape/lib/ap/stdio/sprintf.c 664 sys sys 1367613437 295 /* * pANS stdio -- sprintf */ #include "iolib.h" int sprintf(char *buf, const char *fmt, ...){ int n; va_list args; FILE *f=_IO_sopenw(); if(f==NULL) return 0; setvbuf(f, buf, _IOFBF, 100000); va_start(args, fmt); n=vfprintf(f, fmt, args); va_end(args); _IO_sclose(f); return n; } /sys/src/ape/lib/ap/stdio/sscanf.c 664 sys sys 1367613437 238 /* * pANS stdio -- sscanf */ #include "iolib.h" int sscanf(const char *s, const char *fmt, ...){ int n; FILE *f=_IO_sopenr(s); va_list args; va_start(args, fmt); n=vfscanf(f, fmt, args); va_end(args); _IO_sclose(f); return n; } /sys/src/ape/lib/ap/stdio/stdio.c 664 sys sys 1367613437 209 /* * pANS stdio -- data */ #include "iolib.h" FILE _IO_stream[]={ /* fd flags state buf rp wp lp bufl unbuf */ 0, 0, OPEN, 0, 0, 0, 0, 0, 0, 1, 0, OPEN, 0, 0, 0, 0, 0, 0, 2, 0, OPEN, 0, 0, 0, 0, 0, 0, }; /sys/src/ape/lib/ap/stdio/strerror.c 664 sys sys 1367613437 2227 /* * pANS stdio -- strerror (not really in stdio) * * Shouldn't really call this sys_errlist or make it * externally visible, but too many programs in X assume it... */ #include #include #include "iolib.h" char *sys_errlist[] = { "Error 0", "Too big", "Access denied", "Try again", "Bad file number", "In use", "No children", "Deadlock", "File exists", "Bad address", "File too large", "Interrupted system call", "Invalid argument", "I/O error", "Is a directory", "Too many open files", "Too many links", "Name too long", "File table overflow", "No such device", "No such file or directory", "Exec format error", "Not enough locks", "Not enough memory", "No space left on device", "No such system call", "Not a directory", "Directory not empty", "Inappropriate ioctl", "No such device or address", "Permission denied", "Broken pipe", "Read-only file system", "Illegal seek", "No such process", "Cross-device link", /* bsd networking software */ "Not a socket", "Protocol not supported", /* EPROTONOSUPPORT, EPROTOTYPE */ /* "Protocol wrong type for socket", /* EPROTOTYPE */ "Connection refused", "Address family not supported", "No buffers", "OP not supported", "Address in use", "Destination address required", "Message size", "Protocol option not supported", "Socket option not supported", "Protocol family not supported", /* EPFNOSUPPORT */ "Address not available", "Network down", "Network unreachable", "Network reset", "Connection aborted", "Connected", "Not connected", "Shut down", "Too many references", "Timed out", "Host down", "Host unreachable", "Unknown error", /* EGREG */ /* These added in 1003.1b-1993 */ "Operation canceled", "Operation in progress" }; #define _IO_nerr (sizeof sys_errlist/sizeof sys_errlist[0]) int sys_nerr = _IO_nerr; extern char _plan9err[]; char * strerror(int n) { if(n == EPLAN9) return _plan9err; if(n >= 0 && n < _IO_nerr) return sys_errlist[n]; if(n == EDOM) return "Domain error"; else if(n == ERANGE) return "Range error"; else return "Unknown error"; } char * strerror_r(int n, char *buf, int len) { strncpy(buf, strerror(n), len); buf[len-1] = 0; return buf; } /sys/src/ape/lib/ap/stdio/strtod.c 664 sys sys 1369841564 14417 #include "fconv.h" /* strtod for IEEE-, VAX-, and IBM-arithmetic machines (dmg). * * This strtod returns a nearest machine number to the input decimal * string (or sets errno to ERANGE). With IEEE arithmetic, ties are * broken by the IEEE round-even rule. Otherwise ties are broken by * biased rounding (add half and chop). * * Inspired loosely by William D. Clinger's paper "How to Read Floating * Point Numbers Accurately" [Proc. ACM SIGPLAN '90, pp. 92-101]. * * Modifications: * * 1. We only require IEEE, IBM, or VAX double-precision * arithmetic (not IEEE double-extended). * 2. We get by with floating-point arithmetic in a case that * Clinger missed -- when we're computing d * 10^n * for a small integer d and the integer n is not too * much larger than 22 (the maximum integer k for which * we can represent 10^k exactly), we may be able to * compute (d*10^k) * 10^(e-k) with just one roundoff. * 3. Rather than a bit-at-a-time adjustment of the binary * result in the hard case, we use floating-point * arithmetic to determine the adjustment to within * one bit; only in really hard cases do we need to * compute a second residual. * 4. Because of 3., we don't need a large table of powers of 10 * for ten-to-e (just some small tables, e.g. of 10^k * for 0 <= k <= 22). */ #ifdef RND_PRODQUOT #define rounded_product(a,b) a = rnd_prod(a, b) #define rounded_quotient(a,b) a = rnd_quot(a, b) extern double rnd_prod(double, double), rnd_quot(double, double); #else #define rounded_product(a,b) a *= b #define rounded_quotient(a,b) a /= b #endif static double ulp(double xarg) { long L; Dul a; Dul x; x.d = xarg; L = (word0(x) & Exp_mask) - (P-1)*Exp_msk1; #ifndef Sudden_Underflow if (L > 0) { #endif #ifdef IBM L |= Exp_msk1 >> 4; #endif word0(a) = L; word1(a) = 0; #ifndef Sudden_Underflow } else { L = -L >> Exp_shift; if (L < Exp_shift) { word0(a) = 0x80000 >> L; word1(a) = 0; } else { word0(a) = 0; L -= Exp_shift; word1(a) = L >= 31 ? 1 : 1 << 31 - L; } } #endif return a.d; } static Bigint * s2b(CONST char *s, int nd0, int nd, unsigned long y9) { Bigint *b; int i, k; long x, y; x = (nd + 8) / 9; for(k = 0, y = 1; x > y; y <<= 1, k++) ; #ifdef Pack_32 b = Balloc(k); b->x[0] = y9; b->wds = 1; #else b = Balloc(k+1); b->x[0] = y9 & 0xffff; b->wds = (b->x[1] = y9 >> 16) ? 2 : 1; #endif i = 9; if (9 < nd0) { s += 9; do b = multadd(b, 10, *s++ - '0'); while(++i < nd0); s++; } else s += 10; for(; i < nd; i++) b = multadd(b, 10, *s++ - '0'); return b; } static double b2d(Bigint *a, int *e) { unsigned long *xa, *xa0, w, y, z; int k; Dul d; #ifdef VAX unsigned long d0, d1; #else #define d0 word0(d) #define d1 word1(d) #endif xa0 = a->x; xa = xa0 + a->wds; y = *--xa; #ifdef DEBUG if (!y) Bug("zero y in b2d"); #endif k = hi0bits(y); *e = 32 - k; #ifdef Pack_32 if (k < Ebits) { d0 = Exp_1 | y >> Ebits - k; w = xa > xa0 ? *--xa : 0; d1 = y << (32-Ebits) + k | w >> Ebits - k; goto ret_d; } z = xa > xa0 ? *--xa : 0; if (k -= Ebits) { d0 = Exp_1 | y << k | z >> 32 - k; y = xa > xa0 ? *--xa : 0; d1 = z << k | y >> 32 - k; } else { d0 = Exp_1 | y; d1 = z; } #else if (k < Ebits + 16) { z = xa > xa0 ? *--xa : 0; d0 = Exp_1 | y << k - Ebits | z >> Ebits + 16 - k; w = xa > xa0 ? *--xa : 0; y = xa > xa0 ? *--xa : 0; d1 = z << k + 16 - Ebits | w << k - Ebits | y >> 16 + Ebits - k; goto ret_d; } z = xa > xa0 ? *--xa : 0; w = xa > xa0 ? *--xa : 0; k -= Ebits + 16; d0 = Exp_1 | y << k + 16 | z << k | w >> 16 - k; y = xa > xa0 ? *--xa : 0; d1 = w << k + 16 | y << k; #endif ret_d: #ifdef VAX word0(d) = d0 >> 16 | d0 << 16; word1(d) = d1 >> 16 | d1 << 16; #else #undef d0 #undef d1 #endif return d.d; } static double ratio(Bigint *a, Bigint *b) { Dul da, db; int k, ka, kb; da.d = b2d(a, &ka); db.d = b2d(b, &kb); #ifdef Pack_32 k = ka - kb + 32*(a->wds - b->wds); #else k = ka - kb + 16*(a->wds - b->wds); #endif #ifdef IBM if (k > 0) { word0(da) += (k >> 2)*Exp_msk1; if (k &= 3) da *= 1 << k; } else { k = -k; word0(db) += (k >> 2)*Exp_msk1; if (k &= 3) db *= 1 << k; } #else if (k > 0) word0(da) += k*Exp_msk1; else { k = -k; word0(db) += k*Exp_msk1; } #endif return da.d / db.d; } double strtod(CONST char *s00, char **se) { int bb2, bb5, bbe, bd2, bd5, bbbits, bs2, c, dsign, e, e1, esign, i, j, k, nd, nd0, nf, nz, nz0, sign; CONST char *s, *s0, *s1; double aadj, aadj1, adj; Dul rv, rv0; long L; unsigned long y, z; Bigint *bb, *bb1, *bd, *bd0, *bs, *delta; sign = nz0 = nz = 0; rv.d = 0.; for(s = s00;;s++) switch(*s) { case '-': sign = 1; /* no break */ case '+': if (*++s) goto break2; /* no break */ case 0: s = s00; goto ret; case '\t': case '\n': case '\v': case '\f': case '\r': case ' ': continue; default: goto break2; } break2: if (*s == '0') { nz0 = 1; while(*++s == '0') ; if (!*s) goto ret; } s0 = s; y = z = 0; for(nd = nf = 0; (c = *s) >= '0' && c <= '9'; nd++, s++) if (nd < 9) y = 10*y + c - '0'; else if (nd < 16) z = 10*z + c - '0'; nd0 = nd; if (c == '.') { c = *++s; if (!nd) { for(; c == '0'; c = *++s) nz++; if (c > '0' && c <= '9') { s0 = s; nf += nz; nz = 0; goto have_dig; } goto dig_done; } for(; c >= '0' && c <= '9'; c = *++s) { have_dig: nz++; if (c -= '0') { nf += nz; for(i = 1; i < nz; i++) if (nd++ < 9) y *= 10; else if (nd <= DBL_DIG + 1) z *= 10; if (nd++ < 9) y = 10*y + c; else if (nd <= DBL_DIG + 1) z = 10*z + c; nz = 0; } } } dig_done: e = 0; if (c == 'e' || c == 'E') { if (!nd && !nz && !nz0) { s = s00; goto ret; } s00 = s; esign = 0; switch(c = *++s) { case '-': esign = 1; case '+': c = *++s; } if (c >= '0' && c <= '9') { while(c == '0') c = *++s; if (c > '0' && c <= '9') { e = c - '0'; s1 = s; while((c = *++s) >= '0' && c <= '9') e = 10*e + c - '0'; if (s - s1 > 8) /* Avoid confusion from exponents * so large that e might overflow. */ e = 9999999; if (esign) e = -e; } else e = 0; } else s = s00; } if (!nd) { if (!nz && !nz0) s = s00; goto ret; } e1 = e -= nf; /* Now we have nd0 digits, starting at s0, followed by a * decimal point, followed by nd-nd0 digits. The number we're * after is the integer represented by those digits times * 10**e */ if (!nd0) nd0 = nd; k = nd < DBL_DIG + 1 ? nd : DBL_DIG + 1; rv.d = y; if (k > 9) rv.d = tens[k - 9] * rv.d + z; bd0 = 0; if (nd <= DBL_DIG #ifndef RND_PRODQUOT && FLT_ROUNDS == 1 #endif ) { if (!e) goto ret; if (e > 0) { if (e <= Ten_pmax) { #ifdef VAX goto vax_ovfl_check; #else /* rv = */ rounded_product(rv.d, tens[e]); goto ret; #endif } i = DBL_DIG - nd; if (e <= Ten_pmax + i) { /* A fancier test would sometimes let us do * this for larger i values. */ e -= i; rv.d *= tens[i]; #ifdef VAX /* VAX exponent range is so narrow we must * worry about overflow here... */ vax_ovfl_check: word0(rv) -= P*Exp_msk1; /* rv = */ rounded_product(rv.d, tens[e]); if ((word0(rv) & Exp_mask) > Exp_msk1*(DBL_MAX_EXP+Bias-1-P)) goto ovfl; word0(rv) += P*Exp_msk1; #else /* rv = */ rounded_product(rv.d, tens[e]); #endif goto ret; } } else if (e >= -Ten_pmax) { /* rv = */ rounded_quotient(rv.d, tens[-e]); goto ret; } } e1 += nd - k; /* Get starting approximation = rv * 10**e1 */ if (e1 > 0) { if (nd0 + e1 - 1 > DBL_MAX_10_EXP) goto ovfl; if (i = e1 & 15) rv.d *= tens[i]; if (e1 &= ~15) { if (e1 > DBL_MAX_10_EXP) { ovfl: errno = ERANGE; rv.d = HUGE_VAL; if (bd0) goto retfree; goto ret; } if (e1 >>= 4) { for(j = 0; e1 > 1; j++, e1 >>= 1) if (e1 & 1) rv.d *= bigtens[j]; /* The last multiplication could overflow. */ word0(rv) -= P*Exp_msk1; rv.d *= bigtens[j]; if ((z = word0(rv) & Exp_mask) > Exp_msk1*(DBL_MAX_EXP+Bias-P)) goto ovfl; if (z > Exp_msk1*(DBL_MAX_EXP+Bias-1-P)) { /* set to largest number */ /* (Can't trust DBL_MAX) */ word0(rv) = Big0; word1(rv) = Big1; } else word0(rv) += P*Exp_msk1; } } } else if (e1 < 0) { e1 = -e1; if (i = e1 & 15) rv.d /= tens[i]; if (e1 &= ~15) { e1 >>= 4; if (e1 >= 1 << n_bigtens) goto undfl; for(j = 0; e1 > 1; j++, e1 >>= 1) if (e1 & 1) rv.d *= tinytens[j]; /* The last multiplication could underflow. */ rv0.d = rv.d; rv.d *= tinytens[j]; if (rv.d == 0) { rv.d = 2.*rv0.d; rv.d *= tinytens[j]; if (rv.d == 0) { undfl: rv.d = 0.; errno = ERANGE; if (bd0) goto retfree; goto ret; } word0(rv) = Tiny0; word1(rv) = Tiny1; /* The refinement below will clean * this approximation up. */ } } } /* Now the hard part -- adjusting rv to the correct value.*/ /* Put digits into bd: true value = bd * 10^e */ bd0 = s2b(s0, nd0, nd, y); for(;;) { bd = Balloc(bd0->k); Bcopy(bd, bd0); bb = d2b(rv.d, &bbe, &bbbits); /* rv = bb * 2^bbe */ bs = i2b(1); if (e >= 0) { bb2 = bb5 = 0; bd2 = bd5 = e; } else { bb2 = bb5 = -e; bd2 = bd5 = 0; } if (bbe >= 0) bb2 += bbe; else bd2 -= bbe; bs2 = bb2; #ifdef Sudden_Underflow #ifdef IBM j = 1 + 4*P - 3 - bbbits + ((bbe + bbbits - 1) & 3); #else j = P + 1 - bbbits; #endif #else i = bbe + bbbits - 1; /* logb(rv) */ if (i < Emin) /* denormal */ j = bbe + (P-Emin); else j = P + 1 - bbbits; #endif bb2 += j; bd2 += j; i = bb2 < bd2 ? bb2 : bd2; if (i > bs2) i = bs2; if (i > 0) { bb2 -= i; bd2 -= i; bs2 -= i; } if (bb5 > 0) { bs = pow5mult(bs, bb5); bb1 = mult(bs, bb); Bfree(bb); bb = bb1; } if (bb2 > 0) bb = lshift(bb, bb2); if (bd5 > 0) bd = pow5mult(bd, bd5); if (bd2 > 0) bd = lshift(bd, bd2); if (bs2 > 0) bs = lshift(bs, bs2); delta = diff(bb, bd); dsign = delta->sign; delta->sign = 0; i = cmp(delta, bs); if (i < 0) { /* Error is less than half an ulp -- check for * special case of mantissa a power of two. */ if (dsign || word1(rv) || word0(rv) & Bndry_mask) break; delta = lshift(delta,Log2P); if (cmp(delta, bs) > 0) goto drop_down; break; } if (i == 0) { /* exactly half-way between */ if (dsign) { if ((word0(rv) & Bndry_mask1) == Bndry_mask1 && word1(rv) == 0xffffffff) { /*boundary case -- increment exponent*/ word0(rv) = (word0(rv) & Exp_mask) + Exp_msk1 #ifdef IBM | Exp_msk1 >> 4 #endif ; word1(rv) = 0; break; } } else if (!(word0(rv) & Bndry_mask) && !word1(rv)) { drop_down: /* boundary case -- decrement exponent */ #ifdef Sudden_Underflow L = word0(rv) & Exp_mask; #ifdef IBM if (L < Exp_msk1) #else if (L <= Exp_msk1) #endif goto undfl; L -= Exp_msk1; #else L = (word0(rv) & Exp_mask) - Exp_msk1; #endif word0(rv) = L | Bndry_mask1; word1(rv) = 0xffffffff; #ifdef IBM goto cont; #else break; #endif } #ifndef ROUND_BIASED if (!(word1(rv) & LSB)) break; #endif if (dsign) rv.d += ulp(rv.d); #ifndef ROUND_BIASED else { rv.d -= ulp(rv.d); #ifndef Sudden_Underflow if (rv.d == 0) goto undfl; #endif } #endif break; } if ((aadj = ratio(delta, bs)) <= 2.) { if (dsign) aadj = aadj1 = 1.; else if (word1(rv) || word0(rv) & Bndry_mask) { #ifndef Sudden_Underflow if (word1(rv) == Tiny1 && !word0(rv)) goto undfl; #endif aadj = 1.; aadj1 = -1.; } else { /* special case -- power of FLT_RADIX to be */ /* rounded down... */ if (aadj < 2./FLT_RADIX) aadj = 1./FLT_RADIX; else aadj *= 0.5; aadj1 = -aadj; } } else { aadj *= 0.5; aadj1 = dsign ? aadj : -aadj; #ifdef Check_FLT_ROUNDS switch(FLT_ROUNDS) { case 2: /* towards +infinity */ aadj1 -= 0.5; break; case 0: /* towards 0 */ case 3: /* towards -infinity */ aadj1 += 0.5; } #else if (FLT_ROUNDS == 0) aadj1 += 0.5; #endif } y = word0(rv) & Exp_mask; /* Check for overflow */ if (y == Exp_msk1*(DBL_MAX_EXP+Bias-1)) { rv0.d = rv.d; word0(rv) -= P*Exp_msk1; adj = aadj1 * ulp(rv.d); rv.d += adj; if ((word0(rv) & Exp_mask) >= Exp_msk1*(DBL_MAX_EXP+Bias-P)) { if (word0(rv0) == Big0 && word1(rv0) == Big1) goto ovfl; word0(rv) = Big0; word1(rv) = Big1; goto cont; } else word0(rv) += P*Exp_msk1; } else { #ifdef Sudden_Underflow if ((word0(rv) & Exp_mask) <= P*Exp_msk1) { rv0.d = rv.d; word0(rv) += P*Exp_msk1; adj = aadj1 * ulp(rv.d); rv.d += adj; #ifdef IBM if ((word0(rv) & Exp_mask) < P*Exp_msk1) #else if ((word0(rv) & Exp_mask) <= P*Exp_msk1) #endif { if (word0(rv0) == Tiny0 && word1(rv0) == Tiny1) goto undfl; word0(rv) = Tiny0; word1(rv) = Tiny1; goto cont; } else word0(rv) -= P*Exp_msk1; } else { adj = aadj1 * ulp(rv.d); rv.d += adj; } #else /* Compute adj so that the IEEE rounding rules will * correctly round rv + adj in some half-way cases. * If rv * ulp(rv) is denormalized (i.e., * y <= (P-1)*Exp_msk1), we must adjust aadj to avoid * trouble from bits lost to denormalization; * example: 1.2e-307 . */ if (y <= (P-1)*Exp_msk1 && aadj >= 1.) { aadj1 = (double)(int)(aadj + 0.5); if (!dsign) aadj1 = -aadj1; } adj = aadj1 * ulp(rv.d); rv.d += adj; #endif } z = word0(rv) & Exp_mask; if (y == z) { /* Can we stop now? */ L = aadj; aadj -= L; /* The tolerances below are conservative. */ if (dsign || word1(rv) || word0(rv) & Bndry_mask) { if (aadj < .4999999 || aadj > .5000001) break; } else if (aadj < .4999999/FLT_RADIX) break; } cont: Bfree(bb); Bfree(bd); Bfree(bs); Bfree(delta); } retfree: Bfree(bb); Bfree(bd); Bfree(bs); Bfree(bd0); Bfree(delta); ret: if (se) *se = (char *)s; return sign ? -rv.d : rv.d; } /sys/src/ape/lib/ap/stdio/tmpfile.c 664 sys sys 1367613437 706 /* * This file not used on plan9: see ../plan9/tmpfile.c */ /* * pANS stdio -- tmpfile * * Bug: contains a critical section. Two executions by the same * user could interleave as follows, both yielding the same file: * access fails * access fails * fopen succeeds * fopen succeeds * unlink succeeds * unlink fails * As I read the pANS, this can't reasonably use tmpnam to generate * the name, so that code is duplicated. */ #include "iolib.h" FILE *tmpfile(void){ FILE *f; static char name[]="/tmp/tf000000000000"; char *p; while(access(name, 0)==0){ p=name+7; while(*p=='9') *p++='0'; if(*p=='\0') return NULL; ++*p; } f=fopen(name, "wb+"); unlink(name); return f; } /sys/src/ape/lib/ap/stdio/tmpnam.c 664 sys sys 1367613437 405 /* * pANS stdio -- tmpnam */ #include "iolib.h" #include char * tmpnam(char *s) { static char name[] = "/tmp/tn000000000000"; char *p; do { p = name + 7; while (*p == '9') *p++ = '0'; if (*p == '\0') return NULL; ++*p; } while (access(name, 0) == 0); if (s) { strcpy(s, name); return s; } return name; } char * tmpnam_r(char *s) { return s ? tmpnam(s) : NULL; } /sys/src/ape/lib/ap/stdio/ungetc.c 664 sys sys 1367613437 491 /* * pANS stdio -- ungetc */ #include "iolib.h" int ungetc(int c, FILE *f){ if(c==EOF) return EOF; switch(f->state){ default: /* WR */ f->state=ERR; return EOF; case CLOSED: case ERR: return EOF; case OPEN: _IO_setvbuf(f); case RDWR: case END: f->wp=f->buf; if(f->bufl==0) f->wp += 1; else f->wp += f->bufl; f->rp = f->wp; f->state=RD; case RD: if(f->rp==f->buf) return EOF; if(f->flags&STRING) f->rp--; else *--f->rp=c; return (char)c; } } /sys/src/ape/lib/ap/stdio/vfprintf.c 664 sys sys 1369166818 14770 /* * pANS stdio -- vfprintf */ #include "iolib.h" #include #include #include #include #define _SUSV2_SOURCE #include /* * Leading flags */ #define SPACE 1 /* ' ' prepend space if no sign printed */ #define ALT 2 /* '#' use alternate conversion */ #define SIGN 4 /* '+' prepend sign, even if positive */ #define LEFT 8 /* '-' left-justify */ #define ZPAD 16 /* '0' zero-pad */ /* * Trailing flags */ #define SHORT 32 /* 'h' convert a short integer */ #define LONG 64 /* 'l' convert a long integer */ #define LDBL 128 /* 'L' convert a long double */ #define PTR 256 /* convert a void * (%p) */ #define VLONG 512 /* 'll' convert a long long integer */ static int lflag[] = { /* leading flags */ 0, 0, 0, 0, 0, 0, 0, 0, /* ^@ ^A ^B ^C ^D ^E ^F ^G */ 0, 0, 0, 0, 0, 0, 0, 0, /* ^H ^I ^J ^K ^L ^M ^N ^O */ 0, 0, 0, 0, 0, 0, 0, 0, /* ^P ^Q ^R ^S ^T ^U ^V ^W */ 0, 0, 0, 0, 0, 0, 0, 0, /* ^X ^Y ^Z ^[ ^\ ^] ^^ ^_ */ SPACE, 0, 0, ALT, 0, 0, 0, 0, /* sp ! " # $ % & ' */ 0, 0, 0, SIGN, 0, LEFT, 0, 0, /* ( ) * + , - . / */ ZPAD, 0, 0, 0, 0, 0, 0, 0, /* 0 1 2 3 4 5 6 7 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 8 9 : ; < = > ? */ 0, 0, 0, 0, 0, 0, 0, 0, /* @ A B C D E F G */ 0, 0, 0, 0, 0, 0, 0, 0, /* H I J K L M N O */ 0, 0, 0, 0, 0, 0, 0, 0, /* P Q R S T U V W */ 0, 0, 0, 0, 0, 0, 0, 0, /* X Y Z [ \ ] ^ _ */ 0, 0, 0, 0, 0, 0, 0, 0, /* ` a b c d e f g */ 0, 0, 0, 0, 0, 0, 0, 0, /* h i j k l m n o */ 0, 0, 0, 0, 0, 0, 0, 0, /* p q r s t u v w */ 0, 0, 0, 0, 0, 0, 0, 0, /* x y z { | } ~ ^? */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; static int tflag[] = { /* trailing flags */ 0, 0, 0, 0, 0, 0, 0, 0, /* ^@ ^A ^B ^C ^D ^E ^F ^G */ 0, 0, 0, 0, 0, 0, 0, 0, /* ^H ^I ^J ^K ^L ^M ^N ^O */ 0, 0, 0, 0, 0, 0, 0, 0, /* ^P ^Q ^R ^S ^T ^U ^V ^W */ 0, 0, 0, 0, 0, 0, 0, 0, /* ^X ^Y ^Z ^[ ^\ ^] ^^ ^_ */ 0, 0, 0, 0, 0, 0, 0, 0, /* sp ! " # $ % & ' */ 0, 0, 0, 0, 0, 0, 0, 0, /* ( ) * + , - . / */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0 1 2 3 4 5 6 7 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 8 9 : ; < = > ? */ 0, 0, 0, 0, 0, 0, 0, 0, /* @ A B C D E F G */ 0, 0, 0, 0, LDBL, 0, 0, 0, /* H I J K L M N O */ 0, 0, 0, 0, 0, 0, 0, 0, /* P Q R S T U V W */ 0, 0, 0, 0, 0, 0, 0, 0, /* X Y Z [ \ ] ^ _ */ 0, 0, 0, 0, 0, 0, 0, 0, /* ` a b c d e f g */ SHORT, 0, 0, 0, LONG, 0, 0, 0, /* h i j k l m n o */ 0, 0, 0, 0, 0, 0, 0, 0, /* p q r s t u v w */ 0, 0, 0, 0, 0, 0, 0, 0, /* x y z { | } ~ ^? */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; static int ocvt_E(FILE *, va_list *, int, int, int); static int ocvt_G(FILE *, va_list *, int, int, int); static int ocvt_X(FILE *, va_list *, int, int, int); static int ocvt_c(FILE *, va_list *, int, int, int); static int ocvt_d(FILE *, va_list *, int, int, int); static int ocvt_e(FILE *, va_list *, int, int, int); static int ocvt_f(FILE *, va_list *, int, int, int); static int ocvt_g(FILE *, va_list *, int, int, int); static int ocvt_n(FILE *, va_list *, int, int, int); static int ocvt_o(FILE *, va_list *, int, int, int); static int ocvt_p(FILE *, va_list *, int, int, int); static int ocvt_s(FILE *, va_list *, int, int, int); static int ocvt_u(FILE *, va_list *, int, int, int); static int ocvt_x(FILE *, va_list *, int, int, int); static int(*ocvt[])(FILE *, va_list *, int, int, int) = { 0, 0, 0, 0, 0, 0, 0, 0, /* ^@ ^A ^B ^C ^D ^E ^F ^G */ 0, 0, 0, 0, 0, 0, 0, 0, /* ^H ^I ^J ^K ^L ^M ^N ^O */ 0, 0, 0, 0, 0, 0, 0, 0, /* ^P ^Q ^R ^S ^T ^U ^V ^W */ 0, 0, 0, 0, 0, 0, 0, 0, /* ^X ^Y ^Z ^[ ^\ ^] ^^ ^_ */ 0, 0, 0, 0, 0, 0, 0, 0, /* sp ! " # $ % & ' */ 0, 0, 0, 0, 0, 0, 0, 0, /* ( ) * + , - . / */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0 1 2 3 4 5 6 7 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 8 9 : ; < = > ? */ 0, 0, 0, 0, 0, ocvt_E, 0, ocvt_G, /* @ A B C D E F G */ 0, 0, 0, 0, 0, 0, 0, 0, /* H I J K L M N O */ 0, 0, 0, 0, 0, 0, 0, 0, /* P Q R S T U V W */ ocvt_X, 0, 0, 0, 0, 0, 0, 0, /* X Y Z [ \ ] ^ _ */ 0, 0, 0, ocvt_c, ocvt_d, ocvt_e, ocvt_f, ocvt_g, /* ` a b c d e f g */ 0, ocvt_d, 0, 0, 0, 0, ocvt_n, ocvt_o, /* h i j k l m n o */ ocvt_p, 0, 0, ocvt_s, 0, ocvt_u, 0, 0, /* p q r s t u v w */ ocvt_x, 0, 0, 0, 0, 0, 0, 0, /* x y z { | } ~ ^? */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; static int nprint; int vfprintf(FILE *f, const char *s, va_list args) { int tfl, flags, width, precision; nprint = 0; while(*s){ if(*s != '%'){ putc(*s++, f); nprint++; continue; } s++; flags = 0; while(lflag[*s&_IO_CHMASK]) flags |= lflag[*s++&_IO_CHMASK]; if(*s == '*'){ width = va_arg(args, int); s++; if(width<0){ flags |= LEFT; width = -width; } } else{ width = 0; while('0'<=*s && *s<='9') width = width*10 + *s++ - '0'; } if(*s == '.'){ s++; if(*s == '*'){ precision = va_arg(args, int); s++; } else{ precision = 0; while('0'<=*s && *s<='9') precision = precision*10 + *s++ - '0'; } } else precision = -1; while(tfl = tflag[*s&_IO_CHMASK]){ if(tfl == LONG && (flags & LONG)){ flags &= ~LONG; tfl = VLONG; } flags |= tfl; s++; } if(ocvt[*s]) nprint += (*ocvt[*s++])(f, &args, flags, width, precision); else if(*s){ putc(*s++, f); nprint++; } } return ferror(f)? -1: nprint; } static int ocvt_c(FILE *f, va_list *args, int flags, int width, int precision) { USED(precision); int i; if(!(flags&LEFT)) for(i=1; i= 0) for(i=0; i!=precision && s[i]; i++); else for(i=0; s[i]; i++); for(; i= 0){ for(i=0; i!=precision && *s; i++){ putc(*s++, f); n++; } } else{ for(i=0;*s;i++){ putc(*s++, f); n++; } } if(flags&LEFT){ for(; i 0) digits = _dtoa(d, 2, precision, &exponent, &sign, &edigits); else { digits = _dtoa(d, 0, precision, &exponent, &sign, &edigits); precision = edigits - digits; if (exponent > precision && exponent <= precision + 4) precision = exponent; } if(exponent >= -3 && exponent <= precision){ fmt = 'f'; precision -= exponent; }else{ fmt = 'e'; --precision; } break; } if (exponent == 9999) { /* Infinity or Nan */ precision = 0; exponent = edigits - digits; fmt = 'f'; } ndig = edigits-digits; if(ndig == 0) { ndig = 1; digits = "0"; } if((afmt=='g' || afmt=='G') && !(flags&ALT)){ /* knock off trailing zeros */ if(fmt == 'f'){ if(precision+exponent > ndig) { precision = ndig - exponent; if(precision < 0) precision = 0; } } else{ if(precision > ndig-1) precision = ndig-1; } } nout = precision; /* digits after decimal point */ if(precision!=0 || flags&ALT) nout++; /* decimal point */ if(fmt=='f' && exponent>0) nout += exponent; /* digits before decimal point */ else nout++; /* there's always at least one */ if(sign || flags&(SPACE|SIGN)) nout++; /* sign */ if(fmt != 'f'){ /* exponent */ eptr = ebuf; for(i=exponent<=0?1-exponent:exponent-1; i; i/=10) *eptr++ = '0' + i%10; while(eptr0 || flags&ALT) putc('.', f); for(i=0; i!=precision; i++) putc(0<=i+exponent && i+exponent0 || flags&ALT) putc('.', f); for(i=0; i!=precision; i++) putc(iebuf) putc(*--eptr, f); } while(nout < width){ putc(' ', f); nout++; } return nout; } /sys/src/ape/lib/ap/stdio/vfscanf.c 664 sys sys 1369842180 9345 /* * pANS stdio -- vfscanf */ #include "iolib.h" #include #include #include #include #define _PLAN9_SOURCE #include static int icvt_f(FILE *f, va_list *args, int store, int width, int type); static int icvt_x(FILE *f, va_list *args, int store, int width, int type); static int icvt_sq(FILE *f, va_list *args, int store, int width, int type); static int icvt_c(FILE *f, va_list *args, int store, int width, int type); static int icvt_d(FILE *f, va_list *args, int store, int width, int type); static int icvt_i(FILE *f, va_list *args, int store, int width, int type); static int icvt_n(FILE *f, va_list *args, int store, int width, int type); static int icvt_o(FILE *f, va_list *args, int store, int width, int type); static int icvt_p(FILE *f, va_list *args, int store, int width, int type); static int icvt_s(FILE *f, va_list *args, int store, int width, int type); static int icvt_u(FILE *f, va_list *args, int store, int width, int type); static int (*icvt[])(FILE *, va_list *, int, int, int)={ 0, 0, 0, 0, 0, 0, 0, 0, /* ^@ ^A ^B ^C ^D ^E ^F ^G */ 0, 0, 0, 0, 0, 0, 0, 0, /* ^H ^I ^J ^K ^L ^M ^N ^O */ 0, 0, 0, 0, 0, 0, 0, 0, /* ^P ^Q ^R ^S ^T ^U ^V ^W */ 0, 0, 0, 0, 0, 0, 0, 0, /* ^X ^Y ^Z ^[ ^\ ^] ^^ ^_ */ 0, 0, 0, 0, 0, 0, 0, 0, /* sp ! " # $ % & ' */ 0, 0, 0, 0, 0, 0, 0, 0, /* ( ) * + , - . / */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0 1 2 3 4 5 6 7 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 8 9 : ; < = > ? */ 0, 0, 0, 0, 0, icvt_f, 0, icvt_f, /* @ A B C D E F G */ 0, 0, 0, 0, 0, 0, 0, 0, /* H I J K L M N O */ 0, 0, 0, 0, 0, 0, 0, 0, /* P Q R S T U V W */ icvt_x, 0, 0, icvt_sq,0, 0, 0, 0, /* X Y Z [ \ ] ^ _ */ 0, 0, 0, icvt_c, icvt_d, icvt_f, icvt_f, icvt_f, /* ` a b c d e f g */ 0, icvt_i, 0, 0, 0, 0, icvt_n, icvt_o, /* h i j k l m n o */ icvt_p, 0, 0, icvt_s, 0, icvt_u, 0, 0, /* p q r s t u v w */ icvt_x, 0, 0, 0, 0, 0, 0, 0, /* x y z { | } ~ ^? */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; #define ngetc(f) (nread++, getc(f)) #define nungetc(c, f) (--nread, ungetc((c), f)) #define wgetc(c, f, out) if(width--==0) goto out; (c)=ngetc(f) #define wungetc(c, f) (++width, nungetc(c, f)) static int nread, ncvt; static const char *fmtp; static QLock scanlock; /* lazy solution */ static int vfscanf0(FILE *f, const char *s, va_list args) { int c, width, type, store; nread=0; ncvt=0; fmtp=s; for(;*fmtp;fmtp++) switch(*fmtp){ default: if(isspace(*fmtp)){ do c=ngetc(f); while(isspace(c)); if(c==EOF) return ncvt?ncvt:EOF; nungetc(c, f); break; } NonSpecial: c=ngetc(f); if(c==EOF) return ncvt?ncvt:EOF; if(c!=*fmtp){ nungetc(c, f); return ncvt; } break; case '%': fmtp++; if(*fmtp!='*') store=1; else{ store=0; fmtp++; } if('0'<=*fmtp && *fmtp<='9'){ width=0; while('0'<=*fmtp && *fmtp<='9') width=width*10 + *fmtp++ - '0'; } else width=-1; type=*fmtp=='h' || *fmtp=='l' || *fmtp=='L'?*fmtp++:'n'; if(!icvt[*fmtp]) goto NonSpecial; if(!(*icvt[*fmtp])(f, &args, store, width, type)) return ncvt?ncvt:EOF; if(*fmtp=='\0') break; if(store) ncvt++; } return ncvt; } int vfscanf(FILE *f, const char *s, va_list args) { int r; qlock(&scanlock); r = vfscanf0(f, s, args); qunlock(&scanlock); return r; } static int icvt_n(FILE *f, va_list *args, int store, int width, int type){ USED(f, width); if(store){ --ncvt; /* this assignment doesn't count! */ switch(type){ case 'h': *va_arg(*args, short *)=nread; break; case 'n': *va_arg(*args, int *)=nread; break; case 'l': case 'L': *va_arg(*args, long *)=nread; break; } } return 1; } #define SIGNED 1 #define UNSIGNED 2 #define POINTER 3 /* * Generic fixed-point conversion * f is the input FILE *; * args is the va_list * into which to store the number; * store is a flag to enable storing; * width is the maximum field width; * type is 'h' 'l' or 'L', the scanf type modifier; * unsgned is SIGNED, UNSIGNED or POINTER, giving part of the type to store in; * base is the number base -- if 0, C number syntax is used. */ static int icvt_fixed(FILE *f, va_list *args, int store, int width, int type, int unsgned, int base){ unsigned long int num=0; int sign=1, ndig=0, dig; int c; do c=ngetc(f); while(isspace(c)); if(width--==0){ nungetc(c, f); goto Done; } if(c=='+'){ wgetc(c, f, Done); } else if(c=='-'){ sign=-1; wgetc(c, f, Done); } switch(base){ case 0: if(c=='0'){ wgetc(c, f, Done); if(c=='x' || c=='X'){ wgetc(c, f, Done); base=16; } else{ ndig=1; base=8; } } else base=10; break; case 16: if(c=='0'){ wgetc(c, f, Done); if(c=='x' || c=='X'){ wgetc(c, f, Done); } else ndig=1; } break; } while('0'<=c && c<='9' || 'a'<=c && c<='f' || 'A'<=c && c<='F'){ dig='0'<=c && c<='9'?c-'0':'a'<=c && c<='f'?c-'a'+10:c-'A'+10; if(dig>=base) break; ndig++; num=num*base+dig; wgetc(c, f, Done); } nungetc(c, f); Done: if(ndig==0) return 0; if(store){ switch(unsgned){ case SIGNED: switch(type){ case 'h': *va_arg(*args, short *)=num*sign; break; case 'n': *va_arg(*args, int *)=num*sign; break; case 'l': case 'L': *va_arg(*args, long *)=num*sign; break; } break; case UNSIGNED: switch(type){ case 'h': *va_arg(*args, unsigned short *)=num*sign; break; case 'n': *va_arg(*args, unsigned int *)=num*sign; break; case 'l': case 'L': *va_arg(*args, unsigned long *)=num*sign; break; } break; case POINTER: *va_arg(*args, void **)=(void *)(num*sign); break; } } return 1; } static int icvt_d(FILE *f, va_list *args, int store, int width, int type){ return icvt_fixed(f, args, store, width, type, SIGNED, 10); } static int icvt_x(FILE *f, va_list *args, int store, int width, int type){ return icvt_fixed(f, args, store, width, type, UNSIGNED, 16); } static int icvt_o(FILE *f, va_list *args, int store, int width, int type){ return icvt_fixed(f, args, store, width, type, UNSIGNED, 8); } static int icvt_i(FILE *f, va_list *args, int store, int width, int type){ return icvt_fixed(f, args, store, width, type, SIGNED, 0); } static int icvt_u(FILE *f, va_list *args, int store, int width, int type){ return icvt_fixed(f, args, store, width, type, UNSIGNED, 10); } static int icvt_p(FILE *f, va_list *args, int store, int width, int type){ return icvt_fixed(f, args, store, width, type, POINTER, 16); } #define NBUF 509 static int icvt_f(FILE *f, va_list *args, int store, int width, int type){ char buf[NBUF+1]; char *s=buf; int c, ndig=0, ndpt=0, nexp=1; if(width<0 || NBUF$target chmod +x $target nuke clean:V: rm -f *.[$OS] *.s gencall installall:V: for(objtype in $CPUS) mk install update:V: update $UPDATEFLAGS mkfile /sys/src/ape/lib/auth 20000000775 bootes sys 1369861528 0 /sys/src/ape/lib/auth/authsrv.h 664 bootes sys 1369166818 636 enum { ANAMELEN= 28, /* name max size in previous proto */ AERRLEN= 64, /* errstr max size in previous proto */ DOMLEN= 48, /* authentication domain name length */ DESKEYLEN= 7, /* encrypt/decrypt des key length */ CHALLEN= 8, /* plan9 sk1 challenge length */ NETCHLEN= 16, /* max network challenge length (used in AS protocol) */ CONFIGLEN= 14, SECRETLEN= 32, /* secret max size */ KEYDBOFF= 8, /* bytes of random data at key file's start */ OKEYDBLEN= ANAMELEN+DESKEYLEN+4+2, /* old key file entry length */ KEYDBLEN= OKEYDBLEN+SECRETLEN, /* key file entry length */ OMD5LEN= 16, }; extern int passtokey(char*, char*); /sys/src/ape/lib/auth/fcall.h 664 bootes sys 1369166818 728 #define VERSION9P "9P2000" #define MAXWELEM 16 #define GBIT8(p) ((p)[0]) #define GBIT16(p) ((p)[0]|((p)[1]<<8)) #define GBIT32(p) ((p)[0]|((p)[1]<<8)|((p)[2]<<16)|((p)[3]<<24)) #define GBIT64(p) ((u32int)((p)[0]|((p)[1]<<8)|((p)[2]<<16)|((p)[3]<<24)) |\ ((vlong)((p)[4]|((p)[5]<<8)|((p)[6]<<16)|((p)[7]<<24)) << 32)) #define PBIT8(p,v) (p)[0]=(v) #define PBIT16(p,v) (p)[0]=(v);(p)[1]=(v)>>8 #define PBIT32(p,v) (p)[0]=(v);(p)[1]=(v)>>8;(p)[2]=(v)>>16;(p)[3]=(v)>>24 #define PBIT64(p,v) (p)[0]=(v);(p)[1]=(v)>>8;(p)[2]=(v)>>16;(p)[3]=(v)>>24;\ (p)[4]=(v)>>32;(p)[5]=(v)>>40;(p)[6]=(v)>>48;(p)[7]=(v)>>56 #define BIT8SZ 1 #define BIT16SZ 2 #define BIT32SZ 4 #define BIT64SZ 8 #define QIDSZ (BIT8SZ+BIT32SZ+BIT64SZ) /sys/src/ape/lib/auth/mkfile 664 bootes sys 1369166818 666 APE=/sys/src/ape <$APE/config LIB=/$objtype/lib/ape/libauth.a OFILES=\ amount.$O\ amount_getkey.$O\ attr.$O\ auth_attr.$O\ auth_challenge.$O\ auth_chuid.$O\ auth_getkey.$O\ auth_getuserpasswd.$O\ auth_proxy.$O\ auth_respond.$O\ auth_rpc.$O\ auth_userpasswd.$O\ auth_wep.$O\ login.$O\ # newns.$O\ # noworld.$O\ HFILES=\ /sys/include/ape/auth.h\ /sys/src/libauth/authlocal.h\ ../9/libc.h UPDATE=\ mkfile\ $HFILES\ ${OFILES:%.$O=%.c}\ ${LIB:/$objtype/%=/386/%}\ #include #include #include #include #include #include /* bsd extensions */ #include #include #include #include #include "priv.h" void _sock_ingetaddr(Rock *r, struct sockaddr *sa, int *alen, char *a) { int n, fd; char *p; char name[Ctlsize]; struct sockaddr_in *ip; struct sockaddr_in6 *ip6; /* get remote address */ strcpy(name, r->ctl); p = strrchr(name, '/'); strcpy(p+1, a); fd = open(name, O_RDONLY); if(fd < 0) return; n = read(fd, name, sizeof(name)-1); if(n > 0){ name[n] = 0; if(name[n-1] == '\n') name[n-1] = '\0'; if((p = strchr(name, '!')) != nil){ *p++ = 0; /* * allow a AF_INET listen to accept a AF_INET6 call * so a machine with ip4 and ip6 addresses can accept either. */ if(strchr(name, '.') != nil || (strchr(name, ':') != nil) && strlen(name) == 2){ ip = (struct sockaddr_in*)sa; ip->sin_family = AF_INET; ip->sin_port = htons(atoi(p)); ip->sin_addr.s_addr = inet_addr(name); if(alen) *alen = sizeof(struct sockaddr_in); }else{ ip6 = (struct sockaddr_in6*)sa; ip6->sin6_family = AF_INET6; ip6->sin6_port = htons(atoi(p)); inet_pton(AF_INET6, name, &ip6->sin6_addr); if(alen) *alen = sizeof(struct sockaddr_in6); } } close(fd); } } /sys/src/ape/lib/bsd/_sock_ipattr.c 664 sys sys 1367613437 408 /* posix */ #include #include #include /* bsd extensions */ #include #include #include #include "priv.h" /* * return ndb attribute type of an ip name */ int _sock_ipattr(char *name) { struct in6_addr ia6; if(inet_pton(AF_INET6, name, &ia6) == 1) return Tip; if(strchr(name, '.') != nil) return Tdom; return Tsys; } /sys/src/ape/lib/bsd/_sock_ntop.c 664 bootes sys 1367613437 781 /* posix */ #include #include #include #include #include #include #include /* bsd extensions */ #include #include #include #include #include "priv.h" void _sock_ntop(int af, const void *addr, char *ip, int nip, int *port) { struct sockaddr_in *a; struct sockaddr_in6 *a6; switch(af){ default: abort(); case AF_INET: a = (struct sockaddr_in*)addr; if(port != nil) *port = ntohs(a->sin_port); if(ip != nil) inet_ntop(af, &a->sin_addr, ip, nip); break; case AF_INET6: a6 = (struct sockaddr_in6*)addr; if(port != nil) *port = ntohs(a6->sin6_port); if(ip != nil) inet_ntop(af, &a6->sin6_addr, ip, nip); break; } } /sys/src/ape/lib/bsd/_sock_srv.c 664 sys sys 1367613437 976 /* posix */ #include #include #include #include #include #include #include /* socket extensions */ #include #include #include "priv.h" /* we can't avoid overrunning npath because we don't know how big it is. */ void _sock_srvname(char *npath, char *path) { char *p; strcpy(npath, "/srv/UD."); p = strrchr(path, '/'); if(p == 0) p = path; else p++; strcat(npath, p); } int _sock_srv(char *path, int fd) { int sfd; char msg[8+256+1]; /* change the path to something in srv */ _sock_srvname(msg, path); /* remove any previous instance */ unlink(msg); /* put the fd in /srv and then close it */ sfd = creat(msg, 0666); if(sfd < 0){ close(fd); _syserrno(); return -1; } snprintf(msg, sizeof msg, "%d", fd); if(write(sfd, msg, strlen(msg)) < 0){ _syserrno(); close(sfd); close(fd); return -1; } close(sfd); close(fd); return 0; } /sys/src/ape/lib/bsd/accept.c 664 sys sys 1369861174 2011 /* posix */ #include #include #include #include #include #include #include #include /* bsd extensions */ #include #include #include #include #include "priv.h" int accept(int fd, void *a, int *alen) { int n, nfd, cfd; Rock *r, *nr; char name[Ctlsize]; char file[8+Ctlsize+1]; char *net; r = _sock_findrock(fd, 0); if(r == 0){ errno = ENOTSOCK; return -1; } switch(r->domain){ case PF_INET: case PF_INET6: switch(r->stype){ default: errno = EPROTONOSUPPORT; return -1; case SOCK_DGRAM: net = "udp"; break; case SOCK_STREAM: net = "tcp"; break; } /* get control file name from listener process */ n = read(fd, name, sizeof(name)-1); if(n <= 0){ _syserrno(); return -1; } name[n] = 0; cfd = open(name, O_RDWR); if(cfd < 0){ _syserrno(); return -1; } nfd = _sock_data(cfd, net, r->domain, r->stype, r->protocol, &nr); if(nfd < 0){ _syserrno(); return -1; } if(write(fd, "OK", 2) < 0){ close(nfd); _syserrno(); return -1; } /* get remote address */ _sock_ingetaddr(nr, &nr->raddr, &n, "remote"); if(a != nil){ memmove(a, &nr->raddr, n); *alen = n; } return nfd; case PF_UNIX: if(r->other >= 0){ errno = EGREG; return -1; } for(;;){ /* read path to new connection */ n = read(fd, name, sizeof(name) - 1); if(n < 0) return -1; if(n == 0) continue; name[n] = 0; /* open new connection */ _sock_srvname(file, name); nfd = open(file, O_RDWR); if(nfd < 0) continue; /* confirm opening on new connection */ if(write(nfd, name, strlen(name)) > 0) break; close(nfd); } nr = _sock_newrock(nfd); if(nr == 0){ close(nfd); return -1; } nr->domain = r->domain; nr->stype = r->stype; nr->protocol = r->protocol; return nfd; default: errno = EOPNOTSUPP; return -1; } } /sys/src/ape/lib/bsd/bcopy.c 664 sys sys 1367613437 246 #include #include #include void bcopy(void *f, void *t, size_t n) { memmove(t, f, n); } int bcmp(void *a, void *b, size_t n) { return memcmp(a, b, n); } void bzero(void *a, size_t n) { memset(a, 0, n); } /sys/src/ape/lib/bsd/bind.c 664 sys sys 1370475119 1211 /* posix */ #include #include #include #include #include #include #include #include #include /* socket extensions */ #include #include #include #include /* plan 9 */ #include "lib.h" #include "sys9.h" #include "priv.h" int bind(int fd, void *a, int alen) { int n, len, cfd, port; Rock *r; char msg[128]; /* assign the address */ r = _sock_findrock(fd, 0); if(r == 0){ errno = ENOTSOCK; return -1; } if(alen > sizeof(r->addr)){ errno = ENAMETOOLONG; return -1; } memmove(&r->addr, a, alen); /* the rest is IP sepecific */ switch(r->domain){ case PF_INET: case PF_INET6: cfd = open(r->ctl, O_RDWR); if(cfd < 0){ errno = EBADF; return -1; } _sock_ntop(r->domain, &r->addr, nil, 0, &port); if(port > 0) snprintf(msg, sizeof msg, "bind %d", port); else strcpy(msg, "bind *"); n = write(cfd, msg, strlen(msg)); if(n < 0){ errno = EOPNOTSUPP; /* Improve error reporting!!! */ close(cfd); return -1; } close(cfd); if(port <= 0) _sock_ingetaddr(r, &r->addr, &len, "local"); } return 0; } /sys/src/ape/lib/bsd/connect.c 664 sys sys 1369861223 2183 /* posix */ #include #include #include #include #include #include #include #include /* bsd extensions */ #include #include #include #include #include "priv.h" int connect(int fd, void *a, int alen) { Rock *r; int n, cfd, nfd, lport, rport; char msg[8+256+1], file[8+256+1], rip[48], *rflag; struct sockaddr_un *runix; static int vers; r = _sock_findrock(fd, 0); if(r == nil){ errno = ENOTSOCK; return -1; } if(alen > sizeof(r->raddr)){ errno = ENAMETOOLONG; return -1; } memmove(&r->raddr, a, alen); switch(r->domain){ case PF_INET: case PF_INET6: /* set up a tcp or udp connection */ cfd = open(r->ctl, O_RDWR); if(cfd < 0){ _syserrno(); return -1; } _sock_ntop(r->domain, &r->raddr, rip, sizeof rip, &rport); _sock_ntop(r->domain, &r->addr, nil, 0, &lport); rflag = r->reserved? "!r": ""; if(lport) snprintf(msg, sizeof msg, "connect %s!%d%s %d", rip, rport, rflag, lport); else snprintf(msg, sizeof msg, "connect %s!%d%s", rip, rport, rflag); n = write(cfd, msg, strlen(msg)); if(n < 0){ _syserrno(); close(cfd); return -1; } close(cfd); return 0; case PF_UNIX: /* null terminate the address */ if(alen == sizeof(r->raddr)) alen--; *(((char*)&r->raddr)+alen) = 0; if(r->other < 0){ errno = EGREG; return -1; } /* put far end of our pipe in /srv */ snprintf(msg, sizeof msg, "UD.%d.%d", getpid(), vers++); if(_sock_srv(msg, r->other) < 0){ r->other = -1; return -1; } r->other = -1; /* tell server the /srv file to open */ runix = (struct sockaddr_un*)&r->raddr; _sock_srvname(file, runix->sun_path); nfd = open(file, O_RDWR); if(nfd < 0){ _syserrno(); unlink(msg); return -1; } if(write(nfd, msg, strlen(msg)) < 0){ _syserrno(); close(nfd); unlink(msg); return -1; } close(nfd); /* wait for server to open it and then remove it */ read(fd, file, sizeof(file)); _sock_srvname(file, msg); unlink(file); return 0; default: errno = EAFNOSUPPORT; return -1; } } /sys/src/ape/lib/bsd/endhostent.c 664 sys sys 1367613437 26 void endhostent(void) { } /sys/src/ape/lib/bsd/ffs.c 664 sys sys 1367613437 249 /* Find the first set bit * i.e. least signifigant 1 bit: * 0 => 0 * 1 => 1 * 2 => 2 * 3 => 1 * 4 => 3 */ int ffs(unsigned int mask) { int i; if (!mask) return 0; i = 1; while (!(mask & 1)){ i++; mask = mask >> 1; } return i; } /sys/src/ape/lib/bsd/gai_strerr.c 664 bootes sys 1367613437 699 #include #include #include #define nil ((void*)0) #define nelem(a) (sizeof(a)/sizeof((a)[0])) char *gaitab[] = { [-EAI_BADFLAGS] "bad flags", [-EAI_NONAME] "authoratitive negative response", [-EAI_AGAIN] "temporary lookup failure", [-EAI_FAIL] "name resolution failure", [-EAI_FAMILY] "family not supported", [-EAI_SOCKTYPE] "ai_socktype not supported", [-EAI_SERVICE] "srvname unsupported", [-EAI_MEMORY] "no memory", [-EAI_SYSTEM] "see errno", [-EAI_OVERFLOW] "overflow", }; const char* gai_strerror(int error) { unsigned int e; e = -error; if(e <= nelem(gaitab) && gaitab[e] != nil) return gaitab[e]; return "bogus gai_strerror argument"; } /sys/src/ape/lib/bsd/getaddrinfo.c 664 bootes sys 1367613437 7437 #define _SUSV2_SOURCE #include #include #include #include #include #include #include #include #include #include static const struct addrinfo defhints = { .ai_flags = AI_ALL, .ai_family = PF_UNSPEC, .ai_socktype = 0, .ai_protocol = 0, }; #define nil ((void*)0) #define nelem(a) (sizeof(a)/sizeof((a)[0])) static int chkfamily(int f) { switch(f){ default: return -1; case PF_UNSPEC: case PF_INET: case PF_INET6: return 0; } } static struct { char *s; int proto; int type; } sockttab[] = { "tcp", IPPROTO_TCP, SOCK_STREAM, "il", IPPROTO_RAW, SOCK_STREAM, "udp", IPPROTO_UDP, SOCK_DGRAM, "icmp", IPPROTO_ICMP, SOCK_RAW, "icmpv6", IPPROTO_ICMP, SOCK_RAW, }; typedef struct Storage Storage; struct Storage { struct addrinfo ai; struct sockaddr sa; char buf[64]; }; #define Strround(s) ((strlen(s)+1)+7 & ~7) static int copyout(Storage *storage, int ns, int totsz, struct addrinfo *hint, struct addrinfo **res) { char *p; unsigned char *m; int i; Storage *s; struct addrinfo *ai; struct sockaddr *sa; m = malloc(totsz); if(m == nil) return EAI_MEMORY; memset(m, 0, totsz); *res = (void*)m; for(i = 0; i < ns; i++){ s = storage + i; sa = &s->sa; ai = (struct addrinfo*)m; m += sizeof *ai; ai->ai_addr = (struct sockaddr*)m; m += s->ai.ai_addrlen; ai->ai_addrlen = s->ai.ai_addrlen; memmove(ai->ai_addr, sa, s->ai.ai_addrlen); ai->ai_family = s->ai.ai_family; ai->ai_flags = hint->ai_flags; ai->ai_socktype = s->ai.ai_socktype; ai->ai_protocol = s->ai.ai_protocol; if(hint->ai_flags & AI_CANONNAME){ ai->ai_canonname =(char*)m; p = s->buf; m += Strround(p); memcpy(ai->ai_canonname, p, strlen(p)); } if(i+1 < ns) ai->ai_next = (struct addrinfo*)m; } return 0; } static int canon(int fd, Storage *a, int ns, int *totsz, struct addrinfo *hint) { char buf[128], *f[15], *p; int i, j, n, best, diff; Storage *s; for(i = 0; i < ns; i++){ s = a+i; lseek(fd, 0, 0); if(s->buf[0] != 0 && (hint->ai_flags & AI_PASSIVE) == 0) snprintf(buf, sizeof buf, "!ipinfo ip=%s sys dom", s->buf); else snprintf(buf, sizeof buf, "!ipinfo sys dom"); if(write(fd, buf, strlen(buf)) == -1) return EAI_FAIL; lseek(fd, 0, 0); n = read(fd, buf, sizeof buf-1); if(n <= 0) continue; buf[n] = 0; // n = tokenize(buf, f, nelem(f)); p = buf; best = -1; for(j = 0; j < n; j++){ f[j] = p; p = strchr(f[j], ' '); if(p != nil) *p++ = 0; if(strncmp(f[j], "dom=", 4) == 0) break; if(best == 0) if(strncmp(f[j], "sys=", 4) == 0) best = i; if(p == nil) break; } if(j == n && best != -1) j = best; while((read(fd, buf, sizeof buf)) > 0) ; if(j != n){ p = strchr(f[j], '=')+1; diff = Strround(s->buf) - Strround(p); memset(s->buf, 0, sizeof s->buf); memcpy(s->buf, p, strlen(p)); *totsz -= diff; } } return 0; } static int docsquery(char *nettype, char *host, char *service, struct addrinfo *hint, struct addrinfo **res) { char buf[128], *p, *q, *r, *net; int i, fd, rv, n, ns, af, sz, totsz; struct sockaddr_in *in; struct sockaddr_in6 *in6; Storage storage[6]; fd = open("/net/cs", O_RDWR); if(fd < 0) return EAI_FAIL; snprintf(buf, sizeof buf, "%s!%s!%s", nettype, host, service); if(write(fd, buf, strlen(buf)) == -1){ close(fd); return EAI_FAIL; } lseek(fd, 0, 0); totsz = 0; for(ns = 0; ns < nelem(storage);){ n = read(fd, buf, sizeof buf - 1); if(n < 1) break; buf[n] = 0; if((p = strchr(buf, ' ')) == nil) continue; *p++ = 0; if(strstr(buf, "!fasttimeout") != nil) continue; if((q = strchr(p, '!')) == nil) q = ""; else *q++ = 0; if(strcmp(host, "*") == 0){ q = p; if(hint->ai_family == AF_INET6) p = "::"; else p = "0.0.0.0"; } if(strlen(p) >= sizeof storage[0].buf) continue; /* wrong for passive, except for icmp6 */ af = AF_INET; if(strchr(p, ':') != nil) af = AF_INET6; if(hint->ai_family != AF_UNSPEC && af != hint->ai_family) continue; sz = 0; memset(&storage[ns], 0, sizeof(storage[ns])); if(hint->ai_flags & AI_CANONNAME){ memcpy(storage[ns].buf, p, strlen(p)); sz += Strround(p); } storage[ns].ai.ai_socktype = SOCK_DGRAM; net = "tcp"; r = strrchr(buf, '/'); if(r != nil && strcmp(r, "/clone") == 0){ *r = 0; r = strrchr(buf, '/'); if(r != nil) net = r+1; } for(i = 0; i < nelem(sockttab); i++) if(strcmp(sockttab[i].s, net) == 0) if(sockttab[i].proto != IPPROTO_RAW || hint->ai_protocol == IPPROTO_RAW){ storage[ns].ai.ai_socktype = sockttab[i].type; storage[ns].ai.ai_protocol = sockttab[i].proto; break; } if(i == nelem(sockttab)) continue; storage[ns].ai.ai_family = af; switch(af){ case AF_INET: in = (struct sockaddr_in*)&storage[ns].sa; in->sin_family = af; in->sin_addr.s_addr = inet_addr(p); in->sin_port = ntohs(atoi(q)); storage[ns].ai.ai_addrlen = sizeof *in; sz += sizeof *in; break; case AF_INET6: storage[ns].ai.ai_family = af; in6 = (struct sockaddr_in6*)&storage[ns].sa; in6->sin6_family = af; inet_pton(af, p, &in6->sin6_addr); in6->sin6_port = ntohs(atoi(q)); storage[ns].ai.ai_addrlen = sizeof *in6; sz += sizeof *in6; break; } totsz += sz + sizeof(struct addrinfo); ns++; /* hacky way to get udp */ if(strcmp(nettype, "net") == 0 && hint->ai_protocol == 0) if(ns < nelem(storage)){ storage[ns] = storage[ns-1]; storage[ns].ai.ai_protocol = IPPROTO_UDP; totsz += sz + sizeof(struct addrinfo); ns++; } } rv = 0; if(hint->ai_flags & AI_CANONNAME) rv = canon(fd, storage, ns, &totsz, hint); close(fd); if(rv != 0) return rv; if(ns == 0) return EAI_NONAME; return copyout(storage, ns, totsz, hint, res); } int getaddrinfo(const char *node, const char *service, const struct addrinfo *hint0, struct addrinfo **res) { char *nettype, *p; int i; struct addrinfo hint; *res = nil; if(node == nil && service == nil) return EAI_BADFLAGS; if(hint0 == nil) hint = defhints; else hint = *hint0; if(hint.ai_flags & AI_NUMERICSERV){ if(service == nil) return EAI_BADFLAGS; strtoul(service, &p, 0); if(*p != 0) return EAI_NONAME; } if(chkfamily(hint.ai_family) == -1) return EAI_FAMILY; if(node != nil) hint.ai_flags &= ~AI_PASSIVE; /* not passive and no host → loopback */ if(node == nil && (hint.ai_flags & AI_PASSIVE) == 0){ switch(hint.ai_family){ case PF_UNSPEC: hint.ai_family = AF_INET; case PF_INET: node = "127.1"; break; case PF_INET6: node = "::1"; break; } } else if (node == nil) node = "*"; nettype = "net"; switch(hint.ai_socktype){ for(i = 0; i < nelem(sockttab); i++) if(sockttab[i].type == hint.ai_socktype) nettype = sockttab[i].s; } if(strcmp(nettype, "net") != 0 && hint.ai_protocol != 0) return EAI_BADFLAGS; if(hint.ai_protocol != 0){ for(i = 0; i < nelem(sockttab); i++) if(sockttab[i].proto == hint.ai_protocol) nettype = sockttab[i].s; } if(hint.ai_family == PF_INET6 && strcmp(nettype, "icmp") == 0) nettype = "icmpv6"; if(node == nil || *node == 0) node = "*"; if(service == nil || *service == 0) service = "0"; return docsquery(nettype, node, service, &hint, res); } void freeaddrinfo(struct addrinfo *ai) { free(ai); } /sys/src/ape/lib/bsd/getdtablesize.c 664 sys sys 1367613437 83 /* posix */ #include int getdtablesize(void) { return OPEN_MAX; } /sys/src/ape/lib/bsd/gethostbyaddr.c 664 sys sys 1367613437 335 /* posix */ #include #include /* bsd extensions */ #include #include #include #include int h_errno; struct hostent* gethostbyaddr(void *addr, int len, int af) { char name[64]; USED(len); return gethostbyname(inet_ntop(af, addr, name, sizeof name)); } /sys/src/ape/lib/bsd/gethostbyname.c 664 sys sys 1367613437 2390 /* posix */ #include #include #include #include #include #include #include /* bsd extensions */ #include #include #include #include #include "priv.h" int h_errno; enum { Nname= 6, }; /* * for inet addresses only */ struct hostent* gethostbyname(char *name) { int i, t, fd, m; char *p, *bp; int nn, na, na6; static struct hostent h; static char buf[1024]; static char *nptr[Nname+1]; static char *aptr[Nname+1]; static char addr4[Nname][4]; static char addr6[Nname][16]; h.h_name = 0; t = _sock_ipattr(name); /* connect to server */ fd = open("/net/cs", O_RDWR); if(fd < 0){ _syserrno(); h_errno = NO_RECOVERY; return 0; } /* construct the query, always expect an ip# back */ switch(t){ case Tsys: snprintf(buf, sizeof buf, "!sys=%s ip=*", name); break; case Tdom: snprintf(buf, sizeof buf, "!dom=%s ip=*", name); break; case Tip: snprintf(buf, sizeof buf, "!ip=%s", name); break; } /* query the server */ if(write(fd, buf, strlen(buf)) < 0){ _syserrno(); h_errno = TRY_AGAIN; return 0; } lseek(fd, 0, 0); for(i = 0; i < sizeof(buf)-1; i += m){ m = read(fd, buf+i, sizeof(buf) - 1 - i); if(m <= 0) break; buf[i+m++] = ' '; } close(fd); buf[i] = 0; /* parse the reply */ nn = na = na6 = 0; for(bp = buf;;){ p = strchr(bp, '='); if(p == 0) break; *p++ = 0; if(strcmp(bp, "dom") == 0){ if(h.h_name == 0) h.h_name = p; if(nn < Nname) nptr[nn++] = p; } else if(strcmp(bp, "sys") == 0){ if(nn < Nname) nptr[nn++] = p; } else if(strcmp(bp, "ip") == 0){ if(strchr(p, ':') != nil){ if(inet_pton(AF_INET6, p, addr6[na6]) == 1) na6++; }else{ if(inet_pton(AF_INET, p, addr4[na]) == 1) na++; } } while(*p && *p != ' ') p++; if(*p) *p++ = 0; bp = p; } if(nn+na+na6 == 0){ h_errno = HOST_NOT_FOUND; return 0; } nptr[nn] = 0; if(na == 0){ for(i = 0; i < na6; i++) aptr[i] = addr6[i]; aptr[i] = 0; h.h_addrtype = AF_INET6; h.h_length = 16; }else{ for(i = 0; i < na; i++) aptr[i] = addr4[i]; aptr[i] = 0; h.h_addrtype = AF_INET; h.h_length = 4; } h.h_aliases = nptr; h.h_addr_list = aptr; if(h.h_name == 0) h.h_name = nptr[0]; if(h.h_name == 0) h.h_name = aptr[0]; return &h; } /sys/src/ape/lib/bsd/gethostname.c 664 sys sys 1367613437 403 /* posix */ #include #include #include #include #include int gethostname(char *name, size_t namelen) { int n, fd; char buf[128]; fd = open("/dev/sysname", O_RDONLY); if(fd < 0) return -1; n = read(fd, buf, sizeof(buf)-1); close(fd); if(n <= 0) return -1; buf[n] = 0; strncpy(name, buf, namelen); name[namelen-1] = 0; return 0; } /sys/src/ape/lib/bsd/getnameinfo.c 664 bootes sys 1367613437 3342 #define _SUSV2_SOURCE #define _C99_SNPRINTF_EXTENSION #include #include #include #include #include #include #include #include #include #include #include #define nil ((void*)0) static int getport(char *trans, int port, char *srv, int srvlen) { char buf[128], match[5], *p, *q; int fd, r, n; r = EAI_FAIL; fd = open("/net/cs", O_RDWR); if(fd < 0) return r; snprintf(buf, sizeof buf, "!port=%d %s=*", port, trans); snprintf(match, sizeof match, "%s=", trans); if(write(fd, buf, strlen(buf)) == -1){ close(fd); return r; } lseek(fd, 0, 0); n = read(fd, buf, sizeof buf - 1); if(n > 0){ buf[n] = 0; for(p = buf; p != nil; p = q){ q = strchr(buf, ' '); if(q != nil) *q++ = 0; if(strncmp(p, match, 4) == 0){ r = snprintf(srv, srvlen, "%s", p+4); break; } } } close(fd); return r; } static int getname(int af, void *addr, char *host, long hostlen, int flags) { char ipbuf[128], buf[128], *p, *q; int fd, r, n; r = EAI_FAIL; if(inet_ntop(af, addr, ipbuf, sizeof ipbuf) == nil) return EAI_SYSTEM; fd = open("/net/cs", O_RDWR); if(fd < 0) return r; snprintf(buf, sizeof buf, "!ip=%s dom=*", ipbuf); if(write(fd, buf, strlen(buf)) == -1){ close(fd); return r; } lseek(fd, 0, 0); n = read(fd, buf, sizeof buf - 1); if(n > 0){ buf[n] = 0; for(p = buf; p != nil; p = q){ q = strchr(buf, ' '); if(q != nil) *q++ = 0; if(strncmp(p, "dom=", 4) == 0){ r = snprintf(ipbuf, sizeof ipbuf, "%s", p+4); break; } } } close(fd); if(r >= 0){ if(flags & NI_NOFQDN){ p = strchr(ipbuf, '.'); if(p != nil) *p = 0; } r = EAI_OVERFLOW; if(snprintf(host, hostlen, "%s", ipbuf) >= 0) r = 0; } return r; } static int afhinfo(int af, void *addr, int port, char *host, long hostlen, char *srv, long srvlen, int flags) { char *trans; int r; trans = "tcp"; if(flags & NI_DGRAM) trans = "udp"; if(flags & NI_NUMERICSERV || getport(trans, port, srv, srvlen) < 0) snprintf(srv, srvlen, "%d", port); /* require name even if not returning it */ if(flags & NI_NAMEREQD){ r = getname(af, addr, host, hostlen, flags); if(r < 0) return r; } if(flags & NI_NUMERICHOST){ if(inet_ntop(af, addr, host, hostlen) == nil) return EAI_SYSTEM; return 0; } if(getname(af, addr, host, hostlen, flags) == 0) return 0; return 0; } int getnameinfo(const struct sockaddr *sa, int len, char *host, long hostlen, char *srv, long srvlen, int flags) { char fakehost[64], fakesrv[16]; struct sockaddr_in *in; struct sockaddr_in6 *in6; if(host != nil && hostlen != 0) *host = 0; else{ host = fakehost; hostlen = sizeof fakehost; } if(srv != nil && hostlen != 0) *srv = 0; else{ srv = fakesrv; srvlen = sizeof fakesrv; } switch(sa->sa_family){ default: return EAI_SOCKTYPE; case AF_INET: if(len < sizeof *in) return EAI_OVERFLOW; in = (struct sockaddr_in*)sa; return afhinfo(AF_INET, &in->sin_addr, ntohs(in->sin_port), host, hostlen, srv, srvlen, flags); case AF_INET6: if(len < sizeof *in6) return EAI_OVERFLOW; in6 = (struct sockaddr_in6*)sa; return afhinfo(AF_INET6, &in6->sin6_addr, ntohs(in6->sin6_port), host, hostlen, srv, srvlen, flags); } } /sys/src/ape/lib/bsd/getopt.c 664 sys sys 1369844423 1040 #include #include #include #define ERR(str, chr) if(opterr){fprintf(stderr, "%s%s%c\n", argv[0], str, chr);} int opterr = 1; int optind = 1; int optopt; char *optarg; int getopt (int argc, char **argv, char *opts) { static int sp = 1; int c; char *cp; if (sp == 1) if (optind >= argc || argv[optind][0] != '-' || argv[optind][1] == '\0') return EOF; else if (strcmp(argv[optind], "--") == 0) { optind++; return EOF; } optopt = c = argv[optind][sp]; if (c == ':' || (cp=strchr(opts, c)) == NULL) { ERR (": illegal option -- ", c); if (argv[optind][++sp] == '\0') { optind++; sp = 1; } return '?'; } if (*++cp == ':') { if (argv[optind][sp+1] != '\0') optarg = &argv[optind++][sp+1]; else if (++optind >= argc) { ERR (": option requires an argument -- ", c); sp = 1; return '?'; } else optarg = argv[optind++]; sp = 1; } else { if (argv[optind][++sp] == '\0') { sp = 1; optind++; } optarg = NULL; } return c; } /sys/src/ape/lib/bsd/getpeername.c 664 sys sys 1369861550 1146 /* posix */ #include #include #include #include #include #include #include #include /* bsd extensions */ #include #include #include #include #include "priv.h" static int connected(Rock *r, int len) { static struct sockaddr niladdr; return memcmp(&r->raddr, &niladdr, len) != 0; } int getpeername(int fd, void *addr, int *alen) { Rock *r; int i; struct sockaddr_un *runix; r = _sock_findrock(fd, 0); if(r == nil){ errno = ENOTSOCK; return -1; } switch(r->domain){ case PF_INET: *alen = sizeof(struct sockaddr_in); memmove(addr, &r->raddr, *alen); break; case PF_INET6: *alen = sizeof(struct sockaddr_in6); memmove(addr, &r->raddr, *alen); break; case PF_UNIX: runix = (struct sockaddr_un*)&r->raddr; i = &runix->sun_path[strlen(runix->sun_path)] - (char*)runix; memmove(addr, runix, i); *alen = i; break; default: errno = EAFNOSUPPORT; return -1; } if(!connected(r, *alen)){ errno = ENOTCONN; memset(&addr, 0, *alen); return -1; } return 0; } /sys/src/ape/lib/bsd/getprotobyname.c 664 sys sys 1367613437 1493 /* posix */ #include #include #include #include #include #include #include /* bsd extensions */ #include #include #include #include #include "priv.h" extern int h_errno; enum { Nname= 6, }; static struct protoent r; struct protoent* getprotobyname(const char *name) { int fd, i, m; char *p, *bp; int nn, na; static char buf[1024], proto[1024]; static char *nptr[Nname+1]; /* connect to server */ fd = open("/net/cs", O_RDWR); if(fd < 0){ _syserrno(); h_errno = NO_RECOVERY; return 0; } /* construct the query, always expect a protocol# back */ snprintf(buf, sizeof buf, "!protocol=%s ipv4proto=*", name); /* query the server */ if(write(fd, buf, strlen(buf)) < 0){ _syserrno(); h_errno = TRY_AGAIN; return 0; } lseek(fd, 0, 0); for(i = 0; i < sizeof(buf)-1; i += m){ m = read(fd, buf+i, sizeof(buf) - 1 - i); if(m <= 0) break; buf[i+m++] = ' '; } close(fd); buf[i] = 0; /* parse the reply */ nn = na = 0; for(bp = buf;;){ p = strchr(bp, '='); if(p == 0) break; *p++ = 0; if(strcmp(bp, "protocol") == 0){ if(!nn) r.p_name = p; if(nn < Nname) nptr[nn++] = p; } else if(strcmp(bp, "ipv4proto") == 0){ r.p_proto = atoi(p); na++; } while(*p && *p != ' ') p++; if(*p) *p++ = 0; bp = p; } nptr[nn] = 0; r.p_aliases = nptr; if (nn+na == 0) return 0; return &r; } /sys/src/ape/lib/bsd/getservbyaddr.c 664 sys sys 1367613437 328 #include #include #include /* bsd extensions */ #include #include #include #include struct servent* getservbyport(int port, char *proto) { char buf[32]; snprintf(buf, sizeof buf, "%d", ntohs(port)); return getservbyname(buf, proto); } /sys/src/ape/lib/bsd/getservbyname.c 664 sys sys 1367613437 1762 /* posix */ #include #include #include #include #include #include #include #include /* bsd extensions */ #include #include #include #include #include "priv.h" enum { Nname= 6, }; /* * for inet addresses only */ struct servent* getservbyname(char *name, char *proto) { int i, fd, m, num; char *p, *bp; int nn, na; static struct servent s; static char buf[1024]; static char *nptr[Nname+1]; num = 1; for(p = name; *p; p++) if(!isdigit(*p)) num = 0; s.s_name = 0; /* connect to server */ fd = open("/net/cs", O_RDWR); if(fd < 0){ _syserrno(); return 0; } /* construct the query, always expect an ip# back */ if(num && proto != nil) snprintf(buf, sizeof buf, "!port=%s %s=*", name, proto); else if(num) snprintf(buf, sizeof buf, "!port=%s", name); else snprintf(buf, sizeof buf, "!%s=%s port=*", proto, name); /* query the server */ if(write(fd, buf, strlen(buf)) < 0){ _syserrno(); return 0; } lseek(fd, 0, 0); for(i = 0; i < sizeof(buf)-1; i += m){ m = read(fd, buf+i, sizeof(buf) - 1 - i); if(m <= 0) break; buf[i+m++] = ' '; } close(fd); buf[i] = 0; /* parse the reply */ nn = na = 0; for(bp = buf;;){ p = strchr(bp, '='); if(p == 0) break; *p++ = 0; if(proto != nil && strcmp(bp, proto) == 0){ if(nn < Nname) nptr[nn++] = p; } else if(strcmp(bp, "port") != 0){ if(nn < Nname) nptr[nn++] = p; } else{ s.s_port = htons(atoi(p)); } while(*p && *p != ' ') p++; if(*p) *p++ = 0; bp = p; } if(nn+na == 0) return 0; nptr[nn] = 0; s.s_aliases = nptr; if(s.s_name == 0) s.s_name = nptr[0]; return &s; } /sys/src/ape/lib/bsd/getsockname.c 664 sys sys 1369190812 814 /* posix */ #include #include #include #include #include #include #include #include /* bsd extensions */ #include #include #include #include #include "priv.h" int getsockname(int fd, void *addr, int *alen) { Rock *r; int i; struct sockaddr_un *lunix; r = _sock_findrock(fd, 0); if(r == 0){ errno = ENOTSOCK; return -1; } switch(r->domain){ case PF_INET: case PF_INET6: _sock_ingetaddr(r, addr, alen, "local"); break; case PF_UNIX: lunix = (struct sockaddr_un*)&r->addr; i = &lunix->sun_path[strlen(lunix->sun_path)] - (char*)lunix; memmove(addr, lunix, i); *alen = i; break; default: errno = EAFNOSUPPORT; return -1; } return 0; } /sys/src/ape/lib/bsd/gettimeofday.c 664 sys sys 1367613437 961 #include #include #include #include #include "sys9.h" typedef unsigned long long uvlong; typedef long long vlong; typedef unsigned char uchar; static uvlong order = 0x0001020304050607ULL; static void be2vlong(vlong *to, uchar *f) { uchar *t, *o; int i; t = (uchar*)to; o = (uchar*)ℴ for(i = 0; i < 8; i++) t[o[i]] = f[i]; } int gettimeofday(struct timeval *tp, struct timezone *tzp) { uchar b[8]; vlong t; int opened; static int fd = -1; opened = 0; for(;;) { if(fd < 0) if(opened++ || (fd = _OPEN("/dev/bintime", OREAD|OCEXEC)) < 0) return 0; if(_PREAD(fd, b, sizeof b, 0) == sizeof b) break; /* leave fd open for future use */ /* short read, perhaps try again */ _CLOSE(fd); fd = -1; } be2vlong(&t, b); tp->tv_sec = t/1000000000; tp->tv_usec = (t/1000)%1000000; if(tzp) { tzp->tz_minuteswest = 4*60; /* BUG */ tzp->tz_dsttime = 1; } return 0; } /sys/src/ape/lib/bsd/herror.c 664 bootes sys 1367613437 775 /* posix */ #include #include #include #include #include #include /* bsd extensions */ #include #include //#include #include "priv.h" int h_errno; static const char *hetab[] = { [HOST_NOT_FOUND] "authoritative answer host not found", [TRY_AGAIN] "non-authoritive host not found", [NO_RECOVERY] "non recoverable error", [NO_DATA] "valid name, no data record of requested type" }; static const char* getmsg(unsigned int e) { const char *p; if(e > nelem(hetab) || (p = hetab[e]) == nil) p = "unknown error"; return p; } void herror(const char *s) { fprintf(stderr, "%s: %s", s, getmsg(h_errno)); } const char* hstrerror(int err) { return getmsg(err); } /sys/src/ape/lib/bsd/inet_addr.c 664 sys sys 1367613437 770 /* posix */ #include #include #include #include /* bsd extensions */ #include #include #include #define CLASS(x) (x[0]>>6) unsigned long inet_addr(char *from) { int i; char *p; unsigned char to[4]; unsigned long x; p = from; memset(to, 0, 4); for(i = 0; i < 4 && *p; i++){ to[i] = strtoul(p, &p, 0); if(*p == '.') p++; } switch(CLASS(to)){ case 0: /* class A - 1 byte net */ case 1: if(i == 3){ to[3] = to[2]; to[2] = to[1]; to[1] = 0; } else if (i == 2){ to[3] = to[1]; to[1] = 0; } break; case 2: /* class B - 2 byte net */ if(i == 3){ to[3] = to[2]; to[2] = 0; } break; } x = nptohl(to); x = htonl(x); return x; } /sys/src/ape/lib/bsd/inet_ntoa.c 664 sys sys 1367613437 393 /* posix */ #include #include #include #include /* bsd extensions */ #include #include #include #include char* inet_ntoa(struct in_addr in) { static char s[18]; unsigned char *p; p = (unsigned char*)&in.s_addr; snprintf(s, sizeof s, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); return s; } /sys/src/ape/lib/bsd/inet_ntop.c 664 bootes sys 1367613437 1757 #define _SUSV2_SOURCE #define _C99_SNPRINTF_EXTENSION #include #include #include #include #include #include #include #include #include #include #include #define nil ((void*)0) const char* inet_ntop(int af, const void *src, char *dst, int size) { char map[16/2], buf[32], *c; int d, j, n, run, srun, maxrun, maxsrun; uint8_t *u; uint32_t i; struct in_addr *ia; struct in6_addr *ia6; switch(af){ default: errno = EAFNOSUPPORT; return nil; case AF_INET: ia = (struct in_addr*)src; i = ia->s_addr; i = ntohl(i); n = snprintf(dst, size, "%d.%d.%d.%d", i>>24, i>>16 & 0xff, i>>8 & 0xff, i & 0xff); if(n == -1){ errno = ENOSPC; return nil; } return dst; case AF_INET6: ia6 = (struct in6_addr*)src; u = ia6->s6_addr; srun = run = 0; maxrun = maxsrun = 0; for(i = 0; i < 16; i += 2){ if((u[i]|u[i+1]) == 0){ map[i/2] = 1; if(run == 0) srun = i/2; run++; }else{ if(run > 0 && run > maxrun){ maxrun = run; maxsrun = srun; } srun = run = 0; } } if(run > 0 && run > maxrun){ maxrun = run; maxsrun = srun; } /* buf must be bigger than biggest address, else -1 return gets us */ memset(buf, 0, sizeof buf); j = 0; c = ":"; for(i = 0; i < 8;){ if(map[i] && i == maxsrun){ j += snprintf(buf+j, sizeof buf-j, "%s:", c); c = ""; i += maxrun; }else{ d = u[i*2]<<8 | u[i*2+1]; c = i<7? ":": ""; j += snprintf(buf+j, sizeof buf-j, "%x%s", d, c); c = ""; i++; } } if(strlen(buf)+1 > size){ errno = ENOSPC; return nil; } memcpy(dst, buf, strlen(buf)+1); return dst; } } /sys/src/ape/lib/bsd/inet_pton.c 664 bootes sys 1367613437 1974 #define _SUSV2_SOURCE #include #include #include #include #include #include #include #include #include #include #include #define nil ((void*)0) typedef unsigned char uchar; int classnetbytes[] = {1, 1, 2, 3, }; static char* parseip4(uchar *u, char *s) { char *v; int i, d, b, n; for(i = 0; i < 4; i++){ v = s; d = strtoul(s, &s, 0); if(d > 255) return nil; u[i] = d; if(*s == '.') s++; else if(s == v) break; } if(i < 4 && i > (b = classnetbytes[u[0]>>6])){ n = i - b; memmove(u+4-n, u+b, n); memset(u+b, 0, 4-(b+n)); i = 4; } if(i != 4) return nil; return s; } static char* parseip6(uchar *u, char *s) { char *v; int i, d, cc, is4; is4 = 1; cc = -1; for(i = 0; i < 16;){ v = s; d = strtoul(s, &s, 16); switch(*s){ case '.': if(i + 4 > 16) return nil; s = parseip4(u+i, v); if(s == nil) return nil; i += 4; break; case ':': is4 = 0; s++; default: if(d > 0xffff) return nil; u[i++] = d>>8; u[i++] = d; if(*s == ':'){ if(cc != -1) return nil; cc = i; s++; } if(s == v) i -= 2; break; } if(s == v) break; } if(is4 && i == 4){ memmove(u+12, u, 4); memset(u, 0, 4); memset(u+10, 0xff, 2); } else if(cc != -1 && i < 16){ memmove(u+cc+(16-i), u+cc, i-cc); memset(u+cc, 0, 16-i); } else if(i != 16) return nil; return s; } int inet_pton(int af, const char *src, void *dst) { uchar u[16]; struct in_addr *ia; struct in6_addr *ia6; memset(u, 0, sizeof u); switch(af){ default: errno = EAFNOSUPPORT; return -1; case AF_INET: if(parseip4(u, src) == nil) return 0; ia = (struct in_addr*)dst; memmove(&ia->s_addr, u, 4); return 1; case AF_INET6: if(parseip6(u, src) == nil) return 0; ia6 = (struct in6_addr*)dst; memmove(ia6->s6_addr, u, 16); return 1; } } /sys/src/ape/lib/bsd/ioctl.c 664 sys sys 1367613437 561 /* posix */ #include #include #include #include #include #include #include #include /* bsd extensions */ #include #include #include int ioctl(int fd, unsigned long request, void* arg) { struct stat d; if(request == FIONREAD) { if(fstat(fd, &d) < 0) { errno = EBADF; return -1; } /* this works if the file is buffered somehow */ *(long*)arg = d.st_size; return 0; } else { errno = EINVAL; return -1; } } /sys/src/ape/lib/bsd/ip6const.c 664 bootes sys 1367613437 168 #include #include const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT; const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT; /sys/src/ape/lib/bsd/listen.c 664 sys sys 1369190830 3137 /* posix */ #include #include #include #include #include #include #include #include #include #include #define _SUSV2_SOURCE #include /* socket extensions */ #include #include #include #include /* plan 9 */ #include "lib.h" #include "sys9.h" #include "priv.h" extern int _muxsid; extern void _killmuxsid(void); /* * replace the fd with a pipe and start a process to * accept calls in. this is all to make select work. */ static int listenproc(Rock *r, int fd) { Rock *nr; char *net; int cfd, nfd, dfd; int pfd[2]; struct stat d; char *p; char listen[Ctlsize]; char name[Ctlsize]; void *v; switch(r->stype){ default: errno = EOPNOTSUPP; return -1; case SOCK_DGRAM: net = "udp"; break; case SOCK_STREAM: net = "tcp"; break; } strcpy(listen, r->ctl); p = strrchr(listen, '/'); if(p == 0) return -1; strcpy(p+1, "listen"); if(pipe(pfd) < 0) return -1; /* replace fd with a pipe */ nfd = dup(fd); dup2(pfd[0], fd); close(pfd[0]); fstat(fd, &d); r->inode = d.st_ino; r->dev = d.st_dev; /* start listening process */ switch(fork()){ case -1: close(pfd[1]); close(nfd); return -1; case 0: if(_muxsid == -1) { _RFORK(RFNOTEG); _muxsid = getpgrp(); } else setpgid(getpid(), _muxsid); while(_RENDEZVOUS((void*)2, (void*)_muxsid) == (void*)~0) ; break; default: atexit(_killmuxsid); while((v = _RENDEZVOUS((void*)2, 0)) == (void*)~0) ; _muxsid = (int)(uintptr_t)v; close(pfd[1]); close(nfd); return 0; } /* for(fd = 0; fd < 30; fd++) if(fd != nfd && fd != pfd[1]) close(fd);/**/ for(;;){ cfd = open(listen, O_RDWR); if(cfd < 0) break; dfd = _sock_data(cfd, net, r->domain, r->stype, r->protocol, &nr); if(dfd < 0) break; if(write(pfd[1], nr->ctl, strlen(nr->ctl)) < 0) break; if(read(pfd[1], name, sizeof(name)) <= 0) break; close(dfd); } exit(0); return 0; } int listen(int fd, int /*backlog*/) { Rock *r; int n, cfd, port; char msg[128]; struct sockaddr_un *lunix; r = _sock_findrock(fd, 0); if(r == 0){ errno = ENOTSOCK; return -1; } switch(r->domain){ case PF_INET: case PF_INET6: cfd = open(r->ctl, O_RDWR); if(cfd < 0){ errno = EBADF; return -1; } _sock_ntop(r->domain, &r->addr, nil, 0, &port); if(port != 0 && port != 0xffff) { if(write(cfd, "bind 0", 6) < 0) { errno = EGREG; close(cfd); return -1; } snprintf(msg, sizeof msg, "announce %d", port); } else strcpy(msg, "announce *"); n = write(cfd, msg, strlen(msg)); if(n < 0){ errno = EOPNOTSUPP; /* Improve error reporting!!! */ close(cfd); return -1; } close(cfd); return listenproc(r, fd); case PF_UNIX: if(r->other < 0){ errno = EGREG; return -1; } lunix = (struct sockaddr_un*)&r->addr; if(_sock_srv(lunix->sun_path, r->other) < 0){ _syserrno(); r->other = -1; return -1; } r->other = -1; return 0; default: errno = EAFNOSUPPORT; return -1; } } /sys/src/ape/lib/bsd/lstat.c 664 sys sys 1367613437 256 #include #include #include int lstat(char *name, struct stat *ans) { return stat(name, ans); } int symlink(char*, char*) { errno = EPERM; return -1; } int readlink(char*, char*, int) { errno = EIO; return -1; } /sys/src/ape/lib/bsd/mkfile 664 sys sys 1369844471 923 APE=/sys/src/ape <$APE/config LIB=/$objtype/lib/ape/libbsd.a OFILES=\ accept.$O\ bcopy.$O\ bind.$O\ connect.$O\ endhostent.$O\ ffs.$O\ gai_strerr.$O\ getaddrinfo.$O\ getdtablesize.$O\ gethostbyaddr.$O\ gethostbyname.$O\ gethostname.$O\ getnameinfo.$O\ getopt.$O\ getpeername.$O\ getprotobyname.$O\ getservbyaddr.$O\ getservbyname.$O\ getsockname.$O\ gettimeofday.$O\ herror.$O\ inet_addr.$O\ inet_ntoa.$O\ inet_ntop.$O\ inet_pton.$O\ ioctl.$O\ ip6const.$O\ listen.$O\ lstat.$O\ mktemp.$O\ ntohl.$O\ nptohl.$O\ popen.$O\ putenv.$O\ rcmd.$O\ readv.$O\ rresvport.$O\ send.$O\ sendto.$O\ setlinebuf.$O\ shutdown.$O\ _sock_ingetaddr.$O\ _sock_ipattr.$O\ _sock_ntop.$O\ _sock_srv.$O\ strcasecmp.$O\ strncasecmp.$O\ socket.$O\ socketpair.$O\ pty.$O\ writev.$O\ HFILES=priv.h #include #include #include #include #include char* mktemp(char *template) { int n; long x; char *p; int c; struct stat stbuf; n = strlen(template); p = template+n-6; if (n < 6 || strcmp(p, "XXXXXX") != 0) { *template = 0; } else { x = getpid() % 100000; sprintf(p, "%05d", x); p += 5; for(c = 'a'; c <= 'z'; c++) { *p = c; if (stat(template, &stbuf) < 0) return template; } *template = 0; } return template; } /sys/src/ape/lib/bsd/nptohl.c 664 sys sys 1367613437 140 unsigned long nptohl(void *p) { unsigned char *up; unsigned long x; up = p; x = (up[0]<<24)|(up[1]<<16)|(up[2]<<8)|up[3]; return x; } /sys/src/ape/lib/bsd/ntohl.c 664 sys sys 1369861309 613 unsigned long ntohl(unsigned long x) { unsigned long n; unsigned char *p; n = x; p = (unsigned char*)&n; return (p[0]<<24)|(p[1]<<16)|(p[2]<<8)|p[3]; } unsigned long htonl(unsigned long h) { unsigned long n; unsigned char *p; p = (unsigned char*)&n; p[0] = h>>24; p[1] = h>>16; p[2] = h>>8; p[3] = h; return n; } unsigned short ntohs(unsigned short x) { unsigned short n; unsigned char *p; n = x; p = (unsigned char*)&n; return (p[0]<<8)|p[1]; } unsigned short htons(unsigned short h) { unsigned short n; unsigned char *p; p = (unsigned char*)&n; p[0] = h>>8; p[1] = h; return n; } /sys/src/ape/lib/bsd/popen.c 664 sys sys 1367613437 1615 #include #include #include #include #include #define MAXFORKS 20 #define NSYSFILE 3 #define tst(a,b) (*mode == 'r'? (b) : (a)) #define RDR 0 #define WTR 1 struct a_fork { short done; short fd; int pid; int status; }; static struct a_fork the_fork[MAXFORKS]; FILE * popen(char *cmd, char *mode) { int p[2]; int myside, hisside, pid; int i, ind; for (ind = 0; ind < MAXFORKS; ind++) if (the_fork[ind].pid == 0) break; if (ind == MAXFORKS) return NULL; if(pipe(p) < 0) return NULL; myside = tst(p[WTR], p[RDR]); hisside = tst(p[RDR], p[WTR]); switch (pid = fork()) { case -1: return NULL; case 0: /* myside and hisside reverse roles in child */ close(myside); dup2(hisside, tst(0, 1)); for (i=NSYSFILE; i #include #include #include #include #include #include #include #include #include "lib.h" #include "sys9.h" #include "dir.h" /* * return the name of the slave */ char* ptsname(int fd) { Dir *d; static char buf[32]; if((d = _dirfstat(fd)) == nil || strlen(d->name) < 4){ free(d); _syserrno(); return 0; } snprintf(buf, sizeof buf, "/dev/ptty%d", atoi(d->name+4)); free(d); return buf; } /* * return the name of the master */ char* ptmname(int fd) { Dir *d; static char buf[32]; if((d = _dirfstat(fd)) == nil || strlen(d->name) < 4){ free(d); _syserrno(); return 0; } snprintf(buf, sizeof buf, "/dev/ttym%d", atoi(d->name+4)); return buf; } static char ptycl[] = "/dev/ptyclone"; static char fssrv[] = "/srv/ptyfs"; static void mkserver(void) { int fd, i; char *argv[3]; fd = _OPEN(fssrv, O_RDWR); if(_MOUNT(fd, -1, "/dev", MAFTER, "") < 0) { /* * remove fssrv here, if it exists, to avoid a race * between the loop in the default case below and the * new ptyfs removing fssrv when it starts. * we otherwise might be unlucky enough to open the old * (hung channel) fssrv before ptyfs removes it and break * out of the loop with an open fd to a hung channel? */ _CLOSE(fd); _REMOVE(fssrv); switch(_RFORK(RFPROC|RFFDG)) { case -1: return; case 0: argv[0] = "ptyfs"; argv[1] = 0; _EXEC("/bin/ape/ptyfs", argv); _EXITS(0); default: for(i = 0; i < 3; i++) { fd = _OPEN(fssrv, O_RDWR); if(fd >= 0) break; _SLEEP(1000); } } if(fd < 0) return; if(_MOUNT(fd, -1, "/dev", MAFTER, "") < 0) _CLOSE(fd); } /* successful _MOUNT closes fd */ } /* * allocate a new pty */ int _getpty(void) { struct stat sb; if(stat(ptycl, &sb) < 0) mkserver(); return open(ptycl, O_RDWR); } /sys/src/ape/lib/bsd/putenv.c 664 sys sys 1367613437 483 #include #include #include #include int putenv(char *s) { int f, n; char *value; char buf[300]; value = strchr(s, '='); if (value) { n = value-s; if(n<=0 || n > sizeof(buf)-6) return -1; strcpy(buf, "/env/"); strncpy(buf+5, s, n); buf[n+5] = 0; f = creat(buf, 0666); if(f < 0) return 1; value++; n = strlen(value); if(write(f, value, n) != n) return -1; close(f); return 0; } else return -1; } /sys/src/ape/lib/bsd/rcmd.c 664 sys sys 1369861342 2485 /* posix */ #include #include #include #include #include #include #include #include /* socket extensions */ #include #include #include #include #include "priv.h" static char pbotch[] = "rcmd: protocol botch\n"; static char lbotch[] = "rcmd: botch starting error stream\n"; static void ding(int) { } /* no ip6 and i don't care */ int rcmd(char **dst, int port, char *luser, char *ruser, char *cmd, int *fd2p) { char c; int i, fd, lfd, fd2, port2; struct hostent *h; Rock *r; struct sockaddr_in in; char buf[128]; void (*x)(int); h = gethostbyname(*dst); if(h == 0) return -1; *dst = h->h_name; /* connect using a reserved tcp port */ fd = socket(PF_INET, SOCK_STREAM, 0); if(fd < 0) return -1; r = _sock_findrock(fd, 0); if(r == 0){ errno = ENOTSOCK; return -1; } r->reserved = 1; in.sin_family = AF_INET; in.sin_port = htons(port); memmove(&in.sin_addr, h->h_addr_list[0], sizeof(in.sin_addr)); if(connect(fd, &in, sizeof(in)) < 0){ close(fd); return -1; } /* error stream */ lfd = -1; if(fd2p){ /* create an error stream and wait for a call in */ for(i = 0; i < 10; i++){ lfd = rresvport(&port2); if(lfd < 0) continue; if(listen(lfd, 1) == 0) break; close(lfd); } if(i >= 10){ fprintf(stderr, pbotch); return -1; } snprintf(buf, sizeof buf, "%d", port2); if(write(fd, buf, strlen(buf)+1) < 0){ close(fd); close(lfd); fprintf(stderr, lbotch); return -1; } } else { if(write(fd, "", 1) < 0){ fprintf(stderr, pbotch); return -1; } } /* pass id's and command */ if(write(fd, luser, strlen(luser)+1) < 0 || write(fd, ruser, strlen(ruser)+1) < 0 || write(fd, cmd, strlen(cmd)+1) < 0){ if(fd2p) close(lfd); fprintf(stderr, pbotch); return -1; } fd2 = -1; if(fd2p){ x = signal(SIGALRM, ding); alarm(15); fd2 = accept(lfd, &in, &i); alarm(0); close(lfd); signal(SIGALRM, x); if(fd2 < 0){ close(fd); close(lfd); fprintf(stderr, lbotch); return -1; } *fd2p = fd2; } /* get reply */ if(read(fd, &c, 1) != 1){ if(fd2p) close(fd2); fprintf(stderr, pbotch); return -1; } if(c == 0) return fd; i = 0; while(c){ buf[i++] = c; if(read(fd, &c, 1) != 1) break; if(i >= sizeof(buf)-1) break; } buf[i] = 0; fprintf(stderr, "rcmd: %s\n", buf); close(fd); return -1; } /sys/src/ape/lib/bsd/readv.c 664 sys sys 1367613437 472 #include #include #include int readv(int fd, struct iovec *v, int ent) { int x, i, n, len; char buf[10*1024]; for(len = i = 0; i < ent; i++) len += v[i].iov_len; if(len > sizeof(buf)) len = sizeof(buf); len = read(fd, buf, len); if(len <= 0) return len; for(n = i = 0; n < len && i < ent; i++){ x = len - n; if(x > v[i].iov_len) x = v[i].iov_len; memmove(v[i].iov_base, buf + n, x); n += x; } return len; } /sys/src/ape/lib/bsd/rresvport.c 664 sys sys 1367613437 638 /* posix */ #include #include #include #include #include #include /* socket extensions */ #include #include #include int rresvport(int *p) { int fd; short i; struct sockaddr_in in; static int next; fd = socket(PF_INET, SOCK_STREAM, 0); if(fd < 0) return -1; i = 600 + ((getpid()+next++)%(1024-600)); memset(&in, 0, sizeof(in)); in.sin_family = AF_INET; in.sin_port = htons(i); printf("in.sin_port = %d\n", in.sin_port); if(bind(fd, &in, sizeof(in)) < 0){ close(fd); return -1; } if(p) *p = i; return fd; } /sys/src/ape/lib/bsd/send.c 664 sys sys 1367613437 446 /* posix */ #include #include #include #include /* bsd extensions */ #include #include #include "priv.h" int send(int fd, void *a, int n, int flags) { if(flags & MSG_OOB){ errno = EOPNOTSUPP; return -1; } return write(fd, a, n); } int recv(int fd, void *a, int n, int flags) { if(flags & MSG_OOB){ errno = EOPNOTSUPP; return -1; } return read(fd, a, n); } /sys/src/ape/lib/bsd/sendto.c 664 sys sys 1367613437 520 /* posix */ #include #include #include #include /* bsd extensions */ #include #include #include "priv.h" int sendto(int fd, void *a, int n, int flags, void *to, int tolen) { USED(to, tolen); /* actually, should do connect if not done already */ return send(fd, a, n, flags); } int recvfrom(int fd, void *a, int n, int flags, void *from, int *fromlen) { if(getsockname(fd, from, fromlen) < 0) return -1; return recv(fd, a, n, flags); } /sys/src/ape/lib/bsd/setlinebuf.c 664 sys sys 1367613437 111 #include void setlinebuf(FILE *f) { static char buf[BUFSIZ]; setvbuf (f, buf, _IOLBF, BUFSIZ); } /sys/src/ape/lib/bsd/shutdown.c 664 sys sys 1367613437 117 #include #include int shutdown(int fd, int how) { if(how == 2) close(fd); return 0; } /sys/src/ape/lib/bsd/socket.c 664 sys sys 1367613437 2790 /* posix */ #include #include #include #include #include #include #include #include /* bsd extensions */ #include #include #include "priv.h" Rock *_sock_rock; Rock* _sock_findrock(int fd, struct stat *dp) { Rock *r; struct stat d; if(dp == 0) dp = &d; fstat(fd, dp); for(r = _sock_rock; r; r = r->next){ if(r->inode == dp->st_ino && r->dev == dp->st_dev) break; } return r; } Rock* _sock_newrock(int fd) { Rock *r; struct stat d; r = _sock_findrock(fd, &d); if(r == 0){ r = malloc(sizeof(Rock)); if(r == nil) return nil; r->next = _sock_rock; _sock_rock = r; } memset(&r->raddr, 0, sizeof(r->raddr)); memset(&r->addr, 0, sizeof(r->addr)); r->reserved = 0; r->dev = d.st_dev; r->inode = d.st_ino; r->other = -1; return r; } int _sock_data(int cfd, char *net, int domain, int stype, int protocol, Rock **rp) { int n, fd; Rock *r; char name[Ctlsize]; /* get the data file name */ n = read(cfd, name, sizeof(name)-1); if(n < 0){ close(cfd); errno = ENOBUFS; return -1; } name[n] = 0; n = strtoul(name, 0, 0); snprintf(name, sizeof name, "/net/%s/%d/data", net, n); /* open data file */ fd = open(name, O_RDWR); close(cfd); if(fd < 0){ close(cfd); errno = ENOBUFS; return -1; } /* hide stuff under the rock */ snprintf(name, sizeof name, "/net/%s/%d/ctl", net, n); r = _sock_newrock(fd); if(r == 0){ errno = ENOBUFS; close(fd); return -1; } if(rp) *rp = r; memset(&r->raddr, 0, sizeof(r->raddr)); memset(&r->addr, 0, sizeof(r->addr)); r->domain = domain; r->stype = stype; r->protocol = protocol; strcpy(r->ctl, name); return fd; } int socket(int domain, int stype, int protocol) { Rock *r; int cfd; int pfd[2]; char *net; switch(domain){ case PF_INET: case PF_INET6: /* get a free network directory */ switch(stype){ case SOCK_DGRAM: net = "udp"; cfd = open("/net/udp/clone", O_RDWR); break; case SOCK_STREAM: net = "tcp"; cfd = open("/net/tcp/clone", O_RDWR); break; default: errno = EPROTONOSUPPORT; return -1; } if(cfd < 0){ _syserrno(); return -1; } return _sock_data(cfd, net, domain, stype, protocol, 0); case PF_UNIX: if(pipe(pfd) < 0){ _syserrno(); return -1; } r = _sock_newrock(pfd[0]); r->domain = domain; r->stype = stype; r->protocol = protocol; r->other = pfd[1]; return pfd[0]; default: errno = EPROTONOSUPPORT; return -1; } } int issocket(int fd) { Rock *r; r = _sock_findrock(fd, 0); return (r != 0); } /* * probably should do better than this */ int getsockopt(int, int, int, void *, int *) { return -1; } int setsockopt(int, int, int, void *, int) { return 0; } /sys/src/ape/lib/bsd/socketpair.c 664 sys sys 1367613437 348 /* posix */ #include #include #include #include /* bsd extensions */ #include #include int socketpair(int domain, int type, int protocol, int *sv) { USED(protocol, type); switch(domain){ case PF_UNIX: return pipe(sv); default: errno = EOPNOTSUPP; return -1; } } /sys/src/ape/lib/bsd/strcasecmp.c 664 sys sys 1367613437 343 #include typedef unsigned char uchar; int strcasecmp(char *s1, char *s2) { int c1, c2; while(*s1){ c1 = *(uchar*)s1++; c2 = *(uchar*)s2++; if(c1 == c2) continue; if(c1 >= 'A' && c1 <= 'Z') c1 -= 'A' - 'a'; if(c2 >= 'A' && c2 <= 'Z') c2 -= 'A' - 'a'; if(c1 != c2) return c1 - c2; } return -*s2; } /sys/src/ape/lib/bsd/strncasecmp.c 664 sys sys 1367613437 385 #include typedef unsigned char uchar; int strncasecmp(char *s1, char *s2, int n) { int c1, c2; while(*s1 && n-- > 0){ c1 = *(uchar*)s1++; c2 = *(uchar*)s2++; if(c1 == c2) continue; if(c1 >= 'A' && c1 <= 'Z') c1 -= 'A' - 'a'; if(c2 >= 'A' && c2 <= 'Z') c2 -= 'A' - 'a'; if(c1 != c2) return c1 - c2; } if(n <= 0) return 0; return -*s2; } /sys/src/ape/lib/bsd/writev.c 664 sys sys 1367613437 904 #include #include #include /* bsd extensions */ #include #include #include "priv.h" int writev(int fd, struct iovec *v, int ent) { int i, n, written; char *t, *e, *f; char buf[10*1024]; written = 0; t = buf; e = buf+sizeof(buf); for(;ent ; v++, ent--){ n = v->iov_len; f = v->iov_base; while(n > 0){ i = e-t; if(n < i){ memmove(t, f, n); t += n; break; } memmove(t, f, i); n -= i; f += i; i = write(fd, buf, sizeof(buf)); if(i < 0){ if(written > 0){ return written; }else{ _syserrno(); return -1; } } written += i; if(i != sizeof(buf)) { return written; } t = buf; } } i = t - buf; if(i > 0){ n = write(fd, buf, i); if(n < 0){ if(written == 0){ _syserrno(); return -1; } } else written += n; } return written; } /sys/src/ape/lib/draw 20000000775 sys sys 1369861528 0 /sys/src/ape/lib/draw/colors.c 664 sys sys 1367613437 3607 #include #include #include #include char *argv0; static void _sysfatalimpl(char *fmt, va_list arg) { char buf[1024]; vseprint(buf, buf+sizeof(buf), fmt, arg); if(argv0) fprint(2, "%s: %s\n", argv0, buf); else fprint(2, "%s\n", buf); exits(buf); } void (*_sysfatal)(char *fmt, va_list arg) = _sysfatalimpl; void sysfatal(char *fmt, ...) { va_list arg; va_start(arg, fmt); (*_sysfatal)(fmt, arg); va_end(arg); } int nbit, npix; Image *pixel; Rectangle crect[256]; Image *color[256]; void eresized(int new) { int x, y, i, n, nx, ny; Rectangle r, b; if(new && getwindow(display, Refnone) < 0){ fprint(2, "colors: can't reattach to window: %r\n"); exits("resized"); } if(screen->depth > 8){ n = 256; nx = 16; }else{ n = 1<depth; nx = 1<<(screen->depth/2); } ny = n/nx; draw(screen, screen->r, display->white, nil, ZP); r = insetrect(screen->r, 5); r.min.y+=20; b.max.y=r.min.y; for(i=n-1, y=0; y!=ny; y++){ b.min.y=b.max.y; b.max.y=r.min.y+(r.max.y-r.min.y)*(y+1)/ny; b.max.x=r.min.x; for(x=0; x!=nx; x++, --i){ b.min.x=b.max.x; b.max.x=r.min.x+(r.max.x-r.min.x)*(x+1)/nx; crect[i]=insetrect(b, 1); draw(screen, crect[i], color[i], nil, ZP); } } flushimage(display, 1); } char *buttons[] = { "exit", 0 }; ulong grey(int i) { if(i < 0) return grey(0); if(i > 255) return grey(255); return (i<<16)+(i<<8)+i; } Menu menu = { buttons }; int dither[16] = { 0, 8, 2, 10, 12, 4, 14, 6, 3, 11, 1, 9, 15, 7, 13, 5 }; void main(int argc, char *argv[]) { Point p; Mouse m; int i, j, k, l, n, ramp, prev; char buf[100]; char *fmt; Image *dark; ulong rgb; ramp = 0; fmt = "index %3d r %3lud g %3lud b %3lud 0x%.8luX "; /* ARGBEGIN{ default: goto Usage; case 'x': fmt = "index %2luX r %3luX g %3luX b %3luX 0x%.8luX "; break; case 'r': ramp = 1; break; }ARGEND */ argv0 = argv[0]; if(argc != 1){ Usage: fprint(2, "Usage: %s [-rx]\n", argv0); exits("usage"); } if(initdraw(nil, nil, "colors") < 0) sysfatal("initdraw failed: %r"); einit(Emouse); for(i=0; i<256; i++){ if(ramp){ if(screen->chan == CMAP8){ /* dither the fine grey */ j = i-(i%17); dark = allocimage(display, Rect(0,0,1,1), screen->chan, 1, (grey(j)<<8)+0xFF); color[i] = allocimage(display, Rect(0,0,4,4), screen->chan, 1, (grey(j+17)<<8)+0xFF); for(j=0; j<16; j++){ k = j%4; l = j/4; if(dither[j] > (i%17)) draw(color[i], Rect(k, l, k+1, l+1), dark, nil, ZP); } freeimage(dark); }else color[i] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, (grey(i)<<8)+0xFF); }else color[i] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, (cmap2rgb(i)<<8)+0xFF); if(color[i] == nil) sysfatal("can't allocate image: %r"); } eresized(0); prev = -1; for(;;){ m = emouse(); switch(m.buttons){ case 1: while(m.buttons){ if(screen->depth > 8) n = 256; else n = 1<depth; for(i=0; i!=n; i++) if(i!=prev && ptinrect(m.xy, crect[i])){ if(ramp) rgb = grey(i); else rgb = cmap2rgb(i); sprint(buf, fmt, i, (rgb>>16)&0xFF, (rgb>>8)&0xFF, rgb&0xFF, (rgb<<8) | 0xFF); p = addpt(screen->r.min, Pt(2,2)); draw(screen, Rpt(p, addpt(p, stringsize(font, buf))), display->white, nil, p); string(screen, p, display->black, ZP, font, buf); prev=i; break; } m = emouse(); } break; case 4: switch(emenuhit(3, &m, &menu)){ case 0: exits(0); } } } } /sys/src/ape/lib/draw/mkfile 664 sys sys 1369844581 1167 APE=/sys/src/ape <$APE/config LIB=/$objtype/lib/ape/libdraw.a OFILES=\ alloc.$O\ allocimagemix.$O\ arith.$O\ bezier.$O\ border.$O\ buildfont.$O\ bytesperline.$O\ chan.$O\ cloadimage.$O\ computil.$O\ creadimage.$O\ debug.$O\ defont.$O\ draw.$O\ drawrepl.$O\ egetrect.$O\ ellipse.$O\ emenuhit.$O\ event.$O\ fmt.$O\ font.$O\ freesubfont.$O\ getdefont.$O\ getsubfont.$O\ icossin.$O\ icossin2.$O\ init.$O\ line.$O\ mkfont.$O\ newwindow.$O\ openfont.$O\ poly.$O\ loadimage.$O\ readimage.$O\ readsubfont.$O\ rectclip.$O\ replclipr.$O\ rgb.$O\ string.$O\ stringbg.$O\ stringsubfont.$O\ stringwidth.$O\ subfont.$O\ subfontcache.$O\ subfontname.$O\ unloadimage.$O\ window.$O\ writecolmap.$O\ writeimage.$O\ writesubfont.$O\ HFILES=\ /sys/include/ape/draw.h\ /sys/include/ape/event.h\ /sys/include/ape/mouse.h\ /sys/include/ape/keyboard.h\ UPDATE=\ mkfile\ $HFILES\ ${OFILES:%.$O=%.c}\ ${LIB:/$objtype/%=/386/%}\ #include #include "utf.h" #include "fmt.h" #include "fmtdef.h" /* * Reads a floating-point number by interpreting successive characters * returned by (*f)(vp). The last call it makes to f terminates the * scan, so is not a character in the number. It may therefore be * necessary to back up the input stream up one byte after calling charstod. */ double fmtcharstod(int(*f)(void*), void *vp) { double num, dem; int neg, eneg, dig, exp, c; num = 0; neg = 0; dig = 0; exp = 0; eneg = 0; c = (*f)(vp); while(c == ' ' || c == '\t') c = (*f)(vp); if(c == '-' || c == '+'){ if(c == '-') neg = 1; c = (*f)(vp); } while(c >= '0' && c <= '9'){ num = num*10 + c-'0'; c = (*f)(vp); } if(c == '.') c = (*f)(vp); while(c >= '0' && c <= '9'){ num = num*10 + c-'0'; dig++; c = (*f)(vp); } if(c == 'e' || c == 'E'){ c = (*f)(vp); if(c == '-' || c == '+'){ if(c == '-'){ dig = -dig; eneg = 1; } c = (*f)(vp); } while(c >= '0' && c <= '9'){ exp = exp*10 + c-'0'; c = (*f)(vp); } } exp -= dig; if(exp < 0){ exp = -exp; eneg = !eneg; } dem = __fmtpow10(exp); if(eneg) num /= dem; else num *= dem; if(neg) return -num; return num; } /sys/src/ape/lib/fmt/dofmt.c 664 sys sys 1369844762 10134 /* * The authors of this software are Rob Pike and Ken Thompson. * Copyright (c) 2002 by Lucent Technologies. * Permission to use, copy, modify, and distribute this software for any * purpose without fee is hereby granted, provided that this entire notice * is included in all copies of any software which is or includes a copy * or modification of this software and in all copies of the supporting * documentation for such software. * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. */ #include #include #define _SUSV2_SOURCE #include #include "utf.h" #include "fmt.h" #include "fmtdef.h" /* format the output into f->to and return the number of characters fmted */ int dofmt(Fmt *f, char *fmt) { Rune rune, *rt, *rs; int r; char *t, *s; int n, nfmt; nfmt = f->nfmt; for(;;){ if(f->runes){ rt = (Rune*)f->to; rs = (Rune*)f->stop; while((r = *(uchar*)fmt) && r != '%'){ if(r < Runeself) fmt++; else{ fmt += chartorune(&rune, fmt); r = rune; } FMTRCHAR(f, rt, rs, r); } fmt++; f->nfmt += rt - (Rune *)f->to; f->to = rt; if(!r) return f->nfmt - nfmt; f->stop = rs; }else{ t = (char*)f->to; s = (char*)f->stop; while((r = *(uchar*)fmt) && r != '%'){ if(r < Runeself){ FMTCHAR(f, t, s, r); fmt++; }else{ n = chartorune(&rune, fmt); if(t + n > s){ t = (char*)__fmtflush(f, t, n); if(t != nil) s = (char*)f->stop; else return -1; } while(n--) *t++ = *fmt++; } } fmt++; f->nfmt += t - (char *)f->to; f->to = t; if(!r) return f->nfmt - nfmt; f->stop = s; } fmt = (char*)__fmtdispatch(f, fmt, 0); if(fmt == nil) return -1; } } void * __fmtflush(Fmt *f, void *t, int len) { if(f->runes) f->nfmt += (Rune*)t - (Rune*)f->to; else f->nfmt += (char*)t - (char *)f->to; f->to = t; if(f->flush == 0 || (*f->flush)(f) == 0 || (char*)f->to + len > (char*)f->stop){ f->stop = f->to; return nil; } return f->to; } /* * put a formatted block of memory sz bytes long of n runes into the output buffer, * left/right justified in a field of at least f->width charactes */ int __fmtpad(Fmt *f, int n) { char *t, *s; int i; t = (char*)f->to; s = (char*)f->stop; for(i = 0; i < n; i++) FMTCHAR(f, t, s, ' '); f->nfmt += t - (char *)f->to; f->to = t; return 0; } int __rfmtpad(Fmt *f, int n) { Rune *t, *s; int i; t = (Rune*)f->to; s = (Rune*)f->stop; for(i = 0; i < n; i++) FMTRCHAR(f, t, s, ' '); f->nfmt += t - (Rune *)f->to; f->to = t; return 0; } int __fmtcpy(Fmt *f, const void *vm, int n, int sz) { Rune *rt, *rs, r; char *t, *s, *m, *me; ulong fl; int nc, w; m = (char*)vm; me = m + sz; w = f->width; fl = f->flags; if((fl & FmtPrec) && n > f->prec) n = f->prec; if(f->runes){ if(!(fl & FmtLeft) && __rfmtpad(f, w - n) < 0) return -1; rt = (Rune*)f->to; rs = (Rune*)f->stop; for(nc = n; nc > 0; nc--){ r = *(uchar*)m; if(r < Runeself) m++; else if((me - m) >= UTFmax || fullrune(m, me-m)) m += chartorune(&r, m); else break; FMTRCHAR(f, rt, rs, r); } f->nfmt += rt - (Rune *)f->to; f->to = rt; if(m < me) return -1; if(fl & FmtLeft && __rfmtpad(f, w - n) < 0) return -1; }else{ if(!(fl & FmtLeft) && __fmtpad(f, w - n) < 0) return -1; t = (char*)f->to; s = (char*)f->stop; for(nc = n; nc > 0; nc--){ r = *(uchar*)m; if(r < Runeself) m++; else if((me - m) >= UTFmax || fullrune(m, me-m)) m += chartorune(&r, m); else break; FMTRUNE(f, t, s, r); } f->nfmt += t - (char *)f->to; f->to = t; if(fl & FmtLeft && __fmtpad(f, w - n) < 0) return -1; } return 0; } int __fmtrcpy(Fmt *f, const void *vm, int n) { Rune r, *m, *me, *rt, *rs; char *t, *s; ulong fl; int w; m = (Rune*)vm; w = f->width; fl = f->flags; if((fl & FmtPrec) && n > f->prec) n = f->prec; if(f->runes){ if(!(fl & FmtLeft) && __rfmtpad(f, w - n) < 0) return -1; rt = (Rune*)f->to; rs = (Rune*)f->stop; for(me = m + n; m < me; m++) FMTRCHAR(f, rt, rs, *m); f->nfmt += rt - (Rune *)f->to; f->to = rt; if(fl & FmtLeft && __rfmtpad(f, w - n) < 0) return -1; }else{ if(!(fl & FmtLeft) && __fmtpad(f, w - n) < 0) return -1; t = (char*)f->to; s = (char*)f->stop; for(me = m + n; m < me; m++){ r = *m; FMTRUNE(f, t, s, r); } f->nfmt += t - (char *)f->to; f->to = t; if(fl & FmtLeft && __fmtpad(f, w - n) < 0) return -1; } return 0; } /* fmt out one character */ int __charfmt(Fmt *f) { char x[1]; x[0] = va_arg(f->args, int); f->prec = 1; return __fmtcpy(f, (const char*)x, 1, 1); } /* fmt out one rune */ int __runefmt(Fmt *f) { Rune x[1]; x[0] = va_arg(f->args, int); return __fmtrcpy(f, (const void*)x, 1); } /* public helper routine: fmt out a null terminated string already in hand */ int fmtstrcpy(Fmt *f, char *s) { int p, i; if(!s) return __fmtcpy(f, "", 5, 5); /* if precision is specified, make sure we don't wander off the end */ if(f->flags & FmtPrec){ p = f->prec; for(i = 0; i < p; i++) if(s[i] == 0) break; return __fmtcpy(f, s, utfnlen(s, i), i); /* BUG?: won't print a partial rune at end */ } return __fmtcpy(f, s, utflen(s), strlen(s)); } /* fmt out a null terminated utf string */ int __strfmt(Fmt *f) { char *s; s = va_arg(f->args, char *); return fmtstrcpy(f, s); } /* public helper routine: fmt out a null terminated rune string already in hand */ int fmtrunestrcpy(Fmt *f, Rune *s) { Rune *e; int n, p; if(!s) return __fmtcpy(f, "", 5, 5); /* if precision is specified, make sure we don't wander off the end */ if(f->flags & FmtPrec){ p = f->prec; for(n = 0; n < p; n++) if(s[n] == 0) break; }else{ for(e = s; *e; e++) ; n = e - s; } return __fmtrcpy(f, s, n); } /* fmt out a null terminated rune string */ int __runesfmt(Fmt *f) { Rune *s; s = va_arg(f->args, Rune *); return fmtrunestrcpy(f, s); } /* fmt a % */ int __percentfmt(Fmt *f) { Rune x[1]; x[0] = f->r; f->prec = 1; return __fmtrcpy(f, (const void*)x, 1); } /* fmt an integer */ int __ifmt(Fmt *f) { char buf[70], *p, *conv; uvlong vu; ulong u; uintptr_t pu; int neg, base, i, n, fl, w, isv; neg = 0; fl = f->flags; isv = 0; vu = 0; u = 0; /* * Unsigned verbs */ switch(f->r){ case 'o': case 'u': case 'x': case 'X': fl |= FmtUnsigned; break; } if(f->r == 'p'){ pu = va_arg(f->args, uintptr_t); if(sizeof(uintptr_t) == sizeof(uvlong)){ vu = pu; isv = 1; }else u = pu; f->r = 'x'; fl |= FmtUnsigned; }else if(fl & FmtVLong){ isv = 1; if(fl & FmtUnsigned) vu = va_arg(f->args, uvlong); else vu = va_arg(f->args, vlong); }else if(fl & FmtLong){ if(fl & FmtUnsigned) u = va_arg(f->args, ulong); else u = va_arg(f->args, long); }else if(fl & FmtByte){ if(fl & FmtUnsigned) u = (uchar)va_arg(f->args, int); else u = (char)va_arg(f->args, int); }else if(fl & FmtShort){ if(fl & FmtUnsigned) u = (ushort)va_arg(f->args, int); else u = (short)va_arg(f->args, int); }else{ if(fl & FmtUnsigned) u = va_arg(f->args, uint); else u = va_arg(f->args, int); } conv = "0123456789abcdef"; switch(f->r){ case 'd': case 'i': base = 10; break; case 'u': base = 10; break; case 'x': base = 16; break; case 'X': base = 16; conv = "0123456789ABCDEF"; break; case 'b': base = 2; break; case 'o': base = 8; break; default: return -1; } if(!(fl & FmtUnsigned)){ if(isv && (vlong)vu < 0){ vu = -(vlong)vu; neg = 1; }else if(!isv && (long)u < 0){ u = -(long)u; neg = 1; } }else{ fl &= ~(FmtSign|FmtSpace); /* no + for unsigned conversions */ } p = buf + sizeof buf - 1; n = 0; if(isv){ while(vu){ i = vu % base; vu /= base; if((fl & FmtComma) && n % 4 == 3){ *p-- = ','; n++; } *p-- = conv[i]; n++; } }else{ while(u){ i = u % base; u /= base; if((fl & FmtComma) && n % 4 == 3){ *p-- = ','; n++; } *p-- = conv[i]; n++; } } if(n == 0){ if(!(fl & FmtPrec) || f->prec != 0){ *p-- = '0'; n = 1; } fl &= ~FmtSharp; } for(w = f->prec; n < w && p > buf+3; n++) *p-- = '0'; if(neg || (fl & (FmtSign|FmtSpace))) n++; if(fl & FmtSharp){ if(base == 16) n += 2; else if(base == 8){ if(p[1] == '0') fl &= ~FmtSharp; else n++; } } if((fl & FmtZero) && !(fl & (FmtLeft|FmtPrec))){ for(w = f->width; n < w && p > buf+3; n++) *p-- = '0'; f->width = 0; } if(fl & FmtSharp){ if(base == 16) *p-- = f->r; if(base == 16 || base == 8) *p-- = '0'; } if(neg) *p-- = '-'; else if(fl & FmtSign) *p-- = '+'; else if(fl & FmtSpace) *p-- = ' '; f->flags &= ~FmtPrec; return __fmtcpy(f, p + 1, n, n); } int __countfmt(Fmt *f) { void *p; ulong fl; fl = f->flags; p = va_arg(f->args, void*); if(fl & FmtVLong){ *(vlong*)p = f->nfmt; }else if(fl & FmtLong){ *(long*)p = f->nfmt; }else if(fl & FmtByte){ *(char*)p = f->nfmt; }else if(fl & FmtShort){ *(short*)p = f->nfmt; }else{ *(int*)p = f->nfmt; } return 0; } int __flagfmt(Fmt *f) { switch(f->r){ case ',': f->flags |= FmtComma; break; case '-': f->flags |= FmtLeft; break; case '+': f->flags |= FmtSign; break; case '#': f->flags |= FmtSharp; break; case ' ': f->flags |= FmtSpace; break; case 'u': f->flags |= FmtUnsigned; break; case 'h': if(f->flags & FmtShort) f->flags |= FmtByte; f->flags |= FmtShort; break; case 'L': f->flags |= FmtLDouble; break; case 'l': if(f->flags & FmtLong) f->flags |= FmtVLong; f->flags |= FmtLong; break; } return 1; } /* default error format */ int __badfmt(Fmt *f) { char x[3]; x[0] = '%'; x[1] = f->r; x[2] = '%'; f->prec = 3; __fmtcpy(f, (const void*)x, 3, 3); return 0; } /sys/src/ape/lib/fmt/dorfmt.c 664 sys sys 1367613437 1576 /* * The authors of this software are Rob Pike and Ken Thompson. * Copyright (c) 2002 by Lucent Technologies. * Permission to use, copy, modify, and distribute this software for any * purpose without fee is hereby granted, provided that this entire notice * is included in all copies of any software which is or includes a copy * or modification of this software and in all copies of the supporting * documentation for such software. * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. */ #include #include #include "utf.h" #include "fmt.h" #include "fmtdef.h" /* format the output into f->to and return the number of characters fmted */ int dorfmt(Fmt *f, const Rune *fmt) { Rune *rt, *rs; int r; char *t, *s; int nfmt; nfmt = f->nfmt; for(;;){ if(f->runes){ rt = f->to; rs = f->stop; while((r = *fmt++) && r != '%'){ FMTRCHAR(f, rt, rs, r); } f->nfmt += rt - (Rune *)f->to; f->to = rt; if(!r) return f->nfmt - nfmt; f->stop = rs; }else{ t = f->to; s = f->stop; while((r = *fmt++) && r != '%'){ FMTRUNE(f, t, f->stop, r); } f->nfmt += t - (char *)f->to; f->to = t; if(!r) return f->nfmt - nfmt; f->stop = s; } fmt = __fmtdispatch(f, fmt, 1); if(fmt == nil) return -1; } return 0; /* not reached */ } /sys/src/ape/lib/fmt/errfmt.c 664 sys sys 1368489643 948 /* * The authors of this software are Rob Pike and Ken Thompson. * Copyright (c) 2002 by Lucent Technologies. * Permission to use, copy, modify, and distribute this software for any * purpose without fee is hereby granted, provided that this entire notice * is included in all copies of any software which is or includes a copy * or modification of this software and in all copies of the supporting * documentation for such software. * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. */ #include #include #include #include "utf.h" #include "fmt.h" #include "fmtdef.h" int __errfmt(Fmt *f) { char *s; s = strerror(errno); return fmtstrcpy(f, s); } /sys/src/ape/lib/fmt/fltfmt.c 664 sys sys 1369844812 10282 /* * The authors of this software are Rob Pike and Ken Thompson. * Copyright (c) 2002 by Lucent Technologies. * Permission to use, copy, modify, and distribute this software for any * purpose without fee is hereby granted, provided that this entire notice * is included in all copies of any software which is or includes a copy * or modification of this software and in all copies of the supporting * documentation for such software. * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. */ #include #include #include #include #include #include #include #include "fmt.h" #include "fmtdef.h" #include "nan.h" enum { FDEFLT = 6, NSIGNIF = 17 }; /* * first few powers of 10, enough for about 1/2 of the * total space for doubles. */ static double pows10[] = { 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22, 1e23, 1e24, 1e25, 1e26, 1e27, 1e28, 1e29, 1e30, 1e31, 1e32, 1e33, 1e34, 1e35, 1e36, 1e37, 1e38, 1e39, 1e40, 1e41, 1e42, 1e43, 1e44, 1e45, 1e46, 1e47, 1e48, 1e49, 1e50, 1e51, 1e52, 1e53, 1e54, 1e55, 1e56, 1e57, 1e58, 1e59, 1e60, 1e61, 1e62, 1e63, 1e64, 1e65, 1e66, 1e67, 1e68, 1e69, 1e70, 1e71, 1e72, 1e73, 1e74, 1e75, 1e76, 1e77, 1e78, 1e79, 1e80, 1e81, 1e82, 1e83, 1e84, 1e85, 1e86, 1e87, 1e88, 1e89, 1e90, 1e91, 1e92, 1e93, 1e94, 1e95, 1e96, 1e97, 1e98, 1e99, 1e100, 1e101, 1e102, 1e103, 1e104, 1e105, 1e106, 1e107, 1e108, 1e109, 1e110, 1e111, 1e112, 1e113, 1e114, 1e115, 1e116, 1e117, 1e118, 1e119, 1e120, 1e121, 1e122, 1e123, 1e124, 1e125, 1e126, 1e127, 1e128, 1e129, 1e130, 1e131, 1e132, 1e133, 1e134, 1e135, 1e136, 1e137, 1e138, 1e139, 1e140, 1e141, 1e142, 1e143, 1e144, 1e145, 1e146, 1e147, 1e148, 1e149, 1e150, 1e151, 1e152, 1e153, 1e154, 1e155, 1e156, 1e157, 1e158, 1e159, }; static double pow10(int n) { double d; int neg; neg = 0; if(n < 0){ if(n < DBL_MIN_10_EXP){ return 0.; } neg = 1; n = -n; }else if(n > DBL_MAX_10_EXP){ return HUGE_VAL; } if(n < (int)(sizeof(pows10)/sizeof(pows10[0]))) d = pows10[n]; else{ d = pows10[sizeof(pows10)/sizeof(pows10[0]) - 1]; for(;;){ n -= sizeof(pows10)/sizeof(pows10[0]) - 1; if(n < (int)(sizeof(pows10)/sizeof(pows10[0]))){ d *= pows10[n]; break; } d *= pows10[sizeof(pows10)/sizeof(pows10[0]) - 1]; } } if(neg){ return 1./d; } return d; } static int xadd(char *a, int n, int v) { char *b; int c; if(n < 0 || n >= NSIGNIF) return 0; for(b = a+n; b >= a; b--) { c = *b + v; if(c <= '9') { *b = c; return 0; } *b = '0'; v = 1; } *a = '1'; /* overflow adding */ return 1; } static int xsub(char *a, int n, int v) { char *b; int c; for(b = a+n; b >= a; b--) { c = *b - v; if(c >= '0') { *b = c; return 0; } *b = '9'; v = 1; } *a = '9'; /* underflow subtracting */ return 1; } static void xaddexp(char *p, int e) { char se[9]; int i; *p++ = 'e'; if(e < 0) { *p++ = '-'; e = -e; } i = 0; while(e) { se[i++] = e % 10 + '0'; e /= 10; } if(i == 0) { *p++ = '0'; } else { while(i > 0) *p++ = se[--i]; } *p = '\0'; } static char* xdodtoa(char *s1, double f, int chr, int prec, int *decpt, int *rsign) { char s2[NSIGNIF+10]; double g, h; int e, d, i; int c2, sign, oerr; if(chr == 'F') chr = 'f'; if(prec > NSIGNIF) prec = NSIGNIF; if(prec < 0) prec = 0; if(__isNaN(f)) { *decpt = 9999; *rsign = 0; strcpy(s1, "nan"); return &s1[3]; } sign = 0; if(f < 0) { f = -f; sign++; } *rsign = sign; if(__isInf(f, 1) || __isInf(f, -1)) { *decpt = 9999; strcpy(s1, "inf"); return &s1[3]; } e = 0; g = f; if(g != 0) { frexp(f, &e); e = (int)(e * .301029995664); if(e >= -150 && e <= +150) { d = 0; h = f; } else { d = e/2; h = f * pow10(-d); } g = h * pow10(d-e); while(g < 1) { e--; g = h * pow10(d-e); } while(g >= 10) { e++; g = h * pow10(d-e); } } /* * convert NSIGNIF digits and convert * back to get accuracy. */ for(i=0; i= NSIGNIF-2) { strcpy(s2, s1); d = e; s1[NSIGNIF-2] = '0'; s1[NSIGNIF-1] = '0'; xaddexp(s1+NSIGNIF, e-NSIGNIF+1); g = fmtstrtod(s1, nil); if(g == f) goto found; if(xadd(s1, NSIGNIF-3, 1)) { e++; xaddexp(s1+NSIGNIF, e-NSIGNIF+1); } g = fmtstrtod(s1, nil); if(g == f) goto found; strcpy(s1, s2); e = d; } /* * convert back so s1 gets exact answer */ for(d = 0; d < 10; d++) { xaddexp(s1+NSIGNIF, e-NSIGNIF+1); g = fmtstrtod(s1, nil); if(f > g) { if(xadd(s1, NSIGNIF-1, 1)) e--; continue; } if(f < g) { if(xsub(s1, NSIGNIF-1, 1)) e++; continue; } break; } found: errno = oerr; /* * sign */ /* * round & adjust 'f' digits */ c2 = prec + 1; if(chr == 'f'){ if(xadd(s1, c2+e, 5)) e++; c2 += e; if(c2 < 0){ c2 = 0; e = -prec - 1; } }else{ if(xadd(s1, c2, 5)) e++; } if(c2 > NSIGNIF){ c2 = NSIGNIF; } *decpt = e + 1; /* * terminate the converted digits */ s1[c2] = '\0'; return &s1[c2]; } /* * this function works like the standard dtoa, if you want it. */ #if 0 static char* __dtoa(double f, int mode, int ndigits, int *decpt, int *rsign, char **rve) { static char s2[NSIGNIF + 10]; char *es; int chr, prec; switch(mode) { /* like 'e' */ case 2: case 4: case 6: case 8: chr = 'e'; break; /* like 'g' */ case 0: case 1: default: chr = 'g'; break; /* like 'f' */ case 3: case 5: case 7: case 9: chr = 'f'; break; } if(chr != 'f' && ndigits){ ndigits--; } prec = ndigits; if(prec > NSIGNIF) prec = NSIGNIF; if(ndigits == 0) prec = NSIGNIF; es = xdodtoa(s2, f, chr, prec, decpt, rsign); /* * strip trailing 0 */ for(; es > s2 + 1; es--){ if(es[-1] != '0'){ break; } } *es = '\0'; if(rve != NULL) *rve = es; return s2; } #endif static int fmtzdotpad(Fmt *f, int n, int pt) { char *t, *s; int i; Rune *rt, *rs; if(f->runes){ rt = (Rune*)f->to; rs = (Rune*)f->stop; for(i = 0; i < n; i++){ if(i == pt){ FMTRCHAR(f, rt, rs, '.'); } FMTRCHAR(f, rt, rs, '0'); } f->nfmt += rt - (Rune*)f->to; f->to = rt; }else{ t = (char*)f->to; s = (char*)f->stop; for(i = 0; i < n; i++){ if(i == pt){ FMTCHAR(f, t, s, '.'); } FMTCHAR(f, t, s, '0'); } f->nfmt += t - (char *)f->to; f->to = t; } return 0; } int __efgfmt(Fmt *fmt) { double f; char s1[NSIGNIF+10]; int e, d, n; int c1, c2, c3, c4, ucase, sign, chr, prec, fl; f = va_arg(fmt->args, double); prec = FDEFLT; fl = fmt->flags; fmt->flags = 0; if(fl & FmtPrec) prec = fmt->prec; chr = fmt->r; ucase = 0; if(chr == 'E'){ chr = 'e'; ucase = 1; }else if(chr == 'F'){ chr = 'f'; ucase = 1; }else if(chr == 'G'){ chr = 'g'; ucase = 1; } if(prec > 0 && chr == 'g') prec--; if(prec < 0) prec = 0; xdodtoa(s1, f, chr, prec, &e, &sign); e--; if(*s1 == 'i' || *s1 == 'n'){ if(ucase){ if(*s1 == 'i'){ strcpy(s1, "INF"); }else{ strcpy(s1, "NAN"); } } fmt->flags = fl & (FmtWidth|FmtLeft); return __fmtcpy(fmt, (const void*)s1, 3, 3); } /* * copy into final place * c1 digits of leading '0' * c2 digits from conversion * c3 digits of trailing '0' * c4 digits after '.' */ c1 = 0; c2 = prec + 1; c3 = 0; c4 = prec; switch(chr) { default: chr = 'e'; break; case 'g': /* * decide on 'e' of 'f' style convers */ if(e >= -4 && e <= prec) { c1 = -e; c4 = prec - e; chr = 'h'; /* flag for 'f' style */ } break; case 'f': c1 = -e; if(c1 > prec) c1 = prec + 1; c2 += e; break; } /* * clean up c1 c2 and c3 */ if(c1 < 0) c1 = 0; if(c2 < 0) c2 = 0; if(c2 > NSIGNIF) { c3 = c2-NSIGNIF; c2 = NSIGNIF; } /* * trim trailing zeros for %g */ if(!(fl & FmtSharp) && (chr == 'g' || chr == 'h')){ if(c4 >= c3){ c4 -= c3; c3 = 0; }else{ c3 -= c4; c4 = 0; } while(c4 && c2 > 1 && s1[c2 - 1] == '0'){ c4--; c2--; } } /* * calculate the total length */ n = c1 + c2 + c3; if(sign || (fl & (FmtSign|FmtSpace))) n++; if(c4 || (fl & FmtSharp)){ n++; } if(chr == 'e' || chr == 'g'){ n += 4; if(e >= 100) n++; } /* * pad to width if right justified */ if((fl & (FmtWidth|FmtLeft)) == FmtWidth && n < fmt->width){ if(fl & FmtZero){ c1 += fmt->width - n; }else{ if(__fmtpad(fmt, fmt->width - n) < 0){ return -1; } } } /* * sign */ d = 0; if(sign) d = '-'; else if(fl & FmtSign) d = '+'; else if(fl & FmtSpace) d = ' '; if(d && fmtrune(fmt, d) < 0){ return -1; } /* * copy digits */ c4 = c1 + c2 + c3 - c4; if(c1 > 0){ if(fmtzdotpad(fmt, c1, c4) < 0){ return -1; } c4 -= c1; } d = 0; if(c4 >= 0 && c4 < c2){ if(__fmtcpy(fmt, s1, c4, c4) < 0 || fmtrune(fmt, '.') < 0) return -1; d = c4; c2 -= c4; c4 = -1; } if(__fmtcpy(fmt, (const void*)(s1 + d), c2, c2) < 0){ return -1; } c4 -= c2; if(c3 > 0){ if(fmtzdotpad(fmt, c3, c4) < 0){ return -1; } c4 -= c3; } /* * strip trailing '0' on g conv */ if((fl & FmtSharp) && c4 == 0 && fmtrune(fmt, '.') < 0){ return -1; } if(chr == 'e' || chr == 'g') { d = 0; if(ucase) s1[d++] = 'E'; else s1[d++] = 'e'; c1 = e; if(c1 < 0) { s1[d++] = '-'; c1 = -c1; } else s1[d++] = '+'; if(c1 >= 100) { s1[d++] = c1/100 + '0'; c1 = c1%100; } s1[d++] = c1/10 + '0'; s1[d++] = c1%10 + '0'; if(__fmtcpy(fmt, s1, d, d) < 0){ return -1; } } if((fl & (FmtWidth|FmtLeft)) == (FmtWidth|FmtLeft) && n < fmt->width){ if(__fmtpad(fmt, fmt->width - n) < 0){ return -1; } } return 0; } /sys/src/ape/lib/fmt/fmt.c 664 sys sys 1368489651 4310 /* * The authors of this software are Rob Pike and Ken Thompson. * Copyright (c) 2002 by Lucent Technologies. * Permission to use, copy, modify, and distribute this software for any * purpose without fee is hereby granted, provided that this entire notice * is included in all copies of any software which is or includes a copy * or modification of this software and in all copies of the supporting * documentation for such software. * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. */ #include #include #include "utf.h" #include "fmt.h" #include "fmtdef.h" enum { Maxfmt = 64 }; typedef struct Convfmt Convfmt; struct Convfmt { int c; volatile Fmts fmt; /* for spin lock in fmtfmt; avoids race due to write order */ }; struct { /* lock by calling __fmtlock, __fmtunlock */ int nfmt; Convfmt fmt[Maxfmt]; } fmtalloc; static Convfmt knownfmt[] = { ' ', __flagfmt, '#', __flagfmt, '%', __percentfmt, '+', __flagfmt, ',', __flagfmt, '-', __flagfmt, 'C', __runefmt, /* Plan 9 addition */ 'E', __efgfmt, 'F', __efgfmt, /* ANSI only */ 'G', __efgfmt, 'L', __flagfmt, /* ANSI only */ 'S', __runesfmt, /* Plan 9 addition */ 'X', __ifmt, 'b', __ifmt, /* Plan 9 addition */ 'c', __charfmt, 'd', __ifmt, 'e', __efgfmt, 'f', __efgfmt, 'g', __efgfmt, 'h', __flagfmt, 'i', __ifmt, /* ANSI only */ 'l', __flagfmt, 'n', __countfmt, 'o', __ifmt, 'p', __ifmt, 'r', __errfmt, 's', __strfmt, 'u', __flagfmt, /* in Unix, __ifmt */ 'x', __ifmt, 0, nil, }; int (*fmtdoquote)(int); /* * __fmtlock() must be set */ static int __fmtinstall(int c, Fmts f) { Convfmt *p, *ep; if(c<=0 || c>Runemax) return -1; if(!f) f = __badfmt; ep = &fmtalloc.fmt[fmtalloc.nfmt]; for(p=fmtalloc.fmt; pc == c) break; if(p == &fmtalloc.fmt[Maxfmt]) return -1; p->fmt = f; if(p == ep){ /* installing a new format character */ fmtalloc.nfmt++; p->c = c; } return 0; } int fmtinstall(int c, Fmts f) { int ret; __fmtlock(); ret = __fmtinstall(c, f); __fmtunlock(); return ret; } static Fmts fmtfmt(int c) { Convfmt *p, *ep; ep = &fmtalloc.fmt[fmtalloc.nfmt]; for(p=fmtalloc.fmt; pc == c){ while(p->fmt == nil) /* loop until value is updated */ ; return p->fmt; } /* is this a predefined format char? */ __fmtlock(); for(p=knownfmt; p->c; p++) if(p->c == c){ __fmtinstall(p->c, p->fmt); __fmtunlock(); return p->fmt; } __fmtunlock(); return __badfmt; } void* __fmtdispatch(Fmt *f, void *fmt, int isrunes) { Rune rune, r; int i, n, w, p; ulong fl; void *ret; w = f->width; p = f->prec; fl = f->flags; f->flags = 0; f->width = f->prec = 0; for(;;){ if(isrunes){ r = *(Rune*)fmt; fmt = (Rune*)fmt + 1; }else{ fmt = (char*)fmt + chartorune(&rune, (char*)fmt); r = rune; } f->r = r; switch(r){ case '\0': ret = nil; goto end; case '.': f->flags |= FmtWidth|FmtPrec; continue; case '0': if(!(f->flags & FmtWidth)){ f->flags |= FmtZero; continue; } /* fall through */ case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': i = 0; while(r >= '0' && r <= '9'){ i = i * 10 + r - '0'; if(isrunes){ r = *(Rune*)fmt; fmt = (Rune*)fmt + 1; }else{ r = *(char*)fmt; fmt = (char*)fmt + 1; } } if(isrunes) fmt = (Rune*)fmt - 1; else fmt = (char*)fmt - 1; numflag: if(f->flags & FmtWidth){ f->flags |= FmtPrec; f->prec = i; }else{ f->flags |= FmtWidth; f->width = i; } continue; case '*': i = va_arg(f->args, int); if(i < 0){ /* * negative precision => * ignore the precision. */ if(f->flags & FmtPrec){ f->flags &= ~FmtPrec; f->prec = 0; continue; } i = -i; f->flags |= FmtLeft; } goto numflag; } n = (*fmtfmt(r))(f); if(n < 0){ ret = nil; break; } if(n == 0){ ret = fmt; break; } } end: f->width = w; f->prec = p; f->flags = fl; return ret; } /sys/src/ape/lib/fmt/fmtdef.h 664 sys sys 1369845322 3075 /* * The authors of this software are Rob Pike and Ken Thompson. * Copyright (c) 2002 by Lucent Technologies. * Permission to use, copy, modify, and distribute this software for any * purpose without fee is hereby granted, provided that this entire notice * is included in all copies of any software which is or includes a copy * or modification of this software and in all copies of the supporting * documentation for such software. * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. */ /* * dofmt -- format to a buffer * the number of characters formatted is returned, * or -1 if there was an error. * if the buffer is ever filled, flush is called. * it should reset the buffer and return whether formatting should continue. */ #define uchar _fmtuchar #define ushort _fmtushort #define uint _fmtuint #define ulong _fmtulong #define vlong _fmtvlong #define uvlong _fmtuvlong typedef unsigned char uchar; typedef unsigned short ushort; typedef unsigned int uint; typedef unsigned long ulong; #ifndef NOVLONGS typedef unsigned long long uvlong; typedef long long vlong; #endif /* #define nil 0 /* cannot be ((void*)0) because used for function pointers */ typedef int (*Fmts)(Fmt*); typedef struct Quoteinfo Quoteinfo; struct Quoteinfo { int quoted; /* if set, string must be quoted */ int nrunesin; /* number of input runes that can be accepted */ int nbytesin; /* number of input bytes that can be accepted */ int nrunesout; /* number of runes that will be generated */ int nbytesout; /* number of bytes that will be generated */ }; void *__fmtflush(Fmt*, void*, int); void *__fmtdispatch(Fmt*, void*, int); int __floatfmt(Fmt*, double); int __fmtpad(Fmt*, int); int __rfmtpad(Fmt*, int); int __fmtFdFlush(Fmt*); int __efgfmt(Fmt*); int __charfmt(Fmt*); int __runefmt(Fmt*); int __runesfmt(Fmt*); int __countfmt(Fmt*); int __flagfmt(Fmt*); int __percentfmt(Fmt*); int __ifmt(Fmt*); int __strfmt(Fmt*); int __badfmt(Fmt*); int __fmtcpy(Fmt*, const void*, int, int); int __fmtrcpy(Fmt*, const void*, int n); int __errfmt(Fmt *f); double __fmtpow10(int); void __fmtlock(void); void __fmtunlock(void); #define FMTCHAR(f, t, s, c)\ do{\ if(t + 1 > (char*)s){\ t = __fmtflush(f, t, 1);\ if(t != nil)\ s = f->stop;\ else\ return -1;\ }\ *t++ = c;\ }while(0) #define FMTRCHAR(f, t, s, c)\ do{\ if(t + 1 > (Rune*)s){\ t = __fmtflush(f, t, sizeof(Rune));\ if(t != nil)\ s = f->stop;\ else\ return -1;\ }\ *t++ = c;\ }while(0) #define FMTRUNE(f, t, s, r)\ do{\ Rune _rune;\ int _runelen;\ if(t + UTFmax > (char*)s && t + (_runelen = runelen(r)) > (char*)s){\ t = __fmtflush(f, t, _runelen);\ if(t != nil)\ s = f->stop;\ else\ return -1;\ }\ if(r < Runeself)\ *t++ = r;\ else{\ _rune = r;\ t += runetochar(t, &_rune);\ }\ }while(0) /sys/src/ape/lib/fmt/fmtfd.c 664 sys sys 1368489657 1302 /* * The authors of this software are Rob Pike and Ken Thompson. * Copyright (c) 2002 by Lucent Technologies. * Permission to use, copy, modify, and distribute this software for any * purpose without fee is hereby granted, provided that this entire notice * is included in all copies of any software which is or includes a copy * or modification of this software and in all copies of the supporting * documentation for such software. * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. */ #include #include #include "utf.h" #include "fmt.h" #include "fmtdef.h" /* * public routine for final flush of a formatting buffer * to a file descriptor; returns total char count. */ int fmtfdflush(Fmt *f) { if(__fmtFdFlush(f) <= 0) return -1; return f->nfmt; } /* * initialize an output buffer for buffered printing */ int fmtfdinit(Fmt *f, int fd, char *buf, int size) { f->runes = 0; f->start = buf; f->to = buf; f->stop = buf + size; f->flush = __fmtFdFlush; f->farg = (void*)fd; f->nfmt = 0; return 0; } /sys/src/ape/lib/fmt/fmtfdflush.c 664 sys sys 1369845388 1132 /* * The authors of this software are Rob Pike and Ken Thompson. * Copyright (c) 2002 by Lucent Technologies. * Permission to use, copy, modify, and distribute this software for any * purpose without fee is hereby granted, provided that this entire notice * is included in all copies of any software which is or includes a copy * or modification of this software and in all copies of the supporting * documentation for such software. * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. */ #include #include #define _SUSV2_SOURCE #include #include "fmt.h" #include "fmtdef.h" /* * generic routine for flushing a formatting buffer * to a file descriptor */ int __fmtFdFlush(Fmt *f) { int n; n = (char*)f->to - (char*)f->start; if(n && write((int)(uintptr_t)f->farg, f->start, n) != n) return 0; f->to = f->start; return 1; } /sys/src/ape/lib/fmt/fmtlock.c 664 sys sys 1367613437 868 /* * The authors of this software are Rob Pike and Ken Thompson. * Copyright (c) 2002 by Lucent Technologies. * Permission to use, copy, modify, and distribute this software for any * purpose without fee is hereby granted, provided that this entire notice * is included in all copies of any software which is or includes a copy * or modification of this software and in all copies of the supporting * documentation for such software. * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. */ #include #include "fmt.h" #include "fmtdef.h" void __fmtlock(void) { ; } void __fmtunlock(void) { ; } /sys/src/ape/lib/fmt/fmtprint.c 664 sys sys 1369166818 1082 /* * The authors of this software are Rob Pike and Ken Thompson. * Copyright (c) 2002 by Lucent Technologies. * Permission to use, copy, modify, and distribute this software for any * purpose without fee is hereby granted, provided that this entire notice * is included in all copies of any software which is or includes a copy * or modification of this software and in all copies of the supporting * documentation for such software. * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. */ #include #include #include "utf.h" #include "fmt.h" #include "fmtdef.h" /* * format a string into the output buffer * designed for formats which themselves call fmt */ int fmtprint(Fmt *f, char *fmt, ...) { va_list va; int n; va_start(va, fmt); n = fmtvprint(f, fmt, va); va_end(va); return n; } /sys/src/ape/lib/fmt/fmtquote.c 664 sys sys 1368489675 5582 /* * The authors of this software are Rob Pike and Ken Thompson. * Copyright (c) 2002 by Lucent Technologies. * Permission to use, copy, modify, and distribute this software for any * purpose without fee is hereby granted, provided that this entire notice * is included in all copies of any software which is or includes a copy * or modification of this software and in all copies of the supporting * documentation for such software. * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. */ #include #include #include "utf.h" #include "fmt.h" #include "fmtdef.h" /* * How many bytes of output UTF will be produced by quoting (if necessary) this string? * How many runes? How much of the input will be consumed? * The parameter q is filled in by __quotesetup. * The string may be UTF or Runes (s or r). * Return count does not include NUL. * Terminate the scan at the first of: * NUL in input * count exceeded in input * count exceeded on output * *ninp is set to number of input bytes accepted. * nin may be <0 initially, to avoid checking input by count. */ void __quotesetup(char *s, Rune *r, int nin, int nout, Quoteinfo *q, int sharp, int runesout) { int w; Rune c; q->quoted = 0; q->nbytesout = 0; q->nrunesout = 0; q->nbytesin = 0; q->nrunesin = 0; if(sharp || nin==0 || (s && *s=='\0') || (r && *r=='\0')){ if(nout < 2) return; q->quoted = 1; q->nbytesout = 2; q->nrunesout = 2; } for(; nin!=0; nin-=w){ if(s) w = chartorune(&c, s); else{ c = *r; w = runelen(c); } if(c == '\0') break; if(runesout){ if(q->nrunesout+1 > nout) break; }else{ if(q->nbytesout+w > nout) break; } if((c <= L' ') || (c == L'\'') || (fmtdoquote!=nil && fmtdoquote(c))){ if(!q->quoted){ if(runesout){ if(1+q->nrunesout+1+1 > nout) /* no room for quotes */ break; }else{ if(1+q->nbytesout+w+1 > nout) /* no room for quotes */ break; } q->nrunesout += 2; /* include quotes */ q->nbytesout += 2; /* include quotes */ q->quoted = 1; } if(c == '\'') { if(runesout){ if(1+q->nrunesout+1 > nout) /* no room for quotes */ break; }else{ if(1+q->nbytesout+w > nout) /* no room for quotes */ break; } q->nbytesout++; q->nrunesout++; /* quotes reproduce as two characters */ } } /* advance input */ if(s) s += w; else r++; q->nbytesin += w; q->nrunesin++; /* advance output */ q->nbytesout += w; q->nrunesout++; } } static int qstrfmt(char *sin, Rune *rin, Quoteinfo *q, Fmt *f) { Rune r, *rm, *rme; char *t, *s, *m, *me; Rune *rt, *rs; ulong fl; int nc, w; m = sin; me = m + q->nbytesin; rm = rin; rme = rm + q->nrunesin; w = f->width; fl = f->flags; if(f->runes){ if(!(fl & FmtLeft) && __rfmtpad(f, w - q->nrunesout) < 0) return -1; }else{ if(!(fl & FmtLeft) && __fmtpad(f, w - q->nbytesout) < 0) return -1; } t = (char*)f->to; s = (char*)f->stop; rt = (Rune*)f->to; rs = (Rune*)f->stop; if(f->runes) FMTRCHAR(f, rt, rs, '\''); else FMTRUNE(f, t, s, '\''); for(nc = q->nrunesin; nc > 0; nc--){ if(sin){ r = *(uchar*)m; if(r < Runeself) m++; else if((me - m) >= UTFmax || fullrune(m, me-m)) m += chartorune(&r, m); else break; }else{ if(rm >= rme) break; r = *(uchar*)rm++; } if(f->runes){ FMTRCHAR(f, rt, rs, r); if(r == '\'') FMTRCHAR(f, rt, rs, r); }else{ FMTRUNE(f, t, s, r); if(r == '\'') FMTRUNE(f, t, s, r); } } if(f->runes){ FMTRCHAR(f, rt, rs, '\''); USED(rs); f->nfmt += rt - (Rune *)f->to; f->to = rt; if(fl & FmtLeft && __rfmtpad(f, w - q->nrunesout) < 0) return -1; }else{ FMTRUNE(f, t, s, '\''); USED(s); f->nfmt += t - (char *)f->to; f->to = t; if(fl & FmtLeft && __fmtpad(f, w - q->nbytesout) < 0) return -1; } return 0; } int __quotestrfmt(int runesin, Fmt *f) { int outlen; Rune *r; char *s; Quoteinfo q; f->flags &= ~FmtPrec; /* ignored for %q %Q, so disable for %s %S in easy case */ if(runesin){ r = va_arg(f->args, Rune *); s = nil; }else{ s = va_arg(f->args, char *); r = nil; } if(!s && !r) return __fmtcpy(f, (void*)"", 5, 5); if(f->flush) outlen = 0x7FFFFFFF; /* if we can flush, no output limit */ else if(f->runes) outlen = (Rune*)f->stop - (Rune*)f->to; else outlen = (char*)f->stop - (char*)f->to; __quotesetup(s, r, -1, outlen, &q, f->flags&FmtSharp, f->runes); //print("bytes in %d bytes out %d runes in %d runesout %d\n", q.nbytesin, q.nbytesout, q.nrunesin, q.nrunesout); if(runesin){ if(!q.quoted) return __fmtrcpy(f, r, q.nrunesin); return qstrfmt(nil, r, &q, f); } if(!q.quoted) return __fmtcpy(f, s, q.nrunesin, q.nbytesin); return qstrfmt(s, nil, &q, f); } int quotestrfmt(Fmt *f) { return __quotestrfmt(0, f); } int quoterunestrfmt(Fmt *f) { return __quotestrfmt(1, f); } void quotefmtinstall(void) { fmtinstall('q', quotestrfmt); fmtinstall('Q', quoterunestrfmt); } int __needsquotes(char *s, int *quotelenp) { Quoteinfo q; __quotesetup(s, nil, -1, 0x7FFFFFFF, &q, 0, 0); *quotelenp = q.nbytesout; return q.quoted; } int __runeneedsquotes(Rune *r, int *quotelenp) { Quoteinfo q; __quotesetup(nil, r, -1, 0x7FFFFFFF, &q, 0, 0); *quotelenp = q.nrunesout; return q.quoted; } /sys/src/ape/lib/fmt/fmtrune.c 664 sys sys 1368489682 1120 /* * The authors of this software are Rob Pike and Ken Thompson. * Copyright (c) 2002 by Lucent Technologies. * Permission to use, copy, modify, and distribute this software for any * purpose without fee is hereby granted, provided that this entire notice * is included in all copies of any software which is or includes a copy * or modification of this software and in all copies of the supporting * documentation for such software. * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. */ #include #include #include "utf.h" #include "fmt.h" #include "fmtdef.h" int fmtrune(Fmt *f, int r) { Rune *rt; char *t; int n; if(f->runes){ rt = (Rune*)f->to; FMTRCHAR(f, rt, f->stop, r); f->to = rt; n = 1; }else{ t = (char*)f->to; FMTRUNE(f, t, f->stop, r); n = t - (char*)f->to; f->to = t; } f->nfmt += n; return 0; } /sys/src/ape/lib/fmt/fmtstr.c 664 sys sys 1369849889 1564 /* * The authors of this software are Rob Pike and Ken Thompson. * Copyright (c) 2002 by Lucent Technologies. * Permission to use, copy, modify, and distribute this software for any * purpose without fee is hereby granted, provided that this entire notice * is included in all copies of any software which is or includes a copy * or modification of this software and in all copies of the supporting * documentation for such software. * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. */ #include #include #include #define _SUSV2_SOURCE #include #include "utf.h" #include "fmt.h" #include "fmtdef.h" static int fmtStrFlush(Fmt *f) { char *s; int n; n = (int)(uintptr_t)f->farg; n += 256; f->farg = (void*)n; s = (char*)f->start; f->start = realloc(s, n); if(f->start == nil){ f->start = s; return 0; } f->to = (char*)f->start + ((char*)f->to - s); f->stop = (char*)f->start + n - 1; return 1; } int fmtstrinit(Fmt *f) { int n; f->runes = 0; n = 32; f->start = malloc(n); if(f->start == nil) return -1; f->to = f->start; f->stop = (char*)f->start + n - 1; f->flush = fmtStrFlush; f->farg = (void*)n; f->nfmt = 0; return 0; } char* fmtstrflush(Fmt *f) { *(char*)f->to = '\0'; f->to = f->start; return (char*)f->start; } /sys/src/ape/lib/fmt/fmtvprint.c 664 sys sys 1367613437 1122 /* * The authors of this software are Rob Pike and Ken Thompson. * Copyright (c) 2002 by Lucent Technologies. * Permission to use, copy, modify, and distribute this software for any * purpose without fee is hereby granted, provided that this entire notice * is included in all copies of any software which is or includes a copy * or modification of this software and in all copies of the supporting * documentation for such software. * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. */ #include #include #include "utf.h" #include "fmt.h" #include "fmtdef.h" /* * format a string into the output buffer * designed for formats which themselves call fmt */ int fmtvprint(Fmt *f, char *fmt, va_list args) { va_list va; int n; va = f->args; f->args = args; n = dofmt(f, fmt); f->args = va; if(n >= 0) return 0; return n; } /sys/src/ape/lib/fmt/fprint.c 664 sys sys 1367613437 946 /* * The authors of this software are Rob Pike and Ken Thompson. * Copyright (c) 2002 by Lucent Technologies. * Permission to use, copy, modify, and distribute this software for any * purpose without fee is hereby granted, provided that this entire notice * is included in all copies of any software which is or includes a copy * or modification of this software and in all copies of the supporting * documentation for such software. * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. */ #include #include "utf.h" #include "fmt.h" int fprint(int fd, char *fmt, ...) { int n; va_list args; va_start(args, fmt); n = vfprint(fd, fmt, args); va_end(args); return n; } /sys/src/ape/lib/fmt/mkfile 664 sys sys 1369861376 798 APE=/sys/src/ape <$APE/config LIB=/$objtype/lib/ape/libfmt.a NUM=\ charstod.$O\ pow10.$O\ OFILES=\ dofmt.$O\ errfmt.$O\ fltfmt.$O\ fmt.$O\ fmtfd.$O\ fmtfdflush.$O\ fmtlock.$O\ fmtprint.$O\ fmtquote.$O\ fmtrune.$O\ fmtstr.$O\ fmtvprint.$O\ fprint.$O\ nan64.$O\ print.$O\ runefmtstr.$O\ runeseprint.$O\ runesmprint.$O\ runesnprint.$O\ runesprint.$O\ runevseprint.$O\ runevsmprint.$O\ runevsnprint.$O\ seprint.$O\ smprint.$O\ snprint.$O\ sprint.$O\ strtod.$O\ vfprint.$O\ vseprint.$O\ vsmprint.$O\ vsnprint.$O\ werrstr.$O\ $NUM\ HFILES=\ fmtdef.h\ /sys/include/ape/fmt.h\ >32)==0x7FF00000 && !__isInf(d, 0); } double __Inf(int sign) { uvlong *p; if(sign < 0) p = &uvinf; else p = &uvneginf; return *(double*)p; } int __isInf(double d, int sign) { uvlong x; double *p; p = &d; x = *(uvlong*)p; if(sign == 0) return x==uvinf || x==uvneginf; else if(sign > 0) return x==uvinf; else return x==uvneginf; } /sys/src/ape/lib/fmt/pow10.c 664 sys sys 1367613437 1987 /* * The authors of this software are Rob Pike and Ken Thompson. * Copyright (c) 2002 by Lucent Technologies. * Permission to use, copy, modify, and distribute this software for any * purpose without fee is hereby granted, provided that this entire notice * is included in all copies of any software which is or includes a copy * or modification of this software and in all copies of the supporting * documentation for such software. * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. */ #include #include #include "utf.h" #include "fmt.h" #include "fmtdef.h" /* * this table might overflow 127-bit exponent representations. * in that case, truncate it after 1.0e38. * it is important to get all one can from this * routine since it is used in atof to scale numbers. * the presumption is that C converts fp numbers better * than multipication of lower powers of 10. */ static double tab[] = { 1.0e0, 1.0e1, 1.0e2, 1.0e3, 1.0e4, 1.0e5, 1.0e6, 1.0e7, 1.0e8, 1.0e9, 1.0e10,1.0e11,1.0e12,1.0e13,1.0e14,1.0e15,1.0e16,1.0e17,1.0e18,1.0e19, 1.0e20,1.0e21,1.0e22,1.0e23,1.0e24,1.0e25,1.0e26,1.0e27,1.0e28,1.0e29, 1.0e30,1.0e31,1.0e32,1.0e33,1.0e34,1.0e35,1.0e36,1.0e37,1.0e38,1.0e39, 1.0e40,1.0e41,1.0e42,1.0e43,1.0e44,1.0e45,1.0e46,1.0e47,1.0e48,1.0e49, 1.0e50,1.0e51,1.0e52,1.0e53,1.0e54,1.0e55,1.0e56,1.0e57,1.0e58,1.0e59, 1.0e60,1.0e61,1.0e62,1.0e63,1.0e64,1.0e65,1.0e66,1.0e67,1.0e68,1.0e69, }; double __fmtpow10(int n) { int m; if(n < 0) { n = -n; if(n < (int)(sizeof(tab)/sizeof(tab[0]))) return 1/tab[n]; m = n/2; return __fmtpow10(-m) * __fmtpow10(m-n); } if(n < (int)(sizeof(tab)/sizeof(tab[0]))) return tab[n]; m = n/2; return __fmtpow10(m) * __fmtpow10(n-m); } /sys/src/ape/lib/fmt/print.c 664 sys sys 1367613437 936 /* * The authors of this software are Rob Pike and Ken Thompson. * Copyright (c) 2002 by Lucent Technologies. * Permission to use, copy, modify, and distribute this software for any * purpose without fee is hereby granted, provided that this entire notice * is included in all copies of any software which is or includes a copy * or modification of this software and in all copies of the supporting * documentation for such software. * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. */ #include #include "utf.h" #include "fmt.h" int print(char *fmt, ...) { int n; va_list args; va_start(args, fmt); n = vfprint(1, fmt, args); va_end(args); return n; } /sys/src/ape/lib/fmt/runefmtstr.c 664 sys sys 1369846587 1599 /* * The authors of this software are Rob Pike and Ken Thompson. * Copyright (c) 2002 by Lucent Technologies. * Permission to use, copy, modify, and distribute this software for any * purpose without fee is hereby granted, provided that this entire notice * is included in all copies of any software which is or includes a copy * or modification of this software and in all copies of the supporting * documentation for such software. * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. */ #include #include #include #define _SUSV2_SOURCE #include #include "utf.h" #include "fmt.h" #include "fmtdef.h" static int runeFmtStrFlush(Fmt *f) { Rune *s; int n; n = (int)(uintptr_t)f->farg; n += 256; f->farg = (void*)n; s = (Rune*)f->start; f->start = realloc(s, sizeof(Rune)*n); if(f->start == nil){ f->start = s; return 0; } f->to = (Rune*)f->start + ((Rune*)f->to - s); f->stop = (Rune*)f->start + n - 1; return 1; } int runefmtstrinit(Fmt *f) { int n; f->runes = 1; n = 32; f->start = malloc(sizeof(Rune)*n); if(f->start == nil) return -1; f->to = f->start; f->stop = (Rune*)f->start + n - 1; f->flush = runeFmtStrFlush; f->farg = (void*)n; f->nfmt = 0; return 0; } Rune* runefmtstrflush(Fmt *f) { *(Rune*)f->to = '\0'; f->to = f->start; return f->start; } /sys/src/ape/lib/fmt/runeseprint.c 664 sys sys 1367613437 1016 /* * The authors of this software are Rob Pike and Ken Thompson. * Copyright (c) 2002 by Lucent Technologies. * Permission to use, copy, modify, and distribute this software for any * purpose without fee is hereby granted, provided that this entire notice * is included in all copies of any software which is or includes a copy * or modification of this software and in all copies of the supporting * documentation for such software. * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. */ #include #include #include "utf.h" #include "fmt.h" #include "fmtdef.h" Rune* runeseprint(Rune *buf, Rune *e, char *fmt, ...) { Rune *p; va_list args; va_start(args, fmt); p = runevseprint(buf, e, fmt, args); va_end(args); return p; } /sys/src/ape/lib/fmt/runesmprint.c 664 sys sys 1367613437 988 /* * The authors of this software are Rob Pike and Ken Thompson. * Copyright (c) 2002 by Lucent Technologies. * Permission to use, copy, modify, and distribute this software for any * purpose without fee is hereby granted, provided that this entire notice * is included in all copies of any software which is or includes a copy * or modification of this software and in all copies of the supporting * documentation for such software. * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. */ #include #include #include "utf.h" #include "fmt.h" #include "fmtdef.h" Rune* runesmprint(char *fmt, ...) { va_list args; Rune *p; va_start(args, fmt); p = runevsmprint(fmt, args); va_end(args); return p; } /sys/src/ape/lib/fmt/runesnprint.c 664 sys sys 1367613437 1015 /* * The authors of this software are Rob Pike and Ken Thompson. * Copyright (c) 2002 by Lucent Technologies. * Permission to use, copy, modify, and distribute this software for any * purpose without fee is hereby granted, provided that this entire notice * is included in all copies of any software which is or includes a copy * or modification of this software and in all copies of the supporting * documentation for such software. * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. */ #include #include #include "utf.h" #include "fmt.h" #include "fmtdef.h" int runesnprint(Rune *buf, int len, char *fmt, ...) { int n; va_list args; va_start(args, fmt); n = runevsnprint(buf, len, fmt, args); va_end(args); return n; } /sys/src/ape/lib/fmt/runesprint.c 664 sys sys 1367613437 1004 /* * The authors of this software are Rob Pike and Ken Thompson. * Copyright (c) 2002 by Lucent Technologies. * Permission to use, copy, modify, and distribute this software for any * purpose without fee is hereby granted, provided that this entire notice * is included in all copies of any software which is or includes a copy * or modification of this software and in all copies of the supporting * documentation for such software. * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. */ #include #include #include "utf.h" #include "fmt.h" #include "fmtdef.h" int runesprint(Rune *buf, char *fmt, ...) { int n; va_list args; va_start(args, fmt); n = runevsnprint(buf, 256, fmt, args); va_end(args); return n; } /sys/src/ape/lib/fmt/runevseprint.c 664 sys sys 1367613437 1132 /* * The authors of this software are Rob Pike and Ken Thompson. * Copyright (c) 2002 by Lucent Technologies. * Permission to use, copy, modify, and distribute this software for any * purpose without fee is hereby granted, provided that this entire notice * is included in all copies of any software which is or includes a copy * or modification of this software and in all copies of the supporting * documentation for such software. * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. */ #include #include #include "utf.h" #include "fmt.h" #include "fmtdef.h" Rune* runevseprint(Rune *buf, Rune *e, char *fmt, va_list args) { Fmt f; if(e <= buf) return nil; f.runes = 1; f.start = buf; f.to = buf; f.stop = e - 1; f.flush = nil; f.farg = nil; f.nfmt = 0; f.args = args; dofmt(&f, fmt); *(Rune*)f.to = '\0'; return (Rune*)f.to; } /sys/src/ape/lib/fmt/runevsmprint.c 664 sys sys 1367613437 1109 /* * The authors of this software are Rob Pike and Ken Thompson. * Copyright (c) 2002 by Lucent Technologies. * Permission to use, copy, modify, and distribute this software for any * purpose without fee is hereby granted, provided that this entire notice * is included in all copies of any software which is or includes a copy * or modification of this software and in all copies of the supporting * documentation for such software. * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. */ #include #include #include "utf.h" #include "fmt.h" #include "fmtdef.h" /* * print into an allocated string buffer */ Rune* runevsmprint(char *fmt, va_list args) { Fmt f; int n; if(runefmtstrinit(&f) < 0) return nil; f.args = args; n = dofmt(&f, fmt); if(n < 0) return nil; *(Rune*)f.to = '\0'; return (Rune*)f.start; } /sys/src/ape/lib/fmt/runevsnprint.c 664 sys sys 1367613437 1142 /* * The authors of this software are Rob Pike and Ken Thompson. * Copyright (c) 2002 by Lucent Technologies. * Permission to use, copy, modify, and distribute this software for any * purpose without fee is hereby granted, provided that this entire notice * is included in all copies of any software which is or includes a copy * or modification of this software and in all copies of the supporting * documentation for such software. * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. */ #include #include #include "utf.h" #include "fmt.h" #include "fmtdef.h" int runevsnprint(Rune *buf, int len, char *fmt, va_list args) { Fmt f; if(len <= 0) return -1; f.runes = 1; f.start = buf; f.to = buf; f.stop = buf + len - 1; f.flush = nil; f.farg = nil; f.nfmt = 0; f.args = args; dofmt(&f, fmt); *(Rune*)f.to = '\0'; return (Rune*)f.to - buf; } /sys/src/ape/lib/fmt/seprint.c 664 sys sys 1367613437 951 /* * The authors of this software are Rob Pike and Ken Thompson. * Copyright (c) 2002 by Lucent Technologies. * Permission to use, copy, modify, and distribute this software for any * purpose without fee is hereby granted, provided that this entire notice * is included in all copies of any software which is or includes a copy * or modification of this software and in all copies of the supporting * documentation for such software. * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. */ #include #include "fmt.h" char* seprint(char *buf, char *e, char *fmt, ...) { char *p; va_list args; va_start(args, fmt); p = vseprint(buf, e, fmt, args); va_end(args); return p; } /sys/src/ape/lib/fmt/smprint.c 664 sys sys 1367613437 923 /* * The authors of this software are Rob Pike and Ken Thompson. * Copyright (c) 2002 by Lucent Technologies. * Permission to use, copy, modify, and distribute this software for any * purpose without fee is hereby granted, provided that this entire notice * is included in all copies of any software which is or includes a copy * or modification of this software and in all copies of the supporting * documentation for such software. * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. */ #include #include "fmt.h" char* smprint(char *fmt, ...) { va_list args; char *p; va_start(args, fmt); p = vsmprint(fmt, args); va_end(args); return p; } /sys/src/ape/lib/fmt/snprint.c 664 sys sys 1367613437 950 /* * The authors of this software are Rob Pike and Ken Thompson. * Copyright (c) 2002 by Lucent Technologies. * Permission to use, copy, modify, and distribute this software for any * purpose without fee is hereby granted, provided that this entire notice * is included in all copies of any software which is or includes a copy * or modification of this software and in all copies of the supporting * documentation for such software. * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. */ #include #include "fmt.h" int snprint(char *buf, int len, char *fmt, ...) { int n; va_list args; va_start(args, fmt); n = vsnprint(buf, len, fmt, args); va_end(args); return n; } /sys/src/ape/lib/fmt/sprint.c 664 sys sys 1367613437 991 /* * The authors of this software are Rob Pike and Ken Thompson. * Copyright (c) 2002 by Lucent Technologies. * Permission to use, copy, modify, and distribute this software for any * purpose without fee is hereby granted, provided that this entire notice * is included in all copies of any software which is or includes a copy * or modification of this software and in all copies of the supporting * documentation for such software. * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. */ #include #include "fmt.h" int sprint(char *buf, char *fmt, ...) { int n; va_list args; va_start(args, fmt); n = vsnprint(buf, 65536, fmt, args); /* big number, but sprint is deprecated anyway */ va_end(args); return n; } /sys/src/ape/lib/fmt/strtod.c 664 sys sys 1367613437 9348 /* * The authors of this software are Rob Pike and Ken Thompson. * Copyright (c) 2002 by Lucent Technologies. * Permission to use, copy, modify, and distribute this software for any * purpose without fee is hereby granted, provided that this entire notice * is included in all copies of any software which is or includes a copy * or modification of this software and in all copies of the supporting * documentation for such software. * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. */ #include #include #include #include #include #include #include "fmt.h" #include "nan.h" #ifndef nelem #define nelem(x) (sizeof(x)/sizeof *(x)) #endif #define nil ((void*)0) #define ulong _fmtulong typedef unsigned long ulong; static ulong umuldiv(ulong a, ulong b, ulong c) { double d; d = ((double)a * (double)b) / (double)c; if(d >= 4294967295.) d = 4294967295.; return (ulong)d; } /* * This routine will convert to arbitrary precision * floating point entirely in multi-precision fixed. * The answer is the closest floating point number to * the given decimal number. Exactly half way are * rounded ala ieee rules. * Method is to scale input decimal between .500 and .999... * with external power of 2, then binary search for the * closest mantissa to this decimal number. * Nmant is is the required precision. (53 for ieee dp) * Nbits is the max number of bits/word. (must be <= 28) * Prec is calculated - the number of words of fixed mantissa. */ enum { Nbits = 28, /* bits safely represented in a ulong */ Nmant = 53, /* bits of precision required */ Prec = (Nmant+Nbits+1)/Nbits, /* words of Nbits each to represent mantissa */ Sigbit = 1<<(Prec*Nbits-Nmant), /* first significant bit of Prec-th word */ Ndig = 1500, One = (ulong)(1<>1), Maxe = 310, Fsign = 1<<0, /* found - */ Fesign = 1<<1, /* found e- */ Fdpoint = 1<<2, /* found . */ S0 = 0, /* _ _S0 +S1 #S2 .S3 */ S1, /* _+ #S2 .S3 */ S2, /* _+# #S2 .S4 eS5 */ S3, /* _+. #S4 */ S4, /* _+#.# #S4 eS5 */ S5, /* _+#.#e +S6 #S7 */ S6, /* _+#.#e+ #S7 */ S7, /* _+#.#e+# #S7 */ }; static int xcmp(char*, char*); static int fpcmp(char*, ulong*); static void frnorm(ulong*); static void divascii(char*, int*, int*, int*); static void mulascii(char*, int*, int*, int*); typedef struct Tab Tab; struct Tab { int bp; int siz; char* cmp; }; double fmtstrtod(const char *as, char **aas) { int na, ex, dp, bp, c, i, flag, state; ulong low[Prec], hig[Prec], mid[Prec]; double d; char *s, a[Ndig]; flag = 0; /* Fsign, Fesign, Fdpoint */ na = 0; /* number of digits of a[] */ dp = 0; /* na of decimal point */ ex = 0; /* exonent */ state = S0; for(s=(char*)as;; s++) { c = *s; if(c >= '0' && c <= '9') { switch(state) { case S0: case S1: case S2: state = S2; break; case S3: case S4: state = S4; break; case S5: case S6: case S7: state = S7; ex = ex*10 + (c-'0'); continue; } if(na == 0 && c == '0') { dp--; continue; } if(na < Ndig-50) a[na++] = c; continue; } switch(c) { case '\t': case '\n': case '\v': case '\f': case '\r': case ' ': if(state == S0) continue; break; case '-': if(state == S0) flag |= Fsign; else flag |= Fesign; case '+': if(state == S0) state = S1; else if(state == S5) state = S6; else break; /* syntax */ continue; case '.': flag |= Fdpoint; dp = na; if(state == S0 || state == S1) { state = S3; continue; } if(state == S2) { state = S4; continue; } break; case 'e': case 'E': if(state == S2 || state == S4) { state = S5; continue; } break; } break; } /* * clean up return char-pointer */ switch(state) { case S0: if(xcmp(s, "nan") == 0) { if(aas != nil) *aas = s+3; goto retnan; } case S1: if(xcmp(s, "infinity") == 0) { if(aas != nil) *aas = s+8; goto retinf; } if(xcmp(s, "inf") == 0) { if(aas != nil) *aas = s+3; goto retinf; } case S3: if(aas != nil) *aas = (char*)as; goto ret0; /* no digits found */ case S6: s--; /* back over +- */ case S5: s--; /* back over e */ break; } if(aas != nil) *aas = s; if(flag & Fdpoint) while(na > 0 && a[na-1] == '0') na--; if(na == 0) goto ret0; /* zero */ a[na] = 0; if(!(flag & Fdpoint)) dp = na; if(flag & Fesign) ex = -ex; dp += ex; if(dp < -Maxe){ errno = ERANGE; goto ret0; /* underflow by exp */ } else if(dp > +Maxe) goto retinf; /* overflow by exp */ /* * normalize the decimal ascii number * to range .[5-9][0-9]* e0 */ bp = 0; /* binary exponent */ while(dp > 0) divascii(a, &na, &dp, &bp); while(dp < 0 || a[0] < '5') mulascii(a, &na, &dp, &bp); /* close approx by naive conversion */ mid[0] = 0; mid[1] = 1; for(i=0; c=a[i]; i++) { mid[0] = mid[0]*10 + (c-'0'); mid[1] = mid[1]*10; if(i >= 8) break; } low[0] = umuldiv(mid[0], One, mid[1]); hig[0] = umuldiv(mid[0]+1, One, mid[1]); for(i=1; i>= 1; } frnorm(mid); /* compare */ c = fpcmp(a, mid); if(c > 0) { c = 1; for(i=0; i= Sigbit/2) { mid[Prec-1] += Sigbit; frnorm(mid); } goto out; ret0: return 0; retnan: return __NaN(); retinf: /* * Unix strtod requires these. Plan 9 would return Inf(0) or Inf(-1). */ errno = ERANGE; if(flag & Fsign) return -HUGE_VAL; return HUGE_VAL; out: d = 0; for(i=0; i0; i--) { f[i] += c; c = f[i] >> Nbits; f[i] &= One-1; } f[0] += c; } static int fpcmp(char *a, ulong* f) { ulong tf[Prec]; int i, d, c; for(i=0; i> Nbits) + '0'; tf[0] &= One-1; /* compare next digit */ c = *a; if(c == 0) { if('0' < d) return -1; if(tf[0] != 0) goto cont; for(i=1; i d) return +1; if(c < d) return -1; a++; cont:; } } static void divby(char *a, int *na, int b) { int n, c; char *p; p = a; n = 0; while(n>>b == 0) { c = *a++; if(c == 0) { while(n) { c = n*10; if(c>>b) break; n = c; } goto xx; } n = n*10 + c-'0'; (*na)--; } for(;;) { c = n>>b; n -= c<>b; n -= c<= (int)(nelem(tab1))) d = (int)(nelem(tab1))-1; t = tab1 + d; b = t->bp; if(memcmp(a, t->cmp, t->siz) > 0) d--; *dp -= d; *bp += b; divby(a, na, b); } static void mulby(char *a, char *p, char *q, int b) { int n, c; n = 0; *p = 0; for(;;) { q--; if(q < a) break; c = *q - '0'; c = (c<= (int)(nelem(tab2))) d = (int)(nelem(tab2))-1; t = tab2 + d; b = t->bp; if(memcmp(a, t->cmp, t->siz) < 0) d--; p = a + *na; *bp -= b; *dp += d; *na += d; mulby(a, p+d, p, b); } static int xcmp(char *a, char *b) { int c1, c2; while(c1 = *b++) { c2 = *a++; if(isupper(c2)) c2 = tolower(c2); if(c1 != c2) return 1; } return 0; } /sys/src/ape/lib/fmt/strtod.h 664 sys sys 1367613437 120 extern double __NaN(void); extern double __Inf(int); extern double __isNaN(double); extern double __isInf(double, int); /sys/src/ape/lib/fmt/test.c 664 sys sys 1367613437 1423 /* * The authors of this software are Rob Pike and Ken Thompson. * Copyright (c) 2002 by Lucent Technologies. * Permission to use, copy, modify, and distribute this software for any * purpose without fee is hereby granted, provided that this entire notice * is included in all copies of any software which is or includes a copy * or modification of this software and in all copies of the supporting * documentation for such software. * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. */ #include #include #include "fmt.h" int main(int argc, char *argv[]) { quotefmtinstall(); print("hello world\n"); print("x: %x\n", 0x87654321); print("u: %u\n", 0x87654321); print("d: %d\n", 0x87654321); print("s: %s\n", "hi there"); print("q: %q\n", "hi i'm here"); print("c: %c\n", '!'); print("g: %g %g %g\n", 3.14159, 3.14159e10, 3.14159e-10); print("e: %e %e %e\n", 3.14159, 3.14159e10, 3.14159e-10); print("f: %f %f %f\n", 3.14159, 3.14159e10, 3.14159e-10); print("smiley: %C\n", (Rune)0x263a); print("%g %.18\n", 2e25, 2e25); print("%2.18g\n", 1.0); print("%f\n", 3.1415927/4); print("%d\n", 23); print("%i\n", 23); return 0; } /sys/src/ape/lib/fmt/vfprint.c 664 sys sys 1367613437 1026 /* * The authors of this software are Rob Pike and Ken Thompson. * Copyright (c) 2002 by Lucent Technologies. * Permission to use, copy, modify, and distribute this software for any * purpose without fee is hereby granted, provided that this entire notice * is included in all copies of any software which is or includes a copy * or modification of this software and in all copies of the supporting * documentation for such software. * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. */ #include #include "fmt.h" #include "fmtdef.h" int vfprint(int fd, char *fmt, va_list args) { Fmt f; char buf[256]; int n; fmtfdinit(&f, fd, buf, sizeof(buf)); f.args = args; n = dofmt(&f, fmt); if(n > 0 && __fmtFdFlush(&f) == 0) return -1; return n; } /sys/src/ape/lib/fmt/vseprint.c 664 sys sys 1367613437 1089 /* * The authors of this software are Rob Pike and Ken Thompson. * Copyright (c) 2002 by Lucent Technologies. * Permission to use, copy, modify, and distribute this software for any * purpose without fee is hereby granted, provided that this entire notice * is included in all copies of any software which is or includes a copy * or modification of this software and in all copies of the supporting * documentation for such software. * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. */ #include #include "fmt.h" #include "fmtdef.h" char* vseprint(char *buf, char *e, char *fmt, va_list args) { Fmt f; if(e <= buf) return nil; f.runes = 0; f.start = buf; f.to = buf; f.stop = e - 1; f.flush = 0; f.farg = nil; f.nfmt = 0; f.args = args; dofmt(&f, fmt); *(char*)f.to = '\0'; return (char*)f.to; } /sys/src/ape/lib/fmt/vsmprint.c 664 sys sys 1367613437 1084 /* * The authors of this software are Rob Pike and Ken Thompson. * Copyright (c) 2002 by Lucent Technologies. * Permission to use, copy, modify, and distribute this software for any * purpose without fee is hereby granted, provided that this entire notice * is included in all copies of any software which is or includes a copy * or modification of this software and in all copies of the supporting * documentation for such software. * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. */ #include #include #include "fmt.h" #include "fmtdef.h" /* * print into an allocated string buffer */ char* vsmprint(char *fmt, va_list args) { Fmt f; int n; if(fmtstrinit(&f) < 0) return nil; f.args = args; n = dofmt(&f, fmt); if(n < 0) return nil; *(char*)f.to = '\0'; return (char*)f.start; } /sys/src/ape/lib/fmt/vsnprint.c 664 sys sys 1367613437 1119 /* * The authors of this software are Rob Pike and Ken Thompson. * Copyright (c) 2002 by Lucent Technologies. * Permission to use, copy, modify, and distribute this software for any * purpose without fee is hereby granted, provided that this entire notice * is included in all copies of any software which is or includes a copy * or modification of this software and in all copies of the supporting * documentation for such software. * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. */ #include #include #include "fmt.h" #include "fmtdef.h" int vsnprint(char *buf, int len, char *fmt, va_list args) { Fmt f; if(len <= 0) return -1; f.runes = 0; f.start = buf; f.to = buf; f.stop = buf + len - 1; f.flush = 0; f.farg = nil; f.nfmt = 0; f.args = args; dofmt(&f, fmt); *(char*)f.to = '\0'; return (char*)f.to - buf; } /sys/src/ape/lib/fmt/werrstr.c 664 sys sys 1367613437 241 #include #include #include "fmt.h" extern char _plan9err[128]; void werrstr(const char *fmt, ...) { va_list arg; va_start(arg, fmt); snprint(_plan9err, sizeof _plan9err, fmt, arg); va_end(arg); errno = EPLAN9; } /sys/src/ape/lib/l 20000000775 sys sys 1369861528 0 /sys/src/ape/lib/l/allprint.c 664 sys sys 1367613437 458 #include #include extern FILE* yyout; int printable(int c) { return 040 < c && c < 0177; } void allprint(char c) { switch(c) { case '\n': fprintf(yyout,"\\n"); break; case '\t': fprintf(yyout,"\\t"); break; case '\b': fprintf(yyout,"\\b"); break; case ' ': fprintf(yyout,"\\\bb"); break; default: if(!printable(c)) fprintf(yyout,"\\%-3o",c); else c = putc(c,yyout); USED(c); break; } return; } /sys/src/ape/lib/l/main.c 664 sys sys 1369846613 134 #include #include int yylex(void); void main(int argc, char *argv[]) { USED(argc, argv); yylex(); exit(0); } /sys/src/ape/lib/l/mkfile 664 sys sys 1369846630 183 APE=/sys/src/ape <$APE/config LIB=/$objtype/lib/ape/libl.a OFILES=allprint.$O\ main.$O\ reject.$O\ yyless.$O\ yywrap.$O\ #include extern FILE* yyout; extern FILE* yyin; extern int yyprevious, *yyfnd; extern char yyextra[]; extern char yytext[]; extern int yyleng; extern struct { int *yyaa, *yybb; int *yystops; } *yylstate [], **yylsp, **yyolsp; int yyback(int *p, int m); int yyinput(void); void yyoutput(int c); void yyunput(int c); int yyracc(int m) { yyolsp = yylsp; if(yyextra[m]) { while(yyback((*yylsp)->yystops, -m) != 1 && yylsp > yylstate) { yylsp--; yyunput(yytext[--yyleng]); } } yyprevious = yytext[yyleng-1]; yytext[yyleng] = 0; return m; } int yyreject(void) { for(; yylsp < yyolsp; yylsp++) yytext[yyleng++] = yyinput(); if(*yyfnd > 0) return yyracc(*yyfnd++); while(yylsp-- > yylstate) { yyunput(yytext[yyleng-1]); yytext[--yyleng] = 0; if(*yylsp != 0 && (yyfnd = (*yylsp)->yystops) && *yyfnd > 0) return yyracc(*yyfnd++); } if(yytext[0] == 0) return 0; yyoutput(yyprevious = yyinput()); yyleng = 0; return -1; } /sys/src/ape/lib/l/yyless.c 664 sys sys 1367613437 387 #include #include extern char yytext[]; extern int yyleng; extern int yyprevious; void yyunput(int c); void yyless(int x) { char *lastch, *ptr; lastch = yytext+yyleng; if(x>=0 && x <= yyleng) ptr = x + yytext; else ptr = (char*)x; while(lastch > ptr) yyunput(*--lastch); *lastch = 0; if (ptr >yytext) yyprevious = *--lastch; yyleng = ptr-yytext; } /sys/src/ape/lib/l/yywrap.c 664 sys sys 1367613437 70 #include #include int yywrap(void) { return 1; } /sys/src/ape/lib/mkfile 664 sys sys 1357610071 206 [2]/dev/null | sed 's/..$//' > /tmp/reduce.$pid # # if empty directory, just return the input files # if (! ~ $status '|') { echo $* rm /tmp/reduce.$pid unmount $cwd exit 0 } echo $* | tr ' ' \012 | grep -v -f /tmp/reduce.$pid | tr \012 ' ' rm /tmp/reduce.$pid unmount $cwd /sys/src/ape/lib/mp/power 20000000775 bootes sys 1369168845 0 /sys/src/ape/lib/mp/power/mkfile 664 sys sys 1369166818 371 APE=/sys/src/ape <$APE/config LIB=/$objtype/lib/ape/libmp.a SFILES=\ mpvecadd.s\ mpvecdigmuladd.s\ mpvecdigmulsub.s\ mpvecsub.s\ mpdigdiv.s\ HFILES=\ /sys/include/ape/mp.h\ ../../../../libmp/port/dat.h OFILES=${SFILES:%.s=%.$O} UPDATE=mkfile\ $HFILES\ $SFILES\ #include #include #include #include #include #include #include #define NAMELEN 28 static int nettrans(char*, char*, int na, char*, int); /* * announce a network service. */ int announce(char *addr, char *dir) { int ctl, n, m; char buf[3*NAMELEN]; char buf2[3*NAMELEN]; char netdir[2*NAMELEN]; char naddr[3*NAMELEN]; char *cp; /* * translate the address */ if(nettrans(addr, naddr, sizeof(naddr), netdir, sizeof(netdir)) < 0) return -1; /* * get a control channel */ ctl = open(netdir, O_RDWR); if(ctl<0) return -1; cp = strrchr(netdir, '/'); *cp = 0; /* * find out which line we have */ n = sprintf(buf, "%.*s/", 2*NAMELEN+1, netdir); m = read(ctl, &buf[n], sizeof(buf)-n-1); if(n<=0){ close(ctl); return -1; } buf[n+m] = 0; /* * make the call */ n = sprintf(buf2, "announce %.*s", 2*NAMELEN, naddr); if(write(ctl, buf2, n)!=n){ close(ctl); return -1; } /* * return directory etc. */ if(dir) strcpy(dir, buf); return ctl; } /* * listen for an incoming call */ int listen(char *dir, char *newdir) { int ctl, n, m; char buf[3*NAMELEN]; char *cp; /* * open listen, wait for a call */ sprintf(buf, "%.*s/listen", 2*NAMELEN+1, dir); ctl = open(buf, O_RDWR); if(ctl < 0) return -1; /* * find out which line we have */ strcpy(buf, dir); cp = strrchr(buf, '/'); *++cp = 0; n = cp-buf; m = read(ctl, cp, sizeof(buf) - n - 1); if(n<=0){ close(ctl); return -1; } buf[n+m] = 0; /* * return directory etc. */ if(newdir) strcpy(newdir, buf); return ctl; } /* * accept a call, return an fd to the open data file */ int accept(int ctl, char *dir) { char buf[128]; char *num; long n; num = strrchr(dir, '/'); if(num == 0) num = dir; else num++; sprintf(buf, "accept %s", num); n = strlen(buf); write(ctl, buf, n); /* ignore return value, netowrk might not need accepts */ sprintf(buf, "%s/data", dir); return open(buf, O_RDWR); } /* * reject a call, tell device the reason for the rejection */ int reject(int ctl, char *dir, char *cause) { char buf[128]; char *num; long n; num = strrchr(dir, '/'); if(num == 0) num = dir; else num++; sprintf(buf, "reject %s %s", num, cause); n = strlen(buf); if(write(ctl, buf, n) != n) return -1; return 0; } /* * perform the identity translation (in case we can't reach cs) */ static int identtrans(char *addr, char *naddr, int na, char *file, int nf) { char reply[4*NAMELEN]; char *p; USED(nf); /* parse the network */ strncpy(reply, addr, sizeof(reply)); reply[sizeof(reply)-1] = 0; p = strchr(addr, '!'); if(p) *p++ = 0; sprintf(file, "/net/%.*s/clone", na - sizeof("/net//clone"), reply); strncpy(naddr, p, na); naddr[na-1] = 0; return 1; } /* * call up the connection server and get a translation */ static int nettrans(char *addr, char *naddr, int na, char *file, int nf) { int fd; char reply[4*NAMELEN]; char *cp; long n; /* * ask the connection server */ fd = open("/net/cs", O_RDWR); if(fd < 0) return identtrans(addr, naddr, na, file, nf); if(write(fd, addr, strlen(addr)) < 0){ close(fd); return -1; } lseek(fd, 0, 0); n = read(fd, reply, sizeof(reply)-1); close(fd); if(n <= 0) return -1; reply[n] = 0; /* * parse the reply */ cp = strchr(reply, ' '); if(cp == 0) return -1; *cp++ = 0; strncpy(naddr, cp, na); naddr[na-1] = 0; strncpy(file, reply, nf); file[nf-1] = 0; return 0; } /sys/src/ape/lib/net/dial.c 664 sys sys 1367613437 2411 #include #include #include #include #include #include #include #define NAMELEN 28 static int call(char *clone, char *dest, int *cfdp, char *dir, char *local) { int fd, cfd; int n; char name[3*NAMELEN+5]; char data[3*NAMELEN+10]; char *p; cfd = open(clone, O_RDWR); if(cfd < 0) return -1; /* get directory name */ n = read(cfd, name, sizeof(name)-1); if(n < 0){ close(cfd); return -1; } name[n] = 0; p = strrchr(clone, '/'); *p = 0; if(dir) sprintf(dir, "%.*s/%.*s", 2*NAMELEN+1, clone, NAMELEN, name); sprintf(data, "%.*s/%.*s/data", 2*NAMELEN+1, clone, NAMELEN, name); /* set local side (port number, for example) if we need to */ if(local) sprintf(name, "connect %.*s %.*s", 2*NAMELEN, dest, NAMELEN, local); else sprintf(name, "connect %.*s", 2*NAMELEN, dest); /* connect */ if(write(cfd, name, strlen(name)) < 0){ close(cfd); return -1; } /* open data connection */ fd = open(data, O_RDWR); if(fd < 0){ close(cfd); return -1; } if(cfdp) *cfdp = cfd; else close(cfd); return fd; } int dial(char *dest, char *local, char *dir, int *cfdp) { char net[128]; char netdir[128], csname[NETPATHLEN], *slp; char clone[NAMELEN+12]; char *p; int n; int fd; int rv; /* go for a standard form net!... */ p = strchr(dest, '!'); if(p == 0){ sprintf(net, "net!%.*s", sizeof(net)-5, dest); } else { strncpy(net, dest, sizeof(net)-1); net[sizeof(net)-1] = 0; } slp = strrchr(net, '/'); if (slp != 0) { *slp++ = '\0'; strcpy(netdir, net); memmove(net, slp, strlen(slp)+1); } else strcpy(netdir, "/net"); /* call the connection server */ sprintf(csname, "%s/cs", netdir); fd = open(csname, O_RDWR); if(fd < 0){ /* no connection server, don't translate */ p = strchr(net, '!'); *p++ = 0; sprintf(clone, "%s/%s/clone", netdir, net); return call(clone, p, cfdp, dir, local); } /* * send dest to connection to translate */ if(write(fd, net, strlen(net)) < 0){ close(fd); return -1; } /* * loop through each address from the connection server till * we get one that works. */ rv = -1; lseek(fd, 0, 0); while((n = read(fd, net, sizeof(net) - 1)) > 0){ net[n] = 0; p = strchr(net, ' '); if(p == 0) continue; *p++ = 0; rv = call(net, p, cfdp, dir, local); if(rv >= 0) break; } close(fd); return rv; } /sys/src/ape/lib/net/hangup.c 664 sys sys 1367613437 278 #include #include #include #include #include #include #include /* * force a connection to hangup */ int hangup(int ctl) { return write(ctl, "hangup", sizeof("hangup")-1) != sizeof("hangup")-1; } /sys/src/ape/lib/net/mkfile 664 sys sys 1369846697 211 APE=/sys/src/ape <$APE/config LIB=/$objtype/lib/ape/libnet.a OFILES=dial.$O\ announce.$O\ netmkaddr.$O\ hangup.$O\ #include #include #include #include #include #include /* * make an address, add the defaults */ char * netmkaddr(char *linear, char *defnet, char *defsrv) { static char addr[256]; char *cp; /* * dump network name */ cp = strchr(linear, '!'); if(cp == 0){ if(defnet==0){ if(defsrv) snprintf(addr, sizeof(addr), "net!%s!%s", linear, defsrv); else snprintf(addr, sizeof(addr), "net!%s", linear); } else { if(defsrv) snprintf(addr, sizeof(addr), "%s!%s!%s", defnet, linear, defsrv); else snprintf(addr, sizeof(addr), "%s!%s", defnet, linear); } return addr; } /* * if there is already a service, use it */ cp = strchr(cp+1, '!'); if(cp) return linear; /* * add default service */ if(defsrv == 0) return linear; snprintf(addr, sizeof(addr), "%s!%s", linear, defsrv); return addr; } /sys/src/ape/lib/regexp 20000000775 sys sys 1369861529 0 /sys/src/ape/lib/regexp/mkfile 664 sys sys 1369166818 226 APE=/sys/src/ape <$APE/config LIB=/$objtype/lib/ape/libregexp.a OFILES=regcomp.$O\ regerror.$O\ regexec.$O\ regsub.$O\ regaux.$O\ rregexec.$O\ rregsub.$O\ #include #include "regexp.h" #include "regcomp.h" /* * Machine state */ Relist* _relist[2]; Relist* _reliste[2]; int _relistsize = LISTINCREMENT; /* * save a new match in mp */ extern void _renewmatch(Resub *mp, int ms, Resublist *sp) { int i; if(mp==0 || ms<=0) return; if(mp[0].s.sp==0 || sp->m[0].s.spm[0].s.sp==mp[0].s.sp && sp->m[0].e.ep>mp[0].e.ep)){ for(i=0; im[i]; for(; iinst; p++){ if(p->inst == ip){ if((sep)->m[0].s.sp < p->se.m[0].s.sp) p->se = *sep; return 0; } } p->inst = ip; p->se = *sep; (++p)->inst = 0; return p; } /sys/src/ape/lib/regexp/regcomp.c 664 sys sys 1367613437 9620 #include #include #include #include #include "regexp.h" #include "regcomp.h" #define TRUE 1 #define FALSE 0 /* * Parser Information */ typedef struct Node { Reinst* first; Reinst* last; }Node; #define NSTACK 20 static Node andstack[NSTACK]; static Node *andp; static int atorstack[NSTACK]; static int* atorp; static int cursubid; /* id of current subexpression */ static int subidstack[NSTACK]; /* parallel to atorstack */ static int* subidp; static int lastwasand; /* Last token was operand */ static int nbra; static char* exprp; /* pointer to next character in source expression */ static int lexdone; static int nclass; static Reclass*classp; static Reinst* freep; static int errors; static wchar_t yyrune; /* last lex'd rune */ static Reclass*yyclassp; /* last lex'd class */ /* predeclared crap */ static void operator(int); static void pushand(Reinst*, Reinst*); static void pushator(int); static void evaluntil(int); static int bldcclass(void); static jmp_buf regkaboom; static void rcerror(char *s) { errors++; regerror(s); longjmp(regkaboom, 1); } static Reinst* newinst(int t) { freep->type = t; freep->l.left = 0; freep->r.right = 0; return freep++; } static void operand(int t) { Reinst *i; if(lastwasand) operator(CAT); /* catenate is implicit */ i = newinst(t); if(t == CCLASS || t == NCCLASS) i->r.cp = yyclassp; if(t == RUNE) i->r.r = yyrune; pushand(i, i); lastwasand = TRUE; } static void operator(int t) { if(t==RBRA && --nbra<0) rcerror("unmatched right paren"); if(t==LBRA){ if(++cursubid >= NSUBEXP) rcerror ("too many subexpressions"); nbra++; if(lastwasand) operator(CAT); } else evaluntil(t); if(t != RBRA) pushator(t); lastwasand = FALSE; if(t==STAR || t==QUEST || t==PLUS || t==RBRA) lastwasand = TRUE; /* these look like operands */ } static void regerr2(char *s, int c) { char buf[100]; char *cp = buf; while(*s) *cp++ = *s++; *cp++ = c; *cp = '\0'; rcerror(buf); } static void cant(char *s) { char buf[100]; strcpy(buf, "can't happen: "); strcat(buf, s); rcerror(buf); } static void pushand(Reinst *f, Reinst *l) { if(andp >= &andstack[NSTACK]) cant("operand stack overflow"); andp->first = f; andp->last = l; andp++; } static void pushator(int t) { if(atorp >= &atorstack[NSTACK]) cant("operator stack overflow"); *atorp++ = t; *subidp++ = cursubid; } static Node* popand(int op) { Reinst *inst; if(andp <= &andstack[0]){ regerr2("missing operand for ", op); inst = newinst(NOP); pushand(inst,inst); } return --andp; } static int popator(void) { if(atorp <= &atorstack[0]) cant("operator stack underflow"); --subidp; return *--atorp; } static void evaluntil(int pri) { Node *op1, *op2; Reinst *inst1, *inst2; while(pri==RBRA || atorp[-1]>=pri){ switch(popator()){ default: rcerror("unknown operator in evaluntil"); break; case LBRA: /* must have been RBRA */ op1 = popand('('); inst2 = newinst(RBRA); inst2->r.subid = *subidp; op1->last->l.next = inst2; inst1 = newinst(LBRA); inst1->r.subid = *subidp; inst1->l.next = op1->first; pushand(inst1, inst2); return; case OR: op2 = popand('|'); op1 = popand('|'); inst2 = newinst(NOP); op2->last->l.next = inst2; op1->last->l.next = inst2; inst1 = newinst(OR); inst1->r.right = op1->first; inst1->l.left = op2->first; pushand(inst1, inst2); break; case CAT: op2 = popand(0); op1 = popand(0); op1->last->l.next = op2->first; pushand(op1->first, op2->last); break; case STAR: op2 = popand('*'); inst1 = newinst(OR); op2->last->l.next = inst1; inst1->r.right = op2->first; pushand(inst1, inst1); break; case PLUS: op2 = popand('+'); inst1 = newinst(OR); op2->last->l.next = inst1; inst1->r.right = op2->first; pushand(op2->first, inst1); break; case QUEST: op2 = popand('?'); inst1 = newinst(OR); inst2 = newinst(NOP); inst1->l.left = inst2; inst1->r.right = op2->first; op2->last->l.next = inst2; pushand(inst1, inst2); break; } } } static Reprog* optimize(Reprog *pp) { Reinst *inst, *target; int size; Reprog *npp; int diff; /* * get rid of NOOP chains */ for(inst=pp->firstinst; inst->type!=END; inst++){ target = inst->l.next; while(target->type == NOP) target = target->l.next; inst->l.next = target; } /* * The original allocation is for an area larger than * necessary. Reallocate to the actual space used * and then relocate the code. */ size = sizeof(Reprog) + (freep - pp->firstinst)*sizeof(Reinst); npp = realloc(pp, size); if(npp==0 || npp==pp) return pp; diff = (char *)npp - (char *)pp; freep = (Reinst *)((char *)freep + diff); for(inst=npp->firstinst; insttype){ case OR: case STAR: case PLUS: case QUEST: case CCLASS: case NCCLASS: *(char **)&inst->r.right += diff; break; } *(char **)&inst->l.left += diff; } *(char **)&npp->startinst += diff; return npp; } #ifdef DEBUG static void dumpstack(void){ Node *stk; int *ip; print("operators\n"); for(ip=atorstack; ipfirst->type, stk->last->type); } static void dump(Reprog *pp) { Reinst *l; wchar_t *p; l = pp->firstinst; do{ print("%d:\t0%o\t%d\t%d", l-pp->firstinst, l->type, l->l.left-pp->firstinst, l->l.right-pp->firstinst); if(l->type == RUNE) print("\t%C\n", l->r); else if(l->type == CCLASS || l->type == NCCLASS){ print("\t["); if(l->type == NCCLASS) print("^"); for(p = l->r.cp->spans; p < l->r.cp->end; p += 2) if(p[0] == p[1]) print("%C", p[0]); else print("%C-%C", p[0], p[1]); print("]\n"); } else print("\n"); }while(l++->type); } #endif static Reclass* newclass(void) { if(nclass >= NCLASS) regerr2("too many character classes; limit", NCLASS+'0'); return &(classp[nclass++]); } static int nextc(wchar_t *rp) { int n; if(lexdone){ *rp = 0; return 1; } n = mbtowc(rp, exprp, MB_CUR_MAX); if (n <= 0) n = 1; exprp += n; if(*rp == L'\\'){ n = mbtowc(rp, exprp, MB_CUR_MAX); if (n <= 0) n = 1; exprp += n; return 1; } if(*rp == 0) lexdone = 1; return 0; } static int lex(int literal, int dot_type) { int quoted; quoted = nextc(&yyrune); if(literal || quoted){ if(yyrune == 0) return END; return RUNE; } switch(yyrune){ case 0: return END; case L'*': return STAR; case L'?': return QUEST; case L'+': return PLUS; case L'|': return OR; case L'.': return dot_type; case L'(': return LBRA; case L')': return RBRA; case L'^': return BOL; case L'$': return EOL; case L'[': return bldcclass(); } return RUNE; } static int bldcclass(void) { int type; wchar_t r[NCCRUNE]; wchar_t *p, *ep, *np; wchar_t rune; int quoted; /* we have already seen the '[' */ type = CCLASS; yyclassp = newclass(); /* look ahead for negation */ ep = r; quoted = nextc(&rune); if(!quoted && rune == L'^'){ type = NCCLASS; quoted = nextc(&rune); *ep++ = L'\n'; *ep++ = L'\n'; } /* parse class into a set of spans */ for(; ep<&r[NCCRUNE];){ if(rune == 0){ rcerror("malformed '[]'"); return 0; } if(!quoted && rune == L']') break; if(!quoted && rune == L'-'){ if(ep == r){ rcerror("malformed '[]'"); return 0; } quoted = nextc(&rune); if((!quoted && rune == L']') || rune == 0){ rcerror("malformed '[]'"); return 0; } *(ep-1) = rune; } else { *ep++ = rune; *ep++ = rune; } quoted = nextc(&rune); } /* sort on span start */ for(p = r; p < ep; p += 2){ for(np = p; np < ep; np += 2) if(*np < *p){ rune = np[0]; np[0] = p[0]; p[0] = rune; rune = np[1]; np[1] = p[1]; p[1] = rune; } } /* merge spans */ np = yyclassp->spans; p = r; if(r == ep) yyclassp->end = np; else { np[0] = *p++; np[1] = *p++; for(; p < ep; p += 2) if(p[0] <= np[1]){ if(p[1] > np[1]) np[1] = p[1]; } else { np += 2; np[0] = p[0]; np[1] = p[1]; } yyclassp->end = np+2; } return type; } static Reprog* regcomp1(char *s, int literal, int dot_type) { int token; Reprog *pp; /* get memory for the program */ pp = malloc(sizeof(Reprog) + 6*sizeof(Reinst)*strlen(s)); if(pp == 0){ regerror("out of memory"); return 0; } freep = pp->firstinst; classp = pp->class; errors = 0; if(setjmp(regkaboom)) goto out; /* go compile the sucker */ lexdone = 0; exprp = s; nclass = 0; nbra = 0; atorp = atorstack; andp = andstack; subidp = subidstack; lastwasand = FALSE; cursubid = 0; /* Start with a low priority operator to prime parser */ pushator(START-1); while((token = lex(literal, dot_type)) != END){ if((token&0300) == OPERATOR) operator(token); else operand(token); } /* Close with a low priority operator */ evaluntil(START); /* Force END */ operand(END); evaluntil(START); #ifdef DEBUG dumpstack(); #endif if(nbra) rcerror("unmatched left paren"); --andp; /* points to first and only operand */ pp->startinst = andp->first; #ifdef DEBUG dump(pp); #endif pp = optimize(pp); #ifdef DEBUG print("start: %d\n", andp->first-pp->firstinst); dump(pp); #endif out: if(errors){ free(pp); pp = 0; } return pp; } extern Reprog* regcomp(char *s) { return regcomp1(s, 0, ANY); } extern Reprog* regcomplit(char *s) { return regcomp1(s, 1, ANY); } extern Reprog* regcompnl(char *s) { return regcomp1(s, 0, ANYNL); } /sys/src/ape/lib/regexp/regcomp.h 664 sys sys 1367613437 1522 /* * substitution list */ enum { NSUBEXP = 32, LISTINCREMENT = 8, }; typedef struct Resublist Resublist; struct Resublist { Resub m[NSUBEXP]; }; /* * Actions and Tokens (Reinst types) * * 02xx are operators, value == precedence * 03xx are tokens, i.e. operands for operators */ #define RUNE 0177 #define OPERATOR 0200 /* Bitmask of all operators */ #define START 0200 /* Start, used for marker on stack */ #define RBRA 0201 /* Right bracket, ) */ #define LBRA 0202 /* Left bracket, ( */ #define OR 0203 /* Alternation, | */ #define CAT 0204 /* Concatentation, implicit operator */ #define STAR 0205 /* Closure, * */ #define PLUS 0206 /* a+ == aa* */ #define QUEST 0207 /* a? == a|nothing, i.e. 0 or 1 a's */ #define ANY 0300 /* Any character except newline, . */ #define ANYNL 0301 /* Any character including newline, . */ #define NOP 0302 /* No operation, internal use only */ #define BOL 0303 /* Beginning of line, ^ */ #define EOL 0304 /* End of line, $ */ #define CCLASS 0305 /* Character class, [] */ #define NCCLASS 0306 /* Negated character class, [] */ #define END 0377 /* Terminate: match found */ /* * regexec execution lists */ typedef struct Relist Relist; struct Relist { Reinst *inst; /* Reinstruction of the thread */ Resublist se; /* matched subexpressions in this thread */ }; extern Relist* _relist[2]; extern Relist* _reliste[2]; extern int _relistsize; extern Relist* _renewthread(Relist*, Reinst*, Resublist*); extern void _renewmatch(Resub*, int, Resublist*); /sys/src/ape/lib/regexp/regerror.c 664 sys sys 1367613437 237 #include #include #include #include "regexp.h" void regerror(char *s) { char buf[132]; strcpy(buf, "regerror: "); strcat(buf, s); strcat(buf, "\n"); fwrite(buf, 1, strlen(buf), stderr); exit(1); } /sys/src/ape/lib/regexp/regexec.c 664 sys sys 1367613437 4339 #include #include #include "regexp.h" #include "regcomp.h" static Resublist sempty; /* empty set of matches */ /* * return 0 if no match * >0 if a match * <0 if we ran out of _relist space */ static int regexec1(Reprog *progp, /* program to run */ char *bol, /* string to run machine on */ Resub *mp, /* subexpression elements */ int ms, /* number of elements at mp */ char *starts, char *eol, wchar_t startchar) { int flag=0; Reinst *inst; Relist *tlp; char *s; int i, checkstart; wchar_t r, *rp, *ep; int n; Relist* tl; /* This list, next list */ Relist* nl; Relist* tle; /* ends of this and next list */ Relist* nle; int match; match = 0; checkstart = startchar; sempty.m[0].s.sp = 0; if(mp!=0) for(i=0; iinst = 0; /* Add first instruction to current list */ if(match == 0){ sempty.m[0].s.sp = s; _renewthread(tl, progp->startinst, &sempty); } /* Execute machine until current list is empty */ for(tlp=tl; tlp->inst; tlp++){ /* assignment = */ if(s == eol) break; for(inst = tlp->inst; ; inst = inst->l.next){ switch(inst->type){ case RUNE: /* regular character */ if(inst->r.r == r) if(_renewthread(nl, inst->l.next, &tlp->se)==nle) return -1; break; case LBRA: tlp->se.m[inst->r.subid].s.sp = s; continue; case RBRA: tlp->se.m[inst->r.subid].e.ep = s; continue; case ANY: if(r != '\n') if(_renewthread(nl, inst->l.next, &tlp->se)==nle) return -1; break; case ANYNL: if(_renewthread(nl, inst->l.next, &tlp->se)==nle) return -1; break; case BOL: if(s == bol || *(s-1) == '\n') continue; break; case EOL: if(r == 0 || r == '\n') continue; break; case CCLASS: ep = inst->r.cp->end; for(rp = inst->r.cp->spans; rp < ep; rp += 2) if(r >= rp[0] && r <= rp[1]){ if(_renewthread(nl, inst->l.next, &tlp->se)==nle) return -1; break; } break; case NCCLASS: ep = inst->r.cp->end; for(rp = inst->r.cp->spans; rp < ep; rp += 2) if(r >= rp[0] && r <= rp[1]) break; if(rp == ep) if(_renewthread(nl, inst->l.next, &tlp->se)==nle) return -1; break; case OR: /* evaluate right choice later */ if(_renewthread(tlp, inst->r.right, &tlp->se) == tle) return -1; /* efficiency: advance and re-evaluate */ continue; case END: /* Match! */ match = 1; tlp->se.m[0].e.ep = s; if(mp != 0) _renewmatch(mp, ms, &tlp->se); break; } break; } } checkstart = startchar && nl->inst==0; s += n; }while(r); return match; } extern int regexec(Reprog *progp, /* program to run */ char *bol, /* string to run machine on */ Resub *mp, /* subexpression elements */ int ms) /* number of elements at mp */ { char *starts; /* where to start match */ char *eol; /* where to end match */ wchar_t startchar; int rv; /* * use user-specified starting/ending location if specified */ starts = bol; eol = 0; if(mp && ms>0){ if(mp->s.sp) starts = mp->s.sp; if(mp->e.ep) eol = mp->e.ep; } startchar = (progp->startinst->type == RUNE && progp->startinst->r.r < Runeself) ? progp->startinst->r.r : 0; /* keep trying till we have enough list space to terminate */ for(;;){ if(_relist[0] == 0){ _relist[0] = malloc(2*_relistsize*sizeof(Relist)); _relist[1] = _relist[0] + _relistsize; _reliste[0] = _relist[0] + _relistsize - 1; _reliste[1] = _relist[1] + _relistsize - 1; if(_relist[0] == 0) regerror("_relist overflow"); } rv = regexec1(progp, bol, mp, ms, starts, eol, startchar); if(rv >= 0) return rv; free(_relist[0]); _relist[0] = 0; _relistsize += LISTINCREMENT; } } /sys/src/ape/lib/regexp/regsub.c 664 sys sys 1367613437 1153 #include #include #include "regexp.h" /* substitute into one string using the matches from the last regexec() */ extern void regsub(char *sp, /* source string */ char *dp, /* destination string */ int dlen, Resub *mp, /* subexpression elements */ int ms) /* number of elements pointed to by mp */ { char *ssp, *ep; int i; ep = dp+dlen-1; while(*sp != '\0'){ if(*sp == '\\'){ switch(*++sp){ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': i = *sp-'0'; if(mp[i].s.sp != 0 && mp!=0 && ms>i) for(ssp = mp[i].s.sp; ssp < mp[i].e.ep; ssp++) if(dp < ep) *dp++ = *ssp; break; case '\\': if(dp < ep) *dp++ = '\\'; break; case '\0': sp--; break; default: if(dp < ep) *dp++ = *sp; break; } }else if(*sp == '&'){ if(mp[0].s.sp != 0 && mp!=0 && ms>0) if(mp[0].s.sp != 0) for(ssp = mp[0].s.sp; ssp < mp[0].e.ep; ssp++) if(dp < ep) *dp++ = *ssp; }else{ if(dp < ep) *dp++ = *sp; } sp++; } *dp = '\0'; } /sys/src/ape/lib/regexp/rregexec.c 664 sys sys 1369166818 4181 #include #include #include "regexp.h" #include "regcomp.h" static Resublist sempty; /* empty set of matches */ /* * return 0 if no match * >0 if a match * <0 if we ran out of _relist space */ static int rregexec1(Reprog *progp, /* program to run */ wchar_t *bol, /* string to run machine on */ Resub *mp, /* subexpression elements */ int ms, /* number of elements at mp */ wchar_t *starts, wchar_t *eol, wchar_t startchar) { int flag=0; Reinst *inst; Relist *tlp; wchar_t *s; int i, checkstart; wchar_t r, *rp, *ep; Relist* tl; /* This list, next list */ Relist* nl; Relist* tle; /* ends of this and next list */ Relist* nle; int match; match = 0; checkstart = startchar; sempty.m[0].s.rsp = 0; if(mp!=0) for(i=0; iinst = 0; /* Add first instruction to current list */ sempty.m[0].s.rsp = s; _renewthread(tl, progp->startinst, &sempty); /* Execute machine until current list is empty */ for(tlp=tl; tlp->inst; tlp++){ /* assignment = */ if(s == eol) break; for(inst=tlp->inst; ; inst = inst->l.next){ switch(inst->type){ case RUNE: /* regular character */ if(inst->r.r == r) if(_renewthread(nl, inst->l.next, &tlp->se)==nle) return -1; break; case LBRA: tlp->se.m[inst->r.subid].s.rsp = s; continue; case RBRA: tlp->se.m[inst->r.subid].e.rep = s; continue; case ANY: if(r != '\n') if(_renewthread(nl, inst->l.next, &tlp->se)==nle) return -1; break; case ANYNL: if(_renewthread(nl, inst->l.next, &tlp->se)==nle) return -1; break; case BOL: if(s == bol || *(s-1) == '\n') continue; break; case EOL: if(r == 0 || r == '\n') continue; break; case CCLASS: ep = inst->r.cp->end; for(rp = inst->r.cp->spans; rp < ep; rp += 2) if(r >= rp[0] && r <= rp[1]){ if(_renewthread(nl, inst->l.next, &tlp->se)==nle) return -1; break; } break; case NCCLASS: ep = inst->r.cp->end; for(rp = inst->r.cp->spans; rp < ep; rp += 2) if(r >= rp[0] && r <= rp[1]) break; if(rp == ep) if(_renewthread(nl, inst->l.next, &tlp->se)==nle) return -1; break; case OR: /* evaluate right choice later */ if(_renewthread(tlp, inst->r.right, &tlp->se) == tle) return -1; /* efficiency: advance and re-evaluate */ continue; case END: /* Match! */ match = 1; tlp->se.m[0].e.rep = s; if(mp != 0) _renewmatch(mp, ms, &tlp->se); break; } break; } } checkstart = startchar && nl->inst==0; s++; }while(r); return match; } extern int rregexec(Reprog *progp, /* program to run */ wchar_t *bol, /* string to run machine on */ Resub *mp, /* subexpression elements */ int ms) /* number of elements at mp */ { wchar_t *starts; /* where to start match */ wchar_t *eol; /* where to end match */ wchar_t startchar; int rv; /* * use user-specified starting/ending location if specified */ starts = bol; eol = 0; if(mp && ms>0){ if(mp->s.rsp) starts = mp->s.rsp; if(mp->e.rep) eol = mp->e.rep; } startchar = progp->startinst->type == RUNE ? progp->startinst->r.r : 0; /* keep trying till we have enough list space to terminate */ for(;;){ if(_relist[0] == 0){ _relist[0] = malloc(2*_relistsize*sizeof(Relist)); _relist[1] = _relist[0] + _relistsize; _reliste[0] = _relist[0] + _relistsize - 1; _reliste[1] = _relist[1] + _relistsize - 1; if(_relist[0] == 0) regerror("_relist overflow"); } rv = rregexec1(progp, bol, mp, ms, starts, eol, startchar); if(rv >= 0) return rv; free(_relist[0]); _relist[0] = 0; _relistsize += LISTINCREMENT; } } /sys/src/ape/lib/regexp/rregsub.c 664 sys sys 1367613437 1188 #include #include #include "regexp.h" /* substitute into one string using the matches from the last regexec() */ extern void rregsub(wchar_t *sp, /* source string */ wchar_t *dp, /* destination string */ int dlen, Resub *mp, /* subexpression elements */ int ms) /* number of elements pointed to by mp */ { wchar_t *ssp, *ep; int i; ep = dp+(dlen/sizeof(wchar_t))-1; while(*sp != '\0'){ if(*sp == '\\'){ switch(*++sp){ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': i = *sp-'0'; if(mp[i].s.rsp != 0 && mp!=0 && ms>i) for(ssp = mp[i].s.rsp; ssp < mp[i].e.rep; ssp++) if(dp < ep) *dp++ = *ssp; break; case '\\': if(dp < ep) *dp++ = '\\'; break; case '\0': sp--; break; default: if(dp < ep) *dp++ = *sp; break; } }else if(*sp == '&'){ if(mp[0].s.rsp != 0 && mp!=0 && ms>0) if(mp[0].s.rsp != 0) for(ssp = mp[0].s.rsp; ssp < mp[0].e.rep; ssp++) if(dp < ep) *dp++ = *ssp; }else{ if(dp < ep) *dp++ = *sp; } sp++; } *dp = '\0'; } /sys/src/ape/lib/sec 20000000775 bootes sys 1368053403 0 /sys/src/ape/lib/sec/386 20000000775 bootes sys 1369167058 0 /sys/src/ape/lib/sec/386/mkfile 664 sys sys 1369849442 320 APE=/sys/src/ape <$APE/config LIB=/$objtype/lib/ape/libsec.a FILES=\ md5block\ sha1block\ HFILES=/sys/include/ape/libsec.h SFILES=${FILES:%=%.s} OFILES=${SFILES:%.s=%.$O} UPDATE=mkfile\ $HFILES\ $SFILES\ #include #include #include #include #include // The main groups of functions are: // client/server - main handshake protocol definition // message functions - formating handshake messages // cipher choices - catalog of digest and encrypt algorithms // security functions - PKCS#1, sslHMAC, session keygen // general utility functions - malloc, serialization // The handshake protocol builds on the TLS/SSL3 record layer protocol, // which is implemented in kernel device #a. See also /lib/rfc/rfc2246. enum { TLSFinishedLen = 12, SSL3FinishedLen = MD5dlen+SHA1dlen, MaxKeyData = 136, // amount of secret we may need MaxChunk = 1<<14, RandomSize = 32, SidSize = 32, MasterSecretSize = 48, AQueue = 0, AFlush = 1, }; typedef struct TlsSec TlsSec; typedef struct Bytes{ int len; uchar data[1]; // [len] } Bytes; typedef struct Ints{ int len; int data[1]; // [len] } Ints; typedef struct Algs{ char *enc; char *digest; int nsecret; int tlsid; int ok; } Algs; typedef struct Finished{ uchar verify[SSL3FinishedLen]; int n; } Finished; typedef struct TlsConnection{ TlsSec *sec; // security management goo int hand, ctl; // record layer file descriptors int erred; // set when tlsError called int (*trace)(char*fmt, ...); // for debugging int version; // protocol we are speaking int verset; // version has been set int ver2hi; // server got a version 2 hello int isClient; // is this the client or server? Bytes *sid; // SessionID Bytes *cert; // only last - no chain Lock statelk; int state; // must be set using setstate // input buffer for handshake messages uchar buf[MaxChunk+2048]; uchar *rp, *ep; uchar crandom[RandomSize]; // client random uchar srandom[RandomSize]; // server random int clientVersion; // version in ClientHello char *digest; // name of digest algorithm to use char *enc; // name of encryption algorithm to use int nsecret; // amount of secret data to init keys // for finished messages MD5state hsmd5; // handshake hash SHAstate hssha1; // handshake hash Finished finished; } TlsConnection; typedef struct Msg{ int tag; union { struct { int version; uchar random[RandomSize]; Bytes* sid; Ints* ciphers; Bytes* compressors; } clientHello; struct { int version; uchar random[RandomSize]; Bytes* sid; int cipher; int compressor; } serverHello; struct { int ncert; Bytes **certs; } certificate; struct { Bytes *types; int nca; Bytes **cas; } certificateRequest; struct { Bytes *key; } clientKeyExchange; Finished finished; } u; } Msg; typedef struct TlsSec{ char *server; // name of remote; nil for server int ok; // <0 killed; == 0 in progress; >0 reusable RSApub *rsapub; AuthRpc *rpc; // factotum for rsa private key uchar sec[MasterSecretSize]; // master secret uchar crandom[RandomSize]; // client random uchar srandom[RandomSize]; // server random int clientVers; // version in ClientHello int vers; // final version // byte generation and handshake checksum void (*prf)(uchar*, int, uchar*, int, char*, uchar*, int, uchar*, int); void (*setFinished)(TlsSec*, MD5state, SHAstate, uchar*, int); int nfin; } TlsSec; enum { TLSVersion = 0x0301, SSL3Version = 0x0300, ProtocolVersion = 0x0301, // maximum version we speak MinProtoVersion = 0x0300, // limits on version we accept MaxProtoVersion = 0x03ff, }; // handshake type enum { HHelloRequest, HClientHello, HServerHello, HSSL2ClientHello = 9, /* local convention; see devtls.c */ HCertificate = 11, HServerKeyExchange, HCertificateRequest, HServerHelloDone, HCertificateVerify, HClientKeyExchange, HFinished = 20, HMax }; // alerts enum { ECloseNotify = 0, EUnexpectedMessage = 10, EBadRecordMac = 20, EDecryptionFailed = 21, ERecordOverflow = 22, EDecompressionFailure = 30, EHandshakeFailure = 40, ENoCertificate = 41, EBadCertificate = 42, EUnsupportedCertificate = 43, ECertificateRevoked = 44, ECertificateExpired = 45, ECertificateUnknown = 46, EIllegalParameter = 47, EUnknownCa = 48, EAccessDenied = 49, EDecodeError = 50, EDecryptError = 51, EExportRestriction = 60, EProtocolVersion = 70, EInsufficientSecurity = 71, EInternalError = 80, EUserCanceled = 90, ENoRenegotiation = 100, EMax = 256 }; // cipher suites enum { TLS_NULL_WITH_NULL_NULL = 0x0000, TLS_RSA_WITH_NULL_MD5 = 0x0001, TLS_RSA_WITH_NULL_SHA = 0x0002, TLS_RSA_EXPORT_WITH_RC4_40_MD5 = 0x0003, TLS_RSA_WITH_RC4_128_MD5 = 0x0004, TLS_RSA_WITH_RC4_128_SHA = 0x0005, TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 = 0X0006, TLS_RSA_WITH_IDEA_CBC_SHA = 0X0007, TLS_RSA_EXPORT_WITH_DES40_CBC_SHA = 0X0008, TLS_RSA_WITH_DES_CBC_SHA = 0X0009, TLS_RSA_WITH_3DES_EDE_CBC_SHA = 0X000A, TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA = 0X000B, TLS_DH_DSS_WITH_DES_CBC_SHA = 0X000C, TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA = 0X000D, TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA = 0X000E, TLS_DH_RSA_WITH_DES_CBC_SHA = 0X000F, TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA = 0X0010, TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA = 0X0011, TLS_DHE_DSS_WITH_DES_CBC_SHA = 0X0012, TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA = 0X0013, // ZZZ must be implemented for tls1.0 compliance TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA = 0X0014, TLS_DHE_RSA_WITH_DES_CBC_SHA = 0X0015, TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA = 0X0016, TLS_DH_anon_EXPORT_WITH_RC4_40_MD5 = 0x0017, TLS_DH_anon_WITH_RC4_128_MD5 = 0x0018, TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA = 0X0019, TLS_DH_anon_WITH_DES_CBC_SHA = 0X001A, TLS_DH_anon_WITH_3DES_EDE_CBC_SHA = 0X001B, TLS_RSA_WITH_AES_128_CBC_SHA = 0X002f, // aes, aka rijndael with 128 bit blocks TLS_DH_DSS_WITH_AES_128_CBC_SHA = 0X0030, TLS_DH_RSA_WITH_AES_128_CBC_SHA = 0X0031, TLS_DHE_DSS_WITH_AES_128_CBC_SHA = 0X0032, TLS_DHE_RSA_WITH_AES_128_CBC_SHA = 0X0033, TLS_DH_anon_WITH_AES_128_CBC_SHA = 0X0034, TLS_RSA_WITH_AES_256_CBC_SHA = 0X0035, TLS_DH_DSS_WITH_AES_256_CBC_SHA = 0X0036, TLS_DH_RSA_WITH_AES_256_CBC_SHA = 0X0037, TLS_DHE_DSS_WITH_AES_256_CBC_SHA = 0X0038, TLS_DHE_RSA_WITH_AES_256_CBC_SHA = 0X0039, TLS_DH_anon_WITH_AES_256_CBC_SHA = 0X003A, CipherMax }; // compression methods enum { CompressionNull = 0, CompressionMax }; static Algs cipherAlgs[] = { {"rc4_128", "md5", 2*(16+MD5dlen), TLS_RSA_WITH_RC4_128_MD5}, {"rc4_128", "sha1", 2*(16+SHA1dlen), TLS_RSA_WITH_RC4_128_SHA}, {"3des_ede_cbc", "sha1", 2*(4*8+SHA1dlen), TLS_RSA_WITH_3DES_EDE_CBC_SHA}, {"aes_128_cbc", "sha1", 2*(16+16+SHA1dlen), TLS_RSA_WITH_AES_128_CBC_SHA}, {"aes_256_cbc", "sha1", 2*(32+16+SHA1dlen), TLS_RSA_WITH_AES_256_CBC_SHA} }; static uchar compressors[] = { CompressionNull, }; static TlsConnection *tlsServer2(int ctl, int hand, uchar *cert, int ncert, int (*trace)(char*fmt, ...), PEMChain *chain); static TlsConnection *tlsClient2(int ctl, int hand, uchar *csid, int ncsid, int (*trace)(char*fmt, ...)); static void msgClear(Msg *m); static char* msgPrint(char *buf, int n, Msg *m); static int msgRecv(TlsConnection *c, Msg *m); static int msgSend(TlsConnection *c, Msg *m, int act); static void tlsError(TlsConnection *c, int err, char *msg, ...); #pragma varargck argpos tlsError 3 static int setVersion(TlsConnection *c, int version); static int finishedMatch(TlsConnection *c, Finished *f); static void tlsConnectionFree(TlsConnection *c); static int setAlgs(TlsConnection *c, int a); static int okCipher(Ints *cv); static int okCompression(Bytes *cv); static int initCiphers(void); static Ints* makeciphers(void); static TlsSec* tlsSecInits(int cvers, uchar *csid, int ncsid, uchar *crandom, uchar *ssid, int *nssid, uchar *srandom); static int tlsSecSecrets(TlsSec *sec, int vers, uchar *epm, int nepm, uchar *kd, int nkd); static TlsSec* tlsSecInitc(int cvers, uchar *crandom); static int tlsSecSecretc(TlsSec *sec, uchar *sid, int nsid, uchar *srandom, uchar *cert, int ncert, int vers, uchar **epm, int *nepm, uchar *kd, int nkd); static int tlsSecFinished(TlsSec *sec, MD5state md5, SHAstate sha1, uchar *fin, int nfin, int isclient); static void tlsSecOk(TlsSec *sec); static void tlsSecKill(TlsSec *sec); static void tlsSecClose(TlsSec *sec); static void setMasterSecret(TlsSec *sec, Bytes *pm); static void serverMasterSecret(TlsSec *sec, uchar *epm, int nepm); static void setSecrets(TlsSec *sec, uchar *kd, int nkd); static int clientMasterSecret(TlsSec *sec, RSApub *pub, uchar **epm, int *nepm); static Bytes *pkcs1_encrypt(Bytes* data, RSApub* key, int blocktype); static Bytes *pkcs1_decrypt(TlsSec *sec, uchar *epm, int nepm); static void tlsSetFinished(TlsSec *sec, MD5state hsmd5, SHAstate hssha1, uchar *finished, int isClient); static void sslSetFinished(TlsSec *sec, MD5state hsmd5, SHAstate hssha1, uchar *finished, int isClient); static void sslPRF(uchar *buf, int nbuf, uchar *key, int nkey, char *label, uchar *seed0, int nseed0, uchar *seed1, int nseed1); static int setVers(TlsSec *sec, int version); static AuthRpc* factotum_rsa_open(uchar *cert, int certlen); static mpint* factotum_rsa_decrypt(AuthRpc *rpc, mpint *cipher); static void factotum_rsa_close(AuthRpc*rpc); static void* emalloc(int); static void* erealloc(void*, int); static void put32(uchar *p, u32int); static void put24(uchar *p, int); static void put16(uchar *p, int); static u32int get32(uchar *p); static int get24(uchar *p); static int get16(uchar *p); static Bytes* newbytes(int len); static Bytes* makebytes(uchar* buf, int len); static void freebytes(Bytes* b); static Ints* newints(int len); static Ints* makeints(int* buf, int len); static void freeints(Ints* b); //================= client/server ======================== // push TLS onto fd, returning new (application) file descriptor // or -1 if error. int tlsServer(int fd, TLSconn *conn) { char buf[8]; char dname[64]; int n, data, ctl, hand; TlsConnection *tls; if(conn == nil) return -1; ctl = open("#a/tls/clone", ORDWR); if(ctl < 0) return -1; n = read(ctl, buf, sizeof(buf)-1); if(n < 0){ close(ctl); return -1; } buf[n] = 0; sprint(conn->dir, "#a/tls/%s", buf); sprint(dname, "#a/tls/%s/hand", buf); hand = open(dname, ORDWR); if(hand < 0){ close(ctl); return -1; } fprint(ctl, "fd %d 0x%x", fd, ProtocolVersion); tls = tlsServer2(ctl, hand, conn->cert, conn->certlen, conn->trace, conn->chain); sprint(dname, "#a/tls/%s/data", buf); data = open(dname, ORDWR); close(fd); close(hand); close(ctl); if(data < 0){ return -1; } if(tls == nil){ close(data); return -1; } if(conn->cert) free(conn->cert); conn->cert = 0; // client certificates are not yet implemented conn->certlen = 0; conn->sessionIDlen = tls->sid->len; conn->sessionID = emalloc(conn->sessionIDlen); memcpy(conn->sessionID, tls->sid->data, conn->sessionIDlen); if(conn->sessionKey != nil && conn->sessionType != nil && strcmp(conn->sessionType, "ttls") == 0) tls->sec->prf(conn->sessionKey, conn->sessionKeylen, tls->sec->sec, MasterSecretSize, conn->sessionConst, tls->sec->crandom, RandomSize, tls->sec->srandom, RandomSize); tlsConnectionFree(tls); return data; } // push TLS onto fd, returning new (application) file descriptor // or -1 if error. int tlsClient(int fd, TLSconn *conn) { char buf[8]; char dname[64]; int n, data, ctl, hand; TlsConnection *tls; if(!conn) return -1; ctl = open("#a/tls/clone", ORDWR); if(ctl < 0) return -1; n = read(ctl, buf, sizeof(buf)-1); if(n < 0){ close(ctl); return -1; } buf[n] = 0; sprint(conn->dir, "#a/tls/%s", buf); sprint(dname, "#a/tls/%s/hand", buf); hand = open(dname, ORDWR); if(hand < 0){ close(ctl); return -1; } sprint(dname, "#a/tls/%s/data", buf); data = open(dname, ORDWR); if(data < 0) return -1; fprint(ctl, "fd %d 0x%x", fd, ProtocolVersion); fprint(ctl, "debug"); tls = tlsClient2(ctl, hand, conn->sessionID, conn->sessionIDlen, conn->trace); close(fd); close(hand); close(ctl); if(tls == nil){ close(data); return -1; } conn->certlen = tls->cert->len; conn->cert = emalloc(conn->certlen); memcpy(conn->cert, tls->cert->data, conn->certlen); conn->sessionIDlen = tls->sid->len; conn->sessionID = emalloc(conn->sessionIDlen); memcpy(conn->sessionID, tls->sid->data, conn->sessionIDlen); if(conn->sessionKey != nil && conn->sessionType != nil && strcmp(conn->sessionType, "ttls") == 0) tls->sec->prf(conn->sessionKey, conn->sessionKeylen, tls->sec->sec, MasterSecretSize, conn->sessionConst, tls->sec->crandom, RandomSize, tls->sec->srandom, RandomSize); tlsConnectionFree(tls); return data; } static int countchain(PEMChain *p) { int i = 0; while (p) { i++; p = p->next; } return i; } static TlsConnection * tlsServer2(int ctl, int hand, uchar *cert, int ncert, int (*trace)(char*fmt, ...), PEMChain *chp) { TlsConnection *c; Msg m; Bytes *csid; uchar sid[SidSize], kd[MaxKeyData]; char *secrets; int cipher, compressor, nsid, rv, numcerts, i; if(trace) trace("tlsServer2\n"); if(!initCiphers()) return nil; c = emalloc(sizeof(TlsConnection)); c->ctl = ctl; c->hand = hand; c->trace = trace; c->version = ProtocolVersion; memset(&m, 0, sizeof(m)); if(!msgRecv(c, &m)){ if(trace) trace("initial msgRecv failed\n"); goto Err; } if(m.tag != HClientHello) { tlsError(c, EUnexpectedMessage, "expected a client hello"); goto Err; } c->clientVersion = m.u.clientHello.version; if(trace) trace("ClientHello version %x\n", c->clientVersion); if(setVersion(c, m.u.clientHello.version) < 0) { tlsError(c, EIllegalParameter, "incompatible version"); goto Err; } memmove(c->crandom, m.u.clientHello.random, RandomSize); cipher = okCipher(m.u.clientHello.ciphers); if(cipher < 0) { // reply with EInsufficientSecurity if we know that's the case if(cipher == -2) tlsError(c, EInsufficientSecurity, "cipher suites too weak"); else tlsError(c, EHandshakeFailure, "no matching cipher suite"); goto Err; } if(!setAlgs(c, cipher)){ tlsError(c, EHandshakeFailure, "no matching cipher suite"); goto Err; } compressor = okCompression(m.u.clientHello.compressors); if(compressor < 0) { tlsError(c, EHandshakeFailure, "no matching compressor"); goto Err; } csid = m.u.clientHello.sid; if(trace) trace(" cipher %d, compressor %d, csidlen %d\n", cipher, compressor, csid->len); c->sec = tlsSecInits(c->clientVersion, csid->data, csid->len, c->crandom, sid, &nsid, c->srandom); if(c->sec == nil){ tlsError(c, EHandshakeFailure, "can't initialize security: %r"); goto Err; } c->sec->rpc = factotum_rsa_open(cert, ncert); if(c->sec->rpc == nil){ tlsError(c, EHandshakeFailure, "factotum_rsa_open: %r"); goto Err; } c->sec->rsapub = X509toRSApub(cert, ncert, nil, 0); msgClear(&m); m.tag = HServerHello; m.u.serverHello.version = c->version; memmove(m.u.serverHello.random, c->srandom, RandomSize); m.u.serverHello.cipher = cipher; m.u.serverHello.compressor = compressor; c->sid = makebytes(sid, nsid); m.u.serverHello.sid = makebytes(c->sid->data, c->sid->len); if(!msgSend(c, &m, AQueue)) goto Err; msgClear(&m); m.tag = HCertificate; numcerts = countchain(chp); m.u.certificate.ncert = 1 + numcerts; m.u.certificate.certs = emalloc(m.u.certificate.ncert * sizeof(Bytes)); m.u.certificate.certs[0] = makebytes(cert, ncert); for (i = 0; i < numcerts && chp; i++, chp = chp->next) m.u.certificate.certs[i+1] = makebytes(chp->pem, chp->pemlen); if(!msgSend(c, &m, AQueue)) goto Err; msgClear(&m); m.tag = HServerHelloDone; if(!msgSend(c, &m, AFlush)) goto Err; msgClear(&m); if(!msgRecv(c, &m)) goto Err; if(m.tag != HClientKeyExchange) { tlsError(c, EUnexpectedMessage, "expected a client key exchange"); goto Err; } if(tlsSecSecrets(c->sec, c->version, m.u.clientKeyExchange.key->data, m.u.clientKeyExchange.key->len, kd, c->nsecret) < 0){ tlsError(c, EHandshakeFailure, "couldn't set secrets: %r"); goto Err; } if(trace) trace("tls secrets\n"); secrets = (char*)emalloc(2*c->nsecret); enc64(secrets, 2*c->nsecret, kd, c->nsecret); rv = fprint(c->ctl, "secret %s %s 0 %s", c->digest, c->enc, secrets); memset(secrets, 0, 2*c->nsecret); free(secrets); memset(kd, 0, c->nsecret); if(rv < 0){ tlsError(c, EHandshakeFailure, "can't set keys: %r"); goto Err; } msgClear(&m); /* no CertificateVerify; skip to Finished */ if(tlsSecFinished(c->sec, c->hsmd5, c->hssha1, c->finished.verify, c->finished.n, 1) < 0){ tlsError(c, EInternalError, "can't set finished: %r"); goto Err; } if(!msgRecv(c, &m)) goto Err; if(m.tag != HFinished) { tlsError(c, EUnexpectedMessage, "expected a finished"); goto Err; } if(!finishedMatch(c, &m.u.finished)) { tlsError(c, EHandshakeFailure, "finished verification failed"); goto Err; } msgClear(&m); /* change cipher spec */ if(fprint(c->ctl, "changecipher") < 0){ tlsError(c, EInternalError, "can't enable cipher: %r"); goto Err; } if(tlsSecFinished(c->sec, c->hsmd5, c->hssha1, c->finished.verify, c->finished.n, 0) < 0){ tlsError(c, EInternalError, "can't set finished: %r"); goto Err; } m.tag = HFinished; m.u.finished = c->finished; if(!msgSend(c, &m, AFlush)) goto Err; msgClear(&m); if(trace) trace("tls finished\n"); if(fprint(c->ctl, "opened") < 0) goto Err; tlsSecOk(c->sec); return c; Err: msgClear(&m); tlsConnectionFree(c); return 0; } static TlsConnection * tlsClient2(int ctl, int hand, uchar *csid, int ncsid, int (*trace)(char*fmt, ...)) { TlsConnection *c; Msg m; uchar kd[MaxKeyData], *epm; char *secrets; int creq, nepm, rv; if(!initCiphers()) return nil; epm = nil; c = emalloc(sizeof(TlsConnection)); c->version = ProtocolVersion; c->ctl = ctl; c->hand = hand; c->trace = trace; c->isClient = 1; c->clientVersion = c->version; c->sec = tlsSecInitc(c->clientVersion, c->crandom); if(c->sec == nil) goto Err; /* client hello */ memset(&m, 0, sizeof(m)); m.tag = HClientHello; m.u.clientHello.version = c->clientVersion; memmove(m.u.clientHello.random, c->crandom, RandomSize); m.u.clientHello.sid = makebytes(csid, ncsid); m.u.clientHello.ciphers = makeciphers(); m.u.clientHello.compressors = makebytes(compressors,sizeof(compressors)); if(!msgSend(c, &m, AFlush)) goto Err; msgClear(&m); /* server hello */ if(!msgRecv(c, &m)) goto Err; if(m.tag != HServerHello) { tlsError(c, EUnexpectedMessage, "expected a server hello"); goto Err; } if(setVersion(c, m.u.serverHello.version) < 0) { tlsError(c, EIllegalParameter, "incompatible version %r"); goto Err; } memmove(c->srandom, m.u.serverHello.random, RandomSize); c->sid = makebytes(m.u.serverHello.sid->data, m.u.serverHello.sid->len); if(c->sid->len != 0 && c->sid->len != SidSize) { tlsError(c, EIllegalParameter, "invalid server session identifier"); goto Err; } if(!setAlgs(c, m.u.serverHello.cipher)) { tlsError(c, EIllegalParameter, "invalid cipher suite"); goto Err; } if(m.u.serverHello.compressor != CompressionNull) { tlsError(c, EIllegalParameter, "invalid compression"); goto Err; } msgClear(&m); /* certificate */ if(!msgRecv(c, &m) || m.tag != HCertificate) { tlsError(c, EUnexpectedMessage, "expected a certificate"); goto Err; } if(m.u.certificate.ncert < 1) { tlsError(c, EIllegalParameter, "runt certificate"); goto Err; } c->cert = makebytes(m.u.certificate.certs[0]->data, m.u.certificate.certs[0]->len); msgClear(&m); /* server key exchange (optional) */ if(!msgRecv(c, &m)) goto Err; if(m.tag == HServerKeyExchange) { tlsError(c, EUnexpectedMessage, "got an server key exchange"); goto Err; // If implementing this later, watch out for rollback attack // described in Wagner Schneier 1996, section 4.4. } /* certificate request (optional) */ creq = 0; if(m.tag == HCertificateRequest) { creq = 1; msgClear(&m); if(!msgRecv(c, &m)) goto Err; } if(m.tag != HServerHelloDone) { tlsError(c, EUnexpectedMessage, "expected a server hello done"); goto Err; } msgClear(&m); if(tlsSecSecretc(c->sec, c->sid->data, c->sid->len, c->srandom, c->cert->data, c->cert->len, c->version, &epm, &nepm, kd, c->nsecret) < 0){ tlsError(c, EBadCertificate, "invalid x509/rsa certificate"); goto Err; } secrets = (char*)emalloc(2*c->nsecret); enc64(secrets, 2*c->nsecret, kd, c->nsecret); rv = fprint(c->ctl, "secret %s %s 1 %s", c->digest, c->enc, secrets); memset(secrets, 0, 2*c->nsecret); free(secrets); memset(kd, 0, c->nsecret); if(rv < 0){ tlsError(c, EHandshakeFailure, "can't set keys: %r"); goto Err; } if(creq) { /* send a zero length certificate */ m.tag = HCertificate; if(!msgSend(c, &m, AFlush)) goto Err; msgClear(&m); } /* client key exchange */ m.tag = HClientKeyExchange; m.u.clientKeyExchange.key = makebytes(epm, nepm); free(epm); epm = nil; if(m.u.clientKeyExchange.key == nil) { tlsError(c, EHandshakeFailure, "can't set secret: %r"); goto Err; } if(!msgSend(c, &m, AFlush)) goto Err; msgClear(&m); /* change cipher spec */ if(fprint(c->ctl, "changecipher") < 0){ tlsError(c, EInternalError, "can't enable cipher: %r"); goto Err; } // Cipherchange must occur immediately before Finished to avoid // potential hole; see section 4.3 of Wagner Schneier 1996. if(tlsSecFinished(c->sec, c->hsmd5, c->hssha1, c->finished.verify, c->finished.n, 1) < 0){ tlsError(c, EInternalError, "can't set finished 1: %r"); goto Err; } m.tag = HFinished; m.u.finished = c->finished; if(!msgSend(c, &m, AFlush)) { fprint(2, "tlsClient nepm=%d\n", nepm); tlsError(c, EInternalError, "can't flush after client Finished: %r"); goto Err; } msgClear(&m); if(tlsSecFinished(c->sec, c->hsmd5, c->hssha1, c->finished.verify, c->finished.n, 0) < 0){ fprint(2, "tlsClient nepm=%d\n", nepm); tlsError(c, EInternalError, "can't set finished 0: %r"); goto Err; } if(!msgRecv(c, &m)) { fprint(2, "tlsClient nepm=%d\n", nepm); tlsError(c, EInternalError, "can't read server Finished: %r"); goto Err; } if(m.tag != HFinished) { fprint(2, "tlsClient nepm=%d\n", nepm); tlsError(c, EUnexpectedMessage, "expected a Finished msg from server"); goto Err; } if(!finishedMatch(c, &m.u.finished)) { tlsError(c, EHandshakeFailure, "finished verification failed"); goto Err; } msgClear(&m); if(fprint(c->ctl, "opened") < 0){ if(trace) trace("unable to do final open: %r\n"); goto Err; } tlsSecOk(c->sec); return c; Err: free(epm); msgClear(&m); tlsConnectionFree(c); return 0; } //================= message functions ======================== static uchar sendbuf[9000], *sendp; static int msgSend(TlsConnection *c, Msg *m, int act) { uchar *p; // sendp = start of new message; p = write pointer int nn, n, i; if(sendp == nil) sendp = sendbuf; p = sendp; if(c->trace) c->trace("send %s", msgPrint((char*)p, (sizeof sendbuf) - (p-sendbuf), m)); p[0] = m->tag; // header - fill in size later p += 4; switch(m->tag) { default: tlsError(c, EInternalError, "can't encode a %d", m->tag); goto Err; case HClientHello: // version put16(p, m->u.clientHello.version); p += 2; // random memmove(p, m->u.clientHello.random, RandomSize); p += RandomSize; // sid n = m->u.clientHello.sid->len; assert(n < 256); p[0] = n; memmove(p+1, m->u.clientHello.sid->data, n); p += n+1; n = m->u.clientHello.ciphers->len; assert(n > 0 && n < 200); put16(p, n*2); p += 2; for(i=0; iu.clientHello.ciphers->data[i]); p += 2; } n = m->u.clientHello.compressors->len; assert(n > 0); p[0] = n; memmove(p+1, m->u.clientHello.compressors->data, n); p += n+1; break; case HServerHello: put16(p, m->u.serverHello.version); p += 2; // random memmove(p, m->u.serverHello.random, RandomSize); p += RandomSize; // sid n = m->u.serverHello.sid->len; assert(n < 256); p[0] = n; memmove(p+1, m->u.serverHello.sid->data, n); p += n+1; put16(p, m->u.serverHello.cipher); p += 2; p[0] = m->u.serverHello.compressor; p += 1; break; case HServerHelloDone: break; case HCertificate: nn = 0; for(i = 0; i < m->u.certificate.ncert; i++) nn += 3 + m->u.certificate.certs[i]->len; if(p + 3 + nn - sendbuf > sizeof(sendbuf)) { tlsError(c, EInternalError, "output buffer too small for certificate"); goto Err; } put24(p, nn); p += 3; for(i = 0; i < m->u.certificate.ncert; i++){ put24(p, m->u.certificate.certs[i]->len); p += 3; memmove(p, m->u.certificate.certs[i]->data, m->u.certificate.certs[i]->len); p += m->u.certificate.certs[i]->len; } break; case HClientKeyExchange: n = m->u.clientKeyExchange.key->len; if(c->version != SSL3Version){ put16(p, n); p += 2; } memmove(p, m->u.clientKeyExchange.key->data, n); p += n; break; case HFinished: memmove(p, m->u.finished.verify, m->u.finished.n); p += m->u.finished.n; break; } // go back and fill in size n = p-sendp; assert(p <= sendbuf+sizeof(sendbuf)); put24(sendp+1, n-4); // remember hash of Handshake messages if(m->tag != HHelloRequest) { md5(sendp, n, 0, &c->hsmd5); sha1(sendp, n, 0, &c->hssha1); } sendp = p; if(act == AFlush){ sendp = sendbuf; fprint(2, "write(%d, %s, %d)\n", c->hand, sendbuf, p-sendbuf); if(write(c->hand, sendbuf, p-sendbuf) < 0){ fprint(2, "write error: %r\n"); goto Err; } } msgClear(m); return 1; Err: msgClear(m); return 0; } static uchar* tlsReadN(TlsConnection *c, int n) { uchar *p; int nn, nr; nn = c->ep - c->rp; if(nn < n){ if(c->rp != c->buf){ memmove(c->buf, c->rp, nn); c->rp = c->buf; c->ep = &c->buf[nn]; } for(; nn < n; nn += nr) { nr = read(c->hand, &c->rp[nn], n - nn); if(nr <= 0) return nil; c->ep += nr; } } p = c->rp; c->rp += n; return p; } static int msgRecv(TlsConnection *c, Msg *m) { uchar *p; int type, n, nn, i, nsid, nrandom, nciph; for(;;) { p = tlsReadN(c, 4); if(p == nil) return 0; type = p[0]; n = get24(p+1); if(type != HHelloRequest) break; if(n != 0) { tlsError(c, EDecodeError, "invalid hello request during handshake"); return 0; } } if(n > sizeof(c->buf)) { tlsError(c, EDecodeError, "handshake message too long %d %d", n, sizeof(c->buf)); return 0; } if(type == HSSL2ClientHello){ /* Cope with an SSL3 ClientHello expressed in SSL2 record format. This is sent by some clients that we must interoperate with, such as Java's JSSE and Microsoft's Internet Explorer. */ p = tlsReadN(c, n); if(p == nil) return 0; md5(p, n, 0, &c->hsmd5); sha1(p, n, 0, &c->hssha1); m->tag = HClientHello; if(n < 22) goto Short; m->u.clientHello.version = get16(p+1); p += 3; n -= 3; nn = get16(p); /* cipher_spec_len */ nsid = get16(p + 2); nrandom = get16(p + 4); p += 6; n -= 6; if(nsid != 0 /* no sid's, since shouldn't restart using ssl2 header */ || nrandom < 16 || nn % 3) goto Err; if(c->trace && (n - nrandom != nn)) c->trace("n-nrandom!=nn: n=%d nrandom=%d nn=%d\n", n, nrandom, nn); /* ignore ssl2 ciphers and look for {0x00, ssl3 cipher} */ nciph = 0; for(i = 0; i < nn; i += 3) if(p[i] == 0) nciph++; m->u.clientHello.ciphers = newints(nciph); nciph = 0; for(i = 0; i < nn; i += 3) if(p[i] == 0) m->u.clientHello.ciphers->data[nciph++] = get16(&p[i + 1]); p += nn; m->u.clientHello.sid = makebytes(nil, 0); if(nrandom > RandomSize) nrandom = RandomSize; memset(m->u.clientHello.random, 0, RandomSize - nrandom); memmove(&m->u.clientHello.random[RandomSize - nrandom], p, nrandom); m->u.clientHello.compressors = newbytes(1); m->u.clientHello.compressors->data[0] = CompressionNull; goto Ok; } md5(p, 4, 0, &c->hsmd5); sha1(p, 4, 0, &c->hssha1); p = tlsReadN(c, n); if(p == nil) return 0; md5(p, n, 0, &c->hsmd5); sha1(p, n, 0, &c->hssha1); m->tag = type; switch(type) { default: tlsError(c, EUnexpectedMessage, "can't decode a %d", type); goto Err; case HClientHello: if(n < 2) goto Short; m->u.clientHello.version = get16(p); p += 2; n -= 2; if(n < RandomSize) goto Short; memmove(m->u.clientHello.random, p, RandomSize); p += RandomSize; n -= RandomSize; if(n < 1 || n < p[0]+1) goto Short; m->u.clientHello.sid = makebytes(p+1, p[0]); p += m->u.clientHello.sid->len+1; n -= m->u.clientHello.sid->len+1; if(n < 2) goto Short; nn = get16(p); p += 2; n -= 2; if((nn & 1) || n < nn || nn < 2) goto Short; m->u.clientHello.ciphers = newints(nn >> 1); for(i = 0; i < nn; i += 2) m->u.clientHello.ciphers->data[i >> 1] = get16(&p[i]); p += nn; n -= nn; if(n < 1 || n < p[0]+1 || p[0] == 0) goto Short; nn = p[0]; m->u.clientHello.compressors = newbytes(nn); memmove(m->u.clientHello.compressors->data, p+1, nn); n -= nn + 1; break; case HServerHello: if(n < 2) goto Short; m->u.serverHello.version = get16(p); p += 2; n -= 2; if(n < RandomSize) goto Short; memmove(m->u.serverHello.random, p, RandomSize); p += RandomSize; n -= RandomSize; if(n < 1 || n < p[0]+1) goto Short; m->u.serverHello.sid = makebytes(p+1, p[0]); p += m->u.serverHello.sid->len+1; n -= m->u.serverHello.sid->len+1; if(n < 3) goto Short; m->u.serverHello.cipher = get16(p); m->u.serverHello.compressor = p[2]; n -= 3; break; case HCertificate: if(n < 3) goto Short; nn = get24(p); p += 3; n -= 3; if(n != nn) goto Short; /* certs */ i = 0; while(n > 0) { if(n < 3) goto Short; nn = get24(p); p += 3; n -= 3; if(nn > n) goto Short; m->u.certificate.ncert = i+1; m->u.certificate.certs = erealloc(m->u.certificate.certs, (i+1)*sizeof(Bytes)); m->u.certificate.certs[i] = makebytes(p, nn); p += nn; n -= nn; i++; } break; case HCertificateRequest: if(n < 1) goto Short; nn = p[0]; p += 1; n -= 1; if(nn < 1 || nn > n) goto Short; m->u.certificateRequest.types = makebytes(p, nn); p += nn; n -= nn; if(n < 2) goto Short; nn = get16(p); p += 2; n -= 2; /* nn == 0 can happen; yahoo's servers do it */ if(nn != n) goto Short; /* cas */ i = 0; while(n > 0) { if(n < 2) goto Short; nn = get16(p); p += 2; n -= 2; if(nn < 1 || nn > n) goto Short; m->u.certificateRequest.nca = i+1; m->u.certificateRequest.cas = erealloc( m->u.certificateRequest.cas, (i+1)*sizeof(Bytes)); m->u.certificateRequest.cas[i] = makebytes(p, nn); p += nn; n -= nn; i++; } break; case HServerHelloDone: break; case HClientKeyExchange: /* * this message depends upon the encryption selected * assume rsa. */ if(c->version == SSL3Version) nn = n; else{ if(n < 2) goto Short; nn = get16(p); p += 2; n -= 2; } if(n < nn) goto Short; m->u.clientKeyExchange.key = makebytes(p, nn); n -= nn; break; case HFinished: m->u.finished.n = c->finished.n; if(n < m->u.finished.n) goto Short; memmove(m->u.finished.verify, p, m->u.finished.n); n -= m->u.finished.n; break; } if(type != HClientHello && n != 0) goto Short; Ok: if(c->trace){ char *buf; buf = emalloc(8000); c->trace("recv %s", msgPrint(buf, 8000, m)); free(buf); } return 1; Short: tlsError(c, EDecodeError, "handshake message has invalid length"); Err: msgClear(m); return 0; } static void msgClear(Msg *m) { int i; switch(m->tag) { default: sysfatal("msgClear: unknown message type: %d", m->tag); case HHelloRequest: break; case HClientHello: freebytes(m->u.clientHello.sid); freeints(m->u.clientHello.ciphers); freebytes(m->u.clientHello.compressors); break; case HServerHello: freebytes(m->u.clientHello.sid); break; case HCertificate: for(i=0; iu.certificate.ncert; i++) freebytes(m->u.certificate.certs[i]); free(m->u.certificate.certs); break; case HCertificateRequest: freebytes(m->u.certificateRequest.types); for(i=0; iu.certificateRequest.nca; i++) freebytes(m->u.certificateRequest.cas[i]); free(m->u.certificateRequest.cas); break; case HServerHelloDone: break; case HClientKeyExchange: freebytes(m->u.clientKeyExchange.key); break; case HFinished: break; } memset(m, 0, sizeof(Msg)); } static char * bytesPrint(char *bs, char *be, char *s0, Bytes *b, char *s1) { int i; if(s0) bs = seprint(bs, be, "%s", s0); bs = seprint(bs, be, "["); if(b == nil) bs = seprint(bs, be, "nil"); else for(i=0; ilen; i++) bs = seprint(bs, be, "%.2x ", b->data[i]); bs = seprint(bs, be, "]"); if(s1) bs = seprint(bs, be, "%s", s1); return bs; } static char * intsPrint(char *bs, char *be, char *s0, Ints *b, char *s1) { int i; if(s0) bs = seprint(bs, be, "%s", s0); bs = seprint(bs, be, "["); if(b == nil) bs = seprint(bs, be, "nil"); else for(i=0; ilen; i++) bs = seprint(bs, be, "%x ", b->data[i]); bs = seprint(bs, be, "]"); if(s1) bs = seprint(bs, be, "%s", s1); return bs; } static char* msgPrint(char *buf, int n, Msg *m) { int i; char *bs = buf, *be = buf+n; switch(m->tag) { default: bs = seprint(bs, be, "unknown %d\n", m->tag); break; case HClientHello: bs = seprint(bs, be, "ClientHello\n"); bs = seprint(bs, be, "\tversion: %.4x\n", m->u.clientHello.version); bs = seprint(bs, be, "\trandom: "); for(i=0; iu.clientHello.random[i]); bs = seprint(bs, be, "\n"); bs = bytesPrint(bs, be, "\tsid: ", m->u.clientHello.sid, "\n"); bs = intsPrint(bs, be, "\tciphers: ", m->u.clientHello.ciphers, "\n"); bs = bytesPrint(bs, be, "\tcompressors: ", m->u.clientHello.compressors, "\n"); break; case HServerHello: bs = seprint(bs, be, "ServerHello\n"); bs = seprint(bs, be, "\tversion: %.4x\n", m->u.serverHello.version); bs = seprint(bs, be, "\trandom: "); for(i=0; iu.serverHello.random[i]); bs = seprint(bs, be, "\n"); bs = bytesPrint(bs, be, "\tsid: ", m->u.serverHello.sid, "\n"); bs = seprint(bs, be, "\tcipher: %.4x\n", m->u.serverHello.cipher); bs = seprint(bs, be, "\tcompressor: %.2x\n", m->u.serverHello.compressor); break; case HCertificate: bs = seprint(bs, be, "Certificate\n"); for(i=0; iu.certificate.ncert; i++) bs = bytesPrint(bs, be, "\t", m->u.certificate.certs[i], "\n"); break; case HCertificateRequest: bs = seprint(bs, be, "CertificateRequest\n"); bs = bytesPrint(bs, be, "\ttypes: ", m->u.certificateRequest.types, "\n"); bs = seprint(bs, be, "\tcertificateauthorities\n"); for(i=0; iu.certificateRequest.nca; i++) bs = bytesPrint(bs, be, "\t\t", m->u.certificateRequest.cas[i], "\n"); break; case HServerHelloDone: bs = seprint(bs, be, "ServerHelloDone\n"); break; case HClientKeyExchange: bs = seprint(bs, be, "HClientKeyExchange\n"); bs = bytesPrint(bs, be, "\tkey: ", m->u.clientKeyExchange.key, "\n"); break; case HFinished: bs = seprint(bs, be, "HFinished\n"); for(i=0; iu.finished.n; i++) bs = seprint(bs, be, "%.2x", m->u.finished.verify[i]); bs = seprint(bs, be, "\n"); break; } USED(bs); return buf; } static void tlsError(TlsConnection *c, int err, char *fmt, ...) { char msg[512]; va_list arg; va_start(arg, fmt); vseprint(msg, msg+sizeof(msg), fmt, arg); va_end(arg); if(c->trace) c->trace("tlsError: %s\n", msg); else if(c->erred) fprint(2, "double error: %r, %s", msg); else werrstr("tls: local %s", msg); c->erred = 1; fprint(c->ctl, "alert %d", err); } // commit to specific version number static int setVersion(TlsConnection *c, int version) { if(c->verset || version > MaxProtoVersion || version < MinProtoVersion) return -1; if(version > c->version) version = c->version; if(version == SSL3Version) { c->version = version; c->finished.n = SSL3FinishedLen; }else if(version == TLSVersion){ c->version = version; c->finished.n = TLSFinishedLen; }else return -1; c->verset = 1; return fprint(c->ctl, "version 0x%x", version); } // confirm that received Finished message matches the expected value static int finishedMatch(TlsConnection *c, Finished *f) { return memcmp(f->verify, c->finished.verify, f->n) == 0; } // free memory associated with TlsConnection struct // (but don't close the TLS channel itself) static void tlsConnectionFree(TlsConnection *c) { tlsSecClose(c->sec); freebytes(c->sid); freebytes(c->cert); memset(c, 0, sizeof(c)); free(c); } //================= cipher choices ======================== static int weakCipher[CipherMax] = { 1, /* TLS_NULL_WITH_NULL_NULL */ 1, /* TLS_RSA_WITH_NULL_MD5 */ 1, /* TLS_RSA_WITH_NULL_SHA */ 1, /* TLS_RSA_EXPORT_WITH_RC4_40_MD5 */ 0, /* TLS_RSA_WITH_RC4_128_MD5 */ 0, /* TLS_RSA_WITH_RC4_128_SHA */ 1, /* TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 */ 0, /* TLS_RSA_WITH_IDEA_CBC_SHA */ 1, /* TLS_RSA_EXPORT_WITH_DES40_CBC_SHA */ 0, /* TLS_RSA_WITH_DES_CBC_SHA */ 0, /* TLS_RSA_WITH_3DES_EDE_CBC_SHA */ 1, /* TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA */ 0, /* TLS_DH_DSS_WITH_DES_CBC_SHA */ 0, /* TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA */ 1, /* TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA */ 0, /* TLS_DH_RSA_WITH_DES_CBC_SHA */ 0, /* TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA */ 1, /* TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA */ 0, /* TLS_DHE_DSS_WITH_DES_CBC_SHA */ 0, /* TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA */ 1, /* TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA */ 0, /* TLS_DHE_RSA_WITH_DES_CBC_SHA */ 0, /* TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA */ 1, /* TLS_DH_anon_EXPORT_WITH_RC4_40_MD5 */ 1, /* TLS_DH_anon_WITH_RC4_128_MD5 */ 1, /* TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA */ 1, /* TLS_DH_anon_WITH_DES_CBC_SHA */ 1, /* TLS_DH_anon_WITH_3DES_EDE_CBC_SHA */ }; static int setAlgs(TlsConnection *c, int a) { int i; for(i = 0; i < nelem(cipherAlgs); i++){ if(cipherAlgs[i].tlsid == a){ c->enc = cipherAlgs[i].enc; c->digest = cipherAlgs[i].digest; c->nsecret = cipherAlgs[i].nsecret; if(c->nsecret > MaxKeyData) return 0; return 1; } } return 0; } static int okCipher(Ints *cv) { int weak, i, j, c; weak = 1; for(i = 0; i < cv->len; i++) { c = cv->data[i]; if(c >= CipherMax) weak = 0; else weak &= weakCipher[c]; for(j = 0; j < nelem(cipherAlgs); j++) if(cipherAlgs[j].ok && cipherAlgs[j].tlsid == c) return c; } if(weak) return -2; return -1; } static int okCompression(Bytes *cv) { int i, j, c; for(i = 0; i < cv->len; i++) { c = cv->data[i]; for(j = 0; j < nelem(compressors); j++) { if(compressors[j] == c) return c; } } return -1; } static Lock ciphLock; static int nciphers; static int initCiphers(void) { enum {MaxAlgF = 1024, MaxAlgs = 10}; char s[MaxAlgF], *flds[MaxAlgs]; int i, j, n, ok; lock(&ciphLock); if(nciphers){ unlock(&ciphLock); return nciphers; } j = open("#a/tls/encalgs", OREAD); if(j < 0){ werrstr("can't open #a/tls/encalgs: %r"); return 0; } n = read(j, s, MaxAlgF-1); close(j); if(n <= 0){ werrstr("nothing in #a/tls/encalgs: %r"); return 0; } s[n] = 0; n = getfields(s, flds, MaxAlgs, 1, " \t\r\n"); for(i = 0; i < nelem(cipherAlgs); i++){ ok = 0; for(j = 0; j < n; j++){ if(strcmp(cipherAlgs[i].enc, flds[j]) == 0){ ok = 1; break; } } cipherAlgs[i].ok = ok; } j = open("#a/tls/hashalgs", OREAD); if(j < 0){ werrstr("can't open #a/tls/hashalgs: %r"); return 0; } n = read(j, s, MaxAlgF-1); close(j); if(n <= 0){ werrstr("nothing in #a/tls/hashalgs: %r"); return 0; } s[n] = 0; n = getfields(s, flds, MaxAlgs, 1, " \t\r\n"); for(i = 0; i < nelem(cipherAlgs); i++){ ok = 0; for(j = 0; j < n; j++){ if(strcmp(cipherAlgs[i].digest, flds[j]) == 0){ ok = 1; break; } } cipherAlgs[i].ok &= ok; if(cipherAlgs[i].ok) nciphers++; } unlock(&ciphLock); return nciphers; } static Ints* makeciphers(void) { Ints *is; int i, j; is = newints(nciphers); j = 0; for(i = 0; i < nelem(cipherAlgs); i++){ if(cipherAlgs[i].ok) is->data[j++] = cipherAlgs[i].tlsid; } return is; } //================= security functions ======================== // given X.509 certificate, set up connection to factotum // for using corresponding private key static AuthRpc* factotum_rsa_open(uchar *cert, int certlen) { int afd; char *s; mpint *pub = nil; RSApub *rsapub; AuthRpc *rpc; // start talking to factotum if((afd = open("/mnt/factotum/rpc", ORDWR)) < 0) return nil; if((rpc = auth_allocrpc(afd)) == nil){ close(afd); return nil; } s = "proto=rsa service=tls role=client"; if(auth_rpc(rpc, "start", s, strlen(s)) != ARok){ factotum_rsa_close(rpc); return nil; } // roll factotum keyring around to match certificate rsapub = X509toRSApub(cert, certlen, nil, 0); while(1){ if(auth_rpc(rpc, "read", nil, 0) != ARok){ factotum_rsa_close(rpc); rpc = nil; goto done; } pub = strtomp(rpc->arg, nil, 16, nil); assert(pub != nil); if(mpcmp(pub,rsapub->n) == 0) break; } done: mpfree(pub); rsapubfree(rsapub); return rpc; } static mpint* factotum_rsa_decrypt(AuthRpc *rpc, mpint *cipher) { char *p; int rv; if((p = mptoa(cipher, 16, nil, 0)) == nil) return nil; rv = auth_rpc(rpc, "write", p, strlen(p)); free(p); if(rv != ARok || auth_rpc(rpc, "read", nil, 0) != ARok) return nil; mpfree(cipher); return strtomp(rpc->arg, nil, 16, nil); } static void factotum_rsa_close(AuthRpc*rpc) { if(!rpc) return; close(rpc->afd); auth_freerpc(rpc); } static void tlsPmd5(uchar *buf, int nbuf, uchar *key, int nkey, uchar *label, int nlabel, uchar *seed0, int nseed0, uchar *seed1, int nseed1) { uchar ai[MD5dlen], tmp[MD5dlen]; int i, n; MD5state *s; // generate a1 s = hmac_md5(label, nlabel, key, nkey, nil, nil); s = hmac_md5(seed0, nseed0, key, nkey, nil, s); hmac_md5(seed1, nseed1, key, nkey, ai, s); while(nbuf > 0) { s = hmac_md5(ai, MD5dlen, key, nkey, nil, nil); s = hmac_md5(label, nlabel, key, nkey, nil, s); s = hmac_md5(seed0, nseed0, key, nkey, nil, s); hmac_md5(seed1, nseed1, key, nkey, tmp, s); n = MD5dlen; if(n > nbuf) n = nbuf; for(i = 0; i < n; i++) buf[i] ^= tmp[i]; buf += n; nbuf -= n; hmac_md5(ai, MD5dlen, key, nkey, tmp, nil); memmove(ai, tmp, MD5dlen); } } static void tlsPsha1(uchar *buf, int nbuf, uchar *key, int nkey, uchar *label, int nlabel, uchar *seed0, int nseed0, uchar *seed1, int nseed1) { uchar ai[SHA1dlen], tmp[SHA1dlen]; int i, n; SHAstate *s; // generate a1 s = hmac_sha1(label, nlabel, key, nkey, nil, nil); s = hmac_sha1(seed0, nseed0, key, nkey, nil, s); hmac_sha1(seed1, nseed1, key, nkey, ai, s); while(nbuf > 0) { s = hmac_sha1(ai, SHA1dlen, key, nkey, nil, nil); s = hmac_sha1(label, nlabel, key, nkey, nil, s); s = hmac_sha1(seed0, nseed0, key, nkey, nil, s); hmac_sha1(seed1, nseed1, key, nkey, tmp, s); n = SHA1dlen; if(n > nbuf) n = nbuf; for(i = 0; i < n; i++) buf[i] ^= tmp[i]; buf += n; nbuf -= n; hmac_sha1(ai, SHA1dlen, key, nkey, tmp, nil); memmove(ai, tmp, SHA1dlen); } } // fill buf with md5(args)^sha1(args) static void tlsPRF(uchar *buf, int nbuf, uchar *key, int nkey, char *label, uchar *seed0, int nseed0, uchar *seed1, int nseed1) { int i; int nlabel = strlen(label); int n = (nkey + 1) >> 1; for(i = 0; i < nbuf; i++) buf[i] = 0; tlsPmd5(buf, nbuf, key, n, (uchar*)label, nlabel, seed0, nseed0, seed1, nseed1); tlsPsha1(buf, nbuf, key+nkey-n, n, (uchar*)label, nlabel, seed0, nseed0, seed1, nseed1); } /* * for setting server session id's */ static Lock sidLock; static long maxSid = 1; /* the keys are verified to have the same public components * and to function correctly with pkcs 1 encryption and decryption. */ static TlsSec* tlsSecInits(int cvers, uchar *csid, int ncsid, uchar *crandom, uchar *ssid, int *nssid, uchar *srandom) { TlsSec *sec = emalloc(sizeof(*sec)); USED(csid); USED(ncsid); // ignore csid for now memmove(sec->crandom, crandom, RandomSize); sec->clientVers = cvers; put32(sec->srandom, time(0)); genrandom(sec->srandom+4, RandomSize-4); memmove(srandom, sec->srandom, RandomSize); /* * make up a unique sid: use our pid, and and incrementing id * can signal no sid by setting nssid to 0. */ memset(ssid, 0, SidSize); put32(ssid, getpid()); lock(&sidLock); put32(ssid+4, maxSid++); unlock(&sidLock); *nssid = SidSize; return sec; } static int tlsSecSecrets(TlsSec *sec, int vers, uchar *epm, int nepm, uchar *kd, int nkd) { if(epm != nil){ if(setVers(sec, vers) < 0) goto Err; serverMasterSecret(sec, epm, nepm); }else if(sec->vers != vers){ werrstr("mismatched session versions"); goto Err; } setSecrets(sec, kd, nkd); return 0; Err: sec->ok = -1; return -1; } static TlsSec* tlsSecInitc(int cvers, uchar *crandom) { TlsSec *sec = emalloc(sizeof(*sec)); sec->clientVers = cvers; put32(sec->crandom, time(0)); genrandom(sec->crandom+4, RandomSize-4); memmove(crandom, sec->crandom, RandomSize); return sec; } static int tlsSecSecretc(TlsSec *sec, uchar *sid, int nsid, uchar *srandom, uchar *cert, int ncert, int vers, uchar **epm, int *nepm, uchar *kd, int nkd) { RSApub *pub; pub = nil; USED(sid); USED(nsid); memmove(sec->srandom, srandom, RandomSize); if(setVers(sec, vers) < 0) goto Err; pub = X509toRSApub(cert, ncert, nil, 0); if(pub == nil){ werrstr("invalid x509/rsa certificate"); goto Err; } if(clientMasterSecret(sec, pub, epm, nepm) < 0) goto Err; rsapubfree(pub); setSecrets(sec, kd, nkd); return 0; Err: if(pub != nil) rsapubfree(pub); sec->ok = -1; return -1; } static int tlsSecFinished(TlsSec *sec, MD5state md5, SHAstate sha1, uchar *fin, int nfin, int isclient) { if(sec->nfin != nfin){ sec->ok = -1; werrstr("invalid finished exchange"); return -1; } md5.malloced = 0; sha1.malloced = 0; (*sec->setFinished)(sec, md5, sha1, fin, isclient); return 1; } static void tlsSecOk(TlsSec *sec) { if(sec->ok == 0) sec->ok = 1; } static void tlsSecKill(TlsSec *sec) { if(!sec) return; factotum_rsa_close(sec->rpc); sec->ok = -1; } static void tlsSecClose(TlsSec *sec) { if(!sec) return; factotum_rsa_close(sec->rpc); free(sec->server); free(sec); } static int setVers(TlsSec *sec, int v) { if(v == SSL3Version){ sec->setFinished = sslSetFinished; sec->nfin = SSL3FinishedLen; sec->prf = sslPRF; }else if(v == TLSVersion){ sec->setFinished = tlsSetFinished; sec->nfin = TLSFinishedLen; sec->prf = tlsPRF; }else{ werrstr("invalid version"); return -1; } sec->vers = v; return 0; } /* * generate secret keys from the master secret. * * different crypto selections will require different amounts * of key expansion and use of key expansion data, * but it's all generated using the same function. */ static void setSecrets(TlsSec *sec, uchar *kd, int nkd) { (*sec->prf)(kd, nkd, sec->sec, MasterSecretSize, "key expansion", sec->srandom, RandomSize, sec->crandom, RandomSize); } /* * set the master secret from the pre-master secret. */ static void setMasterSecret(TlsSec *sec, Bytes *pm) { (*sec->prf)(sec->sec, MasterSecretSize, pm->data, MasterSecretSize, "master secret", sec->crandom, RandomSize, sec->srandom, RandomSize); } static void serverMasterSecret(TlsSec *sec, uchar *epm, int nepm) { Bytes *pm; pm = pkcs1_decrypt(sec, epm, nepm); // if the client messed up, just continue as if everything is ok, // to prevent attacks to check for correctly formatted messages. // Hence the fprint(2,) can't be replaced by tlsError(), which sends an Alert msg to the client. if(sec->ok < 0 || pm == nil || get16(pm->data) != sec->clientVers){ fprint(2, "serverMasterSecret failed ok=%d pm=%p pmvers=%x cvers=%x nepm=%d\n", sec->ok, pm, pm ? get16(pm->data) : -1, sec->clientVers, nepm); sec->ok = -1; if(pm != nil) freebytes(pm); pm = newbytes(MasterSecretSize); genrandom(pm->data, MasterSecretSize); } setMasterSecret(sec, pm); memset(pm->data, 0, pm->len); freebytes(pm); } static int clientMasterSecret(TlsSec *sec, RSApub *pub, uchar **epm, int *nepm) { Bytes *pm, *key; pm = newbytes(MasterSecretSize); put16(pm->data, sec->clientVers); genrandom(pm->data+2, MasterSecretSize - 2); setMasterSecret(sec, pm); key = pkcs1_encrypt(pm, pub, 2); memset(pm->data, 0, pm->len); freebytes(pm); if(key == nil){ werrstr("tls pkcs1_encrypt failed"); return -1; } *nepm = key->len; *epm = malloc(*nepm); if(*epm == nil){ freebytes(key); werrstr("out of memory"); return -1; } memmove(*epm, key->data, *nepm); freebytes(key); return 1; } static void sslSetFinished(TlsSec *sec, MD5state hsmd5, SHAstate hssha1, uchar *finished, int isClient) { DigestState *s; uchar h0[MD5dlen], h1[SHA1dlen], pad[48]; char *label; if(isClient) label = "CLNT"; else label = "SRVR"; md5((uchar*)label, 4, nil, &hsmd5); md5(sec->sec, MasterSecretSize, nil, &hsmd5); memset(pad, 0x36, 48); md5(pad, 48, nil, &hsmd5); md5(nil, 0, h0, &hsmd5); memset(pad, 0x5C, 48); s = md5(sec->sec, MasterSecretSize, nil, nil); s = md5(pad, 48, nil, s); md5(h0, MD5dlen, finished, s); sha1((uchar*)label, 4, nil, &hssha1); sha1(sec->sec, MasterSecretSize, nil, &hssha1); memset(pad, 0x36, 40); sha1(pad, 40, nil, &hssha1); sha1(nil, 0, h1, &hssha1); memset(pad, 0x5C, 40); s = sha1(sec->sec, MasterSecretSize, nil, nil); s = sha1(pad, 40, nil, s); sha1(h1, SHA1dlen, finished + MD5dlen, s); } // fill "finished" arg with md5(args)^sha1(args) static void tlsSetFinished(TlsSec *sec, MD5state hsmd5, SHAstate hssha1, uchar *finished, int isClient) { uchar h0[MD5dlen], h1[SHA1dlen]; char *label; // get current hash value, but allow further messages to be hashed in md5(nil, 0, h0, &hsmd5); sha1(nil, 0, h1, &hssha1); if(isClient) label = "client finished"; else label = "server finished"; tlsPRF(finished, TLSFinishedLen, sec->sec, MasterSecretSize, label, h0, MD5dlen, h1, SHA1dlen); } static void sslPRF(uchar *buf, int nbuf, uchar *key, int nkey, char *label, uchar *seed0, int nseed0, uchar *seed1, int nseed1) { DigestState *s; uchar sha1dig[SHA1dlen], md5dig[MD5dlen], tmp[26]; int i, n, len; USED(label); len = 1; while(nbuf > 0){ if(len > 26) return; for(i = 0; i < len; i++) tmp[i] = 'A' - 1 + len; s = sha1(tmp, len, nil, nil); s = sha1(key, nkey, nil, s); s = sha1(seed0, nseed0, nil, s); sha1(seed1, nseed1, sha1dig, s); s = md5(key, nkey, nil, nil); md5(sha1dig, SHA1dlen, md5dig, s); n = MD5dlen; if(n > nbuf) n = nbuf; memmove(buf, md5dig, n); buf += n; nbuf -= n; len++; } } static mpint* bytestomp(Bytes* bytes) { mpint* ans; ans = betomp(bytes->data, bytes->len, nil); return ans; } /* * Convert mpint* to Bytes, putting high order byte first. */ static Bytes* mptobytes(mpint* big) { int n, m; uchar *a; Bytes* ans; a = nil; n = (mpsignif(big)+7)/8; m = mptobe(big, nil, n, &a); ans = makebytes(a, m); if(a != nil) free(a); return ans; } // Do RSA computation on block according to key, and pad // result on left with zeros to make it modlen long. static Bytes* rsacomp(Bytes* block, RSApub* key, int modlen) { mpint *x, *y; Bytes *a, *ybytes; int ylen; x = bytestomp(block); y = rsaencrypt(key, x, nil); mpfree(x); ybytes = mptobytes(y); ylen = ybytes->len; if(ylen < modlen) { a = newbytes(modlen); memset(a->data, 0, modlen-ylen); memmove(a->data+modlen-ylen, ybytes->data, ylen); freebytes(ybytes); ybytes = a; } else if(ylen > modlen) { // assume it has leading zeros (mod should make it so) a = newbytes(modlen); memmove(a->data, ybytes->data, modlen); freebytes(ybytes); ybytes = a; } mpfree(y); return ybytes; } // encrypt data according to PKCS#1, /lib/rfc/rfc2437 9.1.2.1 static Bytes* pkcs1_encrypt(Bytes* data, RSApub* key, int blocktype) { Bytes *pad, *eb, *ans; int i, dlen, padlen, modlen; modlen = (mpsignif(key->n)+7)/8; dlen = data->len; if(modlen < 12 || dlen > modlen - 11) return nil; padlen = modlen - 3 - dlen; pad = newbytes(padlen); genrandom(pad->data, padlen); for(i = 0; i < padlen; i++) { if(blocktype == 0) pad->data[i] = 0; else if(blocktype == 1) pad->data[i] = 255; else if(pad->data[i] == 0) pad->data[i] = 1; } eb = newbytes(modlen); eb->data[0] = 0; eb->data[1] = blocktype; memmove(eb->data+2, pad->data, padlen); eb->data[padlen+2] = 0; memmove(eb->data+padlen+3, data->data, dlen); ans = rsacomp(eb, key, modlen); freebytes(eb); freebytes(pad); return ans; } // decrypt data according to PKCS#1, with given key. // expect a block type of 2. static Bytes* pkcs1_decrypt(TlsSec *sec, uchar *epm, int nepm) { Bytes *eb, *ans = nil; int i, modlen; mpint *x, *y; modlen = (mpsignif(sec->rsapub->n)+7)/8; if(nepm != modlen) return nil; x = betomp(epm, nepm, nil); y = factotum_rsa_decrypt(sec->rpc, x); if(y == nil) return nil; eb = mptobytes(y); if(eb->len < modlen){ // pad on left with zeros ans = newbytes(modlen); memset(ans->data, 0, modlen-eb->len); memmove(ans->data+modlen-eb->len, eb->data, eb->len); freebytes(eb); eb = ans; } if(eb->data[0] == 0 && eb->data[1] == 2) { for(i = 2; i < modlen; i++) if(eb->data[i] == 0) break; if(i < modlen - 1) ans = makebytes(eb->data+i+1, modlen-(i+1)); } freebytes(eb); return ans; } //================= general utility functions ======================== static void * emalloc(int n) { void *p; if(n==0) n=1; p = malloc(n); if(p == nil){ exits("out of memory"); } memset(p, 0, n); return p; } static void * erealloc(void *ReallocP, int ReallocN) { if(ReallocN == 0) ReallocN = 1; if(!ReallocP) ReallocP = emalloc(ReallocN); else if(!(ReallocP = realloc(ReallocP, ReallocN))){ exits("out of memory"); } return(ReallocP); } static void put32(uchar *p, u32int x) { p[0] = x>>24; p[1] = x>>16; p[2] = x>>8; p[3] = x; } static void put24(uchar *p, int x) { p[0] = x>>16; p[1] = x>>8; p[2] = x; } static void put16(uchar *p, int x) { p[0] = x>>8; p[1] = x; } static u32int get32(uchar *p) { return (p[0]<<24)|(p[1]<<16)|(p[2]<<8)|p[3]; } static int get24(uchar *p) { return (p[0]<<16)|(p[1]<<8)|p[2]; } static int get16(uchar *p) { return (p[0]<<8)|p[1]; } #define OFFSET(x, s) offsetof(s, x) /* * malloc and return a new Bytes structure capable of * holding len bytes. (len >= 0) * Used to use crypt_malloc, which aborts if malloc fails. */ static Bytes* newbytes(int len) { Bytes* ans; ans = (Bytes*)malloc(OFFSET(data[0], Bytes) + len); ans->len = len; return ans; } /* * newbytes(len), with data initialized from buf */ static Bytes* makebytes(uchar* buf, int len) { Bytes* ans; ans = newbytes(len); memmove(ans->data, buf, len); return ans; } static void freebytes(Bytes* b) { if(b != nil) free(b); } /* len is number of ints */ static Ints* newints(int len) { Ints* ans; ans = (Ints*)malloc(OFFSET(data[0], Ints) + len*sizeof(int)); ans->len = len; return ans; } static Ints* makeints(int* buf, int len) { Ints* ans; ans = newints(len); if(len > 0) memmove(ans->data, buf, len*sizeof(int)); return ans; } static void freeints(Ints* b) { if(b != nil) free(b); } /sys/src/ape/lib/sec/port/mkfile 664 sys sys 1369203505 1458 APE=/sys/src/ape <$APE/config LIB=/$objtype/lib/ape/libsec.a LIBSECCFILES =\ des.c desmodes.c desECB.c desCBC.c des3ECB.c des3CBC.c\ aes.c blowfish.c \ hmac.c md5.c md5block.c md4.c sha1.c sha1block.c\ sha2_64.c sha2_128.c sha2block64.c sha2block128.c\ sha1block.c md5block.c\ sha1pickle.c md5pickle.c\ rc4.c\ genrandom.c prng.c fastrand.c nfastrand.c\ probably_prime.c smallprimetest.c genprime.c dsaprimes.c\ gensafeprime.c genstrongprime.c\ rsagen.c rsafill.c rsaencrypt.c rsadecrypt.c rsaalloc.c \ rsaprivtopub.c decodepem.c \ eggen.c egencrypt.c egdecrypt.c egalloc.c egprivtopub.c \ egsign.c egverify.c \ dsagen.c dsaalloc.c dsaprivtopub.c dsasign.c dsaverify.c \ tlshand.c readcert.c \ CFILES=\ $LIBSECCFILES\ x509-ape.c\ ALLOFILES=${CFILES:%.c=%.$O} # cull things in the per-machine directories from this list OFILES= `{rc ./reduce $O $objtype $ALLOFILES} HFILES=\ /sys/include/ape/libsec.h\ /sys/include/ape/mp.h\ ../../9/libc.h\ UPDATE=\ mkfile\ $HFILES\ $CFILES\ CLEANFILES=\ x509-ape.c /tm0->tm_/g' | \ sed 's/static //g' > x509-ape.c $O.rsatest: rsatest.$O $LD -o $target $prereq /sys/src/ape/lib/sec/port/reduce 775 sys sys 1369861110 427 #!/bin/rc O=$1 shift objtype=$1 shift cwd=`{basename -d `{pwd}} cwd=$cwd/$objtype bind -ac /sys/src/libsec/$objtype $cwd ls -p ../$objtype/*.[cs] >[2]/dev/null | sed 's/..$//' > /tmp/reduce.$pid # # if empty directory, just return the input files # if (! ~ $status '|') { echo $* rm /tmp/reduce.$pid unmount $cwd exit 0 } echo $* | tr ' ' \012 | grep -v -f /tmp/reduce.$pid | tr \012 ' ' rm /tmp/reduce.$pid unmount $cwd /sys/src/ape/lib/sec/power 20000000775 bootes sys 1368053403 0 /sys/src/ape/lib/sec/power/mkfile 664 sys sys 1369847253 228 APE=/sys/src/ape <$APE/config LIB=/$objtype/lib/ape/libsec.a OFILES= \ HFILES=/sys/include/ape/libsec.h UPDATE=mkfile #include int getfields(char *str, char **args, int max, int mflag, char *set) { Rune r; int nr, intok, narg; if(max <= 0) return 0; narg = 0; args[narg] = str; if(!mflag) narg++; intok = 0; for(;; str += nr) { nr = chartorune(&r, str); if(r == 0) break; if(utfrune(set, r)) { if(narg >= max) break; *str = 0; intok = 0; args[narg] = str + nr; if(!mflag) narg++; } else { if(!intok && mflag) narg++; intok = 1; } } return narg; } /sys/src/ape/lib/utf/gettokens.c 664 bootes sys 1363045420 802 #include #include static char* etoken(char *t, char *sep) { int quoting; /* move to end of next token */ quoting = 0; while(*t!='\0' && (quoting || utfrune(sep, *t)==nil)){ if(*t != '\''){ t++; continue; } /* *t is a quote */ if(!quoting){ quoting = 1; t++; continue; } /* quoting and we're on a quote */ if(t[1] != '\''){ /* end of quoted section; absorb closing quote */ t++; quoting = 0; continue; } /* doubled quote; fold one quote into two */ t += 2; } return t; } int gettokens(char *s, char **args, int maxargs, char *sep) { int nargs; for(nargs=0; nargs #include "utf.h" #define nil ((void*)0) #define uchar _fmtuchar #define ushort _fmtushort #define uint _fmtuint #define ulong _fmtulong #define vlong _fmtvlong #define uvlong _fmtuvlong typedef unsigned char uchar; typedef unsigned short ushort; typedef unsigned int uint; typedef unsigned long ulong; /sys/src/ape/lib/utf/mkfile 664 sys sys 1369158369 575 APE=/sys/src/ape <$APE/config LIB=/$objtype/lib/ape/libutf.a OFILES=\ getfields.$O\ gettokens.$O\ rerrstr.$O\ rune.$O\ runestrcat.$O\ runestrchr.$O\ runestrcmp.$O\ runestrcpy.$O\ runestrdup.$O\ runestrlen.$O\ runestrecpy.$O\ runestrncat.$O\ runestrncmp.$O\ runestrncpy.$O\ runestrrchr.$O\ runestrstr.$O\ runetype.$O\ utfecpy.$O\ utflen.$O\ utfnlen.$O\ utfrrune.$O\ utfrune.$O\ utfutf.$O\ HFILES=\ /sys/include/ape/utf.h\ UPDATE=\ mkfile\ ${OFILES:%.$O=%.c}\ #include #include "utf.h" #include "utfdef.h" enum { Bit1 = 7, Bitx = 6, Bit2 = 5, Bit3 = 4, Bit4 = 3, Bit5 = 2, T1 = ((1<<(Bit1+1))-1) ^ 0xFF, /* 0000 0000 */ Tx = ((1<<(Bitx+1))-1) ^ 0xFF, /* 1000 0000 */ T2 = ((1<<(Bit2+1))-1) ^ 0xFF, /* 1100 0000 */ T3 = ((1<<(Bit3+1))-1) ^ 0xFF, /* 1110 0000 */ T4 = ((1<<(Bit4+1))-1) ^ 0xFF, /* 1111 0000 */ T5 = ((1<<(Bit5+1))-1) ^ 0xFF, /* 1111 1000 */ Rune1 = (1<<(Bit1+0*Bitx))-1, /* 0000 0000 0000 0000 0111 1111 */ Rune2 = (1<<(Bit2+1*Bitx))-1, /* 0000 0000 0000 0111 1111 1111 */ Rune3 = (1<<(Bit3+2*Bitx))-1, /* 0000 0000 1111 1111 1111 1111 */ Rune4 = (1<<(Bit4+3*Bitx))-1, /* 0011 1111 1111 1111 1111 1111 */ Maskx = (1< T1 */ c = *(uchar*)str; if(c < Tx) { *rune = c; return 1; } /* * two character sequence * 0080-07FF => T2 Tx */ c1 = *(uchar*)(str+1) ^ Tx; if(c1 & Testx) goto bad; if(c < T3) { if(c < T2) goto bad; l = ((c << Bitx) | c1) & Rune2; if(l <= Rune1) goto bad; *rune = l; return 2; } /* * three character sequence * 0800-FFFF => T3 Tx Tx */ c2 = *(uchar*)(str+2) ^ Tx; if(c2 & Testx) goto bad; if(c < T4) { l = ((((c << Bitx) | c1) << Bitx) | c2) & Rune3; if(l <= Rune2) goto bad; *rune = l; return 3; } /* * four character sequence * 10000-10FFFF => T4 Tx Tx Tx */ if(UTFmax >= 4) { c3 = *(uchar*)(str+3) ^ Tx; if(c3 & Testx) goto bad; if(c < T5) { l = ((((((c << Bitx) | c1) << Bitx) | c2) << Bitx) | c3) & Rune4; if(l <= Rune3) goto bad; if(l > Runemax) goto bad; *rune = l; return 4; } } /* * bad decoding */ bad: *rune = Bad; return 1; } int runetochar(char *str, Rune *rune) { long c; /* * one character sequence * 00000-0007F => 00-7F */ c = *rune; if(c <= Rune1) { str[0] = c; return 1; } /* * two character sequence * 00080-007FF => T2 Tx */ if(c <= Rune2) { str[0] = T2 | (c >> 1*Bitx); str[1] = Tx | (c & Maskx); return 2; } /* * three character sequence * 00800-0FFFF => T3 Tx Tx */ if(c > Runemax) c = Runeerror; if(c <= Rune3) { str[0] = T3 | (c >> 2*Bitx); str[1] = Tx | ((c >> 1*Bitx) & Maskx); str[2] = Tx | (c & Maskx); return 3; } /* * four character sequence * 010000-1FFFFF => T4 Tx Tx Tx */ str[0] = T4 | (c >> 3*Bitx); str[1] = Tx | ((c >> 2*Bitx) & Maskx); str[2] = Tx | ((c >> 1*Bitx) & Maskx); str[3] = Tx | (c & Maskx); return 4; } int runelen(long c) { Rune rune; char str[10]; rune = c; return runetochar(str, &rune); } int runenlen(Rune *r, int nrune) { int nb, c; nb = 0; while(nrune--) { c = *r++; if(c <= Rune1) nb++; else if(c <= Rune2) nb += 2; else if(c <= Rune3 || c > Runemax) nb += 3; else nb += 4; } return nb; } int fullrune(char *str, int n) { int c; if(n <= 0) return 0; c = *(uchar*)str; if(c < Tx) return 1; if(c < T3) return n >= 2; if(UTFmax == 3 || c < T4) return n >= 3; return n >= 4; } /sys/src/ape/lib/utf/runestrcat.c 664 sys sys 1368489963 919 /* * The authors of this software are Rob Pike and Ken Thompson. * Copyright (c) 2002 by Lucent Technologies. * Permission to use, copy, modify, and distribute this software for any * purpose without fee is hereby granted, provided that this entire notice * is included in all copies of any software which is or includes a copy * or modification of this software and in all copies of the supporting * documentation for such software. * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. */ #include #include #include "utf.h" #include "utfdef.h" Rune* runestrcat(Rune *s1, Rune *s2) { runestrcpy(runestrchr(s1, 0), s2); return s1; } /sys/src/ape/lib/utf/runestrchr.c 664 sys sys 1368489959 1002 /* * The authors of this software are Rob Pike and Ken Thompson. * Copyright (c) 2002 by Lucent Technologies. * Permission to use, copy, modify, and distribute this software for any * purpose without fee is hereby granted, provided that this entire notice * is included in all copies of any software which is or includes a copy * or modification of this software and in all copies of the supporting * documentation for such software. * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. */ #include #include #include "utf.h" #include "utfdef.h" Rune* runestrchr(Rune *s, Rune c) { Rune c0 = c; Rune c1; if(c == 0) { while(*s++) ; return s-1; } while(c1 = *s++) if(c1 == c0) return s-1; return 0; } /sys/src/ape/lib/utf/runestrcmp.c 664 sys sys 1368489953 1016 /* * The authors of this software are Rob Pike and Ken Thompson. * Copyright (c) 2002 by Lucent Technologies. * Permission to use, copy, modify, and distribute this software for any * purpose without fee is hereby granted, provided that this entire notice * is included in all copies of any software which is or includes a copy * or modification of this software and in all copies of the supporting * documentation for such software. * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. */ #include #include #include "utf.h" #include "utfdef.h" int runestrcmp(Rune *s1, Rune *s2) { Rune c1, c2; for(;;) { c1 = *s1++; c2 = *s2++; if(c1 != c2) { if(c1 > c2) return 1; return -1; } if(c1 == 0) return 0; } } /sys/src/ape/lib/utf/runestrcpy.c 664 sys sys 1368489945 933 /* * The authors of this software are Rob Pike and Ken Thompson. * Copyright (c) 2002 by Lucent Technologies. * Permission to use, copy, modify, and distribute this software for any * purpose without fee is hereby granted, provided that this entire notice * is included in all copies of any software which is or includes a copy * or modification of this software and in all copies of the supporting * documentation for such software. * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. */ #include #include #include "utf.h" #include "utfdef.h" Rune* runestrcpy(Rune *s1, Rune *s2) { Rune *os1; os1 = s1; while(*s1++ = *s2++) ; return os1; } /sys/src/ape/lib/utf/runestrdup.c 664 sys sys 1368489931 995 /* * The authors of this software are Rob Pike and Ken Thompson. * Copyright (c) 2002 by Lucent Technologies. * Permission to use, copy, modify, and distribute this software for any * purpose without fee is hereby granted, provided that this entire notice * is included in all copies of any software which is or includes a copy * or modification of this software and in all copies of the supporting * documentation for such software. * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. */ #include #include #include #include "utf.h" #include "utfdef.h" Rune* runestrdup(Rune *s) { Rune *ns; ns = malloc(sizeof(Rune)*(runestrlen(s) + 1)); if(ns == 0) return 0; return runestrcpy(ns, s); } /sys/src/ape/lib/utf/runestrecpy.c 664 sys sys 1368489920 997 /* * The authors of this software are Rob Pike and Ken Thompson. * Copyright (c) 2002 by Lucent Technologies. * Permission to use, copy, modify, and distribute this software for any * purpose without fee is hereby granted, provided that this entire notice * is included in all copies of any software which is or includes a copy * or modification of this software and in all copies of the supporting * documentation for such software. * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. */ #include #include #include "utf.h" #include "utfdef.h" Rune* runestrecpy(Rune *s1, Rune *es1, Rune *s2) { if(s1 >= es1) return s1; while(*s1++ = *s2++){ if(s1 == es1){ *--s1 = '\0'; break; } } return s1; } /sys/src/ape/lib/utf/runestrlen.c 664 sys sys 1368489911 889 /* * The authors of this software are Rob Pike and Ken Thompson. * Copyright (c) 2002 by Lucent Technologies. * Permission to use, copy, modify, and distribute this software for any * purpose without fee is hereby granted, provided that this entire notice * is included in all copies of any software which is or includes a copy * or modification of this software and in all copies of the supporting * documentation for such software. * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. */ #include #include #include "utf.h" #include "utfdef.h" long runestrlen(Rune *s) { return runestrchr(s, 0) - s; } /sys/src/ape/lib/utf/runestrncat.c 664 sys sys 1368489906 1008 /* * The authors of this software are Rob Pike and Ken Thompson. * Copyright (c) 2002 by Lucent Technologies. * Permission to use, copy, modify, and distribute this software for any * purpose without fee is hereby granted, provided that this entire notice * is included in all copies of any software which is or includes a copy * or modification of this software and in all copies of the supporting * documentation for such software. * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. */ #include #include #include "utf.h" #include "utfdef.h" Rune* runestrncat(Rune *s1, Rune *s2, long n) { Rune *os1; os1 = s1; s1 = runestrchr(s1, 0); while(*s1++ = *s2++) if(--n < 0) { s1[-1] = 0; break; } return os1; } /sys/src/ape/lib/utf/runestrncmp.c 664 sys sys 1368489900 1045 /* * The authors of this software are Rob Pike and Ken Thompson. * Copyright (c) 2002 by Lucent Technologies. * Permission to use, copy, modify, and distribute this software for any * purpose without fee is hereby granted, provided that this entire notice * is included in all copies of any software which is or includes a copy * or modification of this software and in all copies of the supporting * documentation for such software. * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. */ #include #include #include "utf.h" #include "utfdef.h" int runestrncmp(Rune *s1, Rune *s2, long n) { Rune c1, c2; while(n > 0) { c1 = *s1++; c2 = *s2++; n--; if(c1 != c2) { if(c1 > c2) return 1; return -1; } if(c1 == 0) break; } return 0; } /sys/src/ape/lib/utf/runestrncpy.c 664 sys sys 1368489896 1029 /* * The authors of this software are Rob Pike and Ken Thompson. * Copyright (c) 2002 by Lucent Technologies. * Permission to use, copy, modify, and distribute this software for any * purpose without fee is hereby granted, provided that this entire notice * is included in all copies of any software which is or includes a copy * or modification of this software and in all copies of the supporting * documentation for such software. * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. */ #include #include #include "utf.h" #include "utfdef.h" Rune* runestrncpy(Rune *s1, Rune *s2, long n) { int i; Rune *os1; os1 = s1; for(i = 0; i < n; i++) if((*s1++ = *s2++) == 0) { while(++i < n) *s1++ = 0; return os1; } return os1; } /sys/src/ape/lib/utf/runestrrchr.c 664 sys sys 1368489890 977 /* * The authors of this software are Rob Pike and Ken Thompson. * Copyright (c) 2002 by Lucent Technologies. * Permission to use, copy, modify, and distribute this software for any * purpose without fee is hereby granted, provided that this entire notice * is included in all copies of any software which is or includes a copy * or modification of this software and in all copies of the supporting * documentation for such software. * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. */ #include #include #include "utf.h" #include "utfdef.h" Rune* runestrrchr(Rune *s, Rune c) { Rune *r; if(c == 0) return runestrchr(s, 0); r = 0; while(s = runestrchr(s, c)) r = s++; return r; } /sys/src/ape/lib/utf/runestrstr.c 664 sys sys 1368489885 1192 /* * The authors of this software are Rob Pike and Ken Thompson. * Copyright (c) 2002 by Lucent Technologies. * Permission to use, copy, modify, and distribute this software for any * purpose without fee is hereby granted, provided that this entire notice * is included in all copies of any software which is or includes a copy * or modification of this software and in all copies of the supporting * documentation for such software. * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. */ #include #include #include "utf.h" #include "utfdef.h" /* * Return pointer to first occurrence of s2 in s1, * 0 if none */ Rune* runestrstr(Rune *s1, Rune *s2) { Rune *p, *pa, *pb; int c0, c; c0 = *s2; if(c0 == 0) return s1; s2++; for(p=runestrchr(s1, c0); p; p=runestrchr(p+1, c0)) { pa = p; for(pb=s2;; pb++) { c = *pb; if(c == 0) return p; if(c != *++pa) break; } } return 0; } /sys/src/ape/lib/utf/runetype.c 664 sys sys 1368489878 30457 /* * The authors of this software are Rob Pike and Ken Thompson. * Copyright (c) 2002 by Lucent Technologies. * Permission to use, copy, modify, and distribute this software for any * purpose without fee is hereby granted, provided that this entire notice * is included in all copies of any software which is or includes a copy * or modification of this software and in all copies of the supporting * documentation for such software. * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. */ #include #include #include "utf.h" #include "utfdef.h" /* * alpha ranges - * only covers ranges not in lower||upper */ static Rune __alpha2[] = { 0x00d8, 0x00f6, /* Ø - ö */ 0x00f8, 0x01f5, /* ø - ǵ */ 0x0250, 0x02a8, /* ɐ - ʨ */ 0x038e, 0x03a1, /* Ύ - Ρ */ 0x03a3, 0x03ce, /* Σ - ώ */ 0x03d0, 0x03d6, /* ϐ - ϖ */ 0x03e2, 0x03f3, /* Ϣ - ϳ */ 0x0490, 0x04c4, /* Ґ - ӄ */ 0x0561, 0x0587, /* ա - և */ 0x05d0, 0x05ea, /* א - ת */ 0x05f0, 0x05f2, /* װ - ײ */ 0x0621, 0x063a, /* ء - غ */ 0x0640, 0x064a, /* ـ - ي */ 0x0671, 0x06b7, /* ٱ - ڷ */ 0x06ba, 0x06be, /* ں - ھ */ 0x06c0, 0x06ce, /* ۀ - ێ */ 0x06d0, 0x06d3, /* ې - ۓ */ 0x0905, 0x0939, /* अ - ह */ 0x0958, 0x0961, /* क़ - ॡ */ 0x0985, 0x098c, /* অ - ঌ */ 0x098f, 0x0990, /* এ - ঐ */ 0x0993, 0x09a8, /* ও - ন */ 0x09aa, 0x09b0, /* প - র */ 0x09b6, 0x09b9, /* শ - হ */ 0x09dc, 0x09dd, /* ড় - ঢ় */ 0x09df, 0x09e1, /* য় - ৡ */ 0x09f0, 0x09f1, /* ৰ - ৱ */ 0x0a05, 0x0a0a, /* ਅ - ਊ */ 0x0a0f, 0x0a10, /* ਏ - ਐ */ 0x0a13, 0x0a28, /* ਓ - ਨ */ 0x0a2a, 0x0a30, /* ਪ - ਰ */ 0x0a32, 0x0a33, /* ਲ - ਲ਼ */ 0x0a35, 0x0a36, /* ਵ - ਸ਼ */ 0x0a38, 0x0a39, /* ਸ - ਹ */ 0x0a59, 0x0a5c, /* ਖ਼ - ੜ */ 0x0a85, 0x0a8b, /* અ - ઋ */ 0x0a8f, 0x0a91, /* એ - ઑ */ 0x0a93, 0x0aa8, /* ઓ - ન */ 0x0aaa, 0x0ab0, /* પ - ર */ 0x0ab2, 0x0ab3, /* લ - ળ */ 0x0ab5, 0x0ab9, /* વ - હ */ 0x0b05, 0x0b0c, /* ଅ - ଌ */ 0x0b0f, 0x0b10, /* ଏ - ଐ */ 0x0b13, 0x0b28, /* ଓ - ନ */ 0x0b2a, 0x0b30, /* ପ - ର */ 0x0b32, 0x0b33, /* ଲ - ଳ */ 0x0b36, 0x0b39, /* ଶ - ହ */ 0x0b5c, 0x0b5d, /* ଡ଼ - ଢ଼ */ 0x0b5f, 0x0b61, /* ୟ - ୡ */ 0x0b85, 0x0b8a, /* அ - ஊ */ 0x0b8e, 0x0b90, /* எ - ஐ */ 0x0b92, 0x0b95, /* ஒ - க */ 0x0b99, 0x0b9a, /* ங - ச */ 0x0b9e, 0x0b9f, /* ஞ - ட */ 0x0ba3, 0x0ba4, /* ண - த */ 0x0ba8, 0x0baa, /* ந - ப */ 0x0bae, 0x0bb5, /* ம - வ */ 0x0bb7, 0x0bb9, /* ஷ - ஹ */ 0x0c05, 0x0c0c, /* అ - ఌ */ 0x0c0e, 0x0c10, /* ఎ - ఐ */ 0x0c12, 0x0c28, /* ఒ - న */ 0x0c2a, 0x0c33, /* ప - ళ */ 0x0c35, 0x0c39, /* వ - హ */ 0x0c60, 0x0c61, /* ౠ - ౡ */ 0x0c85, 0x0c8c, /* ಅ - ಌ */ 0x0c8e, 0x0c90, /* ಎ - ಐ */ 0x0c92, 0x0ca8, /* ಒ - ನ */ 0x0caa, 0x0cb3, /* ಪ - ಳ */ 0x0cb5, 0x0cb9, /* ವ - ಹ */ 0x0ce0, 0x0ce1, /* ೠ - ೡ */ 0x0d05, 0x0d0c, /* അ - ഌ */ 0x0d0e, 0x0d10, /* എ - ഐ */ 0x0d12, 0x0d28, /* ഒ - ന */ 0x0d2a, 0x0d39, /* പ - ഹ */ 0x0d60, 0x0d61, /* ൠ - ൡ */ 0x0e01, 0x0e30, /* ก - ะ */ 0x0e32, 0x0e33, /* า - ำ */ 0x0e40, 0x0e46, /* เ - ๆ */ 0x0e5a, 0x0e5b, /* ๚ - ๛ */ 0x0e81, 0x0e82, /* ກ - ຂ */ 0x0e87, 0x0e88, /* ງ - ຈ */ 0x0e94, 0x0e97, /* ດ - ທ */ 0x0e99, 0x0e9f, /* ນ - ຟ */ 0x0ea1, 0x0ea3, /* ມ - ຣ */ 0x0eaa, 0x0eab, /* ສ - ຫ */ 0x0ead, 0x0eae, /* ອ - ຮ */ 0x0eb2, 0x0eb3, /* າ - ຳ */ 0x0ec0, 0x0ec4, /* ເ - ໄ */ 0x0edc, 0x0edd, /* ໜ - ໝ */ 0x0f18, 0x0f19, /* ༘ - ༙ */ 0x0f40, 0x0f47, /* ཀ - ཇ */ 0x0f49, 0x0f69, /* ཉ - ཀྵ */ 0x10d0, 0x10f6, /* ა - ჶ */ 0x1100, 0x1159, /* ᄀ - ᅙ */ 0x115f, 0x11a2, /* ᅟ - ᆢ */ 0x11a8, 0x11f9, /* ᆨ - ᇹ */ 0x1e00, 0x1e9b, /* Ḁ - ẛ */ 0x1f50, 0x1f57, /* ὐ - ὗ */ 0x1f80, 0x1fb4, /* ᾀ - ᾴ */ 0x1fb6, 0x1fbc, /* ᾶ - ᾼ */ 0x1fc2, 0x1fc4, /* ῂ - ῄ */ 0x1fc6, 0x1fcc, /* ῆ - ῌ */ 0x1fd0, 0x1fd3, /* ῐ - ΐ */ 0x1fd6, 0x1fdb, /* ῖ - Ί */ 0x1fe0, 0x1fec, /* ῠ - Ῥ */ 0x1ff2, 0x1ff4, /* ῲ - ῴ */ 0x1ff6, 0x1ffc, /* ῶ - ῼ */ 0x210a, 0x2113, /* ℊ - ℓ */ 0x2115, 0x211d, /* ℕ - ℝ */ 0x2120, 0x2122, /* ℠ - ™ */ 0x212a, 0x2131, /* K - ℱ */ 0x2133, 0x2138, /* ℳ - ℸ */ 0x3041, 0x3094, /* ぁ - ゔ */ 0x30a1, 0x30fa, /* ァ - ヺ */ 0x3105, 0x312c, /* ㄅ - ㄬ */ 0x3131, 0x318e, /* ㄱ - ㆎ */ 0x3192, 0x319f, /* ㆒ - ㆟ */ 0x3260, 0x327b, /* ㉠ - ㉻ */ 0x328a, 0x32b0, /* ㊊ - ㊰ */ 0x32d0, 0x32fe, /* ㋐ - ㋾ */ 0x3300, 0x3357, /* ㌀ - ㍗ */ 0x3371, 0x3376, /* ㍱ - ㍶ */ 0x337b, 0x3394, /* ㍻ - ㎔ */ 0x3399, 0x339e, /* ㎙ - ㎞ */ 0x33a9, 0x33ad, /* ㎩ - ㎭ */ 0x33b0, 0x33c1, /* ㎰ - ㏁ */ 0x33c3, 0x33c5, /* ㏃ - ㏅ */ 0x33c7, 0x33d7, /* ㏇ - ㏗ */ 0x33d9, 0x33dd, /* ㏙ - ㏝ */ 0x4e00, 0x9fff, /* 一 - 鿿 */ 0xac00, 0xd7a3, /* 가 - 힣 */ 0xf900, 0xfb06, /* 豈 - st */ 0xfb13, 0xfb17, /* ﬓ - ﬗ */ 0xfb1f, 0xfb28, /* ײַ - ﬨ */ 0xfb2a, 0xfb36, /* שׁ - זּ */ 0xfb38, 0xfb3c, /* טּ - לּ */ 0xfb40, 0xfb41, /* נּ - סּ */ 0xfb43, 0xfb44, /* ףּ - פּ */ 0xfb46, 0xfbb1, /* צּ - ﮱ */ 0xfbd3, 0xfd3d, /* ﯓ - ﴽ */ 0xfd50, 0xfd8f, /* ﵐ - ﶏ */ 0xfd92, 0xfdc7, /* ﶒ - ﷇ */ 0xfdf0, 0xfdf9, /* ﷰ - ﷹ */ 0xfe70, 0xfe72, /* ﹰ - ﹲ */ 0xfe76, 0xfefc, /* ﹶ - ﻼ */ 0xff66, 0xff6f, /* ヲ - ッ */ 0xff71, 0xff9d, /* ア - ン */ 0xffa0, 0xffbe, /* ᅠ - ᄒ */ 0xffc2, 0xffc7, /* ᅡ - ᅦ */ 0xffca, 0xffcf, /* ᅧ - ᅬ */ 0xffd2, 0xffd7, /* ᅭ - ᅲ */ 0xffda, 0xffdc, /* ᅳ - ᅵ */ }; /* * alpha singlets - * only covers ranges not in lower||upper */ static Rune __alpha1[] = { 0x00aa, /* ª */ 0x00b5, /* µ */ 0x00ba, /* º */ 0x03da, /* Ϛ */ 0x03dc, /* Ϝ */ 0x03de, /* Ϟ */ 0x03e0, /* Ϡ */ 0x06d5, /* ە */ 0x09b2, /* ল */ 0x0a5e, /* ਫ਼ */ 0x0a8d, /* ઍ */ 0x0ae0, /* ૠ */ 0x0b9c, /* ஜ */ 0x0cde, /* ೞ */ 0x0e4f, /* ๏ */ 0x0e84, /* ຄ */ 0x0e8a, /* ຊ */ 0x0e8d, /* ຍ */ 0x0ea5, /* ລ */ 0x0ea7, /* ວ */ 0x0eb0, /* ະ */ 0x0ebd, /* ຽ */ 0x1fbe, /* ι */ 0x207f, /* ⁿ */ 0x20a8, /* ₨ */ 0x2102, /* ℂ */ 0x2107, /* ℇ */ 0x2124, /* ℤ */ 0x2126, /* Ω */ 0x2128, /* ℨ */ 0xfb3e, /* מּ */ 0xfe74, /* ﹴ */ }; /* * space ranges */ static Rune __space2[] = { 0x0009, 0x000a, /* tab and newline */ 0x0020, 0x0020, /* space */ 0x00a0, 0x00a0, /*   */ 0x2000, 0x200b, /*   - ​ */ 0x2028, 0x2029, /* 
 - 
 */ 0x3000, 0x3000, /*   */ 0xfeff, 0xfeff, /*  */ }; /* * lower case ranges * 3rd col is conversion excess 500 */ static Rune __toupper2[] = { 0x0061, 0x007a, 468, /* a-z A-Z */ 0x00e0, 0x00f6, 468, /* à-ö À-Ö */ 0x00f8, 0x00fe, 468, /* ø-þ Ø-Þ */ 0x0256, 0x0257, 295, /* ɖ-ɗ Ɖ-Ɗ */ 0x0258, 0x0259, 298, /* ɘ-ə Ǝ-Ə */ 0x028a, 0x028b, 283, /* ʊ-ʋ Ʊ-Ʋ */ 0x03ad, 0x03af, 463, /* έ-ί Έ-Ί */ 0x03b1, 0x03c1, 468, /* α-ρ Α-Ρ */ 0x03c3, 0x03cb, 468, /* σ-ϋ Σ-Ϋ */ 0x03cd, 0x03ce, 437, /* ύ-ώ Ύ-Ώ */ 0x0430, 0x044f, 468, /* а-я А-Я */ 0x0451, 0x045c, 420, /* ё-ќ Ё-Ќ */ 0x045e, 0x045f, 420, /* ў-џ Ў-Џ */ 0x0561, 0x0586, 452, /* ա-ֆ Ա-Ֆ */ 0x1f00, 0x1f07, 508, /* ἀ-ἇ Ἀ-Ἇ */ 0x1f10, 0x1f15, 508, /* ἐ-ἕ Ἐ-Ἕ */ 0x1f20, 0x1f27, 508, /* ἠ-ἧ Ἠ-Ἧ */ 0x1f30, 0x1f37, 508, /* ἰ-ἷ Ἰ-Ἷ */ 0x1f40, 0x1f45, 508, /* ὀ-ὅ Ὀ-Ὅ */ 0x1f60, 0x1f67, 508, /* ὠ-ὧ Ὠ-Ὧ */ 0x1f70, 0x1f71, 574, /* ὰ-ά Ὰ-Ά */ 0x1f72, 0x1f75, 586, /* ὲ-ή Ὲ-Ή */ 0x1f76, 0x1f77, 600, /* ὶ-ί Ὶ-Ί */ 0x1f78, 0x1f79, 628, /* ὸ-ό Ὸ-Ό */ 0x1f7a, 0x1f7b, 612, /* ὺ-ύ Ὺ-Ύ */ 0x1f7c, 0x1f7d, 626, /* ὼ-ώ Ὼ-Ώ */ 0x1f80, 0x1f87, 508, /* ᾀ-ᾇ ᾈ-ᾏ */ 0x1f90, 0x1f97, 508, /* ᾐ-ᾗ ᾘ-ᾟ */ 0x1fa0, 0x1fa7, 508, /* ᾠ-ᾧ ᾨ-ᾯ */ 0x1fb0, 0x1fb1, 508, /* ᾰ-ᾱ Ᾰ-Ᾱ */ 0x1fd0, 0x1fd1, 508, /* ῐ-ῑ Ῐ-Ῑ */ 0x1fe0, 0x1fe1, 508, /* ῠ-ῡ Ῠ-Ῡ */ 0x2170, 0x217f, 484, /* ⅰ-ⅿ Ⅰ-Ⅿ */ 0x24d0, 0x24e9, 474, /* ⓐ-ⓩ Ⓐ-Ⓩ */ 0xff41, 0xff5a, 468, /* a-z A-Z */ }; /* * lower case singlets * 2nd col is conversion excess 500 */ static Rune __toupper1[] = { 0x00ff, 621, /* ÿ Ÿ */ 0x0101, 499, /* ā Ā */ 0x0103, 499, /* ă Ă */ 0x0105, 499, /* ą Ą */ 0x0107, 499, /* ć Ć */ 0x0109, 499, /* ĉ Ĉ */ 0x010b, 499, /* ċ Ċ */ 0x010d, 499, /* č Č */ 0x010f, 499, /* ď Ď */ 0x0111, 499, /* đ Đ */ 0x0113, 499, /* ē Ē */ 0x0115, 499, /* ĕ Ĕ */ 0x0117, 499, /* ė Ė */ 0x0119, 499, /* ę Ę */ 0x011b, 499, /* ě Ě */ 0x011d, 499, /* ĝ Ĝ */ 0x011f, 499, /* ğ Ğ */ 0x0121, 499, /* ġ Ġ */ 0x0123, 499, /* ģ Ģ */ 0x0125, 499, /* ĥ Ĥ */ 0x0127, 499, /* ħ Ħ */ 0x0129, 499, /* ĩ Ĩ */ 0x012b, 499, /* ī Ī */ 0x012d, 499, /* ĭ Ĭ */ 0x012f, 499, /* į Į */ 0x0131, 268, /* ı I */ 0x0133, 499, /* ij IJ */ 0x0135, 499, /* ĵ Ĵ */ 0x0137, 499, /* ķ Ķ */ 0x013a, 499, /* ĺ Ĺ */ 0x013c, 499, /* ļ Ļ */ 0x013e, 499, /* ľ Ľ */ 0x0140, 499, /* ŀ Ŀ */ 0x0142, 499, /* ł Ł */ 0x0144, 499, /* ń Ń */ 0x0146, 499, /* ņ Ņ */ 0x0148, 499, /* ň Ň */ 0x014b, 499, /* ŋ Ŋ */ 0x014d, 499, /* ō Ō */ 0x014f, 499, /* ŏ Ŏ */ 0x0151, 499, /* ő Ő */ 0x0153, 499, /* œ Œ */ 0x0155, 499, /* ŕ Ŕ */ 0x0157, 499, /* ŗ Ŗ */ 0x0159, 499, /* ř Ř */ 0x015b, 499, /* ś Ś */ 0x015d, 499, /* ŝ Ŝ */ 0x015f, 499, /* ş Ş */ 0x0161, 499, /* š Š */ 0x0163, 499, /* ţ Ţ */ 0x0165, 499, /* ť Ť */ 0x0167, 499, /* ŧ Ŧ */ 0x0169, 499, /* ũ Ũ */ 0x016b, 499, /* ū Ū */ 0x016d, 499, /* ŭ Ŭ */ 0x016f, 499, /* ů Ů */ 0x0171, 499, /* ű Ű */ 0x0173, 499, /* ų Ų */ 0x0175, 499, /* ŵ Ŵ */ 0x0177, 499, /* ŷ Ŷ */ 0x017a, 499, /* ź Ź */ 0x017c, 499, /* ż Ż */ 0x017e, 499, /* ž Ž */ 0x017f, 200, /* ſ S */ 0x0183, 499, /* ƃ Ƃ */ 0x0185, 499, /* ƅ Ƅ */ 0x0188, 499, /* ƈ Ƈ */ 0x018c, 499, /* ƌ Ƌ */ 0x0192, 499, /* ƒ Ƒ */ 0x0199, 499, /* ƙ Ƙ */ 0x01a1, 499, /* ơ Ơ */ 0x01a3, 499, /* ƣ Ƣ */ 0x01a5, 499, /* ƥ Ƥ */ 0x01a8, 499, /* ƨ Ƨ */ 0x01ad, 499, /* ƭ Ƭ */ 0x01b0, 499, /* ư Ư */ 0x01b4, 499, /* ƴ Ƴ */ 0x01b6, 499, /* ƶ Ƶ */ 0x01b9, 499, /* ƹ Ƹ */ 0x01bd, 499, /* ƽ Ƽ */ 0x01c5, 499, /* Dž DŽ */ 0x01c6, 498, /* dž DŽ */ 0x01c8, 499, /* Lj LJ */ 0x01c9, 498, /* lj LJ */ 0x01cb, 499, /* Nj NJ */ 0x01cc, 498, /* nj NJ */ 0x01ce, 499, /* ǎ Ǎ */ 0x01d0, 499, /* ǐ Ǐ */ 0x01d2, 499, /* ǒ Ǒ */ 0x01d4, 499, /* ǔ Ǔ */ 0x01d6, 499, /* ǖ Ǖ */ 0x01d8, 499, /* ǘ Ǘ */ 0x01da, 499, /* ǚ Ǚ */ 0x01dc, 499, /* ǜ Ǜ */ 0x01df, 499, /* ǟ Ǟ */ 0x01e1, 499, /* ǡ Ǡ */ 0x01e3, 499, /* ǣ Ǣ */ 0x01e5, 499, /* ǥ Ǥ */ 0x01e7, 499, /* ǧ Ǧ */ 0x01e9, 499, /* ǩ Ǩ */ 0x01eb, 499, /* ǫ Ǫ */ 0x01ed, 499, /* ǭ Ǭ */ 0x01ef, 499, /* ǯ Ǯ */ 0x01f2, 499, /* Dz DZ */ 0x01f3, 498, /* dz DZ */ 0x01f5, 499, /* ǵ Ǵ */ 0x01fb, 499, /* ǻ Ǻ */ 0x01fd, 499, /* ǽ Ǽ */ 0x01ff, 499, /* ǿ Ǿ */ 0x0201, 499, /* ȁ Ȁ */ 0x0203, 499, /* ȃ Ȃ */ 0x0205, 499, /* ȅ Ȅ */ 0x0207, 499, /* ȇ Ȇ */ 0x0209, 499, /* ȉ Ȉ */ 0x020b, 499, /* ȋ Ȋ */ 0x020d, 499, /* ȍ Ȍ */ 0x020f, 499, /* ȏ Ȏ */ 0x0211, 499, /* ȑ Ȑ */ 0x0213, 499, /* ȓ Ȓ */ 0x0215, 499, /* ȕ Ȕ */ 0x0217, 499, /* ȗ Ȗ */ 0x0253, 290, /* ɓ Ɓ */ 0x0254, 294, /* ɔ Ɔ */ 0x025b, 297, /* ɛ Ɛ */ 0x0260, 295, /* ɠ Ɠ */ 0x0263, 293, /* ɣ Ɣ */ 0x0268, 291, /* ɨ Ɨ */ 0x0269, 289, /* ɩ Ɩ */ 0x026f, 289, /* ɯ Ɯ */ 0x0272, 287, /* ɲ Ɲ */ 0x0283, 282, /* ʃ Ʃ */ 0x0288, 282, /* ʈ Ʈ */ 0x0292, 281, /* ʒ Ʒ */ 0x03ac, 462, /* ά Ά */ 0x03cc, 436, /* ό Ό */ 0x03d0, 438, /* ϐ Β */ 0x03d1, 443, /* ϑ Θ */ 0x03d5, 453, /* ϕ Φ */ 0x03d6, 446, /* ϖ Π */ 0x03e3, 499, /* ϣ Ϣ */ 0x03e5, 499, /* ϥ Ϥ */ 0x03e7, 499, /* ϧ Ϧ */ 0x03e9, 499, /* ϩ Ϩ */ 0x03eb, 499, /* ϫ Ϫ */ 0x03ed, 499, /* ϭ Ϭ */ 0x03ef, 499, /* ϯ Ϯ */ 0x03f0, 414, /* ϰ Κ */ 0x03f1, 420, /* ϱ Ρ */ 0x0461, 499, /* ѡ Ѡ */ 0x0463, 499, /* ѣ Ѣ */ 0x0465, 499, /* ѥ Ѥ */ 0x0467, 499, /* ѧ Ѧ */ 0x0469, 499, /* ѩ Ѩ */ 0x046b, 499, /* ѫ Ѫ */ 0x046d, 499, /* ѭ Ѭ */ 0x046f, 499, /* ѯ Ѯ */ 0x0471, 499, /* ѱ Ѱ */ 0x0473, 499, /* ѳ Ѳ */ 0x0475, 499, /* ѵ Ѵ */ 0x0477, 499, /* ѷ Ѷ */ 0x0479, 499, /* ѹ Ѹ */ 0x047b, 499, /* ѻ Ѻ */ 0x047d, 499, /* ѽ Ѽ */ 0x047f, 499, /* ѿ Ѿ */ 0x0481, 499, /* ҁ Ҁ */ 0x0491, 499, /* ґ Ґ */ 0x0493, 499, /* ғ Ғ */ 0x0495, 499, /* ҕ Ҕ */ 0x0497, 499, /* җ Җ */ 0x0499, 499, /* ҙ Ҙ */ 0x049b, 499, /* қ Қ */ 0x049d, 499, /* ҝ Ҝ */ 0x049f, 499, /* ҟ Ҟ */ 0x04a1, 499, /* ҡ Ҡ */ 0x04a3, 499, /* ң Ң */ 0x04a5, 499, /* ҥ Ҥ */ 0x04a7, 499, /* ҧ Ҧ */ 0x04a9, 499, /* ҩ Ҩ */ 0x04ab, 499, /* ҫ Ҫ */ 0x04ad, 499, /* ҭ Ҭ */ 0x04af, 499, /* ү Ү */ 0x04b1, 499, /* ұ Ұ */ 0x04b3, 499, /* ҳ Ҳ */ 0x04b5, 499, /* ҵ Ҵ */ 0x04b7, 499, /* ҷ Ҷ */ 0x04b9, 499, /* ҹ Ҹ */ 0x04bb, 499, /* һ Һ */ 0x04bd, 499, /* ҽ Ҽ */ 0x04bf, 499, /* ҿ Ҿ */ 0x04c2, 499, /* ӂ Ӂ */ 0x04c4, 499, /* ӄ Ӄ */ 0x04c8, 499, /* ӈ Ӈ */ 0x04cc, 499, /* ӌ Ӌ */ 0x04d1, 499, /* ӑ Ӑ */ 0x04d3, 499, /* ӓ Ӓ */ 0x04d5, 499, /* ӕ Ӕ */ 0x04d7, 499, /* ӗ Ӗ */ 0x04d9, 499, /* ә Ә */ 0x04db, 499, /* ӛ Ӛ */ 0x04dd, 499, /* ӝ Ӝ */ 0x04df, 499, /* ӟ Ӟ */ 0x04e1, 499, /* ӡ Ӡ */ 0x04e3, 499, /* ӣ Ӣ */ 0x04e5, 499, /* ӥ Ӥ */ 0x04e7, 499, /* ӧ Ӧ */ 0x04e9, 499, /* ө Ө */ 0x04eb, 499, /* ӫ Ӫ */ 0x04ef, 499, /* ӯ Ӯ */ 0x04f1, 499, /* ӱ Ӱ */ 0x04f3, 499, /* ӳ Ӳ */ 0x04f5, 499, /* ӵ Ӵ */ 0x04f9, 499, /* ӹ Ӹ */ 0x1e01, 499, /* ḁ Ḁ */ 0x1e03, 499, /* ḃ Ḃ */ 0x1e05, 499, /* ḅ Ḅ */ 0x1e07, 499, /* ḇ Ḇ */ 0x1e09, 499, /* ḉ Ḉ */ 0x1e0b, 499, /* ḋ Ḋ */ 0x1e0d, 499, /* ḍ Ḍ */ 0x1e0f, 499, /* ḏ Ḏ */ 0x1e11, 499, /* ḑ Ḑ */ 0x1e13, 499, /* ḓ Ḓ */ 0x1e15, 499, /* ḕ Ḕ */ 0x1e17, 499, /* ḗ Ḗ */ 0x1e19, 499, /* ḙ Ḙ */ 0x1e1b, 499, /* ḛ Ḛ */ 0x1e1d, 499, /* ḝ Ḝ */ 0x1e1f, 499, /* ḟ Ḟ */ 0x1e21, 499, /* ḡ Ḡ */ 0x1e23, 499, /* ḣ Ḣ */ 0x1e25, 499, /* ḥ Ḥ */ 0x1e27, 499, /* ḧ Ḧ */ 0x1e29, 499, /* ḩ Ḩ */ 0x1e2b, 499, /* ḫ Ḫ */ 0x1e2d, 499, /* ḭ Ḭ */ 0x1e2f, 499, /* ḯ Ḯ */ 0x1e31, 499, /* ḱ Ḱ */ 0x1e33, 499, /* ḳ Ḳ */ 0x1e35, 499, /* ḵ Ḵ */ 0x1e37, 499, /* ḷ Ḷ */ 0x1e39, 499, /* ḹ Ḹ */ 0x1e3b, 499, /* ḻ Ḻ */ 0x1e3d, 499, /* ḽ Ḽ */ 0x1e3f, 499, /* ḿ Ḿ */ 0x1e41, 499, /* ṁ Ṁ */ 0x1e43, 499, /* ṃ Ṃ */ 0x1e45, 499, /* ṅ Ṅ */ 0x1e47, 499, /* ṇ Ṇ */ 0x1e49, 499, /* ṉ Ṉ */ 0x1e4b, 499, /* ṋ Ṋ */ 0x1e4d, 499, /* ṍ Ṍ */ 0x1e4f, 499, /* ṏ Ṏ */ 0x1e51, 499, /* ṑ Ṑ */ 0x1e53, 499, /* ṓ Ṓ */ 0x1e55, 499, /* ṕ Ṕ */ 0x1e57, 499, /* ṗ Ṗ */ 0x1e59, 499, /* ṙ Ṙ */ 0x1e5b, 499, /* ṛ Ṛ */ 0x1e5d, 499, /* ṝ Ṝ */ 0x1e5f, 499, /* ṟ Ṟ */ 0x1e61, 499, /* ṡ Ṡ */ 0x1e63, 499, /* ṣ Ṣ */ 0x1e65, 499, /* ṥ Ṥ */ 0x1e67, 499, /* ṧ Ṧ */ 0x1e69, 499, /* ṩ Ṩ */ 0x1e6b, 499, /* ṫ Ṫ */ 0x1e6d, 499, /* ṭ Ṭ */ 0x1e6f, 499, /* ṯ Ṯ */ 0x1e71, 499, /* ṱ Ṱ */ 0x1e73, 499, /* ṳ Ṳ */ 0x1e75, 499, /* ṵ Ṵ */ 0x1e77, 499, /* ṷ Ṷ */ 0x1e79, 499, /* ṹ Ṹ */ 0x1e7b, 499, /* ṻ Ṻ */ 0x1e7d, 499, /* ṽ Ṽ */ 0x1e7f, 499, /* ṿ Ṿ */ 0x1e81, 499, /* ẁ Ẁ */ 0x1e83, 499, /* ẃ Ẃ */ 0x1e85, 499, /* ẅ Ẅ */ 0x1e87, 499, /* ẇ Ẇ */ 0x1e89, 499, /* ẉ Ẉ */ 0x1e8b, 499, /* ẋ Ẋ */ 0x1e8d, 499, /* ẍ Ẍ */ 0x1e8f, 499, /* ẏ Ẏ */ 0x1e91, 499, /* ẑ Ẑ */ 0x1e93, 499, /* ẓ Ẓ */ 0x1e95, 499, /* ẕ Ẕ */ 0x1ea1, 499, /* ạ Ạ */ 0x1ea3, 499, /* ả Ả */ 0x1ea5, 499, /* ấ Ấ */ 0x1ea7, 499, /* ầ Ầ */ 0x1ea9, 499, /* ẩ Ẩ */ 0x1eab, 499, /* ẫ Ẫ */ 0x1ead, 499, /* ậ Ậ */ 0x1eaf, 499, /* ắ Ắ */ 0x1eb1, 499, /* ằ Ằ */ 0x1eb3, 499, /* ẳ Ẳ */ 0x1eb5, 499, /* ẵ Ẵ */ 0x1eb7, 499, /* ặ Ặ */ 0x1eb9, 499, /* ẹ Ẹ */ 0x1ebb, 499, /* ẻ Ẻ */ 0x1ebd, 499, /* ẽ Ẽ */ 0x1ebf, 499, /* ế Ế */ 0x1ec1, 499, /* ề Ề */ 0x1ec3, 499, /* ể Ể */ 0x1ec5, 499, /* ễ Ễ */ 0x1ec7, 499, /* ệ Ệ */ 0x1ec9, 499, /* ỉ Ỉ */ 0x1ecb, 499, /* ị Ị */ 0x1ecd, 499, /* ọ Ọ */ 0x1ecf, 499, /* ỏ Ỏ */ 0x1ed1, 499, /* ố Ố */ 0x1ed3, 499, /* ồ Ồ */ 0x1ed5, 499, /* ổ Ổ */ 0x1ed7, 499, /* ỗ Ỗ */ 0x1ed9, 499, /* ộ Ộ */ 0x1edb, 499, /* ớ Ớ */ 0x1edd, 499, /* ờ Ờ */ 0x1edf, 499, /* ở Ở */ 0x1ee1, 499, /* ỡ Ỡ */ 0x1ee3, 499, /* ợ Ợ */ 0x1ee5, 499, /* ụ Ụ */ 0x1ee7, 499, /* ủ Ủ */ 0x1ee9, 499, /* ứ Ứ */ 0x1eeb, 499, /* ừ Ừ */ 0x1eed, 499, /* ử Ử */ 0x1eef, 499, /* ữ Ữ */ 0x1ef1, 499, /* ự Ự */ 0x1ef3, 499, /* ỳ Ỳ */ 0x1ef5, 499, /* ỵ Ỵ */ 0x1ef7, 499, /* ỷ Ỷ */ 0x1ef9, 499, /* ỹ Ỹ */ 0x1f51, 508, /* ὑ Ὑ */ 0x1f53, 508, /* ὓ Ὓ */ 0x1f55, 508, /* ὕ Ὕ */ 0x1f57, 508, /* ὗ Ὗ */ 0x1fb3, 509, /* ᾳ ᾼ */ 0x1fc3, 509, /* ῃ ῌ */ 0x1fe5, 507, /* ῥ Ῥ */ 0x1ff3, 509, /* ῳ ῼ */ }; /* * upper case ranges * 3rd col is conversion excess 500 */ static Rune __tolower2[] = { 0x0041, 0x005a, 532, /* A-Z a-z */ 0x00c0, 0x00d6, 532, /* À-Ö à-ö */ 0x00d8, 0x00de, 532, /* Ø-Þ ø-þ */ 0x0189, 0x018a, 705, /* Ɖ-Ɗ ɖ-ɗ */ 0x018e, 0x018f, 702, /* Ǝ-Ə ɘ-ə */ 0x01b1, 0x01b2, 717, /* Ʊ-Ʋ ʊ-ʋ */ 0x0388, 0x038a, 537, /* Έ-Ί έ-ί */ 0x038e, 0x038f, 563, /* Ύ-Ώ ύ-ώ */ 0x0391, 0x03a1, 532, /* Α-Ρ α-ρ */ 0x03a3, 0x03ab, 532, /* Σ-Ϋ σ-ϋ */ 0x0401, 0x040c, 580, /* Ё-Ќ ё-ќ */ 0x040e, 0x040f, 580, /* Ў-Џ ў-џ */ 0x0410, 0x042f, 532, /* А-Я а-я */ 0x0531, 0x0556, 548, /* Ա-Ֆ ա-ֆ */ 0x10a0, 0x10c5, 548, /* Ⴀ-Ⴥ ა-ჵ */ 0x1f08, 0x1f0f, 492, /* Ἀ-Ἇ ἀ-ἇ */ 0x1f18, 0x1f1d, 492, /* Ἐ-Ἕ ἐ-ἕ */ 0x1f28, 0x1f2f, 492, /* Ἠ-Ἧ ἠ-ἧ */ 0x1f38, 0x1f3f, 492, /* Ἰ-Ἷ ἰ-ἷ */ 0x1f48, 0x1f4d, 492, /* Ὀ-Ὅ ὀ-ὅ */ 0x1f68, 0x1f6f, 492, /* Ὠ-Ὧ ὠ-ὧ */ 0x1f88, 0x1f8f, 492, /* ᾈ-ᾏ ᾀ-ᾇ */ 0x1f98, 0x1f9f, 492, /* ᾘ-ᾟ ᾐ-ᾗ */ 0x1fa8, 0x1faf, 492, /* ᾨ-ᾯ ᾠ-ᾧ */ 0x1fb8, 0x1fb9, 492, /* Ᾰ-Ᾱ ᾰ-ᾱ */ 0x1fba, 0x1fbb, 426, /* Ὰ-Ά ὰ-ά */ 0x1fc8, 0x1fcb, 414, /* Ὲ-Ή ὲ-ή */ 0x1fd8, 0x1fd9, 492, /* Ῐ-Ῑ ῐ-ῑ */ 0x1fda, 0x1fdb, 400, /* Ὶ-Ί ὶ-ί */ 0x1fe8, 0x1fe9, 492, /* Ῠ-Ῡ ῠ-ῡ */ 0x1fea, 0x1feb, 388, /* Ὺ-Ύ ὺ-ύ */ 0x1ff8, 0x1ff9, 372, /* Ὸ-Ό ὸ-ό */ 0x1ffa, 0x1ffb, 374, /* Ὼ-Ώ ὼ-ώ */ 0x2160, 0x216f, 516, /* Ⅰ-Ⅿ ⅰ-ⅿ */ 0x24b6, 0x24cf, 526, /* Ⓐ-Ⓩ ⓐ-ⓩ */ 0xff21, 0xff3a, 532, /* A-Z a-z */ }; /* * upper case singlets * 2nd col is conversion excess 500 */ static Rune __tolower1[] = { 0x0100, 501, /* Ā ā */ 0x0102, 501, /* Ă ă */ 0x0104, 501, /* Ą ą */ 0x0106, 501, /* Ć ć */ 0x0108, 501, /* Ĉ ĉ */ 0x010a, 501, /* Ċ ċ */ 0x010c, 501, /* Č č */ 0x010e, 501, /* Ď ď */ 0x0110, 501, /* Đ đ */ 0x0112, 501, /* Ē ē */ 0x0114, 501, /* Ĕ ĕ */ 0x0116, 501, /* Ė ė */ 0x0118, 501, /* Ę ę */ 0x011a, 501, /* Ě ě */ 0x011c, 501, /* Ĝ ĝ */ 0x011e, 501, /* Ğ ğ */ 0x0120, 501, /* Ġ ġ */ 0x0122, 501, /* Ģ ģ */ 0x0124, 501, /* Ĥ ĥ */ 0x0126, 501, /* Ħ ħ */ 0x0128, 501, /* Ĩ ĩ */ 0x012a, 501, /* Ī ī */ 0x012c, 501, /* Ĭ ĭ */ 0x012e, 501, /* Į į */ 0x0130, 301, /* İ i */ 0x0132, 501, /* IJ ij */ 0x0134, 501, /* Ĵ ĵ */ 0x0136, 501, /* Ķ ķ */ 0x0139, 501, /* Ĺ ĺ */ 0x013b, 501, /* Ļ ļ */ 0x013d, 501, /* Ľ ľ */ 0x013f, 501, /* Ŀ ŀ */ 0x0141, 501, /* Ł ł */ 0x0143, 501, /* Ń ń */ 0x0145, 501, /* Ņ ņ */ 0x0147, 501, /* Ň ň */ 0x014a, 501, /* Ŋ ŋ */ 0x014c, 501, /* Ō ō */ 0x014e, 501, /* Ŏ ŏ */ 0x0150, 501, /* Ő ő */ 0x0152, 501, /* Œ œ */ 0x0154, 501, /* Ŕ ŕ */ 0x0156, 501, /* Ŗ ŗ */ 0x0158, 501, /* Ř ř */ 0x015a, 501, /* Ś ś */ 0x015c, 501, /* Ŝ ŝ */ 0x015e, 501, /* Ş ş */ 0x0160, 501, /* Š š */ 0x0162, 501, /* Ţ ţ */ 0x0164, 501, /* Ť ť */ 0x0166, 501, /* Ŧ ŧ */ 0x0168, 501, /* Ũ ũ */ 0x016a, 501, /* Ū ū */ 0x016c, 501, /* Ŭ ŭ */ 0x016e, 501, /* Ů ů */ 0x0170, 501, /* Ű ű */ 0x0172, 501, /* Ų ų */ 0x0174, 501, /* Ŵ ŵ */ 0x0176, 501, /* Ŷ ŷ */ 0x0178, 379, /* Ÿ ÿ */ 0x0179, 501, /* Ź ź */ 0x017b, 501, /* Ż ż */ 0x017d, 501, /* Ž ž */ 0x0181, 710, /* Ɓ ɓ */ 0x0182, 501, /* Ƃ ƃ */ 0x0184, 501, /* Ƅ ƅ */ 0x0186, 706, /* Ɔ ɔ */ 0x0187, 501, /* Ƈ ƈ */ 0x018b, 501, /* Ƌ ƌ */ 0x0190, 703, /* Ɛ ɛ */ 0x0191, 501, /* Ƒ ƒ */ 0x0193, 705, /* Ɠ ɠ */ 0x0194, 707, /* Ɣ ɣ */ 0x0196, 711, /* Ɩ ɩ */ 0x0197, 709, /* Ɨ ɨ */ 0x0198, 501, /* Ƙ ƙ */ 0x019c, 711, /* Ɯ ɯ */ 0x019d, 713, /* Ɲ ɲ */ 0x01a0, 501, /* Ơ ơ */ 0x01a2, 501, /* Ƣ ƣ */ 0x01a4, 501, /* Ƥ ƥ */ 0x01a7, 501, /* Ƨ ƨ */ 0x01a9, 718, /* Ʃ ʃ */ 0x01ac, 501, /* Ƭ ƭ */ 0x01ae, 718, /* Ʈ ʈ */ 0x01af, 501, /* Ư ư */ 0x01b3, 501, /* Ƴ ƴ */ 0x01b5, 501, /* Ƶ ƶ */ 0x01b7, 719, /* Ʒ ʒ */ 0x01b8, 501, /* Ƹ ƹ */ 0x01bc, 501, /* Ƽ ƽ */ 0x01c4, 502, /* DŽ dž */ 0x01c5, 501, /* Dž dž */ 0x01c7, 502, /* LJ lj */ 0x01c8, 501, /* Lj lj */ 0x01ca, 502, /* NJ nj */ 0x01cb, 501, /* Nj nj */ 0x01cd, 501, /* Ǎ ǎ */ 0x01cf, 501, /* Ǐ ǐ */ 0x01d1, 501, /* Ǒ ǒ */ 0x01d3, 501, /* Ǔ ǔ */ 0x01d5, 501, /* Ǖ ǖ */ 0x01d7, 501, /* Ǘ ǘ */ 0x01d9, 501, /* Ǚ ǚ */ 0x01db, 501, /* Ǜ ǜ */ 0x01de, 501, /* Ǟ ǟ */ 0x01e0, 501, /* Ǡ ǡ */ 0x01e2, 501, /* Ǣ ǣ */ 0x01e4, 501, /* Ǥ ǥ */ 0x01e6, 501, /* Ǧ ǧ */ 0x01e8, 501, /* Ǩ ǩ */ 0x01ea, 501, /* Ǫ ǫ */ 0x01ec, 501, /* Ǭ ǭ */ 0x01ee, 501, /* Ǯ ǯ */ 0x01f1, 502, /* DZ dz */ 0x01f2, 501, /* Dz dz */ 0x01f4, 501, /* Ǵ ǵ */ 0x01fa, 501, /* Ǻ ǻ */ 0x01fc, 501, /* Ǽ ǽ */ 0x01fe, 501, /* Ǿ ǿ */ 0x0200, 501, /* Ȁ ȁ */ 0x0202, 501, /* Ȃ ȃ */ 0x0204, 501, /* Ȅ ȅ */ 0x0206, 501, /* Ȇ ȇ */ 0x0208, 501, /* Ȉ ȉ */ 0x020a, 501, /* Ȋ ȋ */ 0x020c, 501, /* Ȍ ȍ */ 0x020e, 501, /* Ȏ ȏ */ 0x0210, 501, /* Ȑ ȑ */ 0x0212, 501, /* Ȓ ȓ */ 0x0214, 501, /* Ȕ ȕ */ 0x0216, 501, /* Ȗ ȗ */ 0x0386, 538, /* Ά ά */ 0x038c, 564, /* Ό ό */ 0x03e2, 501, /* Ϣ ϣ */ 0x03e4, 501, /* Ϥ ϥ */ 0x03e6, 501, /* Ϧ ϧ */ 0x03e8, 501, /* Ϩ ϩ */ 0x03ea, 501, /* Ϫ ϫ */ 0x03ec, 501, /* Ϭ ϭ */ 0x03ee, 501, /* Ϯ ϯ */ 0x0460, 501, /* Ѡ ѡ */ 0x0462, 501, /* Ѣ ѣ */ 0x0464, 501, /* Ѥ ѥ */ 0x0466, 501, /* Ѧ ѧ */ 0x0468, 501, /* Ѩ ѩ */ 0x046a, 501, /* Ѫ ѫ */ 0x046c, 501, /* Ѭ ѭ */ 0x046e, 501, /* Ѯ ѯ */ 0x0470, 501, /* Ѱ ѱ */ 0x0472, 501, /* Ѳ ѳ */ 0x0474, 501, /* Ѵ ѵ */ 0x0476, 501, /* Ѷ ѷ */ 0x0478, 501, /* Ѹ ѹ */ 0x047a, 501, /* Ѻ ѻ */ 0x047c, 501, /* Ѽ ѽ */ 0x047e, 501, /* Ѿ ѿ */ 0x0480, 501, /* Ҁ ҁ */ 0x0490, 501, /* Ґ ґ */ 0x0492, 501, /* Ғ ғ */ 0x0494, 501, /* Ҕ ҕ */ 0x0496, 501, /* Җ җ */ 0x0498, 501, /* Ҙ ҙ */ 0x049a, 501, /* Қ қ */ 0x049c, 501, /* Ҝ ҝ */ 0x049e, 501, /* Ҟ ҟ */ 0x04a0, 501, /* Ҡ ҡ */ 0x04a2, 501, /* Ң ң */ 0x04a4, 501, /* Ҥ ҥ */ 0x04a6, 501, /* Ҧ ҧ */ 0x04a8, 501, /* Ҩ ҩ */ 0x04aa, 501, /* Ҫ ҫ */ 0x04ac, 501, /* Ҭ ҭ */ 0x04ae, 501, /* Ү ү */ 0x04b0, 501, /* Ұ ұ */ 0x04b2, 501, /* Ҳ ҳ */ 0x04b4, 501, /* Ҵ ҵ */ 0x04b6, 501, /* Ҷ ҷ */ 0x04b8, 501, /* Ҹ ҹ */ 0x04ba, 501, /* Һ һ */ 0x04bc, 501, /* Ҽ ҽ */ 0x04be, 501, /* Ҿ ҿ */ 0x04c1, 501, /* Ӂ ӂ */ 0x04c3, 501, /* Ӄ ӄ */ 0x04c7, 501, /* Ӈ ӈ */ 0x04cb, 501, /* Ӌ ӌ */ 0x04d0, 501, /* Ӑ ӑ */ 0x04d2, 501, /* Ӓ ӓ */ 0x04d4, 501, /* Ӕ ӕ */ 0x04d6, 501, /* Ӗ ӗ */ 0x04d8, 501, /* Ә ә */ 0x04da, 501, /* Ӛ ӛ */ 0x04dc, 501, /* Ӝ ӝ */ 0x04de, 501, /* Ӟ ӟ */ 0x04e0, 501, /* Ӡ ӡ */ 0x04e2, 501, /* Ӣ ӣ */ 0x04e4, 501, /* Ӥ ӥ */ 0x04e6, 501, /* Ӧ ӧ */ 0x04e8, 501, /* Ө ө */ 0x04ea, 501, /* Ӫ ӫ */ 0x04ee, 501, /* Ӯ ӯ */ 0x04f0, 501, /* Ӱ ӱ */ 0x04f2, 501, /* Ӳ ӳ */ 0x04f4, 501, /* Ӵ ӵ */ 0x04f8, 501, /* Ӹ ӹ */ 0x1e00, 501, /* Ḁ ḁ */ 0x1e02, 501, /* Ḃ ḃ */ 0x1e04, 501, /* Ḅ ḅ */ 0x1e06, 501, /* Ḇ ḇ */ 0x1e08, 501, /* Ḉ ḉ */ 0x1e0a, 501, /* Ḋ ḋ */ 0x1e0c, 501, /* Ḍ ḍ */ 0x1e0e, 501, /* Ḏ ḏ */ 0x1e10, 501, /* Ḑ ḑ */ 0x1e12, 501, /* Ḓ ḓ */ 0x1e14, 501, /* Ḕ ḕ */ 0x1e16, 501, /* Ḗ ḗ */ 0x1e18, 501, /* Ḙ ḙ */ 0x1e1a, 501, /* Ḛ ḛ */ 0x1e1c, 501, /* Ḝ ḝ */ 0x1e1e, 501, /* Ḟ ḟ */ 0x1e20, 501, /* Ḡ ḡ */ 0x1e22, 501, /* Ḣ ḣ */ 0x1e24, 501, /* Ḥ ḥ */ 0x1e26, 501, /* Ḧ ḧ */ 0x1e28, 501, /* Ḩ ḩ */ 0x1e2a, 501, /* Ḫ ḫ */ 0x1e2c, 501, /* Ḭ ḭ */ 0x1e2e, 501, /* Ḯ ḯ */ 0x1e30, 501, /* Ḱ ḱ */ 0x1e32, 501, /* Ḳ ḳ */ 0x1e34, 501, /* Ḵ ḵ */ 0x1e36, 501, /* Ḷ ḷ */ 0x1e38, 501, /* Ḹ ḹ */ 0x1e3a, 501, /* Ḻ ḻ */ 0x1e3c, 501, /* Ḽ ḽ */ 0x1e3e, 501, /* Ḿ ḿ */ 0x1e40, 501, /* Ṁ ṁ */ 0x1e42, 501, /* Ṃ ṃ */ 0x1e44, 501, /* Ṅ ṅ */ 0x1e46, 501, /* Ṇ ṇ */ 0x1e48, 501, /* Ṉ ṉ */ 0x1e4a, 501, /* Ṋ ṋ */ 0x1e4c, 501, /* Ṍ ṍ */ 0x1e4e, 501, /* Ṏ ṏ */ 0x1e50, 501, /* Ṑ ṑ */ 0x1e52, 501, /* Ṓ ṓ */ 0x1e54, 501, /* Ṕ ṕ */ 0x1e56, 501, /* Ṗ ṗ */ 0x1e58, 501, /* Ṙ ṙ */ 0x1e5a, 501, /* Ṛ ṛ */ 0x1e5c, 501, /* Ṝ ṝ */ 0x1e5e, 501, /* Ṟ ṟ */ 0x1e60, 501, /* Ṡ ṡ */ 0x1e62, 501, /* Ṣ ṣ */ 0x1e64, 501, /* Ṥ ṥ */ 0x1e66, 501, /* Ṧ ṧ */ 0x1e68, 501, /* Ṩ ṩ */ 0x1e6a, 501, /* Ṫ ṫ */ 0x1e6c, 501, /* Ṭ ṭ */ 0x1e6e, 501, /* Ṯ ṯ */ 0x1e70, 501, /* Ṱ ṱ */ 0x1e72, 501, /* Ṳ ṳ */ 0x1e74, 501, /* Ṵ ṵ */ 0x1e76, 501, /* Ṷ ṷ */ 0x1e78, 501, /* Ṹ ṹ */ 0x1e7a, 501, /* Ṻ ṻ */ 0x1e7c, 501, /* Ṽ ṽ */ 0x1e7e, 501, /* Ṿ ṿ */ 0x1e80, 501, /* Ẁ ẁ */ 0x1e82, 501, /* Ẃ ẃ */ 0x1e84, 501, /* Ẅ ẅ */ 0x1e86, 501, /* Ẇ ẇ */ 0x1e88, 501, /* Ẉ ẉ */ 0x1e8a, 501, /* Ẋ ẋ */ 0x1e8c, 501, /* Ẍ ẍ */ 0x1e8e, 501, /* Ẏ ẏ */ 0x1e90, 501, /* Ẑ ẑ */ 0x1e92, 501, /* Ẓ ẓ */ 0x1e94, 501, /* Ẕ ẕ */ 0x1ea0, 501, /* Ạ ạ */ 0x1ea2, 501, /* Ả ả */ 0x1ea4, 501, /* Ấ ấ */ 0x1ea6, 501, /* Ầ ầ */ 0x1ea8, 501, /* Ẩ ẩ */ 0x1eaa, 501, /* Ẫ ẫ */ 0x1eac, 501, /* Ậ ậ */ 0x1eae, 501, /* Ắ ắ */ 0x1eb0, 501, /* Ằ ằ */ 0x1eb2, 501, /* Ẳ ẳ */ 0x1eb4, 501, /* Ẵ ẵ */ 0x1eb6, 501, /* Ặ ặ */ 0x1eb8, 501, /* Ẹ ẹ */ 0x1eba, 501, /* Ẻ ẻ */ 0x1ebc, 501, /* Ẽ ẽ */ 0x1ebe, 501, /* Ế ế */ 0x1ec0, 501, /* Ề ề */ 0x1ec2, 501, /* Ể ể */ 0x1ec4, 501, /* Ễ ễ */ 0x1ec6, 501, /* Ệ ệ */ 0x1ec8, 501, /* Ỉ ỉ */ 0x1eca, 501, /* Ị ị */ 0x1ecc, 501, /* Ọ ọ */ 0x1ece, 501, /* Ỏ ỏ */ 0x1ed0, 501, /* Ố ố */ 0x1ed2, 501, /* Ồ ồ */ 0x1ed4, 501, /* Ổ ổ */ 0x1ed6, 501, /* Ỗ ỗ */ 0x1ed8, 501, /* Ộ ộ */ 0x1eda, 501, /* Ớ ớ */ 0x1edc, 501, /* Ờ ờ */ 0x1ede, 501, /* Ở ở */ 0x1ee0, 501, /* Ỡ ỡ */ 0x1ee2, 501, /* Ợ ợ */ 0x1ee4, 501, /* Ụ ụ */ 0x1ee6, 501, /* Ủ ủ */ 0x1ee8, 501, /* Ứ ứ */ 0x1eea, 501, /* Ừ ừ */ 0x1eec, 501, /* Ử ử */ 0x1eee, 501, /* Ữ ữ */ 0x1ef0, 501, /* Ự ự */ 0x1ef2, 501, /* Ỳ ỳ */ 0x1ef4, 501, /* Ỵ ỵ */ 0x1ef6, 501, /* Ỷ ỷ */ 0x1ef8, 501, /* Ỹ ỹ */ 0x1f59, 492, /* Ὑ ὑ */ 0x1f5b, 492, /* Ὓ ὓ */ 0x1f5d, 492, /* Ὕ ὕ */ 0x1f5f, 492, /* Ὗ ὗ */ 0x1fbc, 491, /* ᾼ ᾳ */ 0x1fcc, 491, /* ῌ ῃ */ 0x1fec, 493, /* Ῥ ῥ */ 0x1ffc, 491, /* ῼ ῳ */ }; /* * title characters are those between * upper and lower case. ie DZ Dz dz */ static Rune __totitle1[] = { 0x01c4, 501, /* DŽ Dž */ 0x01c6, 499, /* dž Dž */ 0x01c7, 501, /* LJ Lj */ 0x01c9, 499, /* lj Lj */ 0x01ca, 501, /* NJ Nj */ 0x01cc, 499, /* nj Nj */ 0x01f1, 501, /* DZ Dz */ 0x01f3, 499, /* dz Dz */ }; static Rune* bsearch(Rune c, Rune *t, int n, int ne) { Rune *p; int m; while(n > 1) { m = n/2; p = t + m*ne; if(c >= p[0]) { t = p; n = n-m; } else n = m; } if(n && c >= t[0]) return t; return 0; } Rune tolowerrune(Rune c) { Rune *p; p = bsearch(c, __tolower2, nelem(__tolower2)/3, 3); if(p && c >= p[0] && c <= p[1]) return c + p[2] - 500; p = bsearch(c, __tolower1, nelem(__tolower1)/2, 2); if(p && c == p[0]) return c + p[1] - 500; return c; } Rune toupperrune(Rune c) { Rune *p; p = bsearch(c, __toupper2, nelem(__toupper2)/3, 3); if(p && c >= p[0] && c <= p[1]) return c + p[2] - 500; p = bsearch(c, __toupper1, nelem(__toupper1)/2, 2); if(p && c == p[0]) return c + p[1] - 500; return c; } Rune totitlerune(Rune c) { Rune *p; p = bsearch(c, __totitle1, nelem(__totitle1)/2, 2); if(p && c == p[0]) return c + p[1] - 500; return c; } int islowerrune(Rune c) { Rune *p; p = bsearch(c, __toupper2, nelem(__toupper2)/3, 3); if(p && c >= p[0] && c <= p[1]) return 1; p = bsearch(c, __toupper1, nelem(__toupper1)/2, 2); if(p && c == p[0]) return 1; return 0; } int isupperrune(Rune c) { Rune *p; p = bsearch(c, __tolower2, nelem(__tolower2)/3, 3); if(p && c >= p[0] && c <= p[1]) return 1; p = bsearch(c, __tolower1, nelem(__tolower1)/2, 2); if(p && c == p[0]) return 1; return 0; } int isalpharune(Rune c) { Rune *p; if(isupperrune(c) || islowerrune(c)) return 1; p = bsearch(c, __alpha2, nelem(__alpha2)/2, 2); if(p && c >= p[0] && c <= p[1]) return 1; p = bsearch(c, __alpha1, nelem(__alpha1), 1); if(p && c == p[0]) return 1; return 0; } int istitlerune(Rune c) { return isupperrune(c) && islowerrune(c); } int isspacerune(Rune c) { Rune *p; p = bsearch(c, __space2, nelem(__space2)/2, 2); if(p && c >= p[0] && c <= p[1]) return 1; return 0; } /sys/src/ape/lib/utf/utfdef.h 664 sys sys 1368489837 335 #define uchar _utfuchar #define ushort _utfushort #define uint _utfuint #define ulong _utfulong #define vlong _utfvlong #define uvlong _utfuvlong typedef unsigned char uchar; typedef unsigned short ushort; typedef unsigned int uint; typedef unsigned long ulong; #define nelem(x) (sizeof(x)/sizeof((x)[0])) #define nil ((void*)0) /sys/src/ape/lib/utf/utfecpy.c 664 sys sys 1368489874 1079 /* * The authors of this software are Rob Pike and Ken Thompson. * Copyright (c) 2002 by Lucent Technologies. * Permission to use, copy, modify, and distribute this software for any * purpose without fee is hereby granted, provided that this entire notice * is included in all copies of any software which is or includes a copy * or modification of this software and in all copies of the supporting * documentation for such software. * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. */ #include #include #include "utf.h" #include "utfdef.h" char* utfecpy(char *to, char *e, char *from) { char *end; if(to >= e) return to; end = memccpy(to, from, '\0', e - to); if(end == nil){ end = e-1; while(end>to && (*--end&0xC0)==0x80) ; *end = '\0'; }else{ end--; } return end; } /sys/src/ape/lib/utf/utflen.c 664 sys sys 1368489868 1026 /* * The authors of this software are Rob Pike and Ken Thompson. * Copyright (c) 2002 by Lucent Technologies. * Permission to use, copy, modify, and distribute this software for any * purpose without fee is hereby granted, provided that this entire notice * is included in all copies of any software which is or includes a copy * or modification of this software and in all copies of the supporting * documentation for such software. * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. */ #include #include #include "utf.h" #include "utfdef.h" int utflen(char *s) { int c; long n; Rune rune; n = 0; for(;;) { c = *(uchar*)s; if(c < Runeself) { if(c == 0) return n; s++; } else s += chartorune(&rune, s); n++; } } /sys/src/ape/lib/utf/utfnlen.c 664 sys sys 1368489864 1112 /* * The authors of this software are Rob Pike and Ken Thompson. * Copyright (c) 2002 by Lucent Technologies. * Permission to use, copy, modify, and distribute this software for any * purpose without fee is hereby granted, provided that this entire notice * is included in all copies of any software which is or includes a copy * or modification of this software and in all copies of the supporting * documentation for such software. * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. */ #include #include #include "utf.h" #include "utfdef.h" int utfnlen(char *s, long m) { int c; long n; Rune rune; char *es; es = s + m; for(n = 0; s < es; n++) { c = *(uchar*)s; if(c < Runeself){ if(c == '\0') break; s++; continue; } if(!fullrune(s, es-s)) break; s += chartorune(&rune, s); } return n; } /sys/src/ape/lib/utf/utfrrune.c 664 sys sys 1368489857 1198 /* * The authors of this software are Rob Pike and Ken Thompson. * Copyright (c) 2002 by Lucent Technologies. * Permission to use, copy, modify, and distribute this software for any * purpose without fee is hereby granted, provided that this entire notice * is included in all copies of any software which is or includes a copy * or modification of this software and in all copies of the supporting * documentation for such software. * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. */ #include #include #include "utf.h" #include "utfdef.h" char* utfrrune(char *s, long c) { long c1; Rune r; char *s1; if(c < Runesync) /* not part of utf sequence */ return strrchr(s, c); s1 = 0; for(;;) { c1 = *(uchar*)s; if(c1 < Runeself) { /* one byte rune */ if(c1 == 0) return s1; if(c1 == c) s1 = s; s++; continue; } c1 = chartorune(&r, s); if(r == c) s1 = s; s += c1; } } /sys/src/ape/lib/utf/utfrune.c 664 sys sys 1368489852 1185 /* * The authors of this software are Rob Pike and Ken Thompson. * Copyright (c) 2002 by Lucent Technologies. * Permission to use, copy, modify, and distribute this software for any * purpose without fee is hereby granted, provided that this entire notice * is included in all copies of any software which is or includes a copy * or modification of this software and in all copies of the supporting * documentation for such software. * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. */ #include #include #include "utf.h" #include "utfdef.h" char* utfrune(char *s, long c) { long c1; Rune r; int n; if(c < Runesync) /* not part of utf sequence */ return strchr(s, c); for(;;) { c1 = *(uchar*)s; if(c1 < Runeself) { /* one byte rune */ if(c1 == 0) return 0; if(c1 == c) return s; s++; continue; } n = chartorune(&r, s); if(r == c) return s; s += n; } } /sys/src/ape/lib/utf/utfutf.c 664 sys sys 1368489742 1184 /* * The authors of this software are Rob Pike and Ken Thompson. * Copyright (c) 2002 by Lucent Technologies. * Permission to use, copy, modify, and distribute this software for any * purpose without fee is hereby granted, provided that this entire notice * is included in all copies of any software which is or includes a copy * or modification of this software and in all copies of the supporting * documentation for such software. * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. */ #include #include #include "utf.h" #include "utfdef.h" /* * Return pointer to first occurrence of s2 in s1, * 0 if none */ char* utfutf(char *s1, char *s2) { char *p; long f, n1, n2; Rune r; n1 = chartorune(&r, s2); f = r; if(f <= Runesync) /* represents self */ return strstr(s1, s2); n2 = strlen(s2); for(p=s1; p=utfrune(p, f); p+=n1) if(strncmp(p, s2, n2) == 0) return p; return 0; } /sys/src/ape/lib/v 20000000775 sys sys 1369861529 0 /sys/src/ape/lib/v/error.c 664 sys sys 1367613437 197 #define _POSIX_SOURCE #define _RESEARCH_SOURCE #include #include #include char *_progname; void _perror(char *s) { fprintf(stderr, "%s: ", _progname); perror(s); } /sys/src/ape/lib/v/getfields.c 664 sys sys 1369847579 3661 #define _RESEARCH_SOURCE #include #include static char is_sep[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; static char is_field[256] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, }; static char last_sep[256]; char * setfields(char *arg) { unsigned char *s; int i; for(i = 1, s = (unsigned char *)last_sep; i < 256; i++) if(is_sep[i]) *s++ = i; *s = 0; memset(is_sep, 0, sizeof is_sep); memset(is_field, 1, sizeof is_field); for(s = (unsigned char *)arg; *s;){ is_sep[*s] = 1; is_field[*s++] = 0; } is_field[0] = 0; return(last_sep); } int getfields(char *ss, char **sp, int nptrs) { unsigned char *s = (unsigned char *)ss; unsigned char **p = (unsigned char **)sp; unsigned c; c = 0; for(;;){ if(--nptrs < 0) break; *p++ = s; while(is_field[c = *s++]) ; if(c == 0) break; s[-1] = 0; } if(nptrs > 0) *p = 0; else if(--s >= (unsigned char *)ss) *s = c; return(p - (unsigned char **)sp); } int getmfields(char *ss, char **sp, int nptrs) { unsigned char *s = (unsigned char *)ss; unsigned char **p = (unsigned char **)sp; unsigned c; if(nptrs <= 0) return(0); goto flushdelim; for(;;){ *p++ = s; if(--nptrs == 0) break; while(is_field[c = *s++]) ; /* * s is now pointing 1 past the delimiter of the last field * c is the delimiter */ if(c == 0) break; s[-1] = 0; flushdelim: while(is_sep[c = *s++]) ; /* * s is now pointing 1 past the beginning of the next field * c is the first letter of the field */ if(c == 0) break; s--; /* * s is now pointing to the beginning of the next field * c is the first letter of the field */ } if(nptrs > 0) *p = 0; return(p - (unsigned char **)sp); } #ifdef MAIN #include main() { char *fields[256]; char *s; int n, i; char buf[1024]; print("go:\n"); while(s = Frdline(0)){ strcpy(buf, s); Fprint(1, "getf:"); n = getfields(s, fields, 4); for(i = 0; i < n; i++) Fprint(1, " >%s<", fields[i]); Fputc(1, '\n'); Fprint(1, "getmf:"); n = getmfields(buf, fields, 4); for(i = 0; i < n; i++) Fprint(1, " >%s<", fields[i]); Fputc(1, '\n'); Fflush(1); } exit(0); } #endif /sys/src/ape/lib/v/max.c 664 sys sys 1367613437 87 #define _RESEARCH_SOURCE #include max(int a, int b) { return (a>b? a: b); } /sys/src/ape/lib/v/min.c 664 sys sys 1367613437 87 #define _RESEARCH_SOURCE #include min(int a, int b) { return (a #define MASK 0x7FFFFFFFL #define FRACT (1.0 / (MASK + 1.0)) extern long lrand(void); double frand(void) { return lrand() * FRACT; } nrand(int n) { long slop, v; slop = MASK % n; do v = lrand(); while(v <= slop); return v % n; } /sys/src/ape/lib/v/plan9 20000000775 sys sys 1369186293 0 /sys/src/ape/lib/v/plan9/getpass.c 664 sys sys 1367613437 771 #define _POSIX_SOURCE #define _RESEARCH_SOURCE #include #include #include #include char * getpass(char *prompt) { int c; char *p; FILE *fi; static char pbuf[PASS_MAX]; void (*sig)(int); if ((fi = fopen("/dev/cons", "r")) == NULL) fi = stdin; else setbuf(fi, NULL); sig = signal(SIGINT, SIG_IGN); tty_echooff(fileno(fi)); fprintf(stderr, "%s", prompt); fflush(stderr); for (p = pbuf; (c = getc(fi)) != '\n' && c != EOF; ) if (c == ('u' & 037)) p = pbuf; else if (c == '\b') { if (p > pbuf) p--; } else if (p < &pbuf[sizeof(pbuf)-1]) *p++ = c; *p = '\0'; fprintf(stderr, "\n"); fflush(stderr); tty_echoon(fileno(fi)); signal(SIGINT, sig); if (fi != stdin) fclose(fi); return(pbuf); } /sys/src/ape/lib/v/plan9/tty.c 664 sys sys 1369185887 581 /* * turn raw (no echo, etc.) on and off. * ptyfs is gone, so don't even try tcsetattr, etc. */ #define _POSIX_SOURCE #define _RESEARCH_SOURCE #include #include #include #include static int ctlfd = -1; /* fd is ignored */ tty_echooff(int fd) { USED(fd); if(ctlfd >= 0) return 0; ctlfd = open("/dev/consctl", O_WRONLY); if(ctlfd < 0) return -1; write(ctlfd, "rawon", 5); return 0; } tty_echoon(int fd) { USED(fd); if(ctlfd >= 0){ write(ctlfd, "rawoff", 6); close(ctlfd); ctlfd = -1; return 0; } return -1; } /sys/src/ape/lib/v/rand.c 664 sys sys 1367613437 826 #define _RESEARCH_SOURCE #include #include /* random number generator from cacm 31 10, oct 88 for 32 bit integers (called long here) */ #ifdef MAIN #define A 16807 #define M 2147483647 #define Q 127773 #define R 2836 #else #define A 48271 #define M 2147483647 #define Q 44488 #define R 3399 #endif static long seed = 1; void srand(unsigned int newseed) { seed = newseed; } long lrand(void) { long lo, hi, test; hi = seed/Q; lo = seed%Q; test = A*lo - R*hi; if(test > 0) seed = test; else seed = test+M; return(seed); } int rand(void) { return lrand()%(RAND_MAX+1); } #ifdef MAIN main() { int i; for(i = 0; i < 10000; i++) rand(); if(seed == 1043618065) printf(" rand: pass\n"); else printf("*****rand: fail; seed=%u, should be 1043618065\n", seed); exit(0); } #endif /sys/src/ape/mkfile 664 sys sys 1367613437 368 none:VQ: echo usage: mk all, install, installall, cmd, cmd.install, lib, lib.install all:V: mk lib.all mk cmd.all mk 9src.all lib.%:V: cd lib mk $stem lib.clean:V: cd lib rm -f .mk.$objtype mk clean lib.nuke:V: cd lib rm -f .mk.$objtype mk nuke cmd.%:V: cd cmd mk $stem 9src.%:V: cd 9src mk $stem &:V: mk lib.$stem mk cmd.$stem mk 9src.$stem end of archive