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