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 |