home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume4 / archx / archx.c < prev    next >
Encoding:
C/C++ Source or Header  |  1986-11-30  |  7.7 KB  |  334 lines

  1. /*
  2.  *            A R C H X
  3.  *
  4.  * Archive extraction
  5.  *
  6.  */
  7.  
  8. /*
  9.  * Note: the )BUILD comment is extracted by a Decus C tool to construct
  10.  * system-dependent compiler command lines.
  11.  *
  12.  * Text inside #ifdef DOCUMENTATION is converted to runoff by a
  13.  * Decus C tool.
  14.  */
  15.  
  16. /*)BUILD    $(TKBOPTIONS) = {
  17.             TASK    = ...ARX
  18.         }
  19. */
  20.  
  21. #ifdef    DOCUMENTATION
  22.  
  23. title    archx    text file archiver extraction
  24. index        text file archiver extraction
  25.  
  26. synopsis
  27.  
  28.     archx archive_files
  29.  
  30. description
  31.  
  32.     Archx manages archives (libraries) of source files, allowing
  33.     a large number of small files to be stored without using
  34.     excessive system resources.  Archx extracts all files from
  35.     an archive.
  36.  
  37.     If no archive_name file is given, the standard input is read.
  38.     Archive header records are echoed to the standard output.
  39.  
  40. archive file format
  41.  
  42.     Archive files are standard text files.  Each archive element is
  43.     preceeded by a line of the format:
  44.     .s.nf
  45.     -h-    file.name    date    true_name
  46.     .s.f
  47.     Note that there is no line or byte count.  To prevent problems,
  48.     a '-' at the beginning of a record within a user file or embedded
  49.     archive will be "quoted" by doubling it.  The date and true filename
  50.     fields are ignored.  On some operating systems, file.name is
  51.     forced to lowercase.  The archive builder (archc) may prefix
  52.     other characters by '-'.
  53.  
  54.     If the first non-blank line of an input file does not
  55.     begin with "-h", the text will be appended to "archx.tmp"
  56.     This is needed if archives are distributed by mail
  57.     and arrive with initial routing and subject information.
  58.  
  59. diagnostics
  60.  
  61.     Diagnostic messages should be self-explanatory
  62.  
  63. author
  64.  
  65.     Martin Minow
  66.  
  67. bugs
  68.  
  69. #endif
  70.  
  71. #include    <stdio.h>
  72. #include    <ctype.h>
  73. #ifdef vms
  74. #include        <ssdef.h>
  75. #include        <stsdef.h>
  76. #define    IO_SUCCESS    (SS$_NORMAL | STS$M_INHIB_MSG)
  77. #define    IO_ERROR    SS$_ABORT
  78. #endif
  79. /*
  80.  * Note: IO_SUCCESS and IO_ERROR are defined in the Decus C stdio.h file
  81.  */
  82. #ifndef    IO_SUCCESS
  83. #define    IO_SUCCESS    0
  84. #endif
  85. #ifndef    IO_ERROR
  86. #define    IO_ERROR    1
  87. #endif
  88.  
  89. #define EOS        0
  90. #define    FALSE        0
  91. #define    TRUE        1
  92.  
  93. /*
  94.  * The following status codes are returned by gethdr()
  95.  */
  96. #define DONE    0
  97. #define    GOTCHA    1
  98. #define    NOGOOD    2
  99.  
  100. char        text[513];        /* Working text line        */
  101. char        name[81];        /* Current archive member name    */
  102. char        filename[81];        /* Working file name        */
  103. char        arfilename[81];        /* Archive file name        */
  104. char        fullname[81];        /* Output for argetname()    */
  105. int        verbose        = TRUE;    /* TRUE for verbosity        */
  106. int        first_archive;        /* For mail header skipping    */
  107.  
  108. main(argc, argv)
  109. int        argc;            /* Arg count            */
  110. char        *argv[];        /* Arg vector            */
  111. {
  112.     register int        i;    /* Random counter        */
  113.     int            status;    /* Exit status            */
  114.  
  115. #ifdef    vms
  116.     argc = getredirection(argc, argv);
  117. #endif
  118.     status = IO_SUCCESS;
  119.     if (argc == 1)
  120.         process();
  121.     else {
  122.         for (i = 1; i < argc; i++) {
  123.         if (freopen(argv[i], "r", stdin) != NULL)
  124.             process();
  125.         else {
  126.             perror(argv[i]);
  127.             status = IO_ERROR;
  128.         }
  129.         }
  130.     }
  131.     exit(status);
  132. }
  133.  
  134. process()
  135. /*
  136.  * Process archive open on stdin
  137.  */
  138. {
  139.     register char        *fn;    /* File name pointer        */
  140.     register FILE        *outfd;
  141.     register int        i;
  142.  
  143.     text[0] = EOS;
  144.     while ((i = gethdr()) != DONE) {
  145.         switch (i) {
  146.         case GOTCHA:
  147.         if ((outfd = fopen(name, "w")) == NULL) {
  148.             perror(name);
  149.             fprintf(stderr, "Can't create \"%s\"\n", name);
  150.             arskip();
  151.             continue;
  152.         }
  153.         break;
  154.  
  155.         case NOGOOD:
  156.         fprintf(stderr, "Missing -h-, writing to archx.tmp\n");
  157.         fprintf(stderr, "Current text line: %s", text);
  158.         strcpy(name, "archx.tmp");
  159.         if ((outfd = fopen(name, "a")) == NULL) {
  160.             perror(name);
  161.             fprintf(stderr, "Cannot append to %s\n", name);
  162.             arskip();
  163.             continue;
  164.         }
  165.         break;
  166.         }
  167.         arexport(outfd);
  168.         fclose(outfd);
  169.     }
  170. }
  171.  
  172. int
  173. gethdr()
  174. /*
  175.  * If text is null, read a record, returning to signal input state:
  176.  *    DONE    Eof read
  177.  *    NOGOOD    -h- wasn't first non-blank line.  Line is in text[]
  178.  *    GOTCHA    -h- found, parsed into name.
  179.  */
  180. {
  181.     register char    *tp;
  182.     register char    *np;
  183.  
  184. again:    if (text[0] == EOS
  185.      && fgets(text, sizeof text, stdin) == NULL)
  186.         return (DONE);
  187.     if (text[0] == '\n' && text[1] == EOS) {
  188.         text[0] = EOS;
  189.         goto again;
  190.     }
  191.     if (text[0] != '-'
  192.      || text[1] != 'h'
  193.      || text[2] != '-')
  194.         return (NOGOOD);
  195.     for (tp = &text[3]; isspace(*tp); tp++)
  196.         ;
  197.     for (np = name; !isspace(*tp); *np++ = *tp++)
  198.         ;
  199.     *np = EOS;
  200.     return (GOTCHA);
  201. }
  202.  
  203. arskip()
  204. /*
  205.  * Skip to next header
  206.  */
  207. {
  208.     while (fgets(text, sizeof text, stdin) != NULL) {
  209.         if (text[0] == '-' && text[1] == 'h' && text[2] == '-')
  210.         return;
  211.     }
  212.     text[0] = EOS;                /* EOF signal        */
  213. }
  214.  
  215. arexport(outfd)
  216. register FILE    *outfd;
  217. /*
  218.  * Read secret archive format, writing archived data to outfd.
  219.  * Clean out extraneous <cr>,<lf>'s
  220.  */
  221. {
  222.     register char    *tp;
  223.     unsigned int    nrecords;
  224.  
  225.     printf("Creating \"%s\", ", name);
  226.     nrecords = 0;
  227.     while (fgets(text, sizeof text, stdin) != NULL) {
  228.         tp = &text[strlen(text)];
  229.         if (tp > &text[1] && *--tp == '\n' && *--tp == '\r') {
  230.         *tp++ = '\n';
  231.         *tp = EOS;
  232.         }
  233.         if (text[0] == '-') {
  234.         if (text[1] == 'h')
  235.             goto gotcha;
  236.         fputs(text+1, outfd);
  237.         }
  238.         else {
  239.         fputs(text, outfd);
  240.         }
  241.         nrecords++;
  242.     }
  243.     text[0] = EOS;
  244. gotcha:    printf("%u records\n", nrecords);
  245.     if (ferror(stdin) || ferror(outfd))
  246.         printf("Creation of \"%s\" completed with error\n", name);
  247. }
  248.  
  249. /*
  250.  * getredirection() is intended to aid in porting C programs
  251.  * to VMS (Vax-11 C) which does not support '>' and '<'
  252.  * I/O redirection.  With suitable modification, it may
  253.  * useful for other portability problems as well.
  254.  */
  255.  
  256. #ifdef    vms
  257. static int
  258. getredirection(argc, argv)
  259. int        argc;
  260. char        **argv;
  261. /*
  262.  * Process vms redirection arg's.  Exit if any error is seen.
  263.  * If getredirection() processes an argument, it is erased
  264.  * from the vector.  getredirection() returns a new argc value.
  265.  *
  266.  * Warning: do not try to simplify the code for vms.  The code
  267.  * presupposes that getredirection() is called before any data is
  268.  * read from stdin or written to stdout.
  269.  *
  270.  * Normal usage is as follows:
  271.  *
  272.  *    main(argc, argv)
  273.  *    int        argc;
  274.  *    char        *argv[];
  275.  *    {
  276.  *        argc = getredirection(argc, argv);
  277.  *    }
  278.  */
  279. {
  280.     register char        *ap;    /* Argument pointer    */
  281.     int            i;    /* argv[] index        */
  282.     int            j;    /* Output index        */
  283.     int            file;    /* File_descriptor     */
  284.  
  285.     for (j = i = 1; i < argc; i++) {   /* Do all arguments    */
  286.         switch (*(ap = argv[i])) {
  287.         case '<':            /* <file        */
  288.         if (freopen(++ap, "r", stdin) == NULL) {
  289.             perror(ap);        /* Can't find file    */
  290.             exit(IO_ERROR);    /* Is a fatal error    */
  291.         }
  292.  
  293.         case '>':            /* >file or >>file    */
  294.         if (*++ap == '>') {    /* >>file        */
  295.             /*
  296.              * If the file exists, and is writable by us,
  297.              * call freopen to append to the file (using the
  298.              * file's current attributes).  Otherwise, create
  299.              * a new file with "vanilla" attributes as if
  300.              * the argument was given as ">filename".
  301.              * access(name, 2) is TRUE if we can write on
  302.              * the specified file.
  303.              */
  304.             if (access(++ap, 2) == 0) {
  305.             if (freopen(ap, "a", stdout) != NULL)
  306.                 break;    /* Exit case statement    */
  307.             perror(ap);    /* Error, can't append    */
  308.             exit(IO_ERROR);    /* After access test    */
  309.             }            /* If file accessable    */
  310.         }
  311.         /*
  312.          * On vms, we want to create the file using "standard"
  313.          * record attributes.  create(...) creates the file
  314.          * using the caller's default protection mask and
  315.          * "variable length, implied carriage return"
  316.          * attributes. dup2() associates the file with stdout.
  317.          */
  318.         if ((file = creat(ap, 0, "rat=cr", "rfm=var")) == -1
  319.          || dup2(file, fileno(stdout)) == -1) {
  320.             perror(ap);        /* Can't create file    */
  321.             exit(IO_ERROR);    /* is a fatal error    */
  322.         }            /* If '>' creation    */
  323.         break;            /* Exit case test    */
  324.  
  325.         default:
  326.         argv[j++] = ap;        /* Not a redirector    */
  327.         break;            /* Exit case test    */
  328.         }
  329.     }                /* For all arguments    */
  330.     return (j);
  331. }
  332. #endif
  333.  
  334.