154359Sroberto/*
254359Sroberto * ntpq - query an NTP server using mode 6 commands
354359Sroberto */
482498Sroberto
554359Sroberto#include <stdio.h>
682498Sroberto
7182007Sroberto#include <ctype.h>
8182007Sroberto#include <signal.h>
9182007Sroberto#include <setjmp.h>
10182007Sroberto#include <sys/types.h>
11182007Sroberto#include <sys/time.h>
12182007Sroberto
1382498Sroberto#include "ntpq.h"
1482498Sroberto#include "ntp_unixtime.h"
1582498Sroberto#include "ntp_calendar.h"
1682498Sroberto#include "ntp_io.h"
1782498Sroberto#include "ntp_select.h"
1882498Sroberto#include "ntp_stdlib.h"
19132451Sroberto/* Don't include ISC's version of IPv6 variables and structures */
20132451Sroberto#define ISC_IPV6_H 1
21132451Sroberto#include "isc/net.h"
22132451Sroberto#include "isc/result.h"
2382498Sroberto
24182007Sroberto#include "ntpq-opts.h"
25182007Sroberto
2654359Sroberto#ifdef SYS_WINNT
27182007Sroberto# include <Mswsock.h>
2854359Sroberto# include <io.h>
2954359Sroberto#else
30182007Sroberto# define closesocket close
3154359Sroberto#endif /* SYS_WINNT */
3254359Sroberto
33132451Sroberto#if defined(HAVE_LIBREADLINE) || defined(HAVE_LIBEDIT)
34106163Sroberto# include <readline/readline.h>
35106163Sroberto# include <readline/history.h>
36132451Sroberto#endif /* HAVE_LIBREADLINE || HAVE_LIBEDIT */
37106163Sroberto
3854359Sroberto#ifdef SYS_VXWORKS
39182007Sroberto				/* vxWorks needs mode flag -casey*/
40182007Sroberto# define open(name, flags)   open(name, flags, 0777)
41182007Sroberto# define SERVER_PORT_NUM     123
4254359Sroberto#endif
4354359Sroberto
44182007Sroberto/* we use COMMAND as an autogen keyword */
45182007Sroberto#ifdef COMMAND
46182007Sroberto# undef COMMAND
47182007Sroberto#endif
48182007Sroberto
4954359Sroberto/*
5054359Sroberto * Because we potentially understand a lot of commands we will run
5154359Sroberto * interactive if connected to a terminal.
5254359Sroberto */
5354359Srobertoint interactive = 0;		/* set to 1 when we should prompt */
5454359Srobertoconst char *prompt = "ntpq> ";	/* prompt to ask him about */
5554359Sroberto
5654359Sroberto
5754359Sroberto/*
58182007Sroberto * for get_systime()
59182007Sroberto */
60182007Srobertos_char	sys_precision;		/* local clock precision (log2 s) */
61182007Sroberto
62182007Sroberto/*
6354359Sroberto * Keyid used for authenticated requests.  Obtained on the fly.
6454359Sroberto */
65132451Srobertou_long info_auth_keyid = 0;
6654359Sroberto
6754359Sroberto/*
68132451Sroberto * Type of key md5
6954359Sroberto */
7054359Sroberto#define	KEY_TYPE_MD5	4
7154359Sroberto
7254359Srobertostatic	int info_auth_keytype = KEY_TYPE_MD5;	/* MD5 */
7354359Srobertou_long	current_time;		/* needed by authkeys; not used */
7454359Sroberto
7554359Sroberto/*
7654359Sroberto * Flag which indicates we should always send authenticated requests
7754359Sroberto */
7854359Srobertoint always_auth = 0;
7954359Sroberto
8054359Sroberto/*
8154359Sroberto * Flag which indicates raw mode output.
8254359Sroberto */
8354359Srobertoint rawmode = 0;
8454359Sroberto
8554359Sroberto/*
8654359Sroberto * Packet version number we use
8754359Sroberto */
8854359Srobertou_char pktversion = NTP_OLDVERSION + 1;
8954359Sroberto
9054359Sroberto/*
9154359Sroberto * Don't jump if no set jmp.
9254359Sroberto */
9354359Srobertovolatile int jump = 0;
9454359Sroberto
9554359Sroberto/*
9654359Sroberto * Format values
9754359Sroberto */
9854359Sroberto#define	PADDING	0
9954359Sroberto#define	TS	1	/* time stamp */
10054359Sroberto#define	FL	2	/* l_fp type value */
10154359Sroberto#define	FU	3	/* u_fp type value */
10254359Sroberto#define	FS	4	/* s_fp type value */
10354359Sroberto#define	UI	5	/* unsigned integer value */
10454359Sroberto#define	SI	6	/* signed integer value */
10554359Sroberto#define	HA	7	/* host address */
10654359Sroberto#define	NA	8	/* network address */
10754359Sroberto#define	ST	9	/* string value */
10854359Sroberto#define	RF	10	/* refid (sometimes string, sometimes not) */
10954359Sroberto#define	LP	11	/* leap (print in binary) */
11054359Sroberto#define	OC	12	/* integer, print in octal */
11154359Sroberto#define	MD	13	/* mode */
11254359Sroberto#define	AR	14	/* array of times */
11354359Sroberto#define FX	15	/* test flags */
11454359Sroberto#define	EOV	255	/* end of table */
11554359Sroberto
11654359Sroberto
11754359Sroberto/*
11854359Sroberto * System variable values.  The array can be indexed by
11954359Sroberto * the variable index to find the textual name.
12054359Sroberto */
12154359Srobertostruct ctl_var sys_var[] = {
12254359Sroberto	{ 0,		PADDING, "" },		/* 0 */
12354359Sroberto	{ CS_LEAP,	LP,	"leap" },	/* 1 */
12454359Sroberto	{ CS_STRATUM,	UI,	"stratum" },	/* 2 */
12554359Sroberto	{ CS_PRECISION,	SI,	"precision" },	/* 3 */
12654359Sroberto	{ CS_ROOTDELAY,	FS,	"rootdelay" },	/* 4 */
12754359Sroberto	{ CS_ROOTDISPERSION, FU, "rootdispersion" }, /* 5 */
12854359Sroberto	{ CS_REFID,	RF,	"refid" },	/* 6 */
12954359Sroberto	{ CS_REFTIME,	TS,	"reftime" },	/* 7 */
13054359Sroberto	{ CS_POLL,	UI,	"poll" },	/* 8 */
13154359Sroberto	{ CS_PEERID,	UI,	"peer" },	/* 9 */
13254359Sroberto	{ CS_STATE,	UI,	"state" },	/* 10 */
13382498Sroberto	{ CS_OFFSET,	FL,	"offset" },	/* 11 */
13454359Sroberto	{ CS_DRIFT,	FS,	"frequency" },	/* 12 */
13582498Sroberto	{ CS_JITTER,	FU,	"jitter" },	/* 13 */
13654359Sroberto	{ CS_CLOCK,	TS,	"clock" },	/* 14 */
13754359Sroberto	{ CS_PROCESSOR,	ST,	"processor" },	/* 15 */
13854359Sroberto	{ CS_SYSTEM,	ST,	"system" },	/* 16 */
13982498Sroberto	{ CS_VERSION,	ST,	"version" },	/* 17 */
14082498Sroberto	{ CS_STABIL,	FS,	"stability" },	/* 18 */
14182498Sroberto	{ CS_VARLIST,	ST,	"sys_var_list" }, /* 19 */
14254359Sroberto	{ 0,		EOV,	""	}
14354359Sroberto};
14454359Sroberto
14554359Sroberto
14654359Sroberto/*
14754359Sroberto * Peer variable list
14854359Sroberto */
14954359Srobertostruct ctl_var peer_var[] = {
15054359Sroberto	{ 0,		PADDING, "" },		/* 0 */
15154359Sroberto	{ CP_CONFIG,	UI,	"config" },	/* 1 */
15254359Sroberto	{ CP_AUTHENABLE, UI,	"authenable" },	/* 2 */
15354359Sroberto	{ CP_AUTHENTIC,	UI,	"authentic" },	/* 3 */
15454359Sroberto	{ CP_SRCADR,	HA,	"srcadr" },	/* 4 */
15554359Sroberto	{ CP_SRCPORT,	UI,	"srcport" },	/* 5 */
15654359Sroberto	{ CP_DSTADR,	NA,	"dstadr" },	/* 6 */
15754359Sroberto	{ CP_DSTPORT,	UI,	"dstport" },	/* 7 */
15854359Sroberto	{ CP_LEAP,	LP,	"leap" },	/* 8 */
15954359Sroberto	{ CP_HMODE,	MD,	"hmode" },	/* 9 */
16054359Sroberto	{ CP_STRATUM,	UI,	"stratum" },	/* 10 */
16154359Sroberto	{ CP_PPOLL,	UI,	"ppoll" },	/* 11 */
16254359Sroberto	{ CP_HPOLL,	UI,	"hpoll" },	/* 12 */
16354359Sroberto	{ CP_PRECISION,	SI,	"precision" },	/* 13 */
16454359Sroberto	{ CP_ROOTDELAY,	FS,	"rootdelay" },	/* 14 */
16554359Sroberto	{ CP_ROOTDISPERSION, FU, "rootdispersion" }, /* 15 */
16654359Sroberto	{ CP_REFID,	RF,	"refid" },	/* 16 */
16754359Sroberto	{ CP_REFTIME,	TS,	"reftime" },	/* 17 */
16854359Sroberto	{ CP_ORG,	TS,	"org" },	/* 18 */
16954359Sroberto	{ CP_REC,	TS,	"rec" },	/* 19 */
17054359Sroberto	{ CP_XMT,	TS,	"xmt" },	/* 20 */
17154359Sroberto	{ CP_REACH,	OC,	"reach" },	/* 21 */
172182007Sroberto	{ CP_UNREACH,	UI,	"unreach" },	/* 22 */
17354359Sroberto	{ CP_TIMER,	UI,	"timer" },	/* 23 */
17454359Sroberto	{ CP_DELAY,	FS,	"delay" },	/* 24 */
17554359Sroberto	{ CP_OFFSET,	FL,	"offset" },	/* 25 */
17654359Sroberto	{ CP_JITTER,	FU,	"jitter" },	/* 26 */
17754359Sroberto	{ CP_DISPERSION, FU,	"dispersion" },	/* 27 */
17854359Sroberto	{ CP_KEYID,	UI,	"keyid" },	/* 28 */
17954359Sroberto	{ CP_FILTDELAY,	AR,	"filtdelay" },	/* 29 */
18054359Sroberto	{ CP_FILTOFFSET, AR,	"filtoffset" },	/* 30 */
18154359Sroberto	{ CP_PMODE,	ST,	"pmode" },	/* 31 */
18254359Sroberto	{ CP_RECEIVED,	UI,	"received" },	/* 32 */
18354359Sroberto	{ CP_SENT,	UI,	"sent" },	/* 33 */
18454359Sroberto	{ CP_FILTERROR,	AR,	"filtdisp" },	/* 34 */
18554359Sroberto	{ CP_FLASH,     FX,	"flash" },	/* 35 */
18682498Sroberto	{ CP_TTL,	UI,	"ttl" },	/* 36 */
18754359Sroberto	/*
18854359Sroberto	 * These are duplicate entries so that we can
18954359Sroberto	 * process deviant version of the ntp protocol.
19054359Sroberto	 */
19154359Sroberto	{ CP_SRCADR,	HA,	"peeraddr" },	/* 4 */
19254359Sroberto	{ CP_SRCPORT,	UI,	"peerport" },	/* 5 */
19354359Sroberto	{ CP_PPOLL,	UI,	"peerpoll" },	/* 11 */
19454359Sroberto	{ CP_HPOLL,	UI,	"hostpoll" },	/* 12 */
19554359Sroberto	{ CP_FILTERROR,	AR,	"filterror" },	/* 34 */
19654359Sroberto	{ 0,		EOV,	""	}
19754359Sroberto};
19854359Sroberto
19954359Sroberto
20054359Sroberto/*
20154359Sroberto * Clock variable list
20254359Sroberto */
20354359Srobertostruct ctl_var clock_var[] = {
20454359Sroberto	{ 0,		PADDING, "" },		/* 0 */
20554359Sroberto	{ CC_TYPE,	UI,	"type" },	/* 1 */
20654359Sroberto	{ CC_TIMECODE,	ST,	"timecode" },	/* 2 */
20754359Sroberto	{ CC_POLL,	UI,	"poll" },	/* 3 */
20854359Sroberto	{ CC_NOREPLY,	UI,	"noreply" },	/* 4 */
20954359Sroberto	{ CC_BADFORMAT,	UI,	"badformat" },	/* 5 */
21054359Sroberto	{ CC_BADDATA,	UI,	"baddata" },	/* 6 */
21154359Sroberto	{ CC_FUDGETIME1, FL,	"fudgetime1" },	/* 7 */
21254359Sroberto	{ CC_FUDGETIME2, FL,	"fudgetime2" },	/* 8 */
21354359Sroberto	{ CC_FUDGEVAL1,	UI,	"stratum" },	/* 9 */
21454359Sroberto	{ CC_FUDGEVAL2,	RF,	"refid" },	/* 10 */
21554359Sroberto	{ CC_FLAGS,	UI,	"flags" },	/* 11 */
21654359Sroberto	{ CC_DEVICE,	ST,	"device" },	/* 12 */
21754359Sroberto	{ 0,		EOV,	""	}
21854359Sroberto};
21954359Sroberto
22054359Sroberto
22154359Sroberto/*
22254359Sroberto * flasher bits
22354359Sroberto */
22454359Srobertostatic const char *tstflagnames[] = {
225182007Sroberto	"pkt_dup",		/* TEST1 */
226182007Sroberto	"pkt_bogus",		/* TEST2 */
227182007Sroberto	"pkt_proto",		/* TEST3 */
228182007Sroberto	"pkt_denied",		/* TEST4 */
229182007Sroberto	"pkt_auth",		/* TEST5 */
230182007Sroberto	"pkt_synch",		/* TEST6 */
231182007Sroberto	"pkt_dist",		/* TEST7 */
232182007Sroberto	"pkt_autokey",		/* TEST8 */
233182007Sroberto	"pkt_crypto",		/* TEST9 */
234182007Sroberto	"peer_stratum",		/* TEST10 */
235182007Sroberto	"peer_dist",		/* TEST11 */
236182007Sroberto	"peer_loop",		/* TEST12 */
237182007Sroberto	"peer_unfit"		/* TEST13 */
23854359Sroberto};
23954359Sroberto
24054359Sroberto
24154359Srobertoint		ntpqmain	P((int,	char **));
24254359Sroberto/*
24354359Sroberto * Built in command handler declarations
24454359Sroberto */
24554359Srobertostatic	int	openhost	P((const char *));
24654359Srobertostatic	int	sendpkt		P((char *, int));
24754359Srobertostatic	int	getresponse	P((int, int, u_short *, int *, char **, int));
24854359Srobertostatic	int	sendrequest	P((int, int, int, int, char *));
24954359Srobertostatic	char *	tstflags	P((u_long));
25054359Srobertostatic	void	getcmds		P((void));
25154359Srobertostatic	RETSIGTYPE abortcmd	P((int));
25254359Srobertostatic	void	docmd		P((const char *));
25354359Srobertostatic	void	tokenize	P((const char *, char **, int *));
25454359Srobertostatic	int	findcmd		P((char *, struct xcmd *, struct xcmd *, struct xcmd **));
25554359Srobertostatic	int	getarg		P((char *, int, arg_v *));
25654359Srobertostatic	int	rtdatetolfp	P((char *, l_fp *));
25754359Srobertostatic	int	decodearr	P((char *, int *, l_fp *));
25854359Srobertostatic	void	help		P((struct parse *, FILE *));
25954359Sroberto#ifdef QSORT_USES_VOID_P
26054359Srobertostatic	int	helpsort	P((const void *, const void *));
26154359Sroberto#else
26254359Srobertostatic	int	helpsort	P((char **, char **));
26354359Sroberto#endif
26454359Srobertostatic	void	printusage	P((struct xcmd *, FILE *));
26554359Srobertostatic	void	timeout		P((struct parse *, FILE *));
26654359Srobertostatic	void	auth_delay	P((struct parse *, FILE *));
26754359Srobertostatic	void	host		P((struct parse *, FILE *));
26854359Srobertostatic	void	ntp_poll	P((struct parse *, FILE *));
26954359Srobertostatic	void	keyid		P((struct parse *, FILE *));
27054359Srobertostatic	void	keytype		P((struct parse *, FILE *));
27154359Srobertostatic	void	passwd		P((struct parse *, FILE *));
27254359Srobertostatic	void	hostnames	P((struct parse *, FILE *));
27354359Srobertostatic	void	setdebug	P((struct parse *, FILE *));
27454359Srobertostatic	void	quit		P((struct parse *, FILE *));
27554359Srobertostatic	void	version		P((struct parse *, FILE *));
27654359Srobertostatic	void	raw		P((struct parse *, FILE *));
27754359Srobertostatic	void	cooked		P((struct parse *, FILE *));
27854359Srobertostatic	void	authenticate	P((struct parse *, FILE *));
27954359Srobertostatic	void	ntpversion	P((struct parse *, FILE *));
28054359Srobertostatic	void	warning		P((const char *, const char *, const char *));
28154359Srobertostatic	void	error		P((const char *, const char *, const char *));
28254359Srobertostatic	u_long	getkeyid	P((const char *));
28354359Srobertostatic	void	atoascii	P((int, char *, char *));
28454359Srobertostatic	void	makeascii	P((int, char *, FILE *));
28554359Srobertostatic	void	rawprint	P((int, int, char *, int, FILE *));
28654359Srobertostatic	void	startoutput	P((void));
28754359Srobertostatic	void	output		P((FILE *, char *, char *));
28854359Srobertostatic	void	endoutput	P((FILE *));
28954359Srobertostatic	void	outputarr	P((FILE *, char *, int, l_fp *));
29054359Srobertostatic	void	cookedprint	P((int, int, char *, int, FILE *));
29154359Sroberto#ifdef QSORT_USES_VOID_P
29254359Srobertostatic	int	assoccmp	P((const void *, const void *));
29354359Sroberto#else
29454359Srobertostatic	int	assoccmp	P((struct association *, struct association *));
29554359Sroberto#endif /* sgi || bsdi */
29654359Sroberto
29754359Sroberto
29854359Sroberto/*
29954359Sroberto * Built-in commands we understand
30054359Sroberto */
30154359Srobertostruct xcmd builtins[] = {
302182007Sroberto	{ "?",		help,		{  OPT|NTP_STR, NO, NO, NO },
30354359Sroberto	  { "command", "", "", "" },
30454359Sroberto	  "tell the use and syntax of commands" },
305182007Sroberto	{ "help",	help,		{  OPT|NTP_STR, NO, NO, NO },
30654359Sroberto	  { "command", "", "", "" },
30754359Sroberto	  "tell the use and syntax of commands" },
308182007Sroberto	{ "timeout",	timeout,	{ OPT|NTP_UINT, NO, NO, NO },
30954359Sroberto	  { "msec", "", "", "" },
31054359Sroberto	  "set the primary receive time out" },
311182007Sroberto	{ "delay",	auth_delay,	{ OPT|NTP_INT, NO, NO, NO },
31254359Sroberto	  { "msec", "", "", "" },
31354359Sroberto	  "set the delay added to encryption time stamps" },
314182007Sroberto	{ "host",	host,		{ OPT|NTP_STR, OPT|NTP_STR, NO, NO },
315132451Sroberto	  { "-4|-6", "hostname", "", "" },
31654359Sroberto	  "specify the host whose NTP server we talk to" },
317182007Sroberto	{ "poll",	ntp_poll,	{ OPT|NTP_UINT, OPT|NTP_STR, NO, NO },
31854359Sroberto	  { "n", "verbose", "", "" },
31954359Sroberto	  "poll an NTP server in client mode `n' times" },
32054359Sroberto	{ "passwd",	passwd,		{ NO, NO, NO, NO },
32154359Sroberto	  { "", "", "", "" },
32254359Sroberto	  "specify a password to use for authenticated requests"},
323182007Sroberto	{ "hostnames",	hostnames,	{ OPT|NTP_STR, NO, NO, NO },
32454359Sroberto	  { "yes|no", "", "", "" },
32554359Sroberto	  "specify whether hostnames or net numbers are printed"},
326182007Sroberto	{ "debug",	setdebug,	{ OPT|NTP_STR, NO, NO, NO },
32754359Sroberto	  { "no|more|less", "", "", "" },
32854359Sroberto	  "set/change debugging level" },
32954359Sroberto	{ "quit",	quit,		{ NO, NO, NO, NO },
33054359Sroberto	  { "", "", "", "" },
33154359Sroberto	  "exit ntpq" },
33254359Sroberto	{ "exit",	quit,		{ NO, NO, NO, NO },
33354359Sroberto	  { "", "", "", "" },
33454359Sroberto	  "exit ntpq" },
335182007Sroberto	{ "keyid",	keyid,		{ OPT|NTP_UINT, NO, NO, NO },
33654359Sroberto	  { "key#", "", "", "" },
33754359Sroberto	  "set keyid to use for authenticated requests" },
33854359Sroberto	{ "version",	version,	{ NO, NO, NO, NO },
33954359Sroberto	  { "", "", "", "" },
34054359Sroberto	  "print version number" },
34154359Sroberto	{ "raw",	raw,		{ NO, NO, NO, NO },
34254359Sroberto	  { "", "", "", "" },
34354359Sroberto	  "do raw mode variable output" },
34454359Sroberto	{ "cooked",	cooked,		{ NO, NO, NO, NO },
34554359Sroberto	  { "", "", "", "" },
34654359Sroberto	  "do cooked mode variable output" },
347182007Sroberto	{ "authenticate", authenticate,	{ OPT|NTP_STR, NO, NO, NO },
34854359Sroberto	  { "yes|no", "", "", "" },
34954359Sroberto	  "always authenticate requests to this server" },
350182007Sroberto	{ "ntpversion",	ntpversion,	{ OPT|NTP_UINT, NO, NO, NO },
35154359Sroberto	  { "version number", "", "", "" },
35254359Sroberto	  "set the NTP version number to use for requests" },
353182007Sroberto	{ "keytype",	keytype,	{ OPT|NTP_STR, NO, NO, NO },
35454359Sroberto	  { "key type (md5|des)", "", "", "" },
35554359Sroberto	  "set key type to use for authenticated requests (des|md5)" },
35654359Sroberto	{ 0,		0,		{ NO, NO, NO, NO },
35754359Sroberto	  { "", "", "", "" }, "" }
35854359Sroberto};
35954359Sroberto
36054359Sroberto
36154359Sroberto/*
36254359Sroberto * Default values we use.
36354359Sroberto */
36454359Sroberto#define	DEFTIMEOUT	(5)		/* 5 second time out */
36554359Sroberto#define	DEFSTIMEOUT	(2)		/* 2 second time out after first */
36654359Sroberto#define	DEFDELAY	0x51EB852	/* 20 milliseconds, l_fp fraction */
367132451Sroberto#define	DEFHOST		"localhost"	/* default host name */
36854359Sroberto#define	LENHOSTNAME	256		/* host name is 256 characters long */
36954359Sroberto#define	MAXCMDS		100		/* maximum commands on cmd line */
37054359Sroberto#define	MAXHOSTS	200		/* maximum hosts on cmd line */
37154359Sroberto#define	MAXLINE		512		/* maximum line length */
37254359Sroberto#define	MAXTOKENS	(1+MAXARGS+2)	/* maximum number of usable tokens */
37354359Sroberto#define	MAXVARLEN	256		/* maximum length of a variable name */
37454359Sroberto#define	MAXVALLEN	400		/* maximum length of a variable value */
37554359Sroberto#define	MAXOUTLINE	72		/* maximum length of an output line */
376182007Sroberto#define SCREENWIDTH     76              /* nominal screen width in columns */
37754359Sroberto
37854359Sroberto/*
37954359Sroberto * Some variables used and manipulated locally
38054359Sroberto */
38154359Srobertostruct timeval tvout = { DEFTIMEOUT, 0 };	/* time out for reads */
38254359Srobertostruct timeval tvsout = { DEFSTIMEOUT, 0 };	/* secondary time out */
38354359Srobertol_fp delay_time;				/* delay time */
38454359Srobertochar currenthost[LENHOSTNAME];			/* current host name */
38554359Srobertostruct sockaddr_in hostaddr = { 0 };		/* host address */
38654359Srobertoint showhostnames = 1;				/* show host names by default */
38754359Sroberto
388132451Srobertoint ai_fam_templ;				/* address family */
389132451Srobertoint ai_fam_default;				/* default address family */
390132451SrobertoSOCKET sockfd;					/* fd socket is opened on */
39154359Srobertoint havehost = 0;				/* set to 1 when host open */
392132451Srobertoint s_port = 0;
39354359Srobertostruct servent *server_entry = NULL;		/* server entry for ntp */
39454359Sroberto
39554359Sroberto#ifdef SYS_WINNT
39654359SrobertoDWORD NumberOfBytesWritten;
39754359Sroberto
39854359SrobertoHANDLE	TimerThreadHandle = NULL;	/* 1998/06/03 - Used in ntplib/machines.c */
39954359Srobertovoid timer(void)	{  ; };	/* 1998/06/03 - Used in ntplib/machines.c */
40054359Sroberto
40154359Sroberto#endif /* SYS_WINNT */
40254359Sroberto
40354359Sroberto/*
40454359Sroberto * Sequence number used for requests.  It is incremented before
40554359Sroberto * it is used.
40654359Sroberto */
40754359Srobertou_short sequence;
40854359Sroberto
40954359Sroberto/*
41054359Sroberto * Holds data returned from queries.  Declare buffer long to be sure of
41154359Sroberto * alignment.
41254359Sroberto */
41354359Sroberto#define	MAXFRAGS	24		/* maximum number of fragments */
41454359Sroberto#define	DATASIZE	(MAXFRAGS*480)	/* maximum amount of data */
41554359Srobertolong pktdata[DATASIZE/sizeof(long)];
41654359Sroberto
41754359Sroberto/*
41854359Sroberto * Holds association data for use with the &n operator.
41954359Sroberto */
42054359Srobertostruct association assoc_cache[MAXASSOC];
42154359Srobertoint numassoc = 0;		/* number of cached associations */
42254359Sroberto
42354359Sroberto/*
42454359Sroberto * For commands typed on the command line (with the -c option)
42554359Sroberto */
42654359Srobertoint numcmds = 0;
42754359Srobertoconst char *ccmds[MAXCMDS];
42854359Sroberto#define	ADDCMD(cp)	if (numcmds < MAXCMDS) ccmds[numcmds++] = (cp)
42954359Sroberto
43054359Sroberto/*
43154359Sroberto * When multiple hosts are specified.
43254359Sroberto */
43354359Srobertoint numhosts = 0;
43454359Srobertoconst char *chosts[MAXHOSTS];
43554359Sroberto#define	ADDHOST(cp)	if (numhosts < MAXHOSTS) chosts[numhosts++] = (cp)
43654359Sroberto
43754359Sroberto/*
43854359Sroberto * Error codes for internal use
43954359Sroberto */
44054359Sroberto#define	ERR_UNSPEC		256
44154359Sroberto#define	ERR_INCOMPLETE	257
44254359Sroberto#define	ERR_TIMEOUT		258
44354359Sroberto#define	ERR_TOOMUCH		259
44454359Sroberto
44554359Sroberto/*
44654359Sroberto * Macro definitions we use
44754359Sroberto */
44854359Sroberto#define	ISSPACE(c)	((c) == ' ' || (c) == '\t')
44954359Sroberto#define	ISEOL(c)	((c) == '\n' || (c) == '\r' || (c) == '\0')
45054359Sroberto#define	STREQ(a, b)	(*(a) == *(b) && strcmp((a), (b)) == 0)
45154359Sroberto
45254359Sroberto/*
45354359Sroberto * Jump buffer for longjumping back to the command level
45454359Sroberto */
45554359Srobertojmp_buf interrupt_buf;
45654359Sroberto
45754359Sroberto/*
45854359Sroberto * Points at file being currently printed into
45954359Sroberto */
46054359SrobertoFILE *current_output;
46154359Sroberto
46254359Sroberto/*
46354359Sroberto * Command table imported from ntpdc_ops.c
46454359Sroberto */
46554359Srobertoextern struct xcmd opcmds[];
46654359Sroberto
46754359Srobertochar *progname;
46854359Srobertovolatile int debug;
46954359Sroberto
47054359Sroberto#ifdef NO_MAIN_ALLOWED
47154359SrobertoCALL(ntpq,"ntpq",ntpqmain);
47254359Sroberto
47354359Srobertovoid clear_globals(void)
47454359Sroberto{
47554359Sroberto    extern int ntp_optind;
47654359Sroberto    showhostnames = 0;				/* don'tshow host names by default */
47754359Sroberto    ntp_optind = 0;
47854359Sroberto    server_entry = NULL;            /* server entry for ntp */
47954359Sroberto    havehost = 0;				/* set to 1 when host open */
48054359Sroberto    numassoc = 0;		/* number of cached associations */
48154359Sroberto    numcmds = 0;
48254359Sroberto    numhosts = 0;
48354359Sroberto}
48454359Sroberto#endif
48554359Sroberto
48654359Sroberto/*
48754359Sroberto * main - parse arguments and handle options
48854359Sroberto */
48954359Sroberto#ifndef NO_MAIN_ALLOWED
49054359Srobertoint
49154359Srobertomain(
49254359Sroberto	int argc,
49354359Sroberto	char *argv[]
49454359Sroberto	)
49554359Sroberto{
49654359Sroberto	return ntpqmain(argc, argv);
49754359Sroberto}
49854359Sroberto#endif
49954359Sroberto
50054359Srobertoint
50154359Srobertontpqmain(
50254359Sroberto	int argc,
50354359Sroberto	char *argv[]
50454359Sroberto	)
50554359Sroberto{
50654359Sroberto	extern int ntp_optind;
50754359Sroberto
508182007Sroberto#ifdef SYS_VXWORKS
509182007Sroberto	clear_globals();
510182007Sroberto	taskPrioritySet(taskIdSelf(), 100 );
51154359Sroberto#endif
512182007Sroberto
51354359Sroberto	delay_time.l_ui = 0;
51454359Sroberto	delay_time.l_uf = DEFDELAY;
51554359Sroberto
516132451Sroberto#ifdef SYS_WINNT
517132451Sroberto	if (!Win32InitSockets())
518132451Sroberto	{
519132451Sroberto		fprintf(stderr, "No useable winsock.dll:");
520132451Sroberto		exit(1);
521132451Sroberto	}
522132451Sroberto#endif /* SYS_WINNT */
523132451Sroberto
524132451Sroberto	/* Check to see if we have IPv6. Otherwise force the -4 flag */
525132451Sroberto	if (isc_net_probeipv6() != ISC_R_SUCCESS) {
526132451Sroberto		ai_fam_default = AF_INET;
527132451Sroberto	}
528132451Sroberto
52954359Sroberto	progname = argv[0];
530182007Sroberto
531182007Sroberto	{
532182007Sroberto		int optct = optionProcess(&ntpqOptions, argc, argv);
533182007Sroberto		argc -= optct;
534182007Sroberto		argv += optct;
535182007Sroberto	}
536182007Sroberto
537182007Sroberto	switch (WHICH_IDX_IPV4) {
538182007Sroberto	    case INDEX_OPT_IPV4:
539182007Sroberto		ai_fam_templ = AF_INET;
540182007Sroberto		break;
541182007Sroberto	    case INDEX_OPT_IPV6:
542182007Sroberto		ai_fam_templ = AF_INET6;
543182007Sroberto		break;
544182007Sroberto	    default:
545182007Sroberto		ai_fam_templ = ai_fam_default;
546182007Sroberto		break;
547182007Sroberto	}
548182007Sroberto
549182007Sroberto	if (HAVE_OPT(COMMAND)) {
550182007Sroberto		int		cmdct = STACKCT_OPT( COMMAND );
551182007Sroberto		const char**	cmds  = STACKLST_OPT( COMMAND );
552182007Sroberto
553182007Sroberto		while (cmdct-- > 0) {
554182007Sroberto			ADDCMD(*cmds++);
555182007Sroberto		}
556182007Sroberto	}
557182007Sroberto
558182007Sroberto	debug = DESC(DEBUG_LEVEL).optOccCt;
559182007Sroberto
560182007Sroberto	if (HAVE_OPT(INTERACTIVE)) {
561182007Sroberto		interactive = 1;
562182007Sroberto	}
563182007Sroberto
564182007Sroberto	if (HAVE_OPT(NUMERIC)) {
565182007Sroberto		showhostnames = 0;
566182007Sroberto	}
567182007Sroberto
568182007Sroberto	if (HAVE_OPT(PEERS)) {
569182007Sroberto		ADDCMD("peers");
570182007Sroberto	}
571182007Sroberto
572182007Sroberto#if 0
573132451Sroberto	while ((c = ntp_getopt(argc, argv, "46c:dinp")) != EOF)
57454359Sroberto	    switch (c) {
575132451Sroberto		case '4':
576132451Sroberto		    ai_fam_templ = AF_INET;
577132451Sroberto		    break;
578132451Sroberto		case '6':
579132451Sroberto		    ai_fam_templ = AF_INET6;
580132451Sroberto		    break;
58154359Sroberto		case 'c':
58254359Sroberto		    ADDCMD(ntp_optarg);
58354359Sroberto		    break;
58454359Sroberto		case 'd':
58554359Sroberto		    ++debug;
58654359Sroberto		    break;
58754359Sroberto		case 'i':
58854359Sroberto		    interactive = 1;
58954359Sroberto		    break;
59054359Sroberto		case 'n':
59154359Sroberto		    showhostnames = 0;
59254359Sroberto		    break;
59354359Sroberto		case 'p':
59454359Sroberto		    ADDCMD("peers");
59554359Sroberto		    break;
59654359Sroberto		default:
59754359Sroberto		    errflg++;
59854359Sroberto		    break;
59954359Sroberto	    }
60054359Sroberto	if (errflg) {
60154359Sroberto		(void) fprintf(stderr,
602132451Sroberto			       "usage: %s [-46dinp] [-c cmd] host ...\n",
60354359Sroberto			       progname);
60454359Sroberto		exit(2);
60554359Sroberto	}
606182007Sroberto#endif
60754359Sroberto	if (ntp_optind == argc) {
60854359Sroberto		ADDHOST(DEFHOST);
60954359Sroberto	} else {
61054359Sroberto		for (; ntp_optind < argc; ntp_optind++)
61154359Sroberto		    ADDHOST(argv[ntp_optind]);
61254359Sroberto	}
61354359Sroberto
61454359Sroberto	if (numcmds == 0 && interactive == 0
61554359Sroberto	    && isatty(fileno(stdin)) && isatty(fileno(stderr))) {
61654359Sroberto		interactive = 1;
61754359Sroberto	}
61854359Sroberto
61954359Sroberto#ifndef SYS_WINNT /* Under NT cannot handle SIGINT, WIN32 spawns a handler */
62054359Sroberto	if (interactive)
62154359Sroberto	    (void) signal_no_reset(SIGINT, abortcmd);
62254359Sroberto#endif /* SYS_WINNT */
62354359Sroberto
62454359Sroberto	if (numcmds == 0) {
62554359Sroberto		(void) openhost(chosts[0]);
62654359Sroberto		getcmds();
62754359Sroberto	} else {
62854359Sroberto		int ihost;
62954359Sroberto		int icmd;
63054359Sroberto
63154359Sroberto		for (ihost = 0; ihost < numhosts; ihost++) {
63254359Sroberto			if (openhost(chosts[ihost]))
63354359Sroberto			    for (icmd = 0; icmd < numcmds; icmd++)
63454359Sroberto				docmd(ccmds[icmd]);
63554359Sroberto		}
63654359Sroberto	}
63754359Sroberto#ifdef SYS_WINNT
63854359Sroberto	WSACleanup();
63954359Sroberto#endif /* SYS_WINNT */
64054359Sroberto	return 0;
64154359Sroberto}
64254359Sroberto
64354359Sroberto
64454359Sroberto/*
64554359Sroberto * openhost - open a socket to a host
64654359Sroberto */
64754359Srobertostatic int
64854359Srobertoopenhost(
64954359Sroberto	const char *hname
65054359Sroberto	)
65154359Sroberto{
65254359Sroberto	char temphost[LENHOSTNAME];
653132451Sroberto	int a_info, i;
654132451Sroberto	struct addrinfo hints, *ai = NULL;
655132451Sroberto	register const char *cp;
656132451Sroberto	char name[LENHOSTNAME];
657132451Sroberto	char service[5];
65854359Sroberto
659132451Sroberto	/*
660132451Sroberto	 * We need to get by the [] if they were entered
661132451Sroberto	 */
662132451Sroberto
663132451Sroberto	cp = hname;
664132451Sroberto
665132451Sroberto	if(*cp == '[') {
666132451Sroberto		cp++;
667132451Sroberto		for(i = 0; *cp != ']'; cp++, i++)
668132451Sroberto			name[i] = *cp;
669132451Sroberto	name[i] = '\0';
670132451Sroberto	hname = name;
67154359Sroberto	}
67254359Sroberto
673132451Sroberto	/*
674132451Sroberto	 * First try to resolve it as an ip address and if that fails,
675132451Sroberto	 * do a fullblown (dns) lookup. That way we only use the dns
676132451Sroberto	 * when it is needed and work around some implementations that
677132451Sroberto	 * will return an "IPv4-mapped IPv6 address" address if you
678132451Sroberto	 * give it an IPv4 address to lookup.
679132451Sroberto	 */
680132451Sroberto	strcpy(service, "ntp");
681132451Sroberto	memset((char *)&hints, 0, sizeof(struct addrinfo));
682132451Sroberto	hints.ai_family = ai_fam_templ;
683132451Sroberto	hints.ai_protocol = IPPROTO_UDP;
684132451Sroberto	hints.ai_socktype = SOCK_DGRAM;
685132451Sroberto	hints.ai_flags = AI_NUMERICHOST;
686132451Sroberto
687132451Sroberto	a_info = getaddrinfo(hname, service, &hints, &ai);
688182007Sroberto	if (a_info == EAI_NONAME
689182007Sroberto#ifdef EAI_NODATA
690182007Sroberto	    || a_info == EAI_NODATA
691182007Sroberto#endif
692182007Sroberto	   ) {
693132451Sroberto		hints.ai_flags = AI_CANONNAME;
694132451Sroberto#ifdef AI_ADDRCONFIG
695132451Sroberto		hints.ai_flags |= AI_ADDRCONFIG;
696132451Sroberto#endif
697132451Sroberto		a_info = getaddrinfo(hname, service, &hints, &ai);
698132451Sroberto	}
699132451Sroberto	/* Some older implementations don't like AI_ADDRCONFIG. */
700132451Sroberto	if (a_info == EAI_BADFLAGS) {
701132451Sroberto		hints.ai_flags = AI_CANONNAME;
702132451Sroberto		a_info = getaddrinfo(hname, service, &hints, &ai);
703132451Sroberto	}
704132451Sroberto	if (a_info != 0) {
705132451Sroberto		(void) fprintf(stderr, "%s\n", gai_strerror(a_info));
706132451Sroberto		return 0;
707132451Sroberto	}
708132451Sroberto
709132451Sroberto	if (ai->ai_canonname == NULL) {
710132451Sroberto		strncpy(temphost, stoa((struct sockaddr_storage *)ai->ai_addr),
711132451Sroberto		    LENHOSTNAME);
712132451Sroberto		temphost[LENHOSTNAME-1] = '\0';
713132451Sroberto
714132451Sroberto	} else {
715132451Sroberto		strncpy(temphost, ai->ai_canonname, LENHOSTNAME);
716132451Sroberto		temphost[LENHOSTNAME-1] = '\0';
717132451Sroberto	}
718132451Sroberto
71954359Sroberto	if (debug > 2)
72054359Sroberto	    printf("Opening host %s\n", temphost);
72154359Sroberto
72254359Sroberto	if (havehost == 1) {
72354359Sroberto		if (debug > 2)
72454359Sroberto		    printf("Closing old host %s\n", currenthost);
72554359Sroberto		(void) closesocket(sockfd);
72654359Sroberto		havehost = 0;
72754359Sroberto	}
72854359Sroberto	(void) strcpy(currenthost, temphost);
72954359Sroberto
730132451Sroberto	/* port maps to the same location in both families */
731132451Sroberto	s_port = ((struct sockaddr_in6 *)ai->ai_addr)->sin6_port;
732132451Sroberto#ifdef SYS_VXWORKS
733132451Sroberto	((struct sockaddr_in6 *)&hostaddr)->sin6_port = htons(SERVER_PORT_NUM);
734132451Sroberto	if (ai->ai_family == AF_INET)
735132451Sroberto		*(struct sockaddr_in *)&hostaddr=
736132451Sroberto			*((struct sockaddr_in *)ai->ai_addr);
737132451Sroberto	else
738132451Sroberto		*(struct sockaddr_in6 *)&hostaddr=
739132451Sroberto			*((struct sockaddr_in6 *)ai->ai_addr);
740132451Sroberto#endif /* SYS_VXWORKS */
74154359Sroberto
74254359Sroberto#ifdef SYS_WINNT
74354359Sroberto	{
74454359Sroberto		int optionValue = SO_SYNCHRONOUS_NONALERT;
74554359Sroberto		int err;
746182007Sroberto
74754359Sroberto		err = setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, (char *)&optionValue, sizeof(optionValue));
74854359Sroberto		if (err != NO_ERROR) {
74954359Sroberto			(void) fprintf(stderr, "cannot open nonoverlapped sockets\n");
75054359Sroberto			exit(1);
75154359Sroberto		}
75254359Sroberto	}
753132451Sroberto#endif /* SYS_WINNT */
75454359Sroberto
755132451Sroberto	sockfd = socket(ai->ai_family, SOCK_DGRAM, 0);
75654359Sroberto	if (sockfd == INVALID_SOCKET) {
75754359Sroberto		error("socket", "", "");
75854359Sroberto	}
75954359Sroberto
76054359Sroberto
76154359Sroberto#ifdef NEED_RCVBUF_SLOP
76254359Sroberto# ifdef SO_RCVBUF
76354359Sroberto	{ int rbufsize = DATASIZE + 2048;	/* 2K for slop */
76454359Sroberto	if (setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF,
76554359Sroberto		       &rbufsize, sizeof(int)) == -1)
76654359Sroberto	    error("setsockopt", "", "");
76754359Sroberto	}
76854359Sroberto# endif
76954359Sroberto#endif
77054359Sroberto
771132451Sroberto#ifdef SYS_VXWORKS
77254359Sroberto	if (connect(sockfd, (struct sockaddr *)&hostaddr,
77354359Sroberto		    sizeof(hostaddr)) == -1)
774132451Sroberto#else
775132451Sroberto	if (connect(sockfd, (struct sockaddr *)ai->ai_addr,
776132451Sroberto		    ai->ai_addrlen) == -1)
777132451Sroberto#endif /* SYS_VXWORKS */
77854359Sroberto	    error("connect", "", "");
779132451Sroberto	if (a_info == 0)
780132451Sroberto		freeaddrinfo(ai);
78154359Sroberto	havehost = 1;
78254359Sroberto	return 1;
78354359Sroberto}
78454359Sroberto
78554359Sroberto
78654359Sroberto/* XXX ELIMINATE sendpkt similar in ntpq.c, ntpdc.c, ntp_io.c, ntptrace.c */
78754359Sroberto/*
78854359Sroberto * sendpkt - send a packet to the remote host
78954359Sroberto */
79054359Srobertostatic int
79154359Srobertosendpkt(
79254359Sroberto	char *xdata,
79354359Sroberto	int xdatalen
79454359Sroberto	)
79554359Sroberto{
79654359Sroberto	if (debug >= 3)
79754359Sroberto	    printf("Sending %d octets\n", xdatalen);
79854359Sroberto
79954359Sroberto
80082498Sroberto	if (send(sockfd, xdata, (size_t)xdatalen, 0) == -1) {
80154359Sroberto		warning("write to %s failed", currenthost, "");
80254359Sroberto		return -1;
80354359Sroberto	}
80454359Sroberto
80554359Sroberto	if (debug >= 4) {
80654359Sroberto		int first = 8;
80754359Sroberto		printf("Packet data:\n");
80854359Sroberto		while (xdatalen-- > 0) {
80954359Sroberto			if (first-- == 0) {
81054359Sroberto				printf("\n");
81154359Sroberto				first = 7;
81254359Sroberto			}
81354359Sroberto			printf(" %02x", *xdata++ & 0xff);
81454359Sroberto		}
81554359Sroberto		printf("\n");
81654359Sroberto	}
81754359Sroberto	return 0;
81854359Sroberto}
81954359Sroberto
82054359Sroberto
82154359Sroberto
82254359Sroberto/*
82354359Sroberto * getresponse - get a (series of) response packet(s) and return the data
82454359Sroberto */
82554359Srobertostatic int
82654359Srobertogetresponse(
82754359Sroberto	int opcode,
82854359Sroberto	int associd,
82954359Sroberto	u_short *rstatus,
83054359Sroberto	int *rsize,
83154359Sroberto	char **rdata,
83254359Sroberto	int timeo
83354359Sroberto	)
83454359Sroberto{
83554359Sroberto	struct ntp_control rpkt;
83654359Sroberto	struct timeval tvo;
83754359Sroberto	u_short offsets[MAXFRAGS+1];
83854359Sroberto	u_short counts[MAXFRAGS+1];
83954359Sroberto	u_short offset;
84054359Sroberto	u_short count;
84154359Sroberto	int numfrags;
84254359Sroberto	int seenlastfrag;
84354359Sroberto	fd_set fds;
84454359Sroberto	int n;
84554359Sroberto
84654359Sroberto	/*
84754359Sroberto	 * This is pretty tricky.  We may get between 1 and MAXFRAG packets
84854359Sroberto	 * back in response to the request.  We peel the data out of
84954359Sroberto	 * each packet and collect it in one long block.  When the last
85054359Sroberto	 * packet in the sequence is received we'll know how much data we
85154359Sroberto	 * should have had.  Note we use one long time out, should reconsider.
85254359Sroberto	 */
85354359Sroberto	*rsize = 0;
85454359Sroberto	if (rstatus)
85554359Sroberto	    *rstatus = 0;
85654359Sroberto	*rdata = (char *)pktdata;
85754359Sroberto
85854359Sroberto	numfrags = 0;
85954359Sroberto	seenlastfrag = 0;
86054359Sroberto
86154359Sroberto	FD_ZERO(&fds);
86254359Sroberto
86354359Sroberto    again:
86454359Sroberto	if (numfrags == 0)
86554359Sroberto	    tvo = tvout;
86654359Sroberto	else
86754359Sroberto	    tvo = tvsout;
86854359Sroberto
86954359Sroberto	FD_SET(sockfd, &fds);
87054359Sroberto	n = select(sockfd+1, &fds, (fd_set *)0, (fd_set *)0, &tvo);
87154359Sroberto
87254359Sroberto#if 0
87354359Sroberto	if (debug >= 1)
87454359Sroberto	    printf("select() returns %d\n", n);
87554359Sroberto#endif
87654359Sroberto
87754359Sroberto	if (n == -1) {
87854359Sroberto		warning("select fails", "", "");
87954359Sroberto		return -1;
88054359Sroberto	}
88154359Sroberto	if (n == 0) {
88254359Sroberto		/*
88354359Sroberto		 * Timed out.  Return what we have
88454359Sroberto		 */
88554359Sroberto		if (numfrags == 0) {
88654359Sroberto			if (timeo)
88754359Sroberto			    (void) fprintf(stderr,
88854359Sroberto					   "%s: timed out, nothing received\n",
88954359Sroberto					   currenthost);
89054359Sroberto			return ERR_TIMEOUT;
89154359Sroberto		} else {
89254359Sroberto			if (timeo)
89354359Sroberto			    (void) fprintf(stderr,
89454359Sroberto					   "%s: timed out with incomplete data\n",
89554359Sroberto					   currenthost);
89654359Sroberto			if (debug) {
89754359Sroberto				printf("Received fragments:\n");
89854359Sroberto				for (n = 0; n < numfrags; n++)
89954359Sroberto				    printf("%4d %d\n", offsets[n],
90054359Sroberto					   counts[n]);
90154359Sroberto				if (seenlastfrag)
90254359Sroberto				    printf("last fragment received\n");
90354359Sroberto				else
90454359Sroberto				    printf("last fragment not received\n");
90554359Sroberto			}
90654359Sroberto			return ERR_INCOMPLETE;
90754359Sroberto		}
90854359Sroberto	}
90954359Sroberto
91054359Sroberto	n = recv(sockfd, (char *)&rpkt, sizeof(rpkt), 0);
91154359Sroberto	if (n == -1) {
91254359Sroberto		warning("read", "", "");
91354359Sroberto		return -1;
91454359Sroberto	}
91554359Sroberto
91654359Sroberto	if (debug >= 4) {
91754359Sroberto		int len = n, first = 8;
91854359Sroberto		char *data = (char *)&rpkt;
91954359Sroberto
92054359Sroberto		printf("Packet data:\n");
92154359Sroberto		while (len-- > 0) {
92254359Sroberto			if (first-- == 0) {
92354359Sroberto				printf("\n");
92454359Sroberto				first = 7;
92554359Sroberto			}
92654359Sroberto			printf(" %02x", *data++ & 0xff);
92754359Sroberto		}
92854359Sroberto		printf("\n");
92954359Sroberto	}
93054359Sroberto
93154359Sroberto	/*
93254359Sroberto	 * Check for format errors.  Bug proofing.
93354359Sroberto	 */
93454359Sroberto	if (n < CTL_HEADER_LEN) {
93554359Sroberto		if (debug)
93654359Sroberto		    printf("Short (%d byte) packet received\n", n);
93754359Sroberto		goto again;
93854359Sroberto	}
93954359Sroberto	if (PKT_VERSION(rpkt.li_vn_mode) > NTP_VERSION
94054359Sroberto	    || PKT_VERSION(rpkt.li_vn_mode) < NTP_OLDVERSION) {
94154359Sroberto		if (debug)
94254359Sroberto		    printf("Packet received with version %d\n",
94354359Sroberto			   PKT_VERSION(rpkt.li_vn_mode));
94454359Sroberto		goto again;
94554359Sroberto	}
94654359Sroberto	if (PKT_MODE(rpkt.li_vn_mode) != MODE_CONTROL) {
94754359Sroberto		if (debug)
94854359Sroberto		    printf("Packet received with mode %d\n",
94954359Sroberto			   PKT_MODE(rpkt.li_vn_mode));
95054359Sroberto		goto again;
95154359Sroberto	}
95254359Sroberto	if (!CTL_ISRESPONSE(rpkt.r_m_e_op)) {
95354359Sroberto		if (debug)
95454359Sroberto		    printf("Received request packet, wanted response\n");
95554359Sroberto		goto again;
95654359Sroberto	}
95754359Sroberto
95854359Sroberto	/*
95954359Sroberto	 * Check opcode and sequence number for a match.
96054359Sroberto	 * Could be old data getting to us.
96154359Sroberto	 */
96254359Sroberto	if (ntohs(rpkt.sequence) != sequence) {
96354359Sroberto		if (debug)
96454359Sroberto		    printf(
96554359Sroberto			    "Received sequnce number %d, wanted %d\n",
96654359Sroberto			    ntohs(rpkt.sequence), sequence);
96754359Sroberto		goto again;
96854359Sroberto	}
96954359Sroberto	if (CTL_OP(rpkt.r_m_e_op) != opcode) {
97054359Sroberto		if (debug)
97154359Sroberto		    printf(
97254359Sroberto			    "Received opcode %d, wanted %d (sequence number okay)\n",
97354359Sroberto			    CTL_OP(rpkt.r_m_e_op), opcode);
97454359Sroberto		goto again;
97554359Sroberto	}
97654359Sroberto
97754359Sroberto	/*
97854359Sroberto	 * Check the error code.  If non-zero, return it.
97954359Sroberto	 */
98054359Sroberto	if (CTL_ISERROR(rpkt.r_m_e_op)) {
98154359Sroberto		int errcode;
98254359Sroberto
98354359Sroberto		errcode = (ntohs(rpkt.status) >> 8) & 0xff;
98454359Sroberto		if (debug && CTL_ISMORE(rpkt.r_m_e_op)) {
98554359Sroberto			printf("Error code %d received on not-final packet\n",
98654359Sroberto			       errcode);
98754359Sroberto		}
98854359Sroberto		if (errcode == CERR_UNSPEC)
98954359Sroberto		    return ERR_UNSPEC;
99054359Sroberto		return errcode;
99154359Sroberto	}
99254359Sroberto
99354359Sroberto	/*
99454359Sroberto	 * Check the association ID to make sure it matches what
99554359Sroberto	 * we sent.
99654359Sroberto	 */
99754359Sroberto	if (ntohs(rpkt.associd) != associd) {
99854359Sroberto		if (debug)
99954359Sroberto		    printf("Association ID %d doesn't match expected %d\n",
100054359Sroberto			   ntohs(rpkt.associd), associd);
100154359Sroberto		/*
100254359Sroberto		 * Hack for silly fuzzballs which, at the time of writing,
100354359Sroberto		 * return an assID of sys.peer when queried for system variables.
100454359Sroberto		 */
100554359Sroberto#ifdef notdef
100654359Sroberto		goto again;
100754359Sroberto#endif
100854359Sroberto	}
100954359Sroberto
101054359Sroberto	/*
101154359Sroberto	 * Collect offset and count.  Make sure they make sense.
101254359Sroberto	 */
101354359Sroberto	offset = ntohs(rpkt.offset);
101454359Sroberto	count = ntohs(rpkt.count);
101554359Sroberto
101654359Sroberto	if (debug >= 3) {
101754359Sroberto		int shouldbesize;
101854359Sroberto		u_long key;
101954359Sroberto		u_long *lpkt;
102054359Sroberto		int maclen;
102154359Sroberto
102254359Sroberto		/*
102354359Sroberto		 * Usually we ignore authentication, but for debugging purposes
102454359Sroberto		 * we watch it here.
102554359Sroberto		 */
102654359Sroberto		shouldbesize = CTL_HEADER_LEN + count;
102754359Sroberto
102854359Sroberto		/* round to 8 octet boundary */
102954359Sroberto		shouldbesize = (shouldbesize + 7) & ~7;
103054359Sroberto
103154359Sroberto		if (n & 0x3) {
103254359Sroberto			printf("Packet not padded, size = %d\n", n);
103354359Sroberto		} if ((maclen = n - shouldbesize) >= MIN_MAC_LEN) {
103454359Sroberto			printf(
103554359Sroberto				"Packet shows signs of authentication (total %d, data %d, mac %d)\n",
103654359Sroberto				n, shouldbesize, maclen);
103754359Sroberto			lpkt = (u_long *)&rpkt;
103854359Sroberto			printf("%08lx %08lx %08lx %08lx %08lx %08lx\n",
103954359Sroberto			       (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_long) - 3]),
104054359Sroberto			       (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_long) - 2]),
104154359Sroberto			       (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_long) - 1]),
104254359Sroberto			       (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_long)]),
104354359Sroberto			       (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_long) + 1]),
104454359Sroberto			       (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_long) + 2]));
104554359Sroberto			key = ntohl(lpkt[(n - maclen) / sizeof(u_long)]);
104654359Sroberto			printf("Authenticated with keyid %lu\n", (u_long)key);
104754359Sroberto			if (key != 0 && key != info_auth_keyid) {
104854359Sroberto				printf("We don't know that key\n");
104954359Sroberto			} else {
105054359Sroberto				if (authdecrypt(key, (u_int32 *)&rpkt,
105154359Sroberto				    n - maclen, maclen)) {
105254359Sroberto					printf("Auth okay!\n");
105354359Sroberto				} else {
105454359Sroberto					printf("Auth failed!\n");
105554359Sroberto				}
105654359Sroberto			}
105754359Sroberto		}
105854359Sroberto	}
105954359Sroberto
106054359Sroberto	if (debug >= 2)
106154359Sroberto	    printf("Got packet, size = %d\n", n);
106254359Sroberto	if (count > (u_short)(n-CTL_HEADER_LEN)) {
106354359Sroberto		if (debug)
106454359Sroberto		    printf(
106554359Sroberto			    "Received count of %d octets, data in packet is %d\n",
106654359Sroberto			    count, n-CTL_HEADER_LEN);
106754359Sroberto		goto again;
106854359Sroberto	}
106954359Sroberto	if (count == 0 && CTL_ISMORE(rpkt.r_m_e_op)) {
107054359Sroberto		if (debug)
107154359Sroberto		    printf("Received count of 0 in non-final fragment\n");
107254359Sroberto		goto again;
107354359Sroberto	}
107454359Sroberto	if (offset + count > sizeof(pktdata)) {
107554359Sroberto		if (debug)
107654359Sroberto		    printf("Offset %d, count %d, too big for buffer\n",
107754359Sroberto			   offset, count);
107854359Sroberto		return ERR_TOOMUCH;
107954359Sroberto	}
108054359Sroberto	if (seenlastfrag && !CTL_ISMORE(rpkt.r_m_e_op)) {
108154359Sroberto		if (debug)
108254359Sroberto		    printf("Received second last fragment packet\n");
108354359Sroberto		goto again;
108454359Sroberto	}
108554359Sroberto
108654359Sroberto	/*
108754359Sroberto	 * So far, so good.  Record this fragment, making sure it doesn't
108854359Sroberto	 * overlap anything.
108954359Sroberto	 */
109054359Sroberto	if (debug >= 2)
109154359Sroberto	    printf("Packet okay\n");;
109254359Sroberto
109354359Sroberto	if (numfrags == MAXFRAGS) {
109454359Sroberto		if (debug)
109554359Sroberto		    printf("Number of fragments exceeds maximum\n");
109654359Sroberto		return ERR_TOOMUCH;
109754359Sroberto	}
109854359Sroberto
109954359Sroberto	for (n = 0; n < numfrags; n++) {
110054359Sroberto		if (offset == offsets[n])
110154359Sroberto		    goto again;	/* duplicate */
110254359Sroberto		if (offset < offsets[n])
110354359Sroberto		    break;
110454359Sroberto	}
110554359Sroberto
110654359Sroberto	if ((u_short)(n > 0 && offsets[n-1] + counts[n-1]) > offset)
110754359Sroberto	    goto overlap;
110854359Sroberto	if (n < numfrags && (u_short)(offset + count) > offsets[n])
110954359Sroberto	    goto overlap;
111054359Sroberto
111154359Sroberto	{
111254359Sroberto		register int i;
111354359Sroberto
111454359Sroberto		for (i = numfrags; i > n; i--) {
111554359Sroberto			offsets[i] = offsets[i-1];
111654359Sroberto			counts[i] = counts[i-1];
111754359Sroberto		}
111854359Sroberto	}
111954359Sroberto	offsets[n] = offset;
112054359Sroberto	counts[n] = count;
112154359Sroberto	numfrags++;
112254359Sroberto
112354359Sroberto	/*
112454359Sroberto	 * Got that stuffed in right.  Figure out if this was the last.
112554359Sroberto	 * Record status info out of the last packet.
112654359Sroberto	 */
112754359Sroberto	if (!CTL_ISMORE(rpkt.r_m_e_op)) {
112854359Sroberto		seenlastfrag = 1;
112954359Sroberto		if (rstatus != 0)
113054359Sroberto		    *rstatus = ntohs(rpkt.status);
113154359Sroberto	}
113254359Sroberto
113354359Sroberto	/*
113454359Sroberto	 * Copy the data into the data buffer.
113554359Sroberto	 */
113654359Sroberto	memmove((char *)pktdata + offset, (char *)rpkt.data, count);
113754359Sroberto
113854359Sroberto	/*
113954359Sroberto	 * If we've seen the last fragment, look for holes in the sequence.
114054359Sroberto	 * If there aren't any, we're done.
114154359Sroberto	 */
114254359Sroberto	if (seenlastfrag && offsets[0] == 0) {
114354359Sroberto		for (n = 1; n < numfrags; n++) {
114454359Sroberto			if (offsets[n-1] + counts[n-1] != offsets[n])
114554359Sroberto			    break;
114654359Sroberto		}
114754359Sroberto		if (n == numfrags) {
114854359Sroberto			*rsize = offsets[numfrags-1] + counts[numfrags-1];
114954359Sroberto			return 0;
115054359Sroberto		}
115154359Sroberto	}
115254359Sroberto	goto again;
115354359Sroberto
115454359Sroberto    overlap:
115554359Sroberto	/*
115654359Sroberto	 * Print debugging message about overlapping fragments
115754359Sroberto	 */
115854359Sroberto	if (debug)
115954359Sroberto	    printf("Overlapping fragments returned in response\n");
116054359Sroberto	goto again;
116154359Sroberto}
116254359Sroberto
116354359Sroberto
116454359Sroberto/*
116554359Sroberto * sendrequest - format and send a request packet
116654359Sroberto */
116754359Srobertostatic int
116854359Srobertosendrequest(
116954359Sroberto	int opcode,
117054359Sroberto	int associd,
117154359Sroberto	int auth,
117254359Sroberto	int qsize,
117354359Sroberto	char *qdata
117454359Sroberto	)
117554359Sroberto{
117654359Sroberto	struct ntp_control qpkt;
117754359Sroberto	int pktsize;
117854359Sroberto
117954359Sroberto	/*
118054359Sroberto	 * Check to make sure the data will fit in one packet
118154359Sroberto	 */
118254359Sroberto	if (qsize > CTL_MAX_DATA_LEN) {
118354359Sroberto		(void) fprintf(stderr,
118454359Sroberto			       "***Internal error!  qsize (%d) too large\n",
118554359Sroberto			       qsize);
118654359Sroberto		return 1;
118754359Sroberto	}
118854359Sroberto
118954359Sroberto	/*
119054359Sroberto	 * Fill in the packet
119154359Sroberto	 */
119254359Sroberto	qpkt.li_vn_mode = PKT_LI_VN_MODE(0, pktversion, MODE_CONTROL);
1193132451Sroberto	qpkt.r_m_e_op = (u_char)(opcode & CTL_OP_MASK);
119454359Sroberto	qpkt.sequence = htons(sequence);
119554359Sroberto	qpkt.status = 0;
119654359Sroberto	qpkt.associd = htons((u_short)associd);
119754359Sroberto	qpkt.offset = 0;
119854359Sroberto	qpkt.count = htons((u_short)qsize);
119954359Sroberto
120054359Sroberto	/*
120154359Sroberto	 * If we have data, copy it in and pad it out to a 64
120254359Sroberto	 * bit boundary.
120354359Sroberto	 */
120454359Sroberto	if (qsize > 0) {
120554359Sroberto		memmove((char *)qpkt.data, qdata, (unsigned)qsize);
120654359Sroberto		pktsize = qsize + CTL_HEADER_LEN;
120754359Sroberto		while (pktsize & (sizeof(u_long) - 1)) {
120854359Sroberto			qpkt.data[qsize++] = 0;
120954359Sroberto			pktsize++;
121054359Sroberto		}
121154359Sroberto	} else {
121254359Sroberto		pktsize = CTL_HEADER_LEN;
121354359Sroberto	}
121454359Sroberto
121554359Sroberto	/*
121654359Sroberto	 * If it isn't authenticated we can just send it.  Otherwise
121754359Sroberto	 * we're going to have to think about it a little.
121854359Sroberto	 */
121954359Sroberto	if (!auth && !always_auth) {
122054359Sroberto		return sendpkt((char *)&qpkt, pktsize);
122154359Sroberto	} else {
122254359Sroberto		const char *pass = "\0";
122354359Sroberto		int maclen = 0;
122454359Sroberto		u_long my_keyid;
122554359Sroberto
122654359Sroberto		/*
122754359Sroberto		 * Pad out packet to a multiple of 8 octets to be sure
122854359Sroberto		 * receiver can handle it.
122954359Sroberto		 */
123054359Sroberto		while (pktsize & 7) {
123154359Sroberto			qpkt.data[qsize++] = 0;
123254359Sroberto			pktsize++;
123354359Sroberto		}
123454359Sroberto
123554359Sroberto		/*
123654359Sroberto		 * Get the keyid and the password if we don't have one.
123754359Sroberto		 */
123854359Sroberto		if (info_auth_keyid == 0) {
1239132451Sroberto			int u_keyid = getkeyid("Keyid: ");
1240132451Sroberto			if (u_keyid == 0 || u_keyid > NTP_MAXKEY) {
124154359Sroberto				(void) fprintf(stderr,
124254359Sroberto				   "Invalid key identifier\n");
124354359Sroberto				return 1;
124454359Sroberto			}
1245132451Sroberto			info_auth_keyid = u_keyid;
124654359Sroberto		}
124754359Sroberto		if (!authistrusted(info_auth_keyid)) {
1248132451Sroberto			pass = getpass("MD5 Password: ");
124954359Sroberto			if (*pass == '\0') {
125054359Sroberto				(void) fprintf(stderr,
125154359Sroberto				  "Invalid password\n");
125254359Sroberto				return (1);
125354359Sroberto			}
125454359Sroberto		}
125554359Sroberto		authusekey(info_auth_keyid, info_auth_keytype, (const u_char *)pass);
125654359Sroberto		authtrust(info_auth_keyid, 1);
125754359Sroberto
125854359Sroberto		/*
125954359Sroberto		 * Stick the keyid in the packet where
126054359Sroberto		 * cp currently points.  Cp should be aligned
126154359Sroberto		 * properly.  Then do the encryptions.
126254359Sroberto		 */
126354359Sroberto		my_keyid = htonl(info_auth_keyid);
126454359Sroberto		memcpy(&qpkt.data[qsize], &my_keyid, sizeof my_keyid);
126554359Sroberto		maclen = authencrypt(info_auth_keyid, (u_int32 *)&qpkt,
126654359Sroberto		    pktsize);
126754359Sroberto		if (maclen == 0) {
126854359Sroberto			(void) fprintf(stderr, "Key not found\n");
126954359Sroberto			return (1);
127054359Sroberto		}
127154359Sroberto		return sendpkt((char *)&qpkt, pktsize + maclen);
127254359Sroberto	}
127354359Sroberto	/*NOTREACHED*/
127454359Sroberto}
127554359Sroberto
127654359Sroberto
127754359Sroberto/*
127854359Sroberto * doquery - send a request and process the response
127954359Sroberto */
128054359Srobertoint
128154359Srobertodoquery(
128254359Sroberto	int opcode,
128354359Sroberto	int associd,
128454359Sroberto	int auth,
128554359Sroberto	int qsize,
128654359Sroberto	char *qdata,
128754359Sroberto	u_short *rstatus,
128854359Sroberto	int *rsize,
128954359Sroberto	char **rdata
129054359Sroberto	)
129154359Sroberto{
129254359Sroberto	int res;
129354359Sroberto	int done;
129454359Sroberto
129554359Sroberto	/*
129654359Sroberto	 * Check to make sure host is open
129754359Sroberto	 */
129854359Sroberto	if (!havehost) {
129954359Sroberto		(void) fprintf(stderr, "***No host open, use `host' command\n");
130054359Sroberto		return -1;
130154359Sroberto	}
130254359Sroberto
130354359Sroberto	done = 0;
130454359Sroberto	sequence++;
130554359Sroberto
130654359Sroberto    again:
130754359Sroberto	/*
130854359Sroberto	 * send a request
130954359Sroberto	 */
131054359Sroberto	res = sendrequest(opcode, associd, auth, qsize, qdata);
131154359Sroberto	if (res != 0)
131254359Sroberto	    return res;
131354359Sroberto
131454359Sroberto	/*
131554359Sroberto	 * Get the response.  If we got a standard error, print a message
131654359Sroberto	 */
131754359Sroberto	res = getresponse(opcode, associd, rstatus, rsize, rdata, done);
131854359Sroberto
131954359Sroberto	if (res > 0) {
132054359Sroberto		if (!done && (res == ERR_TIMEOUT || res == ERR_INCOMPLETE)) {
132154359Sroberto			if (res == ERR_INCOMPLETE) {
132254359Sroberto				/*
132354359Sroberto				 * better bump the sequence so we don't
132454359Sroberto				 * get confused about differing fragments.
132554359Sroberto				 */
132654359Sroberto				sequence++;
132754359Sroberto			}
132854359Sroberto			done = 1;
132954359Sroberto			goto again;
133054359Sroberto		}
1331182007Sroberto		if (numhosts > 1)
1332182007Sroberto			(void) fprintf(stderr, "server=%s ", currenthost);
133354359Sroberto		switch(res) {
133454359Sroberto		    case CERR_BADFMT:
133554359Sroberto			(void) fprintf(stderr,
133654359Sroberto			    "***Server reports a bad format request packet\n");
133754359Sroberto			break;
133854359Sroberto		    case CERR_PERMISSION:
133954359Sroberto			(void) fprintf(stderr,
134054359Sroberto			    "***Server disallowed request (authentication?)\n");
134154359Sroberto			break;
134254359Sroberto		    case CERR_BADOP:
134354359Sroberto			(void) fprintf(stderr,
134454359Sroberto			    "***Server reports a bad opcode in request\n");
134554359Sroberto			break;
134654359Sroberto		    case CERR_BADASSOC:
134754359Sroberto			(void) fprintf(stderr,
134854359Sroberto			    "***Association ID %d unknown to server\n",associd);
134954359Sroberto			break;
135054359Sroberto		    case CERR_UNKNOWNVAR:
135154359Sroberto			(void) fprintf(stderr,
135254359Sroberto			    "***A request variable unknown to the server\n");
135354359Sroberto			break;
135454359Sroberto		    case CERR_BADVALUE:
135554359Sroberto			(void) fprintf(stderr,
135654359Sroberto			    "***Server indicates a request variable was bad\n");
135754359Sroberto			break;
135854359Sroberto		    case ERR_UNSPEC:
135954359Sroberto			(void) fprintf(stderr,
136054359Sroberto			    "***Server returned an unspecified error\n");
136154359Sroberto			break;
136254359Sroberto		    case ERR_TIMEOUT:
136354359Sroberto			(void) fprintf(stderr, "***Request timed out\n");
136454359Sroberto			break;
136554359Sroberto		    case ERR_INCOMPLETE:
136654359Sroberto			(void) fprintf(stderr,
136754359Sroberto			    "***Response from server was incomplete\n");
136854359Sroberto			break;
136954359Sroberto		    case ERR_TOOMUCH:
137054359Sroberto			(void) fprintf(stderr,
137154359Sroberto			    "***Buffer size exceeded for returned data\n");
137254359Sroberto			break;
137354359Sroberto		    default:
137454359Sroberto			(void) fprintf(stderr,
137554359Sroberto			    "***Server returns unknown error code %d\n", res);
137654359Sroberto			break;
137754359Sroberto		}
137854359Sroberto	}
137954359Sroberto	return res;
138054359Sroberto}
138154359Sroberto
138254359Sroberto
138354359Sroberto/*
138454359Sroberto * getcmds - read commands from the standard input and execute them
138554359Sroberto */
138654359Srobertostatic void
138754359Srobertogetcmds(void)
138854359Sroberto{
1389132451Sroberto#if defined(HAVE_LIBREADLINE) || defined(HAVE_LIBEDIT)
1390106163Sroberto        char *line;
139154359Sroberto
1392106163Sroberto        for (;;) {
1393106163Sroberto                if ((line = readline(interactive?prompt:"")) == NULL) return;
1394106163Sroberto                if (*line) add_history(line);
1395106163Sroberto                docmd(line);
1396106163Sroberto                free(line);
1397106163Sroberto        }
1398132451Sroberto#else /* not (HAVE_LIBREADLINE || HAVE_LIBEDIT) */
1399106163Sroberto        char line[MAXLINE];
1400106163Sroberto
1401106163Sroberto        for (;;) {
1402106163Sroberto                if (interactive) {
1403106163Sroberto#ifdef VMS      /* work around a problem with mixing stdout & stderr */
1404106163Sroberto                        fputs("",stdout);
140554359Sroberto#endif
1406106163Sroberto                        (void) fputs(prompt, stderr);
1407106163Sroberto                        (void) fflush(stderr);
1408106163Sroberto                }
140954359Sroberto
1410106163Sroberto                if (fgets(line, sizeof line, stdin) == NULL)
1411106163Sroberto                    return;
141254359Sroberto
1413106163Sroberto                docmd(line);
1414106163Sroberto        }
1415132451Sroberto#endif /* not (HAVE_LIBREADLINE || HAVE_LIBEDIT) */
141654359Sroberto}
141754359Sroberto
1418132451Sroberto#ifndef SYS_WINNT /* Under NT cannot handle SIGINT, WIN32 spawns a handler */
141954359Sroberto/*
142054359Sroberto * abortcmd - catch interrupts and abort the current command
142154359Sroberto */
142254359Srobertostatic RETSIGTYPE
142354359Srobertoabortcmd(
142454359Sroberto	int sig
142554359Sroberto	)
142654359Sroberto{
142754359Sroberto	if (current_output == stdout)
142854359Sroberto	    (void) fflush(stdout);
142954359Sroberto	putc('\n', stderr);
143054359Sroberto	(void) fflush(stderr);
143154359Sroberto	if (jump) longjmp(interrupt_buf, 1);
143254359Sroberto}
1433132451Sroberto#endif	/* SYS_WINNT */
143454359Sroberto
143554359Sroberto/*
143654359Sroberto * docmd - decode the command line and execute a command
143754359Sroberto */
143854359Srobertostatic void
143954359Srobertodocmd(
144054359Sroberto	const char *cmdline
144154359Sroberto	)
144254359Sroberto{
144354359Sroberto	char *tokens[1+MAXARGS+2];
144454359Sroberto	struct parse pcmd;
144554359Sroberto	int ntok;
144654359Sroberto	static int i;
144754359Sroberto	struct xcmd *xcmd;
144854359Sroberto
144954359Sroberto	/*
145054359Sroberto	 * Tokenize the command line.  If nothing on it, return.
145154359Sroberto	 */
145254359Sroberto	tokenize(cmdline, tokens, &ntok);
145354359Sroberto	if (ntok == 0)
145454359Sroberto	    return;
145554359Sroberto
145654359Sroberto	/*
145754359Sroberto	 * Find the appropriate command description.
145854359Sroberto	 */
145954359Sroberto	i = findcmd(tokens[0], builtins, opcmds, &xcmd);
146054359Sroberto	if (i == 0) {
146154359Sroberto		(void) fprintf(stderr, "***Command `%s' unknown\n",
146254359Sroberto			       tokens[0]);
146354359Sroberto		return;
146454359Sroberto	} else if (i >= 2) {
146554359Sroberto		(void) fprintf(stderr, "***Command `%s' ambiguous\n",
146654359Sroberto			       tokens[0]);
146754359Sroberto		return;
146854359Sroberto	}
146954359Sroberto
147054359Sroberto	/*
147154359Sroberto	 * Save the keyword, then walk through the arguments, interpreting
147254359Sroberto	 * as we go.
147354359Sroberto	 */
147454359Sroberto	pcmd.keyword = tokens[0];
147554359Sroberto	pcmd.nargs = 0;
147654359Sroberto	for (i = 0; i < MAXARGS && xcmd->arg[i] != NO; i++) {
147754359Sroberto		if ((i+1) >= ntok) {
147854359Sroberto			if (!(xcmd->arg[i] & OPT)) {
147954359Sroberto				printusage(xcmd, stderr);
148054359Sroberto				return;
148154359Sroberto			}
148254359Sroberto			break;
148354359Sroberto		}
148454359Sroberto		if ((xcmd->arg[i] & OPT) && (*tokens[i+1] == '>'))
148554359Sroberto		    break;
148654359Sroberto		if (!getarg(tokens[i+1], (int)xcmd->arg[i], &pcmd.argval[i]))
148754359Sroberto		    return;
148854359Sroberto		pcmd.nargs++;
148954359Sroberto	}
149054359Sroberto
149154359Sroberto	i++;
149254359Sroberto	if (i < ntok && *tokens[i] == '>') {
149354359Sroberto		char *fname;
149454359Sroberto
149554359Sroberto		if (*(tokens[i]+1) != '\0')
149654359Sroberto		    fname = tokens[i]+1;
149754359Sroberto		else if ((i+1) < ntok)
149854359Sroberto		    fname = tokens[i+1];
149954359Sroberto		else {
150054359Sroberto			(void) fprintf(stderr, "***No file for redirect\n");
150154359Sroberto			return;
150254359Sroberto		}
150354359Sroberto
150454359Sroberto		current_output = fopen(fname, "w");
150554359Sroberto		if (current_output == NULL) {
150654359Sroberto			(void) fprintf(stderr, "***Error opening %s: ", fname);
150754359Sroberto			perror("");
150854359Sroberto			return;
150954359Sroberto		}
151054359Sroberto		i = 1;		/* flag we need a close */
151154359Sroberto	} else {
151254359Sroberto		current_output = stdout;
151354359Sroberto		i = 0;		/* flag no close */
151454359Sroberto	}
151554359Sroberto
151654359Sroberto	if (interactive && setjmp(interrupt_buf)) {
151754359Sroberto		jump = 0;
151854359Sroberto		return;
151954359Sroberto	} else {
152054359Sroberto		jump++;
152154359Sroberto		(xcmd->handler)(&pcmd, current_output);
152254359Sroberto		jump = 0;	/* HMS: 961106: was after fclose() */
152354359Sroberto		if (i) (void) fclose(current_output);
152454359Sroberto	}
152554359Sroberto}
152654359Sroberto
152754359Sroberto
152854359Sroberto/*
152954359Sroberto * tokenize - turn a command line into tokens
153054359Sroberto */
153154359Srobertostatic void
153254359Srobertotokenize(
153354359Sroberto	const char *line,
153454359Sroberto	char **tokens,
153554359Sroberto	int *ntok
153654359Sroberto	)
153754359Sroberto{
153854359Sroberto	register const char *cp;
153954359Sroberto	register char *sp;
154054359Sroberto	static char tspace[MAXLINE];
154154359Sroberto
154254359Sroberto	sp = tspace;
154354359Sroberto	cp = line;
154454359Sroberto	for (*ntok = 0; *ntok < MAXTOKENS; (*ntok)++) {
154554359Sroberto		tokens[*ntok] = sp;
154654359Sroberto		while (ISSPACE(*cp))
154754359Sroberto		    cp++;
154854359Sroberto		if (ISEOL(*cp))
154954359Sroberto		    break;
155054359Sroberto		do {
155154359Sroberto			*sp++ = *cp++;
155254359Sroberto		} while (!ISSPACE(*cp) && !ISEOL(*cp));
155354359Sroberto
155454359Sroberto		*sp++ = '\0';
155554359Sroberto	}
155654359Sroberto}
155754359Sroberto
155854359Sroberto
155954359Sroberto
156054359Sroberto/*
156154359Sroberto * findcmd - find a command in a command description table
156254359Sroberto */
156354359Srobertostatic int
156454359Srobertofindcmd(
156554359Sroberto	register char *str,
156654359Sroberto	struct xcmd *clist1,
156754359Sroberto	struct xcmd *clist2,
156854359Sroberto	struct xcmd **cmd
156954359Sroberto	)
157054359Sroberto{
157154359Sroberto	register struct xcmd *cl;
157254359Sroberto	register int clen;
157354359Sroberto	int nmatch;
157454359Sroberto	struct xcmd *nearmatch = NULL;
157554359Sroberto	struct xcmd *clist;
157654359Sroberto
157754359Sroberto	clen = strlen(str);
157854359Sroberto	nmatch = 0;
157954359Sroberto	if (clist1 != 0)
158054359Sroberto	    clist = clist1;
158154359Sroberto	else if (clist2 != 0)
158254359Sroberto	    clist = clist2;
158354359Sroberto	else
158454359Sroberto	    return 0;
158554359Sroberto
158654359Sroberto    again:
158754359Sroberto	for (cl = clist; cl->keyword != 0; cl++) {
158854359Sroberto		/* do a first character check, for efficiency */
158954359Sroberto		if (*str != *(cl->keyword))
159054359Sroberto		    continue;
159154359Sroberto		if (strncmp(str, cl->keyword, (unsigned)clen) == 0) {
159254359Sroberto			/*
159354359Sroberto			 * Could be extact match, could be approximate.
159454359Sroberto			 * Is exact if the length of the keyword is the
159554359Sroberto			 * same as the str.
159654359Sroberto			 */
159754359Sroberto			if (*((cl->keyword) + clen) == '\0') {
159854359Sroberto				*cmd = cl;
159954359Sroberto				return 1;
160054359Sroberto			}
160154359Sroberto			nmatch++;
160254359Sroberto			nearmatch = cl;
160354359Sroberto		}
160454359Sroberto	}
160554359Sroberto
160654359Sroberto	/*
160754359Sroberto	 * See if there is more to do.  If so, go again.  Sorry about the
160854359Sroberto	 * goto, too much looking at BSD sources...
160954359Sroberto	 */
161054359Sroberto	if (clist == clist1 && clist2 != 0) {
161154359Sroberto		clist = clist2;
161254359Sroberto		goto again;
161354359Sroberto	}
161454359Sroberto
161554359Sroberto	/*
161654359Sroberto	 * If we got extactly 1 near match, use it, else return number
161754359Sroberto	 * of matches.
161854359Sroberto	 */
161954359Sroberto	if (nmatch == 1) {
162054359Sroberto		*cmd = nearmatch;
162154359Sroberto		return 1;
162254359Sroberto	}
162354359Sroberto	return nmatch;
162454359Sroberto}
162554359Sroberto
162654359Sroberto
162754359Sroberto/*
162854359Sroberto * getarg - interpret an argument token
162954359Sroberto */
163054359Srobertostatic int
163154359Srobertogetarg(
163254359Sroberto	char *str,
163354359Sroberto	int code,
163454359Sroberto	arg_v *argp
163554359Sroberto	)
163654359Sroberto{
163754359Sroberto	int isneg;
163854359Sroberto	char *cp, *np;
163954359Sroberto	static const char *digits = "0123456789";
164054359Sroberto
164154359Sroberto	switch (code & ~OPT) {
1642182007Sroberto	    case NTP_STR:
164354359Sroberto		argp->string = str;
164454359Sroberto		break;
1645182007Sroberto	    case NTP_ADD:
1646132451Sroberto		if (!getnetnum(str, &(argp->netnum), (char *)0, 0)) {
164754359Sroberto			return 0;
164854359Sroberto		}
164954359Sroberto		break;
1650182007Sroberto	    case NTP_INT:
1651182007Sroberto	    case NTP_UINT:
165254359Sroberto		isneg = 0;
165354359Sroberto		np = str;
165454359Sroberto		if (*np == '&') {
165554359Sroberto			np++;
165654359Sroberto			isneg = atoi(np);
165754359Sroberto			if (isneg <= 0) {
165854359Sroberto				(void) fprintf(stderr,
165954359Sroberto					       "***Association value `%s' invalid/undecodable\n", str);
166054359Sroberto				return 0;
166154359Sroberto			}
166254359Sroberto			if (isneg > numassoc) {
1663132451Sroberto				if (numassoc == 0) {
1664132451Sroberto					(void) fprintf(stderr,
1665132451Sroberto						       "***Association for `%s' unknown (max &%d)\n",
1666132451Sroberto						       str, numassoc);
1667132451Sroberto					return 0;
1668132451Sroberto				} else {
1669132451Sroberto					isneg = numassoc;
1670132451Sroberto				}
167154359Sroberto			}
167254359Sroberto			argp->uval = assoc_cache[isneg-1].assid;
167354359Sroberto			break;
167454359Sroberto		}
167554359Sroberto
167654359Sroberto		if (*np == '-') {
167754359Sroberto			np++;
167854359Sroberto			isneg = 1;
167954359Sroberto		}
168054359Sroberto
168154359Sroberto		argp->uval = 0;
168254359Sroberto		do {
168354359Sroberto			cp = strchr(digits, *np);
168454359Sroberto			if (cp == NULL) {
168554359Sroberto				(void) fprintf(stderr,
168654359Sroberto					       "***Illegal integer value %s\n", str);
168754359Sroberto				return 0;
168854359Sroberto			}
168954359Sroberto			argp->uval *= 10;
169054359Sroberto			argp->uval += (cp - digits);
169154359Sroberto		} while (*(++np) != '\0');
169254359Sroberto
169354359Sroberto		if (isneg) {
1694182007Sroberto			if ((code & ~OPT) == NTP_UINT) {
169554359Sroberto				(void) fprintf(stderr,
169654359Sroberto					       "***Value %s should be unsigned\n", str);
169754359Sroberto				return 0;
169854359Sroberto			}
169954359Sroberto			argp->ival = -argp->ival;
170054359Sroberto		}
170154359Sroberto		break;
1702132451Sroberto	     case IP_VERSION:
1703132451Sroberto		if (!strcmp("-6", str))
1704132451Sroberto			argp->ival = 6 ;
1705132451Sroberto		else if (!strcmp("-4", str))
1706132451Sroberto			argp->ival = 4 ;
1707132451Sroberto		else {
1708132451Sroberto			(void) fprintf(stderr,
1709132451Sroberto			    "***Version must be either 4 or 6\n");
1710132451Sroberto			return 0;
1711132451Sroberto		}
1712132451Sroberto		break;
171354359Sroberto	}
171454359Sroberto
171554359Sroberto	return 1;
171654359Sroberto}
171754359Sroberto
171854359Sroberto
171954359Sroberto/*
172054359Sroberto * getnetnum - given a host name, return its net number
172154359Sroberto *	       and (optional) full name
172254359Sroberto */
172354359Srobertoint
172454359Srobertogetnetnum(
172554359Sroberto	const char *hname,
1726132451Sroberto	struct sockaddr_storage *num,
1727132451Sroberto	char *fullhost,
1728132451Sroberto	int af
172954359Sroberto	)
173054359Sroberto{
1731132451Sroberto	int sockaddr_len;
1732132451Sroberto	struct addrinfo hints, *ai = NULL;
173354359Sroberto
1734132451Sroberto	sockaddr_len = (af == AF_INET)
1735132451Sroberto			   ? sizeof(struct sockaddr_in)
1736132451Sroberto			   : sizeof(struct sockaddr_in6);
1737132451Sroberto	memset((char *)&hints, 0, sizeof(struct addrinfo));
1738132451Sroberto	hints.ai_flags = AI_CANONNAME;
1739132451Sroberto#ifdef AI_ADDRCONFIG
1740132451Sroberto	hints.ai_flags |= AI_ADDRCONFIG;
1741132451Sroberto#endif
1742132451Sroberto
1743132451Sroberto	/* decodenetnum works with addresses only */
174454359Sroberto	if (decodenetnum(hname, num)) {
174554359Sroberto		if (fullhost != 0) {
1746132451Sroberto			getnameinfo((struct sockaddr *)num, sockaddr_len,
1747132451Sroberto					fullhost, sizeof(fullhost), NULL, 0,
1748132451Sroberto					NI_NUMERICHOST);
174954359Sroberto		}
175054359Sroberto		return 1;
1751182007Sroberto	} else if (getaddrinfo(hname, "ntp", &hints, &ai) == 0) {
1752132451Sroberto		memmove((char *)num, ai->ai_addr, ai->ai_addrlen);
1753132451Sroberto		if (ai->ai_canonname != 0)
1754132451Sroberto		    (void) strcpy(fullhost, ai->ai_canonname);
175554359Sroberto		return 1;
175654359Sroberto	} else {
175754359Sroberto		(void) fprintf(stderr, "***Can't find host %s\n", hname);
175854359Sroberto		return 0;
175954359Sroberto	}
176054359Sroberto	/*NOTREACHED*/
176154359Sroberto}
176254359Sroberto
176354359Sroberto/*
176454359Sroberto * nntohost - convert network number to host name.  This routine enforces
176554359Sroberto *	       the showhostnames setting.
176654359Sroberto */
176754359Srobertochar *
176854359Srobertonntohost(
1769132451Sroberto	struct sockaddr_storage *netnum
177054359Sroberto	)
177154359Sroberto{
177254359Sroberto	if (!showhostnames)
1773132451Sroberto	    return stoa(netnum);
1774132451Sroberto	if ((netnum->ss_family == AF_INET) && ISREFCLOCKADR(netnum))
1775132451Sroberto    		return refnumtoa(netnum);
1776132451Sroberto	return socktohost(netnum);
177754359Sroberto}
177854359Sroberto
177954359Sroberto
178054359Sroberto/*
178154359Sroberto * rtdatetolfp - decode an RT-11 date into an l_fp
178254359Sroberto */
178354359Srobertostatic int
178454359Srobertortdatetolfp(
178554359Sroberto	char *str,
178654359Sroberto	l_fp *lfp
178754359Sroberto	)
178854359Sroberto{
178954359Sroberto	register char *cp;
179054359Sroberto	register int i;
179154359Sroberto	struct calendar cal;
179254359Sroberto	char buf[4];
179354359Sroberto	static const char *months[12] = {
179454359Sroberto		"Jan", "Feb", "Mar", "Apr", "May", "Jun",
179554359Sroberto		"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
179654359Sroberto	};
179754359Sroberto
179854359Sroberto	cal.yearday = 0;
179954359Sroberto
180054359Sroberto	/*
180154359Sroberto	 * An RT-11 date looks like:
180254359Sroberto	 *
180354359Sroberto	 * d[d]-Mth-y[y] hh:mm:ss
180454359Sroberto	 *
180554359Sroberto	 * (No docs, but assume 4-digit years are also legal...)
180654359Sroberto	 *
180754359Sroberto	 * d[d]-Mth-y[y[y[y]]] hh:mm:ss
180854359Sroberto	 */
180954359Sroberto	cp = str;
181054359Sroberto	if (!isdigit((int)*cp)) {
181154359Sroberto		if (*cp == '-') {
181254359Sroberto			/*
181354359Sroberto			 * Catch special case
181454359Sroberto			 */
181554359Sroberto			L_CLR(lfp);
181654359Sroberto			return 1;
181754359Sroberto		}
181854359Sroberto		return 0;
181954359Sroberto	}
182054359Sroberto
1821132451Sroberto	cal.monthday = (u_char) (*cp++ - '0');	/* ascii dependent */
182254359Sroberto	if (isdigit((int)*cp)) {
1823132451Sroberto		cal.monthday = (u_char)((cal.monthday << 3) + (cal.monthday << 1));
1824132451Sroberto		cal.monthday = (u_char)(cal.monthday + *cp++ - '0');
182554359Sroberto	}
182654359Sroberto
182754359Sroberto	if (*cp++ != '-')
182854359Sroberto	    return 0;
182954359Sroberto
183054359Sroberto	for (i = 0; i < 3; i++)
183154359Sroberto	    buf[i] = *cp++;
183254359Sroberto	buf[3] = '\0';
183354359Sroberto
183454359Sroberto	for (i = 0; i < 12; i++)
183554359Sroberto	    if (STREQ(buf, months[i]))
183654359Sroberto		break;
183754359Sroberto	if (i == 12)
183854359Sroberto	    return 0;
1839132451Sroberto	cal.month = (u_char)(i + 1);
184054359Sroberto
184154359Sroberto	if (*cp++ != '-')
184254359Sroberto	    return 0;
184354359Sroberto
184454359Sroberto	if (!isdigit((int)*cp))
184554359Sroberto	    return 0;
1846132451Sroberto	cal.year = (u_short)(*cp++ - '0');
184754359Sroberto	if (isdigit((int)*cp)) {
1848132451Sroberto		cal.year = (u_short)((cal.year << 3) + (cal.year << 1));
1849132451Sroberto		cal.year = (u_short)(*cp++ - '0');
185054359Sroberto	}
185154359Sroberto	if (isdigit((int)*cp)) {
1852132451Sroberto		cal.year = (u_short)((cal.year << 3) + (cal.year << 1));
1853132451Sroberto		cal.year = (u_short)(cal.year + *cp++ - '0');
185454359Sroberto	}
185554359Sroberto	if (isdigit((int)*cp)) {
1856132451Sroberto		cal.year = (u_short)((cal.year << 3) + (cal.year << 1));
1857132451Sroberto		cal.year = (u_short)(cal.year + *cp++ - '0');
185854359Sroberto	}
185954359Sroberto
186054359Sroberto	/*
186154359Sroberto	 * Catch special case.  If cal.year == 0 this is a zero timestamp.
186254359Sroberto	 */
186354359Sroberto	if (cal.year == 0) {
186454359Sroberto		L_CLR(lfp);
186554359Sroberto		return 1;
186654359Sroberto	}
186754359Sroberto
186854359Sroberto	if (*cp++ != ' ' || !isdigit((int)*cp))
186954359Sroberto	    return 0;
1870132451Sroberto	cal.hour = (u_char)(*cp++ - '0');
187154359Sroberto	if (isdigit((int)*cp)) {
1872132451Sroberto		cal.hour = (u_char)((cal.hour << 3) + (cal.hour << 1));
1873132451Sroberto		cal.hour = (u_char)(cal.hour + *cp++ - '0');
187454359Sroberto	}
187554359Sroberto
187654359Sroberto	if (*cp++ != ':' || !isdigit((int)*cp))
187754359Sroberto	    return 0;
1878132451Sroberto	cal.minute = (u_char)(*cp++ - '0');
187954359Sroberto	if (isdigit((int)*cp)) {
1880132451Sroberto		cal.minute = (u_char)((cal.minute << 3) + (cal.minute << 1));
1881132451Sroberto		cal.minute = (u_char)(cal.minute + *cp++ - '0');
188254359Sroberto	}
188354359Sroberto
188454359Sroberto	if (*cp++ != ':' || !isdigit((int)*cp))
188554359Sroberto	    return 0;
1886132451Sroberto	cal.second = (u_char)(*cp++ - '0');
188754359Sroberto	if (isdigit((int)*cp)) {
1888132451Sroberto		cal.second = (u_char)((cal.second << 3) + (cal.second << 1));
1889132451Sroberto		cal.second = (u_char)(cal.second + *cp++ - '0');
189054359Sroberto	}
189154359Sroberto
189254359Sroberto	/*
189354359Sroberto	 * For RT-11, 1972 seems to be the pivot year
189454359Sroberto	 */
189554359Sroberto	if (cal.year < 72)
189654359Sroberto		cal.year += 2000;
189754359Sroberto	if (cal.year < 100)
189854359Sroberto		cal.year += 1900;
189954359Sroberto
190054359Sroberto	lfp->l_ui = caltontp(&cal);
190154359Sroberto	lfp->l_uf = 0;
190254359Sroberto	return 1;
190354359Sroberto}
190454359Sroberto
190554359Sroberto
190654359Sroberto/*
190754359Sroberto * decodets - decode a timestamp into an l_fp format number, with
190854359Sroberto *	      consideration of fuzzball formats.
190954359Sroberto */
191054359Srobertoint
191154359Srobertodecodets(
191254359Sroberto	char *str,
191354359Sroberto	l_fp *lfp
191454359Sroberto	)
191554359Sroberto{
191654359Sroberto	/*
191754359Sroberto	 * If it starts with a 0x, decode as hex.
191854359Sroberto	 */
191954359Sroberto	if (*str == '0' && (*(str+1) == 'x' || *(str+1) == 'X'))
192054359Sroberto	    return hextolfp(str+2, lfp);
192154359Sroberto
192254359Sroberto	/*
192354359Sroberto	 * If it starts with a '"', try it as an RT-11 date.
192454359Sroberto	 */
192554359Sroberto	if (*str == '"') {
192654359Sroberto		register char *cp = str+1;
192754359Sroberto		register char *bp;
192854359Sroberto		char buf[30];
192954359Sroberto
193054359Sroberto		bp = buf;
193154359Sroberto		while (*cp != '"' && *cp != '\0' && bp < &buf[29])
193254359Sroberto		    *bp++ = *cp++;
193354359Sroberto		*bp = '\0';
193454359Sroberto		return rtdatetolfp(buf, lfp);
193554359Sroberto	}
193654359Sroberto
193754359Sroberto	/*
193854359Sroberto	 * Might still be hex.  Check out the first character.  Talk
193954359Sroberto	 * about heuristics!
194054359Sroberto	 */
194154359Sroberto	if ((*str >= 'A' && *str <= 'F') || (*str >= 'a' && *str <= 'f'))
194254359Sroberto	    return hextolfp(str, lfp);
194354359Sroberto
194454359Sroberto	/*
194554359Sroberto	 * Try it as a decimal.  If this fails, try as an unquoted
194654359Sroberto	 * RT-11 date.  This code should go away eventually.
194754359Sroberto	 */
194854359Sroberto	if (atolfp(str, lfp))
194954359Sroberto	    return 1;
195054359Sroberto	return rtdatetolfp(str, lfp);
195154359Sroberto}
195254359Sroberto
195354359Sroberto
195454359Sroberto/*
195554359Sroberto * decodetime - decode a time value.  It should be in milliseconds
195654359Sroberto */
195754359Srobertoint
195854359Srobertodecodetime(
195954359Sroberto	char *str,
196054359Sroberto	l_fp *lfp
196154359Sroberto	)
196254359Sroberto{
196354359Sroberto	return mstolfp(str, lfp);
196454359Sroberto}
196554359Sroberto
196654359Sroberto
196754359Sroberto/*
196854359Sroberto * decodeint - decode an integer
196954359Sroberto */
197054359Srobertoint
197154359Srobertodecodeint(
197254359Sroberto	char *str,
197354359Sroberto	long *val
197454359Sroberto	)
197554359Sroberto{
197654359Sroberto	if (*str == '0') {
197754359Sroberto		if (*(str+1) == 'x' || *(str+1) == 'X')
1978182007Sroberto		    return hextoint(str+2, val);
1979182007Sroberto		return octtoint(str, val);
198054359Sroberto	}
198154359Sroberto	return atoint(str, val);
198254359Sroberto}
198354359Sroberto
198454359Sroberto
198554359Sroberto/*
198654359Sroberto * decodeuint - decode an unsigned integer
198754359Sroberto */
198854359Srobertoint
198954359Srobertodecodeuint(
199054359Sroberto	char *str,
199154359Sroberto	u_long *val
199254359Sroberto	)
199354359Sroberto{
199454359Sroberto	if (*str == '0') {
199554359Sroberto		if (*(str + 1) == 'x' || *(str + 1) == 'X')
199654359Sroberto			return (hextoint(str + 2, val));
199754359Sroberto		return (octtoint(str, val));
199854359Sroberto	}
199954359Sroberto	return (atouint(str, val));
200054359Sroberto}
200154359Sroberto
200254359Sroberto
200354359Sroberto/*
200454359Sroberto * decodearr - decode an array of time values
200554359Sroberto */
200654359Srobertostatic int
200754359Srobertodecodearr(
200854359Sroberto	char *str,
200954359Sroberto	int *narr,
201054359Sroberto	l_fp *lfparr
201154359Sroberto	)
201254359Sroberto{
201354359Sroberto	register char *cp, *bp;
201454359Sroberto	register l_fp *lfp;
201554359Sroberto	char buf[60];
201654359Sroberto
201754359Sroberto	lfp = lfparr;
201854359Sroberto	cp = str;
201954359Sroberto	*narr = 0;
202054359Sroberto
202154359Sroberto	while (*narr < 8) {
202254359Sroberto		while (isspace((int)*cp))
202354359Sroberto		    cp++;
202454359Sroberto		if (*cp == '\0')
202554359Sroberto		    break;
202654359Sroberto
202754359Sroberto		bp = buf;
202854359Sroberto		while (!isspace((int)*cp) && *cp != '\0')
202954359Sroberto		    *bp++ = *cp++;
203054359Sroberto		*bp++ = '\0';
203154359Sroberto
203254359Sroberto		if (!decodetime(buf, lfp))
203354359Sroberto		    return 0;
203454359Sroberto		(*narr)++;
203554359Sroberto		lfp++;
203654359Sroberto	}
203754359Sroberto	return 1;
203854359Sroberto}
203954359Sroberto
204054359Sroberto
204154359Sroberto/*
204254359Sroberto * Finally, the built in command handlers
204354359Sroberto */
204454359Sroberto
204554359Sroberto/*
204654359Sroberto * help - tell about commands, or details of a particular command
204754359Sroberto */
204854359Srobertostatic void
204954359Srobertohelp(
205054359Sroberto	struct parse *pcmd,
205154359Sroberto	FILE *fp
205254359Sroberto	)
205354359Sroberto{
205454359Sroberto	struct xcmd *xcp;
205554359Sroberto	char *cmd;
2056182007Sroberto	const char *list[100];
2057182007Sroberto        int word, words;
2058182007Sroberto        int row, rows;
2059182007Sroberto        int col, cols;
206054359Sroberto
206154359Sroberto	if (pcmd->nargs == 0) {
2062182007Sroberto		words = 0;
206354359Sroberto		for (xcp = builtins; xcp->keyword != 0; xcp++) {
206454359Sroberto			if (*(xcp->keyword) != '?')
2065182007Sroberto			    list[words++] = xcp->keyword;
206654359Sroberto		}
206754359Sroberto		for (xcp = opcmds; xcp->keyword != 0; xcp++)
2068182007Sroberto		    list[words++] = xcp->keyword;
206954359Sroberto
2070182007Sroberto		qsort(
207154359Sroberto#ifdef QSORT_USES_VOID_P
2072182007Sroberto		    (void *)
207354359Sroberto#else
2074182007Sroberto		    (char *)
207554359Sroberto#endif
2076182007Sroberto			(list), (size_t)(words), sizeof(char *), helpsort);
2077182007Sroberto		col = 0;
2078182007Sroberto		for (word = 0; word < words; word++) {
2079182007Sroberto		 	int length = strlen(list[word]);
2080182007Sroberto			if (col < length) {
2081182007Sroberto			    col = length;
2082182007Sroberto                        }
208354359Sroberto		}
208454359Sroberto
2085182007Sroberto		cols = SCREENWIDTH / ++col;
2086182007Sroberto                rows = (words + cols - 1) / cols;
2087182007Sroberto
2088182007Sroberto		(void) fprintf(fp, "ntpq commands:\n");
2089182007Sroberto
2090182007Sroberto		for (row = 0; row < rows; row++) {
2091182007Sroberto                        for (word = row; word < words; word += rows) {
2092182007Sroberto			        (void) fprintf(fp, "%-*.*s", col, col-1, list[word]);
2093182007Sroberto                        }
2094182007Sroberto                        (void) fprintf(fp, "\n");
2095182007Sroberto                }
209654359Sroberto	} else {
209754359Sroberto		cmd = pcmd->argval[0].string;
2098182007Sroberto		words = findcmd(cmd, builtins, opcmds, &xcp);
2099182007Sroberto		if (words == 0) {
210054359Sroberto			(void) fprintf(stderr,
210154359Sroberto				       "Command `%s' is unknown\n", cmd);
210254359Sroberto			return;
2103182007Sroberto		} else if (words >= 2) {
210454359Sroberto			(void) fprintf(stderr,
210554359Sroberto				       "Command `%s' is ambiguous\n", cmd);
210654359Sroberto			return;
210754359Sroberto		}
210854359Sroberto		(void) fprintf(fp, "function: %s\n", xcp->comment);
210954359Sroberto		printusage(xcp, fp);
211054359Sroberto	}
211154359Sroberto}
211254359Sroberto
211354359Sroberto
211454359Sroberto/*
211554359Sroberto * helpsort - do hostname qsort comparisons
211654359Sroberto */
211754359Sroberto#ifdef QSORT_USES_VOID_P
211854359Srobertostatic int
211954359Srobertohelpsort(
212054359Sroberto	const void *t1,
212154359Sroberto	const void *t2
212254359Sroberto	)
212354359Sroberto{
2124132451Sroberto	char const * const * name1 = (char const * const *)t1;
2125132451Sroberto	char const * const * name2 = (char const * const *)t2;
212654359Sroberto
212754359Sroberto	return strcmp(*name1, *name2);
212854359Sroberto}
212954359Sroberto
213054359Sroberto#else
213154359Srobertostatic int
213254359Srobertohelpsort(
213354359Sroberto	char **name1,
213454359Sroberto	char **name2
213554359Sroberto	)
213654359Sroberto{
213754359Sroberto	return strcmp(*name1, *name2);
213854359Sroberto}
213954359Sroberto#endif
214054359Sroberto
214154359Sroberto/*
214254359Sroberto * printusage - print usage information for a command
214354359Sroberto */
214454359Srobertostatic void
214554359Srobertoprintusage(
214654359Sroberto	struct xcmd *xcp,
214754359Sroberto	FILE *fp
214854359Sroberto	)
214954359Sroberto{
215054359Sroberto	register int i;
215154359Sroberto
215254359Sroberto	(void) fprintf(fp, "usage: %s", xcp->keyword);
215354359Sroberto	for (i = 0; i < MAXARGS && xcp->arg[i] != NO; i++) {
215454359Sroberto		if (xcp->arg[i] & OPT)
215554359Sroberto		    (void) fprintf(fp, " [ %s ]", xcp->desc[i]);
215654359Sroberto		else
215754359Sroberto		    (void) fprintf(fp, " %s", xcp->desc[i]);
215854359Sroberto	}
215954359Sroberto	(void) fprintf(fp, "\n");
216054359Sroberto}
216154359Sroberto
216254359Sroberto
216354359Sroberto/*
216454359Sroberto * timeout - set time out time
216554359Sroberto */
216654359Srobertostatic void
216754359Srobertotimeout(
216854359Sroberto	struct parse *pcmd,
216954359Sroberto	FILE *fp
217054359Sroberto	)
217154359Sroberto{
217254359Sroberto	int val;
217354359Sroberto
217454359Sroberto	if (pcmd->nargs == 0) {
217554359Sroberto		val = tvout.tv_sec * 1000 + tvout.tv_usec / 1000;
217654359Sroberto		(void) fprintf(fp, "primary timeout %d ms\n", val);
217754359Sroberto	} else {
217854359Sroberto		tvout.tv_sec = pcmd->argval[0].uval / 1000;
217954359Sroberto		tvout.tv_usec = (pcmd->argval[0].uval - (tvout.tv_sec * 1000))
218054359Sroberto			* 1000;
218154359Sroberto	}
218254359Sroberto}
218354359Sroberto
218454359Sroberto
218554359Sroberto/*
218654359Sroberto * auth_delay - set delay for auth requests
218754359Sroberto */
218854359Srobertostatic void
218954359Srobertoauth_delay(
219054359Sroberto	struct parse *pcmd,
219154359Sroberto	FILE *fp
219254359Sroberto	)
219354359Sroberto{
219454359Sroberto	int isneg;
219554359Sroberto	u_long val;
219654359Sroberto
219754359Sroberto	if (pcmd->nargs == 0) {
219854359Sroberto		val = delay_time.l_ui * 1000 + delay_time.l_uf / 4294967;
219954359Sroberto		(void) fprintf(fp, "delay %lu ms\n", val);
220054359Sroberto	} else {
220154359Sroberto		if (pcmd->argval[0].ival < 0) {
220254359Sroberto			isneg = 1;
220354359Sroberto			val = (u_long)(-pcmd->argval[0].ival);
220454359Sroberto		} else {
220554359Sroberto			isneg = 0;
220654359Sroberto			val = (u_long)pcmd->argval[0].ival;
220754359Sroberto		}
220854359Sroberto
220954359Sroberto		delay_time.l_ui = val / 1000;
221054359Sroberto		val %= 1000;
221154359Sroberto		delay_time.l_uf = val * 4294967;	/* 2**32/1000 */
221254359Sroberto
221354359Sroberto		if (isneg)
221454359Sroberto		    L_NEG(&delay_time);
221554359Sroberto	}
221654359Sroberto}
221754359Sroberto
221854359Sroberto
221954359Sroberto/*
222054359Sroberto * host - set the host we are dealing with.
222154359Sroberto */
222254359Srobertostatic void
222354359Srobertohost(
222454359Sroberto	struct parse *pcmd,
222554359Sroberto	FILE *fp
222654359Sroberto	)
222754359Sroberto{
2228132451Sroberto	int i;
2229132451Sroberto
223054359Sroberto	if (pcmd->nargs == 0) {
223154359Sroberto		if (havehost)
223254359Sroberto		    (void) fprintf(fp, "current host is %s\n", currenthost);
223354359Sroberto		else
223454359Sroberto		    (void) fprintf(fp, "no current host\n");
2235132451Sroberto		return;
2236132451Sroberto	}
2237132451Sroberto
2238132451Sroberto	i = 0;
2239132451Sroberto	ai_fam_templ = ai_fam_default;
2240132451Sroberto	if (pcmd->nargs == 2) {
2241132451Sroberto		if (!strcmp("-4", pcmd->argval[i].string))
2242132451Sroberto			ai_fam_templ = AF_INET;
2243132451Sroberto		else if (!strcmp("-6", pcmd->argval[i].string))
2244132451Sroberto			ai_fam_templ = AF_INET6;
2245132451Sroberto		else {
2246132451Sroberto			if (havehost)
2247132451Sroberto				(void) fprintf(fp,
2248132451Sroberto				    "current host remains %s\n", currenthost);
2249132451Sroberto			else
2250132451Sroberto				(void) fprintf(fp, "still no current host\n");
2251132451Sroberto			return;
2252132451Sroberto		}
2253132451Sroberto		i = 1;
2254132451Sroberto	}
2255132451Sroberto	if (openhost(pcmd->argval[i].string)) {
225654359Sroberto		(void) fprintf(fp, "current host set to %s\n", currenthost);
225754359Sroberto		numassoc = 0;
225854359Sroberto	} else {
225954359Sroberto		if (havehost)
226054359Sroberto		    (void) fprintf(fp,
226154359Sroberto				   "current host remains %s\n", currenthost);
226254359Sroberto		else
226354359Sroberto		    (void) fprintf(fp, "still no current host\n");
226454359Sroberto	}
226554359Sroberto}
226654359Sroberto
226754359Sroberto
226854359Sroberto/*
226954359Sroberto * poll - do one (or more) polls of the host via NTP
227054359Sroberto */
227154359Sroberto/*ARGSUSED*/
227254359Srobertostatic void
227354359Srobertontp_poll(
227454359Sroberto	struct parse *pcmd,
227554359Sroberto	FILE *fp
227654359Sroberto	)
227754359Sroberto{
227854359Sroberto	(void) fprintf(fp, "poll not implemented yet\n");
227954359Sroberto}
228054359Sroberto
228154359Sroberto
228254359Sroberto/*
228354359Sroberto * keyid - get a keyid to use for authenticating requests
228454359Sroberto */
228554359Srobertostatic void
228654359Srobertokeyid(
228754359Sroberto	struct parse *pcmd,
228854359Sroberto	FILE *fp
228954359Sroberto	)
229054359Sroberto{
229154359Sroberto	if (pcmd->nargs == 0) {
2292132451Sroberto		if (info_auth_keyid == 0)
229354359Sroberto		    (void) fprintf(fp, "no keyid defined\n");
229454359Sroberto		else
229554359Sroberto		    (void) fprintf(fp, "keyid is %lu\n", (u_long)info_auth_keyid);
229654359Sroberto	} else {
2297132451Sroberto		/* allow zero so that keyid can be cleared. */
2298132451Sroberto		if(pcmd->argval[0].uval > NTP_MAXKEY)
2299132451Sroberto		    (void) fprintf(fp, "Invalid key identifier\n");
230054359Sroberto		info_auth_keyid = pcmd->argval[0].uval;
230154359Sroberto	}
230254359Sroberto}
230354359Sroberto
230454359Sroberto/*
230554359Sroberto * keytype - get type of key to use for authenticating requests
230654359Sroberto */
230754359Srobertostatic void
230854359Srobertokeytype(
230954359Sroberto	struct parse *pcmd,
231054359Sroberto	FILE *fp
231154359Sroberto	)
231254359Sroberto{
231354359Sroberto	if (pcmd->nargs == 0)
231454359Sroberto	    fprintf(fp, "keytype is %s\n",
2315132451Sroberto		    (info_auth_keytype == KEY_TYPE_MD5) ? "MD5" : "???");
231654359Sroberto	else
231754359Sroberto	    switch (*(pcmd->argval[0].string)) {
231854359Sroberto		case 'm':
231954359Sroberto		case 'M':
232054359Sroberto		    info_auth_keytype = KEY_TYPE_MD5;
232154359Sroberto		    break;
232254359Sroberto
232354359Sroberto		default:
2324132451Sroberto		    fprintf(fp, "keytype must be 'md5'\n");
232554359Sroberto	    }
232654359Sroberto}
232754359Sroberto
232854359Sroberto
232954359Sroberto
233054359Sroberto/*
233154359Sroberto * passwd - get an authentication key
233254359Sroberto */
233354359Sroberto/*ARGSUSED*/
233454359Srobertostatic void
233554359Srobertopasswd(
233654359Sroberto	struct parse *pcmd,
233754359Sroberto	FILE *fp
233854359Sroberto	)
233954359Sroberto{
234054359Sroberto	char *pass;
234154359Sroberto
2342132451Sroberto	if (info_auth_keyid == 0) {
2343132451Sroberto		int u_keyid = getkeyid("Keyid: ");
2344132451Sroberto		if (u_keyid == 0 || u_keyid > NTP_MAXKEY) {
2345132451Sroberto			(void)fprintf(fp, "Invalid key identifier\n");
234654359Sroberto			return;
234754359Sroberto		}
2348132451Sroberto		info_auth_keyid = u_keyid;
234954359Sroberto	}
2350132451Sroberto	pass = getpass("MD5 Password: ");
235154359Sroberto	if (*pass == '\0')
235254359Sroberto	    (void) fprintf(fp, "Password unchanged\n");
2353132451Sroberto	else {
235454359Sroberto	    authusekey(info_auth_keyid, info_auth_keytype, (u_char *)pass);
2355132451Sroberto	    authtrust(info_auth_keyid, 1);
2356132451Sroberto	}
235754359Sroberto}
235854359Sroberto
235954359Sroberto
236054359Sroberto/*
236154359Sroberto * hostnames - set the showhostnames flag
236254359Sroberto */
236354359Srobertostatic void
236454359Srobertohostnames(
236554359Sroberto	struct parse *pcmd,
236654359Sroberto	FILE *fp
236754359Sroberto	)
236854359Sroberto{
236954359Sroberto	if (pcmd->nargs == 0) {
237054359Sroberto		if (showhostnames)
237154359Sroberto		    (void) fprintf(fp, "hostnames being shown\n");
237254359Sroberto		else
237354359Sroberto		    (void) fprintf(fp, "hostnames not being shown\n");
237454359Sroberto	} else {
237554359Sroberto		if (STREQ(pcmd->argval[0].string, "yes"))
237654359Sroberto		    showhostnames = 1;
237754359Sroberto		else if (STREQ(pcmd->argval[0].string, "no"))
237854359Sroberto		    showhostnames = 0;
237954359Sroberto		else
238054359Sroberto		    (void)fprintf(stderr, "What?\n");
238154359Sroberto	}
238254359Sroberto}
238354359Sroberto
238454359Sroberto
238554359Sroberto
238654359Sroberto/*
238754359Sroberto * setdebug - set/change debugging level
238854359Sroberto */
238954359Srobertostatic void
239054359Srobertosetdebug(
239154359Sroberto	struct parse *pcmd,
239254359Sroberto	FILE *fp
239354359Sroberto	)
239454359Sroberto{
239554359Sroberto	if (pcmd->nargs == 0) {
239654359Sroberto		(void) fprintf(fp, "debug level is %d\n", debug);
239754359Sroberto		return;
239854359Sroberto	} else if (STREQ(pcmd->argval[0].string, "no")) {
239954359Sroberto		debug = 0;
240054359Sroberto	} else if (STREQ(pcmd->argval[0].string, "more")) {
240154359Sroberto		debug++;
240254359Sroberto	} else if (STREQ(pcmd->argval[0].string, "less")) {
240354359Sroberto		debug--;
240454359Sroberto	} else {
240554359Sroberto		(void) fprintf(fp, "What?\n");
240654359Sroberto		return;
240754359Sroberto	}
240854359Sroberto	(void) fprintf(fp, "debug level set to %d\n", debug);
240954359Sroberto}
241054359Sroberto
241154359Sroberto
241254359Sroberto/*
241354359Sroberto * quit - stop this nonsense
241454359Sroberto */
241554359Sroberto/*ARGSUSED*/
241654359Srobertostatic void
241754359Srobertoquit(
241854359Sroberto	struct parse *pcmd,
241954359Sroberto	FILE *fp
242054359Sroberto	)
242154359Sroberto{
242254359Sroberto	if (havehost)
242354359Sroberto	    closesocket(sockfd);	/* cleanliness next to godliness */
242454359Sroberto	exit(0);
242554359Sroberto}
242654359Sroberto
242754359Sroberto
242854359Sroberto/*
242954359Sroberto * version - print the current version number
243054359Sroberto */
243154359Sroberto/*ARGSUSED*/
243254359Srobertostatic void
243354359Srobertoversion(
243454359Sroberto	struct parse *pcmd,
243554359Sroberto	FILE *fp
243654359Sroberto	)
243754359Sroberto{
243854359Sroberto
243954359Sroberto	(void) fprintf(fp, "%s\n", Version);
244054359Sroberto	return;
244154359Sroberto}
244254359Sroberto
244354359Sroberto
244454359Sroberto/*
244554359Sroberto * raw - set raw mode output
244654359Sroberto */
244754359Sroberto/*ARGSUSED*/
244854359Srobertostatic void
244954359Srobertoraw(
245054359Sroberto	struct parse *pcmd,
245154359Sroberto	FILE *fp
245254359Sroberto	)
245354359Sroberto{
245454359Sroberto	rawmode = 1;
245554359Sroberto	(void) fprintf(fp, "Output set to raw\n");
245654359Sroberto}
245754359Sroberto
245854359Sroberto
245954359Sroberto/*
246054359Sroberto * cooked - set cooked mode output
246154359Sroberto */
246254359Sroberto/*ARGSUSED*/
246354359Srobertostatic void
246454359Srobertocooked(
246554359Sroberto	struct parse *pcmd,
246654359Sroberto	FILE *fp
246754359Sroberto	)
246854359Sroberto{
246954359Sroberto	rawmode = 0;
247054359Sroberto	(void) fprintf(fp, "Output set to cooked\n");
247154359Sroberto	return;
247254359Sroberto}
247354359Sroberto
247454359Sroberto
247554359Sroberto/*
247654359Sroberto * authenticate - always authenticate requests to this host
247754359Sroberto */
247854359Srobertostatic void
247954359Srobertoauthenticate(
248054359Sroberto	struct parse *pcmd,
248154359Sroberto	FILE *fp
248254359Sroberto	)
248354359Sroberto{
248454359Sroberto	if (pcmd->nargs == 0) {
248554359Sroberto		if (always_auth) {
248654359Sroberto			(void) fprintf(fp,
248754359Sroberto				       "authenticated requests being sent\n");
248854359Sroberto		} else
248954359Sroberto		    (void) fprintf(fp,
249054359Sroberto				   "unauthenticated requests being sent\n");
249154359Sroberto	} else {
249254359Sroberto		if (STREQ(pcmd->argval[0].string, "yes")) {
249354359Sroberto			always_auth = 1;
249454359Sroberto		} else if (STREQ(pcmd->argval[0].string, "no")) {
249554359Sroberto			always_auth = 0;
249654359Sroberto		} else
249754359Sroberto		    (void)fprintf(stderr, "What?\n");
249854359Sroberto	}
249954359Sroberto}
250054359Sroberto
250154359Sroberto
250254359Sroberto/*
250354359Sroberto * ntpversion - choose the NTP version to use
250454359Sroberto */
250554359Srobertostatic void
250654359Srobertontpversion(
250754359Sroberto	struct parse *pcmd,
250854359Sroberto	FILE *fp
250954359Sroberto	)
251054359Sroberto{
251154359Sroberto	if (pcmd->nargs == 0) {
251254359Sroberto		(void) fprintf(fp,
251354359Sroberto			       "NTP version being claimed is %d\n", pktversion);
251454359Sroberto	} else {
251554359Sroberto		if (pcmd->argval[0].uval < NTP_OLDVERSION
251654359Sroberto		    || pcmd->argval[0].uval > NTP_VERSION) {
251754359Sroberto			(void) fprintf(stderr, "versions %d to %d, please\n",
251854359Sroberto				       NTP_OLDVERSION, NTP_VERSION);
251954359Sroberto		} else {
252054359Sroberto			pktversion = (u_char) pcmd->argval[0].uval;
252154359Sroberto		}
252254359Sroberto	}
252354359Sroberto}
252454359Sroberto
252554359Sroberto
252654359Sroberto/*
252754359Sroberto * warning - print a warning message
252854359Sroberto */
252954359Srobertostatic void
253054359Srobertowarning(
253154359Sroberto	const char *fmt,
253254359Sroberto	const char *st1,
253354359Sroberto	const char *st2
253454359Sroberto	)
253554359Sroberto{
253654359Sroberto	(void) fprintf(stderr, "%s: ", progname);
253754359Sroberto	(void) fprintf(stderr, fmt, st1, st2);
253854359Sroberto	(void) fprintf(stderr, ": ");
253954359Sroberto	perror("");
254054359Sroberto}
254154359Sroberto
254254359Sroberto
254354359Sroberto/*
254454359Sroberto * error - print a message and exit
254554359Sroberto */
254654359Srobertostatic void
254754359Srobertoerror(
254854359Sroberto	const char *fmt,
254954359Sroberto	const char *st1,
255054359Sroberto	const char *st2
255154359Sroberto	)
255254359Sroberto{
255354359Sroberto	warning(fmt, st1, st2);
255454359Sroberto	exit(1);
255554359Sroberto}
255654359Sroberto
255754359Sroberto/*
255854359Sroberto * getkeyid - prompt the user for a keyid to use
255954359Sroberto */
256054359Srobertostatic u_long
256154359Srobertogetkeyid(
256254359Sroberto	const char *keyprompt
256354359Sroberto	)
256454359Sroberto{
256554359Sroberto	register char *p;
256654359Sroberto	register int c;
256754359Sroberto	FILE *fi;
256854359Sroberto	char pbuf[20];
256954359Sroberto
257054359Sroberto#ifndef SYS_WINNT
257154359Sroberto	if ((fi = fdopen(open("/dev/tty", 2), "r")) == NULL)
257254359Sroberto#else
257354359Sroberto	    if ((fi = _fdopen((int)GetStdHandle(STD_INPUT_HANDLE), "r")) == NULL)
257454359Sroberto#endif /* SYS_WINNT */
257554359Sroberto		fi = stdin;
257654359Sroberto	    else
257754359Sroberto		setbuf(fi, (char *)NULL);
257854359Sroberto	fprintf(stderr, "%s", keyprompt); fflush(stderr);
257954359Sroberto	for (p=pbuf; (c = getc(fi))!='\n' && c!=EOF;) {
258054359Sroberto		if (p < &pbuf[18])
2581132451Sroberto		    *p++ = (char)c;
258254359Sroberto	}
258354359Sroberto	*p = '\0';
258454359Sroberto	if (fi != stdin)
258554359Sroberto	    fclose(fi);
258654359Sroberto	if (strcmp(pbuf, "0") == 0)
258754359Sroberto	    return 0;
258854359Sroberto
258954359Sroberto	return (u_long) atoi(pbuf);
259054359Sroberto}
259154359Sroberto
259254359Sroberto
259354359Sroberto/*
259454359Sroberto * atoascii - printable-ize possibly ascii data using the character
259554359Sroberto *	      transformations cat -v uses.
259654359Sroberto */
259754359Srobertostatic void
259854359Srobertoatoascii(
259954359Sroberto	int length,
260054359Sroberto	char *data,
260154359Sroberto	char *outdata
260254359Sroberto	)
260354359Sroberto{
260454359Sroberto	register u_char *cp;
260554359Sroberto	register u_char *ocp;
260654359Sroberto	register u_char c;
260754359Sroberto
260854359Sroberto	if (!data)
260954359Sroberto	{
261054359Sroberto		*outdata = '\0';
261154359Sroberto		return;
261254359Sroberto	}
261354359Sroberto
261454359Sroberto	ocp = (u_char *)outdata;
261554359Sroberto	for (cp = (u_char *)data; cp < (u_char *)data + length; cp++) {
261654359Sroberto		c = *cp;
261754359Sroberto		if (c == '\0')
261854359Sroberto		    break;
261954359Sroberto		if (c == '\0')
262054359Sroberto		    break;
262154359Sroberto		if (c > 0177) {
262254359Sroberto			*ocp++ = 'M';
262354359Sroberto			*ocp++ = '-';
262454359Sroberto			c &= 0177;
262554359Sroberto		}
262654359Sroberto
262754359Sroberto		if (c < ' ') {
262854359Sroberto			*ocp++ = '^';
2629132451Sroberto			*ocp++ = (u_char)(c + '@');
263054359Sroberto		} else if (c == 0177) {
263154359Sroberto			*ocp++ = '^';
263254359Sroberto			*ocp++ = '?';
263354359Sroberto		} else {
263454359Sroberto			*ocp++ = c;
263554359Sroberto		}
263654359Sroberto		if (ocp >= ((u_char *)outdata + length - 4))
263754359Sroberto		    break;
263854359Sroberto	}
263954359Sroberto	*ocp++ = '\0';
264054359Sroberto}
264154359Sroberto
264254359Sroberto
264354359Sroberto
264454359Sroberto/*
264554359Sroberto * makeascii - print possibly ascii data using the character
264654359Sroberto *	       transformations that cat -v uses.
264754359Sroberto */
264854359Srobertostatic void
264954359Srobertomakeascii(
265054359Sroberto	int length,
265154359Sroberto	char *data,
265254359Sroberto	FILE *fp
265354359Sroberto	)
265454359Sroberto{
265554359Sroberto	register u_char *cp;
265654359Sroberto	register int c;
265754359Sroberto
265854359Sroberto	for (cp = (u_char *)data; cp < (u_char *)data + length; cp++) {
265954359Sroberto		c = (int)*cp;
266054359Sroberto		if (c > 0177) {
266154359Sroberto			putc('M', fp);
266254359Sroberto			putc('-', fp);
266354359Sroberto			c &= 0177;
266454359Sroberto		}
266554359Sroberto
266654359Sroberto		if (c < ' ') {
266754359Sroberto			putc('^', fp);
266854359Sroberto			putc(c+'@', fp);
266954359Sroberto		} else if (c == 0177) {
267054359Sroberto			putc('^', fp);
267154359Sroberto			putc('?', fp);
267254359Sroberto		} else {
267354359Sroberto			putc(c, fp);
267454359Sroberto		}
267554359Sroberto	}
267654359Sroberto}
267754359Sroberto
267854359Sroberto
267954359Sroberto/*
268054359Sroberto * asciize - same thing as makeascii except add a newline
268154359Sroberto */
268254359Srobertovoid
268354359Srobertoasciize(
268454359Sroberto	int length,
268554359Sroberto	char *data,
268654359Sroberto	FILE *fp
268754359Sroberto	)
268854359Sroberto{
268954359Sroberto	makeascii(length, data, fp);
269054359Sroberto	putc('\n', fp);
269154359Sroberto}
269254359Sroberto
269354359Sroberto
269454359Sroberto/*
269554359Sroberto * Some circular buffer space
269654359Sroberto */
269754359Sroberto#define	CBLEN	80
269854359Sroberto#define	NUMCB	6
269954359Sroberto
270054359Srobertochar circ_buf[NUMCB][CBLEN];
270154359Srobertoint nextcb = 0;
270254359Sroberto
270354359Sroberto/*
270454359Sroberto * nextvar - find the next variable in the buffer
270554359Sroberto */
270654359Srobertoint
270754359Srobertonextvar(
270854359Sroberto	int *datalen,
270954359Sroberto	char **datap,
271054359Sroberto	char **vname,
271154359Sroberto	char **vvalue
271254359Sroberto	)
271354359Sroberto{
271454359Sroberto	register char *cp;
271554359Sroberto	register char *np;
271654359Sroberto	register char *cpend;
271754359Sroberto	register char *npend;	/* character after last */
271854359Sroberto	int quoted = 0;
271954359Sroberto	static char name[MAXVARLEN];
272054359Sroberto	static char value[MAXVALLEN];
272154359Sroberto
272254359Sroberto	cp = *datap;
272354359Sroberto	cpend = cp + *datalen;
272454359Sroberto
272554359Sroberto	/*
272654359Sroberto	 * Space past commas and white space
272754359Sroberto	 */
272854359Sroberto	while (cp < cpend && (*cp == ',' || isspace((int)*cp)))
272954359Sroberto	    cp++;
273054359Sroberto	if (cp == cpend)
273154359Sroberto	    return 0;
273254359Sroberto
273354359Sroberto	/*
273454359Sroberto	 * Copy name until we hit a ',', an '=', a '\r' or a '\n'.  Backspace
273554359Sroberto	 * over any white space and terminate it.
273654359Sroberto	 */
273754359Sroberto	np = name;
273854359Sroberto	npend = &name[MAXVARLEN];
273954359Sroberto	while (cp < cpend && np < npend && *cp != ',' && *cp != '='
274054359Sroberto	       && *cp != '\r' && *cp != '\n')
274154359Sroberto	    *np++ = *cp++;
274254359Sroberto	/*
274354359Sroberto	 * Check if we ran out of name space, without reaching the end or a
274454359Sroberto	 * terminating character
274554359Sroberto	 */
274654359Sroberto	if (np == npend && !(cp == cpend || *cp == ',' || *cp == '=' ||
274754359Sroberto			     *cp == '\r' || *cp == '\n'))
274854359Sroberto	    return 0;
274954359Sroberto	while (isspace((int)(*(np-1))))
275054359Sroberto	    np--;
275154359Sroberto	*np = '\0';
275254359Sroberto	*vname = name;
275354359Sroberto
275454359Sroberto	/*
275554359Sroberto	 * Check if we hit the end of the buffer or a ','.  If so we are done.
275654359Sroberto	 */
275754359Sroberto	if (cp == cpend || *cp == ',' || *cp == '\r' || *cp == '\n') {
275854359Sroberto		if (cp != cpend)
275954359Sroberto		    cp++;
276054359Sroberto		*datap = cp;
276154359Sroberto		*datalen = cpend - cp;
276254359Sroberto		*vvalue = (char *)0;
276354359Sroberto		return 1;
276454359Sroberto	}
276554359Sroberto
276654359Sroberto	/*
276754359Sroberto	 * So far, so good.  Copy out the value
276854359Sroberto	 */
276954359Sroberto	cp++;	/* past '=' */
277054359Sroberto	while (cp < cpend && (isspace((int)*cp) && *cp != '\r' && *cp != '\n'))
277154359Sroberto	    cp++;
277254359Sroberto	np = value;
277354359Sroberto	npend = &value[MAXVALLEN];
277454359Sroberto	while (cp < cpend && np < npend && ((*cp != ',') || quoted))
277554359Sroberto	{
277654359Sroberto		quoted ^= ((*np++ = *cp++) == '"');
277754359Sroberto	}
277854359Sroberto
277954359Sroberto	/*
278054359Sroberto	 * Check if we overran the value buffer while still in a quoted string
278154359Sroberto	 * or without finding a comma
278254359Sroberto	 */
278354359Sroberto	if (np == npend && (quoted || *cp != ','))
278454359Sroberto	    return 0;
278554359Sroberto	/*
278654359Sroberto	 * Trim off any trailing whitespace
278754359Sroberto	 */
278854359Sroberto	while (np > value && isspace((int)(*(np-1))))
278954359Sroberto	    np--;
279054359Sroberto	*np = '\0';
279154359Sroberto
279254359Sroberto	/*
279354359Sroberto	 * Return this.  All done.
279454359Sroberto	 */
279554359Sroberto	if (cp != cpend)
279654359Sroberto	    cp++;
279754359Sroberto	*datap = cp;
279854359Sroberto	*datalen = cpend - cp;
279954359Sroberto	*vvalue = value;
280054359Sroberto	return 1;
280154359Sroberto}
280254359Sroberto
280354359Sroberto
280454359Sroberto/*
2805182007Sroberto * findvar - see if this variable is known to us.
2806182007Sroberto * If "code" is 1, return ctl_var->code.
2807182007Sroberto * Otherwise return the ordinal position of the found variable.
280854359Sroberto */
280954359Srobertoint
281054359Srobertofindvar(
281154359Sroberto	char *varname,
2812182007Sroberto	struct ctl_var *varlist,
2813182007Sroberto	int code
281454359Sroberto	)
281554359Sroberto{
281654359Sroberto	register char *np;
281754359Sroberto	register struct ctl_var *vl;
281854359Sroberto
281954359Sroberto	vl = varlist;
282054359Sroberto	np = varname;
282154359Sroberto	while (vl->fmt != EOV) {
282254359Sroberto		if (vl->fmt != PADDING && STREQ(np, vl->text))
2823182007Sroberto		    return (code)
2824182007Sroberto				? vl->code
2825182007Sroberto				: (vl - varlist)
2826182007Sroberto			    ;
282754359Sroberto		vl++;
282854359Sroberto	}
282954359Sroberto	return 0;
283054359Sroberto}
283154359Sroberto
283254359Sroberto
283354359Sroberto
283454359Sroberto/*
283554359Sroberto * printvars - print variables returned in response packet
283654359Sroberto */
283754359Srobertovoid
283854359Srobertoprintvars(
283954359Sroberto	int length,
284054359Sroberto	char *data,
284154359Sroberto	int status,
284254359Sroberto	int sttype,
284354359Sroberto	FILE *fp
284454359Sroberto	)
284554359Sroberto{
284654359Sroberto	if (rawmode)
284754359Sroberto	    rawprint(sttype, length, data, status, fp);
284854359Sroberto	else
284954359Sroberto	    cookedprint(sttype, length, data, status, fp);
285054359Sroberto}
285154359Sroberto
285254359Sroberto
285354359Sroberto/*
285454359Sroberto * rawprint - do a printout of the data in raw mode
285554359Sroberto */
285654359Srobertostatic void
285754359Srobertorawprint(
285854359Sroberto	int datatype,
285954359Sroberto	int length,
286054359Sroberto	char *data,
286154359Sroberto	int status,
286254359Sroberto	FILE *fp
286354359Sroberto	)
286454359Sroberto{
286554359Sroberto	register char *cp;
286654359Sroberto	register char *cpend;
286754359Sroberto
286854359Sroberto	/*
286954359Sroberto	 * Essentially print the data as is.  We reformat unprintables, though.
287054359Sroberto	 */
287154359Sroberto	cp = data;
287254359Sroberto	cpend = data + length;
287354359Sroberto
287454359Sroberto	(void) fprintf(fp, "status=0x%04x,\n", status);
287554359Sroberto
287654359Sroberto	while (cp < cpend) {
287754359Sroberto		if (*cp == '\r') {
287854359Sroberto			/*
287954359Sroberto			 * If this is a \r and the next character is a
288054359Sroberto			 * \n, supress this, else pretty print it.  Otherwise
288154359Sroberto			 * just output the character.
288254359Sroberto			 */
288354359Sroberto			if (cp == (cpend-1) || *(cp+1) != '\n')
288454359Sroberto			    makeascii(1, cp, fp);
288554359Sroberto		} else if (isspace((int)*cp) || isprint((int)*cp)) {
288654359Sroberto			putc(*cp, fp);
288754359Sroberto		} else {
288854359Sroberto			makeascii(1, cp, fp);
288954359Sroberto		}
289054359Sroberto		cp++;
289154359Sroberto	}
289254359Sroberto}
289354359Sroberto
289454359Sroberto
289554359Sroberto/*
289654359Sroberto * Global data used by the cooked output routines
289754359Sroberto */
289854359Srobertoint out_chars;		/* number of characters output */
289954359Srobertoint out_linecount;	/* number of characters output on this line */
290054359Sroberto
290154359Sroberto
290254359Sroberto/*
290354359Sroberto * startoutput - get ready to do cooked output
290454359Sroberto */
290554359Srobertostatic void
290654359Srobertostartoutput(void)
290754359Sroberto{
290854359Sroberto	out_chars = 0;
290954359Sroberto	out_linecount = 0;
291054359Sroberto}
291154359Sroberto
291254359Sroberto
291354359Sroberto/*
291454359Sroberto * output - output a variable=value combination
291554359Sroberto */
291654359Srobertostatic void
291754359Srobertooutput(
291854359Sroberto	FILE *fp,
291954359Sroberto	char *name,
292054359Sroberto	char *value
292154359Sroberto	)
292254359Sroberto{
292354359Sroberto	int lenname;
292454359Sroberto	int lenvalue;
292554359Sroberto
292654359Sroberto	lenname = strlen(name);
292754359Sroberto	lenvalue = strlen(value);
292854359Sroberto
292954359Sroberto	if (out_chars != 0) {
293054359Sroberto		putc(',', fp);
293154359Sroberto		out_chars++;
293254359Sroberto		out_linecount++;
293354359Sroberto		if ((out_linecount + lenname + lenvalue + 3) > MAXOUTLINE) {
293454359Sroberto			putc('\n', fp);
293554359Sroberto			out_chars++;
293654359Sroberto			out_linecount = 0;
293754359Sroberto		} else {
293854359Sroberto			putc(' ', fp);
293954359Sroberto			out_chars++;
294054359Sroberto			out_linecount++;
294154359Sroberto		}
294254359Sroberto	}
294354359Sroberto
294454359Sroberto	fputs(name, fp);
294554359Sroberto	putc('=', fp);
294654359Sroberto	fputs(value, fp);
294754359Sroberto	out_chars += lenname + 1 + lenvalue;
294854359Sroberto	out_linecount += lenname + 1 + lenvalue;
294954359Sroberto}
295054359Sroberto
295154359Sroberto
295254359Sroberto/*
295354359Sroberto * endoutput - terminate a block of cooked output
295454359Sroberto */
295554359Srobertostatic void
295654359Srobertoendoutput(
295754359Sroberto	FILE *fp
295854359Sroberto	)
295954359Sroberto{
296054359Sroberto	if (out_chars != 0)
296154359Sroberto	    putc('\n', fp);
296254359Sroberto}
296354359Sroberto
296454359Sroberto
296554359Sroberto/*
296654359Sroberto * outputarr - output an array of values
296754359Sroberto */
296854359Srobertostatic void
296954359Srobertooutputarr(
297054359Sroberto	FILE *fp,
297154359Sroberto	char *name,
297254359Sroberto	int narr,
297354359Sroberto	l_fp *lfp
297454359Sroberto	)
297554359Sroberto{
297654359Sroberto	register char *bp;
297754359Sroberto	register char *cp;
297854359Sroberto	register int i;
297954359Sroberto	register int len;
298054359Sroberto	char buf[256];
298154359Sroberto
298254359Sroberto	bp = buf;
298354359Sroberto	/*
298454359Sroberto	 * Hack to align delay and offset values
298554359Sroberto	 */
298654359Sroberto	for (i = (int)strlen(name); i < 11; i++)
298754359Sroberto	    *bp++ = ' ';
298854359Sroberto
298954359Sroberto	for (i = narr; i > 0; i--) {
299054359Sroberto		if (i != narr)
299154359Sroberto		    *bp++ = ' ';
299254359Sroberto		cp = lfptoms(lfp, 2);
299354359Sroberto		len = strlen(cp);
299454359Sroberto		if (len > 7) {
299554359Sroberto			cp[7] = '\0';
299654359Sroberto			len = 7;
299754359Sroberto		}
299854359Sroberto		while (len < 7) {
299954359Sroberto			*bp++ = ' ';
300054359Sroberto			len++;
300154359Sroberto		}
300254359Sroberto		while (*cp != '\0')
300354359Sroberto		    *bp++ = *cp++;
300454359Sroberto		lfp++;
300554359Sroberto	}
300654359Sroberto	*bp = '\0';
300754359Sroberto	output(fp, name, buf);
300854359Sroberto}
300954359Sroberto
301054359Srobertostatic char *
301154359Srobertotstflags(
301254359Sroberto	u_long val
301354359Sroberto	)
301454359Sroberto{
301554359Sroberto	register char *cb, *s;
301654359Sroberto	register int i;
301754359Sroberto	register const char *sep;
301854359Sroberto
301954359Sroberto	sep = "";
302054359Sroberto	i = 0;
302154359Sroberto	s = cb = &circ_buf[nextcb][0];
302254359Sroberto	if (++nextcb >= NUMCB)
302354359Sroberto	    nextcb = 0;
302454359Sroberto
302554359Sroberto	sprintf(cb, "%02lx", val);
302654359Sroberto	cb += strlen(cb);
302754359Sroberto	if (!val) {
302854359Sroberto		strcat(cb, " ok");
302954359Sroberto		cb += strlen(cb);
303054359Sroberto	} else {
303154359Sroberto		*cb++ = ' ';
3032182007Sroberto		for (i = 0; i < 13; i++) {
303354359Sroberto			if (val & 0x1) {
303454359Sroberto				sprintf(cb, "%s%s", sep, tstflagnames[i]);
303554359Sroberto				sep = ", ";
303654359Sroberto				cb += strlen(cb);
303754359Sroberto			}
303854359Sroberto			val >>= 1;
303954359Sroberto		}
304054359Sroberto	}
304154359Sroberto	*cb = '\0';
304254359Sroberto	return s;
304354359Sroberto}
304454359Sroberto
304554359Sroberto/*
304654359Sroberto * cookedprint - output variables in cooked mode
304754359Sroberto */
304854359Srobertostatic void
304954359Srobertocookedprint(
305054359Sroberto	int datatype,
305154359Sroberto	int length,
305254359Sroberto	char *data,
305354359Sroberto	int status,
305454359Sroberto	FILE *fp
305554359Sroberto	)
305654359Sroberto{
305754359Sroberto	register int varid;
305854359Sroberto	char *name;
305954359Sroberto	char *value;
3060132451Sroberto	char output_raw;
306154359Sroberto	int fmt;
306254359Sroberto	struct ctl_var *varlist;
306354359Sroberto	l_fp lfp;
306454359Sroberto	long ival;
3065132451Sroberto	struct sockaddr_storage hval;
306654359Sroberto	u_long uval;
306754359Sroberto	l_fp lfparr[8];
306854359Sroberto	int narr;
306954359Sroberto
307054359Sroberto	switch (datatype) {
307154359Sroberto	    case TYPE_PEER:
307254359Sroberto		varlist = peer_var;
307354359Sroberto		break;
307454359Sroberto	    case TYPE_SYS:
307554359Sroberto		varlist = sys_var;
307654359Sroberto		break;
307754359Sroberto	    case TYPE_CLOCK:
307854359Sroberto		varlist = clock_var;
307954359Sroberto		break;
308054359Sroberto	    default:
308154359Sroberto		(void) fprintf(stderr, "Unknown datatype(0x%x) in cookedprint\n", datatype);
308254359Sroberto		return;
308354359Sroberto	}
308454359Sroberto
308554359Sroberto	(void) fprintf(fp, "status=%04x %s,\n", status,
308654359Sroberto		       statustoa(datatype, status));
308754359Sroberto
308854359Sroberto	startoutput();
308954359Sroberto	while (nextvar(&length, &data, &name, &value)) {
3090182007Sroberto		varid = findvar(name, varlist, 0);
309154359Sroberto		if (varid == 0) {
309254359Sroberto			output_raw = '*';
309354359Sroberto		} else {
309454359Sroberto			output_raw = 0;
309554359Sroberto			fmt = varlist[varid].fmt;
309654359Sroberto			switch(fmt) {
309754359Sroberto			    case TS:
309854359Sroberto				if (!decodets(value, &lfp))
309954359Sroberto				    output_raw = '?';
310054359Sroberto				else
310154359Sroberto				    output(fp, name, prettydate(&lfp));
310254359Sroberto				break;
310354359Sroberto			    case FL:
310454359Sroberto			    case FU:
310554359Sroberto			    case FS:
310654359Sroberto				if (!decodetime(value, &lfp))
310754359Sroberto				    output_raw = '?';
310854359Sroberto				else {
310954359Sroberto					switch (fmt) {
311054359Sroberto					    case FL:
311154359Sroberto						output(fp, name,
311254359Sroberto						       lfptoms(&lfp, 3));
311354359Sroberto						break;
311454359Sroberto					    case FU:
311554359Sroberto						output(fp, name,
311654359Sroberto						       ulfptoms(&lfp, 3));
311754359Sroberto						break;
311854359Sroberto					    case FS:
311954359Sroberto						output(fp, name,
312054359Sroberto						       lfptoms(&lfp, 3));
312154359Sroberto						break;
312254359Sroberto					}
312354359Sroberto				}
312454359Sroberto				break;
312554359Sroberto
312654359Sroberto			    case UI:
312754359Sroberto				if (!decodeuint(value, &uval))
312854359Sroberto				    output_raw = '?';
312954359Sroberto				else
313054359Sroberto				    output(fp, name, uinttoa(uval));
313154359Sroberto				break;
313254359Sroberto
313354359Sroberto			    case SI:
313454359Sroberto				if (!decodeint(value, &ival))
313554359Sroberto				    output_raw = '?';
313654359Sroberto				else
313754359Sroberto				    output(fp, name, inttoa(ival));
313854359Sroberto				break;
313954359Sroberto
314054359Sroberto			    case HA:
314154359Sroberto			    case NA:
314254359Sroberto				if (!decodenetnum(value, &hval))
314354359Sroberto				    output_raw = '?';
3144132451Sroberto				else if (fmt == HA){
3145132451Sroberto				    output(fp, name, nntohost(&hval));
3146132451Sroberto				} else {
3147132451Sroberto				    output(fp, name, stoa(&hval));
3148132451Sroberto				}
314954359Sroberto				break;
315054359Sroberto
315154359Sroberto			    case ST:
315254359Sroberto				output_raw = '*';
315354359Sroberto				break;
315454359Sroberto
315554359Sroberto			    case RF:
3156132451Sroberto				if (decodenetnum(value, &hval)) {
3157132451Sroberto					if ((hval.ss_family == AF_INET) &&
3158132451Sroberto					    ISREFCLOCKADR(&hval))
3159132451Sroberto    						output(fp, name,
3160132451Sroberto						    refnumtoa(&hval));
3161132451Sroberto					else
3162132451Sroberto				    		output(fp, name, stoa(&hval));
3163132451Sroberto				} else if ((int)strlen(value) <= 4)
316454359Sroberto				    output(fp, name, value);
316554359Sroberto				else
316654359Sroberto				    output_raw = '?';
316754359Sroberto				break;
316854359Sroberto
316954359Sroberto			    case LP:
317054359Sroberto				if (!decodeuint(value, &uval) || uval > 3)
317154359Sroberto				    output_raw = '?';
317254359Sroberto				else {
317354359Sroberto					char b[3];
317454359Sroberto					b[0] = b[1] = '0';
317554359Sroberto					if (uval & 0x2)
317654359Sroberto					    b[0] = '1';
317754359Sroberto					if (uval & 0x1)
317854359Sroberto					    b[1] = '1';
317954359Sroberto					b[2] = '\0';
318054359Sroberto					output(fp, name, b);
318154359Sroberto				}
318254359Sroberto				break;
318354359Sroberto
318454359Sroberto			    case OC:
318554359Sroberto				if (!decodeuint(value, &uval))
318654359Sroberto				    output_raw = '?';
318754359Sroberto				else {
3188191302Sroberto					char b[12];
318954359Sroberto
3190200576Sroberto					(void) snprintf(b, sizeof b, "%03lo", uval);
319154359Sroberto					output(fp, name, b);
319254359Sroberto				}
319354359Sroberto				break;
319454359Sroberto
319554359Sroberto			    case MD:
319654359Sroberto				if (!decodeuint(value, &uval))
319754359Sroberto				    output_raw = '?';
319854359Sroberto				else
319954359Sroberto				    output(fp, name, uinttoa(uval));
320054359Sroberto				break;
320154359Sroberto
320254359Sroberto			    case AR:
320354359Sroberto				if (!decodearr(value, &narr, lfparr))
320454359Sroberto				    output_raw = '?';
320554359Sroberto				else
320654359Sroberto				    outputarr(fp, name, narr, lfparr);
320754359Sroberto				break;
320854359Sroberto
320954359Sroberto			    case FX:
321054359Sroberto				if (!decodeuint(value, &uval))
321154359Sroberto				    output_raw = '?';
321254359Sroberto				else
321354359Sroberto				    output(fp, name, tstflags(uval));
321454359Sroberto				break;
321554359Sroberto
321654359Sroberto			    default:
321754359Sroberto				(void) fprintf(stderr,
321854359Sroberto				    "Internal error in cookedprint, %s=%s, fmt %d\n",
321954359Sroberto				    name, value, fmt);
322054359Sroberto				break;
322154359Sroberto			}
322254359Sroberto
322354359Sroberto		}
322454359Sroberto		if (output_raw != 0) {
322554359Sroberto			char bn[401];
322654359Sroberto			char bv[401];
322754359Sroberto			int len;
322854359Sroberto
322954359Sroberto			atoascii(400, name, bn);
323054359Sroberto			atoascii(400, value, bv);
323154359Sroberto			if (output_raw != '*') {
323254359Sroberto				len = strlen(bv);
323354359Sroberto				bv[len] = output_raw;
323454359Sroberto				bv[len+1] = '\0';
323554359Sroberto			}
323654359Sroberto			output(fp, bn, bv);
323754359Sroberto		}
323854359Sroberto	}
323954359Sroberto	endoutput(fp);
324054359Sroberto}
324154359Sroberto
324254359Sroberto
324354359Sroberto/*
324454359Sroberto * sortassoc - sort associations in the cache into ascending order
324554359Sroberto */
324654359Srobertovoid
324754359Srobertosortassoc(void)
324854359Sroberto{
324954359Sroberto	if (numassoc > 1)
325054359Sroberto	    qsort(
325154359Sroberto#ifdef QSORT_USES_VOID_P
325254359Sroberto		    (void *)
325354359Sroberto#else
325454359Sroberto		    (char *)
325554359Sroberto#endif
325682498Sroberto		    assoc_cache, (size_t)numassoc,
325754359Sroberto		    sizeof(struct association), assoccmp);
325854359Sroberto}
325954359Sroberto
326054359Sroberto
326154359Sroberto/*
326254359Sroberto * assoccmp - compare two associations
326354359Sroberto */
326454359Sroberto#ifdef QSORT_USES_VOID_P
326554359Srobertostatic int
326654359Srobertoassoccmp(
326754359Sroberto	const void *t1,
326854359Sroberto	const void *t2
326954359Sroberto	)
327054359Sroberto{
327154359Sroberto	const struct association *ass1 = (const struct association *)t1;
327254359Sroberto	const struct association *ass2 = (const struct association *)t2;
327354359Sroberto
327454359Sroberto	if (ass1->assid < ass2->assid)
327554359Sroberto	    return -1;
327654359Sroberto	if (ass1->assid > ass2->assid)
327754359Sroberto	    return 1;
327854359Sroberto	return 0;
327954359Sroberto}
328054359Sroberto#else
328154359Srobertostatic int
328254359Srobertoassoccmp(
328354359Sroberto	struct association *ass1,
328454359Sroberto	struct association *ass2
328554359Sroberto	)
328654359Sroberto{
328754359Sroberto	if (ass1->assid < ass2->assid)
328854359Sroberto	    return -1;
328954359Sroberto	if (ass1->assid > ass2->assid)
329054359Sroberto	    return 1;
329154359Sroberto	return 0;
329254359Sroberto}
329354359Sroberto#endif /* not QSORT_USES_VOID_P */
3294