I have an assignment wherein we have to create a shell. Part of that includes using generating paths to different unix commands. (example: /bash/ls). Using execv, I can get everything to work if I hardcode the path, but not if I generate it.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/syscall.h>
#include <unistd.h>
void command(char *args[]);
int main (int argc, char **argv) {
//get the command and arguments
char buffer[32];
char *b = buffer;
size_t bufferSize = 32;
int counter = 0;
char *tokens[10];
char *delims = " \t\n";
printf("dash> ");
getline(&b, &bufferSize, stdin);
tokens[0] = strtok(buffer, delims);
while (tokens[counter] != NULL) {
counter++;
tokens[counter] = strtok(NULL, delims);
}
command(tokens);
}
void command(char *args[]) {
//create path
char *path = NULL;
int length = strlen(args[0]);
path = malloc(5 + length + 1);
strcat(path, "/bin/");
strcat(path, args[0]);
char *input[2];
input[0] = malloc(512);
strcpy(input[0], path);
printf(input[0]); //the path prints out properly
//input[0] = "/bin/ls"; <--- this works!
input[1] = NULL;
//start execv
pid_t pid;
pid = fork();
if(pid < 0) {
printf("ERROR: fork failed.");
exit(0);
}
else if (pid == 0) {
execv(input[0], input);
printf("error.");
}
free(path);
free(input[0]);
}
anyone have any ideas? i'm pretty sure its an issue with malloc, but i'm not sure how to circumvent that.
The problem with getline() since you are reading form stdin, this
getline(&b, &bufferSize, stdin);
store new line \n char at the end of buffer & when you pass tokens to command() function, args will be ls\n instead of ls which is why execv failed with
execv: No such file or directory
So remove that extra \n character to parse tokens correctly, for e.g
ssize_t read;
read = getline(&b, &bufferSize, stdin); /* always check the return value */
if(read != -1 ) {
b[read-1] = '\0'; /* replace \n with \0 */
}
Related
I can compile the code, execute it with the file as a command line argument, but nothing happens. The interactive mode function prompts as normal, but not the batchMode function.
I'm trying to read in a line, and then execute that line.
example file
date
ls -la
cd
(Without spacing between lines. I can't get the formatting right on here.)
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#define bSize 1000
void driveLoop();
char *userInput(void);
void removeExit(char *original, char *subString); // removes string with substring "exit"
void batchMode(char *c);
int main(int argc, char **argv){
char *fTemp;
if (argc == 1)
driveLoop(); // calls the loop function that accepts input and executes commands.
else if (argc == 2)
batchMode(&argv[1][0]);
return 0;
}
void driveLoop(void){
char *comTokens[100];
char *tempTokens;
char *command;
char *cd;
char *cdDir;
char* cdTemp;
char cdBuf[bSize];
char checkExit[] = "exit";
for (;;){
printf("> ");
command = userInput(); // reads input
if (!*command) // allows for empty string error
break;
char *exitPtr = strstr(command, "exit"); // returns a value to a pointer if substring is found
removeExit(command, "exit");
puts(command); // updates the array after the function filter
int i = 0;
tempTokens = strtok(command, " \t\n"); // tokens are how the computer recognizes shell commands
while (tempTokens && i < 99){ // geeksforgeeks.com
comTokens[i++] = tempTokens;
tempTokens = strtok(NULL, "\t\n");
}
if (strcmp(comTokens[0], "exit") == 0) // exit if input is "exit" only
exit(0);
if(strcmp(comTokens[0], "cd") == 0){ // built in change directory command
cd = getcwd(cdBuf, sizeof(cdBuf));
cdDir = strcat(cd, "/");
cdTemp = strcat(cdDir, comTokens[1]); // cplusplus.com reference
chdir(cdTemp);
continue;
}
comTokens[i] = NULL;
pid_t cFork = fork(); // creates duplicate child process of parent
if (cFork == (pid_t) - 1){ // error check
perror("fork");
}
else if (cFork == 0) { // error codes found on cplusplus.com
execvp(comTokens[0], comTokens);
perror("exec");
}
else { // children are returned. parent executes
int status;
waitpid(cFork, &status, 0);
if (exitPtr != NULL){ // if substring exit was found, exit the program
exit(0);
}
}
}
}
char *userInput(void){ // referenced Linux man page - getline(3) (linux.die.net)
char *input = NULL;
size_t size = 0;
getline(&input, &size, stdin); // updates the size as it goes along
return input;
}
void removeExit(char *original, char *subString){ // removes exit from string
char *ex;
int len = strlen(subString);
while ((ex = strstr(original, subString))){ // Referenced from a Stack Overflow page.
*ex = '\0';
strcat(original, ex+len);
}
}
void batchMode(char *c){
char *tok[100];
char *batchTokens;
char *batchBuffer = NULL;
size_t batchSize = 0;
FILE *fp = fopen(c, "r");
unsigned int line = 1;
char buffer[bSize];
while(fgets(buffer, sizeof(buffer), fp)){
int i = 0;
char *toks = strtok(buffer, "\t\n");
while (toks && i < 99){
tok[i] = malloc (strlen(toks) + 1);
strcpy(tok[i++], toks);
toks = strtok(NULL, " \t\n");
}
tok[i] = NULL;
pid_t bFork = fork();
if (bFork == (pid_t) - 1)
perror("fork");
else if (bFork == 0){
execvp(tok[i], tok);
perror("exec");
}
else {
int status;
waitpid(bFork, &status, 0);
}
}
}
side note. This is a re-attempt from a previous question that was locked for inadequate information. I've updated my code and tried to be as detailed as possible.
I'll happily provide anything further to help answer my question.
Thank you all.
edit
I put in a fprintf to verify that it reads the file in, and it does.
First note in your input file, the last command cd isn't a system command, it is a shell built-it, so you would expect it to fail (unless handled specially).
Allocating for each token (tok[i]) as discussed with either strdup (if available) or simply malloc (strlen(toks) + 1); allows you to copy the current token to the block of memory allocated. Now each tok[i] will reference the individual token saved (instead of all pointing to the last token -- due to all being assigned the same pointer)
The biggest logic error in batchMode as your call to execvp with execvp (tok[i], tok); instead of properly providing the file to execute as tok[0]. Be mindful if the file to execute isn't in your PATH, you must provide an absolute path in your input file.
Making the changes, your batchMode could be written as follows (and removing all the unused variables and moving char *tok[100]; within the while loop so it is declared within that scope):
#include <sys/types.h>
#include <sys/wait.h>
...
void batchMode(char *c)
{
char buffer[bSize];
FILE *fp = fopen (c, "r");
if (!fp) {
perror ("fopen-c");
return;
}
while (fgets (buffer, sizeof(buffer), fp)) {
int i = 0;
char *tok[100];
char *toks = strtok(buffer, " \t\n");
while (toks && i < 99){
tok[i] = malloc (strlen(toks) + 1);
strcpy(tok[i++], toks);
toks = strtok(NULL, " \t\n");
}
tok[i] = NULL;
pid_t bFork = fork();
if (bFork == (pid_t) - 1)
perror("fork");
else if (bFork == 0){
execvp (tok[0], tok);
perror("exec");
}
else {
int status;
waitpid(bFork, &status, 0);
}
}
}
Example Input Files
I have two simple input files tested:
$ cat dat/toksfile.txt
echo hello
echo goodbye
$ cat dat/toksfile2.txt
date
ls -al dat/toksfile.txt
cd
Example Use/Output
$ ./bin/shellorbatch dat/toksfile.txt
hello
goodbye
$ ./bin/shellorbatch dat/toksfile2.txt
Tue Feb 4 23:47:00 CST 2020
-rw-r--r-- 1 david david 24 Feb 4 23:24 dat/toksfile.txt
exec: No such file or directory
Look things over and let me know if you have questions:
There is probably more issues at hand, but for now my problem is that when I compile and run this like so:
cc -o parser parser.c
./parser
I expect it to open a particular file, read from it, and parse it. However, it seems to expect me to provide input and I have to Ctrl-C to kill it. Am I using fgets wrong? I tried getline() with the same results. I added the puts() to make sure it was reading what I expected and it does. Any help is appreciated.
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <limits.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
FILE *fp;
char buf[1024];
char *tmp, *pattern, *dir;
char *skip, *p;
char *tok[5];
char **ap;
size_t sz = 0;
ssize_t len;
int i;
int action = 0; // placeholder
int fileinto = 1; // placeholder
char path[PATH_MAX] = "/home/edgar/.patfile";
fp = fopen(path, "r");
if (fp == NULL)
fprintf(stderr, "fopen failed");
while (fgets(buf, sizeof(buf), fp) != NULL) {
buf[strcspn(buf, "\n")] = '\0';
// skip comments
for (skip = buf; *skip; ++skip) {
if (*skip == '#') {
*skip = '\0';
break;
}
}
// skip empty lines
if (strlen(buf) == 0)
continue;
puts(buf); // debug only
// make a copy
tmp = strdup(buf);
for (i = 0, ap = tok; ap < &tok[4] && (*ap = strsep(&tmp, " ")) != NULL; i++) {
if (**ap != '\0')
ap++;
}
while (i >= 0) {
if(tok[i] == "match")
pattern = tok[i + 1];
if(tok[i] == "fileinto") {
action = fileinto;
dir = tok[i + 1];
}
}
}
free(tmp);
fclose(fp);
exit(0);
}
My instructor gave us a basic shell in C to expand upon, and I'm currently working on getting the shell to change directories whenever the user enters 'cd [directory]' into the command line. I've gotten it to stop seg faulting, but it won't change directories. Can anyone tell me why it isn't working?
Here is my code so far:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
/* Array holds arguments: args[0] is the command. */
static char *args[512];
pid_t pid;
int command_pipe[2];
#define READ 0
#define WRITE 1
int chdir(const char* path);
static int
command (int input, int first, int last)
{
int pipettes[2];
/* Invoke pipe */
pipe (pipettes);
pid = fork ();
if (pid == 0)
{
if (first == 1 && last == 0 && input == 0)
{
// 1st cmd
dup2 (pipettes[WRITE], STDOUT_FILENO);
}
else if (first == 0 && last == 0 && input != 0)
{
// Mid cmd
dup2 (input, STDIN_FILENO);
dup2 (pipettes[WRITE], STDOUT_FILENO);
}
else
{
// Last cmd
dup2 (input, STDIN_FILENO);
}
if (execvp (args[0], args) == -1)
_exit (EXIT_FAILURE); // If child fails
}
if (input != 0)
close (input);
close (pipettes[WRITE]);
// If last command, nothing more needs to be read
if (last == 1)
close (pipettes[READ]);
return pipettes[READ];
}
static void
cleanup (int n)
{
int i;
for (i = 0; i < n; ++i)
wait (NULL);
}
static int go (char *cmd, int input, int first, int last);
static char line[1024];
static int n = 0;
int
main (int argc, char* argv[])
{
while (1)
{
/* Initial Prompt */
printf ("?> ");
fflush (NULL);
/* Read in command */
if (!fgets (line, 1024, stdin))
return 0;
int input = 0;
int first = 1;
char *cmd = line;
char *next = strchr (cmd, '|'); /* Find initial '|' */
char *also = strchr (cmd, ';'); /* Find initial ';' */
char *directory = argv[1];
while (next != NULL)
{
/* 'next' points to '|' */
*next = '\0';
input = go (cmd, input, first, 0);
cmd = next + 1;
next = strchr (cmd, '|'); /* Find next '|' */
first = 0;
}
if(argv[0] == "cd"){
chdir(directory);
}
input = go (cmd, input, first, 1);
cleanup (n);
n = 0;
}
return 0;
}
static char *
skip_white_space (char *s)
{
while (isspace (*s))
++s;
return s;
}
static void
parse (char *cmd)
{
cmd = skip_white_space (cmd);
char *next = strchr (cmd, ' ');
int i = 0;
while (next != NULL)
{
next[0] = '\0';
args[i] = cmd;
++i;
cmd = skip_white_space (next + 1);
next = strchr (cmd, ' ');
}
if (cmd[0] != '\0')
{
args[i] = cmd;
next = strchr (cmd, '\n');
next[0] = '\0';
++i;
}
args[i] = NULL;
}
static int
go (char *cmd, int input, int first, int last)
{
parse (cmd);
if (args[0] != NULL)
{
if (strcmp (args[0], "exit") == 0)
exit (0);
n += 1;
return command (input, first, last);
}
return 0;
}
Your immediate problem seems to lie here:
if(argv[0] == "cd"){
chdir(directory);
I think you'll find that argv[0] is the implementation's representation of your program name, not the command you just entered, which is probably in args. Or cmd. Or somewhere.
Even once you fix that, you shouldn't be using == for string comparisons in C. One of the strcmp family is the correct way to do it.
Ok, so, I am currently working on implementing a simpleshell history. Part of the requirement is to output all the commands to a file having them numbered per line.
I have everything else working well at the moment, but the output to the file is giving me a very specific issue. It is saving the commands and putting them in the file numbered, but it is giving me around 3 duplicate copies per command. Ex:
1 hello
1 hello
1 hello
2 there1 hello
2 there3 how1 hello
2 there3 how4 are1 hello
2 there3 how4 are5 you
I have searched over the previous topics on this site, and read up on file i/o. As far as I can tell, I am doing it correctly:
#include "parser.h"
#include "shell.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct node {
char info[MAXINPUTLINE]; //Struct setup for linked list.
struct node *link;
} *start;
void create(char[]); //Function prototypes.
void insert_end(char[]);
void last_cmd(char[]);
int main(void) {
char input[MAXINPUTLINE];
int count = 2;
start=NULL;
char data[MAXINPUTLINE];
FILE *file;
file = fopen(".simpleshell_history", "w");
char *history="history";
char *last="!!";
signal_c_init();
printf("Welcome to the sample shell! You may enter commands here, one\n");
printf("per line. When you're finished, press Ctrl+D on a line by\n");
printf("itself. I understand basic commands and arguments separated by\n");
printf("spaces, redirection with < and >, up to two commands joined\n");
printf("by a pipe, tilde expansion, and background commands with &.\n\n");
printf("\nclsh$ ");
fgets(data, sizeof(data), stdin);
create(data);
fprintf(file, "%d ", 1);
fprintf(file, "%s", data);
parse(data);
printf("\nclsh$ ");
while (fgets(input, sizeof(input), stdin)) {
stripcrlf(input);
parse(input);
insert_end(input);
fprintf(file, "%d ", count);
fprintf(file, "%s", input);
count++;
printf("\nclsh$ ");
}
fclose(file);
return 0;
}
//Functions below.
//Creation Function.
void create(char data[])
{
struct node *temp;
temp = (struct node *)malloc(sizeof(struct node));
if (start == NULL)
{
strcpy(temp->info, data);
temp->link=NULL;
start=temp;
}
}
//Insertion function
void insert_end(char data[])
{
struct node *ptr, *tempnode;
ptr = start;
while(1)
{
if(ptr->link != NULL)
{
ptr=ptr->link;
}
else
break;
}
tempnode=(struct node *)malloc(sizeof(struct node));
strcpy(tempnode->info, data);
tempnode->link=NULL;
ptr->link=tempnode;
}
//Last command repeat function
void last_cmd(char input[])
{
struct node *ptr;
ptr = start;
int i;
int length=0;
char temp[MAXINPUTLINE];
while (ptr!=NULL)
{
ptr=ptr->link;
length++;
}
ptr=start;
for (i=0; i<length-1; i++)
{
ptr=ptr->link;
strcpy(temp, ptr->info);
}
printf("%s", temp);
parse(temp);
}
I understand that some of my previous questions have not been received well, but I have tried my hardest on this, and am only asking this question as a final resort. I hope that I have done it in a manner that isn't annoying or against any rules, and can work to improve my reputation around here.
Thank you.
Edit: Ok, so I've isolated the problem down to the file that parses the input. It is causing the duplication of the lines, but I can't figure out how at this point. Any help is appreciated, but I will keep at it either way, thanks again for all the tips and advice.
Parser:
#include "parser.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <pwd.h>
#include <fcntl.h>
#include <limits.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/resource.h>
#include <sys/wait.h>
/* Static Variables Referenced only in this file */
static int pipefd[2];
static int background;
void parse(char *cmdline)
{
char *cmdpart[2];
pipefd[0] = PARSE_NOPIPE; /* Init: default is no pipe */
background = checkbackground(cmdline);
/* Separate into individual commands if there is a pipe symbol. */
if (strstr(cmdline, "|"))
pipefd[0] = PARSE_USEPIPE;
/* Must do the strtok() stuff before calling parse_cmd because
strtok is used in parse_cmd or the functions parse_cmd calls. */
cmdpart[0] = strtok(cmdline, "|");
cmdpart[1] = strtok((char *)NULL, "|");
parse_cmd(cmdpart[0]);
if (cmdpart[1]) parse_cmd(cmdpart[1]);
}
/* parse_cmd will do what is necessary to separate out cmdpart and run
the specified command. */
void parse_cmd(char *cmdpart)
{
int setoutpipe = 0; /* TRUE if need to set up output pipe
after forking */
int pid; /* Set to pid of child process */
int fd; /* fd to use for input redirection */
char *args[MAXARGS + 5];
char *filename; /* Filename to use for I/O redirection */
splitcmd(cmdpart, args);
if (pipefd[0] == PARSE_USEPIPE) {
pipe(pipefd);
setoutpipe = 1;
}
pid = fork();
if (!pid) { /* child */
if (setoutpipe) {
dup2(pipefd[1], 1); /* connect stdout to pipe if necessary */
}
if (!setoutpipe && (pipefd[0] > -1)) {
/* Need to set up an input pipe. */
dup2(pipefd[0], 0);
}
filename = parseredir('<', args);
if (filename) { /* Input redirection */
fd = open(filename, O_RDONLY);
if (!fd) {
fprintf(stderr, "Couldn't redirect from %s", filename);
exit(255);
}
dup2(fd, 0);
}
if ((filename = parseredir('>', args))) { /* Output redirection */
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
if (!fd) {
fprintf(stderr, "Couldn't redirect to %s\n", filename);
exit(255);
}
dup2(fd, 1);
}
if (!args[0]) {
fprintf(stderr, "No program name specified.\n");
exit(255);
}
execvp(args[0], args);
/* If failed, die. */
exit(255);
} else { /* parent */
if ((!background) &&
(!setoutpipe))
waitpid(pid, (int *)NULL, 0);
else
if (background)
fprintf(stderr, "BG process started: %d\n", (int) pid);
if (pipefd[0] > -1) { /* Close the pipe if necessary. */
if (setoutpipe)
close(pipefd[1]);
else
close(pipefd[0]);
}
} /* if (!pid) */
freeargs(args);
} /* parse_cmd() */
/* splitcmd() will split a string into its component parts.
Since splitcmd() uses strdup, freeargs() should be called on the
args array after it is not used anymore. */
void splitcmd(char *cmdpart, char *args[])
{
int counter = 0;
char *tempstr;
tempstr = strtok(cmdpart, " ");
args[0] = (char *)NULL;
while (tempstr && (counter < MAXARGS - 1)) {
args[counter] = strdup(expandtilde(tempstr));
args[counter + 1] = (char *)NULL;
counter++;
tempstr = strtok(NULL, " ");
}
if (tempstr) { /* Broke out of loop because of num of args */
fprintf(stderr, "WARNING: argument limit reached, command may be truncated.\n");
}
}
/* expandtilde() will perform tilde expansion on str if necessary. */
char *expandtilde(char *str)
{
static char retval[MAXINPUTLINE];
char tempstr[MAXINPUTLINE];
char *homedir;
char *tempptr;
int counter;
if (str[0] != '~') return str; /* No tilde -- no expansion. */
strcpy(tempstr, (str + 1)); /* Make a temporary copy of the string */
if ((tempstr[0] == '/') || (tempstr[0] == 0))
tempptr = (char *)NULL;
else { /* Only parse up to a slash */
/* strtok() cannot be used here because it is being used in the function
that calls expandtilde(). Therefore, use a simple substitute. */
if (strstr(tempstr, "/"))
*(strstr(tempstr, "/")) = 0;
tempptr = tempstr;
}
if ((!tempptr) || !tempptr[0]) { /* Get user's own homedir */
homedir = gethomedir();
} else { /* Get specified user's homedir */
homedir = getuserhomedir(tempptr);
}
/* Now generate the output string in retval. */
strcpy(retval, homedir); /* Put the homedir in there */
/* Now take care of adding in the rest of the parameter */
counter = 1;
while ((str[counter]) && (str[counter] != '/')) counter++;
strcat(retval, (str + counter));
return retval;
}
/* freeargs will free up the memory that was dynamically allocated for the
array */
void freeargs(char *args[])
{
int counter = 0;
while (args[counter]) {
free(args[counter]);
counter++;
}
}
/* Calculates number of arguments in args */
void calcargc(char *args[], int *argc)
{
*argc = 0;
while (args[*argc]) {
(*argc)++; /* Increment while non-null */
}
(*argc)--; /* Decrement after finding a null */
}
/* parseredir will see if it can find a redirection operator oper
in the array args[], and, if so, it will return the parameter (filename)
to that operator. */
char *parseredir(char oper, char *args[])
{
int counter;
int argc;
static char retval[MAXINPUTLINE];
calcargc(args, &argc);
for (counter = argc; counter >= 0; counter--) {
fflush(stderr);
if (args[counter][0] == oper) {
if (args[counter][1]) { /* Filename specified without a space */
strcpy(retval, args[counter] + 1);
argsdelete(args + counter);
return retval;
} else { /* Space seperates oper from filename */
if (!args[counter+1]) { /* Missing filename */
fprintf(stderr, "Error: operator %c without filename", oper);
exit(255);
}
strcpy(retval, args[counter+1]);
argsdelete(args + counter + 1);
argsdelete(args + counter);
return retval;
}
}
}
return NULL; /* No match */
}
/* Argsdelete will remove a string from the array */
void argsdelete(char *args[])
{
int counter = 0;
if (!args[counter]) return; /* Empty argument list: do nothing */
free(args[counter]);
while (args[counter]) {
args[counter] = args[counter + 1];
counter++;
}
}
Edit: Ok, so I got it. The parse(input) and stripcrlf(input) were messing with it. How, I'm not sure, but that's beside the point. What I needed to do in the end was to not just open up the file and leave it open until it was closed just before the end of the program. I had to do this: fopen(".simpleshell_history", "a"); fprintf(file, "%d ", count); fprintf(file, "%s\n", input); fclose(file); Basically, I had to open the file, put the stuff in, and then close it before the parser and such could have an effect. The output is perfect now. Thanks everyone for your help.
I removed irrelevant lines from the question code:
#include <stdio.h>
#include <string.h>
#define MAXINPUTLINE 100
int main(void) {
char input[MAXINPUTLINE];
int count = 2;
// start=NULL;
char data[MAXINPUTLINE];
FILE *file;
I modified the file name from ".simpleshell_history" to "simpleshell_history" so that the file was not hidden (for testing purposes).
file = fopen("simpleshell_history", "w");
// char *history="history";
// char *last="!!";
// signal_c_init();
printf("Welcome to the sample shell! You may enter commands here, one\n");
printf("per line. When you're finished, press Ctrl+D on a line by\n");
printf("itself. I understand basic commands and arguments separated by\n");
printf("spaces, redirection with < and >, up to two commands joined\n");
printf("by a pipe, tilde expansion, and background commands with &.\n\n");
printf("\nclsh$ ");
fgets(data, sizeof(data), stdin);
// create(data);
fprintf(file, "%d ", 1);
fprintf(file, "%s", data);
// parse(data);
printf("\nclsh$ ");
while (fgets(input, sizeof(input), stdin)) {
// stripcrlf(input);
// parse(input);
// insert_end(input);
fprintf(file, "%d ", count);
fprintf(file, "%s", input);
I added the following two lines to allow a way to break out of the loop.
if(0 == strncmp("quit", input, 4))
break;
count++;
printf("\nclsh$ ");
}
fclose(file);
return 0;
}
I ran the program:
> ./test
Welcome to the sample shell! You may enter commands here, one
per line. When you're finished, press Ctrl+D on a line by
itself. I understand basic commands and arguments separated by
spaces, redirection with < and >, up to two commands joined
by a pipe, tilde expansion, and background commands with &.
clsh$ hello
clsh$ there
clsh$ QUIT
clsh$ QUIT
clsh$ quit
The content of the file "simpleshell_history":
1 hello
2 there
3 QUIT
4 QUIT
5 quit
The code seems to execute as expected with no unexpected "Output file content duplication".
in my program, I provide a directory which contains text files. Each of the text files contain a few hundred lines in the following format
Username,Password,BloodType,Domain,Number
I then create a thread for each file in the directory which will merge-sort(by number) these lines into the array char* text_lines[6000];
I can't figure out why I'm getting a segmentation fault because I'm getting different output on every run.
Heres my code:
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <sys/types.h>
#include <dirent.h>
#include <string.h>
void store_line(char* line);
void* my_merge_sort(void* file);
char** text_lines;
int main(int argc, char* argv[])
{
if(argc != 2)
{
fprintf(stderr, "usage: ./coolsort <directory>\n");
}
else
{
text_lines = malloc(6000 * sizeof(char*));
DIR* the_directory;
int filecount = 0;
struct dirent* directory_files[50];
if((the_directory = opendir(argv[1])) != NULL)
{
//make a list of the files in the directory
while((directory_files[filecount++] = readdir(the_directory))) ;
filecount--;
//<<<DEBUGGING INFO>
int i;
fprintf(stderr,"there are %i files in %s:\n", filecount, argv[1]);
for(i = 0; i < filecount; i++)
{
fprintf(stderr, "%s\n",directory_files[i]->d_name);
}
char cwd[512];
chdir(argv[1]);
getcwd(cwd, sizeof(cwd));
fprintf(stderr, "the CWD is: %s\n", cwd);
//<DEBUGGING INFO>>>
//lets start some threads
pthread_t threads[filecount-2];
int x = 0;
for(i = 0; i < (filecount); i++ )
{
if (!strcmp (directory_files[i]->d_name, "."))
continue;
if (!strcmp (directory_files[i]->d_name, ".."))
continue;
pthread_create(&threads[x++], NULL, my_merge_sort, (void*)directory_files[i]->d_name);
}
//do stuff here
//
}
else
{
fprintf(stderr, "Failed to open directory: %s\n", argv[1]);
}
}
}
void* my_merge_sort(void* file)
{
fprintf(stderr, "We got into the function!\n");
FILE* fp = fopen(file, "r");
char* buffer;
char* line;
char delim[2] = "\n";
int numbytes;
//minimize I/O's by reading the entire file into memory;
fseek(fp, 0L, SEEK_END);
numbytes = ftell(fp);
fseek(fp, 0L, SEEK_SET);
buffer = (char*)calloc(numbytes, sizeof(char));
fread(buffer, sizeof(char), numbytes, fp);
fclose(fp);
//now read the buffer by '\n' delimiters
line = strtok(buffer, delim);
fprintf(stderr, "Heres the while loop\n");
while(line != NULL)
{
store_line(line);
line = strtok(buffer, NULL);
}
free(buffer);
}
void store_line(char* line)
{
//extract the ID.no, which is the fifth comma-seperated-token.
char delim[] = ",";
char* buff;
int id;
int i;
strtok(line, delim);
for(i = 0; i < 3; i++)
{
strtok(line, NULL);
}
buff = strtok(line, NULL);
id = atoi(buff);
//copy the line to text_lines[id]
memcpy(text_lines[id], line, strlen(line));
}
edit: I checked to make sure that it would fit into the initial array, and found that the highest ID is only 3000;
You use of strtok() is wrong:
line = strtok(buffer, NULL);
should be
line = strtok(NULL, delim);
Another mistakes should be fixed similarly.
The elements of text_lines are uninitialized:
text_lines = malloc(6000 * sizeof(char*));
this allocated 6000 pointers to char, but none of these pointers are initialized.