1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
|
\input cwebxmac
\N0 1. Comparing text files.
This is an entirely trivial program, that tests whether two text files are
equal, and if not so, points out the first point of difference.
\Y\B\h$\.{<stdio.h>}$\par
\B\h$\.{<stdlib.h>}$\par
\Y\B$\&{typedef}~\&{char}~\&{bool};$\par
\fi
\M2. The outline of the program is simple. We read characters from both input
files into $ c_1$ and~$ c_2$ until the comparison is complete. Line and column
counts are maintained in $\\{line}$ and~$\\{col}$.
\Y\B\D$\\{left\_margin}$\5
$\T{1}$\C{ leftmost column number; change to 0 if you prefer }\par
\Y\B$\X5:Functions\X$\7
\&{int} $\\{main}$\5
$(\1\1\1\&{int}~ n,\31~\&{char}~\m*\m*\\{arg}\2\2\2)$\6
$\a\{\1\&{FILE}~\m* f_1,\31~\m* f_2;$\C{ the two input files }\6
$\&{int}~ c_1,\31~ c_2,\31~\\{col}\K\\{left\_margin};$\5
$\&{long}~\\{line}\K\T{1};$\7
$\X6:Open the files $ f_1$ and~$ f_2$, taking their names from the command line
or from the terminal; in case of an error for which no recovery is possible,
call $\\{exit}(\T{1})$\X$\6
$\X3:Search for first difference, leaving $ c_1\I c_2$ if and only if a
difference was found\X$\6
$\X4:Report the outcome of the comparison\X$\6
$\&{return}~\T{0};$\C{ successful completion }\2\6
$\}$\par
\fi
\M3. The heart of the program is this simple loop. When we reach the end of
one of the files, the files match if and only if the other file has also
reached its end. For this reason the test $ c_1\E c_2$, which requires
characters to be read from both files, must precede the test for file end;
when only one file ends, it is the former test which breaks the loop.
\Y\B\4$\X3:Search for first difference, leaving $ c_1\I c_2$ if and only if a
difference was found\X\EQ{}$\6
\&{while}~$(( c_1\K\\{getc}( f_1))\E( c_2\K\\{getc}( f_2))\W c_1\I\.{EOF})\1$\6
\&{if}~$( c_1\E\.{'\\n'})\1$\5
$\{\1$\5
$\PP\\{line};$\5
$\\{col}\K\\{left\_margin};\2$\5
$\}$\5
\2\&{else}\1\5
$\PP\\{col};\2$\2\par
\U 2.\fi
\M4. When the first difference occurs at the end of one of the files, or at the
end of a line, we give a message indicating this fact.
\Y\B\4$\X4:Report the outcome of the comparison\X\EQ{}$\6
\&{if}~$( c_1\E c_2)\1$\5
$\\{printf}(\.{"Files\ match.\\n"});\2$\6
\&{else}\6
$\a\{\1\\{printf}(\.{"Files\ differ.\\n"});$\6
\&{if}~$( c_1\E\.{EOF}\V c_2\E\.{EOF})$\1\6
$\{\1$\5
$\\{the\_file}( c_1\E\.{EOF});$\5
$\\{printf}(\.{"is\ contained\ in\ the\)\ other\ as\ initial\ seg\)ment.\\n"});%
\2$\5
$\}\2$\6
\&{else}~\&{if}~$( c_1\E\.{'\\n'}\V c_2\E\.{'\\n'})$\1\6
$\{\1$\5
$\\{the\_file}( c_1\E\.{'\\n'});$\5
$\\{printf}(\.{"has\ a\ shorter\ line\ \)number\ \%ld\ than\ the\ o\)ther.%
\\n"},\31\\{line});\2$\5
$\}\2$\6
\&{else}\1\5
$\\{printf}(\.{"First\ difference\ at\)\ line\ \%ld,\ column\ \%d.\)\\n"},\31%
\\{line},\31\\{col});\2$\2\6
$\}$\par
\U 2.\fi
\M5. The function $\\{the\_file}$ starts a sentence about the first or second
file,
depending on its boolean argument.
\Y\B\4$\X5:Functions\X\EQ{}$\6
\&{void} $\\{the\_file}$\5
$(\1\1\1\&{bool}~\\{is\_first}\2\2\2)$\5
$\{\1$\5
$\\{printf}(\.{"The\ \%s\ file\ "},\31\\{is\_first}\?\.{"first"} :%
\.{"second"});\2$\5
$\}$\par
\A 7.
\U 2.\fi
\M6. There can be be zero, one or two command line arguments. If there are
none,
the user is prompted to supply them, and if there are two these are taken as
the file names, prompting the user only in case a file could not be opened.
In case just one argument is present, the first file is assumed to be the
standard input, which does not have to be opened; in this case however we
will not read a file name from terminal in case the second file cannot be
opened.
\Y\B\D$\\{read\_mode}$\5
$\.{"r"}$\par
\Y\B\4$\X6:Open the files $ f_1$ and~$ f_2$, taking their names from the
command line or from the terminal; in case of an error for which no recovery is
possible, call $\\{exit}(\T{1})$\X\EQ{}$\6
$\MM n;$\5
$\PP\\{arg};$\C{ ignore ``argument'' 0, which is the program name }\6
\&{if}~$( n\E\T{0})$\1\6
$\{\1$\5
$\\{open\_file}(\m\AND f_1,\31\.{"First\ file\ to\ compa\)re"},\31\NULL);$\5
$\\{open\_file}(\m\AND f_2,\31\.{"Second\ file\ to\ comp\)are"},\31\NULL);\2$\5
$\}\2$\6
\&{else}~\&{if}~$( n\E\T{1})$\6
$\a\{\1 f_1\K\\{stdin};$\6
\&{if}~$(( f_2\K\\{fopen}(\m*\\{arg},\31\\{read\_mode}))\E\NULL)\1$\5
$\{\1$\5
$\\{printf}(\.{"Could\ not\ open\ file\)\ \%s.\\n"},\31\m*\\{arg});$\5
$\\{exit}(\T{1});\2$\5
$\}\2$\2\6
$\}$\6
\&{else}~\&{if}~$( n\E\T{2})$\6
$\a\{\1\\{open\_file}(\m\AND f_1,\31\.{"Give\ another\ first\ \)file"},\31\m*%
\\{arg}\PP);$\5
$\\{open\_file}(\m\AND f_2,\31\.{"Give\ another\ second\)\ file"},\31\m*%
\\{arg});\2$\6
$\}$\6
\&{else}\1\5
$\{\1$\5
$\\{printf}(\.{"No\ more\ than\ two\ co\)mmand\ line\ arguments\ \)are\
allowed.\\n"});$\5
$\\{exit}(\T{1});\2$\5
$\}\2$\par
\U 2.\fi
\M7. The function $\\{open\_file}$ will try to open the file $\\{name}$ for
reading, and
if this fails it will prompt for another file name until it has success. If
called with $\\{name}\E\NULL$, the function starts with prompting right away.
\Y\B\4$\X5:Functions\X\PE{}$\6
\&{void} $\\{open\_file}$\5
$(\1\1\1\&{FILE}~\m*\m* f,\31~\&{char}~\m*\\{prompt},\31~\&{char}~\m*\\{name}\2%
\2\2)$\6
$\a\{\1\&{char}~\\{buf}[\T{80}];$\7
\&{if}~$(\\{name}\E\NULL\V(\m* f\K\\{fopen}(\\{name},\31\\{read\_mode}))\E%
\NULL)\1$\6
\&{do}\1\5
$\{\1$\5
$\\{printf}(\.{"\%s:\ "},\31\\{prompt});$\5
$\\{fflush}(\\{stdout});$\5
$\\{scanf}(\.{"\%79s"},\31\\{buf});\2$\5
$\}$\2\6
\&{while}~$((\m* f\K\\{fopen}(\\{buf},\31\\{read\_mode}))\E\NULL);$\2\2\6
$\}$\par
\fi
\N0 8. Index.
\fi
\inx
\@m\\{arg}, \[2], 6.
\@h\&{bool}, \[1], 5.
\@m\\{buf}, \[7].
\@m\\{col}, \[2], 3, 4.
\@m c_1, \[2], 3, 4.
\@m c_2, \[2], 3, 4.
\@m\.{EOF}, 3, 4.
\@m\\{exit}, 6.
\@m f, \[7].
\@m\\{fflush}, 7.
\@h\&{FILE}, 2, 7.
\@m\\{fopen}, 6, 7.
\@m f_1, \[2], 3, 6.
\@m f_2, \[2], 3, 6.
\@m\\{getc}, 3.
\@m\\{is\_first}, \[5].
\@m\\{left\_margin}, \[2], 3.
\@m\\{line}, \[2], 3, 4.
\@m\\{main}, \[2].
\@m n, \[2].
\@m\\{name}, \[7].
\@m\\{open\_file}, 6, \[7].
\@m\\{printf}, 4, 5, 6, 7.
\@m\\{prompt}, \[7].
\@m\\{read\_mode}, \[6], 7.
\@m\\{scanf}, 7.
\@m\\{stdin}, 6.
\@m\\{stdout}, 7.
\@m\\{the\_file}, 4, \[5].
\fin
\@$\X5, 7:Functions\X$
\U 2.
\@$\X6:Open the files $ f_1$ and~$ f_2$, taking their names from the command
line or from the terminal; in case of an error for which no recovery is
possible, call $\\{exit}(\T{1})$\X$
\U 2.
\@$\X4:Report the outcome of the comparison\X$
\U 2.
\@$\X3:Search for first difference, leaving $ c_1\I c_2$ if and only if a
difference was found\X$
\U 2.
\con
|