1*6243ff0bSmpi /* $OpenBSD: printf.c,v 1.3 2024/09/06 07:58:50 mpi Exp $ */ 223160851Smpi 323160851Smpi /* 423160851Smpi * Copyright (c) 1989 The Regents of the University of California. 523160851Smpi * All rights reserved. 623160851Smpi * 723160851Smpi * Redistribution and use in source and binary forms, with or without 823160851Smpi * modification, are permitted provided that the following conditions 923160851Smpi * are met: 1023160851Smpi * 1. Redistributions of source code must retain the above copyright 1123160851Smpi * notice, this list of conditions and the following disclaimer. 1223160851Smpi * 2. Redistributions in binary form must reproduce the above copyright 1323160851Smpi * notice, this list of conditions and the following disclaimer in the 1423160851Smpi * documentation and/or other materials provided with the distribution. 1523160851Smpi * 3. Neither the name of the University nor the names of its contributors 1623160851Smpi * may be used to endorse or promote products derived from this software 1723160851Smpi * without specific prior written permission. 1823160851Smpi * 1923160851Smpi * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2023160851Smpi * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2123160851Smpi * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2223160851Smpi * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2323160851Smpi * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2423160851Smpi * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2523160851Smpi * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2623160851Smpi * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2723160851Smpi * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2823160851Smpi * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2923160851Smpi * SUCH DAMAGE. 3023160851Smpi */ 3123160851Smpi 3223160851Smpi #include <sys/queue.h> 3323160851Smpi 3423160851Smpi #include <ctype.h> 3523160851Smpi #include <err.h> 3623160851Smpi #include <errno.h> 3723160851Smpi #include <limits.h> 3823160851Smpi #include <stdio.h> 3923160851Smpi #include <stdlib.h> 4023160851Smpi #include <string.h> 4123160851Smpi #include <unistd.h> 4223160851Smpi 4323160851Smpi #include <dev/dt/dtvar.h> 4423160851Smpi #include "bt_parser.h" 4523160851Smpi 4623160851Smpi #include "btrace.h" 4723160851Smpi 4823160851Smpi static int print_escape_str(const char *); 4923160851Smpi static int print_escape(const char *); 5023160851Smpi 5123160851Smpi static int getchr(void); 5223160851Smpi static double getdouble(void); 5323160851Smpi static int getint(void); 5423160851Smpi static long getlong(void); 5523160851Smpi static unsigned long getulong(void); 5623160851Smpi static const char *getstr(void); 5723160851Smpi static char *mklong(const char *, int); 5823160851Smpi static void check_conversion(const char *, const char *); 5923160851Smpi 6023160851Smpi static int rval; 6123160851Smpi static struct bt_arg *gargv; 6223160851Smpi static struct dt_evt *gdevt; 6323160851Smpi 6423160851Smpi #define isodigit(c) ((c) >= '0' && (c) <= '7') 6523160851Smpi #define octtobin(c) ((c) - '0') 6623160851Smpi #define hextobin(c) ((c) >= 'A' && (c) <= 'F' ? c - 'A' + 10 : (c) >= 'a' && (c) <= 'f' ? c - 'a' + 10 : c - '0') 6723160851Smpi 6823160851Smpi #define PF(f, func) { \ 6923160851Smpi if (havefieldwidth) \ 7023160851Smpi if (haveprecision) \ 7123160851Smpi (void)printf(f, fieldwidth, precision, func); \ 7223160851Smpi else \ 7323160851Smpi (void)printf(f, fieldwidth, func); \ 7423160851Smpi else if (haveprecision) \ 7523160851Smpi (void)printf(f, precision, func); \ 7623160851Smpi else \ 7723160851Smpi (void)printf(f, func); \ 7823160851Smpi } 7923160851Smpi 8023160851Smpi int 8123160851Smpi stmt_printf(struct bt_stmt *bs, struct dt_evt *des) 8223160851Smpi { 8323160851Smpi struct bt_arg *ba = SLIST_FIRST(&bs->bs_args); 8423160851Smpi char *fmt, *start; 8523160851Smpi int havefieldwidth, haveprecision; 8623160851Smpi int fieldwidth, precision; 8723160851Smpi char convch, nextch; 8823160851Smpi char *format; 8923160851Smpi 9023160851Smpi format = (char *)ba2str(ba, gdevt); // XXX modification? 9123160851Smpi gargv = SLIST_NEXT(ba, ba_next); 9223160851Smpi gdevt = des; 9323160851Smpi 9423160851Smpi #define SKIP1 "#-+ 0" 9523160851Smpi #define SKIP2 "0123456789" 9623160851Smpi do { 9723160851Smpi /* 9823160851Smpi * Basic algorithm is to scan the format string for conversion 9923160851Smpi * specifications -- once one is found, find out if the field 10023160851Smpi * width or precision is a '*'; if it is, gather up value. 10123160851Smpi * Note, format strings are reused as necessary to use up the 10223160851Smpi * provided arguments, arguments of zero/null string are 10323160851Smpi * provided to use up the format string. 10423160851Smpi */ 10523160851Smpi 10623160851Smpi /* find next format specification */ 10723160851Smpi for (fmt = format; *fmt; fmt++) { 10823160851Smpi switch (*fmt) { 10923160851Smpi case '%': 11023160851Smpi start = fmt++; 11123160851Smpi 11223160851Smpi if (*fmt == '%') { 11323160851Smpi putchar ('%'); 11423160851Smpi break; 11523160851Smpi } else if (*fmt == 'b') { 11623160851Smpi const char *p = getstr(); 11723160851Smpi if (print_escape_str(p)) { 11823160851Smpi return (rval); 11923160851Smpi } 12023160851Smpi break; 12123160851Smpi } 12223160851Smpi 12323160851Smpi /* skip to field width */ 12423160851Smpi for (; strchr(SKIP1, *fmt); ++fmt) 12523160851Smpi ; 12623160851Smpi if (*fmt == '*') { 12723160851Smpi ++fmt; 12823160851Smpi havefieldwidth = 1; 12923160851Smpi fieldwidth = getint(); 13023160851Smpi } else 13123160851Smpi havefieldwidth = 0; 13223160851Smpi 13323160851Smpi /* skip to field precision */ 13423160851Smpi for (; strchr(SKIP2, *fmt); ++fmt) 13523160851Smpi ; 13623160851Smpi haveprecision = 0; 13723160851Smpi if (*fmt == '.') { 13823160851Smpi ++fmt; 13923160851Smpi if (*fmt == '*') { 14023160851Smpi ++fmt; 14123160851Smpi haveprecision = 1; 14223160851Smpi precision = getint(); 14323160851Smpi } 14423160851Smpi for (; strchr(SKIP2, *fmt); ++fmt) 14523160851Smpi ; 14623160851Smpi } 14723160851Smpi 14823160851Smpi if (!*fmt) { 14923160851Smpi warnx ("missing format character"); 15023160851Smpi return(1); 15123160851Smpi } 15223160851Smpi 15323160851Smpi convch = *fmt; 15423160851Smpi nextch = *(fmt + 1); 15523160851Smpi *(fmt + 1) = '\0'; 15623160851Smpi switch(convch) { 15723160851Smpi case 'c': { 15823160851Smpi char p = getchr(); 15923160851Smpi PF(start, p); 16023160851Smpi break; 16123160851Smpi } 16223160851Smpi case 's': { 16323160851Smpi const char *p = getstr(); 16423160851Smpi PF(start, p); 16523160851Smpi break; 16623160851Smpi } 16723160851Smpi case 'd': 16823160851Smpi case 'i': { 16923160851Smpi long p; 17023160851Smpi char *f = mklong(start, convch); 17123160851Smpi if (!f) { 17223160851Smpi warnx("out of memory"); 17323160851Smpi return (1); 17423160851Smpi } 17523160851Smpi p = getlong(); 17623160851Smpi PF(f, p); 17723160851Smpi break; 17823160851Smpi } 17923160851Smpi case 'o': 18023160851Smpi case 'u': 18123160851Smpi case 'x': 18223160851Smpi case 'X': { 18323160851Smpi unsigned long p; 18423160851Smpi char *f = mklong(start, convch); 18523160851Smpi if (!f) { 18623160851Smpi warnx("out of memory"); 18723160851Smpi return (1); 18823160851Smpi } 18923160851Smpi p = getulong(); 19023160851Smpi PF(f, p); 19123160851Smpi break; 19223160851Smpi } 19323160851Smpi case 'a': 19423160851Smpi case 'A': 19523160851Smpi case 'e': 19623160851Smpi case 'E': 19723160851Smpi case 'f': 19823160851Smpi case 'F': 19923160851Smpi case 'g': 20023160851Smpi case 'G': { 20123160851Smpi double p = getdouble(); 20223160851Smpi PF(start, p); 20323160851Smpi break; 20423160851Smpi } 20523160851Smpi default: 20623160851Smpi warnx ("%s: invalid directive", start); 20723160851Smpi return(1); 20823160851Smpi } 20923160851Smpi *(fmt + 1) = nextch; 21023160851Smpi break; 21123160851Smpi 21223160851Smpi case '\\': 21323160851Smpi fmt += print_escape(fmt); 21423160851Smpi break; 21523160851Smpi 21623160851Smpi default: 21723160851Smpi putchar (*fmt); 21823160851Smpi break; 21923160851Smpi } 22023160851Smpi } 22123160851Smpi } while (gargv != NULL); 22223160851Smpi 22323160851Smpi return (rval); 22423160851Smpi } 22523160851Smpi 22623160851Smpi 22723160851Smpi /* 22823160851Smpi * Print SysV echo(1) style escape string 22923160851Smpi * Halts processing string and returns 1 if a \c escape is encountered. 23023160851Smpi */ 23123160851Smpi static int 23223160851Smpi print_escape_str(const char *str) 23323160851Smpi { 23423160851Smpi int value; 23523160851Smpi int c; 23623160851Smpi 23723160851Smpi while (*str) { 23823160851Smpi if (*str == '\\') { 23923160851Smpi str++; 24023160851Smpi /* 24123160851Smpi * %b string octal constants are not like those in C. 24223160851Smpi * They start with a \0, and are followed by 0, 1, 2, 24323160851Smpi * or 3 octal digits. 24423160851Smpi */ 24523160851Smpi if (*str == '0') { 24623160851Smpi str++; 24723160851Smpi for (c = 3, value = 0; c-- && isodigit(*str); str++) { 24823160851Smpi value <<= 3; 24923160851Smpi value += octtobin(*str); 25023160851Smpi } 25123160851Smpi putchar (value); 25223160851Smpi str--; 25323160851Smpi } else if (*str == 'c') { 25423160851Smpi return 1; 25523160851Smpi } else { 25623160851Smpi str--; 25723160851Smpi str += print_escape(str); 25823160851Smpi } 25923160851Smpi } else { 26023160851Smpi putchar (*str); 26123160851Smpi } 26223160851Smpi str++; 26323160851Smpi } 26423160851Smpi 26523160851Smpi return 0; 26623160851Smpi } 26723160851Smpi 26823160851Smpi /* 26923160851Smpi * Print "standard" escape characters 27023160851Smpi */ 27123160851Smpi static int 27223160851Smpi print_escape(const char *str) 27323160851Smpi { 27423160851Smpi const char *start = str; 27523160851Smpi int value; 27623160851Smpi int c; 27723160851Smpi 27823160851Smpi str++; 27923160851Smpi 28023160851Smpi switch (*str) { 28123160851Smpi case '0': case '1': case '2': case '3': 28223160851Smpi case '4': case '5': case '6': case '7': 28323160851Smpi for (c = 3, value = 0; c-- && isodigit(*str); str++) { 28423160851Smpi value <<= 3; 28523160851Smpi value += octtobin(*str); 28623160851Smpi } 28723160851Smpi putchar(value); 28823160851Smpi return str - start - 1; 28923160851Smpi /* NOTREACHED */ 29023160851Smpi 29123160851Smpi case 'x': 29223160851Smpi str++; 29323160851Smpi for (value = 0; isxdigit((unsigned char)*str); str++) { 29423160851Smpi value <<= 4; 29523160851Smpi value += hextobin(*str); 29623160851Smpi } 29723160851Smpi if (value > UCHAR_MAX) { 29823160851Smpi warnx ("escape sequence out of range for character"); 29923160851Smpi rval = 1; 30023160851Smpi } 30123160851Smpi putchar (value); 30223160851Smpi return str - start - 1; 30323160851Smpi /* NOTREACHED */ 30423160851Smpi 30523160851Smpi case '\\': /* backslash */ 30623160851Smpi putchar('\\'); 30723160851Smpi break; 30823160851Smpi 30923160851Smpi case '\'': /* single quote */ 31023160851Smpi putchar('\''); 31123160851Smpi break; 31223160851Smpi 31323160851Smpi case '"': /* double quote */ 31423160851Smpi putchar('"'); 31523160851Smpi break; 31623160851Smpi 31723160851Smpi case 'a': /* alert */ 31823160851Smpi putchar('\a'); 31923160851Smpi break; 32023160851Smpi 32123160851Smpi case 'b': /* backspace */ 32223160851Smpi putchar('\b'); 32323160851Smpi break; 32423160851Smpi 32523160851Smpi case 'e': /* escape */ 32623160851Smpi #ifdef __GNUC__ 32723160851Smpi putchar('\e'); 32823160851Smpi #else 32923160851Smpi putchar(033); 33023160851Smpi #endif 33123160851Smpi break; 33223160851Smpi 33323160851Smpi case 'f': /* form-feed */ 33423160851Smpi putchar('\f'); 33523160851Smpi break; 33623160851Smpi 33723160851Smpi case 'n': /* newline */ 33823160851Smpi putchar('\n'); 33923160851Smpi break; 34023160851Smpi 34123160851Smpi case 'r': /* carriage-return */ 34223160851Smpi putchar('\r'); 34323160851Smpi break; 34423160851Smpi 34523160851Smpi case 't': /* tab */ 34623160851Smpi putchar('\t'); 34723160851Smpi break; 34823160851Smpi 34923160851Smpi case 'v': /* vertical-tab */ 35023160851Smpi putchar('\v'); 35123160851Smpi break; 35223160851Smpi 35323160851Smpi case '\0': 35423160851Smpi warnx("null escape sequence"); 35523160851Smpi rval = 1; 35623160851Smpi return 0; 35723160851Smpi 35823160851Smpi default: 35923160851Smpi putchar(*str); 36023160851Smpi warnx("unknown escape sequence `\\%c'", *str); 36123160851Smpi rval = 1; 36223160851Smpi } 36323160851Smpi 36423160851Smpi return 1; 36523160851Smpi } 36623160851Smpi 36723160851Smpi static char * 36823160851Smpi mklong(const char *str, int ch) 36923160851Smpi { 37023160851Smpi static char *copy; 37123160851Smpi static int copysize; 37223160851Smpi int len; 37323160851Smpi 37423160851Smpi len = strlen(str) + 2; 37523160851Smpi if (copysize < len) { 37623160851Smpi char *newcopy; 37723160851Smpi copysize = len + 256; 37823160851Smpi 37923160851Smpi newcopy = realloc(copy, copysize); 38023160851Smpi if (newcopy == NULL) { 38123160851Smpi copysize = 0; 38223160851Smpi free(copy); 38323160851Smpi copy = NULL; 38423160851Smpi return (NULL); 38523160851Smpi } 38623160851Smpi copy = newcopy; 38723160851Smpi } 38823160851Smpi (void) memmove(copy, str, len - 3); 38923160851Smpi copy[len - 3] = 'l'; 39023160851Smpi copy[len - 2] = ch; 39123160851Smpi copy[len - 1] = '\0'; 39223160851Smpi return (copy); 39323160851Smpi } 39423160851Smpi 39523160851Smpi static int 39623160851Smpi getchr(void) 39723160851Smpi { 39823160851Smpi int c; 39923160851Smpi 40023160851Smpi if (gargv == NULL) 40123160851Smpi return((int)'\0'); 40223160851Smpi 403*6243ff0bSmpi c = ba2long(gargv, gdevt); 40423160851Smpi gargv = SLIST_NEXT(gargv, ba_next); 40523160851Smpi return c; 40623160851Smpi } 40723160851Smpi 40823160851Smpi static const char * 40923160851Smpi getstr(void) 41023160851Smpi { 41123160851Smpi const char *str; 41223160851Smpi 41323160851Smpi if (gargv == NULL) 41423160851Smpi return ""; 41523160851Smpi 41623160851Smpi str = ba2str(gargv, gdevt); 41723160851Smpi gargv = SLIST_NEXT(gargv, ba_next); 41823160851Smpi return str; 41923160851Smpi } 42023160851Smpi 42123160851Smpi static char *number = "+-.0123456789"; 42223160851Smpi static int 42323160851Smpi getint(void) 42423160851Smpi { 42523160851Smpi const char *str; 42623160851Smpi 42723160851Smpi if (gargv == NULL) 42823160851Smpi return 0; 42923160851Smpi 43023160851Smpi str = ba2str(gargv, gdevt); 43123160851Smpi if (strchr(number, *str)) { 43223160851Smpi gargv = SLIST_NEXT(gargv, ba_next); 43323160851Smpi return atoi(str); 43423160851Smpi } 43523160851Smpi 43623160851Smpi return 0; 43723160851Smpi } 43823160851Smpi 43923160851Smpi static long 44023160851Smpi getlong(void) 44123160851Smpi { 44223160851Smpi const char *str; 44323160851Smpi long val; 44423160851Smpi char *ep; 44523160851Smpi 44623160851Smpi if (gargv == NULL) 44723160851Smpi return 0UL; 44823160851Smpi 44923160851Smpi str = ba2str(gargv, gdevt); 45023160851Smpi gargv = SLIST_NEXT(gargv, ba_next); 45123160851Smpi 45223160851Smpi if (*str == '\"' || *str == '\'') { 45323160851Smpi unsigned char c = (unsigned char)str[1]; 45423160851Smpi return c; 45523160851Smpi } 45623160851Smpi 45723160851Smpi errno = 0; 45823160851Smpi val = strtol(str, &ep, 0); 45923160851Smpi check_conversion(str, ep); 46023160851Smpi return val; 46123160851Smpi } 46223160851Smpi 46323160851Smpi static unsigned long 46423160851Smpi getulong(void) 46523160851Smpi { 46623160851Smpi const char *str; 46723160851Smpi unsigned long val; 46823160851Smpi char *ep; 46923160851Smpi 47023160851Smpi if (gargv == NULL) 47123160851Smpi return 0UL; 47223160851Smpi 47323160851Smpi str = ba2str(gargv, gdevt); 47423160851Smpi gargv = SLIST_NEXT(gargv, ba_next); 47523160851Smpi 47623160851Smpi if (*str == '\"' || *str == '\'') { 47723160851Smpi unsigned char c = (unsigned char)str[1]; 47823160851Smpi return c; 47923160851Smpi } 48023160851Smpi 48123160851Smpi errno = 0; 48223160851Smpi val = strtoul(str, &ep, 0); 48323160851Smpi check_conversion(str, ep); 48423160851Smpi return val; 48523160851Smpi } 48623160851Smpi 48723160851Smpi static double 48823160851Smpi getdouble(void) 48923160851Smpi { 49023160851Smpi const char *str; 49123160851Smpi double val; 49223160851Smpi char *ep; 49323160851Smpi 49423160851Smpi if (gargv == NULL) 49523160851Smpi return 0.0; 49623160851Smpi 49723160851Smpi str = ba2str(gargv, gdevt); 49823160851Smpi gargv = SLIST_NEXT(gargv, ba_next); 49923160851Smpi 50023160851Smpi if (*str == '\"' || *str == '\'') { 50123160851Smpi unsigned char c = (unsigned char)str[1]; 50223160851Smpi return c; 50323160851Smpi } 50423160851Smpi 50523160851Smpi errno = 0; 50623160851Smpi val = strtod(str, &ep); 50723160851Smpi check_conversion(str, ep); 50823160851Smpi return val; 50923160851Smpi } 51023160851Smpi 51123160851Smpi static void 51223160851Smpi check_conversion(const char *s, const char *ep) 51323160851Smpi { 51423160851Smpi if (*ep) { 51523160851Smpi if (ep == s) 51623160851Smpi warnx ("%s: expected numeric value", s); 51723160851Smpi else 51823160851Smpi warnx ("%s: not completely converted", s); 51923160851Smpi rval = 1; 52023160851Smpi } else if (errno == ERANGE) { 52123160851Smpi warnc(ERANGE, "%s", s); 52223160851Smpi rval = 1; 52323160851Smpi } 52423160851Smpi } 525