/*
 * dip		A program for handling dialup IP connecions.
 *		Script language processor.
 *
 * Version:	@(#)command.c	3.3.5	12/13/93
 *
 * Author:      Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
 *		Copyright 1988-1993 MicroWalt Corporation
 *
 *		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.
 */
#include "dip.h"


struct commands {
  char	*name;
  int	(*func)(int, char **);
};


static int	timeout;		/* script "wait" timed out	*/
static int	errlevel;		/* script command return code	*/
static FILE	*scriptfp = (FILE *)NULL;	/* input script pointer	*/
static char	*var_modem = NULL;	/* name of modem we are using	*/
static char	*var_port = NULL;	/* terminal line used		*/
static char	*var_speed = NULL;	/* terminal line speed		*/
static int	var_echo = 0;		/* enable line data echoing	*/


static void
TimeOut(int sig)
{
  (void) sig;
  timeout = 1;
}


/* Convert a C-style backslash sequence to ASCII. */
static char
cvt_char(char c)
{
  if (c == '\0') return(c);
  switch(c) {
	case 'a':
		return('\a');

	case 'b':

		return('\b');

	case 'f':
		return('\f');

	case 'n':
		return('\n');

	case 'r':
		return('\r');

	case 's':
		return(' ');

	case 't':
		return('\t');

	case 'v':
		return('\v');

	case '\\':
		return('\\');

	case '\'':
		return('\'');

	case '"':
		return('\"');

	case '?':
		return('\?');

	default:
		return('?');
  }
  /* NOTREACHED */
  return('?');
}


/* Split the input string into multiple fields. */
static int
getargs(char *string, char *arguments[])
{
  char *sp;
  int argc;
  int i;

  sp = string; i = 0;
  arguments[i] = sp;
  while (*sp && i < 32) {
        while (*sp && (*sp == ' ' || *sp == '\t')) sp++;
        arguments[i++] = sp;
        while (*sp && *sp != ' ' && *sp != '\t') sp++;
        if (*sp != '\0') *sp++ = '\0';
  }
  argc = i;
  while (i < 32) arguments[i++] = (char *)NULL;
  return(argc);
}


/************************************************************************
 *									*
 *		Internal Scripting Commands				*
 *									*
 ************************************************************************/

/* Enable/Disable echoing of data from terminal line. */
static int
do_echo(int argc, char *argv[])
{
  if (argc != 2) {
	fprintf(stderr, "Usage: echo on|off\n");
	return(-1);
  }
  
  if (strcmp(argv[1], "on") == 0) var_echo = 1;
    else if (strcmp(argv[1], "off") == 0) var_echo = 0;
    else {
	fprintf(stderr, "Usage: echo on|off\n");
	return(-1);
  }
	
  if (opt_v == 1) printf("Display modem output: %sabled\n",
				(var_echo == 1) ? "dis" : "en");
  return(0);
}
 

/* Go to some label in the script. */
static int
do_goto(int argc, char *argv[])
{
  char buff[1024];
  off_t oldpos;
  char *label, *xp;
  char *sp;

  if (argc != 2) {
	fprintf(stderr, "Usage: goto label\n");
	return(-1);
  }
  if (scriptfp == stdin) {
	fprintf(stderr, "dip: GOTO not possible in TEST mode!\n");
	return(-1);
  }

  label = argv[1];
  oldpos = ftell(scriptfp);
  rewind(scriptfp);
  (void) fflush(scriptfp);

  do {
        if (fgets(buff, 1024, scriptfp) == (char *)NULL) break;
	if ((sp = strchr(buff, '\n')) != (char *)NULL) *sp = '\0';
	sp = buff;
	while (*sp == ' ' || *sp == '\t') sp++;
	if (*sp == '#' || *sp == '\0') continue;
	if ((xp = strchr(sp, ':')) == (char *)NULL) continue;
	*xp = '\0';
	if (! strcmp(label, sp)) {
		oldpos = ftell(scriptfp);
		(void) fseek(scriptfp, oldpos, SEEK_SET);
		return(0);
	}
  } while(1);
  (void) fseek(scriptfp, oldpos, SEEK_SET);
  (void) fflush(scriptfp);
  return(-1);
}


/* Check some error (result) code. */
static int
do_if(int argc, char *argv[])
{
  char *cmd[3];
  char opcode;
  long val, var;
  int ret;

  if (argc != 6) {
	fprintf(stderr, "Usage: if expr goto label\n");
	return(-1);
  };
  if (! strcmp(argv[2], "==")) opcode = '=';
    else if (! strcmp(argv[2], "!=")) opcode = '@';
    else if (! strcmp(argv[2], "<")) opcode = '<';
    else if (! strcmp(argv[2], ">")) opcode = '>';
    else if (! strcmp(argv[2], "<=")) opcode = 'L';
    else if (! strcmp(argv[2], ">=")) opcode = 'G';
    else {
	fprintf(stderr, "Syntax error: \"%s\" is not an opcode!\n", argv[2]);
	return(-1);
  }
  val = (long) atol(argv[3]);

  if (! strcmp(argv[1], "$errlvl")) var = (long) errlevel;
    else if (! strcmp(argv[1], "$locip")) var = (long) mydip.loc_ip.s_addr;
    else if (! strcmp(argv[1], "$rmtip")) var = (long) mydip.rmt_ip.s_addr;
    else {
	fprintf(stderr, "Invalid variable \"%s\" !\n", argv[1]);
	return(-1);
  }

  ret = -1;
  switch(opcode) {
	case '=':	/* EQUAL */
		ret = (var == val);
		break;

	case '@':	/* NOT EQUAL */
		ret = (var != val);
		break;

	case '<':	/* LESS */
		ret = (var < val);
		break;

	case 'L':	/* LESS-EQ */
		ret = (var <= val);
		break;

	case '>':	/* GREATER */
		ret = (var > val);
		break;

	case 'G':	/* GREATER-EQ */
		ret = (var >= val);
		break;
  }

  if (strcmp(argv[4], "goto")) {
	fprintf(stderr, "Warning: keyword not \"goto\" !\n");
	argv[4] = "goto";
  }

  if (ret != 0) {
	cmd[1] = argv[4];
	cmd[1] = argv[5];
	cmd[2] = (char *)NULL;
	return(do_goto(2, cmd));
  }
  return(-1);
}


/* Print the contents of some variable. */
static int
do_print(int argc, char *argv[])
{
  char *sp;
  int i;

  if (argc == 1) {
	printf("\n");
	return(0);
  }
  i = 0;
  while (argv[++i] != (char *)NULL) {
	sp = argv[i];
	if (i != 1) printf(" ");
	if (*sp == '$') {
		if (! strcmp(++sp, "errlvl")) printf("%d", errlevel);
		  else if (! strcmp(sp, "locip")) printf("%s",
					inet_ntoa(mydip.loc_ip));
		  else if (! strcmp(sp, "rmtip")) printf("%s",
					inet_ntoa(mydip.rmt_ip));
		  else if (! strcmp(sp, "local")) printf("%s", mydip.local);
		  else if (! strcmp(sp, "remote")) printf("%s", mydip.remote);
		  else if (! strcmp(sp, "modem")) printf("%s", var_modem);
		  else if (! strcmp(sp, "port")) printf("%s", var_port);
		  else if (! strcmp(sp, "speed")) printf("%s", var_speed);
		  else fprintf(stderr, "Unknown variable %s\n", sp);
	} else printf("%s", sp);
  }
  printf("\n");
  return(0);
}


/* Send a string to the serial driver. */
static int
do_send(int argc, char *argv[])
{
  char *sp;
  char c;
  int i;
  
  if (argc < 2) {
	fprintf(stderr, "Usage: send text...\n");
	return(-1);
  }

  for (i = 1; i < argc; i++) {
  	sp = argv[i];
  	while(*sp != '\0') {
		switch(*sp) {
			case '~':
				tty_putc('\r');
				tty_putc('\n');
				break;

			case '\\':
				sp++;
				c = cvt_char(*sp);
				tty_putc((int) c);
				break;

			default:
				c = *sp;
				tty_putc((int) c);
		}
		sp++;
  	}
        if (i < (argc - 1)) tty_putc(' ');
  }
  tty_putc(-1);		/* flush the connection */
  return(0);
}


/* Wait some time. */
static int
do_sleep(int argc, char *argv[])
{
  int secs;

  if (argc != 2) {
	fprintf(stderr, "Usage: sleep time_in_secs\n");
	return(-1);
  }

  secs = atoi(argv[1]);
  (void) sleep(secs);
  return(0);
}


/* Flush tty input */
static int
do_flush(int argc, char *argv[])
{
  if (argc != 1) {
	fprintf(stderr, "Usage: flush\n");
	return(-1);
  }

  tty_flush();
  return(0);
}


/* Wait for some string to arrive. */
static int
do_wait(int argc, char *argv[])
{
  char c, c2, *p;
  int howlong;
  void (*oldsig)(int);

  if (argc == 1 || argc > 3) {
	fprintf(stderr, "Usage: wait text [timeout_value]\n");
	return(-1);
  }

  if (argc == 3) howlong = atoi(argv[2]);
    else howlong = 0;
  oldsig = signal(SIGALRM, TimeOut);
  (void) alarm(howlong);

  p = argv[1];
  timeout = 0;
  while(!timeout && *p != '\0') {
	c = (char) tty_getc();
	c &= 0177;
	if (var_echo == 1) {
		fputc(c, stdout);
		fflush(stdout);
	}
	if (timeout == 1) break;
	if (*p == '\\') c2 = cvt_char(*++p);
	  else c2 = *p;
	if (c2 != c) p = argv[1];
	  else p++;
  }
  (void) alarm(0);
  (void) signal(SIGALRM, oldsig);
  return((timeout == 1) ? -1 : 0);
}


/* Show some help. */
static int
do_help(int argc, char *argv[])
{
  extern struct commands commands[];
  int i, j;

  i = 0; j = 0;
  printf("DIP knows about the following commands:\n\n");
  while (commands[i].name != (char *)NULL) {
	if (j++ == 0) printf("\t");
	printf("%-8.8s ", commands[i].name);
	if (j == 5) {
		printf("\n");
		j = 0;
	}
	i++;
  }
  if (j != 0) printf("\n\n");
    else printf("\n");
  return(0);
}


/************************************************************************
 *									*
 *		Modem Handling and Dialing Commands			*
 *									*
 ************************************************************************/


/* Set the name of the terminal port to use. */
static int
do_port(int argc, char *argv[])
{
  if (argc != 2) {
	fprintf(stderr, "Usage: port tty_name\n");
	return(-1);
  }
  if (var_port != NULL) {
	fprintf(stderr, "PORT: terminal port already set to \"%s\".\n",
								var_port);
	return(-1);
  }
  var_port = argv[1];
  if (opt_v == 1) printf("PORT: terminal port set to \"%s\".\n", var_port);

  /* Initialize the terminal line. */
  if (tty_open(var_port) < 0) {
	var_port = NULL;
	return(-1);
  }

  return(0);
}


/* Set the correct DATABITS to use. */
static int
do_databits(int argc, char *argv[])
{
  if (argc != 2) {
        fprintf(stderr, "Usage: databits bits\n");
        return(-1);
  }
  if (var_port == NULL) { 
        fprintf(stderr, "Please set PORT first.\n");
        return(-1);
  }  
  return(tty_databits(argv[1]));
}


/* Define a modem "INIT" string. */
static int
do_init(int argc, char *argv[])
{
  if (argc != 2) {
	fprintf(stderr, "Usage: init <init string>\n");
	return(-1);
  }
  return(mdm_init(argv[1]));
}


static int
do_dial(int argc, char *argv[])
{
  if (argc != 2) {
	fprintf(stderr, "Usage: dial telno\n");
	return(-1);
  }

  if (var_modem == NULL) {
	fprintf(stderr, "Please set MODEM first.\n");
	return(-1);
  }
  return(mdm_dial(argv[1]));
}


/* Set the name of the modem we want to use. */
static int
do_modem(int argc, char *argv[])
{
  if (argc != 2) {
	fprintf(stderr, "Usage: modem modem_name\n");
	return(-1);
  }
  if (var_modem != NULL) {
	fprintf(stderr, "MODEM: modem already set to \"%s\".\n", var_modem);
	return(-1);
  }
  var_modem = argv[1];
  if (opt_v == 1) printf("MODEM: modem set to \"%s\".\n", var_modem);

  /* Initialize this modem. */
  if (mdm_modem(var_modem) < 0) {
	var_modem = NULL;
	return(-1);
  }

  return(0);
}


static int
do_parity(int argc, char *argv[])
{
  if (argc != 2) {
        fprintf(stderr, "Usage: parity E/O/N\n");
        return(-1);
  }

  if (var_port == NULL) {
        fprintf(stderr, "Please set PORT first.\n");
        return(-1);
  }
  return(tty_parity(argv[1]));
}


/* Reset the modem. */
static int
do_reset(int argc, char *argv[])
{
  if (argc != 1) {
	fprintf(stderr, "Usage: reset\n");
	return(-1);
  }

  /* Did we get a modem to work with? */
  if (var_modem == NULL) {
	fprintf(stderr, "Please set MODEM first.\n");
	return(-1);
  }

  /* Reset the modem. */
  (void) mdm_reset();
  return(0);
}


/* Set the correct SPEED to use. */
static int
do_speed(int argc, char *argv[])
{
  if (argc != 2) {
	fprintf(stderr, "Usage: speed baudrate\n");
	return(-1);
  }
  if (var_port == NULL) {
	fprintf(stderr, "Please set PORT first.\n");
	return(-1);
  }
  return(tty_speed(argv[1]));
}


/* Set the correct STOPBITS to use. */
static int
do_stopbits(int argc, char *argv[])
{
  if (argc != 2) {
        fprintf(stderr, "Usage: stopbits bits\n");
        return(-1);
  }
  if (var_port == NULL) {
        fprintf(stderr, "Please set PORT first.\n");
        return(-1);
  }
  return(tty_stopbits(argv[1]));
} 


/* Enter a TERMINAL mode. */
static int
do_term(int argc, char *argv[])
{
  if (argc != 1) {
	fprintf(stderr, "Usage: term\n");
	return(-1);
  }
  if (var_port == NULL) {
	fprintf(stderr, "Please set PORT first.\n");
	return(-1);
  }
  do_terminal();
  return(0);
}


/************************************************************************
 *									*
 *		Connection Setup Commands				*
 *									*
 ************************************************************************/

/*
 * Get or ask for the value of a variable.
 * Get local IP#, Remote IP# and MTU
 */

#define VAR_IP		1
#define VAR_NUM		2
#define VAR_STR		3

static int
do_get(int argc, char *argv[])
{
  char str[128] = "";
  int var = 0;
  void *v1 = NULL, *v2 = NULL;
  void (*oldsig)(int);
   
  if (argc <= 2 || argc > 4 || argv[1][0] != '$') {
 	fprintf(stderr,
		"Usage: get $variable [value|ask|remote [timeout_value]]\n");
 	return(-1);
  }
   
  if (! strcmp(argv[1], "$locip") || !strcmp(argv[1], "$local")) {
	v1 = (void *) mydip.local;
	v2 = (void *) &mydip.loc_ip;
	var = VAR_IP;
  } else if (! strcmp(argv[1], "$rmtip") || !strcmp(argv[1], "$remote")) {
	v1 = (void *) mydip.remote;
	v2 = (void *) &mydip.rmt_ip;
	var = VAR_IP;
  } else if (! strcmp(argv[1], "$mtu")) {
	v1 = (void *) &mydip.mtu;
	var = VAR_NUM;
  } else {
     fprintf(stderr, "Variables must be :$locip|$rmtip|$local|$remote|$mtu\n");
     return (-1);
  }
 
  if (argc > 2) {
	if (! strcmp(argv[2], "ask")) {
		/* Ask the stdinput for the value of the variable. */
		register char *sp;

		fprintf(stderr, "Enter the value for %s: ", argv[1]);
		if (fgets(str, 128, stdin) == (char *)NULL) return(1);
		if ((sp = strchr(str, '\n')) != (char *)NULL) *sp = '\0';
	} else if (! strcmp(argv[2],"remote")) {
		/* Get the variable string from the "remote" line */
		register char c, *p;
		int howlong = 0, state = 0;

		if (argc == 4) howlong = atoi(argv[3]);
		oldsig = signal(SIGALRM, TimeOut);
		(void) alarm(howlong);

		p = str;
		timeout = 0;
		state = 0;
		if (var == VAR_IP || var == VAR_NUM) state = 1;
		while(!timeout && (state >= 0)) {
			c = (char) tty_getc();
			c &= 0177;
			if (timeout == 1) break;
			switch (state) {
				case 0: /* throw away "white space" */
					if (! strchr(" \n\t", c)) break;
					if (var == VAR_STR) {
						*(p++) = c;
						state = 10;
						break;
					}
					state = 1;

				case 1:
					if ( c >= '0' && c <= '9' ) {
						*(p++) = c;
						if (var == VAR_IP) {
							state = 2;
							break;
						} else state = 5;
						break;
					}
					break; 

				case 2:
				case 3:
				case 4:
					if ((c >= '0' && c <= '9') || c == '.') {
						*(p++) = c;
						if (c == '.') state++;
						break;
					} 
					p = str;
					state = 0;
					break;
					case 5:
					if (c >= '0' && c <= '9') {
						*(p++) = c;
						break;
					} 
					state = -1;
					break;

				case 10:
					if (strchr(" \n\t", c)) state = -1;
					else *(p++) = c;
					break;

				default:
					break;
			}
		}
		*p = '\0';
		(void) alarm(0);
		(void) signal(SIGALRM, oldsig);
		if (timeout == 1) return (1);
	} else {
		/* The third argv is the value for the variable. */
		strncpy(str, argv[2], 128);
	}
  }

  if (opt_v == 1) printf("About to set variable %s to %s\n", argv[1], str);

  if (*str) {
	switch (var) {
		case VAR_IP:
			{
				struct hostent *hp;
				hp = gethostbyname(str);
				if (hp == (struct hostent *)NULL) {
					herror(str);
					return(-1);
				}
				strncpy((char *) v1, hp->h_name, 128);
				memcpy((char *) v2, (char *) hp->h_addr_list[0],
				       hp->h_length);
			}
			break;

		case VAR_NUM:
			*(int *) v1 = atoi(str);
			break;

		case VAR_STR:
			strcpy((char *) v1, str);
			break;

		default:
			break;
	}
  }

  return(0);
}


/* Enter a specific protocol. */
static int
do_mode(int argc, char *argv[])
{
  register int i;

  if ((argc != 2) ||
      ((i = get_prot(argv[1])) == 0)) {
	fprintf(stderr, "Usage: mode protocol_name\n");
	return(-1);
  }

  if (mydip.rmt_ip.s_addr == INADDR_ANY) {
	fprintf(stderr, "Please set REMOTE first.\n");
	return(-1);
  }
  mydip.protonr = i;

  /* Enter BACKGROUND mode here! */
  (void) dip_daemon(&mydip);

  /*NOTREACHED*/
  return(i);
}

/* Quit upon error - reset the tty to the proper ldisc */
static int
do_quit(int argc, char *argv[])
{
  (void) tty_close();
  exit(1);
}

/* Store the netmask in "mydip" structure */
static int
do_netmask(int argc, char *argv[])
{
   if (argc != 2) {
      fprintf(stderr, 
              "Usage of this command: netmask xxx.xxx.xxx.xxx\n");
      return(-1);
   }
   strcpy(mydip.netmask, argv[1]);
   return(0);
}

/* Set routing as default */
static int
do_default(int argc, char *argv[])
{
  if (argc != 1) {
	fprintf(stderr, "Usage: default\n");
	return(-1);
  }
  if (opt_v == 1) printf("Destination net/address set to 'default'\n");

  mydip.rtdefault = 1;
  return(0);
}


struct commands commands[] = {
  { "databits",         do_databits     },
  { "default",		do_default	},
  { "dial",		do_dial		},
  { "echo",		do_echo		},
  { "flush",		do_flush	},
  { "get",		do_get   	},
  { "goto",		do_goto		},
  { "help",		do_help		},
  { "if",		do_if		},
  { "init",		do_init		},
  { "mode",		do_mode		},
  { "modem",		do_modem	},
  { "netmask",          do_netmask      },
  { "parity",           do_parity       },
  { "print",		do_print	},
  { "port",		do_port		},
  { "quit",             do_quit         },
  { "reset",		do_reset	},
  { "send",		do_send		},
  { "sleep",		do_sleep	},
  { "speed",		do_speed	},
  { "stopbits",         do_stopbits     },
  { "term",		do_term		},
  { "wait",		do_wait		},
  { (char *)NULL,	NULL		}
};


void do_command(fp)
FILE *fp;
{
  char cline[1024];
  char *argv[32];
  int argc, i;
  int running;
  register char *sp;

  /* Initialize the command level module. */
  var_modem = DEF_MODEM;
  var_speed = DEF_SPEED;
  scriptfp  = fp;
  running   = 1;
  timeout   = 0;
  errlevel  = 0;

  /* Tell the MODEM module about the current modem. */
  mdm_modem(var_modem);

  do {
	if (scriptfp == stdin) {
		if (opt_v == 1) printf("DIP [%-4d]> ", errlevel);
		  else printf("DIP> ");
		fflush(stdout);
	}
        if (fgets(cline, 1024, scriptfp) == (char *)NULL) break;
	if ((sp = strchr(cline, '\n')) != (char *)NULL) *sp = '\0';
	sp = cline;
	while (*sp == ' ' || *sp == '\t') sp++;
	if (*sp == '#' || *sp == '\0') continue;
	if (opt_v) fprintf(stderr, ">> %s\n", sp);
	if ((argc = getargs(sp, argv)) == 0) continue;

	/* If this is a label, skip it. */
	if (strchr(argv[0], ':') != (char *)NULL) continue;

	/* Now, check which command it is. */
	if (strcmp(argv[0], "exit") != 0) {
		i = 0;
		while (commands[i].name != (char *)NULL) {
			if (!strcmp(commands[i].name, argv[0])) break;
			i++;
		}
		if (commands[i].name != (char *)NULL) {
			errlevel = (*commands[i].func)(argc, argv);
		} else printf("? Unknown command (%s).\n", argv[0]);
	} else running = 0;
  } while(running);

  /*NOTREACHED*/
  exit(0);
}
