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