mirror of
https://github.com/tailix/libkernaux.git
synced 2025-02-17 15:45:32 -05:00
137 lines
4.6 KiB
C
137 lines
4.6 KiB
C
#include "main.h"
|
|
#include "dynarg.h"
|
|
|
|
#define BUFFER_SIZE 4096
|
|
|
|
#ifdef KERNAUX_VERSION_WITH_PRINTF
|
|
|
|
/**
|
|
* Typical `printf`.
|
|
*
|
|
* @param format [String] format string
|
|
* @return [String] formatted output
|
|
*
|
|
* @example
|
|
* KernAux.sprintf 'foo%*scar%d', 5, 'bar', 123
|
|
* #=> "foo barcar123"
|
|
*/
|
|
static VALUE rb_KernAux_sprintf(int argc, VALUE *argv, VALUE self);
|
|
|
|
void init_printf()
|
|
{
|
|
rb_define_singleton_method(rb_KernAux, "sprintf", rb_KernAux_sprintf, -1);
|
|
}
|
|
|
|
#define TAKE_ARG \
|
|
if (arg_index >= argc) rb_raise(rb_eArgError, "too few arguments"); \
|
|
VALUE arg_rb = argv[arg_index++]; \
|
|
do {} while (0)
|
|
|
|
VALUE rb_KernAux_sprintf(const int argc, VALUE *const argv, VALUE self)
|
|
{
|
|
if (argc == 0) rb_raise(rb_eArgError, "too few arguments");
|
|
|
|
const char *format = StringValueCStr(argv[0]);
|
|
int arg_index = 1;
|
|
VALUE result = rb_str_new_literal("");
|
|
|
|
while (*format) {
|
|
if (*format != '%') {
|
|
rb_str_cat(result, format, 1);
|
|
++format;
|
|
continue;
|
|
}
|
|
|
|
++format;
|
|
struct KernAux_PrintfFmt_Spec spec =
|
|
KernAux_PrintfFmt_Spec_create_out(&format);
|
|
|
|
if (spec.set_width) {
|
|
TAKE_ARG;
|
|
KernAux_PrintfFmt_Spec_set_width(&spec, NUM2INT(arg_rb));
|
|
}
|
|
if (spec.set_precision) {
|
|
TAKE_ARG;
|
|
KernAux_PrintfFmt_Spec_set_precision(&spec, NUM2INT(arg_rb));
|
|
}
|
|
|
|
struct DynArg dynarg = DynArg_create();
|
|
|
|
if (spec.type == KERNAUX_PRINTF_FMT_TYPE_INT) {
|
|
TAKE_ARG;
|
|
DynArg_use_long_long(&dynarg, NUM2LL(arg_rb));
|
|
} else if (spec.type == KERNAUX_PRINTF_FMT_TYPE_UINT) {
|
|
TAKE_ARG;
|
|
DynArg_use_unsigned_long_long(&dynarg, NUM2ULL(arg_rb));
|
|
} else if (spec.type == KERNAUX_PRINTF_FMT_TYPE_FLOAT ||
|
|
spec.type == KERNAUX_PRINTF_FMT_TYPE_EXP)
|
|
{
|
|
TAKE_ARG;
|
|
DynArg_use_double(&dynarg, NUM2DBL(arg_rb));
|
|
} else if (spec.type == KERNAUX_PRINTF_FMT_TYPE_CHAR) {
|
|
TAKE_ARG;
|
|
Check_Type(arg_rb, T_STRING);
|
|
DynArg_use_char(&dynarg, *StringValuePtr(arg_rb));
|
|
} else if (spec.type == KERNAUX_PRINTF_FMT_TYPE_STR) {
|
|
TAKE_ARG;
|
|
Check_Type(arg_rb, T_STRING);
|
|
DynArg_use_str(&dynarg, StringValueCStr(arg_rb));
|
|
}
|
|
|
|
// 1 additional byte for the '%' character.
|
|
// 1 additional byte for the terminating '\0' character.
|
|
char old_format[2 + spec.format_limit - spec.format_start];
|
|
memset(old_format, '\0', sizeof(old_format));
|
|
old_format[0] = '%';
|
|
strncpy(&old_format[1], spec.format_start, sizeof(old_format) - 2);
|
|
|
|
char buffer[BUFFER_SIZE];
|
|
int slen;
|
|
|
|
if (spec.set_width) {
|
|
if (spec.set_precision) {
|
|
if (dynarg.use_dbl) {
|
|
slen = kernaux_snprintf(buffer, BUFFER_SIZE, old_format,
|
|
spec.width, spec.precision,
|
|
dynarg.dbl);
|
|
} else {
|
|
slen = kernaux_snprintf(buffer, BUFFER_SIZE, old_format,
|
|
spec.width, spec.precision,
|
|
dynarg.arg);
|
|
}
|
|
} else {
|
|
if (dynarg.use_dbl) {
|
|
slen = kernaux_snprintf(buffer, BUFFER_SIZE, old_format,
|
|
spec.width, dynarg.dbl);
|
|
} else {
|
|
slen = kernaux_snprintf(buffer, BUFFER_SIZE, old_format,
|
|
spec.width, dynarg.arg);
|
|
}
|
|
}
|
|
} else {
|
|
if (spec.set_precision) {
|
|
if (dynarg.use_dbl) {
|
|
slen = kernaux_snprintf(buffer, BUFFER_SIZE, old_format,
|
|
spec.precision, dynarg.dbl);
|
|
} else {
|
|
slen = kernaux_snprintf(buffer, BUFFER_SIZE, old_format,
|
|
spec.precision, dynarg.arg);
|
|
}
|
|
} else {
|
|
if (dynarg.use_dbl) {
|
|
slen = kernaux_snprintf(buffer, BUFFER_SIZE, old_format, dynarg.dbl);
|
|
} else {
|
|
slen = kernaux_snprintf(buffer, BUFFER_SIZE, old_format, dynarg.arg);
|
|
}
|
|
}
|
|
}
|
|
|
|
rb_str_cat(result, buffer, slen);
|
|
}
|
|
|
|
if (arg_index < argc) rb_raise(rb_eArgError, "too many arguments");
|
|
|
|
return rb_funcall(result, rb_intern_freeze, 0);
|
|
}
|
|
|
|
#endif // KERNAUX_VERSION_WITH_PRINTF
|