1 /* @source ajmess *************************************************************
2 **
3 ** AJAX message functions
4 **
5 ** @author Richard Durbin and Ed Griffiths from ACEdb (messubs.c)
6 ** @version $Revision: 1.56 $
7 ** @modified Ian Longden for EMBOSS
8 ** @modified $Date: 2012/12/07 09:54:19 $ by $Author: rice $
9 ** @@
10 **
11 ** This library is free software; you can redistribute it and/or
12 ** modify it under the terms of the GNU Lesser General Public
13 ** License as published by the Free Software Foundation; either
14 ** version 2.1 of the License, or (at your option) any later version.
15 **
16 ** This library is distributed in the hope that it will be useful,
17 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
18 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19 ** Lesser General Public License for more details.
20 **
21 ** You should have received a copy of the GNU Lesser General Public
22 ** License along with this library; if not, write to the Free Software
23 ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
24 ** MA  02110-1301,  USA.
25 **
26 ******************************************************************************/
27 
28 #include <string.h>
29 
30 #include <errno.h>
31 #include <limits.h>
32 #include <stdlib.h>
33 
34 #include "ajmess.h"
35 #include "ajassert.h"
36 #include "ajmem.h"
37 #include "ajtable.h"
38 #include "ajfmt.h"
39 #include "ajsys.h"
40 #include "ajfile.h"
41 #include "ajutil.h"
42 #include "ajnam.h"
43 #include "ajfileio.h"
44 
45 #ifdef __CYGWIN__
46 #define fopen(a,b) ajSysFuncFopen(a,b)
47 #endif
48 
49 
50 /* next six moved from acd for library splitting */
51 
52 AjBool acdDebugSet    = 0;
53 AjBool acdDebugBuffer = 0;
54 AjBool acdDebug       = 0;
55 AjPStr acdProgram     = NULL;
56 AjPStr acdAppldoc     = NULL;
57 AjPStr acdArgSave     = NULL;
58 AjPStr acdInputSave   = NULL;
59 
60 
61 
62 AjOError AjErrorLevel =
63 {
64     AJTRUE, AJTRUE, AJTRUE, AJTRUE
65 };
66 
67 /***************************************************/
68 
69 
70 
71 AjPTable messErrorTable = 0;
72 
73 static ajint errorCount = 0;
74 
75 static char *messErrorFile;
76 
77 static AjBool messDebug      = 0;
78 static AjPFile messDebugFile = NULL;
79 static AjPFile messDebugTestFile = NULL;
80 static AjPStr messDebugName  = NULL;
81 static char* messErrMess = NULL;
82 static AjBool messDebugTestInit = AJFALSE;
83 
84 static char* messGetFilename(const char *path);
85 static void messTableDelete(AjPTable* table);
86 
87 static AjPTable messDebugTestTable = NULL;
88 
89 
90 /*============================================================================
91 **======================== Macros ============================================
92 =============================================================================*/
93 
94 
95 
96 
97 /* @macro ajFatal *************************************************************
98 **
99 ** Fatal error message to standard error.
100 ** Includes filename and line number in the source code that invokes it.
101 ** Newline is added automatically at the end of the format string.
102 **
103 ** @param [r] format [const char*] Format
104 ** @param [v] [...] Variable length argument list
105 ** @return [void]
106 ** @@
107 ******************************************************************************/
108 
109 
110 
111 
112 /* @macro ajMessCrash *********************************************************
113 **
114 ** Crash error message to standard error.
115 ** Includes filename and line number in the source code that invokes it.
116 ** Newline is added automatically at the end of the format string.
117 **
118 ** @param [r] format [const char*] Format
119 ** @param [v] [...] Variable length argument list
120 ** @return [void]
121 ** @@
122 ******************************************************************************/
123 
124 
125 
126 
127 /* @func ajMessInvokeDebugger *************************************************
128 **
129 ** Used to trace in a debugger as a breakpoint
130 **
131 ** @return [void]
132 **
133 ** @release 1.0.0
134 ** @@
135 ******************************************************************************/
136 
ajMessInvokeDebugger(void)137 void ajMessInvokeDebugger(void)
138 {
139     static AjBool reentrant = AJFALSE;
140 
141     if(!reentrant)
142     {
143 	reentrant = AJTRUE;
144 	reentrant = AJFALSE;
145     }
146 
147     return;
148 }
149 
150 
151 
152 
153 /******************************************************************************
154 ** Constraints on message buffer size.
155 **
156 ** BUFSIZE:  size of message buffers (messbuf, a global buffer for general
157 **           message stuff and a private one in ajMessDump).
158 ** PREFIX:   length of message prefix (used to report details such as the
159 **           file/line info. for where the error occurred.
160 ** MAINTEXT: space left in buffer is the rest after the prefix and string
161 **           terminator (NULL) are subtracted.
162 ******************************************************************************/
163 
164 enum {BUFSIZE = 32768,
165       PREFIXSIZE = 1024,
166       MAINTEXTSIZE = BUFSIZE - PREFIXSIZE - 1};
167 
168 /******************************************************************************
169 ** This buffer is used by just about all of the below routines and has the
170 ** obvious problem that strings can run over the end of it, we can only
171 ** detect this after the event with vsprintf.
172 ******************************************************************************/
173 
174 static char messbuf[BUFSIZE];
175 
176 /******************************************************************************
177 ** Format strings using va_xx calls.
178 ** Arguments to the macro must have the following types:
179 **   FORMAT_ARGS:   va_list used to get the variable argument list.
180 **        FORMAT:   char *  to a string containing the printf format string.
181 **    TARGET_PTR:   char *  the formatted string will be returned in this
182 **                          string pointer, N.B. do not put &TARGET_PTR
183 **        PREFIX:   char *  to a string to be used as a prefix to the rest
184 **                          of the string, or NULL.
185 ******************************************************************************/
186 
187 #define AJAXFORMATSTRING(FORMAT_ARGS, FORMAT, TARGET_PTR, PREFIX)    \
188 va_start(FORMAT_ARGS, FORMAT);                                       \
189 TARGET_PTR = messFormat(FORMAT_ARGS, FORMAT, PREFIX);                \
190 va_end(FORMAT_ARGS);
191 
192 #define AJAXVFORMATSTRING(FORMAT_ARGS, FORMAT, TARGET_PTR, PREFIX)   \
193 TARGET_PTR = messFormat(FORMAT_ARGS, FORMAT, PREFIX);
194 
195 static char *messFormat(va_list args, const char *format, const char *prefix);
196 static void messDump(const char *message);
197 
198 /* Some standard defines for titles/text for messages:                       */
199 /*                                                                           */
200 
201 #define MESG_TITLE "EMBOSS"
202 #define ERROR_PREFIX "Error: "
203 #define WARNING_PREFIX "Warning: "
204 #define EXIT_PREFIX "   An error spotted (non-EMBOSS): "
205 #define DIE_PREFIX "Died: "
206 #define CRASH_PREFIX_FORMAT "\n   %s An error in %s at line %d:\n"
207 #define FULL_CRASH_PREFIX_FORMAT "\n   %s Program cannot continue " \
208                                  "(%s, in file %s, at line %d):\n"
209 #define SYSERR_FORMAT "Something wrong with a system call (%d - %s)"
210 #define SYSERR_OK "Successful system call (%d - %s)"
211 
212 /******************************************************************************
213 ** ajMessCrash now reports the file/line no. where ajMessCrash was issued
214 ** as an aid to debugging. We do this using a static structure which holds
215 ** the information and a macro version of ajMessCrash (see regular.h), the
216 ** structure elements are retrieved using access functions.
217 ******************************************************************************/
218 
219 
220 
221 
222 /* @datastatic MessPErrorInfo *************************************************
223 **
224 ** Message error information
225 **
226 ** @alias MessSErrorInfo
227 ** @alias MessOErrorInfo
228 **
229 ** @attr progname [char*] Name of executable reporting error
230 ** @attr filename [char*] Filename where error was reported
231 ** @attr line_num [ajint] line number of file where error was reported.
232 ** @attr Padding [char[4]] Padding to alignment boundary
233 ** @@
234 ******************************************************************************/
235 
236 typedef struct MessSErrorInfo
237 {
238     char* progname;
239     char* filename;
240     ajint line_num;
241     char Padding[4];
242 } MessOErrorInfo;
243 
244 #define MessPErrorInfo MessOErrorInfo*
245 
246 static MessOErrorInfo messageG = {NULL, NULL, 0, ""};
247 
248 static ajint messGetErrorLine(void);
249 static char *messGetErrorFile(void);
250 static char *messGetErrorProgram(void);
251 
252 /***************************************************************/
253 /********* call backs and functions to register them ***********/
254 
255 static AjMessVoidRoutine beepRoutine    = 0;
256 static AjMessOutRoutine	 outRoutine     = 0;
257 static AjMessOutRoutine	 dumpRoutine    = 0;
258 static AjMessOutRoutine	 errorRoutine   = 0;
259 static AjMessOutRoutine	 exitRoutine    = 0;
260 static AjMessOutRoutine	 crashRoutine   = 0;
261 static AjMessOutRoutine	 warningRoutine = 0;
262 
263 
264 
265 
266 /* @func ajMessRegBeep ********************************************************
267 **
268 ** Sets a function to process beeps
269 **
270 ** @param [f] func [AjMessVoidRoutine] Function to be registered
271 ** @return [AjMessVoidRoutine] Previously defined function
272 **
273 ** @release 2.9.0
274 ** @@
275 ******************************************************************************/
276 
ajMessRegBeep(AjMessVoidRoutine func)277 AjMessVoidRoutine ajMessRegBeep(AjMessVoidRoutine func)
278 {
279     AjMessVoidRoutine old;
280 
281     old = beepRoutine;
282     beepRoutine = func;
283 
284     return old;
285 }
286 
287 
288 
289 
290 /* @func ajMessRegOut *********************************************************
291 **
292 ** Sets a function to write messages
293 **
294 ** @param [f] func [AjMessOutRoutine] Function to be registered
295 ** @return [AjMessOutRoutine] Previously defined function
296 **
297 ** @release 2.9.0
298 ** @@
299 ******************************************************************************/
300 
ajMessRegOut(AjMessOutRoutine func)301 AjMessOutRoutine ajMessRegOut(AjMessOutRoutine func)
302 {
303     AjMessOutRoutine old;
304 
305     old = outRoutine;
306     outRoutine = func;
307 
308     return old;
309 }
310 
311 
312 
313 
314 /* @func ajMessRegDump ********************************************************
315 **
316 ** Sets a function to dump data
317 **
318 ** @param [f] func [AjMessOutRoutine] Function to be registered
319 ** @return [AjMessOutRoutine] Previously defined function
320 **
321 ** @release 2.9.0
322 ** @@
323 ******************************************************************************/
324 
ajMessRegDump(AjMessOutRoutine func)325 AjMessOutRoutine ajMessRegDump(AjMessOutRoutine func)
326 {
327     AjMessOutRoutine old;
328 
329     old = dumpRoutine;
330     dumpRoutine = func;
331 
332     return old;
333 }
334 
335 
336 
337 
338 /* @func ajMessRegErr *********************************************************
339 **
340 ** Sets a function to report errors
341 **
342 ** @param [f] func [AjMessOutRoutine] Function to be registered
343 ** @return [AjMessOutRoutine] Previously defined function
344 **
345 ** @release 2.9.0
346 ** @@
347 ******************************************************************************/
348 
ajMessRegErr(AjMessOutRoutine func)349 AjMessOutRoutine ajMessRegErr(AjMessOutRoutine func)
350 {
351     AjMessOutRoutine old;
352 
353     old = errorRoutine;
354     errorRoutine = func;
355 
356     return old;
357 }
358 
359 
360 
361 
362 /* @func ajMessRegExit ********************************************************
363 **
364 ** Sets a function to exit
365 **
366 ** @param [f] func [AjMessOutRoutine] Function to be registered
367 ** @return [AjMessOutRoutine] Previously defined function
368 **
369 ** @release 2.9.0
370 ** @@
371 ******************************************************************************/
372 
ajMessRegExit(AjMessOutRoutine func)373 AjMessOutRoutine ajMessRegExit(AjMessOutRoutine func)
374 {
375     AjMessOutRoutine old;
376 
377     old = exitRoutine;
378     exitRoutine = func;
379 
380     return old;
381 }
382 
383 
384 
385 
386 /* @func ajMessRegCrash *******************************************************
387 **
388 ** Sets a function to crash
389 **
390 ** @param [f] func [AjMessOutRoutine] Function to be registered
391 ** @return [AjMessOutRoutine] Previously defined function
392 **
393 ** @release 2.9.0
394 ** @@
395 ******************************************************************************/
396 
ajMessRegCrash(AjMessOutRoutine func)397 AjMessOutRoutine ajMessRegCrash(AjMessOutRoutine func)
398 {
399     AjMessOutRoutine old;
400 
401     old = crashRoutine;
402     crashRoutine = func;
403 
404     return old;
405 }
406 
407 
408 
409 
410 /* @func ajMessRegWarn ********************************************************
411 **
412 ** Sets a function to print warnings
413 **
414 ** @param [f] func [AjMessOutRoutine] Function to be registered
415 ** @return [AjMessOutRoutine] Previously defined function
416 **
417 ** @release 2.9.0
418 ** @@
419 ******************************************************************************/
420 
ajMessRegWarn(AjMessOutRoutine func)421 AjMessOutRoutine ajMessRegWarn(AjMessOutRoutine func)
422 {
423     AjMessOutRoutine old;
424 
425     old = warningRoutine;
426     warningRoutine = func;
427 
428     return old;
429 }
430 
431 
432 
433 
434 /* @func ajMessBeep ***********************************************************
435 **
436 ** Calls the defined beep function, if any. Otherwise prints ASCII 7 to
437 ** standard error.
438 **
439 ** @return [void]
440 **
441 ** @release 1.0.0
442 ** @@
443 ******************************************************************************/
444 
ajMessBeep(void)445 void ajMessBeep(void)
446 {
447     if(beepRoutine)
448 	(*beepRoutine)();
449     else
450     {
451 	printf("%c",0x07);
452 	fflush(stdout);
453     }
454 
455     return;
456 }
457 
458 
459 
460 
461 /* @func ajUser ***************************************************************
462 **
463 ** Formats a message. Calls the defined output function (if any).
464 ** Otherwise prints the message to standard error with an extra newline.
465 **
466 ** @param [r] format [const char*] Format string
467 ** @param [v] [...] Variable length argument list
468 ** @return [void]
469 **
470 ** @release 2.9.0
471 ** @@
472 ******************************************************************************/
473 
ajUser(const char * format,...)474 void ajUser(const char *format,...)
475 {
476     va_list args;
477     const char *mesg_buf;
478 
479     AJAXFORMATSTRING(args, format, mesg_buf, NULL);
480 
481     if(outRoutine)
482 	(*outRoutine)(mesg_buf);
483     else
484 	fprintf(stderr, "%s\n", mesg_buf);
485 
486     ajDebug("ajUser: %s\n", mesg_buf);
487 
488     return;
489 }
490 
491 
492 
493 
494 /* @func ajUserDumpC **********************************************************
495 **
496 ** Prints a string unchanged. Calls the defined output function (if any).
497 ** Otherwise prints the message to standard error with an extra newline.
498 **
499 ** @param [r] txt [const char*] String to print unchanged
500 ** @return [void]
501 **
502 ** @release 6.0.0
503 ** @@
504 ******************************************************************************/
505 
ajUserDumpC(const char * txt)506 void ajUserDumpC(const char* txt)
507 {
508     if(outRoutine)
509 	(*outRoutine)(txt);
510     else
511 	fprintf(stderr, "%s\n", txt);
512 
513     ajDebug("ajUserDumpC: %s\n", txt);
514 
515     return;
516 }
517 
518 
519 
520 
521 /* @func ajUserDumpS **********************************************************
522 **
523 ** Prints a string unchanged. Calls the defined output function (if any).
524 ** Otherwise prints the message to standard error with an extra newline.
525 **
526 ** @param [r] str [const AjPStr] String to print unchanged
527 ** @return [void]
528 **
529 ** @release 6.0.0
530 ** @@
531 ******************************************************************************/
532 
ajUserDumpS(const AjPStr str)533 void ajUserDumpS(const AjPStr str)
534 {
535     const char *mesg_buf = ajStrGetPtr(str);
536 
537     if(outRoutine)
538 	(*outRoutine)(mesg_buf);
539     else
540 	fprintf(stderr, "%s\n", mesg_buf);
541 
542     ajDebug("ajUserDumpS: %s\n", mesg_buf);
543 
544     return;
545 }
546 
547 
548 
549 
550 /* @func ajMessOut ************************************************************
551 **
552 ** Formats a message. Calls the defined output function (if any).
553 ** Otherwise prints the message to standard error with no newline.
554 **
555 ** @param [r] format [const char*] Format string
556 ** @param [v] [...] Variable length argument list
557 ** @return [void]
558 **
559 ** @release 1.0.0
560 ** @@
561 ******************************************************************************/
562 
ajMessOut(const char * format,...)563 void ajMessOut(const char *format,...)
564 {
565     va_list args;
566     char *mesg_buf;
567 
568     AJAXFORMATSTRING(args, format, mesg_buf, NULL);
569 
570     if(outRoutine)
571 	(*outRoutine)(mesg_buf);
572     else
573 	fprintf(stderr, "%s", mesg_buf);
574 
575     ajDebug("ajMessOut: %s\n", mesg_buf);
576 
577     return;
578 }
579 
580 
581 
582 
583 /* @func ajVUser **************************************************************
584 **
585 ** Formats a message. Calls the defined output function (if any).
586 ** Otherwise prints the message to standard error.
587 **
588 ** @param [r] format [const char*] Format string
589 ** @param [v] args [va_list] Variable length argument list
590 ** @return [void]
591 **
592 ** @release 2.9.0
593 ** @@
594 ******************************************************************************/
595 
ajVUser(const char * format,va_list args)596 void ajVUser(const char *format, va_list args)
597 {
598     char *mesg_buf;
599 
600     AJAXVFORMATSTRING(args, format, mesg_buf, NULL);
601 
602     if(outRoutine)
603 	(*outRoutine)(mesg_buf);
604     else
605 	fprintf(stderr, "%s\n", mesg_buf);
606 
607     ajDebug("ajVUser: %s\n", mesg_buf);
608 
609     return;
610 }
611 
612 
613 
614 
615 /* @func ajMessDump ***********************************************************
616 **
617 ** Formats a message. Calls the dump function (if any).
618 ** Otherwise no further action.
619 **
620 ** @param [r] format [const char*] format string.
621 ** @param [v] [...] Variable length argument list.
622 ** @return [void]
623 **
624 ** @release 1.0.0
625 ** @@
626 ******************************************************************************/
627 
ajMessDump(const char * format,...)628 void ajMessDump(const char *format,...)
629 {
630     static char dumpbuf[BUFSIZE];   /* BEWARE limited buffer size. */
631     char *mesg_buf;
632     va_list args;
633 
634     mesg_buf = &dumpbuf[0];
635 
636     AJAXFORMATSTRING(args, format, mesg_buf, NULL);
637 
638     strcat(mesg_buf, "\n"); /* assume we are writing to a file */
639 
640     if(dumpRoutine)
641 	(*dumpRoutine)(mesg_buf);
642 
643     return;
644 }
645 
646 
647 
648 
649 /* @funcstatic messDump *******************************************************
650 **
651 ** Calls the dump function (if any) to dump text followed by a newline.
652 **
653 ** @param [r] message [const char*] Message text
654 ** @return [void]
655 **
656 ** @release 1.0.0
657 ** @@
658 ******************************************************************************/
659 
messDump(const char * message)660 static void messDump(const char *message)
661 {
662     if(dumpRoutine)
663     {
664 	(*dumpRoutine)(message);
665 	(*dumpRoutine)("\n");
666     }
667 
668     return;
669 }
670 
671 
672 
673 
674 /* @func ajMessGetCountError **************************************************
675 **
676 ** Returns the number of times the error routines have been called.
677 **
678 ** @return [ajint] Error function call count.
679 **
680 ** @release 6.2.0
681 ** @@
682 ******************************************************************************/
683 
ajMessGetCountError(void)684 ajint ajMessGetCountError(void)
685 {
686     return errorCount;
687 }
688 
689 
690 
691 
692 /* @func ajErr ****************************************************************
693 **
694 ** Formats an error message. Calls the error function (if any).
695 ** Otherwise prints the message to standard error with a trailing newline.
696 **
697 ** The error message count is incremented by 1 for each call.
698 **
699 ** @param [r] format [const char*] Format
700 ** @param [v] [...] Variable length argument list
701 ** @return [void]
702 **
703 ** @release 2.9.0
704 ** @@
705 ******************************************************************************/
706 
ajErr(const char * format,...)707 void ajErr(const char *format, ...)
708 {
709     const char *prefix   = ERROR_PREFIX;
710     const char *mesg_buf = NULL;
711     va_list args;
712 
713     ++errorCount;
714 
715     if(AjErrorLevel.error)
716     {
717 	AJAXFORMATSTRING(args, format, mesg_buf, prefix);
718 
719 	messDump(mesg_buf);
720 
721 	if(errorRoutine)
722 	    (*errorRoutine)(mesg_buf);
723 	else
724 	    fprintf(stderr, "%s\n", mesg_buf);
725 
726         ajDebug("ajErr: %s\n", mesg_buf);
727 
728 	ajMessInvokeDebugger();
729         ajUtilCatch();
730     }
731 
732     return;
733 }
734 
735 
736 
737 
738 /* @func ajVErr ***************************************************************
739 **
740 ** Formats an error message. Calls the error function (if any).
741 ** Otherwise prints the message to standard error with a trailing newline.
742 **
743 ** The error message count is incremented by 1 for each call.
744 **
745 ** @param [r] format [const char*] Format
746 ** @param [v] args [va_list] Variable length argument list
747 ** @return [void]
748 **
749 ** @release 2.9.0
750 ** @@
751 ******************************************************************************/
752 
ajVErr(const char * format,va_list args)753 void ajVErr(const char *format, va_list args)
754 {
755     const char *prefix   = ERROR_PREFIX;
756     const char *mesg_buf = NULL;
757 
758     ++errorCount;
759 
760     AJAXVFORMATSTRING(args, format, mesg_buf, prefix);
761 
762     messDump(mesg_buf);
763 
764     if(errorRoutine)
765 	(*errorRoutine)(mesg_buf);
766     else
767     {
768 	if(AjErrorLevel.error)
769         {
770 	    fprintf(stderr, "%s\n", mesg_buf);
771             ajDebug("ajVErr: %s\n", mesg_buf);
772         }
773     }
774     ajMessInvokeDebugger();
775 
776     return;
777 }
778 
779 
780 
781 
782 /* @func ajDie ****************************************************************
783 **
784 ** Formats an error message. Calls the error function (if any).
785 ** Otherwise prints the message to standard error with a trailing newline.
786 ** Then kills the application.
787 **
788 ** The error message count is incremented by 1 for each call.
789 **
790 ** @param [r] format [const char*] Format
791 ** @param [v] [...] Variable length argument list
792 ** @return [void]
793 **
794 ** @release 2.9.0
795 ** @@
796 ******************************************************************************/
797 
ajDie(const char * format,...)798 __noreturn void  ajDie(const char *format, ...)
799 {
800     const char *prefix   = DIE_PREFIX;
801     const char *mesg_buf = NULL;
802     va_list args;
803 
804     ++errorCount;
805 
806     if(AjErrorLevel.die)
807     {
808 	AJAXFORMATSTRING(args, format, mesg_buf, prefix);
809 
810 	messDump(mesg_buf);
811 
812 	if(errorRoutine)
813 	    (*errorRoutine)(mesg_buf);
814 	else
815 	    fprintf(stderr, "%s\n", mesg_buf);
816 
817         ajDebug("ajDie: %s\n", mesg_buf);
818 
819         ajMessInvokeDebugger();
820     }
821 
822 
823     exit(EXIT_FAILURE);
824 }
825 
826 
827 
828 
829 /* @func ajVDie ***************************************************************
830 **
831 ** Formats an error message. Calls the error function (if any).
832 ** Otherwise prints the message to standard error with a trailing newline.
833 ** Then kills the application.
834 **
835 ** The error message count is incremented by 1 for each call.
836 **
837 ** @param [r] format [const char*] Format
838 ** @param [v] args [va_list] Variable length argument list
839 ** @return [void]
840 **
841 ** @release 2.9.0
842 ** @@
843 ******************************************************************************/
844 
ajVDie(const char * format,va_list args)845 void ajVDie(const char *format, va_list args)
846 {
847     const char *prefix   = DIE_PREFIX;
848     const char *mesg_buf = NULL;
849 
850     ++errorCount;
851 
852     AJAXVFORMATSTRING(args, format, mesg_buf, prefix);
853 
854     messDump(mesg_buf);
855 
856     if(errorRoutine)
857 	(*errorRoutine)(mesg_buf);
858     else
859 	ajMessCrash(mesg_buf);
860 
861     ajMessInvokeDebugger();
862 
863     return;
864 }
865 
866 
867 
868 
869 /* @func ajWarn ***************************************************************
870 **
871 ** Formats a warning message. Calls the warning function (if any).
872 ** Otherwise prints the message to standard error with a trailing newline.
873 **
874 ** @param [r] format [const char*] Format
875 ** @param [v] [...] Variable length argument list
876 ** @return [void]
877 **
878 ** @release 2.9.0
879 ** @@
880 ******************************************************************************/
881 
ajWarn(const char * format,...)882 void ajWarn(const char *format, ...)
883 {
884     const char *prefix   = WARNING_PREFIX;
885     const char *mesg_buf = NULL;
886     va_list args;
887 
888     if(AjErrorLevel.warning)
889     {
890 	AJAXFORMATSTRING(args, format, mesg_buf, prefix);
891 
892 	messDump(mesg_buf);
893 
894 	if(warningRoutine)
895 	    (*warningRoutine)(mesg_buf);
896 	else
897 	    fprintf(stderr, "%s\n", mesg_buf);
898 
899         ajDebug("ajWarn: %s\n", mesg_buf);
900 
901 	ajMessInvokeDebugger();
902         ajUtilCatch();
903     }
904 
905     return;
906 }
907 
908 
909 
910 
911 /* @func ajVWarn **************************************************************
912 **
913 ** Formats a warning message. Calls the warning function (if any).
914 ** Otherwise prints the message to standard error with a trailing newline.
915 **
916 ** @param [r] format [const char*] Format
917 ** @param [v] args [va_list] Variable length argument list
918 ** @return [void]
919 **
920 ** @release 2.9.0
921 ** @@
922 ******************************************************************************/
923 
ajVWarn(const char * format,va_list args)924 void ajVWarn(const char *format, va_list args)
925 {
926     const char *prefix   = WARNING_PREFIX;
927     const char *mesg_buf = NULL;
928 
929     AJAXVFORMATSTRING(args, format, mesg_buf, prefix);
930 
931     messDump(mesg_buf);
932 
933     if(warningRoutine)
934 	(*warningRoutine)(mesg_buf);
935     else
936 	fprintf(stderr, "%s\n", mesg_buf);
937 
938     ajDebug("ajVWarn: %s\n", mesg_buf);
939 
940     ajMessInvokeDebugger();
941 
942     return;
943 }
944 
945 
946 
947 
948 /* @func ajMessExitmsg ********************************************************
949 **
950 ** Formats an exit message and calls the exit function (if any).
951 ** Otherwise prints the message to standard error with a trailing newline
952 ** and exist with code EXIT_FAILURE.
953 **
954 ** Use this function for errors that while being unrecoverable are not a
955 ** problem with the AJAX code.
956 **
957 ** Note that there errors are logged but that this routine will exit without
958 ** any chance to interrupt it (see the crash routine in ajMessCrashFL), this
959 ** could be changed to allow the application to register an exit handler.
960 **
961 ** @param [r] format [const char*] Format
962 ** @param [v] [...] Variable length argument list
963 ** @return [void]
964 **
965 ** @release 2.7.0
966 ** @@
967 ******************************************************************************/
968 
ajMessExitmsg(const char * format,...)969 __noreturn void ajMessExitmsg(const char *format, ...)
970 {
971     const char *prefix   = EXIT_PREFIX;
972     const char *mesg_buf = NULL;
973     va_list args;
974 
975     AJAXFORMATSTRING(args, format, mesg_buf, prefix);
976 
977     messDump(mesg_buf);
978 
979     if(exitRoutine)
980 	(*exitRoutine)(mesg_buf);
981     else
982 	fprintf(stderr, "%s\n", mesg_buf);
983 
984     ajDebug("ajMessExitmsg: %s\n", mesg_buf);
985 
986     exit(EXIT_FAILURE);
987 }
988 
989 
990 
991 
992 /* @func ajMessCrashFL ********************************************************
993 **
994 ** This is the routine called by the ajFatal macro and others.
995 **
996 ** This routine may encounter errors itself, in which case it will attempt
997 ** to call itself to report the error. To avoid infinite recursion we limit
998 ** this to just one reporting of an internal error and then we abort.
999 **
1000 ** @param [r] format [const char*] Format
1001 ** @param [v] [...] Variable length argument list
1002 ** @return [void]
1003 **
1004 ** @release 1.0.0
1005 ** @@
1006 ******************************************************************************/
1007 
ajMessCrashFL(const char * format,...)1008 __noreturn void ajMessCrashFL(const char *format, ...)
1009 {
1010     enum {MAXERRORS = 1};
1011     static ajint internalErrors = 0;
1012     static char prefix[1024];
1013     ajint rc;
1014     const char *mesg_buf = NULL;
1015     va_list args;
1016 
1017 
1018     if(internalErrors > MAXERRORS)
1019 	abort();
1020     else
1021 	internalErrors++;
1022 
1023     /* Construct the message prefix, adding the program name if possible. */
1024 
1025     if(messGetErrorProgram() == NULL)
1026 	rc = sprintf(prefix, CRASH_PREFIX_FORMAT, MESG_TITLE,
1027 		     messGetErrorFile(), messGetErrorLine());
1028     else
1029 	rc = sprintf(prefix, FULL_CRASH_PREFIX_FORMAT, MESG_TITLE,
1030 		     messGetErrorProgram(), messGetErrorFile(),
1031 		     messGetErrorLine());
1032     if(rc < 0)
1033 	ajMessCrash("sprintf failed");
1034 
1035     if(AjErrorLevel.fatal)
1036     {
1037 	AJAXFORMATSTRING(args, format, mesg_buf, prefix);
1038 
1039 	messDump(mesg_buf);
1040 
1041 	if(crashRoutine)
1042 	    (*crashRoutine)(mesg_buf);
1043 	else
1044 	    fprintf(stderr, "%s\n", mesg_buf);
1045 
1046         ajDebug("ajMessCrashFL: %s\n", mesg_buf);
1047 
1048 	ajMessInvokeDebugger();
1049     }
1050 
1051 
1052     exit(EXIT_FAILURE);
1053 }
1054 
1055 
1056 
1057 
1058 /* @func ajMessVCrashFL *******************************************************
1059 **
1060 ** This is the routine called by the ajVFatal macro and others.
1061 **
1062 ** This routine may encounter errors itself, in which case it will attempt
1063 ** to call itself to report the error. To avoid infinite recursion we limit
1064 ** this to just one reporting of an internal error and then we abort.
1065 **
1066 ** @param [r] format [const char*] Format
1067 ** @param [v] args [va_list] Variable length argument list
1068 ** @return [void]
1069 **
1070 ** @release 1.0.0
1071 ** @@
1072 ******************************************************************************/
1073 
ajMessVCrashFL(const char * format,va_list args)1074 __noreturn void ajMessVCrashFL(const char *format, va_list args)
1075 {
1076     enum {MAXERRORS = 1};
1077     static ajint internalErrors = 0;
1078     static char prefix[1024];
1079     ajint rc;
1080     char *mesg_buf = NULL;
1081 
1082     if(internalErrors > MAXERRORS)
1083 	abort();
1084     else
1085 	internalErrors++;
1086 
1087     /* Construct the message prefix, adding the program name if possible. */
1088 
1089     if(messGetErrorProgram() == NULL)
1090 	rc = sprintf(prefix, CRASH_PREFIX_FORMAT, MESG_TITLE,
1091 		     messGetErrorFile(), messGetErrorLine());
1092     else
1093 	rc = sprintf(prefix, FULL_CRASH_PREFIX_FORMAT, MESG_TITLE,
1094 		     messGetErrorProgram(), messGetErrorFile(),
1095 		     messGetErrorLine());
1096     if(rc < 0)
1097 	ajMessCrash("sprintf failed");
1098 
1099 
1100     AJAXVFORMATSTRING(args, format, mesg_buf, prefix);
1101 
1102     messDump(mesg_buf);
1103 
1104     if(crashRoutine)
1105 	(*crashRoutine)(mesg_buf);
1106     else
1107 	fprintf(stderr, "%s\n", mesg_buf);
1108 
1109     ajDebug("ajMessVCrashFL: %s\n", mesg_buf);
1110 
1111     ajMessInvokeDebugger();
1112 
1113     exit(EXIT_FAILURE);
1114 }
1115 
1116 
1117 
1118 
1119 /* @func ajMessGetMessageC ****************************************************
1120 **
1121 ** Returns the current message text.
1122 **
1123 ** @return [const char*] Message text
1124 **
1125 ** @release 6.2.0
1126 ** @@
1127 ******************************************************************************/
1128 
ajMessGetMessageC(void)1129 const char* ajMessGetMessageC(void)
1130 {
1131     return messbuf;
1132 }
1133 
1134 
1135 
1136 
1137 /* @func ajMessGetSysmessageC *************************************************
1138 **
1139 ** Returns the system message text from 'strerror' from the standard C
1140 ** library.
1141 **
1142 ** @return [const char*] System error message.
1143 **
1144 ** @release 6.2.0
1145 ** @@
1146 ******************************************************************************/
1147 
ajMessGetSysmessageC(void)1148 const char* ajMessGetSysmessageC(void)
1149 {
1150     char *mess;
1151 
1152     if(errno)
1153 	mess = ajFmtString(SYSERR_FORMAT, errno, strerror(errno));
1154     else
1155 	mess = ajFmtString(SYSERR_OK, errno, strerror(errno));
1156 
1157     /* must make copy - will be used when mess* calls itself */
1158     AJFREE(messErrMess);
1159     messErrMess = ajSysFuncStrdup(mess);
1160 
1161     AJFREE(mess);
1162 
1163     return messErrMess;
1164 }
1165 
1166 
1167 
1168 
1169 /************************* message formatting ********************************/
1170 /* This routine does the formatting of the message string using vsprintf,    */
1171 /* it copes with the format string accidentally being our internal buffer.   */
1172 /*                                                                           */
1173 /* This routine does its best to check that the vsprintf is successful, if   */
1174 /* not the routine bombs out with an error message. Note that num_bytes is   */
1175 /* the return value from vsprintf.                                           */
1176 /* Failures trapped:                                                         */
1177 /*      num_bytes less than 0  =  vsprintf failed, reason is reported.      */
1178 /* num_bytes + 1 more than BUFSIZE  =  our internal buffer size was         */
1179 /*                                      exceeded.                            */
1180 /*                                 (vsprintf returns number of bytes written */
1181 /*                                  _minus_ terminating NULL)                */
1182 /*                                                                           */
1183 
1184 
1185 
1186 
1187 /* @funcstatic messFormat *****************************************************
1188 **
1189 ** Used by the AJAXFORMAT macros to format messages.
1190 **
1191 ** This routine does the formatting of the message string using vsprintf,
1192 ** it copes with the format string accidentally being our internal buffer.
1193 **
1194 ** This routine does its best to check that the vsprintf is successful, if
1195 ** not the routine bombs out with an error message. Note that num_bytes is
1196 ** the return value from vsprintf.
1197 **
1198 ** Failures trapped:
1199 **  num_bytes less than 0  =  vsprintf failed, reason is reported.
1200 **  num_bytes + 1 more than BUFSIZE  =  our internal buffer size was exceeded.
1201 **                                 (vsprintf returns number of bytes written
1202 **                                  _minus_ terminating NULL)
1203 **
1204 ** @param [v] args [va_list] Variable length argument list
1205 ** @param [r] format [const char*] Format string
1206 ** @param [r] prefix [const char*] Message prefix
1207 ** @return [char*] Formatted message text
1208 **
1209 ** @release 1.0.0
1210 ** @@
1211 ******************************************************************************/
1212 
messFormat(va_list args,const char * format,const char * prefix)1213 static char* messFormat(va_list args, const char *format, const char *prefix)
1214 {
1215     static char *new_buf = NULL;
1216     char *buf_ptr;
1217     ajint num_bytes;
1218     ajint prefix_len;
1219 
1220 
1221     if(format == NULL)
1222 	ajMessCrash("invalid call, no format string.");
1223 
1224     if(prefix == NULL)
1225 	prefix_len = 0;
1226     else
1227     {
1228 	prefix_len = strlen(prefix);
1229 	if((prefix_len + 1) > PREFIXSIZE)
1230 	    ajMessCrash("prefix string is too long.");
1231     }
1232 
1233 
1234     /* If they supply the internal buffer as an argument, e.g. because they */
1235     /* used ajFmtString as an arg, then make a copy, otherwise use internal */
1236     /* buffer.                                                              */
1237 
1238     if(format == messbuf)
1239     {
1240 	if(new_buf != NULL)
1241 	    AJFREE(new_buf);
1242 	buf_ptr = new_buf = ajSysFuncStrdup(format);
1243     }
1244     else
1245 	buf_ptr = messbuf;
1246 
1247     /* Add the prefix if there is one. */
1248     if(prefix != NULL)
1249     {
1250 	if(!strcpy(buf_ptr, prefix))
1251 	    ajMessCrash("strcpy failed");
1252     }
1253 
1254 
1255     num_bytes = prefix_len + 1;
1256     num_bytes += ajFmtVPrintCL((buf_ptr + prefix_len),BUFSIZE, format, args);
1257 
1258     /*
1259     **  Check the result. This should never happen using the
1260     **  ajFmtVPrintCL routine instead of the vsprintf routine
1261     */
1262 
1263     if(num_bytes < 0)
1264 	ajMessCrash("vsprintf failed: %s", ajMessGetSysmessageC());
1265     else if(num_bytes > BUFSIZE)
1266 	ajMessCrash("messubs internal buffer size (%d) exceeded, "
1267 		    "a total of %d bytes were written",
1268 		    BUFSIZE, num_bytes);
1269 
1270     return(buf_ptr);
1271 }
1272 
1273 
1274 
1275 
1276 /* @funcstatic messGetFilename ************************************************
1277 **
1278 ** Converts a filename into a base file name. Used for filenames passed
1279 ** by macros from __FILE__ which could include part or all of the path
1280 ** depending on how the source code was compiled.
1281 **
1282 ** @param [r] path [const char*] File name, possibly with full path.
1283 ** @return [char*] Base file name
1284 **
1285 ** @release 1.0.0
1286 ** @@
1287 ******************************************************************************/
1288 
messGetFilename(const char * path)1289 static char* messGetFilename(const char *path)
1290 {
1291     static char *path_copy = NULL;
1292     const char *path_delim = SUBDIR_DELIMITER_STR;
1293     char *result = NULL;
1294     char *tmp;
1295 
1296     if(path != NULL)
1297     {
1298 	if(strcmp((path + strlen(path) - 1), path_delim) != 0)
1299 	{				/* Last char = "/" ?? */
1300 	    if(path_copy != NULL)
1301 		AJFREE(path_copy);
1302 
1303 	    path_copy = ajSysFuncStrdup(path);
1304 
1305 	    tmp = ajSysFuncStrtok(path_copy, path_delim);
1306 
1307 	    while(tmp != NULL)
1308 	    {
1309 		result = tmp;	 /* Keep results of previous strtok */
1310 
1311 		tmp = ajSysFuncStrtok(NULL, path_delim);
1312 	    }
1313 	}
1314     }
1315 
1316     return(result);
1317 }
1318 
1319 
1320 
1321 
1322 /*
1323 ** When AJAX needs to crash because there has been an unrecoverable
1324 ** error the file and line number of the code that detected the error
1325 ** need to be output. Here are the functions to do it.
1326 **
1327 ** Applications can optionally initialise the error handling section of the
1328 ** message package, currently the program name can be set (argv[0] in the
1329 ** main routine) as there is no easy way to get at this at run time except
1330 ** from the main.
1331 ** */
1332 
1333 
1334 
1335 
1336 /* @func ajMessErrorInit ******************************************************
1337 **
1338 ** Initialises the stored program name.
1339 **
1340 ** @param [r] progname [const char*] Program name.
1341 ** @return [void]
1342 **
1343 ** @release 1.0.0
1344 ** @@
1345 ******************************************************************************/
1346 
ajMessErrorInit(const char * progname)1347 void ajMessErrorInit(const char *progname)
1348 {
1349     if(progname != NULL)
1350 	messageG.progname = ajSysFuncStrdup(messGetFilename(progname));
1351 
1352     return;
1353 }
1354 
1355 
1356 
1357 
1358 /* @func ajMessSetErr *********************************************************
1359 **
1360 ** Stores the source file name (converted to a base name)
1361 ** and the source line number to be
1362 ** reported by the crash routines.
1363 **
1364 ** Invoked automatically by a macro (e.g. ajFatal) where needed.
1365 **
1366 ** @param [r] filename [const char*] source filename, __FILE__
1367 ** @param [r] line_num [ajint] source line number, __LINE__
1368 ** @return [void]
1369 **
1370 ** @release 1.0.0
1371 ** @@
1372 ******************************************************************************/
1373 
ajMessSetErr(const char * filename,ajint line_num)1374 void ajMessSetErr(const char *filename, ajint line_num)
1375 {
1376     assert(filename != NULL && line_num != 0);
1377 
1378     /*
1379     ** We take the basename here because __FILE__ can be a path rather
1380     ** than just a filename, depending on how a module was compiled.
1381     */
1382 
1383     messageG.filename = ajSysFuncStrdup(messGetFilename(filename));
1384 
1385     messageG.line_num = line_num;
1386 
1387     ajUtilCatch();
1388 
1389     return;
1390 }
1391 
1392 
1393 
1394 
1395 /* Access functions for message error data.                                  */
1396 
1397 
1398 
1399 
1400 /* @funcstatic messGetErrorProgram ********************************************
1401 **
1402 ** Returns the stored program name.
1403 **
1404 ** @return [char*] Program name
1405 **
1406 ** @release 1.0.0
1407 ** @@
1408 ******************************************************************************/
1409 
messGetErrorProgram(void)1410 static char* messGetErrorProgram(void)
1411 {
1412     return(messageG.progname);
1413 }
1414 
1415 
1416 
1417 
1418 /* @funcstatic messGetErrorFile ***********************************************
1419 **
1420 ** Returns the stored error file name.
1421 **
1422 ** @return [char*] Error file name
1423 **
1424 ** @release 1.0.0
1425 ** @@
1426 ******************************************************************************/
1427 
messGetErrorFile(void)1428 static char* messGetErrorFile(void)
1429 {
1430     return(messageG.filename);
1431 }
1432 
1433 
1434 
1435 
1436 /* @funcstatic messGetErrorLine ***********************************************
1437 **
1438 ** Returns the stored error source line number.
1439 **
1440 ** @return [ajint] Original source code line number
1441 **
1442 ** @release 1.0.0
1443 ** @@
1444 ******************************************************************************/
1445 
messGetErrorLine(void)1446 static ajint messGetErrorLine(void)
1447 {
1448     return(messageG.line_num);
1449 }
1450 
1451 
1452 
1453 
1454 /* set a file to read for all the messages. NB if this is not set
1455 Then a default one will be read */
1456 
1457 
1458 
1459 
1460 /* @func ajMessErrorSetFile ***************************************************
1461 **
1462 ** Opens a file and sets this to be the error file.
1463 **
1464 ** @param [r] errfile [const char*] Error file name
1465 ** @return [AjBool] ajTrue on success
1466 **
1467 ** @release 1.0.0
1468 ** @@
1469 ******************************************************************************/
1470 
ajMessErrorSetFile(const char * errfile)1471 AjBool ajMessErrorSetFile(const char *errfile)
1472 {
1473     FILE *fp = 0;
1474 
1475     if(errfile)
1476 	if((fp = fopen(errfile,"r")))
1477 	{
1478 	    messErrorFile = ajSysFuncStrdup(errfile);
1479 	    fclose(fp);
1480 
1481 	    return ajTrue;
1482 	}
1483 
1484     return ajFalse;
1485 }
1486 
1487 
1488 
1489 
1490 /* @funcstatic ajMessReadErrorFile ********************************************
1491 **
1492 ** Reads the error message file (with a default of
1493 ** $EMBOSS_ROOT/messages/messages.english)
1494 ** and loads the results into an internal table.
1495 **
1496 ** @return [AjBool] ajTrue on success
1497 **
1498 ** @release 1.0.0
1499 ** @@
1500 ******************************************************************************/
1501 
ajMessReadErrorFile(void)1502 static AjBool ajMessReadErrorFile(void)
1503 {
1504     char line[512];
1505     char name[12];
1506     char message[200];
1507     FILE *fp=0;
1508     char *mess;
1509     char *cp;
1510     char *namestore;
1511     char *messstore;
1512 
1513     if(messErrorFile)
1514 	fp = fopen(messErrorFile,"r");
1515 
1516     if(!fp)
1517     {
1518 	messErrorFile = ajFmtString("%s/messages/messages.english",
1519 				    getenv("EMBOSS_ROOT"));
1520 	fp = fopen(messErrorFile,"r");
1521     }
1522 
1523 
1524     if(!fp)
1525 	return ajFalse;
1526 
1527     messErrorTable = ajTablecharNew(100);
1528 
1529     while(fgets(line, 512, fp))
1530     {
1531 	if(sscanf(line,"%s %s",name,message)!=2)
1532 	    ajFatal("Library sscanf1");
1533 
1534 	cp = strchr(line,'"');
1535 	cp++;
1536 	mess = &message[0];
1537 
1538 	while(*cp != '"')
1539 	{
1540 	    *mess = *cp;
1541 	    cp++;
1542 	    mess++;
1543 	}
1544 
1545 	*mess = '\0';
1546 	namestore = ajCharNewC(name);
1547 	messstore = ajCharNewC(message);
1548 
1549         if(ajTablePut(messErrorTable, namestore, messstore))
1550 	    ajErr("%s is listed more than once in file %s",
1551                   name,messErrorFile);
1552     }
1553 
1554     return ajTrue;
1555 }
1556 
1557 
1558 
1559 
1560 /* @func ajMessOutCode ********************************************************
1561 **
1562 ** Writes an output message for a given message code.
1563 **
1564 ** @param [r] code [const char*] Message code
1565 ** @return [void]
1566 **
1567 ** @release 1.0.0
1568 ** @@
1569 ******************************************************************************/
1570 
ajMessOutCode(const char * code)1571 void ajMessOutCode(const char *code)
1572 {
1573     const char *mess=0;
1574 
1575     if(messErrorTable)
1576     {
1577 	mess = ajTableFetchC(messErrorTable, code);
1578 
1579 	if(mess)
1580 	    ajMessOut(mess);
1581 	else
1582 	    ajMessOut("could not find error code %s",code);
1583     }
1584     else
1585     {
1586 	if(ajMessReadErrorFile())
1587 	{
1588 	    mess = ajTableFetchC(messErrorTable, code);
1589 
1590 	    if(mess)
1591 		ajMessOut(mess);
1592 	    else
1593 		ajMessOut("could not find error code %s",code);
1594 	}
1595 	else
1596 	    ajMessOut("Could not read the error file hence no reference to %s",
1597 		      code);
1598     }
1599 
1600     return;
1601 }
1602 
1603 
1604 
1605 
1606 /* @func ajMessErrorCode ******************************************************
1607 **
1608 ** Writes an error message for a given message code.
1609 **
1610 ** @param [r] code [const char*] Error code
1611 ** @return [void]
1612 **
1613 ** @release 1.0.0
1614 ** @@
1615 ******************************************************************************/
1616 
ajMessErrorCode(const char * code)1617 void ajMessErrorCode(const char *code)
1618 {
1619     const char *mess = 0;
1620 
1621     if(messErrorTable)
1622     {
1623 	mess = ajTableFetchC(messErrorTable, code);
1624 
1625 	if(mess)
1626 	    ajErr(mess);
1627 	else
1628 	    ajErr("could not find error code %s",code);
1629     }
1630     else
1631     {
1632 	if(ajMessReadErrorFile())
1633 	{
1634 	    mess = ajTableFetchC(messErrorTable, code);
1635 
1636 	    if(mess)
1637 		ajErr(mess);
1638 	    else
1639 		ajErr("could not find error code %s",code);
1640 	}
1641 	else
1642 	    ajErr("Could not read the error file, "
1643 		  "hence no reference to %s",
1644 		  code);
1645     }
1646 
1647     return;
1648 }
1649 
1650 
1651 
1652 
1653 /* @func ajMessCrashCodeFL ****************************************************
1654 **
1655 ** Writes an error message for a given message code and crashes.
1656 **
1657 ** @param [r] code [const char*] Error code
1658 ** @return [void]
1659 **
1660 ** @release 1.0.0
1661 ** @@
1662 ******************************************************************************/
1663 
ajMessCrashCodeFL(const char * code)1664 __noreturn void  ajMessCrashCodeFL(const char *code)
1665 {
1666     const char *mess = 0;
1667 
1668     if(messErrorTable)
1669     {
1670 	mess = ajTableFetchC(messErrorTable, code);
1671 
1672 	if(mess)
1673 	    ajMessCrashFL(mess);
1674 	else
1675 	    ajMessCrashFL("could not find error code %s",code);
1676     }
1677     else
1678     {
1679 	if(ajMessReadErrorFile())
1680 	{
1681 	    mess = ajTableFetchC(messErrorTable, code);
1682 
1683 	    if(mess)
1684 		ajMessCrashFL(mess);
1685 	    else
1686 		ajMessCrashFL("could not find error code %s",code);
1687 	}
1688 	else
1689 	    ajMessCrashFL("Could not read the error file "
1690 			  "hence no reference to %s",
1691 			  code);
1692     }
1693 }
1694 
1695 
1696 
1697 
1698 /* @func ajMessCodesDelete ****************************************************
1699 **
1700 ** Deletes the message codes table.
1701 **
1702 ** @return [void]
1703 **
1704 ** @release 1.0.0
1705 ** @@
1706 ******************************************************************************/
1707 
ajMessCodesDelete(void)1708 void ajMessCodesDelete(void)
1709 {
1710     messTableDelete(&messErrorTable);
1711 }
1712 
1713 
1714 
1715 
1716 /* @funcstatic messTableDelete ************************************************
1717 **
1718 ** Delete a table, simply freeing the key and value
1719 **
1720 ** @param [d] table [AjPTable*] Table
1721 ** @return [void]
1722 **
1723 ** @release 6.2.0
1724 ******************************************************************************/
1725 
messTableDelete(AjPTable * table)1726 static void messTableDelete(AjPTable* table)
1727 {
1728     void **keyarray = NULL;
1729     void **valarray = NULL;
1730     ajint i;
1731 
1732     if(!table)
1733 	return;
1734     if(!*table)
1735 	return;
1736 
1737     ajTableToarrayKeysValues(*table, &keyarray, &valarray);
1738 
1739     for(i = 0; keyarray[i]; i++)
1740     {
1741 	AJFREE(keyarray[i]);
1742 	AJFREE(valarray[i]);
1743     }
1744 
1745     AJFREE(keyarray);
1746     AJFREE(valarray);
1747 
1748     ajTableFree(table);
1749     *table = NULL;
1750 
1751     return;
1752 }
1753 
1754 
1755 
1756 
1757 /* @func ajDebug **************************************************************
1758 **
1759 ** Writes a debug message to the debug file if debugging is on.
1760 ** Typically, debugging is turned on by adding '-debug' to the command line
1761 ** or by defining a variable prefix_DEBUG
1762 **
1763 ** Avoid using this call in any code which can be invoked before the command
1764 ** line processing is complete as it can be a problem to find a reasonable
1765 ** file name for debug output under these circumstances.
1766 **
1767 ** @param [r] fmt [const char*] Format.
1768 ** @param [v] [...] Variable argument list.
1769 ** @return [void]
1770 **
1771 ** @release 1.0.0
1772 ** @@
1773 ******************************************************************************/
1774 
ajDebug(const char * fmt,...)1775 void ajDebug(const char* fmt, ...)
1776 {
1777     va_list args;
1778     static ajint debugset = 0;
1779     static ajint depth    = 0;
1780     AjPStr bufstr         = NULL;
1781 
1782     if(depth)
1783     {				   /* recursive call, get out quick */
1784 	if(messDebugFile)
1785 	{
1786 	    va_start(args, fmt);
1787 	    ajFmtVPrintF(messDebugFile, fmt, args);
1788 	    va_end(args);
1789 	}
1790 
1791 	return;
1792     }
1793 
1794     depth++;
1795 
1796     if(!debugset && acdDebugSet)
1797     {
1798 	if(acdDebug)
1799 	{
1800 	    ajFmtPrintS(&messDebugName, "%s.dbg", ajStrGetPtr(acdProgram));
1801 	    messDebugFile = ajFileNewOutNameS(messDebugName);
1802 
1803 	    if(!messDebugFile)
1804 		ajFatal("Cannot open debug file %S",messDebugName);
1805 
1806 	    if(ajNamGetValueC("debugbuffer", &bufstr))
1807 	    {
1808 		ajStrToBool(bufstr, &acdDebugBuffer);
1809 	    }
1810 
1811 	    if(!acdDebugBuffer)
1812 		ajFileSetUnbuffer(messDebugFile);
1813 
1814 	    ajFmtPrintF(messDebugFile, "Debug file %F buffered:%B\n",
1815 			 messDebugFile, acdDebugBuffer);
1816 	    ajStrDel(&bufstr);
1817 	}
1818 
1819 	messDebug = acdDebug;
1820 
1821 	debugset = 1;
1822     }
1823 
1824     if(messDebug)
1825     {
1826 
1827 	va_start(args, fmt);
1828 	ajFmtVPrintF(messDebugFile, fmt, args);
1829 	va_end(args);
1830 
1831     }
1832 
1833     depth--;
1834 
1835     return;
1836 }
1837 
1838 
1839 
1840 
1841 /* @func ajDebugOn ************************************************************
1842 **
1843 ** Test whether debugging is on.
1844 **
1845 ** @return [AjBool] True if user has enabled debugging
1846 **
1847 ** @release 6.4.0
1848 ** @@
1849 ******************************************************************************/
1850 
ajDebugOn(void)1851 AjBool ajDebugOn(void)
1852 {
1853     return acdDebug;
1854 }
1855 
1856 
1857 
1858 
1859 /* @func ajDebugTest **********************************************************
1860 **
1861 ** Tests a token string and returns true if the user has requested debug output
1862 **
1863 ** @param [r] token [const char*] Token name
1864 ** @return [AjBool] True if token has debugging requested
1865 **
1866 ** @release 6.2.0
1867 ** @@
1868 ******************************************************************************/
1869 
ajDebugTest(const char * token)1870 AjBool ajDebugTest(const char* token)
1871 {
1872     AjPStr filename = NULL;
1873     const char* debugtestname = ".debugtest";
1874     char* ctoken = NULL;
1875     AjPStr line = NULL;
1876     AjPStr strtoken = NULL;
1877     AjPStr rest = NULL;
1878     static ajint depth    = 0;
1879 
1880     struct
1881     {
1882         ajuint count;
1883         ajuint max;
1884     } *stats;
1885 
1886     if(depth)
1887         return ajFalse;
1888 
1889     depth++;
1890 
1891     if(!messDebugTestInit)
1892     {
1893         filename = ajStrNewC(debugtestname);
1894 
1895         if(ajFilenameExists(filename))
1896         {
1897             messDebugTestFile = ajFileNewInNameS(filename);
1898         }
1899         else
1900         {
1901             ajFmtPrintS(&filename, "%s%s%s",
1902                         getenv("HOME"), SLASH_STRING, debugtestname);
1903             if(ajFilenameExists(filename))
1904                 messDebugTestFile = ajFileNewInNameS(filename);
1905         }
1906         ajStrDel(&filename);
1907 
1908         if(messDebugTestFile)
1909         {
1910             messDebugTestTable = ajTablecharNew(256);
1911 
1912             while(ajReadlineTrim(messDebugTestFile, &line))
1913             {
1914                 if(!ajStrCutComments(&line))
1915                     continue;
1916                 if(ajStrExtractFirst(line, &rest, &strtoken))
1917                 {
1918                     AJNEW0(stats);
1919                     ctoken = ajCharNewS(strtoken);
1920                     if(ajStrIsInt(rest))
1921                         ajStrToUint(rest, &stats->max);
1922                     else
1923                         stats->max = UINT_MAX;
1924 
1925                     ajTablePut(messDebugTestTable, ctoken, stats);
1926                     ctoken = NULL;
1927                     stats = NULL;
1928                 }
1929             }
1930 
1931             ajStrDel(&line);
1932             ajStrDel(&strtoken);
1933             ajStrDel(&rest);
1934             ajFileClose(&messDebugTestFile);
1935         }
1936         messDebugTestInit = ajTrue;
1937      }
1938 
1939     depth--;
1940 
1941     if(!messDebugTestTable)
1942         return ajFalse;
1943 
1944     depth++;
1945     stats = ajTableFetchmodC(messDebugTestTable, token);
1946     depth--;
1947 
1948 
1949     if(!stats)
1950         return ajFalse;
1951 
1952     if(!stats->max)
1953         return ajTrue;
1954 
1955     if(stats->count++ >= stats->max)
1956         return ajFalse;
1957 
1958     return ajTrue;
1959 }
1960 
1961 
1962 
1963 
1964 /* @func ajMessGetDebugfile ***************************************************
1965 **
1966 ** Returns the file used for debug output, or NULL if no debug file is open.
1967 **
1968 ** @return [FILE*] C runtime library file handle for debug output.
1969 **
1970 ** @release 6.2.0
1971 ** @@
1972 ******************************************************************************/
1973 
ajMessGetDebugfile(void)1974 FILE* ajMessGetDebugfile(void)
1975 {
1976     if(!messDebugFile)
1977 	return NULL;
1978 
1979     return ajFileGetFileptr(messDebugFile);
1980 }
1981 
1982 
1983 
1984 
1985 /* @func ajUserGet ************************************************************
1986 **
1987 ** Writes a prompt to the terminal and reads one line from the user.
1988 **
1989 ** @param [w] pthis [AjPStr*] Buffer for the user response.
1990 ** @param [r] fmt [const char*] Format string
1991 ** @param [v] [...] Variable argument list.
1992 ** @return [ajint] Length of response string.
1993 **
1994 ** @release 1.0.0
1995 ** @@
1996 ******************************************************************************/
1997 
ajUserGet(AjPStr * pthis,const char * fmt,...)1998 ajint ajUserGet(AjPStr* pthis, const char* fmt, ...)
1999 {
2000     AjPStr thys;
2001     const char *cp;
2002     char *buff;
2003     va_list args;
2004     ajint ipos;
2005     ajint isize;
2006     ajint ilen;
2007     ajint jlen;
2008     ajint fileBuffSize = ajFileValueBuffsize();
2009 
2010     va_start(args, fmt);
2011     ajFmtVError(fmt, args);
2012     va_end(args);
2013 
2014     if(ajFileValueRedirectStdin())
2015     {
2016 	ajUser("(Standard input in use: using default)");
2017 	ajStrAssignC(pthis, "");
2018 
2019 	return ajStrGetLen(*pthis);
2020     }
2021 
2022     ajStrSetRes(pthis, fileBuffSize);
2023     buff  = ajStrGetuniquePtr(pthis);
2024     thys = *pthis;
2025     isize = ajStrGetRes(thys);
2026     ilen  = 0;
2027     ipos  = 0;
2028 
2029 
2030     /*ajDebug("ajUserGet buffer len: %d res: %d ptr: %x\n",
2031 	     ajStrGetLen(thys), ajStrGetRes(thys), thys->Ptr);*/
2032 
2033     while(buff)
2034     {
2035 
2036 #ifndef __ppc__
2037 	cp = fgets(&buff[ipos], isize, stdin);
2038 #else
2039 	cp = ajSysFuncFgets(&buff[ipos], isize, stdin);
2040 #endif
2041 
2042         if(!cp && !ipos)
2043 	{
2044 	    if(feof(stdin))
2045 	    {
2046 		ajErr("Unable to get reply from user - end of standard input");
2047 		ajExitBad();
2048 	    }
2049 	    else
2050 		ajFatal("Error reading from user: '%s'\n",
2051 			strerror(errno));
2052 	}
2053 
2054 	jlen = strlen(&buff[ipos]);
2055 	ilen += jlen;
2056 
2057 	/*
2058 	 ** We need to read again if:
2059 	 ** We have read the entire buffer
2060 	 ** and we don't have a newline at the end
2061 	 ** (must be careful about that - we may just have read enough)
2062 	 */
2063 	ajStrSetValidLen(pthis, ilen);
2064 	thys = *pthis;
2065 
2066 	if((jlen == (isize-1)) &&
2067 	   (ajStrGetCharLast(thys) != '\n'))
2068 	{
2069 	    ajStrSetRes(pthis, ajStrGetRes(thys)+fileBuffSize);
2070 	    thys = *pthis;
2071 	    /*ajDebug("more to do: jlen: %d ipos: %d isize: %d ilen: %d "
2072 		    "Size: %d\n",
2073 		    jlen, ipos, isize, ilen, ajStrGetRes(thys));*/
2074 	    ipos += jlen;
2075 	    buff = ajStrGetuniquePtr(pthis);
2076 	    isize = ajStrGetRes(thys) - ipos;
2077 	    /* ajDebug("expand to: ipos: %d isize: %d Size: %d\n",
2078 		    ipos, isize, ajStrGetRes(thys)); */
2079 
2080 	}
2081 	else
2082 	    buff = NULL;
2083     }
2084 
2085     ajStrSetValidLen(pthis, ilen);
2086 
2087     if(ajStrGetCharLast(*pthis) == '\n')
2088 	ajStrCutEnd(pthis, 1);
2089 
2090     /* PC files have \r\n Macintosh files have just \r : this fixes both */
2091 
2092     if(ajStrGetCharLast(*pthis) == '\r')
2093     {
2094 	/*ajDebug("Remove carriage-return characters from PC-style files\n");*/
2095 	ajStrCutEnd(pthis, 1);
2096     }
2097 
2098     ajStrTrimWhite(pthis);
2099 
2100     return ajStrGetLen(*pthis);
2101 }
2102 
2103 
2104 
2105 
2106 /* @func ajMessExit ***********************************************************
2107 **
2108 ** Delete any static initialised values
2109 **
2110 ** @return [void]
2111 **
2112 ** @release 1.0.0
2113 ** @@
2114 ******************************************************************************/
2115 
ajMessExit(void)2116 void ajMessExit(void)
2117 {
2118     AJFREE(messErrMess);
2119     messTableDelete(&messErrorTable);
2120     messTableDelete(&messDebugTestTable);
2121 
2122     return;
2123 }
2124 
2125 
2126 
2127 
2128 /* @func ajMessExitDebug ******************************************************
2129 **
2130 ** Delete any static initialised values for ajDebug calls
2131 **
2132 ** @return [void]
2133 **
2134 ** @release 6.2.0
2135 ** @@
2136 ******************************************************************************/
2137 
ajMessExitDebug(void)2138 void ajMessExitDebug(void)
2139 {
2140     ajFileClose(&messDebugFile);
2141     ajStrDel(&messDebugName);
2142     ajFileClose(&messDebugTestFile);
2143     ajStrDel(&messDebugName);
2144 
2145     return;
2146 }
2147 
2148 
2149 
2150 
2151 #ifdef AJ_COMPILE_DEPRECATED_BOOK
2152 #endif
2153 
2154 
2155 
2156 
2157 #ifdef AJ_COMPILE_DEPRECATED
2158 /* @obsolete ajMessErrorCount
2159 ** @rename ajMessGetCountError
2160 */
2161 
ajMessErrorCount(void)2162 __deprecated ajint ajMessErrorCount(void)
2163 {
2164     return errorCount;
2165 }
2166 
2167 
2168 
2169 
2170 /* @obsolete ajMessCaughtMessage
2171 ** @rename ajMessGetMessageC
2172 */
2173 
ajMessCaughtMessage(void)2174 __deprecated char* ajMessCaughtMessage(void)
2175 {
2176     return messbuf;
2177 }
2178 
2179 
2180 
2181 
2182 /* @obsolete ajMessSysErrorText
2183 ** @rename ajMessGetSysmessageC
2184 */
2185 
ajMessSysErrorText(void)2186 __deprecated char* ajMessSysErrorText(void)
2187 {
2188     char *mess;
2189 
2190     if(errno)
2191 	mess = ajFmtString(SYSERR_FORMAT, errno, strerror(errno));
2192     else
2193 	mess = ajFmtString(SYSERR_OK, errno, strerror(errno));
2194 
2195     /* must make copy - will be used when mess* calls itself */
2196     AJFREE(messErrMess);
2197     messErrMess = ajSysFuncStrdup(mess);
2198 
2199     AJFREE(mess);
2200 
2201     return messErrMess;
2202 }
2203 
2204 
2205 
2206 
2207 /* @obsolete ajDebugFile
2208 ** @rename ajMessGetDebugfile
2209 */
2210 
ajDebugFile(void)2211 __deprecated FILE* ajDebugFile(void)
2212 {
2213     return ajMessGetDebugfile();
2214 }
2215 #endif
2216