All system calls for performing I/O refer to open files using a file descriptor, a (usually small) nonnegative integer.
— Michael Kerrisk, The Linux Programming Interface, Chapter 4, pp. 69.
User space programs running atop Linux may interact with files through a combination of Linux syscalls and standard library functions that support a wide range of input and output operations. Exploring the different ways in which user space programs can input data from files into variables and character buffers, and can output data from variables and character buffers into files using those calls is the focus of today's material.
In this studio, you will:
Please complete the required exercises below, as well as any optional enrichment exercises that you wish to complete. We encourage you to please work in groups of 2 or 3 people on each studio (and the groups are allowed to change from studio to studio) though if you would prefer to complete any studio by yourself that is allowed.
As you work through these exercises, please record your answers, and when you finish them each and every person who worked on them should please log into Canvas, select this course in this semester, and then upload a file containing them and also upload any other files the assignment asks for, and submit those files for this studio assignment (there should be a separate submission from each person who worked together on them).
Make sure that the name of each person who worked on these exercises is listed in the first answer, and make sure you number each of your responses so it is easy to match your responses with each exercise.
argc - 1).
If the program was passed at least 2 command line arguments it
should (1) use
fopen to open up the file that is named in
the first argument (which is found in
writing (discarding any previous contents if the file already exists), (2) use
fprintf with the appropriate
formatting string to output each of the subsequent program arguments
argv[argc - 1] inclusive) to
the file on its own line, (3) use
fclose to flush the file stream buffer and close the file, and then (4) return 0 to indicate success.
If it was not passed at least two arguments the program should use
printf to output a helpful usage message that uses its name (which is
argv) and illustrates how to invoke it
correctly, and then should return a non-zero value to indicate that it
did not complete successfully.
Compile and run your program with different command lines, and
for any well formed command line that did not generate the usage
message, use the
cat shell command to show the contents
of the file that was produced. As the answer to this exercise please
show the output from one unsuccessful run and one successful run of
your program, as well as what the file contained after the successful
fprintfto print the additional arguments to the file it uses
fputsinstead, to produce the same file formatting as your original program. Note that this will require you to use an additional character string besides the program arguments, to add line breaks.
Compile your new program and run it to confirm that it can produce the same output format as the original program. As the answer to this exercise please show the code for the new program.
fputslibrary functions to print the additional arguments to the file, it instead calls
writeto produce the same file formatting as your original program. Note that this will require you to use an integer variable to hold a file descriptor, instead of a file stream pointer variable, and you will need to specify the appropriate flags in the call to
openper the table given on pp. 74 in section 4.3.1 of the LPI textbook. You will still need to use a separate string to produce line breaks, and each call to
writewill need to pass in the number of bytes that should be written from each string (which the
strlenlibrary function can provide).
Compile your new program and run it to confirm that it can produce the same output format as the original program. As the answer to this exercise please show the code for the new program that uses those lower level syscalls.
fprintf) and modify that new version so that instead of replacing the file's contents if they already exist, it appends to the end of the file. Also modify the program so that it outputs a space instead of a line break in between each of the subsequent program arugment strings and only after the last one outputs a line break, so that each time it runs it adds a single new line to the file.
Compile your new program and run it a few times with the same file name but
with different subsequent arguments, and after that
contents of the file to confirm that the arguments were correctly
concatenated into a separate line that was appended for each run
of the program. As the answer to this
exercise please show the output of those runs to confirm that.
fprintfto output to the file (all on the same line): (1) an unsigned long integer value for the length of the line that follows that number, then (2) a space character, then (3) the program argument strings with spaces in between them, and then (4) the line break.
Compile your new program and run it a few times with the same file
name but with different subsequent arguments, and after that
cat the contents of the file to confirm that each line's
length value and formatting are correct. As the answer to this
exercise please show the output of those runs to confirm that.
The program should declare two unsigned integer variables, both initialized to 0, and a pointer variable that is also initialized to 0, with which to manage data of previously unknown length that it will read from the file.
One of the unsigned integer variables should be used to remember the largest length value it has seen so far (which also should be the size of the dynamically allocated memory to which the pointer variable points), and the other variable should be used to read in each new length value from the file.
The program should open the file for reading and until it reaches the end of the file it should repeatedly:
freeto free the dynamically allocated memory to which the pointer variable points, then in any case (b) store the newly read length as the new largest length that has been seen, and then (c) dynamically allocate memory that can hold that new largest length and store its address in the pointer variable;
Note that instead of using
free and then
malloc to resize the dynamically allocated memory in step 2 of this
exercise, after the first call to
malloc you could use
realloc alternatively to accomplish the same thing.
Note that if the program ever uses
malloc to allocate
memory dynamically, it should call
free to release that memory
before it exits.
Compile your new program and run it with the name of a file that was produced in the previous exercise, to confirm that data from each line of the file is printed out on its own line, and that no extraneous characters are printed. As the answer to this exercise please show the output of the program to confirm that.