#include <ctype.h>
#include "common.h"
#include <regex.h>
-#include <sys/inotify.h>
-#include <signal.h>
-#include <sys/fcntl.h>
-#include <unistd.h>
+#include <sys/stat.h>
+#include <time.h>
// === IMPORTS ===
extern tHandler gCoke_Handler;
// === PROTOTYPES ===
void Init_Handlers(void);
-void ItemList_Changed(int signum);
void Load_Itemlist(void);
+void Items_ReadFromFile(void);
char *trim(char *__str);
// === GLOBALS ===
int giNumItems = 0;
tItem *gaItems = NULL;
+time_t gItems_LastUpdated;
tHandler gPseudo_Handler = {Name:"pseudo"};
-tHandler gSpacer_Handler = {Name:"spacer"};
-tHandler *gaHandlers[] = {&gSpacer_Handler, &gPseudo_Handler, &gCoke_Handler, &gSnack_Handler, &gDoor_Handler};
+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()
#endif
/**
- * \brief Read the item list from disk
+ * \brief Read the initiali 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-Z]*)\\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) )
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
continue ;
}
+ for( i = 0; i < numItems; i ++ )
+ {
+ if( items[i].Handler != handler ) continue;
+ if( items[i].ID != num ) continue;
+
+ printf("Redefinition of %s:%i, updated\n", handler->Name, num);
+ 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;
+ 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;
+ }
+
+ // 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;
+
+ 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);
- gaItems[giNumItems].bHidden = (line[0] == '-');
- giNumItems ++;
- }
+ if( i >= giNumItems ) {
+ continue;
+ }
+ }
+
+ fclose(fp);
+
+ fp = fopen("items.cfg.new", "w"); // DEBUG: Don't kill the real item file until debugged
+
+ // 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;
+ 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 ;
+
+ 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;