home *** CD-ROM | disk | FTP | other *** search
/ Club Amiga de Montreal - CAM / CAM_CD_1.iso / files / 211.lha / Paste / paste.c < prev    next >
C/C++ Source or Header  |  1996-02-14  |  12KB  |  509 lines

  1. /*
  2.  * paste - a recreation of the Unix(Tm) paste(1) command.
  3.  *
  4.  * syntax:    paste file1 file2 ...
  5.  *        paste -dLIST file1 file2 ...
  6.  *        paste -s [-dLIST] file1 file2 ...
  7.  *
  8.  *    Copyright (C) 1984 by David M. Ihnat
  9.  *
  10.  * This program is a total rewrite of the Bell Laboratories Unix(Tm)
  11.  * command of the same name, as of System V.  It contains no proprietary
  12.  * code, and therefore may be used without violation of any proprietary
  13.  * agreements whatsoever.  However, you will notice that the program is
  14.  * copyrighted by me.  This is to assure the program does *not* fall
  15.  * into the public domain.  Thus, I may specify just what I am now:
  16.  * This program may be freely copied and distributed, provided this notice
  17.  * remains; it may not be sold for profit without express written consent of
  18.  * the author.
  19.  * Please note that I recreated the behavior of the Unix(Tm) 'paste' command
  20.  * as faithfully as possible, with minor exceptions (noted below); however,
  21.  * I haven't run a full set of regression * tests.  Thus, the user of
  22.  * this program accepts full responsibility for any effects or loss;
  23.  * in particular, the author is not responsible for any losses,
  24.  * explicit or incidental, that may be incurred through use of this program.
  25.  *
  26.  * The changes to the program, with one exception, are transparent to
  27.  * a user familiar with the Unix command of the same name.  These changes
  28.  * are:
  29.  *
  30.  * 1) The '-s' option had a bug in the Unix version when used with multiple
  31.  *    files.  (It would repeat each file in a list, i.e., for
  32.  *    'paste -s file1 file2 file3', it would list
  33.  *    <file1\n><file1\n><file2\n><file1\n><file2\n><file3\n>
  34.  *    I fixed this, and reported the bug to the providers of the command in
  35.  *    Unix.
  36.  *
  37.  * 2) The list of valid escape sequences has been expanded to include
  38.  *    \b,\f, and \r.  (Just because *I* can't imagine why you'd want
  39.  *    to use them doesn't mean I should keep them from you.)
  40.  *
  41.  * 3) There is no longer any restriction on line length.
  42.  *
  43.  * I ask that any bugs (and, if possible, fixes) be reported to me when
  44.  * possible.  -David Ihnat (312) 784-4544 ihuxx!ignatz
  45.  */
  46.  
  47. /* Modified to run under MINIX 1.1
  48.  * by David O. Tinker  (416) 978-3636 (utgpu!dtinker)
  49.  * Sept. 19, 1987
  50.  */
  51.  
  52. #include <stdio.h>
  53. #include <errno.h>  /* make sure errno.h is available */
  54. #include <ctype.h>  /* make sure ctype.h is available */
  55.  
  56. extern int errno;
  57.  
  58. #ifndef unix
  59. #define _cleanup()  fflush(stdout)
  60. #endif
  61.  
  62. #define MINIX   /* as opposed to C86, or AZTEC in my case */
  63.         /* just in case I want to compile under DOS sometime */
  64.  
  65. /* I'd love to use enums, but not everyone has them.  Portability, y'know. */
  66. #define NODELIM        1
  67. #define USAGE        2
  68. #define BADFILE        3
  69. #define TOOMANY        4
  70.  
  71. #define    TAB        '\t'
  72. #define    NL        '\n'
  73. #define    BS        '\b'
  74. #define    FF        '\f'
  75. #define    CR        '\r'
  76. #define DEL             '\177'
  77.  
  78. #define    _MAXSZ        512
  79. #define _MAXFILES    12
  80. #define    CLOSED        ((FILE *)-1)
  81. #define ENDLIST        ((FILE *)-2)
  82.  
  83. char *cmdnam;
  84.  
  85. #undef toupper        /* Just in case it is a macro */
  86. static char toupper();
  87.  
  88. short int dflag,sflag;
  89. char delims[] = { TAB };
  90.  
  91. main(argc,argv)
  92. int argc;
  93. char **argv;
  94. {
  95.     char *strcpy();
  96.  
  97.     dflag = sflag = 0;
  98.  
  99.     cmdnam = *argv;
  100.  
  101.   if(argc >= 2) {
  102.  
  103.     /* Skip invocation name */
  104.     argv++;
  105.     argc--;
  106.  
  107.     /* First, parse input options */
  108.  
  109.     while(argv[0][0] == '-' && argv[0][1] != '\0')
  110.     {
  111.         switch(toupper(argv[0][1]))
  112.         {
  113.             case 'D':
  114.                 /* Delimiter character(s) */
  115.                 strcpy(delims,&argv[0][2]);
  116.                 if(*delims == '\0')
  117.                     prerr(NODELIM,"");
  118.                 else
  119.                     delimbuild(delims);
  120.  
  121.                 break;
  122.  
  123.             case 'S':
  124.                 sflag++;
  125.                 break;
  126.  
  127.             default:
  128.                 prerr(USAGE,"");
  129.         }
  130.         argv++;
  131.         argc--;
  132.     }
  133.   }
  134.  
  135.   else
  136.         prerr(USAGE,"");
  137.  
  138.     /*
  139.      * If no files specified, simply exit.  Otherwise,
  140.      * if not the old '-s' option, process all files.
  141.      * If '-s', then process files one-at-a-time.
  142.      */
  143.     if(!sflag)
  144.         docol(argc,argv); /* Column paste */
  145.     else
  146.         doserial(argc,argv);    /* Serial paste */
  147.  
  148.         _cleanup();
  149.         exit(0);
  150.  
  151. docol(nfiles,fnamptr)
  152. int nfiles;
  153. char **fnamptr;
  154. {
  155.     char iobuff[_MAXSZ];    /* i/o buffer for the fgets */
  156.     short int somedone;    /* flag for blank field handling */
  157.  
  158.     /*
  159.      * There is a strange case where all files are just ready to
  160.      * be closed, or will on this round.  In that case, the string
  161.      * of delimiters must be preserved.  delbuf[1] ->delbuf[MAXFILES+1]
  162.      * provides intermediate storage for closed files, if needed;
  163.      * delbuf[0] is the current index.
  164.      */
  165.     char delbuf[_MAXFILES+2];
  166.  
  167.     FILE *fileptr[_MAXFILES+1];
  168.     FILE *fopen();
  169.  
  170.     char *fgets();
  171.  
  172.     int filecnt;            /* Set to number of files to process */
  173.     register char *delimptr;    /* Cycling delimiter pointer */
  174.     int index;            /* Working variable */
  175.     int strend;            /* End of string in buffer */
  176.  
  177.     /*
  178.      * Perform column paste.  First, attempt to open all
  179.      * files. (This could be expanded to an infinite number of files,
  180.      * but at the (considerable) expense of remembering the file and
  181.      * its current offset, then opening/reading/closing.  The
  182.      * commands' utility doesn't warrant the effort; at least, to me...)
  183.      */
  184.  
  185.     for(filecnt=0;(nfiles>0) && (filecnt<_MAXFILES);filecnt++,nfiles--,fnamptr++)
  186.     {
  187.         if(fnamptr[0][0] == '-')
  188.             fileptr[filecnt] = stdin;
  189.         else
  190.         {
  191.             fileptr[filecnt] = fopen(*fnamptr,"r");
  192.             if(fileptr[filecnt] == NULL)
  193.                 prerr(BADFILE,*fnamptr);
  194.         }
  195.     }
  196.  
  197.     fileptr[filecnt] = ENDLIST;    /* End of list. */
  198.  
  199.     if(nfiles)
  200.         prerr(TOOMANY,"");
  201.  
  202.     /*
  203.      * Have all files.  Now, read a line from each file, and output
  204.      * to stdout.  Notice that the old 511 character limitation on
  205.      * the line length no longer applies, since this program doesn't
  206.      * do the buffering.  Do this until you go through the loop and
  207.      * don't successfully read from any of the files.
  208.      */
  209.     for(;filecnt;)
  210.     {
  211.         somedone = 0;        /* Blank field handling flag */
  212.         delimptr = delims;    /* Start at beginning of delim list */
  213.         delbuf[0] = 0;        /* No squirreled delims */
  214.  
  215.         for(index = 0;(fileptr[index] != ENDLIST) && filecnt; index++)
  216.         {
  217.             /* Read a line and immediately output.
  218.              * If it's too big for the buffer, then dump what was
  219.              * read and go back for more.
  220.              *
  221.              * Otherwise, if it is from the last file, then leave
  222.              * the carriage return in place; if not, replace with
  223.              * a delimiter (if any)
  224.              */
  225.  
  226.             strend = 0;    /* Set so can easily detect EOF */
  227.  
  228.             if(fileptr[index] != CLOSED)
  229.  
  230.                 while(fgets(iobuff,(_MAXSZ-1),fileptr[index]) != (char *)NULL)
  231.                 {
  232.                     strend = strlen(iobuff);    /* Did the buffer fill? */
  233.  
  234.                     if(strend == (_MAXSZ-1))
  235.                     {
  236.                         /* Gosh, what a long line. */
  237.                         fputs(iobuff,stdout);
  238.                         strend = 0;
  239.                         continue;
  240.                     }
  241.  
  242.                     /* Ok got whole line in buffer. */
  243.                     break;    /* Out of loop for this file */
  244.                 }
  245.  
  246.             /* Ended either on an EOF (well, actually NULL return--
  247.              * it *could* be some sort of file error, but
  248.              * but if the file was opened successfully, this is
  249.              * unlikely. Besides, error checking on streams
  250.              * doesn't allow us to decide exactly what went
  251.              * wrong, so I'm going to be very Unix-like and ignore
  252.              * it!), or a closed file, or a received line.
  253.              * If an EOF, close the file and mark it in the
  254.              * list.  In any case, output the delimiter of choice.
  255.              */
  256.  
  257.             if(!strend)
  258.             {
  259.                 if(fileptr[index] != CLOSED)
  260.                 {
  261.                     fclose(fileptr[index]);
  262.                     fileptr[index] = CLOSED;
  263.                     filecnt--;
  264.                 }
  265.  
  266.                 /* Is this the end of the whole thing? */
  267.                 if((fileptr[index+1] == ENDLIST) && !somedone)
  268.                     continue; /* EXITS */
  269.  
  270.                 /* Ok, some files not closed this line. Last file? */
  271.                 if(fileptr[index+1] == ENDLIST)
  272.                 {
  273.                     if(delbuf[0])
  274.                     {
  275.                         fputs(&delbuf[1],stdout);
  276.                         delbuf[0] = 0;
  277.                     }
  278.                     putc((int)NL,stdout);
  279.                     continue;    /* Next read of files */
  280.                 }else
  281.                 {
  282.                     /* Closed file; setup delim */
  283.                     if(*delimptr != DEL)
  284.                     {
  285.                         delbuf[0]++;
  286.                         delbuf[delbuf[0]] = *delimptr++;
  287.                         delbuf[delbuf[0]+1] = '\0';
  288.                     }else
  289.                         delimptr++;
  290.                 }
  291.  
  292.                 /* Reset end of delimiter string if necessary */
  293.                 if(*delimptr == '\0')
  294.                     delimptr = delims;
  295.             }else
  296.             {
  297.                 /* Some data read. */
  298.                 somedone++;
  299.  
  300.                 /* Any saved delims? */
  301.                 if(delbuf[0])
  302.                 {
  303.                     fputs(&delbuf[1],stdout);
  304.                     delbuf[0] = 0;
  305.                 }
  306.  
  307.                 /* If last file, last char will be NL. */
  308.                 if(fileptr[index+1] != ENDLIST)
  309.                 {
  310.                     if(*delimptr == DEL)
  311.                     {
  312.                         delimptr++;
  313.                         iobuff[strend-1] = '\0'; /* No delim */
  314.                     }else
  315.                         iobuff[strend-1] = *delimptr++;
  316.                 }
  317.  
  318.                 if(*delimptr == '\0')
  319.                     delimptr = delims;
  320.  
  321.                 /* Now dump the buffer */
  322.                 fputs(iobuff,stdout);
  323.                 _cleanup();
  324.             }
  325.         }
  326.     }
  327. }
  328.  
  329. doserial(nfiles,fnamptr)
  330. int nfiles;
  331. char **fnamptr;
  332. {
  333.     /*
  334.      * Do serial paste.  Simply scarf characters, performing
  335.      * one-character buffering to facilitate delim processing.
  336.      */
  337.     
  338.     register int charnew,charold;
  339.     register char *delimptr;
  340.  
  341.     register FILE *fileptr;
  342.     FILE *fopen();
  343.  
  344.     for(;nfiles;nfiles--,fnamptr++)
  345.     {
  346.         if(fnamptr[0][0] == '-')
  347.             fileptr = stdin;
  348.         else
  349.         {
  350.             fileptr = fopen(*fnamptr,"r");
  351.  
  352.             if(fileptr == NULL)
  353.                 prerr(BADFILE,*fnamptr);
  354.         }
  355.  
  356.         /*
  357.          * The file is open; just keep taking characters,
  358.          * stashing them in charnew; output charold, converting to
  359.          * the appropriate delimiter character if needful.  After the
  360.          * EOF, simply output 'charold' if it's a newline; otherwise,
  361.          * output it and then a newline.
  362.          */
  363.  
  364.         delimptr = delims;    /* Set up for delimiter string */
  365.  
  366.         if((charold = getc(fileptr)) == EOF)
  367.         {
  368.             /* Empty file! */
  369.             putc(NL,stdout);
  370.             _cleanup();
  371.             continue;    /* Go on to the next file */
  372.         }
  373.  
  374.         /* Ok, 'charold' is set up.  Hit it! */
  375.  
  376.         while((charnew = getc(fileptr)) != EOF)
  377.         {
  378.             /* Ok, process the old character */
  379.             if(charold == NL)
  380.             {
  381.                 if(*delimptr != DEL)
  382.                     putc(*delimptr++,stdout);
  383.  
  384.                 /* Reset pointer at end of delimiter string */
  385.                 if(*delimptr == '\0')
  386.                     delimptr = delims;
  387.             } else
  388.                 putc((char)charold,stdout);
  389.             
  390.             charold = charnew;
  391.         }
  392.  
  393.         /* Ok, hit EOF.  Process that last character */
  394.  
  395.         putc((char)charold,stdout);
  396.         _cleanup();
  397.         if((char)charold != NL)
  398.             putc(NL,stdout);
  399.     }
  400. }
  401.  
  402. delimbuild(strptr)
  403. char *strptr;
  404. {
  405.     /*
  406.      * Process the delimiter string into something that can be
  407.      * used by the routines.  This involves, primarily, collapsing
  408.      * the backslash representations of special characters into
  409.      * their actual values, and terminating the string in a manner
  410.      * that the routines can recognize.  The set of possible backslash
  411.      * characters has been expanded beyond that recognized by the
  412.      * vanilla Unix(Tm) version.
  413.      */
  414.  
  415.     register char *strout;
  416.  
  417.     strout = strptr;    /* Start at the same place, anyway */
  418.  
  419.     while(*strptr)
  420.     {
  421.         if(*strptr != '\\')    /* Is it an escape character? */
  422.             *strout++ = *strptr++;    /* No, just transfer it */
  423.         else
  424.         {
  425.             strptr++;    /* Get past escape character */
  426.  
  427.             switch(toupper(*strptr))
  428.             {
  429.                 case '0':
  430.                     *strout++ = DEL;
  431.                     break;
  432.  
  433.                 case 'T':
  434.                     *strout++ = TAB;
  435.                     break;
  436.  
  437.                 case 'N':
  438.                     *strout++ = NL;
  439.                     break;
  440.  
  441.                 case 'B':
  442.                     *strout++ = BS;
  443.                     break;
  444.  
  445.                 case 'F':
  446.                     *strout++ = FF;
  447.                     break;
  448.  
  449.                 case 'R':
  450.                     *strout++ = CR;
  451.                     break;
  452.  
  453.                 default:
  454.                     *strout++ = *strptr;
  455.             }
  456.             
  457.             strptr++;
  458.         }
  459.  
  460.     }
  461.     *strout = '\0';    /* Heaven forfend that we forget this! */
  462. }
  463.  
  464. prerr(etype, estring)
  465. int etype;
  466. char *estring;
  467. {
  468.     switch(etype)
  469.     {
  470.         case USAGE:
  471.             fprintf(stderr,"%s : Usage: %s [-s] [-d<delimiter>] file1 file2 ...\n",cmdnam,cmdnam);
  472.             break;
  473.  
  474.         case NODELIM:
  475.             fprintf(stderr,"%s : no delimiters\n",cmdnam);
  476.             break;
  477.  
  478.         case BADFILE:
  479.             fprintf(stderr,"%s : %s : cannot open\n",cmdnam,estring);
  480.             break;
  481.         
  482.         case TOOMANY:
  483.             fprintf(stderr,"%s : too many files\n",cmdnam);
  484.             break;
  485.     }
  486.     _cleanup();
  487.     exit(1);
  488. }
  489.  
  490. static char toupper(c)            /* This is non-standard, but it works */
  491. char c;
  492. {
  493.    char x;
  494.  
  495.    if (isalpha(c)) {
  496.       if (c > 'Z') x = (c - ' ');
  497.       else x = c;
  498.    }
  499.    else {
  500.       if (isdigit(c))
  501.           x = c;
  502.       else x = '\0';        /* this will terminate a string
  503.                  * at any character other than a
  504.                  * letter or numeral.            */ 
  505.    }
  506.    return (x);
  507. }
  508.