[490] | 1 | /* connect to an INDI server and set one or more device.property.element. |
---|
| 2 | */ |
---|
| 3 | |
---|
| 4 | #include <stdio.h> |
---|
| 5 | #include <stdlib.h> |
---|
| 6 | #include <math.h> |
---|
| 7 | #include <string.h> |
---|
| 8 | #include <errno.h> |
---|
| 9 | #include <signal.h> |
---|
| 10 | #include <time.h> |
---|
| 11 | #include <unistd.h> |
---|
| 12 | #include <sys/types.h> |
---|
| 13 | #include <sys/socket.h> |
---|
| 14 | #include <netinet/in.h> |
---|
| 15 | #include <netdb.h> |
---|
| 16 | |
---|
| 17 | #include "indiapi.h" |
---|
| 18 | #include "lilxml.h" |
---|
| 19 | |
---|
| 20 | /* table of INDI definition elements we can set |
---|
| 21 | * N.B. do not change defs[] order, they are indexed via -x/-n/-s args |
---|
| 22 | */ |
---|
| 23 | typedef struct { |
---|
| 24 | char *defType; /* defXXXVector name */ |
---|
| 25 | char *defOne; /* defXXX name */ |
---|
| 26 | char *newType; /* newXXXVector name */ |
---|
| 27 | char *oneType; /* oneXXX name */ |
---|
| 28 | } INDIDef; |
---|
| 29 | static INDIDef defs[] = { |
---|
| 30 | {"defTextVector", "defText", "newTextVector", "oneText"}, |
---|
| 31 | {"defNumberVector", "defNumber", "newNumberVector", "oneNumber"}, |
---|
| 32 | {"defSwitchVector", "defSwitch", "newSwitchVector", "oneSwitch"}, |
---|
| 33 | }; |
---|
| 34 | #define NDEFS (sizeof(defs)/sizeof(defs[0])) |
---|
| 35 | |
---|
| 36 | #define INDIPORT 7624 /* default port */ |
---|
| 37 | static char host_def[] = "localhost"; /* default host name */ |
---|
| 38 | |
---|
| 39 | static char *me; /* our name for usage message */ |
---|
| 40 | static char *host = host_def; /* working host name */ |
---|
| 41 | static int port = INDIPORT; /* working port number */ |
---|
| 42 | static int verbose; /* report extra info */ |
---|
| 43 | static int directfd = -1; /* direct filedes to server, if >= 0 */ |
---|
| 44 | #define TIMEOUT 2 /* default timeout, secs */ |
---|
| 45 | static int timeout = TIMEOUT; /* working timeout, secs */ |
---|
| 46 | static LilXML *lillp; /* XML parser context */ |
---|
| 47 | |
---|
| 48 | typedef struct { |
---|
| 49 | char *e, *v; /* element name and value */ |
---|
| 50 | int ok; /* set when found */ |
---|
| 51 | } SetEV; |
---|
| 52 | |
---|
| 53 | typedef struct { |
---|
| 54 | char *d; /* device */ |
---|
| 55 | char *p; /* property */ |
---|
| 56 | SetEV *ev; /* elements */ |
---|
| 57 | int nev; /* n elements */ |
---|
| 58 | INDIDef *dp; /* one of defs if known, else NULL */ |
---|
| 59 | } SetSpec; |
---|
| 60 | |
---|
| 61 | static SetSpec *sets; /* set of properties to set */ |
---|
| 62 | static int nsets; |
---|
| 63 | |
---|
| 64 | |
---|
| 65 | static void usage (void); |
---|
| 66 | static int crackSpec (int *acp, char **avp[]); |
---|
| 67 | static void openINDIServer(FILE **rfpp, FILE **wfpp); |
---|
| 68 | static void listenINDI (FILE *rfp, FILE *wfp); |
---|
| 69 | static int finished (void); |
---|
| 70 | static void onAlarm (int dummy); |
---|
| 71 | static int readServerChar (FILE *fp); |
---|
| 72 | static void findSet (XMLEle *root, FILE *fp); |
---|
[504] | 73 | static void scanEV (SetSpec *specp, char ev[]); |
---|
| 74 | static void scanEEVV (SetSpec *specp, char *ep, char ev[]); |
---|
| 75 | static void scanEVEV (SetSpec *specp, char ev[]); |
---|
[490] | 76 | static void sendNew (FILE *fp, INDIDef *dp, SetSpec *sp); |
---|
| 77 | static void sendSpecs(FILE *wfp); |
---|
| 78 | |
---|
| 79 | int |
---|
| 80 | main (int ac, char *av[]) |
---|
| 81 | { |
---|
| 82 | FILE *rfp, *wfp; |
---|
| 83 | int stop = 0; |
---|
| 84 | int allspeced; |
---|
| 85 | |
---|
| 86 | /* save our name */ |
---|
| 87 | me = av[0]; |
---|
| 88 | |
---|
| 89 | /* crack args */ |
---|
| 90 | while (!stop && --ac && **++av == '-') { |
---|
| 91 | char *s = *av; |
---|
| 92 | while (*++s) { |
---|
| 93 | switch (*s) { |
---|
| 94 | |
---|
| 95 | case 'd': |
---|
| 96 | if (ac < 2) { |
---|
| 97 | fprintf (stderr, "-d requires open fileno\n"); |
---|
| 98 | usage(); |
---|
| 99 | } |
---|
| 100 | directfd = atoi(*++av); |
---|
| 101 | ac--; |
---|
| 102 | break; |
---|
| 103 | |
---|
| 104 | case 'h': |
---|
| 105 | if (directfd >= 0) { |
---|
| 106 | fprintf (stderr, "Can not combine -d and -h\n"); |
---|
| 107 | usage(); |
---|
| 108 | } |
---|
| 109 | if (ac < 2) { |
---|
| 110 | fprintf (stderr, "-h requires host name\n"); |
---|
| 111 | usage(); |
---|
| 112 | } |
---|
| 113 | host = *++av; |
---|
| 114 | ac--; |
---|
| 115 | break; |
---|
| 116 | |
---|
| 117 | case 'p': |
---|
| 118 | if (directfd >= 0) { |
---|
| 119 | fprintf (stderr, "Can not combine -d and -p\n"); |
---|
| 120 | usage(); |
---|
| 121 | } |
---|
| 122 | if (ac < 2) { |
---|
| 123 | fprintf (stderr, "-p requires tcp port number\n"); |
---|
| 124 | usage(); |
---|
| 125 | } |
---|
| 126 | port = atoi(*++av); |
---|
| 127 | ac--; |
---|
| 128 | break; |
---|
| 129 | |
---|
| 130 | case 't': |
---|
| 131 | if (ac < 2) { |
---|
| 132 | fprintf (stderr, "-t requires timeout\n"); |
---|
| 133 | usage(); |
---|
| 134 | } |
---|
| 135 | timeout = atoi(*++av); |
---|
| 136 | ac--; |
---|
| 137 | break; |
---|
| 138 | |
---|
| 139 | case 'v': /* verbose */ |
---|
| 140 | verbose++; |
---|
| 141 | break; |
---|
| 142 | |
---|
| 143 | case 'x': /* FALLTHRU */ |
---|
| 144 | case 'n': /* FALLTHRU */ |
---|
| 145 | case 's': |
---|
| 146 | /* stop if see one of the property types */ |
---|
| 147 | stop = 1; |
---|
| 148 | break; |
---|
| 149 | |
---|
| 150 | default: |
---|
| 151 | fprintf (stderr, "Unknown flag: %c\n", *s); |
---|
| 152 | usage(); |
---|
| 153 | } |
---|
| 154 | } |
---|
| 155 | } |
---|
| 156 | |
---|
| 157 | /* now ac args starting at av[0] */ |
---|
| 158 | if (ac < 1) |
---|
| 159 | usage(); |
---|
| 160 | |
---|
| 161 | /* crack each property, add to sets[] */ |
---|
| 162 | allspeced = 1; |
---|
| 163 | do { |
---|
| 164 | if (!crackSpec (&ac, &av)) |
---|
| 165 | allspeced = 0; |
---|
| 166 | } while (ac > 0); |
---|
| 167 | |
---|
| 168 | /* open connection */ |
---|
| 169 | if (directfd >= 0) { |
---|
| 170 | wfp = fdopen (directfd, "w"); |
---|
| 171 | rfp = fdopen (directfd, "r"); |
---|
| 172 | setbuf (rfp, NULL); /* don't absorb next guy's stuff */ |
---|
| 173 | if (!rfp || !wfp) { |
---|
| 174 | fprintf (stderr, "Direct fd %d: %s\n",directfd,strerror(errno)); |
---|
| 175 | exit(1); |
---|
| 176 | } |
---|
| 177 | if (verbose) |
---|
| 178 | fprintf (stderr, "Using direct fd %d\n", directfd); |
---|
| 179 | } else { |
---|
| 180 | openINDIServer(&rfp, &wfp); |
---|
| 181 | if (verbose) |
---|
| 182 | fprintf (stderr, "Connected to %s on port %d\n", host, port); |
---|
| 183 | } |
---|
| 184 | |
---|
| 185 | /* build a parser context for cracking XML responses */ |
---|
| 186 | lillp = newLilXML(); |
---|
| 187 | |
---|
| 188 | /* just send it all speced, else check with server */ |
---|
| 189 | if (allspeced) { |
---|
| 190 | sendSpecs(wfp); |
---|
| 191 | } else { |
---|
| 192 | /* issue getProperties */ |
---|
| 193 | if (verbose) |
---|
| 194 | fprintf (stderr, "Querying for properties\n"); |
---|
| 195 | fprintf(wfp, "<getProperties version='%g'/>\n", INDIV); |
---|
| 196 | fflush (wfp); |
---|
| 197 | |
---|
| 198 | /* listen for properties, set when see any we recognize */ |
---|
| 199 | listenINDI(rfp, wfp); |
---|
| 200 | } |
---|
| 201 | |
---|
| 202 | return (0); |
---|
| 203 | } |
---|
| 204 | |
---|
| 205 | static void |
---|
| 206 | usage() |
---|
| 207 | { |
---|
| 208 | fprintf(stderr, "Purpose: set one or more writable INDI properties\n"); |
---|
[504] | 209 | fprintf(stderr, "%s\n", "$Revision: 1.6 $"); |
---|
| 210 | fprintf(stderr, "Usage: %s [options] {[type] spec} ...\n", me); |
---|
[490] | 211 | fprintf(stderr, "Options:\n"); |
---|
| 212 | fprintf(stderr, " -d f : use file descriptor f already open to server\n"); |
---|
| 213 | fprintf(stderr, " -h h : alternate host, default is %s\n", host_def); |
---|
| 214 | fprintf(stderr, " -p p : alternate port, default is %d\n", INDIPORT); |
---|
| 215 | fprintf(stderr, " -t t : max time to wait, default is %d secs\n",TIMEOUT); |
---|
[504] | 216 | fprintf(stderr, " -v : verbose (more are cumulative)\n"); |
---|
| 217 | fprintf(stderr, "Each spec optionally preceded by its type is sent without first confirming\n"); |
---|
[490] | 218 | fprintf(stderr, "its structure. This is much more efficient but there is no error checking.\n"); |
---|
| 219 | fprintf(stderr, "Types are indicated with the following flags:\n"); |
---|
| 220 | fprintf(stderr, " -x : Text\n"); |
---|
| 221 | fprintf(stderr, " -n : Number\n"); |
---|
| 222 | fprintf(stderr, " -s : Switch\n"); |
---|
[504] | 223 | fprintf(stderr, "Spec may be either:\n"); |
---|
| 224 | fprintf(stderr, " device.property.e1[;e2...]=v1[;v2...]\n"); |
---|
| 225 | fprintf(stderr, " or\n"); |
---|
| 226 | fprintf(stderr, " device.property.e1=v1[;e2=v2...]\n"); |
---|
[490] | 227 | fprintf(stderr, "Exit status:\n"); |
---|
| 228 | fprintf(stderr, " 0: all settings successful\n"); |
---|
| 229 | fprintf(stderr, " 1: at least one setting was invalid\n"); |
---|
| 230 | fprintf(stderr, " 2: real trouble, try repeating with -v\n"); |
---|
| 231 | |
---|
| 232 | exit (2); |
---|
| 233 | } |
---|
| 234 | |
---|
| 235 | /* crack property set spec, add to sets [], move to next spec. |
---|
| 236 | * return 1 if see a type |
---|
| 237 | */ |
---|
| 238 | static int |
---|
| 239 | crackSpec (int *acp, char **avp[]) |
---|
| 240 | { |
---|
[504] | 241 | char d[128], p[128], ev[2048]; |
---|
[490] | 242 | char *spec = *avp[0]; |
---|
| 243 | INDIDef *dp = NULL; |
---|
| 244 | |
---|
| 245 | /* check if first arg is type indicator */ |
---|
| 246 | if (spec[0] == '-') { |
---|
| 247 | switch (spec[1]) { |
---|
| 248 | case 'x': dp = &defs[0]; break; |
---|
| 249 | case 'n': dp = &defs[1]; break; |
---|
| 250 | case 's': dp = &defs[2]; break; |
---|
| 251 | default: |
---|
| 252 | fprintf (stderr, "Bad property type: %s\n", spec); |
---|
| 253 | usage(); |
---|
| 254 | } |
---|
| 255 | (*acp)--; |
---|
| 256 | (*avp)++; |
---|
| 257 | spec = *avp[0]; |
---|
| 258 | } |
---|
| 259 | |
---|
| 260 | /* then scan arg for property spec */ |
---|
[504] | 261 | if (sscanf (spec, "%[^.].%[^.].%s", d, p, ev) != 3) { |
---|
| 262 | fprintf (stderr, "Malformed property spec: %s\n", spec); |
---|
[490] | 263 | usage(); |
---|
| 264 | } |
---|
| 265 | |
---|
| 266 | /* add to list */ |
---|
| 267 | if (!sets) |
---|
| 268 | sets = (SetSpec *) malloc (1); /* seed realloc */ |
---|
| 269 | sets = (SetSpec *) realloc (sets, (nsets+1)*sizeof(SetSpec)); |
---|
| 270 | sets[nsets].d = strcpy (malloc(strlen(d)+1), d); |
---|
| 271 | sets[nsets].p = strcpy (malloc(strlen(p)+1), p); |
---|
| 272 | sets[nsets].dp = dp; |
---|
| 273 | sets[nsets].ev = (SetEV *) malloc (1); /* seed realloc */ |
---|
| 274 | sets[nsets].nev = 0; |
---|
[504] | 275 | scanEV (&sets[nsets++], ev); |
---|
[490] | 276 | |
---|
| 277 | /* update caller's pointers */ |
---|
| 278 | (*acp)--; |
---|
| 279 | (*avp)++; |
---|
| 280 | |
---|
| 281 | /* return 1 if saw a spec */ |
---|
| 282 | return (dp ? 1 : 0); |
---|
| 283 | } |
---|
| 284 | |
---|
| 285 | /* open a read and write connection to host and port or die. |
---|
| 286 | * exit if trouble. |
---|
| 287 | */ |
---|
| 288 | static void |
---|
| 289 | openINDIServer (FILE **rfpp, FILE **wfpp) |
---|
| 290 | { |
---|
| 291 | struct sockaddr_in serv_addr; |
---|
| 292 | struct hostent *hp; |
---|
| 293 | int sockfd; |
---|
| 294 | |
---|
| 295 | /* lookup host address */ |
---|
| 296 | hp = gethostbyname (host); |
---|
| 297 | if (!hp) { |
---|
| 298 | perror ("gethostbyname"); |
---|
| 299 | exit (2); |
---|
| 300 | } |
---|
| 301 | |
---|
| 302 | /* create a socket to the INDI server */ |
---|
| 303 | (void) memset ((char *)&serv_addr, 0, sizeof(serv_addr)); |
---|
| 304 | serv_addr.sin_family = AF_INET; |
---|
| 305 | serv_addr.sin_addr.s_addr = |
---|
| 306 | ((struct in_addr *)(hp->h_addr_list[0]))->s_addr; |
---|
| 307 | serv_addr.sin_port = htons(port); |
---|
| 308 | if ((sockfd = socket (AF_INET, SOCK_STREAM, 0)) < 0) { |
---|
| 309 | perror ("socket"); |
---|
| 310 | exit(2); |
---|
| 311 | } |
---|
| 312 | |
---|
| 313 | /* connect */ |
---|
| 314 | if (connect (sockfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr))<0){ |
---|
| 315 | perror ("connect"); |
---|
| 316 | exit(2); |
---|
| 317 | } |
---|
| 318 | |
---|
| 319 | /* prepare for line-oriented i/o to client */ |
---|
| 320 | *rfpp = fdopen (sockfd, "r"); |
---|
| 321 | *wfpp = fdopen (sockfd, "w"); |
---|
| 322 | } |
---|
| 323 | |
---|
| 324 | /* listen for property reports, send new sets if match */ |
---|
| 325 | static void |
---|
| 326 | listenINDI (FILE *rfp, FILE *wfp) |
---|
| 327 | { |
---|
| 328 | char msg[1024]; |
---|
| 329 | |
---|
| 330 | /* arrange to call onAlarm() if not seeing any more defXXX */ |
---|
| 331 | signal (SIGALRM, onAlarm); |
---|
| 332 | alarm (timeout); |
---|
| 333 | |
---|
| 334 | /* read from server, exit if find all properties */ |
---|
| 335 | while (1) { |
---|
| 336 | XMLEle *root = readXMLEle (lillp, readServerChar(rfp), msg); |
---|
| 337 | if (root) { |
---|
| 338 | /* found a complete XML element */ |
---|
| 339 | if (verbose > 1) |
---|
| 340 | prXMLEle (stderr, root, 0); |
---|
| 341 | findSet (root, wfp); |
---|
| 342 | if (finished() == 0) { |
---|
| 343 | shutdown (fileno(wfp), SHUT_WR); /* insure flush */ |
---|
| 344 | exit (0); /* found all we want */ |
---|
| 345 | } |
---|
| 346 | delXMLEle (root); /* not yet, delete and continue */ |
---|
| 347 | } else if (msg[0]) { |
---|
| 348 | fprintf (stderr, "Bad XML from %s/%d: %s\n", host, port, msg); |
---|
| 349 | exit(2); |
---|
| 350 | } |
---|
| 351 | } |
---|
| 352 | } |
---|
| 353 | |
---|
| 354 | /* return 0 if we are sure we set everything we wanted to, else -1 */ |
---|
| 355 | static int |
---|
| 356 | finished () |
---|
| 357 | { |
---|
| 358 | int i, j; |
---|
| 359 | |
---|
| 360 | for (i = 0; i < nsets; i++) |
---|
| 361 | for (j = 0; j < sets[i].nev; j++) |
---|
| 362 | if (!sets[i].ev[j].ok) |
---|
| 363 | return (-1); |
---|
| 364 | return (0); |
---|
| 365 | } |
---|
| 366 | |
---|
| 367 | /* called after timeout seconds because we did not find something we trying |
---|
| 368 | * to set. |
---|
| 369 | */ |
---|
| 370 | static void |
---|
| 371 | onAlarm (int dummy) |
---|
| 372 | { |
---|
| 373 | int i, j; |
---|
| 374 | |
---|
| 375 | for (i = 0; i < nsets; i++) |
---|
| 376 | for (j = 0; j < sets[i].nev; j++) |
---|
| 377 | if (!sets[i].ev[j].ok) |
---|
| 378 | fprintf (stderr, "No %s.%s.%s from %s:%d\n", sets[i].d, |
---|
| 379 | sets[i].p, sets[i].ev[j].e, host, port); |
---|
| 380 | |
---|
| 381 | exit (1); |
---|
| 382 | } |
---|
| 383 | |
---|
| 384 | static int |
---|
| 385 | readServerChar (FILE *fp) |
---|
| 386 | { |
---|
| 387 | int c = fgetc (fp); |
---|
| 388 | |
---|
| 389 | if (c == EOF) { |
---|
| 390 | if (ferror(fp)) |
---|
| 391 | perror ("read"); |
---|
| 392 | else |
---|
| 393 | fprintf (stderr,"INDI server %s:%d disconnected\n", host, port); |
---|
| 394 | exit (2); |
---|
| 395 | } |
---|
| 396 | |
---|
| 397 | if (verbose > 2) |
---|
| 398 | fprintf (stderr, "Read %c\n", c); |
---|
| 399 | |
---|
| 400 | return (c); |
---|
| 401 | } |
---|
| 402 | |
---|
| 403 | /* issue a set command if it matches the given property */ |
---|
| 404 | static void |
---|
| 405 | findSet (XMLEle *root, FILE *fp) |
---|
| 406 | { |
---|
| 407 | char *rtype, *rdev, *rprop; |
---|
| 408 | XMLEle *ep; |
---|
| 409 | int t, s, i; |
---|
| 410 | |
---|
| 411 | /* check type */ |
---|
| 412 | rtype = tagXMLEle (root); |
---|
| 413 | for (t = 0; t < NDEFS; t++) |
---|
| 414 | if (strcmp (rtype, defs[t].defType) == 0) |
---|
| 415 | break; |
---|
| 416 | if (t == NDEFS) |
---|
| 417 | return; |
---|
| 418 | alarm (timeout); /* reset timeout */ |
---|
| 419 | |
---|
| 420 | /* check each set for matching device and property name, send if ok */ |
---|
[642] | 421 | rdev = (char *) findXMLAttValu (root, "device"); |
---|
| 422 | rprop = (char *) findXMLAttValu (root, "name"); |
---|
[490] | 423 | if (verbose > 1) |
---|
| 424 | fprintf (stderr, "Read definition for %s.%s\n", rdev, rprop); |
---|
| 425 | for (s = 0; s < nsets; s++) { |
---|
| 426 | if (!strcmp (rdev, sets[s].d) && !strcmp (rprop, sets[s].p)) { |
---|
| 427 | /* found device and name, check perm */ |
---|
| 428 | if (!strchr (findXMLAttValu (root, "perm"), 'w')) { |
---|
| 429 | if (verbose) |
---|
| 430 | fprintf (stderr, "%s.%s is read-only\n", rdev, rprop); |
---|
| 431 | exit (1); |
---|
| 432 | } |
---|
| 433 | /* check matching elements */ |
---|
| 434 | for (i = 0; i < sets[s].nev; i++) { |
---|
| 435 | for (ep = nextXMLEle(root,1); ep; ep = nextXMLEle(root,0)) { |
---|
| 436 | if (!strcmp(findXMLAttValu (ep,"name"),sets[s].ev[i].e) |
---|
| 437 | && !strcmp(tagXMLEle(ep), defs[t].defOne)) { |
---|
| 438 | sets[s].ev[i].ok = 1; |
---|
| 439 | break; |
---|
| 440 | } |
---|
| 441 | } |
---|
| 442 | if (!ep) |
---|
| 443 | return; /* not in this msg, maybe later */ |
---|
| 444 | } |
---|
| 445 | /* all element names found, send new values */ |
---|
| 446 | sendNew (fp, &defs[t], &sets[s]); |
---|
| 447 | } |
---|
| 448 | } |
---|
| 449 | } |
---|
| 450 | |
---|
| 451 | /* send the given set specification of the given INDI type to channel on fp */ |
---|
| 452 | static void |
---|
| 453 | sendNew (FILE *fp, INDIDef *dp, SetSpec *sp) |
---|
| 454 | { |
---|
| 455 | int i; |
---|
| 456 | |
---|
| 457 | fprintf (fp, "<%s device='%s' name='%s'>\n", dp->newType, sp->d, sp->p); |
---|
| 458 | for (i = 0; i < sp->nev; i++) { |
---|
| 459 | if (verbose) |
---|
| 460 | fprintf (stderr, " %s.%s.%s <- %s\n", sp->d, sp->p, |
---|
| 461 | sp->ev[i].e, sp->ev[i].v); |
---|
| 462 | fprintf (fp, " <%s name='%s'>%s</%s>\n", dp->oneType, sp->ev[i].e, |
---|
| 463 | sp->ev[i].v, dp->oneType); |
---|
| 464 | } |
---|
| 465 | fprintf (fp, "</%s>\n", dp->newType); |
---|
| 466 | fflush (fp); |
---|
| 467 | if (feof(fp) || ferror(fp)) { |
---|
| 468 | fprintf (stderr, "Send error\n"); |
---|
| 469 | exit(2); |
---|
| 470 | } |
---|
| 471 | } |
---|
| 472 | |
---|
[504] | 473 | /* scan ev for element definitions in either of two forms and add to sp: |
---|
| 474 | * e1[;e2...]=v1[;v2...] |
---|
| 475 | * or |
---|
| 476 | * e1=v1[;e2=v2...] |
---|
| 477 | * exit if nothing sensible found. |
---|
| 478 | */ |
---|
| 479 | static void |
---|
| 480 | scanEV (SetSpec *specp, char ev[]) |
---|
| 481 | { |
---|
| 482 | char *ep, *sp; /* pointers to = and ; */ |
---|
| 483 | |
---|
| 484 | if (verbose > 1) |
---|
| 485 | fprintf (stderr, "Scanning assignments %s\n", ev); |
---|
| 486 | |
---|
| 487 | ep = strchr (ev, '='); |
---|
| 488 | sp = strchr (ev, ';'); |
---|
| 489 | |
---|
| 490 | if (!ep) { |
---|
| 491 | fprintf (stderr, "Malformed assignment: %s\n", ev); |
---|
| 492 | usage(); |
---|
| 493 | } |
---|
| 494 | |
---|
| 495 | if (sp < ep) |
---|
| 496 | scanEEVV (specp, ep, ev); /* including just one E=V */ |
---|
| 497 | else |
---|
| 498 | scanEVEV (specp, ev); |
---|
| 499 | } |
---|
| 500 | |
---|
| 501 | /* add specs of the form e1[;e2...]=v1[;v2...] to sp. |
---|
| 502 | * v is pointer to equal sign. |
---|
[490] | 503 | * exit if trouble. |
---|
[504] | 504 | * N.B. e[] and v[] are modified in place. |
---|
[490] | 505 | */ |
---|
| 506 | static void |
---|
[504] | 507 | scanEEVV (SetSpec *sp, char *v, char *e) |
---|
[490] | 508 | { |
---|
| 509 | static char sep[] = ";"; |
---|
[504] | 510 | char *ec, *vc; |
---|
[490] | 511 | |
---|
[504] | 512 | *v++ = '\0'; |
---|
[490] | 513 | |
---|
| 514 | while (1) { |
---|
[504] | 515 | char *e0 = strtok_r (e, sep, &ec); |
---|
| 516 | char *v0 = strtok_r (v, sep, &vc); |
---|
[490] | 517 | |
---|
| 518 | if (!e0 && !v0) |
---|
| 519 | break; |
---|
| 520 | if (!e0) { |
---|
[504] | 521 | fprintf (stderr, "More values than elements for %s.%s\n", sp->d, sp->p); |
---|
[490] | 522 | exit(2); |
---|
| 523 | } |
---|
| 524 | if (!v0) { |
---|
[504] | 525 | fprintf (stderr, "More elements than values for %s.%s\n", sp->d, sp->p); |
---|
[490] | 526 | exit(2); |
---|
| 527 | } |
---|
| 528 | |
---|
| 529 | sp->ev = (SetEV *) realloc (sp->ev, (sp->nev+1)*sizeof(SetEV)); |
---|
| 530 | sp->ev[sp->nev].e = strcpy (malloc(strlen(e0)+1), e0); |
---|
| 531 | sp->ev[sp->nev].v = strcpy (malloc(strlen(v0)+1), v0); |
---|
[504] | 532 | if (verbose > 1) |
---|
| 533 | fprintf (stderr, "Found assignment %s=%s\n", sp->ev[sp->nev].e, sp->ev[sp->nev].v); |
---|
[490] | 534 | sp->nev++; |
---|
| 535 | |
---|
| 536 | e = NULL; |
---|
| 537 | v = NULL; |
---|
| 538 | } |
---|
| 539 | } |
---|
| 540 | |
---|
[504] | 541 | /* add specs of the form e1=v1[;e2=v2...] to sp. |
---|
| 542 | * exit if trouble. |
---|
| 543 | * N.B. ev[] is modified in place. |
---|
| 544 | */ |
---|
| 545 | static void |
---|
| 546 | scanEVEV (SetSpec *sp, char ev[]) |
---|
| 547 | { |
---|
| 548 | char *s, *e; |
---|
| 549 | int last = 0; |
---|
| 550 | |
---|
| 551 | do { |
---|
| 552 | s = strchr (ev, ';'); |
---|
| 553 | if (s) |
---|
| 554 | *s++ = '\0'; |
---|
| 555 | else { |
---|
| 556 | s = ev + strlen (ev); |
---|
| 557 | last = 1; |
---|
| 558 | } |
---|
| 559 | e = strchr (ev, '='); |
---|
| 560 | if (e) |
---|
| 561 | *e++ = '\0'; |
---|
| 562 | else { |
---|
| 563 | fprintf (stderr, "Malformed assignment: %s\n", ev); |
---|
| 564 | usage(); |
---|
| 565 | } |
---|
| 566 | |
---|
| 567 | sp->ev = (SetEV *) realloc (sp->ev, (sp->nev+1)*sizeof(SetEV)); |
---|
| 568 | sp->ev[sp->nev].e = strcpy (malloc(strlen(ev)+1), ev); |
---|
| 569 | sp->ev[sp->nev].v = strcpy (malloc(strlen(e)+1), e); |
---|
| 570 | if (verbose > 1) |
---|
| 571 | fprintf (stderr, "Found assignment %s=%s\n", sp->ev[sp->nev].e, sp->ev[sp->nev].v); |
---|
| 572 | sp->nev++; |
---|
| 573 | |
---|
| 574 | ev = s; |
---|
| 575 | |
---|
| 576 | } while (!last); |
---|
| 577 | } |
---|
| 578 | |
---|
[490] | 579 | /* send each SetSpec, all of which have a known type, to wfp |
---|
| 580 | */ |
---|
| 581 | static void |
---|
| 582 | sendSpecs(FILE *wfp) |
---|
| 583 | { |
---|
| 584 | int i; |
---|
| 585 | |
---|
| 586 | for (i = 0; i < nsets; i++) |
---|
| 587 | sendNew (wfp, sets[i].dp, &sets[i]); |
---|
| 588 | } |
---|
| 589 | |
---|
| 590 | /* For RCS Only -- Do Not Edit */ |
---|
[504] | 591 | static char *rcsid[2] = {(char *)rcsid, "@(#) $RCSfile: setINDI.c,v $ $Date: 2010/11/07 07:13:59 $ $Revision: 1.6 $ $Name: $"}; |
---|