~ubuntu-branches/ubuntu/hardy/klibc/hardy-updates

« back to all changes in this revision

Viewing changes to utils/printf.c

  • Committer: Bazaar Package Importer
  • Author(s): Jeff Bailey
  • Date: 2006-01-04 20:24:52 UTC
  • mfrom: (1.1.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20060104202452-ec4v3n829rymukuv
Tags: 1.1.15-0ubuntu1
* New upstream version.

* Patch to fix compilation on parisc64 kernels.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*      $NetBSD: printf.c,v 1.28 2003/06/25 12:56:59 dsl Exp $  */
2
 
 
3
 
/*
4
 
 * Copyright (c) 1989, 1993
5
 
 *      The Regents of the University of California.  All rights reserved.
6
 
 *
7
 
 * Redistribution and use in source and binary forms, with or without
8
 
 * modification, are permitted provided that the following conditions
9
 
 * are met:
10
 
 * 1. Redistributions of source code must retain the above copyright
11
 
 *    notice, this list of conditions and the following disclaimer.
12
 
 * 2. Redistributions in binary form must reproduce the above copyright
13
 
 *    notice, this list of conditions and the following disclaimer in the
14
 
 *    documentation and/or other materials provided with the distribution.
15
 
 * 3. Neither the name of the University nor the names of its contributors
16
 
 *    may be used to endorse or promote products derived from this software
17
 
 *    without specific prior written permission.
18
 
 *
19
 
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20
 
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
 
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22
 
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23
 
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
 
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25
 
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26
 
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27
 
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28
 
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29
 
 * SUCH DAMAGE.
30
 
 */
31
 
 
32
 
#ifndef __KLIBC__
33
 
#include <sys/cdefs.h>
34
 
#endif
35
 
#ifndef __COPYRIGHT
36
 
#define __COPYRIGHT(arg)
37
 
#endif
38
 
#ifndef __RCSID
39
 
#define __RCSID(arg)
40
 
#endif
41
 
#ifndef lint
42
 
#if !defined(BUILTIN) && !defined(SHELL)
43
 
__COPYRIGHT("@(#) Copyright (c) 1989, 1993\n\
44
 
        The Regents of the University of California.  All rights reserved.\n");
45
 
#endif
46
 
#endif
47
 
 
48
 
#ifndef lint
49
 
#if 0
50
 
static char sccsid[] = "@(#)printf.c    8.2 (Berkeley) 3/22/95";
51
 
#else
52
 
__RCSID("$NetBSD: printf.c,v 1.28 2003/06/25 12:56:59 dsl Exp $");
53
 
#endif
54
 
#endif /* not lint */
55
 
 
56
 
#include <sys/types.h>
57
 
 
58
 
#include <ctype.h>
59
 
#ifndef __KLIBC__
60
 
#include <err.h>
61
 
#endif
62
 
#include <errno.h>
63
 
#include <inttypes.h>
64
 
#include <limits.h>
65
 
#ifdef HAVE_LOCALE_H
66
 
#include <locale.h>
67
 
#endif
68
 
#include <stdarg.h>
69
 
#include <stdio.h>
70
 
#include <stdlib.h>
71
 
#include <string.h>
72
 
#include <unistd.h>
73
 
 
74
 
#ifdef __linux__
75
 
//#include "compat_linux.h"
76
 
#endif
77
 
 
78
 
#ifdef __GNUC__
79
 
#define ESCAPE '\e'
80
 
#else
81
 
#define ESCAPE 033
82
 
#endif
83
 
 
84
 
static void      conv_escape_str(char *, void (*)(int));
85
 
static char     *conv_escape(char *, char *);
86
 
static char     *conv_expand(const char *);
87
 
static int       getchr(void);
88
 
#ifndef __KLIBC__
89
 
static double    getdouble(void);
90
 
#endif
91
 
static int       getwidth(void);
92
 
static intmax_t  getintmax(void);
93
 
static uintmax_t getuintmax(void);
94
 
static char     *getstr(void);
95
 
static char     *mklong(const char *, int);
96
 
static void      check_conversion(const char *, const char *);
97
 
static void      usage(void); 
98
 
 
99
 
static void     b_count(int);
100
 
static void     b_output(int);
101
 
static int      b_length;
102
 
static char     *b_fmt;
103
 
 
104
 
static int      rval;
105
 
static char  **gargv;
106
 
 
107
 
#ifdef BUILTIN          /* csh builtin */
108
 
#define main progprintf
109
 
#endif
110
 
 
111
 
#ifdef SHELL            /* sh (aka ash) builtin */
112
 
#define main printfcmd
113
 
#include "bltin/bltin.h"
114
 
#endif /* SHELL */
115
 
 
116
 
#define PF(f, func) { \
117
 
        if (fieldwidth != -1) { \
118
 
                if (precision != -1) \
119
 
                        (void)printf(f, fieldwidth, precision, func); \
120
 
                else \
121
 
                        (void)printf(f, fieldwidth, func); \
122
 
        } else if (precision != -1) \
123
 
                (void)printf(f, precision, func); \
124
 
        else \
125
 
                (void)printf(f, func); \
126
 
}
127
 
 
128
 
#define APF(cpp, f, func) { \
129
 
        if (fieldwidth != -1) { \
130
 
                if (precision != -1) \
131
 
                        (void)asprintf(cpp, f, fieldwidth, precision, func); \
132
 
                else \
133
 
                        (void)asprintf(cpp, f, fieldwidth, func); \
134
 
        } else if (precision != -1) \
135
 
                (void)asprintf(cpp, f, precision, func); \
136
 
        else \
137
 
                (void)asprintf(cpp, f, func); \
138
 
}
139
 
 
140
 
int main(int, char **);
141
 
int main(int argc, char *argv[])
142
 
{
143
 
        char *fmt, *start;
144
 
        int fieldwidth, precision;
145
 
        char nextch;
146
 
        char *format;
147
 
        int ch;
148
 
 
149
 
        while ((ch = getopt(argc, argv, "")) != -1) {
150
 
                switch (ch) {
151
 
                case '?':
152
 
                default:
153
 
                        usage();
154
 
                        return (1);
155
 
                }
156
 
        }
157
 
        argc -= optind;
158
 
        argv += optind;
159
 
 
160
 
        if (argc < 1) {
161
 
                usage();
162
 
                return (1);
163
 
        }
164
 
 
165
 
        format = *argv;
166
 
        gargv = ++argv;
167
 
 
168
 
#define SKIP1   "#-+ 0"
169
 
#define SKIP2   "*0123456789"
170
 
        do {
171
 
                /*
172
 
                 * Basic algorithm is to scan the format string for conversion
173
 
                 * specifications -- once one is found, find out if the field
174
 
                 * width or precision is a '*'; if it is, gather up value. 
175
 
                 * Note, format strings are reused as necessary to use up the
176
 
                 * provided arguments, arguments of zero/null string are 
177
 
                 * provided to use up the format string.
178
 
                 */
179
 
 
180
 
                /* find next format specification */
181
 
                for (fmt = format; (ch = *fmt++) ;) {
182
 
                        if (ch == '\\') {
183
 
                                char c_ch;
184
 
                                fmt = conv_escape(fmt, &c_ch);
185
 
                                putchar(c_ch);
186
 
                                continue;
187
 
                        }
188
 
                        if (ch != '%' || (*fmt == '%' && ++fmt)) {
189
 
                                (void)putchar(ch);
190
 
                                continue;
191
 
                        }
192
 
 
193
 
                        /* Ok - we've found a format specification,
194
 
                           Save its address for a later printf(). */
195
 
                        start = fmt - 1;
196
 
 
197
 
                        /* skip to field width */
198
 
                        fmt += strspn(fmt, SKIP1);
199
 
                        fieldwidth = *fmt == '*' ? getwidth() : -1;
200
 
 
201
 
                        /* skip to possible '.', get following precision */
202
 
                        fmt += strspn(fmt, SKIP2);
203
 
                        if (*fmt == '.')
204
 
                                ++fmt;
205
 
                        precision = *fmt == '*' ? getwidth() : -1;
206
 
 
207
 
                        fmt += strspn(fmt, SKIP2);
208
 
 
209
 
                        ch = *fmt;
210
 
                        if (!ch) {
211
 
                                fprintf(stderr, "missing format character");
212
 
                                return (1);
213
 
                        }
214
 
                        /* null terminate format string to we can use it
215
 
                           as an argument to printf. */
216
 
                        nextch = fmt[1];
217
 
                        fmt[1] = 0;
218
 
                        switch (ch) {
219
 
 
220
 
                        case 'B': {
221
 
                                const char *p = conv_expand(getstr());
222
 
                                *fmt = 's';
223
 
                                PF(start, p);
224
 
                                break;
225
 
                        }
226
 
                        case 'b': {
227
 
                                /* There has to be a better way to do this,
228
 
                                 * but the string we generate might have
229
 
                                 * embedded nulls. */
230
 
                                static char *a, *t;
231
 
                                char *cp = getstr();
232
 
                                /* Free on entry in case shell longjumped out */
233
 
                                if (a != NULL)
234
 
                                        free(a);
235
 
                                a = NULL;
236
 
                                if (t != NULL)
237
 
                                        free(t);
238
 
                                t = NULL;
239
 
                                /* Count number of bytes we want to output */
240
 
                                b_length = 0;
241
 
                                conv_escape_str(cp, b_count);
242
 
                                t = malloc(b_length + 1);
243
 
                                if (t == NULL)
244
 
                                        break;
245
 
                                memset(t, 'x', b_length);
246
 
                                t[b_length] = 0;
247
 
                                /* Get printf to calculate the lengths */
248
 
                                *fmt = 's';
249
 
                                APF(&a, start, t);
250
 
                                b_fmt = a;
251
 
                                /* Output leading spaces and data bytes */
252
 
                                conv_escape_str(cp, b_output);
253
 
                                /* Add any trailing spaces */
254
 
                                printf("%s", b_fmt);
255
 
                                break;
256
 
                        }
257
 
                        case 'c': {
258
 
                                char p = getchr();
259
 
                                PF(start, p);
260
 
                                break;
261
 
                        }
262
 
                        case 's': {
263
 
                                char *p = getstr();
264
 
                                PF(start, p);
265
 
                                break;
266
 
                        }
267
 
                        case 'd':
268
 
                        case 'i': {
269
 
                                intmax_t p = getintmax();
270
 
                                char *f = mklong(start, ch);
271
 
                                PF(f, p);
272
 
                                break;
273
 
                        }
274
 
                        case 'o':
275
 
                        case 'u':
276
 
                        case 'x':
277
 
                        case 'X': {
278
 
                                uintmax_t p = getuintmax();
279
 
                                char *f = mklong(start, ch);
280
 
                                PF(f, p);
281
 
                                break;
282
 
                        }
283
 
#ifndef __KLIBC__
284
 
                        case 'e':
285
 
                        case 'E':
286
 
                        case 'f':
287
 
                        case 'g':
288
 
                        case 'G': {
289
 
                                double p = getdouble();
290
 
                                PF(start, p);
291
 
                                break;
292
 
                        }
293
 
#endif
294
 
                        default:
295
 
                                fprintf(stderr, "%s: invalid directive", start);
296
 
                                return (1);
297
 
                        }
298
 
                        *fmt++ = ch;
299
 
                        *fmt = nextch;
300
 
                        /* escape if a \c was encountered */
301
 
                        if (rval & 0x100)
302
 
                                return (rval & ~0x100);
303
 
                }
304
 
        } while (gargv != argv && *gargv);
305
 
 
306
 
        return (rval);
307
 
}
308
 
 
309
 
/* helper functions for conv_escape_str */
310
 
 
311
 
static void
312
 
b_count(int ch)
313
 
{
314
 
        ch = b_length++;
315
 
}
316
 
 
317
 
/* Output one converted character for every 'x' in the 'format' */
318
 
 
319
 
static void
320
 
b_output(int ch)
321
 
{
322
 
        for (;;) {
323
 
                switch (*b_fmt++) {
324
 
                case 0:
325
 
                        b_fmt--;
326
 
                        return;
327
 
                case ' ':
328
 
                        putchar(' ');
329
 
                        break;
330
 
                default:
331
 
                        putchar(ch);
332
 
                        return;
333
 
                }
334
 
        }
335
 
}
336
 
 
337
 
 
338
 
/*
339
 
 * Print SysV echo(1) style escape string 
340
 
 *      Halts processing string if a \c escape is encountered.
341
 
 */
342
 
static void
343
 
conv_escape_str(char *str, void (*do_putchar)(int))
344
 
{
345
 
        int value;
346
 
        int ch;
347
 
        char c;
348
 
 
349
 
        while ((ch = *str++)) {
350
 
                if (ch != '\\') {
351
 
                        do_putchar(ch);
352
 
                        continue;
353
 
                }
354
 
 
355
 
                ch = *str++;
356
 
                if (ch == 'c') {
357
 
                        /* \c as in SYSV echo - abort all processing.... */
358
 
                        rval |= 0x100;
359
 
                        break;
360
 
                }
361
 
 
362
 
                /* 
363
 
                 * %b string octal constants are not like those in C.
364
 
                 * They start with a \0, and are followed by 0, 1, 2, 
365
 
                 * or 3 octal digits. 
366
 
                 */
367
 
                if (ch == '0') {
368
 
                        char octnum[4], *oct_end;
369
 
                        octnum[0] = str[0];
370
 
                        octnum[1] = str[1];
371
 
                        octnum[2] = str[2];
372
 
                        octnum[3] = 0;
373
 
                        do_putchar(strtoul(octnum, &oct_end, 8));
374
 
                        str += oct_end - octnum;
375
 
                        continue;
376
 
                }
377
 
 
378
 
                /* \[M][^|-]C as defined by vis(3) */
379
 
                if (ch == 'M' && *str == '-') {
380
 
                        do_putchar(0200 | str[1]);
381
 
                        str += 2;
382
 
                        continue;
383
 
                }
384
 
                if (ch == 'M' && *str == '^') {
385
 
                        str++;
386
 
                        value = 0200;
387
 
                        ch = '^';
388
 
                } else
389
 
                        value = 0;
390
 
                if (ch == '^') {
391
 
                        ch = *str++;
392
 
                        if (ch == '?')
393
 
                                value |= 0177;
394
 
                        else
395
 
                                value |= ch & 037;
396
 
                        do_putchar(value);
397
 
                        continue;
398
 
                }
399
 
 
400
 
                /* Finally test for sequences valid in the format string */
401
 
                str = conv_escape(str - 1, &c);
402
 
                do_putchar(c);
403
 
        }
404
 
}
405
 
 
406
 
/*
407
 
 * Print "standard" escape characters 
408
 
 */
409
 
static char *
410
 
conv_escape(char *str, char *conv_ch)
411
 
{
412
 
        int value;
413
 
        int ch;
414
 
        char num_buf[4], *num_end;
415
 
 
416
 
        ch = *str++;
417
 
 
418
 
        switch (ch) {
419
 
        case '0': case '1': case '2': case '3':
420
 
        case '4': case '5': case '6': case '7':
421
 
                num_buf[0] = ch;
422
 
                ch = str[0];
423
 
                num_buf[1] = ch;
424
 
                num_buf[2] = ch ? str[1] : 0;
425
 
                num_buf[3] = 0;
426
 
                value = strtoul(num_buf, &num_end, 8);
427
 
                str += num_end  - (num_buf + 1);
428
 
                break;
429
 
 
430
 
        case 'x':
431
 
                /* Hexadecimal character constants are not required to be
432
 
                   supported (by SuS v1) because there is no consistent
433
 
                   way to detect the end of the constant.
434
 
                   Supporting 2 byte constants is a compromise. */
435
 
                ch = str[0];
436
 
                num_buf[0] = ch;
437
 
                num_buf[1] = ch ? str[1] : 0;
438
 
                num_buf[2] = 0;
439
 
                value = strtoul(num_buf, &num_end, 16);
440
 
                str += num_end - num_buf;
441
 
                break;
442
 
 
443
 
        case '\\':      value = '\\';   break;  /* backslash */
444
 
        case '\'':      value = '\'';   break;  /* single quote */
445
 
        case '"':       value = '"';    break;  /* double quote */
446
 
        case 'a':       value = '\a';   break;  /* alert */
447
 
        case 'b':       value = '\b';   break;  /* backspace */
448
 
        case 'e':       value = ESCAPE; break;  /* escape */
449
 
        case 'f':       value = '\f';   break;  /* form-feed */
450
 
        case 'n':       value = '\n';   break;  /* newline */
451
 
        case 'r':       value = '\r';   break;  /* carriage-return */
452
 
        case 't':       value = '\t';   break;  /* tab */
453
 
        case 'v':       value = '\v';   break;  /* vertical-tab */
454
 
 
455
 
        default:
456
 
                fprintf(stderr, "unknown escape sequence `\\%c'", ch);
457
 
                rval = 1;
458
 
                value = ch;
459
 
                break;
460
 
        }
461
 
 
462
 
        *conv_ch = value;
463
 
        return str;
464
 
}
465
 
 
466
 
/* expand a string so that everything is printable */
467
 
 
468
 
static char *
469
 
conv_expand(const char *str)
470
 
{
471
 
        static char *conv_str;
472
 
        char *cp;
473
 
        int ch;
474
 
 
475
 
        if (conv_str)
476
 
                free(conv_str);
477
 
        /* get a buffer that is definitely large enough.... */
478
 
        conv_str = malloc(4 * strlen(str) + 1);
479
 
        if (!conv_str)
480
 
                return "<no memory>";
481
 
        cp = conv_str;
482
 
 
483
 
        while ((ch = *(unsigned char *)str++)) {
484
 
                switch (ch) {
485
 
                /* Use C escapes for expected control characters */
486
 
                case '\\':      ch = '\\';      break;  /* backslash */
487
 
                case '\'':      ch = '\'';      break;  /* single quote */
488
 
                case '"':       ch = '"';       break;  /* double quote */
489
 
                case '\a':      ch = 'a';       break;  /* alert */
490
 
                case '\b':      ch = 'b';       break;  /* backspace */
491
 
                case ESCAPE:    ch = 'e';       break;  /* escape */
492
 
                case '\f':      ch = 'f';       break;  /* form-feed */
493
 
                case '\n':      ch = 'n';       break;  /* newline */
494
 
                case '\r':      ch = 'r';       break;  /* carriage-return */
495
 
                case '\t':      ch = 't';       break;  /* tab */
496
 
                case '\v':      ch = 'v';       break;  /* vertical-tab */
497
 
                default:
498
 
                        /* Copy anything printable */
499
 
                        if (isprint(ch)) {
500
 
                                *cp++ = ch;
501
 
                                continue;
502
 
                        }
503
 
                        /* Use vis(3) encodings for the rest */
504
 
                        *cp++ = '\\';
505
 
                        if (ch & 0200) {
506
 
                                *cp++ = 'M';
507
 
                                ch &= ~0200;
508
 
                        }
509
 
                        if (ch == 0177) {
510
 
                                *cp++ = '^';
511
 
                                *cp++ = '?';
512
 
                                continue;
513
 
                        }
514
 
                        if (ch < 040) {
515
 
                                *cp++ = '^';
516
 
                                *cp++ = ch | 0100;
517
 
                                continue;
518
 
                        }
519
 
                        *cp++ = '-';
520
 
                        *cp++ = ch;
521
 
                        continue;
522
 
                }
523
 
                *cp++ = '\\';
524
 
                *cp++ = ch;
525
 
        }
526
 
 
527
 
        *cp = 0;
528
 
        return conv_str;
529
 
}
530
 
 
531
 
static char *
532
 
mklong(const char *str, int ch)
533
 
{
534
 
        static char copy[64];
535
 
        size_t len;     
536
 
 
537
 
        len = strlen(str) + 2;
538
 
        if (len > sizeof copy) {
539
 
                fprintf(stderr, "format %s too complex\n", str);
540
 
                len = 4;
541
 
        }
542
 
        (void)memmove(copy, str, len - 3);
543
 
        copy[len - 3] = 'j';
544
 
        copy[len - 2] = ch;
545
 
        copy[len - 1] = '\0';
546
 
        return (copy);  
547
 
}
548
 
 
549
 
static int
550
 
getchr(void)
551
 
{
552
 
        if (!*gargv)
553
 
                return ('\0');
554
 
        return ((int)**gargv++);
555
 
}
556
 
 
557
 
static char *
558
 
getstr(void)
559
 
{
560
 
        if (!*gargv)
561
 
                return ("");
562
 
        return (*gargv++);
563
 
}
564
 
 
565
 
static int
566
 
getwidth(void)
567
 
{
568
 
        long val;
569
 
        char *s, *ep;
570
 
 
571
 
        s = *gargv;
572
 
        if (!*gargv)
573
 
                return (0);
574
 
        gargv++;
575
 
 
576
 
        errno = 0;
577
 
        val = strtoul(s, &ep, 0);
578
 
        check_conversion(s, ep);
579
 
 
580
 
        /* Arbitrarily 'restrict' field widths to 1Mbyte */
581
 
        if (val < 0 || val > 1 << 20) {
582
 
                fprintf(stderr, "%s: invalid field width", s);
583
 
                return 0;
584
 
        }
585
 
 
586
 
        return val;
587
 
}
588
 
 
589
 
static intmax_t
590
 
getintmax(void)
591
 
{
592
 
        intmax_t val;
593
 
        char *cp, *ep;
594
 
 
595
 
        cp = *gargv;
596
 
        if (cp == NULL)
597
 
                return 0;
598
 
        gargv++;
599
 
 
600
 
        if (*cp == '\"' || *cp == '\'')
601
 
                return *(cp+1);
602
 
 
603
 
        errno = 0;
604
 
        val = strtoimax(cp, &ep, 0);
605
 
        check_conversion(cp, ep);
606
 
        return val;
607
 
}
608
 
 
609
 
static uintmax_t
610
 
getuintmax(void)
611
 
{
612
 
        uintmax_t val;
613
 
        char *cp, *ep;
614
 
 
615
 
        cp = *gargv;
616
 
        if (cp == NULL)
617
 
                return 0;
618
 
        gargv++;
619
 
 
620
 
        if (*cp == '\"' || *cp == '\'')
621
 
                return *(cp+1);
622
 
 
623
 
        /* strtoumax won't error -ve values */
624
 
        while (isspace(*(unsigned char *)cp))
625
 
                cp++;
626
 
        if (*cp == '-') {
627
 
                fprintf(stderr, "%s: expected positive numeric value", cp);
628
 
                rval = 1;
629
 
                return 0;
630
 
        }
631
 
 
632
 
        errno = 0;
633
 
        val = strtoumax(cp, &ep, 0);
634
 
        check_conversion(cp, ep);
635
 
        return val;
636
 
}
637
 
 
638
 
#ifndef __KLIBC__
639
 
static double
640
 
getdouble(void)
641
 
{
642
 
        double val;
643
 
        char *ep;
644
 
 
645
 
        if (!*gargv)
646
 
                return (0.0);
647
 
 
648
 
        if (**gargv == '\"' || **gargv == '\'')
649
 
                return (double) *((*gargv++)+1);
650
 
 
651
 
        errno = 0;
652
 
        val = strtod(*gargv, &ep);
653
 
        check_conversion(*gargv++, ep);
654
 
        return val;
655
 
}
656
 
#endif
657
 
 
658
 
static void
659
 
check_conversion(const char *s, const char *ep)
660
 
{
661
 
        if (*ep) {
662
 
                if (ep == s)
663
 
                        fprintf(stderr, "%s: expected numeric value", s);
664
 
                else
665
 
                        fprintf(stderr, "%s: not completely converted", s);
666
 
                rval = 1;
667
 
        } else if (errno == ERANGE) {
668
 
                fprintf(stderr, "%s: %s", s, strerror(ERANGE));
669
 
                rval = 1;
670
 
        }
671
 
}
672
 
 
673
 
static void
674
 
usage(void)
675
 
{
676
 
        (void)fprintf(stderr, "usage: printf format [arg ...]\n");
677
 
}