1/*  G C M D L I N  --  gkermit command line parser  */
2
3/*
4  Author:
5    Frank da Cruz
6    The Kermit Project
7    Columbia University
8    612 West 115th Street
9    New York NY 10025-7799  USA
10    http://www.columbia.edu/kermit/
11    kermit@columbia.edu
12
13  Copyright (C) 1999,
14  The Trustees of Columbia University in the City of New York.
15
16  This program is free software; you can redistribute it and/or modify
17  it under the terms of the GNU General Public License as published by
18  the Free Software Foundation; either version 2 of the License, or
19  (at your option) any later version.
20
21  This program is distributed in the hope that it will be useful,
22  but WITHOUT ANY WARRANTY; without even the implied warranty of
23  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24  GNU General Public License for more details.
25
26  You should have received a copy of the GNU General Public License
27  along with this program; if not, write to the Free Software
28  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
29*/
30
31#include <stdio.h>
32#include <stdlib.h>
33#include <string.h>
34#include "gkermit.h"
35
36/* Externals */
37
38extern int nfils, parity, text, backup, rpsiz, urpsiz, timint;
39extern int literal, quiet, keep, streamok, nomodes, manual, xonxoff, noxonxoff;
40extern char ttname[], *cmerrp, *cmarg, *cmarg2;
41extern FILE * db;
42
43/* Variables exported from this module */
44
45extern char **cmlist;			/* Pointer to file list in argv */
46extern char **xargv;			/* Global copies of argv */
47extern int xargc;			/* and argc  */
48
49/* Variables and symbols local to this module */
50
51static int action = 0;			/* Action selected on command line */
52
53_MYPROTOTYPE( static int doarg, (char) );
54_MYPROTOTYPE( VOID fatal, (char *) );
55_MYPROTOTYPE( VOID usage, (void) );
56
57#ifndef NOGETENV
58#define GARGC 32
59#define GBUFSIZ 256
60static char gbuf[GBUFSIZ], *gargs[GARGC], *gptr = NULL;
61static int gargc;
62#endif /* NOGETENV */
63
64int					/* Command-line parser */
65cmdlin() {
66    char c;
67    int x;
68#ifndef NOGETENV
69    char * p = NULL;
70#endif /* NOGETENV */
71
72    cmarg = "";				/* Initialize results */
73    cmlist = NULL;
74    action = 0;
75
76#ifndef NOGETENV
77    if ((p = getenv("GKERMIT"))) {
78	int i, xc, flag = 0;
79	char **xv = NULL;
80
81	strncpy(gbuf,p,GBUFSIZ-1);	/* Make a pokeable copy */
82	gbuf[GBUFSIZ-1] = NUL;
83	gptr = p;
84	p = gbuf;
85
86	/* Turn it into an argument vector */
87
88	for (i = 0; gbuf[i] && i < GBUFSIZ && gargc < GARGC; i++) {
89	    if (!flag) {
90		if (gbuf[i] <= SP)
91		  continue;
92		flag = 1;
93		gargs[gargc++] = &gbuf[i];
94	    } else if (flag && gbuf[i] <= SP) {
95		gbuf[i] = NUL;
96		flag = 0;
97		continue;
98	    }
99	}
100	xv = xargv;			/* Save original argument vector */
101	xc = xargc;
102	xargv = gargs;			/* Redirect it to the one we made */
103	xargc = gargc;
104
105	while (xargc-- > 0) {		/* Go through the words */
106	    if (**xargv == '-') {	/* Got an option (begins with dash) */
107		c = *(*xargv+1);	/* Get the option letter */
108		x = doarg(c);		/* Go handle the option */
109		if (x < 0) doexit(1);
110	    } else {			/* No dash where expected */
111		fprintf(stderr,
112			"?GKERMIT variable option error: \"%s\"\n",
113			*xargv
114			);
115		usage();		/* Give usage message */
116		doexit(0);
117	    }
118	    xargv++;
119	}
120	xargv = xv;			/* Restore original argument vector */
121	xargc = xc;
122    }
123#endif /* NOGETENV */
124
125    while (--xargc > 0) {		/* Go through command line words */
126	xargv++;
127    	if (**xargv == '-') {		/* Got an option (begins with dash) */
128	    c = *(*xargv+1);		/* Get the option letter */
129	    x = doarg(c);		/* Go handle the option */
130	    if (x < 0) doexit(1);
131    	} else {			/* No dash where expected */
132	    fprintf(stderr,"?Command-line option error: \"%s\"\n", *xargv);
133	    usage();			/* Give usage message */
134	    doexit(1);
135	}
136    }
137    return(action);			/* Then do any requested protocol */
138}
139
140/*  D O A R G  --  Do a command-line argument.  */
141
142static int
143#ifdef __STDC__
144doarg(char x)
145#else
146doarg(x) char x;
147#endif /* __STDC__ */
148{
149    int z; char *xp, **p;
150
151    xp = *xargv+1;			/* Pointer for bundled args */
152    while (x) {
153	switch (x) {
154	  case 'r':			/* Receive */
155	    if (action) fatal("conflicting actions");
156	    action = 'v';
157	    break;
158
159	  case 's':			/* Send */
160	    if (action) fatal("conflicting actions");
161	    if (*(xp+1)) fatal("invalid argument bundling after -s");
162	    nfils = 0;			/* Initialize file counter, flag */
163	    cmlist = xargv+1;		/* Remember this pointer */
164	    if (zchki(*cmlist) < 0)
165	      fatal("file not found or not accessible");
166	    while (--xargc > 0) {	/* Traverse the list */
167		*xargv++;
168		if (**xargv == '-')
169		  break;
170		nfils++;
171	    }
172	    xargc++, *xargv--;		/* Adjust argv/argc */
173	    if (nfils < 1) fatal("missing filename for -s");
174	    action = 's';
175	    break;
176
177	  case 'g':			/* get */
178	    if (action) fatal("conflicting actions");
179	    if (*(xp+1)) fatal("invalid argument bundling after -g");
180	    *xargv++, xargc--;
181	    if ((xargc == 0) || (**xargv == '-'))
182	      fatal("missing filename for -g");
183	    cmarg = *xargv;
184	    action = 'r';
185	    break;
186
187	  case 'h':			/* Help */
188	  case '?':
189	    usage();
190	    doexit(0);
191
192	  case 'i':			/* Binary (image) file transfer */
193	    manual = 1;
194	    text = 0;
195	    break;
196
197	  case 'T':			/* Text file transfer */
198	    manual = 1;
199	    text = 1;
200	    break;
201
202	  case 'p':			/* Parity */
203	    if (*(xp+1)) fatal("invalid argument bundling");
204	    *xargv++, xargc--;
205	    if ((xargc < 1) || (**xargv == '-'))
206	      fatal("missing parity");
207	    switch(x = **xargv) {
208	      case 'e':			/* Even */
209	      case 'o':			/* Odd */
210	      case 'm':			/* Mark */
211	      case 's': parity = x; break; /* Space */
212	      case 'n': parity = 0; break; /* None */
213	      default:  fatal("invalid parity");
214	    }
215	    break;
216
217	  case 'w':			/* Writeover */
218	    backup = 0;			/* Don't back up existing files */
219	    break;
220
221	  case 'd':			/* Debug */
222	    p = xargv;
223	    *p++;
224	    if ((xargc < 2) || (**p == '-')) {
225		db = fopen("debug.log","w");
226	    } else {
227		*xargv++, xargc--;
228		db = fopen(*xargv,"w");
229	    }
230	    if (db) {
231		extern char * versio, * build;
232		if (!versio) versio = "";
233		if (!build) build = "";
234		debug = 1;
235		setbuf(db,NULL);
236		fprintf(db,"%s: %s\n",
237			(*versio ? versio : "GKERMIT VERSION UNKNOWN"),
238			(*build ? build : "BUILD UNKNOWN")
239			);
240		fprintf(db,"MAXPATHLEN = %d\n",MAXPATHLEN);
241		if (gptr) fprintf(db,"GKERMIT=\"%s\"\n",gptr);
242	    }
243	    break;
244
245	  case 'a':			/* As-name */
246	    if (*(xp+1)) fatal("invalid argument bundling after -a");
247	    *xargv++, xargc--;
248	    if ((xargc == 0) || (**xargv == '-'))
249	      fatal("missing name for -a");
250	    cmarg2 = *xargv;
251	    if (debug) fprintf(db,"as-name: %s\n",cmarg2);
252	    break;
253
254	  case 'e':
255	    if (*(xp+1)) fatal("invalid argument bundling after -e");
256	    xargv++, xargc--;
257	    if ((xargc < 1) || (**xargv == '-'))
258	      fatal("missing length");
259	    z = atoi(*xargv);		/* Convert to number. */
260	    if (z >= 40 && z <= MAXRP) {
261		rpsiz = urpsiz = z;
262		if (z > 94) rpsiz = 94;	/* Fallback if other Kermit can't */
263	    } else {			/* do long packets. */
264		fatal("Unsupported packet length");
265	    }
266	    break;
267
268	  case 'b':			/* Timeout */
269	    if (*(xp+1)) fatal("invalid argument bundling after -b");
270	    xargv++, xargc--;
271	    if ((xargc < 1) || (**xargv == '-'))
272	      fatal("missing value");
273	    z = atoi(*xargv);		/* Convert to number */
274	    if (z < 0) z = 0;
275	    if (z > 90) z = 90;
276	    timint = z;
277	    break;
278
279	  case 'P':			/* Path (file) names literal */
280	    literal = 1;
281	    break;
282
283	  case 'q':			/* Quiet */
284	    quiet = 1;
285	    break;
286
287	  case 'K':			/* Keep incompletely received files */
288	    keep = 1;
289	    break;
290
291	  case 'S':			/* Disable streaming */
292	    streamok = -1;
293	    break;
294
295	  case 'X':			/* gkermit is an external protocol */
296	    quiet = 1;			/* No messages */
297            nomodes = 1;		/* Don't set tty modes */
298	    break;
299
300	  case 'x':			/* Force Xon/Xoff */
301	    xonxoff = 1;
302	    noxonxoff = 0;
303	    break;
304
305	  case '-':			/* Don't force Xon/Xoff */
306	    if (*(xp+1) == 'x') {
307		xonxoff = 0;
308		noxonxoff = 1;
309	    } else {
310		fatal("invalid argument, type 'gkermit -h' for help");
311	    }
312	    xp++;
313	    break;
314
315	  default:			/* Anything else */
316	    fatal("invalid argument, type 'gkermit -h' for help");
317        }
318	x = *++xp;			/* See if options are bundled */
319    }
320    if (debug) {
321	if (action)
322	  fprintf(db,"cmdlin action = %c\n",action);
323	else
324	  fprintf(db,"cmdlin action = (none)\n");
325    }
326    return(action);
327}
328
329VOID
330fatal(msg) char *msg; {			/* Fatal error message */
331    fprintf(stderr,"\r\nFatal: %s",msg); /* doexit supplies crlf.. */
332    doexit(1);				/* Exit indicating failure */
333}
334
335VOID
336usage() {
337    extern char * versio, * build, * url, * email;
338    if (!versio) versio = "";
339    if (!*versio) versio = "gkermit UNKNOWN VERSION";
340    if (!build) build = "UNKNOWN BUILD";
341    if (!url) url = "";
342    if (!email) email = "";
343    fprintf(stderr,"%s: %s.\n",versio,build);
344    fprintf(stderr,"Usage:  gkermit [ options ]\n");
345    fprintf(stderr,"Options:\n");
346    fprintf(stderr," -r      Receive files\n");
347    fprintf(stderr," -s fn   Send files\n");
348    fprintf(stderr," -g fn   Get files from server\n");
349    fprintf(stderr," -a fn   As-name for single file\n");
350    fprintf(stderr," -i      Image (binary) mode transfer\n");
351    fprintf(stderr," -T      Text mode transfer\n");
352    fprintf(stderr," -P      Path/filename conversion disabled\n");
353    fprintf(stderr," -w      Write over existing files with same name\n");
354    fprintf(stderr," -K      Keep incompletely received files\n");
355    fprintf(stderr," -p x    Parity: x = o[dd],e[ven],m[ark],s[pace],n[one]\n")
356      ;
357    fprintf(stderr," -e n    Receive packet-length (40-%d)\n",MAXRP);
358    fprintf(stderr," -b n    Timeout (sec, 0 = none)\n");
359    fprintf(stderr," -x      Force Xon/Xoff (--x = Don't force Xon/Xoff)\n");
360    fprintf(stderr," -S      Disable streaming\n");
361    fprintf(stderr," -X      External protocol\n");
362    fprintf(stderr," -q      Quiet (suppress messages)\n");
363    fprintf(stderr," -d [fn] Debug to ./debug.log [or specified file]\n");
364    fprintf(stderr," -h      Help (this message)\n");
365    if (*url || *email)
366      fprintf(stderr,"More info: %s <%s>",url,email);
367}
368