header.c 6.88 KB
/* GNU Mailutils -- a suite of utilities for electronic mail
   Copyright (C) 2007 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 2, 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, write to the Free Software
   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
   MA 02110-1301 USA */

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>
#include <mailutils/mailutils.h>

char *file;
mu_header_t header;

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, NULL);
  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_header_destroy (&header, NULL);
  load_file (argv[1]);
}

void
cmd_free (int argc, char **argv)
{
  if (check_args (argv[0], argc, 1, 1))
    return;
  mu_header_destroy (&header, NULL);
}

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_stream (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);
  if (status)
    {
      mu_error ("%u: cannot seek: %s", line_num, mu_strerror (status));
      return;
    }

  while (mu_stream_sequential_read (stream, buf, sizeof buf, &n) == 0
	 && n > 0)
    {
      fwrite (buf, 1, n, stdout);
    }
}  

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));
}

/* 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);
  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_stream (header, &str);
  if (status)
    {
      mu_error ("%u: cannot get stream: %s", line_num, mu_strerror (status));
      return;
    }
  mu_stream_seek (str, 0, SEEK_SET);
  while (prompt (1), fgets(buf, sizeof buf, stdin))
    {
      mu_stream_sequential_write (str, buf, strlen (buf));
      if (buf[0] == '\n')
	break;
    }
}
	 
struct cmdtab
{
  char *name;
  void (*fun) (int argc, char **argv);
};

static struct cmdtab cmdtab[] = {
  { "quit", cmd_quit },
  { "load", cmd_load },
  { "free", cmd_free },
  { "print", cmd_print },
  { "dump", cmd_dump },
  { "remove", cmd_remove },
  { "insert", cmd_insert },
  { "write", cmd_write },
  { 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;
}

int
main (int argc, char **argv)
{
  int c;
  char buf[512];

  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);
    }

  while (prompt(0), fgets(buf, sizeof buf, stdin))
    {
      int c;
      char **v;
      struct cmdtab *cmd;
      int status;

      line_num++;
      status = mu_argcv_get (buf, NULL, "#", &c, &v);
      if (status)
	{
	  mu_error ("%u: cannot parse: %s",
		   line_num, mu_strerror (status));
	  continue;
	}

      cmd = find_cmd (v[0]);
      if (!cmd)
	{
	  mu_error ("%u: unknown command %s",
		   line_num, v[0]);
	}
      else
	cmd->fun (c, v);

      mu_argcv_free (c, v);

    }
  exit (0);
}