/* * src/command.c * * Abstractions for parsing commands from tokens and executing them * CISC 301 -- Operating Systems, Project 2 * * Copyright (C) 2025 Douglas B. Rumbaugh * * Distributed under the Modified BSD License * */ #include "command.h" #include "config.h" #include "lexer.h" #include #include command *create_command() { command *cmd = malloc(sizeof(command)); if (!cmd) { return NULL; } cmd->command = NULL; memset(cmd->args, 0, sizeof(char *)); cmd->pipe[0] = -1; cmd->pipe[1] = -1; cmd->infile = NULL; cmd->outfile = NULL; cmd->pid = -1; cmd->next = NULL; cmd->prev = NULL; return cmd; } command *commands_from_tokens(token *parsed_cmdstr, size_t *cnt) { command *cmd_head = create_command(); command *cmd = cmd_head; size_t arg_cnt = 1; *cnt = 0; token *prev_tkn = NULL; for (token *tkn = parsed_cmdstr; tkn; prev_tkn = tkn, tkn = tkn->next) { if (tkn->type == TKN_COMMAND) { cmd->command = tkn->text; cmd->args[0] = tkn->text; arg_cnt = 1; (*cnt)++; } else if (tkn->type == TKN_ARG) { cmd->args[arg_cnt++] = tkn->text; } else if (tkn->type == TKN_FILENAME) { if (prev_tkn->type == TKN_IN_REDIR) { cmd->infile = tkn->text; } else { cmd->outfile = tkn->text; } } else if (tkn->type == TKN_PIPE) { cmd->next = create_command(); cmd->next->prev = cmd; cmd = cmd->next; } } return cmd_head; } void print_commands(FILE *file, command *cmds) { for (command *cmd = cmds; cmd; cmd = cmd->next) { fprintf(file, "Command: %s\n", cmd->command); fprintf(file, "\t"); for (size_t i = 0; i < MAX_ARGUMENT_CNT; i++) { fprintf(file, "%s ", cmd->args[i]); } fprintf(file, "\n\tInfile: %s\n\tOutfile: %s\\n\tPid:%d\n", cmd->infile, cmd->outfile, cmd->pid); fprintf(file, "\tRead Pipe: %d\tWrite Pipe: %d\n", cmd->pipe[0], cmd->pipe[1]); } } void destroy_commands(command *cmds) { if (!cmds) { return; } command **cmd_ptr = &cmds->next; while (*cmd_ptr) { free(cmds); cmds = *cmd_ptr; cmd_ptr = &cmds->next; } } pid_t execute_command(command *cmd) { if (cmd->next && pipe(cmd->pipe) == -1) { perror("Failed to create pipes"); return -1; } pid_t res = fork(); if (res == 0) { if (cmd->infile && !freopen(cmd->infile, "r", stdin)) { perror("Could not open input file"); exit(EXIT_FAILURE); } else if (cmd->prev) { close(cmd->prev->pipe[1]); if (dup2(cmd->prev->pipe[0], STDIN_FILENO) == -1) { perror("Could not assign pipe to stdin"); exit(EXIT_FAILURE); } close(cmd->prev->pipe[0]); } if (cmd->outfile && !freopen(cmd->outfile, "w", stdout)) { perror("Could not open output file"); exit(EXIT_FAILURE); } else if (cmd->next) { close(cmd->pipe[0]); if (dup2(cmd->pipe[1], STDOUT_FILENO) == -1) { perror("Could not assign pipe to stdout"); exit(EXIT_FAILURE); } close(cmd->pipe[1]); } /* * NOTE: discarding the const qualifier here is okay because either * 1) exec fails, in which case the process is aborted immediately * 2) exec succeeds, in which case the memory is released immediately * * In either case, the args won't be accessed again. */ execvp(cmd->command, (char **)cmd->args); perror("Could not run command"); exit(EXIT_FAILURE); } else if (res < 0) { perror("Could not run command"); return -1; } if (cmd->prev) { close(cmd->prev->pipe[0]); close(cmd->prev->pipe[1]); } cmd->pid = res; return res; }