Fixes and updates in 1998/dorssel
Format fixes in README.md. Rename dorssel.hint -> dorssel.md and update formatting. Updated Makefile DATA variable for rename of dorssel.hint.
This commit is contained in:
1 parent
87c12f9e93
commit
c0e237528b
3 files changed
+102
-65
No files matched your search
@@ -111,7 +111,7 @@ ENTRY= dorssel
|
||||
PROG= ${ENTRY}
|
||||
#
|
||||
OBJ= ${PROG}.o
|
||||
DATA= dorssel.hint
|
||||
DATA= dorssel.md
|
||||
TARGET= ${PROG}
|
||||
#
|
||||
ALT_OBJ=
|
||||
|
||||
+41
-30
@@ -1,29 +1,35 @@
|
||||
# Obsolescent Feature
|
||||
|
||||
Frans van Dorsselaer
|
||||
Bakker Industrial Automation
|
||||
Kaiserstraat 35
|
||||
NL-2311 GP Leiden
|
||||
The Netherlands
|
||||
Frans van Dorsselaer
|
||||
Bakker Industrial Automation
|
||||
Kaiserstraat 35
|
||||
NL-2311 GP Leiden
|
||||
The Netherlands
|
||||
|
||||
## To build:
|
||||
|
||||
make all
|
||||
```sh
|
||||
make all
|
||||
```
|
||||
|
||||
## To run:
|
||||
|
||||
./dorssel
|
||||
# input some text
|
||||
```sh
|
||||
./dorssel
|
||||
# input some text
|
||||
|
||||
echo text | ./dorssel
|
||||
echo text | ./dorssel
|
||||
```
|
||||
|
||||
## Try:
|
||||
|
||||
echo Simple as 123 | ./dorssel
|
||||
echo 'Binary as simple as 1, 10 11.' | ./dorssel
|
||||
echo '.... --- .-- -.. --- . ... .. - -.. --- - .... .- -' | ./dorssel
|
||||
echo 'HOW DOES IT DO THAT' | ./dorssel
|
||||
echo 'HOW DOES IT DO THAT' | ./dorssel | ./dorssel
|
||||
```sh
|
||||
echo Simple as 123 | ./dorssel
|
||||
echo 'Binary as simple as 1, 10 11.' | ./dorssel
|
||||
echo '.... --- .-- -.. --- . ... .. - -.. --- - .... .- -' | ./dorssel
|
||||
echo 'HOW DOES IT DO THAT' | ./dorssel
|
||||
echo 'HOW DOES IT DO THAT' | ./dorssel | ./dorssel
|
||||
```
|
||||
|
||||
## Judges' remarks:
|
||||
|
||||
@@ -52,15 +58,14 @@ You might also want to read the [dorssel.hint](dorssel.hint) file.
|
||||
|
||||
## Author's remarks:
|
||||
|
||||
Usage
|
||||
=====
|
||||
### Usage
|
||||
|
||||
This program converts ASCII to Morse code, and vice versa.
|
||||
|
||||
The program reads in lines from standard input and writes its result to
|
||||
standard output. The direction of conversion is determined on a per
|
||||
line basis. Each line must consist of a maximum of 952 characters
|
||||
followed by a new line character, they must not contain any NUL
|
||||
followed by a new line character and they must not contain any NUL
|
||||
characters. Otherwise, some characters may be lost in the conversion.
|
||||
|
||||
- Encoding to Morse code: The program knows the Morse codes for all Latin
|
||||
@@ -69,40 +74,46 @@ are replaced by an ASCII space ' '. Morse code per converted character is
|
||||
written as a sequence of '.' and '-' characters, each sequence followed by a
|
||||
space.
|
||||
|
||||
- Decoding from Morse code: Morse code should be typed as sequences of '.' and
|
||||
'-' characters, separated by a space. Extra spaces will be echoed back (to
|
||||
separate words). Unknown sequences will be converted to a '?' character.
|
||||
- Decoding from Morse code: Morse code should be typed as sequences of `.` and
|
||||
`-` characters, separated by a space. Extra spaces will be echoed back (to
|
||||
separate words). Unknown sequences will be converted to a `?` character.
|
||||
|
||||
Notes
|
||||
=====
|
||||
### Notes
|
||||
|
||||
The program assumes both source and execution character sets to be
|
||||
ASCII. As far as I can tell, the program is conforming and does not
|
||||
invoke undefined behavior.
|
||||
|
||||
Hints
|
||||
=====
|
||||
### Hints
|
||||
|
||||
Try
|
||||
Try:
|
||||
|
||||
./dorssel < dorssel.c | ./dorssel
|
||||
```sh
|
||||
./dorssel < dorssel.c | ./dorssel
|
||||
```
|
||||
|
||||
to see what variables, numbers, and functions are used. (Does a
|
||||
better job than most C beautifiers).
|
||||
|
||||
- Filter your text twice (as in the previous sample) to obtain a version of your
|
||||
text that can be reversibly morsified, i.e.
|
||||
text that can be reversibly Morsified, i.e.
|
||||
|
||||
./dorssel | ./dorssel | ./dorssel | ./dorssel
|
||||
```sh
|
||||
./dorssel < dorssel.c | ./dorssel | ./dorssel | ./dorssel
|
||||
```
|
||||
|
||||
is the same as
|
||||
|
||||
./dorssel | ./dorssel
|
||||
```sh
|
||||
./dorssel < dorssel.c | ./dorssel
|
||||
```
|
||||
|
||||
- The file "dorssel.hint" contains all the information needed to understand this
|
||||
program. For an obfuscated spoiler, try
|
||||
|
||||
./dorssel < dorssel.hint
|
||||
```sh
|
||||
./dorssel < dorssel.hint
|
||||
```
|
||||
|
||||
## Copyright and CC BY-SA 4.0 License:
|
||||
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
# Information about this entry
|
||||
|
||||
Morse codes for letters and digits use sequences of 1 to 5 (inclusive)
|
||||
Morse symbols (denoted as '.' or '-'), e.g.
|
||||
|
||||
```
|
||||
E .
|
||||
Z --..
|
||||
3 ...--
|
||||
```
|
||||
|
||||
(BTW: digits always have 5 symbols, letters 1 to 4. I didn't use this fact)
|
||||
(BTW: digits always have 5 symbols, letters 1 to 4. I didn't use this fact.)
|
||||
|
||||
Of course we can encode these symbols with bits in a byte. I chose '-'
|
||||
to be a bit set to 0, '.' to be a bit set to 1. Characters in C are at
|
||||
Of course we can encode these symbols with bits in a byte. I chose `-`
|
||||
to be a bit set to 0, `.` to be a bit set to 1. Characters in C are at
|
||||
least 8 bits, but they are either signed or unsigned. To be portable,
|
||||
I've used only values from 0 to 127 (inclusive) (both in the encoding
|
||||
of Morse codes, as well as in subexpressions in the program). Hence we
|
||||
@@ -18,42 +22,52 @@ the source code excessively large ;-)
|
||||
The least significant bit contains the first symbol of a Morse code, so
|
||||
we have
|
||||
|
||||
```
|
||||
E > 0......1
|
||||
Z > 0...1100
|
||||
3 > 0..00111
|
||||
```
|
||||
|
||||
The dots have still to be determined. But we also need to encode the
|
||||
length of the sequence. Since there are only 4 different kind of lengths,
|
||||
two bits would suffice, e.g.
|
||||
|
||||
```
|
||||
E > 000....1
|
||||
Z > 010.1100
|
||||
3 > 01100111
|
||||
```
|
||||
|
||||
That means bits 6 and 5 set to 00 is length 1, set to 01 is length 2, etc.
|
||||
But this would mean that we would have to extract the length into a
|
||||
counter before shifting out each symbol, such as
|
||||
|
||||
```c
|
||||
length = (encoding & 96) >> 5;
|
||||
do {
|
||||
putchar(encoding & 1 ? '-' : '.');
|
||||
encoding >>= 1;
|
||||
} while (length--);
|
||||
```
|
||||
|
||||
We can also use the remaining bits to *include* the counter
|
||||
|
||||
```
|
||||
length 1: 00000.1x E > 00000.11
|
||||
length 2: 0000.1xx
|
||||
length 3: 000.1xxx
|
||||
length 4: 00.1xxxx Z > 00.11100
|
||||
length 5: 0.1xxxxx 3 > 0.100111
|
||||
```
|
||||
|
||||
With 1 bit still to be chosen freely (let's assume it is 0). Now we can do
|
||||
|
||||
```c
|
||||
do {
|
||||
putchar(encoding & 1 ? '-' : '.');
|
||||
encoding >>= 1;
|
||||
while (encoding > 1);
|
||||
```
|
||||
|
||||
As you see, the encoded Morse sequence can be the counter itself. But
|
||||
this is not very pleasant, as some Morse sequences will end up having
|
||||
@@ -61,66 +75,75 @@ an ASCII value < 32, i.e. they will be control codes. This would make
|
||||
the initializing (encoding) string awkward. Hence I came up with the
|
||||
following
|
||||
|
||||
```
|
||||
length 1: 0010010x E > 00100101 (= '%')
|
||||
length 2: 001010xx
|
||||
length 3: 00110xxx
|
||||
length 4: 0100xxxx Z > 01001100 (= 'L')
|
||||
length 5: 011xxxxx 3 > 01100111 (= 'g')
|
||||
```
|
||||
|
||||
Notice these encodings in the initialization string. Only the coding for
|
||||
'5' needs an escape (5 = ..... > 01111111 = '\177'). The way the length
|
||||
`5` needs an escape (`5 = ..... > 01111111 = '\177'`). The way the length
|
||||
is encoded still allows the encoding to be its own counter:
|
||||
|
||||
```c
|
||||
do {
|
||||
putchar(encoding & 1 ? '-' : '.');
|
||||
encoding = (encoding + 32) >> 1;
|
||||
} while (encoding > 35);
|
||||
```
|
||||
|
||||
The last shift will always result in 00100010, hence the test for >35.
|
||||
Note that the calculation for the next value of `encoding' is done at int
|
||||
precision, so that the actual value of the char remains <= 127.
|
||||
The last shift will always result in `00100010`, hence the test for `>35`.
|
||||
Note that the calculation for the next value of `encoding` is done at int
|
||||
precision, so that the actual value of the char remains `<= 127`.
|
||||
|
||||
The character '-' (ASCII 45) and '.' (ASCII 46) are conveniently in
|
||||
The character `'-'` (ASCII 45) and `'.'` (ASCII 46) are conveniently in
|
||||
sequence, so the putchar can simply be
|
||||
|
||||
```c
|
||||
putchar(45 + encoding % 2);
|
||||
```
|
||||
|
||||
I used the modulo operator this time, because '+' takes precedence over '&'.
|
||||
I used the modulo operator this time, because `+` takes precedence over `&`.
|
||||
The assignment can be incorporated into the while control test, and a
|
||||
do-while loop with a body of a single expression statement can be written
|
||||
as a while loop with a comma operator in the test:
|
||||
|
||||
```c
|
||||
while(putchar(45 + encoding % 2), encoding = encoding + 32 >> 1);
|
||||
```
|
||||
|
||||
Voila.
|
||||
|
||||
Now, the decoding of Morse codes is just the reverse. We shift in 0's
|
||||
and 1's as we scan the line. But: we have to scan from back to front
|
||||
Now, the decoding of Morse codes is just the reverse. We shift in `0`s
|
||||
and `1`s as we scan the line. But: we have to scan from back to front
|
||||
(the left most Morse symbol is in the least significant bit, and thus
|
||||
has to shifted in last). The test for the scanning while loop must
|
||||
stop when there are no more '.' or '-' on the left. Hence we need a
|
||||
stop when there are no more `.` or `-` on the left. Hence we need a
|
||||
terminating character in front of the line. The input line is read one
|
||||
past the encoding string (into the same character array) the terminating
|
||||
character for the reverse scan is therefore provided by the implicit '\0'
|
||||
character for the reverse scan is therefore provided by the implicit `'\0'`
|
||||
of the array initializer ! The test also stops when at most 5 symbols
|
||||
have been shifted in.
|
||||
|
||||
The encoded character is then looked up in the encoding string with
|
||||
memchr (why memchr is explained later). Either a '?' (when there are
|
||||
`memchr()` (why `memchr()` is explained later). Either a `?` (when there are
|
||||
more than 5 consecutive Morse symbols, or if the sequence is unknown)
|
||||
or the decoded character is printed. Or a ' ' if there was an extra
|
||||
or the decoded character is printed. Or a `' '` if there was an extra
|
||||
space in the source line. Note the use of "or" in these last sentences,
|
||||
as they explain the use of the conditional operator.
|
||||
|
||||
The strspn calls need either " .-" or ".-" as argument. Of course they
|
||||
The `strspn` calls need either `" .-"` or `".-"` as argument. Of course they
|
||||
are overlapped. I put these strings between the encodings for digits
|
||||
and letters. This is OK, since neither of these characters, or the '\0',
|
||||
is a valid encoding ! We must, however, use memchr instead of strchr in
|
||||
the decoding lookup because of this '\0'. Since it is far more obscure
|
||||
and letters. This is OK, since neither of these characters, or the `'\0'`,
|
||||
is a valid encoding ! We must, however, use `memchr()` instead of `strchr()` in
|
||||
the decoding lookup because of this `'\0'`. Since it is far more obscure
|
||||
this way, I hope you'll forgive me.
|
||||
|
||||
The outline of the program is roughly as follows
|
||||
|
||||
```
|
||||
while (more lines) {
|
||||
if (line is in morse) {
|
||||
while (more characters) {
|
||||
@@ -137,23 +160,25 @@ while (more lines) {
|
||||
putchar(' ');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Because of the *two* while(more characters) loops are the same, I've
|
||||
exchanged the if(line is in morse) and the two while loops (so there
|
||||
is *single* while loop). However, we must now store the if(line is in
|
||||
morse) test for efficiency (it would be stupid to test the entire line
|
||||
for every character). This is done in l[0] ! (the first encoding is
|
||||
thus in l[1]). The memchr decoding lookup is not disturbed, because the
|
||||
test is always 0 for Morse lines, and '\0' is not a possible encoding.
|
||||
l[0] is also used as the temporary shift character while encoding a
|
||||
line of text. This does not disturb the if (line is in morse) test,
|
||||
as the temporary l[0] will remain != 0.
|
||||
Because of the *two* `while(more characters)` loops are the same, I've
|
||||
exchanged the `if(line is in morse)` and the two while loops (so there
|
||||
is *single* while loop). However, we must now store the `if(line is in
|
||||
morse)` test for efficiency (it would be stupid to test the entire line
|
||||
for every character). This is done in `l[0]` ! (the first encoding is
|
||||
thus in `l[1]`). The `memchr()` decoding lookup is not disturbed, because the
|
||||
test is always `0` for Morse lines, and `'\0'` is not a possible encoding.
|
||||
`l[0]` is also used as the temporary shift character while encoding a
|
||||
line of text. This does not disturb the `if (line is in morse)` test,
|
||||
as the temporary `l[0]` will remain `!= 0`.
|
||||
|
||||
I've put the if's in the above layout into a single switch. Hope you don't
|
||||
I've put the `if`s in the above layout into a single switch. Hope you don't
|
||||
mind ;-)
|
||||
|
||||
Summary of the use of l[999]:
|
||||
|
||||
```
|
||||
l[0] result of "is this line in morse" test
|
||||
l[0] temporary shift during encoding
|
||||
l[0] - l[33] Morse codings (letters and digits, plus garbage)
|
||||
@@ -161,13 +186,14 @@ l[11] - l[14] strspn argument
|
||||
l[12] - l[14] strspn argument
|
||||
l[34] terminator for backward decoding
|
||||
l[35] - l[998] input line
|
||||
```
|
||||
|
||||
*****
|
||||
---
|
||||
|
||||
Selected notes:
|
||||
- We don't need to declare isalnum() (or include ctype.h) as its implicit
|
||||
- We don't need to declare `isalnum()` (or include ctype.h) as its implicit
|
||||
declaration is correct (int argument, int result). This is not so for
|
||||
strlen(), strspn(), and memchr() as they use size_t.
|
||||
`strlen()`, `strspn()`, and `memchr()` as they use `size_t`.
|
||||
- During development, my gcc 2.7.2.3 had an internal compiler error (signal
|
||||
6) on code that was correct !
|
||||
- lclint 2.4b thinks that "the observer is modified" a couple of times,
|
||||
@@ -176,7 +202,7 @@ Selected notes:
|
||||
- lclint 2.4b is positive the second while loop is infinite, whereas it is
|
||||
not.
|
||||
|
||||
*****
|
||||
---
|
||||
|
||||
.. - .... .. -. -.- - .... .. ... ... .--. --- .. .-.. . .-. .. ... -- --- .-. . --- -... ..-. ..- ... -.-. .- - . -.. - .... .- -. - .... . .--. .-. --- --. .-. .- --
|
||||
|
||||
Reference in new issue
Block a user