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
20 extern tHandler gCoke_Handler;
21 extern tHandler gSnack_Handler;
22 extern tHandler gDoor_Handler;
25 void Init_Handlers(void);
26 void Load_Itemlist(void);
27 void Items_ReadFromFile(void);
28 char *trim(char *__str);
32 tItem *gaItems = NULL;
33 time_t gItems_LastUpdated;
34 tHandler gPseudo_Handler = {.Name="pseudo"};
35 tHandler *gaHandlers[] = {&gPseudo_Handler, &gCoke_Handler, &gSnack_Handler, &gDoor_Handler};
36 int giNumHandlers = sizeof(gaHandlers)/sizeof(gaHandlers[0]);
37 char *gsItemListFile = DEFAULT_ITEM_FILE;
41 regex_t gItemFile_Regex;
47 for( i = 0; i < giNumHandlers; i ++ )
49 if( gaHandlers[i]->Init )
50 gaHandlers[i]->Init(0, NULL); // TODO: Arguments
53 // Use inotify to watch the snack config file
58 giItem_INotifyFD = inotify_init();
59 inotify_add_watch(giItem_INotifyFD, gsItemListFile, IN_MODIFY);
62 signal(SIGIO, &ItemList_Changed);
64 // Fire SIGIO when data is ready on the FD
65 fcntl(giItem_INotifyFD, F_SETOWN, getpid());
66 oflags = fcntl(giItem_INotifyFD, F_GETFL);
67 fcntl(giItem_INotifyFD, F_SETFL, oflags | FASYNC);
73 void ItemList_Changed(int signum)
76 read(giItem_INotifyFD, buf, 512);
79 signum = 0; // Shut up GCC
84 * \brief Read the initial item list
86 void Load_Itemlist(void)
89 rv = regcomp(&gItemFile_Regex, "^-?([a-zA-Z][a-zA-Z]*)\\s+([0-9]+)\\s+([0-9]+)\\s+(.*)", REG_EXTENDED);
92 size_t len = regerror(rv, &gItemFile_Regex, NULL, 0);
94 regerror(rv, &gItemFile_Regex, errorStr, len);
95 fprintf(stderr, "Rexex compilation failed - %s\n", errorStr);
101 // Re-read the item file periodically
102 // TODO: Be less lazy here and check the timestamp
103 AddPeriodicFunction( Items_ReadFromFile );
107 * \brief Read the item list from disk
109 void Items_ReadFromFile(void)
117 regmatch_t matches[5];
119 if( gItems_LastUpdated )
122 if( stat(gsItemListFile, &buf) ) {
123 fprintf(stderr, "Unable to stat() item file '%s'\n", gsItemListFile);
127 // Check if the update is needed
128 if( gItems_LastUpdated > buf.st_mtime )
133 fp = fopen(gsItemListFile, "r");
135 fprintf(stderr, "Unable to open item file '%s'\n", gsItemListFile);
136 perror("Unable to open item file");
140 while( fgets(buffer, BUFSIZ, fp) )
150 tmp = strchr(buffer, '#');
152 tmp = strchr(buffer, ';');
158 if(strlen(line) == 0) continue;
160 // Pass regex over line
161 if( RunRegex( &gItemFile_Regex, line, 5, matches, NULL) ) {
162 fprintf(stderr, "Syntax error on line %i of item file '%s'\n", lineNum, gsItemListFile);
167 type = line + matches[1].rm_so; line[ matches[1].rm_eo ] = '\0';
168 num = atoi( line + matches[2].rm_so );
169 price = atoi( line + matches[3].rm_so );
170 desc = line + matches[4].rm_so;
172 printf("Item '%s' - %i cents, %s:%i\n", desc, price, type, num);
175 for( i = 0; i < giNumHandlers; i ++ )
177 if( strcmp(type, gaHandlers[i]->Name) == 0 ) {
178 handler = gaHandlers[i];
184 fprintf(stderr, "Unknow item type '%s' on line %i (%s)\n", type, lineNum, desc);
188 for( i = 0; i < numItems; i ++ )
190 if( items[i].Handler != handler ) continue;
191 if( items[i].ID != num ) continue;
193 printf("Redefinition of %s:%i, updated\n", handler->Name, num);
194 items[i].Price = price;
196 items[i].Name = strdup(desc);
199 if( i < numItems ) continue;
201 items = realloc( items, (numItems + 1)*sizeof(items[0]) );
202 items[numItems].Handler = handler;
203 items[numItems].ID = num;
204 items[numItems].Price = price;
205 items[numItems].Name = strdup(desc);
206 items[numItems].bHidden = (line[0] == '-');
219 giNumItems = numItems;
222 gItems_LastUpdated = time(NULL);
226 * \brief Update the item file from the internal database
228 void Items_UpdateFile(void)
235 regmatch_t matches[5];
236 char **line_comments;
240 fp = fopen(gsItemListFile, "r");
242 fprintf(stderr, "Unable to open item file '%s'\n", gsItemListFile);
243 perror("Unable to open item file");
248 while( fgets(buffer, BUFSIZ, fp) )
253 line_comments = malloc(lineNum * sizeof(char*));
254 line_items = malloc(lineNum * sizeof(int));
258 fseek(fp, 0, SEEK_SET);
259 while( fgets(buffer, BUFSIZ, fp) )
261 char *hashPos, *semiPos;
269 line_items[lineNum-1] = -1;
270 line_comments[lineNum-1] = NULL;
273 hashPos = strchr(buffer, '#');
274 semiPos = strchr(buffer, ';');
275 if( hashPos && semiPos ) {
276 if( hashPos < semiPos )
277 line_comments[lineNum-1] = strdup(hashPos);
280 line_comments[lineNum-1] = strdup(hashPos);
283 line_comments[lineNum-1] = strdup(semiPos);
285 if(hashPos) *hashPos = '\0';
286 if(semiPos) *semiPos = '\0';
290 if(strlen(line) == 0) continue;
292 // Pass regex over line
293 if( RunRegex( &gItemFile_Regex, line, 5, matches, NULL) ) {
294 fprintf(stderr, "Syntax error on line %i of item file '%s'\n", lineNum, gsItemListFile);
299 type = line + matches[1].rm_so; line[ matches[1].rm_eo ] = '\0';
300 num = atoi( line + matches[2].rm_so );
304 for( i = 0; i < giNumHandlers; i ++ )
306 if( strcmp(type, gaHandlers[i]->Name) == 0 ) {
307 handler = gaHandlers[i];
312 fprintf(stderr, "Warning: Unknown item type '%s' on line %i\n", type, lineNum);
316 for( i = 0; i < giNumItems; i ++ )
318 if( gaItems[i].Handler != handler ) continue;
319 if( gaItems[i].ID != num ) continue;
321 line_items[lineNum-1] = i;
324 if( i >= giNumItems ) {
331 //fp = fopen("items.cfg.new", "w"); // DEBUG: Don't kill the real item file until debugged
332 fp = fopen(gsItemListFile, "w");
336 int done_items[giNumItems];
337 memset(done_items, 0, sizeof(done_items));
340 for( i = 0; i < lineNum; i ++ )
342 if( line_items[i] != -1 ) {
343 tItem *item = &gaItems[ line_items[i] ];
345 if( done_items[ line_items[i] ] ) {
346 fprintf(fp, "; DUP -");
348 done_items[ line_items[i] ] = 1;
353 fprintf(fp, "%s\t%i\t%i\t%s\t",
354 item->Handler->Name, item->ID, item->Price, item->Name
358 if( line_comments[i] ) {
359 fprintf(fp, "%s", line_comments[i]);
360 free( line_comments[i] );
367 for( i = 0; i < giNumItems; i ++ )
369 tItem *item = &gaItems[i];
370 if( done_items[i] ) continue ;
375 fprintf(fp, "%s\t%i\t%i\t%s\n",
376 item->Handler->Name, item->ID, item->Price, item->Name
381 free( line_comments );
387 char *trim(char *__str)
392 while( isspace(*__str) )
397 while( i-- && isspace(__str[i]) ) {