Annotation of 43BSDReno/pgrm/dbx/process.c, revision 1.1.1.1

1.1       root        1: /*
                      2:  * Copyright (c) 1983 The Regents of the University of California.
                      3:  * All rights reserved.
                      4:  *
                      5:  * Redistribution and use in source and binary forms are permitted
                      6:  * provided that: (1) source distributions retain this entire copyright
                      7:  * notice and comment, and (2) distributions including binaries display
                      8:  * the following acknowledgement:  ``This product includes software
                      9:  * developed by the University of California, Berkeley and its contributors''
                     10:  * in the documentation or other materials provided with the distribution
                     11:  * and in all advertising materials mentioning features or use of this
                     12:  * software. Neither the name of the University nor the names of its
                     13:  * contributors may be used to endorse or promote products derived
                     14:  * from this software without specific prior written permission.
                     15:  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
                     16:  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
                     17:  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
                     18:  */
                     19: 
                     20: #ifndef lint
                     21: static char sccsid[] = "@(#)process.c  5.6 (Berkeley) 6/1/90";
                     22: #endif /* not lint */
                     23: 
                     24: /*
                     25:  * Process management.
                     26:  *
                     27:  * This module contains the routines to manage the execution and
                     28:  * tracing of the debuggee process.
                     29:  */
                     30: 
                     31: #include "defs.h"
                     32: #include "process.h"
                     33: #include "machine.h"
                     34: #include "events.h"
                     35: #include "tree.h"
                     36: #include "eval.h"
                     37: #include "operators.h"
                     38: #include "source.h"
                     39: #include "object.h"
                     40: #include "mappings.h"
                     41: #include "main.h"
                     42: #include "coredump.h"
                     43: #include <signal.h>
                     44: #include <errno.h>
                     45: #include <sys/stat.h>
                     46: 
                     47: #ifndef public
                     48: 
                     49: typedef struct Process *Process;
                     50: 
                     51: Process process;
                     52: 
                     53: #define DEFSIG -1
                     54: 
                     55: #include "machine.h"
                     56: 
                     57: #endif
                     58: 
                     59: #define NOTSTARTED 1
                     60: #define STOPPED 0177
                     61: #define FINISHED 0
                     62: 
                     63: /*
                     64:  * A cache of the instruction segment is kept to reduce the number
                     65:  * of system calls.  Might be better just to read the entire
                     66:  * code space into memory.
                     67:  */
                     68: 
                     69: #define CACHESIZE 1003
                     70: 
                     71: typedef struct {
                     72:     Word addr;
                     73:     Word val;
                     74: } CacheWord;
                     75: 
                     76: /*
                     77:  * This structure holds the information we need from the user structure.
                     78:  */
                     79: 
                     80: struct Process {
                     81:     int pid;                   /* process being traced */
                     82:     int mask;                  /* process status word */
                     83:     Word reg[NREG];            /* process' registers */
                     84:     Word oreg[NREG];           /* registers when process last stopped */
                     85:     short status;              /* either STOPPED or FINISHED */
                     86:     short signo;               /* signal that stopped process */
                     87:     short sigcode;             /* extra signal information */
                     88:     int exitval;               /* return value from exit() */
                     89:     long sigset;               /* bit array of traced signals */
                     90:     CacheWord word[CACHESIZE]; /* text segment cache */
                     91:     Ttyinfo ttyinfo;           /* process' terminal characteristics */
                     92:     Address sigstatus;         /* process' handler for current signal */
                     93: };
                     94: 
                     95: /*
                     96:  * These definitions are for the arguments to "pio".
                     97:  */
                     98: 
                     99: typedef enum { PREAD, PWRITE } PioOp;
                    100: typedef enum { TEXTSEG, DATASEG } PioSeg;
                    101: 
                    102: private struct Process pbuf;
                    103: 
                    104: #define MAXNCMDARGS 1000         /* maximum number of arguments to RUN */
                    105: 
                    106: extern int errno;
                    107: 
                    108: private Boolean just_started;
                    109: private int argc;
                    110: private String argv[MAXNCMDARGS];
                    111: private String infile, outfile;
                    112: 
                    113: /*
                    114:  * Initialize process information.
                    115:  */
                    116: 
                    117: public process_init()
                    118: {
                    119:     register integer i;
                    120:     char buf[10];
                    121: 
                    122:     process = &pbuf;
                    123:     process->status = (coredump) ? STOPPED : NOTSTARTED;
                    124:     setsigtrace();
                    125: #   if vax || tahoe
                    126:        for (i = 0; i < NREG; i++) {
                    127:            sprintf(buf, "$r%d", i);
                    128:            defregname(identname(buf, false), i);
                    129:        }
                    130: #      ifdef vax
                    131:            defregname(identname("$ap", true), ARGP);
                    132: #      endif
                    133: #   else
                    134: #       ifdef mc68000
                    135:            for (i = 0; i < 8; i++) {
                    136:                sprintf(buf, "$d%d", i);
                    137:                defregname(identname(buf, false), i);
                    138:                sprintf(buf, "$a%d", i);
                    139:                defregname(identname(buf, false), i + 8);
                    140:            }
                    141: #       endif
                    142: #   endif
                    143:     defregname(identname("$fp", true), FRP);
                    144:     defregname(identname("$sp", true), STKP);
                    145:     defregname(identname("$pc", true), PROGCTR);
                    146:     if (coredump) {
                    147:        coredump_readin(process->mask, process->reg, process->signo);
                    148:        pc = process->reg[PROGCTR];
                    149:     }
                    150:     arginit();
                    151: }
                    152: 
                    153: /*
                    154:  * Routines to get at process information from outside this module.
                    155:  */
                    156: 
                    157: public Word reg(n)
                    158: Integer n;
                    159: {
                    160:     register Word w;
                    161: 
                    162:     if (n == NREG) {
                    163:        w = process->mask;
                    164:     } else {
                    165:        w = process->reg[n];
                    166:     }
                    167:     return w;
                    168: }
                    169: 
                    170: public setreg(n, w)
                    171: Integer n;
                    172: Word w;
                    173: {
                    174:     process->reg[n] = w;
                    175: }
                    176: 
                    177: /*
                    178:  * Begin execution.
                    179:  *
                    180:  * We set a breakpoint at the end of the code so that the
                    181:  * process data doesn't disappear after the program terminates.
                    182:  */
                    183: 
                    184: private Boolean remade();
                    185: 
                    186: public start(argv, infile, outfile)
                    187: String argv[];
                    188: String infile, outfile;
                    189: {
                    190:     String pargv[4];
                    191:     Node cond;
                    192: 
                    193:     if (coredump) {
                    194:        coredump = false;
                    195:        fclose(corefile);
                    196:        coredump_close();
                    197:     }
                    198:     if (argv == nil) {
                    199:        argv = pargv;
                    200:        pargv[0] = objname;
                    201:        pargv[1] = nil;
                    202:     } else {
                    203:        argv[argc] = nil;
                    204:     }
                    205:     pstart(process, argv, infile, outfile);
                    206:     if (remade(objname)) {
                    207:        reinit(argv, infile, outfile);
                    208:     }
                    209:     if (process->status == STOPPED) {
                    210:        pc = CODESTART;
                    211:        setcurfunc(program);
                    212:        if (objsize != 0) {
                    213:            cond = build(O_EQ, build(O_SYM, pcsym), build(O_LCON, lastaddr()));
                    214:            event_once(cond, buildcmdlist(build(O_ENDX)));
                    215:        }
                    216:     }
                    217: }
                    218: 
                    219: /*
                    220:  * Check to see if the object file has changed since the symbolic
                    221:  * information last was read.
                    222:  */
                    223: 
                    224: private time_t modtime;
                    225: 
                    226: private Boolean remade(filename)
                    227: String filename;
                    228: {
                    229:     struct stat s;
                    230:     Boolean b;
                    231: 
                    232:     stat(filename, &s);
                    233:     b = (Boolean) (modtime != 0 and modtime < s.st_mtime);
                    234:     modtime = s.st_mtime;
                    235:     return b;
                    236: }
                    237: 
                    238: /*
                    239:  * Set up what signals we want to trace.
                    240:  */
                    241: 
                    242: private setsigtrace()
                    243: {
                    244:     register Integer i;
                    245:     register Process p;
                    246: 
                    247:     p = process;
                    248:     for (i = 1; i <= NSIG; i++) {
                    249:        psigtrace(p, i, true);
                    250:     }
                    251:     psigtrace(p, SIGHUP, false);
                    252:     psigtrace(p, SIGKILL, false);
                    253:     psigtrace(p, SIGALRM, false);
                    254: #   ifdef SIGTSTP
                    255:        psigtrace(p, SIGTSTP, false);
                    256:        psigtrace(p, SIGCONT, false);
                    257: #   endif
                    258:     psigtrace(p, SIGCHLD, false);
                    259:     psigtrace(p, SIGWINCH, false);
                    260: }
                    261: 
                    262: /*
                    263:  * Initialize the argument list.
                    264:  */
                    265: 
                    266: public arginit()
                    267: {
                    268:     infile = nil;
                    269:     outfile = nil;
                    270:     argv[0] = objname;
                    271:     argc = 1;
                    272: }
                    273: 
                    274: /*
                    275:  * Add an argument to the list for the debuggee.
                    276:  */
                    277: 
                    278: public newarg(arg)
                    279: String arg;
                    280: {
                    281:     if (argc >= MAXNCMDARGS) {
                    282:        error("too many arguments");
                    283:     }
                    284:     argv[argc++] = arg;
                    285: }
                    286: 
                    287: /*
                    288:  * Set the standard input for the debuggee.
                    289:  */
                    290: 
                    291: public inarg(filename)
                    292: String filename;
                    293: {
                    294:     if (infile != nil) {
                    295:        error("multiple input redirects");
                    296:     }
                    297:     infile = filename;
                    298: }
                    299: 
                    300: /*
                    301:  * Set the standard output for the debuggee.
                    302:  * Probably should check to avoid overwriting an existing file.
                    303:  */
                    304: 
                    305: public outarg(filename)
                    306: String filename;
                    307: {
                    308:     if (outfile != nil) {
                    309:        error("multiple output redirect");
                    310:     }
                    311:     outfile = filename;
                    312: }
                    313: 
                    314: /*
                    315:  * Start debuggee executing.
                    316:  */
                    317: 
                    318: public run()
                    319: {
                    320:     process->status = STOPPED;
                    321:     fixbps();
                    322:     curline = 0;
                    323:     start(argv, infile, outfile);
                    324:     just_started = true;
                    325:     isstopped = false;
                    326:     cont(0);
                    327: }
                    328: 
                    329: /*
                    330:  * Continue execution wherever we left off.
                    331:  *
                    332:  * Note that this routine never returns.  Eventually bpact() will fail
                    333:  * and we'll call printstatus or step will call it.
                    334:  */
                    335: 
                    336: typedef int Intfunc();
                    337: 
                    338: private sig_t dbintr;
                    339: private void intr();
                    340: 
                    341: public cont(signo)
                    342: integer signo;
                    343: {
                    344:     integer s;
                    345: 
                    346:     dbintr = signal(SIGINT, intr);
                    347:     if (just_started) {
                    348:        just_started = false;
                    349:     } else {
                    350:        if (not isstopped) {
                    351:            error("can't continue execution");
                    352:        }
                    353:        isstopped = false;
                    354:        stepover();
                    355:     }
                    356:     s = signo;
                    357:     for (;;) {
                    358:        if (single_stepping) {
                    359:            printnews();
                    360:        } else {
                    361:            setallbps();
                    362:            resume(s);
                    363:            unsetallbps();
                    364:            s = DEFSIG;
                    365:            if (not isbperr() or not bpact()) {
                    366:                printstatus();
                    367:            }
                    368:        }
                    369:        stepover();
                    370:     }
                    371:     /* NOTREACHED */
                    372: }
                    373: 
                    374: /*
                    375:  * This routine is called if we get an interrupt while "running"
                    376:  * but actually in the debugger.  Could happen, for example, while
                    377:  * processing breakpoints.
                    378:  *
                    379:  * We basically just want to keep going; the assumption is
                    380:  * that when the process resumes it will get the interrupt,
                    381:  * which will then be handled.
                    382:  */
                    383: 
                    384: private void intr()
                    385: {
                    386:     signal(SIGINT, intr);
                    387: }
                    388: 
                    389: public fixintr()
                    390: {
                    391:     signal(SIGINT, dbintr);
                    392: }
                    393: 
                    394: /*
                    395:  * Resume execution.
                    396:  */
                    397: 
                    398: public resume(signo)
                    399: int signo;
                    400: {
                    401:     register Process p;
                    402: 
                    403:     p = process;
                    404:     pcont(p, signo);
                    405:     pc = process->reg[PROGCTR];
                    406:     if (p->status != STOPPED) {
                    407:        if (p->signo != 0) {
                    408:            error("program terminated by signal %d", p->signo);
                    409:        } else if (not runfirst) {
                    410:            if (p->exitval == 0) {
                    411:                error("program exited");
                    412:            } else {
                    413:                error("program exited with code %d", p->exitval);
                    414:            }
                    415:        }
                    416:     }
                    417: }
                    418: 
                    419: /*
                    420:  * Continue execution up to the next source line.
                    421:  *
                    422:  * There are two ways to define the next source line depending on what
                    423:  * is desired when a procedure or function call is encountered.  Step
                    424:  * stops at the beginning of the procedure or call; next skips over it.
                    425:  */
                    426: 
                    427: /*
                    428:  * Stepc is what is called when the step command is given.
                    429:  * It has to play with the "isstopped" information.
                    430:  */
                    431: 
                    432: public stepc()
                    433: {
                    434:     if (not isstopped) {
                    435:        error("can't continue execution");
                    436:     }
                    437:     isstopped = false;
                    438:     dostep(false);
                    439:     isstopped = true;
                    440: }
                    441: 
                    442: public next()
                    443: {
                    444:     Address oldfrp, newfrp;
                    445: 
                    446:     if (not isstopped) {
                    447:        error("can't continue execution");
                    448:     }
                    449:     isstopped = false;
                    450:     oldfrp = reg(FRP);
                    451:     do {
                    452:        dostep(true);
                    453:        pc = reg(PROGCTR);
                    454:        newfrp = reg(FRP);
                    455:     } while (newfrp < oldfrp and newfrp != 0);
                    456:     isstopped = true;
                    457: }
                    458: 
                    459: /*
                    460:  * Continue execution until the current function returns, or,
                    461:  * if the given argument is non-nil, until execution returns to
                    462:  * somewhere within the given function.
                    463:  */
                    464: 
                    465: public rtnfunc (f)
                    466: Symbol f;
                    467: {
                    468:     Address addr;
                    469:     Symbol t;
                    470: 
                    471:     if (not isstopped) {
                    472:        error("can't continue execution");
                    473:     } else if (f != nil and not isactive(f)) {
                    474:        error("%s is not active", symname(f));
                    475:     } else {
                    476:        addr = return_addr();
                    477:        if (addr == nil) {
                    478:            error("no place to return to");
                    479:        } else {
                    480:            isstopped = false;
                    481:            contto(addr);
                    482:            if (f != nil) {
                    483:                for (;;) {
                    484:                    t = whatblock(pc);
                    485:                    addr = return_addr();
                    486:                if (t == f or addr == nil) break;
                    487:                    contto(addr);
                    488:                }
                    489:            }
                    490:            if (not bpact()) {
                    491:                isstopped = true;
                    492:                printstatus();
                    493:            }
                    494:        }
                    495:     }
                    496: }
                    497: 
                    498: /*
                    499:  * Single-step over the current machine instruction.
                    500:  *
                    501:  * If we're single-stepping by source line we want to step to the
                    502:  * next source line.  Otherwise we're going to continue so there's
                    503:  * no reason to do all the work necessary to single-step to the next
                    504:  * source line.
                    505:  */
                    506: 
                    507: public stepover()
                    508: {
                    509:     Boolean b;
                    510: 
                    511:     if (traceexec) {
                    512:        printf("!! stepping over 0x%x\n", process->reg[PROGCTR]);
                    513:     }
                    514:     if (single_stepping) {
                    515:        dostep(false);
                    516:     } else {
                    517:        b = inst_tracing;
                    518:        inst_tracing = true;
                    519:        dostep(false);
                    520:        inst_tracing = b;
                    521:     }
                    522:     if (traceexec) {
                    523:        printf("!! stepped over to 0x%x\n", process->reg[PROGCTR]);
                    524:     }
                    525: }
                    526: 
                    527: /*
                    528:  * Resume execution up to the given address.  We can either ignore
                    529:  * breakpoints (stepto) or catch them (contto).
                    530:  */
                    531: 
                    532: public stepto(addr)
                    533: Address addr;
                    534: {
                    535:     xto(addr, false);
                    536: }
                    537: 
                    538: private contto (addr)
                    539: Address addr;
                    540: {
                    541:     xto(addr, true);
                    542: }
                    543: 
                    544: private xto (addr, catchbps)
                    545: Address addr;
                    546: boolean catchbps;
                    547: {
                    548:     Address curpc;
                    549: 
                    550:     if (catchbps) {
                    551:        stepover();
                    552:     }
                    553:     curpc = process->reg[PROGCTR];
                    554:     if (addr != curpc) {
                    555:        if (traceexec) {
                    556:            printf("!! stepping from 0x%x to 0x%x\n", curpc, addr);
                    557:        }
                    558:        if (catchbps) {
                    559:            setallbps();
                    560:        }
                    561:        setbp(addr);
                    562:        resume(DEFSIG);
                    563:        unsetbp(addr);
                    564:        if (catchbps) {
                    565:            unsetallbps();
                    566:        }
                    567:        if (not isbperr()) {
                    568:            printstatus();
                    569:        }
                    570:     }
                    571: }
                    572: 
                    573: /*
                    574:  * Print the status of the process.
                    575:  * This routine does not return.
                    576:  */
                    577: 
                    578: public printstatus ()
                    579: {
                    580:     int status;
                    581: 
                    582:     if (process->status == FINISHED) {
                    583:        exit(0);
                    584:     } else {
                    585:        if (runfirst) {
                    586:            fprintf(stderr, "\nEntering debugger ...\n");
                    587:            printheading();
                    588:            init();
                    589:        }
                    590:        setcurfunc(whatblock(pc));
                    591:        getsrcpos();
                    592:        if (process->signo == SIGINT) {
                    593:            isstopped = true;
                    594:            printerror();
                    595:        } else if (isbperr() and isstopped) {
                    596:            printf("stopped ");
                    597:            printloc();
                    598:            putchar('\n');
                    599:            if (curline > 0) {
                    600:                printlines(curline, curline);
                    601:            } else {
                    602:                printinst(pc, pc);
                    603:            }
                    604:            erecover();
                    605:        } else {
                    606:            fixintr();
                    607:            isstopped = true;
                    608:            printerror();
                    609:        }
                    610:     }
                    611: }
                    612: 
                    613: /*
                    614:  * Print out the current location in the debuggee.
                    615:  */
                    616: 
                    617: public printloc()
                    618: {
                    619:     printf("in ");
                    620:     printname(stdout, curfunc);
                    621:     putchar(' ');
                    622:     if (curline > 0 and not useInstLoc) {
                    623:        printsrcpos();
                    624:     } else {
                    625:        useInstLoc = false;
                    626:        curline = 0;
                    627:        printf("at 0x%x", pc);
                    628:     }
                    629: }
                    630: 
                    631: /*
                    632:  * Some functions for testing the state of the process.
                    633:  */
                    634: 
                    635: public Boolean notstarted(p)
                    636: Process p;
                    637: {
                    638:     return (Boolean) (p->status == NOTSTARTED);
                    639: }
                    640: 
                    641: public Boolean isfinished(p)
                    642: Process p;
                    643: {
                    644:     return (Boolean) (p->status == FINISHED);
                    645: }
                    646: 
                    647: /*
                    648:  * Predicate to test if the reason the process stopped was because
                    649:  * of a breakpoint.  If so, as a side effect clear the local copy of
                    650:  * signal handler associated with process.  We must do this so as to
                    651:  * not confuse future stepping or continuing by possibly concluding
                    652:  * the process should continue with a SIGTRAP handler.
                    653:  */
                    654: 
                    655: public boolean isbperr()
                    656: {
                    657:     Process p;
                    658:     boolean b;
                    659: 
                    660:     p = process;
                    661:     if (p->status == STOPPED and p->signo == SIGTRAP) {
                    662:        b = true;
                    663:        p->sigstatus = 0;
                    664:     } else {
                    665:        b = false;
                    666:     }
                    667:     return b;
                    668: }
                    669: 
                    670: /*
                    671:  * Return the signal number that stopped the process.
                    672:  */
                    673: 
                    674: public integer errnum (p)
                    675: Process p;
                    676: {
                    677:     return p->signo;
                    678: }
                    679: 
                    680: /*
                    681:  * Return the signal code associated with the signal.
                    682:  */
                    683: 
                    684: public integer errcode (p)
                    685: Process p;
                    686: {
                    687:     return p->sigcode;
                    688: }
                    689: 
                    690: /*
                    691:  * Return the termination code of the process.
                    692:  */
                    693: 
                    694: public integer exitcode (p)
                    695: Process p;
                    696: {
                    697:     return p->exitval;
                    698: }
                    699: 
                    700: /*
                    701:  * These routines are used to access the debuggee process from
                    702:  * outside this module.
                    703:  *
                    704:  * They invoke "pio" which eventually leads to a call to "ptrace".
                    705:  * The system generates an I/O error when a ptrace fails.  During reads
                    706:  * these are ignored, during writes they are reported as an error, and
                    707:  * for anything else they cause a fatal error.
                    708:  */
                    709: 
                    710: extern Intfunc *onsyserr();
                    711: 
                    712: private badaddr;
                    713: private read_err(), write_err();
                    714: 
                    715: /*
                    716:  * Read from the process' instruction area.
                    717:  */
                    718: 
                    719: public iread(buff, addr, nbytes)
                    720: char *buff;
                    721: Address addr;
                    722: int nbytes;
                    723: {
                    724:     Intfunc *f;
                    725: 
                    726:     f = onsyserr(EIO, read_err);
                    727:     badaddr = addr;
                    728:     if (coredump) {
                    729:        coredump_readtext(buff, addr, nbytes);
                    730:     } else {
                    731:        pio(process, PREAD, TEXTSEG, buff, addr, nbytes);
                    732:     }
                    733:     onsyserr(EIO, f);
                    734: }
                    735: 
                    736: /* 
                    737:  * Write to the process' instruction area, usually in order to set
                    738:  * or unset a breakpoint.
                    739:  */
                    740: 
                    741: public iwrite(buff, addr, nbytes)
                    742: char *buff;
                    743: Address addr;
                    744: int nbytes;
                    745: {
                    746:     Intfunc *f;
                    747: 
                    748:     if (coredump) {
                    749:        error("no process to write to");
                    750:     }
                    751:     f = onsyserr(EIO, write_err);
                    752:     badaddr = addr;
                    753:     pio(process, PWRITE, TEXTSEG, buff, addr, nbytes);
                    754:     onsyserr(EIO, f);
                    755: }
                    756: 
                    757: /*
                    758:  * Read for the process' data area.
                    759:  */
                    760: 
                    761: public dread(buff, addr, nbytes)
                    762: char *buff;
                    763: Address addr;
                    764: int nbytes;
                    765: {
                    766:     Intfunc *f;
                    767: 
                    768:     badaddr = addr;
                    769:     if (coredump) {
                    770:        f = onsyserr(EFAULT, read_err);
                    771:        coredump_readdata(buff, addr, nbytes);
                    772:        onsyserr(EFAULT, f);
                    773:     } else {
                    774:        f = onsyserr(EIO, read_err);
                    775:        pio(process, PREAD, DATASEG, buff, addr, nbytes);
                    776:        onsyserr(EIO, f);
                    777:     }
                    778: }
                    779: 
                    780: /*
                    781:  * Write to the process' data area.
                    782:  */
                    783: 
                    784: public dwrite(buff, addr, nbytes)
                    785: char *buff;
                    786: Address addr;
                    787: int nbytes;
                    788: {
                    789:     Intfunc *f;
                    790: 
                    791:     if (coredump) {
                    792:        error("no process to write to");
                    793:     }
                    794:     f = onsyserr(EIO, write_err);
                    795:     badaddr = addr;
                    796:     pio(process, PWRITE, DATASEG, buff, addr, nbytes);
                    797:     onsyserr(EIO, f);
                    798: }
                    799: 
                    800: /*
                    801:  * Trap for errors in reading or writing to a process.
                    802:  * The current approach is to "ignore" read errors and complain
                    803:  * bitterly about write errors.
                    804:  */
                    805: 
                    806: private read_err()
                    807: {
                    808:     /*
                    809:      * Ignore.
                    810:      */
                    811: }
                    812: 
                    813: private write_err()
                    814: {
                    815:     error("can't write to process (address 0x%x)", badaddr);
                    816: }
                    817: 
                    818: /*
                    819:  * Ptrace interface.
                    820:  */
                    821: 
                    822: #define WMASK           (~(sizeof(Word) - 1))
                    823: #define cachehash(addr) ((unsigned) ((addr >> 2) % CACHESIZE))
                    824: 
                    825: #define FIRSTSIG        SIGINT
                    826: #define LASTSIG         SIGQUIT
                    827: #define ischild(pid)    ((pid) == 0)
                    828: #define traceme()       ptrace(0, 0, 0, 0)
                    829: #define setrep(n)       (1 << ((n)-1))
                    830: #define istraced(p)     (p->sigset&setrep(p->signo))
                    831: 
                    832: /*
                    833:  * Ptrace options (specified in first argument).
                    834:  */
                    835: 
                    836: #define UREAD   3       /* read from process's user structure */
                    837: #define UWRITE  6       /* write to process's user structure */
                    838: #define IREAD   1       /* read from process's instruction space */
                    839: #define IWRITE  4       /* write to process's instruction space */
                    840: #define DREAD   2       /* read from process's data space */
                    841: #define DWRITE  5       /* write to process's data space */
                    842: #define CONT    7       /* continue stopped process */
                    843: #define SSTEP   9       /* continue for approximately one instruction */
                    844: #define PKILL   8       /* terminate the process */
                    845: 
                    846: #ifdef IRIS
                    847: #   define readreg(p, r)       ptrace(10, p->pid, r, 0)
                    848: #   define writereg(p, r, v)   ptrace(11, p->pid, r, v)
                    849: #else
                    850: #   define readreg(p, r)       ptrace(UREAD, p->pid, regloc(r), 0);
                    851: #   define writereg(p, r, v)   ptrace(UWRITE, p->pid, regloc(r), v);
                    852: #endif
                    853: 
                    854: /*
                    855:  * Start up a new process by forking and exec-ing the
                    856:  * given argument list, returning when the process is loaded
                    857:  * and ready to execute.  The PROCESS information (pointed to
                    858:  * by the first argument) is appropriately filled.
                    859:  *
                    860:  * If the given PROCESS structure is associated with an already running
                    861:  * process, we terminate it.
                    862:  */
                    863: 
                    864: /* VARARGS2 */
                    865: private pstart(p, argv, infile, outfile)
                    866: Process p;
                    867: String argv[];
                    868: String infile;
                    869: String outfile;
                    870: {
                    871:     int status;
                    872: 
                    873:     if (p->pid != 0) {
                    874:        pterm(p);
                    875:        cacheflush(p);
                    876:     }
                    877:     fflush(stdout);
                    878:     psigtrace(p, SIGTRAP, true);
                    879: #   ifdef IRIS
                    880:        p->pid = fork();
                    881: #   else
                    882:        p->pid = vfork();
                    883: #   endif
                    884:     if (p->pid == -1) {
                    885:        panic("can't fork");
                    886:     }
                    887:     if (ischild(p->pid)) {
                    888:        nocatcherrs();
                    889:        traceme();
                    890:        if (infile != nil) {
                    891:            infrom(infile);
                    892:        }
                    893:        if (outfile != nil) {
                    894:            outto(outfile);
                    895:        }
                    896:        execv(argv[0], argv);
                    897:        _exit(1);
                    898:     }
                    899:     pwait(p->pid, &status);
                    900:     getinfo(p, status);
                    901:     if (p->status != STOPPED) {
                    902:        beginerrmsg();
                    903:        fprintf(stderr, "warning: cannot execute %s\n", argv[0]);
                    904:     } else {
                    905:        ptraced(p->pid);
                    906:     }
                    907: }
                    908: 
                    909: /*
                    910:  * Terminate a ptrace'd process.
                    911:  */
                    912: 
                    913: public pterm (p)
                    914: Process p;
                    915: {
                    916:     integer status;
                    917: 
                    918:     if (p != nil and p->pid != 0) {
                    919:        ptrace(PKILL, p->pid, 0, 0);
                    920:        pwait(p->pid, &status);
                    921:        unptraced(p->pid);
                    922:     }
                    923: }
                    924: 
                    925: /*
                    926:  * Continue a stopped process.  The first argument points to a Process
                    927:  * structure.  Before the process is restarted it's user area is modified
                    928:  * according to the values in the structure.  When this routine finishes,
                    929:  * the structure has the new values from the process's user area.
                    930:  *
                    931:  * Pcont terminates when the process stops with a signal pending that
                    932:  * is being traced (via psigtrace), or when the process terminates.
                    933:  */
                    934: 
                    935: private pcont(p, signo)
                    936: Process p;
                    937: int signo;
                    938: {
                    939:     int s, status;
                    940: 
                    941:     if (p->pid == 0) {
                    942:        error("program is not active");
                    943:     }
                    944:     s = signo;
                    945:     do {
                    946:        setinfo(p, s);
                    947:        if (traceexec) {
                    948:            printf("!! pcont from 0x%x with signal %d (%d)\n",
                    949:                p->reg[PROGCTR], s, p->signo);
                    950:            fflush(stdout);
                    951:        }
                    952:        sigs_off();
                    953:        if (ptrace(CONT, p->pid, p->reg[PROGCTR], p->signo) < 0) {
                    954:            panic("error %d trying to continue process", errno);
                    955:        }
                    956:        pwait(p->pid, &status);
                    957:        sigs_on();
                    958:        getinfo(p, status);
                    959:        if (p->status == STOPPED and traceexec and not istraced(p)) {
                    960:            printf("!! ignored signal %d at 0x%x\n",
                    961:                p->signo, p->reg[PROGCTR]);
                    962:            fflush(stdout);
                    963:        }
                    964:        s = p->signo;
                    965:     } while (p->status == STOPPED and not istraced(p));
                    966:     if (traceexec) {
                    967:        printf("!! pcont to 0x%x on signal %d\n", p->reg[PROGCTR], p->signo);
                    968:        fflush(stdout);
                    969:     }
                    970: }
                    971: 
                    972: /*
                    973:  * Single step as best ptrace can.
                    974:  */
                    975: 
                    976: public pstep(p, signo)
                    977: Process p;
                    978: integer signo;
                    979: {
                    980:     int s, status;
                    981: 
                    982:     s = signo;
                    983:     do {
                    984:        setinfo(p, s);
                    985:        if (traceexec) {
                    986:            printf("!! pstep from 0x%x with signal %d (%d)\n",
                    987:                p->reg[PROGCTR], s, p->signo);
                    988:            fflush(stdout);
                    989:        }
                    990:        sigs_off();
                    991:        if (ptrace(SSTEP, p->pid, p->reg[PROGCTR], p->signo) < 0) {
                    992:            panic("error %d trying to step process", errno);
                    993:        }
                    994:        pwait(p->pid, &status);
                    995:        sigs_on();
                    996:        getinfo(p, status);
                    997: #      if mc68000 || m68000
                    998:            if (p->status == STOPPED and p->signo == SIGTRAP) {
                    999:                p->reg[PROGCTR] += 2;
                   1000:            }
                   1001: #      endif
                   1002:        if (p->status == STOPPED and traceexec and not istraced(p)) {
                   1003:            printf("!! pstep ignored signal %d at 0x%x\n",
                   1004:                p->signo, p->reg[PROGCTR]);
                   1005:            fflush(stdout);
                   1006:        }
                   1007:        s = p->signo;
                   1008:     } while (p->status == STOPPED and not istraced(p));
                   1009:     if (traceexec) {
                   1010:        printf("!! pstep to 0x%x on signal %d\n",
                   1011:            p->reg[PROGCTR], p->signo);
                   1012:        fflush(stdout);
                   1013:     }
                   1014:     if (p->status != STOPPED) {
                   1015:        if (p->exitval == 0) {
                   1016:            error("program exited\n");
                   1017:        } else {
                   1018:            error("program exited with code %d\n", p->exitval);
                   1019:        }
                   1020:     }
                   1021: }
                   1022: 
                   1023: /*
                   1024:  * Return from execution when the given signal is pending.
                   1025:  */
                   1026: 
                   1027: public psigtrace(p, sig, sw)
                   1028: Process p;
                   1029: int sig;
                   1030: Boolean sw;
                   1031: {
                   1032:     if (sw) {
                   1033:        p->sigset |= setrep(sig);
                   1034:     } else {
                   1035:        p->sigset &= ~setrep(sig);
                   1036:     }
                   1037: }
                   1038: 
                   1039: /*
                   1040:  * Don't catch any signals.
                   1041:  * Particularly useful when letting a process finish uninhibited.
                   1042:  */
                   1043: 
                   1044: public unsetsigtraces(p)
                   1045: Process p;
                   1046: {
                   1047:     p->sigset = 0;
                   1048: }
                   1049: 
                   1050: /*
                   1051:  * Turn off attention to signals not being caught.
                   1052:  */
                   1053: 
                   1054: private sig_t sigfunc[NSIG];
                   1055: 
                   1056: private sigs_off()
                   1057: {
                   1058:     register int i;
                   1059: 
                   1060:     for (i = FIRSTSIG; i < LASTSIG; i++) {
                   1061:        if (i != SIGKILL) {
                   1062:            sigfunc[i] = signal(i, SIG_IGN);
                   1063:        }
                   1064:     }
                   1065: }
                   1066: 
                   1067: /*
                   1068:  * Turn back on attention to signals.
                   1069:  */
                   1070: 
                   1071: private sigs_on()
                   1072: {
                   1073:     register int i;
                   1074: 
                   1075:     for (i = FIRSTSIG; i < LASTSIG; i++) {
                   1076:        if (i != SIGKILL) {
                   1077:            signal(i, sigfunc[i]);
                   1078:        }
                   1079:     }
                   1080: }
                   1081: 
                   1082: /*
                   1083:  * Get process information from user area.
                   1084:  */
                   1085: 
                   1086: private getinfo (p, status)
                   1087: register Process p;
                   1088: register int status;
                   1089: {
                   1090:     register int i;
                   1091:     Address addr;
                   1092: 
                   1093:     p->signo = (status&0177);
                   1094:     p->exitval = ((status >> 8)&0377);
                   1095:     if (p->signo != STOPPED) {
                   1096:        p->status = FINISHED;
                   1097:        p->pid = 0;
                   1098:        p->reg[PROGCTR] = 0;
                   1099:     } else {
                   1100:        p->status = p->signo;
                   1101:        p->signo = p->exitval;
                   1102:        p->exitval = 0;
                   1103: #       ifdef IRIS
                   1104:            p->mask = readreg(p, RPS);
                   1105: #       else
                   1106:            p->sigcode = ptrace(UREAD, p->pid, &((struct user *)0)->u_code, 0);
                   1107:            p->mask = readreg(p, PS);
                   1108: #       endif
                   1109:        for (i = 0; i < NREG; i++) {
                   1110:            p->reg[i] = readreg(p, rloc[i]);
                   1111:            p->oreg[i] = p->reg[i];
                   1112:        }
                   1113: #       ifdef mc68000
                   1114:            if (p->status == STOPPED and p->signo == SIGTRAP and
                   1115:                p->reg[PROGCTR] > CODESTART
                   1116:            ) {
                   1117:                p->reg[PROGCTR] -= 2;
                   1118:            }
                   1119: #       endif
                   1120:        savetty(stdout, &(p->ttyinfo));
                   1121:        addr = (Address) &(((struct user *) 0)->u_signal[p->signo]);
                   1122:        p->sigstatus = (Address) ptrace(UREAD, p->pid, addr, 0);
                   1123:     }
                   1124: }
                   1125: 
                   1126: /*
                   1127:  * Set process's user area information from given process structure.
                   1128:  */
                   1129: 
                   1130: private setinfo (p, signo)
                   1131: register Process p;
                   1132: int signo;
                   1133: {
                   1134:     register int i;
                   1135:     register int r;
                   1136: 
                   1137:     if (signo == DEFSIG) {
                   1138:        if (istraced(p) and (p->sigstatus == 0 or p->sigstatus == 1)) {
                   1139:            p->signo = 0;
                   1140:        }
                   1141:     } else {
                   1142:        p->signo = signo;
                   1143:     }
                   1144:     for (i = 0; i < NREG; i++) {
                   1145:        if ((r = p->reg[i]) != p->oreg[i]) {
                   1146:            writereg(p, rloc[i], r);
                   1147:        }
                   1148:     }
                   1149:     restoretty(stdout, &(p->ttyinfo));
                   1150: }
                   1151: 
                   1152: /*
                   1153:  * Return the address associated with the current signal.
                   1154:  * (Plus two since the address points to the beginning of a procedure).
                   1155:  */
                   1156: 
                   1157: public Address usignal (p)
                   1158: Process p;
                   1159: {
                   1160:     Address r;
                   1161: 
                   1162:     r = p->sigstatus;
                   1163:     if (r != 0 and r != 1) {
                   1164:        r += FUNCOFFSET;
                   1165:     }
                   1166:     return r;
                   1167: }
                   1168: 
                   1169: /*
                   1170:  * Structure for reading and writing by words, but dealing with bytes.
                   1171:  */
                   1172: 
                   1173: typedef union {
                   1174:     Word pword;
                   1175:     Byte pbyte[sizeof(Word)];
                   1176: } Pword;
                   1177: 
                   1178: /*
                   1179:  * Read (write) from (to) the process' address space.
                   1180:  * We must deal with ptrace's inability to look anywhere other
                   1181:  * than at a word boundary.
                   1182:  */
                   1183: 
                   1184: private Word fetch();
                   1185: private store();
                   1186: 
                   1187: private pio(p, op, seg, buff, addr, nbytes)
                   1188: Process p;
                   1189: PioOp op;
                   1190: PioSeg seg;
                   1191: char *buff;
                   1192: Address addr;
                   1193: int nbytes;
                   1194: {
                   1195:     register int i;
                   1196:     register Address newaddr;
                   1197:     register char *cp;
                   1198:     char *bufend;
                   1199:     Pword w;
                   1200:     Address wordaddr;
                   1201:     int byteoff;
                   1202: 
                   1203:     if (p->status != STOPPED) {
                   1204:        error("program is not active");
                   1205:     }
                   1206:     cp = buff;
                   1207:     newaddr = addr;
                   1208:     wordaddr = (newaddr&WMASK);
                   1209:     if (wordaddr != newaddr) {
                   1210:        w.pword = fetch(p, seg, wordaddr);
                   1211:        for (i = newaddr - wordaddr; i < sizeof(Word) and nbytes > 0; i++) {
                   1212:            if (op == PREAD) {
                   1213:                *cp++ = w.pbyte[i];
                   1214:            } else {
                   1215:                w.pbyte[i] = *cp++;
                   1216:            }
                   1217:            nbytes--;
                   1218:        }
                   1219:        if (op == PWRITE) {
                   1220:            store(p, seg, wordaddr, w.pword);
                   1221:        }
                   1222:        newaddr = wordaddr + sizeof(Word);
                   1223:     }
                   1224:     byteoff = (nbytes&(~WMASK));
                   1225:     nbytes -= byteoff;
                   1226:     bufend = cp + nbytes;
                   1227: #ifdef tahoe
                   1228:     if (((int)cp)&WMASK) {
                   1229:        /*
                   1230:         * Must copy a byte at a time, buffer not word addressable.
                   1231:         */
                   1232:        while (cp < bufend) {
                   1233:            if (op == PREAD) {
                   1234:                w.pword = fetch(p, seg, newaddr);
                   1235:                for (i = 0; i < sizeof(Word); i++)
                   1236:                    *cp++ = w.pbyte[i];
                   1237:            } else {
                   1238:                for (i = 0; i < sizeof(Word); i++)
                   1239:                    w.pbyte[i] = *cp++;
                   1240:                store(p, seg, newaddr, w.pword);
                   1241:            }
                   1242:            newaddr += sizeof(Word);
                   1243:        }
                   1244:     } else {
                   1245:     /*
                   1246:      * Buffer, word aligned, act normally...
                   1247:      */
                   1248: #endif
                   1249:     while (cp < bufend) {
                   1250:        if (op == PREAD) {
                   1251:            *((Word *) cp) = fetch(p, seg, newaddr);
                   1252:        } else {
                   1253:            store(p, seg, newaddr, *((Word *) cp));
                   1254:        }
                   1255:        cp += sizeof(Word);
                   1256:        newaddr += sizeof(Word);
                   1257:     }
                   1258: #ifdef tahoe
                   1259:     }
                   1260: #endif
                   1261:     if (byteoff > 0) {
                   1262:        w.pword = fetch(p, seg, newaddr);
                   1263:        for (i = 0; i < byteoff; i++) {
                   1264:            if (op == PREAD) {
                   1265:                *cp++ = w.pbyte[i];
                   1266:            } else {
                   1267:                w.pbyte[i] = *cp++;
                   1268:            }
                   1269:        }
                   1270:        if (op == PWRITE) {
                   1271:            store(p, seg, newaddr, w.pword);
                   1272:        }
                   1273:     }
                   1274: }
                   1275: 
                   1276: /*
                   1277:  * Get a word from a process at the given address.
                   1278:  * The address is assumed to be on a word boundary.
                   1279:  *
                   1280:  * A simple cache scheme is used to avoid redundant ptrace calls
                   1281:  * to the instruction space since it is assumed to be pure.
                   1282:  *
                   1283:  * It is necessary to use a write-through scheme so that
                   1284:  * breakpoints right next to each other don't interfere.
                   1285:  */
                   1286: 
                   1287: private Integer nfetchs, nreads, nwrites;
                   1288: 
                   1289: private Word fetch(p, seg, addr)
                   1290: Process p;
                   1291: PioSeg seg;
                   1292: register int addr;
                   1293: {
                   1294:     register CacheWord *wp;
                   1295:     register Word w;
                   1296: 
                   1297:     switch (seg) {
                   1298:        case TEXTSEG:
                   1299:            ++nfetchs;
                   1300:            wp = &p->word[cachehash(addr)];
                   1301:            if (addr == 0 or wp->addr != addr) {
                   1302:                ++nreads;
                   1303:                w = ptrace(IREAD, p->pid, addr, 0);
                   1304:                wp->addr = addr;
                   1305:                wp->val = w;
                   1306:            } else {
                   1307:                w = wp->val;
                   1308:            }
                   1309:            break;
                   1310: 
                   1311:        case DATASEG:
                   1312:            w = ptrace(DREAD, p->pid, addr, 0);
                   1313:            break;
                   1314: 
                   1315:        default:
                   1316:            panic("fetch: bad seg %d", seg);
                   1317:            /* NOTREACHED */
                   1318:     }
                   1319:     return w;
                   1320: }
                   1321: 
                   1322: /*
                   1323:  * Put a word into the process' address space at the given address.
                   1324:  * The address is assumed to be on a word boundary.
                   1325:  */
                   1326: 
                   1327: private store(p, seg, addr, data)
                   1328: Process p;
                   1329: PioSeg seg;
                   1330: int addr;
                   1331: Word data;
                   1332: {
                   1333:     register CacheWord *wp;
                   1334: 
                   1335:     switch (seg) {
                   1336:        case TEXTSEG:
                   1337:            ++nwrites;
                   1338:            wp = &p->word[cachehash(addr)];
                   1339:            wp->addr = addr;
                   1340:            wp->val = data;
                   1341:            ptrace(IWRITE, p->pid, addr, data);
                   1342:            break;
                   1343: 
                   1344:        case DATASEG:
                   1345:            ptrace(DWRITE, p->pid, addr, data);
                   1346:            break;
                   1347: 
                   1348:        default:
                   1349:            panic("store: bad seg %d", seg);
                   1350:            /* NOTREACHED */
                   1351:     }
                   1352: }
                   1353: 
                   1354: /*
                   1355:  * Flush the instruction cache associated with a process.
                   1356:  */
                   1357: 
                   1358: private cacheflush (p)
                   1359: Process p;
                   1360: {
                   1361:     bzero(p->word, sizeof(p->word));
                   1362: }
                   1363: 
                   1364: public printptraceinfo()
                   1365: {
                   1366:     printf("%d fetchs, %d reads, %d writes\n", nfetchs, nreads, nwrites);
                   1367: }
                   1368: 
                   1369: /*
                   1370:  * Redirect input.
                   1371:  * Assuming this is called from a child, we should be careful to avoid
                   1372:  * (possibly) shared standard I/O buffers.
                   1373:  */
                   1374: 
                   1375: private infrom (filename)
                   1376: String filename;
                   1377: {
                   1378:     Fileid in;
                   1379: 
                   1380:     in = open(filename, 0);
                   1381:     if (in == -1) {
                   1382:        write(2, "can't read ", 11);
                   1383:        write(2, filename, strlen(filename));
                   1384:        write(2, "\n", 1);
                   1385:        _exit(1);
                   1386:     }
                   1387:     fswap(0, in);
                   1388: }
                   1389: 
                   1390: /*
                   1391:  * Redirect standard output.
                   1392:  * Same assumptions as for "infrom" above.
                   1393:  */
                   1394: 
                   1395: private outto (filename)
                   1396: String filename;
                   1397: {
                   1398:     Fileid out;
                   1399: 
                   1400:     out = creat(filename, 0666);
                   1401:     if (out == -1) {
                   1402:        write(2, "can't write ", 12);
                   1403:        write(2, filename, strlen(filename));
                   1404:        write(2, "\n", 1);
                   1405:        _exit(1);
                   1406:     }
                   1407:     fswap(1, out);
                   1408: }
                   1409: 
                   1410: /*
                   1411:  * Swap file numbers, useful for redirecting standard input or output.
                   1412:  */
                   1413: 
                   1414: private fswap(oldfd, newfd)
                   1415: Fileid oldfd;
                   1416: Fileid newfd;
                   1417: {
                   1418:     if (oldfd != newfd) {
                   1419:        close(oldfd);
                   1420:        dup(newfd);
                   1421:        close(newfd);
                   1422:     }
                   1423: }
                   1424: 
                   1425: /*
                   1426:  * Signal name manipulation.
                   1427:  */
                   1428: 
                   1429: private String signames[NSIG] = {
                   1430:     0,
                   1431:     "HUP", "INT", "QUIT", "ILL", "TRAP",
                   1432:     "IOT", "EMT", "FPE", "KILL", "BUS",
                   1433:     "SEGV", "SYS", "PIPE", "ALRM", "TERM",
                   1434:     0, "STOP", "TSTP", "CONT", "CHLD",
                   1435:     "TTIN", "TTOU", "TINT", "XCPU", "XFSZ",
                   1436:     "VTALRM", "PROF", "WINCH", "USR1", "USR2"
                   1437: };
                   1438: 
                   1439: /*
                   1440:  * Get the signal number associated with a given name.
                   1441:  * The name is first translated to upper case if necessary.
                   1442:  */
                   1443: 
                   1444: public integer siglookup (s)
                   1445: String s;
                   1446: {
                   1447:     register char *p, *q;
                   1448:     char buf[100];
                   1449:     integer i;
                   1450: 
                   1451:     p = s;
                   1452:     q = buf;
                   1453:     while (*p != '\0') {
                   1454:        if (*p >= 'a' and *p <= 'z') {
                   1455:            *q = (*p - 'a') + 'A';
                   1456:        } else {
                   1457:            *q = *p;
                   1458:        }
                   1459:        ++p;
                   1460:        ++q;
                   1461:     }
                   1462:     *q = '\0';
                   1463:     p = buf;
                   1464:     if (buf[0] == 'S' and buf[1] == 'I' and buf[2] == 'G') {
                   1465:        p += 3;
                   1466:     }
                   1467:     i = 1;
                   1468:     for (;;) {
                   1469:        if (i >= sizeof(signames) div sizeof(signames[0])) {
                   1470:            error("signal \"%s\" unknown", s);
                   1471:            i = 0;
                   1472:            break;
                   1473:        }
                   1474:        if (signames[i] != nil and streq(signames[i], p)) {
                   1475:            break;
                   1476:        }
                   1477:        ++i;
                   1478:     }
                   1479:     return i;
                   1480: }
                   1481: 
                   1482: /*
                   1483:  * Print all signals being ignored by the debugger.
                   1484:  * These signals are auotmatically
                   1485:  * passed on to the debugged process.
                   1486:  */
                   1487: 
                   1488: public printsigsignored (p)
                   1489: Process p;
                   1490: {
                   1491:     printsigs(~p->sigset);
                   1492: }
                   1493: 
                   1494: /*
                   1495:  * Print all signals being intercepted by
                   1496:  * the debugger for the specified process.
                   1497:  */
                   1498: 
                   1499: public printsigscaught(p)
                   1500: Process p;
                   1501: {
                   1502:     printsigs(p->sigset);
                   1503: }
                   1504: 
                   1505: private printsigs (set)
                   1506: integer set;
                   1507: {
                   1508:     integer s;
                   1509:     char separator[2];
                   1510: 
                   1511:     separator[0] = '\0';
                   1512:     for (s = 1; s < sizeof(signames) div sizeof(signames[0]); s++) {
                   1513:        if (set & setrep(s)) {
                   1514:            if (signames[s] != nil) {
                   1515:                printf("%s%s", separator, signames[s]);
                   1516:                separator[0] = ' ';
                   1517:                separator[1] = '\0';
                   1518:            }
                   1519:        }
                   1520:     }
                   1521:     if (separator[0] == ' ') {
                   1522:        putchar('\n');
                   1523:     }
                   1524: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.