ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/util/wrapBSDF.c
Revision: 2.1
Committed: Wed Feb 11 18:08:10 2015 UTC (9 years, 1 month ago) by greg
Content type: text/plain
Branch: MAIN
Log Message:
Checking in first compilable version of new program

File Contents

# User Rev Content
1 greg 2.1 #ifndef lint
2     static const char RCSid[] = "$Id$";
3     #endif
4     /*
5     * Wrap BSDF data in valid WINDOW XML wrapper
6     *
7     * G. Ward January 2015
8     */
9    
10     #include <ctype.h>
11     #include "rtio.h"
12     #include "rtprocess.h"
13     #include "ezxml.h"
14     #include "bsdf.h"
15     #include "bsdf_m.h"
16     /* XML template file names */
17     const char def_template[] = "minimalBSDFt.xml";
18     const char win6_template[] = "window6BSDFt.xml";
19    
20     const char stdin_name[] = "<stdin>";
21    
22     /* input files (can be stdin_name) */
23     const char *xml_input = NULL;
24     /* unit for materials & geometry */
25     const char *attr_unit = "millimeter";
26     const char legal_units[] = "meter|foot|inch|centimeter|millimeter";
27     /* system materials & geometry */
28     const char *mgf_geometry = NULL;
29    
30     /* angle basis */
31     enum { ABdefault=-1, ABklemsFull=0, ABklemsHalf, ABklemsQuarter,
32     ABtensorTree3, ABtensorTree4, ABend };
33    
34     int angle_basis = ABdefault;
35    
36     /* field IDs and nicknames */
37     struct s_fieldID {
38     char nickName[4];
39     int has_unit;
40     const char *fullName;
41     } XMLfieldID[] = {
42     {"m", 0, "Manufacturer"},
43     {"n", 0, "Name"},
44     {"c", 0, "ThermalConductivity"},
45     {"ef", 0, "EmissivityFront"},
46     {"eb", 0, "EmissivityBack"},
47     {"tir", 0, "TIR"},
48     {"eo", 0, "EffectiveOpennessFraction"},
49     {"t", 1, "Thickness"},
50     {"h", 1, "Height"},
51     {"w", 1, "Width"},
52     {"\0", -1, NULL} /* terminator */
53     };
54     /* field assignments */
55     #define MAXASSIGN 12
56     const char *field_assignment[MAXASSIGN];
57     int nfield_assign = 0;
58     #define FASEP ';'
59    
60     /* data file(s) & spectra */
61     enum { DTtransForward, DTtransBackward, DTreflForward, DTreflBackward };
62    
63     enum { DSsolar=-1, DSnir=-2, DSxbar31=-3, DSvisible=-4, DSzbar31=-5 };
64    
65     #define MAXFILES 20
66    
67     struct s_dfile {
68     const char *fname; /* input data file name */
69     short type; /* BSDF data type */
70     short spectrum; /* BSDF sensor spectrum */
71     } data_file[MAXFILES];
72    
73     int ndataf = 0; /* number of data files */
74    
75     const char *spectr_file[MAXFILES]; /* custom spectral curve input */
76    
77     const char top_level_name[] = "WindowElement";
78    
79     static char *basis_definition[] = {
80    
81     "\t\t<IncidentDataStructure>Columns</IncidentDataStructure>\n"
82     "\t\t<AngleBasis>\n"
83     "\t\t\t<AngleBasisName>LBNL/Klems Full</AngleBasisName>\n"
84     "\t\t</AngleBasis>\n",
85    
86     "\t\t<IncidentDataStructure>Columns</IncidentDataStructure>\n"
87     "\t\t<AngleBasis>\n"
88     "\t\t\t<AngleBasisName>LBNL/Klems Half</AngleBasisName>\n"
89     "\t\t</AngleBasis>\n",
90    
91     "\t\t<IncidentDataStructure>Columns</IncidentDataStructure>\n"
92     "\t\t<AngleBasis>\n"
93     "\t\t\t<AngleBasisName>LBNL/Klems Quarter</AngleBasisName>\n"
94     "\t\t</AngleBasis>\n",
95    
96     "\t\t<IncidentDataStructure>TensorTree3</IncidentDataStructure>\n",
97    
98     "\t\t<IncidentDataStructure>TensorTree4</IncidentDataStructure>\n",
99     };
100    
101     /* Copy data from file descriptor to stdout and close */
102     static int
103     copy_and_close(int fd)
104     {
105     int ok = 1;
106     char buf[8192];
107     int n;
108    
109     if (fd < 0)
110     return 0;
111     while ((n = read(fd, buf, sizeof(buf))) > 0)
112     if (write(fileno(stdout), buf, n) != n) {
113     ok = 0;
114     break;
115     }
116     ok &= (n == 0);
117     close(fd);
118     return ok;
119     }
120    
121     /* Allocate and assign string from file or stream */
122     static char *
123     input2str(const char *inpspec)
124     {
125     FILE *fp = NULL;
126     char *str;
127     int len, pos, n;
128    
129     if (inpspec == NULL || !*inpspec)
130     return "";
131     if (inpspec == stdin_name) { /* read from stdin */
132     fp = stdin;
133     } else if (*inpspec == '!') { /* read from command */
134     fp = popen(inpspec+1, "r");
135     if (fp == NULL) {
136     fprintf(stderr, "Cannot start process '%s'\n",
137     inpspec+1);
138     return "";
139     }
140     } else { /* else load file */
141     int fd = open(inpspec, O_RDONLY);
142     if (fd < 0) {
143     fprintf(stderr, "%s: cannot open\n", inpspec);
144     return "";
145     }
146     len = lseek(fd, 0L, SEEK_END);
147     if (len < 0) {
148     fprintf(stderr, "%s: seek error\n", inpspec);
149     close(fd);
150     return "";
151     }
152     lseek(fd, 0L, SEEK_SET);
153     str = (char *)malloc(len+1);
154     if (str == NULL) {
155     close(fd);
156     goto memerr;
157     }
158     if (read(fd, str, len) != len) {
159     fprintf(stderr, "%s: read error\n", inpspec);
160     free(str);
161     close(fd);
162     return "";
163     }
164     str[len] = '\0';
165     close(fd);
166     return str;
167     }
168     /* reading from stream */
169     str = (char *)malloc((len=8192)+1);
170     if (str == NULL)
171     goto memerr;
172     pos = 0;
173     while ((n = read(fileno(fp), str+pos, len-pos)) > 0)
174     if ((pos += n) >= len) { /* need more space? */
175     str = (char *)realloc(str, (len += len>>2) + 1);
176     if (str == NULL)
177     goto memerr;
178     }
179     if (n < 0) {
180     fprintf(stderr, "%s: read error\n", inpspec);
181     free(str);
182     str = "";
183     } else { /* tidy up result */
184     str[pos] = '\0';
185     str = (char *)realloc(str, (len=pos)+1);
186     if (str == NULL)
187     goto memerr;
188     }
189     if (*inpspec != '!')
190     fclose(fp);
191     else if (pclose(fp))
192     fprintf(stderr, "Error running command '%s'\n", inpspec+1);
193     return str;
194     memerr:
195     fprintf(stderr, "%s: error allocating memory\n", inpspec);
196     if (fp != NULL)
197     (*inpspec == '!') ? pclose(fp) : fclose(fp);
198     return "";
199     }
200    
201     /* Make material assignments in field_assignment to XML fields */
202     static int
203     mat_assignments(const char *caller, const char *fn, ezxml_t wtl)
204     {
205     int i;
206    
207     if (!nfield_assign)
208     return 1;
209     wtl = ezxml_child(wtl, "Material");
210     if (wtl == NULL) {
211     fprintf(stderr, "%s: missing <Material> tag\n", fn);
212     return 0;
213     }
214     for (i = 0; i < nfield_assign; i++) {
215     const char *fnext = field_assignment[i];
216     for ( ; ; ) {
217     ezxml_t fld;
218     char sbuf[512];
219     int j;
220    
221     while (isspace(*fnext))
222     ++fnext;
223     if (!*fnext)
224     break;
225     for (j = 0; (*fnext != '=') & !isspace(*fnext); ) {
226     if (!*fnext | (*fnext == FASEP) |
227     (j >= sizeof(sbuf)-1)) {
228     fprintf(stderr,
229     "%s: bad tag name in assignment '%s'\n",
230     caller, field_assignment[i]);
231     return 0;
232     }
233     sbuf[j++] = *fnext++;
234     }
235     sbuf[j] = '\0'; /* check nick-names */
236     for (j = 0; XMLfieldID[j].nickName[0]; j++)
237     if (!strcasecmp(sbuf, XMLfieldID[j].nickName)) {
238     strcpy(sbuf, XMLfieldID[j].fullName);
239     break;
240     }
241     /* check if tag exists */
242     fld = ezxml_child(wtl, sbuf);
243     if (fld == NULL) { /* otherwise, create one */
244     fprintf(stderr, "%s: warning - adding tag <%s>\n",
245     fn, sbuf);
246     fld = ezxml_add_child_d(wtl, sbuf, strlen(wtl->txt));
247     }
248     if (XMLfieldID[j].has_unit)
249     ezxml_set_attr(fld, "unit", attr_unit);
250     while (isspace(*fnext))
251     ++fnext;
252     if (*fnext++ != '=') {
253     fprintf(stderr,
254     "%s: missing '=' in assignment '%s'\n",
255     caller, field_assignment[i]);
256     return 0;
257     }
258     for (j = 0; *fnext & (*fnext != FASEP); ) {
259     if (j >= sizeof(sbuf)-1) {
260     fprintf(stderr,
261     "%s: field too long in '%s'\n",
262     caller, field_assignment[i]);
263     return 0;
264     }
265     sbuf[j++] = *fnext++;
266     }
267     sbuf[j] = '\0';
268     ezxml_set_txt_d(fld, sbuf);
269     fnext += (*fnext == FASEP);
270     }
271     }
272     return 1; /* no errors */
273     }
274    
275     /* Complete angle basis specification */
276     static int
277     finish_angle_basis(ezxml_t ab)
278     {
279     const char *bn = ezxml_txt(ezxml_child(ab, "AngleBasisName"));
280     int i, n = nabases;
281     char buf[32];
282    
283     if (!*bn) {
284     fputs("Internal error - missing <AngleBasisName>!\n", stderr);
285     return 0;
286     }
287     while (n-- > 0)
288     if (!strcasecmp(bn, abase_list[n].name))
289     break;
290     if (n < 0) {
291     fprintf(stderr, "Internal error - unknown angle basis '%s'", bn);
292     return 0;
293     }
294     for (i = 0; abase_list[n].lat[i].nphis; i++) {
295     ezxml_t tb, abb = ezxml_add_child(ab, "AngleBasisBlock",
296     strlen(ab->txt));
297     sprintf(buf, "%g", i ?
298     .5*(abase_list[n].lat[i].tmin + abase_list[n].lat[i+1].tmin) :
299     .0);
300     ezxml_set_txt_d(ezxml_add_child(abb,"Theta",strlen(abb->txt)), buf);
301     sprintf(buf, "%d", abase_list[n].lat[i].nphis);
302     ezxml_set_txt_d(ezxml_add_child(abb,"nPhis",strlen(abb->txt)), buf);
303     tb = ezxml_add_child(abb, "ThetaBounds", strlen(abb->txt));
304     sprintf(buf, "%g", abase_list[n].lat[i].tmin);
305     ezxml_set_txt(ezxml_add_child(tb,"LowerTheta",strlen(tb->txt)), buf);
306     sprintf(buf, "%g", abase_list[n].lat[i+1].tmin);
307     ezxml_set_txt(ezxml_add_child(tb,"UpperTheta",strlen(tb->txt)), buf);
308     }
309     return 1;
310     }
311    
312     /* Determine our angle basis from current tags */
313     static int
314     determine_angle_basis(const char *fn, ezxml_t wtl)
315     {
316     const char *ids;
317     int i;
318    
319     wtl = ezxml_child(wtl, "DataDefinition");
320     if (wtl == NULL)
321     return -1;
322     ids = ezxml_txt(ezxml_child(wtl, "IncidentDataStructure"));
323     if (ids == NULL)
324     return -1;
325     for (i = 0; i < ABend; i++) {
326     ezxml_t parsed = ezxml_parse_str(basis_definition[i],
327     strlen(basis_definition[i]));
328     int match = 0;
329     if (!strcmp(ids, ezxml_txt(ezxml_child(parsed,
330     "IncidentDataStructure")))) {
331     const char *abn0 = ezxml_txt(
332     ezxml_child(ezxml_child(wtl,
333     "AngleBasis"), "AngleBasisName"));
334     const char *abn1 = ezxml_txt(
335     ezxml_child(ezxml_child(parsed,
336     "AngleBasis"), "AngleBasisName"));
337     match = !strcmp(abn0, abn1);
338     }
339     ezxml_free(parsed);
340     if (match)
341     return i;
342     }
343     return -1;
344     }
345    
346     /* Write out BSDF data block with surrounding tags */
347     static int
348     writeBSDFblock(const char *caller, struct s_dfile *df)
349     {
350     char *cp;
351    
352     puts("\t<WavelengthData>");
353     puts("\t\t<LayerNumber>System</LayerNumber>");
354     switch (df->spectrum) {
355     case DSvisible:
356     puts("\t\t<Wavelength unit=\"Integral\">Visible</Wavelength>");
357     puts("\t\tSourceSpectrum>CIE Illuminant D65 1nm.ssp</SourceSpectrum>");
358     puts("\t\t<DetectorSpectrum>ASTM E308 1931 Y.dsp</DetectorSpectrum>");
359     break;
360     case DSxbar31:
361     puts("\t\t<Wavelength unit=\"Integral\">CIE-X</Wavelength>");
362     puts("\t\tSourceSpectrum>CIE Illuminant D65 1nm.ssp</SourceSpectrum>");
363     puts("\t\t<DetectorSpectrum>ASTM E308 1931 X.dsp</DetectorSpectrum>");
364     break;
365     case DSzbar31:
366     puts("\t\t<Wavelength unit=\"Integral\">CIE-Z</Wavelength>");
367     puts("\t\tSourceSpectrum>CIE Illuminant D65 1nm.ssp</SourceSpectrum>");
368     puts("\t\t<DetectorSpectrum>ASTM E308 1931 Z.dsp</DetectorSpectrum>");
369     break;
370     case DSsolar:
371     puts("\t\t<Wavelength unit=\"Integral\">Solar</Wavelength>");
372     puts("\t\tSourceSpectrum>CIE Illuminant D65 1nm.ssp</SourceSpectrum>");
373     puts("\t\t<DetectorSpectrum>None</DetectorSpectrum>");
374     break;
375     case DSnir:
376     puts("\t\t<Wavelength unit=\"Integral\">NIR</Wavelength>");
377     puts("\t\tSourceSpectrum>PLACE_HOLDER</SourceSpectrum>");
378     puts("\t\t<DetectorSpectrum>PLACE_HOLDER</DetectorSpectrum>");
379     break;
380     default:
381     cp = strrchr(spectr_file[df->spectrum], '.');
382     if (cp != NULL)
383     *cp = '\0';
384     printf("\t\t<Wavelength unit=\"Integral\">%s</Wavelength>\n",
385     spectr_file[df->spectrum]);
386     if (cp != NULL)
387     *cp = '.';
388     puts("\t\tSourceSpectrum>CIE Illuminant D65 1nm.ssp</SourceSpectrum>");
389     printf("\t\t<DetectorSpectrum>%s</DetectorSpectrum>\n",
390     spectr_file[df->spectrum]);
391     break;
392     }
393     puts("\t\t<WavelengthDataBlock>");
394     fputs("\t\t\t<WavelengthDataDirection>", stdout);
395     switch (df->type) {
396     case DTtransForward:
397     fputs("Transmission Front", stdout);
398     break;
399     case DTtransBackward:
400     fputs("Transmission Back", stdout);
401     break;
402     case DTreflForward:
403     fputs("Reflection Front", stdout);
404     break;
405     case DTreflBackward:
406     fputs("Reflection Back", stdout);
407     break;
408     default:
409     fprintf(stderr, "%s: internal - bad BSDF type (%d)\n", caller, df->type);
410     return 0;
411     }
412     puts("</WavelengthDataDirection>");
413     switch (angle_basis) {
414     case ABklemsFull:
415     puts("\t\t\t<ColumnAngleBasis>LBNL/Klems Full</ColumnAngleBasis>");
416     break;
417     case ABklemsHalf:
418     puts("\t\t\t<ColumnAngleBasis>LBNL/Klems Half</ColumnAngleBasis>");
419     break;
420     case ABklemsQuarter:
421     puts("\t\t\t<ColumnAngleBasis>LBNL/Klems Quarter</ColumnAngleBasis>");
422     break;
423     case ABtensorTree3:
424     case ABtensorTree4:
425     puts("\t\t\t<AngleBasis>LBNL/Shirley-Chiu</AngleBasis>");
426     break;
427     default:
428     fprintf(stderr, "%s: bad angle basis (%d)\n", caller, angle_basis);
429     return 0;
430     }
431     puts("\t\t\t<ScatteringDataType>BTDF</ScatteringDataType>");
432     puts("\t\t\t<ScatteringData>");
433     fflush(stdout);
434     if (df->fname == stdin_name) {
435     copy_and_close(fileno(stdin));
436     } else if (*df->fname != '!') {
437     if (!copy_and_close(open(df->fname, O_RDONLY))) {
438     fprintf(stderr, "%s: error reading from '%s'\n",
439     caller, df->fname);
440     return 0;
441     }
442     } else if (system(df->fname+1)) {
443     fprintf(stderr, "%s: error running '%s'\n", caller, df->fname+1);
444     return 0;
445     }
446     puts("\t\t\t</ScatteringData>");
447     puts("\t\t</WavelengthDataBlock>");
448     puts("\t</WavelengthData>");
449     return 1;
450     }
451    
452     /* Write out XML, interpolating BSDF data block(s) */
453     static int
454     writeBSDF(const char *caller, ezxml_t fl)
455     {
456     char *xml = ezxml_toxml(fl);
457     int ei, i;
458     /* locate trailer */
459     for (ei = strlen(xml)-strlen("</Layer></Optical></WindowElement>");
460     ei >= 0; ei--)
461     if (!strncmp(xml+ei, "</Layer>", 8))
462     break;
463     if (ei < 0) {
464     fprintf(stderr, "%s: internal - cannot find trailer\n",
465     caller);
466     free(xml);
467     return 0;
468     }
469     fflush(stdout); /* write previous XML info. */
470     if (write(fileno(stdout), xml, ei) != ei) {
471     free(xml);
472     return 0;
473     }
474     for (i = 0; i < ndataf; i++) /* interpolate new data */
475     if (!writeBSDFblock(caller, &data_file[i])) {
476     free(xml);
477     return 0;
478     }
479     fputs(xml+ei, stdout); /* write trailer */
480     free(xml);
481     return (fflush(stdout) == 0);
482     }
483    
484     /* Insert BSDF data into XML wrapper */
485     static int
486     wrapBSDF(const char *caller)
487     {
488     const char *xml_path = getpath((char *)xml_input, getrlibpath(), R_OK);
489     ezxml_t fl, wtl;
490     /* load previous XML/template */
491     if (xml_path == NULL) {
492     fprintf(stderr, "%s: cannot find XML file named '%s'\n",
493     caller, xml_input==NULL ? "NULL" : xml_input);
494     return 0;
495     }
496     fl = ezxml_parse_file(xml_path);
497     if (fl == NULL) {
498     fprintf(stderr, "%s: cannot open XML path '%s'\n",
499     caller, xml_path);
500     return 0;
501     }
502     if (ezxml_error(fl)[0]) {
503     fprintf(stderr, "%s: error in XML %s: %s\n", caller, xml_path,
504     ezxml_error(fl));
505     goto failure;
506     }
507     if (strcmp(ezxml_name(fl), top_level_name)) {
508     fprintf(stderr, "%s: top level in XML '%s' not '%s'\n",
509     caller, xml_path, top_level_name);
510     goto failure;
511     }
512     wtl = ezxml_child(fl, "FileType");
513     if (wtl != NULL && strcmp(ezxml_txt(wtl), "BSDF")) {
514     fprintf(stderr, "%s: wrong FileType in XML '%s' (must be 'BSDF')",
515     caller, xml_path);
516     goto failure;
517     }
518     wtl = ezxml_child(ezxml_child(fl, "Optical"), "Layer");
519     if (wtl == NULL) {
520     fprintf(stderr, "%s: no optical layers in XML '%s'",
521     caller, xml_path);
522     goto failure;
523     }
524     /* make material assignments */
525     if (!mat_assignments(caller, xml_path, wtl)) {
526     goto failure;
527     }
528     if (mgf_geometry != NULL) { /* add geometry if specified */
529     ezxml_t geom = ezxml_child(wtl, "Geometry");
530     if (geom == NULL)
531     geom = ezxml_add_child(wtl, "Geometry", 0);
532     ezxml_set_attr(geom, "format", "MGF");
533     geom = ezxml_child(geom, "MGFblock");
534     if (geom == NULL) {
535     geom = ezxml_child(wtl, "Geometry");
536     geom = ezxml_add_child(geom, "MGFblock", strlen(geom->txt));
537     }
538     ezxml_set_attr(geom, "unit", attr_unit);
539     ezxml_set_txt(geom, input2str(mgf_geometry));
540     if (geom->txt[0])
541     ezxml_set_flag(geom, EZXML_TXTM);
542     }
543     /* check basis */
544     if (angle_basis != ABdefault) {
545     ezxml_t ab, dd = ezxml_child(wtl, "DataDefinition");
546     if (dd == NULL) {
547     dd = ezxml_add_child(wtl, "DataDefinition", 0);
548     } else {
549     if (ezxml_child(dd, "DataDefinition") != NULL)
550     fprintf(stderr,
551     "%s: warning - replacing existing <DataDefinition> in '%s'\n",
552     caller, xml_path);
553     while (dd->child != NULL)
554     ezxml_remove(dd->child);
555     }
556     ezxml_insert(ezxml_parse_str(basis_definition[angle_basis],
557     strlen(basis_definition[angle_basis])),
558     dd, 0);
559     if ((ab = ezxml_child(dd, "AngleBasis")) != NULL &&
560     !finish_angle_basis(ab))
561     goto failure;
562     } else if ((angle_basis = determine_angle_basis(xml_path, wtl)) < 0) {
563     fprintf(stderr, "%s: need -a option to set angle basis\n",
564     caller);
565     goto failure;
566     }
567     /* write & add BSDF data blocks */
568     if (!writeBSDF(caller, fl))
569     goto failure;
570     ezxml_free(fl); /* all done */
571     return 1;
572     failure:
573     ezxml_free(fl);
574     return 0;
575     }
576    
577     /* Report usage and exit */
578     static void
579     UsageExit(const char *pname)
580     {
581     fputs("Usage: ", stderr);
582     fputs(pname, stderr);
583     fputs(" [options] [input.xml]\n", stderr);
584     exit(1);
585     }
586    
587     /* Load XML file and use to wrap BSDF data (or modify fields) */
588     int
589     main(int argc, char *argv[])
590     {
591     int cur_spectrum = DSvisible;
592     int ncust_spec = 0;
593     int used_stdin = 0;
594     int units_set = 0;
595     int i;
596     /* get/check arguments */
597     for (i = 1; i < argc && argv[i][0] == '-'; i++) {
598     switch (argv[i][1]) {
599     case 'W': /* customize for WINDOW 6 output */
600     xml_input = win6_template;
601     continue;
602     case 'f': /* field assignment(s) */
603     if (++i >= argc)
604     UsageExit(argv[0]);
605     if (nfield_assign >= MAXASSIGN) {
606     fprintf(stderr, "%s: too many -f options",
607     argv[0]);
608     return 1;
609     }
610     field_assignment[nfield_assign++] = argv[i];
611     continue;
612     case 'u': /* unit */
613     if (++i >= argc)
614     UsageExit(argv[0]);
615     if (units_set++) {
616     fprintf(stderr, "%s: only one -u option allowed\n",
617     argv[0]);
618     return 1;
619     }
620     if (strstr(argv[i], legal_units) == NULL) {
621     fprintf(stderr, "%s: unit must be one of (%s)\n",
622     argv[0], legal_units);
623     return 1;
624     }
625     attr_unit = argv[i];
626     continue;
627     case 'a': /* angle basis */
628     if (++i >= argc)
629     UsageExit(argv[0]);
630     if (angle_basis != ABdefault) {
631     fprintf(stderr, "%s: only one -a option allowed\n",
632     argv[0]);
633     return 1;
634     }
635     if (!strcasecmp(argv[i], "kf"))
636     angle_basis = ABklemsFull;
637     else if (!strcasecmp(argv[i], "kh"))
638     angle_basis = ABklemsHalf;
639     else if (!strcasecmp(argv[i], "kq"))
640     angle_basis = ABklemsQuarter;
641     else if (!strcasecmp(argv[i], "t3"))
642     angle_basis = ABtensorTree3;
643     else if (!strcasecmp(argv[i], "t4"))
644     angle_basis = ABtensorTree4;
645     else
646     UsageExit(argv[0]);
647     continue;
648     case 't': /* transmission */
649     if (i >= argc-1)
650     UsageExit(argv[0]);
651     if (ndataf >= MAXFILES) {
652     fprintf(stderr, "%s: too many data files\n",
653     argv[0]);
654     return 1;
655     }
656     if (!strcmp(argv[i], "-tf"))
657     data_file[ndataf].type = DTtransForward;
658     else if (!strcmp(argv[i], "-tb"))
659     data_file[ndataf].type = DTtransBackward;
660     else
661     UsageExit(argv[0]);
662     if (!strcmp(argv[++i], "-")) {
663     if (used_stdin++) UsageExit(argv[i]);
664     argv[i] = (char *)stdin_name;
665     }
666     data_file[ndataf].fname = argv[i];
667     data_file[ndataf].spectrum = cur_spectrum;
668     ndataf++;
669     continue;
670     case 'r': /* reflection */
671     if (i >= argc-1)
672     UsageExit(argv[0]);
673     if (ndataf >= MAXFILES) {
674     fprintf(stderr, "%s: too many data files\n",
675     argv[0]);
676     return 1;
677     }
678     if (!strcmp(argv[i], "-rf"))
679     data_file[ndataf].type = DTreflForward;
680     else if (!strcmp(argv[i], "-rb"))
681     data_file[ndataf].type = DTreflBackward;
682     else
683     UsageExit(argv[0]);
684     if (!strcmp(argv[++i], "-")) {
685     if (used_stdin++) UsageExit(argv[i]);
686     argv[i] = (char *)stdin_name;
687     }
688     data_file[ndataf].fname = argv[i];
689     data_file[ndataf++].spectrum = cur_spectrum;
690     continue;
691     case 's': /* spectrum name or input file */
692     if (++i >= argc)
693     UsageExit(argv[0]);
694     if (!strcasecmp(argv[i], "Solar"))
695     cur_spectrum = DSsolar;
696     else if (!strcasecmp(argv[i], "Visible") ||
697     !strcasecmp(argv[i], "CIE-Y"))
698     cur_spectrum = DSvisible;
699     else if (!strcasecmp(argv[i], "CIE-X"))
700     cur_spectrum = DSxbar31;
701     else if (!strcasecmp(argv[i], "CIE-Z"))
702     cur_spectrum = DSzbar31;
703     else if (!strcasecmp(argv[i], "NIR"))
704     cur_spectrum = DSnir;
705     else {
706     if (!strcmp(argv[i], "-")) {
707     fprintf(stderr,
708     "%s: cannot read spectra from stdin",
709     argv[0]);
710     return 1;
711     }
712     cur_spectrum = ncust_spec;
713     spectr_file[ncust_spec++] = argv[i];
714     }
715     continue;
716     case 'g': /* MGF geometry file */
717     if (i >= argc-1)
718     UsageExit(argv[0]);
719     if (mgf_geometry != NULL) {
720     fprintf(stderr, "%s: only one -g option allowed\n",
721     argv[0]);
722     return 1;
723     }
724     if (!strcmp(argv[++i], "-")) {
725     if (used_stdin++) UsageExit(argv[i]);
726     argv[i] = (char *)stdin_name;
727     }
728     mgf_geometry = argv[i];
729     continue;
730     case '\0': /* input XML from stdin */
731     break;
732     default:
733     UsageExit(argv[0]);
734     break;
735     }
736     break;
737     }
738     doneOptions: /* get XML input */
739     if (i >= argc) {
740     if (xml_input == NULL)
741     xml_input = def_template;
742     } else if ((i < argc-1) | (xml_input != NULL)) {
743     fprintf(stderr, "%s: only one XML input allowed\n", argv[0]);
744     UsageExit(argv[0]);
745     } else if (!strcmp(argv[i], "-")) {
746     if (used_stdin++) UsageExit(argv[0]);
747     xml_input = stdin_name;
748     } else {
749     xml_input = argv[i];
750     }
751     if ((xml_input == win6_template) & (angle_basis == ABdefault))
752     angle_basis = ABklemsFull;
753     /* wrap it! */
754     return !wrapBSDF(argv[0]);
755     }