Benedict Arnold1 versleutelde de berichten die hij naar het Britse leger stuurde aan de hand van het boek Commentaries on the Laws of England2 van sir William Blackstone3.

boekcodes
Benedict Arnold gebruikte geheimschrift om te communiceren met het Britse leger.

Daarbij verving hij sommige woorden door drie getallen die respectievelijk het paginanummer, het regelnummer en de woordpositie aanduiden van een woord in het boek van Blackstone. Zo verstuurde hij bijvoorbeeld het volgende gecodeerde bericht

The 166.8.11 of the 191.9.16 are 129.19.21 266.9.14 of the .286.8.20, and 291.8.27 to be on 163.9.4 115.8.16 114.8.25ing — 263.9.14 are 207.8.17ed 125.8.15 103.8.60 from this 294.8.50 104.9.26 — If 84.8.9ed — 294.9.12 129.8.7 only to 193.8.3 and the 64.9.5 290.9.20 245.8.3 be at an 99.8.14.

Majoor John André was door het Britse leger aangesteld als de contactpersoon van Benedict Arnold. Als hij berichten van Arnold ontving, dan kon hij de versleutelde woorden opzoeken in zijn eigen examplaar van het boek van Blackstone om daarmee de berichten te decoderen.

The mass of the People are heartily tired of the War, and wish to be on their former footing — They are promised great events from this year's exertion — If disappointed — you have only to persevere and the contest will soon be at an end.

Het gevaar van boekcodes is echter dat de vijand makkelijk berichten kan decoderen als hij weet te achterhalen welk boek er gebruikt wordt — en soms zelfs ook als hij niet weet over welk boek het gaat.

Opgave

We hebben een boekcode ontwikkeld die gebruikmaakt van meerdere boeken. De inhoud van elk boek zit vervat in een tekstbestand. De tekstbestanden zelf zitten verspreid over een directorystructuur die er bijvoorbeeld als volgt kan uitzien:

books
├── adventure
│   ├── 1184.txt
│   └── D'Artagnan Series
│       ├── 1257.txt
│       ├── 1259.txt
│       └── 2759.txt
├── children
│   └── 14304.txt
└── fantasy
    ├── 420.txt
    └── 964.txt

Een woord wordt gecodeerd als boek:regelindex:woordindex waarbij boek de naam is van een gewoon bestand dat ergens in de directorystructuur staat, regelindex het volgnummer is van een regel uit dat bestand en woordindex het volgnummer is van een woord op die regel. Hierbij worden de regels in een bestand en de woorden op een regel telkens genummerd vanaf 1. De woorden op een regel worden van elkaar gescheiden door één of meer witruimtekarakters. Dat betekent dus dat de regel

America's favorite sugar-free chocolate.

uit vier woorden bestaat: "America's", "favorite", "sugar-free" en "chocolate." (inclusief het punt op het einde).

Schrijf een bash shell script bookcode waaraan minstens drie argumenten moeten doorgegeven worden. Het eerste argument is een subcommando dat aangeeft of er een gegeven tekst moet gecodeerd worden als boekcode (encode), dan wel of er een gegeven boekcode moet ontcijferd worden (decode). Het tweede argument is de padnaam van de directorystructuur waaronder tekstbestanden met de inhouden van de boeken verspreid zitten die bij het coderen en decoderen moeten gebruikt worden.

Als het shell script wordt uitgevoerd met subcommando decode dan moet als derde argument de padnaam van een tekstbestand met de boekcode doorgegeven worden. Elke regel van het tekstbestand beschrijft waar het volgende woord van de gecodeerde tekst kan teruggevonden worden in de directorystructuur. Het tekstbestand met de boekcode kan er bijvoorbeeld als volgt uitzien (oz.txt4):

964.txt:7037:1
2759.txt:21811:4
1184.txt:22370:5
1259.txt:4124:13
420.txt:2953:4

Het shell script moet de ontcijferde tekst uitschrijven naar stdout, waarbij de opeenvolgende woorden gescheiden worden door één enkele spatie.

Als het shell script wordt uitgevoerd met subcommando encode dan worden alle argumenten samen (behalve de eerste twee) beschouwd als de woorden van een bericht. Het shell script moet voor elk woord van het bericht een regel uitschrijven naar stdout met een boekcode voor het woord. Hierbij mag het shell script ervan uitgaan dat er voor elk woord altijd minstens één boekcode kan gevonden worden in de directorystructuur, zonder dat dit expliciet moet gecontroleerd worden. Als er in de directorystructuur meerdere boekcodes kunnen gevormd worden voor een woord, dan moet het shell script er daar willekeurig één uitkiezen.

Woorden matchen

De definitie van "woord" die we in deze opgave gebruiken, houdt in dat woorden naast letters ook andere karakters kunnen bevatten. Hieronder zitten ook karakters die een speciale betekenis hebben binnen de context van reguliere expressies. Zo hebben de karakters ^$[]*.\ een speciale betekenis voor de reguliere expressies in grep. Bijkomend hebben de karakters ()|+?{} een speciale betekenis voor de reguliere expressies in egrep. Je moet deze karakters dus escapen als je hun letterlijke betekenis wilt behouden. Dit wordt geïllustreerd in onderstaande voorbeeld.

$ echo '$[]*.\^()|+?{}' | sed 's/[]$*.\^()|+?{}\[]/\\&/g'
\$\[\]\*\.\\\^\(\)\|\+\?\{\}

Voorbeeld

Onderstaande voorbeeldsessie geeft aan hoe het shell script bookcode moet kunnen gebruikt worden. Hierbij gaan we ervan uit dat het tekstbestand oz.txt5 en de directory books (ZIP6) zich in de huidige directory bevinden.

$ cat oz.txt
964.txt:7037:1
2759.txt:21811:4
1184.txt:22370:5
1259.txt:4124:13
420.txt:2953:4
$ bookcode decode books7 oz.txt8
There's no place like home.
$ bookcode encode books9 "There's no place like home."
964.txt:7037:1
964.txt:5347:1
1259.txt:31977:5
1257.txt:25501:7
1259.txt:15959:11
$ bookcode encode books10 "There's" "no" "place" "like" "home."
964.txt:7037:1
1184.txt:21498:1
1257.txt:29284:9
1257.txt:4307:2
1259.txt:15690:2
$ bookcode encode books11 "There's no" "place like" "home."
964.txt:7037:1
1184.txt:38640:1
964.txt:9888:9
420.txt:809:11
1257.txt:15533:11