Initial release of Maemo 5 port of gnuplot
[gnuplot] / src / binary.c
1 #ifndef lint
2 static char *RCSid() { return RCSid("$Id: binary.c,v 1.12.2.1 2007/10/02 18:20:30 sfeam Exp $"); }
3 #endif
4
5 /*
6  * The addition of gnubin and binary, along with a small patch
7  * to command.c, will permit gnuplot to plot binary files.
8  * gnubin  - contains the code that relies on gnuplot include files
9  *                     and other definitions
10  * binary      - contains those things that are independent of those
11  *                     definitions and files
12  *
13  * With these routines, hidden line removal of your binary data is possible!
14  *
15  * Last update:  3/29/92 memory allocation bugs fixed. jvdwoude@hut.nl
16  *               3/09/92 spelling errors, general cleanup, use alloc with no
17  *                       nasty fatal errors
18  *               3/03/92 for Gnuplot 3.24.
19  * Created from code for written by RKC for gnuplot 2.0b.
20  *
21  * Copyright (c) 1991,1992 Robert K. Cunningham, MIT Lincoln Laboratory
22  *
23  */
24
25 /* NOTE: a significant fraction of these routines is not called from
26  * anywhere in gnuplot.  They're provided as a utility library for
27  * people wanting to write binary files usable by gnuplot, as the
28  * bf_test.c demo does it. */
29
30 #include "binary.h"
31
32 #include "alloc.h"
33 #include "util.h"
34
35 /* This routine scans the first block of the file to see if the file
36  * is a binary file.  A file is considered binary if 10% of the
37  * characters in it are not in the ascii character set. (values <
38  * 128), or if a NUL is found.  I hope this doesn't break when used on
39  * the bizzare PC's. */
40 int
41 is_binary_file(FILE *fp)
42 {
43     int i, len;
44     int odd;            /* Contains a count of the odd characters */
45     long where;
46     unsigned char *c;
47     unsigned char buffer[512];
48
49     if ((where = ftell(fp)) == -1) {    /* Find out where we start */
50         fprintf(stderr, "Notice: Assuming unseekable data is not binary\n");
51         return (FALSE);
52     } else {
53         rewind(fp);
54
55         len = fread(buffer, sizeof(char), 512, fp);
56         if (len <= 0)           /* Empty file is declared ascii */
57             return (FALSE);
58
59         c = buffer;
60
61         /* now scan buffer to look for odd characters */
62         odd = 0;
63         for (i = 0; i < len; i++, c++) {
64             if (!*c) {          /* NUL _never_ allowed in text */
65                 odd += len;
66                 break;
67             } else if ((*c & 128) ||    /* Meta-characters--we hope it's not formatting */
68                        (*c == 127) ||   /* DEL */
69                        (*c < 32 &&
70                         *c != '\n' && *c != '\r' && *c != '\b' &&
71                         *c != '\t' && *c != '\f' && *c != 27 /*ESC */ ))
72                 odd++;
73         }
74
75         fseek(fp, where, 0);    /* Go back to where we started */
76
77         if (odd * 10 > len)     /* allow 10% of the characters to be odd */
78             return (TRUE);
79         else
80             return (FALSE);
81     }
82 }
83
84
85 /*========================= I/O Routines ================================
86   These may be useful for situations other than just gnuplot.  Note that I
87   have included the reading _and_ the writing routines, so others can create
88   the file as well as read the file.
89 */
90
91 #define START_ROWS 100          /* Each of these must be at least 1 */
92 #define ADD_ROWS 50
93
94
95 /* This function reads a matrix from a stream
96  *
97  * This routine never returns anything other than vectors and arrays
98  * that range from 0 to some number. */
99 int
100 fread_matrix(
101     FILE *fin,
102     float GPFAR * GPFAR * GPFAR * ret_matrix,
103     int *nr, int *nc,
104     float GPFAR * GPFAR * row_title,
105     float GPFAR * GPFAR * column_title)
106 {
107     float GPFAR * GPFAR * m, GPFAR * rt, GPFAR * ct;
108     int num_rows = START_ROWS;
109     size_t num_cols;
110     int current_row = 0;
111     float GPFAR * GPFAR * temp_array;
112     float fdummy;
113
114     if (fread(&fdummy, sizeof(fdummy), 1, fin) != 1)
115         return FALSE;
116
117     num_cols = (size_t) fdummy;
118
119     /* Choose a reasonable number of rows, allocate space for it and
120      * continue until this space runs out, then extend the matrix as
121      * necessary. */
122     ct = alloc_vector(0, num_cols - 1);
123     fread(ct, sizeof(*ct), num_cols, fin);
124
125     rt = alloc_vector(0, num_rows - 1);
126     m = matrix(0, num_rows - 1, 0, num_cols - 1);
127
128     while (fread(&rt[current_row], sizeof(rt[current_row]), 1, fin) == 1) {
129         /* We've got another row */
130         if (fread(m[current_row], sizeof(*(m[current_row])), num_cols, fin) != num_cols)
131             return (FALSE);     /* Not a True matrix */
132
133         current_row++;
134         if (current_row >= num_rows) {  /* We've got to make a bigger rowsize */
135             temp_array = extend_matrix(m, 0, num_rows - 1, 0, num_cols - 1,
136                                        num_rows + ADD_ROWS - 1, num_cols - 1);
137             rt = extend_vector(rt, 0, num_rows + ADD_ROWS - 1);
138
139             num_rows += ADD_ROWS;
140             m = temp_array;
141         }
142     }
143     /*  finally we force the matrix to be the correct row size */
144     /*  bug fixed. procedure called with incorrect 6th argument.
145      *   jvdwoude@hut.nl */
146     temp_array = retract_matrix(m, 0, num_rows - 1, 0, num_cols - 1, current_row - 1, num_cols - 1);
147     /* Now save the things that change */
148     *ret_matrix = temp_array;
149     *row_title = retract_vector(rt, 0, current_row - 1);
150     *column_title = ct;
151     *nr = current_row;          /* Really the total number of rows */
152     *nc = num_cols;
153     return (TRUE);
154 }
155
156 /* This writes a matrix to a stream
157  *
158  * Note that our ranges are inclusive ranges--and we can specify
159  * subsets.  This behaves similarly to the xrange and yrange operators
160  * in gnuplot that we all are familiar with.
161  */
162 int
163 fwrite_matrix(
164     FILE *fout,
165     float GPFAR * GPFAR *m,
166     int nrl, int nrh, int ncl, int nch,
167     float GPFAR *row_title,
168     float GPFAR *column_title)
169 {
170     int j;
171     float length;
172     int col_length;
173     int status;
174     float GPFAR *title = NULL;
175
176     length = (float) (col_length = nch - ncl + 1);
177
178     if ((status = fwrite((char *) &length, sizeof(float), 1, fout)) != 1) {
179         fprintf(stderr, "fwrite 1 returned %d\n", status);
180         return (FALSE);
181     }
182     if (!column_title) {
183         column_title = title = alloc_vector(ncl, nch);
184         for (j = ncl; j <= nch; j++)
185             title[j] = (float) j;
186     }
187     fwrite((char *) column_title, sizeof(float), col_length, fout);
188     if (title) {
189         free_vector(title, ncl);
190         title = NULL;
191     }
192     if (!row_title) {
193         row_title = title = alloc_vector(nrl, nrh);
194         for (j = nrl; j <= nrh; j++)
195             title[j] = (float) j;
196     }
197     for (j = nrl; j <= nrh; j++) {
198         fwrite((char *) &row_title[j], sizeof(float), 1, fout);
199         fwrite((char *) (m[j] + ncl), sizeof(float), col_length, fout);
200     }
201     if (title)
202         free_vector(title, nrl);
203
204     return (TRUE);
205 }
206
207 /*===================== Support routines ==============================*/
208
209 /* ******************************* VECTOR *******************************
210  *       The following routines interact with vectors.
211  *
212  *   If there is an error we don't really return - int_error breaks us out.
213  *
214  *   This subroutine based on a subroutine listed in "Numerical Recipies in C",
215  *   by Press, Flannery, Teukoilsky and Vetterling (1988).
216  *
217  */
218 float GPFAR *
219 alloc_vector(int nl, int nh)
220 {
221     float GPFAR *vec;
222
223     if (! (vec = gp_alloc((nh - nl + 1) * sizeof(float), NULL))) {
224         int_error(NO_CARET, "not enough memory to create vector");
225         return NULL;            /* Not reached */
226     }
227     return (vec - nl);
228 }
229
230
231 /*
232  *  Free a vector allocated above
233  *
234  *   This subroutine based on a subroutine listed in "Numerical Recipies in C",
235  *   by Press, Flannery, Teukoilsky and Vetterling (1988).
236  *
237  */
238 void
239 free_vector(float GPFAR *vec, int nl)
240 {
241     free(vec + nl);
242 }
243
244 /************ Routines to modify the length of a vector ****************/
245 float GPFAR *
246 extend_vector(float GPFAR *vec, int old_nl, int new_nh)
247 {
248     float GPFAR *new_v = gp_realloc((vec + old_nl),
249                                     (new_nh - old_nl + 1) * sizeof(new_v[0]),
250                                     "extend/retract vector");
251     return new_v - old_nl;
252 }
253
254 float GPFAR *
255 retract_vector(float GPFAR *v, int old_nl, int new_nh)
256 {
257     return extend_vector(v, old_nl, new_nh);
258 }
259
260
261 /* **************************** MATRIX ************************
262  *
263  *        The following routines work with matricies
264  *
265  *       I always get confused with this, so here I write it down:
266  *                        for nrl<= nri <=nrh and
267  *                        for ncl<= ncj <=nch
268  *
269  *   This matrix is accessed as:
270  *
271  *     matrix[nri][ncj];
272  *     where nri is the offset to the pointer to a vector where the
273  *     ncjth element lies.
274  *
275  *   If there is an error we don't really return - int_error breaks us out.
276  *
277  *   This subroutine based on a subroutine listed in "Numerical Recipies in C",
278  *   by Press, Flannery, Teukoilsky and Vetterling (1988).
279  *
280  */
281 float GPFAR * GPFAR *
282 matrix(int nrl, int nrh, int ncl, int nch)
283 {
284     int i;
285     float GPFAR * GPFAR * m;
286
287     m = gp_alloc((nrh - nrl + 1) * sizeof(m[0]), "matrix rows");
288     m -= nrl;
289
290     for (i = nrl; i <= nrh; i++) {
291         if (!(m[i] = gp_alloc((nch - ncl + 1) * sizeof(m[i][0]), NULL))) {
292             free_matrix(m, nrl, i - 1, ncl);
293             int_error(NO_CARET, "not enough memory to create matrix");
294             return NULL;
295         }
296         m[i] -= ncl;
297     }
298     return m;
299 }
300
301 /*
302  * Free a matrix allocated above
303  *
304  *
305  *   This subroutine based on a subroutine listed in "Numerical Recipies in C",
306  *   by Press, Flannery, Teukoilsky and Vetterling (1988).
307  *
308  */
309 void
310 free_matrix(
311     float GPFAR * GPFAR * m,
312     int nrl, int nrh, int ncl)
313 {
314     int i;
315
316     for (i = nrl; i <= nrh; i++)
317         free(m[i] + ncl);
318     free(m + nrl);
319 }
320
321 /* This routine takes a sub matrix and extends the number of rows and
322  * columns for a new matrix */
323 float GPFAR * GPFAR *
324 extend_matrix(
325     float GPFAR * GPFAR * a,
326     int nrl, int nrh, int ncl, int nch,
327     int srh, int sch)
328 {
329     int i;
330     float GPFAR * GPFAR * m;
331
332     m = gp_realloc(a + nrl, (srh - nrl + 1) * sizeof(m[0]),
333                     "extend matrix");
334     m -= nrl;
335
336     if (sch != nch) {
337         for (i = nrl; i <= nrh; i++) {  /* Copy and extend rows */
338             if (!(m[i] = extend_vector(m[i], ncl, sch))) {
339                 free_matrix(m, nrl, nrh, ncl);
340                 int_error(NO_CARET, "not enough memory to extend matrix");
341                 return NULL;
342             }
343         }
344     }
345     for (i = nrh + 1; i <= srh; i++) {
346         if (!(m[i] = gp_alloc((nch - ncl + 1) * sizeof(m[i][0]), NULL))) {
347             free_matrix(m, nrl, i - 1, nrl);
348             int_error(NO_CARET, "not enough memory to extend matrix");
349             return NULL;
350         }
351         m[i] -= ncl;
352     }
353     return m;
354 }
355 /* this routine carves a large matrix down to size */
356 float GPFAR * GPFAR *
357 retract_matrix(
358     float GPFAR * GPFAR * a,
359     int nrl, int nrh, int ncl, int nch,
360     int srh, int sch)
361 {
362     int i;
363     float GPFAR * GPFAR * m;
364
365     for (i = srh + 1; i <= nrh; i++) {
366         free_vector(a[i], ncl);
367     }
368
369     m = gp_realloc(a + nrl, (srh - nrl + 1) * sizeof(m[0]),
370                    "retract matrix");
371     m -= nrl;
372
373     if (sch != nch) {
374         for (i = nrl; i <= srh; i++)
375             if (!(m[i] = retract_vector(m[i], ncl, sch))) {
376                 /* Shrink rows */
377                 free_matrix(m, nrl, srh, ncl);
378                 int_error(NO_CARET, "not enough memory to retract matrix");
379                 return NULL;
380             }
381     }
382     return m;
383 }
384
385
386 /* allocate a float matrix m[nrl...nrh][ncl...nch] that points to the
387    matrix declared in the standard C manner as a[nrow][ncol], where
388    nrow=nrh-nrl+1, ncol=nch-ncl+1.  The routine should be called with
389    the address &a[0][0] as the first argument.  This routine does
390    not free the memory used by the original array a but merely assigns
391    pointers to the rows. */
392
393 float GPFAR * GPFAR *
394 convert_matrix(
395     float GPFAR *a,
396     int nrl, int nrh, int ncl, int nch)
397 {
398     int i, j, ncol, nrow;
399     float GPFAR * GPFAR * m;
400
401     nrow = nrh - nrl + 1;
402     ncol = nch - ncl + 1;
403     m = gp_alloc((nrh - nrl + 1) * sizeof(m[0]),
404                  "convert_matrix");
405     m -= nrl;
406
407     m[nrl] = a - ncl;
408     for (i = 1, j = nrl + 1; i <= nrow - 1; i++, j++)
409         m[j] = m[j - 1] + ncol;
410     return m;
411 }
412
413
414 void
415 free_convert_matrix(float GPFAR * GPFAR * b, int nrl)
416 {
417     free(b + nrl);
418 }