e63bb65df618d8a40c85e7428347a3050e53cf0d
[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        *gaHandlers[] = {&gPseudo_Handler, &gCoke_Handler, &gSnack_Handler, &gDoor_Handler};
37  int    giNumHandlers = sizeof(gaHandlers)/sizeof(gaHandlers[0]);
38 char    *gsItemListFile = DEFAULT_ITEM_FILE;
39 #if USE_INOTIFY
40  int    giItem_INotifyFD;
41 #endif
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 item list from disk
85  */
86 void Load_Itemlist(void)
87 {
88         FILE    *fp = fopen(gsItemListFile, "r");
89         char    buffer[BUFSIZ];
90         char    *line;
91          int    lineNum = 0;
92          int    i;
93         regex_t regex;
94         regmatch_t      matches[5];
95         
96         i = regcomp(&regex, "^-?([a-zA-Z][a-zA-Z]*)\\s+([0-9]+)\\s+([0-9]+)\\s+(.*)", REG_EXTENDED);
97         if( i )
98         {
99                 size_t  len = regerror(i, &regex, NULL, 0);
100                 char    *errorStr = malloc(len);
101                 regerror(i, &regex, errorStr, len);
102                 fprintf(stderr, "Rexex compilation failed - %s\n", errorStr);
103                 free(errorStr);
104                 exit(-1);
105         }
106
107         // Error check
108         if(!fp) {
109                 fprintf(stderr, "Unable to open item file '%s'\n", gsItemListFile);
110                 perror("Unable to open item file");
111         }
112         
113         while( fgets(buffer, BUFSIZ, fp) )
114         {
115                 char    *tmp;
116                 char    *type, *desc;
117                  int    num, price;
118                 tHandler        *handler;
119
120                 lineNum ++;
121
122                 // Remove comments
123                 tmp = strchr(buffer, '#');
124                 if(tmp) *tmp = '\0';
125                 tmp = strchr(buffer, ';');
126                 if(tmp) *tmp = '\0';
127                 
128                 // Trim whitespace
129                 line = trim(buffer);
130                 
131                 if(strlen(line) == 0)   continue;
132                 
133                 // Pass regex over line
134                 if( (i = regexec(&regex, line, 5, matches, 0)) ) {
135                         size_t  len = regerror(i, &regex, NULL, 0);
136                         char    *errorStr = malloc(len);
137                         regerror(i, &regex, errorStr, len);
138                         fprintf(stderr, "Syntax error on line %i of item file '%s'\n%s", lineNum, gsItemListFile, errorStr);
139                         free(errorStr);
140                         exit(-1);
141                 }
142
143                 // Read line data
144                 type  = line + matches[1].rm_so;        line[ matches[1].rm_eo ] = '\0';
145                 num   = atoi( line + matches[2].rm_so );
146                 price = atoi( line + matches[3].rm_so );
147                 desc  = line + matches[4].rm_so;        
148
149                 printf("Item '%s' - %i cents, %s:%i\n", desc, price, type, num);
150
151                 handler = NULL;
152                 for( i = 0; i < giNumHandlers; i ++ )
153                 {
154                         if( strcmp(type, gaHandlers[i]->Name) == 0 ) {
155                                 handler = gaHandlers[i];
156                                 break;
157                         }
158                 }
159
160                 if( !handler ) {
161                         fprintf(stderr, "Unknow item type '%s' on line %i (%s)\n", type, lineNum, desc);
162                         continue ;
163                 }
164
165                 for( i = 0; i < giNumItems; i ++ )
166                 {
167                         if( gaItems[i].Handler != handler )     continue;
168                         if( gaItems[i].ID != num )      continue;
169
170                         printf("Redefinition of %s:%i, updated\n", handler->Name, num);
171                         gaItems[i].Price = price;
172                         free(gaItems[i].Name);
173                         gaItems[i].Name = strdup(desc);
174                         break;
175                 }
176                 if( i < giNumItems )    continue;
177
178                 gaItems = realloc( gaItems, (giNumItems + 1)*sizeof(gaItems[0]) );
179                 gaItems[giNumItems].Handler = handler;
180                 gaItems[giNumItems].ID = num;
181                 gaItems[giNumItems].Price = price;
182                 gaItems[giNumItems].Name = strdup(desc);
183                 gaItems[giNumItems].bHidden = (line[0] == '-');
184                 giNumItems ++;
185         }       
186 }
187
188 char *trim(char *__str)
189 {
190         char    *ret;
191          int    i;
192         
193         while( isspace(*__str) )
194                 __str++;
195         ret = __str;
196
197         i = strlen(ret);
198         while( i-- && isspace(__str[i]) ) {
199                 __str[i] = '\0';
200         }
201
202         return ret;
203 }

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