/*
 * pipe03.c
 * 
 * Copyright 2026 osboxes <osboxes@osboxes>
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301, USA.
 * 
 * Grabo datos no binarios de longitud variable en un pipe
 * con un formato determinado, que el lector debe interpretar
 * ejemplo: envio datos de la forma <tag>...mensaje...</tag>
 * 
 * padre: proceso escritor que graba en pipe p1 datos de longitud variable
 *        con el siguiente formato:
 *        <tag>...mensaje...</tag>
 * 
 * hijo: proceso lector que lee datos del pipe p1 y los muestra en
 *       pantalla tag x tag, separados por \n
 *       lee de a un byte y concatena mensaje
 */


#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>

void ingreso(const char *,char *,int);
int leer(int,void *,int);
void grabar(int,void *,int);
int valido(const char *);

int main(int argc, char **argv) {
	int p1[2];
	pipe(p1);
	if ( fork() ) {
		// padre escritor
		close(p1[0]);
		char msg[300],tmp[10];
		do {
			do {
				printf("Ingrese Datos de la forma <tag>..mensaje...</tag>:\n");
				ingreso(":",msg,300);
			} while(!valido(msg));
			ingreso("Grabo los datos? (S/N)",tmp,3);
			if ( strcasecmp(tmp,"S") == 0 ) {
				// grabo en pipe
				grabar(p1[1],msg,strlen(msg));
			}
			ingreso("Desea Salir? (S/N)",tmp,3);
		} while(strcasecmp(tmp,"N") == 0);
		close(p1[1]);
		wait(0);
	} else {
		// hijo lector
		close(p1[1]);
		int n,bloque=10,nn=bloque,pf; //cambiar el valor de bloque para asignar memoria de mas cantidad de bytes
		char c;
		char *buffer = (char *) malloc(bloque);
		while(leer(p1[0],&c,1)) {
			buffer[n]=c;
			buffer[n+1]='\0';
			char *ppf = strstr(buffer,"</");
			if ( ppf ) pf=ppf-buffer;
			else pf=0;
			if ( pf && buffer[n] == '>' ) {
				printf("%s\n",buffer);
				n=0;
				free(buffer);
				nn=bloque;
				buffer=(char *) malloc(bloque);
			} else {
				n++;
				//TRACE
				//printf("c=%c n=%d pf=%d buffer=[%s]\n",c,n,pf,buffer);
				if ( (n % bloque) == 0 ) { // si n llego al tamano de bloque, amplio/agrego otro bloque de espacio adicional a buffer
					nn+=bloque;
					buffer = realloc(buffer,nn);
				}
			}
		}
		close(p1[0]);
	}
	return 0;
}

//
// ATENCION: largo debe incluir espacio necesario para \0
// para ingresar "hola" se necesita un largo de 5
// 
// si se indica un largo de 5 y se ingresan 20 caracteres,
// los 15 caracteres que sobran quedan en el buffer de teclado, incluyendo el \n final
// por lo tanto, las siguientes llamadas a esta funcion, toman esos caracteres como
// ingreso y continua.. bien, para evitar ese efecto, esta funcion se modifico
// para eliminar del buffer de teclado todos los caracteres excedentes, consumiendolos con getchar()
void ingreso(const char *prompt,char *buffer,int largo) {
	printf("%s",prompt);
	memset(buffer,0,largo); // buffer = NULL;
	if ( fgets(buffer,largo,stdin) != NULL ) {
		//se ingreso algo
		if (strchr(buffer, '\n') == NULL) {
			//buffer fue truncado, se ingresaron por teclado mas caracteres del largo permitido
			//descargo con getchar() el buffer de teclado
			int c;
            while ((c = getchar()) != '\n' && c != EOF);
		} else {
			//quito el \n de string buffer
			 buffer[strcspn(buffer, "\n")] = '\0';
		}
	} else printf("ingreso(): Error en ingreso de datos!\n");
}

int leer(int fd,void *buffer,int largo) {
	int n = read(fd,buffer,largo);
	if ( !n ) return 0;
	if ( n != largo ) {
		printf("leer(): Error, lei %d bytes y esperaba leer %d!\n",n,largo);
		return 0;
	}
	return 1;
}

void grabar(int fd,void *p,int largo) {
	int n = write(fd,p,largo);
	if ( n != largo ) printf("grabar(): Error, grabe %d bytes y esperaba grabar %d!\n",n,largo);
}

//devuelvo verdad si p apunta a un string de la forma <tag>...</tag>
//caso contrario devuelve falso
int valido(const char *p) {
	if ( !p ) return 0; //p debe apuntar a algo
	if ( *p != '<' ) return 0; // lo apuntado por p debe comenzar con el caracter <
	char *pi = strstr(p,">"); 
	if ( !pi ) return 0; // debe existir un caracter >
	if ( (pi-p) <= 1 ) return 0; // la distancia entre < y > debe ser >= 1
	char *pf = strstr(p,"</");
	if ( !pf ) return 0; // debe existir un </
	char *pff = strstr(pf+2,">"); 
	if ( !pff ) return 0; // y debe existir un > luego de </
	int n1=1,n2=pf+2-p;
	// comparo el tag inicial contra el tag final que debe ser el mismo
	while(p[n1] != '>') {
		//printf("valido, cmp p[%d]=%c vs p[%d]=%c\n",n1,p[n1],n2,p[n2]);
		if ( p[n1] != p[n2] ) return 0;
		n1++;n2++;
	}
	return 1;
}
