---
# System prepended metadata

title: Assignment 2
tags: [School]

---

---
tags: School
---

# Assignment 2
#### Bryan Peppers & Jason Scott

## Head
#### By Jason
* To compile: gcc -Wall -O3 -o head head.c
* -n \<number\> and a filename can be provided as arguments
#### Process
* I started by writing my_copy_lines, a function to copy 'n' lines from one file descriptor to another, to the basic template with helper functions that Bryan made.
* After the initial version of my_copy_lines I implemented my_parse_arg, to error-check the input from the commandline and send the right values to my_copy_lines.
* Created helper functions throughout the process such as my_buf_copy, my_char_to_int, my_check_num_lines, etc.

#### Troubles
* As I hadn't done any C/C++ in a while, I initially declared my char buffers as "char* \[num\];" which caused issues until I realized I had created a buffer of char arrays.
* I was initially under the impression that lseek was not one of the system calls that could be used, so I made additional work for myself when I didn't need to. (possibly stat too)
* Had a bit of trouble understanding file descriptors at first.
* Initially I had been using buffers created on the stack (either with a fixed length for stdin, or a length based on using lseek on an open fd), but this caused a segfault with nanpa (because of the size of the buffer being allocated), so I created buffers (for non-stdin fd) with malloc instead. The buffer created in the case of stdin is still the same though, because I wasn't able to figure out a way to deal with the unknown length of user input (besides just making a very large buffer).
* Because of the earlier mistake of using char** rather than char*, I made more buffers than I needed because it appeared to fix the issue at first.
* Implementing a "flushing" of the buffer used to grab input from the file descriptor would have probably made things easier, but I didn't think of that at first and so instead did stuff with checking for '\n' rather than '\0', and some offsets.
* This caused additional headaches when testing the case of receiving the output of another program (the example from the assignment worksheet, involving echo) from stdin, rather than user input from a terminal, because I made an (incorrect) assumption that stdin would always be someone typing single lines and hitting return.

#### Testing
* Tested user input and file input with varying amounts of lines.
* Tested running with arguments in any order.
* Tested stdin from another program (echo).
* Tested non-integer arguments after -n.
* Tested some non-text files like .exe, .pdf, .gif; it either prints nothing or garbage (but not as much as actual head would)

## Tail
#### By Jason
* To compile: gcc -Wall -O3 -o tail tail.c
* -n \<number\> and a filename can be provided as arguments
#### Process
* I initially copied over an early working version of head, since head and tail are similar in function.
* Created a function using lseek to find the size of a file.
* Created my_find_end_offset to find the right offset from the end to start from, based on number of lines to print.
* Made some derivatives of helper functions (possibly redundant).
* I adjusted how the my_copy_lines function worked for the stdin case and for opened files.
* Any other bug fixes in head were carried over to tail, otherwise the rest of the code did not change much.

#### Troubles
* Some problems from head also applied to tail, like the buffer overflow with large files and expecting a certain result from stdin.
* Having to work from the end of the file rather than the beginning gave me some issues at first.
* I had to look up how to send an EOF signal from the terminal (it's ctrl+d). This probably contributed a bit to the assumptions I made about stdin.
* I forgot at one point that the 'read' call would only return 0 (aka EOF) if it was not able to read any bytes, because of being at the end of a file. This caused a bit of trouble with the output from text fed into stdin from 'echo'.

#### Testing
* Tested most of the same things as head.

## FindLocationFast
#### By Bryan
#### Process
* I started by implementing both myfile_puts and my_strlen, knowing that I would have to write quite a lot in all the programs. Put this implememntation in all the c programs for use.
* Created two wrappers for myfile_puts, my_print and my_prterr. One to easily write to stdout, and the other to easily write to stderr.
* Started work on my_parse_arg. Simply checks the args, and ensure all the conditions given in the assignment sheet are checked for. It was pretty simple to implement; though during the implementation I realized I needed a check_numeric function to check the prefix. Also added a --help flag.
* Then worked on the implementation of the main program. Realized during I needed a function str_string and my_strcmp.

#### Troubles
* Not having dealt with c or c++ in a while, it took me some time to get use to working with pointers and initializing variable(char arrays especially)
* Took me a while to figure out how to implement a my_strcmp and realizing that strings need to be null terminated.
* For a while I tried to lseek to a location with the bytes, but it caused me a bit of trouble. In order to get it to work a bit smoother, I calculated the number of lines in the file and used the line numbers to lseek through the files. 

#### Testing
* Tested the program by running mulitple searches of different prefixes that I confirmed returned the right output to stdout.
* Tested the program by passing incorrect arguments to the program. (To many arguments, to few arguments, not numeric prefixes, not 6 digit prefixes, files that dont exist.)
* Tested the program by passing it incorrectly formatted files and prefixes that do not exist in the file.
* Running program through strace to ensure no unclosed files.

## FindLocationFastMemory
#### By Bryan
#### Process
* Coding this program used almost all of the same code from the findlocationfast.c file. The only changes came in the main funtion of the program.
* Added a typedef for a way to map the memory where the file is located with nmap to a struct.

#### Troubles
* I had a lot of trouble actually comparing the string in memory with the searched for prefix. Took me a while to realize that I had to use a new function called str_cp to copy the string from memory to a char array to then compare it with the prefix.

#### Testing
* Tested the program by running mulitple searches of different prefixes that I confirmed returned the right output to stdout.
* Tested the program by passing incorrect arguments to the program. (To many arguments, to few arguments, not numeric prefixes, not 6 digit prefixes, files that dont exist.)
* Tested the program by passing it incorrectly formatted files and prefixes that do not exist in the file.
* Running program through strace to ensure no unclosed files.

## PipeObserver
#### By Bryan
#### Process
* Went into this using alot of the helper functions from findlocation and findlocationfastmemory and the example code of pipes and forks that Professor Lauter went over in class.
* Started by implementing the arg_parse to ensure the specification arguments are passed to the program.
* Then started work on pipe_and_fork to implement the majority of the program.
* To do this I just went through the assingment specifications and implemented it as such. The specifications does a good job going step by step on what we had to do.
* I made main call arg_parse, which calls pipe_and_fork, in order to make it easy to pass arguments between the functions.
* Both arg_parse and pipe_and_fork return an int, which is the exit code that the process will exit with in main().
* arg_parse uses a simple state machine to loop through the arguments.
* 
> State 0: Looking for "[" in the next argument.
> State 1: While number_of_opens != 0, add next argument to command one list.
> State 2: Looking for "[" in the next argument.
> State 3: While number_of_opens != 0, add next argument to command two list.
> State 4: If the next argument is not null, exits with error code.

#### Troubles
* A majority of my problems occured when not closeing the read end of the first pipe at the right time. This caused my loop in grandchild one to hang the program. This was good though, since it caused me to rethink my knowledge on how I though the forking worked.
* Another problem I had was when trying to create the arrays to send to execvp. The method I ended up using is not the best, and even causes a warning when compiling since it is not the exact right type of array, but it works on all the cases I have tested it on. Creating a char* const* array from the argv array proved to be difficult.
* Had trouble creating a NULL terminated array for execvp, but ended up just initializing all values of list to NULL.
* Keeping track of all the eror checking was difficult as well. There are just so many different thing to error check for it was a process to check them all.
* Ensuring all pipes were closed for every process during an error was confusing and took a lot of time to get right.

#### Testing
* Tested the program by running multiple different commands as arguments with different flags and arguments to them.
* Tested the program by passing incorrect arguments to the program. (Files that do not exist, commands that do not exist, commands with incorrect arguments, incorrect opening and closing brackets.)
* Running program through strace to ensure no unclosed files or pipes, though this only shows for the parent process so is not all that helpful.