chiark / gitweb /
only autoflush if the message ends with \n
[trains.git] / hostside / commands.c
1 /**/
2
3 #include <setjmp.h>
4 #include <stdlib.h>
5 #include <errno.h>
6 #include <assert.h>
7 #include <string.h>
8
9 #include "realtime.h"
10 #include "auproto-pic.h"
11
12 #define NMRA_MAX_NARGS 10
13
14 typedef struct ManualRetransmitNode ManualRetransmitNode;
15 struct ManualRetransmitNode {
16   ManualRetransmitNode *back, *next;
17   char *name;
18   int lname;
19   RetransmitUrgentNode rn;
20 };
21
22 #define bogus_volatile /*empty*/
23 #ifdef __GNUC__
24 #if __GNUC__ == 2
25 #undef bogus_volatile
26 #define bogus_volatile volatile
27 #endif
28 #endif
29
30 struct NmraParseEncodeCaller {
31   ParseState *ps;
32   unsigned long arg[NMRA_MAX_NARGS];
33   jmp_buf jb;
34 };
35
36 static void ouack(const CmdInfo *ci) {
37   oprintf(UPO, "ack %s\n", ci->name);
38 }
39
40 unsigned long nmra_argnumber(NmraParseEncodeCaller *pec, int argi) {
41   return pec->arg[argi];
42 }
43
44 void nmra_problem(NmraParseEncodeCaller *pec, const char *problem) {
45   badcmd(pec->ps,problem);
46   longjmp(pec->jb, 1);
47 }
48
49 static int cmd_nmra_command(ParseState *ps, PicInsn *pi) {
50   int hex;
51   const char *cmdarg;
52   int lcmdarg;
53   bogus_volatile int argc, checksum;
54   NmraParseEncodeCaller pec;
55   Nmra nmra;
56   char *ep;
57
58   assert(ps->remain);
59   switch (ps->remain[0]) {
60   case '_':  ps->remain++; return ps_needhextoend(ps, pi->d, &pi->l);
61   case '=':  hex=1;  checksum=1;  break;
62   case ':':  hex=1;  checksum=0;  break;
63   default:   hex=0;  checksum=1;  break;
64   }
65
66   if (hex) {
67     ps->remain++;
68     nmra.l= NMRA_PACKET_MAX - checksum;
69     if (!ps_needhextoend(ps, nmra.d, &nmra.l))
70       return 0;
71   } else {
72     if (!ps_needword(ps)) return 0;
73     cmdarg= ps->thisword;
74     lcmdarg= ps->lthisword;
75     pec.ps= ps;
76     argc= 0;
77
78     while (ps_word(ps)) {
79       if (argc >= NMRA_MAX_NARGS) {
80         badcmd(ps,"far too many nmra args");
81         return 0;
82       }
83
84       errno=0; pec.arg[argc++]= strtoul(ps->thisword, &ep, 0);
85       if (errno || ep != ps->thisword + ps->lthisword)
86         { badcmd(ps,"bad numeric argument for nmra"); return 0; }
87     }
88     if (setjmp(pec.jb))
89       return 0;
90   
91     nmra_parse_encode(&nmra, cmdarg,lcmdarg, argc, &pec);
92   }
93   if (checksum)
94     nmra_addchecksum(&nmra);
95
96   nmra_encodeforpic(&nmra, pi);
97   return 1;
98 }
99   
100 static void cmd_nmra(ParseState *ps, const CmdInfo *ci) {
101   static struct { ManualRetransmitNode *head, *tail; } mrns;
102   
103   PicInsn *pi, pi_buf;
104   ManualRetransmitNode *mrn=0;
105   void (*retrans)(RetransmitUrgentNode *urg, Nmra *n)= 0;
106   
107   if (ps->remain) {
108     if (ps->remain[0]=='*') retrans= retransmit_urgent_queue;
109     else if (ps->remain[0]=='%') retrans= retransmit_urgent_queue_relaxed;
110   }
111   if (retrans) {
112     const char *mrname;
113     int lmrname;
114
115     ps_word(ps);
116     mrname= ps->thisword+1;
117     lmrname= ps->lthisword-1;
118
119     for (mrn= mrns.head;
120          !(mrn->lname == lmrname &&
121            !memcmp(mrn->name, mrname, lmrname));
122          mrn= mrn->next);
123     if (mrn) {
124       retransmit_urgent_cancel(&mrn->rn);
125     } else {
126       mrn= mmalloc(sizeof(*mrn));
127       mrn->name= mmalloc(lmrname);
128       memcpy(mrn->name, mrname, lmrname);
129       mrn->lname= lmrname;
130     }
131   }
132
133   if (!ps->remain) {
134     if (!retrans) {
135       badcmd(ps,"nmra must have slot to cancel or data to send");
136       return;
137     }
138     free(mrn->name);
139     free(mrn);
140     ouack(ci);
141     return;
142   }
143
144   pi= retrans ? &mrn->rn.pi : &pi_buf;
145   
146   if (!cmd_nmra_command(ps, pi)) {
147     if (retrans) { free(mrn->name); free(mrn); }
148     return;
149   }
150
151   if (retrans)
152     retrans(&mrn->rn, 0);
153   else
154     serial_transmit(pi);
155   ouack(ci);
156 }
157
158 static void cmd_noop(ParseState *ps, const CmdInfo *ci) {
159   ouack(ci);
160 }
161
162 static void cmd_pic(ParseState *ps, const CmdInfo *ci) {
163   const PicInsnInfo *pii;
164   PicInsn pi;
165   long arg;
166
167   if (ps->remain && ps->remain[0]=='=') {
168     ps->remain++;
169     pi.l= sizeof(pi.d);
170     if (!ps_needhextoend(ps, pi.d, &pi.l)) return;
171   } else {
172     pii= some_needword_lookup(ps,pic_command_infos,"pic command");
173     if (!pii) return;
174   
175     if (pii->argbits) {
176       if (!ps_neednumber(ps, &arg, 0, (1L << pii->argbits) - 1,
177                          "pic object number"))
178         return;
179     } else {
180       arg= 0;
181     }
182     if (!ps_neednoargs(ps))
183       return;
184     enco_pic_pii(&pi, pii, arg);
185   }
186   serial_transmit(&pi);
187   ouack(ci);
188 }
189
190 static void cmd_movfeat(ParseState *ps, const CmdInfo *ci) {
191   Segment *back, *move, *fwd;
192   long ms;
193   ErrorCode ec;
194   
195   if (!ps_needsegment(ps,&back,0) ||
196       !ps_needsegment(ps,&move,0) ||
197       !ps_needsegment(ps,&fwd,0)) return;
198   
199   ms= INT_MAX;
200   if (ps->remain)
201     if (!ps_neednumber(ps,&ms,0,INT_MAX,"milliseconds")) return;
202
203   if (!ps_neednoargs(ps)) return;
204
205   ec= movpos_change(back,move,fwd,ms,0);
206   if (ec) {
207     badcmd(ps,"movfeat %s %s %s %ld %s",
208            back->i->pname, move->i->pname, fwd->i->pname,
209            ms==INT_MAX ? -1L : ms,
210            errorcodelist[ec]);
211     return;
212   }
213
214   ouack(ci);  
215 }
216
217 const CmdInfo toplevel_cmds[]= {
218   { "pic",        cmd_pic         },
219   { "nmra",       cmd_nmra,       },
220   { "noop",       cmd_noop        },
221   { "movfeat",    cmd_movfeat     },
222   { 0 }
223 };