Browse Source

New task2 description

Michael Mächtel 8 years ago
parent
commit
e4b90b79ae
3 changed files with 226 additions and 0 deletions
  1. 226
    0
      hw6/task2/README.md
  2. BIN
      hw6/task2/png/Loop.png
  3. BIN
      hw6/task2/png/shell_module.png

+ 226
- 0
hw6/task2/README.md View File

@@ -0,0 +1,226 @@
1
+# README hw6-t2
2
+- [Einleitung](#einleitung)
3
+- [Implementierung](#implementierung)
4
+    - [*Shell* Datenstruktur](#shell-datenstruktur)
5
+        - [Public Methode *new()*](#public-methode-new)
6
+        - [Public Methode *start() -> Result*](#public-methode-start---result)
7
+        - [Private Methode *shell_loop() -> Result*](#private-methode-shellloop---result)
8
+        - [Private Methode *prompt() -> Result<Option<String>, ...>*](#private-methode-prompt---resultoptionstring)
9
+        - [Private Methode *run()*](#private-methode-run)
10
+    - ['Command Datenstruktur'](#command-datenstruktur)
11
+- [Externe Crates](#externe-crates)
12
+- [Module und Tests](#module-und-tests)
13
+- [Dokumentation](#dokumentation)
14
+- [ALT](#alt)
15
+    - [*Command* Datenstruktur](#command-datenstruktur)
16
+
17
+## Einleitung
18
+
19
+In dieser Aufgabe werden Sie beginnen, eine einfache, aber robuste Shell zu erstellen. In den nächsten Aufgaben werden Sie diese Shell dann weiter ausbauen.
20
+
21
+Dafür werden zu Beginn 2 Objekte (Structs) benötigt:
22
+
23
+- Shell
24
+- Command
25
+
26
+Die Unit Tests dieser Objekte schreiben Sie bitte wieder in eigene Dateien, siehe folgendes Diagramm:
27
+
28
+![Module](png/shell_module.png)
29
+
30
+Die Methoden der Datenstruktur Shell beinhalten das Setup und die Loop einer Shell. In der Loop wird der Prompt ausgegeben und auf die Eingaben des Benutzers gewartet. Schließt der Benutzer seine Eingabe mit RETURN ab, so wertet eine Methode die Eingabezeile aus und erstellt daraus die vom Benutzer eingegeben Kommandos. Diese Loop kann der Benutzer mit dem Kommando `exit` beenden.
31
+
32
+Das Handling der Kommandos übernehmen die Methoden der Datenstruktur Command. Beachten Sie, Ihre derzeitige Shell, mit der Sie selbst im Labor arbeiten (labshell), wird nicht einfach beendet wenn ein Kommando fehlerhaft ist oder nicht ausgeführt werden kann. Ebenso soll Ihre Shell nur bei dem Kommando `exit` beendet werden.
33
+
34
+Die main() Funktion selbst hat somit wenig zu tun, sie muss lediglich eine Instanz des Datentyps Shell starten und das Ergebnis auswerten. Ist das Ergebnis 'Ok' so wird das Programm mit dem Exitcode 0 beendet. Tritt intern bei der Benutzung einer Funktionalität Ihrer Shell ein Fehler auf, welcher nicht von Ihnen behandelt werden kann, so wird die Shell mit dem Exitcode 1 beendet.
35
+
36
+```Rust
37
+...
38
+let mut s = Shell::new(..);
39
+    match s.start() {
40
+        Ok(_) => process::exit(0),
41
+        Err(_) => process::exit(1),
42
+    }
43
+...
44
+```
45
+
46
+## Implementierung
47
+
48
+### *Shell* Datenstruktur
49
+
50
+Die Datenstruktur 'Shell' hat folgende Felder:
51
+
52
+```Rust
53
+struct Shell<R, W> {
54
+    pub reader: R,
55
+    pub writer: W,
56
+    pub should_exit: bool,
57
+    pub name: String,
58
+}
59
+```
60
+
61
+'reader' ist der Input Kanal Ihrer Shell und 'writer' der Output Kanal. Das Flag 'should_exit' signalisiert Ihrer Loop (siehe unten), dass die Loop beendet werden soll. 'name' ist der Name der Shell, den diese immer am Anfang des Prompts ausgibt, damit diese sich von Ihrer normalen Shell unterscheidet.
62
+
63
+Für Ihre Implementierung verwenden Sie folgende Traitbounds:
64
+
65
+- R: BufRead
66
+- W: Write
67
+
68
+Um das Verhalten einer echten Shell zu erhalten benötigen Sie für den Reader die Standard-Eingabe und für den Writer die Standard-Ausgabe.
69
+
70
+In den Unit Tests werden andere Typen für R und W benutzt. Schauen Sie sich dazu die Tests in `unit_tests_shell.rs` an. Diese sollen Ihnen als Beispiel für eigene Tests dienen.
71
+
72
+#### Public Methode *new()*
73
+
74
+```Rust
75
+pub fn new(input: R, output: W, name: String) -> Self
76
+```
77
+
78
+Eine Instanz der Shell wird erstellt.
79
+
80
+#### Public Methode *start() -> Result*
81
+
82
+Die public Methode *start()* ruft die private Methode *shell_loop()* auf. Die Funktion *start()* ist für evtl. spätere Erweiterungen gedacht, wenn in der Shell vor dem Starten der Loop noch weitere Initialisierungsarbeiten durchzuführen sind.
83
+
84
+#### Private Methode *shell_loop() -> Result*
85
+
86
+Die Basis Loop einer Shell haben wir bereits in der Vorlesung besprochen. Die Loop wartet auf Eingaben des Benutzers, die dieser mit RETURN abschließt. Die Eingaben werden dann ausgewertet und das Kommando, das sich u.U. daraus ergibt ausgeführt. Danach steht die Loop wieder für Eingaben bereit.
87
+
88
+![Loop](png/loop.png)
89
+
90
+#### Private Methode *prompt() -> Result<Option<String>, ...>*
91
+
92
+Die Funktion gibt den Namen der Shell, den aktuellen Pfad (siehe `std::env`) sowie das ' >' Zeichen, gefolgt von einem Leerzeichen aus:
93
+
94
+```text
95
+bsys-shell /Users/username >
96
+```
97
+
98
+Nach der Ausgabe wartet die Funktion auf Eingaben des Benutzers.
99
+
100
+Benutzen Sie zum Einlesen die *read_line()* Funktion. Liefert *read_line()* kein Zeichen, so gibt die Funktion None zurück. Werden Zeichen von *read_line()* eingelesen, so liefert die Funktion die Eingabe des Benutzers zurück.
101
+
102
+> Damit bei einem *write!()* Aufruf auf der Konsole auch der String erscheint, muss nach dem *write!()* Aufruf ein *flush()* Aufruf folgen
103
+
104
+Wie am Funktionskopf zu erkennen ist, liefert diese Funktion im Erfolgsfall den eingelesenen String zurück.
105
+
106
+#### Private Methode *run()*
107
+
108
+Diese Methode wird von *shell_loop()* aufgerufen, wenn der Benutzer Eingaben gemacht hat, und daraus ein Kommando geparst werden konnte. Das eigentliche Parsen des Strings geschieht im 'Command' Modul. Das Modul liefert nach einem erfolgreichen Parse-Vorgang ein Kommando zurück, welches dann in run() aufgerufen wird.
109
+
110
+Sowohl die Funktion zum Parsen des Strings, als auch die Methoden um die eigentlichen Kommandos auszuführen, platzieren Sie bitte in die Datei `command.rs`.
111
+
112
+### 'Command Datenstruktur'
113
+
114
+Die Datenstruktur 'Command' in `command.rs` hat zunächst folgende Felder:
115
+
116
+```Rust
117
+enum Command {
118
+    Empty,
119
+    Exit,
120
+    Cd(Option<OsString>),
121
+}
122
+```
123
+
124
+Die Kommandos bedeuten:
125
+
126
+- 'Empty': Keine Eingabe, z.B. hat der Benutzer nur Return, Leerzeichen oder andere Whitespaces getippt.
127
+- 'Exit': Dieses Kommando bedeutet, dass sich die Shell vor dem Start der nächsten Loop beendet.
128
+- 'Cd': ein Change Directory Befehl soll ausgeführt werden. *Option* enthält den Pfad zu welchem gewechselt werden soll.
129
+
130
+
131
+Im Modul `command.rs` wird die Trait Methode `FromStr` bereit gestellt, so dass das Objekt Command dieses Trait unterstützt.
132
+
133
+```Rust
134
+...
135
+fn from_str(s: &str) -> Result<Command, .....> {
136
+```
137
+
138
+Diese Funktion können Sie benutzen, um sich den Input des Users in der Shell analysieren zu lassen. Als Rückgabewert erhalten Sie den entsprechenden `Command`. Somit können Sie im Modul `shell.rs` komfortabel über folgenden Aufruf den Input String in ein Kommando wandeln lassen:
139
+
140
+```Rust
141
+Command::from_str(&line).and_then(|cmd| self.run(cmd))
142
+```
143
+
144
+In der obigen Trait Methode können die einfachen Kommandos wie:
145
+
146
+- Empty und
147
+- Exit
148
+
149
+direkt - je nach Input des User - zurück gegeben werden. Für komplexere Funktionen wie **cd** bietet es sich an, in der Trait Methode spezifische Methoden des Datentyps Command aufzurufen. Für jedes Kommando sind in der command.rs zwei Methoden zu implementieren:
150
+
151
+- *parse_<command>()*
152
+- *exec_<command>()*
153
+
154
+Für das **cd** Kommando somit:
155
+
156
+- *parse_cd()*
157
+- *exec_cd()*
158
+
159
+Die *parse_command()* Methode wird im *from_str()* Trait aufgerufen. Die *exec_<commnad>()>* Methode wird aus dem shell Modul an geeigneter Stelle aufgerufen.
160
+
161
+> Ihr **cd** Kommando sollte sich so verhalten wie das **cd** Kommando der labshell. Somit kommen Sie beim Aufruf von **cd** ohne Parameter in Ihr Home Verzeichnis (siehe env::var_os("HOME")). Mit **cd ..** in das Verzeichnis 'darüber' usw.
162
+
163
+
164
+## Externe Crates
165
+
166
+Benutzen Sie für Ihre Implementierung nur die externe Crate `nix`.
167
+
168
+## Module und Tests
169
+
170
+Ob und wie Sie den Code in weitere Module aufteilen wollen ist Ihnen überlassen. Schreiben Sie jedoch Ihre Unit-Tests in der Datei `unit_test_shell.rs` oder als eigenständigen Test, der von 'cargo test' aufgerufen wird, siehe auch [Testing][]. Einfache Tests können auch direkt in die Dokumentation 'codiert' werden, siehe [Documentation Tests][].
171
+
172
+Wichtig: Erstellen Sie ausreichend Unit Tests, um möglichst alle Methoden aus `shell.rs` und `command.rs` ausreichend testen zu können.
173
+
174
+## Dokumentation
175
+
176
+Bei dieser Aufgabe ist Ihre Dokumentation wichtig, um Ihren Programmablauf nachvollziehen zu können. Bitte dokumentieren Sie Ihre Funktionen entsprechend umfangreicher und kommentieren Sie spezielle Kniffe im Code, die Sie verwendet haben.
177
+
178
+[Testing]: https://doc.rust-lang.org/book/testing.html
179
+[Documentation Tests]: https://doc.rust-lang.org/book/testing.html#documentation-tests
180
+
181
+
182
+
183
+
184
+___
185
+
186
+## ALT
187
+
188
+Da Sie alle Zeicheneingaben, außer Whitespaces (Leerzeichen, TAB usw.) und `exit` als Kommandoaufruf interpretieren, wird sich Ihre Shell beim Aufruf Ihrer Programme genauso verhalten wie die normale Shell. Die zur Prozessverwaltung verwendeten Sonderzeichen wie &, | usw. werden in dieser Aufgabe nicht behandelt. Somit wird immer nur ein Programm inklusive aller angegebenen Parameter aufgerufen, auf dessen Ende die Shell wartet, um dann wieder den Prompt auszugeben und auf neue Eingaben zu warten.
189
+
190
+```text
191
+bsys-shell /Users/maechtel$ ps 1
192
+  PID   TT  STAT      TIME COMMAND
193
+    1   ??  Ss    28:42.16 /sbin/launchd
194
+bsys-shell /Users/maechtel$ cat blabla.txt
195
+cat: blabla.txt: No such file or directory
196
+bsys-shell /Users/maechtel$ cat testdatei.txt
197
+Hello you snoopy user ....
198
+bsys-shell /Users/maechtel$ file testdatei.txt
199
+testdatei.txt: ASCII text
200
+bsys-shell /Users/maechtel$ exit
201
+exit
202
+```
203
+
204
+### *Command* Datenstruktur
205
+
206
+
207
+```Rust
208
+#[derive(Debug)]
209
+pub enum Command {
210
+    /// Execute a command with arguments.
211
+    Exec { prog: CString, argv: Vec<CString> },
212
+    /// Empty command.
213
+    Empty,
214
+    /// Exit.
215
+    Exit,
216
+}
217
+```
218
+
219
+- 'Exec': Die Eingabe wird als Aufruf eines Programms, inkl. Parameter, ausgewertet und es wird versucht, das zugehörige Programm zu starten (mit *execvp()*). Wird das Programm nicht gefunden, so wird eine Fehlermeldung ausgegeben.
220
+- 'Empty': Keine Eingabe, z.B. hat der Benutzer nur Return, Leerzeichen oder andere Whitespaces getippt.
221
+- 'Exit': Dieses Kommando bedeutet, dass sich die Shell vor dem Start der nächsten Loop beendet.
222
+
223
+Mehr Kommandos kommen in der optionalen Aufgabe Task3 dazu.
224
+
225
+
226
+Die Funktion `parse_exec` enthält den generischen Typen I mit dem Trait Bound Iterator. Das bedeutet, die Funktion verlangt von dem generischen Typen I, dass er vom Typ Iterator mit Items auf `&str` ist. Somit können Sie in der Funktion über den Parameter `args` iterieren. Bei diesen Arten von Funktionen mit Iteratoren müssen typischerweise zusätzlich Lifetime Parameter (`a) mit angegeben werden. Auf dieses Thema kommen wir noch in den nächsten Wochen zu sprechen.

BIN
hw6/task2/png/Loop.png View File


BIN
hw6/task2/png/shell_module.png View File


Loading…
Cancel
Save