Programarea Calculatoarelor, seria CC

Tema 4

Data publicării: Luni, 21 decembrie 2009

Deadline: Vineri, 15 ianuarie 2010, ora 23:50

Precizări:



Introducere

Formatarea cu un Coding Style

Tema presupune realizarea unui utilitar pentru formatarea de fisiere sursa C cu un Coding Style clasic numit K&R (denumire ce vine de la Kernigham & Ritchie, autorii unei cărţi foarte cunoscute despre limbajul C şi anume "The C Programming Language", pe care o puteţi găsi şi pe pagina principala a cursului de PC) şi apoi exportarea lor în documente HTML care să poată fi afişate într-un browser (de exemplu, Mozilla Firefox). Sursa se va numi formatkr.c.

Pentru mai multe detalii despre ce înseamnă Coding Style consultaţi linkurile:


Transmiterea de argumente în linia de comandă

Programul scris de voi trebuie să poată fi folosit în consolă pentru a formata orice fişier sursă de C. Pentru aceasta, el va trebui să poată primi diverşi parametri în linia de comandă. Un program scris în C poate accepta parametri din linia de comandă prin modificarea sintaxei funcţiei main astfel încât aceasta să primească parametrii respectivi. Deoarece numărul de parametri care vor fi transmişi nu este cunocut dinainte (ne putem imagina, de exemplu, că un utilizator în necunoştinţă de cauză poate furniza orice opţiuni programului), funcţia main va accepta două argumente, după cum urmează:

Pentru a clarifica sintaxa, iată un exemplu de program care afişează toţi parametrii pe care i-a primit în linia de comandă.

# include <stdio.h>

int main(int argc, char *argv[])
{
        printf("Numele executabilului rulat: %s\n",argv[0]);
        printf("Numarul de parametri propriu-zisi: %d\n",argc-1);
        int i;
        for (i=1;i<=argc-1;i++)
                printf("Parametru cu numarul %d este: %s\n",i,argv[i]);
        fflush(stdin);
        getchar();
        return 0;
}

Copiaţi executabilul de mai sus într-un fişier C şi compilaţi-l folosind următoarele comenzi:

gcc fisier_sursa.c -o executabil1
gcc fisier_sursa.c -o executabil2

Rulaţi pe rând următoarele comenzi şi observaţi funcţionalitatea programului.

./executabil1 -optiune1 valoptiune1 -optiune2 valoptiune2
./executabil2 -optiune val1,val2,val3
./executabil1 -optiune val1, val2, val3
./executabil1 -optiune val1 val2 val3
./executabil2 orice string este considerat dr3pt 4r9Um3n7
./executabil1 numerele 2 si 3 sunt considerate tot stringuri


Exportarea în format HTML

A doua etapă a programului este exportarea unui fişier sursă de C deja formatat cu un Coding Style într-o pagină în format HTML. Formatul HTML este un format standardizat prin care se specifică modul în care trebuie prezentată informaţia dintr-o pagină web. Pentru o scurtă introducere în sintaxa HTML, aveţi posibilitatea să consultaţi următoarele linkuri:

Pentru a vedea un exemplu de cum trebuie să arate un fişier HTML pentru o sursă de C anume, puteţi să generaţi unul urmând paşii de mai jos, în Linux:

Pentru folosirea funcţionalităţilor de mai sus ale vim trebuie să aveţi instalat pachetul vim-full. În Ubuntu (sau distribuţii bazate pe Debian), instalarea lui se poate face, de exemplu, folosind comanda:

sudo apt-get install vim-full

În Fedora (sau distribuţii bazate pe RedHat) puteţi folosi comanda echivalentă yum.


Valgrind

Valgrind este un instrument de debugging pentru Linux folosit pentru depistarea greșelilor de utilizare a memoriei: memory leak-uri, pointeri invalizi, folosirea unor variabile neinițializate, free-uri invalide.

Pentru a folosi valgrind va trebui să instalați pachetul valgrind. De exemplu, pe distribuția Ubuntu:

apt-get install valgrind

Pentru a rula programul în Valgrind cu tool-ul pentru memcheck folosiți:

valgrind --tool=memcheck ./formatkr parametrii

Pentru afișare leak-urilor detaliat:

valgrind --tool=memcheck --leak-check=full ./formatkr parametrii


Cerințe

Atenţie! Stringurile prezente în fişierul de intrare trebuie să rămână nemodificate. De exemplu, linia printf("     Spatii si a+sqrt(5);     "); va rămâne nemodificată, cu excepţia indentării.

1. [20 puncte]

Creaţi un program formatkr care primeşte în linia de comandă numele a unul sau mai multe fişiere sursă C şi formatează acele fişier aplicându-le stilul Kernighan & Ritchie.

Fişierele formatate vor avea numele fisier-intrare_neat.c. Atenţie, conţinutul efectiv al fişierului sursă (instrucţiunile de limbaj C) nu trebuie modificat!

Fişierele formatate vor conţine câte o singură instrucţiune pe o linie. Instrucţiunile se termină în C cu ;, mai puţin antetele instrucţiunilor repetitive(for,while,do-while) sau de control (if-else), antetele de funcţie (int main()) sau macro-urile (# include). Iată mai jos un exemplu de conţinut al unui fişier neformatat, şi modul în care ar trebui să arate acesta după formatare.

Atenţie! Exemplul de mai jos a fost conceput să vă atragă atenţia asupra a doar câteva dintre capcanele pe care vi le poate ridica împărţirea în linii a codului. Comentariile, dacă există, for fi lăsate nemodificate. Atenţie însă, comentariile pot conţine orice şir în interior!!

Atenţie! Se garantează că în codul fişierelor de C pe care le va formata programul scris de voi nu există instrucţiunea switch-case

splitlines.c
# include <stdio.h>

int     main () {               printf("ATENTIE! Caracterele \" ; sau { } pot sa apara in stringuri");
fflush(stdin);if (1=='A') {printf("\nConditia e true!\n");}else printf("\nConditia e false!\n"/*);*/);getchar()   ;return 0;}
splitlines_neat.c
# include <stdio.h>

int     main ()
{              
printf("ATENTIE! Caracterele \" ; sau { } pot sa apara in stringuri");
fflush(stdin);
if (1=='A') {
printf("\nConditia e true!\n");
}
else
printf("\nConditia e false!\n"/*);*/);
getchar()   ;
return 0;
}

2. [15 puncte]

Modificaţi programul de la punctul anterior astfel încât să introduceţi următoarea funcţionalitate: în fişierul formatat, instrucţiunile vor fi indentate (adică aliniate mai la dreapta) conform specificatiilor de stil de cod K&R.

Pentru pasul de indentare puteţi alege orice mărime (frecvent se utilizează tab-uri de 8 spaţii sau de 4 spaţii). Specificaţi în README alegerea făcută.

addindent.c
# include <stdio.h>

int     main ()
{              
printf("ATENTIE! Caracterele \" ; sau { } pot sa apara in stringuri");
fflush(stdin);
if (1=='A') {
printf("\nConditia e true!\n");
}
else
printf("\nConditia e false!\n"/*);*/);
getchar()   ;
return 0;
}

addindent_neat.c
# include <stdio.h>

int     main ()
{              
        printf("ATENTIE! Caracterele \" ; sau { } pot sa apara in stringuri");
        fflush(stdin);
        if (1=='A') {
                printf("\nConditia e true!\n");
        }
        else
                printf("\nConditia e false!\n"/*);*/);
        getchar()   ;
        return 0;
}

3. [15 puncte]

Modificaţi programul de la punctul anterior astfel încât să introduceţi următoarea funcţionalitate: în fişierul formatat, cu excepţia aliniatului, liniile nu trebuie să conţină secvenţe de mai mult de un spaţiu consecutiv. Se vor introduce câte un spaţiu în stânga şi în dreapta operatorilor (=, ==, +, etc).

trimspaces.c
# include <stdio.h>

int     main ()
{              
        printf("ATENTIE! Caracterele \" ; sau { } pot sa apara in stringuri");
        printf("ATENTIE!Sir cu spatii:     ");
        fflush(stdin);
        if (1=='A') {
                printf("\nConditia e true!\n");
        }
        else
                printf("\nConditia e false!\n"/*);*/);
        getchar()   ;
        //comentariu cu spatii:      ;
        /*comentariu cu spatii:      ;*/
        return 0;
}
trimspaces_neat.c
# include <stdio.h>

int main()
{              
        printf("ATENTIE! Caracterele \" ; sau { } pot sa apara in stringuri");
        printf("ATENTIE!Sir cu spatii:     ");
        fflush(stdin);
        if (1 == 'A') {
                printf("\nConditia e true!\n");
        }
        else
                printf("\nConditia e false!\n"/*);*/);
        getchar() ;
        //comentariu cu spatii:      ;
        /*comentariu cu spatii:      ;*/
        return 0;
}

4. [20 puncte]

Pornind de la fisier-intrare.c, generaţi un fişier HTML cu numele fisier-intrare.html care să conţină textul sursă C în stilul K&R, în format HTML.

Vă reamintim despre câteva dintre modificările care trebuiesc introduse (fără a le menţiona nici pe departe pe toate):

Fişierul sourcecode.html este o pagină web. Deschideţi-l cu un browser (de ex., Mozilla Firefox) şi observaţi cum se vede. Ca să vă convingeţi de ce nu ajunge să încadrăm tot fişierul în taguri de tip "html" si "body", puteţi încerca să adăugaţi <html><body> la începutul fişierului sourcecode.c, respectiv </body></html> la sfârşit, salvaţi-l cu extensia *.html şi deschideţi-l în browser. Veţi observa că efectul nu este cel dorit!!

sourcecode.c sourcecode.html
# include <stdio.h>

int main()
{
    printf("Hello world!");
    return 0;
}

<html>
<body><font face="monospace">
# include &lt;stdio.h&gt;<br>
<br>
int main()<br>
{<br>
&nbsp;&nbsp;&nbsp;&nbsp;printf(&quot;Hello world!&quot;);<br>
&nbsp;&nbsp;&nbsp;&nbsp;return 0;<br>
}<br>
<br>
</font></body>
</html>

5. [20 puncte]

Modificaţi programul, astfel încât să adăugaţi următoarele funcţionalităţi:

colorsourcecode.c
# include <stdio.h>

int main()
{
        printf("//Acesta NU(!!) este un comentariu");
        //Acesta este un comentariu
        int a,b;
        a = 5;
        b = 7;
        if (a + b >= 10)
                printf("%d",a + b);
        else 
                printf("%d",b - a);
        return 0;
}
colorsourcecode.html
<html>
<body bgcolor="#ffffff" text="#000000"><font face="monospace">
<font color="#ff40ff"># include </font><font color="#ff6060">&lt;stdio.h&gt;</font><br>
<br>
<font color="#00ff00">int</font>&nbsp;main()<br>
{<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf(<font color="#ff6060">&quot;//Acesta NU(!!) este un comentariu&quot;</font>);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#8080ff">//Acesta este un comentariu</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#00ff00">int</font>&nbsp;a,b;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;a = <font color="#ff6060">5</font>;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;b = <font color="#ff6060">7</font>;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#8A4B08"><b>if</b></font>&nbsp;(a + b &gt;= <font color="#ff6060">10</font>) <br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf(<font color="#ff6060">&quot;%d&quot;</font>,a + b);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#8A4B08"><b>else</b></font>&nbsp;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf(<font color="#ff6060">&quot;%d&quot;</font>,b - a);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#8A4B08"><b>return</b></font>&nbsp;<font color="#ff6060">0</font>;<br>
}<br>
</font></body>
</html>

6. [20 puncte]

Până în acest moment, programul accepta o listă de fişiere în linia de comandă şi le formata în mod implicit pe toate, cu nume generate prin adăugarea sufixului _neat. Modificaţi programul astfel încât apelarea sa să se facă cu următorul format:

./formatkr <sursa_intrare> -o <sursa_iesire> -h <fisier_html>

./formatkr <sursa_intrare> -h <fisier_html> -o <sursa_iesire>
./formatkr -o <sursa_iesire> <sursa_intrare> -h <fisier_html>
./formatkr -o <sursa_iesire> -h <fisier_html> <sursa_intrare>
./formatkr -h <fisier_html> <sursa_intrare> -o <sursa_iesire>
./formatkr -h <fisier_html> -o <sursa_iesire> <sursa_intrare>

Fie -o, fie -h pot lipsi, caz în care respectivele fişiere de ieşire vor avea numele generate automat (după regulile de la subpunctele 1 şi 4). Astfel, comenzi posibile mai sunt şi:

./formatkr <sursa_intrare> -h <fisier_html>
./formatkr <sursa_intrare> -o <sursa_iesire>
./formatkr <sursa_intrare>

Observaţie! Dacă argumentele primite de program nu corespund cu vreounul din formatele de mai sus, atunci se afişează mesajul "Format de apelare greşit!" şi se încheie execuţia programului.

Observaţie! Dacă formatul comenzii este corect, dar vreo unul dintre fişierele care trebuie formatate este inexistent, atunci se afişează mesajul "EROARE: Fişierul <fisier_inexistent> nu a putut fi deschis!" şi se trece la procesarea fişierului următor, dacă programul primise de procesat mai multe fişiere şi acesta nu era ultimul. În caz contrar, se încheie execuţia programului.

7. [15 puncte]

Modificaţi programul, astfel încât acesta să poată primi o opţiune suplimentară -uncomm care specifică păstrarea comentariilor din fişier. Dacă se include această opţiune la apelare, atunci se vor elimina din fişierele de ieşire toate comentariile.

Observaţie! Mare atenţie la stringurile care apar în program. Următoarele linii de cod NU conţin comentarii:

printf("//Acesta NU este un comentariu!");
printf("inceput /* Acesta NU este un comentariu!*/ sfarsit");

8. [15 puncte]

Modificaţi programul, astfel încât numele de tipuri de date definite de utilizator, precum şi numele de funcţii definite de utilizator să apară în pagina HTML cu Italic.

9. [10 puncte]

Modificaţi programul, astfel încât acesta să accepte şi un argument -quiet. Dacă acest arguemnt este prezent, atunci nu se mai afişează nici un mesaj în consolă. (inclusiv cele de eroare).