K&R Exercise 1-19. Reverse program input one line at a time Intro

I'm going through the K&amp;R book (2nd edition, ANSI C ver.) and want to get the most from it: learn (outdated) C and practice problem-solving at the same time. For that reason, I'm trying to solve the exercises "hardcore" - without using any knowledge from the "future". I'll catch-up with it after I finish the book.

I believe that the author's intention was to give the reader a good exercise, to make the reader think hard about what he can do with the tools introduced, and not to skip ahead to find an easier way. For that reason, I'm only using language features introduced up to this point in the book. That also means I'm using an old C standard.

Compiling with gcc -Wall -Wextra -Wconversion -pedantic -ansi.

K&amp;R Exercise 1-19

Write a function reverse(s) that reverses the character string s. Use it to write a program that reverses its input a line at a time.

Solution

The task explicitly states how the function should look like. For that reason, I've coded it similarly to how the author made his copy function.

The 2nd part of the challenge is to reverse the program input a line at a time, but how long can the line be? It would be too easy to set a hard limit for line length, just loop line by line and return an error if it's exceeded. I can't use dynamic memory allocation to deal with arbitrary long lines since it hasn't been introduced yet so what's left? The book already introduced all the building blocks I need for recursion, so at this point it the only solution.

Code

/* Exercise 1-19. Write a function `reverse(s)` that reverses the
 * character string `s`. Use it to write a program that reverses its
 * input a line at a time. */

#include &lt;stdio.h&gt;
#define BUFSIZE 10 /* line buffer size */

int getline(char line[], int maxline);
void copy(char to[], char from[]);
void reverse(char s[]);
int reverseinput(char lastchar[]);

main()
{
    char lastchar[1];

    while (reverseinput(lastchar) != 0)
        if (lastchar[0] == '\n')
            putchar('\n');
    return 0;
}

/* reverseinput: read from input until end of line is reached and then
 * print the line in reverse */
int reverseinput(char lastchar[])
{
    int len;
    char line[BUFSIZE];
    int retval;

    if ((len = getline(line, BUFSIZE)) &gt; 0) {
        if ((lastchar[0] = line[len-1]) == '\n') {
            line[len-1] = '\0';
            retval = 1;
        }
        else
            retval = reverseinput(lastchar);
        reverse(line);
        printf("%s", line);
    }
    else
        retval = 0;
    return retval;
}

/* getline: read a line into s, return length */
int getline(char s[],int lim)
{
    int c, i;

    for (i=0; i &lt; lim-1 &amp;&amp; (c=getchar())!=EOF &amp;&amp; c!='\n'; ++i)
        s[i] = c;
    if (c == '\n') {
        s[i] = c;
        ++i;
    }
    s[i] = '\0';
    return i;
}

/* copy: copy 'from' into 'to'; assume to is big enough */
void copy(char to[], char from[])
{
    int i;

    i = 0;
    while ((to[i] = from[i]) != '\0')
        ++i;
}

/* reverse: reverse a '\0' terminated string */
void reverse(char s[])
{
    int tail;
    int head;
    char c;

    /* find the last character index */
    tail = 0;
    while (s[tail] != '\0')
        ++tail;
    if (tail &gt; 0)
        --tail;
    /* reverse the string by swapping first and last elements */
    for (head = 0; head &lt; tail; ++head &amp;&amp; --tail) {
        c = s[head];
        s[head] = s[tail];
        s[tail] = c;
    }
}


Test

Running $ ./ch1-ex-1-19-01 &lt;ch1-ex-1-19-01.c | ./ch1-ex-1-19-01 prints the program code :)
Minor: 

getline()

When size is pathologically small (sz &lt;= 1), if (c == '\n') { tests the uninitialized c leading to  undefined behavior.  

With sz &lt;= 0, s[i] = '\0'; is not valid either.

I'd recommend to note the condition that sz &gt; 1 is expected.



Further, I'd use size_t rather than int for the buffer size and re-code to handle those edge cases.

size_t getline(char s[], size_t sz) {
    int c = 0;
    size_t i;

    for (i=0; i + 1 &lt; sz &amp;&amp; (c=getchar())!='\n' &amp;&amp; c!=EOF; ++i) {
        s[i] = c;
    }
    if (c == '\n') {
        s[i] = c;
        ++i;
    }
    if (i &lt; sz) {
      s[i] = '\0';
    }
    return i;
}