Exercise 5-11-1 (detab with command-line arguments)

Chapter_5     Exercise_5-10 Exercise_5-11-2







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:     tabs.txt     detab.c




tabs.txt         download


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





detab.c         download


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

// expand UNIX command

#define TAB 4 // 4 spaces
// For every chunk of TAB chars,
// detab("abc\t") == "abc " // 1 space
// detab("ab\t") == "ab " // 2 spaces
// detab("a\t") == "a " // 3 spaces
// detab("\t") == " " // 4 spaces

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

// ./detab 4 8 /5
// align at columns 4, 8, 10, 15, 20, etc.

// ./detab // ./detab 4 // ./detab /4 // ./detab 4 /4 // for TAB == 4

void detab(int tab); // turn '\t' into blanks or spaces
void detabs(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]
{
detab(TAB);
return 0; // end program normally
}

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

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
detab(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] == '/')
{detabs(tabs, argc-1);} // tabs[argc-2] == tabs[argc-1]
else {detabs(tabs, argc);}

return 0;
}

void detab(int tab) // turn '\t' into blanks or spaces
{
int c, i;
int count = 0; // count up to tab chars (next tab stop)

while((c = getchar()) != EOF)
{
if (c == '\n')
{
count = 0; // reset, go to next line
putchar(c); // putchar('\n');
}
else if (c == '\t') // advance to next tab stop
{
for (i = 0; i < tab-count; i++)
{putchar(' ');} // add spaces up to next tab stop
count = 0; // reset, go to next chunk of tab chars
}
else
{
putchar(c);
count++;
if (count >= tab) // if (count == tab)
{count = 0;} // reset, go to next chunk of tab chars
}
}
}

void detabs(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)

while((c = getchar()) != EOF)
{
if (c == '\n')
{
pos = 0; // reset, go to next line
tab = tabs[pos++]; // reset
count = 0; // reset
putchar(c); // putchar('\n');
}
else if (c == '\t') // advance to next tab stop
{
for (i = 0; i < tab-count; i++)
{putchar(' ');} // add spaces up to next tab stop
count = tab; // count the added spaces
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
{
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 detab.c -o detab
./detab // input from the keyboard
// For TAB == 8 on my computer (tabs separate the numbers):
1234567 123456 12345 1234 123 12 1 0 // Enter (tabs)
1234567 123456 12345 1234 123 12 1 0 // spaces
// Ctrl^D or Ctrl^C in Linux, Ctrl^Z + Enter in Windows (EOF)

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

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

./detab 8 // Enter
a ab abc abcd abcde abcdef abcdefg h // Enter (tabs)
a ab abc abcd abcde abcdef abcdefg h // spaces
// Ctrl^D or Ctrl^C in Linux, Ctrl^Z + Enter in Windows (EOF)

expand -t 8 - // Enter
a ab abc abcd abcde abcdef abcdefg h // Enter (tabs)
a ab abc abcd abcde abcdef abcdefg h // spaces
// Ctrl^D or Ctrl^C in Linux, Ctrl^Z + Enter in Windows (EOF)

./detab 2 // Enter
a ab abc abcd abcde abcdef abcdefg h // Enter (tabs)
a ab abc abcd abcde abcdef abcdefg h // spaces
// Ctrl^D or Ctrl^C in Linux, Ctrl^Z + Enter in Windows (EOF)

expand -t 2 - // Enter
a ab abc abcd abcde abcdef abcdefg h // Enter (tabs)
a ab abc abcd abcde abcdef abcdefg h // spaces
// Ctrl^D or Ctrl^C in Linux, Ctrl^Z + Enter in Windows (EOF)

./detab < tabs.txt // input from text file (with TAB == 8)
1234567 123456 12345 1234 123 12 1 0
a ab abc abcd abcde abcdef abcdefg h

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

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

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

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

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

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

expand -t 4 tabs.txt
1234567 123456 12345 1234 123 12 1 0
a ab abc abcd abcde abcdef abcdefg h

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

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

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

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

./detab < detab.c // input from source file

./detab < detab // input from binary file
*/









Chapter_5     Exercise_5-10 BACK_TO_TOP Exercise_5-11-2



Comments

Popular posts from this blog

Contents

Blogger Page Margins in Contempo