add Outputable instance for OccIfaceEq
[ghc-hetmet.git] / utils / hp2ps / HpFile.c
1 #include "Main.h"
2 #include <ctype.h>
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include "Defines.h"
7 #include "Error.h"
8 #include "HpFile.h"
9 #include "Utilities.h"
10
11 #ifndef atof
12 double atof PROTO((const char *));
13 #endif
14
15 /* own stuff already included */
16
17 #define N_MARKS 50              /* start size of the mark table */
18 #define N_SAMPLES 500           /* start size of the sample table */
19
20 char *theident;
21 char *thestring;
22 int theinteger;
23 floatish thefloatish;
24 int ch;                                         /* last character read  */
25 token thetok;                                   /* last token           */
26 int linenum;                                    /* current line number  */
27 int endfile;                                    /* true at end of file  */
28
29 static boolish gotjob = 0;                      /* "JOB" read           */
30 static boolish gotdate = 0;                     /* "DATE" read          */
31 static boolish gotvalueunit = 0;                /* "VALUE_UNIT" read    */
32 static boolish gotsampleunit = 0;               /* "SAMPLE_UNIT" read   */
33 static boolish insample = 0;                    /* true when in sample  */
34
35 static floatish lastsample;                     /* the last sample time */
36
37 static void GetHpLine PROTO((FILE *));          /* forward */
38 static void GetHpTok  PROTO((FILE *));          /* forward */
39
40 static struct entry *GetEntry PROTO((char *));  /* forward */
41
42 static void MakeIdentTable PROTO((void));       /* forward */
43
44 char *jobstring;
45 char *datestring;
46
47 char *sampleunitstring;
48 char *valueunitstring;
49
50 floatish *samplemap;            /* sample intervals     */
51 floatish *markmap;              /* sample marks         */
52
53 /*
54  *      An extremely simple parser. The input is organised into lines of 
55  *      the form
56  *
57  *      JOB s              -- job identifier string
58  *      DATE s             -- date string 
59  *      SAMPLE_UNIT s      -- sample unit eg "seconds" 
60  *      VALUE_UNIT s       -- value unit eg "bytes" 
61  *      MARK i             -- sample mark 
62  *      BEGIN_SAMPLE i     -- start of ith sample 
63  *      identifier i       -- there are i identifiers in this sample 
64  *      END_SAMPLE i       -- end of ith sample 
65  *
66  */
67
68 void
69 GetHpFile(infp)
70   FILE *infp;
71 {
72     nsamples = 0;
73     nmarks   = 0;
74     nidents  = 0;
75
76     ch = ' ';
77     endfile = 0;
78     linenum = 1;
79     lastsample = 0.0;
80
81     GetHpTok(infp);
82
83     while (endfile == 0) {
84         GetHpLine(infp);
85     }
86
87     if (!gotjob) {
88         Error("%s: JOB missing", hpfile);
89     }
90
91     if (!gotdate) {
92         Error("%s: DATE missing", hpfile);
93     }
94
95     if (!gotvalueunit) {
96         Error("%s: VALUE_UNIT missing", hpfile);
97     }
98
99     if (!gotsampleunit) {
100         Error("%s: SAMPLE_UNIT missing", hpfile);
101     }
102
103     if (nsamples == 0) {
104         Error("%s: contains no samples", hpfile);
105     }
106
107
108     MakeIdentTable();
109
110     fclose(hpfp);
111 }
112
113
114 /*
115  *      Read the next line from the input, check the syntax, and perform
116  *      the appropriate action.
117  */
118
119 static void
120 GetHpLine(infp)
121   FILE* infp;
122 {
123     static intish nmarkmax = 0, nsamplemax = 0;
124
125     switch (thetok) {
126     case JOB_TOK:
127         GetHpTok(infp);
128         if (thetok != STRING_TOK) {
129             Error("%s, line %d: string must follow JOB", hpfile, linenum);
130         }
131         jobstring = thestring;
132         gotjob = 1;
133         GetHpTok(infp);
134         break;
135
136     case DATE_TOK:
137         GetHpTok(infp);
138         if (thetok != STRING_TOK) {
139             Error("%s, line %d: string must follow DATE", hpfile, linenum);
140         }
141         datestring = thestring;
142         gotdate = 1;
143         GetHpTok(infp);
144         break;
145
146     case SAMPLE_UNIT_TOK:
147         GetHpTok(infp);
148         if (thetok != STRING_TOK) {
149             Error("%s, line %d: string must follow SAMPLE_UNIT", hpfile, 
150                   linenum);
151         }
152         sampleunitstring = thestring;
153         gotsampleunit = 1;
154         GetHpTok(infp);
155         break;
156
157     case VALUE_UNIT_TOK:
158         GetHpTok(infp);
159         if (thetok != STRING_TOK) {
160             Error("%s, line %d: string must follow VALUE_UNIT", hpfile, 
161                   linenum);
162         }
163         valueunitstring = thestring;
164         gotvalueunit = 1;
165         GetHpTok(infp);
166         break;
167
168     case MARK_TOK:
169         GetHpTok(infp);
170         if (thetok != FLOAT_TOK) {
171             Error("%s, line %d, floating point number must follow MARK",
172                   hpfile, linenum);
173         }
174         if (insample) {
175             Error("%s, line %d, MARK occurs within sample", hpfile, linenum);
176         }
177         if (nmarks >= nmarkmax) {
178             if (!markmap) {
179                 nmarkmax = N_MARKS;
180                 markmap = (floatish*) xmalloc(nmarkmax * sizeof(floatish));
181             } else {
182                 nmarkmax *= 2;
183                 markmap = (floatish*) xrealloc(markmap, nmarkmax * sizeof(floatish));
184             }
185         }
186         markmap[ nmarks++ ] = thefloatish; 
187         GetHpTok(infp);
188         break;
189
190     case BEGIN_SAMPLE_TOK: 
191         insample = 1;
192         GetHpTok(infp); 
193         if (thetok != FLOAT_TOK) {
194             Error("%s, line %d, floating point number must follow BEGIN_SAMPLE",                  hpfile, linenum);
195         }
196         if (thefloatish < lastsample) {
197             Error("%s, line %d, samples out of sequence", hpfile, linenum);
198         } else {
199             lastsample = thefloatish;
200         }
201         if (nsamples >= nsamplemax) {
202             if (!samplemap) {
203                 nsamplemax = N_SAMPLES;
204                 samplemap = (floatish*) xmalloc(nsamplemax * sizeof(floatish));
205             } else {
206                 nsamplemax *= 2;
207                 samplemap = (floatish*) xrealloc(samplemap, 
208                                               nsamplemax * sizeof(floatish));
209             }
210         }
211         samplemap[ nsamples ] = thefloatish;
212         GetHpTok(infp);
213         break;
214
215     case END_SAMPLE_TOK: 
216         insample = 0;
217         GetHpTok(infp); 
218         if (thetok != FLOAT_TOK) {
219             Error("%s, line %d: floating point number must follow END_SAMPLE", 
220                   hpfile, linenum);
221         }
222         nsamples++;
223         GetHpTok(infp);
224         break;
225
226     case IDENTIFIER_TOK:
227         GetHpTok(infp);
228         if (thetok != INTEGER_TOK) {
229             Error("%s, line %d: integer must follow identifier", hpfile, 
230                   linenum);
231         }
232         StoreSample(GetEntry(theident), nsamples, (floatish) theinteger);
233         GetHpTok(infp); 
234         break;
235
236     case EOF_TOK:
237         endfile = 1;
238         break;
239
240     default:
241         Error("%s, line %d: %s unexpected", hpfile, linenum,
242               TokenToString(thetok));
243         break;
244     }
245 }
246
247
248 char *
249 TokenToString(t)
250   token t;
251 {
252    switch (t) {
253         case EOF_TOK:           return "EOF";
254         case INTEGER_TOK:       return "integer";
255         case FLOAT_TOK:         return "floating point number";
256         case IDENTIFIER_TOK:    return "identifier";
257         case STRING_TOK:        return "string";
258         case BEGIN_SAMPLE_TOK:  return "BEGIN_SAMPLE";
259         case END_SAMPLE_TOK:    return "END_SAMPLE";
260         case JOB_TOK:           return "JOB";
261         case DATE_TOK:          return "DATE";
262         case SAMPLE_UNIT_TOK:   return "SAMPLE_UNIT";
263         case VALUE_UNIT_TOK:    return "VALUE_UNIT";
264         case MARK_TOK:          return "MARK";
265
266         case X_RANGE_TOK:       return "X_RANGE";
267         case Y_RANGE_TOK:       return "Y_RANGE";
268         case ORDER_TOK:         return "ORDER";
269         case SHADE_TOK:         return "SHADE";
270         default:                return "(strange token)";
271     }
272 }
273
274 /*
275  *      Read the next token from the input and assign its value
276  *      to the global variable "thetok". In the case of numbers,
277  *      the corresponding value is also assigned to "theinteger"
278  *      or "thefloatish" as appropriate; in the case of identifiers 
279  *      it is assigned to "theident".
280  */
281
282 static void
283 GetHpTok(infp)
284   FILE* infp;
285 {
286
287     while (isspace(ch)) {               /* skip whitespace */
288         if (ch == '\n') linenum++;
289         ch = getc(infp);
290     } 
291
292     if (ch == EOF) {
293         thetok = EOF_TOK;
294         return;
295     }
296
297     if (isdigit(ch)) {
298         thetok = GetNumber(infp);
299         return;
300     } else if (ch == '\"') {
301         GetString(infp);
302         thetok = STRING_TOK;
303         return;
304     } else if (IsIdChar(ch)) {
305         ASSERT(! (isdigit(ch)));        /* ch can't be a digit here */
306         GetIdent(infp);
307         if (!isupper((int)theident[0])) {
308             thetok = IDENTIFIER_TOK;
309         } else if (strcmp(theident, "BEGIN_SAMPLE") == 0) {
310             thetok = BEGIN_SAMPLE_TOK;
311         } else if (strcmp(theident, "END_SAMPLE") == 0) {
312             thetok = END_SAMPLE_TOK;
313         } else if (strcmp(theident, "JOB") == 0) {
314             thetok = JOB_TOK;
315         } else if (strcmp(theident, "DATE") == 0) {
316             thetok = DATE_TOK;
317         } else if (strcmp(theident, "SAMPLE_UNIT") == 0) {
318             thetok = SAMPLE_UNIT_TOK;
319         } else if (strcmp(theident, "VALUE_UNIT") == 0) {
320             thetok = VALUE_UNIT_TOK;
321         } else if (strcmp(theident, "MARK") == 0) {
322             thetok = MARK_TOK;
323         } else {
324             thetok = IDENTIFIER_TOK;
325         }
326         return;
327     } else {
328         Error("%s, line %d: strange character (%c)", hpfile, linenum, ch);
329     }
330 }
331
332
333 /*
334  *      Read a sequence of digits and convert the result to an integer
335  *      or floating point value (assigned to the "theinteger" or 
336  *      "thefloatish").
337  */
338
339 static char numberstring[ NUMBER_LENGTH - 1 ];
340
341 token
342 GetNumber(infp)
343   FILE* infp;
344 {
345     int i;
346     int containsdot;
347  
348     ASSERT(isdigit(ch)); /* we must have a digit to start with */
349
350     containsdot = 0;
351
352     for (i = 0; i < NUMBER_LENGTH && (isdigit(ch) || ch == '.'); i++) {
353         numberstring[ i ] = ch;
354         containsdot |= (ch == '.'); 
355         ch = getc(infp);
356     }   
357  
358     ASSERT(i < NUMBER_LENGTH); /* did not overflow */
359
360     numberstring[ i ] = '\0';
361  
362     if (containsdot) {
363         thefloatish = (floatish) atof(numberstring);
364         return FLOAT_TOK;
365     } else {
366         theinteger = atoi(numberstring);
367         return INTEGER_TOK;
368     }
369 }
370
371 /*
372  *      Read a sequence of identifier characters and assign the result 
373  *      to the string "theident".
374  */
375
376 void
377 GetIdent(infp)
378   FILE *infp;
379 {
380     unsigned int i;
381     char idbuffer[5000];
382
383     for (i = 0; i < (sizeof idbuffer)-1 && IsIdChar(ch); i++) {
384         idbuffer[ i ] = ch;
385         ch = getc(infp);
386     }
387     
388     idbuffer[ i ] = '\0';
389
390     if (theident)
391         free(theident);
392
393     theident = copystring(idbuffer);
394 }
395
396
397 /*
398  *      Read a sequence of characters that make up a string and 
399  *      assign the result to "thestring".
400  */
401
402 void
403 GetString(infp)
404   FILE *infp;
405 {
406     unsigned int i;
407     char stringbuffer[5000];
408
409     ASSERT(ch == '\"');
410
411     ch = getc(infp);    /* skip the '\"' that begins the string */
412
413     for (i = 0; i < (sizeof stringbuffer)-1 && ch != '\"'; i++) {
414         stringbuffer[ i ] = ch;
415         ch = getc(infp);
416     }
417
418     stringbuffer[i] = '\0'; 
419     thestring = copystring(stringbuffer);
420
421     ASSERT(ch == '\"');
422
423     ch = getc(infp);      /* skip the '\"' that terminates the string */
424 }
425
426 boolish
427 IsIdChar(ch)
428   int ch;
429 {
430     return (!isspace(ch));
431 }
432
433
434 /*
435  *      The information associated with each identifier is stored
436  *      in a linked list of chunks. The table below allows the list
437  *      of chunks to be retrieved given an identifier name.
438  */
439
440 #define N_HASH          513 
441
442 static struct entry* hashtable[ N_HASH ];
443
444 static intish
445 Hash(s)
446   char *s;
447 {
448     int r;
449  
450     for (r = 0; *s; s++) {
451         r = r + r + r + *s;
452     }
453
454     if (r < 0) r = -r;
455
456     return r % N_HASH;
457 }
458
459 /*
460  *      Get space for a new chunk. Initialise it, and return a pointer 
461  *      to the new chunk.
462  */
463  
464 static struct chunk*
465 MakeChunk()
466 {
467     struct chunk* ch;
468     struct datapoint* d;
469
470     ch = (struct chunk*) xmalloc( sizeof(struct chunk) );
471  
472     d = (struct datapoint*) xmalloc (sizeof(struct datapoint) * N_CHUNK);
473
474     ch->nd = 0; 
475     ch->d = d;
476     ch->next = 0;
477     return ch;
478 }
479
480
481 /*
482  *      Get space for a new entry. Initialise it, and return a pointer 
483  *      to the new entry.
484  */
485  
486 struct entry *
487 MakeEntry(name)
488   char *name;
489 {
490     struct entry* e;
491
492     e = (struct entry *) xmalloc(sizeof(struct entry));
493     e->chk = MakeChunk();
494     e->name = copystring(name); 
495     return e;
496 }
497
498 /*
499  *      Get the entry associated with "name", creating a new entry if 
500  *      necessary.
501  */
502
503 static struct entry *
504 GetEntry(name)
505   char* name;
506 {
507     intish h;
508     struct entry* e;
509  
510     h = Hash(name);
511  
512     for (e = hashtable[ h ]; e; e = e->next) {
513         if (strcmp(e->name, name) == 0) {
514             break;
515         }
516     }
517  
518     if (e) {
519         return (e); 
520     } else {
521         nidents++;
522         e = MakeEntry(name);
523         e->next = hashtable[ h ];
524         hashtable[ h ] = e;
525         return (e);
526     }
527 }
528
529
530 /*
531  *      Store information from a sample. 
532  */
533  
534 void
535 StoreSample(en, bucket, value)
536   struct entry* en; intish bucket; floatish value;
537 {
538     struct chunk* chk; 
539
540     for (chk = en->chk; chk->next != 0; chk = chk->next)
541         ; 
542
543     if (chk->nd < N_CHUNK) {
544         chk->d[ chk->nd ].bucket = bucket;
545         chk->d[ chk->nd ].value  = value;
546         chk->nd += 1;
547     } else {
548         struct chunk* t;
549         t = chk->next = MakeChunk(); 
550         t->d[ 0 ].bucket = bucket;
551         t->d[ 0 ].value  = value;
552         t->nd += 1;
553     }
554 }
555
556
557 struct entry** identtable;
558
559 /*
560  *      The hash table is useful while reading the input, but it
561  *      becomes a liability thereafter. The code below converts 
562  *      it to a more easily processed table.
563  */
564
565 static void
566 MakeIdentTable()
567 {
568     intish i;
569     intish j;
570     struct entry* e;
571
572     nidents = 0;
573     for (i = 0; i < N_HASH; i++) {
574         for (e = hashtable[ i ]; e; e = e->next) {
575             nidents++;
576         }
577     }
578
579     identtable = (struct entry**) xmalloc(nidents * sizeof(struct entry*));
580     j = 0;
581
582     for (i = 0; i < N_HASH; i++) {
583         for (e = hashtable[ i ]; e; e = e->next, j++) {
584             identtable[ j ] = e; 
585         }
586     }
587 }