Universität Ulm, SAI, Übungen zu Systemnahe Software I

Lösungsbeispiel zu Blatt 9 (Aufgabe 13): parser

Parser für SAM-Archive.

parser.h

/*
 *      parser.h        - Headerfile for SAM archive parser.
 *
 *      Martin Hasch, University of Ulm, January 1997.
 */

#include        <stdio.h>
#include        "sam.h"

typedef struct parser_loc {
        FILE *ploc_file;
        char *ploc_name;
} *parser_Locator;

/*
 *      All functions abort program execution in case of serious errors.
 *      An archive name of "-" stands for stdin/stdout.
 */

/*
 *      Open archive for scanning.  Return locator in *arch.
 */
void parser_openread(char *archivename, parser_Locator *arch);

/*
 *      Open archive for scanning and copying.  Return input locator in *arch
 *      and output locator associated with a temporary file in *tmparch.
 *      If create is nonzero, archive does not need to exist already.
 */
void parser_openreadwrite(char *archivename, int create,
                parser_Locator *arch, parser_Locator *tmparch);

/*
 *      Try to read another header from an archive.
 *      On success return 1, otherwise (on end of file) return 0.
 */
int parser_getheader(parser_Locator arch, sam_Header *header);

/*
 *      Skip input locator past body of an archive member whose header
 *      has just been read into header.
 */
void parser_skipbody(parser_Locator arch, sam_Header *header);

/*
 *      Create in tmparch a new archive member named name from given file.
 *      If file can be accessed properly return 0, otherwise 1.
 */
int parser_copyin(parser_Locator tmparch, char *filename, sam_Name name);

/*
 *      Restore a file from an archive member whose header has just been
 *      read into header.  If file can be written return 0, otherwise 1.
 *      In any case move locator past body of current archive member.
 */
int parser_copyout(parser_Locator arch, sam_Header *header);

/*
 *      Copy to tmparch the archive member whose header has just been read
 *      into header.
 */
void parser_preserve(parser_Locator arch, parser_Locator tmparch,
                sam_Header *header);

/*
 *      Close an archive opened with parser_openread().
 */
void parser_close(parser_Locator arch);

/*
 *      Close an archive opened with parser_openreadwrite() and replace its
 *      contents by those written into tmparch, then discard tmparch.
 */
void parser_closeandcopy(parser_Locator arch, parser_Locator tmparch);

parser.c

/*
 *      parser.c        - SAM archive parser.
 *
 *      Martin Hasch, University of Ulm, January 1997.
 */

#include        <stdio.h>
#include        <stdlib.h>
#include        <string.h>
#include        <errno.h>
#include        "sam.h"
#include        "files.h"
#include        "parser.h"

/*
 *      typedef struct parser_loc {
 *              FILE *ploc_file;
 *              char *ploc_name;
 *      } *parser_Locator;
 */

/*
 *      All global functions abort program execution in case of serious errors.
 *      An archive name of "-" stands for stdin/stdout.
 */

/*
 *      Complain about unreadable or inconsistent archive.
 */
static void my_badarchive(parser_Locator arch)
{
        if ( ferror(arch->ploc_file) )
                perror(arch->ploc_name);
        else
                (void) fprintf(stderr, "%s: archive garbled\n",
                        arch->ploc_name);
}

/*
 *      Read magic number.  If it is there and matches SAM_MAGIC
 *      return 1, otherwise 0.
 */
static int my_readmagic(FILE *archive)
{
        sam_Magic magic;

        return
                fread(&magic, sizeof magic, 1, archive) == 1 &&
                magic == SAM_MAGIC;
}

/*
 *      Write magic number.  On success return 1, otherwise 0.
 */
static int my_writemagic(FILE *newarchive)
{
        static sam_Magic magic = SAM_MAGIC;

        return fwrite(&magic, sizeof magic, 1, newarchive) == 1;
}

/*
 *      Read trailer.  If it is there and matches SAM_MAGIC
 *      return 1, otherwise 0.
 */
static int my_readtrailer(FILE *archive)
{
        sam_Trailer trailer;

        return
                fread(&trailer, sizeof trailer, 1, archive) == 1 &&
                trailer == SAM_TRAILER;
}

/*
 *      Write trailer.  On success return 1, otherwise 0.
 */
static int my_writetrailer(FILE *newarchive)
{
        static sam_Trailer trailer = SAM_TRAILER;

        return fwrite(&trailer, sizeof trailer, 1, newarchive) == 1;
}

/*
 *      Write given header.  On success return 1, otherwise 0.
 */
static int my_writeheader(FILE *newarchive, sam_Header *header)
{
        return fwrite(header, sizeof *header, 1, newarchive) == 1;
}

/*
 *      Check given header for consistency.  If it looks OK return 1, else 0.
 */
static int my_checkheader(sam_Header *header)
{
        return
                header->sam_size >= 0 &&
                header->sam_name[0] != '\0' &&
                header->sam_name[SAM_NAMELEN] == '\0';
}

/*
 *      Copy nbytes bytes from source to target.
 *      On success return 1, otherwise 0.
 */
static int my_copy(FILE *target, FILE *source, size_t nbytes)
{
        char buffer[BUFSIZ];

        while ( nbytes > BUFSIZ ) {
                if ( fread(buffer, 1, BUFSIZ, source) < BUFSIZ ||
                                fwrite(buffer, 1, BUFSIZ, target) < BUFSIZ )
                        return 0;
                nbytes -= BUFSIZ;
        }
        return
                fread(buffer, 1, nbytes, source) == nbytes &&
                fwrite(buffer, 1, nbytes, target) == nbytes;
}

/*
 *      Skip over nbytes bytes in archive.  On success return 1, otherwise 0.
 */
static int my_skip(FILE *archive, size_t nbytes)
{
        char buffer[BUFSIZ];

        /*
         *      Avoid fseek() to allow for input from pipes etc.
         */
        while ( nbytes > BUFSIZ ) {
                if ( fread(buffer, 1, BUFSIZ, archive) < BUFSIZ )
                        return 0;
                nbytes -= BUFSIZ;
        }
        return !nbytes || fread(buffer, 1, nbytes, archive) == nbytes;
}

/*
 *      Copy complete contents of source into target.
 *      On success return 1, otherwise 0.
 */
static int my_copyall(FILE *target, FILE *source)
{
        char buffer[BUFSIZ];
        size_t nbytes;

        while ( (nbytes = fread(buffer, 1, BUFSIZ, source)) &&
                        fwrite(buffer, 1, nbytes, target) == nbytes )
                ;

        return !nbytes && !ferror(source);
}

/*
 *      Allocate memory for a new locator and return it.
 */
static parser_Locator new_locator(void)
{
        parser_Locator result;

        result = (parser_Locator) malloc( sizeof (struct parser_loc) );
        if ( result == NULL ) {
                perror("memory allocation");
                exit(ECODE_PROBLEM);
        }
        return result;
}

/*
 *      Open archive (with given mode) for reading and check its magic
 *      number.  On success return appropriate locator, otherwise abort
 *      program.  If create is nonzero, allow archive not to exist,
 *      and let ploc_file = NULL in that case.
 */
static parser_Locator my_openread(char *archivename, char *mode, int create)
{
        parser_Locator result;

        result = new_locator();

        if ( !strcmp(archivename, "-") ) {
                result->ploc_file = stdin;
                result->ploc_name = "<stdin>";
        } else {
                if ( (result->ploc_file = fopen(archivename, mode)) == NULL &&
                                (!create || errno != ENOENT) ) {
                        perror(archivename);
                        exit(ECODE_FATAL);
                }
                result->ploc_name = archivename;
        }

        if ( result->ploc_file != NULL && !my_readmagic(result->ploc_file) ) {
                (void) fprintf(stderr, "%s: not a SAM archive\n",
                        result->ploc_name);
                exit(ECODE_FATAL);
        }

        return result;
}

/*
 *      Open temporary archive file and write magic number.  Return locator.
 */
static parser_Locator my_opentmp(void)
{
        parser_Locator result;
        
        result = new_locator();
        result->ploc_name = "tmpfile()";
        if ( (result->ploc_file = tmpfile()) == NULL ) {
                perror( result->ploc_name );
                exit(ECODE_PROBLEM);
        }
        if ( !my_writemagic( result->ploc_file ) ) {
                perror( result->ploc_name );
                exit(ECODE_PROBLEM);
        }
        return result;
}

/*
 *      Open archive for scanning.  Return locator in *arch.
 */
void parser_openread(char *archivename, parser_Locator *arch)
{
        *arch = my_openread(archivename, "r", 0);
}

/*
 *      Open archive for scanning and copying.  Return input locator in *arch
 *      and output locator associated with a temporary file in *tmparch.
 *      If create is nonzero, archive does not need to exist already.
 */
void parser_openreadwrite(char *archivename, int create,
                parser_Locator *arch, parser_Locator *tmparch)
{
        *arch = my_openread(archivename, "r+", create);
        /*
         *      Mode "r+" rather than "r" enforces an early test
         *      for write permission (which will be needed later).
         */

        *tmparch = my_opentmp();
}

/*
 *      Try to read another header from an archive.
 *      On success return 1, otherwise (on end of file) return 0.
 */
int parser_getheader(parser_Locator arch, sam_Header *header)
{
        size_t nbytes;

        if ( arch->ploc_file == NULL )
                return 0;               /* previously nonexistent archive */

        nbytes = fread(header, 1, sizeof *header, arch->ploc_file);
        /*
         *      Count bytes rather than header items so as to detect
         *      an incomplete header at the end of the archive.
         */
        if ( !nbytes && !ferror(arch->ploc_file) )
                return 0;
        if ( nbytes < sizeof *header ||
                        !my_checkheader(header) ||
                        !my_readtrailer(arch->ploc_file) ) {
                my_badarchive(arch);
                exit(ECODE_FATAL);
        }
        return 1;
}

/*
 *      Skip input locator past body of an archive member whose header
 *      has just been read into header.
 */
void parser_skipbody(parser_Locator arch, sam_Header *header)
{
        if ( !my_skip(arch->ploc_file, header->sam_size) ||
                        !my_readtrailer(arch->ploc_file) ) {
                my_badarchive(arch);
                exit(ECODE_FATAL);
        }
}

/*
 *      Create in tmparch a new archive member named name from given file.
 *      If file can be accessed properly return 0, otherwise 1.
 */
int parser_copyin(parser_Locator tmparch, char *filename, sam_Name name)
{
        sam_Header header;
        FILE *file;

        (void) memcpy(header.sam_name, name, sizeof (sam_Name));
        if ( files_getinfo(filename, &header) != FILES_SUCCESS ||
                        (file = fopen(filename, "r")) == NULL ) {
                perror(filename);
                return 0;
        }
        if ( !my_writeheader(tmparch->ploc_file, &header) ||
                        !my_writetrailer(tmparch->ploc_file) ||
                        !my_copy(tmparch->ploc_file, file, header.sam_size) ||
                        !my_writetrailer(tmparch->ploc_file) ) {
                if ( ferror(file) )
                        perror(filename);
                else
                        perror(tmparch->ploc_name);
                exit(ECODE_FATAL);
        }
        (void) fclose(file);
        return 1;
}

/*
 *      Restore a file from an archive member whose header has just been
 *      read into header.  If file can be written return 0, otherwise 1.
 *      In any case move locator past body of current archive member.
 */
int parser_copyout(parser_Locator arch, sam_Header *header)
{
        FILE *file;

        /*
         *      Note:  If the following fopen() call happens to hit the
         *      archive we are just reading we will run into trouble...
         */
        if ( (file = fopen(header->sam_name, "w")) == NULL ) {
                perror(header->sam_name);
                parser_skipbody(arch, header);
                return 0;
        }
        if ( !my_copy(file, arch->ploc_file, header->sam_size) ||
                        !my_readtrailer(arch->ploc_file) ) {
                if ( ferror(file) )
                        perror(header->sam_name);
                else
                        my_badarchive(arch);
                exit(ECODE_FATAL);
        }
        if ( fclose(file) ) {
                perror(header->sam_name);
                return 0;
        }
        return 1;
}

/*
 *      Copy to tmparch the archive member whose header has just been read
 *      into header.
 */
void parser_preserve(parser_Locator arch, parser_Locator tmparch,
                sam_Header *header)
{
        if ( !my_writeheader(tmparch->ploc_file, header) ||
                        !my_writetrailer(tmparch->ploc_file) ||
                        !my_copy(tmparch->ploc_file, arch->ploc_file,
                                header->sam_size) ||
                        !my_readtrailer(arch->ploc_file) ||
                        !my_writetrailer(tmparch->ploc_file) ) {
                if ( ferror(tmparch->ploc_file) )
                        perror(tmparch->ploc_name);
                else
                        my_badarchive(arch);
                exit(ECODE_FATAL);
        }
}

/*
 *      Close an archive opened with parser_openread().
 */
void parser_close(parser_Locator arch)
{
        if ( arch->ploc_file != NULL && arch->ploc_file != stdin ) {
                (void) fclose(arch->ploc_file);
                arch->ploc_file = NULL;
        }
        /* free(arch); */
}

/*
 *      Close an archive opened with parser_openreadwrite() and replace its
 *      contents by those written into tmparch, then discard tmparch.
 */
void parser_closeandcopy(parser_Locator arch, parser_Locator tmparch)
{
        /* rewind tmparch */
        if ( fflush(tmparch->ploc_file) ||
                        fseek(tmparch->ploc_file, 0L, SEEK_SET) ) {
                perror(tmparch->ploc_name);
                exit(ECODE_FATAL);
        }

        /* reopen arch for writing */
        if ( arch->ploc_file == stdin ) {
                arch->ploc_file = stdout;
                arch->ploc_name = "<stdout>";
        } else {
                if ( arch->ploc_file != NULL )
                        (void) fclose(arch->ploc_file);
                if ( (arch->ploc_file = fopen(arch->ploc_name, "w")) == NULL ) {
                        perror(arch->ploc_name);
                        exit(ECODE_FATAL);
                }
        }

        /* copy */
        if ( !my_copyall(arch->ploc_file, tmparch->ploc_file) ) {
                if ( ferror(arch->ploc_file) )
                        perror(arch->ploc_name);
                else
                        perror(tmparch->ploc_name);
                exit(ECODE_FATAL);
        }

        /* close archives */
        (void) fclose(tmparch->ploc_file);
        if ( arch->ploc_file == stdout ) {
                if ( fflush(stdout) ) {
                        perror(arch->ploc_name);
                        exit(ECODE_FATAL);
                }
        } else {
                if ( fclose(arch->ploc_file) ) {
                        perror(arch->ploc_name);
                        exit(ECODE_FATAL);
                }
        }
        tmparch->ploc_file = arch->ploc_file = NULL;
}

<- Alle Module
Martin Hasch, Februar 1997