# ifndef FUNCDEF # define INCLUDE_FILE_IO # define INCLUDE_CTYPE # include "kfun.h" # include "path.h" # include "editor.h" # endif # ifdef FUNCDEF FUNCDEF("editor", kf_editor, pt_editor) # else char pt_editor[] = { C_TYPECHECKED | C_VARARGS | C_STATIC, T_STRING, 1, T_STRING }; /* * NAME: kfun->editor() * DESCRIPTION: handle an editor command */ int kf_editor(f, nargs) register frame *f; int nargs; { object *obj; string *str; obj = f->obj; if (obj->count == 0) { error("editor() from destructed object"); } if (obj->flags & O_USER) { error("editor() from user object"); } if (!(obj->flags & O_EDITOR)) { ed_new(obj); } if (nargs == 0) { (--f->sp)->type = T_INT; f->sp->u.number = 0; } else { str = ed_command(obj, f->sp->u.string->text); str_del(f->sp->u.string); if (str != (string *) NULL) { str_ref(f->sp->u.string = str); } else { f->sp->type = T_INT; f->sp->u.number = 0; } } return 0; } # endif # ifdef FUNCDEF FUNCDEF("query_editor", kf_query_editor, pt_query_editor) # else char pt_query_editor[] = { C_TYPECHECKED | C_STATIC, T_STRING, 1, T_OBJECT }; /* * NAME: kfun->query_editor() * DESCRIPTION: query the editing status of an object */ int kf_query_editor(f) register frame *f; { object *obj; char *status; obj = &otable[f->sp->oindex]; if (obj->flags & O_EDITOR) { status = ed_status(obj); f->sp->type = T_STRING; str_ref(f->sp->u.string = str_new(status, (long) strlen(status))); } else { f->sp->type = T_INT; f->sp->u.number = 0; } return 0; } # endif # ifdef FUNCDEF FUNCDEF("save_object", kf_save_object, pt_save_object) # else typedef struct { int fd; /* save/restore file descriptor */ char *buffer; /* save/restore buffer */ unsigned int bufsz; /* size of save/restore buffer */ uindex narrays; /* number of arrays/mappings encountered */ } savecontext; /* * NAME: put() * DESCRIPTION: output a number of characters */ static void put(x, buf, len) register savecontext *x; register char *buf; register unsigned int len; { register unsigned int chunk; while (x->bufsz + len > BUF_SIZE) { chunk = BUF_SIZE - x->bufsz; memcpy(x->buffer + x->bufsz, buf, chunk); write(x->fd, x->buffer, BUF_SIZE); buf += chunk; len -= chunk; x->bufsz = 0; } if (len > 0) { memcpy(x->buffer + x->bufsz, buf, len); x->bufsz += len; } } /* * NAME: save_string() * DESCRIPTION: save a string */ static void save_string(x, str) savecontext *x; string *str; { char buf[STRINGSZ]; register char *p, *q, c; register unsigned short len; register unsigned int size; p = str->text; q = buf; *q++ = '"'; for (len = str->len, size = 1; len > 0; --len, size++) { if (size >= STRINGSZ - 2) { put(x, q = buf, size); size = 0; } switch (c = *p++) { case '\0': c = '0'; break; case BEL: c = 'a'; break; case BS: c = 'b'; break; case HT: c = 't'; break; case LF: c = 'n'; break; case VT: c = 'v'; break; case FF: c = 'f'; break; case CR: c = 'r'; break; case '"': case '\\': break; default: /* ordinary character */ *q++ = c; continue; } /* escaped character */ *q++ = '\\'; size++; *q++ = c; } *q++ = '"'; put(x, buf, size + 1); } static void save_mapping P((savecontext*, array*)); /* * NAME: save_array() * DESCRIPTION: save an array */ static void save_array(x, a) register savecontext *x; array *a; { char buf[16]; register uindex i; register value *v; xfloat flt; i = arr_put(a); if (i < x->narrays) { /* same as some previous array */ sprintf(buf, "#%u", i); put(x, buf, strlen(buf)); return; } x->narrays++; sprintf(buf, "({%d|", a->size); put(x, buf, strlen(buf)); for (i = a->size, v = d_get_elts(a); i > 0; --i, v++) { switch (v->type) { case T_INT: sprintf(buf, "%ld", (long) v->u.number); put(x, buf, strlen(buf)); break; case T_FLOAT: VFLT_GET(v, flt); flt_ftoa(&flt, buf); put(x, buf, strlen(buf)); sprintf(buf, "=%04x%08lx", flt.high, (long) flt.low); put(x, buf, 13); break; case T_STRING: save_string(x, v->u.string); break; case T_OBJECT: put(x, "0", 1); break; case T_ARRAY: save_array(x, v->u.array); break; case T_MAPPING: save_mapping(x, v->u.array); break; } put(x, ",", 1); } put(x, "})", 2); } /* * NAME: save_mapping() * DESCRIPTION: save a mapping */ static void save_mapping(x, a) register savecontext *x; array *a; { char buf[16]; register uindex i, n; register value *v; xfloat flt; i = arr_put(a); if (i < x->narrays) { /* same as some previous mapping */ sprintf(buf, "@%u", i); put(x, buf, strlen(buf)); return; } x->narrays++; map_compact(a); /* * skip index/value pairs of which either is an object */ for (i = n = a->size >> 1, v = d_get_elts(a); i > 0; --i) { if (v->type == T_OBJECT) { /* skip object index */ --n; v += 2; continue; } v++; if (v->type == T_OBJECT) { /* skip object value */ --n; } v++; } sprintf(buf, "([%d|", n); put(x, buf, strlen(buf)); for (i = a->size >> 1, v = a->elts; i > 0; --i) { if (v[0].type == T_OBJECT || v[1].type == T_OBJECT) { v += 2; continue; } switch (v->type) { case T_INT: sprintf(buf, "%ld", (long) v->u.number); put(x, buf, strlen(buf)); break; case T_FLOAT: VFLT_GET(v, flt); flt_ftoa(&flt, buf); put(x, buf, strlen(buf)); sprintf(buf, "=%04x%08lx", flt.high, (long) flt.low); put(x, buf, 13); break; case T_STRING: save_string(x, v->u.string); break; case T_ARRAY: save_array(x, v->u.array); break; case T_MAPPING: save_mapping(x, v->u.array); break; } put(x, ":", 1); v++; switch (v->type) { case T_INT: sprintf(buf, "%ld", (long) v->u.number); put(x, buf, strlen(buf)); break; case T_FLOAT: VFLT_GET(v, flt); flt_ftoa(&flt, buf); put(x, buf, strlen(buf)); sprintf(buf, "=%04x%08lx", flt.high, (long) flt.low); put(x, buf, 13); break; case T_STRING: save_string(x, v->u.string); break; case T_ARRAY: save_array(x, v->u.array); break; case T_MAPPING: save_mapping(x, v->u.array); break; } put(x, ",", 1); v++; } put(x, "])", 2); } char pt_save_object[] = { C_TYPECHECKED | C_STATIC, T_VOID, 1, T_STRING }; /* * NAME: kfun->save_object() * DESCRIPTION: save the variables of the current object */ int kf_save_object(f) register frame *f; { static unsigned short count; register unsigned short i, j, nvars; register value *var; register dvardef *v; register control *ctrl; register string *str; register dinherit *inh; register dataspace *data; char file[STRINGSZ], buf[16], tmp[STRINGSZ + 8], *_tmp; savecontext x; xfloat flt; _tmp = path_string(f->sp->u.string->text, f->sp->u.string->len); if (_tmp == (char *) NULL) { return 1; } strcpy(tmp, _tmp); if ((_tmp=path_file(tmp)) == (char *) NULL) { return 1; } strcpy(file, _tmp); /* * First save in a different file in the same directory, so a possibly * existing old instance will not be lost if something goes wrong. */ i_add_ticks(f, 2000); /* arbitrary */ _tmp = strrchr(tmp, '/'); _tmp = (_tmp == (char *) NULL) ? tmp : _tmp + 1; sprintf(_tmp, "_tmp%04x", ++count); _tmp = path_file(tmp); x.fd = open(_tmp, O_CREAT | O_TRUNC | O_WRONLY | O_BINARY, 0664); if (x.fd < 0) { error("Cannot create temporary save file \"/%s\"", tmp); } x.buffer = ALLOCA(char, BUF_SIZE); x.bufsz = 0; ctrl = f->ctrl; data = f->data; x.narrays = 0; nvars = 0; for (i = ctrl->ninherits, inh = ctrl->inherits; i > 0; --i, inh++) { if (inh->varoffset == nvars) { /* * This is the program that has the next variables in the object. * Save non-static variables. */ ctrl = o_control(inh->obj); for (j = ctrl->nvardefs, v = d_get_vardefs(ctrl); j > 0; --j, v++) { var = d_get_variable(data, nvars); if (!(v->class & C_STATIC) && var->type != T_OBJECT && (var->type != T_INT || var->u.number != 0) && (var->type != T_FLOAT || !VFLT_ISZERO(var))) { /* * don't save object values or 0 */ str = d_get_strconst(ctrl, v->inherit, v->index); put(&x, str->text, str->len); put(&x, " ", 1); switch (var->type) { case T_INT: sprintf(buf, "%ld", (long) var->u.number); put(&x, buf, strlen(buf)); break; case T_FLOAT: VFLT_GET(var, flt); flt_ftoa(&flt, buf); put(&x, buf, strlen(buf)); sprintf(buf, "=%04x%08lx", flt.high, (long) flt.low); put(&x, buf, 13); break; case T_STRING: save_string(&x, var->u.string); break; case T_ARRAY: save_array(&x, var->u.array); break; case T_MAPPING: save_mapping(&x, var->u.array); break; } put(&x, "\012", 1); /* LF */ } nvars++; } } } arr_clear(); if (x.bufsz > 0 && write(x.fd, x.buffer, x.bufsz) != x.bufsz) { close(x.fd); AFREE(x.buffer); unlink(_tmp); error("Cannot write to temporary save file \"/%s\"", tmp); } close(x.fd); AFREE(x.buffer); unlink(file); if (rename(_tmp, file) < 0) { unlink(_tmp); error("Cannot rename temporary save file to \"/%s\"", path_unfile(file)); } str_del(f->sp->u.string); f->sp->type = T_INT; f->sp->u.number = 0; return 0; } # endif # ifdef FUNCDEF FUNCDEF("restore_object", kf_restore_object, pt_restore_object) # else # define ACHUNKSZ 16 typedef struct _achunk_ { value a[ACHUNKSZ]; /* chunk of arrays */ struct _achunk_ *next; /* next in list */ } achunk; typedef struct { char *file; /* current restore file */ int line; /* current line number */ frame *f; /* interpreter frame */ achunk *alist; /* list of array chunks */ int achunksz; /* size of current array chunk */ uindex narrays; /* # of arrays/mappings */ } restcontext; /* * NAME: achunk->put() * DESCRIPTION: put an array into the array chunks */ static void ac_put(x, type, a) register restcontext *x; short type; array *a; { if (x->achunksz == ACHUNKSZ) { register achunk *l; l = ALLOC(achunk, 1); l->next = x->alist; x->alist = l; x->achunksz = 0; } x->alist->a[x->achunksz].type = type; x->alist->a[x->achunksz++].u.array = a; x->narrays++; } /* * NAME: achunk->get() * DESCRIPTION: get an array from the array chunks */ static value *ac_get(x, n) restcontext *x; register uindex n; { register uindex sz; register achunk *l; n = x->narrays - n; for (sz = x->achunksz, l = x->alist; n > sz; l = l->next, sz = ACHUNKSZ) { n -= sz; } return &l->a[sz - n]; } /* * NAME: achunk->clear() * DESCRIPTION: clear the array chunks */ static void ac_clear(x) restcontext *x; { register achunk *l, *f; for (l = x->alist; l != (achunk *) NULL; ) { f = l; l = l->next; FREE(f); } } /* * NAME: restore_error() * DESCRIPTION: handle an error while restoring */ static void restore_error(x, err) restcontext *x; char *err; { error("Format error in \"/%s\", line %d: %s", x->file, x->line, err); } /* * NAME: restore_int() * DESCRIPTION: restore an integer */ static char *restore_int(x, buf, val) restcontext *x; char *buf; value *val; { char *p; val->u.number = strtol(buf, &p, 10); if (p == buf) { restore_error(x, "digit expected"); } val->type = T_INT; return p; } /* * NAME: restore_number() * DESCRIPTION: restore a number */ static char *restore_number(x, buf, val) register restcontext *x; char *buf; register value *val; { register char *p; register int i; char *q; xfloat flt; bool isfloat; val->u.number = strtol(q = buf, &buf, 10); if (q == buf) { restore_error(x, "digit expected"); } isfloat = FALSE; p = buf; if (*p == '.') { isfloat = TRUE; while (isdigit(*++p)) ; } if (*p == 'e' || *p == 'E') { isfloat = TRUE; p++; if (*p == '+' || *p == '-') { p++; } if (!isdigit(*p)) { restore_error(x, "digit expected"); } while (isdigit(*++p)) ; } if (*p == '=') { flt.high = flt.low = 0; for (i = 4; i > 0; --i) { if (!isxdigit(*++p)) { restore_error(x, "hexadecimal digit expected"); } flt.high <<= 4; if (isdigit(*p)) { flt.high += *p - '0'; } else { flt.high += toupper(*p) + 10 - 'A'; } } if ((flt.high & 0x7ff0) == 0x7ff0) { restore_error(x, "illegal exponent"); } for (i = 8; i > 0; --i) { if (!isxdigit(*++p)) { restore_error(x, "hexadecimal digit expected"); } flt.low <<= 4; if (isdigit(*p)) { flt.low += *p - '0'; } else { flt.low += toupper(*p) + 10 - 'A'; } } val->type = T_FLOAT; VFLT_PUT(val, flt); return p + 1; } else if (isfloat) { if (!flt_atof(&q, &flt)) { restore_error(x, "float too large"); } val->type = T_FLOAT; VFLT_PUT(val, flt); return p; } val->type = T_INT; return p; } /* * NAME: restore_string() * DESCRIPTION: restore a string */ static char *restore_string(x, buf, val) restcontext *x; register char *buf; value *val; { register char *p, *q; if (*buf++ != '"') { restore_error(x, "'\"' expected"); } for (p = q = buf; *p != '"'; p++) { if (*p == '\\') { switch (*++p) { case '0': *q++ = '\0'; continue; case 'a': *q++ = BEL; continue; case 'b': *q++ = BS; continue; case 't': *q++ = HT; continue; case 'n': *q++ = LF; continue; case 'v': *q++ = VT; continue; case 'f': *q++ = FF; continue; case 'r': *q++ = CR; continue; } } if (*p == '\0' || *p == LF) { restore_error(x, "unterminated string"); } *q++ = *p; } val->u.string = str_new(buf, (long) q - (long) buf); val->type = T_STRING; return p + 1; } static char *restore_value P((restcontext*, char*, value*)); static char *restore_mapping P((restcontext*, char*, value*)); /* * NAME: restore_array() * DESCRIPTION: restore an array */ static char *restore_array(x, buf, val) register restcontext *x; register char *buf; value *val; { register unsigned short i; register value *v; array *a; /* match ({ */ if (*buf++ != '(' || *buf++ != '{') { restore_error(x, "'({' expected"); } /* get array size */ buf = restore_int(x, buf, val); if (*buf++ != '|') { restore_error(x, "'|' expected"); } ac_put(x, T_ARRAY, a = arr_new(x->f->data, (long) val->u.number)); for (i = a->size, v = a->elts; i > 0; --i) { (v++)->type = T_INT; } i = a->size; v = a->elts; if (ec_push((ec_ftn) NULL)) { arr_ref(a); arr_del(a); error((char *) NULL); /* pass on the error */ } /* restore the values */ while (i > 0) { buf = restore_value(x, buf, v); i_ref_value(v++); if (*buf++ != ',') { restore_error(x, "',' expected"); } --i; } /* match }) */ if (*buf++ != '}' || *buf++ != ')') { restore_error(x, "'})' expected"); } ec_pop(); val->type = T_ARRAY; val->u.array = a; return buf; } /* * NAME: restore_mapping() * DESCRIPTION: restore a mapping */ static char *restore_mapping(x, buf, val) register restcontext *x; register char *buf; value *val; { register unsigned short i; register value *v; array *a; /* match ([ */ if (*buf++ != '(' || *buf++ != '[') { restore_error(x, "'([' expected"); } /* get mapping size */ buf = restore_int(x, buf, val); if (*buf++ != '|') { restore_error(x, "'|' expected"); } ac_put(x, T_MAPPING, a = map_new(x->f->data, (long) val->u.number << 1)); for (i = a->size, v = a->elts; i > 0; --i) { (v++)->type = T_INT; } i = a->size; v = a->elts; if (ec_push((ec_ftn) NULL)) { arr_ref(a); arr_del(a); error((char *) NULL); /* pass on the error */ } /* restore the values */ while (i > 0) { buf = restore_value(x, buf, v); i_ref_value(v++); if (*buf++ != ':') { restore_error(x, "':' expected"); } buf = restore_value(x, buf, v); i_ref_value(v++); if (*buf++ != ',') { restore_error(x, "',' expected"); } i -= 2; } /* match ]) */ if (*buf++ != ']' || *buf++ != ')') { restore_error(x, "'])' expected"); } map_sort(a); ec_pop(); val->type = T_MAPPING; val->u.array = a; return buf; } /* * NAME: restore_value() * DESCRIPTION: restore a value */ static char *restore_value(x, buf, val) register restcontext *x; register char *buf; register value *val; { switch (*buf) { case '"': return restore_string(x, buf, val); case '(': if (buf[1] == '{') { return restore_array(x, buf, val); } else { return restore_mapping(x, buf, val); } case '#': buf = restore_int(x, buf + 1, val); if ((uindex) val->u.number >= x->narrays) { restore_error(x, "bad array reference"); } *val = *ac_get(x, (uindex) val->u.number); if (val->type != T_ARRAY) { restore_error(x, "bad array reference"); } return buf; case '@': buf = restore_int(x, buf + 1, val); if ((uindex) val->u.number >= x->narrays) { restore_error(x, "bad mapping reference"); } *val = *ac_get(x, (uindex) val->u.number); if (val->type != T_MAPPING) { restore_error(x, "bad mapping reference"); } return buf; default: return restore_number(x, buf, val); } } char pt_restore_object[] = { C_TYPECHECKED | C_STATIC, T_INT, 1, T_STRING }; /* * NAME: kfun->restore_object() * DESCRIPTION: restore the variables of the current object from file */ int kf_restore_object(f) register frame *f; { struct stat sbuf; register int i, j; register unsigned short nvars, checkpoint; register char *buf; register value *var; register dvardef *v; register control *ctrl; register dataspace *data; register dinherit *inh; restcontext x; object *obj; int fd; char *buffer, *name; bool pending; obj = f->obj; x.file = path_file(path_string(f->sp->u.string->text, f->sp->u.string->len)); if (x.file == (char *) NULL) { return 1; } i_add_ticks(f, 2000); /* arbitrary */ str_del(f->sp->u.string); f->sp->type = T_INT; f->sp->u.number = 0; fd = open(x.file, O_RDONLY | O_BINARY, 0); if (fd < 0) { /* restore failed */ return 0; } fstat(fd, &sbuf); if ((sbuf.st_mode & S_IFMT) != S_IFREG) { /* not a save file */ close(fd); return 0; } buffer = ALLOCA(char, sbuf.st_size + 1); if (read(fd, buffer, (unsigned int) sbuf.st_size) != sbuf.st_size) { /* read failed (should never happen, but...) */ close(fd); AFREE(buffer); return 0; } buffer[sbuf.st_size] = '\0'; close(fd); /* * First, initialize all non-static variables that do not hold object * values to 0. */ ctrl = o_control(obj); data = o_dataspace(obj); nvars = 0; for (i = ctrl->ninherits, inh = ctrl->inherits; i > 0; --i, inh++) { if (inh->varoffset == nvars) { /* * This is the program that has the next variables in the object. */ ctrl = o_control(inh->obj); for (j = ctrl->nvardefs, v = d_get_vardefs(ctrl); j > 0; --j, v++) { var = d_get_variable(data, nvars); if (!(v->class & C_STATIC) && var->type != T_OBJECT) { d_assign_var(data, var, (v->type == T_FLOAT) ? &zero_float : &zero_value); } nvars++; } } } x.line = 1; x.f = f; x.alist = (achunk *) NULL; x.achunksz = ACHUNKSZ; x.narrays = 0; buf = buffer; pending = FALSE; if (ec_push((ec_ftn) NULL)) { /* error; clean up */ arr_clear(); ac_clear(&x); AFREE(buffer); error((char *) NULL); /* pass on error */ } for (;;) { var = data->variables; nvars = 0; for (i = ctrl->ninherits, inh = ctrl->inherits; i > 0; --i, inh++) { if (inh->varoffset == nvars) { /* * Restore non-static variables. */ ctrl = inh->obj->ctrl; for (j = ctrl->nvardefs, v = ctrl->vardefs; j > 0; --j, v++) { if (pending && nvars == checkpoint) { /* * The saved variable is not in this object. * Skip it. */ buf = strchr(buf, LF); if (buf == (char *) NULL) { restore_error(&x, "'\\n' expected"); } buf++; x.line++; pending = FALSE; } if (!pending) { /* * get a new variable name from the save file */ while (*buf == '#') { /* skip comment */ buf = strchr(buf, LF); if (buf == (char *) NULL) { restore_error(&x, "'\\n' expected"); } buf++; x.line++; } if (*buf == '\0') { /* end of file */ break; } name = buf; if (!isalpha(*buf) && *buf != '_') { restore_error(&x, "alphanumeric expected"); } do { buf++; } while (isalnum(*buf) || *buf == '_'); if (*buf != ' ') { restore_error(&x, "' ' expected"); } *buf++ = '\0'; /* terminate name */ pending = TRUE; /* start checking variables */ checkpoint = nvars; /* from here */ } if (!(v->class & C_STATIC) && strcmp(name, d_get_strconst(ctrl, v->inherit, v->index)->text) == 0) { value tmp; /* * found the proper variable to restore */ buf = restore_value(&x, buf, &tmp); if (v->type != tmp.type && v->type != T_MIXED && conf_typechecking() && (tmp.type != T_INT || tmp.u.number != 0 || v->type == T_FLOAT) && (tmp.type != T_ARRAY || (v->type & T_REF) == 0)) { i_ref_value(&tmp); i_del_value(&tmp); restore_error(&x, "value has wrong type"); } d_assign_var(data, var, &tmp); if (*buf++ != LF) { restore_error(&x, "'\\n' expected"); } x.line++; pending = FALSE; } var++; nvars++; } if (!pending && *buf == '\0') { /* * finished restoring */ ec_pop(); arr_clear(); ac_clear(&x); AFREE(buffer); f->sp->u.number = 1; return 0; } } } } } # endif # ifdef FUNCDEF FUNCDEF("write_file", kf_write_file, pt_write_file) # else char pt_write_file[] = { C_TYPECHECKED | C_VARARGS | C_STATIC, T_INT, 3, T_STRING, T_STRING, T_INT }; /* * NAME: kfun->write_file() * DESCRIPTION: write a string to a file */ int kf_write_file(f, nargs) register frame *f; int nargs; { struct stat sbuf; register Int l; char *file; int fd; switch (nargs) { case 0: case 1: return -1; case 2: l = 0; break; case 3: l = (f->sp++)->u.number; break; } file = path_file(path_string(f->sp[1].u.string->text, f->sp[1].u.string->len)); if (file == (char *) NULL) { return 1; } i_add_ticks(f, 1000 + (Int) 2 * f->sp->u.string->len); str_del(f->sp[1].u.string); f->sp[1].type = T_INT; f->sp[1].u.number = 0; fd = open(file, O_CREAT | O_WRONLY | O_BINARY, 0664); if (fd < 0) { str_del((f->sp++)->u.string); return 0; } fstat(fd, &sbuf); if (l == 0) { /* the default is to append to the file */ l = sbuf.st_size; } else if (l < 0) { /* offset from the end of the file */ l += sbuf.st_size; } if (l < 0 || l > sbuf.st_size || lseek(fd, l, SEEK_SET) < 0) { /* bad offset */ close(fd); str_del((f->sp++)->u.string); return 0; } if (write(fd, f->sp->u.string->text, f->sp->u.string->len) == f->sp->u.string->len) { /* succesful write */ f->sp[1].u.number = 1; } close(fd); str_del((f->sp++)->u.string); return 0; } # endif # ifdef FUNCDEF FUNCDEF("read_file", kf_read_file, pt_read_file) # else char pt_read_file[] = { C_TYPECHECKED | C_VARARGS | C_STATIC, T_STRING, 3, T_STRING, T_INT, T_INT }; /* * NAME: kfun->read_file() * DESCRIPTION: read a string from file */ int kf_read_file(f, nargs) register frame *f; int nargs; { struct stat sbuf; register Int l, size; char *file; int fd; l = 0; size = 0; switch (nargs) { case 3: size = (f->sp++)->u.number; case 2: l = (f->sp++)->u.number; /* offset in file */ case 1: break; case 0: return -1; } file = path_file(path_string(f->sp->u.string->text, f->sp->u.string->len)); if (file == (char *) NULL) { return 1; } str_del(f->sp->u.string); f->sp->type = T_INT; f->sp->u.number = 0; if (size < 0) { /* size has to be >= 0 */ return 3; } i_add_ticks(f, 1000); fd = open(file, O_RDONLY | O_BINARY, 0); if (fd < 0) { /* cannot open file */ return 0; } fstat(fd, &sbuf); if ((sbuf.st_mode & S_IFMT) != S_IFREG) { /* not a plain file */ close(fd); return 0; } if (l != 0) { /* * seek in file */ if (l < 0) { /* offset from end of file */ l += sbuf.st_size; } if (l < 0 || l > sbuf.st_size || lseek(fd, l, SEEK_SET) < 0) { /* bad seek */ close(fd); return 0; } sbuf.st_size -= l; } if (size == 0 || size > sbuf.st_size) { size = sbuf.st_size; } if (ec_push((ec_ftn) NULL)) { close(fd); error((char *) NULL); /* pass on error */ } else { str_ref(f->sp->u.string = str_new((char *) NULL, size)); f->sp->type = T_STRING; ec_pop(); } if (size > 0 && read(fd, f->sp->u.string->text, (unsigned int) size) != size) { /* read failed (should never happen, but...) */ close(fd); error("Read failed in read_file()"); } close(fd); i_add_ticks(f, 2 * size); return 0; } # endif # ifdef FUNCDEF FUNCDEF("rename_file", kf_rename_file, pt_rename_file) # else char pt_rename_file[] = { C_TYPECHECKED | C_STATIC, T_INT, 2, T_STRING, T_STRING }; /* * NAME: kfun->rename_file() * DESCRIPTION: rename a file */ int kf_rename_file(f) register frame *f; { char buf[STRINGSZ], *file; file = path_file(path_string(f->sp[1].u.string->text, f->sp[1].u.string->len)); if (file == (char *) NULL) { return 1; } strcpy(buf, file); file = path_file(path_string(f->sp->u.string->text, f->sp->u.string->len)); if (file == (char *) NULL) { return 2; } i_add_ticks(f, 1000); str_del((f->sp++)->u.string); str_del(f->sp->u.string); f->sp->type = T_INT; f->sp->u.number = (access(buf, W_OK) >= 0 && access(file, F_OK) < 0 && rename(buf, file) >= 0); return 0; } # endif # ifdef FUNCDEF FUNCDEF("remove_file", kf_remove_file, pt_remove_file) # else char pt_remove_file[] = { C_TYPECHECKED | C_STATIC, T_INT, 1, T_STRING }; /* * NAME: kfun->remove_file() * DESCRIPTION: remove a file */ int kf_remove_file(f) register frame *f; { char *file; file = path_file(path_string(f->sp->u.string->text, f->sp->u.string->len)); if (file == (char *) NULL) { return 1; } i_add_ticks(f, 1000); str_del(f->sp->u.string); f->sp->type = T_INT; f->sp->u.number = (access(file, W_OK) >= 0 && unlink(file) >= 0); return 0; } # endif # ifdef FUNCDEF FUNCDEF("make_dir", kf_make_dir, pt_make_dir) # else char pt_make_dir[] = { C_TYPECHECKED | C_STATIC, T_INT, 1, T_STRING }; /* * NAME: kfun->make_dir() * DESCRIPTION: create a directory */ int kf_make_dir(f) register frame *f; { char *file; file = path_file(path_string(f->sp->u.string->text, f->sp->u.string->len)); if (file == (char *) NULL) { return 1; } i_add_ticks(f, 1000); str_del(f->sp->u.string); f->sp->type = T_INT; f->sp->u.number = (mkdir(file, 0775) >= 0); return 0; } # endif # ifdef FUNCDEF FUNCDEF("remove_dir", kf_remove_dir, pt_remove_dir) # else char pt_remove_dir[] = { C_TYPECHECKED | C_STATIC, T_INT, 1, T_STRING }; /* * NAME: kfun->remove_dir() * DESCRIPTION: remove an empty directory */ int kf_remove_dir(f) register frame *f; { char *file; file = path_file(path_string(f->sp->u.string->text, f->sp->u.string->len)); if (file == (char *) NULL) { return 1; } i_add_ticks(f, 1000); str_del(f->sp->u.string); f->sp->type = T_INT; f->sp->u.number = (rmdir(file) >= 0); return 0; } # endif # ifdef FUNCDEF FUNCDEF("get_dir", kf_get_dir, pt_get_dir) # else /* * NAME: match() * DESCRIPTION: match a regular expression */ static int match(pat, text) register char *pat, *text; { bool found, reversed; int matched; for (;;) { switch (*pat) { case '\0': /* end of pattern */ return (*text == '\0'); case '?': /* any single character */ if (*text == '\0') { return 0; } break; case '*': /* any string */ pat++; if (*pat == '\0') { /* quick check */ return 1; } do { matched = match(pat, text); if (matched != 0) { return matched; } } while (*text++ != '\0'); return -1; case '[': /* character class */ pat++; found = FALSE; if (*pat == '^') { reversed = TRUE; pat++; } else { reversed = FALSE; } for (;;) { if (*pat == '\0') { /* missing ']' */ return 0; } if (*pat == ']') { /* end of character class */ if (found != reversed) { break; } return 0; } if (*pat == '\\') { /* escaped char (should be ']') */ ++pat; if (*pat == '\0') { return 0; } } if (pat[1] == '-') { /* character range */ pat += 2; if (*pat == '\0') { return 0; } if (UCHAR(*text) >= UCHAR(pat[-2]) && UCHAR(*text) <= UCHAR(pat[0])) { found = TRUE; } } else if (*pat == *text) { /* matched single character */ found = TRUE; } pat++; } break; case '\\': /* escaped character */ if (*++pat == '\0') { /* malformed pattern */ return 0; } default: /* ordinary character */ if (*pat != *text) { return 0; } } pat++; text++; } } typedef struct _fileinfo_ { string *name; /* file name */ Int size; /* file size */ Int time; /* file time */ } fileinfo; /* * NAME: getinfo() * DESCRIPTION: get info about a file */ static bool getinfo(path, file, finf) char *path, *file; register fileinfo *finf; { struct stat sbuf; if (stat(path_file(path), &sbuf) < 0) { /* * the file does not exist */ return FALSE; } str_ref(finf->name = str_new(file, (long) strlen(file))); if ((sbuf.st_mode & S_IFMT) == S_IFDIR) { finf->size = -2; /* special value for directory */ } else { finf->size = sbuf.st_size; } finf->time = sbuf.st_mtime; return TRUE; } static int cmp P((cvoid*, cvoid*)); /* * NAME: cmp() * DESCRIPTION: compare two file info structs */ static int cmp(cv1, cv2) cvoid *cv1, *cv2; { return strcmp(((fileinfo *) cv1)->name->text, ((fileinfo *) cv2)->name->text); } char pt_get_dir[] = { C_TYPECHECKED | C_STATIC, T_MIXED | (2 << REFSHIFT), 1, T_STRING }; # define FILEINFO_CHUNK 128 /* * NAME: kfun->get_dir() * DESCRIPTION: get directory filelist + info */ int kf_get_dir(f) frame *f; { register unsigned int i, nfiles, ftabsz; register fileinfo *ftable; char *file, *dir, *pat, buf[STRINGSZ]; fileinfo finf; array *a; file = path_string(f->sp->u.string->text, f->sp->u.string->len); if (path_file(file) == (char *) NULL) { return 1; } strcpy(buf, file); pat = strrchr(buf, '/'); if (pat == (char *) NULL) { dir = "."; pat = buf; } else { /* separate directory and pattern */ dir = buf; *pat++ = '\0'; } ftable = ALLOCA(fileinfo, ftabsz = FILEINFO_CHUNK); nfiles = 0; if (strpbrk(pat, "?*[\\") == (char *) NULL && getinfo(file, pat, &ftable[0])) { /* * single file */ nfiles++; } else if (strcmp(dir, ".") == 0 || chdir(path_file(dir)) >= 0) { if (P_opendir(path_file("."))) { /* * read files from directory */ i = conf_array_size(); while (nfiles < i && (file=P_readdir()) != (char *) NULL) { file = path_unfile(file); if (match(pat, file) > 0 && getinfo(file, file, &finf)) { /* add file */ if (nfiles == ftabsz) { fileinfo *tmp; tmp = ALLOCA(fileinfo, ftabsz + FILEINFO_CHUNK); memcpy(tmp, ftable, ftabsz * sizeof(fileinfo)); ftabsz += FILEINFO_CHUNK; AFREE(ftable); ftable = tmp; } ftable[nfiles++] = finf; } } P_closedir(); } if (strcmp(dir, ".") != 0 && chdir(conf_base_dir()) < 0) { fatal("cannot chdir back to base dir"); } } /* prepare return value */ str_del(f->sp->u.string); f->sp->type = T_ARRAY; arr_ref(f->sp->u.array = a = arr_new(f->data, 3L)); a->elts[0].type = T_ARRAY; arr_ref(a->elts[0].u.array = arr_new(f->data, (long) nfiles)); a->elts[1].type = T_ARRAY; arr_ref(a->elts[1].u.array = arr_new(f->data, (long) nfiles)); a->elts[2].type = T_ARRAY; arr_ref(a->elts[2].u.array = arr_new(f->data, (long) nfiles)); i_add_ticks(f, 1000 + 5 * nfiles); if (nfiles != 0) { register value *n, *s, *t; qsort(ftable, nfiles, sizeof(fileinfo), cmp); n = a->elts[0].u.array->elts; s = a->elts[1].u.array->elts; t = a->elts[2].u.array->elts; for (i = nfiles; i > 0; --i, ftable++) { n->type = T_STRING; n->u.string = ftable->name; s->type = T_INT; s->u.number = ftable->size; t->type = T_INT; t->u.number = ftable->time; n++, s++, t++; } ftable -= nfiles; } AFREE(ftable); return 0; } # endif