Commit a7a174d5 a7a174d51ca7845f9eda854709258d64e5d0077e by Sergey Poznyakoff

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.
1 parent b9f10525
...@@ -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
......