25.5. Example To help make all of this more concrete, here is a sample application that makes use of most of qdbm's features. It is intended as a simple phone database, although it can be used to store any simple name/value pairs. It stores its database in the user's home directory as .phonedb. The -a flag adds an entry to the database. If -f is specified, any existing entry is overwritten with the new data. The next parameter is the key value to use, and the last parameter is the data (usually a phone number). The -q flag queries the database for a particular key, which should be the only other parameter specified. Entries are removed from the database through the -d flag, which takes the key value to remove as the only other parameter. If -l is specified, all the key/value pairs in the database are listed. Here is an example of using phones: $ ./phones -a Erik 374-5876 $ ./phones -a Michael 642-4235 $ ./phones -a Larry 527-7976 $ ./phones -a Barbara 227-2272 $ ./phones -q Larry Larry 527-7976 $ ./phones -l Larry 527-7976 Erik 374-5876 Michael 642-4235 Barbara 227-2272 $ ./phones -d Michael $ ./phones -l Larry 527-7976 Erik 374-5876 Barbara 227-2272 This program does something quite useful, has fewer than 200 lines of source code, and is well suited to managing large numbers of key/value pairs, clearly illustrating the value of the qdbm library. 1: /* phones.c */ 2: 3: /* This implements a very simple phone database. Full usage 4: information is given in the text. */ 5: 6: #include <alloca.h> 7: #include <depot.h> 8: #include <errno.h> 9: #include <fcntl.h> 10: #include <stdio.h> 11: #include <stdlib.h> 12: #include <string.h> 13: #include <unistd.h> 14: 15: void usage(void) { 16: fprintf(stderr, "usage: phones -a [-f] <name> <phone>\n"); 17: fprintf(stderr, " -d <name>\n"); 18: fprintf(stderr, " -q <name>\n"); 19: fprintf(stderr, " -l\n"); 20: exit(1); 21: } 22: 23: /* Opens the database $HOME/.phonedb. If writeable is nonzero, 24: the database is opened for updating. If writeable is 0, the 25: database is opened read-only. */ 26: DEPOT * openDatabase(int writeable) { 27: DEPOT * dp; 28: char * filename; 29: int flags; 30: 31: /* Set the open mode */ 32: if (writeable) { 33: flags = DP_OWRITER | DP_OCREAT; 34: } else { 35: flags = DP_OREADER; 36: } 37: 38: filename = alloca(strlen(getenv("HOME")) + 20); 39: strcpy(filename, getenv("HOME")); 40: strcat(filename, "/.phonedb"); 41: 42: dp = dpopen(filename, flags, 0); 43: if (!dp) { 44: fprintf(stderr, "failed to open %s: %s\n", filename, 45: dperrmsg(dpecode)); 46: return NULL; 47: } 48: 49: return dp; 50: } 51: 52: /* add a new record to the database; this parses the 53: command-line arguments directly */ 54: int addRecord(int argc, char ** argv) { 55: DEPOT * dp; 56: char * name, * phone; 57: int rc = 0; 58: int overwrite = 0; 59: int flag; 60: 61: /* check for our parameters; -f means overwrite an 62: existing entry, and the name and phone number should 63: be all that remains */ 64: if (!argc) usage(); 65: if (!strcmp(argv[0], "-f")) { 66: overwrite = 1; 67: argc--, argv++; 68: } 69: 70: if (argc != 2) usage(); 71: 72: name = argv[0]; 73: phone = argv[1]; 74: 75: /* open the database for writing */ 76: if (!(dp = openDatabase(1))) return 1; 77: 78: /* if we shouldn't overwrite an existing entry, check 79: to see if this name is already used */ 80: if (!overwrite) { 81: flag = DP_DKEEP; 82: } else { 83: flag = DP_DOVER; 84: } 85: 86: if (!dpput(dp, name, -1, phone, -1, flag)) { 87: if (dpecode == DP_EKEEP) { 88: fprintf(stderr, "%s already has a listing\n", name); 89: } else { 90: fprintf(stderr, "put failed: %s\n", dperrmsg(dpecode)); 91: } 92: 93: rc = 1; 94: } 95: 96: dpclose(dp); 97: 98: return rc; 99: } 100: 101: /* looks up a name, and prints the phone number associated 102: with it; parses the command line directly */ 103: int queryRecord(int argc, char ** argv) { 104: DEPOT * dp; 105: int rc; 106: char * phone; 107: 108: /* only one argument is expected, a name to look up */ 109: if (argc != 1) usage(); 110: 111: /* open the database for reading */ 112: if (!(dp = openDatabase(0))) return 1; 113: 114: phone = dpget(dp, argv[0], -1, 0, -1, NULL); 115: if (!phone) { 116: if (dpecode == DP_ENOITEM) 117: fprintf(stderr, "%s is not listed\n", argv[0]); 118: else 119: fprintf(stderr, "error reading database: %s\n", 120: dperrmsg(dpecode)); 121: 122: rc = 1; 123: } else { 124: printf("%s %s\n", argv[0], (char *) phone); 125: rc = 0; 126: } 127: 128: dpclose(dp); 129: 130: return rc; 131: } 132: 133: /* delete the specified record; the name is passed as a 134: command-line argument */ 135: int delRecord(int argc, char ** argv) { 136: DEPOT * dp; 137: int rc; 138: 139: /* only a single argument is expected */ 140: if (argc != 1) usage(); 141: 142: /* open the database for updating */ 143: if (!(dp = openDatabase(1))) return 1; 144: 145: if (!(rc = dpout(dp, argv[0], -1))) { 146: if (dpecode == DP_ENOITEM) 147: fprintf(stderr, "%s is not listed\n", argv[0]); 148: else 149: fprintf(stderr, "error removing item: %s\n", 150: dperrmsg(dpecode)); 151: 152: rc = 1; 153: } 154: 155: dpclose(dp); 156: 157: return rc; 158: } 159: 160: /* lists all of the records in the database */ 161: int listRecords(void) { 162: DEPOT * dp; 163: char * key, * value; 164: 165: /* open the database read-only */ 166: if (!(dp = openDatabase(0))) return 1; 167: 168: dpiterinit(dp); 169: 170: /* iterate over all of the records */ 171: while ((key = dpiternext(dp, NULL))) { 172: value = dpget(dp, key, -1, 0, -1, NULL); 173: printf("%s %s\n", key, value); 174: } 175: 176: dpclose(dp); 177: 178: return 0; 179: } 180: 181: int main(int argc, char ** argv) { 182: if (argc == 1) usage(); 183: 184: /* look for a mode flag, and call the appropriate function 185: with the remainder of the arguments */ 186: if (!strcmp(argv[1], "-a")) 187: return addRecord(argc - 2, argv + 2); 188: else if (!strcmp(argv[1], "-q")) 189: return queryRecord(argc - 2, argv + 2); 190: else if (!strcmp(argv[1], "-d")) 191: return delRecord(argc - 2, argv + 2); 192: else if (!strcmp(argv[1], "-l")) { 193: if (argc != 2) usage(); 194: return listRecords(); 195: } 196: 197: usage(); /* did not recognize any options */ 198: return 0; /* doesn't get here due to usage() */ 199: } |