xref: /openbsd-src/usr.bin/tftp/main.c (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
1 /*	$OpenBSD: main.c,v 1.29 2007/05/11 01:47:48 ray 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.29 2007/05/11 01:47:48 ray 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 /* ARGSUSED */
303 void
304 setbinary(int argc, char *argv[])
305 {
306 	settftpmode("octet");
307 }
308 
309 /* ARGSUSED */
310 void
311 setascii(int argc, char *argv[])
312 {
313 	settftpmode("netascii");
314 }
315 
316 static void
317 settftpmode(char *newmode)
318 {
319 	strlcpy(mode, newmode, sizeof(mode));
320 	if (verbose)
321 		printf("mode set to %s\n", mode);
322 }
323 
324 /*
325  * Send file(s).
326  */
327 void
328 put(int argc, char *argv[])
329 {
330 	int	 fd;
331 	int	 n;
332 	char	*cp, *targ;
333 
334 	if (argc < 2) {
335 		strlcpy(line, "send ", sizeof(line));
336 		printf("(file) ");
337 		readcmd(&line[strlen(line)], LBUFLEN - strlen(line), stdin);
338 		if (makeargv())
339 			return;
340 		argc = margc;
341 		argv = margv;
342 	}
343 	if (argc < 2) {
344 		putusage(argv[0]);
345 		return;
346 	}
347 	targ = argv[argc - 1];
348 	if (strchr(argv[argc - 1], ':')) {
349 		struct hostent	*hp;
350 
351 		for (n = 1; n < argc - 1; n++)
352 			if (strchr(argv[n], ':')) {
353 				putusage(argv[0]);
354 				return;
355 			}
356 		cp = argv[argc - 1];
357 		targ = strchr(cp, ':');
358 		*targ++ = 0;
359 		hp = gethostbyname(cp);
360 		if (hp == NULL) {
361 			warnx("%s: %s", cp, hstrerror(h_errno));
362 			return;
363 		}
364 		bcopy(hp->h_addr, (caddr_t)&peeraddr.sin_addr, hp->h_length);
365 		peeraddr.sin_family = hp->h_addrtype;
366 		connected = 1;
367 		port = sp->s_port;
368 		strlcpy(hostname, hp->h_name, sizeof(hostname));
369 	}
370 	if (!connected) {
371 		printf("No target machine specified.\n");
372 		return;
373 	}
374 	if (argc < 4) {
375 		cp = argc == 2 ? tail(targ) : argv[1];
376 		fd = open(cp, O_RDONLY);
377 		if (fd < 0) {
378 			warn("open: %s", cp);
379 			return;
380 		}
381 		if (verbose)
382 			printf("putting %s to %s:%s [%s]\n",
383 			    cp, hostname, targ, mode);
384 		peeraddr.sin_port = port;
385 		sendfile(fd, targ, mode);
386 		return;
387 	}
388 
389 	/*
390 	 * this assumes the target is a directory on
391 	 * on a remote unix system.  hmmmm.
392 	 */
393 	for (n = 1; n < argc - 1; n++) {
394 		if (asprintf(&cp, "%s/%s", targ, tail(argv[n])) == -1)
395 			err(1, "asprintf");
396 		fd = open(argv[n], O_RDONLY);
397 		if (fd < 0) {
398 			warn("open: %s", argv[n]);
399 			free(cp);
400 			continue;
401 		}
402 		if (verbose)
403 			printf("putting %s to %s:%s [%s]\n",
404 			    argv[n], hostname, cp, mode);
405 		peeraddr.sin_port = port;
406 		sendfile(fd, cp, mode);
407 		free(cp);
408 	}
409 }
410 
411 static void
412 putusage(char *s)
413 {
414 	printf("usage: %s file [[host:]remotename]\n", s);
415 	printf("       %s file1 file2 ... fileN [[host:]remote-directory]\n",
416 	    s);
417 }
418 
419 /*
420  * Receive file(s).
421  */
422 void
423 get(int argc, char *argv[])
424 {
425 	int	 fd;
426 	int	 n;
427 	char	*cp;
428 	char	*src;
429 
430 	if (argc < 2) {
431 		strlcpy(line, "get ", sizeof(line));
432 		printf("(files) ");
433 		readcmd(&line[strlen(line)], LBUFLEN - strlen(line), stdin);
434 		if (makeargv())
435 			return;
436 		argc = margc;
437 		argv = margv;
438 	}
439 	if (argc < 2) {
440 		getusage(argv[0]);
441 		return;
442 	}
443 	if (!connected) {
444 		for (n = 1; n < argc; n++)
445 			if (strchr(argv[n], ':') == 0) {
446 				getusage(argv[0]);
447 				return;
448 			}
449 	}
450 	for (n = 1; n < argc; n++) {
451 		src = strchr(argv[n], ':');
452 		if (src == NULL)
453 			src = argv[n];
454 		else {
455 			struct hostent	*hp;
456 
457 			*src++ = 0;
458 			hp = gethostbyname(argv[n]);
459 			if (hp == NULL) {
460 				warnx("%s: %s", argv[n], hstrerror(h_errno));
461 				continue;
462 			}
463 			bcopy(hp->h_addr, (caddr_t)&peeraddr.sin_addr,
464 			    hp->h_length);
465 			peeraddr.sin_family = hp->h_addrtype;
466 			connected = 1;
467 			strlcpy(hostname, hp->h_name, sizeof(hostname));
468 		}
469 		if (argc < 4) {
470 			cp = argc == 3 ? argv[2] : tail(src);
471 			fd = creat(cp, 0644);
472 			if (fd < 0) {
473 				warn("create: %s", cp);
474 				return;
475 			}
476 			if (verbose)
477 				printf("getting from %s:%s to %s [%s]\n",
478 				    hostname, src, cp, mode);
479 			peeraddr.sin_port = port;
480 			recvfile(fd, src, mode);
481 			break;
482 		}
483 		cp = tail(src);	/* new .. jdg */
484 		fd = creat(cp, 0644);
485 		if (fd < 0) {
486 			warn("create: %s", cp);
487 			continue;
488 		}
489 		if (verbose)
490 			printf("getting from %s:%s to %s [%s]\n",
491 			    hostname, src, cp, mode);
492 		peeraddr.sin_port = port;
493 		recvfile(fd, src, mode);
494 	}
495 }
496 
497 static void
498 getusage(char *s)
499 {
500 	printf("usage: %s [host:]file [localname]\n", s);
501 	printf("       %s [host1:]file1 [host2:]file2 ... [hostN:]fileN\n", s);
502 }
503 
504 void
505 setrexmt(int argc, char *argv[])
506 {
507 	int		 t;
508 	const char	*errstr;
509 
510 	if (argc < 2) {
511 		strlcpy(line, "Rexmt-timeout ", sizeof(line));
512 		printf("(value) ");
513 		readcmd(&line[strlen(line)], LBUFLEN - strlen(line), stdin);
514 		if (makeargv())
515 			return;
516 		argc = margc;
517 		argv = margv;
518 	}
519 	if (argc != 2) {
520 		printf("usage: %s value\n", argv[0]);
521 		return;
522 	}
523 	t = strtonum(argv[1], TIMEOUT_MIN, TIMEOUT_MAX, &errstr);
524 	if (errstr)
525 		printf("%s: value is %s\n", argv[1], errstr);
526 	else
527 		rexmtval = t;
528 }
529 
530 void
531 settimeout(int argc, char *argv[])
532 {
533 	int		 t;
534 	const char	*errstr;
535 
536 	if (argc < 2) {
537 		strlcpy(line, "Maximum-timeout ", sizeof(line));
538 		printf("(value) ");
539 		readcmd(&line[strlen(line)], LBUFLEN - strlen(line), stdin);
540 		if (makeargv())
541 			return;
542 		argc = margc;
543 		argv = margv;
544 	}
545 	if (argc != 2) {
546 		printf("usage: %s value\n", argv[0]);
547 		return;
548 	}
549 	t = strtonum(argv[1], TIMEOUT_MIN, TIMEOUT_MAX, &errstr);
550 	if (errstr)
551 		printf("%s: value is %s\n", argv[1], errstr);
552 	else
553 		maxtimeout = t;
554 }
555 
556 /* ARGSUSED */
557 void
558 status(int argc, char *argv[])
559 {
560 	if (connected)
561 		printf("Connected to %s.\n", hostname);
562 	else
563 		printf("Not connected.\n");
564 	printf("Mode: %s Verbose: %s Tracing: %s\n",
565 	    mode, verbose ? "on" : "off", trace ? "on" : "off");
566 	printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n",
567 	    rexmtval, maxtimeout);
568 }
569 
570 /* ARGSUSED */
571 void
572 intr(int signo)
573 {
574 	intrflag = 1;
575 }
576 
577 char *
578 tail(char *filename)
579 {
580 	char	*s;
581 
582 	while (*filename) {
583 		s = strrchr(filename, '/');
584 		if (s == NULL)
585 			break;
586 		if (s[1])
587 			return (s + 1);
588 		*s = '\0';
589 	}
590 
591 	return (filename);
592 }
593 
594 /*
595  * Command parser.
596  */
597 static __dead void
598 command(void)
599 {
600 	struct cmd	*c;
601 
602 	for (;;) {
603 		printf("%s> ", prompt);
604 		if (readcmd(line, LBUFLEN, stdin) < 1)
605 			continue;
606 		if ((line[0] == 0) || (line[0] == '\n'))
607 			continue;
608 		if (makeargv())
609 			continue;
610 		if (margc == 0)
611 			continue;
612 		c = getcmd(margv[0]);
613 		if (c == (struct cmd *) - 1) {
614 			printf("?Ambiguous command\n");
615 			continue;
616 		}
617 		if (c == 0) {
618 			printf("?Invalid command\n");
619 			continue;
620 		}
621 		(*c->handler)(margc, margv);
622 	}
623 }
624 
625 struct cmd *
626 getcmd(char *name)
627 {
628 	char		*p, *q;
629 	struct cmd	*c, *found;
630 	int		 nmatches, longest;
631 
632 	longest = 0;
633 	nmatches = 0;
634 	found = 0;
635 	intrflag = 0;
636 	for (c = cmdtab; (p = c->name) != NULL; c++) {
637 		for (q = name; *q == *p++; q++)
638 			if (*q == 0)		/* exact match? */
639 				return (c);
640 		if (!*q) {			/* the name was a prefix */
641 			if (q - name > longest) {
642 				longest = q - name;
643 				nmatches = 1;
644 				found = c;
645 			} else if (q - name == longest)
646 				nmatches++;
647 		}
648 	}
649 	if (nmatches > 1)
650 		return ((struct cmd *) - 1);
651 
652 	return (found);
653 }
654 
655 /*
656  * Slice a string up into argc/argv.
657  */
658 static int
659 makeargv(void)
660 {
661 	char	 *cp;
662 	char	**argp = margv;
663 	int	  ret = 0;
664 
665 	margc = 0;
666 	for (cp = line; *cp;) {
667 		if (margc >= MAXARGV) {
668 			printf("too many arguments\n");
669 			ret = 1;
670 			break;
671 		}
672 		while (isspace(*cp))
673 			cp++;
674 		if (*cp == '\0')
675 			break;
676 		*argp++ = cp;
677 		margc += 1;
678 		while (*cp != '\0' && !isspace(*cp))
679 			cp++;
680 		if (*cp == '\0')
681 			break;
682 		*cp++ = '\0';
683 	}
684 	*argp++ = 0;
685 
686 	return (ret);
687 }
688 
689 /* ARGSUSED */
690 void
691 quit(int argc, char *argv[])
692 {
693 	exit(0);
694 }
695 
696 /*
697  * Help command.
698  */
699 void
700 help(int argc, char *argv[])
701 {
702 	struct cmd	*c;
703 
704 	if (argc == 1) {
705 		printf("Commands may be abbreviated.  Commands are:\n\n");
706 		for (c = cmdtab; c->name != NULL; c++)
707 			printf("%-*s\t%s\n", (int)HELPINDENT, c->name, c->help);
708 		return;
709 	}
710 	while (--argc > 0) {
711 		char *arg;
712 		arg = *++argv;
713 		c = getcmd(arg);
714 		if (c == (struct cmd *) - 1)
715 			printf("?Ambiguous help command %s\n", arg);
716 		else if (c == (struct cmd *)0)
717 			printf("?Invalid help command %s\n", arg);
718 		else
719 			printf("%s\n", c->help);
720 	}
721 }
722 
723 /* ARGSUSED */
724 void
725 settrace(int argc, char *argv[])
726 {
727 	trace = !trace;
728 	printf("Packet tracing %s.\n", trace ? "on" : "off");
729 }
730 
731 /* ARGSUSED */
732 void
733 setverbose(int argc, char *argv[])
734 {
735 	verbose = !verbose;
736 	printf("Verbose mode %s.\n", verbose ? "on" : "off");
737 }
738 
739 /* ARGSUSED */
740 void
741 settsize(int argc, char *argv[])
742 {
743 	opt_tsize = !opt_tsize;
744 	printf("Tsize option %s.\n", opt_tsize ? "on" : "off");
745 	if (opt_tsize)
746 		has_options++;
747 	else
748 		has_options--;
749 }
750 
751 /* ARGSUSED */
752 void
753 settout(int argc, char *argv[])
754 {
755 	opt_tout = !opt_tout;
756 	printf("Timeout option %s.\n", opt_tout ? "on" : "off");
757 	if (opt_tout)
758 		has_options++;
759 	else
760 		has_options--;
761 }
762 
763 void
764 setblksize(int argc, char *argv[])
765 {
766 	int		 t;
767 	const char	*errstr;
768 
769 	if (argc < 2) {
770 		strlcpy(line, "Blocksize ", sizeof(line));
771 		printf("(value) ");
772 		readcmd(&line[strlen(line)], LBUFLEN - strlen(line), stdin);
773 		if (makeargv())
774 			return;
775 		argc = margc;
776 		argv = margv;
777 	}
778 	if (argc != 2) {
779 		printf("usage: %s value\n", argv[0]);
780 		return;
781 	}
782 	t = strtonum(argv[1], SEGSIZE_MIN, SEGSIZE_MAX, &errstr);
783 	if (errstr)
784 		printf("%s: value is %s\n", argv[1], errstr);
785 	else {
786 		if (opt_blksize == 0)
787 			has_options++;
788 		opt_blksize = t;
789 	}
790 }
791 
792 int
793 readcmd(char *input, int len, FILE *stream)
794 {
795 	int		nfds;
796 	struct pollfd	pfd[1];
797 
798 	fflush(stdout);
799 
800 	pfd[0].fd = 0;
801 	pfd[0].events = POLLIN;
802 	nfds = poll(pfd, 1, INFTIM);
803 	if (nfds == -1) {
804 		if (intrflag) {
805 			intrflag = 0;
806 			putchar('\n');
807 			return (0);
808 		}
809 		exit(1);
810 	}
811 
812 	if (fgets(input, len, stream) == NULL) {
813 		if (feof(stdin))
814 			exit(0);
815 		else
816 			return (-1);
817 	}
818 
819 	return (1);
820 }
821