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