I am trying to work with command line arguments and parsing a text file in C. Basically I want to be able to put in two numbers, like, 1 and 4 and have it read a column of a text file then print it to stdout. I want to be able to do something like take this:
PID TTY TIME CMD
449 ttys000 0:00.35 -bash
1129 ttys001 0:00.35 -bash
25605 ttys001 0:00.15 vi prog.c
6132 ttys002 0:00.11 -bash
6208 ttys002 0:00.03 vi test
And do:
./your_prog 1 4 < data.txt
PID CMD
449 bash
1129 -bash
25605 vi
6132 -bash
6208 vi
So I need to enter the the columns i want to print out, redirect the file in "data.txt" and have it process the file and print like so.
So far I have this for my code:
#include <stdio.h>
int main(int argc, char **argv){
//int row = argc[0];
//int col = argc[1];
//if number entered is less than one, re-enter
int i;
for (i = 0; i < argc; i++) {
printf("argv[%d] = %s\n", i, argv[i]);
}
if(argc < 1){
fprintf(stderr, "Enter a valid input");
//quit
return 1;
}
else{
char ch[256];
//ch[255] = '\0';
while(fgets(ch, 256, stdin) != NULL){
printf("%s", ch);
}
}
return 1;
}
but am not sure if I am on the correct track and am confused as to what to do next. I am new to C, so I apologize if this is an easy question.
To get you started, this improves the way you gather the command line information. Note the first argument argv[0] is the name of the executable itself. Bear in mind comments above, I haven't improved any further. Syntax is progname filename col1 col2
#include <stdio.h>
#include <stdlib.h>
void fatal(char *msg) {
printf("%s\n", msg);
exit (1);
}
int main(int argc, char*argv[])
{
int col1, col2;
FILE *fp;
char ch[256];
if(argc < 4)
fatal("Need three arguments");
if (sscanf(argv[2], "%d", &col1) != 1)
fatal("Argument 1 error");
if (sscanf(argv[3], "%d", &col2) != 1)
fatal("Argument 2 error");
printf("Read columns %d and %d from file %s\n", col1, col2, argv[1]);
if ((fp = fopen(argv[1], "r")) == NULL)
fatal("Unable to open file");
// process the file
while (fgets(ch, 256, fp) != NULL) {
printf("%s", ch);
}
fclose(fp);
return 0;
}
Here is what I have so far. I can get it to parse apart then I can put it back how it was, but for the life of me I cant figure out how to tell it to print only the first column. If I could have some direction, then I could figure out how to do the rest, but I can find anything. Here is what code I have so far:
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv){
int col1, col2;
int size = 512;
char ch[size];
char *temp[size];
char *token;
if(argc == 1){
fprintf(stderr, "I need more!\n");
return 1;
}
else{
//test to see what is stored
int i;
for (i = 0; i < argc; i++) {
printf("argv[%d] = %s\n", i, argv[i]);
}
if(sscanf(argv[1], "%d", &col1) != 1) return 1;
if(sscanf(argv[2], "%d", &col2) != 1) return 1;
while(fgets(ch, size, stdin) != NULL){
//get 1st token
token = strtok(ch, " ");
while(token != NULL){
printf(" %s", token);
temp[i++] = token;
token = strtok(NULL, " ");
}
}
return 0;
}
}
Related
I wrote a program, which reads from a file. I use a condition in which I print that the array is too big, but when I use a too big array instead of showing this message I have segmentation fault.
This is my program
#include <stdio.h>
#include <stdlib.h>
#define N 10000 // Maximum array size
int _strlen(char *array) {
int i;
for (i = 0; array[i] != '\0'; ++i);
return i;
}
int readText(FILE *wp, char *s, int max) {
int sum = 0;
if (_strlen(s) > max) {
printf("This array is too big. Maximum size is %d", max);
} else {
while ((*s++ = fgetc(wp)) != EOF) {
sum++;
}
*(s-1) = '\0';
}
return sum;
}
int main(int argc, char *argv[]) {
FILE *wz, *wc;
char *s;
char array[N];
s = array;
if (argc != 3) {
printf("Wrong arguments number\n");
printf("I should run this way:\n");
printf("%s source result\n",argv[0]);
exit(1);
}
if ((wz = fopen(argv[1], "r")) == NULL) {
printf("Open error %s\n", argv[1]);
exit(1);
}
if ((wc = fopen(argv[2], "w")) == NULL) {
printf("Open error %s\n", argv[2]);
exit(2);
}
fprintf(wc, "Read text from file source.txt");
readText(wz, s, 10000);
return 0;
}
In output I want to have: This array is too big. Maximum size is %d
Instead of Segmentation fault core dumped
In addition, I want to say that the program is when I use a smaller array, but I want to show the user a proper message when he uses too big array instead of segmentation fault.
Thanks, I change my program in that way. The only problem is that this program check the if condition in every while loop so this program could be slow.
int readText(FILE *wp, char *s, int max) {
int sum = 0;
if (_strlen(s) > max) {
printf("This array is too big. Maximum size is %d", max);
} else {
while ((*s++ = fgetc(wp)) != EOF) {
sum++;
if (sum > max) {
printf("This array is too big. Maximum size is %d", max);
break;
}
}
*(s-1) = '\0';
}
return sum;
}
The remarks / other answer solve your undefined behavior (segmentation fault in your case).
The only problem is that this program check the if condition in every while loop so this program could be slow.
Your program is not slow because of a 'if' but because you read the file char per char.
Using stat or equivalent function you can get the size of the file to read it throw only one fread :
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
#define N 10000 // Maximum array size
int main(int argc, char *argv[]) {
char array[N];
FILE *wz, *wc;
struct stat st;
off_t sz;
if (argc != 3) {
printf("Wrong arguments number\n"
"I should run this way:\n"
"%s source result\n", argv[0]);
exit(1);
}
if ((wz = fopen(argv[1], "r")) == NULL) {
printf("Cannot open %s to read : %s\n", argv[1], strerror(errno));
exit(1);
}
if (stat(argv[1], &st) == -1) {
printf("Cannot get stat of %s : %s\n", argv[1], strerror(errno));
exit(1);
}
if (st.st_size > N-1) {
printf("This array is too big. Maximum size is %d", N-1);
sz = N-1;
}
else
sz = st.st_size;
if (fread(array, 1, sz, wz) != sz) {
printf("cannot read %s : %s", argv[1], strerror(errno));
fclose(wz); /* for valgrind end test etc */
exit(1);
}
array[sz] = 0;
fclose(wz);
if ((wc = fopen(argv[2], "w")) == NULL) {
printf("Cannot open %s to write : %s\n", argv[2], strerror(errno));
fclose(wz); /* for valgrind end test etc */
exit(2);
}
/* ... */
fclose(wc);
return 0;
}
Knowing the size of the file allows to remove that limitation to a constant size and try to read the file while you can allocate enough memory for :
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
int main(int argc, char *argv[]) {
char * array;
FILE *wz, *wc;
struct stat st;
if (argc != 3) {
printf("Wrong arguments number\n"
"I should run this way:\n"
"%s source result\n", argv[0]);
exit(1);
}
if ((wz = fopen(argv[1], "r")) == NULL) {
printf("Cannot open %s to read : %s\n", argv[1], strerror(errno));
exit(1);
}
if (stat(argv[1], &st) == -1) {
printf("Cannot get stat of %s : %s\n", argv[1], strerror(errno));
exit(2);
}
if ((array = malloc(st.st_size + 1)) == NULL) {
printf("Not enough memory to memorize the file %s\n", argv[1]);
exit(3);
}
if (fread(array, 1, st.st_size, wz) != st.st_size) {
printf("cannot read %s : %s", argv[1], strerror(errno));
fclose(wz); /* for valgrind end test etc */
free(array); /* for valgrind etc */
exit(4);
}
array[st.st_size] = 0;
fclose(wz);
if ((wc = fopen(argv[2], "w")) == NULL) {
printf("Cannot open %s to write : %s\n", argv[2], strerror(errno));
free(array); /* for valgrind etc */
exit(5);
}
/* ... */
fclose(wc);
free(array); /* for valgrind etc */
return 0;
}
Anyway because of the usage of the program "source result" may be you want to copy the file specified by argv[1] in the file specified by argv[2], in that case better to read and write block by block rather than to read all to not use a lot of memory for nothing and to manage the case the input file size is greater than the memory size.
You cannot measure the length of the destination array with _strlen(s), the size is given as an argument and reading an uninitialized array with _strlen() has undefined behavior.
Furthermore, you store fgetc(fp) to *s++ before testing for EOF. This is incorrect in all cases:
if char type is signed, EOF cannot be distinguished from a valid byte value of \377.
if char is unsigned, EOF cannot be tested because it has been converted as a char value of 0xff, hence the loop runs forever, writing beyond the end of the destination array until this causes a crash.
You simply want to add a test in the reading loop to stop reading bytes from the file when the buffer is full and read the bytes into an int variable so you can test for end of file reliably.
Here is a modified version:
#include <stdio.h>
#include <stdlib.h>
#define N 10000 // Maximum array size
int readText(FILE *wp, char *s, int max) {
int i = 0, c;
while (i < max - 1 && (c = fgetc(wp)) != EOF) {
s[i++] = c;
}
s[i] = '\0';
return i;
}
int main(int argc, char *argv[]) {
FILE *wz, *wc;
char array[N];
int nread;
if (argc != 3) {
printf("Wrong arguments number\n");
printf("I should run this way:\n");
printf("%s source result\n", argv[0]);
exit(1);
}
if ((wz = fopen(argv[1], "r")) == NULL) {
printf("Open error %s\n", argv[1]);
exit(1);
}
if ((wc = fopen(argv[2], "w")) == NULL) {
printf("Open error %s\n", argv[2]);
exit(2);
}
fprintf(wc, "Read text from file source.txt\n");
nread = readText(wz, array, N);
printf("Read %d bytes\n", nread);
return 0;
}
I've been writing a shell program in C. The program is working as expected in Linux (Ubuntu 16.04) but I'm getting unexpected output in MacOS (10.14.2 Mojave).
/* A shell program.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
void input(char* argv[]);
void print_arr(char *argv[]); // For debugging
int
main(void)
{
while (1)
{
pid_t pid;
char *argv[100];
// Display shell prompt
write(1, "(ash) $ ", 8);
// Take user input
input(argv);
// print_arr(argv); // DEBUG STATEMENT
if (argv[0] != NULL)
{
// Exit if exit command is entered
if (strcmp(argv[0], "exit") == 0)
{
exit(0);
}
// Create child process
if ((pid = fork()) > 0)
{
wait(NULL);
}
else if (pid == 0)
{
// print_arr(argv); // DEBUG STATEMENT
execvp(argv[0], argv);
printf("%s: Command not found\n", argv[0]);
exit(0);
}
else
{
printf("Fork Error!\n");
}
}
}
}
/* Takes input from user and splits it in
tokens into argv. The last element in
argv will always be NULL. */
void
input(char* argv[])
{
const int BUF_SIZE = 1024;
char buf[BUF_SIZE];
int i;
buf[0] = '\0';
fgets((void*) buf, BUF_SIZE, stdin);
i = 0;
argv[i] = strtok(buf, " \n\0");
while (argv[i] != NULL)
{
argv[++i] = strtok(NULL, " \n\0");
}
}
/* Print argv for debugging */
void
print_arr(char *argv[])
{
int i = 0;
while (argv[i] != NULL)
{
printf("%d: %s\n", i, argv[i]);
++i;
}
}
In Linux:
(ash) $ ls
// files and folders are listed
In MacOS (with debug statements):
(ash) $ ls
0: p?M??
0: ??M??
: Command not found
(ash) $ ls
0: ls
0: ??M??
: Command not found
(ash) $ ls
0: ls
0: ??M??
I don't understand that why are the contents of char* argv[] getting modified across fork()?
I've also tried it in the default clang compiler and brew's gcc-4.9, the results are same.
When a program behaves different for no good reason, that's a VERY good sign of undefined behavior. And it is also the reason here.
The array buf is local to the function input and ceases to exist when the function exits.
One way of solving this is to declare buf in main and pass it to input. You will also need the size of the buffer for fgets.
void
input(char * argv[], char * buf, size_t size)
{
buf[0] = '\0';
fgets(buf, sizeof buf, stdin);
argv[0] = strtok(buf, " \n\0");
for(int i=0; argv[i] != NULL; i++) argv[i+1] = strtok(NULL, " \n\0");
}
Another solution (although I suspect many will frown upon it) is to declare buf as static, but then you would need to change BUF_SIZE to a #define or a hard coded value, since you cannot have a static VLA.
#define BUF_SIZE 1024
void
input(char * argv[])
{
static char buf[BUF_SIZE];
buf[0] = '\0';
fgets(buf, sizeof buf, stdin);
argv[0] = strtok(buf, " \n\0");
for(int i=0; argv[i] != NULL; i++) argv[i+1] = strtok(NULL, " \n\0");
}
I removed the cast to void* since it's completely unnecessary. I also changed the while loop to a for loop to make the loop variable local to the loop.
I'm trying to write a shell program in c.
The program needs to have multiple processes created by fork function and be able to print multiple output line in one command.
For example, like linux terminal, if input is "ls ; ps ; pwd ;", the output should be like this.
$./shell
shell> ls ; ps ; pwd ;
(ls output)
(ps output)
(pwd output)
And it should be able to open a file and display the command list and the output that file contains.(batch mode I guess?)
Let's say these command lists are in the batch file.
batch
1 ls
2 ps
3 ls ; pwd ; ps
And the output is
$./shell batch
shell> ls
shell> (ls output)
shell> ps
shell> (ps output)
shell> ls ; pwd ; ps
shell> (ls output)
(ps output)
(pwd output)
Here's the code I wrote
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <string.h>
#include <fcntl.h>
void command(char* myargs[][10], char* buffer);
int tokenizing(char* myargs[][10], char* buffer);
int main(int argc, char* argv[]) {
int fd;
char buffer[200];
char* myargs[10][10];
char* token;
if(argc >= 2) {
if((fd=open(argv[1], O_RDONLY)) == -1)
printf("cannot open file\n");
else {
read(fd, buffer, 200);
printf("%s\n", buffer);
token = strtok(buffer, "\n");
while(token != NULL) {
printf("%s\n", token);
command(myargs, token);
token = strtok(NULL, "\n");
}
return 0;
}
}
while(1) {
printf("prompt> ");
if(fgets(buffer, 200, stdin) == NULL||
strcmp(buffer, "quit\n") == 0)
break;
command(myargs, buffer);
}
return 0;
}
void command(char* myargs[][10], char* buffer) {
int rc = fork();
if(rc < 0) {
fprintf(stderr, "fork failed\n");
} else if(rc == 0) {
int n = tokenizing(myargs, buffer);
for(int i = 0 ; i < n; i++) {
int rc2 = fork();
if(rc2 < 0) {
fprintf(stderr, "for failed\n");
} else if(rc2 == 0) {
execvp(myargs[i][0], myargs[i]);
printf("%s: command not found\n", myargs[i][0]);
exit(0);
} else {
wait(NULL);
}
}
exit(0);
}
else {
wait(NULL);
}
}
int tokenizing(char* myargs[][10], char* buffer) {
int i = 0;
int j = 0;
int k = 0;
char* token;
char* subCommand[10];
token = strtok(buffer, ";\n");
while(token != NULL) {
subCommand[k] = token;
k++;
token = strtok(NULL, ";\n");
}
for(int i = 0; i < k; i++) {
token = strtok(subCommand[i], " \n");
while(token != NULL) {
myargs[i][j] = token;
j++;
token = strtok(NULL, " \n");
}
myargs[i][j] = NULL;
j=0;
}
}
This code works fine but has some issues. When this code runs with a batch file, I struggle with some errors.
When the program is executed, I think the output should be like the above image file - as far as I know.
But frequently the program comes out with some weird command line that I didn't even type. These results just happen alternatively.
In addition, if you see 'ps' lists, you can see two shell programs are running.
Can you guys please help me solve these problems?
'strings' in C need a NUL terminator. Calling strng functions like printf("%s....") and strtok() on char arrays that are not securely NUL-terminated results in undefined behaviour.
read() returns a value. You can use it to load a terminator into 'buffer'. To be super-safe, you should attempt to read only [buffer size -1] chars to ensure that there will always be enough space for the terminator, eg:
buffer[read(fd, buffer, 199)]='\0';
I am working on pipeline system with string in C. For example I write this line to console ./pipeline LOWERCASE REVWORD SQUASHWS < stringFile.txt. It should be any like this P0 -> P1 -> P2 -> ...-> Pn. P0 load string from file stringFile.txt and send it to P1.. I can work with pipe(send and read), but I dont know how to work with N processes. It should be any like this. Can you give me any advice or example? Thank you
while(is_something_to_read) {
load_from_previous_process();
do_process(); // for example LOWERCASE
send_to_next_process();
}
Patrik,
I wrote a program that simulate a shell that will spawn a given amount of children and each process will then communicate with another process. Since the number of processes can vary I decided to use a 2D array for the pipes. In the below code NUM_PROCS refers to the amount of processes that will be running (including the parent).
I declare it
int pipes[NUM_PROCS][2];
After this, I create the pipes
for(i = 0; i < NUM_PROCS; i++)
{
if((pipe(pipes[i])) < 0)
{
perror("Failed to open pipe");
}
}
This is a shell program that i wrote for practise.
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#define MAXARGS 256
char ** getargs(char * cmd) {
char** argsarray;
int nargs = 0;
int nlen = strlen(cmd);
int i = 0;
argsarray = (char**) malloc(sizeof(char*) * MAXARGS);
argsarray[0] = strtok(cmd," ");
i = 0;
while (argsarray[i] != NULL){
i++;
argsarray[i] = strtok(NULL," ");
}
return argsarray;
}
int main(void){
pid_t childpid;
int fd[256][2];
char cmd[256];
char * sepCmd[256];
char * pch;
printf("Please enter a command sequence: \n");
gets(cmd);
printf("You have entered: %s ....%d\n", cmd,strlen(cmd));
printf("Attempting to split up command: \n");
pch = strtok (cmd, "|");
int count = 0;
while (pch != NULL && count < 256) {
printf("%s\n", pch);
sepCmd[count] = pch;
printf("The value in this array value is: %s\n", sepCmd[count]);
pch = strtok (NULL, "|");
count++;
}
char ** argue;
int k;
/* Block that deals with the first command given by the user */
k = 0;
pipe(fd[k]);
if(!fork()) {
dup2(fd[k][1], STDOUT_FILENO);
close(fd[k][0]);
argue = getargs(sepCmd[k]);
execvp(argue[0], argue);
perror(argue[0]);
exit(0);
}
/*Loop that will control all other comands except the last*/
for(k = 1; k <= count - 2; k++) {
close(fd[k-1][1]);
pipe(fd[k]);
if(!fork()) {
close(fd[k][0]);
dup2(fd[k-1][0], STDIN_FILENO);
dup2(fd[k][1], STDOUT_FILENO);
argue = getargs(sepCmd[k]);
execvp(argue[0], argue);
perror(argue[0]);
exit(0);
}
}
/*Block that will take care of the last command in the sequence*/
k = count - 1;
close(fd[k-1][1]);
if(!fork()) {
dup2(fd[k-1][0], STDIN_FILENO);
argue = getargs(sepCmd[k]);
execvp(argue[0], argue);
perror(argue[0]);
exit(0);
}
while(waitpid(-1, NULL, 0) != -1);
}
I have the following problem: In my code, here in line 83, I have this: check = wait(NULL);
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include <sys/stat.h>
#include <sys/types.h>
//---------------------------------
//Function: parse_cmdline(const char* cmdline)
//This function takes the input from stdin
//and returns it as array of strings.
//If stdin is /bin/ls -l /usr/include
//the function will return ["/bin/ls","-l","/usr/include"]
//---------------------------------
char** parse_cmdline(const char* cmdline) {
int count, word_count = 0;
char** line_parsed, line_return;
char *pch, *cmdline_copy = (char*)malloc(sizeof(char)*(strlen(cmdline)+1));
strcpy(cmdline_copy, cmdline);
pch = strtok(cmdline_copy," \n\t\r");
while (pch != NULL) {
++word_count;
pch = strtok(NULL, " \n\t\r");
}
line_parsed = (char**)malloc((word_count+1)*sizeof(char*));
count = 0;
strcpy(cmdline_copy, cmdline);
pch = strtok(cmdline_copy," \n\t\r");
while (pch != NULL) {
line_parsed[count] = (char*)malloc((strlen(pch) + 1)*sizeof(char));
strcpy(line_parsed[count], pch);
++count;
pch = strtok(NULL," \n\t\r");
}
line_parsed[count] = NULL;
free(cmdline_copy);
return line_parsed;
}
int main() {
int count = 0, check;
size_t size;
char* line;
char** cmdline;
while(1) {
check = 0;
printf("$Monkey Eats:< ");
getline(&line, &size, stdin);
cmdline = parse_cmdline(line);
pid_t pid = fork();
if (pid == -1) {
perror("fork");
return -1;
} else if(pid == 0) {
struct stat _stat;
stat(cmdline[0],&_stat);
if(_stat.st_mode & S_IXUSR){
execvp(cmdline[0], cmdline);
}else fprintf(stderr,"%s: Permission denied!\n",cmdline[0]);
perror("");
exit(1);
}else {
check = wait(NULL);
}
count = 0;
while(cmdline[count] != NULL) {
free(cmdline[count]);
++count;
}
free(cmdline);
}
return 0;
}
It makes me a problem. When I run it and when I type a command I have the following message:
$Monkey Eats:< ls
ls: Permission denied!
No such file or directory
If I have only wait(NULL); the program runs normally without a problem. Can somebody tell me what is the problem? Thank you :)
The problem is trying to run ls. execvp() doesn't know where ls is. Try running /bin/ls as your command.
The problem is: stat(cmdline[0],&_stat); - the return code is not checked. What if file not found ? The program continues, and finds that _stat.st_mode & S_IXUSR is 0 (randomly).
However you may test the program as is with "/bin/ls" as input..