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