/*
 * Runscript    Run a login-or-something script.
 *		A basic like "programming language".
 *		This program also looks like a basic interpreter :
 *		a bit messy. (But hey, I'm no compiler writer :-))
 *
 * Author:	Miquel van Smoorenburg, miquels@drinkel.ow.nl
 *
 * Bugs:	The "expect" routine is, unlike gosub, NOT reentrant !
 *
 *		This file is part of the minicom communications package,
 *		Copyright 1991-1995 Miquel van Smoorenburg,
 *		1997-1999 Jukka Lahtinen
 *
 *		This program 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 of the License, or (at your option) any later version.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * 10.07.98 jl  Added the log command
 * 05.04.99 jl  The logfile name should also be passed as a parameter
 * 04.03.2002 jl Treat the ^ character between quotes as control code prefix
 * 10.09.2013 ts Support sending the null character
 * 10.10.2013 ts Add the pipedshell command
 */
#include <config.h>
#include <sys/wait.h>
#include <stdarg.h>

#include "port.h"
#include "minicom.h"
#include "intl.h"

#define OK	0
#define ERR	-1
#define RETURN	1
#define BREAK	2

enum {
  NULL_CHARACTER = 254,
  SKIP_NEWLINE   = 255,
};

struct line {
  char *line;
  int labelcount;
  int lineno;
  struct line *next;
};

struct var {
  char *name;
  int value;
  struct var *next;
};

/*
 * Structure describing the script we are currently executing.
 */
struct env {
  struct line *lines;		/* Start of all lines */
  struct var *vars;		/* Start of all variables */
  const char *scriptname;	/* Name of this script */
  int verbose;			/* Are we verbose? */
  jmp_buf ebuf;			/* For exit */
  int exstat;			/* For exit */
};

struct env *curenv;		/* Execution environment */
int gtimeout = 120;		/* Global Timeout */
int etimeout = 0;		/* Timeout in expect routine */
jmp_buf ejmp;			/* To jump to if expect times out */
int inexpect = 0;		/* Are we in the expect routine */
const char *newline;		/* What to print for '\n'. */
const char *s_login = "name";	/* User's login name */
const char *s_pass = "password";/* User's password */
struct line *thisline;		/* Line to be executed */
int laststatus = 0;		/* Status of last command */
char homedir[256];		/* Home directory */
char logfname[PARS_VAL_LEN];	/* Name of logfile */

static char inbuf[65];		/* Input buffer. */

/* Forward declarations */
int s_exec(char *);
int execscript(const char *);

/*
 * Walk through the environment, see if LOGIN and/or PASS are present.
 * If so, delete them. (Someone using "ps" might see them!)
 */
void init_env(void)
{
  extern char **environ;
  char **e;

  for (e = environ; *e; e++) {
    if (!strncmp(*e, "LOGIN=", 6)) {
      s_login = *e + 6;
      *e = "LOGIN=";
    }
    if (!strncmp(*e, "PASS=", 5)) {
      s_pass = *e + 5;
      *e = "PASS=";
    }
  }
}


/*
 * Return an environment variable.
 */
const char *mygetenv(char *env)
{
  if (!strcmp(env, "LOGIN"))
    return s_login;
  if (!strcmp(env, "PASS"))
    return s_pass;
  return getenv(env);
}

/*
 * Display a syntax error and exit.
 */
void syntaxerr(const char *s)
{
  fprintf(stderr, _("script \"%s\": syntax error in line %d %s%s\n"),
          curenv->scriptname, thisline->lineno, s, "\r");
  exit(1);
}

/*
 * Skip all space
 */
void skipspace(char **s)
{
  while (**s == ' ' || **s == '\t')
    (*s)++;
}

/*
 * Our clock. This gets called every second.
 */
void myclock(int dummy)
{
  (void)dummy;
  signal(SIGALRM, myclock);
  alarm(1);

  if (--gtimeout == 0) {
    fprintf(stderr, _("script \"%s\": global timeout%s\n"),
            curenv->scriptname,"\r");
    exit(1);
  }
  if (inexpect && etimeout && --etimeout == 0)
    siglongjmp(ejmp, 1);
}

static char *buffer; /* The buffer is only growing and never freed... */
static unsigned buffersize;

static void buf_wr(unsigned idx, unsigned char val)
{
  if (idx >= buffersize)
    {
      buffersize += 64;
      buffer = realloc(buffer, buffersize);
    }
  buffer[idx] = val;
}

static inline char buf_rd(unsigned idx)
{
  return buffer[idx];
}

static unsigned bufsize()
{
  return buffersize;
}

static inline char *buf()
{
  return buffer;
}

/*
 * Read a word and advance pointer.
 * Also processes quoting, variable substituting, and \ escapes.
 */
char *getword(char **s)
{
  unsigned int len;
  int f;
  int idx = 0;
  const char *t = *s;
  int sawesc = 0;
  int sawq = 0;
  const char *env;
  char envbuf[32];

  if (**s == 0)
    return NULL;

  for (len = 0; ; len++) {
    if (sawesc && t[len]) {
      sawesc = 0;
      if (t[len] <= '7' && t[len] >= '0') {
        buf_wr(idx, 0);
        for (f = 0; f < 4 && len < bufsize() && t[len] <= '7' &&
             t[len] >= '0'; f++)
          buf_wr(idx, 8 * buf_rd(idx) + t[len++] - '0');
        if (buf_rd(idx) == 0)
          buf_wr(idx, NULL_CHARACTER);
        idx++;
        len--;
        continue;
      }
      switch (t[len]) {
        case 'r':
          buf_wr(idx++, '\r');
          break;
        case 'n':
          buf_wr(idx++, '\n');
          break;
        case 'b':
          buf_wr(idx++, '\b');
          break;
        case 'a':
          buf_wr(idx++, '\a');
          break;
        case 'f':
          buf_wr(idx++, '\f');
          break;
        case 'c':
          buf_wr(idx++, SKIP_NEWLINE);
          break;
        default:
          buf_wr(idx++, t[len]);
          break;
      }
      sawesc = 0;
      continue;
    }
    if (t[len] == '\\') {
      sawesc = 1;
      continue;
    }
    if (t[len] == '"') {
      if (sawq == 1) {
        sawq = 0;
        len++;
        break;
      }
      sawq = 1;
      continue;
    }
    if (t[len] == '$' && t[len + 1] == '(') {
      for(f = len; t[f] && t[f] != ')'; f++)
        ;
      if (t[f] == ')') {
        strncpy(envbuf, &t[len + 2], f - len - 2);
        envbuf[f - len - 2] = 0;
        len = f;
        env = mygetenv(envbuf);
        if (env == NULL)
          env = "";
        while (*env)
          buf_wr(idx++, *env++);
        continue;
      }
    }
    /* ^ prefix for control chars - jl 3.2002 */
    if (sawq == 1 && t[len] == '^' && t[len + 1] != 0) {
      char c = toupper(t[len + 1]);
      if (c >= 'A' && c <= '_') {
        len++;
        buf_wr(idx++, c - 'A' + 1);
        continue;
      }
    }
    if ((!sawq && (t[len] == ' ' || t[len] == '\t')) || t[len] == 0)
      break;
    buf_wr(idx++, t[len]);
  }
  buf_wr(idx, 0);
  *s += len;
  skipspace(s);
  if (sawesc || sawq)
    syntaxerr(_("(word contains ESC or quote)"));
  return buf();
}

/*
 * Save a string to memory. Strip trailing '\n'.
 */
char *strsave(char *s)
{
  char *t;
  int len;

  len = strlen(s);
  if (len && s[len - 1] == '\n')
    s[--len] = 0;
  if (!(t = malloc(len + 1)))
    return t;
  strcpy(t, s);
  return t;
}

/*
 * Throw away all malloced memory.
 */
void freemem(void)
{
  struct line *l, *nextl;
  struct var *v, *nextv;

  for (l = curenv->lines; l; l = nextl) {
    nextl = l->next;
    free(l->line);
    free(l);
  }
  for (v = curenv->vars; v; v = nextv) {
    nextv = v->next;
    free(v->name);
    free(v);
  }
}

/*
 * Read a script into memory.
 */
static int readscript(const char *s)
{
  FILE *fp;
  struct line *tl, *prev = NULL;
  char *t;
  char buf[500]; /* max length of a line - this should be dynamically! */
  int lineno = 0;

  if ((fp = fopen(s, "r")) == NULL) {
    fprintf(stderr, _("runscript: couldn't open \"%s\"%s\n"), s, "\r");
    exit(1);
  }

  /* Read all the lines into a linked list in memory. */
  while ((t = fgets(buf, sizeof(buf), fp)) != NULL) {
    lineno++;
    if (strlen(t) == sizeof(buf) - 1) {
      /* Wow, this is really braindead, once upon a time buf was 81 chars
       * big and triggered nice errors for too long input lines, now
       * we just enlarge the buffer and add a sanity check. This code
       * needs to allocate memory dynamically... */
      fprintf(stderr, _("Input line %u too long, aborting (and fix me!)!\n"),
	      lineno);
      exit(1);
    }
    skipspace(&t);
    if (*t == '\n' || *t == '#')
      continue;
    if (((tl = (struct line *)malloc(sizeof (struct line))) == NULL) ||
        ((tl->line = strsave(t)) == NULL)) {
      fprintf(stderr, _("script \"%s\": out of memory%s\n"), s, "\r");
      exit(1);
    }
    if (prev)
      prev->next = tl;
    else
      curenv->lines = tl;
    tl->next = NULL;
    tl->labelcount = 0;
    tl->lineno = lineno;
    prev = tl;
  }
  fclose(fp);
  return 0;
}

/* Read one character, and store it in the buffer. */
void readchar(void)
{
  char c;
  int n;

  while ((n = read(0, &c, 1)) != 1)
    if (errno != EINTR)
      break;

  if (n <= 0)
    return;

  /* Shift character into the buffer. */
#ifdef _SYSV
  memcpy(inbuf, inbuf + 1, 63);
#else
#  ifdef _BSD43
  bcopy(inbuf + 1, inbuf, 63);
#  else
  /* This is Posix, I believe. */
  memmove(inbuf, inbuf + 1, 63);
#  endif
#endif
  if (curenv->verbose)
    fputc(c, stderr);
  inbuf[63] = c;
}

/* See if a string just came in. */
int expfound(const char *word)
{
  int len;

  if (word == NULL) {
    fprintf(stderr, _("NULL paramenter to %s!"), __func__);
    exit(1);
  }

  len = strlen(word);
  if (len > 64)
    len = 64;

  return !strcmp(inbuf + 64 - len, word);
}

/*
 * Send text to a file (stdout or stderr).
 */
int output(char *text, FILE *fp)
{
  unsigned char *w;
  int first = 1;
  int donl = 1;

  while ((w = (unsigned char *)getword(&text)) != NULL) {
    if (!first)
      fputc(' ', fp);
    first = 0;
    for(; *w; w++) {
      if (*w == SKIP_NEWLINE) {
        donl = 0;
        continue;
      }
      if (*w == '\n')
        fputs(newline, fp);
      else if (*w == NULL_CHARACTER)
        fputc('\0', fp);
      else
        fputc(*w, fp);
    }
  }
  if (donl)
    fputs(newline, fp);
  fflush(fp);
  return OK;
}

/*
 * Find a variable in the list.
 * If it is not there, create it.
 */
struct var *getvar(char *name, int cr)
{
  struct var *v, *end = NULL;

  for (v = curenv->vars; v; v = v->next) {
    end = v;
    if (!strcmp(v->name, name))
      return v;
  }
  if (!cr) {
    fprintf(stderr, _("script \"%s\" line %d: unknown variable \"%s\"%s\n"),
            curenv->scriptname, thisline->lineno, name, "\r");
    exit(1);
  }
  if ((v = (struct var *)malloc(sizeof(struct var))) == NULL ||
      (v->name = strsave(name)) == NULL) {
    fprintf(stderr, _("script \"%s\": out of memory%s\n"),
            curenv->scriptname, "\r");
    exit(1);
  }
  if (end)
    end->next = v;
  else
    curenv->vars = v;
  v->next = NULL;
  v->value = 0;
  return v;
}

/*
 * Read a number or variable.
 */
int getnum(char *text)
{
  int val;

  if (!strcmp(text, "$?"))
    return laststatus;
  if ((val = atoi(text)) != 0 || *text == '0')
    return val;
  return getvar(text, 0)->value;
}


/*
 * Get the lines following "expect" into memory.
 */
struct line **buildexpect(void)
{
  static struct line *seq[17];
  int f;
  char *w, *t;

  for(f = 0; f < 16; f++) {
    if (thisline == NULL) {
      fprintf(stderr, _("script \"%s\": unexpected end of file%s\n"),
              curenv->scriptname, "\r");
      exit(1);
    }
    t = thisline->line;
    w = getword(&t);
    if (!strcmp(w, "}")) {
      if (*t)
        syntaxerr(_("(garbage after })"));
      seq[f] = NULL;
      return seq;
    }
    seq[f] = thisline;
    thisline = thisline->next;
  }
  if (f == 16)
    syntaxerr(_("(too many arguments)"));
  return seq;
}

/*
 * Our "expect" function.
 */
int expect(char *text)
{
  char *s, *w;
  struct line **volatile seq;
  struct line oneline;
  struct line *dflseq[2];
  char *volatile toact = "exit 1";
  volatile int found = 0;
  int f, val, c;
  char *action = NULL;

  if (inexpect) {
    fprintf(stderr, _("script \"%s\" line %d: nested expect%s\n"),
            curenv->scriptname, thisline->lineno, "\r");
    exit(1);
  }
  etimeout = 120;
  inexpect = 1;

  s = getword(&text);
  if (!strcmp(s, "{")) {
    if (*text)
      syntaxerr(_("(garbage after {)"));
    thisline = thisline->next;
    seq = buildexpect();
  } else {
    oneline.line = s;
    oneline.next = NULL;
    dflseq[0] = &oneline;
    dflseq[1] = NULL;
    seq = dflseq;
  }
  /* Seek a timeout command */
  for (f = 0; seq[f]; f++) {
    if (!strncmp(seq[f]->line, "timeout", 7)) {
      c = seq[f]->line[7];
      if (c == 0 || (c != ' ' && c != '\t'))
        continue;
      s = seq[f]->line + 7;
      /* seq[f] = NULL; */
      skipspace(&s);
      w = getword(&s);
      if (w == NULL)
        syntaxerr(_("(argument expected)"));
      val = getnum(w);
      if (val == 0)
        syntaxerr(_("(invalid argument)"));
      etimeout = val;
      skipspace(&s);
      if (*s != 0)
        toact = s;
      break;
    }
  }
  if (sigsetjmp(ejmp, 1) != 0) {
    f = s_exec(toact);
    inexpect = 0;
    return f;
  }

  /* Alright. Now do the expect. */
  c = OK;
  while (!found) {
    action = NULL;
    readchar();
    for (f = 0; seq[f]; f++) {
      s = seq[f]->line;
      w = getword(&s);
      if (expfound(w)) {
        action = s;
        found = 1;
        break;
      }
    }
    if (action != NULL && *action) {
      found = 0;
      /* Maybe BREAK or RETURN */
      if ((c = s_exec(action)) != OK)
        found = 1;
    }
  }
  inexpect = 0;
  etimeout = 0;
  return c;
}

/*
 * Jump to a shell and run a command.
 */
int shell(char *text)
{
  int status = system(text);
  if (WIFEXITED(status))
    laststatus = WEXITSTATUS(status);
  else if (WIFSIGNALED(status))
    laststatus = WTERMSIG(status);
  else
    laststatus = status;
  return OK;
}

/*
 * Run a command and send its stdout to stdout ( = modem).
 */
int pipedshell(char *text)
{
  FILE *fp = popen(text, "r");
  if (fp == NULL) {
    laststatus = errno;
    return OK;
  }

  char received[64];
  size_t read = 0;
  while ((read = fread(received, sizeof(char), 64, fp))) {
#ifdef HAVE_USLEEP
    /* 200 ms delay. */
    usleep(200000);
#endif

    char *sent = received;
    while (read-- > 0)
      fputc(*sent++, stdout);

    fflush(stdout);
  }

  int status = pclose(fp);
  if (WIFEXITED(status))
    laststatus = WEXITSTATUS(status);
  else if (WIFSIGNALED(status))
    laststatus = WTERMSIG(status);
  else
    laststatus = status;
  return OK;
}

/*
 * Send output to stdout ( = modem)
 */
int dosend(char *text)
{
#ifdef HAVE_USLEEP
  /* 200 ms delay. */
  usleep(200000);
#endif

  /* Before we send anything, flush input buffer. */
  m_flush(0);
  memset(inbuf, 0, sizeof(inbuf));

  newline = "\r";
  return output(text, stdout);
}

/*
 * Exit from the script, possibly with a value.
 */
int doexit(char *text)
{
  char *w;
  int ret = 0;

  w = getword(&text);
  if (w != NULL)
    ret = getnum(w);
  curenv->exstat = ret;
  longjmp(curenv->ebuf, 1);
  return 0;
}

/*
 * Goto a specific label.
 */
int dogoto(char *text)
{
  char *w;
  struct line *l;
  char buf[32];
  int len;

  w = getword(&text);
  if (w == NULL || *text)
    syntaxerr(_("(in goto/gosub label)"));
  snprintf(buf, sizeof(buf), "%s:", w);
  len = strlen(buf);
  for (l = curenv->lines; l; l = l->next)
    if (!strncmp(l->line, buf, len))
      break;
  if (l == NULL) {
    fprintf(stderr, _("script \"%s\" line %d: label \"%s\" not found%s\n"),
            curenv->scriptname, thisline->lineno, w, "\r");
    exit(1);
  }
  thisline = l;
  /* We return break, to automatically break out of expect loops. */
  return BREAK;
}

/*
 * Goto a subroutine.
 */
int dogosub(char *text)
{
  struct line *oldline;
  int ret = OK;

  oldline = thisline;
  dogoto(text);

  while (ret != ERR) {
    if ((thisline = thisline->next) == NULL) {
      fprintf(stderr, _("script \"%s\": no return from gosub%s\n"),
              curenv->scriptname, "\r");
      exit(1);
    }
    ret = s_exec(thisline->line);
    if (ret == RETURN) {
      ret = OK;
      thisline = oldline;
      break;
    }
  }
  return ret;
}

/*
 * Return from a subroutine.
 */
int doreturn(char *text)
{
  (void)text;
  return RETURN;
}

/*
 * Print text to stderr.
 */
int print(char *text)
{
  newline = "\r\n";

  return output(text, stderr);
}

/*
 * Declare a variable (integer)
 */
int doset(char *text)
{
  char *w;
  struct var *v;

  w = getword(&text);
  if (w == NULL)
    syntaxerr(_("(missing var name)"));
  v = getvar(w, 1);
  if (*text)
    v->value = getnum(getword(&text));
  return OK;
}

/*
 * Lower the value of a variable.
 */
int dodec(char *text)
{
  char *w;
  struct var *v;

  w = getword(&text);
  if (w == NULL)
    syntaxerr(_("(expected variable)"));
  v = getvar(w, 0);
  v->value--;
  return OK;
}

/*
 * Increase the value of a variable.
 */
int doinc(char *text)
{
  char *w;
  struct var *v;

  w = getword(&text);
  if (w == NULL)
    syntaxerr(_("(expected variable)"));
  v = getvar(w, 0);
  v->value++;
  return OK;
}

/*
 * If syntax: if n1 [><=] n2 command.
 */
int doif(char *text)
{
  char *w;
  int n1;
  int n2;
  char op;

  if ((w = getword(&text)) == NULL)
    syntaxerr("(if)");
  n1 = getnum(w);
  if ((w = getword(&text)) == NULL)
    syntaxerr("(if)");
  if (strcmp(w, "!=") == 0)
    op = '!';
  else {
    if (*w == 0 || w[1] != 0)
      syntaxerr("(if)");
    op = *w;
  }
  if ((w = getword(&text)) == NULL)
    syntaxerr("(if)");
  n2 = getnum(w);
  if (!*text)
    syntaxerr(_("(expected command after if)"));

  if (op == '=') {
    if (n1 != n2)
      return OK;
  } else if (op == '!') {
    if (n1 == n2)
      return OK;
  } else if (op == '>') {
    if (n1 <= n2)
      return OK;
  } else if (op == '<') {
    if (n1 >= n2)
      return OK;
  } else
    syntaxerr(_("(unknown operator)"));

  return s_exec(text);
}

/*
 * Set the global timeout-time.
 */
int dotimeout(char *text)
{
  char *w;
  int val;

  w = getword(&text);
  if (w == NULL)
    syntaxerr(_("(argument expected)"));
  if ((val = getnum(w)) == 0)
    syntaxerr(_("(invalid argument)"));
  gtimeout = val;
  return OK;
}

/*
 * Turn verbose on/off (= echo stdin to stderr)
 */
int doverbose(char *text)
{
  char *w;

  curenv->verbose = 1;

  if ((w = getword(&text)) != NULL) {
    if (!strcmp(w, "on"))
      return OK;
    if (!strcmp(w, "off")) {
      curenv->verbose = 0;
      return OK;
    }
  }
  syntaxerr(_("(unexpected argument)"));
  return ERR;
}

/*
 * Sleep for a certain number of seconds.
 */
int dosleep(char *text)
{
  int foo, tm;

  tm = getnum(text);
  foo = gtimeout - tm;

  /* The alarm goes off every second.. */
  while (gtimeout != foo)
    pause();
  return OK;
}

/*
 * Break out of an expect loop.
 */
int dobreak(char *dummy)
{
  (void)dummy;
  if (!inexpect) {
    fprintf(stderr, _("script \"%s\" line %d: break outside of expect%s\n"),
            curenv->scriptname, thisline->lineno, "\r");
    exit(1);
  }
  return BREAK;
}

/*
 * Call another script!
 */
int docall(char *text)
{
  struct line *oldline;
  struct env *oldenv;
  int er;

  if (*text == 0)
    syntaxerr(_("(argument expected)"));

  if (inexpect) {
    fprintf(stderr, _("script \"%s\" line %d: call inside expect%s\n"),
            curenv->scriptname, thisline->lineno, "\r");
    exit(1);
  }

  oldline = thisline;
  oldenv = curenv;
  if ((er = execscript(text)) != 0)
    exit(er);
  /* freemem(); */
  thisline = oldline;
  curenv = oldenv;
  return 0;
}

static int do_log_wrapper(char *s)
{
  do_log("%s", s);
  return 0;
}

/* KEYWORDS */
struct kw {
  const char *command;
  int (*fn)(char *);
} keywords[] = {
  { "expect",	expect },
  { "send",	dosend },
  { "!<",	pipedshell },
  { "!",	shell },
  { "goto",	dogoto },
  { "gosub",	dogosub },
  { "return",	doreturn },
  { "exit",	doexit },
  { "print",	print },
  { "set",	doset },
  { "inc",	doinc },
  { "dec",	dodec },
  { "if",	doif },
  { "timeout",	dotimeout },
  { "verbose",	doverbose },
  { "sleep",	dosleep },
  { "break",	dobreak },
  { "call",	docall },
  { "log",	do_log_wrapper },
  { NULL,	(int(*)(char *))0 }
};

/*
 * Execute one line.
 */
int s_exec(char *text)
{
  char *w;
  struct kw *k;

  w = getword(&text);

  /* If it is a label or a comment, skip it. */
  if (w == NULL || *w == '#' || w[strlen(w) - 1] == ':')
    return OK;

  /* See which command it is. */
  for (k = keywords; k->command; k++)
    if (!strcmp(w, k->command))
      break;

  /* Command not found? */
  if (k->command == NULL) {
    fprintf(stderr, _("script \"%s\" line %d: unknown command \"%s\"%s\n"),
            curenv->scriptname, thisline->lineno, w, "\r");
    exit(1);
  }
  return (*(k->fn))(text);
}

/*
 * Run the script by continuously executing "thisline".
 */
int execscript(const char *s)
{
  volatile int ret = OK;

  curenv = (struct env *)malloc(sizeof(struct env));
  curenv->lines = NULL;
  curenv->vars  = NULL;
  curenv->verbose = 1;
  curenv->scriptname = s;

  if (readscript(s) < 0) {
    freemem();
    free(curenv);
    return ERR;
  }

  signal(SIGALRM, myclock);
  alarm(1);
  if (setjmp(curenv->ebuf) == 0) {
    thisline = curenv->lines;
    while (thisline != NULL && (ret = s_exec(thisline->line)) != ERR)
      thisline = thisline->next;
  } else
    ret = curenv->exstat ? ERR : 0;
  free(curenv);
  return ret;
}

void do_args(int argc, char **argv)
{
  if (argc > 1 && !strcmp(argv[1], "--version")) {
    printf(_("runscript, part of minicom version %s\n"), VERSION);
    exit(0);
  }

  if (argc < 2) {
    fprintf(stderr, _("Usage: runscript <scriptfile> [logfile [homedir]]%s\n"),"\r");
    exit(1);
  }
}

int main(int argc, char **argv)
{
  char *s;
#if 0 /* Shouldn't need this.. */
  signal(SIGHUP, SIG_IGN);
#endif
  /* On some Linux systems SIGALRM is masked by default. Unmask it */
  sigset_t ss;
  sigemptyset(&ss);
  sigaddset(&ss, SIGALRM);
  sigprocmask(SIG_UNBLOCK, &ss, NULL);

  /* initialize locale support */
  setlocale(LC_ALL, "");
  bindtextdomain(PACKAGE, LOCALEDIR);
  textdomain(PACKAGE);

  init_env();

  do_args(argc, argv);

  memset(inbuf, 0, sizeof(inbuf));

  if (argc > 2) {
    strncpy(logfname, argv[2], sizeof(logfname));
    logfname[sizeof(logfname) - 1] = '\0';
    if (argc > 3)
      strncpy(homedir, argv[3], sizeof(homedir));
    else if ((s = getenv("HOME")) != NULL)
      strncpy(homedir, s, sizeof(homedir));
    else
      homedir[0] = 0;
    homedir[sizeof(homedir) - 1] = '\0';
  }
  else
    logfname[0] = 0;

  return execscript(argv[1]) != OK;
}
