home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / misc / volume9 / tbench / perf.c < prev    next >
Encoding:
C/C++ Source or Header  |  1989-11-12  |  15.9 KB  |  851 lines

  1. /*     :ex:se ts=4 sw=4    This program formatted with tabs 4 */
  2.  
  3. /****************************************************
  4.  *     This software released to the public domain  *
  5.  *     without restrictions of any kind.            *
  6.  ****************************************************/
  7.  
  8. /*
  9.  *    NAME
  10.  *        perf - Test I/O system PERFormance.
  11.  *
  12.  *    SYNOPSIS
  13.  *        perf [ -qsv ] [ -c command ] [ -e text ]
  14.  *             [ -t throttle ] [ -x outcount ] [ outcount ... ]
  15.  *
  16.  *    DESCRIPTION
  17.  *        Perf measures the transmission of characters through
  18.  *        a communication media, keeping statistics on throughput
  19.  *        and system utilization.
  20.  *
  21.  *        When the any of the options c, e, q, t or x, or an
  22.  *        outcount is specified, the program runs in output mode;
  23.  *        Otherwise it runs in input mode.  A perf in output mode
  24.  *        may be used to test a system I/O subsystem output speed
  25.  *        directly,  or its output may be fed through communication
  26.  *        hardware back into a perf in input mode for input
  27.  *        performance testing.
  28.  *
  29.  *        -c shell_command    Output the given shell command to be
  30.  *                            executed by the receiving perf program.
  31.  *
  32.  *        -e text                Output the given text to be displayed
  33.  *                            by the receiving perf program.
  34.  *
  35.  *        -q                    Output code 99998, which tells the
  36.  *                            receiving perf to terminate.
  37.  *
  38.  *        -s                    Silent option.  Suppress warning messages.
  39.  *
  40.  *        -t throttle            Throttle the input by sleeping for
  41.  *                            one second every throttle characters.
  42.  *
  43.  *        -v                    On input, print statistics showing
  44.  *                            the length and number of times a
  45.  *                            stream of characters was missed.
  46.  *
  47.  *        -x outchar            Output enough 6 digit + checksum
  48.  *                            codes to produce the given number of
  49.  *                            characters.
  50.  *    
  51.  *        outcount            Same as -x outcount above.
  52.  *
  53.  *        All data sent by an output perf can be interpreted by
  54.  *        an input perf, but the data is all printable ASCII in
  55.  *        a simple format so a human can understand it as well.
  56.  *
  57.  *        To test system throughput, the perf program uses 6 digit
  58.  *        sequential codes, with a checksum.  This format allows
  59.  *        the input program--or a human observer--to easily and
  60.  *        quantitatively compare    desired to actual data.
  61.  *
  62.  *        For input testing, it is often valuable to send data
  63.  *        from one system to another.  In this mode, perf can
  64.  *        pass diagnostics text and shell commands through the
  65.  *        communications link.  This allows the output system
  66.  *        to control the overall test procedure, and provide a
  67.  *        test log on the input system.
  68.  *
  69.  *        To improve accuracy, on output a code of 99998 is output
  70.  *        for about 10K characters before measurement begins and
  71.  *        after measurement completes.  This is done so the
  72.  *        measurement is more likely to be done under steady-state
  73.  *        conditions.
  74.  *
  75.  *    WARNING
  76.  *        The order of parameters 2 & 3 on setvbuf() vary from
  77.  *        system, and are even inconsistent between the manuals
  78.  *        and the libraries on stock system V.2.  You are wise
  79.  *        to check the order of the parameters on your system and
  80.  *        set the SETVBUF #define accordingly.
  81.  */
  82.  
  83. #if !defined(lint)
  84. char whatstring[] = "@(#)perf.c    3.1 9/22/89" ;
  85. #endif
  86.  
  87. #if sun
  88. #define BSD 1
  89. #endif
  90.  
  91. #if BSD
  92. #    include <sys/types.h>
  93. #    include <sys/time.h>
  94. #    include <sys/resource.h>
  95. #else
  96. #    include <sys/types.h>
  97. #    include <sys/times.h>
  98. #    include <sys/param.h>
  99. #endif
  100.  
  101. #include <ctype.h>
  102. #include <stdio.h>
  103.  
  104. #define uchar unsigned char
  105. #define ushort unsigned short
  106. #define ulong unsigned long
  107.  
  108. extern char *optarg ;
  109. extern int optind ;
  110.  
  111. extern char *ttyname() ;
  112. extern long atol() ;
  113. extern unsigned sleep() ;
  114.  
  115. #if BSD
  116. extern char *sprintf() ;
  117. #else
  118. extern int sprintf() ;
  119. extern void exit() ;
  120. extern time_t times() ;
  121. #endif
  122.  
  123.  
  124. #if BSD
  125. #    define TICS 1000        /* Fractional seconds */
  126. #    define MS(tv) (1000000L / TICS * (tv).tv_sec + (tv).tv_usec / TICS)
  127. #else
  128. #    define TICS HZ
  129. #endif
  130.  
  131.  
  132. #define NDIG    6            /* Number of digits/code */
  133. #define MAXCODE    999999        /* Largest NDIG number */
  134.  
  135. #define NHEAD    10000        /* Number of header characters */
  136. #define NTRAIL    10000        /* Number of trailer characters */
  137.  
  138. #define CPL        9            /* Number of codes per line */
  139. #define BASE    0x3f        /* Base character for checksum */
  140.  
  141. #define NCHAR(ncode) ((NDIG + 2) * (ncode) + 2 * (((ncode) + CPL - 1) / CPL))
  142. #define NCODE(nchar) (((nchar) * CPL) / (CPL * (NDIG + 2) + 2))
  143.  
  144.  
  145. char iobuf[3][BUFSIZ] ;
  146.  
  147. /*
  148.  *    Command options.
  149.  */
  150.  
  151. int verbose ;                /* Verbose error reporting */
  152. long throttle ;                /* Max chars between sleep calls */
  153. int silent ;                /* Silent option */
  154. int out ;                    /* Output test */
  155.  
  156. /*
  157.  *    Misc globals.
  158.  */
  159.  
  160. int linepos ;                /* Code position in line */
  161. char *tname ;                /* Input/output ttyname */
  162.  
  163. /*
  164.  *    Statistics gathering.
  165.  */
  166.  
  167. #define MAXSTAT    100            /* Gap max for statistics */
  168. int gap[MAXSTAT] ;            /* Statistics on code gaps */
  169.  
  170.  
  171. /*************************************************************
  172.  *     Output a numeric code sequence                        *
  173.  *************************************************************/
  174.  
  175. outcode(v)
  176. register long v ;
  177. {
  178.     register int ch ;
  179.     register char *p ;
  180.     static char digits[NDIG] ;
  181.     static long lastv ;
  182.  
  183.     if (v == lastv)
  184.     {
  185.         p = &digits[NDIG] ;
  186.     }
  187.     else if (v == lastv + 1)
  188.     {
  189.         lastv++ ;
  190.         for (p = digits ; *p == 9 ;) *p++ = 0 ;
  191.         *p += 1 ;
  192.         p = &digits[NDIG] ;
  193.     }
  194.     else
  195.     {
  196.         lastv = v ;
  197.         for (p = digits ; p != &digits[NDIG] ;)
  198.         {
  199.             *p++ = v % 10 ;
  200.             v /= 10 ;
  201.         }
  202.     }
  203.  
  204.     v = 0 ;
  205.     while (p != &digits[0])
  206.     {
  207.         ch = *--p ;
  208.         v += (v << 4) + ch ;
  209.         ch += '0' ;
  210.         (void) putc(ch, stdout) ;
  211.     }
  212.  
  213.     ch = (v & 0x3f) + BASE ;
  214.     (void) putc(ch, stdout) ;
  215.  
  216.     ch = ((v >> 6) & 0x3f) + BASE ;
  217.     (void) putc(ch, stdout) ;
  218.  
  219.     if (++linepos == CPL)
  220.     {
  221.         (void) printf("\r\n") ;
  222.         linepos = 0 ;
  223.     }
  224. }
  225.  
  226.  
  227.  
  228. /***************************************************************
  229.  *    Close out the current line                               *
  230.  ***************************************************************/
  231.  
  232. closeline()
  233. {
  234.     if (linepos)
  235.     {
  236.         (void) printf("\r\n") ;
  237.         linepos = 0 ;
  238.     }
  239. }
  240.  
  241.  
  242.  
  243. /************************************************************
  244.  *        Send text to a remote                             *
  245.  ************************************************************/
  246.  
  247. sendtext(prefix, text)
  248. int prefix ;
  249. char *text ;
  250. {
  251.     int ch ;
  252.     unsigned check ;
  253.  
  254.     closeline() ;
  255.  
  256.     for (;;)
  257.     {
  258.         check = 0 ;
  259.         (void) fputc(prefix, stdout) ;
  260.  
  261.         while ((ch = *text++) && ch != '\n')
  262.         {
  263.             (void) fputc(ch, stdout) ;
  264.             check = 17 * check + ch ;
  265.         }
  266.  
  267.         (void) fputc((int)(check & 0x3f) + BASE, stdout) ;
  268.         check >>= 6 ;
  269.         (void) fputc((int)(check & 0x3f) + BASE, stdout) ;
  270.  
  271.         (void) printf("\r\n") ;
  272.  
  273.         if (ch == 0) break ;
  274.     }
  275. }
  276.  
  277.  
  278.  
  279. /***************************************************************
  280.  *    Transmit a given number of output codes                  *
  281.  ***************************************************************/
  282.  
  283. transmit(ncode)
  284. long ncode ;
  285. {
  286.     long code ;
  287.     ulong real ;
  288.     ulong user ;
  289.     ulong sys ;
  290.     int cps ;
  291.     char buf[200] ;
  292.  
  293. #if BSD
  294.     struct timeval tv ;
  295.     struct rusage ru ;
  296. #else
  297.     struct tms tms ;
  298. #endif
  299.  
  300.     /*
  301.      *    Output header codes to fill the output queue so we
  302.      *    will get a better idea of the true transmission times.
  303.      */
  304.  
  305.     (void) printf("\r\n") ;
  306.  
  307.     linepos = 0 ;
  308.     code = NCODE(NHEAD) ;
  309.     while (--code >= 0) outcode((long) (MAXCODE - 1)) ;
  310.     closeline() ;
  311.     (void) fflush(stdout) ;
  312.  
  313.     /*
  314.      *    Get time statistics.
  315.      */
  316.  
  317. #if BSD
  318.     (void) gettimeofday(&tv, (struct timezone *)0) ;
  319.     real = MS(tv) ;
  320.  
  321.     (void) getrusage(RUSAGE_SELF, &ru) ;
  322.     user = MS(ru.ru_utime) ;
  323.     sys = MS(ru.ru_stime) ;
  324. #else
  325.     real = times(&tms) ;
  326.     user = tms.tms_utime ;
  327.     sys = tms.tms_stime ;
  328. #endif
  329.  
  330.     /*
  331.      *    Output ncode actual codes.
  332.      */
  333.  
  334.     for (code = 0 ; code < ncode ; code++) outcode(code) ;
  335.     closeline() ;
  336.     (void) fflush(stdout) ;
  337.  
  338.     /*
  339.      *    Figure the system resources used in the
  340.      *    duration of the test.
  341.      */
  342.  
  343. #if BSD
  344.     (void) gettimeofday(&tv, (struct timezone *)0) ;
  345.     real = MS(tv) - real ;
  346.  
  347.     (void) getrusage(RUSAGE_SELF, &ru) ;
  348.     user = MS(ru.ru_utime) - user ;
  349.     sys = MS(ru.ru_stime) - sys ;
  350.     cps = 1000L * NCHAR(ncode) / (real ? real : 1) ;
  351. #else
  352.     real = times(&tms) - real ;
  353.     cps = TICS * NCHAR(ncode) / (real ? real : 1) ;
  354.     user = tms.tms_utime - user ;
  355.     sys = tms.tms_stime - sys ;
  356. #endif
  357.  
  358.     if (real == 0) real = 1 ;
  359.     
  360.     /*
  361.      *    Output trailer codes to end the sequence.
  362.      */
  363.  
  364.     code = NCODE(NTRAIL) ;
  365.     while (--code >= 0) outcode((long) (MAXCODE - 1)) ;
  366.     closeline() ;
  367.  
  368.     (void) fflush(stdout) ;
  369.  
  370.     /*
  371.      *    Output results.
  372.      */
  373.  
  374.     (void) sprintf(buf,
  375.         "%s : OUT cps=%ld, real=%.2f, user=%.1f, sys=%.1f\n",
  376.         tname, cps, (double) real / TICS,
  377.         100.0 * (double) user / (double) real,
  378.         100.0 * (double) sys / (double) real) ;
  379.  
  380.     (void) write(2, buf, (unsigned) strlen(buf)) ;
  381. }
  382.  
  383.  
  384. /***********************************************************
  385.  *    Honor a text escape from the remote.                 *
  386.  ***********************************************************/
  387.  
  388. readtext(prefix)
  389. int prefix ;
  390. {
  391.     int ch ;
  392.     int n ;
  393.     int i ;
  394.     int check ;
  395.     char buf[1000] ;
  396.  
  397.     n = 0 ;
  398.  
  399.     for (;;)
  400.     {
  401.         if ((ch = fgetc(stdin)) == EOF) break ;
  402.         ch &= 0x7f ;
  403.  
  404.         if (ch == '\r' || ch == '\n') break ;
  405.  
  406.         if (n < sizeof(buf))
  407.         {
  408.             buf[n] = ch ;
  409.         }
  410.         n++ ;
  411.     }
  412.  
  413.     if (n < 2 || n > sizeof(buf))
  414.     {
  415.         (void) fprintf(stderr, "Text size error!\n", prefix) ;
  416.         return ;
  417.     }
  418.  
  419.     n -= 2 ;
  420.     check = 0 ;
  421.  
  422.     for (i = 0 ; i < n ; i++) check = 17 * check + buf[i] ;
  423.  
  424.     if    (    ((check & 0x3f) + BASE) != buf[n]
  425.         ||    (((check >>= 6) & 0x3f) + BASE) != buf[n+1]
  426.         )
  427.     {
  428.         (void) fprintf(stderr, "Text checksum error!\n", prefix) ;
  429.         return ;
  430.     }
  431.  
  432.     buf[n] = 0 ;
  433.  
  434.     if (prefix == '!') (void) system(buf) ;
  435.  
  436.     if (prefix == '#')
  437.     {
  438.         (void) fputs(buf, stderr) ;
  439.         (void) fputc('\n', stderr) ;
  440.     }
  441. }
  442.  
  443.  
  444.  
  445. /***********************************************************
  446.  *    Receive data routine.                                *
  447.  ***********************************************************/
  448.  
  449. receive()
  450. {
  451.     register int ch ;
  452.     register long v ;
  453.     int d ;
  454.     int i ;
  455.     int n ;
  456.     int lch ;
  457.     unsigned short s ;
  458.     ulong real ;
  459.     ulong user ;
  460.     ulong sys ;
  461.     long code ;
  462.     long cps ;
  463.     long error ;
  464.     long trash ;
  465.     long scode ;
  466.     long incount ;
  467.     char buf[200] ;
  468.  
  469. #if BSD
  470.     struct timeval tv ;
  471.     struct rusage ru ;
  472. #else
  473.     struct tms tms ;
  474. #endif
  475.  
  476.     real = 0 ;
  477.     user = 0 ;
  478.     sys = 0 ;
  479.     trash = 0 ;
  480.     error = 0 ;
  481.     code = MAXCODE - 1 ;
  482.     scode = 0 ;
  483.     incount = 0 ;
  484.  
  485.     ch = getc(stdin) ;
  486.  
  487.     /*
  488.      *    Loop to read codes until and exit code appears.
  489.      */
  490.  
  491.     for (;;)
  492.     {
  493.         v = 0 ;
  494.         d = 0 ;
  495.         s = 0 ;
  496.  
  497.         if (++incount >= throttle)
  498.         {
  499.             (void) sleep(1) ;
  500.             incount = 0 ;
  501.         }
  502.  
  503.         /*
  504.          *    Ignore white space, detect EOF, and scan
  505.          *    until a digit appears.
  506.          */
  507.  
  508.         lch = '\n' ;
  509.  
  510.         for (;;)
  511.         {
  512.             if (ch == EOF)
  513.             {
  514.                 if (!silent) (void) fprintf(stderr, "Exiting on EOF!\n") ;
  515.                 exit(1) ;
  516.             }
  517.  
  518.             ch &= 0x7f ;
  519.  
  520.             if (ch >= '0' && ch <= '9') break ;
  521.  
  522.             if (lch == '\n' && (ch == '!' || ch == '#'))
  523.             {
  524.                 readtext(ch) ;
  525.                 lch = '\n' ;
  526.             }
  527.             else if
  528.                 (    ch != '\r' && ch != '\n'
  529.                 &&    (ch < BASE || ch > (BASE + 0x3f))
  530.                 )
  531.                 trash++ ;
  532.  
  533.             lch = '\n' ;
  534.             ch = getc(stdin) ;
  535.         }
  536.  
  537.         /*
  538.          *    Pick up a digit string.
  539.          */
  540.  
  541.         while (isdigit(ch))
  542.         {
  543.             ch -= '0' ;
  544.             v = 10 * v + ch ;
  545.             s += (s << 4) + ch ;
  546.             d++ ;
  547.             if ((ch = getc(stdin)) == EOF) break ;
  548.             ch &= 0x7f ;
  549.         }
  550.  
  551.         /*
  552.          *    Process checksum.
  553.          */
  554.  
  555.         if (d != NDIG || (ch - BASE) != (s & 0x3f)) continue ;
  556.  
  557.         if ((ch = getc(stdin)) == EOF) continue ;
  558.         ch &= 0x7f ;
  559.  
  560.         if ((ch - BASE) != ((s >> 6) & 0x3f)) continue ;
  561.  
  562.         ch = getc(stdin) ;
  563.  
  564.         /*
  565.          *    Pick up end of sequence or quit indicator.
  566.          */
  567.  
  568.         if (v >= MAXCODE - 1)
  569.         {
  570.             if (code != MAXCODE - 1)
  571.             {
  572.                 code -= scode ;
  573.  
  574.                 /*
  575.                  *    Figure resources used, and print results.
  576.                  */
  577.  
  578. #if BSD
  579.                 (void) gettimeofday(&tv, (struct timezone *)0) ;
  580.                 real = MS(tv) - real ;
  581.  
  582.                 (void) getrusage(RUSAGE_SELF, &ru) ;
  583.                 user = MS(ru.ru_utime) - user ;
  584.                 sys = MS(ru.ru_stime) - sys ;
  585.                 cps = 1000L * NCHAR(code) / (real ? real : 1) ;
  586. #else
  587.                 real = times(&tms) - real ;
  588.                 cps = TICS * NCHAR(code) / (real ? real : 1) ;
  589.                 user = tms.tms_utime - user ;
  590.                 sys = tms.tms_stime - sys ;
  591. #endif
  592.                 if (real == 0) real = 1 ;
  593.  
  594.                 (void) sprintf(buf,
  595.                     "%s : IN  cps=%ld, real=%.2f, user=%.1f, sys=%.1f, errs=%ld, stray=%ld\n",
  596.                     tname, cps, (double)real / TICS,
  597.                     100.0 * (double) user / (double) real,
  598.                     100.0 * (double) sys / (double) real,
  599.                     error, trash) ;
  600.                 
  601.                 (void) write(2, buf, (unsigned) strlen(buf)) ;
  602.  
  603.                 /*
  604.                  *    Print verbose error statistics.
  605.                  */
  606.  
  607.                 if (verbose && error)
  608.                 {
  609.                     (void) fprintf(stderr,
  610.                         "%s : Sequence gaps were:\n\t", tname) ;
  611.  
  612.                     n = 0 ;
  613.                     for (i = 0 ; i < MAXSTAT ; i++)
  614.                     {
  615.                         if (gap[i])
  616.                         {
  617.                             if (n == 6)
  618.                             {
  619.                                 (void) fprintf(stderr, "\n\t") ;
  620.                                 n = 0 ;
  621.                             }
  622.                             (void) fprintf(stderr,
  623.                                 "%4d:%4d", NCHAR(i+1), gap[i]) ;
  624.                             n++ ;
  625.                         }
  626.                     }
  627.                     if (n) (void) fprintf(stderr, "\n") ;
  628.                 }
  629.             }
  630.  
  631.             /*
  632.              *    Quit when the maximum input code is
  633.              *    received.
  634.              */
  635.  
  636.             if (v == MAXCODE)
  637.             {
  638.                 (void) fprintf(stderr,
  639.                     "%s : Received exit code, quitting\n", tname) ;
  640.                 exit(0) ;
  641.             }
  642.             if (code != MAXCODE - 1)
  643.             {
  644.                 code = MAXCODE - 1 ;
  645.             }
  646.         }
  647.  
  648.         /*
  649.          *    Handle expected sequence number.
  650.          */
  651.  
  652.         else if (v == code) code++ ;
  653.  
  654.         /*
  655.          *    Handle sequence lower than our current sequence,
  656.          *    indicating a restart.
  657.          */
  658.  
  659.         else if (v < code)
  660.         {
  661.             if (code != MAXCODE - 1)
  662.             {
  663.                 (void) fprintf(stderr,
  664.                     "%s : Incomplete sequence aborted\n", tname) ;
  665.             }
  666.  
  667.             for (i = 0 ; i < MAXSTAT ; i++) gap[i] = 0 ;
  668.  
  669.             trash = 0 ;
  670.             error = 0 ;
  671.             code = v + 1 ;
  672.  
  673.             /*
  674.              *    Get start time statistics.
  675.              */
  676.  
  677. #if BSD
  678.             (void) gettimeofday(&tv, (struct timezone *)0) ;
  679.             real = MS(tv) ;
  680.  
  681.             (void) getrusage(RUSAGE_SELF, &ru) ;
  682.             user = MS(ru.ru_utime) ;
  683.             sys = MS(ru.ru_stime) ;
  684. #else
  685.             real = times(&tms) ;
  686.             user = tms.tms_utime ;
  687.             sys = tms.tms_stime ;
  688. #endif
  689.  
  690.             scode = v ;
  691.         }
  692.  
  693.         /*
  694.          *    Handle sequence greater than our expected
  695.          *    sequence.
  696.          */
  697.  
  698.         else
  699.         {
  700.             i = v - code ;
  701.             error += i ;
  702.             if (i > MAXSTAT) i = MAXSTAT ;
  703.             gap[i-1]++ ;
  704.             code = v + 1 ;
  705.         }
  706.     }
  707. }
  708.  
  709.  
  710.  
  711. /*************************************************************
  712.  *        Main Program                                       *
  713.  *************************************************************/
  714.  
  715. main(argc, argv)
  716. int argc ;
  717. char **argv ;
  718. {
  719.     int quit = 0 ;
  720.     int i ;
  721.     int ch ;
  722.     long nchar ;
  723.  
  724. #if SETVBUF
  725.     (void) setvbuf(stdin,  _IOFBF, iobuf[0], sizeof(iobuf[0])) ;
  726.     (void) setvbuf(stdout, _IOFBF, iobuf[1], sizeof(iobuf[0])) ;
  727.     (void) setvbuf(stderr, _IOLBF, iobuf[2], sizeof(iobuf[0])) ;
  728. #else
  729.     (void) setvbuf(stdin,  iobuf[0], _IOFBF, sizeof(iobuf[0])) ;
  730.     (void) setvbuf(stdout, iobuf[1], _IOFBF, sizeof(iobuf[0])) ;
  731.     (void) setvbuf(stderr, iobuf[2], _IOLBF, sizeof(iobuf[0])) ;
  732. #endif
  733.  
  734.     /*
  735.      *    Break out options.
  736.      */
  737.     
  738.     throttle = 9999999 ;
  739.  
  740.     while ((ch = getopt(argc, argv, "c:e:qst:vx:")) != -1)
  741.     {
  742.         switch (ch)
  743.         {
  744.         case 'c':
  745.             out++ ;
  746.             sendtext('!', optarg) ;
  747.             break ;
  748.         
  749.         case 'e':
  750.             out++ ;
  751.             sendtext('#', optarg) ;
  752.             break ;
  753.  
  754.         case 'q':
  755.             out++ ;
  756.             quit++ ;
  757.             break ;
  758.         
  759.         case 's':
  760.             silent++ ;
  761.             break ;
  762.  
  763.         case 't':
  764.             throttle = atoi(optarg) ;
  765.             throttle = NCODE(throttle) ;
  766.             break ;
  767.  
  768.         case 'v':
  769.             verbose++ ;
  770.             break ;
  771.         
  772.         case 'x':
  773.             out++ ;
  774.             nchar = atoi(optarg) ;
  775.             transmit(NCODE(nchar)) ;
  776.             break ;
  777.         
  778.         default:
  779.             (void) fprintf(stderr,
  780.                 "usage: %s [ -qv ] [ nchar ]\n", argv[0]) ;
  781.             exit(2) ;
  782.         }
  783.     }
  784.     
  785.     /*
  786.      *    Check for terminal input/output.
  787.      */
  788.  
  789.     if (optind != argc) out++ ;
  790.  
  791.     if (out)
  792.     {
  793.         tname = ttyname(1) ;
  794.         if (tname == 0)
  795.         {
  796.             if (!silent)
  797.                 (void) fprintf(stderr, "Standard output is not a terminal!\n") ;
  798.             tname = "OUTPUT" ;
  799.         }
  800.     }
  801.     else
  802.     {
  803.         tname = ttyname(0) ;
  804.         if (tname == 0)
  805.         {
  806.             if (!silent)
  807.                 (void) fprintf(stderr, "Standard input is not a terminal!\n") ;
  808.             tname = "INPUT" ;
  809.         }
  810.     }
  811.  
  812.     /*
  813.      *    Output code counts as requested.
  814.      */
  815.  
  816.     while (optind < argc)
  817.     {
  818.         nchar = atol(argv[optind]) ;
  819.         if (nchar > 0) transmit(NCODE(nchar)) ;
  820.         optind++ ;
  821.     }
  822.  
  823.     /*
  824.      *    Output quit codes.
  825.      */
  826.  
  827.     if (quit)
  828.     {
  829.         for (i = 0 ; i < NTRAIL ; i++)
  830.         {
  831.             outcode((long) MAXCODE) ;
  832.         }
  833.         closeline() ;
  834.     }
  835.  
  836.     (void) fflush(stdout) ;
  837.  
  838.     /*
  839.      *    If no code counts or the quit flag, this is
  840.      *    a receive data test.
  841.      */
  842.  
  843.     if (!out)
  844.     {
  845.         receive() ;
  846.         exit(0) ;
  847.     }
  848.  
  849.     return(0) ;
  850. }
  851.