Subversion Repositories NaviCtrl

Rev

Rev 203 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
203 killagreg 1
/*
1 ingob 2
Copyright (C) 1993 Free Software Foundation
3
 
4
This file is part of the GNU IO Library.  This library is free
5
software; you can redistribute it and/or modify it under the
6
terms of the GNU General Public License as published by the
7
Free Software Foundation; either version 2, or (at your option)
8
any later version.
9
 
10
This library is distributed in the hope that it will be useful,
11
but WITHOUT ANY WARRANTY; without even the implied warranty of
12
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
GNU General Public License for more details.
14
 
15
You should have received a copy of the GNU General Public License
16
along with this library; see the file COPYING.  If not, write to the Free
17
Software Foundation, 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
 
19
As a special exception, if you link this library with files
20
compiled with a GNU compiler to produce an executable, this does not cause
21
the resulting executable to be covered by the GNU General Public License.
22
This exception does not however invalidate any other reasons why
23
the executable file might be covered by the GNU General Public License. */
24
 
25
/*
26
 * Copyright (c) 1990 Regents of the University of California.
27
 * All rights reserved.
28
 *
29
 * Redistribution and use in source and binary forms, with or without
30
 * modification, are permitted provided that the following conditions
31
 * are met:
32
 * 1. Redistributions of source code must retain the above copyright
33
 *    notice, this list of conditions and the following disclaimer.
34
 * 2. Redistributions in binary form must reproduce the above copyright
35
 *    notice, this list of conditions and the following disclaimer in the
36
 *    documentation and/or other materials provided with the distribution.
37
 * 3. [rescinded 22 July 1999]
38
 * 4. Neither the name of the University nor the names of its contributors
39
 *    may be used to endorse or promote products derived from this software
40
 *    without specific prior written permission.
41
 *
42
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
43
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
44
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
45
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
46
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
47
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
48
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
49
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
50
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
51
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
52
 * SUCH DAMAGE.
53
 */
54
 
55
/* * ****************************************************************************
56
 This file is a patched version of printf called _printf_P
57
 It is made to work with avr-gcc for Atmel AVR MCUs.
58
 There are some differences from standard printf:
59
        1. There is no floating point support (with fp the code is about 8K!)
60
        2. Return type is void
61
        3. Format string must be in program memory (by using macro printf this is
62
           done automaticaly)
63
        4. %n is not implemented (just remove the comment around it if you need it)
64
        5. If LIGHTPRINTF is defined, the code is about 550 bytes smaller and the
65
           folowing specifiers are disabled :
66
                space # * . - + p s o O
67
        6. A function void uart_sendchar(char c) is used for output. The UART must
68
                be initialized before using printf.
69
 
70
 Alexander Popov
71
 sasho@vip.orbitel.bg
72
******************************************************************************
73
 Changed file to work with LPC - ARM.
203 killagreg 74
 Ingo Busker
1 ingob 75
 busker@mikrocontroller.com   02.2005
203 killagreg 76
******************************************************************************/
1 ingob 77
 
203 killagreg 78
 
1 ingob 79
/*
80
 * Actual printf innards.
81
 *
82
 * This code is large and complicated...
83
 */
84
 
85
#include <string.h>
86
#ifdef __STDC__
87
#include <stdarg.h>
88
#else
89
#include <varargs.h>
90
#endif
41 ingob 91
#include "91x_lib.h"
203 killagreg 92
#include "printf_P.h"
1 ingob 93
 
203 killagreg 94
void PRINT(pVoidFnctChar pPutchar, const char * ptr, unsigned int len)
1 ingob 95
{
203 killagreg 96
        for(;len;len--)
97
                (*pPutchar)(*ptr++);
1 ingob 98
}
99
 
203 killagreg 100
void PRINTP(pVoidFnctChar pPutchar, const char * ptr, unsigned int len)
101
{
102
        for(;len;len--)
103
                (*pPutchar)(*ptr++);
1 ingob 104
}
105
 
203 killagreg 106
void PAD_SP(pVoidFnctChar pPutchar, signed char howmany) {
1 ingob 107
        for(;howmany>0;howmany--)
203 killagreg 108
                (*pPutchar)(' ');
1 ingob 109
}
110
 
203 killagreg 111
void PAD_0(pVoidFnctChar pPutchar, signed char howmany) {
1 ingob 112
        for(;howmany>0;howmany--)
203 killagreg 113
                (*pPutchar)('0');
1 ingob 114
}
115
 
116
#define BUF             40
117
 
118
/*
119
 * Macros for converting digits to letters and vice versa
120
 */
121
#define to_digit(c)     ((c) - '0')
203 killagreg 122
#define is_digit(c)     ((c)<='9' && (c)>='0')
1 ingob 123
#define to_char(n)      ((n) + '0')
124
 
125
/*
126
 * Flags used during conversion.
127
 */
128
#define LONGINT         0x01            /* long integer */
129
#define LONGDBL         0x02            /* long double; unimplemented */
203 killagreg 130
#define SHORTINT        0x04            /* short integer */
1 ingob 131
#define ALT                     0x08            /* alternate form */
132
#define LADJUST         0x10            /* left adjustment */
133
#define ZEROPAD         0x20            /* zero (as opposed to blank) pad */
134
#define HEXPREFIX       0x40            /* add 0x or 0X prefix */
135
 
203 killagreg 136
void _printf_P (pVoidFnctChar pPutchar, char const *fmt0, ...)
1 ingob 137
{
138
        va_list ap;
139
        register const char *fmt; /* format string */
140
        register char ch;       /* character from fmt */
141
        register int n;         /* handy integer (short term usage) */
142
        register char *cp;      /* handy char pointer (short term usage) */
143
        const char *fmark;      /* for remembering a place in fmt */
144
        register unsigned char flags;   /* flags as above */
145
        signed char width;              /* width from format (%8d), or 0 */
146
        signed char prec;               /* precision from format (%.3d), or -1 */
147
        char sign;                              /* sign prefix (' ', '+', '-', or \0) */
148
        unsigned long _ulong=0; /* integer arguments %[diouxX] */
149
#define OCT 8
150
#define DEC 10
151
#define HEX 16
152
        unsigned char base;             /* base for [diouxX] conversion */
153
        signed char dprec;              /* a copy of prec if [diouxX], 0 otherwise */
154
        signed char dpad;                       /* extra 0 padding needed for integers */
155
        signed char fieldsz;            /* field size expanded by sign, dpad etc */
156
        /* The initialization of 'size' is to suppress a warning that
157
           'size' might be used unitialized.  It seems gcc can't
158
           quite grok this spaghetti code ... */
159
        signed char size = 0;           /* size of converted field or string */
160
        char buf[BUF];          /* space for %c, %[diouxX], %[eEfgG] */
161
        char ox[2];                     /* space for 0x hex-prefix */
162
 
203 killagreg 163
        if(pPutchar == NULL) return;
164
 
1 ingob 165
        va_start(ap, fmt0);
203 killagreg 166
 
1 ingob 167
        fmt = fmt0;
168
 
169
        /*
170
         * Scan the format for conversions (`%' character).
171
         */
172
        for (;;) {
173
                for (fmark = fmt; (ch = (*fmt)) != '\0' && ch != '%'; fmt++)
174
                        /* void */;
175
                if ((n = fmt - fmark) != 0) {
203 killagreg 176
                        PRINTP(pPutchar, fmark, n);
1 ingob 177
                }
178
                if (ch == '\0')
179
                        goto done;
180
                fmt++;          /* skip over '%' */
181
 
182
                flags = 0;
183
                dprec = 0;
184
                width = 0;
185
                prec = -1;
186
                sign = '\0';
187
 
188
rflag:          ch = (*fmt++);
189
reswitch:
190
#ifdef LIGHTPRINTF
191
        if (ch=='o' || ch=='u' || (ch|0x20)=='x') {
192
#else
193
        if (ch=='u' || (ch|0x20)=='x') {
194
#endif
195
                if (flags&LONGINT) {
196
                        _ulong=va_arg(ap, unsigned long);
197
                } else {
198
                        register unsigned int _d;
199
                        _d=va_arg(ap, unsigned int);
200
                        _ulong = flags&SHORTINT ? (unsigned long)(unsigned short)_d : (unsigned long)_d;
201
                }
202
        }
203 killagreg 203
 
1 ingob 204
#ifndef LIGHTPRINTF
205
                if(ch==' ') {
206
                        /*
207
                         * ``If the space and + flags both appear, the space
208
                         * flag will be ignored.''
209
                         *      -- ANSI X3J11
210
                         */
211
                        if (!sign)
212
                                sign = ' ';
213
                        goto rflag;
214
                } else if (ch=='#') {
215
                        flags |= ALT;
216
                        goto rflag;
217
                } else if (ch=='*'||ch=='-') {
218
                        if (ch=='*') {
219
                                /*
220
                                 * ``A negative field width argument is taken as a
221
                                 * - flag followed by a positive field width.''
222
                                 *      -- ANSI X3J11
223
                                 * They don't exclude field widths read from args.
224
                                 */
225
                                if ((width = va_arg(ap, int)) >= 0)
226
                                        goto rflag;
227
                                width = -width;
228
                        }
229
                        flags |= LADJUST;
230
                        flags &= ~ZEROPAD; /* '-' disables '0' */
231
                        goto rflag;
232
                } else if (ch=='+') {
233
                        sign = '+';
234
                        goto rflag;
235
                } else if (ch=='.') {
236
                        if ((ch = (*fmt++)) == '*') {
237
                                n = va_arg(ap, int);
238
                                prec = n < 0 ? -1 : n;
239
                                goto rflag;
240
                        }
241
                        n = 0;
242
                        while (is_digit(ch)) {
243
                                n = n*10 + to_digit(ch);
244
                                ch = (*fmt++);
245
                        }
246
                        prec = n < 0 ? -1 : n;
247
                        goto reswitch;
248
                } else
249
#endif /* LIGHTPRINTF */
250
                if (ch=='0') {
251
                        /*
252
                         * ``Note that 0 is taken as a flag, not as the
253
                         * beginning of a field width.''
254
                         *      -- ANSI X3J11
255
                         */
256
                        if (!(flags & LADJUST))
257
                            flags |= ZEROPAD; /* '-' disables '0' */
258
                        goto rflag;
259
                } else if (ch>='1' && ch<='9') {
260
                        n = 0;
261
                        do {
262
                                n = 10 * n + to_digit(ch);
263
                                ch = *fmt++;
264
                        } while (is_digit(ch));
265
                        width = n;
266
                        goto reswitch;
267
                } else if (ch=='h') {
268
                        flags |= SHORTINT;
269
                        goto rflag;
270
                } else if (ch=='l') {
271
                        flags |= LONGINT;
272
                        goto rflag;
273
                } else if (ch=='c') {
274
                        *(cp = buf) = va_arg(ap, int);
275
                        size = 1;
276
                        sign = '\0';
277
                } else if (ch=='D'||ch=='d'||ch=='i') {
278
                        if(ch=='D')
279
                                flags |= LONGINT;
280
                        if (flags&LONGINT) {
281
                                _ulong=va_arg(ap, long);
282
                        } else {
283
                                register int _d;
284
                                _d=va_arg(ap, int);
285
                                _ulong = flags&SHORTINT ? (long)(short)_d : (long)_d;
286
                        }
203 killagreg 287
 
1 ingob 288
                        if ((long)_ulong < 0) {
289
                                _ulong = -_ulong;
290
                                sign = '-';
291
                        }
292
                        base = DEC;
293
                        goto number;
294
                } else
203 killagreg 295
/*
1 ingob 296
                if (ch=='n') {
297
                        if (flags & LONGINT)
298
                                *va_arg(ap, long *) = ret;
299
                        else if (flags & SHORTINT)
300
                                *va_arg(ap, short *) = ret;
301
                        else
302
                                *va_arg(ap, int *) = ret;
203 killagreg 303
                        continue;       // no output
1 ingob 304
                } else
305
*/
203 killagreg 306
#ifndef LIGHTPRINTF
1 ingob 307
                if (ch=='O'||ch=='o') {
308
                        if (ch=='O')
309
                                flags |= LONGINT;
310
                        base = OCT;
311
                        goto nosign;
312
                } else if (ch=='p') {
313
                        /*
314
                         * ``The argument shall be a pointer to void.  The
315
                         * value of the pointer is converted to a sequence
316
                         * of printable characters, in an implementation-
317
                         * defined manner.''
318
                         *      -- ANSI X3J11
319
                         */
320
                        /* NOSTRICT */
321
                        _ulong = (unsigned int)va_arg(ap, void *);
322
                        base = HEX;
323
                        flags |= HEXPREFIX;
324
                        ch = 'x';
325
                        goto nosign;
326
                } else if (ch=='s') {  // print a string from RAM
327
                        if ((cp = va_arg(ap, char *)) == NULL) {
328
                                cp=buf;
329
                                cp[0] = '(';
330
                                cp[1] = 'n';
331
                                cp[2] = 'u';
332
                                cp[4] = cp[3] = 'l';
333
                                cp[5] = ')';
334
                                cp[6] = '\0';
335
                        }
336
                        if (prec >= 0) {
337
                                /*
338
                                 * can't use strlen; can only look for the
339
                                 * NUL in the first `prec' characters, and
340
                                 * strlen() will go further.
341
                                 */
342
                                char *p = (char*)memchr(cp, 0, prec);
343
 
344
                                if (p != NULL) {
345
                                        size = p - cp;
346
                                        if (size > prec)
347
                                                size = prec;
348
                                } else
349
                                        size = prec;
350
                        } else
351
                                size = strlen(cp);
352
                        sign = '\0';
353
                } else
203 killagreg 354
#endif /* LIGHTPRINTF */
1 ingob 355
                if(ch=='U'||ch=='u') {
356
                        if (ch=='U')
357
                                flags |= LONGINT;
358
                        base = DEC;
359
                        goto nosign;
360
                } else if (ch=='X'||ch=='x') {
361
                        base = HEX;
362
                        /* leading 0x/X only if non-zero */
363
                        if (flags & ALT && _ulong != 0)
364
                                flags |= HEXPREFIX;
365
 
366
                        /* unsigned conversions */
367
nosign:                 sign = '\0';
368
                        /*
369
                         * ``... diouXx conversions ... if a precision is
370
                         * specified, the 0 flag will be ignored.''
371
                         *      -- ANSI X3J11
372
                         */
373
number: if ((dprec = prec) >= 0)
374
                                flags &= ~ZEROPAD;
375
 
376
                        /*
377
                         * ``The result of converting a zero value with an
378
                         * explicit precision of zero is no characters.''
379
                         *      -- ANSI X3J11
380
                         */
381
                        cp = buf + BUF;
382
                        if (_ulong != 0 || prec != 0) {
383
                                register unsigned char _d,notlastdigit;
384
                                do {
385
                                        notlastdigit=(_ulong>=base);
386
                                        _d = _ulong % base;
387
 
388
                                        if (_d<10) {
389
                                                _d+='0';
390
                                        } else {
391
                                                _d+='a'-10;
392
                                                if (ch=='X') _d&=~0x20;
393
                                        }
394
                                        *--cp=_d;
395
                                        _ulong /= base;
396
                                } while (notlastdigit);
397
#ifndef LIGHTPRINTF
203 killagreg 398
                                // handle octal leading 0
1 ingob 399
                                if (base==OCT && flags & ALT && *cp != '0')
400
                                        *--cp = '0';
401
#endif
402
                        }
403
 
404
                        size = buf + BUF - cp;
405
        } else {  //default
406
                /* "%?" prints ?, unless ? is NUL */
407
                        if (ch == '\0')
408
                                goto done;
409
                        /* pretend it was %c with argument ch */
410
                        cp = buf;
411
                        *cp = ch;
412
                        size = 1;
413
                        sign = '\0';
414
                }
415
 
416
                /*
417
                 * All reasonable formats wind up here.  At this point,
418
                 * `cp' points to a string which (if not flags&LADJUST)
419
                 * should be padded out to `width' places.  If
420
                 * flags&ZEROPAD, it should first be prefixed by any
421
                 * sign or other prefix; otherwise, it should be blank
422
                 * padded before the prefix is emitted.  After any
423
                 * left-hand padding and prefixing, emit zeroes
424
                 * required by a decimal [diouxX] precision, then print
425
                 * the string proper, then emit zeroes required by any
426
                 * leftover floating precision; finally, if LADJUST,
427
                 * pad with blanks.
428
                 */
429
 
430
                /*
431
                 * compute actual size, so we know how much to pad.
432
                 */
433
                fieldsz = size;
434
 
435
                dpad = dprec - size;
436
                if (dpad < 0)
437
                    dpad = 0;
438
 
439
                if (sign)
440
                        fieldsz++;
441
                else if (flags & HEXPREFIX)
442
                        fieldsz += 2;
443
                fieldsz += dpad;
444
 
445
                /* right-adjusting blank padding */
446
                if ((flags & (LADJUST|ZEROPAD)) == 0)
203 killagreg 447
                        PAD_SP(pPutchar, width - fieldsz);
1 ingob 448
 
449
                /* prefix */
450
                if (sign) {
203 killagreg 451
                        PRINT(pPutchar, &sign, 1);
1 ingob 452
                } else if (flags & HEXPREFIX) {
453
                        ox[0] = '0';
454
                        ox[1] = ch;
203 killagreg 455
                        PRINT(pPutchar, ox, 2);
1 ingob 456
                }
457
 
458
                /* right-adjusting zero padding */
459
                if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD)
203 killagreg 460
                        PAD_0(pPutchar, width - fieldsz);
1 ingob 461
 
462
                /* leading zeroes from decimal precision */
203 killagreg 463
                PAD_0(pPutchar, dpad);
1 ingob 464
 
465
                /* the string or number proper */
203 killagreg 466
                PRINT(pPutchar, cp, size);
1 ingob 467
 
468
                /* left-adjusting padding (always blank) */
469
                if (flags & LADJUST)
203 killagreg 470
                        PAD_SP(pPutchar, width - fieldsz);
1 ingob 471
        }
472
done:
473
        va_end(ap);
474
}