.RI [ msgs ]
.RB [ \-component
.IR field ]
-.RB [ \-date " | " \-nodate ]
+.RB [ \-text
+.IR body ]
.RB [ \-append ]
-.RB [ \-list ]
-.RB [ \-delete ]
-.RB [ \-number
-.IR [ num|all ]]
+.RB [ \-date " | " \-nodate ]
.RB [ \-preserve " | " \-nopreserve ]
.RB [ \-version ]
.RB [ \-help ]
+.HP
+.B anno
+.B \-delete
+.RI [ +folder ]
+.RI [ msgs ]
+.RB [ \-component
+.IR field ]
.RB [ \-text
.IR body ]
+.RB [ \-number
+.IR num " | all ]
+.RB [ \-preserve " | " \-nopreserve ]
+.RB [ \-version ]
+.RB [ \-help ]
+.HP
+.B anno
+.B \-list
+.RI [ +folder ]
+.RI [ msgs ]
+.RB [ \-component
+.IR field ]
+.RB [ \-number ]
+.RB [ \-version ]
+.RB [ \-help ]
.ad
.SH DESCRIPTION
.B Anno
in messages.
Header fields consist of a field name and an optional field body
as defined by RFC-2822.
-The
-.B -component
-option specifies the field name, and the
-.B -text
-option specifies the field body.
-.PP
-The messages are the
-.I msgs
-in the named folder.
+The field name may consist of alphanumerics and dashes only.
+The field body may consist of arbitrary text.
.PP
Usually, annotation is performed by the commands
.BR dist ,
.BR repl ,
if they are given the
.B \-anno
-switch. This allows you to keep track of your distribution of,
+switch. This allows you to keep track of your redistribution of,
forwarding of, and replies to a message.
+The
+.B whatnow
+shell uses annoations to manage attachments, too.
.PP
By using
-.BR anno ,
-you can perform arbitrary annotations of your own.
+.BR anno
+manually, you can perform arbitrary annotations of your own.
+.PP
+.B Anno
+has three operation modes: Adding, deleting and listing of header lines.
+
+.SS "Add mode
+.PP
+This is the default mode.
+Historically, it had been the only mode available.
+.PP
Each message selected will be annotated with the lines
.PP
- field:\ date
- field:\ body
+.RS 5
+.nf
+field:\ date
+field:\ body
+.fi
+.RE
.PP
The
+.B \-component
+option specifies the field name.
+If no
+.B \-component
+.I field
+is specified,
+.B anno
+will prompt the user for the name of field for the annotation.
+.PP
+The
+.B \-text
+option specifies the field body.
+If it is missing, only the date annotation will be added.
+The
.B \-nodate
switch inhibits the date annotation, leaving only the
body annotation.
.B anno
prepends the annotations to the message.
Annotations are instead appended if the
-.B -append
+.B \-append
option is specified.
.PP
-If a
-.B \-component
-.I field
-is not specified when
-.B anno
-is invoked,
-.B anno
-will prompt the user for the name of field for the annotation.
-.PP
-The field specified must be a valid 2822-style message field name,
-which means that it may only consist of alphanumerics and dashes,
-The body specified is arbitrary text.
-.PP
-.B anno
+.B Anno
always does the annotation inplace in order to preserve
any links to the message.
.PP
+By default,
+.B anno
+changes the last-accessed and last-modified times on annotate messages
+to the time at which the annotation occurs.
+.B Anno
+preserves the original times if the
+.B \-preserve
+option is used.
+
+.SS "Delete mode
+.PP
The
-.B -list
-option produces a listing of the field bodies for header fields with
-names matching the specified component, one per line.
-The listing is numbered, starting at 1, if the
-.B -number
-option is also used.
-A tab character separates the number and the field body.
-The field body is treated as if it is a file name, and only the final
-path name component is listed.
-The complete field body is listed if the
-.B -text
-option is used, the contents of the text are ignored.
+.B \-delete
+mode removes header fields from messages.
+By default, the first header field whose name matches the component
+is deleted.
.PP
The
-.B -delete
-option removes header fields from messages.
-The first header field whose name matches the component is deleted if
-no other options are specified.
+.B \-component
+option specifies the field name of headers to delete.
+If no
+.B \-component
+.I field
+is specified,
+.B anno
+will prompt the user for the name.
+.PP
If the
-.B -text
-option is used in conjunction with the
-.B -delete
-option, the first header field whose name matches the component and
+.B \-text
+option is used,
+the first header field whose name matches the component and
whose body matches the text is deleted.
-The text is treated as if it was a file name; if it begins with a
+The text is treated as if it was a path name; if it begins with a
slash, the entire field body must match the text, otherwise just the
last path name component of the field body must match.
+.PP
If the
-.B -number
-option is used in conjuction with the
-.B -delete
-option, header field
-.I num
-whose name matches the component is deleted.
-The number matches that which is produced by the
-.B -list
-option.
-The special value
-.B all
-can be used for the number, and causes all components that match the
-name to be deleted.
+.B \-number
+option is used,
+the
+.IR n th
+header field whose name matches the component is deleted.
+The numbers are the same as those produced in
+.B \-list
+mode.
+The special value `all' can be used for the number,
+and causes all components that match the name to be deleted.
.PP
-By default,
+Either
+.B \-text
+or
+.B \-number
+may be specified, but not both at the same time.
+
+.SS "List mode
+.PP
+The
+.B \-list
+mode produces a listing of the field bodies for header fields with
+matching component names, one per line.
+If the
+.B \-number
+option is also used,
+the listing is numbered, starting at 1.
+.PP
+The
+.B \-component
+option specifies the field name of headers to list.
+If no
+.B \-component
+.I field
+is specified,
.B anno
-changes the last-accessed and last-modified times on annotate messages
-to the time at which the annotation occurs.
-.B Anno
-preserves the original times if the
-.B -preserve
-option is used.
-A matching
-.B -nopreserve
-option exists that allows time preservation to be turned off if enabled
-in the profile.
+will prompt the user for the name.
+
.SH FILES
.fc ^ ~
.nf
.RI ` +folder "' defaults to the current folder"
.RI ` msgs "' defaults to cur"
.RB ` \-date '
+.RB ` \-nopreserve '
.fi
.SH CONTEXT
If a folder is given, it will become the current folder. The first
message annotated will become the current message.
+
+.SH BUGS
+.PP
+The
+.B \-number
+switch must appear after either the
+.B \-list
+or the
+.B \-delete
+mode switch, on the command line.
+Otherwise it is not possible to determine if it takes an argument.
** COPYRIGHT file in the root directory of the nmh distribution for
** complete copyright information.
**
-** Three new options have been added: delete, list, and number.
-** Message header fields are used by the new MIME attachment code in
-** the send command. Adding features to generalize the anno command
-** seemed to be a better approach than the creation of a new command
-** whose features would overlap with those of the anno command.
-**
-** The -delete option deletes header elements that match the -component
-** field name. If -delete is used without the -text option, the first
-** header field whose field name matches the component name is deleted.
-** If the -delete is used with the -text option, and the -text argument
-** begins with a /, the first header field whose field name matches the
-** component name and whose field body matches the text is deleted. If
-** the -text argument does not begin with a /, then the text is assumed
-** to be the last component of a path name, and the first header field
-** whose field name matches the component name and a field body whose
-** last path name component matches the text is deleted. If the -delete
-** option is used with the new -number option described below, the nth
-** header field whose field name matches the component name is deleted.
-** No header fields are deleted if none of the above conditions are met.
-**
-** The -list option outputs the field bodies from each header field whose
-** field name matches the component name, one per line. If no -text
-** option is specified, only the last path name component of each field
-** body is output. The entire field body is output if the -text option
-** is used; the contents of the -text argument are ignored. If the -list
-** option is used in conjuction with the new -number option described
-** below, each line is numbered starting with 1. A tab separates the
-** number from the field body.
-**
-** The -number option works with both the -delete and -list options as
-** described above. The -number option takes an optional argument. A
-** value of 1 is assumed if this argument is absent.
+** Three new options have been added: delete, list, and number. Adding
+** features to generalize the anno command seemed to be a better approach
+** than the creation of a new command whose features would overlap with
+** those of the anno command.
*/
#include <h/mh.h>
#include <errno.h>
#include <utime.h>
+static enum { MODE_ADD, MODE_DEL, MODE_LIST } mode = MODE_ADD;
static struct swit switches[] = {
#define COMPSW 0
** static prototypes
*/
static void make_comp(unsigned char **);
-static int annosbr(int, char *, char *, char *, int, int, int);
-static int annotate(char *, char *, char *, int, int, int, int);
-static void annolist(char *, char *, char *, int);
+static int annotate(char *, unsigned char *, char *, int, int, int, int);
+static void annolist(char *, unsigned char *, int);
+static void dodel(int, unsigned char *, char *, FILE *, int);
+static void doadd(int, unsigned char *, char *, FILE *, int, int);
int
struct msgs_array msgs = { 0, 0, NULL };
struct msgs *mp;
int append = 0; /* append annotations instead of default prepend */
- int delete = -2; /* delete header element if set */
- int list = 0; /* list header elements if set */
int number = 0; /* delete specific number of like elements if set */
- int havemsgs = 0;
#ifdef LOCALE
setlocale(LC_ALL, "");
#endif
invo_name = mhbasename(argv[0]);
-
- /* read user profile/context */
context_read();
arguments = getarguments(invo_name, argc, argv, 1);
print_version(invo_name);
done(1);
+ case DELETESW: /* delete annotations */
+ mode = MODE_DEL;
+ continue;
+
+ case LISTSW: /* produce a listing */
+ mode = MODE_LIST;
+ continue;
+
case COMPSW:
if (comp)
adios(NULL, "only one component at a time!");
argp[-2]);
continue;
- case DATESW:
- datesw++;
- continue;
- case NDATESW:
- datesw = 0;
- continue;
-
case TEXTSW:
if (text)
adios(NULL, "only one body at a time!");
argp[-2]);
continue;
- case DELETESW: /* delete annotations */
- delete = 0;
- continue;
-
- case LISTSW: /* produce a listing */
- list = 1;
- continue;
-
case NUMBERSW: /* number listing or delete by number */
- if (number != 0)
- adios(NULL, "only one number at a time!");
-
- if (argp-arguments == argc-1 || **argp == '-')
+ if (mode == MODE_ADD) {
+ adios(NULL, "-number switch must appear after -list or -delete, only.");
+ }
+ if (mode == MODE_LIST) {
number = 1;
-
- else {
- if (strcmp(*argp, "all") == 0)
- number = -1;
- else if (!(number = atoi(*argp)))
- /* FIXME: fails for
- ** `-list -number l:10'
- ** but okay if we add `all'.
- */
- adios(NULL, "missing argument to %s", argp[-1]);
+ continue;
+ }
+ /* MODE_DEL */
+ if (number) {
+ adios(NULL, "only one number at a time!");
+ }
+ if (*argp && strcmp(*argp, "all")==0) {
+ number = -1;
argp++;
+ continue;
+ }
+ if (!*argp || !(number = atoi(*argp))) {
+ adios(NULL, "missing argument to %s",
+ argp[-1]);
+ }
+ if (number < 0) {
+ adios(NULL, "invalid number (%d).",
+ number);
}
+ argp++;
+ continue;
- delete = number;
+ case DATESW:
+ datesw++;
+ continue;
+ case NDATESW:
+ datesw = 0;
continue;
case APPENDSW:
file = cp;
} else {
app_msgarg(&msgs, cp);
- havemsgs = 1;
}
}
- if (file && (folder || havemsgs)) {
+ if (file && (folder || !msgs.size)) {
adios(NULL, "Don't intermix files and messages.");
}
-
- make_comp(&comp);
+ if (!datesw && !text) {
+ adios(NULL, "-nodate without -text is a no-op.");
+ }
+ if (number && text) {
+ adios(NULL, "Don't combine -number with -text.");
+ }
if (file) {
- if (list)
- annolist(file, comp, text, number);
+ if (mode == MODE_LIST)
+ annolist(file, comp, number);
else
- annotate(file, comp, text,
- datesw, delete, append, preserve);
+ annotate(file, comp, text, datesw, number,
+ append, preserve);
done(0);
}
/* annotate all the SELECTED messages */
for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
if (is_selected(mp, msgnum)) {
- if (list)
- annolist(m_name(msgnum), comp, text, number);
+ if (mode == MODE_LIST)
+ annolist(m_name(msgnum), comp, number);
else
annotate(m_name(msgnum), comp, text, datesw,
- delete, append, preserve);
+ number, append, preserve);
}
}
static void
make_comp(unsigned char **ap)
{
- register unsigned char *cp;
+ unsigned char *cp;
char buffer[BUFSIZ];
- if (*ap == NULL) {
+ if (!*ap) {
printf("Enter component name: ");
fflush(stdout);
- if (fgets(buffer, sizeof buffer, stdin) == NULL)
+ if (!fgets(buffer, sizeof buffer, stdin)) {
done(1);
+ }
*ap = trimcpy(buffer);
}
if ((cp = *ap + strlen(*ap) - 1) > *ap && *cp == ':')
- *cp = 0;
+ *cp = '\0';
if (strlen(*ap) == 0)
adios(NULL, "null component name");
if (**ap == '-')
}
-static int
-annotate(char *file, char *comp, char *text, int datesw,
- int delete, int append, int preserve)
-{
- int i, fd;
- struct utimbuf b;
- struct stat s;
-
- /* open and lock the file to be annotated */
- if ((fd = lkopen(file, O_RDWR, 0)) == NOTOK) {
- switch (errno) {
- case ENOENT:
- break;
- default:
- admonish(file, "unable to lock and open");
- break;
- }
- return 1;
- }
-
- if (stat(file, &s) == -1) {
- advise("can't get access and modification times for %s", file);
- preserve = 0;
- }
-
- b.actime = s.st_atime;
- b.modtime = s.st_mtime;
-
- i = annosbr(fd, file, comp, text, datesw, delete, append);
-
- if (preserve && utime(file, &b) == -1) {
- advise("can't set access and modification times for %s", file);
- }
- lkclose(fd, file);
- return i;
-}
-
/*
** Produce a listing of all header fields (annotations) whose field
-** name matches comp. Number the listing if number is set. Treat the
-** field bodies as path names and just output the last component unless
-** text is non-NULL. We don't care what text is set to.
+** name matches comp. Number the listing if number is set.
*/
static void
-annolist(char *file, char *comp, char *text, int number)
+annolist(char *file, unsigned char *comp, int number)
{
int c;
- int count; /* header field (annotation) counter */
+ int count = 1; /* header field (annotation) counter */
char *cp;
char *field;
int field_size;
FILE *fp;
int length;
int n; /* number of bytes written */
- char *sp;
if ((fp = fopen(file, "r")) == NULL) {
adios(file, "unable to open");
/* We'll grow this buffer as needed. */
field = (char *)mh_xmalloc(field_size = 256);
+ make_comp(&comp);
length = strlen(comp); /* Convenience copy. */
- count = 0;
do {
/*
continue;
}
if (number) {
- printf("%d\t", ++count);
- }
- if (!text && (sp = strrchr(cp, '/'))) {
- cp = sp + 1;
+ printf("%d\t", count++);
}
printf("%s\n", cp);
}
return;
}
+
static int
-annosbr(int fd, char *file, char *comp, char *text, int datesw, int delete,
- int append)
+annotate(char *file, unsigned char *comp, char *text, int datesw,
+ int number, int append, int preserve)
{
- int mode, tmpfd;
- char *cp, *sp;
+ int fd;
+ struct utimbuf b;
+ int perms, tmpfd;
char tmpfil[BUFSIZ];
struct stat st;
FILE *tmp;
- int c;
- int count; /* header field (annotation) counter */
- char *field = NULL;
- int field_size = 0;
- FILE *fp = NULL;
- int length;
- int n; /* number of bytes written */
- mode = fstat(fd, &st) != NOTOK ? (int)(st.st_mode & 0777) : m_gmprot();
+ /* open and lock the file to be annotated */
+ if ((fd = lkopen(file, O_RDWR, 0)) == NOTOK) {
+ switch (errno) {
+ case ENOENT:
+ break;
+ default:
+ admonish(file, "unable to lock and open");
+ break;
+ }
+ return 1;
+ }
+
+ if (stat(file, &st) == -1) {
+ advise("can't get access and modification times for %s", file);
+ preserve = 0;
+ }
+ b.actime = st.st_atime;
+ b.modtime = st.st_mtime;
+
+ perms = fstat(fd, &st) != NOTOK ?
+ (int)(st.st_mode & 0777) : m_gmprot();
strncpy(tmpfil, m_mktemp2(file, "annotate", NULL, &tmp),
sizeof(tmpfil));
- chmod(tmpfil, mode);
+ chmod(tmpfil, perms);
+
+ make_comp(&comp);
+
+ if (mode == MODE_DEL) {
+ dodel(fd, comp, text, tmp, number);
+ }
+ if (mode == MODE_ADD) {
+ doadd(fd, comp, text, tmp, datesw, append);
+ }
+
+ cpydata(fd, fileno(tmp), file, tmpfil);
+ fclose(tmp);
+
+ if ((tmpfd = open(tmpfil, O_RDONLY)) == NOTOK) {
+ adios(tmpfil, "unable to open for re-reading");
+ }
+ lseek(fd, (off_t) 0, SEEK_SET);
+
+ /*
+ ** We're making the file shorter if we're deleting a header field
+ ** so the file has to be truncated or it will contain garbage.
+ */
+ if (mode == MODE_DEL && ftruncate(fd, 0) == -1) {
+ adios(tmpfil, "unable to truncate.");
+ }
+ cpydata(tmpfd, fd, tmpfil, file);
+ close(tmpfd);
+ unlink(tmpfil);
+
+ if (preserve && utime(file, &b) == -1) {
+ advise("can't set access and modification times for %s", file);
+ }
+ lkclose(fd, file);
+ return 0;
+}
+
+/*
+** We're trying to delete a header field (annotation).
+**
+** - If number is greater than zero,
+** we're deleting the nth header field that matches
+** the field (component) name.
+** - If number is zero and text is NULL,
+** we're deleting the first field in which the field name
+** matches the component name.
+** - If number is zero and text is set,
+** we're deleting the first field in which both the field name
+** matches the component name and the field body matches the text.
+** - If number is -1,
+** we delete all matching fields.
+*/
+static void
+dodel(int fd, unsigned char *comp, char *text, FILE *tmp, int number)
+{
+ int length = strlen(comp); /* convenience copy */
+ int count = 1; /* Number of matching header line. */
+ int c, n;
+ char *cp;
+ char *field = NULL;
+ int field_size = 256;
+ FILE *fp;
/*
** We're going to need to copy some of the message file to the
** a buffer to hold the header components as they're read in.
** This buffer is grown as needed later.
*/
- if (delete >= -1 || append != 0) {
- if ((fp = fdopen(fd, "r")) == NULL) {
- adios(NULL, "unable to fdopen file.");
- }
- field = (char *)mh_xmalloc(field_size = 256);
+ if ((fp = fdopen(fd, "r")) == NULL) {
+ adios(NULL, "unable to fdopen file.");
}
+ field = (char *)mh_xmalloc(field_size);
/*
- ** We're trying to delete a header field (annotation) if the
- ** delete flag is greater -2. A value greater than zero
- ** means that we're deleting the nth header field that matches
- ** the field (component) name. A value of zero means that
- ** we're deleting the first field in which both the field name
- ** matches the component name and the field body matches the text.
- ** The text is matched in its entirety if it begins with a slash;
- ** otherwise the text is matched against whatever portion of the
- ** field body follows the last slash. This allows matching of
- ** both absolute and relative path names. This is because this
- ** functionality was added to support attachments. It might be
- ** worth having a separate flag to indicate path name matching
- ** to make it more general. A value of -1 means to delete all
- ** matching fields.
+ ** Copy lines from the input file to the temporary file
+ ** until we either find the one that we're looking
+ ** for (which we don't copy) or we reach the end of
+ ** the headers. Both a blank line and a line beginning
+ ** with a - terminate the headers so that we can handle
+ ** both drafts and RFC-2822 format messages.
*/
- if (delete >= -1) {
- length = strlen(comp); /* convenience copy */
- count = 0; /* Only used if we're deleting by number. */
-
+ do {
/*
- ** Copy lines from the input file to the temporary file
- ** until we either find the one that we're looking
- ** for (which we don't copy) or we reach the end of
- ** the headers. Both a blank line and a line beginning
- ** with a - terminate the headers so that we can handle
- ** both drafts and RFC-2822 format messages.
+ ** Get a line from the input file, growing the
+ ** field buffer as needed. We do this so that
+ ** we can fit an entire line in the buffer making
+ ** it easy to do a string comparison on both the
+ ** field name and the field body which might be
+ ** a long path name.
*/
- do {
- /*
- ** Get a line from the input file, growing the
- ** field buffer as needed. We do this so that
- ** we can fit an entire line in the buffer making
- ** it easy to do a string comparison on both the
- ** field name and the field body which might be
- ** a long path name.
- */
- for (n=0, cp=field; (c=getc(fp)) != EOF; *cp++ = c) {
- if (c == '\n' && (c = getc(fp)) != ' ' &&
- c != '\t') {
- ungetc(c, fp);
- c = '\n';
- break;
- }
+ for (n=0, cp=field; (c=getc(fp)) != EOF; *cp++ = c) {
+ if (c == '\n' && (c = getc(fp)) != ' ' &&
+ c != '\t') {
+ ungetc(c, fp);
+ c = '\n';
+ break;
+ }
- if (++n >= field_size - 1) {
- field = (char *) mh_xrealloc(field,
- field_size *= 2);
- cp = field + n - 1;
- }
+ if (++n >= field_size - 1) {
+ field = (char *) mh_xrealloc(field,
+ field_size *= 2);
+ cp = field + n - 1;
}
- *cp = '\0';
+ }
+ *cp = '\0';
+ if (strncasecmp(field, comp, length)==0 &&
+ field[length] == ':') {
/*
- ** Check for a match on the field name. We delete
- ** the line by not copying it to the temporary
- ** file if
- **
- ** o The delete flag is 0, meaning that we're
- ** going to delete the first matching
- ** field, and the text is NULL meaning that
- ** we don't care about the field body.
- **
- ** o The delete flag is 0, meaning that we're
- ** going to delete the first matching
- ** field, and the text begins with a / meaning
- ** that we're looking for a full path name,
- ** and the text matches the field body.
- **
- ** o The delete flag is 0, meaning that we're
- ** going to delete the first matching
- ** field, the text does not begin with a /
- ** meaning that we're looking for the last
- ** path name component, and the last path
- ** name component matches the text.
- **
- ** o The delete flag is positive meaning that
- ** we're going to delete the nth field
- ** with a matching field name, and this is
- ** the nth matching field name.
- **
- ** o The delete flag is -1 meaning that we're
- ** going to delete all fields with a
- ** matching field name.
+ ** This component matches and thus is a candidate.
+ ** We delete the line by not copying it to the
+ ** temporary file. Thus:
+ ** - Break if we've found the one to delete.
+ ** - Continue if this is one to delete, but
+ ** there'll be further ones.
*/
- if (strncasecmp(field, comp, length)==0 &&
- field[length] == ':') {
- if (!delete) {
- if (!text) {
- break;
- }
- for (cp=field+length+1;
- *cp==' ' || *cp=='\t';
- cp++) {
- continue;
- }
- if (*text == '/' &&
- strcmp(cp, text)==0) {
- break;
- } else {
- if ((sp = strrchr(cp, '/'))) {
- cp = sp + 1;
- }
- if (strcmp(cp, text)==0) {
- break;
- }
- }
- } else if (delete == -1) {
- continue;
- } else if (++count == delete) {
- break;
- }
+
+ if (!number && !text) {
+ /* this first one is it */
+ break;
+ }
+
+ if (number == -1) {
+ /* delete all of them */
+ continue;
+ } else if (number == count++) {
+ /* delete this specific one */
+ break;
}
+ if (text) {
+ /* delete the first matching one */
+ cp = field+length+1;
+ while (*cp==' ' || *cp=='\t') {
+ cp++; /* eat leading whitespace */
+ }
+ if (*text == '/' && strcmp(text, cp)==0) {
+ break; /* full path matches */
+ } else if (strcmp(text, mhbasename(cp))==0) {
+ break; /* basename matches */
+ }
+ }
/*
- ** This line wasn't a match so copy it to the
- ** temporary file.
+ ** Although the compoment name mached, it
+ ** wasn't the right one.
*/
- if ((n = fputs(field, tmp)) == EOF ||
- (c=='\n' && fputc('\n', tmp)==EOF)) {
- adios(NULL, "unable to write temporary file.");
- }
- } while (*field && *field != '-');
- free(field);
+ }
+
+ /* Copy it. */
+ if ((n = fputs(field, tmp)) == EOF ||
+ (c=='\n' && fputc('\n', tmp)==EOF)) {
+ adios(NULL, "unable to write temporary file.");
+ }
+
+ } while (*field && *field != '-');
+
+ free(field);
+
+ fflush(tmp);
+ fflush(fp); /* The underlying fd will be closed by lkclose() */
+
+ /*
+ ** We've been messing with the input file position. Move the
+ ** input file descriptor to the current place in the file
+ ** because the stock data copying routine uses the descriptor,
+ ** not the pointer.
+ */
+ if (lseek(fd, (off_t)ftell(fp), SEEK_SET) == (off_t)-1) {
+ adios(NULL, "can't seek.");
+ }
+}
- } else {
+
+static void
+doadd(int fd, unsigned char *comp, char *text, FILE *tmp, int datesw,
+ int append)
+{
+ char *cp, *sp;
+ int c;
+ FILE *fp = NULL;
+
+ if (append) {
/*
- ** Find the end of the headers before adding the
- ** annotations if we're appending instead of the default
- ** prepending. A special check for no headers is needed
- ** if appending.
+ ** We're going to need to copy some of the message
+ ** file to the temporary file while examining the
+ ** contents. Convert the message file descriptor to
+ ** a file pointer since it's a lot easier and more
+ ** efficient to use stdio for this. Also allocate
+ ** a buffer to hold the header components as they're
+ ** read in. This buffer is grown as needed later.
*/
- if (append) {
+ if ((fp = fdopen(fd, "r")) == NULL) {
+ adios(NULL, "unable to fdopen file.");
+ }
+ /* Find the end of the headers. */
+ if ((c = getc(fp)) == '\n') {
+ /* Special check for no headers is needed. */
+ rewind(fp);
+ } else {
/*
- ** Copy lines from the input file to the temporary
- ** file until we reach the end of the headers.
+ ** Copy lines from the input file to the
+ ** temporary file until we reach the end
+ ** of the headers.
*/
- if ((c = getc(fp)) == '\n') {
- rewind(fp);
- } else {
+ putc(c, tmp);
+ while ((c = getc(fp)) != EOF) {
putc(c, tmp);
- while ((c = getc(fp)) != EOF) {
- putc(c, tmp);
- if (c == '\n') {
- ungetc(c = getc(fp), fp);
- if (c == '\n' || c == '-') {
- break;
- }
+ if (c == '\n') {
+ ungetc(c = getc(fp), fp);
+ if (c == '\n' || c == '-') {
+ break;
}
}
}
}
+ }
- if (datesw) {
- fprintf(tmp, "%s: %s\n", comp, dtimenow());
- }
- if ((cp = text)) {
- do {
- while (*cp == ' ' || *cp == '\t') {
- cp++;
- }
- sp = cp;
- while (*cp && *cp++ != '\n') {
- continue;
- }
- if (cp - sp) {
- fprintf(tmp, "%s: %*.*s", comp,
- (int)(cp - sp),
- (int)(cp - sp), sp);
- }
- } while (*cp);
- if (cp[-1] != '\n' && cp != text) {
- putc('\n', tmp);
+ if (datesw) {
+ fprintf(tmp, "%s: %s\n", comp, dtimenow());
+ }
+ if ((cp = text)) {
+ /* Add body text header */
+ do {
+ while (*cp == ' ' || *cp == '\t') {
+ cp++;
}
+ sp = cp;
+ while (*cp && *cp++ != '\n') {
+ continue;
+ }
+ if (cp - sp) {
+ fprintf(tmp, "%s: %*.*s", comp,
+ (int)(cp - sp),
+ (int)(cp - sp), sp);
+ }
+ } while (*cp);
+ if (cp[-1] != '\n' && cp != text) {
+ putc('\n', tmp);
}
}
fflush(tmp);
** because the stock data copying routine uses the descriptor,
** not the pointer.
*/
- if (append || delete >= -1) {
+ if (append) {
if (lseek(fd, (off_t)ftell(fp), SEEK_SET) == (off_t)-1) {
adios(NULL, "can't seek.");
}
}
-
- cpydata(fd, fileno(tmp), file, tmpfil);
- fclose(tmp);
-
- if ((tmpfd = open(tmpfil, O_RDONLY)) == NOTOK) {
- adios(tmpfil, "unable to open for re-reading");
- }
- lseek(fd, (off_t) 0, SEEK_SET);
-
- /*
- ** We're making the file shorter if we're deleting a header field
- ** so the file has to be truncated or it will contain garbage.
- */
- if (delete >= -1 && ftruncate(fd, 0) == -1) {
- adios(tmpfil, "unable to truncate.");
- }
- cpydata(tmpfd, fd, tmpfil, file);
- close(tmpfd);
- unlink(tmpfil);
-
- /*
- ** Close the delete file so that we don't run out of file pointers if
- ** we're doing piles of files. Note that this will make the close() in
- ** lkclose() fail, but that failure is ignored so it's not a problem.
- */
- if (delete >= -1) {
- fclose(fp);
- }
- return 0;
}