xref: /freebsd/contrib/ntp/ntpq/ntpq.c (revision 4b2eaea43fec8e8792be611dea204071a10b655a)
1 /*
2  * ntpq - query an NTP server using mode 6 commands
3  */
4 
5 #include <stdio.h>
6 
7 #include "ntpq.h"
8 #include "ntp_unixtime.h"
9 #include "ntp_calendar.h"
10 #include "ntp_io.h"
11 #include "ntp_select.h"
12 #include "ntp_stdlib.h"
13 
14 #include <ctype.h>
15 #include <signal.h>
16 #include <setjmp.h>
17 #include <netdb.h>
18 #ifdef SYS_WINNT
19 # include <io.h>
20 #else
21 #define closesocket close
22 #endif /* SYS_WINNT */
23 
24 #ifdef HAVE_LIBREADLINE
25 # include <readline/readline.h>
26 # include <readline/history.h>
27 #endif /* HAVE_LIBREADLINE */
28 
29 #ifdef SYS_VXWORKS
30 /* vxWorks needs mode flag -casey*/
31 #define open(name, flags)   open(name, flags, 0777)
32 #define SERVER_PORT_NUM     123
33 #endif
34 
35 /*
36  * Because we potentially understand a lot of commands we will run
37  * interactive if connected to a terminal.
38  */
39 int interactive = 0;		/* set to 1 when we should prompt */
40 const char *prompt = "ntpq> ";	/* prompt to ask him about */
41 
42 
43 /*
44  * Keyid used for authenticated requests.  Obtained on the fly.
45  */
46 u_long info_auth_keyid = NTP_MAXKEY;
47 
48 /*
49  * Type of key md5 or des
50  */
51 #define	KEY_TYPE_DES	3
52 #define	KEY_TYPE_MD5	4
53 
54 static	int info_auth_keytype = KEY_TYPE_MD5;	/* MD5 */
55 u_long	current_time;		/* needed by authkeys; not used */
56 
57 /*
58  * Flag which indicates we should always send authenticated requests
59  */
60 int always_auth = 0;
61 
62 /*
63  * Flag which indicates raw mode output.
64  */
65 int rawmode = 0;
66 
67 /*
68  * Packet version number we use
69  */
70 u_char pktversion = NTP_OLDVERSION + 1;
71 
72 /*
73  * Don't jump if no set jmp.
74  */
75 volatile int jump = 0;
76 
77 /*
78  * Format values
79  */
80 #define	PADDING	0
81 #define	TS	1	/* time stamp */
82 #define	FL	2	/* l_fp type value */
83 #define	FU	3	/* u_fp type value */
84 #define	FS	4	/* s_fp type value */
85 #define	UI	5	/* unsigned integer value */
86 #define	SI	6	/* signed integer value */
87 #define	HA	7	/* host address */
88 #define	NA	8	/* network address */
89 #define	ST	9	/* string value */
90 #define	RF	10	/* refid (sometimes string, sometimes not) */
91 #define	LP	11	/* leap (print in binary) */
92 #define	OC	12	/* integer, print in octal */
93 #define	MD	13	/* mode */
94 #define	AR	14	/* array of times */
95 #define FX	15	/* test flags */
96 #define	EOV	255	/* end of table */
97 
98 
99 /*
100  * System variable values.  The array can be indexed by
101  * the variable index to find the textual name.
102  */
103 struct ctl_var sys_var[] = {
104 	{ 0,		PADDING, "" },		/* 0 */
105 	{ CS_LEAP,	LP,	"leap" },	/* 1 */
106 	{ CS_STRATUM,	UI,	"stratum" },	/* 2 */
107 	{ CS_PRECISION,	SI,	"precision" },	/* 3 */
108 	{ CS_ROOTDELAY,	FS,	"rootdelay" },	/* 4 */
109 	{ CS_ROOTDISPERSION, FU, "rootdispersion" }, /* 5 */
110 	{ CS_REFID,	RF,	"refid" },	/* 6 */
111 	{ CS_REFTIME,	TS,	"reftime" },	/* 7 */
112 	{ CS_POLL,	UI,	"poll" },	/* 8 */
113 	{ CS_PEERID,	UI,	"peer" },	/* 9 */
114 	{ CS_STATE,	UI,	"state" },	/* 10 */
115 	{ CS_OFFSET,	FL,	"offset" },	/* 11 */
116 	{ CS_DRIFT,	FS,	"frequency" },	/* 12 */
117 	{ CS_JITTER,	FU,	"jitter" },	/* 13 */
118 	{ CS_CLOCK,	TS,	"clock" },	/* 14 */
119 	{ CS_PROCESSOR,	ST,	"processor" },	/* 15 */
120 	{ CS_SYSTEM,	ST,	"system" },	/* 16 */
121 	{ CS_VERSION,	ST,	"version" },	/* 17 */
122 	{ CS_STABIL,	FS,	"stability" },	/* 18 */
123 	{ CS_VARLIST,	ST,	"sys_var_list" }, /* 19 */
124 	{ 0,		EOV,	""	}
125 };
126 
127 
128 /*
129  * Peer variable list
130  */
131 struct ctl_var peer_var[] = {
132 	{ 0,		PADDING, "" },		/* 0 */
133 	{ CP_CONFIG,	UI,	"config" },	/* 1 */
134 	{ CP_AUTHENABLE, UI,	"authenable" },	/* 2 */
135 	{ CP_AUTHENTIC,	UI,	"authentic" },	/* 3 */
136 	{ CP_SRCADR,	HA,	"srcadr" },	/* 4 */
137 	{ CP_SRCPORT,	UI,	"srcport" },	/* 5 */
138 	{ CP_DSTADR,	NA,	"dstadr" },	/* 6 */
139 	{ CP_DSTPORT,	UI,	"dstport" },	/* 7 */
140 	{ CP_LEAP,	LP,	"leap" },	/* 8 */
141 	{ CP_HMODE,	MD,	"hmode" },	/* 9 */
142 	{ CP_STRATUM,	UI,	"stratum" },	/* 10 */
143 	{ CP_PPOLL,	UI,	"ppoll" },	/* 11 */
144 	{ CP_HPOLL,	UI,	"hpoll" },	/* 12 */
145 	{ CP_PRECISION,	SI,	"precision" },	/* 13 */
146 	{ CP_ROOTDELAY,	FS,	"rootdelay" },	/* 14 */
147 	{ CP_ROOTDISPERSION, FU, "rootdispersion" }, /* 15 */
148 	{ CP_REFID,	RF,	"refid" },	/* 16 */
149 	{ CP_REFTIME,	TS,	"reftime" },	/* 17 */
150 	{ CP_ORG,	TS,	"org" },	/* 18 */
151 	{ CP_REC,	TS,	"rec" },	/* 19 */
152 	{ CP_XMT,	TS,	"xmt" },	/* 20 */
153 	{ CP_REACH,	OC,	"reach" },	/* 21 */
154 	{ CP_VALID,	UI,	"valid" },	/* 22 */
155 	{ CP_TIMER,	UI,	"timer" },	/* 23 */
156 	{ CP_DELAY,	FS,	"delay" },	/* 24 */
157 	{ CP_OFFSET,	FL,	"offset" },	/* 25 */
158 	{ CP_JITTER,	FU,	"jitter" },	/* 26 */
159 	{ CP_DISPERSION, FU,	"dispersion" },	/* 27 */
160 	{ CP_KEYID,	UI,	"keyid" },	/* 28 */
161 	{ CP_FILTDELAY,	AR,	"filtdelay" },	/* 29 */
162 	{ CP_FILTOFFSET, AR,	"filtoffset" },	/* 30 */
163 	{ CP_PMODE,	ST,	"pmode" },	/* 31 */
164 	{ CP_RECEIVED,	UI,	"received" },	/* 32 */
165 	{ CP_SENT,	UI,	"sent" },	/* 33 */
166 	{ CP_FILTERROR,	AR,	"filtdisp" },	/* 34 */
167 	{ CP_FLASH,     FX,	"flash" },	/* 35 */
168 	{ CP_TTL,	UI,	"ttl" },	/* 36 */
169 	{ CP_TTLMAX,	UI,	"ttlmax" },	/* 37 */
170 	/*
171 	 * These are duplicate entries so that we can
172 	 * process deviant version of the ntp protocol.
173 	 */
174 	{ CP_SRCADR,	HA,	"peeraddr" },	/* 4 */
175 	{ CP_SRCPORT,	UI,	"peerport" },	/* 5 */
176 	{ CP_PPOLL,	UI,	"peerpoll" },	/* 11 */
177 	{ CP_HPOLL,	UI,	"hostpoll" },	/* 12 */
178 	{ CP_FILTERROR,	AR,	"filterror" },	/* 34 */
179 	{ 0,		EOV,	""	}
180 };
181 
182 
183 /*
184  * Clock variable list
185  */
186 struct ctl_var clock_var[] = {
187 	{ 0,		PADDING, "" },		/* 0 */
188 	{ CC_TYPE,	UI,	"type" },	/* 1 */
189 	{ CC_TIMECODE,	ST,	"timecode" },	/* 2 */
190 	{ CC_POLL,	UI,	"poll" },	/* 3 */
191 	{ CC_NOREPLY,	UI,	"noreply" },	/* 4 */
192 	{ CC_BADFORMAT,	UI,	"badformat" },	/* 5 */
193 	{ CC_BADDATA,	UI,	"baddata" },	/* 6 */
194 	{ CC_FUDGETIME1, FL,	"fudgetime1" },	/* 7 */
195 	{ CC_FUDGETIME2, FL,	"fudgetime2" },	/* 8 */
196 	{ CC_FUDGEVAL1,	UI,	"stratum" },	/* 9 */
197 	{ CC_FUDGEVAL2,	RF,	"refid" },	/* 10 */
198 	{ CC_FLAGS,	UI,	"flags" },	/* 11 */
199 	{ CC_DEVICE,	ST,	"device" },	/* 12 */
200 	{ 0,		EOV,	""	}
201 };
202 
203 
204 /*
205  * flasher bits
206  */
207 static const char *tstflagnames[] = {
208 	"dup_pkt",		/* TEST1 */
209 	"bogus_pkt",		/* TEST2 */
210 	"proto_unsync",		/* TEST3 */
211 	"no_access",		/* TEST4 */
212 	"bad_auth",			/* TEST5 */
213 	"peer_unsync",		/* TEST6 */
214 	"peer_stratum",		/* TEST7 */
215 	"root_bounds",		/* TEST8 */
216 	"peer_bounds",		/* TEST9 */
217 	"bad_autokey",		/* TEST10 */
218 	"not_proventic"		/* TEST11*/
219 };
220 
221 
222 int		ntpqmain	P((int,	char **));
223 /*
224  * Built in command handler declarations
225  */
226 static	int	openhost	P((const char *));
227 static	int	sendpkt		P((char *, int));
228 static	int	getresponse	P((int, int, u_short *, int *, char **, int));
229 static	int	sendrequest	P((int, int, int, int, char *));
230 static	char *	tstflags	P((u_long));
231 static	void	getcmds		P((void));
232 static	RETSIGTYPE abortcmd	P((int));
233 static	void	docmd		P((const char *));
234 static	void	tokenize	P((const char *, char **, int *));
235 static	int	findcmd		P((char *, struct xcmd *, struct xcmd *, struct xcmd **));
236 static	int	getarg		P((char *, int, arg_v *));
237 static	int	rtdatetolfp	P((char *, l_fp *));
238 static	int	decodearr	P((char *, int *, l_fp *));
239 static	void	help		P((struct parse *, FILE *));
240 #ifdef QSORT_USES_VOID_P
241 static	int	helpsort	P((const void *, const void *));
242 #else
243 static	int	helpsort	P((char **, char **));
244 #endif
245 static	void	printusage	P((struct xcmd *, FILE *));
246 static	void	timeout		P((struct parse *, FILE *));
247 static	void	auth_delay	P((struct parse *, FILE *));
248 static	void	host		P((struct parse *, FILE *));
249 static	void	ntp_poll	P((struct parse *, FILE *));
250 static	void	keyid		P((struct parse *, FILE *));
251 static	void	keytype		P((struct parse *, FILE *));
252 static	void	passwd		P((struct parse *, FILE *));
253 static	void	hostnames	P((struct parse *, FILE *));
254 static	void	setdebug	P((struct parse *, FILE *));
255 static	void	quit		P((struct parse *, FILE *));
256 static	void	version		P((struct parse *, FILE *));
257 static	void	raw		P((struct parse *, FILE *));
258 static	void	cooked		P((struct parse *, FILE *));
259 static	void	authenticate	P((struct parse *, FILE *));
260 static	void	ntpversion	P((struct parse *, FILE *));
261 static	void	warning		P((const char *, const char *, const char *));
262 static	void	error		P((const char *, const char *, const char *));
263 static	u_long	getkeyid	P((const char *));
264 static	void	atoascii	P((int, char *, char *));
265 static	void	makeascii	P((int, char *, FILE *));
266 static	void	rawprint	P((int, int, char *, int, FILE *));
267 static	void	startoutput	P((void));
268 static	void	output		P((FILE *, char *, char *));
269 static	void	endoutput	P((FILE *));
270 static	void	outputarr	P((FILE *, char *, int, l_fp *));
271 static	void	cookedprint	P((int, int, char *, int, FILE *));
272 #ifdef QSORT_USES_VOID_P
273 static	int	assoccmp	P((const void *, const void *));
274 #else
275 static	int	assoccmp	P((struct association *, struct association *));
276 #endif /* sgi || bsdi */
277 
278 
279 /*
280  * Built-in commands we understand
281  */
282 struct xcmd builtins[] = {
283 	{ "?",		help,		{  OPT|STR, NO, NO, NO },
284 	  { "command", "", "", "" },
285 	  "tell the use and syntax of commands" },
286 	{ "help",	help,		{  OPT|STR, NO, NO, NO },
287 	  { "command", "", "", "" },
288 	  "tell the use and syntax of commands" },
289 	{ "timeout",	timeout,	{ OPT|UINT, NO, NO, NO },
290 	  { "msec", "", "", "" },
291 	  "set the primary receive time out" },
292 	{ "delay",	auth_delay,	{ OPT|INT, NO, NO, NO },
293 	  { "msec", "", "", "" },
294 	  "set the delay added to encryption time stamps" },
295 	{ "host",	host,		{ OPT|STR, NO, NO, NO },
296 	  { "hostname", "", "", "" },
297 	  "specify the host whose NTP server we talk to" },
298 	{ "poll",	ntp_poll,	{ OPT|UINT, OPT|STR, NO, NO },
299 	  { "n", "verbose", "", "" },
300 	  "poll an NTP server in client mode `n' times" },
301 	{ "passwd",	passwd,		{ NO, NO, NO, NO },
302 	  { "", "", "", "" },
303 	  "specify a password to use for authenticated requests"},
304 	{ "hostnames",	hostnames,	{ OPT|STR, NO, NO, NO },
305 	  { "yes|no", "", "", "" },
306 	  "specify whether hostnames or net numbers are printed"},
307 	{ "debug",	setdebug,	{ OPT|STR, NO, NO, NO },
308 	  { "no|more|less", "", "", "" },
309 	  "set/change debugging level" },
310 	{ "quit",	quit,		{ NO, NO, NO, NO },
311 	  { "", "", "", "" },
312 	  "exit ntpq" },
313 	{ "exit",	quit,		{ NO, NO, NO, NO },
314 	  { "", "", "", "" },
315 	  "exit ntpq" },
316 	{ "keyid",	keyid,		{ OPT|UINT, NO, NO, NO },
317 	  { "key#", "", "", "" },
318 	  "set keyid to use for authenticated requests" },
319 	{ "version",	version,	{ NO, NO, NO, NO },
320 	  { "", "", "", "" },
321 	  "print version number" },
322 	{ "raw",	raw,		{ NO, NO, NO, NO },
323 	  { "", "", "", "" },
324 	  "do raw mode variable output" },
325 	{ "cooked",	cooked,		{ NO, NO, NO, NO },
326 	  { "", "", "", "" },
327 	  "do cooked mode variable output" },
328 	{ "authenticate", authenticate,	{ OPT|STR, NO, NO, NO },
329 	  { "yes|no", "", "", "" },
330 	  "always authenticate requests to this server" },
331 	{ "ntpversion",	ntpversion,	{ OPT|UINT, NO, NO, NO },
332 	  { "version number", "", "", "" },
333 	  "set the NTP version number to use for requests" },
334 	{ "keytype",	keytype,	{ OPT|STR, NO, NO, NO },
335 	  { "key type (md5|des)", "", "", "" },
336 	  "set key type to use for authenticated requests (des|md5)" },
337 	{ 0,		0,		{ NO, NO, NO, NO },
338 	  { "", "", "", "" }, "" }
339 };
340 
341 
342 /*
343  * Default values we use.
344  */
345 #define	DEFTIMEOUT	(5)		/* 5 second time out */
346 #define	DEFSTIMEOUT	(2)		/* 2 second time out after first */
347 #define	DEFDELAY	0x51EB852	/* 20 milliseconds, l_fp fraction */
348 #define	DEFHOST		"127.0.0.1"	/* default host name */
349 #define	LENHOSTNAME	256		/* host name is 256 characters long */
350 #define	MAXCMDS		100		/* maximum commands on cmd line */
351 #define	MAXHOSTS	200		/* maximum hosts on cmd line */
352 #define	MAXLINE		512		/* maximum line length */
353 #define	MAXTOKENS	(1+MAXARGS+2)	/* maximum number of usable tokens */
354 #define	MAXVARLEN	256		/* maximum length of a variable name */
355 #define	MAXVALLEN	400		/* maximum length of a variable value */
356 #define	MAXOUTLINE	72		/* maximum length of an output line */
357 
358 /*
359  * Some variables used and manipulated locally
360  */
361 struct timeval tvout = { DEFTIMEOUT, 0 };	/* time out for reads */
362 struct timeval tvsout = { DEFSTIMEOUT, 0 };	/* secondary time out */
363 l_fp delay_time;				/* delay time */
364 char currenthost[LENHOSTNAME];			/* current host name */
365 struct sockaddr_in hostaddr = { 0 };		/* host address */
366 int showhostnames = 1;				/* show host names by default */
367 
368 int sockfd;					/* fd socket is opened on */
369 int havehost = 0;				/* set to 1 when host open */
370 struct servent *server_entry = NULL;		/* server entry for ntp */
371 
372 #ifdef SYS_WINNT
373 WORD wVersionRequested;
374 WSADATA wsaData;
375 DWORD NumberOfBytesWritten;
376 
377 HANDLE	TimerThreadHandle = NULL;	/* 1998/06/03 - Used in ntplib/machines.c */
378 void timer(void)	{  ; };	/* 1998/06/03 - Used in ntplib/machines.c */
379 
380 #endif /* SYS_WINNT */
381 
382 /*
383  * Sequence number used for requests.  It is incremented before
384  * it is used.
385  */
386 u_short sequence;
387 
388 /*
389  * Holds data returned from queries.  Declare buffer long to be sure of
390  * alignment.
391  */
392 #define	MAXFRAGS	24		/* maximum number of fragments */
393 #define	DATASIZE	(MAXFRAGS*480)	/* maximum amount of data */
394 long pktdata[DATASIZE/sizeof(long)];
395 
396 /*
397  * Holds association data for use with the &n operator.
398  */
399 struct association assoc_cache[MAXASSOC];
400 int numassoc = 0;		/* number of cached associations */
401 
402 /*
403  * For commands typed on the command line (with the -c option)
404  */
405 int numcmds = 0;
406 const char *ccmds[MAXCMDS];
407 #define	ADDCMD(cp)	if (numcmds < MAXCMDS) ccmds[numcmds++] = (cp)
408 
409 /*
410  * When multiple hosts are specified.
411  */
412 int numhosts = 0;
413 const char *chosts[MAXHOSTS];
414 #define	ADDHOST(cp)	if (numhosts < MAXHOSTS) chosts[numhosts++] = (cp)
415 
416 /*
417  * Error codes for internal use
418  */
419 #define	ERR_UNSPEC		256
420 #define	ERR_INCOMPLETE	257
421 #define	ERR_TIMEOUT		258
422 #define	ERR_TOOMUCH		259
423 
424 /*
425  * Macro definitions we use
426  */
427 #define	ISSPACE(c)	((c) == ' ' || (c) == '\t')
428 #define	ISEOL(c)	((c) == '\n' || (c) == '\r' || (c) == '\0')
429 #define	STREQ(a, b)	(*(a) == *(b) && strcmp((a), (b)) == 0)
430 
431 /*
432  * Jump buffer for longjumping back to the command level
433  */
434 jmp_buf interrupt_buf;
435 
436 /*
437  * Points at file being currently printed into
438  */
439 FILE *current_output;
440 
441 /*
442  * Command table imported from ntpdc_ops.c
443  */
444 extern struct xcmd opcmds[];
445 
446 char *progname;
447 volatile int debug;
448 
449 #ifdef NO_MAIN_ALLOWED
450 CALL(ntpq,"ntpq",ntpqmain);
451 
452 void clear_globals(void)
453 {
454     extern int ntp_optind;
455     extern char *ntp_optarg;
456     showhostnames = 0;				/* don'tshow host names by default */
457     ntp_optind = 0;
458     ntp_optarg = 0;
459     server_entry = NULL;            /* server entry for ntp */
460     havehost = 0;				/* set to 1 when host open */
461     numassoc = 0;		/* number of cached associations */
462     numcmds = 0;
463     numhosts = 0;
464 }
465 #endif
466 
467 /*
468  * main - parse arguments and handle options
469  */
470 #ifndef NO_MAIN_ALLOWED
471 int
472 main(
473 	int argc,
474 	char *argv[]
475 	)
476 {
477 	return ntpqmain(argc, argv);
478 }
479 #endif
480 
481 int
482 ntpqmain(
483 	int argc,
484 	char *argv[]
485 	)
486 {
487 	int c;
488 	int errflg = 0;
489 	extern int ntp_optind;
490 	extern char *ntp_optarg;
491 
492 #ifdef NO_MAIN_ALLOWED
493     clear_globals();
494     taskPrioritySet(taskIdSelf(), 100 );
495 #endif
496 	delay_time.l_ui = 0;
497 	delay_time.l_uf = DEFDELAY;
498 
499 	progname = argv[0];
500 	while ((c = ntp_getopt(argc, argv, "c:dinp")) != EOF)
501 	    switch (c) {
502 		case 'c':
503 		    ADDCMD(ntp_optarg);
504 		    break;
505 		case 'd':
506 		    ++debug;
507 		    break;
508 		case 'i':
509 		    interactive = 1;
510 		    break;
511 		case 'n':
512 		    showhostnames = 0;
513 		    break;
514 		case 'p':
515 		    ADDCMD("peers");
516 		    break;
517 		default:
518 		    errflg++;
519 		    break;
520 	    }
521 	if (errflg) {
522 		(void) fprintf(stderr,
523 			       "usage: %s [-dinp] [-c cmd] host ...\n",
524 			       progname);
525 		exit(2);
526 	}
527 	if (ntp_optind == argc) {
528 		ADDHOST(DEFHOST);
529 	} else {
530 		for (; ntp_optind < argc; ntp_optind++)
531 		    ADDHOST(argv[ntp_optind]);
532 	}
533 
534 	if (numcmds == 0 && interactive == 0
535 	    && isatty(fileno(stdin)) && isatty(fileno(stderr))) {
536 		interactive = 1;
537 	}
538 
539 #ifndef SYS_WINNT /* Under NT cannot handle SIGINT, WIN32 spawns a handler */
540 	if (interactive)
541 	    (void) signal_no_reset(SIGINT, abortcmd);
542 #endif /* SYS_WINNT */
543 
544 #ifdef SYS_WINNT
545 	wVersionRequested = MAKEWORD(1,1);
546 	if (WSAStartup(wVersionRequested, &wsaData)) {
547 		fprintf(stderr, "No useable winsock.dll");
548 		exit(1);
549 	}
550 #endif /* SYS_WINNT */
551 
552 	if (numcmds == 0) {
553 		(void) openhost(chosts[0]);
554 		getcmds();
555 	} else {
556 		int ihost;
557 		int icmd;
558 
559 		for (ihost = 0; ihost < numhosts; ihost++) {
560 			if (openhost(chosts[ihost]))
561 			    for (icmd = 0; icmd < numcmds; icmd++)
562 				docmd(ccmds[icmd]);
563 		}
564 	}
565 #ifdef SYS_WINNT
566 	WSACleanup();
567 #endif /* SYS_WINNT */
568 	return 0;
569 }
570 
571 
572 /*
573  * openhost - open a socket to a host
574  */
575 static int
576 openhost(
577 	const char *hname
578 	)
579 {
580 	u_int32 netnum;
581 	char temphost[LENHOSTNAME];
582 
583 	if (server_entry == NULL) {
584 		server_entry = getservbyname("ntp", "udp");
585 		if (server_entry == NULL) {
586 #ifdef VMS /* UCX getservbyname() doesn't work [yet], but we do know better */
587 			server_entry = (struct servent *)
588 				malloc(sizeof(struct servent));
589 			server_entry->s_port = htons(NTP_PORT);
590 #else
591 			(void) fprintf(stderr, "%s: ntp/udp: unknown service\n",
592 				       progname);
593 			exit(1);
594 #endif /* VMS & UCX */
595 		}
596 		if (debug > 2)
597 		    printf("Got ntp/udp service entry\n");
598 	}
599 
600 	if (!getnetnum(hname, &netnum, temphost))
601 	    return 0;
602 
603 	if (debug > 2)
604 	    printf("Opening host %s\n", temphost);
605 
606 	if (havehost == 1) {
607 		if (debug > 2)
608 		    printf("Closing old host %s\n", currenthost);
609 		(void) closesocket(sockfd);
610 		havehost = 0;
611 	}
612 	(void) strcpy(currenthost, temphost);
613 
614 	hostaddr.sin_family = AF_INET;
615 #ifndef SYS_VXWORKS
616 	hostaddr.sin_port = server_entry->s_port;
617 #else
618     hostaddr.sin_port = htons(SERVER_PORT_NUM);
619 #endif
620 	hostaddr.sin_addr.s_addr = netnum;
621 
622 #ifdef SYS_WINNT
623 	{
624 		int optionValue = SO_SYNCHRONOUS_NONALERT;
625 		int err;
626 		err = setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, (char *)&optionValue, sizeof(optionValue));
627 		if (err != NO_ERROR) {
628 			(void) fprintf(stderr, "cannot open nonoverlapped sockets\n");
629 			exit(1);
630 		}
631 	}
632 
633 
634 	sockfd = socket(AF_INET, SOCK_DGRAM, 0);
635 	if (sockfd == INVALID_SOCKET) {
636 		error("socket", "", "");
637 		exit(-1);
638 	}
639 #else
640 	sockfd = socket(AF_INET, SOCK_DGRAM, 0);
641 	if (sockfd == -1)
642 	    error("socket", "", "");
643 #endif /* SYS_WINNT */
644 
645 
646 #ifdef NEED_RCVBUF_SLOP
647 # ifdef SO_RCVBUF
648 	{ int rbufsize = DATASIZE + 2048;	/* 2K for slop */
649 	if (setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF,
650 		       &rbufsize, sizeof(int)) == -1)
651 	    error("setsockopt", "", "");
652 	}
653 # endif
654 #endif
655 
656 	if (connect(sockfd, (struct sockaddr *)&hostaddr,
657 		    sizeof(hostaddr)) == -1)
658 	    error("connect", "", "");
659 
660 	havehost = 1;
661 	return 1;
662 }
663 
664 
665 /* XXX ELIMINATE sendpkt similar in ntpq.c, ntpdc.c, ntp_io.c, ntptrace.c */
666 /*
667  * sendpkt - send a packet to the remote host
668  */
669 static int
670 sendpkt(
671 	char *xdata,
672 	int xdatalen
673 	)
674 {
675 	if (debug >= 3)
676 	    printf("Sending %d octets\n", xdatalen);
677 
678 
679 	if (send(sockfd, xdata, (size_t)xdatalen, 0) == -1) {
680 		warning("write to %s failed", currenthost, "");
681 		return -1;
682 	}
683 
684 	if (debug >= 4) {
685 		int first = 8;
686 		printf("Packet data:\n");
687 		while (xdatalen-- > 0) {
688 			if (first-- == 0) {
689 				printf("\n");
690 				first = 7;
691 			}
692 			printf(" %02x", *xdata++ & 0xff);
693 		}
694 		printf("\n");
695 	}
696 	return 0;
697 }
698 
699 
700 
701 /*
702  * getresponse - get a (series of) response packet(s) and return the data
703  */
704 static int
705 getresponse(
706 	int opcode,
707 	int associd,
708 	u_short *rstatus,
709 	int *rsize,
710 	char **rdata,
711 	int timeo
712 	)
713 {
714 	struct ntp_control rpkt;
715 	struct timeval tvo;
716 	u_short offsets[MAXFRAGS+1];
717 	u_short counts[MAXFRAGS+1];
718 	u_short offset;
719 	u_short count;
720 	int numfrags;
721 	int seenlastfrag;
722 	fd_set fds;
723 	int n;
724 
725 	/*
726 	 * This is pretty tricky.  We may get between 1 and MAXFRAG packets
727 	 * back in response to the request.  We peel the data out of
728 	 * each packet and collect it in one long block.  When the last
729 	 * packet in the sequence is received we'll know how much data we
730 	 * should have had.  Note we use one long time out, should reconsider.
731 	 */
732 	*rsize = 0;
733 	if (rstatus)
734 	    *rstatus = 0;
735 	*rdata = (char *)pktdata;
736 
737 	numfrags = 0;
738 	seenlastfrag = 0;
739 
740 	FD_ZERO(&fds);
741 
742     again:
743 	if (numfrags == 0)
744 	    tvo = tvout;
745 	else
746 	    tvo = tvsout;
747 
748 	FD_SET(sockfd, &fds);
749 	n = select(sockfd+1, &fds, (fd_set *)0, (fd_set *)0, &tvo);
750 
751 #if 0
752 	if (debug >= 1)
753 	    printf("select() returns %d\n", n);
754 #endif
755 
756 	if (n == -1) {
757 		warning("select fails", "", "");
758 		return -1;
759 	}
760 	if (n == 0) {
761 		/*
762 		 * Timed out.  Return what we have
763 		 */
764 		if (numfrags == 0) {
765 			if (timeo)
766 			    (void) fprintf(stderr,
767 					   "%s: timed out, nothing received\n",
768 					   currenthost);
769 			return ERR_TIMEOUT;
770 		} else {
771 			if (timeo)
772 			    (void) fprintf(stderr,
773 					   "%s: timed out with incomplete data\n",
774 					   currenthost);
775 			if (debug) {
776 				printf("Received fragments:\n");
777 				for (n = 0; n < numfrags; n++)
778 				    printf("%4d %d\n", offsets[n],
779 					   counts[n]);
780 				if (seenlastfrag)
781 				    printf("last fragment received\n");
782 				else
783 				    printf("last fragment not received\n");
784 			}
785 			return ERR_INCOMPLETE;
786 		}
787 	}
788 
789 	n = recv(sockfd, (char *)&rpkt, sizeof(rpkt), 0);
790 	if (n == -1) {
791 		warning("read", "", "");
792 		return -1;
793 	}
794 
795 	if (debug >= 4) {
796 		int len = n, first = 8;
797 		char *data = (char *)&rpkt;
798 
799 		printf("Packet data:\n");
800 		while (len-- > 0) {
801 			if (first-- == 0) {
802 				printf("\n");
803 				first = 7;
804 			}
805 			printf(" %02x", *data++ & 0xff);
806 		}
807 		printf("\n");
808 	}
809 
810 	/*
811 	 * Check for format errors.  Bug proofing.
812 	 */
813 	if (n < CTL_HEADER_LEN) {
814 		if (debug)
815 		    printf("Short (%d byte) packet received\n", n);
816 		goto again;
817 	}
818 	if (PKT_VERSION(rpkt.li_vn_mode) > NTP_VERSION
819 	    || PKT_VERSION(rpkt.li_vn_mode) < NTP_OLDVERSION) {
820 		if (debug)
821 		    printf("Packet received with version %d\n",
822 			   PKT_VERSION(rpkt.li_vn_mode));
823 		goto again;
824 	}
825 	if (PKT_MODE(rpkt.li_vn_mode) != MODE_CONTROL) {
826 		if (debug)
827 		    printf("Packet received with mode %d\n",
828 			   PKT_MODE(rpkt.li_vn_mode));
829 		goto again;
830 	}
831 	if (!CTL_ISRESPONSE(rpkt.r_m_e_op)) {
832 		if (debug)
833 		    printf("Received request packet, wanted response\n");
834 		goto again;
835 	}
836 
837 	/*
838 	 * Check opcode and sequence number for a match.
839 	 * Could be old data getting to us.
840 	 */
841 	if (ntohs(rpkt.sequence) != sequence) {
842 		if (debug)
843 		    printf(
844 			    "Received sequnce number %d, wanted %d\n",
845 			    ntohs(rpkt.sequence), sequence);
846 		goto again;
847 	}
848 	if (CTL_OP(rpkt.r_m_e_op) != opcode) {
849 		if (debug)
850 		    printf(
851 			    "Received opcode %d, wanted %d (sequence number okay)\n",
852 			    CTL_OP(rpkt.r_m_e_op), opcode);
853 		goto again;
854 	}
855 
856 	/*
857 	 * Check the error code.  If non-zero, return it.
858 	 */
859 	if (CTL_ISERROR(rpkt.r_m_e_op)) {
860 		int errcode;
861 
862 		errcode = (ntohs(rpkt.status) >> 8) & 0xff;
863 		if (debug && CTL_ISMORE(rpkt.r_m_e_op)) {
864 			printf("Error code %d received on not-final packet\n",
865 			       errcode);
866 		}
867 		if (errcode == CERR_UNSPEC)
868 		    return ERR_UNSPEC;
869 		return errcode;
870 	}
871 
872 	/*
873 	 * Check the association ID to make sure it matches what
874 	 * we sent.
875 	 */
876 	if (ntohs(rpkt.associd) != associd) {
877 		if (debug)
878 		    printf("Association ID %d doesn't match expected %d\n",
879 			   ntohs(rpkt.associd), associd);
880 		/*
881 		 * Hack for silly fuzzballs which, at the time of writing,
882 		 * return an assID of sys.peer when queried for system variables.
883 		 */
884 #ifdef notdef
885 		goto again;
886 #endif
887 	}
888 
889 	/*
890 	 * Collect offset and count.  Make sure they make sense.
891 	 */
892 	offset = ntohs(rpkt.offset);
893 	count = ntohs(rpkt.count);
894 
895 	if (debug >= 3) {
896 		int shouldbesize;
897 		u_long key;
898 		u_long *lpkt;
899 		int maclen;
900 
901 		/*
902 		 * Usually we ignore authentication, but for debugging purposes
903 		 * we watch it here.
904 		 */
905 		shouldbesize = CTL_HEADER_LEN + count;
906 
907 		/* round to 8 octet boundary */
908 		shouldbesize = (shouldbesize + 7) & ~7;
909 
910 		if (n & 0x3) {
911 			printf("Packet not padded, size = %d\n", n);
912 		} if ((maclen = n - shouldbesize) >= MIN_MAC_LEN) {
913 			printf(
914 				"Packet shows signs of authentication (total %d, data %d, mac %d)\n",
915 				n, shouldbesize, maclen);
916 			lpkt = (u_long *)&rpkt;
917 			printf("%08lx %08lx %08lx %08lx %08lx %08lx\n",
918 			       (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_long) - 3]),
919 			       (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_long) - 2]),
920 			       (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_long) - 1]),
921 			       (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_long)]),
922 			       (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_long) + 1]),
923 			       (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_long) + 2]));
924 			key = ntohl(lpkt[(n - maclen) / sizeof(u_long)]);
925 			printf("Authenticated with keyid %lu\n", (u_long)key);
926 			if (key != 0 && key != info_auth_keyid) {
927 				printf("We don't know that key\n");
928 			} else {
929 				if (authdecrypt(key, (u_int32 *)&rpkt,
930 				    n - maclen, maclen)) {
931 					printf("Auth okay!\n");
932 				} else {
933 					printf("Auth failed!\n");
934 				}
935 			}
936 		}
937 	}
938 
939 	if (debug >= 2)
940 	    printf("Got packet, size = %d\n", n);
941 	if (count > (u_short)(n-CTL_HEADER_LEN)) {
942 		if (debug)
943 		    printf(
944 			    "Received count of %d octets, data in packet is %d\n",
945 			    count, n-CTL_HEADER_LEN);
946 		goto again;
947 	}
948 	if (count == 0 && CTL_ISMORE(rpkt.r_m_e_op)) {
949 		if (debug)
950 		    printf("Received count of 0 in non-final fragment\n");
951 		goto again;
952 	}
953 	if (offset + count > sizeof(pktdata)) {
954 		if (debug)
955 		    printf("Offset %d, count %d, too big for buffer\n",
956 			   offset, count);
957 		return ERR_TOOMUCH;
958 	}
959 	if (seenlastfrag && !CTL_ISMORE(rpkt.r_m_e_op)) {
960 		if (debug)
961 		    printf("Received second last fragment packet\n");
962 		goto again;
963 	}
964 
965 	/*
966 	 * So far, so good.  Record this fragment, making sure it doesn't
967 	 * overlap anything.
968 	 */
969 	if (debug >= 2)
970 	    printf("Packet okay\n");;
971 
972 	if (numfrags == MAXFRAGS) {
973 		if (debug)
974 		    printf("Number of fragments exceeds maximum\n");
975 		return ERR_TOOMUCH;
976 	}
977 
978 	for (n = 0; n < numfrags; n++) {
979 		if (offset == offsets[n])
980 		    goto again;	/* duplicate */
981 		if (offset < offsets[n])
982 		    break;
983 	}
984 
985 	if ((u_short)(n > 0 && offsets[n-1] + counts[n-1]) > offset)
986 	    goto overlap;
987 	if (n < numfrags && (u_short)(offset + count) > offsets[n])
988 	    goto overlap;
989 
990 	{
991 		register int i;
992 
993 		for (i = numfrags; i > n; i--) {
994 			offsets[i] = offsets[i-1];
995 			counts[i] = counts[i-1];
996 		}
997 	}
998 	offsets[n] = offset;
999 	counts[n] = count;
1000 	numfrags++;
1001 
1002 	/*
1003 	 * Got that stuffed in right.  Figure out if this was the last.
1004 	 * Record status info out of the last packet.
1005 	 */
1006 	if (!CTL_ISMORE(rpkt.r_m_e_op)) {
1007 		seenlastfrag = 1;
1008 		if (rstatus != 0)
1009 		    *rstatus = ntohs(rpkt.status);
1010 	}
1011 
1012 	/*
1013 	 * Copy the data into the data buffer.
1014 	 */
1015 	memmove((char *)pktdata + offset, (char *)rpkt.data, count);
1016 
1017 	/*
1018 	 * If we've seen the last fragment, look for holes in the sequence.
1019 	 * If there aren't any, we're done.
1020 	 */
1021 	if (seenlastfrag && offsets[0] == 0) {
1022 		for (n = 1; n < numfrags; n++) {
1023 			if (offsets[n-1] + counts[n-1] != offsets[n])
1024 			    break;
1025 		}
1026 		if (n == numfrags) {
1027 			*rsize = offsets[numfrags-1] + counts[numfrags-1];
1028 			return 0;
1029 		}
1030 	}
1031 	goto again;
1032 
1033     overlap:
1034 	/*
1035 	 * Print debugging message about overlapping fragments
1036 	 */
1037 	if (debug)
1038 	    printf("Overlapping fragments returned in response\n");
1039 	goto again;
1040 }
1041 
1042 
1043 /*
1044  * sendrequest - format and send a request packet
1045  */
1046 static int
1047 sendrequest(
1048 	int opcode,
1049 	int associd,
1050 	int auth,
1051 	int qsize,
1052 	char *qdata
1053 	)
1054 {
1055 	struct ntp_control qpkt;
1056 	int pktsize;
1057 
1058 	/*
1059 	 * Check to make sure the data will fit in one packet
1060 	 */
1061 	if (qsize > CTL_MAX_DATA_LEN) {
1062 		(void) fprintf(stderr,
1063 			       "***Internal error!  qsize (%d) too large\n",
1064 			       qsize);
1065 		return 1;
1066 	}
1067 
1068 	/*
1069 	 * Fill in the packet
1070 	 */
1071 	qpkt.li_vn_mode = PKT_LI_VN_MODE(0, pktversion, MODE_CONTROL);
1072 	qpkt.r_m_e_op = (u_char)opcode & CTL_OP_MASK;
1073 	qpkt.sequence = htons(sequence);
1074 	qpkt.status = 0;
1075 	qpkt.associd = htons((u_short)associd);
1076 	qpkt.offset = 0;
1077 	qpkt.count = htons((u_short)qsize);
1078 
1079 	/*
1080 	 * If we have data, copy it in and pad it out to a 64
1081 	 * bit boundary.
1082 	 */
1083 	if (qsize > 0) {
1084 		memmove((char *)qpkt.data, qdata, (unsigned)qsize);
1085 		pktsize = qsize + CTL_HEADER_LEN;
1086 		while (pktsize & (sizeof(u_long) - 1)) {
1087 			qpkt.data[qsize++] = 0;
1088 			pktsize++;
1089 		}
1090 	} else {
1091 		pktsize = CTL_HEADER_LEN;
1092 	}
1093 
1094 	/*
1095 	 * If it isn't authenticated we can just send it.  Otherwise
1096 	 * we're going to have to think about it a little.
1097 	 */
1098 	if (!auth && !always_auth) {
1099 		return sendpkt((char *)&qpkt, pktsize);
1100 	} else {
1101 		const char *pass = "\0";
1102 		int maclen = 0;
1103 		u_long my_keyid;
1104 
1105 		/*
1106 		 * Pad out packet to a multiple of 8 octets to be sure
1107 		 * receiver can handle it.
1108 		 */
1109 		while (pktsize & 7) {
1110 			qpkt.data[qsize++] = 0;
1111 			pktsize++;
1112 		}
1113 
1114 		/*
1115 		 * Get the keyid and the password if we don't have one.
1116 		 */
1117 		if (info_auth_keyid == 0) {
1118 			maclen = getkeyid("Keyid: ");
1119 			if (maclen == 0) {
1120 				(void) fprintf(stderr,
1121 				   "Invalid key identifier\n");
1122 				return 1;
1123 			}
1124 		}
1125 		if (!authistrusted(info_auth_keyid)) {
1126 			pass = getpass((info_auth_keytype == KEY_TYPE_DES)
1127 			    ? "DES Password: " : "MD5 Password: ");
1128 			if (*pass == '\0') {
1129 				(void) fprintf(stderr,
1130 				  "Invalid password\n");
1131 				return (1);
1132 			}
1133 		}
1134 		info_auth_keyid = maclen;
1135 		authusekey(info_auth_keyid, info_auth_keytype, (const u_char *)pass);
1136 		authtrust(info_auth_keyid, 1);
1137 
1138 		/*
1139 		 * Stick the keyid in the packet where
1140 		 * cp currently points.  Cp should be aligned
1141 		 * properly.  Then do the encryptions.
1142 		 */
1143 		my_keyid = htonl(info_auth_keyid);
1144 		memcpy(&qpkt.data[qsize], &my_keyid, sizeof my_keyid);
1145 		maclen = authencrypt(info_auth_keyid, (u_int32 *)&qpkt,
1146 		    pktsize);
1147 		if (maclen == 0) {
1148 			(void) fprintf(stderr, "Key not found\n");
1149 			return (1);
1150 		}
1151 		return sendpkt((char *)&qpkt, pktsize + maclen);
1152 	}
1153 	/*NOTREACHED*/
1154 }
1155 
1156 
1157 /*
1158  * doquery - send a request and process the response
1159  */
1160 int
1161 doquery(
1162 	int opcode,
1163 	int associd,
1164 	int auth,
1165 	int qsize,
1166 	char *qdata,
1167 	u_short *rstatus,
1168 	int *rsize,
1169 	char **rdata
1170 	)
1171 {
1172 	int res;
1173 	int done;
1174 
1175 	/*
1176 	 * Check to make sure host is open
1177 	 */
1178 	if (!havehost) {
1179 		(void) fprintf(stderr, "***No host open, use `host' command\n");
1180 		return -1;
1181 	}
1182 
1183 	done = 0;
1184 	sequence++;
1185 
1186     again:
1187 	/*
1188 	 * send a request
1189 	 */
1190 	res = sendrequest(opcode, associd, auth, qsize, qdata);
1191 	if (res != 0)
1192 	    return res;
1193 
1194 	/*
1195 	 * Get the response.  If we got a standard error, print a message
1196 	 */
1197 	res = getresponse(opcode, associd, rstatus, rsize, rdata, done);
1198 
1199 	if (res > 0) {
1200 		if (!done && (res == ERR_TIMEOUT || res == ERR_INCOMPLETE)) {
1201 			if (res == ERR_INCOMPLETE) {
1202 				/*
1203 				 * better bump the sequence so we don't
1204 				 * get confused about differing fragments.
1205 				 */
1206 				sequence++;
1207 			}
1208 			done = 1;
1209 			goto again;
1210 		}
1211 		switch(res) {
1212 		    case CERR_BADFMT:
1213 			(void) fprintf(stderr,
1214 			    "***Server reports a bad format request packet\n");
1215 			break;
1216 		    case CERR_PERMISSION:
1217 			(void) fprintf(stderr,
1218 			    "***Server disallowed request (authentication?)\n");
1219 			break;
1220 		    case CERR_BADOP:
1221 			(void) fprintf(stderr,
1222 			    "***Server reports a bad opcode in request\n");
1223 			break;
1224 		    case CERR_BADASSOC:
1225 			(void) fprintf(stderr,
1226 			    "***Association ID %d unknown to server\n",associd);
1227 			break;
1228 		    case CERR_UNKNOWNVAR:
1229 			(void) fprintf(stderr,
1230 			    "***A request variable unknown to the server\n");
1231 			break;
1232 		    case CERR_BADVALUE:
1233 			(void) fprintf(stderr,
1234 			    "***Server indicates a request variable was bad\n");
1235 			break;
1236 		    case ERR_UNSPEC:
1237 			(void) fprintf(stderr,
1238 			    "***Server returned an unspecified error\n");
1239 			break;
1240 		    case ERR_TIMEOUT:
1241 			(void) fprintf(stderr, "***Request timed out\n");
1242 			break;
1243 		    case ERR_INCOMPLETE:
1244 			(void) fprintf(stderr,
1245 			    "***Response from server was incomplete\n");
1246 			break;
1247 		    case ERR_TOOMUCH:
1248 			(void) fprintf(stderr,
1249 			    "***Buffer size exceeded for returned data\n");
1250 			break;
1251 		    default:
1252 			(void) fprintf(stderr,
1253 			    "***Server returns unknown error code %d\n", res);
1254 			break;
1255 		}
1256 	}
1257 	return res;
1258 }
1259 
1260 
1261 /*
1262  * getcmds - read commands from the standard input and execute them
1263  */
1264 static void
1265 getcmds(void)
1266 {
1267 #ifdef HAVE_LIBREADLINE
1268         char *line;
1269 
1270         for (;;) {
1271                 if ((line = readline(interactive?prompt:"")) == NULL) return;
1272                 if (*line) add_history(line);
1273                 docmd(line);
1274                 free(line);
1275         }
1276 #else /* not HAVE_LIBREADLINE */
1277         char line[MAXLINE];
1278 
1279         for (;;) {
1280                 if (interactive) {
1281 #ifdef VMS      /* work around a problem with mixing stdout & stderr */
1282                         fputs("",stdout);
1283 #endif
1284                         (void) fputs(prompt, stderr);
1285                         (void) fflush(stderr);
1286                 }
1287 
1288                 if (fgets(line, sizeof line, stdin) == NULL)
1289                     return;
1290 
1291                 docmd(line);
1292         }
1293 #endif /* not HAVE_LIBREADLINE */
1294 }
1295 
1296 
1297 /*
1298  * abortcmd - catch interrupts and abort the current command
1299  */
1300 static RETSIGTYPE
1301 abortcmd(
1302 	int sig
1303 	)
1304 {
1305 	if (current_output == stdout)
1306 	    (void) fflush(stdout);
1307 	putc('\n', stderr);
1308 	(void) fflush(stderr);
1309 	if (jump) longjmp(interrupt_buf, 1);
1310 }
1311 
1312 
1313 /*
1314  * docmd - decode the command line and execute a command
1315  */
1316 static void
1317 docmd(
1318 	const char *cmdline
1319 	)
1320 {
1321 	char *tokens[1+MAXARGS+2];
1322 	struct parse pcmd;
1323 	int ntok;
1324 	static int i;
1325 	struct xcmd *xcmd;
1326 
1327 	/*
1328 	 * Tokenize the command line.  If nothing on it, return.
1329 	 */
1330 	tokenize(cmdline, tokens, &ntok);
1331 	if (ntok == 0)
1332 	    return;
1333 
1334 	/*
1335 	 * Find the appropriate command description.
1336 	 */
1337 	i = findcmd(tokens[0], builtins, opcmds, &xcmd);
1338 	if (i == 0) {
1339 		(void) fprintf(stderr, "***Command `%s' unknown\n",
1340 			       tokens[0]);
1341 		return;
1342 	} else if (i >= 2) {
1343 		(void) fprintf(stderr, "***Command `%s' ambiguous\n",
1344 			       tokens[0]);
1345 		return;
1346 	}
1347 
1348 	/*
1349 	 * Save the keyword, then walk through the arguments, interpreting
1350 	 * as we go.
1351 	 */
1352 	pcmd.keyword = tokens[0];
1353 	pcmd.nargs = 0;
1354 	for (i = 0; i < MAXARGS && xcmd->arg[i] != NO; i++) {
1355 		if ((i+1) >= ntok) {
1356 			if (!(xcmd->arg[i] & OPT)) {
1357 				printusage(xcmd, stderr);
1358 				return;
1359 			}
1360 			break;
1361 		}
1362 		if ((xcmd->arg[i] & OPT) && (*tokens[i+1] == '>'))
1363 		    break;
1364 		if (!getarg(tokens[i+1], (int)xcmd->arg[i], &pcmd.argval[i]))
1365 		    return;
1366 		pcmd.nargs++;
1367 	}
1368 
1369 	i++;
1370 	if (i < ntok && *tokens[i] == '>') {
1371 		char *fname;
1372 
1373 		if (*(tokens[i]+1) != '\0')
1374 		    fname = tokens[i]+1;
1375 		else if ((i+1) < ntok)
1376 		    fname = tokens[i+1];
1377 		else {
1378 			(void) fprintf(stderr, "***No file for redirect\n");
1379 			return;
1380 		}
1381 
1382 		current_output = fopen(fname, "w");
1383 		if (current_output == NULL) {
1384 			(void) fprintf(stderr, "***Error opening %s: ", fname);
1385 			perror("");
1386 			return;
1387 		}
1388 		i = 1;		/* flag we need a close */
1389 	} else {
1390 		current_output = stdout;
1391 		i = 0;		/* flag no close */
1392 	}
1393 
1394 	if (interactive && setjmp(interrupt_buf)) {
1395 		jump = 0;
1396 		return;
1397 	} else {
1398 		jump++;
1399 		(xcmd->handler)(&pcmd, current_output);
1400 		jump = 0;	/* HMS: 961106: was after fclose() */
1401 		if (i) (void) fclose(current_output);
1402 	}
1403 }
1404 
1405 
1406 /*
1407  * tokenize - turn a command line into tokens
1408  */
1409 static void
1410 tokenize(
1411 	const char *line,
1412 	char **tokens,
1413 	int *ntok
1414 	)
1415 {
1416 	register const char *cp;
1417 	register char *sp;
1418 	static char tspace[MAXLINE];
1419 
1420 	sp = tspace;
1421 	cp = line;
1422 	for (*ntok = 0; *ntok < MAXTOKENS; (*ntok)++) {
1423 		tokens[*ntok] = sp;
1424 		while (ISSPACE(*cp))
1425 		    cp++;
1426 		if (ISEOL(*cp))
1427 		    break;
1428 		do {
1429 			*sp++ = *cp++;
1430 		} while (!ISSPACE(*cp) && !ISEOL(*cp));
1431 
1432 		*sp++ = '\0';
1433 	}
1434 }
1435 
1436 
1437 
1438 /*
1439  * findcmd - find a command in a command description table
1440  */
1441 static int
1442 findcmd(
1443 	register char *str,
1444 	struct xcmd *clist1,
1445 	struct xcmd *clist2,
1446 	struct xcmd **cmd
1447 	)
1448 {
1449 	register struct xcmd *cl;
1450 	register int clen;
1451 	int nmatch;
1452 	struct xcmd *nearmatch = NULL;
1453 	struct xcmd *clist;
1454 
1455 	clen = strlen(str);
1456 	nmatch = 0;
1457 	if (clist1 != 0)
1458 	    clist = clist1;
1459 	else if (clist2 != 0)
1460 	    clist = clist2;
1461 	else
1462 	    return 0;
1463 
1464     again:
1465 	for (cl = clist; cl->keyword != 0; cl++) {
1466 		/* do a first character check, for efficiency */
1467 		if (*str != *(cl->keyword))
1468 		    continue;
1469 		if (strncmp(str, cl->keyword, (unsigned)clen) == 0) {
1470 			/*
1471 			 * Could be extact match, could be approximate.
1472 			 * Is exact if the length of the keyword is the
1473 			 * same as the str.
1474 			 */
1475 			if (*((cl->keyword) + clen) == '\0') {
1476 				*cmd = cl;
1477 				return 1;
1478 			}
1479 			nmatch++;
1480 			nearmatch = cl;
1481 		}
1482 	}
1483 
1484 	/*
1485 	 * See if there is more to do.  If so, go again.  Sorry about the
1486 	 * goto, too much looking at BSD sources...
1487 	 */
1488 	if (clist == clist1 && clist2 != 0) {
1489 		clist = clist2;
1490 		goto again;
1491 	}
1492 
1493 	/*
1494 	 * If we got extactly 1 near match, use it, else return number
1495 	 * of matches.
1496 	 */
1497 	if (nmatch == 1) {
1498 		*cmd = nearmatch;
1499 		return 1;
1500 	}
1501 	return nmatch;
1502 }
1503 
1504 
1505 /*
1506  * getarg - interpret an argument token
1507  */
1508 static int
1509 getarg(
1510 	char *str,
1511 	int code,
1512 	arg_v *argp
1513 	)
1514 {
1515 	int isneg;
1516 	char *cp, *np;
1517 	static const char *digits = "0123456789";
1518 
1519 	switch (code & ~OPT) {
1520 	    case STR:
1521 		argp->string = str;
1522 		break;
1523 	    case ADD:
1524 		if (!getnetnum(str, &(argp->netnum), (char *)0)) {
1525 			return 0;
1526 		}
1527 		break;
1528 	    case INT:
1529 	    case UINT:
1530 		isneg = 0;
1531 		np = str;
1532 		if (*np == '&') {
1533 			np++;
1534 			isneg = atoi(np);
1535 			if (isneg <= 0) {
1536 				(void) fprintf(stderr,
1537 					       "***Association value `%s' invalid/undecodable\n", str);
1538 				return 0;
1539 			}
1540 			if (isneg > numassoc) {
1541 				(void) fprintf(stderr,
1542 					       "***Association for `%s' unknown (max &%d)\n",
1543 					       str, numassoc);
1544 				return 0;
1545 			}
1546 			argp->uval = assoc_cache[isneg-1].assid;
1547 			break;
1548 		}
1549 
1550 		if (*np == '-') {
1551 			np++;
1552 			isneg = 1;
1553 		}
1554 
1555 		argp->uval = 0;
1556 		do {
1557 			cp = strchr(digits, *np);
1558 			if (cp == NULL) {
1559 				(void) fprintf(stderr,
1560 					       "***Illegal integer value %s\n", str);
1561 				return 0;
1562 			}
1563 			argp->uval *= 10;
1564 			argp->uval += (cp - digits);
1565 		} while (*(++np) != '\0');
1566 
1567 		if (isneg) {
1568 			if ((code & ~OPT) == UINT) {
1569 				(void) fprintf(stderr,
1570 					       "***Value %s should be unsigned\n", str);
1571 				return 0;
1572 			}
1573 			argp->ival = -argp->ival;
1574 		}
1575 		break;
1576 	}
1577 
1578 	return 1;
1579 }
1580 
1581 
1582 /*
1583  * getnetnum - given a host name, return its net number
1584  *	       and (optional) full name
1585  */
1586 int
1587 getnetnum(
1588 	const char *hname,
1589 	u_int32 *num,
1590 	char *fullhost
1591 	)
1592 {
1593 	struct hostent *hp;
1594 
1595 	if (decodenetnum(hname, num)) {
1596 		if (fullhost != 0) {
1597 			(void) sprintf(fullhost, "%lu.%lu.%lu.%lu",
1598 				       (u_long)((htonl(*num) >> 24) & 0xff),
1599 				       (u_long)((htonl(*num) >> 16) & 0xff),
1600 				       (u_long)((htonl(*num) >> 8) & 0xff),
1601 				       (u_long)(htonl(*num) & 0xff));
1602 		}
1603 		return 1;
1604 	} else if ((hp = gethostbyname(hname)) != 0) {
1605 		memmove((char *)num, hp->h_addr, sizeof(u_int32));
1606 		if (fullhost != 0)
1607 		    (void) strcpy(fullhost, hp->h_name);
1608 		return 1;
1609 	} else {
1610 		(void) fprintf(stderr, "***Can't find host %s\n", hname);
1611 		return 0;
1612 	}
1613 	/*NOTREACHED*/
1614 }
1615 
1616 /*
1617  * nntohost - convert network number to host name.  This routine enforces
1618  *	       the showhostnames setting.
1619  */
1620 char *
1621 nntohost(
1622 	u_int32 netnum
1623 	)
1624 {
1625 	if (!showhostnames)
1626 	    return numtoa(netnum);
1627 	if ((ntohl(netnum) & REFCLOCK_MASK) == REFCLOCK_ADDR)
1628 	    return refnumtoa(netnum);
1629 	return numtohost(netnum);
1630 }
1631 
1632 
1633 /*
1634  * rtdatetolfp - decode an RT-11 date into an l_fp
1635  */
1636 static int
1637 rtdatetolfp(
1638 	char *str,
1639 	l_fp *lfp
1640 	)
1641 {
1642 	register char *cp;
1643 	register int i;
1644 	struct calendar cal;
1645 	char buf[4];
1646 	static const char *months[12] = {
1647 		"Jan", "Feb", "Mar", "Apr", "May", "Jun",
1648 		"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
1649 	};
1650 
1651 	cal.yearday = 0;
1652 
1653 	/*
1654 	 * An RT-11 date looks like:
1655 	 *
1656 	 * d[d]-Mth-y[y] hh:mm:ss
1657 	 *
1658 	 * (No docs, but assume 4-digit years are also legal...)
1659 	 *
1660 	 * d[d]-Mth-y[y[y[y]]] hh:mm:ss
1661 	 */
1662 	cp = str;
1663 	if (!isdigit((int)*cp)) {
1664 		if (*cp == '-') {
1665 			/*
1666 			 * Catch special case
1667 			 */
1668 			L_CLR(lfp);
1669 			return 1;
1670 		}
1671 		return 0;
1672 	}
1673 
1674 	cal.monthday = *cp++ - '0';	/* ascii dependent */
1675 	if (isdigit((int)*cp)) {
1676 		cal.monthday = (cal.monthday << 3) + (cal.monthday << 1);
1677 		cal.monthday += *cp++ - '0';
1678 	}
1679 
1680 	if (*cp++ != '-')
1681 	    return 0;
1682 
1683 	for (i = 0; i < 3; i++)
1684 	    buf[i] = *cp++;
1685 	buf[3] = '\0';
1686 
1687 	for (i = 0; i < 12; i++)
1688 	    if (STREQ(buf, months[i]))
1689 		break;
1690 	if (i == 12)
1691 	    return 0;
1692 	cal.month = i + 1;
1693 
1694 	if (*cp++ != '-')
1695 	    return 0;
1696 
1697 	if (!isdigit((int)*cp))
1698 	    return 0;
1699 	cal.year = *cp++ - '0';
1700 	if (isdigit((int)*cp)) {
1701 		cal.year = (cal.year << 3) + (cal.year << 1);
1702 		cal.year += *cp++ - '0';
1703 	}
1704 	if (isdigit((int)*cp)) {
1705 		cal.year = (cal.year << 3) + (cal.year << 1);
1706 		cal.year += *cp++ - '0';
1707 	}
1708 	if (isdigit((int)*cp)) {
1709 		cal.year = (cal.year << 3) + (cal.year << 1);
1710 		cal.year += *cp++ - '0';
1711 	}
1712 
1713 	/*
1714 	 * Catch special case.  If cal.year == 0 this is a zero timestamp.
1715 	 */
1716 	if (cal.year == 0) {
1717 		L_CLR(lfp);
1718 		return 1;
1719 	}
1720 
1721 	if (*cp++ != ' ' || !isdigit((int)*cp))
1722 	    return 0;
1723 	cal.hour = *cp++ - '0';
1724 	if (isdigit((int)*cp)) {
1725 		cal.hour = (cal.hour << 3) + (cal.hour << 1);
1726 		cal.hour += *cp++ - '0';
1727 	}
1728 
1729 	if (*cp++ != ':' || !isdigit((int)*cp))
1730 	    return 0;
1731 	cal.minute = *cp++ - '0';
1732 	if (isdigit((int)*cp)) {
1733 		cal.minute = (cal.minute << 3) + (cal.minute << 1);
1734 		cal.minute += *cp++ - '0';
1735 	}
1736 
1737 	if (*cp++ != ':' || !isdigit((int)*cp))
1738 	    return 0;
1739 	cal.second = *cp++ - '0';
1740 	if (isdigit((int)*cp)) {
1741 		cal.second = (cal.second << 3) + (cal.second << 1);
1742 		cal.second += *cp++ - '0';
1743 	}
1744 
1745 	/*
1746 	 * For RT-11, 1972 seems to be the pivot year
1747 	 */
1748 	if (cal.year < 72)
1749 		cal.year += 2000;
1750 	if (cal.year < 100)
1751 		cal.year += 1900;
1752 
1753 	lfp->l_ui = caltontp(&cal);
1754 	lfp->l_uf = 0;
1755 	return 1;
1756 }
1757 
1758 
1759 /*
1760  * decodets - decode a timestamp into an l_fp format number, with
1761  *	      consideration of fuzzball formats.
1762  */
1763 int
1764 decodets(
1765 	char *str,
1766 	l_fp *lfp
1767 	)
1768 {
1769 	/*
1770 	 * If it starts with a 0x, decode as hex.
1771 	 */
1772 	if (*str == '0' && (*(str+1) == 'x' || *(str+1) == 'X'))
1773 	    return hextolfp(str+2, lfp);
1774 
1775 	/*
1776 	 * If it starts with a '"', try it as an RT-11 date.
1777 	 */
1778 	if (*str == '"') {
1779 		register char *cp = str+1;
1780 		register char *bp;
1781 		char buf[30];
1782 
1783 		bp = buf;
1784 		while (*cp != '"' && *cp != '\0' && bp < &buf[29])
1785 		    *bp++ = *cp++;
1786 		*bp = '\0';
1787 		return rtdatetolfp(buf, lfp);
1788 	}
1789 
1790 	/*
1791 	 * Might still be hex.  Check out the first character.  Talk
1792 	 * about heuristics!
1793 	 */
1794 	if ((*str >= 'A' && *str <= 'F') || (*str >= 'a' && *str <= 'f'))
1795 	    return hextolfp(str, lfp);
1796 
1797 	/*
1798 	 * Try it as a decimal.  If this fails, try as an unquoted
1799 	 * RT-11 date.  This code should go away eventually.
1800 	 */
1801 	if (atolfp(str, lfp))
1802 	    return 1;
1803 	return rtdatetolfp(str, lfp);
1804 }
1805 
1806 
1807 /*
1808  * decodetime - decode a time value.  It should be in milliseconds
1809  */
1810 int
1811 decodetime(
1812 	char *str,
1813 	l_fp *lfp
1814 	)
1815 {
1816 	return mstolfp(str, lfp);
1817 }
1818 
1819 
1820 /*
1821  * decodeint - decode an integer
1822  */
1823 int
1824 decodeint(
1825 	char *str,
1826 	long *val
1827 	)
1828 {
1829 	if (*str == '0') {
1830 		if (*(str+1) == 'x' || *(str+1) == 'X')
1831 		    return hextoint(str+2, (u_long *)&val);
1832 		return octtoint(str, (u_long *)&val);
1833 	}
1834 	return atoint(str, val);
1835 }
1836 
1837 
1838 /*
1839  * decodeuint - decode an unsigned integer
1840  */
1841 int
1842 decodeuint(
1843 	char *str,
1844 	u_long *val
1845 	)
1846 {
1847 	if (*str == '0') {
1848 		if (*(str + 1) == 'x' || *(str + 1) == 'X')
1849 			return (hextoint(str + 2, val));
1850 		return (octtoint(str, val));
1851 	}
1852 	return (atouint(str, val));
1853 }
1854 
1855 
1856 /*
1857  * decodearr - decode an array of time values
1858  */
1859 static int
1860 decodearr(
1861 	char *str,
1862 	int *narr,
1863 	l_fp *lfparr
1864 	)
1865 {
1866 	register char *cp, *bp;
1867 	register l_fp *lfp;
1868 	char buf[60];
1869 
1870 	lfp = lfparr;
1871 	cp = str;
1872 	*narr = 0;
1873 
1874 	while (*narr < 8) {
1875 		while (isspace((int)*cp))
1876 		    cp++;
1877 		if (*cp == '\0')
1878 		    break;
1879 
1880 		bp = buf;
1881 		while (!isspace((int)*cp) && *cp != '\0')
1882 		    *bp++ = *cp++;
1883 		*bp++ = '\0';
1884 
1885 		if (!decodetime(buf, lfp))
1886 		    return 0;
1887 		(*narr)++;
1888 		lfp++;
1889 	}
1890 	return 1;
1891 }
1892 
1893 
1894 /*
1895  * Finally, the built in command handlers
1896  */
1897 
1898 /*
1899  * help - tell about commands, or details of a particular command
1900  */
1901 static void
1902 help(
1903 	struct parse *pcmd,
1904 	FILE *fp
1905 	)
1906 {
1907 	int i;
1908 	int n;
1909 	struct xcmd *xcp;
1910 	char *cmd;
1911 	const char *cmdsort[100];
1912 	int length[100];
1913 	int maxlength;
1914 	int numperline;
1915 	static const char *spaces = "                    ";	/* 20 spaces */
1916 
1917 	if (pcmd->nargs == 0) {
1918 		n = 0;
1919 		for (xcp = builtins; xcp->keyword != 0; xcp++) {
1920 			if (*(xcp->keyword) != '?')
1921 			    cmdsort[n++] = xcp->keyword;
1922 		}
1923 		for (xcp = opcmds; xcp->keyword != 0; xcp++)
1924 		    cmdsort[n++] = xcp->keyword;
1925 
1926 #ifdef QSORT_USES_VOID_P
1927 		qsort(cmdsort, (size_t)n, sizeof(char *), helpsort);
1928 #else
1929 		qsort((char *)cmdsort, (size_t)n, sizeof(char *), helpsort);
1930 #endif
1931 
1932 		maxlength = 0;
1933 		for (i = 0; i < n; i++) {
1934 			length[i] = strlen(cmdsort[i]);
1935 			if (length[i] > maxlength)
1936 			    maxlength = length[i];
1937 		}
1938 		maxlength++;
1939 		numperline = 76 / maxlength;
1940 
1941 		(void) fprintf(fp, "Commands available:\n");
1942 		for (i = 0; i < n; i++) {
1943 			if ((i % numperline) == (numperline-1)
1944 			    || i == (n-1))
1945 			    (void) fprintf(fp, "%s\n", cmdsort[i]);
1946 			else
1947 			    (void) fprintf(fp, "%s%s", cmdsort[i],
1948 					   spaces+20-maxlength+length[i]);
1949 		}
1950 	} else {
1951 		cmd = pcmd->argval[0].string;
1952 		n = findcmd(cmd, builtins, opcmds, &xcp);
1953 		if (n == 0) {
1954 			(void) fprintf(stderr,
1955 				       "Command `%s' is unknown\n", cmd);
1956 			return;
1957 		} else if (n >= 2) {
1958 			(void) fprintf(stderr,
1959 				       "Command `%s' is ambiguous\n", cmd);
1960 			return;
1961 		}
1962 		(void) fprintf(fp, "function: %s\n", xcp->comment);
1963 		printusage(xcp, fp);
1964 	}
1965 }
1966 
1967 
1968 /*
1969  * helpsort - do hostname qsort comparisons
1970  */
1971 #ifdef QSORT_USES_VOID_P
1972 static int
1973 helpsort(
1974 	const void *t1,
1975 	const void *t2
1976 	)
1977 {
1978 	const char **name1 = (const char **)t1;
1979 	const char **name2 = (const char **)t2;
1980 
1981 	return strcmp(*name1, *name2);
1982 }
1983 
1984 #else
1985 static int
1986 helpsort(
1987 	char **name1,
1988 	char **name2
1989 	)
1990 {
1991 	return strcmp(*name1, *name2);
1992 }
1993 #endif
1994 
1995 /*
1996  * printusage - print usage information for a command
1997  */
1998 static void
1999 printusage(
2000 	struct xcmd *xcp,
2001 	FILE *fp
2002 	)
2003 {
2004 	register int i;
2005 
2006 	(void) fprintf(fp, "usage: %s", xcp->keyword);
2007 	for (i = 0; i < MAXARGS && xcp->arg[i] != NO; i++) {
2008 		if (xcp->arg[i] & OPT)
2009 		    (void) fprintf(fp, " [ %s ]", xcp->desc[i]);
2010 		else
2011 		    (void) fprintf(fp, " %s", xcp->desc[i]);
2012 	}
2013 	(void) fprintf(fp, "\n");
2014 }
2015 
2016 
2017 /*
2018  * timeout - set time out time
2019  */
2020 static void
2021 timeout(
2022 	struct parse *pcmd,
2023 	FILE *fp
2024 	)
2025 {
2026 	int val;
2027 
2028 	if (pcmd->nargs == 0) {
2029 		val = tvout.tv_sec * 1000 + tvout.tv_usec / 1000;
2030 		(void) fprintf(fp, "primary timeout %d ms\n", val);
2031 	} else {
2032 		tvout.tv_sec = pcmd->argval[0].uval / 1000;
2033 		tvout.tv_usec = (pcmd->argval[0].uval - (tvout.tv_sec * 1000))
2034 			* 1000;
2035 	}
2036 }
2037 
2038 
2039 /*
2040  * auth_delay - set delay for auth requests
2041  */
2042 static void
2043 auth_delay(
2044 	struct parse *pcmd,
2045 	FILE *fp
2046 	)
2047 {
2048 	int isneg;
2049 	u_long val;
2050 
2051 	if (pcmd->nargs == 0) {
2052 		val = delay_time.l_ui * 1000 + delay_time.l_uf / 4294967;
2053 		(void) fprintf(fp, "delay %lu ms\n", val);
2054 	} else {
2055 		if (pcmd->argval[0].ival < 0) {
2056 			isneg = 1;
2057 			val = (u_long)(-pcmd->argval[0].ival);
2058 		} else {
2059 			isneg = 0;
2060 			val = (u_long)pcmd->argval[0].ival;
2061 		}
2062 
2063 		delay_time.l_ui = val / 1000;
2064 		val %= 1000;
2065 		delay_time.l_uf = val * 4294967;	/* 2**32/1000 */
2066 
2067 		if (isneg)
2068 		    L_NEG(&delay_time);
2069 	}
2070 }
2071 
2072 
2073 /*
2074  * host - set the host we are dealing with.
2075  */
2076 static void
2077 host(
2078 	struct parse *pcmd,
2079 	FILE *fp
2080 	)
2081 {
2082 	if (pcmd->nargs == 0) {
2083 		if (havehost)
2084 		    (void) fprintf(fp, "current host is %s\n", currenthost);
2085 		else
2086 		    (void) fprintf(fp, "no current host\n");
2087 	} else if (openhost(pcmd->argval[0].string)) {
2088 		(void) fprintf(fp, "current host set to %s\n", currenthost);
2089 		numassoc = 0;
2090 	} else {
2091 		if (havehost)
2092 		    (void) fprintf(fp,
2093 				   "current host remains %s\n", currenthost);
2094 		else
2095 		    (void) fprintf(fp, "still no current host\n");
2096 	}
2097 }
2098 
2099 
2100 /*
2101  * poll - do one (or more) polls of the host via NTP
2102  */
2103 /*ARGSUSED*/
2104 static void
2105 ntp_poll(
2106 	struct parse *pcmd,
2107 	FILE *fp
2108 	)
2109 {
2110 	(void) fprintf(fp, "poll not implemented yet\n");
2111 }
2112 
2113 
2114 /*
2115  * keyid - get a keyid to use for authenticating requests
2116  */
2117 static void
2118 keyid(
2119 	struct parse *pcmd,
2120 	FILE *fp
2121 	)
2122 {
2123 	if (pcmd->nargs == 0) {
2124 		if (info_auth_keyid > NTP_MAXKEY)
2125 		    (void) fprintf(fp, "no keyid defined\n");
2126 		else
2127 		    (void) fprintf(fp, "keyid is %lu\n", (u_long)info_auth_keyid);
2128 	} else {
2129 		info_auth_keyid = pcmd->argval[0].uval;
2130 	}
2131 }
2132 
2133 /*
2134  * keytype - get type of key to use for authenticating requests
2135  */
2136 static void
2137 keytype(
2138 	struct parse *pcmd,
2139 	FILE *fp
2140 	)
2141 {
2142 	if (pcmd->nargs == 0)
2143 	    fprintf(fp, "keytype is %s\n",
2144 		    (info_auth_keytype == KEY_TYPE_MD5) ? "MD5" : "DES");
2145 	else
2146 	    switch (*(pcmd->argval[0].string)) {
2147 		case 'm':
2148 		case 'M':
2149 		    info_auth_keytype = KEY_TYPE_MD5;
2150 		    break;
2151 
2152 		case 'd':
2153 		case 'D':
2154 		    info_auth_keytype = KEY_TYPE_DES;
2155 		    break;
2156 
2157 		default:
2158 		    fprintf(fp, "keytype must be 'md5' or 'des'\n");
2159 	    }
2160 }
2161 
2162 
2163 
2164 /*
2165  * passwd - get an authentication key
2166  */
2167 /*ARGSUSED*/
2168 static void
2169 passwd(
2170 	struct parse *pcmd,
2171 	FILE *fp
2172 	)
2173 {
2174 	char *pass;
2175 
2176 	if (info_auth_keyid > NTP_MAXKEY) {
2177 		info_auth_keyid = getkeyid("Keyid: ");
2178 		if (info_auth_keyid > NTP_MAXKEY) {
2179 			(void)fprintf(fp, "Keyid must be defined\n");
2180 			return;
2181 		}
2182 	}
2183 	pass = getpass((info_auth_keytype == KEY_TYPE_DES)
2184 		       ? "DES Password: "
2185 		       : "MD5 Password: "
2186 		       );
2187 	if (*pass == '\0')
2188 	    (void) fprintf(fp, "Password unchanged\n");
2189 	else
2190 	    authusekey(info_auth_keyid, info_auth_keytype, (u_char *)pass);
2191 }
2192 
2193 
2194 /*
2195  * hostnames - set the showhostnames flag
2196  */
2197 static void
2198 hostnames(
2199 	struct parse *pcmd,
2200 	FILE *fp
2201 	)
2202 {
2203 	if (pcmd->nargs == 0) {
2204 		if (showhostnames)
2205 		    (void) fprintf(fp, "hostnames being shown\n");
2206 		else
2207 		    (void) fprintf(fp, "hostnames not being shown\n");
2208 	} else {
2209 		if (STREQ(pcmd->argval[0].string, "yes"))
2210 		    showhostnames = 1;
2211 		else if (STREQ(pcmd->argval[0].string, "no"))
2212 		    showhostnames = 0;
2213 		else
2214 		    (void)fprintf(stderr, "What?\n");
2215 	}
2216 }
2217 
2218 
2219 
2220 /*
2221  * setdebug - set/change debugging level
2222  */
2223 static void
2224 setdebug(
2225 	struct parse *pcmd,
2226 	FILE *fp
2227 	)
2228 {
2229 	if (pcmd->nargs == 0) {
2230 		(void) fprintf(fp, "debug level is %d\n", debug);
2231 		return;
2232 	} else if (STREQ(pcmd->argval[0].string, "no")) {
2233 		debug = 0;
2234 	} else if (STREQ(pcmd->argval[0].string, "more")) {
2235 		debug++;
2236 	} else if (STREQ(pcmd->argval[0].string, "less")) {
2237 		debug--;
2238 	} else {
2239 		(void) fprintf(fp, "What?\n");
2240 		return;
2241 	}
2242 	(void) fprintf(fp, "debug level set to %d\n", debug);
2243 }
2244 
2245 
2246 /*
2247  * quit - stop this nonsense
2248  */
2249 /*ARGSUSED*/
2250 static void
2251 quit(
2252 	struct parse *pcmd,
2253 	FILE *fp
2254 	)
2255 {
2256 	if (havehost)
2257 	    closesocket(sockfd);	/* cleanliness next to godliness */
2258 	exit(0);
2259 }
2260 
2261 
2262 /*
2263  * version - print the current version number
2264  */
2265 /*ARGSUSED*/
2266 static void
2267 version(
2268 	struct parse *pcmd,
2269 	FILE *fp
2270 	)
2271 {
2272 
2273 	(void) fprintf(fp, "%s\n", Version);
2274 	return;
2275 }
2276 
2277 
2278 /*
2279  * raw - set raw mode output
2280  */
2281 /*ARGSUSED*/
2282 static void
2283 raw(
2284 	struct parse *pcmd,
2285 	FILE *fp
2286 	)
2287 {
2288 	rawmode = 1;
2289 	(void) fprintf(fp, "Output set to raw\n");
2290 }
2291 
2292 
2293 /*
2294  * cooked - set cooked mode output
2295  */
2296 /*ARGSUSED*/
2297 static void
2298 cooked(
2299 	struct parse *pcmd,
2300 	FILE *fp
2301 	)
2302 {
2303 	rawmode = 0;
2304 	(void) fprintf(fp, "Output set to cooked\n");
2305 	return;
2306 }
2307 
2308 
2309 /*
2310  * authenticate - always authenticate requests to this host
2311  */
2312 static void
2313 authenticate(
2314 	struct parse *pcmd,
2315 	FILE *fp
2316 	)
2317 {
2318 	if (pcmd->nargs == 0) {
2319 		if (always_auth) {
2320 			(void) fprintf(fp,
2321 				       "authenticated requests being sent\n");
2322 		} else
2323 		    (void) fprintf(fp,
2324 				   "unauthenticated requests being sent\n");
2325 	} else {
2326 		if (STREQ(pcmd->argval[0].string, "yes")) {
2327 			always_auth = 1;
2328 		} else if (STREQ(pcmd->argval[0].string, "no")) {
2329 			always_auth = 0;
2330 		} else
2331 		    (void)fprintf(stderr, "What?\n");
2332 	}
2333 }
2334 
2335 
2336 /*
2337  * ntpversion - choose the NTP version to use
2338  */
2339 static void
2340 ntpversion(
2341 	struct parse *pcmd,
2342 	FILE *fp
2343 	)
2344 {
2345 	if (pcmd->nargs == 0) {
2346 		(void) fprintf(fp,
2347 			       "NTP version being claimed is %d\n", pktversion);
2348 	} else {
2349 		if (pcmd->argval[0].uval < NTP_OLDVERSION
2350 		    || pcmd->argval[0].uval > NTP_VERSION) {
2351 			(void) fprintf(stderr, "versions %d to %d, please\n",
2352 				       NTP_OLDVERSION, NTP_VERSION);
2353 		} else {
2354 			pktversion = (u_char) pcmd->argval[0].uval;
2355 		}
2356 	}
2357 }
2358 
2359 
2360 /*
2361  * warning - print a warning message
2362  */
2363 static void
2364 warning(
2365 	const char *fmt,
2366 	const char *st1,
2367 	const char *st2
2368 	)
2369 {
2370 	(void) fprintf(stderr, "%s: ", progname);
2371 	(void) fprintf(stderr, fmt, st1, st2);
2372 	(void) fprintf(stderr, ": ");
2373 	perror("");
2374 }
2375 
2376 
2377 /*
2378  * error - print a message and exit
2379  */
2380 static void
2381 error(
2382 	const char *fmt,
2383 	const char *st1,
2384 	const char *st2
2385 	)
2386 {
2387 	warning(fmt, st1, st2);
2388 	exit(1);
2389 }
2390 
2391 /*
2392  * getkeyid - prompt the user for a keyid to use
2393  */
2394 static u_long
2395 getkeyid(
2396 	const char *keyprompt
2397 	)
2398 {
2399 	register char *p;
2400 	register int c;
2401 	FILE *fi;
2402 	char pbuf[20];
2403 
2404 #ifndef SYS_WINNT
2405 	if ((fi = fdopen(open("/dev/tty", 2), "r")) == NULL)
2406 #else
2407 	    if ((fi = _fdopen((int)GetStdHandle(STD_INPUT_HANDLE), "r")) == NULL)
2408 #endif /* SYS_WINNT */
2409 		fi = stdin;
2410 	    else
2411 		setbuf(fi, (char *)NULL);
2412 	fprintf(stderr, "%s", keyprompt); fflush(stderr);
2413 	for (p=pbuf; (c = getc(fi))!='\n' && c!=EOF;) {
2414 		if (p < &pbuf[18])
2415 		    *p++ = c;
2416 	}
2417 	*p = '\0';
2418 	if (fi != stdin)
2419 	    fclose(fi);
2420 	if (strcmp(pbuf, "0") == 0)
2421 	    return 0;
2422 
2423 	return (u_long) atoi(pbuf);
2424 }
2425 
2426 
2427 /*
2428  * atoascii - printable-ize possibly ascii data using the character
2429  *	      transformations cat -v uses.
2430  */
2431 static void
2432 atoascii(
2433 	int length,
2434 	char *data,
2435 	char *outdata
2436 	)
2437 {
2438 	register u_char *cp;
2439 	register u_char *ocp;
2440 	register u_char c;
2441 
2442 	if (!data)
2443 	{
2444 		*outdata = '\0';
2445 		return;
2446 	}
2447 
2448 	ocp = (u_char *)outdata;
2449 	for (cp = (u_char *)data; cp < (u_char *)data + length; cp++) {
2450 		c = *cp;
2451 		if (c == '\0')
2452 		    break;
2453 		if (c == '\0')
2454 		    break;
2455 		if (c > 0177) {
2456 			*ocp++ = 'M';
2457 			*ocp++ = '-';
2458 			c &= 0177;
2459 		}
2460 
2461 		if (c < ' ') {
2462 			*ocp++ = '^';
2463 			*ocp++ = c + '@';
2464 		} else if (c == 0177) {
2465 			*ocp++ = '^';
2466 			*ocp++ = '?';
2467 		} else {
2468 			*ocp++ = c;
2469 		}
2470 		if (ocp >= ((u_char *)outdata + length - 4))
2471 		    break;
2472 	}
2473 	*ocp++ = '\0';
2474 }
2475 
2476 
2477 
2478 /*
2479  * makeascii - print possibly ascii data using the character
2480  *	       transformations that cat -v uses.
2481  */
2482 static void
2483 makeascii(
2484 	int length,
2485 	char *data,
2486 	FILE *fp
2487 	)
2488 {
2489 	register u_char *cp;
2490 	register int c;
2491 
2492 	for (cp = (u_char *)data; cp < (u_char *)data + length; cp++) {
2493 		c = (int)*cp;
2494 		if (c > 0177) {
2495 			putc('M', fp);
2496 			putc('-', fp);
2497 			c &= 0177;
2498 		}
2499 
2500 		if (c < ' ') {
2501 			putc('^', fp);
2502 			putc(c+'@', fp);
2503 		} else if (c == 0177) {
2504 			putc('^', fp);
2505 			putc('?', fp);
2506 		} else {
2507 			putc(c, fp);
2508 		}
2509 	}
2510 }
2511 
2512 
2513 /*
2514  * asciize - same thing as makeascii except add a newline
2515  */
2516 void
2517 asciize(
2518 	int length,
2519 	char *data,
2520 	FILE *fp
2521 	)
2522 {
2523 	makeascii(length, data, fp);
2524 	putc('\n', fp);
2525 }
2526 
2527 
2528 /*
2529  * Some circular buffer space
2530  */
2531 #define	CBLEN	80
2532 #define	NUMCB	6
2533 
2534 char circ_buf[NUMCB][CBLEN];
2535 int nextcb = 0;
2536 
2537 /*
2538  * nextvar - find the next variable in the buffer
2539  */
2540 int
2541 nextvar(
2542 	int *datalen,
2543 	char **datap,
2544 	char **vname,
2545 	char **vvalue
2546 	)
2547 {
2548 	register char *cp;
2549 	register char *np;
2550 	register char *cpend;
2551 	register char *npend;	/* character after last */
2552 	int quoted = 0;
2553 	static char name[MAXVARLEN];
2554 	static char value[MAXVALLEN];
2555 
2556 	cp = *datap;
2557 	cpend = cp + *datalen;
2558 
2559 	/*
2560 	 * Space past commas and white space
2561 	 */
2562 	while (cp < cpend && (*cp == ',' || isspace((int)*cp)))
2563 	    cp++;
2564 	if (cp == cpend)
2565 	    return 0;
2566 
2567 	/*
2568 	 * Copy name until we hit a ',', an '=', a '\r' or a '\n'.  Backspace
2569 	 * over any white space and terminate it.
2570 	 */
2571 	np = name;
2572 	npend = &name[MAXVARLEN];
2573 	while (cp < cpend && np < npend && *cp != ',' && *cp != '='
2574 	       && *cp != '\r' && *cp != '\n')
2575 	    *np++ = *cp++;
2576 	/*
2577 	 * Check if we ran out of name space, without reaching the end or a
2578 	 * terminating character
2579 	 */
2580 	if (np == npend && !(cp == cpend || *cp == ',' || *cp == '=' ||
2581 			     *cp == '\r' || *cp == '\n'))
2582 	    return 0;
2583 	while (isspace((int)(*(np-1))))
2584 	    np--;
2585 	*np = '\0';
2586 	*vname = name;
2587 
2588 	/*
2589 	 * Check if we hit the end of the buffer or a ','.  If so we are done.
2590 	 */
2591 	if (cp == cpend || *cp == ',' || *cp == '\r' || *cp == '\n') {
2592 		if (cp != cpend)
2593 		    cp++;
2594 		*datap = cp;
2595 		*datalen = cpend - cp;
2596 		*vvalue = (char *)0;
2597 		return 1;
2598 	}
2599 
2600 	/*
2601 	 * So far, so good.  Copy out the value
2602 	 */
2603 	cp++;	/* past '=' */
2604 	while (cp < cpend && (isspace((int)*cp) && *cp != '\r' && *cp != '\n'))
2605 	    cp++;
2606 	np = value;
2607 	npend = &value[MAXVALLEN];
2608 	while (cp < cpend && np < npend && ((*cp != ',') || quoted))
2609 	{
2610 		quoted ^= ((*np++ = *cp++) == '"');
2611 	}
2612 
2613 	/*
2614 	 * Check if we overran the value buffer while still in a quoted string
2615 	 * or without finding a comma
2616 	 */
2617 	if (np == npend && (quoted || *cp != ','))
2618 	    return 0;
2619 	/*
2620 	 * Trim off any trailing whitespace
2621 	 */
2622 	while (np > value && isspace((int)(*(np-1))))
2623 	    np--;
2624 	*np = '\0';
2625 
2626 	/*
2627 	 * Return this.  All done.
2628 	 */
2629 	if (cp != cpend)
2630 	    cp++;
2631 	*datap = cp;
2632 	*datalen = cpend - cp;
2633 	*vvalue = value;
2634 	return 1;
2635 }
2636 
2637 
2638 /*
2639  * findvar - see if this variable is known to us
2640  */
2641 int
2642 findvar(
2643 	char *varname,
2644 	struct ctl_var *varlist
2645 	)
2646 {
2647 	register char *np;
2648 	register struct ctl_var *vl;
2649 
2650 	vl = varlist;
2651 	np = varname;
2652 	while (vl->fmt != EOV) {
2653 		if (vl->fmt != PADDING && STREQ(np, vl->text))
2654 		    return vl->code;
2655 		vl++;
2656 	}
2657 	return 0;
2658 }
2659 
2660 
2661 
2662 /*
2663  * printvars - print variables returned in response packet
2664  */
2665 void
2666 printvars(
2667 	int length,
2668 	char *data,
2669 	int status,
2670 	int sttype,
2671 	FILE *fp
2672 	)
2673 {
2674 	if (rawmode)
2675 	    rawprint(sttype, length, data, status, fp);
2676 	else
2677 	    cookedprint(sttype, length, data, status, fp);
2678 }
2679 
2680 
2681 /*
2682  * rawprint - do a printout of the data in raw mode
2683  */
2684 static void
2685 rawprint(
2686 	int datatype,
2687 	int length,
2688 	char *data,
2689 	int status,
2690 	FILE *fp
2691 	)
2692 {
2693 	register char *cp;
2694 	register char *cpend;
2695 
2696 	/*
2697 	 * Essentially print the data as is.  We reformat unprintables, though.
2698 	 */
2699 	cp = data;
2700 	cpend = data + length;
2701 
2702 	(void) fprintf(fp, "status=0x%04x,\n", status);
2703 
2704 	while (cp < cpend) {
2705 		if (*cp == '\r') {
2706 			/*
2707 			 * If this is a \r and the next character is a
2708 			 * \n, supress this, else pretty print it.  Otherwise
2709 			 * just output the character.
2710 			 */
2711 			if (cp == (cpend-1) || *(cp+1) != '\n')
2712 			    makeascii(1, cp, fp);
2713 		} else if (isspace((int)*cp) || isprint((int)*cp)) {
2714 			putc(*cp, fp);
2715 		} else {
2716 			makeascii(1, cp, fp);
2717 		}
2718 		cp++;
2719 	}
2720 }
2721 
2722 
2723 /*
2724  * Global data used by the cooked output routines
2725  */
2726 int out_chars;		/* number of characters output */
2727 int out_linecount;	/* number of characters output on this line */
2728 
2729 
2730 /*
2731  * startoutput - get ready to do cooked output
2732  */
2733 static void
2734 startoutput(void)
2735 {
2736 	out_chars = 0;
2737 	out_linecount = 0;
2738 }
2739 
2740 
2741 /*
2742  * output - output a variable=value combination
2743  */
2744 static void
2745 output(
2746 	FILE *fp,
2747 	char *name,
2748 	char *value
2749 	)
2750 {
2751 	int lenname;
2752 	int lenvalue;
2753 
2754 	lenname = strlen(name);
2755 	lenvalue = strlen(value);
2756 
2757 	if (out_chars != 0) {
2758 		putc(',', fp);
2759 		out_chars++;
2760 		out_linecount++;
2761 		if ((out_linecount + lenname + lenvalue + 3) > MAXOUTLINE) {
2762 			putc('\n', fp);
2763 			out_chars++;
2764 			out_linecount = 0;
2765 		} else {
2766 			putc(' ', fp);
2767 			out_chars++;
2768 			out_linecount++;
2769 		}
2770 	}
2771 
2772 	fputs(name, fp);
2773 	putc('=', fp);
2774 	fputs(value, fp);
2775 	out_chars += lenname + 1 + lenvalue;
2776 	out_linecount += lenname + 1 + lenvalue;
2777 }
2778 
2779 
2780 /*
2781  * endoutput - terminate a block of cooked output
2782  */
2783 static void
2784 endoutput(
2785 	FILE *fp
2786 	)
2787 {
2788 	if (out_chars != 0)
2789 	    putc('\n', fp);
2790 }
2791 
2792 
2793 /*
2794  * outputarr - output an array of values
2795  */
2796 static void
2797 outputarr(
2798 	FILE *fp,
2799 	char *name,
2800 	int narr,
2801 	l_fp *lfp
2802 	)
2803 {
2804 	register char *bp;
2805 	register char *cp;
2806 	register int i;
2807 	register int len;
2808 	char buf[256];
2809 
2810 	bp = buf;
2811 	/*
2812 	 * Hack to align delay and offset values
2813 	 */
2814 	for (i = (int)strlen(name); i < 11; i++)
2815 	    *bp++ = ' ';
2816 
2817 	for (i = narr; i > 0; i--) {
2818 		if (i != narr)
2819 		    *bp++ = ' ';
2820 		cp = lfptoms(lfp, 2);
2821 		len = strlen(cp);
2822 		if (len > 7) {
2823 			cp[7] = '\0';
2824 			len = 7;
2825 		}
2826 		while (len < 7) {
2827 			*bp++ = ' ';
2828 			len++;
2829 		}
2830 		while (*cp != '\0')
2831 		    *bp++ = *cp++;
2832 		lfp++;
2833 	}
2834 	*bp = '\0';
2835 	output(fp, name, buf);
2836 }
2837 
2838 static char *
2839 tstflags(
2840 	u_long val
2841 	)
2842 {
2843 	register char *cb, *s;
2844 	register int i;
2845 	register const char *sep;
2846 
2847 	sep = "";
2848 	i = 0;
2849 	s = cb = &circ_buf[nextcb][0];
2850 	if (++nextcb >= NUMCB)
2851 	    nextcb = 0;
2852 
2853 	sprintf(cb, "%02lx", val);
2854 	cb += strlen(cb);
2855 	if (!val) {
2856 		strcat(cb, " ok");
2857 		cb += strlen(cb);
2858 	} else {
2859 		*cb++ = ' ';
2860 		for (i = 0; i < 11; i++) {
2861 			if (val & 0x1) {
2862 				sprintf(cb, "%s%s", sep, tstflagnames[i]);
2863 				sep = ", ";
2864 				cb += strlen(cb);
2865 			}
2866 			val >>= 1;
2867 		}
2868 	}
2869 	*cb = '\0';
2870 	return s;
2871 }
2872 
2873 /*
2874  * cookedprint - output variables in cooked mode
2875  */
2876 static void
2877 cookedprint(
2878 	int datatype,
2879 	int length,
2880 	char *data,
2881 	int status,
2882 	FILE *fp
2883 	)
2884 {
2885 	register int varid;
2886 	char *name;
2887 	char *value;
2888 	int output_raw;
2889 	int fmt;
2890 	struct ctl_var *varlist;
2891 	l_fp lfp;
2892 	long ival;
2893 	u_int32 hval;
2894 	u_long uval;
2895 	l_fp lfparr[8];
2896 	int narr;
2897 
2898 	switch (datatype) {
2899 	    case TYPE_PEER:
2900 		varlist = peer_var;
2901 		break;
2902 	    case TYPE_SYS:
2903 		varlist = sys_var;
2904 		break;
2905 	    case TYPE_CLOCK:
2906 		varlist = clock_var;
2907 		break;
2908 	    default:
2909 		(void) fprintf(stderr, "Unknown datatype(0x%x) in cookedprint\n", datatype);
2910 		return;
2911 	}
2912 
2913 	(void) fprintf(fp, "status=%04x %s,\n", status,
2914 		       statustoa(datatype, status));
2915 
2916 	startoutput();
2917 	while (nextvar(&length, &data, &name, &value)) {
2918 		varid = findvar(name, varlist);
2919 		if (varid == 0) {
2920 			output_raw = '*';
2921 		} else {
2922 			output_raw = 0;
2923 			fmt = varlist[varid].fmt;
2924 			switch(fmt) {
2925 			    case TS:
2926 				if (!decodets(value, &lfp))
2927 				    output_raw = '?';
2928 				else
2929 				    output(fp, name, prettydate(&lfp));
2930 				break;
2931 			    case FL:
2932 			    case FU:
2933 			    case FS:
2934 				if (!decodetime(value, &lfp))
2935 				    output_raw = '?';
2936 				else {
2937 					switch (fmt) {
2938 					    case FL:
2939 						output(fp, name,
2940 						       lfptoms(&lfp, 3));
2941 						break;
2942 					    case FU:
2943 						output(fp, name,
2944 						       ulfptoms(&lfp, 3));
2945 						break;
2946 					    case FS:
2947 						output(fp, name,
2948 						       lfptoms(&lfp, 3));
2949 						break;
2950 					}
2951 				}
2952 				break;
2953 
2954 			    case UI:
2955 				if (!decodeuint(value, &uval))
2956 				    output_raw = '?';
2957 				else
2958 				    output(fp, name, uinttoa(uval));
2959 				break;
2960 
2961 			    case SI:
2962 				if (!decodeint(value, &ival))
2963 				    output_raw = '?';
2964 				else
2965 				    output(fp, name, inttoa(ival));
2966 				break;
2967 
2968 			    case HA:
2969 			    case NA:
2970 				if (!decodenetnum(value, &hval))
2971 				    output_raw = '?';
2972 				else if (fmt == HA)
2973 				    output(fp, name, nntohost(hval));
2974 				else
2975 				    output(fp, name, numtoa(hval));
2976 				break;
2977 
2978 			    case ST:
2979 				output_raw = '*';
2980 				break;
2981 
2982 			    case RF:
2983 				if (decodenetnum(value, &hval))
2984 				    output(fp, name, nntohost(hval));
2985 				else if ((int)strlen(value) <= 4)
2986 				    output(fp, name, value);
2987 				else
2988 				    output_raw = '?';
2989 				break;
2990 
2991 			    case LP:
2992 				if (!decodeuint(value, &uval) || uval > 3)
2993 				    output_raw = '?';
2994 				else {
2995 					char b[3];
2996 					b[0] = b[1] = '0';
2997 					if (uval & 0x2)
2998 					    b[0] = '1';
2999 					if (uval & 0x1)
3000 					    b[1] = '1';
3001 					b[2] = '\0';
3002 					output(fp, name, b);
3003 				}
3004 				break;
3005 
3006 			    case OC:
3007 				if (!decodeuint(value, &uval))
3008 				    output_raw = '?';
3009 				else {
3010 					char b[10];
3011 
3012 					(void) sprintf(b, "%03lo", uval);
3013 					output(fp, name, b);
3014 				}
3015 				break;
3016 
3017 			    case MD:
3018 				if (!decodeuint(value, &uval))
3019 				    output_raw = '?';
3020 				else
3021 				    output(fp, name, uinttoa(uval));
3022 				break;
3023 
3024 			    case AR:
3025 				if (!decodearr(value, &narr, lfparr))
3026 				    output_raw = '?';
3027 				else
3028 				    outputarr(fp, name, narr, lfparr);
3029 				break;
3030 
3031 			    case FX:
3032 				if (!decodeuint(value, &uval))
3033 				    output_raw = '?';
3034 				else
3035 				    output(fp, name, tstflags(uval));
3036 				break;
3037 
3038 			    default:
3039 				(void) fprintf(stderr,
3040 				    "Internal error in cookedprint, %s=%s, fmt %d\n",
3041 				    name, value, fmt);
3042 				break;
3043 			}
3044 
3045 		}
3046 		if (output_raw != 0) {
3047 			char bn[401];
3048 			char bv[401];
3049 			int len;
3050 
3051 			atoascii(400, name, bn);
3052 			atoascii(400, value, bv);
3053 			if (output_raw != '*') {
3054 				len = strlen(bv);
3055 				bv[len] = output_raw;
3056 				bv[len+1] = '\0';
3057 			}
3058 			output(fp, bn, bv);
3059 		}
3060 	}
3061 	endoutput(fp);
3062 }
3063 
3064 
3065 /*
3066  * sortassoc - sort associations in the cache into ascending order
3067  */
3068 void
3069 sortassoc(void)
3070 {
3071 	if (numassoc > 1)
3072 	    qsort(
3073 #ifdef QSORT_USES_VOID_P
3074 		    (void *)
3075 #else
3076 		    (char *)
3077 #endif
3078 		    assoc_cache, (size_t)numassoc,
3079 		    sizeof(struct association), assoccmp);
3080 }
3081 
3082 
3083 /*
3084  * assoccmp - compare two associations
3085  */
3086 #ifdef QSORT_USES_VOID_P
3087 static int
3088 assoccmp(
3089 	const void *t1,
3090 	const void *t2
3091 	)
3092 {
3093 	const struct association *ass1 = (const struct association *)t1;
3094 	const struct association *ass2 = (const struct association *)t2;
3095 
3096 	if (ass1->assid < ass2->assid)
3097 	    return -1;
3098 	if (ass1->assid > ass2->assid)
3099 	    return 1;
3100 	return 0;
3101 }
3102 #else
3103 static int
3104 assoccmp(
3105 	struct association *ass1,
3106 	struct association *ass2
3107 	)
3108 {
3109 	if (ass1->assid < ass2->assid)
3110 	    return -1;
3111 	if (ass1->assid > ass2->assid)
3112 	    return 1;
3113 	return 0;
3114 }
3115 #endif /* not QSORT_USES_VOID_P */
3116