Adding support for spacers
[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/inotify.h>
17 #include <signal.h>
18 #include <sys/fcntl.h>
19 #include <unistd.h>
20
21 // === IMPORTS ===
22 extern tHandler gCoke_Handler;
23 extern tHandler gSnack_Handler;
24 extern tHandler gDoor_Handler;
25
26 // === PROTOTYPES ===
27 void    Init_Handlers(void);
28 void    ItemList_Changed(int signum);
29 void    Load_Itemlist(void);
30 char    *trim(char *__str);
31
32 // === GLOBALS ===
33  int    giNumItems = 0;
34 tItem   *gaItems = NULL;
35 tHandler        gPseudo_Handler = {Name:"pseudo"};
36 tHandler        gSpacer_Handler = {Name:"spacer"};
37 tHandler        *gaHandlers[] = {&gSpacer_Handler, &gPseudo_Handler, &gCoke_Handler, &gSnack_Handler, &gDoor_Handler};
38  int    giNumHandlers = sizeof(gaHandlers)/sizeof(gaHandlers[0]);
39 char    *gsItemListFile = DEFAULT_ITEM_FILE;
40 #if USE_INOTIFY
41  int    giItem_INotifyFD;
42 #endif
43
44 // === CODE ===
45 void Init_Handlers()
46 {
47          int    i;
48         for( i = 0; i < giNumHandlers; i ++ )
49         {
50                 if( gaHandlers[i]->Init )
51                         gaHandlers[i]->Init(0, NULL);   // TODO: Arguments
52         }
53         
54         // Use inotify to watch the snack config file
55         #if USE_INOTIFY
56         {
57                 int oflags;
58                 
59                 giItem_INotifyFD = inotify_init();
60                 inotify_add_watch(giItem_INotifyFD, gsItemListFile, IN_MODIFY);
61                 
62                 // Handle SIGIO
63                 signal(SIGIO, &ItemList_Changed);
64                 
65                 // Fire SIGIO when data is ready on the FD
66                 fcntl(giItem_INotifyFD, F_SETOWN, getpid());
67                 oflags = fcntl(giItem_INotifyFD, F_GETFL);
68                 fcntl(giItem_INotifyFD, F_SETFL, oflags | FASYNC);
69         }
70         #endif
71 }
72
73 #if USE_INOTIFY
74 void ItemList_Changed(int signum)
75 {
76         char    buf[512];
77         read(giItem_INotifyFD, buf, 512);
78         Load_Itemlist();
79         
80         signum = 0;     // Shut up GCC
81 }
82 #endif
83
84 /**
85  * \brief Read the item list from disk
86  */
87 void Load_Itemlist(void)
88 {
89         FILE    *fp = fopen(gsItemListFile, "r");
90         char    buffer[BUFSIZ];
91         char    *line;
92          int    lineNum = 0;
93          int    i;
94         regex_t regex;
95         regmatch_t      matches[5];
96         
97         i = regcomp(&regex, "^-?([a-zA-Z][a-zA-Z]*)\\s+([0-9]+)\\s+([0-9]+)\\s+(.*)", REG_EXTENDED);
98         if( i )
99         {
100                 size_t  len = regerror(i, &regex, NULL, 0);
101                 char    *errorStr = malloc(len);
102                 regerror(i, &regex, errorStr, len);
103                 fprintf(stderr, "Rexex compilation failed - %s\n", errorStr);
104                 free(errorStr);
105                 exit(-1);
106         }
107
108         // Error check
109         if(!fp) {
110                 fprintf(stderr, "Unable to open item file '%s'\n", gsItemListFile);
111                 perror("Unable to open item file");
112         }
113         
114         while( fgets(buffer, BUFSIZ, fp) )
115         {
116                 char    *tmp;
117                 char    *type, *desc;
118                  int    num, price;
119                 tHandler        *handler;
120
121                 lineNum ++;
122
123                 // Remove comments
124                 tmp = strchr(buffer, '#');
125                 if(tmp) *tmp = '\0';
126                 tmp = strchr(buffer, ';');
127                 if(tmp) *tmp = '\0';
128                 
129                 // Trim whitespace
130                 line = trim(buffer);
131                 
132                 if(strlen(line) == 0)   continue;
133                 
134                 // Pass regex over line
135                 if( (i = regexec(&regex, line, 5, matches, 0)) ) {
136                         size_t  len = regerror(i, &regex, NULL, 0);
137                         char    *errorStr = malloc(len);
138                         regerror(i, &regex, errorStr, len);
139                         fprintf(stderr, "Syntax error on line %i of item file '%s'\n%s", lineNum, gsItemListFile, errorStr);
140                         free(errorStr);
141                         exit(-1);
142                 }
143
144                 // Read line data
145                 type  = line + matches[1].rm_so;        line[ matches[1].rm_eo ] = '\0';
146                 num   = atoi( line + matches[2].rm_so );
147                 price = atoi( line + matches[3].rm_so );
148                 desc  = line + matches[4].rm_so;        
149
150                 printf("Item '%s' - %i cents, %s:%i\n", desc, price, type, num);
151
152                 handler = NULL;
153                 for( i = 0; i < giNumHandlers; i ++ )
154                 {
155                         if( strcmp(type, gaHandlers[i]->Name) == 0 ) {
156                                 handler = gaHandlers[i];
157                                 break;
158                         }
159                 }
160
161                 if( !handler ) {
162                         fprintf(stderr, "Unknow item type '%s' on line %i (%s)\n", type, lineNum, desc);
163                         continue ;
164                 }
165
166                 for( i = 0; i < giNumItems; i ++ )
167                 {
168                         if( gaItems[i].Handler != handler )     continue;
169                         if( gaItems[i].ID != num )      continue;
170
171                         printf("Redefinition of %s:%i, updated\n", handler->Name, num);
172                         gaItems[i].Price = price;
173                         free(gaItems[i].Name);
174                         gaItems[i].Name = strdup(desc);
175                         break;
176                 }
177                 if( i < giNumItems )    continue;
178
179                 gaItems = realloc( gaItems, (giNumItems + 1)*sizeof(gaItems[0]) );
180                 gaItems[giNumItems].Handler = handler;
181                 gaItems[giNumItems].ID = num;
182                 gaItems[giNumItems].Price = price;
183                 gaItems[giNumItems].Name = strdup(desc);
184                 gaItems[giNumItems].bHidden = (line[0] == '-');
185                 giNumItems ++;
186         }       
187 }
188
189 char *trim(char *__str)
190 {
191         char    *ret;
192          int    i;
193         
194         while( isspace(*__str) )
195                 __str++;
196         ret = __str;
197
198         i = strlen(ret);
199         while( i-- && isspace(__str[i]) ) {
200                 __str[i] = '\0';
201         }
202
203         return ret;
204 }

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