leetify.c
/**
* @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;
}