File: errhandler.c

package info (click to toggle)
fcode-utils 1.0.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 46,768 kB
  • sloc: ansic: 9,717; csh: 241; makefile: 129; sh: 17
file content (1001 lines) | stat: -rw-r--r-- 40,375 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
/*
 *                     OpenBIOS - free your system!
 *                         ( FCode tokenizer )
 *
 *  This program is part of a free implementation of the IEEE 1275-1994
 *  Standard for Boot (Initialization Configuration) Firmware.
 *
 *  Copyright (C) 2001-2010 Stefan Reinauer
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; version 2 of the License.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA
 *
 */

/* **************************************************************************
 *
 *      Error-Handler for Tokenizer
 *
 *      Controls printing of various classes of errors
 *
 *      (C) Copyright 2005 IBM Corporation.  All Rights Reserved.
 *      Module Author:  David L. Paktor    dlpaktor@us.ibm.com
 *
 **************************************************************************** */

/* **************************************************************************
 *
 *      Functions Exported:
 *          init_error_handler  Initialize the error-counts,
 *                                  announce the file names.
 *          tokenization_error  Handle an error of the given class,
 *                                  print the given message in the 
 *                                  standard format.
 *          started_at          Supplemental message, giving a back-reference
 *                                  to the "starting"  point of a compound
 *                                  error, including last-colon identification.
 *          just_started_at     Supplemental back-reference to "starting"  point
 *                                  of compound error, but without last-colon
 *                                  identification.
 *          where_started       Supplemental message, giving a more terse back-
 *                                  -reference to "start" of compound-error.
 *          just_where_started Supplemental message, more terse back-reference,
 *                                  without last-colon identification.
 *          in_last_colon      Supplemental back-reference message,
 *                                  identifying last Colon-definition.
 *          safe_malloc         malloc with built-in failure test.
 *          error_summary       Summarize final error-message status
 *                                  before completing tokenization.
 *
 **************************************************************************** */

/* **************************************************************************
 *
 *      Revision History:
 *          Updated Fri, 13 Oct 2006 by David L. Paktor
 *          Added "(Output Position ..." to standard message format.
 *
 **************************************************************************** */


/* **************************************************************************
 *
 *          We will define a set of bit-valued error-types and a
 *          global bit-mask.  Each error-message will be associated
 *          with one of the bit-valued error-types.  The bit-mask,
 *          which will be set by a combination of defaults and user
 *          inputs (mainly command-line arguments), will control
 *          whether an error-message of any given type is printed.
 *          
 *          Another bit-mask variable will accumulate the error-
 *          types that occur within any given run; at the end of
 *          the run, it will be examined to determine if the run
 *          failed, i.e., if the output should be suppressed.
 *
 **************************************************************************** */

/* **************************************************************************
 *
 *          Error-types fall into the following broad categories:
 *              FATAL           Cause to immediately stop activity
 *              TKERROR         Sufficient to make the run a failure,
 *                                  but not to stop activity.
 *              WARNING         Not necessarily an error, but something
 *                                  to avoid.  E.g., it might rely on
 *                                  assumptions that are not necessarily
 *                                  what the user/programmer wants.  Or:
 *                                  It's a deprecated feature, or one
 *                                  that might be incompatible with
 *                                  other standard tokenizers.
 *
 *          Other types of Messages fall into these broad categories:
 *              INFO            Nothing is changed in processing, but
 *                                  an advisory is still in order.  Omitted
 *                                  if "verbose" is not specified.
 *              MESSAGE         Message generated by the user.  (Complete;
 *                                  new-line will be added by display routine.)
 *              P_MESSAGE       Partial Message -- Instigated by user, but
 *                                  pre-formatted and not complete.  New-line
 *                                  will be added by follow-up routine.
 *              TRACER          Message related to the trace-symbols option;
 *                                  either a creation or an invocation message.
 *
 **************************************************************************** */

#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#include "types.h"
#include "toke.h"
#include "stream.h"
#include "emit.h"
#include "errhandler.h"
#include "scanner.h"

/* **************************************************************************
 *
 *          Global Variables Imported
 *              iname           Name of file currently being processed
 *              lineno          Current line-number being processed
 *              noerrors        "Ignore Errors" flag, set by "-i" switch
 *              opc             FCode Output Buffer Position Counter
 *              pci_hdr_end_ob_off
 *                              Position in FCode Output Buffer of
 *                                   end of last PCI Header Block structure
 *              verbose         If true, enable Advisory Messages
 *
 **************************************************************************** */

/* **************************************************************************
 *
 *              Internal Static Variables
 *          print_msg               Whether beginning of a message was printed;
 *                                      therefore, whether to print the rest.
 *          errs_to_print           Error Verbosity Mask.  Bits set correspond
 *                                      to message-types that will be printed
 *                                      May be altered by Command-Line switches.
 *          err_types_found         Accumulated Error-types.  Bits
 *                                      set correspond to error-types
 *                                      that have occurred.
 *          message_dest            Message Dest'n.  Usually ERRMSG_DESTINATION
 *                                      (stdout) except when we need to switch.
 *          err_count               Count of Error Messages
 *          warn_count              Count of Warning Messages
 *          info_count              Count of "Advisory" Messages
 *          user_msg_count          Count of User-generated Messages
 *          trace_msg_count         Count of Trace-Note Messages
 *          fatal_err_exit          Exit code to be used for "Fatal" error.
 *                                       This is a special accommodation
 *                                       for the  safe_malloc  routine.
 *
 **************************************************************************** */

static bool  print_msg ;
static int errs_to_print = ( FATAL | TKERROR | WARNING | 
                             MESSAGE | P_MESSAGE | TRACER | FORCE_MSG ) ;
static int err_types_found =  0 ;
static int err_count       =  0 ;
static int warn_count      =  0 ;
static int info_count      =  0 ;
static int user_msg_count  =  0 ;
static int trace_msg_count =  0 ;
static int fatal_err_exit  = -1 ;
static FILE *message_dest;     /*  Would like to init to  ERRMSG_DESTINATION
				*      here, but the compiler complains...
				*/

/* **************************************************************************
 *
 *              Internal Static Constant Structure
 *          err_category            Correlate each error-type code with its
 *                                      Counter-variable and the printable
 *                                      form of its name.
 *          num_categories          Number of entries in the err_category table
 *
 **************************************************************************** */

typedef struct {
    int  type_bit ;		/*  Error-type single-bit code        */
    char *category_name ;	/*  Printable-name base               */
    char *single ;		/*  Suffix to print singular of name  */
    char *plural ;		/*  Suffix to print plural of name    */
    int  *counter ;		/*  Associated Counter-variable       */
    bool new_line ;		/*  Whether to print new-line at end  */
} err_category ;

static const err_category  error_categories[] = {
    /*  FATAL  must be the first entry in the table.   */
    /*  No plural is needed; only one is allowed....   */
    { FATAL,    "Fatal Error", "", "",     &err_count      , TRUE  },

    { TKERROR,    "Error"     , "", "s",    &err_count      , FALSE },
    { WARNING,    "Warning"   , "", "s",    &warn_count     , FALSE },
    { INFO,       "Advisor"   , "y", "ies", &info_count     , FALSE },
    { MESSAGE ,   "Message"   , "", "s",    &user_msg_count , TRUE  },
    { P_MESSAGE , "Message"   , "", "s",    &user_msg_count  , FALSE },
    { TRACER , "Trace-Note"   , "", "s",    &trace_msg_count , FALSE }
};

static const int num_categories =
    ( sizeof(error_categories) / sizeof(err_category) );


/* **************************************************************************
 *
 *      Function name:  toup
 *      Synopsis:       Support function for  strupper
 *                      Converts one character
 *
 *      Inputs:
 *         Parameters:
 *             chr_ptr                 Pointer to the character
 *
 *      Outputs:
 *         Returned Value:             None
 *         Supplied Pointers:
 *             The character pointed to is changed
 *
 *      Process Explanation:
 *          Because this fills in a lack in the host system, we cannot
 *              rely on the functions  islower  or  toupper , which are
 *              usually built-in but might be similarly missing.
 *
 **************************************************************************** */

static void toup( char *chr_ptr)
{
    const unsigned char upcas_diff = ( 'a' - 'A' );
    if ( ( *chr_ptr >= 'a' ) && ( *chr_ptr <= 'z' ) )
    {
	*chr_ptr -= upcas_diff ;
    }
}

/* **************************************************************************
 *
 *      Function name:  strupper
 *      Synopsis:       Replacement for  strupr  on systems that don't 
 *                      seem to have it.  A necessary hack.
 *
 *      Inputs:
 *         Parameters:
 *             strung              Pointer to the string to be changed
 *
 *      Outputs:
 *         Returned Value:         Same pointer that was passed in
 *         Supplied Pointers:
 *             The string pointed to will be converted to upper case
 *
 *      Process Explanation:
 *          Because it fills in a lack in the host system, this routine
 *              does not rely on the functions  islower  or  toupper
 *              which are usually built-in but might be missing.
 *
 **************************************************************************** */

char *strupper( char *strung)
{
    char *strindx;
    for (strindx = strung; *strindx != 0; strindx++)
    {
        toup( strindx);
    }
    return strung;
}

/* **************************************************************************
 *
 *     If  strupr  is missing, it's a good bet that so is  strlwr 
 *
 **************************************************************************** */

/* **************************************************************************
 *
 *      Function name:  tolow
 *      Synopsis:       Support function for  strlower
 *                      Converts one character
 *
 *      Inputs:
 *         Parameters:
 *             chr_ptr                 Pointer to the character
 *
 *      Outputs:
 *         Returned Value:             None
 *         Supplied Pointers:
 *             The character pointed to is changed
 *
 *      Process Explanation:
 *          Because this fills in a lack in the host system, we cannot
 *              rely on the functions  isupper  or  tolower , which are
 *              usually built-in but might be similarly missing.
 *
 **************************************************************************** */

static void tolow( char *chr_ptr)
{
    const unsigned char lowcas_diff = ( 'A' - 'a' );
    if ( ( *chr_ptr >= 'A' ) && ( *chr_ptr <= 'Z' ) )
    {
	*chr_ptr -= lowcas_diff ;
    }
}

/* **************************************************************************
 *
 *      Function name:  strlower
 *      Synopsis:       Replacement for  strlwr  on systems that don't 
 *                      seem to have it.  A necessary hack.
 *
 *      Inputs:
 *         Parameters:
 *             strung              Pointer to the string to be changed
 *
 *      Outputs:
 *         Returned Value:         Same pointer that was passed in
 *         Supplied Pointers:
 *             The string pointed to will be converted to lower case
 *
 *      Process Explanation:
 *          Because it fills in a lack in the host system, this routine
 *              does not rely on the functions  isupper  or  tolower
 *              which are usually built-in but might be missing.
 *
 **************************************************************************** */

char *strlower( char *strung)
{
    char *strindx;
    for (strindx = strung; *strindx != 0; strindx++)
    {
        tolow( strindx);
    }
    return strung;
}


/* **************************************************************************
 *
 *      Function name:  init_error_handler
 *      Synopsis:       Initialize the error-handler before starting a
 *                          new tokenization; both the aspects that will
 *                          persist across the entire run and those that
 *                          need to be reset, such as error-counts.
 *
 *      Inputs:
 *         Parameters:                 NONE
 *         Global Variables: 
 *              verbose                Set by "-v" switch
 *         Macro:
 *             ERRMSG_DESTINATION      Error message destination;
 *                                         (Set by development-time switch)
 *             FFLUSH_STDOUT           Flush STDOUT if err-msg-dest is STDERR
 *
 *      Outputs:
 *         Returned Value:             NONE
 *         Global Variables:
 *             errs_to_print           Add the INFO bit if verbose is set
 *         Local Static Variables:
 *             message_dest            Point it at ERRMSG_DESTINATION (stderr)
 *           Reset the following to zero:
 *             err_types_found         Accumulated Error-types.
 *             err_count               Count of Error Messages
 *             warn_count              Count of Warning Messages
 *             info_count              Count of "Advisory" Messages
 *             user_msg_count          Count of User-generated Messages
 *             trace_msg_count         Count of Trace-Note Messages
 *         Other Exotic Effects:
 *             Flush stdout if Error message destination is not stdout, to
 *                 avoid collisions with stderr once Error Messaging begins.
 *
 *      Extraneous Remarks:
 *          This needs to be done before attempting to read the input file,
 *              so that any Messages that occur there can be properly counted.
 *
 **************************************************************************** */

void init_error_handler( void)
{
    int indx ;

    message_dest  =  ERRMSG_DESTINATION;
    if ( verbose )  errs_to_print |= INFO ;
    err_types_found = 0 ;

    /*  Start at indx = 1 to skip resetting FATALs   */
    for ( indx = 1; indx < num_categories ; indx ++ )
    {
	*(error_categories[indx].counter) = 0 ;
    }

    FFLUSH_STDOUT
}

/* **************************************************************************
 *
 *      Function name:    tokenization_error
 *      Synopsis:         Handle an error of the given class,
 *                            print the given message in the standard format.
 *      
 *      Inputs:
 *         Parameters:
 *             err_type       int        One of the bit-valued error-types
 *             The remaining parameters are a format string and corresponding
 *                 data objects such as would be sent to  printf() 
 *         Global Variables:
 *             errs_to_print        Error Verbosity Mask.
 *             iname                Name of file currently being processed
 *             lineno               Current line-number being processed
 *             fatal_err_exit       Exit code for "Fatal" error, if applicable.
 *             opc                  FCode Output Buffer Position Counter
 *             pci_hdr_end_ob_off
 *                                  Position in FCode Output Buffer of end
 *                                       of last PCI Header Block structure

 *         Macro:
 *             ERRMSG_DESTINATION        Error message destination;
 *                                           (Development-time switch)
 *         Note:  Whether this routine will or will not supply a new-line
 *             at the end of the printout depends on the category of the
 *             message.  The new-line is included for a FATAL or a User-
 *             Generated Message, and excluded for the rest.  For those,
 *             the calling routine must be responsible for including a
 *             new-line at the end of the format string or for otherwise
 *             finishing the line, as by calling started_at()
 *
 *      Outputs:
 *         Returned Value:                 NONE
 *         Local Static Variables:
 *             err_types_found             Accumulated Error-types.
 *             print_msg                   Whether this message was printed;
 *                                             may be used by started_at()
 *                    One of the following Category Counters
 *                         will be incremented, as applicable:
 *             err_count
 *             warn_count
 *             info_count 
 *             user_msg_count
 *         Printout:    Directed to  stdout or stderr 
 *                          (see definition of ERRMSG_DESTINATION)
 *
 *      Error Detection:
 *              Err_type not in list
 *                      Print special message; treat cause as an Error.
 *                      Force printout.
 *
 *      Process Explanation:
 *          Accumulated the Error-type into  err_types_found 
 *          Identify the Error-Category:
 *              Check the Error-Type against the bit-code.
 *                  The Error-type may have more than one bit set,
 *                  but if it matches the Category bit-code, it's it.
 *              If it doesn't match any Error-Category bit-code, print
 *                  a special message and treat it as an ERROR code.
 *          Check the Error-Type against the Error Verbosity Mask;
 *          If it has a bit set, print the Error-Category, together
 *                  with the source-file name and line number, and
 *                  the rest of the message as supplied.
 *              The table that translates the Error-type into a printable
 *                  Error-Category string also identifies the applicable
 *                  Category Counter; increment it.
 *          Of course, there's no return from a FATAL error; it exits.
 *          The Message will show:
 *              The Error-Category (always)
 *              The Input File-name and Line Number (if input file was opened)
 *              The Output Buffer Position (if output has begun)
 *              The PCI-Block Position (if different from Output Buffer Pos'n)
 *
 **************************************************************************** */

void tokenization_error( int err_type, char* msg, ... )
{
    int indx ;

    /*  Initial settings:  treat as an Error.  */
    char *catgy_name = "Error";
    char *catgy_suffx = "";
    int *catgy_counter = &err_count;
    bool print_new_line = FALSE;

    /*  Accumulated the Error-type into  err_types_found  */
    err_types_found |= err_type;

    /*  Identify the Error-Category.  */
    for ( indx = 0 ; indx < num_categories ; indx ++ )
    {
        if ( ( error_categories[indx].type_bit & err_type ) != 0 )
        {
            catgy_name = error_categories[indx].category_name;
            catgy_suffx = error_categories[indx].single;
            catgy_counter = error_categories[indx].counter;
	    print_new_line = error_categories[indx].new_line;
            break;
        }
    }

    /*  Special message if  err_type  not in list; treat as an Error.  */
    if ( catgy_name == NULL )
    {
         fprintf(ERRMSG_DESTINATION,
	      "Program error: Unknown Error-Type, 0x%08x.  "
              "  Will treat as Error.\n", err_type) ;
         err_types_found |= TKERROR;
         print_msg = TRUE ;
    } else {
         /*  Check the Error-Type against the Error Verbosity Mask  */
         print_msg = BOOLVAL( ( errs_to_print & err_type ) != 0 );
    }

    if ( print_msg )
    {
        va_list argp;

	fprintf(ERRMSG_DESTINATION, "%s%s:  ",
             catgy_name, catgy_suffx);
        if ( iname != NULL )
	{
	    /*  Don't print iname or lineno if no file opened.  */
	    fprintf(ERRMSG_DESTINATION, "File %s, Line %d.  ",
        	 iname, lineno);
	}
        if ( opc > 0 )
	{
	    /*  Don't print Output Position if no output started.  */
	    fprintf(ERRMSG_DESTINATION, "(Output Position = %d).  ", opc);
	}
        if ( pci_hdr_end_ob_off > 0 )
	{
	    /*  Don't print PCI-Block Position if no PCI-Block in effect.  */
	    fprintf(ERRMSG_DESTINATION, "(PCI-Block Position = %d).  ",
	        opc - pci_hdr_end_ob_off );
	}

        va_start(argp, msg);
        vfprintf(ERRMSG_DESTINATION, msg, argp);
        va_end(argp);
	if ( print_new_line ) fprintf(ERRMSG_DESTINATION, "\n");

	/*   Increment the category-counter.  */
	*catgy_counter += 1;
    }
    if ( err_type == FATAL )
    {
        fprintf(ERRMSG_DESTINATION, "Tokenization terminating.\n");
        error_summary();
        exit ( fatal_err_exit );
    }
}

/* **************************************************************************
 *
 *      Function name:  print_where_started
 *      Synopsis:       Supplemental message, following a tokenization_error,
 *                          giving a back-reference to the "start" point of
 *                          the compound-error being reported.
 *                      This is a retro-fit; it does the heavy lifting for
 *                          the routines  started_at() ,  just_started_at() , 
 *                           where_started() ,  just_where_started() and
 *                           in_last_colon() .
 *
 *      Inputs:
 *         Parameters:
 *             show_started         Whether to print a phrase about "started"
 *             show_that_st         Whether to print "that started" as opposed
 *                                      to " , which started"
 *             saved_ifile          File-name saved for "back-reference"
 *             saved_lineno         Line-number saved for "back-reference"
 *             may_show_incolon     Whether to allow a call to  in_last_colon()
 *                                      Needed to prevent infinite recursion...
 *         Global Variables:        
 *             iname                Name of file currently being processed
 *             lineno               Current line-number being processed
 *         Local Static Variables:
 *             print_msg            Whether the beginning part of the message
 *                                      was printed by tokenization_error()
 *             message_dest         Message Destination. Is ERRMSG_DESTINATION
 *                                      (stdout) usually, except sometimes...
 *
 *      Outputs:
 *         Returned Value:          None
 *         Printout:
 *             The remainder of a message:  the location of a back-reference.
 *                 The phrase "that started" is switchable.  This routine
 *                 will supply the leading space and a new-line; the routines
 *                 that call this can be used to finish the line.
 *
 *      Process Explanation:
 *          This routine is called immediately after tokenization_error()
 *              If tokenization_error() didn't print, neither will we.
 *              The residual state of  print_msg  will tell us that.
 *          If the preceding message ended with something general about a
 *              "Colon Definition" or "Device-Node" or the like, we want
 *              the message to read:  "that started on line ... [in file ...]"
 *          If the end of the preceding message was something more specific,
 *              we just want the message to read:  "on line ... [in file ...]"
 *          If the saved input file name doesn't match our current input
 *              file name, we will print it and the saved line-number.
 *          If the file name hasn't changed, we will print only the saved
 *              line-number.
 *          If neither is changed, there's no point in printing any of the
 *              above-mentioned text.    
 *          If a Colon-definition is in progress, show its name and the
 *              line on which it started.  Protect against infinite loop!
 *          End the line.
 *
 *      Extraneous Remarks:
 *          This is a retrofit.  Earlier, it was just  started_at() .  Later,
 *              I generated more specific messages, and needed a way to leave
 *              out the "that started".  I could, theoretically, have added
 *              the extra parameter to  started_at() , but by now there are
 *              so many of calls to it that I'd rather leave them as is, and
 *              just change the name of the routine in the few places that
 *              need the terser form of the message.
 *
 **************************************************************************** */

static void print_where_started( bool show_started,
                                   bool show_that_st,
				   char * saved_ifile,
				       unsigned int saved_lineno,
                                           bool may_show_incolon)
{
    if ( print_msg )
    {
	bool fil_is_diff;
	bool lin_is_diff;

	/*  File names are case-sensitive  */
	fil_is_diff = BOOLVAL(strcmp(saved_ifile, iname) != 0 );
	lin_is_diff = BOOLVAL(saved_lineno != lineno );
	if ( fil_is_diff || lin_is_diff )
	{
	    if ( show_started )
	    {
		if ( show_that_st )
		{
		    fprintf(message_dest, " that");
		}else{
		    fprintf(message_dest, " , which");
		}
		fprintf(message_dest, " started");
	    }
	    fprintf(message_dest, " on line %d", saved_lineno);
	    if ( fil_is_diff )
	    {
	        fprintf(message_dest, " of file %s", saved_ifile);
	    }
	}

	if ( may_show_incolon )
	{
	    in_last_colon( TRUE );
	}else{
	    fprintf(message_dest, "\n");
	}
    }
}

/* **************************************************************************
 *
 *      Function name:  started_at
 *      Synopsis:       Supplemental back-reference message,
 *                          with the "that started"  phrase,
 *                          and with last-colon identification.
 *
 *      Inputs:
 *         Parameters:
 *             saved_ifile          File-name saved for "back-reference"
 *             saved_lineno         Line-number saved for "back-reference"
 *
 *      Outputs:
 *         Returned Value:          None
 *         Global Variables:
 *         Printout:
 *             The "...started at..." remainder of a message, giving a back-
 *                 -reference to the  "start" point supplied in the params,
 *                 and the start of the current Colon-definition if one is
 *                 in effect.
 *             Will supply a new-line and can be used to finish the line.
 *
 **************************************************************************** */

void started_at( char * saved_ifile, unsigned int saved_lineno)
{
    print_where_started( TRUE, TRUE, saved_ifile, saved_lineno, TRUE);
}


/* **************************************************************************
 *
 *      Function name:  print_started_at
 *      Synopsis:       Same as started_at() except output will be directed
 *                          to  stdout  instead of to ERRMSG_DESTINATION
 *
 *      Extraneous Remarks:
 *          A retrofit.  Can you tell?
 *
 **************************************************************************** */
 
void print_started_at( char * saved_ifile, unsigned int saved_lineno)
{
    message_dest = stdout;
	started_at( saved_ifile, saved_lineno);
    message_dest = ERRMSG_DESTINATION;
}


/* **************************************************************************
 *
 *      Function name:  just_started_at
 *      Synopsis:       Supplemental back-reference message,
 *                          with the "that started"  phrase,
 *                          but without last-colon identification.
 *
 *      Inputs:
 *         Parameters:
 *             saved_ifile          File-name saved for "back-reference"
 *             saved_lineno         Line-number saved for "back-reference"
 *
 *      Outputs:
 *         Returned Value:          None
 *         Global Variables:
 *         Printout:
 *             The "...started at..." remainder of a message, giving a back-
 *                 -reference to the  "start" point supplied in the params,
 *                 and no more.
 *             Will supply a new-line and can be used to finish the line.
 *
 **************************************************************************** */

void just_started_at( char * saved_ifile, unsigned int saved_lineno)
{
    print_where_started( TRUE, TRUE, saved_ifile, saved_lineno, FALSE);
}

/* **************************************************************************
 *
 *      Function name:  where_started
 *      Synopsis:       Supplemental back-reference message,
 *                          without the "that started"  phrase,
 *                          but with last-colon identification.
 *
 *      Inputs:
 *         Parameters:
 *             saved_ifile          File-name saved for "back-reference"
 *             saved_lineno         Line-number saved for "back-reference"
 *
 *      Outputs:
 *         Returned Value:          None
 *         Global Variables:
 *         Printout:
 *             The remainder of a message, giving a back-reference to the
 *                 "start" point supplied in the parameters, and the start
 *                 of the current Colon-definition if one is in effect.
 *             Will supply a new-line and can be used to finish the line.
 *
 **************************************************************************** */

void where_started( char * saved_ifile, unsigned int saved_lineno)
{
    print_where_started( FALSE, FALSE, saved_ifile, saved_lineno, TRUE);
}

/* **************************************************************************
 *
 *      Function name:  just_where_started
 *      Synopsis:       Supplemental back-reference message,
 *                          without the "that started"  phrase,
 *                          and without last-colon identification.
 *
 *      Inputs:
 *         Parameters:
 *             saved_ifile          File-name saved for "back-reference"
 *             saved_lineno         Line-number saved for "back-reference"
 *
 *      Outputs:
 *         Returned Value:          None
 *         Global Variables:
 *         Printout:
 *             The remainder of a message, giving a back-reference to the
 *                 "start" point supplied in the parameters, and no more.
 *             Will supply a new-line and can be used to finish the line.
 *
 **************************************************************************** */

void just_where_started( char * saved_ifile, unsigned int saved_lineno)
{
    print_where_started( FALSE, FALSE, saved_ifile, saved_lineno, FALSE);
}

/* **************************************************************************
 *
 *      Function name:  in_last_colon
 *      Synopsis:       Supplemental back-reference message, identifying
 *                          last Colon-definition if one is in effect.
 *                      Can be used to finish the line in either case.
 *
 *      Inputs:
 *         Parameters:
 *             say_in                    If TRUE, lead phrase with " in ".
 *                                           If FALSE, print even if not
 *                                            inside a Colon-def'n.
 *         Global Variables:
 *             incolon                   TRUE if Colon-definition is in progress
 *             last_colon_defname        Name of last colon-definition
 *             last_colon_filename       File where last colon-def'n made
 *             last_colon_lineno         Line number of last colon-def'n
 *         Local Static Variables:
 *             print_msg            Whether the beginning part of the message
 *                                      was printed by tokenization_error()
 *             message_dest         Message Destination. Is ERRMSG_DESTINATION
 *                                      (stdout) usually, except sometimes...
 *
 *      Outputs:
 *         Returned Value:                  NONE
 *         Printout:
 *             Remainder of a message:
 *                "in definition of  ... , which started ..."
 *
 *      Process Explanation:
 *          Because this routine does some of its own printing, it needs
 *              to check the residual state of  print_msg  first.
 *          The calling routine does not need to test   incolon ; it can
 *              call this (with TRUE) to end the line in either case.
 *
 **************************************************************************** */

void in_last_colon( bool say_in )
{
    if ( print_msg )
    {
	if ( incolon || ( ! say_in ) )
	{
	    fprintf( message_dest, "%s definition of  %s ", say_in ? " in" : "",
		strupr( last_colon_defname) );
	    print_where_started( TRUE, FALSE,
		last_colon_filename, last_colon_lineno, FALSE);
	}else{
	    fprintf(message_dest, "\n");
	}
    }
}


/* **************************************************************************
 *
 *      Function name:  safe_malloc
 *      Synopsis:       malloc with built-in failure test.
 *      
 *      Inputs:
 *         Parameters:
 *             size       size_t     Size of memory-chunk to allocate
 *             phrase     char *     Phrase to print after "... while "
 *                                       in case of failure.
 *
 *      Outputs:
 *         Returned Value:           Pointer to allocated memory
 *         Global Variables:
 *             fatal_err_exit       On memory allocation failure, change
 *                                       to a special system-defined value
 *
 *      Error Detection:
 *          On memory allocation failure, declare a FATAL error.  Set up
 *              for a special system-defined EXIT value that indicates
 *              insufficient memory.
 *
 *      Process Explanation:
 *          It is the responsibility of the calling routine to be sure
 *              the "phrase" is unique within the program.  It is intended
 *              as a debugging aid, to help localize the point of failure.
 *
 **************************************************************************** */

_PTR safe_malloc( size_t size, char *phrase)
{
    _PTR retval ;
    retval = malloc (size);
    if ( !retval )
    {
        fatal_err_exit = -ENOMEM ;
        tokenization_error( FATAL, "Out of memory while %s.", phrase);
    }
    return ( retval );
}

/* **************************************************************************
 *
 *      Function name:         error_summary
 *      Synopsis:              Summarize final error-message status
 *                                 before completing tokenization.
 *                             Indicate if OK to produce output.
 *      
 *      Inputs:
 *         Parameters:                   NONE
 *         Global Variables:        
 *             noerrors             "Ignore Errors" flag, set by "-i" switch
 *             err_types_found      Accumulated Error-types.
 *             error_categories     Table of Error-types, Message-Counters
 *                                      and their printable names.
 *             opc                  FCode Output Buffer Position Counter
 *                                      (zero means there was no output).
 *
 *      Outputs:
 *         Returned Value:          True = OK to produce output (But caller
 *                                      must still verify non-zero opc)
 *         Printout:
 *             Various messages.
 *
 *      Process Explanation:
 *          The first entry in the error_categories table is FATAL    
 *              We won't need to print a tally of that...
 *      
 **************************************************************************** */

bool error_summary( void )
{
    /*  Bit-mask of error-types that require suppressing output   */
    static const int suppress_mask = ( FATAL | TKERROR );
    bool retval = TRUE;
    bool suppressing = FALSE;

    /*  There's no escaping a FATAL error   */
    if ( ( err_types_found & FATAL ) != 0 )
    {
	/*   FATAL error.  Don't even bother with the tally.   */
	suppressing = TRUE;
    } else {

	if ( opc == 0 )
	{
	    printf ( "Nothing Tokenized");
	}else{
	    printf ( "Tokenization Completed");
	}

	if ( err_types_found != 0 )
	{
	    int indx;
	    bool tally_started = FALSE ;
	    printf (". ");
	    /*
	     *  Print a tally of the error-types;
	     *  handle plurals and punctuation appropriately.
	     */
	    /*  Start at indx = 1 to skip examining FATALs   */
	    for ( indx = 1; indx < num_categories ; indx ++ )
	    {
		if ( *(error_categories[indx].counter) > 0 )
		{
		    printf ("%s %d %s%s",
	        	tally_started ? "," : "" ,
			    *(error_categories[indx].counter),
				error_categories[indx].category_name,
				    *(error_categories[indx].counter) > 1 ?
					 error_categories[indx].plural :
					     error_categories[indx].single );
		    /*  Zero out the counter, to prevent displaying the
		     *      number of Messages twice, since it's shared
		     *      by the "Messages" and "P_Messages" categories.
		     */
		    *(error_categories[indx].counter) = 0;
		    tally_started = TRUE;
		}
	    }
	}
        printf (".\n");

	if ( ( err_types_found & suppress_mask ) != 0 )
	{    /*  Errors found.  Not  OK to produce output    */
             /*  Unless "Ignore Errors" flag set...          */
	    if ( INVERSE(noerrors) )
            {
		suppressing = TRUE;
            }else{
		if ( opc > 0 )
		{
		    printf ("Error-detection over-ridden; "
				"producing binary output.\n");
		}
            }
	}
    }
    if ( suppressing )
    {
	retval = FALSE ;
	printf ("Suppressing binary output.\n");
    }
    return ( retval );
}