Line data Source code
1 : #include "common.h"
2 :
3 : #if HAVE_SYS_STAT_H
4 : #include <sys/stat.h>
5 : #endif /* HAVE_SYS_STAT_H */
6 :
7 : #include <bkpublic.h>
8 :
9 : #include <freetds/replacements.h>
10 :
11 : static void
12 : do_bind(CS_BLKDESC * blkdesc, int colnum, CS_INT host_format, CS_INT host_type, CS_INT host_maxlen,
13 : void *var_addr, CS_INT * var_len_addr, CS_SMALLINT * var_ind_addr);
14 : static void do_one_bind(CS_BLKDESC * blkdesc, int col, const char *name);
15 : static void do_type_bind(CS_BLKDESC * blkdesc, int col, const char *tok);
16 : static void free_dynamic_bounded(void);
17 : static void sendrow(CS_BLKDESC * blkdesc, const char *tok);
18 : static FILE *open_test_file(const char *filename);
19 : typedef enum
20 : {
21 : PART_END,
22 : PART_SQL,
23 : PART_BIND,
24 : PART_OUTPUT,
25 : } part_t;
26 : static part_t read_part_type(FILE * in);
27 : static char *read_part(FILE * in);
28 : static void read_line(char *buf, size_t buf_len, FILE * f);
29 : static char *append_string(char *s1, const char *s2);
30 : static char *get_output(CS_COMMAND * cmd, bool distinct);
31 : static void single_test(CS_CONNECTION * conn, CS_COMMAND * cmd, FILE * in);
32 :
33 : /*
34 : * Static data for insertion
35 : */
36 : static int not_null_bit = 1;
37 : static CS_INT l_not_null_bit = 4;
38 : static CS_SMALLINT i_not_null_bit = 0;
39 :
40 : static char not_null_char[] = "a char";
41 : static CS_INT l_not_null_char = 6;
42 : static CS_SMALLINT i_not_null_char = 0;
43 :
44 : static char not_null_varchar[] = "a varchar";
45 : static CS_INT l_not_null_varchar = 9;
46 : static CS_SMALLINT i_not_null_varchar = 0;
47 :
48 : static char not_null_datetime[] = "Dec 17 2003 3:44PM";
49 : static CS_INT l_not_null_datetime = 19;
50 : static CS_SMALLINT i_not_null_datetime = 0;
51 :
52 : static char not_null_smalldatetime[] = "Dec 17 2003 3:44PM";
53 : static CS_INT l_not_null_smalldatetime = 19;
54 : static CS_SMALLINT i_not_null_smalldatetime = 0;
55 :
56 : static char not_null_money[] = "12.34";
57 : static CS_INT l_not_null_money = 5;
58 : static CS_SMALLINT i_not_null_money = 0;
59 :
60 : static char not_null_smallmoney[] = "12.34";
61 : static CS_INT l_not_null_smallmoney = 5;
62 : static CS_SMALLINT i_not_null_smallmoney = 0;
63 :
64 : static char not_null_float[] = "12.34";
65 : static CS_INT l_not_null_float = 5;
66 : static CS_SMALLINT i_not_null_float = 0;
67 :
68 : static char not_null_real[] = "12.34";
69 : static CS_INT l_not_null_real = 5;
70 : static CS_SMALLINT i_not_null_real = 0;
71 :
72 : static char not_null_decimal[] = "12.34";
73 : static CS_INT l_not_null_decimal = 5;
74 : static CS_SMALLINT i_not_null_decimal = 0;
75 :
76 : static char not_null_numeric[] = "12.34";
77 : static CS_INT l_not_null_numeric = 5;
78 : static CS_SMALLINT i_not_null_numeric = 0;
79 :
80 : static int not_null_int = 1234;
81 : static CS_INT l_not_null_int = 4;
82 : static CS_SMALLINT i_not_null_int = 0;
83 :
84 : static int not_null_smallint = 1234;
85 : static CS_INT l_not_null_smallint = 4;
86 : static CS_SMALLINT i_not_null_smallint = 0;
87 :
88 : static int not_null_tinyint = 123;
89 : static CS_INT l_not_null_tinyint = 4;
90 : static CS_SMALLINT i_not_null_tinyint = 0;
91 :
92 : static CS_INT l_null_char = 0;
93 : static CS_SMALLINT i_null_char = -1;
94 :
95 : static CS_INT l_null_varchar = 0;
96 : static CS_SMALLINT i_null_varchar = -1;
97 :
98 : static CS_INT l_null_datetime = 0;
99 : static CS_SMALLINT i_null_datetime = -1;
100 :
101 : static CS_INT l_null_smalldatetime = 0;
102 : static CS_SMALLINT i_null_smalldatetime = -1;
103 :
104 : static CS_INT l_null_money = 0;
105 : static CS_SMALLINT i_null_money = -1;
106 :
107 : static CS_INT l_null_smallmoney = 0;
108 : static CS_SMALLINT i_null_smallmoney = -1;
109 :
110 : static CS_INT l_null_float = 0;
111 : static CS_SMALLINT i_null_float = -1;
112 :
113 : static CS_INT l_null_real = 0;
114 : static CS_SMALLINT i_null_real = -1;
115 :
116 : static CS_INT l_null_decimal = 0;
117 : static CS_SMALLINT i_null_decimal = -1;
118 :
119 : static CS_INT l_null_numeric = 0;
120 : static CS_SMALLINT i_null_numeric = -1;
121 :
122 : static CS_INT l_null_int = 0;
123 : static CS_SMALLINT i_null_int = -1;
124 :
125 : static CS_INT l_null_smallint = 0;
126 : static CS_SMALLINT i_null_smallint = -1;
127 :
128 : static CS_INT l_null_tinyint = 0;
129 : static CS_SMALLINT i_null_tinyint = -1;
130 :
131 : static char not_null_varbinary[] = "123456789";
132 : static CS_INT l_not_null_varbinary = 9;
133 : static CS_SMALLINT i_not_null_varbinary = 0;
134 :
135 : static void
136 32 : do_binds(CS_BLKDESC *blkdesc, FILE *in, bool *rows_sent)
137 : {
138 : char line[1024];
139 32 : int col = 1;
140 :
141 32 : *rows_sent = false;
142 : for (;;) {
143 : const char *tok;
144 :
145 446 : read_line(line, sizeof(line), in);
146 446 : if (strcmp(line, "--\n") == 0)
147 32 : return;
148 414 : tok = strtok(line, " \t\n");
149 :
150 414 : if (strcmp(tok, "--") == 0) {
151 : /* comment */
152 10 : continue;
153 404 : } else if (strncmp(tok, "SENDROW", 7) == 0) {
154 30 : sendrow(blkdesc, tok);
155 30 : col = 0;
156 30 : *rows_sent = true;
157 374 : } else if (strncmp(tok, "CS_", 3) == 0) {
158 40 : do_type_bind(blkdesc, col, tok);
159 : } else {
160 334 : do_one_bind(blkdesc, col, line);
161 : }
162 404 : ++col;
163 : }
164 : }
165 :
166 : static void
167 334 : do_one_bind(CS_BLKDESC *blkdesc, int col, const char *name)
168 : {
169 : #define do_bind(bind_name, fmt, type, len, value) do { \
170 : if (strcmp(#bind_name, name) == 0) { \
171 : do_bind(blkdesc, col, fmt, type, len, value, &l_ ## bind_name, &i_ ## bind_name); \
172 : return; \
173 : } \
174 : } while(0)
175 :
176 : /* non nulls */
177 :
178 334 : do_bind(not_null_bit, CS_FMT_UNUSED, CS_INT_TYPE, 4, ¬_null_bit);
179 322 : do_bind(not_null_char, CS_FMT_NULLTERM, CS_CHAR_TYPE, 7, not_null_char);
180 310 : do_bind(not_null_varchar, CS_FMT_NULLTERM, CS_CHAR_TYPE, 10, not_null_varchar);
181 298 : do_bind(not_null_datetime, CS_FMT_NULLTERM, CS_CHAR_TYPE, 20, not_null_datetime);
182 286 : do_bind(not_null_smalldatetime, CS_FMT_NULLTERM, CS_CHAR_TYPE, 20, not_null_smalldatetime);
183 274 : do_bind(not_null_money, CS_FMT_NULLTERM, CS_CHAR_TYPE, 6, not_null_money);
184 262 : do_bind(not_null_smallmoney, CS_FMT_NULLTERM, CS_CHAR_TYPE, 6, not_null_smallmoney);
185 250 : do_bind(not_null_float, CS_FMT_NULLTERM, CS_CHAR_TYPE, 6, not_null_float);
186 238 : do_bind(not_null_real, CS_FMT_NULLTERM, CS_CHAR_TYPE, 6, not_null_real);
187 226 : do_bind(not_null_decimal, CS_FMT_NULLTERM, CS_CHAR_TYPE, 6, not_null_decimal);
188 214 : do_bind(not_null_numeric, CS_FMT_NULLTERM, CS_CHAR_TYPE, 6, not_null_numeric);
189 202 : do_bind(not_null_int, CS_FMT_UNUSED, CS_INT_TYPE, 4, ¬_null_int);
190 190 : do_bind(not_null_smallint, CS_FMT_UNUSED, CS_INT_TYPE, 4, ¬_null_smallint);
191 178 : do_bind(not_null_tinyint, CS_FMT_UNUSED, CS_INT_TYPE, 4, ¬_null_tinyint);
192 166 : do_bind(not_null_varbinary, CS_FMT_NULLTERM, CS_BINARY_TYPE, 10, not_null_varbinary);
193 :
194 : /* nulls */
195 :
196 156 : do_bind(null_char, CS_FMT_NULLTERM, CS_CHAR_TYPE, 7, not_null_char);
197 144 : do_bind(null_varchar, CS_FMT_NULLTERM, CS_CHAR_TYPE, 10, not_null_varchar);
198 132 : do_bind(null_datetime, CS_FMT_NULLTERM, CS_CHAR_TYPE, 20, not_null_datetime);
199 120 : do_bind(null_smalldatetime, CS_FMT_NULLTERM, CS_CHAR_TYPE, 20, not_null_smalldatetime);
200 108 : do_bind(null_money, CS_FMT_NULLTERM, CS_CHAR_TYPE, 6, not_null_money);
201 96 : do_bind(null_smallmoney, CS_FMT_NULLTERM, CS_CHAR_TYPE, 6, not_null_smallmoney);
202 84 : do_bind(null_float, CS_FMT_NULLTERM, CS_CHAR_TYPE, 6, not_null_float);
203 72 : do_bind(null_real, CS_FMT_NULLTERM, CS_CHAR_TYPE, 6, not_null_real);
204 60 : do_bind(null_decimal, CS_FMT_NULLTERM, CS_CHAR_TYPE, 6, not_null_decimal);
205 48 : do_bind(null_numeric, CS_FMT_NULLTERM, CS_CHAR_TYPE, 6, not_null_numeric);
206 36 : do_bind(null_int, CS_FMT_UNUSED, CS_INT_TYPE, 4, ¬_null_int);
207 24 : do_bind(null_smallint, CS_FMT_UNUSED, CS_INT_TYPE, 4, ¬_null_smallint);
208 12 : do_bind(null_tinyint, CS_FMT_UNUSED, CS_INT_TYPE, 4, ¬_null_tinyint);
209 : #undef do_bind
210 :
211 0 : fprintf(stderr, "Column %s not found\n", name);
212 0 : exit(1);
213 : }
214 :
215 : typedef struct bound_value
216 : {
217 : struct bound_value *next;
218 : CS_INT varlen;
219 : CS_SMALLINT ind;
220 : void *value;
221 : } bound_value;
222 :
223 : static bound_value *dynamic_bounded = NULL;
224 :
225 : static void
226 32 : free_dynamic_bounded(void)
227 : {
228 104 : while (dynamic_bounded) {
229 40 : bound_value *next = dynamic_bounded->next;
230 :
231 40 : free(dynamic_bounded->value);
232 40 : free(dynamic_bounded);
233 40 : dynamic_bounded = next;
234 : }
235 32 : }
236 :
237 : static void
238 40 : do_type_bind(CS_BLKDESC *blkdesc, int col, const char *tok)
239 : {
240 40 : if (strcmp(tok, "CS_CHAR_TYPE") == 0) {
241 80 : CS_INT maxlen = atoi(strtok(NULL, " \t\n"));
242 40 : char *value = strdup(strtok(NULL, "\n"));
243 40 : bound_value *bound = calloc(1, sizeof(*bound));
244 :
245 40 : assert(value);
246 40 : assert(bound);
247 :
248 40 : if (strncmp(value, "\"\"\"", 3) == 0) {
249 : char *p;
250 :
251 0 : memmove(value, value + 3, strlen(value + 2));
252 0 : p = strstr(value, "\"\"\"");
253 0 : if (p)
254 0 : *p = '\0';
255 : }
256 40 : bound->varlen = strlen(value);
257 40 : bound->ind = 0;
258 40 : bound->value = value;
259 40 : bound->next = dynamic_bounded;
260 40 : dynamic_bounded = bound;
261 40 : do_bind(blkdesc, col, CS_FMT_NULLTERM, CS_CHAR_TYPE, maxlen, value, &bound->varlen, &bound->ind);
262 :
263 40 : return;
264 : }
265 0 : fprintf(stderr, "Unhandled type %s\n", tok);
266 0 : exit(1);
267 : }
268 :
269 : static void
270 30 : sendrow(CS_BLKDESC *blkdesc, const char *tok)
271 : {
272 : const char *errtype_str;
273 : ct_message_type errtype;
274 : CS_INT number;
275 : const char *message;
276 :
277 30 : if (strcmp(tok, "SENDROWERROR") == 0) {
278 20 : check_fail(blk_rowxfer, (blkdesc));
279 : } else {
280 10 : ct_reset_last_message();
281 10 : check_call(blk_rowxfer, (blkdesc));
282 : }
283 :
284 30 : errtype_str = strtok(NULL, " \t\n");
285 30 : if (!errtype_str) {
286 10 : check_last_message(CTMSG_NONE, 0, NULL);
287 10 : return;
288 : }
289 :
290 20 : if (strcmp(errtype_str, "CLIENT") == 0)
291 : errtype = CTMSG_CLIENT;
292 0 : else if (strcmp(errtype_str, "CLIENT2") == 0)
293 : errtype = CTMSG_CLIENT2;
294 0 : else if (strcmp(errtype_str, "SERVER") == 0)
295 : errtype = CTMSG_SERVER;
296 0 : else if (strcmp(errtype_str, "CSLIB") == 0)
297 : errtype = CTMSG_CSLIB;
298 : else {
299 0 : fprintf(stderr, "Unknown error type %s\n", errtype_str);
300 0 : exit(1);
301 : }
302 :
303 20 : number = strtol(strtok(NULL, " \t\n"), NULL, 0);
304 20 : message = strtok(NULL, "\n");
305 20 : check_last_message(errtype, number, message);
306 : }
307 :
308 : static void
309 374 : do_bind(CS_BLKDESC *blkdesc, int colnum, CS_INT host_format, CS_INT host_type, CS_INT host_maxlen,
310 : void *var_addr, CS_INT *var_len_addr, CS_SMALLINT *var_ind_addr)
311 : {
312 : CS_DATAFMT datafmt;
313 :
314 374 : check_call(blk_describe, (blkdesc, colnum, &datafmt));
315 :
316 374 : datafmt.format = host_format;
317 374 : datafmt.datatype = host_type;
318 374 : datafmt.maxlength = host_maxlen;
319 374 : datafmt.count = 1;
320 :
321 374 : check_call(blk_bind, (blkdesc, colnum, &datafmt, var_addr, var_len_addr, var_ind_addr ));
322 374 : }
323 :
324 : static const char table_name[] = "all_types_bcp_unittest";
325 :
326 10 : TEST_MAIN()
327 : {
328 : CS_CONTEXT *ctx;
329 : CS_CONNECTION *conn;
330 : CS_COMMAND *cmd;
331 10 : bool verbose = false;
332 : FILE *in;
333 : part_t part;
334 :
335 10 : printf("%s: Retrieve data using array binding \n", __FILE__);
336 : if (verbose) {
337 : printf("Trying login\n");
338 : }
339 10 : in = open_test_file(argc > 1 ? argv[1] : NULL);
340 10 : check_call(try_ctlogin, (&ctx, &conn, &cmd, verbose));
341 :
342 : for (;;) {
343 90 : part = read_part_type(in);
344 50 : if (part == PART_END)
345 : break;
346 40 : assert(part == PART_SQL);
347 :
348 40 : single_test(conn, cmd, in);
349 : }
350 :
351 10 : printf("done\n");
352 :
353 10 : check_call(try_ctlogout, (ctx, conn, cmd, verbose));
354 10 : fclose(in);
355 :
356 10 : return 0;
357 : }
358 :
359 : static void
360 40 : single_test(CS_CONNECTION *conn, CS_COMMAND *cmd, FILE *in)
361 : {
362 : char command[512];
363 : char *create_table_sql, *out1, *out2;
364 : CS_BLKDESC *blkdesc;
365 40 : int count = 0;
366 : int i;
367 : part_t part;
368 : CS_RETCODE ret;
369 40 : bool rows_sent = false;
370 :
371 40 : sprintf(command, "if exists (select 1 from sysobjects where type = 'U' and name = '%s') drop table %s",
372 : table_name, table_name);
373 :
374 40 : check_call(run_command, (cmd, command));
375 :
376 40 : create_table_sql = read_part(in);
377 40 : ret = run_command(cmd, create_table_sql);
378 40 : free(create_table_sql);
379 :
380 : /* on error skip the test */
381 40 : if (ret != CS_SUCCEED) {
382 8 : part = read_part_type(in);
383 8 : assert(part == PART_BIND);
384 8 : free(read_part(in));
385 :
386 8 : part = read_part_type(in);
387 8 : assert(part == PART_OUTPUT);
388 8 : free(read_part(in));
389 8 : return;
390 : }
391 :
392 32 : sprintf(command, "delete from %s", table_name);
393 32 : check_call(run_command, (cmd, command));
394 :
395 32 : check_call(blk_alloc, (conn, BLK_VERSION_150, &blkdesc));
396 :
397 32 : check_call(blk_init, (blkdesc, CS_BLK_IN, (char *) table_name, CS_NULLTERM));
398 :
399 32 : part = read_part_type(in);
400 32 : assert(part == PART_BIND);
401 :
402 32 : do_binds(blkdesc, in, &rows_sent);
403 :
404 32 : part = read_part_type(in);
405 32 : assert(part == PART_OUTPUT);
406 :
407 32 : if (!rows_sent) {
408 22 : printf("Sending same row 10 times... \n");
409 242 : for (i = 0; i < 10; i++)
410 220 : check_call(blk_rowxfer, (blkdesc));
411 : } else {
412 10 : check_call(blk_rowxfer, (blkdesc));
413 : }
414 :
415 32 : check_call(blk_done, (blkdesc, CS_BLK_ALL, &count));
416 :
417 32 : blk_drop(blkdesc);
418 :
419 32 : free_dynamic_bounded();
420 :
421 32 : printf("%d rows copied.\n", count);
422 :
423 32 : out1 = read_part(in);
424 32 : out2 = get_output(cmd, !rows_sent);
425 32 : if (strcmp(out1, out2) != 0) {
426 0 : fprintf(stderr, "Wrong output\n-- expected --\n%s\n-- got --\n%s\n--\n", out1, out2);
427 0 : exit(1);
428 : }
429 32 : free(out1);
430 32 : free(out2);
431 : }
432 :
433 : static char *
434 32 : get_output(CS_COMMAND *cmd, bool distinct)
435 : {
436 : char command[512];
437 :
438 : CS_RETCODE ret;
439 : CS_RETCODE results_ret;
440 : CS_DATAFMT datafmt;
441 : CS_INT datalength;
442 32 : CS_SMALLINT *inds = NULL;
443 32 : CS_INT count, row_count = 0;
444 : CS_INT result_type;
445 32 : CS_CHAR *data = NULL;
446 : CS_INT num_cols;
447 : CS_INT i;
448 32 : char *out = strdup("");
449 :
450 32 : assert(out != NULL);
451 :
452 32 : sprintf(command, "select%s * from %s", distinct ? " distinct" : "", table_name);
453 32 : check_call(ct_command, (cmd, CS_LANG_CMD, command, CS_NULLTERM, CS_UNUSED));
454 :
455 32 : check_call(ct_send, (cmd));
456 32 : while ((results_ret = ct_results(cmd, &result_type)) == CS_SUCCEED) {
457 64 : switch ((int) result_type) {
458 : case CS_CMD_SUCCEED:
459 : break;
460 : case CS_CMD_DONE:
461 : break;
462 0 : case CS_CMD_FAIL:
463 0 : fprintf(stderr, "ct_results() result_type CS_CMD_FAIL.\n");
464 0 : exit(1);
465 32 : case CS_ROW_RESULT:
466 32 : check_call(ct_res_info, (cmd, CS_NUMDATA, &num_cols, CS_UNUSED, NULL));
467 32 : data = malloc(num_cols * 256);
468 32 : assert(data != NULL);
469 32 : inds = calloc(num_cols, sizeof(*inds));
470 32 : assert(inds != NULL);
471 344 : for (i = 0; i < num_cols; i++) {
472 344 : datafmt.datatype = CS_CHAR_TYPE;
473 344 : datafmt.format = CS_FMT_NULLTERM;
474 344 : datafmt.maxlength = 256;
475 344 : datafmt.count = 1;
476 344 : datafmt.locale = NULL;
477 344 : check_call(ct_bind, (cmd, i + 1, &datafmt, data + 256 * i, &datalength, &inds[i]));
478 : }
479 :
480 74 : while ((ret = ct_fetch(cmd, CS_UNUSED, CS_UNUSED, CS_UNUSED, &count)) == CS_SUCCEED) {
481 42 : row_count += count;
482 396 : for (i = 0; i < num_cols; i++) {
483 354 : if (!inds[i])
484 198 : out = append_string(out, data + 256 * i);
485 : else
486 156 : out = append_string(out, "NULL");
487 354 : out = append_string(out, "\n");
488 : }
489 : }
490 32 : switch ((int) ret) {
491 : case CS_END_DATA:
492 : break;
493 0 : case CS_FAIL:
494 0 : fprintf(stderr, "ct_fetch() returned CS_FAIL.\n");
495 0 : exit(1);
496 0 : case CS_ROW_FAIL:
497 0 : fprintf(stderr, "ct_fetch() CS_ROW_FAIL on row %d.\n", row_count);
498 0 : exit(1);
499 0 : default:
500 0 : fprintf(stderr, "ct_fetch() unexpected return.\n");
501 0 : exit(1);
502 : }
503 : break;
504 0 : case CS_COMPUTE_RESULT:
505 0 : fprintf(stderr, "ct_results() unexpected CS_COMPUTE_RESULT.\n");
506 0 : exit(1);
507 0 : default:
508 0 : fprintf(stderr, "ct_results() unexpected result_type.\n");
509 0 : exit(1);
510 : }
511 : }
512 32 : switch ((int) results_ret) {
513 : case CS_END_RESULTS:
514 : break;
515 0 : case CS_FAIL:
516 0 : fprintf(stderr, "ct_results() failed.\n");
517 0 : exit(1);
518 : break;
519 0 : default:
520 0 : fprintf(stderr, "ct_results() unexpected return.\n");
521 0 : exit(1);
522 : }
523 :
524 32 : free(data);
525 32 : free(inds);
526 32 : return out;
527 : }
528 :
529 :
530 : static FILE *
531 10 : open_test_file(const char *filename)
532 : {
533 10 : FILE *input_file = NULL;
534 : char in_file[256];
535 :
536 : /* If no filename requested, try blk_in.in in both the expected location and the current directory. */
537 10 : if (filename)
538 0 : input_file = fopen(filename, "r");
539 : else {
540 10 : snprintf(in_file, sizeof(in_file), "%s/blk_in.in", FREETDS_SRCDIR);
541 10 : filename = in_file;
542 10 : input_file = fopen(filename, "r");
543 10 : if (!input_file) {
544 0 : filename = "blk_in.in";
545 0 : input_file = fopen(filename, "r");
546 : }
547 : }
548 10 : if (!input_file) {
549 0 : fprintf(stderr, "could not open %s\n", filename);
550 0 : exit(1);
551 : }
552 10 : return input_file;
553 : }
554 :
555 : static part_t
556 130 : read_part_type(FILE *in)
557 : {
558 : char line[1024];
559 : part_t part;
560 :
561 130 : read_line(line, sizeof(line), in);
562 130 : if (strncmp(line, "end", 3) == 0)
563 : return PART_END;
564 :
565 120 : if (strcmp(line, "sql\n") == 0)
566 : part = PART_SQL;
567 80 : else if (strcmp(line, "bind\n") == 0)
568 : part = PART_BIND;
569 40 : else if (strcmp(line, "output\n") == 0)
570 : part = PART_OUTPUT;
571 : else {
572 0 : fprintf(stderr, "Invalid part: %s\n", line);
573 0 : exit(1);
574 : }
575 120 : read_line(line, sizeof(line), in);
576 120 : if (strcmp(line, "--\n") != 0) {
577 0 : fprintf(stderr, "Error reading line\n");
578 0 : exit(1);
579 : }
580 : return part;
581 : }
582 :
583 : static char *
584 88 : read_part(FILE *in)
585 : {
586 : char line[1024];
587 88 : char *part = strdup("");
588 :
589 88 : assert(part != NULL);
590 : for (;;) {
591 3636 : read_line(line, sizeof(line), in);
592 3724 : if (strcmp(line, "--\n") == 0)
593 88 : return part;
594 3636 : part = append_string(part, line);
595 : }
596 : }
597 :
598 : static void
599 4420 : read_line(char *buf, size_t buf_len, FILE *f)
600 : {
601 4420 : if (fgets(buf, buf_len, f) == NULL || ferror(f)) {
602 0 : fprintf(stderr, "Error reading line\n");
603 0 : exit(1);
604 : }
605 4420 : }
606 :
607 : static char *
608 4344 : append_string(char *s1, const char *s2)
609 : {
610 4344 : assert(s1);
611 4344 : assert(s2);
612 4344 : s1 = realloc(s1, strlen(s1) + strlen(s2) + 1);
613 4344 : assert(s1 != NULL);
614 4344 : strcat(s1, s2);
615 4344 : return s1;
616 : }
|