source: trunk/sys/dietlibc/__v_printf.c @ 1

Last change on this file since 1 was 1, checked in by alain, 5 years ago

First import

File size: 8.1 KB
Line 
1#include "dietfeatures.h"
2#include "dietstdio.h"
3#include "dietwarning.h"
4
5#include <stdarg.h>
6#include <stdlib.h>
7#include <string.h>
8#include <errno.h>
9
10
11#define MAX_WIDTH 10*1024
12
13static inline unsigned long skip_to(const char *format) {
14  unsigned long nr;
15  for (nr=0; format[nr] && (format[nr]!='%'); ++nr);
16  return nr;
17}
18
19#define A_WRITE(fn,buf,sz)      ((fn)->put((void*)(buf),(sz),(fn)->data))
20#define B_WRITE(fn,buf,sz)      { if ((unsigned long)(sz) > (((unsigned long)(int)(-1))>>1) || len+(int)(sz)<len) return -1; A_WRITE(fn,buf,sz); } while (0)
21
22static const char pad_line[2][16]= { "                ", "0000000000000000", };
23static int write_pad(unsigned int* dlen,struct arg_printf* fn, unsigned int len, int padwith) {
24  int nr=0;
25  if ((int)len<=0) return 0;
26  if(*dlen+len<len) return -1;
27  for (;len>15;len-=16,nr+=16) {
28    A_WRITE(fn,pad_line[(padwith=='0')?1:0],16);
29  }
30  if (len>0) {
31    A_WRITE(fn,pad_line[(padwith=='0')?1:0],(unsigned int)len); nr+=len;
32  }
33  *dlen += nr;
34  return 0;
35}
36
37int __v_printf(struct arg_printf* fn, const char *format, va_list arg_ptr)
38{
39  unsigned int len=0;
40#ifdef WANT_ERROR_PRINTF
41  int _errno = errno;
42#endif
43
44  while (*format) {
45    unsigned long sz = skip_to(format);
46    if (sz) {
47      B_WRITE(fn,format,sz); len+=sz;
48      format+=sz;
49    }
50    if (*format=='%') {
51      char buf[128];
52      union { char*s; } u_str;
53#define s u_str.s
54
55      int retval;
56      unsigned char ch, padwith=' ', precpadwith=' ';
57
58      char flag_in_sign=0;
59      char flag_upcase=0;
60      char flag_hash=0;
61      char flag_left=0;
62      char flag_space=0;
63      char flag_sign=0;
64      char flag_dot=0;
65      signed char flag_long=0;
66
67      unsigned int base;
68      unsigned int width=0, preci=0;
69
70      long number=0;
71#ifdef WANT_LONGLONG_PRINTF
72      long long llnumber=0;
73#endif
74
75      ++format;
76inn_printf:
77      switch(ch=*format++) {
78      case 0:
79        return -1;
80        break;
81
82      /* FLAGS */
83      case '#':
84        flag_hash=-1;
85
86      case 'h':
87        --flag_long;
88        goto inn_printf;
89#if __WORDSIZE != 64
90      case 'j':
91#endif
92      case 'q':         /* BSD ... */
93      case 'L':
94        ++flag_long; /* fall through */
95#if __WORDSIZE == 64
96      case 'j':
97#endif
98      case 'z':
99      case 'l':
100        ++flag_long;
101        goto inn_printf;
102
103      case '-':
104        flag_left=1;
105        goto inn_printf;
106
107      case ' ':
108        flag_space=1;
109        goto inn_printf;
110
111      case '+':
112        flag_sign=1;
113        goto inn_printf;
114
115      case '0':
116      case '1':
117      case '2':
118      case '3':
119      case '4':
120      case '5':
121      case '6':
122      case '7':
123      case '8':
124      case '9':
125        if(flag_dot) return -1;
126        width=strtoul(format-1,(char**)&s,10);
127        if (width>MAX_WIDTH) return -1;
128        if (ch=='0' && !flag_left) padwith='0';
129        format=s;
130        goto inn_printf;
131
132      case '*':
133        {
134          /* A negative field width is taken as a '-' flag followed by
135           * a positive field width
136           **/
137          int tmp;
138          if ((tmp=va_arg(arg_ptr,int))<0) {
139            flag_left=1;
140            tmp=-tmp;
141          }
142          if ((width=(unsigned long)tmp)>MAX_WIDTH) return -1;
143          goto inn_printf;
144        }
145      case '.':
146        flag_dot=1;
147        if (*format=='*') {
148          int tmp=va_arg(arg_ptr,int);
149          preci=tmp<0?0:tmp;
150          ++format;
151        } else {
152          long int tmp=strtol(format,(char**)&s,10);
153          preci=tmp<0?0:tmp;
154          format=s;
155        }
156        if (preci>MAX_WIDTH) return -1;
157        goto inn_printf;
158
159      /* print a char or % */
160      case 'c':
161        ch=(char)va_arg(arg_ptr,int);
162      case '%':
163        B_WRITE(fn,&ch,1); ++len;
164        break;
165
166#ifdef WANT_ERROR_PRINTF
167      /* print an error message */
168      case 'm':
169        s=strerror(_errno);
170        sz=strlen(s);
171        B_WRITE(fn,s,sz); len+=sz;
172        break;
173#endif
174      /* print a string */
175      case 's':
176        s=va_arg(arg_ptr,char *);
177#ifdef WANT_NULL_PRINTF
178        if (!s) s="(null)";
179#endif
180        sz = strlen(s);
181        if (flag_dot && sz>preci) sz=preci;
182        preci=0;
183        flag_dot^=flag_dot;
184        padwith=precpadwith=' ';
185
186print_out:
187      {
188        char *sign=s;
189        int todo=0;
190        int vs;
191       
192        if (! (width||preci) ) {
193          B_WRITE(fn,s,sz); len+=sz;
194          break;
195        }
196       
197        if (flag_in_sign) todo=1;
198        if (flag_hash>0)  todo=flag_hash;
199        if (todo) {
200          s+=todo;
201          sz-=todo;
202          width-=todo;
203        }
204
205        /* These are the cases for 1234 or "1234" respectively:
206              %.6u -> "001234"
207              %6u  -> "  1234"
208              %06u -> "001234"
209              %-6u -> "1234  "
210              %.6s -> "1234"
211              %6s  -> "  1234"
212              %06s -> "  1234"
213              %-6s -> "1234  "
214              %6.5u -> " 01234"
215              %6.5s -> "  1234"
216           In this code, for %6.5s, 6 is width, 5 is preci.
217           flag_dot means there was a '.' and preci is set.
218           flag_left means there was a '-'.
219           sz is 4 (strlen("1234")).
220           padwith will be '0' for %06u, ' ' otherwise.
221           precpadwith is '0' for %u, ' ' for %s.
222         */
223
224        if (flag_dot && width==0) width=preci;
225        if (!flag_dot) preci=sz;
226        if (!flag_left && padwith==' ') { /* do left-side padding with spaces */
227          if (write_pad(&len,fn,width-preci,padwith))
228            return -1;
229        }
230        if (todo) {
231          B_WRITE(fn,sign,todo);
232          len+=todo;
233        }
234        if (!flag_left && padwith!=' ') { /* do left-side padding with '0' */
235          if (write_pad(&len,fn,width-preci,padwith))
236            return -1;
237        }
238        /* do preci padding */
239        if (write_pad(&len,fn,preci-sz,precpadwith))
240          return -1;
241        /* write actual string */
242        B_WRITE(fn,s,sz); len+=sz;
243        if (flag_left) {
244          if (write_pad(&len,fn,width-preci,padwith))
245            return -1;
246        }
247       
248        break;
249      }
250
251      /* print an integer value */
252      case 'b':
253        base=2;
254        sz=0;
255        goto num_printf;
256      case 'p':
257        flag_hash=2;
258        flag_long=1;
259        ch='x';
260      case 'X':
261        flag_upcase=(ch=='X');
262      case 'x':
263        base=16;
264        sz=0;
265        if (flag_hash) {
266          buf[1]='0';
267          buf[2]=ch;
268          flag_hash=2;
269          sz=2;
270        }
271        if (preci>width) width=preci;
272        goto num_printf;
273      case 'd':
274      case 'i':
275        flag_in_sign=1;
276      case 'u':
277        base=10;
278        sz=0;
279        goto num_printf;
280      case 'o':
281        base=8;
282        sz=0;
283        if (flag_hash) {
284          buf[1]='0';
285          flag_hash=1;
286          ++sz;
287        }
288
289num_printf:
290        s=buf+1;
291
292        if (flag_long>0) {
293#ifdef WANT_LONGLONG_PRINTF
294          if (flag_long>1)
295            llnumber=va_arg(arg_ptr,long long);
296          else
297#endif
298            number=va_arg(arg_ptr,long);
299        }
300        else {
301          number=va_arg(arg_ptr,int);
302          if (sizeof(int) != sizeof(long) && !flag_in_sign)
303            number&=((unsigned int)-1);
304        }
305
306        if (flag_in_sign) {
307#ifdef WANT_LONGLONG_PRINTF
308          if ((flag_long>1)&&(llnumber<0)) {
309            llnumber=-llnumber;
310            flag_in_sign=2;
311          } else
312#endif
313            if (number<0) {
314              number=-number;
315              flag_in_sign=2;
316            }
317        }
318        if (flag_long<0) number&=0xffff;
319        if (flag_long<-1) number&=0xff;
320#ifdef WANT_LONGLONG_PRINTF
321        if (flag_long>1)
322          retval = __lltostr(s+sz,sizeof(buf)-5,(unsigned long long) llnumber,base,flag_upcase);
323        else
324#endif
325          retval = __ltostr(s+sz,sizeof(buf)-5,(unsigned long) number,base,flag_upcase);
326
327        /* When 0 is printed with an explicit precision 0, the output is empty. */
328        if (flag_dot && retval == 1 && s[sz] == '0') {
329          if (preci == 0||flag_hash > 0) {
330            sz = 0;
331          }
332          flag_hash = 0;
333        } else sz += retval;
334
335        if (flag_in_sign==2) {
336          *(--s)='-';
337          ++sz;
338        } else if ((flag_in_sign)&&(flag_sign || flag_space)) {
339          *(--s)=(flag_sign)?'+':' ';
340          ++sz;
341        } else flag_in_sign=0;
342
343        precpadwith='0';
344
345        goto print_out;
346
347#ifdef WANT_FLOATING_POINT_IN_PRINTF
348      /* print a floating point value */
349      case 'f':
350      case 'g':
351        {
352          int g=(ch=='g');
353          double d=va_arg(arg_ptr,double);
354          s=buf+1;
355          if (width==0) width=1;
356          if (!flag_dot) preci=6;
357          if (flag_sign || d < +0.0) flag_in_sign=1;
358
359          sz=__dtostr(d,s,sizeof(buf)-1,width,preci,g);
360
361          if (flag_dot) {
362            char *tmp;
363            if ((tmp=strchr(s,'.'))) {
364              if (preci || flag_hash) ++tmp;
365              while (preci>0 && *++tmp) --preci;
366              *tmp=0;
367            } else if (flag_hash) {
368              s[sz]='.';
369              s[++sz]='\0';
370            }
371          }
372
373          if (g) {
374            char *tmp,*tmp1;    /* boy, is _this_ ugly! */
375            if ((tmp=strchr(s,'.'))) {
376              tmp1=strchr(tmp,'e');
377              while (*tmp) ++tmp;
378              if (tmp1) tmp=tmp1;
379              while (*--tmp=='0') ;
380              if (*tmp!='.') ++tmp;
381              *tmp=0;
382              if (tmp1) strcpy(tmp,tmp1);
383            }
384          }
385
386          if ((flag_sign || flag_space) && d>=0) {
387            *(--s)=(flag_sign)?'+':' ';
388            ++sz;
389          }
390         
391          sz=strlen(s);
392          if (width<sz) width=sz;
393          precpadwith='0';
394          flag_dot=0;
395          flag_hash=0;
396          goto print_out;
397        }
398#endif
399
400      default:
401        break;
402      }
403    }
404  }
405  return len;
406}
407
408link_warning("__v_printf","warning: the printf functions add several kilobytes of bloat.")
Note: See TracBrowser for help on using the repository browser.