X-Git-Url: https://git.ucc.asn.au/?a=blobdiff_plain;f=src%2Fserver%2Fitemdb.c;h=06c82578a645cf5a01776c280daba4f940b78f6f;hb=008596161453e64354973268125058b49aee60d3;hp=8b494f7dce2a4505cd6af62e291d62077d8bf1e1;hpb=99cf95b138082d1c02705682ad0dfd8b67a27ac4;p=tpg%2Fopendispense2.git diff --git a/src/server/itemdb.c b/src/server/itemdb.c index 8b494f7..06c8257 100644 --- a/src/server/itemdb.c +++ b/src/server/itemdb.c @@ -13,6 +13,10 @@ #include #include "common.h" #include +#include +#include + +#define DUMP_ITEMS 0 // === IMPORTS === extern tHandler gCoke_Handler; @@ -20,16 +24,23 @@ extern tHandler gSnack_Handler; extern tHandler gDoor_Handler; // === PROTOTYPES === +void Init_Handlers(void); void Load_Itemlist(void); +void Items_ReadFromFile(void); char *trim(char *__str); // === GLOBALS === int giNumItems = 0; tItem *gaItems = NULL; -tHandler gPseudo_Handler = {Name:"pseudo"}; +time_t gItems_LastUpdated; +tHandler gPseudo_Handler = {.Name="pseudo"}; tHandler *gaHandlers[] = {&gPseudo_Handler, &gCoke_Handler, &gSnack_Handler, &gDoor_Handler}; int giNumHandlers = sizeof(gaHandlers)/sizeof(gaHandlers[0]); char *gsItemListFile = DEFAULT_ITEM_FILE; +#if USE_INOTIFY + int giItem_INotifyFD; +#endif +regex_t gItemFile_Regex; // === CODE === void Init_Handlers() @@ -40,36 +51,92 @@ void Init_Handlers() if( gaHandlers[i]->Init ) gaHandlers[i]->Init(0, NULL); // TODO: Arguments } + + // Use inotify to watch the snack config file + #if USE_INOTIFY + { + int oflags; + + giItem_INotifyFD = inotify_init(); + inotify_add_watch(giItem_INotifyFD, gsItemListFile, IN_MODIFY); + + // Handle SIGIO + signal(SIGIO, &ItemList_Changed); + + // Fire SIGIO when data is ready on the FD + fcntl(giItem_INotifyFD, F_SETOWN, getpid()); + oflags = fcntl(giItem_INotifyFD, F_GETFL); + fcntl(giItem_INotifyFD, F_SETFL, oflags | FASYNC); + } + #endif } +#if USE_INOTIFY +void ItemList_Changed(int signum) +{ + char buf[512]; + read(giItem_INotifyFD, buf, 512); + Load_Itemlist(); + + signum = 0; // Shut up GCC +} +#endif + /** - * \brief Read the item list from disk + * \brief Read the initial item list */ void Load_Itemlist(void) { - FILE *fp = fopen(gsItemListFile, "r"); + int rv; + rv = regcomp(&gItemFile_Regex, "^-?([a-zA-Z][a-zA-Z]*)\\s+([0-9]+)\\s+([0-9]+)\\s+(.*)", REG_EXTENDED); + if( rv ) + { + size_t len = regerror(rv, &gItemFile_Regex, NULL, 0); + char errorStr[len]; + regerror(rv, &gItemFile_Regex, errorStr, len); + fprintf(stderr, "Rexex compilation failed - %s\n", errorStr); + exit(-1); + } + + Items_ReadFromFile(); + + // Re-read the item file periodically + // TODO: Be less lazy here and check the timestamp + AddPeriodicFunction( Items_ReadFromFile ); +} + +/** + * \brief Read the item list from disk + */ +void Items_ReadFromFile(void) +{ + FILE *fp; char buffer[BUFSIZ]; char *line; int lineNum = 0; - int i; - regex_t regex; + int i, numItems = 0; + tItem *items = NULL; regmatch_t matches[5]; - - i = regcomp(®ex, "^([a-zA-Z][a-zA-Z0-9]*)\\s+([0-9]+)\\s+([0-9]+)\\s+(.*)", REG_EXTENDED); - if( i ) + + if( gItems_LastUpdated ) { - size_t len = regerror(i, ®ex, NULL, 0); - char *errorStr = malloc(len); - regerror(i, ®ex, errorStr, len); - fprintf(stderr, "Rexex compilation failed - %s\n", errorStr); - free(errorStr); - exit(-1); + struct stat buf; + if( stat(gsItemListFile, &buf) ) { + fprintf(stderr, "Unable to stat() item file '%s'\n", gsItemListFile); + return ; + } + + // Check if the update is needed + if( gItems_LastUpdated > buf.st_mtime ) + return ; } // Error check + fp = fopen(gsItemListFile, "r"); if(!fp) { fprintf(stderr, "Unable to open item file '%s'\n", gsItemListFile); perror("Unable to open item file"); + return ; } while( fgets(buffer, BUFSIZ, fp) ) @@ -93,13 +160,9 @@ void Load_Itemlist(void) if(strlen(line) == 0) continue; // Pass regex over line - if( (i = regexec(®ex, line, 5, matches, 0)) ) { - size_t len = regerror(i, ®ex, NULL, 0); - char *errorStr = malloc(len); - regerror(i, ®ex, errorStr, len); - fprintf(stderr, "Syntax error on line %i of item file '%s'\n%s", lineNum, gsItemListFile, errorStr); - free(errorStr); - exit(-1); + if( RunRegex( &gItemFile_Regex, line, 5, matches, NULL) ) { + fprintf(stderr, "Syntax error on line %i of item file '%s'\n", lineNum, gsItemListFile); + return ; } // Read line data @@ -108,7 +171,9 @@ void Load_Itemlist(void) price = atoi( line + matches[3].rm_so ); desc = line + matches[4].rm_so; + #if DUMP_ITEMS printf("Item '%s' - %i cents, %s:%i\n", desc, price, type, num); + #endif handler = NULL; for( i = 0; i < giNumHandlers; i ++ ) @@ -124,28 +189,211 @@ void Load_Itemlist(void) continue ; } + for( i = 0; i < numItems; i ++ ) + { + if( items[i].Handler != handler ) continue; + if( items[i].ID != num ) continue; + + #if DUMP_ITEMS + printf("Redefinition of %s:%i, updated\n", handler->Name, num); + #endif + items[i].Price = price; + free(items[i].Name); + items[i].Name = strdup(desc); + break; + } + if( i < numItems ) continue; + + items = realloc( items, (numItems + 1)*sizeof(items[0]) ); + items[numItems].Handler = handler; + items[numItems].ID = num; + if( gbNoCostMode ) + items[numItems].Price = 0; + else + items[numItems].Price = price; + items[numItems].Name = strdup(desc); + items[numItems].bHidden = (line[0] == '-'); + numItems ++; + } + + // Clean up old + if( giNumItems ) + { + giNumItems = 0; + free(gaItems); + gaItems = NULL; + } + fclose(fp); + + // Replace with new + giNumItems = numItems; + gaItems = items; + + gItems_LastUpdated = time(NULL); +} + +/** + * \brief Update the item file from the internal database + */ +void Items_UpdateFile(void) +{ + FILE *fp; + char buffer[BUFSIZ]; + char *line; + int lineNum = 0; + int i; + regmatch_t matches[5]; + char **line_comments; + int *line_items; + + // Error check + fp = fopen(gsItemListFile, "r"); + if(!fp) { + fprintf(stderr, "Unable to open item file '%s'\n", gsItemListFile); + perror("Unable to open item file"); + return ; + } + + // Count lines + while( fgets(buffer, BUFSIZ, fp) ) + { + lineNum ++; + } + + line_comments = malloc(lineNum * sizeof(char*)); + line_items = malloc(lineNum * sizeof(int)); + + // Parse file + lineNum = 0; + fseek(fp, 0, SEEK_SET); + while( fgets(buffer, BUFSIZ, fp) ) + { + char *hashPos, *semiPos; + char *type; + int num; + tHandler *handler; + + trim(buffer); + + lineNum ++; + line_items[lineNum-1] = -1; + line_comments[lineNum-1] = NULL; + + // Get comments + hashPos = strchr(buffer, '#'); + semiPos = strchr(buffer, ';'); + if( hashPos && semiPos ) { + if( hashPos < semiPos ) + line_comments[lineNum-1] = strdup(hashPos); + } + else if( hashPos ) { + line_comments[lineNum-1] = strdup(hashPos); + } + else if( semiPos ) { + line_comments[lineNum-1] = strdup(semiPos); + } + if(hashPos) *hashPos = '\0'; + if(semiPos) *semiPos = '\0'; + + // Trim whitespace + line = trim(buffer); + if(strlen(line) == 0) continue; + + // Pass regex over line + if( RunRegex( &gItemFile_Regex, line, 5, matches, NULL) ) { + fprintf(stderr, "Syntax error on line %i of item file '%s'\n", lineNum, gsItemListFile); + return ; + } + + // Read line data + type = line + matches[1].rm_so; line[ matches[1].rm_eo ] = '\0'; + num = atoi( line + matches[2].rm_so ); + + // Find handler + handler = NULL; + for( i = 0; i < giNumHandlers; i ++ ) + { + if( strcmp(type, gaHandlers[i]->Name) == 0 ) { + handler = gaHandlers[i]; + break; + } + } + if( !handler ) { + fprintf(stderr, "Warning: Unknown item type '%s' on line %i\n", type, lineNum); + continue ; + } + for( i = 0; i < giNumItems; i ++ ) { if( gaItems[i].Handler != handler ) continue; if( gaItems[i].ID != num ) continue; - - printf("Redefinition of %s:%i, updated\n", handler->Name, num); - gaItems[i].Price = price; - free(gaItems[i].Name); - gaItems[i].Name = strdup(desc); + + line_items[lineNum-1] = i; break; } - if( i < giNumItems ) continue; - - gaItems = realloc( gaItems, (giNumItems + 1)*sizeof(gaItems[0]) ); - gaItems[giNumItems].Handler = handler; - gaItems[giNumItems].ID = num; - gaItems[giNumItems].Price = price; - gaItems[giNumItems].Name = strdup(desc); - giNumItems ++; - } + if( i >= giNumItems ) { + continue; + } + } + + fclose(fp); + + //fp = fopen("items.cfg.new", "w"); // DEBUG: Don't kill the real item file until debugged + fp = fopen(gsItemListFile, "w"); + + // Create new file + { + int done_items[giNumItems]; + memset(done_items, 0, sizeof(done_items)); + + // Existing items + for( i = 0; i < lineNum; i ++ ) + { + if( line_items[i] != -1 ) { + tItem *item = &gaItems[ line_items[i] ]; + + if( done_items[ line_items[i] ] ) { + fprintf(fp, "; DUP -"); + } + done_items[ line_items[i] ] = 1; + + if( item->bHidden ) + fprintf(fp, "-"); + + fprintf(fp, "%s\t%i\t%i\t%s\t", + item->Handler->Name, item->ID, item->Price, item->Name + ); + } + + if( line_comments[i] ) { + fprintf(fp, "%s", line_comments[i]); + free( line_comments[i] ); + } + + fprintf(fp, "\n"); + } + + // New items + for( i = 0; i < giNumItems; i ++ ) + { + tItem *item = &gaItems[i]; + if( done_items[i] ) continue ; + + if( item->bHidden ) + fprintf(fp, "-"); + + fprintf(fp, "%s\t%i\t%i\t%s\n", + item->Handler->Name, item->ID, item->Price, item->Name + ); + } + } + + free( line_comments ); + free( line_items ); + fclose(fp); } + char *trim(char *__str) { char *ret;