/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
</$objtype/mkfile
# ptyfs\ # for X11, not going to bother
TARG=\
stty\
tar\
BIN=/$objtype/bin/ape
</sys/src/cmd/mkmany
/sys/src/ape/9src/stty.c 664 sys sys 1367613436 3867
#include <u.h>
#include <libc.h>
#include <tty.h>
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 <u.h>
#include <libc.h>
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; j<argc; j++, i++)
nargv[i] = argv[j];
nargv[i++] = nil;
assert(i == nargc);
switch(fork()){
case -1:
fprint(2, "ape/tar: fork failed: %r\n");
exits("fork");
case 0:
exec(execbuf, nargv);
fprint(2, "exec %s fails: %r\n", execbuf);
_exits("exec");
default:
w = wait();
if(w == nil)
exits("wait failed");
if(w->msg[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<n> instead of -c and -C <n> 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 <stdlib.h>
#include <string.h>
#include <stdio.h>
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 <u.h>
#include <libc.h>
/*
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.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) 19yy <name of author>
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.
<signature of Ty Coon>, 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 <[email protected]>
* 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 (<sys/wait.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 <[email protected]>
* 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 <[email protected]>
* 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: <string.h>: Include if HAVE_STRING_H, too.
<ctype.h>: Include here. All includers changed.
(CTYPE_DOMAIN, ISDIGIT, ISPRINT, ISSPACE, ISUPPER): New macros that
work around common <ctype.h> 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 <[email protected]>
* 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 <[email protected]>
* 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 <string.h>.
(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 <config.h>, 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 <[email protected]>
* 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 <[email protected]>
* 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 <sys/file.h> 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 ([email protected])
* Makefile.in (info, dvi, diff.dvi): New targets.
(clean): Remove TeX output files.
Fri Jun 17 05:37:52 1994 Paul Eggert ([email protected])
* cmp.c, io.c (word): Change from typedef to #define, to avoid
collision with Unicos 8.0 <sys/types.h>, which also typedefs `word'.
Thu Apr 15 00:53:01 1994 Paul Eggert ([email protected])
* 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 ([email protected])
* Makefile.in (LDFLAGS): Remove -g. Change all link commands
to use both $(CFLAGS) and $(LDFLAGS).
Mon Dec 13 12:23:27 1993 Paul Eggert ([email protected])
* system.h: Don't assume dirent.h exists just because
_POSIX_VERSION is defined.
Fri Dec 3 18:39:39 1993 Paul Eggert ([email protected])
* diff.c (main): allow -pu.
Tue Nov 23 03:51:08 1993 Paul Eggert ([email protected])
* Makefile.in (distclean): Remove config.h.
Wed Nov 10 00:28:27 1993 Paul Eggert ([email protected])
* 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 ([email protected])
* 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 ([email protected])
* 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 <sys/types.h> always; some nonstandard hosts
need it for size_t even if STDC_HEADERS.
Sat Sep 18 01:33:07 1993 Paul Eggert ([email protected])
* 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
<stdlib.h>s don't declare them.
(memchr): Likewise for <string.h>.
* 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 ([email protected])
* 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 <config.h>, 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 <config.h>' works.
(.c.o, diff3.o, sdiff.o): Use it.
Mon Sep 13 06:45:43 1993 Paul Eggert ([email protected])
* 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 <limits.h>, <stdlib.h>, <string.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 ([email protected])
* 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 ([email protected])
* 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 ([email protected])
* 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 ([email protected])
* 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 ([email protected])
* Makefile.in (installdirs): New target.
(install): Use it.
(Makefile, config.status, configure): New targets.
Sat Jun 5 23:10:40 1993 Paul Eggert ([email protected])
* Makefile.in (dist): Switch from .z to .gz.
Wed May 26 17:16:02 1993 Paul Eggert ([email protected])
* 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 ([email protected])
* 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 ([email protected])
* dir.c (dir_sort): Always init `data' to avoid GCC warning.
Sat May 22 15:35:02 1993 Paul Eggert ([email protected])
* 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 ([email protected])
* 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 ([email protected])
* Version 2.3 released.
Fri Apr 23 17:18:44 1993 Paul Eggert ([email protected])
* 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 ([email protected])
* 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 ([email protected])
* diff.c (main): Use NULL in arg to compare_files.
Thu Feb 25 15:26:01 1993 Roland McGrath ([email protected])
* system.h: Declare memchr #if !HAVE_MEMCHR && !STDC_HEADERS,
not #if !HAVE_MEMCHR || !STDC_HEADERS.
Mon Feb 22 15:04:46 1993 Richard Stallman ([email protected])
* io.c (find_identical_ends): Move complicated arg outside GUESS_LINES.
Mon Feb 22 12:56:12 1993 Roland McGrath ([email protected])
* Makefile.in (.c.o): Add -I$(srcdir); put $(CFLAGS) last before $<.
Sat Feb 20 19:18:56 1993 Richard Stallman ([email protected])
* io.c (binary_file_p): Return zero if file size is zero.
Fri Feb 19 17:31:32 1993 Roland McGrath ([email protected])
* 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 ([email protected])
* Makefile.in (srcs): Remove limits.h.
Thu Feb 11 03:36:00 1993 Richard Stallman ([email protected])
* 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 ([email protected])
* limits.h: File deleted (should never have been there).
Tue Feb 9 03:53:22 1993 Richard Stallman ([email protected])
* Makefile.in (.c.o, diff3.o, sdiff.o): Put $(CFLAGS) last.
Wed Feb 3 15:42:10 1993 David J. MacKenzie ([email protected])
* system.h: Don't #define const; let configure do it.
Mon Feb 1 02:13:23 1993 Paul Eggert ([email protected])
* 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 ([email protected])
* 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 ([email protected])
* Makefile.in (.c.o): Add -I.
Fri Dec 18 14:08:20 1992 Paul Eggert ([email protected])
* configure.in: Add HAVE_FCNTL_H, since system.h uses it.
Tue Nov 24 10:06:48 1992 David J. MacKenzie ([email protected])
* Makefile.in: Note change from USG to HAVE_STRING_H.
Mon Nov 23 18:44:00 1992 Paul Eggert ([email protected])
* 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 ([email protected])
* 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 ([email protected])
* 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 ([email protected])
* 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 ([email protected])
* 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 ([email protected])
* io.c (binary_file_p): Consider empty file as non-binary.
Mon Oct 5 05:18:46 1992 Paul Eggert ([email protected])
* 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 ([email protected])
* 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 ([email protected])
* 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 ([email protected])
* 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 ([email protected])
* 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 ([email protected])
* 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 ([email protected])
* 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 ([email protected])
* 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 ([email protected])
* Makefile.in: Include binprefix in -DDIFF_PROGRAM.
Tue Sep 15 14:27:25 1992 David J. MacKenzie ([email protected])
* Version 2.0.
Sat Sep 12 01:31:19 1992 David J. MacKenzie ([email protected])
* 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 ([email protected])
* 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 ([email protected])
* configure.in: Add HAVE_MEMCHR.
* diff.h, util.c: Use it instead of MEMCHR_MISSING.
Sun Sep 6 07:25:49 1992 Paul Eggert ([email protected])
* 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 ([email protected])
* sdiff.c (xmalloc): Renamed from ck_malloc. Callers changed.
Thu Sep 3 15:28:59 1992 David J. MacKenzie ([email protected])
* 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 ([email protected])
* 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 ([email protected])
* diff.h, diff3.c, sdiff.c: Change FOO_MISSING macros to HAVE_FOO.
Thu Jun 25 16:59:47 1992 David J. MacKenzie ([email protected])
* diff.c: --reversed-ed -> --forward-ed.
Wed Feb 26 12:17:32 1992 Paul Eggert ([email protected])
* analyze.c, diff.c, diff.h, io.c: For -y, compare even if same file.
Fri Feb 14 22:46:38 1992 Richard Stallman ([email protected])
* io.c, diff3.c, analyze.c: Add extra parentheses.
Sun Feb 9 00:22:42 1992 Richard Stallman ([email protected])
* 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 ([email protected])
* 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 ([email protected])
* 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:
<PRINTF_SPEC>[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:
<PRINTF_SPEC>n
The line number, printed with <PRINTF_SPEC>.
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 <PRINTF_SPEC>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 [email protected]
/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 <alloca.h> 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 <vfork.h>. */
/* #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 <sys/types.h> 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 <sys/stat.h> do not work properly. */
/* #undef STAT_MACROS_BROKEN */
/* Define if you have the ANSI C header files. */
#define STDC_HEADERS 1
/* Define if <sys/wait.h> 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 <dirent.h> header file. */
#define HAVE_DIRENT_H 1
/* Define if you have the <fcntl.h> header file. */
#define HAVE_FCNTL_H 1
/* Define if you have the <limits.h> header file. */
#define HAVE_LIMITS_H 1
/* Define if you have the <ndir.h> header file. */
/* #undef HAVE_NDIR_H */
/* Define if you have the <stdlib.h> header file. */
#define HAVE_STDLIB_H 1
/* Define if you have the <string.h> header file. */
#define HAVE_STRING_H 1
/* Define if you have the <sys/dir.h> header file. */
/* #undef HAVE_SYS_DIR_H */
/* Define if you have the <sys/file.h> header file. */
#define HAVE_SYS_FILE_H 1
/* Define if you have the <sys/ndir.h> header file. */
/* #undef HAVE_SYS_NDIR_H */
/* Define if you have the <time.h> header file. */
#define HAVE_TIME_H 1
/* Define if you have the <unistd.h> 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 <signal.h>
#include "getopt.h"
#ifdef __FreeBSD__
#include <locale.h>
#include <fnmatch.h>
#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 <stdio.h>
#ifdef __FreeBSD__
#include <gnuregex.h>
#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" <[email protected]>
@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" <[email protected]>
@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 <stdio.h>
#include <signal.h>
#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 <[email protected]> and
Jim Blandy <[email protected]> for CVS use */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "system.h"
/* IGNORE(@ */
/* #include <ansidecl.h> */
/* @) */
#include <errno.h>
#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 [email protected]
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 <stdio.h>.
Ditto for AIX 3.2 and <stdlib.h>. */
#ifndef _NO_PROTO
#define _NO_PROTO
#endif
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifndef __STDC__
/* This is a separate conditional since some stdc systems
reject `defined (const)'. */
#ifndef const
#define const
#endif
#endif
#include <stdio.h>
/* 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 <stdlib.h>
#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 <string.h>
#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 <config.h>
#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 <stdio.h>
/* 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 <stdlib.h>
#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 <stdio.h>
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
</$objtype/mkfile
OFILES=
HFILES=\
system.h\
config.h\
TARG=diff diff3
DIFFO=\
analyze.$O\
cmpbuf.$O\
dir.$O\
io.$O\
util.$O\
context.$O\
ed.$O\
ifdef.$O\
normal.$O\
side.$O\
fnmatch.$O\
getopt.$O\
getopt1.$O\
regex.$O\
version.$O\
prepend_args.$O\
DIFF3O=\
getopt.$O\
getopt1.$O\
version.$O\
SDIFFO=\
getopt.$O\
getopt1.$O\
version.$O\
BIN=/$objtype/bin/ape
</sys/src/cmd/mkmany
CC=pcc -c
CFLAGS=-B -p -D_POSIX_SOURCE -DREGEX_MALLOC -I. \
-DHAVE_CONFIG_H -DDIFF_PROGRAM="/bin/ape/diff" \
$O.diff: $DIFFO
$O.diff3: $DIFF3O
/sys/src/ape/cmd/diff/normal.c 664 sys sys 1367613436 2214
/* Normal-format output routines for GNU DIFF.
Copyright (C) 1988, 1989, 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. */
#include "diff.h"
static void print_normal_hunk PARAMS((struct change *));
/* Print the edit-script SCRIPT as a normal diff.
INF points to an array of descriptions of the two files. */
void
print_normal_script (script)
struct change *script;
{
print_script (script, find_change, print_normal_hunk);
}
/* Print a hunk of a normal diff.
This is a contiguous portion of a complete edit script,
describing changes in consecutive lines. */
static void
print_normal_hunk (hunk)
struct change *hunk;
{
int first0, last0, first1, last1, deletes, inserts;
register int i;
/* Determine range of line numbers involved in each file. */
analyze_hunk (hunk, &first0, &last0, &first1, &last1, &deletes, &inserts);
if (!deletes && !inserts)
return;
begin_output ();
/* Print out the line number header for this hunk */
print_number_range (',', &files[0], first0, last0);
fprintf (outfile, "%c", change_letter (inserts, deletes));
print_number_range (',', &files[1], first1, last1);
fprintf (outfile, "\n");
/* Print the lines that the first file has. */
if (deletes)
for (i = first0; i <= last0; i++)
print_1_line ("<", &files[0].linbuf[i]);
if (inserts && deletes)
fprintf (outfile, "---\n");
/* Print the lines that the second file has. */
if (inserts)
for (i = first1; i <= last1; i++)
print_1_line (">", &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 <sys/types.h>
/* This is for other GNU distributions with internationalized messages. */
#if HAVE_LIBINTL_H || defined (_LIBC)
# include <libintl.h>
#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 <stdlib.h>
#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 <string.h>
#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 <strings.h>
#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 <ctype.h>
/* 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 <alloca.h>
#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 <stdio.h>
/* It is useful to test things that ``must'' be true when debugging. */
#include <assert.h>
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 <jump count> <upper bound>
set_number_at <succeed_n count> <lower bound>
succeed_n <after jump addr> <succeed_n count>
<body of loop>
jump_n <succeed_n addr> <jump count>
(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;
/* \<digit> has been turned into a `duplicate' command which is
followed by the numeric value of <digit> 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 <sys/types.h> must be included (by the caller) before
<regex.h>. */
#if !defined (_POSIX_C_SOURCE) && !defined (_POSIX_SOURCE) && defined (VMS)
/* VMS doesn't have `size_t' in <sys/types.h>, even though POSIX says it
should be there. */
#include <stddef.h>
#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 \<digit> matches <digit>.
If not set, then \<digit> 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 <sys/cdefs.h>
__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 <stdio.h>
#include <signal.h>
#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 <config.h>
#include <sys/types.h>
#include <sys/stat.h>
#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 <unistd.h>
#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 <time.h>
#else
#include <sys/time.h>
#endif
#if HAVE_FCNTL_H
#include <fcntl.h>
#else
#if HAVE_SYS_FILE_H
#include <sys/file.h>
#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 <sys/wait.h>
#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 <dirent.h>
# define NAMLEN(dirent) strlen((dirent)->d_name)
#else
# define dirent direct
# define NAMLEN(dirent) ((dirent)->d_namlen)
# if HAVE_SYS_NDIR_H
# include <sys/ndir.h>
# endif
# if HAVE_SYS_DIR_H
# include <sys/dir.h>
# endif
# if HAVE_NDIR_H
# include <ndir.h>
# endif
#endif
#if HAVE_VFORK_H
#include <vfork.h>
#endif
#if HAVE_STDLIB_H
#include <stdlib.h>
#else
VOID *malloc ();
VOID *realloc ();
#endif
#ifndef getenv
char *getenv ();
#endif
#if HAVE_LIMITS_H
#include <limits.h>
#endif
#ifndef INT_MAX
#define INT_MAX 2147483647
#endif
#ifndef CHAR_BIT
#define CHAR_BIT 8
#endif
#if STDC_HEADERS || HAVE_STRING_H
# include <string.h>
# 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.h>
/* CTYPE_DOMAIN (C) is nonzero if the unsigned char C can safely be given
as an argument to <ctype.h> 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 <errno.h>
#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 <config.h>
#endif
#if __STDC__
#define VOID void
#else
#define VOID char
#endif
#include <sys/types.h>
#if STDC_HEADERS
#include <stdlib.h>
#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 <stdlib.h>
#include <string.h>
#include <stdio.h>
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 <stdio.h>
/* 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
</sys/src/cmd/mkone
YFLAGS=-S
CFLAGS=-B -c
/sys/src/ape/cmd/expr/regexp.h 664 sys sys 1367613436 5958
#define CBRA 2
#define CCHR 4
#define CDOT 8
#define CCL 12
#define CDOL 20
#define CEOF 22
#define CKET 24
#define CBACK 36
#define STAR 01
#define RNGE 03
#define NBRA 9
#define PLACE(c) ep[c >> 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 <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#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 <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <time.h>
#include <dirent.h>
#include <limits.h>
#include <stdio.h>
#include <ctype.h>
#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( ptime<td || (ptime==0 && td==0) || lp->depp==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 && (ptime<tdep || (ptime==0 && tdep==0) ) )
{
ptime = (tdep>0 ? 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 <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
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<nproc; ++i)
if(! procstack[i].done)
++npending;
enbint(SIG_IGN);
if(dbgflag > 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; i<nproc; ++i, ++procp)
procp->done = YES;
proclive = nproc = 0;
}
return MAXPROC;
}
fatal("bad wait code");
}
for(i=0, procp=procstack; i<nproc; ++i, ++procp)
if(procp->pid == 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<tend ; )
{
*p++ = t;
while( *t )
++t;
do {
++t;
} while(t<tend && (*t==' ' || *t=='\t') );
}
*p = NULL;
/*TEMP for(p=argv; *p; ++p)printf("arg=%s\n", *p);*/
if((pid = fork()) == 0)
{
enbint(SIG_DFL);
doclose();
enbint(intrupt);
execvp(str, argv);
printf("\n");
fatal1("Cannot load %s",str);
}
free( (char *) argv);
return pid;
}
void
touch(int force, char *name)
{
struct stat stbuff;
char junk[1];
int fd;
if( stat(name,&stbuff) < 0)
if(force)
goto create;
else
{
fprintf(stderr, "touch: file %s does not exist.\n", name);
return;
}
if(stbuff.st_size == 0)
goto create;
if( (fd = open(name, O_RDWR)) < 0)
goto bad;
if( read(fd, junk, 1) < 1)
{
close(fd);
goto bad;
}
lseek(fd, 0L, SEEK_SET);
if( write(fd, junk, 1) < 1 )
{
close(fd);
goto bad;
}
close(fd);
return;
bad:
fprintf(stderr, "Cannot touch %s\n", name);
return;
create:
if( (fd = creat(name, 0666)) < 0)
goto bad;
close(fd);
}
/sys/src/ape/cmd/make/files.c 664 sys sys 1369259140 8649
/* POSIX DEPENDENT PROCEDURES */
#include "defs.h"
#include <sys/stat.h>
#include <ar.h>
#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( (p2<tempend) && (*p2++ = *p1++)!='\0')
;
if( amatch(nmtemp,filepat) )
{
concat(dirpref,nmtemp,fullname);
if( (q=srchname(fullname)) ==0)
q = makename(copys(fullname));
if(mkchain)
{
thisdbl = ALLOC(depblock);
thisdbl->nxtdepblock = 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 <pwd.h>
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 ; q<send && *p!='\0' && *p!=')' ; *q++ = *p++ )
;
if(p[0]==')' && p[1]!='\0') /* forbid stuff after the paren */
{
clarch();
return 0L;
}
while(q < send)
*q++ = pad;
while(getarch())
{
if( !strncmp(arfname, s, nc))
{
clarch();
/*TEMP fprintf(stderr, "found archive member %14s, time=%d\n", s, arfdate); */
return arfdate;
}
}
clarch();
return 0L;
}
static void
clarch(void)
{
fclose( arfd );
}
static int
openarch(char *f)
{
char magic[SARMAG];
int word;
struct stat buf;
nameblkp p;
stat(f, &buf);
arlen = buf.st_size;
arfd = fopen(f, "r");
if(arfd == NULL)
return NO;
/* fatal1("cannot open %s", f); */
fread( (char *) &word, sizeof(word), 1, arfd);
fseek(arfd, 0L, 0);
fread(magic, SARMAG, 1, arfd);
arpos = SARMAG;
if( strncmp(magic, ARMAG, SARMAG) )
fatal1("%s is not an archive", f);
if( !(p = srchname(f)) )
p = makename( copys(f) );
p->isarch = 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( (p2<fileend) && (*p2++ = *p1++)!='\0')
;
if( ! srchname(fullname) )
makename(copys(fullname));
}
free(fullname);
ret:
cldir(dirp, dirused);
if(lastslash)
*lastslash = '/';
}
void
baddirs(void)
{
struct dirhd *od;
for(od = firstod; od; od = od->nxtdirhd)
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 <yshblock> SHELLINE, shlist, shellist
%type <ynameblock> NAME, namelist
%type <ydepblock> 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<yytextl ; *p++ = kc)
switch(kc = getc(fin))
{
case '\t':
if(p == yytext)
incom = YES;
break;
case ';':
incom = YES;
break;
case '#':
if(! incom)
kc = '\0';
break;
case '\n':
++yylineno;
if(p==yytext || p[-1]!='\\')
{
*p = '\0';
goto endloop;
}
p[-1] = ' ';
while( (kc=getc(fin))=='\t' || kc==' ' || kc=='\n')
if(kc == '\n')
++yylineno;
if(kc != EOF)
break;
case EOF:
*p = '\0';
if(ninclude > 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 <lS> 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 <ctyp.h>. 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, <D, <F, @D, @F.
2.71 Added include stack to input.
Added check for sccs makefiles: s.[Mm]akefile
2.72 Load environment into macro tables. Added Bradford's -e flag.
2.73 Pass changed environment macros out to commands.
2.74 Fixed limit on args in dosys.c.
Non-existent archives now treated as other non-existent files.
2.75 Fixed bug in rehash.
2.76 Fixed bug when pattern searching in non-existent directory
Fixed infinite loop when awaiting failed process
Now wait till all subjobs finish before returning
make, unless a subjob fails
2.77 Added -z option that always forces shell invocation
rather than direct fork-exec
2.78 Check for error (-1) returned from fork
*/
/sys/src/ape/cmd/make/main.c 664 sys sys 1369259553 7639
# include "defs.h"
/*
command make to update programs.
Posix Flags:
'e' use environment macros after rather than before makefiles
'f' the next argument is the name of the description file;
"makefile" is the default
'i' ignore error codes from the shell
'k' continue to update other targets that don't depend
on target if error occurs making a target
'n' don't issue, just print, commands
'p' print out a version of the input graph
'q' don't do anything, but check if object is up to date;
returns exit code 0 if up to date, 1 if not
'r' clear the builtin suffix list and don't use built-in rules
's' silent mode--don't print out commands
'S' stop after any command fails (default; opposite of -k)
't' touch (update time of) files but don't issue command
Nonposix Flags:
'd' print out debugging comments
'N' use % patterns instead of old suffix rules
'Pn' set process limit to n
'z' always use shell, never issue commands directly
*/
nameblkp mainname = NULL;
nameblkp firstname = NULL;
lineblkp sufflist = NULL;
struct varblock *firstvar = NULL;
struct pattern *firstpat = NULL;
struct dirhd *firstod = NULL;
wildp firstwild = NULL;
wildp lastwild = NULL;
nameblkp *hashtab;
int nhashed;
int hashsize;
int hashthresh;
int proclimit = PROCLIMIT;
int nproc = 0;
int proclive = 0;
struct process procstack[MAXPROC];
void (*sigivalue)(int);
void (*sigqvalue)(int);
int dbgflag = NO;
int prtrflag = NO;
int silflag = NO;
int noexflag = NO;
int keepgoing = NO;
int noruleflag = NO;
int touchflag = NO;
int questflag = NO;
int oldflag = YES;
int ndocoms = NO;
int ignerr = NO; /* default is to stop on error */
int forceshell = NO;
int okdel = YES;
int envlast = NO;
int inarglist = NO;
char **envpp = NULL;
extern char *dfltmacro[];
extern char *dfltpat[];
extern char *dfltsuff[];
extern char **environ;
char **linesptr;
char *prompt = "";
int nopdir = 0;
char funny[128];
static void loadenv(void);
static int isprecious(char *);
static int rddescf(char *);
static void rdarray(char **);
static void printdesc(int);
void
main(int argc, char **argv)
{
nameblkp p;
int i, j;
int descset, nfargs;
int nowait = NO;
time_t tjunk;
char c, *s, *mkflagp;
static char makeflags[30] = "-";
static char onechar[2] = "X";
descset = 0;
mkflagp = makeflags+1;
funny['\0'] = (META | TERMINAL);
for(s = "=|^();&<>*?[]:$`'\"\\\n" ; *s ; ++s)
funny[*s] |= META;
for(s = "\n\t :;&>|" ; *s ; ++s)
funny[*s] |= TERMINAL;
newhash(HASHSIZE);
inarglist = YES;
for(i=1; i<argc; ++i)
if(argv[i]!=0 && argv[i][0]!='-' && eqsign(argv[i]))
argv[i] = 0;
setvar("$", "$", NO);
inarglist = NO;
for(i=1; i<argc; ++i)
if(argv[i]!=0 && argv[i][0]=='-')
{
for(j=1 ; (c=argv[i][j])!='\0' ; ++j) switch(c)
{
case 'd':
++dbgflag;
*mkflagp++ = 'd';
break;
case 'e':
envlast = YES;
*mkflagp++ = 'e';
break;
case 'f':
if(i >= 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; i<argc; ++i)
if(s = argv[i])
{
if((p=srchname(s)) == NULL)
p = makename(s);
++nfargs;
if(i+1<argc && argv[i+1] != 0 && equal(argv[i+1], "&") )
{
++i;
nowait = YES;
}
else
nowait = NO;
doname(p, 0, &tjunk, nowait);
if(dbgflag) printdesc(YES);
}
/*
If no file arguments have been encountered, make the first
name encountered that doesn't start with a dot
*/
if(nfargs == 0)
if(mainname == 0)
fatal("No arguments or description file");
else {
doname(mainname, 0, &tjunk, NO);
if(dbgflag) printdesc(YES);
}
if(!nowait)
waitstack(0);
exit(0);
}
void
intrupt(int)
{
char *p;
if(okdel && !noexflag && !touchflag &&
(p = varptr("@")->varval) && 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( hp<endohash )
if(p = *hp++)
hashtab[hashloc(p->namep)] = 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<a) *p++ = *a0++;
*p = '\0';
return 1;
}
int *
ckalloc(int n)
{
int *p;
if( p = (int *) calloc(1,n) )
return p;
fatal("out of memory");
/* NOTREACHED */
return 0;
}
/* copy string a into b, substituting for arguments */
char *
subst(char *a, char *b)
{
static depth = 0;
char *s;
char vname[100];
struct varblock *vbp;
char closer;
if(++depth > 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
</sys/src/cmd/mkone
# stdio prevents turning on warnings -TVwc
CFLAGS=-Tc -DSHELLCOM'="/bin/sh"'
YFLAGS=-S
gram.c: y.tab.c
mv $prereq $target
nuke clean:V:
rm -f *.[$OS] [$OS].out y.tab.? y.debug y.output $TARG gram.c
/sys/src/ape/cmd/make-3.79 20000000775 bootes sys 1367862839 0
/sys/src/ape/cmd/make-3.79/tests 20000000775 bootes sys 1367862839 0
/sys/src/ape/cmd/make-3.79/tests/scripts 20000000755 bootes sys 1367862839 0
/sys/src/ape/cmd/make-3.79/tests/scripts/features 20000000755 bootes sys 1367862839 0
/sys/src/ape/cmd/make-3.79/tests/scripts/features/comments 644 bootes sys 1367613436 1251
$description = "The following test creates a makefile to test comments\n"
."and comment continuation to the next line using a \n"
."backslash within makefiles.";
$details = "To test comments within a makefile, a semi-colon was placed \n"
."after a comment was started. This should not be reported as\n"
."an error since it is within a comment. We then continue the \n"
."comment to the next line using a backslash. To test whether\n"
."the comment really continued, we place an echo command with some\n"
."text on the line which should never execute since it should be \n"
."within a comment\n";
open(MAKEFILE,"> $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 <<EOF;
\#Extra space at the end of the following file name
include $makefile2
all: ; \@echo There should be no errors for this makefile.
-include nonexistent.mk
-include nonexistent.mk
sinclude nonexistent.mk
sinclude nonexistent-2.mk
-include makeit.mk
sinclude makeit.mk
error: makeit.mk
EOF
close(MAKEFILE);
open(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 <<EOF;
objects = foo.o bar.o
foo.o : defs.h
bar.o : defs.h test.h
extradeps =
\$(objects) : config.h \$(extradeps)
\t\@echo EXTRA EXTRA
EOF
# END of Contents of MAKEFILE
close(MAKEFILE);
&touch("defs.h","test.h","config.h");
if ($vos)
{
$error_code = 3307;
}
else
{
$error_code = 512;
}
&run_make_with_options($makefile,
"extradeps=extra.h",
&get_logfile,
$error_code);
# Create the answer to what should be produced by this Makefile
$answer = "$make_name: *** No rule to make target `extra.h', needed by `foo.o'. Stop.\n";
&compare_output($answer,&get_logfile(1));
# TEST #2
# -------
&touch("extra.h");
&run_make_with_options($makefile,
"extradeps=extra.h",
&get_logfile,
0);
# Create the answer to what should be produced by this Makefile
$answer = "EXTRA EXTRA\n";
&compare_output($answer,&get_logfile(1));
unlink("defs.h","test.h","config.h","extra.h");
1;
/sys/src/ape/cmd/make-3.79/tests/scripts/features/mult_targets 644 bootes sys 1367613436 1295
$description = "The following test creates a makefile to test that a \n "
."rule with multiple targets is equivalent to writing \n"
."many rules, each with one target, and all identical aside\n"
."from that.";
$details = "A makefile is created with one rule and two targets. Make \n"
."is called twice, once for each target, and the output which \n"
."contains the target name with \$@ is looked at for the changes.\n"
."This test also tests the substitute function by replacing \n"
."the word output with nothing in the target name giving either\n"
."an output of \"I am little\" or \"I am big\"";
open(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 <<EOM;
all: ; \@echo 'running rules.'
$makefile $makefile2: $makefile_orig
\@echo 'rebuilding \$\@.'
\@echo >> \$\@
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 <<EOF;
foo = bletch null \@ garf
null :=
space = ' '
auto_var = udef space CC null $pathvar MAKE foo CFLAGS WHITE \@ <
av = \$(foreach var, \$(auto_var), \$(origin \$(var)) )
override WHITE := BLACK
for_var = \$(addsuffix .c,foo \$(null) \$(foo) \$(space) \$(av) )
fe = \$(foreach var2, \$(for_var),\$(subst .c,.o, \$(var2) ) )
all: auto for2
auto :
\t\@echo \$(av)
for2:
\t\@echo \$(fe)
EOF
close(MAKEFILE);
&run_make_with_options($makefile,
"-e WHITE=WHITE CFLAGS=",
&get_logfile);
# Create the answer to what should be produced by this Makefile
$answer = "undefined file default file environment default file command line override automatic automatic
foo.o bletch.o null.o @.o garf.o .o .o undefined.o file.o default.o file.o environment.o default.o file.o command.o line.o override.o automatic.o automatic.o\n";
&compare_output($answer,&get_logfile(1));
1;
/sys/src/ape/cmd/make-3.79/tests/scripts/functions/if 644 bootes sys 1367613436 844
# -*-perl-*-
$description = "Test the if function.\n";
$details = "Try various uses of if and ensure they all give the correct
results.\n";
open(MAKEFILE, "> $makefile");
print MAKEFILE <<EOMAKE;
NEQ = \$(subst \$1,,\$2)
all:
\t\@echo \$(if ,true,false)
\t\@echo \$(if ,true,)
\t\@echo \$(if ,true)
\t\@echo \$(if z,true,false)
\t\@echo \$(if z,true,\$(shell echo hi))
\t\@echo \$(if ,\$(shell echo hi),false)
\t\@echo \$(if \$(call NEQ,a,b),true,false)
\t\@echo \$(if \$(call NEQ,a,a),true,false)
\t\@echo \$(if z,true,fal,se) hi
\t\@echo \$(if ,true,fal,se)there
EOMAKE
close(MAKEFILE);
&run_make_with_options($makefile, "", &get_logfile);
$answer = "false\n\n\ntrue\ntrue\nfalse\ntrue\nfalse\ntrue hi\nfal,sethere\n";
&compare_output($answer, &get_logfile(1));
1;
/sys/src/ape/cmd/make-3.79/tests/scripts/functions/join 644 bootes sys 1367613436 1118
$description = "The following test creates a makefile to test the join "
."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 := \$(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 <<EOF;
foo := bletch garf
auto_var = udef CC $homevar MAKE foo CFLAGS WHITE \@
av = \$(foreach var, \$(auto_var), \$(origin \$(var)) )
override WHITE := BLACK
all: auto
\t\@echo \$(origin undefined)
\t\@echo \$(origin CC)
\t\@echo \$(origin $homevar)
\t\@echo \$(origin MAKE)
\t\@echo \$(origin foo)
\t\@echo \$(origin CFLAGS)
\t\@echo \$(origin WHITE)
\t\@echo \$(origin \@)
auto :
\t\@echo \$(av)
EOF
close(MAKEFILE);
&run_make_with_options($makefile,
"-e WHITE=WHITE CFLAGS=",
&get_logfile);
# Create the answer to what should be produced by this Makefile
$answer = "undefined default environment default file command line override automatic
undefined
default
environment
default
file
command line
override
automatic\n";
&compare_output($answer,&get_logfile(1));
1;
/sys/src/ape/cmd/make-3.79/tests/scripts/functions/sort 644 bootes sys 1367613436 2179
$description = "The following test creates a makefile to verify\n"
."the ability of make to sort lists of object. Sort\n"
."will also remove any duplicate entries. This will also\n"
."be tested.";
$details = "The make file is built with a list of object in a random order\n"
."and includes some duplicates. Make should sort all of the elements\n"
."remove all duplicates\n";
open(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 <<EOM;
print1: ;\@echo \$(wildcard example.*)
print2:
\t\@echo \$(wildcard example.?)
\t\@echo \$(wildcard example.[a-z0-9])
\t\@echo \$(wildcard example.[!A-Za-z_\\!])
clean:
\t$delete_command \$(wildcard example.*)
EOM
# END of Contents of MAKEFILE
close(MAKEFILE);
&touch("example.1");
&touch("example.two");
&touch("example.3");
&touch("example.for");
&touch("example._");
# TEST #1
# -------
$answer = "example.1 example.3 example._ example.for example.two\n";
&run_make_with_options($makefile,"print1",&get_logfile);
&compare_output($answer,&get_logfile(1));
# TEST #2
# -------
$answer = "example.1 example.3 example._\n"
."example.1 example.3\n"
."example.1 example.3\n";
&run_make_with_options($makefile,"print2",&get_logfile);
&compare_output($answer,&get_logfile(1));
# TEST #3
# -------
$answer = "$delete_command example.1 example.3 example._ example.for example.two";
if ($vos)
{
$answer .= " \n";
}
else
{
$answer .= "\n";
}
&run_make_with_options($makefile,"clean",&get_logfile);
&compare_output($answer,&get_logfile(1));
if ((-f "example.1")||(-f "example.two")||(-f "example.3")||(-f "example.for"))
{
$test_passed = 0;
}
1;
/sys/src/ape/cmd/make-3.79/tests/scripts/functions/word 644 bootes sys 1367613436 1986
# -*-perl-*-
$description = "Test the word, words, and wordlist functions.\n";
$details = "\
Produce a variable with a large number of words in it,
determine the number of words, and then read each one back.\n";
open(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 <<EOF;
VPATH = $workdir
edit: main.o kbd.o commands.o display.o \\
insert.o
\t\@echo cc -o edit main.o kbd.o commands.o display.o \\
insert.o
main.o : main.c defs.h
\t\@echo cc -c main.c
kbd.o : kbd.c defs.h command.h
\t\@echo cc -c kbd.c
commands.o : command.c defs.h command.h
\t\@echo cc -c commands.c
display.o : display.c defs.h buffer.h
\t\@echo cc -c display.c
insert.o : insert.c defs.h buffer.h
\t\@echo cc -c insert.c
EOF
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 main.c\ncc -c kbd.c\ncc -c commands.c\ncc -c display.c
cc -c insert.c\ncc -o edit main.o kbd.o commands.o display.o insert.o\n";
# COMPARE RESULTS
if (&compare_output($answer,&get_logfile(1))) {
unlink @files_to_touch;
}
1;
/sys/src/ape/cmd/make-3.79/tests/scripts/misc/general2 644 bootes sys 1367613436 1526
# -*-perl-*-
$description = "The following test creates a makefile to test the
simple functionality of make. It is the same as
general_test1 except that this one tests the
definition of a variable to hold the object filenames.";
open(MAKEFILE,"> $makefile");
# The contents of the Makefile ...
print MAKEFILE <<EOF;
VPATH = $workdir
objects = main.o kbd.o commands.o display.o insert.o
edit: \$(objects)
\t\@echo cc -o edit \$(objects)
main.o : main.c defs.h
\t\@echo cc -c main.c
kbd.o : kbd.c defs.h command.h
\t\@echo cc -c kbd.c
commands.o : command.c defs.h command.h
\t\@echo cc -c commands.c
display.o : display.c defs.h buffer.h
\t\@echo cc -c display.c
insert.o : insert.c defs.h buffer.h
\t\@echo cc -c insert.c
EOF
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 main.c\ncc -c kbd.c\ncc -c commands.c\ncc -c display.c
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/misc/general3 644 bootes sys 1367613436 909
# -*-perl-*-
$description = "\
This tests random features of the parser that need to be supported, and
which have either broken at some point in the past or seem likely to
break.";
open(MAKEFILE,"> $makefile");
# The contents of the Makefile ...
print MAKEFILE <<EOF;
\# We want to allow both empty commands _and_ commands that resolve to empty.
EMPTY =
.PHONY: all a1 a2 a3 a4
all: a1 a2 a3 a4
a1:;
a2:
\t
a3:;\$(EMPTY)
a4:
\t\$(EMPTY)
\# Non-empty lines that expand to nothing should also be ignored.
STR = \# Some spaces
TAB = \t \# A TAB and some spaces
\$(STR)
\$(STR) \$(TAB)
EOF
close(MAKEFILE);
&run_make_with_options($makefile,"",&get_logfile);
# Create the answer to what should be produced by this Makefile
$answer = "$make_name: Nothing to be done for `all'.\n";
&compare_output($answer,&get_logfile(1));
1;
/sys/src/ape/cmd/make-3.79/tests/scripts/misc/version 644 bootes sys 1367613436 391
$description = "The following test creates a makefile to ... \n";
$details = "Fill in Later\n";
open(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 <<EOF;
include $mf2
all:
\t\@echo There should be no errors for this makefile.
EOF
# END of Contents of MAKEFILE
close(MAKEFILE);
open(MAKEFILE,"> $makefile2");
print MAKEFILE <<EOF;
ANOTHER:
\t\@echo This is another included makefile
recurse:
\t\$(MAKE) ANOTHER -f $makefile
EOF
close(MAKEFILE);
&run_make_with_options($makefile,"-I $workdir all",&get_logfile);
# Create the answer to what should be produced by this Makefile
$answer = "There should be no errors for this makefile.\n";
&compare_output($answer,&get_logfile(1));
$answer = "This is another included makefile\n";
&run_make_with_options($makefile,"-I $workdir ANOTHER",&get_logfile);
&compare_output($answer,&get_logfile(1));
$answer = "$mkpath ANOTHER -f $makefile
${make_name}[1]: Entering directory `$pwd'
This is another included makefile
${make_name}[1]: Leaving directory `$pwd'\n";
&run_make_with_options($makefile,"-I $workdir recurse",&get_logfile);
&compare_output($answer,&get_logfile(1));
/sys/src/ape/cmd/make-3.79/tests/scripts/options/dash-e 644 bootes sys 1367613436 430
# -*-perl-*-
$description = "The following test creates a makefile to ...";
$details = "";
$ENV{GOOGLE} = 'boggle';
open(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 <<EOF;
VPATH = $workdir
edit: main.o kbd.o commands.o display.o
\t\@echo cc -o edit main.o kbd.o commands.o display.o
main.o : main.c defs.h
\t\@echo cc -c main.c
kbd.o : kbd.c defs.h command.h
\t\@echo cc -c kbd.c
commands.o : command.c defs.h command.h
\t\@echo cc -c commands.c
display.o : display.c defs.h buffer.h
\t\@echo cc -c display.c
EOF
# END of Contents of MAKEFILE
close(MAKEFILE);
@files_to_touch = ("$workdir${pathsep}main.c","$workdir${pathsep}defs.h",
"$workdir${pathsep}command.h",
"$workdir${pathsep}commands.c","$workdir${pathsep}display.c",
"$workdir${pathsep}buffer.h",
"$workdir${pathsep}command.c");
&touch(@files_to_touch);
if ($vos) {
$error_code = 3307;
}
else {
$error_code = 512;
}
&run_make_with_options($makefile, "-k", &get_logfile, $error_code);
# Create the answer to what should be produced by this Makefile
$answer = "cc -c main.c
$make_name: *** No rule to make target `kbd.c', needed by `kbd.o'.
cc -c commands.c
cc -c display.c
$make_name: Target `edit' not remade because of errors.\n";
# COMPARE RESULTS
&compare_output($answer, &get_logfile(1));
unlink(@files_to_touch) unless $keep;
# TEST 1: Make sure that top-level targets that depend on targets that
# previously failed to build, aren't attempted. Regression for PR/1634.
$makefile2 = &get_tmpfile;
open(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: [email protected] (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 ...
<FILL IN DESCRIPTION HERE> ";
$details = "<FILL IN DETAILS OF HOW YOU TEST WHAT YOU SAY YOU ARE TESTING>";
# 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 " <FILL IN THE CONTENTS OF THE MAKEFILE HERE> \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 <FILL IN OPTIONS HERE>), 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,
"<FILL IN OPTIONS HERE>",
&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 = "<INSERT ANSWER HERE>";
# 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 <<EOF;
M2 = m2
NDEF: ; \@echo RULE FROM MAKEFILE 2
EOF
close(MAKEFILE);
open(MAKEFILE,"> $makefile3");
print MAKEFILE <<EOF;
M3 = m3
NDEF3: ; \@echo RULE FROM MAKEFILE 3
EOF
close(MAKEFILE);
&run_make_with_options($makefile, "MAKEFILES='$makefile2 $makefile3'",
&get_logfile);
$answer = "DEFAULT RULE: M2=m2 M3=m3\n";
&compare_output($answer,&get_logfile(1));
1;
/sys/src/ape/cmd/make-3.79/tests/scripts/variables/MAKELEVEL 644 bootes sys 1367613436 657
# -*-perl-mode-*-
$description = "The following test creates a makefile to test
makelevels in Make. It prints \$(MAKELEVEL) and then
prints the environment variable MAKELEVEL";
open(MAKEFILE,"> $makefile");
# The Contents of the MAKEFILE ...
print MAKEFILE <<EOF;
SHELL = /bin/sh
all:
\t\@echo MAKELEVEL is \$(MAKELEVEL)
\techo \$\$MAKELEVEL
EOF
# END of Contents of MAKEFILE
close(MAKEFILE);
# RUN MAKE
&run_make_with_options($makefile,"",&get_logfile);
# SET ANSWER
$answer = "MAKELEVEL is 0\necho \$MAKELEVEL\n1\n";
# COMPARE RESULTS
&compare_output($answer,&get_logfile(1));
1;
/sys/src/ape/cmd/make-3.79/tests/scripts/variables/flavors 644 bootes sys 1367613436 1093
# -*-perl-*-
$description = "Test various flavors of make variable setting.";
$details = "";
open(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 <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#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 <stdlib.h>
#include <windows.h>
#include <limits.h>
#include <sys/types.h>
#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 <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#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 <stdlib.h>
#include <stdio.h>
#include <process.h> /* for msvc _beginthreadex, _endthreadex */
#include <windows.h>
#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 <windows.h>
#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
</sys/src/cmd/mkmany
CFLAGS=-TVwc -D_POSIX_SOURCE
all:V: $DIRS
install:V: install.dir install.rc
installall:V: installall.dir
clean:V: clean.dir
nuke:V: nuke.dir
%.dir:V:
for (i in $DIRS) @{
cd $i
echo '----'$i'----'
mk $stem
}
$DIRS:V:
for (i in $target) @{
cd $i
echo '----'$i'----'
mk all
}
cc.$O: cc.c
mk -f /sys/src/cmd/mkfile cc.$O
$O.cc: cc.$O
mk -f /sys/src/cmd/mkfile $O.cc
install.rc:V: $BIN/psh
$BIN/%: %.rc
cp -x $stem.rc $BIN/$stem
/sys/src/ape/cmd/patch 20000000775 sys sys 1371506136 0
/sys/src/ape/cmd/patch/COPYING 664 sys sys 1367613436 18007
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 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
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.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) 19yy <name of author>
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.
<signature of Ty Coon>, 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 <[email protected]>
* configure.in (VERSION): Version 2.5 released.
1997-07-21 Paul Eggert <[email protected]>
* 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 <[email protected]>
* 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 <[email protected]>
* 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 <[email protected]>
* 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 <[email protected]>
* configure.in (VERSION): Version 2.4 released.
* NEWS: Patch is now verbose when patches do not match exactly.
1997-06-17 Paul Eggert <[email protected]>
* 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 <[email protected]>
* 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 <[email protected]>
* 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 <quotearg.h>.
(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 <quotearg.h> 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 <[email protected]>
* 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 <partime.h> for TM_LOCAL_ZONE.
Don't include <time.h> 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 <[email protected]>
* 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 <[email protected]>
* 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 <[email protected]>
* 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 <[email protected]>
* 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 <[email protected]>
* 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 <[email protected]>
* 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 <[email protected]>
* 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 <[email protected]>
* 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 <errno.h> 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 <[email protected]>
* 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 <[email protected]>
* 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 <[email protected]>
* 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 (<time.h>, <maketime.h>): 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.
<limits.h>: 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 <[email protected]>
* 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 <[email protected]>
* 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 <[email protected]>
* 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 <[email protected]>
* 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 <[email protected]>
* 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 <[email protected]>
* 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.
(<stdlib.h>): 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 <[email protected]>
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 (<config.h>): Include only if HAVE_CONFIG_H.
(<argmatch.h>): Include.
(<string.h>): Include if HAVE_STRING_H, not if STDC_HEADERS.
(<strings.h>): Include if !HAVE_STRING_H.
(<unistd.h>): Do not include.
(<dirent.h>): Redo include as per current autoconf standards.
(<limits.h>): 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.
(<signal.h>): 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.
(<string.h>): Include if either STDC_HEADERS or HAVE_STRING_H.
(memcmp, memcpy): Define if !STDC_HEADERS && !HAVE_STRING_H
&& !HAVE_MEMCHR.
(<stdlib.h>): 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.
(<fcntl.h>): 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 (<signal.h>): Include here, not in common.h.
(vararg_start): New macro.
(va_dcl, va_start, va_arg, va_end): Define if neither <stdarg.h>
nor <varargs.h> 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 ([email protected])
* 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 ([email protected])
* 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 ([email protected])
* 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 <limits.h> 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 ([email protected])
* 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 ([email protected])
* 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 ([email protected])
* 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 ([email protected])
* 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 ([email protected])
* 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 ([email protected])
* 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 ([email protected])
* Makefile.in (installdirs): New target.
(install): Use it.
(Makefile, config.status, configure): New targets.
Wed Jul 7 13:25:40 1993 David J. MacKenzie ([email protected])
* patch.c (get_some_switches, longopts): Recognize --help
option, and call usage.
(usage): New function.
Fri Jun 25 07:49:45 1993 Paul Eggert ([email protected])
* 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 ([email protected])
* 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 ([email protected])
* 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 ([email protected])
* 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 ([email protected])
* 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 ([email protected])
* 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 ([email protected])
* Makefile.in (.c.o): Put $(CFLAGS) after other options.
(dist): Switch from .z to .gz.
Wed Jun 2 10:37:15 1993 Paul Eggert ([email protected])
* backupfile.c (find_backup_file_name): Initialize copy of
file name properly.
Mon May 31 21:55:21 1993 Paul Eggert ([email protected])
* patchlevel.h: Patch level 12g11.
* pch.c (p_Char): Renamed from p_char, which is a system type
in Tex XD88's <sys/types.h>.
* backupfile.c: Include "config.h" first, so that `const' is
treated consistently in system headers.
Mon May 31 16:06:23 1993 Paul Eggert ([email protected])
* 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 ([email protected])
* 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 ([email protected])
* 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 ([email protected])
* 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 ([email protected])
* 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 ([email protected])
* patch.c (get_some_switches): optc should be int, not char.
Tue Sep 15 00:36:46 1992 David J. MacKenzie ([email protected])
* patchlevel.h: PATCHLEVEL 12g8.
Mon Sep 14 22:01:23 1992 David J. MacKenzie ([email protected])
* 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 ([email protected])
* 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 ([email protected])
* 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 ([email protected])
* 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 ([email protected])
* util.c (makedirs): Only make the directories that don't exist.
From [email protected] (Chip Salzenberg).
Wed Jul 8 01:20:56 1992 David J. MacKenzie ([email protected])
* patch.c (main): Open ofp after checking for ed script.
Close ofp and rejfp before trying plan B.
From [email protected] (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 ([email protected])
* 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 ([email protected]).
* inp.c, util.c, patch.c: Don't bother checking for stat() > 0.
Mon Jul 6 13:01:52 1992 David J. MacKenzie ([email protected])
* 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 ([email protected])
* 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 [email protected] (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 ([email protected])
* util.c (fetchname): Accept whitespace between `Index:' and filename.
Also plug a small memory leak for diffs against /dev/null.
From [email protected] (Paul Eggert).
* common.h: Don't define TRUE and FALSE if already defined.
From [email protected] (Poul-Henning Kamp).
Wed Apr 29 10:19:33 1992 David J. MacKenzie ([email protected])
* 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 ([email protected])
* 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 [email protected].
* 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 [email protected] (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
[email protected] (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 [email protected] (Kevin Braunsdorf).
1990-05-01 Wayne Davison <[email protected]>
* 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 <sdcrdcf!lwall>
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 <sdcrdcf!lwall>
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 <sdcrdcf!lwall>
* pch.c: pch_swap didn't swap p_bfake and p_efake.
1987-02-16 Larry Wall <sdcrdcf!lwall>
* patch.c: Short replacement caused spurious "Out of sync" message.
1987-01-30 Larry Wall <sdcrdcf!lwall>
* 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 <sdcrdcf!lwall>
* pch.c: New-style context diffs caused double call to free().
1986-11-21 Larry Wall <sdcrdcf!lwall>
* patch.c: Fuzz factor caused offset of installed lines.
1986-11-14 Larry Wall <sdcrdcf!lwall>
* 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 <sdcrdcf!lwall>
* pch.c: New-style delete triggers spurious assertion error.
1986-10-29 Larry Wall <sdcrdcf!lwall>
* patch.c: Backwards search could terminate prematurely.
* pch.c: Could falsely report new-style context diff.
1986-09-17 Larry Wall <sdcrdcf!lwall>
* 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 <sdcrdcf!lwall>
* 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 <sdcrdcf!lwall>
* patch.c: Frozen.
* patch.man: Frozen.
1985-03-12 Larry Wall <sdcrdcf!lwall>
* 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 <sdcrdcf!lwall>
* patch.c: Made smarter about SCCS subdirectories.
1984-12-05 Larry Wall <sdcrdcf!lwall>
* patch.c: Added -l switch to do loose string comparison.
* patch.man: Added -l switch, and noted bistability bug.
1984-12-04 Larry Wall <sdcrdcf!lwall>
Branch for sdcrdcf changes.
* patch.c: Failed hunk count not reset on multiple patch file.
* patch.man: Baseline version.
1984-11-29 Larry Wall <sdcrdcf!lwall>
* 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 <sdcrdcf!lwall>
* 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_<version>
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..
[email protected] - 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
[email protected].
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 <utime.h>. */
#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 <[email protected]> and Paul Eggert */
#if HAVE_CONFIG_H
# include <config.h>
#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 <backupfile.h>
#if HAVE_LIMITS_H
# include <limits.h>
#endif
#ifndef _POSIX_NAME_MAX
#define _POSIX_NAME_MAX 14
#endif
#include <sys/types.h>
#if HAVE_STRING_H
# include <string.h>
#else
# include <strings.h>
#endif
#if HAVE_UNISTD_H
# include <unistd.h>
#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 <[email protected]> */
#if HAVE_CONFIG_H
# include <config.h>
#endif
#include <argmatch.h>
#include <sys/types.h>
#include <stdio.h>
#if HAVE_STRING_H
# include <string.h>
#else
# include <strings.h>
#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 <[email protected]>.
Some algorithms adapted from GNU Emacs. */
#if HAVE_CONFIG_H
# include <config.h>
#endif
#include <argmatch.h>
#include <backupfile.h>
#include <stdio.h>
#include <sys/types.h>
#if HAVE_STRING_H
# include <string.h>
#else
# include <strings.h>
#endif
#if HAVE_DIRENT_H
# include <dirent.h>
# 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 <sys/ndir.h>
# endif
# if HAVE_SYS_DIR_H
# include <sys/dir.h>
# endif
# if HAVE_NDIR_H
# include <ndir.h>
# 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 <stdlib.h>
#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 <limits.h>
#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 <config.h>
#endif
#include <backupfile.h>
#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 <config.h>
#include <assert.h>
#include <stdio.h>
#include <sys/types.h>
#include <time.h>
#include <sys/stat.h>
#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 <limits.h>
#endif
#ifndef INT_MAX
#define INT_MAX 2147483647
#endif
#ifndef LONG_MIN
#define LONG_MIN (-1-2147483647L)
#endif
#include <ctype.h>
/* CTYPE_DOMAIN (C) is nonzero if the unsigned char C can safely be given
as an argument to <ctype.h> 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 <errno.h>
#if !STDC_HEADERS && !defined errno
extern int errno;
#endif
#if STDC_HEADERS || HAVE_STRING_H
# include <string.h>
#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 <stdlib.h>
#else
long atol ();
char *getenv ();
GENERIC_OBJECT *malloc ();
GENERIC_OBJECT *realloc ();
#endif
#if HAVE_UNISTD_H
# include <unistd.h>
#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 <fcntl.h>
#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 <alloca.h> 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 <vfork.h>. */
/* #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 <sys/types.h> 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 <sys/stat.h> do not work properly. */
/* #undef STAT_MACROS_BROKEN */
/* Define if you have the ANSI C header files. */
#define STDC_HEADERS 1
/* Define if <sys/wait.h> 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 <dirent.h> header file. */
#define HAVE_DIRENT_H 1
/* Define if you have the <fcntl.h> header file. */
#define HAVE_FCNTL_H 1
/* Define if you have the <limits.h> header file. */
#define HAVE_LIMITS_H 1
/* Define if you have the <ndir.h> header file. */
/* #undef HAVE_NDIR_H */
/* Define if you have the <stdlib.h> header file. */
#define HAVE_STDLIB_H 1
/* Define if you have the <string.h> header file. */
#define HAVE_STRING_H 1
/* Define if you have the <sys/dir.h> header file. */
/* #undef HAVE_SYS_DIR_H */
/* Define if you have the <sys/file.h> header file. */
#define HAVE_SYS_FILE_H 1
/* Define if you have the <sys/ndir.h> header file. */
/* #undef HAVE_SYS_NDIR_H */
/* Define if you have the <time.h> header file. */
#define HAVE_TIME_H 1
/* Define if you have the <unistd.h> 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 <alloca.h> 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 <vfork.h>. */
/* #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 <sys/types.h> 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 <sys/stat.h> do not work properly. */
/* #undef STAT_MACROS_BROKEN */
/* Define if you have the ANSI C header files. */
#define STDC_HEADERS 1
/* Define if <sys/wait.h> 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 <dirent.h> header file. */
#define HAVE_DIRENT_H 1
/* Define if you have the <fcntl.h> header file. */
#define HAVE_FCNTL_H 1
/* Define if you have the <limits.h> header file. */
#define HAVE_LIMITS_H 1
/* Define if you have the <ndir.h> header file. */
/* #undef HAVE_NDIR_H */
/* Define if you have the <stdlib.h> header file. */
#define HAVE_STDLIB_H 1
/* Define if you have the <string.h> header file. */
#define HAVE_STRING_H 1
/* Define if you have the <sys/dir.h> header file. */
/* #undef HAVE_SYS_DIR_H */
/* Define if you have the <sys/file.h> header file. */
#define HAVE_SYS_FILE_H 1
/* Define if you have the <sys/ndir.h> header file. */
/* #undef HAVE_SYS_NDIR_H */
/* Define if you have the <time.h> header file. */
#define HAVE_TIME_H 1
/* Define if you have the <unistd.h> 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 [email protected].
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 <<EOF
#line 638 "configure"
#include "confdefs.h"
main(){return(0);}
EOF
if { (eval echo configure:642: \"$ac_link\") 1>&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 <<EOF
#ifdef __GNUC__
yes;
#endif
EOF
if { ac_try='${CC-cc} -E conftest.c'; { (eval echo configure:676: \"$ac_try\") 1>&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 <<EOF
#line 734 "configure"
#include "confdefs.h"
#include <assert.h>
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 <<EOF
#line 751 "configure"
#include "confdefs.h"
#include <assert.h>
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 <errno.h> 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 <<EOF
#line 956 "configure"
#include "confdefs.h"
#ifdef _AIX
yes
#endif
EOF
if (eval "$ac_cpp conftest.$ac_ext") 2>&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
#line 984 "configure"
#include "confdefs.h"
#include <minix/config.h>
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 <<EOF
#line 1054 "configure"
#include "confdefs.h"
int main() {
/* Ultrix mips cc rejects this. */
typedef int charset[2]; const charset x;
/* SunOS 4.1.1 cc rejects this. */
char const *const *ccp;
char **p;
/* NEC SVR4.0.2 mips cc rejects this. */
struct point {int x, y;};
static struct point const zero = {0,0};
/* AIX XL C 1.02.0.0 rejects this.
It does not let you subtract one const X* pointer from another in an arm
of an if-expression whose if-part is not a constant expression */
const char *g = "string";
ccp = &g + (g ? g-g : 0);
/* HPUX 7.0 cc rejects these. */
++ccp;
p = (char**) ccp;
ccp = (char const *const *) p;
{ /* SCO 3.2v4 cc rejects this. */
char *t;
char const *s = 0 ? (char *) 0 : (char const *) 0;
*t++ = 0;
}
{ /* Someone thinks the Sun supposedly-ANSI compiler will reject this. */
int x[] = {25, 17};
const int *foo = &x[0];
++foo;
}
{ /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */
typedef const int *iptr;
iptr p = 0;
++p;
}
{ /* AIX XL C 1.02.0.0 rejects this saying
"k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */
struct s { int j; const int *ap[3]; };
struct s *b; b->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 <<EOF
#line 1134 "configure"
#include "confdefs.h"
#include <sys/types.h>
#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 <<EOF
#define $ac_tr_hdr 1
EOF
ac_header_dirent=$ac_hdr; break
else
echo "$ac_t""no" 1>&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 <<EOF
#line 1175 "configure"
#include "confdefs.h"
/* 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 opendir();
int main() {
opendir()
; return 0; }
EOF
if { (eval echo configure:1186: \"$ac_link\") 1>&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 <<EOF
#line 1216 "configure"
#include "confdefs.h"
/* 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 opendir();
int main() {
opendir()
; return 0; }
EOF
if { (eval echo configure:1227: \"$ac_link\") 1>&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 <<EOF
#line 1255 "configure"
#include "confdefs.h"
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <float.h>
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
#line 1280 "configure"
#include "confdefs.h"
#include <string.h>
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
#line 1298 "configure"
#include "confdefs.h"
#include <stdlib.h>
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 <<EOF
#line 1319 "configure"
#include "confdefs.h"
#include <ctype.h>
#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
#line 1362 "configure"
#include "confdefs.h"
#include <$ac_hdr>
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 <<EOF
#define $ac_tr_hdr 1
EOF
else
echo "$ac_t""no" 1>&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 <<EOF
#line 1400 "configure"
#include "confdefs.h"
#include <sys/types.h>
#if STDC_HEADERS
#include <stdlib.h>
#include <stddef.h>
#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 <<EOF
#line 1433 "configure"
#include "confdefs.h"
#include <sys/types.h>
#if STDC_HEADERS
#include <stdlib.h>
#include <stddef.h>
#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 <<EOF
#line 1466 "configure"
#include "confdefs.h"
#include <sys/types.h>
#include <signal.h>
#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 <<EOF
#define RETSIGTYPE $ac_cv_type_signal
EOF
echo $ac_n "checking for size_t""... $ac_c" 1>&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 <<EOF
#line 1507 "configure"
#include "confdefs.h"
#include <sys/types.h>
#if STDC_HEADERS
#include <stdlib.h>
#include <stddef.h>
#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 <<EOF
#line 1541 "configure"
#include "confdefs.h"
#include <sys/types.h>
#if HAVE_UTIME_H
#include <utime.h>
#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 <<EOF
#line 1582 "configure"
#include "confdefs.h"
#include <string.h>
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 <<EOF
#line 1614 "configure"
#include "confdefs.h"
/* System header to define __stub macros and hopefully few prototypes,
which can conflict with char getopt_long(); below. */
#include <assert.h>
/* 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 <<EOF
#line 1666 "configure"
#include "confdefs.h"
/* System header to define __stub macros and hopefully few prototypes,
which can conflict with char $ac_func(); below. */
#include <assert.h>
/* 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 <<EOF
#define $ac_tr_func 1
EOF
else
echo "$ac_t""no" 1>&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 <<EOF
#line 1721 "configure"
#include "confdefs.h"
/* System header to define __stub macros and hopefully few prototypes,
which can conflict with char $ac_func(); below. */
#include <assert.h>
/* 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 <<EOF
#define $ac_tr_func 1
EOF
else
echo "$ac_t""no" 1>&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 <<EOF
#line 1779 "configure"
#include "confdefs.h"
#include <sys/types.h>
#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 <<EOF
#line 1813 "configure"
#include "confdefs.h"
/* System header to define __stub macros and hopefully few prototypes,
which can conflict with char vprintf(); below. */
#include <assert.h>
/* 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 <<EOF
#line 1865 "configure"
#include "confdefs.h"
/* System header to define __stub macros and hopefully few prototypes,
which can conflict with char _doprnt(); below. */
#include <assert.h>
/* 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 <<EOF
#line 1964 "configure"
#include "confdefs.h"
#include <sys/types.h>
#if HAVE_DIRENT_H
# include <dirent.h>
#else
# define dirent direct
# if HAVE_SYS_NDIR_H
# include <sys/ndir.h>
# endif
# ifdef HAVE_SYS_DIR_H
# include <sys/dir.h>
# endif
# ifdef HAVE_NDIR_H
# include <ndir.h>
# 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 <<EOF
#! /bin/sh
# Generated automatically by configure.
# Run this file to recreate the current configuration.
# This directory was configured as follows,
# on host `(hostname || uname -n) 2>/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 <<EOF
# Protect against being on the right side of a sed subst in config.status.
sed 's/%@/@@/; s/@%/@@/; s/%g\$/@g/; /@g\$/s/[\\\\&%]/\\\\&/g;
s/@@/%@/; s/@@/@%/; s/@g\$/%g/' > 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 <<EOF
CONFIG_FILES=\${CONFIG_FILES-"Makefile"}
EOF
cat >> $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 <<EOF
CONFIG_HEADERS="config.h:config.hin"
EOF
cat >> $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 <<CEOF' >> $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 <<EOF
EOF
cat >> $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 <errno.h> 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 <sys/types.h>
#if HAVE_UTIME_H
#include <utime.h>
#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 <string.h>
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 <sys/types.h>
#if HAVE_DIRENT_H
# include <dirent.h>
#else
# define dirent direct
# if HAVE_SYS_NDIR_H
# include <sys/ndir.h>
# endif
# ifdef HAVE_SYS_DIR_H
# include <sys/dir.h>
# endif
# ifdef HAVE_NDIR_H
# include <ndir.h>
# 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 [email protected]
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 [email protected].
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 <stdio.h>.
Ditto for AIX 3.2 and <stdlib.h>. */
#ifndef _NO_PROTO
#define _NO_PROTO
#endif
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#if !defined (__STDC__) || !__STDC__
/* This is a separate conditional since some stdc systems
reject `defined (const)'. */
#ifndef const
#define const
#endif
#endif
#include <stdio.h>
#include <string.h>
/* 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 <gnu-versions.h>
#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 <stdlib.h>
#include <unistd.h>
#endif /* GNU C library. */
#ifdef VMS
#include <unixlib.h>
#if HAVE_STRING_H - 0
#include <string.h>
#endif
#endif
#if defined (WIN32) && !defined (__CYGWIN32__)
/* It's not Unix, really. See? Capital letters. */
#include <windows.h>
#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 <libintl.h>
# 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 <string.h>
#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 [email protected].
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 [email protected].
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 <config.h>
#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 <stdio.h>
/* 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 <gnu-versions.h>
#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 <stdlib.h>
#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 <stdio.h>
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 <common.h>
#include <backupfile.h>
#include <pch.h>
#include <util.h>
#undef XTERN
#define XTERN
#include <inp.h>
/* 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:
[email protected]
*/
#if has_conf_h
# include <conf.h>
#else
# if HAVE_CONFIG_H
# include <config.h>
# else
# ifndef __STDC__
# define const
# endif
# endif
/* MIPS RISCOS4.52 defines time_t in <sys/types.h> not <time.h>. */
# include <sys/types.h>
# if HAVE_LIMITS_H
# include <limits.h>
# endif
# ifndef LONG_MIN
# define LONG_MIN (-1-2147483647L)
# endif
# if STDC_HEADERS
# include <stdlib.h>
# endif
# include <time.h>
# ifdef __STDC__
# define P(x) x
# else
# define P(x) ()
# endif
#endif
#include <partime.h>
#include <maketime.h>
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 <stdio.h>
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:
[email protected]
*/
#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
</$objtype/mkfile
TARG=patch
OFILES=\
addext.$O\
argmatch.$O\
backupfile.$O\
basename.$O\
getopt.$O\
getopt1.$O\
inp.$O\
maketime.$O\
partime.$O\
patch.$O\
pch.$O\
quotearg.$O\
util.$O\
version.$O\
HFILES=\
backupfile.h\
common.h\
getopt.h\
maketime.h\
inp.h\
partime.h\
quotearg.h\
pch.h\
util.h\
version.h\
BIN=/$objtype/bin/ape
</sys/src/cmd/mkone
CC=pcc -c
CFLAGS=-B -p -D_POSIX_SOURCE \
-D_BSD_EXTENSION -I. -DHAVE_CONFIG_H \
-Ded_PROGRAM="/bin/ed"
/sys/src/ape/cmd/patch/partime.c 664 sys sys 1367613436 18935
/* 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:
[email protected]
*/
#if has_conf_h
# include <conf.h>
#else
# if HAVE_CONFIG_H
# include <config.h>
# else
# ifndef __STDC__
# define const
# endif
# endif
# if HAVE_LIMITS_H
# include <limits.h>
# endif
# ifndef LONG_MIN
# define LONG_MIN (-1-2147483647L)
# endif
# if STDC_HEADERS
# include <stdlib.h>
# endif
# include <time.h>
# ifdef __STDC__
# define P(x) x
# else
# define P(x) ()
# endif
#endif
#include <ctype.h>
#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 <partime.h>
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:
[email protected]
*/
#define TM_UNDEFINED (-1)
#define TM_DEFINED(x) (0 <= (x))
/* #include <limits.h> 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 <URL:ftp://ftp.isi.edu/in-notes/rfc934.txt> (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 <common.h>
#undef XTERN
#define XTERN extern
#include <argmatch.h>
#include <backupfile.h>
#include <getopt.h>
#include <inp.h>
#include <pch.h>
#include <util.h>
#include <version.h>
#if HAVE_UTIME_H
# include <utime.h>
#endif
/* Some nonstandard hosts don't declare this structure even in <utime.h>. */
#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 <[email protected]>.",
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 <common.h>
#include <backupfile.h>
#include <inp.h>
#include <util.h>
#undef XTERN
#define XTERN
#include <pch.h>
#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 <[email protected]> */
#if HAVE_CONFIG_H
# include <config.h>
#endif
#include <sys/types.h>
#include <quotearg.h>
/* 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 <common.h>
#include <backupfile.h>
#include <quotearg.h>
#include <version.h>
#undef XTERN
#define XTERN
#include <util.h>
#include <maketime.h>
#include <partime.h>
#include <signal.h>
#if !defined SIGCHLD && defined SIGCLD
#define SIGCHLD SIGCLD
#endif
#if ! HAVE_RAISE
# define raise(sig) kill (getpid (), sig)
#endif
#ifdef __STDC__
# include <stdarg.h>
# define vararg_start va_start
#else
# define vararg_start(ap,p) va_start (ap)
# if HAVE_VARARGS_H
# include <varargs.h>
# 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 <common.h>
#undef XTERN
#define XTERN
#include <patchlevel.h>
#include <version.h>
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 ([email protected])
#
# $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
[email protected]
/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 ([email protected])
*
*
* 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 ([email protected])
*
* 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 ([email protected])
*
* 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 [email protected], 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
[email protected]
.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 ([email protected])
*
* 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 ([email protected])
*
* 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 ([email protected])
*
* 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 ([email protected])
*
* 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 ([email protected])
*
* 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 <limits.h>
*
* DESCRIPTION
*
* We need to include <limits.h> 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
* <limits.h> 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 ([email protected])
*
* 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 <limits.h>
#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 ([email protected])
*
* 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 ([email protected])
*
* 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 ([email protected])
*
*
* 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
</sys/src/cmd/mkone
CFLAGS =-c -D_POSIX_SOURCE -B -DPATH_MAX=1024
$O.out: $OFILES $LIB
$LD $LDFLAGS -o $target $prereq -lv
/sys/src/ape/cmd/pax/namelist.c 664 sys sys 1367613436 12024
/* $Source: /u/mark/src/pax/RCS/namelist.c,v $
*
* $Revision: 1.6 $
*
* namelist.c - track filenames given as arguments to tar/cpio/pax
*
* DESCRIPTION
*
* Arguments may be regular expressions, therefore all agurments will
* be treated as if they were regular expressions, even if they are
* not.
*
* AUTHOR
*
* Mark H. Colburn, NAPS International ([email protected])
*
* 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: namelist.c,v $
* Revision 1.6 89/02/13 09:14:48 mark
* Fixed problem with directory errors
*
* Revision 1.5 89/02/12 12:14:00 mark
* Fixed misspellings
*
* Revision 1.4 89/02/12 11:25:19 mark
* Modifications to compile and link cleanly under USG
*
* Revision 1.3 89/02/12 10:40:23 mark
* Fixed casting problems
*
* Revision 1.2 89/02/12 10:04:57 mark
* 1.2 release fixes
*
* Revision 1.1 88/12/23 18:02:17 mark
* Initial revision
*
*/
#ifndef lint
static char *ident = "$Id: namelist.c,v 1.6 89/02/13 09:14:48 mark Exp $";
static char *copyright = "Copyright (c) 1989 Mark H. Colburn.\nAll rights reserved.\n";
#endif /* ! lint */
/* Headers */
#include "pax.h"
/* Type Definitions */
/*
* Structure for keeping track of filenames and lists thereof.
*/
struct nm_list {
struct nm_list *next;
short length; /* cached strlen(name) */
char found; /* A matching file has been found */
char firstch; /* First char is literally matched */
char re; /* regexp pattern for item */
char name[1]; /* name of file or rexexp */
};
struct dirinfo {
char dirname[PATH_MAX + 1]; /* name of directory */
OFFSET where; /* current location in directory */
struct dirinfo *next;
};
/* Static Variables */
static struct dirinfo *stack_head = (struct dirinfo *)NULL;
/* Function Prototypes */
#ifndef __STDC__
static void pushdir();
static struct dirinfo *popdir();
#else
static void pushdir(struct dirinfo *info);
static struct dirinfo *popdir(void);
#endif
/* Internal Identifiers */
static struct nm_list *namelast; /* Points to last name in list */
static struct nm_list *namelist; /* Points to first name in list */
/* addname - add a name to the namelist.
*
* DESCRIPTION
*
* Addname adds the name given to the name list. Memory for the
* namelist structure is dynamically allocated. If the space for
* the structure cannot be allocated, then the program will exit
* the an out of memory error message and a non-zero return code
* will be returned to the caller.
*
* PARAMETERS
*
* char *name - A pointer to the name to add to the list
*/
#ifdef __STDC__
void add_name(char *name)
#else
void add_name(name)
char *name; /* pointer to name */
#endif
{
int i; /* Length of string */
struct nm_list *p; /* Current struct pointer */
i = strlen(name);
p = (struct nm_list *) malloc((unsigned) (i + sizeof(struct nm_list)));
if (!p) {
fatal("cannot allocate memory for namelist entry\n");
}
p->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 ([email protected])
*
* 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 ([email protected])
*
* 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 ([email protected])
*
* 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 "<symlink>/.." 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 "<ls -l listing>" " == " linkname
.RE
.sp
and symbolic links (if supported) are shown as
.sp
.RS 1i
.IR "<ls -l listing>" " -> " 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
[email protected]
.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 ([email protected])
*
*
* 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 ([email protected])
*
* 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 <stdio.h>
#include <errno.h>
#include <signal.h>
#include <ctype.h>
#include <sys/types.h>
#ifndef _POSIX_SOURCE
#include <sys/ioctl.h>
#endif
#include <sys/stat.h>
#include "regexp.h"
#if defined(DIRENT) || defined(_POSIX_SOURCE)
# ifdef PAXDIR
# include "paxdir.h"
# else
# include <dirent.h>
# endif
#else
# ifdef hpux
# include <ndir.h>
# else
# ifdef XENIX_286
# include <sys/ndir.h>
# else XENIX_286
# include <sys/dir.h>
# endif XENIX_286
# endif /* hpux */
# define dirent direct
#endif
#ifdef _POSIX_SOURCE
#define major(x) 0
#define minor(x) 0
#else
#ifndef major
# include <sys/sysmacros.h>
#endif /* major */
#endif
#ifdef SYSTIME
# include <sys/time.h>
#else /* SYSTIME */
# include <time.h>
#endif /* SYSTIME */
#ifndef V7
# include <fcntl.h>
#endif
#ifdef XENIX
# include <sys/inode.h>
#endif
#ifdef XENIX_286
#include <sys/param.h>
#endif XENIX_286
#include <pwd.h>
#include <grp.h>
#ifndef XENIX_286
#ifndef _POSIX_SOURCE
#include <sys/file.h>
#endif
#endif /* XENIX_286 */
#ifdef _POSIX_SOURCE
#include <unistd.h>
#include <stdlib.h>
#endif
/* Defines */
#define STDIN 0 /* Standard input file descriptor */
#define STDOUT 1 /* Standard output file descriptor */
/*
* Open modes; there is no <fcntl.h> 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 <sys/errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "paxdir.h"
#ifdef BSD_SYSV
/*
<sys/_dir.h> -- 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: <sys/types.h>
*/
#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 <dirent.h> */
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 <sys/dir.h>
#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 <signal.h>
#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
/*
<dirent.h> -- definitions for SVR3 directory access routines
last edit: 25-Apr-1987 D A Gwyn
Prerequisite: <sys/types.h>
*/
#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 ([email protected])
* 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 ([email protected])
* 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 <string.h>
#ifndef _POSIX_SOURCE
# include <memory.h>
#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 ([email protected])
* Henry Spencer, University of Torronto ([email protected])
*
* 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
[email protected]
.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 ([email protected])
*
* 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 ([email protected])
*
* 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 ([email protected])
*
* 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 <errvalue>)" 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 ([email protected])
* John Gilmore (gnu@hoptoad)
* Rich Salz ([email protected])
*
*
* 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 ([email protected]),
who took over from Simon J. Gerraty ([email protected]) 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 [email protected] 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, <signal.h> 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 [email protected] (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, [email protected]
/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 <[email protected]>
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 <ctype.h>
#ifdef __CYGWIN__
#include <sys/cygwin.h>
#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 ::= <any thing>
*/
#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 <sys/resource.h>
#endif /* HAVE_SYS_RESOURCE_H */
#ifdef HAVE_ULIMIT_H
# include <ulimit.h>
#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 <sys/types.h> 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 <sys/wait.h> that is POSIX.1 compatible. */
#define HAVE_SYS_WAIT_H 1
/* Define if you have <unistd.h>. */
#define HAVE_UNISTD_H
/* Define if on MINIX. */
/* #define _MINIX 1 */
/* Define to `int' if <sys/types.h> doesn't define. */
/* #define mode_t short */
/* Define to `long' if <sys/types.h> doesn't define. */
/* #define off_t long */
/* Define to `int' if <sys/types.h> 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 <sys/stat.h> do not work properly. */
/* #undef STAT_MACROS_BROKEN */
/* Define if `sys_siglist' is declared by <signal.h>. */
/* #undef SYS_SIGLIST_DECLARED */
/* Define if you can safely include both <sys/time.h> and <time.h>. */
#define TIME_WITH_SYS_TIME
/* Define to `int' if <sys/types.h> 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 <sys/types.h> 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 <time.h> */
#define TIME_DECLARED
/* Define to `unsigned' if <signal.h> 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 <errno.h> */
#define SYS_ERRLIST_DECLARED
/* Define if sys_siglist[] is in the C library */
/* #undef HAVE_SYS_SIGLIST */
/* Define if you have a sane <termios.h> 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 <termio.h> 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 <sys/wait.h> */
#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 <dirent.h> header file. */
#define HAVE_DIRENT_H 1
/* Define if you have the <fcntl.h> header file. */
#define HAVE_FCNTL_H 1
/* Define if you have the <limits.h> header file. */
#define HAVE_LIMITS_H 1
/* Define if you have the <memory.h> header file. */
/* #define HAVE_MEMORY_H 1 */
/* Define if you have the <ndir.h> header file. */
/* #undef HAVE_NDIR_H */
/* Define if you have the <paths.h> header file. */
/* #define HAVE_PATHS_H 1 */
/* Define if you have the <stddef.h> header file. */
#define HAVE_STDDEF_H 1
/* Define if you have the <stdlib.h> header file. */
#define HAVE_STDLIB_H 1
/* Define if you have the <string.h> header file. */
#define HAVE_STRING_H 1
/* Define if you have the <sys/dir.h> header file. */
/* #undef HAVE_SYS_DIR_H */
/* Define if you have the <sys/ndir.h> header file. */
/* #undef HAVE_SYS_NDIR_H */
/* Define if you have the <sys/param.h> header file. */
#define HAVE_SYS_PARAM_H 1
/* Define if you have the <sys/resource.h> header file. */
#define HAVE_SYS_RESOURCE_H 1
/* Define if you have the <sys/time.h> header file. */
#define HAVE_SYS_TIME_H 1
/* Define if you have the <sys/wait.h> header file. */
#define HAVE_SYS_WAIT_H 1
/* Define if you have the <ulimit.h> header file. */
/* #define HAVE_ULIMIT_H 1 */
/* Define if you have the <values.h> 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 <signal.h>
#include <setjmp.h>
/* 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 <pwd.h>
#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 && /* $(<file) */
*t->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 <ctype.h>
#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 <ctype.h>
/* 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 [email protected]
* 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 <sys/file.h>
# include <sys/mman.h>
/*
* 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 <sjg@zen>
* 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
* <command byte><command number(4 bytes)><bytes><null>
*/
# 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 <ctype.h>
#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 ([email protected]) and hacked again by
* Michael Rendell ([email protected])
*
* 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 <dirent.h>
# define NLENGTH(dirent) (strlen(dirent->d_name))
#else
# define dirent direct
# define NLENGTH(dirent) (dirent->d_namlen)
# ifdef HAVE_SYS_NDIR_H
# include <sys/ndir.h>
# endif /* HAVE_SYS_NDIR_H */
# ifdef HAVE_SYS_DIR_H
# include <sys/dir.h>
# endif /* HAVE_SYSDIR_H */
# ifdef HAVE_NDIR_H
# include <ndir.h>
# 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 <values.h>
#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 <sys/types.h> already included */
#include <sys/stat.h>
#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 <sys/time.h>
# include <time.h>
#else /* TIME_WITH_SYS_TIME */
# ifdef HAVE_SYS_TIME_H
# include <sys/time.h>
# else
# include <time.h>
# 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 <sys/times.h>
#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 <sys/wait.h>
#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 <ctype.h>
/* 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 "<<word" text into temp file
*/
static void
readhere(iop)
struct ioword *iop;
{
register int c;
char *volatile eof;
char *eofp;
int skiptabs;
XString xs;
char *xp;
int xpos;
eof = evalstr(iop->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 = "<stdin>";
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 <ctype.h> /* for FILECHCONV */
#ifdef HAVE_LIMITS_H
# include <limits.h>
#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 <sys/param.h>
#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
</$objtype/mkfile
BIN=/$objtype/bin/ape
TARG=sh
OFILES=\
alloc.$O\
c_ksh.$O\
c_sh.$O\
c_test.$O\
c_ulimit.$O\
eval.$O\
exec.$O\
expr.$O\
history.$O\
io.$O\
jobs.$O\
lex.$O\
mail.$O\
main.$O\
misc.$O\
path.$O\
shf.$O\
syn.$O\
table.$O\
trap.$O\
tree.$O\
tty.$O\
var.$O\
version.$O
# Autolib doesn't get this right on the alpha, and I don't understand why. -rsc
LIB=/$objtype/lib/ape/libap.a /$objtype/lib/ape/libbsd.a
</sys/src/cmd/mkone
CC=pcc
CFLAGS=-I. -DHAVE_CONFIG_H -c
$O.out: $OFILES $LIB
$LD -o $target $prereq
/sys/src/ape/cmd/pdksh/path.c 664 sys sys 1367613437 7369
#include "sh.h"
#include "ksh_stat.h"
/*
* Contains a routine to search a : separated list of
* paths (a la CDPATH) and make appropiate file names.
* Also contains a routine to simplify .'s and ..'s out of
* a path name.
*
* Larry Bouzane ([email protected])
*/
/*
* $Log: path.c,v $
* Revision 1.2 1994/05/19 18:32:40 michael
* Merge complete, stdio replaced, various fixes. (pre autoconf)
*
* Revision 1.1 1994/04/06 13:14:03 michael
* Initial revision
*
* Revision 4.2 1990/12/06 18:05:24 larry
* Updated test code to reflect parameter change.
* Fixed problem with /a/./.dir being simplified to /a and not /a/.dir due
* to *(cur+2) == *f test instead of the correct cur+2 == f
*
* Revision 4.1 90/10/29 14:42:19 larry
* base MUN version
*
* Revision 3.1.0.4 89/02/16 20:28:36 larry
* Forgot to set *pathlist to NULL when last changed make_path().
*
* Revision 3.1.0.3 89/02/13 20:29:55 larry
* Fixed up cd so that it knew when a node from CDPATH was used and would
* print a message only when really necessary.
*
* Revision 3.1.0.2 89/02/13 17:51:22 larry
* Merged with Eric Gisin's version.
*
* Revision 3.1.0.1 89/02/13 17:50:58 larry
* *** empty log message ***
*
* Revision 3.1 89/02/13 17:49:28 larry
* *** empty log message ***
*
*/
#ifdef S_ISLNK
static char *do_phys_path ARGS((XString *xsp, char *xp, const char *path));
#endif /* S_ISLNK */
/*
* Makes a filename into result using the following algorithm.
* - make result NULL
* - if file starts with '/', append file to result & set cdpathp to NULL
* - if file starts with ./ or ../ append cwd and file to result
* and set cdpathp to NULL
* - if the first element of cdpathp doesnt start with a '/' xx or '.' xx
* then cwd is appended to result.
* - the first element of cdpathp is appended to result
* - file is appended to result
* - cdpathp is set to the start of the next element in cdpathp (or NULL
* if there are no more elements.
* The return value indicates whether a non-null element from cdpathp
* was appened to result.
*/
int
make_path(cwd, file, cdpathp, xsp, phys_pathp)
const char *cwd;
const char *file;
char **cdpathp; /* & of : separated list */
XString *xsp;
int *phys_pathp;
{
int rval = 0;
int use_cdpath = 1;
char *plist;
int len;
int plen = 0;
char *xp = Xstring(*xsp, xp);
if (!file)
file = null;
if (!ISRELPATH(file)) {
*phys_pathp = 0;
use_cdpath = 0;
} else {
if (file[0] == '.') {
char c = file[1];
if (c == '.')
c = file[2];
if (ISDIRSEP(c) || c == '\0')
use_cdpath = 0;
}
plist = *cdpathp;
if (!plist)
use_cdpath = 0;
else if (use_cdpath) {
char *pend;
for (pend = plist; *pend && *pend != PATHSEP; pend++)
;
plen = pend - plist;
*cdpathp = *pend ? ++pend : (char *) 0;
}
if ((use_cdpath == 0 || !plen || ISRELPATH(plist))
&& (cwd && *cwd))
{
len = strlen(cwd);
XcheckN(*xsp, xp, len);
memcpy(xp, cwd, len);
xp += len;
if (!ISDIRSEP(cwd[len - 1]))
Xput(*xsp, xp, DIRSEP);
}
*phys_pathp = Xlength(*xsp, xp);
if (use_cdpath && plen) {
XcheckN(*xsp, xp, plen);
memcpy(xp, plist, plen);
xp += plen;
if (!ISDIRSEP(plist[plen - 1]))
Xput(*xsp, xp, DIRSEP);
rval = 1;
}
}
len = strlen(file) + 1;
XcheckN(*xsp, xp, len);
memcpy(xp, file, len);
if (!use_cdpath)
*cdpathp = (char *) 0;
return rval;
}
/*
* Simplify pathnames containing "." and ".." entries.
* ie, simplify_path("/a/b/c/./../d/..") returns "/a/b"
*/
void
simplify_path(path)
char *path;
{
char *cur;
char *t;
int isrooted;
char *very_start = path;
char *start;
if (!*path)
return;
if ((isrooted = ISROOTEDPATH(path)))
very_start++;
#if defined (OS2) || defined (__CYGWIN__)
if (path[0] && path[1] == ':') /* skip a: */
very_start += 2;
#endif /* OS2 || __CYGWIN__ */
/* Before After
* /foo/ /foo
* /foo/../../bar /bar
* /foo/./blah/.. /foo
* . .
* .. ..
* ./foo foo
* foo/../../../bar ../../bar
* OS2 and CYGWIN:
* a:/foo/../.. a:/
* a:. a:
* a:.. a:..
* a:foo/../../blah a:../blah
*/
#ifdef __CYGWIN__
/* preserve leading double-slash on pathnames (for UNC paths) */
if (path[0] && ISDIRSEP(path[0]) && path[1] && ISDIRSEP(path[1]))
very_start++;
#endif /* __CYGWIN__ */
for (cur = t = start = very_start; ; ) {
/* treat multiple '/'s as one '/' */
while (ISDIRSEP(*t))
t++;
if (*t == '\0') {
if (cur == path)
/* convert empty path to dot */
*cur++ = '.';
*cur = '\0';
break;
}
if (t[0] == '.') {
if (!t[1] || ISDIRSEP(t[1])) {
t += 1;
continue;
} else if (t[1] == '.' && (!t[2] || ISDIRSEP(t[2]))) {
if (!isrooted && cur == start) {
if (cur != very_start)
*cur++ = DIRSEP;
*cur++ = '.';
*cur++ = '.';
start = cur;
} else if (cur != start)
while (--cur > 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 <stdio.h>
#include <sys/types.h>
#include <setjmp.h>
#ifdef HAVE_STDDEF_H
# include <stddef.h>
#endif
#ifdef HAVE_STDLIB_H
# include <stdlib.h>
#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 <unistd.h>
#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 <string.h>
#else
# include <strings.h>
# 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 <memory.h>
#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 <stdarg.h>
# define SH_VA_START(va, argn) va_start(va, argn)
#else
# include <varargs.h>
# define SH_VA_START(va, argn) va_start(va)
#endif /* HAVE_PROTOTYPES */
#include <errno.h>
extern int errno;
#ifdef HAVE_FCNTL_H
# include <fcntl.h>
#else
# include <sys/file.h>
#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 <limits.h>
#endif /* HAVE_LIMITS_H */
#include <signal.h>
#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 <paths.h>
#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 <math.h>
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 <math.h>
* 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, "<botch>");
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 <termios.h>
# 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 <sys/ioctl.h>
# endif /* !(sun && !__svr4__) */
# endif /* SYS_IOCTL_WITH_TERMIOS */
typedef struct termios TTY_state;
#else
# ifdef HAVE_TERMIO_H
# include <termio.h>
# ifdef SYS_IOCTL_WITH_TERMIO
# include <sys/ioctl.h> /* 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 <sgtty.h>
# define TIOCSETN TIOCSETP
# else
# include <sys/ioctl.h>
# 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 <ctype.h>
/*
* 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) /* <sjg> 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
</sys/src/cmd/mkone
CFLAGS=-Tc -D_POSIX_SOURCE
/sys/src/ape/cmd/sed/sed.h 664 sys sys 1367613437 2820
#
/*
* sed -- stream editor
*
*
*/
#define CBRA 1
#define CCHR 2
#define CDOT 4
#define CCL 6
#define CNL 8
#define CDOL 10
#define CEOF 11
#define CKET 12
#define CNULL 13
#define CLNUM 14
#define CEND 16
#define CDONT 17
#define CBACK 18
#define STAR 01
#define NLINES 256
#define DEPTH 20
#define PTRSIZE 1024
#define RESIZE 20000
#define ABUFSIZE 20
#define LBSIZE 4000
#define LABSIZE 50
#define NBRA 9
typedef unsigned char uchar;
FILE *fin;
union reptr *abuf[ABUFSIZE];
union reptr **aptr;
uchar *lastre;
uchar ibuf[512];
uchar *cbp;
uchar *ebp;
uchar genbuf[LBSIZE];
uchar *loc1;
uchar *loc2;
uchar *locs;
uchar seof;
uchar *reend;
uchar *lbend;
uchar *hend;
uchar *lcomend;
union reptr *ptrend;
int eflag;
int dolflag;
int sflag;
int jflag;
int numbra;
int delflag;
long lnum;
uchar linebuf[LBSIZE+1];
uchar holdsp[LBSIZE+1];
uchar *spend;
uchar *hspend;
int nflag;
int gflag;
uchar *braelist[NBRA];
uchar *braslist[NBRA];
long tlno[NLINES];
int nlno;
#define MAXFILES 120
char fname[MAXFILES][40];
FILE *fcode[MAXFILES];
int nfiles;
#define ACOM 01
#define BCOM 020
#define CCOM 02
#define CDCOM 025
#define CNCOM 022
#define COCOM 017
#define CPCOM 023
#define DCOM 03
#define ECOM 015
#define EQCOM 013
#define FCOM 016
#define GCOM 027
#define CGCOM 030
#define HCOM 031
#define CHCOM 032
#define ICOM 04
#define LCOM 05
#define NCOM 012
#define PCOM 010
#define QCOM 011
#define RCOM 06
#define SCOM 07
#define TCOM 021
#define WCOM 014
#define CWCOM 024
#define YCOM 026
#define XCOM 033
uchar *cp;
uchar *reend;
uchar *lbend;
union reptr {
struct reptr1 {
uchar *ad1;
uchar *ad2;
uchar *re1;
uchar *rhs;
FILE *fcode;
uchar command;
uchar gfl;
uchar pfl;
uchar inar;
uchar negfl;
} r1;
struct reptr2 {
uchar *ad1;
uchar *ad2;
union reptr *lb1;
uchar *rhs;
FILE *fcode;
uchar command;
uchar gfl;
uchar pfl;
uchar inar;
uchar negfl;
} r2;
} ptrspace[PTRSIZE], *rep;
uchar respace[RESIZE];
struct label {
uchar asc[9];
union reptr *chain;
union reptr *address;
} ltab[LABSIZE];
struct label *lab;
struct label *labend;
int f;
int depth;
int eargc;
uchar **eargv;
uchar *address(uchar *);
int advance(uchar *, uchar *);
void arout(void);
extern uchar bittab[];
uchar bad;
uchar *badp;
int cmp(uchar *, uchar *);
union reptr **cmpend[DEPTH];
void command(union reptr *);
uchar compfl;
uchar *compile(uchar *);
uchar *compsub(uchar *);
void dechain(void);
int depth;
void dosub(uchar *);
int ecmp(uchar *, uchar *, int);
void execute(uchar *);
void fcomp(void);
uchar *gline(uchar *);
uchar *lformat(int, uchar *);
int match(uchar *, int);
union reptr *pending;
uchar *place(uchar *, uchar *, uchar *);
int rline(uchar *);
struct label *search(struct label *);
int substitute(union reptr *);
uchar *text(uchar *);
uchar *ycomp(uchar *);
/sys/src/ape/cmd/sed/sed0.c 664 sys sys 1367613437 16248
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#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 <stdlib.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#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 <stdio.h>
#include <stdlib.h>
#include <sys/utsname.h>
#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 <lib9.h>
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 <lib9.h>
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 <sys/types.h>
#include <lock.h>
#include <qlock.h>
#include <lib9.h>
#include <stdlib.h>
#include <string.h>
#include <bsd.h>
#include <unistd.h>
#include <fcntl.h>
#include <assert.h>
#include <utf.h>
#include <fmt.h>
#include <signal.h>
#include <time.h>
#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 <u.h> */
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\
</sys/src/cmd/mksyslib
CFLAGS=-TVwc $CFLAGS -D_POSIX_SOURCE -D_PLAN9_SOURCE
%.$O: $objtype/%.s
$AS $prereq
%.$O: $objtype/%.c
$CC $CFLAGS $prereq
sysfatal.$O: ../../../libc/9sys/sysfatal.c
$CC $CFLAGS -I. ../../../libc/9sys/sysfatal.c
tokenize.$O: ../../../libc/port/tokenize.c
$CC $CFLAGS -I. ../../../libc/port/tokenize.c
truerand.$O: ../../../libc/9sys/truerand.c
$CC $CFLAGS -I. ../../../libc/9sys/truerand.c
u16.$O: ../../../libc/port/u16.c
$CC $CFLAGS -I. ../../../libc/port/u16.c
u32.$O: ../../../libc/port/u32.c
$CC $CFLAGS -I. ../../../libc/port/u32.c
u64.$O: ../../../libc/port/u64.c
$CC $CFLAGS -I. ../../../libc/port/u64.c
crypt.$O: ../../../libc/port/crypt.c
$CC $CFLAGS -I. ../../../libc/port/crypt.c
/sys/src/ape/lib/9/mount.c 664 sys sys 1367613437 176
#include <lib9.h>
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 <u.h>
#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 <lib9.h>
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 <lib9.h>
extern int _RFORK(int);
int
rfork(int flags)
{
return _RFORK(flags);
}
/sys/src/ape/lib/9/segattach.c 664 sys sys 1368232624 194
#include <lib9.h>
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 <lib9.h>
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 <lib9.h>
extern int _SEGDETACH(void *);
int
segdetach(void *addr)
{
return _SEGDETACH(addr);
}
/sys/src/ape/lib/9/segflush.c 664 sys sys 1367613437 138
#include <lib9.h>
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 <lib9.h>
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 <lib9.h>
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 <u.h>
#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 <lock.h>
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\
</sys/src/cmd/mksyslib
CFLAGS=-c -D_POSIX_SOURCE -D_PLAN9_SOURCE
/sys/src/ape/lib/ap/386/notetramp.c 664 sys sys 1367613437 1346
#include "../plan9/lib.h"
#include "../plan9/sys9.h"
#include <signal.h>
#include <setjmp.h>
/* 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 <lock.h>
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\
</sys/src/cmd/mksyslib
CFLAGS=-c -D_POSIX_SOURCE -D_PLAN9_SOURCE
/sys/src/ape/lib/ap/68020/notetramp.c 664 sys sys 1367613437 1346
#include "../plan9/lib.h"
#include "../plan9/sys9.h"
#include <signal.h>
#include <setjmp.h>
/* 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<quo; i++)
* den <<= 1;
* quo = 0;
* for(; i>=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<quo; i++)
* den <<= 1;
* quo = 0;
* for(; i>=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 <lock.h>
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 <string.h>
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\
</sys/src/cmd/mksyslib
CFLAGS=-c -D_POSIX_SOURCE -D_PLAN9_SOURCE
/sys/src/ape/lib/ap/alpha/notetramp.c 664 sys sys 1367613437 1342
#include "../plan9/lib.h"
#include "../plan9/sys9.h"
#include <signal.h>
#include <setjmp.h>
/* 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 <lock.h>
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\
</sys/src/cmd/mksyslib
CFLAGS=-c -D_POSIX_SOURCE -D_PLAN9_SOURCE
/sys/src/ape/lib/ap/amd64/notetramp.c 664 sys sys 1367613437 1556
#include "../plan9/lib.h"
#include "../plan9/sys9.h"
#include <signal.h>
#include <setjmp.h>
/* 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 <lock.h>
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(LSHIFT), R(BW1)
MOVM.DB.W (R(FROM)), [R(BR0)-R(BR1)]
ORR R(BR1)>>R(RSHIFT), R(BW1)
MOVW R(BR1)<<R(LSHIFT), R(BW0)
ORR R(BR0)>>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(LSHIFT), R(FW0)
MOVW R(FR0)>>R(RSHIFT), R(FW1)
ORR R(FR1)<<R(LSHIFT), R(FW1)
MOVM.IA.W [R(FW0)-R(FW1)], (R(TS))
B _fu8loop
_fu1tail:
SUB R(OFFSET), R(FROM)
B _f1tail
/sys/src/ape/lib/ap/arm/memset.s 664 sys sys 1367613437 1002
TO = 1
TOE = 2
N = 3
TMP = 3 /* N and TMP don't overlap */
TEXT memset(SB), $0
MOVW R0, R(TO)
MOVW data+4(FP), R(4)
MOVW n+8(FP), R(N)
ADD R(N), R(TO), R(TOE) /* to end pointer */
CMP $4, R(N) /* need at least 4 bytes to copy */
BLT _1tail
AND $0xFF, R(4)
ORR R(4)<<8, R(4)
ORR R(4)<<16, R(4) /* replicate to word */
_4align: /* align on 4 */
AND.S $3, R(TO), R(TMP)
BEQ _4aligned
MOVBU.P R(4), 1(R(TO)) /* implicit write back */
B _4align
_4aligned:
SUB $15, R(TOE), R(TMP) /* do 16-byte chunks if possible */
CMP R(TMP), R(TO)
BHS _4tail
MOVW R4, R5 /* replicate */
MOVW R4, R6
MOVW R4, R7
_f16loop:
CMP R(TMP), R(TO)
BHS _4tail
MOVM.IA.W [R4-R7], (R(TO))
B _f16loop
_4tail:
SUB $3, R(TOE), R(TMP) /* do remaining words if possible */
_4loop:
CMP R(TMP), R(TO)
BHS _1tail
MOVW.P R(4), 4(R(TO)) /* implicit write back */
B _4loop
_1tail:
CMP R(TO), R(TOE)
BEQ _return
MOVBU.P R(4), 1(R(TO)) /* implicit write back */
B _1tail
_return:
RET
/sys/src/ape/lib/ap/arm/mkfile 664 sys sys 1367613437 320
APE=/sys/src/ape
<$APE/config
LIB=/$objtype/lib/ape/libap.a
OFILES=\
cycles.$O\
div.$O\
getfcr.$O\
lock.$O\
main9.$O\
main9p.$O\
memmove.$O\
memset.$O\
notetramp.$O\
setjmp.$O\
strchr.$O\
strcmp.$O\
strcpy.$O\
tas.$O\
vlop.$O\
vlrt.$O\
</sys/src/cmd/mksyslib
CFLAGS=-c -D_POSIX_SOURCE -D_PLAN9_SOURCE
/sys/src/ape/lib/ap/arm/notetramp.c 664 sys sys 1367613437 1342
#include "../plan9/lib.h"
#include "../plan9/sys9.h"
#include <signal.h>
#include <setjmp.h>
/* 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 <stdlib.h>
#include <unistd.h>
#include <assert.h>
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 <sys/types.h>
#include <unistd.h>
#include <signal.h>
void
abort(void)
{
kill(getpid(), SIGABRT);
}
/sys/src/ape/lib/ap/gen/abs.c 664 sys sys 1367613437 133
#include <stdlib.h>
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 <stdlib.h>
double
atof(const char *s)
{
return(strtod(s, (char **)0));
}
/sys/src/ape/lib/ap/gen/atoi.c 664 sys sys 1367613437 85
#include <stdlib.h>
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 <stdlib.h>
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 <stdlib.h>
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 <stdlib.h>
#include <stdio.h>
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 <stdlib.h>
#include <string.h>
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 <time.h>
#include <sys/times.h>
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 <ctype.h>
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 <time.h>
/* 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 <stdlib.h>
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 <stdlib.h>
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 <ctype.h>
#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 <stdlib.h>
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 <stdlib.h>
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 <stdlib.h>
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 <stdlib.h>
#include <utf.h>
/*
* 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 <string.h>
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 <string.h>
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 <string.h>
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 <string.h>
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 <string.h>
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}
</sys/src/cmd/mksyslib
CFLAGS=-TVwc -D_POSIX_SOURCE
/sys/src/ape/lib/ap/gen/mktime.c 664 sys sys 1367613437 2373
#include <time.h>
/*
* 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; i<t->tm_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; i<t->tm_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 <stdlib.h>
#include <inttypes.h>
#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 <sys/types.h>
#include <signal.h>
#include <unistd.h>
int
raise(int sig)
{
return kill(getpid(), sig);
}
/sys/src/ape/lib/ap/gen/rand.c 664 sys sys 1369166818 1109
#include <stdlib.h>
/*
* 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 <string.h>
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 <string.h>
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 <string.h>
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 <string.h>
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 <string.h>
#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 <string.h>
#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 <string.h>
#include <ctype.h>
#include <stdlib.h>
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 <time.h>
#include <string.h>
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 && sp<se; fp++){
if(*fp != '%')
*sp++ = *fp;
else switch(*++fp){
case 'a':
sp = strval(sp, se, awday, t->tm_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<se)
*sp = 0;
return sp-s;
}
static char *
strval(char *start, char *end, char **array, int index, int alen)
{
int n;
if(index<0 || index>=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<width){
*start = '?';
return start+1;
}
p = start+width-1;
while(p>=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 <string.h>
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 <string.h>
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 <string.h>
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 <string.h>
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 <string.h>
#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 <string.h>
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 <string.h>
#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 <string.h>
/* 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 <math.h>
#include <errno.h>
/*
* 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 <string.h>
#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 <stdlib.h>
#include <limits.h>
#include <errno.h>
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)
goto Return;
/*
* Non-empty sequence of digits
*/
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;
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 <stdlib.h>
#include <limits.h>
#include <errno.h>
#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)
goto Return;
/*
* Non-empty sequence of digits
*/
m = VLONG_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;
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 <stdlib.h>
#include <errno.h>
#include <limits.h>
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)
goto Return;
/*
* Non-empty sequence of digits
*/
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;
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 <stdlib.h>
#include <errno.h>
#include <limits.h>
#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 <string.h>
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 <ctype.h>
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 <math.h>
#include <errno.h>
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 <math.h>
#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 <math.h>
#include <errno.h>
/*
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 <math.h>
#include <errno.h>
/*
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 <math.h>
#include <errno.h>
#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 <math.h>
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 <math.h>
/*
* 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 <math.h>
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 <math.h>
#include <errno.h>
/*
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 <math.h>
/*
* 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 <math.h>
#include <errno.h>
/*
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 <math.h>
#include <errno.h>
/*
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 <math.h>
#include <errno.h>
/*
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 n<x, forward recursion us used starting
from values of j0(x) and j1(x).
for n>x, 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; i<n; i++) {
temp = b;
b = (2*i/x)*b - a;
a = temp;
}
return b;
recurs:
xsq = x*x;
for(t=0,i=n+16; i>n; 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<n; i++) {
temp = b;
b = (2*i/x)*b - a;
a = temp;
}
return sign*b;
}
/sys/src/ape/lib/ap/math/log.c 664 sys sys 1367613437 1118
/*
log returns the natural logarithm of its floating
point argument.
The coefficients are #2705 from Hart & Cheney. (19.38D)
It calls frexp.
*/
#include <math.h>
#include <errno.h>
#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\
</sys/src/cmd/mksyslib
CFLAGS=-TVwc -D_POSIX_SOURCE
/sys/src/ape/lib/ap/math/modf.c 664 sys sys 1367613437 736
#include <math.h>
#include <errno.h>
/* 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 <math.h>
#include <errno.h>
#include <limits.h>
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 <math.h>
#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 <math.h>
#include <errno.h>
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 <math.h>
#include <errno.h>
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 <math.h>
/*
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 <stdlib.h>
#include <string.h>
#include "../plan9/sys9.h"
#include <lock.h>
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\
</sys/src/cmd/mksyslib
CFLAGS=-c -D_POSIX_SOURCE -D_PLAN9_SOURCE
/sys/src/ape/lib/ap/mips/notetramp.c 664 sys sys 1367613437 1342
#include "../plan9/lib.h"
#include "../plan9/sys9.h"
#include <signal.h>
#include <setjmp.h>
/* 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 <string.h>
#include <stdlib.h>
#include <fmt.h>
#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 <stdlib.h>
#include <string.h>
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 <string.h>
#include <stdlib.h>
#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 <stdio.h>
#include <string.h>
#include <stdlib.h>
#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; nargs<maxargs; nargs++){
while(*s!='\0' && strchr(qsep, *s)!=nil)
s++;
if(*s == '\0')
break;
args[nargs] = s;
s = qtoken(s);
}
return nargs;
}
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, 5) != 5){
strcpy(buf, "couldn't parse wait message");
_ERRSTR(buf, sizeof buf);
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/plan9/9write.c 664 sys sys 1367613437 171
#include "lib.h"
#include <string.h>
#include <stdlib.h>
#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 <string.h>
#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 <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#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; j<nd; j++){
d9 = &d9a[j];
n = strlen(d9->name);
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; i<m; i++)
if(p[n+1+i] == 0)
p[n+1+i] = 1;
p[n+1+m] = 0;
if(strcmp(d9->name, "_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 <string.h>
#include <errno.h>
/* 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 <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
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 <string.h>
#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 <sys/stat.h>
#include <stdlib.h>
#include "sys9.h"
#include <string.h>
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 <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#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; i<nmemo; i++) {
mem = memo[i];
if(matchnum)
matched = (mem->num == *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 <unistd.h>
#include <time.h>
#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 <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#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 <stddef.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <lock.h>
#include <sys/time.h>
#include <sys/select.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#include <termios.h>
#include <stdarg.h>
#include <math.h>
#include <float.h>
#include <sys/utsname.h>
/* #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 <unistd.h>
#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 <errno.h>
#include <inttypes.h>
#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<<fd)) && selwait ->
selwait = 0;
lock!release;
sel!fd
:: !datawait[fd] && !((rwant & (1<<fd)) && selwait) ->
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<<fd)) && selwait ->
selwait = 0;
lock!release;
sel!fd
:: !datawait[fd] && !((rwant & (1<<fd)) && selwait) ->
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<<i) ->
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<<i)) -> skip
fi;
i = i+1
:: i >= NBUFS -> break
od;
num = 0;
lock?get;
rwant = 0;
i = 0;
do
:: i < NBUFS ->
if
:: r & (1<<i) ->
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<<i));
rwant = rwant | (1<<i)
fi
:: !(r & (1<<i)) -> 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<<fd)) && (n[fd] > 0) ->
r = r | (1<<fd);
num = 1
:: !(rwant & (1<<fd)) || (n[fd] == 0) ->
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 <termios.h>
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 <unistd.h>
#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 <errno.h>
#include <stdlib.h>
#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 <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#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 <stdlib.h>
#include <unistd.h>
#include <errno.h>
#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 <string.h>
#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 <string.h>
#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 <sys/stat.h>
#include <fcntl.h>
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 <unistd.h>
#include <stdio.h>
#include <string.h>
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 <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <unistd.h>
#include <string.h>
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<TZSIZE; i++) {
if(rd_long(&p, &timezone.dlpairs[i]))
goto error;
if(timezone.dlpairs[i] == 0)
return;
}
error:
timezone.stdiff = 0;
strcpy(timezone.stname, "GMT");
timezone.dlpairs[0] = 0;
}
static
rd_name(char **f, char *p)
{
int c, i;
for(;;) {
c = *(*f)++;
if(c != ' ' && c != '\n')
break;
}
for(i=0; i<3; i++) {
if(c == ' ' || c == '\n')
return 1;
*p++ = c;
c = *(*f)++;
}
if(c != ' ' && c != '\n')
return 1;
*p = 0;
return 0;
}
static
rd_long(char **f, long *p)
{
int c, s;
long l;
s = 0;
for(;;) {
c = *(*f)++;
if(c == '-') {
s++;
continue;
}
if(c != ' ' && c != '\n')
break;
}
if(c == 0) {
*p = 0;
return 0;
}
l = 0;
for(;;) {
if(c == ' ' || c == '\n')
break;
if(c < '0' || c > '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 <unistd.h>
#include <stdio.h>
#include <string.h>
/*
* 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 <u.h> */
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 <string.h>
#include <stdlib.h>
#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 <string.h>
#include <stdlib.h>
#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 <sys/stat.h>
#include <errno.h>
#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 <unistd.h>
#include <errno.h>
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 <unistd.h>
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 <unistd.h>
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 <unistd.h>
#include <string.h>
#include <sys/limits.h>
/*
* 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 <unistd.h>
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 <unistd.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#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; n<OPEN_MAX; n++){
fi = &_fdinfo[n];
flags = fi->flags;
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 <unistd.h>
#include <sys/limits.h>
#include <string.h>
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 <unistd.h>
#include <errno.h>
#include <stdarg.h>
#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; i<OPEN_MAX; i++)
if(!(_fdinfo[i].flags&FD_ISOPEN))
break;
if(i == OPEN_MAX)
err = EMFILE;
else {
ans = _DUP(fd, i);
if(ans != i){
if(ans < 0){
_syserrno();
err = errno;
}else
err = EBADF;
}else{
fans = &_fdinfo[ans];
fans->flags = 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 <errno.h>
#include <unistd.h>
#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 <math.h>
#include <errno.h>
#define _RESEARCH_SOURCE
#include <float.h>
#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<<SIG) * d;
*ep = -SIG;
}
*ep += ((x.hi >> 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 <sys/stat.h>
#include <errno.h>
#include <stdlib.h>
#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 <sys/types.h>
#include <unistd.h>
#include <errno.h>
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 <sys/types.h>
#include <unistd.h>
#include <errno.h>
#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 <stddef.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#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 <sys/types.h>
#include <grp.h>
#include <unistd.h>
/*
* 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 <stddef.h>
#include <grp.h>
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 <stddef.h>
#include <grp.h>
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 <sys/types.h>
#include <unistd.h>
#include <errno.h>
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 <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/limits.h>
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 <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#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 <sys/stat.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#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 <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#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 <stddef.h>
#include <pwd.h>
#include <string.h>
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 <stddef.h>
#include <pwd.h>
#include <string.h>
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 <sys/types.h>
#include <pwd.h>
#include <unistd.h>
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 <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#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 <fcntl.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
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 <sys/types.h>
#include <sys/limits.h>
#include <fcntl.h>
#include <ureg.h>
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 <unistd.h>
#include <errno.h>
/*
* 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 <unistd.h>
#include <errno.h>
#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 <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include <lock.h>
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<<pow))
goto good;
}
return nil;
good:
/* Allocate off this list */
lock(&arena);
bp = arena.btab[pow];
if(bp) {
arena.btab[pow] = bp->next;
unlock(&arena);
if(bp->magic != 0)
abort();
bp->magic = MAGIC;
return bp->data;
}
size = sizeof(Bucket)+(1<<pow);
size += 7;
size &= ~7;
if(pow < CUTOFF) {
n = (CUTOFF-pow)+2;
bp = sbrk(size*n);
if((intptr_t)bp < 0){
unlock(&arena);
return nil;
}
next = (intptr_t)bp+size;
nbp = (Bucket*)next;
arena.btab[pow] = nbp;
for(n -= 2; n; n--) {
next = (intptr_t)nbp+size;
nbp->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<<bp->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 <sys/stat.h>
#include <errno.h>
#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}\
</sys/src/cmd/mksyslib
CFLAGS=-TVwc -D_POSIX_SOURCE -D_PLAN9_SOURCE -D_BSD_EXTENSION -D_SUSV2_SOURCE
$OFILES: lib.h
/sys/src/ape/lib/ap/plan9/nan.c 664 sys sys 1367613437 614
#include <float.h>
#include <math.h>
#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 <errno.h>
#include <stdarg.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "lib.h"
#include <sys/stat.h>
#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 <stdlib.h>
#include <sys/stat.h>
#include <dirent.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#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; i<DBLOCKSIZE && d->dirloc < 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 <unistd.h>
#include <errno.h>
#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 <errno.h>
#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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <fcntl.h>
#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 <u.h>
#include <lock.h>
#include <qlock.h>
#include <stdlib.h>
#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 <errno.h>
#include <inttypes.h>
#include <unistd.h>
#include <string.h>
#include "lib.h"
#include "sys9.h"
#include <stdio.h>
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 <unistd.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#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 <errno.h>
#include <unistd.h>
#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 <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <lock.h>
#include <sys/time.h>
#include <sys/select.h>
#include <unistd.h>
#include <inttypes.h>
#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; i<OPEN_MAX; i++)
if(i!=fd && (_fdinfo[i].flags&FD_ISOPEN))
_CLOSE(i);
_RENDEZVOUS((void*)0x101, (void*)_muxsid);
_copyproc(fd, b);
}
/* parent process continues ... */
b->copypid = 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; i<nfds; i++)
if(FD_ISSET(i, wfds)) {
n++;
}
}
lock(&mux->lock);
slots = mux->curfds;
FD_ZERO(&mux->rwant);
FD_ZERO(&mux->ewant);
for(i = 0; i<slots; i++) {
b = &mux->bufs[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; i<OPEN_MAX; i++)
_CLOSE(i);
while(_RENDEZVOUS((void*)0x103, 0) == (void*)~0)
;
for(;;) {
_SLEEP(mux->waittime);
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 <math.h>
#include <errno.h>
#define _RESEARCH_SOURCE
#include <float.h>
#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 <sys/types.h>
#include <unistd.h>
#include <errno.h>
/*
* 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 <unistd.h>
#include <stdio.h>
#include <errno.h>
#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 <unistd.h>
#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 <sys/types.h>
#include <unistd.h>
#include <errno.h>
/*
* 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 <signal.h>
#include <errno.h>
#include <string.h>
#include <setjmp.h>
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 <signal.h> */
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<NSIGTAB; i++){
if(strncmp(msg, sigtab[i].msg, strlen(sigtab[i].msg)) == 0){
f = _sighdlr[sigtab[i].num];
if(f == SIG_DFL || f == SIG_ERR)
break;
if(f != SIG_IGN) {
_notetramp(sigtab[i].num, f, u);
/* notetramp is machine-dependent; doesn't return to here */
}
_NOTED(0); /* NCONT */
return 0;
}
}
_doatexits();
_NOTED(1); /* NDFLT */
return 0;
}
int
_stringsig(char *nam)
{
int i;
for(i = 0; i<NSIGTAB; i++)
if(strncmp(nam, sigtab[i].msg, strlen(sigtab[i].msg)) == 0)
return sigtab[i].num;
return 0;
}
char *
_sigstring(int sig)
{
int i;
for(i=0; i<NSIGTAB; i++)
if(sigtab[i].num == sig)
return sigtab[i].msg;
return "unknown signal";
}
/sys/src/ape/lib/ap/plan9/sigpending.c 664 sys sys 1367613437 118
#include <signal.h>
/*
* 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 <signal.h>
#include <errno.h>
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 <signal.h>
#include <errno.h>
/*
* 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 <unistd.h>
#include <time.h>
#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 <math.h>
#include <errno.h>
#define _RESEARCH_SOURCE
#include <float.h>
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 <sys/stat.h>
#include <errno.h>
#include <stdlib.h>
#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 <stdlib.h>
#include <stdio.h>
#include <sys/wait.h>
#include <unistd.h>
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 <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#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 <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#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 <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/times.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
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 <stdlib.h>
#include <unistd.h>
#include <string.h>
#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 <unistd.h>
#include <stdio.h>
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 <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
/*
* 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 <stdlib.h>
#include <string.h>
#include <sys/utsname.h>
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 <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#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 <sys/types.h>
#include <time.h>
#include <utime.h>
#include <errno.h>
#include <stdlib.h>
#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 <stdlib.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#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 <errno.h>
#include <inttypes.h>
#include <unistd.h>
#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 <stdio.h>
#include <grp.h>
#include <stdlib.h>
#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 <stdio.h>
#include <pwd.h>
#include <stdlib.h>
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 <locale.h>
#include <limits.h>
#include <string.h>
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); c++)
if(strcmp(locale, localename[c]) == 0)
break;
if(c >= ASIZE(localename))
return 0;
catlocale[category] = c;
if(category == LC_ALL)
for(i=0; i<ASIZE(catlocale); i++)
catlocale[i] = c;
return localename[c];
}
struct lconv *
localeconv(void)
{
/* BUG: posix says look at environment variables
* to set locale "", but we just make it the same
* as C, always.
*/
return &Clocale;
}
/sys/src/ape/lib/ap/posix/mkfifo.c 664 sys sys 1369166818 148
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
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\
</sys/src/cmd/mksyslib
CFLAGS=-TVwc -D_POSIX_SOURCE
/sys/src/ape/lib/ap/posix/pathconf.c 664 sys sys 1369166818 816
#include <unistd.h>
#include <limits.h>
#include <errno.h>
#include <sys/limits.h>
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 <signal.h>
#include <errno.h>
/*
* 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 <stdio.h>
#include <unistd.h>
#include <limits.h>
#include <time.h>
#include <errno.h>
#include <sys/limits.h>
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 <stdlib.h>
#include <sys/types.h>
#include <fcntl.h>
#include <time.h>
#include <ctype.h>
#include <string.h>
#include <unistd.h>
#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 <lock.h>
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\
</sys/src/cmd/mksyslib
CFLAGS=-c -D_POSIX_SOURCE -D_PLAN9_SOURCE
/sys/src/ape/lib/ap/power/notetramp.c 664 sys sys 1367613437 1342
#include "../plan9/lib.h"
#include "../plan9/sys9.h"
#include <signal.h>
#include <setjmp.h>
/* 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 <lock.h>
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\
</sys/src/cmd/mksyslib
CFLAGS=-c -D_POSIX_SOURCE -D_PLAN9_SOURCE
/sys/src/ape/lib/ap/sparc/muldiv.s 664 sys sys 1367613437 5379
/*
* 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<quo; i++)
* den <<= 1;
* quo = 0;
* for(; i>=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 <signal.h>
#include <setjmp.h>
/* 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 <lock.h>
FILE *_IO_newfile(void)
{
static FILE *fx=0;
static Lock fl;
FILE *f;
int i;
lock(&fl);
for(i=0; i<FOPEN_MAX; i++){
if(fx==0 || ++fx >= &_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 <stdio.h>
#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 <unistd.h>
#include <stdlib.h>
extern void (*_atexitfns[ATEXIT_MAX])(void);
int
atexit(void (*f)(void))
{
int i;
for(i=0; i<ATEXIT_MAX; i++)
if(!_atexitfns[i]){
_atexitfns[i] = f;
return(0);
}
return(1);
}
/sys/src/ape/lib/ap/stdio/clearerr.c 664 sys sys 1367613437 186
/*
* pANS stdio -- clearerr
*/
#include "iolib.h"
void clearerr(FILE *f){
switch(f->state){
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 <unistd.h>
#include <stdlib.h>
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.
[email protected]
*/
#include <stdlib.h>
#include <string.h>
#define _RESEARCH_SOURCE
#include <errno.h>
#include <float.h>
#include <math.h>
#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 <string.h>
#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 <math.h>
#include <stdlib.h>
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 <string.h>
#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 <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
/*
* 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\
</sys/src/cmd/mksyslib
# getc(), putc() prevent warnings
CFLAGS=-Tc -D_POSIX_SOURCE
/sys/src/ape/lib/ap/stdio/perror.c 664 sys sys 1367613437 236
/*
* pANS stdio -- perror
*/
#include "iolib.h"
void perror(const char *s){
extern int errno;
if(s!=NULL && *s != '\0') fputs(s, stderr), fputs(": ", stderr);
fputs(strerror(errno), stderr);
putc('\n', stderr);
fflush(stderr);
}
/sys/src/ape/lib/ap/stdio/pow10.c 664 sys sys 1367613437 429
#include <errno.h>
#include <float.h>
#include <math.h>
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 <string.h>
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 <stdlib.h>
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 <stdlib.h>
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 <string.h>
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 <string.h>
#include <errno.h>
#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 <string.h>
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 <stdarg.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#define _SUSV2_SOURCE
#include <inttypes.h>
/*
* 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<width; i++) putc(' ', f);
putc((unsigned char)va_arg(*args, int), f);
if(flags&LEFT) for(i=1; i<width; i++) putc(' ', f);
return width<1 ? 1 : width;
}
static int
ocvt_s(FILE *f, va_list *args, int flags, int width, int precision)
{
int i, n = 0;
char *s;
s = va_arg(*args, char *);
if(!s)
s = "";
if(!(flags&LEFT)){
if(precision >= 0)
for(i=0; i!=precision && s[i]; i++);
else
for(i=0; s[i]; i++);
for(; i<width; i++){
putc(' ', f);
n++;
}
}
if(precision >= 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<width; i++){
putc(' ', f);
n++;
}
}
return n;
}
static int
ocvt_n(FILE *f, va_list *args, int flags, int width, int precision)
{
USED(f, width, precision);
if(flags&SHORT)
*va_arg(*args, short *) = nprint;
else if(flags&LONG)
*va_arg(*args, long *) = nprint;
else if(flags&VLONG)
*va_arg(*args, long long*) = nprint;
else
*va_arg(*args, int *) = nprint;
return 0;
}
/*
* Generic fixed-point conversion
* f is the output FILE *;
* args is the va_list * from which to get the number;
* flags, width and precision are the results of printf-cracking;
* radix is the number base to print in;
* alphabet is the set of digits to use;
* prefix is the prefix to print before non-zero numbers when
* using ``alternate form.''
*/
static int
ocvt_fixed(FILE *f, va_list *args, int flags, int width, int precision,
int radix, int sgned, char alphabet[], char *prefix)
{
char digits[128]; /* no reasonable machine will ever overflow this */
char *sign;
char *dp;
long long snum;
unsigned long long num;
int nout, npad, nlzero;
if(sgned){
if(flags&PTR) snum = (uintptr_t)va_arg(*args, void *);
else if(flags&SHORT) snum = va_arg(*args, short);
else if(flags&LONG) snum = va_arg(*args, long);
else if(flags&VLONG) snum = va_arg(*args, long long);
else snum = va_arg(*args, int);
if(snum < 0){
sign = "-";
num = -snum;
} else{
if(flags&SIGN) sign = "+";
else if(flags&SPACE) sign = " ";
else sign = "";
num = snum;
}
} else {
sign = "";
if(flags&PTR) num = (uintptr_t)va_arg(*args, void *);
else if(flags&SHORT) num = va_arg(*args, unsigned short);
else if(flags&LONG) num = va_arg(*args, unsigned long);
else if(flags&VLONG) num = va_arg(*args, unsigned long long);
else num = va_arg(*args, unsigned int);
}
if(num == 0) prefix = "";
dp = digits;
do{
*dp++ = alphabet[num%radix];
num /= radix;
}while(num);
if(precision==0 && dp-digits==1 && dp[-1]=='0')
dp--;
nlzero = precision-(dp-digits);
if(nlzero < 0) nlzero = 0;
if(flags&ALT){
if(radix == 8) if(dp[-1]=='0' || nlzero) prefix = "";
}
else prefix = "";
nout = dp-digits+nlzero+strlen(prefix)+strlen(sign);
npad = width-nout;
if(npad < 0) npad = 0;
nout += npad;
if(!(flags&LEFT)){
if(flags&ZPAD && precision <= 0){
fputs(sign, f);
fputs(prefix, f);
while(npad){
putc('0', f);
--npad;
}
} else{
while(npad){
putc(' ', f);
--npad;
}
fputs(sign, f);
fputs(prefix, f);
}
while(nlzero){
putc('0', f);
--nlzero;
}
while(dp!=digits) putc(*--dp, f);
}
else{
fputs(sign, f);
fputs(prefix, f);
while(nlzero){
putc('0', f);
--nlzero;
}
while(dp != digits) putc(*--dp, f);
while(npad){
putc(' ', f);
--npad;
}
}
return nout;
}
static int
ocvt_X(FILE *f, va_list *args, int flags, int width, int precision)
{
return ocvt_fixed(f, args, flags, width, precision, 16, 0, "0123456789ABCDEF", "0X");
}
static int
ocvt_d(FILE *f, va_list *args, int flags, int width, int precision)
{
return ocvt_fixed(f, args, flags, width, precision, 10, 1, "0123456789", "");
}
static int
ocvt_o(FILE *f, va_list *args, int flags, int width, int precision)
{
return ocvt_fixed(f, args, flags, width, precision, 8, 0, "01234567", "0");
}
static int
ocvt_p(FILE *f, va_list *args, int flags, int width, int precision)
{
return ocvt_fixed(f, args, flags|PTR|ALT, width, precision, 16, 0,
"0123456789ABCDEF", "0x");
}
static int
ocvt_u(FILE *f, va_list *args, int flags, int width, int precision)
{
return ocvt_fixed(f, args, flags, width, precision, 10, 0, "0123456789", "");
}
static int
ocvt_x(FILE *f, va_list *args, int flags, int width, int precision)
{
return ocvt_fixed(f, args, flags, width, precision, 16, 0, "0123456789abcdef", "0x");
}
static int ocvt_flt(FILE *, va_list *, int, int, int, char);
static int
ocvt_E(FILE *f, va_list *args, int flags, int width, int precision)
{
return ocvt_flt(f, args, flags, width, precision, 'E');
}
static int
ocvt_G(FILE *f, va_list *args, int flags, int width, int precision)
{
return ocvt_flt(f, args, flags, width, precision, 'G');
}
static int
ocvt_e(FILE *f, va_list *args, int flags, int width, int precision)
{
return ocvt_flt(f, args, flags, width, precision, 'e');
}
static int
ocvt_f(FILE *f, va_list *args, int flags, int width, int precision)
{
return ocvt_flt(f, args, flags, width, precision, 'f');
}
static int
ocvt_g(FILE *f, va_list *args, int flags, int width, int precision)
{
return ocvt_flt(f, args, flags, width, precision, 'g');
}
static int
ocvt_flt(FILE *f, va_list *args, int flags, int width, int precision, char afmt)
{
extern char *_dtoa(double, int, int, int*, int*, char **);
int echr;
char *digits, *edigits;
int exponent;
char fmt;
int sign;
int ndig;
int nout, i;
char ebuf[20]; /* no sensible machine will overflow this */
char *eptr;
double d;
echr = 'e';
fmt = afmt;
d = va_arg(*args, double);
if(precision < 0) precision = 6;
switch(fmt){
case 'f':
digits = _dtoa(d, 3, precision, &exponent, &sign, &edigits);
break;
case 'E':
echr = 'E';
fmt = 'e';
/* fall through */
case 'e':
digits = _dtoa(d, 2, 1+precision, &exponent, &sign, &edigits);
break;
case 'G':
echr = 'E';
/* fall through */
case 'g':
if (precision > 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(eptr<ebuf+2) *eptr++ = '0';
nout += eptr-ebuf+2; /* e+99 */
}
if(!(flags&ZPAD) && !(flags&LEFT))
while(nout < width){
putc(' ', f);
nout++;
}
if(sign) putc('-', f);
else if(flags&SIGN) putc('+', f);
else if(flags&SPACE) putc(' ', f);
if((flags&ZPAD) && !(flags&LEFT))
while(nout < width){
putc('0', f);
nout++;
}
if(fmt == 'f'){
for(i=0; i<exponent; i++) putc(i<ndig?digits[i]:'0', f);
if(i == 0) putc('0', f);
if(precision>0 || flags&ALT) putc('.', f);
for(i=0; i!=precision; i++)
putc(0<=i+exponent && i+exponent<ndig?digits[i+exponent]:'0', f);
}
else{
putc(digits[0], f);
if(precision>0 || flags&ALT) putc('.', f);
for(i=0; i!=precision; i++) putc(i<ndig-1?digits[i+1]:'0', f);
}
if(fmt != 'f'){
putc(echr, f);
putc(exponent<=0?'-':'+', f);
while(eptr>ebuf) 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 <stdarg.h>
#include <math.h>
#include <stdlib.h>
#include <ctype.h>
#define _PLAN9_SOURCE
#include <qlock.h>
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<width) width=NBUF; /* bug -- no limit specified in ansi */
do
c=ngetc(f);
while(isspace(c));
if(width--==0){
nungetc(c, f);
goto Done;
}
if(c=='+' || c=='-'){
*s++=c;
wgetc(c, f, Done);
}
while('0'<=c && c<='9' || ndpt==0 && c=='.'){
if(c=='.') ndpt++;
else ndig++;
*s++=c;
wgetc(c, f, Done);
}
if(c=='e' || c=='E'){
*s++=c;
nexp=0;
wgetc(c, f, Done);
if(c=='+' || c=='-'){
*s++=c;
wgetc(c, f, Done);
}
while('0'<=c && c<='9'){
*s++=c;
nexp++;
wgetc(c, f, Done);
}
}
nungetc(c, f);
Done:
if(ndig==0 || nexp==0) return 0;
*s='\0';
if(store) switch(type){
case 'h':
case 'n': *va_arg(*args, float *)=atof(buf); break;
case 'L': /* bug -- should store in a long double */
case 'l': *va_arg(*args, double *)=atof(buf); break;
}
return 1;
}
static int icvt_s(FILE *f, va_list *args, int store, int width, int type){
int c, nn;
char *s;
USED(type);
if(store) s=va_arg(*args, char *);
else s=NULL;
do
c=ngetc(f);
while(isspace(c));
if(width--==0){
nungetc(c, f);
goto Done;
}
nn=0;
while(!isspace(c)){
if(c==EOF){
nread--;
if(nn==0) return 0;
else goto Done;
}
nn++;
if(store) *s++=c;
wgetc(c, f, Done);
}
nungetc(c, f);
Done:
if(store) *s='\0';
return 1;
}
static int icvt_c(FILE *f, va_list *args, int store, int width, int type){
int c;
char *s;
USED(type);
if(store) s=va_arg(*args, char *);
else s=NULL;
if(width<0) width=1;
for(;;){
wgetc(c, f, Done);
if(c==EOF) return 0;
if(store) *s++=c;
}
Done:
return 1;
}
static int match(int c, const char *pat){
int ok=1;
if(*pat=='^'){
ok=!ok;
pat++;
}
while(pat!=fmtp){
if(pat+2<fmtp && pat[1]=='-'){
if(pat[0]<=c && c<=pat[2]
|| pat[2]<=c && c<=pat[0])
return ok;
pat+=2;
}
else if(c==*pat) return ok;
pat++;
}
return !ok;
}
static int icvt_sq(FILE *f, va_list *args, int store, int width, int type){
int c, nn;
char *s;
const char *pat;
USED(type);
pat=++fmtp;
if(*fmtp=='^') fmtp++;
if(*fmtp!='\0') fmtp++;
while(*fmtp!='\0' && *fmtp!=']') fmtp++;
if(store) s=va_arg(*args, char *);
else s=NULL;
nn=0;
for(;;){
wgetc(c, f, Done);
if(c==EOF){
nread--;
if(nn==0) return 0;
else goto Done;
}
if(!match(c, pat)) break;
if(store) *s++=c;
nn++;
}
nungetc(c, f);
Done:
if(store) *s='\0';
return 1;
}
/sys/src/ape/lib/ap/stdio/vprintf.c 664 sys sys 1367613437 134
/*
* pANS stdio -- vprintf
*/
#include "iolib.h"
int vprintf(const char *fmt, va_list args){
return vfprintf(stdout, fmt, args);
}
/sys/src/ape/lib/ap/stdio/vsnprintf.c 664 sys sys 1367613437 301
/*
* pANS stdio -- vsnprintf
*/
#define _C99_SNPRINTF_EXTENSION
#include "iolib.h"
int vsnprintf(char *buf, size_t nbuf, const char *fmt, va_list args){
int n;
FILE *f=_IO_sopenw();
if(f==NULL)
return 0;
setvbuf(f, buf, _IOFBF, nbuf);
n=vfprintf(f, fmt, args);
_IO_sclose(f);
return n;
}
/sys/src/ape/lib/ap/stdio/vsprintf.c 664 sys sys 1367613437 254
/*
* pANS stdio -- vsprintf
*/
#include "iolib.h"
int vsprintf(char *buf, const char *fmt, va_list args){
int n;
FILE *f=_IO_sopenw();
if(f==NULL)
return 0;
setvbuf(f, buf, _IOFBF, 100000);
n=vfprintf(f, fmt, args);
_IO_sclose(f);
return n;
}
/sys/src/ape/lib/ap/syscall 20000000775 sys sys 1369861527 0
/sys/src/ape/lib/ap/syscall/genall 775 sys sys 1367613437 520
#!/bin/rc
# genall - generate the APE versions of the system call C interfaces.
# must be invoked by mk so that the right env variables are set.
rfork e
# ugh. sources's build process can't hack absolute path names.
# we're in /sys/src/ape/lib/ap/syscall.
SYSH=../../../../libc/9syscall/sys.h # /sys/src/libc/9syscall/sys.h
SYS=`{sed '/^#define._/d; s/#define.([A-Z0-9_]*).*/\1/' $SYSH}
for(I in $SYS) {
i=_^$I
n=`{sed -n '/[ ]'$I'[ ]/s/.* //p' $SYSH}
gencall
}
ar vu /$objtype/lib/ape/libap.a *.$O
rm -f *.$O *.s
/sys/src/ape/lib/ap/syscall/mkfile 664 sys sys 1367613437 499
NPROC=1
</$objtype/mkfile
all:V: install
install:V: genall gencall
genall
# ugh. sources's build process can't hack absolute path names.
# we're in /sys/src/ape/lib/ap/syscall.
gencall:D: ../../../../libc/9syscall/mkfile # /sys/src/libc/9syscall/mkfile
{
echo '#!/bin/rc'
sed -n -e 's/seek/_SEEK/g' -e '/switch/,/\$AS /p' $prereq
} >$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/%}\
</sys/src/cmd/mksyslib
CFLAGS=-TVwc -D_POSIX_SOURCE -D_PLAN9_SOURCE -D_NET_EXTENSION -I. -I../9 -I/sys/src/libauth
%.$O: /sys/src/libauth/%.c
$CC $CFLAGS /sys/src/libauth/$stem.c
/sys/src/ape/lib/bio 20000000775 bootes sys 1369861528 0
/sys/src/ape/lib/bio/mkfile 664 bootes sys 1369843646 550
APE=/sys/src/ape
<$APE/config
LIB=/$objtype/lib/ape/libbio.a
OFILES=\
bbuffered.$O\
bfildes.$O\
bflush.$O\
bgetrune.$O\
bgetc.$O\
# bgetd.$O\
binit.$O\
boffset.$O\
bprint.$O\
bputrune.$O\
bputc.$O\
brdline.$O\
brdstr.$O\
bread.$O\
bseek.$O\
bwrite.$O\
bvprint.$O\
HFILES=/sys/include/ape/bio.h\
../9/libc.h\
UPDATE=\
mkfile\
$HFILES\
${OFILES:%.$O=%.c}\
${LIB:/$objtype/%=/386/%}\
</sys/src/cmd/mksyslib
CFLAGS=-TVwc -D_PLAN9_SOURCE -D_POSIX_SOURCE -I. -I../9
%.$O: /sys/src/libbio/%.c
$CC $CFLAGS /sys/src/libbio/$stem.c
/sys/src/ape/lib/bsd 20000000775 sys sys 1370495776 0
/sys/src/ape/lib/bsd/_sock_ingetaddr.c 664 sys sys 1370495701 1417
/* posix */
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
/* bsd extensions */
#include <sys/uio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/un.h>
#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 <sys/types.h>
#include <unistd.h>
#include <string.h>
/* bsd extensions */
#include <sys/uio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#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 <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
/* bsd extensions */
#include <sys/uio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/un.h>
#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 <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
/* socket extensions */
#include <sys/uio.h>
#include <sys/socket.h>
#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 <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
/* bsd extensions */
#include <sys/uio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/un.h>
#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 <sys/types.h>
#include <unistd.h>
#include <string.h>
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 <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <sys/stat.h>
/* socket extensions */
#include <sys/uio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/un.h>
/* 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 <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>
/* bsd extensions */
#include <sys/uio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/un.h>
#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 <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#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 <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <inttypes.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
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 <sys/limits.h>
int
getdtablesize(void)
{
return OPEN_MAX;
}
/sys/src/ape/lib/bsd/gethostbyaddr.c 664 sys sys 1367613437 335
/* posix */
#include <sys/types.h>
#include <unistd.h>
/* bsd extensions */
#include <sys/uio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
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 <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
/* bsd extensions */
#include <sys/uio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#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 <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
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 <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <inttypes.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#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 <stdlib.h>
#include <string.h>
#include <stdio.h>
#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 <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>
/* bsd extensions */
#include <sys/uio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/un.h>
#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 <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
/* bsd extensions */
#include <sys/uio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#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 <sys/types.h>
#include <unistd.h>
#include <stdio.h>
/* bsd extensions */
#include <sys/uio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
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 <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
/* bsd extensions */
#include <sys/uio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#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 <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>
/* bsd extensions */
#include <sys/uio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/un.h>
#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 <sys/types.h>
#include <time.h>
#include <sys/time.h>
#include <string.h>
#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 <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
/* bsd extensions */
#include <netdb.h>
#include <sys/socket.h>
//#include <netinet/in.h>
#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 <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
/* bsd extensions */
#include <sys/uio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#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 <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
/* bsd extensions */
#include <sys/uio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/un.h>
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 <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <inttypes.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#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 <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <inttypes.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#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 <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
/* bsd extensions */
#include <sys/uio.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
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 <sys/socket.h>
#include <netinet/in.h>
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 <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>
#include <signal.h>
#include <sys/stat.h>
#define _SUSV2_SOURCE
#include <inttypes.h>
/* socket extensions */
#include <sys/uio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/un.h>
/* 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 <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
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
</sys/src/cmd/mksyslib
CFLAGS=-TVwc -D_POSIX_SOURCE -D_BSD_EXTENSION -D_PLAN9_SOURCE -I../ap/plan9
/sys/src/ape/lib/bsd/mktemp.c 664 sys sys 1367613437 526
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
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 <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#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<FOPEN_MAX; i++)
close(i);
execl("/bin/ape/sh", "sh", "-c", cmd, NULL);
_exit(1);
default:
the_fork[ind].pid = pid;
the_fork[ind].fd = myside;
the_fork[ind].done = 0;
close(hisside);
return(fdopen(myside, mode));
}
}
int
pclose(FILE *ptr)
{
int f, r, ind;
int status;
f = fileno(ptr);
fclose(ptr);
for (ind = 0; ind < MAXFORKS; ind++)
if (the_fork[ind].fd == f && the_fork[ind].pid != 0)
break;
if (ind == MAXFORKS)
return 0;
if (!the_fork[ind].done) {
do {
r = wait(&status);
for (f = 0; f < MAXFORKS; f++)
if (the_fork[f].pid == r) {
the_fork[f].done = 1;
the_fork[f].status = status;
break;
}
} while(r != the_fork[ind].pid && r != -1);
the_fork[ind].status = r == -1 ? -1 : status;
}
the_fork[ind].pid = 0;
return (the_fork[ind].status);
}
/sys/src/ape/lib/bsd/priv.h 664 sys sys 1367613437 1288
typedef struct Rock Rock;
#define nil ((void*)0)
#define nelem(a) (sizeof(a)/sizeof((a)[0]))
enum
{
Ctlsize= 128,
/* states */
Sopen= 0,
Sbound,
Sconnected,
/* types of name */
Tsys= 0,
Tip,
Tdom,
};
/*
* since BSD programs expect to perform both control and data functions
* through a single fd, we need to hide enough info under a rock to
* be able to open the control file when we need it.
*/
struct Rock
{
Rock *next;
unsigned long dev; /* inode & dev of data file */
unsigned long inode; /* ... */
int domain; /* from socket call */
int stype; /* ... */
int protocol; /* ... */
struct sockaddr addr; /* address from bind */
int reserved; /* use a priveledged port # (< 1024) */
struct sockaddr raddr; /* peer address */
char ctl[Ctlsize]; /* name of control file (if any) */
int other; /* fd of the remote end for Unix domain */
};
extern Rock* _sock_findrock(int, struct stat*);
extern Rock* _sock_newrock(int);
extern void _sock_srvname(char*, char*);
extern int _sock_srv(char*, int);
extern int _sock_data(int, char*, int, int, int, Rock**);
extern int _sock_ipattr(char*);
extern void _sock_ingetaddr(Rock*, struct sockaddr*, int*, char*);
extern void _sock_ntop(int, const void*, char*, int , int*);
extern void _syserrno(void);
/sys/src/ape/lib/bsd/pty.c 664 sys sys 1367613437 1918
/* posix */
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/pty.h>
#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 <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
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 <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <sys/stat.h>
/* socket extensions */
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/un.h>
#include <netdb.h>
#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 <unistd.h>
#include <string.h>
#include <sys/uio.h>
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 <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
/* socket extensions */
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/un.h>
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 <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
/* bsd extensions */
#include <sys/uio.h>
#include <sys/socket.h>
#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 <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
/* bsd extensions */
#include <sys/uio.h>
#include <sys/socket.h>
#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 <stdio.h>
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 <sys/types.h>
#include <unistd.h>
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 <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
/* bsd extensions */
#include <sys/uio.h>
#include <sys/socket.h>
#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 <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
/* bsd extensions */
#include <sys/uio.h>
#include <sys/socket.h>
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 <string.h>
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 <string.h>
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 <sys/types.h>
#include <unistd.h>
#include <string.h>
/* bsd extensions */
#include <sys/uio.h>
#include <sys/socket.h>
#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 <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>
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<<screen->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<<screen->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/%}\
</sys/src/cmd/mksyslib
CFLAGS=-TVwc $CFLAGS -D_POSIX_SOURCE -D_PLAN9_SOURCE -I. -I../9
%.$O: /sys/src/libdraw/%.c
$CC $CFLAGS /sys/src/libdraw/$stem.c
$O.colors: colors.$O
$LD -o $target colors.$O
/sys/src/ape/lib/fmt 20000000775 sys sys 1369861528 0
/sys/src/ape/lib/fmt/charstod.c 664 sys sys 1367613437 1995
/*
* 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 <stdarg.h>
#include <string.h>
#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 <stdarg.h>
#include <string.h>
#define _SUSV2_SOURCE
#include <inttypes.h>
#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, "<nil>", 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, "<nil>", 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 <stdarg.h>
#include <string.h>
#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 <stdarg.h>
#include <errno.h>
#include <string.h>
#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 <stdio.h>
#include <math.h>
#include <float.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <stdarg.h>
#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; i++) {
d = (int)g;
s1[i] = d + '0';
g = (g - d) * 10;
}
s1[i] = 0;
/*
* try decimal rounding to eliminate 9s
*/
c2 = prec + 1;
if(chr == 'f')
c2 += e;
oerr = errno;
if(c2 >= 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 <stdarg.h>
#include <string.h>
#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; p<ep; p++)
if(p->c == 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; p<ep; p++)
if(p->c == 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 <stdarg.h>
#include <string.h>
#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 <stdarg.h>
#include <unistd.h>
#define _SUSV2_SOURCE
#include <inttypes.h>
#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 <stdarg.h>
#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 <stdarg.h>
#include <string.h>
#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 <stdarg.h>
#include <string.h>
#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*)"<nil>", 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 <stdarg.h>
#include <string.h>
#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 <stdarg.h>
#include <string.h>
#include <stdlib.h>
#define _SUSV2_SOURCE
#include <inttypes.h>
#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 <stdarg.h>
#include <string.h>
#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 <stdarg.h>
#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\
</sys/src/cmd/mksyslib
$NAN.$O: nan.h
strtod.$O: nan.h
test: $LIB test.$O
$CC -o test test.$O $LIB -L$PLAN9/lib -lutf
CFLAGS=-FVTwc -D_POSIX_SOURCE -D_PLAN9_SOURCE -D_BSD_EXTENSION
/sys/src/ape/lib/fmt/nan.h 664 sys sys 1367613437 114
extern double __NaN(void);
extern double __Inf(int);
extern int __isNaN(double);
extern int __isInf(double, int);
/sys/src/ape/lib/fmt/nan64.c 664 sys sys 1369166818 972
/*
* 64-bit IEEE not-a-number routines.
* This is big/little-endian portable assuming that
* the 64-bit doubles and 64-bit integers have the
* same byte ordering.
*/
#include "nan.h"
typedef unsigned long long uvlong;
typedef unsigned long ulong;
static uvlong uvnan = 0x7FF0000000000001LL;
static uvlong uvinf = 0x7FF0000000000000LL;
static uvlong uvneginf = 0xFFF0000000000000LL;
double
__NaN(void)
{
uvlong *p;
/* gcc complains about "return *(double*)&uvnan;" */
p = &uvnan;
return *(double*)p;
}
int
__isNaN(double d)
{
uvlong x;
double *p;
p = &d;
x = *(uvlong*)p;
return (ulong)(x>>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 <stdarg.h>
#include <string.h>
#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 <stdarg.h>
#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 <stdarg.h>
#include <string.h>
#include <stdlib.h>
#define _SUSV2_SOURCE
#include <inttypes.h>
#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 <stdarg.h>
#include <string.h>
#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 <stdarg.h>
#include <string.h>
#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 <stdarg.h>
#include <string.h>
#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 <stdarg.h>
#include <string.h>
#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 <stdarg.h>
#include <string.h>
#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 <stdarg.h>
#include <stdlib.h>
#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 <stdarg.h>
#include <string.h>
#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 <stdarg.h>
#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 <stdarg.h>
#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 <stdarg.h>
#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 <stdarg.h>
#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 <stdlib.h>
#include <math.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#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<<Nbits),
Half = (ulong)(One>>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<Prec; i++) {
low[i] = 0;
hig[i] = One-1;
}
/* binary search for closest mantissa */
for(;;) {
/* mid = (hig + low) / 2 */
c = 0;
for(i=0; i<Prec; i++) {
mid[i] = hig[i] + low[i];
if(c)
mid[i] += One;
c = mid[i] & 1;
mid[i] >>= 1;
}
frnorm(mid);
/* compare */
c = fpcmp(a, mid);
if(c > 0) {
c = 1;
for(i=0; i<Prec; i++)
if(low[i] != mid[i]) {
c = 0;
low[i] = mid[i];
}
if(c)
break; /* between mid and hig */
continue;
}
if(c < 0) {
for(i=0; i<Prec; i++)
hig[i] = mid[i];
continue;
}
/* only hard part is if even/odd roundings wants to go up */
c = mid[Prec-1] & (Sigbit-1);
if(c == Sigbit/2 && (mid[Prec-1]&Sigbit) == 0)
mid[Prec-1] -= c;
break; /* exactly mid */
}
/* normal rounding applies */
c = mid[Prec-1] & (Sigbit-1);
mid[Prec-1] -= c;
if(c >= 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; i<Prec; i++)
d = d*One + mid[i];
if(flag & Fsign)
d = -d;
d = ldexp(d, bp - Prec*Nbits);
if(d == 0){ /* underflow */
errno = ERANGE;
}
return d;
}
static void
frnorm(ulong *f)
{
int i, c;
c = 0;
for(i=Prec-1; i>0; 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<Prec; i++)
tf[i] = f[i];
for(;;) {
/* tf *= 10 */
for(i=0; i<Prec; i++)
tf[i] = tf[i]*10;
frnorm(tf);
d = (tf[0] >> 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<Prec; i++)
if(tf[i] != 0)
goto cont;
return 0;
}
if(c > 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;
*p++ = c + '0';
c = *a++;
if(c == 0)
break;
n = n*10 + c-'0';
}
(*na)++;
xx:
while(n) {
n = n*10;
c = n>>b;
n -= c<<b;
*p++ = c + '0';
(*na)++;
}
*p = 0;
}
static Tab tab1[] =
{
1, 0, "",
3, 1, "7",
6, 2, "63",
9, 3, "511",
13, 4, "8191",
16, 5, "65535",
19, 6, "524287",
23, 7, "8388607",
26, 8, "67108863",
27, 9, "134217727",
};
static void
divascii(char *a, int *na, int *dp, int *bp)
{
int b, d;
Tab *t;
d = *dp;
if(d >= (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<<b) + n;
n = c/10;
c -= n*10;
p--;
*p = c + '0';
}
while(n) {
c = n;
n = c/10;
c -= n*10;
p--;
*p = c + '0';
}
}
static Tab tab2[] =
{
1, 1, "", /* dp = 0-0 */
3, 3, "125",
6, 5, "15625",
9, 7, "1953125",
13, 10, "1220703125",
16, 12, "152587890625",
19, 14, "19073486328125",
23, 17, "11920928955078125",
26, 19, "1490116119384765625",
27, 19, "7450580596923828125", /* dp 8-9 */
};
static void
mulascii(char *a, int *na, int *dp, int *bp)
{
char *p;
int d, b;
Tab *t;
d = -*dp;
if(d >= (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 <stdarg.h>
#include <utf.h>
#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 <stdarg.h>
#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 <stdarg.h>
#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 <stdlib.h>
#include <stdarg.h>
#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 <stdlib.h>
#include <stdarg.h>
#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 <stdarg.h>
#include <errno.h>
#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 <libl.h>
#include <stdio.h>
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 <libl.h>
#include <stdlib.h>
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\
</sys/src/cmd/mksyslib
CFLAGS=-TVwc -D_RESEARCH_SOURCE
/sys/src/ape/lib/l/reject.c 664 sys sys 1367613437 985
#include <libl.h>
#include <stdio.h>
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 <libl.h>
#include <stdio.h>
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 <libl.h>
#include <stdio.h>
int
yywrap(void)
{
return 1;
}
/sys/src/ape/lib/mkfile 664 sys sys 1357610071 206
</$objtype/mkfile
DIRS=`{ls -l | sed '/^d/!d; s/.* //; /\./d'}
none:V:
echo mk all, install, installall, clean, or nuke
all clean nuke install installall:V:
for (i in $DIRS) @{
cd $i
mk $target
}
/sys/src/ape/lib/mp 20000000775 bootes sys 1368053122 0
/sys/src/ape/lib/mp/386 20000000775 bootes sys 1369168407 0
/sys/src/ape/lib/mp/386/mkfile 664 sys sys 1369166818 367
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\
</sys/src/cmd/mksyslib
%.$O: ../../../../libmp/386/%.s
$AS ../../../../libmp/386/$stem.s
/sys/src/ape/lib/mp/amd64 20000000775 bootes sys 1369861528 0
/sys/src/ape/lib/mp/amd64/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\
</sys/src/cmd/mksyslib
%.$O: ../../../../libmp/amd64/%.s
$AS ../../../../libmp/amd64/$stem.s
/sys/src/ape/lib/mp/arm 20000000775 bootes sys 1369168829 0
/sys/src/ape/lib/mp/arm/mkfile 664 sys sys 1369166818 206
APE=/sys/src/ape
<$APE/config
LIB=/$objtype/lib/ape/libmp.a
OFILES= \
HFILES=/$objtype/include/u.h /sys/include/ape/mp.h ../../../../libmp/port/dat.h
UPDATE=\
mkfile\
$HFILES\
</sys/src/cmd/mksyslib
/sys/src/ape/lib/mp/mips 20000000775 bootes sys 1369168837 0
/sys/src/ape/lib/mp/mips/mkfile 664 sys sys 1369166818 369
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\
</sys/src/cmd/mksyslib
%.$O: ../../../../libmp/mips/%.s
$AS ../../../../libmp/mips/$stem.s
/sys/src/ape/lib/mp/mkfile 664 sys sys 1358051950 1110
APE=/sys/src/ape
<$APE/config
DIRS=port $CPUS
default:V: all
install all:V:
for(i in port $objtype)@{
echo $i
cd $i
mk $MKFLAGS $target
}
nuke:V: clean
rm -f /$objtype/lib/ape/libmp.a
clean:V:
for(i in $DIRS)@{
echo $i
cd $i
mk $MKFLAGS $target
}
installall:V:
for(objtype in $CPUS) mk $MKFLAGS install
everything:V:
rm -f */*.[012456789kvx]
for(objtype in 386)@{
echo $objtype
mk $MKFLAGS install
}
rm -f */*.[012456789kvx]
test.$O: ../../../libmp/test.c /sys/include/ape/mp.h ../../../libmp/port/dat.h
$CC -D_POSIX_SOURCE -D_PLAN9_SOURCE -Iport -I../../../libmp/port ../../../libmp/test.c
$O.test: test.$O /$objtype/lib/ape/libmp.a
$LD -o $O.test test.$O
bigtest.$O: ../../../libmp/bigtest.c /sys/include/ape/mp.h ../../../libmp/port/dat.h
$CC -D_POSIX_SOURCE -D_PLAN9_SOURCE -Iport -I../../../libmp/port ../../../libmp/bigtest.c
$O.bigtest: bigtest.$O /$objtype/lib/ape/libmp.a
$LD -o $O.bigtest bigtest.$O
allout:
objtype=386; mk; mk 8.test 8.bigtest
objtype=amd64; mk; mk 6.test 6.bigtest
objtype=arm; mk; mk 5.test 5.bigtest
cleanout:
rm -f [568].* *.[568]
/sys/src/ape/lib/mp/port 20000000775 bootes sys 1369861528 0
/sys/src/ape/lib/mp/port/mkfile 664 sys sys 1369846673 784
APE=/sys/src/ape
<$APE/config
LIB=/$objtype/lib/ape/libmp.a
FILES=\
mpaux\
mpfmt\
strtomp\
mptobe\
mptole\
betomp\
letomp\
mpadd\
mpsub\
mpcmp\
mpfactorial\
mpmul\
mpleft\
mpright\
mpvecadd\
mpvecsub\
mpvecdigmuladd\
mpveccmp\
mpdigdiv\
mpdiv\
mpexp\
mpmod\
mpextendedgcd\
mpinvert\
mprand\
crt\
mptoi\
mptoui\
mptov\
mptouv\
ALLOFILES=${FILES:%=%.$O}
# cull things in the per-machine directories from this list
OFILES= `{rc ./reduce $O $objtype $ALLOFILES}
HFILES=\
/sys/include/ape/mp.h\
../../../../libmp/port/dat.h\
CFILES=${FILES:%=%.c}
UPDATE=\
mkfile\
$HFILES\
$CFILES\
</sys/src/cmd/mksyslib
CFLAGS=-TVwc -+ -D_POSIX_SOURCE -D_PLAN9_SOURCE -I. -I../../9
%.$O: ../../../../libmp/port/%.c
$CC $CFLAGS ../../../../libmp/port/$stem.c
/sys/src/ape/lib/mp/port/reduce 664 sys sys 1358051950 416
O=$1
shift
objtype=$1
shift
cwd=`{basename -d `{pwd}}
cwd=$cwd/$objtype
bind -ac /sys/src/libmp/$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/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\
</sys/src/cmd/mksyslib
%.$O: ../../../../libmp/power/%.s
$AS ../../../../libmp/power/$stem.s
/sys/src/ape/lib/net 20000000775 sys sys 1369861529 0
/sys/src/ape/lib/net/announce.c 664 sys sys 1367613437 3549
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <libnet.h>
#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 <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <libnet.h>
#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 <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <libnet.h>
/*
* 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\
</sys/src/cmd/mksyslib
CFLAGS=-TVwc -D_POSIX_SOURCE -D_RESEARCH_SOURCE -D_NET_EXTENSION
/sys/src/ape/lib/net/netmkaddr.c 664 sys sys 1367613437 957
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <libnet.h>
/*
* 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\
</sys/src/cmd/mksyslib
CFLAGS=-TVwc -DUTF -D_REGEXP_EXTENSION
/sys/src/ape/lib/regexp/regaux.c 664 sys sys 1367613437 1053
#include <stdlib.h>
#include <stdio.h>
#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.sp<mp[0].s.sp ||
(sp->m[0].s.sp==mp[0].s.sp && sp->m[0].e.ep>mp[0].e.ep)){
for(i=0; i<ms && i<NSUBEXP; i++)
mp[i] = sp->m[i];
for(; i<ms; i++)
mp[i].s.sp = mp[i].e.ep = 0;
}
}
/*
* Note optimization in _renewthread:
* *lp must be pending when _renewthread called; if *l has been looked
* at already, the optimization is a bug.
*/
extern Relist*
_renewthread(Relist *lp, /* _relist to add to */
Reinst *ip, /* instruction to add */
Resublist *sep) /* pointers to subexpressions */
{
Relist *p;
for(p=lp; p->inst; 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 <stdlib.h>
#include <stdio.h>
#include <setjmp.h>
#include <string.h>
#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; inst<freep; inst++){
switch(inst->type){
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; ip<atorp; ip++)
print("0%o\n", *ip);
print("operands\n");
for(stk=andstack; stk<andp; stk++)
print("0%o\t0%o\n", stk->first->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 <stdlib.h>
#include <stdio.h>
#include <string.h>
#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 <stdlib.h>
#include <stdio.h>
#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; i<ms; i++)
mp[i].s.sp = mp[i].e.ep = 0;
_relist[0][0].inst = _relist[1][0].inst = 0;
/* Execute machine once for each character, including terminal NUL */
s = starts;
do{
/* fast check for first char */
r = *(unsigned char*)s;
if(checkstart && r != startchar){
s++;
continue;
}
if(r < Runeself)
n = 1;
else {
n = mbtowc(&r, s, MB_CUR_MAX);
if (n <= 0)
n = 1;
}
/* switch run lists */
tl = _relist[flag];
tle = _reliste[flag];
nl = _relist[flag^=1];
nle = _reliste[flag];
nl->inst = 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 <stdlib.h>
#include <stdio.h>
#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 <stdlib.h>
#include <stdio.h>
#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; i<ms; i++)
mp[i].s.rsp = mp[i].e.rep = 0;
_relist[0][0].inst = _relist[1][0].inst = 0;
/* Execute machine once for each character, including terminal NUL */
s = starts;
do{
r = *s;
/* fast check for first char */
if(checkstart && r!=startchar){
s++;
continue;
}
/* switch run lists */
tl = _relist[flag];
tle = _reliste[flag];
nl = _relist[flag^=1];
nle = _reliste[flag];
nl->inst = 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 <stdlib.h>
#include <stdio.h>
#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\
</sys/src/cmd/mksyslib
%.$O: /sys/src/libsec/$objtype/%.s
$AS $AFLAGS /sys/src/libsec/$objtype/$stem.s
/sys/src/ape/lib/sec/amd64 20000000775 bootes sys 1369861529 0
/sys/src/ape/lib/sec/amd64/mkfile 664 sys sys 1369847177 316
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=${FILES:%=%.$O}
UPDATE=mkfile\
$HFILES\
$SFILES\
</sys/src/cmd/mksyslib
%.$O: /sys/src/libsec/$objtype/%.s
$AS $AFLAGS /sys/src/libsec/$objtype/$stem.s
/sys/src/ape/lib/sec/arm 20000000775 bootes sys 1368053403 0
/sys/src/ape/lib/sec/arm/mkfile 664 bootes sys 1369846959 228
APE=/sys/src/ape
<$APE/config
LIB=/$objtype/lib/ape/libsec.a
OFILES= \
HFILES=/sys/include/ape/libsec.h
UPDATE=mkfile
</sys/src/cmd/mksyslib
%.$O: /sys/src/libsec/$objtype/%.s
$AS $AFLAGS /sys/src/libsec/$objtype/$stem.s
/sys/src/ape/lib/sec/mips 20000000775 bootes sys 1369167039 0
/sys/src/ape/lib/sec/mips/mkfile 664 sys sys 1369847114 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\
</sys/src/cmd/mksyslib
%.$O: /sys/src/libsec/$objtype/%.s
$AS $AFLAGS /sys/src/libsec/$objtype/$stem.s
/sys/src/ape/lib/sec/mkfile 664 sys sys 1358051950 459
APE=/sys/src/ape
<$APE/config
DIRS=port $CPUS
default:V: all
install all:V:
for(i in port $objtype)@{
echo $i
cd $i
mk $MKFLAGS $target
}
clean:V:
for(i in $DIRS)@{
echo $i
cd $i
mk $MKFLAGS $target
}
nuke:V: clean
rm -f /$objtype/lib/ape/libsec.a
installall:V:
for(objtype in $CPUS) mk $MKFLAGS install
everything:V:
rm -f */*.[012456789kqv]
for(objtype in 386)@{
echo $objtype
mk $MKFLAGS install
}
rm -f */*.[012456789kqv]
/sys/src/ape/lib/sec/port 20000000775 bootes sys 1369861529 0
/sys/src/ape/lib/sec/port/_tlshand.c 664 bootes sys 1369153701 55137
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <auth.h>
#include <mp.h>
#include <libsec.h>
// 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; i<n; i++) {
put16(p, m->u.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; i<m->u.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; i<m->u.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; i<b->len; 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; i<b->len; 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; i<RandomSize; i++)
bs = seprint(bs, be, "%.2x", m->u.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; i<RandomSize; i++)
bs = seprint(bs, be, "%.2x", m->u.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; i<m->u.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; i<m->u.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; i<m->u.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
</sys/src/cmd/mksyslib
CFLAGS=-TVwc -+ -I. -I../../9 -D_POSIX_SOURCE -D_PLAN9_SOURCE -I/sys/src/libmp/port
%.$O: /sys/src/libsec/port/%.c
$CC $CFLAGS /sys/src/libsec/port/$stem.c
x509-ape.c: /sys/src/libsec/port/x509.c
cat /sys/src/libsec/port/x509.c | \
sed 's/Tm \*tm = gmtime\(t\)/struct tm *tm0 = gmtime\(\&t\)/;s/tm->/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
</sys/src/cmd/mksyslib
%.$O: /sys/src/libsec/$objtype/%.s
$AS $AFLAGS /sys/src/libsec/$objtype/$stem.s
/sys/src/ape/lib/utf 20000000775 sys sys 1369861529 0
/sys/src/ape/lib/utf/getfields.c 664 bootes sys 1363066162 515
#include <u.h>
#include <utf.h>
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 <u.h>
#include <utf.h>
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<maxargs; nargs++){
while(*s!='\0' && utfrune(sep, *s)!=nil)
*s++ = '\0';
if(*s == '\0')
break;
args[nargs] = s;
s = etoken(s, sep);
}
return nargs;
}
/sys/src/ape/lib/utf/lib9.h 664 sys sys 1070327090 330
#include <string.h>
#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}\
</sys/src/cmd/mksyslib
CFLAGS=-TVwc -D_POSIX_SOURCE -D_PLAN9_SOURCE -D_BSD_EXTENSION
/sys/src/ape/lib/utf/rerrstr.c 664 bootes sys 1363034061 200
#undef _BSD_EXTENSION
#include "../9/libc.h"
void
rerrstr(char *buf, uint nbuf)
{
char tmp[ERRMAX];
tmp[0] = 0;
errstr(tmp, sizeof tmp);
utfecpy(buf, buf+nbuf, tmp);
errstr(tmp, sizeof tmp);
}
/sys/src/ape/lib/utf/rune.c 664 sys sys 1369166818 3964
/*
* 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 <stdarg.h>
#include <string.h>
#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<<Bitx)-1, /* 0011 1111 */
Testx = Maskx ^ 0xFF, /* 1100 0000 */
Bad = Runeerror,
};
int
chartorune(Rune *rune, char *str)
{
int c, c1, c2, c3;
long l;
/*
* one character sequence
* 00000-0007F => 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 <stdarg.h>
#include <string.h>
#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 <stdarg.h>
#include <string.h>
#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 <stdarg.h>
#include <string.h>
#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 <stdarg.h>
#include <string.h>
#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 <stdarg.h>
#include <string.h>
#include <stdlib.h>
#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 <stdarg.h>
#include <string.h>
#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 <stdarg.h>
#include <string.h>
#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 <stdarg.h>
#include <string.h>
#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 <stdarg.h>
#include <string.h>
#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 <stdarg.h>
#include <string.h>
#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 <stdarg.h>
#include <string.h>
#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 <stdarg.h>
#include <string.h>
#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 <stdarg.h>
#include <string.h>
#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 <stdarg.h>
#include <string.h>
#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 <stdarg.h>
#include <string.h>
#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 <stdarg.h>
#include <string.h>
#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 <stdarg.h>
#include <string.h>
#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 <stdarg.h>
#include <string.h>
#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 <stdarg.h>
#include <string.h>
#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 <error.h>
#include <stdio.h>
#include <libv.h>
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 <libv.h>
#include <string.h>
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 <fio.h>
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 <libv.h>
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 <libv.h>
min(int a, int b)
{
return (a<b? a: b);
}
/sys/src/ape/lib/v/mkfile 664 sys sys 1369847600 240
APE=/sys/src/ape
<$APE/config
LIB=/$objtype/lib/ape/libv.a
OFILES=getpass.$O\
tty.$O\
rand.$O\
nrand.$O\
getfields.$O\
min.$O\
max.$O\
error.$O\
nap.$O
</sys/src/cmd/mksyslib
CFLAGS=-TVwc
%.$O: $FAMILY/%.c
$CC $CFLAGS $prereq
/sys/src/ape/lib/v/nap.c 664 sys sys 1369847690 196
extern int _SLEEP(long);
/*
* from nap(2), 8th edition. assume 60Hz
* won't overflow. n can be no bigger than 2*60.
*
* obsolete.
*/
int
nap(int n)
{
_SLEEP((n*16667)/1000);
return 0;
}
/sys/src/ape/lib/v/nrand.c 664 sys sys 1367613437 262
#include <stdlib.h>
#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 <stdio.h>
#include <signal.h>
#include <limits.h>
#include <libv.h>
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 <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <libv.h>
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 <stdlib.h>
#include <libv.h>
/*
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
|