GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: src/meet/meetPull.c Lines: 0 402 0.0 %
Date: 2017-05-26 Branches: 0 237 0.0 %

Line Branch Exec Source
1
/*
2
  Teem: Tools to process and visualize scientific data and images             .
3
  Copyright (C) 2013, 2012, 2011, 2010, 2009  University of Chicago
4
  Copyright (C) 2008, 2007, 2006, 2005  Gordon Kindlmann
5
  Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998  University of Utah
6
7
  This library is free software; you can redistribute it and/or
8
  modify it under the terms of the GNU Lesser General Public License
9
  (LGPL) as published by the Free Software Foundation; either
10
  version 2.1 of the License, or (at your option) any later version.
11
  The terms of redistributing and/or modifying this software also
12
  include exceptions to the LGPL that facilitate static linking.
13
14
  This library is distributed in the hope that it will be useful,
15
  but WITHOUT ANY WARRANTY; without even the implied warranty of
16
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17
  Lesser General Public License for more details.
18
19
  You should have received a copy of the GNU Lesser General Public License
20
  along with this library; if not, write to Free Software Foundation, Inc.,
21
  51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
22
*/
23
24
#include "meet.h"
25
26
meetPullVol *
27
meetPullVolNew(void) {
28
  meetPullVol *ret;
29
30
  ret = AIR_CALLOC(1, meetPullVol);
31
  if (ret) {
32
    ret->kind = NULL;
33
    ret->fileName = ret->volName = NULL;
34
    ret->sbp = NULL;
35
    ret->leeching = AIR_FALSE;
36
    ret->derivNormSS = AIR_FALSE;
37
    ret->recomputedSS = AIR_FALSE;
38
    ret->derivNormBiasSS = 0.0;
39
    ret->nin = NULL;
40
    ret->ninSS = NULL;
41
  }
42
  return ret;
43
}
44
45
/*
46
** DOES use biff
47
*/
48
meetPullVol *
49
meetPullVolCopy(const meetPullVol *mpv) {
50
  static const char me[]="meetPullVolCopy";
51
  meetPullVol *ret;
52
  unsigned int si;
53
  airArray *mop;
54
55
  mop = airMopNew();
56
  ret = meetPullVolNew();
57
  airMopAdd(mop, ret, (airMopper)meetPullVolNix, airMopOnError);
58
  /* HEY: hope this is okay for dynamic kinds */
59
  ret->kind = mpv->kind;
60
  ret->fileName = airStrdup(mpv->fileName);
61
  ret->volName = airStrdup(mpv->volName);
62
  if (mpv->sbp) {
63
    ret->sbp = gageStackBlurParmNew();
64
    if (gageStackBlurParmCopy(ret->sbp, mpv->sbp)) {
65
      biffMovef(MEET, GAGE, "%s: problem", me);
66
      airMopError(mop); return NULL;
67
    }
68
  }
69
  ret->leeching = AIR_FALSE;
70
  ret->derivNormSS = mpv->derivNormSS;
71
  ret->recomputedSS = AIR_FALSE;
72
  ret->derivNormBiasSS = mpv->derivNormBiasSS;
73
  if (mpv->sbp) {
74
    ret->nin = NULL;
75
    ret->ninSS = AIR_CALLOC(ret->sbp->num, Nrrd *);
76
    for (si=0; si<mpv->sbp->num; si++) {
77
      ret->ninSS[si] = nrrdNew();
78
      if (nrrdCopy(ret->ninSS[si], mpv->ninSS[si])) {
79
        biffMovef(MEET, NRRD, "%s: problem with ninSS[%u]", me, si);
80
        airMopError(mop); return NULL;
81
      }
82
    }
83
  } else {
84
    ret->nin = nrrdNew();
85
    if (nrrdCopy(ret->nin, mpv->nin)) {
86
      biffMovef(MEET, NRRD, "%s: problem with nin", me);
87
      airMopError(mop); return NULL;
88
    }
89
    ret->ninSS = NULL;
90
  }
91
  airMopOkay(mop);
92
  return ret;
93
}
94
95
/*
96
******** meetPullVolParse
97
**
98
** parses a string to extract all the information necessary to create
99
** the pullVolume (this function originated in Deft/src/main-pull.c)
100
*/
101
int
102
meetPullVolParse(meetPullVol *mpv, const char *_str) {
103
  static const char me[]="meetPullVolParse";
104
#define VFMT_SHRT "<fileName>:<kind>:<volName>"
105
  /* there are other flags and parms but these are the main ones */
106
#define SFMT "<minScl>-<#smp>-<maxScl>[-n][/k=kss][/b=bspec][/s=smpling]"
107
#define VFMT_LONG "<fileName>:<kind>:" SFMT ":<volName>"
108
  char *str, *ctok, *clast=NULL;
109
  airArray *mop;
110
  int wantSS;
111
  unsigned int ctokn;
112
113
  if (!(mpv && _str)) {
114
    biffAddf(MEET, "%s: got NULL pointer", me);
115
    return 1;
116
  }
117
  if (!( str = airStrdup(_str) )) {
118
    biffAddf(MEET, "%s: couldn't strdup input", me);
119
    return 1;
120
  }
121
122
  mop = airMopNew();
123
  airMopAdd(mop, str, airFree, airMopAlways);
124
  ctokn = airStrntok(str, ":");
125
  /* An annoying side-effect of putting the blurring kernel specification and
126
     boundary specification inside the string representation of the
127
     gageStackBlurParm, is that the colon in dg:1,6" or "pad:10" is now
128
     confused as a delimiter in the (e.g.) "vol.nrrd:scalar:0-8-5.5:V" string
129
     representation of meetPullVol, as in
130
     "vol.nrrd:scalar:0-8-5.5/k=dg:1,6/b=pad:1:V". So we have to be more
131
     permissive in the number of tokens (hacky) */
132
  if (!( ctokn >= 3 )) {
133
    biffAddf(MEET, "%s: didn't get at least 3 \":\"-separated tokens in "
134
             "\"%s\"; not of form " VFMT_SHRT " or " VFMT_LONG , me, _str);
135
    airMopError(mop); return 1;
136
  }
137
  wantSS = (ctokn > 3);
138
139
  ctok = airStrtok(str, ":", &clast);
140
  if (!(mpv->fileName = airStrdup(ctok))) {
141
    biffAddf(MEET, "%s: couldn't strdup fileName", me);
142
    airMopError(mop); return 1;
143
  }
144
  airMopAdd(mop, &(mpv->fileName), (airMopper)airSetNull, airMopOnError);
145
  airMopAdd(mop, mpv->fileName, airFree, airMopOnError);
146
  ctok = airStrtok(NULL, ":", &clast);
147
  if (!(mpv->kind = meetConstGageKindParse(ctok))) {
148
    biffAddf(MEET, "%s: couldn't parse \"%s\" as kind", me, ctok);
149
    airMopError(mop); return 1;
150
  }
151
  if (wantSS) {
152
    int extraFlag[256]; char *extraParm=NULL, *ptok, *plast;
153
    unsigned int efi, cti;
154
    char *sbps;
155
    /* the hack to make the ":" inside a blurring kernel specification or
156
       boundary specification be unlike the ":" that delimits the real
157
       meetPullVol fields */
158
    sbps = airStrdup(_str); /* to have a buffer big enough */
159
    airMopAdd(mop, sbps, airFree, airMopAlways);
160
    strcpy(sbps, "");
161
    for (cti=0; cti<ctokn-3; cti++) {
162
      if (cti) {
163
        strcat(sbps, ":");
164
      }
165
      ctok = airStrtok(NULL, ":", &clast);
166
      strcat(sbps, ctok);
167
    }
168
    mpv->sbp = gageStackBlurParmNix(mpv->sbp);
169
    mpv->sbp = gageStackBlurParmNew();
170
    if (gageStackBlurParmParse(mpv->sbp, extraFlag, &extraParm, sbps)) {
171
      biffMovef(MEET, GAGE, "%s: problem parsing sbp from \"%s\"", me, sbps);
172
      airMopError(mop); return 1;
173
    }
174
    mpv->derivNormSS = !!extraFlag['n'];
175
    extraFlag['n'] = AIR_FALSE;
176
    for (efi=0; efi<256; efi++) {
177
      if (extraFlag[AIR_CAST(unsigned char, efi)]) {
178
        biffAddf(MEET, "%s: got unknown extra flag '%c' in \"%s\"", me,
179
                 AIR_CAST(char, efi), sbps);
180
        airMopError(mop); return 1;
181
      }
182
    }
183
    if (extraParm) {
184
      unsigned int pmi, pmn;
185
      static const char dnbiase[]="dnbias=";
186
      airMopAdd(mop, extraParm, airFree, airMopAlways);
187
      pmn = airStrntok(extraParm, "/");
188
      for (pmi=0; pmi<pmn; pmi++) {
189
        ptok = airStrtok(!pmi ? extraParm : NULL, "/", &plast);
190
        if (strstr(ptok, dnbiase) == ptok) {
191
          if (1 != sscanf(ptok + strlen(dnbiase), "%lg",
192
                          &(mpv->derivNormBiasSS))) {
193
            biffAddf(MEET, "%s: couldn't parse \"%s\" as double in \"%s\"",
194
                     me, ptok + strlen(dnbiase), ptok);
195
            airMopError(mop); return 1;
196
          }
197
        } else {
198
          biffAddf(MEET, "%s: got unknown extra parm %s in \"%s\"",
199
                   me, ptok, extraParm);
200
          airMopError(mop); return 1;
201
        }
202
      }
203
    }
204
  } else {
205
    /* no scale-space stuff wanted */
206
    mpv->sbp = NULL;
207
    mpv->ninSS = NULL;
208
  }
209
  ctok = airStrtok(NULL, ":", &clast);
210
  if (!(mpv->volName = airStrdup(ctok))) {
211
    biffAddf(MEET, "%s: couldn't strdup volName", me);
212
    airMopError(mop); return 1;
213
  }
214
  airMopAdd(mop, &(mpv->volName), (airMopper)airSetNull, airMopOnError);
215
  airMopAdd(mop, mpv->volName, airFree, airMopOnError);
216
217
  if (strchr(ctok, '-')) {
218
    biffAddf(MEET, "%s: you probably don't want \"-\" in volume name \"%s\"; "
219
             "forgot last \":\" in scale sampling specification?", me, ctok);
220
    airMopError(mop); return 1;
221
  }
222
223
  airMopOkay(mop);
224
  return 0;
225
}
226
227
int
228
meetHestPullVolParse(void *ptr, char *str, char err[AIR_STRLEN_HUGE]) {
229
  static const char me[]="meetHestPullVolParse";
230
  meetPullVol *mpv, **mpvP;
231
  airArray *mop;
232
233
  if (!(ptr && str)) {
234
    sprintf(err, "%s: got NULL pointer", me);
235
    return 1;
236
  }
237
  mop = airMopNew();
238
  mpvP = AIR_CAST(meetPullVol **, ptr);
239
  *mpvP = mpv = meetPullVolNew();
240
  airMopAdd(mop, mpvP, (airMopper)airSetNull, airMopOnError);
241
  airMopAdd(mop, mpv, (airMopper)meetPullVolNix, airMopOnError);
242
  if (meetPullVolParse(mpv, str)) {
243
    char *ler;
244
    airMopAdd(mop, ler = biffGetDone(MEET), airFree, airMopOnError);
245
    airStrcpy(err, AIR_STRLEN_HUGE, ler);
246
    airMopError(mop);
247
    return 1;
248
  }
249
  airMopOkay(mop);
250
  return 0;
251
}
252
253
/*
254
******** meetPullVolNix
255
**
256
** this frees stuff allocated meetPullVolParse and meetPullVolLoadMulti
257
*/
258
meetPullVol *
259
meetPullVolNix(meetPullVol *mpv) {
260
261
  if (mpv) {
262
    if (!mpv->leeching && mpv->nin) {
263
      nrrdNuke(mpv->nin);
264
    }
265
    if (mpv->sbp) {
266
      unsigned int ssi;
267
      if (mpv->ninSS) {
268
        /* need this check because the mpv may not have benefitted
269
           from meetPullVolLoadMulti, so it might be incomplete */
270
        for (ssi=0; ssi<mpv->sbp->num; ssi++) {
271
          if (!mpv->leeching) {
272
            nrrdNuke(mpv->ninSS[ssi]);
273
          }
274
        }
275
        airFree(mpv->ninSS);
276
      }
277
      gageStackBlurParmNix(mpv->sbp);
278
    }
279
    airFree(mpv->fileName);
280
    airFree(mpv->volName);
281
    airFree(mpv);
282
  }
283
  return NULL;
284
}
285
286
hestCB
287
_meetHestPullVol = {
288
  sizeof(meetPullVol *),
289
  "meetPullVol",
290
  meetHestPullVolParse,
291
  (airMopper)meetPullVolNix,
292
};
293
294
hestCB *
295
meetHestPullVol = &_meetHestPullVol;
296
297
/*
298
******** meetPullVolLeechable
299
**
300
** indicates whether lchr can leech from orig (saved in *can), and if not,
301
** explanation is saved in explain (if non-NULL)
302
**
303
** always uses biff
304
*/
305
int
306
meetPullVolLeechable(const meetPullVol *lchr,
307
                     const meetPullVol *orig,
308
                     int *can, char explain[AIR_STRLEN_LARGE]) {
309
  static const char me[]="meetPullVolLeechable";
310
  char subexplain[AIR_STRLEN_LARGE];
311
312
  if (!( lchr && orig && can )) {
313
    biffAddf(MEET, "%s: got NULL pointer (%p %p %p)", me, lchr, orig, can);
314
    return 1;
315
  }
316
  /* can leech, if not reading from stdin */
317
  *can = !!strcmp(orig->fileName, "-");
318
  if (!*can) {
319
    if (explain) {
320
      sprintf(explain, "original loaded from stdin");
321
    }
322
    return 0;
323
  }
324
  /* can, if coming from same file */
325
  *can = !strcmp(orig->fileName, lchr->fileName);
326
  if (!*can) {
327
    if (explain) {
328
      sprintf(explain, "not from same file (\"%s\" vs \"%s\")\n",
329
              lchr->fileName, orig->fileName);
330
    }
331
    return 0;
332
  }
333
  /* can, if same kind */
334
  *can = (orig->kind == lchr->kind);
335
  if (!*can) {
336
    if (explain) {
337
      sprintf(explain, "not same kind (%s vs %s)\n",
338
              lchr->kind->name, orig->kind->name);
339
    }
340
    return 0;
341
  }
342
  /* can, if both using or both not using scale-space */
343
  *can = (!!lchr->sbp == !!orig->sbp);
344
  if (!*can) {
345
    if (explain) {
346
      sprintf(explain, "not agreeing on use of scale-space (%s vs %s)\n",
347
              lchr->sbp ? "yes" : "no", orig->sbp ? "yes" : "no");
348
    }
349
    return 0;
350
  }
351
  if (orig->sbp) {
352
    int differ;
353
    if (gageStackBlurParmCompare(lchr->sbp, "potential leecher",
354
                                 orig->sbp, "original",
355
                                 &differ, subexplain)) {
356
      biffAddf(MEET, "%s: problem comparing sbps", me);
357
      return 1;
358
    }
359
    if (differ) {
360
      if (explain) {
361
        sprintf(explain, "different uses of scale-space: %s", subexplain);
362
      }
363
      *can = AIR_FALSE;
364
      return 0;
365
    }
366
  }
367
  /* DO allow difference in derivNormSS (the main reason for leeching),
368
     as well as derivNormBiasSS */
369
  /* no differences so far */
370
  *can = AIR_TRUE;
371
  return 0;
372
}
373
374
void
375
meetPullVolLeech(meetPullVol *vol,
376
                 const meetPullVol *volPrev) {
377
378
  if (vol && volPrev) {
379
    vol->nin = volPrev->nin;
380
    if (vol->sbp) {
381
      unsigned int ni;
382
      /* have to allocate ninSS here; in volPrev it was probably allocated
383
         by gageStackBlurManage */
384
      vol->ninSS = AIR_CALLOC(vol->sbp->num, Nrrd *);
385
      for (ni=0; ni<vol->sbp->num; ni++) {
386
        vol->ninSS[ni] = volPrev->ninSS[ni];
387
      }
388
    }
389
    vol->leeching = AIR_TRUE;
390
  }
391
  return;
392
}
393
394
/*
395
** This is kind of a sad function. The big re-write of gageStackBlurParm in
396
** late August 2013 was motivated by the frustration of how there was no
397
** centralized and consistent way of representing (by text or by command-line
398
** options) all the things that determine scale-space "stack" creation.
399
** Having re-organized gageStackBlurParm, the meetPullVol was re-organized to
400
** include one inside, which is clearly better than the previous reduplication
401
** of the stack blur parms inside the meetPullVol. Parsing the meetPullVol
402
** from the command-line (as in done in puller) highlights the annoying fact
403
** that hest wants to be the origin of information: you can't have hest
404
** supplement existing information with whatever it learns from the
405
** command-line, especially when hest is parsing 1 or more of something, and
406
** especially when the existing information would be coming from other
407
** command-line arguments.
408
**
409
** So, this sad function says, "ok all you meetPullVol parsed from the
410
** command-line: if you don't already have a boundary or a kernel set, here's
411
** one to use". What makes it sad is how the whole point of the
412
** gageStackBlurParm re-org was that knowledge about the internals of the
413
** gageStackBlurParm was now going to be entirely localized in gage. But here
414
** we are listing off two of its fields as parameters to this function, which
415
** means its API might change the next time the gageStackBlurParm is updated.
416
**
417
** To help keep track of what info was actually used, *kssSetP and *bspSetP
418
** (if non-NULL) are set to the number of kernel and boundary specs that are
419
** "finished" in this way.
420
*/
421
int
422
meetPullVolStackBlurParmFinishMulti(meetPullVol **mpv, unsigned int mpvNum,
423
                                    unsigned int *kssSetP,
424
                                    unsigned int *bspSetP,
425
                                    const NrrdKernelSpec *kssblur,
426
                                    const NrrdBoundarySpec *bspec) {
427
  static const char me[]="meetPullVolStackBlurParmFinishMulti";
428
  unsigned int ii, kssSet, bspSet;
429
430
  if (!mpv || !mpvNum) {
431
    biffAddf(MEET, "%s: got NULL mpv (%p) or 0 mpvNum (%u)",
432
             me, AIR_VOIDP(mpv), mpvNum);
433
    return 1;
434
  }
435
  kssSet = bspSet = 0;
436
  for (ii=0; ii<mpvNum; ii++) {
437
    if (kssblur && mpv[ii]->sbp && !(mpv[ii]->sbp->kspec)) {
438
      if (gageStackBlurParmKernelSet(mpv[ii]->sbp, kssblur)) {
439
        biffMovef(MEET, GAGE, "%s: trouble w/ kernel on mpv[%u]", me, ii);
440
        return 1;
441
      }
442
      kssSet++;
443
    }
444
    if (bspec && mpv[ii]->sbp && !(mpv[ii]->sbp->bspec)) {
445
      if (gageStackBlurParmBoundarySpecSet(mpv[ii]->sbp, bspec)) {
446
        biffMovef(MEET, GAGE, "%s: trouble w/ boundary on mpv[%u]", me, ii);
447
        return 1;
448
      }
449
      bspSet++;
450
    }
451
  }
452
  if (kssSetP) {
453
    *kssSetP = kssSet;
454
  }
455
  if (bspSetP) {
456
    *bspSetP = bspSet;
457
  }
458
  return 0;
459
}
460
461
/*
462
******** meetPullVolLoadMulti
463
**
464
** at this point the only per-pullVolume information required for
465
** loading/creating the volumes, which isn't already in the
466
** meetPullVol, is the cachePath, so that is passed explicitly.
467
*/
468
int
469
meetPullVolLoadMulti(meetPullVol **mpv, unsigned int mpvNum,
470
                     char *cachePath, int verbose) {
471
  static const char me[]="meetPullVolLoadMulti";
472
  unsigned int mpvIdx;
473
  airArray *mop;
474
  meetPullVol *vol;
475
476
  if (!( mpv && cachePath)) {
477
    biffAddf(MEET, "%s: got NULL pointer", me);
478
    return 1;
479
  }
480
  mop = airMopNew();
481
  for (mpvIdx=0; mpvIdx<mpvNum; mpvIdx++) {
482
    unsigned int pvi;
483
    int leechable;
484
    char explain[AIR_STRLEN_LARGE];
485
    vol = mpv[mpvIdx];
486
    for (pvi=0; pvi<mpvIdx; pvi++) {
487
      if (meetPullVolLeechable(vol, mpv[pvi], &leechable, explain)) {
488
        biffAddf(MEET, "%s: problem testing leechable(v[%u]->v[%u])",
489
                 me, mpvIdx, pvi);
490
        return 1;
491
      }
492
      if (leechable) {
493
        meetPullVolLeech(vol, mpv[pvi]);
494
        break; /* prevent a chain of leeching */
495
      } else {
496
        if (verbose) {
497
          fprintf(stderr, "%s: mpv[%u] cannot leech mpv[%u]: %s\n", me,
498
                  mpvIdx, pvi, explain);
499
        }
500
      }
501
    }
502
    if (pvi < mpvIdx) {
503
      /* we leeched this one, move on */
504
      if (verbose) {
505
        fprintf(stderr, "%s: vspec[%u] (%s) leeching off vspec[%u] (%s)\n",
506
                me, mpvIdx, vol->volName, pvi, mpv[pvi]->volName);
507
      }
508
      continue;
509
    }
510
    /* else we're not leeching */
511
    vol->leeching = AIR_FALSE;
512
    vol->nin = nrrdNew();
513
    airMopAdd(mop, &(vol->nin), (airMopper)airSetNull, airMopOnError);
514
    airMopAdd(mop, vol->nin, (airMopper)nrrdNuke, airMopOnError);
515
    if (nrrdLoad(vol->nin, vol->fileName, NULL)) {
516
      biffMovef(MEET, NRRD, "%s: trouble loading mpv[%u]->nin (\"%s\")",
517
                me, mpvIdx, vol->volName);
518
      airMopError(mop); return 1;
519
    }
520
    if (vol->sbp) {
521
      char formatSS[AIR_STRLEN_LARGE];
522
      sprintf(formatSS, "%s/%s-%%03u-%03u.nrrd",
523
              cachePath, vol->volName, vol->sbp->num);
524
      if (verbose) {
525
        fprintf(stderr, "%s: managing %s ... \n", me, formatSS);
526
      }
527
      if (gageStackBlurManage(&(vol->ninSS), &(vol->recomputedSS), vol->sbp,
528
                              formatSS, AIR_TRUE, NULL,
529
                              vol->nin, vol->kind)) {
530
        biffMovef(MEET, GAGE, "%s: trouble getting volume stack (\"%s\")",
531
                  me, formatSS);
532
        airMopError(mop); return 1;
533
      }
534
      if (verbose) {
535
        fprintf(stderr, "%s: ... done\n", me);
536
      }
537
    }
538
  }
539
  airMopOkay(mop);
540
  return 0;
541
}
542
543
/*
544
******** meetPullVolAddMulti
545
**
546
** the spatial (k00, k11, k22) and scale (kSSrecon) reconstruction
547
** kernels are not (yet) part of the meetPullVol, so have to be passed
548
** in here
549
*/
550
int
551
meetPullVolAddMulti(pullContext *pctx,
552
                    meetPullVol **mpv, unsigned int mpvNum,
553
                    const NrrdKernelSpec *k00,
554
                    const NrrdKernelSpec *k11,
555
                    const NrrdKernelSpec *k22,
556
                    const NrrdKernelSpec *kSSrecon) {
557
  static const char me[]="meetPullVolAddMulti";
558
  unsigned int mpvIdx;
559
560
  if (!( pctx && mpv )) {
561
    biffAddf(MEET, "%s: got NULL pointer", me);
562
    return 1;
563
  }
564
  for (mpvIdx=0; mpvIdx<mpvNum; mpvIdx++) {
565
    meetPullVol *vol;
566
    int E;
567
    vol = mpv[mpvIdx];
568
    if (!vol->sbp) {
569
      E = pullVolumeSingleAdd(pctx, vol->kind, vol->volName,
570
                              vol->nin, k00, k11, k22);
571
    } else {
572
      E = pullVolumeStackAdd(pctx, vol->kind, vol->volName, vol->nin,
573
                             AIR_CAST(const Nrrd *const *,
574
                                      vol->ninSS),
575
                             vol->sbp->sigma, vol->sbp->num,
576
                             vol->derivNormSS, vol->derivNormBiasSS,
577
                             k00, k11, k22, kSSrecon);
578
    }
579
    if (E) {
580
      biffMovef(MEET, PULL, "%s: trouble adding volume %u/%u (\"%s\")",
581
                me, mpvIdx, mpvNum, vol->volName);
582
      return 1;
583
    }
584
  }
585
586
  return 0;
587
}
588
589
meetPullInfo *
590
meetPullInfoNew(void) {
591
  meetPullInfo *ret;
592
593
  ret = AIR_CALLOC(1, meetPullInfo);
594
  if (ret) {
595
    ret->info = 0;
596
    ret->source = pullSourceUnknown;
597
    ret->prop = pullPropUnknown;
598
    ret->constraint = AIR_FALSE;
599
    ret->volName = ret->itemStr = NULL;
600
    ret->zero = ret->scale = AIR_NAN;
601
  }
602
  return ret;
603
}
604
605
meetPullInfo *
606
meetPullInfoNix(meetPullInfo *minf) {
607
608
  if (minf) {
609
    airFree(minf->volName);
610
    airFree(minf->itemStr);
611
    free(minf);
612
  }
613
  return NULL;
614
}
615
616
static int
617
zeroScaleSet(meetPullInfo *minf, int haveZS, char **lastP) {
618
  static const char me[]="_zeroScaleSet";
619
  char *tok;
620
621
  if (haveZS) {
622
    tok = airStrtok(NULL, ":", lastP);
623
    if (1 != sscanf(tok, "%lf", &(minf->zero))) {
624
      biffAddf(MEET, "%s: couldn't parse %s as zero (double)", me, tok);
625
      return 1;
626
    }
627
    tok = airStrtok(NULL, ":", lastP);
628
    if (1 != sscanf(tok, "%lf", &(minf->scale))) {
629
      biffAddf(MEET, "%s: couldn't parse %s as scale (double)", me, tok);
630
      return 1;
631
    }
632
  } else {
633
    minf->zero = minf->scale = AIR_NAN;
634
  }
635
  return 0;
636
}
637
638
int
639
meetPullInfoParse(meetPullInfo *minf, const char *_str) {
640
  static const char me[]="meetPullInfoParse";
641
#define IFMT_GAGE "<info>[-c]:<volname>:<item>[:<zero>:<scale>]"
642
#define IFMT_PROP "<info>:prop=<prop>[:<zero>:<scale>]"
643
#define PROP_PREFIX "prop="   /* has to end with = */
644
  char *str, *tok, *last=NULL, *iflags;
645
  airArray *mop;
646
  int haveZS, source;
647
648
  if (!(minf && _str)) {
649
    biffAddf(MEET, "%s: got NULL pointer", me);
650
    return 1;
651
  }
652
  if ( (3 == airStrntok(_str, ":") || 5 == airStrntok(_str, ":"))
653
       && 1 == airStrntok(_str, "=") ) {
654
    source = pullSourceGage;
655
    haveZS = (5 == airStrntok(_str, ":"));
656
  } else if ( (2 == airStrntok(_str, ":") || 4 == airStrntok(_str, ":"))
657
              && 2 == airStrntok(_str, "=") ) {
658
    source = pullSourceProp;
659
    haveZS = (4 == airStrntok(_str, ":"));
660
  } else {
661
    biffAddf(MEET, "%s: \"%s\" not of form " IFMT_GAGE " or " IFMT_PROP,
662
             me, _str);
663
    return 1;
664
  }
665
666
  mop = airMopNew();
667
  if (!( str = airStrdup(_str) )) {
668
    biffAddf(MEET, "%s: couldn't strdup input", me);
669
    return 1;
670
  }
671
  airMopAdd(mop, str, airFree, airMopAlways);
672
673
  minf->source = source;
674
  if (pullSourceGage == source) {
675
    tok = airStrtok(str, ":", &last);
676
    iflags = strchr(tok, '-');
677
    if (iflags) {
678
      *iflags = '\0';
679
      iflags++;
680
    }
681
    if (!(minf->info = airEnumVal(pullInfo, tok))) {
682
      biffAddf(MEET, "%s: couldn't parse \"%s\" as %s",
683
               me, tok, pullInfo->name);
684
      airMopError(mop); return 1;
685
    }
686
    if (iflags) {
687
      if (strchr(iflags, 'c')) {
688
        minf->constraint = AIR_TRUE;
689
      }
690
    }
691
    tok = airStrtok(NULL, ":", &last);
692
    airFree(minf->volName);
693
    minf->volName = airStrdup(tok);
694
    airMopAdd(mop, minf->volName, airFree, airMopOnError);
695
    tok = airStrtok(NULL, ":", &last);
696
    airFree(minf->itemStr);
697
    minf->itemStr = airStrdup(tok);
698
    airMopAdd(mop, minf->itemStr, airFree, airMopOnError);
699
    if (zeroScaleSet(minf, haveZS, &last)) {
700
      biffAddf(MEET, "%s: couldn't parse zero or scale",  me);
701
      airMopError(mop); return 1;
702
    }
703
  } else if (pullSourceProp == source) {
704
    /* "<info>:prop=<prop>[:<zero>:<scale>]" */
705
    tok = airStrtok(str, ":", &last);
706
    if (!(minf->info = airEnumVal(pullInfo, tok))) {
707
      biffAddf(MEET, "%s: couldn't parse \"%s\" as %s",
708
               me, tok, pullInfo->name);
709
      airMopError(mop); return 1;
710
    }
711
    tok = airStrtok(NULL, ":", &last);
712
    if (strncmp(PROP_PREFIX, tok, strlen(PROP_PREFIX))) {
713
      biffAddf(MEET, "%s: property info didn't start with %s",
714
               me, PROP_PREFIX);
715
    }
716
    tok += strlen(PROP_PREFIX);
717
    if (!(minf->prop = airEnumVal(pullProp, tok))) {
718
      biffAddf(MEET, "%s: couldn't parse \"%s\" as %s",
719
               me, tok, pullProp->name);
720
      airMopError(mop); return 1;
721
    }
722
    if (zeroScaleSet(minf, haveZS, &last)) {
723
      biffAddf(MEET, "%s: couldn't parse zero or scale",  me);
724
      airMopError(mop); return 1;
725
    }
726
  } else {
727
    biffAddf(MEET, "%s: sorry, source %s not handled",
728
             me, airEnumStr(pullSource, source));
729
    airMopError(mop); return 1;
730
  }
731
732
  airMopOkay(mop);
733
  return 0;
734
}
735
736
int
737
meetHestPullInfoParse(void *ptr, char *str, char err[AIR_STRLEN_HUGE]) {
738
  static const char me[]="meetHestPullInfoParse";
739
  airArray *mop;
740
  meetPullInfo **minfP, *minf;
741
742
  if (!(ptr && str)) {
743
    sprintf(err, "%s: got NULL pointer", me);
744
    return 1;
745
  }
746
  mop = airMopNew();
747
  minfP = AIR_CAST(meetPullInfo **, ptr);
748
  *minfP = minf = meetPullInfoNew();
749
  airMopAdd(mop, minfP, (airMopper)airSetNull, airMopOnError);
750
  airMopAdd(mop, minf, (airMopper)meetPullInfoNix, airMopOnError);
751
  if (meetPullInfoParse(minf, str)) {
752
    char *ler;
753
    airMopAdd(mop, ler = biffGetDone(MEET), airFree, airMopOnError);
754
    airStrcpy(err, AIR_STRLEN_HUGE, ler);
755
    airMopError(mop);
756
    return 1;
757
  }
758
  airMopOkay(mop);
759
  return 0;
760
}
761
762
hestCB
763
_meetHestPullInfo = {
764
  sizeof(meetPullInfo *),
765
  "meetPullInfo",
766
  meetHestPullInfoParse,
767
  (airMopper)meetPullInfoNix
768
};
769
770
hestCB *
771
meetHestPullInfo = &_meetHestPullInfo;
772
773
int
774
meetPullInfoAddMulti(pullContext *pctx,
775
                     meetPullInfo **minf, unsigned int minfNum) {
776
  static const char me[]="meetPullInfoAddMulti";
777
  const pullVolume *vol;
778
  unsigned int ii;
779
  airArray *mop;
780
781
  if (!( pctx && minf )) {
782
    biffAddf(MEET, "%s: got NULL pointer", me);
783
    return 1;
784
  }
785
786
  mop = airMopNew();
787
  for (ii=0; ii<minfNum; ii++) {
788
    pullInfoSpec *ispec;
789
    ispec = pullInfoSpecNew();
790
    airMopAdd(mop, ispec, (airMopper)pullInfoSpecNix, airMopOnError);
791
    ispec->volName = airStrdup(minf[ii]->volName);
792
    ispec->source = minf[ii]->source;
793
    ispec->info = minf[ii]->info;
794
    ispec->prop = minf[ii]->prop;
795
    ispec->zero = minf[ii]->zero;
796
    ispec->scale = minf[ii]->scale;
797
    ispec->constraint = minf[ii]->constraint;
798
    /* the item is the one thing that takes some work to recover;
799
       we need to find the volume and find the item from its kind->enm */
800
    if (pullSourceGage == ispec->source) {
801
      if (!( vol = pullVolumeLookup(pctx, minf[ii]->volName) )) {
802
        biffMovef(MEET, PULL, "%s: can't find volName \"%s\" for minf[%u]",
803
                  me, minf[ii]->volName, ii);
804
        airMopError(mop); return 1;
805
      }
806
      if (!( ispec->item = airEnumVal(vol->kind->enm, minf[ii]->itemStr))) {
807
        biffAddf(MEET, "%s: can't parse \"%s\" as item of %s kind (minf[%u])\n",
808
                 me, minf[ii]->itemStr, vol->kind->name, ii);
809
        airMopError(mop); return 1;
810
      }
811
    }
812
    if (pullInfoSpecAdd(pctx, ispec)) {
813
      biffMovef(MEET, PULL, "%s: trouble adding ispec from minf[%u]", me, ii);
814
      airMopError(mop); return 1;
815
    }
816
    /* else added the ispec okay. If we have an error with a different
817
       ispec later in this loop, who's job is it to free up the ispecs
818
       that have been added successfully?  In teem/src/bin/puller, that
819
       is done by pullContextNix. So we now extricate ourself from the
820
       business of freeing this ispec in case of error; one of the few
821
       times that airMopSub is really needed */
822
    airMopSub(mop, ispec, (airMopper)pullInfoSpecNix);
823
  }
824
825
  airMopOkay(mop);
826
  return 0;
827
}