Teraz zajmiemy się dość istotną sprawą z punktu widzenia programisty i użytkownika oprogramowania: linią poleceń. Nie wszyscy lubią podawać dane programowi w czasie jego pracy i odpowiadać na pytania o dane. Często (o ile jest to możliwe) można tego oszczędzić i zamiast bezustannie zadawać użytkownikowi pytania, przeczytać, co wpisano nam w linię poleceń. Umożliwia to pisanie programów, które raz uruchomione z prawidłową linią poleceń nie pytają już się o nic a tylko wykonują swoją pracę bez przeszkadzania użytkownikom.
Przejdźmy więc do szczegółów. Jeśli ktoś z Was zna język C, to na pewno wie, jak zadeklarować funkcję główną programu tak, aby mogła odczytać parametry i zmienne środowiska. Deklaracja taka wygląda zazwyczaj tak:
int main (int argc, char *argv[], char *env[])
gdzie:
argc - liczba całkowita mówiąca o tym, z jaką ilością parametrów uruchomiono
nasz program.
char *argv[] -
tablica wskaźników do poszczególnych parametrów. Tutaj,
argv[0] - nazwa uruchomionego programu, argv[1] - pierwszy
parametr programu itd.
char *env[] - tablica wskaźników do zmiennych środowiskowych.
Ale gdzie są te zmienne?
Na stosie, oczywiście!
Po wykonaniu typowego prologu do funkcji (czyli push ebp / mov ebp, esp),
zmienna argc
znajduje
się w [ebp+4], wskaźniki do parametrów linii poleceń zaczynają się od [ebp+8]
i idą w górę stosu, po nich jest
wskaźnik zerowy i dalej w górę są wskaźniki do zmiennych środowiska,
też zakończone wskaźnikiem zerowym.
Wszystko ładnie wygląda w teorii, ale jak tego używać?
Aby odpowiedzieć na to pytanie, napisałem ten oto krótki programik. Jedynym celem jego życia
jest wyświetlenie, z iloma argumentami go wywołano (co najmniej jeden - nazwa programu),
wyświetlenie tych argumentów i zmiennych środowiska.
A teraz kod:
; Program wyświetla własną linię poleceń i zmienne środowiskowe.
;
; Autor: Bogdan D.
; kontakt: bogdandr (at) op (dot) pl
;
; nasm -O999 -f elf liniap.asm
; ld -s -o liniap liniap.o bibl/lib/libasmio.a
;
; fasm liniap.asm liniap.o
; ld -s -o liniap liniap.o bibl/lib/libasmio.a
; przyda się nam moja biblioteczka
%include "bibl/incl/linuxbsd/nasm/std_bibl.inc"
section .text
global _start
; FASM:
; format ELF
; include "bibl/incl/linuxbsd/fasm/std_bibl.inc"
; section ".text" executable
; public _start
_start:
push ebp ; typowy prolog, o którym wspomniałem
mov ebp, esp
%idefine argc ebp+4 ; liczba parametrów
%idefine argv ebp+8 ; parametry
; FASM:
; argc equ ebp+4 ; liczba parametrów
; argv equ ebp+8 ; parametry
mov eax, [argc] ; EAX = liczba parametrów
pisz32e ; wypisz EAX
nwln ; przejdź do nowej linii
xor edi, edi ; zerujemy licznik
; wyświetlonych parametrów
wypisz_argv:
cmp edi, eax ; czy liczba wyświetlonych =
; = liczba parametrów?
je koniec_wypisz_argv ; jeśli tak, to koniec
; wyświetlania parametrów
mov esi, [argv+edi*4] ; pobierz parametr numer EDI.
; każdy wskaźnik jest czterobajtowy,
; dlatego mnożymy EDI przez 4.
pisz_esi ; wypisz napis pod adresem ESI
; czyli nasz parametr
nwln ; przejdź do nowej linii
add edi, 1 ; wybieramy kolejny parametr
jmp short wypisz_argv ; i idziemy pisać od nowa
koniec_wypisz_argv:
; parametry się skończyły. Teraz będzie jeden
; wskaźnik zerowy i zmienne środowiska
inc edi ; przeskocz wskaźnik zerowy
wypisz_env:
mov esi, [argv+edi*4] ; pobierz zmienną środowiskową
test esi, esi ; sprawdź, czy nie wskaźnik zerowy
jz koniec_wypisz_env ; jeśli zero, to skończyliśmy
pisz_esi ; wypisz zmienną środowiskową
nwln ; przejdź do nowej linii
add edi, 1 ; przechodzimy na kolejna zmienną
jmp short wypisz_env ; i wypisujemy dalej
koniec_wypisz_env:
wyjscie ; koniec...
Jak widać, nie było to aż takie trudne
jak się mogło zdawać na początku. Właśnie poznaliście
kolejną rzecz, która jest łatwa w użyciu, a możliwości której są duże. Teraz będziecie mogli
śmiało zacząć pisać programy, których jedynym kanałem komunikacyjnym z użytkownikiem
będzie linia poleceń, co znacznie uprości ich obsługę.
Tylko pamiętajcie o dodaniu kodu wyświetlającego sposób użycia programu, gdy nie podano mu żadnych parametrów.