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)
mu_error ("%u: cannot get stream: %s", line_num, mu_strerror (status));
return;
}
printf("[reading headers; end with an empty line]\n");
printf ("[reading headers; end with an empty line]\n");
mu_stream_seek (str, 0, SEEK_SET, NULL);
while (prompt (1), fgets(buf, sizeof buf, stdin))
{
......@@ -296,6 +296,71 @@ cmd_write (int argc, char **argv)
}
void
cmd_overwrite (int argc, char **argv)
{
char buf[512];
mu_stream_t str;
int status;
mu_off_t off;
if (check_args (argv[0], argc, 2, 2))
return;
off = strtoul (argv[1], NULL, 0);
status = mu_header_get_streamref (header, &str);
if (status)
{
mu_error ("%u: cannot get stream: %s", line_num, mu_strerror (status));
return;
}
status = mu_stream_seek (str, off, SEEK_SET, NULL);
if (status)
{
mu_error ("seek error: %s", mu_strerror (status));
return;
}
printf ("[reading headers; end with an empty line]\n");
while (prompt (1), fgets(buf, sizeof buf, stdin))
{
if (buf[0] == '\n')
break;
mu_stream_write (str, buf, strlen (buf), NULL);
}
mu_stream_destroy (&str);
mu_stream_destroy (&hstream);
}
void
cmd_append (int argc, char **argv)
{
char buf[512];
mu_stream_t str;
int status;
if (check_args (argv[0], argc, 1, 1))
return;
status = mu_header_get_streamref (header, &str);
if (status)
{
mu_error ("%u: cannot get stream: %s", line_num, mu_strerror (status));
return;
}
printf ("[reading headers; end with an empty line]\n");
mu_stream_seek (str, 0, SEEK_END, NULL);
while (prompt (1), fgets(buf, sizeof buf, stdin))
{
mu_stream_write (str, buf, strlen (buf), NULL);
if (buf[0] == '\n')
break;
}
mu_stream_destroy (&str);
mu_stream_destroy (&hstream);
}
void
cmd_iterate (int argc, char **argv)
{
if (check_args (argv[0], argc, 1, 2))
......@@ -379,7 +444,7 @@ static struct cmdtab cmdtab[] = {
{ "free", cmd_free, NULL, "discard all headers" },
{ "print", cmd_print, "NAME [N]",
"find and print the Nth (by default, 1st) instance of header named NAME" },
{ "dump", cmd_dump, NULL, "dump all headers on screen" },
{ "dump", cmd_dump, "[OFF]", "dump all headers on screen" },
{ "itr", cmd_iterate, "[first|1|next|n]", "iterate over headers" },
{ "readline", cmd_readline, "[SIZE]", "read line" },
{ "remove", cmd_remove, "NAME [N]",
......@@ -387,6 +452,8 @@ static struct cmdtab cmdtab[] = {
{ "insert", cmd_insert, "NAME VALUE [REF [NUM] [before|after] [replace]]",
"insert new header" },
{ "write", cmd_write, NULL, "accept headers from raw stream" },
{ "overwrite", cmd_overwrite, "OFF", "overwrite raw data from offset OFF" },
{ "append", cmd_append, NULL, "append raw data" },
{ "help", cmd_help, "[COMMAND]", "print short usage message" },
{ NULL }
};
......
......@@ -50,6 +50,7 @@
#define HEADER_MODIFIED 0x01
#define HEADER_INVALIDATE 0x02
#define HEADER_STREAMMOD 0x04
#define HEADER_SET_MODIFIED(h) \
((h)->flags |= (HEADER_MODIFIED|HEADER_INVALIDATE))
......@@ -412,6 +413,33 @@ mu_header_fill (mu_header_t header)
size_t blurb_len = 0;
char *blurb = NULL;
if (header->mstream && header->flags & HEADER_STREAMMOD)
{
mu_off_t end;
mu_header_invalidate (header);
status = mu_stream_size (header->mstream, &end);
if (status)
return status;
status = mu_stream_seek (header->mstream, 0, MU_SEEK_SET, NULL);
if (status)
return status;
blurb_len = end;
blurb = malloc (blurb_len + 1);
if (!blurb)
return ENOMEM;
status = mu_stream_read (header->mstream, blurb, blurb_len, NULL);
if (status)
{
free (blurb);
return status;
}
status = header_parse (header, blurb, blurb_len);
free (blurb);
if (status == 0)
header->flags &= ~HEADER_STREAMMOD;
return status;
}
if (header->spool_used)
return 0;
......@@ -452,6 +480,7 @@ mu_header_destroy (mu_header_t *ph)
{
mu_header_t header = *ph;
mu_stream_destroy (&header->mstream);
mu_stream_destroy (&header->stream);
mu_hdrent_free_list (header);
free (header->spool);
......@@ -1091,68 +1120,50 @@ _header_readline (mu_stream_t is, char *buffer, size_t buflen, size_t *pnread)
static int
header_write (mu_stream_t os, const char *buf, size_t buflen, size_t *pnwrite)
{
struct _mu_header_stream *hstr;
struct _mu_header_stream *hstr = (struct _mu_header_stream *) os;
mu_header_t header;
int status;
mu_off_t mstream_size;
int rc;
if (!os || !buf)
if (os == NULL)
return EINVAL;
hstr = (struct _mu_header_stream *) os;
header = hstr->hdr;
if (header == NULL)
return EINVAL;
/* Skip the obvious. */
if (*buf == '\0' || buflen == 0)
if (!(header->flags & HEADER_STREAMMOD))
{
if (pnwrite)
*pnwrite = 0;
return 0;
}
struct mu_hdrent *ent;
if (!header->mstream)
{
status = mu_memory_stream_create (&header->mstream, MU_STREAM_RDWR);
if (status)
return status;
}
rc = mu_header_fill (header);
if (rc)
return rc;
status = mu_stream_write (header->mstream, buf, buflen, NULL);
if (status)
if (!header->mstream)
{
mu_stream_destroy (&header->mstream);
return status;
rc = mu_memory_stream_create (&header->mstream, MU_STREAM_RDWR);
if (rc)
return rc;
}
status = mu_stream_size (header->mstream, &mstream_size);
if (status == 0 && mstream_size > 1)
{
char nlbuf[2];
status = mu_stream_seek (header->mstream, -2, MU_SEEK_END, NULL);
if (status == 0)
status = mu_stream_read (header->mstream, nlbuf, 2, NULL);
if (status == 0 && memcmp (nlbuf, "\n\n", 2) == 0)
{
char *blurb;
blurb = calloc (1, mstream_size + 1);
if (blurb)
mu_stream_seek (header->mstream, 0, MU_SEEK_SET, NULL);
if (header->spool_used)
{
mu_stream_read (header->mstream, blurb, mstream_size, NULL);
status = header_parse (header, blurb, mstream_size);
}
free (blurb);
mu_stream_destroy (&header->mstream);
for (ent = header->head; ent; ent = ent->next)
mu_hdrent_fixup (header, ent);
rc = mu_stream_write (header->mstream, header->spool,
header->spool_used, NULL);
for (ent = header->head; ent; ent = ent->next)
mu_hdrent_unroll_fixup (header, ent);
if (rc)
return rc;
mu_stream_truncate (header->mstream, header->spool_used);
if (hstr->off > header->spool_used)
hstr->off = header->spool_used;
}
header->flags |= HEADER_STREAMMOD;
}
if (pnwrite)
*pnwrite = buflen;
rc = mu_stream_seek (header->mstream, hstr->off, MU_SEEK_SET, NULL);
if (rc)
return rc;
return status;
return mu_stream_write (header->mstream, buf, buflen, pnwrite);
}
static int
......