Commit 75755ec7 75755ec7febf8c891b4b809120f3da77e1e15549 by Sergey Poznyakoff

Implement line number output in linecon and inline-comment filters.

* libmailutils/filter/inline-comment.c: Implement line number info
facility.
* libmailutils/filter/linecon.c: Likewise.
* libmailutils/stream/logstream.c (_log_done, _log_close): Always
close/destroy the underlying transport stream.
* comsat/action.c: Rewrite using a table-driven parser.
Use line-info facility of the linecon filter.
1 parent 9e253b39
......@@ -23,14 +23,18 @@
The following options modify this behavior:
-r Remove empty lines, i.e. the ones that contain only whitespace
characters.
-s Squeeze whitespace. Each sequence of two or more whitespace
characters encountered on input is replaced by a single space
character on output.
-S A "following whitespace mode". A comment sequence is recognized
only if followed by a whitespace character. The character itself
is retained on output.
-i STR Emit line number information after each contiguous sequence
of removed lines. STR supplies "information starter" -- a
sequence of characters which is output before the actual line
number.
-r Remove empty lines, i.e. the ones that contain only whitespace
characters.
-s Squeeze whitespace. Each sequence of two or more whitespace
characters encountered on input is replaced by a single space
character on output.
-S A "following whitespace mode". A comment sequence is
recognized only if followed by a whitespace character.
The character itself is retained on output.
In encode mode, this filter adds a comment sequence at the beginning
of each line.
......@@ -49,16 +53,19 @@
#include <mailutils/errno.h>
#include <mailutils/filter.h>
#include <mailutils/cctype.h>
#include <mailutils/io.h>
enum ilcmt_state
{
ilcmt_initial,
ilcmt_newline,
ilcmt_newline_ac,
ilcmt_copy,
ilcmt_comment,
ilcmt_partial,
ilcmt_comment_ws,
ilcmt_ws,
ilcmt_rollback,
ilcmt_rollback_ws,
ilcmt_rollback_com
};
......@@ -71,13 +78,20 @@ enum ilcmt_state
whitespace character.
In encode mode: output a space after
each comment prefix. */
#define ILCMT_LINE_INFO 0x08 /* Emit line number information */
#define ILCMT_COMMENT_STATIC 0x0100
#define ILCMT_LINE_INFO_STATIC 0x0200
struct ilcmt_data
{
enum ilcmt_state state; /* Current state */
char *comment; /* Comment sequence ... */
char *comment; /* Comment sequence ... */
size_t length; /* and its length */
int flags; /* ILCMT_ flags */
char *line_info_starter; /* Starter for line number info lines */
unsigned long line_number; /* Current line number */
char sbuf[3]; /* Static location for single-character strings */
char *buf; /* Rollback buffer ... */
size_t size; /* and its size */
size_t level; /* In ilcmt_partial and ilcmt_rollback_com
......@@ -116,6 +130,17 @@ ilcmt_save (struct ilcmt_data *pd, int c)
return 0;
}
static void
_ilcmt_free (struct ilcmt_data *pd)
{
if (!(pd->flags & ILCMT_COMMENT_STATIC))
free (pd->comment);
if ((pd->flags & ILCMT_LINE_INFO) &&
!(pd->flags & ILCMT_LINE_INFO_STATIC))
free (pd->line_info_starter);
free (pd->buf);
}
static enum mu_filter_result
_ilcmt_decoder (void *xd, enum mu_filter_command cmd,
struct mu_filter_io *iobuf)
......@@ -131,8 +156,7 @@ _ilcmt_decoder (void *xd, enum mu_filter_command cmd,
return mu_filter_ok;
case mu_filter_done:
free (pd->comment);
free (pd->buf);
_ilcmt_free (pd);
return mu_filter_ok;
default:
......@@ -150,14 +174,25 @@ _ilcmt_decoder (void *xd, enum mu_filter_command cmd,
{
case ilcmt_initial:
case ilcmt_newline:
case ilcmt_newline_ac:
if (*iptr == *pd->comment)
{
iptr++;
pd->level = 1;
pd->state = ilcmt_partial;
}
else if (pd->state == ilcmt_newline_ac)
{
mu_asnprintf (&pd->buf, &pd->size, "%s %lu\n",
pd->line_info_starter,
pd->line_number);
pd->level = strlen (pd->buf);
pd->replay = 0;
pd->state = ilcmt_rollback;
}
else if (*iptr == '\n')
{
pd->line_number++;
if (pd->flags & ILCMT_REMOVE_EMPTY_LINES)
{
iptr++;
......@@ -230,6 +265,7 @@ _ilcmt_decoder (void *xd, enum mu_filter_command cmd,
case ilcmt_ws:
if (*iptr == '\n')
{
pd->line_number++;
iptr++;
pd->state = ilcmt_newline;
}
......@@ -251,12 +287,21 @@ _ilcmt_decoder (void *xd, enum mu_filter_command cmd,
case ilcmt_copy:
if ((*optr++ = *iptr++) == '\n')
pd->state = ilcmt_newline;
{
pd->line_number++;
pd->state = ilcmt_newline;
}
break;
case ilcmt_comment:
if (*iptr++ == '\n')
pd->state = ilcmt_newline;
{
pd->line_number++;
if (pd->flags & ILCMT_LINE_INFO)
pd->state = ilcmt_newline_ac;
else
pd->state = ilcmt_newline;
}
break;
case ilcmt_rollback_com:
......@@ -270,13 +315,13 @@ _ilcmt_decoder (void *xd, enum mu_filter_command cmd,
{
*optr++ = ' ';
pd->state = ilcmt_copy;
break;
}
else
{
*optr++ = pd->buf[pd->replay++];
if (pd->replay == pd->level)
pd->state = ilcmt_copy;
}
/* fall through */
case ilcmt_rollback:
*optr++ = pd->buf[pd->replay++];
if (pd->replay == pd->level)
pd->state = ilcmt_copy;
}
}
iobuf->isize = iptr - (const unsigned char *) iobuf->input;
......@@ -300,8 +345,7 @@ _ilcmt_encoder (void *xd,
return mu_filter_ok;
case mu_filter_done:
free (pd->comment);
free (pd->buf);
_ilcmt_free (pd);
return mu_filter_ok;
default:
......@@ -353,14 +397,15 @@ alloc_state (void **pret, int mode MU_ARG_UNUSED, int argc, const char **argv)
struct ilcmt_data *pd = malloc (sizeof (*pd));
int i;
const char *comment = ";";
const char *line_info;
if (!pd)
return ENOMEM;
pd->flags = 0;
pd->buf = NULL;
pd->size = pd->level = pd->replay = 0;
pd->line_number = 1;
for (i = 1; i < argc; i++)
{
......@@ -379,7 +424,14 @@ alloc_state (void **pret, int mode MU_ARG_UNUSED, int argc, const char **argv)
case 'S':
pd->flags |= ILCMT_FOLLOW_WS;
break;
case 'i':
pd->flags |= ILCMT_LINE_INFO;
if (i + 1 == argc)
return MU_ERR_PARSE;
line_info = argv[++i];
break;
default:
free (pd);
return MU_ERR_PARSE;
......@@ -388,13 +440,45 @@ alloc_state (void **pret, int mode MU_ARG_UNUSED, int argc, const char **argv)
else
comment = argv[i];
}
pd->comment = strdup (comment);
if (!pd->comment)
if (comment[1] == 0)
{
pd->sbuf[0] = comment[0];
pd->comment = pd->sbuf;
pd->flags |= ILCMT_COMMENT_STATIC;
pd->length = 1;
}
else
{
pd->comment = strdup (comment);
if (!pd->comment)
{
free (pd);
return ENOMEM;
}
pd->length = strlen (comment);
}
if (pd->flags & ILCMT_LINE_INFO)
{
free (pd);
return ENOMEM;
if (line_info[1] == 0)
{
pd->sbuf[1] = line_info[0];
pd->sbuf[2] = 0;
pd->line_info_starter = pd->sbuf + 1;
pd->flags |= ILCMT_LINE_INFO_STATIC;
}
else
{
pd->line_info_starter = strdup (line_info);
if (!pd->line_info_starter)
{
if (!(pd->flags & ILCMT_COMMENT_STATIC))
free (pd->comment);
free (pd);
return ENOMEM;
}
}
}
pd->length = strlen (comment);
*pret = pd;
......
......@@ -18,31 +18,70 @@
This filter has only decode mode. It removes from the input
backslashes immediately followed by a newline, thus implementing
a familiar UNIX line-continuation facility. */
a familiar UNIX line-continuation facility.
The following option is accepted:
-i STR Emit line number information after each contiguous sequence
of joined lines. STR supplies "information starter" -- a
sequence of characters which is output before the actual line
number.
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdlib.h>
#include <string.h>
#include <mailutils/errno.h>
#include <mailutils/filter.h>
#include <mailutils/cctype.h>
#include <mailutils/io.h>
#define LINECON_LINE_INFO 0x01 /* Emit line number information */
#define LINECON_LINE_INFO_STATIC 0x02
enum linecon_state
{
linecon_init,
linecon_escape,
linecon_newline,
linecon_rollback
};
struct linecon_data
{
int flags; /* Mode flags */
enum linecon_state state; /* Current state */
char *line_info_starter; /* Starter for line number info lines */
unsigned long line_number; /* Current line number */
char sbuf[2]; /* Static buffer for single-character starters */
char *buf; /* Rollback buffer ... */
size_t size; /* and its size */
size_t level; /* Number of characters stored in buf. */
size_t replay; /* Index of the next-to-be-replayed character
from buf */
};
static enum mu_filter_result
_linecon_decoder (void *xd, enum mu_filter_command cmd,
struct mu_filter_io *iobuf)
{
int *escaped = xd;
struct linecon_data *pd = xd;
const unsigned char *iptr, *iend;
char *optr, *oend;
switch (cmd)
{
case mu_filter_init:
*escaped = 0;
pd->state = linecon_init;
return mu_filter_ok;
case mu_filter_done:
if ((pd->flags & LINECON_LINE_INFO) &&
!(pd->flags & LINECON_LINE_INFO_STATIC))
free (pd->line_info_starter);
return mu_filter_ok;
default:
......@@ -56,34 +95,57 @@ _linecon_decoder (void *xd, enum mu_filter_command cmd,
while (iptr < iend && optr < oend)
{
int c = *iptr++;
switch (c)
switch (pd->state)
{
case '\\':
*escaped = 1;
continue;
case '\n':
if (*escaped)
case linecon_init:
case linecon_newline:
switch (*iptr)
{
*escaped = 0;
case '\\':
iptr++;
pd->state = linecon_escape;
continue;
case '\n':
pd->line_number++;
if (pd->state == linecon_newline)
{
mu_asnprintf (&pd->buf, &pd->size, "%s %lu\n",
pd->line_info_starter,
pd->line_number);
pd->level = strlen (pd->buf);
pd->replay = 0;
pd->state = linecon_rollback;
}
/* fall through */
default:
*optr++ = *iptr++;
break;
}
*optr++ = c;
break;
default:
if (*escaped)
case linecon_escape:
if (*iptr == '\n')
{
pd->line_number++;
iptr++;
pd->state = (pd->flags & LINECON_LINE_INFO) ?
linecon_newline : linecon_init;
continue;
}
else
{
*escaped = 0;
pd->state = linecon_init;
*optr++ = '\\';
if (optr == oend)
{
iptr--;
break;
}
break;
*optr++ = *iptr++;
}
*optr++ = c;
break;
case linecon_rollback:
*optr++ = pd->buf[pd->replay++];
if (pd->replay == pd->level)
pd->state = linecon_init;
}
}
......@@ -95,9 +157,55 @@ _linecon_decoder (void *xd, enum mu_filter_command cmd,
static int
alloc_state (void **pret, int mode MU_ARG_UNUSED, int argc, const char **argv)
{
*pret = malloc (sizeof (int));
if (!*pret)
int i;
struct linecon_data *pd = malloc (sizeof (pd[0]));
const char *line_info;
if (!pd)
return ENOMEM;
pd->flags = 0;
pd->buf = NULL;
pd->size = pd->level = pd->replay = 0;
pd->line_number = 1;
for (i = 1; i < argc; i++)
{
if (argv[i][0] == '-')
{
switch (argv[i][1])
{
case 'i':
pd->flags |= LINECON_LINE_INFO;
if (i + 1 == argc)
return MU_ERR_PARSE;
line_info = argv[++i];
break;
default:
free (pd);
return MU_ERR_PARSE;
}
}
}
if (pd->flags & LINECON_LINE_INFO)
{
if (line_info[1])
{
pd->line_info_starter = strdup (line_info);
if (!pd->line_info_starter)
{
free (pd);
return ENOMEM;
}
}
else
{
pd->sbuf[0] = line_info[0];
pd->sbuf[1] = 0;
pd->line_info_starter = pd->sbuf;
pd->flags |= LINECON_LINE_INFO_STATIC;
}
}
*pret = pd;
return 0;
}
......
......@@ -257,17 +257,14 @@ static void
_log_done (struct _mu_stream *str)
{
struct _mu_log_stream *sp = (struct _mu_log_stream *)str;
if (str->flags & MU_STREAM_AUTOCLOSE)
mu_stream_destroy (&sp->transport);
mu_stream_destroy (&sp->transport);
}
static int
_log_close (struct _mu_stream *str)
{
struct _mu_log_stream *sp = (struct _mu_log_stream *)str;
if (str->flags & MU_STREAM_AUTOCLOSE)
return mu_stream_close (sp->transport);
return 0;
return mu_stream_close (sp->transport);
}
static int
......