/* GNU Mailutils -- a suite of utilities for electronic mail Copyright (C) 2007, 2009-2012, 2014-2016 Free Software Foundation, Inc. GNU Mailutils is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GNU Mailutils is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */ #ifdef HAVE_CONFIG_H # include <config.h> #endif #include <stdio.h> #include <string.h> #include <errno.h> #include <sys/stat.h> #include <mailutils/mailutils.h> char *file; mu_header_t header; mu_iterator_t iterator; mu_stream_t hstream; char *ps[] = { "> ", ". " }; int interactive; static void prompt(int l) { if (interactive) { printf ("%s", ps[l]); fflush (stdout); } } static int load_file (const char *name) { struct stat st; size_t nread; char *buf; FILE *fp; int status; if (stat (name, &st)) { mu_error ("cannot stat %s: %s", name, mu_strerror (errno)); return 1; } buf = malloc (st.st_size + 2); if (!buf) { mu_error ("not enough memory"); return 1; } fp = fopen (name, "r"); if (!fp) { mu_error ("cannot open file %s: %s", name, mu_strerror (errno)); free (buf); return 1; } nread = fread (buf, 1, st.st_size, fp); fclose (fp); if (nread != st.st_size) { mu_error ("short read on file %s", name); free (buf); return 1; } buf[st.st_size] = '\n'; buf[st.st_size+1] = 0; status = mu_header_create (&header, buf, st.st_size + 1); free (buf); if (status) { mu_error ("cannot create header: %s", mu_strerror (status)); return 1; } return 0; } unsigned line_num = 0; static int check_args (char const *cmdname, int argc, int amin, int amax) { if (argc < amin) { mu_error ("%u: %s: too few arguments", line_num, cmdname); return 1; } if (amax > 0 && argc > amax) { mu_error ("%u: %s: too many arguments", line_num, cmdname); return 1; } return 0; } void cmd_quit (int argc, char **argv) { exit (0); } void cmd_load (int argc, char **argv) { if (check_args (argv[0], argc, 2, 2)) return; mu_stream_destroy (&hstream); mu_header_destroy (&header); load_file (argv[1]); } void cmd_free (int argc, char **argv) { if (check_args (argv[0], argc, 1, 1)) return; mu_iterator_destroy (&iterator); mu_stream_destroy (&hstream); mu_header_destroy (&header); } void cmd_print (int argc, char **argv) { char *fn; int num = 1; int status; const char *str; if (check_args (argv[0], argc, 2, 3)) return; fn = argv[1]; if (argc == 3) num = atoi (argv[2]); status = mu_header_sget_value_n (header, fn, num, &str); if (status == 0) printf ("%s: %s\n", fn, str); else mu_error ("%u: %s", line_num, mu_strerror (status)); } void cmd_dump (int argc, char **argv) { mu_off_t off = 0; size_t n; mu_stream_t stream; char buf[512]; int status; if (check_args (argv[0], argc, 1, 2)) return; if (argc == 2) off = strtoul (argv[1], NULL, 0); status = mu_header_get_streamref (header, &stream); if (status) { mu_error ("%u: cannot get stream: %s", line_num, mu_strerror (status)); return; } status = mu_stream_seek (stream, off, SEEK_SET, NULL); if (status) { mu_stream_destroy (&stream); mu_error ("%u: cannot seek: %s", line_num, mu_strerror (status)); return; } while (mu_stream_read (stream, buf, sizeof buf, &n) == 0 && n > 0) { fwrite (buf, 1, n, stdout); } mu_stream_destroy (&stream); } void cmd_remove (int argc, char **argv) { char *fn; int num = 1; int status; if (check_args (argv[0], argc, 2, 3)) return; fn = argv[1]; if (argc == 3) num = atoi (argv[2]); status = mu_header_remove (header, fn, num); if (status) mu_error ("%u: %s: %s", line_num, argv[0], mu_strerror (status)); mu_stream_destroy (&hstream); } /* insert header value [ref [num] [before|after] [replace]] */ void cmd_insert (int argc, char **argv) { int status; int flags = 0; char *ref = NULL; int num = 1; int n; if (check_args (argv[0], argc, 3, 7)) return; if (argc >= 4) { ref = argv[3]; n = 4; if (n < argc) { char *p; int tmp; tmp = strtoul(argv[4], &p, 0); if (*p == 0) { num = tmp; n++; } for (; n < argc; n++) { if (strcmp(argv[n], "before") == 0) flags |= MU_HEADER_BEFORE; else if (strcmp(argv[n], "after") == 0) ; else if (strcmp(argv[n], "replace") == 0) flags |= MU_HEADER_REPLACE; else { mu_error("%u: %s: unknown option", line_num, argv[4]); return; } } } } status = mu_header_insert (header, argv[1], argv[2], ref, num, flags); mu_stream_destroy (&hstream); if (status) mu_error ("%u: %s: %s", line_num, argv[0], mu_strerror (status)); } void cmd_write (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_SET, 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_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)) return; if (argc == 1) { mu_iterator_t itr; MU_ASSERT (mu_header_get_iterator (header, &itr)); for (mu_iterator_first (itr); !mu_iterator_is_done (itr); mu_iterator_next (itr)) { const char *hdr, *val; MU_ASSERT (mu_iterator_current_kv (itr, (const void**)&hdr, (void**)&val)); printf ("%s: %s\n", hdr, val); } mu_iterator_destroy (&itr); } else { const char *hdr, *val; if (!iterator) MU_ASSERT (mu_header_get_iterator (header, &iterator)); if (strcmp (argv[1], "first") == 0 || strcmp (argv[1], "1") == 0) mu_iterator_first (iterator); else if (strcmp (argv[1], "next") == 0 || strcmp (argv[1], "n") == 0) { mu_iterator_next (iterator); if (mu_iterator_is_done (iterator)) { printf ("Past end of headers. Use `itr first'.\n"); return; } } MU_ASSERT (mu_iterator_current_kv (iterator, (const void **)&hdr, (void**)&val)); printf ("%s: %s\n", hdr, val); } } void cmd_readline (int argc, char **argv) { char *buf; size_t size = 128; size_t nbytes; if (check_args (argv[0], argc, 1, 2)) return; if (argc == 2) size = atoi (argv[1]); buf = malloc (size); if (!buf) abort (); if (!hstream) mu_header_get_streamref (header, &hstream); mu_stream_readline (hstream, buf, size, &nbytes); printf ("\"%*.*s\"", (int) nbytes, (int) nbytes, buf); free (buf); } struct cmdtab { char *name; void (*fun) (int argc, char **argv); char *args; char *help; }; static void cmd_help (int argc, char **argv); static struct cmdtab cmdtab[] = { { "quit", cmd_quit, NULL, "quit the program" }, { "load", cmd_load, "FILE", "read headers from the specified FILE" }, { "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, "[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]", "remove the Nth (by default, 1st) instance of header named NAME" }, { "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 } }; static struct cmdtab * find_cmd (const char *name) { struct cmdtab *p; for (p = cmdtab; p->name; p++) if (strcmp (p->name, name) == 0) return p; return NULL; } static void format_help_str (int col, char *p) { if (col > 31) col = 80; while (*p) { int len; char *q; if (*p == ' ' || *p == '\t') { p++; continue; } q = strchr (p, ' '); if (!q) len = strlen (p); else len = q - p; if (col + len > 80) { fputc ('\n', stdout); for (col = 0; col < 30; col++) fputc (' ', stdout); } for (; len > 0; len--, p++, col++) fputc (*p, stdout); if (q) { if (col < 80) { fputc (' ', stdout); col++; } p++; } } fputc ('\n', stdout); } void cmd_help (int argc, char **argv) { struct cmdtab *p; if (check_args (argv[0], argc, 1, 2)) return; for (p = cmdtab; p->name; p++) { int col; col = printf ("%s", p->name); for (; col < 10; col++) fputc (' ', stdout); if (p->args) col += printf ("%s", p->args); for (; col < 30; col++) fputc (' ', stdout); format_help_str (col, p->help); } } int docmd (int argc, char **argv) { struct cmdtab *cmd = find_cmd (argv[0]); if (!cmd) { mu_error ("%u: unknown command %s", line_num, argv[0]); return 1; } else cmd->fun (argc, argv); return 0; } int main (int argc, char **argv) { int c; char buf[512]; char **prevv = NULL; size_t prevc = 0; interactive = isatty (0); while ((c = getopt (argc, argv, "f:h")) != EOF) { switch (c) { case 'f': file = optarg; break; case 'h': printf ("usage: header [-f file]\n"); exit (0); default: exit (1); } } if (file) { if (load_file (file)) exit (1); } else { int status = mu_header_create (&header, NULL, 0); if (status) { mu_error ("cannot create header: %s", mu_strerror (status)); exit (1); } } while (prompt (0), fgets (buf, sizeof buf, stdin)) { struct mu_wordsplit ws; line_num++; ws.ws_comment = "#"; if (mu_wordsplit (buf, &ws, MU_WRDSF_DEFFLAGS | MU_WRDSF_COMMENT)) { mu_error ("cannot split line `%s': %s", buf, mu_wordsplit_strerror (&ws)); continue; } if (ws.ws_wordc == 0) { if (prevc) docmd (prevc, prevv); } else { docmd (ws.ws_wordc, ws.ws_wordv); mu_argcv_free (prevc, prevv); mu_wordsplit_get_words (&ws, &prevc, &prevv); } mu_wordsplit_free (&ws); } mu_argcv_free (prevc, prevv); exit (0); }