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.
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.
Related
i'm coding this program that allows you to encrypt files using vernam cipher in C language. i would be glad if you could inspect my code and tell me if i can make some improvements to it or if there are any error.
The request they gave me is:
The goal is to develop a simple application that allows you to encrypt files using a variant of the Vernam cipher, here called bvernan. The proposed algorithm exploits a simple property of the binary operator XOR: (A XOR B) XOR B = A Given a sequence of k-bytes b0 ... bk-1 (called key), the encoding function of a sequence of bytes d0 ... dN, the encoding / decoding function follows the following simple procedure. The sequence d0 ... dN is first divided into N / k blocks (integer division), D0, ..., D [(N / k) -1] each of which consists of exactly k bytes (apart from the 'last sequence which, obviously, may contain a smaller number of bytes). Subsequently each sequence Dj = dj, 0 ... dj, k-1 is transformed into the sequence D'j = d'j, 0 ... d'j, k-1 such that for each i: d'j, i = b (j + i) mod k XOR dj, i That is, the byte in position i of block j is placed in XOR with the byte (j + i) mod k of the key. The output sequence will then be obtained from the juxtaposition of the sequences D'0, ..., D '[(N / k) -1].
This is the code:
///file main.c
#include "encode.h"
int main (int argc ,char** argv){
if(argc!=4){
printf("Usage: bvernan keyfile inputfile outputfile \n");
return 1;
}
Key_t* key=openKey(argv[1]);
Register_t* file=openFile(argv[2],argv[3],key->lenght);
encode(file,key);
closeRegister(file);
freeKey(key);
printf("Success!\n");
}
///file encode.c
#include "encode.h"
int encode (Register_t* file, Key_t* key ){
while(readF(file)>0){
encodeDivision(file->buffer,file->bufferLenght,key);
writeF(file);
}
}
void encodeDivision (unsigned char* block,long lenght,Key_t*key){
for(int i=0;i<lenght;i++){
block[i]=block[i]^key->buffer[i];
}
}
///file encode.h
#include "key.h"
#include "register.h"
int encode (Register_t* file, Key_t* key );
void encodeDivision (unsigned char* block,long lenght,Key_t*key);
///file key.c
#include "key.h"
#include <stdio.h>
#include <stdlib.h>
long keySize (FILE* file){
fseek (file,0,SEEK_END);
long size=ftell(file);
rewind (file);
return size;
}
Key_t* openKey(char* path){
Key_t* kFile= malloc (sizeof(Key_t));
FILE*file= fopen(path,"rb");
kFile->lenght=keySize(file);
kFile->buffer= malloc(kFile->lenght);
fread(kFile->buffer,1,kFile->lenght,file);
fclose(file);
return kFile;
}
void freeKey (Key_t* key ){
free (key->buffer);
free (key);
}
/// file key.h
typedef struct Key
{
unsigned char* buffer;
long lenght;
} Key_t;
Key_t* openKey(char* path);
void freeKey (Key_t*);
/// file register.c
#include <stdio.h>
#include <stdlib.h>
#include "register.h"
Register_t* openFile (char* inPath,char* outPath,long bufferLenght){
Register_t* file = malloc (sizeof(Register_t));
file->buffer= malloc(bufferLenght);
file->bufferLenght=bufferLenght;
file->fdIn= fopen(inPath,"rb");
file->fdOut= fopen(outPath,"wb");
if(file->fdOut==NULL || file->fdIn==NULL){
return NULL;
}
return file;
}
int readF (Register_t* file){
int readbyte = fread(file->buffer,1,file->bufferLenght,file->fdIn);
file->bufferLenght=readbyte;
return readbyte;
}
int writeF (Register_t* file){
int writebyte = fwrite(file->buffer,1,file->bufferLenght,file->fdOut);
return writebyte;
}
void closeRegister (Register_t* file){
free(file->buffer);
fclose(file->fdIn);
fclose(file->fdOut);
free(file);
}
/// file register.h
#include <stdio.h>
#include <stdlib.h>
typedef struct Register
{
unsigned char *buffer;
FILE *fdIn;
FILE *fdOut;
long bufferLenght;
} Register_t;
Register_t *openFile(char *inPath, char *outPath, long bufferLenght);
int readF(Register_t *);
int writeF(Register_t *);
void closeRegister(Register_t *);
/// file Makefile
all: bvernan
bvernan: encode.o key.o main.o register.o
gcc -o bvernan $^
%.o: %.c
gcc -c $<
clean:
rm *.o bvernan
The program has some problems:
it is badly presented: you should indent it properly and use spaces wisely.
it has poor error handling: you will have undefined behavior in most error cases.
it is much too complicated for the task.
Here is a simpler alternative:
#include <errno.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[]) {
FILE *key, *in, *out;
int c, x;
if (argc != 4) {
fprintf(stderr, "Usage: bvernan keyfile inputfile outputfile\n");
return 2;
}
if ((key = fopen(argv[1], "rb")) == NULL) {
fprintf(stderr, "bvernan: cannot open key file %s: %s\n", argv[1], strerror(errno));
return 1;
}
if ((x = getc(key)) == EOF) {
fprintf(stderr, "key file is empty\n");
return 1;
}
if ((in = fopen(argv[2], "rb")) == NULL) {
fprintf(stderr, "bvernan: cannot open input file %s: %s\n", argv[2], strerror(errno));
return 1;
}
if ((out = fopen(argv[3], "rb")) == NULL) {
fprintf(stderr, "bvernan: cannot open output file %s: %s\n", argv[3], strerror(errno));
return 1;
}
rewind(key);
while ((c = getc(in)) != EOF) {
if ((x = getc(key)) == EOF) {
rewind(key);
x = getc(key);
}
putc(c ^ x, out);
}
fclose(key);
fclose(in);
fclose(out);
printf("Success!\n");
return 0;
}
I don't understand the behaviour of using printf() after popen(). I have a folder with these 3 files:
middle.txt
test.app
test.c
test.c is the source code of test.app:
test.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char **argv)
{
FILE *fpipe;
char *command = "ls";
char c = 0;
char output[1000];
fpipe = popen(command, "r");
if (fpipe == 0)
{
perror("popen() error");
exit(1);
}
while (fread(&c, sizeof c, 1, fpipe))
{
strncat(output, &c, 1);
}
pclose(fpipe);
char *position;
char result[9];
position = strstr(output, "test.app");
if (position != NULL)
{
memcpy(result, position + 8, sizeof(result));
fflush(stdout);
printf("Output string: %s", result);
fflush(stdout);
}
return 0;
}
When I run test.app I obtain this ouput:
Output string: test.app test.c
Why not this?
Output string:
test.app
EDIT
Using suggestions from the comments I modified 3 lines (position + 8 is a mistake from my original source code) with these:
char output[1000] = {0};
char result[9] = {0};
memcpy(result, position, sizeof(result));
but now this is the output:
Output string: test.app
middle.txt
test.app
test.c
I modified 3 lines (by opening post) with these:
char output[1000] = {0};
char result[9] = {0};
memcpy(result, position, sizeof(result) - 1);
and now this is the output:
Output string: test.app
1 Byte presents in output was responsible of this strange behaviour. We have to pay attention with the copied size, printed buffer must be a null-terminated string
Following is a part of my code for my shell. The code works, but when I try to enter a command like "ls", my program crashes. I think that's a right error because I try to access to the "/bin" file.
void lecture (char cmd1[], char *argv[]){
int x = 0;
char ligne [1024];
char *aux [100], *pch;
while (1){
int mot = fgetc (stdin);
ligne[x] = (char) mot;
x++;
if (mot == (int)'\n') break;
}
aux[0] = strtok (ligne, " \n");
strcpy(cmd1,aux[0]);
for (int i = 1; i <= 1024; i++){
argv[i+1] = aux[i];
}
}
int main(){
char cmd1 [100];
char cmd2 [100];
int a = 10;
char *argv [20];
char *envp[] = {(char *) "PATH=/bin", 0};
while (1){
affichage();
lecture (cmd2, argv);
printf("Test");
if ( fork() != 0){
printf("Err");
wait (NULL);
}else{
strcpy(cmd1, "/bin/");
strcat(cmd1, cmd2);
execve(cmd1, argv, envp);
}
}
}
I get something working without SIGSEGV with following modification in lecture:
for (int i = 0; i < 20; i++){
Example:
./ms
ls
����: cannot access 'ls': No such file or directory
TestErr
...
But you can also debug this as I did with compiling in debug mode:
gcc -o ms -g -Wall -pedantic -Wextra -std=c11 ms.c
and using gdb to check where SIGSEGV occurs.
Note that you are expected to post a https://stackoverflow.com/help/minimal-reproducible-example with full code (here we are missing affichage) and
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
I'm writing a Vanilla File read code.
Most of it look like this.
Firstly the header file file.h
// fheader.h
#ifndef __file_h__
#define __file_h__
// start from here
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void readFile(FILE *fr);
FILE* openFile(char *file_name);
#endif
the main file fmain.c
#include "fheader.h"
void readFile(FILE *fr) {
// Added Printf
printf("\n...\n");
printf("\n...\n");
printf("\n...\n");
printf("\n...\n");
char *buffer = calloc(sizeof(char),1);
while(!feof(fr)) {
fread(buffer,sizeof(char),1,fr);
printf("%s\n",buffer);
}
free(buffer);
return;
}
FILE* openFile(char *file_name) {
// printf("the file name that is going to be opened is %s",file_name);
FILE *fr;
fr = fopen(file_name,"r");
return fr;
}
int main(int argc,char *argv[]) {
if(argc < 2) {
printf("USAGE: ./file test.txt\n");
return 1;
}
if (argc > 2) {
printf("ERROR: Too many argument\n");
return 1;
}
FILE *fr;
char *file_name = calloc(strlen(argv[1]),sizeof(char));
strncpy(file_name,argv[1],strlen(argv[1]));
fr = openFile(file_name);
printf("\nReading from file\n");
readFile(fr);
fclose(fr);
free(file_name);
return 0;
}
I compiled the code using following command
gcc -g3 -Wall fmain.c -o file.o
When I ran the code
./file.o "~/workspaces/myWork/C_experiment/test.txt"
I see Segmentation fault: 11
But when I run the above program in lldb I work and exit with return code 0
lldb ./file.o
(lldb) run "~/workspaces/myWork/C_experiment/test.txt"
// output of the file
Process 28806 exited with status = 0 (0x00000000)
(lldb) quit
Now, I'm clueless as to how to debug the code and find the Seg Fault reason.
you forgot to add the '\0' at the end of the file_name and when you do a fread to read 1 char, you don't add a '\0' at the end of the buffer.
And if you read only one character you should use a char instead of an array of 1.
The errors are here :
char *file_name = calloc(strlen(argv[1]),sizeof(char));
strncpy(file_name,argv[1],strlen(argv[1]));
fr = openFile(file_name);
You copy the argv[1] but you don't add a '\0' at the end. So how fopen will know where your string stop ?
You should add a +1 to calloc and after the strncpy, you should add the '\0' like that:
file_name[strlen(argv[1])] = '\0';
The second error is here:
char *buffer = calloc(sizeof(char),1);
while(!feof(fr)) {
fread(buffer,sizeof(char),1,fr);
printf("%s\n",buffer);
}
You allocate 1 to your buffer and read 1, that's ok but when you send it to printf, you didn't add a '\0' to your buffer so how can printf know where to stop ?
you should calloc 2 and not one and then add a buffer[1] = '\0';
So with these fixes:
#include "fheader.h"
void readFile(FILE *fr) {
char buffer;
while(!feof(fr)) {
fread(&buffer,sizeof(char),1,fr);
printf("%c\n",buffer);
}
return;
}
FILE* openFile(char *file_name) {
// printf("the file name that is going to be opened is %s",file_name);
FILE *fr;
fr = fopen(file_name,"r");
return fr;
}
int main(int argc,char *argv[]) {
if(argc < 2) {
printf("USAGE: ./file test.txt\n");
return 1;
}
if (argc > 2) {
printf("ERROR: Too many argument\n");
return 1;
}
FILE * fr = openFile(argv[1]);
printf("\nReading from file\n");
readFile(fr);
fclose(fr);
return 0;
}
I'm new to C programming. I made a very short program to merge all the files in a folder.
The program runs and produces the right output, but after execution it hangs and I have to manually kill it.
Any ideas why?
The important functions here are scandir and append_to_file
/*
MERGE: Merges text files. Gives the ability to merge a list of files or all files in a
directory with specified extension.
*/
#include <stdio.h>
#include <dirent.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
/* Function prototypes */
int append_to_file(const char *filename, const char *outfilename); // Appends the contents of filename to outfilename
int scandir(char dirname[], char const *ext, char outfile[]); // Scans a directory for files of a specific extension and merges them
bool has_extension(char const *name, char const *ext);
void usage(); // Prints out usage information (help) to the console
void path_combine(char *dest, const char *path1, const char *path2); // Combines a directory name and filename to a single filepath
int main(int argc, char *argv[])
{
int i, // Counters
nfiles; // Number of files merged
if (argc == 4)
{
nfiles = scandir(argv[1], argv[2], argv[3]);
printf("Merged %s files\n", nfiles);
return 0;
}
else
{
printf("Wrong input, quitting");
return 1;
}
}
int append_to_file(const char *filename, const char *outfilename)
{
FILE *infile, *outfile;
char ch;
infile = fopen(filename, "r");
outfile = fopen(outfilename, "a");
if (infile == NULL)
{
printf("Input file is empty, skipping...\n");
return 1;
}
while ((ch = fgetc(infile)) != EOF)
fputc(ch, outfile);
fclose(infile);
fclose(outfile);
return 0;
}
int scandir(char dirname[], char const *ext, char outfile[])
/* Scans a directory and merges all files of given extension */
{
DIR *d = NULL;
struct dirent *dir = NULL;
char filepath[strlen(dirname) + 255];
int i = 0;
d = opendir(dirname);
if (d)
{
while ((dir = readdir(d)) != NULL)
{
if (has_extension(dir->d_name, ext))
{
path_combine(filepath, dirname, dir->d_name);
printf("%s\n", filepath);
append_to_file(filepath, outfile);
i++;
}
}
closedir(d);
}
return i;
}
bool has_extension(char const *name, char const *ext)
{
size_t len = strlen(name);
return len > 4 && strcmp(name+len-4, ext) == 0;
}
void path_combine(char *dest, const char *path1, const char *path2)
{
const char *last_char = path1;
int append_sep = 0;
char sep[] = "/";
#ifdef WIN32
sep[0] = '\\';
#endif
/* Find the last character in the first path*/
while(*last_char != '\0')
last_char++;
/* If the last character is not a seperator, we must add it*/
if (strcmp(last_char, sep) !=0)
{
append_sep = 1;
}
strcpy(dest, path1);
if (append_sep)
strcat(dest, sep);
strcat(dest, path2);
}
void usage()
{
printf("\t=================\n");
printf("\t MERGE\n");
printf("\t=================\n");
printf("Merge two or more text files\n");
printf("Usage:\n");
printf("\tCall merge with a directory name and desired extension:\n");
printf("\tmerge DIRNAME .csv OUTPUTFILE\n\n");
};
As the compiler warned you (if you compile with gcc -Wall -g), the following line:
printf("Merged %s files\n", nfiles);
is wrong, since nfiles is an int. You probably want
printf("Merged %d files\n", nfiles);
Read about undefined behavior. You've got one. Read also carefully the documentation of every function you are using, starting with printf(3) & fopen(3) & perror(3) & exit(3). Don't forget to handle failure, e.g:
FILE *infile, *outfile;
char ch;
infile = fopen(filename, "r");
outfile = fopen(outfilename, "a");
if (infile == NULL) {
printf("failed to open %s (%s), skipping...\n",
filename, strerror(errno));
return 1;
}
if (outfile == NULL) {
perror(outfilename);
exit(EXIT_FAILURE);
}
Learn how to use your debugger (gdb). If on Linux, use also strace(1) and valgrind.
Ah.
I thought I was using the debugger, by specifying -g when compiling:
gcc main.c -g -o main.exe
But it was still hanging.
If I included the flags -Wall and -Werror it soon told me that I had a string formatting error.
the line printf("Merged %s files\n", nfiles) needed to be changed to printf("Merged %d files\n", nfiles).
Compiling with -Wall and -Werror soon pointed out this mistake.