12
\$\begingroup\$

I'm writing a library to register the b, B conversion specifiers and make them work the closest possible to o, u, x, X.

Requirements:

  • All "flag characters" should behave as in x, X (or as close as possible, while being meaningful).
  • "Field width" and "precision" should behave as in x, X (or as close as possible, while being meaningful).
  • All "length modifiers" that are valid on o, u, x, X should be valid.
  • They should work on any printf family function (such as fprintf, snprintf, ...).
  • There are some flags that override others in the case of o, u, x, X. Those behaviours should be kept the same.

Basically, they should work so that a user can predict the output from reading the man printf page.

I decided that the ' flag character, which normally should group the output in thousands, would be more meaningful here if it grouped nibbles (half bytes), and instead of using the locale for thousands separator, it should use _. But I'm open to improvements here.

Here goes the code:

/* 2019 - Alejandro Colomar Andrés */
/******************************************************************************
 ******* headers **************************************************************
 ******************************************************************************/
#include "libalx/base/stdio/printf.h"

#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>

#include <printf.h>


/******************************************************************************
 ******* macros ***************************************************************
 ******************************************************************************/


/******************************************************************************
 ******* enums ****************************************************************
 ******************************************************************************/


/******************************************************************************
 ******* structs / unions *****************************************************
 ******************************************************************************/


/******************************************************************************
 ******* variables ************************************************************
 ******************************************************************************/


/******************************************************************************
 ******* static functions (prototypes) ****************************************
 ******************************************************************************/
static  int printf_output_b     (FILE *stream,
                    const struct printf_info *info,
                    const void *const *args);
static  int printf_arginf_sz_b  (const struct printf_info *info,
                    size_t n, int *argtypes, int *size);


/******************************************************************************
 ******* global functions *****************************************************
 ******************************************************************************/
int alx_printf_init (void)
{

    if (register_printf_specifier('b', printf_output_b, printf_arginf_sz_b))
        return  -1;
    if (register_printf_specifier('B', printf_output_b, printf_arginf_sz_b))
        return  -1;

    return  0;
}


/******************************************************************************
 ******* static functions (definitions) ***************************************
 ******************************************************************************/
static  int printf_output_b     (FILE *stream,
                    const struct printf_info *info,
                    const void *const *args)
{
    uintmax_t   val;
    uintmax_t   utmp;
    bool    bin[sizeof(val) * 8];
    int min_len;
    int bin_len;
    int pad_len;
    int tmp;
    char    pad_ch;
    size_t  len;

    len = 0;

    if (info->is_long_double)
        val = *(unsigned long long *)args[0];
    else if (info->is_long)
        val = *(unsigned long *)args[0];
    else if (info->is_char)
        val = *(unsigned char *)args[0];
    else if (info->is_short)
        val = *(unsigned short *)args[0];
    else
        val = *(unsigned *)args[0];

    /* Binary representation */
    memset(bin, 0, sizeof(bin));
    utmp    = val;
    for (min_len = 0; utmp; min_len++) {
        if (utmp % 2)
            bin[min_len]    = 1;
        utmp >>= 1;
    }
    if (info->prec > min_len)
        bin_len = info->prec;
    else
        bin_len = min_len;

    /* Padding char */
    if ((info->prec != -1)  ||  (info->pad == ' ')  ||  info->left)
        pad_ch = ' ';
    else
        pad_ch = '0';

    /* Padding length */
    if (pad_ch == ' ') {
        pad_len = info->width - bin_len;
        if (info->alt)
            pad_len -= 2;
        if (info->group)
            pad_len -= (bin_len - 1) / 4;
        if (pad_len < 0)
            pad_len = 0;
    }

    /* Padding with ' ' (right aligned) */
    if ((pad_ch == ' ')  &&  !info->left) {
        for (int i = pad_len; i; i--) {
            if (fputc(' ', stream) == EOF)
                return  EOF;
        }
        len += pad_len;
    }

    /* "0b"/"0B" prefix */
    if (info->alt && val) {
        if (fputc('0', stream) == EOF)
            return  EOF;
        if (fputc(info->spec, stream) == EOF)
            return  EOF;
        len += 2;
    }
    
    /* Padding with '0' */
    if (pad_ch == '0') {
        tmp = info->width - (info->alt * 2);
        if (info->group)
            tmp -= (tmp - min_len + 3) / 4;
        for (int i = tmp - 1; i > min_len - 1; i--) {
            if (fputc('0', stream) == EOF)
                return  EOF;
            if (info->group  &&  !(i % 4)  &&  i) {
                if (fputc('_', stream) == EOF)
                    return  EOF;
                len++;
            }
        }
        len += tmp - min_len;
    }

    /* Print leading zeros to fill precission */
    for (int i = bin_len - 1; i > min_len - 1; i--) {
        if (fputc('0', stream) == EOF)
            return  EOF;
        if (info->group  &&  !(i % 4)  &&  i) {
            if (fputc('_', stream) == EOF)
                return  EOF;
            len++;
        }
    }
    len += bin_len - min_len;

    /* Print number */
    for (int i = min_len - 1; i; i--) {
        if (fputc('0' + bin[i], stream) == EOF)
            return  EOF;
        if (info->group  &&  !(i % 4)  &&  i) {
            if (fputc('_', stream) == EOF)
                return  EOF;
            len++;
        }
    }
    if (fputc('0' + bin[0], stream) == EOF)
        return  EOF;
    len += min_len;

    /* Padding with ' ' (left aligned) */
    if (info->left) {
        for (int i = pad_len; i; i--) {
            if (fputc(' ', stream) == EOF)
                return  EOF;
        }
        len += pad_len;
    }

    return  len;
}

static  int printf_arginf_sz_b  (const struct printf_info *info,
                    size_t n, int *argtypes, int *size)
{

    if (n > 0)
        argtypes[0] = PA_INT;

    return 1;
}


/******************************************************************************
 ******* end of file **********************************************************
 ******************************************************************************/

It seems that it works as expected (at least on my tests), but I feel like the code is very heavy and long and could be simplified.

Here is what I've tested:

#include "libalx/base/stdio/printf.h"

#include <stddef.h>
#include <stdint.h>
#include <stdio.h>


int main()
{

    alx_printf_init();

    printf("....----....----....----....----\n");
    printf("%llb;\n", 0x5Ellu);
    printf("%lB;\n", 0x5Elu);
    printf("%b;\n", 0x5Eu);
    printf("%hB;\n", 0x5Eu);
    printf("%hhb;\n", 0x5Eu);
    printf("%jb;\n", (uintmax_t)0x5E);
    printf("%zb;\n", (size_t)0x5E);
    printf("....----....----....----....----\n");
    printf("%#b;\n", 0x5Eu);
    printf("%#B;\n", 0x5Eu);
    printf("....----....----....----....----\n");
    printf("%10b;\n", 0x5Eu);
    printf("%010b;\n", 0x5Eu);
    printf("%.10b;\n", 0x5Eu);
    printf("....----....----....----....----\n");
    printf("%-10B;\n", 0x5Eu);
    printf("....----....----....----....----\n");
    printf("%'B;\n", 0x5Eu);
    printf("....----....----....----....----\n");   
    printf("....----....----....----....----\n");
    printf("%#16.12b;\n", 0xAB);
    printf("%-#'20.12b;\n", 0xAB);
    printf("%#'020B;\n", 0xAB);
    printf("....----....----....----....----\n");
    printf("%#020B;\n", 0xAB);
    printf("%'020B;\n", 0xAB);
    printf("%020B;\n", 0xAB);
    printf("....----....----....----....----\n");
    printf("%#021B;\n", 0xAB);
    printf("%'021B;\n", 0xAB);
    printf("%021B;\n", 0xAB);
    printf("....----....----....----....----\n");
    printf("%#022B;\n", 0xAB);
    printf("%'022B;\n", 0xAB);
    printf("%022B;\n", 0xAB);
    printf("....----....----....----....----\n");
    printf("%#023B;\n", 0xAB);
    printf("%'023B;\n", 0xAB);
    printf("%023B;\n", 0xAB);
    printf("....----....----....----....----\n");
    printf("%-#'19.11b;\n", 0xAB);
    printf("%#'019B;\n", 0xAB);
    printf("%#019B;\n", 0xAB);
    printf("....----....----....----....----\n");
    printf("%'019B;\n", 0xAB);
    printf("%019B;\n", 0xAB);
    printf("%#016b;\n", 0xAB);
    printf("....----....----....----....----\n");

    return 0;
}

And its output:

....----....----....----....----                                                  
1011110;                                                                          
1011110;                                                                          
1011110;                                                                          
1011110;                                                                          
1011110;                                                                          
1011110;                                                                          
1011110;                                                                          
....----....----....----....----                                                  
0b1011110;                                                                        
0B1011110;                                                                        
....----....----....----....----                                                  
   1011110;                                                                       
0001011110;                                                                       
0001011110;                                                                       
....----....----....----....----                                                  
1011110   ;                                                                       
....----....----....----....----                                                  
101_1110;                                                                         
....----....----....----....----
....----....----....----....----                                                  
  0b000010101011;                                                                 
0b0000_1010_1011    ;                                                             
0B000_0000_1010_1011;                                                             
....----....----....----....----                                                  
0B000000000010101011;                                                             
0_0000_0000_1010_1011;                                                            
00000000000010101011;                                                             
....----....----....----....----                                                  
0B0000000000010101011;                                                            
0_0000_0000_1010_1011;                                                            
000000000000010101011;                                                            
....----....----....----....----                                                  
0B00000000000010101011;                                                           
00_0000_0000_1010_1011;                                                           
0000000000000010101011;                                                           
....----....----....----....----                                                  
0B000000000000010101011;                                                          
000_0000_0000_1010_1011;                                                          
00000000000000010101011;                                                          
....----....----....----....----                                                  
0b000_1010_1011    ;                                                              
0B00_0000_1010_1011;                                                              
0B00000000010101011;                                                              
....----....----....----....----                                                  
0000_0000_1010_1011;                                                              
0000000000010101011;                                                              
0b00000010101011;                                                                 
....----....----....----....----

I understood the use of printf_output_b, but I still don't know very well the use of printf_arginf_sz_b, and if I'm missing anything.

Is there anything you miss here, or you think could be improved?

Also, let's say that uintmax_t or size_t are wider than unsigned long long. How would I handle that? I don't receive any information about those in the struct, AFAIK.

EDIT: Add the struct and enum definitions from <printf.h>

struct printf_info{
 int prec;          /* Precision. */
 int width;         /* Width. */
 wchar_t spec;          /* Format letter. */
 unsigned int is_long_double:1;/* L flag. */
 unsigned int is_short:1;   /* h flag. */
 unsigned int is_long:1;    /* l flag. */
 unsigned int alt:1;        /* # flag. */
 unsigned int space:1;      /* Space flag. */
 unsigned int left:1;       /* - flag. */
 unsigned int showsign:1;   /* + flag. */
 unsigned int group:1;      /* ' flag. */
 unsigned int extra:1;      /* For special use. */
 unsigned int is_char:1;    /* hh flag. */
 unsigned int wide:1;       /* Nonzero for wide character streams. */
 unsigned int i18n:1;       /* I flag. */
 unsigned int is_binary128:1;   /* Floating-point argument is ABI-compatible                 with IEC 60559 binary128. */
 unsigned int __pad:3;      /* Unused so far. */
 unsigned short int user;   /* Bits for user-installed modifiers. */
 wchar_t pad;           /* Padding character. */
};

enum{               /* C type: */
 PA_INT,            /* int */
 PA_CHAR,           /* int, cast to char */
 PA_WCHAR,          /* wide char */
 PA_STRING,         /* const char *, a '\0'-terminated string */
 PA_WSTRING,            /* const wchar_t *, wide character string */
 PA_POINTER,            /* void * */
 PA_FLOAT,          /* float */
 PA_DOUBLE,         /* double */
 PA_LAST
};
\$\endgroup\$
4
  • \$\begingroup\$ What exactly are you asking, do you want us to explain what printf_arginf_sz_b() does as well as the size of uintmax_t and size_t? \$\endgroup\$
    – pacmaninbw
    Commented May 9, 2019 at 15:58
  • \$\begingroup\$ @pacmaninbw I'd like to see improvements in code cleanness/performance and if I anticipated (as per the requirements) all possible combinations of flags (including "%jb" (uint_max) for example). Also, obviously, if there is any UB, I would like to know. \$\endgroup\$ Commented May 9, 2019 at 16:12
  • \$\begingroup\$ Could you please add the file(s) that define struct printf_info and PA_INT. \$\endgroup\$
    – pacmaninbw
    Commented May 9, 2019 at 16:34
  • 1
    \$\begingroup\$ @pacmaninbw They're all from <printf.h> from glibc (github.com/lattera/glibc/blob/master/stdio-common/printf.h). I edited the question to add them. \$\endgroup\$ Commented May 9, 2019 at 20:00

3 Answers 3

4
\$\begingroup\$

zero

min_len is calculated as 0 when val == 0. I'd expect min_len to be 1.

Casual conversion

Pedantic: code returns size_t len as int with no range check in the conversion. Might as well just use int len.

len calculation

Code does not display the return value of printf() in its tests. I have suspicions about its correctness. Suggest instead to simply pair each fputc() with a len++.

\$\endgroup\$
5
\$\begingroup\$

EDIT: I also fixed a typo: main should be int main(void)!


After fixing the bugs @chux found, there was still another bug:

This line:

        tmp -= (tmp - min_len + 3) / 4;

should be:

        tmp -= tmp / 5 - !(tmp % 5);

I also restructured the big function into smaller functions, and used CHAR_BIT instead of the magic number 8:

/* 2019 - Alejandro Colomar Andrés */
/******************************************************************************
 ******* headers **************************************************************
 ******************************************************************************/
#include "libalx/base/stdio/printf.h"

#include <limits.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>

#include <printf.h>


/******************************************************************************
 ******* macros ***************************************************************
 ******************************************************************************/
#define BIN_REPR_BUFSIZ (sizeof(uintmax_t) * CHAR_BIT)


/******************************************************************************
 ******* enums ****************************************************************
 ******************************************************************************/


/******************************************************************************
 ******* structs / unions *****************************************************
 ******************************************************************************/
struct  Printf_Pad {
        char    ch;
        int     len;
};


/******************************************************************************
 ******* variables ************************************************************
 ******************************************************************************/


/******************************************************************************
 ******* static functions (prototypes) ****************************************
 ******************************************************************************/
static  int     printf_b_output         (FILE *stream,
                                        const struct printf_info *info,
                                        const void *const args[]);
static  int     printf_b_arginf_sz      (const struct printf_info *info,
                                        size_t n, int *argtypes, int *size);

static  uintmax_t printf_b_value        (const struct printf_info *info,
                                        const void *arg);
static  int     printf_b_bin_repr       (bool bin[BIN_REPR_BUFSIZ],
                                        const struct printf_info *info,
                                        const void *arg);
static  int     printf_b_bin_len        (const struct printf_info *info,
                                        int min_len);
static  int     printf_b_pad_len        (const struct printf_info *info,
                                        int bin_len);
static  int     printf_b_print_prefix   (FILE *stream,
                                        const struct printf_info *info);
static  int     printf_b_pad_zeros      (FILE *stream,
                                        const struct printf_info *info,
                                        int min_len);
static  int     printf_b_print_number   (FILE *stream,
                                        const struct printf_info *info,
                                        bool bin[BIN_REPR_BUFSIZ],
                                        int min_len, int bin_len);
static  char    printf_pad_ch           (const struct printf_info *info);
static  int     printf_pad_spaces       (FILE *stream, int pad_len);




/******************************************************************************
 ******* global functions *****************************************************
 ******************************************************************************/
int     alx_printf_init (void)
{

        if (register_printf_specifier('b', printf_b_output, printf_b_arginf_sz))
                return  -1;
        if (register_printf_specifier('B', printf_b_output, printf_b_arginf_sz))
                return  -1;

        return  0;
}


/******************************************************************************
 ******* static functions (definitions) ***************************************
 ******************************************************************************/
static  int     printf_b_output         (FILE *stream,
                                        const struct printf_info *info,
                                        const void *const args[])
{
        struct  Printf_Pad  pad = {0};
        bool    bin[BIN_REPR_BUFSIZ];
        int     min_len;
        int     bin_len;
        int     len;
        int     tmp;

        len = 0;

        min_len = printf_b_bin_repr(bin, info, args[0]);
        bin_len = printf_b_bin_len(info, min_len);

        pad.ch = printf_pad_ch(info);
        if (pad.ch == ' ')
                pad.len = printf_b_pad_len(info, bin_len);

        /* Padding with ' ' (right aligned) */
        if ((pad.ch == ' ')  &&  !info->left) {
                tmp = printf_pad_spaces(stream, pad.len);
                if (tmp == EOF)
                        return  EOF;
                len += tmp;
        }

        /* "0b"/"0B" prefix */
        if (info->alt) {
                tmp = printf_b_print_prefix(stream, info);
                if (tmp == EOF)
                        return  EOF;
                len += tmp;
        }

        /* Padding with '0' */
        if (pad.ch == '0') {
                tmp = printf_b_pad_zeros(stream, info, min_len);
                if (tmp == EOF)
                        return  EOF;
                len += tmp;
        }

        /* Print number (including leading 0s to fill precission) */
        tmp = printf_b_print_number(stream, info, bin, min_len, bin_len);
        if (tmp == EOF)
                return  EOF;
        len += tmp;

        /* Padding with ' ' (left aligned) */
        if (info->left) {
                tmp = printf_pad_spaces(stream, pad.len);
                if (tmp == EOF)
                        return  EOF;
                len += tmp;
        }

        return  len;
}

static  int     printf_b_arginf_sz      (const struct printf_info *info,
                                        size_t n, int *argtypes, int *size)
{

        (void)info;
        (void)size;

        if (n > 0)
                argtypes[0] = PA_INT;

        return 1;
}

static  uintmax_t printf_b_value        (const struct printf_info *info,
                                        const void *arg)
{

        if (info->is_long_double)
                return  *(unsigned long long *)arg;
        if (info->is_long)
                return  *(unsigned long *)arg;
        if (info->is_char)
                return  *(unsigned char *)arg;
        if (info->is_short)
                return  *(unsigned short *)arg;
        return  *(unsigned *)arg;
}

static  int     printf_b_bin_repr       (bool bin[BIN_REPR_BUFSIZ],
                                        const struct printf_info *info,
                                        const void *arg)
{
        uintmax_t   val;
        int         min_len;

        val = printf_b_value(info, arg);

        memset(bin, 0, sizeof(bin[0]) * BIN_REPR_BUFSIZ);
        for (min_len = 0; val; min_len++) {
                if (val % 2)
                        bin[min_len]    = 1;
                val >>= 1;
        }

        if (!min_len)
                return  1;
        return  min_len;
}

static  int     printf_b_bin_len        (const struct printf_info *info,
                                        int min_len)
{

        if (info->prec > min_len)
                return  info->prec;
        return  min_len;
}

static  int     printf_b_pad_len        (const struct printf_info *info,
                                        int bin_len)
{
        int pad_len;

        pad_len = info->width - bin_len;
        if (info->alt)
                pad_len -= 2;
        if (info->group)
                pad_len -= (bin_len - 1) / 4;
        if (pad_len < 0)
                pad_len = 0;

        return  pad_len;
}

static  int     printf_b_print_prefix   (FILE *stream,
                                        const struct printf_info *info)
{
        int len;

        len = 0;
        if (fputc('0', stream) == EOF)
                return  EOF;
        len++;
        if (fputc(info->spec, stream) == EOF)
                return  EOF;
        len++;

        return  len;
}

static  int     printf_b_pad_zeros      (FILE *stream,
                                        const struct printf_info *info,
                                        int min_len)
{
        int len;
        int tmp;

        len = 0;
        tmp = info->width - (info->alt * 2);
        if (info->group)
                tmp -= tmp / 5 - !(tmp % 5);
        for (int i = tmp - 1; i > min_len - 1; i--) {
                if (fputc('0', stream) == EOF)
                        return  EOF;
                len++;
                if (info->group  &&  !(i % 4)) {
                        if (fputc('_', stream) == EOF)
                                return  EOF;
                        len++;
                }
        }

        return  len;
}

static  int     printf_b_print_number   (FILE *stream,
                                        const struct printf_info *info,
                                        bool bin[sizeof(uintmax_t) * CHAR_BIT],
                                        int min_len, int bin_len)
{
        int len;

        len = 0;

        /* Print leading zeros to fill precission */
        for (int i = bin_len - 1; i > min_len - 1; i--) {
                if (fputc('0', stream) == EOF)
                        return  EOF;
                len++;
                if (info->group  &&  !(i % 4)) {
                        if (fputc('_', stream) == EOF)
                                return  EOF;
                        len++;
                }
        }

        /* Print number */
        for (int i = min_len - 1; i; i--) {
                if (fputc('0' + bin[i], stream) == EOF)
                        return  EOF;
                len++;
                if (info->group  &&  !(i % 4)) {
                        if (fputc('_', stream) == EOF)
                                return  EOF;
                        len++;
                }
        }
        if (fputc('0' + bin[0], stream) == EOF)
                return  EOF;
        len++;

        return  len;
}

static  char    printf_pad_ch           (const struct printf_info *info)
{

        if ((info->prec != -1)  ||  (info->pad == ' ')  ||  info->left)
                return  ' ';
        return  '0';
}

static  int     printf_pad_spaces       (FILE *stream, int pad_len)
{
        int len;

        len = 0;
        for (int i = pad_len; i; i--) {
                if (fputc(' ', stream) == EOF)
                        return  EOF;
                len++;
        }

        return  len;
}


/******************************************************************************
 ******* end of file **********************************************************
 ******************************************************************************/

I also added some more tests to be able to detect that bug, which I was suspecting that existed; now I also show the value of len (calculated indirectly from the return value of printf):

/* Test */
#include "libalx/base/stdio/printf.h"


#include <stddef.h>
#include <stdint.h>
#include <stdio.h>


int main(void)
{
    int len;
    char buff[BUFSIZ];

    alx_printf_init();

    snprintf(buff, 30, "Hey, %i == %#b :)\n", 5, 5);
    printf("%s", buff);
    printf("\n");

    printf("....----....----....----....----\n");
    len = printf("%llb;\n", 0x5Ellu);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%lB;\n", 0x5Elu);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%b;\n", 0x5Eu);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%hB;\n", 0x5Eu);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%hhb;\n", 0x5Eu);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%jb;\n", (uintmax_t)0x5E);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%zb;\n", (size_t)0x5E);
    printf("%i\n", len - strlen(";\n"));
    printf("....----....----....----....----\n");
    len = printf("%#b;\n", 0x5Eu);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%#B;\n", 0x5Eu);
    printf("%i\n", len - strlen(";\n"));
    printf("....----....----....----....----\n");
    len = printf("%10b;\n", 0x5Eu);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%010b;\n", 0x5Eu);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%.10b;\n", 0x5Eu);
    printf("%i\n", len - strlen(";\n"));
    printf("....----....----....----....----\n");
    len = printf("%-10B;\n", 0x5Eu);
    printf("%i\n", len - strlen(";\n"));
    printf("....----....----....----....----\n");
    len = printf("%'B;\n", 0x5Eu);
    printf("%i\n", len - strlen(";\n"));
    printf("....----....----....----....----\n");   
    printf("....----....----....----....----\n");
    len = printf("%#16.12b;\n", 0xAB);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%-#'20.12b;\n", 0xAB);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%#'020B;\n", 0xAB);
    printf("%i\n", len - strlen(";\n"));
    printf("....----....----....----....----\n");
    len = printf("%#020B;\n", 0xAB);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%'020B;\n", 0xAB);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%020B;\n", 0xAB);
    printf("%i\n", len - strlen(";\n"));
    printf("....----....----....----....----\n");
    len = printf("%#021B;\n", 0xAB);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%'021B;\n", 0xAB);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%021B;\n", 0xAB);
    printf("%i\n", len - strlen(";\n"));
    printf("....----....----....----....----\n");
    len = printf("%#022B;\n", 0xAB);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%'022B;\n", 0xAB);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%022B;\n", 0xAB);
    printf("%i\n", len - strlen(";\n"));
    printf("....----....----....----....----\n");
    len = printf("%#023B;\n", 0xAB);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%'023B;\n", 0xAB);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%023B;\n", 0xAB);
    printf("%i\n", len - strlen(";\n"));
    printf("....----....----....----....----\n");
    len = printf("%-#'19.11b;\n", 0xAB);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%#'019B;\n", 0xAB);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%#019B;\n", 0xAB);
    printf("%i\n", len - strlen(";\n"));
    printf("....----....----....----....----\n");
    len = printf("%'019B;\n", 0xAB);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%019B;\n", 0xAB);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%#016b;\n", 0xAB);
    printf("%i\n", len - strlen(";\n"));
    printf("....----....----....----....----\n");
    printf("....----....----....----....----\n");
    len = printf("%'010B;\n", 0xB);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%#'010B;\n", 0xB);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%'010B;\n", 0x1B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%#'010B;\n", 0x1B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%'010B;\n", 0x2B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%#'010B;\n", 0x2B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%'010B;\n", 0x4B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%#'010B;\n", 0x4B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%'010B;\n", 0x8B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%#'010B;\n", 0x8B);
    printf("%i\n", len - strlen(";\n"));
    printf("....----....----....----....----\n");
    len = printf("%'010B;\n", 0xB);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%#'010B;\n", 0xB);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%'011B;\n", 0xB);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%#'011B;\n", 0xB);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%'012B;\n", 0xB);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%#'012B;\n", 0xB);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%'013B;\n", 0xB);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%#'013B;\n", 0xB);
    printf("%i\n", len - strlen(";\n"));
    printf("....----....----....----....----\n");
    len = printf("%'010B;\n", 0x1B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%#'010B;\n", 0x1B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%'011B;\n", 0x1B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%#'011B;\n", 0x1B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%'012B;\n", 0x1B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%#'012B;\n", 0x1B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%'013B;\n", 0x1B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%#'013B;\n", 0x1B);
    printf("%i\n", len - strlen(";\n"));
    printf("....----....----....----....----\n");
    len = printf("%'010B;\n", 0x2B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%#'010B;\n", 0x2B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%'011B;\n", 0x2B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%#'011B;\n", 0x2B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%'012B;\n", 0x2B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%#'012B;\n", 0x2B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%'013B;\n", 0x2B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%#'013B;\n", 0x2B);
    printf("%i\n", len - strlen(";\n"));
    printf("....----....----....----....----\n");
    len = printf("%'010B;\n", 0x4B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%#'010B;\n", 0x4B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%'011B;\n", 0x4B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%#'011B;\n", 0x4B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%'012B;\n", 0x4B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%#'012B;\n", 0x4B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%'013B;\n", 0x4B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%#'013B;\n", 0x4B);
    printf("%i\n", len - strlen(";\n"));
    printf("....----....----....----....----\n");
    len = printf("%'010B;\n", 0x8B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%#'010B;\n", 0x8B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%'011B;\n", 0x8B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%#'011B;\n", 0x8B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%'012B;\n", 0x8B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%#'012B;\n", 0x8B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%'013B;\n", 0x8B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%#'013B;\n", 0x8B);
    printf("%i\n", len - strlen(";\n"));
    printf("....----....----....----....----\n");

    return 0;
}

Which shows the following output:

Hey, 5 == 0b101 :)

....----....----....----....----
1011110;
7
1011110;
7
1011110;
7
1011110;
7
1011110;
7
1011110;
7
1011110;
7
....----....----....----....----
0b1011110;
9
0B1011110;
9
....----....----....----....----
   1011110;
10
0001011110;
10
0001011110;
10
....----....----....----....----
1011110   ;
10
....----....----....----....----
101_1110;
8
....----....----....----....----
....----....----....----....----
  0b000010101011;
16
0b0000_1010_1011    ;
20
0B000_0000_1010_1011;
20
....----....----....----....----
0B000000000010101011;
20
0_0000_0000_1010_1011;
21
00000000000010101011;
20
....----....----....----....----
0B0000000000010101011;
21
0_0000_0000_1010_1011;
21
000000000000010101011;
21
....----....----....----....----
0B00000000000010101011;
22
00_0000_0000_1010_1011;
22
0000000000000010101011;
22
....----....----....----....----
0B000000000000010101011;
23
000_0000_0000_1010_1011;
23
00000000000000010101011;
23
....----....----....----....----
0b000_1010_1011    ;
19
0B00_0000_1010_1011;
19
0B00000000010101011;
19
....----....----....----....----
0000_0000_1010_1011;
19
0000000000010101011;
19
0b00000010101011;
16
....----....----....----....----
....----....----....----....----
0_0000_1011;
11
0B000_1011;
10
0_0001_1011;
11
0B001_1011;
10
0_0010_1011;
11
0B010_1011;
10
0_0100_1011;
11
0B100_1011;
10
0_1000_1011;
11
0B1000_1011;
11
....----....----....----....----
0_0000_1011;
11
0B000_1011;
10
0_0000_1011;
11
0B0000_1011;
11
00_0000_1011;
12
0B0_0000_1011;
13
000_0000_1011;
13
0B0_0000_1011;
13
....----....----....----....----
0_0001_1011;
11
0B001_1011;
10
0_0001_1011;
11
0B0001_1011;
11
00_0001_1011;
12
0B0_0001_1011;
13
000_0001_1011;
13
0B0_0001_1011;
13
....----....----....----....----
0_0010_1011;
11
0B010_1011;
10
0_0010_1011;
11
0B0010_1011;
11
00_0010_1011;
12
0B0_0010_1011;
13
000_0010_1011;
13
0B0_0010_1011;
13
....----....----....----....----
0_0100_1011;
11
0B100_1011;
10
0_0100_1011;
11
0B0100_1011;
11
00_0100_1011;
12
0B0_0100_1011;
13
000_0100_1011;
13
0B0_0100_1011;
13
....----....----....----....----
0_1000_1011;
11
0B1000_1011;
11
0_1000_1011;
11
0B1000_1011;
11
00_1000_1011;
12
0B0_1000_1011;
13
000_1000_1011;
13
0B0_1000_1011;
13
....----....----....----....----
````
\$\endgroup\$
1
\$\begingroup\$

The example code at https://man7.org/linux/man-pages/man3/printf_va_arg_function.3type.html may have some ways to optimize the conversion process.

\$\endgroup\$
7
  • 1
    \$\begingroup\$ Welcome. You did not review the existing solution. You provided an alternative approach, instead. \$\endgroup\$ Commented Oct 23, 2023 at 18:04
  • 1
    \$\begingroup\$ For completeness: I wrote that manual page <man7.org/linux/man-pages/man3/printf_va_arg_function.3type.html>, and the code was based on the one in my post, so they should be very similar (except that maybe I fixed some bugs in the manual page that are present in my post here, or added some optimizations). \$\endgroup\$ Commented Oct 24, 2023 at 15:50
  • \$\begingroup\$ What do you mean by "some printf implementations don't indicate the type correctly"? AFAIK, register_printf_specifier(3) is only implemented in glibc. Or did I misunderstand you? \$\endgroup\$ Commented Oct 24, 2023 at 15:58
  • \$\begingroup\$ On x86_64, __WORDSIZE is 64. I believe your code is incorrect, as it uses PA_FLAG_LONG_LONG for all integers, which means printf will skip 8 bytes from the call arguments for searching the next argument, even if you just passed an int, which should only skip 4 bytes. \$\endgroup\$ Commented Oct 24, 2023 at 16:06
  • \$\begingroup\$ @alx-recommendscodidact, great work on the library. All good points. I had difficulty with a gnu arm cross-compiler that insisted on promoting all passed integers to 64 bit, but would not set any of the info flags regardless of what was passed. I saw that the original solution could experience the same challenge in an embedded environment so it seemed like it may be worth mentioning the possibility. Since it is a corner case, it seems best to just delete my code. \$\endgroup\$
    – theSparky
    Commented Oct 24, 2023 at 19:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.