e48ab868a7a50278cd0479a7c0933566ee69b6a0
[tpg/opendispense2.git] / src / server / itemdb.c
1 /*
2  * OpenDispense 2 
3  * UCC (University [of WA] Computer Club) Electronic Accounting System
4  *
5  * itemdb.c - Dispense Item Databse
6  *
7  * This file is licenced under the 3-clause BSD Licence. See the file COPYING
8  * for full details.
9  */
10 #include <stdlib.h>
11 #include <stdio.h>
12 #include <string.h>
13 #include <ctype.h>
14 #include "common.h"
15 #include <regex.h>
16 #include <sys/stat.h>
17 #include <time.h>
18
19 // === IMPORTS ===
20 extern tHandler gCoke_Handler;
21 extern tHandler gSnack_Handler;
22 extern tHandler gDoor_Handler;
23
24 // === PROTOTYPES ===
25 void    Init_Handlers(void);
26 void    Load_Itemlist(void);
27 void    Items_ReadFromFile(void);
28 char    *trim(char *__str);
29
30 // === GLOBALS ===
31  int    giNumItems = 0;
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;
38 #if USE_INOTIFY
39  int    giItem_INotifyFD;
40 #endif
41 regex_t gItemFile_Regex;
42
43 // === CODE ===
44 void Init_Handlers()
45 {
46          int    i;
47         for( i = 0; i < giNumHandlers; i ++ )
48         {
49                 if( gaHandlers[i]->Init )
50                         gaHandlers[i]->Init(0, NULL);   // TODO: Arguments
51         }
52         
53         // Use inotify to watch the snack config file
54         #if USE_INOTIFY
55         {
56                 int oflags;
57                 
58                 giItem_INotifyFD = inotify_init();
59                 inotify_add_watch(giItem_INotifyFD, gsItemListFile, IN_MODIFY);
60                 
61                 // Handle SIGIO
62                 signal(SIGIO, &ItemList_Changed);
63                 
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);
68         }
69         #endif
70 }
71
72 #if USE_INOTIFY
73 void ItemList_Changed(int signum)
74 {
75         char    buf[512];
76         read(giItem_INotifyFD, buf, 512);
77         Load_Itemlist();
78         
79         signum = 0;     // Shut up GCC
80 }
81 #endif
82
83 /**
84  * \brief Read the initiali item list
85  */
86 void Load_Itemlist(void)
87 {
88          int    rv;
89         rv = regcomp(&gItemFile_Regex, "^-?([a-zA-Z][a-zA-Z]*)\\s+([0-9]+)\\s+([0-9]+)\\s+(.*)", REG_EXTENDED);
90         if( rv )
91         {
92                 size_t  len = regerror(rv, &gItemFile_Regex, NULL, 0);
93                 char    errorStr[len];
94                 regerror(rv, &gItemFile_Regex, errorStr, len);
95                 fprintf(stderr, "Rexex compilation failed - %s\n", errorStr);
96                 exit(-1);
97         }
98         
99         Items_ReadFromFile();
100         
101         // Re-read the item file periodically
102         // TODO: Be less lazy here and check the timestamp
103         AddPeriodicFunction( Items_ReadFromFile );
104 }
105 /**
106  * \brief Read the item list from disk
107  */
108 void Items_ReadFromFile(void)
109 {
110         FILE    *fp;
111         char    buffer[BUFSIZ];
112         char    *line;
113          int    lineNum = 0;
114          int    i, numItems = 0;
115         tItem   *items = NULL;
116         regmatch_t      matches[5];
117
118         if( gItems_LastUpdated ) 
119         {
120                 struct stat buf;
121                 if( stat(gsItemListFile, &buf) ) {
122                         fprintf(stderr, "Unable to stat() item file '%s'\n", gsItemListFile);
123                         return ;
124                 }
125                 
126                 // Check if the update is needed
127                 if( gItems_LastUpdated > buf.st_mtime )
128                         return ;
129         }
130
131         // Error check
132         fp = fopen(gsItemListFile, "r");
133         if(!fp) {
134                 fprintf(stderr, "Unable to open item file '%s'\n", gsItemListFile);
135                 perror("Unable to open item file");
136         }
137         
138         while( fgets(buffer, BUFSIZ, fp) )
139         {
140                 char    *tmp;
141                 char    *type, *desc;
142                  int    num, price;
143                 tHandler        *handler;
144
145                 lineNum ++;
146
147                 // Remove comments
148                 tmp = strchr(buffer, '#');
149                 if(tmp) *tmp = '\0';
150                 tmp = strchr(buffer, ';');
151                 if(tmp) *tmp = '\0';
152                 
153                 // Trim whitespace
154                 line = trim(buffer);
155                 
156                 if(strlen(line) == 0)   continue;
157                 
158                 // Pass regex over line
159                 if( RunRegex( &gItemFile_Regex, line, 5, matches, NULL) ) {
160                         fprintf(stderr, "Syntax error on line %i of item file '%s'\n", lineNum, gsItemListFile);
161                         return ;
162                 }
163
164                 // Read line data
165                 type  = line + matches[1].rm_so;        line[ matches[1].rm_eo ] = '\0';
166                 num   = atoi( line + matches[2].rm_so );
167                 price = atoi( line + matches[3].rm_so );
168                 desc  = line + matches[4].rm_so;        
169
170                 printf("Item '%s' - %i cents, %s:%i\n", desc, price, type, num);
171
172                 handler = NULL;
173                 for( i = 0; i < giNumHandlers; i ++ )
174                 {
175                         if( strcmp(type, gaHandlers[i]->Name) == 0 ) {
176                                 handler = gaHandlers[i];
177                                 break;
178                         }
179                 }
180
181                 if( !handler ) {
182                         fprintf(stderr, "Unknow item type '%s' on line %i (%s)\n", type, lineNum, desc);
183                         continue ;
184                 }
185
186                 for( i = 0; i < numItems; i ++ )
187                 {
188                         if( items[i].Handler != handler )       continue;
189                         if( items[i].ID != num )        continue;
190
191                         printf("Redefinition of %s:%i, updated\n", handler->Name, num);
192                         items[i].Price = price;
193                         free(items[i].Name);
194                         items[i].Name = strdup(desc);
195                         break;
196                 }
197                 if( i < numItems )      continue;
198
199                 items = realloc( items, (numItems + 1)*sizeof(items[0]) );
200                 items[numItems].Handler = handler;
201                 items[numItems].ID = num;
202                 items[numItems].Price = price;
203                 items[numItems].Name = strdup(desc);
204                 items[numItems].bHidden = (line[0] == '-');
205                 numItems ++;
206         }
207         
208         // Clean up old
209         if( giNumItems )
210         {
211                 giNumItems = 0;
212                 free(gaItems);
213                 gaItems = NULL;
214         }
215         
216         // Replace with new
217         giNumItems = numItems;
218         gaItems = items;
219         
220         gItems_LastUpdated = time(NULL);
221 }
222
223 char *trim(char *__str)
224 {
225         char    *ret;
226          int    i;
227         
228         while( isspace(*__str) )
229                 __str++;
230         ret = __str;
231
232         i = strlen(ret);
233         while( i-- && isspace(__str[i]) ) {
234                 __str[i] = '\0';
235         }
236
237         return ret;
238 }

UCC git Repository :: git.ucc.asn.au