]>
Commit | Line | Data |
---|---|---|
9f4a0c62 RC |
1 | /* |
2 | * Copyright (c) 2002, Robert Collins. | |
3 | * | |
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. | |
8 | * | |
9 | * A copy of the GNU General Public License can be found at | |
10 | * http://www.gnu.org/ | |
11 | * | |
12 | * Written by Robert Collins <rbtcollins@hotmail.com> | |
13 | * | |
14 | */ | |
15 | ||
16 | /* Log to one or more files. */ | |
17 | ||
2e0aaec9 | 18 | #include <stdlib.h> |
9f4a0c62 RC |
19 | #include "LogFile.h" |
20 | #include "io_stream.h" | |
21 | #include "win32.h" | |
9f4a0c62 | 22 | #include "msg.h" |
7b21a88e | 23 | #include "dialog.h" |
9f4a0c62 RC |
24 | #include "resource.h" |
25 | #include <iostream> | |
c83c52d8 | 26 | #include <sstream> |
e2cdf72c | 27 | #include <set> |
9f4a0c62 | 28 | #include <time.h> |
74617327 | 29 | #include <string> |
072fb49a | 30 | #include <stdexcept> |
f9e903a3 | 31 | #include "AntiVirus.h" |
eef9ea26 | 32 | #include "filemanip.h" |
08678720 | 33 | #include "String++.h" |
9d350b6d JT |
34 | #include "getopt++/BoolOption.h" |
35 | ||
20f237b4 | 36 | static BoolOption VerboseOutput (false, 'v', "verbose", IDS_HELPTEXT_VERBOSE); |
9f4a0c62 RC |
37 | |
38 | /* private helper class */ | |
39 | class filedef | |
40 | { | |
41 | public: | |
42 | int level; | |
08678720 | 43 | std::string key; |
9f4a0c62 | 44 | bool append; |
08678720 | 45 | filedef (const std::string& _path) : key (_path) {} |
e2cdf72c RC |
46 | bool operator == (filedef const &rhs) const |
47 | { | |
afa76033 | 48 | return casecompare(key, rhs.key) == 0; |
e2cdf72c RC |
49 | } |
50 | bool operator < (filedef const &rhs) const | |
51 | { | |
afa76033 | 52 | return casecompare(key, rhs.key) < 0; |
e2cdf72c | 53 | } |
9f4a0c62 RC |
54 | }; |
55 | ||
56 | /* another */ | |
57 | struct LogEnt | |
58 | { | |
59 | LogEnt *next; | |
60 | enum log_level level; | |
61 | time_t when; | |
08678720 | 62 | std::string msg; |
9f4a0c62 RC |
63 | }; |
64 | ||
65 | static LogEnt *first_logent = 0; | |
66 | static LogEnt **next_logent = &first_logent; | |
67 | static LogEnt *currEnt = 0; | |
5fa64c3c CV |
68 | |
69 | int LogFile::exit_msg = 0; | |
9f4a0c62 | 70 | |
155eacb6 | 71 | typedef std::set<filedef> FileSet; |
e2cdf72c | 72 | static FileSet files; |
155eacb6 | 73 | static std::stringbuf *theStream; |
9f4a0c62 | 74 | |
c83c52d8 RC |
75 | LogFile * |
76 | LogFile::createLogFile() | |
77 | { | |
78 | theStream = new std::stringbuf; | |
79 | return new LogFile(theStream); | |
80 | } | |
81 | ||
82 | LogFile::LogFile(std::stringbuf *aStream) : LogSingleton (aStream) | |
9f4a0c62 | 83 | { |
9f4a0c62 RC |
84 | } |
85 | LogFile::~LogFile(){} | |
86 | ||
87 | void | |
88 | LogFile::clearFiles () | |
89 | { | |
e2cdf72c | 90 | files.clear (); |
9f4a0c62 RC |
91 | } |
92 | ||
93 | void | |
08678720 | 94 | LogFile::setFile (int minlevel, const std::string& path, bool append) |
9f4a0c62 | 95 | { |
e2cdf72c RC |
96 | FileSet::iterator f = files.find (filedef(path)); |
97 | if (f != files.end ()) | |
98 | files.erase (f); | |
99 | ||
100 | filedef t (path); | |
101 | t.level = minlevel; | |
102 | t.append = append; | |
103 | files.insert (t); | |
9f4a0c62 RC |
104 | } |
105 | ||
08678720 | 106 | std::string |
eef9ea26 RC |
107 | LogFile::getFileName (int level) const |
108 | { | |
109 | for (FileSet::iterator i = files.begin(); | |
110 | i != files.end(); ++i) | |
111 | { | |
112 | if (i->level == level) | |
113 | return i->key; | |
114 | } | |
af111c7d | 115 | return "<no log was in use>"; |
eef9ea26 RC |
116 | } |
117 | ||
9f4a0c62 | 118 | void |
5fa64c3c | 119 | LogFile::exit (int exit_code, bool show_end_install_msg) |
9f4a0c62 | 120 | { |
f9e903a3 | 121 | AntiVirus::AtExit(); |
9f4a0c62 | 122 | static int been_here = 0; |
13aebdbb | 123 | /* Exitcode -1 is special... */ |
9f4a0c62 | 124 | if (been_here) |
5fa64c3c | 125 | ::exit (exit_code); |
9f4a0c62 | 126 | been_here = 1; |
b08f9c71 | 127 | |
9f4a0c62 | 128 | if (exit_msg) |
7b21a88e | 129 | { |
b08f9c71 JT |
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; | |
7b21a88e | 133 | } |
b08f9c71 | 134 | |
13aebdbb CV |
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. */ | |
5fa64c3c CV |
137 | if (show_end_install_msg) |
138 | Log (LOG_TIMESTAMP) << "Ending cygwin install" << endLog; | |
9f4a0c62 | 139 | |
e2cdf72c RC |
140 | for (FileSet::iterator i = files.begin(); |
141 | i != files.end(); ++i) | |
9f4a0c62 | 142 | { |
e2cdf72c | 143 | log_save (i->level, i->key, i->append); |
9f4a0c62 | 144 | } |
0df9be37 | 145 | // TODO: remove this when the ::exit issue is tidied up. |
560ac5e2 | 146 | ::exit (exit_code); |
9f4a0c62 RC |
147 | } |
148 | ||
aa09dcbb CV |
149 | void |
150 | LogFile::flushAll () | |
151 | { | |
157dc2b8 | 152 | Log (LOG_TIMESTAMP) << "Writing messages to log files without exiting" << endLog; |
aa09dcbb CV |
153 | |
154 | for (FileSet::iterator i = files.begin(); | |
155 | i != files.end(); ++i) | |
156 | { | |
157 | log_save (i->level, i->key, i->append); | |
158 | } | |
159 | } | |
160 | ||
9f4a0c62 | 161 | void |
08678720 | 162 | LogFile::log_save (int babble, const std::string& filename, bool append) |
9f4a0c62 RC |
163 | { |
164 | static int been_here = 0; | |
165 | if (been_here) | |
166 | return; | |
167 | been_here = 1; | |
168 | ||
b41c2908 | 169 | io_stream::mkpath_p (PATH_TO_FILE, "file://" + filename, 0755); |
9f4a0c62 | 170 | |
26922cd2 | 171 | io_stream *f = io_stream::open("file://" + filename, append ? "at" : "wt", 0644); |
9f4a0c62 RC |
172 | if (!f) |
173 | { | |
d2a3615c | 174 | fatal (NULL, IDS_NOLOGFILE, filename.c_str()); |
9f4a0c62 RC |
175 | return; |
176 | } | |
177 | ||
178 | LogEnt *l; | |
179 | ||
180 | for (l = first_logent; l; l = l->next) | |
181 | { | |
182 | if (babble || !(l->level == LOG_BABBLE)) | |
183 | { | |
d2a3615c | 184 | const char *tstr = l->msg.c_str(); |
9f4a0c62 RC |
185 | f->write (tstr, strlen (tstr)); |
186 | if (tstr[strlen (tstr) - 1] != '\n') | |
187 | f->write ("\n", 1); | |
188 | } | |
189 | } | |
190 | ||
191 | delete f; | |
192 | been_here = 0; | |
193 | } | |
194 | ||
155eacb6 | 195 | std::ostream & |
9f4a0c62 RC |
196 | LogFile::operator() (log_level theLevel) |
197 | { | |
198 | if (theLevel < 1 || theLevel > 2) | |
155eacb6 | 199 | throw new std::invalid_argument("Invalid log_level"); |
9f4a0c62 | 200 | if (!theStream) |
c83c52d8 RC |
201 | theStream = new std::stringbuf; |
202 | rdbuf (theStream); | |
9f4a0c62 RC |
203 | currEnt = new LogEnt; |
204 | currEnt->next = 0; | |
205 | currEnt->level = theLevel; | |
206 | return *this; | |
207 | } | |
208 | ||
209 | void | |
210 | LogFile::endEntry() | |
211 | { | |
155eacb6 | 212 | std::string buf = theStream->str(); |
c8356810 CF |
213 | delete theStream; |
214 | ||
9d350b6d JT |
215 | /* also write to stdout */ |
216 | if ((currEnt->level >= LOG_PLAIN) || VerboseOutput) | |
3f7be627 JT |
217 | { |
218 | /* | |
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. | |
223 | ||
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. | |
226 | ||
227 | If the output is not a console, GetConsoleOutputCP() returns 0. | |
228 | Possibly it's a Cygwin pty? | |
229 | */ | |
230 | std::string cpbuf = buf; | |
231 | ||
232 | unsigned int ocp = GetConsoleOutputCP(); | |
233 | if ((ocp != 0 ) && (ocp != 65001)) | |
234 | cpbuf = wstring_to_string(string_to_wstring(buf), ocp); | |
235 | ||
236 | std::cout << cpbuf << std::endl; | |
237 | } | |
c8356810 | 238 | |
9f4a0c62 RC |
239 | if (!currEnt) |
240 | { | |
241 | /* get a default LogEnt */ | |
242 | currEnt = new LogEnt; | |
243 | currEnt->next = 0; | |
244 | currEnt->level = LOG_PLAIN; | |
245 | } | |
246 | *next_logent = currEnt; | |
247 | next_logent = &(currEnt->next); | |
248 | time (&(currEnt->when)); | |
249 | if (currEnt->level == LOG_TIMESTAMP) | |
250 | { | |
251 | char b[100]; | |
252 | struct tm *tm = localtime (&(currEnt->when)); | |
253 | strftime (b, 1000, "%Y/%m/%d %H:%M:%S ", tm); | |
254 | currEnt->msg = b; | |
255 | } | |
c8356810 | 256 | currEnt->msg += buf; |
c8356810 | 257 | |
9f4a0c62 | 258 | /* reset for next use */ |
c83c52d8 RC |
259 | theStream = new std::stringbuf; |
260 | rdbuf (theStream); | |
261 | init (theStream); | |
9f4a0c62 | 262 | } |