This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
haas:fall2017:cprog:projects:cbf0 [2017/09/09 23:24] – wedge | haas:fall2017:cprog:projects:cbf0 [2017/10/15 20:49] (current) – wedge | ||
---|---|---|---|
Line 3: | Line 3: | ||
< | < | ||
</ | </ | ||
- | |||
- | ~~TOC~~ | ||
======Project: | ======Project: | ||
Line 35: | Line 33: | ||
The computer works in units of **bytes**, which these days means groups of 8 bits. C has the ability to arbitrarily read and write individual bytes of data, and we will want to make use of that to aid us in our current task. | The computer works in units of **bytes**, which these days means groups of 8 bits. C has the ability to arbitrarily read and write individual bytes of data, and we will want to make use of that to aid us in our current task. | ||
+ | |||
+ | =====Opening and reading from files===== | ||
+ | The nice thing about C is that it tends to embody the " | ||
+ | |||
+ | What this means, basically, is that interacting with data in a file is really no different that interacting with data from the keyboard or data to the screen. We merely need a FILE pointer and appropriate resources allocated. | ||
+ | |||
+ | To interact with a file, we must first declare a pointer to type FILE that will be our point of transaction. | ||
+ | |||
+ | Common names for our file pointer variable are **fp**, **fPtr**, **input**, **inp**, but in reality can be anything you want. | ||
+ | |||
+ | The intention is that, of course, you name variables so they are meaningful in the context of the overall implementation. | ||
+ | |||
+ | <code c> | ||
+ | FILE *input | ||
+ | </ | ||
+ | |||
+ | ====Opening a file with fopen()==== | ||
+ | To attach a file stream to a FILE pointer, we utilize a file opening function such as **fopen()**. | ||
+ | |||
+ | It takes two arguments: | ||
+ | - the path and name of file we wish to open (provided as a string) | ||
+ | - the mode we wish to open the file as (provided as a string) | ||
+ | |||
+ | There are 3 common file opening modes (and combinations thereof, among others, sometimes dependent on the particular operating system being run). For now, I highly recommend just sticking to ONE mode of operation per FILE pointer. This can avoid messy things like data corruption and indirect logic/ | ||
+ | |||
+ | The 3 file modes: | ||
+ | * r - open file for reading (start at beginning) | ||
+ | * w - open file for writing (start at beginning) | ||
+ | * a - open file for appending (add to end) | ||
+ | |||
+ | If we wanted to open the file " | ||
+ | |||
+ | <code c> | ||
+ | input = fopen (" | ||
+ | </ | ||
+ | |||
+ | Note the double quotes around each argument. They both need to be strings (ie array of char terminated with NULL terminator characters), | ||
+ | |||
+ | ====Reading from the file==== | ||
+ | If the file is filled with a set format of data you'd like to retrieve, such as one short integer per line (basically, a text file filled with numbers), we can just use our trusty and familiar **fscanf()** function. We merely have have to indicate the correct file pointer: | ||
+ | |||
+ | <code c> | ||
+ | short int value = 0; | ||
+ | | ||
+ | ... | ||
+ | | ||
+ | fscanf (input, " | ||
+ | </ | ||
+ | |||
+ | If there is no simple universal " | ||
+ | |||
+ | The **fscanf()** function is still viable here, but if all we're after is a char value, there' | ||
+ | |||
+ | To read a byte of data from a file and store it in our variable (called byte), we would do the following: | ||
+ | |||
+ | <code c> | ||
+ | char byte = 0; | ||
+ | | ||
+ | ... | ||
+ | | ||
+ | byte = fgetc (input); | ||
+ | </ | ||
+ | |||
+ | The **fgetc()** function takes the intended FILE pointer it is to read from as its argument, so **input** should be a FILE pointer AND should have previously been **fopen()**' | ||
+ | |||
+ | To make things easier, placing logic to read from a file in a loop can be a very powerful combination. | ||
=====Task===== | =====Task===== | ||
Your task is to write a hex viewer, along the lines of the **xxd(1)** tool found on the system. | Your task is to write a hex viewer, along the lines of the **xxd(1)** tool found on the system. | ||
- | =====Grabit===== | ||
- | I have prepared some files to assist in our endeavors, which can be obtained through the use of the special **grabit** tool found on lab46: | ||
=====Experiencing xxd===== | =====Experiencing xxd===== | ||
Line 106: | Line 168: | ||
* a 7-digit hex offset (referring to the first data byte on a given line) | * a 7-digit hex offset (referring to the first data byte on a given line) | ||
* followed by a colon and a single space | * followed by a colon and a single space | ||
- | * then eight space separated groups of two bytes | + | * differently from **xxd(1)**: sixteen |
* however you arrive at it: two total spaces following the hex bytes (again, see output example) | * however you arrive at it: two total spaces following the hex bytes (again, see output example) | ||
* a 16-character ASCII representation field (no separating spaces between the values) | * a 16-character ASCII representation field (no separating spaces between the values) | ||
Line 114: | Line 176: | ||
* The hex values and rendered ASCII displayed will be sourced from the file specified on the command-line. While the target files for this project are less than 512 bytes, your program should be able to handle larger and smaller files, and update its display accordingly. | * The hex values and rendered ASCII displayed will be sourced from the file specified on the command-line. While the target files for this project are less than 512 bytes, your program should be able to handle larger and smaller files, and update its display accordingly. | ||
* If a line throttle is given, your program is to stop output of data and ASCII rendering at that line, once it completes. | * If a line throttle is given, your program is to stop output of data and ASCII rendering at that line, once it completes. | ||
- | * Once the data in the file has been exhausted, you need to wrap up as appropriate; | + | * Once the data in the file has been exhausted, you need to wrap up as appropriate; |
* Don't forget to **fclose()** any open file pointers! And **free()** any **malloc()**' | * Don't forget to **fclose()** any open file pointers! And **free()** any **malloc()**' | ||
+ | Sample output of your program should be as follows (compared to the **xxd(1)** output above): | ||
+ | |||
+ | <cli> | ||
+ | 0000000: 3e 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f > | ||
+ | 0000010: 50 51 52 53 54 55 56 57 58 59 5a 3c 0a 5b 61 62 PQRSTUVWXYZ< | ||
+ | 0000020: 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 cdefghijklmnopqr | ||
+ | 0000030: 73 74 75 76 77 78 79 7a 5d 0a 30 31 3a 09 09 20 stuvwxyz].01: | ||
+ | 0000040: 42 49 4e 41 52 59 0a 30 31 32 33 34 35 36 37 3a BINARY.01234567: | ||
+ | 0000050: 09 20 4f 43 54 41 4c 0a 30 31 32 33 34 35 36 37 . OCTAL.01234567 | ||
+ | 0000060: 38 39 3a 09 20 44 45 43 49 4d 41 4c 0a 30 31 32 89:. DECIMAL.012 | ||
+ | 0000070: 33 34 35 36 37 38 39 41 42 43 44 45 46 3a 48 45 3456789ABCDEF: | ||
+ | 0000080: 58 41 44 45 43 49 4d 41 4c 0a 29 21 40 23 24 25 XADECIMAL.)!@# | ||
+ | 0000090: 5e 26 2a 28 0a 2e 0a | ||
+ | </ | ||
=====Detecting Terminal Size===== | =====Detecting Terminal Size===== | ||
To detect the current size of your terminal, you may make use of the following code, provided in the form of a complete program for you to test, and then adapt into your code as appropriate. | To detect the current size of your terminal, you may make use of the following code, provided in the form of a complete program for you to test, and then adapt into your code as appropriate. | ||
Line 143: | Line 219: | ||
Compile and run the above code to see how it works. Try it in different size terminals. Then incorporate the logic into your hex viewer for this project. | Compile and run the above code to see how it works. Try it in different size terminals. Then incorporate the logic into your hex viewer for this project. | ||
- | |||
- | =====Bonus Opportunities===== | ||
- | The following can be considered a bonus point opportunity: | ||
- | |||
- | * Enhance the program to accept up to 6 pairs of additional values (offset followed by its length), where each offset through length will be colored using ANSI text escape sequences. | ||
- | * For any line containing this colorized text, highlight the address in bold white. | ||
- | |||
- | ====Sample output==== | ||
- | |||
- | As an example, running the program with the following arguments could produce results like this: | ||
- | |||
- | <cli> | ||
- | lab46: | ||
- | </ | ||
- | |||
- | {{: | ||
- | |||
- | ====ANSI escape sequences for color==== | ||
- | This probably isn't very portable, and depending on the terminal, it may not work for some people. | ||
- | |||
- | It may be most convenient to set up preprocessor #define statements near the top of your code, as follows: | ||
- | |||
- | <code c> | ||
- | # | ||
- | # | ||
- | # | ||
- | # | ||
- | # | ||
- | # | ||
- | # | ||
- | # | ||
- | # | ||
- | # | ||
- | # | ||
- | # | ||
- | # | ||
- | # | ||
- | # | ||
- | # | ||
- | # | ||
- | # | ||
- | </ | ||
- | |||
- | To use, you output them: | ||
- | |||
- | < | ||
- | fprintf(stdout, | ||
- | fprintf(stdout, | ||
- | fprintf(stdout, | ||
- | </ | ||
- | |||
- | You have to remember to turn the color or setting off (resetting it) to revert back to the original color. | ||
- | |||
- | You can mix and match as well: | ||
- | |||
- | < | ||
- | fprintf(stdout, | ||
- | fprintf(stdout, | ||
- | fprintf(stdout, | ||
- | fprintf(stdout, | ||
- | fprintf(stdout, | ||
- | </ | ||
- | |||
- | While there are 8 available foreground colors, bolding can double that range to 16. | ||
=====Submission===== | =====Submission===== |