OSDN Git Service

Add initial working GUI mode DMH class implementation.
[mingw/mingw-get.git] / src / dmh.cpp
1 /*
2  * dmh.cpp
3  *
4  * $Id$
5  *
6  * Written by Keith Marshall <keithmarshall@users.sourceforge.net>
7  * Copyright (C) 2009, 2010, 2011, 2012, MinGW.org Project
8  *
9  *
10  * Implementation of the core components of, and the CLI specific
11  * API for, the diagnostic message handling subsystem.
12  *
13  *
14  * This is free software.  Permission is granted to copy, modify and
15  * redistribute this software, under the provisions of the GNU General
16  * Public License, Version 3, (or, at your option, any later version),
17  * as published by the Free Software Foundation; see the file COPYING
18  * for licensing details.
19  *
20  * Note, in particular, that this software is provided "as is", in the
21  * hope that it may prove useful, but WITHOUT WARRANTY OF ANY KIND; not
22  * even an implied WARRANTY OF MERCHANTABILITY, nor of FITNESS FOR ANY
23  * PARTICULAR PURPOSE.  Under no circumstances will the author, or the
24  * MinGW Project, accept liability for any damages, however caused,
25  * arising from the use of this software.
26  *
27  */
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <stdarg.h>
31
32 #include "dmhcore.h"
33
34 #define WIN32_LEAN_AND_MEAN
35 #include <windows.h>
36
37 /* Implementation of the dmh_exception class.
38  */
39 static const char *unspecified_error = "Unspecified error";
40
41 dmh_exception::dmh_exception() throw():
42   message( unspecified_error ){}
43
44 dmh_exception::dmh_exception( const char *msg ) throw():
45   message( unspecified_error )
46   {
47     if( msg && *msg )
48       message = msg;
49   }
50
51 dmh_exception::~dmh_exception() throw(){}
52
53 const char* dmh_exception::what() const throw()
54 {
55   return message;
56 }
57
58 class dmhTypeTTY: public dmhTypeGeneric
59 {
60   /* Diagnostic message handler for use in console applications.
61    */
62   public:
63     dmhTypeTTY( const char* );
64     virtual uint16_t control( const uint16_t, const uint16_t );
65     virtual int notify( const dmh_severity, const char*, va_list );
66     virtual int printf( const char*, va_list );
67
68   private:
69     inline int emit_and_flush( int );
70 };
71
72 /* Constructors serve to initialise the message handler,
73  * simply creating the class instance, and storing the specified
74  * program name within it.
75  */
76 dmhTypeGeneric::dmhTypeGeneric( const char* name ):progname( name ){}
77 dmhTypeTTY::dmhTypeTTY( const char* name ):dmhTypeGeneric( name ){}
78
79 /* This pointer stores the address of the message handler
80  * class instance, after initialisation.
81  */
82 static dmhTypeGeneric *dmh = NULL;
83
84 #define client GetModuleHandle( NULL )
85 static inline dmhTypeGeneric *dmh_init_gui( const char *progname )
86 {
87   /* Stub function to support run-time binding of a client-provided
88    * implementation for the DMH_SUBSYSTEM_GUI class methods.
89    */
90   typedef dmhTypeGeneric *(*init_hook)( const char * );
91   init_hook do_init = (init_hook)(GetProcAddress( client, "dmh_init_gui" ));
92   return do_init ? do_init( progname ) : NULL;
93 }
94
95 EXTERN_C void dmh_init( const dmh_class subsystem, const char *progname )
96 {
97   /* Public entry point for message handler initialisation...
98    *
99    * We only do it once, silently ignoring any attempt to
100    * establish a second handler.
101    */
102   if( dmh == NULL )
103   {
104     /* No message handler has yet been initialised;
105      * passing the specified program name, select...
106      */
107     if( (subsystem == DMH_SUBSYSTEM_GUI)
108       /*
109        * ...a GUI class handler on demand...
110        */
111     && ((dmh = dmh_init_gui( progname )) == NULL) )
112       /*
113        * ...but bail out, if this cannot be initialised...
114        */
115       throw dmh_exception( "DMH subsystem initialisation failed" );
116
117     else
118       /* ...otherwise, a console class handler by default.
119        */
120       dmh = new dmhTypeTTY( progname );
121   }
122 }
123
124 static inline
125 int abort_if_fatal( const dmh_severity code, int status )
126 {
127   /* Helper function to abort an application, on notification
128    * of a DMH_FATAL exception.
129    */
130   if( code == DMH_FATAL )
131     throw dmh_exception( "Fatal error occured" );
132
133   /* If the exception wasn't DMH_FATAL, then fall through to
134    * return the specified status code.
135    */
136   return status;
137 }
138
139 uint16_t dmhTypeTTY::control( const uint16_t request, const uint16_t mask )
140 {
141   /* Select optional features of the console class message handler.
142    * This message handler provides no optional features; we make this
143    * a "no-op".
144    */
145   return 0;
146 }
147
148 inline int dmhTypeTTY::emit_and_flush( int status )
149 {
150   /* Users of MSYS terminal emulators, in place of the standard
151    * MS-Windows console, may experience an I/O buffering issue on
152    * stderr (?!!) which may result in apparently erratic delivery
153    * of progress reporting diagnostics; this may lead to a false
154    * impression that mingw-get has stalled.  This inline wrapper
155    * ensures that any buffer which is improperly associated with
156    * stderr is flushed after each message is posted; this may
157    * mitigate the improper buffering issue.
158    */
159   fflush( stderr );
160   return status;
161 }
162
163 int dmhTypeTTY::notify( const dmh_severity code, const char *fmt, va_list argv )
164 {
165   /* Message dispatcher for console class applications.
166    */
167   static const char *severity[] =
168   {
169     /* Labels to identify message severity...
170      */
171     "INFO",             /* DMH_INFO */
172     "WARNING",          /* DMH_WARNING */
173     "ERROR",            /* DMH_ERROR */
174     "FATAL"             /* DMH_FATAL */
175   };
176
177   /* Dispatch the message to standard error, terminate application
178    * if DMH_FATAL, else continue, returning the message length.
179    */
180   return abort_if_fatal( code,
181       emit_and_flush(
182         fprintf( stderr, "%s: *** %s *** ", progname, severity[code] )
183         + vfprintf( stderr, fmt, argv )
184         )
185       );
186 }
187
188 int dmhTypeTTY::printf( const char *fmt, va_list argv )
189 {
190   /* Display arbitrary text messages via the diagnostic message handler;
191    * for the TTY subsystem, this is equivalent to printf() on stderr.
192    */
193   return emit_and_flush( vfprintf( stderr, fmt, argv ) );
194 }
195
196 EXTERN_C uint16_t dmh_control( const uint16_t request, const uint16_t mask )
197 {
198   return dmh->control( request, mask );
199 }
200
201 EXTERN_C int dmh_notify( const dmh_severity code, const char *fmt, ... )
202 {
203   /* Public entry point for diagnostic message dispatcher.
204    */
205   if( dmh == NULL )
206   {
207     /* The message handler has been called before initialising it;
208      * this is an internal program error -- treat it as fatal!
209      */
210     dmh_init( DMH_SUBSYSTEM_TTY, "dmh" );
211     dmh_notify( DMH_FATAL, "message handler was not initialised\n" );
212   }
213
214   /* Normal operation; pass the message on to the active handler.
215    */
216   va_list argv;
217   va_start( argv, fmt );
218   int retcode = dmh->notify( code, fmt, argv );
219   va_end( argv );
220   return retcode;
221 }
222
223 EXTERN_C int dmh_printf( const char *fmt, ... )
224 {
225   /* Simulate standard printf() function calls, redirecting the display
226    * of formatted output through the diagnostic message handler.
227    */
228   va_list argv;
229   va_start( argv, fmt );
230   int retcode = dmh->printf( fmt, argv );
231   va_end( argv );
232   return retcode;
233 }
234
235 /* $RCSfile$: end of file */