]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/cwfs/con.c
cwfs: fix race between cmd_exec("users") and serve processes, cleanup portfns.h
[plan9front.git] / sys / src / cmd / cwfs / con.c
1 #include "all.h"
2
3 static  Command command[100];
4 static  Flag    flag[35];
5 static  char    statsdef[20];   /* default stats list */
6 static  int     whoflag;
7
8 static  void    consserve1(void *);
9
10 void
11 consserve(void)
12 {
13         int i;
14
15         strncpy(cons.chan->whochan, "console", sizeof(cons.chan->whochan));
16         con_session();
17         for(i = 0; command[i].arg0; i++)
18                 if(strcmp("cwcmd", command[i].arg0) == 0){
19                         cmd_exec("cwcmd touchsb");
20                         break;
21                 }
22
23         newproc(consserve1, 0, "con");
24 }
25
26 /* console commands process */
27 static void
28 consserve1(void *)
29 {
30         char *conline;
31
32         for (;;) {
33                 do {
34                         if ((conline = Brdline(&bin, '\n')) != nil) {
35                                 conline[Blinelen(&bin)-1] = '\0';
36                                 print("%s: %s\n", service, conline);
37                                 cmd_exec(conline);
38                         }
39                 } while (conline != nil);
40         }
41 }
42
43 static int
44 cmdcmp(void *va, void *vb)
45 {
46         Command *a, *b;
47
48         a = va;
49         b = vb;
50         return strcmp(a->arg0, b->arg0);
51 }
52
53 void
54 cmd_install(char *arg0, char *help, void (*func)(int, char*[]))
55 {
56         int i;
57
58         qlock(&cons);
59         for(i=0; command[i].arg0; i++)
60                 ;
61         if(i >= nelem(command)-2) {
62                 qunlock(&cons);
63                 print("cmd_install: too many commands\n");
64                 return;
65         }
66         command[i+1].arg0 = 0;
67         command[i].help = help;
68         command[i].func = func;
69         command[i].arg0 = arg0;
70         qsort(command, i+1, sizeof(Command), cmdcmp);
71         qunlock(&cons);
72 }
73
74 void
75 cmd_exec(char *arg)
76 {
77         char line[2*Maxword], *s;
78         char *argv[10];
79         int argc, i, c;
80
81         if(strlen(arg) >= nelem(line)-2) {
82                 print("cmd_exec: line too long\n");
83                 return;
84         }
85         strcpy(line, arg);
86
87         argc = 0;
88         s = line;
89         c = *s++;
90         for(;;) {
91                 while(isascii(c) && isspace(c))
92                         c = *s++;
93                 if(c == 0)
94                         break;
95                 if(argc >= nelem(argv)-2) {
96                         print("cmd_exec: too many args\n");
97                         return;
98                 }
99                 argv[argc++] = s-1;
100                 while((!isascii(c) || !isspace(c)) && c != '\0')
101                         c = *s++;
102                 s[-1] = 0;
103         }
104         if(argc <= 0)
105                 return;
106         for(i=0; s=command[i].arg0; i++)
107                 if(strcmp(argv[0], s) == 0) {
108                         (*command[i].func)(argc, argv);
109                         prflush();
110                         return;
111                 }
112         print("cmd_exec: unknown command: %s\n", argv[0]);
113 }
114
115 static void
116 cmd_halt(int, char *[])
117 {
118         wlock(&mainlock);       /* halt */
119         sync("halt");
120         exit();
121 }
122
123 static void
124 cmd_duallow(int argc, char *argv[])
125 {
126         int uid;
127
128         if(argc <= 1) {
129                 duallow = 0;
130                 return;
131         }
132
133         uid = strtouid(argv[1]);
134         if(uid < 0)
135                 uid = number(argv[1], -2, 10);
136         if(uid < 0) {
137                 print("bad uid %s\n", argv[1]);
138                 return;
139         }
140         duallow = uid;
141 }
142
143 static void
144 cmd_stats(int argc, char *argv[])
145 {
146         int i, c;
147         char buf[30], *s, *p, *q;
148
149         if(argc <= 1) {
150                 if(statsdef[0] == 0)
151                         strcpy(statsdef, "a");
152                 sprint(buf, "stats s%s", statsdef);
153                 cmd_exec(buf);
154                 return;
155         }
156
157         strcpy(buf, "stat");
158         p = strchr(buf, 0);
159         p[1] = 0;
160
161         q = 0;
162         for(i = 1; i < argc; i++)
163                 for(s = argv[i]; c = *s; s++) {
164                         if(c == 's')
165                                 continue;
166                         if(c == '-') {
167                                 q = statsdef;
168                                 continue;
169                         }
170                         if(q) {
171                                 *q++ = c;
172                                 *q = 0;
173                         }
174                         *p = c;
175                         cmd_exec(buf);
176                 }
177 }
178
179 static void
180 cmd_stata(int, char *[])
181 {
182         int i;
183
184         print("cons stats\n");
185 //      print("\twork =%7W%7W%7W rps\n", cons.work+0, cons.work+1, cons.work+2);
186 //      print("\trate =%7W%7W%7W tBps\n", cons.rate+0, cons.rate+1, cons.rate+2);
187 //      print("\thits =%7W%7W%7W iops\n", cons.bhit+0, cons.bhit+1, cons.bhit+2);
188 //      print("\tread =%7W%7W%7W iops\n", cons.bread+0, cons.bread+1, cons.bread+2);
189 //      print("\trah  =%7W%7W%7W iops\n", cons.brahead+0, cons.brahead+1, cons.brahead+2);
190 //      print("\tinit =%7W%7W%7W iops\n", cons.binit+0, cons.binit+1, cons.binit+2);
191         print("\tbufs =    %3ld sm %3ld lg %ld res\n",
192                 cons.nsmall, cons.nlarge, cons.nreseq);
193
194         for(i=0; i<nelem(mballocs); i++)
195                 if(mballocs[i])
196                         print("\t[%d]=%d\n", i, mballocs[i]);
197
198         print("\tioerr=    %3ld wr %3ld ww %3ld dr %3ld dw\n",
199                 cons.nwormre, cons.nwormwe, cons.nwrenre, cons.nwrenwe);
200         print("\tcache=     %9ld hit %9ld miss\n",
201                 cons.nwormhit, cons.nwormmiss);
202 }
203
204 static int
205 flagcmp(void *va, void *vb)
206 {
207         Flag *a, *b;
208
209         a = va;
210         b = vb;
211         return strcmp(a->arg0, b->arg0);
212 }
213
214 ulong
215 flag_install(char *arg, char *help)
216 {
217         int i;
218
219         qlock(&cons);
220         for(i=0; flag[i].arg0; i++)
221                 ;
222         if(i >= 32) {
223                 qunlock(&cons);
224                 print("flag_install: too many flags\n");
225                 return 0;
226         }
227         flag[i+1].arg0 = 0;
228         flag[i].arg0 = arg;
229         flag[i].help = help;
230         flag[i].flag = 1<<i;
231         qsort(flag, i+1, sizeof(Flag), flagcmp);
232         qunlock(&cons);
233         return 1<<i;
234 }
235
236 void
237 cmd_flag(int argc, char *argv[])
238 {
239         int f, n, i, j;
240         char *s;
241         Chan *cp;
242
243         if(argc <= 1) {
244                 for(i=0; flag[i].arg0; i++)
245                         print("%.4lux %s %s\n",
246                                 flag[i].flag, flag[i].arg0, flag[i].help);
247                 if(cons.flags)
248                         print("flag[*]   = %.4lux\n", cons.flags);
249                 for(cp = chans; cp; cp = cp->next)
250                         if(cp->flags)
251                                 print("flag[%3d] = %.4lux\n", cp->chan, cp->flags);
252                 return;
253         }
254
255         f = 0;
256         n = -1;
257         for(i=1; i<argc; i++) {
258                 for(j=0; s=flag[j].arg0; j++)
259                         if(strcmp(s, argv[i]) == 0)
260                                 goto found;
261                 j = number(argv[i], -1, 10);
262                 if(j < 0) {
263                         print("bad flag argument: %s\n", argv[i]);
264                         continue;
265                 }
266                 n = j;
267                 continue;
268         found:
269                 f |= flag[j].flag;
270         }
271
272         if(n < 0) {
273                 cons.flags ^= f;
274                 if(f == 0)
275                         cons.flags = 0;
276                 print("flag      = %.8lux\n", cons.flags);
277                 return;
278         }
279         for(cp = chans; cp; cp = cp->next)
280                 if(cp->chan == n) {
281                         cp->flags ^= f;
282                         if(f == 0)
283                                 cp->flags = 0;
284                         print("flag[%3d] = %.8lux\n", cp->chan, cp->flags);
285                         return;
286                 }
287         print("no such channel\n");
288 }
289
290 static void
291 cmd_who(int argc, char *argv[])
292 {
293         Chan *cp;
294         int i, c;
295
296         c = 0;
297         for(cp = chans; cp; cp = cp->next) {
298                 if(cp->whotime == 0 && !(cons.flags & whoflag)) {
299                         c++;
300                         continue;
301                 }
302                 if(argc > 1) {
303                         for(i=1; i<argc; i++)
304                                 if(strcmp(argv[i], cp->whoname) == 0)
305                                         break;
306                         if(i >= argc) {
307                                 c++;
308                                 continue;
309                         }
310                 }
311                 print("%3d: %10s %24s", cp->chan,
312                         cp->whoname? cp->whoname: "<nowhoname>", cp->whochan);
313                 if(cp->whoprint)
314                         cp->whoprint(cp);
315                 print("\n");
316                 prflush();
317         }
318         if(c > 0)
319                 print("%d chans not listed\n", c);
320 }
321
322 static void
323 cmd_hangup(int argc, char *argv[])
324 {
325         Chan *cp;
326         int n;
327
328         if(argc < 2) {
329                 print("usage: hangup chan-number\n");
330                 return;
331         }
332         n = number(argv[1], -1, 10);
333         for(cp = chans; cp; cp = cp->next) {
334                 if(cp->whotime == 0) {
335                         if(cp->chan == n)
336                                 print("that chan is hung up\n");
337                         continue;
338                 }
339                 if(cp->chan == n) {
340                         /* need more than just fileinit with tcp */
341                         chanhangup(cp, "console command", 1);
342                         fileinit(cp);
343                 }
344         }
345 }
346
347 static void
348 cmd_sync(int, char *[])
349 {
350         wlock(&mainlock);       /* sync */
351         sync("command");
352         wunlock(&mainlock);
353         print("\n");
354 }
355
356 static void
357 cmd_help(int argc, char *argv[])
358 {
359         char *arg;
360         int i, j;
361
362         for(i=0; arg=command[i].arg0; i++) {
363                 if(argc > 1) {
364                         for(j=1; j<argc; j++)
365                                 if(strcmp(argv[j], arg) == 0)
366                                         goto found;
367                         continue;
368                 }
369         found:
370                 print("\t%s %s\n", arg, command[i].help);
371                 prflush();
372         }
373 }
374
375 void
376 cmd_fstat(int argc, char *argv[])
377 {
378         int i;
379
380         for(i=1; i<argc; i++) {
381                 if(walkto(argv[i])) {
382                         print("cant stat %s\n", argv[i]);
383                         continue;
384                 }
385                 con_fstat(FID2);
386         }
387 }
388
389 void
390 cmd_create(int argc, char *argv[])
391 {
392         int uid, gid;
393         long perm;
394         char elem[NAMELEN], *p;
395
396         if(argc < 5) {
397                 print("usage: create path uid gid mode [lad]\n");
398                 return;
399         }
400
401         p = utfrrune(argv[1], '/');
402         if(p) {
403                 *p++ = 0;
404                 if(walkto(argv[1])) {
405                         print("create failed in walkto: %s\n", p);
406                         return;
407                 }
408         } else {
409                 if(walkto("/"))
410                         return;
411                 p = argv[1];
412         }
413         if(strlen(p) >= NAMELEN) {
414                 print("name too long %s\n", p);
415                 return;
416         }
417
418         memset(elem, 0, sizeof(elem));
419         strcpy(elem, p);
420
421         uid = strtouid(argv[2]);
422         if(uid < -1)
423                 uid = number(argv[2], -2, 10);
424         if(uid < -1) {
425                 print("bad uid %s\n", argv[2]);
426                 return;
427         }
428
429         gid = strtouid(argv[3]);
430         if(gid < -1)
431                 gid = number(argv[3], -2, 10);
432         if(gid < -1) {
433                 print("bad gid %s\n", argv[3]);
434                 return;
435         }
436
437         perm = number(argv[4], 0777, 8) & 0777;
438
439         if(argc > 5) {
440                 if(strchr(argv[5], 'l'))
441                         perm |= PLOCK;
442                 if(strchr(argv[5], 'a'))
443                         perm |= PAPND;
444                 if(strchr(argv[5], 'd'))
445                         perm |= PDIR;
446         }
447
448         if(con_create(FID2, elem, uid, gid, perm, 0))
449                 print("create failed: %s/%s\n", argv[1], p);
450 }
451
452 static void
453 cmd_clri(int argc, char *argv[])
454 {
455         int i;
456
457         for(i=1; i<argc; i++) {
458                 if(walkto(argv[i])) {
459                         print("cant remove %s\n", argv[i]);
460                         continue;
461                 }
462                 con_clri(FID2);
463         }
464 }
465
466 static void
467 cmd_allow(int, char**)
468 {
469         wstatallow = writeallow = 1;
470 }
471
472 static void
473 cmd_disallow(int, char**)
474 {
475         wstatallow = writeallow = 0;
476 }
477
478 void
479 ckblock(Device *d, Off a, int typ, Off qpath)
480 {
481         Iobuf *p;
482
483         if(a) {
484                 p = getbuf(d, a, Brd);
485                 if(p) {
486                         checktag(p, typ, qpath);
487                         putbuf(p);
488                 }
489         }
490 }
491
492 void
493 doclean(Iobuf *p, Dentry *d, int n, Off a)
494 {
495         int i, mod, typ;
496         Off qpath;
497
498         mod = 0;
499         qpath = d->qid.path;
500         typ = Tfile;
501         if(d->mode & DDIR)
502                 typ = Tdir;
503         for(i=0; i<NDBLOCK; i++) {
504                 print("dblock[%d] = %lld\n", i, (Wideoff)d->dblock[i]);
505                 ckblock(p->dev, d->dblock[i], typ, qpath);
506                 if(i == n) {
507                         d->dblock[i] = a;
508                         mod = 1;
509                         print("dblock[%d] modified %lld\n", i, (Wideoff)a);
510                 }
511         }
512
513         /* add NDBLOCK so user can cite block address by index */
514         for (i = 0; i < NIBLOCK; i++) {
515                 print("iblocks[%d] = %lld\n", NDBLOCK+i, (Wideoff)d->iblocks[i]);
516                 ckblock(p->dev, d->iblocks[i], Tind1+i, qpath);
517                 if(NDBLOCK+i == n) {
518                         d->iblocks[i] = a;
519                         mod = 1;
520                         print("iblocks[%d] modified %lld\n", NDBLOCK+i, (Wideoff)a);
521                 }
522         }
523
524         if(mod)
525                 p->flags |= Bmod|Bimm;
526 }
527
528 static void
529 cmd_clean(int argc, char *argv[])
530 {
531         int n;
532         Off a;
533         Iobuf *p;
534         Dentry *d;
535         File *f;
536
537         p = 0;
538         f = 0;
539         while(argc > 1) {
540                 n = -1;
541                 if(argc > 2)
542                         n = number(argv[2], -1, 10);
543                 a = 0;
544                 if(argc > 3)
545                         a = number(argv[3], 0, 10);
546                 if(walkto(argv[1])) {
547                         print("cant remove %s\n", argv[1]);
548                         break;
549                 }
550                 f = filep(cons.chan, FID2, 0);
551                 if(!f)
552                         break;
553                 if(n >= 0 && f->fs->dev->type == Devro) {
554                         print("readonly %s\n", argv[1]);
555                         break;
556                 }
557                 p = getbuf(f->fs->dev, f->addr, Brd);
558                 d = getdir(p, f->slot);
559                 if(!d || !(d->mode & DALLOC)) {
560                         print("not alloc %s\n", argv[1]);
561                         break;
562                 }
563                 doclean(p, d, n, a);
564                 break;
565         }
566         if(f)
567                 qunlock(f);
568         if(p)
569                 putbuf(p);
570 }
571
572 static void
573 cmd_remove(int argc, char *argv[])
574 {
575         int i;
576
577         for(i=1; i<argc; i++) {
578                 if(walkto(argv[i])) {
579                         print("cant remove %s\n", argv[i]);
580                         continue;
581                 }
582                 con_remove(FID2);
583         }
584 }
585
586 static void
587 cmd_version(int, char *[])
588 {
589         print("%d-bit %s as of %T\n", sizeof(Off)*8 - 1, service, fs_mktime);
590         print("\tlast boot %T\n", boottime);
591 }
592
593 static void
594 cmd_cfs(int argc, char *argv[])
595 {
596         Filsys *fs;
597         char *name;
598
599         name = "main";
600         if(argc > 1)
601                 name = argv[1];
602         fs = fsstr(name);
603         if(fs == 0) {
604                 print("%s: unknown file system\n", name);
605                 if(cons.curfs)
606                         return;
607                 fs = &filsys[0];
608         }
609         if(con_attach(FID1, "adm", fs->name))
610                 panic("FID1 attach to root");
611         cons.curfs = fs;
612         print("current fs is \"%s\"\n", cons.curfs->name);
613 }
614
615 static void
616 cmd_prof(int argc, char *argv[])
617 {
618         int n;
619         long m, o;
620         char *p;
621
622         if(cons.profbuf == 0) {
623                 print("no buffer\n");
624                 return;
625         }
626         n = !cons.profile;
627         if(argc > 1)
628                 n = number(argv[1], n, 10);
629         if(n && !cons.profile) {
630                 print("clr and start\n");
631                 memset(cons.profbuf, 0, cons.nprofbuf*sizeof(cons.profbuf[0]));
632                 cons.profile = 1;
633                 return;
634         }
635         if(!n && cons.profile) {
636                 cons.profile = 0;
637                 print("stop and write\n");
638                 if(walkto("/adm/kprofdata"))
639                         goto bad;
640                 if(con_open(FID2, OWRITE|OTRUNC)) {
641                 bad:
642                         print("cant open /adm/kprofdata\n");
643                         return;
644                 }
645                 p = (char*)cons.profbuf;
646                 for(m=0; m<cons.nprofbuf; m++) {
647                         n = cons.profbuf[m];
648                         p[0] = n>>24;
649                         p[1] = n>>16;
650                         p[2] = n>>8;
651                         p[3] = n>>0;
652                         p += 4;
653                 }
654
655                 m = cons.nprofbuf*sizeof(cons.profbuf[0]);
656                 o = 0;
657                 while(m > 0) {
658                         n = 8192;
659                         if(n > m)
660                                 n = m;
661                         con_write(FID2, (char*)cons.profbuf+o, o, n);
662                         m -= n;
663                         o += n;
664                 }
665                 return;
666         }
667 }
668
669 static void
670 cmd_time(int argc, char *argv[])
671 {
672         int i, len;
673         char *cmd;
674         Timet t1, t2;
675
676         t1 = time(nil);
677         len = 0;
678         for(i=1; i<argc; i++)
679                 len += 1 + strlen(argv[i]);
680         cmd = malloc(len + 1);
681         cmd[0] = 0;
682         for(i=1; i<argc; i++) {
683                 strcat(cmd, " ");
684                 strcat(cmd, argv[i]);
685         }
686         cmd_exec(cmd);
687         t2 = time(nil);
688         free(cmd);
689         print("time = %ld ms\n", TK2MS(t2-t1));
690 }
691
692 void
693 cmd_noauth(int, char *[])
694 {
695         noauth = !noauth;
696         if(noauth)
697                 print("authentication is DISABLED\n");
698 }
699
700 void
701 cmd_noattach(int, char *[])
702 {
703         noattach = !noattach;
704         if(noattach)
705                 print("attaches are DISABLED\n");
706 }
707
708 void
709 cmd_files(int, char *[])
710 {
711         long i, n;
712         Chan *cp;
713
714         for(cp = chans; cp; cp = cp->next)
715                 cp->nfile = 0;
716
717         lock(&flock);
718         n = 0;
719         for(i=0; i<conf.nfile; i++)
720                 if(files[i].cp) {
721                         n++;
722                         files[i].cp->nfile++;
723                 }
724         print("%ld out of %ld files used\n", n, conf.nfile);
725         unlock(&flock);
726
727         n = 0;
728         for(cp = chans; cp; cp = cp->next)
729                 if(cp->nfile) {
730                         print("%3d: %5d\n", cp->chan, cp->nfile);
731                         prflush();
732                         n += cp->nfile;
733                 }
734         print("%ld out of %ld files used\n", n, conf.nfile);
735 }
736
737 void
738 cmd_chatty(int argc, char *argv[])
739 {
740         if(argc < 2) {
741                 print("cmd_chatty: usage: chatty n\n");
742                 return;
743         }
744         chatty = atoi(argv[1]);
745 }
746
747 void
748 installcmds(void)
749 {
750         cmd_install("allow", "-- disable permission checking", cmd_allow);
751         cmd_install("cfs", "[file] -- set current filesystem", cmd_cfs);
752         cmd_install("chatty", "n -- set chattiness", cmd_chatty);
753         cmd_install("clean", "file [bno [addr]] -- block print/fix", cmd_clean);
754         cmd_install("check", "[options]", cmd_check);
755         cmd_install("clri", "[file ...] -- purge files/dirs", cmd_clri);
756         cmd_install("create", "path uid gid perm [lad] -- make a file/dir", cmd_create);
757         cmd_install("disallow", "-- enable permission checking", cmd_disallow);
758         cmd_install("duallow", "uid -- duallow", cmd_duallow);
759         cmd_install("flag", "-- print set flags", cmd_flag);
760         cmd_install("fstat", "path -- print info on a file/dir", cmd_fstat);
761         cmd_install("halt", "-- return to boot rom", cmd_halt);
762         cmd_install("help", "", cmd_help);
763         cmd_install("newuser", "username -- add user to /adm/users", cmd_newuser);
764         cmd_install("profile", "[01] -- fs profile", cmd_prof);
765         cmd_install("remove", "[file ...] -- remove files/dirs", cmd_remove);
766         cmd_install("stata", "-- overall stats", cmd_stata);
767         cmd_install("stats", "[[-]flags ...] -- various stats", cmd_stats);
768         cmd_install("sync", "", cmd_sync);
769         cmd_install("time", "command -- time another command", cmd_time);
770         cmd_install("users", "[file] -- read /adm/users", cmd_users);
771         cmd_install("version", "-- print time of mk and boot", cmd_version);
772         cmd_install("who", "[user ...] -- print attaches", cmd_who);
773         cmd_install("hangup", "chan -- clunk files", cmd_hangup);
774         cmd_install("printconf", "-- print configuration", cmd_printconf);
775         cmd_install("noauth", "toggle noauth flag", cmd_noauth);
776         cmd_install("noattach", "toggle noattach flag", cmd_noattach);
777         cmd_install("files", "report on files structure", cmd_files);
778
779         chatflag = flag_install("chat", "-- verbose");
780         errorflag = flag_install("error", "-- on errors");
781         whoflag = flag_install("allchans", "-- on who");
782         authdebugflag = flag_install("authdebug", "-- report authentications");
783 }
784
785 int
786 walkto(char *name)
787 {
788         char elem[NAMELEN], *p;
789         int n;
790
791         if(con_clone(FID1, FID2))
792                 return 1;
793
794         for(;;) {
795                 p = utfrune(name, '/');
796                 if(p == nil)
797                         p = strchr(name, '\0');
798                 if(p == name) {
799                         if(*name == '\0')
800                                 return 0;
801                         name = p+1;
802                         continue;
803                 }
804                 n = p-name;
805                 if(n > NAMELEN)
806                         return 1;
807                 memset(elem, 0, sizeof(elem));
808                 memmove(elem, name, n);
809                 if(con_walk(FID2, elem))
810                         return 1;
811                 name = p;
812         }
813 }
814
815 /* needs to parse and return vlongs to cope with new larger block numbers */
816 vlong
817 number(char *arg, int def, int base)
818 {
819         int c, sign, any;
820         vlong n;
821
822         if(arg == nil)
823                 return def;
824
825         sign = 0;
826         any = 0;
827         n = 0;
828
829         for (c = *arg; isascii(c) && isspace(c) && c != '\n'; c = *arg)
830                 arg++;
831         if(c == '-') {
832                 sign = 1;
833                 arg++;
834                 c = *arg;
835         }
836         while (isascii(c) && (isdigit(c) || base == 16 && isxdigit(c))) {
837                 n *= base;
838                 if(c >= 'a' && c <= 'f')
839                         n += c - 'a' + 10;
840                 else if(c >= 'A' && c <= 'F')
841                         n += c - 'A' + 10;
842                 else
843                         n += c - '0';
844                 arg++;
845                 c = *arg;
846                 any = 1;
847         }
848         if(!any)
849                 return def;
850         if(sign)
851                 n = -n;
852         return n;
853 }