/*  G C M D L I N  --  gkermit command line parser  */

/*
  Author:
    Frank da Cruz
    Originally at:
      The Kermit Project
      Columbia University
      612 West 115th Street
      New York NY 10025-7799  USA
      http://www.columbia.edu/kermit/
      kermit@columbia.edu
    As of October 2011:
      The New Open-Source Kermit Project
      Bronx NY
      http://kermitproject.org
      kermit@kermitproject.org

  Copyright (C) 1999, 2021,
  The Trustees of Columbia University in the City of New York.
  Issued under the GNU General Public License as it existed in 1999.

  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.

  This program 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.
*/

#include <stdio.h>
#include "gkermit.h"

/* Externals */

extern int nfils, parity, text, backup, rpsiz, urpsiz, timint;
extern int literal, quiet, keep, streamok, nomodes, manual, xonxoff, noxonxoff;
extern char ttname[], *cmerrp, *cmarg, *cmarg2;
extern FILE * db;

/* Variables exported from this module */

extern char **cmlist;			/* Pointer to file list in argv */
extern char **xargv;			/* Global copies of argv */
extern int xargc;			/* and argc  */

/* Variables and symbols local to this module */

static int action = 0;			/* Action selected on command line */

_MYPROTOTYPE( static int doarg, (char) );
_MYPROTOTYPE( VOID fatal, (char *) );
_MYPROTOTYPE( VOID usage, (void) );

#ifndef NOGETENV
/* getenv prototype commented out in G-Kermit 2.0 */
/* _MYPROTOTYPE( char * getenv, (char *) ); */
#define GARGC 32
#define GBUFSIZ 256
static char gbuf[GBUFSIZ], *gargs[GARGC], *gptr = NULL;
static int gargc;
#endif /* NOGETENV */

int					/* Command-line parser */
cmdlin() {
    char c;
    int x;
#ifndef NOGETENV
    char * p = NULL;
#endif /* NOGETENV */

    cmarg = "";				/* Initialize results */
    cmlist = NULL;
    action = 0;

#ifndef NOGETENV
    if ((p = getenv("GKERMIT"))) {
	int i, xc, flag = 0;
	char **xv = NULL;

	strncpy(gbuf,p,GBUFSIZ-1);	/* Make a pokeable copy */
	gbuf[GBUFSIZ-1] = NUL;
	gptr = p;
	p = gbuf;

	/* Turn it into an argument vector */

	for (i = 0; gbuf[i] && i < GBUFSIZ && gargc < GARGC; i++) {
	    if (!flag) {
		if (gbuf[i] <= SP)
		  continue;
		flag = 1;
		gargs[gargc++] = &gbuf[i];
	    } else if (flag && gbuf[i] <= SP) {
		gbuf[i] = NUL;
		flag = 0;
		continue;
	    }
	}
	xv = xargv;			/* Save original argument vector */
	xc = xargc;
	xargv = gargs;			/* Redirect it to the one we made */
	xargc = gargc;

	while (xargc-- > 0) {		/* Go through the words */
	    if (**xargv == '-') {	/* Got an option (begins with dash) */
		c = *(*xargv+1);	/* Get the option letter */
		x = doarg(c);		/* Go handle the option */
		if (x < 0) doexit(1);
	    } else {			/* No dash where expected */
		fprintf(stderr,
			"?GKERMIT variable option error: \"%s\"\n",
			*xargv
			);
		usage();		/* Give usage message */
		doexit(0);
	    }
	    xargv++;
	}
	xargv = xv;			/* Restore original argument vector */
	xargc = xc;
    }
#endif /* NOGETENV */

    while (--xargc > 0) {		/* Go through command line words */
	xargv++;
    	if (**xargv == '-') {		/* Got an option (begins with dash) */
	    c = *(*xargv+1);		/* Get the option letter */
	    x = doarg(c);		/* Go handle the option */
	    if (x < 0) doexit(1);
    	} else {			/* No dash where expected */
	    fprintf(stderr,"?Command-line option error: \"%s\"\n", *xargv);
	    usage();			/* Give usage message */
	    doexit(1);
	}
    }
    return(action);			/* Then do any requested protocol */
}

/*  D O A R G  --  Do a command-line argument.  */

static int
#ifdef __STDC__
doarg(char x)
#else
doarg(x) char x;
#endif /* __STDC__ */
{
    int z; char *xp, **p;

    xp = *xargv+1;			/* Pointer for bundled args */
    while (x) {
	switch (x) {
	  case 'r':			/* Receive */
	    if (action) fatal("conflicting actions");
	    action = 'v';
	    break;

	  case 's':			/* Send */
	    if (action) fatal("conflicting actions");
	    if (*(xp+1)) fatal("invalid argument bundling after -s");
	    nfils = 0;			/* Initialize file counter, flag */
	    cmlist = xargv+1;		/* Remember this pointer */
	    if (zchki(*cmlist) < 0)
	      fatal("file not found or not accessible");
	    while (--xargc > 0) {	/* Traverse the list */
		*xargv++;
		if (**xargv == '-')
		  break;
		nfils++;
	    }
	    xargc++, *xargv--;		/* Adjust argv/argc */
	    if (nfils < 1) fatal("missing filename for -s");
	    action = 's';
	    break;

	  case 'g':			/* get */
	    if (action) fatal("conflicting actions");
	    if (*(xp+1)) fatal("invalid argument bundling after -g");
	    *xargv++, xargc--;
	    if ((xargc == 0) || (**xargv == '-'))
	      fatal("missing filename for -g");
	    cmarg = *xargv;
	    action = 'r';
	    break;

	  case 'h':			/* Help */
	  case '?':
	    usage();
	    doexit(0);

	  case 'i':			/* Binary (image) file transfer */
	    manual = 1;
	    text = 0;
	    break;

	  case 'T':			/* Text file transfer */
	    manual = 1;
	    text = 1;
	    break;

	  case 'p':			/* Parity */
	    if (*(xp+1)) fatal("invalid argument bundling");
	    *xargv++, xargc--;
	    if ((xargc < 1) || (**xargv == '-'))
	      fatal("missing parity");
	    switch(x = **xargv) {
	      case 'e':			/* Even */
	      case 'o':			/* Odd */
	      case 'm':			/* Mark */
	      case 's': parity = x; break; /* Space */
	      case 'n': parity = 0; break; /* None */
	      default:  fatal("invalid parity");
	    }
	    break;

	  case 'w':			/* Writeover */
	    backup = 0;			/* Don't back up existing files */
	    break;

	  case 'd':			/* Debug */
	    p = xargv;
	    *p++;
	    if ((xargc < 2) || (**p == '-')) {
		db = fopen("debug.log","w");
	    } else {
		*xargv++, xargc--;
		db = fopen(*xargv,"w");
	    }
	    if (db) {
		extern char * versio, * build;
		if (!versio) versio = "";
		if (!build) build = "";
		debug = 1;
		setbuf(db,NULL);
		fprintf(db,"%s: %s\n",
			(*versio ? versio : "GKERMIT VERSION UNKNOWN"),
			(*build ? build : "BUILD UNKNOWN")
			);
		fprintf(db,"MAXPATHLEN = %d\n",MAXPATHLEN);
		if (gptr) fprintf(db,"GKERMIT=\"%s\"\n",gptr);
	    }
	    break;

	  case 'a':			/* As-name */
	    if (*(xp+1)) fatal("invalid argument bundling after -a");
	    *xargv++, xargc--;
	    if ((xargc == 0) || (**xargv == '-'))
	      fatal("missing name for -a");
	    cmarg2 = *xargv;
	    if (debug) fprintf(db,"as-name: %s\n",cmarg2);
	    break;

	  case 'e':
	    if (*(xp+1)) fatal("invalid argument bundling after -e");
	    xargv++, xargc--;
	    if ((xargc < 1) || (**xargv == '-'))
	      fatal("missing length");
	    z = atoi(*xargv);		/* Convert to number. */
	    if (z >= 40 && z <= MAXRP) {
		rpsiz = urpsiz = z;
		if (z > 94) rpsiz = 94;	/* Fallback if other Kermit can't */
	    } else {			/* do long packets. */
		fatal("Unsupported packet length");
	    }
	    break;

	  case 'b':			/* Timeout */
	    if (*(xp+1)) fatal("invalid argument bundling after -b");
	    xargv++, xargc--;
	    if ((xargc < 1) || (**xargv == '-'))
	      fatal("missing value");
	    z = atoi(*xargv);		/* Convert to number */
	    if (z < 0) z = 0;
	    if (z > 90) z = 90;
	    timint = z;
	    break;

	  case 'P':			/* Path (file) names literal */
	    literal = 1;
	    break;

	  case 'q':			/* Quiet */
	    quiet = 1;
	    break;

	  case 'K':			/* Keep incompletely received files */
	    keep = 1;
	    break;

	  case 'S':			/* Disable streaming */
	    streamok = -1;
	    break;

	  case 'X':			/* gkermit is an external protocol */
	    quiet = 1;			/* No messages */
            nomodes = 1;		/* Don't set tty modes */
	    break;

	  case 'x':			/* Force Xon/Xoff */
	    xonxoff = 1;
	    noxonxoff = 0;
	    break;

	  case '-':			/* Don't force Xon/Xoff */
	    if (*(xp+1) == 'x') {
		xonxoff = 0;
		noxonxoff = 1;
	    } else {
		fatal("invalid argument, type 'gkermit -h' for help");
	    }
	    xp++;
	    break;

	  default:			/* Anything else */
	    fatal("invalid argument, type 'gkermit -h' for help");
        }
	x = *++xp;			/* See if options are bundled */
    }
    if (debug) {
	if (action)
	  fprintf(db,"cmdlin action = %c\n",action);
	else
	  fprintf(db,"cmdlin action = (none)\n");
    }
    return(action);
}

VOID
fatal(msg) char *msg; {			/* Fatal error message */
    fprintf(stderr,"\r\nFatal: %s",msg); /* doexit supplies crlf.. */
    doexit(1);				/* Exit indicating failure */
}

VOID
usage() {
    extern char * versio, * build, * url, * email;
    if (!versio) versio = "";
    if (!*versio) versio = "gkermit UNKNOWN VERSION";
    if (!build) build = "UNKNOWN BUILD";
    if (!url) url = "";
    if (!email) email = "";
    fprintf(stderr,"%s: %s.\n",versio,build);
    fprintf(stderr,"Usage:  gkermit [ options ]\n");
    fprintf(stderr,"Options:\n");
    fprintf(stderr," -r      Receive files\n");
    fprintf(stderr," -s fn   Send files\n");
    fprintf(stderr," -g fn   Get files from server\n");
    fprintf(stderr," -a fn   As-name for single file\n");
    fprintf(stderr," -i      Image (binary) mode transfer\n");
    fprintf(stderr," -T      Text mode transfer\n");
    fprintf(stderr," -P      Path/filename conversion disabled\n");
    fprintf(stderr," -w      Write over existing files with same name\n");
    fprintf(stderr," -K      Keep incompletely received files\n");
    fprintf(stderr," -p x    Parity: x = o[dd],e[ven],m[ark],s[pace],n[one]\n")
      ;
    fprintf(stderr," -e n    Receive packet-length (40-%d)\n",MAXRP);
    fprintf(stderr," -b n    Timeout (sec, 0 = none)\n");
    fprintf(stderr," -x      Force Xon/Xoff (--x = Don't force Xon/Xoff)\n");
    fprintf(stderr," -S      Disable streaming\n");
    fprintf(stderr," -X      External protocol\n");
    fprintf(stderr," -q      Quiet (suppress messages)\n");
    fprintf(stderr," -d [fn] Debug to ./debug.log [or specified file]\n");
    fprintf(stderr," -h      Help (this message)\n");
    if (*url) fprintf(stderr,  "Web:   %s\n",url);
    if (*email) fprintf(stderr,"Email: %s\n",email);
}
