1
\$\begingroup\$

I'm writing a program that has the possibility of outputting a large amount of text on a single logical line to the console. In that scenario, I would like to wrap the text at word ends so that words are not split on the end of the line: i.e., the string lorem ipsum dolor sit amet should print:

lorem ipsum dolor
sit amet

as opposed to

lorem ipsum dolor si
t amet

I wrote the following function

/* print a string, wrapping lines at word breaks */
void
print_long(const char *s, int maxlen)
{
        int pos, len;
        pos = 0;
        while (pos < strlen(s)) {
                len = maxlen;
                for (int i = maxlen; i > 0; i--) {
                        if (s[pos + i] == '\n') {
                                len = i;
                                break;
                        }
                }
                if (len == maxlen) {
                        for (int i = maxlen; i > 0; i--) {
                                if (s[pos + i] == ' ') {
                                        len = i;
                                        break;
                                }
                        }
                }
                printf("%.*s\n", len, s + pos);
                pos += len + 1;
        }
}

This works on my limited test cases, but it seems overly complex and repetitive. I'm also concerned it could break on scenarios that I haven't tested for. How can I shorten this?

Thanks!

Edit: Additional Test Cases

Lorem ipsum from https://lipsum.com/, maxlen = 80.

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus sed ultricies diam. Sed auctor nisl libero, a rutrum leo dapibus eu. Aliquam sit amet elit enim. Pellentesque pellentesque orci at dolor placerat, vitae eleifend metus bibendum. Pellentesque tristique venenatis orci a dapibus. Suspendisse blandit non lorem eu pulvinar. Fusce commodo vel ligula et porttitor. Duis eget nisl massa.

Integer placerat odio ut sagittis suscipit. Sed placerat bibendum tellus, nec tempus nulla dignissim at. Integer semper laoreet lacinia. Proin vitae volutpat justo, eget maximus erat. Ut lobortis, lectus bibendum ornare cursus, sem enim vehicula felis, vel iaculis mi eros vitae justo. Duis vel auctor massa. Pellentesque tincidunt vehicula ligula, sit amet pharetra metus vestibulum in. Aliquam aliquam viverra vehicula. Quisque fermentum pulvinar massa non tempus. Nulla in magna ante.

Morbi non mattis elit. Donec non sagittis lorem. Donec at commodo turpis. Maecenas quis libero a metus rutrum efficitur. Duis fermentum iaculis erat venenatis porttitor. Cras tortor nibh, commodo in mi a, venenatis commodo ipsum. Donec a elementum massa, nec porttitor tortor. Aenean dignissim tortor eget lacus fringilla, auctor hendrerit turpis hendrerit. Vivamus mattis purus ac malesuada luctus.

Sed ullamcorper arcu consectetur ex interdum semper. Integer blandit risus turpis, tempor porttitor augue porttitor et. Nunc sit amet consequat augue. Nullam malesuada risus eu neque elementum ullamcorper. Morbi volutpat leo vitae lacus sagittis, vel viverra lorem scelerisque. Nulla enim ipsum, cursus accumsan magna quis, imperdiet semper orci. Phasellus nisl dui, vehicula ac venenatis vel, imperdiet vitae tortor. Vestibulum eget consequat urna. Etiam id lectus sed velit dignissim iaculis. Nam ac lacus non nisi feugiat suscipit. Nullam eleifend ultricies sapien, eget faucibus erat sollicitudin in. Phasellus sed tellus id diam interdum iaculis. Curabitur vulputate, ante ut commodo tristique, metus lacus rutrum magna, ut pulvinar orci augue ac orci. Ut a felis libero. Integer mattis magna quis diam blandit, in auctor urna hendrerit.

Curabitur vestibulum ex in erat euismod, in lacinia neque vestibulum. Phasellus bibendum nunc sed nibh aliquam, suscipit fringilla dolor semper. Nulla vel felis scelerisque, iaculis velit in, gravida turpis. Cras dolor lacus, convallis et ex a, rutrum pretium mauris. Nam dapibus interdum ipsum, et scelerisque magna. Pellentesque tincidunt viverra consequat. Ut finibus nibh et enim lacinia, non sagittis felis viverra. Nulla laoreet mollis dui. Fusce erat lacus, bibendum ac urna in, pretium elementum velit.

Shold result in

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus sed ultricies
diam. Sed auctor nisl libero, a rutrum leo dapibus eu. Aliquam sit amet elit
enim. Pellentesque pellentesque orci at dolor placerat, vitae eleifend metus
bibendum. Pellentesque tristique venenatis orci a dapibus. Suspendisse blandit
non lorem eu pulvinar. Fusce commodo vel ligula et porttitor. Duis eget nisl
massa.
Integer placerat odio ut sagittis suscipit. Sed placerat bibendum tellus, nec
tempus nulla dignissim at. Integer semper laoreet lacinia. Proin vitae volutpat
justo, eget maximus erat. Ut lobortis, lectus bibendum ornare cursus, sem enim
vehicula felis, vel iaculis mi eros vitae justo. Duis vel auctor massa.
Pellentesque tincidunt vehicula ligula, sit amet pharetra metus vestibulum in.
Aliquam aliquam viverra vehicula. Quisque fermentum pulvinar massa non tempus.
Nulla in magna ante.
Morbi non mattis elit. Donec non sagittis lorem. Donec at commodo turpis.
Maecenas quis libero a metus rutrum efficitur. Duis fermentum iaculis erat
venenatis porttitor. Cras tortor nibh, commodo in mi a, venenatis commodo ipsum.
Donec a elementum massa, nec porttitor tortor. Aenean dignissim tortor eget
lacus fringilla, auctor hendrerit turpis hendrerit. Vivamus mattis purus ac
malesuada luctus.
Sed ullamcorper arcu consectetur ex interdum semper. Integer blandit risus
turpis, tempor porttitor augue porttitor et. Nunc sit amet consequat augue.
Nullam malesuada risus eu neque elementum ullamcorper. Morbi volutpat leo vitae
lacus sagittis, vel viverra lorem scelerisque. Nulla enim ipsum, cursus accumsan
magna quis, imperdiet semper orci. Phasellus nisl dui, vehicula ac venenatis
vel, imperdiet vitae tortor. Vestibulum eget consequat urna. Etiam id lectus sed
velit dignissim iaculis. Nam ac lacus non nisi feugiat suscipit. Nullam eleifend
ultricies sapien, eget faucibus erat sollicitudin in. Phasellus sed tellus id
diam interdum iaculis. Curabitur vulputate, ante ut commodo tristique, metus
lacus rutrum magna, ut pulvinar orci augue ac orci. Ut a felis libero. Integer
mattis magna quis diam blandit, in auctor urna hendrerit.
Curabitur vestibulum ex in erat euismod, in lacinia neque vestibulum. Phasellus
bibendum nunc sed nibh aliquam, suscipit fringilla dolor semper. Nulla vel felis
scelerisque, iaculis velit in, gravida turpis. Cras dolor lacus, convallis et ex
a, rutrum pretium mauris. Nam dapibus interdum ipsum, et scelerisque magna.
Pellentesque tincidunt viverra consequat. Ut finibus nibh et enim lacinia, non
sagittis felis viverra. Nulla laoreet mollis dui. Fusce erat lacus, bibendum ac
urna in, pretium elementum velit.
Loremipsumdolorsitamet,consecteturadipiscingelit.Phasellussedultriciesdiam.Seductornislibero,arutrumleodapibus eu. Aliquam sit amet elit enim. Pellentesque pellentesque orci at dolor placerat, vitae eleifend metus bibendum. Pellentesque tristique venenatis orci a dapibus. Suspendisse blandit non lorem eu pulvinar. Fusce commodo vel ligula et porttitor. Duis eget nisl massa.

Integerplaceratodiout sagittis suscipit. Sed placerat bibendum tellus, nec tempus nulla dignissim atInteger emper laoreetcinia.Proinvitaejusto,egeterat.Ut lobortis, lectus bibendum ornare cursus,sem enim vehicula felis,veliaculismi eros vitaejusto. Duisvelauctorssa. Pellentesquetinciduntvehiculaligulasitmetpharetrametusvestibulumin. Aliquamaliquam viverra vehicula. Quisque fermentum pulvinar massa non tempus. Nulla in magna ante.

Morbi non mattis elit. Donec non sagittis lorem. Donec at commodo turpis. Maecenas quis libero a metus rutrum efficitur. Duis fermentum iaculis erat venenatis porttitor. Cras tortor nibh, commodo in mi a, venenatis commodo ipsum. Donec a elementum massa, nec porttitor tortor. Aenean dignissimtortoregetlacusfringilla,auctorhendreritturpishendreritVivamusmattispurusacmalesuadaluctus.

Should result in

Loremipsumdolorsitamet,consecteturadipiscingelit.Phasellussedultriciesdiam.Seductorni
libero,arutrumleodapibus eu. Aliquam sit amet elit enim. Pellentesque
pellentesque orci at dolor placerat, vitae eleifend metus bibendum. Pellentesque
tristique venenatis orci a dapibus. Suspendisse blandit non lorem eu pulvinar.
Fusce commodo vel ligula et porttitor. Duis eget nisl massa.
Integerplaceratodiout sagittis suscipit. Sed placerat bibendum tellus, nec
tempus nulla dignissim atInteger emper laoreetcinia.Proinvitaejusto,egeterat.Ut
lobortis, lectus bibendum ornare cursus,sem enim vehicula felis,veliaculismi
eros vitaejusto. Duisvelauctorssa.
Pellentesquetinciduntvehiculaligulasitmetpharetrametusvestibulumin.
Aliquamaliquam viverra vehicula. Quisque fermentum pulvinar massa non tempus.
Nulla in magna ante.
Morbi non mattis elit. Donec non sagittis lorem. Donec at commodo turpis.
Maecenas quis libero a metus rutrum efficitur. Duis fermentum iaculis erat
venenatis porttitor. Cras tortor nibh, commodo in mi a, venenatis commodo ipsum.
Donec a elementum massa, nec porttitor tortor. Aenean
dignissimtortoregetlacusfringilla,auctorhendreritturpishendreritVivamusmattispur
sacmalesuadaluctus.

The second is identical to the previous case, but spaces are randomly removed to increase word length.

\$\endgroup\$
4
  • 1
    \$\begingroup\$ Two problems: You're accessing past the end of your string, which results Undefined Behavior. And your code does not properly handle a word that is longer than your line length. \$\endgroup\$ Jan 24, 2021 at 0:32
  • 1
    \$\begingroup\$ Could you add your unit tests to the question? Having a single test case example for such a long function is not enough, by far. \$\endgroup\$ Jan 24, 2021 at 0:37
  • \$\begingroup\$ @1201ProgramAlarm s + pos is never going to go past the end of the string due to the while loop, and, if the string is properly null-terminated, printf should never go past the bounds either, right? As for a word longer than line length, it should split at maxlen as if there was no wrapping, which is what I observed in testing. Am I misinterpreting my code or what you've said? \$\endgroup\$
    – user232645
    Jan 24, 2021 at 0:38
  • 1
    \$\begingroup\$ The first character you access is s[pos + maxlen] (because i starts at maxlen) which can be past the end. For long words, len will be maxlen, and you add that +1 to pos so you'll skip a character (the first line of your second example shows this, as you lose an 's'). \$\endgroup\$ Jan 24, 2021 at 1:06

2 Answers 2

0
\$\begingroup\$

This was... rough.

  • Your method is incorrect
  • @aghast's method is incorrect
  • Your reference outputs are incorrect
  • You're missing unit tests; if you had included some, this would have been caught.

First, all three methods along with the unit testing code:

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


#define LONG_OUTPUT 0


// print a string, wrapping lines at word breaks
static void lincoln(const char *s, size_t maxlen, FILE *out)
{
    size_t len;

    for (size_t pos = 0; pos < strlen(s); pos += len + 1)
    {
        len = maxlen;

        for (size_t i = maxlen; i > 0; i--)
        {
            if (s[pos + i] == '\n')
            {
                len = i;
                break;
            }
        }

        if (len == maxlen) 
        {
            for (int i = maxlen; i > 0; i--) 
            {
                if (s[pos + i] == ' ') 
                {
                    len = i;
                    break;
                }
            }
        }

        fprintf(out, "%.*s\n", (int)len, s + pos);
    }
}

static void aghast(const char *s, size_t maxlen, FILE *out)
{
    size_t len;

    for (size_t pos = 0; pos < strlen(s); pos += len + 1)
    {
        len = maxlen;

        for (size_t i = 0; i < maxlen; ++i) {
            char ch = s[pos + i];
            if ('\n' == ch) {
                len = i;
                break;
            }
            else if (' ' == ch) {
                len = i;
                /* NB: no break! */
            }
        }

        fprintf(out, "%.*s\n", (int)len, s + pos);
    }
}


static void reinderien(const char *s, size_t maxlen, FILE *out)
{
    for (size_t len = strlen(s); len > 0;)
    {
        if (len <= maxlen)
        {
            fwrite(s, 1, len, out);
            fputc('\n', out);
            break;
        }

        // The memrchr() function is a GNU extension, available since glibc 2.1.91. 
        // It would be better here, but anyway...
        size_t end, next;
        for (end = maxlen;; end--)
        {
            if (end == 0)
            {
                end = maxlen;
                next = maxlen;
                break;
            }
            if (s[end] == ' ')
            {
                next = end + 1;
                break;
            }
        }

        fwrite(s, 1, end, out);
        fputc('\n', out);
        s += next;
        len -= next;
    }
}

const char *method_names[] = { "lincoln", "aghast", "reinderien" };
void(*methods[])(const char *s, size_t maxlen, FILE *out) = {
    lincoln,
    aghast,
    reinderien,
};


const char *test_in[] = {
    "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus sed ultricies diam. Sed auctor nisl libero, a rutrum leo dapibus eu. Aliquam sit amet elit enim. Pellentesque pellentesque orci at dolor placerat, vitae eleifend metus bibendum. Pellentesque tristique venenatis orci a dapibus. Suspendisse blandit non lorem eu pulvinar. Fusce commodo vel ligula et porttitor. Duis eget nisl massa.",
    "Integer placerat odio ut sagittis suscipit. Sed placerat bibendum tellus, nec tempus nulla dignissim at. Integer semper laoreet lacinia. Proin vitae volutpat justo, eget maximus erat. Ut lobortis, lectus bibendum ornare cursus, sem enim vehicula felis, vel iaculis mi eros vitae justo. Duis vel auctor massa. Pellentesque tincidunt vehicula ligula, sit amet pharetra metus vestibulum in. Aliquam aliquam viverra vehicula. Quisque fermentum pulvinar massa non tempus. Nulla in magna ante.",
    "Morbi non mattis elit. Donec non sagittis lorem. Donec at commodo turpis. Maecenas quis libero a metus rutrum efficitur. Duis fermentum iaculis erat venenatis porttitor. Cras tortor nibh, commodo in mi a, venenatis commodo ipsum. Donec a elementum massa, nec porttitor tortor. Aenean dignissim tortor eget lacus fringilla, auctor hendrerit turpis hendrerit. Vivamus mattis purus ac malesuada luctus.",
    "Sed ullamcorper arcu consectetur ex interdum semper. Integer blandit risus turpis, tempor porttitor augue porttitor et. Nunc sit amet consequat augue. Nullam malesuada risus eu neque elementum ullamcorper. Morbi volutpat leo vitae lacus sagittis, vel viverra lorem scelerisque. Nulla enim ipsum, cursus accumsan magna quis, imperdiet semper orci. Phasellus nisl dui, vehicula ac venenatis vel, imperdiet vitae tortor. Vestibulum eget consequat urna. Etiam id lectus sed velit dignissim iaculis. Nam ac lacus non nisi feugiat suscipit. Nullam eleifend ultricies sapien, eget faucibus erat sollicitudin in. Phasellus sed tellus id diam interdum iaculis. Curabitur vulputate, ante ut commodo tristique, metus lacus rutrum magna, ut pulvinar orci augue ac orci. Ut a felis libero. Integer mattis magna quis diam blandit, in auctor urna hendrerit.",
    "Curabitur vestibulum ex in erat euismod, in lacinia neque vestibulum. Phasellus bibendum nunc sed nibh aliquam, suscipit fringilla dolor semper. Nulla vel felis scelerisque, iaculis velit in, gravida turpis. Cras dolor lacus, convallis et ex a, rutrum pretium mauris. Nam dapibus interdum ipsum, et scelerisque magna. Pellentesque tincidunt viverra consequat. Ut finibus nibh et enim lacinia, non sagittis felis viverra. Nulla laoreet mollis dui. Fusce erat lacus, bibendum ac urna in, pretium elementum velit.",

    "Loremipsumdolorsitamet,consecteturadipiscingelit. Phasellussedultriciesdiam. Seductornislibero,arutrumleodapibus eu. Aliquam sit amet elit enim. Pellentesque pellentesque orci at dolor placerat, vitae eleifend metus bibendum. Pellentesque tristique venenatis orci a dapibus. Suspendisse blandit non lorem eu pulvinar. Fusce commodo vel ligula et porttitor. Duis eget nisl massa.",
    "Integerplaceratodiout sagittis suscipit. Sed placerat bibendum tellus, nec tempus nulla dignissim atInteger emper laoreetcinia. Proinvitaejusto,egeterat. Ut lobortis, lectus bibendum ornare cursus,sem enim vehicula felis,veliaculismi eros vitaejusto. Duisvelauctorssa. Pellentesquetinciduntvehiculaligulasitmetpharetrametusvestibulumin. Aliquamaliquam viverra vehicula. Quisque fermentum pulvinar massa non tempus. Nulla in magna ante.",
    "Morbi non mattis elit. Donec non sagittis lorem. Donec at commodo turpis. Maecenas quis libero a metus rutrum efficitur. Duis fermentum iaculis erat venenatis porttitor. Cras tortor nibh, commodo in mi a, venenatis commodo ipsum. Donec a elementum massa, nec porttitor tortor. Aenean dignissimtortoregetlacusfringilla,auctorhendreritturpishendreritVivamusmattispurusacmalesuadaluctus.",
};

const char *test_out[] = {
    "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus sed ultricies\n"
    "diam. Sed auctor nisl libero, a rutrum leo dapibus eu. Aliquam sit amet elit\n"
    "enim. Pellentesque pellentesque orci at dolor placerat, vitae eleifend metus\n"
    "bibendum. Pellentesque tristique venenatis orci a dapibus. Suspendisse blandit\n"
    "non lorem eu pulvinar. Fusce commodo vel ligula et porttitor. Duis eget nisl\n"
    "massa.\n",

    "Integer placerat odio ut sagittis suscipit. Sed placerat bibendum tellus, nec\n"
    "tempus nulla dignissim at. Integer semper laoreet lacinia. Proin vitae volutpat\n"
    "justo, eget maximus erat. Ut lobortis, lectus bibendum ornare cursus, sem enim\n"
    "vehicula felis, vel iaculis mi eros vitae justo. Duis vel auctor massa.\n"
    "Pellentesque tincidunt vehicula ligula, sit amet pharetra metus vestibulum in.\n"
    "Aliquam aliquam viverra vehicula. Quisque fermentum pulvinar massa non tempus.\n"
    "Nulla in magna ante.\n",

    "Morbi non mattis elit. Donec non sagittis lorem. Donec at commodo turpis.\n"
    "Maecenas quis libero a metus rutrum efficitur. Duis fermentum iaculis erat\n"
    "venenatis porttitor. Cras tortor nibh, commodo in mi a, venenatis commodo ipsum.\n"
    "Donec a elementum massa, nec porttitor tortor. Aenean dignissim tortor eget\n"
    "lacus fringilla, auctor hendrerit turpis hendrerit. Vivamus mattis purus ac\n"
    "malesuada luctus.\n",

    "Sed ullamcorper arcu consectetur ex interdum semper. Integer blandit risus\n"
    "turpis, tempor porttitor augue porttitor et. Nunc sit amet consequat augue.\n"
    "Nullam malesuada risus eu neque elementum ullamcorper. Morbi volutpat leo vitae\n"
    "lacus sagittis, vel viverra lorem scelerisque. Nulla enim ipsum, cursus accumsan\n"
    "magna quis, imperdiet semper orci. Phasellus nisl dui, vehicula ac venenatis\n"
    "vel, imperdiet vitae tortor. Vestibulum eget consequat urna. Etiam id lectus sed\n"
    "velit dignissim iaculis. Nam ac lacus non nisi feugiat suscipit. Nullam eleifend\n"
    "ultricies sapien, eget faucibus erat sollicitudin in. Phasellus sed tellus id\n"
    "diam interdum iaculis. Curabitur vulputate, ante ut commodo tristique, metus\n"
    "lacus rutrum magna, ut pulvinar orci augue ac orci. Ut a felis libero. Integer\n"
    "mattis magna quis diam blandit, in auctor urna hendrerit.\n",

    "Curabitur vestibulum ex in erat euismod, in lacinia neque vestibulum. Phasellus\n"
    "bibendum nunc sed nibh aliquam, suscipit fringilla dolor semper. Nulla vel felis\n"
    "scelerisque, iaculis velit in, gravida turpis. Cras dolor lacus, convallis et ex\n"
    "a, rutrum pretium mauris. Nam dapibus interdum ipsum, et scelerisque magna.\n"
    "Pellentesque tincidunt viverra consequat. Ut finibus nibh et enim lacinia, non\n"
    "sagittis felis viverra. Nulla laoreet mollis dui. Fusce erat lacus, bibendum ac\n"
    "urna in, pretium elementum velit.\n",

    "Loremipsumdolorsitamet,consecteturadipiscingelit. Phasellussedultriciesdiam.\n"
    "Seductornislibero,arutrumleodapibus eu. Aliquam sit amet elit enim. Pellentesque\n"
    "pellentesque orci at dolor placerat, vitae eleifend metus bibendum. Pellentesque\n"
    "tristique venenatis orci a dapibus. Suspendisse blandit non lorem eu pulvinar.\n"
    "Fusce commodo vel ligula et porttitor. Duis eget nisl massa.\n",

    "Integerplaceratodiout sagittis suscipit. Sed placerat bibendum tellus, nec\n"
    "tempus nulla dignissim atInteger emper laoreetcinia. Proinvitaejusto,egeterat.\n"
    "Ut lobortis, lectus bibendum ornare cursus,sem enim vehicula felis,veliaculismi\n"
    "eros vitaejusto. Duisvelauctorssa.\n"
    "Pellentesquetinciduntvehiculaligulasitmetpharetrametusvestibulumin.\n"
    "Aliquamaliquam viverra vehicula. Quisque fermentum pulvinar massa non tempus.\n"
    "Nulla in magna ante.\n",

    "Morbi non mattis elit. Donec non sagittis lorem. Donec at commodo turpis.\n"
    "Maecenas quis libero a metus rutrum efficitur. Duis fermentum iaculis erat\n"
    "venenatis porttitor. Cras tortor nibh, commodo in mi a, venenatis commodo ipsum.\n"
    "Donec a elementum massa, nec porttitor tortor. Aenean\n"
    "dignissimtortoregetlacusfringilla,auctorhendreritturpishendreritVivamusmattispur\n"
    "usacmalesuadaluctus.\n",
};


int main(void)
{
    char fn[128];
    snprintf(fn, sizeof(fn), "%s/test-temp.txt", getenv("TEMP"));
    FILE *ftest = fopen(fn, "wb+");
    assert(ftest);

    size_t start = 0;

    for (int i_test = 0; i_test < sizeof(test_in) / sizeof(*test_in); i_test++)
    {
        for (int i_method = 0; i_method < sizeof(methods) / sizeof(*methods); i_method++)
        {
            methods[i_method](test_in[i_test], 80, ftest);

            size_t end = ftell(ftest),
                len = end - start;
            assert(end != -1);
            assert(fseek(ftest, start, SEEK_SET) == 0);

            char *buffer = malloc(len + 1);
            assert(buffer);
            assert(fread(buffer, 1, len, ftest) == len);
            buffer[len] = '\0';

            if (memcmp(test_out[i_test], buffer, len))
            {
                fprintf(
                    stderr,
                    "Method %s test %d failed"
#if LONG_OUTPUT
                    ":\n%s"
                    "\tactual != expected\n"
                    "%s"
#endif
                    "\n",
                    method_names[i_method], i_test
#if LONG_OUTPUT
                    , buffer, test_out[i_test]
#endif
                );
            }
            else printf(
                "Method %s test %d passed\n"
#if LONG_OUTPUT
                "\n"
#endif
                , method_names[i_method], i_test);

            free(buffer);
            start = end;
        }

        printf("\n");
    }

    fclose(ftest);
    remove(fn);

    return 0;
}

Test results for each entry and method:

Method lincoln test 0 passed
Method aghast test 0 failed
Method reinderien test 0 passed

Method lincoln test 1 passed
Method aghast test 1 passed
Method reinderien test 1 passed

Method lincoln test 2 passed
Method aghast test 2 failed
Method reinderien test 2 passed

Method lincoln test 3 passed
Method aghast test 3 failed
Method reinderien test 3 passed

Method lincoln test 4 failed
Method aghast test 4 failed
Method reinderien test 4 passed

Method lincoln test 5 failed
Method aghast test 5 failed
Method reinderien test 5 passed

Method lincoln test 6 passed
Method aghast test 6 passed
Method reinderien test 6 passed

Method lincoln test 7 failed
Method aghast test 7 failed
Method reinderien test 7 passed

The test data shown here are now correct (so far as I can tell). Changes in my suggested method:

  • Do indeed scan backward from the end of each line
  • Use a file pointer rather than assuming stdout for testing purposes
  • Only have one inner loop, not two
  • Do pointer-incrementing and a diminishing len rather than an increasing pos
  • Prefer for loops when maintaining a running counter

The full test output is very long; you can see it if you toggle LONG_OUTPUT.

\$\endgroup\$
2
\$\begingroup\$

First, the bug

Consider this: what happens if you have a string that is shorter than maxlen as your input?

Assume that maxlen=78, and the input string s is "foo bar". You have strlen(s) == 7. So then pos = 0 at first matches (pos < maxlen), so the while loop starts, the for loop runs, and s[pos + i] is s[0 + maxlen] is s[0 + 78] which is considerably larger than s[7] where the terminal NUL character is stored in the input string.

This is easy enough to fix, just compare the length against maxlen before you start looping. You might be done before you've begun!

But this problem also appears when your string is longer than maxlen. If you have maxlen=78 and strlen(s) == 84 and s[70] = '\n', then your first pass consumes s[0..70] and the second pass starts with pos = 71, enters the first for loop with i = maxlen and accesses s[pos + i] which is s[71 + 78], far outside the s[84] that you know is safe.

This is less easy. You need to compare maxlen against the length remaining, and drop down to a lower value if need be. You can probably solve both cases by using something like min(maxlen, strlen(s) - pos). You might want to cache strlen(s) though.

Next, the signature

You are receiving your maxlen parameter as an int. This probably makes some sense due to pointer subtraction, but you should convert that to a size_t either in the declaration or as soon as you validate the value (which you are presently not doing) inside the function.

Scan forwards

Right now you have two loops that both scan backwards from an arbitrary point. In the case where the line is long, you scan the entire string once looking for newlines, don't find one, then scan looking for spaces.

You can combine your loops if you remember the last space, but automatically print on newline:

for (size_t i = 0; i < maxlen; ++i) {
    char ch = s[pos + i];
    if ('\n' == ch) {
        len = i;
        break;
    }
    else if (' ' == ch) {
        len = i;
        /* NB: no break! */
    }
}
\$\endgroup\$
1
  • \$\begingroup\$ There is a bug here that prevents endings from being correctly processed. Also, scanning forward is not a great idea - scanning backward is still more efficient. \$\endgroup\$
    – Reinderien
    Jan 25, 2021 at 1:28

Your Answer

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