home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 1 / 1887 / interact.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-12-28  |  16.8 KB  |  911 lines

  1. /*
  2.  * Life search program - user interactions module.
  3.  * Author: David I. Bell.
  4.  */
  5.  
  6. #include "lifesrc.h"
  7.  
  8.  
  9. /*
  10.  * Local procedures
  11.  */
  12. static void    usage();
  13. static void    getsetting();
  14. static void    clearunknowns();
  15. static void    writegen();
  16. static STATUS    loadstate();
  17. static STATUS    readfile();
  18. static BOOL    confirm();
  19. static long    getnum();
  20. static char    *getstr();
  21.  
  22.  
  23. main(argc, argv)
  24.     char    **argv;
  25. {
  26.     char    *str;
  27.     STATUS    status;
  28.  
  29.     if (--argc <= 0) {
  30.         usage();
  31.         exit(1);
  32.     }
  33.     argv++;
  34.  
  35.     while (argc-- > 0) {
  36.         str = *argv++;
  37.         if (*str++ != '-') {
  38.             usage();
  39.             exit(1);
  40.         }
  41.         switch (*str++) {
  42.             case 'r':            /* rows */
  43.                 rowmax = atoi(str);
  44.                 break;
  45.  
  46.             case 'c':            /* columns */
  47.                 colmax = atoi(str);
  48.                 break;
  49.  
  50.             case 'g':            /* generations */
  51.                 genmax = atoi(str);
  52.                 break;
  53.  
  54.             case 't':            /* translation */
  55.                 switch (*str++) {
  56.                     case 'r':
  57.                         rowtrans = atoi(str);
  58.                         break;
  59.                     case 'c':
  60.                         coltrans = atoi(str);
  61.                         break;
  62.                     default:
  63.                         fprintf(stderr, "Bad translate\n");
  64.                         exit(1);
  65.                 }
  66.                 break;
  67.  
  68.             case 's':            /* symmetry */
  69.                 switch (*str++) {
  70.                     case 'r':
  71.                         rowsym = TRUE;
  72.                         break;
  73.                     case 'c':
  74.                         colsym = TRUE;
  75.                         break;
  76.                     default:
  77.                         fprintf(stderr, "Bad symmetry\n");
  78.                         exit(1);
  79.                 }
  80.                 break;
  81.  
  82.             case 'd':            /* dump frequency */
  83.                 dumpfreq = atol(str) * DUMPMULT;
  84.                 dumpfile = DUMPFILE;
  85.                 if ((argc > 0) && (**argv != '-')) {
  86.                     argc--;
  87.                     dumpfile = *argv++;
  88.                 }
  89.                 break;
  90.  
  91.             case 'v':            /* view frequency */
  92.                 viewfreq = atol(str) * VIEWMULT;
  93.                 break;
  94.  
  95.             case 'l':            /* load file */
  96.                 if (*str == 'n')
  97.                     nowait = TRUE;
  98.                 if ((argc <= 0) || (**argv == '-')) {
  99.                     fprintf(stderr, "Missing load file name\n");
  100.                     exit(1);
  101.                 }
  102.                 loadfile = *argv++;
  103.                 argc--;
  104.                 break;
  105.  
  106.             case 'i':            /* initial file */
  107.                 if (*str == 'a')
  108.                     setall = TRUE;
  109.                 if ((argc <= 0) || (**argv == '-')) {
  110.                     fprintf(stderr, "Missing initial file name\n");
  111.                     exit(1);
  112.                 }
  113.                 initfile = *argv++;
  114.                 argc--;
  115.                 break;
  116.  
  117.             case 'o':            /* output file */
  118.                 if ((argc <= 0) || (**argv == '-')) {
  119.                     fprintf(stderr, "Missing output file name\n");
  120.                     exit(1);
  121.                 }
  122.                 outputfile = *argv++;
  123.                 argc--;
  124.                 break;
  125.  
  126.             case 'm':            /* max cell count */
  127.                 maxcount = atoi(str);
  128.                 break;
  129.  
  130.             case 'p':            /* find parents only */
  131.                 parent = TRUE;
  132.                 break;
  133.  
  134.             case 'a':
  135.                 allobjects = TRUE;    /* find all objects */
  136.                 break;
  137.  
  138.             case 'D':            /* debugging output */
  139.                 debug = TRUE;
  140.                 break;
  141.  
  142.             default:
  143.                 fprintf(stderr, "Unknown option -%c\n",
  144.                     str[-1]);
  145.                 exit(1);
  146.         }
  147.     }
  148.  
  149.     if (parent && (rowtrans || coltrans)) {
  150.         fprintf(stderr, "Cannot specify -tr or -tc with -p\n");
  151.         exit(1);
  152.     }
  153.  
  154.     if (ttyopen()) {
  155.         fprintf(stderr, "Cannot initialize terminal\n");
  156.         exit(1);
  157.     }
  158.  
  159.     /*
  160.      * Check for loading state from file or reading initial
  161.      * object from file.
  162.      */
  163.     if (loadfile) {
  164.         if (loadstate(loadfile) != OK) {
  165.             ttyclose();
  166.             exit(1);
  167.         }
  168.     } else {
  169.         initcells();
  170.         if (initfile) {
  171.             if (readfile(initfile) != OK) {
  172.                 ttyclose();
  173.                 exit(1);
  174.             }
  175.             baseset = nextset;
  176.         }
  177.     }
  178.  
  179.     /*
  180.      * If we are looking for parents, then set the current generation
  181.      * to the last one so that it can be input easily.  Then get the
  182.      * commands to initialize the cells, unless we were told to not wait.
  183.      */
  184.     if (parent)
  185.         curgen = genmax - 1;
  186.  
  187.     if (nowait)
  188.         printgen(0);
  189.     else
  190.         getcommands();
  191.  
  192.     /*
  193.      * Initial commands are complet, now look for the object.
  194.      */
  195.     while (TRUE) {
  196.         if (curstatus == OK)
  197.             curstatus = search();
  198.  
  199.         if (dumpfreq) {
  200.             dumpcount = 0;
  201.             dumpstate(dumpfile);
  202.         }
  203.  
  204.         if ((curstatus == FOUND) && !allobjects && subperiods()) {
  205.             curstatus = OK;
  206.             continue;
  207.         }
  208.  
  209.         curgen = 0;
  210.  
  211.         if (outputfile == NULL) {
  212.             getcommands();
  213.             continue;
  214.         }
  215.  
  216.         /*
  217.          * Here if results are going to a file.
  218.          */
  219.         if (curstatus == FOUND) {
  220.             curstatus = OK;
  221.             printgen(0);
  222.             ttystatus("Object %ld found.\n", ++foundcount);
  223.             writegen(outputfile, TRUE);
  224.             continue;
  225.         }
  226.  
  227.         if (foundcount == 0) {
  228.             ttyclose();
  229.             fprintf(stderr, "No objects found\n");
  230.             exit(1);
  231.         }
  232.  
  233.         ttyclose();
  234.         printf("Search completed, file \"%s\" contains %ld object%s\n",
  235.             outputfile, foundcount, (foundcount == 1) ? "" : "s");
  236.         exit(0);
  237.     }
  238. }
  239.  
  240.  
  241. /*
  242.  * Get one or more user commands.
  243.  * Commands are ended by a blank line.
  244.  */
  245. void
  246. getcommands()
  247. {
  248.     char    *cp;
  249.     char    *cmd;
  250.     char    buf[LINESIZE];
  251.  
  252.     dumpcount = 0;
  253.     viewcount = 0;
  254.     printgen(curgen);
  255.  
  256.     while (TRUE) {
  257.         ttyread("> ", buf, LINESIZE);
  258.         cp = buf;
  259.         while (isblank(*cp))
  260.             cp++;
  261.         cmd = cp;
  262.         if (*cp)
  263.             cp++;
  264.         while (isblank(*cp))
  265.             cp++;
  266.  
  267.         switch (*cmd) {
  268.             case 'p':        /* print previous generation */
  269.                 printgen((curgen + genmax - 1) % genmax);
  270.                 break;
  271.  
  272.             case 'n':        /* print next generation */
  273.                 printgen((curgen + 1) % genmax);
  274.                 break;
  275.  
  276.             case 's':        /* add a cell setting */
  277.                 getsetting(cp);
  278.                 break;
  279.  
  280.             case 'c':        /* clear rest of generation */
  281.                 if (confirm("Clear all unknown cells? "))
  282.                     clearunknowns();
  283.                 break;
  284.  
  285.             case 'v':        /* set view frequency */
  286.                 viewfreq = atol(cp) * VIEWMULT;
  287.                 printgen(curgen);
  288.                 break;
  289.  
  290.             case 'w':        /* write generation to file */
  291.                 writegen(cp, FALSE);
  292.                 break;
  293.  
  294.             case 'd':        /* dump state to file */
  295.                 dumpstate(cp);
  296.                 break;
  297.  
  298.             case 'N':        /* find next object */
  299.                 if (curstatus == FOUND)
  300.                     curstatus = OK;
  301.                 return;
  302.  
  303.             case 'q':        /* quit program */
  304.             case 'Q':
  305.                 if (confirm("Really quit? ")) {
  306.                     ttyclose();
  307.                     exit(0);
  308.                 }
  309.                 break;
  310.  
  311.             case '\n':        /* return from commands */
  312.             case '\0':
  313.                 return;
  314.  
  315.             default:
  316.                 if (isdigit(*cmd)) {
  317.                     getsetting(cmd);
  318.                     break;
  319.                 }
  320.                 ttystatus("Unknown command\n");
  321.                 break;
  322.         }
  323.     }
  324. }
  325.  
  326.  
  327. /*
  328.  * Get a cell to be set in the current generation.
  329.  * The state of the cell is defaulted to ON.
  330.  * Warning: Use of this routine invalidates backing up over
  331.  * the setting, so that the setting is permanent.
  332.  */
  333. static void
  334. getsetting(cp)
  335.     char    *cp;
  336. {
  337.     int    row;
  338.     int    col;
  339.     STATE    state;
  340.  
  341.     cp = getstr(cp, "Cell to set (row col [state]): ");
  342.     if (*cp == '\0')
  343.         return;
  344.  
  345.     row = getnum(&cp, -1);
  346.     if (*cp == ',')
  347.         cp++;
  348.     col = getnum(&cp, -1);
  349.     if (*cp == ',')
  350.         cp++;
  351.     state = getnum(&cp, 1);
  352.  
  353.     while (isblank(*cp))
  354.         cp++;
  355.     if (*cp != '\0') {
  356.         ttystatus("Bad input line format\n");
  357.         return;
  358.     }
  359.  
  360.     if ((row <= 0) || (row > rowmax) || (col <= 0) || (col > colmax) ||
  361.         (state < 0) || (state > 1))
  362.     {
  363.         ttystatus("Illegal cell value\n");
  364.         return;
  365.     }
  366.  
  367.     if (proceed(findcell(row, col, curgen), state, FALSE) != OK) {
  368.         ttystatus("Inconsistent state for cell\n");
  369.         return;
  370.     }
  371.  
  372.     baseset = nextset;
  373.     printgen(curgen);
  374. }
  375.  
  376.  
  377. /*
  378.  * Clear all remaining unknown cells in the current generation.
  379.  * This is used when searching for parents of a configuration.
  380.  */
  381. static void
  382. clearunknowns()
  383. {
  384.     int    row;
  385.     int    col;
  386.     CELL    *cell;
  387.  
  388.     for (row = 1; row <= rowmax; row++) {
  389.         for (col = 1; col <= colmax; col++) {
  390.             cell = findcell(row, col, curgen);
  391.             if (cell->state != UNK)
  392.                 continue;
  393.  
  394.             if (proceed(cell, OFF, FALSE) != OK)
  395.             {
  396.                 ttystatus("Inconsistent state for cell\n");
  397.                 return;
  398.             }
  399.         }
  400.     }
  401.  
  402.     baseset = nextset;
  403.     printgen(curgen);
  404. }
  405.  
  406.  
  407. /*
  408.  * Print out the current status of the specified generation.
  409.  * This also sets the current generation.
  410.  */
  411. void
  412. printgen(gen)
  413. {
  414.     int    row;
  415.     int    col;
  416.     int    count;
  417.     CELL    *cell;
  418.     char    *msg;
  419.  
  420.     curgen = gen;
  421.  
  422.     switch (curstatus) {
  423.         case NOTEXIST:    msg = "No such object"; break;
  424.         case FOUND:    msg = "Found object"; break;
  425.         default:    msg = ""; break;
  426.     }
  427.  
  428.     count = 0;
  429.     for (row = 1; row <= rowmax; row++) {
  430.         for (col = 1; col <= colmax; col++) {
  431.             count += (findcell(row, col, gen)->state == ON);
  432.         }
  433.     }
  434.  
  435.     ttyhome();
  436.     ttyeeop();
  437.  
  438.     ttyprintf("%s (gen %d, cells %d) -g%d", msg, gen, count, genmax);
  439.     if (rowtrans)
  440.         ttyprintf(" -tr%d", rowtrans);
  441.     if (coltrans)
  442.         ttyprintf(" -tc%d", coltrans);
  443.     if (rowsym)
  444.         ttyprintf(" -sr");
  445.     if (colsym)
  446.         ttyprintf(" -sc");
  447.     if (parent)
  448.         ttyprintf(" -p");
  449.     if (allobjects)
  450.         ttyprintf(" -a");
  451.     if (maxcount)
  452.         ttyprintf(" -m%d", maxcount);
  453.     if (viewfreq)
  454.         ttyprintf(" -v%d", viewfreq / VIEWMULT);
  455.     if (dumpfreq)
  456.         ttyprintf(" -d%d %s", dumpfreq / DUMPMULT, dumpfile);
  457.     if (outputfile)
  458.         ttyprintf(" -o %s", outputfile);
  459.     ttyprintf("\n");
  460.  
  461.     for (row = 1; row <= rowmax; row++) {
  462.         for (col = 1; col <= colmax; col++) {
  463.             cell = findcell(row, col, gen);
  464.             switch (cell->state) {
  465.                 case OFF:    msg = ". "; break;
  466.                 case ON:    msg = "O "; break;
  467.                 case UNK:    msg = "? "; break;
  468.             }
  469.  
  470.             /*
  471.              * If wide output, print only one character,
  472.              * else print both characters.
  473.              */
  474.             ttywrite(msg, (colmax < 40) + 1);
  475.         }
  476.         ttywrite("\n", 1);
  477.     }
  478.  
  479.     ttyhome();
  480.     ttyflush();
  481. }
  482.  
  483.  
  484. /*
  485.  * Write the current generation to the specified file.
  486.  * Empty rows and columns are not written.
  487.  * If no file is specified, it is asked for.
  488.  */
  489. static void
  490. writegen(file, append)
  491.     char    *file;        /* file name (or NULL) */
  492.     BOOL    append;        /* TRUE to append instead of create */
  493. {
  494.     FILE    *fp;
  495.     CELL    *cell;
  496.     int    row;
  497.     int    col;
  498.     int    ch;
  499.     int    minrow, maxrow, mincol, maxcol;
  500.  
  501.     file = getstr(file, "Write object to file: ");
  502.     if (*file == '\0')
  503.         return;
  504.  
  505.     fp = fopen(file, append ? "a" : "w");
  506.     if (fp == NULL) {
  507.         ttystatus("Cannot create \"%s\"\n", file);
  508.         return;
  509.     }
  510.  
  511.     /*
  512.      * First find the minimum bounds on the object.
  513.      */
  514.     minrow = rowmax;
  515.     mincol = colmax;
  516.     maxrow = 1;
  517.     maxcol = 1;
  518.  
  519.     for (row = 1; row <= rowmax; row++) {
  520.         for (col = 1; col <= colmax; col++) {
  521.             cell = findcell(row, col, curgen);
  522.             if (cell->state == OFF)
  523.                 continue;
  524.             if (row < minrow)
  525.                 minrow = row;
  526.             if (row > maxrow)
  527.                 maxrow = row;
  528.             if (col < mincol)
  529.                 mincol = col;
  530.             if (col > maxcol)
  531.                 maxcol = col;
  532.         }
  533.     }
  534.  
  535.     if (minrow > maxrow) {
  536.         minrow = 1;
  537.         maxrow = 1;
  538.         mincol = 1;
  539.         maxcol = 1;
  540.     }
  541.  
  542.     /*
  543.      * Now write out the bounded area.
  544.      */
  545.     for (row = minrow; row <= maxrow; row++) {
  546.         for (col = mincol; col <= maxcol; col++) {
  547.             cell = findcell(row, col, curgen);
  548.             switch (cell->state) {
  549.                 case OFF:    ch = '.'; break;
  550.                 case ON:    ch = '*'; break;
  551.                 case UNK:    ch = '?'; break;
  552.             }
  553.             fputc(ch, fp);
  554.         }
  555.         fputc('\n', fp);
  556.     }
  557.  
  558.     if (append)
  559.         fprintf(fp, "\n");
  560.  
  561.     if (fclose(fp))
  562.         ttystatus("Error writing \"%s\"\n", file);
  563.     else
  564.         ttystatus("\"%s\" written\n", file);
  565. }
  566.  
  567.  
  568. /*
  569.  * Dump the current state of the search in the specified file.
  570.  * If no file is specified, it is asked for.
  571.  */
  572. void
  573. dumpstate(file)
  574.     char    *file;
  575. {
  576.     FILE    *fp;
  577.     CELL    **set;
  578.     CELL    *cell;
  579.  
  580.     file = getstr(file, "Dump state to file: ");
  581.     if (*file == '\0')
  582.         return;
  583.  
  584.     fp = fopen(file, "w");
  585.     if (fp == NULL) {
  586.         ttystatus("Cannot create \"%s\"\n", file);
  587.         return;
  588.     }
  589.  
  590.     fprintf(fp, "V %d\n", DUMPVERSION);
  591.     fprintf(fp, "I %d %d %d %d %d %d %d %d %d %d %d %d\n", rowmax, colmax,
  592.         genmax, rowtrans, coltrans, rowsym, colsym, parent,
  593.         allobjects, maxcount, cellcount, curstatus);
  594.  
  595.     set = settable;
  596.     while (set != nextset) {
  597.         cell = *set++;
  598.         fprintf(fp, "S %d %d %d %d %d\n", cell->row, cell->col,
  599.             cell->gen, cell->state, cell->free);
  600.     }
  601.  
  602.     fprintf(fp, "T %d %d\n", baseset - settable, nextset - settable);
  603.     fprintf(fp, "E\n");
  604.  
  605.     if (fclose(fp)) {
  606.         ttystatus("Error writing \"%s\"\n", file);
  607.         return;
  608.     }
  609.  
  610.     ttystatus("State dumped to \"%s\"\n", file);
  611. }
  612.  
  613.  
  614. /*
  615.  * Load a previously dumped state from a file.
  616.  * Warning: Almost no checks are made for validity of the state.
  617.  * Returns OK on success, ERROR on failure.
  618.  */
  619. static STATUS
  620. loadstate(file)
  621.     char    *file;
  622. {
  623.     FILE    *fp;
  624.     char    *cp;
  625.     int    row;
  626.     int    col;
  627.     int    gen;
  628.     STATE    state;
  629.     BOOL    free;
  630.     CELL    *cell;
  631.     char    buf[LINESIZE];
  632.  
  633.     file = getstr(file, "Load state from file: ");
  634.     if (*file == '\0')
  635.         return OK;
  636.  
  637.     fp = fopen(file, "r");
  638.     if (fp == NULL) {
  639.         ttystatus("Cannot open state file \"%s\"\n", file);
  640.         return ERROR;
  641.     }
  642.  
  643.     buf[0] = '\0';
  644.     fgets(buf, LINESIZE, fp);
  645.     if (buf[0] != 'V') {
  646.         ttystatus("Missing version line in file \"%s\"\n", file);
  647.         fclose(fp);
  648.         return ERROR;
  649.     }
  650.  
  651.     cp = &buf[1];
  652.     if (getnum(&cp, 0) != DUMPVERSION) {
  653.         ttystatus("Unknown version in state file \"%s\"\n", file);
  654.         fclose(fp);
  655.         return ERROR;
  656.     }
  657.  
  658.     fgets(buf, LINESIZE, fp);
  659.     if (buf[0] != 'I') {
  660.         ttystatus("Missing init line in state file\n");
  661.         fclose(fp);
  662.         return ERROR;
  663.     }
  664.     cp = &buf[1];
  665.     rowmax = getnum(&cp, 0);
  666.     colmax = getnum(&cp, 0);
  667.     genmax = getnum(&cp, 0);
  668.     rowtrans = getnum(&cp, 0);
  669.     coltrans = getnum(&cp, 0);
  670.     rowsym = getnum(&cp, 0);
  671.     colsym = getnum(&cp, 0);
  672.     parent = getnum(&cp, 0);
  673.     allobjects = getnum(&cp, 0);
  674.     maxcount = getnum(&cp, 0);
  675.     cellcount = getnum(&cp, 0);
  676.     curstatus = getnum(&cp, 0);
  677.  
  678.     initcells();
  679.  
  680.     newset = settable;
  681.     for (;;) {
  682.         buf[0] = '\0';
  683.         fgets(buf, LINESIZE, fp);
  684.         if (buf[0] != 'S')
  685.             break;
  686.  
  687.         cp = &buf[1];
  688.         row = getnum(&cp, 0);
  689.         col = getnum(&cp, 0);
  690.         gen = getnum(&cp, 0);
  691.         state = getnum(&cp, 0);
  692.         free = getnum(&cp, 0);
  693.  
  694.         cell = findcell(row, col, gen);
  695.         cell->state = state;
  696.         cell->free = free;
  697.         *newset++ = cell;
  698.     }
  699.  
  700.     if (buf[0] != 'T') {
  701.         ttystatus("Missing table line in state file\n");
  702.         fclose(fp);
  703.         return ERROR;
  704.     }
  705.     cp = &buf[1];
  706.     baseset = &settable[getnum(&cp, 0)];
  707.     nextset = &settable[getnum(&cp, 0)];
  708.  
  709.     fgets(buf, LINESIZE, fp);
  710.     if (buf[0] != 'E') {
  711.         ttystatus("Missing end of file line in state file\n");
  712.         fclose(fp);
  713.         return ERROR;
  714.     }
  715.  
  716.     if (fclose(fp)) {
  717.         ttystatus("Error reading \"%s\"\n", file);
  718.         return ERROR;
  719.     }
  720.  
  721.     ttystatus("State loaded from \"%s\"\n", file);
  722.     return OK;
  723. }
  724.  
  725.  
  726. /*
  727.  * Read a file containing initial settings for either gen 0 or the last gen.
  728.  * If setall is TRUE, both the ON and the OFF cells will be set.
  729.  * Returns OK on success, ERROR on error.
  730.  */
  731. static STATUS
  732. readfile(file)
  733.     char    *file;
  734. {
  735.     FILE    *fp;
  736.     char    *cp;
  737.     char    ch;
  738.     int    row;
  739.     int    col;
  740.     int    gen;
  741.     STATE    state;
  742.     char    buf[LINESIZE];
  743.  
  744.     file = getstr(file, "Read initial object from file: ");
  745.     if (*file == '\0')
  746.         return OK;
  747.  
  748.     fp = fopen(file, "r");
  749.     if (fp == NULL) {
  750.     CELL    **set;
  751.         ttystatus("Cannot open \"%s\"\n", file);
  752.         return ERROR;
  753.     }
  754.  
  755.     gen = (parent ? (genmax - 1) : 0);
  756.     row = 0;
  757.     while (fgets(buf, LINESIZE, fp)) {
  758.         row++;
  759.         cp = buf;
  760.         col = 0;
  761.         while (*cp && (*cp != '\n')) {
  762.             col++;
  763.             ch = *cp++;
  764.             switch (ch) {
  765.                 case '?':
  766.                     continue;
  767.                 case '.':
  768.                 case ' ':
  769.                     if (!setall)
  770.                         continue;
  771.                     state = OFF;
  772.                     break;
  773.                 case 'O':
  774.                 case 'o':
  775.                 case '*':
  776.                     state = ON;
  777.                     break;
  778.                 default:
  779.                     ttystatus("Bad file format in line %d\n",
  780.                         row);
  781.                     fclose(fp);
  782.                     return ERROR;
  783.             }
  784.  
  785.             if (proceed(findcell(row, col, gen), state, FALSE)
  786.                 != OK)
  787.             {
  788.                 ttystatus("Inconsistent state for cell %d %d\n",
  789.                     row, col);
  790.                 fclose(fp);
  791.                 return ERROR;
  792.             }
  793.         }
  794.     }
  795.  
  796.     if (fclose(fp)) {
  797.         ttystatus("Error reading \"%s\"\n", file);
  798.         return ERROR;
  799.     }
  800.     return OK;
  801. }
  802.  
  803.  
  804. /*
  805.  * Check a string for being NULL, and if so, ask the user to specify a
  806.  * value for it.  Returned string may be static and thus is overwritten
  807.  * for each call.  Leading spaces in the string are skipped over.
  808.  */
  809. static char *
  810. getstr(str, prompt)
  811.     char    *str;        /* string to check for NULLness */
  812.     char    *prompt;    /* message to prompt with */
  813. {
  814.     static char    buf[LINESIZE];
  815.  
  816.     if ((str == NULL) || (*str == '\0')) {
  817.         ttyread(prompt, buf, LINESIZE);
  818.         str = buf;
  819.     }
  820.     while (isblank(*str))
  821.         str++;
  822.     return str;
  823. }
  824.  
  825.  
  826. /*
  827.  * Confirm an action by prompting with the specified string and reading
  828.  * an answer.  Entering 'y' or 'Y' indicates TRUE, everything else FALSE.
  829.  */
  830. static BOOL
  831. confirm(prompt)
  832.     char    *prompt;
  833. {
  834.     char    ch;
  835.  
  836.     ch = *getstr(NULL, prompt);
  837.     if ((ch == 'y') || (ch == 'Y'))
  838.         return TRUE;
  839.     return FALSE;
  840. }
  841.  
  842.  
  843. /*
  844.  * Read a number from a string, eating any leading or trailing blanks.
  845.  * Returns the value, and indirectly updates the string pointer.
  846.  * Returns specified default if no number was found.
  847.  */
  848. static long
  849. getnum(cpp, defnum)
  850.     char    **cpp;
  851.     int    defnum;
  852. {
  853.     char    *cp;
  854.     long    num;
  855.  
  856.     cp = *cpp;
  857.     while (isblank(*cp))
  858.         cp++;
  859.     if (!isdigit(*cp)) {
  860.         *cpp = cp;
  861.         return defnum;
  862.     }
  863.     num = 0;
  864.     while (isdigit(*cp))
  865.         num = num * 10 + (*cp++ - '0');
  866.     while (isblank(*cp))
  867.         cp++;
  868.     *cpp = cp;
  869.     return num;
  870. }
  871.  
  872.  
  873. /*
  874.  * Print usage text.
  875.  */
  876. static void
  877. usage()
  878. {
  879.     char    **cpp;
  880.     static char *text[] = {
  881.     "Program to search for life oscillators or spaceships.",
  882.     "",
  883. "lifesrc -r# -c# -g# -tr# -tc# -m# -sr -sc -p -a -v# -i[a] file -o file -d# file",
  884. "lifesrc -l[n] file -v# -o file -d# file",
  885.         "",
  886.         "   -r   Number of rows",
  887.         "   -c   Number of columns",
  888.         "   -g   Number of generations",
  889.         "   -tr  Translate rows between last and first generation",
  890.         "   -tc  Translate columns between last and first generation",
  891.         "   -m   Maximum live cells for generation 0",
  892.         "   -sr  Enforce symmetry on rows",
  893.         "   -sc  Enforce symmetry on columns",
  894.         "   -p   Only look for parents of last generation",
  895.         "   -a   Find all objects (even those with subperiods)",
  896.         "   -v   View object every N thousand searches",
  897.         "   -d   Dump status to file every N thousand searches",
  898.         "   -l   Load status from file",
  899.         "   -ln  Load status without entering command mode",
  900.         "   -i   Read initial object from file setting only ON cells",
  901.         "   -ia  Read initial object setting both ON and OFF cells",
  902.         "   -o   Output found objects to file (appending)",
  903.         NULL
  904.     };
  905.  
  906.     for (cpp = text; *cpp; cpp++)
  907.         fprintf(stderr, "%s\n", *cpp);
  908. }
  909.  
  910. /* END CODE */
  911.