Improve header stream write method.
* libmailutils/mailbox/header.c (HEADER_STREAMMOD): New flag. (mu_header_fill): Read data back from mstream if it was modified. (mu_header_destroy): Destroy mstream. (header_write): Rewrite from scratch. Allow for writing at arbitrary offsets. * examples/header.c: New commands: "overwrite" and "append", for testing the new functionality.
Showing
2 changed files
with
128 additions
and
50 deletions
... | @@ -283,7 +283,7 @@ cmd_write (int argc, char **argv) | ... | @@ -283,7 +283,7 @@ cmd_write (int argc, char **argv) |
283 | mu_error ("%u: cannot get stream: %s", line_num, mu_strerror (status)); | 283 | mu_error ("%u: cannot get stream: %s", line_num, mu_strerror (status)); |
284 | return; | 284 | return; |
285 | } | 285 | } |
286 | printf("[reading headers; end with an empty line]\n"); | 286 | printf ("[reading headers; end with an empty line]\n"); |
287 | mu_stream_seek (str, 0, SEEK_SET, NULL); | 287 | mu_stream_seek (str, 0, SEEK_SET, NULL); |
288 | while (prompt (1), fgets(buf, sizeof buf, stdin)) | 288 | while (prompt (1), fgets(buf, sizeof buf, stdin)) |
289 | { | 289 | { |
... | @@ -296,6 +296,71 @@ cmd_write (int argc, char **argv) | ... | @@ -296,6 +296,71 @@ cmd_write (int argc, char **argv) |
296 | } | 296 | } |
297 | 297 | ||
298 | void | 298 | void |
299 | cmd_overwrite (int argc, char **argv) | ||
300 | { | ||
301 | char buf[512]; | ||
302 | mu_stream_t str; | ||
303 | int status; | ||
304 | mu_off_t off; | ||
305 | |||
306 | if (check_args (argv[0], argc, 2, 2)) | ||
307 | return; | ||
308 | |||
309 | off = strtoul (argv[1], NULL, 0); | ||
310 | |||
311 | status = mu_header_get_streamref (header, &str); | ||
312 | if (status) | ||
313 | { | ||
314 | mu_error ("%u: cannot get stream: %s", line_num, mu_strerror (status)); | ||
315 | return; | ||
316 | } | ||
317 | status = mu_stream_seek (str, off, SEEK_SET, NULL); | ||
318 | if (status) | ||
319 | { | ||
320 | mu_error ("seek error: %s", mu_strerror (status)); | ||
321 | return; | ||
322 | } | ||
323 | |||
324 | printf ("[reading headers; end with an empty line]\n"); | ||
325 | while (prompt (1), fgets(buf, sizeof buf, stdin)) | ||
326 | { | ||
327 | if (buf[0] == '\n') | ||
328 | break; | ||
329 | mu_stream_write (str, buf, strlen (buf), NULL); | ||
330 | } | ||
331 | mu_stream_destroy (&str); | ||
332 | mu_stream_destroy (&hstream); | ||
333 | } | ||
334 | |||
335 | void | ||
336 | cmd_append (int argc, char **argv) | ||
337 | { | ||
338 | char buf[512]; | ||
339 | mu_stream_t str; | ||
340 | int status; | ||
341 | |||
342 | if (check_args (argv[0], argc, 1, 1)) | ||
343 | return; | ||
344 | |||
345 | status = mu_header_get_streamref (header, &str); | ||
346 | if (status) | ||
347 | { | ||
348 | mu_error ("%u: cannot get stream: %s", line_num, mu_strerror (status)); | ||
349 | return; | ||
350 | } | ||
351 | printf ("[reading headers; end with an empty line]\n"); | ||
352 | mu_stream_seek (str, 0, SEEK_END, NULL); | ||
353 | while (prompt (1), fgets(buf, sizeof buf, stdin)) | ||
354 | { | ||
355 | mu_stream_write (str, buf, strlen (buf), NULL); | ||
356 | if (buf[0] == '\n') | ||
357 | break; | ||
358 | } | ||
359 | mu_stream_destroy (&str); | ||
360 | mu_stream_destroy (&hstream); | ||
361 | } | ||
362 | |||
363 | void | ||
299 | cmd_iterate (int argc, char **argv) | 364 | cmd_iterate (int argc, char **argv) |
300 | { | 365 | { |
301 | if (check_args (argv[0], argc, 1, 2)) | 366 | if (check_args (argv[0], argc, 1, 2)) |
... | @@ -379,7 +444,7 @@ static struct cmdtab cmdtab[] = { | ... | @@ -379,7 +444,7 @@ static struct cmdtab cmdtab[] = { |
379 | { "free", cmd_free, NULL, "discard all headers" }, | 444 | { "free", cmd_free, NULL, "discard all headers" }, |
380 | { "print", cmd_print, "NAME [N]", | 445 | { "print", cmd_print, "NAME [N]", |
381 | "find and print the Nth (by default, 1st) instance of header named NAME" }, | 446 | "find and print the Nth (by default, 1st) instance of header named NAME" }, |
382 | { "dump", cmd_dump, NULL, "dump all headers on screen" }, | 447 | { "dump", cmd_dump, "[OFF]", "dump all headers on screen" }, |
383 | { "itr", cmd_iterate, "[first|1|next|n]", "iterate over headers" }, | 448 | { "itr", cmd_iterate, "[first|1|next|n]", "iterate over headers" }, |
384 | { "readline", cmd_readline, "[SIZE]", "read line" }, | 449 | { "readline", cmd_readline, "[SIZE]", "read line" }, |
385 | { "remove", cmd_remove, "NAME [N]", | 450 | { "remove", cmd_remove, "NAME [N]", |
... | @@ -387,6 +452,8 @@ static struct cmdtab cmdtab[] = { | ... | @@ -387,6 +452,8 @@ static struct cmdtab cmdtab[] = { |
387 | { "insert", cmd_insert, "NAME VALUE [REF [NUM] [before|after] [replace]]", | 452 | { "insert", cmd_insert, "NAME VALUE [REF [NUM] [before|after] [replace]]", |
388 | "insert new header" }, | 453 | "insert new header" }, |
389 | { "write", cmd_write, NULL, "accept headers from raw stream" }, | 454 | { "write", cmd_write, NULL, "accept headers from raw stream" }, |
455 | { "overwrite", cmd_overwrite, "OFF", "overwrite raw data from offset OFF" }, | ||
456 | { "append", cmd_append, NULL, "append raw data" }, | ||
390 | { "help", cmd_help, "[COMMAND]", "print short usage message" }, | 457 | { "help", cmd_help, "[COMMAND]", "print short usage message" }, |
391 | { NULL } | 458 | { NULL } |
392 | }; | 459 | }; | ... | ... |
... | @@ -50,6 +50,7 @@ | ... | @@ -50,6 +50,7 @@ |
50 | 50 | ||
51 | #define HEADER_MODIFIED 0x01 | 51 | #define HEADER_MODIFIED 0x01 |
52 | #define HEADER_INVALIDATE 0x02 | 52 | #define HEADER_INVALIDATE 0x02 |
53 | #define HEADER_STREAMMOD 0x04 | ||
53 | 54 | ||
54 | #define HEADER_SET_MODIFIED(h) \ | 55 | #define HEADER_SET_MODIFIED(h) \ |
55 | ((h)->flags |= (HEADER_MODIFIED|HEADER_INVALIDATE)) | 56 | ((h)->flags |= (HEADER_MODIFIED|HEADER_INVALIDATE)) |
... | @@ -412,6 +413,33 @@ mu_header_fill (mu_header_t header) | ... | @@ -412,6 +413,33 @@ mu_header_fill (mu_header_t header) |
412 | size_t blurb_len = 0; | 413 | size_t blurb_len = 0; |
413 | char *blurb = NULL; | 414 | char *blurb = NULL; |
414 | 415 | ||
416 | if (header->mstream && header->flags & HEADER_STREAMMOD) | ||
417 | { | ||
418 | mu_off_t end; | ||
419 | |||
420 | mu_header_invalidate (header); | ||
421 | status = mu_stream_size (header->mstream, &end); | ||
422 | if (status) | ||
423 | return status; | ||
424 | status = mu_stream_seek (header->mstream, 0, MU_SEEK_SET, NULL); | ||
425 | if (status) | ||
426 | return status; | ||
427 | blurb_len = end; | ||
428 | blurb = malloc (blurb_len + 1); | ||
429 | if (!blurb) | ||
430 | return ENOMEM; | ||
431 | status = mu_stream_read (header->mstream, blurb, blurb_len, NULL); | ||
432 | if (status) | ||
433 | { | ||
434 | free (blurb); | ||
435 | return status; | ||
436 | } | ||
437 | status = header_parse (header, blurb, blurb_len); | ||
438 | free (blurb); | ||
439 | if (status == 0) | ||
440 | header->flags &= ~HEADER_STREAMMOD; | ||
441 | return status; | ||
442 | } | ||
415 | if (header->spool_used) | 443 | if (header->spool_used) |
416 | return 0; | 444 | return 0; |
417 | 445 | ||
... | @@ -452,6 +480,7 @@ mu_header_destroy (mu_header_t *ph) | ... | @@ -452,6 +480,7 @@ mu_header_destroy (mu_header_t *ph) |
452 | { | 480 | { |
453 | mu_header_t header = *ph; | 481 | mu_header_t header = *ph; |
454 | 482 | ||
483 | mu_stream_destroy (&header->mstream); | ||
455 | mu_stream_destroy (&header->stream); | 484 | mu_stream_destroy (&header->stream); |
456 | mu_hdrent_free_list (header); | 485 | mu_hdrent_free_list (header); |
457 | free (header->spool); | 486 | free (header->spool); |
... | @@ -1091,68 +1120,50 @@ _header_readline (mu_stream_t is, char *buffer, size_t buflen, size_t *pnread) | ... | @@ -1091,68 +1120,50 @@ _header_readline (mu_stream_t is, char *buffer, size_t buflen, size_t *pnread) |
1091 | static int | 1120 | static int |
1092 | header_write (mu_stream_t os, const char *buf, size_t buflen, size_t *pnwrite) | 1121 | header_write (mu_stream_t os, const char *buf, size_t buflen, size_t *pnwrite) |
1093 | { | 1122 | { |
1094 | struct _mu_header_stream *hstr; | 1123 | struct _mu_header_stream *hstr = (struct _mu_header_stream *) os; |
1095 | mu_header_t header; | 1124 | mu_header_t header; |
1096 | int status; | 1125 | int rc; |
1097 | mu_off_t mstream_size; | ||
1098 | 1126 | ||
1099 | if (!os || !buf) | 1127 | if (os == NULL) |
1100 | return EINVAL; | 1128 | return EINVAL; |
1101 | |||
1102 | hstr = (struct _mu_header_stream *) os; | ||
1103 | header = hstr->hdr; | 1129 | header = hstr->hdr; |
1104 | if (header == NULL) | 1130 | if (!(header->flags & HEADER_STREAMMOD)) |
1105 | return EINVAL; | ||
1106 | |||
1107 | /* Skip the obvious. */ | ||
1108 | if (*buf == '\0' || buflen == 0) | ||
1109 | { | 1131 | { |
1110 | if (pnwrite) | 1132 | struct mu_hdrent *ent; |
1111 | *pnwrite = 0; | ||
1112 | return 0; | ||
1113 | } | ||
1114 | 1133 | ||
1115 | if (!header->mstream) | 1134 | rc = mu_header_fill (header); |
1116 | { | 1135 | if (rc) |
1117 | status = mu_memory_stream_create (&header->mstream, MU_STREAM_RDWR); | 1136 | return rc; |
1118 | if (status) | ||
1119 | return status; | ||
1120 | } | ||
1121 | 1137 | ||
1122 | status = mu_stream_write (header->mstream, buf, buflen, NULL); | 1138 | if (!header->mstream) |
1123 | if (status) | ||
1124 | { | 1139 | { |
1125 | mu_stream_destroy (&header->mstream); | 1140 | rc = mu_memory_stream_create (&header->mstream, MU_STREAM_RDWR); |
1126 | return status; | 1141 | if (rc) |
1142 | return rc; | ||
1127 | } | 1143 | } |
1128 | 1144 | mu_stream_seek (header->mstream, 0, MU_SEEK_SET, NULL); | |
1129 | status = mu_stream_size (header->mstream, &mstream_size); | 1145 | if (header->spool_used) |
1130 | if (status == 0 && mstream_size > 1) | ||
1131 | { | ||
1132 | char nlbuf[2]; | ||
1133 | |||
1134 | status = mu_stream_seek (header->mstream, -2, MU_SEEK_END, NULL); | ||
1135 | if (status == 0) | ||
1136 | status = mu_stream_read (header->mstream, nlbuf, 2, NULL); | ||
1137 | if (status == 0 && memcmp (nlbuf, "\n\n", 2) == 0) | ||
1138 | { | ||
1139 | char *blurb; | ||
1140 | |||
1141 | blurb = calloc (1, mstream_size + 1); | ||
1142 | if (blurb) | ||
1143 | { | 1146 | { |
1144 | mu_stream_read (header->mstream, blurb, mstream_size, NULL); | 1147 | for (ent = header->head; ent; ent = ent->next) |
1145 | status = header_parse (header, blurb, mstream_size); | 1148 | mu_hdrent_fixup (header, ent); |
1146 | } | 1149 | rc = mu_stream_write (header->mstream, header->spool, |
1147 | free (blurb); | 1150 | header->spool_used, NULL); |
1148 | mu_stream_destroy (&header->mstream); | 1151 | for (ent = header->head; ent; ent = ent->next) |
1152 | mu_hdrent_unroll_fixup (header, ent); | ||
1153 | if (rc) | ||
1154 | return rc; | ||
1155 | mu_stream_truncate (header->mstream, header->spool_used); | ||
1156 | if (hstr->off > header->spool_used) | ||
1157 | hstr->off = header->spool_used; | ||
1149 | } | 1158 | } |
1159 | header->flags |= HEADER_STREAMMOD; | ||
1150 | } | 1160 | } |
1151 | 1161 | ||
1152 | if (pnwrite) | 1162 | rc = mu_stream_seek (header->mstream, hstr->off, MU_SEEK_SET, NULL); |
1153 | *pnwrite = buflen; | 1163 | if (rc) |
1164 | return rc; | ||
1154 | 1165 | ||
1155 | return status; | 1166 | return mu_stream_write (header->mstream, buf, buflen, pnwrite); |
1156 | } | 1167 | } |
1157 | 1168 | ||
1158 | static int | 1169 | static int | ... | ... |
-
Please register or sign in to post a comment