The purpose of these questions is to have you use the UNIX system and environment.
(100 points) The purpose of this question is for you to build on your (or my) answer to program 2 to gain experience with pointers and command-line options.
Input Specifications
Read from the standard input or from a list of files named on the command line. The input is supposed to be a C program. You need not check that it is, in fact, a legal C program; your program should simply assume it is.
Your program must handle the following command-line option:
-Dsym=val
Predefine the macro sym to have a value of val. If =val
is omitted, the
symbol is defined as the empty string. Thus, typing
main -Dunix=1 -Ddechas the same effect of putting the preprocessor commands
#define unix 1 #define decat the head of each file.
Output Specifications
Write to the standard output. In the first 6 columns of each output line, print the line number followed by a period ".". Print the input line beginning in column 9 (one tab from the left margin). If the input is from a file, print the name of the file followed by a ":" on the line before the first line.
If the line is a preprocessor control line, and the preprocessor directive is define, add the name, line number on which the define occurs, and definition to a table. When the input is finished, print the table, headed by the title "Table of Defines" beginning on a separate page.
If the macro is defined on the command line (using the -D option), it is to be added to the table and printed, but the line number is to be printed as *predefined*.
If multiple files are named on the command line, the files are to all be processed first, and then the table printed. Note this means one symbol may have multiple definitions; this is not an error, and the definitions should all appear in the table.
Other Constraints
Put the entries into an array, one definition and one name per entry. (So, you will have to use a structure for this.) The form of each entry is
struct defent {
char *name;
char *defn;
char *filename;
int lineno;
};
You
must allocate space for the name and defn fields, but you may
assume that no more than 1000 symbols will be defined.Example Input
/* file test1.c */
#define PI 3.14159
extern void square(double *);
void main(void)
{
double f = PI;
timespie(&f);
printf("pi = %f, pi^2*e = %f\n", PI, f);
}
/* file test1-a.c */
#define PI (22/7)
#define E 2.81728
void square(double *d)
{
return(d * PI * E);
}
Example
Output
test1.c:
1. /* file test1.c */
2. #define PI 3.14159
3.
4. extern void square(double *);
5. void main(void)
6. {
7. double f = PI;
8. timespie(&f);
9. printf("pi = %f, pi^2*e = %f\n", PI, f);
10. }
test1-a.c:
1. /* file test2.c */
2. #define PI (22/7)
3. #define E 2.81728
4. void timespie(double *d)
5. {
6. return(d * PI * E);
7. }
Table of Defines:
name line file definition
PI 2 test1.c 3.14159
PI 2 test1-a.c (22/7)
E 3 test1-a.c 2.81828
The modification for the extra credit is to print the base name of the file before the line number. The output for the example input would look like this:
test1.c: 1. /* file test1.c */
test1.c: 2. #define PI 3.14159
test1.c: 3.
test1.c: 4. extern void square(double *);
test1.c: 5. void main(void)
test1.c: 6. {
test1.c: 7. double f = PI;
test1.c: 8. timespie(&f);
test1.c: 9. printf("pi = %f, pi^2*e = %f\n", PI, f);
test1.c: 10. }
test1-a.c: 1. /* file test2.c */
test1-a.c: 2. #define PI (22/7)
test1-a.c: 3. #define E 2.81728
test1-a.c: 4. void timespie(double *d)
test1-a.c: 5. {
test1-a.c: 6. return(d * PI * E);
test1-a.c: 7. }
Note
that the line numbers are all aligned. This requires you to compute the length
of the longest base name and format accordingly.The base name of a file is the last component of the full path name. For example, the base name of the file /usr/include/stdio.h is stdio.h and the base name of the directory /home/bishop is bishop.
Send email to
cs40@csif.cs.ucdavis.edu.
Department of Computer Science
University of California at Davis
Davis, CA 95616-8562