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