How to replace a char from char array in C Program - c

I am developing a shell code in C. Currently, my task is to redirect stdin to stdout with different commands, for instance cat < hello.c -ne > test.txt
I am able to do it, but my code tries to open "<" and ">" file as well, so for that I tried to assign 0 value which doesn't give me any error but ignores -ne.
Here is my code,
int in, out;
int i;
for (i = 0; i < argc; i++) {
if(strcmp( argv[i],"<")==0) {
in = open(argv[i+1], O_RDONLY);
if (!in)
exit(EXIT_FAILURE);
// argv[i]=0;
// array[i+1]=0;
}
else if(strcmp( argv[i],">")==0) {
out = open(argv[i+1], O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IRGRP | S_IWGRP | S_IWUSR);
if (!out)
exit(EXIT_FAILURE);
// argv[i]=0;
// array[i+1]=0;
}
}
dup2(in, 0);
dup2(out, 1);
// close unused file descriptors
close(in);
close(out);
char *bname;
char *path2 = strdup(*argv);
bname = basename(path2);
//printf("%d",argc);
/*the command will be executed using execvp*/
execvp(bname, argv);
argv array would be like
argv[0]="cat"
argv[1]="<"
argv[2]="hello.c"
argv[3]="-ne"
argv[4]=">"
argv[5]="test.txt"
Basically, I want to open multiple files in between "<" and ">" and then redirect to the given file after ">".
The current Output-
$ ./shell
Enter Shell Command -- cat < hello.c -ne > te1.txt
$ cat: : No such file or directory
cat: : No such file or directory
cat: te1.txt: input file is output file

Related

Writing to file only at the end of the execution in my bash shell

I am writing my own shell. I am facing a problem with the commands like C1 | C2 > file or C1 | C2 >> file. When I execute a command like ls | grep .c > a.txt , I get the result of ls | grep only when I terminate the program. But I want to get it during the execution.
Code from main.c:
if (countPipes == 1 && strstr(userInput, ">>") != NULL){
token = NULL;
resetC(cmd);
resetC(cmdPipe);
token = strtok(userInput, ">>");
char *piped = strdup(token);
token = strtok(NULL, ">>");
char *file = strdup(token);
file = skipwhite(file);
token = strtok(piped, "|");
c1 = strdup(token);
token = strtok(NULL, "|");
c2 = strdup(token);
c2 = skipwhite(c2);
splitCommands(c1, cmd);
splitCommands(c2, cmdPipe);
execPipedCommandsRed(cmd, cmdPipe, file);
memset(userInput, '\0', 1000);
}
Code from functions.c:
void execPipedCommandsRed(char **cmd, char **cmdPiped, char *file){
int pipeOne[2], status, ret_val, s;
status = pipe(pipeOne);
if (status < 0) {
exit(-1);
}
int k;
int e = dup(1);
pid_t p1, p2, w;
int s2 = dup(1);
p1 = fork();
if (p1 < 0) {
printf("Fork failed!\n");
}
if (p1 == 0) {
close(pipeOne[READ]);
dup2(pipeOne[WRITE], STDOUT_FILENO);
close(pipeOne[WRITE]);
if (execvp(cmd[0], cmd) < 0) {
perror("Lathos");
}
} else {
p2 = fork();
if (p2 < 0) {
printf("Fork failed\n");
}
if (p2 == 0) {
close(pipeOne[WRITE]);
dup2(pipeOne[READ], STDIN_FILENO);
close(pipeOne[READ]);
k = open(file, O_WRONLY| O_APPEND | O_CREAT, 0644);
if (k < 0) {
puts("error k");
}
dup2(k, 1);
close(k);
if (execvp(cmdPiped[0], cmdPiped) < 0) {
perror("Lathos!");
}
} else {
// parent is waiting
waitpid(-1, &s, WUNTRACED | WCONTINUED);
printBash();
}
}
}
When i execute a command like ls | grep .c > a.txt, i get the result of ls | grep only when i terminate the program.
The traditional POSIX shell behaviour is that stdout is line-buffered only when it goes into a terminal (isatty returns 1), otherwise it is block-buffered. See setvbuf for buffering descriptions.
If you'd like matching files to be output into stdout as they are found, instead of ls | grep .c > a.txt use the following command:
stdbuf --output=L find -maxdepth 1 -name "*.c" > a.txt
stdbuf allows to explicitly specify the desired buffering mode. And find outputs one filename per-line, unlike plain ls.
You can do ls and grep, but that is sub-optimal in the number of processes involved and that each process must have its output buffering specified explicitly:
stdbuf --output=L ls -1 | stdbuf --output=L egrep '\.c$' > a.txt
Notes:
stdbuf only affects C standard streams and some heavily optimised applications may not use C standard streams at all, so that stdbuf may have no effect.
grep .c matches anything that has character c at non-0 position. Whereas find -name "*.c" matches only files with extension .c. So does egrep '\.c$'.
ls outputs multiple files per line, but grep filtering expects one file per line. ls -1 outputs one file per line.

create new file with system calls

Im trying to create a new file / overwrite an existing file using systemcalls , but for some reason I have two problems:
1. When I'm first running the program it exits with value 0, so it seems like it created the file successfully, but I can't see anything in my project directory.
then when I secondly running the program the file is created, but an error message is printed on the screen.
2. Also after the first iteration of the program, I can't see the prinf message at the end of the main function.
Thanks for helping.
int readFileDesc = 0, writeFiledesc = 0;
int sourceFile = 1, destFile = 2, bufferSize = 3, isOverwrite;
if (argc != 4 && argc != 5) {
printf("Invalid number of arguments\n");
printf("Usage:\n");
printf(" ex1 [-f] SOURCE DEST BUFFER_SIZE");
exit(EXIT_FAILURE);
}
//Checking if -f [OP] is activated.
isOverwrite = (strcmp(argv[1], "-f") == 0);
if (isOverwrite) {
sourceFile++;
destFile++;
bufferSize++;
}
//Opening the source file
readFileDesc = open(argv[sourceFile], O_RDONLY);
if (readFileDesc < 0) {
perror("Unable to open source file for reading: ");
exit(EXIT_FAILURE);
}
//opening the destination file
if (!isOverwrite) {
//Case we dont have the -f [op] so we create the file.
writeFiledesc = open(argv[destFile],
O_CREAT | O_EXCL | O_WRONLY ,
S_IRUSR | S_IWUSR);
if (writeFiledesc < 0) {
perror("Unable to open destination file for reading: ");
exit(EXIT_FAILURE);
}
} else {
//Case we have the -f [op] so we override existing file.
writeFiledesc = open(argv[destFile], O_RDONLY | O_WRONLY | O_TRUNC);
if (writeFiledesc < 0) {
perror("Unable to open destination file for writing: ");
exit(EXIT_FAILURE);
}
}
//Assume the buffersize is legal.
bufferSize = atoi(argv[bufferSize]);
char data[bufferSize];
int nread, nwrite;
while ((nread = read(readFileDesc, data, bufferSize)) > 0) {
if ((nwrite = write(writeFiledesc, data, nread)) != nread) {
printf("write problem: ");
}
}
// cant see this!
printf("File %s was copied to %s" , argv[sourceFile] , argv[destFile]);
//handling errors
close(sourceFile);
close(destFile);
return EXIT_SUCCESS;
}
This is wrong:
writeFiledesc = open(argv[destFile], O_RDONLY | O_WRONLY | O_TRUNC);
Using both O_RDONLY and O_WRONLY is wrong. You need to use O_RDWR.
Per the POSIX standard for open():
SYNOPSIS
#include <sys/stat.h> #include <fcntl.h>
int open(const char *path, int oflag, ...);
...
Values for oflag are constructed by a bitwise-inclusive OR of flags
from the following list, defined in . Applications shall
specify exactly one of the first five values (file access modes)
below in the value of oflag:
O_EXEC
Open for execute only (non-directory files). The result is unspecified if this flag is applied to a directory.
O_RDONLY
Open for reading only.
O_RDWR
Open for reading and writing. The result is undefined if this flag is applied to a FIFO.
O_SEARCH
Open directory for search only. The result is unspecified if this flag is applied to a non-directory file.
O_WRONLY
Open for writing only.
Any combination of the following may be used:
...
Also, read() and write() return ssize_t, not int.

C: execlp() and >

I want to run execlp() from C file and write the result to some output file.
I use the line:
buff = "./cgi-bin/smth";
execlp(buff, buff, "> /cgi-bin/tmp", NULL);
where smth is a compiled c script.
But smth prints to stdout, and no file appears.
What happens, and how to put script result to an output file?
You have to handle it yourself with dup2 if using execlp. You can look at how I handle file out with execvp in comparison. I pass a flag for out redirection and then I handle it:
if (structpipeline->option[0] == 1) { /* output redirection */
int length = structpipeline[i].size;
char *filename = structpipeline->data[length - 1];
for (int k = length - 2; k < length; k++)
structpipeline->data[k] = '\0';
fd[1] = open(filename, O_WRONLY | O_CREAT, 0666);
dup2(fd[1], STDOUT_FILENO);
close(fd[1]);
} /* TODO: input redirection */
execvp(structpipeline[i].data[0], structpipeline[i].data);
See also this question
Redirecting exec output to a buffer or file

Implementation of Redirection in User-Created Shell is not working properly

I am trying to create a shell similar to bash, with redirection and pipes.
In my main(), I call a parser and then call the function below. The problem I am running into is the fact that when I run my shell, it outputs to the terminal correctly, but it does not output to the file correctly when using the >, 1>, or 2>.
For instance, if I call:
pwd > foo5.txt
I end up receiving:
>
foo5.txt
In the text file I write too as opposed to the stdout (for ">"/"1>") or stderr (for "2>") which I am trying to achieve.
This is my code to fork and create the child process:
pid_t create_process(char *part, int const pipes[][2], int pipenum)
{
pid_t pid; // Initialize variables/pointers/arrays.
char *args[64];
int argc=0, n;
char *arg=strtok(part, " \t");
//char const **filename = args;
while(arg != NULL)
{
args[argc++]=arg;
arg=strtok(NULL, " \t");
}
args[argc++]=NULL;
pid = fork(); // Create Fork.
if(pid == 0)
{
int m;
if(pipes[pipenum][STDIN_FILENO] >= 0)
dup2(pipes[pipenum][STDIN_FILENO], STDIN_FILENO); // FD 0.
if(pipes[pipenum][STDOUT_FILENO] >= 0)
dup2(pipes[pipenum][STDOUT_FILENO], STDOUT_FILENO); // FD 1.
// Close all pipes.
for(m=0; m<64; m++)
{
if(pipes[m][STDIN_FILENO] >= 0)
close(pipes[m][STDIN_FILENO]);
if(pipes[m][STDOUT_FILENO] >= 0)
close(pipes[m][STDOUT_FILENO]);
}
char *filename;
char *newargs[64];
newargs[63] = NULL;
int i = 0;
int j = 0;
for(i = 0; i<64; i++)
{
if (args[i] == ">")
{
i++;
if (args[i] != NULL)
{
filename = args[i];
int redir = open(filename, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IRGRP | S_IWGRP | S_IWUSR);
dup2(redir, 1);
close(redir);
}
}
else if (args[i] == "2>")
{
i++;
if (args[i] != NULL)
{
filename = args[i];
int redir = open(filename, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IRGRP | S_IWGRP | S_IWUSR);
dup2(redir, 2);
close(redir);
}
}
else if (args[i] == "2>")
{
i++;
if (args[i] != NULL)
{
filename = args[i];
int redir = open(filename, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IRGRP | S_IWGRP | S_IWUSR);
dup2(redir, 2);
close(redir);
}
}
else if (args[i] == 0)
{
break;
}
else
{
newargs[j] = args[i];
j++;
cout<<"The arg is: " << newargs[j] <<endl;
}
}
execvp(newargs[0], newargs);
fprintf(stderr, "Command not found.\n");
exit(255);
}
else if(pid < 0)
{ // Error checking.
fprintf(stderr, "Fork Failed\n");
}
return(pid);
}
UPDATE: Now my code will not recognize the commands, and the arguments being printed (for error checking) appear as such:
ls > foo5.txt
The arg is: fprintf
The arg is:
The arg is: ▒
Command not found.
I see a few problems here:
First, you have the for loop where you scan the command arguments for redirection syntax (if (strcmp(args[i],">")==0) and so on) - but if the condition is true (meaning you found a redirection character) you're always opening args[2], not args[i+1].
Second (and this is why the redirection syntax gets passed on to the command you're running as command arguments) - once you detect redirection syntax, you don't remove the redirection operator or the target filename from the list of arguments that you pass to execvp().
For instance, if args[] = {"echo", "a", ">", "logfile", 0}, your code detects a request to send the output to a new file called "logfile" and redirects the FDs correctly, but it still passes those three arguments ["a", ">", "logfile"] to the command.
Third - in your loop, you're calling execvp() at the end of each conditional statement - meaning that you don't get to the end of argument processing before you launch the new process. You need to process all the command arguments for shell syntax and then exec the command.
To fix the various problems with arg handling, probably the most effective solution is to build a new argument list as you're processing the raw ones provided by the user. For instance (using a==b as a string equality test for brevity)
if (args[i] == ">")
{
i++; //skip the arg
if (args[i]) { // check we haven't hit the end of the arg list
filename = args[i];
// then open the file, dup it to stdout or whatever, etc...
}
else if (args[i] == "<") // repeat for other redirection syntax...
else { // Finally, handle the case where we didn't identify any shell syntax:
newargs[j] = args[i]; // Copy the arg to the new list, since it isn't "special"
j++; // Size of newargs[] has been increased
}
Note that this still doesn't handle things like no whitespace around the ">" character: "echo foo>file" will just print "foo>file"... Syntax processing gets a little more complicated in that case, as you've got to account for quoting rules and escape characters to process the arguments correctly.

Redirection doesn't work in shell:: ls: cannot access >: No such file or directory

I don't know why redirection doesn't work in the shell I have written. Here's my code"
int i;
for (i=1; !args[i];i++)
{
if (args[i]== ">")
{
printf("argv[i] %s %d \n", args[i], i);
int out;
// out = open("out", O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IRGRP | S_IWGRP | S_IWUSR);
out=open("out", O_CREAT | O_WRONLY | O_TRUNC, S_IRWXU);
int fdl=dup2(out,1);
close(out);
execvp(args[0],args);
}
}
Also here's the error I receive :
mysh> ls
basic_shell basic_shell.c~ fork fork_2 fork_cp.c
basic_shell.c basic_shell_OK.c fork_1 fork.c
mysh> ls > file
ls: cannot access >: No such file or directory
ls: cannot access file: No such file or directory
Please let me know what's wrong?
If args is an array of char*, then this condition
if (args[i]== ">")
does not do what you think it does. It compares the pointers and not what they point to. To compare string you have to use strcmp.

Resources