#include <stdio.h> // for getchar(), putchar(), printf(), EOF
#define MAXLINE 1000 // maximum input line size
int max; // maximum length seen so far
char line[MAXLINE]; // current input line
char longest[MAXLINE]; // longest line saved here
int getLine(void); // getline() is declared in stdio.h
void copy(void);
// print longest input line; specialized version
int main()
{
int len;
// extern int max;
// extern char longest[];
max = 0;
while((len = getLine()) > 0)
{
if (len > max)
{
max = len;
copy();
}
}
if (max > 0) // there was a line
{
printf("%s", longest);
if(longest[max-1] != '\n')
{putchar('\n');} // longest may not end with '\n'
}
return 0;
}
// getLine(): specialized version
int getLine(void)
{
int c = EOF, i;
// extern char line[];
// getchar() is only executed if (i < (MAXLINE-1))
for (i = 0; i < (MAXLINE-1) && (c = getchar()) != EOF && c != '\n'; i++)
{ // from 0 to MAXLINE-2; line[MAXLINE-2]='\n' or not, line[MAXLINE-1]='\0'
line[i] = c;
}
if (c == '\n') // i < (MAXLINE-1), getchar() executed
{
line[i] = c;
i++;
}
line[i] = '\0';
return i;
}
// copy(): specialized version
void copy(void)
{
int i;
// extern char line[], longest[];
i = 0;
while((longest[i] = line[i]) != '\0')
{i++;} // ends after copying '\0'
// for (i = 0; (longest[i] = line[i]) != '\0'; i++) {}
}
/*
gcc extlong.c -o extlong
./extlong // input from the keyboard
The sky is gray
A storm is coming
Rain fills the rivers // Enter, then Ctrl+D in Linux, Ctrl+Z+Enter in Windows (EOF)
Rain fills the rivers
./extlong < extlong.c // source file
Rain fills the rivers // Enter, then Ctrl+D in Linux, Ctrl+Z+Enter in Windows (EOF)
./extlong < extlong // binary file
*/
*****************************************************************************************
*****************************************************************************************
*****************************************************************************************
*****************************************************************************************
*****************************************************************************************
Exercise 1-20. Write a program detab that replaces tabs in the input with the proper number of blanks to space to the next tab stop. Assume a fixed set of tab stops, say every n columns. Should n be a variable or a symbolic parameter?
#include <stdio.h> // for getchar(), putchar(), EOF
#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
int main()
{
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
}
}
return 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)
./detab < detab.c // input from source file
./detab < detab // input from binary file
*/
getline() would only work if it could retrieve arbitrarily long lines. Alternatively we could use a function like ungetch(), see Chapter_4, Section ch4-Calculator, to push the last read char back on input (when getline() stops before reaching newline).
*****************************************************************************************
*****************************************************************************************
*****************************************************************************************
*****************************************************************************************
*****************************************************************************************
Exercise 1-21. Write a program entab that replaces strings of blanks by the minimum number of tabs and blanks to achieve the same spacing. Use the same tab stops as for detab (see Exercise_1-20 ). When either a tab or a single blank would suffice to reach a tab stop, which should be given preference?
#include <stdio.h> // for getchar(), putchar(), EOF
#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 ' '
int main()
{
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
}
}
return 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)
./entab < entab.c // input from source file
./entab < entab // input from binary file
*/
We replace one trailing space with tab:
// entab("abc ") == "abc\t" // 1 space
because we may remove chars later and we may want to retain the same spacing. Thus, we may delete 'c' and remain with "ab\t" with the same spacing.
*****************************************************************************************
*****************************************************************************************
*****************************************************************************************
*****************************************************************************************
*****************************************************************************************
Exercise 1-22. Write a program to "fold" long input lines into two or more shorter lines after the last non-blank character that occurs before the n-th column of input. Make sure your program does something intelligent with very long lines, and if there are no blanks or tabs before the specified column.
#include <stdio.h> // for getchar(), printf(), EOF
#define FOLDLINE 80 // 80 characters
#define MAXWORD 10 // search for gap (whitesp. or punct.) before end of line
#define TRUE 1
#define FALSE 0
int insideWord = FALSE; // global variable, used to signal when a line
// does not end with whitespace or a punctuation mark or sign
int getLine(char line[], int maxline); // getline() is defined by stdio.h
// int getLine(char [], int); // alternative declaration
int main()
{
char c;
int i; // counter
int len; // current line length
char line[FOLDLINE+1]; // +1 for ending '\0'
char word[MAXWORD+1]; // when line doesn't end with whitesp. or punct. sign
int wordlen = 0; // length of word printed on the next line, after
// splitting line[] in two at a gap (whitesp. or punct.)
while((len = getLine(line, FOLDLINE+1-wordlen)) > 0) // outer while()
{ // line containing only '\n' not empty
if (insideWord) // insideWord == 1 (insideWord != 0)
{
wordlen = 0; // reset
while (wordlen < MAXWORD && insideWord) // inner while()
{
c = line[len-1];
if (c == ' ' || c == '\t' || c == ',' || c == ';' ||
c == '.' || c == ':' || c == '!' || c == '?')
{insideWord = FALSE;}
else {len--, wordlen++;}
}
if (insideWord) // could not find a gap to split the line[]
{
printf("%s\r", line); // fold line
wordlen = 0; // reset
}
else // found a gap, split line[]
{
for (i = 0; i < wordlen; i++)
{word[i] = line[len+i];}
word[wordlen] = '\0'; // properly end string
line[len] = '\0'; // reset line to shorter length
printf("%s\n", line); // fold line (split)
printf("%s", word); // rest of initial line
}
}
else // getLine() ended with EOF, '\n' or else
{ // line ended with whitespace or punctuation
printf("%s", line);
wordlen = 0; // reset
}
}
return 0;
}
// getLine(): read a line into s[], return length
int getLine(char s[], int lim)
{
int c = EOF; // initialize
int i;
// getchar() is only executed if (i < (lim-1)):
for (i = 0; i < (lim-1) && (c = getchar()) != EOF && c != '\n'; i++)
{ // from 0 to lim-2; s[lim-2]='\n' or not, s[lim-1]='\0'
s[i] = c;
}
if (i == lim-1) // c != EOF, c != '\n'
{
if (c != ' ' && c != '\t' && c != ',' && c != ';' &&
c != '.' && c != ':' && c != '!' && c != '?')
{insideWord = TRUE;} // line doesn't end with whitespace or punct. mark
else {insideWord = FALSE;} // reset
}
if (c == '\n') // i < (lim-1), getchar() executed
{
s[i] = c; // '\n'
i++;
}
s[i] = '\0'; // the null character ends a string
return i; // max(i) == (lim-1), assuming (lim-1) > 0
}
/*
gcc fold.c -o fold
./fold // input from the keyboard
./fold < fold.c // input from source file
./fold < fold.c > copy.c // input fold.c, output copy.c
meld fold.c copy.c
./fold < fold // input from binary file
rm copy.c // clean
*/
*****************************************************************************************
*****************************************************************************************
*****************************************************************************************
*****************************************************************************************
*****************************************************************************************
Exercise 1-23. Write a program to remove all comments from a C program. Don't forget to handle quoted strings and character constants properly. C comments do not nest.
#include <stdio.h> // for getchar(), putchar(), printf(), EOF
#define FALSE 0
#define TRUE 1
// testing a \
multiline \
C++ style \
comment
int main()
{
int c1, c2;
int insideComment = FALSE; // for C style comments
while ((c1 = getchar()) != EOF)
{
if (c1 == '\\') // escape character or sequence
{ /* \', \", \\ */ // \ continues a C++ style comment on the next line
c2 = getchar();
if (c2 == EOF)
{ // \ newline is part of a multiline preprocessor command or C++ comment
printf("\nEscape character not ended properly\n"); // Error message
return 1; // return from main(), end program with an error message
}
putchar(c1); putchar(c2);
}
else if (c1 == '\'') // character literal
{ // '/', '*', '"', '\"'
putchar(c1);
while ((c1 = getchar()) != '\'')
{
if (c1 == EOF || c1 == '\n')
{
printf("\nCharacter literal not ended properly\n"); // Error message
return 1; // end program with an error message
}
else if (c1 == '\\') // escape char or sequence
{ // '\\', '"', '\"'
putchar(c1);
c1 = getchar();
if (c1 == EOF)
{ // \ newline is part of a multiline preprocessor command or C++ comment
printf("\nEscape character not ended properly\n"); // Error message
return 1; // end program with an error message
}
else {putchar(c1);} // skip char
}
else {putchar(c1);}
} // here c1 == '\''
}
else if (c1 == '\"') // strings are on a single line
{ // "/*", "*/", "//", "'", "\"", "\'", "'", "\\"
putchar(c1);
while ((c1 = getchar()) != '\"')
{
if (c1 == EOF || c1 == '\n')
{
printf("\nQuoted string not ended properly\n"); // Error message
return 1; // end program with an error message
}
else if (c1 == '\\') // escape char or sequence
{
putchar(c1);
c1 = getchar();
if (c1 == EOF || c1 == '\n')
{
printf("\nEscape character not ended properly\n"); // Error message
return 1; // end program with an error message
}
else {putchar(c1);}
}
else {putchar(c1);}
} // here c1 == '\"'
}
if (c1 == '/')
{
c2 = /* testing */ getchar();
if (c2 == '/') // beginning of a C++ style comment
{
while ((c2 = getchar()) != EOF)
{ // \ continues a C++ style comment on the next line
if (c1 != '\\' && c2 == '\n')
{break;} // out of inner while()
c1 = c2; // c1 holds the penultimate char read
} // here c2 == '\n' or EOF, end of C++ style comment
if (c2 == EOF)
{return 0;} // end program normally, exit main()
putchar(c2); // putchar('\n'); // C++ style comment ends with '\n'
}
else if (c2 == '*') // beginning of a C style comment
{
insideComment = TRUE;
while (insideComment) // insideComment == 1 (insideComment != 0)
{
while ((c1 = getchar()) != '*')
{
if (c1 == EOF)
{
printf("\nC style comment not closed properly\n"); // Error message
return 1; // end program with an error message
}
// else skip chars till '*'
}
c2 = getchar();
if (c2 == EOF)
{
printf("\nC style comment not closed properly\n"); // Error message
return 1; // end program with an error message
}
else if (c2 == '/') // end of C style comment
{
insideComment = FALSE; // reset
break; // out of while(insideComment)
}
// else continue; // C style comment continues
} // end of while (insideComment)
}
else // no comment, false alarm
{
putchar(c1);
if (c2 != EOF) {putchar(c2);}
else {return 0;} // end program normally, exit main()
}
}
else {putchar(c1);} // no comment
} // end of outer while()
return 0;
}
/*
gcc nocomment.c -o nocomment
./nocomment < nocomment.c > copy1.c
gcc copy1.c -o copy1
./copy1 < nocomment.c > copy2.c
diff -s copy1.c copy2.c
// Files copy1.c and copy2.c are identical
meld copy1.c copy2.c
// Files are identical
rm copy1 copy1.c copy2.c // clean
*/
*****************************************************************************************
*****************************************************************************************
*****************************************************************************************
*****************************************************************************************
*****************************************************************************************
Write a program to check a C program for rudimentary syntax errors like unbalanced parentheses, brackets and braces. Don't forget about quotes, both single and double, escape sequences, and comments. (This program is hard if you do it in full generality.)
#include <stdio.h> // for getchar(), printf(), EOF
int main()
{
int c;
int leftPar = 0, rightPar = 0;
int leftBrackets = 0, rightBrackets = 0;
int leftBraces = 0, rightBraces = 0;
while ((c = getchar()) != EOF)
{
if (c == '\\') // escape character or sequence
{
c = getchar();
if (c == EOF || c == '\n')
{
printf("\nEscape character not ended properly\n"); // Error message
return 1; // return from main(), end program with an error message
}
}
else if (c == '\'') // character
{
while ((c = getchar()) != '\'')
{
if (c == EOF || c == '\n')
{
printf("\nEscape character not ended properly\n"); // Error message
return 1; // end program with an error message
}
else if (c == '\\') // escape char or sequence
{
c = getchar();
if (c == EOF || c == '\n')
{
printf("\nEscape character not ended properly\n"); // Error message
return 1; // end program with an error message
}
}
} // here c == '\''
}
else if (c == '\"') // strings are on a single line
{ // Skip strings: "/*" "*/" "//" "'" "\"\'"
while ((c = getchar()) != '\"')
{
if (c == EOF || c == '\n')
{
printf("\nQuoted string not ended properly\n"); // Error message
return 1; // end program with an error message
}
else if (c == '\\') // escape char or sequence
{
c = getchar();
if (c == EOF || c == '\n')
{
printf("\nEscape character not ended properly\n"); // Error message
return 1; // end program with an error message
}
}
} // here c == '\"'
}
else if (c == '(') {leftPar++;}
else if (c == ')') {rightPar++;}
else if (c == '[') {leftBrackets++;}
else if (c == ']') {rightBrackets++;}
else if (c == '{') {leftBraces++;}
else if (c == '}') {rightBraces++;}
}
if (leftPar != rightPar)
{printf("Mismatching parentheses\n");}
if (leftBrackets != rightBrackets)
{printf("Mismatching brackets\n");}
if (leftBraces != rightBraces)
{printf("Mismatching braces\n");}
return 0;
}
/*
gcc checker.c -o checker
./nocomment < checker.c > copy.c // remove comments
./checker < copy.c // check syntax
rm copy.c // clean
*/