This commit is contained in:
stsh
2022-02-17 11:45:11 +01:00
parent 674695e82a
commit 62f8e2ce05
212 changed files with 11436 additions and 0 deletions
+532
View File
@@ -0,0 +1,532 @@
# 04 - Modularisieren von C Code
```{eval-rst}
.. figure:: zhaw_neg_P2945.jpg
:width: 100px
:name: logo
:align: right
```
___
```{eval-rst}
.. figure:: modularisieren_von_c_code.JPG
:width: 500px
:name: logo
:align: center
```
___
## Inhalt
{ref}`04_introduction`
{ref}`04_learning_objectives`
{ref}`04_task_01`
{ref}`04_task_02`
{ref}`04_grading`
{ref}`04_appendix`
___
(04_introduction)=
## 1. Übersicht
In diesem Praktikum üben Sie modulare Programmierung indem Sie ein
Java Programm (bestehend aus drei Java Files) in ein entsprechendes C
Programm aus drei Modulen (aus je einem Header- und Implementations-
File) übersetzen. Sie passen das Makefile so an, dass die
entsprechenden Module mit kompiliert werden.
In der zweiten Aufgabe erstellen Sie Makefile Regeln für die drei
Schritte von den C Source Files zur graphischen Darstellung der
Abhängigkeiten.
```{eval-rst}
.. figure:: uebersicht.png
:width: 500px
:name: uebersicht
:align: center
```
Im Anhang ist eine Übersicht über die verwendeten File Formate gegeben.
(04_learning_objectives)=
## 2. Lernziele
In diesem Praktikum lernen Sie die Handgriffe um ein Programm zu modularisieren, d.h. in mehrere Module aufzuteilen.
* Sie wissen, dass ein Modul aus einem C-File und einem passenden
H-File besteht.
* Sie können Header Files korrekt strukturieren.
* Sie deklarieren im Header-File die öffentlichen Typen und Funktionen
eines Moduls.
* Sie wissen wie **Include Guards** anzuwenden sind.
* Sie können Module im `Makefile` zur Kompilation hinzufügen.
* Sie können `Makefile` Regeln schreiben.
Die Bewertung dieses Praktikums ist am Ende angegeben.
Erweitern Sie die vorgegebenen Code Gerüste, welche im `git`
Repository `snp-lab-code` verfügbar sind.
(04_task_01)=
## 3. Aufgabe 1: Modularisieren
Das zu ergänzende Programm dep2dot hat folgende Funktionalität:
Ergänzen Sie in **`modularize/src`** den Code in **`triangle.c`**,
**`read.h`**, **`read.c`**, **`rectang.h`** und **`rectang.c`** so
dass die Tests erfolgreich durchlaufen. Die C Implementation soll
dieselbe Funktionalität haben wie die gegebenen Java Files. Lehnen Sie
sich so nahe wie möglich an die Java Files an.
1. In den Header-Files implementieren Sie den Include-Guard und
deklarieren Sie die öffentlichen Funktionen und gegebenenfalls
**`#define`**.
2. In den Implementations-Files implementieren Sie die Funktionen.
Die drei Java Files liegen in **`modularize/java`**.
### Tipps
* Implementieren Sie die Symbole welche vollständig in Grossbuchstaben
geschrieben sind als **`#define`**.
* **`EOF`** kommt schon aus **`stdio.h`** und sollte deshalb nicht
mehr definiert werden.
* Jene **`#define`** welche von andern Modulen verwendet werden
kommen ins Header-File, die andern ins Implementations-File.
* Ein Grossteil des Java Codes aus den Methoden Bodies kann
eins-zu-eins in C übernommen werden. Listen Sie auf welche
Unterschiede es gibt:
<table>
<style>
table, th, td {
border: 1px solid black;
border-collapse: collapse;
}
table th:first-of-type {
width: 50%;
}
table th:nth-of-type(2) {
width: 50%;
}
</style>
<tr><th>Java</th><th>C</th></tr>
<tr><td>
```Java
byte
```
</td><td></td></tr>
<tr><td>
```Java
boolean
```
</td><td></td></tr>
<tr><td>
```Java
true
```
</td><td></td></tr>
<tr><td>
```Java
false
```
</td><td></td></tr>
<tr><td>
```Java
System.out.print(…)
```
</td><td></td></tr>
<tr><td>
```Java
System.out.println(…)
```
</td><td></td></tr>
<tr><td>
```Java
System.in.read()
```
</td><td></td></tr>
<tr><td>
```Java
byte[] buffer = new byte[BUFFERSIZE];
```
</td><td></td></tr>
<tr><td>
```Java
public class rectang {
public boolean Rectangular(…)
{ … }
}
```
</td><td></td></tr>
<tr><td>
```Java
public class read {
public int getInt(...)
throws java.io.IOException
{ ... }
}
```
</td><td></td></tr>
<tr><td>
```Java
class triangle {
public static void main(String[] args)
throws java.io.IOException
{ ... }
}
```
</td><td></td></tr>
<tr><td>
```Java
read ReadInt = new read();
...
word = ReadInt.getInt(MAX_NUMBER);
```
</td><td></td></tr>
<tr><td>
```Java
rectang Rect = new rectang();
...
if (Rect.Rectangular(a, b, c) == true) { ... }
```
</td><td></td></tr>
<tr><td>
```
System.out.println(
"-> Dreieck " + a + "-" + b + "-" + c
+ " ist rechtwinklig");
```
</td><td></td></tr>
</table>
(04_task_02)=
## 4. Aufgabe 2: Makefile Regeln
Die folgenden drei Schritte erstellen von einem C Source File eine
graphische Darstellung der Abhängigkeiten:
1. `gcc ... -H .. file.c ... 2> file.dep` (Regeln im Makefile bereits vorhanden)
2. `dep2dot file.c <file.dep >file.dot` (in dieser Aufgabe zu erstellen)
3. `dot -Tpng file.dot >file.png` (in dieser Aufgabe zu erstellen)
Sie sollen für die Compiler-ähnlichen Programme `dep2dot` und `dot`
Makefile Regeln schreiben.
```{eval-rst}
.. figure:: uebersicht.png
:width: 500px
:name: uebersicht
:align: center
```
Das Programm `dep2dot` hat folgende Funktionalität:
1. Es liest von `stdin` die vom Compiler generierten
Abhängigkeits-Daten in Form des `dep` Formates ein.
2. Das erste und einzige Command Line Argument gibt das File an für
welches die von `stdin` gelesenen Abhängigkeiten gelten.
3. Auf `stdout` werden die Abhängigkeiten von `stdin` übersetzt als
`dot`-File Format ausgegeben.
Das Programm `dot` hat folgende Funktionalität:
1. Es liest die textuelle Beschreibung eines Graphen aus der
übergebenen Datei (erstes Argument) ein.
2. Auf `stdout` wird die grafische Darstellung der Beschreibung der
Eingabe-Datei im `png`-File Format ausgegeben.
Das `dep`-Format und das `dot`-Format sind im Anhang beschrieben.
Sie können die Funktionalität des Programms `dep2dot` kennen lernen,
indem Sie folgende Zeilen auf der Bash Shell ausführen. Das
`dep.input` File ist Teil der automatisierten Test Suite im
Verzeichnis `tests`:
```bash
bin/dep2dot dir/file <tests/dep.input >dep.dot
dot -Tpng dep.dot >dep.png
firefox dep.png
```
Als Resultat sollte Firefox folgende Graphik darstellen:
```{eval-rst}
.. figure:: dep_dot.png
:width: 150px
:name: dep_dot
:align: center
```
Definieren Sie im `Makefile` Regeln, welche die einzelnen Schritte von
den Source Files zu den `png` Files ausführen.
Prüfen Sie schliesslich die Umsetzung Aufgabe mittels `make dep-clean
dep && firefox src/*.png.`
### 4.1 Neue Regeln hinzufügen
Führen Sie im `Makefile` an den angegebenen Stellen folgende
Ergänzungen durch
* definieren Sie eine Variable `DEPFILES` deren Inhalt die Liste alle
Einträge der Variable `SOURCES` ist, wobei bei allen die Endung `.c`
durch die Endung `.c.png` ersetzt ist
* fügen Sie zum `Pseudo-Target .PHONEY` das Target `dep` dazu dies
besagt, dass das später folgenden Target `dep` nicht ein File
repräsentiert (ohne dieses Setting würde make gegebenenfalls nach
einem File mit Namen `dep` suchen um zu entscheiden ob es
inkrementell gebildet werden muss)
* schreiben Sie das Target `dep` gemäss der Beschreibung im Makefile
* schreiben Sie die Suffix Regel für die Übersetzung von `.png <-
.dot` gemäss Vorgabe im `Makefile` (als Inspiration, siehe auch die
`%.c.dep: %.c` Suffix Regel weiter unten im `Makefile`) erklären
Sie was die Regel macht
* schreiben Sie die Suffix Regel für die Übersetzung von` .dot <-
.dep` gemäss Vorgabe im `Makefile` erklären Sie was die Regel
macht
Die Umsetzung der obigen Änderungen sind erfolgreich, wenn Sie
folgende Shell Command Line erfolgreich ausführen können und in
Firefox die Abhängigkeiten der C-Files von den Inclu-de Files
dargestellt wird.
`make dep-clean dep && firefox src/*.png.`
(04_grading)=
## 5. Bewertung
Die gegebenenfalls gestellten Theorieaufgaben und der funktionierende Programmcode müssen der Praktikumsbetreuung gezeigt werden. Die Lösungen müssen mündlich erklärt werden.
| Aufgabe | Kriterium | Punkte |
| :-- | :-- | :-- |
| 1 | Sie können das funktionierende Programm inklusive funktionierende Tests demonstrieren und erklären. | |
| 1 | Module einbinden, Header Files schreiben | 2 |
| 2 | Sie können das funktionierende Makefile demonstrieren und erklären. | |
| 2 | Neue Regeln hinzufügen | 2 |
(04_appendix)=
## 6. Anhang
### 6.1 Verwendete zusätzliche Sprach Elemente
<table><tr><td>
**Sprach Element**
</td><td>
**Beschreibung**
</td></tr>
<tr><td>
```C
fprintf(stderr, "v=%d", v)
```
</td><td>
Formatierte Ausgabe auf den Standard Error Stream. Siehe ***man 3
stderr*** und ***man 3 fprintf***.
</td></tr>
</table>
### 6.2 Verarbeitung und verwendete File Formate <a name="file_formats"></a>
Das Programm in diesem Praktikum ist Teil für die graphische
Darstellung von `#include` File Abhängigkeit von C Files.
Den ersten Schritt für die Darstellung der `#include` File
Abhängigkeiten bietet der Compiler. Der Compiler kann mittels der `-H`
Command Line Option auf `stderr` ein Text File generieren, welches die
tatsächlich verwendeten Header Files auflistet. Zusätzlich wird im
Resultat die Verschachtelungstiefe der Includes angegeben.
Im zweiten Schritt übersetzt das Programm (`dep2dot`) dieses
Praktikums solche Dependency Files (`dep`) in eine Text Repräsentation
der Abhängigkeiten (`dot`) welche in graphische Darstel-lung (`png`)
übersetzt werden kann.
Als Tool zur Übersetzung der `dot` Files in das `png` Format dient das
`dot` Tool. Dieses Tool muss gegebenenfalls installiert werden:
```sudo apt install graphviz```
Die `png` Files können dann z.B. in der Programm Dokumentation
integriert werden (Darstellung zu Test Zwecken z.B. mittels `firefox
file.png`).
#### 6.2.1 dep File
Siehe: `man gcc`
```bash
-H Print the name of each header file used, in addition to other
normal activities. Each name is indented to show how deep in the
#include stack it is. [...]
```
Das File wird auf `stderr` ausgegeben.
**Beispiel File** (für Abhängigkeiten des `main.c` Files des `dep2dot` Programms)
```bash
. /usr/include/stdio.h
.. /usr/include/x86_64-linux-gnu/bits/libc-header-start.h
... /usr/include/features.h
.... /usr/include/x86_64-linux-gnu/sys/cdefs.h
..... /usr/include/x86_64-linux-gnu/bits/wordsize.h
..... /usr/include/x86_64-linux-gnu/bits/long-double.h
.... /usr/include/x86_64-linux-gnu/gnu/stubs.h
..... /usr/include/x86_64-linux-gnu/gnu/stubs-64.h
.. /usr/lib/gcc/x86_64-linux-gnu/7/include/stddef.h
.. /usr/include/x86_64-linux-gnu/bits/types.h
... /usr/include/x86_64-linux-gnu/bits/wordsize.h
... /usr/include/x86_64-linux-gnu/bits/typesizes.h
.. /usr/include/x86_64-linux-gnu/bits/types/__FILE.h
.. /usr/include/x86_64-linux-gnu/bits/types/FILE.h
.. /usr/include/x86_64-linux-gnu/bits/libio.h
... /usr/include/x86_64-linux-gnu/bits/_G_config.h
.... /usr/lib/gcc/x86_64-linux-gnu/7/include/stddef.h
.... /usr/include/x86_64-linux-gnu/bits/types/__mbstate_t.h
... /usr/lib/gcc/x86_64-linux-gnu/7/include/stdarg.h
.. /usr/include/x86_64-linux-gnu/bits/stdio_lim.h
.. /usr/include/x86_64-linux-gnu/bits/sys_errlist.h
. /usr/include/stdlib.h
.. /usr/include/x86_64-linux-gnu/bits/libc-header-start.h
.. /usr/lib/gcc/x86_64-linux-gnu/7/include/stddef.h
.. /usr/include/x86_64-linux-gnu/bits/floatn.h
... /usr/include/x86_64-linux-gnu/bits/floatn-common.h
.... /usr/include/x86_64-linux-gnu/bits/long-double.h
.. /usr/include/x86_64-linux-gnu/bits/stdlib-float.h
. src/error.h
.. /usr/lib/gcc/x86_64-linux-gnu/7/include/stddef.h
. src/data.h
.. /usr/lib/gcc/x86_64-linux-gnu/7/include/stddef.h
. src/output.h
Multiple include guards may be useful for:
/usr/include/x86_64-linux-gnu/bits/stdlib-float.h
/usr/include/x86_64-linux-gnu/bits/sys_errlist.h
/usr/include/x86_64-linux-gnu/bits/typesizes.h
/usr/include/x86_64-linux-gnu/gnu/stubs-64.h
/usr/include/x86_64-linux-gnu/gnu/stubs.h
```
#### 6.2.2 dot File
**Graphviz** ist ein mächtiges Tool-Set welches Graphen, definiert in
einem `dot`-Text File, automatisch anordnet und in `png`, `gif` und
andere Formate übersetzt.
Siehe die offizielle Web-Page
[https://www.graphviz.org/](https://www.graphviz.org/).
Es gibt als Teil dieses Tool-Sets verschiedene Übersetzer. Der hier
verwendete ist der Basis-übersetzer: `dot`.
Das `dot`-File Format kennt viele Möglichkeiten die Knoten und Kanten
eines Graphen und de-ren Anordnung anzugeben.
Der Vorteil eines solchen Tool-Sets ist, dass man den Inhalt (den
Graphen) einfach definieren kann und sich nicht um das komplexe
Problem der ansprechenden Visualisierung kümmern muss.
**Beispiel File** (`dot -Tpng sample.dot > sample.png`)
```C
digraph G {
node [shape=box]
A [label="a.c"];
B [label="a.h"];
C [label="b.h"];
subgraph cluster_c0 {
label="main"; color=black;
A;
}
subgraph cluster_c1 {
label="others"; style=filled; col-or=lightgrey;
{ B; C; rank=same; }
}
A -> B;
A -> C;
B -> C;
}
```
#### 6.2.3 png File
Das `png` Format ist ein verlustfrei komprimiertes Raster Graphik
Format. Es wird oft in Web Pages verwendet.
___
Version: 15.02.2022
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 392 KiB

@@ -0,0 +1 @@
<mxfile host="Electron" modified="2022-02-07T21:41:53.021Z" agent="5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/16.5.1 Chrome/96.0.4664.110 Electron/16.0.7 Safari/537.36" etag="nbLc9Vjb_iVSFHhKpDKu" version="16.5.1" type="device"><diagram id="L8fo7XVtsVLzhWI8Oabt" name="Page-1">7VnRcuIgFP2aPLoTiIn6WLXd7sx2ptPuTrf7RhM0TDG4BGvs1y9RYgLoGjPqVLtPkgtc4Jx74IKON5hkXzmaxncswtSBbpQ53tCBEAReR/7kloWyuK67sow5iZStNDySd1w0VNYZiXCqNRSMUUGmujFkSYJDodkQ52yuNxsxqo86RWNsGR5DRG3rE4lErKwg6JUVt5iMYzV0F6oVT1DRWK0kjVHE5hWTd+14A86YWJUm2QDTHL0Cl1W/my2164lxnIg6He5GrYenJLv986310P99tfj56oKW8vKG6EwtWE1WLAoEcBJd5UDKr5CiNCXhj5gkjtePxYRKI5BFzmZJhPOBXPkle9wQWtStHOLIwrmcOFjDIQMJswkWfCGbzEvAfQViXIG6sHFMkSBvunukeB+v3a1HuGdEDgxdFaR+EWoqRNtdV3eRshkPsepVBdhw1G3vcCQQH2NhOZKFyrJL05K/PbiE/7n0oUFBryGXpiMAT8ultxeXCUvwv0j86LQFro62Z6Jdl7ZOoDuC7dPSVmwlFd6oTZzk4Tt6kSelRhmiZJzkwpScYC4Nb5gLIk+iK1UxIVGU++hznJJ39LL0l9M7zZezXKDfd/zhmvDcAc40TtQxqTqXh1M1FLYHpB0fyrv7BcBuT4f+IJHR0p0WuigcsNEoxUdhst1EgBFK46XowDmr0RTR2aqxY3EYX4YY2zvEGLQ7UNcNOEhkGAoPTiVG//OK0TwaG2c0O8/YI4sxsDh8+QAkrq+lqx5ntsE25bR0dHztdj+vdk2eG2t3Z8AcWbu9z8vhwa4m5jPDqTkE9lPPPbBolCmK0OlKBWeveMAo4yW3I0KpYaqfMc1jIvDjFIX5mHOOpluCYkPGVCMsCri7vgY37NlbPNwQNp67PUI0SvbGH9r42zK6GPyt3H/DEXta/Bs9qdTbxHBGxK9K+Vm1ysvDrFIxXOy57cnpLfeXnYH1UVIb36DdvPI13S7r3h1PmdoA+16SXa6ireftoJ6iTeIOp2g7LVlcLvymIIBbD34zzagBv/ws/5ZaiaX8d8+7/gs=</diagram></mxfile>
Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

@@ -0,0 +1,127 @@
# 04 - Modularisieren von C Code
___
## 1. Übersicht
In diesem Praktikum wird eine kleine Sammlung von Funktionen als Modul erstellt.
In der ersten Aufgabe schreiben Sie zu einem bestehenden C Programm die notwendigen Header Files plus passen das Makefile so an, dass die entsprechenden Module mit kompiliert werden.
In der zweiten Aufgabe erstellen Sie Makefile Regeln um aus Konfigurationsdateien graphischen Darstellungen zu erzeugen.
___
## 2. Lernziele
In diesem Praktikum lernen Sie die Handgriffe um ein Programm zu modularisieren, d.h. in mehrere Module aufzuteilen.
- Sie wissen, dass ein Modul aus einem C-File und einem passenden H-File bestehen.
- Sie können Header Files korrekt strukturieren.
- Sie wissen wie **Include Guards** anzuwenden sind.
- Sie können Module im **Makefile** zur Kompilation hinzufügen.
- Sie können anhand einer Beschreibung Typen und Funktionen in den passenden Header Files deklarieren.
- Sie können **Makefile** Regeln schreiben.
Die Bewertung dieses Praktikums ist am Ende angegeben.
Erweitern Sie die vorgegebenen Code Gerüste, welche im **git** Repository **snp-lab-code** verfügbar sind.
___
## 3. Aufgabe 1: Modularisieren
![](./P04_Aufgabenstellung.png)
### 3.1 Teilaufgabe Modules einbinden, Header Files schreiben
- src/objects.h
- 2 Datenstukturen definieren
- `struct point` mit 2 double für x und y Koordinate
- `struct line` mit 2 point
- src/functions.h und .c
- 2 Funktionen deklarieren und definieren
- Berechnung der Länge `get_length`einer Linie (Annahme: Koordinaten sind alle positiv)
- l = sqrt(h^ 2 + b^ 2)
- ev. muss hier in den Anhang `#include <math.h>`
- Berechnung der Steigung `get_slope` der Linie gegenüber dem Koordinatensystem
- m = h / b
- tests vorgeben
- src/objects.h
- Include Guard
- Includes
- Struct für Punkt und Linie
- Include Guard
- src/functions.h
- Include Guard
- Includes
- Deklarationen der Funktionen für Berechnung der Länge und Steigung
- Include Guard
- src/functions.c
- Includes
- Definitionen der Funktionen für Berechnung der Länge und Steigung
- Include Guard
___
## 4. Aufgabe 2: Makefile Regeln
Makefile ergänzen, damit Modul `functions` korrekt eingebunden und kompiliert wird.
1. Kompilieren Sie das ganze mittels **make clean default**. Es sollten keine Compiler Fehler auftreten.
### 4.1 Neue Regeln hinzufügen
- Vorraussetzung: tab2svg.sh aus Praktikum 3 wird um die Möglichkeit erweitert eine Linie zu zeichnen (`line:x1:y1:x2:y2:color`)
- Studierende erstellen
- mind. 2 Files `long.line` und `short.line` mit 2 unterschiedlichen Linien
- Makefile Regeln um aus einem File `.line` ein File `.svg` mit Hilfe des Scripts zu erstellen
- PHONY Regel `display` um beide `.svg` mit Firefox darzustellen
- Vorgabe: sie sollen eine Variable für die Input-Dateien nutzen
Nachdem das Programm in Aufgabe 1 umgesetzt ist, geht es nun darum, im **Makefile** Regeln zu definieren welche die einzelnen Schritte von den Source Files zu den **png** Files ausführen.
Prüfen Sie schliesslich die Umsetzung mittels `make display`.
___
## 5. Aufgabe 3
- Studierende sollen Ausgabe von `make doc` analysieren und die Include Diagramme erklären können
```
make doc
firefox doc/index.html &
```
___
## 6. Bewertung
Die gegebenenfalls gestellten Theorieaufgaben und der funktionierende Programmcode müssen der Praktikumsbetreuung gezeigt werden. Die Lösungen müssen mündlich erklärt werden.
___
## 7. Erweiterung Doxyfile für Abhängigkeitsanalyse
```
--- /home/vagrant/huno/snp-new/snp/praktika/Shared/work/Doxyfile 2022-02-07 21:16:42.343302707 +0100
+++ /home/vagrant/snp/Doxyfile 2022-02-07 22:22:36.266839126 +0100
@@ -297,14 +297,14 @@
UML_LOOK = NO
UML_LIMIT_NUM_FIELDS = 10
TEMPLATE_RELATIONS = NO
-INCLUDE_GRAPH = NO
-INCLUDED_BY_GRAPH = NO
+INCLUDE_GRAPH = YES
+INCLUDED_BY_GRAPH = YES
CALL_GRAPH = NO
CALLER_GRAPH = NO
-GRAPHICAL_HIERARCHY = NO
-DIRECTORY_GRAPH = NO
+GRAPHICAL_HIERARCHY = YES
+DIRECTORY_GRAPH = YES
DOT_IMAGE_FORMAT = png
-INTERACTIVE_SVG = NO
+INTERACTIVE_SVG = YES
DOT_PATH =
DOTFILE_DIRS =
MSCFILE_DIRS =
```
Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

@@ -0,0 +1,88 @@
/*****************************************************************************
M. Thaler, Jan. 2000
Datei read.java
Funktion: Unsigned int Zahl via Bytestream von stdin einlesen.
Es wird zuerst eine Zeile eingelesen und dann konvertiert.
Die eingelesene Zeile darf nur eine Zahl enthalten
(mit optionalen Leerzeichen vor/nach der Zahl).
Returns: Die konvertierte Zahl
oder -1 (PARSE_ERROR) wenn keine Zahl oder zu gross
oder -2 (READ_ERROR) wenn Fehler beim einlesen.
Korrekturen: - Maerz 2002: M. Thaler, H. Fierz, Mar. 2002
liest bis EOL oder EOF, korrekter Rueckgabewert
- Sept 2016: A. Gieriet
Refactored (sprechende Variablen-Namen,
MS Windows Support, 0 Wert erlaubt,
Leerzeichen Support,
"magic" Numbers durch Symbole ersetzt,
maxResult Parameter, etc.)
******************************************************************************/
public class read {
public int getInt(int maxResult)
throws java.io.IOException
{
// end of input
int EOF = -1; // end of file
int EOL = 10; // end of line
// abnormal return values
int PARSE_ERROR = -1;
int READ_ERROR = -2;
// ASCII Codes
int ASCII_SPACE = 32; // ' '
int ASCII_DIGIT_0 = 48; // '0'
int ASCII_DIGIT_9 = 57; // '9'
// conversion buffer
int NO_POS = -1;
int BUFFERSIZE = 10;
byte[] buffer = new byte[BUFFERSIZE];
int result = 0;
// read line: up to EOL or EOF (i.e. error while reading)
int bytes = 0;
int input = System.in.read();
while ((input != EOL) && (input != EOF)) { // read whole line
if (bytes < BUFFERSIZE) { // only buffer first n characters
buffer[bytes] = (byte)input;
bytes++;
} else {
result = PARSE_ERROR; // exceed buffer size, continue read line
}
input = System.in.read();
}
if (input == EOF) {
result = READ_ERROR;
}
// check for numbers: skip leading and trailing spaces
// (i.e. this includes all control chars below the space ASCII code)
int pos = 0;
while((pos < bytes) && (buffer[pos] <= ASCII_SPACE)) pos++; // skip SP
int posOfFirstDigit = pos;
int posOfLastDigit = NO_POS;
while ((pos < bytes)
&& (buffer[pos] >= ASCII_DIGIT_0)
&& (buffer[pos] <= ASCII_DIGIT_9))
{
posOfLastDigit = pos;
pos++;
}
while((pos < bytes) && (buffer[pos] <= ASCII_SPACE)) pos++; // skip SP
// produce return value
if (result != 0) {
// previously detected read or parse error given
} else if ((pos != bytes) || (posOfLastDigit == NO_POS)) {
result = PARSE_ERROR;
} else { // convert number
for(int i = posOfFirstDigit; i <= posOfLastDigit; i++) {
result = result * 10 + (buffer[i] - ASCII_DIGIT_0);
if (result > maxResult) {
result = PARSE_ERROR;
break;
}
}
}
return result;
}
}
@@ -0,0 +1,32 @@
/*****************************************************************************
M. Thaler, Jan. 2000
Datei: rectang.java
Funktion: Bestimmt, ob Dreieck rechtwinklig ist.
Returns: true wenn rechtwinklig, sonst false.
Korrekturen: - Sept 2016, A. Gieriet
Refactored (sprechende Variablen Namen, etc.)
******************************************************************************/
public class rectang {
public boolean Rectangular(int a, int b, int c) {
int aS = a*a;
int bS = b*b;
int cS = c*c;
boolean isRightAngled;
if ((a == 0) && (b == 0) && (c == 0))
isRightAngled = false;
else if ((aS + bS) == cS)
isRightAngled = true;
else if ((aS + cS) == bS)
isRightAngled = true;
else if ((bS + cS) == aS)
isRightAngled = true;
else
isRightAngled = false;
return isRightAngled;
}
}
@@ -0,0 +1,73 @@
/*****************************************************************************
M. Thaler, Jan. 2000
Datei: triangle.java
Funktion: Die drei Seiten eines Dreiecks einlesen und bestimmen ob
das Dreieck rechtwinklig ist.
Returns: Nichts.
Korrekturen: - Maerz 2002, M. Thaler, H. Fierz
Abfrage bei unkorrekter Eingabe wiederholen
- Sept 2016, A. Gieriet
Refactored (sprechende Variablen-Namen,
"magic" Numbers durch Symbole ersetzt, etc.)
******************************************************************************/
class triangle {
public static void main(String[] args)
throws java.io.IOException
{
int READ_ERROR = -2;
int MAX_NUMBER = 1000;
read ReadInt = new read();
rectang Rect = new rectang();
while (true) {
System.out.println("\nDreiecksbestimmung (CTRL-C: Abbruch)\n");
int word = 0;
int a = 0;
int b = 0;
int c = 0;
do {
System.out.print("Seite a: ");
word = ReadInt.getInt(MAX_NUMBER);
}
while ((word < 0) && (word != READ_ERROR));
if (word >= 0)
a = word;
else
break;
do {
System.out.print("Seite b: ");
word = ReadInt.getInt(MAX_NUMBER);
}
while ((word < 0) && (word != READ_ERROR));
if (word >= 0)
b = word;
else
break;
do {
System.out.print("Seite c: ");
word = ReadInt.getInt(MAX_NUMBER);
}
while ((word < 0) && (word != READ_ERROR));
if (word >= 0)
c = word;
else
break;
if (Rect.Rectangular(a, b, c) == true)
System.out.println("-> Dreieck " + a + "-" + b + "-" + c
+ " ist rechtwinklig");
else
System.out.println("-> Dreieck " + a + "-" + b + "-" + c
+ " ist nicht rechtwinklig");
System.out.println("\n");
}
System.out.println("\n\nbye bye\n");
}
}
@@ -0,0 +1,16 @@
/* ----------------------------------------------------------------------------
* -- _____ ______ _____ -
* -- |_ _| | ____|/ ____| -
* -- | | _ __ | |__ | (___ Institute of Embedded Systems -
* -- | | | '_ \| __| \___ \ Zuercher Hochschule Winterthur -
* -- _| |_| | | | |____ ____) | (University of Applied Sciences) -
* -- |_____|_| |_|______|_____/ 8401 Winterthur, Switzerland -
* ----------------------------------------------------------------------------
*/
/**
* @file
* @brief Lab implementation
*/
// begin students to add code for task 4.1
// end students to add code
@@ -0,0 +1,16 @@
/* ----------------------------------------------------------------------------
* -- _____ ______ _____ -
* -- |_ _| | ____|/ ____| -
* -- | | _ __ | |__ | (___ Institute of Embedded Systems -
* -- | | | '_ \| __| \___ \ Zuercher Hochschule Winterthur -
* -- _| |_| | | | |____ ____) | (University of Applied Sciences) -
* -- |_____|_| |_|______|_____/ 8401 Winterthur, Switzerland -
* ----------------------------------------------------------------------------
*/
/**
* @file
* @brief Lab implementation
*/
// begin students to add code for task 4.1
// end students to add code
@@ -0,0 +1,16 @@
/* ----------------------------------------------------------------------------
* -- _____ ______ _____ -
* -- |_ _| | ____|/ ____| -
* -- | | _ __ | |__ | (___ Institute of Embedded Systems -
* -- | | | '_ \| __| \___ \ Zuercher Hochschule Winterthur -
* -- _| |_| | | | |____ ____) | (University of Applied Sciences) -
* -- |_____|_| |_|______|_____/ 8401 Winterthur, Switzerland -
* ----------------------------------------------------------------------------
*/
/**
* @file
* @brief Lab implementation
*/
// begin students to add code for task 4.1
// end students to add code
@@ -0,0 +1,16 @@
/* ----------------------------------------------------------------------------
* -- _____ ______ _____ -
* -- |_ _| | ____|/ ____| -
* -- | | _ __ | |__ | (___ Institute of Embedded Systems -
* -- | | | '_ \| __| \___ \ Zuercher Hochschule Winterthur -
* -- _| |_| | | | |____ ____) | (University of Applied Sciences) -
* -- |_____|_| |_|______|_____/ 8401 Winterthur, Switzerland -
* ----------------------------------------------------------------------------
*/
/**
* @file
* @brief Lab implementation
*/
// begin students to add code for task 4.1
// end students to add code
@@ -0,0 +1,16 @@
/* ----------------------------------------------------------------------------
* -- _____ ______ _____ -
* -- |_ _| | ____|/ ____| -
* -- | | _ __ | |__ | (___ Institute of Embedded Systems -
* -- | | | '_ \| __| \___ \ Zuercher Hochschule Winterthur -
* -- _| |_| | | | |____ ____) | (University of Applied Sciences) -
* -- |_____|_| |_|______|_____/ 8401 Winterthur, Switzerland -
* ----------------------------------------------------------------------------
*/
/**
* @file
* @brief Lab implementation
*/
// begin students to add code for task 4.2
// end students to add code
@@ -0,0 +1,34 @@
/* ----------------------------------------------------------------------------
* -- _____ ______ _____ -
* -- |_ _| | ____|/ ____| -
* -- | | _ __ | |__ | (___ Institute of Embedded Systems -
* -- | | | '_ \| __| \___ \ Zuercher Hochschule Winterthur -
* -- _| |_| | | | |____ ____) | (University of Applied Sciences) -
* -- |_____|_| |_|______|_____/ 8401 Winterthur, Switzerland -
* ----------------------------------------------------------------------------
*/
/**
* @file
* @brief Lab implementation
*/
#include <stdio.h>
#include <stdlib.h>
#include "read.h"
#include "rectang.h"
#include "trace.h"
/// max side length
#define MAX_NUMBER 1000
/**
* @brief Main entry point.
* @returns Returns EXIT_SUCCESS (=0) on success, EXIT_FAILURE (=1) on failure.
*/
int main(void)
{
// begin students to add code for task 4.1
// end students to add code
return EXIT_SUCCESS;
}
@@ -0,0 +1,12 @@
a
a
a
3
4444
4444
44444444444444
4
4
5
5
@@ -0,0 +1,12 @@
3
4
6
5
4
4
3
5
5
33
43
55
@@ -0,0 +1,12 @@
3
4
5
5
4
3
3
5
4
33
44
55
@@ -0,0 +1,205 @@
/* ----------------------------------------------------------------------------
* -- _____ ______ _____ -
* -- |_ _| | ____|/ ____| -
* -- | | _ __ | |__ | (___ Institute of Embedded Systems -
* -- | | | '_ \| __| \___ \ Zuercher Hochschule Winterthur -
* -- _| |_| | | | |____ ____) | (University of Applied Sciences) -
* -- |_____|_| |_|______|_____/ 8401 Winterthur, Switzerland -
* ----------------------------------------------------------------------------
*/
/**
* @file
* @brief Lab implementation
*/
#include <stdio.h>
#include <stdlib.h>
#include "CUnit/Basic.h"
#include "test_utils.h"
#ifndef TARGET // must be given by the make file --> see test target
#error missing TARGET define
#endif
/// @brief The name of the STDOUT text file.
#define OUTFILE "stdout.txt"
/// @brief The name of the STDERR text file.
#define ERRFILE "stderr.txt"
/// @brief The stimulus for the right-angled triangles
#define INFILE_RIGHT_ANGLED "stim-right-angled.input"
/// @brief The stimulus for the not right-angled triangles
#define INFILE_NOT_RIGHT_ANGLED "stim-not-right-angled.input"
/// @brief The stimulus for input errors
#define INFILE_ERROR "stim-error.input"
// setup & cleanup
static int setup(void)
{
remove_file_if_exists(OUTFILE);
remove_file_if_exists(ERRFILE);
return 0; // success
}
static int teardown(void)
{
// Do nothing.
// Especially: do not remove result files - they are removed in int setup(void) *before* running a test.
return 0; // success
}
// tests
static void test_right_angled(void)
{
// arrange
const char *out_txt[] = {
"\n",
"Dreiecksbestimmung (CTRL-C: Abbruch)\n",
"\n",
"Seite a: Seite b: Seite c: -> Dreieck 3-4-5 ist rechtwinklig\n",
"\n",
"\n",
"\n",
"Dreiecksbestimmung (CTRL-C: Abbruch)\n",
"\n",
"Seite a: Seite b: Seite c: -> Dreieck 5-4-3 ist rechtwinklig\n",
"\n",
"\n",
"\n",
"Dreiecksbestimmung (CTRL-C: Abbruch)\n",
"\n",
"Seite a: Seite b: Seite c: -> Dreieck 3-5-4 ist rechtwinklig\n",
"\n",
"\n",
"\n",
"Dreiecksbestimmung (CTRL-C: Abbruch)\n",
"\n",
"Seite a: Seite b: Seite c: -> Dreieck 33-44-55 ist rechtwinklig\n",
"\n",
"\n",
"\n",
"Dreiecksbestimmung (CTRL-C: Abbruch)\n",
"\n",
"Seite a: \n",
"\n",
"bye bye\n",
"\n",
};
// act
int exit_code = system(XSTR(TARGET) " 1>" OUTFILE " 2>" ERRFILE " <" INFILE_RIGHT_ANGLED);
// assert
CU_ASSERT_EQUAL(exit_code, 0);
assert_lines(OUTFILE, out_txt, sizeof(out_txt)/sizeof(*out_txt));
}
static void test_not_right_angled(void)
{
// arrange
const char *out_txt[] = {
"\n",
"Dreiecksbestimmung (CTRL-C: Abbruch)\n",
"\n",
"Seite a: Seite b: Seite c: -> Dreieck 3-4-6 ist nicht rechtwinklig\n",
"\n",
"\n",
"\n",
"Dreiecksbestimmung (CTRL-C: Abbruch)\n",
"\n",
"Seite a: Seite b: Seite c: -> Dreieck 5-4-4 ist nicht rechtwinklig\n",
"\n",
"\n",
"\n",
"Dreiecksbestimmung (CTRL-C: Abbruch)\n",
"\n",
"Seite a: Seite b: Seite c: -> Dreieck 3-5-5 ist nicht rechtwinklig\n",
"\n",
"\n",
"\n",
"Dreiecksbestimmung (CTRL-C: Abbruch)\n",
"\n",
"Seite a: Seite b: Seite c: -> Dreieck 33-43-55 ist nicht rechtwinklig\n",
"\n",
"\n",
"\n",
"Dreiecksbestimmung (CTRL-C: Abbruch)\n",
"\n",
"Seite a: \n",
"\n",
"bye bye\n",
"\n",
};
// act
int exit_code = system(XSTR(TARGET) " 1>" OUTFILE " 2>" ERRFILE " <" INFILE_NOT_RIGHT_ANGLED);
// assert
CU_ASSERT_EQUAL(exit_code, 0);
assert_lines(OUTFILE, out_txt, sizeof(out_txt)/sizeof(*out_txt));
}
static void test_trace(void)
{
// arrange
const char *err_txt[] = {
"TRACE: main()\n",
"TRACE: getInt(1000)\n",
"TRACE: getInt(1000)\n",
"TRACE: getInt(1000)\n",
"TRACE: rectangular(3, 4, 6)\n",
"TRACE: getInt(1000)\n",
"TRACE: getInt(1000)\n",
"TRACE: getInt(1000)\n",
"TRACE: rectangular(5, 4, 4)\n",
"TRACE: getInt(1000)\n",
"TRACE: getInt(1000)\n",
"TRACE: getInt(1000)\n",
"TRACE: rectangular(3, 5, 5)\n",
"TRACE: getInt(1000)\n",
"TRACE: getInt(1000)\n",
"TRACE: getInt(1000)\n",
"TRACE: rectangular(33, 43, 55)\n",
"TRACE: getInt(1000)\n",
};
// act
int exit_code = system(XSTR(TARGET) " 1>" OUTFILE " 2>" ERRFILE " <" INFILE_NOT_RIGHT_ANGLED);
// assert
CU_ASSERT_EQUAL(exit_code, 0);
assert_lines(ERRFILE, err_txt, sizeof(err_txt)/sizeof(*err_txt));
}
static void test_error(void)
{
// arrange
const char *out_txt[] = {
"\n",
"Dreiecksbestimmung (CTRL-C: Abbruch)\n",
"\n",
"Seite a: Seite a: Seite a: Seite a: Seite a: Seite b: Seite b: Seite b: Seite b: Seite b: Seite c: Seite c: -> Dreieck 3-4-5 ist rechtwinklig\n",
"\n",
"\n",
"\n",
"Dreiecksbestimmung (CTRL-C: Abbruch)\n",
"\n",
"Seite a: \n",
"\n",
"bye bye\n",
"\n",
};
// act
int exit_code = system(XSTR(TARGET) " 1>" OUTFILE " 2>" ERRFILE " <" INFILE_ERROR);
// assert
CU_ASSERT_EQUAL(exit_code, 0);
assert_lines(OUTFILE, out_txt, sizeof(out_txt)/sizeof(*out_txt));
}
/**
* @brief Registers and runs the tests.
*/
int main(void)
{
// setup, run, teardown
TestMainBasic("Triangle", setup, teardown
, test_right_angled
, test_not_right_angled
, test_trace
, test_error
);
}
@@ -0,0 +1,65 @@
SNP_SHARED_MAKEFILE := $(if $(SNP_SHARED_MAKEFILE),$(SNP_SHARED_MAKEFILE),"~/snp/shared.mk")
TARGET := bin/dep2dot
# Add all additional c-files to the SOURCES variable
# BEGIN-STUDENTS-TO-ADD-CODE
SOURCES := src/main.c
# END-STUDENTS-TO-ADD-CODE
TSTSOURCES := tests/tests.c
include $(SNP_SHARED_MAKEFILE)
# DEPFILES := ... define a list of png file names: %.c -> %.c.png
# BEGIN-STUDENTS-TO-ADD-CODE
# END-STUDENTS-TO-ADD-CODE
# define dep target as .PHONEY
# BEGIN-STUDENTS-TO-ADD-CODE
# BEGIN-STUDENTS-TO-ADD-CODE
# define dep target depending on FULLTARGET and DEPFILES above
# action: echo some text telling that the target is done using $@ - the echo command shall not be echoed before execution
# BEGIN-STUDENTS-TO-ADD-CODE
# BEGIN-STUDENTS-TO-ADD-CODE
# define new suffix rule for %.png depending on %.dot
# action: dot -Tpng $< >$@ || $(RM) $@
# BEGIN-STUDENTS-TO-ADD-CODE
# BEGIN-STUDENTS-TO-ADD-CODE
# define new suffix rule for %.dot depending on %.dep
# action: call $(TARGET) $(@:.dot=) <$< >$@ || $(RM) $@
# BEGIN-STUDENTS-TO-ADD-CODE
# BEGIN-STUDENTS-TO-ADD-CODE
# converts any .c file into a .c.dep file by means of GCC -H switch
# note: it removes intermediate files which were created as side effect
%.c.dep: %.c
$(COMPILE.c) -H -o $@.x $< 2>$@ && $(RM) $@.x $@.d
# cleanup all results, including the ones od creating the dependencies
dep-clean: clean
$(RM) $(DEPFILES) $(wildcard src/*.dep src/*.dot)
@@ -0,0 +1,8 @@
/**
* @mainpage SNP - P04 Modularisation
*
* @section Purpose
*
* This is a lab for splitting functionality into multiple modules.
*
*/
@@ -0,0 +1,149 @@
/**
* @file
* @brief Implementation of the dependency file access.
*/
#include "data.h"
#include "error.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libgen.h>
#include <assert.h>
#define MAX_PATH_LEN 512 ///< @brief Arbitrarily chosen maximum accepted path lenght.
#define MAX_LINE_LEN 512 ///< @brief Arbitrarily chosen maximum accepted line length
#define MAX_DIRS 64 ///< @brief Arbitrarily chosen maximum number of supported individual directories per dependency file.
#define MAX_FILES 256 ///< @brief Arbitrarily chosen maximum number of supported individual denendency entries.
/**
* @brief Declaration of POSIX (but not C99) function.
* @param s [IN] The string to duplicate on the heap memory.
* @return The duplicated string.
* @remark Since the Makefile calls gcc with -std=c99, non-C99 POSIX and GNU extensions are excluded - the glibc, though, provides the function to the linker.
*/
char *strdup(const char *s); // not stdc99, but available in the glibc
/**
* @brief Initialized the data structure before the data is to be read from th edependency file.
* @param data [INOUT] The address of the instance to initialize.
*/
static void init(data_t *data)
{
assert(data);
memset(data, 0, sizeof(data_t));
data->dirs = malloc(MAX_DIRS * sizeof(dir_t));
if (!data->dirs) FATAL("no memory left");
data->files = malloc(MAX_FILES * sizeof(file_t));
if (!data->files) FATAL("no memory left");
}
/**
* @brief Updates the directory list with the given data.
* @param data [INOUT] The instance to update.
* @param path [IN] The file path of a dependency entry as given by the dependency file.
* @return The index of the directory entry (either an existing matching one or a newly added one).
* @remark Extracts the directory part by means of dirname() from the given path and looks up an existing entry or adds a new one.
*/
static size_t get_or_add_dir(data_t *data, const char *path)
{
assert(data);
assert(path);
// The function dirname() gives no guarantee to not modify the parameter, therefore, need to produce a copy before calling dirname().
// Likewise, the returned value may refer to the passed paremater, therefore, a copy is made from the return value.
char *dup = strdup(path);
if (!dup) FATAL("no memory left");
char *name = strdup(dirname(dup));
if (!name) FATAL("no memory left");
free(dup);
// search for a matching entry...
size_t i = 0;
while(i < data->n_dirs && strcmp(data->dirs[i].name, name) != 0) {
i++;
}
if (i >= MAX_DIRS) FATAL("too many directories");
if (i == data->n_dirs) { // no match found: add
// handover the allocated name to the owning new directory entry
dir_t dir = { .name = name };
// append the new directory entry
data->dirs[data->n_dirs] = dir;
data->n_dirs++;
} else {
// release the name since match found, and therefore, no need to keep the allocated name anymore
free(name);
}
return i;
}
/**
* @brief Add a file entry from the dependency file to the data structure.
* @param data [INOUT] The data container instance.
* @param path [IN] The path of one file entry from the dependency file.
* @param level [IN] The dependency level of the file entry from the dependency file.
* @remark The sequence of entries in the dependency file is relevant - it implies direct dependencies.
*/
static void add_file(data_t *data, const char *path, size_t level)
{
assert(data);
assert(path);
// The function basename() gives no guarantee to not modify the parameter, therefore, need to produce a copy before calling basename().
// Likewise, the returned value may refer to the passed paremater, therefore, a copy is made from the return value.
char *dup = strdup(path);
if (!dup) FATAL("no memory left");
char *name = strdup(basename(dup));
if (!name) FATAL("no memory left");
free(dup);
if (data->n_files >= MAX_FILES) FATAL("too many files");
// produce a file entry
file_t file = { .name = name, .dir = get_or_add_dir(data, path), .level = level };
data->files[data->n_files] = file;
data->n_files++;
}
/**
* @brief Processes one dependency line of the dependency file.
* @param data [INOUT] The data container instance.
* @param line [IN] The line to parse and store in data.
*/
static void process_line(data_t *data, const char line[])
{
assert(data);
size_t len = strlen(line);
assert(len > 0);
assert(line[0] == '.');
// read level
size_t i = strspn(line, ".");
size_t level = i;
// skip spaces
i += strspn(line+i, " \t");
// take rest as path and add the file to the records
add_file(data, line+i, level);
}
/*
* The public interface.
*/
const data_t data_read_all(const char *root)
{
data_t data;
init(&data);
// add as first file the root for the given dependencies
add_file(&data, root, 0);
char line[MAX_LINE_LEN] = { 0 };
// read all stdin line and only process dependency lines (those starting on a '.')
clearerr(stdin);
while(fgets(line, MAX_LINE_LEN, stdin)) {
size_t len = strlen(line);
if (len > 0 && line[len-1] == '\n' && line[0] == '.') { // only dependency lines
line[len-1] = '\0';
process_line(&data, line);
}
}
return data;
}
@@ -0,0 +1,66 @@
/**
* @file
* @brief Access to the GCC produced dependency data (via gcc -H command line option).
*/
// begin of include guard
// BEGIN-STUDENTS-TO-ADD-CODE
// END-STUDENTS-TO-ADD-CODE
// includes which are needed in this header file
// BEGIN-STUDENTS-TO-ADD-CODE
// END-STUDENTS-TO-ADD-CODE
/**
* @brief Directory container for file entries of the dependency file.
*/
// BEGIN-STUDENTS-TO-ADD-CODE
// END-STUDENTS-TO-ADD-CODE
/**
* @brief File container for the file entries of the dependency file.
*/
// BEGIN-STUDENTS-TO-ADD-CODE
// END-STUDENTS-TO-ADD-CODE
/**
* @brief Overall container for all directories and all files from the dependency file.
*/
// BEGIN-STUDENTS-TO-ADD-CODE
// END-STUDENTS-TO-ADD-CODE
/**
* @brief Entry function to read the deendency data from stdin.
* @param root [IN] The name of the root file (the deoendency file does not mention the root file, so, it has to be passed from outside).
* @return The container of the read data from stdin. See the documentation on gcc -H for details on the dependencies, etc.
*/
// BEGIN-STUDENTS-TO-ADD-CODE
// END-STUDENTS-TO-ADD-CODE
// end of include guard
// BEGIN-STUDENTS-TO-ADD-CODE
// END-STUDENTS-TO-ADD-CODE
@@ -0,0 +1,17 @@
/**
* @file
* @brief Error handling convenience functions.
*/
#ifndef _ERROR_H_
#define _ERROR_H_
#include <stdio.h>
#include <stddef.h>
/**
* @brief Prints the message to stderr and terminates with EXIT_FAILURE.
* @param MSG [IN] The "..." *string literal* to emit as error - no format parameters nor variables supported.
*/
#define FATAL(MSG) do { fprintf(stderr, "ERROR: %s\n", MSG); exit(EXIT_FAILURE); } while(0)
#endif // _ERROR_H_
@@ -0,0 +1,36 @@
/* ----------------------------------------------------------------------------
* -- _____ ______ _____ -
* -- |_ _| | ____|/ ____| -
* -- | | _ __ | |__ | (___ Institute of Embedded Systems -
* -- | | | '_ \| __| \___ \ Zuercher Hochschule Winterthur -
* -- _| |_| | | | |____ ____) | (University of Applied Sciences) -
* -- |_____|_| |_|______|_____/ 8401 Winterthur, Switzerland -
* ----------------------------------------------------------------------------
*/
/**
* @file
* @brief Lab P04 dep2dot
*/
#include <stdio.h>
#include <stdlib.h>
#include "error.h"
#include "data.h"
#include "output.h"
/**
* @brief main function
* @param argc [in] number of entries in argv
* @param argv [in] program name plus command line arguments
* @returns returns success if valid date is given, failure otherwise
* @remark Prerequisit to convert the resulting DOT file on the shell: sodo apt install graphviz
* @remark Convert: gcc -H ... file.c ... 2>file.dep ; dep2dot file.c <file.dep >file.dot && dot -Tpng file.dot >file.png
*/
int main(int argc, const char *argv[])
{
if (argc < 2) FATAL("missing arguments\nusage: dep2dot file.c <file.dep >file.dot # from gcc -H ... file.c ... 2>file.dep\n");
output_dot(data_read_all(argv[1]));
return EXIT_SUCCESS;
}
@@ -0,0 +1,100 @@
/**
* @file
* @brief Provides output functions for various file formats.
*/
#include "output.h"
#include <stdio.h>
#include <assert.h>
#include <string.h>
/**
* @brief Writes the node name of the given file.
* @param file [IN] The file for which to write the node name.
* @remark The dependency data contain duplicates of file entries - the node name must be unique for the path and the *basename* of the files.
*/
static void print_node(file_t file)
{
printf("\"%s (cluster_c%zd)\"", file.name, file.dir);
}
/**
* @brief Recursively writes the individual direct dependencies for the file given by curr.
* @param files [IN] The array of all files - the sequence is relevant.
* @param len [IN] The lenght of the files array, i.e. the upper limit for curr values and the subsequent index values.
* @param curr [IN] The index into files for the current root for dependencies: curr -> x, curr -> y, ...
* @return Returns the index into files for the next file to process (i.e. curr value for the next call to this function).
* @remark For a given *curr* file, all following files are with depth level + 1 are direct include files.
* @remark All files with a higher level are *indirect* include files, thus *direct* includes from files processed by recursive calls.
* @remark The list of direct includes to the *curr* file terminates with a level equal of less the the *curr* one (or when the list is exchausted).
*/
static size_t dependencies(file_t files[], size_t len, size_t curr)
{
assert(curr < len);
size_t level = files[curr].level;
size_t file = curr + 1;
while(file < len && files[file].level > level) {
if (files[file].level == level + 1) {
// Write to stdout " file -> include;\n" where file and include are the DOT node names of the respective files
// BEGIN-STUDENTS-TO-ADD-CODE
// END-STUDENTS-TO-ADD-CODE
file = dependencies(files, len, file);
} else {
file++;
}
}
return file;
}
/*
* Public interface
*/
void output_dot(const data_t data)
{
printf("digraph dep {\n");
// nodes
printf(" node [shape=box]\n");
for (size_t file = 0; file < data.n_files; file++) {
// Write to stdout " file [label=\"name\"];\n" where file is the DOT node name and name is the file name
// BEGIN-STUDENTS-TO-ADD-CODE
// END-STUDENTS-TO-ADD-CODE
}
// directory clusters
for (size_t dir = 0; dir < data.n_dirs; dir++) {
printf(" subgraph cluster_c%zd {\n", dir);
printf(" label=\"%s\"; %s\n", data.dirs[dir].name, strncmp(data.dirs[dir].name, "/usr/", 5) == 0 ? "style=filled; color=lightgrey;" : "color=black;");
for (size_t file = 0; file < data.n_files; file++) {
if (data.files[file].dir == dir) {
// Write to stdout " file;\n" where file is the DOT node name
// BEGIN-STUDENTS-TO-ADD-CODE
// END-STUDENTS-TO-ADD-CODE
}
}
printf(" }\n");
}
// dependencies
size_t curr = 0;
do {
curr = dependencies(data.files, data.n_files, curr);
} while(curr < data.n_files);
printf("}\n");
}
@@ -0,0 +1,12 @@
/**
* @file
* @brief Provides output functions for various file formats.
*/
// define proper header file here, with include gaurd, etc.
// BEGIN-STUDENTS-TO-ADD-CODE
// END-STUDENTS-TO-ADD-CODE
@@ -0,0 +1,7 @@
Test File
. dir1/h1_1
.. dir1/h1_1_2
. dir1/h1_2
. dir2/h2_1
.. dir1/h1_1
Done
@@ -0,0 +1,2 @@
Test File
Done
@@ -0,0 +1,140 @@
/* ----------------------------------------------------------------------------
* -- _____ ______ _____ -
* -- |_ _| | ____|/ ____| -
* -- | | _ __ | |__ | (___ Institute of Embedded Systems -
* -- | | | '_ \| __| \___ \ Zuercher Hochschule Winterthur -
* -- _| |_| | | | |____ ____) | (University of Applied Sciences) -
* -- |_____|_| |_|______|_____/ 8401 Winterthur, Switzerland -
* ----------------------------------------------------------------------------
*/
/**
* @file
* @brief Test suite for the given package.
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <time.h>
#include <assert.h>
#include <CUnit/Basic.h>
#include "test_utils.h"
#ifndef TARGET // must be given by the make file --> see test target
#error missing TARGET define
#endif
/// @brief alias for EXIT_SUCCESS
#define OK EXIT_SUCCESS
/// @brief alias for EXIT_FAILURE
#define FAIL EXIT_FAILURE
/// @brief The name of the STDOUT text file.
#define OUTFILE "stdout.txt"
/// @brief The name of the STDERR text file.
#define ERRFILE "stderr.txt"
/// @brief test data file
#define IN_NO_DEP "no_dep.input"
/// @brief test data file
#define IN_DEP "dep.input"
// setup & cleanup
static int setup(void)
{
remove_file_if_exists(OUTFILE);
remove_file_if_exists(ERRFILE);
return 0; // success
}
static int teardown(void)
{
// Do nothing.
// Especially: do not remove result files - they are removed in int setup(void) *before* running a test.
return 0; // success
}
// tests
static void test_fail_no_arg(void)
{
// arrange & act & assert
CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " >" OUTFILE " 2>" ERRFILE)), FAIL);
}
static void test_no_dep(void)
{
// arrange
const char *out_txt[] = {
"digraph dep {\n",
" node [shape=box]\n",
" \"root (cluster_c0)\" [label=\"root\"];\n",
" subgraph cluster_c0 {\n",
" label=\".\"; color=black;\n",
" \"root (cluster_c0)\";\n",
" }\n",
"}\n",
};
// act & assert
CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " root <" IN_NO_DEP " >" OUTFILE " 2>" ERRFILE)), OK);
// assert
assert_lines(OUTFILE, out_txt, sizeof(out_txt)/sizeof(*out_txt));
}
static void test_dep(void)
{
// arrange
const char *out_txt[] = {
"digraph dep {\n",
" node [shape=box]\n",
" \"root (cluster_c0)\" [label=\"root\"];\n",
" \"h1_1 (cluster_c1)\" [label=\"h1_1\"];\n",
" \"h1_1_2 (cluster_c1)\" [label=\"h1_1_2\"];\n",
" \"h1_2 (cluster_c1)\" [label=\"h1_2\"];\n",
" \"h2_1 (cluster_c2)\" [label=\"h2_1\"];\n",
" \"h1_1 (cluster_c1)\" [label=\"h1_1\"];\n",
" subgraph cluster_c0 {\n",
" label=\".\"; color=black;\n",
" \"root (cluster_c0)\";\n",
" }\n",
" subgraph cluster_c1 {\n",
" label=\"dir1\"; color=black;\n",
" \"h1_1 (cluster_c1)\";\n",
" \"h1_1_2 (cluster_c1)\";\n",
" \"h1_2 (cluster_c1)\";\n",
" \"h1_1 (cluster_c1)\";\n",
" }\n",
" subgraph cluster_c2 {\n",
" label=\"dir2\"; color=black;\n",
" \"h2_1 (cluster_c2)\";\n",
" }\n",
" \"root (cluster_c0)\" -> \"h1_1 (cluster_c1)\";\n",
" \"h1_1 (cluster_c1)\" -> \"h1_1_2 (cluster_c1)\";\n",
" \"root (cluster_c0)\" -> \"h1_2 (cluster_c1)\";\n",
" \"root (cluster_c0)\" -> \"h2_1 (cluster_c2)\";\n",
" \"h2_1 (cluster_c2)\" -> \"h1_1 (cluster_c1)\";\n",
"}\n",
};
// act & assert
CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " root <" IN_DEP " >" OUTFILE " 2>" ERRFILE)), OK);
// assert
assert_lines(OUTFILE, out_txt, sizeof(out_txt)/sizeof(*out_txt));
}
/**
* @brief Registers and runs the tests.
* @returns success (0) or one of the CU_ErrorCode (>0)
*/
int main(void)
{
// setup, run, teardown
TestMainBasic("lab test", setup, teardown
, test_fail_no_arg
, test_no_dep
, test_dep
);
}