1 /* ----------------------------- MNI Header -----------------------------------
2 minccalc.c
3 A expression parser that works voxel-by-voxel for minc files
4 
5 Andrew Janke - a.janke@gmail.com
6 Center for Magnetic Resonance
7 University of Queensland
8 
9 Original Grammar and parser by
10 David Leonard - leonard@csee.uq.edu.au
11 Department of Computer Science
12 University of Queensland
13 
14 Modifications by Peter Neelin - neelin@bic.mni.mcgill.ca
15 McConnell Brain Imaging Centre
16 Montreal Neurological Institute
17 McGill University
18 
19 This is predominately a rehash of mincmath by Peter Neelin
20 
21  * $Log: minccalc.c,v $
22  * Revision 1.19  2010-03-27 15:24:03  rotor
23  *  * added outfile checks (with clobber) in minccalc
24  *
25  * Revision 1.18  2008/01/17 02:33:02  rotor
26  *  * removed all rcsids
27  *  * removed a bunch of ^L's that somehow crept in
28  *  * removed old (and outdated) BUGS file
29  *
30  * Revision 1.17  2008/01/13 09:38:54  stever
31  * Avoid compiler warnings about functions and variables that are defined
32  * but not used.  Remove some such functions and variables,
33  * conditionalize some, and move static declarations out of header files
34  * into C files.
35  *
36  * Revision 1.16  2008/01/12 19:08:15  stever
37  * Add __attribute__ ((unused)) to all rcsid variables.
38  *
39  * Revision 1.15  2008/01/11 04:24:16  rotor
40  *  * updated all my email addresses
41  *  * removed a (very) outdated TODO file
42  *
43  * Revision 1.14  2007/12/11 12:43:01  rotor
44  *  * added static to all global variables in main programs to avoid linking
45  *       problems with libraries (compress in mincconvert and libz for example)
46  *
47  * Revision 1.13  2005/08/26 21:07:16  bert
48  * Use #if rather than #ifdef with MINC2 symbol, and be sure to include config.h whereever MINC2 is used
49  *
50  * Revision 1.12  2004/12/14 23:52:23  bert
51  * Get rid of compilation warnings w/c99
52  *
53  * Revision 1.11  2004/11/01 22:38:38  bert
54  * Eliminate all references to minc_def.h
55  *
56  * Revision 1.10  2004/06/11 20:55:37  bert
57  * Fix for nasty bug which causes lots of bogus zero values to be inserted when minccalc is used with a file with a vector_dimension
58  *
59  * Revision 1.9  2004/04/27 15:37:52  bert
60  * Added -2 option
61  *
62  * Revision 1.8  2001/05/24 15:08:40  neelin
63  * Added support for comments so that minccalc scripts can be created.
64  *
65  * Revision 1.7  2001/05/04 15:40:33  neelin
66  * Added -outfile option.
67  * Changed syntax of for to use curlies around first part.
68  * Changed syntax of for and if to evaluate an expression in the body, rather
69  * than an expression list in curlies.
70  *
71  * Revision 1.6  2001/05/02 16:27:19  neelin
72  * Fixed handling of invalid values. Added NaN constant. Force copy of
73  * data when assigning to a symbol so that s1 = s2 = expr does the right
74  * thing (s1 and s2 should be separate copies of the data). Updated man
75  * page.
76  *
77  * Revision 1.5  2001/05/02 01:38:15  neelin
78  * Major changes to allow parallel evaluations. Created scalar_t type
79  * along the lines of vector_t (reference counting, etc.). Compiles
80  * and runs on basic standard deviation calculation (with test for invalid
81  * data). Gives over 3 times speedup compared to old version (on linux box).
82  * SD calculation is slightly under half the speed of mincaverage.
83  * Changes are significant enough and testing is little enough that there
84  * are probably lots of bugs left.
85  *
86  * Revision 1.4  2001/04/30 19:16:43  neelin
87  * Added assignment operator, made symbol table global, added expression lists,
88  * for loops, if operators and changed range operator to colon.
89  *
90  * Revision 1.3  2001/04/26 19:12:39  neelin
91  * Finished up addition of operators and handling of invalid values.
92  * This version seems to work.
93  *
94  * Revision 1.2  2001/04/24 18:17:09  neelin
95  * Added CVS logging.
96  *
97 
98 Thu Dec 21 17:26:46 EST 2000 - Added use of voxel_loop
99 Thu Oct  5 17:09:12 EST 2000 - First alpha version
100 Mon May 28 01:00:01 EST 2000 - First minc version - Andrew Janke
101 Mon May 21 01:01:01 EST 2000 - Original version "imgcalc" by David Leonard
102 
103 ---------------------------------------------------------------------------- */
104 
105 #if HAVE_CONFIG_H
106 #include "config.h"
107 #endif
108 
109 #define _GNU_SOURCE 1
110 #include <stdlib.h>
111 #include <stdio.h>
112 #include <sys/types.h>
113 #include <sys/stat.h>
114 #include <unistd.h>
115 #include <string.h>
116 #include <ctype.h>
117 #include <float.h>
118 #include <limits.h>
119 #include <math.h>
120 #include <ParseArgv.h>
121 #include <voxel_loop.h>
122 #include <time_stamp.h>
123 #include "node.h"
124 #include "read_file_names.h"
125 
126 /* Constants */
127 
128 #ifndef TRUE
129 #  define TRUE 1
130 #  define FALSE 0
131 #endif
132 
133 /* Data values for invalid data and for uninitialized data */
134 #define INVALID_DATA -DBL_MAX
135 #define UNINITIALIZED_DATA DBL_MAX
136 
137 /* Values for representing default case for command-line options */
138 #define DEFAULT_DBL DBL_MAX
139 #define DEFAULT_BOOL -1
140 
141 /* Function prototypes */
142 static void do_math(void *caller_data, long num_voxels,
143                     int input_num_buffers,
144                     int input_vector_length, double *input_data[],
145                     int output_num_buffers, int output_vector_length,
146                     double *output_data[], Loop_Info *loop_info);
147 static char *read_expression_file(char *filename);
148 static int get_list_option(char *dst, char *key, int argc, char **argv);
149 
150 /* Argument variables */
151 static int Output_list_size = 0;
152 static int Output_list_alloc = 0;
153 struct {
154    char *symbol;
155    char *file;
156 } *Output_list = NULL;
157 static int clobber = FALSE;
158 static int verbose = TRUE;
159 int debug = FALSE;
160 static int is_signed = FALSE;
161 int propagate_nan = TRUE;
162 static int check_dim_info = TRUE;
163 static int copy_all_header = DEFAULT_BOOL;
164 static int use_nan_for_illegal_values = TRUE;
165 static int max_buffer_size_in_kb = 0;
166 static double valid_range[2] = {0.0, 0.0};
167 double value_for_illegal_operations = DEFAULT_DBL;
168 static nc_type datatype = MI_ORIGINAL_TYPE;
169 static char *filelist = NULL;
170 static char *expr_file = NULL;
171 char *expression = NULL;
172 static int eval_width = 200;
173 #if MINC2
174 static int minc2_format = FALSE;
175 #endif /* MINC2 */
176 
177 /* Argument table */
178 ArgvInfo argTable[] = {
179    {NULL, ARGV_HELP, (char *) NULL, (char *) NULL,
180        "General options:"},
181 #if MINC2
182     {"-2", ARGV_CONSTANT, (char *) TRUE, (char *) &minc2_format,
183      "Produce a MINC 2.0 format output file"},
184 #endif /* MINC2 */
185    {"-clobber", ARGV_CONSTANT, (char *) TRUE, (char *) &clobber,
186        "Overwrite existing file."},
187    {"-noclobber", ARGV_CONSTANT, (char *) FALSE, (char *) &clobber,
188        "Don't overwrite existing file (default)."},
189    {"-no_clobber", ARGV_CONSTANT, (char *) FALSE, (char *) &clobber,
190        "Synonym for -noclobber."},
191    {"-verbose", ARGV_CONSTANT, (char *) TRUE, (char *) &verbose,
192        "Print out log messages (default)."},
193    {"-quiet", ARGV_CONSTANT, (char *) FALSE, (char *) &verbose,
194        "Do not print out log messages."},
195    {"-debug", ARGV_CONSTANT, (char *) TRUE, (char *) &debug,
196        "Print out debugging messages."},
197    {"-filelist", ARGV_STRING, (char *) 1, (char *) &filelist,
198        "Specify the name of a file containing input file names (- for stdin)."},
199    {"-copy_header", ARGV_CONSTANT, (char *) TRUE, (char *) &copy_all_header,
200        "Copy all of the header from the first file."},
201    {"-nocopy_header", ARGV_CONSTANT, (char *) FALSE, (char *) &copy_all_header,
202        "Do not copy all of the header from the first file."},
203    {"-filetype", ARGV_CONSTANT, (char *) MI_ORIGINAL_TYPE, (char *) &datatype,
204        "Use data type of first file (default)."},
205    {"-byte", ARGV_CONSTANT, (char *) NC_BYTE, (char *) &datatype,
206        "Write out byte data."},
207    {"-short", ARGV_CONSTANT, (char *) NC_SHORT, (char *) &datatype,
208        "Write out short integer data."},
209    {"-int", ARGV_CONSTANT, (char *) NC_INT, (char *) &datatype,
210        "Write out 32-bit integer data."},
211    {"-long", ARGV_CONSTANT, (char *) NC_INT, (char *) &datatype,
212        "Superseded by -int."},
213    {"-float", ARGV_CONSTANT, (char *) NC_FLOAT, (char *) &datatype,
214        "Write out single-precision floating-point data."},
215    {"-double", ARGV_CONSTANT, (char *) NC_DOUBLE, (char *) &datatype,
216        "Write out double-precision floating-point data."},
217    {"-signed", ARGV_CONSTANT, (char *) TRUE, (char *) &is_signed,
218        "Write signed integer data."},
219    {"-unsigned", ARGV_CONSTANT, (char *) FALSE, (char *) &is_signed,
220        "Write unsigned integer data (default if type specified)."},
221    {"-range", ARGV_FLOAT, (char *) 2, (char *) valid_range,
222        "Valid range for output data."},
223    {"-max_buffer_size_in_kb", ARGV_INT, (char *) 1,
224        (char *) &max_buffer_size_in_kb,
225        "Specify the maximum size of the internal buffers (in kbytes)."},
226    {"-check_dimensions", ARGV_CONSTANT, (char *) TRUE,
227        (char *) &check_dim_info,
228        "Check that files have matching dimensions (default)."},
229    {"-nocheck_dimensions", ARGV_CONSTANT, (char *) FALSE,
230        (char *) &check_dim_info,
231        "Do not check that files have matching dimensions."},
232    {"-ignore_nan", ARGV_CONSTANT, (char *) FALSE, (char *) &propagate_nan,
233        "Ignore invalid data (NaN) for accumulations."},
234    {"-propagate_nan", ARGV_CONSTANT, (char *) TRUE, (char *) &propagate_nan,
235        "Invalid data in any file at a voxel produces a NaN (default)."},
236    {"-nan", ARGV_CONSTANT, (char *) TRUE,
237        (char *) &use_nan_for_illegal_values,
238        "Output NaN when an illegal operation is done (default)."},
239    {"-zero", ARGV_CONSTANT, (char *) FALSE,
240        (char *) &use_nan_for_illegal_values,
241        "Output zero when an illegal operation is done."},
242    {"-illegal_value", ARGV_FLOAT, (char *) 1,
243        (char *) &value_for_illegal_operations,
244        "Value to write out when an illegal operation is done."},
245    {"-expression",  ARGV_STRING,  (char*)1,    (char*) &expression,
246           "Expression to use in calculations."},
247    {"-expfile",  ARGV_STRING,  (char*)1,    (char*) &expr_file,
248           "Name of file containing expression."},
249    {"-outfile",  ARGV_GENFUNC,  (char*)get_list_option, (char*) &Output_list,
250           "Symbol to save in an output file (2 args)."},
251    {"-eval_width",  ARGV_INT,  (char*)1,    (char*) &eval_width,
252           "Number of voxels to evaluate simultaneously."},
253    {NULL, ARGV_END, NULL, NULL, NULL}
254 };
255 
256 extern int yydebug;
257 sym_t      rootsym;
258 vector_t   A;
259 scalar_t   *Output_values;
260 
261 /* Main program */
main(int argc,char * argv[])262 int main(int argc, char *argv[]){
263    char **infiles, **outfiles;
264    int nfiles, nout;
265    char *arg_string;
266    Loop_Options *loop_options;
267    char *pname;
268    int i;
269    ident_t ident;
270    scalar_t scalar;
271 
272    /* Save time stamp and args */
273    arg_string = time_stamp(argc, argv);
274 
275    /* Get arguments */
276    pname = argv[0];
277    if (ParseArgv(&argc, argv, argTable, 0) || (argc < 2)) {
278       (void) fprintf(stderr,
279       "\nUsage: %s [options] [<in1.mnc> ...] <out.mnc>\n",
280                      pname);
281       (void) fprintf(stderr,
282         "       %s -help\n\n", pname);
283       exit(EXIT_FAILURE);
284    }
285 
286    /* Get output file names */
287    nout = (Output_list == NULL ? 1 : Output_list_size);
288    outfiles = malloc(nout * sizeof(*outfiles));
289    if (Output_list == NULL) {
290       outfiles[0] = argv[argc-1];
291    }
292    else {
293       for (i=0; i < Output_list_size; i++) {
294          outfiles[i] = Output_list[i].file;
295       }
296    }
297 
298    /* check for output files */
299    for (i=0; i < nout; i++){
300       if(access(outfiles[i], F_OK) == 0 && !clobber){
301          fprintf(stderr, "%s: %s exists, use -clobber to overwrite\n\n",
302                  argv[0], outfiles[i]);
303          exit(EXIT_FAILURE);
304       }
305    }
306 
307    /* Get the list of input files either from the command line or
308       from a file, or report an error if both are specified.
309       Note that if -outfile is given then there is no output file name
310       left on argv after option parsing. */
311    nfiles = argc - 2;
312    if (Output_list != NULL) nfiles++;
313    if (filelist == NULL) {
314       infiles = &argv[1];
315    }
316    else if (nfiles <= 0) {
317       infiles = read_file_names(filelist, &nfiles);
318       if (infiles == NULL) {
319          (void) fprintf(stderr,
320                         "Error reading in file names from file \"%s\"\n",
321                         filelist);
322          exit(EXIT_FAILURE);
323       }
324    }
325    else {
326       (void) fprintf(stderr,
327                      "Do not specify both -filelist and input file names\n");
328       exit(EXIT_FAILURE);
329    }
330 
331    /* Make sure that we have something to process */
332    if (nfiles == 0) {
333       (void) fprintf(stderr, "No input files specified\n");
334       exit(EXIT_FAILURE);
335    }
336 
337    /* Get the expression from the file if needed */
338    if ((expression == NULL) && (expr_file == NULL)) {
339       (void) fprintf(stderr,
340                      "An expression must be specified on the command line\n");
341       exit(EXIT_FAILURE);
342    }
343    else if (expression == NULL) {
344       expression = read_expression_file(expr_file);
345    }
346 
347    /* Parse expression argument */
348    if (debug) fprintf(stderr, "Feeding in expression %s\n", expression);
349    lex_init(expression);
350    if (debug) yydebug = 1; else yydebug = 0;
351    yyparse();
352    lex_finalize();
353 
354    /* Optimize the expression tree */
355    root = optimize(root);
356 
357    /* Setup the input vector from the input files */
358    A = new_vector();
359    for (i=0; i<nfiles; i++) {
360       if (debug) fprintf(stderr,"Getting file[%d] %s\n", i, argv[i+1]);
361       scalar = new_scalar(eval_width);
362       vector_append(A, scalar);
363       scalar_free(scalar);
364    }
365 
366    /* Construct initial symbol table from the A vector. Since setting
367       a symbol makes a copy, we have to get a handle to that copy. */
368    rootsym = sym_enter_scope(NULL);
369    ident = new_ident("A");
370    sym_set_vector(eval_width, NULL, A, ident, rootsym);
371    vector_free(A);
372    A = sym_lookup_vector(ident, rootsym);
373    if (A==NULL) {
374       (void) fprintf(stderr, "Error initializing symbol table\n");
375       exit(EXIT_FAILURE);
376    }
377    vector_incr_ref(A);
378 
379    /* Add output symbols to the table */
380    if (Output_list == NULL) {
381       Output_values = NULL;
382    }
383    else {
384       Output_values = malloc(Output_list_size * sizeof(*Output_values));
385       for (i=0; i < Output_list_size; i++) {
386          ident = ident_lookup(Output_list[i].symbol);
387          scalar = new_scalar(eval_width);
388          sym_set_scalar(eval_width, NULL, scalar, ident, rootsym);
389          scalar_free(scalar);
390          Output_values[i] = sym_lookup_scalar(ident, rootsym);
391       }
392    }
393 
394    /* Set default copy_all_header according to number of input files */
395    if (copy_all_header == DEFAULT_BOOL)
396       copy_all_header = (nfiles == 1);
397 
398    if (value_for_illegal_operations == DEFAULT_DBL) {
399       if (use_nan_for_illegal_values)
400          value_for_illegal_operations = INVALID_DATA;
401       else
402          value_for_illegal_operations = 0.0;
403    }
404 
405    /* Do math */
406    loop_options = create_loop_options();
407    set_loop_verbose(loop_options, verbose);
408    set_loop_clobber(loop_options, clobber);
409 #if MINC2
410    set_loop_v2format(loop_options, minc2_format);
411 #endif /* MINC2 */
412    set_loop_datatype(loop_options, datatype, is_signed,
413                      valid_range[0], valid_range[1]);
414    set_loop_copy_all_header(loop_options, copy_all_header);
415 
416    /* only set buffer size if specified */
417    if(max_buffer_size_in_kb != 0){
418       set_loop_buffer_size(loop_options, (long) 1024 * max_buffer_size_in_kb);
419       }
420 
421    set_loop_check_dim_info(loop_options, check_dim_info);
422    voxel_loop(nfiles, infiles, nout, outfiles, arg_string, loop_options,
423               do_math, NULL);
424    free_loop_options(loop_options);
425 
426 
427    /* Clean up */
428    vector_free(A);
429    sym_leave_scope(rootsym);
430    if (expr_file != NULL) free(expression);
431    free(outfiles);
432    if (Output_list != NULL) free(Output_list);
433    if (Output_values != NULL) free(Output_values);
434    exit(EXIT_SUCCESS);
435 }
436 
437 /* ----------------------------- MNI Header -----------------------------------
438 @NAME       : do_math
439 @INPUT      : Standard for voxel loop
440 @OUTPUT     : Standard for voxel loop
441 @RETURNS    : (nothing)
442 @DESCRIPTION: Routine doing math operations.
443 @METHOD     :
444 @GLOBALS    : Output_values, A
445 @CALLS      :
446 @CREATED    : April 25, 1995 (Peter Neelin)
447 @MODIFIED   : Thu Dec 21 17:08:40 EST 2000 (Andrew Janke - a.janke@gmail.com)
448 ---------------------------------------------------------------------------- */
do_math(void * caller_data,long num_voxels,int input_num_buffers,int input_vector_length,double * input_data[],int output_num_buffers,int output_vector_length,double * output_data[],Loop_Info * loop_info)449 static void do_math(void *caller_data, long num_voxels,
450                     int input_num_buffers, int input_vector_length,
451                     double *input_data[],
452                     int output_num_buffers, int output_vector_length,
453                     double *output_data[],
454                     Loop_Info *loop_info){
455    long ivox, ibuff, ivalue, nvox;
456    scalar_t scalar, *output_scalars;
457    int num_output, iout;
458    long total_values;  /* Total # of values to process in this call */
459 
460    /* Check arguments */
461    if ((output_num_buffers < 1) ||
462        (output_vector_length != input_vector_length)) {
463       (void) fprintf(stderr, "Bad arguments to do_math!\n");
464       exit(EXIT_FAILURE);
465    }
466 
467    total_values = num_voxels * input_vector_length;
468 
469    /* Loop through the voxels */
470    for (ivox=0; ivox < total_values; ivox+=eval_width) {
471 
472       /* Figure out how many voxels to work on at once */
473       nvox = eval_width;
474       if (ivox + nvox > total_values)
475           nvox = total_values - ivox;
476 
477       /* Copy the data into the A vector */
478       for (ivalue=0; ivalue < nvox; ivalue++) {
479          for (ibuff=0; ibuff < input_num_buffers; ibuff++){
480             A->el[ibuff]->vals[ivalue] = input_data[ibuff][ivox+ivalue];
481          }
482       }
483 
484       /* Some debugging */
485       if (debug) {
486          (void) fprintf(stderr, "\n===New voxel===\n");
487       }
488 
489       /* Evaluate the expression */
490       scalar = eval_scalar((int) nvox, NULL, root, rootsym);
491 
492       /* Get the list of scalar values to write out */
493       if (Output_values == NULL) {
494          num_output = 1;
495          output_scalars = &scalar;
496       }
497       else {
498          num_output = Output_list_size;
499          output_scalars = Output_values;
500       }
501 
502       /* Copy the scalar values into the right buffers */
503       for (iout=0; iout < num_output; iout++) {
504          for (ivalue=0; ivalue < nvox; ivalue++) {
505             output_data[iout][ivox+ivalue] =
506                output_scalars[iout]->vals[ivalue];
507          }
508       }
509 
510       /* Free things up */
511       scalar_free(scalar);
512 
513       if (debug) {
514          (void) printf("Voxel result = %g\n", output_data[0][ivox]);
515       }
516 
517 
518    }
519 
520    return;
521 }
522 
523 /* ----------------------------- MNI Header -----------------------------------
524 @NAME       : read_expression_file
525 @INPUT      : filename - Name of file from which to read expression
526 @OUTPUT     : (none)
527 @RETURNS    : String containing expression - must be freed by caller.
528 @DESCRIPTION: Reads in an expression from a file.
529 @METHOD     :
530 @GLOBALS    :
531 @CALLS      :
532 @CREATED    : May 3, 2001 (Peter Neelin)
533 @MODIFIED   :
534 ---------------------------------------------------------------------------- */
read_expression_file(char * filename)535 static char *read_expression_file(char *filename)
536 {
537    struct stat statbuf;
538    size_t size;
539    FILE *fp;
540    char *expression;
541    int ichar;
542    int beginning_of_line, in_comment;
543    int ch;
544 
545 #define ALLOC_SIZE 1024
546 
547    /* Set the default allocation size - zero means allocate as we go */
548    size = 0;
549 
550    /* Check for reading from stdin */
551    if (strcmp(filename, "-") == 0) {
552       fp = stdin;
553    }
554 
555    /* Otherwise read from file. Get allocation size from file size. */
556    else {
557 
558       /* Get the file size */
559       if (stat(filename, &statbuf) >= 0) {
560          size = statbuf.st_size + 1;
561       }
562 
563       /* Open the file */
564       if ((fp=fopen(filename, "r")) == NULL) {
565          (void) fprintf(stderr, "Unable to open expression file \"%s\"\n",
566                         filename);
567          exit(EXIT_FAILURE);
568       }
569 
570    }
571 
572    /* Make sure that we are going to allocate something */
573    if (size == 0) size = ALLOC_SIZE;
574 
575    /* Get space */
576    expression = malloc(size * sizeof(*expression));
577 
578    /* Read the expression */
579    ichar = 0;
580    beginning_of_line = TRUE;
581    in_comment = FALSE;
582    while ((ch = getc(fp)) != EOF) {
583 
584       /* Check for newline to end comments */
585       if (ch == '\n') {
586          beginning_of_line = TRUE;
587          in_comment = FALSE;
588       }
589 
590       /* Check for comment character as first non-whitespace char of line */
591       else if (beginning_of_line && (ch == '#')) {
592          in_comment = TRUE;
593          beginning_of_line = FALSE;
594       }
595 
596       /* Check for first non-whitespace char of line */
597       else if (!isspace(ch)) {
598          beginning_of_line = FALSE;
599       }
600 
601       /* If not in a comment, then save the character */
602       if (!in_comment) {
603 
604          /* Check whether we need more space */
605          if (ichar >= size-1) {
606             size += ALLOC_SIZE;
607             expression = realloc(expression, size * sizeof(expression));
608          }
609 
610          /* Save the character */
611          expression[ichar] = (char) ch;
612          ichar++;
613       }
614    }
615    expression[ichar] = '\0';
616 
617    /* Close the file */
618    if (fp != stdin) {
619       (void) fclose(fp);
620    }
621 
622    /* Return the expression */
623    return expression;
624 }
625 
626 /* ----------------------------- MNI Header -----------------------------------
627 @NAME       : get_list_option
628 @INPUT      : dst - client data passed by ParseArgv
629               key - matching key in argv
630               argc - number of arguments passed in
631               argv - argument list
632 @OUTPUT     : (none)
633 @RETURNS    : Number of arguments left in argv list.
634 @DESCRIPTION: Gets arguments from the command line and appends them
635               to a list, chosen based on key.
636 @METHOD     :
637 @GLOBALS    :
638 @CALLS      :
639 @CREATED    : May 3, 2001 (Peter Neelin)
640 @MODIFIED   :
641 ---------------------------------------------------------------------------- */
get_list_option(char * dst,char * key,int argc,char ** argv)642 static int get_list_option(char *dst, char *key, int argc, char **argv)
643      /* ARGSUSED */
644 {
645    enum {OPT_OUTPUT_SYMBOL} option_type;
646    void **list;
647    size_t entry_size;
648    int *list_size, *list_alloc, index;
649    int num_args, iarg;
650 
651    /* Check the key */
652    list = (void **) dst;
653    if (strcmp(key, "-outfile") == 0) {
654       option_type = OPT_OUTPUT_SYMBOL;
655       list_size = &Output_list_size;
656       list_alloc = &Output_list_alloc;
657       entry_size = sizeof(Output_list[0]);
658       num_args = 2;
659    }
660    else {
661       (void) fprintf(stderr,
662                      "Internal error - unrecognized key in get_list_option\n");
663       exit(EXIT_FAILURE);
664    }
665 
666    /* Check for following arguments */
667    if (argc < num_args) {
668       (void) fprintf(stderr,
669                      "\"%s\" option requires %d additional arguments\n",
670                      key, num_args);
671       exit(EXIT_FAILURE);
672    }
673 
674    /* Get more space */
675    (*list_size)++;
676    if (*list_size > *list_alloc) {
677       *list_alloc += 10;
678       if (*list == NULL) {
679          *list =
680             malloc(*list_alloc * entry_size);
681       }
682       else {
683          *list =
684             realloc(*list,
685                     *list_alloc * entry_size);
686       }
687    }
688    index = *list_size - 1;
689 
690    /* Save the values */
691    if (option_type == OPT_OUTPUT_SYMBOL) {
692       Output_list[index].symbol = argv[0];
693       Output_list[index].file = argv[1];
694    }
695 
696    /* Modify the argument list */
697    if (num_args > 0) {
698       for (iarg=0; iarg < (argc - num_args); iarg++) {
699          argv[iarg] = argv[iarg + num_args];
700       }
701    }
702 
703    return argc - num_args;
704 
705 }
706 
707