This is an old revision of the document!
Deadline: 4 Aprilie 2018, ora 23:55
Deadline hard: 11 Aprilie 2018, ora 23:55
Să se realizeze o implementare minimală a bibliotecii stdio, care să permită lucrul cu fișiere. Biblioteca va trebui să implementeze structura SO_FILE (similar cu FILE din biblioteca standard C), împreună cu funcțiile de citire/scriere. De asemenea, va trebui să implementați funcționalitatea de buffering.
Este tipul de date care descrie un fișier deschis și este folosit de toate celelalte funcții. Un pointer la o astfel de structură este obținut la deschiderea unui fișier, folosind funcția so_fopen.
SO_FILE *so_fopen(const char *pathname, const char *mode);
Argumentele funcției sunt similare cu cele pentru fopen:
pathname reprezintă calea către un fișiermode este un string care determină modul în care va fi deschis fișierul. Poate fi unul din următoarele:r - deschide fișierul pentru citire. Dacă fișierul nu există, funcția eșuează.r+ - deschide fișierul pentru citire și scriere. Dacă fișierul nu există, funcția eșuează.w - deschide fișierul pentru scriere. Dacă fișierul nu există, este creat. Dacă fișierul există, este trunchiat la dimensiune 0w+ - deschide fișierul pentru citire și scriere. Dacă fișierul nu există, este creat. Dacă fișierul există, este trunchiat la dimensiune 0a - deschide fișierul în modul append - scriere la sfârșitul fișierului. Dacă fișierul nu există, este creat.a+ - deschide fișierul în modul append+read. Dacă fișierul nu există, este creat.
Funcția so_fopen alocă o structură SO_FILE pe care o întoarce. În caz de eroare, funcția întoarce NULL.
Închiderea unui fișier se face cu so_fclose.
int so_fclose(SO_FILE *stream);
Închide fișierul primit ca parametru și eliberează memoria folosită de structura SO_FILE. Întoarce 0 în caz de succes sau SO_EOF în caz de eroare.
Aceste operații se realizează cu ajutorul funcțiilor so_fgetc, so_fputc, so_fread și so_fwrite.
int so_fgetc(SO_FILE *stream);
Citește un caracter din fișier. Întoarce caracterul respectiv sau SO_EOF în caz de eroare.
int so_fputc(int c, SO_FILE *stream);
Scrie un caracter în fișier. Întoarce caracterul scris sau SO_EOF în caz de eroare.
size_t so_fread(void *ptr, size_t size, size_t nmemb, SO_FILE *stream);
Citește nmemb elemente, fiecare de dimensiune size. Datele citite sunt stocate la adresa de memorie specificată prin ptr. Întoarce numărul de elemente citite. În caz de eroare sau dacă s-a ajuns la sfârșitul fișierului, funcția întoarce 0.
size_t so_fwrite(const void *ptr, size_t size, size_t nmemb, SO_FILE *stream);
Scrie nmemb elemente, fiecare de dimensiune size. Datele ce urmează a fi scrise sunt luate de la adresa de memorie specificată prin ptr. Întoarce numărul de elemente scrise, sau 0 în caz de eroare.
int so_fseek(SO_FILE *stream, long offset, int whence);
Mută cursorul fișierului. Noua poziție este obținută prin adunarea valorii offset la poziția specificată de whence, astfel:
offset bytes față de începutul fișieruluioffset bytes față de poziția curentăoffset bytes față de sfârșitul fișieruluiÎntoarce 0 în caz de succes și -1 în caz de eroare.
long so_ftell(SO_FILE *stream);
Întoarce poziția curentă din fișier. În caz de eroare funcția întoarce -1.
Proprietatea esențială a bibliotecii stdio este că aceasta face buffering. O structură SO_FILE are un buffer asociat, iar operațiile de citire/scriere se folosesc de acest buffer:
so_fgetc, so_fread) întorc datele direct din buffer. Atunci când bufferul este gol sau nu există date suficiente, acesta este populat cu date din fișier, folosind API-ul pus la dispoziție de sistemul de operare (read/ReadFile)so_fputc, so_fwrite) scriu datele în buffer. În situația când bufferul este plin (sau când se apelează so_fflush), datele se scriu în fișier, folosind API-ul pus la dispoziție de sistemul de operare(write/WriteFile).int so_fflush(SO_FILE *stream);
Are sens doar pentru fișierele pentru care ultima operație a fost una de scriere. În urma apelului acestei funcții, datele din buffer sunt scrise în fișier. Întoarce 0 în caz de succes sau SO_EOF în caz de eroare.
/* Linux */ int so_fileno(SO_FILE *stream); /* Windows */ HANDLE so_fileno(SO_FILE *stream);
Întoarce file descriptorul/HANDLE-ul asociat structurii SO_FILE.
int so_feof(SO_FILE *stream);
Întoarce o valoarea diferită de 0 dacă s-a ajuns la sfârșitul fișierului sau 0 în caz contrar.
int so_ferror(SO_FILE *stream);
Întoarce o valoarea diferită de 0 dacă s-a întâlnit vreo eroare în urma unei operații cu fișierul sau 0 în caz contrar.
SO_FILE *so_popen(const char *command, const char *type);
Lansează un proces nou, care va executa comanda specificată de parametrul command. Ca implementare, se va executa sh -c command, respectiv cmd /C command, folosind un pipe pentru a redirecta intrarea standard/ieșirea standard a noului proces. Funcția întoarce o structură SO_FILE pe care apoi se pot face operațiile uzuale de citire/scriere, ca și cum ar fi un fișier obișnuit.
Valorile parametrului type pot fi:
so_fgetc/so_fread executate pe fișier vor citi de la ieșirea standard a procesului creat.so_fputc/so_fwrite executate pe fișier vor scrie la intrarea standard a procesului creat.int so_pclose(SO_FILE *stream);
Se apelează doar pentru fișierele deschise cu so_popen. Așteaptă terminarea procesului lansat de so_popen și eliberează memoria ocupată de structura SO_FILE. Întoarce codul de ieșire al procesului (cel obținut în urma apelului waitpid/GetExitCodeProcess).
so_fgetc/so_fputc. Apoi so_fread/so_fwrite pot fi implementate pe baza lor.fseek.fflush sau fseek.so_fseek trebuie avute în vedere următoarele:so_fopen se va implementa folosind open, so_fgetc/so_fread folosind read, so_fputc/so_write folosind write, etc.popen, trebuie să închideți capetele de pipe nefolosite atât în procesul părinte cât și în procesul copil.so_fopen se va implementa folosind CreateFile, so_fgetc/so_fread folosind ReadFile, so_fputc/so_write folosind WriteFile, etc.popen, înainte de a apela CreateProcess trebuie să marcați ca nemoștenibil capătul de pipe nefolosit de către procesul copil. În acest sens, puteți folosi funcția SetHandleInformation.ReadFile să întoarcă FALSE, iar GetLastError să întoarcă ERROR_BROKEN_PIPE.