/*
    detab_v6.c      FINAL       01.03.2018.


Skida beline (space karaktere i tabulatore) sa kraja redova
i preostale tabulatore zamenjuje space karakterima.
Obraca se paznja na tab stopove.

Verzija sa fgets() i fputs() i funkcijama
trim_string_end()
detab_string()
detab_file().

Dodata je funkcija trim_string_end() koja uklanja beline
(space i tabulatore) sa kraja stringa s,
i njeni brojaci
br_skinutih_space,
br_skinutih_tabulatora,
br_skinutih_belina.

U main() se obradjuju argumenti programa (ImeFajla.ekstenzija i t)
a funkciji detab_file() se prosledjuju imena ulaznog i izlaznog fajla, t
i pointeri na brojace karaktera, redova i tabulatora.

Tabulator pomera kurzor od trenutne pozicije do sledeceg tab stopa.

t je razmak izmedju dva susedna tab stopa izrazen brojem space karaktera.
Najcesce je t=4, a moze biti 2, 4, 6, 8 ili bilo koja druga pozitivna vrednost.

Ako se tabulator nalazi na i-tom indeksu reda,
zamenjuje se odgovarajucim brojem space karaktera:
    nb = t-(i%t)                                        ( ZAPAMTI FORMULU ! )
do sledeceg tab stopa.

Ako t=4, tab stopovi su na indeksima reda:

          1         2         3         4         5         6         7         8
012345678901234567890123456789012345678901234567890123456789012345678901234567890
    4   8   12  16  20  24  28  32  36  40  44  48  52  56  60  64  68  72  76  80 ... indeksu.

Celobrojno deljenje:
x%y = x ,                                       ako je x<y
x%y = 0 ,                                       ako je x=y
x%y = celobrojni ostatak pri delenju x sa y,    ako je x>y

Tabulator na indeksu i se menja sa nb = t-(i%t) space karakterom    ( ZAPAMTI FORMULU ! )
da bi se stiglo do sledeceg tab stopa:
indeks                    nb                                         tab stop
   0, menja se sa t-(i%t) = 4-( 0%4) = 4-0 = 4 space da bi se stiglo do  4. indeksa
   1, menja se sa t-(i%t) = 4-( 1%4) = 4-1 = 3 space da bi se stiglo do  4. indeksa
   2, menja se sa t-(i%t) = 4-( 2%4) = 4-2 = 2 space da bi se stiglo do  4. indeksa
   3, menja se sa t-(i%t) = 4-( 3%4) = 4-3 = 1 space da bi se stiglo do  4. indeksa

   4, menja se sa t-(i%t) = 4-( 4%4) = 4-0 = 4 space da bi se stiglo do  8. indeksa
   5, menja se sa t-(i%t) = 4-( 5%4) = 4-1 = 3 space da bi se stiglo do  8. indeksa
   6, menja se sa t-(i%t) = 4-( 6%4) = 4-2 = 2 space da bi se stiglo do  8. indeksa
   7, menja se sa t-(i%t) = 4-( 7%4) = 4-3 = 1 space da bi se stiglo do  8. indeksa

   8, menja se sa t-(i%t) = 4-( 8%4) = 4-0 = 4 space da bi se stiglo do 12. indeksa
   9, menja se sa t-(i%t) = 4-( 9%4) = 4-1 = 3 space da bi se stiglo do 12. indeksa
  10, menja se sa t-(i%t) = 4-(10%4) = 4-2 = 2 space da bi se stiglo do 12. indeksa
  11, menja se sa t-(i%t) = 4-(11%4) = 4-3 = 1 space da bi se stiglo do 12. indeksa

  12, menja se sa t-(i%t) = 4-(12%4) = 4-0 = 4 space da bi se stiglo do 16. indeksa
  13, menja se sa t-(i%t) = 4-(13%4) = 4-1 = 3 space da bi se stiglo do 16. indeksa
  14, menja se sa t-(i%t) = 4-(14%4) = 4-2 = 2 space da bi se stiglo do 16. indeksa
  15, menja se sa t-(i%t) = 4-(15%4) = 4-3 = 1 space da bi se stiglo do 16. indeksa
...

Primer poziva programa: detab ImeFajla 4

U tekst fajlu ImeFajla
tabulatore zamenjuje space karakterima
i rezultat snima u tekst fajl ImeFajla.txt

Ako t nije navedeno ili je t=0, ne zamenjuje tabulatore.

*/

#include <stdio.h>
#include <stdlib.h>                         // zbog exit()
#include <string.h>                         // zbog strcpy()

#define MAX_DUZINA_STRINGA 1024             // maksimalna duzina jednog reda u fajlu


// Uklanja beline (space i tabulatore) sa kraja stringa s.
// U stringu s moze biti samo jedan znak za novi red '\n' i to samo na kraju stringa s,
// i tada taj znak za novi red ostaje i na kraju trimovanog stringa.
 void trim_string_end( char s[],
                       int *br_skinutih_space,
                       int *br_skinutih_tabulatora,
                       int *br_skinutih_belina){

    int znak_za_novi_red_je_na_kraju=0;     // flag kojim pamtimo da je '\n' bio na kraju stringa s
    int n = strlen(s);                      // n je duzina stringa s (ne broji se zavrsni karakter '\0')
                                            // n je redni broj poslednjeg karaktera u stringu, ciji je indeks n-1

    if (s[n-1]=='\n'){                      // ako je poslednji karakter u stringu znak za novi red '\n'
        znak_za_novi_red_je_na_kraju=1;     // pamtimo ga u flagu
        n--;                                // i preskacemo, ponovo cemo ga dodati na kraju funkcije
    }

    while(n > 0){                           // od kraja stringa prema pocetku, ulevo

        if  (s[n-1]==' '){                  // ako je poslednji karakter space
            (*br_skinutih_space)++;
            (*br_skinutih_belina)++;
            s[n-1]='\0';                    // brisemo ga
        }else if (s[n-1]=='\t'){            // ako je poslednji karakter tabulator
            (*br_skinutih_tabulatora)++;
            (*br_skinutih_belina)++;
            s[n-1]='\0';                    // brisemo ga
        }else                               // ako nije space ili tabulator
            break;                          // prekini while petlju

        n--;                                // prelazimo na sledeci karakter ulevo
    }

    if (znak_za_novi_red_je_na_kraju==1){   // ako je na kraju stringa s bio znak za novi red
        n = strlen(s);                      // n je nova duzina stringa s na koju
        s[n]='\n';                          // ponovo dodajemo karakter za novi red '\n' i
        s[n+1]='\0';                        // zavrsavamo string s
    }

} // trim_string_end()


// detab_string() u stringu s, tabulatore zamenjuje odgovarajucim brojem space znakova.
// Vraca broj zamenjenih tabulatora br_tab.
// t je razmak izmedju tab stopova izrazen brojem space znakova.
int detab_string(char s[], int t){
    char sp[MAX_DUZINA_STRINGA];        // pomocni string
    int br_tab = 0;                     // brojac za tabulatore
    int len = strlen(s);                // duzina stringa s (broj karaktera u stringu s)
    int nb = 0; // broj space karaktera kojima menjamo konkretni tabulator na mestu indeksa i
    int i = 0;                          // i je index za glavni string s
    int j = 0;                          // j je index za pomocni string sp
    int k;                              // brojac for petlje

// Kopiramo s u sp, dalje citamo iz sp i pisemo u s,
// zato sto ce duzina stringa s kada tabulatore zamenimo space karakterima biti veca od pocetne
// Bezbedno je kopirati s u sp jer su oba deklarisana sa MAX_DUZINA_STRINGA.
    strcpy(sp,s);

    while(sp[j]!='\0'){                 // citamo sp od pocetka do kraja

        if( sp[j] == '\t') {            // ako ch ocitan iz sp tabulator
            br_tab++;                   // brojimo tabulatore

            // racunamo koliko space karaktera treba od trenutne pozicije (i) do sledeceg tab stopa
            nb = t-(i%t);               // ( ZAPAMTI FORMULU ! )

            for(k=0;k<nb;k++)           // u s menjamo ch sa nb space karaktera
                s[i++]=' ';             // za svaki space karakter, uvecavamo indeks i od s

            j++;                        // prelazimo na sledeci ch u sp
        }else                           // sve ostale ch iz sp koji nisu tabulator,
            s[i++]=sp[j++];             // nepromenjene stavljamo u s
    }
    s[i]='\0';                          // zavrsavamo string s

    return br_tab;                      // vraca broj tabulatora u stringu sp
} // int detab_string()


// detab_file() otvara ulazni i izlazni fajl, vrsi obradu i zatvara oba fajla.
// U ulaznom fajlu uklanja beline (space karaktere i tabulatore) sa kraja redova,
// preostale tabulatore zamenjuje odgovarajucim brojem space karaktera

// i rezultat snima u novi izlazni fajl.
// Poziva funkcije trim() i detab_string().
void detab_file(char ime_ulaznog_fajla[],
                char ime_izlaznog_fajla[],
                int t,
                int *br_karaktera,
                int *br_redova,
                int *br_tabulatora,
                int *br_skinutih_space,
                int *br_skinutih_tabulatora,
                int *br_skinutih_belina ){

    FILE* pointer_na_ulazni_fajl;
    FILE* pointer_na_izlazni_fajl;
    char red[MAX_DUZINA_STRINGA];       // jedan red teksta

    // Otvaramo ulazni tekst fajl za citanje
    if( (pointer_na_ulazni_fajl = fopen(ime_ulaznog_fajla, "r")) == NULL ) {
        fprintf(stderr, "\n\n Greska pri otvaranju ulaznog fajla %s ! \n\n", ime_ulaznog_fajla );
        exit(EXIT_FAILURE);             // prekidamo izvrsavanje programa
    }
    // Otvaramo izlazni tekst fajl za pisanje
    if( (pointer_na_izlazni_fajl = fopen(ime_izlaznog_fajla, "w")) == NULL ) {
        fprintf(stderr, "\n\n Greska pri otvaranju izlaznog fajla %s ! \n\n", ime_izlaznog_fajla );
        exit(EXIT_FAILURE);             // prekidamo izvrsavanje programa
    }
    // ucitava jedan po jedan red iz ulaznog fajla i vrsi njihovu obradu
    while( (fgets(red,MAX_DUZINA_STRINGA,pointer_na_ulazni_fajl)) != NULL ) {
        // skidamo beline sa kraja stringa red
        trim_string_end( red,
                         br_skinutih_space,
                         br_skinutih_tabulatora,
                         br_skinutih_belina);
        // provera maksimalne duzine reda iz fajla
        if (strlen(red)>MAX_DUZINA_STRINGA){
            fprintf(stderr, "\n\n %8d. red je duzi od %d karaktera ! \n\n",
                    (*br_redova)+1,MAX_DUZINA_STRINGA);
            exit(EXIT_FAILURE);         // prekidamo izvrsavanje programa
        }
        (*br_karaktera)+=strlen(red);   // ucitali smo 1 red i povecavamo ukupan broj karaktera
                                        // fgets() ucitava i broji i karakter \n na kraju reda
        (*br_redova)++;                 // ucitali smo 1 red i povecavamo ukupan broj redova
        (*br_tabulatora) += detab_string(red,t);        // racuna ukupan broj tabulatora
        fputs(red,pointer_na_izlazni_fajl);             // upisuje obradjeni red u izlazni fajl
    }

    // Zatvaranje ulaznog fajla
    if( ( fclose(pointer_na_ulazni_fajl) ) == EOF ) {   // Zatvaramo ulazni fajl
        fprintf(stderr, "\n\n Greska pri zatvaranju ulaznog fajla %s ! \n\n", ime_ulaznog_fajla);
        exit(EXIT_FAILURE);                             // prekidamo izvrsavanje programa
    }
//    else
//        printf("\n\n Fajl \"%s\" je uspesno zatvoren. \n\n", ime_ulaznog_fajla );

    // Zatvaranje izlaznog fajla
    if( ( fclose(pointer_na_izlazni_fajl) ) == EOF ) {  // Zatvaramo izlazni fajl
        fprintf(stderr, "\n\n Greska pri zatvaranju izlaznog fajla %s ! \n\n", ime_izlaznog_fajla);
        exit(EXIT_FAILURE);                             // prekidamo izvrsavanje programa
    }
//    else
//        printf("\n\n Fajl \"%s\" je uspesno zatvoren. \n\n", ime_izlaznog_fajla );

} // void detab_file()



int main(int argc, char *argv[]) {

    char ime_ulaznog_fajla[MAX_DUZINA_STRINGA];
    char ime_izlaznog_fajla[MAX_DUZINA_STRINGA];
    char red[MAX_DUZINA_STRINGA];   // jedan red teksta
    int ch;                         // Ascii kod jednog karaktera
    int br_karaktera = 0;           // Ukupan broj karaktera u fajlu
    int br_redova = 0;              // Ukupan broj redova u fajlu
    int br_tabulatora = 0;          // Ukupan broj tabulatora u fajlu
    int br_skinutih_space = 0;      // Ukupan broj skinutih space sa krajeva redova fajla
    int br_skinutih_tabulatora = 0; // Ukupan broj skinutih tabulatora sa krajeva redova fajla
    int br_skinutih_belina = 0;     // Ukupan broj skinutih belina sa krajeva redova fajla
    int t=0;                        // razmak izmedju tab stopova (izrazen brojem space karaktera)

// broj argumenata komandne linije pri pozivu programa
// detab                , argc = 1, to je ime samog programa
// detab fajl.txt       , argc = 2, to je ime samog programa i ime ulaznog fajla
// detab fajl.txt 4     , argc = 3, to je ime samog programa, ime ulaznog fajla i t

// Zamena tabulatora space karakterima vrsi se samo ako je argc=3 i t>0
// inace se samo ispisuje opis programa.
// printf("\n argc = %d \n ", argc);
// argv[0] je ime samog programa,   (detab.exe)
// argv[1] je ime ulaznog fajla,    (fajl.txt)
// argv[2] je t,                    (4)

    if( argc == 3 ) {                                   // ako su navedeni svi argumenti

        t = atoi(argv[2]);                              // pretvaranjem stringa argv[2] u integer dobijamo t

        if (t<=0) {                                     // provera da li je t pozitivan broj
            fprintf(stderr, "\n GRESKA: t = %d nije pozitivan broj ! \n\n",t);
            system("PAUSE");
            exit(EXIT_FAILURE);                         // prekidamo izvrsavanje programa
        }

        // provera da li funkcija strcpy() ima dovoljno mesta u memoriji
        if (strlen(argv[1]) > MAX_DUZINA_STRINGA-5) {   // -5 zbog .txt i \0 za ime_izlaznog_fajla
            fprintf(stderr, "\n GRESKA: Ime ulaznog fajla \"%s\" je duze od %d karaktera ! \n\n",
                    argv[1], MAX_DUZINA_STRINGA-5);
            system("PAUSE");                            // pauziramo zatvaranje ekrana
            exit(EXIT_FAILURE);                         // prekidamo izvrsavanje programa
        }
                                                        // formiramo imena ulaznog i izlaznog fajla
        strcpy(ime_ulaznog_fajla,argv[1]);              // od argv[1] pravimo ime ulaznog fajla
        strcpy(ime_izlaznog_fajla,ime_ulaznog_fajla);   // ime izlaznog fajla je kao ime ulaznog
        strcat(ime_izlaznog_fajla,".txt");              // sa dodatkom .txt
    }

    if( argc != 3 || t<=0 ) {   // ispis opisa programa, ako nisu ispravno navedeni svi argumenti ImeFajla i t
        printf("\n DETAB ImeFajla 4 \n"
               "\n u tekst fajlu ImeFajla tabulatore zamenjuje odgovarajucim \n"
               "\n brojem space karaktera i rezultat snima u ImeFajla.txt \n\n");
        system("PAUSE");                                // pauziramo zatvaranje ekrana
        exit(EXIT_FAILURE);                             // prekidamo izvrsavanje programa
    }

    // poziv funkcije sa njenim argumentima
    detab_file( ime_ulaznog_fajla,
                ime_izlaznog_fajla,
                t,
                &br_karaktera,
                &br_redova,
                &br_tabulatora,
                &br_skinutih_space,
                &br_skinutih_tabulatora,
                &br_skinutih_belina);

    // Prikaz rezultata obrade ulaznog fajla:
    printf("\n U ulaznom fajlu \"%s\" ima: \n\n", ime_ulaznog_fajla );
    printf("%8d karaktera \n\n", br_karaktera);         // Ukupan broj karaktera u ulaznom fajlu
    printf("%8d redova \n\n", br_redova);               // Ukupan broj redova u ulaznom fajlu
    printf("%8d tabulatora koji su zamenjeni sa space\n\n", br_tabulatora);
    printf("%8d skinutih space sa krajeva redova \n\n", br_skinutih_space);
    printf("%8d skinutih tabulatora sa krajeva redova \n\n", br_skinutih_tabulatora);
    printf("%8d skinutih belina (space karaktera i tabulatora) sa krajeva redova \n\n", br_skinutih_belina);
    printf(" Izlazni fajl je: \"%s\" \n\n",ime_izlaznog_fajla);

    system("PAUSE");                                    // pauziramo zatvaranje ekrana
    return 0;                                           // vracamo 0, program je uspesno zavrsen
} // int main()