-1

I have something like a basic shell. My problem is that when i do something like ./test ls -l it doesnt use the -l argument. It's exactly the same as if i did ./test ls. According to man, int execvp(const char *file, char *const argv[]), so isn't execvp suposed to read the entire **argv?

int mySystem(char* str[]){
    wordexp_t p;
    char **w;
    wordexp(str[1],&p,0);
    w = p.we_wordv;
    int q= fork();
    if(q==0){
        execvp(w[0],w);
        perror("erro");
        _exit(-1);
    }

    int sp;
    wordfree(&p); 
    wait(&sp);
    if(WIFEXITED(sp)){
        return WEXITSTATUS(sp);
    }
    return 0;
}

int main(int argc,char **argv){
    mySystem(argv);
    return 0;
}

Thanks.

ohiohai
  • 73
  • 8
  • Did you verify that the argv argument has the expected contents? Or are you just assuming that you set it up correctly? – rici May 16 '16 at 22:32
  • If I loop the p.we_wordv arguments and print it, it's only passing the ls, like (i think) you were pointing out... I'm assuming the problem is the 1st argument on wordexp (str[1])? – ohiohai May 16 '16 at 22:51
  • Why exactly are you calling wordexp? – rici May 16 '16 at 23:14
  • This is for a small project. I want to be able to do something on my terminal like, ./program backup *.txt and it will zip all the .txt files on that directory. I have the ziping part done, im trying to use the wordexp because (as far as i know) it will interpret commands like the * automatically. Not sure if i explained it right. – ohiohai May 16 '16 at 23:21
  • @rici after some research I figured it should be done like this bc of this page: http://www.gnu.org/software/libc/manual/html_node/Word-Expansion.html#Word-Expansion . – ohiohai May 16 '16 at 23:40
  • The shell will interpret globs, so you don't need to worry about it. Try it and see. – rici May 16 '16 at 23:41
  • Since `str[1]` contains just `ls`, the result of `wordexp()` doesn't contain the `-l`, so `ls` is executed without the `-l` option. No mystery there. – Jonathan Leffler May 17 '16 at 06:18

2 Answers2

0

Your argument should look like this (note the trailing 0 and that there are no quotes)

static char *cmd[] = { "ls", "-l", 0 };

So you can try and hard-code the call to exec and see that it works.

I can't debug your code because it is not complete. If you use a debugger you can inspect the values. Remember that the args must be in an array to exec. Here comes some basic code that does it and explains it, and also admits you to make a pipeline.

/* One way to create a pipeline of N processes */

/* stderr.h */
#ifndef STDERR_H_INCLUDED
#define STDERR_H_INCLUDED

static void err_setarg0(const char *argv0);
static void err_sysexit(char const *fmt, ...);
static void err_syswarn(char const *fmt, ...);

#endif /* STDERR_H_INCLUDED */

/* pipeline.c */
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
/*#include "stderr.h"*/

typedef int Pipe[2];

/* exec_nth_command() and exec_pipe_command() are mutually recursive */
static void exec_pipe_command(int ncmds, char ***cmds, Pipe output);

/* With the standard output plumbing sorted, execute Nth command */
static void exec_nth_command(int ncmds, char ***cmds)
{
    assert(ncmds >= 1);
    if (ncmds > 1)
    {
        pid_t pid;
        Pipe input;
        if (pipe(input) != 0)
            err_sysexit("Failed to create pipe");
        if ((pid = fork()) < 0)
            err_sysexit("Failed to fork");
        if (pid == 0)
        {
            /* Child */
            exec_pipe_command(ncmds-1, cmds, input);
        }
        /* Fix standard input to read end of pipe */
        dup2(input[0], 0);
        close(input[0]);
        close(input[1]);
    }
    execvp(cmds[ncmds-1][0], cmds[ncmds-1]);
    err_sysexit("Failed to exec %s", cmds[ncmds-1][0]);
    /*NOTREACHED*/
}

/* Given pipe, plumb it to standard output, then execute Nth command */
static void exec_pipe_command(int ncmds, char ***cmds, Pipe output)
{
    assert(ncmds >= 1);
    /* Fix stdout to write end of pipe */
    dup2(output[1], 1);
    close(output[0]);
    close(output[1]);
    exec_nth_command(ncmds, cmds);
}

/* Execute the N commands in the pipeline */
static void exec_pipeline(int ncmds, char ***cmds)
{
    assert(ncmds >= 1);
    pid_t pid;
    if ((pid = fork()) < 0)
        err_syswarn("Failed to fork");
    if (pid != 0)
        return;
    exec_nth_command(ncmds, cmds);
}

/* Collect dead children until there are none left */
static void corpse_collector(void)
{
    pid_t parent = getpid();
    pid_t corpse;
    int   status;
    while ((corpse = waitpid(0, &status, 0)) != -1)
    {
        fprintf(stderr, "%d: child %d status 0x%.4X\n",
                (int)parent, (int)corpse, status);
    }
}

/*  who | awk '{print $1}' | sort | uniq -c | sort -n */
static char *cmd0[] = { "who",                0 };
static char *cmd1[] = { "awk",  "{print $1}", 0 };
static char *cmd2[] = { "sort",               0 };
static char *cmd3[] = { "uniq", "-c",         0 };
static char *cmd4[] = { "sort", "-n",         0 };

static char **cmds[] = { cmd0, cmd1, cmd2, cmd3, cmd4 };
static int   ncmds = sizeof(cmds) / sizeof(cmds[0]);

static void exec_arguments(int argc, char **argv)
{
    /* Split the command line into sequences of arguments */
    /* Break at pipe symbols as arguments on their own */
    char **cmdv[argc/2];            // Way too many
    char  *args[argc+1];
    int cmdn = 0;
    int argn = 0;

    cmdv[cmdn++] = &args[argn];
    for (int i = 1; i < argc; i++)
    {
        char *arg = argv[i];
        if (strcmp(arg, "|") == 0)
        {
            if (i == 1)
                err_sysexit("Syntax error: pipe before any command");
            if (args[argn-1] == 0)
                err_sysexit("Syntax error: two pipes with no command between");
            arg = 0;
        }
        args[argn++] = arg;
        if (arg == 0)
            cmdv[cmdn++] = &args[argn];
    }
    if (args[argn-1] == 0)
        err_sysexit("Syntax error: pipe with no command following");
    args[argn] = 0;
    exec_pipeline(cmdn, cmdv);
}
#include <stdio.h>
#include <stdarg.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char **argv)
{

    printf("%s", argv[1]);

    err_setarg0(argv[0]);
    if (argc == 1)
    {
        /* Run the built in pipe-line */
        exec_pipeline(ncmds, cmds);
    }
    else
    {

        /* Run command line specified by user */
        char *original = "ls";
        char **pointer_to_original = &original;
        for (int i = 0; i < argc; ++i) {   printf("blarg blarg %i\n", argc);
            printf("%s", argv[i]);// = "ls";
        }
        exec_arguments(argc, argv);
    }
    corpse_collector();
    return(0);
}

/* stderr.c */
/*#include "stderr.h"*/
#include <stdio.h>
#include <stdarg.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>

static const char *arg0 = "<undefined>";

static void err_setarg0(const char *argv0)
{
    arg0 = argv0;
}

static void err_vsyswarn(char const *fmt, va_list args)
{
    int errnum = errno;
    fprintf(stderr, "%s:%d: ", arg0, (int)getpid());
    vfprintf(stderr, fmt, args);
    if (errnum != 0)
        fprintf(stderr, " (%d: %s)", errnum, strerror(errnum));
    putc('\n', stderr);
}

static void err_syswarn(char const *fmt, ...)
{
    va_list args;
    va_start(args, fmt);
    err_vsyswarn(fmt, args);
    va_end(args);
}

static void err_sysexit(char const *fmt, ...)
{
    va_list args;
    va_start(args, fmt);
    err_vsyswarn(fmt, args);
    va_end(args);
    exit(1);
}
Niklas Rosencrantz
  • 25,640
  • 75
  • 229
  • 424
0

You don't need wordexp to interpret command-line arguments. The shell will already have done that for you. Just pass argv + 1 to execvp.

The only reason you would need wordexp would be to me interpret entire command lines passed as single arguments (eg. `myutil "ls -l *.txt") or read from a file or some such. But why would you want all that complication?

rici
  • 234,347
  • 28
  • 237
  • 341