* seg <segpn> at <movposcomb>
*/
+#include <sys/mman.h>
+
+#include "record.h"
+#include "record-l.h"
+
+/*---------- input and error handling ----------*/
+
+static const char *filename;
+
+void record_yyerror(const char *m) {
+ die("config: %s:%d: %s\n", filename, record_yylineno, m);
+}
+
+/*---------- pname lookup (also handles train counting) ----------*/
+
+static char **train_pnames;
+
+Train *record_pname2train(const char *pname) {
+ int tran;
+ char **trap;
+
+ for (tran=0, trap=train_pnames; tran<n_trains; tran++,trap++) {
+ if (!strcmp(*trap, pname)) goto found;
+ }
+ if (trains) record_yyerror("number of trains changed between passes!");
+ tran= n_trains++;
+ train_pnames= mrealloc(train_pnames, sizeof(*train_pnames)*n_trains);
+ train_pnames[tran]= mstrdup(pname);
+
+found:
+ if (!trains) return 0;
+ return trains + tran;
+}
+
+Segment *record_pname2seg(const char *pname) {
+ int i;
+ const SegmentInfo *segi;
+
+ for (i=0, segi=info_segments;
+ i<NUM_SEGMENTS;
+ i++, segi++)
+ if (!strcmp(segi->pname, pname))
+ return segments + i;
+
+ return 0; /* silently discard data for segments no longer in the layout */
+}
+
+/*---------- zone allocator for strings ----------*/
+
+typedef struct TempZoneBlock TempZoneBlock;
+struct TempZoneBlock {
+ TempZoneBlock *next; /* for deallocation */
+ char buf[1];
+};
+
+static TempZoneBlock *tempzones_head;
+static char *tempzone_ptr;
+static int tempzone_remain, tempzone_head_len;
+
+char *record_tempzone_strdup(const char *s) {
+ int l;
+ char *r;
+
+ l= strlen(s) + 1;
+ if (l > tempzone_remain) {
+ TempZoneBlock *new;
+ assert(l < INT_MAX/20);
+ tempzone_head_len= tempzone_remain= l*10 + 65536;
+ new= mmalloc(sizeof(new) + tempzone_remain);
+ tempzone_ptr= (char*)new + sizeof(new);
+ new->next= tempzones_head;
+ tempzones_head= new;
+ }
+ r= tempzone_ptr;
+ tempzone_remain -= l;
+ tempzone_ptr += l;
+ memcpy(r,s,l);
+ return r;
+}
+
+void record_tempzone_clear(void) {
+ TempZoneBlock *clear, *clear_next;
+ if (!tempzones_head) return;
+
+ clear= tempzones_head->next;
+ while (clear) { clear_next= clear->next; free(clear); clear= clear_next; }
+ tempzones_head->next= 0;
+ tempzone_remain= tempzone_head_len;
+ tempzone_ptr= tempzones_head->buf;
+}
+
+/*---------- semantic actions ----------*/
+/*
+ * These are all called only during the second pass, when trains is non-0.
+ * Also, all Train* and Segment* arguments are known to be non-0.
+ */
+
+void record_train_is(Train *tra, int addr, int head, int det, int tail) {
+ tra->addr= addr;
+ tra->head= head;
+ tra->detectable= det;
+ tra->tail= tail;
+}
+
+void record_train_at(Train *tra, int backw, Segment *seg, int maxi, int unc) {
+ tra->foredetect= seg;
+ tra->maxinto= maxi;
+ tra->uncertainty= unc;
+ tra->backwards= backw;
+}
+
+void record_seg_has(Segment *seg, int backw, Train *tra) {
+ seg->owner= tra;
+ seg->tr_backwards= backw;
+}
+
+void record_seg_at(Segment *seg, const char *movposcombpname) {
+ const SegPosCombInfo *spci;
+ int i;
+
+ for (i=0, spci=seg->i->poscombs;
+ i<seg->i->n_poscombs;
+ i++, spci++)
+ if (!strcmp(spci->pname, movposcombpname))
+ goto found;
+ return;
+
+found:
+ seg->movposcomb= i;
+}
+
+/*---------- speed curves ----------*/
+
+static SpeedCurveEntry *curvebuf;
+static int curvebufsz, curvebufused;
+
+void record_train_step_count(void) {
+ curvebufsz++;
+}
+
+void record_train_step(Train *tra, int step, int speed, int upw, int downw) {
+ Train *other;
+ SpeedCurveEntry *new;
+ int i;
+
+ if (curvebufused >= curvebufsz) record_yyerror("more speed points on 2nd pass!");
+
+ if (!tra->accel.curve) {
+ tra->accel.curve= curvebuf + curvebufused;
+ }
+
+ /* Make room for an extra speed step point for this train,
+ * by moving all of the other trains up. First move the data: */
+ memmove(tra->accel.curve + tra->accel.curvesz + 1,
+ tra->accel.curve + tra->accel.curvesz,
+ (char*)(curvebuf + curvebufused)
+ - (char*)(tra->accel.curve + tra->accel.curvesz));
+ /* Then adjust everyone's pointers: */
+ for (i=0, other=trains;
+ i<n_trains;
+ i++, other++)
+ if (other != tra && other->accel.curve &&
+ other->accel.curve >= tra->accel.curve)
+ other->accel.curve++;
+ /* ... that whole thing is O(n^2) if the speed points aren't sorted
+ * by train; we hope they are, in which case we are always adding
+ * to the last train in the list and there is then no data to copy,
+ * which makes it O(n*t) where n is the number of speed points and
+ * t is the number of trains. If we cared we could optimise away
+ * the loop in the common case where this train is right at the end
+ * but a special case seems like asking for a bug when we do have
+ * out of order speed points. */
+
+ new= &tra->accel.curve[tra->accel.curvesz++];
+ curvebufsz++;
+
+ new->step= step;
+ new->speed= speed;
+ new->upwait= upw;
+ new->downwait= downw;
+}
+
+static int speedcurveentry_compare(const void *av, const void *bv) {
+ const SpeedCurveEntry *a= av, *b= bv;
+ if (a->step == b->step)
+ record_yyerror("multiple speed curve points at same step");
+ return a->step - b->step;
+}
+
+static void sort_curves(void) {
+ int i;
+ Train *tra;
+
+ for (i=0, tra=trains;
+ i<n_trains;
+ i++, tra++) {
+ if (tra->accel.curve) {
+ if (tra->accel.curvesz < 2)
+ die("config: speed curve too short for %s", tra->pname);
+ qsort(tra->accel.curve, tra->accel.curvesz,
+ sizeof(*tra->accel.curve), speedcurveentry_compare);
+ if (tra->accel.curve[0].step || tra->accel.curve[0].speed)
+ die("config: speed curve missing zero point for %s", tra->pname);
+ }
+ }
+}
+
+/*---------- entrypoint from main, and its subroutines ----------*/
+
+static void alloc(void) {
+ Train *tra; Segment *seg; const SegmentInfo *segi;
+ char **trap;
+ int i;
+
+ segments= mmap(0, sizeof(*segments)*NUM_SEGMENTS + sizeof(*trains)*n_trains,
+ PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1,0);
+ if (!segments) diee("mmap for shared region");
+
+ for (i=0, seg=segments, segi=info_segments;
+ i<NUM_SEGMENTS;
+ i++, seg++, segi++) {
+ seg->movposcomb= -1;
+ seg->i= segi;
+ }
+
+ trains= (void*)(segments + NUM_SEGMENTS);
+ for (i=0, tra=trains, trap=train_pnames;
+ i<n_trains;
+ i++, tra++, trap++) {
+ tra->pname= *trap;
+ tra->addr= -1;
+ tra->head= -1;
+ }
+
+ curvebuf= mmalloc(sizeof(*curvebuf) * curvebufsz);
+}
+
+static void parse_pass(const char **argv) {
+ FILE *f;
+
+ while ((filename= *argv++)) {
+ f= fopen(filename,"r");
+ if (!f) diee("config: cannot open input file: %s", filename);
+ record_yyrestart(f);
+ record_yyparse();
+ }
+}
+
+void records_parse(const char **argv) {
+ parse_pass(argv); /* trains==0: counts trains and curve points. */
+ alloc();
+ parse_pass(argv); /* trains!=0: populates data area */
+ record_tempzone_clear();
+ sort_curves();
+}