I'm doing an exercise for the multiple execution of commands passed by the cmd arguments.
The description of the exercise is: "The program must read the file line by line, executing the command specified in the command line once for each line, replacing at each occurrence of '@' in the command line, the contents of the current line of the file."
But as soon as I execute ./file_exec esempio.txt cat @
the cli shows me this error zsh illegal hardware instruction
.
I put here the various file that I've create:
main.c
#include "file_exec.h"
int main(int argc, char **argv) {
if(argc < 3) {
fprintf(stderr,
"Specificare il file di testo e il comando da eseguire.\n");
return 1;
}
char *filename = argv[1];
int cmdargc = argc - 2;
char **opz = argv 2;
char *cmd = argv[2];
char *line = (char *)malloc(sizeof(char) * BUZZ_SIZE);
int statusLine = 0;
FILE *file = fopen(filename, "r");
if(file == NULL) {
fprintf(stderr, "%s: Impossibile aprire %s: %s\n",
argv[0], argv[1], strerror(errno));
return 2;
}
int size = 1024;
do{
statusLine = fileReadLine(line, file, size); // leggo la singola linea
if(statusLine == 2){
size = 1024;
}
if(strlen(line) != 0){
int ris = executeLine(cmdargc, cmd, opz);
if(ris == -1){
fprintf(stderr, "Impossibile eseguire il comando: %s\n", cmd);
return -1;
}
}
}while(statusLine != -1);
fclose(file);
return 0;
}
file_exec.c
#include "file_exec.h"
int fileReadLine(char *line, FILE *file, int size){
if(!feof(file) && (int)strlen(line) < size){
fgets(line, size, file);
}else if((int)strlen(line) == size){
line = realloc(line, size*2);
return 2;
}else{
free(line);
return -1;
}
return 1;
}
int executeLine(int nOpz, char *cmd, char **opz){
char *pathCmd = strcat("/bin/",cmd);
pid_t pidSon = fork();
opz = addArguments(nOpz, cmd, opz);
switch(pidSon) {
case -1:
perror("fork() failed");
free(opz);
return 1;
case 0:
printf("Esecuzione di ls...\n");
execl(pathCmd, cmd, opz, NULL);
perror("exec failed");
free(opz);
return 1;
default:
wait(NULL);
printf("%s completed\n",cmd);
}
free(opz);
return 0;
}
char **addArguments(int cmdargc, char *line, char **cmdargv){
char **newArgs = (char **)malloc((cmdargc 1) * sizeof(char *));
for(int i = 0; i < cmdargc; i ){
if(strcmp(cmdargv[i], "@") == 0){
newArgs[i] = line;
}else{
newArgs[i] = cmdargv[i];
}
}
newArgs[cmdargc] = NULL;
return newArgs;
}
file_exec.h
#ifndef FILEEX_H__
#define FILEEX_H__
#define BUZZ_SIZE 1024
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>
#include <errno.h>
int fileReadLine(char *, FILE *, int);
int executeLine(int, char *, char **);
char **addArguments(int, char *, char **);
#endif
esempio.txt
file1.txt
file2.txt
file3.txt
file1.txt
Hello
file2.txt
I'm
file3.txt
italian
Makefile
file_exec: file_exec.o main.o
gcc -Wall -Werror -Wextra -std=c99 file_exec.o main.o -o file_exec
file_exec.o: file_exec.h file_exec.c
gcc -Wall -Werror -Wextra -std=c99 -c file_exec.c
main.o: file_exec.h main.c
gcc -Wall -Werror -Wextra -std=c99 -c main.c
clean:
rm -f *.o main
(I'm on an Intel Mac)
The program should show this output from this command ./file_exec esempio.txt cat @
:
Hello
I'm
italian
I've just tried to put some debugging printf after the first if statement on the main.c but it was ignored and not executed.
CodePudding user response:
You are using strcat
the wrong way
char *pathCmd = strcat("/bin/",cmd);
is adding cmd to the end of string (constant array of char) "/bin"
. And then is returning this array. To be more accurate, what it does, is going at the address "/bin"
- yes, that is an address - iterates 1 by 1 this address until it finds the terminal 0, that is the end of the string. And copy the content of address cmd
there. And obviously, you can't do that. Not from a language point of view. Both "/bin"
and cmd
are pointers to char, as wanted. And the fact that "/bin"
is a constant array of chars is not really a problem neither, since strcat
doesn't change it: it only changes its content. But that string "/bin"
is stored in an area of memory that your program is not supposed to try to write to. Which it does, when it tries to copy cmd
after the second /
of /bin/
. Hence an execution error.
You need to allocate memory for pathCmd
and concatenate there
char pathCmd[1024];
strcpy(pathCmd, "/bin/");
strcat(pathCmd, cmd);
Well, in reality, you should not do that neither. It is insecure (if cmd overflow the 1024 bytes I allocated here). So you should rather use strncat
char pathCmd[1024];
strcpy(pathCmd, "/bin/");
strncat(pathCmd, cmd, 1024-5); // 5 bytes already used by "/bin/"
Alternatively, you can use snprintf
char pathCmd[1024];
snprintf(pathCmd, 1024, "/bin/%s", cmd);
(it is 1 less line of code, and you don't need to compute the 1024-5 limit, since the limit is for the whole target)
Note that this has nothing to do with zsh
. zsh
is just the one complaining because you probably call this in a zsh script.