Rev 193 | Rev 268 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
259 | daniel-mar | 1 | /* |
2 | This file is part of a common library |
||
3 | Copyright (C) 2002-6 Toby Thain, toby@telegraphics.com.au |
||
4 | |||
5 | This program is free software; you can redistribute it and/or modify |
||
6 | it under the terms of the GNU General Public License as published by |
||
7 | the Free Software Foundation; either version 2 of the License, or |
||
8 | (at your option) any later version. |
||
9 | |||
10 | This program 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 program; if not, write to the Free Software |
||
17 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
||
18 | */ |
||
19 | |||
20 | #include <stdarg.h> |
||
21 | #include <string.h> // memcpy |
||
22 | |||
23 | #include "sprintf_tiny.h" |
||
24 | #include "str.h" |
||
25 | |||
26 | // integer exponentiation; |
||
27 | // may not be more efficient than repeated multiplication, for small n |
||
28 | // (a small lookup table would be much quicker!) |
||
29 | // refer: http://www-mitpress.mit.edu/sicp/chapter1/node15.html |
||
30 | // and http://www.uvsc.edu/profpages/merrinst/exponentiation_and_java.html |
||
31 | long int_exp(long b,int n){ |
||
32 | long temp; |
||
33 | if(n&1) |
||
34 | return b*int_exp(b,n^1); |
||
35 | else |
||
36 | return n ? ( temp = int_exp(b,n>>1),temp*temp ) : 1; |
||
37 | } |
||
38 | |||
39 | // format a signed integer as a decimal string |
||
40 | // returns pointer after last character copied |
||
41 | // (easily generalised to other bases) |
||
42 | // *** this is reimplemented here mainly to avoid |
||
43 | // *** global data, a dealbreaker on MPW 68K |
||
44 | |||
45 | #define INT_BASE 10 |
||
46 | |||
47 | // format a positive integer; return pointer after last digit |
||
48 | // DON'T add trailing NUL |
||
49 | char *udigits(char *p,unsigned long x,int base){ |
||
50 | long m = x/base; |
||
51 | if(m) |
||
52 | p = udigits(p,m,base); |
||
53 | *p++ = (x%base)+'0'; |
||
54 | return p; |
||
55 | } |
||
56 | // format a possibly negative integer; |
||
57 | // return pointer to trailing NUL (after last digit) |
||
58 | char *int_str(char *dst,long x,int base){ |
||
59 | char *p = dst; |
||
60 | if(x<0){ // handle negative case |
||
61 | *p++ = '-'; |
||
62 | x = -x; |
||
63 | } |
||
64 | p = udigits(p,x,base); |
||
65 | *p = 0; // add terminating null |
||
66 | return p; |
||
67 | } |
||
68 | |||
69 | // format unsigned integer into n decimal places, zero padded |
||
70 | // return pointer to trailing NUL (after last digit) |
||
71 | char *int_strpad(char *dst,unsigned long x,int places,int base){ |
||
72 | char *p; |
||
73 | // generate digits, storing last to first |
||
74 | for(p=dst+places;p!=dst;x/=base) |
||
75 | *(--p) = (x%base)+'0'; |
||
76 | dst[places] = 0; // add terminating null |
||
77 | return dst+places; |
||
78 | } |
||
79 | |||
80 | // quick and dirty way to format a double value as a string |
||
81 | // with a certain number of decimal places |
||
82 | // note INT_BASE^decplaces must be representable as signed long |
||
83 | // therefore WILL FAIL FOR (base 10) decplaces > 9 !! |
||
84 | // will also fail for fp values > 2^31-1 |
||
85 | char *float_str(char *dst,double x,int places){ |
||
86 | char *q = dst; |
||
87 | |||
88 | // format integer part |
||
89 | q = int_str(q,(long)x /*truncated towards zero*/,INT_BASE); |
||
90 | *q++ = '.'; |
||
91 | |||
92 | // format fractional part |
||
93 | return int_strpad(q,(x-(long)x)*int_exp(INT_BASE,places)+.5,places,INT_BASE); |
||
94 | } |
||
95 | |||
96 | // *** this is here because the stdio sprintf() |
||
97 | // *** has global data, which breaks a 68K code resource build. |
||
98 | // *** (also, sprintf() brings a lot of unnecessary baggage) |
||
99 | |||
100 | int vsprintf_tiny(char *s,char *fmt,va_list v){ |
||
101 | char *p,*q; |
||
102 | |||
103 | for(p=fmt,q=s;*p;) |
||
104 | if(*p == '%'){ |
||
105 | int lflag = 0; |
||
106 | ++p; // eat '%' |
||
107 | if(*p == 'l'){ |
||
108 | lflag = 1; |
||
109 | ++p; // eat 'l' |
||
110 | } |
||
111 | switch(*p){ |
||
112 | case 'c': *q++ = va_arg(v,int); break; |
||
113 | // case 'o': q = udigits(q,lflag ? va_arg(v,unsigned long) : va_arg(v,unsigned int),8); break; |
||
114 | case 'i': |
||
115 | case 'd': q = int_str(q,lflag ? va_arg(v,long) : va_arg(v,int),10); break; |
||
116 | case 'u': q = udigits(q,lflag ? va_arg(v,unsigned long) : va_arg(v,unsigned int),10); break; |
||
117 | // case 'x': q = udigits(q,lflag ? va_arg(v,unsigned long) : va_arg(v,unsigned int),16); break; |
||
118 | case 'F': q = float_str(q,va_arg(v,double),1); break; |
||
119 | case 'g': |
||
120 | case 'f': q = float_str(q,va_arg(v,double),4); break; |
||
121 | case 's': q = cat(q,va_arg(v,char*)); break; |
||
122 | default: *q++ = *p; |
||
123 | } |
||
124 | ++p; // eat format character |
||
125 | }else |
||
126 | *q++ = *p++; |
||
127 | |||
128 | *q = 0; |
||
129 | return q - s; |
||
130 | } |
||
131 | |||
132 | int sprintf_tiny(char *s,char *fmt,...){ |
||
133 | va_list v; |
||
134 | int n; |
||
135 | |||
136 | va_start(v,fmt); |
||
137 | n = vsprintf_tiny(s,fmt,v); |
||
138 | va_end(v); |
||
139 | return n; |
||
140 | } |