jcs's openbsd hax
openbsd
1/* $OpenBSD: printf.c,v 1.3 2024/09/06 07:58:50 mpi Exp $ */
2
3/*
4 * Copyright (c) 1989 The Regents of the University of California.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32#include <sys/queue.h>
33
34#include <ctype.h>
35#include <err.h>
36#include <errno.h>
37#include <limits.h>
38#include <stdio.h>
39#include <stdlib.h>
40#include <string.h>
41#include <unistd.h>
42
43#include <dev/dt/dtvar.h>
44#include "bt_parser.h"
45
46#include "btrace.h"
47
48static int print_escape_str(const char *);
49static int print_escape(const char *);
50
51static int getchr(void);
52static double getdouble(void);
53static int getint(void);
54static long getlong(void);
55static unsigned long getulong(void);
56static const char *getstr(void);
57static char *mklong(const char *, int);
58static void check_conversion(const char *, const char *);
59
60static int rval;
61static struct bt_arg *gargv;
62static struct dt_evt *gdevt;
63
64#define isodigit(c) ((c) >= '0' && (c) <= '7')
65#define octtobin(c) ((c) - '0')
66#define hextobin(c) ((c) >= 'A' && (c) <= 'F' ? c - 'A' + 10 : (c) >= 'a' && (c) <= 'f' ? c - 'a' + 10 : c - '0')
67
68#define PF(f, func) { \
69 if (havefieldwidth) \
70 if (haveprecision) \
71 (void)printf(f, fieldwidth, precision, func); \
72 else \
73 (void)printf(f, fieldwidth, func); \
74 else if (haveprecision) \
75 (void)printf(f, precision, func); \
76 else \
77 (void)printf(f, func); \
78}
79
80int
81stmt_printf(struct bt_stmt *bs, struct dt_evt *des)
82{
83 struct bt_arg *ba = SLIST_FIRST(&bs->bs_args);
84 char *fmt, *start;
85 int havefieldwidth, haveprecision;
86 int fieldwidth, precision;
87 char convch, nextch;
88 char *format;
89
90 format = (char *)ba2str(ba, gdevt); // XXX modification?
91 gargv = SLIST_NEXT(ba, ba_next);
92 gdevt = des;
93
94#define SKIP1 "#-+ 0"
95#define SKIP2 "0123456789"
96 do {
97 /*
98 * Basic algorithm is to scan the format string for conversion
99 * specifications -- once one is found, find out if the field
100 * width or precision is a '*'; if it is, gather up value.
101 * Note, format strings are reused as necessary to use up the
102 * provided arguments, arguments of zero/null string are
103 * provided to use up the format string.
104 */
105
106 /* find next format specification */
107 for (fmt = format; *fmt; fmt++) {
108 switch (*fmt) {
109 case '%':
110 start = fmt++;
111
112 if (*fmt == '%') {
113 putchar ('%');
114 break;
115 } else if (*fmt == 'b') {
116 const char *p = getstr();
117 if (print_escape_str(p)) {
118 return (rval);
119 }
120 break;
121 }
122
123 /* skip to field width */
124 for (; strchr(SKIP1, *fmt); ++fmt)
125 ;
126 if (*fmt == '*') {
127 ++fmt;
128 havefieldwidth = 1;
129 fieldwidth = getint();
130 } else
131 havefieldwidth = 0;
132
133 /* skip to field precision */
134 for (; strchr(SKIP2, *fmt); ++fmt)
135 ;
136 haveprecision = 0;
137 if (*fmt == '.') {
138 ++fmt;
139 if (*fmt == '*') {
140 ++fmt;
141 haveprecision = 1;
142 precision = getint();
143 }
144 for (; strchr(SKIP2, *fmt); ++fmt)
145 ;
146 }
147
148 if (!*fmt) {
149 warnx ("missing format character");
150 return(1);
151 }
152
153 convch = *fmt;
154 nextch = *(fmt + 1);
155 *(fmt + 1) = '\0';
156 switch(convch) {
157 case 'c': {
158 char p = getchr();
159 PF(start, p);
160 break;
161 }
162 case 's': {
163 const char *p = getstr();
164 PF(start, p);
165 break;
166 }
167 case 'd':
168 case 'i': {
169 long p;
170 char *f = mklong(start, convch);
171 if (!f) {
172 warnx("out of memory");
173 return (1);
174 }
175 p = getlong();
176 PF(f, p);
177 break;
178 }
179 case 'o':
180 case 'u':
181 case 'x':
182 case 'X': {
183 unsigned long p;
184 char *f = mklong(start, convch);
185 if (!f) {
186 warnx("out of memory");
187 return (1);
188 }
189 p = getulong();
190 PF(f, p);
191 break;
192 }
193 case 'a':
194 case 'A':
195 case 'e':
196 case 'E':
197 case 'f':
198 case 'F':
199 case 'g':
200 case 'G': {
201 double p = getdouble();
202 PF(start, p);
203 break;
204 }
205 default:
206 warnx ("%s: invalid directive", start);
207 return(1);
208 }
209 *(fmt + 1) = nextch;
210 break;
211
212 case '\\':
213 fmt += print_escape(fmt);
214 break;
215
216 default:
217 putchar (*fmt);
218 break;
219 }
220 }
221 } while (gargv != NULL);
222
223 return (rval);
224}
225
226
227/*
228 * Print SysV echo(1) style escape string
229 * Halts processing string and returns 1 if a \c escape is encountered.
230 */
231static int
232print_escape_str(const char *str)
233{
234 int value;
235 int c;
236
237 while (*str) {
238 if (*str == '\\') {
239 str++;
240 /*
241 * %b string octal constants are not like those in C.
242 * They start with a \0, and are followed by 0, 1, 2,
243 * or 3 octal digits.
244 */
245 if (*str == '0') {
246 str++;
247 for (c = 3, value = 0; c-- && isodigit(*str); str++) {
248 value <<= 3;
249 value += octtobin(*str);
250 }
251 putchar (value);
252 str--;
253 } else if (*str == 'c') {
254 return 1;
255 } else {
256 str--;
257 str += print_escape(str);
258 }
259 } else {
260 putchar (*str);
261 }
262 str++;
263 }
264
265 return 0;
266}
267
268/*
269 * Print "standard" escape characters
270 */
271static int
272print_escape(const char *str)
273{
274 const char *start = str;
275 int value;
276 int c;
277
278 str++;
279
280 switch (*str) {
281 case '0': case '1': case '2': case '3':
282 case '4': case '5': case '6': case '7':
283 for (c = 3, value = 0; c-- && isodigit(*str); str++) {
284 value <<= 3;
285 value += octtobin(*str);
286 }
287 putchar(value);
288 return str - start - 1;
289 /* NOTREACHED */
290
291 case 'x':
292 str++;
293 for (value = 0; isxdigit((unsigned char)*str); str++) {
294 value <<= 4;
295 value += hextobin(*str);
296 }
297 if (value > UCHAR_MAX) {
298 warnx ("escape sequence out of range for character");
299 rval = 1;
300 }
301 putchar (value);
302 return str - start - 1;
303 /* NOTREACHED */
304
305 case '\\': /* backslash */
306 putchar('\\');
307 break;
308
309 case '\'': /* single quote */
310 putchar('\'');
311 break;
312
313 case '"': /* double quote */
314 putchar('"');
315 break;
316
317 case 'a': /* alert */
318 putchar('\a');
319 break;
320
321 case 'b': /* backspace */
322 putchar('\b');
323 break;
324
325 case 'e': /* escape */
326#ifdef __GNUC__
327 putchar('\e');
328#else
329 putchar(033);
330#endif
331 break;
332
333 case 'f': /* form-feed */
334 putchar('\f');
335 break;
336
337 case 'n': /* newline */
338 putchar('\n');
339 break;
340
341 case 'r': /* carriage-return */
342 putchar('\r');
343 break;
344
345 case 't': /* tab */
346 putchar('\t');
347 break;
348
349 case 'v': /* vertical-tab */
350 putchar('\v');
351 break;
352
353 case '\0':
354 warnx("null escape sequence");
355 rval = 1;
356 return 0;
357
358 default:
359 putchar(*str);
360 warnx("unknown escape sequence `\\%c'", *str);
361 rval = 1;
362 }
363
364 return 1;
365}
366
367static char *
368mklong(const char *str, int ch)
369{
370 static char *copy;
371 static int copysize;
372 int len;
373
374 len = strlen(str) + 2;
375 if (copysize < len) {
376 char *newcopy;
377 copysize = len + 256;
378
379 newcopy = realloc(copy, copysize);
380 if (newcopy == NULL) {
381 copysize = 0;
382 free(copy);
383 copy = NULL;
384 return (NULL);
385 }
386 copy = newcopy;
387 }
388 (void) memmove(copy, str, len - 3);
389 copy[len - 3] = 'l';
390 copy[len - 2] = ch;
391 copy[len - 1] = '\0';
392 return (copy);
393}
394
395static int
396getchr(void)
397{
398 int c;
399
400 if (gargv == NULL)
401 return((int)'\0');
402
403 c = ba2long(gargv, gdevt);
404 gargv = SLIST_NEXT(gargv, ba_next);
405 return c;
406}
407
408static const char *
409getstr(void)
410{
411 const char *str;
412
413 if (gargv == NULL)
414 return "";
415
416 str = ba2str(gargv, gdevt);
417 gargv = SLIST_NEXT(gargv, ba_next);
418 return str;
419}
420
421static char *number = "+-.0123456789";
422static int
423getint(void)
424{
425 const char *str;
426
427 if (gargv == NULL)
428 return 0;
429
430 str = ba2str(gargv, gdevt);
431 if (strchr(number, *str)) {
432 gargv = SLIST_NEXT(gargv, ba_next);
433 return atoi(str);
434 }
435
436 return 0;
437}
438
439static long
440getlong(void)
441{
442 const char *str;
443 long val;
444 char *ep;
445
446 if (gargv == NULL)
447 return 0UL;
448
449 str = ba2str(gargv, gdevt);
450 gargv = SLIST_NEXT(gargv, ba_next);
451
452 if (*str == '\"' || *str == '\'') {
453 unsigned char c = (unsigned char)str[1];
454 return c;
455 }
456
457 errno = 0;
458 val = strtol(str, &ep, 0);
459 check_conversion(str, ep);
460 return val;
461}
462
463static unsigned long
464getulong(void)
465{
466 const char *str;
467 unsigned long val;
468 char *ep;
469
470 if (gargv == NULL)
471 return 0UL;
472
473 str = ba2str(gargv, gdevt);
474 gargv = SLIST_NEXT(gargv, ba_next);
475
476 if (*str == '\"' || *str == '\'') {
477 unsigned char c = (unsigned char)str[1];
478 return c;
479 }
480
481 errno = 0;
482 val = strtoul(str, &ep, 0);
483 check_conversion(str, ep);
484 return val;
485}
486
487static double
488getdouble(void)
489{
490 const char *str;
491 double val;
492 char *ep;
493
494 if (gargv == NULL)
495 return 0.0;
496
497 str = ba2str(gargv, gdevt);
498 gargv = SLIST_NEXT(gargv, ba_next);
499
500 if (*str == '\"' || *str == '\'') {
501 unsigned char c = (unsigned char)str[1];
502 return c;
503 }
504
505 errno = 0;
506 val = strtod(str, &ep);
507 check_conversion(str, ep);
508 return val;
509}
510
511static void
512check_conversion(const char *s, const char *ep)
513{
514 if (*ep) {
515 if (ep == s)
516 warnx ("%s: expected numeric value", s);
517 else
518 warnx ("%s: not completely converted", s);
519 rval = 1;
520 } else if (errno == ERANGE) {
521 warnc(ERANGE, "%s", s);
522 rval = 1;
523 }
524}