ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/freemol/trunk/src/mengine/src/tabulator.h
Revision: 126
Committed: Thu Jun 18 18:53:19 2009 UTC (11 years ago) by wdelano
File size: 50512 byte(s)
Log Message:
Line File contents
1 #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 #if 0
970 static void tabulator_debug_dump(char ***tab)
971 {
972 if(tab) {
973 tabulator *I = ((tabulator*)(tab+1))-1;
974 fprintf(stderr,"dump: I->n_col=%d, I->n_row=%d \n",I->n_col, I->n_row);
975 if(I->n_col) {
976 char ***row = I->row;
977 while(*row) {
978 char **col = *(row++);
979 fprintf(stderr,"dump: ");
980 while(*col) {
981 fprintf(stderr,"[%s] ",*(col++));
982 }
983 fprintf(stderr,"\n");
984 }
985 }
986 } else {
987 fprintf(stderr,"dump: null tabulator\n");
988 }
989 }
990 #endif
991
992 int tabulator_add_row(char ****tab_ptr, char *line)
993 {
994 if(*tab_ptr) {
995 tabulator *I = ((tabulator*)((*tab_ptr)+1))-1;
996 int line_len = strlen(line);
997 int ok = true;
998
999 if(I->debug) {
1000 fprintf(I->debug, "add_row(^1,\"%s\"):\n",line);
1001 }
1002
1003 ok = tabulator_grow_text(I, line_len + 1);
1004
1005 if(ok) ok = tabulator_grow_col(I, 1);
1006
1007 if(ok) {
1008
1009 /* extend row storage as necessary */
1010 int new_row_size = (I->n_row + 3);
1011 if(new_row_size > I->row_max) {
1012 int new_row_max = (new_row_size + (new_row_size>>1));
1013 tabulator *new_I = (tabulator*)realloc(I, sizeof(tabulator)
1014 + sizeof(char***) * new_row_max);
1015 if(new_I) {
1016 I = new_I;
1017 memset(I->row + I->row_max, 0, sizeof(char**) * (new_row_max - I->row_max));
1018 I->row_max = new_row_max;
1019 } else {
1020 ok = false;
1021 }
1022 }
1023
1024 if(ok) {
1025
1026 /* tokenize and link */
1027 char *new_text = I->text + I->text_size;
1028 char **col_start = I->col + (I->n_col+1) * (I->n_row+1);
1029 int col_cnt = I->n_col;
1030 I->row[ 1 + I->n_row++ ] = col_start;
1031
1032 /* copy the source text */
1033 memcpy(new_text, line, line_len);
1034 I->text_size += line_len + 1;
1035
1036 /* link all n_columns to text token (handles blanks) */
1037 while(col_cnt--) {
1038
1039 /* eliminate leading whitespace */
1040 while(*new_text && tabulator_char_is_white(*new_text)) new_text++;
1041
1042 if(!*new_text) {
1043 *(col_start++) = I->text; /* or substitute a blank string */
1044 } else {
1045 char *tmp = new_text;
1046 *(col_start++) = new_text; /* copy the token */
1047 tabulator_copy_and_unquote(&tmp, &new_text);
1048 }
1049 }
1050 }
1051 }
1052 (*tab_ptr) = ((char***)(I+1))-1;
1053 return ok;
1054 } else {
1055 return false;
1056 }
1057 }
1058
1059 static int tabulator_escaped_strlen(char *src,int *quotes)
1060 {
1061 if(!src[0]) {
1062 return 2;
1063 } else {
1064 int base_len = strlen(src);
1065 char ch;
1066 int quotes_required = false;
1067 int extra_len = 0;
1068
1069 while(*src) {
1070 ch = *(src++);
1071 if(tabulator_char_is_white(ch)) {
1072 quotes_required = true;
1073 switch(ch) {
1074 case ' ': /* space */
1075 break;
1076 case '\n': /* newline */
1077 extra_len++;
1078 break;
1079 default:
1080 extra_len +=3; /* will use \xxx */
1081 break;
1082 }
1083 } else {
1084 switch(ch) {
1085 case '"':
1086 case '\\':
1087 extra_len++;
1088 break;
1089 }
1090 }
1091 }
1092 if(quotes_required) extra_len += 2;
1093 if(quotes) {
1094 *quotes = quotes_required;
1095 }
1096 return base_len + extra_len;
1097 }
1098 }
1099
1100 static void tabulator_escaped_copy(char *dst, int width, char *src)
1101 {
1102 int need_quotes = false;
1103 int len = tabulator_escaped_strlen(src,&need_quotes);
1104 dst += (width-len);
1105 if(!src[0]) {
1106 dst[0]='"';
1107 dst[1]='"';
1108 } else {
1109 if(need_quotes) {
1110 *(dst++) = '"';
1111 }
1112 while(*src) {
1113 unsigned char ch = *(src++);
1114 if(tabulator_char_is_white(ch)) {
1115 switch(ch) {
1116 case ' ': /* space */
1117 *(dst++)=ch;
1118 break;
1119 case '\n': /* newline */
1120 *(dst++)='\\';
1121 *(dst++)='n';
1122 break;
1123 default:
1124 *(dst++)='\\';
1125 *(dst++)=((ch>>6)&0x7)+'0';
1126 *(dst++)=((ch>>3)&0x7)+'0';
1127 *(dst++)=((ch>>0)&0x7)+'0';
1128 break;
1129 }
1130 } else {
1131 switch(ch) {
1132 case '"':
1133 case '\\':
1134 *(dst++)='\\';
1135 *(dst++)=ch;
1136 break;
1137 default:
1138 *(dst++)=ch;
1139 break;
1140 }
1141 }
1142 }
1143 if(need_quotes) {
1144 *(dst++) = '"';
1145 }
1146 }
1147 }
1148
1149 char *tabulator_as_table(char ***tab)
1150 {
1151 char *result = NULL;
1152 if(tab) {
1153 tabulator *I = ((tabulator*)(tab+1))-1;
1154 int *max_width = (int*)w_calloc(sizeof(int),I->n_col);
1155
1156 if(max_width) {
1157 if(I->debug) {
1158 fprintf(I->debug, "as_table(^1): I->n_row=%d, I->n_col=%d\n",
1159 I->n_row, I->n_col);
1160 }
1161
1162 /* measure field widths */
1163 {
1164 char ***row = I->row;
1165 while(*row) {
1166 char **col = *(row++);
1167 int *mw = max_width;
1168 while(*col) {
1169 int fld_len = tabulator_escaped_strlen(*col,NULL);
1170 if(fld_len > *mw) *mw = fld_len;
1171 mw++;
1172 col++;
1173 }
1174 }
1175 }
1176
1177 {
1178 int output_size = 0;
1179
1180 /* compute total table size */
1181 {
1182 int i, line_width = 0;
1183 for(i=0;i<I->n_col;i++) {
1184 line_width += (++max_width[i]);
1185 }
1186 output_size = ( (line_width + 2) * /* +2 for prefix & newline */
1187 (I->n_row + 1) /* +1 = for headers */
1188 + 1); /* and then +1 for terminal newline */
1189 }
1190
1191 /* allocate buffer for the output */
1192
1193 if(I->output) w_free(I->output);
1194 result = I->output = w_malloc(output_size+1);
1195
1196 if(result) {
1197 char *dst = result;
1198
1199 /* fill with spaces */
1200 memset(result, 32, output_size);
1201
1202 /* terminate */
1203 result[output_size] = 0;
1204
1205 /* copy fields into the output at proper locations */
1206
1207 {
1208 char ***row = I->row;
1209 char token = TABULATOR_TABLE_TOKEN;
1210 while(*row) {
1211 char **col = *(row++);
1212 int *mw = max_width;
1213 *(dst++) = token;
1214 while(*col) {
1215 tabulator_escaped_copy(dst, *mw, *(col++));
1216 dst += *(mw++);
1217 }
1218 *(dst++) = '\n'; /* row-ending newline */
1219 token = TABULATOR_LINE_TOKEN;
1220 }
1221 }
1222 *(dst++) = '\n'; /* table-ending newline */
1223 }
1224 }
1225 w_free(max_width);
1226 }
1227 }
1228 return result;
1229 }
1230
1231 int tabulator_set(char ***tab, int row, int col, char *str)
1232 {
1233 if(tab) {
1234 tabulator *I = ((tabulator*)(tab+1))-1;
1235 if( (row>-2) && (row<I->n_row) && (col>=0) && (col<I->n_col) ) {
1236 int len = strlen(str) + 1;
1237 if(tabulator_grow_text(I,len)) {
1238 char *dst = I->text + I->text_size;
1239 memcpy(dst, str, len);
1240 tab[row][col] = dst;
1241 I->text_size += len;
1242 return true;
1243 }
1244 }
1245 }
1246 return false;
1247 }
1248
1249 #endif
1250
1251 #ifdef TABULATOR_INCLUDE_UNIT_TEST
1252 int main(int argc, char **argv)
1253 {
1254
1255 fprintf(stderr,"---------------- TEST-tabulator.h: %d ----------------\n",__LINE__);
1256 {
1257 char ***tab = tabulator_new_from_header("",
1258 TABULATOR_FLAG_DEBUG_STDERR);
1259 tabulator_debug_dump(tab);
1260 tabulator_free(tab);
1261 }
1262
1263 fprintf(stderr,"---------------- TEST-tabulator.h: %d ----------------\n",__LINE__);
1264 {
1265 char ***tab = tabulator_new_from_header(" ONE ",
1266 TABULATOR_FLAG_DEBUG_STDERR);
1267 tabulator_debug_dump(tab);
1268 tabulator_free(tab);
1269 }
1270
1271 fprintf(stderr,"---------------- TEST-tabulator.h: %d ----------------\n",__LINE__);
1272 {
1273 char ***tab = tabulator_new_from_header("TWO THREE",
1274 TABULATOR_FLAG_DEBUG_STDERR);
1275 tabulator_debug_dump(tab);
1276 tabulator_free(tab);
1277 }
1278
1279 fprintf(stderr,"---------------- TEST-tabulator.h: %d ----------------\n",__LINE__);
1280 {
1281 char buffer[255];
1282 char ***tab = tabulator_new_from_header(" ONE FIELD_TWO THREE FOUR ",
1283 TABULATOR_FLAG_DEBUG_STDERR);
1284
1285 tabulator_debug_dump(tab);
1286 sprintf(buffer," asdf 245.6 ");
1287 tabulator_add_row(&tab, buffer);
1288 tabulator_debug_dump(tab);
1289
1290 sprintf(buffer,"-45.6 CA 235 01");
1291 tabulator_add_row(&tab, buffer);
1292 tabulator_debug_dump(tab);
1293
1294 sprintf(buffer,"blah1 blah2 blah3 blah4 blah5 blah6");
1295 tabulator_add_row(&tab, buffer);
1296 tabulator_debug_dump(tab);
1297
1298 fprintf(stderr, "tabulator_as_table(tab):\n%s",tabulator_as_table(tab));
1299 tabulator_free(tab);
1300 }
1301
1302 fprintf(stderr,"---------------- TEST-tabulator.h: %d ----------------\n",__LINE__);
1303 {
1304 char ***tab = tabulator_new_from_table("",
1305 TABULATOR_FLAG_DEBUG_STDERR);
1306 fprintf(stderr, "tab^%d\n",tab&&1);
1307 }
1308
1309 fprintf(stderr,"---------------- TEST-tabulator.h: %d ----------------\n",__LINE__);
1310 {
1311 char ***tab = tabulator_new_from_table("+",
1312 TABULATOR_FLAG_DEBUG_STDERR);
1313 tabulator_debug_dump(tab);
1314 fprintf(stderr, "tabulator_as_table(tab):\n%s",tabulator_as_table(tab));
1315 tabulator_free(tab);
1316 }
1317
1318 fprintf(stderr,"---------------- TEST-tabulator.h: %d ----------------\n",__LINE__);
1319 {
1320 char ***tab = tabulator_new_from_table("+\n|\n|\n",
1321 TABULATOR_FLAG_DEBUG_STDERR);
1322 tabulator_debug_dump(tab);
1323 fprintf(stderr, "tabulator_as_table(tab):\n%s",tabulator_as_table(tab));
1324 tabulator_free(tab);
1325 }
1326
1327 fprintf(stderr,"---------------- TEST-tabulator.h: %d ----------------\n",__LINE__);
1328 {
1329 char ***tab = tabulator_new_from_table("+ TEST",
1330 TABULATOR_FLAG_DEBUG_STDERR);
1331 tabulator_debug_dump(tab);
1332 fprintf(stderr, "tabulator_as_table(tab):\n%s",tabulator_as_table(tab));
1333 tabulator_free(tab);
1334 }
1335
1336 fprintf(stderr,"---------------- TEST-tabulator.h: %d ----------------\n",__LINE__);
1337 {
1338 char ***tab = tabulator_new_from_table("+ TEST TOO",
1339 TABULATOR_FLAG_DEBUG_STDERR);
1340 tabulator_debug_dump(tab);
1341 fprintf(stderr, "tabulator_as_table(tab):\n%s",tabulator_as_table(tab));
1342 tabulator_free(tab);
1343 }
1344
1345 fprintf(stderr,"---------------- TEST-tabulator.h: %d ----------------\n",__LINE__);
1346 {
1347 char ***tab = tabulator_new_from_table("+ A Ab Abc\n| 1\n",
1348 TABULATOR_FLAG_DEBUG_STDERR);
1349 tabulator_debug_dump(tab);
1350 fprintf(stderr, "tabulator_as_table(tab):\n%s",tabulator_as_table(tab));
1351 tabulator_free(tab);
1352 }
1353
1354 fprintf(stderr,"---------------- TEST-tabulator.h: %d ----------------\n",__LINE__);
1355 {
1356 char ***tab = tabulator_new_from_table("+ A Ab Abc\n| 1 2 3\n|4 5 6\n",
1357 TABULATOR_FLAG_DEBUG_STDERR);
1358 tabulator_debug_dump(tab);
1359 fprintf(stderr, "tabulator_as_table(tab):\n%s",tabulator_as_table(tab));
1360 tabulator_free(tab);
1361 }
1362
1363 fprintf(stderr,"---------------- TEST-tabulator.h: %d ----------------\n",__LINE__);
1364 {
1365 char ***tab = tabulator_new_from_table("+ Abc Ab A\n| 1 2 3 4 5 6\n|7 8\n",
1366 TABULATOR_FLAG_DEBUG_STDERR);
1367 tabulator_debug_dump(tab);
1368 fprintf(stderr, "tabulator_as_table(tab):\n%s",tabulator_as_table(tab));
1369 tabulator_free(tab);
1370 }
1371
1372 fprintf(stderr,"---------------- TEST-tabulator.h: %d ----------------\n",__LINE__);
1373 {
1374 char ***tab = tabulator_new_from_table("+ ONE TWO THREE\n| 1 2 3\n| 11 22 33 \n",
1375 TABULATOR_FLAG_DEBUG_STDERR);
1376 tabulator_debug_dump(tab);
1377 fprintf(stderr, "tabulator_as_table(tab):\n%s",tabulator_as_table(tab));
1378
1379 fprintf(stderr,"---------------- TEST-tabulator.h: %d ----------------\n",__LINE__);
1380 {
1381 char ***tab2 = tabulator_copy_using_header(tab, "",
1382 TABULATOR_FLAG_DEBUG_STDERR);
1383 tabulator_debug_dump(tab2);
1384 if(tab2) fprintf(stderr, "tabulator_as_table(tab2):\n%s",tabulator_as_table(tab2));
1385 tabulator_free(tab2);
1386 }
1387 fprintf(stderr,"---------------- TEST-tabulator.h: %d ----------------\n",__LINE__);
1388 {
1389 char ***tab2 = tabulator_copy_using_header(tab, "TWO",
1390 TABULATOR_FLAG_DEBUG_STDERR);
1391 tabulator_debug_dump(tab2);
1392 fprintf(stderr, "tabulator_as_table(tab2):\n%s",tabulator_as_table(tab2));
1393 tabulator_free(tab2);
1394 }
1395 fprintf(stderr,"---------------- TEST-tabulator.h: %d ----------------\n",__LINE__);
1396 {
1397 char ***tab2 = tabulator_copy_using_header(tab, "THREE TWO ONE",
1398 TABULATOR_FLAG_DEBUG_STDERR);
1399 tabulator_debug_dump(tab2);
1400 if(tab2) fprintf(stderr, "tabulator_as_table(tab2):\n%s",tabulator_as_table(tab2));
1401 tabulator_free(tab2);
1402 }
1403
1404 fprintf(stderr,"---------------- TEST-tabulator.h: %d ----------------\n",__LINE__);
1405 {
1406 char ***tab2 = tabulator_new_from_table_using_header(tabulator_as_table(tab),
1407 "THREE ONE TWO",
1408 TABULATOR_FLAG_DEBUG_STDERR);
1409 tabulator_debug_dump(tab2);
1410 if(tab2) fprintf(stderr, "tabulator_as_table(tab2):\n%s",tabulator_as_table(tab2));
1411 tabulator_free(tab2);
1412 }
1413
1414 fprintf(stderr,"---------------- TEST-tabulator.h: %d ----------------\n",__LINE__);
1415 {
1416 char ***tab2 = tabulator_new_from_table_using_header(tabulator_as_table(tab),
1417 "THREE FOUR TWO",
1418 TABULATOR_FLAG_DEBUG_STDERR);
1419 tabulator_debug_dump(tab2);
1420 if(tab2) fprintf(stderr, "tabulator_as_table(tab2):\n%s",tabulator_as_table(tab2));
1421 tabulator_free(tab2);
1422 }
1423
1424 fprintf(stderr,"---------------- TEST-tabulator.h: %d ----------------\n",__LINE__);
1425 {
1426 char ***tab2 = tabulator_new_from_table_using_header(tabulator_as_table(tab),
1427 "THREE FOUR TWO",
1428 TABULATOR_FLAG_STRICT |
1429 TABULATOR_FLAG_DEBUG_STDERR);
1430 tabulator_debug_dump(tab2);
1431 if(tab2) fprintf(stderr, "tabulator_as_table(tab2):\n%s",tabulator_as_table(tab2));
1432 tabulator_free(tab2);
1433 }
1434 tabulator_free(tab);
1435 }
1436
1437 fprintf(stderr,"---------------- TEST-tabulator.h: %d ----------------\n",__LINE__);
1438 {
1439 char ***tab = tabulator_new_from_header(" \"ONE\" \"TWO DAY\" DONT\\\\DO\\\"IT ",
1440 TABULATOR_FLAG_DEBUG_STDERR);
1441 tabulator_add_row(&tab, "1 2");
1442 tabulator_debug_dump(tab);
1443 if(tab) fprintf(stderr, "tabulator_as_table(tab):\n%s",tabulator_as_table(tab));
1444 fprintf(stderr,"---------------- TEST-tabulator.h: %d ----------------\n",__LINE__);
1445 {
1446 char ***tab2 = tabulator_new_from_table_using_header(tabulator_as_table(tab),
1447 "\"TWO DAY\" ONE",
1448 TABULATOR_FLAG_STRICT |
1449 TABULATOR_FLAG_DEBUG_STDERR);
1450 tabulator_debug_dump(tab2);
1451 if(tab2) fprintf(stderr, "tabulator_as_table(tab2):\n%s",tabulator_as_table(tab2));
1452 tabulator_free(tab2);
1453 }
1454 tabulator_free(tab);
1455 }
1456
1457 fprintf(stderr,"---------------- TEST-tabulator.h: %d ----------------\n",__LINE__);
1458 {
1459 char ***tab = tabulator_new_from_header(" \"TWO\\037\" hi\\nyou ",
1460 TABULATOR_FLAG_DEBUG_STDERR);
1461 tabulator_add_row(&tab, "1 2");
1462 tabulator_debug_dump(tab);
1463 if(tab) fprintf(stderr, "tabulator_as_table(tab):\n%s",tabulator_as_table(tab));
1464 fprintf(stderr,"---------------- TEST-tabulator.h: %d ----------------\n",__LINE__);
1465 {
1466 char ***tab2 = tabulator_new_from_table_using_header(tabulator_as_table(tab),
1467 " hi\\nyou \"TWO\\037\"",
1468 TABULATOR_FLAG_STRICT |
1469 TABULATOR_FLAG_DEBUG_STDERR);
1470 tabulator_debug_dump(tab2);
1471 if(tab2) fprintf(stderr, "tabulator_as_table(tab2):\n%s",tabulator_as_table(tab2));
1472 tabulator_free(tab2);
1473 }
1474 tabulator_free(tab);
1475 }
1476
1477 fprintf(stderr,"---------------- TEST-tabulator.h: %d ----------------\n",__LINE__);
1478 {
1479 char ***tab = tabulator_new_from_table("+ A \"Ab \" Abc\n| 1 2 3\n|4 5 6\n",
1480 TABULATOR_FLAG_DEBUG_STDERR);
1481 tabulator_debug_dump(tab);
1482 fprintf(stderr, "tabulator_as_table(tab):\n%s",tabulator_as_table(tab));
1483
1484 fprintf(stderr, "%d\n", tabulator_set(tab,-1,1,"Abx"));
1485 fprintf(stderr, "%d\n", tabulator_set(tab,1,1,"xxx"));
1486
1487 tabulator_debug_dump(tab);
1488 fprintf(stderr, "tabulator_as_table(tab):\n%s",tabulator_as_table(tab));
1489
1490 tabulator_free(tab);
1491 }
1492
1493 fprintf(stderr,"---------------- TEST-tabulator.h: %d ----------------\n",__LINE__);
1494 {
1495 char ***tab = tabulator_new_from_table("+ A \"\" Abc\n| 1\n",
1496 TABULATOR_FLAG_DEBUG_STDERR);
1497 tabulator_debug_dump(tab);
1498 fprintf(stderr, "tabulator_as_table(tab):\n%s",tabulator_as_table(tab));
1499 tabulator_free(tab);
1500 }
1501
1502 fprintf(stderr,"---------------- TEST-tabulator.h: %d ----------------\n",__LINE__);
1503 {
1504 char ***tab = tabulator_new_from_header("AT_ID1 AT_ID2 DISTANCE COLOR DRAW_TYPE", 0);
1505
1506 tabulator_add_row(&tab, "31 12 1 red 6");
1507 tabulator_add_row(&tab, "3 8 2 blue 7");
1508 tabulator_add_row(&tab, "1 2 2 red 3");
1509
1510 fprintf(stderr,"%s", tabulator_as_table(tab));
1511
1512 fprintf(stderr,"---------------- TEST-tabulator.h: %d ----------------\n",__LINE__);
1513 {
1514 char ***tab2 = tabulator_new_from_table_using_header(tabulator_as_table(tab),
1515 "COLOR AT_ID2 DRAW_TYPE", 0);
1516 if(tab) {
1517 {
1518 int n_row = tabulator_height(tab2);
1519 int i;
1520 for(i=0;i<n_row;i++) {
1521 fprintf(stderr,"Row %d: COLOR=%s AT_ID2=%s DRAW_TYPE=%s\n",i, tab[i][0], tab[i][1], tab[i][2]);
1522 }
1523 tabulator_free(tab2);
1524 }
1525 }
1526 }
1527
1528 tabulator_free(tab);
1529 }
1530
1531 fprintf(stderr,"---------------- TEST-tabulator.h: %d ----------------\n",__LINE__);
1532 {
1533 char ***tab = tabulator_new_from_header("AT_ID1 AT_ID2 DISTANCE COLOR DRAW_TYPE", 0);
1534
1535 tabulator_add_row(&tab, "31 12 1 red 6");
1536 tabulator_add_row(&tab, "3 8 2 blue 7");
1537 tabulator_add_row(&tab, "1 2 2 red 3");
1538
1539 fprintf(stderr,"%s", tabulator_as_table(tab));
1540
1541 fprintf(stderr,"---------------- TEST-tabulator.h: %d ----------------\n",__LINE__);
1542 {
1543 char ***tab2 = tabulator_new_from_table_using_header(tabulator_as_table(tab),
1544 "COLOR AT_ID2 DRAW_TYPE", 0);
1545 if(tab) {
1546 {
1547 int n_row = tabulator_height(tab2);
1548 int i;
1549 for(i=0;i<n_row;i++) {
1550 fprintf(stderr,"Row %d: COLOR=%s AT_ID2=%s DRAW_TYPE=%s\n",i, tab[i][0], tab[i][1], tab[i][2]);
1551 }
1552 tabulator_free(tab2);
1553 }
1554 }
1555 }
1556
1557 tabulator_free(tab);
1558 }
1559
1560
1561 fprintf(stderr,"---------------- TEST-tabulator.h: %d ----------------\n",__LINE__);
1562 /* confirm the ability to read and write tables of all different
1563 size, with all sorts of zero-terminated strings */
1564
1565 {
1566 int a,cnt=0;
1567 for(a=0;a<5000;a++) {
1568 int width = random() & 0x0F;
1569 int height = random() & 0x3F;
1570 char *buf = malloc(0x10*width*(height+1));
1571
1572 char ***tab = tabulator_new_with_size(height,width,0);
1573 if(buf && tab) {
1574 int i;
1575 for(i=-1;i<height;i++) {
1576 int j;
1577 for(j=0;j<width;j++) {
1578 int stlen = (random() & 0x3);
1579 char *str = buf + (((i+1)*width)+j)*0xF;
1580 int s;
1581 for(s=0;s<stlen;s++) {
1582 str[s] = (random()&0xFE)+1;
1583 }
1584 str[stlen] = 0;
1585 tabulator_set(tab,i,j,str);
1586 }
1587 }
1588
1589 {
1590 char *tmp = tabulator_as_table(tab);
1591 char ***tab2 = tabulator_new_from_table(tmp,TABULATOR_FLAG_STRICT);
1592 char *tmp2 = tabulator_as_table(tab2);
1593 int valid = !strcmp(tmp,tmp2);
1594
1595 if(valid) {
1596 for(i=-1;i<height;i++) {
1597 int j;
1598 for(j=0;j<width;j++) {
1599 char *str = buf + (((i+1)*width)+j)*0xF;
1600 valid = valid && (!strcmp(str,tab2[i][j]));
1601 tabulator_set(tab,i,j,str);
1602 }
1603 }
1604 }
1605
1606 if(!valid) {
1607 fprintf(stderr,"INVALID: %dx%d\n",height,width);
1608 fprintf(stderr, "[\n%s]<\n%s>",tmp,tmp2);
1609 break;
1610 }
1611 tabulator_free(tab2);
1612 }
1613 free(buf);
1614 tabulator_free(tab);
1615 }
1616 cnt++;
1617 }
1618 fprintf(stderr,"Confirmed read/write conversion for %d tables.\n",cnt);
1619 }
1620
1621
1622 fprintf(stderr,"---------------- TEST-tabulator.h: %d ----------------\n",__LINE__);
1623 /* confirm the ability to read and write tables of all different
1624 size to and from a file with all sorts of zero-terminated strings */
1625
1626 {
1627 int a,cnt=0;
1628 for(a=0;a<12;a++) {
1629
1630 /* first write */
1631 int width = a;
1632 int height = a;
1633 FILE *f = fopen("tabulator.tmp","w");
1634 if(f) {
1635 for(width=0;width<a;width++) {
1636 for(height=0;height<a;height++) {
1637 char *buf = malloc(0x10*width*(height+1));
1638
1639 char ***tab = tabulator_new_with_size(height,width,0);
1640 if(buf && tab) {
1641 int i;
1642 for(i=-1;i<height;i++) {
1643 int j;
1644 for(j=0;j<width;j++) {
1645 int stlen = (random() & 0x3);
1646 char *str = buf + (((i+1)*width)+j)*0xF;
1647 int s;
1648 for(s=0;s<stlen;s++) {
1649 str[s] = (random()&0xFE)+1;
1650 }
1651 str[stlen] = 0;
1652 tabulator_set(tab,i,j,str);
1653 }
1654 }
1655 {
1656 char *tmp = tabulator_as_table(tab);
1657 fwrite(tmp,strlen(tmp),1,f);
1658 }
1659 tabulator_free(tab);
1660 }
1661 free(buf);
1662 }
1663 }
1664 fclose(f);
1665
1666 f=fopen("tabulator.tmp","r");
1667 if(f) {
1668 for(width=0;width<a;width++) {
1669 for(height=0;height<a;height++) {
1670 char ***tab2 = tabulator_new_from_file(f,TABULATOR_FLAG_STRICT);
1671
1672 if(!tab2) {
1673 fprintf(stderr,"UNABLE TO READ TABLE FROM FILE %dx%d\n",height,width);
1674 } else if((tabulator_width(tab2)!=width)||
1675 (tabulator_height(tab2)!=height)) {
1676 fprintf(stderr,"INVALID: %dx%d\n",height,width);
1677 }
1678 if(tab2) {
1679 cnt++;
1680 tabulator_free(tab2);
1681 }
1682 }
1683 }
1684 if(tabulator_new_from_file(f,TABULATOR_FLAG_STRICT)) { /* should always fail */
1685 fprintf(stderr,"SHOULD HAVE FAILED\n");
1686 }
1687 fclose(f);
1688 }
1689
1690 f=fopen("tabulator.tmp","r");
1691 if(f) {
1692 for(width=0;width<a;width++) {
1693 for(height=0;height<a;height++) {
1694 char ***tab2 = tabulator_new_from_file_using_header(f,"1 2 3 4 5",0);
1695
1696 if(!tab2) {
1697 fprintf(stderr,"UNABLE TO READ TABLE FROM FILE %dx%d\n",height,width);
1698 } else if((tabulator_width(tab2)!=5)||
1699 (tabulator_height(tab2)!=height)) {
1700 fprintf(stderr,"INVALID: %dx%d\n",height,width);
1701 }
1702 if(tab2) {
1703 cnt++;
1704 tabulator_free(tab2);
1705 }
1706 }
1707 }
1708 if(tabulator_new_from_file(f,TABULATOR_FLAG_STRICT)) { /* should always fail */
1709 fprintf(stderr,"SHOULD HAVE FAILED\n");
1710 }
1711 fclose(f);
1712 }
1713
1714
1715 }
1716 }
1717 unlink("tabulator.tmp");
1718 fprintf(stderr,"Confirmed file read/write conversion %d times.\n",cnt);
1719 }
1720
1721 fprintf(stderr,"HEAP BLOCKS ALLOCATED: max=%d, now=%d\n",alloc_max, alloc_cnt);
1722 return 0;
1723 }
1724
1725 #endif