K&R C Programs Exercise 5-11

Exercise 5-11. Modify the programs entab and detab (written as exercises in Chapter 1) to accept a list of tab stops as arguments. Use the default tab settings if there are no arguments.

The Chapter 1 versions of entab and detab used a fixed tab width of 8. This version reads tab stop positions from argv (e.g., detab 4 8 12 16) or falls back to every-8-columns if no arguments are given. The key helper is tabstop(col), which checks whether column col (0-based) is in the tab stop list.

detab — Expand Tabs to Spaces

/* K&R Exercise 5-11 — detab: expand tabs, tab stops from argv
 * Compile: gcc -ansi -Wall detab.c -o detab
 * Usage:   ./detab 4 8 12   OR   ./detab (default: every 8) */
#include <stdio.h>
#include <stdlib.h>

#define MAXSTOPS 100
#define DEFAULT_TAB 8

static int tabstops[MAXSTOPS];
static int ntabs = 0;

static int is_tabstop(int col)
{
    int i;
    if (ntabs == 0)
        return (col % DEFAULT_TAB) == 0;
    for (i = 0; i < ntabs; i++)
        if (col == tabstops[i])
            return 1;
    return 0;
}

/* Advance to next tab stop > col */
static int next_tabstop(int col)
{
    int i, best = -1;
    if (ntabs == 0)
        return col - (col % DEFAULT_TAB) + DEFAULT_TAB;
    for (i = 0; i < ntabs; i++)
        if (tabstops[i] > col && (best < 0 || tabstops[i] < best))
            best = tabstops[i];
    return best < 0 ? col + DEFAULT_TAB : best;
}

int main(int argc, char *argv[])
{
    int c, col = 0, spaces, next;

    /* Parse tab stops from arguments */
    while (--argc > 0)
        if (ntabs < MAXSTOPS)
            tabstops[ntabs++] = atoi(*++argv);

    while ((c = getchar()) != EOF) {
        if (c == '\t') {
            next = next_tabstop(col);
            for (spaces = next - col; spaces > 0; spaces--)
                putchar(' ');
            col = next;
        } else if (c == '\n') {
            putchar(c);
            col = 0;
        } else {
            putchar(c);
            col++;
        }
    }
    return 0;
}

entab — Replace Spaces with Tabs

/* K&R Exercise 5-11 — entab: replace spaces with tabs, tab stops from argv
 * Compile: gcc -ansi -Wall entab.c -o entab */
#include <stdio.h>
#include <stdlib.h>

#define MAXSTOPS 100
#define DEFAULT_TAB 8

static int tabstops[MAXSTOPS];
static int ntabs = 0;

static int next_tabstop(int col)
{
    int i, best = -1;
    if (ntabs == 0)
        return col - (col % DEFAULT_TAB) + DEFAULT_TAB;
    for (i = 0; i < ntabs; i++)
        if (tabstops[i] > col && (best < 0 || tabstops[i] < best))
            best = tabstops[i];
    return best < 0 ? col + DEFAULT_TAB : best;
}

int main(int argc, char *argv[])
{
    int c, col = 0, spaces = 0, next;

    while (--argc > 0)
        if (ntabs < MAXSTOPS)
            tabstops[ntabs++] = atoi(*++argv);

    while ((c = getchar()) != EOF) {
        if (c == ' ') {
            spaces++;
            col++;
            next = next_tabstop(col - spaces);
            if (col == next) {
                putchar('\t');
                spaces = 0;
            }
        } else {
            while (spaces-- > 0) putchar(' ');
            spaces = 0;
            putchar(c);
            col = (c == '\n') ? 0 : col + 1;
        }
    }
    return 0;
}

What This Exercise Teaches

  • argv as configuration — parsing command-line numbers into a table; the program has two modes (explicit stops vs default) selected by whether argc has arguments
  • Tab stop lookup — a linear scan through the stops array; for large numbers of stops a binary search or bitset would be more efficient
  • Entab complexity — deciding when accumulated spaces can be replaced by a single tab requires tracking how many spaces were buffered and where the next stop falls

Set Up Your C Environment

← Exercise 5-10  | 
Chapter 5 Solutions  | 
Exercise 5-12 →

Book: The C Programming Language, 2nd Ed — Kernighan & Ritchie

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>