2 * ASCII-format record and config file reader/writer
7 * train <trainpn> at [-]<foredetectpn>:<maxinto>+-<uncertainty>
8 * train <trainpn> is <addr> <head>+<detectable>+<tail>
9 * train <trainpn> step <step>=<speed> <upwait>/<downwait>
10 * train <trainpn> home [-]<segpn> [-]<segpn> [-]<segpn>
11 * seg <segpn> has [-]<ownerpn>
12 * seg <segpn> at <movposcomb>
13 * feature <trainorfeatpn> <letter> is <addr> <nmranum>
14 * feature <trainorfeatpn> <letter> is <addr> step [-]<nmranum>
16 * speed is in um/s, upwait and downwait are in us
24 /*---------- input and error handling ----------*/
26 static const char *filename;
28 void record_yyerror(const char *m) {
29 die("config: %s:%d: %s", filename, record_yylineno, m);
32 /*---------- pname lookup (also handles train counting) ----------*/
34 static char **train_pnames;
36 Train *record_pname2train(const char *pname) {
40 for (tran=0, trap=train_pnames; tran<n_trains; tran++,trap++) {
41 if (!strcmp(*trap, pname)) goto found;
43 if (trains) record_yyerror("number of trains changed between passes!");
45 train_pnames= mrealloc(train_pnames, sizeof(*train_pnames)*n_trains);
46 train_pnames[tran]= mstrdup(pname);
49 if (!trains) return 0;
53 Segment *record_pname2seg(const char *pname) {
57 if (!strcmp(segi->pname, pname))
60 return 0; /* silently discard data for segments no longer in the layout */
63 /*---------- zone allocator for strings ----------*/
65 typedef struct TempZoneBlock TempZoneBlock;
66 struct TempZoneBlock {
67 TempZoneBlock *next; /* for deallocation */
71 static TempZoneBlock *tempzones_head;
72 static char *tempzone_ptr;
73 static int tempzone_remain, tempzone_head_len;
75 char *record_tempzone_strdup(const char *s) {
80 if (l > tempzone_remain) {
82 assert(l < INT_MAX/20);
83 tempzone_head_len= tempzone_remain= l*10 + 65536;
84 new= mmalloc(sizeof(new) + tempzone_remain);
85 tempzone_ptr= (char*)new + sizeof(new);
86 new->next= tempzones_head;
96 void record_tempzone_clear(void) {
97 TempZoneBlock *clear, *clear_next;
98 if (!tempzones_head) return;
100 clear= tempzones_head->next;
101 while (clear) { clear_next= clear->next; free(clear); clear= clear_next; }
102 tempzones_head->next= 0;
103 tempzone_remain= tempzone_head_len;
104 tempzone_ptr= tempzones_head->buf;
107 /*---------- semantic actions ----------*/
109 * These are all called only during the second pass, when trains is non-0.
110 * Also, all Train* and Segment* arguments are known to be non-0.
113 void record_train_is(Train *tra, int addr, int head, int det, int tail) {
116 tra->detectable= det;
120 void record_train_at(Train *tra, int backw, Segment *seg, int maxi, int unc) {
121 tra->foredetect= seg;
123 tra->uncertainty= unc;
124 tra->backwards= backw;
127 void record_train_home(Train *tra, int backw, Segment *seg) {
130 seg->ho_backwards= backw;
133 void record_seg_has(Segment *seg, int backw, Train *tra) {
135 seg->tr_backwards= backw;
138 void record_seg_at(Segment *seg, const char *movposcombpname) {
139 const SegPosCombInfo *spci;
142 for (poscomb=0, spci=seg->i->poscombs;
143 poscomb < seg->i->n_poscombs;
145 if (!strcmp(spci->pname, movposcombpname))
150 seg->movposcomb= poscomb;
153 /*---------- speed curves ----------*/
155 static SpeedCurveEntry *curvebuf;
156 static int curvebufsz, curvebufused;
158 void record_train_step_count(void) {
162 void record_train_step(Train *tra, int step, int speed, int upw, int downw) {
164 SpeedCurveEntry *new;
167 if (curvebufused >= curvebufsz)
168 record_yyerror("more speed points on 2nd pass!");
170 if (!tra->accel.curve) {
171 tra->accel.curve= curvebuf + curvebufused;
174 /* Make room for an extra speed step point for this train,
175 * by moving all of the other trains up. First move the data: */
176 memmove(tra->accel.curve + tra->accel.curvesz + 1,
177 tra->accel.curve + tra->accel.curvesz,
178 (char*)(curvebuf + curvebufused)
179 - (char*)(tra->accel.curve + tra->accel.curvesz));
180 /* Then adjust everyone's pointers: */
181 for (i=0, other=trains;
184 if (other != tra && other->accel.curve &&
185 other->accel.curve >= tra->accel.curve)
186 other->accel.curve++;
187 /* ... that whole thing is O(n^2) if the speed points aren't sorted
188 * by train; we hope they are, in which case we are always adding
189 * to the last train in the list and there is then no data to copy,
190 * which makes it O(n*t) where n is the number of speed points and
191 * t is the number of trains. If we cared we could optimise away
192 * the loop in the common case where this train is right at the end
193 * but a special case seems like asking for a bug when we do have
194 * out of order speed points. */
196 new= &tra->accel.curve[tra->accel.curvesz++];
200 new->speed= speed * 1e-6 * SPEED_UNIT;
201 new->upwait= upw * 1e-3;
202 new->downwait= downw * 1e-3;
205 static int speedcurveentry_compare(const void *av, const void *bv) {
206 const SpeedCurveEntry *a= av, *b= bv;
207 if (a->step == b->step)
208 record_yyerror("multiple speed curve points at same step");
209 return a->step - b->step;
212 static void sort_curves(void) {
216 if (tra->accel.curve) {
217 if (tra->accel.curvesz < 2)
218 die("config: speed curve too short for %s", tra->pname);
219 qsort(tra->accel.curve, tra->accel.curvesz,
220 sizeof(*tra->accel.curve), speedcurveentry_compare);
221 if (tra->accel.curve[0].step || tra->accel.curve[0].speed)
222 die("config: speed curve missing zero point for %s", tra->pname);
227 /*---------- persistent data file layout ----------*/
229 static void *alloc_some(void *mapbase, int *offset, size_t sz, int count) {
233 while (*offset % sz) {
234 if (mapbase) ((Byte*)mapbase)[*offset]= 0xaa;
239 r= (Byte*)mapbase + *offset;
248 static void alloc(void) {
253 int phase, offset, datalen=0;
255 #define ALLOC(array,count) \
256 ((array)= alloc_some(mapbase,&offset,sizeof(*(array)),(count)))
258 for (phase=0; ; phase++) {
259 /* phase 0: count up how much space will be needed
260 * phase 1: fill in most of the details, leaving header blank
261 * phase 2: fill in the header, and then exit before doing rest
266 mapbase= record_allocate(datalen);
268 #define PHI_SAVE(x) { \
272 memcpy(p, &(x), sizeof(x)); \
275 DO_PERSIST_HEADER_ITEMS(PHI_SAVE, PHI_SAVE, PHI_SAVE)
280 ALLOC(trains, n_trains);
281 FOR_TRAIN(tra, trap=train_pnames, trap++) {
283 ALLOC(pname, strlen(*trap)+1);
285 strcpy(pname, *trap);
287 tra->pname= *trap= pname;
293 ALLOC(segments, info_nsegments);
305 curvebuf= mmalloc(sizeof(*curvebuf) * curvebufsz);
308 /*---------- entrypoint from main, and its subroutines ----------*/
310 static void parse_file(const char *why) {
313 f= fopen(filename,"r");
314 if (!f) diee("config: cannot open %s: %s", why, filename);
319 static void parse_pass(const char **argv) {
320 while ((filename= *argv++))
321 parse_file("commandline-specified record file");
323 filename= persist_record_converted;
325 parse_file("converted persistent data file");
328 void records_parse(const char **argv) {
329 parse_pass(argv); /* trains==0: counts trains and curve points. */
331 parse_pass(argv); /* trains!=0: populates data area */
332 record_tempzone_clear();