Subversion Repositories MK3Mag

Rev

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

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