2 * Copyright (c) 2002, Robert Collins.
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * A copy of the GNU General Public License can be found at
12 * Written by Robert Collins <rbtcollins@hotmail.com>
16 /* Log to one or more files. */
20 #include "io_stream.h"
31 #include "AntiVirus.h"
32 #include "filemanip.h"
34 #include "getopt++/BoolOption.h"
36 static BoolOption
VerboseOutput (false, 'v', "verbose", IDS_HELPTEXT_VERBOSE
);
38 /* private helper class */
45 filedef (const std::string
& _path
) : key (_path
) {}
46 bool operator == (filedef
const &rhs
) const
48 return casecompare(key
, rhs
.key
) == 0;
50 bool operator < (filedef
const &rhs
) const
52 return casecompare(key
, rhs
.key
) < 0;
65 static LogEnt
*first_logent
= 0;
66 static LogEnt
**next_logent
= &first_logent
;
67 static LogEnt
*currEnt
= 0;
69 int LogFile::exit_msg
= 0;
71 typedef std::set
<filedef
> FileSet
;
73 static std::stringbuf
*theStream
;
76 LogFile::createLogFile()
78 theStream
= new std::stringbuf
;
79 return new LogFile(theStream
);
82 LogFile::LogFile(std::stringbuf
*aStream
) : LogSingleton (aStream
)
88 LogFile::clearFiles ()
94 LogFile::setFile (int minlevel
, const std::string
& path
, bool append
)
96 FileSet::iterator f
= files
.find (filedef(path
));
97 if (f
!= files
.end ())
107 LogFile::getFileName (int level
) const
109 for (FileSet::iterator i
= files
.begin();
110 i
!= files
.end(); ++i
)
112 if (i
->level
== level
)
115 return "<no log was in use>";
119 LogFile::exit (int exit_code
, bool show_end_install_msg
)
122 static int been_here
= 0;
123 /* Exitcode -1 is special... */
130 std::wstring fmt
= LoadStringWEx(exit_msg
, MAKELANGID(LANG_ENGLISH
, SUBLANG_ENGLISH_US
));
131 std::wstring buf
= format(fmt
, backslash(getFileName(LOG_BABBLE
)).c_str());
132 Log (LOG_PLAIN
) << "note: " << wstring_to_string(buf
) << endLog
;
135 /* ... in that it skips the boring log messages. Exit code -1 is used when
136 just printing the help output and when we're self-elevating. */
137 if (show_end_install_msg
)
138 Log (LOG_TIMESTAMP
) << "Ending cygwin install" << endLog
;
140 for (FileSet::iterator i
= files
.begin();
141 i
!= files
.end(); ++i
)
143 log_save (i
->level
, i
->key
, i
->append
);
145 // TODO: remove this when the ::exit issue is tidied up.
152 Log (LOG_TIMESTAMP
) << "Writing messages to log files without exiting" << endLog
;
154 for (FileSet::iterator i
= files
.begin();
155 i
!= files
.end(); ++i
)
157 log_save (i
->level
, i
->key
, i
->append
);
162 LogFile::log_save (int babble
, const std::string
& filename
, bool append
)
164 static int been_here
= 0;
169 io_stream::mkpath_p (PATH_TO_FILE
, "file://" + filename
, 0755);
171 io_stream
*f
= io_stream::open("file://" + filename
, append
? "at" : "wt", 0644);
174 fatal (NULL
, IDS_NOLOGFILE
, filename
.c_str());
180 for (l
= first_logent
; l
; l
= l
->next
)
182 if (babble
|| !(l
->level
== LOG_BABBLE
))
184 const char *tstr
= l
->msg
.c_str();
185 f
->write (tstr
, strlen (tstr
));
186 if (tstr
[strlen (tstr
) - 1] != '\n')
196 LogFile::operator() (log_level theLevel
)
198 if (theLevel
< 1 || theLevel
> 2)
199 throw new std::invalid_argument("Invalid log_level");
201 theStream
= new std::stringbuf
;
203 currEnt
= new LogEnt
;
205 currEnt
->level
= theLevel
;
212 std::string buf
= theStream
->str();
215 /* also write to stdout */
216 if ((currEnt
->level
>= LOG_PLAIN
) || VerboseOutput
)
219 The log message is UTF-8 encoded. Re-encode this in the console output
220 codepage (so it can be correctly decoded by a Windows terminal).
221 Unfortunately there's no API for direct multibyte re-encoding, so we
222 must do it in two steps UTF-8 -> UTF-16 -> CP_COCP.
224 If the console output codepage is UTF-8, we already have the log message
225 in the correct encoding, so we can avoid doing all that work.
227 If the output is not a console, GetConsoleOutputCP() returns 0.
228 Possibly it's a Cygwin pty?
230 std::string cpbuf
= buf
;
232 unsigned int ocp
= GetConsoleOutputCP();
233 if ((ocp
!= 0 ) && (ocp
!= 65001))
234 cpbuf
= wstring_to_string(string_to_wstring(buf
), ocp
);
236 std::cout
<< cpbuf
<< std::endl
;
241 /* get a default LogEnt */
242 currEnt
= new LogEnt
;
244 currEnt
->level
= LOG_PLAIN
;
246 *next_logent
= currEnt
;
247 next_logent
= &(currEnt
->next
);
248 time (&(currEnt
->when
));
249 if (currEnt
->level
== LOG_TIMESTAMP
)
252 struct tm
*tm
= localtime (&(currEnt
->when
));
253 strftime (b
, 1000, "%Y/%m/%d %H:%M:%S ", tm
);
258 /* reset for next use */
259 theStream
= new std::stringbuf
;