3 * UCC (University [of WA] Computer Club) Electronic Accounting System
5 * itemdb.c - Dispense Item Databse
7 * This file is licenced under the 3-clause BSD Licence. See the file COPYING
22 extern tHandler gCoke_Handler;
23 extern tHandler gSnack_Handler;
24 extern tHandler gDoor_Handler;
27 void Init_Handlers(void);
28 void Load_Itemlist(void);
29 void Items_ReadFromFile(void);
30 char *trim(char *__str);
34 tItem *gaItems = NULL;
35 time_t gItems_LastUpdated;
36 tHandler gPseudo_Handler = {.Name="pseudo"};
37 tHandler *gaHandlers[] = {&gPseudo_Handler, &gCoke_Handler, &gSnack_Handler, &gDoor_Handler};
38 int giNumHandlers = sizeof(gaHandlers)/sizeof(gaHandlers[0]);
39 char *gsItemListFile = DEFAULT_ITEM_FILE;
43 regex_t gItemFile_Regex;
49 for( i = 0; i < giNumHandlers; i ++ )
51 if( gaHandlers[i]->Init )
52 gaHandlers[i]->Init(0, NULL); // TODO: Arguments
55 // Use inotify to watch the snack config file
60 giItem_INotifyFD = inotify_init();
61 inotify_add_watch(giItem_INotifyFD, gsItemListFile, IN_MODIFY);
64 signal(SIGIO, &ItemList_Changed);
66 // Fire SIGIO when data is ready on the FD
67 fcntl(giItem_INotifyFD, F_SETOWN, getpid());
68 oflags = fcntl(giItem_INotifyFD, F_GETFL);
69 fcntl(giItem_INotifyFD, F_SETFL, oflags | FASYNC);
75 void ItemList_Changed(int signum)
78 read(giItem_INotifyFD, buf, 512);
81 signum = 0; // Shut up GCC
86 * \brief Read the initial item list
88 void Load_Itemlist(void)
91 rv = regcomp(&gItemFile_Regex, "^-?([a-zA-Z][a-zA-Z]*)\\s+([0-9]+)\\s+([0-9]+)\\s+(.*)", REG_EXTENDED);
94 size_t len = regerror(rv, &gItemFile_Regex, NULL, 0);
96 regerror(rv, &gItemFile_Regex, errorStr, len);
97 fprintf(stderr, "Rexex compilation failed - %s\n", errorStr);
101 Items_ReadFromFile();
103 // Re-read the item file periodically
104 // TODO: Be less lazy here and check the timestamp
105 AddPeriodicFunction( Items_ReadFromFile );
109 * \brief Read the item list from disk
111 void Items_ReadFromFile(void)
119 regmatch_t matches[5];
121 if( gItems_LastUpdated )
124 if( stat(gsItemListFile, &buf) ) {
125 fprintf(stderr, "Unable to stat() item file '%s'\n", gsItemListFile);
129 // Check if the update is needed
130 if( gItems_LastUpdated > buf.st_mtime )
135 fp = fopen(gsItemListFile, "r");
137 fprintf(stderr, "Unable to open item file '%s'\n", gsItemListFile);
138 perror("Unable to open item file");
142 while( fgets(buffer, BUFSIZ, fp) )
152 tmp = strchr(buffer, '#');
154 tmp = strchr(buffer, ';');
160 if(strlen(line) == 0) continue;
162 // Pass regex over line
163 if( RunRegex( &gItemFile_Regex, line, 5, matches, NULL) ) {
164 fprintf(stderr, "Syntax error on line %i of item file '%s'\n", lineNum, gsItemListFile);
169 type = line + matches[1].rm_so; line[ matches[1].rm_eo ] = '\0';
170 num = atoi( line + matches[2].rm_so );
171 price = atoi( line + matches[3].rm_so );
172 desc = line + matches[4].rm_so;
175 printf("Item '%s' - %i cents, %s:%i\n", desc, price, type, num);
179 for( i = 0; i < giNumHandlers; i ++ )
181 if( strcmp(type, gaHandlers[i]->Name) == 0 ) {
182 handler = gaHandlers[i];
188 fprintf(stderr, "Unknow item type '%s' on line %i (%s)\n", type, lineNum, desc);
192 for( i = 0; i < numItems; i ++ )
194 if( items[i].Handler != handler ) continue;
195 if( items[i].ID != num ) continue;
198 printf("Redefinition of %s:%i, updated\n", handler->Name, num);
200 items[i].Price = price;
202 items[i].Name = strdup(desc);
205 if( i < numItems ) continue;
207 items = realloc( items, (numItems + 1)*sizeof(items[0]) );
208 items[numItems].Handler = handler;
209 items[numItems].ID = num;
211 items[numItems].Price = 0;
213 items[numItems].Price = price;
214 items[numItems].Name = strdup(desc);
215 items[numItems].bHidden = (line[0] == '-');
228 giNumItems = numItems;
231 gItems_LastUpdated = time(NULL);
235 * \brief Update the item file from the internal database
237 void Items_UpdateFile(void)
244 regmatch_t matches[5];
245 char **line_comments;
249 fp = fopen(gsItemListFile, "r");
251 fprintf(stderr, "Unable to open item file '%s'\n", gsItemListFile);
252 perror("Unable to open item file");
257 while( fgets(buffer, BUFSIZ, fp) )
262 line_comments = malloc(lineNum * sizeof(char*));
263 line_items = malloc(lineNum * sizeof(int));
267 fseek(fp, 0, SEEK_SET);
268 while( fgets(buffer, BUFSIZ, fp) )
270 char *hashPos, *semiPos;
278 line_items[lineNum-1] = -1;
279 line_comments[lineNum-1] = NULL;
282 hashPos = strchr(buffer, '#');
283 semiPos = strchr(buffer, ';');
284 if( hashPos && semiPos ) {
285 if( hashPos < semiPos )
286 line_comments[lineNum-1] = strdup(hashPos);
289 line_comments[lineNum-1] = strdup(hashPos);
292 line_comments[lineNum-1] = strdup(semiPos);
294 if(hashPos) *hashPos = '\0';
295 if(semiPos) *semiPos = '\0';
299 if(strlen(line) == 0) continue;
301 // Pass regex over line
302 if( RunRegex( &gItemFile_Regex, line, 5, matches, NULL) ) {
303 fprintf(stderr, "Syntax error on line %i of item file '%s'\n", lineNum, gsItemListFile);
308 type = line + matches[1].rm_so; line[ matches[1].rm_eo ] = '\0';
309 num = atoi( line + matches[2].rm_so );
313 for( i = 0; i < giNumHandlers; i ++ )
315 if( strcmp(type, gaHandlers[i]->Name) == 0 ) {
316 handler = gaHandlers[i];
321 fprintf(stderr, "Warning: Unknown item type '%s' on line %i\n", type, lineNum);
325 for( i = 0; i < giNumItems; i ++ )
327 if( gaItems[i].Handler != handler ) continue;
328 if( gaItems[i].ID != num ) continue;
330 line_items[lineNum-1] = i;
333 if( i >= giNumItems ) {
340 //fp = fopen("items.cfg.new", "w"); // DEBUG: Don't kill the real item file until debugged
341 fp = fopen(gsItemListFile, "w");
345 int done_items[giNumItems];
346 memset(done_items, 0, sizeof(done_items));
349 for( i = 0; i < lineNum; i ++ )
351 if( line_items[i] != -1 ) {
352 tItem *item = &gaItems[ line_items[i] ];
354 if( done_items[ line_items[i] ] ) {
355 fprintf(fp, "; DUP -");
357 done_items[ line_items[i] ] = 1;
362 fprintf(fp, "%s\t%i\t%i\t%s\t",
363 item->Handler->Name, item->ID, item->Price, item->Name
367 if( line_comments[i] ) {
368 fprintf(fp, "%s", line_comments[i]);
369 free( line_comments[i] );
376 for( i = 0; i < giNumItems; i ++ )
378 tItem *item = &gaItems[i];
379 if( done_items[i] ) continue ;
384 fprintf(fp, "%s\t%i\t%i\t%s\n",
385 item->Handler->Name, item->ID, item->Price, item->Name
390 free( line_comments );
396 char *trim(char *__str)
401 while( isspace(*__str) )
406 while( i-- && isspace(__str[i]) ) {