将文件拆分为多个文件并在 C 中使用 #include

问题描述 投票:0回答:0

我有一个正在开发的操作系统项目(这个问题与项目解决方案没有任何关系),它正在开发一个交互式 Unix 风格的操作系统 shell。我得到了一个要处理的文件,我已将该文件拆分为多个文件并尝试执行它。我收到与“functionX”的多个定义相关的错误......首先在这里定义。

我还没有理解如何将其他文件“#include”到 C 中的单个文件中。

  • 为 c 文件创建头文件并包含头文件而不是实际的 c 文件会更好吗?
  • 为什么我只定义了一次就说(multiple definition of "functionX" first defined here)?

错误:

gcc  ./build/command.o  ./build/promptInfo.o  ./build/global.o  ./build/shell-skeleton.o -o mishell 
/nix/store/8qm6sjqa09a03glzmafprpp69k74l4lm-binutils-2.40/bin/ld: ./build/promptInfo.o: in function `print_command':
promptInfo.c:(.text+0x0): multiple definition of `print_command'; ./build/command.o:command.c:(.text+0x0): first defined here
/nix/store/8qm6sjqa09a03glzmafprpp69k74l4lm-binutils-2.40/bin/ld: ./build/promptInfo.o: in function `free_command.localalias':
promptInfo.c:(.text+0x140): multiple definition of `free_command'; ./build/command.o:command.c:(.text+0x140): first defined here
/nix/store/8qm6sjqa09a03glzmafprpp69k74l4lm-binutils-2.40/bin/ld: ./build/promptInfo.o: in function `parse_command.localalias':
promptInfo.c:(.text+0x1c0): multiple definition of `parse_command'; ./build/command.o:command.c:(.text+0x1c0): first defined here
/nix/store/8qm6sjqa09a03glzmafprpp69k74l4lm-binutils-2.40/bin/ld: ./build/promptInfo.o: in function `process_command':
promptInfo.c:(.text+0x650): multiple definition of `process_command'; ./build/command.o:command.c:(.text+0x650): first defined here
/nix/store/8qm6sjqa09a03glzmafprpp69k74l4lm-binutils-2.40/bin/ld: ./build/promptInfo.o:(.data.rel.local+0x0): multiple definition of `sysname'; ./build/command.o:(.data.rel.local+0x0): first defined here
/nix/store/8qm6sjqa09a03glzmafprpp69k74l4lm-binutils-2.40/bin/ld: ./build/global.o:(.data.rel.local+0x0): multiple definition of `sysname'; ./build/command.o:(.data.rel.local+0x0): first defined here
/nix/store/8qm6sjqa09a03glzmafprpp69k74l4lm-binutils-2.40/bin/ld: ./build/shell-skeleton.o: in function `print_command':
shell-skeleton.c:(.text+0x0): multiple definition of `print_command'; ./build/command.o:command.c:(.text+0x0): first defined here
/nix/store/8qm6sjqa09a03glzmafprpp69k74l4lm-binutils-2.40/bin/ld: ./build/shell-skeleton.o: in function `free_command.localalias':
shell-skeleton.c:(.text+0x140): multiple definition of `free_command'; ./build/command.o:command.c:(.text+0x140): first defined here
/nix/store/8qm6sjqa09a03glzmafprpp69k74l4lm-binutils-2.40/bin/ld: ./build/shell-skeleton.o: in function `parse_command.localalias':
shell-skeleton.c:(.text+0x1c0): multiple definition of `parse_command'; ./build/command.o:command.c:(.text+0x1c0): first defined here
/nix/store/8qm6sjqa09a03glzmafprpp69k74l4lm-binutils-2.40/bin/ld: ./build/shell-skeleton.o: in function `process_command':
shell-skeleton.c:(.text+0x650): multiple definition of `process_command'; ./build/command.o:command.c:(.text+0x650): first defined here
/nix/store/8qm6sjqa09a03glzmafprpp69k74l4lm-binutils-2.40/bin/ld: ./build/shell-skeleton.o:(.data.rel.local+0x0): multiple definition of `sysname'; ./build/command.o:(.data.rel.local+0x0): first defined here
/nix/store/8qm6sjqa09a03glzmafprpp69k74l4lm-binutils-2.40/bin/ld: ./build/shell-skeleton.o: in function `show_prompt':
shell-skeleton.c:(.text+0x720): multiple definition of `show_prompt'; ./build/promptInfo.o:promptInfo.c:(.text+0x720): first defined here
/nix/store/8qm6sjqa09a03glzmafprpp69k74l4lm-binutils-2.40/bin/ld: ./build/shell-skeleton.o: in function `prompt_backspace':
shell-skeleton.c:(.text+0x7c0): multiple definition of `prompt_backspace'; ./build/promptInfo.o:promptInfo.c:(.text+0x7c0): first defined here
/nix/store/8qm6sjqa09a03glzmafprpp69k74l4lm-binutils-2.40/bin/ld: ./build/shell-skeleton.o: in function `prompt':
shell-skeleton.c:(.text+0x7f0): multiple definition of `prompt'; ./build/promptInfo.o:promptInfo.c:(.text+0x7f0): first defined here
collect2: error: ld returned 1 exit status
make: *** [Makefile:28: mishell] Error 1

global.c:

#include <stdbool.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <termios.h> // termios, TCSANOW, ECHO, ICANON
#include <unistd.h>

const char *sysname = "mishell";

enum return_codes {
    SUCCESS = 0,
    EXIT = 1,
    UNKNOWN = 2,
};

struct command_t {
    char *name;
    bool background;
    bool auto_complete;
    int arg_count;
    char **args;
    char *redirects[3]; // in/out redirection
    struct command_t *next; // for piping
};

命令.c:

#include "global.c"

/**
 * Prints a command struct
 * @param struct command_t *
 */
void print_command(struct command_t *command) {
    int i = 0;
    printf("Command: <%s>\n", command->name);
    printf("\tIs Background: %s\n", command->background ? "yes" : "no");
    printf("\tNeeds Auto-complete: %s\n",
          command->auto_complete ? "yes" : "no");
    printf("\tRedirects:\n");

    for (i = 0; i < 3; i++) {
        printf("\t\t%d: %s\n", i,
              command->redirects[i] ? command->redirects[i] : "N/A");
    }

    printf("\tArguments (%d):\n", command->arg_count);

    for (i = 0; i < command->arg_count; ++i) {
        printf("\t\tArg %d: %s\n", i, command->args[i]);
    }

    if (command->next) {
        printf("\tPiped to:\n");
        print_command(command->next);
    }
}

/**
 * Release allocated memory of a command
 * @param  command [description]
 * @return         [description]
 */
int free_command(struct command_t *command) {
    if (command->arg_count) {
        for (int i = 0; i < command->arg_count; ++i)
            free(command->args[i]);
        free(command->args);
    }

    for (int i = 0; i < 3; ++i) {
        if (command->redirects[i])
            free(command->redirects[i]);
    }

    if (command->next) {
        free_command(command->next);
        command->next = NULL;
    }

    free(command->name);
    free(command);
    return 0;
}

/**
 * Parse a command string into a command struct
 * @param  buf     [description]
 * @param  command [description]
 * @return         0
 */
int parse_command(char *buf, struct command_t *command) {
    const char *splitters = " \t"; // split at whitespace
    int index, len;
    len = strlen(buf);

    // trim left whitespace
    while (len > 0 && strchr(splitters, buf[0]) != NULL) {
        buf++;
        len--;
    }

    while (len > 0 && strchr(splitters, buf[len - 1]) != NULL) {
        // trim right whitespace
        buf[--len] = 0;
    }

    // auto-complete
    if (len > 0 && buf[len - 1] == '?') {
        command->auto_complete = true;
    }

    // background
    if (len > 0 && buf[len - 1] == '&') {
        command->background = true;
    }

    char *pch = strtok(buf, splitters);
    if (pch == NULL) {
        command->name = (char *)malloc(1);
        command->name[0] = 0;
    } else {
        command->name = (char *)malloc(strlen(pch) + 1);
        strcpy(command->name, pch);
    }

    command->args = (char **)malloc(sizeof(char *));

    int redirect_index;
    int arg_index = 0;
    char temp_buf[1024], *arg;

    while (1) {
        // tokenize input on splitters
        pch = strtok(NULL, splitters);
        if (!pch)
            break;
        arg = temp_buf;
        strcpy(arg, pch);
        len = strlen(arg);

        // empty arg, go for next
        if (len == 0) {
            continue;
        }

        // trim left whitespace
        while (len > 0 && strchr(splitters, arg[0]) != NULL) {
            arg++;
            len--;
        }

        // trim right whitespace
        while (len > 0 && strchr(splitters, arg[len - 1]) != NULL) {
            arg[--len] = 0;
        }

        // empty arg, go for next
        if (len == 0) {
            continue;
        }

        // piping to another command
        if (strcmp(arg, "|") == 0) {
            struct command_t *c = malloc(sizeof(struct command_t));
            int l = strlen(pch);
            pch[l] = splitters[0]; // restore strtok termination
            index = 1;
            while (pch[index] == ' ' || pch[index] == '\t')
                index++; // skip whitespaces

            parse_command(pch + index, c);
            pch[l] = 0; // put back strtok termination
            command->next = c;
            continue;
        }

        // background process
        if (strcmp(arg, "&") == 0) {
            // handled before
            continue;
        }

        // handle input redirection
        redirect_index = -1;
        if (arg[0] == '<') {
            redirect_index = 0;
        }

        if (arg[0] == '>') {
            if (len > 1 && arg[1] == '>') {
                redirect_index = 2;
                arg++;
                len--;
            } else {
                redirect_index = 1;
            }
        }

        if (redirect_index != -1) {
            command->redirects[redirect_index] = malloc(len);
            strcpy(command->redirects[redirect_index], arg + 1);
            continue;
        }

        // normal arguments
        if (len > 2 &&
            ((arg[0] == '"' && arg[len - 1] == '"') ||
            (arg[0] == '\'' && arg[len - 1] == '\''))) // quote wrapped arg
        {
            arg[--len] = 0;
            arg++;
        }

        command->args =
            (char **)realloc(command->args, sizeof(char *) * (arg_index + 1));

        command->args[arg_index] = (char *)malloc(len + 1);
        strcpy(command->args[arg_index++], arg);
    }
    command->arg_count = arg_index;

    // increase args size by 2
    command->args = (char **)realloc(
        command->args, sizeof(char *) * (command->arg_count += 2));

    // shift everything forward by 1
    for (int i = command->arg_count - 2; i > 0; --i) {
        command->args[i] = command->args[i - 1];
    }

    // set args[0] as a copy of name
    command->args[0] = strdup(command->name);

    // set args[arg_count-1] (last) to NULL
    command->args[command->arg_count - 1] = NULL;

    return 0;
}

int process_command(struct command_t *command) {
    int r;

    if (strcmp(command->name, "") == 0) {
        return SUCCESS;
    }

    if (strcmp(command->name, "exit") == 0) {
        return EXIT;
    }

    if (strcmp(command->name, "cd") == 0) {
        if (command->arg_count > 0) {
            r = chdir(command->args[1]);
            if (r == -1) {
                printf("-%s: %s: %s\n", sysname, command->name,
                      strerror(errno));
            }

            return SUCCESS;
        }
    }

    pid_t pid = fork();
    // child
    if (pid == 0) {
        /// This shows how to do exec with environ (but is not available on MacOs)
        // extern char** environ; // environment variables
        // execvpe(command->name, command->args, environ); // exec+args+path+environ

        /// This shows how to do exec with auto-path resolve
        // add a NULL argument to the end of args, and the name to the beginning
        // as required by exec

        // TODO: do your own exec with path resolving using execv()
        // do so by replacing the execvp call below
        execvp(command->name, command->args); // exec+args+path
        exit(0);
    } else {
        // TODO: implement background processes here
        wait(0); // wait for child process to finish
        return SUCCESS;
    }

    // TODO: your implementation here

    printf("-%s: %s: command not found\n", sysname, command->name);
    return UNKNOWN;
}

promptInfo.c:

#include "command.c"

/**
 * Show the command prompt
 * @return [description]
 */
int show_prompt() {
    char cwd[1024], hostname[1024];
    gethostname(hostname, sizeof(hostname));
    getcwd(cwd, sizeof(cwd));
    printf("%s@%s:%s %s$ ", getenv("USER"), hostname, cwd, sysname);
    return 0;
}

void prompt_backspace() {
    putchar(8); // go back 1
    putchar(' '); // write empty over
    putchar(8); // go back 1 again
}

/**
 * Prompt a command from the user
 * @param  buf      [description]
 * @param  buf_size [description]
 * @return          [description]
 */
int prompt(struct command_t *command) {
    size_t index = 0;
    char c;
    char buf[4096];
    static char oldbuf[4096];

    // tcgetattr gets the parameters of the current terminal
    // STDIN_FILENO will tell tcgetattr that it should write the settings
    // of stdin to oldt
    static struct termios backup_termios, new_termios;
    tcgetattr(STDIN_FILENO, &backup_termios);
    new_termios = backup_termios;
    // ICANON normally takes care that one line at a time will be processed
    // that means it will return if it sees a "\n" or an EOF or an EOL
    new_termios.c_lflag &=
        ~(ICANON |
         ECHO); // Also disable automatic echo. We manually echo each char.
    // Those new settings will be set to STDIN
    // TCSANOW tells tcsetattr to change attributes immediately.
    tcsetattr(STDIN_FILENO, TCSANOW, &new_termios);

    show_prompt();
    buf[0] = 0;

    while (1) {
        c = getchar();
        // printf("Keycode: %u\n", c); // DEBUG: uncomment for debugging

        // handle tab
        if (c == 9) {
            buf[index++] = '?'; // autocomplete
            break;
        }

        // handle backspace
        if (c == 127) {
            if (index > 0) {
                prompt_backspace();
                index--;
            }
            continue;
        }

        if (c == 27 || c == 91 || c == 66 || c == 67 || c == 68) {
            continue;
        }

        // up arrow
        if (c == 65) {
            while (index > 0) {
                prompt_backspace();
                index--;
            }

            char tmpbuf[4096];
            printf("%s", oldbuf);
            strcpy(tmpbuf, buf);
            strcpy(buf, oldbuf);
            strcpy(oldbuf, tmpbuf);
            index += strlen(buf);
            continue;
        }

        putchar(c); // echo the character
        buf[index++] = c;
        if (index >= sizeof(buf) - 1)
            break;
        if (c == '\n') // enter key
            break;
        if (c == 4) // Ctrl+D
            return EXIT;
    }

    // trim newline from the end
    if (index > 0 && buf[index - 1] == '\n') {
        index--;
    }

    // null terminate string
    buf[index++] = '\0';

    strcpy(oldbuf, buf);

    parse_command(buf, command);

    // print_command(command); // DEBUG: uncomment for debugging

    // restore the old settings
    tcsetattr(STDIN_FILENO, TCSANOW, &backup_termios);
    return SUCCESS;
}

shell-skeleton.c:

#include "promptInfo.c"

/* int process_command(struct command_t *command); */

int main() {
    while (1) {
        struct command_t *command = malloc(sizeof(struct command_t));

        // set all bytes to 0
        memset(command, 0, sizeof(struct command_t));

        int code;
        code = prompt(command);
        if (code == EXIT) {
            break;
        }

        code = process_command(command);
        if (code == EXIT) {
            break;
        }

        free_command(command);
    }

    printf("\n");
    return 0;
}

.

c include header-files
© www.soinside.com 2019 - 2024. All rights reserved.