home *** CD-ROM | disk | FTP | other *** search
/ Super Net 1 / SUPERNET_1.iso / PC / OTROS / MSDOS / WATTCP / PISA_TAR.TAR / tar / buffer.c next >
Encoding:
C/C++ Source or Header  |  1992-06-30  |  17.3 KB  |  772 lines

  1. /******************************************************************************
  2.     RTAR - remotely execute a TAR command
  3.  
  4.     This program is free software; you can redistribute it and/or modify
  5.     it, but you may not sell it.
  6.  
  7.     This program is distributed in the hope that it will be useful,
  8.     but without any warranty; without even the implied warranty of
  9.     merchantability or fitness for a particular purpose.
  10.  
  11.         Giovanni Mercaldo 
  12.         Faculty of Engineering
  13.         University of Pisa 
  14.         
  15. ******************************************************************************/
  16. #include <stdio.h>
  17. #include <string.h>
  18. #include <time.h>        /* for randomize */
  19. #include <stdlib.h>
  20. #include <conio.h>        /* for getpass */
  21. #include <tcp.h>
  22. #include <sys/types.h>        /* Needed for typedefs in tar.h */
  23. #include <fcntl.h>
  24. #include <signal.h>
  25.  
  26. #include <tar.h>
  27. #include <port.h>
  28.  
  29. #define    STDIN    0        /* Standard input  file descriptor */
  30. #define    STDOUT    1        /* Standard output file descriptor */
  31.  
  32. #define    PREAD    0        /* Read  file descriptor from pipe() */
  33. #define    PWRITE    1        /* Write file descriptor from pipe() */
  34.  
  35. extern char    *valloc();
  36.  
  37. /*
  38.  * V7 doesn't have a #define for this.
  39.  */
  40. #ifndef O_RDONLY
  41. #define    O_RDONLY    0
  42. #endif
  43.  
  44. #define    MAGIC_STAT    105    /* Magic status returned by child, if
  45.                    it can't exec compress.  We hope compress
  46.                    never returns this status! */
  47. /*
  48.  * The record pointed to by save_rec should not be overlaid
  49.  * when reading in a new tape block.  Copy it to record_save_area first, and
  50.  * change the pointer in *save_rec to point to record_save_area.
  51.  * Saved_recno records the record number at the time of the save.
  52.  * This is used by annofile() to print the record number of a file's
  53.  * header record.
  54.  */
  55. static union record **save_rec;
  56. static union record record_save_area;
  57. static int        saved_recno;
  58.  
  59. /*
  60.  * PID of child compress program, if f_compress.
  61.  */
  62. static int    compress_pid;
  63.  
  64. /*
  65.  * Record number of the start of this block of records
  66.  */
  67. static int    baserec;
  68.  
  69. /*
  70.  * Error recovery stuff
  71.  */
  72. static int    r_error_count;
  73.  
  74. char cmdbuf[ 1024 ];
  75. word cmdbuflen;
  76.  
  77. char lname[ 64 ];        /* local copies if none supplied */
  78. char lpass[ 64 ];
  79. char lcmd[ 128 ];
  80. /*
  81.  * makecmdbuf builds a command string for rsh
  82.  */
  83. makecmdbuf( void *err, char *name, char *pass, char *cmd )
  84. {
  85.     char *p;
  86.  
  87.     p = cmdbuf;
  88.     *p++ = 0;
  89.     strcpy( p, name );
  90.     p = strchr( p, 0 );
  91.     strcpy( ++p, pass );
  92.     p = strchr( p, 0 );
  93.     strcpy( ++p, cmd );
  94.     p = strchr( p, 0 );
  95.  
  96.     cmdbuflen = (word)(p - cmdbuf) + 1;
  97. }
  98.  
  99. char *
  100. getpass(char *s)
  101. {
  102.     static char thepass[128];
  103.     char c, *p=thepass;
  104.     fprintf(stderr,s);
  105.     while (c=getch()) {
  106.     switch (c) {
  107.     case 8 : /* backspace */
  108.     case 127: /* DEL */
  109.         if (p>thepass) p--;
  110.         break;
  111.     case 13:
  112.     case 10:
  113.         *p=0;
  114.         fprintf(stderr,"\n");
  115.         return(thepass);
  116.     default: 
  117.         *p++=c;
  118.         if (p-thepass >=sizeof(thepass)-1) p--;
  119.         break;
  120.     }
  121.     }
  122.     return(NULL);
  123. }
  124.  
  125. tcp_Socket rsh_sock;
  126. char ch,buffer[1024];
  127.  
  128. rsh(word port)
  129. {
  130.     word lport, jqpublic, count;
  131.     int status;
  132.     longword host;
  133.  
  134.     lport = (word)(getrandom(0,512) + 512);  /* return 511 < port < 1024 */
  135.     if (!(host = resolve( hostname ))) {
  136.     printf("Unable to resolve '%s'\naborting\n", hostname );
  137.     return( 2 );
  138.     }
  139.  
  140.     jqpublic = 0;
  141.     if ( !name ) {
  142.         printf(" Userid   : ");
  143.     gets( name = lname );
  144.     if ( !*name ) {
  145.         printf( name = "anonymous");
  146.         jqpublic = 1;
  147.     }
  148.     }
  149.     if (!strcmp(pass,"-")) pass = "";
  150.     else if ( !pass ) {
  151.     if (jqpublic) pass="guest";
  152.     else
  153.         strcpy( pass = lpass, getpass(" Password : "));
  154.          /* copy for neatness since getpass overwrites */
  155.     }
  156.     if (!(strcmp(cmd,""))) {
  157.         printf(" Command  : ");
  158.         gets( lcmd );
  159.     strcpy(cmd,lcmd);
  160.         if ( !*cmd ) {
  161.             puts("No command given\n");
  162.             exit( 2 );
  163.         }
  164.     }
  165.  
  166.     makecmdbuf( NULL, name, pass, cmd);
  167.  
  168.     if (f_create) set_tcp_txbufsize(8192);
  169.     else set_tcp_rxbufsize(8192);
  170.     if (! tcp_open( &rsh_sock, lport, host, port, NULL )) {
  171.     printf("Remote host unaccessible");
  172.     return( 1 );
  173.     }
  174.     fprintf(stderr, "[waiting for remote host to connect...]\n");
  175.     sock_wait_established( &rsh_sock, sock_delay, NULL, &status );
  176.  
  177.     fprintf(stderr, "[remote host connected, waiting verification...]\n");
  178.  
  179.     sock_write( &rsh_sock, cmdbuf, cmdbuflen );
  180.  
  181.     while (1) {
  182.     sock_tick( &rsh_sock, &status );
  183.     if (!sock_dataready(&rsh_sock))
  184.         continue;
  185.     sock_read( &rsh_sock, buffer, 1 );
  186.     fprintf(stderr, "                                              \r");
  187.     if ( *buffer == 1 )
  188.         fprintf(stdout, "RSH failed...\n\r");
  189.     break;
  190.     }
  191.  
  192. sock_err:
  193.     switch (status) {
  194.     case 1 : puts("\nConnection closed");
  195.          sock_close(&rsh_sock);
  196.          exit(EX_BADARCH);
  197.          break;
  198.         case-1 : printf("ERROR: %s\n", sockerr( &rsh_sock ));
  199.          sock_close(&rsh_sock);
  200.          exit(EX_BADARCH);
  201.          break;
  202.     }
  203.     return( (status == 1) ? 0 : 1 );
  204. }
  205.  
  206. int
  207. sread(void *rbuffer)
  208. {
  209.     int status;
  210.     word count;
  211.  
  212. /*** gm This is the read routine case of connection ***/
  213.  
  214.     while (1) {
  215.     if (kbhit()) { /* so what ??? */
  216.         sock_putc( &rsh_sock, getch());
  217.     }
  218.     sock_tick( &rsh_sock, &status );
  219.     if (sock_dataready( &rsh_sock )) {
  220.         count = sock_read( &rsh_sock, rbuffer, 1024);
  221.         return ((int)count);
  222.     }
  223.     }
  224.  
  225. sock_err:
  226.     switch (status) {
  227.     case 1 : puts("\nConnection closed");
  228.          sock_close(&rsh_sock);
  229.          exit(EX_BADARCH);
  230.          break;
  231.         case-1 : printf("ERROR: %s\n", sockerr( &rsh_sock ));
  232.          sock_close(&rsh_sock);
  233.          exit(EX_BADARCH);
  234.          break;
  235.     }
  236. }
  237.  
  238. int
  239. swrite(byte *rbuffer,int blockdim)
  240. {
  241.     int status;
  242.     word count,tot=0;
  243.  
  244. /*** gm This is the write routine case of connection ***/
  245.  
  246.     while ( tot < (word) blockdim) {
  247.     if (kbhit()) {
  248.         sock_putc( &rsh_sock, getch());
  249.     }
  250.     count = sock_write( &rsh_sock, &rbuffer[(int)tot], 512 );
  251.     tot=tot+count;
  252.     }
  253.     return ((int)tot);
  254.  
  255. sock_err:
  256.     switch (status) {
  257.     case 1 : puts("\nConnection closed");
  258.          sock_close(&rsh_sock);
  259.          exit(EX_BADARCH);
  260.          break;
  261.         case-1 : printf("ERROR: %s\n", sockerr( &rsh_sock ));
  262.          sock_close(&rsh_sock);
  263.          exit(EX_BADARCH);
  264.          break;
  265.     }
  266. }
  267.  
  268. /*
  269.  * Buffer management for public domain tar.
  270.  *
  271.  * Written by John Gilmore, ihnp4!hoptoad!gnu, on 25 August 1985.
  272.  *
  273.  * @(#) buffer.c 1.14 10/28/86 Public Domain - gnu
  274.  */
  275.  
  276. /*
  277.  * Return the location of the next available input or output record.
  278.  */
  279. union record *
  280. findrec()
  281. {
  282.     if (ar_record == ar_last) {
  283.         flush_archive();
  284.         if (ar_record == ar_last)
  285.             return (union record *)NULL;    /* EOF */
  286.     }
  287.     return ar_record;
  288. }
  289.  
  290.  
  291. /*
  292.  * Indicate that we have used all records up thru the argument.
  293.  * (should the arg have an off-by-1? XXX FIXME)
  294.  */
  295. void
  296. userec(rec)
  297.     union record *rec;
  298. {
  299.     while(rec >= ar_record)
  300.         ar_record++;
  301.     /*
  302.      * Do NOT flush the archive here.  If we do, the same
  303.      * argument to userec() could mean the next record (if the
  304.      * input block is exactly one record long), which is not what
  305.      * is intended.
  306.      */
  307.     if (ar_record > ar_last)
  308.         abort();
  309. }
  310.  
  311.  
  312. /*
  313.  * Return a pointer to the end of the current records buffer.
  314.  * All the space between findrec() and endofrecs() is available
  315.  * for filling with data, or taking data from.
  316.  */
  317. union record *
  318. endofrecs()
  319. {
  320.     return ar_last;
  321. }
  322.  
  323.  
  324. /*
  325.  * Open an archive file.  The argument specifies whether we are
  326.  * reading or writing.
  327.  */
  328. open_archive(read)
  329.     int read;
  330. {
  331.  
  332.     if(!(f_remote)) {
  333.     if (ar_file[0] == '-' && ar_file[1] == '\0') {
  334.         if (read)    archive = STDIN;
  335.         else        archive = STDOUT;
  336.     } else if (read) {
  337.         archive = open(ar_file, O_RDONLY);
  338.     } else {
  339.         archive = creat(ar_file, 0666);
  340.     }
  341.  
  342.     if (archive < 0) {
  343.         perror(ar_file);
  344.         exit(EX_BADARCH);
  345.     }
  346. #ifdef    MSDOS
  347.     setmode(archive, O_BINARY);
  348. #endif
  349.     } else {
  350.         printf("cmd = %s \n",cmd);
  351.         rsh(RSH_PORT);
  352.        }
  353.  
  354.     /*NOSTRICT*/
  355.     ar_block = (union record *) valloc((unsigned)blocksize);
  356.     if (!ar_block) {
  357.         fprintf(stderr,
  358.         "tar: could not allocate memory for blocking factor %d\n",
  359.             blocking);
  360.         exit(EX_ARGSBAD);
  361.     }
  362.  
  363.     ar_record = ar_block;
  364.     ar_last   = ar_block + blocking;
  365.  
  366. #ifndef    MSDOS
  367.     /*
  368.      * Handle compressed archives.
  369.      *
  370.      * FIXME, currently supported for reading only.
  371.      * FIXME, writing involves forking again for a small process
  372.      * that will reblock the output of compress to the user's specs.
  373.      */
  374.     if (f_compress) {
  375.         int pipes[2];
  376.         int err;
  377.  
  378.         if (!read) {
  379.             fprintf(stderr,
  380.                 "tar: cannot write compressed archives yet.\n");
  381.             exit(EX_ARGSBAD);
  382.         }
  383.  
  384.         /* Create a pipe to get compress's output to us */
  385.         err = pipe(pipes);
  386.         if (err < 0) {
  387.             perror ("tar: cannot create pipe to compress");
  388.             exit(EX_SYSTEM);
  389.         }
  390.         
  391.         /* Fork compress process */
  392.         compress_pid = fork();
  393.         if (compress_pid < 0) {
  394.             perror("tar: cannot fork compress");
  395.             exit(EX_SYSTEM);
  396.         }
  397.  
  398.         /*
  399.          * Child process.
  400.          * 
  401.           * Move input to stdin, write side of pipe to stdout,
  402.          * then exec compress.
  403.          */
  404.         if (compress_pid == 0) {
  405.             (void) close (pipes[PREAD]);    /* We won't use it */
  406.             if (archive != STDIN) {
  407.                 (void) close(STDIN);
  408.                 err = dup(archive);
  409.                 if (err != 0) {
  410.                     perror(
  411.                      "tar: cannot dup input to stdin");
  412.                     exit(EX_SYSTEM);
  413.                 }
  414.                 (void) close(archive);
  415.             }
  416.             if (pipes[PWRITE] != STDOUT) {
  417.                 (void) close (STDOUT);
  418.                 err = dup (pipes[PWRITE]);
  419.                 if (err != STDOUT) {
  420.                     perror(
  421.                       "tar: cannot dup pipe output");
  422.                     exit(MAGIC_STAT);
  423.                 }
  424.                 (void) close (pipes[PWRITE]);
  425.             }
  426.             execlp("compress", "compress", "-d", (char *)0);
  427.             perror("tar: cannot exec compress");
  428.             exit(MAGIC_STAT);
  429.         }
  430.  
  431.         /*
  432.          * Parent process.  Clean up.
  433.          * FIXME, note that this may leave standard input closed,
  434.          * if the compressed archive was on standard input.
  435.          */
  436.         (void) close (archive);        /* Close compressed archive */
  437.         (void) close (pipes[PWRITE]);    /* Close write side of pipe */
  438.         archive = pipes[PREAD];        /* Read side is our archive */
  439.  
  440. #ifdef BSD42
  441.         f_reblock++;        /* Pipe will give random # of bytes */
  442. #endif
  443.     }
  444. #endif
  445.  
  446.     ar_reading = read;
  447.     if (read) {
  448.         ar_last = ar_block;        /* Set up for 1st block = # 0 */
  449.         flush_archive();
  450.     }
  451. }
  452.  
  453.  
  454. /*
  455.  * Remember a union record * as pointing to something that we
  456.  * need to keep when reading onward in the file.  Only one such
  457.  * thing can be remembered at once, and it only works when reading
  458.  * an archive.
  459.  */
  460. saverec(pointer)
  461.     union record **pointer;
  462. {
  463.  
  464.     save_rec = pointer;
  465.     saved_recno = baserec + ar_record - ar_block;
  466. }
  467.  
  468. /*
  469.  * Perform a write to flush the buffer.
  470.  */
  471. #ifdef    XBUF
  472. fl_write(l)
  473. #else
  474. fl_write()
  475. #endif
  476. {
  477.     int err;
  478.  
  479. #ifdef    XBUF
  480.     static long seq = 0;
  481.     static int cnt = 0;
  482.     char nbuf[20];
  483.  
  484.     if (f_remote) { 
  485.         err=sock_write( &rsh_sock , &ar_block->charptr, blocksize);
  486.     }
  487.     else err = write(archive, ar_block->charptr, blocksize);
  488.     if (err == blocksize) {
  489.         if(f_xbuf && (l || cnt++ > 20)) {
  490.             cnt = 0;
  491.             close(archive);
  492.             free(ar_block);
  493.             sprintf(nbuf, "N%07ld", seq++);
  494.             if(spawnlp(0, "tarsend", "tarsend", ar_file, nbuf, 0) &&
  495.             spawnlp(0, "tarsend", "tarsend", ar_file, nbuf, 0) &&
  496.             spawnlp(0, "tarsend", "tarsend", ar_file, nbuf, 0) &&
  497.             spawnlp(0, "tarsend", "tarsend", ar_file, nbuf, 0)) {
  498.                 fprintf(stderr, "File transfer failed.\n");
  499.                 exit(EX_BADARCH);
  500.             }
  501.             open_archive(0);
  502.         }
  503.         return;
  504.     }
  505. #else
  506.     if (f_remote) { 
  507.         err=sock_write( &rsh_sock , &ar_block->charptr, blocksize);
  508.     }
  509.         else err = write(archive, ar_block->charptr, blocksize);
  510.     if (err == blocksize) return;
  511. #endif
  512.     /* FIXME, multi volume support on write goes here */
  513.     if (err < 0)
  514.         perror(ar_file);
  515.     else
  516.         fprintf(stderr, "tar: %s: write failed, short %d bytes\n",
  517.             ar_file, blocksize - err);
  518.     exit(EX_BADARCH);
  519. }
  520.  
  521.  
  522. /*
  523.  * Handle read errors on the archive.
  524.  *
  525.  * If the read should be retried, readerror() returns to the caller.
  526.  */
  527. void
  528. readerror()
  529. {
  530. #    define    READ_ERROR_MAX    10
  531.  
  532.     read_error_flag++;        /* Tell callers */
  533.  
  534.     annorec(stderr, tar);
  535.     fprintf(stderr, "Read error on ");
  536.     perror(ar_file);
  537.  
  538.     if (baserec == 0) {
  539.         /* First block of tape.  Probably stupidity error */
  540.         exit(EX_BADARCH);
  541.     }    
  542.  
  543.     /*
  544.      * Read error in mid archive.  We retry up to READ_ERROR_MAX times
  545.      * and then give up on reading the archive.  We set read_error_flag
  546.      * for our callers, so they can cope if they want.
  547.      */
  548.     if (r_error_count++ > READ_ERROR_MAX) {
  549.         annorec(stderr, tar);
  550.         fprintf(stderr, "Too many errors, quitting.\n");
  551.         exit(EX_BADARCH);
  552.     }
  553.     return;
  554. }
  555.  
  556.  
  557. /*
  558.  * Perform a read to flush the buffer.
  559.  */
  560. fl_read()
  561. {
  562.     int err;        /* Result from system call */
  563.     int left;        /* Bytes left */
  564.     char *more;        /* Pointer to next byte to read */
  565.  
  566.     /*
  567.      * Clear the count of errors.  This only applies to a single
  568.      * call to fl_read.  We leave read_error_flag alone; it is
  569.      * only turned off by higher level software.
  570.      */
  571.     r_error_count = 0;    /* Clear error count */
  572.  
  573.     /*
  574.      * If we are about to wipe out a record that
  575.      * somebody needs to keep, copy it out to a holding
  576.      * area and adjust somebody's pointer to it.
  577.      */
  578.     if (save_rec &&
  579.         *save_rec >= ar_record &&
  580.         *save_rec < ar_last) {
  581.         record_save_area = **save_rec;
  582.         *save_rec = &record_save_area;
  583.     }
  584. error_loop:
  585.     if (f_remote) { 
  586.         err=sread(ar_block->charptr);
  587.     }
  588.     else err = read(archive, ar_block->charptr, blocksize);
  589.     if (err == blocksize) return;
  590.     if (err < 0) {
  591.         readerror();
  592.         goto error_loop;    /* Try again */
  593.     }
  594.  
  595.     more = ar_block->charptr + err;
  596.     left = blocksize - err;
  597.  
  598. again:
  599.     if (0 == (((unsigned)left) % RECORDSIZE)) {
  600.         /* FIXME, for size=0, multi vol support */
  601.         /* On the first block, warn about the problem */
  602.         if (!f_reblock && baserec == 0 && f_verbose) {
  603.             annorec(stderr, tar);
  604.             fprintf(stderr, "Blocksize = %d records\n",
  605.                 err / RECORDSIZE);
  606.         }
  607.         ar_last = ar_block + ((unsigned)(blocksize - left))/RECORDSIZE;
  608.         return;
  609.     }
  610.     if (f_reblock) {
  611.         /*
  612.          * User warned us about this.  Fix up.
  613.          */
  614.         if (left > 0) {
  615. error_loop_2:
  616.             if (f_remote) {
  617.                 printf("reading more\n");
  618.                 err=sread(more);
  619.             }
  620.             else err = read(archive, more, left);
  621.             if (err < 0) {
  622.                 readerror();
  623.                 goto error_loop_2;    /* Try again */
  624.             }
  625.             if (err == 0) {
  626.                 annorec(stderr, tar);
  627.                 fprintf(stderr,
  628.         "%s: eof not on block boundary, strange...\n",
  629.                     ar_file);
  630.                 exit(EX_BADARCH);
  631.             }
  632.             left -= err;
  633.             more += err;
  634.             goto again;
  635.         }
  636.     } else {
  637.         annorec(stderr, tar);
  638.         fprintf(stderr, "%s: read %d bytes, strange...\n",
  639.             ar_file, err);
  640.         exit(EX_BADARCH);
  641.     }
  642. }
  643.  
  644.  
  645. /*
  646.  * Flush the current buffer to/from the archive.
  647.  */
  648. flush_archive()
  649. {
  650.     baserec += ar_last - ar_block;/* Keep track of block #s */
  651.     ar_record = ar_block;        /* Restore pointer to start */
  652.     ar_last = ar_block + blocking;    /* Restore pointer to end */
  653.  
  654.     if (!ar_reading) 
  655. #ifdef    XBUF
  656.         fl_write(0);
  657. #else
  658.         fl_write();
  659. #endif
  660.     else
  661.         fl_read();
  662. }
  663.  
  664. /*
  665.  * Close the archive file.
  666.  */
  667. close_archive()
  668. {
  669.     int child;
  670.     int status;
  671.  
  672. #ifdef    XBUF
  673.     if (!ar_reading) {flush_archive(); fl_write(1);}
  674. #else
  675.     if (!ar_reading) flush_archive();
  676. #endif
  677.     if (f_remote) { 
  678.         sock_close(&rsh_sock);
  679.     }
  680.     else (void) close(archive);
  681.  
  682. #ifndef    MSDOS
  683.     if (f_compress) {
  684.         /*
  685.          * Loop waiting for the right child to die, or for
  686.          * no more kids.
  687.          */
  688.         while (((child = wait(&status)) != compress_pid) && child != -1)
  689.             ;
  690.  
  691.         if (child != -1) {
  692.             switch (TERM_SIGNAL(status)) {
  693.             case 0:        /* Terminated by itself */
  694.                 if (TERM_VALUE(status) == MAGIC_STAT) {
  695.                     exit(EX_SYSTEM);/* Child had trouble */
  696.                 }
  697.                 if (TERM_VALUE(status))
  698.                     fprintf(stderr,
  699.                   "tar: compress child returned status %d\n",
  700.                         TERM_VALUE(status));
  701.             case SIGPIPE:
  702.                 break;        /* This is OK. */
  703.  
  704.             default:
  705.                 fprintf(stderr,
  706.                  "tar: compress child died with signal %d%s\n",
  707.                  TERM_SIGNAL(status),
  708.                  TERM_COREDUMP(status)? " (core dumped)": "");
  709.             }
  710.         }
  711.     }
  712. #endif
  713.  
  714. sock_err:
  715.     switch (status) {
  716.     case 1 : puts("\nConnection closed");
  717.          sock_close(&rsh_sock);
  718.          exit(EX_BADARCH);
  719.          break;
  720.         case-1 : printf("ERROR: %s\n", sockerr( &rsh_sock ));
  721.          sock_close(&rsh_sock);
  722.          exit(EX_BADARCH);
  723.          break;
  724.     }
  725. }
  726.  
  727.  
  728. /*
  729.  * Message management.
  730.  *
  731.  * anno writes a message prefix on stream (eg stdout, stderr).
  732.  *
  733.  * The specified prefix is normally output followed by a colon and a space.
  734.  * However, if other command line options are set, more output can come
  735.  * out, such as the record # within the archive.
  736.  *
  737.  * If the specified prefix is NULL, no output is produced unless the
  738.  * command line option(s) are set.
  739.  *
  740.  * If the third argument is 1, the "saved" record # is used; if 0, the
  741.  * "current" record # is used.
  742.  */
  743. void
  744. anno(stream, prefix, savedp)
  745.     FILE    *stream;
  746.     char    *prefix;
  747.     int    savedp;
  748. {
  749. #    define    MAXANNO    50
  750.     char    buffer[MAXANNO];    /* Holds annorecment */
  751. #    define    ANNOWIDTH 13
  752.     int    space;
  753.  
  754.     if (f_sayblock) {
  755.         if (prefix) {
  756.             fputs(prefix, stream);
  757.             putc(' ', stream);
  758.         }
  759.         sprintf(buffer, "rec %d: ",
  760.             savedp?    saved_recno:
  761.                 baserec + ar_record - ar_block);
  762.         fputs(buffer, stream);
  763.         space = ANNOWIDTH - strlen(buffer);
  764.         if (space > 0) {
  765.             fprintf(stream, "%*s", space, "");
  766.         }
  767.     } else if (prefix) {
  768.         fputs(prefix, stream);
  769.         fputs(": ", stream);
  770.     }
  771. }
  772.