GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/tftp/main.c Lines: 0 330 0.0 %
Date: 2017-11-13 Branches: 0 218 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: main.c,v 1.41 2017/01/21 11:32:04 guenther Exp $	*/
2
/*	$NetBSD: main.c,v 1.6 1995/05/21 16:54:10 mycroft Exp $	*/
3
4
/*
5
 * Copyright (c) 1983, 1993
6
 *	The Regents of the University of California.  All rights reserved.
7
 *
8
 * Redistribution and use in source and binary forms, with or without
9
 * modification, are permitted provided that the following conditions
10
 * are met:
11
 * 1. Redistributions of source code must retain the above copyright
12
 *    notice, this list of conditions and the following disclaimer.
13
 * 2. Redistributions in binary form must reproduce the above copyright
14
 *    notice, this list of conditions and the following disclaimer in the
15
 *    documentation and/or other materials provided with the distribution.
16
 * 3. Neither the name of the University nor the names of its contributors
17
 *    may be used to endorse or promote products derived from this software
18
 *    without specific prior written permission.
19
 *
20
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30
 * SUCH DAMAGE.
31
 */
32
33
/*
34
 * TFTP User Program -- Command Interface
35
 *
36
 * This version includes many modifications by Jim Guyton <guyton@rand-unix>
37
 */
38
39
#include <sys/socket.h>
40
#include <sys/file.h>
41
42
#include <netinet/in.h>
43
#include <arpa/inet.h>
44
#include <arpa/tftp.h>
45
46
#include <ctype.h>
47
#include <err.h>
48
#include <errno.h>
49
#include <netdb.h>
50
#include <poll.h>
51
#include <signal.h>
52
#include <stdio.h>
53
#include <stdlib.h>
54
#include <string.h>
55
#include <unistd.h>
56
#include <limits.h>
57
58
#include "extern.h"
59
60
#define	LBUFLEN		200		/* size of input buffer */
61
#define	MAXARGV		20
62
#define HELPINDENT	(sizeof("connect"))
63
64
void			 get(int, char **);
65
void			 help(int, char **);
66
void			 modecmd(int, char **);
67
void			 put(int, char **);
68
void			 quit(int, char **);
69
void			 setascii(int, char **);
70
void			 setbinary(int, char **);
71
void			 setpeer(char *, char *);
72
void			 parsearg(int, char **);
73
void			 setrexmt(int, char **);
74
void			 settimeout(int, char **);
75
void			 settrace(int, char **);
76
void			 setverbose(int, char **);
77
void			 settsize(int, char **);
78
void			 settout(int, char **);
79
void			 setblksize(int, char **);
80
void			 status(int, char **);
81
int			 readcmd(char *, int, FILE *);
82
static void		 getusage(char *);
83
static int		 makeargv(void);
84
static void		 putusage(char *);
85
static void		 settftpmode(char *);
86
static __dead void	 command(void);
87
struct cmd	 	*getcmd(char *);
88
char			*tail(char *);
89
90
struct sockaddr_storage	 peeraddr;
91
int			 f;
92
int			 trace;
93
int			 verbose;
94
int			 connected;
95
char			 mode[32];
96
char			 line[LBUFLEN];
97
int			 margc;
98
char			*margv[MAXARGV+1];
99
char			*prompt = "tftp";
100
void			 intr(int);
101
int	 		 rexmtval = TIMEOUT;
102
int	 		 maxtimeout = 5 * TIMEOUT;
103
char	 		 hostname[HOST_NAME_MAX+1];
104
FILE			*file = NULL;
105
volatile sig_atomic_t	 intrflag = 0;
106
char			*ackbuf;
107
int			 has_options = 0;
108
int			 opt_tsize = 0;
109
int			 opt_tout = 0;
110
int			 opt_blksize = 0;
111
112
char	vhelp[] = "toggle verbose mode";
113
char	thelp[] = "toggle packet tracing";
114
char	chelp[] = "connect to remote tftp";
115
char	qhelp[] = "exit tftp";
116
char	hhelp[] = "print help information";
117
char	shelp[] = "send file";
118
char	rhelp[] = "receive file";
119
char	mhelp[] = "set file transfer mode";
120
char	sthelp[] = "show current status";
121
char	xhelp[] = "set per-packet retransmission timeout";
122
char	ihelp[] = "set total retransmission timeout";
123
char	ashelp[] = "set mode to netascii";
124
char	bnhelp[] = "set mode to octet";
125
char	oshelp[] = "toggle tsize option";
126
char	othelp[] = "toggle timeout option";
127
char	obhelp[] = "set alternative blksize option";
128
129
struct cmd {
130
	char	*name;
131
	char	*help;
132
	void	 (*handler)(int, char **);
133
};
134
135
struct cmd cmdtab[] = {
136
	{ "connect",	chelp,	parsearg },
137
	{ "mode",       mhelp,	modecmd },
138
	{ "put",	shelp,	put },
139
	{ "get",	rhelp,	get },
140
	{ "quit",	qhelp,	quit },
141
	{ "verbose",	vhelp,	setverbose },
142
	{ "trace",	thelp,	settrace },
143
	{ "status",	sthelp,	status },
144
	{ "binary",     bnhelp,	setbinary },
145
	{ "ascii",      ashelp,	setascii },
146
	{ "rexmt",	xhelp,	setrexmt },
147
	{ "timeout",	ihelp,	settimeout },
148
	{ "tsize",	oshelp, settsize },
149
	{ "tout",	othelp, settout },
150
	{ "blksize",	obhelp,	setblksize },
151
	{ "help",	hhelp,	help },
152
	{ "?",		hhelp,	help },
153
	{ NULL,		NULL,	NULL }
154
};
155
156
struct	modes {
157
	char	*m_name;
158
	char	*m_mode;
159
} modes[] = {
160
	{ "ascii",	"netascii" },
161
	{ "netascii",	"netascii" },
162
	{ "binary",	"octet" },
163
	{ "image",	"octet" },
164
	{ "octet",	"octet" },
165
/*	{ "mail",	"mail" }, */
166
	{ NULL,		NULL }
167
};
168
169
int
170
main(int argc, char *argv[])
171
{
172
	f = -1;
173
174
	if (pledge("stdio rpath wpath cpath dns inet flock", NULL) == -1)
175
		err(1, "pledge");
176
177
	/* set default transfer mode */
178
	strlcpy(mode, "netascii", sizeof(mode));
179
180
	/* set peer if given */
181
	if (argc > 1)
182
		parsearg(argc, argv);
183
184
	/* catch SIGINT */
185
	signal(SIGINT, intr);
186
187
	/* allocate memory for packets */
188
	if ((ackbuf = malloc(SEGSIZE_MAX + 4)) == NULL)
189
		err(1, "malloc");
190
191
	/* command prompt */
192
	command();
193
194
	return (0);
195
}
196
197
void
198
setpeer(char *host, char *port)
199
{
200
	struct addrinfo hints, *res0, *res;
201
	int error;
202
	struct sockaddr_storage ss;
203
	char *cause = "unknown";
204
205
	if (connected) {
206
		close(f);
207
		f = -1;
208
	}
209
	connected = 0;
210
211
	memset(&hints, 0, sizeof(hints));
212
	hints.ai_family = PF_UNSPEC;
213
	hints.ai_socktype = SOCK_DGRAM;
214
	hints.ai_protocol = IPPROTO_UDP;
215
	hints.ai_flags = AI_CANONNAME;
216
	if (!port)
217
		port = "tftp";
218
	error = getaddrinfo(host, port, &hints, &res0);
219
	if (error) {
220
		warnx("%s", gai_strerror(error));
221
		return;
222
	}
223
224
	for (res = res0; res; res = res->ai_next) {
225
		if (res->ai_addrlen > sizeof(peeraddr))
226
			continue;
227
		f = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
228
		if (f < 0) {
229
			cause = "socket";
230
			continue;
231
		}
232
233
		memset(&ss, 0, sizeof(ss));
234
		ss.ss_family = res->ai_family;
235
		if (bind(f, (struct sockaddr *)&ss, res->ai_addrlen) < 0) {
236
			cause = "bind";
237
			close(f);
238
			f = -1;
239
			continue;
240
		}
241
242
		break;
243
	}
244
245
	if (f < 0)
246
		warn("%s", cause);
247
	else {
248
		/* res->ai_addr <= sizeof(peeraddr) is guaranteed */
249
		memcpy(&peeraddr, res->ai_addr, res->ai_addrlen);
250
		if (res->ai_canonname) {
251
			(void)strlcpy(hostname, res->ai_canonname,
252
			    sizeof(hostname));
253
		} else
254
			(void)strlcpy(hostname, host, sizeof(hostname));
255
			connected = 1;
256
	}
257
	freeaddrinfo(res0);
258
}
259
260
void
261
parsearg(int argc, char *argv[])
262
{
263
	if (argc < 2) {
264
		strlcpy(line, "Connect ", sizeof(line));
265
		printf("(to) ");
266
		readcmd(&line[strlen(line)], LBUFLEN - strlen(line), stdin);
267
		if (makeargv())
268
			return;
269
		argc = margc;
270
		argv = margv;
271
	}
272
	if ((argc < 2) || (argc > 3)) {
273
		printf("usage: %s [host [port]]\n", argv[0]);
274
		return;
275
	}
276
	if (argc == 2)
277
		setpeer(argv[1], NULL);
278
	else
279
		setpeer(argv[1], argv[2]);
280
}
281
282
void
283
modecmd(int argc, char *argv[])
284
{
285
	struct modes	*p;
286
	char		*sep;
287
288
	if (argc < 2) {
289
		printf("Using %s mode to transfer files.\n", mode);
290
		return;
291
	}
292
	if (argc == 2) {
293
		for (p = modes; p->m_name != NULL; p++)
294
			if (strcmp(argv[1], p->m_name) == 0)
295
				break;
296
		if (p->m_name) {
297
			settftpmode(p->m_mode);
298
			return;
299
		}
300
		printf("%s: unknown mode\n", argv[1]);
301
		/* drop through and print usage message */
302
	}
303
304
	printf("usage: %s [", argv[0]);
305
	sep = " ";
306
	for (p = modes; p->m_name != NULL; p++) {
307
		printf("%s%s", sep, p->m_name);
308
		if (*sep == ' ')
309
			sep = " | ";
310
	}
311
	printf(" ]\n");
312
313
	return;
314
}
315
316
/* ARGSUSED */
317
void
318
setbinary(int argc, char *argv[])
319
{
320
	settftpmode("octet");
321
}
322
323
/* ARGSUSED */
324
void
325
setascii(int argc, char *argv[])
326
{
327
	settftpmode("netascii");
328
}
329
330
static void
331
settftpmode(char *newmode)
332
{
333
	strlcpy(mode, newmode, sizeof(mode));
334
	if (verbose)
335
		printf("mode set to %s\n", mode);
336
}
337
338
/*
339
 * Send file(s).
340
 */
341
void
342
put(int argc, char *argv[])
343
{
344
	int	 fd;
345
	int	 n;
346
	char	*cp, *targ;
347
348
	if (argc < 2) {
349
		strlcpy(line, "put ", sizeof(line));
350
		printf("(file) ");
351
		readcmd(&line[strlen(line)], LBUFLEN - strlen(line), stdin);
352
		if (makeargv())
353
			return;
354
		argc = margc;
355
		argv = margv;
356
	}
357
	if (argc < 2) {
358
		putusage(argv[0]);
359
		return;
360
	}
361
	targ = argv[argc - 1];
362
	if (strrchr(argv[argc - 1], ':')) {
363
364
		for (n = 1; n < argc - 1; n++)
365
			if (strchr(argv[n], ':')) {
366
				putusage(argv[0]);
367
				return;
368
			}
369
		cp = argv[argc - 1];
370
		targ = strrchr(cp, ':');
371
		*targ++ = 0;
372
		if (cp[0] == '[' && cp[strlen(cp) - 1] == ']') {
373
			cp[strlen(cp) - 1] = '\0';
374
			cp++;
375
		}
376
		setpeer(cp, NULL);
377
	}
378
	if (!connected) {
379
		printf("No target machine specified.\n");
380
		return;
381
	}
382
	if (argc < 4) {
383
		cp = argc == 2 ? tail(targ) : argv[1];
384
		fd = open(cp, O_RDONLY);
385
		if (fd < 0) {
386
			warn("open: %s", cp);
387
			return;
388
		}
389
		if (verbose)
390
			printf("putting %s to %s:%s [%s]\n",
391
			    cp, hostname, targ, mode);
392
		sendfile(fd, targ, mode);
393
		return;
394
	}
395
396
	/*
397
	 * this assumes the target is a directory on
398
	 * on a remote unix system.  hmmmm.
399
	 */
400
	for (n = 1; n < argc - 1; n++) {
401
		if (asprintf(&cp, "%s/%s", targ, tail(argv[n])) == -1)
402
			err(1, "asprintf");
403
		fd = open(argv[n], O_RDONLY);
404
		if (fd < 0) {
405
			warn("open: %s", argv[n]);
406
			free(cp);
407
			continue;
408
		}
409
		if (verbose)
410
			printf("putting %s to %s:%s [%s]\n",
411
			    argv[n], hostname, cp, mode);
412
		sendfile(fd, cp, mode);
413
		free(cp);
414
	}
415
}
416
417
static void
418
putusage(char *s)
419
{
420
	printf("usage: %s file [[host:]remotename]\n", s);
421
	printf("       %s file1 file2 ... fileN [[host:]remote-directory]\n",
422
	    s);
423
}
424
425
/*
426
 * Receive file(s).
427
 */
428
void
429
get(int argc, char *argv[])
430
{
431
	int	 fd;
432
	int	 n;
433
	char	*cp;
434
	char	*src;
435
436
	if (argc < 2) {
437
		strlcpy(line, "get ", sizeof(line));
438
		printf("(files) ");
439
		readcmd(&line[strlen(line)], LBUFLEN - strlen(line), stdin);
440
		if (makeargv())
441
			return;
442
		argc = margc;
443
		argv = margv;
444
	}
445
	if (argc < 2) {
446
		getusage(argv[0]);
447
		return;
448
	}
449
	if (!connected) {
450
		for (n = 1; n < argc; n++)
451
			if (strrchr(argv[n], ':') == 0) {
452
				getusage(argv[0]);
453
				return;
454
			}
455
	}
456
	for (n = 1; n < argc; n++) {
457
		src = strrchr(argv[n], ':');
458
		if (src == NULL)
459
			src = argv[n];
460
		else {
461
			char *cp;
462
463
			*src++ = 0;
464
			cp = argv[n];
465
			if (cp[0] == '[' && cp[strlen(cp) - 1] == ']') {
466
				cp[strlen(cp) - 1] = '\0';
467
				cp++;
468
			}
469
			setpeer(cp, NULL);
470
			if (!connected)
471
				continue;
472
		}
473
		if (argc < 4) {
474
			cp = argc == 3 ? argv[2] : tail(src);
475
			fd = open(cp, O_CREAT | O_TRUNC | O_WRONLY, 0644);
476
			if (fd < 0) {
477
				warn("create: %s", cp);
478
				return;
479
			}
480
			if (verbose)
481
				printf("getting from %s:%s to %s [%s]\n",
482
				    hostname, src, cp, mode);
483
			recvfile(fd, src, mode);
484
			break;
485
		}
486
		cp = tail(src);	/* new .. jdg */
487
		fd = open(cp, O_CREAT | O_TRUNC | O_WRONLY, 0644);
488
		if (fd < 0) {
489
			warn("create: %s", cp);
490
			continue;
491
		}
492
		if (verbose)
493
			printf("getting from %s:%s to %s [%s]\n",
494
			    hostname, src, cp, mode);
495
		recvfile(fd, src, mode);
496
	}
497
}
498
499
static void
500
getusage(char *s)
501
{
502
	printf("usage: %s [host:]file [localname]\n", s);
503
	printf("       %s [host1:]file1 [host2:]file2 ... [hostN:]fileN\n", s);
504
}
505
506
void
507
setrexmt(int argc, char *argv[])
508
{
509
	int		 t;
510
	const char	*errstr;
511
512
	if (argc < 2) {
513
		strlcpy(line, "Rexmt-timeout ", sizeof(line));
514
		printf("(value) ");
515
		readcmd(&line[strlen(line)], LBUFLEN - strlen(line), stdin);
516
		if (makeargv())
517
			return;
518
		argc = margc;
519
		argv = margv;
520
	}
521
	if (argc != 2) {
522
		printf("usage: %s value\n", argv[0]);
523
		return;
524
	}
525
	t = strtonum(argv[1], TIMEOUT_MIN, TIMEOUT_MAX, &errstr);
526
	if (errstr)
527
		printf("%s: value is %s\n", argv[1], errstr);
528
	else
529
		rexmtval = t;
530
}
531
532
void
533
settimeout(int argc, char *argv[])
534
{
535
	int		 t;
536
	const char	*errstr;
537
538
	if (argc < 2) {
539
		strlcpy(line, "Maximum-timeout ", sizeof(line));
540
		printf("(value) ");
541
		readcmd(&line[strlen(line)], LBUFLEN - strlen(line), stdin);
542
		if (makeargv())
543
			return;
544
		argc = margc;
545
		argv = margv;
546
	}
547
	if (argc != 2) {
548
		printf("usage: %s value\n", argv[0]);
549
		return;
550
	}
551
	t = strtonum(argv[1], TIMEOUT_MIN, TIMEOUT_MAX, &errstr);
552
	if (errstr)
553
		printf("%s: value is %s\n", argv[1], errstr);
554
	else
555
		maxtimeout = t;
556
}
557
558
/* ARGSUSED */
559
void
560
status(int argc, char *argv[])
561
{
562
	if (connected)
563
		printf("Connected to %s.\n", hostname);
564
	else
565
		printf("Not connected.\n");
566
	printf("Mode: %s Verbose: %s Tracing: %s\n",
567
	    mode, verbose ? "on" : "off", trace ? "on" : "off");
568
	printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n",
569
	    rexmtval, maxtimeout);
570
}
571
572
/* ARGSUSED */
573
void
574
intr(int signo)
575
{
576
	intrflag = 1;
577
}
578
579
char *
580
tail(char *filename)
581
{
582
	char	*s;
583
584
	while (*filename) {
585
		s = strrchr(filename, '/');
586
		if (s == NULL)
587
			break;
588
		if (s[1])
589
			return (s + 1);
590
		*s = '\0';
591
	}
592
593
	return (filename);
594
}
595
596
/*
597
 * Command parser.
598
 */
599
static __dead void
600
command(void)
601
{
602
	struct cmd	*c;
603
604
	for (;;) {
605
		printf("%s> ", prompt);
606
		if (readcmd(line, LBUFLEN, stdin) < 1)
607
			continue;
608
		if ((line[0] == 0) || (line[0] == '\n'))
609
			continue;
610
		if (makeargv())
611
			continue;
612
		if (margc == 0)
613
			continue;
614
		c = getcmd(margv[0]);
615
		if (c == (struct cmd *) - 1) {
616
			printf("?Ambiguous command\n");
617
			continue;
618
		}
619
		if (c == 0) {
620
			printf("?Invalid command\n");
621
			continue;
622
		}
623
		(*c->handler)(margc, margv);
624
	}
625
}
626
627
struct cmd *
628
getcmd(char *name)
629
{
630
	char		*p, *q;
631
	struct cmd	*c, *found;
632
	int		 nmatches, longest;
633
634
	longest = 0;
635
	nmatches = 0;
636
	found = 0;
637
	intrflag = 0;
638
	for (c = cmdtab; (p = c->name) != NULL; c++) {
639
		for (q = name; *q == *p++; q++)
640
			if (*q == 0)		/* exact match? */
641
				return (c);
642
		if (!*q) {			/* the name was a prefix */
643
			if (q - name > longest) {
644
				longest = q - name;
645
				nmatches = 1;
646
				found = c;
647
			} else if (q - name == longest)
648
				nmatches++;
649
		}
650
	}
651
	if (nmatches > 1)
652
		return ((struct cmd *) - 1);
653
654
	return (found);
655
}
656
657
/*
658
 * Slice a string up into argc/argv.
659
 */
660
static int
661
makeargv(void)
662
{
663
	char	 *cp;
664
	char	**argp = margv;
665
	int	  ret = 0;
666
667
	margc = 0;
668
	for (cp = line; *cp;) {
669
		if (margc >= MAXARGV) {
670
			printf("too many arguments\n");
671
			ret = 1;
672
			break;
673
		}
674
		while (isspace((unsigned char)*cp))
675
			cp++;
676
		if (*cp == '\0')
677
			break;
678
		*argp++ = cp;
679
		margc += 1;
680
		while (*cp != '\0' && !isspace((unsigned char)*cp))
681
			cp++;
682
		if (*cp == '\0')
683
			break;
684
		*cp++ = '\0';
685
	}
686
	*argp++ = 0;
687
688
	return (ret);
689
}
690
691
/* ARGSUSED */
692
void
693
quit(int argc, char *argv[])
694
{
695
	exit(0);
696
}
697
698
/*
699
 * Help command.
700
 */
701
void
702
help(int argc, char *argv[])
703
{
704
	struct cmd	*c;
705
706
	if (argc == 1) {
707
		printf("Commands may be abbreviated.  Commands are:\n\n");
708
		for (c = cmdtab; c->name != NULL; c++)
709
			printf("%-*s\t%s\n", (int)HELPINDENT, c->name, c->help);
710
		return;
711
	}
712
	while (--argc > 0) {
713
		char *arg;
714
		arg = *++argv;
715
		c = getcmd(arg);
716
		if (c == (struct cmd *) - 1)
717
			printf("?Ambiguous help command %s\n", arg);
718
		else if (c == NULL)
719
			printf("?Invalid help command %s\n", arg);
720
		else
721
			printf("%s\n", c->help);
722
	}
723
}
724
725
/* ARGSUSED */
726
void
727
settrace(int argc, char *argv[])
728
{
729
	trace = !trace;
730
	printf("Packet tracing %s.\n", trace ? "on" : "off");
731
}
732
733
/* ARGSUSED */
734
void
735
setverbose(int argc, char *argv[])
736
{
737
	verbose = !verbose;
738
	printf("Verbose mode %s.\n", verbose ? "on" : "off");
739
}
740
741
/* ARGSUSED */
742
void
743
settsize(int argc, char *argv[])
744
{
745
	opt_tsize = !opt_tsize;
746
	printf("Tsize option %s.\n", opt_tsize ? "on" : "off");
747
	if (opt_tsize)
748
		has_options++;
749
	else
750
		has_options--;
751
}
752
753
/* ARGSUSED */
754
void
755
settout(int argc, char *argv[])
756
{
757
	opt_tout = !opt_tout;
758
	printf("Timeout option %s.\n", opt_tout ? "on" : "off");
759
	if (opt_tout)
760
		has_options++;
761
	else
762
		has_options--;
763
}
764
765
void
766
setblksize(int argc, char *argv[])
767
{
768
	int		 t;
769
	const char	*errstr;
770
771
	if (argc < 2) {
772
		strlcpy(line, "Blocksize ", sizeof(line));
773
		printf("(value) ");
774
		readcmd(&line[strlen(line)], LBUFLEN - strlen(line), stdin);
775
		if (makeargv())
776
			return;
777
		argc = margc;
778
		argv = margv;
779
	}
780
	if (argc != 2) {
781
		printf("usage: %s value\n", argv[0]);
782
		return;
783
	}
784
	t = strtonum(argv[1], SEGSIZE_MIN, SEGSIZE_MAX, &errstr);
785
	if (errstr)
786
		printf("%s: value is %s\n", argv[1], errstr);
787
	else {
788
		if (opt_blksize == 0)
789
			has_options++;
790
		opt_blksize = t;
791
	}
792
}
793
794
int
795
readcmd(char *input, int len, FILE *stream)
796
{
797
	int		nfds;
798
	struct pollfd	pfd[1];
799
800
	fflush(stdout);
801
802
	pfd[0].fd = 0;
803
	pfd[0].events = POLLIN;
804
	nfds = poll(pfd, 1, INFTIM);
805
	if (nfds == -1) {
806
		if (intrflag) {
807
			intrflag = 0;
808
			putchar('\n');
809
			return (0);
810
		}
811
		exit(1);
812
	}
813
814
	if (fgets(input, len, stream) == NULL) {
815
		if (feof(stdin))
816
			exit(0);
817
		else
818
			return (-1);
819
	}
820
821
	return (1);
822
}