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