ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/freemol/branches/sync4pymol12/src/mengine/src/tabulator.h
Revision: 93
Committed: Sat Jan 17 23:24:55 2009 UTC (13 years, 5 months ago) by wdelano
File size: 50499 byte(s)
Log Message:
branch created
Line User Rev File contents
1 wdelano 58 #ifndef _H_TABULATOR
2     #define _H_TABULATOR
3    
4     /*! \mainpage tabulator
5     * \ref tabulator.h
6     *
7     * File "tabulator.h" is Copyright (c) 2008 DeLano Scientific LLC,
8     * Palo Alto, California, USA. All rights reserved.
9     *
10     * Redistribution and use in source and binary forms, with or without
11     * modification, are permitted provided that the following conditions are met:
12     * * Redistributions of source code must retain the above copyright
13     * notice, this list of conditions and the following disclaimer.
14     * * Redistributions in binary form must reproduce the above copyright
15     * notice, this list of conditions and the following disclaimer in the
16     * documentation and/or other materials provided with the distribution.
17     * * Neither the name of DeLano Scientific LLC nor the
18     * names of its contributors may be used to endorse or promote products
19     * derived from this software without specific prior written permission.
20     *
21     * THIS SOFTWARE IS PROVIDED BY DELANO SCIENTIFIC LLC ''AS IS'' AND ANY
22     * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23     * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24     * DISCLAIMED. IN NO EVENT SHALL DELANO SCIENTIFIC LLC BE LIABLE FOR ANY
25     * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26     * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27     * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
28     * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29     * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30     * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31     *
32     */
33    
34     /*! \file tabulator.h
35     * \brief Tabulator: reader/writer utility for simple text tables.
36    
37     * Tabulator manages formatted whitespace-separated ASCII text tables
38     * for use as tag records in MDL format SD files (and elsewhere). The
39     * source code is released under the BSD open-source license.
40    
41     - \ref behaviors
42     - \ref example
43     - \ref options
44     - \ref conventions
45    
46     * \section behaviors Instance Behaviors
47    
48     * Tabulator instances simulate a two dimensional array of character
49     * strings and thus have a type char***. They can be accessing using
50     * C array syntax or C pointer syntax. When using pointer syntax, you
51     * can trust that the pointer arrays are null terminated as diagrammed
52     * below:
53    
54     \verbatim
55     "0 0" "0 1" "0 2" NULL
56     "1 0" "1 1" "1 2" NULL
57     NULL \endverbatim
58    
59     * such that you can dump the table contents from the tabulator "tab"
60     * with the following nested loop:
61    
62     \verbatim
63     {
64     char **col, ***row = tab;
65     while(col = *(row++)) {
66     while(*col) {
67     printf("%s ",*(col++))
68     }
69     printf("\n");
70     }
71     } \endverbatim
72    
73     * Column header tags are stored in row -1, so you display the tags as follows:
74     \verbatim
75     {
76     char **col_tag = tab[-1];
77     while(*col_tag) {
78     printf("%s ",*(col_tag++));
79     }
80     printf("\n");
81     }\endverbatim
82    
83     * \section example Example Usage
84     * - \ref writing
85     * - \ref reading
86     * - \ref table
87    
88     * \subsection writing Writing a Table
89     * \verbatim
90     {
91     char ***tab = tabulator_new_from_header("AT_ID1 AT_ID2 DISTANCE COLOR DRAW_TYPE", 0);
92    
93     tabulator_add_row(&tab, "31 12 1 red 6");
94     tabulator_add_row(&tab, "3 8 2 blue 7");
95     tabulator_add_row(&tab, "1 2 2 red 3");
96    
97     {
98     char *buffer = tabulator_as_table(tab);
99     printf("%s", buffer);
100     }
101    
102     tabulator_free(tab);
103     } \endverbatim
104    
105     * \subsection reading Reading a Table
106    
107     * Assuming that "buffer" points at a char* text table in memory...
108    
109     \verbatim
110     {
111     char ***tab2 = tabulator_new_from_table_using_header(buffer, "COLOR AT_ID2 DRAW_TYPE", 0);
112    
113     if(tab2) {
114     int n_row = tabulator_height(tab2);
115     int i;
116     for(i=0;i<n_row;i++) {
117     printf("Row %d: COLOR=%s AT_ID2=%s DRAW_TYPE=%s\n",i, tab2[i][0], tab2[i][1], tab2[i][2]);
118     }
119     tabulator_free(tab2);
120     }
121     } \endverbatim
122    
123     * \subsection table Sample Table
124    
125     * Note that column alignment is purely for benefit of human
126     * perception. The logical structure of the file is solely determined
127     * by tokens, whitespace, double-quotes, and newlines. Note that with the current implementation,
128     * double-quotes are not supported characters within tokens (that can be fixed).
129    
130     \verbatim
131     + AT_ID1 AT_ID2 DISTANCE COLOR DRAW_TYPE
132     | 31 12 1 red 6
133     | 3 8 2 "" 7
134     | 1 2 2 blue 3
135     \endverbatim
136     * \section options Optional Preprocessor Defines
137    
138     * Must define before #include "tabulator.h".
139    
140     * \verbatim #define TABULATOR_INCLUDE_IMPLEMENTATION \endverbatim
141     * Activates inclusion of the implementation code.
142    
143     * To use tabulator, one and only .c file in your application must define
144     * TABULATOR_INCLUDE_IMPLEMENTATION before including "tabulator.h".
145    
146     * \verbatim #define TABULATOR_INCLUDE_UNIT_TEST \endverbatim
147     * Activates inclusion "main" with the tabulator unit testing code.
148    
149     * For example, the tabulator unit test can be built by compiling the following main.c:
150     \verbatim
151     #define TABULATOR_INCLUDE_IMPLEMENTATION
152     #define TABULATOR_INCLUDE_UNIT_TEST
153    
154     #include "tabulator.h"
155     \endverbatim
156    
157     * \section conventions Coding Conventions
158    
159     * Tabulator source code conventions as per DeLano Scientific
160     * "lazy C" coding style:
161     *
162     * - ALL_CAPS_UNDERSCORE constants, macros, and defines.
163     *
164     * - lowercase_underscore symbol names throughout.
165     *
166     * - within member functions, the local pointer "I" used to refer to
167     * the instance (synonymous with C++ "this", or Python "self").
168     * \verbatim I->attribute = value; \endverbatim
169    
170     */
171    
172     #include <stdio.h>
173    
174     /*! \def TABULATOR_FLAG_STRICT
175     * Constructor flag dictating failure if the input text table does not
176     * already contain each of the requested header tags.
177     */
178     #define TABULATOR_FLAG_STRICT 0x1
179    
180     /*! \def TABULATOR_FLAG_DEBUG_STDERR
181     * Constructor flag dictating that tabulator should export debug
182     * information to stderr.
183     */
184     #define TABULATOR_FLAG_DEBUG_STDERR 0x2
185    
186     /*!
187     * Constructs an new empty tabulator instance with structure matching
188     * the provided column header tags.
189    
190     * \param table should point at the first character of the text table (the '+' token).
191    
192     * \param header should point at a char* string containing a
193     * whitespace separated list of column tags.
194    
195     * \return a char*** table whose members can be read directly using C
196     * array syntax.
197     */
198     char ***tabulator_new_from_header(char *header, int flags);
199    
200     /*!
201     * Constructs an new empty tabulator instance with the given size.
202    
203     * \return a char*** table whose members can be read directly using C
204     * array syntax and written using \ref tabulator_set
205     */
206     char ***tabulator_new_with_size(int height, int width, int flags);
207    
208     /*!
209     * Constructs a new tabulator instance from an existing table record.
210     * \return a char*** table whose members can be read directly using
211     * C array syntax.
212     */
213     char ***tabulator_new_from_table(char *table, int flags);
214    
215     /*!
216     * Constructs a new tabulator instance from a stream.
217     * \return a char*** table whose members can be read directly using
218     * C array syntax.
219     */
220     char ***tabulator_new_from_file(FILE *input, int flags);
221    
222     /*!
223     * Constructs a new tabulator instance from an existing table with a
224     * structure matching the headers provided.
225    
226     * \param table should point at the first character of the text table
227     * (the '+' token).
228    
229     * \param flags OR'd bitmask of tabulator constructor flags.
230    
231     * \return a char*** table whose members can be read directly using C
232     * array syntax.
233    
234     * When TABULATOR_FLAG_STRICT is specified, then this constructor will
235     * return NULL if any of the requested column tags are missing from
236     * the source table.
237     */
238     char ***tabulator_new_from_table_using_header(char *table, char *header, int flags);
239    
240     /*!
241     * Constructs a new tabulator instance from a stream with a structure
242     * matching the headers provided.
243    
244     * \param input should point at the first character of the table in
245     * the file (the '+' token).
246    
247     * \param flags OR'd bitmask of tabulator constructor flags.
248    
249     * \return a char*** table whose members can be read directly using C
250     * array syntax.
251    
252     * When TABULATOR_FLAG_STRICT is specified, then this constructor will
253     * return NULL if any of the requested column tags are missing from
254     * the source table.
255     */
256     char ***tabulator_new_from_file_using_header(FILE *input, char *header, int flags);
257    
258     /*!
259     * Destroys a tabulator instance.
260     */
261     void tabulator_free(char ***tab);
262    
263     /*!
264     * Returns the number of rows in the table, not including the header row.
265    
266     * \return table height or -1 on error.
267     */
268     int tabulator_height(char ***tab);
269    
270     /*!
271     * Returns the number of columns in the table.
272    
273     * \return table width or -1 on error.
274     */
275     int tabulator_width(char ***tab);
276    
277     /*!
278     * Copies an existing tabulator instance to a new instance with a
279     * structure matching the provided column header tags.
280    
281     * When TABULATOR_FLAG_STRICT is specified, then this constructor will
282     * return NULL if any of the requested column tags are missing from
283     * the source table.
284    
285     */
286     char ***tabulator_copy_using_header(char ***tab_ptr, char *header, int flags);
287    
288     /*!
289     * Extends an existing table with a new row consisting of the
290     * whitespace-delimited tokens provided.
291    
292     *\param tab_ptr is a char**** pointer which WILL be modified as the
293     * expanded tabulator instance is relocated within the heap.
294    
295     *\param line is a whitespace separated list of tokens to be used in
296     *the new row.
297    
298     * If the number of tokens is less than the table width, the remaining
299     * columns are populated with empty tokens ("").
300    
301     */
302     int tabulator_add_row(char ****tab_ptr, char *line);
303    
304     /*!
305     * Returns a pointer to a character buffer which will remain valid until
306     * the tabulator instance is destroyed or the call is repeated
307     */
308     char *tabulator_as_table(char ***tab);
309    
310     /*!
311     * Replaces an existing entry in the tabulator instance.
312    
313     *\returns true if successful, false if not
314    
315     * This routine does not release the memory associated with the previous entry.
316    
317     */
318     int tabulator_set(char ***tab, int row, int col, char *str);
319    
320     #endif
321    
322     #ifdef TABULATOR_INCLUDE_UNIT_TEST
323     #include <stdlib.h>
324     #include <unistd.h>
325    
326     static int alloc_cnt = 0;
327     static int alloc_max = 0;
328     static void *w_malloc(size_t size)
329     {
330     alloc_cnt++;
331     if(alloc_max<alloc_cnt) alloc_max = alloc_cnt;
332     return malloc(size);
333     }
334     static void *w_calloc(size_t count, size_t size)
335     {
336     alloc_cnt++;
337     if(alloc_max<alloc_cnt) alloc_max = alloc_cnt;
338     return calloc(count,size);
339     }
340     static void w_free(void *ptr)
341     {
342     alloc_cnt--;
343     free(ptr);
344     }
345     #else
346     #define w_malloc malloc
347     #define w_calloc calloc
348     #define w_free free
349     #endif
350    
351     #ifdef TABULATOR_INCLUDE_IMPLEMENTATION
352    
353     #include <stdio.h>
354     #include <string.h>
355     #include <stdlib.h>
356     #include <stddef.h>
357    
358     #ifndef NULL
359     #define NULL ((void*)0)
360     #endif
361    
362     #ifndef true
363     #define true 1
364     #endif
365    
366     #ifndef false
367     #define false 0
368     #endif
369    
370     typedef struct {
371     int flags;
372     FILE *log, *debug;
373     char *output, *text, **col;
374     int text_size, text_max;
375     int n_col, col_max;
376     int n_row, row_max;
377     char **row[2]; /* "row" must be last field in struct */
378     } tabulator;
379    
380     #define TABULATOR_TABLE_TOKEN '+'
381     #define TABULATOR_LINE_TOKEN '|'
382    
383    
384     static void tabulator_copy_and_unquote(char **dst_ptr, char **src_ptr)
385     {
386     char *src = *src_ptr;
387     char *dst = *dst_ptr;
388     int quote = false;
389     int esc = false;
390     char ch,m1=0,m2=0,m3=0;
391    
392     if(*src=='"') {
393     quote = true;
394     src++;
395     }
396     while(*src) {
397     ch = *(src++);
398     if(esc) {
399     switch(ch) {
400     case '\\':
401     *(dst++)=ch;
402     esc = false;
403     break;
404     case '"':
405     *(dst++)=ch;
406     esc = false;
407     break;
408     case 'n':
409     *(dst++)='\n';
410     esc = false;
411     break;
412     default:
413     /* encoded characters */
414     m3=m2;
415     m2=m1;
416     m1=ch;
417     if(m3) { /* octal */
418     *(dst++)=(m3-'0')*64 + (m2-'0')*8 + (m1-'0');
419     esc = false;
420     }
421     break;
422     }
423     } else if(quote) {
424     if(ch=='"') {
425     quote = false;
426     } else if(ch=='\\') {
427     esc = true; m1=0;m2=0;m3=0;
428     } else {
429     *(dst++)=ch;
430     }
431     } else {
432     if(ch=='"') {
433     quote = true;
434     } else if(ch=='\\') {
435     esc = true; m1=0;m2=0;m3=0;
436     } else {
437     if(!((ch<33)||(ch>126)))
438     *(dst++)=ch;
439     else
440     break;
441     }
442     }
443     }
444     *(dst++) = 0;
445     *src_ptr = src;
446     *dst_ptr = dst;
447     }
448    
449     static int tabulator_char_is_white(char ch)
450     {
451     return ch&&((ch<33)||(ch>126));
452     }
453    
454     static int tabulator_char_is_newline(char ch) { return (ch==10)||(ch==13); }
455    
456     char ***tabulator_new_from_header(char *header, int flags)
457     {
458     int ok = true;
459     tabulator *I = (tabulator*)w_calloc(sizeof(tabulator),1);
460    
461     if(flags & TABULATOR_FLAG_DEBUG_STDERR) {
462     fprintf(stderr, "new_from_header(\"%s\",0x%x): I^%d",
463     header, flags, I&&I);
464     }
465    
466     if(I && header) {
467     I->flags = flags;
468     I->debug = (I->flags & TABULATOR_FLAG_DEBUG_STDERR) ? stderr : NULL;
469     I->text_max = strlen(header) + 3;
470     I->text_size = I->text_max;
471    
472     if( (ok = ok && (I->text = (char*)w_calloc(I->text_max , 1))) ) {
473    
474     I->row_max = 2;
475    
476     /* count header strings */
477     {
478     int n_col = 0;
479     char *src = header;
480     char *dst = I->text + 1;
481     while(1) { /* pack header into a sequence of non-null strings */
482     while(tabulator_char_is_white(*src)) src++;
483     if(!*src) {
484     break;
485     }
486     n_col++;
487     tabulator_copy_and_unquote(&dst,&src);
488     }
489     I->n_col = n_col;
490     }
491    
492     if(I->debug) {
493     fprintf(I->debug, ", I->n_col=%d",I->n_col);
494     }
495    
496     /* copy header strings */
497    
498     I->col_max = I->n_col + 1;
499     if( (ok = ok && (I->col = (char**)w_calloc(sizeof(char*), I->col_max))) ) {
500    
501     char *src = header;
502     char *dst = I->text + 1;
503     char **col = I->col;
504    
505     I->row[0] = col; /* becomes c2s[-1] */
506    
507     while(1) { /* pack header into a sequence of non-null strings */
508     while(tabulator_char_is_white(*src)) src++;
509     if(!*src) {
510     break;
511     }
512     *(col++) = dst;
513     tabulator_copy_and_unquote(&dst,&src);
514     }
515     }
516     }
517     }
518    
519     if(flags & TABULATOR_FLAG_DEBUG_STDERR) {
520     fprintf(stderr,"\n");
521     }
522     if(!ok) {
523     if(I) tabulator_free(I->row + 1);
524     return NULL;
525     } else {
526     return (I->row + 1); /* point at the second entry in I->row */
527     }
528     }
529    
530    
531     static char *tabulator_copy_line(char *dst, char *src, int buffer_size)
532     {
533     if(buffer_size>0) {
534     while(*src && (!tabulator_char_is_newline(*src))) {
535     if(buffer_size--) {
536     *(dst++) = *(src++);
537     } else {
538     break;
539     }
540     }
541     *dst = 0;
542     if(((*src)==10)&&((*src)==13)) src++; /* CRLF */
543     src++; /* skip CR or LF */
544     }
545     return src;
546     }
547    
548     static int tabulator_grow_text(tabulator *I, int chunk)
549     {
550     int new_text_size = I->text_size + chunk;
551     if(new_text_size > I->text_max) {
552     /* grow text storage as necessary */
553     int new_text_max = (new_text_size + (new_text_size>>1));
554     char *new_text = (char*)realloc(I->text, new_text_max + 1);
555    
556     if(new_text) {
557     char **col_ptr = I->col;
558     char **col_stop = I->col + (I->n_col+1) * (I->n_row+1);
559     ptrdiff_t diff = new_text - I->text;
560     if(diff) {
561     while(col_ptr != col_stop) {
562     (*col_ptr) = *(col_ptr) ? (*col_ptr + diff) : (*col_ptr);
563     col_ptr++;
564     }
565     }
566     I->text = new_text;
567     memset(I->text + I->text_max, 0, new_text_max - I->text_max);
568     I->text_max = new_text_max;
569     } else {
570     return false;
571     }
572     }
573     return true;
574     }
575    
576     static int tabulator_grow_col(tabulator *I, int chunk)
577     {
578     int new_col_size = (I->n_col+1) * (I->n_row + 1 + chunk);
579     if(new_col_size > I->col_max) {
580     int new_col_max = (new_col_size + (new_col_size>>1));
581     char **new_col = (char**)realloc(I->col, sizeof(char*) * (new_col_max + 1));
582     if(new_col) {
583     char ***row = I->row;
584     ptrdiff_t diff = new_col - I->col;
585     if(diff) {
586     while(*row) { *row = *row + diff; row++; }
587     }
588     I->col = new_col;
589     memset(I->col + I->col_max, 0, sizeof(char**) * (new_col_max - I->col_max));
590     I->col_max = new_col_max;
591     } else {
592     return false;
593     }
594     }
595     return true;
596     }
597    
598    
599     char ***tabulator_new_with_size(int height, int width, int flags)
600     {
601     int ok = true;
602     tabulator *I = (tabulator*)w_calloc(sizeof(tabulator)+(sizeof(char**)*height),1);
603    
604     if(flags & TABULATOR_FLAG_DEBUG_STDERR) {
605     fprintf(stderr, "new_with_size(%d,%d): I^%d\n",
606     height,width, I&&I);
607     }
608    
609     if(I) {
610     I->flags = flags;
611     I->debug = (I->flags & TABULATOR_FLAG_DEBUG_STDERR) ? stderr : NULL;
612     I->text_max = 3;
613     I->text_size = I->text_max;
614    
615     if( (ok = ok && (I->text = (char*)w_calloc(I->text_max, 1))) ) {
616    
617     I->n_row = height;
618     I->n_col = width;
619     I->row_max = I->n_row + 3;
620     I->col_max = (width + 1) * (I->n_row + 2);
621    
622     if( (ok = ok && (I->col = (char**)w_calloc(sizeof(char*), I->col_max)))) {
623     int i;
624     for(i=0;i<=height;i++) {
625     int j;
626     char **col = I->col + i*(width+1);
627     I->row[i] = col;
628     for(j=0;j<width;j++) {
629     *(col++) = I->text;
630     }
631     }
632     }
633     }
634     }
635     if(!ok) {
636     if(I) tabulator_free(I->row + 1);
637     return NULL;
638     } else {
639     return (I->row + 1); /* point at the second entry in I->row */
640     }
641     }
642    
643     char ***tabulator_copy_using_header(char ***tab, char *header, int flags)
644     {
645     if(flags & TABULATOR_FLAG_DEBUG_STDERR) {
646     fprintf(stderr, "copy_using_header(^%d,\"%s\",0x%x):\n",
647     tab&&1, header, flags);
648     }
649     {
650     char ***result = tabulator_new_from_header(header, flags);
651     if(result) {
652     int ok = true;
653     tabulator *I = ((tabulator*)(result+1))-1;
654     tabulator *S = ((tabulator*)(tab+1))-1;
655     int *xref = (int*)w_malloc(sizeof(int)*I->n_col);
656     int *used = (int*)w_calloc(sizeof(int),S->n_col);
657    
658     if(!(xref && used)) {
659     ok = false;
660     } else {
661    
662     memset(xref,-1,sizeof(int)*I->n_col);
663    
664     /* allocate row pointer storage */
665     {
666     tabulator *new_I = (tabulator*)realloc(I, sizeof(tabulator) +
667     sizeof(char***) * S->n_row+1);
668     if(new_I) {
669     I = new_I;
670     result = (I->row + 1);
671    
672     I->row_max = S->n_row+1;
673     I->n_row = S->n_row;
674     memset(I->row+2, 0, sizeof(char**)*I->n_row);
675    
676     } else
677     ok = false;
678     }
679    
680     /* allocate col pointer storage */
681    
682     if(ok) ok = tabulator_grow_col(I, 0);
683    
684     /* create the header cross-reference table */
685     if(ok) {
686     int i,s;
687     char **i_col = I->row[0];
688     char **s_col = S->row[0];
689     for(i=0; i<I->n_col; i++) {
690     for(s=0; s<S->n_col; s++) {
691     if(!used[s]) {
692     if( !strcmp(i_col[i],s_col[s]) ) {
693     xref[i] = s;
694     used[s] = true;
695     }
696     }
697     }
698     }
699     }
700     }
701    
702     if(ok) {
703     if(flags & TABULATOR_FLAG_STRICT) {
704     int i;
705     for(i=0; i<I->n_col; i++) {
706     if(xref[i]<0) {
707     ok = false;
708     break;
709     }
710     }
711     }
712     }
713    
714     /* iterate through rows and cols */
715    
716     if(ok) {
717     int row_idx = 1;
718     char ***src_row = S->row + 1;
719     char ***dst_row = I->row + 1;
720    
721     while(*src_row) {
722     char **src_col = *(src_row++);
723     char **dst_col = I->col + (I->n_col+1) * (row_idx++);
724     int i;
725     *(dst_row++) = dst_col;
726    
727     for(i=0;i<I->n_col;i++) {
728     if(xref[i]<0)
729     dst_col[i] = I->text; /* missing / blank entry */
730     else {
731     char *st = src_col[xref[i]];
732     int len = strlen(st) + 1;
733     if(tabulator_grow_text(I,len)) {
734     memcpy( (dst_col[i] = I->text + I->text_size), st, len);
735     I->text_size += len;
736     } else
737     ok = false;
738     }
739     }
740     }
741     }
742     w_free(xref);
743     w_free(used);
744     if(result && !ok) {
745     tabulator_free(result);
746     result = NULL;
747     }
748     }
749     return result;
750     }
751     }
752    
753     char ***tabulator_new_from_table(char *table, int flags)
754     {
755     char ***result = NULL;
756    
757     if(flags & TABULATOR_FLAG_DEBUG_STDERR) {
758     fprintf(stderr, "new_from_table(^%d,0x%x):\n",
759     table&&1, flags);
760     }
761    
762     if(table && (*table == TABULATOR_TABLE_TOKEN)) {
763     int buf_size = 0;
764    
765     /* determine size of table (for allocating scratch space) */
766     {
767     char *src = table;
768     while(*src) {
769     while(*src && (!tabulator_char_is_newline(*src))) src++; /* seek end of line */
770     if(((*src)==10)&&((*src)==13)) src++; /* CRLF */
771     src++;
772     if(*src != TABULATOR_LINE_TOKEN) /* new line without start token ends table */
773     break;
774     }
775     buf_size = 1 + (src - table);
776     }
777    
778     /* load the table */
779     if(buf_size) {
780     char *scratch = (char*)w_malloc(buf_size);
781     if(scratch) {
782     char *src = table;
783     if(*src && (*src) == TABULATOR_TABLE_TOKEN) {
784     src = tabulator_copy_line(scratch, src+1, buf_size);
785     result = tabulator_new_from_header(scratch, flags);
786     }
787     if(result) {
788     while(*src && (*src == TABULATOR_LINE_TOKEN)) {
789     src = tabulator_copy_line(scratch, src+1, buf_size);
790     tabulator_add_row(&result, scratch);
791     }
792     }
793     w_free(scratch);
794     }
795     }
796     }
797     return result;
798     }
799    
800     /*!
801     * Constructs a new tabulator instance from a table on a stream.
802     * \return a char*** table whose members can be read directly using
803     * C array syntax.
804     */
805     char ***tabulator_new_from_file(FILE *input, int flags)
806     {
807     char ***result = NULL;
808     int buffer_size = 4000;
809     int bytes_free = buffer_size;
810     char *buffer = (char*)malloc(buffer_size);
811     char *newline = NULL;
812     char *last = buffer;
813     if(buffer) {
814     buffer[0] = 0;
815     while(1) {
816    
817     if(!fgets(last,bytes_free,input)) {
818     /* EOF or no characters read -> incomplete/invalid table */
819     buffer[0] = 0;
820     break;
821     }
822     if(newline) {
823    
824     /* check for a blank line (last would be pointing at a newline) */
825    
826     while(*last) {
827     /* skip accidental/invisible whitespace */
828     if((*last!=10)&&(*last!=13)&&(tabulator_char_is_white(*last)))
829     last++;
830     else
831     break;
832     }
833    
834     if( ((last[0]==10) && (!last[1]) && (newline[0]==10)) || /* LF LF (Unix) */
835     ((last[0]==13) && (!last[1]) && (newline[0]==13)) || /* CR CR (Mac) */
836     ((last[0]==13) && (last[1]==10) && (!last[2]) &&
837     (newline[0]==13) && (newline[1]==10)) ) { /* CRLF CRLF (Win) */
838     /* found blank line */
839     break;
840     }
841     }
842    
843     /* otherwise, scan to end of string */
844    
845     while(*last) {
846     last++;
847     bytes_free--;
848     }
849    
850     /* and fine the newline (if present) */
851    
852     newline = NULL;
853     switch(last-buffer) {
854     case 0:
855     break;
856     case 1:
857     if(last[-1]==10)
858     newline = last-1;
859     break;
860     default:
861     if((last[-2]==13) && last[-1]==10)
862     newline = last-2;
863     else if(last[-1]==10)
864     newline = last-1;
865     break;
866     }
867    
868     /* get us more space if we need it, and update our variables */
869    
870     if(bytes_free<2) {
871     char *new_buffer;
872     int new_size = buffer_size + (buffer_size>>1);
873    
874     new_buffer = (char*)realloc(buffer, new_size);
875     if(!new_buffer) {
876     buffer[0] = 0; /* error */
877     break;
878     }
879    
880     bytes_free += new_size - buffer_size;
881     last = new_buffer + (last - buffer);
882     if(newline) newline = new_buffer + (newline - buffer);
883    
884     buffer_size = new_size;
885     buffer = new_buffer;
886     }
887     }
888    
889     if(buffer[0]) { /* read a table */
890     result = tabulator_new_from_table(buffer,flags);
891     }
892     free(buffer);
893     }
894     return result;
895     }
896    
897     char ***tabulator_new_from_file_using_header(FILE *input, char *header, int flags)
898     {
899     if(flags & TABULATOR_FLAG_DEBUG_STDERR) {
900     fprintf(stderr, "new_from_file_using_header(^%d,\"%s\",0x%x):\n",
901     input&&1, header, flags);
902     }
903     {
904     char ***result = NULL;
905     char ***tmp = tabulator_new_from_file(input, flags);
906    
907     if(tmp) {
908     result = tabulator_copy_using_header(tmp, header, flags);
909     tabulator_free(tmp);
910     }
911     return result;
912     }
913     }
914    
915     char ***tabulator_new_from_table_using_header(char *table, char *header, int flags)
916     {
917     if(flags & TABULATOR_FLAG_DEBUG_STDERR) {
918     fprintf(stderr, "new_from_table_using_header(^%d,\"%s\",0x%x):\n",
919     table&&1, header, flags);
920     }
921     {
922     char ***result = NULL;
923     char ***tmp = tabulator_new_from_table(table, flags);
924    
925     if(tmp) {
926     result = tabulator_copy_using_header(tmp, header, flags);
927     tabulator_free(tmp);
928     }
929     return result;
930     }
931     }
932    
933     int tabulator_height(char ***tab)
934     {
935     if(tab) {
936     tabulator *I = ((tabulator*)(tab+1))-1;
937     return I->n_row;
938     } else {
939     return -1;
940     }
941     }
942    
943     int tabulator_width(char ***tab)
944     {
945     if(tab) {
946     tabulator *I = ((tabulator*)(tab+1))-1;
947     return I->n_col;
948     } else {
949     return -1;
950     }
951     }
952    
953     void tabulator_free(char ***tab)
954     {
955     if(tab) {
956     tabulator *I = ((tabulator*)(tab+1))-1;
957    
958     if(I->debug) {
959     fprintf(I->debug, "free: I->text^%d, I->col^%d, I->output^%d\n",
960     I->text&&1, I->col&&1, I->output&&1);
961     }
962     if(I->text) w_free(I->text);
963     if(I->col) w_free(I->col);
964     if(I->output) w_free(I->output);
965     w_free(I);
966     }
967     }
968    
969     static void tabulator_debug_dump(char ***tab)
970     {
971     if(tab) {
972     tabulator *I = ((tabulator*)(tab+1))-1;
973     fprintf(stderr,"dump: I->n_col=%d, I->n_row=%d \n",I->n_col, I->n_row);
974     if(I->n_col) {
975     char ***row = I->row;
976     while(*row) {
977     char **col = *(row++);
978     fprintf(stderr,"dump: ");
979     while(*col) {
980     fprintf(stderr,"[%s] ",*(col++));
981     }
982     fprintf(stderr,"\n");
983     }
984     }
985     } else {
986     fprintf(stderr,"dump: null tabulator\n");
987     }
988     }
989    
990     int tabulator_add_row(char ****tab_ptr, char *line)
991     {
992     if(*tab_ptr) {
993     tabulator *I = ((tabulator*)((*tab_ptr)+1))-1;
994     int line_len = strlen(line);
995     int ok = true;
996    
997     if(I->debug) {
998     fprintf(I->debug, "add_row(^1,\"%s\"):\n",line);
999     }
1000    
1001     ok = tabulator_grow_text(I, line_len + 1);
1002    
1003     if(ok) ok = tabulator_grow_col(I, 1);
1004    
1005     if(ok) {
1006    
1007     /* extend row storage as necessary */
1008     int new_row_size = (I->n_row + 3);
1009     if(new_row_size > I->row_max) {
1010     int new_row_max = (new_row_size + (new_row_size>>1));
1011     tabulator *new_I = (tabulator*)realloc(I, sizeof(tabulator)
1012     + sizeof(char***) * new_row_max);
1013     if(new_I) {
1014     I = new_I;
1015     memset(I->row + I->row_max, 0, sizeof(char**) * (new_row_max - I->row_max));
1016     I->row_max = new_row_max;
1017     } else {
1018     ok = false;
1019     }
1020     }
1021    
1022     if(ok) {
1023    
1024     /* tokenize and link */
1025     char *new_text = I->text + I->text_size;
1026     char **col_start = I->col + (I->n_col+1) * (I->n_row+1);
1027     int col_cnt = I->n_col;
1028     I->row[ 1 + I->n_row++ ] = col_start;
1029    
1030     /* copy the source text */
1031     memcpy(new_text, line, line_len);
1032     I->text_size += line_len + 1;
1033    
1034     /* link all n_columns to text token (handles blanks) */
1035     while(col_cnt--) {
1036    
1037     /* eliminate leading whitespace */
1038     while(*new_text && tabulator_char_is_white(*new_text)) new_text++;
1039    
1040     if(!*new_text) {
1041     *(col_start++) = I->text; /* or substitute a blank string */
1042     } else {
1043     char *tmp = new_text;
1044     *(col_start++) = new_text; /* copy the token */
1045     tabulator_copy_and_unquote(&tmp, &new_text);
1046     }
1047     }
1048     }
1049     }
1050     (*tab_ptr) = ((char***)(I+1))-1;
1051     return ok;
1052     } else {
1053     return false;
1054     }
1055     }
1056    
1057     static int tabulator_escaped_strlen(char *src,int *quotes)
1058     {
1059     if(!src[0]) {
1060     return 2;
1061     } else {
1062     int base_len = strlen(src);
1063     char ch;
1064     int quotes_required = false;
1065     int extra_len = 0;
1066    
1067     while(*src) {
1068     ch = *(src++);
1069     if(tabulator_char_is_white(ch)) {
1070     quotes_required = true;
1071     switch(ch) {
1072     case ' ': /* space */
1073     break;
1074     case '\n': /* newline */
1075     extra_len++;
1076     break;
1077     default:
1078     extra_len +=3; /* will use \xxx */
1079     break;
1080     }
1081     } else {
1082     switch(ch) {
1083     case '"':
1084     case '\\':
1085     extra_len++;
1086     break;
1087     }
1088     }
1089     }
1090     if(quotes_required) extra_len += 2;
1091     if(quotes) {
1092     *quotes = quotes_required;
1093     }
1094     return base_len + extra_len;
1095     }
1096     }
1097    
1098     static void tabulator_escaped_copy(char *dst, int width, char *src)
1099     {
1100     int need_quotes = false;
1101     int len = tabulator_escaped_strlen(src,&need_quotes);
1102     dst += (width-len);
1103     if(!src[0]) {
1104     dst[0]='"';
1105     dst[1]='"';
1106     } else {
1107     if(need_quotes) {
1108     *(dst++) = '"';
1109     }
1110     while(*src) {
1111     unsigned char ch = *(src++);
1112     if(tabulator_char_is_white(ch)) {
1113     switch(ch) {
1114     case ' ': /* space */
1115     *(dst++)=ch;
1116     break;
1117     case '\n': /* newline */
1118     *(dst++)='\\';
1119     *(dst++)='n';
1120     break;
1121     default:
1122     *(dst++)='\\';
1123     *(dst++)=((ch>>6)&0x7)+'0';
1124     *(dst++)=((ch>>3)&0x7)+'0';
1125     *(dst++)=((ch>>0)&0x7)+'0';
1126     break;
1127     }
1128     } else {
1129     switch(ch) {
1130     case '"':
1131     case '\\':
1132     *(dst++)='\\';
1133     *(dst++)=ch;
1134     break;
1135     default:
1136     *(dst++)=ch;
1137     break;
1138     }
1139     }
1140     }
1141     if(need_quotes) {
1142     *(dst++) = '"';
1143     }
1144     }
1145     }
1146    
1147     char *tabulator_as_table(char ***tab)
1148     {
1149     char *result = NULL;
1150     if(tab) {
1151     tabulator *I = ((tabulator*)(tab+1))-1;
1152     int *max_width = (int*)w_calloc(sizeof(int),I->n_col);
1153    
1154     if(max_width) {
1155     if(I->debug) {
1156     fprintf(I->debug, "as_table(^1): I->n_row=%d, I->n_col=%d\n",
1157     I->n_row, I->n_col);
1158     }
1159    
1160     /* measure field widths */
1161     {
1162     char ***row = I->row;
1163     while(*row) {
1164     char **col = *(row++);
1165     int *mw = max_width;
1166     while(*col) {
1167     int fld_len = tabulator_escaped_strlen(*col,NULL);
1168     if(fld_len > *mw) *mw = fld_len;
1169     mw++;
1170     col++;
1171     }
1172     }
1173     }
1174    
1175     {
1176     int output_size = 0;
1177    
1178     /* compute total table size */
1179     {
1180     int i, line_width = 0;
1181     for(i=0;i<I->n_col;i++) {
1182     line_width += (++max_width[i]);
1183     }
1184     output_size = ( (line_width + 2) * /* +2 for prefix & newline */
1185     (I->n_row + 1) /* +1 = for headers */
1186     + 1); /* and then +1 for terminal newline */
1187     }
1188    
1189     /* allocate buffer for the output */
1190    
1191     if(I->output) w_free(I->output);
1192     result = I->output = w_malloc(output_size+1);
1193    
1194     if(result) {
1195     char *dst = result;
1196    
1197     /* fill with spaces */
1198     memset(result, 32, output_size);
1199    
1200     /* terminate */
1201     result[output_size] = 0;
1202    
1203     /* copy fields into the output at proper locations */
1204    
1205     {
1206     char ***row = I->row;
1207     char token = TABULATOR_TABLE_TOKEN;
1208     while(*row) {
1209     char **col = *(row++);
1210     int *mw = max_width;
1211     *(dst++) = token;
1212     while(*col) {
1213     tabulator_escaped_copy(dst, *mw, *(col++));
1214     dst += *(mw++);
1215     }
1216     *(dst++) = '\n'; /* row-ending newline */
1217     token = TABULATOR_LINE_TOKEN;
1218     }
1219     }
1220     *(dst++) = '\n'; /* table-ending newline */
1221     }
1222     }
1223     w_free(max_width);
1224     }
1225     }
1226     return result;
1227     }
1228    
1229     int tabulator_set(char ***tab, int row, int col, char *str)
1230     {
1231     if(tab) {
1232     tabulator *I = ((tabulator*)(tab+1))-1;
1233     if( (row>-2) && (row<I->n_row) && (col>=0) && (col<I->n_col) ) {
1234     int len = strlen(str) + 1;
1235     if(tabulator_grow_text(I,len)) {
1236     char *dst = I->text + I->text_size;
1237     memcpy(dst, str, len);
1238     tab[row][col] = dst;
1239     I->text_size += len;
1240     return true;
1241     }
1242     }
1243     }
1244     return false;
1245     }
1246    
1247     #endif
1248    
1249     #ifdef TABULATOR_INCLUDE_UNIT_TEST
1250     int main(int argc, char **argv)
1251     {
1252    
1253     fprintf(stderr,"---------------- TEST-tabulator.h: %d ----------------\n",__LINE__);
1254     {
1255     char ***tab = tabulator_new_from_header("",
1256     TABULATOR_FLAG_DEBUG_STDERR);
1257     tabulator_debug_dump(tab);
1258     tabulator_free(tab);
1259     }
1260    
1261     fprintf(stderr,"---------------- TEST-tabulator.h: %d ----------------\n",__LINE__);
1262     {
1263     char ***tab = tabulator_new_from_header(" ONE ",
1264     TABULATOR_FLAG_DEBUG_STDERR);
1265     tabulator_debug_dump(tab);
1266     tabulator_free(tab);
1267     }
1268    
1269     fprintf(stderr,"---------------- TEST-tabulator.h: %d ----------------\n",__LINE__);
1270     {
1271     char ***tab = tabulator_new_from_header("TWO THREE",
1272     TABULATOR_FLAG_DEBUG_STDERR);
1273     tabulator_debug_dump(tab);
1274     tabulator_free(tab);
1275     }
1276    
1277     fprintf(stderr,"---------------- TEST-tabulator.h: %d ----------------\n",__LINE__);
1278     {
1279     char buffer[255];
1280     char ***tab = tabulator_new_from_header(" ONE FIELD_TWO THREE FOUR ",
1281     TABULATOR_FLAG_DEBUG_STDERR);
1282    
1283     tabulator_debug_dump(tab);
1284     sprintf(buffer," asdf 245.6 ");
1285     tabulator_add_row(&tab, buffer);
1286     tabulator_debug_dump(tab);
1287    
1288     sprintf(buffer,"-45.6 CA 235 01");
1289     tabulator_add_row(&tab, buffer);
1290     tabulator_debug_dump(tab);
1291    
1292     sprintf(buffer,"blah1 blah2 blah3 blah4 blah5 blah6");
1293     tabulator_add_row(&tab, buffer);
1294     tabulator_debug_dump(tab);
1295    
1296     fprintf(stderr, "tabulator_as_table(tab):\n%s",tabulator_as_table(tab));
1297     tabulator_free(tab);
1298     }
1299    
1300     fprintf(stderr,"---------------- TEST-tabulator.h: %d ----------------\n",__LINE__);
1301     {
1302     char ***tab = tabulator_new_from_table("",
1303     TABULATOR_FLAG_DEBUG_STDERR);
1304     fprintf(stderr, "tab^%d\n",tab&&1);
1305     }
1306    
1307     fprintf(stderr,"---------------- TEST-tabulator.h: %d ----------------\n",__LINE__);
1308     {
1309     char ***tab = tabulator_new_from_table("+",
1310     TABULATOR_FLAG_DEBUG_STDERR);
1311     tabulator_debug_dump(tab);
1312     fprintf(stderr, "tabulator_as_table(tab):\n%s",tabulator_as_table(tab));
1313     tabulator_free(tab);
1314     }
1315    
1316     fprintf(stderr,"---------------- TEST-tabulator.h: %d ----------------\n",__LINE__);
1317     {
1318     char ***tab = tabulator_new_from_table("+\n|\n|\n",
1319     TABULATOR_FLAG_DEBUG_STDERR);
1320     tabulator_debug_dump(tab);
1321     fprintf(stderr, "tabulator_as_table(tab):\n%s",tabulator_as_table(tab));
1322     tabulator_free(tab);
1323     }
1324    
1325     fprintf(stderr,"---------------- TEST-tabulator.h: %d ----------------\n",__LINE__);
1326     {
1327     char ***tab = tabulator_new_from_table("+ TEST",
1328     TABULATOR_FLAG_DEBUG_STDERR);
1329     tabulator_debug_dump(tab);
1330     fprintf(stderr, "tabulator_as_table(tab):\n%s",tabulator_as_table(tab));
1331     tabulator_free(tab);
1332     }
1333    
1334     fprintf(stderr,"---------------- TEST-tabulator.h: %d ----------------\n",__LINE__);
1335     {
1336     char ***tab = tabulator_new_from_table("+ TEST TOO",
1337     TABULATOR_FLAG_DEBUG_STDERR);
1338     tabulator_debug_dump(tab);
1339     fprintf(stderr, "tabulator_as_table(tab):\n%s",tabulator_as_table(tab));
1340     tabulator_free(tab);
1341     }
1342    
1343     fprintf(stderr,"---------------- TEST-tabulator.h: %d ----------------\n",__LINE__);
1344     {
1345     char ***tab = tabulator_new_from_table("+ A Ab Abc\n| 1\n",
1346     TABULATOR_FLAG_DEBUG_STDERR);
1347     tabulator_debug_dump(tab);
1348     fprintf(stderr, "tabulator_as_table(tab):\n%s",tabulator_as_table(tab));
1349     tabulator_free(tab);
1350     }
1351    
1352     fprintf(stderr,"---------------- TEST-tabulator.h: %d ----------------\n",__LINE__);
1353     {
1354     char ***tab = tabulator_new_from_table("+ A Ab Abc\n| 1 2 3\n|4 5 6\n",
1355     TABULATOR_FLAG_DEBUG_STDERR);
1356     tabulator_debug_dump(tab);
1357     fprintf(stderr, "tabulator_as_table(tab):\n%s",tabulator_as_table(tab));
1358     tabulator_free(tab);
1359     }
1360    
1361     fprintf(stderr,"---------------- TEST-tabulator.h: %d ----------------\n",__LINE__);
1362     {
1363     char ***tab = tabulator_new_from_table("+ Abc Ab A\n| 1 2 3 4 5 6\n|7 8\n",
1364     TABULATOR_FLAG_DEBUG_STDERR);
1365     tabulator_debug_dump(tab);
1366     fprintf(stderr, "tabulator_as_table(tab):\n%s",tabulator_as_table(tab));
1367     tabulator_free(tab);
1368     }
1369    
1370     fprintf(stderr,"---------------- TEST-tabulator.h: %d ----------------\n",__LINE__);
1371     {
1372     char ***tab = tabulator_new_from_table("+ ONE TWO THREE\n| 1 2 3\n| 11 22 33 \n",
1373     TABULATOR_FLAG_DEBUG_STDERR);
1374     tabulator_debug_dump(tab);
1375     fprintf(stderr, "tabulator_as_table(tab):\n%s",tabulator_as_table(tab));
1376    
1377     fprintf(stderr,"---------------- TEST-tabulator.h: %d ----------------\n",__LINE__);
1378     {
1379     char ***tab2 = tabulator_copy_using_header(tab, "",
1380     TABULATOR_FLAG_DEBUG_STDERR);
1381     tabulator_debug_dump(tab2);
1382     if(tab2) fprintf(stderr, "tabulator_as_table(tab2):\n%s",tabulator_as_table(tab2));
1383     tabulator_free(tab2);
1384     }
1385     fprintf(stderr,"---------------- TEST-tabulator.h: %d ----------------\n",__LINE__);
1386     {
1387     char ***tab2 = tabulator_copy_using_header(tab, "TWO",
1388     TABULATOR_FLAG_DEBUG_STDERR);
1389     tabulator_debug_dump(tab2);
1390     fprintf(stderr, "tabulator_as_table(tab2):\n%s",tabulator_as_table(tab2));
1391     tabulator_free(tab2);
1392     }
1393     fprintf(stderr,"---------------- TEST-tabulator.h: %d ----------------\n",__LINE__);
1394     {
1395     char ***tab2 = tabulator_copy_using_header(tab, "THREE TWO ONE",
1396     TABULATOR_FLAG_DEBUG_STDERR);
1397     tabulator_debug_dump(tab2);
1398     if(tab2) fprintf(stderr, "tabulator_as_table(tab2):\n%s",tabulator_as_table(tab2));
1399     tabulator_free(tab2);
1400     }
1401    
1402     fprintf(stderr,"---------------- TEST-tabulator.h: %d ----------------\n",__LINE__);
1403     {
1404     char ***tab2 = tabulator_new_from_table_using_header(tabulator_as_table(tab),
1405     "THREE ONE TWO",
1406     TABULATOR_FLAG_DEBUG_STDERR);
1407     tabulator_debug_dump(tab2);
1408     if(tab2) fprintf(stderr, "tabulator_as_table(tab2):\n%s",tabulator_as_table(tab2));
1409     tabulator_free(tab2);
1410     }
1411    
1412     fprintf(stderr,"---------------- TEST-tabulator.h: %d ----------------\n",__LINE__);
1413     {
1414     char ***tab2 = tabulator_new_from_table_using_header(tabulator_as_table(tab),
1415     "THREE FOUR TWO",
1416     TABULATOR_FLAG_DEBUG_STDERR);
1417     tabulator_debug_dump(tab2);
1418     if(tab2) fprintf(stderr, "tabulator_as_table(tab2):\n%s",tabulator_as_table(tab2));
1419     tabulator_free(tab2);
1420     }
1421    
1422     fprintf(stderr,"---------------- TEST-tabulator.h: %d ----------------\n",__LINE__);
1423     {
1424     char ***tab2 = tabulator_new_from_table_using_header(tabulator_as_table(tab),
1425     "THREE FOUR TWO",
1426     TABULATOR_FLAG_STRICT |
1427     TABULATOR_FLAG_DEBUG_STDERR);
1428     tabulator_debug_dump(tab2);
1429     if(tab2) fprintf(stderr, "tabulator_as_table(tab2):\n%s",tabulator_as_table(tab2));
1430     tabulator_free(tab2);
1431     }
1432     tabulator_free(tab);
1433     }
1434    
1435     fprintf(stderr,"---------------- TEST-tabulator.h: %d ----------------\n",__LINE__);
1436     {
1437     char ***tab = tabulator_new_from_header(" \"ONE\" \"TWO DAY\" DONT\\\\DO\\\"IT ",
1438     TABULATOR_FLAG_DEBUG_STDERR);
1439     tabulator_add_row(&tab, "1 2");
1440     tabulator_debug_dump(tab);
1441     if(tab) fprintf(stderr, "tabulator_as_table(tab):\n%s",tabulator_as_table(tab));
1442     fprintf(stderr,"---------------- TEST-tabulator.h: %d ----------------\n",__LINE__);
1443     {
1444     char ***tab2 = tabulator_new_from_table_using_header(tabulator_as_table(tab),
1445     "\"TWO DAY\" ONE",
1446     TABULATOR_FLAG_STRICT |
1447     TABULATOR_FLAG_DEBUG_STDERR);
1448     tabulator_debug_dump(tab2);
1449     if(tab2) fprintf(stderr, "tabulator_as_table(tab2):\n%s",tabulator_as_table(tab2));
1450     tabulator_free(tab2);
1451     }
1452     tabulator_free(tab);
1453     }
1454    
1455     fprintf(stderr,"---------------- TEST-tabulator.h: %d ----------------\n",__LINE__);
1456     {
1457     char ***tab = tabulator_new_from_header(" \"TWO\\037\" hi\\nyou ",
1458     TABULATOR_FLAG_DEBUG_STDERR);
1459     tabulator_add_row(&tab, "1 2");
1460     tabulator_debug_dump(tab);
1461     if(tab) fprintf(stderr, "tabulator_as_table(tab):\n%s",tabulator_as_table(tab));
1462     fprintf(stderr,"---------------- TEST-tabulator.h: %d ----------------\n",__LINE__);
1463     {
1464     char ***tab2 = tabulator_new_from_table_using_header(tabulator_as_table(tab),
1465     " hi\\nyou \"TWO\\037\"",
1466     TABULATOR_FLAG_STRICT |
1467     TABULATOR_FLAG_DEBUG_STDERR);
1468     tabulator_debug_dump(tab2);
1469     if(tab2) fprintf(stderr, "tabulator_as_table(tab2):\n%s",tabulator_as_table(tab2));
1470     tabulator_free(tab2);
1471     }
1472     tabulator_free(tab);
1473     }
1474    
1475     fprintf(stderr,"---------------- TEST-tabulator.h: %d ----------------\n",__LINE__);
1476     {
1477     char ***tab = tabulator_new_from_table("+ A \"Ab \" Abc\n| 1 2 3\n|4 5 6\n",
1478     TABULATOR_FLAG_DEBUG_STDERR);
1479     tabulator_debug_dump(tab);
1480     fprintf(stderr, "tabulator_as_table(tab):\n%s",tabulator_as_table(tab));
1481    
1482     fprintf(stderr, "%d\n", tabulator_set(tab,-1,1,"Abx"));
1483     fprintf(stderr, "%d\n", tabulator_set(tab,1,1,"xxx"));
1484    
1485     tabulator_debug_dump(tab);
1486     fprintf(stderr, "tabulator_as_table(tab):\n%s",tabulator_as_table(tab));
1487    
1488     tabulator_free(tab);
1489     }
1490    
1491     fprintf(stderr,"---------------- TEST-tabulator.h: %d ----------------\n",__LINE__);
1492     {
1493     char ***tab = tabulator_new_from_table("+ A \"\" Abc\n| 1\n",
1494     TABULATOR_FLAG_DEBUG_STDERR);
1495     tabulator_debug_dump(tab);
1496     fprintf(stderr, "tabulator_as_table(tab):\n%s",tabulator_as_table(tab));
1497     tabulator_free(tab);
1498     }
1499    
1500     fprintf(stderr,"---------------- TEST-tabulator.h: %d ----------------\n",__LINE__);
1501     {
1502     char ***tab = tabulator_new_from_header("AT_ID1 AT_ID2 DISTANCE COLOR DRAW_TYPE", 0);
1503    
1504     tabulator_add_row(&tab, "31 12 1 red 6");
1505     tabulator_add_row(&tab, "3 8 2 blue 7");
1506     tabulator_add_row(&tab, "1 2 2 red 3");
1507    
1508     fprintf(stderr,"%s", tabulator_as_table(tab));
1509    
1510     fprintf(stderr,"---------------- TEST-tabulator.h: %d ----------------\n",__LINE__);
1511     {
1512     char ***tab2 = tabulator_new_from_table_using_header(tabulator_as_table(tab),
1513     "COLOR AT_ID2 DRAW_TYPE", 0);
1514     if(tab) {
1515     {
1516     int n_row = tabulator_height(tab2);
1517     int i;
1518     for(i=0;i<n_row;i++) {
1519     fprintf(stderr,"Row %d: COLOR=%s AT_ID2=%s DRAW_TYPE=%s\n",i, tab[i][0], tab[i][1], tab[i][2]);
1520     }
1521     tabulator_free(tab2);
1522     }
1523     }
1524     }
1525    
1526     tabulator_free(tab);
1527     }
1528    
1529     fprintf(stderr,"---------------- TEST-tabulator.h: %d ----------------\n",__LINE__);
1530     {
1531     char ***tab = tabulator_new_from_header("AT_ID1 AT_ID2 DISTANCE COLOR DRAW_TYPE", 0);
1532    
1533     tabulator_add_row(&tab, "31 12 1 red 6");
1534     tabulator_add_row(&tab, "3 8 2 blue 7");
1535     tabulator_add_row(&tab, "1 2 2 red 3");
1536    
1537     fprintf(stderr,"%s", tabulator_as_table(tab));
1538    
1539     fprintf(stderr,"---------------- TEST-tabulator.h: %d ----------------\n",__LINE__);
1540     {
1541     char ***tab2 = tabulator_new_from_table_using_header(tabulator_as_table(tab),
1542     "COLOR AT_ID2 DRAW_TYPE", 0);
1543     if(tab) {
1544     {
1545     int n_row = tabulator_height(tab2);
1546     int i;
1547     for(i=0;i<n_row;i++) {
1548     fprintf(stderr,"Row %d: COLOR=%s AT_ID2=%s DRAW_TYPE=%s\n",i, tab[i][0], tab[i][1], tab[i][2]);
1549     }
1550     tabulator_free(tab2);
1551     }
1552     }
1553     }
1554    
1555     tabulator_free(tab);
1556     }
1557    
1558    
1559     fprintf(stderr,"---------------- TEST-tabulator.h: %d ----------------\n",__LINE__);
1560     /* confirm the ability to read and write tables of all different
1561     size, with all sorts of zero-terminated strings */
1562    
1563     {
1564     int a,cnt=0;
1565     for(a=0;a<5000;a++) {
1566     int width = random() & 0x0F;
1567     int height = random() & 0x3F;
1568     char *buf = malloc(0x10*width*(height+1));
1569    
1570     char ***tab = tabulator_new_with_size(height,width,0);
1571     if(buf && tab) {
1572     int i;
1573     for(i=-1;i<height;i++) {
1574     int j;
1575     for(j=0;j<width;j++) {
1576     int stlen = (random() & 0x3);
1577     char *str = buf + (((i+1)*width)+j)*0xF;
1578     int s;
1579     for(s=0;s<stlen;s++) {
1580     str[s] = (random()&0xFE)+1;
1581     }
1582     str[stlen] = 0;
1583     tabulator_set(tab,i,j,str);
1584     }
1585     }
1586    
1587     {
1588     char *tmp = tabulator_as_table(tab);
1589     char ***tab2 = tabulator_new_from_table(tmp,TABULATOR_FLAG_STRICT);
1590     char *tmp2 = tabulator_as_table(tab2);
1591     int valid = !strcmp(tmp,tmp2);
1592    
1593     if(valid) {
1594     for(i=-1;i<height;i++) {
1595     int j;
1596     for(j=0;j<width;j++) {
1597     char *str = buf + (((i+1)*width)+j)*0xF;
1598     valid = valid && (!strcmp(str,tab2[i][j]));
1599     tabulator_set(tab,i,j,str);
1600     }
1601     }
1602     }
1603    
1604     if(!valid) {
1605     fprintf(stderr,"INVALID: %dx%d\n",height,width);
1606     fprintf(stderr, "[\n%s]<\n%s>",tmp,tmp2);
1607     break;
1608     }
1609     tabulator_free(tab2);
1610     }
1611     free(buf);
1612     tabulator_free(tab);
1613     }
1614     cnt++;
1615     }
1616     fprintf(stderr,"Confirmed read/write conversion for %d tables.\n",cnt);
1617     }
1618    
1619    
1620     fprintf(stderr,"---------------- TEST-tabulator.h: %d ----------------\n",__LINE__);
1621     /* confirm the ability to read and write tables of all different
1622     size to and from a file with all sorts of zero-terminated strings */
1623    
1624     {
1625     int a,cnt=0;
1626     for(a=0;a<12;a++) {
1627    
1628     /* first write */
1629     int width = a;
1630     int height = a;
1631     FILE *f = fopen("tabulator.tmp","w");
1632     if(f) {
1633     for(width=0;width<a;width++) {
1634     for(height=0;height<a;height++) {
1635     char *buf = malloc(0x10*width*(height+1));
1636    
1637     char ***tab = tabulator_new_with_size(height,width,0);
1638     if(buf && tab) {
1639     int i;
1640     for(i=-1;i<height;i++) {
1641     int j;
1642     for(j=0;j<width;j++) {
1643     int stlen = (random() & 0x3);
1644     char *str = buf + (((i+1)*width)+j)*0xF;
1645     int s;
1646     for(s=0;s<stlen;s++) {
1647     str[s] = (random()&0xFE)+1;
1648     }
1649     str[stlen] = 0;
1650     tabulator_set(tab,i,j,str);
1651     }
1652     }
1653     {
1654     char *tmp = tabulator_as_table(tab);
1655     fwrite(tmp,strlen(tmp),1,f);
1656     }
1657     tabulator_free(tab);
1658     }
1659     free(buf);
1660     }
1661     }
1662     fclose(f);
1663    
1664     f=fopen("tabulator.tmp","r");
1665     if(f) {
1666     for(width=0;width<a;width++) {
1667     for(height=0;height<a;height++) {
1668     char ***tab2 = tabulator_new_from_file(f,TABULATOR_FLAG_STRICT);
1669    
1670     if(!tab2) {
1671     fprintf(stderr,"UNABLE TO READ TABLE FROM FILE %dx%d\n",height,width);
1672     } else if((tabulator_width(tab2)!=width)||
1673     (tabulator_height(tab2)!=height)) {
1674     fprintf(stderr,"INVALID: %dx%d\n",height,width);
1675     }
1676     if(tab2) {
1677     cnt++;
1678     tabulator_free(tab2);
1679     }
1680     }
1681     }
1682     if(tabulator_new_from_file(f,TABULATOR_FLAG_STRICT)) { /* should always fail */
1683     fprintf(stderr,"SHOULD HAVE FAILED\n");
1684     }
1685     fclose(f);
1686     }
1687    
1688     f=fopen("tabulator.tmp","r");
1689     if(f) {
1690     for(width=0;width<a;width++) {
1691     for(height=0;height<a;height++) {
1692     char ***tab2 = tabulator_new_from_file_using_header(f,"1 2 3 4 5",0);
1693    
1694     if(!tab2) {
1695     fprintf(stderr,"UNABLE TO READ TABLE FROM FILE %dx%d\n",height,width);
1696     } else if((tabulator_width(tab2)!=5)||
1697     (tabulator_height(tab2)!=height)) {
1698     fprintf(stderr,"INVALID: %dx%d\n",height,width);
1699     }
1700     if(tab2) {
1701     cnt++;
1702     tabulator_free(tab2);
1703     }
1704     }
1705     }
1706     if(tabulator_new_from_file(f,TABULATOR_FLAG_STRICT)) { /* should always fail */
1707     fprintf(stderr,"SHOULD HAVE FAILED\n");
1708     }
1709     fclose(f);
1710     }
1711    
1712    
1713     }
1714     }
1715     unlink("tabulator.tmp");
1716     fprintf(stderr,"Confirmed file read/write conversion %d times.\n",cnt);
1717     }
1718    
1719     fprintf(stderr,"HEAP BLOCKS ALLOCATED: max=%d, now=%d\n",alloc_max, alloc_cnt);
1720     return 0;
1721     }
1722    
1723     #endif