DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator
src/debug/debug_gui.cpp
00001 /*
00002  *  Copyright (C) 2002-2015  The DOSBox Team
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; either version 2 of the License, or
00007  *  (at your option) any later version.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU General Public License
00015  *  along with this program; if not, write to the Free Software
00016  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00017  */
00018 
00019 
00020 #include <stdlib.h>
00021 #include <stdarg.h>
00022 #include <string.h>
00023 #include <stdio.h>
00024 
00025 #include "dosbox.h"
00026 #include "logging.h"
00027 #include "support.h"
00028 #include "control.h"
00029 #include "regs.h"
00030 #include "menu.h"
00031 #include "debug.h"
00032 #include "debug_inc.h"
00033 
00034 #include <stdexcept>
00035 #include <exception>
00036 
00037 using namespace std;
00038 
00039 static bool has_LOG_Init = false;
00040 static bool has_LOG_EarlyInit = false;
00041 static bool do_LOG_stderr = false;
00042 
00043 bool logBuffSuppressConsole = false;
00044 bool logBuffSuppressConsoleNeedUpdate = false;
00045 
00046 _LogGroup loggrp[LOG_MAX]={{"",LOG_NORMAL},{0,LOG_NORMAL}};
00047 FILE* debuglog = NULL;
00048 
00049 #if C_DEBUG
00050 static bool logBuffHasDiscarded = false;
00051 
00052 #include <curses.h>
00053 
00054 #include <list>
00055 #include <string>
00056 using namespace std;
00057 
00058 std::string DBGBlock::windowlist_by_name(void) {
00059     std::string comp;
00060     unsigned int i;
00061 
00062     for (i=0;i < DBGBlock::WINI_MAX_INDEX;i++) {
00063         if (i != 0) comp += ",";
00064         comp += dbg_win_names[i];
00065     }
00066 
00067     return comp;
00068 }
00069 
00070 const unsigned int dbg_def_win_height[DBGBlock::WINI_MAX_INDEX] = {
00071     5,          /* WINI_REG */
00072     9,          /* WINI_DATA */
00073     12,         /* WINI_CODE */
00074     5,          /* WINI_VAR */
00075     6           /* WINI_OUT */
00076 };
00077 
00078 const char *dbg_def_win_titles[DBGBlock::WINI_MAX_INDEX] = {
00079     "Register Overview",        /* WINI_REG */
00080     "Data Overview",            /* WINI_DATA */
00081     "Code Overview",            /* WINI_CODE */
00082     "Variable Overview",        /* WINI_VAR */
00083     "Output"                    /* WINI_OUT */
00084 };
00085 
00086 const char *dbg_win_names[DBGBlock::WINI_MAX_INDEX] = {
00087     "REG",
00088     "DATA",
00089     "CODE",
00090     "VAR",
00091     "OUT"
00092 };
00093 
00094 #define MAX_LOG_BUFFER 4000
00095 static list<string> logBuff;
00096 static list<string>::iterator logBuffPos = logBuff.end();
00097 
00098 extern int old_cursor_state;
00099 
00100 const char *DBGBlock::get_winname(int idx) {
00101     if (idx >= 0 && idx < DBGBlock::WINI_MAX_INDEX)
00102         return dbg_win_names[idx];
00103 
00104     return NULL;
00105 }
00106 
00107 const char *DBGBlock::get_wintitle(int idx) {
00108     if (idx >= 0 && idx < DBGBlock::WINI_MAX_INDEX)
00109         return dbg.win_title[idx].c_str();
00110 
00111     return NULL;
00112 }
00113 
00114 int DBGBlock::name_to_win(const char *name) {
00115     for (unsigned int i=0;i < DBGBlock::WINI_MAX_INDEX;i++) {
00116         if (!strcasecmp(name,dbg_win_names[i]))
00117             return (int)i;
00118     }
00119 
00120     return -1;
00121 }
00122 
00123 void DBGBlock::next_window(void) {
00124     int order = win_find_order((int)active_win);
00125 
00126     if (order < 0) order = 0;
00127 
00128     order = win_next_by_order(order);
00129     if (order >= 0) active_win = win_order[order];
00130 }
00131 
00132 void DBGBlock::swap_order(int o1,int o2) {
00133     if (o1 != o2)
00134         std::swap(win_order[o1],win_order[o2]);
00135 }
00136 
00137 WINDOW* &DBGBlock::get_win_ref(int idx) {
00138     switch (idx) {
00139         case WINI_REG:  return win_reg;
00140         case WINI_DATA: return win_data;
00141         case WINI_CODE: return win_code;
00142         case WINI_VAR:  return win_var;
00143         case WINI_OUT:  return win_out;
00144     }
00145 
00146     throw domain_error("get_win_ref");
00147 }
00148 
00149 WINDOW* DBGBlock::get_win(int idx) {
00150     return get_win_ref(idx);
00151 }
00152 
00153 WINDOW *DBGBlock::get_active_win(void) {
00154     return get_win((int)active_win);
00155 }
00156 
00157 int DBGBlock::win_find_order(int wnd) {
00158     for (unsigned int i=0;i < DBGBlock::WINI_MAX_INDEX;i++) {
00159         if (dbg.win_order[i] == wnd)
00160             return (int)i;
00161     }
00162 
00163     return -1;
00164 }
00165 
00166 int DBGBlock::win_prev_by_order(int order) {
00167     int limit = DBGBlock::WINI_MAX_INDEX;
00168 
00169     do {
00170         if (--order < 0)
00171             order = DBGBlock::WINI_MAX_INDEX - 1;
00172 
00173         if (--limit <= 0)
00174             break;
00175     } while (get_win(win_order[order]) == NULL);
00176 
00177     return order;
00178 }
00179 
00180 int DBGBlock::win_next_by_order(int order) {
00181     int limit = DBGBlock::WINI_MAX_INDEX;
00182 
00183     do {
00184         if (++order >= DBGBlock::WINI_MAX_INDEX)
00185             order = 0;
00186 
00187         if (--limit <= 0)
00188             break;
00189     } while (get_win(win_order[order]) == NULL);
00190 
00191     return order;
00192 }
00193 
00194 void DBGUI_DrawBlankOutputLine(int y) {
00195     if (dbg.win_out == NULL) return;
00196 
00197     wattrset(dbg.win_out,COLOR_PAIR(PAIR_GREEN_BLACK));
00198     if (logBuffHasDiscarded)
00199         mvwprintw(dbg.win_out, y, 0, "<LOG BUFFER ENDS, OLDER CONTENT DISCARDED BEYOND THIS POINT>");
00200     else
00201         mvwprintw(dbg.win_out, y, 0, "<END OF LOG>");
00202 //    wclrtoeol(dbg.win_out);
00203 }
00204 
00205 void DBGUI_DrawDebugOutputLine(int y,std::string line) {
00206         if (dbg.win_out == NULL) return;
00207 
00208         int maxy, maxx; getmaxyx(dbg.win_out,maxy,maxx);
00209     bool ellipsisEnd = false;
00210 
00211     (void)maxy;//UNUSED
00212 
00213     /* cut the line short if it's too long for the terminal window */
00214     if (line.length() > (size_t)maxx) {
00215         line = line.substr(0,(size_t)(maxx - 3));
00216         ellipsisEnd = true;
00217     }
00218 
00219     /* Const cast is needed for pdcurses which has no const char in mvwprintw (bug maybe) */
00220     wattrset(dbg.win_out,0);
00221     mvwprintw(dbg.win_out, y, 0, const_cast<char*>(line.c_str()));
00222 
00223     if (ellipsisEnd) {
00224         wattrset(dbg.win_out,COLOR_PAIR(PAIR_GREEN_BLACK));
00225         mvwprintw(dbg.win_out, y, maxx-3,  "...");
00226     }
00227 
00228 //    wclrtoeol(dbg.win_out);
00229 }
00230 
00231 void DEBUG_LimitTopPos(void) {
00232         if (dbg.win_out != NULL) {
00233         int w,h;
00234 
00235         getmaxyx(dbg.win_out,h,w);
00236 
00237         auto i = logBuff.begin();
00238         for (int y=0;i != logBuff.end() && y < (h-1);y++) {
00239             if (i == logBuffPos) {
00240                 i++;
00241                 logBuffPos = i;
00242             }
00243             else {
00244                 i++;
00245             }
00246         }
00247     }
00248 }
00249 
00250 void DEBUG_RefreshPage(char scroll) {
00251         if (dbg.win_out == NULL) return;
00252 
00253         while (scroll < 0 && logBuffPos!=logBuff.begin()) {
00254         logBuffPos--;
00255         scroll++;
00256     }
00257         while (scroll > 0 && logBuffPos!=logBuff.end()) {
00258         logBuffPos++;
00259         scroll--;
00260     }
00261 
00262     DEBUG_LimitTopPos();
00263 
00264         list<string>::iterator i = logBuffPos;
00265         int maxy, maxx; getmaxyx(dbg.win_out,maxy,maxx);
00266         int rem_lines = maxy;
00267         if(rem_lines <= 0) return;
00268 
00269         wclear(dbg.win_out);
00270 
00271     /* NTS: Often, i == logBuff.end() unless the user scrolled up in the interface.
00272      *
00273      *      So the original intent of this code is that the iterator is one entry PAST
00274      *      the line to begin displaying at the bottom just as end() is a sentinel
00275      *      value just past the last element of the list. So if i == logBuff.begin()
00276      *      then there's nothing to display.
00277      *
00278      *      Note that the iterator defines what is drawn on the bottom-most line,
00279      *      and iteration is done backwards until we've drawn the line at i == logBuff.begin()
00280      *      or until we've drawn something at line 0 of the subwin.
00281      *
00282      *      rem_lines starts out as the number of lines in the subwin. */
00283     if (i != logBuff.begin()) {
00284         i--;
00285 
00286         wattrset(dbg.win_out,0);
00287         while (rem_lines > 0) {
00288             rem_lines--;
00289 
00290             DBGUI_DrawDebugOutputLine(rem_lines,*i);
00291 
00292             if (i != logBuff.begin())
00293                 i--;
00294             else
00295                 break;
00296         }
00297 
00298         /* show that the lines above are beyond the end of the log */
00299         while (rem_lines > 0) {
00300             rem_lines--;
00301             DBGUI_DrawBlankOutputLine(rem_lines);
00302         }
00303     }
00304 
00305         wrefresh(dbg.win_out);
00306 }
00307 
00308 void DEBUG_ScrollHomeOutput(void) {
00309     logBuffPos = logBuff.begin();
00310     DEBUG_RefreshPage(0);
00311 }
00312 
00313 void DEBUG_ScrollToEndOutput(void) {
00314     logBuffPos = logBuff.end();
00315     DEBUG_RefreshPage(0);
00316 }
00317 
00318 static void Draw_RegisterLayout(void) {
00319         if (dbg.win_main == NULL)
00320                 return;
00321 
00322         mvwaddstr(dbg.win_reg,0,0,"EAX=");
00323         mvwaddstr(dbg.win_reg,1,0,"EBX=");
00324         mvwaddstr(dbg.win_reg,2,0,"ECX=");
00325         mvwaddstr(dbg.win_reg,3,0,"EDX=");
00326 
00327         mvwaddstr(dbg.win_reg,0,14,"ESI=");
00328         mvwaddstr(dbg.win_reg,1,14,"EDI=");
00329         mvwaddstr(dbg.win_reg,2,14,"EBP=");
00330         mvwaddstr(dbg.win_reg,3,14,"ESP=");
00331 
00332         mvwaddstr(dbg.win_reg,0,28,"DS=");
00333         mvwaddstr(dbg.win_reg,0,38,"ES=");
00334         mvwaddstr(dbg.win_reg,0,48,"FS=");
00335         mvwaddstr(dbg.win_reg,0,58,"GS=");
00336         mvwaddstr(dbg.win_reg,0,68,"SS=");
00337 
00338         mvwaddstr(dbg.win_reg,1,28,"CS=");
00339         mvwaddstr(dbg.win_reg,1,38,"EIP=");
00340 
00341         mvwaddstr(dbg.win_reg,2,75,"CPL");
00342         mvwaddstr(dbg.win_reg,2,68,"IOPL");
00343 
00344         mvwaddstr(dbg.win_reg,1,52,"C  Z  S  O  A  P  D  I  T ");
00345 }
00346 
00347 static void DrawSubWinBox(WINDOW *wnd,const char *title) {
00348     bool active = false;
00349     int x,y;
00350     int w,h;
00351 
00352     if (wnd == NULL) return;
00353 
00354     WINDOW *active_win = dbg.get_active_win();
00355 
00356     if (wnd == active_win)
00357         active = true;
00358 
00359     getbegyx(wnd,y,x);
00360     getmaxyx(wnd,h,w);
00361 
00362     (void)h;//UNUSED
00363 
00364         if (has_colors()) {
00365         if (active)
00366                 attrset(COLOR_PAIR(PAIR_BLACK_BLUE));
00367         else
00368                 attrset(COLOR_PAIR(PAIR_WHITE_BLUE));
00369     }
00370 
00371     mvhline(y-1,x,ACS_HLINE,w);
00372     if (title != NULL) mvaddstr(y-1,x+4,title);
00373 }
00374 
00375 void DrawBars(void) {
00376         if (dbg.win_main == NULL)
00377                 return;
00378 
00379     for (unsigned int wnd=0;wnd < DBGBlock::WINI_MAX_INDEX;wnd++) {
00380         WINDOW* &ref = dbg.get_win_ref((int)wnd);
00381 
00382         if (ref != NULL) DrawSubWinBox(ref,dbg.get_wintitle((int)wnd));
00383     }
00384 
00385         attrset(0);
00386 }
00387 
00388 static void DestroySubWindows(void) {
00389     for (unsigned int wnd=0;wnd < DBGBlock::WINI_MAX_INDEX;wnd++) {
00390         WINDOW* &ref = dbg.get_win_ref((int)wnd);
00391 
00392         if (ref != NULL) {
00393             if (ref) delwin(ref);
00394             ref = NULL;
00395         }
00396     }
00397 }
00398 
00399 void DEBUG_GUI_DestroySubWindows(void) {
00400     DestroySubWindows();
00401 }
00402 
00403 static void MakeSubWindows(void) {
00404         /* The Std output win should go at the bottom */
00405         /* Make all the subwindows */
00406         int win_main_maxy, win_main_maxx; getmaxyx(dbg.win_main,win_main_maxy,win_main_maxx);
00407     unsigned int yheight[DBGBlock::WINI_MAX_INDEX];
00408     unsigned int yofs[DBGBlock::WINI_MAX_INDEX];
00409     int win_limit = win_main_maxy - 1;
00410     int expand_wndi = -1;
00411         int outy=0,height;
00412 
00413     /* main window needs to clear itself */
00414     werase(dbg.win_main);
00415 
00416     /* arrange windows */
00417     for (unsigned int wndi=0;wndi < DBGBlock::WINI_MAX_INDEX;wndi++) {
00418         unsigned int wnd = dbg.win_order[wndi];
00419 
00420         /* window must have been freed, must have height greater than 1 (more than just titlebar)
00421          * must be visible and there must be room for the window on the screen, where the last
00422          * row is reserved for the input bar */
00423         if (dbg.win_height[wnd] > 1 &&
00424             dbg.win_vis[wnd] && (outy+1) < win_limit) {
00425             outy++; // header
00426             height=(int)dbg.win_height[wnd] - 1;
00427             if ((outy+height) > win_limit) height = win_limit - outy;
00428             assert(height > 0);
00429             yofs[wndi]=(unsigned int)outy;
00430             yheight[wndi]=(unsigned int)height;
00431             outy+=height;
00432 
00433             if (wnd == DBGBlock::WINI_OUT)
00434                 expand_wndi = (int)wndi;
00435         }
00436         else {
00437             yofs[wndi]=0;
00438             yheight[wndi]=0;
00439         }
00440     }
00441 
00442     /* last window expands if output not there */
00443     if (expand_wndi < 0) {
00444         for (int wndi=DBGBlock::WINI_MAX_INDEX-1;wndi >= 0;wndi--) {
00445             if (yheight[wndi] != 0) {
00446                 expand_wndi = wndi;
00447                 break;
00448             }
00449         }
00450     }
00451 
00452     /* we give one window the power to expand to help fill the screen */
00453     if (outy < win_limit) {
00454         int expand_by = win_limit - outy;
00455 
00456         if (expand_wndi >= 0 && expand_wndi < DBGBlock::WINI_MAX_INDEX) {
00457             unsigned int wndi = (unsigned int)expand_wndi;
00458 
00459             /* add height to the window */
00460             yheight[wndi] += (unsigned int)expand_by;
00461             outy += (int)wndi;
00462             wndi++;
00463 
00464             /* move the others down */
00465             for (;wndi < DBGBlock::WINI_MAX_INDEX;wndi++)
00466                 yofs[wndi] += (unsigned int)expand_by;
00467         }
00468     }
00469 
00470     for (unsigned int wndi=0;wndi < DBGBlock::WINI_MAX_INDEX;wndi++) {
00471         if (yheight[wndi] != 0) {
00472             unsigned int wnd = dbg.win_order[wndi];
00473             WINDOW* &ref = dbg.get_win_ref((int)wnd);
00474             assert(ref == NULL);
00475             ref = subwin(dbg.win_main,(int)yheight[wndi],win_main_maxx,(int)yofs[wndi],0);
00476         }
00477     }
00478 
00479     if (outy < win_main_maxy) {
00480         // no header
00481         height = 1;
00482         outy = win_main_maxy - height;
00483         dbg.win_inp=subwin(dbg.win_main,height,win_main_maxx,outy,0);
00484         outy += height;
00485     }
00486 
00487         DrawBars();
00488         Draw_RegisterLayout();
00489         refresh();
00490 }
00491 
00492 void DEBUG_GUI_Rebuild(void) {
00493     DestroySubWindows();
00494     MakeSubWindows();
00495     DEBUG_RefreshPage(0);
00496 }
00497 
00498 void DEBUG_GUI_OnResize(void) {
00499     DEBUG_GUI_Rebuild();
00500 }
00501 
00502 static void MakePairs(void) {
00503         init_pair(PAIR_BLACK_BLUE, COLOR_BLACK, COLOR_CYAN);
00504         init_pair(PAIR_BYELLOW_BLACK, COLOR_YELLOW /*| FOREGROUND_INTENSITY */, COLOR_BLACK);
00505         init_pair(PAIR_GREEN_BLACK, COLOR_GREEN /*| FOREGROUND_INTENSITY */, COLOR_BLACK);
00506         init_pair(PAIR_BLACK_GREY, COLOR_BLACK /*| FOREGROUND_INTENSITY */, COLOR_WHITE);
00507         init_pair(PAIR_GREY_RED, COLOR_WHITE/*| FOREGROUND_INTENSITY */, COLOR_RED);
00508     init_pair(PAIR_BLACK_GREEN, COLOR_BLACK, COLOR_GREEN);
00509         init_pair(PAIR_WHITE_BLUE, COLOR_WHITE/* | FOREGROUND_INTENSITY*/, COLOR_BLUE);
00510 }
00511 
00512 void DBGUI_NextWindow(void) {
00513     dbg.next_window();
00514         DrawBars();
00515         DEBUG_DrawScreen();
00516 }
00517 
00518 void DBGUI_NextWindowIfActiveHidden(void) {
00519     if (dbg.get_active_win() == NULL)
00520         DBGUI_NextWindow();
00521 }
00522 
00523 bool DEBUG_IsDebuggerConsoleVisible(void) {
00524         return (dbg.win_main != NULL);
00525 }
00526 
00527 void DBGUI_StartUp(void) {
00528         mainMenu.get_item("show_console").check(true).refresh_item(mainMenu);
00529 
00530         LOG(LOG_MISC,LOG_DEBUG)("DEBUG GUI startup");
00531         /* Start the main window */
00532         dbg.win_main=initscr();
00533         cbreak();       /* take input chars one at a time, no wait for \n */
00534         noecho();       /* don't echo input */
00535         scrollok(stdscr,false);
00536         nodelay(dbg.win_main,true);
00537         keypad(dbg.win_main,true);
00538         #ifndef WIN32
00539         touchwin(dbg.win_main);
00540         #endif
00541         old_cursor_state = curs_set(0);
00542         start_color();
00543         cycle_count=0;
00544         MakePairs();
00545         MakeSubWindows();
00546 
00547     /* make sure the output window is synced up */
00548     logBuffPos = logBuff.end();
00549     DEBUG_RefreshPage(0);
00550 }
00551 
00552 #endif
00553 
00554 void DEBUG_ShowMsg(char const* format,...) {
00555         bool stderrlog = false;
00556         char buf[512];
00557         va_list msg;
00558         size_t len;
00559 
00560         va_start(msg,format);
00561         len = (size_t)vsnprintf(buf,sizeof(buf)-2u,format,msg); /* <- NTS: Did you know sprintf/vsnprintf returns number of chars written? */
00562         va_end(msg);
00563 
00564     /* remove newlines if present */
00565     while (len > 0 && buf[len-1] == '\n') buf[--len] = 0;
00566 
00567         if (do_LOG_stderr || debuglog == NULL)
00568                 stderrlog = true;
00569 
00570 #if C_DEBUG
00571         if (dbg.win_out != NULL)
00572                 stderrlog = false;
00573     else
00574         stderrlog = true;
00575 #endif
00576 
00577         if (debuglog != NULL) {
00578                 fprintf(debuglog,"%s\n",buf);
00579                 fflush(debuglog);
00580         }
00581         if (stderrlog) {
00582                 fprintf(stderr,"LOG: %s\n",buf);
00583                 fflush(stderr);
00584         }
00585 
00586 #if C_DEBUG
00587         if (logBuffPos!=logBuff.end()) {
00588                 logBuffPos=logBuff.end();
00589                 DEBUG_RefreshPage(0);
00590         }
00591         logBuff.push_back(buf);
00592         if (logBuff.size() > MAX_LOG_BUFFER) {
00593         logBuffHasDiscarded = true;
00594         if (logBuffPos == logBuff.begin()) logBuffPos++; /* keep the iterator valid */
00595                 logBuff.pop_front();
00596     }
00597 
00598         logBuffPos = logBuff.end();
00599 
00600         if (dbg.win_out != NULL) {
00601         if (logBuffSuppressConsole) {
00602             logBuffSuppressConsoleNeedUpdate = true;
00603         }
00604         else {
00605             int maxy, maxx; getmaxyx(dbg.win_out,maxy,maxx);
00606 
00607             scrollok(dbg.win_out,TRUE);
00608             scroll(dbg.win_out);
00609             scrollok(dbg.win_out,FALSE);
00610 
00611             DBGUI_DrawDebugOutputLine(maxy-1,buf);
00612 
00613             wrefresh(dbg.win_out);
00614         }
00615         }
00616 #endif
00617 }
00618 
00619 /* callback function when DOSBox-X exits */
00620 void LOG::Exit() {
00621         if (debuglog != NULL) {
00622                 fprintf(debuglog,"--END OF LOG--\n");
00623                 fclose(debuglog);
00624                 debuglog = NULL;
00625         }
00626 }
00627 
00628 void Null_Init(Section *sec);
00629 
00630 void LOG::operator() (char const* format, ...){
00631         const char *s_severity = "";
00632         char buf[512];
00633         va_list msg;
00634 
00635         switch (d_severity) {
00636                 case LOG_DEBUG: s_severity = " DEBUG"; break;
00637                 case LOG_NORMAL:s_severity = "      "; break;
00638                 case LOG_WARN:  s_severity = " WARN "; break;
00639                 case LOG_ERROR: s_severity = " ERROR"; break;
00640                 case LOG_FATAL: s_severity = " FATAL"; break;
00641                 default: break;
00642         };
00643 
00644         va_start(msg,format);
00645         vsnprintf(buf,sizeof(buf)-1,format,msg);
00646         va_end(msg);
00647 
00648         if (d_type>=LOG_MAX) return;
00649         if (d_severity < loggrp[d_type].min_severity) return;
00650         DEBUG_ShowMsg("%10u%s %s:%s\n",static_cast<Bit32u>(cycle_count),s_severity,loggrp[d_type].front,buf);
00651 }
00652 
00653 void LOG::ParseEnableSetting(_LogGroup &group,const char *setting) {
00654         if (!strcmp(setting,"true") || !strcmp(setting,"1") || !strcmp(setting,"normal"))
00655                 group.min_severity = LOG_NORMAL; /* original code's handling is equivalent to our "normal" setting */
00656         else if (!strcmp(setting,"false") || !strcmp(setting,"0") || !strcmp(setting,""))
00657                 group.min_severity = LOG_ERROR; /* original code's handling is equivalent to our "error" setting */
00658         else if (!strcmp(setting,"debug"))
00659                 group.min_severity = LOG_DEBUG;
00660         else if (!strcmp(setting,"warn"))
00661                 group.min_severity = LOG_WARN;
00662         else if (!strcmp(setting,"error"))
00663                 group.min_severity = LOG_ERROR;
00664         else if (!strcmp(setting,"fatal"))
00665                 group.min_severity = LOG_FATAL;
00666         else if (!strcmp(setting,"never"))
00667                 group.min_severity = LOG_NEVER;
00668         else
00669                 group.min_severity = LOG_NORMAL;
00670 }
00671 
00672 void LOG::Init() {
00673         char buf[1024];
00674 
00675         assert(control != NULL);
00676 
00677         /* do not init twice */
00678         if (has_LOG_Init) return;
00679         has_LOG_Init = true;
00680 
00681         /* announce */
00682         LOG_MSG("Logging init: beginning logging proper. This is the end of the early init logging");
00683 
00684         /* get the [log] section */
00685         Section_prop *sect = static_cast<Section_prop *>(control->GetSection("log"));
00686         assert(sect != NULL);
00687 
00688         /* do we write to a logfile, or not? */
00689         const char *blah = sect->Get_string("logfile");
00690         if (blah != NULL && blah[0] != 0) {
00691                 if ((debuglog=fopen(blah,"wt+")) != NULL) {
00692                         LOG_MSG("Logging: opened logfile '%s' successfully. All further logging will go to that file.",blah);
00693                         setbuf(debuglog,NULL);
00694                 }
00695                 else {
00696                         LOG_MSG("Logging: failed to open logfile '%s'. All further logging will be discarded. Error: %s",blah,strerror(errno));
00697                 }
00698         }
00699         else {
00700                 LOG_MSG("Logging: No logfile was given. All further logging will be discarded.");
00701                 debuglog=0;
00702         }
00703 
00704         /* end of early init logging */
00705         do_LOG_stderr = false;
00706 
00707         /* read settings for each log category, unless the -debug option was given,
00708          * in which case everything is set to debug level */
00709         for (Bitu i=1;i<LOG_MAX;i++) {
00710                 strcpy(buf,loggrp[i].front);
00711                 buf[strlen(buf)]=0;
00712                 lowcase(buf);
00713 
00714                 if (control->opt_debug)
00715                         ParseEnableSetting(/*&*/loggrp[i],"debug");
00716                 else
00717                         ParseEnableSetting(/*&*/loggrp[i],sect->Get_string(buf));
00718         }
00719 
00720         LOG(LOG_MISC,LOG_DEBUG)("Logging init complete");
00721 }
00722 
00723 void LOG::EarlyInit(void) {
00724         assert(control != NULL);
00725 
00726         /* do not init twice */
00727         if (has_LOG_EarlyInit) return;
00728         has_LOG_EarlyInit = true;
00729 
00730         /* Setup logging groups */
00731         loggrp[LOG_ALL].front="ALL";
00732         loggrp[LOG_VGA].front="VGA";
00733         loggrp[LOG_VGAGFX].front="VGAGFX";
00734         loggrp[LOG_VGAMISC].front="VGAMISC";
00735         loggrp[LOG_INT10].front="INT10";
00736         loggrp[LOG_SB].front="SBLASTER";
00737         loggrp[LOG_DMACONTROL].front="DMA_CONTROL";
00738         
00739         loggrp[LOG_FPU].front="FPU";
00740         loggrp[LOG_CPU].front="CPU";
00741         loggrp[LOG_PAGING].front="PAGING";
00742 
00743         loggrp[LOG_FCB].front="FCB";
00744         loggrp[LOG_FILES].front="FILES";
00745         loggrp[LOG_IOCTL].front="IOCTL";
00746         loggrp[LOG_EXEC].front="EXEC";
00747         loggrp[LOG_DOSMISC].front="DOSMISC";
00748 
00749         loggrp[LOG_PIT].front="PIT";
00750         loggrp[LOG_KEYBOARD].front="KEYBOARD";
00751         loggrp[LOG_PIC].front="PIC";
00752 
00753         loggrp[LOG_MOUSE].front="MOUSE";
00754         loggrp[LOG_BIOS].front="BIOS";
00755         loggrp[LOG_GUI].front="GUI";
00756         loggrp[LOG_MISC].front="MISC";
00757 
00758         loggrp[LOG_IO].front="IO";
00759         loggrp[LOG_PCI].front="PCI";
00760         
00761         loggrp[LOG_VOODOO].front="SST";
00762 
00763         do_LOG_stderr = control->opt_earlydebug;
00764 
00765         for (Bitu i=1;i<LOG_MAX;i++) {
00766                 if (control->opt_earlydebug)
00767                         ParseEnableSetting(/*&*/loggrp[i],"debug");
00768                 else
00769                         ParseEnableSetting(/*&*/loggrp[i],"warn");
00770         }
00771 
00772         LOG_MSG("Early LOG Init complete");
00773 }
00774 
00775 void LOG::SetupConfigSection(void) {
00776         const char *log_values[] = {
00777                 /* compatibility with existing dosbox.conf files */
00778                 "true", "false",
00779 
00780                 /* log levels */
00781                 "debug",
00782                 "normal",
00783                 "warn",
00784                 "error",
00785                 "fatal",
00786                 "never",                /* <- this means NEVER EVER log anything */
00787 
00788                 0};
00789 
00790         /* Register the log section */
00791         Section_prop * sect=control->AddSection_prop("log",Null_Init);
00792         Prop_string* Pstring = sect->Add_string("logfile",Property::Changeable::Always,"");
00793         Pstring->Set_help("file where the log messages will be saved to");
00794         char buf[1024];
00795         for (Bitu i=1;i<LOG_MAX;i++) {
00796                 strcpy(buf,loggrp[i].front);
00797                 lowcase(buf);
00798 
00799                 Pstring = sect->Add_string(buf,Property::Changeable::Always,"false");
00800                 Pstring->Set_values(log_values);
00801                 Pstring->Set_help("Enable/Disable logging of this type.");
00802         }
00803 }
00804