printf Implemented by Apple

2

https://opensource.apple.com/source/xnu/xnu-201/osfmk/kern/printf.c.auto.html

Hello! While analyzing Apple's printf code, I have a question!

case 's':
        {
            register char *p;
            register char *p2;

            if (prec == -1)
            prec = 0x7fffffff;  /* MAXINT */

            p = va_arg(*argp, char *);

            if (p == (char *)0)
            p = "";

            if (length > 0 && !ladjust) {
            n = 0;
            p2 = p;

            for (; *p != '\0' && n < prec; p++)
                n++;

            p = p2;

            while (n < length) {
                (*putc)(' ');
                n++;
            }
            }

            n = 0;

            while (*p != '\0') {
            if (++n > prec || (length > 0 && n > length))
                break;

            (*putc)(*p++);
            }

            if (n < length && ladjust) {
            while (n < length) {
                (*putc)(' ');
                n++;
            }
            }

            break;
        }

As follows, Apple cuts out the word for '% 2s' and prints it.

For example, "hello" is printed as "he"!

However, the actual printf output will return the entire string if it does not exceed the length of the string.

For example, "hello" is printed as "hello"!

Which part of the Apple code am I missing?


This is the code for% 3s.
In my code:

void printf_str(va_list listp, t_list *_flags)
{
    char *src;
    char *src_cpy;
    long n;

    if (_flags->prec == -1)
        _flags->prec = 0x7fffffff;
    src = va_arg(listp, char *);
    if(src == (char *)0)
        src = "";
    if (_flags->length > 0 && !_flags->ladjust)
    {
        n = 0;
        src_cpy = src;
        while (*src != '\0' && n < _flags->prec)
        {
            n++;
            src++;
        }
        printf("%ld, %d, %d\n", n, _flags->prec, _flags->length);
        src = src_cpy;
        while (n < _flags->length)
        {
            putchar_fd(1, ' ');
            n++;
        }
    }
    n = 0;
    printf("%ld, %d, %d\n", n, _flags->prec, _flags->length);
    while (*src != '\0')
    {
        printf("%ld, %d, %d\n", n, _flags->prec, _flags->length);
        if (++n > _flags->prec || (_flags->length > 0 && n > _flags->length))
            break;
        putchar_fd(1, *(src++));
    }
    printf("%ld, %d, %d\n", n, _flags->prec, _flags->length);

    if (n < _flags->length && _flags->ladjust)
    {
        while (n < _flags->length)
        {
            putchar_fd(1, ' ');
            n++;
        }
    }
}

length: 3 prec: -1 ladjust: 0 *fmt: s

Result:
5, 2147483647, 3
0, 2147483647, 3
0, 2147483647, 3
h1, 2147483647, 3
e2, 2147483647, 3
l3, 2147483647, 3
4, 2147483647, 3

The actual output part will be this part.

while (*p != '\0') {
            if (++n > prec || (length > 0 && n > length))
                break;

            (*putc)(*p++);
            }

To summarize my question again, it's Apple's code, but printf's behavior on mac prints out hello entirely. So when I re-implement, what guidelines should I follow?

c
asked on Stack Overflow May 16, 2020 by hochan • edited May 16, 2020 by hochan

3 Answers

3

This isn't the printf you think it is — it's an internal kernel printf, not the libc printf which would get called by a C program. As far as I can see, your interpretation of the code is correct, and it does truncate strings if a length is given which is less than the length of the string. You don't see that behavior in your programs because you aren't using this printf.

answered on Stack Overflow May 16, 2020 by hobbs
2

To answer your question:

To summarize my question again, it's Apple's code, but printf's behavior on mac prints out hello entirely. So when I re-implement, what guidelines should I follow?

You should implement it as defined by the C ISO standard. Meaning that the integer number specifies the minimum field width, so in your hello example %2s should print the entire word hello.

Quote from: https://en.cppreference.com/w/c/io/fprintf

(optional) integer value or * that specifies minimum field width. The result is padded with space characters (by default), if required, on the left when right-justified, or on the right if left-justified. In the case when * is used, the width is specified by an additional argument of type int. If the value of the argument is negative, it results with the - flag specified and positive field width. (Note: This is the minimum width: The value is never truncated.)

Read the documentation for more details about the printf function.

answered on Stack Overflow May 16, 2020 by isrnick • edited May 16, 2020 by isrnick
0

This is an old version (2000) of printf for the Mac.

In case of %2s, length is 2, word is "hello".

Some explanations. Here,

if (length > 0 && !ladjust) {
        n = 0;
        p2 = p;

        // Does n = strlen(p)
        for (; *p != '\0' && n < prec; p++)
            n++;

        p = p2;  // p is reset to beginning of string

        // That does nothing, since 4 >= 2
        while (n < length) {
            (*putc)(' ');
            n++;
        }

Then, the part of interest

        n = 0;  // Reset n !

        // Prints the chars, maximum of length characters
        // This will break as soon as n > length
        while (*p != '\0') {
           if (++n > prec || (length > 0 && n > length))
              break;

           (*putc)(*p++);
        }

So it indeed only prints 2 chars, he.

answered on Stack Overflow May 16, 2020 by Breaking not so bad • edited May 16, 2020 by Breaking not so bad

User contributions licensed under CC BY-SA 3.0