1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 28 /* All Rights Reserved */ 29 30 /* 31 * University Copyright- Copyright (c) 1982, 1986, 1988 32 * The Regents of the University of California 33 * All Rights Reserved 34 * 35 * University Acknowledgment- Portions of this document are derived from 36 * software developed by the University of California, Berkeley, and its 37 * contributors. 38 */ 39 40 /* The pwb version this is based on */ 41 static char *printf_id = "@(#) printf.c:2.2 6/5/79"; 42 /* The local sccs version within ex */ 43 44 #pragma ident "%Z%%M% %I% %E% SMI" 45 46 #include <stdarg.h> 47 #include <stdlib.h> 48 #include <limits.h> 49 extern short putoctal; 50 /* 51 * This version of printf is compatible with the Version 7 C 52 * printf. The differences are only minor except that 53 * viprintf assumes it is to print through putchar. Version 7 54 * printf is more general (and is much larger) and includes 55 * provisions for floating point. 56 */ 57 58 59 #define MAXOCT 11 /* Maximum octal digits in a long */ 60 #define MAXINT 32767 /* largest normal length positive integer */ 61 #define BIG 1000000000 /* largest power of 10 less than an unsigned long */ 62 #define MAXDIGS 10 /* number of digits in BIG */ 63 64 static int width, sign, fill; 65 66 unsigned char *_p_dconv(); 67 void _p_emit(unsigned char *, unsigned char *); 68 69 /*VARARGS*/ 70 void 71 viprintf(unsigned char *fmt, ...) 72 { 73 va_list ap; 74 unsigned char fcode; 75 wchar_t wfcode; 76 int prec; 77 int length,mask1,nbits,n; 78 int length2; 79 long int mask2, num; 80 unsigned char *bptr; 81 unsigned char *ptr; 82 unsigned char buf[134]; 83 84 va_start(ap, fmt); 85 for (;;) { 86 /* process format string first */ 87 for (;;) { 88 length2 = mbtowc(&wfcode, (char *)fmt, MB_LEN_MAX); 89 if(length2 <= 0) { 90 if (*fmt == '\0') { 91 fmt++; 92 return; 93 } 94 putchar(*fmt++); 95 } else { 96 if (wfcode == '%') { 97 fmt++; 98 break; 99 } 100 /* ordinary (non-%) character */ 101 putchar(wfcode); 102 fmt += length2; 103 } 104 } 105 /* length modifier: -1 for h, 1 for l, 0 for none */ 106 length = 0; 107 /* check for a leading - sign */ 108 sign = 0; 109 if (*fmt == '-') { 110 sign++; 111 fmt++; 112 } 113 /* a '0' may follow the - sign */ 114 /* this is the requested fill character */ 115 fill = 1; 116 if (*fmt == '0') { 117 fill--; 118 fmt++; 119 } 120 121 /* Now comes a digit string which may be a '*' */ 122 if (*fmt == '*') { 123 width = va_arg(ap, int); 124 if (width < 0) { 125 width = -width; 126 sign = !sign; 127 } 128 fmt++; 129 } 130 else { 131 width = 0; 132 while (*fmt>='0' && *fmt<='9') 133 width = width * 10 + (*fmt++ - '0'); 134 } 135 136 /* maybe a decimal point followed by more digits (or '*') */ 137 if (*fmt=='.') { 138 if (*++fmt == '*') { 139 prec = va_arg(ap, int); 140 fmt++; 141 } 142 else { 143 prec = 0; 144 while (*fmt>='0' && *fmt<='9') 145 prec = prec * 10 + (*fmt++ - '0'); 146 } 147 } 148 else 149 prec = -1; 150 151 /* 152 * At this point, "sign" is nonzero if there was 153 * a sign, "fill" is 0 if there was a leading 154 * zero and 1 otherwise, "width" and "prec" 155 * contain numbers corresponding to the digit 156 * strings before and after the decimal point, 157 * respectively, and "fmt" addresses the next 158 * character after the whole mess. If there was 159 * no decimal point, "prec" will be -1. 160 */ 161 switch (*fmt) { 162 case 'L': 163 case 'l': 164 length = 2; 165 /* no break!! */ 166 case 'h': 167 case 'H': 168 length--; 169 fmt++; 170 break; 171 } 172 173 /* 174 * At exit from the following switch, we will 175 * emit the characters starting at "bptr" and 176 * ending at "ptr"-1, unless fcode is '\0'. 177 */ 178 switch (fcode = *fmt++) { 179 /* process characters and strings first */ 180 case 'c': 181 buf[0] = va_arg(ap, int); 182 ptr = bptr = &buf[0]; 183 if (buf[0] != '\0') 184 ptr++; 185 break; 186 case 's': 187 bptr = va_arg(ap,unsigned char *); 188 if (bptr==0) 189 bptr = (unsigned char *)"(null pointer)"; 190 if (prec < 0) 191 prec = MAXINT; 192 for (n=0; *bptr++ && n < prec; n++) ; 193 ptr = --bptr; 194 bptr -= n; 195 break; 196 case 'O': 197 length = 1; 198 fcode = 'o'; 199 /* no break */ 200 case 'o': 201 case 'X': 202 case 'x': 203 if (length > 0) 204 num = va_arg(ap,long); 205 else 206 num = (unsigned)va_arg(ap,int); 207 if (fcode=='o') { 208 mask1 = 0x7; 209 mask2 = 0x1fffffffL; 210 nbits = 3; 211 } 212 else { 213 mask1 = 0xf; 214 mask2 = 0x0fffffffL; 215 nbits = 4; 216 } 217 n = (num!=0); 218 bptr = buf + MAXOCT + 3; 219 /* shift and mask for speed */ 220 do 221 if (((int) num & mask1) < 10) 222 *--bptr = ((int) num & mask1) + 060; 223 else 224 *--bptr = ((int) num & mask1) + 0127; 225 while (num = (num >> nbits) & mask2); 226 227 if (fcode=='o') { 228 if (n) 229 *--bptr = '0'; 230 } 231 else 232 if (!sign && fill <= 0) { 233 putchar('0'); 234 putchar(fcode); 235 width -= 2; 236 } 237 else { 238 *--bptr = fcode; 239 *--bptr = '0'; 240 } 241 ptr = buf + MAXOCT + 3; 242 break; 243 case 'D': 244 case 'U': 245 case 'I': 246 length = 1; 247 fcode = fcode + 'a' - 'A'; 248 /* no break */ 249 case 'd': 250 case 'i': 251 case 'u': 252 if (length > 0) 253 num = va_arg(ap,long); 254 else { 255 n = va_arg(ap,int); 256 if (fcode=='u') 257 num = (unsigned) n; 258 else 259 num = (long) n; 260 } 261 if (n = (fcode != 'u' && num < 0)) 262 num = -num; 263 /* now convert to digits */ 264 bptr = _p_dconv(num, buf); 265 if (n) 266 *--bptr = '-'; 267 if (fill == 0) 268 fill = -1; 269 ptr = buf + MAXDIGS + 1; 270 break; 271 default: 272 /* not a control character, 273 * print it. 274 */ 275 ptr = bptr = &fcode; 276 ptr++; 277 break; 278 } 279 if (fcode != '\0') 280 _p_emit(bptr,ptr); 281 } 282 va_end(ap); 283 } 284 285 /* _p_dconv converts the unsigned long integer "value" to 286 * printable decimal and places it in "buffer", right-justified. 287 * The value returned is the address of the first non-zero character, 288 * or the address of the last character if all are zero. 289 * The result is NOT null terminated, and is MAXDIGS characters long, 290 * starting at buffer[1] (to allow for insertion of a sign). 291 * 292 * This program assumes it is running on 2's complement machine 293 * with reasonable overflow treatment. 294 */ 295 unsigned char * 296 _p_dconv(value, buffer) 297 long value; 298 unsigned char *buffer; 299 { 300 unsigned char *bp; 301 int svalue; 302 int n; 303 long lval; 304 305 bp = buffer; 306 307 /* zero is a special case */ 308 if (value == 0) { 309 bp += MAXDIGS; 310 *bp = '0'; 311 return(bp); 312 } 313 314 /* develop the leading digit of the value in "n" */ 315 n = 0; 316 while (value < 0) { 317 value -= BIG; /* will eventually underflow */ 318 n++; 319 } 320 while ((lval = value - BIG) >= 0) { 321 value = lval; 322 n++; 323 } 324 325 /* stash it in buffer[1] to allow for a sign */ 326 bp[1] = n + '0'; 327 /* 328 * Now develop the rest of the digits. Since speed counts here, 329 * we do it in two loops. The first gets "value" down until it 330 * is no larger than MAXINT. The second one uses integer divides 331 * rather than long divides to speed it up. 332 */ 333 bp += MAXDIGS + 1; 334 while (value > MAXINT) { 335 *--bp = (int)(value % 10) + '0'; 336 value /= 10; 337 } 338 339 /* cannot lose precision */ 340 svalue = value; 341 while (svalue > 0) { 342 *--bp = (svalue % 10) + '0'; 343 svalue /= 10; 344 } 345 346 /* fill in intermediate zeroes if needed */ 347 if (buffer[1] != '0') { 348 while (bp > buffer + 2) 349 *--bp = '0'; 350 --bp; 351 } 352 return(bp); 353 } 354 355 /* 356 * This program sends string "s" to putchar. The character after 357 * the end of "s" is given by "send". This allows the size of the 358 * field to be computed; it is stored in "alen". "width" contains the 359 * user specified length. If width<alen, the width will be taken to 360 * be alen. "sign" is zero if the string is to be right-justified 361 * in the field, nonzero if it is to be left-justified. "fill" is 362 * 0 if the string is to be padded with '0', positive if it is to be 363 * padded with ' ', and negative if an initial '-' should appear before 364 * any padding in right-justification (to avoid printing "-3" as 365 * "000-3" where "-0003" was intended). 366 */ 367 void 368 _p_emit(unsigned char *s, unsigned char *send) 369 { 370 unsigned char cfill; 371 int alen; 372 int npad, length; 373 wchar_t wchar; 374 375 alen = send - s; 376 if (alen > width) 377 width = alen; 378 cfill = fill>0? ' ': '0'; 379 380 /* we may want to print a leading '-' before anything */ 381 if (*s == '-' && fill < 0) { 382 putchar(*s++); 383 alen--; 384 width--; 385 } 386 npad = width - alen; 387 388 /* emit any leading pad characters */ 389 if (!sign) 390 while (--npad >= 0) 391 putchar(cfill); 392 393 /* emit the string itself */ 394 while (--alen >= 0) { 395 length = mbtowc(&wchar, (char *)s, MB_LEN_MAX); 396 if(length <= 0) { 397 putoctal = 1; 398 putchar((unsigned char)*s++); 399 putoctal = 0; 400 } else { 401 putchar(wchar); 402 s += length; 403 alen = alen - length + 1; 404 } 405 } 406 /* emit trailing pad characters */ 407 if (sign) 408 while (--npad >= 0) 409 putchar(cfill); 410 } 411