diff srf2fastq/io_lib-1.12.2/io_lib/expFileIO.c @ 0:d901c9f41a6a default tip

Migrated tool version 1.0.1 from old tool shed archive to new tool shed repository
author dawe
date Tue, 07 Jun 2011 17:48:05 -0400
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/srf2fastq/io_lib-1.12.2/io_lib/expFileIO.c	Tue Jun 07 17:48:05 2011 -0400
@@ -0,0 +1,1251 @@
+/*
+ * Copyright (c) Medical Research Council 1994. All rights reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * this copyright and notice appears in all copies.
+ *
+ * This file was written by James Bonfield, Simon Dear, Rodger Staden,
+ * as part of the Staden Package at the MRC Laboratory of Molecular
+ * Biology, Hills Road, Cambridge, CB2 2QH, United Kingdom.
+ *
+ * MRC disclaims all warranties with regard to this software.
+ */
+
+/*
+ * File: expFileIO.c
+ * Version:
+ *
+ * Description: Routines for reading and writing to experiment files.
+ *
+ * 1. Opening experiment files
+ * 2. Reading information from an experiment file
+ * 3. Appending to experiment files
+ * 4. Closing an opened experiment file
+ *
+ * Created:
+ * Updated:
+ *
+ */
+
+/*
+ * Tag format:
+ *
+ * 0        10 
+ * |----.----|-
+ * TG   TYPE S position..length
+ * TG        One or more comment lines starting at character position 10
+ * TG        Each line represents a line of tag.
+ * TG         Extra indentation is simply added to the comment.
+ *
+ * Where S is the strand, either "+", "-", or "=" (both).
+ * Eg:
+ *
+ * TG   COMM = 100..110
+ * TG        This comment contains
+ * TG          several lines.
+ *
+ * So the above is a COMMent tag on both strands from bases 100 to 110
+ * inclusive containing the annotation
+ * "This comment contains\n  several lines.\n"
+ *
+ * This is written using exp_put_str giving the multi line string:
+ * "COMM = 100..110\nThis comment contains\n  several lines."
+ *
+ * (ie the indentation is added by the experiment file format, not by the
+ *  calling routines. Similarly this indentation is stripped out again when
+ *  reading back.)
+ */
+
+
+#include <stdio.h>
+#include <string.h> /* IMPORT: strdup (hopefully!) */
+#include <ctype.h>
+
+/* 6/1/99 johnt - includes needed for Visual C++ */
+#ifdef _MSC_VER
+#  include <io.h>
+#  include <fcntl.h>
+#endif
+
+#include "io_lib/expFileIO.h"
+#include "io_lib/xalloc.h"
+#include "io_lib/misc.h"
+#include "io_lib/stdio_hack.h"
+
+/* Fixup for broken SunOS 4.x systems */
+#ifndef FOPEN_MAX
+#define FOPEN_MAX 20
+#endif
+
+static int exp_check_eid_read(Exp_info *e,int id);
+
+/*************************************************************
+ * Line types for experiment file
+ *************************************************************/
+
+char eflt_feature_ids[MAXIMUM_EFLTS][MAXIMUM_EFLT_LENGTH+1] = {
+    "CF", /*  0 cloning vector sequence file */
+    "CN", /*  1 clone name */
+    "CS", /*  2 cloning vector sequence present in sequence */
+    "CV", /*  3 cloning vector type */
+    "DR", /*  4 direction of read */
+    "DT", /*  5 date of experiment */
+    "EN", /*  6 experiment name */
+    "EX", /*  7 experimental notes */
+    "FM", /*  8 sequencing vector fragmentation method */
+    "LN", /*  9 local format trace file name */
+    "LT", /* 10 local format trace file type */
+    "MC", /* 11 machine on which experiment ran */
+    "MN", /* 12 machine generated trace file name */
+    "MT", /* 13 machine generated trace file type */
+    "OP", /* 14 operator */
+    "PN", /* 15 primer name */
+    "QR", /* 16 poor quality sequence present at right (3') end */
+    "SC", /* 17 sequencing vector cloning site */
+    "SF", /* 18 sequencing vector sequence file */
+    "SI", /* 19 sequencing vector insertion length */
+    "SL", /* 20 sequencing vector present at left (5') end */
+    "SP", /* 21 sequencing vector primer site (relative to cloning site) */
+    "SQ", /* 22 sequence */
+    "SR", /* 23 sequencing vector present at right (3') end */
+    "ST", /* 24 strands */
+    "SV", /* 25 sequencing vector type */
+    "TN", /* 26 template name */
+    "QL", /* 27 poor quality sequence present at left (5') end */
+    "PS", /* 28 processing status */
+    "CC", /* 29 comments */
+    "SS", /* 30 sequence to screen against */
+    /* added 27-May-93 */
+    "TG", /* 31 gel tag line */
+    "ID", /* 32 identifier */
+    /* added 24-Sep-93 */
+    "AQ", /* 33 average quality measure */
+    /* added 15-Oct-93 */
+    "PR", /* 34 primer type */
+    "LI", /* 35 subclone library (mtd) */
+    "LE", /* 36 subclone library entry (well) */
+    /* added 19-Apr-94 */
+    "TC", /* 37 contig tag line */
+    "AC", /* 38 accession number */
+    /* added 11-Nov-94 */
+    "BC", /* 39 base calling software */
+    "ON", /* 40 original base numbers (positions) */
+    "AV", /* 41 accuracy (quality) values */
+    "PC", /* 42 position in contig */
+    "SE", /* 43 sense, whether it is complemented */
+    /* added 5-4-95 */
+    "CL", /* 44 cloning vector left end*/
+    "CR", /* 45 cloning vector right end*/
+    "AP", /* 46 assembly position */
+    "CH", /* 47 special chemistry used (eg taq) */
+    "PD", /* 48 primer data - the sequence of a primer */
+    "WT", /* 49 wild type trace */
+    "NT", /* 50 note */
+    "GD", /* 51 Gap4 database file */
+    "WL", /* 52 wildtype trace left clip point */
+    "WR", /* 53 wildtype trace right clip point */
+    "FT", /* 54 EMBL format feature table */
+    "LG"  /* 55 LiGation: an amalgamation of LI and LE */
+};
+
+
+
+
+/*************************************************************
+ * Output/update lines
+ *************************************************************/
+
+static int exp_print_line_(FILE *fp, char *eflt, char *entry)
+/*
+ * Output an experiment file line
+ */
+{
+    return fprintf(fp,
+		   "%-5s%s\n",
+		   eflt,
+		   entry
+		   )  <  0;
+}
+
+int exp_print_line(FILE *fp, Exp_info *e, int eflt, int i)
+/*
+ * Output an experiment file line
+ */
+{
+    return exp_print_line_(fp,
+			   eflt_feature_ids[eflt], 
+			   arr(char *,e->entries[eflt],i)
+			   );
+}
+
+/*
+ * Outputs a multi-line experiment file line.
+ * Continuation lines are automatically added by adding 5 characters of extra
+ * indentation at the start of each continuation.
+ *
+ * returns -1 for failure, 0 for success.
+ */
+int exp_print_mline(FILE *fp, Exp_info *e, int eflt, int i) {
+    char *p, *c;
+
+    p = arr(char *, e->entries[eflt], i);
+
+    /* first line */
+    if (c = strchr(p, '\n'))
+	*c = '\0';
+    if (-1 == exp_print_line_(fp, eflt_feature_ids[eflt], p))
+	return -1;
+
+    while (c) {
+	*c = '\n';
+	p = c+1;
+	
+	if (c = strchr(p, '\n')) {
+	    *c = '\0';
+	}
+	
+	if (-1 == fprintf(fp, "%-10s%s\n", eflt_feature_ids[eflt], p))
+	    return -1;
+    }
+
+    return 0;
+}
+
+
+int exp_print_seq(FILE *fp, Exp_info *e, int eflt, int i)
+/*
+ * Output an experiment file multi line
+ */
+{
+    int j, l;
+    char *seq;
+    if (fprintf(fp,"%-5s",eflt_feature_ids[eflt])<0) return 1;
+
+    l = strlen(seq = arr(char *,e->entries[eflt],i));
+    for(j=0;j<l;j++) {
+	if (j%60==0) if ( fprintf(fp,"\n    ") < 0 ) return 1;
+	if (j%10==0) if ( fprintf(fp," ") < 0 ) return 1;
+	if ( fprintf(fp,"%c",seq[j]) < 0 ) return 1;
+    }
+    if ( fprintf(fp,"\n//\n") < 0 ) return 1;
+
+    return 0;
+}
+
+int exp_get_feature_index(char *e)
+{
+    int i;
+    for (i = 0; i < MAXIMUM_EFLTS; i++) {
+	if (eflt_feature_ids[i][0] == e[0] &&
+	    eflt_feature_ids[i][1] == e[1])
+	    return i;
+    }
+    
+    return -1;
+}
+
+
+/*************************************************************
+ * Utility routines
+ *************************************************************/
+
+/*
+ * Creates a string of 'range format' from the start and end points.
+ * The string (of form start..end) is also returned.
+ */
+char *exp_create_range(char *str, int start, int end) {
+    sprintf(str, "%d..%d", start, end);
+    return str;
+}
+
+/*
+ * Extracts the start and end points from a range string.
+ * Returns 0 for success and -1 for failure.
+ */
+int exp_extract_range(char *str, int *start, int *end) {
+    return sscanf(str, "%d..%d", start, end) != 2;
+}
+
+Exp_info *exp_create_info(void)
+/*
+ * Allocate space for new experiment file information
+ */
+{
+    Exp_info *new;
+    int i;
+    
+    new = (Exp_info *)xmalloc(sizeof(Exp_info));
+    if (new != NULL) {
+	for(i=0; i< MAXIMUM_EFLTS ; i++) {
+	    new->Nentries[i] = 0;
+	    new->entries[i] = ArrayCreate(sizeof(char *), 1/*one entry*/);
+	}
+	new->fp = NULL;
+    }
+    
+    return new;
+}
+
+
+void exp_destroy_info(Exp_info *e)
+/*
+ * Destroy experiment file information
+ */
+{
+    int i;
+    int j;
+    if (e != NULL_Exp_info) {
+	for (i = 0; i < MAXIMUM_EFLTS; i++) {
+	    Array a = e->entries[i];
+	    for(j=0;j<e->Nentries[i];j++)
+		if (arr(char *,a,j) != NULL) xfree(arr(char *,a,j));
+	    ArrayDestroy(a);
+	}
+	if (e->fp != NULL) fclose(e->fp);
+	xfree(e);
+    }
+}
+
+
+
+
+/*
+ * Read from file a sequence, discarding all white space til a // is
+ * encountered
+ */
+static char *exp_read_sequence(FILE *fp)
+{
+    char *seq = NULL;
+    size_t seq_len = 0, seq_alloc;
+    char line[EXP_FILE_LINE_LENGTH+1];
+    char *l;
+    static int valid_char[256], init = 0;
+
+    /* Initialise lookup tables for efficiency later on.*/
+    if (!init) {
+	int i;
+	for (i = 0; i < 256; i++) {
+	    if (i < 128 && !isspace(i) && !isdigit(i) && !iscntrl(i))
+		valid_char[i] = 1;
+	    else
+		valid_char[i] = 0;
+	}
+	init = 1;
+    }
+
+    /* Initialise memory */
+    seq_alloc = EXP_FILE_LINE_LENGTH * 8;
+    seq = (char *)xmalloc(seq_alloc);
+    if (NULL == seq)
+	return NULL;
+    seq[0] = '\0';
+
+    /* Reading line by line, until we get "//" */
+    l = fgets(line,EXP_FILE_LINE_LENGTH,fp);
+    while (l!= NULL && strncmp(l,"//",2)) {
+	char *a, *b;
+
+	/* make sure the seq buffer is large enough */
+	if (seq_len + EXP_FILE_LINE_LENGTH + 1 > seq_alloc) {
+	    seq_alloc *= 2;
+	    if (NULL == (seq = (char *)xrealloc(seq, seq_alloc)))
+		return NULL;
+	}
+
+	/* copy to seq, stripping spaces on the fly */
+	for(a=line, b = &seq[seq_len]; *a; a++)
+	    if (valid_char[(unsigned char)*a])
+		*b++ = *a;
+	*b = '\0';
+	seq_len = b-seq;
+
+	l = fgets(line,EXP_FILE_LINE_LENGTH,fp);
+    }
+
+    /* Shrink the allocated string to reduce memory usage */
+    seq = (char *)xrealloc(seq, seq_len + 1);
+    
+    return seq;
+}
+
+
+/*
+ * Converts the opos[] array into a char array.
+ * In doing so this shrinks the data size by using a .. notation.
+ * No check is made that buf is large enough. It is recommended that buf is
+ * allocated to 5*len which covers the worst case (for sequences less that
+ * 9999 bases long).
+ *
+ * Note that on older systems sprintf may return the first argument rather
+ * than the number of characters written.
+ * For this reason we have to do the counting ourselves.
+ */
+char *opos2str(int2 *opos, int len, char *buf) {
+    int i, st, f, dir = 0;
+    char *r = buf, *rs = buf;
+    
+    f = opos[st = 0];
+    for (i = 1; i < len; f=opos[i++]) {
+	if (dir == 0)
+	    if (opos[i] == f+1)
+		dir=1;
+	    else if (opos[i] == f-1)
+		dir=-1;
+
+	if (dir && opos[i] != f + dir) {
+	    if (st != i-1)
+		sprintf(buf, "%d..%d ", opos[st], opos[i-1]);
+	    else
+		sprintf(buf, "%d ", opos[st]);
+	    st = i;
+	    dir = 0;
+
+	    buf += strlen(buf);
+		
+	} else if (dir == 0) {
+	    sprintf(buf, "%d ", f);
+
+	    st = i;
+	    buf += strlen(buf);
+	}
+
+	if (buf - rs > 60) {
+	    *buf++ = '\n';
+	    *buf = '\0';
+	    rs = buf - 6;
+	}	
+    }
+    
+    if (st != i-1)
+	sprintf(buf, "%d..%d", opos[st], opos[i-1]);
+    else
+	sprintf(buf, "%d", opos[st]);
+    
+    return r;
+}
+
+
+
+/*
+ * Expands from the character string .. notation to the opos[] array, up to
+ * a maximum of len elements in opos[].
+ *
+ * Returns the length of the opos array.
+ */
+int str2opos(int2 *opos, int len, char *buf) {
+    /* int i, n1, n2, st, en, m, j = 0; */
+    int i, j = 0, st, en;
+    char *cp;
+
+    while (j < len && *buf) {
+	st = strtol(buf, &cp, 10);
+	if (buf == cp) {
+	    buf++;
+	    continue;
+	}
+	buf = cp;
+	if (buf[0] == '.' && buf[1] == '.') {
+	    en = strtol(buf += 2, &cp, 10);
+	    if (buf == cp) {
+		opos[j++] = st;
+		buf++;
+		continue;
+	    }
+	    buf = cp;
+
+	    if (en >= st)
+		for (i = st; i <= en && j < len; i++)
+		    opos[j++] = i;
+	    else
+		for (i = st; i >= en && j < len; i--)
+		    opos[j++] = i;
+	} else {
+	    opos[j++] = st;
+	}
+    }
+
+    return j;
+}
+
+
+/*
+ * Converts the accuracy value string (AV) to the confidence array up to
+ * a maximum of len elements in conf[].
+ *
+ * The AV string is of format:
+ * "x y z ..." where x, y and z are confidence values for the first three
+ * called bases. Or:
+ * "a,b,c,d e,f,g,h i,j,k,l ..." where the 4-tuples represent the four
+ * confidence values for each base.
+ *
+ * Returns: number of confidence values read, or -1 for error.
+ */
+int str2conf(int1 *conf, int len, char *buf) {
+    int ind = 0;
+
+    while (*buf && ind < len) {
+	char *new_buf;
+	int val1;
+
+	val1 = strtol(buf, &new_buf, 10);
+	if (new_buf == buf)
+	    break;
+
+	if (*new_buf == ',') {
+	    fprintf(stderr, "4-tuple system is currently unsupported\n");
+	    return -1;
+	}
+
+	conf[ind++] = val1;
+	buf = new_buf;
+    }
+
+    return ind;
+}
+
+
+/*
+ * Converts the confidence array to the accuracy value string (AV).
+ *
+ * Note no memory overrun checks are performed on buf. It is recommended
+ * that it is allocated to 4*len (worst case of "255 " for each base) plus.
+ * a couple of terminating newline and null plus another byte per 15 values
+ * to allow for the 60-char line length.
+ * For ease, allocating to 5*len+2 is more than sufficient.
+ *
+ * Returns the buf argument.
+ */
+char *conf2str(int1 *conf, int len, char *buf) {
+    int i;
+    char *ret = buf, *rs = buf;
+
+    for (i = 0; i < len; i++) {
+	sprintf(buf, "%d ", conf[i]);
+	buf += strlen(buf);
+
+	if (buf - rs > 60) {
+	    *buf++ = '\n';
+	    *buf = '\0';
+	    rs = buf - 6;
+	}
+    }
+
+    return ret;
+}
+
+/*************************************************************
+ * Main C interface routines
+ *************************************************************/
+
+
+/*
+ * Closes an experiment file (if open), but does not free it.
+ */
+void exp_close(Exp_info *e) {
+    if (e->fp) {
+	fclose(e->fp);
+	e->fp = NULL;
+    }
+}
+
+
+Exp_info *exp_read_info(char *file)
+/*
+ * Read in an experiment file and return handle
+ */
+{
+    Exp_info *e;
+    FILE *fp;
+    
+    /*
+     * open for read
+     */
+    if ((fp = fopen(file,"r"))==NULL) {
+	return NULL_Exp_info;
+    }
+
+    e = exp_fread_info(fp);
+    fclose(fp);
+
+    if (NULL_Exp_info == e) {
+	return NULL_Exp_info;
+    }	
+
+    /*
+     * reopen for appending
+     */
+    e->fp = fopen(file,"a");
+    
+    return e;
+    
+}
+
+
+/*
+ * Read in an experiment file and return handle
+ */
+Exp_info *exp_fread_info(FILE *fp)
+{
+    Exp_info *e;
+    char line[EXP_FILE_LINE_LENGTH+1];
+    char *aline;
+    int alloced_length = EXP_FILE_LINE_LENGTH+1;
+    int apos, len;
+    int last_entry = -1;
+    size_t entry_len = 0;
+    
+    e = exp_create_info();
+
+  
+    /*
+     * No longer has an effect due to mFILE already being loaded. Ifdef not
+     * triggered under mingw anyway.
+     */
+#ifdef _WIN32
+    /* 6/1/99 johnt - need to ensure text mode to translate \r\n to \n */
+    /* _setmode(fileno(fp),_O_TEXT); */
+    mfascii(fp);
+#endif
+    
+    /*
+     * open for read, set this temporarily in this function. Should be NULL
+     * when exiting as this isn't our file pointer to own, but the destroy
+     * function does attempt to automatically close it.
+     */
+    e->fp = fp;
+
+    if (NULL == (aline = (char *)xmalloc(alloced_length)))
+	return NULL;
+    
+    if (e != NULL_Exp_info) {
+	int at_end = 0;
+
+	for(;;) {
+	    char *c;
+	    int entry;
+
+	    /* Read into aline, joining and allocating as necessary */
+	    apos = 0;
+	    do {
+		if (fgets(line,EXP_FILE_LINE_LENGTH,e->fp) == NULL) {
+		    at_end = 1;
+		    break;
+		}
+
+		len = strlen(line);
+		if (apos + len >= alloced_length) {
+		    alloced_length *= 2;
+		    if (NULL == (aline = (char *)xrealloc(aline,
+							  alloced_length))) {
+			e->fp = NULL;
+			return NULL;
+		    }
+		}
+
+		strcpy(aline+apos, line);
+		apos += len;
+	    } while (line[len-1] != '\n');
+
+	    if (at_end)
+		break;
+
+	    /*
+	     * zero terminate first argument
+	     * set c to point to second argument
+	     *
+	     * FIXME: c should point to character 6 always. Indentation is
+	     * important when considering continuation lines.
+	     */
+	    for (c=aline;*c && !isspace(*c); c++) ;
+	    if (*c) {
+		*c++ = '\0';
+		for (;*c && isspace(*c); c++) ;
+	    }
+	    
+	    entry = exp_get_feature_index(aline);
+	    if (entry >= 0) {
+		/*
+		 * Tag lines may be split over multiple lines. If we have no
+		 * tag type then we append to the existing tag.
+		 */
+		if (entry == last_entry &&
+		    (int)(c-aline) >= 10/* continuation lines */
+		    && (entry == EFLT_TG || entry == EFLT_TC ||
+			entry == EFLT_ON || entry == EFLT_AV ||
+			entry == EFLT_NT || entry == EFLT_FT)) {
+		    char *en;
+		    size_t l1, l2;
+
+		    /*
+		     * Extend our current line by the appropriate amount
+		     */
+		    if( exp_check_eid_read(e,entry) )
+			return NULL;
+		    en = exp_get_entry(e,entry);
+		    l1 = entry_len;
+		    l2 = strlen(&aline[10]);
+
+		    if (NULL == (en = exp_get_entry(e, entry) =
+				 (char *)xrealloc(en, l1 + l2 + 1))) {
+			e->fp = NULL;
+			return NULL;
+		    }
+		    
+
+		    /*
+		     * Append the new line (without the \n char)
+		     */
+		    en[l1] = '\n';
+		    aline[l2+9] = '\0';
+		    strcpy(&en[l1+1], &aline[10]);
+
+		    entry_len += l2;
+		} else {
+		    /*
+		     * Increment number of entries for line type entry
+		     * This will force exp_get_entry() to return pointer to
+		     * next free element in array
+		     */
+		    (void)ArrayRef(e->entries[entry],e->Nentries[entry]++);
+		    
+		    if (entry == EFLT_SQ)
+			exp_get_entry(e,entry) = exp_read_sequence(e->fp);
+		    else {
+			char *eoln = strchr(c,'\n');
+			int i;
+
+			if (eoln!=NULL) *eoln='\0';
+
+			if (entry == EFLT_LT)
+			    for (i=3; isspace(c[i]) && i >= 0; c[i--]='\0');
+
+			exp_get_entry(e,entry) = (char *)strdup(c);
+			entry_len = strlen(c);
+		    }
+		}
+	    }
+
+	    last_entry = entry;
+	}
+    }
+
+    e->fp = NULL;
+    xfree(aline);
+    
+    return e;
+}
+
+static int exp_check_eid_read(Exp_info *e,int id)
+/*
+ * Check these are a valid combination and that
+ * an entry exists for read
+ */
+{
+    return (
+	    e == NULL ||
+	    id < 0 ||
+	    id >= MAXIMUM_EFLTS ||
+	    e->Nentries[id] == 0 ||
+	    eflt_feature_ids[id][0]=='\0'
+	    );
+}
+
+static int exp_check_eid_write(Exp_info *e,int id)
+/*
+ * Check these are a valid combination and that
+ * an entry exists for write
+ */
+{
+    return (e == NULL ||
+	    id < 0 ||
+	    id >= MAXIMUM_EFLTS ||
+	    e->fp == NULL ||
+	    eflt_feature_ids[id][0]=='\0');
+}
+
+
+
+
+
+
+int exp_get_int(Exp_info *e, int id, int *val)
+/*
+ * Get the integer for entry id
+ * returns:
+ *    0 - success
+ *    1 - no entry
+ */
+{
+    if ( exp_check_eid_read(e,id) ) return 1;
+    *val = atoi(exp_get_entry(e,id));
+    return 0;
+}
+
+
+int exp_get_rng(Exp_info *e, int id, int *from, int *to)
+/*
+ * Get the integer pair for entry id
+ * returns:
+ *    0 - success
+ *    1 - no entry
+ */
+{
+    if ( exp_check_eid_read(e,id) ) return 1;
+    (void)exp_extract_range(exp_get_entry(e,id), from, to);
+
+    return 0;
+}
+
+
+
+int exp_get_str(Exp_info *e, int id, char *s, f_implicit s_l)
+/*
+ * Get the string for entry id
+ * returns:
+ *    0 - success
+ *    1 - no entry
+ */
+{
+    if ( exp_check_eid_read(e,id) ) return 1;
+    strncpy(s,exp_get_entry(e,id),s_l);
+    
+    return 0;
+}
+
+
+static int exp_append_str(Exp_info *e, int id, char *s, int len)
+/*
+ * Append the string to experiment file for entry id
+ * returns:
+ *    0 - success
+ *    1 - no update
+ */
+{
+    (void)ArrayRef(e->entries[id],e->Nentries[id]++);
+    exp_get_entry(e,id) = (char *)xmalloc(len+1);
+    strncpy(exp_get_entry(e,id), s, len);
+    exp_get_entry(e,id)[len] = '\0';
+    
+    if ( id == EFLT_SQ )
+	return exp_print_seq(e->fp,e,id,e->Nentries[id]-1);
+    else if (id == EFLT_TG || id == EFLT_TC ||
+	     id == EFLT_ON || id == EFLT_AV ||
+	     id == EFLT_NT || id == EFLT_FT)
+	return exp_print_mline(e->fp,e,id,e->Nentries[id]-1);
+    else
+	return exp_print_line(e->fp,e,id,e->Nentries[id]-1);
+}
+
+
+int exp_put_int(Exp_info *e, int id, int *val)
+/*
+ * Append the integer for entry id to the experiment file
+ * returns:
+ *    0 - success
+ *    1 - no update
+ */
+{
+    char buf[EXP_FILE_LINE_LENGTH];
+    if ( exp_check_eid_write(e,id) ) return 1;
+    sprintf(buf,"%d",*val);
+    return exp_append_str(e,id,buf,strlen(buf));
+}
+
+
+int exp_put_rng(Exp_info *e, int id, int *from, int *to)
+/*
+ * Append the integer pair for entry id to the experiment file
+ * returns:
+ *    0 - success
+ *    1 - no update
+ */
+{
+    char buf[EXP_FILE_LINE_LENGTH];
+    if ( exp_check_eid_write(e,id) ) return 1;
+
+    (void )exp_create_range(buf, *from, *to);
+
+    return exp_append_str(e,id,buf,strlen(buf));
+}
+
+
+
+int exp_put_str(Exp_info *e, int id, char *s, f_implicit s_l)
+/*
+ * Append the string for entry id to the experiment file
+ * returns:
+ *    0 - success
+ *    1 - no update
+ */
+{
+    if ( exp_check_eid_write(e,id) ) return 1;
+    return exp_append_str(e,id,s,s_l);
+}
+
+
+/*************************************************************
+ * FORTRAN INTERFACE
+ *************************************************************/
+
+
+
+static int init_done = 0;
+static int NHandles = 0;
+static Exp_info **Handles = NULL;
+
+static int initialise(void)
+{
+    int i;
+    
+    if (init_done) return 0;
+    init_done++;
+    
+    NHandles = FOPEN_MAX;
+    
+    if ( (Handles = (Exp_info **)xmalloc(sizeof(Exp_info *) * NHandles)) == NULL) {
+	NHandles = 0;
+	return 1;
+    }
+    
+    for (i=0; i<NHandles; i++) Handles[i] = NULL;
+    
+    return 0;
+}
+
+
+static int get_free_handle(void)
+/*
+ * find a free entry in the Exp array
+ * returns -1 if there is none
+ */
+{
+    int i;
+    
+    (void) initialise();
+    
+    if (!NHandles) return -1; /* no slots! */
+    for (i=0; i<NHandles && Handles[i]!=NULL; i++) ;
+    return (i==NHandles)?-1:i;
+}
+
+
+static int check_handle(f_int *handle)
+{
+    return (handle == NULL ||
+	    (int) (*handle) <= 0 ||
+	    (int) (*handle) > NHandles);
+}
+
+
+
+f_int expopn_(char *fn, f_implicit fn_l)
+/*
+ * FORTRAN interface to exp_open_file()
+ */
+{
+    char cfn[1025];
+    int handle;
+    
+    if ( (handle = get_free_handle()) >= 0 ) {
+	f2cstr(fn,fn_l,cfn,1024);
+	Handles[handle] = exp_read_info(cfn);
+    }
+    
+    return (f_int) (handle+1);
+}
+
+
+
+f_proc_ret expkil_(f_int *handle)
+/*
+ * FORTRAN interface to exp_destroy_info
+ */
+{
+    Exp_info *e;
+    if ( check_handle(handle) ) f_proc_return();
+    e = (Exp_info *) Handles[(int)(*handle)-1];
+    
+    exp_destroy_info(e);
+    
+    Handles[(int)(*handle)-1] = NULL;
+    *handle = 0;
+    
+    f_proc_return();
+}
+
+f_int expri_(f_int *handle, f_int *id, f_int *val)
+/*
+ * FORTRAN interface to exp_get_int
+ */
+{
+    Exp_info *e;
+    if ( check_handle(handle) ) return 1;
+    e = (Exp_info *) Handles[(int)(*handle)-1];
+    
+    return exp_get_int(e, (int)*id, (int *)val);
+}
+
+
+f_int exprr_(f_int *handle, f_int *id, f_int *from, f_int *to)
+/*
+ * FORTRAN interface to exp_get_rng
+ */
+{
+    Exp_info *e;
+    if ( check_handle(handle) ) return 1;
+    e = (Exp_info *) Handles[(int)(*handle)-1];
+    
+    return exp_get_rng(e,(int)*id,(int *)from,(int *)to);
+    
+}
+
+/* ARGSUSED */
+f_int exprsa_(f_int *handle, f_int *id, char *s, f_int *max_len, f_implicit s_l)
+/*
+ * FORTRAN interface to exp_get_str workalike
+ * NOTE: for use with FORTRAN CHARACTER arrays instead CHARACTER strings
+ */
+{
+    Exp_info *e;
+    if ( check_handle(handle) ) return 1;
+    e = (Exp_info *) Handles[(int)(*handle)-1];
+    
+    if ( exp_check_eid_read(e,*id) ) return 1;
+    c2fstr(exp_get_entry(e,*id),(int)*max_len,s,(int)*max_len);
+    return 0;
+}
+
+
+f_int exprs_(f_int *handle, f_int *id, char *s, f_implicit s_l)
+/*
+ * FORTRAN interface to exp_get_str workalike
+ * NOTE: for use with FORTRAN CHARACTER strings instead CHARACTER arrays
+ */
+{
+    Exp_info *e;
+    if ( check_handle(handle) ) return 1;
+    e = (Exp_info *) Handles[(int)(*handle)-1];
+    
+    if ( exp_check_eid_read(e,*id) ) return 1;
+    c2fstr(exp_get_entry(e,*id),s_l,s,s_l);
+    return 0;
+}
+
+
+f_int expwi_(f_int *handle, f_int *id, f_int *val)
+/*
+ * FORTRAN interface to exp_put_int
+ */
+{
+    Exp_info *e;
+    if ( check_handle(handle) ) return 1;
+    e = (Exp_info *) Handles[(int)(*handle)-1];
+    
+    return exp_put_int(e, (int)*id, (int *)val);
+}
+
+
+f_int expwr_(f_int *handle, f_int *id, f_int *from, f_int *to)
+/*
+ * FORTRAN interface to exp_put_rng
+ */
+{
+    Exp_info *e;
+    if ( check_handle(handle) ) return 1;
+    e = (Exp_info *) Handles[(int)(*handle)-1];
+    
+    return exp_put_rng(e, (int)*id, (int *)from, (int *)to);
+}
+
+
+/* ARGSUSED */
+f_int expwsa_(f_int *handle, f_int *id, char *s, f_int *max_len, f_implicit s_l)
+/*
+ * FORTRAN interface to exp_put_str workalike
+ * NOTE: for use with FORTRAN CHARACTER arrays instead CHARACTER strings
+ */
+{
+    Exp_info *e;
+    char buf[EXP_FILE_LINE_LENGTH];
+    if ( check_handle(handle) ) return 1;
+    e = (Exp_info *) Handles[(int)(*handle)-1];
+    
+    
+    if ( exp_check_eid_write(e,*id) ) return 1;
+    /* don't allow multi-line entries to be written */
+    if (*id == EFLT_SQ ) return 1;
+    f2cstr(s,(int)*max_len,buf,sizeof(buf));
+    return exp_append_str(e,*id,buf,strlen(buf));
+    
+}
+
+f_int expws_(f_int *handle, f_int *id, char *s, f_implicit s_l)
+/*
+ * FORTRAN interface to exp_put_str workalike
+ * NOTE: for use with FORTRAN CHARACTER strings instead CHARACTER arrays
+ */
+{
+    char buf[EXP_FILE_LINE_LENGTH];
+    Exp_info *e;
+    if ( check_handle(handle) ) return 1;
+    e = (Exp_info *) Handles[(int)(*handle)-1];
+    
+    
+    if ( exp_check_eid_write(e,*id) ) return 1;
+    /* don't allow multi-line entries to be written */
+    if (*id == EFLT_SQ ) return 1;
+    f2cstr(s,s_l,buf,sizeof(buf));
+    return exp_append_str(e,*id,buf,s_l);
+}
+
+/*
+ * FORTRAN interface to exp_create_range()
+ */
+void expcr_(char *str, f_int *start, f_int *end, f_implicit str_l) {
+    exp_create_range(str, *start, *end);
+    c2fstr(str, str_l, str, str_l);
+
+    f_proc_return();
+}
+
+/*
+ * FORTRAN interface to exp_extract_range()
+ */
+/* ARGSUSED */
+f_int exper_(char *str, f_int *start, f_int *end, f_implicit str_l) {
+    return exp_extract_range(str, start, end);
+}
+
+
+
+
+/*************************************************************
+ * Go for it!
+ *************************************************************/
+
+static void print_line(FILE *fp, Exp_info *e, int eflt, int all)
+{
+    if (all) {
+	int i;
+	for(i=0;i<e->Nentries[eflt];i++) exp_print_line(fp,e,eflt,i);
+    } else if (e->Nentries[eflt] > 0) {
+	exp_print_line(fp,e,eflt,e->Nentries[eflt]-1);
+    }
+}
+
+
+static void print_mline(FILE *fp, Exp_info *e, int eflt, int all)
+{
+    if (all) {
+	int i;
+	for(i=0;i<e->Nentries[eflt];i++) exp_print_mline(fp,e,eflt,i);
+    } else if (e->Nentries[eflt] > 0) {
+	exp_print_mline(fp,e,eflt,e->Nentries[eflt]-1);
+    }
+}
+
+
+
+static void print_seq(FILE *fp, Exp_info *e, int eflt)
+{
+    if (e->Nentries[eflt] > 0)
+        exp_print_seq(fp,e,eflt,e->Nentries[eflt]-1);
+}
+
+
+
+
+void exp_print_file(FILE *fp, Exp_info *e)
+{
+    print_line(fp,e,EFLT_ID, 0);
+    print_line(fp,e,EFLT_AC, 0);
+    print_line(fp,e,EFLT_EN, 0);
+
+    print_line(fp,e,EFLT_CC, 1);
+    print_line(fp,e,EFLT_EX, 1);
+    print_line(fp,e,EFLT_PS, 1);
+
+    print_line(fp,e,EFLT_LN, 0);
+    print_line(fp,e,EFLT_LT, 0);
+
+    print_line(fp,e,EFLT_CF, 0);
+    print_line(fp,e,EFLT_CV, 0);
+    print_line(fp,e,EFLT_CS, 0);
+    print_line(fp,e,EFLT_CL, 0);
+    print_line(fp,e,EFLT_CR, 0);
+
+    print_line(fp,e,EFLT_SF, 0);
+    print_line(fp,e,EFLT_SV, 0);
+    print_line(fp,e,EFLT_SI, 0);
+    print_line(fp,e,EFLT_SC, 0);
+    print_line(fp,e,EFLT_SP, 0);
+    print_line(fp,e,EFLT_PD, 0);
+    print_line(fp,e,EFLT_FM, 0);
+    print_line(fp,e,EFLT_SL, 0);
+    print_line(fp,e,EFLT_SR, 0);
+
+    print_line(fp,e,EFLT_QL, 0);
+    print_line(fp,e,EFLT_QR, 0);
+
+    print_mline(fp,e,EFLT_TG,1);
+    print_mline(fp,e,EFLT_TC,1);
+    print_mline(fp,e,EFLT_NT,1);
+
+    print_line(fp,e,EFLT_CN, 0);
+    print_line(fp,e,EFLT_TN, 0);
+    print_line(fp,e,EFLT_PN, 0);
+    print_line(fp,e,EFLT_PR, 0);
+    print_line(fp,e,EFLT_LI, 0);
+    print_line(fp,e,EFLT_LE, 0);
+    print_line(fp,e,EFLT_CH, 0);
+
+    print_mline(fp,e,EFLT_ON,0);
+    print_line(fp,e,EFLT_AQ, 0);
+    print_mline(fp,e,EFLT_AV,0);
+
+    print_line(fp,e,EFLT_DR, 0);
+    print_line(fp,e,EFLT_SE, 0);
+    print_line(fp,e,EFLT_PC, 0);
+    print_line(fp,e,EFLT_AP, 0);
+    print_line(fp,e,EFLT_ST, 0);
+
+    print_line(fp,e,EFLT_DT, 0);
+    print_line(fp,e,EFLT_MC, 0);
+    print_line(fp,e,EFLT_MN, 0);
+    print_line(fp,e,EFLT_MT, 0);
+    print_line(fp,e,EFLT_OP, 1);
+    print_line(fp,e,EFLT_BC, 0);
+    print_line(fp,e,EFLT_SS, 0);
+
+    print_line(fp,e,EFLT_WT, 0);
+    print_line(fp,e,EFLT_WL, 0);
+    print_line(fp,e,EFLT_WR, 0);
+
+    print_mline(fp,e,EFLT_FT,1);
+
+    print_seq (fp,e,EFLT_SQ);
+}
+
+
+/*
+ * Allocate an set a new experiment file entry
+ */
+char *exp_set_entry(Exp_info *e, int eflt, char *str) {
+    char *s;
+    size_t l;
+
+    if (NULL == ArrayRef(e->entries[eflt], e->Nentries[eflt]))
+	return NULL;
+    else
+	e->Nentries[eflt]++;
+
+    l = strlen(str);
+    if (NULL == (s = exp_get_entry(e, eflt) = (char *)xmalloc(l+1))) {
+	e->Nentries[eflt]--;
+	return NULL;
+    }
+    strcpy(s, str);
+
+    return s;
+}