/*
* Fisierul in format JSON este un vector cu elemente hash-uri.
* Fiecare hash este format din perechi cheie:valoare.
*
* Parsez fiecare linie caracter cu caracter si stochez caracterele pe care le
* intalnesc intr-un buffer pentru cuvinte. Cand intalnesc un caracter
* delimitator sterg simbolurile specifice JSON si adaug cuvantul curent fie in
* stringul de valori, fie in cel de chei.
*
* La sfarsitul fiecarui hash scriu cele doua stringuri in fisier.
*/
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#define LG_MAX 1025
#define START_CHEIE '"' /* inceputul cheii */
#define STOP_CHEIE '"' /* sfarsitul cheii */
#define DELIM_CV ':' /* delimitator cheie valoare */
#define STOP_VAL ',' /* sfarsitul valorii */
#define START_DATE '[' /* inceputul datelor de citit */
#define STOP_DATE ']' /* sfarsitul datelor de citit */
#define START_HASH '{' /* semnaleaza inceputul unui hash */
#define STOP_HASH '}' /* semnaleaza sfarsitul unui hash */
/* Masturbare intelectuala (copyright RD) */
#undef END_OF_LINE
#define END_OF_LINE(a) (((a) == NULL || \
((a)[0]) == EOF || \
((a)[0]) == '\n' || \
((a)[0]) == '\0') ? 1 : 0)
#undef EROARE
#define EROARE(c, s) do { \
fprintf(stderr, "\nEROARE - %s\n\n", (s));\
return (c); \
} while(0)
#undef SKIP_BLANKS
#define SKIP_BLANKS(s) if ((s) != NULL) \
for (; isblank(*(s)); (s)++)
struct Flag {
unsigned short in_hash;
unsigned short in_date;
unsigned short citeste_antet;
unsigned short citeste_valoare;
unsigned short citeste_cheie;
unsigned short citeste_linie;
};
struct Hash {
char **chei;
int n_chei;
int maloc_chei;
unsigned short chei_scrise;
char **valori;
int n_valori;
int maloc_valori;
};
/* Stocheaza secventa de prelucrat in dest si avanseaza sursa */
int carve(char **dest, /* pointer catre stringul destinatie */
int *maloc_phash,
char **sursa, /* pointer catre stringul sursa */
char delim);
/* Stocheaza cheia in dest si avanseaza sursa */
int carve_key(char **dest, /* pointer catre stringul destinatie */
int *maloc_phash,
char **sursa, /* pointer catre stringul sursa */
char delim);
/* Stocheaza valoarea in dest si avanseaza sursa */
int carve_val(char **dest, /* pointer catre stringul destinatie */
int *maloc_phash,
char **sursa, /* pointer catre stringul sursa */
char delim);
/* Sterge caracterele nevalide */
char *strip_key(char *s);
char *strip_val(char *s);
/* Adauga cuvintele curente */
int add_key(struct Hash *h, char *s);
int add_val(struct Hash *h, char *s);
/* Scrie datele in fisier, returneaza 0 in caz de eroare */
int flush_data(FILE *f, struct Hash *h);
/* Initializeaza hash-ul inainte de parcurgerea fisierului */
int init_hash(struct Hash *h);
/* Reseteaza hash-ul dupa ce a fost scris in fisier */
int reset_hash(struct Hash *h);
int main(void)
{
FILE *in, *out;
char buflin[LG_MAX]; /* buffer citire linii */
char *pbuflin; /* parcurge bufferul de citire linii */
char *phash;
int maloc_phash; /* Memoria hashului de prelucrat */
char *cuv; /* Cheia/valoarea curenta */
int maloc_cuv;
struct Hash hash;
struct Flag flags = {0};
if ((in = fopen("convertor.in", "r")) == NULL)
EROARE(1, "Fisierul convertor.in nu a putut fi deschis.");
if ((out = fopen("convertor.out", "w")) == NULL)
EROARE(2, "Fisierul convertor.out nu a putut fi scris.");
maloc_phash = LG_MAX;
phash = (char *)malloc(maloc_phash * sizeof(char));
phash[0] = '\0';
maloc_cuv = LG_MAX;
cuv = (char *)malloc(maloc_cuv * sizeof(char));
flags.citeste_antet = 1;
if (init_hash(&hash) == 0)
EROARE(4, "Initializare hash a esuat.");
while (fgets(buflin, LG_MAX, in) != NULL) {
pbuflin = buflin;
flags.citeste_linie = 0;
if (flags.in_date == 0)
if ((pbuflin = strchr(pbuflin, START_DATE)) != NULL) {
flags.in_date = 1;
pbuflin++;
}
SKIP_BLANKS(pbuflin);
if (END_OF_LINE(pbuflin))
continue;
/* Prelucrez linia curenta */
while (!(END_OF_LINE(pbuflin)) && flags.citeste_linie == 0) {
if (flags.in_hash == 0) {
for (; *pbuflin != '\0'; pbuflin++) {
/* Am terminat de citit date */
if (*pbuflin == STOP_DATE)
return 0;
if (*pbuflin == START_HASH)
break;
}
if (*pbuflin == '\0')
continue;
flags.in_hash = 1;
phash[0] = '\0';
pbuflin++;
SKIP_BLANKS(pbuflin);
if (END_OF_LINE(pbuflin))
continue;
}
if (strchr(pbuflin, STOP_HASH) == NULL) {
/* Prelucrez intreaga linie */
strcpy(phash, pbuflin);
/* Fortez citirea unei noi linii */
flags.citeste_linie = 1;
} else {
carve(&phash, &maloc_phash, &pbuflin, STOP_HASH);
/* La urmatoarea iteratie caut un nou hash */
flags.in_hash = 0;
}
/* Prelucrez hashul curent */
while (!END_OF_LINE(phash)) {
SKIP_BLANKS(phash);
if (END_OF_LINE(phash))
break;
/*
* Daca nu citesc nici cheie nici valoare atunci
* caut markerul pentru cheie
*/
if (flags.citeste_cheie == 0 &&
flags.citeste_valoare == 0 &&
strchr(phash, START_CHEIE) != NULL) {
flags.citeste_cheie = 1;
phash = strchr(phash, START_CHEIE);
/* Sar peste delimitator */
phash++;
}
if (flags.citeste_cheie){
carve_key(&cuv, &maloc_cuv, &phash,
STOP_CHEIE);
if (flags.citeste_antet) {
strip_key(cuv);
add_key(&hash, cuv);
}
flags.citeste_cheie = 0;
}
if (flags.citeste_cheie == 0 &&
flags.citeste_valoare == 0) {
/*
* Daca nu citesc nici cheie, nici nu
* gasesc delimitatorul pentru valori
* atunci citesc urmatoarea linie.
*/
if (strchr(phash, DELIM_CV) != NULL) {
flags.citeste_valoare = 1;
phash = strchr(phash, DELIM_CV);
/* Sar peste delimitator */
phash++;
SKIP_BLANKS(phash);
if (END_OF_LINE(phash))
break;
} else {
flags.citeste_linie = 1;
break;
}
}
/* Citesc o valoare */
if (flags.citeste_valoare) {
carve_val(&cuv, &maloc_cuv, &phash,
STOP_VAL);
strip_val(cuv);
add_val(&hash, cuv);
flags.citeste_valoare = 0;
}
}
/* Am terminat de citit un hash */
if (flags.in_hash == 0) {
if (flush_data(out, &hash) == 0)
EROARE(3, "Scriere date CSV.");
reset_hash(&hash);
/* Nu mai citesc antetul la urmatorul hash */
flags.citeste_antet = 0;
}
}
}
/* Programul nu s-a terminat prin intalnirea terminatorului de date */
EROARE(5, "Date JSON incomplete sau inexistente.");
}
/*
* Stocheaza hashul de prelucrat in dest si avanseaza sursa.
* return 0: nu am gasit delimitatorul
* 1: am gasit delimitatorul
*/
int carve(char **dest, /* pointer catre stringul destinatie */
int *maloc,
char **sursa, /* pointer catre stringul sursa */
char delim)
{
char *aux;
int len;
aux = strchr(*sursa, delim);
/* Nu am gasit delimitatorul */
if (aux == NULL)
len = strlen(*sursa);
else
/* Copiez si terminatorul de hash */
len = aux - *sursa + 1;
if (*maloc < len + 1) {
for (; *maloc < len + 1; (*maloc) <<= 1)
;
*dest = (char *)realloc(*dest, (*maloc) *
sizeof(char));
}
if (*dest == NULL);
//EROARE(3, "Alocare memorie carve.");
strncpy(*dest, *sursa, len);
(*dest)[len] = '\0';
/* Trec peste delimitator */
*sursa = aux + 1;
return 1;
}
int carve_key(char **dest, /* pointer catre stringul destinatie */
int *maloc,
char **sursa,
char delim) /* pointer catre stringul sursa */
{
char *aux;
int len;
aux = strchr(*sursa, delim);
len = aux - *sursa;
if (*maloc < len + 1) {
for (; *maloc < len + 1; (*maloc) <<= 1)
;
*dest = (char *)realloc(*dest, (*maloc) *
sizeof(char));
}
if (*dest == NULL);
//EROARE(3, "Alocare memorie carve.");
strncpy(*dest, *sursa, len);
(*dest)[len] = '\0';
/* Trec peste delimitator */
*sursa = aux + 1;
return 1;
}
int carve_val(char **dest, /* pointer catre stringul destinatie */
int *maloc,
char **sursa,
char delim) /* pointer catre stringul sursa */
{
char *aux;
int len;
aux = strchr(*sursa, delim);
if (aux == NULL)
aux = strchr(*sursa, STOP_HASH);
if (aux == NULL)
aux = strchr(*sursa, '\n');
len = aux - *sursa;
if (*maloc < len + 1) {
for (; *maloc < len + 1; (*maloc) <<= 1)
;
*dest = (char *)realloc(*dest, (*maloc) *
sizeof(char));
}
if (*dest == NULL);
//EROARE(3, "Alocare memorie carve.");
strncpy(*dest, *sursa, len);
(*dest)[len] = '\0';
/* Trec peste delimitator */
*sursa = aux + 1;
return 1;
}
/* Sterge caracterele nevalide din cheie */
char *strip_key(char *s)
{
int i, j;
if (s == NULL)
return NULL;
for (i = 0; s[i] != '"' && s[i] != '\0'; i++)
;
if (s[i] == '\0')
return NULL;
i++;
j = 0;
while (s[i] != '"')
if (isprint(s[i]))
s[j++] = s[i++];
else
i++;
s[j] = '\0';
return s;
}
/* Sterge caracterele nevalide din valoare */
char *strip_val(char *s)
{
int i, j;
if (s == NULL)
return NULL;
for (i = 0; (s[i] != '\0') && (isblank(s[i]) || (!isprint(s[i]))); i++)
;
if (s[i] == '\0')
return NULL;
if (s[i] == '"')
return strip_key(s);
j = 0;
/* Blankurile apar numai intre ghilimele */
while (s[i] != '\0' && !isblank(s[i]))
if (isprint(s[i]))
s[j++] = s[i++];
else
i++;
s[j] = '\0';
return s;
}
/* Scrie datele in fisier, returneaza 0 in caz de eroare */
int flush_data(FILE *f, struct Hash *h)
{
int i;
if (h->chei_scrise == 0) {
for (i = 0; i < h->n_chei; i++)
if (fprintf(f, "%s,", h->chei[i]) == 0)
return 0;
fprintf(f, "\n");
h->chei_scrise = 1;
}
for (i = 0; i < h->n_valori; i++)
if (fprintf(f, "%s,", h->valori[i]) == 0)
return 0;
fprintf(f, "\n");
h->n_valori = 0;
fflush(f);
return 1;
}
/* Returneaza 0 in caz de esec la alocare de memorie */
int add_key(struct Hash *h, char *s)
{
if (h->n_chei == h->maloc_chei) {
h->maloc_chei <<= 1;
h->chei = (char **)realloc(h->chei,
h->maloc_chei * sizeof(char *));
}
if (h->chei == NULL)
return 0;
h->chei[h->n_chei++] = strdup(s);
return 1;
}
/* Returneaza 0 in caz de esec la alocare de memorie */
int add_val(struct Hash *h, char *s)
{
if (h->n_valori == h->maloc_valori) {
h->maloc_valori <<= 1;
h->valori = (char **)realloc(h->valori,
h->maloc_valori * sizeof(char *));
}
if (h->valori == NULL)
return 0;
h->valori[h->n_valori++] = strdup(s);
return 1;
}
/* Initializeaza hash-ul inainte de parcurgerea fisierului */
int init_hash(struct Hash *h)
{
h->chei_scrise = 0;
h->n_chei = 0;
h->maloc_chei = LG_MAX;
h->chei = (char **)malloc(h->maloc_chei * sizeof(char *));
h->n_valori = 0;
h->maloc_valori = LG_MAX;
h->valori = (char **)malloc(h->maloc_valori * sizeof(char *));
if (h->chei == NULL || h->valori == NULL)
return 0;
return 1;
}
/* Reseteaza hash-ul dupa ce a fost scris in fisier */
int reset_hash(struct Hash *h)
{
int i;
for (i = 0; i < h->n_valori; i++)
free(h->valori[i]);
h->n_valori = 0;
return 1;
}