Exercise 5-11. Modify the programs
entabanddetab(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