]> git.neil.brown.name Git - TABS.git/blob - tools/attr.c
Terminal Alocation and Booking System - June 2006
[TABS.git] / tools / attr.c
1 #include        "../db_client/db_client.h"
2 #include        "../lib/skip.h"
3 #include        "../lib/dlink.h"
4
5 char Digits[] = "0123456789";
6 /*
7  * set up the attribute info
8  *
9  * we read a file listing attributes, implications
10  * and general config entries
11  * building a list
12  * this is compared with the database and changes made
13  * if necessary
14  *
15  * the file contains:
16  *
17  * attributes defines:   nnn:name type
18  * implications:    day-day time-time dow-dow attr !attr -> new
19  * config:          name=value
20  *
21  * config entries are checked and set whenever they are seen
22  * attributes and implications are stored in tables
23  * and then compared against the database
24  *
25  */
26
27 typedef struct attr {
28     entityid num;
29     char *name;
30 } *attr;
31
32 static int attr_cmp_num(attr a, attr b, int *np)
33 {
34         int n;
35         if (b != NULL)
36                 n = b->num;
37         else n = *np;
38         return a->num - n;
39 }
40
41 static int attr_cmp_name(attr a, attr b, char *n)
42 {
43         if (b!=NULL)
44                 n = b->name;
45         return strcmp(a->name, n);
46 }
47
48 static void attr_free(attr a)
49 {
50         free(a->name);
51         free(a);
52 }
53
54 void *nums, *names;
55 void attr_store(int num, char *name)
56 {
57         attr a = (attr)malloc(sizeof(struct attr));
58         a->num = num;
59         a->name = name;
60         skip_insert(nums, a);
61         skip_insert(names, a);
62 }
63
64 void attr_init()
65 {
66         nums = skip_new(attr_cmp_num, attr_free, NULL);
67         names = skip_new(attr_cmp_name, NULL, NULL);
68 }
69
70 int attr_find(char *name)
71 {
72         attr *ap = skip_search(names, name);
73         if (ap)
74                 return (*ap)->num;
75         else return -1;
76 }
77
78
79 void attr_update(int check)
80 {
81         /* first make sure each name we know about is set
82          * then check all other numbers an clear them
83          */
84         attr *ap;
85
86         for (ap = skip_first(nums); ap ; ap=skip_next(ap))
87         {
88                 attr a = *ap;
89                 char *nam;
90                 nam = get_mappingchar(a->num, M_ATTRIBUTE);
91                 if (nam == NULL || strcmp(nam, a->name)!= 0)
92                 {
93                         /* need to change name
94                          * first make sure name not in use
95                          */
96                         entityid other;
97                         other = get_mappingint(a->name, M_ATTRIBUTE);
98                         if (other>=0)
99                         {
100                                 if (other == a->num)
101                                         printf("WARNING: mapping confusion for %d %s\n",
102                                                a->num, a->name);
103                                 else
104                                 {
105                                         make_mapping(other, "", M_ATTRIBUTE);
106                                 }
107                         }
108                         if (make_mapping(a->num, a->name, M_ATTRIBUTE) != 0)
109                                 fprintf(stderr,"attr: failed to make mapping %d -> %s\n",
110                                         a->num, a->name);
111                 }
112                 if (nam) free(nam);
113         }
114         /* don't worry about others yet FIXME */
115 }
116
117 char *attr_add(char *line, attr_type_desc *at_type)
118 {
119         /* it has been deduced that this line is an
120          * attribute line, probably because it has a colon.
121          * parse out the number, name and type(s) as
122          *  num:name type [type]
123          *
124          * if this does not conflict with list so far,
125          * store it and set appropriate bits in
126          * at_type
127          *
128          * types are: lab optional open closed available bookable reusable firmalloc
129          *  open et.al can only appear on one attribute.
130          */
131
132         int digits;
133         char *name;
134         int num;
135         char *cp;
136         attr *a;
137
138         digits = strspn(line, Digits);
139         if (digits<0)
140                 return "no attribute number";
141         num = atoi(line);
142         if (num < 0 || num > BITINDMAX)
143                 return "bad attribute number";
144         if (line[digits] != ':')
145                 return "colon not found after number";
146
147         cp = line + digits+1;
148         while (*cp == ' ' || *cp == '\t' ) cp++;
149         if (!*cp)
150                 return "attribute name missing";
151         name = cp;
152         while (*cp && *cp != ' ' && *cp != '\t')
153                 cp++;
154         name = strndup(name, cp-name);
155         while (*cp == ' ' || *cp == '\t') cp++;
156
157         set_a_bit(&at_type->all, num);
158         set_a_bit(&at_type->mandatory, num);
159         while (*cp) /* looking at a type */
160         {
161                 char *type = cp;
162                 int *ip = NULL;
163                 while (*cp && *cp != ' ' && *cp != '\t')
164                         cp++;
165                 type = strndup(type, cp-type);
166                 while (*cp == ' ' || *cp == '\t') cp++;
167
168                 if (strcmp(type, "lab")==0)
169                         set_a_bit(&at_type->lab, num);
170                 else if (strcmp(type, "optional")==0)
171                         del_a_bit(&at_type->mandatory, num);
172                 else if (strcmp(type, "open")==0)
173                         ip = &at_type->open;
174                 else if (strcmp(type, "closed")==0)
175                         ip = &at_type->closed;
176                 else if (strcmp(type, "available")==0)
177                         ip = &at_type->available;
178                 else if (strcmp(type, "bookable")==0)
179                         ip = &at_type->bookable;
180                 else if (strcmp(type, "reusable")==0)
181                         ip = &at_type->reusable;
182                 else if (strcmp(type, "firmalloc") == 0)
183                         ip = &at_type->firmalloc;
184                 else
185                         return "unknown attribute type";
186
187                 free(type);
188                 if (ip)
189                 {
190                         if (*ip >= 0)
191                                 return "multiple defination of singular type";
192                         else
193                                 *ip = num;
194                 }
195         }
196
197         if ((a=skip_search(nums, &num)))
198                 return "repeat definition of attribute number";
199         if ((a=skip_search(names, name)))
200                 return "repeat definition of attribute name";
201     
202         attr_store(num, name);
203         return NULL;
204     
205 }
206
207 /*
208  * implicatations are stored in a dlist of implication
209  * structures
210  */
211
212 void *implist;
213 int impcnt = 0;
214
215 char *msg(char *b, char *m, char *a1, char *a2)
216 {
217         sprintf(b,m,a1,a2);
218         return b;
219 }
220
221 char *get_word(char **line)
222 {
223         char *cp  = *line;
224         char *rv;
225         while (*cp == ' ' || *cp == '\t') cp++;
226         rv = cp;
227         while (*cp && *cp != ' ' && *cp != '\t')
228                 cp++;
229         if (*cp) *cp++ = '\0';
230         if (!*rv) rv = NULL;
231         *line = cp;
232         return rv;
233 }
234
235 char *impl_add(char *line)
236 {
237         /* this appears to be an implication,
238          * attempt to extract relevant fields
239          * and add implication to implist
240          *
241          * fields are:
242          *   01jan[-31dec]  startday  endday
243          *   su[-sa]    startdow enddow
244          *   0000-2330  starttime endtime
245          *   xxxxx              included
246          *      !xxxxx          excluded
247          *   -> yyyyy   attribute
248          */
249         static char buf[200];
250         char *w;
251         implication imp, *ip;
252         imp.startday = 0;
253         imp.endday = 366;
254         imp.startdow = 0;
255         imp.enddow = 6;
256         imp.starttime = 0;
257         imp.endtime = 24*60;
258         imp.included = new_desc();
259         imp.excluded = new_desc();
260         imp.attribute = 0;
261
262         while ((w=get_word(&line)) && strcmp(w, "->")!= 0)
263         {
264                 int at;
265
266                 if ((at=attr_find(w)) >= 0)
267                         set_a_bit(&imp.included, at);
268                 else if (*w == '!' && (at=attr_find(w+1))>=0)
269                         set_a_bit(&imp.excluded, at);
270                 else
271                 {
272                         char *type;
273                         char *w2;
274                         int v1, v2;
275                         w2 = strchr(w, '-');
276                         if (w2) *w2++ = '\0';
277                         else w2 = w;
278
279                         if ((v1=dayofweek(w))>=0)
280                         {
281                                 type="Day of Week";
282                                 if ((v2=dayofweek(w2))>=0)
283                                         imp.startdow = v1,
284                                                 imp.enddow = v2;
285                         }
286                         else if ((v1=dayofyear(w))>=0)
287                         {
288                                 type = "Day of Year";
289                                 if ((v2=dayofyear(w2))>= 0)
290                                         imp.startday = v1,
291                                                 imp.endday = v2;
292                         }
293                         else if ((v1=podofday(w))>= 0)
294                         {
295                                 type = "Time of day";
296                                 if (v1 >= 48)
297                                         return msg(buf, "Invalid time: %s", w, NULL);
298                                 if ((v2 = podofday(w2))>= 0)
299                                 {
300                                         if (v2 >= 49)
301                                                 return msg(buf,"Invalid time: %s", w, NULL);
302                                         imp.starttime = v1;
303                                         if (w2==w) imp.endtime = v1+1;
304                                         else imp.endtime = v2-1;
305                                 }
306                         }
307                         else
308                                 return msg(buf, "invalid implicand: %s", w, NULL);
309                         if (v2 < 0)
310                                 return msg(buf, "%s is not a valid %s", w2, type);
311                         if (v2 < v1)
312                                 return msg(buf, "%s preceeds %s", w2, w);
313                 }
314         }
315         if (w == NULL)
316                 return "missing ->";
317         w = get_word(&line);
318         if (w == NULL)
319                 return "missing implicate";
320         imp.attribute = attr_find(w);
321         if (imp.attribute < 0)
322                 return msg(buf, "unknown attribute: %s", w, NULL);
323
324         ip = dl_new(implication);
325         *ip = imp;
326         if (impcnt == 0)
327                 implist = dl_head();
328         dl_add(implist, ip);
329         impcnt++;
330         return NULL;
331 }
332
333 int impl_store()
334 {
335         /* store implications to the database
336          * about 100 bytes each, so send 8 at a time
337          */
338
339         int i;
340         implication *next;
341
342         if (implist)
343         {
344                 next = dl_next(implist);
345
346                 for (i=0 ; i<impcnt ; i+= 8)
347                 {
348                         int i2;
349                         impls *ip;
350                         implication ilist[8];
351                         book_change ch;
352                         ch.chng = IMPLICATIONS;
353                         ip = &ch.book_change_u.implch;
354                         ip->first = i;
355                         ip->total = impcnt;
356                         ip->exlist.exlist_val = ilist;
357                         for (i2=0 ; i2<8 && i+i2 < impcnt ; i2++)
358                         {
359                                 ilist[i2] = *next;
360                                 next = dl_next(next);
361                         }
362                         ip->exlist.exlist_len = i2;
363                         if (send_change(&ch) != 0)
364                                 return -1;
365                 }
366         }
367         return 0;
368 }
369
370 int impl_cmp()
371 {
372         /* complare implications in implist with
373          * those in impl_list
374          * return 1 if different
375          */
376         int i;
377         implication *next = dl_next(implist);
378
379         get_impl_list();
380     
381         if (impcnt != impl_list.total)
382                 return 1;
383
384         for (i=0 ; i<impcnt ; i++, next = dl_next(next))
385         {
386                 implication *ip = &impl_list.exlist.exlist_val[i];
387
388                 if (ip->startday != next->startday
389                     || ip->endday != next->endday
390                     || ip->starttime != next->starttime
391                     || ip->endtime != next->endtime
392                     || ip->startdow != next->startdow
393                     || ip->enddow != next->enddow
394                     || ip->attribute != next->attribute
395                     || !desc_eql(ip->included, next->included)
396                     || !desc_eql(ip->excluded, next->excluded)
397                         )
398                         return 1;
399         }
400         return 0;
401 }
402
403 void impl_update()
404 {
405         if (implist && impl_cmp())
406                 impl_store();
407 }
408
409 attr_type_desc new_attrs;
410
411 int attr_type_update()
412 {
413         get_attr_types();
414
415         if (get_attr_types() == 0
416             || !desc_eql(new_attrs.mandatory, attr_types.mandatory)
417             || !desc_eql(new_attrs.lab, attr_types.lab)
418             || !desc_eql(new_attrs.all, attr_types.all)
419             || new_attrs.open != attr_types.open
420             || new_attrs.closed != attr_types.closed
421             || new_attrs.available != attr_types.available
422             || new_attrs.bookable != attr_types.bookable
423             || new_attrs.reusable != attr_types.reusable
424             || new_attrs.firmalloc != attr_types.firmalloc
425                 )
426         {
427                 /* there is a difference, send the change */
428                 book_change ch;
429                 ch.chng = ATTR_TYPE;
430                 ch.book_change_u.attrs = new_attrs;
431                 if (send_change(&ch) != 0)
432                         return -1;
433         }
434         return 0;
435 }
436
437
438 void attr_print()
439 {
440         /* list attributes definitions just as they would be read in */
441         int i;
442         if (get_attr_types()==0)
443                 printf("Cannot get attribute type information\n");
444         else
445                 for (i=0 ; i <BITINDMAX ; i++)
446                         if (query_bit(&attr_types.all, i))
447                         {
448                                 char *name = get_mappingchar(i, M_ATTRIBUTE);
449                                 if (name)
450                                 {
451                                         printf("%d:%s", i, name);
452                                         free(name);
453                                 }
454                                 else
455                                         printf("%d:#%d", i, i);
456                                 if (query_bit(&attr_types.mandatory, i)==0)
457                                         printf(" optional");
458                                 if (query_bit(&attr_types.lab, i)==1)
459                                         printf(" lab");
460                                 if (i == attr_types.open) printf(" open");
461                                 if (i == attr_types.closed) printf(" closed");
462                                 if (i == attr_types.available) printf(" available");
463                                 if (i == attr_types.bookable) printf(" bookable");
464                                 if (i == attr_types.reusable) printf(" reusable");
465                                 if (i == attr_types.firmalloc) printf(" firmalloc");
466                                 printf("\n");
467                         }
468 }
469             
470
471 void impl_print()
472 {
473         /* list the implications in form suitable for input
474          */
475         int i;
476         if (get_impl_list() == 0)
477                 printf("Cannot get implication list\n");
478         else
479                 for (i=0 ; i<impl_list.total ; i++)
480                 {
481                         implication *ip = &impl_list.exlist.exlist_val[i];
482                         char str[30];
483                         char *name;
484                         int j;
485
486                         if (ip->startday > 0 || ip->endday < 366)
487                         {
488                                 printf("%s", doy2str(str, ip->startday));
489                                 if (ip->startday != ip->endday)
490                                         printf("-%s", doy2str(str, ip->endday));
491                                 printf(" ");
492                         }
493                         if (ip->startdow > 0 || ip->enddow < 6)
494                         {
495                                 extern char *days[];
496                                 printf("%s", days[ip->startdow]);
497                                 if (ip->startdow != ip->enddow)
498                                         printf("-%s", days[ip->enddow]);
499                                 printf(" ");
500                         }
501                         if (ip->starttime >0 || ip->endtime < 24*60)
502                         {
503                                 printf("%s", pod2str(str, ip->starttime));
504                                 if (ip->starttime != ip->endtime)
505                                         printf("-%s", pod2str(str, ip->endtime+1));
506                                 printf(" ");
507                         }
508                         for (j=0 ; j < BITINDMAX ; j++)
509                         {
510                                 if (query_bit(&ip->included, j))
511                                 {
512                                         char *name = get_mappingchar(j, M_ATTRIBUTE);
513                                         if (name)
514                                                 printf("%s ", name);
515                                         else
516                                                 printf("#%d ", j);
517                                         if (name)
518                                                 free(name);
519                                 }
520                                 if (query_bit(&ip->excluded, j))
521                                 {
522                                         char *name = get_mappingchar(j, M_ATTRIBUTE);
523                                         if (name)
524                                                 printf("!%s ", name);
525                                         else
526                                                 printf("!#%d ", j);
527                                         if (name)
528                                                 free(name);
529                                 }
530                         }
531                         name = get_mappingchar(ip->attribute, M_ATTRIBUTE);
532                         if (name)
533                                 printf("-> %s\n", name);
534                         else
535                                 printf("-> #%d\n", ip->attribute);
536                         if (name) free(name);
537                 }
538 }
539
540 static void list_desc(char *prefix, description d)
541 {
542         int first = 1;
543         int i;
544         for (i=0 ; i<d.item_len*8 ; i++)
545         {
546                 if ((i%32)==0 && !first)
547                 {
548                         printf("\n");
549                         first = 1;
550                 }
551                 if (query_bit(&d, i))
552                 {
553                         char *n;
554                         if (first)
555                                 printf("%s", prefix);
556                         first = 0;
557                         n = get_mappingchar(i, M_ATTRIBUTE);
558                         if (n== NULL) n = strdup("*unknown*attr*");
559                         printf("%s ", n);
560                         free(n);
561                 }
562         }
563         if (!first) printf("\n");
564 }
565
566 int main(int argc, char *argv[])
567 {
568         /* If no args, display attr file
569          * if -t then rest of args are time/date/attr - interpret
570          * else one arg- file to read and set
571          */
572
573         if (argc == 1) {
574                 attr_print();
575                 impl_print();
576         } else if (argc >= 2 && strcmp(argv[1], "-t")==0 ) {
577                 int a;
578                 int doy=-1, pod=-1;
579                 description bits = new_desc();
580                 for (a=2; a < argc ; a++) {
581                         int v;
582                         if ((v=get_mappingint(argv[a], M_ATTRIBUTE)) >= 0)
583                                 set_a_bit(&bits, v);
584                         else if ((v=dayofyear(argv[a]))>=0)
585                                 doy = v;
586                         else if ((v=podofday(argv[a]))>=0)
587                                 pod = v;
588                         else if (strcmp(argv[a], "--")==0)
589                                 break;
590                         else
591                                 fprintf(stderr, "attr: cannot understand '%s' - ignoring\n", argv[a]);
592                 }
593                 if (doy == -1)
594                         fprintf(stderr, "attr: no day of year given\n");
595                 else if (pod == -1 && a == argc)
596                         fprintf(stderr, "attr: no period of day given\n");
597                 else {
598                         description *result;
599                         if (a == argc)
600                         {
601                                 result = process_exclusions(pod, doy, &bits);
602                                 list_desc("  ", *result);
603                         }
604                         else {
605                                 int a1;
606                                 int lo, hi;
607                                 a++;
608
609                                 if (pod==-1) lo=0, hi=47;
610                                 else lo=hi=pod;
611                                 for (; lo <= hi ; lo++) {
612                                         char *prefix="";
613                                         description b2;
614                                         desc_cpy(&b2, &bits);
615                                         result = process_exclusions(lo, doy, &b2);
616                                         if (pod == -1) printf("%02d:%02d: ", lo/2, (lo&1)*30);
617                                         for (a1=a ; a1 < argc ; a1++) {
618                                                 int v;
619                                                 v = get_mappingint(argv[a1], M_ATTRIBUTE);
620                                                 if (v <0 )
621                                                         fprintf(stderr, "attr: unknown attribute: '%s'\n", argv[a1]);
622                                                 else {
623                                                         if (query_bit(result, v)) {
624                                                                 printf("%s%s", prefix, argv[a1]);
625                                                                 prefix=", ";
626                                                         }
627                                                 }
628                                         }
629                                         if (*prefix || pod==-1) printf("\n");
630                                 }
631                         }
632                 }
633         } else if (argc == 2) {
634                 FILE *f;
635                 int ln = 0;
636                 int errs = 0;
637                 char *err;
638                 char line[1024];
639                 if (strcmp(argv[1], "-")==0)
640                         f = stdin;
641                 else
642                         f = fopen(argv[1], "r");
643                 if (f == NULL)
644                 {
645                         fprintf(stderr, "attr: cannot open ");
646                         perror(argv[1]);
647                         fprintf(stderr, "Usage: attr [file]\n");
648                         exit(1);
649                 }
650                 attr_init();
651                 new_attrs.mandatory = new_desc();
652                 new_attrs.lab = new_desc();
653                 new_attrs.all = new_desc();
654                 new_attrs.open = new_attrs.closed = new_attrs.available
655                         = new_attrs.bookable = new_attrs.reusable = new_attrs.firmalloc = -1;
656                 while (fgets(line, sizeof(line), f))
657                 {
658                         char *cp;
659                         ln++;
660                         cp = strchr(line, '\n');
661                         if (cp) *cp = '\0';
662                         cp = line+strlen(line);
663                         while (cp > line && (cp[-1]== ' ' || cp[-1] == '\t'))
664                                 *--cp = '\0';
665                         cp = line;
666                         while (*cp==' ' || *cp == '\t')
667                                 cp++;
668                         if (*cp == '#' || *cp == '\0')
669                                 continue;
670                         if (strchr(cp, '>')==NULL)
671                                 err = attr_add(cp, &new_attrs);
672                         else
673                                 err = impl_add(cp);
674
675                         if (err)
676                         {
677                                 fprintf(stderr,"attr: %s at line %d\n", err, ln);
678                                 errs++;
679                         }
680                 }
681                 if (errs == 0)
682                 {
683                         attr_update(1);
684                         attr_type_update();
685                         impl_update();
686                 }
687         } else if (argc != 2) {
688                 fprintf(stderr, "Usage: attr [file]\n");
689                 fprintf(stderr, "       attr -t time date lab ( -- attributes to check)\n");
690                 exit(2);
691         }
692         exit(0);
693 }