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>
14 * speed is in um/s, upwait and downwait are in us
22 /*---------- input and error handling ----------*/
24 static const char *filename;
26 void record_yyerror(const char *m) {
27 die("config: %s:%d: %s", filename, record_yylineno, m);
30 /*---------- pname lookup (also handles train counting) ----------*/
32 static char **train_pnames;
34 Train *record_pname2train(const char *pname) {
38 for (tran=0, trap=train_pnames; tran<n_trains; tran++,trap++) {
39 if (!strcmp(*trap, pname)) goto found;
41 if (trains) record_yyerror("number of trains changed between passes!");
43 train_pnames= mrealloc(train_pnames, sizeof(*train_pnames)*n_trains);
44 train_pnames[tran]= mstrdup(pname);
47 if (!trains) return 0;
51 Segment *record_pname2seg(const char *pname) {
55 if (!strcmp(segi->pname, pname))
58 return 0; /* silently discard data for segments no longer in the layout */
61 /*---------- zone allocator for strings ----------*/
63 typedef struct TempZoneBlock TempZoneBlock;
64 struct TempZoneBlock {
65 TempZoneBlock *next; /* for deallocation */
69 static TempZoneBlock *tempzones_head;
70 static char *tempzone_ptr;
71 static int tempzone_remain, tempzone_head_len;
73 char *record_tempzone_strdup(const char *s) {
78 if (l > tempzone_remain) {
80 assert(l < INT_MAX/20);
81 tempzone_head_len= tempzone_remain= l*10 + 65536;
82 new= mmalloc(sizeof(new) + tempzone_remain);
83 tempzone_ptr= (char*)new + sizeof(new);
84 new->next= tempzones_head;
94 void record_tempzone_clear(void) {
95 TempZoneBlock *clear, *clear_next;
96 if (!tempzones_head) return;
98 clear= tempzones_head->next;
99 while (clear) { clear_next= clear->next; free(clear); clear= clear_next; }
100 tempzones_head->next= 0;
101 tempzone_remain= tempzone_head_len;
102 tempzone_ptr= tempzones_head->buf;
105 /*---------- semantic actions ----------*/
107 * These are all called only during the second pass, when trains is non-0.
108 * Also, all Train* and Segment* arguments are known to be non-0.
111 void record_train_is(Train *tra, int addr, int head, int det, int tail) {
114 tra->detectable= det;
118 void record_train_at(Train *tra, int backw, Segment *seg, int maxi, int unc) {
119 tra->foredetect= seg;
121 tra->uncertainty= unc;
122 tra->backwards= backw;
125 void record_train_home(Train *tra, int backw, Segment *seg) {
128 seg->ho_backwards= backw;
131 void record_seg_has(Segment *seg, int backw, Train *tra) {
133 seg->tr_backwards= backw;
136 void record_seg_at(Segment *seg, const char *movposcombpname) {
137 const SegPosCombInfo *spci;
140 for (poscomb=0, spci=seg->i->poscombs;
141 poscomb < seg->i->n_poscombs;
143 if (!strcmp(spci->pname, movposcombpname))
148 seg->movposcomb= poscomb;
151 /*---------- speed curves ----------*/
153 static SpeedCurveEntry *curvebuf;
154 static int curvebufsz, curvebufused;
156 void record_train_step_count(void) {
160 void record_train_step(Train *tra, int step, int speed, int upw, int downw) {
162 SpeedCurveEntry *new;
165 if (curvebufused >= curvebufsz)
166 record_yyerror("more speed points on 2nd pass!");
168 if (!tra->accel.curve) {
169 tra->accel.curve= curvebuf + curvebufused;
172 /* Make room for an extra speed step point for this train,
173 * by moving all of the other trains up. First move the data: */
174 memmove(tra->accel.curve + tra->accel.curvesz + 1,
175 tra->accel.curve + tra->accel.curvesz,
176 (char*)(curvebuf + curvebufused)
177 - (char*)(tra->accel.curve + tra->accel.curvesz));
178 /* Then adjust everyone's pointers: */
179 for (i=0, other=trains;
182 if (other != tra && other->accel.curve &&
183 other->accel.curve >= tra->accel.curve)
184 other->accel.curve++;
185 /* ... that whole thing is O(n^2) if the speed points aren't sorted
186 * by train; we hope they are, in which case we are always adding
187 * to the last train in the list and there is then no data to copy,
188 * which makes it O(n*t) where n is the number of speed points and
189 * t is the number of trains. If we cared we could optimise away
190 * the loop in the common case where this train is right at the end
191 * but a special case seems like asking for a bug when we do have
192 * out of order speed points. */
194 new= &tra->accel.curve[tra->accel.curvesz++];
198 new->speed= speed * 1e-6 * SPEED_UNIT;
199 new->upwait= upw * 1e-3;
200 new->downwait= downw * 1e-3;
203 static int speedcurveentry_compare(const void *av, const void *bv) {
204 const SpeedCurveEntry *a= av, *b= bv;
205 if (a->step == b->step)
206 record_yyerror("multiple speed curve points at same step");
207 return a->step - b->step;
210 static void sort_curves(void) {
214 if (tra->accel.curve) {
215 if (tra->accel.curvesz < 2)
216 die("config: speed curve too short for %s", tra->pname);
217 qsort(tra->accel.curve, tra->accel.curvesz,
218 sizeof(*tra->accel.curve), speedcurveentry_compare);
219 if (tra->accel.curve[0].step || tra->accel.curve[0].speed)
220 die("config: speed curve missing zero point for %s", tra->pname);
225 /*---------- persistent data file layout ----------*/
227 static void *alloc_some(void *mapbase, int *offset, size_t sz, int count) {
231 while (*offset % sz) {
232 if (mapbase) ((Byte*)mapbase)[*offset]= 0xaa;
237 r= (Byte*)mapbase + *offset;
246 static void alloc(void) {
251 int phase, offset, datalen=0;
253 #define ALLOC(array,count) \
254 ((array)= alloc_some(mapbase,&offset,sizeof(*(array)),(count)))
256 for (phase=0; ; phase++) {
257 /* phase 0: count up how much space will be needed
258 * phase 1: fill in most of the details, leaving header blank
259 * phase 2: fill in the header, and then exit before doing rest
264 mapbase= record_allocate(datalen);
266 #define PHI_SAVE(x) { \
270 memcpy(p, &(x), sizeof(x)); \
273 DO_PERSIST_HEADER_ITEMS(PHI_SAVE, PHI_SAVE, PHI_SAVE)
278 ALLOC(trains, n_trains);
279 FOR_TRAIN(tra, trap=train_pnames, trap++) {
281 ALLOC(pname, strlen(*trap)+1);
283 strcpy(pname, *trap);
285 tra->pname= *trap= pname;
291 ALLOC(segments, info_nsegments);
303 curvebuf= mmalloc(sizeof(*curvebuf) * curvebufsz);
306 /*---------- entrypoint from main, and its subroutines ----------*/
308 static void parse_file(const char *why) {
311 f= fopen(filename,"r");
312 if (!f) diee("config: cannot open %s: %s", why, filename);
317 static void parse_pass(const char **argv) {
318 while ((filename= *argv++))
319 parse_file("commandline-specified record file");
321 filename= persist_record_converted;
323 parse_file("converted persistent data file");
326 void records_parse(const char **argv) {
327 parse_pass(argv); /* trains==0: counts trains and curve points. */
329 parse_pass(argv); /* trains!=0: populates data area */
330 record_tempzone_clear();