leetify.c

DownloadView Raw

/**
 * @file leetify.c
 *
 * Scaffolding to create an amazing l337speak generator using only external
 * commands combined with pipelines.
 */

#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"

/**
 * Represents a command in a pipeline.
 * Note that EITHER stdout_pipe can be true, or a stdout_file can be set.
 *
 * - tokens Array of strings that describe the command to run
 * - stdout_pipe set to true if this command's output should be written to pipe
 * - stdout_file set to a file name if this command's output should be written
 *     to a file
 */
struct command {
  char **tokens;
  bool stdout_pipe;
  char *stdout_file;
};

void
execute_pipeline(struct command *cmd)
{
  /**
   * TODO: design an algorithm that sets up a pipeline piece by piece.
   * Solutions will probably either iterate over the pieces of the pipeline or
   * work recursively, creating a new process for each piece with fork() and
   * executing the corresponding command with exec().
   *
   * While we aren't at the last command, set up a pipe for the current
   * command's output to go into before forking. For example, let's say our
   * command is `cat file.txt`. We will create a pipe and send the stdout of the
   * command to the pipe. Before running the next command, we'll make stdin of
   * the parent come from the pipe (thus the CLONED next process will also be
   * receiving its stdin on the same pipe!), and execute_pipeline will run
   * whatever command comes next (for instance, `lowercase`).
   *
   * Here's some pseudocode to help:
   *
   * create a new pipe
   * fork a new process
   * in the child:
   *   send stdout to pipe
   *   close other end of pipe
   *   exec command
   * in the parent:
   *   receive stdin from pipe
   *   close other end of pipe
   *   move on to the next command in the pipeline
   *
   * The special case is when there are no more commands left. Simply exec the
   * final command (no need to create another pipe). The extra process we
   * created in main() to call execute_pipeline will be replaced by this last
   * call to exec.
   */
}

int
main(int argc, char *argv[])
{
  char *input_file = NULL;
  char *output_file = NULL;

  if (argc < 2 || argc > 3) {
    printf("Usage: %s file-to-leetify [output-file]\n", argv[0]);
    return 1;
  }

  input_file = argv[1];

  if (argc == 3) {
    output_file = argv[2];
  }

  printf("Input file: %s\n", input_file);
  if (output_file) {
    printf("Writing to output file: %s\n", output_file);
  }

  /**
   * TODO: Next steps:
   * - Read and understand the setup code above
   * - Finish preparing the commands that will be run
   * - Set up the array of command structs
   * - Implement execute_pipeline
   */

  /* TODO: These commands aren't quite finished; check the lab spec. */
  char *command1[] = { "cat", "something.txt", (char *) NULL };
  char *command2[] = { "tolower", (char *) NULL };
  char *command3[] = { "fnr", "a", "4", (char *) NULL };

  /* TODO: Finish these. Only some struct fields are populated. */
  struct command cmds[4] = { 0 };
  cmds[0].tokens = command1;  /* What this command runs */
  cmds[0].stdout_pipe = true;
  cmds[0].stdout_file = NULL; /* This command is not writing to a file. */

  cmds[1].tokens = command2;
  cmds[1].stdout_pipe = true; /* This command's output goes to a pipe. */

  cmds[2].tokens = command3;
  cmds[2].stdout_pipe = true;

  cmds[3].stdout_pipe = false; /* Last command so set stdout_pipe = false */
  cmds[3].stdout_file = output_file;

  int pid = fork();
  if (pid == -1) {
    fprintf(2, "Fork failed\n");
    return 1;
  } else if (pid == 0) {
    execute_pipeline(cmds);
  } else {
    int status;
    wait(&status);
    printf("Child exited with status %d\n", status);
  }

  return 0;
}