Minishell / msh.2022b / main.c
main.c
Raw
/*-
 * main.c
 * Minishell C source
 * Shows how to use "obtain_order" input interface function.
 *
 * Copyright (c) 1993-2002-2019, Francisco Rosales <frosal@fi.upm.es>
 * Todos los derechos reservados.
 *
 * Publicado bajo Licencia de Proyecto Educativo Práctico
 * <http://laurel.datsi.fi.upm.es/~ssoo/LICENCIA/LPEP>
 *
 * Queda prohibida la difusión total o parcial por cualquier
 * medio del material entregado al alumno para la realización
 * de este proyecto o de cualquier material derivado de este,
 * incluyendo la solución particular que desarrolle el alumno.
 *
 * DO NOT MODIFY ANYTHING OVER THIS LINE
 * THIS FILE IS TO BE MODIFIED
 */

#include <stddef.h>			/* NULL */
#include <stdio.h>			/* setbuf, printf */
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/times.h>
#include <fcntl.h>
#include <signal.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <pwd.h>
#include <dirent.h> 
#include <glob.h>

extern int obtain_order();		/* See parser.y for description */
extern char *environ[];

// ---------------------------------------------------------------------------- Funciones ------------
// Mandatos
int mandato_simple(char **argv){
	execvp(argv[0], argv);
	perror("Error al ejecutar el comando: ");
	return 1;
}

int mi_cd(char **argv){
	char *directorio;
	if(argv[1]){
		directorio=argv[1];
		if(argv[2]){
			fprintf(stderr,"cd: too many arguments\n");
			return 1;
		}
	}
	else
		directorio=(getenv("HOME"));
	
	if(chdir(directorio)==0){
		char cwd[1024];
		getcwd(cwd,sizeof(cwd));
		fprintf(stdout,"%s\n",cwd);
		return 0;
	}
	fprintf(stdout,"%s\n",directorio);
	perror("Error cd: ");
	return 1;
}

int mi_umask(char **argv){
	char *mascara;
	char *endptr;
	if((mascara=(argv[1]))){
		if(argv[2]){
			fprintf(stderr, "umask: too many arguments\n");
			return(1);
		}
	}
	else mascara = "0";
	mode_t nueva_mascara = strtol(mascara,&endptr,8);
	if(endptr[0]||nueva_mascara==LONG_MIN||nueva_mascara==LONG_MAX){
		fprintf(stderr,"umask: error en la mascara\n");
		return 1;
	}
	umask(nueva_mascara);
	fprintf(stdout,"%o\n",nueva_mascara);
	return(0);
}

int mi_time(char **argv){
	struct tms buf;
	int estado;
	char **argv_1;
	int tics_s;
	tics_s = sysconf(_SC_CLK_TCK);
	double usuario=0, sistema=0;

	if(argv[1]){ //si se le ha pasado mandato
		argv_1=&argv[1];
		switch(fork()){
			case -1:
				perror("Error al llamar a fork: ");
				return 1;
			//HIJO
			case 0:
				mandato_simple(argv_1);
				exit(1);
			default:
				if (wait(&estado) == -1){
					perror("time: error wait: ");
					return 1;
				}
				else if (!WIFEXITED(estado)){
					fprintf(stderr,"El hijo no ha terminado con exito");
					return 1;
				}
				else if (times(&buf) == -1){
					perror("time: times(): ");
					return 1;
				}
				usuario=((double)buf.tms_cutime)/tics_s;
				sistema=((double)buf.tms_cstime)/tics_s;
		}		
	}
	else {
		if (times(&buf) == -1){
				perror("time: times(): ");
				return 1;
		}
		usuario=((double)buf.tms_utime)/tics_s;
		sistema=((double)buf.tms_stime)/tics_s;
	}
	
	fprintf(stdout,"%d.%03du %d.%03ds %d.%03dr\n", 
	(int)usuario, (int)(1000*(usuario-(int)usuario)), //usuario
	(int)sistema, (int)(1000*(sistema-(int)sistema)), //sistema
	(int)usuario + (int)sistema, (int)(1000*(usuario + sistema - (int)usuario - (int)sistema))); //conjunto
	
	return 0;
}

int mi_read(char **argv){
	int argc=0;
	char *buffer=calloc(100,sizeof(char));
    fgets(buffer, 100, stdin);
	char *token;
	char *var=NULL;
	if(!argv[1]){
		fprintf(stderr,"Read sin argumentos\n");
		return 1;
	}
		 // no hay variables por cambiar

	if(argv[2]) // si hay mas de una variable
		token = strtok(buffer, " \t\n\r");
	else
		token = strtok(buffer, "\n\r"); // si es la ultima variable, se le asignan todos los valores que quedan
	
	for(argc=1;argv[argc];argc++){
							
		if (token==NULL) // comprobar que quedan valores
			break;
		
		// Se modifica el valor de la variable de entorno.
		if ((var = (char *)malloc(strlen(token)+strlen(argv[argc]))+strlen("=")+2) == NULL){
			perror("mi_read: malloc:");
			return 0;
		}
        strcpy(var, argv[argc]); //copia el nombre de la variable
        strcat(var, "="); //suma el igual
        strcat(var, token); //suma el token o la palabra
        if(putenv(var)!=0){ //cambia el valor
			perror("mi_read: putenv: ");
			return 1;
		}
		//printf("%s\n",getenv(argv[argc]));
		if(argv[argc+2])
			token = strtok(NULL, " \t\n\r");
		else
			token = strtok(NULL, "\n\r"); // si es la ultima variable, se le asignan todos los valores que quedan
	}
	return 0;
}

int metacaracteres(char ***argvv){
	int argvc;
	int argc;
	char *valor_var = calloc(128,sizeof(char));
	char *despues = calloc(100,sizeof(char));
	char *antes = calloc(100,sizeof(char));
	struct passwd *usuario;
	int formato;
	char **argv = NULL;
	char *var = calloc(100,sizeof(char));
	for(argvc = 0; (argv = argvv[argvc]); argvc++){
		for(argc = 0; argv[argc]; argc++){
			if(argv[argc][0]=='$'){
				formato=sscanf(argv[argc],"$%[_a-zA-Z0-9]%[^ \n\t\r]",var,despues);
				strcpy(antes,"");
			}
			else formato=sscanf(argv[argc],"%[^$]$%[_a-zA-Z0-9]%[^ \n\t\r]",antes,var,despues);
			if(formato==-1){
				fprintf(stderr,"Error en la llamada sscanf\n");
				return(1);
			}
			if(formato>1||(argv[argc][0]=='$'&&formato>0)){ // si no se ha encontrado $, siguiente
				//printf("%s\n",despues);
				valor_var=getenv(var); // obtenemos el valor de la variable
				if(valor_var==NULL){
					continue; //no lo cambiamos si no lo encuentra
				}
				argv[argc]=realloc(argv[argc],strlen(valor_var)+strlen(despues)+strlen(antes)+2);
				if(argv[argc]==NULL){
					perror("metacaracteres: realloc: ");
					return 1;
				}
				strcat(antes,valor_var);
				strcat(antes,despues);
				strcpy(argv[argc],antes);

				memset(antes,0,100);
				memset(despues,0,100);
			}
			
			if(argv[argc][0]=='~'){
				if(!argv[argc][1]){
					valor_var=getenv("HOME");
				}
				else{
					formato=sscanf(&argv[argc][1],"%[_a-zA-Z0-9]",var);
					if(formato==-1){
						fprintf(stderr,"Error en la llamada sscanf2\n");
						return(1);
					}
					if(formato==0)
						continue;
					
					usuario=getpwnam(var);
					valor_var=usuario->pw_dir;
				}
			
				if(valor_var==NULL){
					strcpy(argv[argc],"\0");
					continue;
				}

				argv[argc]=realloc(argv[argc],strlen(valor_var));
				strcpy(argv[argc],valor_var);
			}
		
		}
	}
	return 0;
}

int tiene_barra(char *string){
	int i;
	for(i=0;string[i];i++)
		if(string[i]=='/') return 1;
	return 0;
}

int tiene_cuestion(char *string){
	int i;
	for(i=0;string[i];i++)
		if(string[i]=='?') return 1;
	return 0;
}

// ----------------------------------------------------------------------------- Main -----------------------------
int main(void)
{
	char ***argvv = NULL;
	int argvc;
	char **argv = NULL;
	int argc;
	char *filev[3] = { NULL, NULL, NULL };
	int bg;
	int ret;
	pid_t pid = 1;

	setbuf(stdout, NULL);			/* Unbuffered */
	setbuf(stdin, NULL);

	int original_stdin, original_stdout, original_stderr, ultimo_stdout=6;

	int bgpid;
	int fd [2];
	int salida_viejo=7, entrada_nuevo; //Salida del viejo pipe y entrada del nuevo pipe

	// Configurar manejador de señales
	signal(SIGINT, SIG_IGN);
	signal(SIGQUIT, SIG_IGN);

	//Guardado estandares originales que se cierran si un exec es ejecutado con exito
	original_stdin=fcntl(0, F_DUPFD_CLOEXEC,0);
	original_stdout=fcntl(1, F_DUPFD_CLOEXEC,0);
	original_stderr=fcntl(2, F_DUPFD_CLOEXEC,0);

	// Declaracion de las variables especiales
	// mypid
	pid_t mypid = getpid();
	char *string_pid=calloc(32,sizeof(char));
	sprintf(string_pid, "%d", mypid);
	//printf(string_pid);
	char *var = calloc(64,sizeof(char));
	strcpy(var,"mypid=");
	strcat(var,string_pid);
	putenv(var);

	// prompt
	putenv("prompt=msh> ");

	// -------------------------------------------------------------------minichel + prompt-----------------
	while (1) {

		// Nueva linea
		fprintf(stderr, "%s", "msh> ");	/* Prompt */
		ret = obtain_order(&argvv, filev, &bg);
		if (ret == 0) break;		/* EOF */
		if (ret == -1) continue;	/* Syntax error */
		argvc = ret - 1;			/* Line */
		if (argvc == 0) continue;	/* Empty line */

		// Sustitucion de $ o ~
		if(metacaracteres(argvv)==1) // si surge un error al cambiar los metacaracteres, no se ejecuta
			continue;

		// Cambio entrada, salida y salida de error estandar:
		// Entrada estandar
		if (filev[0]){
			close(0);
			if(open(filev[0], O_RDONLY)<0){
				perror("Error al arbir el fichero de entrada: ");
				dup2(original_stdin,0);
				continue; //nueva linea
			}
		}
		// Salida estandar. (guardamos el stdout para futuro)
		if (filev[1]){
			close(1);
			creat(filev[1], 0666);
		}
		// Salida de error estandar
		if (filev[2]){
			close(2);
			creat(filev[2], 0666);
		}
		dup2(0,salida_viejo); // la primera entrada es la entrada estandar

		if((ultimo_stdout=fcntl(1, F_DUPFD_CLOEXEC))==-1){
			perror("error:");
			continue;
		} // guardado de la salida actual

		for(argvc = 0; argvv[argvc]; argvc++){
			for(argc = 0; argvv[argvc][argc]; argc++){
				if(!tiene_barra(argvv[argvc][argc])&&tiene_cuestion(argvv[argvc][argc])){
					char **lista_nombres;
					int size;
					glob_t globo;
					if(glob(argvv[argvc][argc],GLOB_ERR,NULL, &globo)==0){
						lista_nombres=globo.gl_pathv;
						size=globo.gl_pathc;
						if(size>0){
							int size_argv;
							for(size_argv=0;argvv[argvc][size_argv];size_argv++);
							argvv[argvc] = realloc(argvv[argvc], sizeof(char*)*(size_argv + size));

							memmove(&argvv[argvc][argc+size], &argvv[argvc][argc+1], sizeof(char*)*(size_argv-argc));
							memmove(&argvv[argvc][argc], lista_nombres, sizeof(char*)*(size));
						}
					}
				}
			}
		}

		// Bucle para argumentos
		for (argvc = 0; (argv = argvv[argvc]); argvc++){
			// Creacion pipe nuevo mientras no sean ultimo hijo
			if(argvc<ret-2){
				if (pipe(fd) < 0){
					perror("Error al crear el pipe\n");
					continue;
				}
				entrada_nuevo=fd[1];
				//guardamos fd[0] para el siguiente
			}
			// Si es el ultimo la entrada del pipe es la salida estandar
			else
				entrada_nuevo=ultimo_stdout; //la salida del ultimo es la especificada
			
			// Red. entrada estandar
			dup2(salida_viejo,0);
			close(salida_viejo);
			// Red. salida estandar
			dup2(entrada_nuevo, 1);
			close(entrada_nuevo);

			//unicos casos que ejecuta el minichel
			if(argvc==ret-2 && !bg && (!strcmp(argv[0],"cd") || !strcmp(argv[0],"umask") 
			|| !strcmp(argv[0],"time") || !strcmp(argv[0],"read")))
				break;

			// creacion y ejecucion hijo
			switch((pid=fork())){
				case -1:
						perror("Error al llamar a fork: ");
						break;
				//HIJO
				case 0:
					//cerrar descriptores que no se van a usar
					//fprintf(stderr,"hijo %d\n", getpid());
					if (argvc>0) close(fd[0]);

					//opcion background
					if(!bg){
						signal(SIGINT, SIG_DFL);
						signal(SIGQUIT, SIG_DFL);
					}

					//mandatos internos
					if(!strcmp(argv[0],"cd"))
						exit(mi_cd(argv));
					if(!strcmp(argv[0],"umask"))
						exit(mi_umask(argv));
					if(!strcmp(argv[0],"time"))
						exit(mi_time(argv));
					if(!strcmp(argv[0],"read"))
						exit(mi_read(argv));
					
					//mandatos simples
					mandato_simple(argv);
					exit(0);
			}
			//actualizacion punteros
			if(argvc<ret-2) salida_viejo=fd[0];
		}

		if(argvc==ret-2) {
			int status;
			if(!strcmp(argv[0],"cd"))
				status=mi_cd(argv);
			else if(!strcmp(argv[0],"umask"))
				status=mi_umask(argv);
			else if(!strcmp(argv[0],"time"))
				status=mi_time(argv);
			else if(!strcmp(argv[0],"read"))
				status=mi_read(argv);
			char *var_status = calloc(64,sizeof(char));
			char *str_status = calloc(32,sizeof(char));
			sprintf(str_status, "%d", status);
			strcpy(var_status,"status=");
			strcat(var_status,str_status);
			putenv(var_status);
		}
		else {
			bgpid = pid;
			if(bg){ 
				fprintf(stdout,"[%d]\n",bgpid);
				char *var_bgpid = calloc(64,sizeof(char));
				strcpy(var_bgpid,"bgpid=");
				char *str_bgpid = calloc(32,sizeof(char));
				sprintf(str_bgpid, "%d", bgpid);
				strcat(var_bgpid,str_bgpid);
				putenv(var_bgpid);
			}
			else {
				int status;
				waitpid(pid,&status,0);
				char *var_status = calloc(64,sizeof(char));
				char *str_status = calloc(32,sizeof(char));
				sprintf(str_status, "%d", status);
				strcpy(var_status,"status=");
				strcat(var_status,str_status);
				putenv(var_status);
			}
		}

		// Vuelta estandar original
		dup2(original_stdin,0);
		dup2(original_stdout,1);
		dup2(original_stderr,2);
	}

	//Cerrado de descriptores de fichero
	if(close(original_stderr)==-1) fprintf(stderr,"Error al cerrar descriptor\n");
	if(close(original_stdin)==-1) fprintf(stderr,"Error al cerrar descriptor\n");
	if(close(original_stdout)==-1) fprintf(stderr,"Error al cerrar descriptor\n");

	return 0;
}