Minishell – Felhasználói útmutató

A program elindítása után egy prompt (>) jelenik meg és a felhasználótól várja a shell-parancsokat. Az ENTER gomb lenyomása után végrehajtja a kapott parancsot, illetve amennyiben nem tudja végrehajtani, hibaüzenetet ad. A parancs végrehajtása után újra megjelenik a prompt, ami jelezi, hogy készen áll az újabb shell-parancs végrehajtására.

A program az alábbi feladatok ellátására alkalmas:

  • Paraméter nélküli parancsok végrehajtása (pl. ls, date, pwd).
  • Paraméterekkel rendelkező parancsokat futtatása (pl. more fájlnév, ls –l /tmp).
  • Átirányításokat elvégezése. Ehhez a művelethez a <, > jelek egyikét kell használnunk, amely utáni szót tekinti a  shell fájlnévnek és abból, vagy abba próbál átirányítani. Ha több szó is létezik a megfelelő jel után, akkor hibaüzenettel tér vissza.
    • Fájl tartalmának átirányítása a képernyőre a < karakter használatával lehetséges. Amennyiben a megadott fájl nem létezik, akkor a shell jelzi, hogy nem tudja elvégezni a műveletet.
    • Képernyő tartalmának átirányításához egy fájlba a > jelet kell használnunk. Ha a fájl nem létezik, akkor létrehozza és elvégzi az átirányítást, míg ha létező fájlba akarok átirányítani akkor felülírja a fájl tartalmát.
  • A környezeti változók megmutatására, változtatására, könyvtárak közti navigálásra, ismeri az "echo"-t. 

A programból való kilépés az exit parancs beolvasásával történik.

Fejlesztői dokumentáció

A program a főprogramon kívül néhány eljárást használ, amelyekben külön végzem el az előbbiekben felsorolt különböző műveleteket (átirányítás, környezeti változó módosítás, stb.). Elsősorban csatolom a későbbiekben használandó könyvtári header állományokat, majd a főprogram által használt eljárások leírása következik és végül a főprogram.

A főprogram változóinak deklarálása, inicializálása után kiíratom a promptot a képernyőre. A működéshez szükség van az alábbi változókra:

·         egy s nevű változó, amelyben a felhasználó által beolvasott parancsot tárolom;

·         egy pid_t típusú pid nevű változó, amely a program pidszámát tartalmazza;

·         valamint egy merre nevű int típusú változó, amelyben tárolom azt, hogy melyik irányba lépjek tovább. 

Ez utóbbira azért van szükség, mert bizonyos shell-parancsokat nem szabad kombinálni pl. nem végezhetek el olyant, hogy echo xxx > fájlnév vagy printenv PATH > fájlnev. A prompt megjelenítése után várom a felhasználótól érkező parancsot, amit az s változóba fogok tárolni. Miután megkaptam, ellenőrzöm, hogy megegyezik-e az exit-tel, mert ha igen, akkor csak annyi a tennivalóm, hogy kilépek a programból. Amennyiben nem, akkor kezdődnek a komplikációk, mivel először ellenőriznem kell, hogy milyen parancsot adott ki a felhasználó - a fenti visszásságok elkerülése érdekében -, ugyanis például egy echo vagy környezeti változó lekérdezés, módosítás vagy könyvtárváltás után nem engedhetek meg pl. átirányítást.  Azt, hogy milyen parancsot kapott a shell és mi az, amit tennem kell, a tovabbmehet() nevű eljárásban döntöm el és a visszaadott paraméter függvényében lépek tovább. Ha a visszaadott paraméter értéke nem nulla akkor a következő promptot iratom ki, ha 0 és nincs forkhiba, valamint a pid is 0 akkor végrehajtom a beolvasott shell-parancsot. A végrehajtás az execlp használatával történik, amely paraméter nélküli parancsokat hajt végre. Ha ez nem sikerült akkor meghívom a szetszed() eljárást, amely a parancsot lebontja szavakra és úgy próbálja végrehajtani. Persze ez sem biztos, hogy sikerül, ilyen esetben jelzi a felhasználónak a parancsvégrehajtás sikertelenségét, majd újra a prompt jelenik meg.

A tovabbmehet() eljárás a beolvasott parancsot kapja paraméterként. A helyi változók deklarálása után a visszatérő érték inicializálása következik, majd a kapott parancs lebontása. A visszatérő értéket 0-val teszem egyenlővé, amivel azt feltételezem, hogy a kapott parancs nem echo, nem könyvtárváltás és nem környezeti változóval kapcsolatos művelet. Amennyiben tévedtem akkor a következő lépésekben módosítom az értékét. A kapott parancsot kétfelé bontom, az elsőbe teszem az első SPACE-szig tartozó részt. Ha ez echo, chdir, setenv vagy printenv, akkor a másodikba jön a parancs többi része és a visszatérési érték az otodik() nevű eljárás által visszaadott érték lesz. A végén persze visszaadom az allj nevű változóban tárolt visszatérési értéket.

Az otodik() nevű eljárás két paramétert kap, a tovabbmehet()-ben két részre lebontott parancsot. Visszatérési értéke 1 lesz. Az eljárás feladata végrehajtani az echo-t, a környezeti változókkal kapcsolatos műveleteket valamint a könyvtárváltásokat. Ha a kapott első paraméter echo, akkor egyszerűen megjeleníti a képernyőn a kapott második paramétert. Ha a második paraméter " " jelben van akkor ezt leszűri belőle. A környezeti változókkal kapcsolatos műveleteket a putenv valamint getenv használatával végzi el, vagy ellenkező esetben figyelmeztető- és hibaüzenetekkel igyekszik a felhasználó segítségére.  A könyvtárváltás a chdir használatával került kivitelezésre.

A szetszed() eljárás ugyancsak egy paramétert kap, a beolvasott parancsot. Végigmegy a parancson és annak függvényében, hogy talált-e benne átirányítási jelt vagy sem, és ha igen, akkor milyent, a van nevű változónak különböző értékeket ad. A van változó értékének függvényében különböző eljárások kerülnek meghívásra: átirányítási jel hiányában a masodik(), átirányítási jel létezése esetén az ellenoriz().

Az ellenoriz() eljárás három paramétert kap: a parancssort, azt a pozíciót ahol valamilyen átirányítási jel található, valamint az előbbiekben említett van változó értékét, ami tulajdonképpen azt jelzi, hogy milyen átirányítási jelről van szó (< vagy > lehet). Az eljárásban attól a pozíciótól, ahol az átirányítási jel található, végigmegyek a parancssoron a végjelig vagy amíg a parancssor végére érek. Végjelet kaphatok abban az esetben, ha téves a parancssor, vagyis az átirányítási jel után nemcsak egy fájlnév következik. Természetesen ilyen esetben hibaüzenettel tér vissza a program. Ha helyes a parancssor akkor az átirányítási jel után következő fájlnevet kimentem egy változóba, az átirányítási jel előtti részt egy másik változóba és az átirányítási jel függvényében meghívom a harmadik(), illetve a negyedik() nevű eljárásokat, melyeknek ezt a két változót adom át paraméterként.

A harmadik() illetve a negyedik() eljárások célja ugyanaz, elvégezni a megfelelő átirányítást. Mindkettőben megpróbálom megnyitni a fájlt a megfelelő módra (írás, olvasás), majd dup2-vel elvégzem az átirányítást és meghívom a masodik() nevű eljárást. Sikertelen fájlmegnyitás, fájllétrehozás esetén hibaüzenetet küldök a felhasználó számára.

A masodik() nevű eljárás szintén a beolvasott parancssort kapja meg paraméterként.  Tulajdonképpen annyit csinál, hogy a kapott parancssort szavakra bontja és végrehajtja az execvp paranccsal. A szavakra bontás úgy történik, hogy végigmegy a parancssoron és egy vektorba menti ki a szavakat, pontosabban az üres karakterig tartó karaktersorozatokat. Szükség van a malloc-ra, ahhoz hogy a szavakat tudjam megjegyezni. E parancs hiányában mindig csak az elsőt adja vissza. A szavakra bontás szükséges az execvp parancs helyes használatához.

Finta Annamária