Universität Ulm,
SAI,
Übungen zu
Systemnahe Software I
Lösungsbeispiel zu Blatt 9 (Aufgabe 13): parser
Parser für SAM-Archive.
/*
* 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 - 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