]>
Commit | Line | Data |
---|---|---|
1c79356b A |
1 | /* |
2 | * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * The contents of this file constitute Original Code as defined in and | |
7 | * are subject to the Apple Public Source License Version 1.1 (the | |
8 | * "License"). You may not use this file except in compliance with the | |
9 | * License. Please obtain a copy of the License at | |
10 | * http://www.apple.com/publicsource and read it before using this file. | |
11 | * | |
12 | * This Original Code and all software distributed under the License are | |
13 | * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
14 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
15 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
16 | * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the | |
17 | * License for the specific language governing rights and limitations | |
18 | * under the License. | |
19 | * | |
20 | * @APPLE_LICENSE_HEADER_END@ | |
21 | */ | |
22 | /* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */ | |
23 | /*- | |
24 | * Copyright (c) 1986, 1988, 1991, 1993 | |
25 | * The Regents of the University of California. All rights reserved. | |
26 | * (c) UNIX System Laboratories, Inc. | |
27 | * All or some portions of this file are derived from material licensed | |
28 | * to the University of California by American Telephone and Telegraph | |
29 | * Co. or Unix System Laboratories, Inc. and are reproduced herein with | |
30 | * the permission of UNIX System Laboratories, Inc. | |
31 | * | |
32 | * Redistribution and use in source and binary forms, with or without | |
33 | * modification, are permitted provided that the following conditions | |
34 | * are met: | |
35 | * 1. Redistributions of source code must retain the above copyright | |
36 | * notice, this list of conditions and the following disclaimer. | |
37 | * 2. Redistributions in binary form must reproduce the above copyright | |
38 | * notice, this list of conditions and the following disclaimer in the | |
39 | * documentation and/or other materials provided with the distribution. | |
40 | * 3. All advertising materials mentioning features or use of this software | |
41 | * must display the following acknowledgement: | |
42 | * This product includes software developed by the University of | |
43 | * California, Berkeley and its contributors. | |
44 | * 4. Neither the name of the University nor the names of its contributors | |
45 | * may be used to endorse or promote products derived from this software | |
46 | * without specific prior written permission. | |
47 | * | |
48 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
49 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
50 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
51 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
52 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
53 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
54 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
55 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
56 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
57 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
58 | * SUCH DAMAGE. | |
59 | * | |
60 | * @(#)subr_prf.c 8.4 (Berkeley) 5/4/95 | |
61 | */ | |
62 | /* HISTORY | |
63 | * 22-Sep-1997 Umesh Vaishampayan (umeshv@apple.com) | |
64 | * Cleaned up m68k crud. Fixed vlog() to do logpri() for ppc, too. | |
65 | * | |
66 | * 17-July-97 Umesh Vaishampayan (umeshv@apple.com) | |
67 | * Eliminated multiple definition of constty which is defined | |
68 | * in bsd/dev/XXX/cons.c | |
69 | * | |
70 | * 26-MAR-1997 Umesh Vaishampayan (umeshv@NeXT.com | |
71 | * Fixed tharshing format in many functions. Cleanup. | |
72 | * | |
73 | * 17-Jun-1995 Mac Gillon (mgillon) at NeXT | |
74 | * Purged old history | |
75 | * New version based on 4.4 and NS3.3 | |
76 | */ | |
77 | ||
78 | #include <sys/param.h> | |
79 | #include <sys/systm.h> | |
80 | #include <sys/buf.h> | |
81 | #include <sys/conf.h> | |
82 | #include <sys/reboot.h> | |
83 | #include <sys/msgbuf.h> | |
84 | #include <sys/proc.h> | |
85 | #include <sys/ioctl.h> | |
86 | #include <sys/tty.h> | |
87 | #include <sys/file.h> | |
88 | #include <sys/tprintf.h> | |
89 | #include <sys/syslog.h> | |
90 | #include <stdarg.h> | |
91 | #include <sys/malloc.h> | |
92 | #include <sys/lock.h> | |
93 | #include <kern/parallel.h> | |
94 | #include <sys/subr_prf.h> | |
95 | ||
96 | #include <kern/cpu_number.h> /* for cpu_number() */ | |
97 | #include <machine/spl.h> | |
98 | #include <libkern/libkern.h> | |
99 | ||
100 | struct snprintf_arg { | |
101 | char *str; | |
102 | size_t remain; | |
103 | }; | |
104 | ||
105 | ||
106 | /* | |
107 | * In case console is off, | |
108 | * panicstr contains argument to last | |
109 | * call to panic. | |
110 | */ | |
111 | extern const char *panicstr; | |
112 | ||
113 | extern cnputc(); /* standard console putc */ | |
114 | int (*v_putc)() = cnputc; /* routine to putc on virtual console */ | |
115 | ||
116 | extern struct tty cons; /* standard console tty */ | |
117 | extern struct tty *constty; /* pointer to console "window" tty */ | |
118 | ||
119 | /* | |
120 | * Record cpu that panic'd and lock around panic data | |
121 | */ | |
122 | ||
123 | static void puts(const char *s, int flags, struct tty *ttyp); | |
124 | static void printn(u_long n, int b, int flags, struct tty *ttyp, int zf, int fld_size); | |
125 | ||
126 | /* MP printf stuff */ | |
127 | decl_simple_lock_data(,printf_lock) | |
128 | #if NCPUS > 1 | |
129 | boolean_t new_printf_cpu_number; /* do we need to output who we are */ | |
130 | #endif | |
131 | ||
132 | extern void logwakeup(); | |
133 | extern void halt_cpu(); | |
134 | extern boot(); | |
135 | int putchar(); | |
136 | ||
137 | static void | |
138 | snprintf_func(int ch, void *arg); | |
139 | ||
140 | ||
141 | ||
142 | /* | |
143 | * Uprintf prints to the controlling terminal for the current process. | |
144 | * It may block if the tty queue is overfull. No message is printed if | |
145 | * the queue does not clear in a reasonable time. | |
146 | */ | |
147 | void | |
148 | uprintf(const char *fmt, ...) | |
149 | { | |
150 | register struct proc *p = current_proc(); | |
151 | va_list ap; | |
152 | ||
153 | unix_master(); /* sessions, sigh */ | |
154 | if (p->p_flag & P_CONTROLT && p->p_session->s_ttyvp) { | |
155 | va_start(ap, fmt); | |
156 | prf(fmt, ap, TOTTY, (struct tty *)p->p_session->s_ttyvp); | |
157 | va_end(ap); | |
158 | } | |
159 | unix_release(); | |
160 | } | |
161 | ||
162 | tpr_t | |
163 | tprintf_open(p) | |
164 | register struct proc *p; | |
165 | { | |
166 | unix_master(); /* sessions, sigh */ | |
167 | if (p->p_flag & P_CONTROLT && p->p_session->s_ttyvp) { | |
168 | SESSHOLD(p->p_session); | |
169 | unix_release(); | |
170 | return ((tpr_t) p->p_session); | |
171 | } | |
172 | unix_release(); | |
173 | return ((tpr_t) NULL); | |
174 | } | |
175 | ||
176 | void | |
177 | tprintf_close(sess) | |
178 | tpr_t sess; | |
179 | { | |
180 | unix_master(); /* sessions, sigh */ | |
181 | if (sess) | |
182 | SESSRELE((struct session *) sess); | |
183 | unix_release(); | |
184 | } | |
185 | ||
186 | /* | |
187 | * tprintf prints on the controlling terminal associated | |
188 | * with the given session. | |
189 | */ | |
190 | void | |
191 | tprintf(tpr_t tpr, const char *fmt, ...) | |
192 | { | |
193 | register struct session *sess = (struct session *)tpr; | |
194 | struct tty *tp = NULL; | |
195 | int flags = TOLOG; | |
196 | va_list ap; | |
197 | ||
198 | logpri(LOG_INFO); | |
199 | unix_master(); /* sessions, sigh */ | |
200 | if (sess && sess->s_ttyvp && ttycheckoutq(sess->s_ttyp, 0)) { | |
201 | flags |= TOTTY; | |
202 | tp = sess->s_ttyp; | |
203 | } | |
204 | if (tp != NULL) { | |
205 | va_start(ap, fmt); | |
206 | prf(fmt, ap, TOTTY, tp); | |
207 | va_end(ap); | |
208 | } | |
209 | unix_release(); | |
210 | logwakeup(); | |
211 | } | |
212 | ||
213 | /* | |
214 | * Ttyprintf displays a message on a tty; it should be used only by | |
215 | * the tty driver, or anything that knows the underlying tty will not | |
216 | * be revoke(2)'d away. Other callers should use tprintf. | |
217 | */ | |
218 | void | |
219 | ttyprintf(struct tty *tp, const char *fmt, ...) | |
220 | { | |
221 | va_list ap; | |
222 | ||
223 | if (tp != NULL) { | |
224 | va_start(ap, fmt); | |
225 | prf(fmt, ap, TOTTY, tp); | |
226 | va_end(ap); | |
227 | } | |
228 | } | |
229 | ||
230 | extern int log_open; | |
231 | ||
232 | ||
233 | void | |
234 | logpri(level) | |
235 | int level; | |
236 | { | |
237 | ||
238 | putchar('<', TOLOG, (struct tty *)0); | |
239 | printn((u_long)level, 10, TOLOG, (struct tty *)0, 0, 0); | |
240 | putchar('>', TOLOG, (struct tty *)0); | |
241 | } | |
242 | ||
243 | void | |
244 | addlog(const char *fmt, ...) | |
245 | { | |
246 | register s = splhigh(); | |
247 | va_list ap; | |
248 | ||
249 | va_start(ap, fmt); | |
250 | prf(fmt, ap, TOLOG, (struct tty *)0); | |
251 | splx(s); | |
252 | if (!log_open) | |
253 | prf(fmt, ap, TOCONS, (struct tty *)0); | |
254 | va_end(ap); | |
255 | logwakeup(); | |
256 | } | |
257 | void _printf(int flags, struct tty *ttyp, const char *format, ...) | |
258 | { | |
259 | va_list ap; | |
260 | ||
261 | va_start(ap, format); | |
262 | prf(format, ap, flags, ttyp); | |
263 | va_end(ap); | |
264 | } | |
265 | ||
266 | int prf(const char *fmt, va_list ap, int flags, struct tty *ttyp) | |
267 | { | |
268 | register int b, c, i; | |
269 | char *s; | |
270 | int any; | |
271 | int zf = 0, fld_size; | |
272 | ||
273 | #if NCPUS > 1 | |
274 | int cpun = cpu_number(); | |
275 | ||
276 | if(ttyp == 0) { | |
277 | simple_lock(&printf_lock); | |
278 | } else | |
279 | TTY_LOCK(ttyp); | |
280 | ||
281 | if (cpun != master_cpu) | |
282 | new_printf_cpu_number = TRUE; | |
283 | ||
284 | if (new_printf_cpu_number) { | |
285 | putchar('{', flags, ttyp); | |
286 | printn((u_long)cpun, 10, flags, ttyp, 0, 0); | |
287 | putchar('}', flags, ttyp); | |
288 | } | |
289 | #endif /* NCPUS > 1 */ | |
290 | loop: | |
291 | while ((c = *fmt++) != '%') { | |
292 | if (c == '\0') { | |
293 | #if NCPUS > 1 | |
294 | if(ttyp == 0) { | |
295 | simple_unlock(&printf_lock); | |
296 | } else | |
297 | TTY_UNLOCK(ttyp); | |
298 | #endif | |
299 | return 0; | |
300 | } | |
301 | putchar(c, flags, ttyp); | |
302 | } | |
303 | again: | |
304 | zf = 0; | |
305 | fld_size = 0; | |
306 | c = *fmt++; | |
307 | if (c == '0') | |
308 | zf = '0'; | |
309 | fld_size = 0; | |
310 | for (;c <= '9' && c >= '0'; c = *fmt++) | |
311 | fld_size = fld_size * 10 + c - '0'; | |
312 | ||
313 | /* THIS CODE IS VAX DEPENDENT IN HANDLING %l? AND %c */ | |
314 | switch (c) { | |
315 | ||
316 | case 'l': | |
317 | goto again; | |
318 | case 'x': case 'X': | |
319 | b = 16; | |
320 | goto number; | |
321 | case 'd': case 'D': | |
322 | case 'u': /* what a joke */ | |
323 | b = 10; | |
324 | goto number; | |
325 | case 'o': case 'O': | |
326 | b = 8; | |
327 | number: | |
328 | printn(va_arg(ap, unsigned), b, flags, ttyp, zf, fld_size); | |
329 | break; | |
330 | case 'c': | |
331 | b = va_arg(ap, unsigned); | |
332 | #if BYTE_ORDER == LITTLE_ENDIAN | |
333 | for (i = 24; i >= 0; i -= 8) | |
334 | if (c = (b >> i) & 0x7f) | |
335 | putchar(c, flags, ttyp); | |
336 | #endif | |
337 | #if BYTE_ORDER == BIG_ENDIAN | |
338 | if ((c = (b & 0x7f))) | |
339 | putchar(c, flags, ttyp); | |
340 | #endif | |
341 | break; | |
342 | case 'b': | |
343 | b = va_arg(ap, unsigned); | |
344 | s = va_arg(ap, char *); | |
345 | printn((u_long)b, *s++, flags, ttyp, 0, 0); | |
346 | any = 0; | |
347 | if (b) { | |
348 | while ((i = *s++)) { | |
349 | if (*s <= 32) { | |
350 | register int j; | |
351 | ||
352 | if (any++) | |
353 | putchar(',', flags, ttyp); | |
354 | j = *s++ ; | |
355 | for (; (c = *s) > 32 ; s++) | |
356 | putchar(c, flags, ttyp); | |
357 | printn( (u_long)( (b >> (j-1)) & | |
358 | ( (2 << (i-j)) -1)), | |
359 | 8, flags, ttyp, 0, 0); | |
360 | } else if (b & (1 << (i-1))) { | |
361 | putchar(any? ',' : '<', flags, ttyp); | |
362 | any = 1; | |
363 | for (; (c = *s) > 32; s++) | |
364 | putchar(c, flags, ttyp); | |
365 | } else | |
366 | for (; *s > 32; s++) | |
367 | ; | |
368 | } | |
369 | putchar('>', flags, ttyp); | |
370 | } | |
371 | break; | |
372 | ||
373 | case 's': | |
374 | s = va_arg(ap, char *); | |
375 | #ifdef DEBUG | |
376 | if (fld_size) { | |
377 | while (fld_size-- > 0) | |
378 | putchar((c = *s++)? c : '_', flags, ttyp); | |
379 | } else { | |
380 | while ((c = *s++)) | |
381 | putchar(c, flags, ttyp); | |
382 | } | |
383 | #else | |
384 | while (c = *s++) | |
385 | putchar(c, flags, ttyp); | |
386 | #endif | |
387 | break; | |
388 | ||
389 | case '%': | |
390 | putchar('%', flags, ttyp); | |
391 | goto loop; | |
392 | case 'C': | |
393 | b = va_arg(ap, unsigned); | |
394 | #if BYTE_ORDER == LITTLE_ENDIAN | |
395 | for (i = 24; i >= 0; i -= 8) | |
396 | if (c = (b >> i) & 0x7f) | |
397 | putchar(c, flags, ttyp); | |
398 | #endif | |
399 | #if BYTE_ORDER == BIG_ENDIAN | |
400 | if ((c = (b & 0x7f))) | |
401 | putchar(c, flags, ttyp); | |
402 | #endif | |
403 | ||
404 | case 'r': | |
405 | case 'R': | |
406 | b = va_arg(ap, unsigned); | |
407 | s = va_arg(ap, char *); | |
408 | if (c == 'R') { | |
409 | puts("0x", flags, ttyp); | |
410 | printn((u_long)b, 16, flags, ttyp, 0, 0); | |
411 | } | |
412 | any = 0; | |
413 | if (c == 'r' || b) { | |
414 | register struct reg_desc *rd; | |
415 | register struct reg_values *rv; | |
416 | unsigned field; | |
417 | ||
418 | putchar('<', flags, ttyp); | |
419 | for (rd = (struct reg_desc *)s; rd->rd_mask; rd++) { | |
420 | field = b & rd->rd_mask; | |
421 | field = (rd->rd_shift > 0) | |
422 | ? field << rd->rd_shift | |
423 | : field >> -rd->rd_shift; | |
424 | if (any && | |
425 | (rd->rd_format || rd->rd_values | |
426 | || (rd->rd_name && field) | |
427 | ) | |
428 | ) | |
429 | putchar(',', flags, ttyp); | |
430 | if (rd->rd_name) { | |
431 | if (rd->rd_format || rd->rd_values | |
432 | || field) { | |
433 | puts(rd->rd_name, flags, ttyp); | |
434 | any = 1; | |
435 | } | |
436 | if (rd->rd_format || rd->rd_values) { | |
437 | putchar('=', flags, ttyp); | |
438 | any = 1; | |
439 | } | |
440 | } | |
441 | if (rd->rd_format) { | |
442 | _printf(flags, ttyp, rd->rd_format, | |
443 | field); | |
444 | any = 1; | |
445 | if (rd->rd_values) | |
446 | putchar(':', flags, ttyp); | |
447 | } | |
448 | if (rd->rd_values) { | |
449 | any = 1; | |
450 | for (rv = rd->rd_values; | |
451 | rv->rv_name; | |
452 | rv++) { | |
453 | if (field == rv->rv_value) { | |
454 | puts(rv->rv_name, flags, | |
455 | ttyp); | |
456 | break; | |
457 | } | |
458 | } | |
459 | if (rv->rv_name == NULL) | |
460 | puts("???", flags, ttyp); | |
461 | } | |
462 | } | |
463 | putchar('>', flags, ttyp); | |
464 | } | |
465 | break; | |
466 | ||
467 | case 'n': | |
468 | case 'N': | |
469 | { | |
470 | register struct reg_values *rv; | |
471 | ||
472 | b = va_arg(ap, unsigned); | |
473 | s = va_arg(ap,char *); | |
474 | for (rv = (struct reg_values *)s; rv->rv_name; rv++) { | |
475 | if (b == rv->rv_value) { | |
476 | puts(rv->rv_name, flags, ttyp); | |
477 | break; | |
478 | } | |
479 | } | |
480 | if (rv->rv_name == NULL) | |
481 | puts("???", flags, ttyp); | |
482 | if (c == 'N' || rv->rv_name == NULL) { | |
483 | putchar(':', flags, ttyp); | |
484 | printn((u_long)b, 10, flags, ttyp, 0, 0); | |
485 | } | |
486 | } | |
487 | break; | |
488 | } | |
489 | goto loop; | |
490 | } | |
491 | ||
492 | static void puts(const char *s, int flags, struct tty *ttyp) | |
493 | { | |
494 | register char c; | |
495 | ||
496 | while ((c = *s++)) | |
497 | putchar(c, flags, ttyp); | |
498 | } | |
499 | ||
500 | /* | |
501 | * Printn prints a number n in base b. | |
502 | * We don't use recursion to avoid deep kernel stacks. | |
503 | */ | |
504 | static void printn(u_long n, int b, int flags, struct tty *ttyp, int zf, int fld_size) | |
505 | { | |
506 | char prbuf[11]; | |
507 | register char *cp; | |
508 | ||
509 | if (b == 10 && (int)n < 0) { | |
510 | putchar('-', flags, ttyp); | |
511 | n = (unsigned)(-(int)n); | |
512 | } | |
513 | cp = prbuf; | |
514 | do { | |
515 | *cp++ = "0123456789abcdef"[n%b]; | |
516 | n /= b; | |
517 | } while (n); | |
518 | if (fld_size) { | |
519 | for (fld_size -= cp - prbuf; fld_size > 0; fld_size--) | |
520 | if (zf) | |
521 | putchar('0', flags, ttyp); | |
522 | else | |
523 | putchar(' ', flags, ttyp); | |
524 | } | |
525 | do | |
526 | putchar(*--cp, flags, ttyp); | |
527 | while (cp > prbuf); | |
528 | } | |
529 | ||
530 | ||
531 | ||
532 | /* | |
533 | * Warn that a system table is full. | |
534 | */ | |
535 | void tablefull(const char *tab) | |
536 | { | |
537 | log(LOG_ERR, "%s: table is full\n", tab); | |
538 | } | |
539 | ||
540 | /* | |
541 | * Print a character on console or users terminal. | |
542 | * If destination is console then the last MSGBUFS characters | |
543 | * are saved in msgbuf for inspection later. | |
544 | */ | |
545 | /*ARGSUSED*/ | |
546 | int | |
547 | putchar(c, flags, tp) | |
548 | register int c; | |
549 | struct tty *tp; | |
550 | { | |
551 | register struct msgbuf *mbp; | |
552 | char **sp = (char**) tp; | |
553 | ||
554 | if (panicstr) | |
555 | constty = 0; | |
556 | if ((flags & TOCONS) && tp == NULL && constty) { | |
557 | tp = constty; | |
558 | flags |= TOTTY; | |
559 | } | |
560 | if ((flags & TOTTY) && tp && tputchar(c, tp) < 0 && | |
561 | (flags & TOCONS) && tp == constty) | |
562 | constty = 0; | |
563 | if ((flags & TOLOG) && c != '\0' && c != '\r' && c != 0177) | |
564 | log_putc(c); | |
565 | if ((flags & TOCONS) && constty == 0 && c != '\0') | |
566 | (*v_putc)(c); | |
567 | if (flags & TOSTR) { | |
568 | **sp = c; | |
569 | (*sp)++; | |
570 | } | |
571 | return 0; | |
572 | } | |
573 | ||
574 | ||
575 | ||
576 | /* | |
577 | * Scaled down version of vsprintf(3). | |
578 | */ | |
579 | int | |
580 | vsprintf(char *buf, const char *cfmt, va_list ap) | |
581 | { | |
582 | int retval; | |
583 | ||
584 | retval = kvprintf(cfmt, NULL, (void *)buf, 10, ap); | |
585 | buf[retval] = '\0'; | |
586 | return retval; | |
587 | } | |
588 | ||
589 | /* | |
590 | * Scaled down version of snprintf(3). | |
591 | */ | |
592 | int | |
593 | snprintf(char *str, size_t size, const char *format, ...) | |
594 | { | |
595 | int retval; | |
596 | va_list ap; | |
597 | ||
598 | va_start(ap, format); | |
599 | retval = vsnprintf(str, size, format, ap); | |
600 | va_end(ap); | |
601 | return(retval); | |
602 | } | |
603 | ||
604 | /* | |
605 | * Scaled down version of vsnprintf(3). | |
606 | */ | |
607 | int | |
608 | vsnprintf(char *str, size_t size, const char *format, va_list ap) | |
609 | { | |
610 | struct snprintf_arg info; | |
611 | int retval; | |
612 | ||
613 | info.str = str; | |
614 | info.remain = size; | |
615 | retval = kvprintf(format, snprintf_func, &info, 10, ap); | |
616 | if (info.remain >= 1) | |
617 | *info.str++ = '\0'; | |
618 | return retval; | |
619 | } | |
620 | ||
621 | static void | |
622 | snprintf_func(int ch, void *arg) | |
623 | { | |
624 | struct snprintf_arg *const info = arg; | |
625 | ||
626 | if (info->remain >= 2) { | |
627 | *info->str++ = ch; | |
628 | info->remain--; | |
629 | } | |
630 | } | |
631 | ||
632 | /* | |
633 | * Put a number (base <= 16) in a buffer in reverse order; return an | |
634 | * optional length and a pointer to the NULL terminated (preceded?) | |
635 | * buffer. | |
636 | */ | |
637 | static char * | |
638 | ksprintn(ul, base, lenp) | |
639 | register u_long ul; | |
640 | register int base, *lenp; | |
641 | { /* A long in base 8, plus NULL. */ | |
642 | static char buf[sizeof(long) * NBBY / 3 + 2]; | |
643 | register char *p; | |
644 | ||
645 | p = buf; | |
646 | do { | |
647 | *++p = hex2ascii(ul % base); | |
648 | } while (ul /= base); | |
649 | if (lenp) | |
650 | *lenp = p - buf; | |
651 | return (p); | |
652 | } | |
653 | ||
654 | /* | |
655 | * Scaled down version of printf(3). | |
656 | * | |
657 | * Two additional formats: | |
658 | * | |
659 | * The format %b is supported to decode error registers. | |
660 | * Its usage is: | |
661 | * | |
662 | * printf("reg=%b\n", regval, "<base><arg>*"); | |
663 | * | |
664 | * where <base> is the output base expressed as a control character, e.g. | |
665 | * \10 gives octal; \20 gives hex. Each arg is a sequence of characters, | |
666 | * the first of which gives the bit number to be inspected (origin 1), and | |
667 | * the next characters (up to a control character, i.e. a character <= 32), | |
668 | * give the name of the register. Thus: | |
669 | * | |
670 | * kvprintf("reg=%b\n", 3, "\10\2BITTWO\1BITONE\n"); | |
671 | * | |
672 | * would produce output: | |
673 | * | |
674 | * reg=3<BITTWO,BITONE> | |
675 | * | |
676 | * XXX: %D -- Hexdump, takes pointer and separator string: | |
677 | * ("%6D", ptr, ":") -> XX:XX:XX:XX:XX:XX | |
678 | * ("%*D", len, ptr, " " -> XX XX XX XX ... | |
679 | */ | |
680 | int | |
681 | kvprintf(char const *fmt, void (*func)(int, void*), void *arg, int radix, va_list ap) | |
682 | { | |
683 | #define PCHAR(c) {int cc=(c); if (func) (*func)(cc,arg); else *d++ = cc; retval++; } | |
684 | char *p, *q, *d; | |
685 | u_char *up; | |
686 | int ch, n; | |
687 | u_long ul; | |
688 | int base, lflag, tmp, width, ladjust, sharpflag, neg, sign, dot; | |
689 | int dwidth; | |
690 | char padc; | |
691 | int retval = 0; | |
692 | ||
693 | if (!func) | |
694 | d = (char *) arg; | |
695 | else | |
696 | d = NULL; | |
697 | ||
698 | if (fmt == NULL) | |
699 | fmt = "(fmt null)\n"; | |
700 | ||
701 | if (radix < 2 || radix > 36) | |
702 | radix = 10; | |
703 | ||
704 | for (;;) { | |
705 | padc = ' '; | |
706 | width = 0; | |
707 | while ((ch = (u_char)*fmt++) != '%') { | |
708 | if (ch == '\0') | |
709 | return retval; | |
710 | PCHAR(ch); | |
711 | } | |
712 | lflag = 0; ladjust = 0; sharpflag = 0; neg = 0; | |
713 | sign = 0; dot = 0; dwidth = 0; | |
714 | reswitch: switch (ch = (u_char)*fmt++) { | |
715 | case '.': | |
716 | dot = 1; | |
717 | goto reswitch; | |
718 | case '#': | |
719 | sharpflag = 1; | |
720 | goto reswitch; | |
721 | case '+': | |
722 | sign = 1; | |
723 | goto reswitch; | |
724 | case '-': | |
725 | ladjust = 1; | |
726 | goto reswitch; | |
727 | case '%': | |
728 | PCHAR(ch); | |
729 | break; | |
730 | case '*': | |
731 | if (!dot) { | |
732 | width = va_arg(ap, int); | |
733 | if (width < 0) { | |
734 | ladjust = !ladjust; | |
735 | width = -width; | |
736 | } | |
737 | } else { | |
738 | dwidth = va_arg(ap, int); | |
739 | } | |
740 | goto reswitch; | |
741 | case '0': | |
742 | if (!dot) { | |
743 | padc = '0'; | |
744 | goto reswitch; | |
745 | } | |
746 | case '1': case '2': case '3': case '4': | |
747 | case '5': case '6': case '7': case '8': case '9': | |
748 | for (n = 0;; ++fmt) { | |
749 | n = n * 10 + ch - '0'; | |
750 | ch = *fmt; | |
751 | if (ch < '0' || ch > '9') | |
752 | break; | |
753 | } | |
754 | if (dot) | |
755 | dwidth = n; | |
756 | else | |
757 | width = n; | |
758 | goto reswitch; | |
759 | case 'b': | |
760 | ul = va_arg(ap, int); | |
761 | p = va_arg(ap, char *); | |
762 | for (q = ksprintn(ul, *p++, NULL); *q;) | |
763 | PCHAR(*q--); | |
764 | ||
765 | if (!ul) | |
766 | break; | |
767 | ||
768 | for (tmp = 0; *p;) { | |
769 | n = *p++; | |
770 | if (ul & (1 << (n - 1))) { | |
771 | PCHAR(tmp ? ',' : '<'); | |
772 | for (; (n = *p) > ' '; ++p) | |
773 | PCHAR(n); | |
774 | tmp = 1; | |
775 | } else | |
776 | for (; *p > ' '; ++p) | |
777 | continue; | |
778 | } | |
779 | if (tmp) | |
780 | PCHAR('>'); | |
781 | break; | |
782 | case 'c': | |
783 | PCHAR(va_arg(ap, int)); | |
784 | break; | |
785 | case 'D': | |
786 | up = va_arg(ap, u_char *); | |
787 | p = va_arg(ap, char *); | |
788 | if (!width) | |
789 | width = 16; | |
790 | while(width--) { | |
791 | PCHAR(hex2ascii(*up >> 4)); | |
792 | PCHAR(hex2ascii(*up & 0x0f)); | |
793 | up++; | |
794 | if (width) | |
795 | for (q=p;*q;q++) | |
796 | PCHAR(*q); | |
797 | } | |
798 | break; | |
799 | case 'd': | |
800 | ul = lflag ? va_arg(ap, long) : va_arg(ap, int); | |
801 | sign = 1; | |
802 | base = 10; | |
803 | goto number; | |
804 | case 'l': | |
805 | lflag = 1; | |
806 | goto reswitch; | |
807 | case 'o': | |
808 | ul = lflag ? va_arg(ap, u_long) : va_arg(ap, u_int); | |
809 | base = 8; | |
810 | goto nosign; | |
811 | case 'p': | |
812 | ul = (uintptr_t)va_arg(ap, void *); | |
813 | base = 16; | |
814 | sharpflag = (width == 0); | |
815 | goto nosign; | |
816 | case 'n': | |
817 | case 'r': | |
818 | ul = lflag ? va_arg(ap, u_long) : | |
819 | sign ? (u_long)va_arg(ap, int) : va_arg(ap, u_int); | |
820 | base = radix; | |
821 | goto number; | |
822 | case 's': | |
823 | p = va_arg(ap, char *); | |
824 | if (p == NULL) | |
825 | p = "(null)"; | |
826 | if (!dot) | |
827 | n = strlen (p); | |
828 | else | |
829 | for (n = 0; n < dwidth && p[n]; n++) | |
830 | continue; | |
831 | ||
832 | width -= n; | |
833 | ||
834 | if (!ladjust && width > 0) | |
835 | while (width--) | |
836 | PCHAR(padc); | |
837 | while (n--) | |
838 | PCHAR(*p++); | |
839 | if (ladjust && width > 0) | |
840 | while (width--) | |
841 | PCHAR(padc); | |
842 | break; | |
843 | case 'u': | |
844 | ul = lflag ? va_arg(ap, u_long) : va_arg(ap, u_int); | |
845 | base = 10; | |
846 | goto nosign; | |
847 | case 'x': | |
848 | ul = lflag ? va_arg(ap, u_long) : va_arg(ap, u_int); | |
849 | base = 16; | |
850 | goto nosign; | |
851 | case 'z': | |
852 | ul = lflag ? va_arg(ap, u_long) : | |
853 | sign ? (u_long)va_arg(ap, int) : va_arg(ap, u_int); | |
854 | base = 16; | |
855 | goto number; | |
856 | nosign: sign = 0; | |
857 | number: if (sign && (long)ul < 0L) { | |
858 | neg = 1; | |
859 | ul = -(long)ul; | |
860 | } | |
861 | p = ksprintn(ul, base, &tmp); | |
862 | if (sharpflag && ul != 0) { | |
863 | if (base == 8) | |
864 | tmp++; | |
865 | else if (base == 16) | |
866 | tmp += 2; | |
867 | } | |
868 | if (neg) | |
869 | tmp++; | |
870 | ||
871 | if (!ladjust && width && (width -= tmp) > 0) | |
872 | while (width--) | |
873 | PCHAR(padc); | |
874 | if (neg) | |
875 | PCHAR('-'); | |
876 | if (sharpflag && ul != 0) { | |
877 | if (base == 8) { | |
878 | PCHAR('0'); | |
879 | } else if (base == 16) { | |
880 | PCHAR('0'); | |
881 | PCHAR('x'); | |
882 | } | |
883 | } | |
884 | ||
885 | while (*p) | |
886 | PCHAR(*p--); | |
887 | ||
888 | if (ladjust && width && (width -= tmp) > 0) | |
889 | while (width--) | |
890 | PCHAR(padc); | |
891 | ||
892 | break; | |
893 | default: | |
894 | PCHAR('%'); | |
895 | if (lflag) | |
896 | PCHAR('l'); | |
897 | PCHAR(ch); | |
898 | break; | |
899 | } | |
900 | } | |
901 | #undef PCHAR | |
902 | } |