+2013-11-26 Carl Hetherington <cth@carlh.net>
+
+ * A whole load of fixes for lots of bugs when handling filenames
+ using non-Latin characters on Windows.
+
2013-11-22 Carl Hetherington <cth@carlh.net>
* Version 1.34 released.
def dependencies(target):
return (('ffmpeg-cdist', '0b7ef017aca8b572914518c759db1e234d8fc505'),
- ('libdcp', 'f38137b21051ce770bbb4d829ae9d6229e97508f'))
+ ('libdcp', 'd0b7892cbab3618fca85a4c95d70d8a1c26a5c4d'))
def build(target):
cmd = './waf configure --prefix=%s' % target.work_dir_cscript()
id="svg2"
version="1.1"
inkscape:version="0.48.4 r9939"
- sodipodi:docname="New document 1">
+ sodipodi:docname="file-structure.svg">
<defs
id="defs4">
<marker
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
- inkscape:window-width="1280"
- inkscape:window-height="961"
- inkscape:window-x="1912"
- inkscape:window-y="-8"
+ inkscape:window-width="1366"
+ inkscape:window-height="714"
+ inkscape:window-x="0"
+ inkscape:window-y="283"
inkscape:window-maximized="1" />
<metadata
id="metadata7">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title></dc:title>
+ <dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
sodipodi:nodetypes="cc" />
<path
style="color:#000000;fill:none;stroke:#8f5902;stroke-width:1.25;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
- d="m 181.68106,157.3527 12.5586,0 0,192.56519 -13.39584,0"
+ d="m 201.68106,157.3527 12.5586,0 0,192.56519 -13.39584,0"
id="path9382"
inkscape:connector-curvature="0" />
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Inconsolata;-inkscape-font-specification:Inconsolata"
- x="205.96103"
+ x="225.96103"
y="241.07671"
id="text9384"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan9386"
- x="205.96103"
+ x="225.96103"
y="241.07671">DCP-o-matic's</tspan><tspan
sodipodi:role="line"
- x="205.96103"
- y="256.07669"
+ x="225.96103"
+ y="256.07672"
id="tspan9388">working</tspan><tspan
sodipodi:role="line"
- x="205.96103"
- y="271.07669"
+ x="225.96103"
+ y="271.07672"
id="tspan9390">files</tspan></text>
<path
style="color:#000000;fill:none;stroke:#8f5902;stroke-width:1.25;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
- d="m 444.5744,360.802 12.5586,0 0,272.94023 -13.39584,0"
+ d="m 486.5744,360.802 12.5586,0 0,272.94023 -13.39584,0"
id="path9382-3"
inkscape:connector-curvature="0" />
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Inconsolata;-inkscape-font-specification:Inconsolata"
- x="468.01709"
+ x="510.01709"
y="500.62109"
id="text9410"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan9412"
- x="468.01709"
+ x="510.01709"
y="500.62109">DCP</tspan></text>
</g>
</svg>
File "%static_deps%/bin/libboost_system-mt.dll"
File "%static_deps%/bin/libboost_thread_win32-mt.dll"
File "%static_deps%/bin/libboost_date_time-mt.dll"
+File "%static_deps%/bin/libboost_locale-mt.dll"
File "%static_deps%/bin/libeay32.dll"
File "%static_deps%/bin/libgcc_s_sjlj-1.dll"
File "%static_deps%/bin/libgio-2.0-0.dll"
File "%static_deps%/bin/libgobject-2.0-0.dll"
File "%static_deps%/bin/libiconv-2.dll"
File "%static_deps%/bin/libjpeg-8.dll"
-File "%static_deps%/bin/libMagick++-6.Q16-2.dll"
+File "%static_deps%/bin/libMagick++-6.Q16-3.dll"
File "%static_deps%/bin/libMagickCore-6.Q16-1.dll"
File "%static_deps%/bin/libMagickWand-6.Q16-1.dll"
File "%static_deps%/bin/libpng15-15.dll"
File "%static_deps%/bin/libboost_system-mt.dll"
File "%static_deps%/bin/libboost_thread_win32-mt.dll"
File "%static_deps%/bin/libboost_date_time-mt.dll"
+File "%static_deps%/bin/libboost_locale-mt.dll"
File "%static_deps%/bin/libeay32.dll"
File "%static_deps%/bin/libgcc_s_sjlj-1.dll"
File "%static_deps%/bin/libgio-2.0-0.dll"
File "%static_deps%/bin/libgobject-2.0-0.dll"
File "%static_deps%/bin/libiconv-2.dll"
File "%static_deps%/bin/libjpeg-8.dll"
-File "%static_deps%/bin/libMagick++-6.Q16-2.dll"
+File "%static_deps%/bin/libMagick++-6.Q16-3.dll"
File "%static_deps%/bin/libMagickCore-6.Q16-1.dll"
File "%static_deps%/bin/libMagickWand-6.Q16-1.dll"
File "%static_deps%/bin/libpng15-15.dll"
#include <stdint.h>
#include <cmath>
#include <cassert>
-#include <fstream>
+#include <cstdio>
#include <boost/filesystem.hpp>
#include "audio_analysis.h"
+#include "cross.h"
using std::ostream;
using std::istream;
using std::string;
-using std::ofstream;
-using std::ifstream;
using std::vector;
using std::cout;
using std::max;
}
}
-AudioPoint::AudioPoint (istream& s)
+AudioPoint::AudioPoint (FILE* f)
{
for (int i = 0; i < COUNT; ++i) {
- s >> _data[i];
+ fscanf (f, "%f", &_data[i]);
}
}
}
void
-AudioPoint::write (ostream& s) const
+AudioPoint::write (FILE* f) const
{
for (int i = 0; i < COUNT; ++i) {
- s << _data[i] << "\n";
+ fprintf (f, "%f\n", _data[i]);
}
}
AudioAnalysis::AudioAnalysis (boost::filesystem::path filename)
{
- ifstream f (filename.string().c_str ());
+ FILE* f = fopen_boost (filename, "r");
int channels;
- f >> channels;
+ fscanf (f, "%d", &channels);
_data.resize (channels);
for (int i = 0; i < channels; ++i) {
int points;
- f >> points;
+ fscanf (f, "%d", &points);
for (int j = 0; j < points; ++j) {
_data[i].push_back (AudioPoint (f));
}
void
AudioAnalysis::write (boost::filesystem::path filename)
{
- string tmp = filename.string() + ".tmp";
+ boost::filesystem::path tmp = filename;
+ tmp.replace_extension (".tmp");
- ofstream f (tmp.c_str ());
- f << _data.size() << "\n";
+ FILE* f = fopen_boost (tmp, "w");
+
+ fprintf (f, "%ld\n", _data.size ());
for (vector<vector<AudioPoint> >::iterator i = _data.begin(); i != _data.end(); ++i) {
- f << i->size () << "\n";
+ fprintf (f, "%ld\n", i->size ());
for (vector<AudioPoint>::iterator j = i->begin(); j != i->end(); ++j) {
j->write (f);
}
}
- f.close ();
+ fclose (f);
boost::filesystem::rename (tmp, filename);
}
#ifndef DCPOMATIC_AUDIO_ANALYSIS_H
#define DCPOMATIC_AUDIO_ANALYSIS_H
-#include <iostream>
#include <vector>
#include <list>
#include <boost/filesystem.hpp>
};
AudioPoint ();
- AudioPoint (std::istream &);
+ AudioPoint (FILE *);
AudioPoint (AudioPoint const &);
AudioPoint& operator= (AudioPoint const &);
- void write (std::ostream &) const;
+ void write (FILE *) const;
float& operator[] (int t) {
return _data[t];
using std::vector;
using std::ifstream;
using std::string;
-using std::ofstream;
using std::list;
using std::max;
using std::exception;
void
Config::read_old_metadata ()
{
+ /* XXX: this won't work with non-Latin filenames */
ifstream f (file(true).string().c_str ());
string line;
string info;
#ifdef DCPOMATIC_LINUX
+ /* This use of ifstream is ok; the filename can never
+ be non-Latin
+ */
ifstream f ("/proc/cpuinfo");
while (f.good ()) {
string l;
#endif
}
+
+/* Apparently there is no way to create an ofstream using a UTF-8
+ filename under Windows. We are hence reduced to using fopen
+ with this wrapper.
+*/
+FILE *
+fopen_boost (boost::filesystem::path p, string t)
+{
+#ifdef DCPOMATIC_WINDOWS
+ wstring w (t.begin(), t.end());
+ /* c_str() here should give a UTF-16 string */
+ return _wfopen (p.c_str(), w.c_str ());
+#else
+ return fopen (p.c_str(), t.c_str ());
+#endif
+}
#ifdef DCPOMATIC_OSX
extern boost::filesystem::path app_contents ();
#endif
+extern FILE * fopen_boost (boost::filesystem::path, std::string);
#include "scaler.h"
#include "image.h"
#include "log.h"
+#include "cross.h"
#include "i18n.h"
using std::string;
using std::stringstream;
-using std::ofstream;
using std::cout;
using boost::shared_ptr;
using boost::lexical_cast;
EncodedData::write_info (shared_ptr<const Film> film, int frame, Eyes eyes, libdcp::FrameInfo fin) const
{
boost::filesystem::path const info = film->info_path (frame, eyes);
- ofstream h (info.string().c_str());
+ FILE* h = fopen_boost (info, "w");
fin.write (h);
+ fclose (h);
}
/** Send this data to a socket.
using std::pair;
using std::map;
using std::vector;
-using std::ifstream;
-using std::ofstream;
using std::setfill;
using std::min;
using std::make_pair;
* @brief A very simple logging class.
*/
-#include <fstream>
#include <time.h>
+#include <cstdio>
#include "log.h"
+#include "cross.h"
#include "i18n.h"
void
FileLog::do_log (string m)
{
- ofstream f (_file.string().c_str(), fstream::app);
- f << m << N_("\n");
+ FILE* f = fopen_boost (_file, "a");
+ fprintf (f, "%s\n", m.c_str ());
+ fclose (f);
}
#include "i18n.h"
using std::cout;
+using std::string;
using boost::shared_ptr;
StillImageExaminer::StillImageExaminer (shared_ptr<const Film> f, shared_ptr<const StillImageContent> c)
, _film (f)
{
using namespace MagickCore;
- Magick::Image* image = new Magick::Image (_still_image_content->path().string());
+ Magick::Image* image = new Magick::Image (_still_image_content->path().string ());
_video_size = libdcp::Size (image->columns(), image->rows());
delete image;
}
#include <boost/lexical_cast.hpp>
#include <boost/thread.hpp>
#include <boost/filesystem.hpp>
+#include <boost/locale.hpp>
#include <glib.h>
#include <openjpeg.h>
#include <openssl/md5.h>
using std::vector;
using std::hex;
using std::setw;
-using std::ifstream;
using std::ios;
using std::min;
using std::max;
using std::istream;
using std::numeric_limits;
using std::pair;
-using std::ofstream;
using std::cout;
using boost::shared_ptr;
using boost::thread;
LONG WINAPI exception_handler(struct _EXCEPTION_POINTERS *)
{
dbg::stack s;
- ofstream f (backtrace_file.string().c_str());
- std::copy(s.begin(), s.end(), std::ostream_iterator<dbg::stack_frame>(f, "\n"));
+ FILE* f = fopen_boost (backtrace_file, "w");
+ for (dbg::stack::const_iterator i = s.begin(); i != s.end(); ++i) {
+ fprintf (f, "%p %s %d %s", i->instruction, i->function.c_str(), i->line, i->module.c_str());
+ }
+ fclose (f);
return EXCEPTION_CONTINUE_SEARCH;
}
#endif
backtrace_file /= g_get_user_config_dir ();
backtrace_file /= "backtrace.txt";
SetUnhandledExceptionFilter(exception_handler);
+
+ /* Dark voodoo which, I think, gets boost::filesystem::path to
+ correctly convert UTF-8 strings to paths, and also paths
+ back to UTF-8 strings (on path::string()).
+
+ After this, constructing boost::filesystem::paths from strings
+ converts from UTF-8 to UTF-16 inside the path. Then
+ path::string().c_str() gives UTF-8 and
+ path::c_str() gives UTF-16.
+
+ This is all Windows-only. AFAICT Linux/OS X use UTF-8 everywhere,
+ so things are much simpler.
+ */
+ std::locale::global (boost::locale::generator().generate (""));
+ boost::filesystem::path::imbue (std::locale ());
#endif
avfilter_register_all ();
string
md5_digest (boost::filesystem::path file)
{
- ifstream f (file.string().c_str(), std::ios::binary);
- if (!f.good ()) {
+ FILE* f = fopen_boost (file, "rb");
+ if (!f) {
throw OpenFileError (file.string());
}
-
- f.seekg (0, std::ios::end);
- int bytes = f.tellg ();
- f.seekg (0, std::ios::beg);
- int const buffer_size = 64 * 1024;
+ boost::uintmax_t bytes = boost::filesystem::file_size (file);
+
+ boost::uintmax_t const buffer_size = 64 * 1024;
char buffer[buffer_size];
MD5_CTX md5_context;
MD5_Init (&md5_context);
while (bytes > 0) {
int const t = min (bytes, buffer_size);
- f.read (buffer, t);
+ fread (buffer, 1, t, f);
MD5_Update (&md5_context, buffer, t);
bytes -= t;
}
unsigned char digest[MD5_DIGEST_LENGTH];
MD5_Final (digest, &md5_context);
+ fclose (f);
stringstream s;
for (int i = 0; i < MD5_DIGEST_LENGTH; ++i) {
string
md5_digest_directory (boost::filesystem::path directory, shared_ptr<Job> job)
{
- int const buffer_size = 64 * 1024;
+ boost::uintmax_t const buffer_size = 64 * 1024;
char buffer[buffer_size];
MD5_CTX md5_context;
int j = 0;
for (boost::filesystem::directory_iterator i(directory); i != boost::filesystem::directory_iterator(); ++i) {
- ifstream f (i->path().string().c_str(), std::ios::binary);
- if (!f.good ()) {
+ FILE* f = fopen_boost (i->path(), "rb");
+ if (!f) {
throw OpenFileError (i->path().string());
}
-
- f.seekg (0, std::ios::end);
- int bytes = f.tellg ();
- f.seekg (0, std::ios::beg);
+
+ boost::uintmax_t bytes = boost::filesystem::file_size (i->path ());
while (bytes > 0) {
int const t = min (bytes, buffer_size);
- f.read (buffer, t);
+ fread (buffer, 1, t, f);
MD5_Update (&md5_context, buffer, t);
bytes -= t;
}
job->set_progress (float (j) / files);
++j;
}
+
+ fclose (f);
}
unsigned char digest[MD5_DIGEST_LENGTH];
#include "audio_mapping.h"
#include "config.h"
#include "job.h"
+#include "cross.h"
#include "i18n.h"
using std::make_pair;
using std::pair;
using std::string;
-using std::ifstream;
using std::list;
using std::cout;
using boost::shared_ptr;
{
boost::mutex::scoped_lock lock (_mutex);
- ifstream ifi (_film->info_path (frame, eyes).string().c_str());
+ FILE* ifi = fopen_boost (_film->info_path (frame, eyes), "r");
libdcp::FrameInfo info (ifi);
+ fclose (ifi);
QueueItem qi;
qi.type = QueueItem::FAKE;
Writer::check_existing_picture_mxf_frame (FILE* mxf, int f, Eyes eyes)
{
/* Read the frame info as written */
- ifstream ifi (_film->info_path (f, eyes).string().c_str());
+ FILE* ifi = fopen_boost (_film->info_path (f, eyes), "r");
libdcp::FrameInfo info (ifi);
+ fclose (ifi);
if (info.size == 0) {
_film->log()->log (String::compose ("Existing frame %1 has no info file", f));
return false;
obj.export_includes = ['..']
obj.uselib = """
AVCODEC AVUTIL AVFORMAT AVFILTER SWSCALE SWRESAMPLE
- BOOST_FILESYSTEM BOOST_THREAD BOOST_DATETIME BOOST_SIGNALS2
+ BOOST_FILESYSTEM BOOST_THREAD BOOST_DATETIME BOOST_SIGNALS2 BOOST_LOCALE
SNDFILE OPENJPEG POSTPROC TIFF MAGICK SSH DCP CXML GLIB LZMA XML++
CURL ZIP QUICKMAIL
"""
using std::make_pair;
using std::list;
using std::exception;
-using std::ofstream;
using boost::shared_ptr;
using boost::dynamic_pointer_cast;
: wxFrame (NULL, -1, title)
, _servers_list_dialog (0)
{
+#ifdef DCPOMATIC_WINDOWS_CONSOLE
+ AllocConsole();
+
+ HANDLE handle_out = GetStdHandle(STD_OUTPUT_HANDLE);
+ int hCrt = _open_osfhandle((intptr_t) handle_out, _O_TEXT);
+ FILE* hf_out = _fdopen(hCrt, "w");
+ setvbuf(hf_out, NULL, _IONBF, 1);
+ *stdout = *hf_out;
+
+ HANDLE handle_in = GetStdHandle(STD_INPUT_HANDLE);
+ hCrt = _open_osfhandle((intptr_t) handle_in, _O_TEXT);
+ FILE* hf_in = _fdopen(hCrt, "r");
+ setvbuf(hf_in, NULL, _IONBF, 128);
+ *stdin = *hf_in;
+#endif
+
wxMenuBar* bar = new wxMenuBar;
setup_menu (bar);
SetMenuBar (bar);
std_to_wx (
String::compose (wx_to_std (_("The directory %1 already exists and is not empty. "
"Are you sure you want to use it?")),
- d->get_path().c_str())
+ d->get_path().string().c_str())
)
)) {
return;
void
FilmEditor::content_add_file_clicked ()
{
- wxFileDialog* d = new wxFileDialog (this, _("Choose a file or files"), wxT (""), wxT (""), wxT ("*.*"), wxFD_MULTIPLE);
+ /* The wxFD_CHANGE_DIR here prevents a `could not set working directory' error 123 on Windows when using
+ non-Latin filenames or paths.
+ */
+ wxFileDialog* d = new wxFileDialog (this, _("Choose a file or files"), wxT (""), wxT (""), wxT ("*.*"), wxFD_MULTIPLE | wxFD_CHANGE_DIR);
int const r = d->ShowModal ();
- d->Destroy ();
if (r != wxID_OK) {
+ d->Destroy ();
return;
}
/* XXX: check for lots of files here and do something */
for (unsigned int i = 0; i < paths.GetCount(); ++i) {
- _film->examine_and_add_content (content_factory (_film, wx_to_std (paths[i])));
+ _film->examine_and_add_content (content_factory (_film, wx_to_std (d->GetPath ())));
}
+
+ d->Destroy ();
}
void
_directory = wx_to_std (_folder->GetPath ());
}
-string
+boost::filesystem::path
NewFilmDialog::get_path () const
{
filesystem::path p;
p /= wx_to_std (_folder->GetPath ());
p /= wx_to_std (_name->GetValue ());
- return p.string ();
+ return p;
}
NewFilmDialog (wxWindow *);
~NewFilmDialog ();
- std::string get_path () const;
+ boost::filesystem::path get_path () const;
private:
wxTextCtrl* _name;
string
wx_to_std (wxString s)
{
- return string (s.mb_str ());
+ return string (s.ToUTF8 ());
}
/** @param s STL string.
--- /dev/null
+/*
+ Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <boost/test/unit_test.hpp>
+#include "lib/audio_analysis.h"
+
+static float
+random_float ()
+{
+ return (float (rand ()) / RAND_MAX) * 2 - 1;
+}
+
+/* Check serialisation of audio analyses */
+BOOST_AUTO_TEST_CASE (audio_analysis_test)
+{
+ int const channels = 3;
+ int const points = 4096;
+
+ srand (1);
+
+ AudioAnalysis a (3);
+ for (int i = 0; i < channels; ++i) {
+ for (int j = 0; j < points; ++j) {
+ AudioPoint p;
+ p[AudioPoint::PEAK] = random_float ();
+ p[AudioPoint::RMS] = random_float ();
+ a.add_point (i, p);
+ }
+ }
+
+ a.write ("build/test/audio_analysis_test");
+
+ srand (1);
+
+ AudioAnalysis b ("build/test/audio_analysis_test");
+ for (int i = 0; i < channels; ++i) {
+ BOOST_CHECK (b.points(i) == points);
+ for (int j = 0; j < points; ++j) {
+ AudioPoint p = b.get_point (i, j);
+ BOOST_CHECK_CLOSE (p[AudioPoint::PEAK], random_float (), 1);
+ BOOST_CHECK_CLOSE (p[AudioPoint::RMS], random_float (), 1);
+ }
+ }
+}
obj.use = 'libdcpomatic'
obj.source = """
test.cc
+ audio_analysis_test.cc
scaling_test.cc
film_metadata_test.cc
frame_rate_test.cc
lib=['boost_date_time%s' % boost_lib_suffix, 'boost_system%s' % boost_lib_suffix],
uselib_store='BOOST_DATETIME')
+ conf.check_cxx(fragment="""
+ #include <boost/locale.hpp>\n
+ int main() { std::locale::global (boost::locale::generator().generate ("")); }\n
+ """, msg='Checking for boost locale library',
+ libpath='/usr/local/lib',
+ lib=['boost_locale%s' % boost_lib_suffix, 'boost_system%s' % boost_lib_suffix],
+ uselib_store='BOOST_LOCALE')
+
conf.check_cxx(fragment="""
#include <boost/signals2.hpp>\n
int main() { boost::signals2::signal<void (int)> x; }\n