Unser hex-Programm wird nützlicher, wenn es die Dateinamen der Ein- und Ausgabedatei über die Kommandozeile einlesen kann, d.h., wenn es Kommandozeilenparameter verarbeiten kann. Aber... Wo sind die?
Bevor ein UNIX®-System ein Programm ausführt, legt es
einige Daten auf dem Stack ab (push
) und springt dann an das
_start
-Label des Programms. Ja, ich sagte
springen, nicht aufrufen. Das bedeutet, dass auf die Daten
zugegriffen werden kann, indem [esp+offset]
ausgelesen wird oder die Daten einfach vom Stack genommen werden
(pop
).
Der Wert ganz oben auf dem Stack enthält die Zahl der
Kommandozeilenparameter. Er wird traditionell
argc
wie "argument count" genannt.
Die Kommandozeilenparameter folgen einander, alle
argc
. Von diesen wird üblicherweise als
argv
wie "argument value(s)" gesprochen. So
erhalten wir argv[0]
,
argv[1]
, ...
und
argv[argc-1]
. Dies sind nicht die eigentlichen
Parameter, sondern Zeiger (Pointer) auf diese, d.h.,
Speicheradressen der tatsächlichen Parameter. Die Parameter
selbst sind durch NULL beendete Zeichenketten.
Der argv
-Liste folgt ein NULL-Zeiger, was
einfach eine 0
ist. Es gibt noch mehr, aber
dies ist erst einmal genug für unsere Zwecke.
Falls Sie von der MS-DOS®-Programmierumgebung kommen, ist der größte Unterschied die Tatsache, dass jeder Parameter eine separate Zeichenkette ist. Der zweite Unterschied ist, dass es praktisch keine Grenze gibt, wie viele Parameter vorhanden sein können.
Ausgerüstet mit diesen Kenntnissen, sind wir beinahe
bereit für eine weitere Version von
hex.asm
. Zuerst müssen wir jedoch
noch ein paar Zeilen zu system.inc
hinzufügen:
Erstens benötigen wir zwei neue Einträge in unserer Liste mit den Systemaufrufnummern:
Zweitens fügen wir zwei neue Makros am Ende der Datei ein:
Und hier ist schließlich unser veränderter Quelltext:
In unserem .data
-Abschnitt befinden
sich nun die zwei neuen Variablen fd.in
und
fd.out
. Hier legen wir die Dateideskriptoren
der Ein- und Ausgabedatei ab.
Im .text
-Abschnitt haben wir die
Verweise auf stdin
und
stdout
durch [fd.in]
und
[fd.out]
ersetzt.
Der .text
-Abschnitt beginnt nun mit
einer einfachen Fehlerbehandlung, welche nur das Programm mit
einem Rückgabewert von 1
beendet. Die
Fehlerbehandlung befindet sich vor _start
,
sodass wir in geringer Entfernung von der Stelle sind, an der
der Fehler auftritt.
Selbstverständlich beginnt die
Programmausführung immer noch bei
_start
. Zuerst entfernen wir
argc
und argv[0]
vom
Stack: Sie sind für uns nicht von Interesse (sprich, in
diesem Programm).
Wir nehmen argv[1]
vom Stack und legen
es in ECX
ab. Dieses Register
ist besonders für Zeiger geeignet, da wir mit jecxz
NULL-Zeiger verarbeiten
können. Falls argv[1]
nicht NULL ist,
versuchen wir, die Datei zu öffnen, die der erste Parameter
festlegt. Andernfalls fahren wir mit dem Programm fort wie
vorher: Lesen von stdin
und Schreiben nach
stdout
. Falls wir die Eingabedatei nicht
öffnen können (z.B. sie ist nicht vorhanden), springen
wir zur Fehlerbehandlung und beenden das Programm.
Falls es keine Probleme gibt, sehen wir nun nach dem
zweiten Parameter. Falls er vorhanden ist, öffnen wir die
Ausgabedatei. Andernfalls schreiben wir die Ausgabe nach
stdout
. Falls wir die Ausgabedatei nicht
öffnen können (z.B. sie ist zwar vorhanden, aber wir
haben keine Schreibberechtigung), springen wir auch wieder in
die Fehlerbehandlung.
Der Rest des Codes ist derselbe wie vorher, außer
dem Schließen der Ein- und Ausgabedatei vor dem Verlassen
des Programms und, wie bereits erwähnt, die Benutzung von
[fd.in]
und
[fd.out]
.
Unsere Binärdatei ist nun kolossale 768 Bytes groß.
Können wir das Programm immer noch verbessern? Natürlich! Jedes Programm kann verbessert werden. Hier finden sich einige Ideen, was wir tun könnten:
Die Fehlerbehandlung eine Warnung auf
stderr
ausgeben lassen.
Den Lese
- und
Schreib
funkionen eine Fehlerbehandlung
hinzufügen.
Schließen von stdin
, sobald wir
eine Eingabedatei öffnen, von stdout
,
sobald wir eine Ausgabedatei öffnen.
Hinzufügen von Kommandozeilenschaltern wie zum
Beispiel -i
und
-o
, sodass wir die Ein- und
Ausgabedatei in irgendeiner Reihenfolge angeben oder
vielleicht von stdin
lesen und in eine
Datei schreiben können.
Ausgeben einer Gebrauchsanweisung, falls die Kommandozeilenparameter fehlerhaft sind.
Ich beabsichtige, diese Verbesserungen dem Leser als Übung zu hinterlassen: Sie wissen bereits alles, das Sie wissen müssen, um die Verbesserungen durchzuführen.
Wenn Sie Fragen zu FreeBSD haben, schicken Sie eine E-Mail an
<de-bsd-questions@de.FreeBSD.org>.
Wenn Sie Fragen zu dieser Dokumentation haben, schicken Sie eine E-Mail an
<de-bsd-translators@de.FreeBSD.org>.