2 * ASCII-format record and config file reader/writer
7 * train <trainpn> is <addr> <head>+<detectable>+<tail>
8 * train <trainpn> home [-]<segpn> [-]<segpn> [-]<segpn>
9 * train <trainpn> step <step>=<speed>
10 * train <trainpn> stops <step> at <distance> after <milliseconds>
11 * seg <segpn> has [-]<ownerpn> [inverted]
12 * seg <segpn> at <movposcomb>
13 * adjunct <trainoradjpn> <adjpn> is <addr> <nmranum>
14 * adjunct <trainoradjpn> <adjpn> is <addr> step [-]<nmranum>
15 * adjunct - is <addr> <nmranum> } prevents enabling adjunct;
16 * adjunct - is <addr> step [-]<nmranum> } xmits turning it off
18 * speed is floating point in m/s
26 int n_adjtargs, n_adjaddrs;
27 AdjunctsTarget *adjtargs;
28 AdjunctsAddr **adjaddrs;
30 /*---------- input and error handling ----------*/
32 static const char *filename;
34 void record_yyerror(const char *m) {
35 die("config: %s:%d: %s", filename, record_yylineno, m);
38 /*---------- pname lookup (also handles train counting) ----------*/
40 static char **train_pnames;
42 Train *record_pname2train(const char *pname) {
46 for (tran=0, trap=train_pnames; tran<n_trains; tran++,trap++) {
47 if (!strcmp(*trap, pname)) goto found;
49 if (trains) record_yyerror("number of trains changed between passes!");
51 train_pnames= mrealloc(train_pnames, sizeof(*train_pnames)*n_trains);
52 train_pnames[tran]= mstrdup(pname);
55 if (!trains) return 0;
59 Segment *record_pname2seg(const char *pname) {
62 if (!segments) return 0;
65 if (!strcmp(segi->pname, pname))
68 return 0; /* silently discard data for segments no longer in the layout */
71 #define SLIST_FIND_OR_INSERT(head,search,node,found,fillin) do{ \
72 for ((search)= &(head); \
73 ((node)=*(search)) && !(found); \
74 (search)= &(node)->next); \
76 *(search)= (node)= mmalloc(sizeof(*(node))); \
81 #define ARY_FIND_OR_APPEND(things,n_things,i,entry,found,fillin) do{ \
82 for ((i)=0, (entry)=(things); \
83 (i)<(n_things) && !(found); \
85 if ((i) >= (n_things)) { \
87 (things)= mrealloc((things), sizeof(*things) * (n_things)); \
88 entry= &(things)[(i)]; \
93 AdjunctsAddr *record_adjaddr(int num) {
94 AdjunctsAddr **search;
97 if (num<1 || num>0x3ff) record_yyerror("adjunct address out of range");
100 (adjaddrs,n_adjaddrs, ix,search, (*search)->addr == num, ({
102 AdjunctsAddr *item= mmalloc(sizeof(*item));
106 item->current= item->permit= item->all= 0;
107 for (i=0; i<4; i++) item->rn[i].pi.l= 0;
114 AdjunctsAdjunct *record_pname2adjunct(const char *targpn, const char *adjpn) {
116 AdjunctsTarget *targ;
117 AdjunctsAdjunct *adj;
120 (adjtargs,n_adjtargs,i,targ, !strcmp(targ->pname, targpn), ({
121 targ->pname= mstrdup(targpn);
127 (targ->adjs,targ->n_adjs,i,adj, !strcmp(adj->pname, adjpn), ({
128 adj->pname= mstrdup(adjpn);
136 /*---------- zone allocator for strings ----------*/
138 typedef struct TempZoneBlock TempZoneBlock;
139 struct TempZoneBlock {
140 TempZoneBlock *next; /* for deallocation */
144 static TempZoneBlock *tempzones_head;
145 static char *tempzone_ptr;
146 static int tempzone_remain, tempzone_head_len;
148 char *record_tempzone_strdup(const char *s) {
153 if (l > tempzone_remain) {
155 assert(l < INT_MAX/20);
156 tempzone_head_len= tempzone_remain= l*10 + 65536;
157 new= mmalloc(sizeof(new) + tempzone_remain);
158 tempzone_ptr= (char*)new + sizeof(new);
159 new->next= tempzones_head;
163 tempzone_remain -= l;
169 void record_tempzone_clear(void) {
170 TempZoneBlock *clear, *clear_next;
171 if (!tempzones_head) return;
173 clear= tempzones_head->next;
174 while (clear) { clear_next= clear->next; free(clear); clear= clear_next; }
175 tempzones_head->next= 0;
176 tempzone_remain= tempzone_head_len;
177 tempzone_ptr= tempzones_head->buf;
180 static void record_tempzone_freeall(void) {
181 record_tempzone_clear();
182 free(tempzones_head);
185 tempzone_remain= tempzone_head_len= 0;
188 /*---------- semantic actions ----------*/
190 * These are all called only during the second pass, when trains is non-0.
191 * Also, all Train* and Segment* arguments are known to be non-0.
194 void record_train_is(Train *tra, int addr, int head, int det, int tail) {
197 tra->detectable= det;
201 void record_train_home(Train *tra, int backw, Segment *seg) {
204 seg->ho_backwards= backw;
207 void record_train_step_speed(Train *tra, int step, double speed) {
208 if (step<1 || step>SPEEDSTEPS)
209 record_yyerror("speed step out of range 0..126");
210 tra->speedcurve[step]= speed;
213 void record_seg_has(Segment *seg, int backw, Train *tra, int inverted) {
215 seg->tr_backwards= backw;
216 seg->seg_inverted= inverted;
219 void record_seg_at(Segment *seg, const char *movposcombpname) {
220 const SegPosCombInfo *spci;
223 for (poscomb=0, spci=seg->i->poscombs;
224 poscomb < seg->i->n_poscombs;
226 if (!strcmp(spci->pname, movposcombpname))
231 seg->movposcomb= poscomb;
234 /*---------- adjuncts ----------*/
236 static void record_adjunct_bits(AdjunctsAdjunct *adj, AdjunctsAddr *addr,
238 if (!adj) /* `-' adjunct */
240 if (adj->a && adj->a != addr)
241 record_yyerror("adjunct includes multiple decoder addresses");
244 addr->permit |= bits;
248 void record_adjunct_nmrafunc(AdjunctsAdjunct *adj, AdjunctsAddr *addr,
252 if (num<0 || num>12) record_yyerror("NMRA function out of range 0..12");
255 if (addr->all & bitval)
256 record_yyerror("adjunct bit respecified");
258 record_adjunct_bits(adj,addr,bitval);
261 void record_adjunct_motor(AdjunctsAdjunct *adj, AdjunctsAddr *addr,
263 if (speed<-126 || speed>126) record_yyerror("motor speed out of range");
264 if (!speed) record_yyerror("zero motor speed?!");
265 if (addr->speedstep) record_yyerror("adjunct specifies multiple speeds");
267 record_adjunct_bits(adj,addr,ADJS_SPEEDSTEP_BIT);
269 addr->speedstep= speed;
270 if (addr->speedstep<0) {
271 addr->all |= ADJS_SPEEDSTEP_REVERSE;
272 addr->speedstep= -addr->speedstep;
276 static int pname1st_compar(const void *av, const void *bv) {
277 const char *const *a= av;
278 const char *const *b= bv;
279 return strcmp(*a,*b);
282 static int adjaddrp_compar(const void *av, const void *bv) {
283 const AdjunctsAddr *const *a= av;
284 const AdjunctsAddr *const *b= bv;
285 return (*a)->addr - (*b)->addr;
288 static void adjuncts_postprocess(void) {
290 AdjunctsTarget *targ;
292 qsort(adjtargs, n_adjtargs, sizeof(*adjtargs), pname1st_compar);
293 qsort(adjaddrs, n_adjaddrs, sizeof(*adjaddrs), adjaddrp_compar);
295 for (i=0, targ=adjtargs;
298 qsort(targ->adjs, targ->n_adjs, sizeof(*targ->adjs), pname1st_compar);
301 /*---------- speed curves ----------*/
303 static SpeedRange *rangebuf;
304 static int rangebufsz, rangebufused;
306 void record_train_stopregime_count(void) {
310 void record_train_stopregime(Train *tra, int step, int xs, int ts) {
315 if (step<1 || step>126) record_yyerror("stop regime step out of range");
317 if (rangebufused >= rangebufsz)
318 record_yyerror("more speed points on 2nd pass!");
320 if (!tra->speedregimes) {
321 tra->speedregimes= rangebuf + rangebufused;
324 /* Make room for an extra speed step point for this train,
325 * by moving all of the other trains up. First move the data: */
326 memmove(tra->speedregimes + tra->n_speedregimes + 1,
327 tra->speedregimes + tra->n_speedregimes,
328 (char*)(rangebuf + rangebufused)
329 - (char*)(tra->speedregimes + tra->n_speedregimes));
330 /* Then adjust everyone's pointers: */
331 for (i=0, other=trains;
334 if (other != tra && other->speedregimes &&
335 other->speedregimes >= tra->speedregimes)
336 other->speedregimes++;
337 /* ... that whole thing is O(n^2) if the regimes aren't sorted by
338 * train; we hope they are, in which case we are always adding to
339 * the last train in the list and there is then no data to copy,
340 * which makes it O(n*t) where n is the number of regimes and t is
341 * the number of trains. If we cared we could optimise away the
342 * loop in the common case where this train is right at the end but
343 * a special case seems like asking for a bug when we do have out of
344 * order speed points. */
346 new= &tra->speedregimes[tra->n_speedregimes++];
354 static int speedregime_compare(const void *av, const void *bv) {
355 const SpeedRange *a= av, *b= bv;
356 if (a->speed == b->speed)
357 record_yyerror("multiple identical speed regimes");
358 return a->speed > b->speed ? 1 : -1;
361 static void speeds_postprocess(void) {
366 if (!tra->speedregimes || tra->speedcurve[1]<0) {
370 if (tra->n_speedregimes < 1)
371 die("config: speed curve too short for %s", tra->pname);
373 qsort(tra->speedregimes, tra->n_speedregimes,
374 sizeof(*tra->speedregimes), speedregime_compare);
375 if (tra->speedregimes[tra->n_speedregimes-1].speed < 126)
376 die("config: speed curve missing top point for %s", tra->pname);
378 for (step=1; step<=SPEEDSTEPS; step++)
379 if (tra->speedcurve[step] < 0)
380 die("config: speed curve for %s missing step %d", tra->pname, step);
381 tra->speedcurve[0]= 0;
383 for (i=0; i<tra->n_speedregimes; i++) {
384 step= tra->speedregimes[i].speed;
385 tra->speedregimes[i].speed= tra->speedcurve[step];
390 /*---------- persistent data file layout ----------*/
392 static void *alloc_some(void *mapbase, int *offset, size_t sz, int count) {
396 while (*offset % sz) {
397 if (mapbase) ((Byte*)mapbase)[*offset]= 0xaa;
402 r= (Byte*)mapbase + *offset;
411 static void alloc(void) {
416 int phase, offset, datalen=0, step;
418 #define ALLOC(array,count) \
419 ((array)= alloc_some(mapbase,&offset,sizeof(*(array)),(count)))
421 for (phase=0; ; phase++) {
422 /* phase 0: count up how much space will be needed
423 * phase 1: fill in most of the details, leaving header blank
424 * phase 2: fill in the header, and then exit before doing rest
429 mapbase= record_allocate(datalen);
431 #define PHI_SAVE(x) { \
435 memcpy(p, &(x), sizeof(x)); \
438 DO_PERSIST_HEADER_ITEMS(PHI_SAVE, PHI_SAVE, PHI_SAVE)
443 ALLOC(trains, n_trains);
444 FOR_TRAIN(tra, trap=train_pnames, trap++) {
446 ALLOC(pname, strlen(*trap)+1);
448 strcpy(pname, *trap);
450 tra->pname= *trap= pname;
453 tra->uncertainty= tra->maxinto= 0;
456 for (step=0; step<=SPEEDSTEPS; step++)
457 tra->speedcurve[step]= -1;
461 ALLOC(segments, info_nsegments);
465 seg->seg_inverted= 0;
466 seg->res_movposset= 0;
469 seg->motion= seg->motion_newplan= 0;
478 rangebuf= mmalloc(sizeof(*rangebuf) * rangebufsz);
481 /*---------- entrypoint from main, and its subroutines ----------*/
483 static void parse_file(const char *why) {
487 f= fopen(filename,"r");
488 if (!f) diee("config: cannot open %s: %s", why, filename);
492 assert(!r); /* we're supposed to call yyerror which dies */
496 static void parse_pass(const char **argv) {
497 while ((filename= *argv++))
498 parse_file("commandline-specified record file");
500 filename= persist_record_converted;
502 parse_file("converted persistent data file");
505 void records_parse(const char **argv) {
506 parse_pass(argv); /* trains==0: counts trains and speed ranges. */
508 parse_pass(argv); /* trains!=0: populates data area */
509 record_tempzone_clear();
510 speeds_postprocess();
511 adjuncts_postprocess();
512 record_tempzone_freeall();
513 record_yylex_destroy();