xref: /openbsd-src/usr.bin/tftp/main.c (revision 7bbe964f6b7d22ad07ca46292495604f942eba4e)
1 /*	$OpenBSD: main.c,v 1.30 2009/10/27 23:59:44 deraadt 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/param.h>
40 #include <sys/socket.h>
41 #include <sys/file.h>
42 
43 #include <netinet/in.h>
44 #include <arpa/inet.h>
45 #include <arpa/tftp.h>
46 
47 #include <ctype.h>
48 #include <err.h>
49 #include <errno.h>
50 #include <netdb.h>
51 #include <poll.h>
52 #include <signal.h>
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <string.h>
56 #include <unistd.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(int, char **);
72 void			 setrexmt(int, char **);
73 void			 settimeout(int, char **);
74 void			 settrace(int, char **);
75 void			 setverbose(int, char **);
76 void			 settsize(int, char **);
77 void			 settout(int, char **);
78 void			 setblksize(int, char **);
79 void			 status(int, char **);
80 int			 readcmd(char *, int, FILE *);
81 static void		 getusage(char *);
82 static int		 makeargv(void);
83 static void		 putusage(char *);
84 static void		 settftpmode(char *);
85 static __dead void	 command(void);
86 struct cmd	 	*getcmd(char *);
87 char			*tail(char *);
88 
89 struct sockaddr_in	 peeraddr;
90 int			 f;
91 short			 port;
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 struct servent		*sp;
102 int	 		 rexmtval = TIMEOUT;
103 int	 		 maxtimeout = 5 * TIMEOUT;
104 char	 		 hostname[MAXHOSTNAMELEN];
105 FILE			*file = NULL;
106 volatile sig_atomic_t	 intrflag = 0;
107 char			*ackbuf;
108 int			 has_options = 0;
109 int			 opt_tsize = 0;
110 int			 opt_tout = 0;
111 int			 opt_blksize = 0;
112 
113 char	vhelp[] = "toggle verbose mode";
114 char	thelp[] = "toggle packet tracing";
115 char	chelp[] = "connect to remote tftp";
116 char	qhelp[] = "exit tftp";
117 char	hhelp[] = "print help information";
118 char	shelp[] = "send file";
119 char	rhelp[] = "receive file";
120 char	mhelp[] = "set file transfer mode";
121 char	sthelp[] = "show current status";
122 char	xhelp[] = "set per-packet retransmission timeout";
123 char	ihelp[] = "set total retransmission timeout";
124 char	ashelp[] = "set mode to netascii";
125 char	bnhelp[] = "set mode to octet";
126 char	oshelp[] = "toggle tsize option";
127 char	othelp[] = "toggle timeout option";
128 char	obhelp[] = "set alternative blksize option";
129 
130 struct cmd {
131 	char	*name;
132 	char	*help;
133 	void	 (*handler)(int, char **);
134 };
135 
136 struct cmd cmdtab[] = {
137 	{ "connect",	chelp,	setpeer },
138 	{ "mode",       mhelp,	modecmd },
139 	{ "put",	shelp,	put },
140 	{ "get",	rhelp,	get },
141 	{ "quit",	qhelp,	quit },
142 	{ "verbose",	vhelp,	setverbose },
143 	{ "trace",	thelp,	settrace },
144 	{ "status",	sthelp,	status },
145 	{ "binary",     bnhelp,	setbinary },
146 	{ "ascii",      ashelp,	setascii },
147 	{ "rexmt",	xhelp,	setrexmt },
148 	{ "timeout",	ihelp,	settimeout },
149 	{ "tsize",	oshelp, settsize },
150 	{ "tout",	othelp, settout },
151 	{ "blksize",	obhelp,	setblksize },
152 	{ "help",	hhelp,	help },
153 	{ "?",		hhelp,	help },
154 	{ NULL,		NULL,	NULL }
155 };
156 
157 struct	modes {
158 	char	*m_name;
159 	char	*m_mode;
160 } modes[] = {
161 	{ "ascii",	"netascii" },
162 	{ "netascii",	"netascii" },
163 	{ "binary",	"octet" },
164 	{ "image",	"octet" },
165 	{ "octet",	"octet" },
166 /*	{ "mail",	"mail" }, */
167 	{ NULL,		NULL }
168 };
169 
170 int
171 main(int argc, char *argv[])
172 {
173 	struct sockaddr_in	s_in;
174 
175 	/* socket, bind */
176 	sp = getservbyname("tftp", "udp");
177 	if (sp == 0)
178 		errx(1, "udp/tftp: unknown service");
179 	f = socket(AF_INET, SOCK_DGRAM, 0);
180 	if (f < 0)
181 		err(3, "socket");
182 	bzero((char *)&s_in, sizeof(s_in));
183 	s_in.sin_family = AF_INET;
184 	if (bind(f, (struct sockaddr *)&s_in, sizeof(s_in)) < 0)
185 		err(1, "bind");
186 
187 	/* set default transfer mode */
188 	strlcpy(mode, "netascii", sizeof(mode));
189 
190 	/* set peer if given */
191 	if (argc > 1)
192 		setpeer(argc, argv);
193 
194 	/* catch SIGINT */
195 	signal(SIGINT, intr);
196 
197 	/* allocate memory for packets */
198 	if ((ackbuf = malloc(SEGSIZE_MAX + 4)) == NULL)
199 		err(1, "malloc");
200 
201 	/* command prompt */
202 	command();
203 
204 	return (0);
205 }
206 
207 void
208 setpeer(int argc, char *argv[])
209 {
210 	struct hostent	*host;
211 	const char	*errstr;
212 
213 	if (argc < 2) {
214 		strlcpy(line, "Connect ", sizeof(line));
215 		printf("(to) ");
216 		readcmd(&line[strlen(line)], LBUFLEN - strlen(line), stdin);
217 		if (makeargv())
218 			return;
219 		argc = margc;
220 		argv = margv;
221 	}
222 	if ((argc < 2) || (argc > 3)) {
223 		printf("usage: %s [host [port]]\n", argv[0]);
224 		return;
225 	}
226 	if (inet_aton(argv[1], &peeraddr.sin_addr) != 0) {
227 		peeraddr.sin_family = AF_INET;
228 		(void)strncpy(hostname, argv[1], sizeof(hostname));
229 		hostname[sizeof(hostname) - 1] = '\0';
230 	} else {
231 		host = gethostbyname(argv[1]);
232 		if (host == 0) {
233 			connected = 0;
234 			printf("%s: unknown host\n", argv[1]);
235 			return;
236 		}
237 		peeraddr.sin_family = host->h_addrtype;
238 		bcopy(host->h_addr, &peeraddr.sin_addr, host->h_length);
239 		(void)strlcpy(hostname, host->h_name, sizeof(hostname));
240 	}
241 	port = sp->s_port;
242 	if (argc == 3) {
243 		port = strtonum(argv[2], 1, 65535, &errstr);
244 		if (errstr) {
245 			printf("%s: port number is %s\n", argv[2], errstr);
246 			connected = 0;
247 			return;
248 		}
249 		port = htons(port);
250 	}
251 	connected = 1;
252 }
253 
254 void
255 modecmd(int argc, char *argv[])
256 {
257 	struct modes	*p;
258 	char		*sep;
259 
260 	if (argc < 2) {
261 		printf("Using %s mode to transfer files.\n", mode);
262 		return;
263 	}
264 	if (argc == 2) {
265 		for (p = modes; p->m_name != NULL; p++)
266 			if (strcmp(argv[1], p->m_name) == 0)
267 				break;
268 		if (p->m_name) {
269 			settftpmode(p->m_mode);
270 			return;
271 		}
272 		printf("%s: unknown mode\n", argv[1]);
273 		/* drop through and print usage message */
274 	}
275 
276 	printf("usage: %s [", argv[0]);
277 	sep = " ";
278 	for (p = modes; p->m_name != NULL; p++) {
279 		printf("%s%s", sep, p->m_name);
280 		if (*sep == ' ')
281 			sep = " | ";
282 	}
283 	printf(" ]\n");
284 
285 	return;
286 }
287 
288 /* ARGSUSED */
289 void
290 setbinary(int argc, char *argv[])
291 {
292 	settftpmode("octet");
293 }
294 
295 /* ARGSUSED */
296 void
297 setascii(int argc, char *argv[])
298 {
299 	settftpmode("netascii");
300 }
301 
302 static void
303 settftpmode(char *newmode)
304 {
305 	strlcpy(mode, newmode, sizeof(mode));
306 	if (verbose)
307 		printf("mode set to %s\n", mode);
308 }
309 
310 /*
311  * Send file(s).
312  */
313 void
314 put(int argc, char *argv[])
315 {
316 	int	 fd;
317 	int	 n;
318 	char	*cp, *targ;
319 
320 	if (argc < 2) {
321 		strlcpy(line, "send ", sizeof(line));
322 		printf("(file) ");
323 		readcmd(&line[strlen(line)], LBUFLEN - strlen(line), stdin);
324 		if (makeargv())
325 			return;
326 		argc = margc;
327 		argv = margv;
328 	}
329 	if (argc < 2) {
330 		putusage(argv[0]);
331 		return;
332 	}
333 	targ = argv[argc - 1];
334 	if (strchr(argv[argc - 1], ':')) {
335 		struct hostent	*hp;
336 
337 		for (n = 1; n < argc - 1; n++)
338 			if (strchr(argv[n], ':')) {
339 				putusage(argv[0]);
340 				return;
341 			}
342 		cp = argv[argc - 1];
343 		targ = strchr(cp, ':');
344 		*targ++ = 0;
345 		hp = gethostbyname(cp);
346 		if (hp == NULL) {
347 			warnx("%s: %s", cp, hstrerror(h_errno));
348 			return;
349 		}
350 		bcopy(hp->h_addr, (caddr_t)&peeraddr.sin_addr, hp->h_length);
351 		peeraddr.sin_family = hp->h_addrtype;
352 		connected = 1;
353 		port = sp->s_port;
354 		strlcpy(hostname, hp->h_name, sizeof(hostname));
355 	}
356 	if (!connected) {
357 		printf("No target machine specified.\n");
358 		return;
359 	}
360 	if (argc < 4) {
361 		cp = argc == 2 ? tail(targ) : argv[1];
362 		fd = open(cp, O_RDONLY);
363 		if (fd < 0) {
364 			warn("open: %s", cp);
365 			return;
366 		}
367 		if (verbose)
368 			printf("putting %s to %s:%s [%s]\n",
369 			    cp, hostname, targ, mode);
370 		peeraddr.sin_port = port;
371 		sendfile(fd, targ, mode);
372 		return;
373 	}
374 
375 	/*
376 	 * this assumes the target is a directory on
377 	 * on a remote unix system.  hmmmm.
378 	 */
379 	for (n = 1; n < argc - 1; n++) {
380 		if (asprintf(&cp, "%s/%s", targ, tail(argv[n])) == -1)
381 			err(1, "asprintf");
382 		fd = open(argv[n], O_RDONLY);
383 		if (fd < 0) {
384 			warn("open: %s", argv[n]);
385 			free(cp);
386 			continue;
387 		}
388 		if (verbose)
389 			printf("putting %s to %s:%s [%s]\n",
390 			    argv[n], hostname, cp, mode);
391 		peeraddr.sin_port = port;
392 		sendfile(fd, cp, mode);
393 		free(cp);
394 	}
395 }
396 
397 static void
398 putusage(char *s)
399 {
400 	printf("usage: %s file [[host:]remotename]\n", s);
401 	printf("       %s file1 file2 ... fileN [[host:]remote-directory]\n",
402 	    s);
403 }
404 
405 /*
406  * Receive file(s).
407  */
408 void
409 get(int argc, char *argv[])
410 {
411 	int	 fd;
412 	int	 n;
413 	char	*cp;
414 	char	*src;
415 
416 	if (argc < 2) {
417 		strlcpy(line, "get ", sizeof(line));
418 		printf("(files) ");
419 		readcmd(&line[strlen(line)], LBUFLEN - strlen(line), stdin);
420 		if (makeargv())
421 			return;
422 		argc = margc;
423 		argv = margv;
424 	}
425 	if (argc < 2) {
426 		getusage(argv[0]);
427 		return;
428 	}
429 	if (!connected) {
430 		for (n = 1; n < argc; n++)
431 			if (strchr(argv[n], ':') == 0) {
432 				getusage(argv[0]);
433 				return;
434 			}
435 	}
436 	for (n = 1; n < argc; n++) {
437 		src = strchr(argv[n], ':');
438 		if (src == NULL)
439 			src = argv[n];
440 		else {
441 			struct hostent	*hp;
442 
443 			*src++ = 0;
444 			hp = gethostbyname(argv[n]);
445 			if (hp == NULL) {
446 				warnx("%s: %s", argv[n], hstrerror(h_errno));
447 				continue;
448 			}
449 			bcopy(hp->h_addr, (caddr_t)&peeraddr.sin_addr,
450 			    hp->h_length);
451 			peeraddr.sin_family = hp->h_addrtype;
452 			connected = 1;
453 			strlcpy(hostname, hp->h_name, sizeof(hostname));
454 		}
455 		if (argc < 4) {
456 			cp = argc == 3 ? argv[2] : tail(src);
457 			fd = creat(cp, 0644);
458 			if (fd < 0) {
459 				warn("create: %s", cp);
460 				return;
461 			}
462 			if (verbose)
463 				printf("getting from %s:%s to %s [%s]\n",
464 				    hostname, src, cp, mode);
465 			peeraddr.sin_port = port;
466 			recvfile(fd, src, mode);
467 			break;
468 		}
469 		cp = tail(src);	/* new .. jdg */
470 		fd = creat(cp, 0644);
471 		if (fd < 0) {
472 			warn("create: %s", cp);
473 			continue;
474 		}
475 		if (verbose)
476 			printf("getting from %s:%s to %s [%s]\n",
477 			    hostname, src, cp, mode);
478 		peeraddr.sin_port = port;
479 		recvfile(fd, src, mode);
480 	}
481 }
482 
483 static void
484 getusage(char *s)
485 {
486 	printf("usage: %s [host:]file [localname]\n", s);
487 	printf("       %s [host1:]file1 [host2:]file2 ... [hostN:]fileN\n", s);
488 }
489 
490 void
491 setrexmt(int argc, char *argv[])
492 {
493 	int		 t;
494 	const char	*errstr;
495 
496 	if (argc < 2) {
497 		strlcpy(line, "Rexmt-timeout ", sizeof(line));
498 		printf("(value) ");
499 		readcmd(&line[strlen(line)], LBUFLEN - strlen(line), stdin);
500 		if (makeargv())
501 			return;
502 		argc = margc;
503 		argv = margv;
504 	}
505 	if (argc != 2) {
506 		printf("usage: %s value\n", argv[0]);
507 		return;
508 	}
509 	t = strtonum(argv[1], TIMEOUT_MIN, TIMEOUT_MAX, &errstr);
510 	if (errstr)
511 		printf("%s: value is %s\n", argv[1], errstr);
512 	else
513 		rexmtval = t;
514 }
515 
516 void
517 settimeout(int argc, char *argv[])
518 {
519 	int		 t;
520 	const char	*errstr;
521 
522 	if (argc < 2) {
523 		strlcpy(line, "Maximum-timeout ", sizeof(line));
524 		printf("(value) ");
525 		readcmd(&line[strlen(line)], LBUFLEN - strlen(line), stdin);
526 		if (makeargv())
527 			return;
528 		argc = margc;
529 		argv = margv;
530 	}
531 	if (argc != 2) {
532 		printf("usage: %s value\n", argv[0]);
533 		return;
534 	}
535 	t = strtonum(argv[1], TIMEOUT_MIN, TIMEOUT_MAX, &errstr);
536 	if (errstr)
537 		printf("%s: value is %s\n", argv[1], errstr);
538 	else
539 		maxtimeout = t;
540 }
541 
542 /* ARGSUSED */
543 void
544 status(int argc, char *argv[])
545 {
546 	if (connected)
547 		printf("Connected to %s.\n", hostname);
548 	else
549 		printf("Not connected.\n");
550 	printf("Mode: %s Verbose: %s Tracing: %s\n",
551 	    mode, verbose ? "on" : "off", trace ? "on" : "off");
552 	printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n",
553 	    rexmtval, maxtimeout);
554 }
555 
556 /* ARGSUSED */
557 void
558 intr(int signo)
559 {
560 	intrflag = 1;
561 }
562 
563 char *
564 tail(char *filename)
565 {
566 	char	*s;
567 
568 	while (*filename) {
569 		s = strrchr(filename, '/');
570 		if (s == NULL)
571 			break;
572 		if (s[1])
573 			return (s + 1);
574 		*s = '\0';
575 	}
576 
577 	return (filename);
578 }
579 
580 /*
581  * Command parser.
582  */
583 static __dead void
584 command(void)
585 {
586 	struct cmd	*c;
587 
588 	for (;;) {
589 		printf("%s> ", prompt);
590 		if (readcmd(line, LBUFLEN, stdin) < 1)
591 			continue;
592 		if ((line[0] == 0) || (line[0] == '\n'))
593 			continue;
594 		if (makeargv())
595 			continue;
596 		if (margc == 0)
597 			continue;
598 		c = getcmd(margv[0]);
599 		if (c == (struct cmd *) - 1) {
600 			printf("?Ambiguous command\n");
601 			continue;
602 		}
603 		if (c == 0) {
604 			printf("?Invalid command\n");
605 			continue;
606 		}
607 		(*c->handler)(margc, margv);
608 	}
609 }
610 
611 struct cmd *
612 getcmd(char *name)
613 {
614 	char		*p, *q;
615 	struct cmd	*c, *found;
616 	int		 nmatches, longest;
617 
618 	longest = 0;
619 	nmatches = 0;
620 	found = 0;
621 	intrflag = 0;
622 	for (c = cmdtab; (p = c->name) != NULL; c++) {
623 		for (q = name; *q == *p++; q++)
624 			if (*q == 0)		/* exact match? */
625 				return (c);
626 		if (!*q) {			/* the name was a prefix */
627 			if (q - name > longest) {
628 				longest = q - name;
629 				nmatches = 1;
630 				found = c;
631 			} else if (q - name == longest)
632 				nmatches++;
633 		}
634 	}
635 	if (nmatches > 1)
636 		return ((struct cmd *) - 1);
637 
638 	return (found);
639 }
640 
641 /*
642  * Slice a string up into argc/argv.
643  */
644 static int
645 makeargv(void)
646 {
647 	char	 *cp;
648 	char	**argp = margv;
649 	int	  ret = 0;
650 
651 	margc = 0;
652 	for (cp = line; *cp;) {
653 		if (margc >= MAXARGV) {
654 			printf("too many arguments\n");
655 			ret = 1;
656 			break;
657 		}
658 		while (isspace(*cp))
659 			cp++;
660 		if (*cp == '\0')
661 			break;
662 		*argp++ = cp;
663 		margc += 1;
664 		while (*cp != '\0' && !isspace(*cp))
665 			cp++;
666 		if (*cp == '\0')
667 			break;
668 		*cp++ = '\0';
669 	}
670 	*argp++ = 0;
671 
672 	return (ret);
673 }
674 
675 /* ARGSUSED */
676 void
677 quit(int argc, char *argv[])
678 {
679 	exit(0);
680 }
681 
682 /*
683  * Help command.
684  */
685 void
686 help(int argc, char *argv[])
687 {
688 	struct cmd	*c;
689 
690 	if (argc == 1) {
691 		printf("Commands may be abbreviated.  Commands are:\n\n");
692 		for (c = cmdtab; c->name != NULL; c++)
693 			printf("%-*s\t%s\n", (int)HELPINDENT, c->name, c->help);
694 		return;
695 	}
696 	while (--argc > 0) {
697 		char *arg;
698 		arg = *++argv;
699 		c = getcmd(arg);
700 		if (c == (struct cmd *) - 1)
701 			printf("?Ambiguous help command %s\n", arg);
702 		else if (c == (struct cmd *)0)
703 			printf("?Invalid help command %s\n", arg);
704 		else
705 			printf("%s\n", c->help);
706 	}
707 }
708 
709 /* ARGSUSED */
710 void
711 settrace(int argc, char *argv[])
712 {
713 	trace = !trace;
714 	printf("Packet tracing %s.\n", trace ? "on" : "off");
715 }
716 
717 /* ARGSUSED */
718 void
719 setverbose(int argc, char *argv[])
720 {
721 	verbose = !verbose;
722 	printf("Verbose mode %s.\n", verbose ? "on" : "off");
723 }
724 
725 /* ARGSUSED */
726 void
727 settsize(int argc, char *argv[])
728 {
729 	opt_tsize = !opt_tsize;
730 	printf("Tsize option %s.\n", opt_tsize ? "on" : "off");
731 	if (opt_tsize)
732 		has_options++;
733 	else
734 		has_options--;
735 }
736 
737 /* ARGSUSED */
738 void
739 settout(int argc, char *argv[])
740 {
741 	opt_tout = !opt_tout;
742 	printf("Timeout option %s.\n", opt_tout ? "on" : "off");
743 	if (opt_tout)
744 		has_options++;
745 	else
746 		has_options--;
747 }
748 
749 void
750 setblksize(int argc, char *argv[])
751 {
752 	int		 t;
753 	const char	*errstr;
754 
755 	if (argc < 2) {
756 		strlcpy(line, "Blocksize ", sizeof(line));
757 		printf("(value) ");
758 		readcmd(&line[strlen(line)], LBUFLEN - strlen(line), stdin);
759 		if (makeargv())
760 			return;
761 		argc = margc;
762 		argv = margv;
763 	}
764 	if (argc != 2) {
765 		printf("usage: %s value\n", argv[0]);
766 		return;
767 	}
768 	t = strtonum(argv[1], SEGSIZE_MIN, SEGSIZE_MAX, &errstr);
769 	if (errstr)
770 		printf("%s: value is %s\n", argv[1], errstr);
771 	else {
772 		if (opt_blksize == 0)
773 			has_options++;
774 		opt_blksize = t;
775 	}
776 }
777 
778 int
779 readcmd(char *input, int len, FILE *stream)
780 {
781 	int		nfds;
782 	struct pollfd	pfd[1];
783 
784 	fflush(stdout);
785 
786 	pfd[0].fd = 0;
787 	pfd[0].events = POLLIN;
788 	nfds = poll(pfd, 1, INFTIM);
789 	if (nfds == -1) {
790 		if (intrflag) {
791 			intrflag = 0;
792 			putchar('\n');
793 			return (0);
794 		}
795 		exit(1);
796 	}
797 
798 	if (fgets(input, len, stream) == NULL) {
799 		if (feof(stdin))
800 			exit(0);
801 		else
802 			return (-1);
803 	}
804 
805 	return (1);
806 }
807