Merge pull request #186 from xexyl/1992-adrian
Make 1992/adrian use fgets() not gets()
This commit is contained in:
2 files changed
+150
-123
No files matched your search
+149
-121
@@ -11,6 +11,20 @@
|
|||||||
make all
|
make all
|
||||||
|
|
||||||
|
|
||||||
|
[Cody Boone Ferguson](/winners.html#Cody_Boone_Ferguson) changed the location
|
||||||
|
that it used `gets()` to be `fgets()` instead to make it safer. One might think
|
||||||
|
that simply changing the gets() to fgets() (with stdin) would work but it did
|
||||||
|
not because `fgets()` stores the newline and `gets()` does not. The code was
|
||||||
|
relying on not having this newline. With `fgets()` the code `if(A(Y)) puts(Y);`
|
||||||
|
ended up printing an extra line which made the generation of some files (like
|
||||||
|
`adhead.c`) fail to compile (`gets()` does not store the newline but `fgets()`
|
||||||
|
does). Why? There was a blank line after a `\` at the end of the first line of a
|
||||||
|
macro definition! Thus the code now first trims off the last character of the
|
||||||
|
buffer read to get the same correct functionality but in a safe way. Thank you
|
||||||
|
Cody for your assistance!
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Try:
|
## Try:
|
||||||
|
|
||||||
./adrian adrian.grep.try < README.md
|
./adrian adrian.grep.try < README.md
|
||||||
@@ -19,56 +33,56 @@ For the slow minded, try:
|
|||||||
|
|
||||||
./adsleep 32767
|
./adsleep 32767
|
||||||
|
|
||||||
## Judges' comments
|
## Judges' comments:
|
||||||
|
|
||||||
Once you get past the obfuscation, you have an opportunity to learn
|
Once you get past the obfuscation, you have an opportunity to learn
|
||||||
about regular expressions and state machines.
|
about regular expressions and state machines.
|
||||||
|
|
||||||
NOTE: Some compilers have had trouble optimizing this entry.
|
NOTE: Some compilers have had trouble optimizing this entry.
|
||||||
|
|
||||||
## Author's comments
|
## Author's comments:
|
||||||
|
|
||||||
ADrian's GREP (adgrep)
|
ADrian's GREP (adgrep)
|
||||||
|
|
||||||
For those confused by the complexity of full-blown egrep style regular
|
For those confused by the complexity of full-blown egrep style regular
|
||||||
expressions, this program offers an alternative. It implements an
|
expressions, this program offers an alternative. It implements an
|
||||||
equivalent search, using a deterministic finite automaton.
|
equivalent search, using a deterministic finite automaton.
|
||||||
|
|
||||||
A deterministic finite automaton consists of a finite set of states,
|
A deterministic finite automaton consists of a finite set of states,
|
||||||
along with transition rules to move from one state to another, an initial
|
along with transition rules to move from one state to another, an initial
|
||||||
state, and a set of accepting states. The automaton takes a string as
|
state, and a set of accepting states. The automaton takes a string as
|
||||||
input and begins in the start state. It reads a character of the string
|
input and begins in the start state. It reads a character of the string
|
||||||
and consults the rules for the current state, moving to the new state
|
and consults the rules for the current state, moving to the new state
|
||||||
indicated by the appropriate rule. This process is repeated until the
|
indicated by the appropriate rule. This process is repeated until the
|
||||||
string is consumed. If the current state at this point is one of the
|
string is consumed. If the current state at this point is one of the
|
||||||
accepting states, then the string is accepted.
|
accepting states, then the string is accepted.
|
||||||
|
|
||||||
The deterministic finite automaton is specified as a series of rules for
|
The deterministic finite automaton is specified as a series of rules for
|
||||||
each state:
|
each state:
|
||||||
|
|
||||||
<state> chars1 <dest1> chars2 <dest2> ...
|
<state> chars1 <dest1> chars2 <dest2> ...
|
||||||
|
|
||||||
chars1 is a list of characters (only the first 8 are significant) which
|
`chars1` is a list of characters (only the first 8 are significant) which
|
||||||
should trigger a transition to <dest1>. <dest1> is another state which
|
should trigger a transition to `<dest1>`. `<dest1>` is another state which
|
||||||
should have a similar specification somewhere. A state is accepting if
|
should have a similar specification somewhere. A state is accepting if
|
||||||
it is specified in square brackets: [final], and state strings are
|
it is specified in square brackets: `[final]`, and state strings are
|
||||||
significant to only eight characters.
|
significant to only eight characters.
|
||||||
|
|
||||||
Example 1: matches ^abc$
|
### Example 1: matches `^abc$`
|
||||||
|
|
||||||
<q0> a <q1> The first state to appear is the start state
|
<q0> a <q1> The first state to appear is the start state
|
||||||
<q1> b <q2>
|
<q1> b <q2>
|
||||||
<q2> c [q3]
|
<q2> c [q3]
|
||||||
[q3]
|
[q3]
|
||||||
|
|
||||||
Technically, a deterministic finite automaton should have a rule for each
|
Technically, a deterministic finite automaton should have a rule for each
|
||||||
possible input character at each state. To simplify descriptions of the
|
possible input character at each state. To simplify descriptions of the
|
||||||
automata, if no rule is present, the string will not be accepted. Also,
|
automata, if no rule is present, the string will not be accepted. Also,
|
||||||
the '.' character matches any character if it occurs first in the
|
the `'.'` character matches any character if it occurs first in the
|
||||||
character list.
|
character list.
|
||||||
|
|
||||||
|
|
||||||
Example 2: ^abc
|
### Example 2: `^abc`
|
||||||
|
|
||||||
<q0> a <q1>
|
<q0> a <q1>
|
||||||
<q1> b <q2>
|
<q1> b <q2>
|
||||||
@@ -76,7 +90,7 @@ For the slow minded, try:
|
|||||||
[q3] . [q3]
|
[q3] . [q3]
|
||||||
|
|
||||||
|
|
||||||
Example 3: abc$
|
### Example 3: `abc$`
|
||||||
|
|
||||||
<q0> a <q1> . <q0>
|
<q0> a <q1> . <q0>
|
||||||
<q1> b <q2> a <q1> . <q0>
|
<q1> b <q2> a <q1> . <q0>
|
||||||
@@ -84,14 +98,14 @@ For the slow minded, try:
|
|||||||
[q3] . <q1>
|
[q3] . <q1>
|
||||||
|
|
||||||
|
|
||||||
Example 4: ^(abc)*$
|
### Example 4: `^(abc)*$`
|
||||||
|
|
||||||
[q0] a <q1>
|
[q0] a <q1>
|
||||||
<q1> b <q2>
|
<q1> b <q2>
|
||||||
<q2> c [q0]
|
<q2> c [q0]
|
||||||
|
|
||||||
|
|
||||||
Example 5: ^[ab][cd][ef]$
|
### Example 5: `^[ab][cd][ef]$`
|
||||||
|
|
||||||
<q0> ab <q1>
|
<q0> ab <q1>
|
||||||
<q1> cd <q2>
|
<q1> cd <q2>
|
||||||
@@ -99,7 +113,7 @@ For the slow minded, try:
|
|||||||
[q3]
|
[q3]
|
||||||
|
|
||||||
|
|
||||||
Example 6: ^(abc|efg)$
|
### Example 6: `^(abc|efg)$`
|
||||||
|
|
||||||
<q0> a <q1> e <q3>
|
<q0> a <q1> e <q3>
|
||||||
<q1> b <q2>
|
<q1> b <q2>
|
||||||
@@ -109,118 +123,132 @@ For the slow minded, try:
|
|||||||
[q5]
|
[q5]
|
||||||
|
|
||||||
|
|
||||||
With the automaton specification in 'filename', invoke the program by
|
With the automaton specification in 'filename', invoke the program by
|
||||||
typing
|
typing
|
||||||
|
|
||||||
adgrep 'filename'
|
```sh
|
||||||
|
./adgrep 'filename'
|
||||||
It will read stdin and print out all the lines which the automaton
|
```
|
||||||
accepts. If the file cannot be opened, a system error message will
|
|
||||||
be printed. If the input contains errors, then an error message along
|
|
||||||
with the number of the offending line will be printed to stderr. The
|
|
||||||
number of rules for each state is limited to 17. If more than 17 rules
|
|
||||||
are present, you get the error to_many_rules, and the state that was
|
|
||||||
being processed is printed. Error no_destination occurs if you specify a
|
|
||||||
set of characters, but no destination state, and error too_many_states
|
|
||||||
occurs if your automaton has more than 257 states.
|
|
||||||
|
|
||||||
Running
|
|
||||||
adgrep from < your_mailbox
|
|
||||||
|
|
||||||
will perform a function similar to that of the unix from command.
|
|
||||||
|
|
||||||
If no filename is specified on the command line, then "adgrep.c" is used
|
|
||||||
as the specification for the automaton. (This file has been renamed
|
|
||||||
to adrian.c by the judges.) In this case, the program will search for
|
|
||||||
matches to the regular expression:
|
|
||||||
|
|
||||||
^.[^|C][^w[Q]*(Q|[w[]c).*|^.[C|]$
|
|
||||||
|
|
||||||
I suggest using adgrep.c as input, and storing the output in adwc.c:
|
|
||||||
|
|
||||||
adgrep < adgrep.c > adwc.c
|
|
||||||
|
|
||||||
Compiling the new file, mywc.c, yields a clone of the unix wc command. It
|
|
||||||
runs on one file (defaulting to "adgrep.c" if no file is given) and
|
|
||||||
displays the number of lines, words, and bytes in the input file.
|
|
||||||
|
|
||||||
|
|
||||||
Another possibly interesting automaton can be created by slightly
|
It will read stdin and print out all the lines which the automaton
|
||||||
adjusting the adgrep.c file. Change the first line to read
|
accepts. If the file cannot be opened, a system error message will
|
||||||
|
be printed. If the input contains errors, then an error message along
|
||||||
|
with the number of the offending line will be printed to stderr. The
|
||||||
|
number of rules for each state is limited to 17. If more than 17 rules
|
||||||
|
are present, you get the error `too_many_rules`, and the state that was
|
||||||
|
being processed is printed. Error `no_destination` occurs if you specify a
|
||||||
|
set of characters, but no destination state, and error `too_many_states`
|
||||||
|
occurs if your automaton has more than 257 states.
|
||||||
|
|
||||||
/* . echo| . */
|
Running:
|
||||||
|
|
||||||
and repeat the process above
|
```sh
|
||||||
|
./adgrep from < your_mailbox
|
||||||
|
```
|
||||||
|
|
||||||
adgrep <adgrep.c > adecho.c
|
will perform a function similar to that of the unix from command.
|
||||||
|
|
||||||
The new file now contains all lines which match
|
If no filename is specified on the command line, then `"adgrep.c"` is used
|
||||||
|
as the specification for the automaton. (This file has been renamed
|
||||||
^.[^5|m^]*[m^]([e=p,;]|[^e=+p,;].*)$
|
to `adrian.c` by the judges.) In this case, the program will search for
|
||||||
|
matches to the regular expression:
|
||||||
Compile and run. This is an echo clone. Note the efficient algorithm
|
|
||||||
employed.
|
|
||||||
|
|
||||||
|
|
||||||
Two other adjustments to the first line also yield useful results. By
|
^.[^|C][^w[Q]*(Q|[w[]c).*|^.[C|]$
|
||||||
changing it to
|
|
||||||
/* . head; . */
|
|
||||||
|
|
||||||
you can search for matches to
|
I suggest using `adgrep.c` as input, and storing the output in `adwc.c`:
|
||||||
|
|
||||||
^.[^W]*W..*$
|
```sh
|
||||||
|
./adgrep < adgrep.c > adwc.c
|
||||||
|
```
|
||||||
|
|
||||||
By some freak happenstance, lines of adgrep.c which match this regular
|
Compiling the new file, `mywc.c`, yields a clone of the unix wc command. It
|
||||||
expression form a unix head command. It prints the first ten lines of
|
runs on one file (defaulting to `"adgrep.c"` if no file is given) and
|
||||||
the file specified on the command line (or adgrep.c if no file is
|
displays the number of lines, words, and bytes in the input file.
|
||||||
specified).
|
|
||||||
|
|
||||||
By setting the first line to
|
|
||||||
|
|
||||||
/* . basename . */
|
Another possibly interesting automaton can be created by slightly
|
||||||
|
adjusting the `adgrep.c` file. Change the first line to read
|
||||||
|
|
||||||
a clone of the unix basename command can be unearthed. The automaton will
|
/* . echo| . */
|
||||||
search for
|
|
||||||
^.[^j]*jr.*$
|
|
||||||
|
|
||||||
on standard input. And the program which results by running adgrep.c
|
and repeat the process above
|
||||||
through this filter requires two parameters. The first is meant to be a
|
|
||||||
filename, and the second, an extension. All leading pathname components
|
|
||||||
are removed from the filename, and the extension is removed if present.
|
|
||||||
The resulting base name is printed to stdout.
|
|
||||||
|
|
||||||
Lastly, by setting the first line to
|
```sh
|
||||||
|
./adgrep <adgrep.c > adecho.c
|
||||||
|
```
|
||||||
|
|
||||||
/* . sleep . */
|
The new file now contains all lines which match
|
||||||
|
|
||||||
you can search for
|
^.[^5|m^]*[m^]([e=p,;]|[^e=+p,;].*)$
|
||||||
|
|
||||||
^.[^(~][^s]*sl.*$
|
Compile and run. This is an echo clone. Note the efficient algorithm
|
||||||
|
employed.
|
||||||
|
|
||||||
Filtering adgrep.c through this search yields a clone of the sleep
|
|
||||||
command. Invoke with a single integer parameter, and it will pause
|
|
||||||
for that many seconds.
|
|
||||||
|
|
||||||
If either adbasename or adsleep is invoked with too few parameters,
|
Two other adjustments to the first line also yield useful results. By
|
||||||
the program will print the error message:
|
changing it to
|
||||||
Segmentation fault (core dumped)
|
|
||||||
|
|
||||||
(The exact text of the above error messages varies from machine to
|
/* . head; . */
|
||||||
machine.) The four programs which read from stdin require lines
|
|
||||||
shorter than 999 characters.
|
|
||||||
|
|
||||||
The other info files are adrian.grep.[1-6] which contain the six
|
you can search for matches to
|
||||||
examples that appear above, and from, which is used to emulate the
|
|
||||||
unix from command. For reasons of clarity, the name "from" should
|
|
||||||
probably not be changed if possible. I wouldn't want to be accused of
|
|
||||||
confusing people by giving the input files weird names.
|
|
||||||
|
|
||||||
If you want to change the default input filename (line 80) you must be
|
^.[^W]*W..*$
|
||||||
careful to choose a name that doesn't match the wrong search patterns,
|
|
||||||
introducing extra lines into one of the programs.
|
|
||||||
|
|
||||||
The program will produce at least one warning and possible several
|
By some freak happenstance, lines of adgrep.c which match this regular
|
||||||
when compiled depending on the compiler.
|
expression form a unix head command. It prints the first ten lines of
|
||||||
|
the file specified on the command line (or adgrep.c if no file is
|
||||||
|
specified).
|
||||||
|
|
||||||
|
By setting the first line to
|
||||||
|
|
||||||
|
/* . basename . */
|
||||||
|
|
||||||
|
a clone of the unix basename command can be unearthed. The automaton will
|
||||||
|
search for
|
||||||
|
|
||||||
|
^.[^j]*jr.*$
|
||||||
|
|
||||||
|
on standard input. And the program which results by running adgrep.c
|
||||||
|
through this filter requires two parameters. The first is meant to be a
|
||||||
|
filename, and the second, an extension. All leading pathname components
|
||||||
|
are removed from the filename, and the extension is removed if present.
|
||||||
|
The resulting base name is printed to stdout.
|
||||||
|
|
||||||
|
Lastly, by setting the first line to
|
||||||
|
|
||||||
|
/* . sleep . */
|
||||||
|
|
||||||
|
you can search for
|
||||||
|
|
||||||
|
^.[^(~][^s]*sl.*$
|
||||||
|
|
||||||
|
Filtering adgrep.c through this search yields a clone of the sleep
|
||||||
|
command. Invoke with a single integer parameter, and it will pause
|
||||||
|
for that many seconds.
|
||||||
|
|
||||||
|
If either adbasename or adsleep is invoked with too few parameters,
|
||||||
|
the program will print the error message:
|
||||||
|
|
||||||
|
> Segmentation fault (core dumped)
|
||||||
|
|
||||||
|
(The exact text of the above error messages varies from machine to
|
||||||
|
machine.) The four programs which read from stdin require lines
|
||||||
|
shorter than 999 characters.
|
||||||
|
|
||||||
|
The other info files are adrian.grep.[1-6] which contain the six
|
||||||
|
examples that appear above, and from, which is used to emulate the
|
||||||
|
unix from command. For reasons of clarity, the name "from" should
|
||||||
|
probably not be changed if possible. I wouldn't want to be accused of
|
||||||
|
confusing people by giving the input files weird names.
|
||||||
|
|
||||||
|
If you want to change the default input filename (line 80) you must be
|
||||||
|
careful to choose a name that doesn't match the wrong search patterns,
|
||||||
|
introducing extra lines into one of the programs.
|
||||||
|
|
||||||
|
The program will produce at least one warning and possible several
|
||||||
|
when compiled depending on the compiler.
|
||||||
|
|
||||||
## Copyright and CC BY-SA 4.0 License:
|
## Copyright and CC BY-SA 4.0 License:
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
int W ,head;
|
int W ,head;
|
||||||
#define S(W,b,f) strncpy(W,b,f),W[f]=0\
|
#define S(W,b,f) strncpy(W,b,f),W[f]=0\
|
||||||
|
|
||||||
|
|
||||||
char *wcs=" \t\n";
|
char *wcs=" \t\n";
|
||||||
struct{ char X[z+1];
|
struct{ char X[z+1];
|
||||||
char f ;
|
char f ;
|
||||||
@@ -105,7 +104,7 @@ while( s = strtok(0,wcs)) {
|
|||||||
< 10 && printf((W,Y)); }
|
< 10 && printf((W,Y)); }
|
||||||
if(j+28) { {
|
if(j+28) { {
|
||||||
; } printf("%7u%7u%7u\n", wcl , wcw , wcc); }
|
; } printf("%7u%7u%7u\n", wcl , wcw , wcc); }
|
||||||
while( gets(Y) ) if(A(Y)) puts(Y);
|
while( fgets(Y,998,stdin) ){ Y[strlen(Y)-1]='\0'; if(A(Y)) puts(Y); }
|
||||||
W, jr; }
|
W, jr; }
|
||||||
|
|
||||||
O(int wc,char**V) {
|
O(int wc,char**V) {
|
||||||
|
|||||||
Reference in new issue
Block a user