Open Computing ``Hands-On'': ``Wizard's Grabbag'' Column: February 1994: Listings

Listing: The upload program collects input characters, writing them to an output file.

A. Listing of the upload program:

  1  #ifdef TERMIO                   /* System V or BSD4.3+ */
  2  #include <termio.h>             /* Declares struct termio and flags */
  3  #define STDIN_FILENO    0       /* Standard-input file descriptor */
  4  struct termio   oldterm,        /* Original driver settings */
  5                  newterm;        /* "raw" I/O driver settings */
  6  #else                           /* Use POSIX serial I/O interface */
  7  #define _POSIX_SOURCE   1       /* Hide vendor extensions to POSIX */
  8  #include <stdlib.h>             /* Declares atoi(), exit(); */
  9  #include <unistd.h>             /* close(), isatty(), read(), write(),
 10                                      tcgetattr(), and tcsetattr() */
 11  #include <termios.h>            /* Declares termios, CRTL(c) */
 12  struct termios  oldterm,        /* Original driver settings */
 13                  newterm;        /* "Raw" I/O driver settings */
 14  #endif  /* POSIX */
 15  
 16  #include <stdio.h>              /* Read these with _POSIX_SOURCE set */
 17  #include <fcntl.h>              /* Defines open() symbolic constants */
 18  
 19  #define CTRL(c)     ((c)&037)   /* Macro to get control char code */
 20  #define CR          CTRL('m')   /* Carriage return is Control-M */
 21  #define CZ          CTRL('z')   /* DOS EOF character is Control Z */
 22  #define FALSE       0           /* Logical falsehood */
 23  #define TRUE        1           /* Logical truth */
 24  static char         *prog;      /* Points to name of this program */
 25  
 26  static char SccsID[] = "@(#) upload.c, Frank Lees, Jan. 89; \
 27  POSIX port by Becca Thomas, read time out by Ray Swartz, Oct. 93";
 28  
 29  void reset_term()   /* Reset serial driver */
 30  {
 31  #ifdef TERMIO
 32      if (ioctl(STDIN_FILENO, TCSETAF, &oldterm) < 0)
 33  #else   /* Assume POSIX */
 34      if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &oldterm) < 0)
 35  #endif
 36          perror("Error resetting serial I/O flags");
 37  }
 38  
 39  void usage()    /* Display correct command-line usage */
 40  {
 41      fprintf(stderr,
 42  "\nUsage: %s [ -b ] [ -m ] [ -q secs ] [ -z ] output-file\n\
 43      -b   Binary transfer  (8 data bits; default: 7 data bits)\n\
 44      -m   Pass Control-Ms to output (default: block them)\n\
 45      -q # Quit time (is rounded up to next 10 secs; default 10 sec)\n\
 46      -z   Pass Control-Zs to output (default: block them)\n\
 47      output-file  Won't overwrite existing file with same path name\n",
 48          prog);
 49  }
 50  
 51  main(argc, argv)
 52  int argc; char *argv[];
 53  {
 54      char            c;                  /* Character read/written */
 55      int             binary = FALSE,     /* Default is text transfer */
 56                      block_M = TRUE,     /* Don't pass carriage retns */
 57                      block_Z = TRUE,     /* Don't pass control-Z */
 58                      fdo,                /* Output-file descriptor */
 59                      loop_count,         /* Outer loop counter */
 60                      optletter,          /* Option letter */
 61                      quit_time = 10,     /* Max wait time betwn chars */
 62                      status = 0;         /* Exit status */
 63      extern char     *optarg;            /* Option argument value */
 64      extern int      optind;             /* Index of next argument */
 65  
 66      prog = argv[0];                     /* Point to program's name */
 67  
 68      /* Process command-line options: */
 69      while ((optletter = getopt(argc, argv, "bmq:z")) != EOF) {
 70          switch(optletter) {
 71              case 'b':   binary = TRUE; break;
 72              case 'm':   block_M = FALSE; break;
 73              case 'q':   if ((quit_time = atoi(optarg)) < 10) {
 74                              fprintf(stderr,
 75                                  "Quit time must greater than 10 sec\n");
 76                              usage(); exit(1);
 77                          }
 78                          /* round up to next highest multiple of ten: */
 79                          quit_time = (10 + ((quit_time - 1)/10) * 10);
 80                          break;
 81              case 'z':   block_Z = FALSE; break;
 82              case '?':   usage(); exit(1);   /* unknown option */
 83          }   /* end of switch statement */
 84      }       /* end of while loop */
 85  
 86      /* Check remaining (non-option) argument(s): */
 87      switch (argc - optind) {            /* number of remaining args */
 88          case 0:     (void)fprintf(stderr,
 89                          "Must specify output-file name argument.\n");
 90                      usage(); exit(1);
 91          case 1:     break;              /* one argument is okay */
 92          default:    (void)fprintf(stderr,
 93                          "Only one non-option argument is allowed.\n");
 94                      usage(); exit(1);
 95      }
 96  
 97      /* Open output-file for writing: */
 98      if ((fdo = open(argv[optind], O_WRONLY|O_CREAT|O_EXCL, 0600)) < 0) {
 99          perror("Error opening output file"); exit(2);
100      }
101  
102      /* Make sure standard input denotes a terminal: */
103      if (!isatty(STDIN_FILENO)) {
104          (void)fprintf(stderr,
105              "Error, standard input is not a terminal device.\n");
106          exit(3);
107      }
108  
109      /* Get original serial I/O settings: */
110  #ifdef TERMIO
111      if (ioctl(STDIN_FILENO, TCGETA, &oldterm) < 0) {
112  #else   /* Assume POSIX */
113      if (tcgetattr(STDIN_FILENO, &oldterm) < 0) {
114  #endif
115          perror("Error getting serial I/O flags"); exit(4);
116      }
117  
118      newterm = oldterm;              /* structure copy */
119      /*  Reset option flags for non-canonical operation:
120          (most SYSV, BSD4.3+, and POSIX option flags are the same) */
121      newterm.c_lflag &= ~(ECHO   | ICANON | ISIG);
122      newterm.c_iflag &= ~(BRKINT | ICRNL  | INPCK | ISTRIP | IXON);
123      newterm.c_cflag &= ~(CSIZE  | PARENB);
124      newterm.c_cflag |= CS8;
125      newterm.c_oflag &= ~(OPOST);
126      newterm.c_cc[VMIN] = 0;
127      newterm.c_cc[VTIME] = 100;  /* Times out after 100 * 0.1 secs */
128  
129      /* Set "raw" mode operation, flushing input queue: */
130  #ifdef TERMIO
131      if (ioctl(STDIN_FILENO, TCSETAF, &newterm) < 0) {
132  #else   /* Assume POSIX */
133       if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &newterm) < 0) {
134  #endif
135          perror("Error setting raw serial I/O"); exit(5);
136      }
137  
138      /* Collect input characters and write to output file: */
139      (void)printf("START of Transfer\r\n");
140      loop_count = quit_time/10;
141      while (loop_count-- > 0) {
142          while (read(STDIN_FILENO, &c, 1)) {
143              if (!binary)
144                  c &= 0x7f;              /* Mask bit 8 if not binary */
145              /* Pass the character if one of these tests is successful:*/
146              if (binary || ((c != CR) && (c != CZ)) ||
147                  ((c == CR) && !block_M) || ((c == CZ) && !block_Z))
148              if (write(fdo, &c, 1) < 0) {
149                  perror("Error writing to output file");
150                  reset_term(); exit(6);
151              }
152              loop_count = (quit_time/10 - 1); /* reset if read */
153          }
154      }
155  
156      if (close(fdo) < 0) {   /* Done with output file */
157          perror("Error closing output file"); reset_term(); exit(7);
158      }
159  
160      (void)printf("END of Transfer\007\r\n");
161      reset_term(); exit(0);
162  }

B. A command line to compile for ``termio''-structure environments:

$ cc -D TERMIO -o upload upload.c

C. Alternate call that will open an existing file without an error indication:

98    if ((fdo = open(argv[optind], O_WRONLY|O_CREAT|O_TRUNC, 0600)) < 0 {

D. The ``termios''-structure definition:

struct termios {
    tcflag_t    c_iflag;    /* input modes */
    tcflag_t    c_oflag;    /* output modes */
    tcflag_t    c_cflag;    /* control modes */
    tcflag_t    c_lflag;    /* line discipline modes */
    cc_t        c_cc[NCCS]; /* control chars */
};

E. The ``termio''-structure definition:

struct termio {
    unsigned short  c_iflag;    /* input modes */
    unsigned short  c_oflag;    /* output modes */
    unsigned short  c_cflag;    /* control modes */
    unsigned short  c_lflag;    /* line discipline modes */
    char            c_line;     /* line discipline */
    unsigned char   c_cc[NCC];  /* control chars */
};

TABLE: Serial-driver structure flags manipulated by upload.

ECHO      Input characters echoed back to source
ICANON    Assemble input into lines, recognize control characters
ISIG      Recognize control characters that cause signals
BRKINT    Generate SIGINT (interrupt signal) on "break"
ICRNL     Received carriage return is translated into newline character
ISTRIP    Strip most-significant bit of each character
IXON      Use start-stop control on input
CSIZE     Character size mask
PARENB    Perform parity checking on incoming characters
CS8       Use all eight bits
OPOST     Use implementation-dependent output processing
VMIN      Minimum number of bytes read before "read" call returns
VTIME     Number of tenths-of-a-second to wait before "read" returns

Copyright © 1995 The McGraw-Hill Companies, Inc. All Rights Reserved.
Edited by Becca Thomas / Online Editor / UnixWorld Online / beccat@wcmh.com

[Go to Contents] [Search Editorial]

Last Modified: Sunday, 10-Sep-95 07:55:46 PDT