Skip to content
Snippets Groups Projects
Commit 1ecbb35d authored by Deucе's avatar Deucе :ok_hand_tone4:
Browse files

Add support for the '$' field specifier delimiter thing.

Just in case xp_asprintf() is used for text.dat strings.
The field specifier allows you to change the order of parameters,
which is useful for translating (and useless for anything else).

There must not be any "skipped" parameters though, you must use
all of them between the first one and the last one you use.
parent e6680f74
No related branches found
No related tags found
No related merge requests found
Pipeline #7710 failed
...@@ -61,3 +61,6 @@ $(XPDEV-MT_SHLIB_BUILD): $(MTOBJS) | $(MTOBJODIR) ...@@ -61,3 +61,6 @@ $(XPDEV-MT_SHLIB_BUILD): $(MTOBJS) | $(MTOBJODIR)
@echo Creating $@ @echo Creating $@
$(QUIET)$(MKSHLIB) $(LDFLAGS) $(MTOBJS) $(SHLIBOPTS) -o $@ $(QUIET)$(MKSHLIB) $(LDFLAGS) $(MTOBJS) $(SHLIBOPTS) -o $@
xpprintf_test: xpprintf.c $(DEPS)
@echo Linking $@
$(QUIET)$(CC) $(CFLAGS) $(MT_CFLAGS) -DXP_PRINTF_TEST $< -o $@ $(LDFLAGS) $(MT_LDFLAGS) $(XPDEV-MT_LIB_BUILD) -lm $(XPDEV-MT_LIBS)
...@@ -68,11 +68,91 @@ int asprintf(char **strptr, const char *format, ...) ...@@ -68,11 +68,91 @@ int asprintf(char **strptr, const char *format, ...)
/* Maximum length of a format specifier including the % */ /* Maximum length of a format specifier including the % */
#define MAX_FORMAT_LEN 256 #define MAX_FORMAT_LEN 256
struct arg_table_entry {
int type;
union {
int int_type;
unsigned uint_type;
long long_type;
unsigned long ulong_type;
#if defined(XP_PRINTF_TYPE_LONGLONG)
long long longlong_type;
#endif
#if defined(XP_PRINTF_TYPE_ULONGLONG)
unsigned long long ulonglong_type;
#endif
char *charp_type;
double double_type;
long double longdouble_type;
void *voidp_type;
#if 0
intmax_t intmax_type;
uintmax_t uintmax_type;
ptrdiff_t ptrdiff_type;
#endif
size_t size_type;
};
};
void xp_asprintf_free(char *format) void xp_asprintf_free(char *format)
{ {
free(format); free(format);
} }
int xp_printf_get_next(char *format)
{
char buf[6];
size_t bufpos = 0;
int j = 1;
long l;
if (!*(size_t *)format)
return (-1);
char *p = format + *(size_t *)format;
if (*p != '%')
return(-1);
p++;
while (j) {
switch(*p) {
case '0':
if (j > 1)
buf[bufpos++] = *p++;
else
j = 0;
break;
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
j = 2;
buf[bufpos++] = *p++;
if (bufpos == sizeof(buf))
return -1;
break;
case '$':
buf[bufpos++] = 0;
j = 0;
break;
default:
j = 0;
break;
}
}
if (*p == '$' && bufpos) {
l = strtol(buf, NULL, 10);
if (l > 0 && l < 100000)
return l;
return -1;
}
return 0;
}
int xp_printf_get_type(const char *format) int xp_printf_get_type(const char *format)
{ {
const char *p; const char *p;
...@@ -87,6 +167,41 @@ int xp_printf_get_type(const char *format) ...@@ -87,6 +167,41 @@ int xp_printf_get_type(const char *format)
return(0); return(0);
p++; p++;
/*
* Skip field specifier (zero or one)
* Do not allow a leading zero
*/
j = 1;
while (j) {
switch(*p) {
case '0':
if (j > 1)
p++;
else
j = 0;
break;
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
p++;
j = 2;
break;
case '$':
p++;
j = 0;
break;
default:
j = 0;
break;
}
}
/* /*
* Skip flags (zero or more) * Skip flags (zero or more)
*/ */
...@@ -288,13 +403,38 @@ int xp_printf_get_type(const char *format) ...@@ -288,13 +403,38 @@ int xp_printf_get_type(const char *format)
return(correct_type); return(correct_type);
} }
/*
* Search for next non-%% separator and set offset
* to zero if none found for wrappers to know when
* they're done.
*/
static const char *
find_next_replacement(char *format)
{
/*
* Find the next non %% format, leaving %% as it is
*/
char *p;
for (p = format + *(size_t *)format + 1; *p; p++) {
if (*p == '%') {
if (*(p + 1) == '%')
p++;
else
break;
}
}
if (!*p)
*(size_t *)format = 0;
else
*(size_t *)format = p - format;
return (format);
}
/* /*
* Performs the next replacement in format using the variable * Performs the next replacement in format using the variable
* specified as the only vararg which is currently the type * specified as the only vararg which is currently the type
* specified in type (defined in xpprintf.h). * specified in type (defined in xpprintf.h).
* *
* Does not currently support the $ argument selector.
*
* Currently, the type is not overly useful, but this could be used for * Currently, the type is not overly useful, but this could be used for
* automatic type conversions (ie: int to char *). Right now it just assures * automatic type conversions (ie: int to char *). Right now it just assures
* that the type passed to sprintf() is the type passed to * that the type passed to sprintf() is the type passed to
...@@ -326,6 +466,7 @@ char* xp_asprintf_next(char *format, int type, ...) ...@@ -326,6 +466,7 @@ char* xp_asprintf_next(char *format, int type, ...)
char *entry; char *entry;
char this_format[MAX_FORMAT_LEN]; char this_format[MAX_FORMAT_LEN];
char *fmt; char *fmt;
char *fmt_nofield;
int modifier=0; int modifier=0;
int correct_type=0; int correct_type=0;
char num_str[128]; /* More than enough room for a 256-bit int */ char num_str[128]; /* More than enough room for a 256-bit int */
...@@ -340,9 +481,46 @@ char* xp_asprintf_next(char *format, int type, ...) ...@@ -340,9 +481,46 @@ char* xp_asprintf_next(char *format, int type, ...)
offset=p-format; offset=p-format;
format_len=*(size_t *)(format+sizeof(size_t))+sizeof(size_t)*2+1; format_len=*(size_t *)(format+sizeof(size_t))+sizeof(size_t)*2+1;
this_format[0]=0; this_format[0]=0;
fmt=this_format; fmt=fmt_nofield=this_format;
*(fmt++)=*(p++); *(fmt++)=*(p++);
/*
* Skip field specifier (zero or one)
* Do not allow a leading zero
*/
j = 1;
while (j) {
switch(*p) {
case '0':
if (j > 1)
*(fmt++)=*(p++);
else
j = 0;
break;
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
*(fmt++)=*(p++);
j = 2;
break;
case '$':
fmt_nofield = fmt;
*(fmt++)='%';
p++;
j = 0;
break;
default:
j = 0;
break;
}
}
/* /*
* Skip flags (zero or more) * Skip flags (zero or more)
*/ */
...@@ -1185,42 +1363,42 @@ char* xp_asprintf_next(char *format, int type, ...) ...@@ -1185,42 +1363,42 @@ char* xp_asprintf_next(char *format, int type, ...)
switch(type) { switch(type) {
case XP_PRINTF_TYPE_CHAR: /* Also includes char and short */ case XP_PRINTF_TYPE_CHAR: /* Also includes char and short */
case XP_PRINTF_TYPE_INT: /* Also includes char and short */ case XP_PRINTF_TYPE_INT: /* Also includes char and short */
j=asprintf(&entry, this_format, i); j=asprintf(&entry, fmt_nofield, i);
break; break;
case XP_PRINTF_TYPE_UINT: /* Also includes char and short */ case XP_PRINTF_TYPE_UINT: /* Also includes char and short */
j=asprintf(&entry, this_format, ui); j=asprintf(&entry, fmt_nofield, ui);
break; break;
case XP_PRINTF_TYPE_LONG: case XP_PRINTF_TYPE_LONG:
j=asprintf(&entry, this_format, l); j=asprintf(&entry, fmt_nofield, l);
break; break;
case XP_PRINTF_TYPE_ULONG: case XP_PRINTF_TYPE_ULONG:
j=asprintf(&entry, this_format, ul); j=asprintf(&entry, fmt_nofield, ul);
break; break;
#if defined(XP_PRINTF_TYPE_LONGLONG) #if defined(XP_PRINTF_TYPE_LONGLONG)
case XP_PRINTF_TYPE_LONGLONG: case XP_PRINTF_TYPE_LONGLONG:
j=asprintf(&entry, this_format, ll); j=asprintf(&entry, fmt_nofield, ll);
break; break;
case XP_PRINTF_TYPE_ULONGLONG: case XP_PRINTF_TYPE_ULONGLONG:
j=asprintf(&entry, this_format, ull); j=asprintf(&entry, fmt_nofield, ull);
break; break;
#endif #endif
case XP_PRINTF_TYPE_CHARP: case XP_PRINTF_TYPE_CHARP:
if(cp==NULL) if(cp==NULL)
j=asprintf(&entry, this_format, "<null>"); j=asprintf(&entry, fmt_nofield, "<null>");
else else
j=asprintf(&entry, this_format, cp); j=asprintf(&entry, fmt_nofield, cp);
break; break;
case XP_PRINTF_TYPE_DOUBLE: case XP_PRINTF_TYPE_DOUBLE:
j=asprintf(&entry, this_format, d); j=asprintf(&entry, fmt_nofield, d);
break; break;
case XP_PRINTF_TYPE_LONGDOUBLE: case XP_PRINTF_TYPE_LONGDOUBLE:
j=asprintf(&entry, this_format, ld); j=asprintf(&entry, fmt_nofield, ld);
break; break;
case XP_PRINTF_TYPE_VOIDP: case XP_PRINTF_TYPE_VOIDP:
j=asprintf(&entry, this_format, pntr); j=asprintf(&entry, fmt_nofield, pntr);
break; break;
case XP_PRINTF_TYPE_SIZET: case XP_PRINTF_TYPE_SIZET:
j=asprintf(&entry, this_format, s); j=asprintf(&entry, fmt_nofield, s);
break; break;
default: default:
j = -1; j = -1;
...@@ -1332,63 +1510,269 @@ char* xp_asprintf_end(char *format, size_t *lenret) ...@@ -1332,63 +1510,269 @@ char* xp_asprintf_end(char *format, size_t *lenret)
return(format); return(format);
} }
char* xp_vasprintf(const char *format, va_list va) static struct arg_table_entry *
build_arg_table(const char *format, va_list va)
{ {
char *working;
char *next; char *next;
int type; va_list wva;
unsigned maxarg = 0;
static struct arg_table_entry *ret;
// First, count arguments...
next=xp_asprintf_start(format); next=xp_asprintf_start(format);
if(next==NULL) if(next==NULL)
return(NULL); return(NULL);
working=next; int curpos = 0;
while(*(size_t *)working) { while(*(size_t *)next) {
type=xp_printf_get_type(working); int newpos = xp_printf_get_next(next);
switch(type) { if (newpos == -1) {
case 0: free(next);
free(working); return(NULL);
return(NULL); }
if (newpos > 0 && newpos != (curpos + 1))
curpos = newpos;
else
curpos++;
if (curpos > maxarg)
maxarg = curpos;
find_next_replacement(next);
}
free(next);
// Now, allocate the table...
ret = calloc(maxarg, sizeof(*ret));
if (ret == NULL)
return NULL;
// Now go through again and get the types
next=xp_asprintf_start(format);
if(next==NULL) {
free(ret);
return(NULL);
}
curpos = 0;
while(*(size_t *)next) {
int newpos = xp_printf_get_next(next);
if (newpos == -1) {
free(next);
free(ret);
return(NULL);
}
if (newpos > 0 && newpos != (curpos + 1))
curpos = newpos;
else
curpos++;
int type = xp_printf_get_type(next);
if (type == XP_PRINTF_TYPE_AUTO) {
free(next);
free(ret);
return NULL;
}
ret[curpos - 1].type = type;
find_next_replacement(next);
}
free(next);
// And finally, get all the values...
va_copy(wva, va);
for (int i = 0; i < maxarg; i++) {
switch(ret[i].type) {
case XP_PRINTF_TYPE_CHAR: case XP_PRINTF_TYPE_CHAR:
case XP_PRINTF_TYPE_INT: /* Also includes char and short */ case XP_PRINTF_TYPE_INT:
next=xp_asprintf_next(working, type, va_arg(va, int)); ret[i].int_type = va_arg(wva, int);
break; break;
case XP_PRINTF_TYPE_UINT: /* Also includes char and short */ case XP_PRINTF_TYPE_UINT:
next=xp_asprintf_next(working, type, va_arg(va, unsigned int)); ret[i].uint_type = va_arg(wva, unsigned);
break; break;
case XP_PRINTF_TYPE_LONG: case XP_PRINTF_TYPE_LONG:
next=xp_asprintf_next(working, type, va_arg(va, long)); ret[i].long_type = va_arg(wva, long);
break; break;
case XP_PRINTF_TYPE_ULONG: case XP_PRINTF_TYPE_ULONG:
next=xp_asprintf_next(working, type, va_arg(va, unsigned long)); ret[i].ulong_type = va_arg(wva, unsigned long);
break; break;
#if defined(XP_PRINTF_TYPE_LONGLONG) #ifdef XP_PRINTF_TYPE_LONGLONG
case XP_PRINTF_TYPE_LONGLONG: case XP_PRINTF_TYPE_LONGLONG:
next=xp_asprintf_next(working, type, va_arg(va, long long)); ret[i].longlong_type = va_arg(wva, long long);
break; break;
#endif
#ifdef XP_PRINTF_TYPE_ULONGLONG
case XP_PRINTF_TYPE_ULONGLONG: case XP_PRINTF_TYPE_ULONGLONG:
next=xp_asprintf_next(working, type, va_arg(va, unsigned long long)); ret[i].ulonglong_type = va_arg(wva, unsigned long long);
break; break;
#endif #endif
case XP_PRINTF_TYPE_CHARP: case XP_PRINTF_TYPE_CHARP:
next=xp_asprintf_next(working, type, va_arg(va, char *)); ret[i].charp_type = va_arg(wva, char *);
break; break;
case XP_PRINTF_TYPE_DOUBLE: case XP_PRINTF_TYPE_DOUBLE:
next=xp_asprintf_next(working, type, va_arg(va, double)); ret[i].double_type = va_arg(wva, double);
break; break;
case XP_PRINTF_TYPE_LONGDOUBLE: case XP_PRINTF_TYPE_LONGDOUBLE:
next=xp_asprintf_next(working, type, va_arg(va, long double)); ret[i].longdouble_type = va_arg(wva, long double);
break; break;
case XP_PRINTF_TYPE_VOIDP: case XP_PRINTF_TYPE_VOIDP:
next=xp_asprintf_next(working, type, va_arg(va, void *)); ret[i].voidp_type = va_arg(wva, void *);
break; break;
case XP_PRINTF_TYPE_SIZET: case XP_PRINTF_TYPE_SIZET:
next=xp_asprintf_next(working, type, va_arg(va, size_t)); ret[i].size_type = va_arg(wva, size_t);
break; break;
default:
va_end(wva);
free(ret);
return NULL;
} }
if(next==NULL) }
va_end(wva);
return ret;
}
char* xp_vasprintf(const char *format, va_list va)
{
char *working;
char *next;
int type;
int curpos;
int newpos;
va_list wva;
struct arg_table_entry *atable = NULL;
next=xp_asprintf_start(format);
if(next==NULL)
return(NULL);
working=next;
va_copy(wva, va);
curpos = 1;
while(*(size_t *)working) {
newpos = xp_printf_get_next(working);
if (atable == NULL) {
if (newpos == -1) {
va_end(wva);
free(working);
return(NULL);
}
if (newpos > 0 && newpos != curpos) {
va_end(wva);
va_copy(wva, va);
atable = build_arg_table(format, va);
if (atable == NULL) {
free(working);
return NULL;
}
continue;
}
else
curpos++;
type=xp_printf_get_type(working);
switch(type) {
case 0:
va_end(wva);
free(working);
return(NULL);
case XP_PRINTF_TYPE_CHAR:
case XP_PRINTF_TYPE_INT: /* Also includes char and short */
next=xp_asprintf_next(working, type, va_arg(wva, int));
break;
case XP_PRINTF_TYPE_UINT: /* Also includes char and short */
next=xp_asprintf_next(working, type, va_arg(wva, unsigned int));
break;
case XP_PRINTF_TYPE_LONG:
next=xp_asprintf_next(working, type, va_arg(wva, long));
break;
case XP_PRINTF_TYPE_ULONG:
next=xp_asprintf_next(working, type, va_arg(wva, unsigned long));
break;
#if defined(XP_PRINTF_TYPE_LONGLONG)
case XP_PRINTF_TYPE_LONGLONG:
next=xp_asprintf_next(working, type, va_arg(wva, long long));
break;
case XP_PRINTF_TYPE_ULONGLONG:
next=xp_asprintf_next(working, type, va_arg(wva, unsigned long long));
break;
#endif
case XP_PRINTF_TYPE_CHARP:
next=xp_asprintf_next(working, type, va_arg(wva, char *));
break;
case XP_PRINTF_TYPE_DOUBLE:
next=xp_asprintf_next(working, type, va_arg(wva, double));
break;
case XP_PRINTF_TYPE_LONGDOUBLE:
next=xp_asprintf_next(working, type, va_arg(wva, long double));
break;
case XP_PRINTF_TYPE_VOIDP:
next=xp_asprintf_next(working, type, va_arg(wva, void *));
break;
case XP_PRINTF_TYPE_SIZET:
next=xp_asprintf_next(working, type, va_arg(wva, size_t));
break;
}
}
else {
if (newpos == -1) {
free(atable);
free(working);
return(NULL);
}
if (newpos > 0 && newpos != curpos)
curpos = newpos;
else
curpos++;
// Use atable
type = atable[curpos - 1].type;
switch(type) {
case 0:
free(atable);
free(working);
return(NULL);
case XP_PRINTF_TYPE_CHAR:
case XP_PRINTF_TYPE_INT: /* Also includes char and short */
next=xp_asprintf_next(working, type, atable[curpos - 1].int_type);
break;
case XP_PRINTF_TYPE_UINT: /* Also includes char and short */
next=xp_asprintf_next(working, type, atable[curpos - 1].uint_type);
break;
case XP_PRINTF_TYPE_LONG:
next=xp_asprintf_next(working, type, atable[curpos - 1].long_type);
break;
case XP_PRINTF_TYPE_ULONG:
next=xp_asprintf_next(working, type, atable[curpos - 1].ulong_type);
break;
#if defined(XP_PRINTF_TYPE_LONGLONG)
case XP_PRINTF_TYPE_LONGLONG:
next=xp_asprintf_next(working, type, atable[curpos - 1].longlong_type);
break;
#endif
#if defined(XP_PRINTF_TYPE_ULONGLONG)
case XP_PRINTF_TYPE_ULONGLONG:
next=xp_asprintf_next(working, type, atable[curpos - 1].ulonglong_type);
break;
#endif
case XP_PRINTF_TYPE_CHARP:
next=xp_asprintf_next(working, type, atable[curpos - 1].charp_type);
break;
case XP_PRINTF_TYPE_DOUBLE:
next=xp_asprintf_next(working, type, atable[curpos - 1].double_type);
break;
case XP_PRINTF_TYPE_LONGDOUBLE:
next=xp_asprintf_next(working, type, atable[curpos - 1].longdouble_type);
break;
case XP_PRINTF_TYPE_VOIDP:
next=xp_asprintf_next(working, type, atable[curpos - 1].voidp_type);
break;
case XP_PRINTF_TYPE_SIZET:
next=xp_asprintf_next(working, type, atable[curpos - 1].size_type);
break;
}
}
if(next==NULL) {
if (atable == NULL)
va_end(wva);
free(atable);
return(NULL); return(NULL);
}
working=next; working=next;
} }
if (atable == NULL)
va_end(wva);
free(atable);
next=xp_asprintf_end(working, NULL); next=xp_asprintf_end(working, NULL);
if(next==NULL) { if(next==NULL) {
free(working); free(working);
...@@ -1427,6 +1811,11 @@ int main(int argc, char *argv[]) ...@@ -1427,6 +1811,11 @@ int main(int argc, char *argv[])
p=xp_asprintf("%%%%%*.*f %% %%%ss %cs %*.*lu",3,3,123.123456789,"%llutesting%",32,3,3,123); p=xp_asprintf("%%%%%*.*f %% %%%ss %cs %*.*lu",3,3,123.123456789,"%llutesting%",32,3,3,123);
printf("%s\n",p); printf("%s\n",p);
free(p); free(p);
p=xp_asprintf("%3$d %1$d %d",2, 3, 1);
printf("%s\n",p);
free(p);
if(argc < 2) if(argc < 2)
return(1); return(1);
......
...@@ -50,7 +50,7 @@ ...@@ -50,7 +50,7 @@
#define XP_PRINTF_TYPE_PTRDIFF 14 /* Not currently implemented */ #define XP_PRINTF_TYPE_PTRDIFF 14 /* Not currently implemented */
#define XP_PRINTF_TYPE_SIZET 15 #define XP_PRINTF_TYPE_SIZET 15
#define XP_PRINTF_CONVERT (1<<31) /* OR with type to request a conversion - Not implemented */ #define XP_PRINTF_CONVERT (1<<31) /* OR with type to request a conversion to format-specified type */
#if defined(__cplusplus) #if defined(__cplusplus)
extern "C" { extern "C" {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment