Om de verspreiding van COVID-19 tegen te gaan, heeft de overheid Coronalert1 laten ontwikkelen. Deze smartphone app houdt volledig anoniem bij of je risicovolle contacten gehad hebt. De app maakt hiervoor gebruik van anonieme tokens: een opeenvolging van willekeurige hoofdletters en cijfers, bijvoorbeeld 4658VR72S. Coronalert genereert om de paar minuten automatisch een nieuw token en wisselt dat via Bluetooth uit met de app van mensen die dicht bij jou in de buurt zijn. Als iemand positief test op het coronavirus dan worden alle tokens die de app van die persoon de afgelopen 14 dagen gegenereerd heeft anoniem doorgestuurd naar een centrale databank. Jouw app kan deze databank raadplegen om te controleren of je nauwe contacten had met besmette personen.

Coronalert
Coronalert: de Belgische corona-app die door iedereen gratis kan gedownload worden.

Omdat Coronalert enkel gebruikmaakt van anonieme tokens en geen locatiegegevens of andere persoonsgegevens, kan de app niet gebruikt worden om personen te identificeren. De anonieme tokens die jij uitwisselt met andere mensen veranderen ook steeds, zodat verschillende mensen met wie je in contact geweest bent ook verschillende tokens zullen ontvangen. Het is dus ook niet mogelijk om patronen te ontdekken in de uitwisseling van tokens, wat zou kunnen leiden tot persoonlijke identificatie. Coronalert is dus volledig anoniem en verzamelt geen informatie die jouw privacy in het gedrang brengt. Bovendien is de app volledig open source2!

Opgave

Schrijf een bash shell script coronalert dat gebruikt kan worden om risicovolle contacten op te sporen van iemand die positief getest heeft op het coronavirus. Aan het shell script moeten verplicht drie argumenten doorgegeven worden: i) de naam van een persoon die positief getest heeft, ii) de padnaam van een user directory en iii) de padnaam van een token directory.

Rechtstreeks onder de user directory zitten tekstbestanden die benoemd zijn met de (unieke) naam van een persoon en de extensie .txt. Louter voor de aanschouwelijkheid hebben we in deze opgave gekozen om namen van personen te gebruiken, maar in de praktijk blijft alles uiteraard anoniem. De user directory ziet er dan bijvoorbeeld als volgt uit:

users/sem1/
    ├── Bart.txt
    ├── Eric.txt
    ├── Guy.txt
    ├── Kris.txt
    └── Peter.txt

De bestanden in de user directory bevatten alle tokens die de app van de persoon in de laatste 14 dagen gegenereerd heeft, één token per regel. Zo heeft de app van Peter bijvoorbeeld de volgende tokens gegenereerd (users/sem1/Peter.txt):

S8YQWZDC1
9I48820AU
6UYYCMSPL
5OQ1BQMV4
3CNQVMWGH

De token directory bevat een volledige directorystructuur met gewone leesbare bestanden. Zowel de directories als de bestanden zijn vernoemd zijn naar tokens. De token directory ziet er dan bijvoorbeeld als volgt uit:

tokens/sem1/
├── 1OMRIFDLU/
│   └── QPB8GSUSR/
│       └── 6UYYCMSPL
├── 1TFNNBIJ9/
│   └── AUX3BBP1Y
├── 1VVXYNWDB/
│   └── HV1T4FP69/
│       └── Y3LTGX3JW/
│           └── 7BO7DUHO5/
…
├── 21GYZ9B2V/
│   └── 9I48820AU

Voor elk gegeneerd token is er juist één gewoon bestand onder de token directory dat naar het token vernoemd is. Daarnaast kunnen er verschillende directories zijn die naar het token vernoemd zijn. Maar de namen van de directories doen er eigenlijk niet toe. Het gaat om het gewoon bestand dat naar een token vernoemd is. Dat bevat immers de namen van alle personen wiens app het token ontvangen heeft van de app van een andere gebruiker, één naam per regel.

In de token directory die we hierboven als voorbeeld gebruikt hebben, vind je bijvoorbeeld een bestand voor het token 9I48820AU (waarvan we uit de user directory weten dat dit gegenereerd is door de app van Peter) in de directory tokens/sem1/21GYZ9B2V/. Dit bestand bevat de volgende lijst van namen:

Bart
Guy
Kris

Dit zijn dus de namen van alle gebruikers die het token 9I48820AU ontvangen hebben dat gegenereerd werd door de app van Peter. Om de risicovolle contacten van Peter (of de persoon die als eerste argument wordt doorgegeven) op te sporen, moet het shell script alle personen vinden wiens app een bepaald aantal (verschillende) tokens ontvangen heeft van de app van Peter. Dit aantal moet groter dan of gelijk zijn aan een gegeven drempelwaarde. Het shell script moet een lexicografisch gerangschikte lijst met de namen van deze personen (de risicovolle contacten) uitschrijven naar stdout, één naam per regel, rekening houdend met de volgende opties die het shell script moet ondersteunen:

Het shell script moet voor de verwerking van de opties de flexibiliteit aan de dag leggen die gebruikelijk is bij Unix commando's: volgorde van opties speelt geen rol, opties kunnen eventueel samengenomen worden, argument bij een optie moet niet noodzakelijk van de optieletter gescheiden worden door witruimte, …. Daarnaast moet het shell script de volgende foutafhandeling voorzien:

Zie onderstaande voorbeeldsessie voor wat we telkens bedoelen met de gepaste boodschap die moet uitgeschreven worden naar stderr.

Voorbeeld

Onderstaande voorbeeldsessie toont aan hoe het shell script coronalert moet kunnen gebruikt worden. In dit ZIP-bestand3 vind je de user directories en token directories zoals ze in onderstaande voorbeelden en in de testgevallen gebruikt worden.

$ coronalert
Syntaxis: coronalert [-d <drempelwaarde>] [-t] [-c] <naam> <userdir> <tokendir>
$ echo $?
1
$ coronalert -d abc123 Peter users/sem1 tokens/sem1
coronalert: ongeldige drempelwaarde
$ echo $?
2
$ coronalert -d abc123 Peter dir1 dir2
coronalert: ongeldig pad
$ echo $?
3
$ coronalert Hamtaro users/sem1 tokens/sem1
coronalert: de opgegeven naam is niet gevonden
$ echo $?
4
$ coronalert Peter users/sem1 tokens/sem1
Bart
Eric
Guy
Kris
$ coronalert -t Peter users/sem1 tokens/sem1
Bart: 3CNQVMWGH, 5OQ1BQMV4, 6UYYCMSPL, 9I48820AU, S8YQWZDC1
Eric: 3CNQVMWGH, 5OQ1BQMV4, 6UYYCMSPL
Guy: 3CNQVMWGH, 5OQ1BQMV4, 6UYYCMSPL, 9I48820AU
Kris: 3CNQVMWGH, 5OQ1BQMV4, 6UYYCMSPL, 9I48820AU, S8YQWZDC1
$ coronalert -c Peter users/sem1 tokens/sem1
Bart (5)
Eric (3)
Guy (4)
Kris (5)
$ coronalert -tcd 5 Peter users/sem1 tokens/sem1
Bart (5): 3CNQVMWGH, 5OQ1BQMV4, 6UYYCMSPL, 9I48820AU, S8YQWZDC1
Kris (5): 3CNQVMWGH, 5OQ1BQMV4, 6UYYCMSPL, 9I48820AU, S8YQWZDC1