simulating '<' operator in a c programme - c

I am recreating a complete shell. For that I must simulate <. To do this, I have to use the function dup2().
I made this but it didn't work and I donc't understand why :
void simple_redirection_left(char *buffer, int index, global *glob)
{
char *file_name = &buffer[index + 1];
int fd = 0;
file_name = clean_file_name(file_name);
if (file_name[0] == '\0') {
my_putstr(1, "Missing name for redirect.\n");
return;
}
if ((fd = open(file_name, O_RDONLY)) == -1) {
printf("Error: file not found.\n");
return;
}
dup2(fd, STDIN_FILENO);
}
I first execute the binary on the left of the < in a fork() and then call the function above.
Did someone know why it didn't work ?
Thanks in advance for your answers.

Related

Bad address in my file I/O operation function

I am learning Linux file I/O operation functions and trying to write a program that could creat a new file, read or write it as well as change the permissions and attributes of a file. When I try to use function "open(pathname, O_RDWR);" to get a file descriptor and use "read()" function to read current file, there is always "bad address" error. However, when I use "open(pathname, O_RDWR|O_CREATE,S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)" before write, the file will be writen currectly.
Here are the source code when I using that two function.
//the welcome function is a function that serve as a function nemu bar
void Create()
{
char filepath[8];
int fd;
printf("请输入要创建的文件所在的目录及其名称:\n");
scanf("%s",&filepath);
fd = open(filepath, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH );//总是以默认的方式创建文件
if(fd == -1)
{
printf("创建失败,请重新输入文件名及其所在目录创建\n");
sleep(5);
Create();
}
else
{
char is;
printf("创建成功!是否对新创建的文件进行写操作?");
scanf("%s",&is);
if(is == 'y'||is == 'Y')
Write(fd,filepath);
else if(is == 'n'||is == 'N')
{
close(fd);
welcome();
}
else
{
printf("您的输入有误,回到欢迎界面中…\n");
close(fd);
sleep(5);
welcome();
}
}
}
void Write(int fd, char *path)
{
volatile int len;
if(fd == -1)
{
len = 0;
char filepath[8];
printf("请输入要写的文件的完整路径及其名称:");
scanf("%s",&filepath);
fd = open(filepath, O_RDWR);
if(fd == -1)
{
perror("open during write");
Write(-1, "");
}
char w1[BUFFERSIZE], *w;
printf("请输入要写入文件的内容,以回车结束:\n");
scanf("%s",&w1);
w = w1;
//用于写入的模块
int n = 1;
while(n > 0)
{
if(n = write(fd, w+len, (strlen(w)-len))<0)
{
perror("write");
return;
}
len += n;
}
char is;
printf("是否查看写入的内容?");
scanf("%s",&is);
if(is == 'Y'||is == 'y')
{
close(fd);
Read(filepath,strlen(w));
}
else
{
close(fd);
welcome();
}
}
else
{
printf("请输入要写入文件的数据:\n");
char w1[BUFFERSIZE];
char *w;
scanf("%s",&w1);
w = w1;
len = 0;
int n = 1;
while(n > 0)
{
n = write(fd, w+len, (strlen(w)-len));
len += n;
}
char is;
printf("是否查看写入的内容?");
scanf("%s",&is);
if(is == 'Y'||is == 'y')
{
close(fd);
Read(path,strlen(w));
}
else
{
close(fd);
welcome();
}
}
};
Could you please help me solve this question? I would be highly appreciated if you could help me!

Is there a way to make this code concise?

I'm currently learning and practicing c, but the exercise I'm doing wants each functions to have 25 lines limit (without changing { } or using single-line if statements!)
Please help if there's a way to make this even shorter.
void ft_write_file(void)
{
char c;
int fd;
int i;
i = 0;
if ((fd = open("write_exam", O_WRONLY | O_TRUNC | O_CREAT, 00777)) == -1)
{
ft_putstr("map error");
return ;
}
while (read(0, &c, 1))
{
write(fd, &c, 1);
if (c == '\n')
break ;
ft_allocate_g_var(i, c, 0);
i++;
}
int j = 0;
while (j < g_line)
{
while (read(0, &c, 1))
{
write(fd, &c, 1);
if (c == '\n')
break ;
}
j++;
}
close(fd);
}
To start with, don't try to do two things in one function. And try to write your functions with sensible arguments instead of hard-coding their subjects.
For example, your function is really doing two things:
Finding and potentially creating the output file (with a hard-coded name).
Copying the entire contents of one stream (hard-coded to stdin) to another stream.
So you could break that down: (prototypes only)
/* Returns fd or -1 on error */
int open_output(const char* name);
/* Returns number of bytes copied or -1 on error */
ssize_t copy_fd(int fd_dest, int fd_source);
Then your driver could be:
ssize_t copy_stdin_to_file(const char *name)
{
int fd = open_output(name);
if (fd < 0)
{
ft_putstr("Could not open output file");
return -1;
}
ssize_t copied = copy_fd(fd, 0);
if (copied < 0) {
ft_putstr("Could not write data.");
return copied;
}
}
A simple way would be to declare all the variables at the top in one line, for exemple :
char c; int fd; int i; i = 0;
Except from that I dont know, hope it can help a bit a least !

Redirecting execvp path

I'm trying to write a simple code which execute a program from subfolders from a input file and print thr result into a output file.
My problem is that when i execute the program it keeps failing on me. since the execvp command is trying to look for an exe named "a.out" on the wrong location. in (desktop rather than searching the correct path address).
here's the code. please help me out :)
pid_t runner;
char enter[] = "/home/demo/Desktop/OS/Ex1/Ex12/code/input.txt"; // input file
char path[] = "/home/demo/Desktop/OS/Ex1/Ex12/Ex1/ronen/"; correct path
char *r [] = {"./a.out", NULL};
int savedFD = dup(0);
int sever2Fd=dup(1);
int fdin = open(enter,O_RDONLY);
int fdout = open ("output.txt", O_CREAT | O_RDWR, 0466);
dup2(fdin, 0);
dup2(fdout, 1);
if ((runner = fork()) < 0) {perror("could not make fork");}
else if (runner == 0) {
if (execvp(r[0],r) < 0 ) {printf("Failed!\n");}
} else if (runner != 0) {
waitpid(runner,0,0);
dup2(savedFD, 0);
dup2(sever2Fd, 1);
printf("done\n");
}
close(fdin);close(fdout);
The answer was simple.
"chdir(wanted path)"
int dirchange = chdir(argv[1]);

Handle Spaces in File Path Unix programmatically

I'm trying to open files at random from my disk. Problem Is Most have Spaces in their path and won't
open unless I add backslashes on the fly, which is proving more difficult than I thought. While doing research I saw
how does unix handle full path name with space and arguments?
which say's that you can use backslashes or quotes but that was referring to passing arguments to the shell. Is it possible to do something like...
static int removeSpaces(char **inBuf, char **outBuf)
{
/* Declarations */
int rtrn;
char *pos;
/* Null terminate incoming buffer */
if((pos=strchr(*inBuf, '\n')) != NULL)
{
*pos = '\0';
}
rtrn = asprintf(outBuf, "\"%s\"", *inBuf);
if(rtrn < 0)
{
printf("Memory Error\n");
return -1;
}
return 0;
}
int main(void)
{
int rtrn;
char *file, *parsed;
rtrn = getFile(&file);
if(rtrn < 0)
{
printf("Can't Get File\n");
return -1;
}
rtrn = removeSpaces(&file, &parsed);
if(rtrn < 0)
{
printf("Can't Get File\n");
return -1;
}
printf("Parsed: %s\n", parsed);
fd = open(parsed, O_RDONLY);
if(fd < 0)
{
perror("Can't Open File\n");
return -1;
}
return 0;
}
I currently get no such file or directory, but i was wondering can I pass quoted file paths to open() or fopen(), or is that only the case with passing file paths to the shell. I'm asking because I saw very little information on the web specific to unix and unix-like systems, most was about windows (at least what i saw), and what i did find for unix was about the shell and not system calls.

how to get the size of a dir programatically in linux?

I want to get the exact size of a particular dir in linux through a C program.
I tried using statfs(path,struct statfs &) but it doesn't give exact size.
I also tried with stat() but it returns size as 4096 for any dir !
Please suggest me the way through which I can get the exact size of dir just like we get after "du -sh dirPath" command.
Also I dont wanna use du through system().
Thanks in advance.
Based on Jim Plank's example to get you started:
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
int main( int argc, char **argv ) {
DIR *d = opendir( "." );
if( d == NULL ) {
fprintf( stderr, "Cannot open current working directory\n" );
return 1;
}
struct dirent *de;
struct stat buf;
int total_size = 0;
for( de = readdir( d ); de != NULL; de = readdir( d ) ) {
int exists = stat( de->d_name, &buf );
if( exists < 0 ) {
fprintf( stderr, "Cannot read file statistics for %s\n", de->d_name );
} else {
total_size += buf.st_size;
}
}
closedir( d );
printf( "%d\n", total_size );
return 0;
}
Notes, considerations, and questions for the reader:
This is example is incomplete. See Plank's notes for more details.
What happens if there are locked files?
Do symbolic links need special handling (to avoid infinite loops)?
How would you output the full path name for erroneous files?
This answer is a starting point, not a complete and robust program to calculate directory sizes. If you need more help, read the source code for the du program.
You need to stat() all the files in the current directory and sub directories and add them up.
Consider using a recursive algorithm for this.
If you do not want to use 'system', but are ok to use 'pipe', 'fork', 'execlp' and 'du', you could build a pipe, fork a new process, redirect 'STDOUT' of the child in the pipe, exec 'du' in the child, and read the result in the parent. A sample code would be:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void) {
int pfd[2], n;
char str[1000];
if (pipe(pfd) < 0) {
printf("Oups, pipe failed. Exiting\n");
exit(-1);
}
n = fork();
if (n < 0) {
printf("Oups, fork failed. Exiting\n");
exit(-2);
} else if (n == 0) {
close(pfd[0]);
dup2(pfd[1], 1);
close(pfd[1]);
execlp("du", "du", "-sh", "/tmp", (char *) 0);
printf("Oups, execlp failed. Exiting\n"); /* This will be read by the parent. */
exit(-1); /* To avoid problem if execlp fails, especially if in a loop. */
} else {
close(pfd[1]);
n = read(pfd[0], str, 1000); /* Should be done in a loop until read return 0, but I am lazy. */
str[n] = '\0';
close(pfd[0]);
wait(&n); /* To avoid the zombie process. */
if (n == 0) {
printf("%s", str);
} else {
printf("Oups, du or execlp failed.\n");
}
}
}
I guess the solution may be useful for those who still may encounter the problem.
Here's the function that was written to imitate linux du program. It recursively goes through all directories and adds up file sizes.
Note, that this function is still incomplete, since it behaves incorrectly on hard links. One should add a container to store file descriptors that point to the same inode entity and use that to get rid of multiple counts of the very same file. lstat() is used to handle symlinks (aka soft links), hard links is an issue here.
size_t countDiskUsage(const char* pathname)
{
if (pathname == NULL) {
printf("Erorr: pathname is NULL\n");
}
struct stat stats;
if (lstat(pathname, &stats) == 0) {
if (S_ISREG(stats.st_mode)){
return stats.st_size;
}
} else {
perror("lstat\n");
}
DIR* dir = opendir(pathname);
if (dir == NULL) {
perror("Error");
return 0;
}
struct dirent *dirEntry;
size_t totalSize = 4096;
for (dirEntry = readdir(dir); dirEntry != NULL; dirEntry = readdir(dir)) {
long pathLength = sizeof(char) * (strlen(pathname) + strlen(dirEntry->d_name) + 2);
char* name = (char*)malloc(pathLength);
strcpy(name, pathname);
strcpy(name + strlen(pathname), "/");
strcpy(name + strlen(pathname) + 1, dirEntry->d_name);
if (dirEntry->d_type == DT_DIR) {
if (strcmp(dirEntry->d_name, ".") != 0 && strcmp(dirEntry->d_name, "..") != 0) {
totalSize += countDiskUsage(name);
}
} else {
int status = lstat(name, &stats);
if (status == 0) {
totalSize += stats.st_size;
} else {
perror("lstat\n");
}
}
free(name);
}
closedir(dir);
return totalSize;
}

Resources