#include <stdio.h> // for printf(), putchar()
int main(int argc, char *argv[])
{
int i;
for (i = 1; i < argc; i++)
{
printf("%s%s", argv[i], (i < argc-1) ? " " : "");
}
putchar('\n');
return 0;
}
/*
gcc echo1.c -o echo1
./echo1
./echo1 Hello!
Hello!
./echo1 Hello, world!
Hello, world!
*/
*****************************************************************************************
*****************************************************************************************
*****************************************************************************************
#include <stdio.h> // for printf(), putchar()
int main(int argc, char *argv[])
{
while (--argc) // while (argc-- > 1)
{
printf("%s%s", *++argv, (argc > 1) ? " " : "");
}
putchar('\n');
return 0;
}
/*
gcc echo2.c -o echo2
./echo2
./echo2 Hello!
Hello!
./echo2 Hello, world!
Hello, world!
*/
*****************************************************************************************
*****************************************************************************************
*****************************************************************************************
#include <stdio.h> // for printf(), putchar()
int main(int argc, char *argv[])
{
while (--argc) // while (argc-- > 1)
{
printf((argc > 1) ? "%s " : "%s", *++argv);
}
putchar('\n');
return 0;
}
/*
gcc echo3.c -o echo3
./echo3
./echo3 Hello!
Hello!
./echo3 Hello, world!
Hello, world!
*/
*****************************************************************************************
*****************************************************************************************
*****************************************************************************************
*****************************************************************************************
*****************************************************************************************
Ah Love! could you and I with Fate conspire
To grasp this sorry Scheme of Things entire,
Would not we shatter it to bits -- and then
Re-mould it nearer to the Heart's Desire!
#include <stdio.h> // for getchar(), putchar(), printf(), EOF
#include <string.h> // for strstr()
#define MAXLINE 1000
int getLine(char *line, int max); // getline() is declared by stdio.h
// print lines that match pattern from first argument
int main(int argc, char *argv[])
{
char line[MAXLINE];
int found = 0, len;
if (argc != 2)
{printf("Usage: ./find pattern\n");}
else
{
while ((len = getLine(line, MAXLINE)) > 0)
{
if (strstr(line, argv[1]) != NULL)
{
printf("%s", line);
if (line[len-1] != '\n')
{putchar('\n');} // line may not end with '\n'
found++;
}
}
}
return found;
}
// getLine(): read a line into s[], return length
int getLine(char *s, int lim)
{
int c = EOF; // initialize
char *p;
// getchar() is only executed if ((p-s) < (lim-1)):
for (p = s; (p-s) < (lim-1); p++)
{ // from 0 to lim-2; s[lim-2]='\n' or not, s[lim-1]='\0'
c = getchar();
if (c == EOF) // (p-s) < (lim-1), getchar() executed
{
*p = '\0'; // the null character ends a string
return p-s;
}
if (c == '\n') // (p-s) < (lim-1), getchar() executed
{
*p++ = c; // '\n'
*p = '\0'; // the null character ends a string
return p-s;
}
*p = c;
} // here c != EOF && c != '\n'
*p = '\0'; // max(p-s) == (lim-1)
return p-s;
}
/*
gcc find.c -o find
./find
Usage: ./find pattern
./find "ould " "Khayam.txt"
Usage: ./find pattern
./find "ould " < Khayam.txt
Ah Love! could you and I with Fate conspire
Would not we shatter it to bits -- and then
Re-mould it nearer to the Heart's Desire!
*/
*****************************************************************************************
*****************************************************************************************
*****************************************************************************************
#include <stdio.h> // for getchar(), putchar(), printf(), EOF
#include <string.h> // for strstr()
#define MAXLINE 1000
int getLine(char *line, int max); // getline() is declared by stdio.h
// print lines that match pattern with optional arguments or parameters
int main(int argc, char *argv[])
{
char line[MAXLINE];
long lineno = 0; // line number
int c, except = 0, number = 0, found = 0, len; // length
while (--argc > 0 && (*++argv)[0] == '-') // optional arguments
{
while (c = *++argv[0]) // -x -n -xn -nx
{
switch(c)
{
case 'x' :
except = 1;
break;
case 'n' :
number = 1;
break;
default:
printf("Illegal option: '%c'\n", c);
printf("Usage: ./findp -x -n pattern\n");
argc = 0; // end program
found = -1; // signal error
return found; // exit main()
}
}
}
if (argc != 1) // pattern
{printf("Usage: ./findp -x -n pattern\n");}
else
{
while ((len = getLine(line, MAXLINE)) > 0)
{
lineno++;
if (strstr(line, *argv) != NULL)
{
if (!except)
{
if (number)
{printf("%ld: ", lineno);}
printf("%s", line);
if (line[len-1] != '\n')
{putchar('\n');} // line may not end with '\n'
}
found++;
}
else if (except)
{
if (number)
{printf("%ld: ", lineno);}
printf("%s", line);
if (line[len-1] != '\n')
{putchar('\n');} // line may not end with '\n'
}
}
}
return found;
}
// getLine(): read a line into s[], return length
int getLine(char *s, int lim)
{
int c = EOF; // initialize
char *p;
// getchar() is only executed if ((p-s) < (lim-1)):
for (p = s; (p-s) < (lim-1); p++)
{ // from 0 to lim-2; s[lim-2]='\n' or not, s[lim-1]='\0'
c = getchar();
if (c == EOF) // (p-s) < (lim-1), getchar() executed
{
*p = '\0'; // the null character ends a string
return p-s;
}
if (c == '\n') // (p-s) < (lim-1), getchar() executed
{
*p++ = c; // '\n'
*p = '\0'; // the null character ends a string
return p-s;
}
*p = c;
} // here c != EOF && c != '\n'
*p = '\0'; // max(p-s) == (lim-1)
return p-s;
}
/*
gcc findp.c -o findp
./findp
Usage: ./findp -x -n pattern
./findp "ould " "Khayam.txt"
Usage: ./findp -x -n pattern
./findp "ould " < Khayam.txt
Ah Love! could you and I with Fate conspire
Would not we shatter it to bits -- and then
Re-mould it nearer to the Heart's Desire!
./findp -n "ould " < Khayam.txt
1: Ah Love! could you and I with Fate conspire
3: Would not we shatter it to bits -- and then
4: Re-mould it nearer to the Heart's Desire!
./findp -xn "ould " < Khayam.txt
2: To grasp this sorry Scheme of Things entire,
./findp -x -n "ould " < Khayam.txt
2: To grasp this sorry Scheme of Things entire,
./findp -x "ould " < Khayam.txt
To grasp this sorry Scheme of Things entire,
./findp -m -x "ould " < Khayam.txt
Illegal option: 'm'
Usage: ./findp -x -n pattern
*/
*****************************************************************************************
*****************************************************************************************
*****************************************************************************************
*****************************************************************************************
*****************************************************************************************
Exercise 5-10. Write the program expr, which evaluates a reverse Polish expression from the command line, where each operator or operand is a separate argument. For example,
expr 2 3 4 + *
evaluates to 2 * (3 + 4).
#include <stdio.h> // for printf()
#include <stdlib.h> // for atof()
#define MAXOP 100 // max size of operand or operator
#define NUMBER '0' // signal that a number has been found
int getop(char *); // get the next operand or operator
void push(double); // push operand on stack
double pop(void); // pop operand from stack
// evaluate a reverse Polish expression from the command line
int main(int argc, char *argv[])
{
if (argc < 2)
{
printf("Usage: ./expr [operand]+ [operand|operator]*\n");
return 1; // exit main() with an error code
}
int type, i1, i2;
double op2; // second operand for -, /, %
while (--argc > 0) // read all command-line arguments except program name
{
type = getop(*++argv); // initially, ++argv goes beyond program name
switch(type)
{
case NUMBER :
push(atof(*argv));
break;
case '+' :
push(pop() + pop());
break;
case '-' :
op2 = pop();
push(pop() - op2);
break;
case '*' :
push(pop() * pop());
break;
case '/' :
op2 = pop();
if (op2 != 0.0)
{push(pop() / op2);}
else // continuing operations would give wrong results
{
printf("Error: zero divisor\n");
return 1; // exit main() with an error code
}
break;
case '%' :
i2 = pop(); // automatic conversions
i1 = pop(); // from double to int
if (i2 != 0.0)
{push(i1 % i2);} // automatic conversion from int to double, argument of push()
else // continuing operations would give wrong results
{
printf("Error: zero divisor\n");
return 1; // exit main() with an error code
}
break;
default : // continuing operations would give wrong results
printf("Unknown command: %s\n", *argv);
return 1; // exit main() with an error code
}
}
printf("\t%.8g\n", pop()); // top of stack
return 0;
}
#define MAXVAL 100 // stack size
double val[MAXVAL]; // value stack
double *sp = val; // stack pointer (*val is top of stack)
void push(double f) // push f on (top of) stack
{
if (sp < val+MAXVAL) {*sp++ = f;}
else {printf("Error: stack full, can't push %g\n", f);}
}
double pop(void) // pop and return top value from stack
{
if (sp > val) {return *--sp;}
else
{
printf("Error: stack empty\n");
return 0.0;
}
}
#include <ctype.h> // for isdigit()
int getop(char *s) // get next operator or numeric operand
{
int c;
while ((c = *s++) == ' ' || c == '\t') // skip beginning whitespace (' ', '\t')
{} // last value read in c is not ' ' or '\t'
if (!isdigit(c) && c != '.' && c != '+' && c != '-')
{ // not a number, probably an operator
while (*s != '\0')
{
if (*s != ' ' && *s != '\t') // "*1" is not a legal argument
{
return 'a'; // illegal command
}
s++;
}
return c; // " * " is a legal argument, operator '*' (multiplication)
}
// here c is digit or '.' or '+' or '-'
if (c == '+' || c == '-') // unary or binary sign
{
if (!isdigit(*s) && *s != '.')
{
while (*s != '\0')
{
if (*s != ' ' && *s != '\t') // "+*" is not a legal argument
{
return 'a'; // illegal command
}
s++;
}
return c; // binary '+' or '-'
}
else // *s is digit or '.'
{
return NUMBER; // let atof() deal with other error cases
}
}
// here c is digit or '.'
return NUMBER; // let atof() deal with other error cases
}
/*
gcc expr.c -o expr
./expr
Usage: ./expr [operand]+ [operand|operator]*
./expr +
Error: stack empty
Error: stack empty
0 // value returned by pop() for an empty stack
./expr 1
1 // pop()
./expr 1 2
2 // pop() returns top of stack
./expr 1 2 +
3
./expr " 1 " " 2 " " + "
3
./expr 1 0 /
Error: zero divisor
./expr 1 0 %
Error: zero divisor
./expr a
Unknown command: a
./expr +*
Unknown command: +*
./expr +1 -2 +
-1
./expr -1.2 +.3 "*"
-0.36
./expr 4 3 /
1.3333333
./expr 4 3 %
1
./expr 4 3 % 2 -
-1
./expr +1*2
1 // atof(+1*2) = 1
./expr +1 2 "*"
2
./expr +1 2 '*'
2
*/
./expr 1 2 * interprets `*' as `all files' in the current folder, so we write
./expr 1 2 "*" or ./expr 1 2 '*' instead.
*****************************************************************************************
*****************************************************************************************
*****************************************************************************************
*****************************************************************************************
*****************************************************************************************
1234567 123456 12345 1234 123 12 1 0
a ab abc abcd abcde abcdef abcdefg h
#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
*/
*****************************************************************************************
*****************************************************************************************
*****************************************************************************************
*****************************************************************************************
*****************************************************************************************
1234567 123456 12345 1234 123 12 1 0
a ab abc abcd abcde abcdef abcdefg h
#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
*/
*****************************************************************************************
*****************************************************************************************
*****************************************************************************************
*****************************************************************************************
*****************************************************************************************
Exercise 5-12. Extend entab and detab to accept the shorthand
entab -m +n
to mean tab stops every n columns, starting at column m. Choose convenient
(for the user) default behavior.
1234567 123456 12345 1234 123 12 1 0
a ab abc abcd abcde abcdef abcdefg h
#include <stdio.h> // for getchar(), putchar(), printf(), EOF
#include <stdlib.h> // for atoi()
#define TAB 4 // 4 spaces
// For every chunk of TAB chars,
// detabn("abc\t") == "abc " // 1 space
// detabn("ab\t") == "ab " // 2 spaces
// detabn("a\t") == "a " // 3 spaces
// detabn("\t") == " " // 4 spaces
// we consider first column to be 1, just like first line is 1, not 0
void detabn(int col, int tab); // turn tabs into spaces
// use TAB before reaching column col, then use tab
int main(int argc, char *argv[])
{
if (argc != 3 || *argv[1] != '-' || *argv[2] != '+')
{
printf("Usage: ./detabn -m +n\n");
printf("m = column number, n = tab size\n");
return 1; // end program, signalling error
}
argv[1]++, argv[2]++; // move past '-' or '+'
int col = atoi(argv[1]);
int tab = atoi(argv[2]);
if (col <= 0 || tab <= 0)
{
printf("Usage: ./detabn -m +n\n");
printf("m = column number > 0, n = tab size > 0\n");
return 1; // end program, signalling error
}
detabn(col, tab);
return 0;
}
// use TAB before reaching column col, then use tab
void detabn(int col, int tab) // turn tabs into spaces
{ // col > 0, tab > 0
int c, i;
int chunk = 0; // count up to tab chars (next tab stop)
int count = 1; // count up to col-1 chars, start at column 1
// if col == 1, detabn(1, tab) is detab(tab)
int tabsize = TAB; // default value until count >= col
// starting at column col, tabsize = tab
if (count >= col) // if (count == col)
{
tabsize = tab;
// chunk = 0; // restart counting
}
while((c = getchar()) != EOF)
{
if (c == '\n')
{
chunk = 0; // reset, go to next line
count = 1; // reset to column 1
tabsize = TAB; // reset to default
if (count >= col) // if (count == col)
{
tabsize = tab;
// chunk = 0; // restart counting
}
putchar(c); // putchar('\n');
}
else if (c == '\t') // advance to next tab stop
{
for (i = 0; i < tabsize-chunk; i++)
{putchar(' ');} // add spaces up to next tab stop
if (count < col)
{
count += tabsize-chunk; // count added spaces
if (count >= col) // if (count == col)
{
tabsize = tab;
// chunk = 0; // restart counting
}
}
chunk = 0; // reset, go to next chunk of tabsize chars
}
else
{
putchar(c);
chunk++;
if (chunk >= tabsize) // if (chunk == tabsize)
{chunk = 0;} // reset, go to next chunk of tabsize chars
if (count < col)
{
count++;
if (count >= col) // if (count == col)
{
tabsize = tab;
chunk = 0; // restart counting
}
}
}
}
}
/*
gcc detabn.c -o detabn
./detabn
Usage: ./detabn -m +n
m = column number, n = tab size
./detabn 1 8
Usage: ./detabn -m +n
m = column number, n = tab size
./detabn -i +8
Usage: ./detabn -m +n
m = column number > 0, n = tab size > 0
./detabn -1 +-8
Usage: ./detabn -m +n
m = column number > 0, n = tab size > 0
./detabn -1 +8 // input from the 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)
./detabn -1 +8 < tabs.txt
1234567 123456 12345 1234 123 12 1 0
a ab abc abcd abcde abcdef abcdefg h
./detabn -1 +8 < tabs.txt > spaces.txt
./detabn -5 +4 < tabs.txt
1234567 123456 12345 1234 123 12 1 0
a ab abc abcd abcde abcdef abcdefg h
./detabn -5 +7 < detabn.c // input from source file
./detabn -3 +4 < detabn // input from binary file
*/
*****************************************************************************************
*****************************************************************************************
*****************************************************************************************
*****************************************************************************************
*****************************************************************************************
1234567 123456 12345 1234 123 12 1 0
a ab abc abcd abcde abcdef abcdefg h
#include <stdio.h> // for getchar(), putchar(), printf(), EOF
#include <stdlib.h> // for atoi()
#define TAB 4 // 4 spaces
// For every chunk of TAB chars,
// trailing spaces [+'\t'] become '\t',
// [trailing spaces +] '\t' become '\t':
// entabn(" ") == "\t" // 4 spaces
// entabn(" \t") == "\t" // 3 spaces
// entabn(" \t") == "\t" // 2 spaces
// entabn(" \t") == "\t" // 1 space
// entabn("a ") == "a\t" // 3 spaces
// entabn("a \t") == "a\t" // 2 spaces
// entabn("ab ") == "ab\t" // 2 spaces
// entabn("abc ") == "abc\t" // 1 space
// entabn("\t") == "\t"
// entabn("a\t") == "a\t"
// entabn("*a\t") == "*a\t" // '*' is not '\t',
// entabn("**a\t") == "**a\t" // but can be ' '
// we consider first column to be 1, just like first line is 1, not 0
void entabn(int col, int tab); // turn spaces into tabs
// use TAB before reaching column col, then use tab
int main(int argc, char *argv[])
{
if (argc != 3 || *argv[1] != '-' || *argv[2] != '+')
{
printf("Usage: ./entabn -m +n\n");
printf("m = column number, n = tab size\n");
return 1; // end program, signalling error
}
argv[1]++, argv[2]++; // move past '-' or '+'
int col = atoi(argv[1]);
int tab = atoi(argv[2]);
if (col <= 0 || tab <= 0)
{
printf("Usage: ./entabn -m +n\n");
printf("m = column number > 0, n = tab size > 0\n");
return 1; // end program, signalling error
}
entabn(col, tab);
return 0;
}
// use TAB before reaching column col, then use tab
void entabn(int col, int tab) // turn spaces into tabs
{ // col > 0, tab > 0
int c, i;
int chunk = 0; // count up to tab chars (next tab stop)
int count = 1; // count up to col-1 chars, start at column 1
// if col == 1, entabn(1, tab) is entab(tab)
int spaces = 0; // consecutive trailing spaces until '\t' or tab stop
// (chunk of tab chars), spaces <= chunk
int tabsize = TAB; // default value until count >= col
// starting at column col, tabsize = tab
if (count >= col) // if (count == col)
{
tabsize = tab;
// chunk = 0; // restart counting
}
while((c = getchar()) != EOF)
{
if (c == '\n') // chunk < tabsize
{
while (spaces > 0) // add trailing spaces at the end of line
{
putchar(' ');
spaces--;
} // here spaces == 0
chunk = 0; // reset, go to next line
count = 1; // reset to column 1
tabsize = TAB; // reset to default
if (count >= col) // if (count == col)
{
tabsize = tab;
// chunk = 0; // restart counting
}
putchar(c); // putchar('\n');
}
else if (c == '\t') // advance to next tab stop, chunk < tabsize
{ // replace ([trailing spaces +] '\t') with '\t':
spaces = chunk = 0; // reset, go to next chunk of tab chars
putchar(c); // putchar('\t');
if (count < col)
{ // count columns in the original file, so we don't subtract removed spaces
count++; // for the added '\t'
// we don't subtract spaces from count
if (count >= col) // if (count == col)
{
tabsize = tab;
// chunk = 0; // restart counting
}
}
}
else if (c == ' ')
{
spaces++, chunk++;
if (chunk >= tabsize) // if (chunk == tabsize)
{ // replace trailing spaces with '\t'
putchar('\t');
spaces = chunk = 0; // reset, go to next chunk of tab chars
} // else continue;
if (count < col)
{ // count columns in the original file, so we don't subtract removed spaces
count++; // for the added ' '
// we don't subtract spaces from count
if (count >= col) // if (count == col)
{
tabsize = tab;
chunk = 0; // restart counting
}
}
}
else
{
while (spaces > 0) // add trailing spaces before char
{
putchar(' ');
spaces--;
} // here spaces == 0
putchar(c);
chunk++;
if (chunk >= tabsize) // if (chunk == tabsize)
{chunk = 0;} // reset, go to next chunk of tab chars
if (count < col)
{
count++; // for the added char
if (count >= col) // if (count == col)
{
tabsize = tab;
chunk = 0; // restart counting
}
}
}
}
}
/*
gcc entabn.c -o entabn
./entabn
Usage: ./entabn -m +n
m = column number, n = tab size
./entabn 1 8
Usage: ./entabn -m +n
m = column number, n = tab size
./entabn -i +8
Usage: ./entabn -m +n
m = column number > 0, n = tab size > 0
./entabn -1 +-8
Usage: ./entabn -m +n
m = column number > 0, n = tab size > 0
./entabn -1 +8 // input from the keyboard
1234567 123456 12345 1234 123 12 1 0 // Enter (spaces)
1234567 123456 12345 1234 123 12 1 0 // tabs (tab = 8)
// Ctrl^D or Ctrl^C in Linux, Ctrl^Z + Enter in Windows (EOF)
./entabn -1 +8 < spaces.txt
1234567 123456 12345 1234 123 12 1 0 // tab = 8
a ab abc abcd abcde abcdef abcdefg h
./entabn -1 +8 < spaces.txt > tabs.txt
./entabn -5 +4 < spaces.txt > output.txt
./entabn -5 +7 < entabn.c // input from source file
./entabn -3 +4 < entabn // input from binary file
*/
*****************************************************************************************
*****************************************************************************************
*****************************************************************************************
1234567 123456 12345 1234 123 12 1 0
a ab abc abcd abcde abcdef abcdefg h
On the second line, a is followed by 7 spaces. After reading a and the first 3 spaces, chunk==TAB==4, so we replace the 3 trailing spaces with '\t'. Then tabsize=4, chunk=0 (count==col). After reading the next 4 spaces, upon reaching a from ab, chunk==tabsize, so we replace the 4 read spaces by '\t'. As such, there are two tabs between a and ab.
*****************************************************************************************
*****************************************************************************************
*****************************************************************************************
*****************************************************************************************
*****************************************************************************************
Exercise 5-13. Write the program tail, which prints the last n lines of its input. By default, n is 10, let us say, but it can be changed by an optional argument, so that
tail -n
prints the last n lines. The program should behave rationally no matter how unreasonable the input or the value of n. Write the program so that it makes the best use of available storage; lines should be stored as in the sorting program of Section 5.6, not in a two-dimensional array of fixed size.
#include <stdio.h> // for getchar(), putchar(), printf(), EOF
#include <stdlib.h> // for atoi()
#include <string.h> // for strcpy()
#define MAXLINES 20000 // max no of lines to be sorted
#define MAXLEN 100 // max line length
int readlines (char *lineptr[], int nlines);
void writelines (char *lineptr[], int lines); // write last lines
int main(int argc, char *argv[])
{
if (argc > 2)
{
printf("Usage ./tail [-n]\n");
printf("n > 0 no of lines to display\n");
return 1; // exit program, signalling error
}
int lines = 10; // no of lines to display
if (argc == 2)
{
if (*argv[1] != '-')
{
printf("Usage ./tail [-n]\n");
printf("n > 0 no of lines to display\n");
return 1; // exit program, signalling error
}
argv[1]++; // move past '-'
lines = atoi(argv[1]);
if (lines <= 0)
{
printf("Usage ./tail [-n]\n");
printf("n > 0 no of lines to display\n");
return 1; // exit program, signalling error
}
}
char *lineptr[MAXLINES];
int nlines; // no of input lines read
if ((nlines = readlines(lineptr, MAXLINES)) >= 0)
{
if (lines > nlines)
{
lines = nlines;
}
writelines(lineptr+nlines, lines); // lineptr+nlines points after the last element
}
else
{
printf("Error: input too big to sort\n");
return 1; // exit program, signalling error
}
return 0;
}
int getLine(char *, int); // getline() is declared in stdio.h
char* alloc(int); // allocate storage
int readlines (char *lineptr[], int maxlines) // read input lines
{
int len, nlines;
char *p, line[MAXLEN];
nlines = 0;
while ((len = getLine(line, MAXLEN)) > 0)
{
if(nlines >= maxlines || (p = alloc(len+1)) == NULL)
{return -1;} // len+1 for ending '\0'
else
{
strcpy(p, line);
lineptr[nlines++] = p;
}
}
return nlines;
}
int getLine(char *s, int lim)
{
int c = EOF; // initialize
char *p;
// getchar() is only executed if ((p-s) < (lim-1)):
for (p = s; (p-s) < (lim-1) && (c = getchar()) != EOF && c != '\n'; p++)
{ // from 0 to lim-2; s[lim-2]='\n', s[lim-1]='\0'
*p = c;
}
if (c == '\n')
{*p++ = c;}
*p = '\0'; // the null character ends a string
return p-s; // max(p-s) == (lim-1)
}
#define ALLOCSIZE (MAXLINES * MAXLEN)
static char allocbuf[ALLOCSIZE]; // storage for alloc()
static char *allocp = allocbuf; // next free position
char* alloc(int n) // return pointer to n chars
{
if (allocbuf + ALLOCSIZE - allocp >= n) // it fits
{
allocp += n;
return allocp-n; // old pointer
}
else {return 0;} // null pointer
}
void afree(char *p) // free storage pointed to by p
{
if (p >= allocbuf && p < allocbuf + ALLOCSIZE)
{allocp = p;}
}
void writelines (char *lineptr[], int lines) // write output lines
{
lineptr -= lines;
while (lines-- > 0)
{printf("%s", *lineptr++);}
}
/*
gcc tail.c -o tail
./tail 10
Usage ./tail [-n]
n > 0 no of lines to display
./tail -0
Usage ./tail [-n]
n > 0 no of lines to display
./tail --1
Usage ./tail [-n]
n > 0 no of lines to display
./tail < me.txt // prints last 10 lines
./tail -10 < me.txt // prints last 10 lines
./tail -100 < me.txt // prints last 100 lines
*/
Redefine MAXLINES and MAXLEN if you use other input file.
getLine() should return after reading '\n' or EOF, otherwise it may need ungetch(), see Section ch4-Calculator in Chapter_4.