xref: /minix/external/bsd/dhcp/dist/common/print.c (revision fb9c64b2)
1 /*	$NetBSD: print.c,v 1.1.1.3 2014/07/12 11:57:46 spz Exp $	*/
2 /* print.c
3 
4    Turn data structures into printable text. */
5 
6 /*
7  * Copyright (c) 2009-2014 by Internet Systems Consortium, Inc. ("ISC")
8  * Copyright (c) 2004-2007 by Internet Systems Consortium, Inc. ("ISC")
9  * Copyright (c) 1995-2003 by Internet Software Consortium
10  *
11  * Permission to use, copy, modify, and distribute this software for any
12  * purpose with or without fee is hereby granted, provided that the above
13  * copyright notice and this permission notice appear in all copies.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
16  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
17  * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
18  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
20  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
21  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22  *
23  *   Internet Systems Consortium, Inc.
24  *   950 Charter Street
25  *   Redwood City, CA 94063
26  *   <info@isc.org>
27  *   https://www.isc.org/
28  *
29  */
30 
31 #include <sys/cdefs.h>
32 __RCSID("$NetBSD: print.c,v 1.1.1.3 2014/07/12 11:57:46 spz Exp $");
33 
34 #include "dhcpd.h"
35 
36 int db_time_format = DEFAULT_TIME_FORMAT;
37 
38 char *quotify_string (const char *s, const char *file, int line)
39 {
40 	unsigned len = 0;
41 	const char *sp;
42 	char *buf, *nsp;
43 
44 	for (sp = s; sp && *sp; sp++) {
45 		if (*sp == ' ')
46 			len++;
47 		else if (!isascii ((int)*sp) || !isprint ((int)*sp))
48 			len += 4;
49 		else if (*sp == '"' || *sp == '\\')
50 			len += 2;
51 		else
52 			len++;
53 	}
54 
55 	buf = dmalloc (len + 1, file, line);
56 	if (buf) {
57 		nsp = buf;
58 		for (sp = s; sp && *sp; sp++) {
59 			if (*sp == ' ')
60 				*nsp++ = ' ';
61 			else if (!isascii ((int)*sp) || !isprint ((int)*sp)) {
62 				sprintf (nsp, "\\%03o",
63 					 *(const unsigned char *)sp);
64 				nsp += 4;
65 			} else if (*sp == '"' || *sp == '\\') {
66 				*nsp++ = '\\';
67 				*nsp++ = *sp;
68 			} else
69 				*nsp++ = *sp;
70 		}
71 		*nsp++ = 0;
72 	}
73 	return buf;
74 }
75 
76 char *quotify_buf (const unsigned char *s, unsigned len,
77 		   const char *file, int line)
78 {
79 	unsigned nulen = 0;
80 	char *buf, *nsp;
81 	int i;
82 
83 	for (i = 0; i < len; i++) {
84 		if (s [i] == ' ')
85 			nulen++;
86 		else if (!isascii (s [i]) || !isprint (s [i]))
87 			nulen += 4;
88 		else if (s [i] == '"' || s [i] == '\\')
89 			nulen += 2;
90 		else
91 			nulen++;
92 	}
93 
94 	buf = dmalloc (nulen + 1, MDL);
95 	if (buf) {
96 		nsp = buf;
97 		for (i = 0; i < len; i++) {
98 			if (s [i] == ' ')
99 				*nsp++ = ' ';
100 			else if (!isascii (s [i]) || !isprint (s [i])) {
101 				sprintf (nsp, "\\%03o", s [i]);
102 				nsp += 4;
103 			} else if (s [i] == '"' || s [i] == '\\') {
104 				*nsp++ = '\\';
105 				*nsp++ = s [i];
106 			} else
107 				*nsp++ = s [i];
108 		}
109 		*nsp++ = 0;
110 	}
111 	return buf;
112 }
113 
114 char *print_base64 (const unsigned char *buf, unsigned len,
115 		    const char *file, int line)
116 {
117 	char *s, *b;
118 	unsigned bl;
119 	int i;
120 	unsigned val, extra;
121 	static char to64 [] =
122 	   "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
123 
124 	bl = ((len * 4 + 2) / 3) + 1;
125 	b = dmalloc (bl + 1, file, line);
126 	if (!b)
127 		return (char *)0;
128 
129 	i = 0;
130 	s = b;
131 	while (i != len) {
132 		val = buf [i++];
133 		extra = val & 3;
134 		val = val >> 2;
135 		*s++ = to64 [val];
136 		if (i == len) {
137 			*s++ = to64 [extra << 4];
138 			*s++ = '=';
139 			break;
140 		}
141 		val = (extra << 8) + buf [i++];
142 		extra = val & 15;
143 		val = val >> 4;
144 		*s++ = to64 [val];
145 		if (i == len) {
146 			*s++ = to64 [extra << 2];
147 			*s++ = '=';
148 			break;
149 		}
150 		val = (extra << 8) + buf [i++];
151 		extra = val & 0x3f;
152 		val = val >> 6;
153 		*s++ = to64 [val];
154 		*s++ = to64 [extra];
155 	}
156 	if (!len)
157 		*s++ = '=';
158 	*s++ = 0;
159 	if (s > b + bl + 1)
160 		abort ();
161 	return b;
162 }
163 
164 char *print_hw_addr (htype, hlen, data)
165 	const int htype;
166 	const int hlen;
167 	const unsigned char *data;
168 {
169 	static char habuf [49];
170 	char *s;
171 	int i;
172 
173 	if (hlen <= 0)
174 		habuf [0] = 0;
175 	else {
176 		s = habuf;
177 		for (i = 0; i < hlen; i++) {
178 			sprintf (s, "%02x", data [i]);
179 			s += strlen (s);
180 			*s++ = ':';
181 		}
182 		*--s = 0;
183 	}
184 	return habuf;
185 }
186 
187 void print_lease (lease)
188 	struct lease *lease;
189 {
190 	struct tm *t;
191 	char tbuf [32];
192 
193 	log_debug ("  Lease %s",
194 	       piaddr (lease -> ip_addr));
195 
196 	t = gmtime (&lease -> starts);
197 	strftime (tbuf, sizeof tbuf, "%Y/%m/%d %H:%M:%S", t);
198 	log_debug ("  start %s", tbuf);
199 
200 	t = gmtime (&lease -> ends);
201 	strftime (tbuf, sizeof tbuf, "%Y/%m/%d %H:%M:%S", t);
202 	log_debug ("  end %s", tbuf);
203 
204 	if (lease -> hardware_addr.hlen)
205 		log_debug ("    hardware addr = %s",
206 			   print_hw_addr (lease -> hardware_addr.hbuf [0],
207 					  lease -> hardware_addr.hlen - 1,
208 					  &lease -> hardware_addr.hbuf [1]));
209 	log_debug ("  host %s  ",
210 	       lease -> host ? lease -> host -> name : "<none>");
211 }
212 
213 #if defined (DEBUG_PACKET)
214 void dump_packet_option (struct option_cache *oc,
215 			 struct packet *packet,
216 			 struct lease *lease,
217 			 struct client_state *client,
218 			 struct option_state *in_options,
219 			 struct option_state *cfg_options,
220 			 struct binding_scope **scope,
221 			 struct universe *u, void *foo)
222 {
223 	const char *name, *dot;
224 	struct data_string ds;
225 	memset (&ds, 0, sizeof ds);
226 
227 	if (u != &dhcp_universe) {
228 		name = u -> name;
229 		dot = ".";
230 	} else {
231 		name = "";
232 		dot = "";
233 	}
234 	if (evaluate_option_cache (&ds, packet, lease, client,
235 				   in_options, cfg_options, scope, oc, MDL)) {
236 		log_debug ("  option %s%s%s %s;\n",
237 			   name, dot, oc -> option -> name,
238 			   pretty_print_option (oc -> option,
239 						ds.data, ds.len, 1, 1));
240 		data_string_forget (&ds, MDL);
241 	}
242 }
243 
244 void dump_packet (tp)
245 	struct packet *tp;
246 {
247 	struct dhcp_packet *tdp = tp -> raw;
248 
249 	log_debug ("packet length %d", tp -> packet_length);
250 	log_debug ("op = %d  htype = %d  hlen = %d  hops = %d",
251 	       tdp -> op, tdp -> htype, tdp -> hlen, tdp -> hops);
252 	log_debug ("xid = %x  secs = %ld  flags = %x",
253 	       tdp -> xid, (unsigned long)tdp -> secs, tdp -> flags);
254 	log_debug ("ciaddr = %s", inet_ntoa (tdp -> ciaddr));
255 	log_debug ("yiaddr = %s", inet_ntoa (tdp -> yiaddr));
256 	log_debug ("siaddr = %s", inet_ntoa (tdp -> siaddr));
257 	log_debug ("giaddr = %s", inet_ntoa (tdp -> giaddr));
258 	log_debug ("chaddr = %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x",
259 	       ((unsigned char *)(tdp -> chaddr)) [0],
260 	       ((unsigned char *)(tdp -> chaddr)) [1],
261 	       ((unsigned char *)(tdp -> chaddr)) [2],
262 	       ((unsigned char *)(tdp -> chaddr)) [3],
263 	       ((unsigned char *)(tdp -> chaddr)) [4],
264 	       ((unsigned char *)(tdp -> chaddr)) [5]);
265 	log_debug ("filename = %s", tdp -> file);
266 	log_debug ("server_name = %s", tdp -> sname);
267 	if (tp -> options_valid) {
268 		int i;
269 
270 		for (i = 0; i < tp -> options -> universe_count; i++) {
271 			if (tp -> options -> universes [i]) {
272 				option_space_foreach (tp, (struct lease *)0,
273 						      (struct client_state *)0,
274 						      (struct option_state *)0,
275 						      tp -> options,
276 						      &global_scope,
277 						      universes [i], 0,
278 						      dump_packet_option);
279 			}
280 		}
281 	}
282 	log_debug ("%s", "");
283 }
284 #endif
285 
286 void dump_raw (buf, len)
287 	const unsigned char *buf;
288 	unsigned len;
289 {
290 	int i;
291 	char lbuf [80];
292 	int lbix = 0;
293 
294 /*
295           1         2         3         4         5         6         7
296 01234567890123456789012345678901234567890123456789012345678901234567890123
297 280: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   .................
298 */
299 
300 	memset(lbuf, ' ', 79);
301 	lbuf [79] = 0;
302 
303 	for (i = 0; i < len; i++) {
304 		if ((i & 15) == 0) {
305 		  if (lbix) {
306 		    	lbuf[53]=' ';
307 			lbuf[54]=' ';
308 			lbuf[55]=' ';
309 			lbuf[73]='\0';
310 			log_info ("%s", lbuf);
311 		  }
312 		  memset(lbuf, ' ', 79);
313 		  lbuf [79] = 0;
314 		  sprintf (lbuf, "%03x:", i);
315 		  lbix = 4;
316 		} else if ((i & 7) == 0)
317 			lbuf [lbix++] = ' ';
318 
319 		if(isprint(buf[i])) {
320 		  lbuf[56+(i%16)]=buf[i];
321 		} else {
322 		  lbuf[56+(i%16)]='.';
323 		}
324 
325 		sprintf (&lbuf [lbix], " %02x", buf [i]);
326 		lbix += 3;
327 		lbuf[lbix]=' ';
328 
329 	}
330 	lbuf[53]=' ';
331 	lbuf[54]=' ';
332 	lbuf[55]=' ';
333 	lbuf[73]='\0';
334 	log_info ("%s", lbuf);
335 }
336 
337 void hash_dump (table)
338 	struct hash_table *table;
339 {
340 	int i;
341 	struct hash_bucket *bp;
342 
343 	if (!table)
344 		return;
345 
346 	for (i = 0; i < table -> hash_count; i++) {
347 		if (!table -> buckets [i])
348 			continue;
349 		log_info ("hash bucket %d:", i);
350 		for (bp = table -> buckets [i]; bp; bp = bp -> next) {
351 			if (bp -> len)
352 				dump_raw (bp -> name, bp -> len);
353 			else
354 				log_info ("%s", (const char *)bp -> name);
355 		}
356 	}
357 }
358 
359 /*
360  * print a string as hex.  This only outputs
361  * colon separated hex list no matter what
362  * the input looks like.  See print_hex
363  * for a function that prints either cshl
364  * or a string if all bytes are printible
365  * It only uses limit characters from buf
366  * and doesn't do anything if buf == NULL
367  *
368  * len - length of data
369  * data - input data
370  * limit - length of buf to use
371  * buf - output buffer
372  */
373 void print_hex_only (len, data, limit, buf)
374 	unsigned len;
375 	const u_int8_t *data;
376 	unsigned limit;
377 	char *buf;
378 {
379 	unsigned i;
380 
381 	if ((buf == NULL) || (limit < 3))
382 		return;
383 
384 	for (i = 0; (i < limit / 3) && (i < len); i++) {
385 		sprintf(&buf[i*3], "%02x:", data[i]);
386 	}
387 	buf[(i * 3) - 1] = 0;
388 	return;
389 }
390 
391 /*
392  * print a string as either text if all the characters
393  * are printable or colon separated hex if they aren't
394  *
395  * len - length of data
396  * data - input data
397  * limit - length of buf to use
398  * buf - output buffer
399  */
400 void print_hex_or_string (len, data, limit, buf)
401 	unsigned len;
402 	const u_int8_t *data;
403 	unsigned limit;
404 	char *buf;
405 {
406 	unsigned i;
407 	if ((buf == NULL) || (limit < 3))
408 		return;
409 
410 	for (i = 0; (i < (limit - 3)) && (i < len); i++) {
411 		if (!isascii(data[i]) || !isprint(data[i])) {
412 			print_hex_only(len, data, limit, buf);
413 			return;
414 		}
415 	}
416 
417 	buf[0] = '"';
418 	i = len;
419 	if (i > (limit - 3))
420 		i = limit - 3;
421 	memcpy(&buf[1], data, i);
422 	buf[i + 1] = '"';
423 	buf[i + 2] = 0;
424 	return;
425 }
426 
427 /*
428  * print a string as either hex or text
429  * using static buffers to hold the output
430  *
431  * len - length of data
432  * data - input data
433  * limit - length of buf
434  * buf_num - the output buffer to use
435  */
436 #define HBLEN 1024
437 char *print_hex(len, data, limit, buf_num)
438 	unsigned len;
439 	const u_int8_t *data;
440 	unsigned limit;
441 	unsigned buf_num;
442 {
443 	static char hex_buf_1[HBLEN + 1];
444 	static char hex_buf_2[HBLEN + 1];
445 	static char hex_buf_3[HBLEN + 1];
446 	char *hex_buf;
447 
448 	switch(buf_num) {
449 	  case 0:
450 		hex_buf = hex_buf_1;
451 		if (limit >= sizeof(hex_buf_1))
452 			limit = sizeof(hex_buf_1);
453 		break;
454 	  case 1:
455 		hex_buf = hex_buf_2;
456 		if (limit >= sizeof(hex_buf_2))
457 			limit = sizeof(hex_buf_2);
458 		break;
459 	  case 2:
460 		hex_buf = hex_buf_3;
461 		if (limit >= sizeof(hex_buf_3))
462 			limit = sizeof(hex_buf_3);
463 		break;
464 	  default:
465 		return(NULL);
466 	}
467 
468 	print_hex_or_string(len, data, limit, hex_buf);
469 	return(hex_buf);
470 }
471 
472 #define DQLEN	80
473 
474 char *print_dotted_quads (len, data)
475 	unsigned len;
476 	const u_int8_t *data;
477 {
478 	static char dq_buf [DQLEN + 1];
479 	int i;
480 	char *s;
481 
482 	s = &dq_buf [0];
483 
484 	i = 0;
485 
486 	/* %Audit% Loop bounds checks to 21 bytes. %2004.06.17,Safe%
487 	 * The sprintf can't exceed 18 bytes, and since the loop enforces
488 	 * 21 bytes of space per iteration at no time can we exit the
489 	 * loop without at least 3 bytes spare.
490 	 */
491 	do {
492 		sprintf (s, "%u.%u.%u.%u, ",
493 			 data [i], data [i + 1], data [i + 2], data [i + 3]);
494 		s += strlen (s);
495 		i += 4;
496 	} while ((s - &dq_buf [0] > DQLEN - 21) &&
497 		 i + 3 < len);
498 	if (i == len)
499 		s [-2] = 0;
500 	else
501 		strcpy (s, "...");
502 	return dq_buf;
503 }
504 
505 char *print_dec_1 (val)
506 	unsigned long val;
507 {
508 	static char vbuf [32];
509 	sprintf (vbuf, "%lu", val);
510 	return vbuf;
511 }
512 
513 char *print_dec_2 (val)
514 	unsigned long val;
515 {
516 	static char vbuf [32];
517 	sprintf (vbuf, "%lu", val);
518 	return vbuf;
519 }
520 
521 static unsigned print_subexpression (struct expression *, char *, unsigned);
522 
523 static unsigned print_subexpression (expr, buf, len)
524 	struct expression *expr;
525 	char *buf;
526 	unsigned len;
527 {
528 	unsigned rv, left;
529 	const char *s;
530 
531 	switch (expr -> op) {
532 	      case expr_none:
533 		if (len > 3) {
534 			strcpy (buf, "nil");
535 			return 3;
536 		}
537 		break;
538 
539 	      case expr_match:
540 		if (len > 7) {
541 			strcpy (buf, "(match)");
542 			return 7;
543 		}
544 		break;
545 
546 	      case expr_check:
547 		rv = 10 + strlen (expr -> data.check -> name);
548 		if (len > rv) {
549 			sprintf (buf, "(check %s)",
550 				 expr -> data.check -> name);
551 			return rv;
552 		}
553 		break;
554 
555 	      case expr_equal:
556 		if (len > 6) {
557 			rv = 4;
558 			strcpy (buf, "(eq ");
559 			rv += print_subexpression (expr -> data.equal [0],
560 						   buf + rv, len - rv - 2);
561 			buf [rv++] = ' ';
562 			rv += print_subexpression (expr -> data.equal [1],
563 						   buf + rv, len - rv - 1);
564 			buf [rv++] = ')';
565 			buf [rv] = 0;
566 			return rv;
567 		}
568 		break;
569 
570 	      case expr_not_equal:
571 		if (len > 7) {
572 			rv = 5;
573 			strcpy (buf, "(neq ");
574 			rv += print_subexpression (expr -> data.equal [0],
575 						   buf + rv, len - rv - 2);
576 			buf [rv++] = ' ';
577 			rv += print_subexpression (expr -> data.equal [1],
578 						   buf + rv, len - rv - 1);
579 			buf [rv++] = ')';
580 			buf [rv] = 0;
581 			return rv;
582 		}
583 		break;
584 
585 	      case expr_regex_match:
586 		if (len > 10) {
587 			rv = 4;
588 			strcpy(buf, "(regex ");
589 			rv += print_subexpression(expr->data.equal[0],
590 						  buf + rv, len - rv - 2);
591 			buf[rv++] = ' ';
592 			rv += print_subexpression(expr->data.equal[1],
593 						  buf + rv, len - rv - 1);
594 			buf[rv++] = ')';
595 			buf[rv] = 0;
596 			return rv;
597 		}
598 		break;
599 
600 	      case expr_substring:
601 		if (len > 11) {
602 			rv = 8;
603 			strcpy (buf, "(substr ");
604 			rv += print_subexpression (expr -> data.substring.expr,
605 						   buf + rv, len - rv - 3);
606 			buf [rv++] = ' ';
607 			rv += print_subexpression
608 				(expr -> data.substring.offset,
609 				 buf + rv, len - rv - 2);
610 			buf [rv++] = ' ';
611 			rv += print_subexpression (expr -> data.substring.len,
612 						   buf + rv, len - rv - 1);
613 			buf [rv++] = ')';
614 			buf [rv] = 0;
615 			return rv;
616 		}
617 		break;
618 
619 	      case expr_suffix:
620 		if (len > 10) {
621 			rv = 8;
622 			strcpy (buf, "(suffix ");
623 			rv += print_subexpression (expr -> data.suffix.expr,
624 						   buf + rv, len - rv - 2);
625 			if (len > rv)
626 				buf [rv++] = ' ';
627 			rv += print_subexpression (expr -> data.suffix.len,
628 						   buf + rv, len - rv - 1);
629 			if (len > rv)
630 				buf [rv++] = ')';
631 			buf [rv] = 0;
632 			return rv;
633 		}
634 		break;
635 
636 	      case expr_lcase:
637 		if (len > 9) {
638 			rv = 7;
639 			strcpy(buf, "(lcase ");
640 			rv += print_subexpression(expr->data.lcase,
641 						  buf + rv, len - rv - 1);
642 			buf[rv++] = ')';
643 			buf[rv] = 0;
644 			return rv;
645 		}
646 		break;
647 
648 	      case expr_ucase:
649 		if (len > 9) {
650 			rv = 7;
651 			strcpy(buf, "(ucase ");
652 			rv += print_subexpression(expr->data.ucase,
653 						  buf + rv, len - rv - 1);
654 			buf[rv++] = ')';
655 			buf[rv] = 0;
656 			return rv;
657 		}
658 		break;
659 
660 	      case expr_concat:
661 		if (len > 10) {
662 			rv = 8;
663 			strcpy (buf, "(concat ");
664 			rv += print_subexpression (expr -> data.concat [0],
665 						   buf + rv, len - rv - 2);
666 			buf [rv++] = ' ';
667 			rv += print_subexpression (expr -> data.concat [1],
668 						   buf + rv, len - rv - 1);
669 			buf [rv++] = ')';
670 			buf [rv] = 0;
671 			return rv;
672 		}
673 		break;
674 
675 	      case expr_pick_first_value:
676 		if (len > 8) {
677 			rv = 6;
678 			strcpy (buf, "(pick1st ");
679 			rv += print_subexpression
680 				(expr -> data.pick_first_value.car,
681 				 buf + rv, len - rv - 2);
682 			buf [rv++] = ' ';
683 			rv += print_subexpression
684 				(expr -> data.pick_first_value.cdr,
685 				 buf + rv, len - rv - 1);
686 			buf [rv++] = ')';
687 			buf [rv] = 0;
688 			return rv;
689 		}
690 		break;
691 
692 	      case expr_host_lookup:
693 		rv = 15 + strlen (expr -> data.host_lookup -> hostname);
694 		if (len > rv) {
695 			sprintf (buf, "(dns-lookup %s)",
696 				 expr -> data.host_lookup -> hostname);
697 			return rv;
698 		}
699 		break;
700 
701 	      case expr_and:
702 		s = "and";
703 	      binop:
704 		rv = strlen (s);
705 		if (len > rv + 4) {
706 			buf [0] = '(';
707 			strcpy (&buf [1], s);
708 			rv += 1;
709 			buf [rv++] = ' ';
710 			rv += print_subexpression (expr -> data.and [0],
711 						buf + rv, len - rv - 2);
712 			buf [rv++] = ' ';
713 			rv += print_subexpression (expr -> data.and [1],
714 						   buf + rv, len - rv - 1);
715 			buf [rv++] = ')';
716 			buf [rv] = 0;
717 			return rv;
718 		}
719 		break;
720 
721 	      case expr_or:
722 		s = "or";
723 		goto binop;
724 
725 	      case expr_add:
726 		s = "+";
727 		goto binop;
728 
729 	      case expr_subtract:
730 		s = "-";
731 		goto binop;
732 
733 	      case expr_multiply:
734 		s = "*";
735 		goto binop;
736 
737 	      case expr_divide:
738 		s = "/";
739 		goto binop;
740 
741 	      case expr_remainder:
742 		s = "%";
743 		goto binop;
744 
745 	      case expr_binary_and:
746 		s = "&";
747 		goto binop;
748 
749 	      case expr_binary_or:
750 		s = "|";
751 		goto binop;
752 
753 	      case expr_binary_xor:
754 		s = "^";
755 		goto binop;
756 
757 	      case expr_not:
758 		if (len > 6) {
759 			rv = 5;
760 			strcpy (buf, "(not ");
761 			rv += print_subexpression (expr -> data.not,
762 						   buf + rv, len - rv - 1);
763 			buf [rv++] = ')';
764 			buf [rv] = 0;
765 			return rv;
766 		}
767 		break;
768 
769 	      case expr_config_option:
770 		s = "cfg-option";
771 		goto dooption;
772 
773 	      case expr_option:
774 		s = "option";
775 	      dooption:
776 		rv = strlen (s) + 2 + (strlen (expr -> data.option -> name) +
777 			   strlen (expr -> data.option -> universe -> name));
778 		if (len > rv) {
779 			sprintf (buf, "(option %s.%s)",
780 				 expr -> data.option -> universe -> name,
781 				 expr -> data.option -> name);
782 			return rv;
783 		}
784 		break;
785 
786 	      case expr_hardware:
787 		if (len > 10) {
788 			strcpy (buf, "(hardware)");
789 			return 10;
790 		}
791 		break;
792 
793 	      case expr_packet:
794 		if (len > 10) {
795 			rv = 8;
796 			strcpy (buf, "(substr ");
797 			rv += print_subexpression (expr -> data.packet.offset,
798 						   buf + rv, len - rv - 2);
799 			buf [rv++] = ' ';
800 			rv += print_subexpression (expr -> data.packet.len,
801 						   buf + rv, len - rv - 1);
802 			buf [rv++] = ')';
803 			buf [rv] = 0;
804 			return rv;
805 		}
806 		break;
807 
808 	      case expr_const_data:
809 		s = print_hex_1 (expr -> data.const_data.len,
810 				 expr -> data.const_data.data, len);
811 		rv = strlen (s);
812 		if (rv >= len)
813 			rv = len - 1;
814 		strncpy (buf, s, rv);
815 		buf [rv] = 0;
816 		return rv;
817 
818 	      case expr_encapsulate:
819 		rv = 13;
820 		strcpy (buf, "(encapsulate ");
821 		rv += expr -> data.encapsulate.len;
822 		if (rv + 2 > len)
823 			rv = len - 2;
824 		strncpy (buf,
825 			 (const char *)expr -> data.encapsulate.data, rv - 13);
826 		buf [rv++] = ')';
827 		buf [rv++] = 0;
828 		break;
829 
830 	      case expr_extract_int8:
831 		if (len > 7) {
832 			rv = 6;
833 			strcpy (buf, "(int8 ");
834 			rv += print_subexpression (expr -> data.extract_int,
835 						   buf + rv, len - rv - 1);
836 			buf [rv++] = ')';
837 			buf [rv] = 0;
838 			return rv;
839 		}
840 		break;
841 
842 	      case expr_extract_int16:
843 		if (len > 8) {
844 			rv = 7;
845 			strcpy (buf, "(int16 ");
846 			rv += print_subexpression (expr -> data.extract_int,
847 						   buf + rv, len - rv - 1);
848 			buf [rv++] = ')';
849 			buf [rv] = 0;
850 			return rv;
851 		}
852 		break;
853 
854 	      case expr_extract_int32:
855 		if (len > 8) {
856 			rv = 7;
857 			strcpy (buf, "(int32 ");
858 			rv += print_subexpression (expr -> data.extract_int,
859 						   buf + rv, len - rv - 1);
860 			buf [rv++] = ')';
861 			buf [rv] = 0;
862 			return rv;
863 		}
864 		break;
865 
866 	      case expr_encode_int8:
867 		if (len > 7) {
868 			rv = 6;
869 			strcpy (buf, "(to-int8 ");
870 			rv += print_subexpression (expr -> data.encode_int,
871 						   buf + rv, len - rv - 1);
872 			buf [rv++] = ')';
873 			buf [rv] = 0;
874 			return rv;
875 		}
876 		break;
877 
878 	      case expr_encode_int16:
879 		if (len > 8) {
880 			rv = 7;
881 			strcpy (buf, "(to-int16 ");
882 			rv += print_subexpression (expr -> data.encode_int,
883 						   buf + rv, len - rv - 1);
884 			buf [rv++] = ')';
885 			buf [rv] = 0;
886 			return rv;
887 		}
888 		break;
889 
890 	      case expr_encode_int32:
891 		if (len > 8) {
892 			rv = 7;
893 			strcpy (buf, "(to-int32 ");
894 			rv += print_subexpression (expr -> data.encode_int,
895 						   buf + rv, len - rv - 1);
896 			buf [rv++] = ')';
897 			buf [rv] = 0;
898 			return rv;
899 		}
900 		break;
901 
902 	      case expr_const_int:
903 		s = print_dec_1 (expr -> data.const_int);
904 		rv = strlen (s);
905 		if (len > rv) {
906 			strcpy (buf, s);
907 			return rv;
908 		}
909 		break;
910 
911 	      case expr_exists:
912 		rv = 10 + (strlen (expr -> data.option -> name) +
913 			   strlen (expr -> data.option -> universe -> name));
914 		if (len > rv) {
915 			sprintf (buf, "(exists %s.%s)",
916 				 expr -> data.option -> universe -> name,
917 				 expr -> data.option -> name);
918 			return rv;
919 		}
920 		break;
921 
922 	      case expr_variable_exists:
923 		rv = 10 + strlen (expr -> data.variable);
924 		if (len > rv) {
925 			sprintf (buf, "(defined %s)", expr -> data.variable);
926 			return rv;
927 		}
928 		break;
929 
930 	      case expr_variable_reference:
931 		rv = strlen (expr -> data.variable);
932 		if (len > rv) {
933 			sprintf (buf, "%s", expr -> data.variable);
934 			return rv;
935 		}
936 		break;
937 
938 	      case expr_known:
939 		s = "known";
940 	      astring:
941 		rv = strlen (s);
942 		if (len > rv) {
943 			strcpy (buf, s);
944 			return rv;
945 		}
946 		break;
947 
948 	      case expr_leased_address:
949 		s = "leased-address";
950 		goto astring;
951 
952 	      case expr_client_state:
953 		s = "client-state";
954 		goto astring;
955 
956 	      case expr_host_decl_name:
957 		s = "host-decl-name";
958 		goto astring;
959 
960 	      case expr_lease_time:
961 		s = "lease-time";
962 		goto astring;
963 
964 	      case expr_static:
965 		s = "static";
966 		goto astring;
967 
968 	      case expr_filename:
969 		s = "filename";
970 		goto astring;
971 
972 	      case expr_sname:
973 		s = "server-name";
974 		goto astring;
975 
976 	      case expr_reverse:
977 		if (len > 11) {
978 			rv = 13;
979 			strcpy (buf, "(reverse ");
980 			rv += print_subexpression (expr -> data.reverse.width,
981 						   buf + rv, len - rv - 2);
982 			buf [rv++] = ' ';
983 			rv += print_subexpression (expr -> data.reverse.buffer,
984 						   buf + rv, len - rv - 1);
985 			buf [rv++] = ')';
986 			buf [rv] = 0;
987 			return rv;
988 		}
989 		break;
990 
991 	      case expr_binary_to_ascii:
992 		if (len > 5) {
993 			rv = 9;
994 			strcpy (buf, "(b2a ");
995 			rv += print_subexpression (expr -> data.b2a.base,
996 						   buf + rv, len - rv - 4);
997 			buf [rv++] = ' ';
998 			rv += print_subexpression (expr -> data.b2a.width,
999 						   buf + rv, len - rv - 3);
1000 			buf [rv++] = ' ';
1001 			rv += print_subexpression (expr -> data.b2a.separator,
1002 						   buf + rv, len - rv - 2);
1003 			buf [rv++] = ' ';
1004 			rv += print_subexpression (expr -> data.b2a.buffer,
1005 						   buf + rv, len - rv - 1);
1006 			buf [rv++] = ')';
1007 			buf [rv] = 0;
1008 			return rv;
1009 		}
1010 		break;
1011 
1012 	      case expr_dns_transaction:
1013 		rv = 10;
1014 		if (len < rv + 2) {
1015 			buf [0] = '(';
1016 			strcpy (&buf [1], "ns-update ");
1017 			while (len < rv + 2) {
1018 				rv += print_subexpression
1019 					(expr -> data.dns_transaction.car,
1020 					 buf + rv, len - rv - 2);
1021 				buf [rv++] = ' ';
1022 				expr = expr -> data.dns_transaction.cdr;
1023 			}
1024 			buf [rv - 1] = ')';
1025 			buf [rv] = 0;
1026 			return rv;
1027 		}
1028 		return 0;
1029 
1030 	      case expr_ns_delete:
1031 		s = "delete";
1032 		left = 4;
1033 		goto dodnsupd;
1034 	      case expr_ns_exists:
1035 		s = "exists";
1036 		left = 4;
1037 		goto dodnsupd;
1038 	      case expr_ns_not_exists:
1039 		s = "not_exists";
1040 		left = 4;
1041 		goto dodnsupd;
1042 	      case expr_ns_add:
1043 		s = "update";
1044 		left = 5;
1045 	      dodnsupd:
1046 		rv = strlen (s);
1047 		if (len > strlen (s) + 1) {
1048 			buf [0] = '(';
1049 			strcpy (buf + 1, s);
1050 			rv++;
1051 			buf [rv++] = ' ';
1052 			s = print_dec_1 (expr -> data.ns_add.rrclass);
1053 			if (len > rv + strlen (s) + left) {
1054 				strcpy (&buf [rv], s);
1055 				rv += strlen (&buf [rv]);
1056 			}
1057 			buf [rv++] = ' ';
1058 			left--;
1059 			s = print_dec_1 (expr -> data.ns_add.rrtype);
1060 			if (len > rv + strlen (s) + left) {
1061 				strcpy (&buf [rv], s);
1062 				rv += strlen (&buf [rv]);
1063 			}
1064 			buf [rv++] = ' ';
1065 			left--;
1066 			rv += print_subexpression
1067 				(expr -> data.ns_add.rrname,
1068 				 buf + rv, len - rv - left);
1069 			buf [rv++] = ' ';
1070 			left--;
1071 			rv += print_subexpression
1072 				(expr -> data.ns_add.rrdata,
1073 				 buf + rv, len - rv - left);
1074 			buf [rv++] = ' ';
1075 			left--;
1076 			rv += print_subexpression
1077 				(expr -> data.ns_add.ttl,
1078 				 buf + rv, len - rv - left);
1079 			buf [rv++] = ')';
1080 			buf [rv] = 0;
1081 			return rv;
1082 		}
1083 		break;
1084 
1085 	      case expr_null:
1086 		if (len > 6) {
1087 			strcpy (buf, "(null)");
1088 			return 6;
1089 		}
1090 		break;
1091 	      case expr_funcall:
1092 		rv = 12 + strlen (expr -> data.funcall.name);
1093 		if (len > rv + 1) {
1094 			strcpy (buf, "(funcall  ");
1095 			strcpy (buf + 9, expr -> data.funcall.name);
1096 			buf [rv++] = ' ';
1097 			rv += print_subexpression
1098 				(expr -> data.funcall.arglist, buf + rv,
1099 				 len - rv - 1);
1100 			buf [rv++] = ')';
1101 			buf [rv] = 0;
1102 			return rv;
1103 		}
1104 		break;
1105 
1106 	      case expr_arg:
1107 		rv = print_subexpression (expr -> data.arg.val, buf, len);
1108 		if (expr -> data.arg.next && rv + 2 < len) {
1109 			buf [rv++] = ' ';
1110 			rv += print_subexpression (expr -> data.arg.next,
1111 						   buf, len);
1112 			if (rv + 1 < len)
1113 				buf [rv++] = 0;
1114 			return rv;
1115 		}
1116 		break;
1117 
1118 	      case expr_function:
1119 		rv = 9;
1120 		if (len > rv + 1) {
1121 			struct string_list *foo;
1122 			strcpy (buf, "(function");
1123 			for (foo = expr -> data.func -> args;
1124 			     foo; foo = foo -> next) {
1125 				if (len > rv + 2 + strlen (foo -> string)) {
1126 					buf [rv - 1] = ' ';
1127 					strcpy (&buf [rv], foo -> string);
1128 					rv += strlen (foo -> string);
1129 				}
1130 			}
1131 			buf [rv++] = ')';
1132 			buf [rv] = 0;
1133 			return rv;
1134 		}
1135 		break;
1136 
1137 	      case expr_gethostname:
1138 		if (len > 13) {
1139 			strcpy(buf, "(gethostname)");
1140 			return 13;
1141 		}
1142 		break;
1143 
1144 	      default:
1145 		log_fatal("Impossible case at %s:%d (undefined expression "
1146 			  "%d).", MDL, expr->op);
1147 		break;
1148 	}
1149 	return 0;
1150 }
1151 
1152 void print_expression (name, expr)
1153 	const char *name;
1154 	struct expression *expr;
1155 {
1156 	char buf [1024];
1157 
1158 	print_subexpression (expr, buf, sizeof buf);
1159 	log_info ("%s: %s", name, buf);
1160 }
1161 
1162 int token_print_indent_concat (FILE *file, int col,  int indent,
1163 			       const char *prefix,
1164 			       const char *suffix, ...)
1165 {
1166 	va_list list;
1167 	unsigned len;
1168 	char *s, *t, *u;
1169 
1170 	va_start (list, suffix);
1171 	s = va_arg (list, char *);
1172 	len = 0;
1173 	while (s) {
1174 		len += strlen (s);
1175 		s = va_arg (list, char *);
1176 	}
1177 	va_end (list);
1178 
1179 	t = dmalloc (len + 1, MDL);
1180 	if (!t)
1181 		log_fatal ("token_print_indent: no memory for copy buffer");
1182 
1183 	va_start (list, suffix);
1184 	s = va_arg (list, char *);
1185 	u = t;
1186 	while (s) {
1187 		len = strlen (s);
1188 		strcpy (u, s);
1189 		u += len;
1190 		s = va_arg (list, char *);
1191 	}
1192 	va_end (list);
1193 
1194 	col = token_print_indent (file, col, indent,
1195 				  prefix, suffix, t);
1196 	dfree (t, MDL);
1197 	return col;
1198 }
1199 
1200 int token_indent_data_string (FILE *file, int col, int indent,
1201 			      const char *prefix, const char *suffix,
1202 			      struct data_string *data)
1203 {
1204 	int i;
1205 	char *buf;
1206 	char obuf [3];
1207 
1208 	/* See if this is just ASCII. */
1209 	for (i = 0; i < data -> len; i++)
1210 		if (!isascii (data -> data [i]) ||
1211 		    !isprint (data -> data [i]))
1212 			break;
1213 
1214 	/* If we have a purely ASCII string, output it as text. */
1215 	if (i == data -> len) {
1216 		buf = dmalloc (data -> len + 3, MDL);
1217 		if (buf) {
1218 			buf [0] = '"';
1219 			memcpy (buf + 1, data -> data, data -> len);
1220 			buf [data -> len + 1] = '"';
1221 			buf [data -> len + 2] = 0;
1222 			i = token_print_indent (file, col, indent,
1223 						prefix, suffix, buf);
1224 			dfree (buf, MDL);
1225 			return i;
1226 		}
1227 	}
1228 
1229 	for (i = 0; i < data -> len; i++) {
1230 		sprintf (obuf, "%2.2x", data -> data [i]);
1231 		col = token_print_indent (file, col, indent,
1232 					  i == 0 ? prefix : "",
1233 					  (i + 1 == data -> len
1234 					   ? suffix
1235 					   : ""), obuf);
1236 		if (i + 1 != data -> len)
1237 			col = token_print_indent (file, col, indent,
1238 						  prefix, suffix, ":");
1239 	}
1240 	return col;
1241 }
1242 
1243 int token_print_indent (FILE *file, int col, int indent,
1244 			const char *prefix,
1245 			const char *suffix, const char *buf)
1246 {
1247 	int len = 0;
1248 	if (prefix != NULL)
1249 		len += strlen (prefix);
1250 	if (buf != NULL)
1251 		len += strlen (buf);
1252 
1253 	if (col + len > 79) {
1254 		if (indent + len < 79) {
1255 			indent_spaces (file, indent);
1256 			col = indent;
1257 		} else {
1258 			indent_spaces (file, col);
1259 			col = len > 79 ? 0 : 79 - len - 1;
1260 		}
1261 	} else if (prefix && *prefix) {
1262 		fputs (prefix, file);
1263 		col += strlen (prefix);
1264 	}
1265 	if ((buf != NULL) && (*buf != 0)) {
1266 		fputs (buf, file);
1267 		col += strlen(buf);
1268 	}
1269 	if (suffix && *suffix) {
1270 		if (col + strlen (suffix) > 79) {
1271 			indent_spaces (file, indent);
1272 			col = indent;
1273 		} else {
1274 			fputs (suffix, file);
1275 			col += strlen (suffix);
1276 		}
1277 	}
1278 	return col;
1279 }
1280 
1281 void indent_spaces (FILE *file, int indent)
1282 {
1283 	int i;
1284 	fputc ('\n', file);
1285 	for (i = 0; i < indent; i++)
1286 		fputc (' ', file);
1287 }
1288 
1289 #if defined (NSUPDATE)
1290 #if defined (DEBUG_DNS_UPDATES)
1291 /*
1292  * direction outbound (messages to the dns server)
1293  *           inbound  (messages from the dns server)
1294  * ddns_cb is the control block associated with the message
1295  * result is the result from the dns code.  For outbound calls
1296  * it is from the call to pass the message to the dns library.
1297  * For inbound calls it is from the event returned by the library.
1298  *
1299  * For outbound messages we print whatever we think is interesting
1300  * from the control block.
1301  * For inbound messages we only print the transaction id pointer
1302  * and the result and expect that the user will match them up as
1303  * necessary.  Note well: the transaction information is opaque to
1304  * us so we simply print the pointer to it.  This should be sufficient
1305  * to match requests and replys in a short sequence but is awkward
1306  * when trying to use it for longer sequences.
1307  */
1308 void
1309 print_dns_status (int direction,
1310 		  struct dhcp_ddns_cb *ddns_cb,
1311 		  isc_result_t result)
1312 {
1313 	char obuf[1024];
1314 	char *s = obuf, *end = &obuf[sizeof(obuf)-2];
1315 	char *en;
1316 	const char *result_str;
1317 	char ddns_address[
1318 		sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
1319 
1320 	if (direction == DDNS_PRINT_INBOUND) {
1321 		log_info("DDNS reply: id ptr %p, result: %s",
1322 			 ddns_cb->transaction, isc_result_totext(result));
1323 		return;
1324 	}
1325 
1326 	/*
1327 	 * To avoid having to figure out if any of the strings
1328 	 * aren't NULL terminated, just 0 the whole string
1329 	 */
1330 	memset(obuf, 0, 1024);
1331 
1332 	en = "DDNS request: id ptr ";
1333 	if (s + strlen(en) + 16 < end) {
1334 		sprintf(s, "%s%p", en, ddns_cb->transaction);
1335 		s += strlen(s);
1336 	} else {
1337 		goto bailout;
1338 	}
1339 
1340 	switch (ddns_cb->state) {
1341 	case DDNS_STATE_ADD_FW_NXDOMAIN:
1342 		en = " add forward ";
1343 		break;
1344 	case DDNS_STATE_ADD_FW_YXDHCID:
1345 		en = " modify forward ";
1346 		break;
1347 
1348 	case DDNS_STATE_ADD_PTR:
1349 		en = " add reverse ";
1350 		break;
1351 
1352 	case DDNS_STATE_REM_FW_YXDHCID:
1353 		en = " remove forward ";
1354 		break;
1355 
1356 	case DDNS_STATE_REM_FW_NXRR:
1357 		en = " remove rrset ";
1358 		break;
1359 
1360 	case DDNS_STATE_REM_PTR:
1361 		en = " remove reverse ";
1362 		break;
1363 
1364 	case DDNS_STATE_CLEANUP:
1365 		en = " cleanup ";
1366 		break;
1367 
1368 	default:
1369 		en = " unknown state ";
1370 		break;
1371 	}
1372 
1373 	switch (ddns_cb->state) {
1374 	case DDNS_STATE_ADD_FW_NXDOMAIN:
1375 	case DDNS_STATE_ADD_FW_YXDHCID:
1376 	case DDNS_STATE_REM_FW_YXDHCID:
1377 	case DDNS_STATE_REM_FW_NXRR:
1378 		strcpy(ddns_address, piaddr(ddns_cb->address));
1379 		if (s + strlen(en) + strlen(ddns_address) +
1380 		    ddns_cb->fwd_name.len + 5 < end) {
1381 			sprintf(s, "%s%s for %.*s", en, ddns_address,
1382 				ddns_cb->fwd_name.len,
1383 				ddns_cb->fwd_name.data);
1384 			s += strlen(s);
1385 		} else {
1386 			goto bailout;
1387 		}
1388 		break;
1389 
1390 	case DDNS_STATE_ADD_PTR:
1391 	case DDNS_STATE_REM_PTR:
1392 		if (s + strlen(en) + ddns_cb->fwd_name.len +
1393 		    ddns_cb->rev_name.len + 5 < end) {
1394 			sprintf(s, "%s%.*s for %.*s", en,
1395 				ddns_cb->fwd_name.len,
1396 				ddns_cb->fwd_name.data,
1397 				ddns_cb->rev_name.len,
1398 				ddns_cb->rev_name.data);
1399 			s += strlen(s);
1400 		} else {
1401 			goto bailout;
1402 		}
1403 		break;
1404 
1405 	case DDNS_STATE_CLEANUP:
1406 	default:
1407 		if (s + strlen(en) < end) {
1408 			sprintf(s, "%s", en);
1409 			s += strlen(s);
1410 		} else {
1411 			goto bailout;
1412 		}
1413 		break;
1414 	}
1415 
1416 	en = " zone: ";
1417 	if (s + strlen(en) + strlen((char *)ddns_cb->zone_name) < end) {
1418 		sprintf(s, "%s%s", en, ddns_cb->zone_name);
1419 		s += strlen(s);
1420 	} else {
1421 		goto bailout;
1422 	}
1423 
1424 	en = " dhcid: ";
1425 	if (ddns_cb->dhcid.len > 0) {
1426 		if (s + strlen(en) + ddns_cb->dhcid.len-1 < end) {
1427 			strcpy(s, en);
1428 			s += strlen(s);
1429 			strncpy(s, (char *)ddns_cb->dhcid.data+1,
1430 				ddns_cb->dhcid.len-1);
1431 			s += strlen(s);
1432 		} else {
1433 			goto bailout;
1434 		}
1435 	} else {
1436 		en = " dhcid: <empty>";
1437 		if (s + strlen(en) < end) {
1438 			strcpy(s, en);
1439 			s += strlen(s);
1440 		} else {
1441 			goto bailout;
1442 		}
1443 	}
1444 
1445 	en = " ttl: ";
1446 	if (s + strlen(en) + 10 < end) {
1447 		sprintf(s, "%s%ld", en, ddns_cb->ttl);
1448 		s += strlen(s);
1449 	} else {
1450 		goto bailout;
1451 	}
1452 
1453 	en = " result: ";
1454 	result_str = isc_result_totext(result);
1455 	if (s + strlen(en) + strlen(result_str) < end) {
1456 		sprintf(s, "%s%s", en, result_str);
1457 		s += strlen(s);
1458 	} else {
1459 		goto bailout;
1460 	}
1461 
1462  bailout:
1463 	/*
1464 	 * We either finished building the string or ran out
1465 	 * of space, print whatever we have in case it is useful
1466 	 */
1467 	log_info("%s", obuf);
1468 
1469 	return;
1470 }
1471 #endif
1472 #endif /* NSUPDATE */
1473 
1474 /* Format the given time as "A; # B", where A is the format
1475  * used by the parser, and B is the local time, for humans.
1476  */
1477 const char *
1478 print_time(TIME t)
1479 {
1480 	static char buf[sizeof("epoch 9223372036854775807; "
1481 			       "# Wed Jun 30 21:49:08 2147483647")];
1482 	static char buf1[sizeof("# Wed Jun 30 21:49:08 2147483647")];
1483 	time_t since_epoch;
1484 	/* The string: 	       "6 2147483647/12/31 23:59:60;"
1485 	 * is smaller than the other, used to declare the buffer size, so
1486 	 * we can use one buffer for both.
1487 	 */
1488 
1489 	if (t == MAX_TIME)
1490 		return "never;";
1491 
1492 	if (t < 0)
1493 		return NULL;
1494 
1495 	/* For those lucky enough to have a 128-bit time_t, ensure that
1496 	 * whatever (corrupt) value we're given doesn't exceed the static
1497 	 * buffer.
1498 	 */
1499 #if (MAX_TIME > 0x7fffffffffffffff)
1500 	if (t > 0x7fffffffffffffff)
1501 		return NULL;
1502 #endif
1503 
1504 	if (db_time_format == LOCAL_TIME_FORMAT) {
1505 		since_epoch = mktime(localtime(&t));
1506 		if ((strftime(buf1, sizeof(buf1),
1507 			      "# %a %b %d %H:%M:%S %Y",
1508 			      localtime(&t)) == 0) ||
1509 		    (snprintf(buf, sizeof(buf), "epoch %lu; %s",
1510 			      (unsigned long)since_epoch, buf1) >= sizeof(buf)))
1511 			return NULL;
1512 
1513 	} else {
1514 		/* No bounds check for the year is necessary - in this case,
1515 		 * strftime() will run out of space and assert an error.
1516 		 */
1517 		if (strftime(buf, sizeof(buf), "%w %Y/%m/%d %H:%M:%S;",
1518 			     gmtime(&t)) == 0)
1519 			return NULL;
1520 	}
1521 
1522 	return buf;
1523 }
1524