xref: /netbsd-src/external/bsd/ntp/dist/util/ntptime.c (revision cdfa2a7ef92791ba9db70a584a1d904730e6fb46)
1*cdfa2a7eSchristos /*	$NetBSD: ntptime.c,v 1.9 2020/05/25 20:47:37 christos Exp $	*/
2abb0f93cSkardel 
3abb0f93cSkardel /*
4abb0f93cSkardel  * NTP test program
5abb0f93cSkardel  *
6abb0f93cSkardel  * This program tests to see if the NTP user interface routines
7abb0f93cSkardel  * ntp_gettime() and ntp_adjtime() have been implemented in the kernel.
8abb0f93cSkardel  * If so, each of these routines is called to display current timekeeping
9abb0f93cSkardel  * data.
10abb0f93cSkardel  *
11abb0f93cSkardel  * For more information, see the README.kern file in the doc directory
12abb0f93cSkardel  * of the xntp3 distribution.
13abb0f93cSkardel  */
14abb0f93cSkardel 
15abb0f93cSkardel #ifdef HAVE_CONFIG_H
16abb0f93cSkardel # include <config.h>
17abb0f93cSkardel #endif /* HAVE_CONFIG_H */
18abb0f93cSkardel 
19abb0f93cSkardel #include "ntp_fp.h"
202950cc38Schristos #include "timevalops.h"
21abb0f93cSkardel #include "ntp_syscall.h"
22abb0f93cSkardel #include "ntp_stdlib.h"
23abb0f93cSkardel 
24abb0f93cSkardel #include <stdio.h>
25abb0f93cSkardel #include <ctype.h>
26abb0f93cSkardel #include <signal.h>
27abb0f93cSkardel #include <setjmp.h>
28abb0f93cSkardel 
29abb0f93cSkardel #ifdef NTP_SYSCALLS_STD
30abb0f93cSkardel # ifndef SYS_DECOSF1
31abb0f93cSkardel #  define BADCALL -1		/* this is supposed to be a bad syscall */
32abb0f93cSkardel # endif /* SYS_DECOSF1 */
33abb0f93cSkardel #endif
34abb0f93cSkardel 
35abb0f93cSkardel #ifdef HAVE_STRUCT_NTPTIMEVAL_TIME_TV_NSEC
36abb0f93cSkardel #define tv_frac_sec tv_nsec
37abb0f93cSkardel #else
38abb0f93cSkardel #define tv_frac_sec tv_usec
39abb0f93cSkardel #endif
40abb0f93cSkardel 
41abb0f93cSkardel 
42abb0f93cSkardel #define TIMEX_MOD_BITS \
43abb0f93cSkardel "\20\1OFFSET\2FREQUENCY\3MAXERROR\4ESTERROR\5STATUS\6TIMECONST\
44abb0f93cSkardel \13PLL\14FLL\15MICRO\16NANO\17CLKB\20CLKA"
45abb0f93cSkardel 
46abb0f93cSkardel #define TIMEX_STA_BITS \
47abb0f93cSkardel "\20\1PLL\2PPSFREQ\3PPSTIME\4FLL\5INS\6DEL\7UNSYNC\10FREQHOLD\
48abb0f93cSkardel \11PPSSIGNAL\12PPSJITTER\13PPSWANDER\14PPSERROR\15CLOCKERR\
49abb0f93cSkardel \16NANO\17MODE\20CLK"
50abb0f93cSkardel 
51abb0f93cSkardel #define SCALE_FREQ 65536		/* frequency scale */
52abb0f93cSkardel 
532950cc38Schristos /*
542950cc38Schristos  * These constants are used to round the time stamps computed from
552950cc38Schristos  * a struct timeval to the microsecond (more or less).  This keeps
562950cc38Schristos  * things neat.
572950cc38Schristos  */
58*cdfa2a7eSchristos #define	TS_MASK_US	0xfffff000	/* mask to usec, for time stamps */
59*cdfa2a7eSchristos #define	TS_ROUNDBIT_US	0x00000800	/* round at this bit */
60*cdfa2a7eSchristos #define	TS_DIGITS_US	6
61*cdfa2a7eSchristos 
62*cdfa2a7eSchristos #define	TS_MASK_NS	0xfffffffc	/* 1/2^30, for nsec */
63*cdfa2a7eSchristos #define	TS_ROUNDBIT_NS	0x00000002
64*cdfa2a7eSchristos #define	TS_DIGITS_NS	9
65abb0f93cSkardel 
66abb0f93cSkardel /*
67abb0f93cSkardel  * Function prototypes
68abb0f93cSkardel  */
692950cc38Schristos const char *	sprintb		(u_int, const char *);
702950cc38Schristos const char *	timex_state	(int);
71abb0f93cSkardel 
72abb0f93cSkardel #ifdef SIGSYS
73abb0f93cSkardel void pll_trap		(int);
74abb0f93cSkardel 
75abb0f93cSkardel static struct sigaction newsigsys;	/* new sigaction status */
76abb0f93cSkardel static struct sigaction sigsys;		/* current sigaction status */
77abb0f93cSkardel static sigjmp_buf env;		/* environment var. for pll_trap() */
78abb0f93cSkardel #endif
79abb0f93cSkardel 
80abb0f93cSkardel static volatile int pll_control; /* (0) daemon, (1) kernel loop */
81abb0f93cSkardel static volatile int status;	/* most recent status bits */
82abb0f93cSkardel static volatile int flash;	/* most recent ntp_adjtime() bits */
83af12ab5eSchristos char const * progname;
84abb0f93cSkardel static char optargs[] = "MNT:cde:f:hm:o:rs:t:";
85abb0f93cSkardel 
86abb0f93cSkardel int
main(int argc,char * argv[])87abb0f93cSkardel main(
88abb0f93cSkardel 	int argc,
89abb0f93cSkardel 	char *argv[]
90abb0f93cSkardel 	)
91abb0f93cSkardel {
92abb0f93cSkardel 	extern int ntp_optind;
93abb0f93cSkardel 	extern char *ntp_optarg;
94abb0f93cSkardel #ifdef SUBST_ADJTIMEX
95abb0f93cSkardel 	struct timex ntv;
96abb0f93cSkardel #else
97abb0f93cSkardel 	struct ntptimeval ntv;
98abb0f93cSkardel #endif
99abb0f93cSkardel 	struct timeval tv;
100abb0f93cSkardel 	struct timex ntx, _ntx;
101af12ab5eSchristos 	int	times[20] = { 0 };
102abb0f93cSkardel 	double ftemp, gtemp, htemp;
103*cdfa2a7eSchristos 	volatile double nscale = 1.0;			/* assume usec scale for now */
104abb0f93cSkardel 	long time_frac;				/* ntv.time.tv_frac_sec (us/ns) */
105abb0f93cSkardel 	l_fp ts;
106*cdfa2a7eSchristos 	volatile unsigned ts_mask = TS_MASK_US;		/* defaults to 20 bits (us) */
107*cdfa2a7eSchristos 	volatile unsigned ts_roundbit = TS_ROUNDBIT_US;	/* defaults to 20 bits (us) */
108*cdfa2a7eSchristos 	volatile int fdigits = TS_DIGITS_US;		/* fractional digits for us */
109e19314b7Schristos 	size_t c;
110e19314b7Schristos 	int ch;
111abb0f93cSkardel 	int errflg	= 0;
112abb0f93cSkardel 	int cost	= 0;
113abb0f93cSkardel 	volatile int rawtime	= 0;
114abb0f93cSkardel 
1152950cc38Schristos 	ZERO(ntx);
116abb0f93cSkardel 	progname = argv[0];
1172950cc38Schristos 	while ((ch = ntp_getopt(argc, argv, optargs)) != EOF) {
1182950cc38Schristos 		switch (ch) {
119abb0f93cSkardel #ifdef MOD_MICRO
120abb0f93cSkardel 		case 'M':
121abb0f93cSkardel 			ntx.modes |= MOD_MICRO;
122abb0f93cSkardel 			break;
123abb0f93cSkardel #endif
124abb0f93cSkardel #ifdef MOD_NANO
125abb0f93cSkardel 		case 'N':
126abb0f93cSkardel 			ntx.modes |= MOD_NANO;
127abb0f93cSkardel 			break;
128abb0f93cSkardel #endif
129*cdfa2a7eSchristos #if defined(NTP_API) && NTP_API > 3
130abb0f93cSkardel 		case 'T':
131abb0f93cSkardel 			ntx.modes = MOD_TAI;
132abb0f93cSkardel 			ntx.constant = atoi(ntp_optarg);
133abb0f93cSkardel 			break;
134abb0f93cSkardel #endif
135abb0f93cSkardel 		case 'c':
136abb0f93cSkardel 			cost++;
137abb0f93cSkardel 			break;
1382950cc38Schristos 
139abb0f93cSkardel 		case 'e':
140abb0f93cSkardel 			ntx.modes |= MOD_ESTERROR;
141abb0f93cSkardel 			ntx.esterror = atoi(ntp_optarg);
142abb0f93cSkardel 			break;
1432950cc38Schristos 
144abb0f93cSkardel 		case 'f':
145abb0f93cSkardel 			ntx.modes |= MOD_FREQUENCY;
146abb0f93cSkardel 			ntx.freq = (long)(atof(ntp_optarg) * SCALE_FREQ);
147abb0f93cSkardel 			break;
1482950cc38Schristos 
149abb0f93cSkardel 		case 'm':
150abb0f93cSkardel 			ntx.modes |= MOD_MAXERROR;
151abb0f93cSkardel 			ntx.maxerror = atoi(ntp_optarg);
152abb0f93cSkardel 			break;
1532950cc38Schristos 
154abb0f93cSkardel 		case 'o':
155abb0f93cSkardel 			ntx.modes |= MOD_OFFSET;
156abb0f93cSkardel 			ntx.offset = atoi(ntp_optarg);
157abb0f93cSkardel 			break;
1582950cc38Schristos 
159abb0f93cSkardel 		case 'r':
160abb0f93cSkardel 			rawtime++;
161abb0f93cSkardel 			break;
1622950cc38Schristos 
163abb0f93cSkardel 		case 's':
164abb0f93cSkardel 			ntx.modes |= MOD_STATUS;
165abb0f93cSkardel 			ntx.status = atoi(ntp_optarg);
1662950cc38Schristos 			if (ntx.status < 0 || ntx.status >= 0x100)
1672950cc38Schristos 				errflg++;
168abb0f93cSkardel 			break;
1692950cc38Schristos 
170abb0f93cSkardel 		case 't':
171abb0f93cSkardel 			ntx.modes |= MOD_TIMECONST;
172abb0f93cSkardel 			ntx.constant = atoi(ntp_optarg);
173abb0f93cSkardel 			break;
1742950cc38Schristos 
175abb0f93cSkardel 		default:
176abb0f93cSkardel 			errflg++;
177abb0f93cSkardel 		}
1782950cc38Schristos 	}
179abb0f93cSkardel 	if (errflg || (ntp_optind != argc)) {
1802950cc38Schristos 		fprintf(stderr,
181abb0f93cSkardel 			"usage: %s [-%s]\n\n\
182abb0f93cSkardel %s%s%s\
183abb0f93cSkardel -c		display the time taken to call ntp_gettime (us)\n\
184abb0f93cSkardel -e esterror	estimate of the error (us)\n\
185abb0f93cSkardel -f frequency	Frequency error (-500 .. 500) (ppm)\n\
186abb0f93cSkardel -h		display this help info\n\
187abb0f93cSkardel -m maxerror	max possible error (us)\n\
188abb0f93cSkardel -o offset	current offset (ms)\n\
189abb0f93cSkardel -r		print the unix and NTP time raw\n\
190abb0f93cSkardel -s status	Set the status bits\n\
191abb0f93cSkardel -t timeconstant	log2 of PLL time constant (0 .. %d)\n",
192abb0f93cSkardel 			progname, optargs,
193abb0f93cSkardel #ifdef MOD_MICRO
194abb0f93cSkardel "-M		switch to microsecond mode\n",
195abb0f93cSkardel #else
196abb0f93cSkardel "",
197abb0f93cSkardel #endif
198abb0f93cSkardel #ifdef MOD_NANO
199abb0f93cSkardel "-N		switch to nanosecond mode\n",
200abb0f93cSkardel #else
201abb0f93cSkardel "",
202abb0f93cSkardel #endif
203abb0f93cSkardel #ifdef NTP_API
204abb0f93cSkardel # if NTP_API > 3
205abb0f93cSkardel "-T tai_offset	set TAI offset\n",
206abb0f93cSkardel # else
207abb0f93cSkardel "",
208abb0f93cSkardel # endif
209abb0f93cSkardel #else
210abb0f93cSkardel "",
211abb0f93cSkardel #endif
212abb0f93cSkardel 			MAXTC);
213abb0f93cSkardel 		exit(2);
214abb0f93cSkardel 	}
215abb0f93cSkardel 
216abb0f93cSkardel #ifdef SIGSYS
217abb0f93cSkardel 	/*
218abb0f93cSkardel 	 * Test to make sure the sigaction() works in case of invalid
219abb0f93cSkardel 	 * syscall codes.
220abb0f93cSkardel 	 */
221abb0f93cSkardel 	newsigsys.sa_handler = pll_trap;
222abb0f93cSkardel 	newsigsys.sa_flags = 0;
223abb0f93cSkardel 	if (sigaction(SIGSYS, &newsigsys, &sigsys)) {
224abb0f93cSkardel 		perror("sigaction() fails to save SIGSYS trap");
225abb0f93cSkardel 		exit(1);
226abb0f93cSkardel 	}
227abb0f93cSkardel #endif /* SIGSYS */
228abb0f93cSkardel 
229abb0f93cSkardel #ifdef	BADCALL
230abb0f93cSkardel 	/*
231abb0f93cSkardel 	 * Make sure the trapcatcher works.
232abb0f93cSkardel 	 */
233abb0f93cSkardel 	pll_control = 1;
234abb0f93cSkardel #ifdef SIGSYS
235*cdfa2a7eSchristos 	if (sigsetjmp(env, 1) == 0)
236abb0f93cSkardel #endif
237*cdfa2a7eSchristos 	{
238abb0f93cSkardel 		status = syscall(BADCALL, &ntv); /* dummy parameter */
239abb0f93cSkardel 		if ((status < 0) && (errno == ENOSYS))
240abb0f93cSkardel 			--pll_control;
241abb0f93cSkardel 	}
242abb0f93cSkardel 	if (pll_control)
243abb0f93cSkardel 	    printf("sigaction() failed to catch an invalid syscall\n");
244abb0f93cSkardel #endif /* BADCALL */
245abb0f93cSkardel 
246abb0f93cSkardel 	if (cost) {
247abb0f93cSkardel #ifdef SIGSYS
248*cdfa2a7eSchristos 		if (sigsetjmp(env, 1) == 0)
249abb0f93cSkardel #endif
250*cdfa2a7eSchristos 		{
2512950cc38Schristos 			for (c = 0; c < COUNTOF(times); c++) {
252abb0f93cSkardel 				status = ntp_gettime(&ntv);
253abb0f93cSkardel 				if ((status < 0) && (errno == ENOSYS))
254abb0f93cSkardel 					--pll_control;
255abb0f93cSkardel 				if (pll_control < 0)
256abb0f93cSkardel 					break;
257abb0f93cSkardel 				times[c] = ntv.time.tv_frac_sec;
258abb0f93cSkardel 			}
259abb0f93cSkardel 		}
260abb0f93cSkardel 		if (pll_control >= 0) {
261abb0f93cSkardel 			printf("[ us %06d:", times[0]);
2622950cc38Schristos 			for (c = 1; c < COUNTOF(times); c++)
263abb0f93cSkardel 			    printf(" %d", times[c] - times[c - 1]);
264abb0f93cSkardel 			printf(" ]\n");
265abb0f93cSkardel 		}
266abb0f93cSkardel 	}
267abb0f93cSkardel #ifdef SIGSYS
268*cdfa2a7eSchristos 	if (sigsetjmp(env, 1) == 0)
269abb0f93cSkardel #endif
270*cdfa2a7eSchristos 	{
271abb0f93cSkardel 		status = ntp_gettime(&ntv);
272abb0f93cSkardel 		if ((status < 0) && (errno == ENOSYS))
273abb0f93cSkardel 			--pll_control;
274abb0f93cSkardel 	}
275abb0f93cSkardel 	_ntx.modes = 0;				/* Ensure nothing is set */
276abb0f93cSkardel #ifdef SIGSYS
277*cdfa2a7eSchristos 	if (sigsetjmp(env, 1) == 0)
278abb0f93cSkardel #endif
279*cdfa2a7eSchristos 	{
280abb0f93cSkardel 		status = ntp_adjtime(&_ntx);
281abb0f93cSkardel 		if ((status < 0) && (errno == ENOSYS))
282abb0f93cSkardel 			--pll_control;
283abb0f93cSkardel 		flash = _ntx.status;
284abb0f93cSkardel 	}
285abb0f93cSkardel 	if (pll_control < 0) {
286abb0f93cSkardel 		printf("NTP user interface routines are not configured in this kernel.\n");
287abb0f93cSkardel 		goto lexit;
288abb0f93cSkardel 	}
289abb0f93cSkardel 
290abb0f93cSkardel 	/*
291abb0f93cSkardel 	 * Fetch timekeeping data and display.
292abb0f93cSkardel 	 */
293abb0f93cSkardel 	status = ntp_gettime(&ntv);
2942950cc38Schristos 	if (status < 0) {
295abb0f93cSkardel 		perror("ntp_gettime() call fails");
2962950cc38Schristos 	} else {
297abb0f93cSkardel 		printf("ntp_gettime() returns code %d (%s)\n",
298abb0f93cSkardel 		    status, timex_state(status));
299abb0f93cSkardel 		time_frac = ntv.time.tv_frac_sec;
300abb0f93cSkardel #ifdef STA_NANO
301abb0f93cSkardel 		if (flash & STA_NANO) {
302abb0f93cSkardel 			ntv.time.tv_frac_sec /= 1000;
303*cdfa2a7eSchristos 			ts_mask = TS_MASK_NS;
304*cdfa2a7eSchristos 			ts_roundbit = TS_ROUNDBIT_NS;
305*cdfa2a7eSchristos 			fdigits = TS_DIGITS_NS;
306abb0f93cSkardel 		}
307abb0f93cSkardel #endif
308abb0f93cSkardel 		tv.tv_sec = ntv.time.tv_sec;
309abb0f93cSkardel 		tv.tv_usec = ntv.time.tv_frac_sec;
310abb0f93cSkardel 		TVTOTS(&tv, &ts);
311abb0f93cSkardel 		ts.l_ui += JAN_1970;
312abb0f93cSkardel 		ts.l_uf += ts_roundbit;
313abb0f93cSkardel 		ts.l_uf &= ts_mask;
314abb0f93cSkardel 		printf("  time %s, (.%0*d),\n",
315abb0f93cSkardel 		       prettydate(&ts), fdigits, (int)time_frac);
316*cdfa2a7eSchristos 		printf("  maximum error %ld us, estimated error %ld us",
317*cdfa2a7eSchristos 		       ntv.maxerror, ntv.esterror);
318abb0f93cSkardel 		if (rawtime)
319abb0f93cSkardel 			printf("  ntptime=%x.%x unixtime=%x.%0*d %s",
3202950cc38Schristos 			       (u_int)ts.l_ui, (u_int)ts.l_uf,
3212950cc38Schristos 			       (int)ntv.time.tv_sec, fdigits,
3222950cc38Schristos 			       (int)time_frac,
3232950cc38Schristos 			       ctime((time_t *)&ntv.time.tv_sec));
324*cdfa2a7eSchristos #if defined(NTP_API) && NTP_API > 3
325abb0f93cSkardel 		printf(", TAI offset %ld\n", (long)ntv.tai);
326abb0f93cSkardel #else
327abb0f93cSkardel 		printf("\n");
328abb0f93cSkardel #endif /* NTP_API */
329abb0f93cSkardel 	}
330abb0f93cSkardel 	status = ntp_adjtime(&ntx);
3312950cc38Schristos 	if (status < 0) {
332abb0f93cSkardel 		perror((errno == EPERM) ?
333abb0f93cSkardel 		   "Must be root to set kernel values\nntp_adjtime() call fails" :
334abb0f93cSkardel 		   "ntp_adjtime() call fails");
3352950cc38Schristos 	} else {
336abb0f93cSkardel 		flash = ntx.status;
337abb0f93cSkardel 		printf("ntp_adjtime() returns code %d (%s)\n",
338abb0f93cSkardel 		     status, timex_state(status));
339abb0f93cSkardel 		printf("  modes %s,\n", sprintb(ntx.modes, TIMEX_MOD_BITS));
340abb0f93cSkardel #ifdef STA_NANO
341abb0f93cSkardel 		if (flash & STA_NANO)
342*cdfa2a7eSchristos 			nscale = 1e-3;
343abb0f93cSkardel #endif
344*cdfa2a7eSchristos 		ftemp = (double)ntx.offset * nscale;
345abb0f93cSkardel 		printf("  offset %.3f", ftemp);
346abb0f93cSkardel 		ftemp = (double)ntx.freq / SCALE_FREQ;
347abb0f93cSkardel 		printf(" us, frequency %.3f ppm, interval %d s,\n",
348abb0f93cSkardel 		       ftemp, 1 << ntx.shift);
349*cdfa2a7eSchristos 		printf("  maximum error %ld us, estimated error %ld us,\n",
350*cdfa2a7eSchristos 		     ntx.maxerror, ntx.esterror);
351abb0f93cSkardel 		printf("  status %s,\n", sprintb((u_int)ntx.status, TIMEX_STA_BITS));
352abb0f93cSkardel 		ftemp = (double)ntx.tolerance / SCALE_FREQ;
353*cdfa2a7eSchristos 		gtemp = (double)ntx.precision * nscale;
354abb0f93cSkardel 		printf(
355abb0f93cSkardel 			"  time constant %lu, precision %.3f us, tolerance %.0f ppm,\n",
356abb0f93cSkardel 			(u_long)ntx.constant, gtemp, ftemp);
357abb0f93cSkardel 		if (ntx.shift == 0)
358abb0f93cSkardel 			exit(0);
359abb0f93cSkardel 		ftemp = (double)ntx.ppsfreq / SCALE_FREQ;
360abb0f93cSkardel 		gtemp = (double)ntx.stabil / SCALE_FREQ;
361*cdfa2a7eSchristos 		htemp = (double)ntx.jitter * nscale;
362*cdfa2a7eSchristos 		printf("  pps frequency %.3f ppm, stability %.3f ppm, jitter %.3f us,\n",
363abb0f93cSkardel 		       ftemp, gtemp, htemp);
364abb0f93cSkardel 		printf("  intervals %lu, jitter exceeded %lu, stability exceeded %lu, errors %lu.\n",
365abb0f93cSkardel 		       (u_long)ntx.calcnt, (u_long)ntx.jitcnt,
366abb0f93cSkardel 		       (u_long)ntx.stbcnt, (u_long)ntx.errcnt);
3672950cc38Schristos 		return 0;
368abb0f93cSkardel 	}
369abb0f93cSkardel 
370abb0f93cSkardel 	/*
371abb0f93cSkardel 	 * Put things back together the way we found them.
372abb0f93cSkardel 	 */
373abb0f93cSkardel     lexit:
374abb0f93cSkardel #ifdef SIGSYS
375abb0f93cSkardel 	if (sigaction(SIGSYS, &sigsys, (struct sigaction *)NULL)) {
376abb0f93cSkardel 		perror("sigaction() fails to restore SIGSYS trap");
377abb0f93cSkardel 		exit(1);
378abb0f93cSkardel 	}
379abb0f93cSkardel #endif
380abb0f93cSkardel 	exit(0);
381abb0f93cSkardel }
382abb0f93cSkardel 
383abb0f93cSkardel #ifdef SIGSYS
384abb0f93cSkardel /*
385abb0f93cSkardel  * pll_trap - trap processor for undefined syscalls
386abb0f93cSkardel  */
387abb0f93cSkardel void
pll_trap(int arg)388abb0f93cSkardel pll_trap(
389abb0f93cSkardel 	int arg
390abb0f93cSkardel 	)
391abb0f93cSkardel {
392abb0f93cSkardel 	pll_control--;
393abb0f93cSkardel 	siglongjmp(env, 1);
394abb0f93cSkardel }
395abb0f93cSkardel #endif
396abb0f93cSkardel 
397abb0f93cSkardel /*
398abb0f93cSkardel  * Print a value a la the %b format of the kernel's printf
399abb0f93cSkardel  */
4002950cc38Schristos const char *
sprintb(u_int v,const char * bits)401abb0f93cSkardel sprintb(
4022950cc38Schristos 	u_int		v,
4032950cc38Schristos 	const char *	bits
404abb0f93cSkardel 	)
405abb0f93cSkardel {
4062950cc38Schristos 	char *cp;
4072950cc38Schristos 	char *cplim;
4082950cc38Schristos 	int i;
4092950cc38Schristos 	int any;
4102950cc38Schristos 	char c;
411abb0f93cSkardel 	static char buf[132];
412abb0f93cSkardel 
4132950cc38Schristos 	if (bits != NULL && *bits == 8)
4142950cc38Schristos 		snprintf(buf, sizeof(buf), "0%o", v);
415abb0f93cSkardel 	else
4162950cc38Schristos 		snprintf(buf, sizeof(buf), "0x%x", v);
417abb0f93cSkardel 	cp = buf + strlen(buf);
4182950cc38Schristos 	cplim = buf + sizeof(buf);
4192950cc38Schristos 	if (bits != NULL) {
420abb0f93cSkardel 		bits++;
421abb0f93cSkardel 		*cp++ = ' ';
422abb0f93cSkardel 		*cp++ = '(';
4232950cc38Schristos 		any = FALSE;
424abb0f93cSkardel 		while ((i = *bits++) != 0) {
425abb0f93cSkardel 			if (v & (1 << (i - 1))) {
4262950cc38Schristos 				if (any) {
427abb0f93cSkardel 					*cp++ = ',';
4282950cc38Schristos 					if (cp >= cplim)
4292950cc38Schristos 						goto overrun;
4302950cc38Schristos 				}
4312950cc38Schristos 				any = TRUE;
4322950cc38Schristos 				for (; (c = *bits) > 32; bits++) {
433abb0f93cSkardel 					*cp++ = c;
4342950cc38Schristos 					if (cp >= cplim)
4352950cc38Schristos 						goto overrun;
4362950cc38Schristos 				}
4372950cc38Schristos 			} else {
438abb0f93cSkardel 				for (; *bits > 32; bits++)
439abb0f93cSkardel 					continue;
440abb0f93cSkardel 			}
4412950cc38Schristos 		}
442abb0f93cSkardel 		*cp++ = ')';
4432950cc38Schristos 		if (cp >= cplim)
4442950cc38Schristos 			goto overrun;
445abb0f93cSkardel 	}
446abb0f93cSkardel 	*cp = '\0';
4472950cc38Schristos 	return buf;
4482950cc38Schristos 
4492950cc38Schristos     overrun:
4502950cc38Schristos 	return "sprintb buffer too small";
451abb0f93cSkardel }
452abb0f93cSkardel 
4532950cc38Schristos const char * const timex_states[] = {
454abb0f93cSkardel 	"OK", "INS", "DEL", "OOP", "WAIT", "ERROR"
455abb0f93cSkardel };
456abb0f93cSkardel 
457abb0f93cSkardel const char *
timex_state(int s)458abb0f93cSkardel timex_state(
4592950cc38Schristos 	int s
460abb0f93cSkardel 	)
461abb0f93cSkardel {
462abb0f93cSkardel 	static char buf[32];
463abb0f93cSkardel 
4642950cc38Schristos 	if ((size_t)s < COUNTOF(timex_states))
4652950cc38Schristos 		return timex_states[s];
4662950cc38Schristos 	snprintf(buf, sizeof(buf), "TIME-#%d", s);
4672950cc38Schristos 	return buf;
468abb0f93cSkardel }
469