This is an old revision of the document!
Dezvoltarea trebuie făcută exclusiv pe mașinile virtuale SO.
git. Indicați în README link-ul către repository dacă ați folosit git.
Asigurați-vă că responsabilii de teme au drepturi de citire asupra repo-ului vostru.
Pentru a folosi git, puteți să vă creați un repository privat pe github și să dați drepturi de citire pentru molecula2788.
Motivul pentru care încurajăm acest lucru este că responsabilii de teme se pot uita mai rapid pe github la temele voastre pentru a vă ajuta în cazul în care întâmpinați probleme/bug-uri.
Crash-course practic de git puteți găsi aici: git-immersion
Dacă ați creat deja un repo privat pe Gitlab puteți să-l folosiți în continuare pe acela.
student@so:~$ git clone https://github.com/systems-cs-pub-ro/so student@so:~$ cd assignments/2-stdio
În repository-ul de pe Github se vor găsi și scheletele pentru temele viitoare, care vor fi actualizate și se vor putea descărca pe viitor folosind comanda:
student@so:~$ git pull
Tot prin comanda de mai sus se pot obține toate actualizările făcute în cadrul temei 2.
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ă ofere funcționalitatea de buffering.
Rezolvarea temei va trebui să genereze o bibliotecă dinamică numită libso_stdio.so/so_stdio.dll care implementează header-ul
so_stdio.h. În acest header găsiți semnăturile functiilor exportate de biblioteca generată de voi.
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 ca unsigned char extins la int, 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.
Dacă apelul fork/CreateProcess eșuează se va întoarce NULL.
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). Dacă apelul waitpid/WaitForSingleObject eșuează, se va întoarce -1.
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:Makefile care să aibă cel puțin următoarele reguli:build: compilează biblioteca dinamică libso_stdio.so/so_stdio.dllclean: șterge toate fișierele binare rezultate în urma compilăriiREADMEso_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.system()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 scop, puteți folosi funcția SetHandleInformation.ReadFile să întoarcă FALSE, iar GetLastError să întoarcă ERROR_BROKEN_PIPE.GENERIC_READ|GENERIC_WRITE și FILE_SHARE_READ|FILE_SHARE_WRITE.so_fileno să fie implementată.
Cursuri utile:
Laboratoare utile:
Resurse:
so_stdio.Pagina de Upload:
Pentru întrebări sau nelămuriri legate de temă folosiți forumul temei.
ATENȚIE să nu postați imagini cu părți din soluția voastră pe forumul pus la dispoziție sau orice alt canal public de comunicație. Dacă veți face acest lucru, vă asumați răspunderea dacă veți primi copiat pe temă.