ftp.nice.ch/pub/next/connectivity/infosystems/WAIStation.1.9.6.N.b.tar.gz#/WAIS/ir/irfileio.c

This is irfileio.c in view mode; [Download] [Up]

/* WIDE AREA INFORMATION SERVER SOFTWARE:
   No guarantees or restrictions.  See the readme file for the full standard
   disclaimer.

   Brewster@think.com
*/

/* Change log:
 * $Log:	irfileio.c,v $
 * Revision 1.12  92/04/12  17:30:21  jonathan
 * Fixed WriteString to quote backslash as well as double-quotes.  Thanks to
 * dxl0243@hertz.njit.edu (Dong Liu).
 * 
 * Revision 1.11  92/03/06  10:57:16  jonathan
 * removed extraneous ; after function WriteTM.
 * 
 * Revision 1.10  92/03/04  19:20:33  jonathan
 * Made SkipObject return EOF if at end of file.
 * 
 * Revision 1.9  92/02/16  21:08:37  jonathan
 * Changed a few more char ch to long ch (for return value of fgetc).
 * 
 * Revision 1.8  92/02/12  13:24:13  jonathan
 * Added "$Log" so RCS will put the log message in the header
 * 
*/

/********************************************************
 *  Writing and reading structures to files.		*
 *							*
 *  These use the Lisp printer format with the		*
 *  lisp naming conventions.  You ask: "Why would	*
 *  you want to use that?".  Well, we need an		*
 *  easily readable data syntax that can handle		*
 *  a large number of different data types.		*
 *  Further, we need it to be tagged so that		*
 *  run time tagged languages can read it and 		*
 *  it is flexible.  We need one that supports		*
 *  optional fields so that the format can 		*
 *  grow backcompatibly.  And (the kicker),		*
 *  it must be read from many languages since		*
 *  user interfaces may be written in anything		*
 *  from smalltalk to hypercard.			*
 * 							*
 *  -brewster 5/10/90					*
 ********************************************************/

#include <string.h>
#include <ctype.h>
#include "irfileio.h"
#include "cutil.h"

#define INDENT_SPACES 2
#define MAX_INDENT 40
static long indent_level;  /* this is the number of indent levels */


/**********************/
/*  WRITING TO FILES  */
/**********************/


static void indent _AP((FILE* file));

static void indent(file)
FILE* file;
/* indent the right number of spaces.  make sure that indent_level is 
 * non-negative, and that it does not indent too much
 */
{
  long i;
  for(i = 0; i <= MIN(MAX_INDENT, MAX(0L, indent_level * INDENT_SPACES)); i++){
    putc(' ', file);
  }
}

long WriteStartOfList(file)
FILE* file;
{
  indent_level++;
  return(fprintf(file, " ( "));
}

long WriteEndOfList(file)
FILE* file;
{
  indent_level--;
  return(fprintf(file, " ) "));
}

long WriteStartOfStruct(name,file)
char* name;
FILE* file;
{
  indent_level++;
  return(fprintf(file, " (:%s ", name));
}

long WriteEndOfStruct(file)
FILE* file;
{
  indent_level--;
  return(fprintf(file, " ) "));
}

long WriteSymbol(name,file)
char* name;
FILE* file;
{
  return(fprintf(file, " %s ", name));
}

long WriteNewline(file)
FILE* file;
{
  long return_value = fprintf(file, "\n");
  indent(file);
  return(return_value);
}

long WriteLong(number,file)
long number;
FILE* file;
{
  return(fprintf(file, " %ld ", number));  
}

long WriteDouble(number,file)
double number;
FILE* file;
{
  return(fprintf(file, " %f ", number));
}

long WriteString(string,file)
char* string;
FILE* file;
{
  long i;
  putc('\"', file);
  for(i = 0; i < strlen(string); i++){
    if(string[i] == '\\' || string[i] == '\"')
      putc('\\', file);		/* quote the string quotes going into the file */
    putc(string[i], file);
  }
  putc('\"', file);
  return(1);
}

long WriteAny(value,file)
any* value;
FILE* file;
{
  WriteStartOfStruct("any", file);
  WriteSymbol(":size", file); WriteLong(value->size, file);
  WriteSymbol(":bytes", file);
  Write8BitArray(value->size, value->bytes, file);
  return(WriteEndOfStruct(file));
}

long Write8BitArray(length,array,file)
long length;
char* array;
FILE* file;
{
  long i;
  fprintf(file, " #( ");
  for(i=0; i<length; i++){
    WriteLong((long)array[i], file);
  }
  return(fprintf(file, " ) "));
}

/* Writes a time object to a file */
long WriteTM(atime,file)
struct tm* atime;
FILE* file;
{
  WriteStartOfStruct("tm", file);
  WriteSymbol(":tm-sec", file); WriteLong(atime->tm_sec, file);
  WriteSymbol(":tm-min", file); WriteLong(atime->tm_min, file);
  WriteSymbol(":tm-hour", file); WriteLong(atime->tm_hour, file);
  WriteSymbol(":tm-mday", file); WriteLong(atime->tm_mday, file);
  WriteSymbol(":tm-mon", file); WriteLong(atime->tm_mon, file);
  WriteNewline(file);
  WriteSymbol(":tm-year", file); WriteLong(atime->tm_year, file);
  WriteSymbol(":tm-wday", file); WriteLong(atime->tm_wday, file);
  WriteNewline(file);
  WriteSymbol(":tm-yday", file); WriteLong(atime->tm_yday, file);
  WriteSymbol(":tm-isdst", file); WriteLong(atime->tm_isdst, file);
  WriteEndOfStruct(file);
  return(WriteNewline(file));
}

Boolean  
writeAbsoluteTime(atime,file)
struct tm* atime;
FILE* file;
{
  WriteStartOfStruct("absolute-time",file);
  WriteNewline(file);
  WriteSymbol(":year",file); WriteLong((long)atime->tm_year,file);
  WriteNewline(file);
  WriteSymbol(":month",file); WriteLong((long)atime->tm_mon,file);
  WriteNewline(file);
  WriteSymbol(":mday",file); WriteLong((long)atime->tm_mday,file);
  WriteNewline(file);
  WriteSymbol(":hour",file); WriteLong((long)atime->tm_hour,file);
  WriteNewline(file);
  WriteSymbol(":minute",file); WriteLong((long)atime->tm_min,file);
  WriteNewline(file);
  WriteSymbol(":second",file); WriteLong((long)atime->tm_sec,file);
  WriteNewline(file);
  return(WriteEndOfStruct(file));
}




/************************/
/*  READING FROM FILES  */
/************************/


/* these are states of the parser */
#define BEFORE 1
#define DURING 2
#define HASH 3
#define S 4
#define QUOTE 5

/* returns TRUE if it hits an '(' before hitting any non whitespace.
   It has an added hack to detect NIL: it looks to make sure that it is nil,
   and then ungetc's a \) on the stream so that the end-checkers will catch it.
   Quack. */
Boolean ReadStartOfList(file)
FILE* file;
{
  long ch;
  while(TRUE){
    ch = getc(file);
    if(ch == '(') 
      return(TRUE);
    if(!isspace(ch)){
      /* check for NIL */
      if(ch == 'N' || ch == 'n'){
	ch = getc(file);
	if(ch == 'I' || ch == 'i'){
	  ch = getc(file);
	  if(ch == 'L' || ch == 'l'){
	    ungetc(')', file);
	    return(TRUE);
	  }
	}
      }
      return(FALSE); /* not NIL */
    }
  }
}


/* returns TRUE if it hits an ')' before hitting any non whitespace*/
Boolean ReadEndOfList(file)
FILE* file;
{
  long ch;
  while(TRUE){
    ch = getc(file);
    if(ch == ')') 
      return(TRUE);
    if(!isspace(ch)) 
      return(FALSE);
  }
}

#define STRING_ESC '\\'

long 
SkipObject(file)
FILE* file;
/* read an object of unknown type out of the file.  We handle:
      strings
      longs, doubles, and symbols
      structs, lists, and arrays
   and anything composed of them (absolute time etc)
*/
{
  long ch;

  while (true)	
    { ch = getc(file);
      if (ch == EOF)
	return (EOF); /* we are done */
      else
	{ if (isspace(ch))
	    continue; /* skip this char */
	  else if (ch == '"') /* string */
	    { long escapeCount = 0;
	      while (true)
		{ ch = getc(file);
		  if (ch == EOF)
		    return (EOF);
		  else
		    { if (ch == STRING_ESC)
			{ escapeCount++;
			  escapeCount = escapeCount % 2;
			}
		      if (ch == '"' && escapeCount == 0)
			break; /* out of reading string */
                    }
		}
	      break; /* we are done */
            }
	  else if ((isdigit(ch) || ch == '-' || ch == '.') || /* number */
		   (ch == ':')) /* symbol */
	    { while (!isspace(ch)) /* just read till there is white space */
		{ ch = getc(file);
		  if (ch == EOF)
		    return(EOF);
		}
	      break; /* we are done */
	    }
	  else if ((ch == '#') || /* array */
		   (ch == '(')) /* struct or list */
	    { long parenCount = 1;
	      if (ch == '#')	
		ch = getc(file); /* read in the '(' so we can think of it
				    as a list */
	      while (parenCount > 0)
		{ ch = getc(file);
		  if (ch == EOF)
		    return(EOF);
		  else if (ch == '"')
		    { /* start of a string, it may contain parens, 
			 so we will have to skip it */
		      ungetc(ch,file);
		      SkipObject(file);
		    }
		  else if (ch == '(') /* entering a new structure */	
		    parenCount++;
		  else if (ch == ')') /* leaving a structure */
		    parenCount--;
		}
	      break; /* we are done */
	    }
	}
    }

  return(true);
}

long ReadLong(file,answer)
FILE* file;
long* answer;
/* reads a long int a file returns true if successful, false otherwise */
{
  long ch;
  long state = BEFORE;
  boolean isNegative = false;
  long count = 0;
  
  *answer = 0;
  
  while(TRUE){
    ch = getc(file);
    if (ch == EOF){
      break;			/* we are done */
    }
    else if (isdigit(ch)){
      if(state == BEFORE){
	state = DURING;
      }
      count++;
      if(count == 12){
	/* then we have an error in the file, 32 bit numbers can not be more
	   than 10 digits long */
	return(false);
      }
      *answer = *answer * 10 + (ch - '0');
    }
    else if (ch == '-') {
      if (isNegative)
	/* then we have an error since there should be only one - in a number */
	return(false);
      if (state == BEFORE) {
	/* we are ok since the - must come before any digits */
	isNegative = true;
	state = DURING;
      }
      else {
	ungetc(ch,file);
	break;			/* we are done */
      }
    }
    else if(ch == ')' && (state == DURING)){
      ungetc(ch, file);
      return(true);		/* we are done */
    }
    else if(!isspace(ch)){
      /* then we have an error since it should be a digit or a space */
      return(false);
    }
    /* we do not have an digit */
    else if(state == DURING){
      ungetc(ch, file);
      break;			/* we are done */
    }
    /* otherwise we are still before the start */
  }
  
  if (isNegative)
    *answer *= -1;
  return(true);
}

long ReadDouble(file,answer)
FILE* file;
double* answer;
{
  /* XXX this routine needs to deal with negative numbers! */
  long ch;
  long state = BEFORE;
  long count = 0;
  long decimal_count = 0;
  
  *answer = 0.0;
  
  while(TRUE){	
    ch = getc(file);
    if (ch == EOF){
      return(true);
    }
    else if (ch == '.'){
      decimal_count ++;
    }
    else if (isdigit(ch)){
      if(state == BEFORE){
	state = DURING;
      }
      count++;
      if(count == 12){
	/* then we have an error in the file, 32 bit numbers can not be more
	   than 10 digits long */
	return(false);
      }
      if (decimal_count == 0){
	*answer = *answer * 10 + (ch - '0');
      }
      else{			/* then we are in the fraction part */
	double fraction = (ch - '0');
	long internal_count;
	for(internal_count = 0; internal_count < decimal_count; 
	    internal_count++){
	  fraction = fraction / 10.0;
	}
	*answer = *answer + fraction;
	decimal_count++;
      }
    }
    else if(!isspace(ch)){
      /* then we have an error since it should be a digit or a space */
      return(false);
    }
    /* we do not have an digit */
    else if(state == DURING){
      ungetc(ch, file);
      return(true);		/* we are done */
    }
    /* otherwise we are still before the start */
  }
}

static Boolean issymbolchar _AP((long ch));

static 
Boolean issymbolchar(ch)
long ch;
/* reads a symbol from a file and put it in the string argument.
 * The string_size argument is used to make sure the string is not 
 * overflowed.
 */
{
  return(!( isspace(ch) || ch == ')' || ch == '(' || ch == EOF));
}

/* reads a symbol from a file */
long ReadSymbol(string,file,string_size)
char* string;
FILE* file;
long string_size;
{
  long ch;
  long state = BEFORE;
  long position = 0;
  
  while(TRUE){
    ch = getc(file);
    if((state == BEFORE) && (ch == ')'))
      return(END_OF_STRUCT_OR_LIST);
    if(issymbolchar((long)ch)){	/* we are in a symbol */
      if(state == BEFORE)
	state = DURING;
      string[position] = ch;
      position++;
      if(position >= string_size){
	string[string_size - 1] = '\0';
	return(FALSE);
      }
    }
    /* we do not have an symbol character. we are done */
    else if((state == DURING) || ch == EOF){
      if(ch != EOF) ungetc(ch, file);
      string[position] = '\0';
      return(TRUE);		/* we are done */
    }
    /* otherwise we are still before the start of the symbol */
  }
}

long ReadEndOfListOrStruct(file)
FILE* file;
{
  long ch;
  while(TRUE){
    ch = getc(file);
    if (EOF == ch) 
      return(FALSE);
    else if(')' == ch) 
      return(TRUE);
    else if(!isspace(ch)) 
      return(FALSE);
  }
}

/* reads a string from a file */
long ReadString(string,file,string_size)
char* string;
FILE* file;
long string_size;
{
  long ch;
  long state = BEFORE;
  long position = 0;
  string[0] = '\0';  /* initialize to nothing */
  
  while(TRUE){
    ch = getc(file);
    if((state == BEFORE) && (ch == '\"'))
      state = DURING;
    else if (EOF == ch){
      string[position] = '\0';
      return(FALSE);
    }
    else if ((state == BEFORE) && (ch == ')'))
      return(END_OF_STRUCT_OR_LIST);
    else if ((state == DURING) && (ch == '\\'))
      state = QUOTE; /* do nothing */
    else if ((state == DURING) && (ch == '"')){	
      string[position] = '\0';
      return(TRUE);
    }
    else if ((state == QUOTE) || (state == DURING)){
			if(state == QUOTE)
				state = DURING;
			string[position] = ch;
			position++;
			if(position >= string_size){
				string[string_size - 1] = '\0';
				return(FALSE);
			}
		}
		/* otherwise we are still before the start of the string */
	}
}

/* returns TRUE if it is the start of a struct
 * returns END_OF_STRUCT_OR_LIST if it is a ')'
 * returns FALSE if it is something unexpected
 */
long ReadStartOfStruct(name,file)
char* name;
FILE* file;
{
  long ch;
  long state = BEFORE;
	
  name[0] = '\0';
	
  while(TRUE){
    ch = getc(file);
    if((state == BEFORE) && (ch == '#'))
      state = HASH;
    if((state == BEFORE) && (ch == '('))
      state = DURING;
    else if((state == BEFORE) && (ch == ')'))
      return(END_OF_STRUCT_OR_LIST);
    else if((state == BEFORE) && !isspace(ch))
      return(FALSE);		/* we have a problem */
    else if(state == HASH){
      if (ch == 's')
	state = S;
      else{
	fprintf(stderr,"Expected an 's' but got an %c\n", ch);
	return(FALSE);
      }
    }
    else if(state == S){
      if (ch == '(')
	state = DURING;
      else{
	fprintf(stderr,"Expected an '(' but got an an %c\n",ch);
	return(FALSE);
      }
    }
    else if(state == DURING){
      return(ReadSymbol(name, file, MAX_SYMBOL_SIZE));
    }
  }
}

/* returns TRUE if it is the right start of a struct,
 * returns END_OF_STRUCT_OR_LIST if it is the end of a list,
 * returns FALSE if it is something weird
 */
long CheckStartOfStruct(name,file)
char* name;
FILE* file;
{
  char temp_string[MAX_SYMBOL_SIZE];
  long result = ReadStartOfStruct(temp_string, file);
  if(result == END_OF_STRUCT_OR_LIST)
    return(END_OF_STRUCT_OR_LIST);
  else if(result == FALSE)
    return(FALSE);
  else if(0 == strcmp(temp_string, name))
    return(TRUE);
  else 
    return(FALSE);
}

/* reads an any.  an any with no bytes allocated.  The right number of bytes
 * will be malloc'ed while reading
 */
long ReadAny(destination,file)
any* destination;
FILE* file;
{
  char temp_string[MAX_SYMBOL_SIZE];
	
  destination->size = 0; /* initialize so that if an error happens 
			    it does not blow up */
  if(FALSE == CheckStartOfStruct("any", file)){
    fprintf(stderr,"An 'any' structure was not read from the disk");
    return(FALSE);
  }
	
  while(TRUE){
    long check_result;
    check_result = ReadSymbol(temp_string, file, MAX_SYMBOL_SIZE);
    if(FALSE == check_result) 
      return(FALSE);
    if(END_OF_STRUCT_OR_LIST == check_result) 
      return(TRUE);
		
    if(0 == strcmp(temp_string, ":size")) {
      long	size;
      ReadLong(file,&size);
      destination->size = (unsigned long)size;
    }
    else if(0 == strcmp(temp_string, ":bytes")){
      long result;
      /* the size must have been read in by now */
      destination->bytes = (char*)s_malloc(destination->size);
      if(NULL == destination->bytes){
	fprintf(stderr,
		"Error on reading file. Malloc ran out of memory in an ANY");
	return(FALSE);
      }
      result = Read8BitArray(destination->bytes, file, destination->size);
      if(FALSE == result)
	return(FALSE);
    }
    else{
      fprintf(stderr,"Unknown keyword for ANY %s\n", temp_string);
      return(FALSE);
    }
  }
}

/* this does not need the length, but it will probably know it in all cases */
long Read8BitArray(destination,file,length)
char* destination;
FILE* file;
long length;
{
  /* arrays start with #( */
  long ch;
  long state = BEFORE;
  while(TRUE){
    ch = getc(file);
    if((state == BEFORE) && ((ch == '#') || (ch == '('))) {
      if (ch == '(') state = DURING;
      else state = HASH;
    }
    else if((state == BEFORE) && !isspace(ch)){
      fprintf(stderr,"error in reading array.  Expected # and got %c", ch);
      return(FALSE);
    }
    else if(state == HASH){
      if (ch == '(')
	state = DURING;
      else{
	fprintf(stderr,"Expected an '(' but got an %c\n", ch);
	return(FALSE);
      }
    }
    else if(state == DURING){
      long i;
      ungetc(ch, file);
      for(i = 0; i < length; i++){
	long value;
	if(ReadLong(file,&value) == false){ /* then it error'ed */
	  fprintf(stderr,"Error in reading a number from the file.");
	  return(FALSE);
	}
	if(value > 255){	/* then we have read a non-char */
	  fprintf(stderr,"Error in reading file.  Expected a byte in an ANY, but got %ld", value);
	  return(FALSE);
	}
	destination[i] = (char)value;
      }
      if(FALSE == ReadEndOfListOrStruct(file)){
	fprintf(stderr,"array was wrong length");
	return(FALSE);
      }
      return(TRUE);
    }
  }
}
			

Boolean
readAbsoluteTime(atime,file)
struct tm* atime;
FILE* file;
{
  if (CheckStartOfStruct("absolute-time",file) == FALSE)
    return(false);
		  
  while (true)
    { long result;
      long val;
      char temp_string[MAX_SYMBOL_SIZE + 1];
     
      result = ReadSymbol(temp_string,file,MAX_SYMBOL_SIZE);
     
      if (result == END_OF_STRUCT_OR_LIST)
	break;
      else if (result == false)
	return(false);
	   	       
      if (strcmp(temp_string,":second") == 0)
	{ if (ReadLong(file,&val) == false)
	    return(false);
	  atime->tm_sec = val;
	}

      else if (strcmp(temp_string,":minute") == 0)
	{ if (ReadLong(file,&val) == false)
	    return(false);
	  atime->tm_min = val;
	}

      else if (strcmp(temp_string,":hour") == 0)
	{ if (ReadLong(file,&val) == false)
	    return(false);
	  atime->tm_hour = val;
	}

      else if (strcmp(temp_string,":mday") == 0)
	{ if (ReadLong(file,&val) == false)
	    return(false);
	  atime->tm_mday = val;
	}

      else if (strcmp(temp_string,":month") == 0)
	{ if (ReadLong(file,&val) == false)
	    return(false);
	  atime->tm_mon = val;
	}

      else if (strcmp(temp_string,":year") == 0)
	{ if (ReadLong(file,&val) == false)
	    return(false);
	  atime->tm_year = val;
	}
      
      else
	SkipObject(file);

    }

  return(true);
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.