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.