Fixes fixes fixes, init.d script
[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                 return ;
137         }
138         
139         while( fgets(buffer, BUFSIZ, fp) )
140         {
141                 char    *tmp;
142                 char    *type, *desc;
143                  int    num, price;
144                 tHandler        *handler;
145
146                 lineNum ++;
147
148                 // Remove comments
149                 tmp = strchr(buffer, '#');
150                 if(tmp) *tmp = '\0';
151                 tmp = strchr(buffer, ';');
152                 if(tmp) *tmp = '\0';
153                 
154                 // Trim whitespace
155                 line = trim(buffer);
156                 
157                 if(strlen(line) == 0)   continue;
158                 
159                 // Pass regex over line
160                 if( RunRegex( &gItemFile_Regex, line, 5, matches, NULL) ) {
161                         fprintf(stderr, "Syntax error on line %i of item file '%s'\n", lineNum, gsItemListFile);
162                         return ;
163                 }
164
165                 // Read line data
166                 type  = line + matches[1].rm_so;        line[ matches[1].rm_eo ] = '\0';
167                 num   = atoi( line + matches[2].rm_so );
168                 price = atoi( line + matches[3].rm_so );
169                 desc  = line + matches[4].rm_so;        
170
171                 printf("Item '%s' - %i cents, %s:%i\n", desc, price, type, num);
172
173                 handler = NULL;
174                 for( i = 0; i < giNumHandlers; i ++ )
175                 {
176                         if( strcmp(type, gaHandlers[i]->Name) == 0 ) {
177                                 handler = gaHandlers[i];
178                                 break;
179                         }
180                 }
181
182                 if( !handler ) {
183                         fprintf(stderr, "Unknow item type '%s' on line %i (%s)\n", type, lineNum, desc);
184                         continue ;
185                 }
186
187                 for( i = 0; i < numItems; i ++ )
188                 {
189                         if( items[i].Handler != handler )       continue;
190                         if( items[i].ID != num )        continue;
191
192                         printf("Redefinition of %s:%i, updated\n", handler->Name, num);
193                         items[i].Price = price;
194                         free(items[i].Name);
195                         items[i].Name = strdup(desc);
196                         break;
197                 }
198                 if( i < numItems )      continue;
199
200                 items = realloc( items, (numItems + 1)*sizeof(items[0]) );
201                 items[numItems].Handler = handler;
202                 items[numItems].ID = num;
203                 items[numItems].Price = price;
204                 items[numItems].Name = strdup(desc);
205                 items[numItems].bHidden = (line[0] == '-');
206                 numItems ++;
207         }
208         
209         // Clean up old
210         if( giNumItems )
211         {
212                 giNumItems = 0;
213                 free(gaItems);
214                 gaItems = NULL;
215         }
216         
217         // Replace with new
218         giNumItems = numItems;
219         gaItems = items;
220         
221         gItems_LastUpdated = time(NULL);
222 }
223
224 char *trim(char *__str)
225 {
226         char    *ret;
227          int    i;
228         
229         while( isspace(*__str) )
230                 __str++;
231         ret = __str;
232
233         i = strlen(ret);
234         while( i-- && isspace(__str[i]) ) {
235                 __str[i] = '\0';
236         }
237
238         return ret;
239 }

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