1 /*
2  * $Id: prefix.i,v 1.1 2005-09-18 22:06:04 dhmunro Exp $
3  * Functions to read and write arrays of real numbers tagged by
4  * a prefix at the beginning of each line.
5  */
6 /* Copyright (c) 2005, The Regents of the University of California.
7  * All rights reserved.
8  * This file is part of yorick (http://yorick.sourceforge.net).
9  * Read the accompanying LICENSE file for details.
10  */
11 
12 func prefix_find(f, prefix, delimit=)
13 /* DOCUMENT prefix_find(f, prefix)
14      scan to the first line of text file F which begins with the
15      blank delimited prefix PREFIX.  (You may specify other delimiters
16      by giving prefix_find a DELIMIT keyword, whose value will be
17      passed to the strtok function.)  The return value is 1 if such
18      a line was found, 0 if not.  In the first case, F will be positioned
19      to reread the prefixed line; in the second case, F will be at the
20      end-of-file.
21 
22    SEE ALSO: prefix_read, prefix_write, strtok
23  */
24 {
25   if (is_void(delimit)) delimit= " \t\n";
26   found= 0;
27   while (line= rdline(f)) {
28     if (strtok(line,delimit)(1)==prefix) {
29       found= 1;
30       backup, f;
31       break;
32     }
33   }
34   return found;
35 }
36 
37 func prefix_read(f, prefix, delimit=, comment=)
38 /* DOCUMENT value_array= prefix_read(f, prefix)
39      reads lines of text file F which begin with the blank delimited
40      prefix PREFIX.  (You may specify other delimiters by giving
41      prefix_read a DELIMIT keyword, whose value will be passed to the
42      strtok function.)  Stops when a line not beginning with that
43      prefix is encountered.  You may also supply a COMMENT keyword,
44      which should be a function accepting a string argument and
45      returning 0 to indicate that the line is not a comment line,
46      and 1 to indicate that it is a comment.  By default, blank lines
47      and lines beginning with "#" are taken as comments and skipped.
48 
49      The returned VALUE_ARRAY is [] if no PREFIX lines were found,
50      and an array of type double and length equal to the total number
51      of numbers Ni:
52 
53        prefix N1 N2 N3 N4
54        prefix N5 N6
55        prefix ... Nn
56 
57    SEE ALSO: prefix_find, prefix_write, prefix_comment, strtok
58  */
59 {
60   if (is_void(delimit)) delimit= " \t\n";
61   if (is_void(comment)) comment= prefix_comment;
62   n= l= 0;
63   values= array(pointer, 100);
64   target= array(0.0, 256);   /* max number of values per line */
65   while (line= rdline(f)) {
66     tok= strtok(line,delimit);
67     if (tok(1)==prefix) {
68       nn= sread(prefix_unD(tok(2)), target);
69       if (nn>0) {
70         n+= nn;
71         if (l==numberof(values)) grow, values, array(pointer, 100);
72         values(++l)= &target(1:nn);
73       }
74     } else if (!comment(line)) {
75       backup, f;
76       break;
77     }
78   }
79   if (!n) return [];
80   result= array(0.0, n);
81   n= 0;
82   for (i=1 ; i<=l ; i++) {
83     target= *values(i);
84     nn= n+numberof(target);
85     result(n+1:nn)= target;
86     n= nn;
87   }
88   return result;
89 }
90 
prefix_comment(line)91 func prefix_comment(line)
92 /* DOCUMENT prefix_comment(line)
93      the default comment detector function for prefix_read, makes
94      blank lines and any line beginning with "#" as its first non-blank
95      character a comment.
96  */
97 {
98   tok= strtok(line)(1);
99   return (strlen(tok)<1 || strpart(tok,1:1)=="#");
100 }
101 
prefix_unD(text)102 func prefix_unD(text)
103 {
104   if (text) {
105     c= *pointer(text);
106     d= where(c=='D' | c=='d');
107     if (numberof(d)) {
108       c(d)= 'e';
109       text= string(&c);
110     }
111   }
112   return text;
113 }
114 
115 func prefix_write(f, prefix, values, ndigits=, width=)
116 /* DOCUMENT prefix_read, f, prefix, value_array
117      writes lines of text file F which begin with the prefix PREFIX:
118 
119        prefix N1 N2 N3 N4 N5
120        prefix N6 N7 N8 N9 N10
121        prefix N11 N12
122 
123      The format is %14.6e by default, but you can adjust the ".6" by
124      specifying an NDIGITS keyword (6 is the default).
125 
126      Yorick will put as many numbers as fit within 79 characters by
127      default, and each successive line begins with PREFIX.  You can
128      change this default line width by specifying a WIDTH keyword
129      (default 79).
130 
131    SEE ALSO: prefix_find, prefix_read
132  */
133 {
134   if (is_void(width)) width= 79;
135   if (is_void(ndigits)) ndigits= 6;
136   else if (ndigits>24) ndigits= 24;
137   else if (ndigits<0) ndigits= 0;
138   ntot= 8+ndigits;
139   format= swrite(format="%%%ld.%lde",ntot,ndigits);
140   l= (width-strlen(prefix))/ntot;
141   if (l<1) l= 1;
142   n= numberof(values);
143   nlines= n/l;
144   j= n%l;
145   text= array(prefix, (j? nlines+1 : nlines));
146   for (i=1 ; i<=l ; i++) {
147     /* format the text one column at a time --
148        some columns may be one shorter than others */
149     if (i>n) break;
150     column= swrite(format=format, values(i:0:l));
151     if (j && i>j) text(1:nlines)+= column;
152     else text+= column;
153   }
154   write, f, format="%s\n", text;
155 }
156