home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / misc / volume5 / grabchars1.3 / grabchars.c < prev    next >
Encoding:
C/C++ Source or Header  |  1989-02-03  |  8.3 KB  |  319 lines

  1. /*
  2. **    $Header: grabchars.c,v 1.3 88/12/05 19:46:06 daniel grabchars_1_3 $
  3. **
  4. **    grabchars.c    - get characters directly from the user
  5. **
  6. **    Dan Smith (daniel@island.uu.net), October 23, 1988
  7. **
  8. **    This program grabs characters from the user as they are
  9. **    typed in, without having to wait for the return key to
  10. **    be pressed.  Among other things, this allows shell scripts
  11. **    to be written with highly interactive menus...
  12. **
  13. **    [to jump right to the code, search for "start of grabchars"]
  14. **
  15. **    Usage rundown:
  16. **
  17. **    grabchars            gets one keystroke
  18. **    grabchars -b            output to stdout and stderr
  19. **    grabchars -c<valid characters>  only <valid chars> are returned
  20. **    grabchars -d<char(s)>        default char or string to return
  21. **    grabchars -e            output to stderr instead of stdout
  22. **    grabchars -f            flush any previous input
  23. **    grabchars -h            help screen
  24. **    grabchars -n<number>        number of characters to read
  25. **    grabchars -p<prompt>        prompt to help user
  26. **    grabchars -q<prompt>        prompt to help user (through stderr)
  27. **    grabchars -r            RETURN key exits (use with -n)
  28. **    grabchars -s            silent, just return status
  29. **    grabchars -t<seconds>        timeout after <seconds>
  30. **    grabchars -E            erase/kill character processing
  31. **
  32. **    examples: (values to arguments can be in the same word or the next one)
  33. **
  34. **    grabchars -caeiou     or
  35. **    grabchars -c aeiou        get one of the vowels
  36. **    grabchars -c i            get the letter 'i'
  37. **    grabchars '-penter a letter '    print the prompt "enter a letter "
  38. **    grabchars '-qenter a letter '    print the prompt ('q' for question)
  39. **                    "enter a letter " through stderr...
  40. **    grabchars -n4            get four characters
  41. **    grabchars -t2            timeout after two seconds
  42. **
  43. **    print a prompt and grab three characters...
  44. **    grabchars -p 'enter three characters >> ' -n 3
  45. **
  46. **    get two numbers with a ten second timeout...
  47. **    grabchars -c 0123456789 -n2 -t10
  48. **
  49. **    note that arguments like "-n4" or "-n 4" are handled the same way
  50. **
  51. **    History:
  52. **    
  53. **    Oct 1988: versions 1.0 - 1.1
  54. **
  55. **    November 6, 1988 (1.15)
  56. **    added -f flag to flush input, default is to use    TIOCSETN instead
  57. **    of TIOCSETP
  58. **
  59. **    November 22, 1988 (1.16)
  60. **    added -d flag for a default character or string to use if the
  61. **    user hits return first thing or times out.  handle_default ()
  62. **    was added at this time
  63. **
  64. **    November 23, 1988 (1.19)
  65. **    added -r flag to exit when RETURN is hit.  This was suggested by
  66. **    David Vezie.
  67. **
  68. **    November 29, 1988 (1.2)
  69. **    Disaster strikes...I was updating Makefile.dist, and copied SRCS
  70. **    to OBJS, and forgot to change grabchars.c to grabchars.o, and
  71. **    then (after Config) did a "make clean"!  I realized that I did not
  72. **    have a backup, but fortunately had David Vezie's hack of grabchars.c
  73. **    from a few days ago (he had added erase/line kill character processing)
  74. **    ...moral: use RCS, check your Makefiles!  Used this as an opportunity
  75. **    to split things up into grabchars.h, globals.c,    and sys.c
  76. **    Got -c to handle ranges (via re_comp() and re_exec())
  77. */
  78.  
  79. /*    start of grabchars... */
  80. #include <stdio.h>
  81. #include <signal.h>
  82. #include "grabchars.h"
  83.  
  84. /*    see globals.c */
  85. extern FILE *outfile, *otherout;
  86. extern FLAG *flags;
  87. extern int exit_stat;
  88. extern char *usage_statement[];
  89.  
  90. /*
  91. **    David Vezie (island!r2d2!dv) took a great shot at putting in
  92. **    erase/kill processing.  I need to test this some more, and I'll
  93. **    most likely change it a bit.  I put DV_ERASE in the Makefile
  94. **    as a default; take it out if it misbehaves :-)
  95. */
  96. #ifdef DV_ERASE
  97. extern char *erase_buf;    /* DV: Malloc'ed later */
  98. #endif
  99.  
  100. main (argc, argv)
  101. int argc;
  102. register char **argv;
  103. {
  104.     /* two signal/wrapup handling routines in sys.c */
  105.     int lets_go (), overtime ();
  106.  
  107.     /* for -d option */
  108.     void handle_default ();
  109.  
  110.     /* for getopt () */
  111.     extern int optind, opterr;
  112.     extern char *optarg;
  113.     char comarg;
  114.  
  115.     int how_many = 1;    /* how many chars to read */
  116.     int num_read;        /* how many we have read... */
  117.     int timeout;        /* and an optional time to do it in... */
  118.     int i;            /* for usage_statement if we need it.. */
  119.  
  120.     /*
  121.     **    re_comp (), re_exec () let us do things
  122.     **    like "grabchars -c '[a-d]'" or "grabchars -c '[^a-z]'"...
  123.     */
  124.     char *re_comp (), *re_error;
  125.     extern char valid_chars[128], default_string[128];
  126.     char ch, check_str[2];
  127.  
  128.     alarm (0);
  129.     opterr = 0;
  130.     exit_stat = -1;    /* if we're interrupted, exit with this status */
  131.  
  132.     outfile = stdout;
  133.     otherout = stderr;
  134.     if ((flags = (FLAG *) malloc (sizeof (FLAG))) == (FLAG *) NULL) {
  135.         fprintf (stderr, "can't find enough memory...bye!\n");
  136.         exit (exit_stat);    /* we don't need lets_go () for this */
  137.     }
  138.  
  139.     init_flags ();
  140.     init_signal ();
  141.  
  142.     while ((comarg = getopt (argc, argv,  "befrsEc:d:n:p:q:t:")) != EOF) {
  143.         switch (comarg) {
  144.             case 'b':
  145.                 flags->both = 1;
  146.                 break;
  147.             case 'c':
  148.                 flags->check = 1;
  149.                 strcpy (valid_chars, optarg);
  150.                 if (strlen (optarg) == 0) {
  151.                     fprintf (stderr, "-c option: must have at least one valid character\n");
  152.                     exit (-1);
  153.                 }
  154.                 /*
  155.                 ** most of the time, grabchars can be
  156.                 ** called safely with things like
  157.                 ** "a-z", because we can check to
  158.                 ** see if we need to add brackets...
  159.                 */
  160.                 if (valid_chars[0] != '[' && 
  161.                 valid_chars[strlen (valid_chars) - 1] != ']')
  162.                     sprintf (valid_chars, "%c%s%c",
  163.                         '[', optarg, ']');
  164.  
  165.                 if ((re_error = re_comp (valid_chars))
  166.                                 != NULL) {
  167.                     fprintf (stderr,
  168.                         "-c option: %s\n", re_error);
  169.                     exit (-1);
  170.                 }
  171.                 break;
  172.             case 'd':
  173.                 flags->dflt = 1;
  174.                 strcpy (default_string, optarg);
  175.                 if (strlen (optarg) == 0) {
  176.                 fprintf (stderr, "-d option: must have at least one character for default\n");
  177.                     exit (-1);
  178.                 }
  179.                 break;
  180.             case 'e':
  181.                 outfile = stderr;
  182.                 otherout = stdout;
  183.                 break;
  184.             case 'f':
  185.                 flags->flush = 1;
  186.                 break;
  187.             case 'n':
  188.                 how_many = atoi (optarg);
  189.                 if (how_many <= 0) {
  190.                     fprintf (stderr, "-n option: number of characters to read must be greater than zero\n");
  191.                     exit (-1);
  192.                 }
  193.                 break;
  194.             case 'p':
  195.                 fprintf (stdout, "%s", optarg);
  196.                 break;
  197.             case 'q':
  198.                 fprintf (stderr, "%s", optarg);
  199.                 break;
  200.             case 'r':
  201.                 flags->ret_key = 1;
  202.                 break;
  203.             case 's':
  204.                 flags->silent = 1;
  205.                 break;
  206.             case 't':
  207.                 timeout = atoi (optarg);
  208.                 if (timeout <= 0) {
  209.                     fprintf (stderr, "-t option: number of seconds to timeout must be greater than zero\n");
  210.                     exit (-1);
  211.                 }
  212.  
  213.                 /*
  214.                 ** we must have some valid time >0 seconds to
  215.                 ** get here, so we'll set an alarm...
  216.                 */
  217.                 signal (SIGALRM, overtime);
  218.                 alarm ((unsigned int) timeout);
  219.                 break;
  220.             case 'E':    /* DV: honor erase/kill flag */
  221. #ifdef DV_ERASE
  222.                 flags->erase = 1;
  223. #else
  224.                 fprintf (stderr, "-E is disabled\n");
  225.                 exit (-1);
  226. #endif
  227.                 break;
  228.  
  229.                 /*
  230.                 ** I bet I could leave out "default", but
  231.                 ** I also bet that all getopt () routines
  232.                 ** are not created equal, so in it stays!
  233.                 */
  234.             case '?':
  235.             default:
  236.                 i = 0;
  237.                 while (usage_statement[i])
  238.                     puts (usage_statement[i++]);
  239.                 exit (-1);
  240.         }
  241.     }
  242.  
  243.     /* we're still here, really running...now change the tty... */
  244.     init_term ();
  245.  
  246. #ifdef DV_ERASE
  247.     /* DV: malloc (okay, well calloc) space for the erase buffer */
  248.     if (flags->erase) {
  249.         /* We can't do it up in the switch, because we don't know
  250.         ** how many is how_many
  251.         */
  252.         erase_buf = (char *)calloc (1, how_many);
  253.         if (erase_buf == NULL) {
  254.             fprintf (stderr,
  255.             "Error:  Couldn't malloc space for erase buffer\n");
  256.             exit_stat = -1;
  257.             lets_go();
  258.         }
  259.     }
  260. #endif
  261.  
  262.     for (num_read = 0; num_read < how_many; num_read++) {
  263.         ch = getchar ();
  264.  
  265.         /* use default_string, this does *not* return */
  266.         if (ch == '\n' && flags->dflt && num_read == 0)
  267.             handle_default ();
  268.  
  269.         /*
  270.         ** set by -r, a RETURN key gets us out (use with -n)
  271.         ** suggested by David Vezie
  272.         */
  273.         if (ch == '\n' && flags->ret_key)
  274.             break;
  275.  
  276.         /*    filter chars... */
  277.         if (flags->check) {
  278.             sprintf (check_str, "%c", ch);
  279.             if (re_exec (check_str) != 1) {
  280.                 num_read--;
  281.                 continue;
  282.             }
  283.         }
  284.  
  285.         /*
  286.         ** if we're just looking for a return status
  287.         ** then have flags->silent set (-s)
  288.         **
  289.         ** DV: Also, we don't want to output yet,
  290.         ** if we're processing erase/kill charfacters.
  291.         */
  292. #ifdef DV_ERASE
  293.         if (! flags->silent && ! flags->erase) {
  294. #else
  295.         if (! flags->silent) { 
  296. #endif
  297.             putc (ch, outfile);
  298.             if (flags->both)
  299.                 putc (ch, otherout);
  300.         }
  301.  
  302. #ifdef DV_ERASE
  303.         if (flags->erase)
  304.             handle_erase (ch, &num_read);
  305. #endif
  306.     }
  307.  
  308. #ifdef DV_ERASE
  309.     if (flags->erase) {
  310.         fprintf (outfile, "%s", erase_buf);
  311.         if (flags->both)
  312.             fprintf (otherout, "%s", erase_buf);
  313.     }
  314. #endif
  315.  
  316.     exit_stat = num_read;
  317.     lets_go ();
  318. }
  319.