2004-04-19 19:32:07 +00:00
|
|
|
#include <u.h>
|
|
|
|
#include <libc.h>
|
|
|
|
#include <bio.h>
|
|
|
|
#include <mach.h>
|
|
|
|
#define Extern
|
|
|
|
#include "acid.h"
|
|
|
|
#include "y.tab.h"
|
|
|
|
|
|
|
|
extern int __ifmt(Fmt*);
|
|
|
|
|
|
|
|
static Biobuf bioout;
|
|
|
|
static char* lm[16];
|
|
|
|
static int nlm;
|
|
|
|
static char* mtype;
|
|
|
|
|
|
|
|
static int attachfiles(int, char**);
|
|
|
|
int xfmt(Fmt*);
|
|
|
|
int isnumeric(char*);
|
|
|
|
void die(void);
|
|
|
|
void setcore(Fhdr*);
|
|
|
|
|
|
|
|
void
|
|
|
|
usage(void)
|
|
|
|
{
|
|
|
|
fprint(2, "usage: acid [-c core] [-l module] [-m machine] [-qrw] [-k] [pid] [file]\n");
|
|
|
|
exits("usage");
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
main(int argc, char *argv[])
|
|
|
|
{
|
|
|
|
Lsym *l;
|
|
|
|
Node *n;
|
|
|
|
char buf[128], *s;
|
|
|
|
int pid, i;
|
|
|
|
|
|
|
|
argv0 = argv[0];
|
|
|
|
pid = 0;
|
|
|
|
quiet = 1;
|
|
|
|
|
|
|
|
mtype = 0;
|
|
|
|
ARGBEGIN{
|
|
|
|
case 'A':
|
|
|
|
abort();
|
|
|
|
break;
|
|
|
|
case 'm':
|
|
|
|
mtype = ARGF();
|
|
|
|
break;
|
|
|
|
case 'w':
|
|
|
|
wtflag = 1;
|
|
|
|
break;
|
|
|
|
case 'l':
|
|
|
|
s = ARGF();
|
|
|
|
if(s == 0)
|
|
|
|
usage();
|
|
|
|
lm[nlm++] = s;
|
|
|
|
break;
|
|
|
|
case 'k':
|
|
|
|
kernel++;
|
|
|
|
break;
|
|
|
|
case 'q':
|
|
|
|
quiet = 0;
|
|
|
|
break;
|
|
|
|
case 'r':
|
|
|
|
pid = 1;
|
|
|
|
remote++;
|
|
|
|
kernel++;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
usage();
|
|
|
|
}ARGEND
|
|
|
|
|
|
|
|
fmtinstall('x', xfmt);
|
|
|
|
fmtinstall('Z', Zfmt);
|
|
|
|
fmtinstall('L', locfmt);
|
|
|
|
Binit(&bioout, 1, OWRITE);
|
|
|
|
bout = &bioout;
|
|
|
|
|
|
|
|
kinit();
|
|
|
|
initialising = 1;
|
|
|
|
pushfile(0);
|
|
|
|
loadvars();
|
|
|
|
installbuiltin();
|
|
|
|
|
|
|
|
if(mtype && machbyname(mtype) == 0)
|
|
|
|
print("unknown machine %s", mtype);
|
|
|
|
|
|
|
|
if (attachfiles(argc, argv) < 0)
|
|
|
|
varreg(); /* use default register set on error */
|
|
|
|
if(mach == nil)
|
|
|
|
mach = machcpu;
|
|
|
|
|
|
|
|
symhdr = nil; /* not supposed to use this anymore */
|
|
|
|
|
|
|
|
l = mkvar("acid");
|
|
|
|
l->v->set = 1;
|
|
|
|
l->v->type = TLIST;
|
|
|
|
l->v->store.u.l = nil;
|
|
|
|
|
|
|
|
loadmodule("/usr/local/plan9/acid/port");
|
|
|
|
for(i = 0; i < nlm; i++) {
|
|
|
|
if(access(lm[i], AREAD) >= 0)
|
|
|
|
loadmodule(lm[i]);
|
|
|
|
else {
|
|
|
|
sprint(buf, "/usr/local/plan9/acid/%s", lm[i]);
|
|
|
|
loadmodule(buf);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
userinit();
|
|
|
|
varsym();
|
|
|
|
|
|
|
|
l = look("acidmap");
|
|
|
|
if(l && l->proc) {
|
2004-04-19 23:03:46 +00:00
|
|
|
if(setjmp(err) == 0){
|
|
|
|
n = an(ONAME, ZN, ZN);
|
|
|
|
n->sym = l;
|
|
|
|
n = an(OCALL, n, ZN);
|
|
|
|
execute(n);
|
|
|
|
}
|
2004-04-19 19:32:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
interactive = 1;
|
|
|
|
initialising = 0;
|
|
|
|
line = 1;
|
|
|
|
|
|
|
|
notify(catcher);
|
|
|
|
|
|
|
|
for(;;) {
|
|
|
|
if(setjmp(err)) {
|
|
|
|
Binit(&bioout, 1, OWRITE);
|
|
|
|
unwind();
|
|
|
|
}
|
|
|
|
stacked = 0;
|
|
|
|
|
|
|
|
Bprint(bout, "acid; ");
|
|
|
|
|
|
|
|
if(yyparse() != 1)
|
|
|
|
die();
|
|
|
|
restartio();
|
|
|
|
|
|
|
|
unwind();
|
|
|
|
}
|
|
|
|
Bputc(bout, '\n');
|
|
|
|
exits(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
attachfiles(int argc, char **argv)
|
|
|
|
{
|
2004-04-19 23:03:46 +00:00
|
|
|
int pid;
|
2004-04-19 19:32:07 +00:00
|
|
|
char *s;
|
|
|
|
int i, omode;
|
|
|
|
Fhdr *hdr;
|
|
|
|
Lsym *l;
|
|
|
|
Value *v;
|
|
|
|
|
2004-04-19 23:03:46 +00:00
|
|
|
pid = 0;
|
2004-04-19 19:32:07 +00:00
|
|
|
interactive = 0;
|
|
|
|
if(setjmp(err))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Unix and Plan 9 differ on what the right order of pid, text, and core is.
|
|
|
|
* I never remember anyway. Let's just accept them in any order.
|
|
|
|
*/
|
|
|
|
omode = wtflag ? ORDWR : OREAD;
|
|
|
|
for(i=0; i<argc; i++){
|
|
|
|
if(isnumeric(argv[i])){
|
|
|
|
if(pid){
|
|
|
|
fprint(2, "already have pid %d; ignoring pid %d\n", pid, argv[i]);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if(corhdr){
|
|
|
|
fprint(2, "already have core %s; ignoring pid %d\n", corfil, pid);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
pid = atoi(argv[i]);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if((hdr = crackhdr(argv[i], omode)) == nil){
|
|
|
|
fprint(2, "crackhdr %s: %r\n", argv[i]);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
fprint(2, "%s: %s %s %s\n", argv[i], hdr->aname, hdr->mname, hdr->fname);
|
|
|
|
if(hdr->ftype == FCORE){
|
|
|
|
if(pid){
|
|
|
|
fprint(2, "already have pid %d; ignoring core %s\n", pid, argv[i]);
|
|
|
|
uncrackhdr(hdr);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if(corhdr){
|
|
|
|
fprint(2, "already have core %s; ignoring core %s\n", corfil, argv[i]);
|
|
|
|
uncrackhdr(hdr);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
corhdr = hdr;
|
|
|
|
corfil = argv[i];
|
|
|
|
}else{
|
|
|
|
if(symhdr){
|
|
|
|
fprint(2, "already have text %s; ignoring text %s\n", symfil, argv[i]);
|
|
|
|
uncrackhdr(hdr);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
symhdr = hdr;
|
|
|
|
symfil = argv[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(symhdr==nil){
|
|
|
|
symfil = "a.out";
|
|
|
|
if(pid){
|
|
|
|
if((s = proctextfile(pid)) != nil){
|
|
|
|
fprint(2, "pid %d: text %s\n", pid, s);
|
|
|
|
symfil = s;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* XXX pull command from core */
|
|
|
|
|
2004-04-19 19:34:22 +00:00
|
|
|
if((symhdr = crackhdr(symfil, omode)) == nil){
|
2004-04-19 19:32:07 +00:00
|
|
|
fprint(2, "crackhdr %s: %r\n", symfil);
|
|
|
|
symfil = nil;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(symhdr)
|
|
|
|
syminit(symhdr);
|
|
|
|
|
|
|
|
if(!mach)
|
|
|
|
mach = machcpu;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set up maps.
|
|
|
|
*/
|
|
|
|
symmap = allocmap();
|
|
|
|
cormap = allocmap();
|
|
|
|
if(symmap == nil || cormap == nil)
|
|
|
|
sysfatal("allocating maps: %r");
|
|
|
|
|
|
|
|
if(symhdr){
|
|
|
|
if(mapfile(symhdr, 0, symmap, nil) < 0)
|
|
|
|
fprint(2, "mapping %s: %r\n", symfil);
|
|
|
|
mapfile(symhdr, 0, cormap, nil);
|
|
|
|
}
|
|
|
|
|
|
|
|
l = mkvar("objtype");
|
|
|
|
v = l->v;
|
|
|
|
v->store.fmt = 's';
|
|
|
|
v->set = 1;
|
|
|
|
v->store.u.string = strnode(mach->name);
|
|
|
|
v->type = TSTRING;
|
|
|
|
|
|
|
|
l = mkvar("textfile");
|
|
|
|
v = l->v;
|
|
|
|
v->store.fmt = 's';
|
|
|
|
v->set = 1;
|
|
|
|
v->store.u.string = strnode(symfil ? symfil : "");
|
|
|
|
v->type = TSTRING;
|
|
|
|
|
|
|
|
l = mkvar("systype");
|
|
|
|
v = l->v;
|
|
|
|
v->store.fmt = 's';
|
|
|
|
v->set = 1;
|
|
|
|
v->store.u.string = strnode(symhdr ? symhdr->aname : "");
|
|
|
|
v->type = TSTRING;
|
|
|
|
|
|
|
|
l = mkvar("corefile");
|
|
|
|
v = l->v;
|
|
|
|
v->store.fmt = 's';
|
|
|
|
v->set = 1;
|
|
|
|
v->store.u.string = strnode(corfil ? corfil : "");
|
|
|
|
v->type = TSTRING;
|
|
|
|
|
|
|
|
if(pid)
|
|
|
|
sproc(pid);
|
|
|
|
if(corhdr)
|
|
|
|
setcore(corhdr);
|
|
|
|
varreg();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
setcore(Fhdr *hdr)
|
|
|
|
{
|
|
|
|
unmapproc(cormap);
|
|
|
|
unmapfile(corhdr, cormap);
|
|
|
|
free(correg);
|
|
|
|
correg = nil;
|
|
|
|
|
|
|
|
if(hdr == nil)
|
|
|
|
error("no core");
|
|
|
|
if(mapfile(hdr, 0, cormap, &correg) < 0)
|
|
|
|
error("mapfile %s: %r", hdr->filename);
|
|
|
|
corhdr = hdr;
|
|
|
|
corfil = hdr->filename;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
die(void)
|
|
|
|
{
|
|
|
|
Lsym *s;
|
|
|
|
List *f;
|
|
|
|
|
|
|
|
Bprint(bout, "\n");
|
|
|
|
|
|
|
|
s = look("proclist");
|
|
|
|
if(s && s->v->type == TLIST) {
|
|
|
|
for(f = s->v->store.u.l; f; f = f->next){
|
|
|
|
detachproc((int)f->store.u.ival);
|
|
|
|
Bprint(bout, "/bin/kill -9 %d\n", (int)f->store.u.ival);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
exits(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
userinit(void)
|
|
|
|
{
|
|
|
|
Lsym *l;
|
|
|
|
Node *n;
|
|
|
|
char buf[128], *p;
|
|
|
|
|
|
|
|
sprint(buf, "/usr/local/plan9/acid/%s", mach->name);
|
|
|
|
loadmodule(buf);
|
|
|
|
p = getenv("home");
|
|
|
|
if(p != 0) {
|
|
|
|
sprint(buf, "%s/lib/acid", p);
|
|
|
|
silent = 1;
|
|
|
|
loadmodule(buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
interactive = 0;
|
|
|
|
if(setjmp(err)) {
|
|
|
|
unwind();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
l = look("acidinit");
|
|
|
|
if(l && l->proc) {
|
|
|
|
n = an(ONAME, ZN, ZN);
|
|
|
|
n->sym = l;
|
|
|
|
n = an(OCALL, n, ZN);
|
|
|
|
execute(n);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
loadmodule(char *s)
|
|
|
|
{
|
|
|
|
interactive = 0;
|
|
|
|
if(setjmp(err)) {
|
|
|
|
unwind();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
pushfile(s);
|
|
|
|
silent = 0;
|
|
|
|
yyparse();
|
|
|
|
popio();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
Node*
|
|
|
|
an(int op, Node *l, Node *r)
|
|
|
|
{
|
|
|
|
Node *n;
|
|
|
|
|
|
|
|
n = gmalloc(sizeof(Node));
|
|
|
|
memset(n, 0, sizeof(Node));
|
|
|
|
n->gc.gclink = gcl;
|
|
|
|
gcl = (Gc*)n;
|
|
|
|
n->op = op;
|
|
|
|
n->left = l;
|
|
|
|
n->right = r;
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
List*
|
|
|
|
al(int t)
|
|
|
|
{
|
|
|
|
List *l;
|
|
|
|
|
|
|
|
l = gmalloc(sizeof(List));
|
|
|
|
memset(l, 0, sizeof(List));
|
|
|
|
l->type = t;
|
|
|
|
l->gc.gclink = gcl;
|
|
|
|
gcl = (Gc*)l;
|
|
|
|
return l;
|
|
|
|
}
|
|
|
|
|
|
|
|
Node*
|
|
|
|
con(int v)
|
|
|
|
{
|
|
|
|
Node *n;
|
|
|
|
|
|
|
|
n = an(OCONST, ZN, ZN);
|
|
|
|
n->store.u.ival = v;
|
|
|
|
n->store.fmt = 'X';
|
|
|
|
n->type = TINT;
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
fatal(char *fmt, ...)
|
|
|
|
{
|
|
|
|
char buf[128];
|
|
|
|
va_list arg;
|
|
|
|
|
|
|
|
va_start(arg, fmt);
|
|
|
|
vseprint(buf, buf+sizeof(buf), fmt, arg);
|
|
|
|
va_end(arg);
|
|
|
|
fprint(2, "%s: %Z (fatal problem) %s\n", argv0, buf);
|
|
|
|
exits(buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
yyerror(char *fmt, ...)
|
|
|
|
{
|
|
|
|
char buf[128];
|
|
|
|
va_list arg;
|
|
|
|
|
|
|
|
if(strcmp(fmt, "syntax error") == 0) {
|
|
|
|
yyerror("syntax error, near symbol '%s'", symbol);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
va_start(arg, fmt);
|
|
|
|
vseprint(buf, buf+sizeof(buf), fmt, arg);
|
|
|
|
va_end(arg);
|
|
|
|
print("%Z: %s\n", buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
marktree(Node *n)
|
|
|
|
{
|
|
|
|
|
|
|
|
if(n == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
marktree(n->left);
|
|
|
|
marktree(n->right);
|
|
|
|
|
|
|
|
n->gc.gcmark = 1;
|
|
|
|
if(n->op != OCONST)
|
|
|
|
return;
|
|
|
|
|
|
|
|
switch(n->type) {
|
|
|
|
case TSTRING:
|
|
|
|
n->store.u.string->gc.gcmark = 1;
|
|
|
|
break;
|
|
|
|
case TLIST:
|
|
|
|
marklist(n->store.u.l);
|
|
|
|
break;
|
|
|
|
case TCODE:
|
|
|
|
marktree(n->store.u.cc);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
marklist(List *l)
|
|
|
|
{
|
|
|
|
while(l) {
|
|
|
|
l->gc.gcmark = 1;
|
|
|
|
switch(l->type) {
|
|
|
|
case TSTRING:
|
|
|
|
l->store.u.string->gc.gcmark = 1;
|
|
|
|
break;
|
|
|
|
case TLIST:
|
|
|
|
marklist(l->store.u.l);
|
|
|
|
break;
|
|
|
|
case TCODE:
|
|
|
|
marktree(l->store.u.cc);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
l = l->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
gc(void)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
Lsym *f;
|
|
|
|
Value *v;
|
|
|
|
Gc *m, **p, *next;
|
|
|
|
|
|
|
|
if(dogc < Mempergc)
|
|
|
|
return;
|
|
|
|
dogc = 0;
|
|
|
|
|
|
|
|
/* Mark */
|
|
|
|
for(m = gcl; m; m = m->gclink)
|
|
|
|
m->gcmark = 0;
|
|
|
|
|
|
|
|
/* Scan */
|
|
|
|
for(i = 0; i < Hashsize; i++) {
|
|
|
|
for(f = hash[i]; f; f = f->hash) {
|
|
|
|
marktree(f->proc);
|
|
|
|
if(f->lexval != Tid)
|
|
|
|
continue;
|
|
|
|
for(v = f->v; v; v = v->pop) {
|
|
|
|
switch(v->type) {
|
|
|
|
case TSTRING:
|
|
|
|
v->store.u.string->gc.gcmark = 1;
|
|
|
|
break;
|
|
|
|
case TLIST:
|
|
|
|
marklist(v->store.u.l);
|
|
|
|
break;
|
|
|
|
case TCODE:
|
|
|
|
marktree(v->store.u.cc);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Free */
|
|
|
|
p = &gcl;
|
|
|
|
for(m = gcl; m; m = next) {
|
|
|
|
next = m->gclink;
|
|
|
|
if(m->gcmark == 0) {
|
|
|
|
*p = next;
|
|
|
|
free(m); /* Sleazy reliance on my malloc */
|
|
|
|
}
|
|
|
|
else
|
|
|
|
p = &m->gclink;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void*
|
|
|
|
gmalloc(long l)
|
|
|
|
{
|
|
|
|
void *p;
|
|
|
|
|
|
|
|
dogc += l;
|
|
|
|
p = malloc(l);
|
|
|
|
if(p == 0)
|
|
|
|
fatal("out of memory");
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
checkqid(int f1, int pid)
|
|
|
|
{
|
|
|
|
int fd;
|
|
|
|
Dir *d1, *d2;
|
|
|
|
char buf[128];
|
|
|
|
|
|
|
|
if(kernel)
|
|
|
|
return;
|
|
|
|
|
|
|
|
d1 = dirfstat(f1);
|
|
|
|
if(d1 == nil){
|
|
|
|
print("checkqid: (qid not checked) dirfstat: %r\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
sprint(buf, "/proc/%d/text", pid);
|
|
|
|
fd = open(buf, OREAD);
|
|
|
|
if(fd < 0 || (d2 = dirfstat(fd)) == nil){
|
|
|
|
print("checkqid: (qid not checked) dirstat %s: %r\n", buf);
|
|
|
|
free(d1);
|
|
|
|
if(fd >= 0)
|
|
|
|
close(fd);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
close(fd);
|
|
|
|
|
|
|
|
if(d1->qid.path != d2->qid.path || d1->qid.vers != d2->qid.vers || d1->qid.type != d2->qid.type){
|
|
|
|
print("path %llux %llux vers %lud %lud type %d %d\n",
|
|
|
|
d1->qid.path, d2->qid.path, d1->qid.vers, d2->qid.vers, d1->qid.type, d2->qid.type);
|
|
|
|
print("warning: image does not match text for pid %d\n", pid);
|
|
|
|
}
|
|
|
|
free(d1);
|
|
|
|
free(d2);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
catcher(void *junk, char *s)
|
|
|
|
{
|
|
|
|
USED(junk);
|
|
|
|
|
|
|
|
if(strstr(s, "interrupt")) {
|
|
|
|
gotint = 1;
|
|
|
|
noted(NCONT);
|
|
|
|
}
|
|
|
|
if(strstr(s, "child"))
|
|
|
|
noted(NCONT);
|
|
|
|
fprint(2, "note: %s\n", s);
|
|
|
|
noted(NDFLT);
|
|
|
|
}
|
|
|
|
|
|
|
|
char*
|
|
|
|
system(void)
|
|
|
|
{
|
|
|
|
char *cpu, *p, *q;
|
|
|
|
static char kernel[128];
|
|
|
|
|
|
|
|
cpu = getenv("cputype");
|
|
|
|
if(cpu == 0) {
|
|
|
|
cpu = "mips";
|
|
|
|
print("$cputype not set; assuming %s\n", cpu);
|
|
|
|
}
|
|
|
|
p = getenv("terminal");
|
|
|
|
if(p == 0 || (p=strchr(p, ' ')) == 0 || p[1] == ' ' || p[1] == 0) {
|
|
|
|
p = "9power";
|
|
|
|
print("missing or bad $terminal; assuming %s\n", p);
|
|
|
|
}
|
|
|
|
else{
|
|
|
|
p++;
|
|
|
|
q = strchr(p, ' ');
|
|
|
|
if(q)
|
|
|
|
*q = 0;
|
|
|
|
sprint(kernel, "/%s/9%s", cpu, p);
|
|
|
|
}
|
|
|
|
return kernel;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
isnumeric(char *s)
|
|
|
|
{
|
|
|
|
while(*s) {
|
|
|
|
if(*s < '0' || *s > '9')
|
|
|
|
return 0;
|
|
|
|
s++;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
xfmt(Fmt *f)
|
|
|
|
{
|
|
|
|
f->flags ^= FmtSharp;
|
|
|
|
return __ifmt(f);
|
|
|
|
}
|