/*
 * pipe01.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 binarios de longitud fija en pipe
 * 
 * padre: proceso escritor que graba en pipe p1 datos de clientes
 * hijo: proceso lector que lee datos del pipe p1 y los muestra en
 *       pantalla
 * los datos de clientes son binarios, de longitud fija, almacenados
 * en estructura t_cliente
 */


#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>

struct t_cliente {
	int codigo;
	char nombre[30];
	double saldo;
};

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

int main(int argc, char **argv) {
	int p1[2];
	pipe(p1);
	if ( fork() ) {
		// padre escritor
		close(p1[0]);
		struct t_cliente c;
		char tmp[30];
		do {
			memset(&c,0,sizeof(struct t_cliente)); // c = NULL
			printf("Ingrese Datos de Cliente:\n");
			ingreso("Codigo:",tmp,10);
			c.codigo = atoi(tmp);
			ingreso("Nombre:",tmp,30);
			strcpy(c.nombre,tmp);
			ingreso("Saldo :",tmp,20);
			c.saldo = atof(tmp);
			ingreso("Grabo los datos? (S/N)",tmp,3);
			if ( strcasecmp(tmp,"S") == 0 ) {
				// grabo en pipe
				grabar(p1[1],&c,sizeof(struct t_cliente));
			}
			ingreso("Desea Salir? (S/N)",tmp,3);
		} while(strcasecmp(tmp,"N") == 0);
		close(p1[1]);
		wait(0);
	} else {
		// hijo lector
		close(p1[1]);
		struct t_cliente c;
		while(leer(p1[0],&c,sizeof(struct t_cliente))) {
			printf("Datos del Cliente:\n");
			printf("Codigo: [%d]\n",c.codigo);
			printf("Nombre: [%s]\n",c.nombre);
			printf("Saldo : [%10.2lf]\n",c.saldo);
			printf("--------------------------------------\n");
		}
		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(): 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(): grabe %d bytes y esperaba grabar %d!\n",n,largo);
}

