home *** CD-ROM | disk | FTP | other *** search
- /*
- * average -- produce a composite file from multiple, similar, files
- *
- * This general-purpose program takes multiple files containing
- * text (which should be the same in all files) and numbers (which
- * may differ from file to file), and it outputs one file, with
- * the same text, and with each occurence of a number replaced
- * by its average over all of the input files. There is an option
- * for outputting [min,average,max] tuples instead of a single number.
- *
- * HISTORY
- *
- * 1987/03/31 Liudvikas Bukys <bukys@cs.rochester.edu>
- *
- * Version 1.0 created.
- *
- * I needed it to compute some average runtimes from several
- * timing runs of some multiprocessor code.
- *
- * There is no support for numbers with 'e', 'E', or '+' in
- * them. Those characters would be parsed as text.
- *
- * The whole file is sucked into memory.
- */
-
- #define USAGE "USAGE: %s [-m] file1 [file2 ...]\n"
-
- #include <stdio.h>
- #include <ctype.h>
-
- extern char *malloc();
- extern char *strchr(); /* or try index() if you don't have strchr() */
-
- #define MAX(a,b) (((a)>(b))?(a):(b))
- #define MIN(a,b) (((a)<(b))?(a):(b))
-
- struct token
- {
- struct token *next;
- char *buffer;
- int precision;
- double average, min, max;
- int count;
- } head = { 0, };
-
- /*
- * MAXTOKENLEN doesn't show through to the user, who doesn't care whether
- * a bunch of text is parsed as a single 20000-byte token or a pile of
- * 512-byte tokens. The only thing it might affect would be if you had
- * numbers with length > MAXTOKENLEN, but this code doesn't have any
- * bignum support anyway.
- */
- #define MAXTOKENLEN 512
-
-
- main(argc, argv)
- int argc;
- char **argv;
- {
- register int i;
- register FILE *f;
- register struct token *p;
- int minmaxflag = 0;
-
- if (argc <= 1)
- {
- (void) fprintf(stderr, USAGE, argv[0]);
- exit(1);
- }
-
- /*
- * Read in each of my input files and tokenize it.
- */
- for (i= 1; i < argc; i++)
- if (argv[i][0] == '-')
- if (argv[i][1] == 'm')
- minmaxflag = 1;
- else
- {
- (void) fprintf(stderr, USAGE, argv[0]);
- exit(1);
- }
- else if ((f= fopen(argv[i], "r")) != NULL)
- {
- for (p= &head; !feof(f); p= p->next)
- readword(p, f);
- (void) fclose(f);
- }
- else
- perror(argv[i]);
-
- /*
- * Output the token list.
- * Numbers retain the maximum precision of the input numbers.
- */
- for (p = head.next; p; p = p->next)
- if (p->buffer)
- (void) printf("%s", p->buffer);
- else if (p->precision == 0)
- if (p->min == p->max)
- (void) printf("%d", (int) p->min);
- else if (minmaxflag)
- (void) printf("[%d %d %d]", (int) p->min, (int) p->average, (int) p->max);
- else
- (void) printf("%d", (int) p->average);
- else
- if (p->min == p->max)
- (void) printf("%.*lf", p->precision-1, p->min);
- else if (minmaxflag)
- (void) printf("[%.*lf %.*lf %.*lf]",
- p->precision-1, p->min,
- p->precision-1, p->average,
- p->precision-1, p->max);
- else
- (void) printf("%.*lf", p->precision-1, p->average);
-
- exit (0);
- }
-
- /*
- * Read one token -- either a number or a text string.
- */
- readword(ptail, f)
- struct token *ptail;
- FILE *f;
- {
- register struct token *p;
- register int c, hasdigit, wasseparator;
- register unsigned len;
- char tok[MAXTOKENLEN+1];
-
- /*
- * get next token, or add a new one to the end of the list
- */
- if (p= ptail->next)
- ;
- else if (ptail->next = p = (struct token *) malloc(sizeof *p))
- {
- p->next = 0;
- p->buffer = 0;
- p->count = 0;
- p->precision = 0;
- }
- else
- {
- (void) fprintf(stderr, "malloc failed\n");
- exit(1);
- }
-
- c = getc(f);
- (void) ungetc(c, f);
- if (strchr("0123456789.-", c))
- {
- /*
- * possibly data -- defer judgement until after I see some
- * digits (this might just be a string of '-', after all).
- */
- len = 0;
- hasdigit = 0;
- while (len < MAXTOKENLEN && ((c= getc(f)) != EOF))
- if (strchr("0123456789.-", c))
- {
- tok[len++] = c;
- if (strchr("0123456789", c))
- hasdigit = 1;
- }
- else
- {
- (void) ungetc(c, f);
- break;
- }
- tok[len] = '\0';
- if (hasdigit)
- dodata(p, tok, len);
- else
- dotext(p, tok, len);
- }
- else
- {
- /*
- * text
- */
- wasseparator = 0;
- len = 0;
- while (len < MAXTOKENLEN && ((c= getc(f)) != EOF))
- if (wasseparator && strchr("0123456789.-", c))
- {
- (void) ungetc(c, f);
- break;
- }
- else
- {
- tok[len++] = c;
- wasseparator = !isascii(c) || !isalnum(c);
- }
- tok[len] = '\0';
- dotext(p, tok, len);
- }
- }
-
- /*
- * Do number-specific processing -- compute min, max, average
- */
- dodata(p, tok, len)
- struct token *p;
- char *tok;
- unsigned len;
- {
- char *dot;
- double value;
-
- if (dot= strchr(tok, '.'))
- p->precision = MAX(p->precision, (tok+len)-dot);
-
- value = 0.0;
- (void) sscanf(tok, "%lf", &value);
-
- if (p->count++)
- {
- p->min = MIN(p->min, value);
- p->max = MAX(p->max, value);
- p->average = p->average*((p->count-1.0)/(p->count)) + value/p->count;
- }
- else
- p->min = p->max = p->average = value;
- }
-
- /*
- * Do text-specific processing -- save the text permanently (but only the first time!)
- */
- dotext(p, tok, len)
- struct token *p;
- char *tok;
- unsigned len;
- {
- if (p->buffer)
- ; /* remember the old string only */
- else if (p->buffer= malloc(len+1))
- bcopy(tok, p->buffer, len+1);
- else
- {
- (void) fprintf(stderr, "malloc failed\n");
- exit(1);
- }
- }
-