v-r to drano merge conflicts wip

master
Brad Payne (Vir Linden) 2012-06-26 16:43:58 -04:00
commit 74d4f8af01
166 changed files with 9579 additions and 2355 deletions

22
.hgtags
View File

@ -110,6 +110,7 @@ bb1075286b3b147b1dae2e3d6b2d56f04ff03f35 DRTVWR-52_2.6.6-beta1
bb1075286b3b147b1dae2e3d6b2d56f04ff03f35 2.6.6-beta1
5e349dbe9cc84ea5795af8aeb6d473a0af9d4953 2.6.8-start
beafa8a9bd1d1b670b7523d865204dc4a4b38eef DRTVWR-55_2.6.8-beta1
bb9932a7a5fd00edf52d95f354e3b37ae6a942db DRTVWR-156
beafa8a9bd1d1b670b7523d865204dc4a4b38eef 2.6.8-beta1
11d5d8080e67c3955914caf98f2eb116af30e55a 2.6.9-start
e67da2c6e3125966dd49eef98b36317afac1fcfe 2.6.9-start
@ -149,6 +150,7 @@ a9abb9633a266c8d2fe62411cfd1c86d32da72bf 2.7.1-release
09984bfa6cae17e0f72d02b75c1b7393c65eecfc 2.7.5-beta1
e1ed60913230dd64269a7f7fc52cbc6004f6d52c 2.8.0-start
502f6a5deca9365ddae57db4f1e30172668e171e 2.8.1-start
2a3965b3ad202df7ea25d2be689291bb14a1280e DRTVWR-155
6866d9df6efbd441c66451debd376d21211de39c DRTVWR-68_2.7.5-release
6866d9df6efbd441c66451debd376d21211de39c 2.7.5-release
e1ed60913230dd64269a7f7fc52cbc6004f6d52c DRTVWR-71_2.8.0-beta1
@ -260,12 +262,12 @@ c6175c955a19e9b9353d242889ec1779b5762522 3.2.5-release
3d75c836d178c7c7e788f256afe195f6cab764a2 3.2.7-beta1
89980333c99dbaf1787fe20784f1d8849e9b5d4f 3.2.8-start
16f8e2915f3f2e4d732fb3125daf229cb0fd1875 DRTVWR-114_3.2.8-beta1
37dd400ad721e2a89ee820ffc1e7e433c68f3ca2 3.2.9-start
16f8e2915f3f2e4d732fb3125daf229cb0fd1875 3.2.8-beta1
987425b1acf4752379b2e1eb20944b4b35d67a85 DRTVWR-115_3.2.8-beta2
987425b1acf4752379b2e1eb20944b4b35d67a85 3.2.8-beta2
51b2fd52e36aab8f670e0874e7e1472434ec4b4a DRTVWR-113_3.2.8-release
51b2fd52e36aab8f670e0874e7e1472434ec4b4a 3.2.8-release
37dd400ad721e2a89ee820ffc1e7e433c68f3ca2 3.2.9-start
e9c82fca5ae6fb8a8af29012d78fb194a29323f3 DRTVWR-117_3.2.9-beta1
e9c82fca5ae6fb8a8af29012d78fb194a29323f3 3.2.9-beta1
a01ef9bed28627f4ca543fbc1d70c79cc297a90f DRTVWR-118_3.2.9-beta2
@ -297,3 +299,21 @@ d29a260119f8d5a5d168e25fed0c7ea6b3f40161 3.3.2-beta1
c623bbc854b6f7ee1b33a3718f76715046aa2937 viewer-release-candidate
675668bd24d3bea570814f71762a2a806f7e1b8d viewer-release-candidate
675668bd24d3bea570814f71762a2a806f7e1b8d 3.3.2-release
675668bd24d3bea570814f71762a2a806f7e1b8d viewer-release-candidate
600f3b3920d94de805ac6dc8bb6def9c069dd360 DRTVWR-162
24a7281bef42bd4430ceb25db8b195449c2c7de3 DRTVWR-153
15e90b52dc0297921b022b90d10d797436b8a1bd viewer-release-candidate
6414ecdabc5d89515b08d1f872cf923ed3a5523a DRTVWR-148
1b7f311b5a5dbfbed3dcbb4ed44afa20f89cad4c DRTVWR-144
1b7f311b5a5dbfbed3dcbb4ed44afa20f89cad4c 3.3.3-beta1
5910f8063a7e1ddddf504c2f35ca831cc5e8f469 DRTVWR-160
1b7f311b5a5dbfbed3dcbb4ed44afa20f89cad4c 3.3.3-beta1
f0a174c2adb4bc39b16722a61d7eeb4f2a1d4843 3.3.3-beta1
1b7f311b5a5dbfbed3dcbb4ed44afa20f89cad4c DRTVWR-144
f0a174c2adb4bc39b16722a61d7eeb4f2a1d4843 DRTVWR-144
2d6c0634b11e6f3df11002b8510a72a0433da00a DRTVWR-164
80b5e5e9775966d3839331ffa7a16a60f9d7c930 DRTVWR-165
fdcc08a4f20ae9bb060f4693c8980d216534efdf 3.3.3-beta2
af5f3e43e6e4424b1da19d9e16f6b853a7b822ed DRTVWR-169
4b3c68199a86cabaa5d9466d7b0f7e141e901d7a 3.3.3-beta3
6428242e124b523813bfaf4c45b3d422f0298c81 3.3.3-release

View File

@ -90,9 +90,9 @@
<key>archive</key>
<map>
<key>hash</key>
<string>9868bfa0b6954e4884c49c6f30068c80</string>
<string>2dfcd809e747f714b3fe0bf82a175812</string>
<key>url</key>
<string>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/apr_suite-1.4.2-darwin-20110217.tar.bz2</string>
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-apr/rev/259951/arch/Darwin/installer/apr_suite-1.4.5-darwin-20120618.tar.bz2</string>
</map>
<key>name</key>
<string>darwin</string>
@ -102,9 +102,9 @@
<key>archive</key>
<map>
<key>hash</key>
<string>ff62946c518a247c86e1066c1e9a5855</string>
<string>f38c966a430012dc157fdc104f23a59b</string>
<key>url</key>
<string>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/apr_suite-1.4.2-linux-20110309.tar.bz2</string>
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-apr/rev/259951/arch/Linux/installer/apr_suite-1.4.5-linux-20120618.tar.bz2</string>
</map>
<key>name</key>
<string>linux</string>
@ -114,9 +114,9 @@
<key>archive</key>
<map>
<key>hash</key>
<string>73785c200a5b4ef74a1230b028bb680d</string>
<string>4a9d040582342699c58c886c5ccd2caf</string>
<key>url</key>
<string>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/apr_suite-1.4.2-windows-20110217.tar.bz2</string>
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-apr/rev/259951/arch/CYGWIN/installer/apr_suite-1.4.5-windows-20120618.tar.bz2</string>
</map>
<key>name</key>
<string>windows</string>

View File

@ -1160,9 +1160,9 @@ Tofu Buzzard
CTS-411
STORM-546
VWR-24509
STORM-1684
SH-2477
STORM-1684
STORM-1819
Tony Kembia
Torben Trautman
TouchaHoney Perhaps

View File

@ -66,7 +66,6 @@ if (VIEWER)
add_subdirectory(${LIBS_OPEN_PREFIX}llcrashlogger)
add_subdirectory(${LIBS_OPEN_PREFIX}llplugin)
add_subdirectory(${LIBS_OPEN_PREFIX}llui)
add_subdirectory(${LIBS_OPEN_PREFIX}llxuixml)
add_subdirectory(${LIBS_OPEN_PREFIX}viewer_components)
# Legacy C++ tests. Build always, run if LL_TESTS is true.

View File

@ -9,6 +9,9 @@ MACRO(LL_TEST_COMMAND OUTVAR LD_LIBRARY_PATH)
FOREACH(dir ${LD_LIBRARY_PATH})
LIST(APPEND value "-l${dir}")
ENDFOREACH(dir)
# Enough different tests want to be able to find CMake's PYTHON_EXECUTABLE
# that we should just pop it into the environment for everybody.
LIST(APPEND value "-DPYTHON=${PYTHON_EXECUTABLE}")
LIST(APPEND value ${ARGN})
SET(${OUTVAR} ${value})
##IF(LL_TEST_VERBOSE)

View File

@ -46,6 +46,7 @@ $/LicenseInfo$
import os
import sys
import signal
import subprocess
def main(command, libpath=[], vars={}):
@ -113,6 +114,33 @@ def main(command, libpath=[], vars={}):
sys.stdout.flush()
return subprocess.call(command)
# swiped from vita, sigh, seems like a Bad Idea to introduce dependency
def translate_rc(rc):
"""
Accept an rc encoded as for subprocess.Popen.returncode:
None means still running
int >= 0 means terminated voluntarily with specified rc
int < 0 means terminated by signal (-rc)
Return a string explaining the outcome. In case of a signal, try to
name the corresponding symbol from the 'signal' module.
"""
if rc is None:
return "still running"
if rc >= 0:
return "terminated with rc %s" % rc
# Negative rc means the child was terminated by signal -rc.
rc = -rc
for attr in dir(signal):
if attr.startswith('SIG') and getattr(signal, attr) == rc:
strc = attr
break
else:
strc = str(rc)
return "terminated by signal %s" % strc
if __name__ == "__main__":
from optparse import OptionParser
parser = OptionParser(usage="usage: %prog [options] command args...")
@ -140,5 +168,5 @@ if __name__ == "__main__":
vars=dict([(pair.split('=', 1) + [""])[:2] for pair in opts.vars]))
if rc not in (None, 0):
print >>sys.stderr, "Failure running: %s" % " ".join(args)
print >>sys.stderr, "Error: %s" % rc
print >>sys.stderr, "Error %s: %s" % (rc, translate_rc(rc))
sys.exit((rc < 0) and 255 or rc)

View File

@ -18,7 +18,6 @@ include(LLWindow)
include(LLUI)
include(LLVFS) # ugh, needed for LLDir
include(LLXML)
include(LLXUIXML)
include(Linking)
# include(Tut)
@ -32,7 +31,6 @@ include_directories(
${LLVFS_INCLUDE_DIRS}
${LLWINDOW_INCLUDE_DIRS}
${LLXML_INCLUDE_DIRS}
${LLXUIXML_INCLUDE_DIRS}
)
set(llui_libtest_SOURCE_FILES

View File

@ -41,6 +41,14 @@ import tarfile
import errno
import subprocess
class ManifestError(RuntimeError):
"""Use an exception more specific than generic Python RuntimeError"""
pass
class MissingError(ManifestError):
"""You specified a file that doesn't exist"""
pass
def path_ancestors(path):
drive, path = os.path.splitdrive(os.path.normpath(path))
result = []
@ -180,6 +188,9 @@ def usage(srctree=""):
arg['description'] % nd)
def main():
## import itertools
## print ' '.join((("'%s'" % item) if ' ' in item else item)
## for item in itertools.chain([sys.executable], sys.argv))
option_names = [arg['name'] + '=' for arg in ARGUMENTS]
option_names.append('help')
options, remainder = getopt.getopt(sys.argv[1:], "", option_names)
@ -385,7 +396,7 @@ class LLManifest(object):
child.stdout.close()
status = child.wait()
if status:
raise RuntimeError(
raise ManifestError(
"Command %s returned non-zero status (%s) \noutput:\n%s"
% (command, status, output) )
return output
@ -395,7 +406,7 @@ class LLManifest(object):
a) verify that you really have created it
b) schedule it for cleanup"""
if not os.path.exists(path):
raise RuntimeError, "Should be something at path " + path
raise ManifestError, "Should be something at path " + path
self.created_paths.append(path)
def put_in_file(self, contents, dst):
@ -550,7 +561,7 @@ class LLManifest(object):
except (IOError, os.error), why:
errors.append((srcname, dstname, why))
if errors:
raise RuntimeError, errors
raise ManifestError, errors
def cmakedirs(self, path):
@ -598,11 +609,10 @@ class LLManifest(object):
def check_file_exists(self, path):
if not os.path.exists(path) and not os.path.islink(path):
raise RuntimeError("Path %s doesn't exist" % (
os.path.normpath(os.path.join(os.getcwd(), path)),))
raise MissingError("Path %s doesn't exist" % (os.path.abspath(path),))
wildcard_pattern = re.compile('\*')
wildcard_pattern = re.compile(r'\*')
def expand_globs(self, src, dst):
src_list = glob.glob(src)
src_re, d_template = self.wildcard_regex(src.replace('\\', '/'),
@ -615,7 +625,7 @@ class LLManifest(object):
sys.stdout.write("Processing %s => %s ... " % (src, dst))
sys.stdout.flush()
if src == None:
raise RuntimeError("No source file, dst is " + dst)
raise ManifestError("No source file, dst is " + dst)
if dst == None:
dst = src
dst = os.path.join(self.get_dst_prefix(), dst)
@ -637,13 +647,23 @@ class LLManifest(object):
else:
count += self.process_file(src, dst)
return count
try:
count = try_path(os.path.join(self.get_src_prefix(), src))
except RuntimeError:
for pfx in self.get_src_prefix(), self.get_artwork_prefix(), self.get_build_prefix():
try:
count = try_path(os.path.join(self.get_artwork_prefix(), src))
except RuntimeError:
count = try_path(os.path.join(self.get_build_prefix(), src))
count = try_path(os.path.join(pfx, src))
except MissingError:
# If src isn't a wildcard, and if that file doesn't exist in
# this pfx, try next pfx.
count = 0
continue
# Here try_path() didn't raise MissingError. Did it process any files?
if count:
break
# Even though try_path() didn't raise MissingError, it returned 0
# files. src is probably a wildcard meant for some other pfx. Loop
# back to try the next.
print "%d files" % count
def do(self, *actions):

View File

@ -10,14 +10,14 @@ include(UI)
include(LLCommon)
include(LLVFS)
include(LLXML)
include(LLXUIXML)
include(LLUI)
include(Linking)
include_directories(
${LLCOMMON_INCLUDE_DIRS}
${LLVFS_INCLUDE_DIRS}
${LLXML_INCLUDE_DIRS}
${LLXUIXML_INCLUDE_DIRS}
${LLUI_INCLUDE_DIRS}
${CURL_INCLUDE_DIRS}
${CARES_INCLUDE_DIRS}
${OPENSSL_INCLUDE_DIRS}
@ -42,7 +42,7 @@ target_link_libraries(linux-updater
${CRYPTO_LIBRARIES}
${UI_LIBRARIES}
${LLXML_LIBRARIES}
${LLXUIXML_LIBRARIES}
${LLUI_LIBRARIES}
${LLVFS_LIBRARIES}
${LLCOMMON_LIBRARIES}
)

View File

@ -1,4 +1,4 @@
/**
/**
* @file linux_updater.cpp
* @author Kyle Ambroff <ambroff@lindenlab.com>, Tofu Linden
* @brief Viewer update program for unix platforms that support GTK+
@ -6,21 +6,21 @@
* $LicenseInfo:firstyear=2008&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@ -34,10 +34,30 @@
#include "llfile.h"
#include "lldir.h"
#include "lldiriterator.h"
/*==========================================================================*|
// IQA-490: Use of LLTrans -- by this program at least -- appears to be buggy.
// With it, the 3.3.2 beta 1 linux-updater.bin crashes; without it seems stable.
#include "llxmlnode.h"
#include "lltrans.h"
|*==========================================================================*/
static class LLTrans
{
public:
LLTrans();
static std::string getString(const std::string& key);
private:
std::string _getString(const std::string& key) const;
typedef std::map<std::string, std::string> MessageMap;
MessageMap mMessages;
} sLLTransInstance;
#include <curl/curl.h>
#include <map>
#include <boost/foreach.hpp>
extern "C" {
#include <gtk/gtk.h>
@ -85,6 +105,8 @@ void init_default_trans_args()
bool translate_init(std::string comma_delim_path_list,
std::string base_xml_name)
{
return true;
/*==========================================================================*|
init_default_trans_args();
// extract paths string vector from comma-delimited flat string
@ -112,6 +134,7 @@ bool translate_init(std::string comma_delim_path_list,
LLTrans::parseStrings(root, default_trans_args);
return true;
}
|*==========================================================================*/
}
@ -151,7 +174,7 @@ void updater_app_ui_init(UpdaterAppState *app_state)
GTK_WIN_POS_CENTER_ALWAYS);
gtk_container_set_border_width(GTK_CONTAINER(app_state->window), 12);
g_signal_connect(G_OBJECT(app_state->window), "delete-event",
g_signal_connect(G_OBJECT(app_state->window), "delete-event",
G_CALLBACK(on_window_closed), app_state);
vbox = gtk_vbox_new(FALSE, 6);
@ -165,7 +188,7 @@ void updater_app_ui_init(UpdaterAppState *app_state)
summary_label = gtk_label_new(NULL);
gtk_label_set_use_markup(GTK_LABEL(summary_label), TRUE);
gtk_label_set_markup(GTK_LABEL(summary_label),
gtk_label_set_markup(GTK_LABEL(summary_label),
label_ostr.str().c_str());
gtk_misc_set_alignment(GTK_MISC(summary_label), 0, 0.5);
gtk_box_pack_start(GTK_BOX(vbox), summary_label, FALSE, FALSE, 0);
@ -195,9 +218,9 @@ void updater_app_ui_init(UpdaterAppState *app_state)
// set up progress bar, and update it roughly every 1/10 of a second
app_state->progress_bar = gtk_progress_bar_new();
gtk_progress_bar_set_text(GTK_PROGRESS_BAR(app_state->progress_bar),
gtk_progress_bar_set_text(GTK_PROGRESS_BAR(app_state->progress_bar),
LLTrans::getString("UpdaterProgressBarTextWithEllipses").c_str());
gtk_box_pack_start(GTK_BOX(vbox),
gtk_box_pack_start(GTK_BOX(vbox),
app_state->progress_bar, FALSE, TRUE, 0);
app_state->progress_update_timeout_id = g_timeout_add
(UPDATE_PROGRESS_TIMEOUT, progress_update_timeout, app_state);
@ -299,7 +322,7 @@ gpointer worker_thread_cb(gpointer data)
g_error_free(error);
throw 0;
}
if(tmp_local_filename != NULL)
{
app_state->file = tmp_local_filename;
@ -342,7 +365,7 @@ gpointer worker_thread_cb(gpointer data)
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, TRUE);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, package_file);
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, FALSE);
curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION,
curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION,
&download_progress_cb);
curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, app_state);
@ -352,8 +375,8 @@ gpointer worker_thread_cb(gpointer data)
if (result)
{
llerrs << "Failed to download update: "
<< app_state->url
llerrs << "Failed to download update: "
<< app_state->url
<< llendl;
gdk_threads_enter();
@ -365,7 +388,7 @@ gpointer worker_thread_cb(gpointer data)
throw 0;
}
}
// now pulse the progres bar back and forth while the package is
// being unpacked
gdk_threads_enter();
@ -386,8 +409,8 @@ gpointer worker_thread_cb(gpointer data)
gdk_threads_enter();
display_error(app_state->window,
LLTrans::getString("UpdaterFailInstallTitle"),
LLTrans::getString("UpdaterFailUpdateDescriptive"));
LLTrans::getString("UpdaterFailInstallTitle"),
LLTrans::getString("UpdaterFailUpdateDescriptive"));
//"Failed to update " + app_state->app_name,
gdk_threads_leave();
throw 0;
@ -402,8 +425,8 @@ gpointer worker_thread_cb(gpointer data)
gdk_threads_enter();
display_error(app_state->window,
LLTrans::getString("UpdaterFailStartTitle"),
LLTrans::getString("UpdaterFailUpdateDescriptive"));
LLTrans::getString("UpdaterFailStartTitle"),
LLTrans::getString("UpdaterFailUpdateDescriptive"));
gdk_threads_leave();
throw 0;
}
@ -448,7 +471,7 @@ gboolean less_anal_gspawnsync(gchar **argv,
// restore SIGCHLD handler
sigaction(SIGCHLD, &sigchld_backup, NULL);
return rtn;
}
@ -477,7 +500,7 @@ rename_with_sudo_fallback(const std::string& filename, const std::string& newnam
{
char *src_string_copy = g_strdup(filename.c_str());
char *dst_string_copy = g_strdup(newname.c_str());
char* argv[] =
char* argv[] =
{
sudo_cmd,
mv_cmd,
@ -492,8 +515,8 @@ rename_with_sudo_fallback(const std::string& filename, const std::string& newnam
if (!less_anal_gspawnsync(argv, &stderr_output,
&child_exit_status, &spawn_error))
{
llwarns << "Failed to spawn child process: "
<< spawn_error->message
llwarns << "Failed to spawn child process: "
<< spawn_error->message
<< llendl;
}
else if (child_exit_status)
@ -506,7 +529,7 @@ rename_with_sudo_fallback(const std::string& filename, const std::string& newnam
{
// everything looks good, clear the error code
rtncode = 0;
}
}
g_free(src_string_copy);
g_free(dst_string_copy);
@ -531,7 +554,7 @@ gboolean install_package(std::string package_file, std::string destination)
}
llinfos << "Found tar command: " << tar_cmd << llendl;
// Unpack the tarball in a temporary place first, then move it to
// Unpack the tarball in a temporary place first, then move it to
// its final destination
std::string tmp_dest_dir = gDirUtilp->getTempFilename();
if (LLFile::mkdir(tmp_dest_dir, 0744))
@ -571,8 +594,8 @@ gboolean install_package(std::string package_file, std::string destination)
if (!less_anal_gspawnsync(argv, &stderr_output,
&child_exit_status, &untar_error))
{
llwarns << "Failed to spawn child process: "
<< untar_error->message
llwarns << "Failed to spawn child process: "
<< untar_error->message
<< llendl;
return FALSE;
}
@ -605,8 +628,8 @@ gboolean install_package(std::string package_file, std::string destination)
if (rename_with_sudo_fallback(destination, backup_dir))
{
llwarns << "Failed to move directory: '"
<< destination << "' -> '" << backup_dir
llwarns << "Failed to move directory: '"
<< destination << "' -> '" << backup_dir
<< llendl;
return FALSE;
}
@ -617,7 +640,7 @@ gboolean install_package(std::string package_file, std::string destination)
if (rename_with_sudo_fallback(tmp_dest_dir, destination))
{
llwarns << "Failed to move installation to the destination: "
<< destination
<< destination
<< llendl;
return FALSE;
}
@ -713,7 +736,7 @@ BOOL spawn_viewer(UpdaterAppState *app_state)
if (!success)
{
llwarns << "Failed to launch viewer: " << error->message
llwarns << "Failed to launch viewer: " << error->message
<< llendl;
}
@ -751,7 +774,7 @@ void parse_args_and_init(int argc, char **argv, UpdaterAppState *app_state)
else if ((!strcmp(argv[i], "--image-dir")) && (++i < argc))
{
app_state->image_dir = argv[i];
app_state->image_dir_iter = new LLDirIterator(argv[i], "/*.jpg");
app_state->image_dir_iter = new LLDirIterator(argv[i], "*.jpg");
}
else if ((!strcmp(argv[i], "--dest")) && (++i < argc))
{
@ -772,8 +795,8 @@ void parse_args_and_init(int argc, char **argv, UpdaterAppState *app_state)
}
}
if (app_state->app_name.empty()
|| (app_state->url.empty() && app_state->file.empty())
if (app_state->app_name.empty()
|| (app_state->url.empty() && app_state->file.empty())
|| app_state->dest_dir.empty())
{
show_usage_and_exit();
@ -799,7 +822,7 @@ int main(int argc, char **argv)
(gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, ""));
std::string old_log_file = gDirUtilp->getExpandedFilename
(LL_PATH_LOGS, "updater.log.old");
std::string log_file =
std::string log_file =
gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "updater.log");
LLFile::rename(log_file, old_log_file);
LLError::logToFile(log_file);
@ -841,3 +864,63 @@ int main(int argc, char **argv)
return success ? 0 : 1;
}
/*****************************************************************************
* Dummy LLTrans implementation (IQA-490)
*****************************************************************************/
static LLTrans sStaticStrings;
// lookup
std::string LLTrans::_getString(const std::string& key) const
{
MessageMap::const_iterator found = mMessages.find(key);
if (found != mMessages.end())
{
return found->second;
}
LL_WARNS("linux_updater") << "No message for key '" << key
<< "' -- add to LLTrans::LLTrans() in linux_updater.cpp"
<< LL_ENDL;
return key;
}
// static lookup
std::string LLTrans::getString(const std::string& key)
{
return sLLTransInstance._getString(key);
}
// initialization
LLTrans::LLTrans()
{
typedef std::pair<const char*, const char*> Pair;
static const Pair data[] =
{
Pair("UpdaterFailDownloadTitle",
"Failed to download update"),
Pair("UpdaterFailInstallTitle",
"Failed to install update"),
Pair("UpdaterFailStartTitle",
"Failed to start viewer"),
Pair("UpdaterFailUpdateDescriptive",
"An error occurred while updating Second Life. "
"Please download the latest version from www.secondlife.com."),
Pair("UpdaterNowInstalling",
"Installing Second Life..."),
Pair("UpdaterNowUpdating",
"Now updating Second Life..."),
Pair("UpdaterProgressBarText",
"Downloading update"),
Pair("UpdaterProgressBarTextWithEllipses",
"Downloading update..."),
Pair("UpdaterUpdatingDescriptive",
"Your Second Life Viewer is being updated to the latest release. "
"This may take some time, so please be patient."),
Pair("UpdaterWindowTitle",
"Second Life Update")
};
BOOST_FOREACH(Pair pair, data)
{
mMessages[pair.first] = pair.second;
}
}

View File

@ -571,7 +571,8 @@ void LLAudioDecodeMgr::Impl::processQueue(const F32 num_secs)
llwarns << mCurrentDecodep->getUUID() << " has invalid vorbis data, aborting decode" << llendl;
mCurrentDecodep->flushBadFile();
LLAudioData *adp = gAudiop->getAudioData(mCurrentDecodep->getUUID());
adp->setHasValidData(FALSE);
adp->setHasValidData(false);
adp->setHasCompletedDecode(true);
mCurrentDecodep = NULL;
done = TRUE;
}
@ -586,11 +587,16 @@ void LLAudioDecodeMgr::Impl::processQueue(const F32 num_secs)
if (mCurrentDecodep->finishDecode())
{
// We finished!
if (mCurrentDecodep->isValid() && mCurrentDecodep->isDone())
LLAudioData *adp = gAudiop->getAudioData(mCurrentDecodep->getUUID());
if (!adp)
{
LLAudioData *adp = gAudiop->getAudioData(mCurrentDecodep->getUUID());
adp->setHasDecodedData(TRUE);
adp->setHasValidData(TRUE);
llwarns << "Missing LLAudioData for decode of " << mCurrentDecodep->getUUID() << llendl;
}
else if (mCurrentDecodep->isValid() && mCurrentDecodep->isDone())
{
adp->setHasCompletedDecode(true);
adp->setHasDecodedData(true);
adp->setHasValidData(true);
// At this point, we could see if anyone needs this sound immediately, but
// I'm not sure that there's a reason to - we need to poll all of the playing
@ -599,7 +605,8 @@ void LLAudioDecodeMgr::Impl::processQueue(const F32 num_secs)
}
else
{
llinfos << "Vorbis decode failed!!!" << llendl;
adp->setHasCompletedDecode(true);
llinfos << "Vorbis decode failed for " << mCurrentDecodep->getUUID() << llendl;
}
mCurrentDecodep = NULL;
}
@ -667,16 +674,19 @@ BOOL LLAudioDecodeMgr::addDecodeRequest(const LLUUID &uuid)
if (gAudiop->hasDecodedFile(uuid))
{
// Already have a decoded version, don't need to decode it.
//llinfos << "addDecodeRequest for " << uuid << " has decoded file already" << llendl;
return TRUE;
}
if (gAssetStorage->hasLocalAsset(uuid, LLAssetType::AT_SOUND))
{
// Just put it on the decode queue.
//llinfos << "addDecodeRequest for " << uuid << " has local asset file already" << llendl;
mImpl->mDecodeQueue.push(uuid);
return TRUE;
}
//llinfos << "addDecodeRequest for " << uuid << " no file available" << llendl;
return FALSE;
}

View File

@ -1221,10 +1221,11 @@ void LLAudioEngine::assetCallback(LLVFS *vfs, const LLUUID &uuid, LLAssetType::E
// Need to mark data as bad to avoid constant rerequests.
LLAudioData *adp = gAudiop->getAudioData(uuid);
if (adp)
{
{ // Make sure everything is cleared
adp->setHasValidData(false);
adp->setHasLocalData(false);
adp->setHasDecodedData(false);
adp->setHasCompletedDecode(true);
}
}
else
@ -1237,6 +1238,7 @@ void LLAudioEngine::assetCallback(LLVFS *vfs, const LLUUID &uuid, LLAssetType::E
}
else
{
// llinfos << "Got asset callback with good audio data for " << uuid << ", making decode request" << llendl;
adp->setHasValidData(true);
adp->setHasLocalData(true);
gAudioDecodeMgrp->addDecodeRequest(uuid);
@ -1304,16 +1306,18 @@ void LLAudioSource::update()
if (!getCurrentBuffer())
{
if (getCurrentData())
LLAudioData *adp = getCurrentData();
if (adp)
{
// Hack - try and load the sound. Will do this as a callback
// on decode later.
if (getCurrentData()->load() && getCurrentData()->getBuffer())
if (adp->load() && adp->getBuffer())
{
play(getCurrentData()->getID());
play(adp->getID());
}
else
else if (adp->hasCompletedDecode()) // Only mark corrupted after decode is done
{
llwarns << "Marking LLAudioSource corrupted for " << adp->getID() << llendl;
mCorrupted = true ;
}
}
@ -1731,6 +1735,7 @@ LLAudioData::LLAudioData(const LLUUID &uuid) :
mBufferp(NULL),
mHasLocalData(false),
mHasDecodedData(false),
mHasCompletedDecode(false),
mHasValidData(true)
{
if (uuid.isNull())
@ -1742,12 +1747,13 @@ LLAudioData::LLAudioData(const LLUUID &uuid) :
if (gAudiop && gAudiop->hasDecodedFile(uuid))
{
// Already have a decoded version, don't need to decode it.
mHasLocalData = true;
mHasDecodedData = true;
setHasLocalData(true);
setHasDecodedData(true);
setHasCompletedDecode(true);
}
else if (gAssetStorage && gAssetStorage->hasLocalAsset(uuid, LLAssetType::AT_SOUND))
{
mHasLocalData = true;
setHasLocalData(true);
}
}

View File

@ -372,10 +372,12 @@ public:
bool hasLocalData() const { return mHasLocalData; }
bool hasDecodedData() const { return mHasDecodedData; }
bool hasCompletedDecode() const { return mHasCompletedDecode; }
bool hasValidData() const { return mHasValidData; }
void setHasLocalData(const bool hld) { mHasLocalData = hld; }
void setHasDecodedData(const bool hdd) { mHasDecodedData = hdd; }
void setHasCompletedDecode(const bool hcd) { mHasCompletedDecode = hcd; }
void setHasValidData(const bool hvd) { mHasValidData = hvd; }
friend class LLAudioEngine; // Severe laziness, bad.
@ -383,9 +385,10 @@ public:
protected:
LLUUID mID;
LLAudioBuffer *mBufferp; // If this data is being used by the audio system, a pointer to the buffer will be set here.
bool mHasLocalData;
bool mHasDecodedData;
bool mHasValidData;
bool mHasLocalData; // Set true if the sound asset file is available locally
bool mHasDecodedData; // Set true if the sound file has been decoded
bool mHasCompletedDecode; // Set true when the sound is decoded
bool mHasValidData; // Set false if decoding failed, meaning the sound asset is bad
};

View File

@ -61,7 +61,10 @@ set(llcommon_SOURCE_FILES
llformat.cpp
llframetimer.cpp
llheartbeat.cpp
llinitparam.cpp
llinstancetracker.cpp
llleap.cpp
llleaplistener.cpp
llliveappconfig.cpp
lllivefile.cpp
lllog.cpp
@ -74,13 +77,14 @@ set(llcommon_SOURCE_FILES
llmortician.cpp
lloptioninterface.cpp
llptrto.cpp
llprocesslauncher.cpp
llprocess.cpp
llprocessor.cpp
llqueuedthread.cpp
llrand.cpp
llrefcount.cpp
llrun.cpp
llsd.cpp
llsdparam.cpp
llsdserialize.cpp
llsdserialize_xml.cpp
llsdutil.cpp
@ -88,6 +92,7 @@ set(llcommon_SOURCE_FILES
llsingleton.cpp
llstat.cpp
llstacktrace.cpp
llstreamqueue.cpp
llstreamtools.cpp
llstring.cpp
llstringtable.cpp
@ -173,9 +178,12 @@ set(llcommon_HEADER_FILES
llheartbeat.h
llhttpstatuscodes.h
llindexedqueue.h
llinitparam.h
llinstancetracker.h
llkeythrottle.h
lllazy.h
llleap.h
llleaplistener.h
lllistenerwrapper.h
lllinkedqueue.h
llliveappconfig.h
@ -196,7 +204,7 @@ set(llcommon_HEADER_FILES
llpointer.h
llpreprocessor.h
llpriqueuemap.h
llprocesslauncher.h
llprocess.h
llprocessor.h
llptrskiplist.h
llptrskipmap.h
@ -204,10 +212,12 @@ set(llcommon_HEADER_FILES
llqueuedthread.h
llrand.h
llrefcount.h
llregistry.h
llrun.h
llrefcount.h
llsafehandle.h
llsd.h
llsdparam.h
llsdserialize.h
llsdserialize_xml.h
llsdutil.h
@ -216,11 +226,13 @@ set(llcommon_HEADER_FILES
llsingleton.h
llskiplist.h
llskipmap.h
llsortedvector.h
llstack.h
llstacktrace.h
llstat.h
llstatenums.h
llstl.h
llstreamqueue.h
llstreamtools.h
llstrider.h
llstring.h
@ -230,6 +242,7 @@ set(llcommon_HEADER_FILES
llthreadsafequeue.h
lltimer.h
lltreeiterators.h
lltypeinfolookup.h
lluri.h
lluuid.h
lluuidhashmap.h
@ -317,8 +330,7 @@ if (LL_TESTS)
LL_ADD_INTEGRATION_TEST(lllazy "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llprocessor "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llrand "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llsdserialize "" "${test_libs}"
"${PYTHON_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/tests/setpython.py")
LL_ADD_INTEGRATION_TEST(llsdserialize "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llsingleton "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llstring "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(lltreeiterators "" "${test_libs}")
@ -326,6 +338,9 @@ if (LL_TESTS)
LL_ADD_INTEGRATION_TEST(reflection "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(stringize "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(lleventdispatcher "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llprocess "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llleap "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llstreamqueue "" "${test_libs}")
# *TODO - reenable these once tcmalloc libs no longer break the build.
#ADD_BUILD_TEST(llallocator llcommon)

View File

@ -654,9 +654,7 @@ namespace LLError
g.invalidateCallSites();
s.tagLevelMap[tag_name] = level;
}
}
namespace {
LLError::ELevel decodeLevel(std::string name)
{
static LevelMap level_names;
@ -681,7 +679,9 @@ namespace {
return i->second;
}
}
namespace {
void setLevels(LevelMap& map, const LLSD& list, LLError::ELevel level)
{
LLSD::array_const_iterator i, end;

View File

@ -1,4 +1,4 @@
/**
/**
* @file llerrorcontrol.h
* @date December 2006
* @brief error message system control
@ -6,21 +6,21 @@
* $LicenseInfo:firstyear=2007&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@ -38,7 +38,7 @@ class LLSD;
This is the part of the LLError namespace that manages the messages
produced by the logging. The logging support is defined in llerror.h.
Most files do not need to include this.
These implementations are in llerror.cpp.
*/
@ -72,7 +72,7 @@ namespace LLError
Settings that control what is logged.
Setting a level means log messages at that level or above.
*/
LL_COMMON_API void setPrintLocation(bool);
LL_COMMON_API void setDefaultLevel(LLError::ELevel);
LL_COMMON_API ELevel getDefaultLevel();
@ -80,7 +80,8 @@ namespace LLError
LL_COMMON_API void setClassLevel(const std::string& class_name, LLError::ELevel);
LL_COMMON_API void setFileLevel(const std::string& file_name, LLError::ELevel);
LL_COMMON_API void setTagLevel(const std::string& file_name, LLError::ELevel);
LL_COMMON_API LLError::ELevel decodeLevel(std::string name);
LL_COMMON_API void configure(const LLSD&);
// the LLSD can configure all of the settings
// usually read automatically from the live errorlog.xml file
@ -100,31 +101,31 @@ namespace LLError
// (by, for example, setting a class level to LEVEL_NONE), will keep
// the that message from causing the fatal funciton to be invoked.
LL_COMMON_API FatalFunction getFatalFunction();
// Retrieve the previously-set FatalFunction
LL_COMMON_API FatalFunction getFatalFunction();
// Retrieve the previously-set FatalFunction
/// temporarily override the FatalFunction for the duration of a
/// particular scope, e.g. for unit tests
class LL_COMMON_API OverrideFatalFunction
{
public:
OverrideFatalFunction(const FatalFunction& func):
mPrev(getFatalFunction())
{
setFatalFunction(func);
}
~OverrideFatalFunction()
{
setFatalFunction(mPrev);
}
/// temporarily override the FatalFunction for the duration of a
/// particular scope, e.g. for unit tests
class LL_COMMON_API OverrideFatalFunction
{
public:
OverrideFatalFunction(const FatalFunction& func):
mPrev(getFatalFunction())
{
setFatalFunction(func);
}
~OverrideFatalFunction()
{
setFatalFunction(mPrev);
}
private:
FatalFunction mPrev;
};
private:
FatalFunction mPrev;
};
typedef std::string (*TimeFunction)();
LL_COMMON_API std::string utcTime();
LL_COMMON_API void setTimeFunction(TimeFunction);
// The function is use to return the current time, formatted for
// display by those error recorders that want the time included.
@ -136,19 +137,27 @@ namespace LLError
// An object that handles the actual output or error messages.
public:
virtual ~Recorder();
virtual void recordMessage(LLError::ELevel, const std::string& message) = 0;
// use the level for better display, not for filtering
virtual bool wantsTime(); // default returns false
// override and return true if the recorder wants the time string
// included in the text of the message
};
/**
* @NOTE: addRecorder() conveys ownership to the underlying Settings
* object -- when destroyed, it will @em delete the passed Recorder*!
*/
LL_COMMON_API void addRecorder(Recorder*);
/**
* @NOTE: removeRecorder() reclaims ownership of the Recorder*: its
* lifespan becomes the caller's problem.
*/
LL_COMMON_API void removeRecorder(Recorder*);
// each error message is passed to each recorder via recordMessage()
LL_COMMON_API void logToFile(const std::string& filename);
LL_COMMON_API void logToFixedBuffer(LLLineBuffer*);
// Utilities to add recorders for logging to a file or a fixed buffer
@ -166,10 +175,9 @@ namespace LLError
class Settings;
LL_COMMON_API Settings* saveAndResetSettings();
LL_COMMON_API void restoreSettings(Settings *);
LL_COMMON_API std::string abbreviateFile(const std::string& filePath);
LL_COMMON_API int shouldLogCallCount();
};
#endif // LL_LLERRORCONTROL_H

View File

@ -112,13 +112,8 @@ void LLErrorThread::run()
#if !LL_WINDOWS
U32 last_sig_child_count = 0;
#endif
while (1)
while (! (LLApp::isError() || LLApp::isStopped()))
{
if (LLApp::isError() || LLApp::isStopped())
{
// The application has stopped running, time to take action (maybe)
break;
}
#if !LL_WINDOWS
// Check whether or not the main thread had a sig child we haven't handled.
U32 current_sig_child_count = LLApp::getSigChildCount();

View File

@ -1,4 +1,4 @@
/**
/**
* @file llfile.cpp
* @author Michael Schlachter
* @date 2006-03-23
@ -8,60 +8,194 @@
* $LicenseInfo:firstyear=2006&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#if LL_WINDOWS
#include <windows.h>
#include <stdlib.h> // Windows errno
#else
#include <errno.h>
#endif
#include "linden_common.h"
#include "llfile.h"
#include "llstring.h"
#include "llerror.h"
#include "stringize.h"
using namespace std;
static std::string empty;
// Many of the methods below use OS-level functions that mess with errno. Wrap
// variants of strerror() to report errors.
#if LL_WINDOWS
// On Windows, use strerror_s().
std::string strerr(int errn)
{
char buffer[256];
strerror_s(buffer, errn); // infers sizeof(buffer) -- love it!
return buffer;
}
#else
// On Posix we want to call strerror_r(), but alarmingly, there are two
// different variants. The one that returns int always populates the passed
// buffer (except in case of error), whereas the other one always returns a
// valid char* but might or might not populate the passed buffer. How do we
// know which one we're getting? Define adapters for each and let the compiler
// select the applicable adapter.
// strerror_r() returns char*
std::string message_from(int /*orig_errno*/, const char* /*buffer*/, size_t /*bufflen*/,
const char* strerror_ret)
{
return strerror_ret;
}
// strerror_r() returns int
std::string message_from(int orig_errno, const char* buffer, size_t bufflen,
int strerror_ret)
{
if (strerror_ret == 0)
{
return buffer;
}
// Here strerror_r() has set errno. Since strerror_r() has already failed,
// seems like a poor bet to call it again to diagnose its own error...
int stre_errno = errno;
if (stre_errno == ERANGE)
{
return STRINGIZE("strerror_r() can't explain errno " << orig_errno
<< " (" << bufflen << "-byte buffer too small)");
}
if (stre_errno == EINVAL)
{
return STRINGIZE("unknown errno " << orig_errno);
}
// Here we don't even understand the errno from strerror_r()!
return STRINGIZE("strerror_r() can't explain errno " << orig_errno
<< " (error " << stre_errno << ')');
}
std::string strerr(int errn)
{
char buffer[256];
// Select message_from() function matching the strerror_r() we have on hand.
return message_from(errn, buffer, sizeof(buffer),
strerror_r(errn, buffer, sizeof(buffer)));
}
#endif // ! LL_WINDOWS
// On either system, shorthand call just infers global 'errno'.
std::string strerr()
{
return strerr(errno);
}
int warnif(const std::string& desc, const std::string& filename, int rc, int accept=0)
{
if (rc < 0)
{
// Capture errno before we start emitting output
int errn = errno;
// For certain operations, a particular errno value might be
// acceptable -- e.g. stat() could permit ENOENT, mkdir() could permit
// EEXIST. Don't warn if caller explicitly says this errno is okay.
if (errn != accept)
{
LL_WARNS("LLFile") << "Couldn't " << desc << " '" << filename
<< "' (errno " << errn << "): " << strerr(errn) << LL_ENDL;
}
#if 0 && LL_WINDOWS // turn on to debug file-locking problems
// If the problem is "Permission denied," maybe it's because another
// process has the file open. Try to find out.
if (errn == EACCES) // *not* EPERM
{
// Only do any of this stuff (before LL_ENDL) if it will be logged.
LL_DEBUGS("LLFile") << empty;
const char* TEMP = getenv("TEMP");
if (! TEMP)
{
LL_CONT << "No $TEMP, not running 'handle'";
}
else
{
std::string tf(TEMP);
tf += "\\handle.tmp";
// http://technet.microsoft.com/en-us/sysinternals/bb896655
std::string cmd(STRINGIZE("handle \"" << filename
// "openfiles /query /v | fgrep -i \"" << filename
<< "\" > \"" << tf << '"'));
LL_CONT << cmd;
if (system(cmd.c_str()) != 0)
{
LL_CONT << "\nDownload 'handle.exe' from http://technet.microsoft.com/en-us/sysinternals/bb896655";
}
else
{
std::ifstream inf(tf);
std::string line;
while (std::getline(inf, line))
{
LL_CONT << '\n' << line;
}
}
LLFile::remove(tf);
}
LL_CONT << LL_ENDL;
}
#endif // LL_WINDOWS hack to identify processes holding file open
}
return rc;
}
// static
int LLFile::mkdir(const std::string& dirname, int perms)
{
#if LL_WINDOWS
#if LL_WINDOWS
// permissions are ignored on Windows
std::string utf8dirname = dirname;
llutf16string utf16dirname = utf8str_to_utf16str(utf8dirname);
return _wmkdir(utf16dirname.c_str());
int rc = _wmkdir(utf16dirname.c_str());
#else
return ::mkdir(dirname.c_str(), (mode_t)perms);
int rc = ::mkdir(dirname.c_str(), (mode_t)perms);
#endif
// We often use mkdir() to ensure the existence of a directory that might
// already exist. Don't spam the log if it does.
return warnif("mkdir", dirname, rc, EEXIST);
}
// static
int LLFile::rmdir(const std::string& dirname)
{
#if LL_WINDOWS
#if LL_WINDOWS
// permissions are ignored on Windows
std::string utf8dirname = dirname;
llutf16string utf16dirname = utf8str_to_utf16str(utf8dirname);
return _wrmdir(utf16dirname.c_str());
int rc = _wrmdir(utf16dirname.c_str());
#else
return ::rmdir(dirname.c_str());
int rc = ::rmdir(dirname.c_str());
#endif
return warnif("rmdir", dirname, rc);
}
// static
@ -108,10 +242,11 @@ int LLFile::remove(const std::string& filename)
#if LL_WINDOWS
std::string utf8filename = filename;
llutf16string utf16filename = utf8str_to_utf16str(utf8filename);
return _wremove(utf16filename.c_str());
int rc = _wremove(utf16filename.c_str());
#else
return ::remove(filename.c_str());
int rc = ::remove(filename.c_str());
#endif
return warnif("remove", filename, rc);
}
int LLFile::rename(const std::string& filename, const std::string& newname)
@ -121,10 +256,11 @@ int LLFile::rename(const std::string& filename, const std::string& newname)
std::string utf8newname = newname;
llutf16string utf16filename = utf8str_to_utf16str(utf8filename);
llutf16string utf16newname = utf8str_to_utf16str(utf8newname);
return _wrename(utf16filename.c_str(),utf16newname.c_str());
int rc = _wrename(utf16filename.c_str(),utf16newname.c_str());
#else
return ::rename(filename.c_str(),newname.c_str());
int rc = ::rename(filename.c_str(),newname.c_str());
#endif
return warnif(STRINGIZE("rename to '" << newname << "' from"), filename, rc);
}
int LLFile::stat(const std::string& filename, llstat* filestatus)
@ -132,23 +268,26 @@ int LLFile::stat(const std::string& filename, llstat* filestatus)
#if LL_WINDOWS
std::string utf8filename = filename;
llutf16string utf16filename = utf8str_to_utf16str(utf8filename);
return _wstat(utf16filename.c_str(),filestatus);
int rc = _wstat(utf16filename.c_str(),filestatus);
#else
return ::stat(filename.c_str(),filestatus);
int rc = ::stat(filename.c_str(),filestatus);
#endif
// We use stat() to determine existence (see isfile(), isdir()).
// Don't spam the log if the subject pathname doesn't exist.
return warnif("stat", filename, rc, ENOENT);
}
bool LLFile::isdir(const std::string& filename)
{
llstat st;
return stat(filename, &st) == 0 && S_ISDIR(st.st_mode);
}
bool LLFile::isfile(const std::string& filename)
{
llstat st;
return stat(filename, &st) == 0 && S_ISREG(st.st_mode);
}
@ -260,7 +399,7 @@ void llifstream::open(const std::string& _Filename, /* Flawfinder: ignore */
ios_base::openmode _Mode,
int _Prot)
{ // open a C stream with specified mode
LLFILE* filep = LLFile::_Fiopen(_Filename,_Mode | ios_base::in, _Prot);
if(filep == NULL)
{
@ -280,7 +419,7 @@ bool llifstream::is_open() const
return false;
}
llifstream::~llifstream()
{
{
if (_ShouldClose)
{
close();
@ -309,7 +448,7 @@ bool llofstream::is_open() const
void llofstream::open(const std::string& _Filename, /* Flawfinder: ignore */
ios_base::openmode _Mode,
int _Prot)
int _Prot)
{ // open a C stream with specified mode
LLFILE* filep = LLFile::_Fiopen(_Filename,_Mode | ios_base::out, _Prot);
@ -340,14 +479,14 @@ void llofstream::close()
llofstream::llofstream(const std::string& _Filename,
std::ios_base::openmode _Mode,
int _Prot)
int _Prot)
: std::basic_ostream<char,std::char_traits < char > >(NULL,true),_Filebuffer(NULL),_ShouldClose(false)
{ // construct with named file and specified mode
open(_Filename, _Mode , _Prot); /* Flawfinder: ignore */
}
llofstream::~llofstream()
{
{
// destroy the object
if (_ShouldClose)
{

View File

@ -35,6 +35,7 @@
#include <boost/shared_ptr.hpp>
#include "llerror.h"
#include "lltypeinfolookup.h"
namespace LLInitParam
{
@ -205,7 +206,7 @@ namespace LLInitParam
mutable std::string mValueName;
};
class Parser
class LL_COMMON_API Parser
{
LOG_CLASS(Parser);
@ -227,9 +228,9 @@ namespace LLInitParam
typedef bool (*parser_write_func_t)(Parser& parser, const void*, name_stack_t&);
typedef boost::function<void (name_stack_t&, S32, S32, const possible_values_t*)> parser_inspect_func_t;
typedef std::map<const std::type_info*, parser_read_func_t, CompareTypeID> parser_read_func_map_t;
typedef std::map<const std::type_info*, parser_write_func_t, CompareTypeID> parser_write_func_map_t;
typedef std::map<const std::type_info*, parser_inspect_func_t, CompareTypeID> parser_inspect_func_map_t;
typedef LLTypeInfoLookup<parser_read_func_t> parser_read_func_map_t;
typedef LLTypeInfoLookup<parser_write_func_t> parser_write_func_map_t;
typedef LLTypeInfoLookup<parser_inspect_func_t> parser_inspect_func_map_t;
Parser(parser_read_func_map_t& read_map, parser_write_func_map_t& write_map, parser_inspect_func_map_t& inspect_map)
: mParseSilently(false),
@ -301,7 +302,7 @@ namespace LLInitParam
class Param;
// various callbacks and constraints associated with an individual param
struct ParamDescriptor
struct LL_COMMON_API ParamDescriptor
{
struct UserData
{
@ -341,7 +342,7 @@ namespace LLInitParam
typedef boost::shared_ptr<ParamDescriptor> ParamDescriptorPtr;
// each derived Block class keeps a static data structure maintaining offsets to various params
class BlockDescriptor
class LL_COMMON_API BlockDescriptor
{
public:
BlockDescriptor();
@ -369,7 +370,7 @@ namespace LLInitParam
class BaseBlock* mCurrentBlockPtr; // pointer to block currently being constructed
};
class BaseBlock
class LL_COMMON_API BaseBlock
{
public:
//TODO: implement in terms of owned_ptr
@ -566,7 +567,7 @@ namespace LLInitParam
static bool equals(const BaseBlock::Lazy<T>& a, const BaseBlock::Lazy<T>& b) { return !a.empty() || !b.empty(); }
};
class Param
class LL_COMMON_API Param
{
public:
void setProvided(bool is_provided = true)
@ -2062,8 +2063,8 @@ namespace LLInitParam
// block param interface
bool deserializeBlock(Parser& p, Parser::name_stack_range_t name_stack_range, bool new_name);
void serializeBlock(Parser& p, Parser::name_stack_t& name_stack, const BaseBlock* diff_block = NULL) const;
LL_COMMON_API bool deserializeBlock(Parser& p, Parser::name_stack_range_t name_stack_range, bool new_name);
LL_COMMON_API void serializeBlock(Parser& p, Parser::name_stack_t& name_stack, const BaseBlock* diff_block = NULL) const;
bool inspectBlock(Parser& p, Parser::name_stack_t name_stack = Parser::name_stack_t(), S32 min_count = 0, S32 max_count = S32_MAX) const
{
//TODO: implement LLSD params as schema type Any

View File

@ -167,8 +167,9 @@ public:
static T* getInstance(const KEY& k)
{
typename InstanceMap::const_iterator found = getMap_().find(k);
return (found == getMap_().end()) ? NULL : found->second;
const InstanceMap& map(getMap_());
typename InstanceMap::const_iterator found = map.find(k);
return (found == map.end()) ? NULL : found->second;
}
static instance_iter beginInstances()
@ -239,8 +240,20 @@ class LLInstanceTracker<T, T*> : public LLInstanceTrackerBase
public:
/// for completeness of analogy with the generic implementation
static T* getInstance(T* k) { return k; }
/**
* Does a particular instance still exist? Of course, if you already have
* a T* in hand, you need not call getInstance() to @em locate the
* instance -- unlike the case where getInstance() accepts some kind of
* key. Nonetheless this method is still useful to @em validate a
* particular T*, since each instance's destructor removes itself from the
* underlying set.
*/
static T* getInstance(T* k)
{
const InstanceSet& set(getSet_());
typename InstanceSet::const_iterator found = set.find(k);
return (found == set.end())? NULL : *found;
}
static S32 instanceCount() { return getSet_().size(); }
class instance_iter : public boost::iterator_facade<instance_iter, T, boost::forward_traversal_tag>

459
indra/llcommon/llleap.cpp Normal file
View File

@ -0,0 +1,459 @@
/**
* @file llleap.cpp
* @author Nat Goodspeed
* @date 2012-02-20
* @brief Implementation for llleap.
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Copyright (c) 2012, Linden Research, Inc.
* $/LicenseInfo$
*/
// Precompiled header
#include "linden_common.h"
// associated header
#include "llleap.h"
// STL headers
#include <sstream>
#include <algorithm>
// std headers
// external library headers
#include <boost/bind.hpp>
#include <boost/scoped_ptr.hpp>
#include <boost/tokenizer.hpp>
// other Linden headers
#include "llerror.h"
#include "llstring.h"
#include "llprocess.h"
#include "llevents.h"
#include "stringize.h"
#include "llsdutil.h"
#include "llsdserialize.h"
#include "llerrorcontrol.h"
#include "lltimer.h"
#include "lluuid.h"
#include "llleaplistener.h"
#if LL_MSVC
#pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally
#endif
LLLeap::LLLeap() {}
LLLeap::~LLLeap() {}
class LLLeapImpl: public LLLeap
{
LOG_CLASS(LLLeap);
public:
// Called only by LLLeap::create()
LLLeapImpl(const std::string& desc, const std::vector<std::string>& plugin):
// We might reassign mDesc in the constructor body if it's empty here.
mDesc(desc),
// We expect multiple LLLeapImpl instances. Definitely tweak
// mDonePump's name for uniqueness.
mDonePump("LLLeap", true),
// Troubling thought: what if one plugin intentionally messes with
// another plugin? LLEventPump names are in a single global namespace.
// Try to make that more difficult by generating a UUID for the reply-
// pump name -- so it should NOT need tweaking for uniqueness.
mReplyPump(LLUUID::generateNewID().asString()),
mExpect(0),
mPrevFatalFunction(LLError::getFatalFunction()),
// Instantiate a distinct LLLeapListener for this plugin. (Every
// plugin will want its own collection of managed listeners, etc.)
// Pass it a callback to our connect() method, so it can send events
// from a particular LLEventPump to the plugin without having to know
// this class or method name.
mListener(new LLLeapListener(boost::bind(&LLLeapImpl::connect, this, _1, _2)))
{
// Rule out empty vector
if (plugin.empty())
{
throw Error("no plugin command");
}
// Don't leave desc empty either, but in this case, if we weren't
// given one, we'll fake one.
if (desc.empty())
{
mDesc = LLProcess::basename(plugin[0]);
// how about a toLower() variant that returns the transformed string?!
std::string desclower(mDesc);
LLStringUtil::toLower(desclower);
// If we're running a Python script, use the script name for the
// desc instead of just 'python'. Arguably we should check for
// more different interpreters as well, but there's a reason to
// notice Python specially: we provide Python LLSD serialization
// support, so there's a pretty good reason to implement plugins
// in that language.
if (plugin.size() >= 2 && (desclower == "python" || desclower == "python.exe"))
{
mDesc = LLProcess::basename(plugin[1]);
}
}
// Listen for child "termination" right away to catch launch errors.
mDonePump.listen("LLLeap", boost::bind(&LLLeapImpl::bad_launch, this, _1));
// Okay, launch child.
LLProcess::Params params;
params.desc = mDesc;
std::vector<std::string>::const_iterator pi(plugin.begin()), pend(plugin.end());
params.executable = *pi++;
for ( ; pi != pend; ++pi)
{
params.args.add(*pi);
}
params.files.add(LLProcess::FileParam("pipe")); // stdin
params.files.add(LLProcess::FileParam("pipe")); // stdout
params.files.add(LLProcess::FileParam("pipe")); // stderr
params.postend = mDonePump.getName();
mChild = LLProcess::create(params);
// If that didn't work, no point in keeping this LLLeap object.
if (! mChild)
{
throw Error(STRINGIZE("failed to run " << mDesc));
}
// Okay, launch apparently worked. Change our mDonePump listener.
mDonePump.stopListening("LLLeap");
mDonePump.listen("LLLeap", boost::bind(&LLLeapImpl::done, this, _1));
// Child might pump large volumes of data through either stdout or
// stderr. Don't bother copying all that data into notification event.
LLProcess::ReadPipe
&childout(mChild->getReadPipe(LLProcess::STDOUT)),
&childerr(mChild->getReadPipe(LLProcess::STDERR));
childout.setLimit(20);
childerr.setLimit(20);
// Serialize any event received on mReplyPump to our child's stdin.
mStdinConnection = connect(mReplyPump, "LLLeap");
// Listening on stdout is stateful. In general, we're either waiting
// for the length prefix or waiting for the specified length of data.
// We address that with two different listener methods -- one of which
// is blocked at any given time.
mStdoutConnection = childout.getPump()
.listen("prefix", boost::bind(&LLLeapImpl::rstdout, this, _1));
mStdoutDataConnection = childout.getPump()
.listen("data", boost::bind(&LLLeapImpl::rstdoutData, this, _1));
mBlocker.reset(new LLEventPump::Blocker(mStdoutDataConnection));
// Log anything sent up through stderr. When a typical program
// encounters an error, it writes its error message to stderr and
// terminates with nonzero exit code. In particular, the Python
// interpreter behaves that way. More generally, though, a plugin
// author can log whatever s/he wants to the viewer log using stderr.
mStderrConnection = childerr.getPump()
.listen("LLLeap", boost::bind(&LLLeapImpl::rstderr, this, _1));
// For our lifespan, intercept any LL_ERRS so we can notify plugin
LLError::setFatalFunction(boost::bind(&LLLeapImpl::fatalFunction, this, _1));
// Send child a preliminary event reporting our own reply-pump name --
// which would otherwise be pretty tricky to guess!
wstdin(mReplyPump.getName(),
LLSDMap
("command", mListener->getName())
// Include LLLeap features -- this may be important for child to
// construct (or recognize) current protocol.
("features", LLLeapListener::getFeatures()));
}
// Normally we'd expect to arrive here only via done()
virtual ~LLLeapImpl()
{
LL_DEBUGS("LLLeap") << "destroying LLLeap(\"" << mDesc << "\")" << LL_ENDL;
// Restore original FatalFunction
LLError::setFatalFunction(mPrevFatalFunction);
}
// Listener for failed launch attempt
bool bad_launch(const LLSD& data)
{
LL_WARNS("LLLeap") << data["string"].asString() << LL_ENDL;
return false;
}
// Listener for child-process termination
bool done(const LLSD& data)
{
// Log the termination
LL_INFOS("LLLeap") << data["string"].asString() << LL_ENDL;
// Any leftover data at this moment are because protocol was not
// satisfied. Possibly the child was interrupted in the middle of
// sending a message, possibly the child didn't flush stdout before
// terminating, possibly it's just garbage. Log its existence but
// discard it.
LLProcess::ReadPipe& childout(mChild->getReadPipe(LLProcess::STDOUT));
if (childout.size())
{
LLProcess::ReadPipe::size_type
peeklen((std::min)(LLProcess::ReadPipe::size_type(50), childout.size()));
LL_WARNS("LLLeap") << "Discarding final " << childout.size() << " bytes: "
<< childout.peek(0, peeklen) << "..." << LL_ENDL;
}
// Kill this instance. MUST BE LAST before return!
delete this;
return false;
}
// Listener for events on mReplyPump: send to child stdin
bool wstdin(const std::string& pump, const LLSD& data)
{
LLSD packet(LLSDMap("pump", pump)("data", data));
std::ostringstream buffer;
buffer << LLSDNotationStreamer(packet);
/*==========================================================================*|
// DEBUGGING ONLY: don't copy str() if we can avoid it.
std::string strdata(buffer.str());
if (std::size_t(buffer.tellp()) != strdata.length())
{
LL_ERRS("LLLeap") << "tellp() -> " << buffer.tellp() << " != "
<< "str().length() -> " << strdata.length() << LL_ENDL;
}
// DEBUGGING ONLY: reading back is terribly inefficient.
std::istringstream readback(strdata);
LLSD echo;
LLPointer<LLSDParser> parser(new LLSDNotationParser());
S32 parse_status(parser->parse(readback, echo, strdata.length()));
if (parse_status == LLSDParser::PARSE_FAILURE)
{
LL_ERRS("LLLeap") << "LLSDNotationParser() cannot parse output of "
<< "LLSDNotationStreamer()" << LL_ENDL;
}
if (! llsd_equals(echo, packet))
{
LL_ERRS("LLLeap") << "LLSDNotationParser() produced different LLSD "
<< "than passed to LLSDNotationStreamer()" << LL_ENDL;
}
|*==========================================================================*/
LL_DEBUGS("EventHost") << "Sending: " << buffer.tellp() << ':';
std::string::size_type truncate(80);
if (buffer.tellp() <= truncate)
{
LL_CONT << buffer.str();
}
else
{
LL_CONT << buffer.str().substr(0, truncate) << "...";
}
LL_CONT << LL_ENDL;
LLProcess::WritePipe& childin(mChild->getWritePipe(LLProcess::STDIN));
childin.get_ostream() << buffer.tellp() << ':' << buffer.str() << std::flush;
return false;
}
// Initial state of stateful listening on child stdout: wait for a length
// prefix, followed by ':'.
bool rstdout(const LLSD& data)
{
LLProcess::ReadPipe& childout(mChild->getReadPipe(LLProcess::STDOUT));
// It's possible we got notified of a couple digit characters without
// seeing the ':' -- unlikely, but still. Until we see ':', keep
// waiting.
if (childout.contains(':'))
{
std::istream& childstream(childout.get_istream());
// Saw ':', read length prefix and store in mExpect.
size_t expect;
childstream >> expect;
int colon(childstream.get());
if (colon != ':')
{
// Protocol failure. Clear out the rest of the pending data in
// childout (well, up to a max length) to log what was wrong.
LLProcess::ReadPipe::size_type
readlen((std::min)(childout.size(), LLProcess::ReadPipe::size_type(80)));
bad_protocol(STRINGIZE(expect << char(colon) << childout.read(readlen)));
}
else
{
// Saw length prefix, saw colon, life is good. Now wait for
// that length of data to arrive.
mExpect = expect;
LL_DEBUGS("LLLeap") << "got length, waiting for "
<< mExpect << " bytes of data" << LL_ENDL;
// Block calls to this method; resetting mBlocker unblocks
// calls to the other method.
mBlocker.reset(new LLEventPump::Blocker(mStdoutConnection));
// Go check if we've already received all the advertised data.
if (childout.size())
{
LLSD updata(data);
updata["len"] = LLSD::Integer(childout.size());
rstdoutData(updata);
}
}
}
else if (childout.contains('\n'))
{
// Since this is the initial listening state, this is where we'd
// arrive if the child isn't following protocol at all -- say
// because the user specified 'ls' or some darn thing.
bad_protocol(childout.getline());
}
return false;
}
// State in which we listen on stdout for the specified length of data to
// arrive.
bool rstdoutData(const LLSD& data)
{
LLProcess::ReadPipe& childout(mChild->getReadPipe(LLProcess::STDOUT));
// Until we've accumulated the promised length of data, keep waiting.
if (childout.size() >= mExpect)
{
// Ready to rock and roll.
LL_DEBUGS("LLLeap") << "needed " << mExpect << " bytes, got "
<< childout.size() << ", parsing LLSD" << LL_ENDL;
LLSD data;
LLPointer<LLSDParser> parser(new LLSDNotationParser());
S32 parse_status(parser->parse(childout.get_istream(), data, mExpect));
if (parse_status == LLSDParser::PARSE_FAILURE)
{
bad_protocol("unparseable LLSD data");
}
else if (! (data.isMap() && data["pump"].isString() && data.has("data")))
{
// we got an LLSD object, but it lacks required keys
bad_protocol("missing 'pump' or 'data'");
}
else
{
// The LLSD object we got from our stream contains the keys we
// need.
LLEventPumps::instance().obtain(data["pump"]).post(data["data"]);
// Block calls to this method; resetting mBlocker unblocks calls
// to the other method.
mBlocker.reset(new LLEventPump::Blocker(mStdoutDataConnection));
// Go check for any more pending events in the buffer.
if (childout.size())
{
LLSD updata(data);
data["len"] = LLSD::Integer(childout.size());
rstdout(updata);
}
}
}
return false;
}
void bad_protocol(const std::string& data)
{
LL_WARNS("LLLeap") << mDesc << ": invalid protocol: " << data << LL_ENDL;
// No point in continuing to run this child.
mChild->kill();
}
// Listen on child stderr and log everything that arrives
bool rstderr(const LLSD& data)
{
LLProcess::ReadPipe& childerr(mChild->getReadPipe(LLProcess::STDERR));
// We might have gotten a notification involving only a partial line
// -- or multiple lines. Read all complete lines; stop when there's
// only a partial line left.
while (childerr.contains('\n'))
{
// DO NOT make calls with side effects in a logging statement! If
// that log level is suppressed, your side effects WON'T HAPPEN.
std::string line(childerr.getline());
// Log the received line. Prefix it with the desc so we know which
// plugin it's from. This method name rstderr() is intentionally
// chosen to further qualify the log output.
LL_INFOS("LLLeap") << mDesc << ": " << line << LL_ENDL;
}
// What if child writes a final partial line to stderr?
if (data["eof"].asBoolean() && childerr.size())
{
std::string rest(childerr.read(childerr.size()));
// Read all remaining bytes and log.
LL_INFOS("LLLeap") << mDesc << ": " << rest << LL_ENDL;
}
return false;
}
void fatalFunction(const std::string& error)
{
// Notify plugin
LLSD event;
event["type"] = "error";
event["error"] = error;
mReplyPump.post(event);
// All the above really accomplished was to buffer the serialized
// event in our WritePipe. Have to pump mainloop a couple times to
// really write it out there... but time out in case we can't write.
LLProcess::WritePipe& childin(mChild->getWritePipe(LLProcess::STDIN));
LLEventPump& mainloop(LLEventPumps::instance().obtain("mainloop"));
LLSD nop;
F64 until(LLTimer::getElapsedSeconds() + 2);
while (childin.size() && LLTimer::getElapsedSeconds() < until)
{
mainloop.post(nop);
}
// forward the call to the previous FatalFunction
mPrevFatalFunction(error);
}
private:
/// We always want to listen on mReplyPump with wstdin(); under some
/// circumstances we'll also echo other LLEventPumps to the plugin.
LLBoundListener connect(LLEventPump& pump, const std::string& listener)
{
// Serialize any event received on the specified LLEventPump to our
// child's stdin, suitably enriched with the pump name on which it was
// received.
return pump.listen(listener,
boost::bind(&LLLeapImpl::wstdin, this, pump.getName(), _1));
}
std::string mDesc;
LLEventStream mDonePump;
LLEventStream mReplyPump;
LLProcessPtr mChild;
LLTempBoundListener
mStdinConnection, mStdoutConnection, mStdoutDataConnection, mStderrConnection;
boost::scoped_ptr<LLEventPump::Blocker> mBlocker;
LLProcess::ReadPipe::size_type mExpect;
LLError::FatalFunction mPrevFatalFunction;
boost::scoped_ptr<LLLeapListener> mListener;
};
// This must follow the declaration of LLLeapImpl, so it may as well be last.
LLLeap* LLLeap::create(const std::string& desc, const std::vector<std::string>& plugin, bool exc)
{
// If caller is willing to permit exceptions, just instantiate.
if (exc)
return new LLLeapImpl(desc, plugin);
// Caller insists on suppressing LLLeap::Error. Very well, catch it.
try
{
return new LLLeapImpl(desc, plugin);
}
catch (const LLLeap::Error&)
{
return NULL;
}
}
LLLeap* LLLeap::create(const std::string& desc, const std::string& plugin, bool exc)
{
// Use LLStringUtil::getTokens() to parse the command line
return create(desc,
LLStringUtil::getTokens(plugin,
" \t\r\n", // drop_delims
"", // no keep_delims
"\"'", // either kind of quotes
"\\"), // backslash escape
exc);
}

80
indra/llcommon/llleap.h Normal file
View File

@ -0,0 +1,80 @@
/**
* @file llleap.h
* @author Nat Goodspeed
* @date 2012-02-20
* @brief Class that implements "LLSD Event API Plugin"
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Copyright (c) 2012, Linden Research, Inc.
* $/LicenseInfo$
*/
#if ! defined(LL_LLLEAP_H)
#define LL_LLLEAP_H
#include "llinstancetracker.h"
#include <string>
#include <vector>
#include <stdexcept>
/**
* LLSD Event API Plugin class. Because instances are managed by
* LLInstanceTracker, you can instantiate LLLeap and forget the instance
* unless you need it later. Each instance manages an LLProcess; when the
* child process terminates, LLLeap deletes itself. We don't require a unique
* LLInstanceTracker key.
*
* The fact that a given LLLeap instance vanishes when its child process
* terminates makes it problematic to store an LLLeap* anywhere. Any stored
* LLLeap* pointer should be validated before use by
* LLLeap::getInstance(LLLeap*) (see LLInstanceTracker).
*/
class LL_COMMON_API LLLeap: public LLInstanceTracker<LLLeap>
{
public:
/**
* Pass a brief string description, mostly for logging purposes. The desc
* need not be unique, but obviously the clearer we can make it, the
* easier these things will be to debug. The strings are the command line
* used to launch the desired plugin process.
*
* Pass exc=false to suppress LLLeap::Error exception. Obviously in that
* case the caller cannot discover the nature of the error, merely that an
* error of some kind occurred (because create() returned NULL). Either
* way, the error is logged.
*/
static LLLeap* create(const std::string& desc, const std::vector<std::string>& plugin,
bool exc=true);
/**
* Pass a brief string description, mostly for logging purposes. The desc
* need not be unique, but obviously the clearer we can make it, the
* easier these things will be to debug. Pass a command-line string
* to launch the desired plugin process.
*
* Pass exc=false to suppress LLLeap::Error exception. Obviously in that
* case the caller cannot discover the nature of the error, merely that an
* error of some kind occurred (because create() returned NULL). Either
* way, the error is logged.
*/
static LLLeap* create(const std::string& desc, const std::string& plugin,
bool exc=true);
/**
* Exception thrown for invalid create() arguments, e.g. no plugin
* program. This is more resiliant than an LL_ERRS failure, because the
* string(s) passed to create() might come from an external source. This
* way the caller can catch LLLeap::Error and try to recover.
*/
struct Error: public std::runtime_error
{
Error(const std::string& what): std::runtime_error(what) {}
};
virtual ~LLLeap();
protected:
LLLeap();
};
#endif /* ! defined(LL_LLLEAP_H) */

View File

@ -0,0 +1,287 @@
/**
* @file llleaplistener.cpp
* @author Nat Goodspeed
* @date 2012-03-16
* @brief Implementation for llleaplistener.
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Copyright (c) 2012, Linden Research, Inc.
* $/LicenseInfo$
*/
// Precompiled header
#include "linden_common.h"
// associated header
#include "llleaplistener.h"
// STL headers
// std headers
// external library headers
#include <boost/foreach.hpp>
// other Linden headers
#include "lluuid.h"
#include "llsdutil.h"
#include "stringize.h"
/*****************************************************************************
* LEAP FEATURE STRINGS
*****************************************************************************/
/**
* Implement "getFeatures" command. The LLSD map thus obtained is intended to
* be machine-readable (read: easily-parsed, if parsing be necessary) and to
* highlight the differences between this version of the LEAP protocol and
* the baseline version. A client may thus determine whether or not the
* running viewer supports some recent feature of interest.
*
* This method is defined at the top of this implementation file so it's easy
* to find, easy to spot, easy to update as we enhance the LEAP protocol.
*/
/*static*/ LLSD LLLeapListener::getFeatures()
{
static LLSD features;
if (features.isUndefined())
{
features = LLSD::emptyMap();
// This initial implementation IS the baseline LEAP protocol; thus the
// set of differences is empty; thus features is initially empty.
// features["featurename"] = "value";
}
return features;
}
LLLeapListener::LLLeapListener(const ConnectFunc& connect):
// Each LEAP plugin has an instance of this listener. Make the command
// pump name difficult for other such plugins to guess.
LLEventAPI(LLUUID::generateNewID().asString(),
"Operations relating to the LLSD Event API Plugin (LEAP) protocol"),
mConnect(connect)
{
LLSD need_name(LLSDMap("name", LLSD()));
add("newpump",
"Instantiate a new LLEventPump named like [\"name\"] and listen to it.\n"
"If [\"type\"] == \"LLEventQueue\", make LLEventQueue, else LLEventStream.\n"
"Events sent through new LLEventPump will be decorated with [\"pump\"]=name.\n"
"Returns actual name in [\"name\"] (may be different if collision).",
&LLLeapListener::newpump,
need_name);
add("killpump",
"Delete LLEventPump [\"name\"] created by \"newpump\".\n"
"Returns [\"status\"] boolean indicating whether such a pump existed.",
&LLLeapListener::killpump,
need_name);
LLSD need_source_listener(LLSDMap("source", LLSD())("listener", LLSD()));
add("listen",
"Listen to an existing LLEventPump named [\"source\"], with listener name\n"
"[\"listener\"].\n"
"By default, send events on [\"source\"] to the plugin, decorated\n"
"with [\"pump\"]=[\"source\"].\n"
"If [\"dest\"] specified, send undecorated events on [\"source\"] to the\n"
"LLEventPump named [\"dest\"].\n"
"Returns [\"status\"] boolean indicating whether the connection was made.",
&LLLeapListener::listen,
need_source_listener);
add("stoplistening",
"Disconnect a connection previously established by \"listen\".\n"
"Pass same [\"source\"] and [\"listener\"] arguments.\n"
"Returns [\"status\"] boolean indicating whether such a listener existed.",
&LLLeapListener::stoplistening,
need_source_listener);
add("ping",
"No arguments, just a round-trip sanity check.",
&LLLeapListener::ping);
add("getAPIs",
"Enumerate all LLEventAPI instances by name and description.",
&LLLeapListener::getAPIs);
add("getAPI",
"Get name, description, dispatch key and operations for LLEventAPI [\"api\"].",
&LLLeapListener::getAPI,
LLSD().with("api", LLSD()));
add("getFeatures",
"Return an LLSD map of feature strings (deltas from baseline LEAP protocol)",
static_cast<void (LLLeapListener::*)(const LLSD&) const>(&LLLeapListener::getFeatures));
add("getFeature",
"Return the feature value with key [\"feature\"]",
&LLLeapListener::getFeature,
LLSD().with("feature", LLSD()));
}
LLLeapListener::~LLLeapListener()
{
// We'd have stored a map of LLTempBoundListener instances, save that the
// operation of inserting into a std::map necessarily copies the
// value_type, and Bad Things would happen if you copied an
// LLTempBoundListener. (Destruction of the original would disconnect the
// listener, invalidating every stored connection.)
BOOST_FOREACH(ListenersMap::value_type& pair, mListeners)
{
pair.second.disconnect();
}
}
void LLLeapListener::newpump(const LLSD& request)
{
Response reply(LLSD(), request);
std::string name = request["name"];
LLSD const & type = request["type"];
LLEventPump * new_pump = NULL;
if (type.asString() == "LLEventQueue")
{
new_pump = new LLEventQueue(name, true); // tweak name for uniqueness
}
else
{
if (! (type.isUndefined() || type.asString() == "LLEventStream"))
{
reply.warn(STRINGIZE("unknown 'type' " << type << ", using LLEventStream"));
}
new_pump = new LLEventStream(name, true); // tweak name for uniqueness
}
name = new_pump->getName();
mEventPumps.insert(name, new_pump);
// Now listen on this new pump with our plugin listener
std::string myname("llleap");
saveListener(name, myname, mConnect(*new_pump, myname));
reply["name"] = name;
}
void LLLeapListener::killpump(const LLSD& request)
{
Response reply(LLSD(), request);
std::string name = request["name"];
// success == (nonzero number of entries were erased)
reply["status"] = bool(mEventPumps.erase(name));
}
void LLLeapListener::listen(const LLSD& request)
{
Response reply(LLSD(), request);
std::string source_name = request["source"];
std::string dest_name = request["dest"];
std::string listener_name = request["listener"];
LLEventPump & source = LLEventPumps::instance().obtain(source_name);
reply["status"] = false;
if (mListeners.find(ListenersMap::key_type(source_name, listener_name)) == mListeners.end())
{
try
{
if (request["dest"].isDefined())
{
// If we're asked to connect the "source" pump to a
// specific "dest" pump, find dest pump and connect it.
LLEventPump & dest = LLEventPumps::instance().obtain(dest_name);
saveListener(source_name, listener_name,
source.listen(listener_name,
boost::bind(&LLEventPump::post, &dest, _1)));
}
else
{
// "dest" unspecified means to direct events on "source"
// to our plugin listener.
saveListener(source_name, listener_name, mConnect(source, listener_name));
}
reply["status"] = true;
}
catch (const LLEventPump::DupListenerName &)
{
// pass - status already set to false
}
}
}
void LLLeapListener::stoplistening(const LLSD& request)
{
Response reply(LLSD(), request);
std::string source_name = request["source"];
std::string listener_name = request["listener"];
ListenersMap::iterator finder =
mListeners.find(ListenersMap::key_type(source_name, listener_name));
reply["status"] = false;
if(finder != mListeners.end())
{
reply["status"] = true;
finder->second.disconnect();
mListeners.erase(finder);
}
}
void LLLeapListener::ping(const LLSD& request) const
{
// do nothing, default reply suffices
Response(LLSD(), request);
}
void LLLeapListener::getAPIs(const LLSD& request) const
{
Response reply(LLSD(), request);
for (LLEventAPI::instance_iter eai(LLEventAPI::beginInstances()),
eaend(LLEventAPI::endInstances());
eai != eaend; ++eai)
{
LLSD info;
info["desc"] = eai->getDesc();
reply[eai->getName()] = info;
}
}
void LLLeapListener::getAPI(const LLSD& request) const
{
Response reply(LLSD(), request);
LLEventAPI* found = LLEventAPI::getInstance(request["api"]);
if (found)
{
reply["name"] = found->getName();
reply["desc"] = found->getDesc();
reply["key"] = found->getDispatchKey();
LLSD ops;
for (LLEventAPI::const_iterator oi(found->begin()), oend(found->end());
oi != oend; ++oi)
{
ops.append(found->getMetadata(oi->first));
}
reply["ops"] = ops;
}
}
void LLLeapListener::getFeatures(const LLSD& request) const
{
// Merely constructing and destroying a Response object suffices here.
// Giving it a name would only produce fatal 'unreferenced variable'
// warnings.
Response(getFeatures(), request);
}
void LLLeapListener::getFeature(const LLSD& request) const
{
Response reply(LLSD(), request);
LLSD::String feature_name(request["feature"]);
LLSD features(getFeatures());
if (features[feature_name].isDefined())
{
reply["feature"] = features[feature_name];
}
}
void LLLeapListener::saveListener(const std::string& pump_name,
const std::string& listener_name,
const LLBoundListener& listener)
{
mListeners.insert(ListenersMap::value_type(ListenersMap::key_type(pump_name, listener_name),
listener));
}

View File

@ -0,0 +1,73 @@
/**
* @file llleaplistener.h
* @author Nat Goodspeed
* @date 2012-03-16
* @brief LLEventAPI supporting LEAP plugins
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Copyright (c) 2012, Linden Research, Inc.
* $/LicenseInfo$
*/
#if ! defined(LL_LLLEAPLISTENER_H)
#define LL_LLLEAPLISTENER_H
#include "lleventapi.h"
#include <map>
#include <string>
#include <boost/function.hpp>
#include <boost/ptr_container/ptr_map.hpp>
/// Listener class implementing LLLeap query/control operations.
/// See https://jira.lindenlab.com/jira/browse/DEV-31978.
class LLLeapListener: public LLEventAPI
{
public:
/**
* Decouple LLLeap by dependency injection. Certain LLLeapListener
* operations must be able to cause LLLeap to listen on a specified
* LLEventPump with the LLLeap listener that wraps incoming events in an
* outer (pump=, data=) map and forwards them to the plugin. Very well,
* define the signature for a function that will perform that, and make
* our constructor accept such a function.
*/
typedef boost::function<LLBoundListener(LLEventPump&, const std::string& listener)>
ConnectFunc;
LLLeapListener(const ConnectFunc& connect);
~LLLeapListener();
static LLSD getFeatures();
private:
void newpump(const LLSD&);
void killpump(const LLSD&);
void listen(const LLSD&);
void stoplistening(const LLSD&);
void ping(const LLSD&) const;
void getAPIs(const LLSD&) const;
void getAPI(const LLSD&) const;
void getFeatures(const LLSD&) const;
void getFeature(const LLSD&) const;
void saveListener(const std::string& pump_name, const std::string& listener_name,
const LLBoundListener& listener);
ConnectFunc mConnect;
// In theory, listen() could simply call the relevant LLEventPump's
// listen() method, stoplistening() likewise. Lifespan issues make us
// capture the LLBoundListener objects: when this object goes away, all
// those listeners should be disconnected. But what if the client listens,
// stops, listens again on the same LLEventPump with the same listener
// name? Merely collecting LLBoundListeners wouldn't adequately track
// that. So capture the latest LLBoundListener for this LLEventPump name
// and listener name.
typedef std::map<std::pair<std::string, std::string>, LLBoundListener> ListenersMap;
ListenersMap mListeners;
// Similar lifespan reasoning applies to LLEventPumps instantiated by
// newpump() operations.
typedef boost::ptr_map<std::string, LLEventPump> EventPumpsMap;
EventPumpsMap mEventPumps;
};
#endif /* ! defined(LL_LLLEAPLISTENER_H) */

1351
indra/llcommon/llprocess.cpp Normal file

File diff suppressed because it is too large Load Diff

553
indra/llcommon/llprocess.h Normal file
View File

@ -0,0 +1,553 @@
/**
* @file llprocess.h
* @brief Utility class for launching, terminating, and tracking child processes.
*
* $LicenseInfo:firstyear=2008&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef LL_LLPROCESS_H
#define LL_LLPROCESS_H
#include "llinitparam.h"
#include "llsdparam.h"
#include "apr_thread_proc.h"
#include <boost/shared_ptr.hpp>
#include <boost/ptr_container/ptr_vector.hpp>
#include <boost/optional.hpp>
#include <boost/noncopyable.hpp>
#include <iosfwd> // std::ostream
#include <stdexcept>
#if LL_WINDOWS
#define WIN32_LEAN_AND_MEAN
#include <windows.h> // HANDLE (eye roll)
#elif LL_LINUX
#if defined(Status)
#undef Status
#endif
#endif
class LLEventPump;
class LLProcess;
/// LLProcess instances are created on the heap by static factory methods and
/// managed by ref-counted pointers.
typedef boost::shared_ptr<LLProcess> LLProcessPtr;
/**
* LLProcess handles launching an external process with specified command line
* arguments. It also keeps track of whether the process is still running, and
* can kill it if required.
*
* In discussing LLProcess, we use the term "parent" to refer to this process
* (the process invoking LLProcess), versus "child" to refer to the process
* spawned by LLProcess.
*
* LLProcess relies on periodic post() calls on the "mainloop" LLEventPump: an
* LLProcess object's Status won't update until the next "mainloop" tick. For
* instance, the Second Life viewer's main loop already posts to an
* LLEventPump by that name once per iteration. See
* indra/llcommon/tests/llprocess_test.cpp for an example of waiting for
* child-process termination in a standalone test context.
*/
class LL_COMMON_API LLProcess: public boost::noncopyable
{
LOG_CLASS(LLProcess);
public:
/**
* Specify what to pass for each of child stdin, stdout, stderr.
* @see LLProcess::Params::files.
*/
struct FileParam: public LLInitParam::Block<FileParam>
{
/**
* type of file handle to pass to child process
*
* - "" (default): let the child inherit the same file handle used by
* this process. For instance, if passed as stdout, child stdout
* will be interleaved with stdout from this process. In this case,
* @a name is moot and should be left "".
*
* - "file": open an OS filesystem file with the specified @a name.
* <i>Not yet implemented.</i>
*
* - "pipe" or "tpipe" or "npipe": depends on @a name
*
* - @a name.empty(): construct an OS pipe used only for this slot
* of the forthcoming child process.
*
* - ! @a name.empty(): in a global registry, find or create (using
* the specified @a name) an OS pipe. The point of the (purely
* internal) @a name is that passing the same @a name in more than
* one slot for a given LLProcess -- or for slots in different
* LLProcess instances -- means the same pipe. For example, you
* might pass the same @a name value as both stdout and stderr to
* make the child process produce both on the same actual pipe. Or
* you might pass the same @a name as the stdout for one LLProcess
* and the stdin for another to connect the two child processes.
* Use LLProcess::getPipeName() to generate a unique name
* guaranteed not to already exist in the registry. <i>Not yet
* implemented.</i>
*
* The difference between "pipe", "tpipe" and "npipe" is as follows.
*
* - "pipe": direct LLProcess to monitor the parent end of the pipe,
* pumping nonblocking I/O every frame. The expectation (at least
* for stdout or stderr) is that the caller will listen for
* incoming data and consume it as it arrives. It's important not
* to neglect such a pipe, because it's buffered in memory. If you
* suspect the child may produce a great volume of output between
* frames, consider directing the child to write to a filesystem
* file instead, then read the file later.
*
* - "tpipe": do not engage LLProcess machinery to monitor the
* parent end of the pipe. A "tpipe" is used only to connect
* different child processes. As such, it makes little sense to
* pass an empty @a name. <i>Not yet implemented.</i>
*
* - "npipe": like "tpipe", but use an OS named pipe with a
* generated name. Note that @a name is the @em internal name of
* the pipe in our global registry -- it doesn't necessarily have
* anything to do with the pipe's name in the OS filesystem. Use
* LLProcess::getPipeName() to obtain the named pipe's OS
* filesystem name, e.g. to pass it as the @a name to another
* LLProcess instance using @a type "file". This supports usage
* like bash's &lt;(subcommand...) or &gt;(subcommand...)
* constructs. <i>Not yet implemented.</i>
*
* In all cases the open mode (read, write) is determined by the child
* slot you're filling. Child stdin means select the "read" end of a
* pipe, or open a filesystem file for reading; child stdout or stderr
* means select the "write" end of a pipe, or open a filesystem file
* for writing.
*
* Confusion such as passing the same pipe as the stdin of two
* processes (rather than stdout for one and stdin for the other) is
* explicitly permitted: it's up to the caller to construct meaningful
* LLProcess pipe graphs.
*/
Optional<std::string> type;
Optional<std::string> name;
FileParam(const std::string& tp="", const std::string& nm=""):
type("type"),
name("name")
{
// If caller wants to specify values, use explicit assignment to
// set them rather than initialization.
if (! tp.empty()) type = tp;
if (! nm.empty()) name = nm;
}
};
/// Param block definition
struct Params: public LLInitParam::Block<Params>
{
Params():
executable("executable"),
args("args"),
cwd("cwd"),
autokill("autokill", true),
files("files"),
postend("postend"),
desc("desc")
{}
/// pathname of executable
Mandatory<std::string> executable;
/**
* zero or more additional command-line arguments. Arguments are
* passed through as exactly as we can manage, whitespace and all.
* @note On Windows we manage this by implicitly double-quoting each
* argument while assembling the command line.
*/
Multiple<std::string> args;
/// current working directory, if need it changed
Optional<std::string> cwd;
/// implicitly kill process on destruction of LLProcess object
/// (default true)
Optional<bool> autokill;
/**
* Up to three FileParam items: for child stdin, stdout, stderr.
* Passing two FileParam entries means default treatment for stderr,
* and so forth.
*
* @note LLInitParam::Block permits usage like this:
* @code
* LLProcess::Params params;
* ...
* params.files
* .add(LLProcess::FileParam()) // stdin
* .add(LLProcess::FileParam().type("pipe") // stdout
* .add(LLProcess::FileParam().type("file").name("error.log"));
* @endcode
*
* @note While it's theoretically plausible to pass additional open
* file handles to a child specifically written to expect them, our
* underlying implementation doesn't yet support that.
*/
Multiple<FileParam, AtMost<3> > files;
/**
* On child-process termination, if this LLProcess object still
* exists, post LLSD event to LLEventPump with specified name (default
* no event). Event contains at least:
*
* - "id" as obtained from getProcessID()
* - "desc" short string description of child (executable + pid)
* - "state" @c state enum value, from Status.mState
* - "data" if "state" is EXITED, exit code; if KILLED, on Posix,
* signal number
* - "string" English text describing "state" and "data" (e.g. "exited
* with code 0")
*/
Optional<std::string> postend;
/**
* Description of child process for logging purposes. It need not be
* unique; the logged description string will contain the PID as well.
* If this is omitted, a description will be derived from the
* executable name.
*/
Optional<std::string> desc;
};
typedef LLSDParamAdapter<Params> LLSDOrParams;
/**
* Factory accepting either plain LLSD::Map or Params block.
* MAY RETURN DEFAULT-CONSTRUCTED LLProcessPtr if params invalid!
*/
static LLProcessPtr create(const LLSDOrParams& params);
virtual ~LLProcess();
/// Is child process still running?
bool isRunning() const;
// static isRunning(LLProcessPtr), getStatus(LLProcessPtr),
// getStatusString(LLProcessPtr), kill(LLProcessPtr) handle the case in
// which the passed LLProcessPtr might be NULL (default-constructed).
static bool isRunning(const LLProcessPtr&);
/**
* State of child process
*/
enum state
{
UNSTARTED, ///< initial value, invisible to consumer
RUNNING, ///< child process launched
EXITED, ///< child process terminated voluntarily
KILLED ///< child process terminated involuntarily
};
/**
* Status info
*/
struct Status
{
Status():
mState(UNSTARTED),
mData(0)
{}
state mState; ///< @see state
/**
* - for mState == EXITED: mData is exit() code
* - for mState == KILLED: mData is signal number (Posix)
* - otherwise: mData is undefined
*/
int mData;
};
/// Status query
Status getStatus() const;
static Status getStatus(const LLProcessPtr&);
/// English Status string query, for logging etc.
std::string getStatusString() const;
static std::string getStatusString(const std::string& desc, const LLProcessPtr&);
/// English Status string query for previously-captured Status
std::string getStatusString(const Status& status) const;
/// static English Status string query
static std::string getStatusString(const std::string& desc, const Status& status);
// Attempt to kill the process -- returns true if the process is no longer running when it returns.
// Note that even if this returns false, the process may exit some time after it's called.
bool kill(const std::string& who="");
static bool kill(const LLProcessPtr& p, const std::string& who="");
#if LL_WINDOWS
typedef int id; ///< as returned by getProcessID()
typedef HANDLE handle; ///< as returned by getProcessHandle()
#else
typedef pid_t id;
typedef pid_t handle;
#endif
/**
* Get an int-like id value. This is primarily intended for a human reader
* to differentiate processes.
*/
id getProcessID() const;
/**
* Get a "handle" of a kind that you might pass to platform-specific API
* functions to engage features not directly supported by LLProcess.
*/
handle getProcessHandle() const;
/**
* Test if a process (@c handle obtained from getProcessHandle()) is still
* running. Return same nonzero @c handle value if still running, else
* zero, so you can test it like a bool. But if you want to update a
* stored variable as a side effect, you can write code like this:
* @code
* hchild = LLProcess::isRunning(hchild);
* @endcode
* @note This method is intended as a unit-test hook, not as the first of
* a whole set of operations supported on freestanding @c handle values.
* New functionality should be added as nonstatic members operating on
* the same data as getProcessHandle().
*
* In particular, if child termination is detected by this static isRunning()
* rather than by nonstatic isRunning(), the LLProcess object won't be
* aware of the child's changed status and may encounter OS errors trying
* to obtain it. This static isRunning() is only intended for after the
* launching LLProcess object has been destroyed.
*/
static handle isRunning(handle, const std::string& desc="");
/// Provide symbolic access to child's file slots
enum FILESLOT { STDIN=0, STDOUT=1, STDERR=2, NSLOTS=3 };
/**
* For a pipe constructed with @a type "npipe", obtain the generated OS
* filesystem name for the specified pipe. Otherwise returns the empty
* string. @see LLProcess::FileParam::type
*/
std::string getPipeName(FILESLOT) const;
/// base of ReadPipe, WritePipe
class LL_COMMON_API BasePipe
{
public:
virtual ~BasePipe() = 0;
typedef std::size_t size_type;
static const size_type npos;
/**
* Get accumulated buffer length.
*
* For WritePipe, is there still pending data to send to child?
*
* For ReadPipe, we often need to refrain from actually reading the
* std::istream returned by get_istream() until we've accumulated
* enough data to make it worthwhile. For instance, if we're expecting
* a number from the child, but the child happens to flush "12" before
* emitting "3\n", get_istream() >> myint could return 12 rather than
* 123!
*/
virtual size_type size() const = 0;
};
/// As returned by getWritePipe() or getOptWritePipe()
class WritePipe: public BasePipe
{
public:
/**
* Get ostream& on which to write to child's stdin.
*
* @usage
* @code
* myProcess->getWritePipe().get_ostream() << "Hello, child!" << std::endl;
* @endcode
*/
virtual std::ostream& get_ostream() = 0;
};
/// As returned by getReadPipe() or getOptReadPipe()
class ReadPipe: public BasePipe
{
public:
/**
* Get istream& on which to read from child's stdout or stderr.
*
* @usage
* @code
* std::string stuff;
* myProcess->getReadPipe().get_istream() >> stuff;
* @endcode
*
* You should be sure in advance that the ReadPipe in question can
* fill the request. @see getPump()
*/
virtual std::istream& get_istream() = 0;
/**
* Like std::getline(get_istream(), line), but trims off trailing '\r'
* to make calling code less platform-sensitive.
*/
virtual std::string getline() = 0;
/**
* Like get_istream().read(buffer, n), but returns std::string rather
* than requiring caller to construct a buffer, etc.
*/
virtual std::string read(size_type len) = 0;
/**
* Peek at accumulated buffer data without consuming it. Optional
* parameters give you substr() functionality.
*
* @note You can discard buffer data using get_istream().ignore(n).
*/
virtual std::string peek(size_type offset=0, size_type len=npos) const = 0;
/**
* Detect presence of a substring (or char) in accumulated buffer data
* without retrieving it. Optional offset allows you to search from
* specified position.
*/
template <typename SEEK>
bool contains(SEEK seek, size_type offset=0) const
{ return find(seek, offset) != npos; }
/**
* Search for a substring in accumulated buffer data without
* retrieving it. Returns size_type position at which found, or npos
* meaning not found. Optional offset allows you to search from
* specified position.
*/
virtual size_type find(const std::string& seek, size_type offset=0) const = 0;
/**
* Search for a char in accumulated buffer data without retrieving it.
* Returns size_type position at which found, or npos meaning not
* found. Optional offset allows you to search from specified
* position.
*/
virtual size_type find(char seek, size_type offset=0) const = 0;
/**
* Get LLEventPump& on which to listen for incoming data. The posted
* LLSD::Map event will contain:
*
* - "data" part of pending data; see setLimit()
* - "len" entire length of pending data, regardless of setLimit()
* - "slot" this ReadPipe's FILESLOT, e.g. LLProcess::STDOUT
* - "name" e.g. "stdout"
* - "desc" e.g. "SLPlugin (pid) stdout"
* - "eof" @c true means there no more data will arrive on this pipe,
* therefore no more events on this pump
*
* If the child sends "abc", and this ReadPipe posts "data"="abc", but
* you don't consume it by reading the std::istream returned by
* get_istream(), and the child next sends "def", ReadPipe will post
* "data"="abcdef".
*/
virtual LLEventPump& getPump() = 0;
/**
* Set maximum length of buffer data that will be posted in the LLSD
* announcing arrival of new data from the child. If you call
* setLimit(5), and the child sends "abcdef", the LLSD event will
* contain "data"="abcde". However, you may still read the entire
* "abcdef" from get_istream(): this limit affects only the size of
* the data posted with the LLSD event. If you don't call this method,
* @em no data will be posted: the default is 0 bytes.
*/
virtual void setLimit(size_type limit) = 0;
/**
* Query the current setLimit() limit.
*/
virtual size_type getLimit() const = 0;
};
/// Exception thrown by getWritePipe(), getReadPipe() if you didn't ask to
/// create a pipe at the corresponding FILESLOT.
struct NoPipe: public std::runtime_error
{
NoPipe(const std::string& what): std::runtime_error(what) {}
};
/**
* Get a reference to the (only) WritePipe for this LLProcess. @a slot, if
* specified, must be STDIN. Throws NoPipe if you did not request a "pipe"
* for child stdin. Use this method when you know how you created the
* LLProcess in hand.
*/
WritePipe& getWritePipe(FILESLOT slot=STDIN);
/**
* Get a boost::optional<WritePipe&> to the (only) WritePipe for this
* LLProcess. @a slot, if specified, must be STDIN. The return value is
* empty if you did not request a "pipe" for child stdin. Use this method
* for inspecting an LLProcess you did not create.
*/
boost::optional<WritePipe&> getOptWritePipe(FILESLOT slot=STDIN);
/**
* Get a reference to one of the ReadPipes for this LLProcess. @a slot, if
* specified, must be STDOUT or STDERR. Throws NoPipe if you did not
* request a "pipe" for child stdout or stderr. Use this method when you
* know how you created the LLProcess in hand.
*/
ReadPipe& getReadPipe(FILESLOT slot);
/**
* Get a boost::optional<ReadPipe&> to one of the ReadPipes for this
* LLProcess. @a slot, if specified, must be STDOUT or STDERR. The return
* value is empty if you did not request a "pipe" for child stdout or
* stderr. Use this method for inspecting an LLProcess you did not create.
*/
boost::optional<ReadPipe&> getOptReadPipe(FILESLOT slot);
/// little utilities that really should already be somewhere else in the
/// code base
static std::string basename(const std::string& path);
static std::string getline(std::istream&);
private:
/// constructor is private: use create() instead
LLProcess(const LLSDOrParams& params);
void autokill();
// Classic-C-style APR callback
static void status_callback(int reason, void* data, int status);
// Object-oriented callback
void handle_status(int reason, int status);
// implementation for get[Opt][Read|Write]Pipe()
template <class PIPETYPE>
PIPETYPE& getPipe(FILESLOT slot);
template <class PIPETYPE>
boost::optional<PIPETYPE&> getOptPipe(FILESLOT slot);
template <class PIPETYPE>
PIPETYPE* getPipePtr(std::string& error, FILESLOT slot);
std::string mDesc;
std::string mPostend;
apr_proc_t mProcess;
bool mAutokill;
Status mStatus;
// explicitly want this ptr_vector to be able to store NULLs
typedef boost::ptr_vector< boost::nullable<BasePipe> > PipeVector;
PipeVector mPipes;
};
/// for logging
LL_COMMON_API std::ostream& operator<<(std::ostream&, const LLProcess::Params&);
#endif // LL_LLPROCESS_H

View File

@ -1,357 +0,0 @@
/**
* @file llprocesslauncher.cpp
* @brief Utility class for launching, terminating, and tracking the state of processes.
*
* $LicenseInfo:firstyear=2008&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "linden_common.h"
#include "llprocesslauncher.h"
#include <iostream>
#if LL_DARWIN || LL_LINUX
// not required or present on Win32
#include <sys/wait.h>
#endif
LLProcessLauncher::LLProcessLauncher()
{
#if LL_WINDOWS
mProcessHandle = 0;
#else
mProcessID = 0;
#endif
}
LLProcessLauncher::~LLProcessLauncher()
{
kill();
}
void LLProcessLauncher::setExecutable(const std::string &executable)
{
mExecutable = executable;
}
void LLProcessLauncher::setWorkingDirectory(const std::string &dir)
{
mWorkingDir = dir;
}
const std::string& LLProcessLauncher::getExecutable() const
{
return mExecutable;
}
void LLProcessLauncher::clearArguments()
{
mLaunchArguments.clear();
}
void LLProcessLauncher::addArgument(const std::string &arg)
{
mLaunchArguments.push_back(arg);
}
void LLProcessLauncher::addArgument(const char *arg)
{
mLaunchArguments.push_back(std::string(arg));
}
#if LL_WINDOWS
int LLProcessLauncher::launch(void)
{
// If there was already a process associated with this object, kill it.
kill();
orphan();
int result = 0;
PROCESS_INFORMATION pinfo;
STARTUPINFOA sinfo;
memset(&sinfo, 0, sizeof(sinfo));
std::string args = mExecutable;
for(int i = 0; i < (int)mLaunchArguments.size(); i++)
{
args += " ";
args += mLaunchArguments[i];
}
// So retarded. Windows requires that the second parameter to CreateProcessA be a writable (non-const) string...
char *args2 = new char[args.size() + 1];
strcpy(args2, args.c_str());
const char * working_directory = 0;
if(!mWorkingDir.empty()) working_directory = mWorkingDir.c_str();
if( ! CreateProcessA( NULL, args2, NULL, NULL, FALSE, 0, NULL, working_directory, &sinfo, &pinfo ) )
{
result = GetLastError();
LPTSTR error_str = 0;
if(
FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
result,
0,
(LPTSTR)&error_str,
0,
NULL)
!= 0)
{
char message[256];
wcstombs(message, error_str, 256);
message[255] = 0;
llwarns << "CreateProcessA failed: " << message << llendl;
LocalFree(error_str);
}
if(result == 0)
{
// Make absolutely certain we return a non-zero value on failure.
result = -1;
}
}
else
{
// foo = pinfo.dwProcessId; // get your pid here if you want to use it later on
// CloseHandle(pinfo.hProcess); // stops leaks - nothing else
mProcessHandle = pinfo.hProcess;
CloseHandle(pinfo.hThread); // stops leaks - nothing else
}
delete[] args2;
return result;
}
bool LLProcessLauncher::isRunning(void)
{
if(mProcessHandle != 0)
{
DWORD waitresult = WaitForSingleObject(mProcessHandle, 0);
if(waitresult == WAIT_OBJECT_0)
{
// the process has completed.
mProcessHandle = 0;
}
}
return (mProcessHandle != 0);
}
bool LLProcessLauncher::kill(void)
{
bool result = true;
if(mProcessHandle != 0)
{
TerminateProcess(mProcessHandle,0);
if(isRunning())
{
result = false;
}
}
return result;
}
void LLProcessLauncher::orphan(void)
{
// Forget about the process
mProcessHandle = 0;
}
// static
void LLProcessLauncher::reap(void)
{
// No actions necessary on Windows.
}
#else // Mac and linux
#include <signal.h>
#include <fcntl.h>
#include <errno.h>
static std::list<pid_t> sZombies;
// Attempt to reap a process ID -- returns true if the process has exited and been reaped, false otherwise.
static bool reap_pid(pid_t pid)
{
bool result = false;
pid_t wait_result = ::waitpid(pid, NULL, WNOHANG);
if(wait_result == pid)
{
result = true;
}
else if(wait_result == -1)
{
if(errno == ECHILD)
{
// No such process -- this may mean we're ignoring SIGCHILD.
result = true;
}
}
return result;
}
int LLProcessLauncher::launch(void)
{
// If there was already a process associated with this object, kill it.
kill();
orphan();
int result = 0;
int current_wd = -1;
// create an argv vector for the child process
const char ** fake_argv = new const char *[mLaunchArguments.size() + 2]; // 1 for the executable path, 1 for the NULL terminator
int i = 0;
// add the executable path
fake_argv[i++] = mExecutable.c_str();
// and any arguments
for(int j=0; j < mLaunchArguments.size(); j++)
fake_argv[i++] = mLaunchArguments[j].c_str();
// terminate with a null pointer
fake_argv[i] = NULL;
if(!mWorkingDir.empty())
{
// save the current working directory
current_wd = ::open(".", O_RDONLY);
// and change to the one the child will be executed in
if (::chdir(mWorkingDir.c_str()))
{
// chdir failed
}
}
// flush all buffers before the child inherits them
::fflush(NULL);
pid_t id = vfork();
if(id == 0)
{
// child process
::execv(mExecutable.c_str(), (char * const *)fake_argv);
// If we reach this point, the exec failed.
// Use _exit() instead of exit() per the vfork man page.
_exit(0);
}
// parent process
if(current_wd >= 0)
{
// restore the previous working directory
if (::fchdir(current_wd))
{
// chdir failed
}
::close(current_wd);
}
delete[] fake_argv;
mProcessID = id;
return result;
}
bool LLProcessLauncher::isRunning(void)
{
if(mProcessID != 0)
{
// Check whether the process has exited, and reap it if it has.
if(reap_pid(mProcessID))
{
// the process has exited.
mProcessID = 0;
}
}
return (mProcessID != 0);
}
bool LLProcessLauncher::kill(void)
{
bool result = true;
if(mProcessID != 0)
{
// Try to kill the process. We'll do approximately the same thing whether the kill returns an error or not, so we ignore the result.
(void)::kill(mProcessID, SIGTERM);
// This will have the side-effect of reaping the zombie if the process has exited.
if(isRunning())
{
result = false;
}
}
return result;
}
void LLProcessLauncher::orphan(void)
{
// Disassociate the process from this object
if(mProcessID != 0)
{
// We may still need to reap the process's zombie eventually
sZombies.push_back(mProcessID);
mProcessID = 0;
}
}
// static
void LLProcessLauncher::reap(void)
{
// Attempt to real all saved process ID's.
std::list<pid_t>::iterator iter = sZombies.begin();
while(iter != sZombies.end())
{
if(reap_pid(*iter))
{
iter = sZombies.erase(iter);
}
else
{
iter++;
}
}
}
#endif

View File

@ -1,90 +0,0 @@
/**
* @file llprocesslauncher.h
* @brief Utility class for launching, terminating, and tracking the state of processes.
*
* $LicenseInfo:firstyear=2008&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef LL_LLPROCESSLAUNCHER_H
#define LL_LLPROCESSLAUNCHER_H
#if LL_WINDOWS
#include <windows.h>
#endif
/*
LLProcessLauncher handles launching external processes with specified command line arguments.
It also keeps track of whether the process is still running, and can kill it if required.
*/
class LL_COMMON_API LLProcessLauncher
{
LOG_CLASS(LLProcessLauncher);
public:
LLProcessLauncher();
virtual ~LLProcessLauncher();
void setExecutable(const std::string &executable);
void setWorkingDirectory(const std::string &dir);
const std::string& getExecutable() const;
void clearArguments();
void addArgument(const std::string &arg);
void addArgument(const char *arg);
int launch(void);
bool isRunning(void);
// Attempt to kill the process -- returns true if the process is no longer running when it returns.
// Note that even if this returns false, the process may exit some time after it's called.
bool kill(void);
// Use this if you want the external process to continue execution after the LLProcessLauncher instance controlling it is deleted.
// Normally, the destructor will attempt to kill the process and wait for termination.
// This should only be used if the viewer is about to exit -- otherwise, the child process will become a zombie after it exits.
void orphan(void);
// This needs to be called periodically on Mac/Linux to clean up zombie processes.
static void reap(void);
// Accessors for platform-specific process ID
#if LL_WINDOWS
HANDLE getProcessHandle() { return mProcessHandle; };
#else
pid_t getProcessID() { return mProcessID; };
#endif
private:
std::string mExecutable;
std::string mWorkingDir;
std::vector<std::string> mLaunchArguments;
#if LL_WINDOWS
HANDLE mProcessHandle;
#else
pid_t mProcessID;
#endif
};
#endif // LL_LLPROCESSLAUNCHER_H

View File

@ -31,6 +31,7 @@
#include <boost/type_traits.hpp>
#include "llsingleton.h"
#include "lltypeinfolookup.h"
template <typename T>
class LLRegistryDefaultComparator
@ -38,6 +39,24 @@ class LLRegistryDefaultComparator
bool operator()(const T& lhs, const T& rhs) { return lhs < rhs; }
};
template <typename KEY, typename VALUE>
struct LLRegistryMapSelector
{
typedef std::map<KEY, VALUE> type;
};
template <typename VALUE>
struct LLRegistryMapSelector<std::type_info*, VALUE>
{
typedef LLTypeInfoLookup<VALUE> type;
};
template <typename VALUE>
struct LLRegistryMapSelector<const std::type_info*, VALUE>
{
typedef LLTypeInfoLookup<VALUE> type;
};
template <typename KEY, typename VALUE, typename COMPARATOR = LLRegistryDefaultComparator<KEY> >
class LLRegistry
{
@ -53,7 +72,7 @@ public:
{
friend class LLRegistry<KEY, VALUE, COMPARATOR>;
public:
typedef typename std::map<KEY, VALUE> registry_map_t;
typedef typename LLRegistryMapSelector<KEY, VALUE>::type registry_map_t;
bool add(ref_const_key_t key, ref_const_value_t value)
{

View File

@ -31,7 +31,7 @@
#include "llinitparam.h"
#include "boost/function.hpp"
struct LLParamSDParserUtilities
struct LL_COMMON_API LLParamSDParserUtilities
{
static LLSD& getSDWriteNode(LLSD& input, LLInitParam::Parser::name_stack_range_t& name_stack_range);
@ -40,7 +40,7 @@ struct LLParamSDParserUtilities
static void readSDValues(read_sd_cb_t cb, const LLSD& sd);
};
class LLParamSDParser
class LL_COMMON_API LLParamSDParser
: public LLInitParam::Parser
{
LOG_CLASS(LLParamSDParser);
@ -92,7 +92,7 @@ private:
};
extern LLFastTimer::DeclareTimer FTM_SD_PARAM_ADAPTOR;
extern LL_COMMON_API LLFastTimer::DeclareTimer FTM_SD_PARAM_ADAPTOR;
template<typename T>
class LLSDParamAdapter : public T
{

View File

@ -0,0 +1,152 @@
/**
* @file llsortedvector.h
* @author Nat Goodspeed
* @date 2012-04-08
* @brief LLSortedVector class wraps a vector that we maintain in sorted
* order so we can perform binary-search lookups.
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Copyright (c) 2012, Linden Research, Inc.
* $/LicenseInfo$
*/
#if ! defined(LL_LLSORTEDVECTOR_H)
#define LL_LLSORTEDVECTOR_H
#include <vector>
#include <algorithm>
/**
* LLSortedVector contains a std::vector<std::pair> that we keep sorted on the
* first of the pair. This makes insertion somewhat more expensive than simple
* std::vector::push_back(), but allows us to use binary search for lookups.
* It's intended for small aggregates where lookup is far more performance-
* critical than insertion; in such cases a binary search on a small, sorted
* std::vector can be more performant than a std::map lookup.
*/
template <typename KEY, typename VALUE>
class LLSortedVector
{
public:
typedef LLSortedVector<KEY, VALUE> self;
typedef KEY key_type;
typedef VALUE mapped_type;
typedef std::pair<key_type, mapped_type> value_type;
typedef std::vector<value_type> PairVector;
typedef typename PairVector::iterator iterator;
typedef typename PairVector::const_iterator const_iterator;
/// Empty
LLSortedVector() {}
/// Fixed initial size
LLSortedVector(std::size_t size):
mVector(size)
{}
/// Bulk load
template <typename ITER>
LLSortedVector(ITER begin, ITER end):
mVector(begin, end)
{
// Allow caller to dump in a bunch of (pairs convertible to)
// value_type if desired, but make sure we sort afterwards.
std::sort(mVector.begin(), mVector.end());
}
/// insert(key, value)
std::pair<iterator, bool> insert(const key_type& key, const mapped_type& value)
{
return insert(value_type(key, value));
}
/// insert(value_type)
std::pair<iterator, bool> insert(const value_type& pair)
{
typedef std::pair<iterator, bool> iterbool;
iterator found = std::lower_bound(mVector.begin(), mVector.end(), pair,
less<value_type>());
// have to check for end() before it's even valid to dereference
if (found == mVector.end())
{
std::size_t index(mVector.size());
mVector.push_back(pair);
// don't forget that push_back() invalidates 'found'
return iterbool(mVector.begin() + index, true);
}
if (found->first == pair.first)
{
return iterbool(found, false);
}
// remember that insert() invalidates 'found' -- save index
std::size_t index(found - mVector.begin());
mVector.insert(found, pair);
// okay, convert from index back to iterator
return iterbool(mVector.begin() + index, true);
}
iterator begin() { return mVector.begin(); }
iterator end() { return mVector.end(); }
const_iterator begin() const { return mVector.begin(); }
const_iterator end() const { return mVector.end(); }
bool empty() const { return mVector.empty(); }
std::size_t size() const { return mVector.size(); }
/// find
iterator find(const key_type& key)
{
iterator found = std::lower_bound(mVector.begin(), mVector.end(),
value_type(key, mapped_type()),
less<value_type>());
if (found == mVector.end() || found->first != key)
return mVector.end();
return found;
}
const_iterator find(const key_type& key) const
{
return const_cast<self*>(this)->find(key);
}
private:
// Define our own 'less' comparator so we can specialize without messing
// with std::less.
template <typename T>
struct less: public std::less<T> {};
// Specialize 'less' for an LLSortedVector::value_type involving
// std::type_info*. This is one of LLSortedVector's foremost use cases. We
// specialize 'less' rather than just defining a specific comparator
// because LLSortedVector should be usable for other key_types as well.
template <typename T>
struct less< std::pair<std::type_info*, T> >:
public std::binary_function<std::pair<std::type_info*, T>,
std::pair<std::type_info*, T>,
bool>
{
bool operator()(const std::pair<std::type_info*, T>& lhs,
const std::pair<std::type_info*, T>& rhs) const
{
return lhs.first->before(*rhs.first);
}
};
// Same as above, but with const std::type_info*.
template <typename T>
struct less< std::pair<const std::type_info*, T> >:
public std::binary_function<std::pair<const std::type_info*, T>,
std::pair<const std::type_info*, T>,
bool>
{
bool operator()(const std::pair<const std::type_info*, T>& lhs,
const std::pair<const std::type_info*, T>& rhs) const
{
return lhs.first->before(*rhs.first);
}
};
PairVector mVector;
};
#endif /* ! defined(LL_LLSORTEDVECTOR_H) */

View File

@ -0,0 +1,24 @@
/**
* @file llstreamqueue.cpp
* @author Nat Goodspeed
* @date 2012-01-05
* @brief Implementation for llstreamqueue.
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Copyright (c) 2012, Linden Research, Inc.
* $/LicenseInfo$
*/
// Precompiled header
#include "linden_common.h"
// associated header
#include "llstreamqueue.h"
// STL headers
// std headers
// external library headers
// other Linden headers
// As of this writing, llstreamqueue.h is entirely template-based, therefore
// we don't strictly need a corresponding .cpp file. However, our CMake test
// macro assumes one. Here it is.
bool llstreamqueue_cpp_ignored = true;

View File

@ -0,0 +1,240 @@
/**
* @file llstreamqueue.h
* @author Nat Goodspeed
* @date 2012-01-04
* @brief Definition of LLStreamQueue
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Copyright (c) 2012, Linden Research, Inc.
* $/LicenseInfo$
*/
#if ! defined(LL_LLSTREAMQUEUE_H)
#define LL_LLSTREAMQUEUE_H
#include <string>
#include <list>
#include <iosfwd> // std::streamsize
#include <boost/iostreams/categories.hpp>
/**
* This class is a growable buffer between a producer and consumer. It serves
* as a queue usable with Boost.Iostreams -- hence, a "stream queue."
*
* This is especially useful for buffering nonblocking I/O. For instance, we
* want application logic to be able to serialize LLSD to a std::ostream. We
* may write more data than the destination pipe can handle all at once, but
* it's imperative NOT to block the application-level serialization call. So
* we buffer it instead. Successive frames can try nonblocking writes to the
* destination pipe until all buffered data has been sent.
*
* Similarly, we want application logic be able to deserialize LLSD from a
* std::istream. Again, we must not block that deserialize call waiting for
* more data to arrive from the input pipe! Instead we build up a buffer over
* a number of frames, using successive nonblocking reads, until we have
* "enough" data to be able to present it through a std::istream.
*
* @note The use cases for this class overlap somewhat with those for the
* LLIOPipe/LLPumpIO hierarchies, and indeed we considered using those. This
* class has two virtues over the older machinery:
*
* # It's vastly simpler -- way fewer concepts. It's not clear to me whether
* there were ever LLIOPipe/etc. use cases that demanded all the fanciness
* rolled in, or whether they were simply overdesigned. In any case, no
* remaining Lindens will admit to familiarity with those classes -- and
* they're sufficiently obtuse that it would take considerable learning
* curve to figure out how to use them properly. The bottom line is that
* current management is not keen on any more engineers climbing that curve.
* # This class is designed around available components such as std::string,
* std::list, Boost.Iostreams. There's less proprietary code.
*/
template <typename Ch>
class LLGenericStreamQueue
{
public:
LLGenericStreamQueue():
mSize(0),
mClosed(false)
{}
/**
* Boost.Iostreams Source Device facade for use with other Boost.Iostreams
* functionality. LLGenericStreamQueue doesn't quite fit any of the Boost
* 1.48 Iostreams concepts; instead it behaves as both a Sink and a
* Source. This is its Source facade.
*/
struct Source
{
typedef Ch char_type;
typedef boost::iostreams::source_tag category;
/// Bind the underlying LLGenericStreamQueue
Source(LLGenericStreamQueue& sq):
mStreamQueue(sq)
{}
// Read up to n characters from the underlying data source into the
// buffer s, returning the number of characters read; return -1 to
// indicate EOF
std::streamsize read(Ch* s, std::streamsize n)
{
return mStreamQueue.read(s, n);
}
LLGenericStreamQueue& mStreamQueue;
};
/**
* Boost.Iostreams Sink Device facade for use with other Boost.Iostreams
* functionality. LLGenericStreamQueue doesn't quite fit any of the Boost
* 1.48 Iostreams concepts; instead it behaves as both a Sink and a
* Source. This is its Sink facade.
*/
struct Sink
{
typedef Ch char_type;
typedef boost::iostreams::sink_tag category;
/// Bind the underlying LLGenericStreamQueue
Sink(LLGenericStreamQueue& sq):
mStreamQueue(sq)
{}
/// Write up to n characters from the buffer s to the output sequence,
/// returning the number of characters written
std::streamsize write(const Ch* s, std::streamsize n)
{
return mStreamQueue.write(s, n);
}
/// Send EOF to consumer
void close()
{
mStreamQueue.close();
}
LLGenericStreamQueue& mStreamQueue;
};
/// Present Boost.Iostreams Source facade
Source asSource() { return Source(*this); }
/// Present Boost.Iostreams Sink facade
Sink asSink() { return Sink(*this); }
/// append data to buffer
std::streamsize write(const Ch* s, std::streamsize n)
{
// Unclear how often we might be asked to write 0 bytes -- perhaps a
// naive caller responding to an unready nonblocking read. But if we
// do get such a call, don't add a completely empty BufferList entry.
if (n == 0)
return n;
// We could implement this using a single std::string object, a la
// ostringstream. But the trouble with appending to a string is that
// you might have to recopy all previous contents to grow its size. If
// we want this to scale to large data volumes, better to allocate
// individual pieces.
mBuffer.push_back(string(s, n));
mSize += n;
return n;
}
/**
* Inform this LLGenericStreamQueue that no further data are forthcoming.
* For our purposes, close() is strictly a producer-side operation;
* there's little point in closing the consumer side.
*/
void close()
{
mClosed = true;
}
/// consume data from buffer
std::streamsize read(Ch* s, std::streamsize n)
{
// read() is actually a convenience method for peek() followed by
// skip().
std::streamsize got(peek(s, n));
// We can only skip() as many characters as we can peek(); ignore
// skip() return here.
skip(n);
return got;
}
/// Retrieve data from buffer without consuming. Like read(), return -1 on
/// EOF.
std::streamsize peek(Ch* s, std::streamsize n) const;
/// Consume data from buffer without retrieving. Unlike read() and peek(),
/// at EOF we simply skip 0 characters.
std::streamsize skip(std::streamsize n);
/// How many characters do we currently have buffered?
std::streamsize size() const
{
return mSize;
}
private:
typedef std::basic_string<Ch> string;
typedef std::list<string> BufferList;
BufferList mBuffer;
std::streamsize mSize;
bool mClosed;
};
template <typename Ch>
std::streamsize LLGenericStreamQueue<Ch>::peek(Ch* s, std::streamsize n) const
{
// Here we may have to build up 'n' characters from an arbitrary
// number of individual BufferList entries.
typename BufferList::const_iterator bli(mBuffer.begin()), blend(mBuffer.end());
// Indicate EOF if producer has closed the pipe AND we've exhausted
// all previously-buffered data.
if (mClosed && bli == blend)
{
return -1;
}
// Here either producer hasn't yet closed, or we haven't yet exhausted
// remaining data.
std::streamsize needed(n), got(0);
// Loop until either we run out of BufferList entries or we've
// completely satisfied the request.
for ( ; bli != blend && needed; ++bli)
{
std::streamsize chunk(std::min(needed, std::streamsize(bli->length())));
std::copy(bli->begin(), bli->begin() + chunk, s);
needed -= chunk;
s += chunk;
got += chunk;
}
return got;
}
template <typename Ch>
std::streamsize LLGenericStreamQueue<Ch>::skip(std::streamsize n)
{
typename BufferList::iterator bli(mBuffer.begin()), blend(mBuffer.end());
std::streamsize toskip(n), skipped(0);
while (bli != blend && toskip >= bli->length())
{
std::streamsize chunk(bli->length());
typename BufferList::iterator zap(bli++);
mBuffer.erase(zap);
mSize -= chunk;
toskip -= chunk;
skipped += chunk;
}
if (bli != blend && toskip)
{
bli->erase(bli->begin(), bli->begin() + toskip);
mSize -= toskip;
skipped += toskip;
}
return skipped;
}
typedef LLGenericStreamQueue<char> LLStreamQueue;
typedef LLGenericStreamQueue<wchar_t> LLWStreamQueue;
#endif /* ! defined(LL_LLSTREAMQUEUE_H) */

View File

@ -912,22 +912,24 @@ S32 LLStringUtil::format(std::string& s, const format_map_t& substitutions);
template<>
void LLStringUtil::getTokens(const std::string& instr, std::vector<std::string >& tokens, const std::string& delims)
{
std::string currToken;
std::string::size_type begIdx, endIdx;
begIdx = instr.find_first_not_of (delims);
while (begIdx != std::string::npos)
// Starting at offset 0, scan forward for the next non-delimiter. We're
// done when the only characters left in 'instr' are delimiters.
for (std::string::size_type begIdx, endIdx = 0;
(begIdx = instr.find_first_not_of (delims, endIdx)) != std::string::npos; )
{
// Found a non-delimiter. After that, find the next delimiter.
endIdx = instr.find_first_of (delims, begIdx);
if (endIdx == std::string::npos)
{
// No more delimiters: this token extends to the end of the string.
endIdx = instr.length();
}
currToken = instr.substr(begIdx, endIdx - begIdx);
// extract the token between begIdx and endIdx; substr() needs length
std::string currToken(instr.substr(begIdx, endIdx - begIdx));
LLStringUtil::trim (currToken);
tokens.push_back(currToken);
begIdx = instr.find_first_not_of (delims, endIdx);
// next scan past delimiters starts at endIdx
}
}

View File

@ -40,6 +40,7 @@
#endif
#include <string.h>
#include <boost/scoped_ptr.hpp>
#if LL_SOLARIS
// stricmp and strnicmp do not exist on Solaris:
@ -237,40 +238,77 @@ private:
static std::string sLocale;
public:
typedef typename std::basic_string<T>::size_type size_type;
typedef std::basic_string<T> string_type;
typedef typename string_type::size_type size_type;
public:
/////////////////////////////////////////////////////////////////////////////////////////
// Static Utility functions that operate on std::strings
static const std::basic_string<T> null;
static const string_type null;
typedef std::map<LLFormatMapString, LLFormatMapString> format_map_t;
LL_COMMON_API static void getTokens(const std::basic_string<T>& instr, std::vector<std::basic_string<T> >& tokens, const std::basic_string<T>& delims);
LL_COMMON_API static void formatNumber(std::basic_string<T>& numStr, std::basic_string<T> decimals);
LL_COMMON_API static bool formatDatetime(std::basic_string<T>& replacement, std::basic_string<T> token, std::basic_string<T> param, S32 secFromEpoch);
LL_COMMON_API static S32 format(std::basic_string<T>& s, const format_map_t& substitutions);
LL_COMMON_API static S32 format(std::basic_string<T>& s, const LLSD& substitutions);
LL_COMMON_API static bool simpleReplacement(std::basic_string<T>& replacement, std::basic_string<T> token, const format_map_t& substitutions);
LL_COMMON_API static bool simpleReplacement(std::basic_string<T>& replacement, std::basic_string<T> token, const LLSD& substitutions);
/// considers any sequence of delims as a single field separator
LL_COMMON_API static void getTokens(const string_type& instr,
std::vector<string_type >& tokens,
const string_type& delims);
/// like simple scan overload, but returns scanned vector
static std::vector<string_type> getTokens(const string_type& instr,
const string_type& delims);
/// add support for keep_delims and quotes (either could be empty string)
static void getTokens(const string_type& instr,
std::vector<string_type>& tokens,
const string_type& drop_delims,
const string_type& keep_delims,
const string_type& quotes=string_type());
/// like keep_delims-and-quotes overload, but returns scanned vector
static std::vector<string_type> getTokens(const string_type& instr,
const string_type& drop_delims,
const string_type& keep_delims,
const string_type& quotes=string_type());
/// add support for escapes (could be empty string)
static void getTokens(const string_type& instr,
std::vector<string_type>& tokens,
const string_type& drop_delims,
const string_type& keep_delims,
const string_type& quotes,
const string_type& escapes);
/// like escapes overload, but returns scanned vector
static std::vector<string_type> getTokens(const string_type& instr,
const string_type& drop_delims,
const string_type& keep_delims,
const string_type& quotes,
const string_type& escapes);
LL_COMMON_API static void formatNumber(string_type& numStr, string_type decimals);
LL_COMMON_API static bool formatDatetime(string_type& replacement, string_type token, string_type param, S32 secFromEpoch);
LL_COMMON_API static S32 format(string_type& s, const format_map_t& substitutions);
LL_COMMON_API static S32 format(string_type& s, const LLSD& substitutions);
LL_COMMON_API static bool simpleReplacement(string_type& replacement, string_type token, const format_map_t& substitutions);
LL_COMMON_API static bool simpleReplacement(string_type& replacement, string_type token, const LLSD& substitutions);
LL_COMMON_API static void setLocale (std::string inLocale);
LL_COMMON_API static std::string getLocale (void);
static bool isValidIndex(const std::basic_string<T>& string, size_type i)
static bool isValidIndex(const string_type& string, size_type i)
{
return !string.empty() && (0 <= i) && (i <= string.size());
}
static void trimHead(std::basic_string<T>& string);
static void trimTail(std::basic_string<T>& string);
static void trim(std::basic_string<T>& string) { trimHead(string); trimTail(string); }
static void truncate(std::basic_string<T>& string, size_type count);
static bool contains(const string_type& string, T c, size_type i=0)
{
return string.find(c, i) != string_type::npos;
}
static void toUpper(std::basic_string<T>& string);
static void toLower(std::basic_string<T>& string);
static void trimHead(string_type& string);
static void trimTail(string_type& string);
static void trim(string_type& string) { trimHead(string); trimTail(string); }
static void truncate(string_type& string, size_type count);
static void toUpper(string_type& string);
static void toLower(string_type& string);
// True if this is the head of s.
static BOOL isHead( const std::basic_string<T>& string, const T* s );
static BOOL isHead( const string_type& string, const T* s );
/**
* @brief Returns true if string starts with substr
@ -278,8 +316,8 @@ public:
* If etither string or substr are empty, this method returns false.
*/
static bool startsWith(
const std::basic_string<T>& string,
const std::basic_string<T>& substr);
const string_type& string,
const string_type& substr);
/**
* @brief Returns true if string ends in substr
@ -287,19 +325,32 @@ public:
* If etither string or substr are empty, this method returns false.
*/
static bool endsWith(
const std::basic_string<T>& string,
const std::basic_string<T>& substr);
const string_type& string,
const string_type& substr);
static void addCRLF(std::basic_string<T>& string);
static void removeCRLF(std::basic_string<T>& string);
static void addCRLF(string_type& string);
static void removeCRLF(string_type& string);
static void replaceTabsWithSpaces( std::basic_string<T>& string, size_type spaces_per_tab );
static void replaceNonstandardASCII( std::basic_string<T>& string, T replacement );
static void replaceChar( std::basic_string<T>& string, T target, T replacement );
static void replaceString( std::basic_string<T>& string, std::basic_string<T> target, std::basic_string<T> replacement );
static void replaceTabsWithSpaces( string_type& string, size_type spaces_per_tab );
static void replaceNonstandardASCII( string_type& string, T replacement );
static void replaceChar( string_type& string, T target, T replacement );
static void replaceString( string_type& string, string_type target, string_type replacement );
static BOOL containsNonprintable(const std::basic_string<T>& string);
static void stripNonprintable(std::basic_string<T>& string);
static BOOL containsNonprintable(const string_type& string);
static void stripNonprintable(string_type& string);
/**
* Double-quote an argument string if needed, unless it's already
* double-quoted. Decide whether it's needed based on the presence of any
* character in @a triggers (default space or double-quote). If we quote
* it, escape any embedded double-quote with the @a escape string (default
* backslash).
*
* Passing triggers="" means always quote, unless it's already double-quoted.
*/
static string_type quote(const string_type& str,
const string_type& triggers=" \"",
const string_type& escape="\\");
/**
* @brief Unsafe way to make ascii characters. You should probably
@ -308,18 +359,18 @@ public:
* The 2 and 4 byte std::string probably work, so LLWStringUtil::_makeASCII
* should work.
*/
static void _makeASCII(std::basic_string<T>& string);
static void _makeASCII(string_type& string);
// Conversion to other data types
static BOOL convertToBOOL(const std::basic_string<T>& string, BOOL& value);
static BOOL convertToU8(const std::basic_string<T>& string, U8& value);
static BOOL convertToS8(const std::basic_string<T>& string, S8& value);
static BOOL convertToS16(const std::basic_string<T>& string, S16& value);
static BOOL convertToU16(const std::basic_string<T>& string, U16& value);
static BOOL convertToU32(const std::basic_string<T>& string, U32& value);
static BOOL convertToS32(const std::basic_string<T>& string, S32& value);
static BOOL convertToF32(const std::basic_string<T>& string, F32& value);
static BOOL convertToF64(const std::basic_string<T>& string, F64& value);
static BOOL convertToBOOL(const string_type& string, BOOL& value);
static BOOL convertToU8(const string_type& string, U8& value);
static BOOL convertToS8(const string_type& string, S8& value);
static BOOL convertToS16(const string_type& string, S16& value);
static BOOL convertToU16(const string_type& string, U16& value);
static BOOL convertToU32(const string_type& string, U32& value);
static BOOL convertToS32(const string_type& string, S32& value);
static BOOL convertToF32(const string_type& string, F32& value);
static BOOL convertToF64(const string_type& string, F64& value);
/////////////////////////////////////////////////////////////////////////////////////////
// Utility functions for working with char*'s and strings
@ -327,24 +378,24 @@ public:
// Like strcmp but also handles empty strings. Uses
// current locale.
static S32 compareStrings(const T* lhs, const T* rhs);
static S32 compareStrings(const std::basic_string<T>& lhs, const std::basic_string<T>& rhs);
static S32 compareStrings(const string_type& lhs, const string_type& rhs);
// case insensitive version of above. Uses current locale on
// Win32, and falls back to a non-locale aware comparison on
// Linux.
static S32 compareInsensitive(const T* lhs, const T* rhs);
static S32 compareInsensitive(const std::basic_string<T>& lhs, const std::basic_string<T>& rhs);
static S32 compareInsensitive(const string_type& lhs, const string_type& rhs);
// Case sensitive comparison with good handling of numbers. Does not use current locale.
// a.k.a. strdictcmp()
static S32 compareDict(const std::basic_string<T>& a, const std::basic_string<T>& b);
static S32 compareDict(const string_type& a, const string_type& b);
// Case *in*sensitive comparison with good handling of numbers. Does not use current locale.
// a.k.a. strdictcmp()
static S32 compareDictInsensitive(const std::basic_string<T>& a, const std::basic_string<T>& b);
static S32 compareDictInsensitive(const string_type& a, const string_type& b);
// Puts compareDict() in a form appropriate for LL container classes to use for sorting.
static BOOL precedesDict( const std::basic_string<T>& a, const std::basic_string<T>& b );
static BOOL precedesDict( const string_type& a, const string_type& b );
// A replacement for strncpy.
// If the dst buffer is dst_size bytes long or more, ensures that dst is null terminated and holds
@ -352,7 +403,7 @@ public:
static void copy(T* dst, const T* src, size_type dst_size);
// Copies src into dst at a given offset.
static void copyInto(std::basic_string<T>& dst, const std::basic_string<T>& src, size_type offset);
static void copyInto(string_type& dst, const string_type& src, size_type offset);
static bool isPartOfWord(T c) { return (c == (T)'_') || LLStringOps::isAlnum(c); }
@ -362,7 +413,7 @@ public:
#endif
private:
LL_COMMON_API static size_type getSubstitution(const std::basic_string<T>& instr, size_type& start, std::vector<std::basic_string<T> >& tokens);
LL_COMMON_API static size_type getSubstitution(const string_type& instr, size_type& start, std::vector<string_type >& tokens);
};
template<class T> const std::basic_string<T> LLStringUtilBase<T>::null;
@ -636,10 +687,325 @@ namespace LLStringFn
////////////////////////////////////////////////////////////
// NOTE: LLStringUtil::format, getTokens, and support functions moved to llstring.cpp.
// There is no LLWStringUtil::format implementation currently.
// Calling thse for anything other than LLStringUtil will produce link errors.
// Calling these for anything other than LLStringUtil will produce link errors.
////////////////////////////////////////////////////////////
// static
template <class T>
std::vector<typename LLStringUtilBase<T>::string_type>
LLStringUtilBase<T>::getTokens(const string_type& instr, const string_type& delims)
{
std::vector<string_type> tokens;
getTokens(instr, tokens, delims);
return tokens;
}
// static
template <class T>
std::vector<typename LLStringUtilBase<T>::string_type>
LLStringUtilBase<T>::getTokens(const string_type& instr,
const string_type& drop_delims,
const string_type& keep_delims,
const string_type& quotes)
{
std::vector<string_type> tokens;
getTokens(instr, tokens, drop_delims, keep_delims, quotes);
return tokens;
}
// static
template <class T>
std::vector<typename LLStringUtilBase<T>::string_type>
LLStringUtilBase<T>::getTokens(const string_type& instr,
const string_type& drop_delims,
const string_type& keep_delims,
const string_type& quotes,
const string_type& escapes)
{
std::vector<string_type> tokens;
getTokens(instr, tokens, drop_delims, keep_delims, quotes, escapes);
return tokens;
}
namespace LLStringUtilBaseImpl
{
/**
* Input string scanner helper for getTokens(), or really any other
* character-parsing routine that may have to deal with escape characters.
* This implementation defines the concept (also an interface, should you
* choose to implement the concept by subclassing) and provides trivial
* implementations for a string @em without escape processing.
*/
template <class T>
struct InString
{
typedef std::basic_string<T> string_type;
typedef typename string_type::const_iterator const_iterator;
InString(const_iterator b, const_iterator e):
mIter(b),
mEnd(e)
{}
virtual ~InString() {}
bool done() const { return mIter == mEnd; }
/// Is the current character (*mIter) escaped? This implementation can
/// answer trivially because it doesn't support escapes.
virtual bool escaped() const { return false; }
/// Obtain the current character and advance @c mIter.
virtual T next() { return *mIter++; }
/// Does the current character match specified character?
virtual bool is(T ch) const { return (! done()) && *mIter == ch; }
/// Is the current character any one of the specified characters?
virtual bool oneof(const string_type& delims) const
{
return (! done()) && LLStringUtilBase<T>::contains(delims, *mIter);
}
/**
* Scan forward from @from until either @a delim or end. This is primarily
* useful for processing quoted substrings.
*
* If we do see @a delim, append everything from @from until (excluding)
* @a delim to @a into, advance @c mIter to skip @a delim, and return @c
* true.
*
* If we do not see @a delim, do not alter @a into or @c mIter and return
* @c false. Do not pass GO, do not collect $200.
*
* @note The @c false case described above implements normal getTokens()
* treatment of an unmatched open quote: treat the quote character as if
* escaped, that is, simply collect it as part of the current token. Other
* plausible behaviors directly affect the way getTokens() deals with an
* unmatched quote: e.g. throwing an exception to treat it as an error, or
* assuming a close quote beyond end of string (in which case return @c
* true).
*/
virtual bool collect_until(string_type& into, const_iterator from, T delim)
{
const_iterator found = std::find(from, mEnd, delim);
// If we didn't find delim, change nothing, just tell caller.
if (found == mEnd)
return false;
// Found delim! Append everything between from and found.
into.append(from, found);
// advance past delim in input
mIter = found + 1;
return true;
}
const_iterator mIter, mEnd;
};
/// InString subclass that handles escape characters
template <class T>
class InEscString: public InString<T>
{
public:
typedef InString<T> super;
typedef typename super::string_type string_type;
typedef typename super::const_iterator const_iterator;
using super::done;
using super::mIter;
using super::mEnd;
InEscString(const_iterator b, const_iterator e, const string_type& escapes):
super(b, e),
mEscapes(escapes)
{
// Even though we've already initialized 'mIter' via our base-class
// constructor, set it again to check for initial escape char.
setiter(b);
}
/// This implementation uses the answer cached by setiter().
virtual bool escaped() const { return mIsEsc; }
virtual T next()
{
// If we're looking at the escape character of an escape sequence,
// skip that character. This is the one time we can modify 'mIter'
// without using setiter: for this one case we DO NOT CARE if the
// escaped character is itself an escape.
if (mIsEsc)
++mIter;
// If we were looking at an escape character, this is the escaped
// character; otherwise it's just the next character.
T result(*mIter);
// Advance mIter, checking for escape sequence.
setiter(mIter + 1);
return result;
}
virtual bool is(T ch) const
{
// Like base-class is(), except that an escaped character matches
// nothing.
return (! done()) && (! mIsEsc) && *mIter == ch;
}
virtual bool oneof(const string_type& delims) const
{
// Like base-class oneof(), except that an escaped character matches
// nothing.
return (! done()) && (! mIsEsc) && LLStringUtilBase<T>::contains(delims, *mIter);
}
virtual bool collect_until(string_type& into, const_iterator from, T delim)
{
// Deal with escapes in the characters we collect; that is, an escaped
// character must become just that character without the preceding
// escape. Collect characters in a separate string rather than
// directly appending to 'into' in case we do not find delim, in which
// case we're supposed to leave 'into' unmodified.
string_type collected;
// For scanning purposes, we're going to work directly with 'mIter'.
// Save its current value in case we fail to see delim.
const_iterator save_iter(mIter);
// Okay, set 'mIter', checking for escape.
setiter(from);
while (! done())
{
// If we see an unescaped delim, stop and report success.
if ((! mIsEsc) && *mIter == delim)
{
// Append collected chars to 'into'.
into.append(collected);
// Don't forget to advance 'mIter' past delim.
setiter(mIter + 1);
return true;
}
// We're not at end, and either we're not looking at delim or it's
// escaped. Collect this character and keep going.
collected.push_back(next());
}
// Here we hit 'mEnd' without ever seeing delim. Restore mIter and tell
// caller.
setiter(save_iter);
return false;
}
private:
void setiter(const_iterator i)
{
mIter = i;
// Every time we change 'mIter', set 'mIsEsc' to be able to repetitively
// answer escaped() without having to rescan 'mEscapes'. mIsEsc caches
// contains(mEscapes, *mIter).
// We're looking at an escaped char if we're not already at end (that
// is, *mIter is even meaningful); if *mIter is in fact one of the
// specified escape characters; and if there's one more character
// following it. That is, if an escape character is the very last
// character of the input string, it loses its special meaning.
mIsEsc = (! done()) &&
LLStringUtilBase<T>::contains(mEscapes, *mIter) &&
(mIter+1) != mEnd;
}
const string_type mEscapes;
bool mIsEsc;
};
/// getTokens() implementation based on InString concept
template <typename INSTRING, typename string_type>
void getTokens(INSTRING& instr, std::vector<string_type>& tokens,
const string_type& drop_delims, const string_type& keep_delims,
const string_type& quotes)
{
// There are times when we want to match either drop_delims or
// keep_delims. Concatenate them up front to speed things up.
string_type all_delims(drop_delims + keep_delims);
// no tokens yet
tokens.clear();
// try for another token
while (! instr.done())
{
// scan past any drop_delims
while (instr.oneof(drop_delims))
{
// skip this drop_delim
instr.next();
// but if that was the end of the string, done
if (instr.done())
return;
}
// found the start of another token: make a slot for it
tokens.push_back(string_type());
if (instr.oneof(keep_delims))
{
// *iter is a keep_delim, a token of exactly 1 character. Append
// that character to the new token and proceed.
tokens.back().push_back(instr.next());
continue;
}
// Here we have a non-delimiter token, which might consist of a mix of
// quoted and unquoted parts. Use bash rules for quoting: you can
// embed a quoted substring in the midst of an unquoted token (e.g.
// ~/"sub dir"/myfile.txt); you can ram two quoted substrings together
// to make a single token (e.g. 'He said, "'"Don't."'"'). We diverge
// from bash in that bash considers an unmatched quote an error. Our
// param signature doesn't allow for errors, so just pretend it's not
// a quote and embed it.
// At this level, keep scanning until we hit the next delimiter of
// either type (drop_delims or keep_delims).
while (! instr.oneof(all_delims))
{
// If we're looking at an open quote, search forward for
// a close quote, collecting characters along the way.
if (instr.oneof(quotes) &&
instr.collect_until(tokens.back(), instr.mIter+1, *instr.mIter))
{
// collect_until is cleverly designed to do exactly what we
// need here. No further action needed if it returns true.
}
else
{
// Either *iter isn't a quote, or there's no matching close
// quote: in other words, just an ordinary char. Append it to
// current token.
tokens.back().push_back(instr.next());
}
// having scanned that segment of this token, if we've reached the
// end of the string, we're done
if (instr.done())
return;
}
}
}
} // namespace LLStringUtilBaseImpl
// static
template <class T>
void LLStringUtilBase<T>::getTokens(const string_type& string, std::vector<string_type>& tokens,
const string_type& drop_delims, const string_type& keep_delims,
const string_type& quotes)
{
// Because this overload doesn't support escapes, use simple InString to
// manage input range.
LLStringUtilBaseImpl::InString<T> instring(string.begin(), string.end());
LLStringUtilBaseImpl::getTokens(instring, tokens, drop_delims, keep_delims, quotes);
}
// static
template <class T>
void LLStringUtilBase<T>::getTokens(const string_type& string, std::vector<string_type>& tokens,
const string_type& drop_delims, const string_type& keep_delims,
const string_type& quotes, const string_type& escapes)
{
// This overload must deal with escapes. Delegate that to InEscString
// (unless there ARE no escapes).
boost::scoped_ptr< LLStringUtilBaseImpl::InString<T> > instrp;
if (escapes.empty())
instrp.reset(new LLStringUtilBaseImpl::InString<T>(string.begin(), string.end()));
else
instrp.reset(new LLStringUtilBaseImpl::InEscString<T>(string.begin(), string.end(), escapes));
LLStringUtilBaseImpl::getTokens(*instrp, tokens, drop_delims, keep_delims, quotes);
}
// static
template<class T>
@ -669,7 +1035,7 @@ S32 LLStringUtilBase<T>::compareStrings(const T* lhs, const T* rhs)
//static
template<class T>
S32 LLStringUtilBase<T>::compareStrings(const std::basic_string<T>& lhs, const std::basic_string<T>& rhs)
S32 LLStringUtilBase<T>::compareStrings(const string_type& lhs, const string_type& rhs)
{
return LLStringOps::collate(lhs.c_str(), rhs.c_str());
}
@ -695,8 +1061,8 @@ S32 LLStringUtilBase<T>::compareInsensitive(const T* lhs, const T* rhs )
}
else
{
std::basic_string<T> lhs_string(lhs);
std::basic_string<T> rhs_string(rhs);
string_type lhs_string(lhs);
string_type rhs_string(rhs);
LLStringUtilBase<T>::toUpper(lhs_string);
LLStringUtilBase<T>::toUpper(rhs_string);
result = LLStringOps::collate(lhs_string.c_str(), rhs_string.c_str());
@ -706,10 +1072,10 @@ S32 LLStringUtilBase<T>::compareInsensitive(const T* lhs, const T* rhs )
//static
template<class T>
S32 LLStringUtilBase<T>::compareInsensitive(const std::basic_string<T>& lhs, const std::basic_string<T>& rhs)
S32 LLStringUtilBase<T>::compareInsensitive(const string_type& lhs, const string_type& rhs)
{
std::basic_string<T> lhs_string(lhs);
std::basic_string<T> rhs_string(rhs);
string_type lhs_string(lhs);
string_type rhs_string(rhs);
LLStringUtilBase<T>::toUpper(lhs_string);
LLStringUtilBase<T>::toUpper(rhs_string);
return LLStringOps::collate(lhs_string.c_str(), rhs_string.c_str());
@ -720,7 +1086,7 @@ S32 LLStringUtilBase<T>::compareInsensitive(const std::basic_string<T>& lhs, con
//static
template<class T>
S32 LLStringUtilBase<T>::compareDict(const std::basic_string<T>& astr, const std::basic_string<T>& bstr)
S32 LLStringUtilBase<T>::compareDict(const string_type& astr, const string_type& bstr)
{
const T* a = astr.c_str();
const T* b = bstr.c_str();
@ -761,7 +1127,7 @@ S32 LLStringUtilBase<T>::compareDict(const std::basic_string<T>& astr, const std
// static
template<class T>
S32 LLStringUtilBase<T>::compareDictInsensitive(const std::basic_string<T>& astr, const std::basic_string<T>& bstr)
S32 LLStringUtilBase<T>::compareDictInsensitive(const string_type& astr, const string_type& bstr)
{
const T* a = astr.c_str();
const T* b = bstr.c_str();
@ -796,7 +1162,7 @@ S32 LLStringUtilBase<T>::compareDictInsensitive(const std::basic_string<T>& astr
// Puts compareDict() in a form appropriate for LL container classes to use for sorting.
// static
template<class T>
BOOL LLStringUtilBase<T>::precedesDict( const std::basic_string<T>& a, const std::basic_string<T>& b )
BOOL LLStringUtilBase<T>::precedesDict( const string_type& a, const string_type& b )
{
if( a.size() && b.size() )
{
@ -810,7 +1176,7 @@ BOOL LLStringUtilBase<T>::precedesDict( const std::basic_string<T>& a, const std
//static
template<class T>
void LLStringUtilBase<T>::toUpper(std::basic_string<T>& string)
void LLStringUtilBase<T>::toUpper(string_type& string)
{
if( !string.empty() )
{
@ -824,7 +1190,7 @@ void LLStringUtilBase<T>::toUpper(std::basic_string<T>& string)
//static
template<class T>
void LLStringUtilBase<T>::toLower(std::basic_string<T>& string)
void LLStringUtilBase<T>::toLower(string_type& string)
{
if( !string.empty() )
{
@ -838,7 +1204,7 @@ void LLStringUtilBase<T>::toLower(std::basic_string<T>& string)
//static
template<class T>
void LLStringUtilBase<T>::trimHead(std::basic_string<T>& string)
void LLStringUtilBase<T>::trimHead(string_type& string)
{
if( !string.empty() )
{
@ -853,7 +1219,7 @@ void LLStringUtilBase<T>::trimHead(std::basic_string<T>& string)
//static
template<class T>
void LLStringUtilBase<T>::trimTail(std::basic_string<T>& string)
void LLStringUtilBase<T>::trimTail(string_type& string)
{
if( string.size() )
{
@ -872,7 +1238,7 @@ void LLStringUtilBase<T>::trimTail(std::basic_string<T>& string)
// Replace line feeds with carriage return-line feed pairs.
//static
template<class T>
void LLStringUtilBase<T>::addCRLF(std::basic_string<T>& string)
void LLStringUtilBase<T>::addCRLF(string_type& string)
{
const T LF = 10;
const T CR = 13;
@ -914,7 +1280,7 @@ void LLStringUtilBase<T>::addCRLF(std::basic_string<T>& string)
// Remove all carriage returns
//static
template<class T>
void LLStringUtilBase<T>::removeCRLF(std::basic_string<T>& string)
void LLStringUtilBase<T>::removeCRLF(string_type& string)
{
const T CR = 13;
@ -935,10 +1301,10 @@ void LLStringUtilBase<T>::removeCRLF(std::basic_string<T>& string)
//static
template<class T>
void LLStringUtilBase<T>::replaceChar( std::basic_string<T>& string, T target, T replacement )
void LLStringUtilBase<T>::replaceChar( string_type& string, T target, T replacement )
{
size_type found_pos = 0;
while( (found_pos = string.find(target, found_pos)) != std::basic_string<T>::npos )
while( (found_pos = string.find(target, found_pos)) != string_type::npos )
{
string[found_pos] = replacement;
found_pos++; // avoid infinite defeat if target == replacement
@ -947,10 +1313,10 @@ void LLStringUtilBase<T>::replaceChar( std::basic_string<T>& string, T target, T
//static
template<class T>
void LLStringUtilBase<T>::replaceString( std::basic_string<T>& string, std::basic_string<T> target, std::basic_string<T> replacement )
void LLStringUtilBase<T>::replaceString( string_type& string, string_type target, string_type replacement )
{
size_type found_pos = 0;
while( (found_pos = string.find(target, found_pos)) != std::basic_string<T>::npos )
while( (found_pos = string.find(target, found_pos)) != string_type::npos )
{
string.replace( found_pos, target.length(), replacement );
found_pos += replacement.length(); // avoid infinite defeat if replacement contains target
@ -959,7 +1325,7 @@ void LLStringUtilBase<T>::replaceString( std::basic_string<T>& string, std::basi
//static
template<class T>
void LLStringUtilBase<T>::replaceNonstandardASCII( std::basic_string<T>& string, T replacement )
void LLStringUtilBase<T>::replaceNonstandardASCII( string_type& string, T replacement )
{
const char LF = 10;
const S8 MIN = 32;
@ -979,12 +1345,12 @@ void LLStringUtilBase<T>::replaceNonstandardASCII( std::basic_string<T>& string,
//static
template<class T>
void LLStringUtilBase<T>::replaceTabsWithSpaces( std::basic_string<T>& str, size_type spaces_per_tab )
void LLStringUtilBase<T>::replaceTabsWithSpaces( string_type& str, size_type spaces_per_tab )
{
const T TAB = '\t';
const T SPACE = ' ';
std::basic_string<T> out_str;
string_type out_str;
// Replace tabs with spaces
for (size_type i = 0; i < str.length(); i++)
{
@ -1003,7 +1369,7 @@ void LLStringUtilBase<T>::replaceTabsWithSpaces( std::basic_string<T>& str, size
//static
template<class T>
BOOL LLStringUtilBase<T>::containsNonprintable(const std::basic_string<T>& string)
BOOL LLStringUtilBase<T>::containsNonprintable(const string_type& string)
{
const char MIN = 32;
BOOL rv = FALSE;
@ -1020,7 +1386,7 @@ BOOL LLStringUtilBase<T>::containsNonprintable(const std::basic_string<T>& strin
//static
template<class T>
void LLStringUtilBase<T>::stripNonprintable(std::basic_string<T>& string)
void LLStringUtilBase<T>::stripNonprintable(string_type& string)
{
const char MIN = 32;
size_type j = 0;
@ -1051,8 +1417,43 @@ void LLStringUtilBase<T>::stripNonprintable(std::basic_string<T>& string)
delete []c_string;
}
template<class T>
std::basic_string<T> LLStringUtilBase<T>::quote(const string_type& str,
const string_type& triggers,
const string_type& escape)
{
size_type len(str.length());
// If the string is already quoted, assume user knows what s/he's doing.
if (len >= 2 && str[0] == '"' && str[len-1] == '"')
{
return str;
}
// Not already quoted: do we need to? triggers.empty() is a special case
// meaning "always quote."
if ((! triggers.empty()) && str.find_first_of(triggers) == string_type::npos)
{
// no trigger characters, don't bother quoting
return str;
}
// For whatever reason, we must quote this string.
string_type result;
result.push_back('"');
for (typename string_type::const_iterator ci(str.begin()), cend(str.end()); ci != cend; ++ci)
{
if (*ci == '"')
{
result.append(escape);
}
result.push_back(*ci);
}
result.push_back('"');
return result;
}
template<class T>
void LLStringUtilBase<T>::_makeASCII(std::basic_string<T>& string)
void LLStringUtilBase<T>::_makeASCII(string_type& string)
{
// Replace non-ASCII chars with LL_UNKNOWN_CHAR
for (size_type i = 0; i < string.length(); i++)
@ -1082,7 +1483,7 @@ void LLStringUtilBase<T>::copy( T* dst, const T* src, size_type dst_size )
// static
template<class T>
void LLStringUtilBase<T>::copyInto(std::basic_string<T>& dst, const std::basic_string<T>& src, size_type offset)
void LLStringUtilBase<T>::copyInto(string_type& dst, const string_type& src, size_type offset)
{
if ( offset == dst.length() )
{
@ -1092,7 +1493,7 @@ void LLStringUtilBase<T>::copyInto(std::basic_string<T>& dst, const std::basic_s
}
else
{
std::basic_string<T> tail = dst.substr(offset);
string_type tail = dst.substr(offset);
dst = dst.substr(0, offset);
dst += src;
@ -1103,7 +1504,7 @@ void LLStringUtilBase<T>::copyInto(std::basic_string<T>& dst, const std::basic_s
// True if this is the head of s.
//static
template<class T>
BOOL LLStringUtilBase<T>::isHead( const std::basic_string<T>& string, const T* s )
BOOL LLStringUtilBase<T>::isHead( const string_type& string, const T* s )
{
if( string.empty() )
{
@ -1119,8 +1520,8 @@ BOOL LLStringUtilBase<T>::isHead( const std::basic_string<T>& string, const T* s
// static
template<class T>
bool LLStringUtilBase<T>::startsWith(
const std::basic_string<T>& string,
const std::basic_string<T>& substr)
const string_type& string,
const string_type& substr)
{
if(string.empty() || (substr.empty())) return false;
if(0 == string.find(substr)) return true;
@ -1130,8 +1531,8 @@ bool LLStringUtilBase<T>::startsWith(
// static
template<class T>
bool LLStringUtilBase<T>::endsWith(
const std::basic_string<T>& string,
const std::basic_string<T>& substr)
const string_type& string,
const string_type& substr)
{
if(string.empty() || (substr.empty())) return false;
std::string::size_type idx = string.rfind(substr);
@ -1141,14 +1542,14 @@ bool LLStringUtilBase<T>::endsWith(
template<class T>
BOOL LLStringUtilBase<T>::convertToBOOL(const std::basic_string<T>& string, BOOL& value)
BOOL LLStringUtilBase<T>::convertToBOOL(const string_type& string, BOOL& value)
{
if( string.empty() )
{
return FALSE;
}
std::basic_string<T> temp( string );
string_type temp( string );
trim(temp);
if(
(temp == "1") ||
@ -1178,7 +1579,7 @@ BOOL LLStringUtilBase<T>::convertToBOOL(const std::basic_string<T>& string, BOOL
}
template<class T>
BOOL LLStringUtilBase<T>::convertToU8(const std::basic_string<T>& string, U8& value)
BOOL LLStringUtilBase<T>::convertToU8(const string_type& string, U8& value)
{
S32 value32 = 0;
BOOL success = convertToS32(string, value32);
@ -1191,7 +1592,7 @@ BOOL LLStringUtilBase<T>::convertToU8(const std::basic_string<T>& string, U8& va
}
template<class T>
BOOL LLStringUtilBase<T>::convertToS8(const std::basic_string<T>& string, S8& value)
BOOL LLStringUtilBase<T>::convertToS8(const string_type& string, S8& value)
{
S32 value32 = 0;
BOOL success = convertToS32(string, value32);
@ -1204,7 +1605,7 @@ BOOL LLStringUtilBase<T>::convertToS8(const std::basic_string<T>& string, S8& va
}
template<class T>
BOOL LLStringUtilBase<T>::convertToS16(const std::basic_string<T>& string, S16& value)
BOOL LLStringUtilBase<T>::convertToS16(const string_type& string, S16& value)
{
S32 value32 = 0;
BOOL success = convertToS32(string, value32);
@ -1217,7 +1618,7 @@ BOOL LLStringUtilBase<T>::convertToS16(const std::basic_string<T>& string, S16&
}
template<class T>
BOOL LLStringUtilBase<T>::convertToU16(const std::basic_string<T>& string, U16& value)
BOOL LLStringUtilBase<T>::convertToU16(const string_type& string, U16& value)
{
S32 value32 = 0;
BOOL success = convertToS32(string, value32);
@ -1230,17 +1631,17 @@ BOOL LLStringUtilBase<T>::convertToU16(const std::basic_string<T>& string, U16&
}
template<class T>
BOOL LLStringUtilBase<T>::convertToU32(const std::basic_string<T>& string, U32& value)
BOOL LLStringUtilBase<T>::convertToU32(const string_type& string, U32& value)
{
if( string.empty() )
{
return FALSE;
}
std::basic_string<T> temp( string );
string_type temp( string );
trim(temp);
U32 v;
std::basic_istringstream<T> i_stream((std::basic_string<T>)temp);
std::basic_istringstream<T> i_stream((string_type)temp);
if(i_stream >> v)
{
value = v;
@ -1250,17 +1651,17 @@ BOOL LLStringUtilBase<T>::convertToU32(const std::basic_string<T>& string, U32&
}
template<class T>
BOOL LLStringUtilBase<T>::convertToS32(const std::basic_string<T>& string, S32& value)
BOOL LLStringUtilBase<T>::convertToS32(const string_type& string, S32& value)
{
if( string.empty() )
{
return FALSE;
}
std::basic_string<T> temp( string );
string_type temp( string );
trim(temp);
S32 v;
std::basic_istringstream<T> i_stream((std::basic_string<T>)temp);
std::basic_istringstream<T> i_stream((string_type)temp);
if(i_stream >> v)
{
//TODO: figure out overflow and underflow reporting here
@ -1277,7 +1678,7 @@ BOOL LLStringUtilBase<T>::convertToS32(const std::basic_string<T>& string, S32&
}
template<class T>
BOOL LLStringUtilBase<T>::convertToF32(const std::basic_string<T>& string, F32& value)
BOOL LLStringUtilBase<T>::convertToF32(const string_type& string, F32& value)
{
F64 value64 = 0.0;
BOOL success = convertToF64(string, value64);
@ -1290,17 +1691,17 @@ BOOL LLStringUtilBase<T>::convertToF32(const std::basic_string<T>& string, F32&
}
template<class T>
BOOL LLStringUtilBase<T>::convertToF64(const std::basic_string<T>& string, F64& value)
BOOL LLStringUtilBase<T>::convertToF64(const string_type& string, F64& value)
{
if( string.empty() )
{
return FALSE;
}
std::basic_string<T> temp( string );
string_type temp( string );
trim(temp);
F64 v;
std::basic_istringstream<T> i_stream((std::basic_string<T>)temp);
std::basic_istringstream<T> i_stream((string_type)temp);
if(i_stream >> v)
{
//TODO: figure out overflow and underflow reporting here
@ -1317,7 +1718,7 @@ BOOL LLStringUtilBase<T>::convertToF64(const std::basic_string<T>& string, F64&
}
template<class T>
void LLStringUtilBase<T>::truncate(std::basic_string<T>& string, size_type count)
void LLStringUtilBase<T>::truncate(string_type& string, size_type count)
{
size_type cur_size = string.size();
string.resize(count < cur_size ? count : cur_size);

View File

@ -0,0 +1,110 @@
/**
* @file lltypeinfolookup.h
* @author Nat Goodspeed
* @date 2012-04-08
* @brief Template data structure like std::map<std::type_info*, T>
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Copyright (c) 2012, Linden Research, Inc.
* $/LicenseInfo$
*/
#if ! defined(LL_LLTYPEINFOLOOKUP_H)
#define LL_LLTYPEINFOLOOKUP_H
#include "llsortedvector.h"
#include <typeinfo>
/**
* LLTypeInfoLookup is specifically designed for use cases for which you might
* consider std::map<std::type_info*, VALUE>. We have several such data
* structures in the viewer. The trouble with them is that at least on Linux,
* you can't rely on always getting the same std::type_info* for a given type:
* different load modules will produce different std::type_info*.
* LLTypeInfoLookup contains a workaround to address this issue.
*
* Specifically, when we don't find the passed std::type_info*,
* LLTypeInfoLookup performs a linear search over registered entries to
* compare name() strings. Presuming that this succeeds, we cache the new
* (previously unrecognized) std::type_info* to speed future lookups.
*
* This worst-case fallback search (linear search with string comparison)
* should only happen the first time we look up a given type from a particular
* load module other than the one from which we initially registered types.
* (However, a lookup which wouldn't succeed anyway will always have
* worst-case performance.) This class is probably best used with less than a
* few dozen different types.
*/
template <typename VALUE>
class LLTypeInfoLookup
{
public:
typedef LLTypeInfoLookup<VALUE> self;
typedef LLSortedVector<const std::type_info*, VALUE> vector_type;
typedef typename vector_type::key_type key_type;
typedef typename vector_type::mapped_type mapped_type;
typedef typename vector_type::value_type value_type;
typedef typename vector_type::iterator iterator;
typedef typename vector_type::const_iterator const_iterator;
LLTypeInfoLookup() {}
iterator begin() { return mVector.begin(); }
iterator end() { return mVector.end(); }
const_iterator begin() const { return mVector.begin(); }
const_iterator end() const { return mVector.end(); }
bool empty() const { return mVector.empty(); }
std::size_t size() const { return mVector.size(); }
std::pair<iterator, bool> insert(const std::type_info* key, const VALUE& value)
{
return insert(value_type(key, value));
}
std::pair<iterator, bool> insert(const value_type& pair)
{
return mVector.insert(pair);
}
// const find() forwards to non-const find(): this can alter mVector!
const_iterator find(const std::type_info* key) const
{
return const_cast<self*>(this)->find(key);
}
// non-const find() caches previously-unknown type_info* to speed future
// lookups.
iterator find(const std::type_info* key)
{
iterator found = mVector.find(key);
if (found != mVector.end())
{
// If LLSortedVector::find() found, great, we're done.
return found;
}
// Here we didn't find the passed type_info*. On Linux, though, even
// for the same type, typeid(sametype) produces a different type_info*
// when used in different load modules. So the fact that we didn't
// find the type_info* we seek doesn't mean this type isn't
// registered. Scan for matching name() string.
for (typename vector_type::iterator ti(mVector.begin()), tend(mVector.end());
ti != tend; ++ti)
{
if (std::string(ti->first->name()) == key->name())
{
// This unrecognized 'key' is for the same type as ti->first.
// To speed future lookups, insert a new entry that lets us
// look up ti->second using this same 'key'.
return insert(key, ti->second).first;
}
}
// We simply have never seen a type with this type_info* from any load
// module.
return mVector.end();
}
private:
vector_type mVector;
};
#endif /* ! defined(LL_LLTYPEINFOLOOKUP_H) */

View File

@ -29,7 +29,7 @@
const S32 LL_VERSION_MAJOR = 3;
const S32 LL_VERSION_MINOR = 3;
const S32 LL_VERSION_PATCH = 2;
const S32 LL_VERSION_PATCH = 3;
const S32 LL_VERSION_BUILD = 0;
const char * const LL_CHANNEL = "Second Life Developer";

View File

@ -0,0 +1,37 @@
/**
* @file StringVec.h
* @author Nat Goodspeed
* @date 2012-02-24
* @brief Extend TUT ensure_equals() to handle std::vector<std::string>
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Copyright (c) 2012, Linden Research, Inc.
* $/LicenseInfo$
*/
#if ! defined(LL_STRINGVEC_H)
#define LL_STRINGVEC_H
#include <vector>
#include <string>
#include <iostream>
typedef std::vector<std::string> StringVec;
std::ostream& operator<<(std::ostream& out, const StringVec& strings)
{
out << '(';
StringVec::const_iterator begin(strings.begin()), end(strings.end());
if (begin != end)
{
out << '"' << *begin << '"';
while (++begin != end)
{
out << ", \"" << *begin << '"';
}
}
out << ')';
return out;
}
#endif /* ! defined(LL_STRINGVEC_H) */

View File

@ -30,6 +30,8 @@
#define LL_LISTENER_H
#include "llsd.h"
#include "llevents.h"
#include "tests/StringVec.h"
#include <iostream>
/*****************************************************************************
@ -133,24 +135,7 @@ struct Collect
return false;
}
void clear() { result.clear(); }
typedef std::vector<std::string> StringList;
StringList result;
StringVec result;
};
std::ostream& operator<<(std::ostream& out, const Collect::StringList& strings)
{
out << '(';
Collect::StringList::const_iterator begin(strings.begin()), end(strings.end());
if (begin != end)
{
out << '"' << *begin << '"';
while (++begin != end)
{
out << ", \"" << *begin << '"';
}
}
out << ')';
return out;
}
#endif /* ! defined(LL_LISTENER_H) */

View File

@ -1,4 +1,4 @@
/**
/**
* @file llerror_test.cpp
* @date December 2006
* @brief error unit tests
@ -6,21 +6,21 @@
* $LicenseInfo:firstyear=2006&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@ -49,7 +49,7 @@ namespace
static bool fatalWasCalled;
void fatalCall(const std::string&) { fatalWasCalled = true; }
}
namespace tut
{
class TestRecorder : public LLError::Recorder
@ -57,59 +57,65 @@ namespace tut
public:
TestRecorder() : mWantsTime(false) { }
~TestRecorder() { LLError::removeRecorder(this); }
void recordMessage(LLError::ELevel level,
const std::string& message)
{
mMessages.push_back(message);
}
int countMessages() { return (int) mMessages.size(); }
void clearMessages() { mMessages.clear(); }
void setWantsTime(bool t) { mWantsTime = t; }
bool wantsTime() { return mWantsTime; }
std::string message(int n)
{
std::ostringstream test_name;
test_name << "testing message " << n << ", not enough messages";
tut::ensure(test_name.str(), n < countMessages());
return mMessages[n];
}
private:
typedef std::vector<std::string> MessageVector;
MessageVector mMessages;
bool mWantsTime;
};
struct ErrorTestData
{
TestRecorder mRecorder;
// addRecorder() expects to be able to later delete the passed
// Recorder*. Even though removeRecorder() reclaims ownership, passing
// a pointer to a data member rather than a heap Recorder subclass
// instance would just be Wrong.
TestRecorder* mRecorder;
LLError::Settings* mPriorErrorSettings;
ErrorTestData()
ErrorTestData():
mRecorder(new TestRecorder)
{
fatalWasCalled = false;
mPriorErrorSettings = LLError::saveAndResetSettings();
LLError::setDefaultLevel(LLError::LEVEL_DEBUG);
LLError::setFatalFunction(fatalCall);
LLError::addRecorder(&mRecorder);
LLError::addRecorder(mRecorder);
}
~ErrorTestData()
{
LLError::removeRecorder(&mRecorder);
LLError::removeRecorder(mRecorder);
delete mRecorder;
LLError::restoreSettings(mPriorErrorSettings);
}
void ensure_message_count(int expectedCount)
{
ensure_equals("message count", mRecorder.countMessages(), expectedCount);
ensure_equals("message count", mRecorder->countMessages(), expectedCount);
}
void ensure_message_contains(int n, const std::string& expectedText)
@ -117,7 +123,7 @@ namespace tut
std::ostringstream test_name;
test_name << "testing message " << n;
ensure_contains(test_name.str(), mRecorder.message(n), expectedText);
ensure_contains(test_name.str(), mRecorder->message(n), expectedText);
}
void ensure_message_does_not_contain(int n, const std::string& expectedText)
@ -125,22 +131,22 @@ namespace tut
std::ostringstream test_name;
test_name << "testing message " << n;
ensure_does_not_contain(test_name.str(), mRecorder.message(n), expectedText);
ensure_does_not_contain(test_name.str(), mRecorder->message(n), expectedText);
}
};
typedef test_group<ErrorTestData> ErrorTestGroup;
typedef ErrorTestGroup::object ErrorTestObject;
ErrorTestGroup errorTestGroup("error");
template<> template<>
void ErrorTestObject::test<1>()
// basic test of output
{
llinfos << "test" << llendl;
llinfos << "bob" << llendl;
ensure_message_contains(0, "test");
ensure_message_contains(1, "bob");
}
@ -159,7 +165,7 @@ namespace
};
namespace tut
{
{
template<> template<>
void ErrorTestObject::test<2>()
// messages are filtered based on default level
@ -172,7 +178,7 @@ namespace tut
ensure_message_contains(3, "error");
ensure_message_contains(4, "four");
ensure_message_count(5);
LLError::setDefaultLevel(LLError::LEVEL_INFO);
writeSome();
ensure_message_contains(5, "two");
@ -180,20 +186,20 @@ namespace tut
ensure_message_contains(7, "error");
ensure_message_contains(8, "four");
ensure_message_count(9);
LLError::setDefaultLevel(LLError::LEVEL_WARN);
writeSome();
ensure_message_contains(9, "three");
ensure_message_contains(10, "error");
ensure_message_contains(11, "four");
ensure_message_count(12);
LLError::setDefaultLevel(LLError::LEVEL_ERROR);
writeSome();
ensure_message_contains(12, "error");
ensure_message_contains(13, "four");
ensure_message_count(14);
LLError::setDefaultLevel(LLError::LEVEL_NONE);
writeSome();
ensure_message_count(14);
@ -218,14 +224,14 @@ namespace tut
{
std::string thisFile = __FILE__;
std::string abbreviateFile = LLError::abbreviateFile(thisFile);
ensure_ends_with("file name abbreviation",
abbreviateFile,
"llcommon/tests/llerror_test.cpp"
);
ensure_does_not_contain("file name abbreviation",
abbreviateFile, "indra");
std::string someFile =
#if LL_WINDOWS
"C:/amy/bob/cam.cpp"
@ -234,12 +240,12 @@ namespace tut
#endif
;
std::string someAbbreviation = LLError::abbreviateFile(someFile);
ensure_equals("non-indra file abbreviation",
someAbbreviation, someFile);
}
}
namespace
{
std::string locationString(int line)
@ -247,22 +253,22 @@ namespace
std::ostringstream location;
location << LLError::abbreviateFile(__FILE__)
<< "(" << line << ") : ";
return location.str();
}
std::string writeReturningLocation()
{
llinfos << "apple" << llendl; int this_line = __LINE__;
return locationString(this_line);
}
std::string writeReturningLocationAndFunction()
{
llinfos << "apple" << llendl; int this_line = __LINE__;
return locationString(this_line) + __FUNCTION__;
}
std::string errorReturningLocation()
{
llerrs << "die" << llendl; int this_line = __LINE__;
@ -271,20 +277,20 @@ namespace
}
namespace tut
{
{
template<> template<>
void ErrorTestObject::test<5>()
// file and line information in log messages
{
std::string location = writeReturningLocation();
// expecting default to not print location information
LLError::setPrintLocation(true);
writeReturningLocation();
LLError::setPrintLocation(false);
writeReturningLocation();
ensure_message_does_not_contain(0, location);
ensure_message_contains(1, location);
ensure_message_does_not_contain(2, location);
@ -297,7 +303,7 @@ namespace tut
existing log messages often do.) The functions all return their C++
name so that test can be substantial mechanized.
*/
std::string logFromGlobal(bool id)
{
llinfos << (id ? "logFromGlobal: " : "") << "hi" << llendl;
@ -345,7 +351,7 @@ namespace
return "ClassWithNoLogType::logFromStatic";
}
};
class ClassWithLogType {
LOG_CLASS(ClassWithLogType);
public:
@ -360,13 +366,13 @@ namespace
return "ClassWithLogType::logFromStatic";
}
};
std::string logFromNamespace(bool id) { return Foo::logFromNamespace(id); }
std::string logFromClassWithNoLogTypeMember(bool id) { ClassWithNoLogType c; return c.logFromMember(id); }
std::string logFromClassWithNoLogTypeStatic(bool id) { return ClassWithNoLogType::logFromStatic(id); }
std::string logFromClassWithLogTypeMember(bool id) { ClassWithLogType c; return c.logFromMember(id); }
std::string logFromClassWithLogTypeStatic(bool id) { return ClassWithLogType::logFromStatic(id); }
void ensure_has(const std::string& message,
const std::string& actual, const std::string& expected)
{
@ -379,18 +385,18 @@ namespace
throw tut::failure(ss.str().c_str());
}
}
typedef std::string (*LogFromFunction)(bool);
void testLogName(tut::TestRecorder& recorder, LogFromFunction f,
void testLogName(tut::TestRecorder* recorder, LogFromFunction f,
const std::string& class_name = "")
{
recorder.clearMessages();
recorder->clearMessages();
std::string name = f(false);
f(true);
std::string messageWithoutName = recorder.message(0);
std::string messageWithName = recorder.message(1);
std::string messageWithoutName = recorder->message(0);
std::string messageWithName = recorder->message(1);
ensure_has(name + " logged without name",
messageWithoutName, name);
ensure_has(name + " logged with name",
@ -431,18 +437,18 @@ namespace
llinfos << "inside" << llendl;
return "moo";
}
std::string outerLogger()
{
llinfos << "outside(" << innerLogger() << ")" << llendl;
return "bar";
}
void uberLogger()
{
llinfos << "uber(" << outerLogger() << "," << innerLogger() << ")" << llendl;
}
class LogWhileLogging
{
public:
@ -461,11 +467,11 @@ namespace
LogWhileLogging l;
llinfos << "meta(" << l << ")" << llendl;
}
}
namespace tut
{
{
template<> template<>
// handle nested logging
void ErrorTestObject::test<7>()
@ -474,31 +480,31 @@ namespace tut
ensure_message_contains(0, "inside");
ensure_message_contains(1, "outside(moo)");
ensure_message_count(2);
uberLogger();
ensure_message_contains(2, "inside");
ensure_message_contains(3, "inside");
ensure_message_contains(4, "outside(moo)");
ensure_message_contains(5, "uber(bar,moo)");
ensure_message_count(6);
metaLogger();
ensure_message_contains(6, "logging");
ensure_message_contains(7, "meta(baz)");
ensure_message_count(8);
}
template<> template<>
// special handling of llerrs calls
void ErrorTestObject::test<8>()
{
LLError::setPrintLocation(false);
std::string location = errorReturningLocation();
ensure_message_contains(0, location + "error");
ensure_message_contains(1, "die");
ensure_message_count(2);
ensure("fatal callback called", fatalWasCalled);
}
}
@ -509,7 +515,7 @@ namespace
{
return "1947-07-08T03:04:05Z";
}
void ufoSighting()
{
llinfos << "ufo" << llendl;
@ -517,35 +523,35 @@ namespace
}
namespace tut
{
{
template<> template<>
// time in output (for recorders that need it)
void ErrorTestObject::test<9>()
{
LLError::setTimeFunction(roswell);
mRecorder.setWantsTime(false);
mRecorder->setWantsTime(false);
ufoSighting();
ensure_message_contains(0, "ufo");
ensure_message_does_not_contain(0, roswell());
mRecorder.setWantsTime(true);
mRecorder->setWantsTime(true);
ufoSighting();
ensure_message_contains(1, "ufo");
ensure_message_contains(1, roswell());
}
template<> template<>
// output order
void ErrorTestObject::test<10>()
{
LLError::setPrintLocation(true);
LLError::setTimeFunction(roswell);
mRecorder.setWantsTime(true);
mRecorder->setWantsTime(true);
std::string locationAndFunction = writeReturningLocationAndFunction();
ensure_equals("order is time type location function message",
mRecorder.message(0),
mRecorder->message(0),
roswell() + " INFO: " + locationAndFunction + ": apple");
}
@ -553,30 +559,30 @@ namespace tut
// multiple recorders
void ErrorTestObject::test<11>()
{
TestRecorder altRecorder;
LLError::addRecorder(&altRecorder);
TestRecorder* altRecorder(new TestRecorder);
LLError::addRecorder(altRecorder);
llinfos << "boo" << llendl;
ensure_message_contains(0, "boo");
ensure_equals("alt recorder count", altRecorder.countMessages(), 1);
ensure_contains("alt recorder message 0", altRecorder.message(0), "boo");
ensure_equals("alt recorder count", altRecorder->countMessages(), 1);
ensure_contains("alt recorder message 0", altRecorder->message(0), "boo");
LLError::setTimeFunction(roswell);
TestRecorder anotherRecorder;
anotherRecorder.setWantsTime(true);
LLError::addRecorder(&anotherRecorder);
TestRecorder* anotherRecorder(new TestRecorder);
anotherRecorder->setWantsTime(true);
LLError::addRecorder(anotherRecorder);
llinfos << "baz" << llendl;
std::string when = roswell();
ensure_message_does_not_contain(1, when);
ensure_equals("alt recorder count", altRecorder.countMessages(), 2);
ensure_does_not_contain("alt recorder message 1", altRecorder.message(1), when);
ensure_equals("another recorder count", anotherRecorder.countMessages(), 1);
ensure_contains("another recorder message 0", anotherRecorder.message(0), when);
ensure_equals("alt recorder count", altRecorder->countMessages(), 2);
ensure_does_not_contain("alt recorder message 1", altRecorder->message(1), when);
ensure_equals("another recorder count", anotherRecorder->countMessages(), 1);
ensure_contains("another recorder message 0", anotherRecorder->message(0), when);
}
}
@ -610,10 +616,10 @@ namespace tut
{
LLError::setDefaultLevel(LLError::LEVEL_WARN);
LLError::setClassLevel("TestBeta", LLError::LEVEL_INFO);
TestAlpha::doAll();
TestBeta::doAll();
ensure_message_contains(0, "aim west");
ensure_message_contains(1, "error");
ensure_message_contains(2, "ate eels");
@ -623,7 +629,7 @@ namespace tut
ensure_message_contains(6, "big easy");
ensure_message_count(7);
}
template<> template<>
// filtering by function, and that it will override class filtering
void ErrorTestObject::test<13>()
@ -632,13 +638,13 @@ namespace tut
LLError::setClassLevel("TestBeta", LLError::LEVEL_WARN);
LLError::setFunctionLevel("TestBeta::doInfo", LLError::LEVEL_DEBUG);
LLError::setFunctionLevel("TestBeta::doError", LLError::LEVEL_NONE);
TestBeta::doAll();
ensure_message_contains(0, "buy iron");
ensure_message_contains(1, "bad word");
ensure_message_count(2);
}
template<> template<>
// filtering by file
// and that it is overridden by both class and function filtering
@ -652,7 +658,7 @@ namespace tut
LLError::LEVEL_NONE);
LLError::setFunctionLevel("TestBeta::doError",
LLError::LEVEL_NONE);
TestAlpha::doAll();
TestBeta::doAll();
ensure_message_contains(0, "any idea");
@ -660,7 +666,7 @@ namespace tut
ensure_message_contains(2, "bad word");
ensure_message_count(3);
}
template<> template<>
// proper cached, efficient lookup of filtering
void ErrorTestObject::test<15>()
@ -690,7 +696,7 @@ namespace tut
ensure_message_count(2);
ensure_equals("sixth check", LLError::shouldLogCallCount(), 3);
}
template<> template<>
// configuration from LLSD
void ErrorTestObject::test<16>()
@ -699,26 +705,26 @@ namespace tut
LLSD config;
config["print-location"] = true;
config["default-level"] = "DEBUG";
LLSD set1;
set1["level"] = "WARN";
set1["files"][0] = this_file;
LLSD set2;
set2["level"] = "INFO";
set2["classes"][0] = "TestAlpha";
LLSD set3;
set3["level"] = "NONE";
set3["functions"][0] = "TestAlpha::doError";
set3["functions"][1] = "TestBeta::doError";
config["settings"][0] = set1;
config["settings"][1] = set2;
config["settings"][2] = set3;
LLError::configure(config);
TestAlpha::doAll();
TestBeta::doAll();
ensure_message_contains(0, "any idea");
@ -726,13 +732,13 @@ namespace tut
ensure_message_contains(1, "aim west");
ensure_message_contains(2, "bad word");
ensure_message_count(3);
// make sure reconfiguring works
LLSD config2;
config2["default-level"] = "WARN";
LLError::configure(config2);
TestAlpha::doAll();
TestBeta::doAll();
ensure_message_contains(3, "aim west");
@ -744,13 +750,13 @@ namespace tut
ensure_message_contains(8, "big easy");
ensure_message_count(9);
}
}
}
/* Tests left:
handling of classes without LOG_CLASS
live update of filtering from file
live update of filtering from file
syslog recorder
file recorder
cerr/stderr recorder

View File

@ -35,6 +35,7 @@
#include <vector>
#include <set>
#include <algorithm> // std::sort()
#include <stdexcept>
// std headers
// external library headers
#include <boost/scoped_ptr.hpp>
@ -42,6 +43,11 @@
#include "../test/lltut.h"
#include "wrapllerrs.h"
struct Badness: public std::runtime_error
{
Badness(const std::string& what): std::runtime_error(what) {}
};
struct Keyed: public LLInstanceTracker<Keyed, std::string>
{
Keyed(const std::string& name):
@ -53,6 +59,17 @@ struct Keyed: public LLInstanceTracker<Keyed, std::string>
struct Unkeyed: public LLInstanceTracker<Unkeyed>
{
Unkeyed(const std::string& thrw="")
{
// LLInstanceTracker should respond appropriately if a subclass
// constructor throws an exception. Specifically, it should run
// LLInstanceTracker's destructor and remove itself from the
// underlying container.
if (! thrw.empty())
{
throw Badness(thrw);
}
}
};
/*****************************************************************************
@ -95,6 +112,7 @@ namespace tut
void object::test<2>()
{
ensure_equals(Unkeyed::instanceCount(), 0);
Unkeyed* dangling = NULL;
{
Unkeyed one;
ensure_equals(Unkeyed::instanceCount(), 1);
@ -107,7 +125,11 @@ namespace tut
ensure_equals(found, two.get());
}
ensure_equals(Unkeyed::instanceCount(), 1);
}
// store an unwise pointer to a temp Unkeyed instance
dangling = &one;
} // make that instance vanish
// check the now-invalid pointer to the destroyed instance
ensure("getInstance(T*) failed to track destruction", ! Unkeyed::getInstance(dangling));
ensure_equals(Unkeyed::instanceCount(), 0);
}
@ -229,4 +251,49 @@ namespace tut
}
ensure(! what.empty());
}
template<> template<>
void object::test<8>()
{
set_test_name("exception in subclass ctor");
typedef std::set<Unkeyed*> InstanceSet;
InstanceSet existing;
// We can't use the iterator-range InstanceSet constructor because
// beginInstances() returns an iterator that dereferences to an
// Unkeyed&, not an Unkeyed*.
for (Unkeyed::instance_iter uki(Unkeyed::beginInstances()),
ukend(Unkeyed::endInstances());
uki != ukend; ++uki)
{
existing.insert(&*uki);
}
Unkeyed* puk = NULL;
try
{
// We don't expect the assignment to take place because we expect
// Unkeyed to respond to the non-empty string param by throwing.
// We know the LLInstanceTracker base-class constructor will have
// run before Unkeyed's constructor, therefore the new instance
// will have added itself to the underlying set. The whole
// question is, when Unkeyed's constructor throws, will
// LLInstanceTracker's destructor remove it from the set? I
// realize we're testing the C++ implementation more than
// Unkeyed's implementation, but this seems an important point to
// nail down.
puk = new Unkeyed("throw");
}
catch (const Badness&)
{
}
// Ensure that every member of the new, updated set of Unkeyed
// instances was also present in the original set. If that's not true,
// it's because our new Unkeyed ended up in the updated set despite
// its constructor exception.
for (Unkeyed::instance_iter uki(Unkeyed::beginInstances()),
ukend(Unkeyed::endInstances());
uki != ukend; ++uki)
{
ensure("failed to remove instance", existing.find(&*uki) != existing.end());
}
}
} // namespace tut

View File

@ -0,0 +1,694 @@
/**
* @file llleap_test.cpp
* @author Nat Goodspeed
* @date 2012-02-21
* @brief Test for llleap.
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Copyright (c) 2012, Linden Research, Inc.
* $/LicenseInfo$
*/
// Precompiled header
#include "linden_common.h"
// associated header
#include "llleap.h"
// STL headers
// std headers
// external library headers
#include <boost/assign/list_of.hpp>
#include <boost/lambda/lambda.hpp>
#include <boost/foreach.hpp>
// other Linden headers
#include "../test/lltut.h"
#include "../test/namedtempfile.h"
#include "../test/manageapr.h"
#include "../test/catch_and_store_what_in.h"
#include "wrapllerrs.h"
#include "llevents.h"
#include "llprocess.h"
#include "stringize.h"
#include "StringVec.h"
#include <functional>
using boost::assign::list_of;
static ManageAPR manager;
StringVec sv(const StringVec& listof) { return listof; }
#if defined(LL_WINDOWS)
#define sleep(secs) _sleep((secs) * 1000)
#endif
#if ! LL_WINDOWS
const size_t BUFFERED_LENGTH = 1023*1024; // try wrangling just under a megabyte of data
#else
// "Then there's Windows... sigh." The "very large message" test is flaky in a
// way that seems to point to either the OS (nonblocking writes to pipes) or
// possibly the apr_file_write() function. Poring over log messages reveals
// that at some point along the way apr_file_write() returns 11 (Resource
// temporarily unavailable, i.e. EAGAIN) and says it wrote 0 bytes -- even
// though it did write the chunk! Our next write attempt retries the same
// chunk, resulting in the chunk being duplicated at the child end, corrupting
// the data stream. Much as I would love to be able to fix it for real, such a
// fix would appear to require distinguishing bogus EAGAIN returns from real
// ones -- how?? Empirically this behavior is only observed when writing a
// "very large message". To be able to move forward at all, try to bypass this
// particular failure by adjusting the size of a "very large message" on
// Windows.
const size_t BUFFERED_LENGTH = 65336;
#endif // LL_WINDOWS
void waitfor(const std::vector<LLLeap*>& instances, int timeout=60)
{
int i;
for (i = 0; i < timeout; ++i)
{
// Every iteration, test whether any of the passed LLLeap instances
// still exist (are still running).
std::vector<LLLeap*>::const_iterator vli(instances.begin()), vlend(instances.end());
for ( ; vli != vlend; ++vli)
{
// getInstance() returns NULL if it's terminated/gone, non-NULL if
// it's still running
if (LLLeap::getInstance(*vli))
break;
}
// If we made it through all of 'instances' without finding one that's
// still running, we're done.
if (vli == vlend)
{
/*==========================================================================*|
std::cout << instances.size() << " LLLeap instances terminated in "
<< i << " seconds, proceeding" << std::endl;
|*==========================================================================*/
return;
}
// Found an instance that's still running. Wait and pump LLProcess.
sleep(1);
LLEventPumps::instance().obtain("mainloop").post(LLSD());
}
tut::ensure(STRINGIZE("at least 1 of " << instances.size()
<< " LLLeap instances timed out ("
<< timeout << " seconds) without terminating"),
i < timeout);
}
void waitfor(LLLeap* instance, int timeout=60)
{
std::vector<LLLeap*> instances;
instances.push_back(instance);
waitfor(instances, timeout);
}
/*****************************************************************************
* TUT
*****************************************************************************/
namespace tut
{
struct llleap_data
{
llleap_data():
reader(".py",
// This logic is adapted from vita.viewerclient.receiveEvent()
boost::lambda::_1 <<
"import re\n"
"import os\n"
"import sys\n"
"\n"
// Don't forget that this Python script is written to some
// temp directory somewhere! Its __file__ is useless in
// finding indra/lib/python. Use our __FILE__, with
// raw-string syntax to deal with Windows pathnames.
"mydir = os.path.dirname(r'" << __FILE__ << "')\n"
"try:\n"
" from llbase import llsd\n"
"except ImportError:\n"
// We expect mydir to be .../indra/llcommon/tests.
" sys.path.insert(0,\n"
" os.path.join(mydir, os.pardir, os.pardir, 'lib', 'python'))\n"
" from indra.base import llsd\n"
"\n"
"class ProtocolError(Exception):\n"
" def __init__(self, msg, data):\n"
" Exception.__init__(self, msg)\n"
" self.data = data\n"
"\n"
"class ParseError(ProtocolError):\n"
" pass\n"
"\n"
"def get():\n"
" hdr = ''\n"
" while ':' not in hdr and len(hdr) < 20:\n"
" hdr += sys.stdin.read(1)\n"
" if not hdr:\n"
" sys.exit(0)\n"
" if not hdr.endswith(':'):\n"
" raise ProtocolError('Expected len:data, got %r' % hdr, hdr)\n"
" try:\n"
" length = int(hdr[:-1])\n"
" except ValueError:\n"
" raise ProtocolError('Non-numeric len %r' % hdr[:-1], hdr[:-1])\n"
" parts = []\n"
" received = 0\n"
" while received < length:\n"
" parts.append(sys.stdin.read(length - received))\n"
" received += len(parts[-1])\n"
" data = ''.join(parts)\n"
" assert len(data) == length\n"
" try:\n"
" return llsd.parse(data)\n"
// Seems the old indra.base.llsd module didn't properly
// convert IndexError (from running off end of string) to
// LLSDParseError.
" except (IndexError, llsd.LLSDParseError), e:\n"
" msg = 'Bad received packet (%s)' % e\n"
" print >>sys.stderr, '%s, %s bytes:' % (msg, len(data))\n"
" showmax = 40\n"
// We've observed failures with very large packets;
// dumping the entire packet wastes time and space.
// But if the error states a particular byte offset,
// truncate to (near) that offset when dumping data.
" location = re.search(r' at (byte|index) ([0-9]+)', str(e))\n"
" if not location:\n"
" # didn't find offset, dump whole thing, no ellipsis\n"
" ellipsis = ''\n"
" else:\n"
" # found offset within error message\n"
" trunc = int(location.group(2)) + showmax\n"
" data = data[:trunc]\n"
" ellipsis = '... (%s more)' % (length - trunc)\n"
" offset = -showmax\n"
" for offset in xrange(0, len(data)-showmax, showmax):\n"
" print >>sys.stderr, '%04d: %r +' % \\\n"
" (offset, data[offset:offset+showmax])\n"
" offset += showmax\n"
" print >>sys.stderr, '%04d: %r%s' % \\\n"
" (offset, data[offset:], ellipsis)\n"
" raise ParseError(msg, data)\n"
"\n"
"# deal with initial stdin message\n"
// this will throw if the initial write to stdin doesn't
// follow len:data protocol, or if we couldn't find 'pump'
// in the dict
"_reply = get()['pump']\n"
"\n"
"def replypump():\n"
" return _reply\n"
"\n"
"def put(req):\n"
" sys.stdout.write(':'.join((str(len(req)), req)))\n"
" sys.stdout.flush()\n"
"\n"
"def send(pump, data):\n"
" put(llsd.format_notation(dict(pump=pump, data=data)))\n"
"\n"
"def request(pump, data):\n"
" # we expect 'data' is a dict\n"
" data['reply'] = _reply\n"
" send(pump, data)\n"),
// Get the actual pathname of the NamedExtTempFile and trim off
// the ".py" extension. (We could cache reader.getName() in a
// separate member variable, but I happen to know getName() just
// returns a NamedExtTempFile member rather than performing any
// computation, so I don't mind calling it twice.) Then take the
// basename.
reader_module(LLProcess::basename(
reader.getName().substr(0, reader.getName().length()-3))),
pPYTHON(getenv("PYTHON")),
PYTHON(pPYTHON? pPYTHON : "")
{
ensure("Set PYTHON to interpreter pathname", pPYTHON);
}
NamedExtTempFile reader;
const std::string reader_module;
const char* pPYTHON;
const std::string PYTHON;
};
typedef test_group<llleap_data> llleap_group;
typedef llleap_group::object object;
llleap_group llleapgrp("llleap");
template<> template<>
void object::test<1>()
{
set_test_name("multiple LLLeap instances");
NamedTempFile script("py",
"import time\n"
"time.sleep(1)\n");
std::vector<LLLeap*> instances;
instances.push_back(LLLeap::create(get_test_name(),
sv(list_of(PYTHON)(script.getName()))));
instances.push_back(LLLeap::create(get_test_name(),
sv(list_of(PYTHON)(script.getName()))));
// In this case we're simply establishing that two LLLeap instances
// can coexist without throwing exceptions or bombing in any other
// way. Wait for them to terminate.
waitfor(instances);
}
template<> template<>
void object::test<2>()
{
set_test_name("stderr to log");
NamedTempFile script("py",
"import sys\n"
"sys.stderr.write('''Hello from Python!\n"
"note partial line''')\n");
CaptureLog log(LLError::LEVEL_INFO);
waitfor(LLLeap::create(get_test_name(),
sv(list_of(PYTHON)(script.getName()))));
log.messageWith("Hello from Python!");
log.messageWith("note partial line");
}
template<> template<>
void object::test<3>()
{
set_test_name("bad stdout protocol");
NamedTempFile script("py",
"print 'Hello from Python!'\n");
CaptureLog log(LLError::LEVEL_WARN);
waitfor(LLLeap::create(get_test_name(),
sv(list_of(PYTHON)(script.getName()))));
ensure_contains("error log line",
log.messageWith("invalid protocol"), "Hello from Python!");
}
template<> template<>
void object::test<4>()
{
set_test_name("leftover stdout");
NamedTempFile script("py",
"import sys\n"
// note lack of newline
"sys.stdout.write('Hello from Python!')\n");
CaptureLog log(LLError::LEVEL_WARN);
waitfor(LLLeap::create(get_test_name(),
sv(list_of(PYTHON)(script.getName()))));
ensure_contains("error log line",
log.messageWith("Discarding"), "Hello from Python!");
}
template<> template<>
void object::test<5>()
{
set_test_name("bad stdout len prefix");
NamedTempFile script("py",
"import sys\n"
"sys.stdout.write('5a2:something')\n");
CaptureLog log(LLError::LEVEL_WARN);
waitfor(LLLeap::create(get_test_name(),
sv(list_of(PYTHON)(script.getName()))));
ensure_contains("error log line",
log.messageWith("invalid protocol"), "5a2:");
}
template<> template<>
void object::test<6>()
{
set_test_name("empty plugin vector");
std::string threw;
try
{
LLLeap::create("empty", StringVec());
}
CATCH_AND_STORE_WHAT_IN(threw, LLLeap::Error)
ensure_contains("LLLeap::Error", threw, "no plugin");
// try the suppress-exception variant
ensure("bad launch returned non-NULL", ! LLLeap::create("empty", StringVec(), false));
}
template<> template<>
void object::test<7>()
{
set_test_name("bad launch");
// Synthesize bogus executable name
std::string BADPYTHON(PYTHON.substr(0, PYTHON.length()-1) + "x");
CaptureLog log;
std::string threw;
try
{
LLLeap::create("bad exe", BADPYTHON);
}
CATCH_AND_STORE_WHAT_IN(threw, LLLeap::Error)
ensure_contains("LLLeap::create() didn't throw", threw, "failed");
log.messageWith("failed");
log.messageWith(BADPYTHON);
// try the suppress-exception variant
ensure("bad launch returned non-NULL", ! LLLeap::create("bad exe", BADPYTHON, false));
}
// Generic self-contained listener: derive from this and override its
// call() method, then tell somebody to post on the pump named getName().
// Control will reach your call() override.
struct ListenerBase
{
// Pass the pump name you want; will tweak for uniqueness.
ListenerBase(const std::string& name):
mPump(name, true)
{
mPump.listen(name, boost::bind(&ListenerBase::call, this, _1));
}
virtual ~ListenerBase() {} // pacify MSVC
virtual bool call(const LLSD& request)
{
return false;
}
LLEventPump& getPump() { return mPump; }
const LLEventPump& getPump() const { return mPump; }
std::string getName() const { return mPump.getName(); }
void post(const LLSD& data) { mPump.post(data); }
LLEventStream mPump;
};
// Mimic a dummy little LLEventAPI that merely sends a reply back to its
// requester on the "reply" pump.
struct AckAPI: public ListenerBase
{
AckAPI(): ListenerBase("AckAPI") {}
virtual bool call(const LLSD& request)
{
LLEventPumps::instance().obtain(request["reply"]).post("ack");
return false;
}
};
// Give LLLeap script a way to post success/failure.
struct Result: public ListenerBase
{
Result(): ListenerBase("Result") {}
virtual bool call(const LLSD& request)
{
mData = request;
return false;
}
void ensure() const
{
tut::ensure(std::string("never posted to ") + getName(), mData.isDefined());
// Post an empty string for success, non-empty string is failure message.
tut::ensure(mData, mData.asString().empty());
}
LLSD mData;
};
template<> template<>
void object::test<8>()
{
set_test_name("round trip");
AckAPI api;
Result result;
NamedTempFile script("py",
boost::lambda::_1 <<
"from " << reader_module << " import *\n"
// make a request on our little API
"request(pump='" << api.getName() << "', data={})\n"
// wait for its response
"resp = get()\n"
"result = '' if resp == dict(pump=replypump(), data='ack')\\\n"
" else 'bad: ' + str(resp)\n"
"send(pump='" << result.getName() << "', data=result)\n");
waitfor(LLLeap::create(get_test_name(), sv(list_of(PYTHON)(script.getName()))));
result.ensure();
}
struct ReqIDAPI: public ListenerBase
{
ReqIDAPI(): ListenerBase("ReqIDAPI") {}
virtual bool call(const LLSD& request)
{
// free function from llevents.h
sendReply(LLSD(), request);
return false;
}
};
template<> template<>
void object::test<9>()
{
set_test_name("many small messages");
// It's not clear to me whether there's value in iterating many times
// over a send/receive loop -- I don't think that will exercise any
// interesting corner cases. This test first sends a large number of
// messages, then receives all the responses. The intent is to ensure
// that some of that data stream crosses buffer boundaries, loop
// iterations etc. in OS pipes and the LLLeap/LLProcess implementation.
ReqIDAPI api;
Result result;
NamedTempFile script("py",
boost::lambda::_1 <<
"import sys\n"
"from " << reader_module << " import *\n"
// Note that since reader imports llsd, this
// 'import *' gets us llsd too.
"sample = llsd.format_notation(dict(pump='" <<
api.getName() << "', data=dict(reqid=999999, reply=replypump())))\n"
// The whole packet has length prefix too: "len:data"
"samplen = len(str(len(sample))) + 1 + len(sample)\n"
// guess how many messages it will take to
// accumulate BUFFERED_LENGTH
"count = int(" << BUFFERED_LENGTH << "/samplen)\n"
"print >>sys.stderr, 'Sending %s requests' % count\n"
"for i in xrange(count):\n"
" request('" << api.getName() << "', dict(reqid=i))\n"
// The assumption in this specific test that
// replies will arrive in the same order as
// requests is ONLY valid because the API we're
// invoking sends replies instantly. If the API
// had to wait for some external event before
// sending its reply, replies could arrive in
// arbitrary order, and we'd have to tick them
// off from a set.
"result = ''\n"
"for i in xrange(count):\n"
" resp = get()\n"
" if resp['data']['reqid'] != i:\n"
" result = 'expected reqid=%s in %s' % (i, resp)\n"
" break\n"
"send(pump='" << result.getName() << "', data=result)\n");
waitfor(LLLeap::create(get_test_name(), sv(list_of(PYTHON)(script.getName()))),
300); // needs more realtime than most tests
result.ensure();
}
// This is the body of test<10>, extracted so we can run it over a number
// of large-message sizes.
void test_large_message(const std::string& PYTHON, const std::string& reader_module,
const std::string& test_name, size_t size)
{
ReqIDAPI api;
Result result;
NamedTempFile script("py",
boost::lambda::_1 <<
"import sys\n"
"from " << reader_module << " import *\n"
// Generate a very large string value.
"desired = int(sys.argv[1])\n"
// 7 chars per item: 6 digits, 1 comma
"count = int((desired - 50)/7)\n"
"large = ''.join('%06d,' % i for i in xrange(count))\n"
// Pass 'large' as reqid because we know the API
// will echo reqid, and we want to receive it back.
"request('" << api.getName() << "', dict(reqid=large))\n"
"try:\n"
" resp = get()\n"
"except ParseError, e:\n"
" # try to find where e.data diverges from expectation\n"
// Normally we'd expect a 'pump' key in there,
// too, with value replypump(). But Python
// serializes keys in a different order than C++,
// so incoming data start with 'data'.
// Truthfully, though, if we get as far as 'pump'
// before we find a difference, something's very
// strange.
" expect = llsd.format_notation(dict(data=dict(reqid=large)))\n"
" chunk = 40\n"
" for offset in xrange(0, max(len(e.data), len(expect)), chunk):\n"
" if e.data[offset:offset+chunk] != \\\n"
" expect[offset:offset+chunk]:\n"
" print >>sys.stderr, 'Offset %06d: expect %r,\\n'\\\n"
" ' get %r' %\\\n"
" (offset,\n"
" expect[offset:offset+chunk],\n"
" e.data[offset:offset+chunk])\n"
" break\n"
" else:\n"
" print >>sys.stderr, 'incoming data matches expect?!'\n"
" send('" << result.getName() << "', '%s: %s' % (e.__class__.__name__, e))\n"
" sys.exit(1)\n"
"\n"
"echoed = resp['data']['reqid']\n"
"if echoed == large:\n"
" send('" << result.getName() << "', '')\n"
" sys.exit(0)\n"
// Here we know echoed did NOT match; try to find where
"for i in xrange(count):\n"
" start = 7*i\n"
" end = 7*(i+1)\n"
" if end > len(echoed)\\\n"
" or echoed[start:end] != large[start:end]:\n"
" send('" << result.getName() << "',\n"
" 'at offset %s, expected %r but got %r' %\n"
" (start, large[start:end], echoed[start:end]))\n"
"sys.exit(1)\n");
waitfor(LLLeap::create(test_name,
sv(list_of
(PYTHON)
(script.getName())
(stringize(size)))),
180); // try a longer timeout
result.ensure();
}
struct TestLargeMessage: public std::binary_function<size_t, size_t, bool>
{
TestLargeMessage(const std::string& PYTHON_, const std::string& reader_module_,
const std::string& test_name_):
PYTHON(PYTHON_),
reader_module(reader_module_),
test_name(test_name_)
{}
bool operator()(size_t left, size_t right) const
{
// We don't know whether upper_bound is going to pass the "sought
// value" as the left or the right operand. We pass 0 as the
// "sought value" so we can distinguish it. Of course that means
// the sequence we're searching must not itself contain 0!
size_t size;
bool success;
if (left)
{
size = left;
// Consider our return value carefully. Normal binary_search
// (or, in our case, upper_bound) expects a container sorted
// in ascending order, and defaults to the std::less
// comparator. Our container is in fact in ascending order, so
// return consistently with std::less. Here we were called as
// compare(item, sought). If std::less were called that way,
// 'true' would mean to move right (to higher numbers) within
// the sequence: the item being considered is less than the
// sought value. For us, that means that test_large_message()
// success should return 'true'.
success = true;
}
else
{
size = right;
// Here we were called as compare(sought, item). If std::less
// were called that way, 'true' would mean to move left (to
// lower numbers) within the sequence: the sought value is
// less than the item being considered. For us, that means
// test_large_message() FAILURE should return 'true', hence
// test_large_message() success should return 'false'.
success = false;
}
try
{
test_large_message(PYTHON, reader_module, test_name, size);
std::cout << "test_large_message(" << size << ") succeeded" << std::endl;
return success;
}
catch (const failure& e)
{
std::cout << "test_large_message(" << size << ") failed: " << e.what() << std::endl;
return ! success;
}
}
const std::string PYTHON, reader_module, test_name;
};
// The point of this function is to try to find a size at which
// test_large_message() can succeed. We still want the overall test to
// fail; otherwise we won't get the coder's attention -- but if
// test_large_message() fails, try to find a plausible size at which it
// DOES work.
void test_or_split(const std::string& PYTHON, const std::string& reader_module,
const std::string& test_name, size_t size)
{
try
{
test_large_message(PYTHON, reader_module, test_name, size);
}
catch (const failure& e)
{
std::cout << "test_large_message(" << size << ") failed: " << e.what() << std::endl;
// If it still fails below 4K, give up: subdividing any further is
// pointless.
if (size >= 4096)
{
try
{
// Recur with half the size
size_t smaller(size/2);
test_or_split(PYTHON, reader_module, test_name, smaller);
// Recursive call will throw if test_large_message()
// failed, therefore we only reach the line below if it
// succeeded.
std::cout << "but test_large_message(" << smaller << ") succeeded" << std::endl;
// Binary search for largest size that works. But since
// std::binary_search() only returns bool, actually use
// std::upper_bound(), consistent with our desire to find
// the LARGEST size that works. First generate a sorted
// container of all the sizes we intend to try, from
// 'smaller' (known to work) to 'size' (known to fail). We
// could whomp up magic iterators to do this dynamically,
// without actually instantiating a vector, but for a test
// program this will do. At least preallocate the vector.
// Per TestLargeMessage comments, it's important that this
// vector not contain 0.
std::vector<size_t> sizes;
sizes.reserve((size - smaller)/4096 + 1);
for (size_t sz(smaller), szend(size); sz < szend; sz += 4096)
sizes.push_back(sz);
// our comparator
TestLargeMessage tester(PYTHON, reader_module, test_name);
// Per TestLargeMessage comments, pass 0 as the sought value.
std::vector<size_t>::const_iterator found =
std::upper_bound(sizes.begin(), sizes.end(), 0, tester);
if (found != sizes.end() && found != sizes.begin())
{
std::cout << "test_large_message(" << *(found - 1)
<< ") is largest that succeeds" << std::endl;
}
else
{
std::cout << "cannot determine largest test_large_message(size) "
<< "that succeeds" << std::endl;
}
}
catch (const failure&)
{
// The recursive test_or_split() call above has already
// handled the exception. We don't want our caller to see
// innermost exception; propagate outermost (below).
}
}
// In any case, because we reached here through failure of
// our original test_large_message(size) call, ensure failure
// propagates.
throw e;
}
}
template<> template<>
void object::test<10>()
{
set_test_name("very large message");
test_or_split(PYTHON, reader_module, get_test_name(), BUFFERED_LENGTH);
}
} // namespace tut

File diff suppressed because it is too large Load Diff

View File

@ -40,41 +40,15 @@ typedef U32 uint32_t;
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include "llprocesslauncher.h"
#include "llprocess.h"
#endif
#include <sstream>
/*==========================================================================*|
// Whoops, seems Linden's Boost package and the viewer are built with
// different settings of VC's /Zc:wchar_t switch! Using Boost.Filesystem
// pathname operations produces Windows link errors:
// unresolved external symbol "private: static class std::codecvt<unsigned short,
// char,int> const * & __cdecl boost::filesystem3::path::wchar_t_codecvt_facet()"
// unresolved external symbol "void __cdecl boost::filesystem3::path_traits::convert()"
// See:
// http://boost.2283326.n4.nabble.com/filesystem-v3-unicode-and-std-codecvt-linker-error-td3455549.html
// which points to:
// http://msdn.microsoft.com/en-us/library/dh8che7s%28v=VS.100%29.aspx
// As we're not trying to preserve compatibility with old Boost.Filesystem
// code, but rather writing brand-new code, use the newest available
// Filesystem API.
#define BOOST_FILESYSTEM_VERSION 3
#include "boost/filesystem.hpp"
#include "boost/filesystem/v3/fstream.hpp"
|*==========================================================================*/
#include "boost/range.hpp"
#include "boost/foreach.hpp"
#include "boost/function.hpp"
#include "boost/lambda/lambda.hpp"
#include "boost/lambda/bind.hpp"
namespace lambda = boost::lambda;
/*==========================================================================*|
// Aaaarrgh, Linden's Boost package doesn't even include Boost.Iostreams!
#include "boost/iostreams/stream.hpp"
#include "boost/iostreams/device/file_descriptor.hpp"
|*==========================================================================*/
#include "../llsd.h"
#include "../llsdserialize.h"
@ -82,236 +56,17 @@ namespace lambda = boost::lambda;
#include "../llformat.h"
#include "../test/lltut.h"
#include "../test/manageapr.h"
#include "../test/namedtempfile.h"
#include "stringize.h"
static ManageAPR manager;
std::vector<U8> string_to_vector(const std::string& str)
{
return std::vector<U8>(str.begin(), str.end());
}
#if ! LL_WINDOWS
// We want to call strerror_r(), but alarmingly, there are two different
// variants. The one that returns int always populates the passed buffer
// (except in case of error), whereas the other one always returns a valid
// char* but might or might not populate the passed buffer. How do we know
// which one we're getting? Define adapters for each and let the compiler
// select the applicable adapter.
// strerror_r() returns char*
std::string message_from(int /*orig_errno*/, const char* /*buffer*/, const char* strerror_ret)
{
return strerror_ret;
}
// strerror_r() returns int
std::string message_from(int orig_errno, const char* buffer, int strerror_ret)
{
if (strerror_ret == 0)
{
return buffer;
}
// Here strerror_r() has set errno. Since strerror_r() has already failed,
// seems like a poor bet to call it again to diagnose its own error...
int stre_errno = errno;
if (stre_errno == ERANGE)
{
return STRINGIZE("strerror_r() can't explain errno " << orig_errno
<< " (buffer too small)");
}
if (stre_errno == EINVAL)
{
return STRINGIZE("unknown errno " << orig_errno);
}
// Here we don't even understand the errno from strerror_r()!
return STRINGIZE("strerror_r() can't explain errno " << orig_errno
<< " (error " << stre_errno << ')');
}
#endif // ! LL_WINDOWS
// boost::filesystem::temp_directory_path() isn't yet in Boost 1.45! :-(
std::string temp_directory_path()
{
#if LL_WINDOWS
char buffer[4096];
GetTempPathA(sizeof(buffer), buffer);
return buffer;
#else // LL_DARWIN, LL_LINUX
static const char* vars[] = { "TMPDIR", "TMP", "TEMP", "TEMPDIR" };
BOOST_FOREACH(const char* var, vars)
{
const char* found = getenv(var);
if (found)
return found;
}
return "/tmp";
#endif // LL_DARWIN, LL_LINUX
}
// Windows presents a kinda sorta compatibility layer. Code to the yucky
// Windows names because they're less likely than the Posix names to collide
// with any other names in this source.
#if LL_WINDOWS
#define _remove DeleteFileA
#else // ! LL_WINDOWS
#define _open open
#define _write write
#define _close close
#define _remove remove
#endif // ! LL_WINDOWS
// Create a text file with specified content "somewhere in the
// filesystem," cleaning up when it goes out of scope.
class NamedTempFile
{
public:
// Function that accepts an ostream ref and (presumably) writes stuff to
// it, e.g.:
// (lambda::_1 << "the value is " << 17 << '\n')
typedef boost::function<void(std::ostream&)> Streamer;
NamedTempFile(const std::string& ext, const std::string& content):
mPath(temp_directory_path())
{
createFile(ext, lambda::_1 << content);
}
// Disambiguate when passing string literal
NamedTempFile(const std::string& ext, const char* content):
mPath(temp_directory_path())
{
createFile(ext, lambda::_1 << content);
}
NamedTempFile(const std::string& ext, const Streamer& func):
mPath(temp_directory_path())
{
createFile(ext, func);
}
~NamedTempFile()
{
_remove(mPath.c_str());
}
std::string getName() const { return mPath; }
private:
void createFile(const std::string& ext, const Streamer& func)
{
// Silly maybe, but use 'ext' as the name prefix. Strip off a leading
// '.' if present.
int pfx_offset = ((! ext.empty()) && ext[0] == '.')? 1 : 0;
#if ! LL_WINDOWS
// Make sure mPath ends with a directory separator, if it doesn't already.
if (mPath.empty() ||
! (mPath[mPath.length() - 1] == '\\' || mPath[mPath.length() - 1] == '/'))
{
mPath.append("/");
}
// mkstemp() accepts and modifies a char* template string. Generate
// the template string, then copy to modifiable storage.
// mkstemp() requires its template string to end in six X's.
mPath += ext.substr(pfx_offset) + "XXXXXX";
// Copy to vector<char>
std::vector<char> pathtemplate(mPath.begin(), mPath.end());
// append a nul byte for classic-C semantics
pathtemplate.push_back('\0');
// std::vector promises that a pointer to the 0th element is the same
// as a pointer to a contiguous classic-C array
int fd(mkstemp(&pathtemplate[0]));
if (fd == -1)
{
// The documented errno values (http://linux.die.net/man/3/mkstemp)
// are used in a somewhat unusual way, so provide context-specific
// errors.
if (errno == EEXIST)
{
LL_ERRS("NamedTempFile") << "mkstemp(\"" << mPath
<< "\") could not create unique file " << LL_ENDL;
}
if (errno == EINVAL)
{
LL_ERRS("NamedTempFile") << "bad mkstemp() file path template '"
<< mPath << "'" << LL_ENDL;
}
// Shrug, something else
int mkst_errno = errno;
char buffer[256];
LL_ERRS("NamedTempFile") << "mkstemp(\"" << mPath << "\") failed: "
<< message_from(mkst_errno, buffer,
strerror_r(mkst_errno, buffer, sizeof(buffer)))
<< LL_ENDL;
}
// mkstemp() seems to have worked! Capture the modified filename.
// Avoid the nul byte we appended.
mPath.assign(pathtemplate.begin(), (pathtemplate.end()-1));
/*==========================================================================*|
// Define an ostream on the open fd. Tell it to close fd on destruction.
boost::iostreams::stream<boost::iostreams::file_descriptor_sink>
out(fd, boost::iostreams::close_handle);
|*==========================================================================*/
// Write desired content.
std::ostringstream out;
// Stream stuff to it.
func(out);
std::string data(out.str());
int written(_write(fd, data.c_str(), data.length()));
int closed(_close(fd));
llassert_always(written == data.length() && closed == 0);
#else // LL_WINDOWS
// GetTempFileName() is documented to require a MAX_PATH buffer.
char tempname[MAX_PATH];
// Use 'ext' as filename prefix, but skip leading '.' if any.
// The 0 param is very important: requests iterating until we get a
// unique name.
if (0 == GetTempFileNameA(mPath.c_str(), ext.c_str() + pfx_offset, 0, tempname))
{
// I always have to look up this call... :-P
LPSTR msgptr;
FormatMessageA(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
LPSTR(&msgptr), // have to cast (char**) to (char*)
0, NULL );
LL_ERRS("NamedTempFile") << "GetTempFileName(\"" << mPath << "\", \""
<< (ext.c_str() + pfx_offset) << "\") failed: "
<< msgptr << LL_ENDL;
LocalFree(msgptr);
}
// GetTempFileName() appears to have worked! Capture the actual
// filename.
mPath = tempname;
// Open the file and stream content to it. Destructor will close.
std::ofstream out(tempname);
func(out);
#endif // LL_WINDOWS
}
void peep()
{
std::cout << "File '" << mPath << "' contains:\n";
std::ifstream reader(mPath.c_str());
std::string line;
while (std::getline(reader, line))
std::cout << line << '\n';
std::cout << "---\n";
}
std::string mPath;
};
namespace tut
{
struct sd_xml_data
@ -1783,7 +1538,7 @@ namespace tut
const char* PYTHON(getenv("PYTHON"));
ensure("Set $PYTHON to the Python interpreter", PYTHON);
NamedTempFile scriptfile(".py", script);
NamedTempFile scriptfile("py", script);
#if LL_WINDOWS
std::string q("\"");
@ -1802,14 +1557,15 @@ namespace tut
}
#else // LL_DARWIN, LL_LINUX
LLProcessLauncher py;
py.setExecutable(PYTHON);
py.addArgument(scriptfile.getName());
ensure_equals(STRINGIZE("Couldn't launch " << desc << " script"), py.launch(), 0);
LLProcess::Params params;
params.executable = PYTHON;
params.args.add(scriptfile.getName());
LLProcessPtr py(LLProcess::create(params));
ensure(STRINGIZE("Couldn't launch " << desc << " script"), py);
// Implementing timeout would mean messing with alarm() and
// catching SIGALRM... later maybe...
int status(0);
if (waitpid(py.getProcessID(), &status, 0) == -1)
if (waitpid(py->getProcessID(), &status, 0) == -1)
{
int waitpid_errno(errno);
ensure_equals(STRINGIZE("Couldn't retrieve rc from " << desc << " script: "
@ -1888,12 +1644,12 @@ namespace tut
" else:\n"
" assert False, 'Too many data items'\n";
// Create a something.llsd file containing 'data' serialized to
// Create an llsdXXXXXX file containing 'data' serialized to
// notation. It's important to separate with newlines because Python's
// llsd module doesn't support parsing from a file stream, only from a
// string, so we have to know how much of the file to read into a
// string.
NamedTempFile file(".llsd",
NamedTempFile file("llsd",
// NamedTempFile's boost::function constructor
// takes a callable. To this callable it passes the
// std::ostream with which it's writing the
@ -1926,7 +1682,7 @@ namespace tut
// Create an empty data file. This is just a placeholder for our
// script to write into. Create it to establish a unique name that
// we know.
NamedTempFile file(".llsd", "");
NamedTempFile file("llsd", "");
python("write Python notation",
lambda::_1 <<

View File

@ -0,0 +1,197 @@
/**
* @file llstreamqueue_test.cpp
* @author Nat Goodspeed
* @date 2012-01-05
* @brief Test for llstreamqueue.
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Copyright (c) 2012, Linden Research, Inc.
* $/LicenseInfo$
*/
// Precompiled header
#include "linden_common.h"
// associated header
#include "llstreamqueue.h"
// STL headers
#include <vector>
// std headers
// external library headers
#include <boost/foreach.hpp>
// other Linden headers
#include "../test/lltut.h"
#include "stringize.h"
/*****************************************************************************
* TUT
*****************************************************************************/
namespace tut
{
struct llstreamqueue_data
{
llstreamqueue_data():
// we want a buffer with actual bytes in it, not an empty vector
buffer(10)
{}
// As LLStreamQueue is merely a typedef for
// LLGenericStreamQueue<char>, and no logic in LLGenericStreamQueue is
// specific to the <char> instantiation, we're comfortable for now
// testing only the narrow-char version.
LLStreamQueue strq;
// buffer for use in multiple tests
std::vector<char> buffer;
};
typedef test_group<llstreamqueue_data> llstreamqueue_group;
typedef llstreamqueue_group::object object;
llstreamqueue_group llstreamqueuegrp("llstreamqueue");
template<> template<>
void object::test<1>()
{
set_test_name("empty LLStreamQueue");
ensure_equals("brand-new LLStreamQueue isn't empty",
strq.size(), 0);
ensure_equals("brand-new LLStreamQueue returns data",
strq.asSource().read(&buffer[0], buffer.size()), 0);
strq.asSink().close();
ensure_equals("closed empty LLStreamQueue not at EOF",
strq.asSource().read(&buffer[0], buffer.size()), -1);
}
template<> template<>
void object::test<2>()
{
set_test_name("one internal block, one buffer");
LLStreamQueue::Sink sink(strq.asSink());
ensure_equals("write(\"\")", sink.write("", 0), 0);
ensure_equals("0 write should leave LLStreamQueue empty (size())",
strq.size(), 0);
ensure_equals("0 write should leave LLStreamQueue empty (peek())",
strq.peek(&buffer[0], buffer.size()), 0);
// The meaning of "atomic" is that it must be smaller than our buffer.
std::string atomic("atomic");
ensure("test data exceeds buffer", atomic.length() < buffer.size());
ensure_equals(STRINGIZE("write(\"" << atomic << "\")"),
sink.write(&atomic[0], atomic.length()), atomic.length());
ensure_equals("size() after write()", strq.size(), atomic.length());
size_t peeklen(strq.peek(&buffer[0], buffer.size()));
ensure_equals(STRINGIZE("peek(\"" << atomic << "\")"),
peeklen, atomic.length());
ensure_equals(STRINGIZE("peek(\"" << atomic << "\") result"),
std::string(buffer.begin(), buffer.begin() + peeklen), atomic);
ensure_equals("size() after peek()", strq.size(), atomic.length());
// peek() should not consume. Use a different buffer to prove it isn't
// just leftover data from the first peek().
std::vector<char> again(buffer.size());
peeklen = size_t(strq.peek(&again[0], again.size()));
ensure_equals(STRINGIZE("peek(\"" << atomic << "\") again"),
peeklen, atomic.length());
ensure_equals(STRINGIZE("peek(\"" << atomic << "\") again result"),
std::string(again.begin(), again.begin() + peeklen), atomic);
// now consume.
std::vector<char> third(buffer.size());
size_t readlen(strq.read(&third[0], third.size()));
ensure_equals(STRINGIZE("read(\"" << atomic << "\")"),
readlen, atomic.length());
ensure_equals(STRINGIZE("read(\"" << atomic << "\") result"),
std::string(third.begin(), third.begin() + readlen), atomic);
ensure_equals("peek() after read()", strq.peek(&buffer[0], buffer.size()), 0);
ensure_equals("size() after read()", strq.size(), 0);
}
template<> template<>
void object::test<3>()
{
set_test_name("basic skip()");
std::string lovecraft("lovecraft");
ensure("test data exceeds buffer", lovecraft.length() < buffer.size());
ensure_equals(STRINGIZE("write(\"" << lovecraft << "\")"),
strq.write(&lovecraft[0], lovecraft.length()), lovecraft.length());
size_t peeklen(strq.peek(&buffer[0], buffer.size()));
ensure_equals(STRINGIZE("peek(\"" << lovecraft << "\")"),
peeklen, lovecraft.length());
ensure_equals(STRINGIZE("peek(\"" << lovecraft << "\") result"),
std::string(buffer.begin(), buffer.begin() + peeklen), lovecraft);
std::streamsize skip1(4);
ensure_equals(STRINGIZE("skip(" << skip1 << ")"), strq.skip(skip1), skip1);
ensure_equals("size() after skip()", strq.size(), lovecraft.length() - skip1);
size_t readlen(strq.read(&buffer[0], buffer.size()));
ensure_equals(STRINGIZE("read(\"" << lovecraft.substr(skip1) << "\")"),
readlen, lovecraft.length() - skip1);
ensure_equals(STRINGIZE("read(\"" << lovecraft.substr(skip1) << "\") result"),
std::string(buffer.begin(), buffer.begin() + readlen),
lovecraft.substr(skip1));
ensure_equals("unconsumed", strq.read(&buffer[0], buffer.size()), 0);
}
template<> template<>
void object::test<4>()
{
set_test_name("skip() multiple blocks");
std::string blocks[] = { "books of ", "H.P. ", "Lovecraft" };
std::streamsize total(blocks[0].length() + blocks[1].length() + blocks[2].length());
std::streamsize leave(5); // len("craft") above
std::streamsize skip(total - leave);
std::streamsize written(0);
BOOST_FOREACH(const std::string& block, blocks)
{
written += strq.write(&block[0], block.length());
ensure_equals("size() after write()", strq.size(), written);
}
std::streamsize skiplen(strq.skip(skip));
ensure_equals(STRINGIZE("skip(" << skip << ")"), skiplen, skip);
ensure_equals("size() after skip()", strq.size(), leave);
size_t readlen(strq.read(&buffer[0], buffer.size()));
ensure_equals("read(\"craft\")", readlen, leave);
ensure_equals("read(\"craft\") result",
std::string(buffer.begin(), buffer.begin() + readlen), "craft");
}
template<> template<>
void object::test<5>()
{
set_test_name("concatenate blocks");
std::string blocks[] = { "abcd", "efghij", "klmnopqrs" };
BOOST_FOREACH(const std::string& block, blocks)
{
strq.write(&block[0], block.length());
}
std::vector<char> longbuffer(30);
std::streamsize readlen(strq.read(&longbuffer[0], longbuffer.size()));
ensure_equals("read() multiple blocks",
readlen, blocks[0].length() + blocks[1].length() + blocks[2].length());
ensure_equals("read() multiple blocks result",
std::string(longbuffer.begin(), longbuffer.begin() + readlen),
blocks[0] + blocks[1] + blocks[2]);
}
template<> template<>
void object::test<6>()
{
set_test_name("split blocks");
std::string blocks[] = { "abcdefghijklm", "nopqrstuvwxyz" };
BOOST_FOREACH(const std::string& block, blocks)
{
strq.write(&block[0], block.length());
}
strq.close();
// We've already verified what strq.size() should be at this point;
// see above test named "skip() multiple blocks"
std::streamsize chksize(strq.size());
std::streamsize readlen(strq.read(&buffer[0], buffer.size()));
ensure_equals("read() 0", readlen, buffer.size());
ensure_equals("read() 0 result", std::string(buffer.begin(), buffer.end()), "abcdefghij");
chksize -= readlen;
ensure_equals("size() after read() 0", strq.size(), chksize);
readlen = strq.read(&buffer[0], buffer.size());
ensure_equals("read() 1", readlen, buffer.size());
ensure_equals("read() 1 result", std::string(buffer.begin(), buffer.end()), "klmnopqrst");
chksize -= readlen;
ensure_equals("size() after read() 1", strq.size(), chksize);
readlen = strq.read(&buffer[0], buffer.size());
ensure_equals("read() 2", readlen, chksize);
ensure_equals("read() 2 result",
std::string(buffer.begin(), buffer.begin() + readlen), "uvwxyz");
ensure_equals("read() 3", strq.read(&buffer[0], buffer.size()), -1);
}
} // namespace tut

View File

@ -29,7 +29,11 @@
#include "linden_common.h"
#include "../test/lltut.h"
#include <boost/assign/list_of.hpp>
#include "../llstring.h"
#include "StringVec.h"
using boost::assign::list_of;
namespace tut
{
@ -750,4 +754,118 @@ namespace tut
ensure("empty substr.", !LLStringUtil::endsWith(empty, value));
ensure("empty everything.", !LLStringUtil::endsWith(empty, empty));
}
template<> template<>
void string_index_object_t::test<41>()
{
set_test_name("getTokens(\"delims\")");
ensure_equals("empty string", LLStringUtil::getTokens("", " "), StringVec());
ensure_equals("only delims",
LLStringUtil::getTokens(" \r\n ", " \r\n"), StringVec());
ensure_equals("sequence of delims",
LLStringUtil::getTokens(",,, one ,,,", ","), list_of("one"));
// nat considers this a dubious implementation side effect, but I'd
// hate to change it now...
ensure_equals("noncontiguous tokens",
LLStringUtil::getTokens(", ,, , one ,,,", ","), list_of("")("")("one"));
ensure_equals("space-padded tokens",
LLStringUtil::getTokens(", one , two ,", ","), list_of("one")("two"));
ensure_equals("no delims", LLStringUtil::getTokens("one", ","), list_of("one"));
}
// Shorthand for verifying that getTokens() behaves the same when you
// don't pass a string of escape characters, when you pass an empty string
// (different overloads), and when you pass a string of characters that
// aren't actually present.
void ensure_getTokens(const std::string& desc,
const std::string& string,
const std::string& drop_delims,
const std::string& keep_delims,
const std::string& quotes,
const std::vector<std::string>& expect)
{
ensure_equals(desc + " - no esc",
LLStringUtil::getTokens(string, drop_delims, keep_delims, quotes),
expect);
ensure_equals(desc + " - empty esc",
LLStringUtil::getTokens(string, drop_delims, keep_delims, quotes, ""),
expect);
ensure_equals(desc + " - unused esc",
LLStringUtil::getTokens(string, drop_delims, keep_delims, quotes, "!"),
expect);
}
void ensure_getTokens(const std::string& desc,
const std::string& string,
const std::string& drop_delims,
const std::string& keep_delims,
const std::vector<std::string>& expect)
{
ensure_getTokens(desc, string, drop_delims, keep_delims, "", expect);
}
template<> template<>
void string_index_object_t::test<42>()
{
set_test_name("getTokens(\"delims\", etc.)");
// Signatures to test in this method:
// getTokens(string, drop_delims, keep_delims [, quotes [, escapes]])
// If you omit keep_delims, you get the older function (test above).
// cases like the getTokens(string, delims) tests above
ensure_getTokens("empty string", "", " ", "", StringVec());
ensure_getTokens("only delims",
" \r\n ", " \r\n", "", StringVec());
ensure_getTokens("sequence of delims",
",,, one ,,,", ", ", "", list_of("one"));
// Note contrast with the case in the previous method
ensure_getTokens("noncontiguous tokens",
", ,, , one ,,,", ", ", "", list_of("one"));
ensure_getTokens("space-padded tokens",
", one , two ,", ", ", "",
list_of("one")("two"));
ensure_getTokens("no delims", "one", ",", "", list_of("one"));
// drop_delims vs. keep_delims
ensure_getTokens("arithmetic",
" ab+def / xx* yy ", " ", "+-*/",
list_of("ab")("+")("def")("/")("xx")("*")("yy"));
// quotes
ensure_getTokens("no quotes",
"She said, \"Don't go.\"", " ", ",", "",
list_of("She")("said")(",")("\"Don't")("go.\""));
ensure_getTokens("quotes",
"She said, \"Don't go.\"", " ", ",", "\"",
list_of("She")("said")(",")("Don't go."));
ensure_getTokens("quotes and delims",
"run c:/'Documents and Settings'/someone", " ", "", "'",
list_of("run")("c:/Documents and Settings/someone"));
ensure_getTokens("unmatched quote",
"baby don't leave", " ", "", "'",
list_of("baby")("don't")("leave"));
ensure_getTokens("adjacent quoted",
"abc'def \"ghi'\"jkl' mno\"pqr", " ", "", "\"'",
list_of("abcdef \"ghijkl' mnopqr"));
ensure_getTokens("quoted empty string",
"--set SomeVar ''", " ", "", "'",
list_of("--set")("SomeVar")(""));
// escapes
// Don't use backslash as an escape for these tests -- you'll go nuts
// between the C++ string scanner and getTokens() escapes. Test with
// something else!
ensure_equals("escaped delims",
LLStringUtil::getTokens("^ a - dog^-gone^ phrase", " ", "-", "", "^"),
list_of(" a")("-")("dog-gone phrase"));
ensure_equals("escaped quotes",
LLStringUtil::getTokens("say: 'this isn^'t w^orking'.", " ", "", "'", "^"),
list_of("say:")("this isn't working."));
ensure_equals("escaped escape",
LLStringUtil::getTokens("want x^^2", " ", "", "", "^"),
list_of("want")("x^2"));
ensure_equals("escape at end",
LLStringUtil::getTokens("it's^ up there^", " ", "", "'", "^"),
list_of("it's up")("there^"));
}
}

View File

@ -1,19 +0,0 @@
#!/usr/bin/python
"""\
@file setpython.py
@author Nat Goodspeed
@date 2011-07-13
@brief Set PYTHON environment variable for tests that care.
$LicenseInfo:firstyear=2011&license=viewerlgpl$
Copyright (c) 2011, Linden Research, Inc.
$/LicenseInfo$
"""
import os
import sys
import subprocess
if __name__ == "__main__":
os.environ["PYTHON"] = sys.executable
sys.exit(subprocess.call(sys.argv[1:]))

View File

@ -29,7 +29,22 @@
#if ! defined(LL_WRAPLLERRS_H)
#define LL_WRAPLLERRS_H
#if LL_WINDOWS
#pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally
#endif
#include <tut/tut.hpp>
#include "llerrorcontrol.h"
#include "stringize.h"
#include <boost/bind.hpp>
#include <boost/noncopyable.hpp>
#include <list>
#include <string>
#include <stdexcept>
// statically reference the function in test.cpp... it's short, we could
// replicate, but better to reuse
extern void wouldHaveCrashed(const std::string& message);
struct WrapLL_ERRS
{
@ -70,4 +85,118 @@ struct WrapLL_ERRS
LLError::FatalFunction mPriorFatal;
};
/**
* LLError::addRecorder() accepts ownership of the passed Recorder* -- it
* expects to be able to delete it later. CaptureLog isa Recorder whose
* pointer we want to be able to pass without any ownership implications.
* For such cases, instantiate a new RecorderProxy(yourRecorder) and pass
* that. Your heap RecorderProxy might later be deleted, but not yourRecorder.
*/
class RecorderProxy: public LLError::Recorder
{
public:
RecorderProxy(LLError::Recorder* recorder):
mRecorder(recorder)
{}
virtual void recordMessage(LLError::ELevel level, const std::string& message)
{
mRecorder->recordMessage(level, message);
}
virtual bool wantsTime()
{
return mRecorder->wantsTime();
}
private:
LLError::Recorder* mRecorder;
};
/**
* Capture log messages. This is adapted (simplified) from the one in
* llerror_test.cpp.
*/
class CaptureLog : public LLError::Recorder, public boost::noncopyable
{
public:
CaptureLog(LLError::ELevel level=LLError::LEVEL_DEBUG):
// Mostly what we're trying to accomplish by saving and resetting
// LLError::Settings is to bypass the default RecordToStderr and
// RecordToWinDebug Recorders. As these are visible only inside
// llerror.cpp, we can't just call LLError::removeRecorder() with
// each. For certain tests we need to produce, capture and examine
// DEBUG log messages -- but we don't want to spam the user's console
// with that output. If it turns out that saveAndResetSettings() has
// some bad effect, give up and just let the DEBUG level log messages
// display.
mOldSettings(LLError::saveAndResetSettings()),
mProxy(new RecorderProxy(this))
{
LLError::setFatalFunction(wouldHaveCrashed);
LLError::setDefaultLevel(level);
LLError::addRecorder(mProxy);
}
~CaptureLog()
{
LLError::removeRecorder(mProxy);
delete mProxy;
LLError::restoreSettings(mOldSettings);
}
void recordMessage(LLError::ELevel level,
const std::string& message)
{
mMessages.push_back(message);
}
/// Don't assume the message we want is necessarily the LAST log message
/// emitted by the underlying code; search backwards through all messages
/// for the sought string.
std::string messageWith(const std::string& search, bool required=true)
{
for (MessageList::const_reverse_iterator rmi(mMessages.rbegin()), rmend(mMessages.rend());
rmi != rmend; ++rmi)
{
if (rmi->find(search) != std::string::npos)
return *rmi;
}
// failed to find any such message
if (! required)
return std::string();
throw tut::failure(STRINGIZE("failed to find '" << search
<< "' in captured log messages:\n"
<< boost::ref(*this)));
}
std::ostream& streamto(std::ostream& out) const
{
MessageList::const_iterator mi(mMessages.begin()), mend(mMessages.end());
if (mi != mend)
{
// handle first message separately: it doesn't get a newline
out << *mi++;
for ( ; mi != mend; ++mi)
{
// every subsequent message gets a newline
out << '\n' << *mi;
}
}
return out;
}
typedef std::list<std::string> MessageList;
MessageList mMessages;
LLError::Settings* mOldSettings;
LLError::Recorder* mProxy;
};
inline
std::ostream& operator<<(std::ostream& out, const CaptureLog& log)
{
return log.streamto(out);
}
#endif /* ! defined(LL_WRAPLLERRS_H) */

1
indra/llkdu/tests/llimagej2ckdu_test.cpp Normal file → Executable file
View File

@ -127,7 +127,6 @@ kdu_subband kdu_resolution::access_subband(int ) { kdu_subband a; return a; }
void kdu_resolution::get_dims(kdu_dims& ) { }
int kdu_resolution::which() { return 0; }
int kdu_resolution::get_valid_band_indices(int &) { return 1; }
//kdu_decoder::kdu_decoder(kdu_subband , kdu_sample_allocator*, bool , float, int, kdu_thread_env*, kdu_thread_queue*) { }
kdu_synthesis::kdu_synthesis(kdu_resolution, kdu_sample_allocator*, bool, float, kdu_thread_env*, kdu_thread_queue*) { }
kdu_params::kdu_params(const char*, bool, bool, bool, bool, bool) { }
kdu_params::~kdu_params() { }

View File

@ -548,6 +548,7 @@ LLCurl::Multi::Multi(F32 idle_time_out)
mErrorCount(0),
mState(STATE_READY),
mDead(FALSE),
mValid(TRUE),
mMutexp(NULL),
mDeletionMutexp(NULL),
mEasyMutexp(NULL)
@ -583,22 +584,33 @@ LLCurl::Multi::Multi(F32 idle_time_out)
LLCurl::Multi::~Multi()
{
cleanup() ;
cleanup(true) ;
delete mDeletionMutexp ;
mDeletionMutexp = NULL ;
}
void LLCurl::Multi::cleanup()
void LLCurl::Multi::cleanup(bool deleted)
{
if(!mCurlMultiHandle)
{
return ; //nothing to clean.
}
llassert_always(deleted || !mValid) ;
LLMutexLock lock(mDeletionMutexp);
// Clean up active
for(easy_active_list_t::iterator iter = mEasyActiveList.begin();
iter != mEasyActiveList.end(); ++iter)
{
Easy* easy = *iter;
check_curl_multi_code(curl_multi_remove_handle(mCurlMultiHandle, easy->getCurlHandle()));
if(deleted)
{
easy->mResponder = NULL ; //avoid triggering mResponder.
}
delete easy;
}
mEasyActiveList.clear();
@ -610,11 +622,9 @@ void LLCurl::Multi::cleanup()
check_curl_multi_code(LLCurl::deleteMultiHandle(mCurlMultiHandle));
mCurlMultiHandle = NULL ;
delete mMutexp ;
mMutexp = NULL ;
delete mDeletionMutexp ;
mDeletionMutexp = NULL ;
delete mEasyMutexp ;
mEasyMutexp = NULL ;
@ -644,10 +654,20 @@ void LLCurl::Multi::unlock()
void LLCurl::Multi::markDead()
{
LLMutexLock lock(mDeletionMutexp) ;
{
LLMutexLock lock(mDeletionMutexp) ;
mDead = TRUE ;
LLCurl::getCurlThread()->setPriority(mHandle, LLQueuedThread::PRIORITY_URGENT) ;
if(mCurlMultiHandle != NULL)
{
mDead = TRUE ;
LLCurl::getCurlThread()->setPriority(mHandle, LLQueuedThread::PRIORITY_URGENT) ;
return;
}
}
//not valid, delete it.
delete this;
}
void LLCurl::Multi::setState(LLCurl::Multi::ePerformState state)
@ -741,10 +761,14 @@ bool LLCurl::Multi::doPerform()
setState(STATE_COMPLETED) ;
mIdleTimer.reset() ;
}
else if(mIdleTimer.getElapsedTimeF32() > mIdleTimeOut) //idle for too long, remove it.
else if(!mValid && mIdleTimer.getElapsedTimeF32() > mIdleTimeOut) //idle for too long, remove it.
{
dead = true ;
}
else if(mValid && mIdleTimer.getElapsedTimeF32() > mIdleTimeOut - 1.f) //idle for too long, mark it invalid.
{
mValid = FALSE ;
}
return dead ;
}
@ -966,6 +990,7 @@ void LLCurlThread::killMulti(LLCurl::Multi* multi)
return ;
}
<<<<<<< local
if(multi->isValid())
{
multi->markDead() ;
@ -974,6 +999,9 @@ void LLCurlThread::killMulti(LLCurl::Multi* multi)
{
deleteMulti(multi) ;
}
=======
multi->markDead() ;
>>>>>>> other
}
//private
@ -992,6 +1020,10 @@ void LLCurlThread::deleteMulti(LLCurl::Multi* multi)
void LLCurlThread::cleanupMulti(LLCurl::Multi* multi)
{
multi->cleanup() ;
if(multi->isDead()) //check if marked dead during cleaning up.
{
deleteMulti(multi) ;
}
}
//------------------------------------------------------------

View File

@ -304,7 +304,7 @@ public:
ePerformState getState() ;
bool isCompleted() ;
bool isValid() {return mCurlMultiHandle != NULL ;}
bool isValid() {return mCurlMultiHandle != NULL && mValid;}
bool isDead() {return mDead;}
bool waitToComplete() ;
@ -318,7 +318,7 @@ public:
private:
void easyFree(LLCurl::Easy*);
void cleanup() ;
void cleanup(bool deleted = false) ;
CURLM* mCurlMultiHandle;
@ -333,6 +333,7 @@ private:
ePerformState mState;
BOOL mDead ;
BOOL mValid ;
LLMutex* mMutexp ;
LLMutex* mDeletionMutexp ;
LLMutex* mEasyMutexp ;

View File

@ -42,6 +42,7 @@
// external library headers
// other Linden headers
#include "../test/lltut.h"
#include "../test/catch_and_store_what_in.h"
#include "llsdserialize.h"
#include "llevents.h"
#include "stringize.h"
@ -72,43 +73,14 @@ namespace tut
template<> template<>
void llsdmessage_object::test<1>()
{
bool threw = false;
std::string threw;
// This should fail...
try
{
LLSDMessage localListener;
}
catch (const LLEventPump::DupPumpName&)
{
threw = true;
}
catch (const std::runtime_error& ex)
{
// This clause is because on Linux, on the viewer side, for this
// one test program (though not others!), the
// LLEventPump::DupPumpName exception isn't caught by the clause
// above. Warn the user...
std::cerr << "Failed to catch " << typeid(ex).name() << std::endl;
// But if the expected exception was thrown, allow the test to
// succeed anyway. Not sure how else to handle this odd case.
if (std::string(typeid(ex).name()) == typeid(LLEventPump::DupPumpName).name())
{
threw = true;
}
else
{
// We don't even recognize this exception. Let it propagate
// out to TUT to fail the test.
throw;
}
}
catch (...)
{
std::cerr << "Utterly failed to catch expected exception!" << std::endl;
// This case is full of fail. We HAVE to address it.
throw;
}
ensure("second LLSDMessage should throw", threw);
CATCH_AND_STORE_WHAT_IN(threw, LLEventPump::DupPumpName)
ensure("second LLSDMessage should throw", ! threw.empty());
}
template<> template<>

View File

@ -31,6 +31,7 @@
#include "llpluginprocessparent.h"
#include "llpluginmessagepipe.h"
#include "llpluginmessageclasses.h"
#include "stringize.h"
#include "llapr.h"
@ -133,8 +134,8 @@ LLPluginProcessParent::~LLPluginProcessParent()
// and remove it from our map
mSharedMemoryRegions.erase(iter);
}
mProcess.kill();
LLProcess::kill(mProcess);
killSockets();
}
@ -159,8 +160,8 @@ void LLPluginProcessParent::errorState(void)
void LLPluginProcessParent::init(const std::string &launcher_filename, const std::string &plugin_dir, const std::string &plugin_filename, bool debug)
{
mProcess.setExecutable(launcher_filename);
mProcess.setWorkingDirectory(plugin_dir);
mProcessParams.executable = launcher_filename;
mProcessParams.cwd = plugin_dir;
mPluginFile = plugin_filename;
mPluginDir = plugin_dir;
mCPUUsage = 0.0f;
@ -371,10 +372,8 @@ void LLPluginProcessParent::idle(void)
// Launch the plugin process.
// Only argument to the launcher is the port number we're listening on
std::stringstream stream;
stream << mBoundPort;
mProcess.addArgument(stream.str());
if(mProcess.launch() != 0)
mProcessParams.args.add(stringize(mBoundPort));
if (! (mProcess = LLProcess::create(mProcessParams)))
{
errorState();
}
@ -388,19 +387,18 @@ void LLPluginProcessParent::idle(void)
// The command we're constructing would look like this on the command line:
// osascript -e 'tell application "Terminal"' -e 'set win to do script "gdb -pid 12345"' -e 'do script "continue" in win' -e 'end tell'
std::stringstream cmd;
mDebugger.setExecutable("/usr/bin/osascript");
mDebugger.addArgument("-e");
mDebugger.addArgument("tell application \"Terminal\"");
mDebugger.addArgument("-e");
cmd << "set win to do script \"gdb -pid " << mProcess.getProcessID() << "\"";
mDebugger.addArgument(cmd.str());
mDebugger.addArgument("-e");
mDebugger.addArgument("do script \"continue\" in win");
mDebugger.addArgument("-e");
mDebugger.addArgument("end tell");
mDebugger.launch();
LLProcess::Params params;
params.executable = "/usr/bin/osascript";
params.args.add("-e");
params.args.add("tell application \"Terminal\"");
params.args.add("-e");
params.args.add(STRINGIZE("set win to do script \"gdb -pid "
<< mProcess->getProcessID() << "\""));
params.args.add("-e");
params.args.add("do script \"continue\" in win");
params.args.add("-e");
params.args.add("end tell");
mDebugger = LLProcess::create(params);
#endif
}
@ -470,7 +468,7 @@ void LLPluginProcessParent::idle(void)
break;
case STATE_EXITING:
if(!mProcess.isRunning())
if (! LLProcess::isRunning(mProcess))
{
setState(STATE_CLEANUP);
}
@ -498,7 +496,7 @@ void LLPluginProcessParent::idle(void)
break;
case STATE_CLEANUP:
mProcess.kill();
LLProcess::kill(mProcess);
killSockets();
setState(STATE_DONE);
break;
@ -1077,7 +1075,7 @@ bool LLPluginProcessParent::pluginLockedUpOrQuit()
{
bool result = false;
if(!mProcess.isRunning())
if (! LLProcess::isRunning(mProcess))
{
LL_WARNS("Plugin") << "child exited" << LL_ENDL;
result = true;

View File

@ -30,13 +30,14 @@
#define LL_LLPLUGINPROCESSPARENT_H
#include "llapr.h"
#include "llprocesslauncher.h"
#include "llprocess.h"
#include "llpluginmessage.h"
#include "llpluginmessagepipe.h"
#include "llpluginsharedmemory.h"
#include "lliosocket.h"
#include "llthread.h"
#include "llsd.h"
class LLPluginProcessParentOwner
{
@ -139,26 +140,27 @@ private:
};
EState mState;
void setState(EState state);
bool pluginLockedUp();
bool pluginLockedUpOrQuit();
bool accept();
LLSocket::ptr_t mListenSocket;
LLSocket::ptr_t mSocket;
U32 mBoundPort;
LLProcessLauncher mProcess;
LLProcess::Params mProcessParams;
LLProcessPtr mProcess;
std::string mPluginFile;
std::string mPluginDir;
LLPluginProcessParentOwner *mOwner;
typedef std::map<std::string, LLPluginSharedMemory*> sharedMemoryRegionsType;
sharedMemoryRegionsType mSharedMemoryRegions;
LLSD mMessageClassVersions;
std::string mPluginVersionString;
@ -171,7 +173,7 @@ private:
bool mBlocked;
bool mPolledInput;
LLProcessLauncher mDebugger;
LLProcessPtr mDebugger;
F32 mPluginLaunchTimeout; // Somewhat longer timeout for initial launch.
F32 mPluginLockupTimeout; // If we don't receive a heartbeat in this many seconds, we declare the plugin locked up.

View File

@ -94,6 +94,10 @@ void APIENTRY gl_debug_callback(GLenum source,
llwarns << "Severity: " << std::hex << severity << llendl;
llwarns << "Message: " << message << llendl;
llwarns << "-----------------------" << llendl;
if (severity == GL_DEBUG_SEVERITY_HIGH_ARB)
{
llerrs << "Halting on GL Error" << llendl;
}
}
#endif
@ -572,6 +576,15 @@ bool LLGLManager::initGL()
#endif
}
if (mGLVersion >= 2.1f && LLImageGL::sCompressTextures)
{ //use texture compression
glHint(GL_TEXTURE_COMPRESSION_HINT, GL_NICEST);
}
else
{ //GL version is < 3.0, always disable texture compression
LLImageGL::sCompressTextures = false;
}
// Trailing space necessary to keep "nVidia Corpor_ati_on" cards
// from being recognized as ATI.
if (mGLVendor.substr(0,4) == "ATI ")
@ -592,11 +605,8 @@ bool LLGLManager::initGL()
#endif // LL_WINDOWS
#if (LL_WINDOWS || LL_LINUX) && !LL_MESA_HEADLESS
// release 7277 is a point at which we verify that ATI OpenGL
// drivers get pretty stable with SL, ~Catalyst 8.2,
// for both Win32 and Linux.
if (mDriverVersionRelease < 7277 &&
mDriverVersionRelease != 0) // 0 == Undetectable driver version - these get to pretend to be new ATI drivers, though that decision may be revisited.
// count any pre OpenGL 3.0 implementation as an old driver
if (mGLVersion < 3.f)
{
mATIOldDriver = TRUE;
}
@ -735,6 +745,11 @@ bool LLGLManager::initGL()
}
#endif
if (mIsIntel && mGLVersion <= 3.f)
{ //never try to use framebuffer objects on older intel drivers (crashy)
mHasFramebufferObject = FALSE;
}
if (mHasFramebufferObject)
{
glGetIntegerv(GL_MAX_SAMPLES, &mMaxSamples);
@ -1897,7 +1912,7 @@ void LLGLState::checkClientArrays(const std::string& msg, U32 data_mask)
glClientActiveTextureARB(GL_TEXTURE0_ARB);
gGL.getTexUnit(0)->activate();
if (gGLManager.mHasVertexShader)
if (gGLManager.mHasVertexShader && LLGLSLShader::sNoFixedFunction)
{ //make sure vertex attribs are all disabled
GLint count;
glGetIntegerv(GL_MAX_VERTEX_ATTRIBS_ARB, &count);

View File

@ -56,6 +56,7 @@ BOOL LLImageGL::sGlobalUseAnisotropic = FALSE;
F32 LLImageGL::sLastFrameTime = 0.f;
BOOL LLImageGL::sAllowReadBackRaw = FALSE ;
LLImageGL* LLImageGL::sDefaultGLTexture = NULL ;
bool LLImageGL::sCompressTextures = false;
std::set<LLImageGL*> LLImageGL::sImageList;
@ -409,6 +410,8 @@ void LLImageGL::init(BOOL usemipmaps)
mDiscardLevelInAtlas = -1 ;
mTexelsInAtlas = 0 ;
mTexelsInGLTexture = 0 ;
mAllowCompression = true;
mTarget = GL_TEXTURE_2D;
mBindTarget = LLTexUnit::TT_TEXTURE;
@ -637,7 +640,7 @@ void LLImageGL::setImage(const U8* data_in, BOOL data_hasmips)
stop_glerror();
}
LLImageGL::setManualImage(mTarget, gl_level, mFormatInternal, w, h, mFormatPrimary, GL_UNSIGNED_BYTE, (GLvoid*)data_in);
LLImageGL::setManualImage(mTarget, gl_level, mFormatInternal, w, h, mFormatPrimary, GL_UNSIGNED_BYTE, (GLvoid*)data_in, mAllowCompression);
if (gl_level == 0)
{
analyzeAlpha(data_in, w, h);
@ -679,7 +682,7 @@ void LLImageGL::setImage(const U8* data_in, BOOL data_hasmips)
LLImageGL::setManualImage(mTarget, 0, mFormatInternal,
w, h,
mFormatPrimary, mFormatType,
data_in);
data_in, mAllowCompression);
analyzeAlpha(data_in, w, h);
stop_glerror();
@ -737,7 +740,7 @@ void LLImageGL::setImage(const U8* data_in, BOOL data_hasmips)
stop_glerror();
}
LLImageGL::setManualImage(mTarget, m, mFormatInternal, w, h, mFormatPrimary, mFormatType, cur_mip_data);
LLImageGL::setManualImage(mTarget, m, mFormatInternal, w, h, mFormatPrimary, mFormatType, cur_mip_data, mAllowCompression);
if (m == 0)
{
analyzeAlpha(data_in, w, h);
@ -795,7 +798,7 @@ void LLImageGL::setImage(const U8* data_in, BOOL data_hasmips)
}
LLImageGL::setManualImage(mTarget, 0, mFormatInternal, w, h,
mFormatPrimary, mFormatType, (GLvoid *)data_in);
mFormatPrimary, mFormatType, (GLvoid *)data_in, mAllowCompression);
analyzeAlpha(data_in, w, h);
updatePickMask(w, h, data_in);
@ -1042,7 +1045,7 @@ void LLImageGL::deleteTextures(S32 numTextures, U32 *textures, bool immediate)
}
// static
void LLImageGL::setManualImage(U32 target, S32 miplevel, S32 intformat, S32 width, S32 height, U32 pixformat, U32 pixtype, const void *pixels)
void LLImageGL::setManualImage(U32 target, S32 miplevel, S32 intformat, S32 width, S32 height, U32 pixformat, U32 pixtype, const void *pixels, bool allow_compression)
{
bool use_scratch = false;
U32* scratch = NULL;
@ -1105,6 +1108,36 @@ void LLImageGL::setManualImage(U32 target, S32 miplevel, S32 intformat, S32 widt
}
}
if (LLImageGL::sCompressTextures && allow_compression)
{
switch (intformat)
{
case GL_RGB:
case GL_RGB8:
intformat = GL_COMPRESSED_RGB;
break;
case GL_RGBA:
case GL_RGBA8:
intformat = GL_COMPRESSED_RGBA;
break;
case GL_LUMINANCE:
case GL_LUMINANCE8:
intformat = GL_COMPRESSED_LUMINANCE;
break;
case GL_LUMINANCE_ALPHA:
case GL_LUMINANCE8_ALPHA8:
intformat = GL_COMPRESSED_LUMINANCE_ALPHA;
break;
case GL_ALPHA:
case GL_ALPHA8:
intformat = GL_COMPRESSED_ALPHA;
break;
default:
llwarns << "Could not compress format: " << std::hex << intformat << llendl;
break;
}
}
stop_glerror();
glTexImage2D(target, miplevel, intformat, width, height, 0, pixformat, pixtype, use_scratch ? scratch : pixels);
stop_glerror();

View File

@ -94,12 +94,13 @@ public:
void setSize(S32 width, S32 height, S32 ncomponents);
void setComponents(S32 ncomponents) { mComponents = (S8)ncomponents ;}
void setAllowCompression(bool allow) { mAllowCompression = allow; }
// These 3 functions currently wrap glGenTextures(), glDeleteTextures(), and glTexImage2D()
// for tracking purposes and will be deprecated in the future
static void generateTextures(S32 numTextures, U32 *textures);
static void deleteTextures(S32 numTextures, U32 *textures, bool immediate = false);
static void setManualImage(U32 target, S32 miplevel, S32 intformat, S32 width, S32 height, U32 pixformat, U32 pixtype, const void *pixels);
static void setManualImage(U32 target, S32 miplevel, S32 intformat, S32 width, S32 height, U32 pixformat, U32 pixtype, const void *pixels, bool allow_compression = true);
BOOL createGLTexture() ;
BOOL createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S32 usename = 0, BOOL to_create = TRUE,
@ -210,6 +211,8 @@ private:
U32 mTexelsInAtlas ;
U32 mTexelsInGLTexture;
bool mAllowCompression;
protected:
LLGLenum mTarget; // Normally GL_TEXTURE2D, sometimes something else (ex. cube maps)
LLTexUnit::eTextureType mBindTarget; // Normally TT_TEXTURE, sometimes something else (ex. cube maps)
@ -245,7 +248,7 @@ public:
static BOOL sGlobalUseAnisotropic;
static LLImageGL* sDefaultGLTexture ;
static BOOL sAutomatedTest;
static bool sCompressTextures; //use GL texture compression
#if DEBUG_MISS
BOOL mMissed; // Missed on last bind?
BOOL getMissed() const { return mMissed; };

View File

@ -143,7 +143,7 @@ bool LLRenderTarget::addColorAttachment(U32 color_fmt)
{
clear_glerror();
LLImageGL::setManualImage(LLTexUnit::getInternalType(mUsage), 0, color_fmt, mResX, mResY, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
LLImageGL::setManualImage(LLTexUnit::getInternalType(mUsage), 0, color_fmt, mResX, mResY, GL_RGBA, GL_UNSIGNED_BYTE, NULL, false);
if (glGetError() != GL_NO_ERROR)
{
llwarns << "Could not allocate color buffer for render target." << llendl;
@ -223,7 +223,7 @@ bool LLRenderTarget::allocateDepth()
U32 internal_type = LLTexUnit::getInternalType(mUsage);
stop_glerror();
clear_glerror();
LLImageGL::setManualImage(internal_type, 0, GL_DEPTH_COMPONENT24, mResX, mResY, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL);
LLImageGL::setManualImage(internal_type, 0, GL_DEPTH_COMPONENT24, mResX, mResY, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL, false);
gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_POINT);
}

View File

@ -1062,8 +1062,9 @@ void LLShaderMgr::initAttribsAndUniforms()
mReservedUniforms.push_back("proj_shadow_res");
mReservedUniforms.push_back("depth_cutoff");
mReservedUniforms.push_back("norm_cutoff");
mReservedUniforms.push_back("shadow_target_width");
llassert(mReservedUniforms.size() == LLShaderMgr::DEFERRED_NORM_CUTOFF+1);
llassert(mReservedUniforms.size() == LLShaderMgr::DEFERRED_SHADOW_TARGET_WIDTH+1);
mReservedUniforms.push_back("tc_scale");
mReservedUniforms.push_back("rcp_screen_res");

View File

@ -130,6 +130,7 @@ public:
DEFERRED_PROJ_SHADOW_RES,
DEFERRED_DEPTH_CUTOFF,
DEFERRED_NORM_CUTOFF,
DEFERRED_SHADOW_TARGET_WIDTH,
FXAA_TC_SCALE,
FXAA_RCP_SCREEN_RES,

View File

@ -38,6 +38,12 @@
#include "llglslshader.h"
#include "llmemory.h"
#if LL_DARWIN
#define LL_VBO_POOLING 1
#else
#define LL_VBO_POOLING 0
#endif
//Next Highest Power Of Two
//helper function, returns first number > v that is a power of 2, or v if v is already a power of 2
U32 nhpo2(U32 v)
@ -49,6 +55,35 @@ U32 nhpo2(U32 v)
return r;
}
//which power of 2 is i?
//assumes i is a power of 2 > 0
U32 wpo2(U32 i)
{
llassert(i > 0);
llassert(nhpo2(i) == i);
U32 r = 0;
while (i >>= 1) ++r;
return r;
}
const U32 LL_VBO_BLOCK_SIZE = 2048;
U32 vbo_block_size(U32 size)
{ //what block size will fit size?
U32 mod = size % LL_VBO_BLOCK_SIZE;
return mod == 0 ? size : size + (LL_VBO_BLOCK_SIZE-mod);
}
U32 vbo_block_index(U32 size)
{
return vbo_block_size(size)/LL_VBO_BLOCK_SIZE;
}
//============================================================================
@ -57,7 +92,11 @@ LLVBOPool LLVertexBuffer::sStreamVBOPool(GL_STREAM_DRAW_ARB, GL_ARRAY_BUFFER_ARB
LLVBOPool LLVertexBuffer::sDynamicVBOPool(GL_DYNAMIC_DRAW_ARB, GL_ARRAY_BUFFER_ARB);
LLVBOPool LLVertexBuffer::sStreamIBOPool(GL_STREAM_DRAW_ARB, GL_ELEMENT_ARRAY_BUFFER_ARB);
LLVBOPool LLVertexBuffer::sDynamicIBOPool(GL_DYNAMIC_DRAW_ARB, GL_ELEMENT_ARRAY_BUFFER_ARB);
U32 LLVBOPool::sBytesPooled = 0;
U32 LLVBOPool::sIndexBytesPooled = 0;
U32 LLVertexBuffer::sAllocatedIndexBytes = 0;
U32 LLVertexBuffer::sIndexCount = 0;
LLPrivateMemoryPool* LLVertexBuffer::sPrivatePoolp = NULL;
U32 LLVertexBuffer::sBindCount = 0;
@ -74,6 +113,7 @@ U32 LLVertexBuffer::sLastMask = 0;
bool LLVertexBuffer::sVBOActive = false;
bool LLVertexBuffer::sIBOActive = false;
U32 LLVertexBuffer::sAllocatedBytes = 0;
U32 LLVertexBuffer::sVertexCount = 0;
bool LLVertexBuffer::sMapped = false;
bool LLVertexBuffer::sUseStreamDraw = true;
bool LLVertexBuffer::sUseVAO = false;
@ -134,39 +174,35 @@ public:
};
//which power of 2 is i?
//assumes i is a power of 2 > 0
U32 wpo2(U32 i)
{
llassert(i > 0);
llassert(nhpo2(i) == i);
U32 r = 0;
while (i >>= 1) ++r;
return r;
}
volatile U8* LLVBOPool::allocate(U32& name, U32 size)
{
llassert(nhpo2(size) == size);
llassert(vbo_block_size(size) == size);
volatile U8* ret = NULL;
U32 i = wpo2(size);
#if LL_VBO_POOLING
U32 i = vbo_block_index(size);
if (mFreeList.size() <= i)
{
mFreeList.resize(i+1);
}
volatile U8* ret = NULL;
if (mFreeList[i].empty())
{
//make a new buffer
glGenBuffersARB(1, &name);
glBindBufferARB(mType, name);
LLVertexBuffer::sAllocatedBytes += size;
if (mType == GL_ARRAY_BUFFER_ARB)
{
LLVertexBuffer::sAllocatedBytes += size;
}
else
{
LLVertexBuffer::sAllocatedIndexBytes += size;
}
if (LLVertexBuffer::sDisableVBOMapping || mUsage != GL_DYNAMIC_DRAW_ARB)
{
@ -185,19 +221,55 @@ volatile U8* LLVBOPool::allocate(U32& name, U32 size)
name = mFreeList[i].front().mGLName;
ret = mFreeList[i].front().mClientData;
sBytesPooled -= size;
if (mType == GL_ARRAY_BUFFER_ARB)
{
sBytesPooled -= size;
}
else
{
sIndexBytesPooled -= size;
}
mFreeList[i].pop_front();
}
#else //no pooling
glGenBuffersARB(1, &name);
glBindBufferARB(mType, name);
if (mType == GL_ARRAY_BUFFER_ARB)
{
LLVertexBuffer::sAllocatedBytes += size;
}
else
{
LLVertexBuffer::sAllocatedIndexBytes += size;
}
if (LLVertexBuffer::sDisableVBOMapping || mUsage != GL_DYNAMIC_DRAW_ARB)
{
glBufferDataARB(mType, size, 0, mUsage);
ret = (U8*) ll_aligned_malloc_16(size);
}
else
{ //always use a true hint of static draw when allocating non-client-backed buffers
glBufferDataARB(mType, size, 0, GL_STATIC_DRAW_ARB);
}
glBindBufferARB(mType, 0);
#endif
return ret;
}
void LLVBOPool::release(U32 name, volatile U8* buffer, U32 size)
{
llassert(nhpo2(size) == size);
llassert(vbo_block_size(size) == size);
U32 i = wpo2(size);
#if LL_VBO_POOLING
U32 i = vbo_block_index(size);
llassert(mFreeList.size() > i);
@ -211,9 +283,29 @@ void LLVBOPool::release(U32 name, volatile U8* buffer, U32 size)
}
else
{
sBytesPooled += size;
if (mType == GL_ARRAY_BUFFER_ARB)
{
sBytesPooled += size;
}
else
{
sIndexBytesPooled += size;
}
mFreeList[i].push_back(rec);
}
#else //no pooling
glDeleteBuffersARB(1, &name);
ll_aligned_free_16((U8*) buffer);
if (mType == GL_ARRAY_BUFFER_ARB)
{
LLVertexBuffer::sAllocatedBytes -= size;
}
else
{
LLVertexBuffer::sAllocatedIndexBytes -= size;
}
#endif
}
void LLVBOPool::cleanup()
@ -237,8 +329,16 @@ void LLVBOPool::cleanup()
l.pop_front();
LLVertexBuffer::sAllocatedBytes -= size;
sBytesPooled -= size;
if (mType == GL_ARRAY_BUFFER_ARB)
{
sBytesPooled -= size;
LLVertexBuffer::sAllocatedBytes -= size;
}
else
{
sIndexBytesPooled -= size;
LLVertexBuffer::sAllocatedIndexBytes -= size;
}
}
size *= 2;
@ -898,6 +998,9 @@ LLVertexBuffer::~LLVertexBuffer()
mFence = NULL;
sVertexCount -= mNumVerts;
sIndexCount -= mNumIndices;
llassert_always(!mMappedData && !mMappedIndexData);
};
@ -929,7 +1032,7 @@ void LLVertexBuffer::waitFence() const
void LLVertexBuffer::genBuffer(U32 size)
{
mSize = nhpo2(size);
mSize = vbo_block_size(size);
if (mUsage == GL_STREAM_DRAW_ARB)
{
@ -945,7 +1048,7 @@ void LLVertexBuffer::genBuffer(U32 size)
void LLVertexBuffer::genIndices(U32 size)
{
mIndicesSize = nhpo2(size);
mIndicesSize = vbo_block_size(size);
if (mUsage == GL_STREAM_DRAW_ARB)
{
@ -1121,7 +1224,9 @@ void LLVertexBuffer::updateNumVerts(S32 nverts)
createGLBuffer(needed_size);
}
sVertexCount -= mNumVerts;
mNumVerts = nverts;
sVertexCount += mNumVerts;
}
void LLVertexBuffer::updateNumIndices(S32 nindices)
@ -1137,7 +1242,9 @@ void LLVertexBuffer::updateNumIndices(S32 nindices)
createGLIndices(needed_size);
}
sIndexCount -= mNumIndices;
mNumIndices = nindices;
sIndexCount += mNumIndices;
}
void LLVertexBuffer::allocateBuffer(S32 nverts, S32 nindices, bool create)

View File

@ -55,6 +55,7 @@ class LLVBOPool
{
public:
static U32 sBytesPooled;
static U32 sIndexBytesPooled;
LLVBOPool(U32 vboUsage, U32 vboType)
: mUsage(vboUsage)
@ -332,6 +333,9 @@ public:
static bool sIBOActive;
static U32 sLastMask;
static U32 sAllocatedBytes;
static U32 sAllocatedIndexBytes;
static U32 sVertexCount;
static U32 sIndexCount;
static U32 sBindCount;
static U32 sSetCount;
};

View File

@ -12,7 +12,6 @@ include(LLRender)
include(LLWindow)
include(LLVFS)
include(LLXML)
include(LLXUIXML)
include_directories(
${LLCOMMON_INCLUDE_DIRS}
@ -24,7 +23,6 @@ include_directories(
${LLWINDOW_INCLUDE_DIRS}
${LLVFS_INCLUDE_DIRS}
${LLXML_INCLUDE_DIRS}
${LLXUIXML_INCLUDE_DIRS}
)
set(llui_SOURCE_FILES
@ -83,7 +81,6 @@ set(llui_SOURCE_FILES
llscrolllistcolumn.cpp
llscrolllistctrl.cpp
llscrolllistitem.cpp
llsdparam.cpp
llsearcheditor.cpp
llslider.cpp
llsliderctrl.cpp
@ -100,11 +97,13 @@ set(llui_SOURCE_FILES
lltextutil.cpp
lltextvalidate.cpp
lltimectrl.cpp
lltrans.cpp
lltransutil.cpp
lltoggleablemenu.cpp
lltoolbar.cpp
lltooltip.cpp
llui.cpp
lluicolor.cpp
lluicolortable.cpp
lluictrl.cpp
lluictrlfactory.cpp
@ -121,6 +120,7 @@ set(llui_SOURCE_FILES
llview.cpp
llviewquery.cpp
llwindowshade.cpp
llxuiparser.cpp
)
set(llui_HEADER_FILES
@ -189,7 +189,6 @@ set(llui_HEADER_FILES
llscrolllistcolumn.h
llscrolllistctrl.h
llscrolllistitem.h
llsdparam.h
llsliderctrl.h
llslider.h
llspinctrl.h
@ -208,6 +207,7 @@ set(llui_HEADER_FILES
lltoggleablemenu.h
lltoolbar.h
lltooltip.h
lltrans.h
lltransutil.h
lluicolortable.h
lluiconstants.h
@ -215,6 +215,7 @@ set(llui_HEADER_FILES
lluictrl.h
lluifwd.h
llui.h
lluicolor.h
lluiimage.h
lluistring.h
llundo.h
@ -228,6 +229,7 @@ set(llui_HEADER_FILES
llview.h
llviewquery.h
llwindowshade.h
llxuiparser.h
)
set_source_files_properties(${llui_HEADER_FILES}

View File

@ -349,7 +349,7 @@ void LLMultiFloater::setVisible(BOOL visible)
BOOL LLMultiFloater::handleKeyHere(KEY key, MASK mask)
{
if (key == 'W' && mask == (MASK_CONTROL|MASK_SHIFT))
if (key == 'W' && mask == MASK_CONTROL)
{
LLFloater* floater = getActiveFloater();
// is user closeable and is system closeable

View File

@ -399,6 +399,7 @@ LLNotificationTemplate::LLNotificationTemplate(const LLNotificationTemplate::Par
: mName(p.name),
mType(p.type),
mMessage(p.value),
mFooter(p.footer.value),
mLabel(p.label),
mIcon(p.icon),
mURL(p.url.value),
@ -870,6 +871,16 @@ std::string LLNotification::getMessage() const
return message;
}
std::string LLNotification::getFooter() const
{
if (!mTemplatep)
return std::string();
std::string footer = mTemplatep->mFooter;
LLStringUtil::format(footer, mSubstitutions);
return footer;
}
std::string LLNotification::getLabel() const
{
std::string label = mTemplatep->mLabel;

View File

@ -509,6 +509,7 @@ public:
std::string getType() const;
std::string getMessage() const;
std::string getFooter() const;
std::string getLabel() const;
std::string getURL() const;
S32 getURLOption() const;

View File

@ -167,6 +167,17 @@ struct LLNotificationTemplate
{}
};
struct Footer : public LLInitParam::Block<Footer>
{
Mandatory<std::string> value;
Footer()
: value("value")
{
addSynonym(value, "");
}
};
struct Params : public LLInitParam::Block<Params>
{
Mandatory<std::string> name;
@ -184,7 +195,8 @@ struct LLNotificationTemplate
Optional<FormRef> form_ref;
Optional<ENotificationPriority,
NotificationPriorityValues> priority;
Multiple<Tag> tags;
Multiple<Tag> tags;
Optional<Footer> footer;
Params()
@ -202,7 +214,8 @@ struct LLNotificationTemplate
url("url"),
unique("unique"),
form_ref(""),
tags("tag")
tags("tag"),
footer("footer")
{}
};
@ -231,6 +244,8 @@ struct LLNotificationTemplate
// The text used to display the notification. Replaceable parameters
// are enclosed in square brackets like this [].
std::string mMessage;
// The text used to display the notification, but under the form.
std::string mFooter;
// The label for the notification; used for
// certain classes of notification (those with a window and a window title).
// Also used when a notification pops up underneath the current one.

View File

@ -105,28 +105,6 @@ LLStyle::Params::Params()
namespace LLInitParam
{
Param::Param(BaseBlock* enclosing_block)
: mIsProvided(false)
{
const U8* my_addr = reinterpret_cast<const U8*>(this);
const U8* block_addr = reinterpret_cast<const U8*>(enclosing_block);
mEnclosingBlockOffset = (U16)(my_addr - block_addr);
}
void BaseBlock::addParam(BlockDescriptor& block_data, const ParamDescriptorPtr in_param, const char* char_name){}
void BaseBlock::addSynonym(Param& param, const std::string& synonym) {}
param_handle_t BaseBlock::getHandleFromParam(const Param* param) const {return 0;}
void BaseBlock::init(BlockDescriptor& descriptor, BlockDescriptor& base_descriptor, size_t block_size)
{
descriptor.mCurrentBlockPtr = this;
}
bool BaseBlock::deserializeBlock(Parser& p, Parser::name_stack_range_t name_stack, bool new_name){ return true; }
void BaseBlock::serializeBlock(Parser& parser, Parser::name_stack_t& name_stack, const LLInitParam::BaseBlock* diff_block) const {}
bool BaseBlock::inspectBlock(Parser& parser, Parser::name_stack_t name_stack, S32 min_value, S32 max_value) const { return true; }
bool BaseBlock::mergeBlock(BlockDescriptor& block_data, const BaseBlock& other, bool overwrite) { return true; }
bool BaseBlock::validateBlock(bool emit_errors) const { return true; }
ParamValue<LLUIColor, TypeValues<LLUIColor> >::ParamValue(const LLUIColor& color)
: super_t(color)
{}

View File

@ -70,21 +70,6 @@ S32 LLUIImage::getHeight() const
return 0;
}
namespace LLInitParam
{
BlockDescriptor::BlockDescriptor() {}
ParamDescriptor::ParamDescriptor(param_handle_t p,
merge_func_t merge_func,
deserialize_func_t deserialize_func,
serialize_func_t serialize_func,
validation_func_t validation_func,
inspect_func_t inspect_func,
S32 min_count,
S32 max_count){}
ParamDescriptor::~ParamDescriptor() {}
}
namespace tut
{
struct LLUrlEntryData

View File

@ -63,40 +63,6 @@ S32 LLUIImage::getHeight() const
namespace LLInitParam
{
BlockDescriptor::BlockDescriptor() {}
ParamDescriptor::ParamDescriptor(param_handle_t p,
merge_func_t merge_func,
deserialize_func_t deserialize_func,
serialize_func_t serialize_func,
validation_func_t validation_func,
inspect_func_t inspect_func,
S32 min_count,
S32 max_count){}
ParamDescriptor::~ParamDescriptor() {}
void BaseBlock::addParam(BlockDescriptor& block_data, const ParamDescriptorPtr in_param, const char* char_name){}
param_handle_t BaseBlock::getHandleFromParam(const Param* param) const {return 0;}
void BaseBlock::addSynonym(Param& param, const std::string& synonym) {}
void BaseBlock::init(BlockDescriptor& descriptor, BlockDescriptor& base_descriptor, size_t block_size)
{
descriptor.mCurrentBlockPtr = this;
}
Param::Param(BaseBlock* enclosing_block)
: mIsProvided(false)
{
const U8* my_addr = reinterpret_cast<const U8*>(this);
const U8* block_addr = reinterpret_cast<const U8*>(enclosing_block);
mEnclosingBlockOffset = 0x7FFFffff & ((U32)(my_addr - block_addr));
}
bool BaseBlock::deserializeBlock(Parser& p, Parser::name_stack_range_t name_stack, bool new_name){ return true; }
void BaseBlock::serializeBlock(Parser& parser, Parser::name_stack_t& name_stack, const LLInitParam::BaseBlock* diff_block) const {}
bool BaseBlock::inspectBlock(Parser& parser, Parser::name_stack_t name_stack, S32 min_count, S32 max_count) const { return true; }
bool BaseBlock::mergeBlock(BlockDescriptor& block_data, const BaseBlock& other, bool overwrite) { return true; }
bool BaseBlock::validateBlock(bool emit_errors) const { return true; }
ParamValue<LLUIColor, TypeValues<LLUIColor> >::ParamValue(const LLUIColor& color)
: super_t(color)
{}

View File

@ -86,6 +86,13 @@ S32 LLDir::deleteFilesInDir(const std::string &dirname, const std::string &mask)
std::string fullpath;
S32 result;
// File masks starting with "/" will match nothing, so we consider them invalid.
if (LLStringUtil::startsWith(mask, getDirDelimiter()))
{
llwarns << "Invalid file mask: " << mask << llendl;
llassert(!"Invalid file mask");
}
LLDirIterator iter(dirname, mask);
while (iter.next(filename))
{

View File

@ -1477,7 +1477,8 @@ BOOL LLWindowWin32::switchContext(BOOL fullscreen, const LLCoordScreen &size, BO
}
else
{
llinfos << "Created OpenGL " << llformat("%d.%d", attribs[1], attribs[3]) << " context." << llendl;
llinfos << "Created OpenGL " << llformat("%d.%d", attribs[1], attribs[3]) <<
(LLRender::sGLCoreProfile ? " core" : " compatibility") << " context." << llendl;
done = true;
if (LLRender::sGLCoreProfile)

View File

@ -1,45 +0,0 @@
# -*- cmake -*-
project(llxuixml)
include(00-Common)
include(LLCommon)
include(LLMath)
include(LLXML)
include_directories(
${LLCOMMON_INCLUDE_DIRS}
${LLMATH_INCLUDE_DIRS}
${LLXML_INCLUDE_DIRS}
)
set(llxuixml_SOURCE_FILES
llinitparam.cpp
lltrans.cpp
lluicolor.cpp
llxuiparser.cpp
)
set(llxuixml_HEADER_FILES
CMakeLists.txt
llinitparam.h
lltrans.h
llregistry.h
lluicolor.h
llxuiparser.h
)
set_source_files_properties(${llxuixml_HEADER_FILES}
PROPERTIES HEADER_FILE_ONLY TRUE)
list(APPEND llxuixml_SOURCE_FILES ${llxuixml_HEADER_FILES})
add_library (llxuixml ${llxuixml_SOURCE_FILES})
# Libraries on which this library depends, needed for Linux builds
# Sort by high-level to low-level
target_link_libraries(llxuixml
llxml
llcommon
llmath
)

View File

@ -1188,9 +1188,7 @@ void *updatethreadproc(void*)
llinfos << "Clearing cache..." << llendl;
char mask[LL_MAX_PATH]; /* Flawfinder: ignore */
snprintf(mask, LL_MAX_PATH, "%s*.*", gDirUtilp->getDirDelimiter().c_str());
gDirUtilp->deleteFilesInDir(gDirUtilp->getExpandedFilename(LL_PATH_CACHE,""),mask);
gDirUtilp->deleteFilesInDir(gDirUtilp->getExpandedFilename(LL_PATH_CACHE,""), "*.*");
llinfos << "Clear complete." << llendl;

View File

@ -30,7 +30,6 @@ include(LLUI)
include(LLVFS)
include(LLWindow)
include(LLXML)
include(LLXUIXML)
include(LScript)
include(Linking)
include(NDOF)
@ -66,7 +65,6 @@ include_directories(
${LLVFS_INCLUDE_DIRS}
${LLWINDOW_INCLUDE_DIRS}
${LLXML_INCLUDE_DIRS}
${LLXUIXML_INCLUDE_DIRS}
${LSCRIPT_INCLUDE_DIRS}
${LSCRIPT_INCLUDE_DIRS}/lscript_compile
${LLLOGIN_INCLUDE_DIRS}
@ -237,7 +235,7 @@ set(viewer_SOURCE_FILES
llfloatertelehub.cpp
llfloatertestinspectors.cpp
llfloatertestlistview.cpp
llfloatertexturefetchdebugger.cpp
llfloatertexturefetchdebugger.cpp
llfloatertools.cpp
llfloatertopobjects.cpp
llfloatertos.cpp
@ -491,6 +489,7 @@ set(viewer_SOURCE_FILES
lltoastnotifypanel.cpp
lltoastpanel.cpp
lltoastscripttextbox.cpp
lltoastscriptquestion.cpp
lltool.cpp
lltoolbarview.cpp
lltoolbrush.cpp
@ -794,7 +793,7 @@ set(viewer_HEADER_FILES
llfloatertelehub.h
llfloatertestinspectors.h
llfloatertestlistview.h
llfloatertexturefetchdebugger.h
llfloatertexturefetchdebugger.h
llfloatertools.h
llfloatertopobjects.h
llfloatertos.h
@ -1039,6 +1038,7 @@ set(viewer_HEADER_FILES
lltoastnotifypanel.h
lltoastpanel.h
lltoastscripttextbox.h
lltoastscriptquestion.h
lltool.h
lltoolbarview.h
lltoolbrush.h
@ -1748,7 +1748,6 @@ target_link_libraries(${VIEWER_BINARY_NAME}
${LLVFS_LIBRARIES}
${LLWINDOW_LIBRARIES}
${LLXML_LIBRARIES}
${LLXUIXML_LIBRARIES}
${LSCRIPT_LIBRARIES}
${LLMATH_LIBRARIES}
${LLCOMMON_LIBRARIES}

View File

@ -1,21 +1,76 @@
<?xml version="1.0"?>
<llsd>
<map>
<key>help</key>
<!-- Please insert new keys in alphabetical order. -->
<key>analyzeperformance</key>
<map>
<key>desc</key>
<string>display this help message</string>
<key>short</key>
<string>h</string>
<string>When used in conjunction with logperformance, analyzes result of log against baseline.</string>
<key>map-to</key>
<string>AnalyzePerformance</string>
</map>
<key>port</key>
<key>autologin</key>
<map>
<key>desc</key>
<string>log in as last saved user</string>
<key>map-to</key>
<string>AutoLogin</string>
</map>
<key>channel</key>
<map>
<key>count</key>
<integer>1</integer>
<!-- Special case. Not mapped to a setting. -->
</map>
<key>console</key>
<map>
<key>count</key>
<integer>1</integer>
<key>map-to</key>
<string>UserConnectionPort</string>
<string>ShowConsoleWindow</string>
</map>
<key>cooperative</key>
<map>
<key>desc</key>
<string>Yield some idle time to local host.</string>
<key>count</key>
<integer>1</integer>
<key>map-to</key>
<string>YieldTime</string>
</map>
<key>crashonstartup</key>
<map>
<key>desc</key>
<string>Crashes on startup. For QA use.</string>
<key>map-to</key>
<string>CrashOnStartup</string>
</map>
<key>debugsession</key>
<map>
<key>desc</key>
<string>Run as if RenderDebugGL is TRUE, but log errors until end of session.</string>
<key>map-to</key>
<string>DebugSession</string>
</map>
<key>debugviews</key>
<map>
<key>map-to</key>
<string>DebugViews</string>
</map>
<key>disablecrashlogger</key>
<map>
<key>desc</key>
<string>Disables the crash logger and lets the OS handle crashes</string>
<key>map-to</key>
<string>DisableCrashLogger</string>
</map>
<key>drop</key>
@ -26,20 +81,21 @@
<string>PacketDropPercentage</string>
</map>
<key>inbw</key>
<key>god</key>
<map>
<key>count</key>
<integer>1</integer>
<key>desc</key>
<string>Log in a god if you have god access.</string>
<key>map-to</key>
<string>InBandwidth</string>
<string>ConnectAsGod</string>
</map>
<key>outbw</key>
<key>graphicslevel</key>
<map>
<key>desc</key>
<string>Set the detail level.
0 - low, 1 - medium, 2 - high, 3 - ultra</string>
<key>count</key>
<integer>1</integer>
<key>map-to</key>
<string>OutBandwidth</string>
</map>
<key>grid</key>
@ -52,6 +108,82 @@
<string>CmdLineGridChoice</string>
</map>
<key>help</key>
<map>
<key>desc</key>
<string>display this help message</string>
<key>short</key>
<string>h</string>
</map>
<key>helperuri</key>
<map>
<key>desc</key>
<string>helper web CGI prefix to use</string>
<key>count</key>
<integer>1</integer>
<key>map-to</key>
<string>CmdLineHelperURI</string>
</map>
<key>ignorepixeldepth</key>
<map>
<key>desc</key>
<string>Ignore pixel depth settings.</string>
<key>map-to</key>
<string>IgnorePixelDepth</string>
</map>
<key>inbw</key>
<map>
<key>count</key>
<integer>1</integer>
<key>map-to</key>
<string>InBandwidth</string>
</map>
<key>leap</key>
<map>
<key>desc</key>
<string>command line to run an LLSD Event API Plugin</string>
<key>count</key>
<integer>1</integer>
<!-- you can specify multiple such plugins -->
<key>compose</key>
<boolean>true</boolean>
<key>map-to</key>
<string>LeapCommand</string>
</map>
<key>logfile</key>
<map>
<key>count</key>
<integer>1</integer>
<key>map-to</key>
<string>UserLogFile</string>
</map>
<key>login</key>
<map>
<key>desc</key>
<string>3 tokens: first, last and password</string>
<key>count</key>
<integer>3</integer>
<key>map-to</key>
<string>UserLoginInfo</string>
</map>
<key>loginpage</key>
<map>
<key>desc</key>
<string>Login authentication page to use.</string>
<key>count</key>
<integer>1</integer>
<key>map-to</key>
<string>LoginPage</string>
</map>
<key>loginuri</key>
<map>
<key>desc</key>
@ -64,46 +196,14 @@
<string>CmdLineLoginURI</string>
</map>
<key>helperuri</key>
<key>logmetrics</key>
<map>
<key>desc</key>
<string>helper web CGI prefix to use</string>
<string>Log metrics for benchmarking</string>
<key>count</key>
<integer>1</integer>
<key>map-to</key>
<string>CmdLineHelperURI</string>
</map>
<key>debugviews</key>
<map>
<key>map-to</key>
<string>DebugViews</string>
</map>
<key>skin</key>
<map>
<key>desc</key>
<string>ui/branding skin folder to use</string>
<key>count</key>
<integer>1</integer>
<key>map-to</key>
<string>SkinFolder</string>
</map>
<key>autologin</key>
<map>
<key>desc</key>
<string>log in as last saved user</string>
<key>map-to</key>
<string>AutoLogin</string>
</map>
<key>quitafter</key>
<map>
<key>count</key>
<integer>1</integer>
<key>map-to</key>
<string>QuitAfterSeconds</string>
<string>LogMetrics</string>
</map>
<key>logperformance</key>
@ -114,38 +214,26 @@
<string>LogPerformance</string>
</map>
<key>logmetrics</key>
<key>multiple</key>
<map>
<key>desc</key>
<string>Log metrics for benchmarking</string>
<key>count</key>
<integer>1</integer>
<string>Allow multiple viewers.</string>
<key>map-to</key>
<string>LogMetrics</string>
</map>
<key>analyzeperformance</key>
<map>
<key>desc</key>
<string>When used in conjunction with logperformance, analyzes result of log against baseline.</string>
<key>map-to</key>
<string>AnalyzePerformance</string>
<string>AllowMultipleViewers</string>
</map>
<key>debugsession</key>
<key>noaudio</key>
<map>
<key>desc</key>
<string>Run as if RenderDebugGL is TRUE, but log errors until end of session.</string>
<key>map-to</key>
<string>DebugSession</string>
<string>NoAudio</string>
</map>
<key>replaysession</key>
<key>noinvlib</key>
<map>
<key>desc</key>
<string>After login, replay last recorded session and quit.</string>
<string>Do not request the inventory library.</string>
<key>map-to</key>
<string>ReplaySession</string>
<string>NoInventoryLibrary</string>
</map>
<key>nonotifications</key>
@ -156,22 +244,10 @@
<string>IgnoreAllNotifications</string>
</map>
<key>rotate</key>
<key>nopreload</key>
<map>
<key>map-to</key>
<string>RotateRight</string>
</map>
<key>noaudio</key>
<map>
<key>map-to</key>
<string>NoAudio</string>
</map>
<key>nosound</key>
<map>
<key>map-to</key>
<string>NoAudio</string>
<string>NoPreload</string>
</map>
<key>noprobe</key>
@ -186,10 +262,40 @@
<string>NoQuickTime</string>
</map>
<key>nopreload</key>
<key>nosound</key>
<map>
<key>map-to</key>
<string>NoPreload</string>
<string>NoAudio</string>
</map>
<key>no-verify-ssl-cert</key>
<map>
<key>map-to</key>
<string>NoVerifySSLCert</string>
</map>
<key>novoice</key>
<map>
<key>desc</key>
<string>Disable voice.</string>
<key>map-to</key>
<string>CmdLineDisableVoice</string>
</map>
<key>outbw</key>
<map>
<key>count</key>
<integer>1</integer>
<key>map-to</key>
<string>OutBandwidth</string>
</map>
<key>port</key>
<map>
<key>count</key>
<integer>1</integer>
<key>map-to</key>
<string>UserConnectionPort</string>
</map>
<key>purge</key>
@ -200,37 +306,50 @@
<string>PurgeCacheOnNextStartup</string>
</map>
<key>noinvlib</key>
<key>qa</key>
<map>
<key>desc</key>
<string>Do not request the inventory library.</string>
<string>Activated debugging menu in Advanced Settings.</string>
<key>map-to</key>
<string>NoInventoryLibrary</string>
<string>QAMode</string>
</map>
<key>logfile</key>
<key>quitafter</key>
<map>
<key>count</key>
<integer>1</integer>
<key>map-to</key>
<string>UserLogFile</string>
<string>QuitAfterSeconds</string>
</map>
<key>graphicslevel</key>
<key>replaysession</key>
<map>
<key>desc</key>
<string>Set the detail level.
0 - low, 1 - medium, 2 - high, 3 - ultra</string>
<string>After login, replay last recorded session and quit.</string>
<key>map-to</key>
<string>ReplaySession</string>
</map>
<key>rotate</key>
<map>
<key>map-to</key>
<string>RotateRight</string>
</map>
<key>safe</key>
<map>
<key>desc</key>
<string>Reset preferences, run in safe mode.</string>
<key>map-to</key>
<string>SafeMode</string>
</map>
<key>sessionsettings</key>
<map>
<key>desc</key>
<string>Specify the filename of a configuration file that contains temporary per-session configuration overrides.</string>
<key>count</key>
<integer>1</integer>
</map>
<key>setdefault</key>
<map>
<key>desc</key>
<string>specify the value of a particular configuration variable which can be overridden by settings.xml.</string>
<key>count</key>
<integer>2</integer>
<!-- Special case. Mapped to settings procedurally. -->
</map>
@ -245,6 +364,15 @@
<!-- Special case. Mapped to settings procedurally. -->
</map>
<key>setdefault</key>
<map>
<key>desc</key>
<string>specify the value of a particular configuration variable which can be overridden by settings.xml.</string>
<key>count</key>
<integer>2</integer>
<!-- Special case. Mapped to settings procedurally. -->
</map>
<key>settings</key>
<map>
<key>desc</key>
@ -254,83 +382,14 @@
<!-- Special case. Mapped to settings procedurally. -->
</map>
<key>sessionsettings</key>
<key>skin</key>
<map>
<key>desc</key>
<string>Specify the filename of a configuration file that contains temporary per-session configuration overrides.</string>
<key>count</key>
<integer>1</integer>
<!-- Special case. Mapped to settings procedurally. -->
</map>
<key>usersessionsettings</key>
<map>
<key>desc</key>
<string>Specify the filename of a configuration file that contains temporary per-session configuration user overrides.</string>
<key>count</key>
<integer>1</integer>
<!-- Special case. Mapped to settings procedurally. -->
</map>
<key>login</key>
<map>
<key>desc</key>
<string>3 tokens: first, last and password</string>
<key>count</key>
<integer>3</integer>
<key>map-to</key>
<string>UserLoginInfo</string>
</map>
<key>god</key>
<map>
<key>desc</key>
<string>Log in a god if you have god access.</string>
<key>map-to</key>
<string>ConnectAsGod</string>
</map>
<key>console</key>
<map>
<string>ui/branding skin folder to use</string>
<key>count</key>
<integer>1</integer>
<key>map-to</key>
<string>ShowConsoleWindow</string>
</map>
<key>safe</key>
<map>
<key>desc</key>
<string>Reset preferences, run in safe mode.</string>
<key>map-to</key>
<string>SafeMode</string>
</map>
<key>multiple</key>
<map>
<key>desc</key>
<string>Allow multiple viewers.</string>
<key>map-to</key>
<string>AllowMultipleViewers</string>
</map>
<key>novoice</key>
<map>
<key>desc</key>
<string>Disable voice.</string>
<key>map-to</key>
<string>CmdLineDisableVoice</string>
</map>
<key>url</key>
<map>
<key>desc</key>
<string>Startup location</string>
<key>count</key>
<integer>1</integer>
<key>last_option</key>
<boolean>true</boolean>
<!-- Special case. Not mapped to a setting. -->
<string>SkinFolder</string>
</map>
<key>slurl</key>
@ -346,69 +405,24 @@
<!-- Special case. Not mapped to a setting. -->
</map>
<key>ignorepixeldepth</key>
<key>url</key>
<map>
<key>desc</key>
<string>Ignore pixel depth settings.</string>
<key>map-to</key>
<string>IgnorePixelDepth</string>
</map>
<key>cooperative</key>
<map>
<key>desc</key>
<string>Yield some idle time to local host.</string>
<key>count</key>
<integer>1</integer>
<key>map-to</key>
<string>YieldTime</string>
</map>
<key>no-verify-ssl-cert</key>
<map>
<key>map-to</key>
<string>NoVerifySSLCert</string>
</map>
<key>channel</key>
<map>
<string>Startup location</string>
<key>count</key>
<integer>1</integer>
<key>last_option</key>
<boolean>true</boolean>
<!-- Special case. Not mapped to a setting. -->
</map>
<key>loginpage</key>
<key>usersessionsettings</key>
<map>
<key>desc</key>
<string>Login authentication page to use.</string>
<string>Specify the filename of a configuration file that contains temporary per-session configuration user overrides.</string>
<key>count</key>
<integer>1</integer>
<key>map-to</key>
<string>LoginPage</string>
</map>
<key>qa</key>
<map>
<key>desc</key>
<string>Activated debugging menu in Advanced Settings.</string>
<key>map-to</key>
<string>QAMode</string>
</map>
<key>crashonstartup</key>
<map>
<key>desc</key>
<string>Crashes on startup. For QA use.</string>
<key>map-to</key>
<string>CrashOnStartup</string>
</map>
<key>disablecrashlogger</key>
<map>
<key>desc</key>
<string>Disables the crash logger and lets the OS handle crashes</string>
<key>map-to</key>
<string>DisableCrashLogger</string>
<!-- Special case. Mapped to settings procedurally. -->
</map>
</map>
</llsd>

View File

@ -4601,6 +4601,17 @@
<key>Value</key>
<integer>0</integer>
</map>
<key>LeapCommand</key>
<map>
<key>Comment</key>
<string>Zero or more command lines to run LLSD Event API Plugin programs.</string>
<key>Persist</key>
<integer>0</integer>
<key>Type</key>
<string>LLSD</string>
<key>Value</key>
<array />
</map>
<key>LSLFindCaseInsensitivity</key>
<map>
<key>Comment</key>
@ -7164,7 +7175,7 @@
<key>QAModeEventHostPort</key>
<map>
<key>Comment</key>
<string>Port on which lleventhost should listen</string>
<string>DEPRECATED: Port on which lleventhost should listen</string>
<key>Persist</key>
<integer>0</integer>
<key>Type</key>
@ -7642,6 +7653,17 @@
<key>Value</key>
<integer>1</integer>
</map>
<key>RenderCompressTextures</key>
<map>
<key>Comment</key>
<string>Enable texture compression on OpenGL 3.0 and later implementations (EXPERIMENTAL, requires restart)</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>0</integer>
</map>
<key>RenderPerformanceTest</key>
<map>
<key>Comment</key>
@ -9134,28 +9156,19 @@
<key>Value</key>
<integer>0</integer>
</map>
<key>RenderUseShaderLOD</key>
<map>
<key>Comment</key>
<string>Whether we want to have different shaders for LOD</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>1</integer>
</map>
<key>RenderUseShaderNearParticles</key>
<map>
<key>Comment</key>
<string>Whether we want to use shaders on near particles</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>0</integer>
</map>
<key>RenderAutoHideSurfaceAreaLimit</key>
<map>
<key>Comment</key>
<string>Maximum surface area of a set of proximal objects inworld before automatically hiding geometry to prevent system overload.</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>F32</string>
<key>Value</key>
<integer>0</integer>
</map>
<key>RenderVBOEnable</key>
<map>
<key>Comment</key>

View File

@ -29,11 +29,11 @@ out vec4 frag_color;
#define frag_color gl_FragColor
#endif
uniform float minimum_alpha;
uniform sampler2D diffuseMap;
VARYING vec4 post_pos;
VARYING float pos_zd2;
VARYING float pos_w;
VARYING float target_pos_x;
VARYING vec4 vertex_color;
VARYING vec2 vary_texcoord0;
@ -41,12 +41,20 @@ void main()
{
float alpha = diffuseLookup(vary_texcoord0.xy).a * vertex_color.a;
if (alpha < minimum_alpha)
if (alpha < 0.05) // treat as totally transparent
{
discard;
}
if (alpha < 0.88) // treat as semi-transparent
{
if (fract(0.5*floor(target_pos_x / pos_w )) < 0.25)
{
discard;
}
}
frag_color = vec4(1,1,1,1);
gl_FragDepth = max(post_pos.z/post_pos.w*0.5+0.5, 0.0);
gl_FragDepth = max(pos_zd2/pos_w+0.5, 0.0);
}

View File

@ -25,12 +25,15 @@
uniform mat4 texture_matrix0;
uniform mat4 modelview_projection_matrix;
uniform float shadow_target_width;
ATTRIBUTE vec3 position;
ATTRIBUTE vec4 diffuse_color;
ATTRIBUTE vec2 texcoord0;
VARYING vec4 post_pos;
VARYING float pos_zd2;
VARYING float pos_w;
VARYING float target_pos_x;
VARYING vec4 vertex_color;
VARYING vec2 vary_texcoord0;
@ -39,8 +42,11 @@ void passTextureIndex();
void main()
{
//transform vertex
vec4 pos = modelview_projection_matrix*vec4(position.xyz, 1.0);
post_pos = pos;
vec4 pre_pos = vec4(position.xyz, 1.0);
vec4 pos = modelview_projection_matrix * pre_pos;
target_pos_x = 0.5 * (shadow_target_width - 1.0) * pos.x;
pos_w = pos.w;
pos_zd2 = pos.z * 0.5;
gl_Position = vec4(pos.x, pos.y, pos.w*0.5, pos.w);

View File

@ -58,20 +58,22 @@ uniform float shadow_bias;
uniform mat4 inv_proj;
float pcfShadow(sampler2DRectShadow shadowMap, vec4 stc, float scl)
float pcfShadow(sampler2DRectShadow shadowMap, vec4 stc)
{
stc.xyz /= stc.w;
stc.z += shadow_bias;
stc.x = floor(stc.x + fract(stc.y*12345)); // add some chaotic jitter to X sample pos according to Y to disguise the snapping going on here
float cs = shadow2DRect(shadowMap, stc.xyz).x;
float shadow = cs;
shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(scl, scl, 0.0)).x, cs);
shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(scl, -scl, 0.0)).x, cs);
shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(-scl, scl, 0.0)).x, cs);
shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(-scl, -scl, 0.0)).x, cs);
return shadow/5.0;
shadow += shadow2DRect(shadowMap, stc.xyz+vec3(2.0, 1.5, 0.0)).x;
shadow += shadow2DRect(shadowMap, stc.xyz+vec3(1.0, -1.5, 0.0)).x;
shadow += shadow2DRect(shadowMap, stc.xyz+vec3(-1.0, 1.5, 0.0)).x;
shadow += shadow2DRect(shadowMap, stc.xyz+vec3(-2.0, -1.5, 0.0)).x;
return shadow*0.2;
}
@ -101,7 +103,7 @@ void main()
float w = 1.0;
w -= max(spos.z-far_split.z, 0.0)/transition_domain.z;
shadow += pcfShadow(shadowMap3, lpos, 0.25)*w;
shadow += pcfShadow(shadowMap3, lpos)*w;
weight += w;
shadow += max((pos.z+shadow_clip.z)/(shadow_clip.z-shadow_clip.w)*2.0-1.0, 0.0);
}
@ -114,7 +116,7 @@ void main()
float w = 1.0;
w -= max(spos.z-far_split.y, 0.0)/transition_domain.y;
w -= max(near_split.z-spos.z, 0.0)/transition_domain.z;
shadow += pcfShadow(shadowMap2, lpos, 0.75)*w;
shadow += pcfShadow(shadowMap2, lpos)*w;
weight += w;
}
@ -126,7 +128,7 @@ void main()
float w = 1.0;
w -= max(spos.z-far_split.x, 0.0)/transition_domain.x;
w -= max(near_split.y-spos.z, 0.0)/transition_domain.y;
shadow += pcfShadow(shadowMap1, lpos, 0.75)*w;
shadow += pcfShadow(shadowMap1, lpos)*w;
weight += w;
}
@ -138,7 +140,7 @@ void main()
float w = 1.0;
w -= max(near_split.x-spos.z, 0.0)/transition_domain.x;
shadow += pcfShadow(shadowMap0, lpos, 1.0)*w;
shadow += pcfShadow(shadowMap0, lpos)*w;
weight += w;
}

View File

@ -71,20 +71,22 @@ vec4 getPosition(vec2 pos_screen)
return pos;
}
float pcfShadow(sampler2DRectShadow shadowMap, vec4 stc, float scl)
float pcfShadow(sampler2DRectShadow shadowMap, vec4 stc)
{
stc.xyz /= stc.w;
stc.z += shadow_bias;
stc.x = floor(stc.x + fract(stc.y*12345)); // add some chaotic jitter to X sample pos according to Y to disguise the snapping going on here
float cs = shadow2DRect(shadowMap, stc.xyz).x;
float shadow = cs;
shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(scl, scl, 0.0)).x, cs);
shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(scl, -scl, 0.0)).x, cs);
shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(-scl, scl, 0.0)).x, cs);
shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(-scl, -scl, 0.0)).x, cs);
return shadow/5.0;
shadow += shadow2DRect(shadowMap, stc.xyz+vec3(2.0, 1.5, 0.0)).x;
shadow += shadow2DRect(shadowMap, stc.xyz+vec3(1.0, -1.5, 0.0)).x;
shadow += shadow2DRect(shadowMap, stc.xyz+vec3(-1.0, 1.5, 0.0)).x;
shadow += shadow2DRect(shadowMap, stc.xyz+vec3(-2.0, -1.5, 0.0)).x;
return shadow*0.2;
}
@ -114,7 +116,7 @@ void main()
float w = 1.0;
w -= max(spos.z-far_split.z, 0.0)/transition_domain.z;
shadow += pcfShadow(shadowMap3, lpos, 0.25)*w;
shadow += pcfShadow(shadowMap3, lpos)*w;
weight += w;
shadow += max((pos.z+shadow_clip.z)/(shadow_clip.z-shadow_clip.w)*2.0-1.0, 0.0);
}
@ -127,7 +129,7 @@ void main()
float w = 1.0;
w -= max(spos.z-far_split.y, 0.0)/transition_domain.y;
w -= max(near_split.z-spos.z, 0.0)/transition_domain.z;
shadow += pcfShadow(shadowMap2, lpos, 0.75)*w;
shadow += pcfShadow(shadowMap2, lpos)*w;
weight += w;
}
@ -139,7 +141,7 @@ void main()
float w = 1.0;
w -= max(spos.z-far_split.x, 0.0)/transition_domain.x;
w -= max(near_split.y-spos.z, 0.0)/transition_domain.y;
shadow += pcfShadow(shadowMap1, lpos, 0.75)*w;
shadow += pcfShadow(shadowMap1, lpos)*w;
weight += w;
}
@ -151,7 +153,7 @@ void main()
float w = 1.0;
w -= max(near_split.x-spos.z, 0.0)/transition_domain.x;
shadow += pcfShadow(shadowMap0, lpos, 1.0)*w;
shadow += pcfShadow(shadowMap0, lpos)*w;
weight += w;
}

View File

@ -70,20 +70,22 @@ vec4 getPosition(vec2 pos_screen)
return pos;
}
float pcfShadow(sampler2DRectShadow shadowMap, vec4 stc, float scl)
float pcfShadow(sampler2DRectShadow shadowMap, vec4 stc)
{
stc.xyz /= stc.w;
stc.z += shadow_bias;
stc.x = floor(stc.x + fract(stc.y*12345)); // add some chaotic jitter to X sample pos according to Y to disguise the snapping going on here
float cs = shadow2DRect(shadowMap, stc.xyz).x;
float shadow = cs;
shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(scl, scl, 0.0)).x, cs);
shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(scl, -scl, 0.0)).x, cs);
shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(-scl, scl, 0.0)).x, cs);
shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(-scl, -scl, 0.0)).x, cs);
return shadow/5.0;
shadow += shadow2DRect(shadowMap, stc.xyz+vec3(2.0, 1.5, 0.0)).x;
shadow += shadow2DRect(shadowMap, stc.xyz+vec3(1.0, -1.5, 0.0)).x;
shadow += shadow2DRect(shadowMap, stc.xyz+vec3(-1.0, 1.5, 0.0)).x;
shadow += shadow2DRect(shadowMap, stc.xyz+vec3(-2.0, -1.5, 0.0)).x;
return shadow*0.2;
}
@ -113,7 +115,7 @@ void main()
float w = 1.0;
w -= max(spos.z-far_split.z, 0.0)/transition_domain.z;
shadow += pcfShadow(shadowMap3, lpos, 0.25)*w;
shadow += pcfShadow(shadowMap3, lpos)*w;
weight += w;
shadow += max((pos.z+shadow_clip.z)/(shadow_clip.z-shadow_clip.w)*2.0-1.0, 0.0);
}
@ -126,7 +128,7 @@ void main()
float w = 1.0;
w -= max(spos.z-far_split.y, 0.0)/transition_domain.y;
w -= max(near_split.z-spos.z, 0.0)/transition_domain.z;
shadow += pcfShadow(shadowMap2, lpos, 0.75)*w;
shadow += pcfShadow(shadowMap2, lpos)*w;
weight += w;
}
@ -138,7 +140,7 @@ void main()
float w = 1.0;
w -= max(spos.z-far_split.x, 0.0)/transition_domain.x;
w -= max(near_split.y-spos.z, 0.0)/transition_domain.y;
shadow += pcfShadow(shadowMap1, lpos, 0.75)*w;
shadow += pcfShadow(shadowMap1, lpos)*w;
weight += w;
}
@ -150,7 +152,7 @@ void main()
float w = 1.0;
w -= max(near_split.x-spos.z, 0.0)/transition_domain.x;
shadow += pcfShadow(shadowMap0, lpos, 1.0)*w;
shadow += pcfShadow(shadowMap0, lpos)*w;
weight += w;
}

View File

@ -78,42 +78,42 @@ vec4 getPosition(vec2 pos_screen)
return pos;
}
float pcfShadow(sampler2DRectShadow shadowMap, vec4 stc, float scl)
float pcfShadow(sampler2DRectShadow shadowMap, vec4 stc, float scl, vec2 pos_screen)
{
stc.xyz /= stc.w;
stc.z += shadow_bias*scl;
stc.x = floor(stc.x + fract(pos_screen.y*0.666666666)); // add some jitter to X sample pos according to Y to disguise the snapping going on here
float cs = shadow2DRect(shadowMap, stc.xyz).x;
float shadow = cs;
shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(1.5, 1.5, 0.0)).x, cs);
shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(1.5, -1.5, 0.0)).x, cs);
shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(-1.5, 1.5, 0.0)).x, cs);
shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(-1.5, -1.5, 0.0)).x, cs);
shadow += shadow2DRect(shadowMap, stc.xyz+vec3(2.0, 1.5, 0.0)).x;
shadow += shadow2DRect(shadowMap, stc.xyz+vec3(1.0, -1.5, 0.0)).x;
shadow += shadow2DRect(shadowMap, stc.xyz+vec3(-2.0, 1.5, 0.0)).x;
shadow += shadow2DRect(shadowMap, stc.xyz+vec3(-1.0, -1.5, 0.0)).x;
return shadow/5.0;
//return shadow;
return shadow*0.2;
}
float pcfShadow(sampler2DShadow shadowMap, vec4 stc, float scl)
float pcfShadow(sampler2DShadow shadowMap, vec4 stc, float scl, vec2 pos_screen)
{
stc.xyz /= stc.w;
stc.z += spot_shadow_bias*scl;
stc.x = floor(proj_shadow_res.x * stc.x + fract(pos_screen.y*0.666666666)) / proj_shadow_res.x; // snap
float cs = shadow2D(shadowMap, stc.xyz).x;
float shadow = cs;
vec2 off = 1.5/proj_shadow_res;
vec2 off = 1.0/proj_shadow_res;
off.y *= 1.5;
shadow += max(shadow2D(shadowMap, stc.xyz+vec3(off.x, off.y, 0.0)).x, cs);
shadow += max(shadow2D(shadowMap, stc.xyz+vec3(off.x, -off.y, 0.0)).x, cs);
shadow += max(shadow2D(shadowMap, stc.xyz+vec3(-off.x, off.y, 0.0)).x, cs);
shadow += max(shadow2D(shadowMap, stc.xyz+vec3(-off.x, -off.y, 0.0)).x, cs);
return shadow/5.0;
//return shadow;
shadow += shadow2D(shadowMap, stc.xyz+vec3(off.x*2.0, off.y, 0.0)).x;
shadow += shadow2D(shadowMap, stc.xyz+vec3(off.x, -off.y, 0.0)).x;
shadow += shadow2D(shadowMap, stc.xyz+vec3(-off.x, off.y, 0.0)).x;
shadow += shadow2D(shadowMap, stc.xyz+vec3(-off.x*2.0, -off.y, 0.0)).x;
return shadow*0.2;
}
void main()
@ -166,7 +166,7 @@ void main()
float w = 1.0;
w -= max(spos.z-far_split.z, 0.0)/transition_domain.z;
shadow += pcfShadow(shadowMap3, lpos, 0.25)*w;
shadow += pcfShadow(shadowMap3, lpos, 0.25, pos_screen)*w;
weight += w;
shadow += max((pos.z+shadow_clip.z)/(shadow_clip.z-shadow_clip.w)*2.0-1.0, 0.0);
}
@ -179,7 +179,7 @@ void main()
float w = 1.0;
w -= max(spos.z-far_split.y, 0.0)/transition_domain.y;
w -= max(near_split.z-spos.z, 0.0)/transition_domain.z;
shadow += pcfShadow(shadowMap2, lpos, 0.75)*w;
shadow += pcfShadow(shadowMap2, lpos, 0.5, pos_screen)*w;
weight += w;
}
@ -191,7 +191,7 @@ void main()
float w = 1.0;
w -= max(spos.z-far_split.x, 0.0)/transition_domain.x;
w -= max(near_split.y-spos.z, 0.0)/transition_domain.y;
shadow += pcfShadow(shadowMap1, lpos, 0.75)*w;
shadow += pcfShadow(shadowMap1, lpos, 0.75, pos_screen)*w;
weight += w;
}
@ -203,7 +203,7 @@ void main()
float w = 1.0;
w -= max(near_split.x-spos.z, 0.0)/transition_domain.x;
shadow += pcfShadow(shadowMap0, lpos, 1.0)*w;
shadow += pcfShadow(shadowMap0, lpos, 1.0, pos_screen)*w;
weight += w;
}
@ -237,11 +237,11 @@ void main()
//spotlight shadow 1
vec4 lpos = shadow_matrix[4]*spos;
frag_color[2] = pcfShadow(shadowMap4, lpos, 0.8);
frag_color[2] = pcfShadow(shadowMap4, lpos, 0.8, pos_screen);
//spotlight shadow 2
lpos = shadow_matrix[5]*spos;
frag_color[3] = pcfShadow(shadowMap5, lpos, 0.8);
frag_color[3] = pcfShadow(shadowMap5, lpos, 0.8, pos_screen);
//frag_color.rgb = pos.xyz;
//frag_color.b = shadow;

View File

@ -90,7 +90,7 @@ vec2 getKern(int i)
kern[5] = vec2(-0.7071, -0.7071) * 0.750*0.750;
kern[6] = vec2(-0.7071, 0.7071) * 0.875*0.875;
kern[7] = vec2(0.7071, -0.7071) * 1.000*1.000;
return kern[i];
}
@ -139,42 +139,42 @@ float calcAmbientOcclusion(vec4 pos, vec3 norm)
return min(ret, 1.0);
}
float pcfShadow(sampler2DRectShadow shadowMap, vec4 stc, float scl)
float pcfShadow(sampler2DRectShadow shadowMap, vec4 stc, float scl, vec2 pos_screen)
{
stc.xyz /= stc.w;
stc.z += shadow_bias*scl;
stc.x = floor(stc.x + fract(pos_screen.y*0.666666666));
float cs = shadow2DRect(shadowMap, stc.xyz).x;
float shadow = cs;
shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(1.5, 1.5, 0.0)).x, cs);
shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(1.5, -1.5, 0.0)).x, cs);
shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(-1.5, 1.5, 0.0)).x, cs);
shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(-1.5, -1.5, 0.0)).x, cs);
return shadow/5.0;
//return shadow;
shadow += shadow2DRect(shadowMap, stc.xyz+vec3(2.0, 1.5, 0.0)).x;
shadow += shadow2DRect(shadowMap, stc.xyz+vec3(1.0, -1.5, 0.0)).x;
shadow += shadow2DRect(shadowMap, stc.xyz+vec3(-1.0, 1.5, 0.0)).x;
shadow += shadow2DRect(shadowMap, stc.xyz+vec3(-2.0, -1.5, 0.0)).x;
return shadow*0.2;
}
float pcfShadow(sampler2DShadow shadowMap, vec4 stc, float scl)
float pcfShadow(sampler2DShadow shadowMap, vec4 stc, float scl, vec2 pos_screen)
{
stc.xyz /= stc.w;
stc.z += spot_shadow_bias*scl;
stc.x = floor(proj_shadow_res.x * stc.x + fract(pos_screen.y*0.666666666)) / proj_shadow_res.x; // snap
float cs = shadow2D(shadowMap, stc.xyz).x;
float shadow = cs;
vec2 off = 1.5/proj_shadow_res;
vec2 off = 1.0/proj_shadow_res;
off.y *= 1.5;
shadow += max(shadow2D(shadowMap, stc.xyz+vec3(off.x, off.y, 0.0)).x, cs);
shadow += max(shadow2D(shadowMap, stc.xyz+vec3(off.x, -off.y, 0.0)).x, cs);
shadow += max(shadow2D(shadowMap, stc.xyz+vec3(-off.x, off.y, 0.0)).x, cs);
shadow += max(shadow2D(shadowMap, stc.xyz+vec3(-off.x, -off.y, 0.0)).x, cs);
return shadow/5.0;
//return shadow;
shadow += shadow2D(shadowMap, stc.xyz+vec3(off.x*2.0, off.y, 0.0)).x;
shadow += shadow2D(shadowMap, stc.xyz+vec3(off.x, -off.y, 0.0)).x;
shadow += shadow2D(shadowMap, stc.xyz+vec3(-off.x, off.y, 0.0)).x;
shadow += shadow2D(shadowMap, stc.xyz+vec3(-off.x*2.0, -off.y, 0.0)).x;
return shadow*0.2;
}
void main()
@ -227,7 +227,7 @@ void main()
float w = 1.0;
w -= max(spos.z-far_split.z, 0.0)/transition_domain.z;
shadow += pcfShadow(shadowMap3, lpos, 0.25)*w;
shadow += pcfShadow(shadowMap3, lpos, 0.25, pos_screen)*w;
weight += w;
shadow += max((pos.z+shadow_clip.z)/(shadow_clip.z-shadow_clip.w)*2.0-1.0, 0.0);
}
@ -240,7 +240,7 @@ void main()
float w = 1.0;
w -= max(spos.z-far_split.y, 0.0)/transition_domain.y;
w -= max(near_split.z-spos.z, 0.0)/transition_domain.z;
shadow += pcfShadow(shadowMap2, lpos, 0.75)*w;
shadow += pcfShadow(shadowMap2, lpos, 0.5, pos_screen)*w;
weight += w;
}
@ -252,7 +252,7 @@ void main()
float w = 1.0;
w -= max(spos.z-far_split.x, 0.0)/transition_domain.x;
w -= max(near_split.y-spos.z, 0.0)/transition_domain.y;
shadow += pcfShadow(shadowMap1, lpos, 0.75)*w;
shadow += pcfShadow(shadowMap1, lpos, 0.75, pos_screen)*w;
weight += w;
}
@ -264,7 +264,7 @@ void main()
float w = 1.0;
w -= max(near_split.x-spos.z, 0.0)/transition_domain.x;
shadow += pcfShadow(shadowMap0, lpos, 1.0)*w;
shadow += pcfShadow(shadowMap0, lpos, 1.0, pos_screen)*w;
weight += w;
}
@ -298,11 +298,11 @@ void main()
//spotlight shadow 1
vec4 lpos = shadow_matrix[4]*spos;
frag_color[2] = pcfShadow(shadowMap4, lpos, 0.8);
frag_color[2] = pcfShadow(shadowMap4, lpos, 0.8, pos_screen);
//spotlight shadow 2
lpos = shadow_matrix[5]*spos;
frag_color[3] = pcfShadow(shadowMap5, lpos, 0.8);
frag_color[3] = pcfShadow(shadowMap5, lpos, 0.8, pos_screen);
//frag_color.rgb = pos.xyz;
//frag_color.b = shadow;

View File

@ -61,6 +61,7 @@ WLSkyDetail 1 128
Disregard128DefaultDrawDistance 1 1
Disregard96DefaultDrawDistance 1 1
RenderTextureMemoryMultiple 1 1.0
RenderCompressTextures 1 1
RenderShaderLightingMaxLevel 1 3
RenderDeferred 1 1
RenderDeferredSSAO 1 1
@ -71,6 +72,38 @@ RenderFSAASamples 1 16
RenderMaxTextureIndex 1 16
//
// Low Graphics Settings (fixed function)
//
list LowFixedFunction
RenderAnisotropic 1 0
RenderAvatarCloth 1 0
RenderAvatarLODFactor 1 0
RenderAvatarPhysicsLODFactor 1 0
RenderAvatarMaxVisible 1 3
RenderAvatarVP 1 0
RenderFarClip 1 64
RenderFlexTimeFactor 1 0
RenderGlowResolutionPow 1 8
RenderMaxPartCount 1 0
RenderObjectBump 1 0
RenderLocalLights 1 0
RenderReflectionDetail 1 0
RenderTerrainDetail 1 0
RenderTerrainLODFactor 1 1
RenderTransparentWater 1 0
RenderTreeLODFactor 1 0
RenderUseImpostors 1 1
RenderVolumeLODFactor 1 1.125
VertexShaderEnable 1 0
WindLightUseAtmosShaders 1 0
WLSkyDetail 1 48
RenderDeferred 1 0
RenderDeferredSSAO 1 0
RenderShadowDetail 1 0
RenderFSAASamples 1 0
//
// Low Graphics Settings
//
@ -94,7 +127,7 @@ RenderTransparentWater 1 0
RenderTreeLODFactor 1 0
RenderUseImpostors 1 1
RenderVolumeLODFactor 1 1.125
VertexShaderEnable 1 0
VertexShaderEnable 1 1
WindLightUseAtmosShaders 1 0
WLSkyDetail 1 48
RenderDeferred 1 0
@ -222,6 +255,12 @@ RenderVBOEnable 1 1
list Class3
RenderVBOEnable 1 1
//
// VRAM > 512MB
//
list VRAMGT512
RenderCompressTextures 1 0
//
// No Pixel Shaders available
//
@ -302,6 +341,7 @@ RenderMaxTextureIndex 1 1
list Intel
RenderAnisotropic 1 0
RenderVBOEnable 1 0
RenderFSAASamples 1 0
list GeForce2
RenderAnisotropic 1 0

View File

@ -61,6 +61,7 @@ WLSkyDetail 1 128
Disregard128DefaultDrawDistance 1 1
Disregard96DefaultDrawDistance 1 1
RenderTextureMemoryMultiple 1 1.0
RenderCompressTextures 1 1
RenderShaderLightingMaxLevel 1 3
RenderDeferred 1 1
RenderDeferredSSAO 1 1
@ -68,6 +69,37 @@ RenderShadowDetail 1 2
RenderFSAASamples 1 16
RenderMaxTextureIndex 1 16
//
// Low Graphics Settings (fixed function)
//
list LowFixedFunction
RenderAnisotropic 1 0
RenderAvatarCloth 1 0
RenderAvatarLODFactor 1 0
RenderAvatarPhysicsLODFactor 1 0
RenderAvatarMaxVisible 1 3
RenderAvatarVP 1 0
RenderFarClip 1 64
RenderFlexTimeFactor 1 0
RenderGlowResolutionPow 1 8
RenderLocalLights 1 0
RenderMaxPartCount 1 0
RenderObjectBump 1 0
RenderReflectionDetail 1 0
RenderTerrainDetail 1 0
RenderTerrainLODFactor 1 1
RenderTransparentWater 1 0
RenderTreeLODFactor 1 0
RenderUseImpostors 1 1
RenderVolumeLODFactor 1 0.5
VertexShaderEnable 1 1
WindLightUseAtmosShaders 1 0
WLSkyDetail 1 48
RenderDeferred 1 0
RenderDeferredSSAO 1 0
RenderShadowDetail 1 0
RenderFSAASamples 1 0
//
// Low Graphics Settings
//
@ -219,6 +251,12 @@ RenderVBOEnable 1 1
list Class3
RenderVBOEnable 1 1
//
// VRAM > 512MB
//
list VRAMGT512
RenderCompressTextures 1 0
//
// No Pixel Shaders available
//
@ -291,10 +329,15 @@ RenderObjectBump 0 0
list OpenGLPre15
RenderVBOEnable 1 0
list OpenGLPre30
RenderDeferred 0 0
RenderMaxTextureIndex 1 1
list Intel
RenderAnisotropic 1 0
// Avoid some Intel crashes on Linux
RenderCubeMap 0 0
RenderFSAASamples 1 0
list GeForce2
RenderAnisotropic 1 0

View File

@ -61,6 +61,7 @@ WLSkyDetail 1 128
Disregard128DefaultDrawDistance 1 1
Disregard96DefaultDrawDistance 1 1
RenderTextureMemoryMultiple 1 0.5
RenderCompressTextures 1 1
RenderShaderLightingMaxLevel 1 3
RenderDeferred 1 1
RenderDeferredSSAO 1 1
@ -70,6 +71,37 @@ RenderUseStreamVBO 1 1
RenderFSAASamples 1 16
RenderMaxTextureIndex 1 16
//
// Low Graphics Settings (fixed function)
//
list LowFixedFunction
RenderAnisotropic 1 0
RenderAvatarCloth 1 0
RenderAvatarLODFactor 1 0
RenderAvatarPhysicsLODFactor 1 0
RenderAvatarMaxVisible 1 3
RenderAvatarVP 1 0
RenderFarClip 1 64
RenderFlexTimeFactor 1 0
RenderGlowResolutionPow 1 8
RenderLocalLights 1 0
RenderMaxPartCount 1 0
RenderObjectBump 1 0
RenderReflectionDetail 1 0
RenderTerrainDetail 1 0
RenderTerrainLODFactor 1 1
RenderTransparentWater 1 0
RenderTreeLODFactor 1 0
RenderUseImpostors 1 1
RenderVolumeLODFactor 1 0.5
VertexShaderEnable 1 0
WindLightUseAtmosShaders 1 0
WLSkyDetail 1 48
RenderDeferred 1 0
RenderDeferredSSAO 1 0
RenderShadowDetail 1 0
RenderFSAASamples 1 0
//
// Low Graphics Settings
//
@ -93,7 +125,7 @@ RenderTransparentWater 1 0
RenderTreeLODFactor 1 0
RenderUseImpostors 1 1
RenderVolumeLODFactor 1 0.5
VertexShaderEnable 1 0
VertexShaderEnable 1 1
WindLightUseAtmosShaders 1 0
WLSkyDetail 1 48
RenderDeferred 1 0
@ -247,6 +279,12 @@ RenderDeferred 0 0
RenderDeferredSSAO 0 0
RenderShadowDetail 0 0
//
// VRAM > 512MB
//
list VRAMGT512
RenderCompressTextures 1 0
//
// "Default" setups for safe, low, medium, high
//
@ -286,6 +324,7 @@ RenderObjectBump 0 0
list OpenGLPre15
RenderVBOEnable 1 0
list TexUnit8orLess
RenderDeferredSSAO 0 0
@ -295,6 +334,7 @@ RenderDeferredSSAO 1 0
list Intel
RenderAnisotropic 1 0
RenderLocalLights 1 0
RenderFSAASamples 1 0
list GeForce2
RenderAnisotropic 1 0

View File

@ -61,6 +61,7 @@ WLSkyDetail 1 128
Disregard128DefaultDrawDistance 1 1
Disregard96DefaultDrawDistance 1 1
RenderTextureMemoryMultiple 1 1.0
RenderCompressTextures 1 1
RenderShaderLightingMaxLevel 1 3
RenderDeferred 1 0
RenderDeferredSSAO 1 0
@ -70,6 +71,37 @@ RenderUseStreamVBO 1 1
RenderFSAASamples 1 16
RenderMaxTextureIndex 1 16
//
// Low Graphics Settings (fixed function)
//
list LowFixedFunction
RenderAnisotropic 1 0
RenderAvatarCloth 1 0
RenderAvatarLODFactor 1 0
RenderAvatarPhysicsLODFactor 1 0
RenderAvatarMaxVisible 1 3
RenderAvatarVP 1 0
RenderFarClip 1 64
RenderFlexTimeFactor 1 0
RenderGlowResolutionPow 1 8
RenderLocalLights 1 0
RenderMaxPartCount 1 0
RenderObjectBump 1 0
RenderReflectionDetail 1 0
RenderTerrainDetail 1 0
RenderTerrainLODFactor 1 1
RenderTransparentWater 1 0
RenderTreeLODFactor 1 0
RenderUseImpostors 1 1
RenderVolumeLODFactor 1 0.5
VertexShaderEnable 1 0
WindLightUseAtmosShaders 1 0
WLSkyDetail 1 48
RenderDeferred 1 0
RenderDeferredSSAO 1 0
RenderShadowDetail 1 0
RenderFSAASamples 1 0
//
// Low Graphics Settings
//
@ -93,7 +125,7 @@ RenderTransparentWater 1 0
RenderTreeLODFactor 1 0
RenderUseImpostors 1 1
RenderVolumeLODFactor 1 0.5
VertexShaderEnable 1 0
VertexShaderEnable 1 1
WindLightUseAtmosShaders 1 0
WLSkyDetail 1 48
RenderDeferred 1 0
@ -221,6 +253,12 @@ RenderVBOEnable 1 1
list Class3
RenderVBOEnable 1 1
//
// VRAM > 512MB
//
list VRAMGT512
RenderCompressTextures 1 0
//
// No Pixel Shaders available
//
@ -299,6 +337,7 @@ RenderMaxTextureIndex 1 1
list Intel
RenderAnisotropic 1 0
RenderVBOEnable 1 0
RenderFSAASamples 1 0
list GeForce2
RenderAnisotropic 1 0

View File

@ -205,6 +205,7 @@ ATI Radeon X800 .*ATI.*Radeon ?X8.* 2 1
ATI Radeon X900 .*ATI.*Radeon ?X9.* 2 1
ATI Radeon Xpress .*ATI.*Radeon Xpress.* 0 1
ATI Rage 128 .*ATI.*Rage 128.* 0 1
ATI R300 (9700) .*R300.* 1 1
ATI R350 (9800) .*R350.* 1 1
ATI R580 (X1900) .*R580.* 3 1
ATI RC410 (Xpress 200) .*RC410.* 0 0

View File

@ -110,22 +110,34 @@ export SAVED_LD_LIBRARY_PATH="${LD_LIBRARY_PATH}"
# fi
#fi
export SL_ENV='LD_LIBRARY_PATH="`pwd`"/lib:"${LD_LIBRARY_PATH}"'
export SL_CMD='$LL_WRAPPER bin/do-not-directly-run-secondlife-bin'
export SL_OPT="`cat etc/gridargs.dat` $@"
export LD_LIBRARY_PATH="$PWD/lib:${LD_LIBRARY_PATH}"
# Run the program
eval ${SL_ENV} ${SL_CMD} ${SL_OPT} || LL_RUN_ERR=runerr
# Have to deal specially with gridargs.dat; typical contents look like:
# --channel "Second Life Developer" --settings settings_developer.xml
# Simply embedding $(<etc/gridargs.dat) into a command line treats each of
# Second, Life and Developer as separate args -- no good. We need bash to
# process quotes using eval.
# First read it without scanning, then scan that string. Break quoted words
# into a bash array. Note that if gridargs.dat is empty, or contains only
# whitespace, the resulting gridargs array will be empty -- zero entries --
# therefore "${gridargs[@]}" entirely vanishes from the command line below,
# just as we want.
eval gridargs=("$(<etc/gridargs.dat)")
# Run the program.
# Don't quote $LL_WRAPPER because, if empty, it should simply vanish from the
# command line. But DO quote "$@": preserve separate args as individually
# quoted. Similar remarks about the contents of gridargs.
$LL_WRAPPER bin/do-not-directly-run-secondlife-bin "${gridargs[@]}" "$@"
LL_RUN_ERR=$?
# Handle any resulting errors
if [ -n "$LL_RUN_ERR" ]; then
LL_RUN_ERR_MSG=""
if [ "$LL_RUN_ERR" = "runerr" ]; then
# generic error running the binary
echo '*** Bad shutdown. ***'
if [ "`uname -m`" = "x86_64" ]; then
echo
cat << EOFMARKER
if [ $LL_RUN_ERR -ne 0 ]; then
# generic error running the binary
echo '*** Bad shutdown ($LL_RUN_ERR). ***'
if [ "$(uname -m)" = "x86_64" ]; then
echo
cat << EOFMARKER
You are running the Second Life Viewer on a x86_64 platform. The
most common problems when launching the Viewer (particularly
'bin/do-not-directly-run-secondlife-bin: not found' and 'error while
@ -134,10 +146,8 @@ distribution's 32-bit compatibility packages.
For example, on Ubuntu and other Debian-based Linuxes you might run:
$ sudo apt-get install ia32-libs ia32-libs-gtk ia32-libs-kde ia32-libs-sdl
EOFMARKER
fi
fi
fi
echo
echo '*******************************************************'

View File

@ -112,6 +112,8 @@
#include "llnotifications.h"
#include "llnotificationsutil.h"
#include "llleap.h"
// Third party library includes
#include <boost/bind.hpp>
#include <boost/foreach.hpp>
@ -125,7 +127,6 @@
#endif
#include "llapr.h"
#include "apr_dso.h"
#include <boost/lexical_cast.hpp>
#include "llviewerkeyboard.h"
@ -162,6 +163,7 @@
#include "llcontainerview.h"
#include "lltooltip.h"
#include "llsdutil.h"
#include "llsdserialize.h"
#include "llworld.h"
@ -529,6 +531,7 @@ static void settings_to_globals()
LLRender::sGLCoreProfile = gSavedSettings.getBOOL("RenderGLCoreProfile");
LLImageGL::sGlobalUseAnisotropic = gSavedSettings.getBOOL("RenderAnisotropic");
LLImageGL::sCompressTextures = gSavedSettings.getBOOL("RenderCompressTextures");
LLVOVolume::sLODFactor = gSavedSettings.getF32("RenderVolumeLODFactor");
LLVOVolume::sDistanceFactor = 1.f-LLVOVolume::sLODFactor * 0.1f;
LLVolumeImplFlexible::sUpdateFactor = gSavedSettings.getF32("RenderFlexTimeFactor");
@ -546,7 +549,7 @@ static void settings_to_globals()
gAgentPilot.setNumRuns(gSavedSettings.getS32("StatsNumRuns"));
gAgentPilot.setQuitAfterRuns(gSavedSettings.getBOOL("StatsQuitAfterRuns"));
gAgent.setHideGroupTitle(gSavedSettings.getBOOL("RenderHideGroupTitle"));
gDebugWindowProc = gSavedSettings.getBOOL("DebugWindowProc");
gShowObjectUpdates = gSavedSettings.getBOOL("ShowObjectUpdates");
LLWorldMapView::sMapScale = gSavedSettings.getF32("MapScale");
@ -1014,6 +1017,15 @@ bool LLAppViewer::init()
}
}
#if LL_WINDOWS
if (gGLManager.mIsIntel &&
LLFeatureManager::getInstance()->getGPUClass() > 0 &&
gGLManager.mGLVersion <= 3.f)
{
LLNotificationsUtil::add("IntelOldDriver");
}
#endif
// save the graphics card
gDebugInfo["GraphicsCard"] = LLFeatureManager::getInstance()->getGPUString();
@ -1038,11 +1050,38 @@ bool LLAppViewer::init()
gGLActive = FALSE;
// Iterate over --leap command-line options. But this is a bit tricky: if
// there's only one, it won't be an array at all.
LLSD LeapCommand(gSavedSettings.getLLSD("LeapCommand"));
LL_DEBUGS("InitInfo") << "LeapCommand: " << LeapCommand << LL_ENDL;
if (LeapCommand.isDefined() && ! LeapCommand.isArray())
{
// If LeapCommand is actually a scalar value, make an array of it.
// Have to do it in two steps because LeapCommand.append(LeapCommand)
// trashes content! :-P
LLSD item(LeapCommand);
LeapCommand.append(item);
}
BOOST_FOREACH(const std::string& leap, llsd::inArray(LeapCommand))
{
LL_INFOS("InitInfo") << "processing --leap \"" << leap << '"' << LL_ENDL;
// We don't have any better description of this plugin than the
// user-specified command line. Passing "" causes LLLeap to derive a
// description from the command line itself.
// Suppress LLLeap::Error exception: trust LLLeap's own logging. We
// don't consider any one --leap command mission-critical, so if one
// fails, log it, shrug and carry on.
LLLeap::create("", leap, false); // exception=false
}
if (gSavedSettings.getBOOL("QAMode") && gSavedSettings.getS32("QAModeEventHostPort") > 0)
{
loadEventHostModule(gSavedSettings.getS32("QAModeEventHostPort"));
LL_WARNS("InitInfo") << "QAModeEventHostPort DEPRECATED: "
<< "lleventhost no longer supported as a dynamic library"
<< LL_ENDL;
}
LLViewerMedia::initClass();
LL_INFOS("InitInfo") << "Viewer media initialized." << LL_ENDL ;
@ -1520,21 +1559,27 @@ bool LLAppViewer::cleanup()
if (! isError())
{
std::string logdir = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "");
logdir += gDirUtilp->getDirDelimiter();
gDirUtilp->deleteFilesInDir(logdir, "*-*-*-*-*.dmp");
}
// *TODO - generalize this and move DSO wrangling to a helper class -brad
std::set<struct apr_dso_handle_t *>::const_iterator i;
for(i = mPlugins.begin(); i != mPlugins.end(); ++i)
{
int (*ll_plugin_stop_func)(void) = NULL;
apr_status_t rv = apr_dso_sym((apr_dso_handle_sym_t*)&ll_plugin_stop_func, *i, "ll_plugin_stop");
ll_plugin_stop_func();
rv = apr_dso_unload(*i);
}
mPlugins.clear();
// Kill off LLLeap objects. We can find them all because LLLeap is derived
// from LLInstanceTracker. But collect instances first: LLInstanceTracker
// specifically forbids adding/deleting instances while iterating.
std::vector<LLLeap*> leaps;
leaps.reserve(LLLeap::instanceCount());
for (LLLeap::instance_iter li(LLLeap::beginInstances()), lend(LLLeap::endInstances());
li != lend; ++li)
{
leaps.push_back(&*li);
}
// Okay, now trash them all. We don't have to NULL or erase the entry
// in 'leaps' because the whole vector is going away momentarily.
BOOST_FOREACH(LLLeap* leap, leaps)
{
delete leap;
}
} // destroy 'leaps'
//flag all elements as needing to be destroyed immediately
// to ensure shutdown order
@ -1769,8 +1814,7 @@ bool LLAppViewer::cleanup()
if (mPurgeOnExit)
{
llinfos << "Purging all cache files on exit" << llendflush;
std::string mask = gDirUtilp->getDirDelimiter() + "*.*";
gDirUtilp->deleteFilesInDir(gDirUtilp->getExpandedFilename(LL_PATH_CACHE,""),mask);
gDirUtilp->deleteFilesInDir(gDirUtilp->getExpandedFilename(LL_PATH_CACHE,""), "*.*");
}
removeMarkerFile(); // Any crashes from here on we'll just have to ignore
@ -3004,8 +3048,7 @@ void LLAppViewer::cleanupSavedSettings()
void LLAppViewer::removeCacheFiles(const std::string& file_mask)
{
std::string mask = gDirUtilp->getDirDelimiter() + file_mask;
gDirUtilp->deleteFilesInDir(gDirUtilp->getExpandedFilename(LL_PATH_CACHE, ""), mask);
gDirUtilp->deleteFilesInDir(gDirUtilp->getExpandedFilename(LL_PATH_CACHE, ""), file_mask);
}
void LLAppViewer::writeSystemInfo()
@ -3864,8 +3907,7 @@ void LLAppViewer::purgeCache()
LL_INFOS("AppCache") << "Purging Cache and Texture Cache..." << LL_ENDL;
LLAppViewer::getTextureCache()->purgeCache(LL_PATH_CACHE);
LLVOCache::getInstance()->removeCache(LL_PATH_CACHE);
std::string mask = "*.*";
gDirUtilp->deleteFilesInDir(gDirUtilp->getExpandedFilename(LL_PATH_CACHE, ""), mask);
gDirUtilp->deleteFilesInDir(gDirUtilp->getExpandedFilename(LL_PATH_CACHE, ""), "*.*");
}
std::string LLAppViewer::getSecondLifeTitle() const
@ -4973,87 +5015,6 @@ void LLAppViewer::handleLoginComplete()
writeDebugInfo();
}
// *TODO - generalize this and move DSO wrangling to a helper class -brad
void LLAppViewer::loadEventHostModule(S32 listen_port)
{
std::string dso_name =
#if LL_WINDOWS
"lleventhost.dll";
#elif LL_DARWIN
"liblleventhost.dylib";
#else
"liblleventhost.so";
#endif
std::string dso_path = gDirUtilp->findFile(dso_name,
gDirUtilp->getAppRODataDir(),
gDirUtilp->getExecutableDir());
if(dso_path == "")
{
llerrs << "QAModeEventHost requested but module \"" << dso_name << "\" not found!" << llendl;
return;
}
LL_INFOS("eventhost") << "Found lleventhost at '" << dso_path << "'" << LL_ENDL;
#if ! defined(LL_WINDOWS)
{
std::string outfile("/tmp/lleventhost.file.out");
std::string command("file '" + dso_path + "' > '" + outfile + "' 2>&1");
int rc = system(command.c_str());
if (rc != 0)
{
LL_WARNS("eventhost") << command << " ==> " << rc << ':' << LL_ENDL;
}
else
{
LL_INFOS("eventhost") << command << ':' << LL_ENDL;
}
{
std::ifstream reader(outfile.c_str());
std::string line;
while (std::getline(reader, line))
{
size_t len = line.length();
if (len && line[len-1] == '\n')
line.erase(len-1);
LL_INFOS("eventhost") << line << LL_ENDL;
}
}
remove(outfile.c_str());
}
#endif // LL_WINDOWS
apr_dso_handle_t * eventhost_dso_handle = NULL;
apr_pool_t * eventhost_dso_memory_pool = NULL;
//attempt to load the shared library
apr_pool_create(&eventhost_dso_memory_pool, NULL);
apr_status_t rv = apr_dso_load(&eventhost_dso_handle,
dso_path.c_str(),
eventhost_dso_memory_pool);
llassert_always(! ll_apr_warn_status(rv, eventhost_dso_handle));
llassert_always(eventhost_dso_handle != NULL);
int (*ll_plugin_start_func)(LLSD const &) = NULL;
rv = apr_dso_sym((apr_dso_handle_sym_t*)&ll_plugin_start_func, eventhost_dso_handle, "ll_plugin_start");
llassert_always(! ll_apr_warn_status(rv, eventhost_dso_handle));
llassert_always(ll_plugin_start_func != NULL);
LLSD args;
args["listen_port"] = listen_port;
int status = ll_plugin_start_func(args);
if(status != 0)
{
llerrs << "problem loading eventhost plugin, status: " << status << llendl;
}
mPlugins.insert(eventhost_dso_handle);
}
void LLAppViewer::launchUpdater()
{
LLSD query_map = LLSD::emptyMap();

View File

@ -41,8 +41,6 @@ class LLTextureFetch;
class LLWatchdogTimeout;
class LLUpdaterService;
struct apr_dso_handle_t;
class LLAppViewer : public LLApp
{
public:
@ -220,8 +218,6 @@ private:
void sendLogoutRequest();
void disconnectViewer();
void loadEventHostModule(S32 listen_port);
// *FIX: the app viewer class should be some sort of singleton, no?
// Perhaps its child class is the singleton and this should be an abstract base.
@ -270,8 +266,6 @@ private:
LLAllocator mAlloc;
std::set<struct apr_dso_handle_t*> mPlugins;
LLFrameTimer mMemCheckTimer;
boost::scoped_ptr<LLUpdaterService> mUpdater;

Some files were not shown because too many files have changed in this diff Show More