mirror of https://github.com/vitalif/openscad
toLocal8Bit->toUtf8, 'space in filename' test, portable stat(), clang+glib fix.
parent
b3f57fb4a5
commit
5f9053057a
|
@ -1,12 +1,14 @@
|
|||
This README covers a few caveats regarding OpenSCAD for Windows(TM) for
|
||||
programmers and users.
|
||||
|
||||
Summary:
|
||||
Summary: As of writing, early 2014:
|
||||
|
||||
-Windows filenames & cmdline args are UTF16, in conflict with assumptions
|
||||
about Mac(TM) and Unix(TM) which generally assume everything is UTF8.
|
||||
-All file i/o must go through PlatformUtils:: wrappers
|
||||
-Windows filenames & cmdline args are ~UTF16, Linux/Mac are UTF8.
|
||||
-There is no open source ifstream/ofstream fopen() that works with both.
|
||||
-Therefore all file i/o must go through PlatformUtils:: wrappers
|
||||
-std::ifstream & std::ofstream are limited in capability by our wrappers
|
||||
-Windows stdout/stdin differs in behavior between GUI programs and cmdline
|
||||
-Windows console has issues outputting unicode of any encoding.
|
||||
-openscad.com is the command line wrapper for openscad.exe, the gui
|
||||
|
||||
00. Encoding of Unicode
|
||||
|
@ -98,4 +100,10 @@ before any processing is done on them. Therefore. All file writing should
|
|||
be in 'binary mode' on Windows and we write sequences of 8-bit bytes,
|
||||
encoded in UTF8.
|
||||
|
||||
5. Boost and portability
|
||||
|
||||
Theoretically, Boost provides portable wrapper to some Windows and
|
||||
Unix/Mac file code, including fstream. In reality, since Boost is built
|
||||
on top of the libstdc++ that one is using, a version of Boost built
|
||||
under the current MingW cross-build will not be able to deal with UTF16
|
||||
filenames.
|
||||
|
|
19
glib-2.0.pri
19
glib-2.0.pri
|
@ -13,9 +13,22 @@ GLIB2_DIR = $$(GLIB2DIR)
|
|||
|
||||
!isEmpty(OPENSCAD_LIBRARIES_DIR) {
|
||||
isEmpty(GLIB2_INCLUDEPATH) {
|
||||
GLIB2_INCLUDEPATH = $$OPENSCAD_LIBRARIES_DIR/include/glib-2.0
|
||||
GLIB2_INCLUDEPATH_2 = $$OPENSCAD_LIBRARIES_DIR/lib/glib-2.0/include
|
||||
GLIB2_LIBPATH = $$OPENSCAD_LIBRARIES_DIR/lib
|
||||
!isEmpty(GLIB2_DIR) {
|
||||
GLIB2_INCLUDEPATH = $$GLIB2_DIR/include/glib-2.0
|
||||
GLIB2_INCLUDEPATH_2 = $$GLIB2_DIR/lib/glib-2.0/include
|
||||
GLIB2_LIBPATH = $$GLIB2_DIR/lib
|
||||
} else {
|
||||
GLIB2_INCLUDEPATH = $$OPENSCAD_LIBRARIES_DIR/include/glib-2.0
|
||||
GLIB2_INCLUDEPATH_2 = $$OPENSCAD_LIBRARIES_DIR/lib/glib-2.0/include
|
||||
GLIB2_LIBPATH = $$OPENSCAD_LIBRARIES_DIR/lib
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
!exists($$GLIB2_INCLUDEPATH/glib.h) {
|
||||
!exists($$GLIB2_INCLUDEPATH_2/glib.h) {
|
||||
GLIB2_INCLUDEPATH =
|
||||
GLIB2_LIBPATH =
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -118,8 +118,8 @@ void PlatformUtils::resetArgvToUtf8( int argc, char ** &argv, std::vector<std::s
|
|||
}
|
||||
}
|
||||
|
||||
/* allow fopen() to work with unicode filenames on windows(TM)
|
||||
by transcoding to UTF16 and then using _wfopen() */
|
||||
/* allow fopen() to work with unicode filenames on windows(TM) by
|
||||
transcoding to UTF16 and then using _wfopen(). See also: ifstream/ofstream */
|
||||
FILE *PlatformUtils::fopen( const char *utf8path, const char *mode )
|
||||
{
|
||||
std::wstring winpath;
|
||||
|
@ -129,3 +129,15 @@ FILE *PlatformUtils::fopen( const char *utf8path, const char *mode )
|
|||
return _wfopen( winpath.c_str() , winmode.c_str() );
|
||||
}
|
||||
|
||||
/* allow stat() to work with unicode filenames on windows(TM) by
|
||||
transcoding to UTF16 and then using _wstat(). Note we also have to
|
||||
use a wrapped version of struct stat in PlatformUtils.h */
|
||||
int PlatformUtils::stat( const char *utf8path, void *buf )
|
||||
{
|
||||
std::wstring winpath;
|
||||
winpath = utf8_to_winapi_wstring( std::string( utf8path ) );
|
||||
return _wstat( winpath.c_str(), (PlatformUtils::struct_stat *)buf );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -4,6 +4,11 @@
|
|||
#include <glib.h>
|
||||
#include <sstream>
|
||||
|
||||
// types/stat/unistd: stat()
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/* Quick and dirty hack to work around floating point rounding differences
|
||||
across platforms for testing purposes. */
|
||||
std::string PlatformUtils::formatDouble( const double &op1 )
|
||||
|
@ -78,6 +83,11 @@ FILE *PlatformUtils::fopen( const char *utf8path, const char *mode )
|
|||
{
|
||||
return std::fopen( utf8path, mode );
|
||||
}
|
||||
|
||||
int PlatformUtils::stat( const char *utf8path, void *buf )
|
||||
{
|
||||
return stat( utf8path, (PlatformUtils::struct_stat *)buf );
|
||||
}
|
||||
#endif
|
||||
|
||||
#include "version_check.h"
|
||||
|
|
|
@ -13,8 +13,8 @@ namespace PlatformUtils {
|
|||
void resetArgvToUtf8( int argc, char ** &argv, std::vector<std::string> &argstorage);
|
||||
FILE *fopen( const char *utf8path, const char *mode );
|
||||
std::string formatDouble( const double &x );
|
||||
}
|
||||
|
||||
int stat(const char *utf8path, void *buf);
|
||||
};
|
||||
|
||||
#if defined (__MINGW32__) || defined (__MINGW64__)
|
||||
#define __PLATFORM_MINGW__
|
||||
|
@ -23,6 +23,11 @@ namespace PlatformUtils {
|
|||
#define __PLATFORM_WIN__
|
||||
#endif
|
||||
|
||||
#if defined (__PLATFORM_WIN__)
|
||||
namespace PlatformUtils { typedef struct _stat struct_stat; }
|
||||
#else
|
||||
namespace PlatformUtils { typedef struct stat struct_stat; }
|
||||
#endif
|
||||
|
||||
// MingW ifstream/ofstream: see ../doc/windows_issues.txt & ../patches/minsgream
|
||||
#ifdef __PLATFORM_MINGW__
|
||||
|
@ -30,12 +35,12 @@ namespace PlatformUtils {
|
|||
namespace PlatformUtils {
|
||||
typedef omingstream ofstream;
|
||||
typedef imingstream ifstream;
|
||||
}
|
||||
};
|
||||
#else
|
||||
namespace PlatformUtils {
|
||||
typedef std::ofstream ofstream;
|
||||
typedef std::ifstream ifstream;
|
||||
}
|
||||
};
|
||||
#endif // Mingw ifstream/ofstream
|
||||
|
||||
|
||||
|
|
|
@ -170,7 +170,7 @@
|
|||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_9">
|
||||
<widget class="QLabel" name="label_csh">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
|
|
|
@ -81,7 +81,6 @@
|
|||
#include <algorithm>
|
||||
#include <boost/version.hpp>
|
||||
#include <boost/foreach.hpp>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#ifdef ENABLE_CGAL
|
||||
|
||||
|
@ -554,7 +553,7 @@ MainWindow::setFileName(const QString &filename)
|
|||
this->fileName = fileinfo.fileName();
|
||||
}
|
||||
|
||||
this->top_ctx.setDocumentPath(fileinfo.dir().absolutePath().toLocal8Bit().constData());
|
||||
this->top_ctx.setDocumentPath(fileinfo.dir().absolutePath().toUtf8().constData());
|
||||
QDir::setCurrent(fileinfo.dir().absolutePath());
|
||||
}
|
||||
|
||||
|
@ -618,13 +617,13 @@ void MainWindow::refreshDocument()
|
|||
QFile file(this->fileName);
|
||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
PRINTB("Failed to open file %s: %s",
|
||||
this->fileName.toLocal8Bit().constData() % file.errorString().toLocal8Bit().constData());
|
||||
this->fileName.toUtf8().constData() % file.errorString().toUtf8().constData());
|
||||
}
|
||||
else {
|
||||
QTextStream reader(&file);
|
||||
reader.setCodec("UTF-8");
|
||||
QString text = reader.readAll();
|
||||
PRINTB("Loaded design '%s'.", this->fileName.toLocal8Bit().constData());
|
||||
PRINTB("Loaded design '%s'.", this->fileName.toUtf8().constData());
|
||||
editor->setPlainText(text);
|
||||
}
|
||||
}
|
||||
|
@ -1022,7 +1021,7 @@ void MainWindow::actionSave()
|
|||
setCurrentOutput();
|
||||
QFile file(this->fileName);
|
||||
if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) {
|
||||
PRINTB("Failed to open file for writing: %s (%s)", this->fileName.toLocal8Bit().constData() % file.errorString().toLocal8Bit().constData());
|
||||
PRINTB("Failed to open file for writing: %s (%s)", this->fileName.toUtf8().constData() % file.errorString().toUtf8().constData());
|
||||
QMessageBox::warning(this, windowTitle(), tr("Failed to open file for writing:\n %1 (%2)")
|
||||
.arg(this->fileName).arg(file.errorString()));
|
||||
}
|
||||
|
@ -1030,7 +1029,7 @@ void MainWindow::actionSave()
|
|||
QTextStream writer(&file);
|
||||
writer.setCodec("UTF-8");
|
||||
writer << this->editor->toPlainText();
|
||||
PRINTB("Saved design '%s'.", this->fileName.toLocal8Bit().constData());
|
||||
PRINTB("Saved design '%s'.", this->fileName.toUtf8().constData());
|
||||
this->editor->setContentModified(false);
|
||||
}
|
||||
clearCurrentOutput();
|
||||
|
@ -1230,9 +1229,9 @@ void MainWindow::updateTemporalVariables()
|
|||
bool MainWindow::fileChangedOnDisk()
|
||||
{
|
||||
if (!this->fileName.isEmpty()) {
|
||||
struct stat st;
|
||||
memset(&st, 0, sizeof(struct stat));
|
||||
bool valid = (stat(this->fileName.toLocal8Bit(), &st) == 0);
|
||||
PlatformUtils::struct_stat st;
|
||||
memset(&st, 0, sizeof(PlatformUtils::struct_stat));
|
||||
bool valid = (PlatformUtils::stat(this->fileName.toUtf8().constData(), &st) == 0);
|
||||
// If file isn't there, just return and use current editor text
|
||||
if (!valid) return false;
|
||||
|
||||
|
@ -1255,7 +1254,7 @@ void MainWindow::compileTopLevelDocument()
|
|||
|
||||
this->last_compiled_doc = editor->toPlainText();
|
||||
std::string fulltext =
|
||||
std::string(this->last_compiled_doc.toLocal8Bit().constData()) +
|
||||
std::string(this->last_compiled_doc.toUtf8().constData()) +
|
||||
"\n" + commandline_commands;
|
||||
|
||||
delete this->root_module;
|
||||
|
@ -1264,7 +1263,7 @@ void MainWindow::compileTopLevelDocument()
|
|||
this->root_module = parse(fulltext.c_str(),
|
||||
this->fileName.isEmpty() ?
|
||||
"" :
|
||||
QFileInfo(this->fileName).absolutePath().toLocal8Bit(),
|
||||
QFileInfo(this->fileName).absolutePath().toUtf8(),
|
||||
false);
|
||||
|
||||
if (!animate_panel->isVisible()) {
|
||||
|
@ -1489,7 +1488,7 @@ void MainWindow::actionDisplayAST()
|
|||
e->setWindowTitle("AST Dump");
|
||||
e->setReadOnly(true);
|
||||
if (root_module) {
|
||||
e->setPlainText(QString::fromLocal8Bit(root_module->dump("", "").c_str()));
|
||||
e->setPlainText(QString::fromUtf8(root_module->dump("", "").c_str()));
|
||||
} else {
|
||||
e->setPlainText("No AST to dump. Please try compiling first...");
|
||||
}
|
||||
|
@ -1507,7 +1506,7 @@ void MainWindow::actionDisplayCSGTree()
|
|||
e->setWindowTitle("CSG Tree Dump");
|
||||
e->setReadOnly(true);
|
||||
if (this->root_node) {
|
||||
e->setPlainText(QString::fromLocal8Bit(this->tree.getString(*this->root_node).c_str()));
|
||||
e->setPlainText(QString::fromUtf8(this->tree.getString(*this->root_node).c_str()));
|
||||
} else {
|
||||
e->setPlainText("No CSG to dump. Please try compiling first...");
|
||||
}
|
||||
|
@ -1525,11 +1524,11 @@ void MainWindow::actionDisplayCSGProducts()
|
|||
e->setWindowTitle("CSG Products Dump");
|
||||
e->setReadOnly(true);
|
||||
e->setPlainText(QString("\nCSG before normalization:\n%1\n\n\nCSG after normalization:\n%2\n\n\nCSG rendering chain:\n%3\n\n\nHighlights CSG rendering chain:\n%4\n\n\nBackground CSG rendering chain:\n%5\n")
|
||||
.arg(root_raw_term ? QString::fromLocal8Bit(root_raw_term->dump().c_str()) : "N/A",
|
||||
root_norm_term ? QString::fromLocal8Bit(root_norm_term->dump().c_str()) : "N/A",
|
||||
this->root_chain ? QString::fromLocal8Bit(this->root_chain->dump().c_str()) : "N/A",
|
||||
highlights_chain ? QString::fromLocal8Bit(highlights_chain->dump().c_str()) : "N/A",
|
||||
background_chain ? QString::fromLocal8Bit(background_chain->dump().c_str()) : "N/A"));
|
||||
.arg(root_raw_term ? QString::fromUtf8(root_raw_term->dump().c_str()) : "N/A",
|
||||
root_norm_term ? QString::fromUtf8(root_norm_term->dump().c_str()) : "N/A",
|
||||
this->root_chain ? QString::fromUtf8(this->root_chain->dump().c_str()) : "N/A",
|
||||
highlights_chain ? QString::fromUtf8(highlights_chain->dump().c_str()) : "N/A",
|
||||
background_chain ? QString::fromUtf8(background_chain->dump().c_str()) : "N/A"));
|
||||
e->show();
|
||||
e->resize(600, 400);
|
||||
clearCurrentOutput();
|
||||
|
@ -1605,7 +1604,7 @@ void MainWindow::actionExportSTLorOFF(bool)
|
|||
|
||||
PlatformUtils::ofstream fstream(stl_filename.toUtf8());
|
||||
if (!fstream.is_open()) {
|
||||
PRINTB("Can't open file \"%s\" for export", stl_filename.toLocal8Bit().constData());
|
||||
PRINTB("Can't open file \"%s\" for export", stl_filename.toUtf8().constData());
|
||||
}
|
||||
else {
|
||||
if (stl_mode) exportFile(this->root_geom.get(), fstream, OPENSCAD_STL);
|
||||
|
@ -1658,7 +1657,7 @@ void MainWindow::actionExportDXF()
|
|||
|
||||
PlatformUtils::ofstream fstream(dxf_filename.toUtf8());
|
||||
if (!fstream.is_open()) {
|
||||
PRINTB("Can't open file \"%s\" for export", dxf_filename.toLocal8Bit().constData());
|
||||
PRINTB("Can't open file \"%s\" for export", dxf_filename.toUtf8().constData());
|
||||
}
|
||||
else {
|
||||
exportFile(this->root_geom.get(), fstream, OPENSCAD_DXF);
|
||||
|
@ -1691,7 +1690,7 @@ void MainWindow::actionExportCSG()
|
|||
|
||||
PlatformUtils::ofstream fstream(csg_filename.toUtf8());
|
||||
if (!fstream.is_open()) {
|
||||
PRINTB("Can't open file \"%s\" for export", csg_filename.toLocal8Bit().constData());
|
||||
PRINTB("Can't open file \"%s\" for export", csg_filename.toUtf8().constData());
|
||||
}
|
||||
else {
|
||||
fstream << this->tree.getString(*this->root_node) << "\n";
|
||||
|
@ -1711,7 +1710,7 @@ void MainWindow::actionExportImage()
|
|||
if (img_filename.isEmpty()) {
|
||||
PRINT("No filename specified. Image export aborted.");
|
||||
} else {
|
||||
qglview->save(img_filename.toLocal8Bit().constData());
|
||||
qglview->save(img_filename.toUtf8().constData());
|
||||
}
|
||||
clearCurrentOutput();
|
||||
return;
|
||||
|
@ -2061,7 +2060,7 @@ void MainWindow::consoleOutput(const std::string &msg, void *userdata)
|
|||
// originates in a worker thread.
|
||||
MainWindow *thisp = static_cast<MainWindow*>(userdata);
|
||||
QMetaObject::invokeMethod(thisp->console, "append", Qt::QueuedConnection,
|
||||
Q_ARG(QString, QString::fromLocal8Bit(msg.c_str())));
|
||||
Q_ARG(QString, QString::fromUtf8(msg.c_str())));
|
||||
}
|
||||
|
||||
void MainWindow::setCurrentOutput()
|
||||
|
|
|
@ -206,7 +206,7 @@ int cmdline(const char *deps_output_file, const std::string &filename, Camera &c
|
|||
{
|
||||
#ifdef OPENSCAD_QTGUI
|
||||
QCoreApplication app(argc, argv);
|
||||
const std::string application_path = QApplication::instance()->applicationDirPath().toLocal8Bit().constData();
|
||||
const std::string application_path = QApplication::instance()->applicationDirPath().toUtf8().constData();
|
||||
#else
|
||||
const std::string application_path = boosty::stringy(boosty::absolute(boost::filesystem::path(argv[0]).parent_path()));
|
||||
(void)argc;
|
||||
|
@ -461,8 +461,8 @@ Q_DECLARE_METATYPE(shared_ptr<const Geometry>);
|
|||
static QString assemblePath(const fs::path& absoluteBaseDir,
|
||||
const string& fileName) {
|
||||
if (fileName.empty()) return "";
|
||||
QString qsDir( boosty::stringy( absoluteBaseDir ).c_str() );
|
||||
QString qsFile( fileName.c_str() );
|
||||
QString qsDir = QString::fromUtf8( boosty::stringy( absoluteBaseDir ).c_str() );
|
||||
QString qsFile = QString::fromUtf8( fileName.c_str() );
|
||||
QFileInfo info( qsDir, qsFile ); // if qsfile is absolute, dir is ignored.
|
||||
return info.absoluteFilePath();
|
||||
}
|
||||
|
@ -526,7 +526,7 @@ int gui(vector<string> &inputFiles, const fs::path &original_path, int argc, cha
|
|||
qexamplesdir = exdir.path();
|
||||
}
|
||||
MainWindow::setExamplesDir(qexamplesdir);
|
||||
parser_init(app_path.toLocal8Bit().constData());
|
||||
parser_init(app_path.toUtf8().constData());
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
installAppleEventHandlers();
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
cube();
|
||||
|
|
@ -1024,6 +1024,9 @@ add_cmdline_test(openscad-nonascii EXE ${OPENSCAD_BINPATH} ARGS -o
|
|||
SUFFIX csg
|
||||
FILES ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/sfære.scad)
|
||||
|
||||
add_cmdline_test(openscad-space-name EXE ${OPENSCAD_BINPATH} ARGS -o
|
||||
SUFFIX csg
|
||||
FILES "${CMAKE_SOURCE_DIR}/../testdata/scad/misc/my file.scad")
|
||||
|
||||
# Image output
|
||||
add_cmdline_test(openscad-imgsize EXE ${OPENSCAD_BINPATH}
|
||||
|
|
Loading…
Reference in New Issue