I am coding a basic shell and my first requirement is to test for cd, I have all my conditions for the possible cd commands, after which, I will handing commands like ls. As for now I am really confused about one block of code.
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#define MAX_TOK 50
#define MAX_LEN 100
#define BUFSIZE 81
int tokenize(char *cmd, char tokens[MAX_TOK][MAX_LEN]){
char *token;
int NUM_TOKENS = 0;
//printf("Splitting the string \"%s\" into tokens:\n",cmd);
token = strtok(cmd, " ");
while(token != NULL){
strcpy(tokens[NUM_TOKENS],token);
token = strtok(NULL, " ");
NUM_TOKENS++;
}
return NUM_TOKENS;
}
void decide(char tokens[MAX_TOK][MAX_LEN], int NUM_TOKENS){
char *home = getenv("HOME");
int success;
char *cd = {"cd"};
char *string = tokens[1];
//printf("Number of tokens %d\n", NUM_TOKENS);
//printf("%d\n",strcmp(tokens[0], cd));
if(strcmp(tokens[0], cd) == 0){
if(NUM_TOKENS > 2){
printf("error: Too many arguments\n");
}
else{
printf("Changing to new directory %s \n",tokens[1]);
char *string = tokens[0];
//printf("%s\n", tokens[1]);
success = chdir(tokens[1]);
printf("%d\n",success);
}
}
else{
printf("Completing the %s request\n",tokens[0]);
take_action(tokens[0]);
}
}
void take_action(char *cmd){
printf("%s\n",cmd);
int len;
int return_code;
char buffer[BUFSIZE];
int pid;
pid = fork();
if(pid != 0){
//parent process executing
wait(NULL);
}else{
//child process executing
len = strlen(buffer);
if(buffer[len-1] == '\n'){
buffer[len-1] = '\0';
}
return_code = execlp(cmd, cmd, NULL);
if(return_code != 0){
printf("Error executing %s.\n", cmd);
}
}//end else
}
int main(){
char *cmd;
char tokens[MAX_TOK][MAX_LEN];
int len;
int return_code;
char buffer[BUFSIZE];
int pid;
while(cmd != NULL){
printf("Enter a command\n");
cmd = fgets(buffer, BUFSIZE, stdin);
// find the command
int NUM_TOKENS = tokenize(cmd, tokens);
//print_tokens(NUM_TOKENS, tokens);
decide(tokens,NUM_TOKENS);
}
}//end main
When hardcoding chdir("test") the code runs fine, if a user on the command line enters "cd test" tokens[0] is cd and tokens[1] is the string "test" but chdir(tokens[1]) fails and I don't understand why.
Printing tokens[1] also shows "test" as the string stored. As well when passing a parameter to take_action I am told conflicting types occurs. In both print statements the proper string is shown. As far as I can tell there are no extra spaces because my tokentize function strips them all. I am so confused, these two parts seem so simple but just wont work.
The user does not enter cd test. He enters cd test and then hits the Enter key.
This means that you will have:
token[0] = "cd";
token[1] = "test\n";
And you don't have a directory named "test\n" , it would be "test". You need to strip off the newline character in the last token.
e.g. change main to do
char *tmp;
cmd = fgets(buffer, BUFSIZE, stdin);
if (cmd && (tmp = strrchr(buffer, '\n')) != NULL) {
*tmp = 0;
}
The code
char *string = tokens[0];
printf("%s\n", tokens[1]);
success = chdir(string);
will translate to
success = chdir("cd");
I think you wanted
char *string = tokens[1];
// ^
instead.
token[0] is cd and token[1] is the path. So use char* string=token[1]
Related
So to start off, this program has two main parts. The first takes input from the command line using fgets and then makes an argv array with a function called makeargv. The second, takes that argv array and runs it using execvp. The problem I am having is that the program will only run system programs such as "ls", "pwd", "vim", etc. but will not run any program when the directory is specified, such as "./program". I have already tried different versions of exec but the only difference that has made is that then my program will no longer run any commands.
For the below program I cut out all the code that was not relevant to the question to avoid confusion.
#ifndef MAX_CANON
#define MAX_CANON 8192
#endif
int makeargv(const char *s, const char *delimiters, char ***argvp);
int main (int argc, char *argv[]) {
char cmd[MAX_CANON];
char delim[] = "\t";
char **myargv;
printf("Beginning program...\nEnter a command to execute:\n");
while(fgets(cmd, MAX_CANON, stdin) != NULL){ // Here's where I get input from the command line
/* Remove newline from end of command */
if (*(cmd + strlen(cmd) - 1) == '\n' || *(cmd + strlen(cmd) - 1) == ' ' )
*(cmd + strlen(cmd) - 1) = 0;
/*---- Child Code ----*/
if((p = fork()) == 0){
if (makeargv(cmd, delim, &myargv) == -1) { // Here is where I make the argv array
fprintf(stderr, "Child failed to construct an argument array for %s\n", &cmd[0]);
return 1;
}
fprintf(stderr, "Command is: %s\n", *&myargv[0]);
if(execvp(myargv[0], &myargv[0]) == -1){ // Here is where the error keeps appearing
fprintf(stderr, "Error: Failed to execute command!\n");
return 1;
}
return 0;
}
/*---- Parent Code ----*/
Here is the makeargv code
#include <errno.h>
#include <stdlib.h>
#include <string.h>
int makeargv(const char *s, const char *delimiters, char ***argvp) {
int error;
int i;
int numtokens;
const char *snew;
char *t;
if ((s == NULL) || (delimiters == NULL) || (argvp == NULL)) {
errno = EINVAL;
return -1;
}
*argvp = NULL;
snew = s + strspn(s, delimiters); /* snew is real start of string */
if ((t = malloc(strlen(snew) + 1)) == NULL)
return -1;
strcpy(t, snew);
numtokens = 0;
if (strtok(t, delimiters) != NULL) /* count the number of tokens in s */
for (numtokens = 1; strtok(NULL, delimiters) != NULL; numtokens++) ;
/* create argument array for ptrs to the tokens */
if ((*argvp = malloc((numtokens + 1)*sizeof(char *))) == NULL) {
error = errno;
free(t);
errno = error;
return -1;
}
/* insert pointers to tokens into the argument array */
if (numtokens == 0)
free(t);
else {
strcpy(t, snew);
**argvp = strtok(t, delimiters);
for (i = 1; i < numtokens; i++)
*((*argvp) + i) = strtok(NULL, delimiters);
}
*((*argvp) + numtokens) = NULL; /* put in final NULL pointer */
return numtokens;
}
Edit:
Swapped fprintf for perror.
if(execvp(myargv[0], &myargv[0]) == -1){ // Here is where the error keeps appearing
fprintf(stderr, "Error: Failed to execute command!\n");
return 1;
}
if(execvp(myargv[0], &myargv[0]) == -1){ // Here is where the error keeps appearing
perror("Error: Failed to execute command!\n");
return 1;
}
I am now getting a "No such file or directory" error.
FIXED:
The makeargv program was using "\t" as it's delimeter instead of " " so it was not creating the array correctly.
Changing:
char delim[] = "\t";
To:
char delim[] = " ";
Fixes the problem.
There is 2 type of exec:
the first need a path as the environment PATH who contain a location (where find sys executables)
and the others how take real file location.
Form the man page (https://linux.die.net/man/3/exec):
The execlp(), execvp(), and execvpe() functions duplicate the actions of the shell in searching for an executable file if the specified filename does not contain a slash (/) character.
So this mean that you need to use an absolute location, who always begin with /.
You can use getwd() and strcat() to concatenate the 2 strings.
For execvp:
int execvp(const char *file, char *const argv[]); // From the man (I <3 mans)
So:
char argv[3][] = { "/bin/ls", "/", NULL};
if (execvp("/bin/ls", argv) == -1)
return (1);
For evecv:
int execv(const char *path, char *const argv[]);
There is a problem, it need the system PATH. If you don't know what it is type "echo $PATH" in bash. It's a list of directories where the system can find binaries like "ls" in "/bin", concatenate with ":" as sparator. I find the exact definition here.
For your makeargv:
I don't understand why you deference it and take the address after.
execvp(myargv[0], &myargv[0])
char **myargv; //That's we have
char *const argv[]; //that's we need
//so you should try simply:
execvp(myargv[0], myargv);
//or if there is a cast problem
execvp(myargv[0], (char *const[])myargv);
Good luck!
I am creating a simple shell. User enters a system call, I fork and call execvp on the child process for whatever command the user entered. I debugged and it seems the args that I am passing to the execvp is not correct. Could someone help me out, I am new to C
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#define MAXLINE 80
int main(void){
char *args[MAXLINE/2 + 1];
char buf[MAXLINE/2 + 1];
int should_run = 1;
printf("Shell\n");
fgets(buf, MAXLINE,stdin);
buf[strlen(buf)-1] = '\0';
int i=0;
char *token;
token = strtok(buf," ");
while(token != NULL){
args[i++] = token;
token = strtok(NULL," ");
}
while(should_run){
if(strcmp(args[0],"exit") == 0){
should_run = 0;
break;
}
pid_t pid;
pid = fork();
//child process
if(pid == 0){
execvp(args[0],args);
}
else{
wait(0);
//Also how do I make it so that the program starts from top again, meaning it lets the user enter another system call
}
}
}
The execvp function requires that you NULL-terminate the list itself. Currently, your tokenising loop does not put a NULL on the last token, and so the behaviour is undefined (you never initialised your array).
A compact (but not very pretty) solution is to rearrange your loop like this:
int i = 0;
char *token;
do {
token = strtok( i == 0 ? buf : NULL," ");
args[i++] = token;
} while( token != NULL );
I've only found a few threads like this, and none with information that I am able to make any sense of. I'm programming a shell in C and I feel like it should be easy but my C programming is not so fresh. I'm having issues with passing a double pointer and the contents disappearing
I feel I am on the right track, and it sounds like it has something to do with initialization, but I've tried a few things, setting pointers to NULL just to be sure. Thanks.
void runProgram (char **cLine);
char **parse(char *str);
/*
*
*/
int main(int argc, char** argv)
{
char *cin = NULL;
ssize_t buffer = 0;
char **tempArgs = NULL;
printf(">");
while(1)
{
getline(&cin, &buffer, stdin);
tempArgs = parse(cin); //malloc, parse, and return
printf("passing %s", tempArgs[0]); //works just fine here, can see the string
runProgram(tempArgs); //enter this function and array is lost
}
return (EXIT_SUCCESS);
}
char** parse( char* str )
{
char *token = NULL;
char tokens[256];
char** args = malloc( 256 );
int i = 0;
strcpy( tokens, str );
args[i] = strtok( tokens, " " );
while( args[i] )
{
i++;
args[i] = strtok(NULL, " ");
}
args[i] = NULL;
return args;
}
Visible in main up until this function call
void runProgram (char **cLine)
{
//function that calls fork and execvp
}
The simplest fix is not to use tokens at all in the parse() function:
int main(void)
{
char *buffer = NULL;
size_t buflen = 0;
char **tempArgs = NULL;
printf("> ");
while (getline(&buffer, &buflen, stdin) != -1)
{
tempArgs = parse(buffer);
printf("passing %s", tempArgs[0]);
runProgram(tempArgs);
printf("> ");
free(tempArgs); // Free the space allocated by parse()
}
free(buffer); // Free the space allocated by getline()
return (EXIT_SUCCESS);
}
char **parse(char *str)
{
char **args = malloc(256);
if (args == 0)
…handle error appropriately…
int i = 0;
args[i] = strtok(str, " ");
// Bounds checking omitted
while (args[i])
args[++i] = strtok(NULL, " ");
return args;
}
Note that when the loop terminates, the array is already null terminated, so the extra assignment wasn't necessary (but it is better to be safe than sorry).
I'm writing a program to parse a command-line argument into three different parts: host name, file path, and file name, however I am unsure of how to parse a single command-line argument and store the separate parts in three different variables.
I need each portion to create a socket on the client-side of my program. So far I've been able to parse the host name portion, but I get stuck after that.
Is there a way that, after parsing a portion of the string?
EDIT:
The string I'm trying to parse is something like camelot.cba.csuohio.edu/~yourloginid/filename.txt
Here's my code
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main(int argc, char *argv[])
{
int i, sk;
char buf[256], temp[256];
struct sockaddr_in remote;
struct hostent *hp;
if(argc != 2)
{
printf("Invalid number of arguments. Program terminating...");
exit(1);
}
sk = socket(AF_INET, SOCK_STREAM, 0);
remote.sin_family = AF_INET;
strcpy(buf, argv[1]);
for(i = 0; i < strlen(buf); i++)
{
if(buf[i] == '/')
break;
temp[i] = buf[i];
}
hp = gethostbyname(temp);
return 0;
}
EDIT:
I've implemented a while loop to achieve what I'm looking for, but I feel like it's sloppy. Is there a way I can improve it?
while(tk != NULL)
{
if(c == 0)
strcpy(host, tk);
else if(c == 1)
strcpy(path, tk);
else
strcpy(fname, tk);
c++;
tk = strtok(NULL, "/");
}
char st[] = "camelot.cba.csuohio.edu/~yourloginid/filename.txt";
char *host, *path, *fname;
char *ch[3];
for (int i=0; i < 3; ++i) {
ch[i] = strtok(st, "/");
(if ch[i] == NULL) break;
printf("%s\n", ch[i]);
}
if (ch[0] != NULL) {
host = ch[0];
}
if (ch[1] != NULL) {
path = ch[1];
}
if (ch[2] != null) {
path = ch[2];
}
Output:
camelot.cba.csuohio.edu
~yourloginid
filename.txt
You can parse that with strtok
A rough example for you case would be
const char s[2] = "/";
char *token;
/* get the first token */
token = strtok(argv[1], s);
/* walk through other tokens */
while( token != NULL )
{
printf( " %s\n", token );
token = strtok(NULL, s);
}
I didn't compile it but I hope you can use it as an example.
Here you have a complete example of how to use it
http://www.tutorialspoint.com/c_standard_library/c_function_strtok.htm
Hope this helps.
When you know the delimiters, never forget you have simple pointer arithmetic available to you to split/parse any sting. strtok and sscanf are fine tools, but you can do the same thing manually. Here is a short example to add to your list:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXS 128
int main (int argc, char **argv) {
if (argc < 2 ) {
fprintf (stderr, "Error: insufficient input, usage: %s host,path,file\n", argv[0]);
return 1;
}
char *line = strdup (argv[1]); /* make a copy of argument string */
if (!line) {
fprintf (stderr, "error: strdup memory allocation/copy failed.\n");
return 1;
}
char *p = line; /* pointer to the argument string */
char *sp = NULL; /* pointer to use as start pointer */
char host[MAXS] = {0}; /* variables to hold tokens */
char path[MAXS] = {0};
char file[MAXS] = {0};
while (*p && *p != ',') p++; /* find the first ',' */
*p++ = 0; /* null-terminate, advance pointer */
strcpy (host, line); /* read/copy host name */
sp = p; /* set start pointer at current pos */
while (*p && *p != ',') p++; /* find next ',' */
*p++ = 0; /* null-terminate, advance pointer */
strcpy (path, sp); /* read/copy path */
strcpy (file, p); /* pointer on file, read/copy file */
printf ("\n host: %s\n path: %s\n file: %s\n\n", host, path, file);
free (line); /* free memory allocate by strdup */
return 0;
}
Output
$ ./bin/split_host_path_file hostname,pathname,filename
host: hostname
path: pathname
file: filename
Updated to prevent potential read beyond end of line with p.
you can also parse with strtok_r as follows, since strtok is not thread safe.
const char *delim="/";
char *str, *savePtr;
char hosts[3][32];
int i;
for(i=0,str=strtok_r(argv[1], delim, &savePtr);(str!=NULL);str=strtok_r(NULL, delim, &savePtr), i++)
{
print("%s\n", str);
strcpy((char *)host[i], (const char *)str);
}
access host array elements, as it will contain the indexed values delimited by "/"
I know how to loop through a file line by line by reading into a FILE* using fopen, fgets etc
but how can i look through a char array line by line using plain C?
i have googled a lot an can only find stuff that reads from a file.
#include <stdio.h>
char *sgets(char *s, int n, const char **strp){
if(**strp == '\0')return NULL;
int i;
for(i=0;i<n-1;++i, ++(*strp)){
s[i] = **strp;
if(**strp == '\0')
break;
if(**strp == '\n'){
s[i+1]='\0';
++(*strp);
break;
}
}
if(i==n-1)
s[i] = '\0';
return s;
}
int main(){
const char *data = "abc\nefg\nhhh\nij";
char buff[16];
const char **p = &data;
while(NULL!=sgets(buff, sizeof(buff), p))
printf("%s", buff);
return 0;
}
Reading a character array line by line : What does a line mean ? '\n' Perhaps.
so, iterate through the array.
int main()
{ char array[10]="ab\nbc\ncd\n";
int lines =0;int i=0;
while(array[i]!='\0')
{ if(array[i]!='\n')
printf("%c",array[i++]);
else { lines++;i++; printf("\n"); }
} return 0;
}
In case if you want to keep your separator flexible (e.g. you got "\r\n") and stick to libraries, strtok is handy:
#include <cstring>
int main() {
const char separator[3] = "\r\n";
char text[13] = "ab\r\ncd\r\nef\r\n";
char *line = NULL;
line = strtok(text, separator);
while (line != NULL) {
printf("%s\n", line); // call your function to process line
line = strtok(NULL, separator); // NULL means continue from where the previous successful call to strtok ended
}
system("pause"); // just prevent console from closing
}