162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) Paul Mackerras 1997. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci#include <stdarg.h> 662306a36Sopenharmony_ci#include <stddef.h> 762306a36Sopenharmony_ci#include "string.h" 862306a36Sopenharmony_ci#include "stdio.h" 962306a36Sopenharmony_ci#include "ops.h" 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_cisize_t strnlen(const char * s, size_t count) 1262306a36Sopenharmony_ci{ 1362306a36Sopenharmony_ci const char *sc; 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci for (sc = s; count-- && *sc != '\0'; ++sc) 1662306a36Sopenharmony_ci /* nothing */; 1762306a36Sopenharmony_ci return sc - s; 1862306a36Sopenharmony_ci} 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cichar *strrchr(const char *s, int c) 2162306a36Sopenharmony_ci{ 2262306a36Sopenharmony_ci const char *last = NULL; 2362306a36Sopenharmony_ci do { 2462306a36Sopenharmony_ci if (*s == (char)c) 2562306a36Sopenharmony_ci last = s; 2662306a36Sopenharmony_ci } while (*s++); 2762306a36Sopenharmony_ci return (char *)last; 2862306a36Sopenharmony_ci} 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#ifdef __powerpc64__ 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci# define do_div(n, base) ({ \ 3362306a36Sopenharmony_ci unsigned int __base = (base); \ 3462306a36Sopenharmony_ci unsigned int __rem; \ 3562306a36Sopenharmony_ci __rem = ((unsigned long long)(n)) % __base; \ 3662306a36Sopenharmony_ci (n) = ((unsigned long long)(n)) / __base; \ 3762306a36Sopenharmony_ci __rem; \ 3862306a36Sopenharmony_ci}) 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci#else 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ciextern unsigned int __div64_32(unsigned long long *dividend, 4362306a36Sopenharmony_ci unsigned int divisor); 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci/* The unnecessary pointer compare is there 4662306a36Sopenharmony_ci * to check for type safety (n must be 64bit) 4762306a36Sopenharmony_ci */ 4862306a36Sopenharmony_ci# define do_div(n,base) ({ \ 4962306a36Sopenharmony_ci unsigned int __base = (base); \ 5062306a36Sopenharmony_ci unsigned int __rem; \ 5162306a36Sopenharmony_ci (void)(((typeof((n)) *)0) == ((unsigned long long *)0)); \ 5262306a36Sopenharmony_ci if (((n) >> 32) == 0) { \ 5362306a36Sopenharmony_ci __rem = (unsigned int)(n) % __base; \ 5462306a36Sopenharmony_ci (n) = (unsigned int)(n) / __base; \ 5562306a36Sopenharmony_ci } else \ 5662306a36Sopenharmony_ci __rem = __div64_32(&(n), __base); \ 5762306a36Sopenharmony_ci __rem; \ 5862306a36Sopenharmony_ci }) 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci#endif /* __powerpc64__ */ 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistatic int skip_atoi(const char **s) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci int i, c; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci for (i = 0; '0' <= (c = **s) && c <= '9'; ++*s) 6762306a36Sopenharmony_ci i = i*10 + c - '0'; 6862306a36Sopenharmony_ci return i; 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci#define ZEROPAD 1 /* pad with zero */ 7262306a36Sopenharmony_ci#define SIGN 2 /* unsigned/signed long */ 7362306a36Sopenharmony_ci#define PLUS 4 /* show plus */ 7462306a36Sopenharmony_ci#define SPACE 8 /* space if plus */ 7562306a36Sopenharmony_ci#define LEFT 16 /* left justified */ 7662306a36Sopenharmony_ci#define SPECIAL 32 /* 0x */ 7762306a36Sopenharmony_ci#define LARGE 64 /* use 'ABCDEF' instead of 'abcdef' */ 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic char * number(char * str, unsigned long long num, int base, int size, int precision, int type) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci char c,sign,tmp[66]; 8262306a36Sopenharmony_ci const char *digits="0123456789abcdefghijklmnopqrstuvwxyz"; 8362306a36Sopenharmony_ci int i; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci if (type & LARGE) 8662306a36Sopenharmony_ci digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 8762306a36Sopenharmony_ci if (type & LEFT) 8862306a36Sopenharmony_ci type &= ~ZEROPAD; 8962306a36Sopenharmony_ci if (base < 2 || base > 36) 9062306a36Sopenharmony_ci return 0; 9162306a36Sopenharmony_ci c = (type & ZEROPAD) ? '0' : ' '; 9262306a36Sopenharmony_ci sign = 0; 9362306a36Sopenharmony_ci if (type & SIGN) { 9462306a36Sopenharmony_ci if ((signed long long)num < 0) { 9562306a36Sopenharmony_ci sign = '-'; 9662306a36Sopenharmony_ci num = - (signed long long)num; 9762306a36Sopenharmony_ci size--; 9862306a36Sopenharmony_ci } else if (type & PLUS) { 9962306a36Sopenharmony_ci sign = '+'; 10062306a36Sopenharmony_ci size--; 10162306a36Sopenharmony_ci } else if (type & SPACE) { 10262306a36Sopenharmony_ci sign = ' '; 10362306a36Sopenharmony_ci size--; 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci if (type & SPECIAL) { 10762306a36Sopenharmony_ci if (base == 16) 10862306a36Sopenharmony_ci size -= 2; 10962306a36Sopenharmony_ci else if (base == 8) 11062306a36Sopenharmony_ci size--; 11162306a36Sopenharmony_ci } 11262306a36Sopenharmony_ci i = 0; 11362306a36Sopenharmony_ci if (num == 0) 11462306a36Sopenharmony_ci tmp[i++]='0'; 11562306a36Sopenharmony_ci else while (num != 0) { 11662306a36Sopenharmony_ci tmp[i++] = digits[do_div(num, base)]; 11762306a36Sopenharmony_ci } 11862306a36Sopenharmony_ci if (i > precision) 11962306a36Sopenharmony_ci precision = i; 12062306a36Sopenharmony_ci size -= precision; 12162306a36Sopenharmony_ci if (!(type&(ZEROPAD+LEFT))) 12262306a36Sopenharmony_ci while(size-->0) 12362306a36Sopenharmony_ci *str++ = ' '; 12462306a36Sopenharmony_ci if (sign) 12562306a36Sopenharmony_ci *str++ = sign; 12662306a36Sopenharmony_ci if (type & SPECIAL) { 12762306a36Sopenharmony_ci if (base==8) 12862306a36Sopenharmony_ci *str++ = '0'; 12962306a36Sopenharmony_ci else if (base==16) { 13062306a36Sopenharmony_ci *str++ = '0'; 13162306a36Sopenharmony_ci *str++ = digits[33]; 13262306a36Sopenharmony_ci } 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci if (!(type & LEFT)) 13562306a36Sopenharmony_ci while (size-- > 0) 13662306a36Sopenharmony_ci *str++ = c; 13762306a36Sopenharmony_ci while (i < precision--) 13862306a36Sopenharmony_ci *str++ = '0'; 13962306a36Sopenharmony_ci while (i-- > 0) 14062306a36Sopenharmony_ci *str++ = tmp[i]; 14162306a36Sopenharmony_ci while (size-- > 0) 14262306a36Sopenharmony_ci *str++ = ' '; 14362306a36Sopenharmony_ci return str; 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ciint vsprintf(char *buf, const char *fmt, va_list args) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci int len; 14962306a36Sopenharmony_ci unsigned long long num; 15062306a36Sopenharmony_ci int i, base; 15162306a36Sopenharmony_ci char * str; 15262306a36Sopenharmony_ci const char *s; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci int flags; /* flags to number() */ 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci int field_width; /* width of output field */ 15762306a36Sopenharmony_ci int precision; /* min. # of digits for integers; max 15862306a36Sopenharmony_ci number of chars for from string */ 15962306a36Sopenharmony_ci int qualifier; /* 'h', 'l', or 'L' for integer fields */ 16062306a36Sopenharmony_ci /* 'z' support added 23/7/1999 S.H. */ 16162306a36Sopenharmony_ci /* 'z' changed to 'Z' --davidm 1/25/99 */ 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci for (str=buf ; *fmt ; ++fmt) { 16562306a36Sopenharmony_ci if (*fmt != '%') { 16662306a36Sopenharmony_ci *str++ = *fmt; 16762306a36Sopenharmony_ci continue; 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci /* process flags */ 17162306a36Sopenharmony_ci flags = 0; 17262306a36Sopenharmony_ci repeat: 17362306a36Sopenharmony_ci ++fmt; /* this also skips first '%' */ 17462306a36Sopenharmony_ci switch (*fmt) { 17562306a36Sopenharmony_ci case '-': flags |= LEFT; goto repeat; 17662306a36Sopenharmony_ci case '+': flags |= PLUS; goto repeat; 17762306a36Sopenharmony_ci case ' ': flags |= SPACE; goto repeat; 17862306a36Sopenharmony_ci case '#': flags |= SPECIAL; goto repeat; 17962306a36Sopenharmony_ci case '0': flags |= ZEROPAD; goto repeat; 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci /* get field width */ 18362306a36Sopenharmony_ci field_width = -1; 18462306a36Sopenharmony_ci if ('0' <= *fmt && *fmt <= '9') 18562306a36Sopenharmony_ci field_width = skip_atoi(&fmt); 18662306a36Sopenharmony_ci else if (*fmt == '*') { 18762306a36Sopenharmony_ci ++fmt; 18862306a36Sopenharmony_ci /* it's the next argument */ 18962306a36Sopenharmony_ci field_width = va_arg(args, int); 19062306a36Sopenharmony_ci if (field_width < 0) { 19162306a36Sopenharmony_ci field_width = -field_width; 19262306a36Sopenharmony_ci flags |= LEFT; 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci /* get the precision */ 19762306a36Sopenharmony_ci precision = -1; 19862306a36Sopenharmony_ci if (*fmt == '.') { 19962306a36Sopenharmony_ci ++fmt; 20062306a36Sopenharmony_ci if ('0' <= *fmt && *fmt <= '9') 20162306a36Sopenharmony_ci precision = skip_atoi(&fmt); 20262306a36Sopenharmony_ci else if (*fmt == '*') { 20362306a36Sopenharmony_ci ++fmt; 20462306a36Sopenharmony_ci /* it's the next argument */ 20562306a36Sopenharmony_ci precision = va_arg(args, int); 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci if (precision < 0) 20862306a36Sopenharmony_ci precision = 0; 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci /* get the conversion qualifier */ 21262306a36Sopenharmony_ci qualifier = -1; 21362306a36Sopenharmony_ci if (*fmt == 'l' && *(fmt + 1) == 'l') { 21462306a36Sopenharmony_ci qualifier = 'q'; 21562306a36Sopenharmony_ci fmt += 2; 21662306a36Sopenharmony_ci } else if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' 21762306a36Sopenharmony_ci || *fmt == 'Z') { 21862306a36Sopenharmony_ci qualifier = *fmt; 21962306a36Sopenharmony_ci ++fmt; 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci /* default base */ 22362306a36Sopenharmony_ci base = 10; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci switch (*fmt) { 22662306a36Sopenharmony_ci case 'c': 22762306a36Sopenharmony_ci if (!(flags & LEFT)) 22862306a36Sopenharmony_ci while (--field_width > 0) 22962306a36Sopenharmony_ci *str++ = ' '; 23062306a36Sopenharmony_ci *str++ = (unsigned char) va_arg(args, int); 23162306a36Sopenharmony_ci while (--field_width > 0) 23262306a36Sopenharmony_ci *str++ = ' '; 23362306a36Sopenharmony_ci continue; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci case 's': 23662306a36Sopenharmony_ci s = va_arg(args, char *); 23762306a36Sopenharmony_ci if (!s) 23862306a36Sopenharmony_ci s = "<NULL>"; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci len = strnlen(s, precision); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci if (!(flags & LEFT)) 24362306a36Sopenharmony_ci while (len < field_width--) 24462306a36Sopenharmony_ci *str++ = ' '; 24562306a36Sopenharmony_ci for (i = 0; i < len; ++i) 24662306a36Sopenharmony_ci *str++ = *s++; 24762306a36Sopenharmony_ci while (len < field_width--) 24862306a36Sopenharmony_ci *str++ = ' '; 24962306a36Sopenharmony_ci continue; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci case 'p': 25262306a36Sopenharmony_ci if (field_width == -1) { 25362306a36Sopenharmony_ci field_width = 2*sizeof(void *); 25462306a36Sopenharmony_ci flags |= ZEROPAD; 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci str = number(str, 25762306a36Sopenharmony_ci (unsigned long) va_arg(args, void *), 16, 25862306a36Sopenharmony_ci field_width, precision, flags); 25962306a36Sopenharmony_ci continue; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci case 'n': 26362306a36Sopenharmony_ci if (qualifier == 'l') { 26462306a36Sopenharmony_ci long * ip = va_arg(args, long *); 26562306a36Sopenharmony_ci *ip = (str - buf); 26662306a36Sopenharmony_ci } else if (qualifier == 'Z') { 26762306a36Sopenharmony_ci size_t * ip = va_arg(args, size_t *); 26862306a36Sopenharmony_ci *ip = (str - buf); 26962306a36Sopenharmony_ci } else { 27062306a36Sopenharmony_ci int * ip = va_arg(args, int *); 27162306a36Sopenharmony_ci *ip = (str - buf); 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci continue; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci case '%': 27662306a36Sopenharmony_ci *str++ = '%'; 27762306a36Sopenharmony_ci continue; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci /* integer number formats - set up the flags and "break" */ 28062306a36Sopenharmony_ci case 'o': 28162306a36Sopenharmony_ci base = 8; 28262306a36Sopenharmony_ci break; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci case 'X': 28562306a36Sopenharmony_ci flags |= LARGE; 28662306a36Sopenharmony_ci case 'x': 28762306a36Sopenharmony_ci base = 16; 28862306a36Sopenharmony_ci break; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci case 'd': 29162306a36Sopenharmony_ci case 'i': 29262306a36Sopenharmony_ci flags |= SIGN; 29362306a36Sopenharmony_ci case 'u': 29462306a36Sopenharmony_ci break; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci default: 29762306a36Sopenharmony_ci *str++ = '%'; 29862306a36Sopenharmony_ci if (*fmt) 29962306a36Sopenharmony_ci *str++ = *fmt; 30062306a36Sopenharmony_ci else 30162306a36Sopenharmony_ci --fmt; 30262306a36Sopenharmony_ci continue; 30362306a36Sopenharmony_ci } 30462306a36Sopenharmony_ci if (qualifier == 'l') { 30562306a36Sopenharmony_ci num = va_arg(args, unsigned long); 30662306a36Sopenharmony_ci if (flags & SIGN) 30762306a36Sopenharmony_ci num = (signed long) num; 30862306a36Sopenharmony_ci } else if (qualifier == 'q') { 30962306a36Sopenharmony_ci num = va_arg(args, unsigned long long); 31062306a36Sopenharmony_ci if (flags & SIGN) 31162306a36Sopenharmony_ci num = (signed long long) num; 31262306a36Sopenharmony_ci } else if (qualifier == 'Z') { 31362306a36Sopenharmony_ci num = va_arg(args, size_t); 31462306a36Sopenharmony_ci } else if (qualifier == 'h') { 31562306a36Sopenharmony_ci num = (unsigned short) va_arg(args, int); 31662306a36Sopenharmony_ci if (flags & SIGN) 31762306a36Sopenharmony_ci num = (signed short) num; 31862306a36Sopenharmony_ci } else { 31962306a36Sopenharmony_ci num = va_arg(args, unsigned int); 32062306a36Sopenharmony_ci if (flags & SIGN) 32162306a36Sopenharmony_ci num = (signed int) num; 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci str = number(str, num, base, field_width, precision, flags); 32462306a36Sopenharmony_ci } 32562306a36Sopenharmony_ci *str = '\0'; 32662306a36Sopenharmony_ci return str-buf; 32762306a36Sopenharmony_ci} 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ciint sprintf(char * buf, const char *fmt, ...) 33062306a36Sopenharmony_ci{ 33162306a36Sopenharmony_ci va_list args; 33262306a36Sopenharmony_ci int i; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci va_start(args, fmt); 33562306a36Sopenharmony_ci i=vsprintf(buf,fmt,args); 33662306a36Sopenharmony_ci va_end(args); 33762306a36Sopenharmony_ci return i; 33862306a36Sopenharmony_ci} 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_cistatic char sprint_buf[1024]; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ciint 34362306a36Sopenharmony_ciprintf(const char *fmt, ...) 34462306a36Sopenharmony_ci{ 34562306a36Sopenharmony_ci va_list args; 34662306a36Sopenharmony_ci int n; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci va_start(args, fmt); 34962306a36Sopenharmony_ci n = vsprintf(sprint_buf, fmt, args); 35062306a36Sopenharmony_ci va_end(args); 35162306a36Sopenharmony_ci if (console_ops.write) 35262306a36Sopenharmony_ci console_ops.write(sprint_buf, n); 35362306a36Sopenharmony_ci return n; 35462306a36Sopenharmony_ci} 355