Add ProgramData to the env var whitelist for running scripts
[cygwin-setup.git] / LogFile.cc
blobab2e3ec9c936368fe8ecb50b5b0ac15e78954d81
1 /*
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
10 * http://www.gnu.org/
12 * Written by Robert Collins <rbtcollins@hotmail.com>
16 /* Log to one or more files. */
18 #include <stdlib.h>
19 #include "LogFile.h"
20 #include "io_stream.h"
21 #include "win32.h"
22 #include "msg.h"
23 #include "dialog.h"
24 #include "resource.h"
25 #include <iostream>
26 #include <sstream>
27 #include <set>
28 #include <time.h>
29 #include <string>
30 #include <stdexcept>
31 #include "AntiVirus.h"
32 #include "filemanip.h"
33 #include "String++.h"
34 #include "getopt++/BoolOption.h"
36 static BoolOption VerboseOutput (false, 'v', "verbose", IDS_HELPTEXT_VERBOSE);
38 /* private helper class */
39 class filedef
41 public:
42 int level;
43 std::string key;
44 bool append;
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;
56 /* another */
57 struct LogEnt
59 LogEnt *next;
60 enum log_level level;
61 time_t when;
62 std::string msg;
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;
72 static FileSet files;
73 static std::stringbuf *theStream;
75 LogFile *
76 LogFile::createLogFile()
78 theStream = new std::stringbuf;
79 return new LogFile(theStream);
82 LogFile::LogFile(std::stringbuf *aStream) : LogSingleton (aStream)
85 LogFile::~LogFile(){}
87 void
88 LogFile::clearFiles ()
90 files.clear ();
93 void
94 LogFile::setFile (int minlevel, const std::string& path, bool append)
96 FileSet::iterator f = files.find (filedef(path));
97 if (f != files.end ())
98 files.erase (f);
100 filedef t (path);
101 t.level = minlevel;
102 t.append = append;
103 files.insert (t);
106 std::string
107 LogFile::getFileName (int level) const
109 for (FileSet::iterator i = files.begin();
110 i != files.end(); ++i)
112 if (i->level == level)
113 return i->key;
115 return "<no log was in use>";
118 void
119 LogFile::exit (int exit_code, bool show_end_install_msg)
121 AntiVirus::AtExit();
122 static int been_here = 0;
123 /* Exitcode -1 is special... */
124 if (been_here)
125 ::exit (exit_code);
126 been_here = 1;
128 if (exit_msg)
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.
146 ::exit (exit_code);
149 void
150 LogFile::flushAll ()
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);
161 void
162 LogFile::log_save (int babble, const std::string& filename, bool append)
164 static int been_here = 0;
165 if (been_here)
166 return;
167 been_here = 1;
169 io_stream::mkpath_p (PATH_TO_FILE, "file://" + filename, 0755);
171 io_stream *f = io_stream::open("file://" + filename, append ? "at" : "wt", 0644);
172 if (!f)
174 fatal (NULL, IDS_NOLOGFILE, filename.c_str());
175 return;
178 LogEnt *l;
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')
187 f->write ("\n", 1);
191 delete f;
192 been_here = 0;
195 std::ostream &
196 LogFile::operator() (log_level theLevel)
198 if (theLevel < 1 || theLevel > 2)
199 throw new std::invalid_argument("Invalid log_level");
200 if (!theStream)
201 theStream = new std::stringbuf;
202 rdbuf (theStream);
203 currEnt = new LogEnt;
204 currEnt->next = 0;
205 currEnt->level = theLevel;
206 return *this;
209 void
210 LogFile::endEntry()
212 std::string buf = theStream->str();
213 delete theStream;
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;
239 if (!currEnt)
241 /* get a default LogEnt */
242 currEnt = new LogEnt;
243 currEnt->next = 0;
244 currEnt->level = LOG_PLAIN;
246 *next_logent = currEnt;
247 next_logent = &(currEnt->next);
248 time (&(currEnt->when));
249 if (currEnt->level == LOG_TIMESTAMP)
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;
256 currEnt->msg += buf;
258 /* reset for next use */
259 theStream = new std::stringbuf;
260 rdbuf (theStream);
261 init (theStream);