Homework 4 Answers Due Date: Monday, May 27, 2002, at 11:59PM Points: 100 UNIX System 1. (10 points) How do I delete the file i? Answer: Either rm./-i or rm -- -i will work. 2. (15 points) Please list all sections of the manual on the CSIF Linux systems containing a command named time. What are the file names of those manual pages? Answer: The command man -a time will print the appropriate man pages, which are time(1), time(2), and time(n). The command man -a -w time gives the names of the file containing each manual page. The file names are /usr/share/man/man1/time.1.gz, /usr/share/man/man2/time.2.gz, and /usr/share/man/mann/time.n.gz, respectively. C Programming 3. (30 points) A palindrome is a word or sentence which is the same whether read backwards or forwards. Please write a program ispal.c which reads each line from its standard input and says whether or not the line is a palindrome. For each line, all non-alphanumeric characters are to be ignored, and case is not to matter. For example, Hint: use recursion. standard input Madam, I'm Adam! O give me a home Able was I ere I saw Elba Answer: Here is my program. It uses recursion. standard output A palindrome Not a palindrome A palindrome * ispal.c Matt Bishop * * This program tests a line of input to see if it * is a palindrome #include <stdio.h> * macros #define SZBUFFER 1024 max length of input buffer * forward declarations void shrink(char *); eliminate non-alphanumerics int ispal(char *, char *); test for palindromosity * this reads each line, calls a function (shrink) * to clobber non-alphanumerics, and then prints a message * about whether the input is a palindrome void main(void) Version of June 2, 2002 11:51 pm Page 1 of 11
char buf[szbuffer]; input buffer register int len; length of input line * loop until user is done while(fgets(buf, SZBUFFER, stdin)!= NULL) clobber all non-alphanumerics (including the newline) shrink(buf); check for error if ((len = strlen(buf)) == 0) printf("no string given\n"); else no error -- check for palindromeness (?!) printf("%s palindrome\n", ispal(buf, &buf[len-1])? "A" : "Not a"); * exit nicely exit(0); * shrink: eliminate all non-alphanumerics, in place * arguments: char *buf string to be shrunk * return: nothing; string reduced in place * output: none void shrink(char *buf) register char *p; next char to look at register char *n; where to put it * go through the array, moving all alphanumerics to the * front of the array and clobbering everything else for(n = p = buf; *p; p++) if not alphanumeric, skip it if (!isalnum(*p)) continue; alphanumeric; make lower case (for consistency) *n++ = isupper(*p)? tolower(*p) : *p; add string terminator *n = '\0'; * ispal: is it a palindrome? Version of June 2, 2002 11:51 pm Page 2 of 11
* arguments: char *buf beginning of string * char *ebuf end of string * return: 1 if the string (buf,..., ebuf) is a palindrome * 0 if the string (buf,..., ebuf) is not a palindrome * output: none int ispal(char *buf, char *ebuf) * first, the special cases be sure there is a string to check! if (buf >= ebuf) if first and last differ, it isn't if (*buf!= *ebuf) * and now recurse if the same, check the inner part of the string return(ispal(buf+1, ebuf-1)); 4. (30 points) Write a program to list the files in a directory in the order in which the directory entries occur. Your program is to use the library functions opendir (3), readdir(3), and closedir(3). Your output should include the number of the directory entry, the inode number of the file, and the file name. For example, in a directory containing the files a.out and d.c, the output might look like: order inode name 1 1119713. 2 272679.. 3 1119719 d.c 4 1119738 a.out Hint: On the Linux systems in the CSIF, the inode field of the dirent structure is d_ino. Please see the manual pages for the functions for more details. (The inode field is not documented there, but the field containing the name of the file is.) Answer: * dumpdir -- Matt Bishop * This program dumps the file names in a directory file in the order * in which they occur; it also prints the inode numbers #include <stdio.h> #include <sys/types.h> #include <dirent.h> * dumpdir: open directory file, dump contents * arguments: char *dname name of directory * return: 0 on success * 1 on error/failure * output: inode/name pairs in order of directory contents Version of June 2, 2002 11:51 pm Page 3 of 11
int dumpdir(char *dname) DIR *dp; points to directory struct dirent *dent; pointer to directory entry int counter; position in listing * open the directory if ((dp = opendir(dname)) == NULL) perror(dname); * print header printf("%s:\n%5s\t%10s\t%s\n", dname, "count", " inode ", "name"); * just go through directory one entry at a time for(counter = 1; (dent = readdir(dp))!= NULL; counter++) printf("%5d\t%10d\t%s\n", counter, dent->d_ino, dent->d_name); * close it -- ignore errors here (void) closedir(dp); * the main routine int main(int argc, char *argv[]) process arguments if (argc == 1) return(dumpdir(".")); else if (argc == 2) return(dumpdir(argv[1])); more than 1 argument -- error fprintf(stderr, "Usage: %s [ directory ]\n", argv[0]); Version of June 2, 2002 11:51 pm Page 4 of 11
Debugging 5. (15 points) On the web page for the class is the source for a set of routines to manage a linked list. The functions provided are: int insert(struct link **head, int value) Insert value into the linked list pointed to by *head; update *head if necessary. If the insertion succeeds, return 1; if it fails, return 0. int delete(struct link **head, int value) Delete value from the linked list pointed to by *head; update *head if necessary (if the list is empty, make it NULL). If the deletion succeeds, return 1; if it fails (because value is not in the list), return 0. void prforw(struct link *head) Print all integers in the linked list pointed to by head in order (from head to tail). void prback(struct link *head) Print all integers in the linked list pointed to by head in reverse order (from tail to head). Unfortunately, they don't quite work right. Debug them. Turn in commented, corrected routines; be sure you mark what changes were made, and why! Hints: The program testll.c is a program that calls the library routines to manage a linked list of numbers. You can use it to help figure out what the problems are. The manual page on the web tells you how to use that program. Both the file containing the routines, llist.c, and the test program, testll.c, require the header file llist.h. Answer: * llist.c Matt Bishop * * This is a library of linked list functions: * insert -- inserts a value into the linked list * delete -- deletes a value from the linked list * prforw -- prints the values in the list in order * prback -- prints the values in the list in reverse order #include "llist.h" * insert: insert a value into a linked list * arguments: LLIST **head address of pointer to head * of linked list * int value value to be inserted * return: 1 insertion succeeded; on return, head points * to the address of the (possibly new) head of * the linked list * 0 insertion failed * output: none * exceptions: memory cannot be allocated (returns 0) int insert(llist **head, int value) register LLIST *p, *q; pointers to walk linked list * you can argue whether or not this next line had a bug, * but it certainly was poor style, since lalloc is * called below! Version of June 2, 2002 11:51 pm Page 5 of 11
register LLIST *temp; points to new node * CONSTRUCT NEW NODE FOR THE LINKED LIST if ((temp = lalloc()) == NULL) temp->val = value; temp->next = L_NULL; * if an empty list, make this node the head if (*head == NULL) *head = temp; ********** BUG FIX #1 ******************************* * add code to deal with inserting before head of list if ((*head)->val > value) temp->next = *head; *head = temp; ********** END BUG FIX #1 *************************** * walk the linked list; after this loop, * you'll need to insert the new node between * p and q for(p = *head; p!= NULL && p->val < value; p = p->next) q = p; * insert the new node and return success q->next = temp; temp->next = p; * delete: delete a value from a linked list * arguments: LLIST **head address of pointer to head * of linked list * int value value to be deleted * return: 1 deletion succeeded; on return, head points * to the address of the (possibly new) head of * the linked list * 0 deletion failed Version of June 2, 2002 11:51 pm Page 6 of 11
* output: none * exceptions: pointer to pointer to head of list is NULL (return 0) * no node in the list contains value (return 0) int delete(llist **head, int value) register LLIST *p, *q; used to locate node in list * if an empty list, nothing to delete if (*head == NULL) * value is at the head of linked list if ((*head)->val == value) make head point to next one p = *head; *head = (*head)->next; free it and return success (void) free(p); * figure out where the node to be deleted is * after this loop, if there is a node with the given * value in the linked list, p points to it, and * q points to its predecessor for(p = *head; p!= NULL && p->val < value; p = p->next) q = p; see if the value is in the list if (p->val!= value) ********** BUG FIX #2 ******************************* * make next of element before deleted element * point to element after deleted element q->next = p->next; ********** END BUG FIX #2 *************************** * release the deleted node and return success (void) free(p); * prforw print list * arguments: LLIST *head pointer to head of linked list Version of June 2, 2002 11:51 pm Page 7 of 11
* return: nothing * output: list of values in linked list, one per line * exceptions:none void prforw(llist *head) register LLIST *p; pointer in a for loop * walk the list, printing as you go for(p = head; p!= NULL; p = p->next) printf("%d\n", p->val); * prback print list backwards * arguments: LLIST *head pointer to head of linked list * return: nothing * output: list of values in linked list (reversed), one per line void prback(llist *head) if list is empty, print nothing if (head == NULL) return; print rest of list (void) prback(head->next); print this node printf("%d\n", head->val); Extra Credit 6. (10 points) Modify the program you wrote in problem 4 to use the scandir(3) function to sort the files by either inode number (option i) or name (option n). Answer: * dumpdir -- Matt Bishop * This program dumps the file names in a directory file in the order * in which they occur; it also prints the inode numbers #include <stdio.h> #include <sys/types.h> #include <dirent.h> * global telling us how to sort int (*dsort)(const struct dirent **, const struct dirent **) = NULL; Version of June 2, 2002 11:51 pm Page 8 of 11
* sort on inode * returns > 0 if first arg has larger inode than second * returns = 0 if first arg has same inode than second * returns < 0 if first arg has smaller inode than second int cmpino(const struct dirent **a, const struct dirent **b) return((*a)->d_ino - (*b)->d_ino); * sdumpdir: open directory file, dump contents in sorted order * arguments: char *dname name of directory * return: 0 on success * 1 on error/failure * output: inode/name pairs in order of directory contents int sdumpdir(char *dname) struct dirent **namelist; sorted directory entries int counter; position in listing int nent; number of directory entries if ((nent = scandir(dname, &namelist, NULL, dsort)) < 0) fprintf(stderr, "sort of directory entries failed\n"); * print header printf("%s (%d):\n%5s\t%10s\t%s\n", dname, nent, "count", " inode ", "name"); * just go through directory one entry at a time for(counter = 0; counter < nent; counter++) printf("%5d\t%10d\t%s\n", counter, namelist[counter]->d_ino, namelist[counter]->d_name); * udumpdir: open directory file, dump contents in directory order * arguments: char *dname name of directory * return: 0 on success * 1 on error/failure * output: inode/name pairs in order of directory contents int udumpdir(char *dname) Version of June 2, 2002 11:51 pm Page 9 of 11
DIR *dp; points to directory struct dirent *dent; pointer to directory entry int counter; position in listing * open the directory if ((dp = opendir(dname)) == NULL) perror(dname); * print header printf("%s:\n%5s\t%10s\t%s\n", dname, "count", " inode ", "name"); * just go through directory one entry at a time for(counter = 1; (dent = readdir(dp))!= NULL; counter++) printf("%5d\t%10d\t%s\n", counter, dent->d_ino, dent->d_name); * close it -- ignore errors here (void) closedir(dp); * dumpdir: call right directory dumper, sorted or unsorted * arguments: char *dname name of directory * return: value of right directory dumper * output: none per se; only from called routine int dumpdir(char *dname) if not to be sorted, called unsort dumper if (dsort == NULL) return(udumpdir(dname)); call sorted directory dumper return(sdumpdir(dname)); * the main routine int main(int argc, char *argv[]) Version of June 2, 2002 11:51 pm Page 10 of 11
int i = 1; counter in a for loop * first do options if (argc > 1) for(i = 1; i < argc && argv[i][0] == '-'; i++) if (strcmp(argv[i], "-i") == 0) dsort = cmpino; else if (strcmp(argv[i], "-n") == 0) dsort = alphasort; else fprintf(stderr, "%s: invalid option\n", argv[i]); fprintf(stderr, "Usage: %s [ -i -n ] [ directory ]\n", argv[0]); * now process directories if (i == argc) return(dumpdir(".")); else if (i == argc - 1) return(dumpdir(argv[i])); more than 1 argument -- error fprintf(stderr, "Usage: %s [ -i -n ] [ directory ]\n", argv[0]); Version of June 2, 2002 11:51 pm Page 11 of 11