Exercise 5-11-2 (entab with command-line arguments)

Chapter_5     Exercise_5-11-1 Exercise_5-12-1







Exercise 5-11     K&R, p. 118


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.




CONTENTS:     spaces.txt     entab.c




spaces.txt         download


1234567 123456  12345   1234    123     12      1       0
a       ab      abc     abcd    abcde   abcdef  abcdefg h





entab.c         download


#include <stdio.h> // for getchar(), putchar(), printf(), EOF
#include <stdlib.h> // for atoi()

// unexpand UNIX command

#define TAB 4 // 4 spaces
// For every chunk of TAB chars,
// trailing spaces [+'\t'] become '\t',
// [trailing spaces +] '\t' become '\t':
// entab(" ") == "\t" // 4 spaces
// entab(" \t") == "\t" // 3 spaces
// entab(" \t") == "\t" // 2 spaces
// entab(" \t") == "\t" // 1 space
// entab("a ") == "a\t" // 3 spaces
// entab("a \t") == "a\t" // 2 spaces
// entab("ab ") == "ab\t" // 2 spaces
// entab("abc ") == "abc\t" // 1 space
// entab("\t") == "\t"
// entab("a\t") == "a\t"
// entab("*a\t") == "*a\t" // '*' is not '\t',
// entab("**a\t") == "**a\t" // but can be ' '

// unexpand -t 4,8,/4
// ./entab 4 8 /4 // for each line, tab is 4, then 8-4 (8 from first column), then 4
// ./entab 4 8 // for each line, tab is 4, then 8-4 (8 from first column), then 1
// unexpand -t 4,8

// ./entab 4 8 /5
// unexpand -t 4,8,/5
// align at columns 4, 8, 10, 15, 20, etc.

// ./entab // ./entab 4 // ./entab /4 // ./entab 4 /4 // for TAB == 4

void entab(int tab); // turn '\t' into blanks or spaces
void entabs(int tabs[], int len); // turn '\t' into blanks or spaces

int main(int argc, char *argv[])
{
if (argc == 1) // no command-line arguments, just the program name, argv[0]
{
entab(TAB);
return 0; // end program normally
}

if (argc > 100)
{
printf("argc <= 100\n");
return 2; // exit main() with the same error number as unexpand
}

int tab; // current tab value

if (argc == 2) // one command-line argument besides program name
{ // the single argument can be prefixed by '/'
if (*argv[1] == '/') {argv[1]++;} // move past '/'
tab = atoi(argv[1]); // argv[0] is the program name
if (tab <= 0)
{
printf("Tabs must be positive integers (> 0)\n");
return 1; // exit main(), signalling error
}
// else
entab(tab);

return 0; // end program normally
}

// here argc > 2
int tabs[argc]; // no of command-line arguments, along with program name
// tabs[argc-1] will store the default value after args
if (*argv[argc-1] == '/') // last command-line argument
{ // after aligning the text by tabs[], tab defaults to last value
tab = atoi(argv[argc-1]+1); // after '/'
if (tab <= 0)
{
printf("Tabs must be positive integers (> 0)\n");
return 1; // exit main(), signalling error
}
tabs[argc-2] = tab; // last value is also the default
tabs[argc-1] = tab; // tab defaults to last value
}
else
{ // after aligning the text by tabs[], tab defaults to 1 space
tab = atoi(argv[argc-1]);
if (tab <= 0)
{
printf("Tabs must be positive integers (> 0)\n");
return 1; // exit main(), signalling error
}
tabs[argc-2] = tab; // last value
tabs[argc-1] = 1; // tab defaults to 1 space
}

tab = atoi(argv[1]);
if (tab <= 0)
{
printf("Tabs must be positive integers (> 0)\n");
return 1; // exit main(), signalling error
}
// else
tabs[0] = tab;

int i;

for (i = 2; i < argc-1; i++) // from second arg after program name
{ // to penultimate one, argv[argc-2]
tab = atoi(argv[i]);
if (tab <= 0)
{
printf("Tabs must be positive integers (> 0)\n");
return 1; // exit main(), signalling error
}
if (tab <= tabs[i-2])
{
printf("Tabs must be given in ascending order\n");
return 1; // exit main(), signalling error
}
tabs[i-1] = tab; // no of blanks to the next tab stop: tab - column no
}

if (*argv[argc-1] != '/' && tabs[i-1] <= tabs[i-2]) // i == argc-1
{ // if last tab value is prefixed by '/', it can be less equal than the previous value
printf("Tabs must be given in ascending order\n");
return 1; // exit main(), signalling error
}

if (*argv[argc-1] == '/')
{entabs(tabs, argc-1);} // tabs[argc-2] == tabs[argc-1]
else {entabs(tabs, argc);}

return 0;
}
void entab(int tab) // turn '\t' into blanks or spaces
{
int c, i;
int count = 0; // count up to tab chars (next tab stop)
int spaces = 0; // consecutive trailing spaces until '\t' or tab stop
// (chunk of tab chars), spaces <= count
while((c = getchar()) != EOF)
{
if (c == '\n') // count < tab
{
while (spaces > 0) // add trailing spaces at the end of line
{
putchar(' ');
spaces--;
} // here spaces == 0
count = 0; // reset, go to next line
putchar(c); // putchar('\n');
}
else if (c == '\t') // advance to next tab stop, count < tab
{ // replace ([trailing spaces +] '\t') with '\t':
spaces = count = 0; // reset, go to next chunk of tab chars
putchar(c); // putchar('\t');
}
else if (c == ' ')
{
spaces++;
count++;
if (count >= tab) // if (count == tab)
{ // replace trailing spaces with '\t'
putchar('\t');
spaces = count = 0; // reset, go to next chunk of tab chars
} // else continue;
}
else
{
while (spaces > 0) // add trailing spaces before char
{
putchar(' ');
spaces--;
} // here spaces == 0
putchar(c);
count++;
if (count >= tab) // if (count == tab)
{count = 0;} // reset, go to next chunk of tab chars
}
}
}

void entabs(int tabs[], int len) // turn '\t' into blanks or spaces
{
int c, i;
int pos = 0; // current position in tabs[]
int tab = tabs[pos++]; // tabs[0]
int count = 0; // count up to tab chars (next tab stop)
int spaces = 0; // consecutive trailing spaces until '\t' or tab stop
// (chunk of tab chars), spaces <= count

while((c = getchar()) != EOF)
{
if (c == '\n') // count < tab
{
while (spaces > 0) // add trailing spaces at the end of line
{
putchar(' ');
spaces--;
} // here spaces == 0
pos = 0; // reset, go to next line
tab = tabs[pos++]; // reset
count = 0; // reset, go to next line
putchar(c); // putchar('\n');
}
else if (c == '\t') // advance to next tab stop, count < tab
{ // replace ([trailing spaces +] '\t') with '\t':
spaces = 0; // reset, go to next chunk of tab chars
count = tab; // next tab should be greater unless it is the last
putchar(c); // putchar('\t');

if (pos < len-1)
{
tab = tabs[pos++];
}
else
{ // default value tabs[len-1] remains till the end of line
tab = tabs[pos]; // if count >= tab, 0 <= (count % tab) < tab
count %= tab; // if count < tab, (count % tab) == count < tab
} // at the next iteration, tab is constant, count == tab, count % tab == 0
}
else if (c == ' ')
{
spaces++;
count++;
if (count >= tab) // if (count == tab)
{ // replace trailing spaces with '\t'
spaces = 0; // reset, go to next chunk of tab chars
count = tab; // next tab should be greater unless it is the last
putchar('\t');

if (pos < len-1)
{
tab = tabs[pos++];
}
else
{ // default value tabs[len-1] remains till the end of line
tab = tabs[pos]; // if count >= tab, 0 <= (count % tab) < tab
count %= tab; // if count < tab, (count % tab) == count < tab
} // at the next iteration, tab is constant, count == tab, count % tab == 0
} // else continue;
}
else
{
while (spaces > 0) // add trailing spaces before char
{
putchar(' ');
spaces--;
} // here spaces == 0
putchar(c);
count++;
if (count >= tab) // if (count == tab)
{
if (pos < len-1)
{ // count == tab < tabs[pos]
tab = tabs[pos++]; // count < tab
}
else
{ // default value tabs[len-1] remains till the end of line
tab = tabs[pos]; // if count >= tab, 0 <= (count % tab) < tab
count %= tab; // if count < tab, (count % tab) == count < tab
} // at the next iteration, tab is constant, count == tab, count % tab == 0
}
}
}
}
/*
gcc entab.c -o entab
./entab // input from the keyboard
// For TAB == 8 on my computer (blanks or spaces separate the numbers):
1234567 123456 12345 1234 123 12 1 0 // Enter (spaces)
1234567 123456 12345 1234 123 12 1 0 // tabs
// Ctrl^D or Ctrl^C in Linux, Ctrl^Z + Enter in Windows (EOF)

unexpand - // Enter, '-' is stdin, standard input (keyboard)
1234567 123456 12345 1234 123 12 1 0 // Enter (spaces)
1234567 123456 12345 1234 123 12 1 0 // tabs (default 8 spaces)
// Ctrl^D or Ctrl^C in Linux, Ctrl^Z + Enter in Windows (EOF)

./entab 8 // Enter
1234567 123456 12345 1234 123 12 1 0 // Enter (spaces)
1234567 123456 12345 1234 123 12 1 0 // tabs

./entab /8 // Enter
1234567 123456 12345 1234 123 12 1 0 // Enter (spaces)
1234567 123456 12345 1234 123 12 1 0 // tabs

unexpand -t 8 - // Enter
1234567 123456 12345 1234 123 12 1 0 // Enter (spaces)
1234567 123456 12345 1234 123 12 1 0 // tabs

unexpand -t /8 - // Enter
1234567 123456 12345 1234 123 12 1 0 // Enter (spaces)
1234567 123456 12345 1234 123 12 1 0 // tabs

./entab < entab.c // input from source file
./entab < entab // input from binary file

./entab 8 < spaces.txt
1234567 123456 12345 1234 123 12 1 0
a ab abc abcd abcde abcdef abcdefg h

./entab /8 < spaces.txt
1234567 123456 12345 1234 123 12 1 0
a ab abc abcd abcde abcdef abcdefg h

unexpand -t 8 spaces.txt
1234567 123456 12345 1234 123 12 1 0
a ab abc abcd abcde abcdef abcdefg h

unexpand -t /8 spaces.txt
1234567 123456 12345 1234 123 12 1 0
a ab abc abcd abcde abcdef abcdefg h

./entab -1 // Enter
Tabs must be positive integers (> 0)


./entab 2 // Enter
a ab abc abcd abcde abcdef abcdefg h // Enter (spaces)
a ab abc abcd abcde abcdef abcdefg h // tabs

unexpand -t 2 - // Enter
a ab abc abcd abcde abcdef abcdefg h // Enter (spaces)
a ab abc abcd abcde abcdef abcdefg h // tabs
// (un)expand do not replace one space with tab or viceversa

./entab 4 /8 < spaces.txt
1234567 123456 12345 1234 123 12 1 0
a ab abc abcd abcde abcdef abcdefg h

unexpand -t 4,/8 spaces.txt
1234567 123456 12345 1234 123 12 1 0
a ab abc abcd abcde abcdef abcdefg h

./entab 4 8 4 < spaces.txt
Tabs must be given in ascending order

unexpand -t 4,8,4 spaces.txt
unexpand: tab sizes must be ascending

./entab 4 8 < spaces.txt
// tab is 4, then 8, then 1

unexpand -t 4,8 spaces.txt
// tab is 4, then 8, then for 1 no more replacements are made
*/









Chapter_5     Exercise_5-11-1 BACK_TO_TOP Exercise_5-12-1



Comments

Popular posts from this blog

Contents

Blogger Page Margins in Contempo