-
Notifications
You must be signed in to change notification settings - Fork 1
/
printf.c
384 lines (353 loc) · 11.7 KB
/
printf.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
/* -*- Mode: C; Character-encoding: utf-8; -*- */
/* Copyright (C) 2004-2019 beingmeta, inc.
Copyright (C) 2020-2022 Kenneth Haase ([email protected])
This file is part of the libu8 UTF-8 unicode library.
This program comes with absolutely NO WARRANTY, including implied
warranties of merchantability or fitness for any particular
purpose.
Use, modification, and redistribution of this program is permitted
under any of the licenses found in the the 'licenses' directory
accompanying this distribution, including the GNU General Public License
(GPL) Version 2 or the GNU Lesser General Public License.
*/
#include "libu8/u8source.h"
#include "libu8/libu8.h"
#ifndef _FILEINFO
#define _FILEINFO __FILE__
#endif
#include "libu8/libu8io.h"
#include "libu8/u8streamio.h"
#include "libu8/u8stringfns.h"
#include "libu8/u8printf.h"
#if HAVE_LIBINTL_H
#include <libintl.h>
#endif
#include <stdarg.h>
#ifndef PRINTF_CHUNK_SIZE
#define PRINTF_CHUNK_SIZE 1234
#endif
int u8_printf_underscore_ints = -1;
u8_condition u8_BadPrintFormat=_("u8_printf: Bad format code");
#if ((U8_THREADS_ENABLED) && (HAVE_GETTEXT))
static u8_mutex textdomains_lock;
static u8_mutex gettext_lock;
#endif
/* Message lookup */
#if HAVE_GETTEXT
static struct U8_TEXTDOMAIN {
const char *domain;
u8_mutex gettext_lock;
u8_xlatefn xlate;
struct U8_TEXTDOMAIN *next;} *textdomains=NULL;
static u8_string getmessage(u8_string msg)
{
u8_string result=msg;
struct U8_TEXTDOMAIN *scan=textdomains;
while (scan) {
u8_string newstring;
if (scan->xlate)
newstring=scan->xlate(msg);
else {
u8_lock_mutex(&(scan->gettext_lock));
newstring=(u8_string)dgettext(scan->domain,msg);
u8_unlock_mutex(&(scan->gettext_lock));}
if ( (newstring==NULL) || (newstring==msg) )
scan=scan->next;
else return newstring;}
u8_lock_mutex(&(gettext_lock));
result=dgettext("libu8",msg);
u8_unlock_mutex(&(gettext_lock));
return result;
}
U8_EXPORT
/* u8_register_textdomain:
Arguments: a gettext domain and an encoding
Returns: void
Records the text domain for message translation by u8_printf.
*/
void u8_register_textdomain(char *domain)
{
struct U8_TEXTDOMAIN *scan, *new;
u8_lock_mutex(&textdomains_lock);
scan=textdomains;
while (scan) {
if (strcmp(scan->domain,domain)==0) {
u8_unlock_mutex(&textdomains_lock);
return;}
else scan=scan->next;}
new=u8_alloc(struct U8_TEXTDOMAIN);
new->domain=u8_strdup(domain);
new->xlate=NULL;
u8_init_mutex(&(new->gettext_lock));
new->next=textdomains;
textdomains=new;
u8_unlock_mutex(&textdomains_lock);
}
U8_EXPORT
/* u8_register_xlatefn:
Arguments: a translation function
Returns: void
Adds a translation function to the lookup list.
*/
void u8_register_xlatefn(u8_xlatefn fn)
{
}
U8_EXPORT
/* u8_getmessage:
Arguments: a string
Returns: a string
Searches all registered text domains for translations for a particular
message string.
*/
u8_string u8_getmessage(u8_string msg)
{
return getmessage(msg);
}
#else
#define getmessage(x) (x)
#endif
/* Output integers with separators */
char *write_int_helper(unsigned long long int num,char *buf)
{
int base = 10;
unsigned long long mult = 10000000000000000000ULL;
const char *digits="0123456789";
char *write=buf;
if (num==0) {
*write++='0'; *write++='\0';
return buf;}
unsigned long long int reduce=num;
int started=0; while (mult>0) {
int weight=(reduce/mult)%base;
if (started) *write++=digits[weight];
else if (weight) {
*write++=digits[weight];
started=1;}
else NO_ELSE;
reduce=reduce%mult;
mult=mult/base;}
*write++='\0';
return buf;
}
/* Extended printf */
u8_printf_handler u8_printf_handlers[128];
#define printf_codep(c) ((c<128)&&(u8_printf_handlers[(int)c]!=NULL))
/* This is the key internal function for normal printf, which checks
for a translation of the format string, calls message_printf if there
is one, and otherwise just processes the format string and arguments
in order. */
int u8_do_printf(u8_output s,u8_string format_string,va_list *args)
{
int n_directives=0;
const unsigned char *scan=format_string, *fmt=strchr(scan,'%');
while (fmt) {
unsigned char cmd[16]; int i=0, code; u8_string to_free=NULL;
/* First, output everything leading up to the % sign */
if (fmt>scan) {
u8_putn(s,scan,fmt-scan); scan=fmt;}
/* Read the percent sign */
cmd[i++]=*scan++;
/* Read in the format code */
while ((i<16) && (*scan) && (!(printf_codep(*scan))))
cmd[i++]=*scan++;
/* Read the final byte */
cmd[i++]=code=*scan++; cmd[i]='\0'; n_directives++;
if (code == '%') u8_putc(s,'%');
else if ( (code == 'd') &&
(u8_printf_underscore_ints!=0) &&
( (u8_printf_underscore_ints>0) || (strchr(cmd,'_')) ) ) {
char buf[64];
long long intval;
if (strstr(cmd,"ll"))
intval = (long long) va_arg(*args,long long);
else if (strstr(cmd,"l"))
intval = (long long) va_arg(*args,long);
else intval = (long long) va_arg(*args,int);
int sign = (intval<0) ? (-1) : (0);
if (sign<0) intval=-intval;
u8_string digits = write_int_helper(intval,buf);
int place = strlen(digits);
u8_string scan = digits;
if (sign<0) u8_putc(s,'-');
while (*scan) {
if ( (scan>digits) && ((place%3)==0) ) u8_putc(s,'_');
u8_putc(s,*scan);
place--;
scan++;}}
else {
unsigned char buf[PRINTF_CHUNK_SIZE], *string=buf;
if ((code == 'd') || (code == 'i') ||
(code == 'u') || (code == 'o') ||
(code == 'x')) {
/* various kinds of integers */
if (strstr(cmd,"ll")) {
long long i=va_arg(*args,long long);
sprintf(buf,cmd,i);}
else if (strstr(cmd,"l")) {
long i=va_arg(*args,long);
sprintf(buf,cmd,i);}
else {
int i=va_arg(*args,int);
sprintf(buf,cmd,i);}}
else if (code == 'p') {
void *ptr = va_arg(*args,void *);
sprintf(buf,"0x%llx",((unsigned long long)ptr));}
else if ((code == 'f') || (code == 'g') || (code == 'e')) {
/* various kinds of floats */
double f=va_arg(*args,double);
sprintf(buf,cmd,f);}
else if ((code == 's')||(code == 'm')) {
/* strings and messages */
char *prefix=NULL;
char *arg=va_arg(*args,char *); string=arg;
/* A - modifer on s indicates that the string arg should be
freed after use. A modifier # indicates that the argument
is a libc string and will need to be converted to UTF-8.
Modifiers l and u indicates upper and lower case conversions.
Further :/,.? modifiers indicate a prefix character which
precedes the string when the string is not empty. */
if (strchr(cmd,'/')) prefix="/";
else if (strchr(cmd,':')) prefix=":";
else if (strchr(cmd,'.')) prefix=".";
else if (strchr(cmd,',')) prefix=", ";
else NO_ELSE;
if (strchr(cmd,'-')) to_free=arg;
/* The m conversion is like s but passes its argument through the
message catalog. */
if ((arg)&&(code=='m'))
/* 'm' indicates a message to be 'translated,' typically using gettext */
arg=(u8_byte *)getmessage(arg);
if ((string) && (strchr(cmd,'#'))) {
/* '#' indicates a string which isn't UTF-8. This isn't always
used consistently. */
string=(u8_byte *)u8_fromlibc(string);
if (to_free) u8_free(to_free);
to_free=string;}
if (arg==NULL) {
if ((prefix)||(strchr(cmd,'?')))
string="";
else string="(null)";}
else if (strchr(cmd,'l')) {
string=(u8_byte *)u8_downcase(string);
if (to_free) u8_free(to_free);
to_free=string;}
else if (strchr(cmd,'u')) {
string=(u8_byte *)u8_upcase(string);
if (to_free) u8_free(to_free);
to_free=string;}
else NO_ELSE;
if ((arg)&&(prefix)) {
string=(u8_byte *)u8_string_append(prefix,string,NULL);
if (to_free) u8_free(to_free);
to_free=string;}}
else if (code == 'v') { /* This is for vectors of scalars */
unsigned char *start=va_arg(*args,unsigned char *);
unsigned char *scan=start, *limit;
ssize_t len=
((strchr(cmd,'l'))?(va_arg(*args,ssize_t)):(va_arg(*args,int)));
if (len>=0) limit=start+len;
else {limit=start; while (*limit) limit++;}
while (scan<limit) {
int ch=*scan++; char buf[8];
if (scan<limit) sprintf(buf,"%02x:",ch);
else sprintf(buf,"%02x",ch);
u8_puts(s,buf);}
string=NULL;}
else if (code == 'c') {
/* This is for unicode character codepoints */
unsigned int codepoint = va_arg(*args,int);
u8_putc(s,codepoint); string = NULL; }
else if ((code<128) && (u8_printf_handlers[(int)code]))
/* We pass the pointer args because some stdarg implementations
work better that way. */
string=(u8_byte *)u8_printf_handlers[(int)code]
(s,cmd,buf,PRINTF_CHUNK_SIZE,args);
else return u8_reterr(u8_BadPrintFormat,"u8_do_printf",u8_strdup(cmd));
if (string == NULL) {} else u8_puts(s,string);
if (to_free) u8_free(to_free);}
fmt=strchr(scan,'%');}
u8_puts(s,scan); /* At the end, output the tail of the format string */
return n_directives;
}
U8_EXPORT
/* u8_printf
Arguments: a string stream, a format string, and other args
Returns: void
Outputs a string to string stream generated from the format string and
using the provided arguments. Much like printf (surprise). */
int u8_printf(u8_output s,u8_string format_string,...)
{
va_list args; int retval;
va_start(args,format_string);
retval=u8_do_printf(s,format_string,&args);
va_end(args);
return retval;
}
U8_EXPORT
/* u8_mkstring
Arguments: a format string, and other args
Returns: a malloc'd string whose contains are generated from the arguments
*/
u8_string u8_mkstring(u8_string format_string,...)
{
struct U8_OUTPUT out; va_list args; int retval=0;
U8_INIT_STATIC_OUTPUT(out,128);
va_start(args,format_string);
if ((retval=u8_do_printf(&out,format_string,&args))<0) {
va_end(args);
u8_free(out.u8_outbuf); return NULL;}
else {
va_end(args);
return out.u8_outbuf;}
}
U8_EXPORT
/* u8_sprintf:
Arguments: a string buffer, its length, a format string, and other args
Returns: a malloc'd string whose contains are generated from the arguments
*/
u8_string u8_sprintf
(unsigned char *buf_arg,size_t buflen,u8_string format_string,...)
{
struct U8_OUTPUT out; va_list args; int retval=0;
u8_byte *buf = (buf_arg) ? (buf_arg) : (u8_malloc(buflen));
U8_INIT_FIXED_OUTPUT(&out,buflen,buf);
va_start(args,format_string);
if ((retval=u8_do_printf(&out,format_string,&args))<0) {
va_end(args);
u8_free(out.u8_outbuf); return NULL;}
else {
va_end(args);
return out.u8_outbuf;}
}
static u8_string default_printf_handler
(u8_output s,char *cmd,u8_byte *buf,int bufsiz,va_list *args)
{
return NULL;
}
void init_printf_c()
{
/* These are all really no-ops, but they help with parsing. */
u8_printf_handlers['s']=default_printf_handler;
u8_printf_handlers['p']=default_printf_handler;
u8_printf_handlers['m']=default_printf_handler;
u8_printf_handlers['d']=default_printf_handler;
u8_printf_handlers['u']=default_printf_handler;
u8_printf_handlers['o']=default_printf_handler;
u8_printf_handlers['x']=default_printf_handler;
u8_printf_handlers['e']=default_printf_handler;
u8_printf_handlers['f']=default_printf_handler;
u8_printf_handlers['g']=default_printf_handler;
u8_printf_handlers['v']=default_printf_handler;
u8_printf_handlers['%']=default_printf_handler;
#if ((U8_THREADS_ENABLED) && (HAVE_GETTEXT))
u8_init_mutex(&textdomains_lock);
u8_init_mutex(&gettext_lock);
#endif
u8_register_source_file(_FILEINFO);
}
/* Emacs local variables
;;; Local variables: ***
;;; compile-command: "make debugging;" ***
;;; indent-tabs-mode: nil ***
;;; End: ***
*/