Exercise 5.12 - entab -m + which accepts arguments

Question

Extend entab and detab to accept the shorthand.

/* Extend entab and detab to accept the shorthand entab -m +n
    to mean tab stops every n columns; starting at column m.
choose convenient size for the default behaviour */

#include<stdio.h>

#define MAXLINE 100    /* maximum line size */
#define TABINC    8    /* default increment size */
#define YES    1
#define NO    0

void esettab(int argc, char *argv[], char *tab);

void entab(char *tab);

/* replace strings of blanks with tabs */

int main(int argc, char *argv[]) {
    char tab[MAXLINE + 1];
    esettab(argc, argv, tab);    /* intialize tab stops */
    entab(tab);  /* replace blanks with tabs */

    return 0;
}

/* The source file for esettab.c */

#include<stdlib.h>

#define MAXLINE 100    /* maximum line size */
#define TABINC    8    /* default tab increment size */
#define YES    1
#define NO    0

/* esettab: set tab stops in the array tab */
void esettab(int argc, char *argv[], char *tab) {
    int i, inc, pos;

    if (argc <= 1) /* default tab stops */
        for (i = 1; i <= MAXLINE; i++)
            if (i % TABINC == 0)
                tab[i] = YES;
            else
                tab[i] = NO;
    else if (argc == 3 && /* user provided range */ *argv[1] == '-' && *argv[2] == '+') {
        pos = atoi(&(*++argv)[1]);
        inc = atoi(&(*++argv)[1]);

        for (i = 1; i <= MAXLINE; i++)
            if (i != pos)
                tab[i] = NO;
            else {
                tab[i] = YES;
                pos += inc;
            }
    } else {
        for (i = 1; i <= MAXLINE; i++)
            tab[i] = NO; /* turn off all tab stops */

        while (--argc > 0) {
            /* walk through argument list */
            pos = atoi(*++argv);
            if (pos > 0 && pos <= MAXLINE)
                tab[pos] = YES;
        }
    }
}

/* entab: replace strings of blanks with tabs and blanks */

void entab(char *tab) {
    int c, pos;
    int nb = 0;    /* # of blanks necessary */
    int nt = 0;    /* # of tabs necessary */

    for (pos = 1; (c = getchar()) != EOF; pos++)
        if (c == ' ') {
            if (tabpos(pos, tab) == NO)
                ++nb;
            else {
                nb = 0;  /* reset the number of blanks */
                ++nt;  /* one more tab */
            }
        } else {
            for (; nt > 0; nt--)
                putchar('\t'); /* output tabs */
            if (c == '\t')
                nb = 0;
            else
                for (; nb > 0; nb--)
                    putchar(' ');
            putchar(c);

            if (c == '\n')
                pos = 0;
            else if (c == '\t')
                while (tabpos(pos, tab) != YES)
                    ++pos;
        }
}

/* The source file for tabpos.c */

int tabpos(int pos, char *tab) {
    if (pos > MAXLINE)
        return YES;
    else
        return tab[pos];
}

Explanation

This program is similar to the Exercise 5.11, where we send the arguments to entab and detab. So the main program accepts argc and argv. The program is to take an argument like -m +n, which means tab stops every n columns;starting at column m.

The main program sends it to esettab function, both argc, argv and a character array tab[MAXLINE-1].

esettab function’s purpose is to fill the character array tab with values YES (1) or NO(0). It determines from the arguments the -m , which is the POS and +n, the increment, and marks at each m, the tab value as YES, and then increments by n, and marks the next tab value as YES. If m and n are not provided, it goes with sane defaults.

The entab function implemented in this program, converts the spaces to tab characters. So, the entab function, when it encounters a space character c, like if(c == ‘ ‘), it checks the corresponding position in the previously formed tab, if the position value is YES or NO. If it is YES, then it increments the tab count, ++nt, if it is not tab position, it increments the blank count ++nb.

When it encounters a first non-space character, then it checks it internal variables, nt and nb. If nt is greater than 0, it meansm that we have tabs to print, so it prints the tab characters for each nt count. It also prints the literal tabs, it encounters.

After printing it all the tabs, it checks the variable, nb, namely if we have determined any blanks. If there blanks to be printed, it prints them out too.

And finally, the prints the character using putchar(c).

We also have to handle cases when we encounter a newline character. When we encounter a newline, like n, we print the new line, but reset the position, so that our position, pos, now becomes 0. When we encounter a tab character, we increment the position (pos) to the next tab, so that when we encounter the next space, we can verify it for the new position.