Cod sursa(job #1349770)

Utilizator alexandru.eliseiAlexandru Elisei alexandru.elisei Data 20 februarie 2015 14:33:13
Problema Convertor Scor 20
Compilator c Status done
Runda rosedu_cdl_2015 Marime 10.8 kb
/*
 * 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;
}