/*
* animnode.c
*
* Created by Thomas Davie on Fri Nov 21 2003.
* Copyright (c) 2003 Thomas Davie. All rights reserved.
*
*/
#include <stdio.h>
#include "art.h"
#include "animnode.h"
// Private functions
node* readAtomAbstract(FILE *hatFile, unsigned long offset);
node* readAtomConstructor(FILE *hatFile, unsigned long offset);
node* readAtomVariable(FILE *hatFile, unsigned long offset);
node* readExpApp(FILE *hatFile, unsigned long offset);
node* readExpCase(FILE *hatFile, unsigned long offset);
node* readExpChar(FILE *hatFile, unsigned long offset);
node* readExpConstDef(FILE *hatFile, unsigned long offset);
node* readExpConstUse(FILE *hatFile, unsigned long offset);
node* readExpDouble(FILE *hatFile, unsigned long offset);
node* readExpDoStmt(FILE *hatFile, unsigned long offset);
node* readExpFieldUpdate(FILE *hatFile, unsigned long offset);
node* readExpFloat(FILE *hatFile, unsigned long offset);
node* readExpForward(FILE *hatFile, unsigned long offset);
node* readExpGuard(FILE *hatFile, unsigned long offset);
node* readExpHidden(FILE *hatFile, unsigned long offset);
node* readExpIf(FILE *hatFile, unsigned long offset);
node* readExpInt(FILE *hatFile, unsigned long offset);
node* readExpInteger(FILE *hatFile, unsigned long offset);
node* readExpProjection(FILE *hatFile, unsigned long offset);
node* readExpRat(FILE *hatFile, unsigned long offset);
node* readExpRational(FILE *hatFile, unsigned long offset);
node* readExpValueApp(FILE *hatFile, unsigned long offset);
node* readExpValueUse(FILE *hatFile, unsigned long offset);
node* readModule(FILE *hatFile, unsigned long offset);
node* readSrcPos(FILE *hatFile, unsigned long offset);
void setFilePos (FILE *hatFile, unsigned long offset);
unsigned int readArity (FILE *hatFile);
unsigned long readPointer (FILE *hatFile);
unsigned long readInt (FILE *hatFile);
float readFloat (FILE *hatFile);
double readDouble (FILE *hatFile);
position readPosition (FILE *hatFile);
fixPri readFixPri (FILE *hatFile);
char *readString (FILE *hatFile);
fourbytes readFourBytes (FILE *hatFile);
char readByte (FILE *hatFile);
// Code
/**
* Read a node from a hat file.
*
* @param hatFile The file to read from - assumed to be open.
* @param offset The offset to start reading in the hat file.
* @return A node pointer to the node found in the hatFile at offset.
*/
node* readNode (FILE *hatFile, unsigned long offset)
{
int err;
char b;
node *newNode;
// Find out which type of node we have.
fseek (hatFile, offset, SEEK_SET);
err = fread (&b, sizeof(char), 1, hatFile);
if (err != 1)
{
return NULL;
}
else
{
int tag = lower5(b);
if ((ExpDoStmt < tag && tag < AtomVariable) || tag > AtomAbstract)
{
fprintf (stderr, "strange tag %d at byte offset 0x%x\n", tag, offset);
exit (1);
}
switch (tag)
{
case AtomAbstract:
newNode = readAtomAbstract(hatFile, offset);
break;
case AtomConstructor:
newNode = readAtomConstructor(hatFile, offset);
break;
case AtomVariable:
newNode = readAtomVariable(hatFile, offset);
break;
case ExpApp:
newNode = readExpApp(hatFile, offset);
break;
case ExpCase:
newNode = readExpCase(hatFile, offset);
break;
case ExpChar:
newNode = readExpChar(hatFile, offset);
break;
case ExpConstDef:
newNode = readExpConstDef(hatFile, offset);
break;
case ExpConstUse:
newNode = readExpConstUse(hatFile, offset);
break;
case ExpDouble:
newNode = readExpDouble(hatFile, offset);
break;
case ExpDoStmt:
newNode = readExpDoStmt(hatFile, offset);
break;
case ExpFieldUpdate:
newNode = readExpFieldUpdate(hatFile, offset);
break;
case ExpFloat:
newNode = readExpFloat(hatFile, offset);
break;
case ExpForward:
newNode = readExpForward(hatFile, offset);
break;
case ExpGuard:
newNode = readExpGuard(hatFile, offset);
break;
case ExpHidden:
newNode = readExpHidden(hatFile, offset);
break;
case ExpIf:
newNode = readExpIf(hatFile, offset);
break;
case ExpInt:
newNode = readExpInt(hatFile, offset);
break;
case ExpInteger:
newNode = readExpInteger(hatFile, offset);
break;
case ExpProjection:
newNode = readExpProjection(hatFile, offset);
break;
case ExpRat:
newNode = readExpRat(hatFile, offset);
break;
case ExpRational:
newNode = readExpRational(hatFile, offset);
break;
case ExpValueApp:
newNode = readExpValueApp(hatFile, offset);
break;
case ExpValueUse:
newNode = readExpValueUse(hatFile, offset);
break;
case Module:
newNode = readModule(hatFile, offset);
break;
case SrcPos:
newNode = readSrcPos(hatFile, offset);
break;
}
return newNode;
}
}
// Read individual nodes
/**
* Attempt to read an atom abstract type node at a certain offset.
*
* @param hatFile The file to read the node from.
* @param offset The offset where the start of the node should be.
* @return A node construct containing the read values.
*/
node* readAtomAbstract(FILE *hatFile, unsigned long offset)
{
node *newNode = (node *)malloc(sizeof(node));
newNode->nodeType = AtomAbstract;
newNode->offset = offset;
setFilePos(hatFile, offset + 1);
newNode->params.atomAbstract.value = readString(hatFile);
return newNode;
}
/**
* Attempt to read an atom constructor type node at a certain offset.
*
* @param hatFile The file to read the node from.
* @param offset The offset where the start of the node should be.
* @return A node construct containing the read values.
*/
node* readAtomConstructor(FILE *hatFile, unsigned long offset)
{
int argIndex;
char tag;
node *newNode = (node *)malloc(sizeof(node));
newNode->nodeType = AtomConstructor;
newNode->offset = offset;
setFilePos(hatFile, offset);
tag = readByte(hatFile);
newNode->params.atomConstructor.module = readPointer(hatFile);
newNode->params.atomConstructor.filePos = readPosition(hatFile);
readPosition(hatFile); /* skip position end */
newNode->params.atomConstructor.fix = readFixPri(hatFile);
newNode->params.atomConstructor.arity = readArity(hatFile);
newNode->params.atomConstructor.name = readString(hatFile);
if (newNode->params.atomConstructor.hasFields = hasFields(tag))
{
newNode->params.atomConstructor.args = (unsigned long *)malloc(newNode->params.atomConstructor.arity * sizeof(unsigned long));
for (argIndex = 0; argIndex < newNode->params.atomConstructor.arity; argIndex++)
{
newNode->params.atomConstructor.args[argIndex] = readPointer(hatFile);
}
}
return newNode;
}
node* readAtomVariable(FILE *hatFile, unsigned long offset)
{
node *newNode = (node *)malloc(sizeof(node));
char tag;
newNode->nodeType = AtomVariable;
newNode->offset = offset;
setFilePos(hatFile, offset);
tag = readByte(hatFile);
newNode->params.atomVariable.local = localDef(tag);
newNode->params.atomVariable.module = readPointer(hatFile);
newNode->params.atomVariable.filePos = readPosition(hatFile);
readPosition(hatFile); /* skip position end */
newNode->params.atomVariable.fix = readFixPri(hatFile);
newNode->params.atomVariable.arity = readArity(hatFile);
newNode->params.atomVariable.name = readString(hatFile);
return newNode;
}
/**
* Attempt to read an expression application type node at a certain offset.
*
* @param hatFile The file to read the node from.
* @param offset The offset where the start of the node should be.
* @return A node construct containing the read values.
*/
node* readExpApp(FILE *hatFile, unsigned long offset)
{
int argIndex;
node *newNode = (node *)malloc(sizeof(node));
char tag;
newNode->nodeType = ExpApp;
newNode->offset = offset;
setFilePos(hatFile, offset);
tag = readByte(hatFile);
if (newNode->params.expApp.hasUse = hasSrcPos(tag))
{
newNode->params.expApp.use = readPointer(hatFile);
}
newNode->params.expApp.parent = readPointer(hatFile);
newNode->params.expApp.result = readPointer(hatFile);
newNode->params.expApp.function = readPointer(hatFile);
newNode->params.expApp.arity = readArity(hatFile);
newNode->params.expApp.args = (unsigned long *)malloc(newNode->params.expApp.arity * sizeof(unsigned long));
for (argIndex = 0; argIndex < newNode->params.expApp.arity; argIndex++)
{
newNode->params.expApp.args[argIndex] = readPointer(hatFile);
}
return newNode;
}
node* readExpCase(FILE *hatFile, unsigned long offset)
{
node *newNode = (node *)malloc(sizeof(node));
char tag;
newNode->nodeType = ExpCase;
newNode->offset = offset;
setFilePos(hatFile, offset);
tag = readByte(hatFile);
if (newNode->params.expCase.hasUse = hasSrcPos(tag))
{
newNode->params.expCase.use = readPointer(hatFile);
}
newNode->params.expCase.parent = readPointer(hatFile);
newNode->params.expCase.result = readPointer(hatFile);
newNode->params.expCase.condition = readPointer(hatFile);
return newNode;
}
node* readExpChar(FILE *hatFile, unsigned long offset)
{
node *newNode = (node *)malloc(sizeof(node));
char tag;
newNode->nodeType = ExpChar;
newNode->offset = offset;
setFilePos(hatFile, offset);
tag = readByte(hatFile);
if (newNode->params.expChar.hasUse = hasSrcPos(tag))
{
newNode->params.expChar.use = readPointer(hatFile);
}
newNode->params.expChar.parent = readPointer(hatFile);
newNode->params.expChar.value = readByte(hatFile);
return newNode;
}
/**
* Attempt to read a constant deffinition expression type node at a certain offset.
*
* @param hatFile The file to read the node from.
* @param offset The offset where the start of the node should be.
* @return A node construct containing the read values.
*/
node* readExpConstDef(FILE *hatFile, unsigned long offset)
{
node *newNode = (node *)malloc(sizeof(node));
newNode->nodeType = ExpConstDef;
newNode->offset = offset;
setFilePos(hatFile, offset + 1);
newNode->params.expConstDef.parent = readPointer(hatFile);
newNode->params.expConstDef.result = readPointer(hatFile);
newNode->params.expConstDef.var = readPointer(hatFile);
return newNode;
}
node* readExpConstUse(FILE *hatFile, unsigned long offset)
{
node *newNode = (node *)malloc(sizeof(node));
char tag;
newNode->nodeType = ExpConstUse;
newNode->offset = offset;
setFilePos(hatFile, offset);
tag = readByte(hatFile);
if (newNode->params.expConstUse.hasUse = hasSrcPos(tag))
{
newNode->params.expConstUse.use = readPointer(hatFile);
}
newNode->params.expConstUse.parent = readPointer(hatFile);
newNode->params.expConstUse.value = readPointer(hatFile);
return newNode;
}
node* readExpDoStmt(FILE *hatFile, unsigned long offset)
{
node *newNode = (node *)malloc(sizeof(node));
char tag;
newNode->nodeType = ExpDoStmt;
newNode->offset = offset;
setFilePos(hatFile, offset);
tag = readByte(hatFile);
if (newNode->params.expDoStmt.hasUse = hasSrcPos(tag))
{
newNode->params.expDoStmt.use = readPointer(hatFile);
}
newNode->params.expDoStmt.statement = readPointer(hatFile);
return newNode;
}
node* readExpDouble(FILE *hatFile, unsigned long offset)
{
node *newNode = (node *)malloc(sizeof(node));
char tag;
newNode->nodeType = ExpDouble;
newNode->offset = offset;
setFilePos(hatFile, offset);
tag = readByte(hatFile);
if (newNode->params.expDouble.hasUse = hasSrcPos(tag))
{
newNode->params.expDouble.use = readPointer(hatFile);
}
newNode->params.expDouble.parent = readPointer(hatFile);
newNode->params.expDouble.value = readDouble(hatFile);
return newNode;
}
node* readExpFloat(FILE *hatFile, unsigned long offset)
{
node *newNode = (node *)malloc(sizeof(node));
char tag;
newNode->nodeType = ExpFloat;
newNode->offset = offset;
setFilePos(hatFile, offset);
tag = readByte(hatFile);
if (newNode->params.expFloat.hasUse = hasSrcPos(tag))
{
newNode->params.expFloat.use = readPointer(hatFile);
}
newNode->params.expFloat.parent = readPointer(hatFile);
newNode->params.expFloat.value = readFloat(hatFile);
return newNode;
}
node* readExpFieldUpdate(FILE *hatFile, unsigned long offset)
{
int argIndex;
node *newNode = (node *)malloc(sizeof(node));
char tag;
newNode->nodeType = ExpFieldUpdate;
newNode->offset = offset;
setFilePos(hatFile, offset);
tag = readByte(hatFile);
if (newNode->params.expFieldUpdate.hasUse = hasSrcPos(tag))
{
newNode->params.expFieldUpdate.use = readPointer(hatFile);
}
newNode->params.expFieldUpdate.parent = readPointer(hatFile);
newNode->params.expFieldUpdate.result = readPointer(hatFile);
newNode->params.expFieldUpdate.arg = readPointer(hatFile);
newNode->params.expFieldUpdate.arity = readArity(hatFile);
newNode->params.expFieldUpdate.bindees = (unsigned long *)malloc(newNode->params.expFieldUpdate.arity * sizeof(unsigned long));
newNode->params.expFieldUpdate.binders = (unsigned long *)malloc(newNode->params.expFieldUpdate.arity * sizeof(unsigned long));
for (argIndex = 0; argIndex < newNode->params.expFieldUpdate.arity; argIndex++)
{
newNode->params.expFieldUpdate.binders[argIndex] = readPointer(hatFile);
}
for (argIndex = 0; argIndex < newNode->params.expFieldUpdate.arity; argIndex++)
{
newNode->params.expFieldUpdate.bindees[argIndex] = readPointer(hatFile);
}
return newNode;
}
/**
* Attempt to read a forward type node at a certain offset.
*
* @param hatFile The file to read the node from.
* @param offset The offset where the start of the node should be.
* @return A node construct containing the read values.
*/
node* readExpForward(FILE *hatFile, unsigned long offset)
{
node *newNode = (node *)malloc(sizeof(node));
newNode->nodeType = ExpForward;
newNode->offset = offset;
setFilePos(hatFile, offset + 1);
newNode->params.expForward.result = readPointer(hatFile);
return newNode;
}
node* readExpGuard(FILE *hatFile, unsigned long offset)
{
node *newNode = (node *)malloc(sizeof(node));
char tag;
newNode->nodeType = ExpGuard;
newNode->offset = offset;
setFilePos(hatFile, offset);
tag = readByte(hatFile);
if (newNode->params.expGuard.hasUse = hasSrcPos(tag))
{
newNode->params.expGuard.use = readPointer(hatFile);
}
newNode->params.expGuard.parent = readPointer(hatFile);
newNode->params.expGuard.result = readPointer(hatFile);
newNode->params.expGuard.condition = readPointer(hatFile);
return newNode;
}
/**
* Attempt to read a hidden expression type node at a certain offset.
*
* @param hatFile The file to read the node from.
* @param offset The offset where the start of the node should be.
* @return A node construct containing the read values.
*/
node* readExpHidden(FILE *hatFile, unsigned long offset)
{
node *newNode = (node *)malloc(sizeof(node));
newNode->nodeType = ExpHidden;
newNode->offset = offset;
setFilePos(hatFile, offset + 1);
newNode->params.expHidden.parent = readPointer(hatFile);
newNode->params.expHidden.result = readPointer(hatFile);
return newNode;
}
node* readExpIf(FILE *hatFile, unsigned long offset)
{
node *newNode = (node *)malloc(sizeof(node));
char tag;
newNode->nodeType = ExpIf;
newNode->offset = offset;
setFilePos(hatFile, offset);
tag = readByte(hatFile);
if (newNode->params.expIf.hasUse = hasSrcPos(tag))
{
newNode->params.expIf.use = readPointer(hatFile);
}
newNode->params.expIf.parent = readPointer(hatFile);
newNode->params.expIf.result = readPointer(hatFile);
newNode->params.expIf.condition = readPointer(hatFile);
return newNode;
}
node* readExpInt(FILE *hatFile, unsigned long offset)
{
node *newNode = (node *)malloc(sizeof(node));
char tag;
newNode->nodeType = ExpInt;
newNode->offset = offset;
setFilePos(hatFile, offset);
tag = readByte(hatFile);
if (newNode->params.expInt.hasUse = hasSrcPos(tag))
{
newNode->params.expInt.use = readPointer(hatFile);
}
newNode->params.expInt.parent = readPointer(hatFile);
newNode->params.expInt.value = readInt(hatFile);
return newNode;
}
node* readExpInteger(FILE *hatFile, unsigned long offset)
{
node *newNode = (node *)malloc(sizeof(node));
char tag;
newNode->nodeType = ExpInteger;
newNode->offset = offset;
setFilePos(hatFile, offset);
tag = readByte(hatFile);
if (newNode->params.expInteger.hasUse = hasSrcPos(tag))
{
newNode->params.expInteger.use = readPointer(hatFile);
}
newNode->params.expInteger.parent = readPointer(hatFile);
newNode->params.expInteger.value = readString(hatFile);
return newNode;
}
node* readExpProjection(FILE *hatFile, unsigned long offset)
{
node *newNode = (node *)malloc(sizeof(node));
char tag;
newNode->nodeType = ExpProjection;
newNode->offset = offset;
setFilePos(hatFile, offset);
tag = readByte(hatFile);
if (newNode->params.expProjection.hasUse = hasSrcPos(tag))
{
newNode->params.expProjection.use = readPointer(hatFile);
}
newNode->params.expProjection.parent = readPointer(hatFile);
newNode->params.expProjection.exp = readPointer(hatFile);
return newNode;
}
node* readExpRat(FILE *hatFile, unsigned long offset)
{
node *newNode = (node *)malloc(sizeof(node));
char tag;
newNode->nodeType = ExpRat;
newNode->offset = offset;
setFilePos(hatFile, offset);
tag = readByte(hatFile);
if (newNode->params.expRat.hasUse = hasSrcPos(tag))
{
newNode->params.expRat.use = readPointer(hatFile);
}
newNode->params.expRat.parent = readPointer(hatFile);
newNode->params.expRat.numerator = readInt(hatFile);
newNode->params.expRat.denominator = readInt(hatFile);
return newNode;
}
node* readExpRational(FILE *hatFile, unsigned long offset)
{
node *newNode = (node *)malloc(sizeof(node));
char tag;
newNode->nodeType = ExpRational;
newNode->offset = offset;
setFilePos(hatFile, offset);
tag = readByte(hatFile);
if (newNode->params.expRational.hasUse = hasSrcPos(tag))
{
newNode->params.expRational.use = readPointer(hatFile);
}
newNode->params.expRational.parent = readPointer(hatFile);
newNode->params.expRational.numerator = readString(hatFile);
newNode->params.expRational.denominator = readString(hatFile);
return newNode;
}
/**
* Attempt to read a value applicaiton expression type node at a certain offset.
*
* @param hatFile The file to read the node from.
* @param offset The offset where the start of the node should be.
* @return A node construct containing the read values.
*/
node* readExpValueApp(FILE *hatFile, unsigned long offset)
{
int argIndex;
node *newNode = (node *)malloc(sizeof(node));
char tag;
newNode->nodeType = ExpValueApp;
newNode->offset = offset;
setFilePos(hatFile, offset);
tag = readByte(hatFile);
if (newNode->params.expValueApp.hasUse = hasSrcPos(tag))
{
newNode->params.expValueApp.use = readPointer(hatFile);
}
newNode->params.expValueApp.parent = readPointer(hatFile);
newNode->params.expValueApp.function = readPointer(hatFile);
newNode->params.expValueApp.arity = readArity(hatFile);
newNode->params.expValueApp.args = (unsigned long *)malloc(newNode->params.expValueApp.arity * sizeof(unsigned long));
for (argIndex = 0; argIndex < newNode->params.expValueApp.arity; argIndex++)
{
newNode->params.expValueApp.args[argIndex] = readPointer(hatFile);
}
return newNode;
}
/**
* Attempt to read a value use expression type node at a certain offset.
*
* @param hatFile The file to read the node from.
* @param offset The offset where the start of the node should be.
* @return A node construct containing the read values.
*/
node* readExpValueUse(FILE *hatFile, unsigned long offset)
{
node *newNode = (node *)malloc(sizeof(node));
char tag;
newNode->nodeType = ExpValueUse;
newNode->offset = offset;
setFilePos(hatFile, offset);
tag = readByte(hatFile);
if (newNode->params.expValueUse.hasUse = hasSrcPos(tag))
{
newNode->params.expValueUse.use = readPointer(hatFile);
}
newNode->params.expValueUse.parent = readPointer(hatFile);
newNode->params.expValueUse.value = readPointer(hatFile);
newNode->params.expValueUse.isLambda = (newNode->params.expValueUse.value == 4);
return newNode;
}
node* readModule(FILE *hatFile, unsigned long offset)
{
node *newNode = (node *)malloc(sizeof(node));
char tag;
newNode->nodeType = Module;
newNode->offset = offset;
setFilePos(hatFile, offset);
tag = readByte(hatFile);
newNode->params.module.name = readString(hatFile);
newNode->params.module.filename = readString(hatFile);
newNode->params.module.wasTraced = tracedModule(tag);
return newNode;
}
node* readSrcPos(FILE *hatFile, unsigned long offset)
{
node *newNode = (node *)malloc(sizeof(node));
newNode->nodeType = SrcPos;
newNode->offset = offset;
setFilePos(hatFile, offset);
newNode->params.srcPos.module = readPointer(hatFile);
newNode->params.srcPos.filePos = readPosition(hatFile);
readPosition(hatFile); /* skip position end */
return newNode;
}
// Lower level file reading opps
void setFilePos (FILE *hatFile, unsigned long offset)
{
fseek (hatFile, offset, SEEK_SET);
}
unsigned int readArity (FILE *hatFile)
{
return (unsigned int)(unsigned char)readByte(hatFile);
}
unsigned long readPointer (FILE *hatFile)
{
return readFourBytes(hatFile);
}
unsigned long readInt (FILE *hatFile)
{
return readFourBytes(hatFile);
}
float readFloat (FILE *hatFile)
{
return readFourBytes(hatFile);
}
double readDouble (FILE *hatFile)
{
unsigned long bytes[2];
*(bytes+1) = readFourBytes(hatFile);
*(bytes) = readFourBytes(hatFile);
return (double)(*bytes);
}
position readPosition (FILE *hatFile) /* reads only position beginning, not end */
{
position returnVal;
unsigned long posn = readFourBytes(hatFile);
returnVal.line = posn / 10000;
returnVal.column = posn % 10000;
return returnVal;
}
fixPri readFixPri (FILE *hatFile)
{
int b = (int)readByte(hatFile);
fixPri returnVal;
switch (b % 4)
{
case 0:
returnVal.fix = infix;
returnVal.something = b / 4;
break;
case 1:
returnVal.fix = infixr;
returnVal.something = b / 4;
break;
case 2:
returnVal.fix = infixl;
returnVal.something = b / 4;
break;
case 3:
returnVal.fix = defaultFix;
returnVal.something = 0;
break;
}
return returnVal;
}
/* readstring() reads a length-annotated string from the current position
* in the file and stores it in a global buffer.
*/
char* readString (FILE *hatFile)
{
char *buf;
int charsRead,
stringLength;
stringLength = (int)fgetc(hatFile);
if (stringLength == 0xff)
{
stringLength = (int)fgetc(hatFile);
stringLength = (stringLength << 8) + (int)fgetc(hatFile);
}
buf = (char *)malloc(stringLength * sizeof(char));
charsRead = fread(buf, sizeof(char), stringLength, hatFile);
buf[stringLength] = '\0';
if (charsRead < stringLength)
{
fprintf(stderr,"hat-anim: warning, only read %d characters of %d in string\n", charsRead, stringLength);
}
return buf;
}
fourbytes readFourBytes (FILE *hatFile)
{
int err;
fourbytes slot;
err = fread(&slot, sizeof(fourbytes), 1, hatFile);
if (err!=1)
{
fflush(stdout);
fprintf(stderr, "unexpected end of trace file\n");
fprintf(stderr, "actual file position is 0x%x\n",ftell(hatFile));
exit(1);
}
return ntohl(slot);
}
char readByte (FILE *hatFile)
{
char c;
int err;
err = fread(&c, sizeof(char), 1, hatFile);
if (err!=1)
{
fflush(stdout);
fprintf(stderr, "unexpected end of trace file\n");
exit(1);
}
return c;
}
syntax highlighted by Code2HTML, v. 0.9.1