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 gMembership_Handler = {.Name="membership"};
38 tHandler *gaHandlers[] = {
39 &gPseudo_Handler, &gMembership_Handler,
40 &gCoke_Handler, &gSnack_Handler, &gDoor_Handler
42 int giNumHandlers = sizeof(gaHandlers)/sizeof(gaHandlers[0]);
43 char *gsItemListFile = DEFAULT_ITEM_FILE;
47 regex_t gItemFile_Regex;
52 for( int i = 0; i < giNumHandlers; i ++ )
54 if( gaHandlers[i]->Init )
55 gaHandlers[i]->Init(0, NULL); // TODO: Arguments
58 // Use inotify to watch the snack config file
63 giItem_INotifyFD = inotify_init();
64 inotify_add_watch(giItem_INotifyFD, gsItemListFile, IN_MODIFY);
67 signal(SIGIO, &ItemList_Changed);
69 // Fire SIGIO when data is ready on the FD
70 fcntl(giItem_INotifyFD, F_SETOWN, getpid());
71 oflags = fcntl(giItem_INotifyFD, F_GETFL);
72 fcntl(giItem_INotifyFD, F_SETFL, oflags | FASYNC);
78 void ItemList_Changed(int signum)
81 read(giItem_INotifyFD, buf, 512);
84 signum = 0; // Shut up GCC
89 * \brief Read the initial item list
91 void Load_Itemlist(void)
94 rv = regcomp(&gItemFile_Regex, "^-?([a-zA-Z][a-zA-Z]*)\\s+([0-9]+)\\s+([0-9]+)\\s+(.*)", REG_EXTENDED);
97 size_t len = regerror(rv, &gItemFile_Regex, NULL, 0);
99 regerror(rv, &gItemFile_Regex, errorStr, len);
100 fprintf(stderr, "Rexex compilation failed - %s\n", errorStr);
104 Items_ReadFromFile();
106 // Re-read the item file periodically
107 // TODO: Be less lazy here and check the timestamp
108 AddPeriodicFunction( Items_ReadFromFile );
112 * \brief Read the item list from disk
114 void Items_ReadFromFile(void)
122 regmatch_t matches[5];
124 if( gItems_LastUpdated )
127 if( stat(gsItemListFile, &buf) ) {
128 fprintf(stderr, "Unable to stat() item file '%s'\n", gsItemListFile);
132 // Check if the update is needed
133 if( gItems_LastUpdated > buf.st_mtime )
138 fp = fopen(gsItemListFile, "r");
140 fprintf(stderr, "Unable to open item file '%s'\n", gsItemListFile);
141 perror("Unable to open item file");
145 while( fgets(buffer, BUFSIZ, fp) )
155 tmp = strchr(buffer, '#');
157 tmp = strchr(buffer, ';');
163 if(strlen(line) == 0) continue;
165 // Pass regex over line
166 if( RunRegex( &gItemFile_Regex, line, 5, matches, NULL) ) {
167 fprintf(stderr, "Syntax error on line %i of item file '%s'\n", lineNum, gsItemListFile);
172 type = line + matches[1].rm_so; line[ matches[1].rm_eo ] = '\0';
173 num = atoi( line + matches[2].rm_so );
174 price = atoi( line + matches[3].rm_so );
175 desc = line + matches[4].rm_so;
178 printf("Item '%s' - %i cents, %s:%i\n", desc, price, type, num);
182 for( i = 0; i < giNumHandlers; i ++ )
184 if( strcmp(type, gaHandlers[i]->Name) == 0 ) {
185 handler = gaHandlers[i];
191 fprintf(stderr, "Unknow item type '%s' on line %i (%s)\n", type, lineNum, desc);
195 for( i = 0; i < numItems; i ++ )
197 if( items[i].Handler != handler ) continue;
198 if( items[i].ID != num ) continue;
201 printf("Redefinition of %s:%i, updated\n", handler->Name, num);
203 items[i].Price = price;
205 items[i].Name = strdup(desc);
208 if( i < numItems ) continue;
210 items = realloc( items, (numItems + 1)*sizeof(items[0]) );
211 items[numItems].Handler = handler;
212 items[numItems].ID = num;
214 items[numItems].Price = 0;
216 items[numItems].Price = price;
217 items[numItems].Name = strdup(desc);
218 items[numItems].bHidden = (line[0] == '-');
232 giNumItems = numItems;
235 gItems_LastUpdated = time(NULL);
239 * \brief Update the item file from the internal database
241 void Items_UpdateFile(void)
248 regmatch_t matches[5];
249 char **line_comments;
253 fp = fopen(gsItemListFile, "r");
255 fprintf(stderr, "Unable to open item file '%s'\n", gsItemListFile);
256 perror("Unable to open item file");
261 while( fgets(buffer, BUFSIZ, fp) )
266 line_comments = malloc(lineNum * sizeof(char*));
267 line_items = malloc(lineNum * sizeof(int));
271 fseek(fp, 0, SEEK_SET);
272 while( fgets(buffer, BUFSIZ, fp) )
274 char *hashPos, *semiPos;
282 line_items[lineNum-1] = -1;
283 line_comments[lineNum-1] = NULL;
286 hashPos = strchr(buffer, '#');
287 semiPos = strchr(buffer, ';');
288 if( hashPos && semiPos ) {
289 if( hashPos < semiPos )
290 line_comments[lineNum-1] = strdup(hashPos);
293 line_comments[lineNum-1] = strdup(hashPos);
296 line_comments[lineNum-1] = strdup(semiPos);
298 if(hashPos) *hashPos = '\0';
299 if(semiPos) *semiPos = '\0';
303 if(strlen(line) == 0) continue;
305 // Pass regex over line
306 if( RunRegex( &gItemFile_Regex, line, 5, matches, NULL) ) {
307 fprintf(stderr, "Syntax error on line %i of item file '%s'\n", lineNum, gsItemListFile);
312 type = line + matches[1].rm_so; line[ matches[1].rm_eo ] = '\0';
313 num = atoi( line + matches[2].rm_so );
317 for( i = 0; i < giNumHandlers; i ++ )
319 if( strcmp(type, gaHandlers[i]->Name) == 0 ) {
320 handler = gaHandlers[i];
325 fprintf(stderr, "Warning: Unknown item type '%s' on line %i\n", type, lineNum);
329 for( i = 0; i < giNumItems; i ++ )
331 if( gaItems[i].Handler != handler ) continue;
332 if( gaItems[i].ID != num ) continue;
334 line_items[lineNum-1] = i;
337 if( i >= giNumItems ) {
344 //fp = fopen("items.cfg.new", "w"); // DEBUG: Don't kill the real item file until debugged
345 fp = fopen(gsItemListFile, "w");
349 int done_items[giNumItems];
350 memset(done_items, 0, sizeof(done_items));
353 for( i = 0; i < lineNum; i ++ )
355 if( line_items[i] != -1 ) {
356 tItem *item = &gaItems[ line_items[i] ];
358 if( done_items[ line_items[i] ] ) {
359 fprintf(fp, "; DUP -");
361 done_items[ line_items[i] ] = 1;
366 fprintf(fp, "%s\t%i\t%i\t%s\t",
367 item->Handler->Name, item->ID, item->Price, item->Name
371 if( line_comments[i] ) {
372 fprintf(fp, "%s", line_comments[i]);
373 free( line_comments[i] );
380 for( i = 0; i < giNumItems; i ++ )
382 tItem *item = &gaItems[i];
383 if( done_items[i] ) continue ;
388 fprintf(fp, "%s\t%i\t%i\t%s\n",
389 item->Handler->Name, item->ID, item->Price, item->Name
394 free( line_comments );
400 char *trim(char *__str)
405 while( isspace(*__str) )
410 while( i-- && isspace(__str[i]) ) {