I've got a basic shell program that can change directories using cd and list the files with ls. I want to extend this further by adding optional flags to the ls command. In particular, I want to implement the ls -l (lowercase 'ell') command whereby it shows a total sum of all file sizes on the line before the long listing. I'm not sure how I can implement this. My code I have so far is:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <dirent.h>
#define MAX_LENGTH 1024
#define DELIMS " \t\r\n"
int main(int argc, char *argv[]) {
char *cmd;
char line[MAX_LENGTH];
while(1){
printf("> ");
if (!fgets(line, MAX_LENGTH, stdin)) break;
if ((cmd = strtok(line, DELIMS))) {
char *arg = strtok(0, DELIMS);
if (strcmp(cmd, "cd") == 0) {
if (!arg) fprintf(stderr, "cd missing argument.\n");
else {
chdir(arg);
}
}
else if (strcmp(cmd, "ls") == 0) {
struct dirent **namelist;
int n;
n = scandir(".",&namelist,NULL,alphasort);
if(n < 0)
{
perror("scandir");
exit(EXIT_FAILURE);
}
else
{
while (n--)
{
printf("%s\n",namelist[n]->d_name);
free(namelist[n]);
}
free(namelist);
}
}
else if (strcmp(cmd, "exit") == 0) {
break;
}
}
}
return 0;
}
Related
I have been trying to implement an exit command on my C shell. I have tried the fork-exec method since it's a system call.
When I run the program, it prompts for the stdin input and when I type in "exit" it returns a "segmentation fault (core dumped)" error.
What am I doing wrong?
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <fcntl.h>
#define ARGVMAX 100
#define LINESIZE 1024
#define EXITCMD "exit"
//makeargv - builds an argv vector from words in a string
int makeargv(char *s, char *argv[ARGVMAX]) {
int ntokens = 0;
if (s == NULL || argv == NULL || ARGVMAX == 0)
return -1;
argv[ntokens] = strtok(s, " \t\n");
while ((argv[ntokens] != NULL) && (ntokens < ARGVMAX)) {
ntokens++;
argv[ntokens] = strtok(NULL, " \t\n");
}
argv[ntokens] = NULL; // it must terminate with NULL
return ntokens;
}
void prompt() {
printf("sish> ");
fflush(stdout); //writes the prompt
}
/****** MAIN ******/
int main() {
char line[LINESIZE];
int wstatus;
while (1) {
prompt();
if (fgets(line, LINESIZE, stdin) == NULL)
break;
// TODO:
if(fgets(line, LINESIZE, strcmp(stdin, EXITCMD )) == 0)
return 0;
signal(SIGINT, SIG_DFL);
if (fork() == 0) exit(execvp(line[0], line));
{
signal(SIGINT, SIG_IGN);
}
wait(&wstatus);
if(WIFEXITED(wstatus))
printf("<%d>", WEXITSTATUS(wstatus));
}
return 0;
}
After reviewing and cleaning the code, I was finally able to implement the exit command.
Here goes the code:
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <fcntl.h>
#define ARGVMAX 100
#define LINESIZE 1024
#define EXITCMD "exit"
//makeargv - build an argv vector from words in a string
int makeargv(char *s, char *argv[ARGVMAX]) {
int ntokens = 0;
if (s == NULL || argv == NULL || ARGVMAX == 0)
return -1;
argv[ntokens] = strtok(s, " \t\n");
while ((argv[ntokens] != NULL) && (ntokens < ARGVMAX)) {
ntokens++;
argv[ntokens] = strtok(NULL, " \t\n");
}
argv[ntokens] = NULL; // it must terminate with NULL
return ntokens;
}
void prompt() {
printf("C Shell >> ");
fflush(stdout); //writes the prompt
}
/****** MAIN ******/
int main() {
char line[LINESIZE];
while (1) {
prompt();
if (fgets(line, LINESIZE, stdin) == NULL)
break;
// TODO:
char *p = strchr(line, '\n');
if (p)
*p = 0;
if(strcmp(line, "exit") == 0)
break;
char *args[] = {line, (char*) 0};
int pid = fork();
if (pid == 0){
execvp(line, args);
perror("Command Error!");
exit(1);
} else {
wait(NULL);
}
return 0;
}
return 0;
}
I am building a simple shell I want to handle the argument of exit. How can I handle it here?
I have already handle exit but I want to handle exit with argument.
Usage: exit status, where status is an integer used to exit the shell
For example
exit 98
exit 1
Present output
sh: No file or directory found
Expected Output
Exit the shell with the specific status code
shell.h
#ifndef SHELL_H
#define SHELL_H
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
void print_prompt(void);
void tok_str(char *, char **);
int add_path(char **cmd);
void print_env(char **);
#endif
shell.c
#include "shell.h"
int main(int argc, char **argv, char **env)
{
char *buf = NULL;
size_t buflen = 0;
char *cmd[20];
do
{
fprintf(stderr, ":) ");
if (getline(&buf, &buflen, stdin) == -1)
{
if (feof(stdin))
{
free(buf);
buf = NULL;
exit(EXIT_SUCCESS);
}
perror("Error occurred");
free(buf);
buf = NULL;
exit(EXIT_FAILURE);
}
if (buf[0] == '\0' || strcmp(buf, "\n") == 0)
{
free(buf);
buf = NULL;
continue;
}
if (strcmp(buf, "exit\n") == 0)
{
free(buf);
buf = NULL;
exit(EXIT_SUCCESS);
}
if (strcmp(buf, "env\n") == 0)
{
print_env(env);
free(buf);
buf = NULL;
continue;
}
tok_str(buf, cmd);
add_path(cmd);
if (fork() == 0)
{
execve(cmd[0], cmd, env);
printf("%s: No such file or directory\n", argv[0]);
exit(EXIT_SUCCESS);
}else
{
wait(NULL);
free(buf);
buf = NULL;
}
} while (1);
exit(EXIT_SUCCESS);
}
utils.c
#include "shell.h"
void tok_str(char *buf, char **cmd)
{
char *ptr;
int i = 0;
ptr = strtok(buf, " \n");
while (ptr)
{
cmd[i] = ptr;
ptr = strtok(NULL, " \n");
i++;
}
cmd[i] = NULL;
}
int add_path(char **cmd)
{
const char *PATH[] = {"/usr/local/bin/ls", "/bin/", "/sbin/", "/usr/bin/", NULL};
unsigned int i = 0;
struct stat st;
while (PATH[i])
{
char s[120] = "";
strcat(s, PATH[i]);
strcat(s, cmd[0]);
if (stat(s, &st) == 0)
{
cmd[0] = s;
return (0);
}
i++;
}
return (-1);
}
void print_env(char **env)
{
int i = 0;
while (env[i])
printf("%s\n", env[i++]);
}
Hello and thank you for attention. I am writing my own shell and I have small problem with redirect output to file. For example user wrote: ls -l >> output. If I find ">>", I should redirect first part of command, I mean call effect ls -l to file "output". I try to do it in case 1 but to file is redirected just one lane and the program is stopped, there is not appear "Shell -> " and nothing is going on. Can you give some advice to solve that problem? Thanks.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <ctype.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
int *parse(char *linia, char **argv)
{
int counter = -1;
while (*linia != '\0')
{
while (*linia == ' ' || *linia == '\t' || *linia == '\n')
*linia++ = '\0';
*argv++ = linia;
counter++;
while (*linia != '\0' && *linia != ' ' && *linia != '\t' && *linia != '\n')
linia++;
}
*argv = '\0';
return counter;
}
void execute(char **argv)
{
pid_t pid;
int status;
if ((pid = fork()) < 0)
{
printf("*** ERROR ***\n");
exit(1);
}
else if (pid == 0)
{
if (execvp(*argv, argv) < 0)
{
printf("*** ERROR ***\n");
exit(1);
}
}
else
{
while (wait(&status) != pid);
}
}
int specialChar(char *argv)
{
int i=0;
while(argv[i]!='\0')
{
if(argv[i]=='>' && argv[i+1]=='>')
return 1;
else if(argv[i]=='&')
return 2;
else if(argv[i]=='|')
return 3;
i++;
}
}
void main()
{
char command[20];
char *argv[64];
char **history = (char**)malloc(20*sizeof(char*));
int counter1=-1;
int counter2=0;
for(counter2 = 0; counter2<20; counter2++)
{
history[counter2]=(char*)malloc(100*sizeof(char));
}
int start = 0;
FILE *file;
file=fopen("his", "w");
if(!file)
printf("ERROR");
int i=0;
while (1)
{
printf("Shell -> ");
gets(command);
counter1++;
strcpy(history[counter1],command);
fopen("his", "w");
if(counter1<20)
for(i=0; i<=counter1; i++)
{
fprintf(file,"%s\n",history[i]);
}
else
for(i=counter1-20; i<counter1; i++)
{
fprintf(file,"%s\n",history[i]);
}
fflush(file);
printf("\n");
switch(specialChar(command))
{
case 1:
i = parse(command, argv);
int file1 = open(argv[i], O_APPEND | O_WRONLY);
dup2(file1,1) ;
if (strcmp(argv[0], "exit") == 0)
exit(0);
execute(argv);
close(file1);
break;
case 2:
break;
case 3:
break;
default:
parse(command, argv);
if (strcmp(argv[0], "exit") == 0)
exit(0);
execute(argv);
break;
}
fclose(file);
}
}
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
int show_dir_content(char *path);
int show_dir_content(char *path)
{
char readbuffer[80];
char buffer[150];
char uzun[9999];
int totalfound = 0;
DIR *d = opendir(path);
if(d == NULL)
return -3;
struct dirent *dir;
int piper[2];
pid_t typer = 0;
while((dir = readdir(d)) != NULL)
{
pipe(piper);
typer = fork();
if(typer == 0)
{
close(piper[0]);
if(dir->d_type != DT_DIR)
{ // if the type is not directory blue
// printf("%s\n",dir->d_name);
sprintf(buffer, "%s", dir->d_name);
write(piper[1], buffer, (strlen(buffer) + 1));
}
else if(dir->d_type == DT_DIR && strcmp(dir->d_name, ".") != 0 &&
strcmp(dir->d_name, "..") != 0) // if it is a directory
{
char d_path[255]; // here I am using sprintf which is safer than
// strcat
sprintf(d_path, "%s/%s", path, dir->d_name);
show_dir_content(d_path); // recall with the new path
}
exit(0);
close(piper[1]);
}
else if(typer > 0)
{
close(piper[1]);
read(piper[0], readbuffer, sizeof(readbuffer));
// strcat(uzun,readbuffer);
// close(piper[0]);
break;
}
}
while(wait(NULL) > 0)
;
closedir(d);
// printf("%s x_x\n",uzun);
return totalfound + 1; // finally close the directory
}
int main(int argc, char **argv)
{
show_dir_content(argv[1]);
return (0);
}
It doesn't execute on forked child, only on parent and only once.
I use Linux Mint 18 and gcc comes with it.
i don't what causes it, it doesn't seems to be Segmentation fault because it exits correctly. I would be more then happy if anyone can see my errors here
I'm debugging on CLion.
I'm writing a small shell in C as an exercis to learn about linux and C. Now I can execute custom commands and the exit command, but I can't execute a builtin CD command since I don't know how to split it in two part (the cd command and the name of the directory to CD to).
The desired functionality is that my program should accept the cd command with a parameter that is the directory. I can do it with command-line arguments but I don't know how to do it in its current form. How can it be done?
#include <sys/stat.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <dirent.h>
#include <errno.h>
#include <stdarg.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#define BUFFER_LEN 1024
#define BUFFERSIZE 1024
int mystrcmp(char const *, char const *);
void err_syserr(char *fmt, ...)
{
int errnum = errno;
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
if (errnum != 0)
fprintf(stderr, "(%d: %s)\n", errnum, strerror(errnum));
exit(EXIT_FAILURE);
}
int main() {
char line[BUFFER_LEN];
char* argv[100];
char* path= "/bin/";
char progpath[20];
int argc;
size_t length;
char *token;
int i=0;
int pid;
while(1) {
i = 0;
printf("miniShell>> ");
if(!fgets(line, BUFFER_LEN, stdin)) {
break;
}
length = strlen(line);
if (line[length - 1] == '\n') {
line[length - 1] = '\0';
}
if(strcmp(line, "exit")==0) {
break;
}
if(strcmp(line, "cd")==0) {
/*printf("change directory to %s\n", argv[2]);
chdir(argv[2]);*/
}
token = strtok(line," ");
while(token!=NULL) {
argv[i]=token;
token = strtok(NULL," ");
i++;
}
argv[i]=NULL;
argc=i;
for(i=0; i<argc; i++) {
printf("%s\n", argv[i]);
}
strcpy(progpath, path);
strcat(progpath, argv[0]);
for(i=0; i<strlen(progpath); i++) {
if(progpath[i]=='\n') {
progpath[i]='\0';
}
}
pid= fork();
if(pid==0) {
execvp(progpath,argv);
fprintf(stderr, "Child process could not do execvp\n");
} else {
wait(NULL);
printf("Child exited\n");
}
}
return (0);
}
int mystrcmp(char const *p, char const *q)
{
int i = 0;
for(i = 0; q[i]; i++)
{
if(p[i] != q[i])
return -1;
}
return 0;
}
int cd(char *pth) {
char path[BUFFERSIZE];
char cwd[BUFFERSIZE];
char * return_value;
int other_return;
strcpy(path,pth);
if(pth[0] != '/')
{
return_value = getcwd(cwd,sizeof(cwd));
strcat(cwd,"/");
strcat(cwd,path);
other_return = chdir(cwd);
} else {
other_return = chdir(pth);
}
printf("Spawned foreground process: %d\n", getpid());
return 0;
}
Use strpbrk or strsep to split your input into white-space separated tokens, then use strcmp on the first one and use the remaining ones as arguments.
This answer to a related question has an example, and here are some notes on portability.
I'd suggest to parse your argument with the getopt function from unistd library. It's covered in this question too.
Here is the documentation for getopt command.