From 10110ed3075b2b2c50096e842a45ea456c8106e2 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 14 Apr 2020 11:53:28 +0200 Subject: [PATCH 01/16] WIP: Ironing over top surfaces. --- src/libslic3r/ExtrusionEntity.cpp | 1 + src/libslic3r/ExtrusionEntity.hpp | 7 +- src/libslic3r/Fill/Fill.cpp | 164 +++++++++++++++++++++++++- src/libslic3r/Fill/FillBase.hpp | 14 +-- src/libslic3r/GCode.cpp | 42 ++++--- src/libslic3r/GCode.hpp | 2 +- src/libslic3r/GCode/PreviewData.cpp | 1 + src/libslic3r/Layer.hpp | 6 +- src/libslic3r/Print.cpp | 2 + src/libslic3r/Print.hpp | 3 +- src/libslic3r/PrintConfig.cpp | 47 ++++++++ src/libslic3r/PrintConfig.hpp | 28 +++++ src/libslic3r/PrintObject.cpp | 19 +++ src/slic3r/GUI/ConfigManipulation.cpp | 4 + src/slic3r/GUI/Field.cpp | 2 + src/slic3r/GUI/GUI.cpp | 2 + src/slic3r/GUI/OptionsGroup.cpp | 3 + src/slic3r/GUI/Preset.cpp | 5 +- src/slic3r/GUI/Tab.cpp | 7 ++ 19 files changed, 321 insertions(+), 38 deletions(-) diff --git a/src/libslic3r/ExtrusionEntity.cpp b/src/libslic3r/ExtrusionEntity.cpp index c0d08c84b..c482f7edb 100644 --- a/src/libslic3r/ExtrusionEntity.cpp +++ b/src/libslic3r/ExtrusionEntity.cpp @@ -312,6 +312,7 @@ std::string ExtrusionEntity::role_to_string(ExtrusionRole role) case erOverhangPerimeter : return L("Overhang perimeter"); case erInternalInfill : return L("Internal infill"); case erSolidInfill : return L("Solid infill"); + case erIroning : return L("Ironing"); case erTopSolidInfill : return L("Top solid infill"); case erBridgeInfill : return L("Bridge infill"); case erGapFill : return L("Gap fill"); diff --git a/src/libslic3r/ExtrusionEntity.hpp b/src/libslic3r/ExtrusionEntity.hpp index b76991f1c..879f564b6 100644 --- a/src/libslic3r/ExtrusionEntity.hpp +++ b/src/libslic3r/ExtrusionEntity.hpp @@ -22,6 +22,7 @@ enum ExtrusionRole : uint8_t { erInternalInfill, erSolidInfill, erTopSolidInfill, + erIroning, erBridgeInfill, erGapFill, erSkirt, @@ -54,14 +55,16 @@ inline bool is_infill(ExtrusionRole role) return role == erBridgeInfill || role == erInternalInfill || role == erSolidInfill - || role == erTopSolidInfill; + || role == erTopSolidInfill + || role == erIroning; } inline bool is_solid_infill(ExtrusionRole role) { return role == erBridgeInfill || role == erSolidInfill - || role == erTopSolidInfill; + || role == erTopSolidInfill + || role == erIroning; } inline bool is_bridge(ExtrusionRole role) { diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index 498abe89e..f62b3ab25 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -10,6 +10,7 @@ #include "../Surface.hpp" #include "FillBase.hpp" +#include "FillRectilinear2.hpp" namespace Slic3r { @@ -388,8 +389,8 @@ void Layer::make_fills() flow_width = new_flow.width; } // Save into layer. - auto *eec = new ExtrusionEntityCollection(); - m_regions[surface_fill.region_id]->fills.entities.push_back(eec); + ExtrusionEntityCollection* eec = nullptr; + m_regions[surface_fill.region_id]->fills.entities.push_back(eec = new ExtrusionEntityCollection()); // Only concentric fills are not sorted. eec->no_sort = f->no_sort(); extrusion_entities_append_paths( @@ -418,4 +419,163 @@ void Layer::make_fills() #endif } +// Create ironing extrusions over top surfaces. +void Layer::make_ironing() +{ + // LayerRegion::slices contains surfaces marked with SurfaceType. + // Here we want to collect top surfaces extruded with the same extruder. + // A surface will be ironed with the same extruder to not contaminate the print with another material leaking from the nozzle. + + // First classify regions based on the extruder used. + struct IroningParams { + int extruder = -1; + bool just_infill = false; + // Spacing of the ironing lines, also to calculate the extrusion flow from. + double line_spacing; + // Height of the extrusion, to calculate the extrusion flow from. + double height; + double speed; + double angle; + + bool operator<(const IroningParams &rhs) const { + if (this->extruder < rhs.extruder) + return true; + if (this->extruder > rhs.extruder) + return false; + if (int(this->just_infill) < int(rhs.just_infill)) + return true; + if (int(this->just_infill) > int(rhs.just_infill)) + return false; + if (this->line_spacing < rhs.line_spacing) + return true; + if (this->line_spacing > rhs.line_spacing) + return false; + if (this->height < rhs.height) + return true; + if (this->height > rhs.height) + return false; + if (this->speed < rhs.speed) + return true; + if (this->speed > rhs.speed) + return false; + if (this->angle < rhs.angle) + return true; + if (this->angle > rhs.angle) + return false; + return false; + } + + bool operator==(const IroningParams &rhs) const { + return this->extruder == rhs.extruder && this->just_infill == rhs.just_infill && + this->line_spacing == rhs.line_spacing && this->height == rhs.height && this->speed == rhs.speed && + this->angle == rhs.angle; + } + + LayerRegion *layerm = nullptr; + + // IdeaMaker: ironing + // ironing flowrate (5% percent) + // ironing speed (10 mm/sec) + + // Kisslicer: + // iron off, Sweep, Group + // ironing speed: 15 mm/sec + + // Cura: + // Pattern (zig-zag / concentric) + // line spacing (0.1mm) + // flow: from normal layer height. 10% + // speed: 20 mm/sec + }; + + std::vector by_extruder; + bool extruder_dont_care = this->object()->config().wipe_into_objects; + double default_layer_height = this->object()->config().layer_height; + + for (LayerRegion *layerm : m_regions) + if (! layerm->slices.empty()) { + IroningParams ironing_params; + const PrintRegionConfig &config = layerm->region()->config(); + if (config.ironing_type == IroningType::AllSolid || + (config.top_solid_layers > 0 && + (config.ironing_type == IroningType::TopSurfaces || + (config.ironing_type == IroningType::TopmostOnly && layerm->layer()->upper_layer == nullptr)))) { + if (config.perimeter_extruder == config.solid_infill_extruder || config.perimeters == 0) { + // Iron the whole face. + ironing_params.extruder = config.solid_infill_extruder; + } else { + // Iron just the infill. + ironing_params.extruder = config.solid_infill_extruder; + } + } + if (ironing_params.extruder != -1) { + ironing_params.just_infill = false; + ironing_params.line_spacing = config.ironing_spacing; + ironing_params.height = default_layer_height * 0.01 * config.ironing_flowrate; + ironing_params.speed = config.ironing_speed; + ironing_params.angle = config.fill_angle * M_PI / 180.; + ironing_params.layerm = layerm; + by_extruder.emplace_back(ironing_params); + } + } + std::sort(by_extruder.begin(), by_extruder.end()); + + FillRectilinear2 fill; + FillParams fill_params; + fill.set_bounding_box(this->object()->bounding_box()); + fill.layer_id = this->id(); + fill.z = this->print_z; + fill.overlap = 0; + fill_params.density = 1.; + fill_params.dont_connect = true; + + for (size_t i = 0; i < by_extruder.size(); ++ i) { + // Find span of regions equivalent to the ironing operation. + IroningParams &ironing_params = by_extruder[i]; + size_t j = i; + for (++ j; j < by_extruder.size() && ironing_params == by_extruder[j]; ++ j) ; + + // Create the ironing extrusions for regions object()->print()->config().nozzle_diameter.values[ironing_params.extruder - 1]; + if (ironing_params.just_infill) { + // Just infill. + } else { + // Infill and perimeter. + // Merge top surfaces with the same ironing parameters. + Polygons polys; + for (size_t k = i; k < j; ++ k) + for (const Surface &surface : by_extruder[k].layerm->slices.surfaces) + if (surface.surface_type == stTop) + polygons_append(polys, surface.expolygon); + // Trim the top surfaces with half the nozzle diameter. + ironing_areas = intersection_ex(polys, offset(this->lslices, - float(scale_(0.5 * nozzle_dmr)))); + } + + // Create the filler object. + fill.spacing = ironing_params.line_spacing; + fill.angle = float(ironing_params.angle + 0.25 * M_PI); + fill.link_max_length = (coord_t)scale_(3. * fill.spacing); + double height = ironing_params.height * fill.spacing / nozzle_dmr; + Flow flow = Flow::new_from_spacing(float(nozzle_dmr), 0., float(height), false); + double flow_mm3_per_mm = flow.mm3_per_mm(); + Surface surface_fill(stTop, ExPolygon()); + for (ExPolygon &expoly : ironing_areas) { + surface_fill.expolygon = std::move(expoly); + Polylines polylines = fill.fill_surface(&surface_fill, fill_params); + if (! polylines.empty()) { + // Save into layer. + ExtrusionEntityCollection *eec = nullptr; + ironing_params.layerm->fills.entities.push_back(eec = new ExtrusionEntityCollection()); + //FIXME we may not want to sort a monotonous infill. + eec->no_sort = false; + extrusion_entities_append_paths( + eec->entities, std::move(polylines), + erIroning, + flow_mm3_per_mm, float(flow.width), float(height)); + } + } + } +} + } // namespace Slic3r diff --git a/src/libslic3r/Fill/FillBase.hpp b/src/libslic3r/Fill/FillBase.hpp index 517ce8383..5a9e92739 100644 --- a/src/libslic3r/Fill/FillBase.hpp +++ b/src/libslic3r/Fill/FillBase.hpp @@ -20,27 +20,21 @@ class Surface; struct FillParams { - FillParams() { - memset(this, 0, sizeof(FillParams)); - // Adjustment does not work. - dont_adjust = true; - } - bool full_infill() const { return density > 0.9999f; } // Fill density, fraction in <0, 1> - float density; + float density { 0.f }; // Don't connect the fill lines around the inner perimeter. - bool dont_connect; + bool dont_connect { false }; // Don't adjust spacing to fill the space evenly. - bool dont_adjust; + bool dont_adjust { true }; // For Honeycomb. // we were requested to complete each loop; // in this case we don't try to make more continuous paths - bool complete; + bool complete { false }; }; static_assert(IsTriviallyCopyable::value, "FillParams class is not POD (and it should be - see constructor)."); diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 3a90349b5..01a804ee7 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -2246,12 +2246,14 @@ void GCode::process_layer( const auto& by_region_specific = is_anything_overridden ? island.by_region_per_copy(by_region_per_copy_cache, static_cast(instance_to_print.instance_id), extruder_id, print_wipe_extrusions != 0) : island.by_region; //FIXME the following code prints regions in the order they are defined, the path is not optimized in any way. if (print.config().infill_first) { - gcode += this->extrude_infill(print, by_region_specific); + gcode += this->extrude_infill(print, by_region_specific, false); gcode += this->extrude_perimeters(print, by_region_specific, lower_layer_edge_grids[instance_to_print.layer_id]); } else { gcode += this->extrude_perimeters(print, by_region_specific, lower_layer_edge_grids[instance_to_print.layer_id]); - gcode += this->extrude_infill(print,by_region_specific); + gcode += this->extrude_infill(print,by_region_specific, false); } + // ironing + gcode += this->extrude_infill(print,by_region_specific, true); } if (this->config().gcode_label_objects) gcode += std::string("; stop printing object ") + instance_to_print.print_object.model_object()->name + " id:" + std::to_string(instance_to_print.layer_id) + " copy " + std::to_string(instance_to_print.instance_id) + "\n"; @@ -2873,22 +2875,30 @@ std::string GCode::extrude_perimeters(const Print &print, const std::vector &by_region) +std::string GCode::extrude_infill(const Print &print, const std::vector &by_region, bool ironing) { - std::string gcode; + std::string gcode; + ExtrusionEntitiesPtr extrusions; + const char* extrusion_name = ironing ? "ironing" : "infill"; for (const ObjectByExtruder::Island::Region ®ion : by_region) if (! region.infills.empty()) { - m_config.apply(print.regions()[®ion - &by_region.front()]->config()); - ExtrusionEntitiesPtr extrusions { region.infills }; - chain_and_reorder_extrusion_entities(extrusions, &m_last_pos); - for (const ExtrusionEntity *fill : extrusions) { - auto *eec = dynamic_cast(fill); - if (eec) { - for (ExtrusionEntity *ee : eec->chained_path_from(m_last_pos).entities) - gcode += this->extrude_entity(*ee, "infill"); - } else - gcode += this->extrude_entity(*fill, "infill"); - } + extrusions.clear(); + extrusions.reserve(region.infills.size()); + for (ExtrusionEntity *ee : region.infills) + if ((ee->role() == erIroning) == ironing) + extrusions.emplace_back(ee); + if (! extrusions.empty()) { + m_config.apply(print.regions()[®ion - &by_region.front()]->config()); + chain_and_reorder_extrusion_entities(extrusions, &m_last_pos); + for (const ExtrusionEntity *fill : extrusions) { + auto *eec = dynamic_cast(fill); + if (eec) { + for (ExtrusionEntity *ee : eec->chained_path_from(m_last_pos).entities) + gcode += this->extrude_entity(*ee, extrusion_name); + } else + gcode += this->extrude_entity(*fill, extrusion_name); + } + } } return gcode; } @@ -3027,6 +3037,8 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, speed = m_config.get_abs_value("solid_infill_speed"); } else if (path.role() == erTopSolidInfill) { speed = m_config.get_abs_value("top_solid_infill_speed"); + } else if (path.role() == erIroning) { + speed = m_config.get_abs_value("ironing_speed"); } else if (path.role() == erGapFill) { speed = m_config.get_abs_value("gap_fill_speed"); } else { diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 7fc75c92b..2daf0fe16 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -295,7 +295,7 @@ private: const size_t single_object_instance_idx); std::string extrude_perimeters(const Print &print, const std::vector &by_region, std::unique_ptr &lower_layer_edge_grid); - std::string extrude_infill(const Print &print, const std::vector &by_region); + std::string extrude_infill(const Print &print, const std::vector &by_region, bool ironing); std::string extrude_support(const ExtrusionEntityCollection &support_fills); std::string travel_to(const Point &point, ExtrusionRole role, std::string comment); diff --git a/src/libslic3r/GCode/PreviewData.cpp b/src/libslic3r/GCode/PreviewData.cpp index 8a9184e64..3aae15748 100644 --- a/src/libslic3r/GCode/PreviewData.cpp +++ b/src/libslic3r/GCode/PreviewData.cpp @@ -117,6 +117,7 @@ const Color GCodePreviewData::Extrusion::Default_Extrusion_Role_Colors[erCount] Color(1.0f, 1.0f, 0.0f, 1.0f), // erInternalInfill Color(1.0f, 0.0f, 1.0f, 1.0f), // erSolidInfill Color(0.0f, 1.0f, 1.0f, 1.0f), // erTopSolidInfill + Color(0.0f, 1.0f, 1.0f, 1.0f), // erIroning Color(0.5f, 0.5f, 0.5f, 1.0f), // erBridgeInfill Color(1.0f, 1.0f, 1.0f, 1.0f), // erGapFill Color(0.5f, 0.0f, 0.0f, 1.0f), // erSkirt diff --git a/src/libslic3r/Layer.hpp b/src/libslic3r/Layer.hpp index d66aa8f01..54e4baf2c 100644 --- a/src/libslic3r/Layer.hpp +++ b/src/libslic3r/Layer.hpp @@ -36,11 +36,6 @@ public: // collection of surfaces for infill generation SurfaceCollection fill_surfaces; - // Collection of perimeter surfaces. This is a cached result of diff(slices, fill_surfaces). - // While not necessary, the memory consumption is meager and it speeds up calculation. - // The perimeter_surfaces keep the IDs of the slices (top/bottom/) - SurfaceCollection perimeter_surfaces; - // collection of expolygons representing the bridged areas (thus not // needing support material) Polygons bridged; @@ -140,6 +135,7 @@ public: } void make_perimeters(); void make_fills(); + void make_ironing(); void export_region_slices_to_svg(const char *path) const; void export_region_fill_surfaces_to_svg(const char *path) const; diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 8631db624..986b7fa09 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -1583,6 +1583,8 @@ void Print::process() this->set_status(70, L("Infilling layers")); for (PrintObject *obj : m_objects) obj->infill(); + for (PrintObject *obj : m_objects) + obj->ironing(); for (PrintObject *obj : m_objects) obj->generate_support_material(); if (this->set_started(psWipeTower)) { diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 7180bae17..d4d3a1ddc 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -41,7 +41,7 @@ enum PrintStep { enum PrintObjectStep { posSlice, posPerimeters, posPrepareInfill, - posInfill, posSupportMaterial, posCount, + posInfill, posIroning, posSupportMaterial, posCount, }; // A PrintRegion object represents a group of volumes to print @@ -218,6 +218,7 @@ private: void make_perimeters(); void prepare_infill(); void infill(); + void ironing(); void generate_support_material(); void _slice(const std::vector &layer_height_profile); diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index c7a7a9c8e..259b016e3 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -1076,6 +1076,53 @@ void PrintConfigDef::init_fff_params() def->mode = comExpert; def->set_default_value(new ConfigOptionBool(false)); + def = this->add("ironing", coBool); + def->label = L("Enable ironing"); + def->tooltip = L("Enable ironing of the top layers with the hot print head for smooth surface"); + def->category = L("Ironing"); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("ironing_type", coEnum); + def->label = L("Ironingy Type"); + def->tooltip = L("Ironingy Type"); + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("top"); + def->enum_values.push_back("topmost"); + def->enum_values.push_back("solid"); + def->enum_labels.push_back("All top surfaces"); + def->enum_labels.push_back("Topmost surface only"); + def->enum_labels.push_back("All solid surfaces"); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionEnum(IroningType::TopSurfaces)); + + def = this->add("ironing_flowrate", coPercent); + def->label = L("Flow rate"); + def->category = L("Ironing"); + def->tooltip = L("Percent of a flow rate relative to object's normal layer height."); + def->sidetext = L("%"); + def->ratio_over = "layer_height"; + def->min = 0; + def->mode = comExpert; + def->set_default_value(new ConfigOptionPercent(15)); + + def = this->add("ironing_spacing", coFloat); + def->label = L("Spacing between ironing passes"); + def->tooltip = L("Distance between ironing lins"); + def->sidetext = L("mm"); + def->min = 0; + def->mode = comExpert; + def->set_default_value(new ConfigOptionFloat(0.1)); + + def = this->add("ironing_speed", coFloat); + def->label = L("Ironing speed"); + def->category = L("Speed"); + def->tooltip = L("Ironing speed"); + def->sidetext = L("mm/s"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(60)); + def = this->add("layer_gcode", coString); def->label = L("After layer change G-code"); def->tooltip = L("This custom code is inserted at every layer change, right after the Z move " diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index ca509e37a..a7d2d8270 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -38,6 +38,13 @@ enum InfillPattern { ipGyroid, ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, ipCount, }; +enum class IroningType { + TopSurfaces, + TopmostOnly, + AllSolid, + Count, +}; + enum SupportMaterialPattern { smpRectilinear, smpRectilinearGrid, smpHoneycomb, }; @@ -122,6 +129,16 @@ template<> inline const t_config_enum_values& ConfigOptionEnum::g return keys_map; } +template<> inline const t_config_enum_values& ConfigOptionEnum::get_enum_values() { + static t_config_enum_values keys_map; + if (keys_map.empty()) { + keys_map["top"] = int(IroningType::TopSurfaces); + keys_map["topmost"] = int(IroningType::TopmostOnly); + keys_map["solid"] = int(IroningType::AllSolid); + } + return keys_map; +} + template<> inline const t_config_enum_values& ConfigOptionEnum::get_enum_values() { static t_config_enum_values keys_map; if (keys_map.empty()) { @@ -485,6 +502,12 @@ public: ConfigOptionInt infill_every_layers; ConfigOptionFloatOrPercent infill_overlap; ConfigOptionFloat infill_speed; + // Ironing options + ConfigOptionBool ironing; + ConfigOptionEnum ironing_type; + ConfigOptionPercent ironing_flowrate; + ConfigOptionFloat ironing_spacing; + ConfigOptionFloat ironing_speed; // Detect bridging perimeters ConfigOptionBool overhangs; ConfigOptionInt perimeter_extruder; @@ -530,6 +553,11 @@ protected: OPT_PTR(infill_every_layers); OPT_PTR(infill_overlap); OPT_PTR(infill_speed); + OPT_PTR(ironing); + OPT_PTR(ironing_type); + OPT_PTR(ironing_flowrate); + OPT_PTR(ironing_spacing); + OPT_PTR(ironing_speed); OPT_PTR(overhangs); OPT_PTR(perimeter_extruder); OPT_PTR(perimeter_extrusion_width); diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 5573f4ac3..a68a9605b 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -387,6 +387,25 @@ void PrintObject::infill() } } +void PrintObject::ironing() +{ + if (this->set_started(posIroning)) { + BOOST_LOG_TRIVIAL(debug) << "Ironing in parallel - start"; + tbb::parallel_for( + tbb::blocked_range(1, m_layers.size()), + [this](const tbb::blocked_range& range) { + for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { + m_print->throw_if_canceled(); + m_layers[layer_idx]->make_ironing(); + } + } + ); + m_print->throw_if_canceled(); + BOOST_LOG_TRIVIAL(debug) << "Ironing in parallel - end"; + this->set_done(posIroning); + } +} + void PrintObject::generate_support_material() { if (this->set_started(posSupportMaterial)) { diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index 8d1daeb8e..d7f0a37b0 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -298,6 +298,10 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config) toggle_field("support_material_extruder", have_support_material || have_skirt); toggle_field("support_material_speed", have_support_material || have_brim || have_skirt); + bool has_ironing = config->opt_bool("ironing"); + for (auto el : { "ironing_type", "ironing_flowrate", "ironing_spacing", "ironing_speed" }) + toggle_field(el, has_ironing); + bool have_sequential_printing = config->opt_bool("complete_objects"); for (auto el : { "extruder_clearance_radius", "extruder_clearance_height" }) toggle_field(el, have_sequential_printing); diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index a1a6583bc..12699c37f 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -1027,6 +1027,8 @@ boost::any& Choice::get_value() } if (m_opt_id.compare("fill_pattern") == 0) m_value = static_cast(ret_enum); + else if (m_opt_id.compare("ironing_type") == 0) + m_value = static_cast(ret_enum); else if (m_opt_id.compare("gcode_flavor") == 0) m_value = static_cast(ret_enum); else if (m_opt_id.compare("support_material_pattern") == 0) diff --git a/src/slic3r/GUI/GUI.cpp b/src/slic3r/GUI/GUI.cpp index caeb8da03..7deed0786 100644 --- a/src/slic3r/GUI/GUI.cpp +++ b/src/slic3r/GUI/GUI.cpp @@ -188,6 +188,8 @@ void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt opt_key == "bottom_fill_pattern" || opt_key == "fill_pattern") config.set_key_value(opt_key, new ConfigOptionEnum(boost::any_cast(value))); + else if (opt_key.compare("ironing_type") == 0) + config.set_key_value(opt_key, new ConfigOptionEnum(boost::any_cast(value))); else if (opt_key.compare("gcode_flavor") == 0) config.set_key_value(opt_key, new ConfigOptionEnum(boost::any_cast(value))); else if (opt_key.compare("support_material_pattern") == 0) diff --git a/src/slic3r/GUI/OptionsGroup.cpp b/src/slic3r/GUI/OptionsGroup.cpp index 207d42b5b..d561d8e2a 100644 --- a/src/slic3r/GUI/OptionsGroup.cpp +++ b/src/slic3r/GUI/OptionsGroup.cpp @@ -680,6 +680,9 @@ boost::any ConfigOptionsGroup::get_config_value(const DynamicPrintConfig& config opt_key == "fill_pattern" ) { ret = static_cast(config.option>(opt_key)->value); } + else if (opt_key.compare("ironing_type") == 0 ) { + ret = static_cast(config.option>(opt_key)->value); + } else if (opt_key.compare("gcode_flavor") == 0 ) { ret = static_cast(config.option>(opt_key)->value); } diff --git a/src/slic3r/GUI/Preset.cpp b/src/slic3r/GUI/Preset.cpp index 3731f03de..a1f83141a 100644 --- a/src/slic3r/GUI/Preset.cpp +++ b/src/slic3r/GUI/Preset.cpp @@ -405,8 +405,9 @@ const std::vector& Preset::print_options() "extra_perimeters", "ensure_vertical_shell_thickness", "avoid_crossing_perimeters", "thin_walls", "overhangs", "seam_position", "external_perimeters_first", "fill_density", "fill_pattern", "top_fill_pattern", "bottom_fill_pattern", "infill_every_layers", "infill_only_where_needed", "solid_infill_every_layers", "fill_angle", "bridge_angle", - "solid_infill_below_area", "only_retract_when_crossing_perimeters", "infill_first", "max_print_speed", - "max_volumetric_speed", + "solid_infill_below_area", "only_retract_when_crossing_perimeters", "infill_first", + "ironing", "ironing_type", "ironing_flowrate", "ironing_speed", "ironing_spacing", + "max_print_speed", "max_volumetric_speed", #ifdef HAS_PRESSURE_EQUALIZER "max_volumetric_extrusion_rate_slope_positive", "max_volumetric_extrusion_rate_slope_negative", #endif /* HAS_PRESSURE_EQUALIZER */ diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 0c68979f1..2caa7baaf 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1162,6 +1162,12 @@ void TabPrint::build() optgroup->append_single_option_line("top_fill_pattern"); optgroup->append_single_option_line("bottom_fill_pattern"); + optgroup = page->new_optgroup(_(L("Ironing"))); + optgroup->append_single_option_line("ironing"); + optgroup->append_single_option_line("ironing_type"); + optgroup->append_single_option_line("ironing_flowrate"); + optgroup->append_single_option_line("ironing_spacing"); + optgroup = page->new_optgroup(_(L("Reducing printing time"))); optgroup->append_single_option_line("infill_every_layers"); optgroup->append_single_option_line("infill_only_where_needed"); @@ -1222,6 +1228,7 @@ void TabPrint::build() optgroup->append_single_option_line("support_material_interface_speed"); optgroup->append_single_option_line("bridge_speed"); optgroup->append_single_option_line("gap_fill_speed"); + optgroup->append_single_option_line("ironing_speed"); optgroup = page->new_optgroup(_(L("Speed for non-print moves"))); optgroup->append_single_option_line("travel_speed"); From 03eb5ffcd5d71181e2c0533060fd94a6601be76f Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 22 Apr 2020 10:54:11 +0200 Subject: [PATCH 02/16] WIP: Reworking of FillRectilinear2 to support monotonous infill with ant colony optimization and 3-opt flips. --- src/libslic3r/Fill/FillBase.hpp | 6 + src/libslic3r/Fill/FillRectilinear2.cpp | 2007 ++++++++++++++++------- src/libslic3r/libslic3r.h | 1 + 3 files changed, 1445 insertions(+), 569 deletions(-) diff --git a/src/libslic3r/Fill/FillBase.hpp b/src/libslic3r/Fill/FillBase.hpp index 5a9e92739..9fb37d2c0 100644 --- a/src/libslic3r/Fill/FillBase.hpp +++ b/src/libslic3r/Fill/FillBase.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include @@ -18,6 +19,11 @@ namespace Slic3r { class ExPolygon; class Surface; +class InfillFailedException : public std::runtime_error { +public: + InfillFailedException() : std::runtime_error("Infill failed") {} +}; + struct FillParams { bool full_infill() const { return density > 0.9999f; } diff --git a/src/libslic3r/Fill/FillRectilinear2.cpp b/src/libslic3r/Fill/FillRectilinear2.cpp index 8aea75886..c2c19046e 100644 --- a/src/libslic3r/Fill/FillRectilinear2.cpp +++ b/src/libslic3r/Fill/FillRectilinear2.cpp @@ -4,7 +4,9 @@ #include #include #include +#include +#include #include #include "../ClipperUtils.hpp" @@ -105,26 +107,15 @@ static inline void polygon_segment_append_reversed(Points &out, const Polygon &p } // Intersection point of a vertical line with a polygon segment. -class SegmentIntersection +struct SegmentIntersection { -public: - SegmentIntersection() : - iContour(0), - iSegment(0), - pos_p(0), - pos_q(1), - type(UNKNOWN), - consumed_vertical_up(false), - consumed_perimeter_right(false) - {} - // Index of a contour in ExPolygonWithOffset, with which this vertical line intersects. - size_t iContour; + size_t iContour { 0 }; // Index of a segment in iContour, with which this vertical line intersects. - size_t iSegment; - // y position of the intersection, ratinal number. - int64_t pos_p; - uint32_t pos_q; + size_t iSegment { 0 }; + // y position of the intersection, rational number. + int64_t pos_p { 0 }; + uint32_t pos_q { 1 }; coord_t pos() const { // Division rounds both positive and negative down to zero. @@ -141,30 +132,140 @@ public: // A vertical segment will be at least intersected by OUTER_LOW, OUTER_HIGH, // but it could be intersected with OUTER_LOW, INNER_LOW, INNER_HIGH, OUTER_HIGH, // and there may be more than one pair of INNER_LOW, INNER_HIGH between OUTER_LOW, OUTER_HIGH. - enum SegmentIntersectionType { + enum SegmentIntersectionType : char { OUTER_LOW = 0, OUTER_HIGH = 1, INNER_LOW = 2, INNER_HIGH = 3, UNKNOWN = -1 }; - SegmentIntersectionType type; + SegmentIntersectionType type { UNKNOWN }; + // Left vertical line / contour intersection point. + // null if next_on_contour_vertical. + int32_t prev_on_contour { 0 }; + // Right vertical line / contour intersection point. + // If next_on_contour_vertical, then then next_on_contour contains next contour point on the same vertical line. + int32_t next_on_contour { 0 }; + + enum class LinkType : uint8_t { + // Horizontal link (left or right). + Horizontal, + // Vertical link, up. + Up, + // Vertical link, down. + Down + }; + + enum class LinkQuality : uint8_t { + Invalid, + Valid, + // Valid link, to be followed when extruding. + // Link inside a monotonous region. + ValidMonotonous, + // Valid link, to be possibly followed when extruding. + // Link between two monotonous regions. + ValidNonMonotonous, + // Link from T to end of another contour. + FromT, + // Link from end of one contour to T. + ToT, + // Link from one T to another T, making a letter H. + H, + // Vertical segment + TooLong, + }; + + // Kept grouped with other booleans for smaller memory footprint. + LinkType prev_on_contour_type { LinkType::Horizontal }; + LinkType next_on_contour_type { LinkType::Horizontal }; + LinkQuality prev_on_contour_quality { true }; + LinkQuality next_on_contour_quality { true }; // Was this segment along the y axis consumed? // Up means up along the vertical segment. - bool consumed_vertical_up; + bool consumed_vertical_up { false }; // Was a segment of the inner perimeter contour consumed? // Right means right from the vertical segment. - bool consumed_perimeter_right; + bool consumed_perimeter_right { false }; // For the INNER_LOW type, this point may be connected to another INNER_LOW point following a perimeter contour. // For the INNER_HIGH type, this point may be connected to another INNER_HIGH point following a perimeter contour. // If INNER_LOW is connected to INNER_HIGH or vice versa, // one has to make sure the vertical infill line does not overlap with the connecting perimeter line. - bool is_inner() const { return type == INNER_LOW || type == INNER_HIGH; } - bool is_outer() const { return type == OUTER_LOW || type == OUTER_HIGH; } - bool is_low () const { return type == INNER_LOW || type == OUTER_LOW; } - bool is_high () const { return type == INNER_HIGH || type == OUTER_HIGH; } + bool is_inner() const { return type == INNER_LOW || type == INNER_HIGH; } + bool is_outer() const { return type == OUTER_LOW || type == OUTER_HIGH; } + bool is_low () const { return type == INNER_LOW || type == OUTER_LOW; } + bool is_high () const { return type == INNER_HIGH || type == OUTER_HIGH; } + + enum class Side { + Left, + Right + }; + enum class Direction { + Up, + Down + }; + + bool has_left_horizontal() const { return this->prev_on_contour_type == LinkType::Horizontal; } + bool has_right_horizontal() const { return this->next_on_contour_type == LinkType::Horizontal; } + bool has_horizontal(Side side) const { return side == Side::Left ? this->has_left_horizontal() : this->has_right_horizontal(); } + + bool has_left_vertical_up() const { return this->prev_on_contour_type == LinkType::Up; } + bool has_left_vertical_down() const { return this->prev_on_contour_type == LinkType::Down; } + bool has_left_vertical(Direction dir) const { return dir == Direction::Up ? this->has_left_vertical_up() : this->has_left_vertical_down(); } + bool has_left_vertical() const { return this->has_left_vertical_up() || this->has_left_vertical_down(); } + bool has_left_vertical_outside() const { return this->is_low() ? this->has_left_vertical_down() : this->has_left_vertical_up(); } + + bool has_right_vertical_up() const { return this->next_on_contour_type == LinkType::Up; } + bool has_right_vertical_down() const { return this->next_on_contour_type == LinkType::Down; } + bool has_right_vertical(Direction dir) const { return dir == Direction::Up ? this->has_right_vertical_up() : this->has_right_vertical_down(); } + bool has_right_vertical() const { return this->has_right_vertical_up() || this->has_right_vertical_down(); } + bool has_right_vertical_outside() const { return this->is_low() ? this->has_right_vertical_down() : this->has_right_vertical_up(); } + + bool has_vertical() const { return this->has_left_vertical() || this->has_right_vertical(); } + bool has_vertical(Side side) const { return side == Side::Left ? this->has_left_vertical() : this->has_right_vertical(); } + bool has_vertical_up() const { return this->has_left_vertical_up() || this->has_right_vertical_up(); } + bool has_vertical_down() const { return this->has_left_vertical_down() || this->has_right_vertical_down(); } + bool has_vertical(Direction dir) const { return dir == Direction::Up ? this->has_vertical_up() : this->has_vertical_down(); } + + int left_horizontal() const { return this->has_left_horizontal() ? this->prev_on_contour : -1; } + int right_horizontal() const { return this->has_right_horizontal() ? this->next_on_contour : -1; } + int horizontal(Side side) const { return side == Side::Left ? this->left_horizontal() : this->right_horizontal(); } + + int left_vertical_up() const { return this->has_left_vertical_up() ? this->prev_on_contour : -1; } + int left_vertical_down() const { return this->has_left_vertical_down() ? this->prev_on_contour : -1; } + int left_vertical(Direction dir) const { return (dir == Direction::Up ? this->has_left_vertical_up() : this->has_left_vertical_down()) ? this->prev_on_contour : -1; } + int left_vertical() const { return this->has_left_vertical() ? this->prev_on_contour : -1; } + int left_vertical_outside() const { return this->is_low() ? this->left_vertical_down() : this->left_vertical_up(); } + int right_vertical_up() const { return this->has_right_vertical_up() ? this->prev_on_contour : -1; } + int right_vertical_down() const { return this->has_right_vertical_down() ? this->prev_on_contour : -1; } + int right_vertical(Direction dir) const { return (dir == Direction::Up ? this->has_right_vertical_up() : this->has_right_vertical_down()) ? this->next_on_contour : -1; } + int right_vertical() const { return this->has_right_vertical() ? this->prev_on_contour : -1; } + int right_vertical_outside() const { return this->is_low() ? this->right_vertical_down() : this->right_vertical_up(); } + + int vertical_up(Side side) const { return side == Side::Left ? this->left_vertical_up() : this->right_vertical_up(); } + int vertical_down(Side side) const { return side == Side::Left ? this->left_vertical_down() : this->right_vertical_down(); } + int vertical_outside(Side side) const { return side == Side::Left ? this->left_vertical_outside() : this->right_vertical_outside(); } + int vertical_up() const { + assert(! this->has_left_vertical_up() || ! this->has_right_vertical_up()); + return this->has_left_vertical_up() ? this->left_vertical_up() : this->right_vertical_up(); + } + LinkQuality vertical_up_quality() const { + assert(! this->has_left_vertical_up() || ! this->has_right_vertical_up()); + return this->has_left_vertical_up() ? this->prev_on_contour_quality : this->next_on_contour_quality; + } + int vertical_down() const { + assert(! this->has_left_vertical_down() || ! this->has_right_vertical_down()); + return this->has_left_vertical_down() ? this->left_vertical_down() : this->right_vertical_down(); + } + LinkQuality vertical_down_quality() const { + assert(! this->has_left_vertical_down() || ! this->has_right_vertical_down()); + return this->has_left_vertical_down() ? this->prev_on_contour_quality : this->next_on_contour_quality; + } + int vertical_outside() const { return this->is_low() ? this->vertical_down() : this->vertical_up(); } + +// int next_up() const { return this->prev_on_contour_vertical ? -1 : this->prev_on_contour; } +// int next_right() const { return this->next_on_contour_vertical ? -1 : this->next_on_contour; } // Compare two y intersection points given by rational numbers. // Note that the rational number is given as pos_p/pos_q, where pos_p is int64 and pos_q is uint32. @@ -250,11 +351,11 @@ public: return l_hi + (l_lo >> 32) == r_hi + (r_lo >> 32); } }; +static_assert(sizeof(SegmentIntersection::pos_q) == 4, "SegmentIntersection::pos_q has to be 32bit long!"); // A vertical line with intersection points with polygons. -class SegmentedIntersectionLine +struct SegmentedIntersectionLine { -public: // Index of this vertical intersection line. size_t idx; // x position of this vertical intersection line. @@ -290,10 +391,10 @@ public: // bool sticks_removed = remove_sticks(polygons_src); // if (sticks_removed) printf("Sticks removed!\n"); - polygons_outer = offset(polygons_src, aoffset1, + polygons_outer = offset(polygons_src, float(aoffset1), ClipperLib::jtMiter, mitterLimit); - polygons_inner = offset(polygons_outer, aoffset2 - aoffset1, + polygons_inner = offset(polygons_outer, float(aoffset2 - aoffset1), ClipperLib::jtMiter, mitterLimit); // Filter out contours with zero area or small area, contours with 2 points only. @@ -364,97 +465,6 @@ static inline int distance_of_segmens(const Polygon &poly, size_t seg1, size_t s return d; } -// For a vertical line, an inner contour and an intersection point, -// find an intersection point on the previous resp. next vertical line. -// The intersection point is connected with the prev resp. next intersection point with iInnerContour. -// Return -1 if there is no such point on the previous resp. next vertical line. -static inline int intersection_on_prev_next_vertical_line( - const ExPolygonWithOffset &poly_with_offset, - const std::vector &segs, - size_t iVerticalLine, - size_t iInnerContour, - size_t iIntersection, - bool dir_is_next) -{ - size_t iVerticalLineOther = iVerticalLine; - if (dir_is_next) { - if (++ iVerticalLineOther == segs.size()) - // No successive vertical line. - return -1; - } else if (iVerticalLineOther -- == 0) { - // No preceding vertical line. - return -1; - } - - const SegmentedIntersectionLine &il = segs[iVerticalLine]; - const SegmentIntersection &itsct = il.intersections[iIntersection]; - const SegmentedIntersectionLine &il2 = segs[iVerticalLineOther]; - const Polygon &poly = poly_with_offset.contour(iInnerContour); -// const bool ccw = poly_with_offset.is_contour_ccw(iInnerContour); - const bool forward = itsct.is_low() == dir_is_next; - // Resulting index of an intersection point on il2. - int out = -1; - // Find an intersection point on iVerticalLineOther, intersecting iInnerContour - // at the same orientation as iIntersection, and being closest to iIntersection - // in the number of contour segments, when following the direction of the contour. - int dmin = std::numeric_limits::max(); - for (size_t i = 0; i < il2.intersections.size(); ++ i) { - const SegmentIntersection &itsct2 = il2.intersections[i]; - if (itsct.iContour == itsct2.iContour && itsct.type == itsct2.type) { - /* - if (itsct.is_low()) { - assert(itsct.type == SegmentIntersection::INNER_LOW); - assert(iIntersection > 0); - assert(il.intersections[iIntersection-1].type == SegmentIntersection::OUTER_LOW); - assert(i > 0); - if (il2.intersections[i-1].is_inner()) - // Take only the lowest inner intersection point. - continue; - assert(il2.intersections[i-1].type == SegmentIntersection::OUTER_LOW); - } else { - assert(itsct.type == SegmentIntersection::INNER_HIGH); - assert(iIntersection+1 < il.intersections.size()); - assert(il.intersections[iIntersection+1].type == SegmentIntersection::OUTER_HIGH); - assert(i+1 < il2.intersections.size()); - if (il2.intersections[i+1].is_inner()) - // Take only the highest inner intersection point. - continue; - assert(il2.intersections[i+1].type == SegmentIntersection::OUTER_HIGH); - } - */ - // The intersection points lie on the same contour and have the same orientation. - // Find the intersection point with a shortest path in the direction of the contour. - int d = distance_of_segmens(poly, itsct.iSegment, itsct2.iSegment, forward); - if (d < dmin) { - out = i; - dmin = d; - } - } - } - //FIXME this routine is not asymptotic optimal, it will be slow if there are many intersection points along the line. - return out; -} - -static inline int intersection_on_prev_vertical_line( - const ExPolygonWithOffset &poly_with_offset, - const std::vector &segs, - size_t iVerticalLine, - size_t iInnerContour, - size_t iIntersection) -{ - return intersection_on_prev_next_vertical_line(poly_with_offset, segs, iVerticalLine, iInnerContour, iIntersection, false); -} - -static inline int intersection_on_next_vertical_line( - const ExPolygonWithOffset &poly_with_offset, - const std::vector &segs, - size_t iVerticalLine, - size_t iInnerContour, - size_t iIntersection) -{ - return intersection_on_prev_next_vertical_line(poly_with_offset, segs, iVerticalLine, iInnerContour, iIntersection, true); -} - enum IntersectionTypeOtherVLine { // There is no connection point on the other vertical line. INTERSECTION_TYPE_OTHER_VLINE_UNDEFINED = -1, @@ -477,17 +487,19 @@ static inline IntersectionTypeOtherVLine intersection_type_on_prev_next_vertical const std::vector &segs, size_t iVerticalLine, size_t iIntersection, - size_t iIntersectionOther, - bool dir_is_next) + SegmentIntersection::Side side) { - // This routine will propose a connecting line even if the connecting perimeter segment intersects - // iVertical line multiple times before reaching iIntersectionOther. - if (iIntersectionOther == size_t(-1)) - return INTERSECTION_TYPE_OTHER_VLINE_UNDEFINED; - assert(dir_is_next ? (iVerticalLine + 1 < segs.size()) : (iVerticalLine > 0)); const SegmentedIntersectionLine &il_this = segs[iVerticalLine]; const SegmentIntersection &itsct_this = il_this.intersections[iIntersection]; - const SegmentedIntersectionLine &il_other = segs[dir_is_next ? (iVerticalLine+1) : (iVerticalLine-1)]; + if (itsct_this.has_vertical(side)) + // Not the first intersection along the contor. This intersection point + // has been preceded by an intersection point along the vertical line. + return INTERSECTION_TYPE_OTHER_VLINE_NOT_FIRST; + int iIntersectionOther = itsct_this.horizontal(side); + if (iIntersectionOther == -1) + return INTERSECTION_TYPE_OTHER_VLINE_UNDEFINED; + assert(side == SegmentIntersection::Side::Right ? (iVerticalLine + 1 < segs.size()) : (iVerticalLine > 0)); + const SegmentedIntersectionLine &il_other = segs[side == SegmentIntersection::Side::Right ? (iVerticalLine+1) : (iVerticalLine-1)]; const SegmentIntersection &itsct_other = il_other.intersections[iIntersectionOther]; assert(itsct_other.is_inner()); assert(iIntersectionOther > 0); @@ -499,7 +511,7 @@ static inline IntersectionTypeOtherVLine intersection_type_on_prev_next_vertical // Only perimeter segments connecting to the end of a vertical segment are followed. return INTERSECTION_TYPE_OTHER_VLINE_INNER; assert(itsct_other.is_low() == itsct_other2.is_low()); - if (dir_is_next ? itsct_this.consumed_perimeter_right : itsct_other.consumed_perimeter_right) + if (side == SegmentIntersection::Side::Right ? itsct_this.consumed_perimeter_right : itsct_other.consumed_perimeter_right) // This perimeter segment was already consumed. return INTERSECTION_TYPE_OTHER_VLINE_CONSUMED; if (itsct_other.is_low() ? itsct_other.consumed_vertical_up : il_other.intersections[iIntersectionOther-1].consumed_vertical_up) @@ -511,19 +523,17 @@ static inline IntersectionTypeOtherVLine intersection_type_on_prev_next_vertical static inline IntersectionTypeOtherVLine intersection_type_on_prev_vertical_line( const std::vector &segs, size_t iVerticalLine, - size_t iIntersection, - size_t iIntersectionPrev) + size_t iIntersection) { - return intersection_type_on_prev_next_vertical_line(segs, iVerticalLine, iIntersection, iIntersectionPrev, false); + return intersection_type_on_prev_next_vertical_line(segs, iVerticalLine, iIntersection, SegmentIntersection::Side::Left); } static inline IntersectionTypeOtherVLine intersection_type_on_next_vertical_line( const std::vector &segs, size_t iVerticalLine, - size_t iIntersection, - size_t iIntersectionNext) + size_t iIntersection) { - return intersection_type_on_prev_next_vertical_line(segs, iVerticalLine, iIntersection, iIntersectionNext, true); + return intersection_type_on_prev_next_vertical_line(segs, iVerticalLine, iIntersection, SegmentIntersection::Side::Right); } // Measure an Euclidian length of a perimeter segment when going from iIntersection to iIntersection2. @@ -531,7 +541,6 @@ static inline coordf_t measure_perimeter_prev_next_segment_length( const ExPolygonWithOffset &poly_with_offset, const std::vector &segs, size_t iVerticalLine, - size_t iInnerContour, size_t iIntersection, size_t iIntersection2, bool dir_is_next) @@ -550,8 +559,9 @@ static inline coordf_t measure_perimeter_prev_next_segment_length( const SegmentIntersection &itsct = il.intersections[iIntersection]; const SegmentedIntersectionLine &il2 = segs[iVerticalLineOther]; const SegmentIntersection &itsct2 = il2.intersections[iIntersection2]; - const Polygon &poly = poly_with_offset.contour(iInnerContour); -// const bool ccw = poly_with_offset.is_contour_ccw(iInnerContour); + assert(itsct.iContour == itsct2.iContour); + const Polygon &poly = poly_with_offset.contour(itsct.iContour); +// const bool ccw = poly_with_offset.is_contour_ccw(il.iContour); assert(itsct.type == itsct2.type); assert(itsct.iContour == itsct2.iContour); assert(itsct.is_inner()); @@ -568,22 +578,20 @@ static inline coordf_t measure_perimeter_prev_segment_length( const ExPolygonWithOffset &poly_with_offset, const std::vector &segs, size_t iVerticalLine, - size_t iInnerContour, size_t iIntersection, size_t iIntersection2) { - return measure_perimeter_prev_next_segment_length(poly_with_offset, segs, iVerticalLine, iInnerContour, iIntersection, iIntersection2, false); + return measure_perimeter_prev_next_segment_length(poly_with_offset, segs, iVerticalLine, iIntersection, iIntersection2, false); } static inline coordf_t measure_perimeter_next_segment_length( const ExPolygonWithOffset &poly_with_offset, const std::vector &segs, size_t iVerticalLine, - size_t iInnerContour, size_t iIntersection, size_t iIntersection2) { - return measure_perimeter_prev_next_segment_length(poly_with_offset, segs, iVerticalLine, iInnerContour, iIntersection, iIntersection2, true); + return measure_perimeter_prev_next_segment_length(poly_with_offset, segs, iVerticalLine, iIntersection, iIntersection2, true); } // Append the points of a perimeter segment when going from iIntersection to iIntersection2. @@ -632,7 +640,6 @@ static inline coordf_t measure_perimeter_segment_on_vertical_line_length( const ExPolygonWithOffset &poly_with_offset, const std::vector &segs, size_t iVerticalLine, - size_t iInnerContour, size_t iIntersection, size_t iIntersection2, bool forward) @@ -640,11 +647,10 @@ static inline coordf_t measure_perimeter_segment_on_vertical_line_length( const SegmentedIntersectionLine &il = segs[iVerticalLine]; const SegmentIntersection &itsct = il.intersections[iIntersection]; const SegmentIntersection &itsct2 = il.intersections[iIntersection2]; - const Polygon &poly = poly_with_offset.contour(iInnerContour); + const Polygon &poly = poly_with_offset.contour(itsct.iContour); assert(itsct.is_inner()); assert(itsct2.is_inner()); assert(itsct.type != itsct2.type); - assert(itsct.iContour == iInnerContour); assert(itsct.iContour == itsct2.iContour); Point p1(il.pos, itsct.pos()); Point p2(il.pos, itsct2.pos()); @@ -759,80 +765,15 @@ enum DirectionMask DIR_BACKWARD = 2 }; -bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillParams ¶ms, float angleBase, float pattern_shift, Polylines &polylines_out) +static std::vector slice_region_by_vertical_lines(const ExPolygonWithOffset &poly_with_offset, size_t n_vlines, coord_t x0, coord_t line_spacing) { - // At the end, only the new polylines will be rotated back. - size_t n_polylines_out_initial = polylines_out.size(); - - // Shrink the input polygon a bit first to not push the infill lines out of the perimeters. -// const float INFILL_OVERLAP_OVER_SPACING = 0.3f; - const float INFILL_OVERLAP_OVER_SPACING = 0.45f; - assert(INFILL_OVERLAP_OVER_SPACING > 0 && INFILL_OVERLAP_OVER_SPACING < 0.5f); - - // Rotate polygons so that we can work with vertical lines here - std::pair rotate_vector = this->_infill_direction(surface); - rotate_vector.first += angleBase; - - assert(params.density > 0.0001f && params.density <= 1.f); - coord_t line_spacing = coord_t(scale_(this->spacing) / params.density); - - // On the polygons of poly_with_offset, the infill lines will be connected. - ExPolygonWithOffset poly_with_offset( - surface->expolygon, - - rotate_vector.first, - scale_(this->overlap - (0.5 - INFILL_OVERLAP_OVER_SPACING) * this->spacing), - scale_(this->overlap - 0.5 * this->spacing)); - if (poly_with_offset.n_contours_inner == 0) { - // Not a single infill line fits. - //FIXME maybe one shall trigger the gap fill here? - return true; - } - - BoundingBox bounding_box = poly_with_offset.bounding_box_src(); - - // define flow spacing according to requested density - if (params.full_infill() && !params.dont_adjust) { - line_spacing = this->_adjust_solid_spacing(bounding_box.size()(0), line_spacing); - this->spacing = unscale(line_spacing); - } else { - // extend bounding box so that our pattern will be aligned with other layers - // Transform the reference point to the rotated coordinate system. - Point refpt = rotate_vector.second.rotated(- rotate_vector.first); - // _align_to_grid will not work correctly with positive pattern_shift. - coord_t pattern_shift_scaled = coord_t(scale_(pattern_shift)) % line_spacing; - refpt(0) -= (pattern_shift_scaled >= 0) ? pattern_shift_scaled : (line_spacing + pattern_shift_scaled); - bounding_box.merge(_align_to_grid( - bounding_box.min, - Point(line_spacing, line_spacing), - refpt)); - } - - // Intersect a set of euqally spaced vertical lines wiht expolygon. - // n_vlines = ceil(bbox_width / line_spacing) - size_t n_vlines = (bounding_box.max(0) - bounding_box.min(0) + line_spacing - 1) / line_spacing; - coord_t x0 = bounding_box.min(0); - if (params.full_infill()) - x0 += (line_spacing + SCALED_EPSILON) / 2; - -#ifdef SLIC3R_DEBUG - static int iRun = 0; - BoundingBox bbox_svg = poly_with_offset.bounding_box_outer(); - ::Slic3r::SVG svg(debug_out_path("FillRectilinear2-%d.svg", iRun), bbox_svg); // , scale_(1.)); - poly_with_offset.export_to_svg(svg); - { - ::Slic3r::SVG svg(debug_out_path("FillRectilinear2-initial-%d.svg", iRun), bbox_svg); // , scale_(1.)); - poly_with_offset.export_to_svg(svg); - } - iRun ++; -#endif /* SLIC3R_DEBUG */ - - // For each contour // Allocate storage for the segments. std::vector segs(n_vlines, SegmentedIntersectionLine()); - for (size_t i = 0; i < n_vlines; ++ i) { + for (coord_t i = 0; i < coord_t(n_vlines); ++ i) { segs[i].idx = i; segs[i].pos = x0 + i * line_spacing; } + // For each contour for (size_t iContour = 0; iContour < poly_with_offset.n_contours; ++ iContour) { const Points &contour = poly_with_offset.contour(iContour).points; if (contour.size() < 2) @@ -977,26 +918,1291 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillP } // Verify the segments. If something is wrong, give up. -#define ASSERT_OR_RETURN(CONDITION) do { assert(CONDITION); if (! (CONDITION)) return false; } while (0) +#define ASSERT_THROW(CONDITION) do { assert(CONDITION); throw InfillFailedException(); } while (0) for (size_t i_seg = 0; i_seg < segs.size(); ++ i_seg) { SegmentedIntersectionLine &sil = segs[i_seg]; // The intersection points have to be even. - ASSERT_OR_RETURN((sil.intersections.size() & 1) == 0); + ASSERT_THROW((sil.intersections.size() & 1) == 0); for (size_t i = 0; i < sil.intersections.size();) { // An intersection segment crossing the bigger contour may cross the inner offsetted contour even number of times. - ASSERT_OR_RETURN(sil.intersections[i].type == SegmentIntersection::OUTER_LOW); + ASSERT_THROW(sil.intersections[i].type == SegmentIntersection::OUTER_LOW); size_t j = i + 1; - ASSERT_OR_RETURN(j < sil.intersections.size()); - ASSERT_OR_RETURN(sil.intersections[j].type == SegmentIntersection::INNER_LOW || sil.intersections[j].type == SegmentIntersection::OUTER_HIGH); + ASSERT_THROW(j < sil.intersections.size()); + ASSERT_THROW(sil.intersections[j].type == SegmentIntersection::INNER_LOW || sil.intersections[j].type == SegmentIntersection::OUTER_HIGH); for (; j < sil.intersections.size() && sil.intersections[j].is_inner(); ++ j) ; - ASSERT_OR_RETURN(j < sil.intersections.size()); - ASSERT_OR_RETURN((j & 1) == 1); - ASSERT_OR_RETURN(sil.intersections[j].type == SegmentIntersection::OUTER_HIGH); - ASSERT_OR_RETURN(i + 1 == j || sil.intersections[j - 1].type == SegmentIntersection::INNER_HIGH); + ASSERT_THROW(j < sil.intersections.size()); + ASSERT_THROW((j & 1) == 1); + ASSERT_THROW(sil.intersections[j].type == SegmentIntersection::OUTER_HIGH); + ASSERT_THROW(i + 1 == j || sil.intersections[j - 1].type == SegmentIntersection::INNER_HIGH); i = j + 1; } } -#undef ASSERT_OR_RETURN +#undef ASSERT_THROW + + return segs; +} + +// Connect each contour / vertical line intersection point with another two contour / vertical line intersection points. +// (fill in SegmentIntersection::{prev_on_contour, prev_on_contour_vertical, next_on_contour, next_on_contour_vertical}. +// These contour points are either on the same vertical line, or on the vertical line left / right to the current one. +static void connect_segment_intersections_by_contours(const ExPolygonWithOffset &poly_with_offset, std::vector &segs) +{ + for (size_t i_vline = 0; i_vline < segs.size(); ++ i_vline) { + SegmentedIntersectionLine &il = segs[i_vline]; + const SegmentedIntersectionLine *il_prev = i_vline > 0 ? &segs[i_vline - 1] : nullptr; + const SegmentedIntersectionLine *il_next = i_vline + 1 < segs.size() ? &segs[i_vline + 1] : nullptr; + + for (size_t i_intersection = 0; i_intersection + 1 < il.intersections.size(); ++ i_intersection) { + SegmentIntersection &itsct = il.intersections[i_intersection]; + const Polygon &poly = poly_with_offset.contour(itsct.iContour); + + // 1) Find possible connection points on the previous / next vertical line. + // Find an intersection point on iVerticalLineOther, intersecting iInnerContour + // at the same orientation as iIntersection, and being closest to iIntersection + // in the number of contour segments, when following the direction of the contour. + int iprev = -1; + if (il_prev) { + int dmin = std::numeric_limits::max(); + for (size_t i = 0; i < il_prev->intersections.size(); ++ i) { + const SegmentIntersection &itsct2 = il_prev->intersections[i]; + if (itsct.iContour == itsct2.iContour && itsct.type == itsct2.type) { + // The intersection points lie on the same contour and have the same orientation. + // Find the intersection point with a shortest path in the direction of the contour. + int d = distance_of_segmens(poly, itsct.iSegment, itsct2.iSegment, false); + if (d < dmin) { + iprev = i; + dmin = d; + } + } + } + } + int inext = -1; + if (il_next) { + int dmin = std::numeric_limits::max(); + for (size_t i = 0; i < il_next->intersections.size(); ++ i) { + const SegmentIntersection &itsct2 = il_next->intersections[i]; + if (itsct.iContour == itsct2.iContour && itsct.type == itsct2.type) { + // The intersection points lie on the same contour and have the same orientation. + // Find the intersection point with a shortest path in the direction of the contour. + int d = distance_of_segmens(poly, itsct.iSegment, itsct2.iSegment, true); + if (d < dmin) { + inext = i; + dmin = d; + } + } + } + } + + // 2) Find possible connection points on the same vertical line. + int iabove = -1; + // Does the perimeter intersect the current vertical line above intrsctn? + for (size_t i = i_intersection + 1; i + 1 < il.intersections.size(); ++ i) + if (il.intersections[i].iContour == itsct.iContour) { + iabove = i; + break; + } + // Does the perimeter intersect the current vertical line below intrsctn? + int ibelow = -1; + for (size_t i = i_intersection - 1; i > 0; -- i) + if (il.intersections[i].iContour == itsct.iContour) { + ibelow = i; + break; + } + + // 3) Sort the intersection points, clear iprev / inext / iSegBelow / iSegAbove, + // if it is preceded by any other intersection point along the contour. + // The perimeter contour orientation. + const bool forward = itsct.is_low(); // == poly_with_offset.is_contour_ccw(intrsctn->iContour); + { + int d_horiz = (iprev == -1) ? std::numeric_limits::max() : + distance_of_segmens(poly, il_prev->intersections[iprev].iSegment, itsct.iSegment, forward); + int d_down = (ibelow == -1) ? std::numeric_limits::max() : + distance_of_segmens(poly, il.intersections[ibelow].iSegment, itsct.iSegment, forward); + int d_up = (iabove == -1) ? std::numeric_limits::max() : + distance_of_segmens(poly, il.intersections[ibelow].iSegment, itsct.iSegment, forward); + if (d_horiz < std::min(d_down, d_up)) { + itsct.prev_on_contour = iprev; + itsct.prev_on_contour_type = SegmentIntersection::LinkType::Horizontal; + } else if (d_down < d_up) { + itsct.prev_on_contour = ibelow; + itsct.prev_on_contour_type = SegmentIntersection::LinkType::Down; + } else { + itsct.prev_on_contour = iabove; + itsct.prev_on_contour_type = SegmentIntersection::LinkType::Up; + } + // There should always be a link to the next intersection point on the same contour. + assert(itsct.prev_on_contour != -1); + } + { + int d_horiz = (inext == -1) ? std::numeric_limits::max() : + distance_of_segmens(poly, itsct.iSegment, il_next->intersections[inext].iSegment, forward); + int d_down = (ibelow == -1) ? std::numeric_limits::max() : + distance_of_segmens(poly, itsct.iSegment, il.intersections[ibelow].iSegment, forward); + int d_up = (iabove == -1) ? std::numeric_limits::max() : + distance_of_segmens(poly, itsct.iSegment, il.intersections[iabove].iSegment, forward); + if (d_horiz < std::min(d_down, d_up)) { + itsct.next_on_contour = inext; + itsct.next_on_contour_type = SegmentIntersection::LinkType::Horizontal; + } else if (d_down < d_up) { + itsct.next_on_contour = ibelow; + itsct.next_on_contour_type = SegmentIntersection::LinkType::Down; + } else { + itsct.next_on_contour = iabove; + itsct.next_on_contour_type = SegmentIntersection::LinkType::Up; + } + // There should always be a link to the next intersection point on the same contour. + assert(itsct.next_on_contour != -1); + } + } + } +} + +// Find the last INNER_HIGH intersection starting with INNER_LOW, that is followed by OUTER_HIGH intersection. +// Such intersection shall always exist. +static const SegmentIntersection& end_of_vertical_run_raw(const SegmentIntersection &start) +{ + assert(start.type != SegmentIntersection::INNER_LOW); + // Step back to the beginning of the vertical segment to mark it as consumed. + auto *it = &start; + do { + ++ it; + } while (it->type != SegmentIntersection::OUTER_HIGH); + if ((it - 1)->is_inner()) { + // Step back. + -- it; + assert(it->type == SegmentIntersection::INNER_HIGH); + } + return *it; +} +static SegmentIntersection& end_of_vertical_run_raw(SegmentIntersection &start) +{ + return const_cast(end_of_vertical_run_raw(std::as_const(start))); +} + +// Find the last INNER_HIGH intersection starting with INNER_LOW, that is followed by OUTER_HIGH intersection, traversing vertical up contours if enabled. +// Such intersection shall always exist. +static const SegmentIntersection& end_of_vertical_run(const SegmentedIntersectionLine &il, const SegmentIntersection &start) +{ + assert(start.type != SegmentIntersection::INNER_LOW); + const SegmentIntersection *end = &end_of_vertical_run_raw(start); + assert(end->type == SegmentIntersection::INNER_HIGH); + for (;;) { + int up = end->vertical_up(); + if (up == -1 || (end->has_left_vertical_up() ? end->prev_on_contour_quality : end->next_on_contour_quality) != SegmentIntersection::LinkQuality::Valid) + break; + const SegmentIntersection &new_start = il.intersections[up]; + assert(end->iContour == new_start.iContour); + assert(new_start.type == SegmentIntersection::INNER_LOW); + end = &end_of_vertical_run_raw(new_start); + } + assert(end->type == SegmentIntersection::INNER_HIGH); + return *end; +} +static SegmentIntersection& end_of_vertical_run(SegmentedIntersectionLine &il, SegmentIntersection &start) +{ + return const_cast(end_of_vertical_run(std::as_const(il), std::as_const(start))); +} + +static void classify_vertical_runs( + const ExPolygonWithOffset &poly_with_offset, const FillParams ¶ms, const coord_t link_max_length, + std::vector &segs, size_t i_vline) +{ + SegmentedIntersectionLine &vline = segs[i_vline]; + for (size_t i_intersection = 0; i_intersection + 1 < vline.intersections.size(); ++ i_intersection) { + if (vline.intersections[i_intersection].type == SegmentIntersection::OUTER_LOW) { + if (vline.intersections[++ i_intersection].type == SegmentIntersection::INNER_LOW) { + for (;;) { + SegmentIntersection &start = vline.intersections[i_intersection]; + SegmentIntersection &end = end_of_vertical_run_raw(start); + SegmentIntersection::LinkQuality link_quality = SegmentIntersection::LinkQuality::Valid; + // End of a contour starting at end and ending above end at the same vertical line. + int inext = end.vertical_outside(); + if (inext == -1) { + i_intersection = &end - vline.intersections.data() + 1; + break; + } + SegmentIntersection &start2 = vline.intersections[inext]; + if (params.dont_connect) + link_quality = SegmentIntersection::LinkQuality::TooLong; + else { + for (SegmentIntersection *it = &end + 1; it != &start2; ++ it) + if (it->is_inner()) { + link_quality = SegmentIntersection::LinkQuality::Invalid; + break; + } + if (link_quality == SegmentIntersection::LinkQuality::Valid && link_max_length > 0) { + // Measure length of the link. + coordf_t link_length = measure_perimeter_segment_on_vertical_line_length( + poly_with_offset, segs, i_vline, i_intersection, inext, end.has_right_vertical_outside()); + if (link_length > link_max_length) + link_quality = SegmentIntersection::LinkQuality::TooLong; + } + } + (end.has_left_vertical_up() ? end.prev_on_contour_quality : end.next_on_contour_quality) = link_quality; + (start2.has_left_vertical_down() ? start2.prev_on_contour_quality : start2.next_on_contour_quality) = link_quality; + if (link_quality != SegmentIntersection::LinkQuality::Valid) { + i_intersection = &end - vline.intersections.data() + 1; + break; + } + i_intersection = &start2 - vline.intersections.data(); + } + } else + ++ i_intersection; + } else + ++ i_intersection; + } +} + +static void classify_horizontal_links( + const ExPolygonWithOffset &poly_with_offset, const FillParams ¶ms, const coord_t link_max_length, + std::vector &segs, size_t i_vline) +{ + SegmentedIntersectionLine &vline_left = segs[i_vline]; + SegmentedIntersectionLine &vline_right = segs[i_vline + 1]; + + // Traverse both left and right together. + size_t i_intersection_left = 0; + size_t i_intersection_right = 0; + while (i_intersection_left + 1 < vline_left.intersections.size() && i_intersection_right + 1 < vline_right.intersections.size()) { + if (i_intersection_left < vline_left.intersections.size() && vline_left.intersections[i_intersection_left].type != SegmentIntersection::INNER_LOW) { + ++ i_intersection_left; + continue; + } + if (i_intersection_right < vline_right.intersections.size() && vline_right.intersections[i_intersection_right].type != SegmentIntersection::INNER_LOW) { + ++ i_intersection_right; + continue; + } + + if (i_intersection_left + 1 >= vline_left.intersections.size()) { + // Trace right only. + } else if (i_intersection_right + 1 >= vline_right.intersections.size()) { + // Trace left only. + } else { + // Trace both. + SegmentIntersection &start_left = vline_left.intersections[i_intersection_left]; + SegmentIntersection &end_left = end_of_vertical_run(vline_left, start_left); + SegmentIntersection &start_right = vline_right.intersections[i_intersection_right]; + SegmentIntersection &end_right = end_of_vertical_run(vline_right, start_right); + // Do these runs overlap? + int end_right_horizontal = end_left.right_horizontal(); + int end_left_horizontal = end_right.left_horizontal(); + if (end_right_horizontal != -1) { + if (end_right_horizontal < &start_right - vline_right.intersections.data()) { + // Left precedes the right segment. + } + } else if (end_left_horizontal != -1) { + if (end_left_horizontal < &start_left - vline_left.intersections.data()) { + // Right precedes the left segment. + } + } + } + } + +#if 0 + for (size_t i_intersection = 0; i_intersection + 1 < seg.intersections.size(); ++ i_intersection) { + if (segs.intersections[i_intersection].type == SegmentIntersection::OUTER_LOW) { + if (segs.intersections[++ i_intersection].type == SegmentIntersection::INNER_LOW) { + for (;;) { + SegmentIntersection &start = segs.intersections[i_intersection]; + SegmentIntersection &end = end_of_vertical_run_raw(start); + SegmentIntersection::LinkQuality link_quality = SegmentIntersection::LinkQuality::Valid; + // End of a contour starting at end and ending above end at the same vertical line. + int inext = end.vertical_outside(); + if (inext == -1) { + i_intersection = &end - segs.intersections.data() + 1; + break; + } + SegmentIntersection &start2 = segs.intersections[inext]; + if (params.dont_connect) + link_quality = SegmentIntersection::LinkQuality::TooLong; + else { + for (SegmentIntersection *it = &end + 1; it != &start2; ++ it) + if (it->is_inner()) { + link_quality = SegmentIntersection::LinkQuality::Invalid; + break; + } + if (link_quality == SegmentIntersection::LinkQuality::Valid && link_max_length > 0) { + // Measure length of the link. + coordf_t link_length = measure_perimeter_segment_on_vertical_line_length( + poly_with_offset, segs, i_vline, i_intersection, inext, intrsctn->has_right_vertical_outside()); + if (link_length > link_max_length) + link_quality = SegmentIntersection::LinkQuality::TooLong; + } + } + (end.has_left_vertical_up() ? end.prev_on_contour_quality : end.next_on_contour_quality) = link_quality; + (start2.has_left_vertical_down() ? start2.prev_on_contour_quality : start2.next_on_contour_quality) = link_quality; + if (link_quality != SegmentIntersection::LinkQuality::Valid) { + i_intersection = &end - segs.intersections.data() + 1; + break; + } + i_intersection = &start2 - segs.intersections.data(); + } + } else + ++ i_intersection; + } else + ++ i_intersection; + } +#endif +} + +static void disconnect_invalid_contour_links( + const ExPolygonWithOffset& poly_with_offset, const FillParams& params, const coord_t link_max_length, std::vector& segs) +{ + // Make the links symmetric! + + // Validate vertical runs including vertical contour links. + for (size_t i_vline = 0; i_vline < segs.size(); ++ i_vline) { + classify_vertical_runs(poly_with_offset, params, link_max_length, segs, i_vline); + if (i_vline > 0) + classify_horizontal_links(poly_with_offset, params, link_max_length, segs, i_vline - 1); + } +} + +static void traverse_graph_generate_polylines( + const ExPolygonWithOffset& poly_with_offset, const FillParams& params, const coord_t link_max_length, std::vector& segs, Polylines& polylines_out) +{ + // For each outer only chords, measure their maximum distance to the bow of the outer contour. + // Mark an outer only chord as consumed, if the distance is low. + for (size_t i_vline = 0; i_vline < segs.size(); ++i_vline) { + SegmentedIntersectionLine& seg = segs[i_vline]; + for (size_t i_intersection = 0; i_intersection + 1 < seg.intersections.size(); ++i_intersection) { + if (seg.intersections[i_intersection].type == SegmentIntersection::OUTER_LOW && + seg.intersections[i_intersection + 1].type == SegmentIntersection::OUTER_HIGH) { + bool consumed = false; + // if (params.full_infill()) { + // measure_outer_contour_slab(poly_with_offset, segs, i_vline, i_ntersection); + // } else + consumed = true; + seg.intersections[i_intersection].consumed_vertical_up = consumed; + } + } + } + + // Now construct a graph. + // Find the first point. + // Naively one would expect to achieve best results by chaining the paths by the shortest distance, + // but that procedure does not create the longest continuous paths. + // A simple "sweep left to right" procedure achieves better results. + size_t i_vline = 0; + size_t i_intersection = size_t(-1); + // Follow the line, connect the lines into a graph. + // Until no new line could be added to the output path: + Point pointLast; + Polyline* polyline_current = NULL; + if (!polylines_out.empty()) + pointLast = polylines_out.back().points.back(); + for (;;) { + if (i_intersection == size_t(-1)) { + // The path has been interrupted. Find a next starting point, closest to the previous extruder position. + coordf_t dist2min = std::numeric_limits().max(); + for (size_t i_vline2 = 0; i_vline2 < segs.size(); ++i_vline2) { + const SegmentedIntersectionLine& seg = segs[i_vline2]; + if (!seg.intersections.empty()) { + assert(seg.intersections.size() > 1); + // Even number of intersections with the loops. + assert((seg.intersections.size() & 1) == 0); + assert(seg.intersections.front().type == SegmentIntersection::OUTER_LOW); + for (size_t i = 0; i < seg.intersections.size(); ++i) { + const SegmentIntersection& intrsctn = seg.intersections[i]; + if (intrsctn.is_outer()) { + assert(intrsctn.is_low() || i > 0); + bool consumed = intrsctn.is_low() ? + intrsctn.consumed_vertical_up : + seg.intersections[i - 1].consumed_vertical_up; + if (!consumed) { + coordf_t dist2 = sqr(coordf_t(pointLast(0) - seg.pos)) + sqr(coordf_t(pointLast(1) - intrsctn.pos())); + if (dist2 < dist2min) { + dist2min = dist2; + i_vline = i_vline2; + i_intersection = i; + //FIXME We are taking the first left point always. Verify, that the caller chains the paths + // by a shortest distance, while reversing the paths if needed. + //if (polylines_out.empty()) + // Initial state, take the first line, which is the first from the left. + goto found; + } + } + } + } + } + } + if (i_intersection == size_t(-1)) + // We are finished. + break; + found: + // Start a new path. + polylines_out.push_back(Polyline()); + polyline_current = &polylines_out.back(); + // Emit the first point of a path. + pointLast = Point(segs[i_vline].pos, segs[i_vline].intersections[i_intersection].pos()); + polyline_current->points.push_back(pointLast); + } + + // From the initial point (i_vline, i_intersection), follow a path. + SegmentedIntersectionLine& seg = segs[i_vline]; + SegmentIntersection* intrsctn = &seg.intersections[i_intersection]; + bool going_up = intrsctn->is_low(); + bool try_connect = false; + if (going_up) { + assert(!intrsctn->consumed_vertical_up); + assert(i_intersection + 1 < seg.intersections.size()); + // Step back to the beginning of the vertical segment to mark it as consumed. + if (intrsctn->is_inner()) { + assert(i_intersection > 0); + --intrsctn; + --i_intersection; + } + // Consume the complete vertical segment up to the outer contour. + do { + intrsctn->consumed_vertical_up = true; + ++intrsctn; + ++i_intersection; + assert(i_intersection < seg.intersections.size()); + } while (intrsctn->type != SegmentIntersection::OUTER_HIGH); + if ((intrsctn - 1)->is_inner()) { + // Step back. + --intrsctn; + --i_intersection; + assert(intrsctn->type == SegmentIntersection::INNER_HIGH); + try_connect = true; + } + } else { + // Going down. + assert(intrsctn->is_high()); + assert(i_intersection > 0); + assert(!(intrsctn - 1)->consumed_vertical_up); + // Consume the complete vertical segment up to the outer contour. + if (intrsctn->is_inner()) + intrsctn->consumed_vertical_up = true; + do { + assert(i_intersection > 0); + --intrsctn; + --i_intersection; + intrsctn->consumed_vertical_up = true; + } while (intrsctn->type != SegmentIntersection::OUTER_LOW); + if ((intrsctn + 1)->is_inner()) { + // Step back. + ++intrsctn; + ++i_intersection; + assert(intrsctn->type == SegmentIntersection::INNER_LOW); + try_connect = true; + } + } + if (try_connect) { + // Decide, whether to finish the segment, or whether to follow the perimeter. + + // 1) Find possible connection points on the previous / next vertical line. + IntersectionTypeOtherVLine intrsctn_type_prev = intersection_type_on_prev_vertical_line(segs, i_vline, i_intersection); + IntersectionTypeOtherVLine intrsctn_type_next = intersection_type_on_next_vertical_line(segs, i_vline, i_intersection); + // Try to connect to a previous or next vertical line, making a zig-zag pattern. + if (intrsctn_type_prev == INTERSECTION_TYPE_OTHER_VLINE_OK || intrsctn_type_next == INTERSECTION_TYPE_OTHER_VLINE_OK) { + int iPrev = intrsctn->left_horizontal(); + int iNext = intrsctn->right_horizontal(); + coordf_t distPrev = (intrsctn_type_prev != INTERSECTION_TYPE_OTHER_VLINE_OK) ? std::numeric_limits::max() : + measure_perimeter_prev_segment_length(poly_with_offset, segs, i_vline, i_intersection, iPrev); + coordf_t distNext = (intrsctn_type_next != INTERSECTION_TYPE_OTHER_VLINE_OK) ? std::numeric_limits::max() : + measure_perimeter_next_segment_length(poly_with_offset, segs, i_vline, i_intersection, iNext); + // Take the shorter path. + //FIXME this may not be always the best strategy to take the shortest connection line now. + bool take_next = (intrsctn_type_prev == INTERSECTION_TYPE_OTHER_VLINE_OK && intrsctn_type_next == INTERSECTION_TYPE_OTHER_VLINE_OK) ? + (distNext < distPrev) : + intrsctn_type_next == INTERSECTION_TYPE_OTHER_VLINE_OK; + assert(intrsctn->is_inner()); + bool skip = params.dont_connect || (link_max_length > 0 && (take_next ? distNext : distPrev) > link_max_length); + if (skip) { + // Just skip the connecting contour and start a new path. + goto dont_connect; + polyline_current->points.push_back(Point(seg.pos, intrsctn->pos())); + polylines_out.push_back(Polyline()); + polyline_current = &polylines_out.back(); + const SegmentedIntersectionLine& il2 = segs[take_next ? (i_vline + 1) : (i_vline - 1)]; + polyline_current->points.push_back(Point(il2.pos, il2.intersections[take_next ? iNext : iPrev].pos())); + } else { + polyline_current->points.push_back(Point(seg.pos, intrsctn->pos())); + emit_perimeter_prev_next_segment(poly_with_offset, segs, i_vline, intrsctn->iContour, i_intersection, take_next ? iNext : iPrev, *polyline_current, take_next); + } + // Mark both the left and right connecting segment as consumed, because one cannot go to this intersection point as it has been consumed. + if (iPrev != -1) + segs[i_vline - 1].intersections[iPrev].consumed_perimeter_right = true; + if (iNext != -1) + intrsctn->consumed_perimeter_right = true; + //FIXME consume the left / right connecting segments at the other end of this line? Currently it is not critical because a perimeter segment is not followed if the vertical segment at the other side has already been consumed. + // Advance to the neighbor line. + if (take_next) { + ++i_vline; + i_intersection = iNext; + } + else { + --i_vline; + i_intersection = iPrev; + } + continue; + } + + // 5) Try to connect to a previous or next point on the same vertical line. + if (int inext = intrsctn->vertical_outside(); inext != -1) { + bool valid = true; + // Verify, that there is no intersection with the inner contour up to the end of the contour segment. + // Verify, that the successive segment has not been consumed yet. + if (going_up) { + if (seg.intersections[inext].consumed_vertical_up) + valid = false; + else { + for (int i = (int)i_intersection + 1; i < inext && valid; ++i) + if (seg.intersections[i].is_inner()) + valid = false; + } + } else { + if (seg.intersections[inext - 1].consumed_vertical_up) + valid = false; + else { + for (int i = inext + 1; i < (int)i_intersection && valid; ++i) + if (seg.intersections[i].is_inner()) + valid = false; + } + } + if (valid) { + const Polygon& poly = poly_with_offset.contour(intrsctn->iContour); + assert(intrsctn->iContour == seg.intersections[inext].iContour); + int iSegNext = seg.intersections[inext].iSegment; + // Skip this perimeter line? + bool skip = params.dont_connect; + bool dir_forward = intrsctn->has_right_vertical_outside(); + if (! skip && link_max_length > 0) { + coordf_t link_length = measure_perimeter_segment_on_vertical_line_length( + poly_with_offset, segs, i_vline, i_intersection, inext, dir_forward); + skip = link_length > link_max_length; + } + polyline_current->points.push_back(Point(seg.pos, intrsctn->pos())); + if (skip) { + // Just skip the connecting contour and start a new path. + polylines_out.push_back(Polyline()); + polyline_current = &polylines_out.back(); + polyline_current->points.push_back(Point(seg.pos, seg.intersections[inext].pos())); + } else { + // Consume the connecting contour and the next segment. + emit_perimeter_segment_on_vertical_line(poly_with_offset, segs, i_vline, intrsctn->iContour, i_intersection, inext, *polyline_current, dir_forward); + } + // Mark both the left and right connecting segment as consumed, because one cannot go to this intersection point as it has been consumed. + // If there are any outer intersection points skipped (bypassed) by the contour, + // mark them as processed. + if (going_up) { + for (int i = (int)i_intersection; i < inext; ++i) + seg.intersections[i].consumed_vertical_up = true; + } else { + for (int i = inext; i < (int)i_intersection; ++i) + seg.intersections[i].consumed_vertical_up = true; + } + // seg.intersections[going_up ? i_intersection : i_intersection - 1].consumed_vertical_up = true; + intrsctn->consumed_perimeter_right = true; + i_intersection = inext; + if (going_up) + ++intrsctn; + else + --intrsctn; + intrsctn->consumed_perimeter_right = true; + continue; + } + } + dont_connect: + // No way to continue the current polyline. Take the rest of the line up to the outer contour. + // This will finish the polyline, starting another polyline at a new point. + if (going_up) + ++intrsctn; + else + --intrsctn; + } + + // Finish the current vertical line, + // reset the current vertical line to pick a new starting point in the next round. + assert(intrsctn->is_outer()); + assert(intrsctn->is_high() == going_up); + pointLast = Point(seg.pos, intrsctn->pos()); + polyline_current->points.push_back(pointLast); + // Handle duplicate points and zero length segments. + polyline_current->remove_duplicate_points(); + assert(!polyline_current->has_duplicate_points()); + // Handle nearly zero length edges. + if (polyline_current->points.size() <= 1 || + (polyline_current->points.size() == 2 && + std::abs(polyline_current->points.front()(0) - polyline_current->points.back()(0)) < SCALED_EPSILON && + std::abs(polyline_current->points.front()(1) - polyline_current->points.back()(1)) < SCALED_EPSILON)) + polylines_out.pop_back(); + intrsctn = NULL; + i_intersection = -1; + polyline_current = NULL; + } +} + +struct MonotonousRegion; + +struct NextMonotonousRegion +{ + MonotonousRegion *region; + struct Path { + float length { 0 }; // Length of the link to the next region. + float visibility { 0 }; // 1 / length. Which length, just to the next region, or including the path accross the region? + float pheromone { 0 }; // <0, 1> + }; + enum Index : int { + LowLow, + LowHigh, + HighLow, + HighHigh + }; + Path paths[4]; +}; + +struct MonotonousRegion +{ + struct Boundary { + int vline; + int low; + int high; + }; + + Boundary left; + Boundary right; + + // Length when starting at left.low + double len1; + // Length when starting at left.high + double len2; + // If true, then when starting at left.low, then ending at right.high and vice versa. + // If false, then ending at the same side as starting. + bool flips; + + int left_intersection_point(bool region_flipped) const { return region_flipped ? left.high : left.low; } + int right_intersection_point(bool region_flipped) const { return (region_flipped == flips) ? right.low : right.high; } + + // Left regions are used to track whether all regions left to this one have already been printed. + boost::container::small_vector left_neighbors; + // Right regions are held to pick a next region to be extruded using the "Ant colony" heuristics. + boost::container::small_vector right_neighbors; +}; + +struct MonotonousRegionLink +{ + MonotonousRegion *region; + bool flipped; + // Distance of right side of this region to left side of the next region, if the "flipped" flag of this region and the next region + // is applied as defined. + NextMonotonousRegion::Path *next; + // Distance of right side of this region to left side of the next region, if the "flipped" flag of this region and the next region + // is applied in reverse order as if the zig-zags were flipped. + NextMonotonousRegion::Path *next_flipped; +}; + +static const SegmentIntersection& vertical_run_bottom(const SegmentedIntersectionLine &vline, const SegmentIntersection &start) +{ + assert(start.is_inner()); + const SegmentIntersection *it = &start; + // Find the lowest SegmentIntersection::INNER_LOW starting with right. + for (;;) { + while (it->type != SegmentIntersection::INNER_LOW) + -- it; + int down = it->vertical_down(); + if (down == -1 || it->vertical_down_quality() != SegmentIntersection::LinkQuality::Valid) + break; + it = &vline.intersections[down]; + } + return *it; +} +static SegmentIntersection& vertical_run_bottom(SegmentedIntersectionLine& vline, SegmentIntersection& start) +{ + return const_cast(vertical_run_bottom(std::as_const(vline), std::as_const(start))); +} + +static const SegmentIntersection& vertical_run_top(const SegmentedIntersectionLine &vline, const SegmentIntersection &start) +{ + assert(start.is_inner()); + const SegmentIntersection *it = &start; + // Find the lowest SegmentIntersection::INNER_LOW starting with right. + for (;;) { + while (it->type != SegmentIntersection::INNER_HIGH) + ++ it; + int up = it->vertical_up(); + if (up == -1 || it->vertical_up_quality() != SegmentIntersection::LinkQuality::Valid) + break; + it = &vline.intersections[up]; + } + return *it; +} +static SegmentIntersection& vertical_run_top(SegmentedIntersectionLine& vline, SegmentIntersection& start) +{ + return const_cast(vertical_run_top(std::as_const(vline), std::as_const(start))); +} + +static SegmentIntersection* left_overlap_bottom(SegmentIntersection &start, SegmentIntersection &end, SegmentedIntersectionLine &vline_left) +{ + SegmentIntersection *left = nullptr; + for (SegmentIntersection *it = &start; it <= &end; ++ it) { + int i = it->left_horizontal(); + if (i != -1) { + left = &vline_left.intersections[i]; + break; + } + } + return left == nullptr ? nullptr : &vertical_run_bottom(vline_left, *left); +} + +static SegmentIntersection* left_overlap_top(SegmentIntersection &start, SegmentIntersection &end, SegmentedIntersectionLine &vline_left) +{ + SegmentIntersection *left = nullptr; + for (SegmentIntersection *it = &end; it >= &start; -- it) { + int i = it->left_horizontal(); + if (i != -1) { + left = &vline_left.intersections[i]; + break; + } + } + return left == nullptr ? nullptr : &vertical_run_top(vline_left, *left); +} + +static std::pair left_overlap(SegmentIntersection &start, SegmentIntersection &end, SegmentedIntersectionLine &vline_left) +{ + std::pair out(nullptr, nullptr); + out.first = left_overlap_bottom(start, end, vline_left); + if (out.first != nullptr) + out.second = left_overlap_top(start, end, vline_left); + return out; +} + +static std::pair left_overlap(std::pair &start_end, SegmentedIntersectionLine &vline_left) +{ + assert((start_end.first == nullptr) == (start_end.second == nullptr)); + return start_end.first == nullptr ? start_end : left_overlap(*start_end.first, *start_end.second, vline_left); +} + +static SegmentIntersection* right_overlap_bottom(SegmentIntersection &start, SegmentIntersection &end, SegmentedIntersectionLine &vline_right) +{ + SegmentIntersection *right = nullptr; + for (SegmentIntersection *it = &start; it <= &end; ++ it) { + int i = it->right_horizontal(); + if (i != -1) { + right = &vline_right.intersections[i]; + break; + } + } + return right == nullptr ? nullptr : &vertical_run_bottom(vline_right, *right); +} + +static SegmentIntersection* right_overlap_top(SegmentIntersection &start, SegmentIntersection &end, SegmentedIntersectionLine &vline_right) +{ + SegmentIntersection *right = nullptr; + for (SegmentIntersection *it = &end; it >= &start; -- it) { + int i = it->right_horizontal(); + if (i != -1) { + right = &vline_right.intersections[i]; + break; + } + } + return right == nullptr ? nullptr : &vertical_run_top(vline_right, *right); +} + +static std::pair right_overlap(SegmentIntersection &start, SegmentIntersection &end, SegmentedIntersectionLine &vline_right) +{ + std::pair out(nullptr, nullptr); + out.first = right_overlap_bottom(start, end, vline_right); + if (out.first != nullptr) + out.second = right_overlap_top(start, end, vline_right); + return out; +} + +static std::pair right_overlap(std::pair &start_end, SegmentedIntersectionLine &vline_right) +{ + assert((start_end.first == nullptr) == (start_end.second == nullptr)); + return start_end.first == nullptr ? start_end : right_overlap(*start_end.first, *start_end.second, vline_right); +} + +static std::vector generate_montonous_regions(std::vector &segs) +{ + std::vector monotonous_regions; + + for (size_t i_vline_seed = 0; i_vline_seed < segs.size(); ++ i_vline_seed) { + SegmentedIntersectionLine &vline_seed = segs[i_vline_seed]; + for (size_t i_intersection_seed = 1; i_intersection_seed + 1 < vline_seed.intersections.size(); ) { + while (i_intersection_seed + 1 < vline_seed.intersections.size() && + vline_seed.intersections[i_intersection_seed].type != SegmentIntersection::INNER_LOW) + ++ i_intersection_seed; + SegmentIntersection *start = &vline_seed.intersections[i_intersection_seed]; + SegmentIntersection *end = &end_of_vertical_run_raw(*start); + if (! start->consumed_vertical_up) { + // Draw a new monotonous region starting with this segment. + // while there is only a single right neighbor + start->consumed_vertical_up = true; + size_t i_vline = i_vline_seed; + std::pair left(start, end); + MonotonousRegion region; + region.left.vline = i_vline; + region.left.low = left.first - vline_seed.intersections.data(); + region.left.high = left.second - vline_seed.intersections.data(); + region.right = region.left; + while (++ i_vline < segs.size()) { + SegmentedIntersectionLine &vline_left = segs[i_vline - 1]; + SegmentedIntersectionLine &vline_right = segs[i_vline]; + std::pair right = right_overlap(left, vline_right); + std::pair right_left = left_overlap(right, vline_left); + if (left != right_left) + // Left & right draws don't overlap exclusively. + break; + region.right.vline = i_vline; + region.right.low = right.first - vline_right.intersections.data(); + region.right.high = right.second - vline_right.intersections.data(); + right.first->consumed_vertical_up = true; + left = right; + } + } + i_intersection_seed = end - vline_seed.intersections.data() + 1; + } + } + + return monotonous_regions; +} + +static void connect_monotonous_regions(std::vector ®ions, std::vector &segs) +{ + // Map from low intersection to left / right side of a monotonous region. + using MapType = std::pair; + std::vector map_intersection_to_region_start; + std::vector map_intersection_to_region_end; + map_intersection_to_region_start.reserve(regions.size()); + map_intersection_to_region_end.reserve(regions.size()); + for (MonotonousRegion ®ion : regions) { + map_intersection_to_region_start.emplace_back(&segs[region.left.vline].intersections[region.left.low], ®ion); + map_intersection_to_region_end.emplace_back(&segs[region.right.vline].intersections[region.right.low], ®ion); + } + auto intersections_lower = [](const MapType &l, const MapType &r){ return l.first < r.first ; }; + auto intersections_equal = [](const MapType &l, const MapType &r){ return l.first == r.first ; }; + std::sort(map_intersection_to_region_start.begin(), map_intersection_to_region_start.end(), intersections_lower); + std::sort(map_intersection_to_region_end.begin(), map_intersection_to_region_end.end(), intersections_lower); + + // Scatter links to neighboring regions. + for (MonotonousRegion ®ion : regions) { + if (region.left.vline > 0) { + auto &vline = segs[region.left.vline]; + auto begin = &vline.intersections[region.left.low]; + auto end = &vline.intersections[region.left.high]; + for (;;) { + MapType key(begin, nullptr); + auto it = std::lower_bound(map_intersection_to_region_end.begin(), map_intersection_to_region_end.end(), key); + assert(it != map_intersection_to_region_end.end() && it->first == key.first); + NextMonotonousRegion next_region{ ®ion }; + it->second->right_neighbors.emplace_back(next_region); + SegmentIntersection *next = &vertical_run_top(vline, *begin); + if (next == end) + break; + while (next->type != SegmentIntersection::INNER_LOW) + ++ next; + begin = next; + } + } + if (region.right.vline + 1 < segs.size()) { + auto &vline = segs[region.right.vline]; + auto begin = &vline.intersections[region.right.low]; + auto end = &vline.intersections[region.right.high]; + for (;;) { + MapType key(begin, nullptr); + auto it = std::lower_bound(map_intersection_to_region_start.begin(), map_intersection_to_region_start.end(), key); + assert(it != map_intersection_to_region_start.end() && it->first == key.first); + it->second->left_neighbors.emplace_back(®ion); + SegmentIntersection *next = &vertical_run_top(vline, *begin); + if (next == end) + break; + while (next->type != SegmentIntersection::INNER_LOW) + ++ next; + begin = next; + } + } + } +} + +// Raad Salman: Algorithms for the Precedence Constrained Generalized Travelling Salesperson Problem +// https://www.chalmers.se/en/departments/math/research/research-groups/optimization/OptimizationMasterTheses/MScThesis-RaadSalman-final.pdf +// Algorithm 6.1 Lexicographic Path Preserving 3-opt +// Optimize path while maintaining the ordering constraints. +void monotonous_3_opt(std::vector &path, std::vector &segs) +{ + // When doing the 3-opt path preserving flips, one has to fulfill two constraints: + // + // 1) The new path should be shorter than the old path. + // 2) The precedence constraints shall be satisified on the new path. + // + // Branch & bound with KD-tree may be used with the shorter path constraint, but the precedence constraint will have to be recalculated for each + // shorter path candidate found, which has a quadratic cost for a dense precedence graph. For a sparse precedence graph the precedence + // constraint verification will be cheaper. + // + // On the other side, if the full search space is traversed as in the diploma thesis by Raad Salman (page 24, Algorithm 6.1 Lexicographic Path Preserving 3-opt), + // then the precedence constraint verification is amortized inside the O(n^3) loop. Now which is better for our task? + // + // It is beneficial to also try flipping of the infill zig-zags, for which a prefix sum of both flipped and non-flipped paths over + // MonotonousRegionLinks may be utilized, however updating the prefix sum has a linear complexity, the same complexity as doing the 3-opt + // exchange by copying the pieces. +} + +// Find a run through monotonous infill blocks using an 'Ant colony" optimization method. +static std::vector chain_monotonous_regions( + std::vector ®ions, std::vector &segs, std::mt19937_64 &rng) +{ + // Start point of a region (left) given the direction of the initial infill line. + auto region_start_point = [&segs](const MonotonousRegion ®ion, bool dir) { + SegmentedIntersectionLine &vline = segs[region.left.vline]; + SegmentIntersection &ipt = vline.intersections[dir ? region.left.high : region.left.low]; + return Vec2f(float(vline.pos), float(ipt.pos())); + }; + // End point of a region (right) given the direction of the initial infill line and whether the monotonous run contains + // even or odd number of vertical lines. + auto region_end_point = [&segs](const MonotonousRegion ®ion, bool dir) { + SegmentedIntersectionLine &vline = segs[region.right.vline]; + SegmentIntersection &ipt = vline.intersections[(dir == region.flips) ? region.right.low : region.right.high]; + return Vec2f(float(vline.pos), float(ipt.pos())); + }; + + // Number of left neighbors (regions that this region depends on, this region cannot be printed before the regions left of it are printed). + std::vector left_neighbors_unprocessed(regions.size(), 0); + // Queue of regions, which have their left neighbors already printed. + std::vector queue; + queue.reserve(regions.size()); + for (MonotonousRegion ®ion : regions) + if (region.left_neighbors.empty()) + queue.emplace_back(®ion); + else + left_neighbors_unprocessed[®ion - regions.data()] = region.left_neighbors.size(); + // Make copy of structures that need to be initialized at each ant iteration. + auto left_neighbors_unprocessed_initial = left_neighbors_unprocessed; + auto queue_initial = queue; + + std::vector path, best_path; + path.reserve(regions.size()); + best_path.reserve(regions.size()); + float best_path_length = std::numeric_limits::max(); + + struct NextCandidate { + NextMonotonousRegion *region; + NextMonotonousRegion::Path *link; + NextMonotonousRegion::Path *link_flipped; + float cost; + bool dir; + }; + std::vector next_candidates; + + // How many times to repeat the ant simulation. + constexpr int num_runs = 10; + // With how many ants each of the run will be performed? + constexpr int num_ants = 10; + // Base (initial) pheromone level. + constexpr float pheromone_initial_deposit = 0.5f; + // Evaporation rate of pheromones. + constexpr float pheromone_evaporation = 0.1f; + // Probability at which to take the next best path. Otherwise take the the path based on the cost distribution. + constexpr float probability_take_best = 0.9f; + // Exponents of the cost function. + constexpr float pheromone_alpha = 1.f; // pheromone exponent + constexpr float pheromone_beta = 2.f; // attractiveness weighted towards edge length + // Cost of traversing a link between two monotonous regions. + auto path_cost = [pheromone_alpha, pheromone_beta](NextMonotonousRegion::Path &path) { + return pow(path.pheromone, pheromone_alpha) * pow(path.visibility, pheromone_beta); + }; + for (int run = 0; run < num_runs; ++ run) + { + for (int ant = 0; ant < num_ants; ++ ant) + { + // Find a new path following the pheromones deposited by the previous ants. + path.clear(); + queue = queue_initial; + left_neighbors_unprocessed = left_neighbors_unprocessed_initial; + while (! queue.empty()) { + // Sort the queue by distance to the last point. + // Take a candidate based on shortest distance? or ant colony? + if (path.empty()) { + // Pick randomly the first from the queue at random orientation. + int first_idx = std::uniform_int_distribution<>(0, int(queue.size()) - 1)(rng); + path.emplace_back(MonotonousRegionLink{ queue[first_idx], rng() > rng.max() / 2 }); + *(queue.begin() + first_idx) = std::move(queue.back()); + queue.pop_back(); + } else { + // Pick the closest neighbor from the queue? + } + -- left_neighbors_unprocessed[path.back().region - regions.data()]; + while (! path.back().region->right_neighbors.empty()) { + // Chain. + MonotonousRegion ®ion = *path.back().region; + bool dir = path.back().flipped; + Vec2f end_pt = region_end_point(region, dir); + // Sort by distance to pt. + next_candidates.reserve(region.right_neighbors.size() * 2); + for (NextMonotonousRegion &next : region.right_neighbors) { + int unprocessed = left_neighbors_unprocessed[next.region - regions.data()]; + assert(unprocessed > 0); + if (unprocessed == 1) { + // Dependencies of the successive blocks are satisfied. + bool flip = dir == region.flips; + auto path_cost = [pheromone_alpha, pheromone_beta](NextMonotonousRegion::Path& path) { + return pow(path.pheromone, pheromone_alpha) * pow(path.visibility, pheromone_beta); + }; + NextMonotonousRegion::Path &path_low = next.paths[flip ? NextMonotonousRegion::HighLow : NextMonotonousRegion::LowLow]; + NextMonotonousRegion::Path &path_low_flipped = next.paths[flip ? NextMonotonousRegion::LowHigh : NextMonotonousRegion::HighHigh]; + NextMonotonousRegion::Path &path_high = next.paths[flip ? NextMonotonousRegion::HighHigh : NextMonotonousRegion::LowHigh]; + NextMonotonousRegion::Path &path_high_flipped = next.paths[flip ? NextMonotonousRegion::LowLow : NextMonotonousRegion::HighLow]; + next_candidates.emplace_back(NextCandidate{ &next, &path_low, &path_low_flipped, path_cost(path_low), false }); + next_candidates.emplace_back(NextCandidate{ &next, &path_high, &path_high_flipped, path_cost(path_high), true }); + } + } + //std::sort(next_candidates.begin(), next_candidates.end(), [](const auto &l, const auto &r) { l.dist < r.dist; }); + float dice = float(rng()) / float(rng.max()); + std::vector::iterator take_path; + if (dice < probability_take_best) { + // Take the lowest cost path. + take_path = std::min_element(next_candidates.begin(), next_candidates.end(), [](auto &l, auto &r){ return l.cost < r.cost; }); + } else { + // Take the path based on the cost. + // Calculate the total cost. + float total_cost = std::accumulate(next_candidates.begin(), next_candidates.end(), 0.f, [](const float l, const NextCandidate& r) { return l + r.cost; }); + // Take a random path based on the cost. + float cost_threshold = floor(float(rng()) * total_cost / float(rng.max())); + take_path = next_candidates.end(); + -- take_path; + for (auto it = next_candidates.begin(); it < next_candidates.end(); ++ it) + if (cost_threshold -= it->cost <= 0.) { + take_path = it; + break; + } + } + // Extend the path. + NextMonotonousRegion &next_region = *take_path->region; + bool next_dir = take_path->dir; + path.back().next = take_path->link; + path.back().next_flipped = take_path->link_flipped; + path.emplace_back(MonotonousRegionLink{ next_region.region, next_dir }); + // Decrease the number of next block dependencies. + -- left_neighbors_unprocessed[next_region.region - regions.data()]; + // Update pheromones along this link. + take_path->link->pheromone = (1.f - pheromone_evaporation) * take_path->link->pheromone + pheromone_evaporation * pheromone_initial_deposit; + } + } + + // Perform 3-opt local optimization of the path. + monotonous_3_opt(path, segs); + + // Measure path length. + float path_length = std::accumulate(path.begin(), path.end(), 0.f, [](const float l, const MonotonousRegionLink& r) { return l + r.next->length; }); + // Save the shortest path. + if (path_length < best_path_length) { + best_path_length = path_length; + std::swap(best_path_length, path_length); + } + } + + // Reinforce the path feromones with the best path. + float total_cost = best_path_length; + for (MonotonousRegionLink &link : path) + link.next->pheromone = (1.f - pheromone_evaporation) * link.next->pheromone + pheromone_evaporation / total_cost; + } + + return best_path; +} + +// Traverse path, produce polylines. +static void polylines_from_paths(const std::vector &path, const ExPolygonWithOffset &poly_with_offset, const std::vector &segs, Polylines &polylines_out) +{ + Polyline *polyline = nullptr; + auto finish_polyline = [&polyline, &polylines_out]() { + polyline->remove_duplicate_points(); + // Handle duplicate points and zero length segments. + assert(!polyline->has_duplicate_points()); + // Handle nearly zero length edges. + if (polyline->points.size() <= 1 || + (polyline->points.size() == 2 && + std::abs(polyline->points.front()(0) - polyline->points.back()(0)) < SCALED_EPSILON && + std::abs(polyline->points.front()(1) - polyline->points.back()(1)) < SCALED_EPSILON)) + polylines_out.pop_back(); + polyline = nullptr; + }; + + for (const MonotonousRegionLink &path_segment : path) { + MonotonousRegion ®ion = *path_segment.region; + bool dir = path_segment.flipped; + + // From the initial point (i_vline, i_intersection), follow a path. + int i_intersection = region.left_intersection_point(dir); + int i_vline = region.left.vline; + + if (polyline != nullptr && &path_segment != path.data()) { + // Connect previous path segment with the new one. + const MonotonousRegionLink &path_segment_prev = *(&path_segment - 1); + const MonotonousRegion ®ion_prev = *path_segment_prev.region; + bool dir_prev = path_segment_prev.flipped; + int i_vline_prev = region_prev.right.vline; + const SegmentedIntersectionLine &vline_prev = segs[i_vline_prev]; + int i_intersection_prev = region_prev.right_intersection_point(dir_prev); + const SegmentIntersection *ip_prev = &vline_prev.intersections[i_intersection_prev]; + bool extended = false; + if (i_vline_prev + 1 == i_vline) { + if (ip_prev->right_horizontal() == i_intersection && ip_prev->next_on_contour_quality == SegmentIntersection::LinkQuality::Valid) { + // Emit a horizontal connection contour. + emit_perimeter_prev_next_segment(poly_with_offset, segs, i_vline_prev, ip_prev->iContour, i_intersection_prev, i_intersection, *polyline, true); + extended = true; + } + } + if (! extended) { + // Finish the current vertical line, + assert(ip_prev->is_inner()); + ip_prev->is_low() ? -- ip_prev : ++ ip_prev; + assert(ip_prev->is_outer()); + polyline->points.back() = Point(vline_prev.pos, ip_prev->pos()); + finish_polyline(); + } + } + + for (;;) { + const SegmentedIntersectionLine &seg = segs[i_vline]; + const SegmentIntersection *intrsctn = &seg.intersections[i_intersection]; + const bool going_up = intrsctn->is_low(); + if (polyline == nullptr) { + polylines_out.emplace_back(); + polyline = &polylines_out.back(); + // Extend the infill line up to the outer contour. + polyline->points.emplace_back(seg.pos, (intrsctn + (going_up ? - 1 : 1))->pos()); + } else + polyline->points.emplace_back(seg.pos, intrsctn->pos()); + + int iright = intrsctn->right_horizontal(); + if (going_up) { + // Consume the complete vertical segment up to the inner contour. + for (;;) { + do { + ++ intrsctn; + iright = std::max(iright, intrsctn->right_horizontal()); + } while (intrsctn->type != SegmentIntersection::INNER_HIGH); + polyline->points.emplace_back(seg.pos, intrsctn->pos()); + int inext = intrsctn->vertical_up(); + if (inext == -1) + break; + const Polygon &poly = poly_with_offset.contour(intrsctn->iContour); + assert(intrsctn->iContour == seg.intersections[inext].iContour); + emit_perimeter_segment_on_vertical_line(poly_with_offset, segs, i_vline, intrsctn->iContour, i_intersection, inext, *polyline, intrsctn->has_right_vertical_up()); + intrsctn = seg.intersections.data() + inext; + } + } else { + // Going down. + assert(intrsctn->is_high()); + assert(i_intersection > 0); + for (;;) { + do { + -- intrsctn; + if (int iright_new = intrsctn->right_horizontal(); iright_new != -1) + iright = iright_new; + } while (intrsctn->type != SegmentIntersection::INNER_LOW); + polyline->points.emplace_back(seg.pos, intrsctn->pos()); + int inext = intrsctn->vertical_down(); + if (inext == -1) + break; + const Polygon &poly = poly_with_offset.contour(intrsctn->iContour); + assert(intrsctn->iContour == seg.intersections[inext].iContour); + emit_perimeter_segment_on_vertical_line(poly_with_offset, segs, i_vline, intrsctn->iContour, intrsctn - seg.intersections.data(), inext, *polyline, intrsctn->has_right_vertical_down()); + intrsctn = seg.intersections.data() + inext; + } + } + + if (i_vline == region.right.vline) + break; + + int inext = intrsctn->right_horizontal(); + if (inext != -1 && intrsctn->next_on_contour_quality == SegmentIntersection::LinkQuality::Valid) { + // Emit a horizontal connection contour. + emit_perimeter_prev_next_segment(poly_with_offset, segs, i_vline, intrsctn->iContour, intrsctn - seg.intersections.data(), inext, *polyline, true); + i_intersection = inext; + } else { + // Finish the current vertical line, + going_up ? ++ intrsctn : -- intrsctn; + assert(intrsctn->is_outer()); + assert(intrsctn->is_high() == going_up); + polyline->points.back() = Point(seg.pos, intrsctn->pos()); + finish_polyline(); + if (inext == -1) { + // Find the end of the next overlapping vertical segment. + const SegmentedIntersectionLine &vline_right = segs[i_vline + 1]; + const SegmentIntersection *right = going_up ? + &vertical_run_top(vline_right, vline_right.intersections[iright]) : &vertical_run_bottom(vline_right, vline_right.intersections[iright]); + i_intersection = right - vline_right.intersections.data(); + } else + i_intersection = inext; + } + + ++ i_vline; + } + } +} + +bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillParams ¶ms, float angleBase, float pattern_shift, Polylines &polylines_out) +{ + // At the end, only the new polylines will be rotated back. + size_t n_polylines_out_initial = polylines_out.size(); + + // Shrink the input polygon a bit first to not push the infill lines out of the perimeters. +// const float INFILL_OVERLAP_OVER_SPACING = 0.3f; + const float INFILL_OVERLAP_OVER_SPACING = 0.45f; + assert(INFILL_OVERLAP_OVER_SPACING > 0 && INFILL_OVERLAP_OVER_SPACING < 0.5f); + + // Rotate polygons so that we can work with vertical lines here + std::pair rotate_vector = this->_infill_direction(surface); + rotate_vector.first += angleBase; + + assert(params.density > 0.0001f && params.density <= 1.f); + coord_t line_spacing = coord_t(scale_(this->spacing) / params.density); + + // On the polygons of poly_with_offset, the infill lines will be connected. + ExPolygonWithOffset poly_with_offset( + surface->expolygon, + - rotate_vector.first, + scale_(this->overlap - (0.5 - INFILL_OVERLAP_OVER_SPACING) * this->spacing), + scale_(this->overlap - 0.5 * this->spacing)); + if (poly_with_offset.n_contours_inner == 0) { + // Not a single infill line fits. + //FIXME maybe one shall trigger the gap fill here? + return true; + } + + BoundingBox bounding_box = poly_with_offset.bounding_box_src(); + + // define flow spacing according to requested density + if (params.full_infill() && !params.dont_adjust) { + line_spacing = this->_adjust_solid_spacing(bounding_box.size()(0), line_spacing); + this->spacing = unscale(line_spacing); + } else { + // extend bounding box so that our pattern will be aligned with other layers + // Transform the reference point to the rotated coordinate system. + Point refpt = rotate_vector.second.rotated(- rotate_vector.first); + // _align_to_grid will not work correctly with positive pattern_shift. + coord_t pattern_shift_scaled = coord_t(scale_(pattern_shift)) % line_spacing; + refpt(0) -= (pattern_shift_scaled >= 0) ? pattern_shift_scaled : (line_spacing + pattern_shift_scaled); + bounding_box.merge(_align_to_grid( + bounding_box.min, + Point(line_spacing, line_spacing), + refpt)); + } + + // Intersect a set of euqally spaced vertical lines wiht expolygon. + // n_vlines = ceil(bbox_width / line_spacing) + size_t n_vlines = (bounding_box.max(0) - bounding_box.min(0) + line_spacing - 1) / line_spacing; + coord_t x0 = bounding_box.min(0); + if (params.full_infill()) + x0 += (line_spacing + SCALED_EPSILON) / 2; + +#ifdef SLIC3R_DEBUG + static int iRun = 0; + BoundingBox bbox_svg = poly_with_offset.bounding_box_outer(); + ::Slic3r::SVG svg(debug_out_path("FillRectilinear2-%d.svg", iRun), bbox_svg); // , scale_(1.)); + poly_with_offset.export_to_svg(svg); + { + ::Slic3r::SVG svg(debug_out_path("FillRectilinear2-initial-%d.svg", iRun), bbox_svg); // , scale_(1.)); + poly_with_offset.export_to_svg(svg); + } + iRun ++; +#endif /* SLIC3R_DEBUG */ + + std::vector segs = slice_region_by_vertical_lines(poly_with_offset, n_vlines, x0, line_spacing); + connect_segment_intersections_by_contours(poly_with_offset, segs); #ifdef SLIC3R_DEBUG // Paint the segments and finalize the SVG file. @@ -1018,352 +2224,15 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillP svg.Close(); #endif /* SLIC3R_DEBUG */ - // For each outer only chords, measure their maximum distance to the bow of the outer contour. - // Mark an outer only chord as consumed, if the distance is low. - for (size_t i_vline = 0; i_vline < segs.size(); ++ i_vline) { - SegmentedIntersectionLine &seg = segs[i_vline]; - for (size_t i_intersection = 0; i_intersection + 1 < seg.intersections.size(); ++ i_intersection) { - if (seg.intersections[i_intersection].type == SegmentIntersection::OUTER_LOW && - seg.intersections[i_intersection+1].type == SegmentIntersection::OUTER_HIGH) { - bool consumed = false; -// if (params.full_infill()) { -// measure_outer_contour_slab(poly_with_offset, segs, i_vline, i_ntersection); -// } else - consumed = true; - seg.intersections[i_intersection].consumed_vertical_up = consumed; - } - } - } - - // Now construct a graph. - // Find the first point. - // Naively one would expect to achieve best results by chaining the paths by the shortest distance, - // but that procedure does not create the longest continuous paths. - // A simple "sweep left to right" procedure achieves better results. - size_t i_vline = 0; - size_t i_intersection = size_t(-1); - // Follow the line, connect the lines into a graph. - // Until no new line could be added to the output path: - Point pointLast; - Polyline *polyline_current = NULL; - if (! polylines_out.empty()) - pointLast = polylines_out.back().points.back(); - for (;;) { - if (i_intersection == size_t(-1)) { - // The path has been interrupted. Find a next starting point, closest to the previous extruder position. - coordf_t dist2min = std::numeric_limits().max(); - for (size_t i_vline2 = 0; i_vline2 < segs.size(); ++ i_vline2) { - const SegmentedIntersectionLine &seg = segs[i_vline2]; - if (! seg.intersections.empty()) { - assert(seg.intersections.size() > 1); - // Even number of intersections with the loops. - assert((seg.intersections.size() & 1) == 0); - assert(seg.intersections.front().type == SegmentIntersection::OUTER_LOW); - for (size_t i = 0; i < seg.intersections.size(); ++ i) { - const SegmentIntersection &intrsctn = seg.intersections[i]; - if (intrsctn.is_outer()) { - assert(intrsctn.is_low() || i > 0); - bool consumed = intrsctn.is_low() ? - intrsctn.consumed_vertical_up : - seg.intersections[i-1].consumed_vertical_up; - if (! consumed) { - coordf_t dist2 = sqr(coordf_t(pointLast(0) - seg.pos)) + sqr(coordf_t(pointLast(1) - intrsctn.pos())); - if (dist2 < dist2min) { - dist2min = dist2; - i_vline = i_vline2; - i_intersection = i; - //FIXME We are taking the first left point always. Verify, that the caller chains the paths - // by a shortest distance, while reversing the paths if needed. - //if (polylines_out.empty()) - // Initial state, take the first line, which is the first from the left. - goto found; - } - } - } - } - } - } - if (i_intersection == size_t(-1)) - // We are finished. - break; - found: - // Start a new path. - polylines_out.push_back(Polyline()); - polyline_current = &polylines_out.back(); - // Emit the first point of a path. - pointLast = Point(segs[i_vline].pos, segs[i_vline].intersections[i_intersection].pos()); - polyline_current->points.push_back(pointLast); - } - - // From the initial point (i_vline, i_intersection), follow a path. - SegmentedIntersectionLine &seg = segs[i_vline]; - SegmentIntersection *intrsctn = &seg.intersections[i_intersection]; - bool going_up = intrsctn->is_low(); - bool try_connect = false; - if (going_up) { - assert(! intrsctn->consumed_vertical_up); - assert(i_intersection + 1 < seg.intersections.size()); - // Step back to the beginning of the vertical segment to mark it as consumed. - if (intrsctn->is_inner()) { - assert(i_intersection > 0); - -- intrsctn; - -- i_intersection; - } - // Consume the complete vertical segment up to the outer contour. - do { - intrsctn->consumed_vertical_up = true; - ++ intrsctn; - ++ i_intersection; - assert(i_intersection < seg.intersections.size()); - } while (intrsctn->type != SegmentIntersection::OUTER_HIGH); - if ((intrsctn - 1)->is_inner()) { - // Step back. - -- intrsctn; - -- i_intersection; - assert(intrsctn->type == SegmentIntersection::INNER_HIGH); - try_connect = true; - } - } else { - // Going down. - assert(intrsctn->is_high()); - assert(i_intersection > 0); - assert(! (intrsctn - 1)->consumed_vertical_up); - // Consume the complete vertical segment up to the outer contour. - if (intrsctn->is_inner()) - intrsctn->consumed_vertical_up = true; - do { - assert(i_intersection > 0); - -- intrsctn; - -- i_intersection; - intrsctn->consumed_vertical_up = true; - } while (intrsctn->type != SegmentIntersection::OUTER_LOW); - if ((intrsctn + 1)->is_inner()) { - // Step back. - ++ intrsctn; - ++ i_intersection; - assert(intrsctn->type == SegmentIntersection::INNER_LOW); - try_connect = true; - } - } - if (try_connect) { - // Decide, whether to finish the segment, or whether to follow the perimeter. - - // 1) Find possible connection points on the previous / next vertical line. - int iPrev = intersection_on_prev_vertical_line(poly_with_offset, segs, i_vline, intrsctn->iContour, i_intersection); - int iNext = intersection_on_next_vertical_line(poly_with_offset, segs, i_vline, intrsctn->iContour, i_intersection); - IntersectionTypeOtherVLine intrsctn_type_prev = intersection_type_on_prev_vertical_line(segs, i_vline, i_intersection, iPrev); - IntersectionTypeOtherVLine intrsctn_type_next = intersection_type_on_next_vertical_line(segs, i_vline, i_intersection, iNext); - - // 2) Find possible connection points on the same vertical line. - int iAbove = -1; - int iBelow = -1; - int iSegAbove = -1; - int iSegBelow = -1; - { -// SegmentIntersection::SegmentIntersectionType type_crossing = (intrsctn->type == SegmentIntersection::INNER_LOW) ? -// SegmentIntersection::INNER_HIGH : SegmentIntersection::INNER_LOW; - // Does the perimeter intersect the current vertical line above intrsctn? - for (size_t i = i_intersection + 1; i + 1 < seg.intersections.size(); ++ i) -// if (seg.intersections[i].iContour == intrsctn->iContour && seg.intersections[i].type == type_crossing) { - if (seg.intersections[i].iContour == intrsctn->iContour) { - iAbove = i; - iSegAbove = seg.intersections[i].iSegment; - break; - } - // Does the perimeter intersect the current vertical line below intrsctn? - for (size_t i = i_intersection - 1; i > 0; -- i) -// if (seg.intersections[i].iContour == intrsctn->iContour && seg.intersections[i].type == type_crossing) { - if (seg.intersections[i].iContour == intrsctn->iContour) { - iBelow = i; - iSegBelow = seg.intersections[i].iSegment; - break; - } - } - - // 3) Sort the intersection points, clear iPrev / iNext / iSegBelow / iSegAbove, - // if it is preceded by any other intersection point along the contour. - unsigned int vert_seg_dir_valid_mask = - (going_up ? - (iSegAbove != -1 && seg.intersections[iAbove].type == SegmentIntersection::INNER_LOW) : - (iSegBelow != -1 && seg.intersections[iBelow].type == SegmentIntersection::INNER_HIGH)) ? - (DIR_FORWARD | DIR_BACKWARD) : - 0; - { - // Invalidate iPrev resp. iNext, if the perimeter crosses the current vertical line earlier than iPrev resp. iNext. - // The perimeter contour orientation. - const bool forward = intrsctn->is_low(); // == poly_with_offset.is_contour_ccw(intrsctn->iContour); - const Polygon &poly = poly_with_offset.contour(intrsctn->iContour); - { - int d_horiz = (iPrev == -1) ? std::numeric_limits::max() : - distance_of_segmens(poly, segs[i_vline-1].intersections[iPrev].iSegment, intrsctn->iSegment, forward); - int d_down = (iSegBelow == -1) ? std::numeric_limits::max() : - distance_of_segmens(poly, iSegBelow, intrsctn->iSegment, forward); - int d_up = (iSegAbove == -1) ? std::numeric_limits::max() : - distance_of_segmens(poly, iSegAbove, intrsctn->iSegment, forward); - if (intrsctn_type_prev == INTERSECTION_TYPE_OTHER_VLINE_OK && d_horiz > std::min(d_down, d_up)) - // The vertical crossing comes eralier than the prev crossing. - // Disable the perimeter going back. - intrsctn_type_prev = INTERSECTION_TYPE_OTHER_VLINE_NOT_FIRST; - if (going_up ? (d_up > std::min(d_horiz, d_down)) : (d_down > std::min(d_horiz, d_up))) - // The horizontal crossing comes earlier than the vertical crossing. - vert_seg_dir_valid_mask &= ~(forward ? DIR_BACKWARD : DIR_FORWARD); - } - { - int d_horiz = (iNext == -1) ? std::numeric_limits::max() : - distance_of_segmens(poly, intrsctn->iSegment, segs[i_vline+1].intersections[iNext].iSegment, forward); - int d_down = (iSegBelow == -1) ? std::numeric_limits::max() : - distance_of_segmens(poly, intrsctn->iSegment, iSegBelow, forward); - int d_up = (iSegAbove == -1) ? std::numeric_limits::max() : - distance_of_segmens(poly, intrsctn->iSegment, iSegAbove, forward); - if (intrsctn_type_next == INTERSECTION_TYPE_OTHER_VLINE_OK && d_horiz > std::min(d_down, d_up)) - // The vertical crossing comes eralier than the prev crossing. - // Disable the perimeter going forward. - intrsctn_type_next = INTERSECTION_TYPE_OTHER_VLINE_NOT_FIRST; - if (going_up ? (d_up > std::min(d_horiz, d_down)) : (d_down > std::min(d_horiz, d_up))) - // The horizontal crossing comes earlier than the vertical crossing. - vert_seg_dir_valid_mask &= ~(forward ? DIR_FORWARD : DIR_BACKWARD); - } - } - - // 4) Try to connect to a previous or next vertical line, making a zig-zag pattern. - if (intrsctn_type_prev == INTERSECTION_TYPE_OTHER_VLINE_OK || intrsctn_type_next == INTERSECTION_TYPE_OTHER_VLINE_OK) { - coordf_t distPrev = (intrsctn_type_prev != INTERSECTION_TYPE_OTHER_VLINE_OK) ? std::numeric_limits::max() : - measure_perimeter_prev_segment_length(poly_with_offset, segs, i_vline, intrsctn->iContour, i_intersection, iPrev); - coordf_t distNext = (intrsctn_type_next != INTERSECTION_TYPE_OTHER_VLINE_OK) ? std::numeric_limits::max() : - measure_perimeter_next_segment_length(poly_with_offset, segs, i_vline, intrsctn->iContour, i_intersection, iNext); - // Take the shorter path. - //FIXME this may not be always the best strategy to take the shortest connection line now. - bool take_next = (intrsctn_type_prev == INTERSECTION_TYPE_OTHER_VLINE_OK && intrsctn_type_next == INTERSECTION_TYPE_OTHER_VLINE_OK) ? - (distNext < distPrev) : - intrsctn_type_next == INTERSECTION_TYPE_OTHER_VLINE_OK; - assert(intrsctn->is_inner()); - bool skip = params.dont_connect || (link_max_length > 0 && (take_next ? distNext : distPrev) > link_max_length); - if (skip) { - // Just skip the connecting contour and start a new path. - goto dont_connect; - polyline_current->points.push_back(Point(seg.pos, intrsctn->pos())); - polylines_out.push_back(Polyline()); - polyline_current = &polylines_out.back(); - const SegmentedIntersectionLine &il2 = segs[take_next ? (i_vline + 1) : (i_vline - 1)]; - polyline_current->points.push_back(Point(il2.pos, il2.intersections[take_next ? iNext : iPrev].pos())); - } else { - polyline_current->points.push_back(Point(seg.pos, intrsctn->pos())); - emit_perimeter_prev_next_segment(poly_with_offset, segs, i_vline, intrsctn->iContour, i_intersection, take_next ? iNext : iPrev, *polyline_current, take_next); - } - // Mark both the left and right connecting segment as consumed, because one cannot go to this intersection point as it has been consumed. - if (iPrev != -1) - segs[i_vline-1].intersections[iPrev].consumed_perimeter_right = true; - if (iNext != -1) - intrsctn->consumed_perimeter_right = true; - //FIXME consume the left / right connecting segments at the other end of this line? Currently it is not critical because a perimeter segment is not followed if the vertical segment at the other side has already been consumed. - // Advance to the neighbor line. - if (take_next) { - ++ i_vline; - i_intersection = iNext; - } else { - -- i_vline; - i_intersection = iPrev; - } - continue; - } - - // 5) Try to connect to a previous or next point on the same vertical line. - if (vert_seg_dir_valid_mask) { - bool valid = true; - // Verify, that there is no intersection with the inner contour up to the end of the contour segment. - // Verify, that the successive segment has not been consumed yet. - if (going_up) { - if (seg.intersections[iAbove].consumed_vertical_up) { - valid = false; - } else { - for (int i = (int)i_intersection + 1; i < iAbove && valid; ++i) - if (seg.intersections[i].is_inner()) - valid = false; - } - } else { - if (seg.intersections[iBelow-1].consumed_vertical_up) { - valid = false; - } else { - for (int i = iBelow + 1; i < (int)i_intersection && valid; ++i) - if (seg.intersections[i].is_inner()) - valid = false; - } - } - if (valid) { - const Polygon &poly = poly_with_offset.contour(intrsctn->iContour); - int iNext = going_up ? iAbove : iBelow; - int iSegNext = going_up ? iSegAbove : iSegBelow; - bool dir_forward = (vert_seg_dir_valid_mask == (DIR_FORWARD | DIR_BACKWARD)) ? - // Take the shorter length between the current and the next intersection point. - (distance_of_segmens(poly, intrsctn->iSegment, iSegNext, true) < - distance_of_segmens(poly, intrsctn->iSegment, iSegNext, false)) : - (vert_seg_dir_valid_mask == DIR_FORWARD); - // Skip this perimeter line? - bool skip = params.dont_connect; - if (! skip && link_max_length > 0) { - coordf_t link_length = measure_perimeter_segment_on_vertical_line_length( - poly_with_offset, segs, i_vline, intrsctn->iContour, i_intersection, iNext, dir_forward); - skip = link_length > link_max_length; - } - polyline_current->points.push_back(Point(seg.pos, intrsctn->pos())); - if (skip) { - // Just skip the connecting contour and start a new path. - polylines_out.push_back(Polyline()); - polyline_current = &polylines_out.back(); - polyline_current->points.push_back(Point(seg.pos, seg.intersections[iNext].pos())); - } else { - // Consume the connecting contour and the next segment. - emit_perimeter_segment_on_vertical_line(poly_with_offset, segs, i_vline, intrsctn->iContour, i_intersection, iNext, *polyline_current, dir_forward); - } - // Mark both the left and right connecting segment as consumed, because one cannot go to this intersection point as it has been consumed. - // If there are any outer intersection points skipped (bypassed) by the contour, - // mark them as processed. - if (going_up) { - for (int i = (int)i_intersection; i < iAbove; ++ i) - seg.intersections[i].consumed_vertical_up = true; - } else { - for (int i = iBelow; i < (int)i_intersection; ++ i) - seg.intersections[i].consumed_vertical_up = true; - } -// seg.intersections[going_up ? i_intersection : i_intersection - 1].consumed_vertical_up = true; - intrsctn->consumed_perimeter_right = true; - i_intersection = iNext; - if (going_up) - ++ intrsctn; - else - -- intrsctn; - intrsctn->consumed_perimeter_right = true; - continue; - } - } - dont_connect: - // No way to continue the current polyline. Take the rest of the line up to the outer contour. - // This will finish the polyline, starting another polyline at a new point. - if (going_up) - ++ intrsctn; - else - -- intrsctn; - } - - // Finish the current vertical line, - // reset the current vertical line to pick a new starting point in the next round. - assert(intrsctn->is_outer()); - assert(intrsctn->is_high() == going_up); - pointLast = Point(seg.pos, intrsctn->pos()); - polyline_current->points.push_back(pointLast); - // Handle duplicate points and zero length segments. - polyline_current->remove_duplicate_points(); - assert(! polyline_current->has_duplicate_points()); - // Handle nearly zero length edges. - if (polyline_current->points.size() <= 1 || - (polyline_current->points.size() == 2 && - std::abs(polyline_current->points.front()(0) - polyline_current->points.back()(0)) < SCALED_EPSILON && - std::abs(polyline_current->points.front()(1) - polyline_current->points.back()(1)) < SCALED_EPSILON)) - polylines_out.pop_back(); - intrsctn = NULL; - i_intersection = -1; - polyline_current = NULL; - } + bool monotonous_infill = params.density > 0.99; + if (monotonous_infill) { + std::vector regions = generate_montonous_regions(segs); + connect_monotonous_regions(regions, segs); + std::mt19937_64 rng; + std::vector path = chain_monotonous_regions(regions, segs, rng); + polylines_from_paths(path, poly_with_offset, segs, polylines_out); + } else + traverse_graph_generate_polylines(poly_with_offset, params, this->link_max_length, segs, polylines_out); #ifdef SLIC3R_DEBUG { diff --git a/src/libslic3r/libslic3r.h b/src/libslic3r/libslic3r.h index 1cf946f8b..f6fbba994 100644 --- a/src/libslic3r/libslic3r.h +++ b/src/libslic3r/libslic3r.h @@ -25,6 +25,7 @@ // Saves around 32% RAM after slicing step, 6.7% after G-code export (tested on PrusaSlicer 2.2.0 final). typedef int32_t coord_t; #else +//FIXME At least FillRectilinear2 requires coord_t to be 32bit. typedef int64_t coord_t; #endif From e390ebc95c09db6947d7c009108aa4fbf66f90ad Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 24 Apr 2020 09:41:48 +0200 Subject: [PATCH 03/16] WIP: Monotonous infill --- src/libslic3r/Fill/FillRectilinear2.cpp | 834 +++++++++++++----------- src/libslic3r/GCode.cpp | 9 +- 2 files changed, 473 insertions(+), 370 deletions(-) diff --git a/src/libslic3r/Fill/FillRectilinear2.cpp b/src/libslic3r/Fill/FillRectilinear2.cpp index c2c19046e..3d5710d29 100644 --- a/src/libslic3r/Fill/FillRectilinear2.cpp +++ b/src/libslic3r/Fill/FillRectilinear2.cpp @@ -179,8 +179,8 @@ struct SegmentIntersection // Kept grouped with other booleans for smaller memory footprint. LinkType prev_on_contour_type { LinkType::Horizontal }; LinkType next_on_contour_type { LinkType::Horizontal }; - LinkQuality prev_on_contour_quality { true }; - LinkQuality next_on_contour_quality { true }; + LinkQuality prev_on_contour_quality { LinkQuality::Valid }; + LinkQuality next_on_contour_quality { LinkQuality::Valid }; // Was this segment along the y axis consumed? // Up means up along the vertical segment. bool consumed_vertical_up { false }; @@ -237,36 +237,34 @@ struct SegmentIntersection int left_vertical(Direction dir) const { return (dir == Direction::Up ? this->has_left_vertical_up() : this->has_left_vertical_down()) ? this->prev_on_contour : -1; } int left_vertical() const { return this->has_left_vertical() ? this->prev_on_contour : -1; } int left_vertical_outside() const { return this->is_low() ? this->left_vertical_down() : this->left_vertical_up(); } - int right_vertical_up() const { return this->has_right_vertical_up() ? this->prev_on_contour : -1; } - int right_vertical_down() const { return this->has_right_vertical_down() ? this->prev_on_contour : -1; } + int right_vertical_up() const { return this->has_right_vertical_up() ? this->next_on_contour : -1; } + int right_vertical_down() const { return this->has_right_vertical_down() ? this->next_on_contour : -1; } int right_vertical(Direction dir) const { return (dir == Direction::Up ? this->has_right_vertical_up() : this->has_right_vertical_down()) ? this->next_on_contour : -1; } - int right_vertical() const { return this->has_right_vertical() ? this->prev_on_contour : -1; } + int right_vertical() const { return this->has_right_vertical() ? this->next_on_contour : -1; } int right_vertical_outside() const { return this->is_low() ? this->right_vertical_down() : this->right_vertical_up(); } int vertical_up(Side side) const { return side == Side::Left ? this->left_vertical_up() : this->right_vertical_up(); } int vertical_down(Side side) const { return side == Side::Left ? this->left_vertical_down() : this->right_vertical_down(); } int vertical_outside(Side side) const { return side == Side::Left ? this->left_vertical_outside() : this->right_vertical_outside(); } + // Returns -1 if there is no link up. int vertical_up() const { - assert(! this->has_left_vertical_up() || ! this->has_right_vertical_up()); return this->has_left_vertical_up() ? this->left_vertical_up() : this->right_vertical_up(); } LinkQuality vertical_up_quality() const { - assert(! this->has_left_vertical_up() || ! this->has_right_vertical_up()); + assert(this->has_left_vertical_up() != this->has_right_vertical_up()); return this->has_left_vertical_up() ? this->prev_on_contour_quality : this->next_on_contour_quality; } + // Returns -1 if there is no link down. int vertical_down() const { - assert(! this->has_left_vertical_down() || ! this->has_right_vertical_down()); +// assert(! this->has_left_vertical_down() || ! this->has_right_vertical_down()); return this->has_left_vertical_down() ? this->left_vertical_down() : this->right_vertical_down(); } LinkQuality vertical_down_quality() const { - assert(! this->has_left_vertical_down() || ! this->has_right_vertical_down()); + assert(this->has_left_vertical_down() != this->has_right_vertical_down()); return this->has_left_vertical_down() ? this->prev_on_contour_quality : this->next_on_contour_quality; } int vertical_outside() const { return this->is_low() ? this->vertical_down() : this->vertical_up(); } -// int next_up() const { return this->prev_on_contour_vertical ? -1 : this->prev_on_contour; } -// int next_right() const { return this->next_on_contour_vertical ? -1 : this->next_on_contour; } - // Compare two y intersection points given by rational numbers. // Note that the rational number is given as pos_p/pos_q, where pos_p is int64 and pos_q is uint32. // This function calculates pos_p * other.pos_q < other.pos_p * pos_q as a 48bit number. @@ -489,32 +487,32 @@ static inline IntersectionTypeOtherVLine intersection_type_on_prev_next_vertical size_t iIntersection, SegmentIntersection::Side side) { - const SegmentedIntersectionLine &il_this = segs[iVerticalLine]; - const SegmentIntersection &itsct_this = il_this.intersections[iIntersection]; - if (itsct_this.has_vertical(side)) + const SegmentedIntersectionLine &vline_this = segs[iVerticalLine]; + const SegmentIntersection &it_this = vline_this.intersections[iIntersection]; + if (it_this.has_vertical(side)) // Not the first intersection along the contor. This intersection point // has been preceded by an intersection point along the vertical line. return INTERSECTION_TYPE_OTHER_VLINE_NOT_FIRST; - int iIntersectionOther = itsct_this.horizontal(side); + int iIntersectionOther = it_this.horizontal(side); if (iIntersectionOther == -1) return INTERSECTION_TYPE_OTHER_VLINE_UNDEFINED; assert(side == SegmentIntersection::Side::Right ? (iVerticalLine + 1 < segs.size()) : (iVerticalLine > 0)); - const SegmentedIntersectionLine &il_other = segs[side == SegmentIntersection::Side::Right ? (iVerticalLine+1) : (iVerticalLine-1)]; - const SegmentIntersection &itsct_other = il_other.intersections[iIntersectionOther]; - assert(itsct_other.is_inner()); + const SegmentedIntersectionLine &vline_other = segs[side == SegmentIntersection::Side::Right ? (iVerticalLine + 1) : (iVerticalLine - 1)]; + const SegmentIntersection &it_other = vline_other.intersections[iIntersectionOther]; + assert(it_other.is_inner()); assert(iIntersectionOther > 0); - assert(iIntersectionOther + 1 < il_other.intersections.size()); + assert(iIntersectionOther + 1 < vline_other.intersections.size()); // Is iIntersectionOther at the boundary of a vertical segment? - const SegmentIntersection &itsct_other2 = il_other.intersections[itsct_other.is_low() ? iIntersectionOther - 1 : iIntersectionOther + 1]; - if (itsct_other2.is_inner()) + const SegmentIntersection &it_other2 = vline_other.intersections[it_other.is_low() ? iIntersectionOther - 1 : iIntersectionOther + 1]; + if (it_other2.is_inner()) // Cannot follow a perimeter segment into the middle of another vertical segment. // Only perimeter segments connecting to the end of a vertical segment are followed. return INTERSECTION_TYPE_OTHER_VLINE_INNER; - assert(itsct_other.is_low() == itsct_other2.is_low()); - if (side == SegmentIntersection::Side::Right ? itsct_this.consumed_perimeter_right : itsct_other.consumed_perimeter_right) + assert(it_other.is_low() == it_other2.is_low()); + if (side == SegmentIntersection::Side::Right ? it_this.consumed_perimeter_right : it_other.consumed_perimeter_right) // This perimeter segment was already consumed. return INTERSECTION_TYPE_OTHER_VLINE_CONSUMED; - if (itsct_other.is_low() ? itsct_other.consumed_vertical_up : il_other.intersections[iIntersectionOther-1].consumed_vertical_up) + if (it_other.is_low() ? it_other.consumed_vertical_up : vline_other.intersections[iIntersectionOther - 1].consumed_vertical_up) // This vertical segment was already consumed. return INTERSECTION_TYPE_OTHER_VLINE_CONSUMED; return INTERSECTION_TYPE_OTHER_VLINE_OK; @@ -555,23 +553,23 @@ static inline coordf_t measure_perimeter_prev_next_segment_length( return coordf_t(-1); } - const SegmentedIntersectionLine &il = segs[iVerticalLine]; - const SegmentIntersection &itsct = il.intersections[iIntersection]; - const SegmentedIntersectionLine &il2 = segs[iVerticalLineOther]; - const SegmentIntersection &itsct2 = il2.intersections[iIntersection2]; - assert(itsct.iContour == itsct2.iContour); - const Polygon &poly = poly_with_offset.contour(itsct.iContour); -// const bool ccw = poly_with_offset.is_contour_ccw(il.iContour); - assert(itsct.type == itsct2.type); - assert(itsct.iContour == itsct2.iContour); - assert(itsct.is_inner()); - const bool forward = itsct.is_low() == dir_is_next; + const SegmentedIntersectionLine &vline = segs[iVerticalLine]; + const SegmentIntersection &it = vline.intersections[iIntersection]; + const SegmentedIntersectionLine &vline2 = segs[iVerticalLineOther]; + const SegmentIntersection &it2 = vline2.intersections[iIntersection2]; + assert(it.iContour == it2.iContour); + const Polygon &poly = poly_with_offset.contour(it.iContour); +// const bool ccw = poly_with_offset.is_contour_ccw(vline.iContour); + assert(it.type == it2.type); + assert(it.iContour == it2.iContour); + assert(it.is_inner()); + const bool forward = it.is_low() == dir_is_next; - Point p1(il.pos, itsct.pos()); - Point p2(il2.pos, itsct2.pos()); + Point p1(vline.pos, it.pos()); + Point p2(vline2.pos, it2.pos()); return forward ? - segment_length(poly, itsct .iSegment, p1, itsct2.iSegment, p2) : - segment_length(poly, itsct2.iSegment, p2, itsct .iSegment, p1); + segment_length(poly, it .iSegment, p1, it2.iSegment, p2) : + segment_length(poly, it2.iSegment, p2, it .iSegment, p1); } static inline coordf_t measure_perimeter_prev_segment_length( @@ -736,10 +734,10 @@ static inline float measure_outer_contour_slab( distance_of_segmens(poly, iSegBelow, itsct.iSegment, true); int d_up = (iAbove == -1) ? std::numeric_limits::max() : distance_of_segmens(poly, iSegAbove, itsct.iSegment, true); - if (intrsctn_type_prev == INTERSECTION_TYPE_OTHER_VLINE_OK && d_horiz > std::min(d_down, d_up)) + if (intrsection_type_prev == INTERSECTION_TYPE_OTHER_VLINE_OK && d_horiz > std::min(d_down, d_up)) // The vertical crossing comes eralier than the prev crossing. // Disable the perimeter going back. - intrsctn_type_prev = INTERSECTION_TYPE_OTHER_VLINE_NOT_FIRST; + intrsection_type_prev = INTERSECTION_TYPE_OTHER_VLINE_NOT_FIRST; if (d_up > std::min(d_horiz, d_down)) // The horizontal crossing comes earlier than the vertical crossing. vert_seg_dir_valid_mask &= ~DIR_BACKWARD; @@ -918,7 +916,7 @@ static std::vector slice_region_by_vertical_lines(con } // Verify the segments. If something is wrong, give up. -#define ASSERT_THROW(CONDITION) do { assert(CONDITION); throw InfillFailedException(); } while (0) +#define ASSERT_THROW(CONDITION) do { assert(CONDITION); if (! (CONDITION)) throw InfillFailedException(); } while (0) for (size_t i_seg = 0; i_seg < segs.size(); ++ i_seg) { SegmentedIntersectionLine &sil = segs[i_seg]; // The intersection points have to be even. @@ -952,18 +950,19 @@ static void connect_segment_intersections_by_contours(const ExPolygonWithOffset const SegmentedIntersectionLine *il_prev = i_vline > 0 ? &segs[i_vline - 1] : nullptr; const SegmentedIntersectionLine *il_next = i_vline + 1 < segs.size() ? &segs[i_vline + 1] : nullptr; - for (size_t i_intersection = 0; i_intersection + 1 < il.intersections.size(); ++ i_intersection) { + for (int i_intersection = 0; i_intersection < il.intersections.size(); ++ i_intersection) { SegmentIntersection &itsct = il.intersections[i_intersection]; const Polygon &poly = poly_with_offset.contour(itsct.iContour); // 1) Find possible connection points on the previous / next vertical line. - // Find an intersection point on iVerticalLineOther, intersecting iInnerContour - // at the same orientation as iIntersection, and being closest to iIntersection + // Find an intersection point on il_prev, intersecting i_intersection + // at the same orientation as i_intersection, and being closest to i_intersection // in the number of contour segments, when following the direction of the contour. + //FIXME this has O(n) time complexity. Likely an O(log(n)) scheme is possible. int iprev = -1; if (il_prev) { int dmin = std::numeric_limits::max(); - for (size_t i = 0; i < il_prev->intersections.size(); ++ i) { + for (int i = 0; i < il_prev->intersections.size(); ++ i) { const SegmentIntersection &itsct2 = il_prev->intersections[i]; if (itsct.iContour == itsct2.iContour && itsct.type == itsct2.type) { // The intersection points lie on the same contour and have the same orientation. @@ -976,10 +975,11 @@ static void connect_segment_intersections_by_contours(const ExPolygonWithOffset } } } + // The same for il_next. int inext = -1; if (il_next) { int dmin = std::numeric_limits::max(); - for (size_t i = 0; i < il_next->intersections.size(); ++ i) { + for (int i = 0; i < il_next->intersections.size(); ++ i) { const SegmentIntersection &itsct2 = il_next->intersections[i]; if (itsct.iContour == itsct2.iContour && itsct.type == itsct2.type) { // The intersection points lie on the same contour and have the same orientation. @@ -996,14 +996,14 @@ static void connect_segment_intersections_by_contours(const ExPolygonWithOffset // 2) Find possible connection points on the same vertical line. int iabove = -1; // Does the perimeter intersect the current vertical line above intrsctn? - for (size_t i = i_intersection + 1; i + 1 < il.intersections.size(); ++ i) + for (int i = i_intersection + 1; i < il.intersections.size(); ++ i) if (il.intersections[i].iContour == itsct.iContour) { iabove = i; break; } // Does the perimeter intersect the current vertical line below intrsctn? int ibelow = -1; - for (size_t i = i_intersection - 1; i > 0; -- i) + for (int i = i_intersection - 1; i >= 0; -- i) if (il.intersections[i].iContour == itsct.iContour) { ibelow = i; break; @@ -1019,7 +1019,7 @@ static void connect_segment_intersections_by_contours(const ExPolygonWithOffset int d_down = (ibelow == -1) ? std::numeric_limits::max() : distance_of_segmens(poly, il.intersections[ibelow].iSegment, itsct.iSegment, forward); int d_up = (iabove == -1) ? std::numeric_limits::max() : - distance_of_segmens(poly, il.intersections[ibelow].iSegment, itsct.iSegment, forward); + distance_of_segmens(poly, il.intersections[iabove].iSegment, itsct.iSegment, forward); if (d_horiz < std::min(d_down, d_up)) { itsct.prev_on_contour = iprev; itsct.prev_on_contour_type = SegmentIntersection::LinkType::Horizontal; @@ -1061,7 +1061,7 @@ static void connect_segment_intersections_by_contours(const ExPolygonWithOffset // Such intersection shall always exist. static const SegmentIntersection& end_of_vertical_run_raw(const SegmentIntersection &start) { - assert(start.type != SegmentIntersection::INNER_LOW); + assert(start.type == SegmentIntersection::INNER_LOW); // Step back to the beginning of the vertical segment to mark it as consumed. auto *it = &start; do { @@ -1083,7 +1083,7 @@ static SegmentIntersection& end_of_vertical_run_raw(SegmentIntersection &start) // Such intersection shall always exist. static const SegmentIntersection& end_of_vertical_run(const SegmentedIntersectionLine &il, const SegmentIntersection &start) { - assert(start.type != SegmentIntersection::INNER_LOW); + assert(start.type == SegmentIntersection::INNER_LOW); const SegmentIntersection *end = &end_of_vertical_run_raw(start); assert(end->type == SegmentIntersection::INNER_HIGH); for (;;) { @@ -1263,17 +1263,17 @@ static void traverse_graph_generate_polylines( { // For each outer only chords, measure their maximum distance to the bow of the outer contour. // Mark an outer only chord as consumed, if the distance is low. - for (size_t i_vline = 0; i_vline < segs.size(); ++i_vline) { - SegmentedIntersectionLine& seg = segs[i_vline]; - for (size_t i_intersection = 0; i_intersection + 1 < seg.intersections.size(); ++i_intersection) { - if (seg.intersections[i_intersection].type == SegmentIntersection::OUTER_LOW && - seg.intersections[i_intersection + 1].type == SegmentIntersection::OUTER_HIGH) { + for (int i_vline = 0; i_vline < segs.size(); ++ i_vline) { + SegmentedIntersectionLine &vline = segs[i_vline]; + for (int i_intersection = 0; i_intersection + 1 < vline.intersections.size(); ++ i_intersection) { + if (vline.intersections[i_intersection].type == SegmentIntersection::OUTER_LOW && + vline.intersections[i_intersection + 1].type == SegmentIntersection::OUTER_HIGH) { bool consumed = false; // if (params.full_infill()) { // measure_outer_contour_slab(poly_with_offset, segs, i_vline, i_ntersection); // } else consumed = true; - seg.intersections[i_intersection].consumed_vertical_up = consumed; + vline.intersections[i_intersection].consumed_vertical_up = consumed; } } } @@ -1283,34 +1283,34 @@ static void traverse_graph_generate_polylines( // Naively one would expect to achieve best results by chaining the paths by the shortest distance, // but that procedure does not create the longest continuous paths. // A simple "sweep left to right" procedure achieves better results. - size_t i_vline = 0; - size_t i_intersection = size_t(-1); + int i_vline = 0; + int i_intersection = -1; // Follow the line, connect the lines into a graph. // Until no new line could be added to the output path: Point pointLast; - Polyline* polyline_current = NULL; - if (!polylines_out.empty()) + Polyline* polyline_current = nullptr; + if (! polylines_out.empty()) pointLast = polylines_out.back().points.back(); for (;;) { - if (i_intersection == size_t(-1)) { + if (i_intersection == -1) { // The path has been interrupted. Find a next starting point, closest to the previous extruder position. coordf_t dist2min = std::numeric_limits().max(); - for (size_t i_vline2 = 0; i_vline2 < segs.size(); ++i_vline2) { - const SegmentedIntersectionLine& seg = segs[i_vline2]; - if (!seg.intersections.empty()) { - assert(seg.intersections.size() > 1); + for (int i_vline2 = 0; i_vline2 < segs.size(); ++ i_vline2) { + const SegmentedIntersectionLine &vline = segs[i_vline2]; + if (! vline.intersections.empty()) { + assert(vline.intersections.size() > 1); // Even number of intersections with the loops. - assert((seg.intersections.size() & 1) == 0); - assert(seg.intersections.front().type == SegmentIntersection::OUTER_LOW); - for (size_t i = 0; i < seg.intersections.size(); ++i) { - const SegmentIntersection& intrsctn = seg.intersections[i]; + assert((vline.intersections.size() & 1) == 0); + assert(vline.intersections.front().type == SegmentIntersection::OUTER_LOW); + for (int i = 0; i < vline.intersections.size(); ++ i) { + const SegmentIntersection& intrsctn = vline.intersections[i]; if (intrsctn.is_outer()) { assert(intrsctn.is_low() || i > 0); bool consumed = intrsctn.is_low() ? intrsctn.consumed_vertical_up : - seg.intersections[i - 1].consumed_vertical_up; - if (!consumed) { - coordf_t dist2 = sqr(coordf_t(pointLast(0) - seg.pos)) + sqr(coordf_t(pointLast(1) - intrsctn.pos())); + vline.intersections[i - 1].consumed_vertical_up; + if (! consumed) { + coordf_t dist2 = sqr(coordf_t(pointLast(0) - vline.pos)) + sqr(coordf_t(pointLast(1) - intrsctn.pos())); if (dist2 < dist2min) { dist2min = dist2; i_vline = i_vline2; @@ -1326,7 +1326,7 @@ static void traverse_graph_generate_polylines( } } } - if (i_intersection == size_t(-1)) + if (i_intersection == -1) // We are finished. break; found: @@ -1339,220 +1339,196 @@ static void traverse_graph_generate_polylines( } // From the initial point (i_vline, i_intersection), follow a path. - SegmentedIntersectionLine& seg = segs[i_vline]; - SegmentIntersection* intrsctn = &seg.intersections[i_intersection]; - bool going_up = intrsctn->is_low(); - bool try_connect = false; + SegmentedIntersectionLine &vline = segs[i_vline]; + SegmentIntersection *it = &vline.intersections[i_intersection]; + bool going_up = it->is_low(); + bool try_connect = false; if (going_up) { - assert(!intrsctn->consumed_vertical_up); - assert(i_intersection + 1 < seg.intersections.size()); + assert(! it->consumed_vertical_up); + assert(i_intersection + 1 < vline.intersections.size()); // Step back to the beginning of the vertical segment to mark it as consumed. - if (intrsctn->is_inner()) { + if (it->is_inner()) { assert(i_intersection > 0); - --intrsctn; - --i_intersection; + -- it; + -- i_intersection; } // Consume the complete vertical segment up to the outer contour. do { - intrsctn->consumed_vertical_up = true; - ++intrsctn; - ++i_intersection; - assert(i_intersection < seg.intersections.size()); - } while (intrsctn->type != SegmentIntersection::OUTER_HIGH); - if ((intrsctn - 1)->is_inner()) { + it->consumed_vertical_up = true; + ++ it; + ++ i_intersection; + assert(i_intersection < vline.intersections.size()); + } while (it->type != SegmentIntersection::OUTER_HIGH); + if ((it - 1)->is_inner()) { // Step back. - --intrsctn; - --i_intersection; - assert(intrsctn->type == SegmentIntersection::INNER_HIGH); + -- it; + -- i_intersection; + assert(it->type == SegmentIntersection::INNER_HIGH); try_connect = true; } } else { // Going down. - assert(intrsctn->is_high()); + assert(it->is_high()); assert(i_intersection > 0); - assert(!(intrsctn - 1)->consumed_vertical_up); + assert(!(it - 1)->consumed_vertical_up); // Consume the complete vertical segment up to the outer contour. - if (intrsctn->is_inner()) - intrsctn->consumed_vertical_up = true; + if (it->is_inner()) + it->consumed_vertical_up = true; do { assert(i_intersection > 0); - --intrsctn; - --i_intersection; - intrsctn->consumed_vertical_up = true; - } while (intrsctn->type != SegmentIntersection::OUTER_LOW); - if ((intrsctn + 1)->is_inner()) { + -- it; + -- i_intersection; + it->consumed_vertical_up = true; + } while (it->type != SegmentIntersection::OUTER_LOW); + if ((it + 1)->is_inner()) { // Step back. - ++intrsctn; - ++i_intersection; - assert(intrsctn->type == SegmentIntersection::INNER_LOW); + ++ it; + ++ i_intersection; + assert(it->type == SegmentIntersection::INNER_LOW); try_connect = true; } } if (try_connect) { // Decide, whether to finish the segment, or whether to follow the perimeter. - // 1) Find possible connection points on the previous / next vertical line. - IntersectionTypeOtherVLine intrsctn_type_prev = intersection_type_on_prev_vertical_line(segs, i_vline, i_intersection); + IntersectionTypeOtherVLine intrsection_type_prev = intersection_type_on_prev_vertical_line(segs, i_vline, i_intersection); IntersectionTypeOtherVLine intrsctn_type_next = intersection_type_on_next_vertical_line(segs, i_vline, i_intersection); // Try to connect to a previous or next vertical line, making a zig-zag pattern. - if (intrsctn_type_prev == INTERSECTION_TYPE_OTHER_VLINE_OK || intrsctn_type_next == INTERSECTION_TYPE_OTHER_VLINE_OK) { - int iPrev = intrsctn->left_horizontal(); - int iNext = intrsctn->right_horizontal(); - coordf_t distPrev = (intrsctn_type_prev != INTERSECTION_TYPE_OTHER_VLINE_OK) ? std::numeric_limits::max() : - measure_perimeter_prev_segment_length(poly_with_offset, segs, i_vline, i_intersection, iPrev); - coordf_t distNext = (intrsctn_type_next != INTERSECTION_TYPE_OTHER_VLINE_OK) ? std::numeric_limits::max() : - measure_perimeter_next_segment_length(poly_with_offset, segs, i_vline, i_intersection, iNext); + if (intrsection_type_prev == INTERSECTION_TYPE_OTHER_VLINE_OK || intrsctn_type_next == INTERSECTION_TYPE_OTHER_VLINE_OK) { + // A horizontal connection along the perimeter line exists. + int i_prev = it->left_horizontal(); + int i_next = it->right_horizontal(); + coordf_t dist_prev = (intrsection_type_prev != INTERSECTION_TYPE_OTHER_VLINE_OK) ? std::numeric_limits::max() : + measure_perimeter_prev_segment_length(poly_with_offset, segs, i_vline, i_intersection, i_prev); + coordf_t dist_next = (intrsctn_type_next != INTERSECTION_TYPE_OTHER_VLINE_OK) ? std::numeric_limits::max() : + measure_perimeter_next_segment_length(poly_with_offset, segs, i_vline, i_intersection, i_next); // Take the shorter path. //FIXME this may not be always the best strategy to take the shortest connection line now. - bool take_next = (intrsctn_type_prev == INTERSECTION_TYPE_OTHER_VLINE_OK && intrsctn_type_next == INTERSECTION_TYPE_OTHER_VLINE_OK) ? - (distNext < distPrev) : + bool take_next = (intrsection_type_prev == INTERSECTION_TYPE_OTHER_VLINE_OK && intrsctn_type_next == INTERSECTION_TYPE_OTHER_VLINE_OK) ? + (dist_next < dist_prev) : intrsctn_type_next == INTERSECTION_TYPE_OTHER_VLINE_OK; - assert(intrsctn->is_inner()); - bool skip = params.dont_connect || (link_max_length > 0 && (take_next ? distNext : distPrev) > link_max_length); + assert(it->is_inner()); + bool skip = params.dont_connect || (link_max_length > 0 && (take_next ? dist_next : dist_prev) > link_max_length); if (skip) { +#if 1 // Just skip the connecting contour and start a new path. goto dont_connect; - polyline_current->points.push_back(Point(seg.pos, intrsctn->pos())); - polylines_out.push_back(Polyline()); +#else + polyline_current->points.emplace_back(vline.pos, it->pos()); + polylines_out.emplace_back(); polyline_current = &polylines_out.back(); const SegmentedIntersectionLine& il2 = segs[take_next ? (i_vline + 1) : (i_vline - 1)]; - polyline_current->points.push_back(Point(il2.pos, il2.intersections[take_next ? iNext : iPrev].pos())); + polyline_current->points.emplace_back(il2.pos, il2.intersections[take_next ? i_next : i_prev].pos()); +#endif } else { - polyline_current->points.push_back(Point(seg.pos, intrsctn->pos())); - emit_perimeter_prev_next_segment(poly_with_offset, segs, i_vline, intrsctn->iContour, i_intersection, take_next ? iNext : iPrev, *polyline_current, take_next); + polyline_current->points.emplace_back(vline.pos, it->pos()); + emit_perimeter_prev_next_segment(poly_with_offset, segs, i_vline, it->iContour, i_intersection, take_next ? i_next : i_prev, *polyline_current, take_next); } // Mark both the left and right connecting segment as consumed, because one cannot go to this intersection point as it has been consumed. - if (iPrev != -1) - segs[i_vline - 1].intersections[iPrev].consumed_perimeter_right = true; - if (iNext != -1) - intrsctn->consumed_perimeter_right = true; + if (i_prev != -1) + segs[i_vline - 1].intersections[i_prev].consumed_perimeter_right = true; + if (i_next != -1) + it->consumed_perimeter_right = true; //FIXME consume the left / right connecting segments at the other end of this line? Currently it is not critical because a perimeter segment is not followed if the vertical segment at the other side has already been consumed. // Advance to the neighbor line. if (take_next) { - ++i_vline; - i_intersection = iNext; + ++ i_vline; + i_intersection = i_next; } else { - --i_vline; - i_intersection = iPrev; + -- i_vline; + i_intersection = i_prev; } continue; } // 5) Try to connect to a previous or next point on the same vertical line. - if (int inext = intrsctn->vertical_outside(); inext != -1) { + if (int inext = it->vertical_outside(); inext != -1) { bool valid = true; // Verify, that there is no intersection with the inner contour up to the end of the contour segment. // Verify, that the successive segment has not been consumed yet. if (going_up) { - if (seg.intersections[inext].consumed_vertical_up) + if (vline.intersections[inext].consumed_vertical_up) valid = false; else { - for (int i = (int)i_intersection + 1; i < inext && valid; ++i) - if (seg.intersections[i].is_inner()) + for (int i = i_intersection + 1; i < inext && valid; ++ i) + if (vline.intersections[i].is_inner()) valid = false; } } else { - if (seg.intersections[inext - 1].consumed_vertical_up) + if (vline.intersections[inext - 1].consumed_vertical_up) valid = false; else { - for (int i = inext + 1; i < (int)i_intersection && valid; ++i) - if (seg.intersections[i].is_inner()) + for (int i = inext + 1; i < i_intersection && valid; ++ i) + if (vline.intersections[i].is_inner()) valid = false; } } if (valid) { - const Polygon& poly = poly_with_offset.contour(intrsctn->iContour); - assert(intrsctn->iContour == seg.intersections[inext].iContour); - int iSegNext = seg.intersections[inext].iSegment; + const Polygon &poly = poly_with_offset.contour(it->iContour); + assert(it->iContour == vline.intersections[inext].iContour); // Skip this perimeter line? bool skip = params.dont_connect; - bool dir_forward = intrsctn->has_right_vertical_outside(); + bool dir_forward = it->has_right_vertical_outside(); if (! skip && link_max_length > 0) { coordf_t link_length = measure_perimeter_segment_on_vertical_line_length( poly_with_offset, segs, i_vline, i_intersection, inext, dir_forward); skip = link_length > link_max_length; } - polyline_current->points.push_back(Point(seg.pos, intrsctn->pos())); + polyline_current->points.emplace_back(vline.pos, it->pos()); if (skip) { // Just skip the connecting contour and start a new path. - polylines_out.push_back(Polyline()); + polylines_out.emplace_back(); polyline_current = &polylines_out.back(); - polyline_current->points.push_back(Point(seg.pos, seg.intersections[inext].pos())); + polyline_current->points.emplace_back(vline.pos, vline.intersections[inext].pos()); } else { // Consume the connecting contour and the next segment. - emit_perimeter_segment_on_vertical_line(poly_with_offset, segs, i_vline, intrsctn->iContour, i_intersection, inext, *polyline_current, dir_forward); + emit_perimeter_segment_on_vertical_line(poly_with_offset, segs, i_vline, it->iContour, i_intersection, inext, *polyline_current, dir_forward); } // Mark both the left and right connecting segment as consumed, because one cannot go to this intersection point as it has been consumed. // If there are any outer intersection points skipped (bypassed) by the contour, // mark them as processed. - if (going_up) { - for (int i = (int)i_intersection; i < inext; ++i) - seg.intersections[i].consumed_vertical_up = true; - } else { - for (int i = inext; i < (int)i_intersection; ++i) - seg.intersections[i].consumed_vertical_up = true; - } - // seg.intersections[going_up ? i_intersection : i_intersection - 1].consumed_vertical_up = true; - intrsctn->consumed_perimeter_right = true; - i_intersection = inext; if (going_up) - ++intrsctn; + for (int i = i_intersection; i < inext; ++ i) + vline.intersections[i].consumed_vertical_up = true; else - --intrsctn; - intrsctn->consumed_perimeter_right = true; + for (int i = inext; i < i_intersection; ++ i) + vline.intersections[i].consumed_vertical_up = true; + // seg.intersections[going_up ? i_intersection : i_intersection - 1].consumed_vertical_up = true; + it->consumed_perimeter_right = true; + (going_up ? ++ it : -- it)->consumed_perimeter_right = true; + i_intersection = inext; continue; } } + dont_connect: // No way to continue the current polyline. Take the rest of the line up to the outer contour. // This will finish the polyline, starting another polyline at a new point. - if (going_up) - ++intrsctn; - else - --intrsctn; + going_up ? ++ it : -- it; } // Finish the current vertical line, // reset the current vertical line to pick a new starting point in the next round. - assert(intrsctn->is_outer()); - assert(intrsctn->is_high() == going_up); - pointLast = Point(seg.pos, intrsctn->pos()); - polyline_current->points.push_back(pointLast); + assert(it->is_outer()); + assert(it->is_high() == going_up); + pointLast = Point(vline.pos, it->pos()); + polyline_current->points.emplace_back(pointLast); // Handle duplicate points and zero length segments. polyline_current->remove_duplicate_points(); - assert(!polyline_current->has_duplicate_points()); + assert(! polyline_current->has_duplicate_points()); // Handle nearly zero length edges. if (polyline_current->points.size() <= 1 || (polyline_current->points.size() == 2 && std::abs(polyline_current->points.front()(0) - polyline_current->points.back()(0)) < SCALED_EPSILON && std::abs(polyline_current->points.front()(1) - polyline_current->points.back()(1)) < SCALED_EPSILON)) polylines_out.pop_back(); - intrsctn = NULL; - i_intersection = -1; - polyline_current = NULL; + it = nullptr; + i_intersection = -1; + polyline_current = nullptr; } } -struct MonotonousRegion; - -struct NextMonotonousRegion -{ - MonotonousRegion *region; - struct Path { - float length { 0 }; // Length of the link to the next region. - float visibility { 0 }; // 1 / length. Which length, just to the next region, or including the path accross the region? - float pheromone { 0 }; // <0, 1> - }; - enum Index : int { - LowLow, - LowHigh, - HighLow, - HighHigh - }; - Path paths[4]; -}; - struct MonotonousRegion { struct Boundary { @@ -1565,20 +1541,28 @@ struct MonotonousRegion Boundary right; // Length when starting at left.low - double len1; + float len1 { 0.f }; // Length when starting at left.high - double len2; + float len2 { 0.f }; // If true, then when starting at left.low, then ending at right.high and vice versa. // If false, then ending at the same side as starting. - bool flips; + bool flips { false }; + bool length(bool region_flipped) const { return region_flipped ? len2 : len1; } int left_intersection_point(bool region_flipped) const { return region_flipped ? left.high : left.low; } int right_intersection_point(bool region_flipped) const { return (region_flipped == flips) ? right.low : right.high; } // Left regions are used to track whether all regions left to this one have already been printed. boost::container::small_vector left_neighbors; // Right regions are held to pick a next region to be extruded using the "Ant colony" heuristics. - boost::container::small_vector right_neighbors; + boost::container::small_vector right_neighbors; +}; + +struct AntPath +{ + float length { -1. }; // Length of the link to the next region. + float visibility { -1. }; // 1 / length. Which length, just to the next region, or including the path accross the region? + float pheromone { 0 }; // <0, 1> }; struct MonotonousRegionLink @@ -1587,10 +1571,65 @@ struct MonotonousRegionLink bool flipped; // Distance of right side of this region to left side of the next region, if the "flipped" flag of this region and the next region // is applied as defined. - NextMonotonousRegion::Path *next; + AntPath *next; // Distance of right side of this region to left side of the next region, if the "flipped" flag of this region and the next region // is applied in reverse order as if the zig-zags were flipped. - NextMonotonousRegion::Path *next_flipped; + AntPath *next_flipped; +}; + +class AntPathMatrix +{ +public: + AntPathMatrix(const std::vector ®ions, const ExPolygonWithOffset &poly_with_offset, const std::vector &segs) : + m_regions(regions), + m_poly_with_offset(poly_with_offset), + m_segs(segs), + // From end of one region to the start of another region, both flipped or not flipped. + m_matrix(regions.size() * regions.size() * 4) {} + + AntPath& operator()(const MonotonousRegion ®ion_from, bool flipped_from, const MonotonousRegion ®ion_to, bool flipped_to) + { + int row = 2 * int(®ion_from - m_regions.data()) + flipped_from; + int col = 2 * int(®ion_to - m_regions.data()) + flipped_to; + AntPath &path = m_matrix[row * m_regions.size() * 2 + col]; + if (path.length == -1.) { + // This path is accessed for the first time. Update the length and cost. + int i_from = region_from.right_intersection_point(flipped_from); + int i_to = region_to.left_intersection_point(flipped_to); + const SegmentedIntersectionLine &vline_from = m_segs[region_from.right.vline]; + const SegmentedIntersectionLine &vline_to = m_segs[region_to.left.vline]; + if (region_from.right.vline + 1 == region_from.left.vline) { + int i_right = vline_from.intersections[i_from].right_horizontal(); + if (i_right == i_to && vline_from.intersections[i_from].next_on_contour_quality == SegmentIntersection::LinkQuality::Valid) { + // Measure length along the contour. + path.length = measure_perimeter_next_segment_length(m_poly_with_offset, m_segs, region_from.right.vline, i_from, i_to); + } + } + if (path.length == -1.) { + // Just apply the Eucledian distance of the end points. + path.length = Vec2f(vline_to.pos - vline_from.pos, vline_to.intersections[i_to].pos() - vline_from.intersections[i_from].pos()).norm(); + } + path.visibility = 1. / (path.length + EPSILON); + } + return path; + } + + AntPath& operator()(const MonotonousRegionLink ®ion_from, const MonotonousRegion ®ion_to, bool flipped_to) + { return (*this)(*region_from.region, region_from.flipped, region_to, flipped_to); } + AntPath& operator()(const MonotonousRegion ®ion_from, bool flipped_from, const MonotonousRegionLink ®ion_to) + { return (*this)(region_from, flipped_from, *region_to.region, region_to.flipped); } + AntPath& operator()(const MonotonousRegionLink ®ion_from, const MonotonousRegionLink ®ion_to) + { return (*this)(*region_from.region, region_from.flipped, *region_to.region, region_to.flipped); } + +private: + // Source regions, used for addressing and updating m_matrix. + const std::vector &m_regions; + // To calculate the intersection points and contour lengths. + const ExPolygonWithOffset &m_poly_with_offset; + const std::vector &m_segs; + // From end of one region to the start of another region, both flipped or not flipped. + //FIXME one may possibly use sparse representation of the matrix. + std::vector m_matrix; }; static const SegmentIntersection& vertical_run_bottom(const SegmentedIntersectionLine &vline, const SegmentIntersection &start) @@ -1601,10 +1640,15 @@ static const SegmentIntersection& vertical_run_bottom(const SegmentedIntersectio for (;;) { while (it->type != SegmentIntersection::INNER_LOW) -- it; - int down = it->vertical_down(); - if (down == -1 || it->vertical_down_quality() != SegmentIntersection::LinkQuality::Valid) - break; - it = &vline.intersections[down]; + if ((it - 1)->type == SegmentIntersection::INNER_HIGH) + -- it; + else { + int down = it->vertical_down(); + if (down == -1 || it->vertical_down_quality() != SegmentIntersection::LinkQuality::Valid) + break; + it = &vline.intersections[down]; + assert(it->type == SegmentIntersection::INNER_HIGH); + } } return *it; } @@ -1621,10 +1665,15 @@ static const SegmentIntersection& vertical_run_top(const SegmentedIntersectionLi for (;;) { while (it->type != SegmentIntersection::INNER_HIGH) ++ it; - int up = it->vertical_up(); - if (up == -1 || it->vertical_up_quality() != SegmentIntersection::LinkQuality::Valid) - break; - it = &vline.intersections[up]; + if ((it + 1)->type == SegmentIntersection::INNER_LOW) + ++ it; + else { + int up = it->vertical_up(); + if (up == -1 || it->vertical_up_quality() != SegmentIntersection::LinkQuality::Valid) + break; + it = &vline.intersections[up]; + assert(it->type == SegmentIntersection::INNER_LOW); + } } return *it; } @@ -1719,41 +1768,53 @@ static std::vector generate_montonous_regions(std::vector monotonous_regions; - for (size_t i_vline_seed = 0; i_vline_seed < segs.size(); ++ i_vline_seed) { + for (int i_vline_seed = 0; i_vline_seed < segs.size(); ++ i_vline_seed) { SegmentedIntersectionLine &vline_seed = segs[i_vline_seed]; - for (size_t i_intersection_seed = 1; i_intersection_seed + 1 < vline_seed.intersections.size(); ) { + for (int i_intersection_seed = 1; i_intersection_seed + 1 < vline_seed.intersections.size(); ) { while (i_intersection_seed + 1 < vline_seed.intersections.size() && vline_seed.intersections[i_intersection_seed].type != SegmentIntersection::INNER_LOW) ++ i_intersection_seed; SegmentIntersection *start = &vline_seed.intersections[i_intersection_seed]; - SegmentIntersection *end = &end_of_vertical_run_raw(*start); + SegmentIntersection *end = &end_of_vertical_run(vline_seed, *start); if (! start->consumed_vertical_up) { // Draw a new monotonous region starting with this segment. // while there is only a single right neighbor - start->consumed_vertical_up = true; - size_t i_vline = i_vline_seed; + int i_vline = i_vline_seed; std::pair left(start, end); MonotonousRegion region; region.left.vline = i_vline; - region.left.low = left.first - vline_seed.intersections.data(); - region.left.high = left.second - vline_seed.intersections.data(); + region.left.low = int(left.first - vline_seed.intersections.data()); + region.left.high = int(left.second - vline_seed.intersections.data()); region.right = region.left; + start->consumed_vertical_up = true; + int num_lines = 1; while (++ i_vline < segs.size()) { SegmentedIntersectionLine &vline_left = segs[i_vline - 1]; SegmentedIntersectionLine &vline_right = segs[i_vline]; - std::pair right = right_overlap(left, vline_right); - std::pair right_left = left_overlap(right, vline_left); + std::pair right = right_overlap(left, vline_right); + if (right.first == nullptr) + // No neighbor at the right side of the current segment. + break; + SegmentIntersection* right_top_first = &vertical_run_top(vline_right, *right.first); + if (right_top_first != right.second) + // This segment overlaps with multiple segments at its right side. + break; + std::pair right_left = left_overlap(right, vline_left); if (left != right_left) - // Left & right draws don't overlap exclusively. + // Left & right draws don't overlap exclusively, right neighbor segment overlaps with multiple segments at its left. break; region.right.vline = i_vline; - region.right.low = right.first - vline_right.intersections.data(); - region.right.high = right.second - vline_right.intersections.data(); + region.right.low = int(right.first - vline_right.intersections.data()); + region.right.high = int(right.second - vline_right.intersections.data()); right.first->consumed_vertical_up = true; + ++ num_lines; left = right; } + // Even number of lines makes the infill zig-zag to exit on the other side of the region than where it starts. + region.flips = (num_lines & 1) != 0; + monotonous_regions.emplace_back(region); } - i_intersection_seed = end - vline_seed.intersections.data() + 1; + i_intersection_seed = int(end - vline_seed.intersections.data()) + 1; } } @@ -1781,38 +1842,41 @@ static void connect_monotonous_regions(std::vector ®ions, s for (MonotonousRegion ®ion : regions) { if (region.left.vline > 0) { auto &vline = segs[region.left.vline]; - auto begin = &vline.intersections[region.left.low]; - auto end = &vline.intersections[region.left.high]; - for (;;) { - MapType key(begin, nullptr); - auto it = std::lower_bound(map_intersection_to_region_end.begin(), map_intersection_to_region_end.end(), key); - assert(it != map_intersection_to_region_end.end() && it->first == key.first); - NextMonotonousRegion next_region{ ®ion }; - it->second->right_neighbors.emplace_back(next_region); - SegmentIntersection *next = &vertical_run_top(vline, *begin); - if (next == end) - break; - while (next->type != SegmentIntersection::INNER_LOW) - ++ next; - begin = next; - } + auto &vline_left = segs[region.left.vline - 1]; + auto[lbegin, lend] = left_overlap(vline.intersections[region.left.low], vline.intersections[region.left.high], vline_left); + if (lbegin != nullptr) { + for (;;) { + MapType key(lbegin, nullptr); + auto it = std::lower_bound(map_intersection_to_region_end.begin(), map_intersection_to_region_end.end(), key); + assert(it != map_intersection_to_region_end.end() && it->first == key.first); + it->second->right_neighbors.emplace_back(®ion); + SegmentIntersection *lnext = &vertical_run_top(vline_left, *lbegin); + if (lnext == lend) + break; + while (lnext->type != SegmentIntersection::INNER_LOW) + ++ lnext; + lbegin = lnext; + } + } } if (region.right.vline + 1 < segs.size()) { auto &vline = segs[region.right.vline]; - auto begin = &vline.intersections[region.right.low]; - auto end = &vline.intersections[region.right.high]; - for (;;) { - MapType key(begin, nullptr); - auto it = std::lower_bound(map_intersection_to_region_start.begin(), map_intersection_to_region_start.end(), key); - assert(it != map_intersection_to_region_start.end() && it->first == key.first); - it->second->left_neighbors.emplace_back(®ion); - SegmentIntersection *next = &vertical_run_top(vline, *begin); - if (next == end) - break; - while (next->type != SegmentIntersection::INNER_LOW) - ++ next; - begin = next; - } + auto &vline_right = segs[region.right.vline + 1]; + auto [rbegin, rend] = right_overlap(vline.intersections[region.right.low], vline.intersections[region.right.high], vline_right); + if (rbegin != nullptr) { + for (;;) { + MapType key(rbegin, nullptr); + auto it = std::lower_bound(map_intersection_to_region_start.begin(), map_intersection_to_region_start.end(), key); + assert(it != map_intersection_to_region_start.end() && it->first == key.first); + it->second->left_neighbors.emplace_back(®ion); + SegmentIntersection *rnext = &vertical_run_top(vline_right, *rbegin); + if (rnext == rend) + break; + while (rnext->type != SegmentIntersection::INNER_LOW) + ++ rnext; + rbegin = rnext; + } + } } } } @@ -1821,7 +1885,7 @@ static void connect_monotonous_regions(std::vector ®ions, s // https://www.chalmers.se/en/departments/math/research/research-groups/optimization/OptimizationMasterTheses/MScThesis-RaadSalman-final.pdf // Algorithm 6.1 Lexicographic Path Preserving 3-opt // Optimize path while maintaining the ordering constraints. -void monotonous_3_opt(std::vector &path, std::vector &segs) +void monotonous_3_opt(std::vector &path, const std::vector &segs) { // When doing the 3-opt path preserving flips, one has to fulfill two constraints: // @@ -1842,19 +1906,19 @@ void monotonous_3_opt(std::vector &path, std::vector chain_monotonous_regions( - std::vector ®ions, std::vector &segs, std::mt19937_64 &rng) + std::vector ®ions, const ExPolygonWithOffset &poly_with_offset, const std::vector &segs, std::mt19937_64 &rng) { // Start point of a region (left) given the direction of the initial infill line. auto region_start_point = [&segs](const MonotonousRegion ®ion, bool dir) { - SegmentedIntersectionLine &vline = segs[region.left.vline]; - SegmentIntersection &ipt = vline.intersections[dir ? region.left.high : region.left.low]; + const SegmentedIntersectionLine &vline = segs[region.left.vline]; + const SegmentIntersection &ipt = vline.intersections[dir ? region.left.high : region.left.low]; return Vec2f(float(vline.pos), float(ipt.pos())); }; // End point of a region (right) given the direction of the initial infill line and whether the monotonous run contains // even or odd number of vertical lines. auto region_end_point = [&segs](const MonotonousRegion ®ion, bool dir) { - SegmentedIntersectionLine &vline = segs[region.right.vline]; - SegmentIntersection &ipt = vline.intersections[(dir == region.flips) ? region.right.low : region.right.high]; + const SegmentedIntersectionLine &vline = segs[region.right.vline]; + const SegmentIntersection &ipt = vline.intersections[(dir == region.flips) ? region.right.low : region.right.high]; return Vec2f(float(vline.pos), float(ipt.pos())); }; @@ -1867,7 +1931,7 @@ static std::vector chain_monotonous_regions( if (region.left_neighbors.empty()) queue.emplace_back(®ion); else - left_neighbors_unprocessed[®ion - regions.data()] = region.left_neighbors.size(); + left_neighbors_unprocessed[®ion - regions.data()] = int(region.left_neighbors.size()); // Make copy of structures that need to be initialized at each ant iteration. auto left_neighbors_unprocessed_initial = left_neighbors_unprocessed; auto queue_initial = queue; @@ -1878,14 +1942,16 @@ static std::vector chain_monotonous_regions( float best_path_length = std::numeric_limits::max(); struct NextCandidate { - NextMonotonousRegion *region; - NextMonotonousRegion::Path *link; - NextMonotonousRegion::Path *link_flipped; - float cost; - bool dir; + MonotonousRegion *region; + AntPath *link; + AntPath *link_flipped; + float cost; + bool dir; }; std::vector next_candidates; + AntPathMatrix path_matrix(regions, poly_with_offset, segs); + // How many times to repeat the ant simulation. constexpr int num_runs = 10; // With how many ants each of the run will be performed? @@ -1900,10 +1966,10 @@ static std::vector chain_monotonous_regions( constexpr float pheromone_alpha = 1.f; // pheromone exponent constexpr float pheromone_beta = 2.f; // attractiveness weighted towards edge length // Cost of traversing a link between two monotonous regions. - auto path_cost = [pheromone_alpha, pheromone_beta](NextMonotonousRegion::Path &path) { + auto path_cost = [pheromone_alpha, pheromone_beta](AntPath &path) { return pow(path.pheromone, pheromone_alpha) * pow(path.visibility, pheromone_beta); }; - for (int run = 0; run < num_runs; ++ run) + for (int run = 0; run < num_runs; ++ run) { for (int ant = 0; ant < num_ants; ++ ant) { @@ -1911,92 +1977,111 @@ static std::vector chain_monotonous_regions( path.clear(); queue = queue_initial; left_neighbors_unprocessed = left_neighbors_unprocessed_initial; - while (! queue.empty()) { - // Sort the queue by distance to the last point. - // Take a candidate based on shortest distance? or ant colony? - if (path.empty()) { - // Pick randomly the first from the queue at random orientation. - int first_idx = std::uniform_int_distribution<>(0, int(queue.size()) - 1)(rng); - path.emplace_back(MonotonousRegionLink{ queue[first_idx], rng() > rng.max() / 2 }); - *(queue.begin() + first_idx) = std::move(queue.back()); - queue.pop_back(); + // Pick randomly the first from the queue at random orientation. + int first_idx = std::uniform_int_distribution<>(0, int(queue.size()) - 1)(rng); + path.emplace_back(MonotonousRegionLink{ queue[first_idx], rng() > rng.max() / 2 }); + *(queue.begin() + first_idx) = std::move(queue.back()); + queue.pop_back(); + assert(left_neighbors_unprocessed[path.back().region - regions.data()] == 0); + + while (! queue.empty() || ! path.back().region->right_neighbors.empty()) { + // Chain. + MonotonousRegion ®ion = *path.back().region; + bool dir = path.back().flipped; + Vec2f end_pt = region_end_point(region, dir); + // Sort by distance to pt. + next_candidates.clear(); + next_candidates.reserve(region.right_neighbors.size() * 2); + for (MonotonousRegion *next : region.right_neighbors) { + int &unprocessed = left_neighbors_unprocessed[next - regions.data()]; + assert(unprocessed > 0); + if (-- unprocessed == 0) { + // Dependencies of the successive blocks are satisfied. + AntPath &path1 = path_matrix(region, dir, *next, false); + AntPath &path1_flipped = path_matrix(region, ! dir, *next, true); + AntPath &path2 = path_matrix(region, dir, *next, true); + AntPath &path2_flipped = path_matrix(region, ! dir, *next, false); + next_candidates.emplace_back(NextCandidate{ next, &path1, &path1_flipped, path_cost(path1), false }); + next_candidates.emplace_back(NextCandidate{ next, &path2, &path2_flipped, path_cost(path2), true }); + } + } + size_t num_direct_neighbors = next_candidates.size(); + //FIXME add the queue items to the candidates? These are valid moves as well. + if (num_direct_neighbors == 0) { + // Add the queue candidates. + for (MonotonousRegion *next : queue) { + AntPath &path1 = path_matrix(region, dir, *next, false); + AntPath &path1_flipped = path_matrix(region, ! dir, *next, true); + AntPath &path2 = path_matrix(region, dir, *next, true); + AntPath &path2_flipped = path_matrix(region, ! dir, *next, false); + next_candidates.emplace_back(NextCandidate{ next, &path1, &path1_flipped, path_cost(path1), false }); + next_candidates.emplace_back(NextCandidate{ next, &path2, &path2_flipped, path_cost(path2), true }); + } + } + float dice = float(rng()) / float(rng.max()); + std::vector::iterator take_path; + if (dice < probability_take_best) { + // Take the lowest cost path. + take_path = std::min_element(next_candidates.begin(), next_candidates.end(), [](auto &l, auto &r){ return l.cost < r.cost; }); } else { - // Pick the closest neighbor from the queue? - } - -- left_neighbors_unprocessed[path.back().region - regions.data()]; - while (! path.back().region->right_neighbors.empty()) { - // Chain. - MonotonousRegion ®ion = *path.back().region; - bool dir = path.back().flipped; - Vec2f end_pt = region_end_point(region, dir); - // Sort by distance to pt. - next_candidates.reserve(region.right_neighbors.size() * 2); - for (NextMonotonousRegion &next : region.right_neighbors) { - int unprocessed = left_neighbors_unprocessed[next.region - regions.data()]; - assert(unprocessed > 0); - if (unprocessed == 1) { - // Dependencies of the successive blocks are satisfied. - bool flip = dir == region.flips; - auto path_cost = [pheromone_alpha, pheromone_beta](NextMonotonousRegion::Path& path) { - return pow(path.pheromone, pheromone_alpha) * pow(path.visibility, pheromone_beta); - }; - NextMonotonousRegion::Path &path_low = next.paths[flip ? NextMonotonousRegion::HighLow : NextMonotonousRegion::LowLow]; - NextMonotonousRegion::Path &path_low_flipped = next.paths[flip ? NextMonotonousRegion::LowHigh : NextMonotonousRegion::HighHigh]; - NextMonotonousRegion::Path &path_high = next.paths[flip ? NextMonotonousRegion::HighHigh : NextMonotonousRegion::LowHigh]; - NextMonotonousRegion::Path &path_high_flipped = next.paths[flip ? NextMonotonousRegion::LowLow : NextMonotonousRegion::HighLow]; - next_candidates.emplace_back(NextCandidate{ &next, &path_low, &path_low_flipped, path_cost(path_low), false }); - next_candidates.emplace_back(NextCandidate{ &next, &path_high, &path_high_flipped, path_cost(path_high), true }); - } - } - //std::sort(next_candidates.begin(), next_candidates.end(), [](const auto &l, const auto &r) { l.dist < r.dist; }); - float dice = float(rng()) / float(rng.max()); - std::vector::iterator take_path; - if (dice < probability_take_best) { - // Take the lowest cost path. - take_path = std::min_element(next_candidates.begin(), next_candidates.end(), [](auto &l, auto &r){ return l.cost < r.cost; }); - } else { - // Take the path based on the cost. - // Calculate the total cost. - float total_cost = std::accumulate(next_candidates.begin(), next_candidates.end(), 0.f, [](const float l, const NextCandidate& r) { return l + r.cost; }); - // Take a random path based on the cost. - float cost_threshold = floor(float(rng()) * total_cost / float(rng.max())); - take_path = next_candidates.end(); - -- take_path; - for (auto it = next_candidates.begin(); it < next_candidates.end(); ++ it) - if (cost_threshold -= it->cost <= 0.) { - take_path = it; - break; - } - } - // Extend the path. - NextMonotonousRegion &next_region = *take_path->region; - bool next_dir = take_path->dir; - path.back().next = take_path->link; - path.back().next_flipped = take_path->link_flipped; - path.emplace_back(MonotonousRegionLink{ next_region.region, next_dir }); - // Decrease the number of next block dependencies. - -- left_neighbors_unprocessed[next_region.region - regions.data()]; - // Update pheromones along this link. - take_path->link->pheromone = (1.f - pheromone_evaporation) * take_path->link->pheromone + pheromone_evaporation * pheromone_initial_deposit; + // Take the path based on the cost. + // Calculate the total cost. + float total_cost = std::accumulate(next_candidates.begin(), next_candidates.end(), 0.f, [](const float l, const NextCandidate& r) { return l + r.cost; }); + // Take a random path based on the cost. + float cost_threshold = floor(float(rng()) * total_cost / float(rng.max())); + take_path = next_candidates.end(); + -- take_path; + for (auto it = next_candidates.begin(); it < next_candidates.end(); ++ it) + if (cost_threshold -= it->cost <= 0.) { + take_path = it; + break; + } } + // Move the other right neighbors with satisified constraints to the queue. + bool direct_neighbor_taken = take_path - next_candidates.begin() < num_direct_neighbors; + for (std::vector::iterator it_next_candidate = next_candidates.begin(); it_next_candidate != next_candidates.begin() + num_direct_neighbors; ++ it_next_candidate) + if ((queue.empty() || it_next_candidate->region != queue.back()) && it_next_candidate->region != take_path->region) + queue.emplace_back(it_next_candidate->region); + if (take_path - next_candidates.begin() >= num_direct_neighbors) { + // Remove the selected path from the queue. + auto it = std::find(queue.begin(), queue.end(), take_path->region); + *it = queue.back(); + queue.pop_back(); + } + // Extend the path. + MonotonousRegion *next_region = take_path->region; + bool next_dir = take_path->dir; + path.back().next = take_path->link; + path.back().next_flipped = take_path->link_flipped; + path.emplace_back(MonotonousRegionLink{ next_region, next_dir }); + // Update pheromones along this link. + take_path->link->pheromone = (1.f - pheromone_evaporation) * take_path->link->pheromone + pheromone_evaporation * pheromone_initial_deposit; } // Perform 3-opt local optimization of the path. monotonous_3_opt(path, segs); // Measure path length. - float path_length = std::accumulate(path.begin(), path.end(), 0.f, [](const float l, const MonotonousRegionLink& r) { return l + r.next->length; }); + assert(! path.empty()); + float path_length = std::accumulate(path.begin(), path.end() - 1, + path.back().region->length(path.back().flipped), + [&path_matrix](const float l, const MonotonousRegionLink &r) { + const MonotonousRegionLink &next = *(&r + 1); + return l + r.region->length(r.flipped) + path_matrix(*r.region, r.flipped, *next.region, next.flipped).length; + }); // Save the shortest path. if (path_length < best_path_length) { best_path_length = path_length; - std::swap(best_path_length, path_length); + std::swap(best_path, path); } } // Reinforce the path feromones with the best path. - float total_cost = best_path_length; - for (MonotonousRegionLink &link : path) + float total_cost = best_path_length + EPSILON; + for (size_t i = 0; i + 1 < path.size(); ++ i) { + MonotonousRegionLink &link = path[i]; link.next->pheromone = (1.f - pheromone_evaporation) * link.next->pheromone + pheromone_evaporation / total_cost; + } } return best_path; @@ -2055,76 +2140,76 @@ static void polylines_from_paths(const std::vector &path, } for (;;) { - const SegmentedIntersectionLine &seg = segs[i_vline]; - const SegmentIntersection *intrsctn = &seg.intersections[i_intersection]; - const bool going_up = intrsctn->is_low(); + const SegmentedIntersectionLine &vline = segs[i_vline]; + const SegmentIntersection *it = &vline.intersections[i_intersection]; + const bool going_up = it->is_low(); if (polyline == nullptr) { polylines_out.emplace_back(); polyline = &polylines_out.back(); // Extend the infill line up to the outer contour. - polyline->points.emplace_back(seg.pos, (intrsctn + (going_up ? - 1 : 1))->pos()); + polyline->points.emplace_back(vline.pos, (it + (going_up ? - 1 : 1))->pos()); } else - polyline->points.emplace_back(seg.pos, intrsctn->pos()); + polyline->points.emplace_back(vline.pos, it->pos()); - int iright = intrsctn->right_horizontal(); + int iright = it->right_horizontal(); if (going_up) { // Consume the complete vertical segment up to the inner contour. for (;;) { do { - ++ intrsctn; - iright = std::max(iright, intrsctn->right_horizontal()); - } while (intrsctn->type != SegmentIntersection::INNER_HIGH); - polyline->points.emplace_back(seg.pos, intrsctn->pos()); - int inext = intrsctn->vertical_up(); + ++ it; + iright = std::max(iright, it->right_horizontal()); + } while (it->type != SegmentIntersection::INNER_HIGH); + polyline->points.emplace_back(vline.pos, it->pos()); + int inext = it->vertical_up(); if (inext == -1) break; - const Polygon &poly = poly_with_offset.contour(intrsctn->iContour); - assert(intrsctn->iContour == seg.intersections[inext].iContour); - emit_perimeter_segment_on_vertical_line(poly_with_offset, segs, i_vline, intrsctn->iContour, i_intersection, inext, *polyline, intrsctn->has_right_vertical_up()); - intrsctn = seg.intersections.data() + inext; + const Polygon &poly = poly_with_offset.contour(it->iContour); + assert(it->iContour == vline.intersections[inext].iContour); + emit_perimeter_segment_on_vertical_line(poly_with_offset, segs, i_vline, it->iContour, it - vline.intersections.data(), inext, *polyline, it->has_left_vertical_up()); + it = vline.intersections.data() + inext; } } else { // Going down. - assert(intrsctn->is_high()); + assert(it->is_high()); assert(i_intersection > 0); for (;;) { do { - -- intrsctn; - if (int iright_new = intrsctn->right_horizontal(); iright_new != -1) + -- it; + if (int iright_new = it->right_horizontal(); iright_new != -1) iright = iright_new; - } while (intrsctn->type != SegmentIntersection::INNER_LOW); - polyline->points.emplace_back(seg.pos, intrsctn->pos()); - int inext = intrsctn->vertical_down(); + } while (it->type != SegmentIntersection::INNER_LOW); + polyline->points.emplace_back(vline.pos, it->pos()); + int inext = it->vertical_down(); if (inext == -1) break; - const Polygon &poly = poly_with_offset.contour(intrsctn->iContour); - assert(intrsctn->iContour == seg.intersections[inext].iContour); - emit_perimeter_segment_on_vertical_line(poly_with_offset, segs, i_vline, intrsctn->iContour, intrsctn - seg.intersections.data(), inext, *polyline, intrsctn->has_right_vertical_down()); - intrsctn = seg.intersections.data() + inext; + const Polygon &poly = poly_with_offset.contour(it->iContour); + assert(it->iContour == vline.intersections[inext].iContour); + emit_perimeter_segment_on_vertical_line(poly_with_offset, segs, i_vline, it->iContour, it - vline.intersections.data(), inext, *polyline, it->has_right_vertical_down()); + it = vline.intersections.data() + inext; } } if (i_vline == region.right.vline) break; - int inext = intrsctn->right_horizontal(); - if (inext != -1 && intrsctn->next_on_contour_quality == SegmentIntersection::LinkQuality::Valid) { + int inext = it->right_horizontal(); + if (inext != -1 && it->next_on_contour_quality == SegmentIntersection::LinkQuality::Valid) { // Emit a horizontal connection contour. - emit_perimeter_prev_next_segment(poly_with_offset, segs, i_vline, intrsctn->iContour, intrsctn - seg.intersections.data(), inext, *polyline, true); + emit_perimeter_prev_next_segment(poly_with_offset, segs, i_vline, it->iContour, it - vline.intersections.data(), inext, *polyline, true); i_intersection = inext; } else { // Finish the current vertical line, - going_up ? ++ intrsctn : -- intrsctn; - assert(intrsctn->is_outer()); - assert(intrsctn->is_high() == going_up); - polyline->points.back() = Point(seg.pos, intrsctn->pos()); + going_up ? ++ it : -- it; + assert(it->is_outer()); + assert(it->is_high() == going_up); + polyline->points.back() = Point(vline.pos, it->pos()); finish_polyline(); if (inext == -1) { // Find the end of the next overlapping vertical segment. const SegmentedIntersectionLine &vline_right = segs[i_vline + 1]; const SegmentIntersection *right = going_up ? &vertical_run_top(vline_right, vline_right.intersections[iright]) : &vertical_run_bottom(vline_right, vline_right.intersections[iright]); - i_intersection = right - vline_right.intersections.data(); + i_intersection = int(right - vline_right.intersections.data()); } else i_intersection = inext; } @@ -2132,6 +2217,18 @@ static void polylines_from_paths(const std::vector &path, ++ i_vline; } } + + if (polyline != nullptr) { + // Finish the current vertical line, + const MonotonousRegion ®ion = *path.back().region; + const SegmentedIntersectionLine &vline = segs[region.right.vline]; + const SegmentIntersection *ip = &vline.intersections[region.right_intersection_point(path.back().flipped)]; + assert(ip->is_inner()); + ip->is_low() ? -- ip : ++ ip; + assert(ip->is_outer()); + polyline->points.back() = Point(vline.pos, ip->pos()); + finish_polyline(); + } } bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillParams ¶ms, float angleBase, float pattern_shift, Polylines &polylines_out) @@ -2224,13 +2321,16 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillP svg.Close(); #endif /* SLIC3R_DEBUG */ + //FIXME this is a hack to get the monotonous infill rolling. We likely want a smarter switch, likely based on user decison. bool monotonous_infill = params.density > 0.99; if (monotonous_infill) { std::vector regions = generate_montonous_regions(segs); connect_monotonous_regions(regions, segs); - std::mt19937_64 rng; - std::vector path = chain_monotonous_regions(regions, segs, rng); - polylines_from_paths(path, poly_with_offset, segs, polylines_out); + if (! regions.empty()) { + std::mt19937_64 rng; + std::vector path = chain_monotonous_regions(regions, poly_with_offset, segs, rng); + polylines_from_paths(path, poly_with_offset, segs, polylines_out); + } } else traverse_graph_generate_polylines(poly_with_offset, params, this->link_max_length, segs, polylines_out); diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 01a804ee7..b5890f578 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -3439,10 +3439,13 @@ void GCode::ObjectByExtruder::Island::Region::append(const Type type, const Extr // First we append the entities, there are eec->entities.size() of them: size_t old_size = perimeters_or_infills->size(); - size_t new_size = old_size + eec->entities.size(); + size_t new_size = old_size + (eec->can_reverse() ? eec->entities.size() : 1); perimeters_or_infills->reserve(new_size); - for (auto* ee : eec->entities) - perimeters_or_infills->emplace_back(ee); + if (eec->can_reverse()) { + for (auto* ee : eec->entities) + perimeters_or_infills->emplace_back(ee); + } else + perimeters_or_infills->emplace_back(const_cast(eec)); if (copies_extruder != nullptr) { // Don't reallocate overrides if not needed. From 033548a56873ef5d827e0fdfa3826b4827559b55 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Sat, 25 Apr 2020 08:15:04 +0200 Subject: [PATCH 04/16] Introduction of Monotonous infill type. Fill no-sort only for monotonous and ironing infills. --- src/libslic3r/Fill/Fill.cpp | 26 +- src/libslic3r/Fill/FillBase.cpp | 2 +- src/libslic3r/Fill/FillBase.hpp | 3 + src/libslic3r/Fill/FillRectilinear2.cpp | 401 ++++++++++++++---------- src/libslic3r/Fill/FillRectilinear2.hpp | 19 +- src/libslic3r/PrintConfig.cpp | 4 +- src/libslic3r/PrintConfig.hpp | 3 +- src/libslic3r/PrintObject.cpp | 1 + src/libslic3r/ShortestPath.cpp | 2 +- src/libslic3r/SupportMaterial.cpp | 16 +- 10 files changed, 286 insertions(+), 191 deletions(-) diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index f62b3ab25..ad1830e5f 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -373,7 +373,11 @@ void Layer::make_fills() // Spacing is modified by the filler to indicate adjustments. Reset it for each expolygon. f->spacing = surface_fill.params.spacing; surface_fill.surface.expolygon = std::move(expoly); - Polylines polylines = f->fill_surface(&surface_fill.surface, params); + Polylines polylines; + try { + polylines = f->fill_surface(&surface_fill.surface, params); + } catch (InfillFailedException &) { + } if (! polylines.empty()) { // calculate actual flow from spacing (which might have been adjusted by the infill // pattern generator) @@ -496,10 +500,11 @@ void Layer::make_ironing() if (! layerm->slices.empty()) { IroningParams ironing_params; const PrintRegionConfig &config = layerm->region()->config(); - if (config.ironing_type == IroningType::AllSolid || - (config.top_solid_layers > 0 && - (config.ironing_type == IroningType::TopSurfaces || - (config.ironing_type == IroningType::TopmostOnly && layerm->layer()->upper_layer == nullptr)))) { + if (config.ironing && + (config.ironing_type == IroningType::AllSolid || + (config.top_solid_layers > 0 && + (config.ironing_type == IroningType::TopSurfaces || + (config.ironing_type == IroningType::TopmostOnly && layerm->layer()->upper_layer == nullptr))))) { if (config.perimeter_extruder == config.solid_infill_extruder || config.perimeters == 0) { // Iron the whole face. ironing_params.extruder = config.solid_infill_extruder; @@ -528,6 +533,7 @@ void Layer::make_ironing() fill.overlap = 0; fill_params.density = 1.; fill_params.dont_connect = true; + fill_params.monotonous = true; for (size_t i = 0; i < by_extruder.size(); ++ i) { // Find span of regions equivalent to the ironing operation. @@ -562,13 +568,17 @@ void Layer::make_ironing() Surface surface_fill(stTop, ExPolygon()); for (ExPolygon &expoly : ironing_areas) { surface_fill.expolygon = std::move(expoly); - Polylines polylines = fill.fill_surface(&surface_fill, fill_params); + Polylines polylines; + try { + polylines = fill.fill_surface(&surface_fill, fill_params); + } catch (InfillFailedException &) { + } if (! polylines.empty()) { // Save into layer. ExtrusionEntityCollection *eec = nullptr; ironing_params.layerm->fills.entities.push_back(eec = new ExtrusionEntityCollection()); - //FIXME we may not want to sort a monotonous infill. - eec->no_sort = false; + // Don't sort the ironing infill lines as they are monotonously ordered. + eec->no_sort = true; extrusion_entities_append_paths( eec->entities, std::move(polylines), erIroning, diff --git a/src/libslic3r/Fill/FillBase.cpp b/src/libslic3r/Fill/FillBase.cpp index 3b200769c..c760218c0 100644 --- a/src/libslic3r/Fill/FillBase.cpp +++ b/src/libslic3r/Fill/FillBase.cpp @@ -27,7 +27,7 @@ Fill* Fill::new_from_type(const InfillPattern type) case ip3DHoneycomb: return new Fill3DHoneycomb(); case ipGyroid: return new FillGyroid(); case ipRectilinear: return new FillRectilinear2(); -// case ipRectilinear: return new FillRectilinear(); + case ipMonotonous: return new FillMonotonous(); case ipLine: return new FillLine(); case ipGrid: return new FillGrid2(); case ipTriangles: return new FillTriangles(); diff --git a/src/libslic3r/Fill/FillBase.hpp b/src/libslic3r/Fill/FillBase.hpp index 9fb37d2c0..083cb86ce 100644 --- a/src/libslic3r/Fill/FillBase.hpp +++ b/src/libslic3r/Fill/FillBase.hpp @@ -37,6 +37,9 @@ struct FillParams // Don't adjust spacing to fill the space evenly. bool dont_adjust { true }; + // Monotonous infill - strictly left to right for better surface quality of top infills. + bool monotonous { false }; + // For Honeycomb. // we were requested to complete each loop; // in this case we don't try to make more continuous paths diff --git a/src/libslic3r/Fill/FillRectilinear2.cpp b/src/libslic3r/Fill/FillRectilinear2.cpp index 3d5710d29..6dd6bb883 100644 --- a/src/libslic3r/Fill/FillRectilinear2.cpp +++ b/src/libslic3r/Fill/FillRectilinear2.cpp @@ -951,110 +951,111 @@ static void connect_segment_intersections_by_contours(const ExPolygonWithOffset const SegmentedIntersectionLine *il_next = i_vline + 1 < segs.size() ? &segs[i_vline + 1] : nullptr; for (int i_intersection = 0; i_intersection < il.intersections.size(); ++ i_intersection) { - SegmentIntersection &itsct = il.intersections[i_intersection]; - const Polygon &poly = poly_with_offset.contour(itsct.iContour); + SegmentIntersection &itsct = il.intersections[i_intersection]; + const Polygon &poly = poly_with_offset.contour(itsct.iContour); + const bool forward = itsct.is_low(); // == poly_with_offset.is_contour_ccw(intrsctn->iContour); // 1) Find possible connection points on the previous / next vertical line. // Find an intersection point on il_prev, intersecting i_intersection // at the same orientation as i_intersection, and being closest to i_intersection // in the number of contour segments, when following the direction of the contour. //FIXME this has O(n) time complexity. Likely an O(log(n)) scheme is possible. - int iprev = -1; + int iprev = -1; + int d_prev = std::numeric_limits::max(); if (il_prev) { - int dmin = std::numeric_limits::max(); for (int i = 0; i < il_prev->intersections.size(); ++ i) { const SegmentIntersection &itsct2 = il_prev->intersections[i]; if (itsct.iContour == itsct2.iContour && itsct.type == itsct2.type) { // The intersection points lie on the same contour and have the same orientation. // Find the intersection point with a shortest path in the direction of the contour. - int d = distance_of_segmens(poly, itsct.iSegment, itsct2.iSegment, false); - if (d < dmin) { + int d = distance_of_segmens(poly, itsct2.iSegment, itsct.iSegment, forward); + if (d < d_prev) { iprev = i; - dmin = d; + d_prev = d; } } } } + // The same for il_next. - int inext = -1; - if (il_next) { - int dmin = std::numeric_limits::max(); + int inext = -1; + int d_next = std::numeric_limits::max(); + if (il_next) { for (int i = 0; i < il_next->intersections.size(); ++ i) { const SegmentIntersection &itsct2 = il_next->intersections[i]; if (itsct.iContour == itsct2.iContour && itsct.type == itsct2.type) { // The intersection points lie on the same contour and have the same orientation. // Find the intersection point with a shortest path in the direction of the contour. - int d = distance_of_segmens(poly, itsct.iSegment, itsct2.iSegment, true); - if (d < dmin) { + int d = distance_of_segmens(poly, itsct.iSegment, itsct2.iSegment, forward); + if (d < d_next) { inext = i; - dmin = d; + d_next = d; } } } } // 2) Find possible connection points on the same vertical line. - int iabove = -1; + bool same_prev = false; + bool same_next = false; // Does the perimeter intersect the current vertical line above intrsctn? - for (int i = i_intersection + 1; i < il.intersections.size(); ++ i) - if (il.intersections[i].iContour == itsct.iContour) { - iabove = i; - break; - } - // Does the perimeter intersect the current vertical line below intrsctn? - int ibelow = -1; - for (int i = i_intersection - 1; i >= 0; -- i) - if (il.intersections[i].iContour == itsct.iContour) { - ibelow = i; - break; + for (int i = 0; i < il.intersections.size(); ++ i) + if (const SegmentIntersection &it2 = il.intersections[i]; + i != i_intersection && it2.iContour == itsct.iContour && it2.type != itsct.type) { + int d = distance_of_segmens(poly, it2.iSegment, itsct.iSegment, forward); + if (d < d_prev) { + iprev = i; + d_prev = d; + same_prev = true; + } + d = distance_of_segmens(poly, itsct.iSegment, it2.iSegment, forward); + if (d < d_next) { + inext = i; + d_next = d; + same_next = true; + } } + assert(iprev >= 0); + assert(inext >= 0); - // 3) Sort the intersection points, clear iprev / inext / iSegBelow / iSegAbove, - // if it is preceded by any other intersection point along the contour. - // The perimeter contour orientation. - const bool forward = itsct.is_low(); // == poly_with_offset.is_contour_ccw(intrsctn->iContour); - { - int d_horiz = (iprev == -1) ? std::numeric_limits::max() : - distance_of_segmens(poly, il_prev->intersections[iprev].iSegment, itsct.iSegment, forward); - int d_down = (ibelow == -1) ? std::numeric_limits::max() : - distance_of_segmens(poly, il.intersections[ibelow].iSegment, itsct.iSegment, forward); - int d_up = (iabove == -1) ? std::numeric_limits::max() : - distance_of_segmens(poly, il.intersections[iabove].iSegment, itsct.iSegment, forward); - if (d_horiz < std::min(d_down, d_up)) { - itsct.prev_on_contour = iprev; - itsct.prev_on_contour_type = SegmentIntersection::LinkType::Horizontal; - } else if (d_down < d_up) { - itsct.prev_on_contour = ibelow; - itsct.prev_on_contour_type = SegmentIntersection::LinkType::Down; - } else { - itsct.prev_on_contour = iabove; - itsct.prev_on_contour_type = SegmentIntersection::LinkType::Up; - } - // There should always be a link to the next intersection point on the same contour. - assert(itsct.prev_on_contour != -1); - } - { - int d_horiz = (inext == -1) ? std::numeric_limits::max() : - distance_of_segmens(poly, itsct.iSegment, il_next->intersections[inext].iSegment, forward); - int d_down = (ibelow == -1) ? std::numeric_limits::max() : - distance_of_segmens(poly, itsct.iSegment, il.intersections[ibelow].iSegment, forward); - int d_up = (iabove == -1) ? std::numeric_limits::max() : - distance_of_segmens(poly, itsct.iSegment, il.intersections[iabove].iSegment, forward); - if (d_horiz < std::min(d_down, d_up)) { - itsct.next_on_contour = inext; - itsct.next_on_contour_type = SegmentIntersection::LinkType::Horizontal; - } else if (d_down < d_up) { - itsct.next_on_contour = ibelow; - itsct.next_on_contour_type = SegmentIntersection::LinkType::Down; - } else { - itsct.next_on_contour = iabove; - itsct.next_on_contour_type = SegmentIntersection::LinkType::Up; - } - // There should always be a link to the next intersection point on the same contour. - assert(itsct.next_on_contour != -1); - } + itsct.prev_on_contour = iprev; + itsct.prev_on_contour_type = same_prev ? + (iprev < i_intersection ? SegmentIntersection::LinkType::Down : SegmentIntersection::LinkType::Up) : + SegmentIntersection::LinkType::Horizontal; + itsct.next_on_contour = inext; + itsct.next_on_contour_type = same_next ? + (inext < i_intersection ? SegmentIntersection::LinkType::Down : SegmentIntersection::LinkType::Up) : + SegmentIntersection::LinkType::Horizontal; } } + +#ifndef NDEBUG + // Validate the connectivity. + for (size_t i_vline = 0; i_vline + 1 < segs.size(); ++ i_vline) { + const SegmentedIntersectionLine &il_left = segs[i_vline]; + const SegmentedIntersectionLine &il_right = segs[i_vline + 1]; + for (const SegmentIntersection &it : il_left.intersections) { + if (it.has_right_horizontal()) { + const SegmentIntersection &it_right = il_right.intersections[it.right_horizontal()]; + // For a right link there is a symmetric left link. + assert(it.iContour == it_right.iContour); + assert(it.type == it_right.type); + assert(it_right.has_left_horizontal()); + assert(it_right.left_horizontal() == int(&it - il_left.intersections.data())); + } + } + for (const SegmentIntersection &it : il_right.intersections) { + if (it.has_left_horizontal()) { + const SegmentIntersection &it_left = il_left.intersections[it.left_horizontal()]; + // For a right link there is a symmetric left link. + assert(it.iContour == it_left.iContour); + assert(it.type == it_left.type); + assert(it_left.has_right_horizontal()); + assert(it_left.right_horizontal() == int(&it - il_right.intersections.data())); + } + } + } +#endif /* NDEBUG */ } // Find the last INNER_HIGH intersection starting with INNER_LOW, that is followed by OUTER_HIGH intersection. @@ -1548,7 +1549,7 @@ struct MonotonousRegion // If false, then ending at the same side as starting. bool flips { false }; - bool length(bool region_flipped) const { return region_flipped ? len2 : len1; } + float length(bool region_flipped) const { return region_flipped ? len2 : len1; } int left_intersection_point(bool region_flipped) const { return region_flipped ? left.high : left.low; } int right_intersection_point(bool region_flipped) const { return (region_flipped == flips) ? right.low : right.high; } @@ -1580,12 +1581,16 @@ struct MonotonousRegionLink class AntPathMatrix { public: - AntPathMatrix(const std::vector ®ions, const ExPolygonWithOffset &poly_with_offset, const std::vector &segs) : + AntPathMatrix( + const std::vector ®ions, + const ExPolygonWithOffset &poly_with_offset, + const std::vector &segs, + const float initial_pheromone) : m_regions(regions), m_poly_with_offset(poly_with_offset), m_segs(segs), // From end of one region to the start of another region, both flipped or not flipped. - m_matrix(regions.size() * regions.size() * 4) {} + m_matrix(regions.size() * regions.size() * 4, AntPath{ -1., -1., initial_pheromone}) {} AntPath& operator()(const MonotonousRegion ®ion_from, bool flipped_from, const MonotonousRegion ®ion_to, bool flipped_to) { @@ -1602,12 +1607,12 @@ public: int i_right = vline_from.intersections[i_from].right_horizontal(); if (i_right == i_to && vline_from.intersections[i_from].next_on_contour_quality == SegmentIntersection::LinkQuality::Valid) { // Measure length along the contour. - path.length = measure_perimeter_next_segment_length(m_poly_with_offset, m_segs, region_from.right.vline, i_from, i_to); + path.length = unscale(measure_perimeter_next_segment_length(m_poly_with_offset, m_segs, region_from.right.vline, i_from, i_to)); } } if (path.length == -1.) { // Just apply the Eucledian distance of the end points. - path.length = Vec2f(vline_to.pos - vline_from.pos, vline_to.intersections[i_to].pos() - vline_from.intersections[i_from].pos()).norm(); + path.length = unscale(Vec2f(vline_to.pos - vline_from.pos, vline_to.intersections[i_to].pos() - vline_from.intersections[i_from].pos()).norm()); } path.visibility = 1. / (path.length + EPSILON); } @@ -1682,86 +1687,98 @@ static SegmentIntersection& vertical_run_top(SegmentedIntersectionLine& vline, S return const_cast(vertical_run_top(std::as_const(vline), std::as_const(start))); } -static SegmentIntersection* left_overlap_bottom(SegmentIntersection &start, SegmentIntersection &end, SegmentedIntersectionLine &vline_left) +static SegmentIntersection* overlap_bottom(SegmentIntersection &start, SegmentIntersection &end, SegmentedIntersectionLine &vline_this, SegmentedIntersectionLine &vline_other, SegmentIntersection::Side side) { - SegmentIntersection *left = nullptr; - for (SegmentIntersection *it = &start; it <= &end; ++ it) { - int i = it->left_horizontal(); - if (i != -1) { - left = &vline_left.intersections[i]; - break; - } - } - return left == nullptr ? nullptr : &vertical_run_bottom(vline_left, *left); + SegmentIntersection *other = nullptr; + assert(start.is_inner()); + assert(end.is_inner()); + const SegmentIntersection *it = &start; + for (;;) { + if (it->is_inner()) { + int i = it->horizontal(side); + if (i != -1) { + other = &vline_other.intersections[i]; + break; + } + if (it == &end) + break; + } + if (it->type != SegmentIntersection::INNER_HIGH) + ++ it; + else if ((it + 1)->type == SegmentIntersection::INNER_LOW) + ++ it; + else { + int up = it->vertical_up(); + if (up == -1 || it->vertical_up_quality() != SegmentIntersection::LinkQuality::Valid) + break; + it = &vline_this.intersections[up]; + assert(it->type == SegmentIntersection::INNER_LOW); + } + } + return other == nullptr ? nullptr : &vertical_run_bottom(vline_other, *other); } -static SegmentIntersection* left_overlap_top(SegmentIntersection &start, SegmentIntersection &end, SegmentedIntersectionLine &vline_left) +static SegmentIntersection* overlap_top(SegmentIntersection &start, SegmentIntersection &end, SegmentedIntersectionLine &vline_this, SegmentedIntersectionLine &vline_other, SegmentIntersection::Side side) { - SegmentIntersection *left = nullptr; - for (SegmentIntersection *it = &end; it >= &start; -- it) { - int i = it->left_horizontal(); - if (i != -1) { - left = &vline_left.intersections[i]; - break; - } - } - return left == nullptr ? nullptr : &vertical_run_top(vline_left, *left); + SegmentIntersection *other = nullptr; + assert(start.is_inner()); + assert(end.is_inner()); + const SegmentIntersection *it = &end; + for (;;) { + if (it->is_inner()) { + int i = it->horizontal(side); + if (i != -1) { + other = &vline_other.intersections[i]; + break; + } + if (it == &start) + break; + } + if (it->type != SegmentIntersection::INNER_LOW) + -- it; + else if ((it - 1)->type == SegmentIntersection::INNER_HIGH) + -- it; + else { + int down = it->vertical_down(); + if (down == -1 || it->vertical_down_quality() != SegmentIntersection::LinkQuality::Valid) + break; + it = &vline_this.intersections[down]; + assert(it->type == SegmentIntersection::INNER_HIGH); + } + } + return other == nullptr ? nullptr : &vertical_run_top(vline_other, *other); } -static std::pair left_overlap(SegmentIntersection &start, SegmentIntersection &end, SegmentedIntersectionLine &vline_left) +static std::pair left_overlap(SegmentIntersection &start, SegmentIntersection &end, SegmentedIntersectionLine &vline_this, SegmentedIntersectionLine &vline_left) { std::pair out(nullptr, nullptr); - out.first = left_overlap_bottom(start, end, vline_left); + out.first = overlap_bottom(start, end, vline_this, vline_left, SegmentIntersection::Side::Left); if (out.first != nullptr) - out.second = left_overlap_top(start, end, vline_left); + out.second = overlap_top(start, end, vline_this, vline_left, SegmentIntersection::Side::Left); + assert((out.first == nullptr && out.second == nullptr) || out.first < out.second); return out; } -static std::pair left_overlap(std::pair &start_end, SegmentedIntersectionLine &vline_left) +static std::pair left_overlap(std::pair &start_end, SegmentedIntersectionLine &vline_this, SegmentedIntersectionLine &vline_left) { assert((start_end.first == nullptr) == (start_end.second == nullptr)); - return start_end.first == nullptr ? start_end : left_overlap(*start_end.first, *start_end.second, vline_left); + return start_end.first == nullptr ? start_end : left_overlap(*start_end.first, *start_end.second, vline_this, vline_left); } -static SegmentIntersection* right_overlap_bottom(SegmentIntersection &start, SegmentIntersection &end, SegmentedIntersectionLine &vline_right) -{ - SegmentIntersection *right = nullptr; - for (SegmentIntersection *it = &start; it <= &end; ++ it) { - int i = it->right_horizontal(); - if (i != -1) { - right = &vline_right.intersections[i]; - break; - } - } - return right == nullptr ? nullptr : &vertical_run_bottom(vline_right, *right); -} - -static SegmentIntersection* right_overlap_top(SegmentIntersection &start, SegmentIntersection &end, SegmentedIntersectionLine &vline_right) -{ - SegmentIntersection *right = nullptr; - for (SegmentIntersection *it = &end; it >= &start; -- it) { - int i = it->right_horizontal(); - if (i != -1) { - right = &vline_right.intersections[i]; - break; - } - } - return right == nullptr ? nullptr : &vertical_run_top(vline_right, *right); -} - -static std::pair right_overlap(SegmentIntersection &start, SegmentIntersection &end, SegmentedIntersectionLine &vline_right) +static std::pair right_overlap(SegmentIntersection &start, SegmentIntersection &end, SegmentedIntersectionLine &vline_this, SegmentedIntersectionLine &vline_right) { std::pair out(nullptr, nullptr); - out.first = right_overlap_bottom(start, end, vline_right); + out.first = overlap_bottom(start, end, vline_this, vline_right, SegmentIntersection::Side::Right); if (out.first != nullptr) - out.second = right_overlap_top(start, end, vline_right); - return out; + out.second = overlap_top(start, end, vline_this, vline_right, SegmentIntersection::Side::Right); + assert((out.first == nullptr && out.second == nullptr) || out.first < out.second); + return out; } -static std::pair right_overlap(std::pair &start_end, SegmentedIntersectionLine &vline_right) +static std::pair right_overlap(std::pair &start_end, SegmentedIntersectionLine &vline_this, SegmentedIntersectionLine &vline_right) { assert((start_end.first == nullptr) == (start_end.second == nullptr)); - return start_end.first == nullptr ? start_end : right_overlap(*start_end.first, *start_end.second, vline_right); + return start_end.first == nullptr ? start_end : right_overlap(*start_end.first, *start_end.second, vline_this, vline_right); } static std::vector generate_montonous_regions(std::vector &segs) @@ -1771,9 +1788,11 @@ static std::vector generate_montonous_regions(std::vectorconsumed_vertical_up) { @@ -1791,7 +1810,7 @@ static std::vector generate_montonous_regions(std::vector right = right_overlap(left, vline_right); + std::pair right = right_overlap(left, vline_left, vline_right); if (right.first == nullptr) // No neighbor at the right side of the current segment. break; @@ -1799,7 +1818,7 @@ static std::vector generate_montonous_regions(std::vector right_left = left_overlap(right, vline_left); + std::pair right_left = left_overlap(right, vline_right, vline_left); if (left != right_left) // Left & right draws don't overlap exclusively, right neighbor segment overlaps with multiple segments at its left. break; @@ -1843,7 +1862,7 @@ static void connect_monotonous_regions(std::vector ®ions, s if (region.left.vline > 0) { auto &vline = segs[region.left.vline]; auto &vline_left = segs[region.left.vline - 1]; - auto[lbegin, lend] = left_overlap(vline.intersections[region.left.low], vline.intersections[region.left.high], vline_left); + auto[lbegin, lend] = left_overlap(vline.intersections[region.left.low], vline.intersections[region.left.high], vline, vline_left); if (lbegin != nullptr) { for (;;) { MapType key(lbegin, nullptr); @@ -1862,7 +1881,7 @@ static void connect_monotonous_regions(std::vector ®ions, s if (region.right.vline + 1 < segs.size()) { auto &vline = segs[region.right.vline]; auto &vline_right = segs[region.right.vline + 1]; - auto [rbegin, rend] = right_overlap(vline.intersections[region.right.low], vline.intersections[region.right.high], vline_right); + auto [rbegin, rend] = right_overlap(vline.intersections[region.right.low], vline.intersections[region.right.high], vline, vline_right); if (rbegin != nullptr) { for (;;) { MapType key(rbegin, nullptr); @@ -1904,24 +1923,19 @@ void monotonous_3_opt(std::vector &path, const std::vector // exchange by copying the pieces. } +// #define SLIC3R_DEBUG_ANTS + +template +inline void print_ant(const std::string& fmt, TArgs&&... args) { +#ifdef SLIC3R_DEBUG_ANTS + std::cout << Slic3r::format(fmt, std::forward(args)...) << std::endl; +#endif +} + // Find a run through monotonous infill blocks using an 'Ant colony" optimization method. static std::vector chain_monotonous_regions( std::vector ®ions, const ExPolygonWithOffset &poly_with_offset, const std::vector &segs, std::mt19937_64 &rng) { - // Start point of a region (left) given the direction of the initial infill line. - auto region_start_point = [&segs](const MonotonousRegion ®ion, bool dir) { - const SegmentedIntersectionLine &vline = segs[region.left.vline]; - const SegmentIntersection &ipt = vline.intersections[dir ? region.left.high : region.left.low]; - return Vec2f(float(vline.pos), float(ipt.pos())); - }; - // End point of a region (right) given the direction of the initial infill line and whether the monotonous run contains - // even or odd number of vertical lines. - auto region_end_point = [&segs](const MonotonousRegion ®ion, bool dir) { - const SegmentedIntersectionLine &vline = segs[region.right.vline]; - const SegmentIntersection &ipt = vline.intersections[(dir == region.flips) ? region.right.low : region.right.high]; - return Vec2f(float(vline.pos), float(ipt.pos())); - }; - // Number of left neighbors (regions that this region depends on, this region cannot be printed before the regions left of it are printed). std::vector left_neighbors_unprocessed(regions.size(), 0); // Queue of regions, which have their left neighbors already printed. @@ -1945,15 +1959,13 @@ static std::vector chain_monotonous_regions( MonotonousRegion *region; AntPath *link; AntPath *link_flipped; - float cost; + float probability; bool dir; }; std::vector next_candidates; - AntPathMatrix path_matrix(regions, poly_with_offset, segs); - // How many times to repeat the ant simulation. - constexpr int num_runs = 10; + constexpr int num_rounds = 10; // With how many ants each of the run will be performed? constexpr int num_ants = 10; // Base (initial) pheromone level. @@ -1965,15 +1977,25 @@ static std::vector chain_monotonous_regions( // Exponents of the cost function. constexpr float pheromone_alpha = 1.f; // pheromone exponent constexpr float pheromone_beta = 2.f; // attractiveness weighted towards edge length - // Cost of traversing a link between two monotonous regions. - auto path_cost = [pheromone_alpha, pheromone_beta](AntPath &path) { + + AntPathMatrix path_matrix(regions, poly_with_offset, segs, pheromone_initial_deposit); + + // Probability (unnormalized) of traversing a link between two monotonous regions. + auto path_probability = [pheromone_alpha, pheromone_beta](AntPath &path) { return pow(path.pheromone, pheromone_alpha) * pow(path.visibility, pheromone_beta); }; - for (int run = 0; run < num_runs; ++ run) + +#ifdef SLIC3R_DEBUG_ANTS + static int irun = 0; + ++ irun; +#endif /* SLIC3R_DEBUG_ANTS */ + + for (int round = 0; round < num_rounds; ++ round) { for (int ant = 0; ant < num_ants; ++ ant) { // Find a new path following the pheromones deposited by the previous ants. + print_ant("Round %1% ant %2%", round, ant); path.clear(); queue = queue_initial; left_neighbors_unprocessed = left_neighbors_unprocessed_initial; @@ -1983,12 +2005,18 @@ static std::vector chain_monotonous_regions( *(queue.begin() + first_idx) = std::move(queue.back()); queue.pop_back(); assert(left_neighbors_unprocessed[path.back().region - regions.data()] == 0); + print_ant("\tRegion (%1%:%2%,%3%) (%4%:%5%,%6%)", + path.back().region->left.vline, + path.back().flipped ? path.back().region->left.high : path.back().region->left.low, + path.back().flipped ? path.back().region->left.low : path.back().region->left.high, + path.back().region->right.vline, + path.back().flipped == path.back().region->flips ? path.back().region->right.high : path.back().region->right.low, + path.back().flipped == path.back().region->flips ? path.back().region->right.low : path.back().region->right.high); while (! queue.empty() || ! path.back().region->right_neighbors.empty()) { // Chain. MonotonousRegion ®ion = *path.back().region; bool dir = path.back().flipped; - Vec2f end_pt = region_end_point(region, dir); // Sort by distance to pt. next_candidates.clear(); next_candidates.reserve(region.right_neighbors.size() * 2); @@ -2001,8 +2029,8 @@ static std::vector chain_monotonous_regions( AntPath &path1_flipped = path_matrix(region, ! dir, *next, true); AntPath &path2 = path_matrix(region, dir, *next, true); AntPath &path2_flipped = path_matrix(region, ! dir, *next, false); - next_candidates.emplace_back(NextCandidate{ next, &path1, &path1_flipped, path_cost(path1), false }); - next_candidates.emplace_back(NextCandidate{ next, &path2, &path2_flipped, path_cost(path2), true }); + next_candidates.emplace_back(NextCandidate{ next, &path1, &path1_flipped, path_probability(path1), false }); + next_candidates.emplace_back(NextCandidate{ next, &path2, &path2_flipped, path_probability(path2), true }); } } size_t num_direct_neighbors = next_candidates.size(); @@ -2014,28 +2042,30 @@ static std::vector chain_monotonous_regions( AntPath &path1_flipped = path_matrix(region, ! dir, *next, true); AntPath &path2 = path_matrix(region, dir, *next, true); AntPath &path2_flipped = path_matrix(region, ! dir, *next, false); - next_candidates.emplace_back(NextCandidate{ next, &path1, &path1_flipped, path_cost(path1), false }); - next_candidates.emplace_back(NextCandidate{ next, &path2, &path2_flipped, path_cost(path2), true }); + next_candidates.emplace_back(NextCandidate{ next, &path1, &path1_flipped, path_probability(path1), false }); + next_candidates.emplace_back(NextCandidate{ next, &path2, &path2_flipped, path_probability(path2), true }); } } float dice = float(rng()) / float(rng.max()); std::vector::iterator take_path; if (dice < probability_take_best) { - // Take the lowest cost path. - take_path = std::min_element(next_candidates.begin(), next_candidates.end(), [](auto &l, auto &r){ return l.cost < r.cost; }); + // Take the highest probability path. + take_path = std::max_element(next_candidates.begin(), next_candidates.end(), [](auto &l, auto &r){ return l.probability < r.probability; }); + print_ant("\tTaking best path at probability %1% below %2%", dice, probability_take_best); } else { - // Take the path based on the cost. - // Calculate the total cost. - float total_cost = std::accumulate(next_candidates.begin(), next_candidates.end(), 0.f, [](const float l, const NextCandidate& r) { return l + r.cost; }); - // Take a random path based on the cost. - float cost_threshold = floor(float(rng()) * total_cost / float(rng.max())); + // Take the path based on the probability. + // Calculate the total probability. + float total_probability = std::accumulate(next_candidates.begin(), next_candidates.end(), 0.f, [](const float l, const NextCandidate& r) { return l + r.probability; }); + // Take a random path based on the probability. + float probability_threshold = float(rng()) * total_probability / float(rng.max()); take_path = next_candidates.end(); -- take_path; for (auto it = next_candidates.begin(); it < next_candidates.end(); ++ it) - if (cost_threshold -= it->cost <= 0.) { + if ((probability_threshold -= it->probability) <= 0.) { take_path = it; break; } + print_ant("\tTaking path at probability threshold %1% of %2%", probability_threshold, total_probability); } // Move the other right neighbors with satisified constraints to the queue. bool direct_neighbor_taken = take_path - next_candidates.begin() < num_direct_neighbors; @@ -2054,6 +2084,23 @@ static std::vector chain_monotonous_regions( path.back().next = take_path->link; path.back().next_flipped = take_path->link_flipped; path.emplace_back(MonotonousRegionLink{ next_region, next_dir }); + print_ant("\tRegion (%1%:%2%,%3%) (%4%:%5%,%6%) length to prev %7%", + next_region->left.vline, + next_dir ? next_region->left.high : next_region->left.low, + next_dir ? next_region->left.low : next_region->left.high, + next_region->right.vline, + next_dir == next_region->flips ? next_region->right.high : next_region->right.low, + next_dir == next_region->flips ? next_region->right.low : next_region->right.high, + take_path->link->length); + + print_ant("\tRegion (%1%:%2%,%3%) (%4%:%5%,%6%)", + path.back().region->left.vline, + path.back().flipped ? path.back().region->left.high : path.back().region->left.low, + path.back().flipped ? path.back().region->left.low : path.back().region->left.high, + path.back().region->right.vline, + path.back().flipped == path.back().region->flips ? path.back().region->right.high : path.back().region->right.low, + path.back().flipped == path.back().region->flips ? path.back().region->right.low : path.back().region->right.high); + // Update pheromones along this link. take_path->link->pheromone = (1.f - pheromone_evaporation) * take_path->link->pheromone + pheromone_evaporation * pheromone_initial_deposit; } @@ -2070,6 +2117,7 @@ static std::vector chain_monotonous_regions( return l + r.region->length(r.flipped) + path_matrix(*r.region, r.flipped, *next.region, next.flipped).length; }); // Save the shortest path. + print_ant("\tThis length: %1%, shortest length: %2%", path_length, best_path_length); if (path_length < best_path_length) { best_path_length = path_length; std::swap(best_path, path); @@ -2322,7 +2370,7 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillP #endif /* SLIC3R_DEBUG */ //FIXME this is a hack to get the monotonous infill rolling. We likely want a smarter switch, likely based on user decison. - bool monotonous_infill = params.density > 0.99; + bool monotonous_infill = params.monotonous; // || params.density > 0.99; if (monotonous_infill) { std::vector regions = generate_montonous_regions(segs); connect_monotonous_regions(regions, segs); @@ -2379,6 +2427,17 @@ Polylines FillRectilinear2::fill_surface(const Surface *surface, const FillParam return polylines_out; } +Polylines FillMonotonous::fill_surface(const Surface *surface, const FillParams ¶ms) +{ + FillParams params2 = params; + params2.monotonous = true; + Polylines polylines_out; + if (! fill_surface_by_lines(surface, params2, 0.f, 0.f, polylines_out)) { + printf("FillMonotonous::fill_surface() failed to fill a region.\n"); + } + return polylines_out; +} + Polylines FillGrid2::fill_surface(const Surface *surface, const FillParams ¶ms) { // Each linear fill covers half of the target coverage. diff --git a/src/libslic3r/Fill/FillRectilinear2.hpp b/src/libslic3r/Fill/FillRectilinear2.hpp index 4459919b0..3fe95f19c 100644 --- a/src/libslic3r/Fill/FillRectilinear2.hpp +++ b/src/libslic3r/Fill/FillRectilinear2.hpp @@ -13,18 +13,27 @@ class FillRectilinear2 : public Fill { public: virtual Fill* clone() const { return new FillRectilinear2(*this); }; - virtual ~FillRectilinear2() {} + virtual ~FillRectilinear2() = default; virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms); protected: bool fill_surface_by_lines(const Surface *surface, const FillParams ¶ms, float angleBase, float pattern_shift, Polylines &polylines_out); }; +class FillMonotonous : public FillRectilinear2 +{ +public: + virtual Fill* clone() const { return new FillMonotonous(*this); }; + virtual ~FillMonotonous() = default; + virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms); + virtual bool no_sort() const { return true; } +}; + class FillGrid2 : public FillRectilinear2 { public: virtual Fill* clone() const { return new FillGrid2(*this); }; - virtual ~FillGrid2() {} + virtual ~FillGrid2() = default; virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms); protected: @@ -36,7 +45,7 @@ class FillTriangles : public FillRectilinear2 { public: virtual Fill* clone() const { return new FillTriangles(*this); }; - virtual ~FillTriangles() {} + virtual ~FillTriangles() = default; virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms); protected: @@ -48,7 +57,7 @@ class FillStars : public FillRectilinear2 { public: virtual Fill* clone() const { return new FillStars(*this); }; - virtual ~FillStars() {} + virtual ~FillStars() = default; virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms); protected: @@ -60,7 +69,7 @@ class FillCubic : public FillRectilinear2 { public: virtual Fill* clone() const { return new FillCubic(*this); }; - virtual ~FillCubic() {} + virtual ~FillCubic() = default; virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms); protected: diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 259b016e3..9c7a31d3e 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -418,18 +418,20 @@ void PrintConfigDef::init_fff_params() def->cli = "top-fill-pattern|external-fill-pattern|solid-fill-pattern"; def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); def->enum_values.push_back("rectilinear"); + def->enum_values.push_back("monotonous"); def->enum_values.push_back("concentric"); def->enum_values.push_back("hilbertcurve"); def->enum_values.push_back("archimedeanchords"); def->enum_values.push_back("octagramspiral"); def->enum_labels.push_back(L("Rectilinear")); + def->enum_labels.push_back(L("Monotonous")); def->enum_labels.push_back(L("Concentric")); def->enum_labels.push_back(L("Hilbert Curve")); def->enum_labels.push_back(L("Archimedean Chords")); def->enum_labels.push_back(L("Octagram Spiral")); // solid_fill_pattern is an obsolete equivalent to top_fill_pattern/bottom_fill_pattern. def->aliases = { "solid_fill_pattern", "external_fill_pattern" }; - def->set_default_value(new ConfigOptionEnum(ipRectilinear)); + def->set_default_value(new ConfigOptionEnum(ipMonotonous)); def = this->add("bottom_fill_pattern", coEnum); def->label = L("Bottom fill pattern"); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index a7d2d8270..b3dc3f154 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -34,7 +34,7 @@ enum PrintHostType { }; enum InfillPattern { - ipRectilinear, ipGrid, ipTriangles, ipStars, ipCubic, ipLine, ipConcentric, ipHoneycomb, ip3DHoneycomb, + ipRectilinear, ipMonotonous, ipGrid, ipTriangles, ipStars, ipCubic, ipLine, ipConcentric, ipHoneycomb, ip3DHoneycomb, ipGyroid, ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, ipCount, }; @@ -113,6 +113,7 @@ template<> inline const t_config_enum_values& ConfigOptionEnum::g static t_config_enum_values keys_map; if (keys_map.empty()) { keys_map["rectilinear"] = ipRectilinear; + keys_map["monotonous"] = ipMonotonous; keys_map["grid"] = ipGrid; keys_map["triangles"] = ipTriangles; keys_map["stars"] = ipStars; diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index a68a9605b..5d8d6a756 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -2629,6 +2629,7 @@ void PrintObject::combine_infill() // Because fill areas for rectilinear and honeycomb are grown // later to overlap perimeters, we need to counteract that too. ((region->config().fill_pattern == ipRectilinear || + region->config().fill_pattern == ipMonotonous || region->config().fill_pattern == ipGrid || region->config().fill_pattern == ipLine || region->config().fill_pattern == ipHoneycomb) ? 1.5f : 0.5f) * diff --git a/src/libslic3r/ShortestPath.cpp b/src/libslic3r/ShortestPath.cpp index 0aa897fd7..01f39872f 100644 --- a/src/libslic3r/ShortestPath.cpp +++ b/src/libslic3r/ShortestPath.cpp @@ -38,7 +38,7 @@ std::vector> chain_segments_closest_point(std::vector 1 && end_points[idx].chain_id == 0 && ((idx ^ 1) == 0 || could_reverse_func(idx >> 1)); + return (idx ^ this_idx) > 1 && end_points[idx].chain_id == 0 && ((idx & 1) == 0 || could_reverse_func(idx >> 1)); }); assert(next_idx < end_points.size()); EndPointType &end_point = end_points[next_idx]; diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp index 1d98bb0e6..72b4f465b 100644 --- a/src/libslic3r/SupportMaterial.cpp +++ b/src/libslic3r/SupportMaterial.cpp @@ -2317,10 +2317,15 @@ static inline void fill_expolygons_generate_paths( fill_params.dont_adjust = true; for (const ExPolygon &expoly : expolygons) { Surface surface(stInternal, expoly); + Polylines polylines; + try { + polylines = filler->fill_surface(&surface, fill_params); + } catch (InfillFailedException &) { + } extrusion_entities_append_paths( dst, - filler->fill_surface(&surface, fill_params), - role, + std::move(polylines), + role, flow.mm3_per_mm(), flow.width, flow.height); } } @@ -2339,9 +2344,14 @@ static inline void fill_expolygons_generate_paths( fill_params.dont_adjust = true; for (ExPolygon &expoly : expolygons) { Surface surface(stInternal, std::move(expoly)); + Polylines polylines; + try { + polylines = filler->fill_surface(&surface, fill_params); + } catch (InfillFailedException &) { + } extrusion_entities_append_paths( dst, - filler->fill_surface(&surface, fill_params), + std::move(polylines), role, flow.mm3_per_mm(), flow.width, flow.height); } From 572b5ba8bbf7903d4c0b67f6f441002ee1680cab Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 28 Apr 2020 09:56:54 +0200 Subject: [PATCH 05/16] Add PNG and wxWidgets 3.1.3 uniformly to all platforms with cmake build. wx is built with png as a dependency, wxpng is disabled. --- CMakeLists.txt | 9 +- deps/CMakeLists.txt | 35 +- deps/EXPAT/EXPAT.cmake | 9 + deps/EXPAT/expat/CMakeLists.txt | 71 + deps/EXPAT/expat/COPYING | 21 + deps/EXPAT/expat/README | 146 + deps/EXPAT/expat/ascii.h | 92 + deps/EXPAT/expat/asciitab.h | 36 + deps/EXPAT/expat/config.cmake.in | 4 + deps/EXPAT/expat/expat.h | 1048 +++++ deps/EXPAT/expat/expat_config.h | 33 + deps/EXPAT/expat/expat_external.h | 129 + deps/EXPAT/expat/iasciitab.h | 37 + deps/EXPAT/expat/internal.h | 95 + deps/EXPAT/expat/latin1tab.h | 36 + deps/EXPAT/expat/nametab.h | 150 + deps/EXPAT/expat/utf8tab.h | 37 + deps/EXPAT/expat/xmlparse.c | 6458 +++++++++++++++++++++++++++++ deps/EXPAT/expat/xmlrole.c | 1322 ++++++ deps/EXPAT/expat/xmlrole.h | 114 + deps/EXPAT/expat/xmltok.c | 1737 ++++++++ deps/EXPAT/expat/xmltok.h | 322 ++ deps/EXPAT/expat/xmltok_impl.h | 46 + deps/EXPAT/expat/xmltok_impl.inc | 1779 ++++++++ deps/EXPAT/expat/xmltok_ns.inc | 115 + deps/GLEW/GLEW.cmake | 3 +- deps/OpenCSG/OpenCSG.cmake | 4 +- deps/PNG/PNG.cmake | 13 + deps/deps-linux.cmake | 48 +- deps/deps-macos.cmake | 26 - deps/deps-mingw.cmake | 16 - deps/deps-unix-common.cmake | 8 +- deps/deps-windows.cmake | 61 +- deps/wxWidgets/wxWidgets.cmake | 36 + src/CMakeLists.txt | 4 + src/libslic3r/CMakeLists.txt | 3 +- src/slic3r/CMakeLists.txt | 2 +- 37 files changed, 13948 insertions(+), 157 deletions(-) create mode 100644 deps/EXPAT/EXPAT.cmake create mode 100644 deps/EXPAT/expat/CMakeLists.txt create mode 100644 deps/EXPAT/expat/COPYING create mode 100644 deps/EXPAT/expat/README create mode 100644 deps/EXPAT/expat/ascii.h create mode 100644 deps/EXPAT/expat/asciitab.h create mode 100644 deps/EXPAT/expat/config.cmake.in create mode 100644 deps/EXPAT/expat/expat.h create mode 100644 deps/EXPAT/expat/expat_config.h create mode 100644 deps/EXPAT/expat/expat_external.h create mode 100644 deps/EXPAT/expat/iasciitab.h create mode 100644 deps/EXPAT/expat/internal.h create mode 100644 deps/EXPAT/expat/latin1tab.h create mode 100644 deps/EXPAT/expat/nametab.h create mode 100644 deps/EXPAT/expat/utf8tab.h create mode 100644 deps/EXPAT/expat/xmlparse.c create mode 100644 deps/EXPAT/expat/xmlrole.c create mode 100644 deps/EXPAT/expat/xmlrole.h create mode 100644 deps/EXPAT/expat/xmltok.c create mode 100644 deps/EXPAT/expat/xmltok.h create mode 100644 deps/EXPAT/expat/xmltok_impl.h create mode 100644 deps/EXPAT/expat/xmltok_impl.inc create mode 100644 deps/EXPAT/expat/xmltok_ns.inc create mode 100644 deps/PNG/PNG.cmake create mode 100644 deps/wxWidgets/wxWidgets.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 52be8e847..691e234ae 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -369,9 +369,9 @@ include_directories(BEFORE SYSTEM ${EIGEN3_INCLUDE_DIR}) # Find expat or use bundled version # Always use the system libexpat on Linux. -if (NOT SLIC3R_STATIC OR CMAKE_SYSTEM_NAME STREQUAL "Linux") - find_package(EXPAT) -endif () + +find_package(EXPAT) + if (NOT EXPAT_FOUND) add_library(expat STATIC ${LIBDIR}/expat/xmlparse.c @@ -382,7 +382,8 @@ if (NOT EXPAT_FOUND) set(EXPAT_INCLUDE_DIRS ${LIBDIR}/expat/) set(EXPAT_LIBRARIES expat) endif () -include_directories(${EXPAT_INCLUDE_DIRS}) + +find_package(PNG REQUIRED) find_package(OpenGL REQUIRED) diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt index 8702fd6d6..3251d2c62 100644 --- a/deps/CMakeLists.txt +++ b/deps/CMakeLists.txt @@ -34,8 +34,10 @@ endif () set(DESTDIR "${CMAKE_CURRENT_BINARY_DIR}/destdir" CACHE PATH "Destination directory") option(DEP_DEBUG "Build debug variants (only applicable on Windows)" ON) -option(DEP_WX_STABLE "Build against wxWidgets stable 3.0 as opposed to default 3.1 (Linux only)" OFF) -option(DEP_WX_GTK3 "Build wxWidgets against GTK3" OFF) + +if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + option(DEP_WX_GTK3 "Build wxWidgets against GTK3" OFF) +endif() # On developer machines, it can be enabled to speed up compilation and suppress warnings coming from IGL. # FIXME: @@ -127,12 +129,28 @@ else() include("deps-linux.cmake") endif() +set(ZLIB_PKG "") +if (NOT ZLIB_FOUND) + include(ZLIB/ZLIB.cmake) + set(ZLIB_PKG dep_ZLIB) +endif () +set(PNG_PKG "") +if (NOT PNG_FOUND) + include(PNG/PNG.cmake) + set(PNG_PKG dep_PNG) +endif () +set(EXPAT_PKG "") +if (NOT EXPAT_FOUND) + include(EXPAT/EXPAT.cmake) + set(EXPAT_PKG dep_EXPAT) +endif () + include(GLEW/GLEW.cmake) include(OpenCSG/OpenCSG.cmake) - include(GMP/GMP.cmake) include(MPFR/MPFR.cmake) include(CGAL/CGAL.cmake) +include(wxWidgets/wxWidgets.cmake) if (MSVC) @@ -141,15 +159,17 @@ if (MSVC) dep_boost dep_tbb dep_libcurl - dep_wxwidgets + dep_wxWidgets dep_gtest dep_cereal dep_nlopt # dep_qhull # Experimental - dep_ZLIB # on Windows we still need zlib dep_openvdb dep_OpenCSG dep_CGAL + ${PNG_PKG} + ${ZLIB_PKG} + ${EXPAT_PKG} ) else() @@ -159,7 +179,7 @@ else() dep_boost dep_tbb dep_libcurl - dep_wxwidgets + dep_wxWidgets dep_gtest dep_cereal dep_nlopt @@ -167,6 +187,9 @@ else() dep_openvdb dep_OpenCSG dep_CGAL + ${PNG_PKG} + ${ZLIB_PKG} + ${EXPAT_PKG} # dep_libigl # Not working, static build has different Eigen ) diff --git a/deps/EXPAT/EXPAT.cmake b/deps/EXPAT/EXPAT.cmake new file mode 100644 index 000000000..ed5eb220f --- /dev/null +++ b/deps/EXPAT/EXPAT.cmake @@ -0,0 +1,9 @@ +prusaslicer_add_cmake_project(EXPAT + # GIT_REPOSITORY https://github.com/nigels-com/glew.git + # GIT_TAG 3a8eff7 # 2.1.0 + SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/expat +) + +if (MSVC) + add_debug_dep(dep_EXPAT) +endif () diff --git a/deps/EXPAT/expat/CMakeLists.txt b/deps/EXPAT/expat/CMakeLists.txt new file mode 100644 index 000000000..fa54c098f --- /dev/null +++ b/deps/EXPAT/expat/CMakeLists.txt @@ -0,0 +1,71 @@ +cmake_minimum_required(VERSION 3.0) + +project(EXPAT) + +if (BUILD_SHARED_LIBS AND MSVC) + set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) +endif() + +add_library(expat + xmlparse.c + xmlrole.c + xmltok.c +) + +target_include_directories(expat PRIVATE ${PROJECT_SOURCE_DIR}) + +include(GNUInstallDirs) + +install( + FILES + ${PROJECT_SOURCE_DIR}/expat.h + ${PROJECT_SOURCE_DIR}/expat_config.h + ${PROJECT_SOURCE_DIR}/expat_external.h + DESTINATION + ${CMAKE_INSTALL_INCLUDEDIR} +) + +add_library(EXPAT INTERFACE) +target_link_libraries(EXPAT INTERFACE expat) + +include(CMakePackageConfigHelpers) + +write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake" + VERSION 1.95 + COMPATIBILITY AnyNewerVersion +) + +install(TARGETS expat EXPAT + EXPORT ${PROJECT_NAME}Targets + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} +) + +export(EXPORT ${PROJECT_NAME}Targets + FILE "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Targets.cmake" + NAMESPACE ${PROJECT_NAME}:: ) + +set(ConfigPackageLocation ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}) + +install(EXPORT ${PROJECT_NAME}Targets + FILE + "${PROJECT_NAME}Targets.cmake" + NAMESPACE + ${PROJECT_NAME}:: + DESTINATION + ${ConfigPackageLocation} +) + +configure_file(config.cmake.in ${PROJECT_NAME}Config.cmake @ONLY) + +install( + FILES + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake" + DESTINATION + ${ConfigPackageLocation} +) + diff --git a/deps/EXPAT/expat/COPYING b/deps/EXPAT/expat/COPYING new file mode 100644 index 000000000..092c83bae --- /dev/null +++ b/deps/EXPAT/expat/COPYING @@ -0,0 +1,21 @@ +Copyright (c) 1998-2000 Thai Open Source Software Center Ltd and Clark Cooper +Copyright (c) 2001-2016 Expat maintainers + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/deps/EXPAT/expat/README b/deps/EXPAT/expat/README new file mode 100644 index 000000000..40873a6f8 --- /dev/null +++ b/deps/EXPAT/expat/README @@ -0,0 +1,146 @@ +Expat, Release 2.2.0, stripped and modified for inclusion into Slic3r. +Only the library sources needed for static linking were left. + +The original README follows: +--------------------------------------------------------------------- + + + + Expat, Release 2.2.0 + +This is Expat, a C library for parsing XML, written by James Clark. +Expat is a stream-oriented XML parser. This means that you register +handlers with the parser before starting the parse. These handlers +are called when the parser discovers the associated structures in the +document being parsed. A start tag is an example of the kind of +structures for which you may register handlers. + +Windows users should use the expat_win32bin package, which includes +both precompiled libraries and executables, and source code for +developers. + +Expat is free software. You may copy, distribute, and modify it under +the terms of the License contained in the file COPYING distributed +with this package. This license is the same as the MIT/X Consortium +license. + +Versions of Expat that have an odd minor version (the middle number in +the release above), are development releases and should be considered +as beta software. Releases with even minor version numbers are +intended to be production grade software. + +If you are building Expat from a check-out from the CVS repository, +you need to run a script that generates the configure script using the +GNU autoconf and libtool tools. To do this, you need to have +autoconf 2.58 or newer. Run the script like this: + + ./buildconf.sh + +Once this has been done, follow the same instructions as for building +from a source distribution. + +To build Expat from a source distribution, you first run the +configuration shell script in the top level distribution directory: + + ./configure + +There are many options which you may provide to configure (which you +can discover by running configure with the --help option). But the +one of most interest is the one that sets the installation directory. +By default, the configure script will set things up to install +libexpat into /usr/local/lib, expat.h into /usr/local/include, and +xmlwf into /usr/local/bin. If, for example, you'd prefer to install +into /home/me/mystuff/lib, /home/me/mystuff/include, and +/home/me/mystuff/bin, you can tell configure about that with: + + ./configure --prefix=/home/me/mystuff + +Another interesting option is to enable 64-bit integer support for +line and column numbers and the over-all byte index: + + ./configure CPPFLAGS=-DXML_LARGE_SIZE + +However, such a modification would be a breaking change to the ABI +and is therefore not recommended for general use - e.g. as part of +a Linux distribution - but rather for builds with special requirements. + +After running the configure script, the "make" command will build +things and "make install" will install things into their proper +location. Have a look at the "Makefile" to learn about additional +"make" options. Note that you need to have write permission into +the directories into which things will be installed. + +If you are interested in building Expat to provide document +information in UTF-16 encoding rather than the default UTF-8, follow +these instructions (after having run "make distclean"): + + 1. For UTF-16 output as unsigned short (and version/error + strings as char), run: + + ./configure CPPFLAGS=-DXML_UNICODE + + For UTF-16 output as wchar_t (incl. version/error strings), + run: + + ./configure CFLAGS="-g -O2 -fshort-wchar" \ + CPPFLAGS=-DXML_UNICODE_WCHAR_T + + 2. Edit the MakeFile, changing: + + LIBRARY = libexpat.la + + to: + + LIBRARY = libexpatw.la + + (Note the additional "w" in the library name.) + + 3. Run "make buildlib" (which builds the library only). + Or, to save step 2, run "make buildlib LIBRARY=libexpatw.la". + + 4. Run "make installlib" (which installs the library only). + Or, if step 2 was omitted, run "make installlib LIBRARY=libexpatw.la". + +Using DESTDIR or INSTALL_ROOT is enabled, with INSTALL_ROOT being the default +value for DESTDIR, and the rest of the make file using only DESTDIR. +It works as follows: + $ make install DESTDIR=/path/to/image +overrides the in-makefile set DESTDIR, while both + $ INSTALL_ROOT=/path/to/image make install + $ make install INSTALL_ROOT=/path/to/image +use DESTDIR=$(INSTALL_ROOT), even if DESTDIR eventually is defined in the +environment, because variable-setting priority is +1) commandline +2) in-makefile +3) environment + +Note: This only applies to the Expat library itself, building UTF-16 versions +of xmlwf and the tests is currently not supported. + +Note for Solaris users: The "ar" command is usually located in +"/usr/ccs/bin", which is not in the default PATH. You will need to +add this to your path for the "make" command, and probably also switch +to GNU make (the "make" found in /usr/ccs/bin does not seem to work +properly -- apparently it does not understand .PHONY directives). If +you're using ksh or bash, use this command to build: + + PATH=/usr/ccs/bin:$PATH make + +When using Expat with a project using autoconf for configuration, you +can use the probing macro in conftools/expat.m4 to determine how to +include Expat. See the comments at the top of that file for more +information. + +A reference manual is available in the file doc/reference.html in this +distribution. + +The homepage for this project is http://www.libexpat.org/. There +are links there to connect you to the bug reports page. If you need +to report a bug when you don't have access to a browser, you may also +send a bug report by email to expat-bugs@mail.libexpat.org. + +Discussion related to the direction of future expat development takes +place on expat-discuss@mail.libexpat.org. Archives of this list and +other Expat-related lists may be found at: + + http://mail.libexpat.org/mailman/listinfo/ diff --git a/deps/EXPAT/expat/ascii.h b/deps/EXPAT/expat/ascii.h new file mode 100644 index 000000000..d10530b09 --- /dev/null +++ b/deps/EXPAT/expat/ascii.h @@ -0,0 +1,92 @@ +/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +#define ASCII_A 0x41 +#define ASCII_B 0x42 +#define ASCII_C 0x43 +#define ASCII_D 0x44 +#define ASCII_E 0x45 +#define ASCII_F 0x46 +#define ASCII_G 0x47 +#define ASCII_H 0x48 +#define ASCII_I 0x49 +#define ASCII_J 0x4A +#define ASCII_K 0x4B +#define ASCII_L 0x4C +#define ASCII_M 0x4D +#define ASCII_N 0x4E +#define ASCII_O 0x4F +#define ASCII_P 0x50 +#define ASCII_Q 0x51 +#define ASCII_R 0x52 +#define ASCII_S 0x53 +#define ASCII_T 0x54 +#define ASCII_U 0x55 +#define ASCII_V 0x56 +#define ASCII_W 0x57 +#define ASCII_X 0x58 +#define ASCII_Y 0x59 +#define ASCII_Z 0x5A + +#define ASCII_a 0x61 +#define ASCII_b 0x62 +#define ASCII_c 0x63 +#define ASCII_d 0x64 +#define ASCII_e 0x65 +#define ASCII_f 0x66 +#define ASCII_g 0x67 +#define ASCII_h 0x68 +#define ASCII_i 0x69 +#define ASCII_j 0x6A +#define ASCII_k 0x6B +#define ASCII_l 0x6C +#define ASCII_m 0x6D +#define ASCII_n 0x6E +#define ASCII_o 0x6F +#define ASCII_p 0x70 +#define ASCII_q 0x71 +#define ASCII_r 0x72 +#define ASCII_s 0x73 +#define ASCII_t 0x74 +#define ASCII_u 0x75 +#define ASCII_v 0x76 +#define ASCII_w 0x77 +#define ASCII_x 0x78 +#define ASCII_y 0x79 +#define ASCII_z 0x7A + +#define ASCII_0 0x30 +#define ASCII_1 0x31 +#define ASCII_2 0x32 +#define ASCII_3 0x33 +#define ASCII_4 0x34 +#define ASCII_5 0x35 +#define ASCII_6 0x36 +#define ASCII_7 0x37 +#define ASCII_8 0x38 +#define ASCII_9 0x39 + +#define ASCII_TAB 0x09 +#define ASCII_SPACE 0x20 +#define ASCII_EXCL 0x21 +#define ASCII_QUOT 0x22 +#define ASCII_AMP 0x26 +#define ASCII_APOS 0x27 +#define ASCII_MINUS 0x2D +#define ASCII_PERIOD 0x2E +#define ASCII_COLON 0x3A +#define ASCII_SEMI 0x3B +#define ASCII_LT 0x3C +#define ASCII_EQUALS 0x3D +#define ASCII_GT 0x3E +#define ASCII_LSQB 0x5B +#define ASCII_RSQB 0x5D +#define ASCII_UNDERSCORE 0x5F +#define ASCII_LPAREN 0x28 +#define ASCII_RPAREN 0x29 +#define ASCII_FF 0x0C +#define ASCII_SLASH 0x2F +#define ASCII_HASH 0x23 +#define ASCII_PIPE 0x7C +#define ASCII_COMMA 0x2C diff --git a/deps/EXPAT/expat/asciitab.h b/deps/EXPAT/expat/asciitab.h new file mode 100644 index 000000000..79a15c28c --- /dev/null +++ b/deps/EXPAT/expat/asciitab.h @@ -0,0 +1,36 @@ +/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +/* 0x00 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x04 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x08 */ BT_NONXML, BT_S, BT_LF, BT_NONXML, +/* 0x0C */ BT_NONXML, BT_CR, BT_NONXML, BT_NONXML, +/* 0x10 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x14 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x18 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x1C */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x20 */ BT_S, BT_EXCL, BT_QUOT, BT_NUM, +/* 0x24 */ BT_OTHER, BT_PERCNT, BT_AMP, BT_APOS, +/* 0x28 */ BT_LPAR, BT_RPAR, BT_AST, BT_PLUS, +/* 0x2C */ BT_COMMA, BT_MINUS, BT_NAME, BT_SOL, +/* 0x30 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT, +/* 0x34 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT, +/* 0x38 */ BT_DIGIT, BT_DIGIT, BT_COLON, BT_SEMI, +/* 0x3C */ BT_LT, BT_EQUALS, BT_GT, BT_QUEST, +/* 0x40 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX, +/* 0x44 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT, +/* 0x48 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x4C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x50 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x54 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x58 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_LSQB, +/* 0x5C */ BT_OTHER, BT_RSQB, BT_OTHER, BT_NMSTRT, +/* 0x60 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX, +/* 0x64 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT, +/* 0x68 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x6C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x70 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x74 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x78 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER, +/* 0x7C */ BT_VERBAR, BT_OTHER, BT_OTHER, BT_OTHER, diff --git a/deps/EXPAT/expat/config.cmake.in b/deps/EXPAT/expat/config.cmake.in new file mode 100644 index 000000000..8edb5bbbd --- /dev/null +++ b/deps/EXPAT/expat/config.cmake.in @@ -0,0 +1,4 @@ +include(${CMAKE_CURRENT_LIST_DIR}/EXPATTargets.cmake) +set(EXPAT_LIBRARIES EXPAT::expat) +set(EXPAT_INCLUDE_DIRS ${_IMPORT_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}) + diff --git a/deps/EXPAT/expat/expat.h b/deps/EXPAT/expat/expat.h new file mode 100644 index 000000000..086e24b39 --- /dev/null +++ b/deps/EXPAT/expat/expat.h @@ -0,0 +1,1048 @@ +/* Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +#ifndef Expat_INCLUDED +#define Expat_INCLUDED 1 + +#ifdef __VMS +/* 0 1 2 3 0 1 2 3 + 1234567890123456789012345678901 1234567890123456789012345678901 */ +#define XML_SetProcessingInstructionHandler XML_SetProcessingInstrHandler +#define XML_SetUnparsedEntityDeclHandler XML_SetUnparsedEntDeclHandler +#define XML_SetStartNamespaceDeclHandler XML_SetStartNamespcDeclHandler +#define XML_SetExternalEntityRefHandlerArg XML_SetExternalEntRefHandlerArg +#endif + +#include +#include "expat_external.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct XML_ParserStruct; +typedef struct XML_ParserStruct *XML_Parser; + +/* Should this be defined using stdbool.h when C99 is available? */ +typedef unsigned char XML_Bool; +#define XML_TRUE ((XML_Bool) 1) +#define XML_FALSE ((XML_Bool) 0) + +/* The XML_Status enum gives the possible return values for several + API functions. The preprocessor #defines are included so this + stanza can be added to code that still needs to support older + versions of Expat 1.95.x: + + #ifndef XML_STATUS_OK + #define XML_STATUS_OK 1 + #define XML_STATUS_ERROR 0 + #endif + + Otherwise, the #define hackery is quite ugly and would have been + dropped. +*/ +enum XML_Status { + XML_STATUS_ERROR = 0, +#define XML_STATUS_ERROR XML_STATUS_ERROR + XML_STATUS_OK = 1, +#define XML_STATUS_OK XML_STATUS_OK + XML_STATUS_SUSPENDED = 2 +#define XML_STATUS_SUSPENDED XML_STATUS_SUSPENDED +}; + +enum XML_Error { + XML_ERROR_NONE, + XML_ERROR_NO_MEMORY, + XML_ERROR_SYNTAX, + XML_ERROR_NO_ELEMENTS, + XML_ERROR_INVALID_TOKEN, + XML_ERROR_UNCLOSED_TOKEN, + XML_ERROR_PARTIAL_CHAR, + XML_ERROR_TAG_MISMATCH, + XML_ERROR_DUPLICATE_ATTRIBUTE, + XML_ERROR_JUNK_AFTER_DOC_ELEMENT, + XML_ERROR_PARAM_ENTITY_REF, + XML_ERROR_UNDEFINED_ENTITY, + XML_ERROR_RECURSIVE_ENTITY_REF, + XML_ERROR_ASYNC_ENTITY, + XML_ERROR_BAD_CHAR_REF, + XML_ERROR_BINARY_ENTITY_REF, + XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF, + XML_ERROR_MISPLACED_XML_PI, + XML_ERROR_UNKNOWN_ENCODING, + XML_ERROR_INCORRECT_ENCODING, + XML_ERROR_UNCLOSED_CDATA_SECTION, + XML_ERROR_EXTERNAL_ENTITY_HANDLING, + XML_ERROR_NOT_STANDALONE, + XML_ERROR_UNEXPECTED_STATE, + XML_ERROR_ENTITY_DECLARED_IN_PE, + XML_ERROR_FEATURE_REQUIRES_XML_DTD, + XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING, + /* Added in 1.95.7. */ + XML_ERROR_UNBOUND_PREFIX, + /* Added in 1.95.8. */ + XML_ERROR_UNDECLARING_PREFIX, + XML_ERROR_INCOMPLETE_PE, + XML_ERROR_XML_DECL, + XML_ERROR_TEXT_DECL, + XML_ERROR_PUBLICID, + XML_ERROR_SUSPENDED, + XML_ERROR_NOT_SUSPENDED, + XML_ERROR_ABORTED, + XML_ERROR_FINISHED, + XML_ERROR_SUSPEND_PE, + /* Added in 2.0. */ + XML_ERROR_RESERVED_PREFIX_XML, + XML_ERROR_RESERVED_PREFIX_XMLNS, + XML_ERROR_RESERVED_NAMESPACE_URI +}; + +enum XML_Content_Type { + XML_CTYPE_EMPTY = 1, + XML_CTYPE_ANY, + XML_CTYPE_MIXED, + XML_CTYPE_NAME, + XML_CTYPE_CHOICE, + XML_CTYPE_SEQ +}; + +enum XML_Content_Quant { + XML_CQUANT_NONE, + XML_CQUANT_OPT, + XML_CQUANT_REP, + XML_CQUANT_PLUS +}; + +/* If type == XML_CTYPE_EMPTY or XML_CTYPE_ANY, then quant will be + XML_CQUANT_NONE, and the other fields will be zero or NULL. + If type == XML_CTYPE_MIXED, then quant will be NONE or REP and + numchildren will contain number of elements that may be mixed in + and children point to an array of XML_Content cells that will be + all of XML_CTYPE_NAME type with no quantification. + + If type == XML_CTYPE_NAME, then the name points to the name, and + the numchildren field will be zero and children will be NULL. The + quant fields indicates any quantifiers placed on the name. + + CHOICE and SEQ will have name NULL, the number of children in + numchildren and children will point, recursively, to an array + of XML_Content cells. + + The EMPTY, ANY, and MIXED types will only occur at top level. +*/ + +typedef struct XML_cp XML_Content; + +struct XML_cp { + enum XML_Content_Type type; + enum XML_Content_Quant quant; + XML_Char * name; + unsigned int numchildren; + XML_Content * children; +}; + + +/* This is called for an element declaration. See above for + description of the model argument. It's the caller's responsibility + to free model when finished with it. +*/ +typedef void (XMLCALL *XML_ElementDeclHandler) (void *userData, + const XML_Char *name, + XML_Content *model); + +XMLPARSEAPI(void) +XML_SetElementDeclHandler(XML_Parser parser, + XML_ElementDeclHandler eldecl); + +/* The Attlist declaration handler is called for *each* attribute. So + a single Attlist declaration with multiple attributes declared will + generate multiple calls to this handler. The "default" parameter + may be NULL in the case of the "#IMPLIED" or "#REQUIRED" + keyword. The "isrequired" parameter will be true and the default + value will be NULL in the case of "#REQUIRED". If "isrequired" is + true and default is non-NULL, then this is a "#FIXED" default. +*/ +typedef void (XMLCALL *XML_AttlistDeclHandler) ( + void *userData, + const XML_Char *elname, + const XML_Char *attname, + const XML_Char *att_type, + const XML_Char *dflt, + int isrequired); + +XMLPARSEAPI(void) +XML_SetAttlistDeclHandler(XML_Parser parser, + XML_AttlistDeclHandler attdecl); + +/* The XML declaration handler is called for *both* XML declarations + and text declarations. The way to distinguish is that the version + parameter will be NULL for text declarations. The encoding + parameter may be NULL for XML declarations. The standalone + parameter will be -1, 0, or 1 indicating respectively that there + was no standalone parameter in the declaration, that it was given + as no, or that it was given as yes. +*/ +typedef void (XMLCALL *XML_XmlDeclHandler) (void *userData, + const XML_Char *version, + const XML_Char *encoding, + int standalone); + +XMLPARSEAPI(void) +XML_SetXmlDeclHandler(XML_Parser parser, + XML_XmlDeclHandler xmldecl); + + +typedef struct { + void *(*malloc_fcn)(size_t size); + void *(*realloc_fcn)(void *ptr, size_t size); + void (*free_fcn)(void *ptr); +} XML_Memory_Handling_Suite; + +/* Constructs a new parser; encoding is the encoding specified by the + external protocol or NULL if there is none specified. +*/ +XMLPARSEAPI(XML_Parser) +XML_ParserCreate(const XML_Char *encoding); + +/* Constructs a new parser and namespace processor. Element type + names and attribute names that belong to a namespace will be + expanded; unprefixed attribute names are never expanded; unprefixed + element type names are expanded only if there is a default + namespace. The expanded name is the concatenation of the namespace + URI, the namespace separator character, and the local part of the + name. If the namespace separator is '\0' then the namespace URI + and the local part will be concatenated without any separator. + It is a programming error to use the separator '\0' with namespace + triplets (see XML_SetReturnNSTriplet). +*/ +XMLPARSEAPI(XML_Parser) +XML_ParserCreateNS(const XML_Char *encoding, XML_Char namespaceSeparator); + + +/* Constructs a new parser using the memory management suite referred to + by memsuite. If memsuite is NULL, then use the standard library memory + suite. If namespaceSeparator is non-NULL it creates a parser with + namespace processing as described above. The character pointed at + will serve as the namespace separator. + + All further memory operations used for the created parser will come from + the given suite. +*/ +XMLPARSEAPI(XML_Parser) +XML_ParserCreate_MM(const XML_Char *encoding, + const XML_Memory_Handling_Suite *memsuite, + const XML_Char *namespaceSeparator); + +/* Prepare a parser object to be re-used. This is particularly + valuable when memory allocation overhead is disproportionatly high, + such as when a large number of small documnents need to be parsed. + All handlers are cleared from the parser, except for the + unknownEncodingHandler. The parser's external state is re-initialized + except for the values of ns and ns_triplets. + + Added in Expat 1.95.3. +*/ +XMLPARSEAPI(XML_Bool) +XML_ParserReset(XML_Parser parser, const XML_Char *encoding); + +/* atts is array of name/value pairs, terminated by 0; + names and values are 0 terminated. +*/ +typedef void (XMLCALL *XML_StartElementHandler) (void *userData, + const XML_Char *name, + const XML_Char **atts); + +typedef void (XMLCALL *XML_EndElementHandler) (void *userData, + const XML_Char *name); + + +/* s is not 0 terminated. */ +typedef void (XMLCALL *XML_CharacterDataHandler) (void *userData, + const XML_Char *s, + int len); + +/* target and data are 0 terminated */ +typedef void (XMLCALL *XML_ProcessingInstructionHandler) ( + void *userData, + const XML_Char *target, + const XML_Char *data); + +/* data is 0 terminated */ +typedef void (XMLCALL *XML_CommentHandler) (void *userData, + const XML_Char *data); + +typedef void (XMLCALL *XML_StartCdataSectionHandler) (void *userData); +typedef void (XMLCALL *XML_EndCdataSectionHandler) (void *userData); + +/* This is called for any characters in the XML document for which + there is no applicable handler. This includes both characters that + are part of markup which is of a kind that is not reported + (comments, markup declarations), or characters that are part of a + construct which could be reported but for which no handler has been + supplied. The characters are passed exactly as they were in the XML + document except that they will be encoded in UTF-8 or UTF-16. + Line boundaries are not normalized. Note that a byte order mark + character is not passed to the default handler. There are no + guarantees about how characters are divided between calls to the + default handler: for example, a comment might be split between + multiple calls. +*/ +typedef void (XMLCALL *XML_DefaultHandler) (void *userData, + const XML_Char *s, + int len); + +/* This is called for the start of the DOCTYPE declaration, before + any DTD or internal subset is parsed. +*/ +typedef void (XMLCALL *XML_StartDoctypeDeclHandler) ( + void *userData, + const XML_Char *doctypeName, + const XML_Char *sysid, + const XML_Char *pubid, + int has_internal_subset); + +/* This is called for the start of the DOCTYPE declaration when the + closing > is encountered, but after processing any external + subset. +*/ +typedef void (XMLCALL *XML_EndDoctypeDeclHandler)(void *userData); + +/* This is called for entity declarations. The is_parameter_entity + argument will be non-zero if the entity is a parameter entity, zero + otherwise. + + For internal entities (), value will + be non-NULL and systemId, publicID, and notationName will be NULL. + The value string is NOT nul-terminated; the length is provided in + the value_length argument. Since it is legal to have zero-length + values, do not use this argument to test for internal entities. + + For external entities, value will be NULL and systemId will be + non-NULL. The publicId argument will be NULL unless a public + identifier was provided. The notationName argument will have a + non-NULL value only for unparsed entity declarations. + + Note that is_parameter_entity can't be changed to XML_Bool, since + that would break binary compatibility. +*/ +typedef void (XMLCALL *XML_EntityDeclHandler) ( + void *userData, + const XML_Char *entityName, + int is_parameter_entity, + const XML_Char *value, + int value_length, + const XML_Char *base, + const XML_Char *systemId, + const XML_Char *publicId, + const XML_Char *notationName); + +XMLPARSEAPI(void) +XML_SetEntityDeclHandler(XML_Parser parser, + XML_EntityDeclHandler handler); + +/* OBSOLETE -- OBSOLETE -- OBSOLETE + This handler has been superseded by the EntityDeclHandler above. + It is provided here for backward compatibility. + + This is called for a declaration of an unparsed (NDATA) entity. + The base argument is whatever was set by XML_SetBase. The + entityName, systemId and notationName arguments will never be + NULL. The other arguments may be. +*/ +typedef void (XMLCALL *XML_UnparsedEntityDeclHandler) ( + void *userData, + const XML_Char *entityName, + const XML_Char *base, + const XML_Char *systemId, + const XML_Char *publicId, + const XML_Char *notationName); + +/* This is called for a declaration of notation. The base argument is + whatever was set by XML_SetBase. The notationName will never be + NULL. The other arguments can be. +*/ +typedef void (XMLCALL *XML_NotationDeclHandler) ( + void *userData, + const XML_Char *notationName, + const XML_Char *base, + const XML_Char *systemId, + const XML_Char *publicId); + +/* When namespace processing is enabled, these are called once for + each namespace declaration. The call to the start and end element + handlers occur between the calls to the start and end namespace + declaration handlers. For an xmlns attribute, prefix will be + NULL. For an xmlns="" attribute, uri will be NULL. +*/ +typedef void (XMLCALL *XML_StartNamespaceDeclHandler) ( + void *userData, + const XML_Char *prefix, + const XML_Char *uri); + +typedef void (XMLCALL *XML_EndNamespaceDeclHandler) ( + void *userData, + const XML_Char *prefix); + +/* This is called if the document is not standalone, that is, it has an + external subset or a reference to a parameter entity, but does not + have standalone="yes". If this handler returns XML_STATUS_ERROR, + then processing will not continue, and the parser will return a + XML_ERROR_NOT_STANDALONE error. + If parameter entity parsing is enabled, then in addition to the + conditions above this handler will only be called if the referenced + entity was actually read. +*/ +typedef int (XMLCALL *XML_NotStandaloneHandler) (void *userData); + +/* This is called for a reference to an external parsed general + entity. The referenced entity is not automatically parsed. The + application can parse it immediately or later using + XML_ExternalEntityParserCreate. + + The parser argument is the parser parsing the entity containing the + reference; it can be passed as the parser argument to + XML_ExternalEntityParserCreate. The systemId argument is the + system identifier as specified in the entity declaration; it will + not be NULL. + + The base argument is the system identifier that should be used as + the base for resolving systemId if systemId was relative; this is + set by XML_SetBase; it may be NULL. + + The publicId argument is the public identifier as specified in the + entity declaration, or NULL if none was specified; the whitespace + in the public identifier will have been normalized as required by + the XML spec. + + The context argument specifies the parsing context in the format + expected by the context argument to XML_ExternalEntityParserCreate; + context is valid only until the handler returns, so if the + referenced entity is to be parsed later, it must be copied. + context is NULL only when the entity is a parameter entity. + + The handler should return XML_STATUS_ERROR if processing should not + continue because of a fatal error in the handling of the external + entity. In this case the calling parser will return an + XML_ERROR_EXTERNAL_ENTITY_HANDLING error. + + Note that unlike other handlers the first argument is the parser, + not userData. +*/ +typedef int (XMLCALL *XML_ExternalEntityRefHandler) ( + XML_Parser parser, + const XML_Char *context, + const XML_Char *base, + const XML_Char *systemId, + const XML_Char *publicId); + +/* This is called in two situations: + 1) An entity reference is encountered for which no declaration + has been read *and* this is not an error. + 2) An internal entity reference is read, but not expanded, because + XML_SetDefaultHandler has been called. + Note: skipped parameter entities in declarations and skipped general + entities in attribute values cannot be reported, because + the event would be out of sync with the reporting of the + declarations or attribute values +*/ +typedef void (XMLCALL *XML_SkippedEntityHandler) ( + void *userData, + const XML_Char *entityName, + int is_parameter_entity); + +/* This structure is filled in by the XML_UnknownEncodingHandler to + provide information to the parser about encodings that are unknown + to the parser. + + The map[b] member gives information about byte sequences whose + first byte is b. + + If map[b] is c where c is >= 0, then b by itself encodes the + Unicode scalar value c. + + If map[b] is -1, then the byte sequence is malformed. + + If map[b] is -n, where n >= 2, then b is the first byte of an + n-byte sequence that encodes a single Unicode scalar value. + + The data member will be passed as the first argument to the convert + function. + + The convert function is used to convert multibyte sequences; s will + point to a n-byte sequence where map[(unsigned char)*s] == -n. The + convert function must return the Unicode scalar value represented + by this byte sequence or -1 if the byte sequence is malformed. + + The convert function may be NULL if the encoding is a single-byte + encoding, that is if map[b] >= -1 for all bytes b. + + When the parser is finished with the encoding, then if release is + not NULL, it will call release passing it the data member; once + release has been called, the convert function will not be called + again. + + Expat places certain restrictions on the encodings that are supported + using this mechanism. + + 1. Every ASCII character that can appear in a well-formed XML document, + other than the characters + + $@\^`{}~ + + must be represented by a single byte, and that byte must be the + same byte that represents that character in ASCII. + + 2. No character may require more than 4 bytes to encode. + + 3. All characters encoded must have Unicode scalar values <= + 0xFFFF, (i.e., characters that would be encoded by surrogates in + UTF-16 are not allowed). Note that this restriction doesn't + apply to the built-in support for UTF-8 and UTF-16. + + 4. No Unicode character may be encoded by more than one distinct + sequence of bytes. +*/ +typedef struct { + int map[256]; + void *data; + int (XMLCALL *convert)(void *data, const char *s); + void (XMLCALL *release)(void *data); +} XML_Encoding; + +/* This is called for an encoding that is unknown to the parser. + + The encodingHandlerData argument is that which was passed as the + second argument to XML_SetUnknownEncodingHandler. + + The name argument gives the name of the encoding as specified in + the encoding declaration. + + If the callback can provide information about the encoding, it must + fill in the XML_Encoding structure, and return XML_STATUS_OK. + Otherwise it must return XML_STATUS_ERROR. + + If info does not describe a suitable encoding, then the parser will + return an XML_UNKNOWN_ENCODING error. +*/ +typedef int (XMLCALL *XML_UnknownEncodingHandler) ( + void *encodingHandlerData, + const XML_Char *name, + XML_Encoding *info); + +XMLPARSEAPI(void) +XML_SetElementHandler(XML_Parser parser, + XML_StartElementHandler start, + XML_EndElementHandler end); + +XMLPARSEAPI(void) +XML_SetStartElementHandler(XML_Parser parser, + XML_StartElementHandler handler); + +XMLPARSEAPI(void) +XML_SetEndElementHandler(XML_Parser parser, + XML_EndElementHandler handler); + +XMLPARSEAPI(void) +XML_SetCharacterDataHandler(XML_Parser parser, + XML_CharacterDataHandler handler); + +XMLPARSEAPI(void) +XML_SetProcessingInstructionHandler(XML_Parser parser, + XML_ProcessingInstructionHandler handler); +XMLPARSEAPI(void) +XML_SetCommentHandler(XML_Parser parser, + XML_CommentHandler handler); + +XMLPARSEAPI(void) +XML_SetCdataSectionHandler(XML_Parser parser, + XML_StartCdataSectionHandler start, + XML_EndCdataSectionHandler end); + +XMLPARSEAPI(void) +XML_SetStartCdataSectionHandler(XML_Parser parser, + XML_StartCdataSectionHandler start); + +XMLPARSEAPI(void) +XML_SetEndCdataSectionHandler(XML_Parser parser, + XML_EndCdataSectionHandler end); + +/* This sets the default handler and also inhibits expansion of + internal entities. These entity references will be passed to the + default handler, or to the skipped entity handler, if one is set. +*/ +XMLPARSEAPI(void) +XML_SetDefaultHandler(XML_Parser parser, + XML_DefaultHandler handler); + +/* This sets the default handler but does not inhibit expansion of + internal entities. The entity reference will not be passed to the + default handler. +*/ +XMLPARSEAPI(void) +XML_SetDefaultHandlerExpand(XML_Parser parser, + XML_DefaultHandler handler); + +XMLPARSEAPI(void) +XML_SetDoctypeDeclHandler(XML_Parser parser, + XML_StartDoctypeDeclHandler start, + XML_EndDoctypeDeclHandler end); + +XMLPARSEAPI(void) +XML_SetStartDoctypeDeclHandler(XML_Parser parser, + XML_StartDoctypeDeclHandler start); + +XMLPARSEAPI(void) +XML_SetEndDoctypeDeclHandler(XML_Parser parser, + XML_EndDoctypeDeclHandler end); + +XMLPARSEAPI(void) +XML_SetUnparsedEntityDeclHandler(XML_Parser parser, + XML_UnparsedEntityDeclHandler handler); + +XMLPARSEAPI(void) +XML_SetNotationDeclHandler(XML_Parser parser, + XML_NotationDeclHandler handler); + +XMLPARSEAPI(void) +XML_SetNamespaceDeclHandler(XML_Parser parser, + XML_StartNamespaceDeclHandler start, + XML_EndNamespaceDeclHandler end); + +XMLPARSEAPI(void) +XML_SetStartNamespaceDeclHandler(XML_Parser parser, + XML_StartNamespaceDeclHandler start); + +XMLPARSEAPI(void) +XML_SetEndNamespaceDeclHandler(XML_Parser parser, + XML_EndNamespaceDeclHandler end); + +XMLPARSEAPI(void) +XML_SetNotStandaloneHandler(XML_Parser parser, + XML_NotStandaloneHandler handler); + +XMLPARSEAPI(void) +XML_SetExternalEntityRefHandler(XML_Parser parser, + XML_ExternalEntityRefHandler handler); + +/* If a non-NULL value for arg is specified here, then it will be + passed as the first argument to the external entity ref handler + instead of the parser object. +*/ +XMLPARSEAPI(void) +XML_SetExternalEntityRefHandlerArg(XML_Parser parser, + void *arg); + +XMLPARSEAPI(void) +XML_SetSkippedEntityHandler(XML_Parser parser, + XML_SkippedEntityHandler handler); + +XMLPARSEAPI(void) +XML_SetUnknownEncodingHandler(XML_Parser parser, + XML_UnknownEncodingHandler handler, + void *encodingHandlerData); + +/* This can be called within a handler for a start element, end + element, processing instruction or character data. It causes the + corresponding markup to be passed to the default handler. +*/ +XMLPARSEAPI(void) +XML_DefaultCurrent(XML_Parser parser); + +/* If do_nst is non-zero, and namespace processing is in effect, and + a name has a prefix (i.e. an explicit namespace qualifier) then + that name is returned as a triplet in a single string separated by + the separator character specified when the parser was created: URI + + sep + local_name + sep + prefix. + + If do_nst is zero, then namespace information is returned in the + default manner (URI + sep + local_name) whether or not the name + has a prefix. + + Note: Calling XML_SetReturnNSTriplet after XML_Parse or + XML_ParseBuffer has no effect. +*/ + +XMLPARSEAPI(void) +XML_SetReturnNSTriplet(XML_Parser parser, int do_nst); + +/* This value is passed as the userData argument to callbacks. */ +XMLPARSEAPI(void) +XML_SetUserData(XML_Parser parser, void *userData); + +/* Returns the last value set by XML_SetUserData or NULL. */ +#define XML_GetUserData(parser) (*(void **)(parser)) + +/* This is equivalent to supplying an encoding argument to + XML_ParserCreate. On success XML_SetEncoding returns non-zero, + zero otherwise. + Note: Calling XML_SetEncoding after XML_Parse or XML_ParseBuffer + has no effect and returns XML_STATUS_ERROR. +*/ +XMLPARSEAPI(enum XML_Status) +XML_SetEncoding(XML_Parser parser, const XML_Char *encoding); + +/* If this function is called, then the parser will be passed as the + first argument to callbacks instead of userData. The userData will + still be accessible using XML_GetUserData. +*/ +XMLPARSEAPI(void) +XML_UseParserAsHandlerArg(XML_Parser parser); + +/* If useDTD == XML_TRUE is passed to this function, then the parser + will assume that there is an external subset, even if none is + specified in the document. In such a case the parser will call the + externalEntityRefHandler with a value of NULL for the systemId + argument (the publicId and context arguments will be NULL as well). + Note: For the purpose of checking WFC: Entity Declared, passing + useDTD == XML_TRUE will make the parser behave as if the document + had a DTD with an external subset. + Note: If this function is called, then this must be done before + the first call to XML_Parse or XML_ParseBuffer, since it will + have no effect after that. Returns + XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING. + Note: If the document does not have a DOCTYPE declaration at all, + then startDoctypeDeclHandler and endDoctypeDeclHandler will not + be called, despite an external subset being parsed. + Note: If XML_DTD is not defined when Expat is compiled, returns + XML_ERROR_FEATURE_REQUIRES_XML_DTD. +*/ +XMLPARSEAPI(enum XML_Error) +XML_UseForeignDTD(XML_Parser parser, XML_Bool useDTD); + + +/* Sets the base to be used for resolving relative URIs in system + identifiers in declarations. Resolving relative identifiers is + left to the application: this value will be passed through as the + base argument to the XML_ExternalEntityRefHandler, + XML_NotationDeclHandler and XML_UnparsedEntityDeclHandler. The base + argument will be copied. Returns XML_STATUS_ERROR if out of memory, + XML_STATUS_OK otherwise. +*/ +XMLPARSEAPI(enum XML_Status) +XML_SetBase(XML_Parser parser, const XML_Char *base); + +XMLPARSEAPI(const XML_Char *) +XML_GetBase(XML_Parser parser); + +/* Returns the number of the attribute/value pairs passed in last call + to the XML_StartElementHandler that were specified in the start-tag + rather than defaulted. Each attribute/value pair counts as 2; thus + this correspondds to an index into the atts array passed to the + XML_StartElementHandler. +*/ +XMLPARSEAPI(int) +XML_GetSpecifiedAttributeCount(XML_Parser parser); + +/* Returns the index of the ID attribute passed in the last call to + XML_StartElementHandler, or -1 if there is no ID attribute. Each + attribute/value pair counts as 2; thus this correspondds to an + index into the atts array passed to the XML_StartElementHandler. +*/ +XMLPARSEAPI(int) +XML_GetIdAttributeIndex(XML_Parser parser); + +#ifdef XML_ATTR_INFO +/* Source file byte offsets for the start and end of attribute names and values. + The value indices are exclusive of surrounding quotes; thus in a UTF-8 source + file an attribute value of "blah" will yield: + info->valueEnd - info->valueStart = 4 bytes. +*/ +typedef struct { + XML_Index nameStart; /* Offset to beginning of the attribute name. */ + XML_Index nameEnd; /* Offset after the attribute name's last byte. */ + XML_Index valueStart; /* Offset to beginning of the attribute value. */ + XML_Index valueEnd; /* Offset after the attribute value's last byte. */ +} XML_AttrInfo; + +/* Returns an array of XML_AttrInfo structures for the attribute/value pairs + passed in last call to the XML_StartElementHandler that were specified + in the start-tag rather than defaulted. Each attribute/value pair counts + as 1; thus the number of entries in the array is + XML_GetSpecifiedAttributeCount(parser) / 2. +*/ +XMLPARSEAPI(const XML_AttrInfo *) +XML_GetAttributeInfo(XML_Parser parser); +#endif + +/* Parses some input. Returns XML_STATUS_ERROR if a fatal error is + detected. The last call to XML_Parse must have isFinal true; len + may be zero for this call (or any other). + + Though the return values for these functions has always been + described as a Boolean value, the implementation, at least for the + 1.95.x series, has always returned exactly one of the XML_Status + values. +*/ +XMLPARSEAPI(enum XML_Status) +XML_Parse(XML_Parser parser, const char *s, int len, int isFinal); + +XMLPARSEAPI(void *) +XML_GetBuffer(XML_Parser parser, int len); + +XMLPARSEAPI(enum XML_Status) +XML_ParseBuffer(XML_Parser parser, int len, int isFinal); + +/* Stops parsing, causing XML_Parse() or XML_ParseBuffer() to return. + Must be called from within a call-back handler, except when aborting + (resumable = 0) an already suspended parser. Some call-backs may + still follow because they would otherwise get lost. Examples: + - endElementHandler() for empty elements when stopped in + startElementHandler(), + - endNameSpaceDeclHandler() when stopped in endElementHandler(), + and possibly others. + + Can be called from most handlers, including DTD related call-backs, + except when parsing an external parameter entity and resumable != 0. + Returns XML_STATUS_OK when successful, XML_STATUS_ERROR otherwise. + Possible error codes: + - XML_ERROR_SUSPENDED: when suspending an already suspended parser. + - XML_ERROR_FINISHED: when the parser has already finished. + - XML_ERROR_SUSPEND_PE: when suspending while parsing an external PE. + + When resumable != 0 (true) then parsing is suspended, that is, + XML_Parse() and XML_ParseBuffer() return XML_STATUS_SUSPENDED. + Otherwise, parsing is aborted, that is, XML_Parse() and XML_ParseBuffer() + return XML_STATUS_ERROR with error code XML_ERROR_ABORTED. + + *Note*: + This will be applied to the current parser instance only, that is, if + there is a parent parser then it will continue parsing when the + externalEntityRefHandler() returns. It is up to the implementation of + the externalEntityRefHandler() to call XML_StopParser() on the parent + parser (recursively), if one wants to stop parsing altogether. + + When suspended, parsing can be resumed by calling XML_ResumeParser(). +*/ +XMLPARSEAPI(enum XML_Status) +XML_StopParser(XML_Parser parser, XML_Bool resumable); + +/* Resumes parsing after it has been suspended with XML_StopParser(). + Must not be called from within a handler call-back. Returns same + status codes as XML_Parse() or XML_ParseBuffer(). + Additional error code XML_ERROR_NOT_SUSPENDED possible. + + *Note*: + This must be called on the most deeply nested child parser instance + first, and on its parent parser only after the child parser has finished, + to be applied recursively until the document entity's parser is restarted. + That is, the parent parser will not resume by itself and it is up to the + application to call XML_ResumeParser() on it at the appropriate moment. +*/ +XMLPARSEAPI(enum XML_Status) +XML_ResumeParser(XML_Parser parser); + +enum XML_Parsing { + XML_INITIALIZED, + XML_PARSING, + XML_FINISHED, + XML_SUSPENDED +}; + +typedef struct { + enum XML_Parsing parsing; + XML_Bool finalBuffer; +} XML_ParsingStatus; + +/* Returns status of parser with respect to being initialized, parsing, + finished, or suspended and processing the final buffer. + XXX XML_Parse() and XML_ParseBuffer() should return XML_ParsingStatus, + XXX with XML_FINISHED_OK or XML_FINISHED_ERROR replacing XML_FINISHED +*/ +XMLPARSEAPI(void) +XML_GetParsingStatus(XML_Parser parser, XML_ParsingStatus *status); + +/* Creates an XML_Parser object that can parse an external general + entity; context is a '\0'-terminated string specifying the parse + context; encoding is a '\0'-terminated string giving the name of + the externally specified encoding, or NULL if there is no + externally specified encoding. The context string consists of a + sequence of tokens separated by formfeeds (\f); a token consisting + of a name specifies that the general entity of the name is open; a + token of the form prefix=uri specifies the namespace for a + particular prefix; a token of the form =uri specifies the default + namespace. This can be called at any point after the first call to + an ExternalEntityRefHandler so longer as the parser has not yet + been freed. The new parser is completely independent and may + safely be used in a separate thread. The handlers and userData are + initialized from the parser argument. Returns NULL if out of memory. + Otherwise returns a new XML_Parser object. +*/ +XMLPARSEAPI(XML_Parser) +XML_ExternalEntityParserCreate(XML_Parser parser, + const XML_Char *context, + const XML_Char *encoding); + +enum XML_ParamEntityParsing { + XML_PARAM_ENTITY_PARSING_NEVER, + XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE, + XML_PARAM_ENTITY_PARSING_ALWAYS +}; + +/* Controls parsing of parameter entities (including the external DTD + subset). If parsing of parameter entities is enabled, then + references to external parameter entities (including the external + DTD subset) will be passed to the handler set with + XML_SetExternalEntityRefHandler. The context passed will be 0. + + Unlike external general entities, external parameter entities can + only be parsed synchronously. If the external parameter entity is + to be parsed, it must be parsed during the call to the external + entity ref handler: the complete sequence of + XML_ExternalEntityParserCreate, XML_Parse/XML_ParseBuffer and + XML_ParserFree calls must be made during this call. After + XML_ExternalEntityParserCreate has been called to create the parser + for the external parameter entity (context must be 0 for this + call), it is illegal to make any calls on the old parser until + XML_ParserFree has been called on the newly created parser. + If the library has been compiled without support for parameter + entity parsing (ie without XML_DTD being defined), then + XML_SetParamEntityParsing will return 0 if parsing of parameter + entities is requested; otherwise it will return non-zero. + Note: If XML_SetParamEntityParsing is called after XML_Parse or + XML_ParseBuffer, then it has no effect and will always return 0. +*/ +XMLPARSEAPI(int) +XML_SetParamEntityParsing(XML_Parser parser, + enum XML_ParamEntityParsing parsing); + +/* Sets the hash salt to use for internal hash calculations. + Helps in preventing DoS attacks based on predicting hash + function behavior. This must be called before parsing is started. + Returns 1 if successful, 0 when called after parsing has started. +*/ +XMLPARSEAPI(int) +XML_SetHashSalt(XML_Parser parser, + unsigned long hash_salt); + +/* If XML_Parse or XML_ParseBuffer have returned XML_STATUS_ERROR, then + XML_GetErrorCode returns information about the error. +*/ +XMLPARSEAPI(enum XML_Error) +XML_GetErrorCode(XML_Parser parser); + +/* These functions return information about the current parse + location. They may be called from any callback called to report + some parse event; in this case the location is the location of the + first of the sequence of characters that generated the event. When + called from callbacks generated by declarations in the document + prologue, the location identified isn't as neatly defined, but will + be within the relevant markup. When called outside of the callback + functions, the position indicated will be just past the last parse + event (regardless of whether there was an associated callback). + + They may also be called after returning from a call to XML_Parse + or XML_ParseBuffer. If the return value is XML_STATUS_ERROR then + the location is the location of the character at which the error + was detected; otherwise the location is the location of the last + parse event, as described above. +*/ +XMLPARSEAPI(XML_Size) XML_GetCurrentLineNumber(XML_Parser parser); +XMLPARSEAPI(XML_Size) XML_GetCurrentColumnNumber(XML_Parser parser); +XMLPARSEAPI(XML_Index) XML_GetCurrentByteIndex(XML_Parser parser); + +/* Return the number of bytes in the current event. + Returns 0 if the event is in an internal entity. +*/ +XMLPARSEAPI(int) +XML_GetCurrentByteCount(XML_Parser parser); + +/* If XML_CONTEXT_BYTES is defined, returns the input buffer, sets + the integer pointed to by offset to the offset within this buffer + of the current parse position, and sets the integer pointed to by size + to the size of this buffer (the number of input bytes). Otherwise + returns a NULL pointer. Also returns a NULL pointer if a parse isn't + active. + + NOTE: The character pointer returned should not be used outside + the handler that makes the call. +*/ +XMLPARSEAPI(const char *) +XML_GetInputContext(XML_Parser parser, + int *offset, + int *size); + +/* For backwards compatibility with previous versions. */ +#define XML_GetErrorLineNumber XML_GetCurrentLineNumber +#define XML_GetErrorColumnNumber XML_GetCurrentColumnNumber +#define XML_GetErrorByteIndex XML_GetCurrentByteIndex + +/* Frees the content model passed to the element declaration handler */ +XMLPARSEAPI(void) +XML_FreeContentModel(XML_Parser parser, XML_Content *model); + +/* Exposing the memory handling functions used in Expat */ +XMLPARSEAPI(void *) +XML_ATTR_MALLOC +XML_ATTR_ALLOC_SIZE(2) +XML_MemMalloc(XML_Parser parser, size_t size); + +XMLPARSEAPI(void *) +XML_ATTR_ALLOC_SIZE(3) +XML_MemRealloc(XML_Parser parser, void *ptr, size_t size); + +XMLPARSEAPI(void) +XML_MemFree(XML_Parser parser, void *ptr); + +/* Frees memory used by the parser. */ +XMLPARSEAPI(void) +XML_ParserFree(XML_Parser parser); + +/* Returns a string describing the error. */ +XMLPARSEAPI(const XML_LChar *) +XML_ErrorString(enum XML_Error code); + +/* Return a string containing the version number of this expat */ +XMLPARSEAPI(const XML_LChar *) +XML_ExpatVersion(void); + +typedef struct { + int major; + int minor; + int micro; +} XML_Expat_Version; + +/* Return an XML_Expat_Version structure containing numeric version + number information for this version of expat. +*/ +XMLPARSEAPI(XML_Expat_Version) +XML_ExpatVersionInfo(void); + +/* Added in Expat 1.95.5. */ +enum XML_FeatureEnum { + XML_FEATURE_END = 0, + XML_FEATURE_UNICODE, + XML_FEATURE_UNICODE_WCHAR_T, + XML_FEATURE_DTD, + XML_FEATURE_CONTEXT_BYTES, + XML_FEATURE_MIN_SIZE, + XML_FEATURE_SIZEOF_XML_CHAR, + XML_FEATURE_SIZEOF_XML_LCHAR, + XML_FEATURE_NS, + XML_FEATURE_LARGE_SIZE, + XML_FEATURE_ATTR_INFO + /* Additional features must be added to the end of this enum. */ +}; + +typedef struct { + enum XML_FeatureEnum feature; + const XML_LChar *name; + long int value; +} XML_Feature; + +XMLPARSEAPI(const XML_Feature *) +XML_GetFeatureList(void); + + +/* Expat follows the semantic versioning convention. + See http://semver.org. +*/ +#define XML_MAJOR_VERSION 2 +#define XML_MINOR_VERSION 2 +#define XML_MICRO_VERSION 0 + +#ifdef __cplusplus +} +#endif + +#endif /* not Expat_INCLUDED */ diff --git a/deps/EXPAT/expat/expat_config.h b/deps/EXPAT/expat/expat_config.h new file mode 100644 index 000000000..8aa80db0d --- /dev/null +++ b/deps/EXPAT/expat/expat_config.h @@ -0,0 +1,33 @@ +/*================================================================ +** Copyright 2000, Clark Cooper +** All rights reserved. +** +** This is free software. You are permitted to copy, distribute, or modify +** it under the terms of the MIT/X license (contained in the COPYING file +** with this distribution.) +*/ + +#ifndef EXPATCONFIG_H +#define EXPATCONFIG_H + +#include +#include + +#define XML_NS 1 +#define XML_DTD 1 +#define XML_CONTEXT_BYTES 1024 + +/* we will assume all Windows platforms are little endian */ +#define BYTEORDER 1234 + +/* Windows has memmove() available. */ +#define HAVE_MEMMOVE + +#ifdef WIN32 + #define WIN32_LEAN_AND_MEAN + #include + #undef WIN32_LEAN_AND_MEAN +#else +#endif + +#endif /* ifndef EXPATCONFIG_H */ diff --git a/deps/EXPAT/expat/expat_external.h b/deps/EXPAT/expat/expat_external.h new file mode 100644 index 000000000..56cd84367 --- /dev/null +++ b/deps/EXPAT/expat/expat_external.h @@ -0,0 +1,129 @@ +/* Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +#ifndef Expat_External_INCLUDED +#define Expat_External_INCLUDED 1 + +/* External API definitions */ + +#if defined(_MSC_EXTENSIONS) && !defined(__BEOS__) && !defined(__CYGWIN__) +#define XML_USE_MSC_EXTENSIONS 1 +#endif + +/* Expat tries very hard to make the API boundary very specifically + defined. There are two macros defined to control this boundary; + each of these can be defined before including this header to + achieve some different behavior, but doing so it not recommended or + tested frequently. + + XMLCALL - The calling convention to use for all calls across the + "library boundary." This will default to cdecl, and + try really hard to tell the compiler that's what we + want. + + XMLIMPORT - Whatever magic is needed to note that a function is + to be imported from a dynamically loaded library + (.dll, .so, or .sl, depending on your platform). + + The XMLCALL macro was added in Expat 1.95.7. The only one which is + expected to be directly useful in client code is XMLCALL. + + Note that on at least some Unix versions, the Expat library must be + compiled with the cdecl calling convention as the default since + system headers may assume the cdecl convention. +*/ +#ifndef XMLCALL +#if defined(_MSC_VER) +#define XMLCALL __cdecl +#elif defined(__GNUC__) && defined(__i386) && !defined(__INTEL_COMPILER) +#define XMLCALL __attribute__((cdecl)) +#else +/* For any platform which uses this definition and supports more than + one calling convention, we need to extend this definition to + declare the convention used on that platform, if it's possible to + do so. + + If this is the case for your platform, please file a bug report + with information on how to identify your platform via the C + pre-processor and how to specify the same calling convention as the + platform's malloc() implementation. +*/ +#define XMLCALL +#endif +#endif /* not defined XMLCALL */ + + +#if !defined(XML_STATIC) && !defined(XMLIMPORT) +#ifndef XML_BUILDING_EXPAT +/* using Expat from an application */ + +#ifdef XML_USE_MSC_EXTENSIONS +// #define XMLIMPORT __declspec(dllimport) +#endif + +#endif +#endif /* not defined XML_STATIC */ + +#if !defined(XMLIMPORT) && defined(__GNUC__) && (__GNUC__ >= 4) +#define XMLIMPORT __attribute__ ((visibility ("default"))) +#endif + +/* If we didn't define it above, define it away: */ +#ifndef XMLIMPORT +#define XMLIMPORT +#endif + +#if defined(__GNUC__) && (__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 96)) +#define XML_ATTR_MALLOC __attribute__((__malloc__)) +#else +#define XML_ATTR_MALLOC +#endif + +#if defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) +#define XML_ATTR_ALLOC_SIZE(x) __attribute__((__alloc_size__(x))) +#else +#define XML_ATTR_ALLOC_SIZE(x) +#endif + +#define XMLPARSEAPI(type) XMLIMPORT type XMLCALL + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef XML_UNICODE_WCHAR_T +#define XML_UNICODE +#endif + +#ifdef XML_UNICODE /* Information is UTF-16 encoded. */ +#ifdef XML_UNICODE_WCHAR_T +typedef wchar_t XML_Char; +typedef wchar_t XML_LChar; +#else +typedef unsigned short XML_Char; +typedef char XML_LChar; +#endif /* XML_UNICODE_WCHAR_T */ +#else /* Information is UTF-8 encoded. */ +typedef char XML_Char; +typedef char XML_LChar; +#endif /* XML_UNICODE */ + +#ifdef XML_LARGE_SIZE /* Use large integers for file/stream positions. */ +#if defined(XML_USE_MSC_EXTENSIONS) && _MSC_VER < 1400 +typedef __int64 XML_Index; +typedef unsigned __int64 XML_Size; +#else +typedef long long XML_Index; +typedef unsigned long long XML_Size; +#endif +#else +typedef long XML_Index; +typedef unsigned long XML_Size; +#endif /* XML_LARGE_SIZE */ + +#ifdef __cplusplus +} +#endif + +#endif /* not Expat_External_INCLUDED */ diff --git a/deps/EXPAT/expat/iasciitab.h b/deps/EXPAT/expat/iasciitab.h new file mode 100644 index 000000000..24a1d5ccc --- /dev/null +++ b/deps/EXPAT/expat/iasciitab.h @@ -0,0 +1,37 @@ +/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +/* Like asciitab.h, except that 0xD has code BT_S rather than BT_CR */ +/* 0x00 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x04 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x08 */ BT_NONXML, BT_S, BT_LF, BT_NONXML, +/* 0x0C */ BT_NONXML, BT_S, BT_NONXML, BT_NONXML, +/* 0x10 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x14 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x18 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x1C */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x20 */ BT_S, BT_EXCL, BT_QUOT, BT_NUM, +/* 0x24 */ BT_OTHER, BT_PERCNT, BT_AMP, BT_APOS, +/* 0x28 */ BT_LPAR, BT_RPAR, BT_AST, BT_PLUS, +/* 0x2C */ BT_COMMA, BT_MINUS, BT_NAME, BT_SOL, +/* 0x30 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT, +/* 0x34 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT, +/* 0x38 */ BT_DIGIT, BT_DIGIT, BT_COLON, BT_SEMI, +/* 0x3C */ BT_LT, BT_EQUALS, BT_GT, BT_QUEST, +/* 0x40 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX, +/* 0x44 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT, +/* 0x48 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x4C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x50 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x54 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x58 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_LSQB, +/* 0x5C */ BT_OTHER, BT_RSQB, BT_OTHER, BT_NMSTRT, +/* 0x60 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX, +/* 0x64 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT, +/* 0x68 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x6C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x70 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x74 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x78 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER, +/* 0x7C */ BT_VERBAR, BT_OTHER, BT_OTHER, BT_OTHER, diff --git a/deps/EXPAT/expat/internal.h b/deps/EXPAT/expat/internal.h new file mode 100644 index 000000000..94cb98e15 --- /dev/null +++ b/deps/EXPAT/expat/internal.h @@ -0,0 +1,95 @@ +/* internal.h + + Internal definitions used by Expat. This is not needed to compile + client code. + + The following calling convention macros are defined for frequently + called functions: + + FASTCALL - Used for those internal functions that have a simple + body and a low number of arguments and local variables. + + PTRCALL - Used for functions called though function pointers. + + PTRFASTCALL - Like PTRCALL, but for low number of arguments. + + inline - Used for selected internal functions for which inlining + may improve performance on some platforms. + + Note: Use of these macros is based on judgement, not hard rules, + and therefore subject to change. +*/ + +#if defined(__GNUC__) && defined(__i386__) && !defined(__MINGW32__) +/* We'll use this version by default only where we know it helps. + + regparm() generates warnings on Solaris boxes. See SF bug #692878. + + Instability reported with egcs on a RedHat Linux 7.3. + Let's comment out: + #define FASTCALL __attribute__((stdcall, regparm(3))) + and let's try this: +*/ +#define FASTCALL __attribute__((regparm(3))) +#define PTRFASTCALL __attribute__((regparm(3))) +#endif + +/* Using __fastcall seems to have an unexpected negative effect under + MS VC++, especially for function pointers, so we won't use it for + now on that platform. It may be reconsidered for a future release + if it can be made more effective. + Likely reason: __fastcall on Windows is like stdcall, therefore + the compiler cannot perform stack optimizations for call clusters. +*/ + +/* Make sure all of these are defined if they aren't already. */ + +#ifndef FASTCALL +#define FASTCALL +#endif + +#ifndef PTRCALL +#define PTRCALL +#endif + +#ifndef PTRFASTCALL +#define PTRFASTCALL +#endif + +#ifndef XML_MIN_SIZE +#if !defined(__cplusplus) && !defined(inline) +#ifdef __GNUC__ +#define inline __inline +#endif /* __GNUC__ */ +#endif +#endif /* XML_MIN_SIZE */ + +#ifdef __cplusplus +#define inline inline +#else +#ifndef inline +#define inline +#endif +#endif + +#ifndef UNUSED_P +# ifdef __GNUC__ +# define UNUSED_P(p) UNUSED_ ## p __attribute__((__unused__)) +# else +# define UNUSED_P(p) UNUSED_ ## p +# endif +#endif + + +#ifdef __cplusplus +extern "C" { +#endif + + +void +align_limit_to_full_utf8_characters(const char * from, const char ** fromLimRef); + + +#ifdef __cplusplus +} +#endif diff --git a/deps/EXPAT/expat/latin1tab.h b/deps/EXPAT/expat/latin1tab.h new file mode 100644 index 000000000..53c25d76b --- /dev/null +++ b/deps/EXPAT/expat/latin1tab.h @@ -0,0 +1,36 @@ +/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +/* 0x80 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0x84 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0x88 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0x8C */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0x90 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0x94 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0x98 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0x9C */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0xA0 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0xA4 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0xA8 */ BT_OTHER, BT_OTHER, BT_NMSTRT, BT_OTHER, +/* 0xAC */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0xB0 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0xB4 */ BT_OTHER, BT_NMSTRT, BT_OTHER, BT_NAME, +/* 0xB8 */ BT_OTHER, BT_OTHER, BT_NMSTRT, BT_OTHER, +/* 0xBC */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0xC0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xC4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xC8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xCC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xD0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xD4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER, +/* 0xD8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xDC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xE0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xE4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xE8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xEC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xF0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xF4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER, +/* 0xF8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xFC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, diff --git a/deps/EXPAT/expat/nametab.h b/deps/EXPAT/expat/nametab.h new file mode 100644 index 000000000..b05e62c77 --- /dev/null +++ b/deps/EXPAT/expat/nametab.h @@ -0,0 +1,150 @@ +static const unsigned namingBitmap[] = { +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, +0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, +0x00000000, 0x04000000, 0x87FFFFFE, 0x07FFFFFE, +0x00000000, 0x00000000, 0xFF7FFFFF, 0xFF7FFFFF, +0xFFFFFFFF, 0x7FF3FFFF, 0xFFFFFDFE, 0x7FFFFFFF, +0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFE00F, 0xFC31FFFF, +0x00FFFFFF, 0x00000000, 0xFFFF0000, 0xFFFFFFFF, +0xFFFFFFFF, 0xF80001FF, 0x00000003, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0xFFFFD740, 0xFFFFFFFB, 0x547F7FFF, 0x000FFFFD, +0xFFFFDFFE, 0xFFFFFFFF, 0xDFFEFFFF, 0xFFFFFFFF, +0xFFFF0003, 0xFFFFFFFF, 0xFFFF199F, 0x033FCFFF, +0x00000000, 0xFFFE0000, 0x027FFFFF, 0xFFFFFFFE, +0x0000007F, 0x00000000, 0xFFFF0000, 0x000707FF, +0x00000000, 0x07FFFFFE, 0x000007FE, 0xFFFE0000, +0xFFFFFFFF, 0x7CFFFFFF, 0x002F7FFF, 0x00000060, +0xFFFFFFE0, 0x23FFFFFF, 0xFF000000, 0x00000003, +0xFFF99FE0, 0x03C5FDFF, 0xB0000000, 0x00030003, +0xFFF987E0, 0x036DFDFF, 0x5E000000, 0x001C0000, +0xFFFBAFE0, 0x23EDFDFF, 0x00000000, 0x00000001, +0xFFF99FE0, 0x23CDFDFF, 0xB0000000, 0x00000003, +0xD63DC7E0, 0x03BFC718, 0x00000000, 0x00000000, +0xFFFDDFE0, 0x03EFFDFF, 0x00000000, 0x00000003, +0xFFFDDFE0, 0x03EFFDFF, 0x40000000, 0x00000003, +0xFFFDDFE0, 0x03FFFDFF, 0x00000000, 0x00000003, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0xFFFFFFFE, 0x000D7FFF, 0x0000003F, 0x00000000, +0xFEF02596, 0x200D6CAE, 0x0000001F, 0x00000000, +0x00000000, 0x00000000, 0xFFFFFEFF, 0x000003FF, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0xFFFFFFFF, 0xFFFF003F, 0x007FFFFF, +0x0007DAED, 0x50000000, 0x82315001, 0x002C62AB, +0x40000000, 0xF580C900, 0x00000007, 0x02010800, +0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, +0x0FFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x03FFFFFF, +0x3F3FFFFF, 0xFFFFFFFF, 0xAAFF3F3F, 0x3FFFFFFF, +0xFFFFFFFF, 0x5FDFFFFF, 0x0FCF1FDC, 0x1FDC1FFF, +0x00000000, 0x00004C40, 0x00000000, 0x00000000, +0x00000007, 0x00000000, 0x00000000, 0x00000000, +0x00000080, 0x000003FE, 0xFFFFFFFE, 0xFFFFFFFF, +0x001FFFFF, 0xFFFFFFFE, 0xFFFFFFFF, 0x07FFFFFF, +0xFFFFFFE0, 0x00001FFF, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, +0xFFFFFFFF, 0x0000003F, 0x00000000, 0x00000000, +0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, +0xFFFFFFFF, 0x0000000F, 0x00000000, 0x00000000, +0x00000000, 0x07FF6000, 0x87FFFFFE, 0x07FFFFFE, +0x00000000, 0x00800000, 0xFF7FFFFF, 0xFF7FFFFF, +0x00FFFFFF, 0x00000000, 0xFFFF0000, 0xFFFFFFFF, +0xFFFFFFFF, 0xF80001FF, 0x00030003, 0x00000000, +0xFFFFFFFF, 0xFFFFFFFF, 0x0000003F, 0x00000003, +0xFFFFD7C0, 0xFFFFFFFB, 0x547F7FFF, 0x000FFFFD, +0xFFFFDFFE, 0xFFFFFFFF, 0xDFFEFFFF, 0xFFFFFFFF, +0xFFFF007B, 0xFFFFFFFF, 0xFFFF199F, 0x033FCFFF, +0x00000000, 0xFFFE0000, 0x027FFFFF, 0xFFFFFFFE, +0xFFFE007F, 0xBBFFFFFB, 0xFFFF0016, 0x000707FF, +0x00000000, 0x07FFFFFE, 0x0007FFFF, 0xFFFF03FF, +0xFFFFFFFF, 0x7CFFFFFF, 0xFFEF7FFF, 0x03FF3DFF, +0xFFFFFFEE, 0xF3FFFFFF, 0xFF1E3FFF, 0x0000FFCF, +0xFFF99FEE, 0xD3C5FDFF, 0xB080399F, 0x0003FFCF, +0xFFF987E4, 0xD36DFDFF, 0x5E003987, 0x001FFFC0, +0xFFFBAFEE, 0xF3EDFDFF, 0x00003BBF, 0x0000FFC1, +0xFFF99FEE, 0xF3CDFDFF, 0xB0C0398F, 0x0000FFC3, +0xD63DC7EC, 0xC3BFC718, 0x00803DC7, 0x0000FF80, +0xFFFDDFEE, 0xC3EFFDFF, 0x00603DDF, 0x0000FFC3, +0xFFFDDFEC, 0xC3EFFDFF, 0x40603DDF, 0x0000FFC3, +0xFFFDDFEC, 0xC3FFFDFF, 0x00803DCF, 0x0000FFC3, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0xFFFFFFFE, 0x07FF7FFF, 0x03FF7FFF, 0x00000000, +0xFEF02596, 0x3BFF6CAE, 0x03FF3F5F, 0x00000000, +0x03000000, 0xC2A003FF, 0xFFFFFEFF, 0xFFFE03FF, +0xFEBF0FDF, 0x02FE3FFF, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x1FFF0000, 0x00000002, +0x000000A0, 0x003EFFFE, 0xFFFFFFFE, 0xFFFFFFFF, +0x661FFFFF, 0xFFFFFFFE, 0xFFFFFFFF, 0x77FFFFFF, +}; +static const unsigned char nmstrtPages[] = { +0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x00, +0x00, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, +0x10, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x13, +0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x15, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x17, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x18, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; +static const unsigned char namePages[] = { +0x19, 0x03, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x00, +0x00, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, +0x10, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x13, +0x26, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x27, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x17, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x18, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; diff --git a/deps/EXPAT/expat/utf8tab.h b/deps/EXPAT/expat/utf8tab.h new file mode 100644 index 000000000..7bb3e7760 --- /dev/null +++ b/deps/EXPAT/expat/utf8tab.h @@ -0,0 +1,37 @@ +/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + + +/* 0x80 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0x84 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0x88 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0x8C */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0x90 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0x94 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0x98 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0x9C */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xA0 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xA4 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xA8 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xAC */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xB0 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xB4 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xB8 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xBC */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xC0 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xC4 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xC8 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xCC */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xD0 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xD4 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xD8 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xDC */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xE0 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3, +/* 0xE4 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3, +/* 0xE8 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3, +/* 0xEC */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3, +/* 0xF0 */ BT_LEAD4, BT_LEAD4, BT_LEAD4, BT_LEAD4, +/* 0xF4 */ BT_LEAD4, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0xF8 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0xFC */ BT_NONXML, BT_NONXML, BT_MALFORM, BT_MALFORM, diff --git a/deps/EXPAT/expat/xmlparse.c b/deps/EXPAT/expat/xmlparse.c new file mode 100644 index 000000000..fbe5e0200 --- /dev/null +++ b/deps/EXPAT/expat/xmlparse.c @@ -0,0 +1,6458 @@ +/* Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +#include +#include /* memset(), memcpy() */ +#include +#include /* UINT_MAX */ + +#ifdef WIN32 +#define getpid GetCurrentProcessId +#else +#include /* gettimeofday() */ +#include /* getpid() */ +#include /* getpid() */ +#endif + +#define XML_BUILDING_EXPAT 1 + +#include "expat_config.h" +#include "ascii.h" +#include "expat.h" + +#ifdef XML_UNICODE +#define XML_ENCODE_MAX XML_UTF16_ENCODE_MAX +#define XmlConvert XmlUtf16Convert +#define XmlGetInternalEncoding XmlGetUtf16InternalEncoding +#define XmlGetInternalEncodingNS XmlGetUtf16InternalEncodingNS +#define XmlEncode XmlUtf16Encode +/* Using pointer subtraction to convert to integer type. */ +#define MUST_CONVERT(enc, s) (!(enc)->isUtf16 || (((char *)(s) - (char *)NULL) & 1)) +typedef unsigned short ICHAR; +#else +#define XML_ENCODE_MAX XML_UTF8_ENCODE_MAX +#define XmlConvert XmlUtf8Convert +#define XmlGetInternalEncoding XmlGetUtf8InternalEncoding +#define XmlGetInternalEncodingNS XmlGetUtf8InternalEncodingNS +#define XmlEncode XmlUtf8Encode +#define MUST_CONVERT(enc, s) (!(enc)->isUtf8) +typedef char ICHAR; +#endif + + +#ifndef XML_NS + +#define XmlInitEncodingNS XmlInitEncoding +#define XmlInitUnknownEncodingNS XmlInitUnknownEncoding +#undef XmlGetInternalEncodingNS +#define XmlGetInternalEncodingNS XmlGetInternalEncoding +#define XmlParseXmlDeclNS XmlParseXmlDecl + +#endif + +#ifdef XML_UNICODE + +#ifdef XML_UNICODE_WCHAR_T +#define XML_T(x) (const wchar_t)x +#define XML_L(x) L ## x +#else +#define XML_T(x) (const unsigned short)x +#define XML_L(x) x +#endif + +#else + +#define XML_T(x) x +#define XML_L(x) x + +#endif + +/* Round up n to be a multiple of sz, where sz is a power of 2. */ +#define ROUND_UP(n, sz) (((n) + ((sz) - 1)) & ~((sz) - 1)) + +/* Handle the case where memmove() doesn't exist. */ +#ifndef HAVE_MEMMOVE +#ifdef HAVE_BCOPY +#define memmove(d,s,l) bcopy((s),(d),(l)) +#else +#error memmove does not exist on this platform, nor is a substitute available +#endif /* HAVE_BCOPY */ +#endif /* HAVE_MEMMOVE */ + +#include "internal.h" +#include "xmltok.h" +#include "xmlrole.h" + +typedef const XML_Char *KEY; + +typedef struct { + KEY name; +} NAMED; + +typedef struct { + NAMED **v; + unsigned char power; + size_t size; + size_t used; + const XML_Memory_Handling_Suite *mem; +} HASH_TABLE; + +/* Basic character hash algorithm, taken from Python's string hash: + h = h * 1000003 ^ character, the constant being a prime number. + +*/ +#ifdef XML_UNICODE +#define CHAR_HASH(h, c) \ + (((h) * 0xF4243) ^ (unsigned short)(c)) +#else +#define CHAR_HASH(h, c) \ + (((h) * 0xF4243) ^ (unsigned char)(c)) +#endif + +/* For probing (after a collision) we need a step size relative prime + to the hash table size, which is a power of 2. We use double-hashing, + since we can calculate a second hash value cheaply by taking those bits + of the first hash value that were discarded (masked out) when the table + index was calculated: index = hash & mask, where mask = table->size - 1. + We limit the maximum step size to table->size / 4 (mask >> 2) and make + it odd, since odd numbers are always relative prime to a power of 2. +*/ +#define SECOND_HASH(hash, mask, power) \ + ((((hash) & ~(mask)) >> ((power) - 1)) & ((mask) >> 2)) +#define PROBE_STEP(hash, mask, power) \ + ((unsigned char)((SECOND_HASH(hash, mask, power)) | 1)) + +typedef struct { + NAMED **p; + NAMED **end; +} HASH_TABLE_ITER; + +#define INIT_TAG_BUF_SIZE 32 /* must be a multiple of sizeof(XML_Char) */ +#define INIT_DATA_BUF_SIZE 1024 +#define INIT_ATTS_SIZE 16 +#define INIT_ATTS_VERSION 0xFFFFFFFF +#define INIT_BLOCK_SIZE 1024 +#define INIT_BUFFER_SIZE 1024 + +#define EXPAND_SPARE 24 + +typedef struct binding { + struct prefix *prefix; + struct binding *nextTagBinding; + struct binding *prevPrefixBinding; + const struct attribute_id *attId; + XML_Char *uri; + int uriLen; + int uriAlloc; +} BINDING; + +typedef struct prefix { + const XML_Char *name; + BINDING *binding; +} PREFIX; + +typedef struct { + const XML_Char *str; + const XML_Char *localPart; + const XML_Char *prefix; + int strLen; + int uriLen; + int prefixLen; +} TAG_NAME; + +/* TAG represents an open element. + The name of the element is stored in both the document and API + encodings. The memory buffer 'buf' is a separately-allocated + memory area which stores the name. During the XML_Parse()/ + XMLParseBuffer() when the element is open, the memory for the 'raw' + version of the name (in the document encoding) is shared with the + document buffer. If the element is open across calls to + XML_Parse()/XML_ParseBuffer(), the buffer is re-allocated to + contain the 'raw' name as well. + + A parser re-uses these structures, maintaining a list of allocated + TAG objects in a free list. +*/ +typedef struct tag { + struct tag *parent; /* parent of this element */ + const char *rawName; /* tagName in the original encoding */ + int rawNameLength; + TAG_NAME name; /* tagName in the API encoding */ + char *buf; /* buffer for name components */ + char *bufEnd; /* end of the buffer */ + BINDING *bindings; +} TAG; + +typedef struct { + const XML_Char *name; + const XML_Char *textPtr; + int textLen; /* length in XML_Chars */ + int processed; /* # of processed bytes - when suspended */ + const XML_Char *systemId; + const XML_Char *base; + const XML_Char *publicId; + const XML_Char *notation; + XML_Bool open; + XML_Bool is_param; + XML_Bool is_internal; /* true if declared in internal subset outside PE */ +} ENTITY; + +typedef struct { + enum XML_Content_Type type; + enum XML_Content_Quant quant; + const XML_Char * name; + int firstchild; + int lastchild; + int childcnt; + int nextsib; +} CONTENT_SCAFFOLD; + +#define INIT_SCAFFOLD_ELEMENTS 32 + +typedef struct block { + struct block *next; + int size; + XML_Char s[1]; +} BLOCK; + +typedef struct { + BLOCK *blocks; + BLOCK *freeBlocks; + const XML_Char *end; + XML_Char *ptr; + XML_Char *start; + const XML_Memory_Handling_Suite *mem; +} STRING_POOL; + +/* The XML_Char before the name is used to determine whether + an attribute has been specified. */ +typedef struct attribute_id { + XML_Char *name; + PREFIX *prefix; + XML_Bool maybeTokenized; + XML_Bool xmlns; +} ATTRIBUTE_ID; + +typedef struct { + const ATTRIBUTE_ID *id; + XML_Bool isCdata; + const XML_Char *value; +} DEFAULT_ATTRIBUTE; + +typedef struct { + unsigned long version; + unsigned long hash; + const XML_Char *uriName; +} NS_ATT; + +typedef struct { + const XML_Char *name; + PREFIX *prefix; + const ATTRIBUTE_ID *idAtt; + int nDefaultAtts; + int allocDefaultAtts; + DEFAULT_ATTRIBUTE *defaultAtts; +} ELEMENT_TYPE; + +typedef struct { + HASH_TABLE generalEntities; + HASH_TABLE elementTypes; + HASH_TABLE attributeIds; + HASH_TABLE prefixes; + STRING_POOL pool; + STRING_POOL entityValuePool; + /* false once a parameter entity reference has been skipped */ + XML_Bool keepProcessing; + /* true once an internal or external PE reference has been encountered; + this includes the reference to an external subset */ + XML_Bool hasParamEntityRefs; + XML_Bool standalone; +#ifdef XML_DTD + /* indicates if external PE has been read */ + XML_Bool paramEntityRead; + HASH_TABLE paramEntities; +#endif /* XML_DTD */ + PREFIX defaultPrefix; + /* === scaffolding for building content model === */ + XML_Bool in_eldecl; + CONTENT_SCAFFOLD *scaffold; + unsigned contentStringLen; + unsigned scaffSize; + unsigned scaffCount; + int scaffLevel; + int *scaffIndex; +} DTD; + +typedef struct open_internal_entity { + const char *internalEventPtr; + const char *internalEventEndPtr; + struct open_internal_entity *next; + ENTITY *entity; + int startTagLevel; + XML_Bool betweenDecl; /* WFC: PE Between Declarations */ +} OPEN_INTERNAL_ENTITY; + +typedef enum XML_Error PTRCALL Processor(XML_Parser parser, + const char *start, + const char *end, + const char **endPtr); + +static Processor prologProcessor; +static Processor prologInitProcessor; +static Processor contentProcessor; +static Processor cdataSectionProcessor; +#ifdef XML_DTD +static Processor ignoreSectionProcessor; +static Processor externalParEntProcessor; +static Processor externalParEntInitProcessor; +static Processor entityValueProcessor; +static Processor entityValueInitProcessor; +#endif /* XML_DTD */ +static Processor epilogProcessor; +static Processor errorProcessor; +static Processor externalEntityInitProcessor; +static Processor externalEntityInitProcessor2; +static Processor externalEntityInitProcessor3; +static Processor externalEntityContentProcessor; +static Processor internalEntityProcessor; + +static enum XML_Error +handleUnknownEncoding(XML_Parser parser, const XML_Char *encodingName); +static enum XML_Error +processXmlDecl(XML_Parser parser, int isGeneralTextEntity, + const char *s, const char *next); +static enum XML_Error +initializeEncoding(XML_Parser parser); +static enum XML_Error +doProlog(XML_Parser parser, const ENCODING *enc, const char *s, + const char *end, int tok, const char *next, const char **nextPtr, + XML_Bool haveMore); +static enum XML_Error +processInternalEntity(XML_Parser parser, ENTITY *entity, + XML_Bool betweenDecl); +static enum XML_Error +doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc, + const char *start, const char *end, const char **endPtr, + XML_Bool haveMore); +static enum XML_Error +doCdataSection(XML_Parser parser, const ENCODING *, const char **startPtr, + const char *end, const char **nextPtr, XML_Bool haveMore); +#ifdef XML_DTD +static enum XML_Error +doIgnoreSection(XML_Parser parser, const ENCODING *, const char **startPtr, + const char *end, const char **nextPtr, XML_Bool haveMore); +#endif /* XML_DTD */ + +static enum XML_Error +storeAtts(XML_Parser parser, const ENCODING *, const char *s, + TAG_NAME *tagNamePtr, BINDING **bindingsPtr); +static enum XML_Error +addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId, + const XML_Char *uri, BINDING **bindingsPtr); +static int +defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *, XML_Bool isCdata, + XML_Bool isId, const XML_Char *dfltValue, XML_Parser parser); +static enum XML_Error +storeAttributeValue(XML_Parser parser, const ENCODING *, XML_Bool isCdata, + const char *, const char *, STRING_POOL *); +static enum XML_Error +appendAttributeValue(XML_Parser parser, const ENCODING *, XML_Bool isCdata, + const char *, const char *, STRING_POOL *); +static ATTRIBUTE_ID * +getAttributeId(XML_Parser parser, const ENCODING *enc, const char *start, + const char *end); +static int +setElementTypePrefix(XML_Parser parser, ELEMENT_TYPE *); +static enum XML_Error +storeEntityValue(XML_Parser parser, const ENCODING *enc, const char *start, + const char *end); +static int +reportProcessingInstruction(XML_Parser parser, const ENCODING *enc, + const char *start, const char *end); +static int +reportComment(XML_Parser parser, const ENCODING *enc, const char *start, + const char *end); +static void +reportDefault(XML_Parser parser, const ENCODING *enc, const char *start, + const char *end); + +static const XML_Char * getContext(XML_Parser parser); +static XML_Bool +setContext(XML_Parser parser, const XML_Char *context); + +static void FASTCALL normalizePublicId(XML_Char *s); + +static DTD * dtdCreate(const XML_Memory_Handling_Suite *ms); +/* do not call if parentParser != NULL */ +static void dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms); +static void +dtdDestroy(DTD *p, XML_Bool isDocEntity, const XML_Memory_Handling_Suite *ms); +static int +dtdCopy(XML_Parser oldParser, + DTD *newDtd, const DTD *oldDtd, const XML_Memory_Handling_Suite *ms); +static int +copyEntityTable(XML_Parser oldParser, + HASH_TABLE *, STRING_POOL *, const HASH_TABLE *); +static NAMED * +lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize); +static void FASTCALL +hashTableInit(HASH_TABLE *, const XML_Memory_Handling_Suite *ms); +static void FASTCALL hashTableClear(HASH_TABLE *); +static void FASTCALL hashTableDestroy(HASH_TABLE *); +static void FASTCALL +hashTableIterInit(HASH_TABLE_ITER *, const HASH_TABLE *); +static NAMED * FASTCALL hashTableIterNext(HASH_TABLE_ITER *); + +static void FASTCALL +poolInit(STRING_POOL *, const XML_Memory_Handling_Suite *ms); +static void FASTCALL poolClear(STRING_POOL *); +static void FASTCALL poolDestroy(STRING_POOL *); +static XML_Char * +poolAppend(STRING_POOL *pool, const ENCODING *enc, + const char *ptr, const char *end); +static XML_Char * +poolStoreString(STRING_POOL *pool, const ENCODING *enc, + const char *ptr, const char *end); +static XML_Bool FASTCALL poolGrow(STRING_POOL *pool); +static const XML_Char * FASTCALL +poolCopyString(STRING_POOL *pool, const XML_Char *s); +static const XML_Char * +poolCopyStringN(STRING_POOL *pool, const XML_Char *s, int n); +static const XML_Char * FASTCALL +poolAppendString(STRING_POOL *pool, const XML_Char *s); + +static int FASTCALL nextScaffoldPart(XML_Parser parser); +static XML_Content * build_model(XML_Parser parser); +static ELEMENT_TYPE * +getElementType(XML_Parser parser, const ENCODING *enc, + const char *ptr, const char *end); + +static unsigned long generate_hash_secret_salt(XML_Parser parser); +static XML_Bool startParsing(XML_Parser parser); + +static XML_Parser +parserCreate(const XML_Char *encodingName, + const XML_Memory_Handling_Suite *memsuite, + const XML_Char *nameSep, + DTD *dtd); + +static void +parserInit(XML_Parser parser, const XML_Char *encodingName); + +#define poolStart(pool) ((pool)->start) +#define poolEnd(pool) ((pool)->ptr) +#define poolLength(pool) ((pool)->ptr - (pool)->start) +#define poolChop(pool) ((void)--(pool->ptr)) +#define poolLastChar(pool) (((pool)->ptr)[-1]) +#define poolDiscard(pool) ((pool)->ptr = (pool)->start) +#define poolFinish(pool) ((pool)->start = (pool)->ptr) +#define poolAppendChar(pool, c) \ + (((pool)->ptr == (pool)->end && !poolGrow(pool)) \ + ? 0 \ + : ((*((pool)->ptr)++ = c), 1)) + +struct XML_ParserStruct { + /* The first member must be userData so that the XML_GetUserData + macro works. */ + void *m_userData; + void *m_handlerArg; + char *m_buffer; + const XML_Memory_Handling_Suite m_mem; + /* first character to be parsed */ + const char *m_bufferPtr; + /* past last character to be parsed */ + char *m_bufferEnd; + /* allocated end of buffer */ + const char *m_bufferLim; + XML_Index m_parseEndByteIndex; + const char *m_parseEndPtr; + XML_Char *m_dataBuf; + XML_Char *m_dataBufEnd; + XML_StartElementHandler m_startElementHandler; + XML_EndElementHandler m_endElementHandler; + XML_CharacterDataHandler m_characterDataHandler; + XML_ProcessingInstructionHandler m_processingInstructionHandler; + XML_CommentHandler m_commentHandler; + XML_StartCdataSectionHandler m_startCdataSectionHandler; + XML_EndCdataSectionHandler m_endCdataSectionHandler; + XML_DefaultHandler m_defaultHandler; + XML_StartDoctypeDeclHandler m_startDoctypeDeclHandler; + XML_EndDoctypeDeclHandler m_endDoctypeDeclHandler; + XML_UnparsedEntityDeclHandler m_unparsedEntityDeclHandler; + XML_NotationDeclHandler m_notationDeclHandler; + XML_StartNamespaceDeclHandler m_startNamespaceDeclHandler; + XML_EndNamespaceDeclHandler m_endNamespaceDeclHandler; + XML_NotStandaloneHandler m_notStandaloneHandler; + XML_ExternalEntityRefHandler m_externalEntityRefHandler; + XML_Parser m_externalEntityRefHandlerArg; + XML_SkippedEntityHandler m_skippedEntityHandler; + XML_UnknownEncodingHandler m_unknownEncodingHandler; + XML_ElementDeclHandler m_elementDeclHandler; + XML_AttlistDeclHandler m_attlistDeclHandler; + XML_EntityDeclHandler m_entityDeclHandler; + XML_XmlDeclHandler m_xmlDeclHandler; + const ENCODING *m_encoding; + INIT_ENCODING m_initEncoding; + const ENCODING *m_internalEncoding; + const XML_Char *m_protocolEncodingName; + XML_Bool m_ns; + XML_Bool m_ns_triplets; + void *m_unknownEncodingMem; + void *m_unknownEncodingData; + void *m_unknownEncodingHandlerData; + void (XMLCALL *m_unknownEncodingRelease)(void *); + PROLOG_STATE m_prologState; + Processor *m_processor; + enum XML_Error m_errorCode; + const char *m_eventPtr; + const char *m_eventEndPtr; + const char *m_positionPtr; + OPEN_INTERNAL_ENTITY *m_openInternalEntities; + OPEN_INTERNAL_ENTITY *m_freeInternalEntities; + XML_Bool m_defaultExpandInternalEntities; + int m_tagLevel; + ENTITY *m_declEntity; + const XML_Char *m_doctypeName; + const XML_Char *m_doctypeSysid; + const XML_Char *m_doctypePubid; + const XML_Char *m_declAttributeType; + const XML_Char *m_declNotationName; + const XML_Char *m_declNotationPublicId; + ELEMENT_TYPE *m_declElementType; + ATTRIBUTE_ID *m_declAttributeId; + XML_Bool m_declAttributeIsCdata; + XML_Bool m_declAttributeIsId; + DTD *m_dtd; + const XML_Char *m_curBase; + TAG *m_tagStack; + TAG *m_freeTagList; + BINDING *m_inheritedBindings; + BINDING *m_freeBindingList; + int m_attsSize; + int m_nSpecifiedAtts; + int m_idAttIndex; + ATTRIBUTE *m_atts; + NS_ATT *m_nsAtts; + unsigned long m_nsAttsVersion; + unsigned char m_nsAttsPower; +#ifdef XML_ATTR_INFO + XML_AttrInfo *m_attInfo; +#endif + POSITION m_position; + STRING_POOL m_tempPool; + STRING_POOL m_temp2Pool; + char *m_groupConnector; + unsigned int m_groupSize; + XML_Char m_namespaceSeparator; + XML_Parser m_parentParser; + XML_ParsingStatus m_parsingStatus; +#ifdef XML_DTD + XML_Bool m_isParamEntity; + XML_Bool m_useForeignDTD; + enum XML_ParamEntityParsing m_paramEntityParsing; +#endif + unsigned long m_hash_secret_salt; +}; + +#define MALLOC(s) (parser->m_mem.malloc_fcn((s))) +#define REALLOC(p,s) (parser->m_mem.realloc_fcn((p),(s))) +#define FREE(p) (parser->m_mem.free_fcn((p))) + +#define userData (parser->m_userData) +#define handlerArg (parser->m_handlerArg) +#define startElementHandler (parser->m_startElementHandler) +#define endElementHandler (parser->m_endElementHandler) +#define characterDataHandler (parser->m_characterDataHandler) +#define processingInstructionHandler \ + (parser->m_processingInstructionHandler) +#define commentHandler (parser->m_commentHandler) +#define startCdataSectionHandler \ + (parser->m_startCdataSectionHandler) +#define endCdataSectionHandler (parser->m_endCdataSectionHandler) +#define defaultHandler (parser->m_defaultHandler) +#define startDoctypeDeclHandler (parser->m_startDoctypeDeclHandler) +#define endDoctypeDeclHandler (parser->m_endDoctypeDeclHandler) +#define unparsedEntityDeclHandler \ + (parser->m_unparsedEntityDeclHandler) +#define notationDeclHandler (parser->m_notationDeclHandler) +#define startNamespaceDeclHandler \ + (parser->m_startNamespaceDeclHandler) +#define endNamespaceDeclHandler (parser->m_endNamespaceDeclHandler) +#define notStandaloneHandler (parser->m_notStandaloneHandler) +#define externalEntityRefHandler \ + (parser->m_externalEntityRefHandler) +#define externalEntityRefHandlerArg \ + (parser->m_externalEntityRefHandlerArg) +#define internalEntityRefHandler \ + (parser->m_internalEntityRefHandler) +#define skippedEntityHandler (parser->m_skippedEntityHandler) +#define unknownEncodingHandler (parser->m_unknownEncodingHandler) +#define elementDeclHandler (parser->m_elementDeclHandler) +#define attlistDeclHandler (parser->m_attlistDeclHandler) +#define entityDeclHandler (parser->m_entityDeclHandler) +#define xmlDeclHandler (parser->m_xmlDeclHandler) +#define encoding (parser->m_encoding) +#define initEncoding (parser->m_initEncoding) +#define internalEncoding (parser->m_internalEncoding) +#define unknownEncodingMem (parser->m_unknownEncodingMem) +#define unknownEncodingData (parser->m_unknownEncodingData) +#define unknownEncodingHandlerData \ + (parser->m_unknownEncodingHandlerData) +#define unknownEncodingRelease (parser->m_unknownEncodingRelease) +#define protocolEncodingName (parser->m_protocolEncodingName) +#define ns (parser->m_ns) +#define ns_triplets (parser->m_ns_triplets) +#define prologState (parser->m_prologState) +#define processor (parser->m_processor) +#define errorCode (parser->m_errorCode) +#define eventPtr (parser->m_eventPtr) +#define eventEndPtr (parser->m_eventEndPtr) +#define positionPtr (parser->m_positionPtr) +#define position (parser->m_position) +#define openInternalEntities (parser->m_openInternalEntities) +#define freeInternalEntities (parser->m_freeInternalEntities) +#define defaultExpandInternalEntities \ + (parser->m_defaultExpandInternalEntities) +#define tagLevel (parser->m_tagLevel) +#define buffer (parser->m_buffer) +#define bufferPtr (parser->m_bufferPtr) +#define bufferEnd (parser->m_bufferEnd) +#define parseEndByteIndex (parser->m_parseEndByteIndex) +#define parseEndPtr (parser->m_parseEndPtr) +#define bufferLim (parser->m_bufferLim) +#define dataBuf (parser->m_dataBuf) +#define dataBufEnd (parser->m_dataBufEnd) +#define _dtd (parser->m_dtd) +#define curBase (parser->m_curBase) +#define declEntity (parser->m_declEntity) +#define doctypeName (parser->m_doctypeName) +#define doctypeSysid (parser->m_doctypeSysid) +#define doctypePubid (parser->m_doctypePubid) +#define declAttributeType (parser->m_declAttributeType) +#define declNotationName (parser->m_declNotationName) +#define declNotationPublicId (parser->m_declNotationPublicId) +#define declElementType (parser->m_declElementType) +#define declAttributeId (parser->m_declAttributeId) +#define declAttributeIsCdata (parser->m_declAttributeIsCdata) +#define declAttributeIsId (parser->m_declAttributeIsId) +#define freeTagList (parser->m_freeTagList) +#define freeBindingList (parser->m_freeBindingList) +#define inheritedBindings (parser->m_inheritedBindings) +#define tagStack (parser->m_tagStack) +#define atts (parser->m_atts) +#define attsSize (parser->m_attsSize) +#define nSpecifiedAtts (parser->m_nSpecifiedAtts) +#define idAttIndex (parser->m_idAttIndex) +#define nsAtts (parser->m_nsAtts) +#define nsAttsVersion (parser->m_nsAttsVersion) +#define nsAttsPower (parser->m_nsAttsPower) +#define attInfo (parser->m_attInfo) +#define tempPool (parser->m_tempPool) +#define temp2Pool (parser->m_temp2Pool) +#define groupConnector (parser->m_groupConnector) +#define groupSize (parser->m_groupSize) +#define namespaceSeparator (parser->m_namespaceSeparator) +#define parentParser (parser->m_parentParser) +#define ps_parsing (parser->m_parsingStatus.parsing) +#define ps_finalBuffer (parser->m_parsingStatus.finalBuffer) +#ifdef XML_DTD +#define isParamEntity (parser->m_isParamEntity) +#define useForeignDTD (parser->m_useForeignDTD) +#define paramEntityParsing (parser->m_paramEntityParsing) +#endif /* XML_DTD */ +#define hash_secret_salt (parser->m_hash_secret_salt) + +XML_Parser XMLCALL +XML_ParserCreate(const XML_Char *encodingName) +{ + return XML_ParserCreate_MM(encodingName, NULL, NULL); +} + +XML_Parser XMLCALL +XML_ParserCreateNS(const XML_Char *encodingName, XML_Char nsSep) +{ + XML_Char tmp[2]; + *tmp = nsSep; + return XML_ParserCreate_MM(encodingName, NULL, tmp); +} + +static const XML_Char implicitContext[] = { + ASCII_x, ASCII_m, ASCII_l, ASCII_EQUALS, ASCII_h, ASCII_t, ASCII_t, ASCII_p, + ASCII_COLON, ASCII_SLASH, ASCII_SLASH, ASCII_w, ASCII_w, ASCII_w, + ASCII_PERIOD, ASCII_w, ASCII_3, ASCII_PERIOD, ASCII_o, ASCII_r, ASCII_g, + ASCII_SLASH, ASCII_X, ASCII_M, ASCII_L, ASCII_SLASH, ASCII_1, ASCII_9, + ASCII_9, ASCII_8, ASCII_SLASH, ASCII_n, ASCII_a, ASCII_m, ASCII_e, + ASCII_s, ASCII_p, ASCII_a, ASCII_c, ASCII_e, '\0' +}; + +static unsigned long +gather_time_entropy(void) +{ +#ifdef WIN32 + FILETIME ft; + GetSystemTimeAsFileTime(&ft); /* never fails */ + return ft.dwHighDateTime ^ ft.dwLowDateTime; +#else + struct timeval tv; + int gettimeofday_res; + + gettimeofday_res = gettimeofday(&tv, NULL); + assert (gettimeofday_res == 0); + + /* Microseconds time is <20 bits entropy */ + return tv.tv_usec; +#endif +} + +static unsigned long +generate_hash_secret_salt(XML_Parser parser) +{ + /* Process ID is 0 bits entropy if attacker has local access + * XML_Parser address is few bits of entropy if attacker has local access */ + // Prusa3D specific: Fix for a following warning, which turns to an error on some Perl/XS installations: + // error: cast from 'XML_Parser' to 'long unsigned int' loses precision [-fpermissive] + unsigned long *parser_addr = (unsigned long*)&parser; + const unsigned long entropy = + gather_time_entropy() ^ getpid() ^ *parser_addr; + + /* Factors are 2^31-1 and 2^61-1 (Mersenne primes M31 and M61) */ + if (sizeof(unsigned long) == 4) { + return entropy * 2147483647; + } else { + return entropy * (unsigned long)2305843009213693951; + } +} + +static XML_Bool /* only valid for root parser */ +startParsing(XML_Parser parser) +{ + /* hash functions must be initialized before setContext() is called */ + if (hash_secret_salt == 0) + hash_secret_salt = generate_hash_secret_salt(parser); + if (ns) { + /* implicit context only set for root parser, since child + parsers (i.e. external entity parsers) will inherit it + */ + return setContext(parser, implicitContext); + } + return XML_TRUE; +} + +XML_Parser XMLCALL +XML_ParserCreate_MM(const XML_Char *encodingName, + const XML_Memory_Handling_Suite *memsuite, + const XML_Char *nameSep) +{ + return parserCreate(encodingName, memsuite, nameSep, NULL); +} + +static XML_Parser +parserCreate(const XML_Char *encodingName, + const XML_Memory_Handling_Suite *memsuite, + const XML_Char *nameSep, + DTD *dtd) +{ + XML_Parser parser; + + if (memsuite) { + XML_Memory_Handling_Suite *mtemp; + parser = (XML_Parser) + memsuite->malloc_fcn(sizeof(struct XML_ParserStruct)); + if (parser != NULL) { + mtemp = (XML_Memory_Handling_Suite *)&(parser->m_mem); + mtemp->malloc_fcn = memsuite->malloc_fcn; + mtemp->realloc_fcn = memsuite->realloc_fcn; + mtemp->free_fcn = memsuite->free_fcn; + } + } + else { + XML_Memory_Handling_Suite *mtemp; + parser = (XML_Parser)malloc(sizeof(struct XML_ParserStruct)); + if (parser != NULL) { + mtemp = (XML_Memory_Handling_Suite *)&(parser->m_mem); + mtemp->malloc_fcn = malloc; + mtemp->realloc_fcn = realloc; + mtemp->free_fcn = free; + } + } + + if (!parser) + return parser; + + buffer = NULL; + bufferLim = NULL; + + attsSize = INIT_ATTS_SIZE; + atts = (ATTRIBUTE *)MALLOC(attsSize * sizeof(ATTRIBUTE)); + if (atts == NULL) { + FREE(parser); + return NULL; + } +#ifdef XML_ATTR_INFO + attInfo = (XML_AttrInfo*)MALLOC(attsSize * sizeof(XML_AttrInfo)); + if (attInfo == NULL) { + FREE(atts); + FREE(parser); + return NULL; + } +#endif + dataBuf = (XML_Char *)MALLOC(INIT_DATA_BUF_SIZE * sizeof(XML_Char)); + if (dataBuf == NULL) { + FREE(atts); +#ifdef XML_ATTR_INFO + FREE(attInfo); +#endif + FREE(parser); + return NULL; + } + dataBufEnd = dataBuf + INIT_DATA_BUF_SIZE; + + if (dtd) + _dtd = dtd; + else { + _dtd = dtdCreate(&parser->m_mem); + if (_dtd == NULL) { + FREE(dataBuf); + FREE(atts); +#ifdef XML_ATTR_INFO + FREE(attInfo); +#endif + FREE(parser); + return NULL; + } + } + + freeBindingList = NULL; + freeTagList = NULL; + freeInternalEntities = NULL; + + groupSize = 0; + groupConnector = NULL; + + unknownEncodingHandler = NULL; + unknownEncodingHandlerData = NULL; + + namespaceSeparator = ASCII_EXCL; + ns = XML_FALSE; + ns_triplets = XML_FALSE; + + nsAtts = NULL; + nsAttsVersion = 0; + nsAttsPower = 0; + + poolInit(&tempPool, &(parser->m_mem)); + poolInit(&temp2Pool, &(parser->m_mem)); + parserInit(parser, encodingName); + + if (encodingName && !protocolEncodingName) { + XML_ParserFree(parser); + return NULL; + } + + if (nameSep) { + ns = XML_TRUE; + internalEncoding = XmlGetInternalEncodingNS(); + namespaceSeparator = *nameSep; + } + else { + internalEncoding = XmlGetInternalEncoding(); + } + + return parser; +} + +static void +parserInit(XML_Parser parser, const XML_Char *encodingName) +{ + processor = prologInitProcessor; + XmlPrologStateInit(&prologState); + protocolEncodingName = (encodingName != NULL + ? poolCopyString(&tempPool, encodingName) + : NULL); + curBase = NULL; + XmlInitEncoding(&initEncoding, &encoding, 0); + userData = NULL; + handlerArg = NULL; + startElementHandler = NULL; + endElementHandler = NULL; + characterDataHandler = NULL; + processingInstructionHandler = NULL; + commentHandler = NULL; + startCdataSectionHandler = NULL; + endCdataSectionHandler = NULL; + defaultHandler = NULL; + startDoctypeDeclHandler = NULL; + endDoctypeDeclHandler = NULL; + unparsedEntityDeclHandler = NULL; + notationDeclHandler = NULL; + startNamespaceDeclHandler = NULL; + endNamespaceDeclHandler = NULL; + notStandaloneHandler = NULL; + externalEntityRefHandler = NULL; + externalEntityRefHandlerArg = parser; + skippedEntityHandler = NULL; + elementDeclHandler = NULL; + attlistDeclHandler = NULL; + entityDeclHandler = NULL; + xmlDeclHandler = NULL; + bufferPtr = buffer; + bufferEnd = buffer; + parseEndByteIndex = 0; + parseEndPtr = NULL; + declElementType = NULL; + declAttributeId = NULL; + declEntity = NULL; + doctypeName = NULL; + doctypeSysid = NULL; + doctypePubid = NULL; + declAttributeType = NULL; + declNotationName = NULL; + declNotationPublicId = NULL; + declAttributeIsCdata = XML_FALSE; + declAttributeIsId = XML_FALSE; + memset(&position, 0, sizeof(POSITION)); + errorCode = XML_ERROR_NONE; + eventPtr = NULL; + eventEndPtr = NULL; + positionPtr = NULL; + openInternalEntities = NULL; + defaultExpandInternalEntities = XML_TRUE; + tagLevel = 0; + tagStack = NULL; + inheritedBindings = NULL; + nSpecifiedAtts = 0; + unknownEncodingMem = NULL; + unknownEncodingRelease = NULL; + unknownEncodingData = NULL; + parentParser = NULL; + ps_parsing = XML_INITIALIZED; +#ifdef XML_DTD + isParamEntity = XML_FALSE; + useForeignDTD = XML_FALSE; + paramEntityParsing = XML_PARAM_ENTITY_PARSING_NEVER; +#endif + hash_secret_salt = 0; +} + +/* moves list of bindings to freeBindingList */ +static void FASTCALL +moveToFreeBindingList(XML_Parser parser, BINDING *bindings) +{ + while (bindings) { + BINDING *b = bindings; + bindings = bindings->nextTagBinding; + b->nextTagBinding = freeBindingList; + freeBindingList = b; + } +} + +XML_Bool XMLCALL +XML_ParserReset(XML_Parser parser, const XML_Char *encodingName) +{ + TAG *tStk; + OPEN_INTERNAL_ENTITY *openEntityList; + if (parentParser) + return XML_FALSE; + /* move tagStack to freeTagList */ + tStk = tagStack; + while (tStk) { + TAG *tag = tStk; + tStk = tStk->parent; + tag->parent = freeTagList; + moveToFreeBindingList(parser, tag->bindings); + tag->bindings = NULL; + freeTagList = tag; + } + /* move openInternalEntities to freeInternalEntities */ + openEntityList = openInternalEntities; + while (openEntityList) { + OPEN_INTERNAL_ENTITY *openEntity = openEntityList; + openEntityList = openEntity->next; + openEntity->next = freeInternalEntities; + freeInternalEntities = openEntity; + } + moveToFreeBindingList(parser, inheritedBindings); + FREE(unknownEncodingMem); + if (unknownEncodingRelease) + unknownEncodingRelease(unknownEncodingData); + poolClear(&tempPool); + poolClear(&temp2Pool); + parserInit(parser, encodingName); + dtdReset(_dtd, &parser->m_mem); + return XML_TRUE; +} + +enum XML_Status XMLCALL +XML_SetEncoding(XML_Parser parser, const XML_Char *encodingName) +{ + /* Block after XML_Parse()/XML_ParseBuffer() has been called. + XXX There's no way for the caller to determine which of the + XXX possible error cases caused the XML_STATUS_ERROR return. + */ + if (ps_parsing == XML_PARSING || ps_parsing == XML_SUSPENDED) + return XML_STATUS_ERROR; + if (encodingName == NULL) + protocolEncodingName = NULL; + else { + protocolEncodingName = poolCopyString(&tempPool, encodingName); + if (!protocolEncodingName) + return XML_STATUS_ERROR; + } + return XML_STATUS_OK; +} + +XML_Parser XMLCALL +XML_ExternalEntityParserCreate(XML_Parser oldParser, + const XML_Char *context, + const XML_Char *encodingName) +{ + XML_Parser parser = oldParser; + DTD *newDtd = NULL; + DTD *oldDtd = _dtd; + XML_StartElementHandler oldStartElementHandler = startElementHandler; + XML_EndElementHandler oldEndElementHandler = endElementHandler; + XML_CharacterDataHandler oldCharacterDataHandler = characterDataHandler; + XML_ProcessingInstructionHandler oldProcessingInstructionHandler + = processingInstructionHandler; + XML_CommentHandler oldCommentHandler = commentHandler; + XML_StartCdataSectionHandler oldStartCdataSectionHandler + = startCdataSectionHandler; + XML_EndCdataSectionHandler oldEndCdataSectionHandler + = endCdataSectionHandler; + XML_DefaultHandler oldDefaultHandler = defaultHandler; + XML_UnparsedEntityDeclHandler oldUnparsedEntityDeclHandler + = unparsedEntityDeclHandler; + XML_NotationDeclHandler oldNotationDeclHandler = notationDeclHandler; + XML_StartNamespaceDeclHandler oldStartNamespaceDeclHandler + = startNamespaceDeclHandler; + XML_EndNamespaceDeclHandler oldEndNamespaceDeclHandler + = endNamespaceDeclHandler; + XML_NotStandaloneHandler oldNotStandaloneHandler = notStandaloneHandler; + XML_ExternalEntityRefHandler oldExternalEntityRefHandler + = externalEntityRefHandler; + XML_SkippedEntityHandler oldSkippedEntityHandler = skippedEntityHandler; + XML_UnknownEncodingHandler oldUnknownEncodingHandler + = unknownEncodingHandler; + XML_ElementDeclHandler oldElementDeclHandler = elementDeclHandler; + XML_AttlistDeclHandler oldAttlistDeclHandler = attlistDeclHandler; + XML_EntityDeclHandler oldEntityDeclHandler = entityDeclHandler; + XML_XmlDeclHandler oldXmlDeclHandler = xmlDeclHandler; + ELEMENT_TYPE * oldDeclElementType = declElementType; + + void *oldUserData = userData; + void *oldHandlerArg = handlerArg; + XML_Bool oldDefaultExpandInternalEntities = defaultExpandInternalEntities; + XML_Parser oldExternalEntityRefHandlerArg = externalEntityRefHandlerArg; +#ifdef XML_DTD + enum XML_ParamEntityParsing oldParamEntityParsing = paramEntityParsing; + int oldInEntityValue = prologState.inEntityValue; +#endif + XML_Bool oldns_triplets = ns_triplets; + /* Note that the new parser shares the same hash secret as the old + parser, so that dtdCopy and copyEntityTable can lookup values + from hash tables associated with either parser without us having + to worry which hash secrets each table has. + */ + unsigned long oldhash_secret_salt = hash_secret_salt; + +#ifdef XML_DTD + if (!context) + newDtd = oldDtd; +#endif /* XML_DTD */ + + /* Note that the magical uses of the pre-processor to make field + access look more like C++ require that `parser' be overwritten + here. This makes this function more painful to follow than it + would be otherwise. + */ + if (ns) { + XML_Char tmp[2]; + *tmp = namespaceSeparator; + parser = parserCreate(encodingName, &parser->m_mem, tmp, newDtd); + } + else { + parser = parserCreate(encodingName, &parser->m_mem, NULL, newDtd); + } + + if (!parser) + return NULL; + + startElementHandler = oldStartElementHandler; + endElementHandler = oldEndElementHandler; + characterDataHandler = oldCharacterDataHandler; + processingInstructionHandler = oldProcessingInstructionHandler; + commentHandler = oldCommentHandler; + startCdataSectionHandler = oldStartCdataSectionHandler; + endCdataSectionHandler = oldEndCdataSectionHandler; + defaultHandler = oldDefaultHandler; + unparsedEntityDeclHandler = oldUnparsedEntityDeclHandler; + notationDeclHandler = oldNotationDeclHandler; + startNamespaceDeclHandler = oldStartNamespaceDeclHandler; + endNamespaceDeclHandler = oldEndNamespaceDeclHandler; + notStandaloneHandler = oldNotStandaloneHandler; + externalEntityRefHandler = oldExternalEntityRefHandler; + skippedEntityHandler = oldSkippedEntityHandler; + unknownEncodingHandler = oldUnknownEncodingHandler; + elementDeclHandler = oldElementDeclHandler; + attlistDeclHandler = oldAttlistDeclHandler; + entityDeclHandler = oldEntityDeclHandler; + xmlDeclHandler = oldXmlDeclHandler; + declElementType = oldDeclElementType; + userData = oldUserData; + if (oldUserData == oldHandlerArg) + handlerArg = userData; + else + handlerArg = parser; + if (oldExternalEntityRefHandlerArg != oldParser) + externalEntityRefHandlerArg = oldExternalEntityRefHandlerArg; + defaultExpandInternalEntities = oldDefaultExpandInternalEntities; + ns_triplets = oldns_triplets; + hash_secret_salt = oldhash_secret_salt; + parentParser = oldParser; +#ifdef XML_DTD + paramEntityParsing = oldParamEntityParsing; + prologState.inEntityValue = oldInEntityValue; + if (context) { +#endif /* XML_DTD */ + if (!dtdCopy(oldParser, _dtd, oldDtd, &parser->m_mem) + || !setContext(parser, context)) { + XML_ParserFree(parser); + return NULL; + } + processor = externalEntityInitProcessor; +#ifdef XML_DTD + } + else { + /* The DTD instance referenced by _dtd is shared between the document's + root parser and external PE parsers, therefore one does not need to + call setContext. In addition, one also *must* not call setContext, + because this would overwrite existing prefix->binding pointers in + _dtd with ones that get destroyed with the external PE parser. + This would leave those prefixes with dangling pointers. + */ + isParamEntity = XML_TRUE; + XmlPrologStateInitExternalEntity(&prologState); + processor = externalParEntInitProcessor; + } +#endif /* XML_DTD */ + return parser; +} + +static void FASTCALL +destroyBindings(BINDING *bindings, XML_Parser parser) +{ + for (;;) { + BINDING *b = bindings; + if (!b) + break; + bindings = b->nextTagBinding; + FREE(b->uri); + FREE(b); + } +} + +void XMLCALL +XML_ParserFree(XML_Parser parser) +{ + TAG *tagList; + OPEN_INTERNAL_ENTITY *entityList; + if (parser == NULL) + return; + /* free tagStack and freeTagList */ + tagList = tagStack; + for (;;) { + TAG *p; + if (tagList == NULL) { + if (freeTagList == NULL) + break; + tagList = freeTagList; + freeTagList = NULL; + } + p = tagList; + tagList = tagList->parent; + FREE(p->buf); + destroyBindings(p->bindings, parser); + FREE(p); + } + /* free openInternalEntities and freeInternalEntities */ + entityList = openInternalEntities; + for (;;) { + OPEN_INTERNAL_ENTITY *openEntity; + if (entityList == NULL) { + if (freeInternalEntities == NULL) + break; + entityList = freeInternalEntities; + freeInternalEntities = NULL; + } + openEntity = entityList; + entityList = entityList->next; + FREE(openEntity); + } + + destroyBindings(freeBindingList, parser); + destroyBindings(inheritedBindings, parser); + poolDestroy(&tempPool); + poolDestroy(&temp2Pool); +#ifdef XML_DTD + /* external parameter entity parsers share the DTD structure + parser->m_dtd with the root parser, so we must not destroy it + */ + if (!isParamEntity && _dtd) +#else + if (_dtd) +#endif /* XML_DTD */ + dtdDestroy(_dtd, (XML_Bool)!parentParser, &parser->m_mem); + FREE((void *)atts); +#ifdef XML_ATTR_INFO + FREE((void *)attInfo); +#endif + FREE(groupConnector); + FREE(buffer); + FREE(dataBuf); + FREE(nsAtts); + FREE(unknownEncodingMem); + if (unknownEncodingRelease) + unknownEncodingRelease(unknownEncodingData); + FREE(parser); +} + +void XMLCALL +XML_UseParserAsHandlerArg(XML_Parser parser) +{ + handlerArg = parser; +} + +enum XML_Error XMLCALL +XML_UseForeignDTD(XML_Parser parser, XML_Bool useDTD) +{ +#ifdef XML_DTD + /* block after XML_Parse()/XML_ParseBuffer() has been called */ + if (ps_parsing == XML_PARSING || ps_parsing == XML_SUSPENDED) + return XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING; + useForeignDTD = useDTD; + return XML_ERROR_NONE; +#else + return XML_ERROR_FEATURE_REQUIRES_XML_DTD; +#endif +} + +void XMLCALL +XML_SetReturnNSTriplet(XML_Parser parser, int do_nst) +{ + /* block after XML_Parse()/XML_ParseBuffer() has been called */ + if (ps_parsing == XML_PARSING || ps_parsing == XML_SUSPENDED) + return; + ns_triplets = do_nst ? XML_TRUE : XML_FALSE; +} + +void XMLCALL +XML_SetUserData(XML_Parser parser, void *p) +{ + if (handlerArg == userData) + handlerArg = userData = p; + else + userData = p; +} + +enum XML_Status XMLCALL +XML_SetBase(XML_Parser parser, const XML_Char *p) +{ + if (p) { + p = poolCopyString(&_dtd->pool, p); + if (!p) + return XML_STATUS_ERROR; + curBase = p; + } + else + curBase = NULL; + return XML_STATUS_OK; +} + +const XML_Char * XMLCALL +XML_GetBase(XML_Parser parser) +{ + return curBase; +} + +int XMLCALL +XML_GetSpecifiedAttributeCount(XML_Parser parser) +{ + return nSpecifiedAtts; +} + +int XMLCALL +XML_GetIdAttributeIndex(XML_Parser parser) +{ + return idAttIndex; +} + +#ifdef XML_ATTR_INFO +const XML_AttrInfo * XMLCALL +XML_GetAttributeInfo(XML_Parser parser) +{ + return attInfo; +} +#endif + +void XMLCALL +XML_SetElementHandler(XML_Parser parser, + XML_StartElementHandler start, + XML_EndElementHandler end) +{ + startElementHandler = start; + endElementHandler = end; +} + +void XMLCALL +XML_SetStartElementHandler(XML_Parser parser, + XML_StartElementHandler start) { + startElementHandler = start; +} + +void XMLCALL +XML_SetEndElementHandler(XML_Parser parser, + XML_EndElementHandler end) { + endElementHandler = end; +} + +void XMLCALL +XML_SetCharacterDataHandler(XML_Parser parser, + XML_CharacterDataHandler handler) +{ + characterDataHandler = handler; +} + +void XMLCALL +XML_SetProcessingInstructionHandler(XML_Parser parser, + XML_ProcessingInstructionHandler handler) +{ + processingInstructionHandler = handler; +} + +void XMLCALL +XML_SetCommentHandler(XML_Parser parser, + XML_CommentHandler handler) +{ + commentHandler = handler; +} + +void XMLCALL +XML_SetCdataSectionHandler(XML_Parser parser, + XML_StartCdataSectionHandler start, + XML_EndCdataSectionHandler end) +{ + startCdataSectionHandler = start; + endCdataSectionHandler = end; +} + +void XMLCALL +XML_SetStartCdataSectionHandler(XML_Parser parser, + XML_StartCdataSectionHandler start) { + startCdataSectionHandler = start; +} + +void XMLCALL +XML_SetEndCdataSectionHandler(XML_Parser parser, + XML_EndCdataSectionHandler end) { + endCdataSectionHandler = end; +} + +void XMLCALL +XML_SetDefaultHandler(XML_Parser parser, + XML_DefaultHandler handler) +{ + defaultHandler = handler; + defaultExpandInternalEntities = XML_FALSE; +} + +void XMLCALL +XML_SetDefaultHandlerExpand(XML_Parser parser, + XML_DefaultHandler handler) +{ + defaultHandler = handler; + defaultExpandInternalEntities = XML_TRUE; +} + +void XMLCALL +XML_SetDoctypeDeclHandler(XML_Parser parser, + XML_StartDoctypeDeclHandler start, + XML_EndDoctypeDeclHandler end) +{ + startDoctypeDeclHandler = start; + endDoctypeDeclHandler = end; +} + +void XMLCALL +XML_SetStartDoctypeDeclHandler(XML_Parser parser, + XML_StartDoctypeDeclHandler start) { + startDoctypeDeclHandler = start; +} + +void XMLCALL +XML_SetEndDoctypeDeclHandler(XML_Parser parser, + XML_EndDoctypeDeclHandler end) { + endDoctypeDeclHandler = end; +} + +void XMLCALL +XML_SetUnparsedEntityDeclHandler(XML_Parser parser, + XML_UnparsedEntityDeclHandler handler) +{ + unparsedEntityDeclHandler = handler; +} + +void XMLCALL +XML_SetNotationDeclHandler(XML_Parser parser, + XML_NotationDeclHandler handler) +{ + notationDeclHandler = handler; +} + +void XMLCALL +XML_SetNamespaceDeclHandler(XML_Parser parser, + XML_StartNamespaceDeclHandler start, + XML_EndNamespaceDeclHandler end) +{ + startNamespaceDeclHandler = start; + endNamespaceDeclHandler = end; +} + +void XMLCALL +XML_SetStartNamespaceDeclHandler(XML_Parser parser, + XML_StartNamespaceDeclHandler start) { + startNamespaceDeclHandler = start; +} + +void XMLCALL +XML_SetEndNamespaceDeclHandler(XML_Parser parser, + XML_EndNamespaceDeclHandler end) { + endNamespaceDeclHandler = end; +} + +void XMLCALL +XML_SetNotStandaloneHandler(XML_Parser parser, + XML_NotStandaloneHandler handler) +{ + notStandaloneHandler = handler; +} + +void XMLCALL +XML_SetExternalEntityRefHandler(XML_Parser parser, + XML_ExternalEntityRefHandler handler) +{ + externalEntityRefHandler = handler; +} + +void XMLCALL +XML_SetExternalEntityRefHandlerArg(XML_Parser parser, void *arg) +{ + if (arg) + externalEntityRefHandlerArg = (XML_Parser)arg; + else + externalEntityRefHandlerArg = parser; +} + +void XMLCALL +XML_SetSkippedEntityHandler(XML_Parser parser, + XML_SkippedEntityHandler handler) +{ + skippedEntityHandler = handler; +} + +void XMLCALL +XML_SetUnknownEncodingHandler(XML_Parser parser, + XML_UnknownEncodingHandler handler, + void *data) +{ + unknownEncodingHandler = handler; + unknownEncodingHandlerData = data; +} + +void XMLCALL +XML_SetElementDeclHandler(XML_Parser parser, + XML_ElementDeclHandler eldecl) +{ + elementDeclHandler = eldecl; +} + +void XMLCALL +XML_SetAttlistDeclHandler(XML_Parser parser, + XML_AttlistDeclHandler attdecl) +{ + attlistDeclHandler = attdecl; +} + +void XMLCALL +XML_SetEntityDeclHandler(XML_Parser parser, + XML_EntityDeclHandler handler) +{ + entityDeclHandler = handler; +} + +void XMLCALL +XML_SetXmlDeclHandler(XML_Parser parser, + XML_XmlDeclHandler handler) { + xmlDeclHandler = handler; +} + +int XMLCALL +XML_SetParamEntityParsing(XML_Parser parser, + enum XML_ParamEntityParsing peParsing) +{ + /* block after XML_Parse()/XML_ParseBuffer() has been called */ + if (ps_parsing == XML_PARSING || ps_parsing == XML_SUSPENDED) + return 0; +#ifdef XML_DTD + paramEntityParsing = peParsing; + return 1; +#else + return peParsing == XML_PARAM_ENTITY_PARSING_NEVER; +#endif +} + +int XMLCALL +XML_SetHashSalt(XML_Parser parser, + unsigned long hash_salt) +{ + /* block after XML_Parse()/XML_ParseBuffer() has been called */ + if (ps_parsing == XML_PARSING || ps_parsing == XML_SUSPENDED) + return 0; + hash_secret_salt = hash_salt; + return 1; +} + +enum XML_Status XMLCALL +XML_Parse(XML_Parser parser, const char *s, int len, int isFinal) +{ + switch (ps_parsing) { + case XML_SUSPENDED: + errorCode = XML_ERROR_SUSPENDED; + return XML_STATUS_ERROR; + case XML_FINISHED: + errorCode = XML_ERROR_FINISHED; + return XML_STATUS_ERROR; + case XML_INITIALIZED: + if (parentParser == NULL && !startParsing(parser)) { + errorCode = XML_ERROR_NO_MEMORY; + return XML_STATUS_ERROR; + } + default: + ps_parsing = XML_PARSING; + } + + if (len == 0) { + ps_finalBuffer = (XML_Bool)isFinal; + if (!isFinal) + return XML_STATUS_OK; + positionPtr = bufferPtr; + parseEndPtr = bufferEnd; + + /* If data are left over from last buffer, and we now know that these + data are the final chunk of input, then we have to check them again + to detect errors based on that fact. + */ + errorCode = processor(parser, bufferPtr, parseEndPtr, &bufferPtr); + + if (errorCode == XML_ERROR_NONE) { + switch (ps_parsing) { + case XML_SUSPENDED: + XmlUpdatePosition(encoding, positionPtr, bufferPtr, &position); + positionPtr = bufferPtr; + return XML_STATUS_SUSPENDED; + case XML_INITIALIZED: + case XML_PARSING: + ps_parsing = XML_FINISHED; + /* fall through */ + default: + return XML_STATUS_OK; + } + } + eventEndPtr = eventPtr; + processor = errorProcessor; + return XML_STATUS_ERROR; + } +#ifndef XML_CONTEXT_BYTES + else if (bufferPtr == bufferEnd) { + const char *end; + int nLeftOver; + enum XML_Status result; + parseEndByteIndex += len; + positionPtr = s; + ps_finalBuffer = (XML_Bool)isFinal; + + errorCode = processor(parser, s, parseEndPtr = s + len, &end); + + if (errorCode != XML_ERROR_NONE) { + eventEndPtr = eventPtr; + processor = errorProcessor; + return XML_STATUS_ERROR; + } + else { + switch (ps_parsing) { + case XML_SUSPENDED: + result = XML_STATUS_SUSPENDED; + break; + case XML_INITIALIZED: + case XML_PARSING: + if (isFinal) { + ps_parsing = XML_FINISHED; + return XML_STATUS_OK; + } + /* fall through */ + default: + result = XML_STATUS_OK; + } + } + + XmlUpdatePosition(encoding, positionPtr, end, &position); + nLeftOver = s + len - end; + if (nLeftOver) { + if (buffer == NULL || nLeftOver > bufferLim - buffer) { + /* FIXME avoid integer overflow */ + char *temp; + temp = (buffer == NULL + ? (char *)MALLOC(len * 2) + : (char *)REALLOC(buffer, len * 2)); + if (temp == NULL) { + errorCode = XML_ERROR_NO_MEMORY; + eventPtr = eventEndPtr = NULL; + processor = errorProcessor; + return XML_STATUS_ERROR; + } + buffer = temp; + bufferLim = buffer + len * 2; + } + memcpy(buffer, end, nLeftOver); + } + bufferPtr = buffer; + bufferEnd = buffer + nLeftOver; + positionPtr = bufferPtr; + parseEndPtr = bufferEnd; + eventPtr = bufferPtr; + eventEndPtr = bufferPtr; + return result; + } +#endif /* not defined XML_CONTEXT_BYTES */ + else { + void *buff = XML_GetBuffer(parser, len); + if (buff == NULL) + return XML_STATUS_ERROR; + else { + memcpy(buff, s, len); + return XML_ParseBuffer(parser, len, isFinal); + } + } +} + +enum XML_Status XMLCALL +XML_ParseBuffer(XML_Parser parser, int len, int isFinal) +{ + const char *start; + enum XML_Status result = XML_STATUS_OK; + + switch (ps_parsing) { + case XML_SUSPENDED: + errorCode = XML_ERROR_SUSPENDED; + return XML_STATUS_ERROR; + case XML_FINISHED: + errorCode = XML_ERROR_FINISHED; + return XML_STATUS_ERROR; + case XML_INITIALIZED: + if (parentParser == NULL && !startParsing(parser)) { + errorCode = XML_ERROR_NO_MEMORY; + return XML_STATUS_ERROR; + } + default: + ps_parsing = XML_PARSING; + } + + start = bufferPtr; + positionPtr = start; + bufferEnd += len; + parseEndPtr = bufferEnd; + parseEndByteIndex += len; + ps_finalBuffer = (XML_Bool)isFinal; + + errorCode = processor(parser, start, parseEndPtr, &bufferPtr); + + if (errorCode != XML_ERROR_NONE) { + eventEndPtr = eventPtr; + processor = errorProcessor; + return XML_STATUS_ERROR; + } + else { + switch (ps_parsing) { + case XML_SUSPENDED: + result = XML_STATUS_SUSPENDED; + break; + case XML_INITIALIZED: + case XML_PARSING: + if (isFinal) { + ps_parsing = XML_FINISHED; + return result; + } + default: ; /* should not happen */ + } + } + + XmlUpdatePosition(encoding, positionPtr, bufferPtr, &position); + positionPtr = bufferPtr; + return result; +} + +void * XMLCALL +XML_GetBuffer(XML_Parser parser, int len) +{ + if (len < 0) { + errorCode = XML_ERROR_NO_MEMORY; + return NULL; + } + switch (ps_parsing) { + case XML_SUSPENDED: + errorCode = XML_ERROR_SUSPENDED; + return NULL; + case XML_FINISHED: + errorCode = XML_ERROR_FINISHED; + return NULL; + default: ; + } + + if (len > bufferLim - bufferEnd) { +#ifdef XML_CONTEXT_BYTES + int keep; +#endif /* defined XML_CONTEXT_BYTES */ + /* Do not invoke signed arithmetic overflow: */ + int neededSize = (int) ((unsigned)len + (unsigned)(bufferEnd - bufferPtr)); + if (neededSize < 0) { + errorCode = XML_ERROR_NO_MEMORY; + return NULL; + } +#ifdef XML_CONTEXT_BYTES + keep = (int)(bufferPtr - buffer); + if (keep > XML_CONTEXT_BYTES) + keep = XML_CONTEXT_BYTES; + neededSize += keep; +#endif /* defined XML_CONTEXT_BYTES */ + if (neededSize <= bufferLim - buffer) { +#ifdef XML_CONTEXT_BYTES + if (keep < bufferPtr - buffer) { + int offset = (int)(bufferPtr - buffer) - keep; + memmove(buffer, &buffer[offset], bufferEnd - bufferPtr + keep); + bufferEnd -= offset; + bufferPtr -= offset; + } +#else + memmove(buffer, bufferPtr, bufferEnd - bufferPtr); + bufferEnd = buffer + (bufferEnd - bufferPtr); + bufferPtr = buffer; +#endif /* not defined XML_CONTEXT_BYTES */ + } + else { + char *newBuf; + int bufferSize = (int)(bufferLim - bufferPtr); + if (bufferSize == 0) + bufferSize = INIT_BUFFER_SIZE; + do { + /* Do not invoke signed arithmetic overflow: */ + bufferSize = (int) (2U * (unsigned) bufferSize); + } while (bufferSize < neededSize && bufferSize > 0); + if (bufferSize <= 0) { + errorCode = XML_ERROR_NO_MEMORY; + return NULL; + } + newBuf = (char *)MALLOC(bufferSize); + if (newBuf == 0) { + errorCode = XML_ERROR_NO_MEMORY; + return NULL; + } + bufferLim = newBuf + bufferSize; +#ifdef XML_CONTEXT_BYTES + if (bufferPtr) { + int keep = (int)(bufferPtr - buffer); + if (keep > XML_CONTEXT_BYTES) + keep = XML_CONTEXT_BYTES; + memcpy(newBuf, &bufferPtr[-keep], bufferEnd - bufferPtr + keep); + FREE(buffer); + buffer = newBuf; + bufferEnd = buffer + (bufferEnd - bufferPtr) + keep; + bufferPtr = buffer + keep; + } + else { + bufferEnd = newBuf + (bufferEnd - bufferPtr); + bufferPtr = buffer = newBuf; + } +#else + if (bufferPtr) { + memcpy(newBuf, bufferPtr, bufferEnd - bufferPtr); + FREE(buffer); + } + bufferEnd = newBuf + (bufferEnd - bufferPtr); + bufferPtr = buffer = newBuf; +#endif /* not defined XML_CONTEXT_BYTES */ + } + eventPtr = eventEndPtr = NULL; + positionPtr = NULL; + } + return bufferEnd; +} + +enum XML_Status XMLCALL +XML_StopParser(XML_Parser parser, XML_Bool resumable) +{ + switch (ps_parsing) { + case XML_SUSPENDED: + if (resumable) { + errorCode = XML_ERROR_SUSPENDED; + return XML_STATUS_ERROR; + } + ps_parsing = XML_FINISHED; + break; + case XML_FINISHED: + errorCode = XML_ERROR_FINISHED; + return XML_STATUS_ERROR; + default: + if (resumable) { +#ifdef XML_DTD + if (isParamEntity) { + errorCode = XML_ERROR_SUSPEND_PE; + return XML_STATUS_ERROR; + } +#endif + ps_parsing = XML_SUSPENDED; + } + else + ps_parsing = XML_FINISHED; + } + return XML_STATUS_OK; +} + +enum XML_Status XMLCALL +XML_ResumeParser(XML_Parser parser) +{ + enum XML_Status result = XML_STATUS_OK; + + if (ps_parsing != XML_SUSPENDED) { + errorCode = XML_ERROR_NOT_SUSPENDED; + return XML_STATUS_ERROR; + } + ps_parsing = XML_PARSING; + + errorCode = processor(parser, bufferPtr, parseEndPtr, &bufferPtr); + + if (errorCode != XML_ERROR_NONE) { + eventEndPtr = eventPtr; + processor = errorProcessor; + return XML_STATUS_ERROR; + } + else { + switch (ps_parsing) { + case XML_SUSPENDED: + result = XML_STATUS_SUSPENDED; + break; + case XML_INITIALIZED: + case XML_PARSING: + if (ps_finalBuffer) { + ps_parsing = XML_FINISHED; + return result; + } + default: ; + } + } + + XmlUpdatePosition(encoding, positionPtr, bufferPtr, &position); + positionPtr = bufferPtr; + return result; +} + +void XMLCALL +XML_GetParsingStatus(XML_Parser parser, XML_ParsingStatus *status) +{ + assert(status != NULL); + *status = parser->m_parsingStatus; +} + +enum XML_Error XMLCALL +XML_GetErrorCode(XML_Parser parser) +{ + return errorCode; +} + +XML_Index XMLCALL +XML_GetCurrentByteIndex(XML_Parser parser) +{ + if (eventPtr) + return (XML_Index)(parseEndByteIndex - (parseEndPtr - eventPtr)); + return -1; +} + +int XMLCALL +XML_GetCurrentByteCount(XML_Parser parser) +{ + if (eventEndPtr && eventPtr) + return (int)(eventEndPtr - eventPtr); + return 0; +} + +const char * XMLCALL +XML_GetInputContext(XML_Parser parser, int *offset, int *size) +{ +#ifdef XML_CONTEXT_BYTES + if (eventPtr && buffer) { + *offset = (int)(eventPtr - buffer); + *size = (int)(bufferEnd - buffer); + return buffer; + } +#endif /* defined XML_CONTEXT_BYTES */ + return (char *) 0; +} + +XML_Size XMLCALL +XML_GetCurrentLineNumber(XML_Parser parser) +{ + if (eventPtr && eventPtr >= positionPtr) { + XmlUpdatePosition(encoding, positionPtr, eventPtr, &position); + positionPtr = eventPtr; + } + return position.lineNumber + 1; +} + +XML_Size XMLCALL +XML_GetCurrentColumnNumber(XML_Parser parser) +{ + if (eventPtr && eventPtr >= positionPtr) { + XmlUpdatePosition(encoding, positionPtr, eventPtr, &position); + positionPtr = eventPtr; + } + return position.columnNumber; +} + +void XMLCALL +XML_FreeContentModel(XML_Parser parser, XML_Content *model) +{ + FREE(model); +} + +void * XMLCALL +XML_MemMalloc(XML_Parser parser, size_t size) +{ + return MALLOC(size); +} + +void * XMLCALL +XML_MemRealloc(XML_Parser parser, void *ptr, size_t size) +{ + return REALLOC(ptr, size); +} + +void XMLCALL +XML_MemFree(XML_Parser parser, void *ptr) +{ + FREE(ptr); +} + +void XMLCALL +XML_DefaultCurrent(XML_Parser parser) +{ + if (defaultHandler) { + if (openInternalEntities) + reportDefault(parser, + internalEncoding, + openInternalEntities->internalEventPtr, + openInternalEntities->internalEventEndPtr); + else + reportDefault(parser, encoding, eventPtr, eventEndPtr); + } +} + +const XML_LChar * XMLCALL +XML_ErrorString(enum XML_Error code) +{ + static const XML_LChar* const message[] = { + 0, + XML_L("out of memory"), + XML_L("syntax error"), + XML_L("no element found"), + XML_L("not well-formed (invalid token)"), + XML_L("unclosed token"), + XML_L("partial character"), + XML_L("mismatched tag"), + XML_L("duplicate attribute"), + XML_L("junk after document element"), + XML_L("illegal parameter entity reference"), + XML_L("undefined entity"), + XML_L("recursive entity reference"), + XML_L("asynchronous entity"), + XML_L("reference to invalid character number"), + XML_L("reference to binary entity"), + XML_L("reference to external entity in attribute"), + XML_L("XML or text declaration not at start of entity"), + XML_L("unknown encoding"), + XML_L("encoding specified in XML declaration is incorrect"), + XML_L("unclosed CDATA section"), + XML_L("error in processing external entity reference"), + XML_L("document is not standalone"), + XML_L("unexpected parser state - please send a bug report"), + XML_L("entity declared in parameter entity"), + XML_L("requested feature requires XML_DTD support in Expat"), + XML_L("cannot change setting once parsing has begun"), + XML_L("unbound prefix"), + XML_L("must not undeclare prefix"), + XML_L("incomplete markup in parameter entity"), + XML_L("XML declaration not well-formed"), + XML_L("text declaration not well-formed"), + XML_L("illegal character(s) in public id"), + XML_L("parser suspended"), + XML_L("parser not suspended"), + XML_L("parsing aborted"), + XML_L("parsing finished"), + XML_L("cannot suspend in external parameter entity"), + XML_L("reserved prefix (xml) must not be undeclared or bound to another namespace name"), + XML_L("reserved prefix (xmlns) must not be declared or undeclared"), + XML_L("prefix must not be bound to one of the reserved namespace names") + }; + if (code > 0 && code < sizeof(message)/sizeof(message[0])) + return message[code]; + return NULL; +} + +const XML_LChar * XMLCALL +XML_ExpatVersion(void) { + + /* V1 is used to string-ize the version number. However, it would + string-ize the actual version macro *names* unless we get them + substituted before being passed to V1. CPP is defined to expand + a macro, then rescan for more expansions. Thus, we use V2 to expand + the version macros, then CPP will expand the resulting V1() macro + with the correct numerals. */ + /* ### I'm assuming cpp is portable in this respect... */ + +#define V1(a,b,c) XML_L(#a)XML_L(".")XML_L(#b)XML_L(".")XML_L(#c) +#define V2(a,b,c) XML_L("expat_")V1(a,b,c) + + return V2(XML_MAJOR_VERSION, XML_MINOR_VERSION, XML_MICRO_VERSION); + +#undef V1 +#undef V2 +} + +XML_Expat_Version XMLCALL +XML_ExpatVersionInfo(void) +{ + XML_Expat_Version version; + + version.major = XML_MAJOR_VERSION; + version.minor = XML_MINOR_VERSION; + version.micro = XML_MICRO_VERSION; + + return version; +} + +const XML_Feature * XMLCALL +XML_GetFeatureList(void) +{ + static const XML_Feature features[] = { + {XML_FEATURE_SIZEOF_XML_CHAR, XML_L("sizeof(XML_Char)"), + sizeof(XML_Char)}, + {XML_FEATURE_SIZEOF_XML_LCHAR, XML_L("sizeof(XML_LChar)"), + sizeof(XML_LChar)}, +#ifdef XML_UNICODE + {XML_FEATURE_UNICODE, XML_L("XML_UNICODE"), 0}, +#endif +#ifdef XML_UNICODE_WCHAR_T + {XML_FEATURE_UNICODE_WCHAR_T, XML_L("XML_UNICODE_WCHAR_T"), 0}, +#endif +#ifdef XML_DTD + {XML_FEATURE_DTD, XML_L("XML_DTD"), 0}, +#endif +#ifdef XML_CONTEXT_BYTES + {XML_FEATURE_CONTEXT_BYTES, XML_L("XML_CONTEXT_BYTES"), + XML_CONTEXT_BYTES}, +#endif +#ifdef XML_MIN_SIZE + {XML_FEATURE_MIN_SIZE, XML_L("XML_MIN_SIZE"), 0}, +#endif +#ifdef XML_NS + {XML_FEATURE_NS, XML_L("XML_NS"), 0}, +#endif +#ifdef XML_LARGE_SIZE + {XML_FEATURE_LARGE_SIZE, XML_L("XML_LARGE_SIZE"), 0}, +#endif +#ifdef XML_ATTR_INFO + {XML_FEATURE_ATTR_INFO, XML_L("XML_ATTR_INFO"), 0}, +#endif + {XML_FEATURE_END, NULL, 0} + }; + + return features; +} + +/* Initially tag->rawName always points into the parse buffer; + for those TAG instances opened while the current parse buffer was + processed, and not yet closed, we need to store tag->rawName in a more + permanent location, since the parse buffer is about to be discarded. +*/ +static XML_Bool +storeRawNames(XML_Parser parser) +{ + TAG *tag = tagStack; + while (tag) { + int bufSize; + int nameLen = sizeof(XML_Char) * (tag->name.strLen + 1); + char *rawNameBuf = tag->buf + nameLen; + /* Stop if already stored. Since tagStack is a stack, we can stop + at the first entry that has already been copied; everything + below it in the stack is already been accounted for in a + previous call to this function. + */ + if (tag->rawName == rawNameBuf) + break; + /* For re-use purposes we need to ensure that the + size of tag->buf is a multiple of sizeof(XML_Char). + */ + bufSize = nameLen + ROUND_UP(tag->rawNameLength, sizeof(XML_Char)); + if (bufSize > tag->bufEnd - tag->buf) { + char *temp = (char *)REALLOC(tag->buf, bufSize); + if (temp == NULL) + return XML_FALSE; + /* if tag->name.str points to tag->buf (only when namespace + processing is off) then we have to update it + */ + if (tag->name.str == (XML_Char *)tag->buf) + tag->name.str = (XML_Char *)temp; + /* if tag->name.localPart is set (when namespace processing is on) + then update it as well, since it will always point into tag->buf + */ + if (tag->name.localPart) + tag->name.localPart = (XML_Char *)temp + (tag->name.localPart - + (XML_Char *)tag->buf); + tag->buf = temp; + tag->bufEnd = temp + bufSize; + rawNameBuf = temp + nameLen; + } + memcpy(rawNameBuf, tag->rawName, tag->rawNameLength); + tag->rawName = rawNameBuf; + tag = tag->parent; + } + return XML_TRUE; +} + +static enum XML_Error PTRCALL +contentProcessor(XML_Parser parser, + const char *start, + const char *end, + const char **endPtr) +{ + enum XML_Error result = doContent(parser, 0, encoding, start, end, + endPtr, (XML_Bool)!ps_finalBuffer); + if (result == XML_ERROR_NONE) { + if (!storeRawNames(parser)) + return XML_ERROR_NO_MEMORY; + } + return result; +} + +static enum XML_Error PTRCALL +externalEntityInitProcessor(XML_Parser parser, + const char *start, + const char *end, + const char **endPtr) +{ + enum XML_Error result = initializeEncoding(parser); + if (result != XML_ERROR_NONE) + return result; + processor = externalEntityInitProcessor2; + return externalEntityInitProcessor2(parser, start, end, endPtr); +} + +static enum XML_Error PTRCALL +externalEntityInitProcessor2(XML_Parser parser, + const char *start, + const char *end, + const char **endPtr) +{ + const char *next = start; /* XmlContentTok doesn't always set the last arg */ + int tok = XmlContentTok(encoding, start, end, &next); + switch (tok) { + case XML_TOK_BOM: + /* If we are at the end of the buffer, this would cause the next stage, + i.e. externalEntityInitProcessor3, to pass control directly to + doContent (by detecting XML_TOK_NONE) without processing any xml text + declaration - causing the error XML_ERROR_MISPLACED_XML_PI in doContent. + */ + if (next == end && !ps_finalBuffer) { + *endPtr = next; + return XML_ERROR_NONE; + } + start = next; + break; + case XML_TOK_PARTIAL: + if (!ps_finalBuffer) { + *endPtr = start; + return XML_ERROR_NONE; + } + eventPtr = start; + return XML_ERROR_UNCLOSED_TOKEN; + case XML_TOK_PARTIAL_CHAR: + if (!ps_finalBuffer) { + *endPtr = start; + return XML_ERROR_NONE; + } + eventPtr = start; + return XML_ERROR_PARTIAL_CHAR; + } + processor = externalEntityInitProcessor3; + return externalEntityInitProcessor3(parser, start, end, endPtr); +} + +static enum XML_Error PTRCALL +externalEntityInitProcessor3(XML_Parser parser, + const char *start, + const char *end, + const char **endPtr) +{ + int tok; + const char *next = start; /* XmlContentTok doesn't always set the last arg */ + eventPtr = start; + tok = XmlContentTok(encoding, start, end, &next); + eventEndPtr = next; + + switch (tok) { + case XML_TOK_XML_DECL: + { + enum XML_Error result; + result = processXmlDecl(parser, 1, start, next); + if (result != XML_ERROR_NONE) + return result; + switch (ps_parsing) { + case XML_SUSPENDED: + *endPtr = next; + return XML_ERROR_NONE; + case XML_FINISHED: + return XML_ERROR_ABORTED; + default: + start = next; + } + } + break; + case XML_TOK_PARTIAL: + if (!ps_finalBuffer) { + *endPtr = start; + return XML_ERROR_NONE; + } + return XML_ERROR_UNCLOSED_TOKEN; + case XML_TOK_PARTIAL_CHAR: + if (!ps_finalBuffer) { + *endPtr = start; + return XML_ERROR_NONE; + } + return XML_ERROR_PARTIAL_CHAR; + } + processor = externalEntityContentProcessor; + tagLevel = 1; + return externalEntityContentProcessor(parser, start, end, endPtr); +} + +static enum XML_Error PTRCALL +externalEntityContentProcessor(XML_Parser parser, + const char *start, + const char *end, + const char **endPtr) +{ + enum XML_Error result = doContent(parser, 1, encoding, start, end, + endPtr, (XML_Bool)!ps_finalBuffer); + if (result == XML_ERROR_NONE) { + if (!storeRawNames(parser)) + return XML_ERROR_NO_MEMORY; + } + return result; +} + +static enum XML_Error +doContent(XML_Parser parser, + int startTagLevel, + const ENCODING *enc, + const char *s, + const char *end, + const char **nextPtr, + XML_Bool haveMore) +{ + /* save one level of indirection */ + DTD * const dtd = _dtd; + + const char **eventPP; + const char **eventEndPP; + if (enc == encoding) { + eventPP = &eventPtr; + eventEndPP = &eventEndPtr; + } + else { + eventPP = &(openInternalEntities->internalEventPtr); + eventEndPP = &(openInternalEntities->internalEventEndPtr); + } + *eventPP = s; + + for (;;) { + const char *next = s; /* XmlContentTok doesn't always set the last arg */ + int tok = XmlContentTok(enc, s, end, &next); + *eventEndPP = next; + switch (tok) { + case XML_TOK_TRAILING_CR: + if (haveMore) { + *nextPtr = s; + return XML_ERROR_NONE; + } + *eventEndPP = end; + if (characterDataHandler) { + XML_Char c = 0xA; + characterDataHandler(handlerArg, &c, 1); + } + else if (defaultHandler) + reportDefault(parser, enc, s, end); + /* We are at the end of the final buffer, should we check for + XML_SUSPENDED, XML_FINISHED? + */ + if (startTagLevel == 0) + return XML_ERROR_NO_ELEMENTS; + if (tagLevel != startTagLevel) + return XML_ERROR_ASYNC_ENTITY; + *nextPtr = end; + return XML_ERROR_NONE; + case XML_TOK_NONE: + if (haveMore) { + *nextPtr = s; + return XML_ERROR_NONE; + } + if (startTagLevel > 0) { + if (tagLevel != startTagLevel) + return XML_ERROR_ASYNC_ENTITY; + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_NO_ELEMENTS; + case XML_TOK_INVALID: + *eventPP = next; + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL: + if (haveMore) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_UNCLOSED_TOKEN; + case XML_TOK_PARTIAL_CHAR: + if (haveMore) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_PARTIAL_CHAR; + case XML_TOK_ENTITY_REF: + { + const XML_Char *name; + ENTITY *entity; + XML_Char ch = (XML_Char) XmlPredefinedEntityName(enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (ch) { + if (characterDataHandler) + characterDataHandler(handlerArg, &ch, 1); + else if (defaultHandler) + reportDefault(parser, enc, s, next); + break; + } + name = poolStoreString(&dtd->pool, enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!name) + return XML_ERROR_NO_MEMORY; + entity = (ENTITY *)lookup(parser, &dtd->generalEntities, name, 0); + poolDiscard(&dtd->pool); + /* First, determine if a check for an existing declaration is needed; + if yes, check that the entity exists, and that it is internal, + otherwise call the skipped entity or default handler. + */ + if (!dtd->hasParamEntityRefs || dtd->standalone) { + if (!entity) + return XML_ERROR_UNDEFINED_ENTITY; + else if (!entity->is_internal) + return XML_ERROR_ENTITY_DECLARED_IN_PE; + } + else if (!entity) { + if (skippedEntityHandler) + skippedEntityHandler(handlerArg, name, 0); + else if (defaultHandler) + reportDefault(parser, enc, s, next); + break; + } + if (entity->open) + return XML_ERROR_RECURSIVE_ENTITY_REF; + if (entity->notation) + return XML_ERROR_BINARY_ENTITY_REF; + if (entity->textPtr) { + enum XML_Error result; + if (!defaultExpandInternalEntities) { + if (skippedEntityHandler) + skippedEntityHandler(handlerArg, entity->name, 0); + else if (defaultHandler) + reportDefault(parser, enc, s, next); + break; + } + result = processInternalEntity(parser, entity, XML_FALSE); + if (result != XML_ERROR_NONE) + return result; + } + else if (externalEntityRefHandler) { + const XML_Char *context; + entity->open = XML_TRUE; + context = getContext(parser); + entity->open = XML_FALSE; + if (!context) + return XML_ERROR_NO_MEMORY; + if (!externalEntityRefHandler(externalEntityRefHandlerArg, + context, + entity->base, + entity->systemId, + entity->publicId)) + return XML_ERROR_EXTERNAL_ENTITY_HANDLING; + poolDiscard(&tempPool); + } + else if (defaultHandler) + reportDefault(parser, enc, s, next); + break; + } + case XML_TOK_START_TAG_NO_ATTS: + /* fall through */ + case XML_TOK_START_TAG_WITH_ATTS: + { + TAG *tag; + enum XML_Error result; + XML_Char *toPtr; + if (freeTagList) { + tag = freeTagList; + freeTagList = freeTagList->parent; + } + else { + tag = (TAG *)MALLOC(sizeof(TAG)); + if (!tag) + return XML_ERROR_NO_MEMORY; + tag->buf = (char *)MALLOC(INIT_TAG_BUF_SIZE); + if (!tag->buf) { + FREE(tag); + return XML_ERROR_NO_MEMORY; + } + tag->bufEnd = tag->buf + INIT_TAG_BUF_SIZE; + } + tag->bindings = NULL; + tag->parent = tagStack; + tagStack = tag; + tag->name.localPart = NULL; + tag->name.prefix = NULL; + tag->rawName = s + enc->minBytesPerChar; + tag->rawNameLength = XmlNameLength(enc, tag->rawName); + ++tagLevel; + { + const char *rawNameEnd = tag->rawName + tag->rawNameLength; + const char *fromPtr = tag->rawName; + toPtr = (XML_Char *)tag->buf; + for (;;) { + int bufSize; + int convLen; + const enum XML_Convert_Result convert_res = XmlConvert(enc, + &fromPtr, rawNameEnd, + (ICHAR **)&toPtr, (ICHAR *)tag->bufEnd - 1); + convLen = (int)(toPtr - (XML_Char *)tag->buf); + if ((convert_res == XML_CONVERT_COMPLETED) || (convert_res == XML_CONVERT_INPUT_INCOMPLETE)) { + tag->name.strLen = convLen; + break; + } + bufSize = (int)(tag->bufEnd - tag->buf) << 1; + { + char *temp = (char *)REALLOC(tag->buf, bufSize); + if (temp == NULL) + return XML_ERROR_NO_MEMORY; + tag->buf = temp; + tag->bufEnd = temp + bufSize; + toPtr = (XML_Char *)temp + convLen; + } + } + } + tag->name.str = (XML_Char *)tag->buf; + *toPtr = XML_T('\0'); + result = storeAtts(parser, enc, s, &(tag->name), &(tag->bindings)); + if (result) + return result; + if (startElementHandler) + startElementHandler(handlerArg, tag->name.str, + (const XML_Char **)atts); + else if (defaultHandler) + reportDefault(parser, enc, s, next); + poolClear(&tempPool); + break; + } + case XML_TOK_EMPTY_ELEMENT_NO_ATTS: + /* fall through */ + case XML_TOK_EMPTY_ELEMENT_WITH_ATTS: + { + const char *rawName = s + enc->minBytesPerChar; + enum XML_Error result; + BINDING *bindings = NULL; + XML_Bool noElmHandlers = XML_TRUE; + TAG_NAME name; + name.str = poolStoreString(&tempPool, enc, rawName, + rawName + XmlNameLength(enc, rawName)); + if (!name.str) + return XML_ERROR_NO_MEMORY; + poolFinish(&tempPool); + result = storeAtts(parser, enc, s, &name, &bindings); + if (result) + return result; + poolFinish(&tempPool); + if (startElementHandler) { + startElementHandler(handlerArg, name.str, (const XML_Char **)atts); + noElmHandlers = XML_FALSE; + } + if (endElementHandler) { + if (startElementHandler) + *eventPP = *eventEndPP; + endElementHandler(handlerArg, name.str); + noElmHandlers = XML_FALSE; + } + if (noElmHandlers && defaultHandler) + reportDefault(parser, enc, s, next); + poolClear(&tempPool); + while (bindings) { + BINDING *b = bindings; + if (endNamespaceDeclHandler) + endNamespaceDeclHandler(handlerArg, b->prefix->name); + bindings = bindings->nextTagBinding; + b->nextTagBinding = freeBindingList; + freeBindingList = b; + b->prefix->binding = b->prevPrefixBinding; + } + } + if (tagLevel == 0) + return epilogProcessor(parser, next, end, nextPtr); + break; + case XML_TOK_END_TAG: + if (tagLevel == startTagLevel) + return XML_ERROR_ASYNC_ENTITY; + else { + int len; + const char *rawName; + TAG *tag = tagStack; + tagStack = tag->parent; + tag->parent = freeTagList; + freeTagList = tag; + rawName = s + enc->minBytesPerChar*2; + len = XmlNameLength(enc, rawName); + if (len != tag->rawNameLength + || memcmp(tag->rawName, rawName, len) != 0) { + *eventPP = rawName; + return XML_ERROR_TAG_MISMATCH; + } + --tagLevel; + if (endElementHandler) { + const XML_Char *localPart; + const XML_Char *prefix; + XML_Char *uri; + localPart = tag->name.localPart; + if (ns && localPart) { + /* localPart and prefix may have been overwritten in + tag->name.str, since this points to the binding->uri + buffer which gets re-used; so we have to add them again + */ + uri = (XML_Char *)tag->name.str + tag->name.uriLen; + /* don't need to check for space - already done in storeAtts() */ + while (*localPart) *uri++ = *localPart++; + prefix = (XML_Char *)tag->name.prefix; + if (ns_triplets && prefix) { + *uri++ = namespaceSeparator; + while (*prefix) *uri++ = *prefix++; + } + *uri = XML_T('\0'); + } + endElementHandler(handlerArg, tag->name.str); + } + else if (defaultHandler) + reportDefault(parser, enc, s, next); + while (tag->bindings) { + BINDING *b = tag->bindings; + if (endNamespaceDeclHandler) + endNamespaceDeclHandler(handlerArg, b->prefix->name); + tag->bindings = tag->bindings->nextTagBinding; + b->nextTagBinding = freeBindingList; + freeBindingList = b; + b->prefix->binding = b->prevPrefixBinding; + } + if (tagLevel == 0) + return epilogProcessor(parser, next, end, nextPtr); + } + break; + case XML_TOK_CHAR_REF: + { + int n = XmlCharRefNumber(enc, s); + if (n < 0) + return XML_ERROR_BAD_CHAR_REF; + if (characterDataHandler) { + XML_Char buf[XML_ENCODE_MAX]; + characterDataHandler(handlerArg, buf, XmlEncode(n, (ICHAR *)buf)); + } + else if (defaultHandler) + reportDefault(parser, enc, s, next); + } + break; + case XML_TOK_XML_DECL: + return XML_ERROR_MISPLACED_XML_PI; + case XML_TOK_DATA_NEWLINE: + if (characterDataHandler) { + XML_Char c = 0xA; + characterDataHandler(handlerArg, &c, 1); + } + else if (defaultHandler) + reportDefault(parser, enc, s, next); + break; + case XML_TOK_CDATA_SECT_OPEN: + { + enum XML_Error result; + if (startCdataSectionHandler) + startCdataSectionHandler(handlerArg); +#if 0 + /* Suppose you doing a transformation on a document that involves + changing only the character data. You set up a defaultHandler + and a characterDataHandler. The defaultHandler simply copies + characters through. The characterDataHandler does the + transformation and writes the characters out escaping them as + necessary. This case will fail to work if we leave out the + following two lines (because & and < inside CDATA sections will + be incorrectly escaped). + + However, now we have a start/endCdataSectionHandler, so it seems + easier to let the user deal with this. + */ + else if (characterDataHandler) + characterDataHandler(handlerArg, dataBuf, 0); +#endif + else if (defaultHandler) + reportDefault(parser, enc, s, next); + result = doCdataSection(parser, enc, &next, end, nextPtr, haveMore); + if (result != XML_ERROR_NONE) + return result; + else if (!next) { + processor = cdataSectionProcessor; + return result; + } + } + break; + case XML_TOK_TRAILING_RSQB: + if (haveMore) { + *nextPtr = s; + return XML_ERROR_NONE; + } + if (characterDataHandler) { + if (MUST_CONVERT(enc, s)) { + ICHAR *dataPtr = (ICHAR *)dataBuf; + XmlConvert(enc, &s, end, &dataPtr, (ICHAR *)dataBufEnd); + characterDataHandler(handlerArg, dataBuf, + (int)(dataPtr - (ICHAR *)dataBuf)); + } + else + characterDataHandler(handlerArg, + (XML_Char *)s, + (int)((XML_Char *)end - (XML_Char *)s)); + } + else if (defaultHandler) + reportDefault(parser, enc, s, end); + /* We are at the end of the final buffer, should we check for + XML_SUSPENDED, XML_FINISHED? + */ + if (startTagLevel == 0) { + *eventPP = end; + return XML_ERROR_NO_ELEMENTS; + } + if (tagLevel != startTagLevel) { + *eventPP = end; + return XML_ERROR_ASYNC_ENTITY; + } + *nextPtr = end; + return XML_ERROR_NONE; + case XML_TOK_DATA_CHARS: + { + XML_CharacterDataHandler charDataHandler = characterDataHandler; + if (charDataHandler) { + if (MUST_CONVERT(enc, s)) { + for (;;) { + ICHAR *dataPtr = (ICHAR *)dataBuf; + const enum XML_Convert_Result convert_res = XmlConvert(enc, &s, next, &dataPtr, (ICHAR *)dataBufEnd); + *eventEndPP = s; + charDataHandler(handlerArg, dataBuf, + (int)(dataPtr - (ICHAR *)dataBuf)); + if ((convert_res == XML_CONVERT_COMPLETED) || (convert_res == XML_CONVERT_INPUT_INCOMPLETE)) + break; + *eventPP = s; + } + } + else + charDataHandler(handlerArg, + (XML_Char *)s, + (int)((XML_Char *)next - (XML_Char *)s)); + } + else if (defaultHandler) + reportDefault(parser, enc, s, next); + } + break; + case XML_TOK_PI: + if (!reportProcessingInstruction(parser, enc, s, next)) + return XML_ERROR_NO_MEMORY; + break; + case XML_TOK_COMMENT: + if (!reportComment(parser, enc, s, next)) + return XML_ERROR_NO_MEMORY; + break; + default: + if (defaultHandler) + reportDefault(parser, enc, s, next); + break; + } + *eventPP = s = next; + switch (ps_parsing) { + case XML_SUSPENDED: + *nextPtr = next; + return XML_ERROR_NONE; + case XML_FINISHED: + return XML_ERROR_ABORTED; + default: ; + } + } + /* not reached */ +} + +/* Precondition: all arguments must be non-NULL; + Purpose: + - normalize attributes + - check attributes for well-formedness + - generate namespace aware attribute names (URI, prefix) + - build list of attributes for startElementHandler + - default attributes + - process namespace declarations (check and report them) + - generate namespace aware element name (URI, prefix) +*/ +static enum XML_Error +storeAtts(XML_Parser parser, const ENCODING *enc, + const char *attStr, TAG_NAME *tagNamePtr, + BINDING **bindingsPtr) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + ELEMENT_TYPE *elementType; + int nDefaultAtts; + const XML_Char **appAtts; /* the attribute list for the application */ + int attIndex = 0; + int prefixLen; + int i; + int n; + XML_Char *uri; + int nPrefixes = 0; + BINDING *binding; + const XML_Char *localPart; + + /* lookup the element type name */ + elementType = (ELEMENT_TYPE *)lookup(parser, &dtd->elementTypes, tagNamePtr->str,0); + if (!elementType) { + const XML_Char *name = poolCopyString(&dtd->pool, tagNamePtr->str); + if (!name) + return XML_ERROR_NO_MEMORY; + elementType = (ELEMENT_TYPE *)lookup(parser, &dtd->elementTypes, name, + sizeof(ELEMENT_TYPE)); + if (!elementType) + return XML_ERROR_NO_MEMORY; + if (ns && !setElementTypePrefix(parser, elementType)) + return XML_ERROR_NO_MEMORY; + } + nDefaultAtts = elementType->nDefaultAtts; + + /* get the attributes from the tokenizer */ + n = XmlGetAttributes(enc, attStr, attsSize, atts); + if (n + nDefaultAtts > attsSize) { + int oldAttsSize = attsSize; + ATTRIBUTE *temp; +#ifdef XML_ATTR_INFO + XML_AttrInfo *temp2; +#endif + attsSize = n + nDefaultAtts + INIT_ATTS_SIZE; + temp = (ATTRIBUTE *)REALLOC((void *)atts, attsSize * sizeof(ATTRIBUTE)); + if (temp == NULL) + return XML_ERROR_NO_MEMORY; + atts = temp; +#ifdef XML_ATTR_INFO + temp2 = (XML_AttrInfo *)REALLOC((void *)attInfo, attsSize * sizeof(XML_AttrInfo)); + if (temp2 == NULL) + return XML_ERROR_NO_MEMORY; + attInfo = temp2; +#endif + if (n > oldAttsSize) + XmlGetAttributes(enc, attStr, n, atts); + } + + appAtts = (const XML_Char **)atts; + for (i = 0; i < n; i++) { + ATTRIBUTE *currAtt = &atts[i]; +#ifdef XML_ATTR_INFO + XML_AttrInfo *currAttInfo = &attInfo[i]; +#endif + /* add the name and value to the attribute list */ + ATTRIBUTE_ID *attId = getAttributeId(parser, enc, currAtt->name, + currAtt->name + + XmlNameLength(enc, currAtt->name)); + if (!attId) + return XML_ERROR_NO_MEMORY; +#ifdef XML_ATTR_INFO + currAttInfo->nameStart = parseEndByteIndex - (parseEndPtr - currAtt->name); + currAttInfo->nameEnd = currAttInfo->nameStart + + XmlNameLength(enc, currAtt->name); + currAttInfo->valueStart = parseEndByteIndex - + (parseEndPtr - currAtt->valuePtr); + currAttInfo->valueEnd = parseEndByteIndex - (parseEndPtr - currAtt->valueEnd); +#endif + /* Detect duplicate attributes by their QNames. This does not work when + namespace processing is turned on and different prefixes for the same + namespace are used. For this case we have a check further down. + */ + if ((attId->name)[-1]) { + if (enc == encoding) + eventPtr = atts[i].name; + return XML_ERROR_DUPLICATE_ATTRIBUTE; + } + (attId->name)[-1] = 1; + appAtts[attIndex++] = attId->name; + if (!atts[i].normalized) { + enum XML_Error result; + XML_Bool isCdata = XML_TRUE; + + /* figure out whether declared as other than CDATA */ + if (attId->maybeTokenized) { + int j; + for (j = 0; j < nDefaultAtts; j++) { + if (attId == elementType->defaultAtts[j].id) { + isCdata = elementType->defaultAtts[j].isCdata; + break; + } + } + } + + /* normalize the attribute value */ + result = storeAttributeValue(parser, enc, isCdata, + atts[i].valuePtr, atts[i].valueEnd, + &tempPool); + if (result) + return result; + appAtts[attIndex] = poolStart(&tempPool); + poolFinish(&tempPool); + } + else { + /* the value did not need normalizing */ + appAtts[attIndex] = poolStoreString(&tempPool, enc, atts[i].valuePtr, + atts[i].valueEnd); + if (appAtts[attIndex] == 0) + return XML_ERROR_NO_MEMORY; + poolFinish(&tempPool); + } + /* handle prefixed attribute names */ + if (attId->prefix) { + if (attId->xmlns) { + /* deal with namespace declarations here */ + enum XML_Error result = addBinding(parser, attId->prefix, attId, + appAtts[attIndex], bindingsPtr); + if (result) + return result; + --attIndex; + } + else { + /* deal with other prefixed names later */ + attIndex++; + nPrefixes++; + (attId->name)[-1] = 2; + } + } + else + attIndex++; + } + + /* set-up for XML_GetSpecifiedAttributeCount and XML_GetIdAttributeIndex */ + nSpecifiedAtts = attIndex; + if (elementType->idAtt && (elementType->idAtt->name)[-1]) { + for (i = 0; i < attIndex; i += 2) + if (appAtts[i] == elementType->idAtt->name) { + idAttIndex = i; + break; + } + } + else + idAttIndex = -1; + + /* do attribute defaulting */ + for (i = 0; i < nDefaultAtts; i++) { + const DEFAULT_ATTRIBUTE *da = elementType->defaultAtts + i; + if (!(da->id->name)[-1] && da->value) { + if (da->id->prefix) { + if (da->id->xmlns) { + enum XML_Error result = addBinding(parser, da->id->prefix, da->id, + da->value, bindingsPtr); + if (result) + return result; + } + else { + (da->id->name)[-1] = 2; + nPrefixes++; + appAtts[attIndex++] = da->id->name; + appAtts[attIndex++] = da->value; + } + } + else { + (da->id->name)[-1] = 1; + appAtts[attIndex++] = da->id->name; + appAtts[attIndex++] = da->value; + } + } + } + appAtts[attIndex] = 0; + + /* expand prefixed attribute names, check for duplicates, + and clear flags that say whether attributes were specified */ + i = 0; + if (nPrefixes) { + int j; /* hash table index */ + unsigned long version = nsAttsVersion; + int nsAttsSize = (int)1 << nsAttsPower; + /* size of hash table must be at least 2 * (# of prefixed attributes) */ + if ((nPrefixes << 1) >> nsAttsPower) { /* true for nsAttsPower = 0 */ + NS_ATT *temp; + /* hash table size must also be a power of 2 and >= 8 */ + while (nPrefixes >> nsAttsPower++); + if (nsAttsPower < 3) + nsAttsPower = 3; + nsAttsSize = (int)1 << nsAttsPower; + temp = (NS_ATT *)REALLOC(nsAtts, nsAttsSize * sizeof(NS_ATT)); + if (!temp) + return XML_ERROR_NO_MEMORY; + nsAtts = temp; + version = 0; /* force re-initialization of nsAtts hash table */ + } + /* using a version flag saves us from initializing nsAtts every time */ + if (!version) { /* initialize version flags when version wraps around */ + version = INIT_ATTS_VERSION; + for (j = nsAttsSize; j != 0; ) + nsAtts[--j].version = version; + } + nsAttsVersion = --version; + + /* expand prefixed names and check for duplicates */ + for (; i < attIndex; i += 2) { + const XML_Char *s = appAtts[i]; + if (s[-1] == 2) { /* prefixed */ + ATTRIBUTE_ID *id; + const BINDING *b; + unsigned long uriHash = hash_secret_salt; + ((XML_Char *)s)[-1] = 0; /* clear flag */ + id = (ATTRIBUTE_ID *)lookup(parser, &dtd->attributeIds, s, 0); + if (!id || !id->prefix) + return XML_ERROR_NO_MEMORY; + b = id->prefix->binding; + if (!b) + return XML_ERROR_UNBOUND_PREFIX; + + /* as we expand the name we also calculate its hash value */ + for (j = 0; j < b->uriLen; j++) { + const XML_Char c = b->uri[j]; + if (!poolAppendChar(&tempPool, c)) + return XML_ERROR_NO_MEMORY; + uriHash = CHAR_HASH(uriHash, c); + } + while (*s++ != XML_T(ASCII_COLON)) + ; + do { /* copies null terminator */ + const XML_Char c = *s; + if (!poolAppendChar(&tempPool, *s)) + return XML_ERROR_NO_MEMORY; + uriHash = CHAR_HASH(uriHash, c); + } while (*s++); + + { /* Check hash table for duplicate of expanded name (uriName). + Derived from code in lookup(parser, HASH_TABLE *table, ...). + */ + unsigned char step = 0; + unsigned long mask = nsAttsSize - 1; + j = uriHash & mask; /* index into hash table */ + while (nsAtts[j].version == version) { + /* for speed we compare stored hash values first */ + if (uriHash == nsAtts[j].hash) { + const XML_Char *s1 = poolStart(&tempPool); + const XML_Char *s2 = nsAtts[j].uriName; + /* s1 is null terminated, but not s2 */ + for (; *s1 == *s2 && *s1 != 0; s1++, s2++); + if (*s1 == 0) + return XML_ERROR_DUPLICATE_ATTRIBUTE; + } + if (!step) + step = PROBE_STEP(uriHash, mask, nsAttsPower); + j < step ? (j += nsAttsSize - step) : (j -= step); + } + } + + if (ns_triplets) { /* append namespace separator and prefix */ + tempPool.ptr[-1] = namespaceSeparator; + s = b->prefix->name; + do { + if (!poolAppendChar(&tempPool, *s)) + return XML_ERROR_NO_MEMORY; + } while (*s++); + } + + /* store expanded name in attribute list */ + s = poolStart(&tempPool); + poolFinish(&tempPool); + appAtts[i] = s; + + /* fill empty slot with new version, uriName and hash value */ + nsAtts[j].version = version; + nsAtts[j].hash = uriHash; + nsAtts[j].uriName = s; + + if (!--nPrefixes) { + i += 2; + break; + } + } + else /* not prefixed */ + ((XML_Char *)s)[-1] = 0; /* clear flag */ + } + } + /* clear flags for the remaining attributes */ + for (; i < attIndex; i += 2) + ((XML_Char *)(appAtts[i]))[-1] = 0; + for (binding = *bindingsPtr; binding; binding = binding->nextTagBinding) + binding->attId->name[-1] = 0; + + if (!ns) + return XML_ERROR_NONE; + + /* expand the element type name */ + if (elementType->prefix) { + binding = elementType->prefix->binding; + if (!binding) + return XML_ERROR_UNBOUND_PREFIX; + localPart = tagNamePtr->str; + while (*localPart++ != XML_T(ASCII_COLON)) + ; + } + else if (dtd->defaultPrefix.binding) { + binding = dtd->defaultPrefix.binding; + localPart = tagNamePtr->str; + } + else + return XML_ERROR_NONE; + prefixLen = 0; + if (ns_triplets && binding->prefix->name) { + for (; binding->prefix->name[prefixLen++];) + ; /* prefixLen includes null terminator */ + } + tagNamePtr->localPart = localPart; + tagNamePtr->uriLen = binding->uriLen; + tagNamePtr->prefix = binding->prefix->name; + tagNamePtr->prefixLen = prefixLen; + for (i = 0; localPart[i++];) + ; /* i includes null terminator */ + n = i + binding->uriLen + prefixLen; + if (n > binding->uriAlloc) { + TAG *p; + uri = (XML_Char *)MALLOC((n + EXPAND_SPARE) * sizeof(XML_Char)); + if (!uri) + return XML_ERROR_NO_MEMORY; + binding->uriAlloc = n + EXPAND_SPARE; + memcpy(uri, binding->uri, binding->uriLen * sizeof(XML_Char)); + for (p = tagStack; p; p = p->parent) + if (p->name.str == binding->uri) + p->name.str = uri; + FREE(binding->uri); + binding->uri = uri; + } + /* if namespaceSeparator != '\0' then uri includes it already */ + uri = binding->uri + binding->uriLen; + memcpy(uri, localPart, i * sizeof(XML_Char)); + /* we always have a namespace separator between localPart and prefix */ + if (prefixLen) { + uri += i - 1; + *uri = namespaceSeparator; /* replace null terminator */ + memcpy(uri + 1, binding->prefix->name, prefixLen * sizeof(XML_Char)); + } + tagNamePtr->str = binding->uri; + return XML_ERROR_NONE; +} + +/* addBinding() overwrites the value of prefix->binding without checking. + Therefore one must keep track of the old value outside of addBinding(). +*/ +static enum XML_Error +addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId, + const XML_Char *uri, BINDING **bindingsPtr) +{ + static const XML_Char xmlNamespace[] = { + ASCII_h, ASCII_t, ASCII_t, ASCII_p, ASCII_COLON, ASCII_SLASH, ASCII_SLASH, + ASCII_w, ASCII_w, ASCII_w, ASCII_PERIOD, ASCII_w, ASCII_3, ASCII_PERIOD, + ASCII_o, ASCII_r, ASCII_g, ASCII_SLASH, ASCII_X, ASCII_M, ASCII_L, + ASCII_SLASH, ASCII_1, ASCII_9, ASCII_9, ASCII_8, ASCII_SLASH, + ASCII_n, ASCII_a, ASCII_m, ASCII_e, ASCII_s, ASCII_p, ASCII_a, ASCII_c, + ASCII_e, '\0' + }; + static const int xmlLen = + (int)sizeof(xmlNamespace)/sizeof(XML_Char) - 1; + static const XML_Char xmlnsNamespace[] = { + ASCII_h, ASCII_t, ASCII_t, ASCII_p, ASCII_COLON, ASCII_SLASH, ASCII_SLASH, + ASCII_w, ASCII_w, ASCII_w, ASCII_PERIOD, ASCII_w, ASCII_3, ASCII_PERIOD, + ASCII_o, ASCII_r, ASCII_g, ASCII_SLASH, ASCII_2, ASCII_0, ASCII_0, + ASCII_0, ASCII_SLASH, ASCII_x, ASCII_m, ASCII_l, ASCII_n, ASCII_s, + ASCII_SLASH, '\0' + }; + static const int xmlnsLen = + (int)sizeof(xmlnsNamespace)/sizeof(XML_Char) - 1; + + XML_Bool mustBeXML = XML_FALSE; + XML_Bool isXML = XML_TRUE; + XML_Bool isXMLNS = XML_TRUE; + + BINDING *b; + int len; + + /* empty URI is only valid for default namespace per XML NS 1.0 (not 1.1) */ + if (*uri == XML_T('\0') && prefix->name) + return XML_ERROR_UNDECLARING_PREFIX; + + if (prefix->name + && prefix->name[0] == XML_T(ASCII_x) + && prefix->name[1] == XML_T(ASCII_m) + && prefix->name[2] == XML_T(ASCII_l)) { + + /* Not allowed to bind xmlns */ + if (prefix->name[3] == XML_T(ASCII_n) + && prefix->name[4] == XML_T(ASCII_s) + && prefix->name[5] == XML_T('\0')) + return XML_ERROR_RESERVED_PREFIX_XMLNS; + + if (prefix->name[3] == XML_T('\0')) + mustBeXML = XML_TRUE; + } + + for (len = 0; uri[len]; len++) { + if (isXML && (len > xmlLen || uri[len] != xmlNamespace[len])) + isXML = XML_FALSE; + + if (!mustBeXML && isXMLNS + && (len > xmlnsLen || uri[len] != xmlnsNamespace[len])) + isXMLNS = XML_FALSE; + } + isXML = isXML && len == xmlLen; + isXMLNS = isXMLNS && len == xmlnsLen; + + if (mustBeXML != isXML) + return mustBeXML ? XML_ERROR_RESERVED_PREFIX_XML + : XML_ERROR_RESERVED_NAMESPACE_URI; + + if (isXMLNS) + return XML_ERROR_RESERVED_NAMESPACE_URI; + + if (namespaceSeparator) + len++; + if (freeBindingList) { + b = freeBindingList; + if (len > b->uriAlloc) { + XML_Char *temp = (XML_Char *)REALLOC(b->uri, + sizeof(XML_Char) * (len + EXPAND_SPARE)); + if (temp == NULL) + return XML_ERROR_NO_MEMORY; + b->uri = temp; + b->uriAlloc = len + EXPAND_SPARE; + } + freeBindingList = b->nextTagBinding; + } + else { + b = (BINDING *)MALLOC(sizeof(BINDING)); + if (!b) + return XML_ERROR_NO_MEMORY; + b->uri = (XML_Char *)MALLOC(sizeof(XML_Char) * (len + EXPAND_SPARE)); + if (!b->uri) { + FREE(b); + return XML_ERROR_NO_MEMORY; + } + b->uriAlloc = len + EXPAND_SPARE; + } + b->uriLen = len; + memcpy(b->uri, uri, len * sizeof(XML_Char)); + if (namespaceSeparator) + b->uri[len - 1] = namespaceSeparator; + b->prefix = prefix; + b->attId = attId; + b->prevPrefixBinding = prefix->binding; + /* NULL binding when default namespace undeclared */ + if (*uri == XML_T('\0') && prefix == &_dtd->defaultPrefix) + prefix->binding = NULL; + else + prefix->binding = b; + b->nextTagBinding = *bindingsPtr; + *bindingsPtr = b; + /* if attId == NULL then we are not starting a namespace scope */ + if (attId && startNamespaceDeclHandler) + startNamespaceDeclHandler(handlerArg, prefix->name, + prefix->binding ? uri : 0); + return XML_ERROR_NONE; +} + +/* The idea here is to avoid using stack for each CDATA section when + the whole file is parsed with one call. +*/ +static enum XML_Error PTRCALL +cdataSectionProcessor(XML_Parser parser, + const char *start, + const char *end, + const char **endPtr) +{ + enum XML_Error result = doCdataSection(parser, encoding, &start, end, + endPtr, (XML_Bool)!ps_finalBuffer); + if (result != XML_ERROR_NONE) + return result; + if (start) { + if (parentParser) { /* we are parsing an external entity */ + processor = externalEntityContentProcessor; + return externalEntityContentProcessor(parser, start, end, endPtr); + } + else { + processor = contentProcessor; + return contentProcessor(parser, start, end, endPtr); + } + } + return result; +} + +/* startPtr gets set to non-null if the section is closed, and to null if + the section is not yet closed. +*/ +static enum XML_Error +doCdataSection(XML_Parser parser, + const ENCODING *enc, + const char **startPtr, + const char *end, + const char **nextPtr, + XML_Bool haveMore) +{ + const char *s = *startPtr; + const char **eventPP; + const char **eventEndPP; + if (enc == encoding) { + eventPP = &eventPtr; + *eventPP = s; + eventEndPP = &eventEndPtr; + } + else { + eventPP = &(openInternalEntities->internalEventPtr); + eventEndPP = &(openInternalEntities->internalEventEndPtr); + } + *eventPP = s; + *startPtr = NULL; + + for (;;) { + const char *next; + int tok = XmlCdataSectionTok(enc, s, end, &next); + *eventEndPP = next; + switch (tok) { + case XML_TOK_CDATA_SECT_CLOSE: + if (endCdataSectionHandler) + endCdataSectionHandler(handlerArg); +#if 0 + /* see comment under XML_TOK_CDATA_SECT_OPEN */ + else if (characterDataHandler) + characterDataHandler(handlerArg, dataBuf, 0); +#endif + else if (defaultHandler) + reportDefault(parser, enc, s, next); + *startPtr = next; + *nextPtr = next; + if (ps_parsing == XML_FINISHED) + return XML_ERROR_ABORTED; + else + return XML_ERROR_NONE; + case XML_TOK_DATA_NEWLINE: + if (characterDataHandler) { + XML_Char c = 0xA; + characterDataHandler(handlerArg, &c, 1); + } + else if (defaultHandler) + reportDefault(parser, enc, s, next); + break; + case XML_TOK_DATA_CHARS: + { + XML_CharacterDataHandler charDataHandler = characterDataHandler; + if (charDataHandler) { + if (MUST_CONVERT(enc, s)) { + for (;;) { + ICHAR *dataPtr = (ICHAR *)dataBuf; + const enum XML_Convert_Result convert_res = XmlConvert(enc, &s, next, &dataPtr, (ICHAR *)dataBufEnd); + *eventEndPP = next; + charDataHandler(handlerArg, dataBuf, + (int)(dataPtr - (ICHAR *)dataBuf)); + if ((convert_res == XML_CONVERT_COMPLETED) || (convert_res == XML_CONVERT_INPUT_INCOMPLETE)) + break; + *eventPP = s; + } + } + else + charDataHandler(handlerArg, + (XML_Char *)s, + (int)((XML_Char *)next - (XML_Char *)s)); + } + else if (defaultHandler) + reportDefault(parser, enc, s, next); + } + break; + case XML_TOK_INVALID: + *eventPP = next; + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL_CHAR: + if (haveMore) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_PARTIAL_CHAR; + case XML_TOK_PARTIAL: + case XML_TOK_NONE: + if (haveMore) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_UNCLOSED_CDATA_SECTION; + default: + *eventPP = next; + return XML_ERROR_UNEXPECTED_STATE; + } + + *eventPP = s = next; + switch (ps_parsing) { + case XML_SUSPENDED: + *nextPtr = next; + return XML_ERROR_NONE; + case XML_FINISHED: + return XML_ERROR_ABORTED; + default: ; + } + } + /* not reached */ +} + +#ifdef XML_DTD + +/* The idea here is to avoid using stack for each IGNORE section when + the whole file is parsed with one call. +*/ +static enum XML_Error PTRCALL +ignoreSectionProcessor(XML_Parser parser, + const char *start, + const char *end, + const char **endPtr) +{ + enum XML_Error result = doIgnoreSection(parser, encoding, &start, end, + endPtr, (XML_Bool)!ps_finalBuffer); + if (result != XML_ERROR_NONE) + return result; + if (start) { + processor = prologProcessor; + return prologProcessor(parser, start, end, endPtr); + } + return result; +} + +/* startPtr gets set to non-null is the section is closed, and to null + if the section is not yet closed. +*/ +static enum XML_Error +doIgnoreSection(XML_Parser parser, + const ENCODING *enc, + const char **startPtr, + const char *end, + const char **nextPtr, + XML_Bool haveMore) +{ + const char *next; + int tok; + const char *s = *startPtr; + const char **eventPP; + const char **eventEndPP; + if (enc == encoding) { + eventPP = &eventPtr; + *eventPP = s; + eventEndPP = &eventEndPtr; + } + else { + eventPP = &(openInternalEntities->internalEventPtr); + eventEndPP = &(openInternalEntities->internalEventEndPtr); + } + *eventPP = s; + *startPtr = NULL; + tok = XmlIgnoreSectionTok(enc, s, end, &next); + *eventEndPP = next; + switch (tok) { + case XML_TOK_IGNORE_SECT: + if (defaultHandler) + reportDefault(parser, enc, s, next); + *startPtr = next; + *nextPtr = next; + if (ps_parsing == XML_FINISHED) + return XML_ERROR_ABORTED; + else + return XML_ERROR_NONE; + case XML_TOK_INVALID: + *eventPP = next; + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL_CHAR: + if (haveMore) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_PARTIAL_CHAR; + case XML_TOK_PARTIAL: + case XML_TOK_NONE: + if (haveMore) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_SYNTAX; /* XML_ERROR_UNCLOSED_IGNORE_SECTION */ + default: + *eventPP = next; + return XML_ERROR_UNEXPECTED_STATE; + } + /* not reached */ +} + +#endif /* XML_DTD */ + +static enum XML_Error +initializeEncoding(XML_Parser parser) +{ + const char *s; +#ifdef XML_UNICODE + char encodingBuf[128]; + if (!protocolEncodingName) + s = NULL; + else { + int i; + for (i = 0; protocolEncodingName[i]; i++) { + if (i == sizeof(encodingBuf) - 1 + || (protocolEncodingName[i] & ~0x7f) != 0) { + encodingBuf[0] = '\0'; + break; + } + encodingBuf[i] = (char)protocolEncodingName[i]; + } + encodingBuf[i] = '\0'; + s = encodingBuf; + } +#else + s = protocolEncodingName; +#endif + if ((ns ? XmlInitEncodingNS : XmlInitEncoding)(&initEncoding, &encoding, s)) + return XML_ERROR_NONE; + return handleUnknownEncoding(parser, protocolEncodingName); +} + +static enum XML_Error +processXmlDecl(XML_Parser parser, int isGeneralTextEntity, + const char *s, const char *next) +{ + const char *encodingName = NULL; + const XML_Char *storedEncName = NULL; + const ENCODING *newEncoding = NULL; + const char *version = NULL; + const char *versionend; + const XML_Char *storedversion = NULL; + int standalone = -1; + if (!(ns + ? XmlParseXmlDeclNS + : XmlParseXmlDecl)(isGeneralTextEntity, + encoding, + s, + next, + &eventPtr, + &version, + &versionend, + &encodingName, + &newEncoding, + &standalone)) { + if (isGeneralTextEntity) + return XML_ERROR_TEXT_DECL; + else + return XML_ERROR_XML_DECL; + } + if (!isGeneralTextEntity && standalone == 1) { + _dtd->standalone = XML_TRUE; +#ifdef XML_DTD + if (paramEntityParsing == XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE) + paramEntityParsing = XML_PARAM_ENTITY_PARSING_NEVER; +#endif /* XML_DTD */ + } + if (xmlDeclHandler) { + if (encodingName != NULL) { + storedEncName = poolStoreString(&temp2Pool, + encoding, + encodingName, + encodingName + + XmlNameLength(encoding, encodingName)); + if (!storedEncName) + return XML_ERROR_NO_MEMORY; + poolFinish(&temp2Pool); + } + if (version) { + storedversion = poolStoreString(&temp2Pool, + encoding, + version, + versionend - encoding->minBytesPerChar); + if (!storedversion) + return XML_ERROR_NO_MEMORY; + } + xmlDeclHandler(handlerArg, storedversion, storedEncName, standalone); + } + else if (defaultHandler) + reportDefault(parser, encoding, s, next); + if (protocolEncodingName == NULL) { + if (newEncoding) { + if (newEncoding->minBytesPerChar != encoding->minBytesPerChar) { + eventPtr = encodingName; + return XML_ERROR_INCORRECT_ENCODING; + } + encoding = newEncoding; + } + else if (encodingName) { + enum XML_Error result; + if (!storedEncName) { + storedEncName = poolStoreString( + &temp2Pool, encoding, encodingName, + encodingName + XmlNameLength(encoding, encodingName)); + if (!storedEncName) + return XML_ERROR_NO_MEMORY; + } + result = handleUnknownEncoding(parser, storedEncName); + poolClear(&temp2Pool); + if (result == XML_ERROR_UNKNOWN_ENCODING) + eventPtr = encodingName; + return result; + } + } + + if (storedEncName || storedversion) + poolClear(&temp2Pool); + + return XML_ERROR_NONE; +} + +static enum XML_Error +handleUnknownEncoding(XML_Parser parser, const XML_Char *encodingName) +{ + if (unknownEncodingHandler) { + XML_Encoding info; + int i; + for (i = 0; i < 256; i++) + info.map[i] = -1; + info.convert = NULL; + info.data = NULL; + info.release = NULL; + if (unknownEncodingHandler(unknownEncodingHandlerData, encodingName, + &info)) { + ENCODING *enc; + unknownEncodingMem = MALLOC(XmlSizeOfUnknownEncoding()); + if (!unknownEncodingMem) { + if (info.release) + info.release(info.data); + return XML_ERROR_NO_MEMORY; + } + enc = (ns + ? XmlInitUnknownEncodingNS + : XmlInitUnknownEncoding)(unknownEncodingMem, + info.map, + info.convert, + info.data); + if (enc) { + unknownEncodingData = info.data; + unknownEncodingRelease = info.release; + encoding = enc; + return XML_ERROR_NONE; + } + } + if (info.release != NULL) + info.release(info.data); + } + return XML_ERROR_UNKNOWN_ENCODING; +} + +static enum XML_Error PTRCALL +prologInitProcessor(XML_Parser parser, + const char *s, + const char *end, + const char **nextPtr) +{ + enum XML_Error result = initializeEncoding(parser); + if (result != XML_ERROR_NONE) + return result; + processor = prologProcessor; + return prologProcessor(parser, s, end, nextPtr); +} + +#ifdef XML_DTD + +static enum XML_Error PTRCALL +externalParEntInitProcessor(XML_Parser parser, + const char *s, + const char *end, + const char **nextPtr) +{ + enum XML_Error result = initializeEncoding(parser); + if (result != XML_ERROR_NONE) + return result; + + /* we know now that XML_Parse(Buffer) has been called, + so we consider the external parameter entity read */ + _dtd->paramEntityRead = XML_TRUE; + + if (prologState.inEntityValue) { + processor = entityValueInitProcessor; + return entityValueInitProcessor(parser, s, end, nextPtr); + } + else { + processor = externalParEntProcessor; + return externalParEntProcessor(parser, s, end, nextPtr); + } +} + +static enum XML_Error PTRCALL +entityValueInitProcessor(XML_Parser parser, + const char *s, + const char *end, + const char **nextPtr) +{ + int tok; + const char *start = s; + const char *next = start; + eventPtr = start; + + for (;;) { + tok = XmlPrologTok(encoding, start, end, &next); + eventEndPtr = next; + if (tok <= 0) { + if (!ps_finalBuffer && tok != XML_TOK_INVALID) { + *nextPtr = s; + return XML_ERROR_NONE; + } + switch (tok) { + case XML_TOK_INVALID: + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL: + return XML_ERROR_UNCLOSED_TOKEN; + case XML_TOK_PARTIAL_CHAR: + return XML_ERROR_PARTIAL_CHAR; + case XML_TOK_NONE: /* start == end */ + default: + break; + } + /* found end of entity value - can store it now */ + return storeEntityValue(parser, encoding, s, end); + } + else if (tok == XML_TOK_XML_DECL) { + enum XML_Error result; + result = processXmlDecl(parser, 0, start, next); + if (result != XML_ERROR_NONE) + return result; + switch (ps_parsing) { + case XML_SUSPENDED: + *nextPtr = next; + return XML_ERROR_NONE; + case XML_FINISHED: + return XML_ERROR_ABORTED; + default: + *nextPtr = next; + } + /* stop scanning for text declaration - we found one */ + processor = entityValueProcessor; + return entityValueProcessor(parser, next, end, nextPtr); + } + /* If we are at the end of the buffer, this would cause XmlPrologTok to + return XML_TOK_NONE on the next call, which would then cause the + function to exit with *nextPtr set to s - that is what we want for other + tokens, but not for the BOM - we would rather like to skip it; + then, when this routine is entered the next time, XmlPrologTok will + return XML_TOK_INVALID, since the BOM is still in the buffer + */ + else if (tok == XML_TOK_BOM && next == end && !ps_finalBuffer) { + *nextPtr = next; + return XML_ERROR_NONE; + } + start = next; + eventPtr = start; + } +} + +static enum XML_Error PTRCALL +externalParEntProcessor(XML_Parser parser, + const char *s, + const char *end, + const char **nextPtr) +{ + const char *next = s; + int tok; + + tok = XmlPrologTok(encoding, s, end, &next); + if (tok <= 0) { + if (!ps_finalBuffer && tok != XML_TOK_INVALID) { + *nextPtr = s; + return XML_ERROR_NONE; + } + switch (tok) { + case XML_TOK_INVALID: + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL: + return XML_ERROR_UNCLOSED_TOKEN; + case XML_TOK_PARTIAL_CHAR: + return XML_ERROR_PARTIAL_CHAR; + case XML_TOK_NONE: /* start == end */ + default: + break; + } + } + /* This would cause the next stage, i.e. doProlog to be passed XML_TOK_BOM. + However, when parsing an external subset, doProlog will not accept a BOM + as valid, and report a syntax error, so we have to skip the BOM + */ + else if (tok == XML_TOK_BOM) { + s = next; + tok = XmlPrologTok(encoding, s, end, &next); + } + + processor = prologProcessor; + return doProlog(parser, encoding, s, end, tok, next, + nextPtr, (XML_Bool)!ps_finalBuffer); +} + +static enum XML_Error PTRCALL +entityValueProcessor(XML_Parser parser, + const char *s, + const char *end, + const char **nextPtr) +{ + const char *start = s; + const char *next = s; + const ENCODING *enc = encoding; + int tok; + + for (;;) { + tok = XmlPrologTok(enc, start, end, &next); + if (tok <= 0) { + if (!ps_finalBuffer && tok != XML_TOK_INVALID) { + *nextPtr = s; + return XML_ERROR_NONE; + } + switch (tok) { + case XML_TOK_INVALID: + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL: + return XML_ERROR_UNCLOSED_TOKEN; + case XML_TOK_PARTIAL_CHAR: + return XML_ERROR_PARTIAL_CHAR; + case XML_TOK_NONE: /* start == end */ + default: + break; + } + /* found end of entity value - can store it now */ + return storeEntityValue(parser, enc, s, end); + } + start = next; + } +} + +#endif /* XML_DTD */ + +static enum XML_Error PTRCALL +prologProcessor(XML_Parser parser, + const char *s, + const char *end, + const char **nextPtr) +{ + const char *next = s; + int tok = XmlPrologTok(encoding, s, end, &next); + return doProlog(parser, encoding, s, end, tok, next, + nextPtr, (XML_Bool)!ps_finalBuffer); +} + +static enum XML_Error +doProlog(XML_Parser parser, + const ENCODING *enc, + const char *s, + const char *end, + int tok, + const char *next, + const char **nextPtr, + XML_Bool haveMore) +{ +#ifdef XML_DTD + static const XML_Char externalSubsetName[] = { ASCII_HASH , '\0' }; +#endif /* XML_DTD */ + static const XML_Char atypeCDATA[] = + { ASCII_C, ASCII_D, ASCII_A, ASCII_T, ASCII_A, '\0' }; + static const XML_Char atypeID[] = { ASCII_I, ASCII_D, '\0' }; + static const XML_Char atypeIDREF[] = + { ASCII_I, ASCII_D, ASCII_R, ASCII_E, ASCII_F, '\0' }; + static const XML_Char atypeIDREFS[] = + { ASCII_I, ASCII_D, ASCII_R, ASCII_E, ASCII_F, ASCII_S, '\0' }; + static const XML_Char atypeENTITY[] = + { ASCII_E, ASCII_N, ASCII_T, ASCII_I, ASCII_T, ASCII_Y, '\0' }; + static const XML_Char atypeENTITIES[] = { ASCII_E, ASCII_N, + ASCII_T, ASCII_I, ASCII_T, ASCII_I, ASCII_E, ASCII_S, '\0' }; + static const XML_Char atypeNMTOKEN[] = { + ASCII_N, ASCII_M, ASCII_T, ASCII_O, ASCII_K, ASCII_E, ASCII_N, '\0' }; + static const XML_Char atypeNMTOKENS[] = { ASCII_N, ASCII_M, ASCII_T, + ASCII_O, ASCII_K, ASCII_E, ASCII_N, ASCII_S, '\0' }; + static const XML_Char notationPrefix[] = { ASCII_N, ASCII_O, ASCII_T, + ASCII_A, ASCII_T, ASCII_I, ASCII_O, ASCII_N, ASCII_LPAREN, '\0' }; + static const XML_Char enumValueSep[] = { ASCII_PIPE, '\0' }; + static const XML_Char enumValueStart[] = { ASCII_LPAREN, '\0' }; + + /* save one level of indirection */ + DTD * const dtd = _dtd; + + const char **eventPP; + const char **eventEndPP; + enum XML_Content_Quant quant; + + if (enc == encoding) { + eventPP = &eventPtr; + eventEndPP = &eventEndPtr; + } + else { + eventPP = &(openInternalEntities->internalEventPtr); + eventEndPP = &(openInternalEntities->internalEventEndPtr); + } + + for (;;) { + int role; + XML_Bool handleDefault = XML_TRUE; + *eventPP = s; + *eventEndPP = next; + if (tok <= 0) { + if (haveMore && tok != XML_TOK_INVALID) { + *nextPtr = s; + return XML_ERROR_NONE; + } + switch (tok) { + case XML_TOK_INVALID: + *eventPP = next; + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL: + return XML_ERROR_UNCLOSED_TOKEN; + case XML_TOK_PARTIAL_CHAR: + return XML_ERROR_PARTIAL_CHAR; + case -XML_TOK_PROLOG_S: + tok = -tok; + break; + case XML_TOK_NONE: +#ifdef XML_DTD + /* for internal PE NOT referenced between declarations */ + if (enc != encoding && !openInternalEntities->betweenDecl) { + *nextPtr = s; + return XML_ERROR_NONE; + } + /* WFC: PE Between Declarations - must check that PE contains + complete markup, not only for external PEs, but also for + internal PEs if the reference occurs between declarations. + */ + if (isParamEntity || enc != encoding) { + if (XmlTokenRole(&prologState, XML_TOK_NONE, end, end, enc) + == XML_ROLE_ERROR) + return XML_ERROR_INCOMPLETE_PE; + *nextPtr = s; + return XML_ERROR_NONE; + } +#endif /* XML_DTD */ + return XML_ERROR_NO_ELEMENTS; + default: + tok = -tok; + next = end; + break; + } + } + role = XmlTokenRole(&prologState, tok, s, next, enc); + switch (role) { + case XML_ROLE_XML_DECL: + { + enum XML_Error result = processXmlDecl(parser, 0, s, next); + if (result != XML_ERROR_NONE) + return result; + enc = encoding; + handleDefault = XML_FALSE; + } + break; + case XML_ROLE_DOCTYPE_NAME: + if (startDoctypeDeclHandler) { + doctypeName = poolStoreString(&tempPool, enc, s, next); + if (!doctypeName) + return XML_ERROR_NO_MEMORY; + poolFinish(&tempPool); + doctypePubid = NULL; + handleDefault = XML_FALSE; + } + doctypeSysid = NULL; /* always initialize to NULL */ + break; + case XML_ROLE_DOCTYPE_INTERNAL_SUBSET: + if (startDoctypeDeclHandler) { + startDoctypeDeclHandler(handlerArg, doctypeName, doctypeSysid, + doctypePubid, 1); + doctypeName = NULL; + poolClear(&tempPool); + handleDefault = XML_FALSE; + } + break; +#ifdef XML_DTD + case XML_ROLE_TEXT_DECL: + { + enum XML_Error result = processXmlDecl(parser, 1, s, next); + if (result != XML_ERROR_NONE) + return result; + enc = encoding; + handleDefault = XML_FALSE; + } + break; +#endif /* XML_DTD */ + case XML_ROLE_DOCTYPE_PUBLIC_ID: +#ifdef XML_DTD + useForeignDTD = XML_FALSE; + declEntity = (ENTITY *)lookup(parser, + &dtd->paramEntities, + externalSubsetName, + sizeof(ENTITY)); + if (!declEntity) + return XML_ERROR_NO_MEMORY; +#endif /* XML_DTD */ + dtd->hasParamEntityRefs = XML_TRUE; + if (startDoctypeDeclHandler) { + XML_Char *pubId; + if (!XmlIsPublicId(enc, s, next, eventPP)) + return XML_ERROR_PUBLICID; + pubId = poolStoreString(&tempPool, enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!pubId) + return XML_ERROR_NO_MEMORY; + normalizePublicId(pubId); + poolFinish(&tempPool); + doctypePubid = pubId; + handleDefault = XML_FALSE; + goto alreadyChecked; + } + /* fall through */ + case XML_ROLE_ENTITY_PUBLIC_ID: + if (!XmlIsPublicId(enc, s, next, eventPP)) + return XML_ERROR_PUBLICID; + alreadyChecked: + if (dtd->keepProcessing && declEntity) { + XML_Char *tem = poolStoreString(&dtd->pool, + enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!tem) + return XML_ERROR_NO_MEMORY; + normalizePublicId(tem); + declEntity->publicId = tem; + poolFinish(&dtd->pool); + if (entityDeclHandler) + handleDefault = XML_FALSE; + } + break; + case XML_ROLE_DOCTYPE_CLOSE: + if (doctypeName) { + startDoctypeDeclHandler(handlerArg, doctypeName, + doctypeSysid, doctypePubid, 0); + poolClear(&tempPool); + handleDefault = XML_FALSE; + } + /* doctypeSysid will be non-NULL in the case of a previous + XML_ROLE_DOCTYPE_SYSTEM_ID, even if startDoctypeDeclHandler + was not set, indicating an external subset + */ +#ifdef XML_DTD + if (doctypeSysid || useForeignDTD) { + XML_Bool hadParamEntityRefs = dtd->hasParamEntityRefs; + dtd->hasParamEntityRefs = XML_TRUE; + if (paramEntityParsing && externalEntityRefHandler) { + ENTITY *entity = (ENTITY *)lookup(parser, + &dtd->paramEntities, + externalSubsetName, + sizeof(ENTITY)); + if (!entity) + return XML_ERROR_NO_MEMORY; + if (useForeignDTD) + entity->base = curBase; + dtd->paramEntityRead = XML_FALSE; + if (!externalEntityRefHandler(externalEntityRefHandlerArg, + 0, + entity->base, + entity->systemId, + entity->publicId)) + return XML_ERROR_EXTERNAL_ENTITY_HANDLING; + if (dtd->paramEntityRead) { + if (!dtd->standalone && + notStandaloneHandler && + !notStandaloneHandler(handlerArg)) + return XML_ERROR_NOT_STANDALONE; + } + /* if we didn't read the foreign DTD then this means that there + is no external subset and we must reset dtd->hasParamEntityRefs + */ + else if (!doctypeSysid) + dtd->hasParamEntityRefs = hadParamEntityRefs; + /* end of DTD - no need to update dtd->keepProcessing */ + } + useForeignDTD = XML_FALSE; + } +#endif /* XML_DTD */ + if (endDoctypeDeclHandler) { + endDoctypeDeclHandler(handlerArg); + handleDefault = XML_FALSE; + } + break; + case XML_ROLE_INSTANCE_START: +#ifdef XML_DTD + /* if there is no DOCTYPE declaration then now is the + last chance to read the foreign DTD + */ + if (useForeignDTD) { + XML_Bool hadParamEntityRefs = dtd->hasParamEntityRefs; + dtd->hasParamEntityRefs = XML_TRUE; + if (paramEntityParsing && externalEntityRefHandler) { + ENTITY *entity = (ENTITY *)lookup(parser, &dtd->paramEntities, + externalSubsetName, + sizeof(ENTITY)); + if (!entity) + return XML_ERROR_NO_MEMORY; + entity->base = curBase; + dtd->paramEntityRead = XML_FALSE; + if (!externalEntityRefHandler(externalEntityRefHandlerArg, + 0, + entity->base, + entity->systemId, + entity->publicId)) + return XML_ERROR_EXTERNAL_ENTITY_HANDLING; + if (dtd->paramEntityRead) { + if (!dtd->standalone && + notStandaloneHandler && + !notStandaloneHandler(handlerArg)) + return XML_ERROR_NOT_STANDALONE; + } + /* if we didn't read the foreign DTD then this means that there + is no external subset and we must reset dtd->hasParamEntityRefs + */ + else + dtd->hasParamEntityRefs = hadParamEntityRefs; + /* end of DTD - no need to update dtd->keepProcessing */ + } + } +#endif /* XML_DTD */ + processor = contentProcessor; + return contentProcessor(parser, s, end, nextPtr); + case XML_ROLE_ATTLIST_ELEMENT_NAME: + declElementType = getElementType(parser, enc, s, next); + if (!declElementType) + return XML_ERROR_NO_MEMORY; + goto checkAttListDeclHandler; + case XML_ROLE_ATTRIBUTE_NAME: + declAttributeId = getAttributeId(parser, enc, s, next); + if (!declAttributeId) + return XML_ERROR_NO_MEMORY; + declAttributeIsCdata = XML_FALSE; + declAttributeType = NULL; + declAttributeIsId = XML_FALSE; + goto checkAttListDeclHandler; + case XML_ROLE_ATTRIBUTE_TYPE_CDATA: + declAttributeIsCdata = XML_TRUE; + declAttributeType = atypeCDATA; + goto checkAttListDeclHandler; + case XML_ROLE_ATTRIBUTE_TYPE_ID: + declAttributeIsId = XML_TRUE; + declAttributeType = atypeID; + goto checkAttListDeclHandler; + case XML_ROLE_ATTRIBUTE_TYPE_IDREF: + declAttributeType = atypeIDREF; + goto checkAttListDeclHandler; + case XML_ROLE_ATTRIBUTE_TYPE_IDREFS: + declAttributeType = atypeIDREFS; + goto checkAttListDeclHandler; + case XML_ROLE_ATTRIBUTE_TYPE_ENTITY: + declAttributeType = atypeENTITY; + goto checkAttListDeclHandler; + case XML_ROLE_ATTRIBUTE_TYPE_ENTITIES: + declAttributeType = atypeENTITIES; + goto checkAttListDeclHandler; + case XML_ROLE_ATTRIBUTE_TYPE_NMTOKEN: + declAttributeType = atypeNMTOKEN; + goto checkAttListDeclHandler; + case XML_ROLE_ATTRIBUTE_TYPE_NMTOKENS: + declAttributeType = atypeNMTOKENS; + checkAttListDeclHandler: + if (dtd->keepProcessing && attlistDeclHandler) + handleDefault = XML_FALSE; + break; + case XML_ROLE_ATTRIBUTE_ENUM_VALUE: + case XML_ROLE_ATTRIBUTE_NOTATION_VALUE: + if (dtd->keepProcessing && attlistDeclHandler) { + const XML_Char *prefix; + if (declAttributeType) { + prefix = enumValueSep; + } + else { + prefix = (role == XML_ROLE_ATTRIBUTE_NOTATION_VALUE + ? notationPrefix + : enumValueStart); + } + if (!poolAppendString(&tempPool, prefix)) + return XML_ERROR_NO_MEMORY; + if (!poolAppend(&tempPool, enc, s, next)) + return XML_ERROR_NO_MEMORY; + declAttributeType = tempPool.start; + handleDefault = XML_FALSE; + } + break; + case XML_ROLE_IMPLIED_ATTRIBUTE_VALUE: + case XML_ROLE_REQUIRED_ATTRIBUTE_VALUE: + if (dtd->keepProcessing) { + if (!defineAttribute(declElementType, declAttributeId, + declAttributeIsCdata, declAttributeIsId, + 0, parser)) + return XML_ERROR_NO_MEMORY; + if (attlistDeclHandler && declAttributeType) { + if (*declAttributeType == XML_T(ASCII_LPAREN) + || (*declAttributeType == XML_T(ASCII_N) + && declAttributeType[1] == XML_T(ASCII_O))) { + /* Enumerated or Notation type */ + if (!poolAppendChar(&tempPool, XML_T(ASCII_RPAREN)) + || !poolAppendChar(&tempPool, XML_T('\0'))) + return XML_ERROR_NO_MEMORY; + declAttributeType = tempPool.start; + poolFinish(&tempPool); + } + *eventEndPP = s; + attlistDeclHandler(handlerArg, declElementType->name, + declAttributeId->name, declAttributeType, + 0, role == XML_ROLE_REQUIRED_ATTRIBUTE_VALUE); + poolClear(&tempPool); + handleDefault = XML_FALSE; + } + } + break; + case XML_ROLE_DEFAULT_ATTRIBUTE_VALUE: + case XML_ROLE_FIXED_ATTRIBUTE_VALUE: + if (dtd->keepProcessing) { + const XML_Char *attVal; + enum XML_Error result = + storeAttributeValue(parser, enc, declAttributeIsCdata, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar, + &dtd->pool); + if (result) + return result; + attVal = poolStart(&dtd->pool); + poolFinish(&dtd->pool); + /* ID attributes aren't allowed to have a default */ + if (!defineAttribute(declElementType, declAttributeId, + declAttributeIsCdata, XML_FALSE, attVal, parser)) + return XML_ERROR_NO_MEMORY; + if (attlistDeclHandler && declAttributeType) { + if (*declAttributeType == XML_T(ASCII_LPAREN) + || (*declAttributeType == XML_T(ASCII_N) + && declAttributeType[1] == XML_T(ASCII_O))) { + /* Enumerated or Notation type */ + if (!poolAppendChar(&tempPool, XML_T(ASCII_RPAREN)) + || !poolAppendChar(&tempPool, XML_T('\0'))) + return XML_ERROR_NO_MEMORY; + declAttributeType = tempPool.start; + poolFinish(&tempPool); + } + *eventEndPP = s; + attlistDeclHandler(handlerArg, declElementType->name, + declAttributeId->name, declAttributeType, + attVal, + role == XML_ROLE_FIXED_ATTRIBUTE_VALUE); + poolClear(&tempPool); + handleDefault = XML_FALSE; + } + } + break; + case XML_ROLE_ENTITY_VALUE: + if (dtd->keepProcessing) { + enum XML_Error result = storeEntityValue(parser, enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (declEntity) { + declEntity->textPtr = poolStart(&dtd->entityValuePool); + declEntity->textLen = (int)(poolLength(&dtd->entityValuePool)); + poolFinish(&dtd->entityValuePool); + if (entityDeclHandler) { + *eventEndPP = s; + entityDeclHandler(handlerArg, + declEntity->name, + declEntity->is_param, + declEntity->textPtr, + declEntity->textLen, + curBase, 0, 0, 0); + handleDefault = XML_FALSE; + } + } + else + poolDiscard(&dtd->entityValuePool); + if (result != XML_ERROR_NONE) + return result; + } + break; + case XML_ROLE_DOCTYPE_SYSTEM_ID: +#ifdef XML_DTD + useForeignDTD = XML_FALSE; +#endif /* XML_DTD */ + dtd->hasParamEntityRefs = XML_TRUE; + if (startDoctypeDeclHandler) { + doctypeSysid = poolStoreString(&tempPool, enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (doctypeSysid == NULL) + return XML_ERROR_NO_MEMORY; + poolFinish(&tempPool); + handleDefault = XML_FALSE; + } +#ifdef XML_DTD + else + /* use externalSubsetName to make doctypeSysid non-NULL + for the case where no startDoctypeDeclHandler is set */ + doctypeSysid = externalSubsetName; +#endif /* XML_DTD */ + if (!dtd->standalone +#ifdef XML_DTD + && !paramEntityParsing +#endif /* XML_DTD */ + && notStandaloneHandler + && !notStandaloneHandler(handlerArg)) + return XML_ERROR_NOT_STANDALONE; +#ifndef XML_DTD + break; +#else /* XML_DTD */ + if (!declEntity) { + declEntity = (ENTITY *)lookup(parser, + &dtd->paramEntities, + externalSubsetName, + sizeof(ENTITY)); + if (!declEntity) + return XML_ERROR_NO_MEMORY; + declEntity->publicId = NULL; + } + /* fall through */ +#endif /* XML_DTD */ + case XML_ROLE_ENTITY_SYSTEM_ID: + if (dtd->keepProcessing && declEntity) { + declEntity->systemId = poolStoreString(&dtd->pool, enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!declEntity->systemId) + return XML_ERROR_NO_MEMORY; + declEntity->base = curBase; + poolFinish(&dtd->pool); + if (entityDeclHandler) + handleDefault = XML_FALSE; + } + break; + case XML_ROLE_ENTITY_COMPLETE: + if (dtd->keepProcessing && declEntity && entityDeclHandler) { + *eventEndPP = s; + entityDeclHandler(handlerArg, + declEntity->name, + declEntity->is_param, + 0,0, + declEntity->base, + declEntity->systemId, + declEntity->publicId, + 0); + handleDefault = XML_FALSE; + } + break; + case XML_ROLE_ENTITY_NOTATION_NAME: + if (dtd->keepProcessing && declEntity) { + declEntity->notation = poolStoreString(&dtd->pool, enc, s, next); + if (!declEntity->notation) + return XML_ERROR_NO_MEMORY; + poolFinish(&dtd->pool); + if (unparsedEntityDeclHandler) { + *eventEndPP = s; + unparsedEntityDeclHandler(handlerArg, + declEntity->name, + declEntity->base, + declEntity->systemId, + declEntity->publicId, + declEntity->notation); + handleDefault = XML_FALSE; + } + else if (entityDeclHandler) { + *eventEndPP = s; + entityDeclHandler(handlerArg, + declEntity->name, + 0,0,0, + declEntity->base, + declEntity->systemId, + declEntity->publicId, + declEntity->notation); + handleDefault = XML_FALSE; + } + } + break; + case XML_ROLE_GENERAL_ENTITY_NAME: + { + if (XmlPredefinedEntityName(enc, s, next)) { + declEntity = NULL; + break; + } + if (dtd->keepProcessing) { + const XML_Char *name = poolStoreString(&dtd->pool, enc, s, next); + if (!name) + return XML_ERROR_NO_MEMORY; + declEntity = (ENTITY *)lookup(parser, &dtd->generalEntities, name, + sizeof(ENTITY)); + if (!declEntity) + return XML_ERROR_NO_MEMORY; + if (declEntity->name != name) { + poolDiscard(&dtd->pool); + declEntity = NULL; + } + else { + poolFinish(&dtd->pool); + declEntity->publicId = NULL; + declEntity->is_param = XML_FALSE; + /* if we have a parent parser or are reading an internal parameter + entity, then the entity declaration is not considered "internal" + */ + declEntity->is_internal = !(parentParser || openInternalEntities); + if (entityDeclHandler) + handleDefault = XML_FALSE; + } + } + else { + poolDiscard(&dtd->pool); + declEntity = NULL; + } + } + break; + case XML_ROLE_PARAM_ENTITY_NAME: +#ifdef XML_DTD + if (dtd->keepProcessing) { + const XML_Char *name = poolStoreString(&dtd->pool, enc, s, next); + if (!name) + return XML_ERROR_NO_MEMORY; + declEntity = (ENTITY *)lookup(parser, &dtd->paramEntities, + name, sizeof(ENTITY)); + if (!declEntity) + return XML_ERROR_NO_MEMORY; + if (declEntity->name != name) { + poolDiscard(&dtd->pool); + declEntity = NULL; + } + else { + poolFinish(&dtd->pool); + declEntity->publicId = NULL; + declEntity->is_param = XML_TRUE; + /* if we have a parent parser or are reading an internal parameter + entity, then the entity declaration is not considered "internal" + */ + declEntity->is_internal = !(parentParser || openInternalEntities); + if (entityDeclHandler) + handleDefault = XML_FALSE; + } + } + else { + poolDiscard(&dtd->pool); + declEntity = NULL; + } +#else /* not XML_DTD */ + declEntity = NULL; +#endif /* XML_DTD */ + break; + case XML_ROLE_NOTATION_NAME: + declNotationPublicId = NULL; + declNotationName = NULL; + if (notationDeclHandler) { + declNotationName = poolStoreString(&tempPool, enc, s, next); + if (!declNotationName) + return XML_ERROR_NO_MEMORY; + poolFinish(&tempPool); + handleDefault = XML_FALSE; + } + break; + case XML_ROLE_NOTATION_PUBLIC_ID: + if (!XmlIsPublicId(enc, s, next, eventPP)) + return XML_ERROR_PUBLICID; + if (declNotationName) { /* means notationDeclHandler != NULL */ + XML_Char *tem = poolStoreString(&tempPool, + enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!tem) + return XML_ERROR_NO_MEMORY; + normalizePublicId(tem); + declNotationPublicId = tem; + poolFinish(&tempPool); + handleDefault = XML_FALSE; + } + break; + case XML_ROLE_NOTATION_SYSTEM_ID: + if (declNotationName && notationDeclHandler) { + const XML_Char *systemId + = poolStoreString(&tempPool, enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!systemId) + return XML_ERROR_NO_MEMORY; + *eventEndPP = s; + notationDeclHandler(handlerArg, + declNotationName, + curBase, + systemId, + declNotationPublicId); + handleDefault = XML_FALSE; + } + poolClear(&tempPool); + break; + case XML_ROLE_NOTATION_NO_SYSTEM_ID: + if (declNotationPublicId && notationDeclHandler) { + *eventEndPP = s; + notationDeclHandler(handlerArg, + declNotationName, + curBase, + 0, + declNotationPublicId); + handleDefault = XML_FALSE; + } + poolClear(&tempPool); + break; + case XML_ROLE_ERROR: + switch (tok) { + case XML_TOK_PARAM_ENTITY_REF: + /* PE references in internal subset are + not allowed within declarations. */ + return XML_ERROR_PARAM_ENTITY_REF; + case XML_TOK_XML_DECL: + return XML_ERROR_MISPLACED_XML_PI; + default: + return XML_ERROR_SYNTAX; + } +#ifdef XML_DTD + case XML_ROLE_IGNORE_SECT: + { + enum XML_Error result; + if (defaultHandler) + reportDefault(parser, enc, s, next); + handleDefault = XML_FALSE; + result = doIgnoreSection(parser, enc, &next, end, nextPtr, haveMore); + if (result != XML_ERROR_NONE) + return result; + else if (!next) { + processor = ignoreSectionProcessor; + return result; + } + } + break; +#endif /* XML_DTD */ + case XML_ROLE_GROUP_OPEN: + if (prologState.level >= groupSize) { + if (groupSize) { + char *temp = (char *)REALLOC(groupConnector, groupSize *= 2); + if (temp == NULL) + return XML_ERROR_NO_MEMORY; + groupConnector = temp; + if (dtd->scaffIndex) { + int *temp = (int *)REALLOC(dtd->scaffIndex, + groupSize * sizeof(int)); + if (temp == NULL) + return XML_ERROR_NO_MEMORY; + dtd->scaffIndex = temp; + } + } + else { + groupConnector = (char *)MALLOC(groupSize = 32); + if (!groupConnector) + return XML_ERROR_NO_MEMORY; + } + } + groupConnector[prologState.level] = 0; + if (dtd->in_eldecl) { + int myindex = nextScaffoldPart(parser); + if (myindex < 0) + return XML_ERROR_NO_MEMORY; + dtd->scaffIndex[dtd->scaffLevel] = myindex; + dtd->scaffLevel++; + dtd->scaffold[myindex].type = XML_CTYPE_SEQ; + if (elementDeclHandler) + handleDefault = XML_FALSE; + } + break; + case XML_ROLE_GROUP_SEQUENCE: + if (groupConnector[prologState.level] == ASCII_PIPE) + return XML_ERROR_SYNTAX; + groupConnector[prologState.level] = ASCII_COMMA; + if (dtd->in_eldecl && elementDeclHandler) + handleDefault = XML_FALSE; + break; + case XML_ROLE_GROUP_CHOICE: + if (groupConnector[prologState.level] == ASCII_COMMA) + return XML_ERROR_SYNTAX; + if (dtd->in_eldecl + && !groupConnector[prologState.level] + && (dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel - 1]].type + != XML_CTYPE_MIXED) + ) { + dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel - 1]].type + = XML_CTYPE_CHOICE; + if (elementDeclHandler) + handleDefault = XML_FALSE; + } + groupConnector[prologState.level] = ASCII_PIPE; + break; + case XML_ROLE_PARAM_ENTITY_REF: +#ifdef XML_DTD + case XML_ROLE_INNER_PARAM_ENTITY_REF: + dtd->hasParamEntityRefs = XML_TRUE; + if (!paramEntityParsing) + dtd->keepProcessing = dtd->standalone; + else { + const XML_Char *name; + ENTITY *entity; + name = poolStoreString(&dtd->pool, enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!name) + return XML_ERROR_NO_MEMORY; + entity = (ENTITY *)lookup(parser, &dtd->paramEntities, name, 0); + poolDiscard(&dtd->pool); + /* first, determine if a check for an existing declaration is needed; + if yes, check that the entity exists, and that it is internal, + otherwise call the skipped entity handler + */ + if (prologState.documentEntity && + (dtd->standalone + ? !openInternalEntities + : !dtd->hasParamEntityRefs)) { + if (!entity) + return XML_ERROR_UNDEFINED_ENTITY; + else if (!entity->is_internal) + return XML_ERROR_ENTITY_DECLARED_IN_PE; + } + else if (!entity) { + dtd->keepProcessing = dtd->standalone; + /* cannot report skipped entities in declarations */ + if ((role == XML_ROLE_PARAM_ENTITY_REF) && skippedEntityHandler) { + skippedEntityHandler(handlerArg, name, 1); + handleDefault = XML_FALSE; + } + break; + } + if (entity->open) + return XML_ERROR_RECURSIVE_ENTITY_REF; + if (entity->textPtr) { + enum XML_Error result; + XML_Bool betweenDecl = + (role == XML_ROLE_PARAM_ENTITY_REF ? XML_TRUE : XML_FALSE); + result = processInternalEntity(parser, entity, betweenDecl); + if (result != XML_ERROR_NONE) + return result; + handleDefault = XML_FALSE; + break; + } + if (externalEntityRefHandler) { + dtd->paramEntityRead = XML_FALSE; + entity->open = XML_TRUE; + if (!externalEntityRefHandler(externalEntityRefHandlerArg, + 0, + entity->base, + entity->systemId, + entity->publicId)) { + entity->open = XML_FALSE; + return XML_ERROR_EXTERNAL_ENTITY_HANDLING; + } + entity->open = XML_FALSE; + handleDefault = XML_FALSE; + if (!dtd->paramEntityRead) { + dtd->keepProcessing = dtd->standalone; + break; + } + } + else { + dtd->keepProcessing = dtd->standalone; + break; + } + } +#endif /* XML_DTD */ + if (!dtd->standalone && + notStandaloneHandler && + !notStandaloneHandler(handlerArg)) + return XML_ERROR_NOT_STANDALONE; + break; + + /* Element declaration stuff */ + + case XML_ROLE_ELEMENT_NAME: + if (elementDeclHandler) { + declElementType = getElementType(parser, enc, s, next); + if (!declElementType) + return XML_ERROR_NO_MEMORY; + dtd->scaffLevel = 0; + dtd->scaffCount = 0; + dtd->in_eldecl = XML_TRUE; + handleDefault = XML_FALSE; + } + break; + + case XML_ROLE_CONTENT_ANY: + case XML_ROLE_CONTENT_EMPTY: + if (dtd->in_eldecl) { + if (elementDeclHandler) { + XML_Content * content = (XML_Content *) MALLOC(sizeof(XML_Content)); + if (!content) + return XML_ERROR_NO_MEMORY; + content->quant = XML_CQUANT_NONE; + content->name = NULL; + content->numchildren = 0; + content->children = NULL; + content->type = ((role == XML_ROLE_CONTENT_ANY) ? + XML_CTYPE_ANY : + XML_CTYPE_EMPTY); + *eventEndPP = s; + elementDeclHandler(handlerArg, declElementType->name, content); + handleDefault = XML_FALSE; + } + dtd->in_eldecl = XML_FALSE; + } + break; + + case XML_ROLE_CONTENT_PCDATA: + if (dtd->in_eldecl) { + dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel - 1]].type + = XML_CTYPE_MIXED; + if (elementDeclHandler) + handleDefault = XML_FALSE; + } + break; + + case XML_ROLE_CONTENT_ELEMENT: + quant = XML_CQUANT_NONE; + goto elementContent; + case XML_ROLE_CONTENT_ELEMENT_OPT: + quant = XML_CQUANT_OPT; + goto elementContent; + case XML_ROLE_CONTENT_ELEMENT_REP: + quant = XML_CQUANT_REP; + goto elementContent; + case XML_ROLE_CONTENT_ELEMENT_PLUS: + quant = XML_CQUANT_PLUS; + elementContent: + if (dtd->in_eldecl) { + ELEMENT_TYPE *el; + const XML_Char *name; + int nameLen; + const char *nxt = (quant == XML_CQUANT_NONE + ? next + : next - enc->minBytesPerChar); + int myindex = nextScaffoldPart(parser); + if (myindex < 0) + return XML_ERROR_NO_MEMORY; + dtd->scaffold[myindex].type = XML_CTYPE_NAME; + dtd->scaffold[myindex].quant = quant; + el = getElementType(parser, enc, s, nxt); + if (!el) + return XML_ERROR_NO_MEMORY; + name = el->name; + dtd->scaffold[myindex].name = name; + nameLen = 0; + for (; name[nameLen++]; ); + dtd->contentStringLen += nameLen; + if (elementDeclHandler) + handleDefault = XML_FALSE; + } + break; + + case XML_ROLE_GROUP_CLOSE: + quant = XML_CQUANT_NONE; + goto closeGroup; + case XML_ROLE_GROUP_CLOSE_OPT: + quant = XML_CQUANT_OPT; + goto closeGroup; + case XML_ROLE_GROUP_CLOSE_REP: + quant = XML_CQUANT_REP; + goto closeGroup; + case XML_ROLE_GROUP_CLOSE_PLUS: + quant = XML_CQUANT_PLUS; + closeGroup: + if (dtd->in_eldecl) { + if (elementDeclHandler) + handleDefault = XML_FALSE; + dtd->scaffLevel--; + dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel]].quant = quant; + if (dtd->scaffLevel == 0) { + if (!handleDefault) { + XML_Content *model = build_model(parser); + if (!model) + return XML_ERROR_NO_MEMORY; + *eventEndPP = s; + elementDeclHandler(handlerArg, declElementType->name, model); + } + dtd->in_eldecl = XML_FALSE; + dtd->contentStringLen = 0; + } + } + break; + /* End element declaration stuff */ + + case XML_ROLE_PI: + if (!reportProcessingInstruction(parser, enc, s, next)) + return XML_ERROR_NO_MEMORY; + handleDefault = XML_FALSE; + break; + case XML_ROLE_COMMENT: + if (!reportComment(parser, enc, s, next)) + return XML_ERROR_NO_MEMORY; + handleDefault = XML_FALSE; + break; + case XML_ROLE_NONE: + switch (tok) { + case XML_TOK_BOM: + handleDefault = XML_FALSE; + break; + } + break; + case XML_ROLE_DOCTYPE_NONE: + if (startDoctypeDeclHandler) + handleDefault = XML_FALSE; + break; + case XML_ROLE_ENTITY_NONE: + if (dtd->keepProcessing && entityDeclHandler) + handleDefault = XML_FALSE; + break; + case XML_ROLE_NOTATION_NONE: + if (notationDeclHandler) + handleDefault = XML_FALSE; + break; + case XML_ROLE_ATTLIST_NONE: + if (dtd->keepProcessing && attlistDeclHandler) + handleDefault = XML_FALSE; + break; + case XML_ROLE_ELEMENT_NONE: + if (elementDeclHandler) + handleDefault = XML_FALSE; + break; + } /* end of big switch */ + + if (handleDefault && defaultHandler) + reportDefault(parser, enc, s, next); + + switch (ps_parsing) { + case XML_SUSPENDED: + *nextPtr = next; + return XML_ERROR_NONE; + case XML_FINISHED: + return XML_ERROR_ABORTED; + default: + s = next; + tok = XmlPrologTok(enc, s, end, &next); + } + } + /* not reached */ +} + +static enum XML_Error PTRCALL +epilogProcessor(XML_Parser parser, + const char *s, + const char *end, + const char **nextPtr) +{ + processor = epilogProcessor; + eventPtr = s; + for (;;) { + const char *next = NULL; + int tok = XmlPrologTok(encoding, s, end, &next); + eventEndPtr = next; + switch (tok) { + /* report partial linebreak - it might be the last token */ + case -XML_TOK_PROLOG_S: + if (defaultHandler) { + reportDefault(parser, encoding, s, next); + if (ps_parsing == XML_FINISHED) + return XML_ERROR_ABORTED; + } + *nextPtr = next; + return XML_ERROR_NONE; + case XML_TOK_NONE: + *nextPtr = s; + return XML_ERROR_NONE; + case XML_TOK_PROLOG_S: + if (defaultHandler) + reportDefault(parser, encoding, s, next); + break; + case XML_TOK_PI: + if (!reportProcessingInstruction(parser, encoding, s, next)) + return XML_ERROR_NO_MEMORY; + break; + case XML_TOK_COMMENT: + if (!reportComment(parser, encoding, s, next)) + return XML_ERROR_NO_MEMORY; + break; + case XML_TOK_INVALID: + eventPtr = next; + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL: + if (!ps_finalBuffer) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_UNCLOSED_TOKEN; + case XML_TOK_PARTIAL_CHAR: + if (!ps_finalBuffer) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_PARTIAL_CHAR; + default: + return XML_ERROR_JUNK_AFTER_DOC_ELEMENT; + } + eventPtr = s = next; + switch (ps_parsing) { + case XML_SUSPENDED: + *nextPtr = next; + return XML_ERROR_NONE; + case XML_FINISHED: + return XML_ERROR_ABORTED; + default: ; + } + } +} + +static enum XML_Error +processInternalEntity(XML_Parser parser, ENTITY *entity, + XML_Bool betweenDecl) +{ + const char *textStart, *textEnd; + const char *next; + enum XML_Error result; + OPEN_INTERNAL_ENTITY *openEntity; + + if (freeInternalEntities) { + openEntity = freeInternalEntities; + freeInternalEntities = openEntity->next; + } + else { + openEntity = (OPEN_INTERNAL_ENTITY *)MALLOC(sizeof(OPEN_INTERNAL_ENTITY)); + if (!openEntity) + return XML_ERROR_NO_MEMORY; + } + entity->open = XML_TRUE; + entity->processed = 0; + openEntity->next = openInternalEntities; + openInternalEntities = openEntity; + openEntity->entity = entity; + openEntity->startTagLevel = tagLevel; + openEntity->betweenDecl = betweenDecl; + openEntity->internalEventPtr = NULL; + openEntity->internalEventEndPtr = NULL; + textStart = (char *)entity->textPtr; + textEnd = (char *)(entity->textPtr + entity->textLen); + +#ifdef XML_DTD + if (entity->is_param) { + int tok = XmlPrologTok(internalEncoding, textStart, textEnd, &next); + result = doProlog(parser, internalEncoding, textStart, textEnd, tok, + next, &next, XML_FALSE); + } + else +#endif /* XML_DTD */ + result = doContent(parser, tagLevel, internalEncoding, textStart, + textEnd, &next, XML_FALSE); + + if (result == XML_ERROR_NONE) { + if (textEnd != next && ps_parsing == XML_SUSPENDED) { + entity->processed = (int)(next - textStart); + processor = internalEntityProcessor; + } + else { + entity->open = XML_FALSE; + openInternalEntities = openEntity->next; + /* put openEntity back in list of free instances */ + openEntity->next = freeInternalEntities; + freeInternalEntities = openEntity; + } + } + return result; +} + +static enum XML_Error PTRCALL +internalEntityProcessor(XML_Parser parser, + const char *s, + const char *end, + const char **nextPtr) +{ + ENTITY *entity; + const char *textStart, *textEnd; + const char *next; + enum XML_Error result; + OPEN_INTERNAL_ENTITY *openEntity = openInternalEntities; + if (!openEntity) + return XML_ERROR_UNEXPECTED_STATE; + + entity = openEntity->entity; + textStart = ((char *)entity->textPtr) + entity->processed; + textEnd = (char *)(entity->textPtr + entity->textLen); + +#ifdef XML_DTD + if (entity->is_param) { + int tok = XmlPrologTok(internalEncoding, textStart, textEnd, &next); + result = doProlog(parser, internalEncoding, textStart, textEnd, tok, + next, &next, XML_FALSE); + } + else +#endif /* XML_DTD */ + result = doContent(parser, openEntity->startTagLevel, internalEncoding, + textStart, textEnd, &next, XML_FALSE); + + if (result != XML_ERROR_NONE) + return result; + else if (textEnd != next && ps_parsing == XML_SUSPENDED) { + entity->processed = (int)(next - (char *)entity->textPtr); + return result; + } + else { + entity->open = XML_FALSE; + openInternalEntities = openEntity->next; + /* put openEntity back in list of free instances */ + openEntity->next = freeInternalEntities; + freeInternalEntities = openEntity; + } + +#ifdef XML_DTD + if (entity->is_param) { + int tok; + processor = prologProcessor; + tok = XmlPrologTok(encoding, s, end, &next); + return doProlog(parser, encoding, s, end, tok, next, nextPtr, + (XML_Bool)!ps_finalBuffer); + } + else +#endif /* XML_DTD */ + { + processor = contentProcessor; + /* see externalEntityContentProcessor vs contentProcessor */ + return doContent(parser, parentParser ? 1 : 0, encoding, s, end, + nextPtr, (XML_Bool)!ps_finalBuffer); + } +} + +static enum XML_Error PTRCALL +errorProcessor(XML_Parser parser, + const char *UNUSED_P(s), + const char *UNUSED_P(end), + const char **UNUSED_P(nextPtr)) +{ + return errorCode; +} + +static enum XML_Error +storeAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata, + const char *ptr, const char *end, + STRING_POOL *pool) +{ + enum XML_Error result = appendAttributeValue(parser, enc, isCdata, ptr, + end, pool); + if (result) + return result; + if (!isCdata && poolLength(pool) && poolLastChar(pool) == 0x20) + poolChop(pool); + if (!poolAppendChar(pool, XML_T('\0'))) + return XML_ERROR_NO_MEMORY; + return XML_ERROR_NONE; +} + +static enum XML_Error +appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata, + const char *ptr, const char *end, + STRING_POOL *pool) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + for (;;) { + const char *next; + int tok = XmlAttributeValueTok(enc, ptr, end, &next); + switch (tok) { + case XML_TOK_NONE: + return XML_ERROR_NONE; + case XML_TOK_INVALID: + if (enc == encoding) + eventPtr = next; + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL: + if (enc == encoding) + eventPtr = ptr; + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_CHAR_REF: + { + XML_Char buf[XML_ENCODE_MAX]; + int i; + int n = XmlCharRefNumber(enc, ptr); + if (n < 0) { + if (enc == encoding) + eventPtr = ptr; + return XML_ERROR_BAD_CHAR_REF; + } + if (!isCdata + && n == 0x20 /* space */ + && (poolLength(pool) == 0 || poolLastChar(pool) == 0x20)) + break; + n = XmlEncode(n, (ICHAR *)buf); + if (!n) { + if (enc == encoding) + eventPtr = ptr; + return XML_ERROR_BAD_CHAR_REF; + } + for (i = 0; i < n; i++) { + if (!poolAppendChar(pool, buf[i])) + return XML_ERROR_NO_MEMORY; + } + } + break; + case XML_TOK_DATA_CHARS: + if (!poolAppend(pool, enc, ptr, next)) + return XML_ERROR_NO_MEMORY; + break; + case XML_TOK_TRAILING_CR: + next = ptr + enc->minBytesPerChar; + /* fall through */ + case XML_TOK_ATTRIBUTE_VALUE_S: + case XML_TOK_DATA_NEWLINE: + if (!isCdata && (poolLength(pool) == 0 || poolLastChar(pool) == 0x20)) + break; + if (!poolAppendChar(pool, 0x20)) + return XML_ERROR_NO_MEMORY; + break; + case XML_TOK_ENTITY_REF: + { + const XML_Char *name; + ENTITY *entity; + char checkEntityDecl; + XML_Char ch = (XML_Char) XmlPredefinedEntityName(enc, + ptr + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (ch) { + if (!poolAppendChar(pool, ch)) + return XML_ERROR_NO_MEMORY; + break; + } + name = poolStoreString(&temp2Pool, enc, + ptr + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!name) + return XML_ERROR_NO_MEMORY; + entity = (ENTITY *)lookup(parser, &dtd->generalEntities, name, 0); + poolDiscard(&temp2Pool); + /* First, determine if a check for an existing declaration is needed; + if yes, check that the entity exists, and that it is internal. + */ + if (pool == &dtd->pool) /* are we called from prolog? */ + checkEntityDecl = +#ifdef XML_DTD + prologState.documentEntity && +#endif /* XML_DTD */ + (dtd->standalone + ? !openInternalEntities + : !dtd->hasParamEntityRefs); + else /* if (pool == &tempPool): we are called from content */ + checkEntityDecl = !dtd->hasParamEntityRefs || dtd->standalone; + if (checkEntityDecl) { + if (!entity) + return XML_ERROR_UNDEFINED_ENTITY; + else if (!entity->is_internal) + return XML_ERROR_ENTITY_DECLARED_IN_PE; + } + else if (!entity) { + /* Cannot report skipped entity here - see comments on + skippedEntityHandler. + if (skippedEntityHandler) + skippedEntityHandler(handlerArg, name, 0); + */ + /* Cannot call the default handler because this would be + out of sync with the call to the startElementHandler. + if ((pool == &tempPool) && defaultHandler) + reportDefault(parser, enc, ptr, next); + */ + break; + } + if (entity->open) { + if (enc == encoding) + eventPtr = ptr; + return XML_ERROR_RECURSIVE_ENTITY_REF; + } + if (entity->notation) { + if (enc == encoding) + eventPtr = ptr; + return XML_ERROR_BINARY_ENTITY_REF; + } + if (!entity->textPtr) { + if (enc == encoding) + eventPtr = ptr; + return XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF; + } + else { + enum XML_Error result; + const XML_Char *textEnd = entity->textPtr + entity->textLen; + entity->open = XML_TRUE; + result = appendAttributeValue(parser, internalEncoding, isCdata, + (char *)entity->textPtr, + (char *)textEnd, pool); + entity->open = XML_FALSE; + if (result) + return result; + } + } + break; + default: + if (enc == encoding) + eventPtr = ptr; + return XML_ERROR_UNEXPECTED_STATE; + } + ptr = next; + } + /* not reached */ +} + +static enum XML_Error +storeEntityValue(XML_Parser parser, + const ENCODING *enc, + const char *entityTextPtr, + const char *entityTextEnd) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + STRING_POOL *pool = &(dtd->entityValuePool); + enum XML_Error result = XML_ERROR_NONE; +#ifdef XML_DTD + int oldInEntityValue = prologState.inEntityValue; + prologState.inEntityValue = 1; +#endif /* XML_DTD */ + /* never return Null for the value argument in EntityDeclHandler, + since this would indicate an external entity; therefore we + have to make sure that entityValuePool.start is not null */ + if (!pool->blocks) { + if (!poolGrow(pool)) + return XML_ERROR_NO_MEMORY; + } + + for (;;) { + const char *next; + int tok = XmlEntityValueTok(enc, entityTextPtr, entityTextEnd, &next); + switch (tok) { + case XML_TOK_PARAM_ENTITY_REF: +#ifdef XML_DTD + if (isParamEntity || enc != encoding) { + const XML_Char *name; + ENTITY *entity; + name = poolStoreString(&tempPool, enc, + entityTextPtr + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!name) { + result = XML_ERROR_NO_MEMORY; + goto endEntityValue; + } + entity = (ENTITY *)lookup(parser, &dtd->paramEntities, name, 0); + poolDiscard(&tempPool); + if (!entity) { + /* not a well-formedness error - see XML 1.0: WFC Entity Declared */ + /* cannot report skipped entity here - see comments on + skippedEntityHandler + if (skippedEntityHandler) + skippedEntityHandler(handlerArg, name, 0); + */ + dtd->keepProcessing = dtd->standalone; + goto endEntityValue; + } + if (entity->open) { + if (enc == encoding) + eventPtr = entityTextPtr; + result = XML_ERROR_RECURSIVE_ENTITY_REF; + goto endEntityValue; + } + if (entity->systemId) { + if (externalEntityRefHandler) { + dtd->paramEntityRead = XML_FALSE; + entity->open = XML_TRUE; + if (!externalEntityRefHandler(externalEntityRefHandlerArg, + 0, + entity->base, + entity->systemId, + entity->publicId)) { + entity->open = XML_FALSE; + result = XML_ERROR_EXTERNAL_ENTITY_HANDLING; + goto endEntityValue; + } + entity->open = XML_FALSE; + if (!dtd->paramEntityRead) + dtd->keepProcessing = dtd->standalone; + } + else + dtd->keepProcessing = dtd->standalone; + } + else { + entity->open = XML_TRUE; + result = storeEntityValue(parser, + internalEncoding, + (char *)entity->textPtr, + (char *)(entity->textPtr + + entity->textLen)); + entity->open = XML_FALSE; + if (result) + goto endEntityValue; + } + break; + } +#endif /* XML_DTD */ + /* In the internal subset, PE references are not legal + within markup declarations, e.g entity values in this case. */ + eventPtr = entityTextPtr; + result = XML_ERROR_PARAM_ENTITY_REF; + goto endEntityValue; + case XML_TOK_NONE: + result = XML_ERROR_NONE; + goto endEntityValue; + case XML_TOK_ENTITY_REF: + case XML_TOK_DATA_CHARS: + if (!poolAppend(pool, enc, entityTextPtr, next)) { + result = XML_ERROR_NO_MEMORY; + goto endEntityValue; + } + break; + case XML_TOK_TRAILING_CR: + next = entityTextPtr + enc->minBytesPerChar; + /* fall through */ + case XML_TOK_DATA_NEWLINE: + if (pool->end == pool->ptr && !poolGrow(pool)) { + result = XML_ERROR_NO_MEMORY; + goto endEntityValue; + } + *(pool->ptr)++ = 0xA; + break; + case XML_TOK_CHAR_REF: + { + XML_Char buf[XML_ENCODE_MAX]; + int i; + int n = XmlCharRefNumber(enc, entityTextPtr); + if (n < 0) { + if (enc == encoding) + eventPtr = entityTextPtr; + result = XML_ERROR_BAD_CHAR_REF; + goto endEntityValue; + } + n = XmlEncode(n, (ICHAR *)buf); + if (!n) { + if (enc == encoding) + eventPtr = entityTextPtr; + result = XML_ERROR_BAD_CHAR_REF; + goto endEntityValue; + } + for (i = 0; i < n; i++) { + if (pool->end == pool->ptr && !poolGrow(pool)) { + result = XML_ERROR_NO_MEMORY; + goto endEntityValue; + } + *(pool->ptr)++ = buf[i]; + } + } + break; + case XML_TOK_PARTIAL: + if (enc == encoding) + eventPtr = entityTextPtr; + result = XML_ERROR_INVALID_TOKEN; + goto endEntityValue; + case XML_TOK_INVALID: + if (enc == encoding) + eventPtr = next; + result = XML_ERROR_INVALID_TOKEN; + goto endEntityValue; + default: + if (enc == encoding) + eventPtr = entityTextPtr; + result = XML_ERROR_UNEXPECTED_STATE; + goto endEntityValue; + } + entityTextPtr = next; + } +endEntityValue: +#ifdef XML_DTD + prologState.inEntityValue = oldInEntityValue; +#endif /* XML_DTD */ + return result; +} + +static void FASTCALL +normalizeLines(XML_Char *s) +{ + XML_Char *p; + for (;; s++) { + if (*s == XML_T('\0')) + return; + if (*s == 0xD) + break; + } + p = s; + do { + if (*s == 0xD) { + *p++ = 0xA; + if (*++s == 0xA) + s++; + } + else + *p++ = *s++; + } while (*s); + *p = XML_T('\0'); +} + +static int +reportProcessingInstruction(XML_Parser parser, const ENCODING *enc, + const char *start, const char *end) +{ + const XML_Char *target; + XML_Char *data; + const char *tem; + if (!processingInstructionHandler) { + if (defaultHandler) + reportDefault(parser, enc, start, end); + return 1; + } + start += enc->minBytesPerChar * 2; + tem = start + XmlNameLength(enc, start); + target = poolStoreString(&tempPool, enc, start, tem); + if (!target) + return 0; + poolFinish(&tempPool); + data = poolStoreString(&tempPool, enc, + XmlSkipS(enc, tem), + end - enc->minBytesPerChar*2); + if (!data) + return 0; + normalizeLines(data); + processingInstructionHandler(handlerArg, target, data); + poolClear(&tempPool); + return 1; +} + +static int +reportComment(XML_Parser parser, const ENCODING *enc, + const char *start, const char *end) +{ + XML_Char *data; + if (!commentHandler) { + if (defaultHandler) + reportDefault(parser, enc, start, end); + return 1; + } + data = poolStoreString(&tempPool, + enc, + start + enc->minBytesPerChar * 4, + end - enc->minBytesPerChar * 3); + if (!data) + return 0; + normalizeLines(data); + commentHandler(handlerArg, data); + poolClear(&tempPool); + return 1; +} + +static void +reportDefault(XML_Parser parser, const ENCODING *enc, + const char *s, const char *end) +{ + if (MUST_CONVERT(enc, s)) { + enum XML_Convert_Result convert_res; + const char **eventPP; + const char **eventEndPP; + if (enc == encoding) { + eventPP = &eventPtr; + eventEndPP = &eventEndPtr; + } + else { + eventPP = &(openInternalEntities->internalEventPtr); + eventEndPP = &(openInternalEntities->internalEventEndPtr); + } + do { + ICHAR *dataPtr = (ICHAR *)dataBuf; + convert_res = XmlConvert(enc, &s, end, &dataPtr, (ICHAR *)dataBufEnd); + *eventEndPP = s; + defaultHandler(handlerArg, dataBuf, (int)(dataPtr - (ICHAR *)dataBuf)); + *eventPP = s; + } while ((convert_res != XML_CONVERT_COMPLETED) && (convert_res != XML_CONVERT_INPUT_INCOMPLETE)); + } + else + defaultHandler(handlerArg, (XML_Char *)s, (int)((XML_Char *)end - (XML_Char *)s)); +} + + +static int +defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *attId, XML_Bool isCdata, + XML_Bool isId, const XML_Char *value, XML_Parser parser) +{ + DEFAULT_ATTRIBUTE *att; + if (value || isId) { + /* The handling of default attributes gets messed up if we have + a default which duplicates a non-default. */ + int i; + for (i = 0; i < type->nDefaultAtts; i++) + if (attId == type->defaultAtts[i].id) + return 1; + if (isId && !type->idAtt && !attId->xmlns) + type->idAtt = attId; + } + if (type->nDefaultAtts == type->allocDefaultAtts) { + if (type->allocDefaultAtts == 0) { + type->allocDefaultAtts = 8; + type->defaultAtts = (DEFAULT_ATTRIBUTE *)MALLOC(type->allocDefaultAtts + * sizeof(DEFAULT_ATTRIBUTE)); + if (!type->defaultAtts) + return 0; + } + else { + DEFAULT_ATTRIBUTE *temp; + int count = type->allocDefaultAtts * 2; + temp = (DEFAULT_ATTRIBUTE *) + REALLOC(type->defaultAtts, (count * sizeof(DEFAULT_ATTRIBUTE))); + if (temp == NULL) + return 0; + type->allocDefaultAtts = count; + type->defaultAtts = temp; + } + } + att = type->defaultAtts + type->nDefaultAtts; + att->id = attId; + att->value = value; + att->isCdata = isCdata; + if (!isCdata) + attId->maybeTokenized = XML_TRUE; + type->nDefaultAtts += 1; + return 1; +} + +static int +setElementTypePrefix(XML_Parser parser, ELEMENT_TYPE *elementType) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + const XML_Char *name; + for (name = elementType->name; *name; name++) { + if (*name == XML_T(ASCII_COLON)) { + PREFIX *prefix; + const XML_Char *s; + for (s = elementType->name; s != name; s++) { + if (!poolAppendChar(&dtd->pool, *s)) + return 0; + } + if (!poolAppendChar(&dtd->pool, XML_T('\0'))) + return 0; + prefix = (PREFIX *)lookup(parser, &dtd->prefixes, poolStart(&dtd->pool), + sizeof(PREFIX)); + if (!prefix) + return 0; + if (prefix->name == poolStart(&dtd->pool)) + poolFinish(&dtd->pool); + else + poolDiscard(&dtd->pool); + elementType->prefix = prefix; + + } + } + return 1; +} + +static ATTRIBUTE_ID * +getAttributeId(XML_Parser parser, const ENCODING *enc, + const char *start, const char *end) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + ATTRIBUTE_ID *id; + const XML_Char *name; + if (!poolAppendChar(&dtd->pool, XML_T('\0'))) + return NULL; + name = poolStoreString(&dtd->pool, enc, start, end); + if (!name) + return NULL; + /* skip quotation mark - its storage will be re-used (like in name[-1]) */ + ++name; + id = (ATTRIBUTE_ID *)lookup(parser, &dtd->attributeIds, name, sizeof(ATTRIBUTE_ID)); + if (!id) + return NULL; + if (id->name != name) + poolDiscard(&dtd->pool); + else { + poolFinish(&dtd->pool); + if (!ns) + ; + else if (name[0] == XML_T(ASCII_x) + && name[1] == XML_T(ASCII_m) + && name[2] == XML_T(ASCII_l) + && name[3] == XML_T(ASCII_n) + && name[4] == XML_T(ASCII_s) + && (name[5] == XML_T('\0') || name[5] == XML_T(ASCII_COLON))) { + if (name[5] == XML_T('\0')) + id->prefix = &dtd->defaultPrefix; + else + id->prefix = (PREFIX *)lookup(parser, &dtd->prefixes, name + 6, sizeof(PREFIX)); + id->xmlns = XML_TRUE; + } + else { + int i; + for (i = 0; name[i]; i++) { + /* attributes without prefix are *not* in the default namespace */ + if (name[i] == XML_T(ASCII_COLON)) { + int j; + for (j = 0; j < i; j++) { + if (!poolAppendChar(&dtd->pool, name[j])) + return NULL; + } + if (!poolAppendChar(&dtd->pool, XML_T('\0'))) + return NULL; + id->prefix = (PREFIX *)lookup(parser, &dtd->prefixes, poolStart(&dtd->pool), + sizeof(PREFIX)); + if (!id->prefix) + return NULL; + if (id->prefix->name == poolStart(&dtd->pool)) + poolFinish(&dtd->pool); + else + poolDiscard(&dtd->pool); + break; + } + } + } + } + return id; +} + +#define CONTEXT_SEP XML_T(ASCII_FF) + +static const XML_Char * +getContext(XML_Parser parser) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + HASH_TABLE_ITER iter; + XML_Bool needSep = XML_FALSE; + + if (dtd->defaultPrefix.binding) { + int i; + int len; + if (!poolAppendChar(&tempPool, XML_T(ASCII_EQUALS))) + return NULL; + len = dtd->defaultPrefix.binding->uriLen; + if (namespaceSeparator) + len--; + for (i = 0; i < len; i++) + if (!poolAppendChar(&tempPool, dtd->defaultPrefix.binding->uri[i])) + return NULL; + needSep = XML_TRUE; + } + + hashTableIterInit(&iter, &(dtd->prefixes)); + for (;;) { + int i; + int len; + const XML_Char *s; + PREFIX *prefix = (PREFIX *)hashTableIterNext(&iter); + if (!prefix) + break; + if (!prefix->binding) + continue; + if (needSep && !poolAppendChar(&tempPool, CONTEXT_SEP)) + return NULL; + for (s = prefix->name; *s; s++) + if (!poolAppendChar(&tempPool, *s)) + return NULL; + if (!poolAppendChar(&tempPool, XML_T(ASCII_EQUALS))) + return NULL; + len = prefix->binding->uriLen; + if (namespaceSeparator) + len--; + for (i = 0; i < len; i++) + if (!poolAppendChar(&tempPool, prefix->binding->uri[i])) + return NULL; + needSep = XML_TRUE; + } + + + hashTableIterInit(&iter, &(dtd->generalEntities)); + for (;;) { + const XML_Char *s; + ENTITY *e = (ENTITY *)hashTableIterNext(&iter); + if (!e) + break; + if (!e->open) + continue; + if (needSep && !poolAppendChar(&tempPool, CONTEXT_SEP)) + return NULL; + for (s = e->name; *s; s++) + if (!poolAppendChar(&tempPool, *s)) + return 0; + needSep = XML_TRUE; + } + + if (!poolAppendChar(&tempPool, XML_T('\0'))) + return NULL; + return tempPool.start; +} + +static XML_Bool +setContext(XML_Parser parser, const XML_Char *context) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + const XML_Char *s = context; + + while (*context != XML_T('\0')) { + if (*s == CONTEXT_SEP || *s == XML_T('\0')) { + ENTITY *e; + if (!poolAppendChar(&tempPool, XML_T('\0'))) + return XML_FALSE; + e = (ENTITY *)lookup(parser, &dtd->generalEntities, poolStart(&tempPool), 0); + if (e) + e->open = XML_TRUE; + if (*s != XML_T('\0')) + s++; + context = s; + poolDiscard(&tempPool); + } + else if (*s == XML_T(ASCII_EQUALS)) { + PREFIX *prefix; + if (poolLength(&tempPool) == 0) + prefix = &dtd->defaultPrefix; + else { + if (!poolAppendChar(&tempPool, XML_T('\0'))) + return XML_FALSE; + prefix = (PREFIX *)lookup(parser, &dtd->prefixes, poolStart(&tempPool), + sizeof(PREFIX)); + if (!prefix) + return XML_FALSE; + if (prefix->name == poolStart(&tempPool)) { + prefix->name = poolCopyString(&dtd->pool, prefix->name); + if (!prefix->name) + return XML_FALSE; + } + poolDiscard(&tempPool); + } + for (context = s + 1; + *context != CONTEXT_SEP && *context != XML_T('\0'); + context++) + if (!poolAppendChar(&tempPool, *context)) + return XML_FALSE; + if (!poolAppendChar(&tempPool, XML_T('\0'))) + return XML_FALSE; + if (addBinding(parser, prefix, NULL, poolStart(&tempPool), + &inheritedBindings) != XML_ERROR_NONE) + return XML_FALSE; + poolDiscard(&tempPool); + if (*context != XML_T('\0')) + ++context; + s = context; + } + else { + if (!poolAppendChar(&tempPool, *s)) + return XML_FALSE; + s++; + } + } + return XML_TRUE; +} + +static void FASTCALL +normalizePublicId(XML_Char *publicId) +{ + XML_Char *p = publicId; + XML_Char *s; + for (s = publicId; *s; s++) { + switch (*s) { + case 0x20: + case 0xD: + case 0xA: + if (p != publicId && p[-1] != 0x20) + *p++ = 0x20; + break; + default: + *p++ = *s; + } + } + if (p != publicId && p[-1] == 0x20) + --p; + *p = XML_T('\0'); +} + +static DTD * +dtdCreate(const XML_Memory_Handling_Suite *ms) +{ + DTD *p = (DTD *)ms->malloc_fcn(sizeof(DTD)); + if (p == NULL) + return p; + poolInit(&(p->pool), ms); + poolInit(&(p->entityValuePool), ms); + hashTableInit(&(p->generalEntities), ms); + hashTableInit(&(p->elementTypes), ms); + hashTableInit(&(p->attributeIds), ms); + hashTableInit(&(p->prefixes), ms); +#ifdef XML_DTD + p->paramEntityRead = XML_FALSE; + hashTableInit(&(p->paramEntities), ms); +#endif /* XML_DTD */ + p->defaultPrefix.name = NULL; + p->defaultPrefix.binding = NULL; + + p->in_eldecl = XML_FALSE; + p->scaffIndex = NULL; + p->scaffold = NULL; + p->scaffLevel = 0; + p->scaffSize = 0; + p->scaffCount = 0; + p->contentStringLen = 0; + + p->keepProcessing = XML_TRUE; + p->hasParamEntityRefs = XML_FALSE; + p->standalone = XML_FALSE; + return p; +} + +static void +dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms) +{ + HASH_TABLE_ITER iter; + hashTableIterInit(&iter, &(p->elementTypes)); + for (;;) { + ELEMENT_TYPE *e = (ELEMENT_TYPE *)hashTableIterNext(&iter); + if (!e) + break; + if (e->allocDefaultAtts != 0) + ms->free_fcn(e->defaultAtts); + } + hashTableClear(&(p->generalEntities)); +#ifdef XML_DTD + p->paramEntityRead = XML_FALSE; + hashTableClear(&(p->paramEntities)); +#endif /* XML_DTD */ + hashTableClear(&(p->elementTypes)); + hashTableClear(&(p->attributeIds)); + hashTableClear(&(p->prefixes)); + poolClear(&(p->pool)); + poolClear(&(p->entityValuePool)); + p->defaultPrefix.name = NULL; + p->defaultPrefix.binding = NULL; + + p->in_eldecl = XML_FALSE; + + ms->free_fcn(p->scaffIndex); + p->scaffIndex = NULL; + ms->free_fcn(p->scaffold); + p->scaffold = NULL; + + p->scaffLevel = 0; + p->scaffSize = 0; + p->scaffCount = 0; + p->contentStringLen = 0; + + p->keepProcessing = XML_TRUE; + p->hasParamEntityRefs = XML_FALSE; + p->standalone = XML_FALSE; +} + +static void +dtdDestroy(DTD *p, XML_Bool isDocEntity, const XML_Memory_Handling_Suite *ms) +{ + HASH_TABLE_ITER iter; + hashTableIterInit(&iter, &(p->elementTypes)); + for (;;) { + ELEMENT_TYPE *e = (ELEMENT_TYPE *)hashTableIterNext(&iter); + if (!e) + break; + if (e->allocDefaultAtts != 0) + ms->free_fcn(e->defaultAtts); + } + hashTableDestroy(&(p->generalEntities)); +#ifdef XML_DTD + hashTableDestroy(&(p->paramEntities)); +#endif /* XML_DTD */ + hashTableDestroy(&(p->elementTypes)); + hashTableDestroy(&(p->attributeIds)); + hashTableDestroy(&(p->prefixes)); + poolDestroy(&(p->pool)); + poolDestroy(&(p->entityValuePool)); + if (isDocEntity) { + ms->free_fcn(p->scaffIndex); + ms->free_fcn(p->scaffold); + } + ms->free_fcn(p); +} + +/* Do a deep copy of the DTD. Return 0 for out of memory, non-zero otherwise. + The new DTD has already been initialized. +*/ +static int +dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd, const XML_Memory_Handling_Suite *ms) +{ + HASH_TABLE_ITER iter; + + /* Copy the prefix table. */ + + hashTableIterInit(&iter, &(oldDtd->prefixes)); + for (;;) { + const XML_Char *name; + const PREFIX *oldP = (PREFIX *)hashTableIterNext(&iter); + if (!oldP) + break; + name = poolCopyString(&(newDtd->pool), oldP->name); + if (!name) + return 0; + if (!lookup(oldParser, &(newDtd->prefixes), name, sizeof(PREFIX))) + return 0; + } + + hashTableIterInit(&iter, &(oldDtd->attributeIds)); + + /* Copy the attribute id table. */ + + for (;;) { + ATTRIBUTE_ID *newA; + const XML_Char *name; + const ATTRIBUTE_ID *oldA = (ATTRIBUTE_ID *)hashTableIterNext(&iter); + + if (!oldA) + break; + /* Remember to allocate the scratch byte before the name. */ + if (!poolAppendChar(&(newDtd->pool), XML_T('\0'))) + return 0; + name = poolCopyString(&(newDtd->pool), oldA->name); + if (!name) + return 0; + ++name; + newA = (ATTRIBUTE_ID *)lookup(oldParser, &(newDtd->attributeIds), name, + sizeof(ATTRIBUTE_ID)); + if (!newA) + return 0; + newA->maybeTokenized = oldA->maybeTokenized; + if (oldA->prefix) { + newA->xmlns = oldA->xmlns; + if (oldA->prefix == &oldDtd->defaultPrefix) + newA->prefix = &newDtd->defaultPrefix; + else + newA->prefix = (PREFIX *)lookup(oldParser, &(newDtd->prefixes), + oldA->prefix->name, 0); + } + } + + /* Copy the element type table. */ + + hashTableIterInit(&iter, &(oldDtd->elementTypes)); + + for (;;) { + int i; + ELEMENT_TYPE *newE; + const XML_Char *name; + const ELEMENT_TYPE *oldE = (ELEMENT_TYPE *)hashTableIterNext(&iter); + if (!oldE) + break; + name = poolCopyString(&(newDtd->pool), oldE->name); + if (!name) + return 0; + newE = (ELEMENT_TYPE *)lookup(oldParser, &(newDtd->elementTypes), name, + sizeof(ELEMENT_TYPE)); + if (!newE) + return 0; + if (oldE->nDefaultAtts) { + newE->defaultAtts = (DEFAULT_ATTRIBUTE *) + ms->malloc_fcn(oldE->nDefaultAtts * sizeof(DEFAULT_ATTRIBUTE)); + if (!newE->defaultAtts) { + ms->free_fcn(newE); + return 0; + } + } + if (oldE->idAtt) + newE->idAtt = (ATTRIBUTE_ID *) + lookup(oldParser, &(newDtd->attributeIds), oldE->idAtt->name, 0); + newE->allocDefaultAtts = newE->nDefaultAtts = oldE->nDefaultAtts; + if (oldE->prefix) + newE->prefix = (PREFIX *)lookup(oldParser, &(newDtd->prefixes), + oldE->prefix->name, 0); + for (i = 0; i < newE->nDefaultAtts; i++) { + newE->defaultAtts[i].id = (ATTRIBUTE_ID *) + lookup(oldParser, &(newDtd->attributeIds), oldE->defaultAtts[i].id->name, 0); + newE->defaultAtts[i].isCdata = oldE->defaultAtts[i].isCdata; + if (oldE->defaultAtts[i].value) { + newE->defaultAtts[i].value + = poolCopyString(&(newDtd->pool), oldE->defaultAtts[i].value); + if (!newE->defaultAtts[i].value) + return 0; + } + else + newE->defaultAtts[i].value = NULL; + } + } + + /* Copy the entity tables. */ + if (!copyEntityTable(oldParser, + &(newDtd->generalEntities), + &(newDtd->pool), + &(oldDtd->generalEntities))) + return 0; + +#ifdef XML_DTD + if (!copyEntityTable(oldParser, + &(newDtd->paramEntities), + &(newDtd->pool), + &(oldDtd->paramEntities))) + return 0; + newDtd->paramEntityRead = oldDtd->paramEntityRead; +#endif /* XML_DTD */ + + newDtd->keepProcessing = oldDtd->keepProcessing; + newDtd->hasParamEntityRefs = oldDtd->hasParamEntityRefs; + newDtd->standalone = oldDtd->standalone; + + /* Don't want deep copying for scaffolding */ + newDtd->in_eldecl = oldDtd->in_eldecl; + newDtd->scaffold = oldDtd->scaffold; + newDtd->contentStringLen = oldDtd->contentStringLen; + newDtd->scaffSize = oldDtd->scaffSize; + newDtd->scaffLevel = oldDtd->scaffLevel; + newDtd->scaffIndex = oldDtd->scaffIndex; + + return 1; +} /* End dtdCopy */ + +static int +copyEntityTable(XML_Parser oldParser, + HASH_TABLE *newTable, + STRING_POOL *newPool, + const HASH_TABLE *oldTable) +{ + HASH_TABLE_ITER iter; + const XML_Char *cachedOldBase = NULL; + const XML_Char *cachedNewBase = NULL; + + hashTableIterInit(&iter, oldTable); + + for (;;) { + ENTITY *newE; + const XML_Char *name; + const ENTITY *oldE = (ENTITY *)hashTableIterNext(&iter); + if (!oldE) + break; + name = poolCopyString(newPool, oldE->name); + if (!name) + return 0; + newE = (ENTITY *)lookup(oldParser, newTable, name, sizeof(ENTITY)); + if (!newE) + return 0; + if (oldE->systemId) { + const XML_Char *tem = poolCopyString(newPool, oldE->systemId); + if (!tem) + return 0; + newE->systemId = tem; + if (oldE->base) { + if (oldE->base == cachedOldBase) + newE->base = cachedNewBase; + else { + cachedOldBase = oldE->base; + tem = poolCopyString(newPool, cachedOldBase); + if (!tem) + return 0; + cachedNewBase = newE->base = tem; + } + } + if (oldE->publicId) { + tem = poolCopyString(newPool, oldE->publicId); + if (!tem) + return 0; + newE->publicId = tem; + } + } + else { + const XML_Char *tem = poolCopyStringN(newPool, oldE->textPtr, + oldE->textLen); + if (!tem) + return 0; + newE->textPtr = tem; + newE->textLen = oldE->textLen; + } + if (oldE->notation) { + const XML_Char *tem = poolCopyString(newPool, oldE->notation); + if (!tem) + return 0; + newE->notation = tem; + } + newE->is_param = oldE->is_param; + newE->is_internal = oldE->is_internal; + } + return 1; +} + +#define INIT_POWER 6 + +static XML_Bool FASTCALL +keyeq(KEY s1, KEY s2) +{ + for (; *s1 == *s2; s1++, s2++) + if (*s1 == 0) + return XML_TRUE; + return XML_FALSE; +} + +static unsigned long FASTCALL +hash(XML_Parser parser, KEY s) +{ + unsigned long h = hash_secret_salt; + while (*s) + h = CHAR_HASH(h, *s++); + return h; +} + +static NAMED * +lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize) +{ + size_t i; + if (table->size == 0) { + size_t tsize; + if (!createSize) + return NULL; + table->power = INIT_POWER; + /* table->size is a power of 2 */ + table->size = (size_t)1 << INIT_POWER; + tsize = table->size * sizeof(NAMED *); + table->v = (NAMED **)table->mem->malloc_fcn(tsize); + if (!table->v) { + table->size = 0; + return NULL; + } + memset(table->v, 0, tsize); + i = hash(parser, name) & ((unsigned long)table->size - 1); + } + else { + unsigned long h = hash(parser, name); + unsigned long mask = (unsigned long)table->size - 1; + unsigned char step = 0; + i = h & mask; + while (table->v[i]) { + if (keyeq(name, table->v[i]->name)) + return table->v[i]; + if (!step) + step = PROBE_STEP(h, mask, table->power); + i < step ? (i += table->size - step) : (i -= step); + } + if (!createSize) + return NULL; + + /* check for overflow (table is half full) */ + if (table->used >> (table->power - 1)) { + unsigned char newPower = table->power + 1; + size_t newSize = (size_t)1 << newPower; + unsigned long newMask = (unsigned long)newSize - 1; + size_t tsize = newSize * sizeof(NAMED *); + NAMED **newV = (NAMED **)table->mem->malloc_fcn(tsize); + if (!newV) + return NULL; + memset(newV, 0, tsize); + for (i = 0; i < table->size; i++) + if (table->v[i]) { + unsigned long newHash = hash(parser, table->v[i]->name); + size_t j = newHash & newMask; + step = 0; + while (newV[j]) { + if (!step) + step = PROBE_STEP(newHash, newMask, newPower); + j < step ? (j += newSize - step) : (j -= step); + } + newV[j] = table->v[i]; + } + table->mem->free_fcn(table->v); + table->v = newV; + table->power = newPower; + table->size = newSize; + i = h & newMask; + step = 0; + while (table->v[i]) { + if (!step) + step = PROBE_STEP(h, newMask, newPower); + i < step ? (i += newSize - step) : (i -= step); + } + } + } + table->v[i] = (NAMED *)table->mem->malloc_fcn(createSize); + if (!table->v[i]) + return NULL; + memset(table->v[i], 0, createSize); + table->v[i]->name = name; + (table->used)++; + return table->v[i]; +} + +static void FASTCALL +hashTableClear(HASH_TABLE *table) +{ + size_t i; + for (i = 0; i < table->size; i++) { + table->mem->free_fcn(table->v[i]); + table->v[i] = NULL; + } + table->used = 0; +} + +static void FASTCALL +hashTableDestroy(HASH_TABLE *table) +{ + size_t i; + for (i = 0; i < table->size; i++) + table->mem->free_fcn(table->v[i]); + table->mem->free_fcn(table->v); +} + +static void FASTCALL +hashTableInit(HASH_TABLE *p, const XML_Memory_Handling_Suite *ms) +{ + p->power = 0; + p->size = 0; + p->used = 0; + p->v = NULL; + p->mem = ms; +} + +static void FASTCALL +hashTableIterInit(HASH_TABLE_ITER *iter, const HASH_TABLE *table) +{ + iter->p = table->v; + iter->end = iter->p + table->size; +} + +static NAMED * FASTCALL +hashTableIterNext(HASH_TABLE_ITER *iter) +{ + while (iter->p != iter->end) { + NAMED *tem = *(iter->p)++; + if (tem) + return tem; + } + return NULL; +} + +static void FASTCALL +poolInit(STRING_POOL *pool, const XML_Memory_Handling_Suite *ms) +{ + pool->blocks = NULL; + pool->freeBlocks = NULL; + pool->start = NULL; + pool->ptr = NULL; + pool->end = NULL; + pool->mem = ms; +} + +static void FASTCALL +poolClear(STRING_POOL *pool) +{ + if (!pool->freeBlocks) + pool->freeBlocks = pool->blocks; + else { + BLOCK *p = pool->blocks; + while (p) { + BLOCK *tem = p->next; + p->next = pool->freeBlocks; + pool->freeBlocks = p; + p = tem; + } + } + pool->blocks = NULL; + pool->start = NULL; + pool->ptr = NULL; + pool->end = NULL; +} + +static void FASTCALL +poolDestroy(STRING_POOL *pool) +{ + BLOCK *p = pool->blocks; + while (p) { + BLOCK *tem = p->next; + pool->mem->free_fcn(p); + p = tem; + } + p = pool->freeBlocks; + while (p) { + BLOCK *tem = p->next; + pool->mem->free_fcn(p); + p = tem; + } +} + +static XML_Char * +poolAppend(STRING_POOL *pool, const ENCODING *enc, + const char *ptr, const char *end) +{ + if (!pool->ptr && !poolGrow(pool)) + return NULL; + for (;;) { + const enum XML_Convert_Result convert_res = XmlConvert(enc, &ptr, end, (ICHAR **)&(pool->ptr), (ICHAR *)pool->end); + if ((convert_res == XML_CONVERT_COMPLETED) || (convert_res == XML_CONVERT_INPUT_INCOMPLETE)) + break; + if (!poolGrow(pool)) + return NULL; + } + return pool->start; +} + +static const XML_Char * FASTCALL +poolCopyString(STRING_POOL *pool, const XML_Char *s) +{ + do { + if (!poolAppendChar(pool, *s)) + return NULL; + } while (*s++); + s = pool->start; + poolFinish(pool); + return s; +} + +static const XML_Char * +poolCopyStringN(STRING_POOL *pool, const XML_Char *s, int n) +{ + if (!pool->ptr && !poolGrow(pool)) + return NULL; + for (; n > 0; --n, s++) { + if (!poolAppendChar(pool, *s)) + return NULL; + } + s = pool->start; + poolFinish(pool); + return s; +} + +static const XML_Char * FASTCALL +poolAppendString(STRING_POOL *pool, const XML_Char *s) +{ + while (*s) { + if (!poolAppendChar(pool, *s)) + return NULL; + s++; + } + return pool->start; +} + +static XML_Char * +poolStoreString(STRING_POOL *pool, const ENCODING *enc, + const char *ptr, const char *end) +{ + if (!poolAppend(pool, enc, ptr, end)) + return NULL; + if (pool->ptr == pool->end && !poolGrow(pool)) + return NULL; + *(pool->ptr)++ = 0; + return pool->start; +} + +static XML_Bool FASTCALL +poolGrow(STRING_POOL *pool) +{ + if (pool->freeBlocks) { + if (pool->start == 0) { + pool->blocks = pool->freeBlocks; + pool->freeBlocks = pool->freeBlocks->next; + pool->blocks->next = NULL; + pool->start = pool->blocks->s; + pool->end = pool->start + pool->blocks->size; + pool->ptr = pool->start; + return XML_TRUE; + } + if (pool->end - pool->start < pool->freeBlocks->size) { + BLOCK *tem = pool->freeBlocks->next; + pool->freeBlocks->next = pool->blocks; + pool->blocks = pool->freeBlocks; + pool->freeBlocks = tem; + memcpy(pool->blocks->s, pool->start, + (pool->end - pool->start) * sizeof(XML_Char)); + pool->ptr = pool->blocks->s + (pool->ptr - pool->start); + pool->start = pool->blocks->s; + pool->end = pool->start + pool->blocks->size; + return XML_TRUE; + } + } + if (pool->blocks && pool->start == pool->blocks->s) { + BLOCK *temp; + int blockSize = (int)((unsigned)(pool->end - pool->start)*2U); + + if (blockSize < 0) + return XML_FALSE; + + temp = (BLOCK *) + pool->mem->realloc_fcn(pool->blocks, + (offsetof(BLOCK, s) + + blockSize * sizeof(XML_Char))); + if (temp == NULL) + return XML_FALSE; + pool->blocks = temp; + pool->blocks->size = blockSize; + pool->ptr = pool->blocks->s + (pool->ptr - pool->start); + pool->start = pool->blocks->s; + pool->end = pool->start + blockSize; + } + else { + BLOCK *tem; + int blockSize = (int)(pool->end - pool->start); + + if (blockSize < 0) + return XML_FALSE; + + if (blockSize < INIT_BLOCK_SIZE) + blockSize = INIT_BLOCK_SIZE; + else + blockSize *= 2; + tem = (BLOCK *)pool->mem->malloc_fcn(offsetof(BLOCK, s) + + blockSize * sizeof(XML_Char)); + if (!tem) + return XML_FALSE; + tem->size = blockSize; + tem->next = pool->blocks; + pool->blocks = tem; + if (pool->ptr != pool->start) + memcpy(tem->s, pool->start, + (pool->ptr - pool->start) * sizeof(XML_Char)); + pool->ptr = tem->s + (pool->ptr - pool->start); + pool->start = tem->s; + pool->end = tem->s + blockSize; + } + return XML_TRUE; +} + +static int FASTCALL +nextScaffoldPart(XML_Parser parser) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + CONTENT_SCAFFOLD * me; + int next; + + if (!dtd->scaffIndex) { + dtd->scaffIndex = (int *)MALLOC(groupSize * sizeof(int)); + if (!dtd->scaffIndex) + return -1; + dtd->scaffIndex[0] = 0; + } + + if (dtd->scaffCount >= dtd->scaffSize) { + CONTENT_SCAFFOLD *temp; + if (dtd->scaffold) { + temp = (CONTENT_SCAFFOLD *) + REALLOC(dtd->scaffold, dtd->scaffSize * 2 * sizeof(CONTENT_SCAFFOLD)); + if (temp == NULL) + return -1; + dtd->scaffSize *= 2; + } + else { + temp = (CONTENT_SCAFFOLD *)MALLOC(INIT_SCAFFOLD_ELEMENTS + * sizeof(CONTENT_SCAFFOLD)); + if (temp == NULL) + return -1; + dtd->scaffSize = INIT_SCAFFOLD_ELEMENTS; + } + dtd->scaffold = temp; + } + next = dtd->scaffCount++; + me = &dtd->scaffold[next]; + if (dtd->scaffLevel) { + CONTENT_SCAFFOLD *parent = &dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel-1]]; + if (parent->lastchild) { + dtd->scaffold[parent->lastchild].nextsib = next; + } + if (!parent->childcnt) + parent->firstchild = next; + parent->lastchild = next; + parent->childcnt++; + } + me->firstchild = me->lastchild = me->childcnt = me->nextsib = 0; + return next; +} + +static void +build_node(XML_Parser parser, + int src_node, + XML_Content *dest, + XML_Content **contpos, + XML_Char **strpos) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + dest->type = dtd->scaffold[src_node].type; + dest->quant = dtd->scaffold[src_node].quant; + if (dest->type == XML_CTYPE_NAME) { + const XML_Char *src; + dest->name = *strpos; + src = dtd->scaffold[src_node].name; + for (;;) { + *(*strpos)++ = *src; + if (!*src) + break; + src++; + } + dest->numchildren = 0; + dest->children = NULL; + } + else { + unsigned int i; + int cn; + dest->numchildren = dtd->scaffold[src_node].childcnt; + dest->children = *contpos; + *contpos += dest->numchildren; + for (i = 0, cn = dtd->scaffold[src_node].firstchild; + i < dest->numchildren; + i++, cn = dtd->scaffold[cn].nextsib) { + build_node(parser, cn, &(dest->children[i]), contpos, strpos); + } + dest->name = NULL; + } +} + +static XML_Content * +build_model (XML_Parser parser) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + XML_Content *ret; + XML_Content *cpos; + XML_Char * str; + int allocsize = (dtd->scaffCount * sizeof(XML_Content) + + (dtd->contentStringLen * sizeof(XML_Char))); + + ret = (XML_Content *)MALLOC(allocsize); + if (!ret) + return NULL; + + str = (XML_Char *) (&ret[dtd->scaffCount]); + cpos = &ret[1]; + + build_node(parser, 0, ret, &cpos, &str); + return ret; +} + +static ELEMENT_TYPE * +getElementType(XML_Parser parser, + const ENCODING *enc, + const char *ptr, + const char *end) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + const XML_Char *name = poolStoreString(&dtd->pool, enc, ptr, end); + ELEMENT_TYPE *ret; + + if (!name) + return NULL; + ret = (ELEMENT_TYPE *) lookup(parser, &dtd->elementTypes, name, sizeof(ELEMENT_TYPE)); + if (!ret) + return NULL; + if (ret->name != name) + poolDiscard(&dtd->pool); + else { + poolFinish(&dtd->pool); + if (!setElementTypePrefix(parser, ret)) + return NULL; + } + return ret; +} diff --git a/deps/EXPAT/expat/xmlrole.c b/deps/EXPAT/expat/xmlrole.c new file mode 100644 index 000000000..8475238d3 --- /dev/null +++ b/deps/EXPAT/expat/xmlrole.c @@ -0,0 +1,1322 @@ +/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +#include +#include "expat_config.h" +#include "expat_external.h" +#include "internal.h" +#include "xmlrole.h" +#include "ascii.h" + +/* Doesn't check: + + that ,| are not mixed in a model group + content of literals + +*/ + +static const char KW_ANY[] = { + ASCII_A, ASCII_N, ASCII_Y, '\0' }; +static const char KW_ATTLIST[] = { + ASCII_A, ASCII_T, ASCII_T, ASCII_L, ASCII_I, ASCII_S, ASCII_T, '\0' }; +static const char KW_CDATA[] = { + ASCII_C, ASCII_D, ASCII_A, ASCII_T, ASCII_A, '\0' }; +static const char KW_DOCTYPE[] = { + ASCII_D, ASCII_O, ASCII_C, ASCII_T, ASCII_Y, ASCII_P, ASCII_E, '\0' }; +static const char KW_ELEMENT[] = { + ASCII_E, ASCII_L, ASCII_E, ASCII_M, ASCII_E, ASCII_N, ASCII_T, '\0' }; +static const char KW_EMPTY[] = { + ASCII_E, ASCII_M, ASCII_P, ASCII_T, ASCII_Y, '\0' }; +static const char KW_ENTITIES[] = { + ASCII_E, ASCII_N, ASCII_T, ASCII_I, ASCII_T, ASCII_I, ASCII_E, ASCII_S, + '\0' }; +static const char KW_ENTITY[] = { + ASCII_E, ASCII_N, ASCII_T, ASCII_I, ASCII_T, ASCII_Y, '\0' }; +static const char KW_FIXED[] = { + ASCII_F, ASCII_I, ASCII_X, ASCII_E, ASCII_D, '\0' }; +static const char KW_ID[] = { + ASCII_I, ASCII_D, '\0' }; +static const char KW_IDREF[] = { + ASCII_I, ASCII_D, ASCII_R, ASCII_E, ASCII_F, '\0' }; +static const char KW_IDREFS[] = { + ASCII_I, ASCII_D, ASCII_R, ASCII_E, ASCII_F, ASCII_S, '\0' }; +#ifdef XML_DTD +static const char KW_IGNORE[] = { + ASCII_I, ASCII_G, ASCII_N, ASCII_O, ASCII_R, ASCII_E, '\0' }; +#endif +static const char KW_IMPLIED[] = { + ASCII_I, ASCII_M, ASCII_P, ASCII_L, ASCII_I, ASCII_E, ASCII_D, '\0' }; +#ifdef XML_DTD +static const char KW_INCLUDE[] = { + ASCII_I, ASCII_N, ASCII_C, ASCII_L, ASCII_U, ASCII_D, ASCII_E, '\0' }; +#endif +static const char KW_NDATA[] = { + ASCII_N, ASCII_D, ASCII_A, ASCII_T, ASCII_A, '\0' }; +static const char KW_NMTOKEN[] = { + ASCII_N, ASCII_M, ASCII_T, ASCII_O, ASCII_K, ASCII_E, ASCII_N, '\0' }; +static const char KW_NMTOKENS[] = { + ASCII_N, ASCII_M, ASCII_T, ASCII_O, ASCII_K, ASCII_E, ASCII_N, ASCII_S, + '\0' }; +static const char KW_NOTATION[] = + { ASCII_N, ASCII_O, ASCII_T, ASCII_A, ASCII_T, ASCII_I, ASCII_O, ASCII_N, + '\0' }; +static const char KW_PCDATA[] = { + ASCII_P, ASCII_C, ASCII_D, ASCII_A, ASCII_T, ASCII_A, '\0' }; +static const char KW_PUBLIC[] = { + ASCII_P, ASCII_U, ASCII_B, ASCII_L, ASCII_I, ASCII_C, '\0' }; +static const char KW_REQUIRED[] = { + ASCII_R, ASCII_E, ASCII_Q, ASCII_U, ASCII_I, ASCII_R, ASCII_E, ASCII_D, + '\0' }; +static const char KW_SYSTEM[] = { + ASCII_S, ASCII_Y, ASCII_S, ASCII_T, ASCII_E, ASCII_M, '\0' }; + +#ifndef MIN_BYTES_PER_CHAR +#define MIN_BYTES_PER_CHAR(enc) ((enc)->minBytesPerChar) +#endif + +#ifdef XML_DTD +#define setTopLevel(state) \ + ((state)->handler = ((state)->documentEntity \ + ? internalSubset \ + : externalSubset1)) +#else /* not XML_DTD */ +#define setTopLevel(state) ((state)->handler = internalSubset) +#endif /* not XML_DTD */ + +typedef int PTRCALL PROLOG_HANDLER(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc); + +static PROLOG_HANDLER + prolog0, prolog1, prolog2, + doctype0, doctype1, doctype2, doctype3, doctype4, doctype5, + internalSubset, + entity0, entity1, entity2, entity3, entity4, entity5, entity6, + entity7, entity8, entity9, entity10, + notation0, notation1, notation2, notation3, notation4, + attlist0, attlist1, attlist2, attlist3, attlist4, attlist5, attlist6, + attlist7, attlist8, attlist9, + element0, element1, element2, element3, element4, element5, element6, + element7, +#ifdef XML_DTD + externalSubset0, externalSubset1, + condSect0, condSect1, condSect2, +#endif /* XML_DTD */ + declClose, + error; + +static int FASTCALL common(PROLOG_STATE *state, int tok); + +static int PTRCALL +prolog0(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + state->handler = prolog1; + return XML_ROLE_NONE; + case XML_TOK_XML_DECL: + state->handler = prolog1; + return XML_ROLE_XML_DECL; + case XML_TOK_PI: + state->handler = prolog1; + return XML_ROLE_PI; + case XML_TOK_COMMENT: + state->handler = prolog1; + return XML_ROLE_COMMENT; + case XML_TOK_BOM: + return XML_ROLE_NONE; + case XML_TOK_DECL_OPEN: + if (!XmlNameMatchesAscii(enc, + ptr + 2 * MIN_BYTES_PER_CHAR(enc), + end, + KW_DOCTYPE)) + break; + state->handler = doctype0; + return XML_ROLE_DOCTYPE_NONE; + case XML_TOK_INSTANCE_START: + state->handler = error; + return XML_ROLE_INSTANCE_START; + } + return common(state, tok); +} + +static int PTRCALL +prolog1(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_PI: + return XML_ROLE_PI; + case XML_TOK_COMMENT: + return XML_ROLE_COMMENT; + case XML_TOK_BOM: + return XML_ROLE_NONE; + case XML_TOK_DECL_OPEN: + if (!XmlNameMatchesAscii(enc, + ptr + 2 * MIN_BYTES_PER_CHAR(enc), + end, + KW_DOCTYPE)) + break; + state->handler = doctype0; + return XML_ROLE_DOCTYPE_NONE; + case XML_TOK_INSTANCE_START: + state->handler = error; + return XML_ROLE_INSTANCE_START; + } + return common(state, tok); +} + +static int PTRCALL +prolog2(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_PI: + return XML_ROLE_PI; + case XML_TOK_COMMENT: + return XML_ROLE_COMMENT; + case XML_TOK_INSTANCE_START: + state->handler = error; + return XML_ROLE_INSTANCE_START; + } + return common(state, tok); +} + +static int PTRCALL +doctype0(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_DOCTYPE_NONE; + case XML_TOK_NAME: + case XML_TOK_PREFIXED_NAME: + state->handler = doctype1; + return XML_ROLE_DOCTYPE_NAME; + } + return common(state, tok); +} + +static int PTRCALL +doctype1(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_DOCTYPE_NONE; + case XML_TOK_OPEN_BRACKET: + state->handler = internalSubset; + return XML_ROLE_DOCTYPE_INTERNAL_SUBSET; + case XML_TOK_DECL_CLOSE: + state->handler = prolog2; + return XML_ROLE_DOCTYPE_CLOSE; + case XML_TOK_NAME: + if (XmlNameMatchesAscii(enc, ptr, end, KW_SYSTEM)) { + state->handler = doctype3; + return XML_ROLE_DOCTYPE_NONE; + } + if (XmlNameMatchesAscii(enc, ptr, end, KW_PUBLIC)) { + state->handler = doctype2; + return XML_ROLE_DOCTYPE_NONE; + } + break; + } + return common(state, tok); +} + +static int PTRCALL +doctype2(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_DOCTYPE_NONE; + case XML_TOK_LITERAL: + state->handler = doctype3; + return XML_ROLE_DOCTYPE_PUBLIC_ID; + } + return common(state, tok); +} + +static int PTRCALL +doctype3(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_DOCTYPE_NONE; + case XML_TOK_LITERAL: + state->handler = doctype4; + return XML_ROLE_DOCTYPE_SYSTEM_ID; + } + return common(state, tok); +} + +static int PTRCALL +doctype4(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_DOCTYPE_NONE; + case XML_TOK_OPEN_BRACKET: + state->handler = internalSubset; + return XML_ROLE_DOCTYPE_INTERNAL_SUBSET; + case XML_TOK_DECL_CLOSE: + state->handler = prolog2; + return XML_ROLE_DOCTYPE_CLOSE; + } + return common(state, tok); +} + +static int PTRCALL +doctype5(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_DOCTYPE_NONE; + case XML_TOK_DECL_CLOSE: + state->handler = prolog2; + return XML_ROLE_DOCTYPE_CLOSE; + } + return common(state, tok); +} + +static int PTRCALL +internalSubset(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_DECL_OPEN: + if (XmlNameMatchesAscii(enc, + ptr + 2 * MIN_BYTES_PER_CHAR(enc), + end, + KW_ENTITY)) { + state->handler = entity0; + return XML_ROLE_ENTITY_NONE; + } + if (XmlNameMatchesAscii(enc, + ptr + 2 * MIN_BYTES_PER_CHAR(enc), + end, + KW_ATTLIST)) { + state->handler = attlist0; + return XML_ROLE_ATTLIST_NONE; + } + if (XmlNameMatchesAscii(enc, + ptr + 2 * MIN_BYTES_PER_CHAR(enc), + end, + KW_ELEMENT)) { + state->handler = element0; + return XML_ROLE_ELEMENT_NONE; + } + if (XmlNameMatchesAscii(enc, + ptr + 2 * MIN_BYTES_PER_CHAR(enc), + end, + KW_NOTATION)) { + state->handler = notation0; + return XML_ROLE_NOTATION_NONE; + } + break; + case XML_TOK_PI: + return XML_ROLE_PI; + case XML_TOK_COMMENT: + return XML_ROLE_COMMENT; + case XML_TOK_PARAM_ENTITY_REF: + return XML_ROLE_PARAM_ENTITY_REF; + case XML_TOK_CLOSE_BRACKET: + state->handler = doctype5; + return XML_ROLE_DOCTYPE_NONE; + case XML_TOK_NONE: + return XML_ROLE_NONE; + } + return common(state, tok); +} + +#ifdef XML_DTD + +static int PTRCALL +externalSubset0(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + state->handler = externalSubset1; + if (tok == XML_TOK_XML_DECL) + return XML_ROLE_TEXT_DECL; + return externalSubset1(state, tok, ptr, end, enc); +} + +static int PTRCALL +externalSubset1(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_COND_SECT_OPEN: + state->handler = condSect0; + return XML_ROLE_NONE; + case XML_TOK_COND_SECT_CLOSE: + if (state->includeLevel == 0) + break; + state->includeLevel -= 1; + return XML_ROLE_NONE; + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_CLOSE_BRACKET: + break; + case XML_TOK_NONE: + if (state->includeLevel) + break; + return XML_ROLE_NONE; + default: + return internalSubset(state, tok, ptr, end, enc); + } + return common(state, tok); +} + +#endif /* XML_DTD */ + +static int PTRCALL +entity0(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ENTITY_NONE; + case XML_TOK_PERCENT: + state->handler = entity1; + return XML_ROLE_ENTITY_NONE; + case XML_TOK_NAME: + state->handler = entity2; + return XML_ROLE_GENERAL_ENTITY_NAME; + } + return common(state, tok); +} + +static int PTRCALL +entity1(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ENTITY_NONE; + case XML_TOK_NAME: + state->handler = entity7; + return XML_ROLE_PARAM_ENTITY_NAME; + } + return common(state, tok); +} + +static int PTRCALL +entity2(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ENTITY_NONE; + case XML_TOK_NAME: + if (XmlNameMatchesAscii(enc, ptr, end, KW_SYSTEM)) { + state->handler = entity4; + return XML_ROLE_ENTITY_NONE; + } + if (XmlNameMatchesAscii(enc, ptr, end, KW_PUBLIC)) { + state->handler = entity3; + return XML_ROLE_ENTITY_NONE; + } + break; + case XML_TOK_LITERAL: + state->handler = declClose; + state->role_none = XML_ROLE_ENTITY_NONE; + return XML_ROLE_ENTITY_VALUE; + } + return common(state, tok); +} + +static int PTRCALL +entity3(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ENTITY_NONE; + case XML_TOK_LITERAL: + state->handler = entity4; + return XML_ROLE_ENTITY_PUBLIC_ID; + } + return common(state, tok); +} + +static int PTRCALL +entity4(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ENTITY_NONE; + case XML_TOK_LITERAL: + state->handler = entity5; + return XML_ROLE_ENTITY_SYSTEM_ID; + } + return common(state, tok); +} + +static int PTRCALL +entity5(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ENTITY_NONE; + case XML_TOK_DECL_CLOSE: + setTopLevel(state); + return XML_ROLE_ENTITY_COMPLETE; + case XML_TOK_NAME: + if (XmlNameMatchesAscii(enc, ptr, end, KW_NDATA)) { + state->handler = entity6; + return XML_ROLE_ENTITY_NONE; + } + break; + } + return common(state, tok); +} + +static int PTRCALL +entity6(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ENTITY_NONE; + case XML_TOK_NAME: + state->handler = declClose; + state->role_none = XML_ROLE_ENTITY_NONE; + return XML_ROLE_ENTITY_NOTATION_NAME; + } + return common(state, tok); +} + +static int PTRCALL +entity7(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ENTITY_NONE; + case XML_TOK_NAME: + if (XmlNameMatchesAscii(enc, ptr, end, KW_SYSTEM)) { + state->handler = entity9; + return XML_ROLE_ENTITY_NONE; + } + if (XmlNameMatchesAscii(enc, ptr, end, KW_PUBLIC)) { + state->handler = entity8; + return XML_ROLE_ENTITY_NONE; + } + break; + case XML_TOK_LITERAL: + state->handler = declClose; + state->role_none = XML_ROLE_ENTITY_NONE; + return XML_ROLE_ENTITY_VALUE; + } + return common(state, tok); +} + +static int PTRCALL +entity8(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ENTITY_NONE; + case XML_TOK_LITERAL: + state->handler = entity9; + return XML_ROLE_ENTITY_PUBLIC_ID; + } + return common(state, tok); +} + +static int PTRCALL +entity9(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ENTITY_NONE; + case XML_TOK_LITERAL: + state->handler = entity10; + return XML_ROLE_ENTITY_SYSTEM_ID; + } + return common(state, tok); +} + +static int PTRCALL +entity10(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ENTITY_NONE; + case XML_TOK_DECL_CLOSE: + setTopLevel(state); + return XML_ROLE_ENTITY_COMPLETE; + } + return common(state, tok); +} + +static int PTRCALL +notation0(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NOTATION_NONE; + case XML_TOK_NAME: + state->handler = notation1; + return XML_ROLE_NOTATION_NAME; + } + return common(state, tok); +} + +static int PTRCALL +notation1(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NOTATION_NONE; + case XML_TOK_NAME: + if (XmlNameMatchesAscii(enc, ptr, end, KW_SYSTEM)) { + state->handler = notation3; + return XML_ROLE_NOTATION_NONE; + } + if (XmlNameMatchesAscii(enc, ptr, end, KW_PUBLIC)) { + state->handler = notation2; + return XML_ROLE_NOTATION_NONE; + } + break; + } + return common(state, tok); +} + +static int PTRCALL +notation2(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NOTATION_NONE; + case XML_TOK_LITERAL: + state->handler = notation4; + return XML_ROLE_NOTATION_PUBLIC_ID; + } + return common(state, tok); +} + +static int PTRCALL +notation3(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NOTATION_NONE; + case XML_TOK_LITERAL: + state->handler = declClose; + state->role_none = XML_ROLE_NOTATION_NONE; + return XML_ROLE_NOTATION_SYSTEM_ID; + } + return common(state, tok); +} + +static int PTRCALL +notation4(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NOTATION_NONE; + case XML_TOK_LITERAL: + state->handler = declClose; + state->role_none = XML_ROLE_NOTATION_NONE; + return XML_ROLE_NOTATION_SYSTEM_ID; + case XML_TOK_DECL_CLOSE: + setTopLevel(state); + return XML_ROLE_NOTATION_NO_SYSTEM_ID; + } + return common(state, tok); +} + +static int PTRCALL +attlist0(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_NAME: + case XML_TOK_PREFIXED_NAME: + state->handler = attlist1; + return XML_ROLE_ATTLIST_ELEMENT_NAME; + } + return common(state, tok); +} + +static int PTRCALL +attlist1(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_DECL_CLOSE: + setTopLevel(state); + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_NAME: + case XML_TOK_PREFIXED_NAME: + state->handler = attlist2; + return XML_ROLE_ATTRIBUTE_NAME; + } + return common(state, tok); +} + +static int PTRCALL +attlist2(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_NAME: + { + static const char * const types[] = { + KW_CDATA, + KW_ID, + KW_IDREF, + KW_IDREFS, + KW_ENTITY, + KW_ENTITIES, + KW_NMTOKEN, + KW_NMTOKENS, + }; + int i; + for (i = 0; i < (int)(sizeof(types)/sizeof(types[0])); i++) + if (XmlNameMatchesAscii(enc, ptr, end, types[i])) { + state->handler = attlist8; + return XML_ROLE_ATTRIBUTE_TYPE_CDATA + i; + } + } + if (XmlNameMatchesAscii(enc, ptr, end, KW_NOTATION)) { + state->handler = attlist5; + return XML_ROLE_ATTLIST_NONE; + } + break; + case XML_TOK_OPEN_PAREN: + state->handler = attlist3; + return XML_ROLE_ATTLIST_NONE; + } + return common(state, tok); +} + +static int PTRCALL +attlist3(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_NMTOKEN: + case XML_TOK_NAME: + case XML_TOK_PREFIXED_NAME: + state->handler = attlist4; + return XML_ROLE_ATTRIBUTE_ENUM_VALUE; + } + return common(state, tok); +} + +static int PTRCALL +attlist4(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_CLOSE_PAREN: + state->handler = attlist8; + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_OR: + state->handler = attlist3; + return XML_ROLE_ATTLIST_NONE; + } + return common(state, tok); +} + +static int PTRCALL +attlist5(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_OPEN_PAREN: + state->handler = attlist6; + return XML_ROLE_ATTLIST_NONE; + } + return common(state, tok); +} + +static int PTRCALL +attlist6(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_NAME: + state->handler = attlist7; + return XML_ROLE_ATTRIBUTE_NOTATION_VALUE; + } + return common(state, tok); +} + +static int PTRCALL +attlist7(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_CLOSE_PAREN: + state->handler = attlist8; + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_OR: + state->handler = attlist6; + return XML_ROLE_ATTLIST_NONE; + } + return common(state, tok); +} + +/* default value */ +static int PTRCALL +attlist8(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_POUND_NAME: + if (XmlNameMatchesAscii(enc, + ptr + MIN_BYTES_PER_CHAR(enc), + end, + KW_IMPLIED)) { + state->handler = attlist1; + return XML_ROLE_IMPLIED_ATTRIBUTE_VALUE; + } + if (XmlNameMatchesAscii(enc, + ptr + MIN_BYTES_PER_CHAR(enc), + end, + KW_REQUIRED)) { + state->handler = attlist1; + return XML_ROLE_REQUIRED_ATTRIBUTE_VALUE; + } + if (XmlNameMatchesAscii(enc, + ptr + MIN_BYTES_PER_CHAR(enc), + end, + KW_FIXED)) { + state->handler = attlist9; + return XML_ROLE_ATTLIST_NONE; + } + break; + case XML_TOK_LITERAL: + state->handler = attlist1; + return XML_ROLE_DEFAULT_ATTRIBUTE_VALUE; + } + return common(state, tok); +} + +static int PTRCALL +attlist9(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_LITERAL: + state->handler = attlist1; + return XML_ROLE_FIXED_ATTRIBUTE_VALUE; + } + return common(state, tok); +} + +static int PTRCALL +element0(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ELEMENT_NONE; + case XML_TOK_NAME: + case XML_TOK_PREFIXED_NAME: + state->handler = element1; + return XML_ROLE_ELEMENT_NAME; + } + return common(state, tok); +} + +static int PTRCALL +element1(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ELEMENT_NONE; + case XML_TOK_NAME: + if (XmlNameMatchesAscii(enc, ptr, end, KW_EMPTY)) { + state->handler = declClose; + state->role_none = XML_ROLE_ELEMENT_NONE; + return XML_ROLE_CONTENT_EMPTY; + } + if (XmlNameMatchesAscii(enc, ptr, end, KW_ANY)) { + state->handler = declClose; + state->role_none = XML_ROLE_ELEMENT_NONE; + return XML_ROLE_CONTENT_ANY; + } + break; + case XML_TOK_OPEN_PAREN: + state->handler = element2; + state->level = 1; + return XML_ROLE_GROUP_OPEN; + } + return common(state, tok); +} + +static int PTRCALL +element2(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ELEMENT_NONE; + case XML_TOK_POUND_NAME: + if (XmlNameMatchesAscii(enc, + ptr + MIN_BYTES_PER_CHAR(enc), + end, + KW_PCDATA)) { + state->handler = element3; + return XML_ROLE_CONTENT_PCDATA; + } + break; + case XML_TOK_OPEN_PAREN: + state->level = 2; + state->handler = element6; + return XML_ROLE_GROUP_OPEN; + case XML_TOK_NAME: + case XML_TOK_PREFIXED_NAME: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT; + case XML_TOK_NAME_QUESTION: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT_OPT; + case XML_TOK_NAME_ASTERISK: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT_REP; + case XML_TOK_NAME_PLUS: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT_PLUS; + } + return common(state, tok); +} + +static int PTRCALL +element3(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ELEMENT_NONE; + case XML_TOK_CLOSE_PAREN: + state->handler = declClose; + state->role_none = XML_ROLE_ELEMENT_NONE; + return XML_ROLE_GROUP_CLOSE; + case XML_TOK_CLOSE_PAREN_ASTERISK: + state->handler = declClose; + state->role_none = XML_ROLE_ELEMENT_NONE; + return XML_ROLE_GROUP_CLOSE_REP; + case XML_TOK_OR: + state->handler = element4; + return XML_ROLE_ELEMENT_NONE; + } + return common(state, tok); +} + +static int PTRCALL +element4(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ELEMENT_NONE; + case XML_TOK_NAME: + case XML_TOK_PREFIXED_NAME: + state->handler = element5; + return XML_ROLE_CONTENT_ELEMENT; + } + return common(state, tok); +} + +static int PTRCALL +element5(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ELEMENT_NONE; + case XML_TOK_CLOSE_PAREN_ASTERISK: + state->handler = declClose; + state->role_none = XML_ROLE_ELEMENT_NONE; + return XML_ROLE_GROUP_CLOSE_REP; + case XML_TOK_OR: + state->handler = element4; + return XML_ROLE_ELEMENT_NONE; + } + return common(state, tok); +} + +static int PTRCALL +element6(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ELEMENT_NONE; + case XML_TOK_OPEN_PAREN: + state->level += 1; + return XML_ROLE_GROUP_OPEN; + case XML_TOK_NAME: + case XML_TOK_PREFIXED_NAME: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT; + case XML_TOK_NAME_QUESTION: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT_OPT; + case XML_TOK_NAME_ASTERISK: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT_REP; + case XML_TOK_NAME_PLUS: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT_PLUS; + } + return common(state, tok); +} + +static int PTRCALL +element7(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ELEMENT_NONE; + case XML_TOK_CLOSE_PAREN: + state->level -= 1; + if (state->level == 0) { + state->handler = declClose; + state->role_none = XML_ROLE_ELEMENT_NONE; + } + return XML_ROLE_GROUP_CLOSE; + case XML_TOK_CLOSE_PAREN_ASTERISK: + state->level -= 1; + if (state->level == 0) { + state->handler = declClose; + state->role_none = XML_ROLE_ELEMENT_NONE; + } + return XML_ROLE_GROUP_CLOSE_REP; + case XML_TOK_CLOSE_PAREN_QUESTION: + state->level -= 1; + if (state->level == 0) { + state->handler = declClose; + state->role_none = XML_ROLE_ELEMENT_NONE; + } + return XML_ROLE_GROUP_CLOSE_OPT; + case XML_TOK_CLOSE_PAREN_PLUS: + state->level -= 1; + if (state->level == 0) { + state->handler = declClose; + state->role_none = XML_ROLE_ELEMENT_NONE; + } + return XML_ROLE_GROUP_CLOSE_PLUS; + case XML_TOK_COMMA: + state->handler = element6; + return XML_ROLE_GROUP_SEQUENCE; + case XML_TOK_OR: + state->handler = element6; + return XML_ROLE_GROUP_CHOICE; + } + return common(state, tok); +} + +#ifdef XML_DTD + +static int PTRCALL +condSect0(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_NAME: + if (XmlNameMatchesAscii(enc, ptr, end, KW_INCLUDE)) { + state->handler = condSect1; + return XML_ROLE_NONE; + } + if (XmlNameMatchesAscii(enc, ptr, end, KW_IGNORE)) { + state->handler = condSect2; + return XML_ROLE_NONE; + } + break; + } + return common(state, tok); +} + +static int PTRCALL +condSect1(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_OPEN_BRACKET: + state->handler = externalSubset1; + state->includeLevel += 1; + return XML_ROLE_NONE; + } + return common(state, tok); +} + +static int PTRCALL +condSect2(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_OPEN_BRACKET: + state->handler = externalSubset1; + return XML_ROLE_IGNORE_SECT; + } + return common(state, tok); +} + +#endif /* XML_DTD */ + +static int PTRCALL +declClose(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return state->role_none; + case XML_TOK_DECL_CLOSE: + setTopLevel(state); + return state->role_none; + } + return common(state, tok); +} + +static int PTRCALL +error(PROLOG_STATE *UNUSED_P(state), + int UNUSED_P(tok), + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + return XML_ROLE_NONE; +} + +static int FASTCALL +common(PROLOG_STATE *state, int tok) +{ +#ifdef XML_DTD + if (!state->documentEntity && tok == XML_TOK_PARAM_ENTITY_REF) + return XML_ROLE_INNER_PARAM_ENTITY_REF; +#endif + state->handler = error; + return XML_ROLE_ERROR; +} + +void +XmlPrologStateInit(PROLOG_STATE *state) +{ + state->handler = prolog0; +#ifdef XML_DTD + state->documentEntity = 1; + state->includeLevel = 0; + state->inEntityValue = 0; +#endif /* XML_DTD */ +} + +#ifdef XML_DTD + +void +XmlPrologStateInitExternalEntity(PROLOG_STATE *state) +{ + state->handler = externalSubset0; + state->documentEntity = 0; + state->includeLevel = 0; +} + +#endif /* XML_DTD */ diff --git a/deps/EXPAT/expat/xmlrole.h b/deps/EXPAT/expat/xmlrole.h new file mode 100644 index 000000000..4dd9f06f9 --- /dev/null +++ b/deps/EXPAT/expat/xmlrole.h @@ -0,0 +1,114 @@ +/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +#ifndef XmlRole_INCLUDED +#define XmlRole_INCLUDED 1 + +#ifdef __VMS +/* 0 1 2 3 0 1 2 3 + 1234567890123456789012345678901 1234567890123456789012345678901 */ +#define XmlPrologStateInitExternalEntity XmlPrologStateInitExternalEnt +#endif + +#include "xmltok.h" + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + XML_ROLE_ERROR = -1, + XML_ROLE_NONE = 0, + XML_ROLE_XML_DECL, + XML_ROLE_INSTANCE_START, + XML_ROLE_DOCTYPE_NONE, + XML_ROLE_DOCTYPE_NAME, + XML_ROLE_DOCTYPE_SYSTEM_ID, + XML_ROLE_DOCTYPE_PUBLIC_ID, + XML_ROLE_DOCTYPE_INTERNAL_SUBSET, + XML_ROLE_DOCTYPE_CLOSE, + XML_ROLE_GENERAL_ENTITY_NAME, + XML_ROLE_PARAM_ENTITY_NAME, + XML_ROLE_ENTITY_NONE, + XML_ROLE_ENTITY_VALUE, + XML_ROLE_ENTITY_SYSTEM_ID, + XML_ROLE_ENTITY_PUBLIC_ID, + XML_ROLE_ENTITY_COMPLETE, + XML_ROLE_ENTITY_NOTATION_NAME, + XML_ROLE_NOTATION_NONE, + XML_ROLE_NOTATION_NAME, + XML_ROLE_NOTATION_SYSTEM_ID, + XML_ROLE_NOTATION_NO_SYSTEM_ID, + XML_ROLE_NOTATION_PUBLIC_ID, + XML_ROLE_ATTRIBUTE_NAME, + XML_ROLE_ATTRIBUTE_TYPE_CDATA, + XML_ROLE_ATTRIBUTE_TYPE_ID, + XML_ROLE_ATTRIBUTE_TYPE_IDREF, + XML_ROLE_ATTRIBUTE_TYPE_IDREFS, + XML_ROLE_ATTRIBUTE_TYPE_ENTITY, + XML_ROLE_ATTRIBUTE_TYPE_ENTITIES, + XML_ROLE_ATTRIBUTE_TYPE_NMTOKEN, + XML_ROLE_ATTRIBUTE_TYPE_NMTOKENS, + XML_ROLE_ATTRIBUTE_ENUM_VALUE, + XML_ROLE_ATTRIBUTE_NOTATION_VALUE, + XML_ROLE_ATTLIST_NONE, + XML_ROLE_ATTLIST_ELEMENT_NAME, + XML_ROLE_IMPLIED_ATTRIBUTE_VALUE, + XML_ROLE_REQUIRED_ATTRIBUTE_VALUE, + XML_ROLE_DEFAULT_ATTRIBUTE_VALUE, + XML_ROLE_FIXED_ATTRIBUTE_VALUE, + XML_ROLE_ELEMENT_NONE, + XML_ROLE_ELEMENT_NAME, + XML_ROLE_CONTENT_ANY, + XML_ROLE_CONTENT_EMPTY, + XML_ROLE_CONTENT_PCDATA, + XML_ROLE_GROUP_OPEN, + XML_ROLE_GROUP_CLOSE, + XML_ROLE_GROUP_CLOSE_REP, + XML_ROLE_GROUP_CLOSE_OPT, + XML_ROLE_GROUP_CLOSE_PLUS, + XML_ROLE_GROUP_CHOICE, + XML_ROLE_GROUP_SEQUENCE, + XML_ROLE_CONTENT_ELEMENT, + XML_ROLE_CONTENT_ELEMENT_REP, + XML_ROLE_CONTENT_ELEMENT_OPT, + XML_ROLE_CONTENT_ELEMENT_PLUS, + XML_ROLE_PI, + XML_ROLE_COMMENT, +#ifdef XML_DTD + XML_ROLE_TEXT_DECL, + XML_ROLE_IGNORE_SECT, + XML_ROLE_INNER_PARAM_ENTITY_REF, +#endif /* XML_DTD */ + XML_ROLE_PARAM_ENTITY_REF +}; + +typedef struct prolog_state { + int (PTRCALL *handler) (struct prolog_state *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc); + unsigned level; + int role_none; +#ifdef XML_DTD + unsigned includeLevel; + int documentEntity; + int inEntityValue; +#endif /* XML_DTD */ +} PROLOG_STATE; + +void XmlPrologStateInit(PROLOG_STATE *); +#ifdef XML_DTD +void XmlPrologStateInitExternalEntity(PROLOG_STATE *); +#endif /* XML_DTD */ + +#define XmlTokenRole(state, tok, ptr, end, enc) \ + (((state)->handler)(state, tok, ptr, end, enc)) + +#ifdef __cplusplus +} +#endif + +#endif /* not XmlRole_INCLUDED */ diff --git a/deps/EXPAT/expat/xmltok.c b/deps/EXPAT/expat/xmltok.c new file mode 100644 index 000000000..f10b459ff --- /dev/null +++ b/deps/EXPAT/expat/xmltok.c @@ -0,0 +1,1737 @@ +/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +#include +#include "expat_config.h" +#include "expat_external.h" +#include "internal.h" +#include "xmltok.h" +#include "nametab.h" + +#ifdef XML_DTD +#define IGNORE_SECTION_TOK_VTABLE , PREFIX(ignoreSectionTok) +#else +#define IGNORE_SECTION_TOK_VTABLE /* as nothing */ +#endif + +#define VTABLE1 \ + { PREFIX(prologTok), PREFIX(contentTok), \ + PREFIX(cdataSectionTok) IGNORE_SECTION_TOK_VTABLE }, \ + { PREFIX(attributeValueTok), PREFIX(entityValueTok) }, \ + PREFIX(sameName), \ + PREFIX(nameMatchesAscii), \ + PREFIX(nameLength), \ + PREFIX(skipS), \ + PREFIX(getAtts), \ + PREFIX(charRefNumber), \ + PREFIX(predefinedEntityName), \ + PREFIX(updatePosition), \ + PREFIX(isPublicId) + +#define VTABLE VTABLE1, PREFIX(toUtf8), PREFIX(toUtf16) + +#define UCS2_GET_NAMING(pages, hi, lo) \ + (namingBitmap[(pages[hi] << 3) + ((lo) >> 5)] & (1u << ((lo) & 0x1F))) + +/* A 2 byte UTF-8 representation splits the characters 11 bits between + the bottom 5 and 6 bits of the bytes. We need 8 bits to index into + pages, 3 bits to add to that index and 5 bits to generate the mask. +*/ +#define UTF8_GET_NAMING2(pages, byte) \ + (namingBitmap[((pages)[(((byte)[0]) >> 2) & 7] << 3) \ + + ((((byte)[0]) & 3) << 1) \ + + ((((byte)[1]) >> 5) & 1)] \ + & (1u << (((byte)[1]) & 0x1F))) + +/* A 3 byte UTF-8 representation splits the characters 16 bits between + the bottom 4, 6 and 6 bits of the bytes. We need 8 bits to index + into pages, 3 bits to add to that index and 5 bits to generate the + mask. +*/ +#define UTF8_GET_NAMING3(pages, byte) \ + (namingBitmap[((pages)[((((byte)[0]) & 0xF) << 4) \ + + ((((byte)[1]) >> 2) & 0xF)] \ + << 3) \ + + ((((byte)[1]) & 3) << 1) \ + + ((((byte)[2]) >> 5) & 1)] \ + & (1u << (((byte)[2]) & 0x1F))) + +#define UTF8_GET_NAMING(pages, p, n) \ + ((n) == 2 \ + ? UTF8_GET_NAMING2(pages, (const unsigned char *)(p)) \ + : ((n) == 3 \ + ? UTF8_GET_NAMING3(pages, (const unsigned char *)(p)) \ + : 0)) + +/* Detection of invalid UTF-8 sequences is based on Table 3.1B + of Unicode 3.2: http://www.unicode.org/unicode/reports/tr28/ + with the additional restriction of not allowing the Unicode + code points 0xFFFF and 0xFFFE (sequences EF,BF,BF and EF,BF,BE). + Implementation details: + (A & 0x80) == 0 means A < 0x80 + and + (A & 0xC0) == 0xC0 means A > 0xBF +*/ + +#define UTF8_INVALID2(p) \ + ((*p) < 0xC2 || ((p)[1] & 0x80) == 0 || ((p)[1] & 0xC0) == 0xC0) + +#define UTF8_INVALID3(p) \ + (((p)[2] & 0x80) == 0 \ + || \ + ((*p) == 0xEF && (p)[1] == 0xBF \ + ? \ + (p)[2] > 0xBD \ + : \ + ((p)[2] & 0xC0) == 0xC0) \ + || \ + ((*p) == 0xE0 \ + ? \ + (p)[1] < 0xA0 || ((p)[1] & 0xC0) == 0xC0 \ + : \ + ((p)[1] & 0x80) == 0 \ + || \ + ((*p) == 0xED ? (p)[1] > 0x9F : ((p)[1] & 0xC0) == 0xC0))) + +#define UTF8_INVALID4(p) \ + (((p)[3] & 0x80) == 0 || ((p)[3] & 0xC0) == 0xC0 \ + || \ + ((p)[2] & 0x80) == 0 || ((p)[2] & 0xC0) == 0xC0 \ + || \ + ((*p) == 0xF0 \ + ? \ + (p)[1] < 0x90 || ((p)[1] & 0xC0) == 0xC0 \ + : \ + ((p)[1] & 0x80) == 0 \ + || \ + ((*p) == 0xF4 ? (p)[1] > 0x8F : ((p)[1] & 0xC0) == 0xC0))) + +static int PTRFASTCALL +isNever(const ENCODING *UNUSED_P(enc), const char *UNUSED_P(p)) +{ + return 0; +} + +static int PTRFASTCALL +utf8_isName2(const ENCODING *UNUSED_P(enc), const char *p) +{ + return UTF8_GET_NAMING2(namePages, (const unsigned char *)p); +} + +static int PTRFASTCALL +utf8_isName3(const ENCODING *UNUSED_P(enc), const char *p) +{ + return UTF8_GET_NAMING3(namePages, (const unsigned char *)p); +} + +#define utf8_isName4 isNever + +static int PTRFASTCALL +utf8_isNmstrt2(const ENCODING *UNUSED_P(enc), const char *p) +{ + return UTF8_GET_NAMING2(nmstrtPages, (const unsigned char *)p); +} + +static int PTRFASTCALL +utf8_isNmstrt3(const ENCODING *UNUSED_P(enc), const char *p) +{ + return UTF8_GET_NAMING3(nmstrtPages, (const unsigned char *)p); +} + +#define utf8_isNmstrt4 isNever + +static int PTRFASTCALL +utf8_isInvalid2(const ENCODING *UNUSED_P(enc), const char *p) +{ + return UTF8_INVALID2((const unsigned char *)p); +} + +static int PTRFASTCALL +utf8_isInvalid3(const ENCODING *UNUSED_P(enc), const char *p) +{ + return UTF8_INVALID3((const unsigned char *)p); +} + +static int PTRFASTCALL +utf8_isInvalid4(const ENCODING *UNUSED_P(enc), const char *p) +{ + return UTF8_INVALID4((const unsigned char *)p); +} + +struct normal_encoding { + ENCODING enc; + unsigned char type[256]; +#ifdef XML_MIN_SIZE + int (PTRFASTCALL *byteType)(const ENCODING *, const char *); + int (PTRFASTCALL *isNameMin)(const ENCODING *, const char *); + int (PTRFASTCALL *isNmstrtMin)(const ENCODING *, const char *); + int (PTRFASTCALL *byteToAscii)(const ENCODING *, const char *); + int (PTRCALL *charMatches)(const ENCODING *, const char *, int); +#endif /* XML_MIN_SIZE */ + int (PTRFASTCALL *isName2)(const ENCODING *, const char *); + int (PTRFASTCALL *isName3)(const ENCODING *, const char *); + int (PTRFASTCALL *isName4)(const ENCODING *, const char *); + int (PTRFASTCALL *isNmstrt2)(const ENCODING *, const char *); + int (PTRFASTCALL *isNmstrt3)(const ENCODING *, const char *); + int (PTRFASTCALL *isNmstrt4)(const ENCODING *, const char *); + int (PTRFASTCALL *isInvalid2)(const ENCODING *, const char *); + int (PTRFASTCALL *isInvalid3)(const ENCODING *, const char *); + int (PTRFASTCALL *isInvalid4)(const ENCODING *, const char *); +}; + +#define AS_NORMAL_ENCODING(enc) ((const struct normal_encoding *) (enc)) + +#ifdef XML_MIN_SIZE + +#define STANDARD_VTABLE(E) \ + E ## byteType, \ + E ## isNameMin, \ + E ## isNmstrtMin, \ + E ## byteToAscii, \ + E ## charMatches, + +#else + +#define STANDARD_VTABLE(E) /* as nothing */ + +#endif + +#define NORMAL_VTABLE(E) \ + E ## isName2, \ + E ## isName3, \ + E ## isName4, \ + E ## isNmstrt2, \ + E ## isNmstrt3, \ + E ## isNmstrt4, \ + E ## isInvalid2, \ + E ## isInvalid3, \ + E ## isInvalid4 + +#define NULL_VTABLE \ + /* isName2 */ NULL, \ + /* isName3 */ NULL, \ + /* isName4 */ NULL, \ + /* isNmstrt2 */ NULL, \ + /* isNmstrt3 */ NULL, \ + /* isNmstrt4 */ NULL, \ + /* isInvalid2 */ NULL, \ + /* isInvalid3 */ NULL, \ + /* isInvalid4 */ NULL + +static int FASTCALL checkCharRefNumber(int); + +#include "xmltok_impl.h" +#include "ascii.h" + +#ifdef XML_MIN_SIZE +#define sb_isNameMin isNever +#define sb_isNmstrtMin isNever +#endif + +#ifdef XML_MIN_SIZE +#define MINBPC(enc) ((enc)->minBytesPerChar) +#else +/* minimum bytes per character */ +#define MINBPC(enc) 1 +#endif + +#define SB_BYTE_TYPE(enc, p) \ + (((struct normal_encoding *)(enc))->type[(unsigned char)*(p)]) + +#ifdef XML_MIN_SIZE +static int PTRFASTCALL +sb_byteType(const ENCODING *enc, const char *p) +{ + return SB_BYTE_TYPE(enc, p); +} +#define BYTE_TYPE(enc, p) \ + (AS_NORMAL_ENCODING(enc)->byteType(enc, p)) +#else +#define BYTE_TYPE(enc, p) SB_BYTE_TYPE(enc, p) +#endif + +#ifdef XML_MIN_SIZE +#define BYTE_TO_ASCII(enc, p) \ + (AS_NORMAL_ENCODING(enc)->byteToAscii(enc, p)) +static int PTRFASTCALL +sb_byteToAscii(const ENCODING *enc, const char *p) +{ + return *p; +} +#else +#define BYTE_TO_ASCII(enc, p) (*(p)) +#endif + +#define IS_NAME_CHAR(enc, p, n) \ + (AS_NORMAL_ENCODING(enc)->isName ## n(enc, p)) +#define IS_NMSTRT_CHAR(enc, p, n) \ + (AS_NORMAL_ENCODING(enc)->isNmstrt ## n(enc, p)) +#define IS_INVALID_CHAR(enc, p, n) \ + (AS_NORMAL_ENCODING(enc)->isInvalid ## n(enc, p)) + +#ifdef XML_MIN_SIZE +#define IS_NAME_CHAR_MINBPC(enc, p) \ + (AS_NORMAL_ENCODING(enc)->isNameMin(enc, p)) +#define IS_NMSTRT_CHAR_MINBPC(enc, p) \ + (AS_NORMAL_ENCODING(enc)->isNmstrtMin(enc, p)) +#else +#define IS_NAME_CHAR_MINBPC(enc, p) (0) +#define IS_NMSTRT_CHAR_MINBPC(enc, p) (0) +#endif + +#ifdef XML_MIN_SIZE +#define CHAR_MATCHES(enc, p, c) \ + (AS_NORMAL_ENCODING(enc)->charMatches(enc, p, c)) +static int PTRCALL +sb_charMatches(const ENCODING *enc, const char *p, int c) +{ + return *p == c; +} +#else +/* c is an ASCII character */ +#define CHAR_MATCHES(enc, p, c) (*(p) == c) +#endif + +#define PREFIX(ident) normal_ ## ident +#define XML_TOK_IMPL_C +#include "xmltok_impl.inc" +#undef XML_TOK_IMPL_C + +#undef MINBPC +#undef BYTE_TYPE +#undef BYTE_TO_ASCII +#undef CHAR_MATCHES +#undef IS_NAME_CHAR +#undef IS_NAME_CHAR_MINBPC +#undef IS_NMSTRT_CHAR +#undef IS_NMSTRT_CHAR_MINBPC +#undef IS_INVALID_CHAR + +enum { /* UTF8_cvalN is value of masked first byte of N byte sequence */ + UTF8_cval1 = 0x00, + UTF8_cval2 = 0xc0, + UTF8_cval3 = 0xe0, + UTF8_cval4 = 0xf0 +}; + +void +align_limit_to_full_utf8_characters(const char * from, const char ** fromLimRef) +{ + const char * fromLim = *fromLimRef; + size_t walked = 0; + for (; fromLim > from; fromLim--, walked++) { + const unsigned char prev = (unsigned char)fromLim[-1]; + if ((prev & 0xf8u) == 0xf0u) { /* 4-byte character, lead by 0b11110xxx byte */ + if (walked + 1 >= 4) { + fromLim += 4 - 1; + break; + } else { + walked = 0; + } + } else if ((prev & 0xf0u) == 0xe0u) { /* 3-byte character, lead by 0b1110xxxx byte */ + if (walked + 1 >= 3) { + fromLim += 3 - 1; + break; + } else { + walked = 0; + } + } else if ((prev & 0xe0u) == 0xc0u) { /* 2-byte character, lead by 0b110xxxxx byte */ + if (walked + 1 >= 2) { + fromLim += 2 - 1; + break; + } else { + walked = 0; + } + } else if ((prev & 0x80u) == 0x00u) { /* 1-byte character, matching 0b0xxxxxxx */ + break; + } + } + *fromLimRef = fromLim; +} + +static enum XML_Convert_Result PTRCALL +utf8_toUtf8(const ENCODING *UNUSED_P(enc), + const char **fromP, const char *fromLim, + char **toP, const char *toLim) +{ + enum XML_Convert_Result res = XML_CONVERT_COMPLETED; + char *to; + const char *from; + if (fromLim - *fromP > toLim - *toP) { + /* Avoid copying partial characters. */ + res = XML_CONVERT_OUTPUT_EXHAUSTED; + fromLim = *fromP + (toLim - *toP); + align_limit_to_full_utf8_characters(*fromP, &fromLim); + } + for (to = *toP, from = *fromP; (from < fromLim) && (to < toLim); from++, to++) + *to = *from; + *fromP = from; + *toP = to; + + if ((to == toLim) && (from < fromLim)) + return XML_CONVERT_OUTPUT_EXHAUSTED; + else + return res; +} + +static enum XML_Convert_Result PTRCALL +utf8_toUtf16(const ENCODING *enc, + const char **fromP, const char *fromLim, + unsigned short **toP, const unsigned short *toLim) +{ + enum XML_Convert_Result res = XML_CONVERT_COMPLETED; + unsigned short *to = *toP; + const char *from = *fromP; + while (from < fromLim && to < toLim) { + switch (((struct normal_encoding *)enc)->type[(unsigned char)*from]) { + case BT_LEAD2: + if (fromLim - from < 2) { + res = XML_CONVERT_INPUT_INCOMPLETE; + break; + } + *to++ = (unsigned short)(((from[0] & 0x1f) << 6) | (from[1] & 0x3f)); + from += 2; + break; + case BT_LEAD3: + if (fromLim - from < 3) { + res = XML_CONVERT_INPUT_INCOMPLETE; + break; + } + *to++ = (unsigned short)(((from[0] & 0xf) << 12) + | ((from[1] & 0x3f) << 6) | (from[2] & 0x3f)); + from += 3; + break; + case BT_LEAD4: + { + unsigned long n; + if (toLim - to < 2) { + res = XML_CONVERT_OUTPUT_EXHAUSTED; + goto after; + } + if (fromLim - from < 4) { + res = XML_CONVERT_INPUT_INCOMPLETE; + goto after; + } + n = ((from[0] & 0x7) << 18) | ((from[1] & 0x3f) << 12) + | ((from[2] & 0x3f) << 6) | (from[3] & 0x3f); + n -= 0x10000; + to[0] = (unsigned short)((n >> 10) | 0xD800); + to[1] = (unsigned short)((n & 0x3FF) | 0xDC00); + to += 2; + from += 4; + } + break; + default: + *to++ = *from++; + break; + } + } +after: + *fromP = from; + *toP = to; + return res; +} + +#ifdef XML_NS +static const struct normal_encoding utf8_encoding_ns = { + { VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0 }, + { +#include "asciitab.h" +#include "utf8tab.h" + }, + STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_) +}; +#endif + +static const struct normal_encoding utf8_encoding = { + { VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0 }, + { +#define BT_COLON BT_NMSTRT +#include "asciitab.h" +#undef BT_COLON +#include "utf8tab.h" + }, + STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_) +}; + +#ifdef XML_NS + +static const struct normal_encoding internal_utf8_encoding_ns = { + { VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0 }, + { +#include "iasciitab.h" +#include "utf8tab.h" + }, + STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_) +}; + +#endif + +static const struct normal_encoding internal_utf8_encoding = { + { VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0 }, + { +#define BT_COLON BT_NMSTRT +#include "iasciitab.h" +#undef BT_COLON +#include "utf8tab.h" + }, + STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_) +}; + +static enum XML_Convert_Result PTRCALL +latin1_toUtf8(const ENCODING *UNUSED_P(enc), + const char **fromP, const char *fromLim, + char **toP, const char *toLim) +{ + for (;;) { + unsigned char c; + if (*fromP == fromLim) + return XML_CONVERT_COMPLETED; + c = (unsigned char)**fromP; + if (c & 0x80) { + if (toLim - *toP < 2) + return XML_CONVERT_OUTPUT_EXHAUSTED; + *(*toP)++ = (char)((c >> 6) | UTF8_cval2); + *(*toP)++ = (char)((c & 0x3f) | 0x80); + (*fromP)++; + } + else { + if (*toP == toLim) + return XML_CONVERT_OUTPUT_EXHAUSTED; + *(*toP)++ = *(*fromP)++; + } + } +} + +static enum XML_Convert_Result PTRCALL +latin1_toUtf16(const ENCODING *UNUSED_P(enc), + const char **fromP, const char *fromLim, + unsigned short **toP, const unsigned short *toLim) +{ + while (*fromP < fromLim && *toP < toLim) + *(*toP)++ = (unsigned char)*(*fromP)++; + + if ((*toP == toLim) && (*fromP < fromLim)) + return XML_CONVERT_OUTPUT_EXHAUSTED; + else + return XML_CONVERT_COMPLETED; +} + +#ifdef XML_NS + +static const struct normal_encoding latin1_encoding_ns = { + { VTABLE1, latin1_toUtf8, latin1_toUtf16, 1, 0, 0 }, + { +#include "asciitab.h" +#include "latin1tab.h" + }, + STANDARD_VTABLE(sb_) NULL_VTABLE +}; + +#endif + +static const struct normal_encoding latin1_encoding = { + { VTABLE1, latin1_toUtf8, latin1_toUtf16, 1, 0, 0 }, + { +#define BT_COLON BT_NMSTRT +#include "asciitab.h" +#undef BT_COLON +#include "latin1tab.h" + }, + STANDARD_VTABLE(sb_) NULL_VTABLE +}; + +static enum XML_Convert_Result PTRCALL +ascii_toUtf8(const ENCODING *UNUSED_P(enc), + const char **fromP, const char *fromLim, + char **toP, const char *toLim) +{ + while (*fromP < fromLim && *toP < toLim) + *(*toP)++ = *(*fromP)++; + + if ((*toP == toLim) && (*fromP < fromLim)) + return XML_CONVERT_OUTPUT_EXHAUSTED; + else + return XML_CONVERT_COMPLETED; +} + +#ifdef XML_NS + +static const struct normal_encoding ascii_encoding_ns = { + { VTABLE1, ascii_toUtf8, latin1_toUtf16, 1, 1, 0 }, + { +#include "asciitab.h" +/* BT_NONXML == 0 */ + }, + STANDARD_VTABLE(sb_) NULL_VTABLE +}; + +#endif + +static const struct normal_encoding ascii_encoding = { + { VTABLE1, ascii_toUtf8, latin1_toUtf16, 1, 1, 0 }, + { +#define BT_COLON BT_NMSTRT +#include "asciitab.h" +#undef BT_COLON +/* BT_NONXML == 0 */ + }, + STANDARD_VTABLE(sb_) NULL_VTABLE +}; + +static int PTRFASTCALL +unicode_byte_type(char hi, char lo) +{ + switch ((unsigned char)hi) { + case 0xD8: case 0xD9: case 0xDA: case 0xDB: + return BT_LEAD4; + case 0xDC: case 0xDD: case 0xDE: case 0xDF: + return BT_TRAIL; + case 0xFF: + switch ((unsigned char)lo) { + case 0xFF: + case 0xFE: + return BT_NONXML; + } + break; + } + return BT_NONASCII; +} + +#define DEFINE_UTF16_TO_UTF8(E) \ +static enum XML_Convert_Result PTRCALL \ +E ## toUtf8(const ENCODING *UNUSED_P(enc), \ + const char **fromP, const char *fromLim, \ + char **toP, const char *toLim) \ +{ \ + const char *from = *fromP; \ + fromLim = from + (((fromLim - from) >> 1) << 1); /* shrink to even */ \ + for (; from < fromLim; from += 2) { \ + int plane; \ + unsigned char lo2; \ + unsigned char lo = GET_LO(from); \ + unsigned char hi = GET_HI(from); \ + switch (hi) { \ + case 0: \ + if (lo < 0x80) { \ + if (*toP == toLim) { \ + *fromP = from; \ + return XML_CONVERT_OUTPUT_EXHAUSTED; \ + } \ + *(*toP)++ = lo; \ + break; \ + } \ + /* fall through */ \ + case 0x1: case 0x2: case 0x3: \ + case 0x4: case 0x5: case 0x6: case 0x7: \ + if (toLim - *toP < 2) { \ + *fromP = from; \ + return XML_CONVERT_OUTPUT_EXHAUSTED; \ + } \ + *(*toP)++ = ((lo >> 6) | (hi << 2) | UTF8_cval2); \ + *(*toP)++ = ((lo & 0x3f) | 0x80); \ + break; \ + default: \ + if (toLim - *toP < 3) { \ + *fromP = from; \ + return XML_CONVERT_OUTPUT_EXHAUSTED; \ + } \ + /* 16 bits divided 4, 6, 6 amongst 3 bytes */ \ + *(*toP)++ = ((hi >> 4) | UTF8_cval3); \ + *(*toP)++ = (((hi & 0xf) << 2) | (lo >> 6) | 0x80); \ + *(*toP)++ = ((lo & 0x3f) | 0x80); \ + break; \ + case 0xD8: case 0xD9: case 0xDA: case 0xDB: \ + if (toLim - *toP < 4) { \ + *fromP = from; \ + return XML_CONVERT_OUTPUT_EXHAUSTED; \ + } \ + if (fromLim - from < 4) { \ + *fromP = from; \ + return XML_CONVERT_INPUT_INCOMPLETE; \ + } \ + plane = (((hi & 0x3) << 2) | ((lo >> 6) & 0x3)) + 1; \ + *(*toP)++ = ((plane >> 2) | UTF8_cval4); \ + *(*toP)++ = (((lo >> 2) & 0xF) | ((plane & 0x3) << 4) | 0x80); \ + from += 2; \ + lo2 = GET_LO(from); \ + *(*toP)++ = (((lo & 0x3) << 4) \ + | ((GET_HI(from) & 0x3) << 2) \ + | (lo2 >> 6) \ + | 0x80); \ + *(*toP)++ = ((lo2 & 0x3f) | 0x80); \ + break; \ + } \ + } \ + *fromP = from; \ + if (from < fromLim) \ + return XML_CONVERT_INPUT_INCOMPLETE; \ + else \ + return XML_CONVERT_COMPLETED; \ +} + +#define DEFINE_UTF16_TO_UTF16(E) \ +static enum XML_Convert_Result PTRCALL \ +E ## toUtf16(const ENCODING *UNUSED_P(enc), \ + const char **fromP, const char *fromLim, \ + unsigned short **toP, const unsigned short *toLim) \ +{ \ + enum XML_Convert_Result res = XML_CONVERT_COMPLETED; \ + fromLim = *fromP + (((fromLim - *fromP) >> 1) << 1); /* shrink to even */ \ + /* Avoid copying first half only of surrogate */ \ + if (fromLim - *fromP > ((toLim - *toP) << 1) \ + && (GET_HI(fromLim - 2) & 0xF8) == 0xD8) { \ + fromLim -= 2; \ + res = XML_CONVERT_INPUT_INCOMPLETE; \ + } \ + for (; *fromP < fromLim && *toP < toLim; *fromP += 2) \ + *(*toP)++ = (GET_HI(*fromP) << 8) | GET_LO(*fromP); \ + if ((*toP == toLim) && (*fromP < fromLim)) \ + return XML_CONVERT_OUTPUT_EXHAUSTED; \ + else \ + return res; \ +} + +#define SET2(ptr, ch) \ + (((ptr)[0] = ((ch) & 0xff)), ((ptr)[1] = ((ch) >> 8))) +#define GET_LO(ptr) ((unsigned char)(ptr)[0]) +#define GET_HI(ptr) ((unsigned char)(ptr)[1]) + +DEFINE_UTF16_TO_UTF8(little2_) +DEFINE_UTF16_TO_UTF16(little2_) + +#undef SET2 +#undef GET_LO +#undef GET_HI + +#define SET2(ptr, ch) \ + (((ptr)[0] = ((ch) >> 8)), ((ptr)[1] = ((ch) & 0xFF))) +#define GET_LO(ptr) ((unsigned char)(ptr)[1]) +#define GET_HI(ptr) ((unsigned char)(ptr)[0]) + +DEFINE_UTF16_TO_UTF8(big2_) +DEFINE_UTF16_TO_UTF16(big2_) + +#undef SET2 +#undef GET_LO +#undef GET_HI + +#define LITTLE2_BYTE_TYPE(enc, p) \ + ((p)[1] == 0 \ + ? ((struct normal_encoding *)(enc))->type[(unsigned char)*(p)] \ + : unicode_byte_type((p)[1], (p)[0])) +#define LITTLE2_BYTE_TO_ASCII(enc, p) ((p)[1] == 0 ? (p)[0] : -1) +#define LITTLE2_CHAR_MATCHES(enc, p, c) ((p)[1] == 0 && (p)[0] == c) +#define LITTLE2_IS_NAME_CHAR_MINBPC(enc, p) \ + UCS2_GET_NAMING(namePages, (unsigned char)p[1], (unsigned char)p[0]) +#define LITTLE2_IS_NMSTRT_CHAR_MINBPC(enc, p) \ + UCS2_GET_NAMING(nmstrtPages, (unsigned char)p[1], (unsigned char)p[0]) + +#ifdef XML_MIN_SIZE + +static int PTRFASTCALL +little2_byteType(const ENCODING *enc, const char *p) +{ + return LITTLE2_BYTE_TYPE(enc, p); +} + +static int PTRFASTCALL +little2_byteToAscii(const ENCODING *enc, const char *p) +{ + return LITTLE2_BYTE_TO_ASCII(enc, p); +} + +static int PTRCALL +little2_charMatches(const ENCODING *enc, const char *p, int c) +{ + return LITTLE2_CHAR_MATCHES(enc, p, c); +} + +static int PTRFASTCALL +little2_isNameMin(const ENCODING *enc, const char *p) +{ + return LITTLE2_IS_NAME_CHAR_MINBPC(enc, p); +} + +static int PTRFASTCALL +little2_isNmstrtMin(const ENCODING *enc, const char *p) +{ + return LITTLE2_IS_NMSTRT_CHAR_MINBPC(enc, p); +} + +#undef VTABLE +#define VTABLE VTABLE1, little2_toUtf8, little2_toUtf16 + +#else /* not XML_MIN_SIZE */ + +#undef PREFIX +#define PREFIX(ident) little2_ ## ident +#define MINBPC(enc) 2 +/* CHAR_MATCHES is guaranteed to have MINBPC bytes available. */ +#define BYTE_TYPE(enc, p) LITTLE2_BYTE_TYPE(enc, p) +#define BYTE_TO_ASCII(enc, p) LITTLE2_BYTE_TO_ASCII(enc, p) +#define CHAR_MATCHES(enc, p, c) LITTLE2_CHAR_MATCHES(enc, p, c) +#define IS_NAME_CHAR(enc, p, n) 0 +#define IS_NAME_CHAR_MINBPC(enc, p) LITTLE2_IS_NAME_CHAR_MINBPC(enc, p) +#define IS_NMSTRT_CHAR(enc, p, n) (0) +#define IS_NMSTRT_CHAR_MINBPC(enc, p) LITTLE2_IS_NMSTRT_CHAR_MINBPC(enc, p) + +#define XML_TOK_IMPL_C +#include "xmltok_impl.inc" +#undef XML_TOK_IMPL_C + +#undef MINBPC +#undef BYTE_TYPE +#undef BYTE_TO_ASCII +#undef CHAR_MATCHES +#undef IS_NAME_CHAR +#undef IS_NAME_CHAR_MINBPC +#undef IS_NMSTRT_CHAR +#undef IS_NMSTRT_CHAR_MINBPC +#undef IS_INVALID_CHAR + +#endif /* not XML_MIN_SIZE */ + +#ifdef XML_NS + +static const struct normal_encoding little2_encoding_ns = { + { VTABLE, 2, 0, +#if BYTEORDER == 1234 + 1 +#else + 0 +#endif + }, + { +#include "asciitab.h" +#include "latin1tab.h" + }, + STANDARD_VTABLE(little2_) NULL_VTABLE +}; + +#endif + +static const struct normal_encoding little2_encoding = { + { VTABLE, 2, 0, +#if BYTEORDER == 1234 + 1 +#else + 0 +#endif + }, + { +#define BT_COLON BT_NMSTRT +#include "asciitab.h" +#undef BT_COLON +#include "latin1tab.h" + }, + STANDARD_VTABLE(little2_) NULL_VTABLE +}; + +#if BYTEORDER != 4321 + +#ifdef XML_NS + +static const struct normal_encoding internal_little2_encoding_ns = { + { VTABLE, 2, 0, 1 }, + { +#include "iasciitab.h" +#include "latin1tab.h" + }, + STANDARD_VTABLE(little2_) NULL_VTABLE +}; + +#endif + +static const struct normal_encoding internal_little2_encoding = { + { VTABLE, 2, 0, 1 }, + { +#define BT_COLON BT_NMSTRT +#include "iasciitab.h" +#undef BT_COLON +#include "latin1tab.h" + }, + STANDARD_VTABLE(little2_) NULL_VTABLE +}; + +#endif + + +#define BIG2_BYTE_TYPE(enc, p) \ + ((p)[0] == 0 \ + ? ((struct normal_encoding *)(enc))->type[(unsigned char)(p)[1]] \ + : unicode_byte_type((p)[0], (p)[1])) +#define BIG2_BYTE_TO_ASCII(enc, p) ((p)[0] == 0 ? (p)[1] : -1) +#define BIG2_CHAR_MATCHES(enc, p, c) ((p)[0] == 0 && (p)[1] == c) +#define BIG2_IS_NAME_CHAR_MINBPC(enc, p) \ + UCS2_GET_NAMING(namePages, (unsigned char)p[0], (unsigned char)p[1]) +#define BIG2_IS_NMSTRT_CHAR_MINBPC(enc, p) \ + UCS2_GET_NAMING(nmstrtPages, (unsigned char)p[0], (unsigned char)p[1]) + +#ifdef XML_MIN_SIZE + +static int PTRFASTCALL +big2_byteType(const ENCODING *enc, const char *p) +{ + return BIG2_BYTE_TYPE(enc, p); +} + +static int PTRFASTCALL +big2_byteToAscii(const ENCODING *enc, const char *p) +{ + return BIG2_BYTE_TO_ASCII(enc, p); +} + +static int PTRCALL +big2_charMatches(const ENCODING *enc, const char *p, int c) +{ + return BIG2_CHAR_MATCHES(enc, p, c); +} + +static int PTRFASTCALL +big2_isNameMin(const ENCODING *enc, const char *p) +{ + return BIG2_IS_NAME_CHAR_MINBPC(enc, p); +} + +static int PTRFASTCALL +big2_isNmstrtMin(const ENCODING *enc, const char *p) +{ + return BIG2_IS_NMSTRT_CHAR_MINBPC(enc, p); +} + +#undef VTABLE +#define VTABLE VTABLE1, big2_toUtf8, big2_toUtf16 + +#else /* not XML_MIN_SIZE */ + +#undef PREFIX +#define PREFIX(ident) big2_ ## ident +#define MINBPC(enc) 2 +/* CHAR_MATCHES is guaranteed to have MINBPC bytes available. */ +#define BYTE_TYPE(enc, p) BIG2_BYTE_TYPE(enc, p) +#define BYTE_TO_ASCII(enc, p) BIG2_BYTE_TO_ASCII(enc, p) +#define CHAR_MATCHES(enc, p, c) BIG2_CHAR_MATCHES(enc, p, c) +#define IS_NAME_CHAR(enc, p, n) 0 +#define IS_NAME_CHAR_MINBPC(enc, p) BIG2_IS_NAME_CHAR_MINBPC(enc, p) +#define IS_NMSTRT_CHAR(enc, p, n) (0) +#define IS_NMSTRT_CHAR_MINBPC(enc, p) BIG2_IS_NMSTRT_CHAR_MINBPC(enc, p) + +#define XML_TOK_IMPL_C +#include "xmltok_impl.inc" +#undef XML_TOK_IMPL_C + +#undef MINBPC +#undef BYTE_TYPE +#undef BYTE_TO_ASCII +#undef CHAR_MATCHES +#undef IS_NAME_CHAR +#undef IS_NAME_CHAR_MINBPC +#undef IS_NMSTRT_CHAR +#undef IS_NMSTRT_CHAR_MINBPC +#undef IS_INVALID_CHAR + +#endif /* not XML_MIN_SIZE */ + +#ifdef XML_NS + +static const struct normal_encoding big2_encoding_ns = { + { VTABLE, 2, 0, +#if BYTEORDER == 4321 + 1 +#else + 0 +#endif + }, + { +#include "asciitab.h" +#include "latin1tab.h" + }, + STANDARD_VTABLE(big2_) NULL_VTABLE +}; + +#endif + +static const struct normal_encoding big2_encoding = { + { VTABLE, 2, 0, +#if BYTEORDER == 4321 + 1 +#else + 0 +#endif + }, + { +#define BT_COLON BT_NMSTRT +#include "asciitab.h" +#undef BT_COLON +#include "latin1tab.h" + }, + STANDARD_VTABLE(big2_) NULL_VTABLE +}; + +#if BYTEORDER != 1234 + +#ifdef XML_NS + +static const struct normal_encoding internal_big2_encoding_ns = { + { VTABLE, 2, 0, 1 }, + { +#include "iasciitab.h" +#include "latin1tab.h" + }, + STANDARD_VTABLE(big2_) NULL_VTABLE +}; + +#endif + +static const struct normal_encoding internal_big2_encoding = { + { VTABLE, 2, 0, 1 }, + { +#define BT_COLON BT_NMSTRT +#include "iasciitab.h" +#undef BT_COLON +#include "latin1tab.h" + }, + STANDARD_VTABLE(big2_) NULL_VTABLE +}; + +#endif + +#undef PREFIX + +static int FASTCALL +streqci(const char *s1, const char *s2) +{ + for (;;) { + char c1 = *s1++; + char c2 = *s2++; + if (ASCII_a <= c1 && c1 <= ASCII_z) + c1 += ASCII_A - ASCII_a; + if (ASCII_a <= c2 && c2 <= ASCII_z) + c2 += ASCII_A - ASCII_a; + if (c1 != c2) + return 0; + if (!c1) + break; + } + return 1; +} + +static void PTRCALL +initUpdatePosition(const ENCODING *UNUSED_P(enc), const char *ptr, + const char *end, POSITION *pos) +{ + normal_updatePosition(&utf8_encoding.enc, ptr, end, pos); +} + +static int +toAscii(const ENCODING *enc, const char *ptr, const char *end) +{ + char buf[1]; + char *p = buf; + XmlUtf8Convert(enc, &ptr, end, &p, p + 1); + if (p == buf) + return -1; + else + return buf[0]; +} + +static int FASTCALL +isSpace(int c) +{ + switch (c) { + case 0x20: + case 0xD: + case 0xA: + case 0x9: + return 1; + } + return 0; +} + +/* Return 1 if there's just optional white space or there's an S + followed by name=val. +*/ +static int +parsePseudoAttribute(const ENCODING *enc, + const char *ptr, + const char *end, + const char **namePtr, + const char **nameEndPtr, + const char **valPtr, + const char **nextTokPtr) +{ + int c; + char open; + if (ptr == end) { + *namePtr = NULL; + return 1; + } + if (!isSpace(toAscii(enc, ptr, end))) { + *nextTokPtr = ptr; + return 0; + } + do { + ptr += enc->minBytesPerChar; + } while (isSpace(toAscii(enc, ptr, end))); + if (ptr == end) { + *namePtr = NULL; + return 1; + } + *namePtr = ptr; + for (;;) { + c = toAscii(enc, ptr, end); + if (c == -1) { + *nextTokPtr = ptr; + return 0; + } + if (c == ASCII_EQUALS) { + *nameEndPtr = ptr; + break; + } + if (isSpace(c)) { + *nameEndPtr = ptr; + do { + ptr += enc->minBytesPerChar; + } while (isSpace(c = toAscii(enc, ptr, end))); + if (c != ASCII_EQUALS) { + *nextTokPtr = ptr; + return 0; + } + break; + } + ptr += enc->minBytesPerChar; + } + if (ptr == *namePtr) { + *nextTokPtr = ptr; + return 0; + } + ptr += enc->minBytesPerChar; + c = toAscii(enc, ptr, end); + while (isSpace(c)) { + ptr += enc->minBytesPerChar; + c = toAscii(enc, ptr, end); + } + if (c != ASCII_QUOT && c != ASCII_APOS) { + *nextTokPtr = ptr; + return 0; + } + open = (char)c; + ptr += enc->minBytesPerChar; + *valPtr = ptr; + for (;; ptr += enc->minBytesPerChar) { + c = toAscii(enc, ptr, end); + if (c == open) + break; + if (!(ASCII_a <= c && c <= ASCII_z) + && !(ASCII_A <= c && c <= ASCII_Z) + && !(ASCII_0 <= c && c <= ASCII_9) + && c != ASCII_PERIOD + && c != ASCII_MINUS + && c != ASCII_UNDERSCORE) { + *nextTokPtr = ptr; + return 0; + } + } + *nextTokPtr = ptr + enc->minBytesPerChar; + return 1; +} + +static const char KW_version[] = { + ASCII_v, ASCII_e, ASCII_r, ASCII_s, ASCII_i, ASCII_o, ASCII_n, '\0' +}; + +static const char KW_encoding[] = { + ASCII_e, ASCII_n, ASCII_c, ASCII_o, ASCII_d, ASCII_i, ASCII_n, ASCII_g, '\0' +}; + +static const char KW_standalone[] = { + ASCII_s, ASCII_t, ASCII_a, ASCII_n, ASCII_d, ASCII_a, ASCII_l, ASCII_o, + ASCII_n, ASCII_e, '\0' +}; + +static const char KW_yes[] = { + ASCII_y, ASCII_e, ASCII_s, '\0' +}; + +static const char KW_no[] = { + ASCII_n, ASCII_o, '\0' +}; + +static int +doParseXmlDecl(const ENCODING *(*encodingFinder)(const ENCODING *, + const char *, + const char *), + int isGeneralTextEntity, + const ENCODING *enc, + const char *ptr, + const char *end, + const char **badPtr, + const char **versionPtr, + const char **versionEndPtr, + const char **encodingName, + const ENCODING **encoding, + int *standalone) +{ + const char *val = NULL; + const char *name = NULL; + const char *nameEnd = NULL; + ptr += 5 * enc->minBytesPerChar; + end -= 2 * enc->minBytesPerChar; + if (!parsePseudoAttribute(enc, ptr, end, &name, &nameEnd, &val, &ptr) + || !name) { + *badPtr = ptr; + return 0; + } + if (!XmlNameMatchesAscii(enc, name, nameEnd, KW_version)) { + if (!isGeneralTextEntity) { + *badPtr = name; + return 0; + } + } + else { + if (versionPtr) + *versionPtr = val; + if (versionEndPtr) + *versionEndPtr = ptr; + if (!parsePseudoAttribute(enc, ptr, end, &name, &nameEnd, &val, &ptr)) { + *badPtr = ptr; + return 0; + } + if (!name) { + if (isGeneralTextEntity) { + /* a TextDecl must have an EncodingDecl */ + *badPtr = ptr; + return 0; + } + return 1; + } + } + if (XmlNameMatchesAscii(enc, name, nameEnd, KW_encoding)) { + int c = toAscii(enc, val, end); + if (!(ASCII_a <= c && c <= ASCII_z) && !(ASCII_A <= c && c <= ASCII_Z)) { + *badPtr = val; + return 0; + } + if (encodingName) + *encodingName = val; + if (encoding) + *encoding = encodingFinder(enc, val, ptr - enc->minBytesPerChar); + if (!parsePseudoAttribute(enc, ptr, end, &name, &nameEnd, &val, &ptr)) { + *badPtr = ptr; + return 0; + } + if (!name) + return 1; + } + if (!XmlNameMatchesAscii(enc, name, nameEnd, KW_standalone) + || isGeneralTextEntity) { + *badPtr = name; + return 0; + } + if (XmlNameMatchesAscii(enc, val, ptr - enc->minBytesPerChar, KW_yes)) { + if (standalone) + *standalone = 1; + } + else if (XmlNameMatchesAscii(enc, val, ptr - enc->minBytesPerChar, KW_no)) { + if (standalone) + *standalone = 0; + } + else { + *badPtr = val; + return 0; + } + while (isSpace(toAscii(enc, ptr, end))) + ptr += enc->minBytesPerChar; + if (ptr != end) { + *badPtr = ptr; + return 0; + } + return 1; +} + +static int FASTCALL +checkCharRefNumber(int result) +{ + switch (result >> 8) { + case 0xD8: case 0xD9: case 0xDA: case 0xDB: + case 0xDC: case 0xDD: case 0xDE: case 0xDF: + return -1; + case 0: + if (latin1_encoding.type[result] == BT_NONXML) + return -1; + break; + case 0xFF: + if (result == 0xFFFE || result == 0xFFFF) + return -1; + break; + } + return result; +} + +int FASTCALL +XmlUtf8Encode(int c, char *buf) +{ + enum { + /* minN is minimum legal resulting value for N byte sequence */ + min2 = 0x80, + min3 = 0x800, + min4 = 0x10000 + }; + + if (c < 0) + return 0; + if (c < min2) { + buf[0] = (char)(c | UTF8_cval1); + return 1; + } + if (c < min3) { + buf[0] = (char)((c >> 6) | UTF8_cval2); + buf[1] = (char)((c & 0x3f) | 0x80); + return 2; + } + if (c < min4) { + buf[0] = (char)((c >> 12) | UTF8_cval3); + buf[1] = (char)(((c >> 6) & 0x3f) | 0x80); + buf[2] = (char)((c & 0x3f) | 0x80); + return 3; + } + if (c < 0x110000) { + buf[0] = (char)((c >> 18) | UTF8_cval4); + buf[1] = (char)(((c >> 12) & 0x3f) | 0x80); + buf[2] = (char)(((c >> 6) & 0x3f) | 0x80); + buf[3] = (char)((c & 0x3f) | 0x80); + return 4; + } + return 0; +} + +int FASTCALL +XmlUtf16Encode(int charNum, unsigned short *buf) +{ + if (charNum < 0) + return 0; + if (charNum < 0x10000) { + buf[0] = (unsigned short)charNum; + return 1; + } + if (charNum < 0x110000) { + charNum -= 0x10000; + buf[0] = (unsigned short)((charNum >> 10) + 0xD800); + buf[1] = (unsigned short)((charNum & 0x3FF) + 0xDC00); + return 2; + } + return 0; +} + +struct unknown_encoding { + struct normal_encoding normal; + CONVERTER convert; + void *userData; + unsigned short utf16[256]; + char utf8[256][4]; +}; + +#define AS_UNKNOWN_ENCODING(enc) ((const struct unknown_encoding *) (enc)) + +int +XmlSizeOfUnknownEncoding(void) +{ + return sizeof(struct unknown_encoding); +} + +static int PTRFASTCALL +unknown_isName(const ENCODING *enc, const char *p) +{ + const struct unknown_encoding *uenc = AS_UNKNOWN_ENCODING(enc); + int c = uenc->convert(uenc->userData, p); + if (c & ~0xFFFF) + return 0; + return UCS2_GET_NAMING(namePages, c >> 8, c & 0xFF); +} + +static int PTRFASTCALL +unknown_isNmstrt(const ENCODING *enc, const char *p) +{ + const struct unknown_encoding *uenc = AS_UNKNOWN_ENCODING(enc); + int c = uenc->convert(uenc->userData, p); + if (c & ~0xFFFF) + return 0; + return UCS2_GET_NAMING(nmstrtPages, c >> 8, c & 0xFF); +} + +static int PTRFASTCALL +unknown_isInvalid(const ENCODING *enc, const char *p) +{ + const struct unknown_encoding *uenc = AS_UNKNOWN_ENCODING(enc); + int c = uenc->convert(uenc->userData, p); + return (c & ~0xFFFF) || checkCharRefNumber(c) < 0; +} + +static enum XML_Convert_Result PTRCALL +unknown_toUtf8(const ENCODING *enc, + const char **fromP, const char *fromLim, + char **toP, const char *toLim) +{ + const struct unknown_encoding *uenc = AS_UNKNOWN_ENCODING(enc); + char buf[XML_UTF8_ENCODE_MAX]; + for (;;) { + const char *utf8; + int n; + if (*fromP == fromLim) + return XML_CONVERT_COMPLETED; + utf8 = uenc->utf8[(unsigned char)**fromP]; + n = *utf8++; + if (n == 0) { + int c = uenc->convert(uenc->userData, *fromP); + n = XmlUtf8Encode(c, buf); + if (n > toLim - *toP) + return XML_CONVERT_OUTPUT_EXHAUSTED; + utf8 = buf; + *fromP += (AS_NORMAL_ENCODING(enc)->type[(unsigned char)**fromP] + - (BT_LEAD2 - 2)); + } + else { + if (n > toLim - *toP) + return XML_CONVERT_OUTPUT_EXHAUSTED; + (*fromP)++; + } + do { + *(*toP)++ = *utf8++; + } while (--n != 0); + } +} + +static enum XML_Convert_Result PTRCALL +unknown_toUtf16(const ENCODING *enc, + const char **fromP, const char *fromLim, + unsigned short **toP, const unsigned short *toLim) +{ + const struct unknown_encoding *uenc = AS_UNKNOWN_ENCODING(enc); + while (*fromP < fromLim && *toP < toLim) { + unsigned short c = uenc->utf16[(unsigned char)**fromP]; + if (c == 0) { + c = (unsigned short) + uenc->convert(uenc->userData, *fromP); + *fromP += (AS_NORMAL_ENCODING(enc)->type[(unsigned char)**fromP] + - (BT_LEAD2 - 2)); + } + else + (*fromP)++; + *(*toP)++ = c; + } + + if ((*toP == toLim) && (*fromP < fromLim)) + return XML_CONVERT_OUTPUT_EXHAUSTED; + else + return XML_CONVERT_COMPLETED; +} + +ENCODING * +XmlInitUnknownEncoding(void *mem, + int *table, + CONVERTER convert, + void *userData) +{ + int i; + struct unknown_encoding *e = (struct unknown_encoding *)mem; + for (i = 0; i < (int)sizeof(struct normal_encoding); i++) + ((char *)mem)[i] = ((char *)&latin1_encoding)[i]; + for (i = 0; i < 128; i++) + if (latin1_encoding.type[i] != BT_OTHER + && latin1_encoding.type[i] != BT_NONXML + && table[i] != i) + return 0; + for (i = 0; i < 256; i++) { + int c = table[i]; + if (c == -1) { + e->normal.type[i] = BT_MALFORM; + /* This shouldn't really get used. */ + e->utf16[i] = 0xFFFF; + e->utf8[i][0] = 1; + e->utf8[i][1] = 0; + } + else if (c < 0) { + if (c < -4) + return 0; + e->normal.type[i] = (unsigned char)(BT_LEAD2 - (c + 2)); + e->utf8[i][0] = 0; + e->utf16[i] = 0; + } + else if (c < 0x80) { + if (latin1_encoding.type[c] != BT_OTHER + && latin1_encoding.type[c] != BT_NONXML + && c != i) + return 0; + e->normal.type[i] = latin1_encoding.type[c]; + e->utf8[i][0] = 1; + e->utf8[i][1] = (char)c; + e->utf16[i] = (unsigned short)(c == 0 ? 0xFFFF : c); + } + else if (checkCharRefNumber(c) < 0) { + e->normal.type[i] = BT_NONXML; + /* This shouldn't really get used. */ + e->utf16[i] = 0xFFFF; + e->utf8[i][0] = 1; + e->utf8[i][1] = 0; + } + else { + if (c > 0xFFFF) + return 0; + if (UCS2_GET_NAMING(nmstrtPages, c >> 8, c & 0xff)) + e->normal.type[i] = BT_NMSTRT; + else if (UCS2_GET_NAMING(namePages, c >> 8, c & 0xff)) + e->normal.type[i] = BT_NAME; + else + e->normal.type[i] = BT_OTHER; + e->utf8[i][0] = (char)XmlUtf8Encode(c, e->utf8[i] + 1); + e->utf16[i] = (unsigned short)c; + } + } + e->userData = userData; + e->convert = convert; + if (convert) { + e->normal.isName2 = unknown_isName; + e->normal.isName3 = unknown_isName; + e->normal.isName4 = unknown_isName; + e->normal.isNmstrt2 = unknown_isNmstrt; + e->normal.isNmstrt3 = unknown_isNmstrt; + e->normal.isNmstrt4 = unknown_isNmstrt; + e->normal.isInvalid2 = unknown_isInvalid; + e->normal.isInvalid3 = unknown_isInvalid; + e->normal.isInvalid4 = unknown_isInvalid; + } + e->normal.enc.utf8Convert = unknown_toUtf8; + e->normal.enc.utf16Convert = unknown_toUtf16; + return &(e->normal.enc); +} + +/* If this enumeration is changed, getEncodingIndex and encodings +must also be changed. */ +enum { + UNKNOWN_ENC = -1, + ISO_8859_1_ENC = 0, + US_ASCII_ENC, + UTF_8_ENC, + UTF_16_ENC, + UTF_16BE_ENC, + UTF_16LE_ENC, + /* must match encodingNames up to here */ + NO_ENC +}; + +static const char KW_ISO_8859_1[] = { + ASCII_I, ASCII_S, ASCII_O, ASCII_MINUS, ASCII_8, ASCII_8, ASCII_5, ASCII_9, + ASCII_MINUS, ASCII_1, '\0' +}; +static const char KW_US_ASCII[] = { + ASCII_U, ASCII_S, ASCII_MINUS, ASCII_A, ASCII_S, ASCII_C, ASCII_I, ASCII_I, + '\0' +}; +static const char KW_UTF_8[] = { + ASCII_U, ASCII_T, ASCII_F, ASCII_MINUS, ASCII_8, '\0' +}; +static const char KW_UTF_16[] = { + ASCII_U, ASCII_T, ASCII_F, ASCII_MINUS, ASCII_1, ASCII_6, '\0' +}; +static const char KW_UTF_16BE[] = { + ASCII_U, ASCII_T, ASCII_F, ASCII_MINUS, ASCII_1, ASCII_6, ASCII_B, ASCII_E, + '\0' +}; +static const char KW_UTF_16LE[] = { + ASCII_U, ASCII_T, ASCII_F, ASCII_MINUS, ASCII_1, ASCII_6, ASCII_L, ASCII_E, + '\0' +}; + +static int FASTCALL +getEncodingIndex(const char *name) +{ + static const char * const encodingNames[] = { + KW_ISO_8859_1, + KW_US_ASCII, + KW_UTF_8, + KW_UTF_16, + KW_UTF_16BE, + KW_UTF_16LE, + }; + int i; + if (name == NULL) + return NO_ENC; + for (i = 0; i < (int)(sizeof(encodingNames)/sizeof(encodingNames[0])); i++) + if (streqci(name, encodingNames[i])) + return i; + return UNKNOWN_ENC; +} + +/* For binary compatibility, we store the index of the encoding + specified at initialization in the isUtf16 member. +*/ + +#define INIT_ENC_INDEX(enc) ((int)(enc)->initEnc.isUtf16) +#define SET_INIT_ENC_INDEX(enc, i) ((enc)->initEnc.isUtf16 = (char)i) + +/* This is what detects the encoding. encodingTable maps from + encoding indices to encodings; INIT_ENC_INDEX(enc) is the index of + the external (protocol) specified encoding; state is + XML_CONTENT_STATE if we're parsing an external text entity, and + XML_PROLOG_STATE otherwise. +*/ + + +static int +initScan(const ENCODING * const *encodingTable, + const INIT_ENCODING *enc, + int state, + const char *ptr, + const char *end, + const char **nextTokPtr) +{ + const ENCODING **encPtr; + + if (ptr >= end) + return XML_TOK_NONE; + encPtr = enc->encPtr; + if (ptr + 1 == end) { + /* only a single byte available for auto-detection */ +#ifndef XML_DTD /* FIXME */ + /* a well-formed document entity must have more than one byte */ + if (state != XML_CONTENT_STATE) + return XML_TOK_PARTIAL; +#endif + /* so we're parsing an external text entity... */ + /* if UTF-16 was externally specified, then we need at least 2 bytes */ + switch (INIT_ENC_INDEX(enc)) { + case UTF_16_ENC: + case UTF_16LE_ENC: + case UTF_16BE_ENC: + return XML_TOK_PARTIAL; + } + switch ((unsigned char)*ptr) { + case 0xFE: + case 0xFF: + case 0xEF: /* possibly first byte of UTF-8 BOM */ + if (INIT_ENC_INDEX(enc) == ISO_8859_1_ENC + && state == XML_CONTENT_STATE) + break; + /* fall through */ + case 0x00: + case 0x3C: + return XML_TOK_PARTIAL; + } + } + else { + switch (((unsigned char)ptr[0] << 8) | (unsigned char)ptr[1]) { + case 0xFEFF: + if (INIT_ENC_INDEX(enc) == ISO_8859_1_ENC + && state == XML_CONTENT_STATE) + break; + *nextTokPtr = ptr + 2; + *encPtr = encodingTable[UTF_16BE_ENC]; + return XML_TOK_BOM; + /* 00 3C is handled in the default case */ + case 0x3C00: + if ((INIT_ENC_INDEX(enc) == UTF_16BE_ENC + || INIT_ENC_INDEX(enc) == UTF_16_ENC) + && state == XML_CONTENT_STATE) + break; + *encPtr = encodingTable[UTF_16LE_ENC]; + return XmlTok(*encPtr, state, ptr, end, nextTokPtr); + case 0xFFFE: + if (INIT_ENC_INDEX(enc) == ISO_8859_1_ENC + && state == XML_CONTENT_STATE) + break; + *nextTokPtr = ptr + 2; + *encPtr = encodingTable[UTF_16LE_ENC]; + return XML_TOK_BOM; + case 0xEFBB: + /* Maybe a UTF-8 BOM (EF BB BF) */ + /* If there's an explicitly specified (external) encoding + of ISO-8859-1 or some flavour of UTF-16 + and this is an external text entity, + don't look for the BOM, + because it might be a legal data. + */ + if (state == XML_CONTENT_STATE) { + int e = INIT_ENC_INDEX(enc); + if (e == ISO_8859_1_ENC || e == UTF_16BE_ENC + || e == UTF_16LE_ENC || e == UTF_16_ENC) + break; + } + if (ptr + 2 == end) + return XML_TOK_PARTIAL; + if ((unsigned char)ptr[2] == 0xBF) { + *nextTokPtr = ptr + 3; + *encPtr = encodingTable[UTF_8_ENC]; + return XML_TOK_BOM; + } + break; + default: + if (ptr[0] == '\0') { + /* 0 isn't a legal data character. Furthermore a document + entity can only start with ASCII characters. So the only + way this can fail to be big-endian UTF-16 if it it's an + external parsed general entity that's labelled as + UTF-16LE. + */ + if (state == XML_CONTENT_STATE && INIT_ENC_INDEX(enc) == UTF_16LE_ENC) + break; + *encPtr = encodingTable[UTF_16BE_ENC]; + return XmlTok(*encPtr, state, ptr, end, nextTokPtr); + } + else if (ptr[1] == '\0') { + /* We could recover here in the case: + - parsing an external entity + - second byte is 0 + - no externally specified encoding + - no encoding declaration + by assuming UTF-16LE. But we don't, because this would mean when + presented just with a single byte, we couldn't reliably determine + whether we needed further bytes. + */ + if (state == XML_CONTENT_STATE) + break; + *encPtr = encodingTable[UTF_16LE_ENC]; + return XmlTok(*encPtr, state, ptr, end, nextTokPtr); + } + break; + } + } + *encPtr = encodingTable[INIT_ENC_INDEX(enc)]; + return XmlTok(*encPtr, state, ptr, end, nextTokPtr); +} + + +#define NS(x) x +#define ns(x) x +#define XML_TOK_NS_C +#include "xmltok_ns.inc" +#undef XML_TOK_NS_C +#undef NS +#undef ns + +#ifdef XML_NS + +#define NS(x) x ## NS +#define ns(x) x ## _ns + +#define XML_TOK_NS_C +#include "xmltok_ns.inc" +#undef XML_TOK_NS_C + +#undef NS +#undef ns + +ENCODING * +XmlInitUnknownEncodingNS(void *mem, + int *table, + CONVERTER convert, + void *userData) +{ + ENCODING *enc = XmlInitUnknownEncoding(mem, table, convert, userData); + if (enc) + ((struct normal_encoding *)enc)->type[ASCII_COLON] = BT_COLON; + return enc; +} + +#endif /* XML_NS */ diff --git a/deps/EXPAT/expat/xmltok.h b/deps/EXPAT/expat/xmltok.h new file mode 100644 index 000000000..752007e8b --- /dev/null +++ b/deps/EXPAT/expat/xmltok.h @@ -0,0 +1,322 @@ +/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +#ifndef XmlTok_INCLUDED +#define XmlTok_INCLUDED 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* The following token may be returned by XmlContentTok */ +#define XML_TOK_TRAILING_RSQB -5 /* ] or ]] at the end of the scan; might be + start of illegal ]]> sequence */ +/* The following tokens may be returned by both XmlPrologTok and + XmlContentTok. +*/ +#define XML_TOK_NONE -4 /* The string to be scanned is empty */ +#define XML_TOK_TRAILING_CR -3 /* A CR at the end of the scan; + might be part of CRLF sequence */ +#define XML_TOK_PARTIAL_CHAR -2 /* only part of a multibyte sequence */ +#define XML_TOK_PARTIAL -1 /* only part of a token */ +#define XML_TOK_INVALID 0 + +/* The following tokens are returned by XmlContentTok; some are also + returned by XmlAttributeValueTok, XmlEntityTok, XmlCdataSectionTok. +*/ +#define XML_TOK_START_TAG_WITH_ATTS 1 +#define XML_TOK_START_TAG_NO_ATTS 2 +#define XML_TOK_EMPTY_ELEMENT_WITH_ATTS 3 /* empty element tag */ +#define XML_TOK_EMPTY_ELEMENT_NO_ATTS 4 +#define XML_TOK_END_TAG 5 +#define XML_TOK_DATA_CHARS 6 +#define XML_TOK_DATA_NEWLINE 7 +#define XML_TOK_CDATA_SECT_OPEN 8 +#define XML_TOK_ENTITY_REF 9 +#define XML_TOK_CHAR_REF 10 /* numeric character reference */ + +/* The following tokens may be returned by both XmlPrologTok and + XmlContentTok. +*/ +#define XML_TOK_PI 11 /* processing instruction */ +#define XML_TOK_XML_DECL 12 /* XML decl or text decl */ +#define XML_TOK_COMMENT 13 +#define XML_TOK_BOM 14 /* Byte order mark */ + +/* The following tokens are returned only by XmlPrologTok */ +#define XML_TOK_PROLOG_S 15 +#define XML_TOK_DECL_OPEN 16 /* */ +#define XML_TOK_NAME 18 +#define XML_TOK_NMTOKEN 19 +#define XML_TOK_POUND_NAME 20 /* #name */ +#define XML_TOK_OR 21 /* | */ +#define XML_TOK_PERCENT 22 +#define XML_TOK_OPEN_PAREN 23 +#define XML_TOK_CLOSE_PAREN 24 +#define XML_TOK_OPEN_BRACKET 25 +#define XML_TOK_CLOSE_BRACKET 26 +#define XML_TOK_LITERAL 27 +#define XML_TOK_PARAM_ENTITY_REF 28 +#define XML_TOK_INSTANCE_START 29 + +/* The following occur only in element type declarations */ +#define XML_TOK_NAME_QUESTION 30 /* name? */ +#define XML_TOK_NAME_ASTERISK 31 /* name* */ +#define XML_TOK_NAME_PLUS 32 /* name+ */ +#define XML_TOK_COND_SECT_OPEN 33 /* */ +#define XML_TOK_CLOSE_PAREN_QUESTION 35 /* )? */ +#define XML_TOK_CLOSE_PAREN_ASTERISK 36 /* )* */ +#define XML_TOK_CLOSE_PAREN_PLUS 37 /* )+ */ +#define XML_TOK_COMMA 38 + +/* The following token is returned only by XmlAttributeValueTok */ +#define XML_TOK_ATTRIBUTE_VALUE_S 39 + +/* The following token is returned only by XmlCdataSectionTok */ +#define XML_TOK_CDATA_SECT_CLOSE 40 + +/* With namespace processing this is returned by XmlPrologTok for a + name with a colon. +*/ +#define XML_TOK_PREFIXED_NAME 41 + +#ifdef XML_DTD +#define XML_TOK_IGNORE_SECT 42 +#endif /* XML_DTD */ + +#ifdef XML_DTD +#define XML_N_STATES 4 +#else /* not XML_DTD */ +#define XML_N_STATES 3 +#endif /* not XML_DTD */ + +#define XML_PROLOG_STATE 0 +#define XML_CONTENT_STATE 1 +#define XML_CDATA_SECTION_STATE 2 +#ifdef XML_DTD +#define XML_IGNORE_SECTION_STATE 3 +#endif /* XML_DTD */ + +#define XML_N_LITERAL_TYPES 2 +#define XML_ATTRIBUTE_VALUE_LITERAL 0 +#define XML_ENTITY_VALUE_LITERAL 1 + +/* The size of the buffer passed to XmlUtf8Encode must be at least this. */ +#define XML_UTF8_ENCODE_MAX 4 +/* The size of the buffer passed to XmlUtf16Encode must be at least this. */ +#define XML_UTF16_ENCODE_MAX 2 + +typedef struct position { + /* first line and first column are 0 not 1 */ + XML_Size lineNumber; + XML_Size columnNumber; +} POSITION; + +typedef struct { + const char *name; + const char *valuePtr; + const char *valueEnd; + char normalized; +} ATTRIBUTE; + +struct encoding; +typedef struct encoding ENCODING; + +typedef int (PTRCALL *SCANNER)(const ENCODING *, + const char *, + const char *, + const char **); + +enum XML_Convert_Result { + XML_CONVERT_COMPLETED = 0, + XML_CONVERT_INPUT_INCOMPLETE = 1, + XML_CONVERT_OUTPUT_EXHAUSTED = 2 /* and therefore potentially input remaining as well */ +}; + +struct encoding { + SCANNER scanners[XML_N_STATES]; + SCANNER literalScanners[XML_N_LITERAL_TYPES]; + int (PTRCALL *sameName)(const ENCODING *, + const char *, + const char *); + int (PTRCALL *nameMatchesAscii)(const ENCODING *, + const char *, + const char *, + const char *); + int (PTRFASTCALL *nameLength)(const ENCODING *, const char *); + const char *(PTRFASTCALL *skipS)(const ENCODING *, const char *); + int (PTRCALL *getAtts)(const ENCODING *enc, + const char *ptr, + int attsMax, + ATTRIBUTE *atts); + int (PTRFASTCALL *charRefNumber)(const ENCODING *enc, const char *ptr); + int (PTRCALL *predefinedEntityName)(const ENCODING *, + const char *, + const char *); + void (PTRCALL *updatePosition)(const ENCODING *, + const char *ptr, + const char *end, + POSITION *); + int (PTRCALL *isPublicId)(const ENCODING *enc, + const char *ptr, + const char *end, + const char **badPtr); + enum XML_Convert_Result (PTRCALL *utf8Convert)(const ENCODING *enc, + const char **fromP, + const char *fromLim, + char **toP, + const char *toLim); + enum XML_Convert_Result (PTRCALL *utf16Convert)(const ENCODING *enc, + const char **fromP, + const char *fromLim, + unsigned short **toP, + const unsigned short *toLim); + int minBytesPerChar; + char isUtf8; + char isUtf16; +}; + +/* Scan the string starting at ptr until the end of the next complete + token, but do not scan past eptr. Return an integer giving the + type of token. + + Return XML_TOK_NONE when ptr == eptr; nextTokPtr will not be set. + + Return XML_TOK_PARTIAL when the string does not contain a complete + token; nextTokPtr will not be set. + + Return XML_TOK_INVALID when the string does not start a valid + token; nextTokPtr will be set to point to the character which made + the token invalid. + + Otherwise the string starts with a valid token; nextTokPtr will be + set to point to the character following the end of that token. + + Each data character counts as a single token, but adjacent data + characters may be returned together. Similarly for characters in + the prolog outside literals, comments and processing instructions. +*/ + + +#define XmlTok(enc, state, ptr, end, nextTokPtr) \ + (((enc)->scanners[state])(enc, ptr, end, nextTokPtr)) + +#define XmlPrologTok(enc, ptr, end, nextTokPtr) \ + XmlTok(enc, XML_PROLOG_STATE, ptr, end, nextTokPtr) + +#define XmlContentTok(enc, ptr, end, nextTokPtr) \ + XmlTok(enc, XML_CONTENT_STATE, ptr, end, nextTokPtr) + +#define XmlCdataSectionTok(enc, ptr, end, nextTokPtr) \ + XmlTok(enc, XML_CDATA_SECTION_STATE, ptr, end, nextTokPtr) + +#ifdef XML_DTD + +#define XmlIgnoreSectionTok(enc, ptr, end, nextTokPtr) \ + XmlTok(enc, XML_IGNORE_SECTION_STATE, ptr, end, nextTokPtr) + +#endif /* XML_DTD */ + +/* This is used for performing a 2nd-level tokenization on the content + of a literal that has already been returned by XmlTok. +*/ +#define XmlLiteralTok(enc, literalType, ptr, end, nextTokPtr) \ + (((enc)->literalScanners[literalType])(enc, ptr, end, nextTokPtr)) + +#define XmlAttributeValueTok(enc, ptr, end, nextTokPtr) \ + XmlLiteralTok(enc, XML_ATTRIBUTE_VALUE_LITERAL, ptr, end, nextTokPtr) + +#define XmlEntityValueTok(enc, ptr, end, nextTokPtr) \ + XmlLiteralTok(enc, XML_ENTITY_VALUE_LITERAL, ptr, end, nextTokPtr) + +#define XmlSameName(enc, ptr1, ptr2) (((enc)->sameName)(enc, ptr1, ptr2)) + +#define XmlNameMatchesAscii(enc, ptr1, end1, ptr2) \ + (((enc)->nameMatchesAscii)(enc, ptr1, end1, ptr2)) + +#define XmlNameLength(enc, ptr) \ + (((enc)->nameLength)(enc, ptr)) + +#define XmlSkipS(enc, ptr) \ + (((enc)->skipS)(enc, ptr)) + +#define XmlGetAttributes(enc, ptr, attsMax, atts) \ + (((enc)->getAtts)(enc, ptr, attsMax, atts)) + +#define XmlCharRefNumber(enc, ptr) \ + (((enc)->charRefNumber)(enc, ptr)) + +#define XmlPredefinedEntityName(enc, ptr, end) \ + (((enc)->predefinedEntityName)(enc, ptr, end)) + +#define XmlUpdatePosition(enc, ptr, end, pos) \ + (((enc)->updatePosition)(enc, ptr, end, pos)) + +#define XmlIsPublicId(enc, ptr, end, badPtr) \ + (((enc)->isPublicId)(enc, ptr, end, badPtr)) + +#define XmlUtf8Convert(enc, fromP, fromLim, toP, toLim) \ + (((enc)->utf8Convert)(enc, fromP, fromLim, toP, toLim)) + +#define XmlUtf16Convert(enc, fromP, fromLim, toP, toLim) \ + (((enc)->utf16Convert)(enc, fromP, fromLim, toP, toLim)) + +typedef struct { + ENCODING initEnc; + const ENCODING **encPtr; +} INIT_ENCODING; + +int XmlParseXmlDecl(int isGeneralTextEntity, + const ENCODING *enc, + const char *ptr, + const char *end, + const char **badPtr, + const char **versionPtr, + const char **versionEndPtr, + const char **encodingNamePtr, + const ENCODING **namedEncodingPtr, + int *standalonePtr); + +int XmlInitEncoding(INIT_ENCODING *, const ENCODING **, const char *name); +const ENCODING *XmlGetUtf8InternalEncoding(void); +const ENCODING *XmlGetUtf16InternalEncoding(void); +int FASTCALL XmlUtf8Encode(int charNumber, char *buf); +int FASTCALL XmlUtf16Encode(int charNumber, unsigned short *buf); +int XmlSizeOfUnknownEncoding(void); + + +typedef int (XMLCALL *CONVERTER) (void *userData, const char *p); + +ENCODING * +XmlInitUnknownEncoding(void *mem, + int *table, + CONVERTER convert, + void *userData); + +int XmlParseXmlDeclNS(int isGeneralTextEntity, + const ENCODING *enc, + const char *ptr, + const char *end, + const char **badPtr, + const char **versionPtr, + const char **versionEndPtr, + const char **encodingNamePtr, + const ENCODING **namedEncodingPtr, + int *standalonePtr); + +int XmlInitEncodingNS(INIT_ENCODING *, const ENCODING **, const char *name); +const ENCODING *XmlGetUtf8InternalEncodingNS(void); +const ENCODING *XmlGetUtf16InternalEncodingNS(void); +ENCODING * +XmlInitUnknownEncodingNS(void *mem, + int *table, + CONVERTER convert, + void *userData); +#ifdef __cplusplus +} +#endif + +#endif /* not XmlTok_INCLUDED */ diff --git a/deps/EXPAT/expat/xmltok_impl.h b/deps/EXPAT/expat/xmltok_impl.h new file mode 100644 index 000000000..da0ea60a6 --- /dev/null +++ b/deps/EXPAT/expat/xmltok_impl.h @@ -0,0 +1,46 @@ +/* +Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd +See the file COPYING for copying permission. +*/ + +enum { + BT_NONXML, + BT_MALFORM, + BT_LT, + BT_AMP, + BT_RSQB, + BT_LEAD2, + BT_LEAD3, + BT_LEAD4, + BT_TRAIL, + BT_CR, + BT_LF, + BT_GT, + BT_QUOT, + BT_APOS, + BT_EQUALS, + BT_QUEST, + BT_EXCL, + BT_SOL, + BT_SEMI, + BT_NUM, + BT_LSQB, + BT_S, + BT_NMSTRT, + BT_COLON, + BT_HEX, + BT_DIGIT, + BT_NAME, + BT_MINUS, + BT_OTHER, /* known not to be a name or name start character */ + BT_NONASCII, /* might be a name or name start character */ + BT_PERCNT, + BT_LPAR, + BT_RPAR, + BT_AST, + BT_PLUS, + BT_COMMA, + BT_VERBAR +}; + +#include diff --git a/deps/EXPAT/expat/xmltok_impl.inc b/deps/EXPAT/expat/xmltok_impl.inc new file mode 100644 index 000000000..5f779c057 --- /dev/null +++ b/deps/EXPAT/expat/xmltok_impl.inc @@ -0,0 +1,1779 @@ +/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +/* This file is included! */ +#ifdef XML_TOK_IMPL_C + +#ifndef IS_INVALID_CHAR +#define IS_INVALID_CHAR(enc, ptr, n) (0) +#endif + +#define INVALID_LEAD_CASE(n, ptr, nextTokPtr) \ + case BT_LEAD ## n: \ + if (end - ptr < n) \ + return XML_TOK_PARTIAL_CHAR; \ + if (IS_INVALID_CHAR(enc, ptr, n)) { \ + *(nextTokPtr) = (ptr); \ + return XML_TOK_INVALID; \ + } \ + ptr += n; \ + break; + +#define INVALID_CASES(ptr, nextTokPtr) \ + INVALID_LEAD_CASE(2, ptr, nextTokPtr) \ + INVALID_LEAD_CASE(3, ptr, nextTokPtr) \ + INVALID_LEAD_CASE(4, ptr, nextTokPtr) \ + case BT_NONXML: \ + case BT_MALFORM: \ + case BT_TRAIL: \ + *(nextTokPtr) = (ptr); \ + return XML_TOK_INVALID; + +#define CHECK_NAME_CASE(n, enc, ptr, end, nextTokPtr) \ + case BT_LEAD ## n: \ + if (end - ptr < n) \ + return XML_TOK_PARTIAL_CHAR; \ + if (!IS_NAME_CHAR(enc, ptr, n)) { \ + *nextTokPtr = ptr; \ + return XML_TOK_INVALID; \ + } \ + ptr += n; \ + break; + +#define CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) \ + case BT_NONASCII: \ + if (!IS_NAME_CHAR_MINBPC(enc, ptr)) { \ + *nextTokPtr = ptr; \ + return XML_TOK_INVALID; \ + } \ + case BT_NMSTRT: \ + case BT_HEX: \ + case BT_DIGIT: \ + case BT_NAME: \ + case BT_MINUS: \ + ptr += MINBPC(enc); \ + break; \ + CHECK_NAME_CASE(2, enc, ptr, end, nextTokPtr) \ + CHECK_NAME_CASE(3, enc, ptr, end, nextTokPtr) \ + CHECK_NAME_CASE(4, enc, ptr, end, nextTokPtr) + +#define CHECK_NMSTRT_CASE(n, enc, ptr, end, nextTokPtr) \ + case BT_LEAD ## n: \ + if (end - ptr < n) \ + return XML_TOK_PARTIAL_CHAR; \ + if (!IS_NMSTRT_CHAR(enc, ptr, n)) { \ + *nextTokPtr = ptr; \ + return XML_TOK_INVALID; \ + } \ + ptr += n; \ + break; + +#define CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr) \ + case BT_NONASCII: \ + if (!IS_NMSTRT_CHAR_MINBPC(enc, ptr)) { \ + *nextTokPtr = ptr; \ + return XML_TOK_INVALID; \ + } \ + case BT_NMSTRT: \ + case BT_HEX: \ + ptr += MINBPC(enc); \ + break; \ + CHECK_NMSTRT_CASE(2, enc, ptr, end, nextTokPtr) \ + CHECK_NMSTRT_CASE(3, enc, ptr, end, nextTokPtr) \ + CHECK_NMSTRT_CASE(4, enc, ptr, end, nextTokPtr) + +#ifndef PREFIX +#define PREFIX(ident) ident +#endif + + +#define HAS_CHARS(enc, ptr, end, count) \ + (end - ptr >= count * MINBPC(enc)) + +#define HAS_CHAR(enc, ptr, end) \ + HAS_CHARS(enc, ptr, end, 1) + +#define REQUIRE_CHARS(enc, ptr, end, count) \ + { \ + if (! HAS_CHARS(enc, ptr, end, count)) { \ + return XML_TOK_PARTIAL; \ + } \ + } + +#define REQUIRE_CHAR(enc, ptr, end) \ + REQUIRE_CHARS(enc, ptr, end, 1) + + +/* ptr points to character following " */ + switch (BYTE_TYPE(enc, ptr + MINBPC(enc))) { + case BT_S: case BT_CR: case BT_LF: case BT_PERCNT: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + /* fall through */ + case BT_S: case BT_CR: case BT_LF: + *nextTokPtr = ptr; + return XML_TOK_DECL_OPEN; + case BT_NMSTRT: + case BT_HEX: + ptr += MINBPC(enc); + break; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + } + return XML_TOK_PARTIAL; +} + +static int PTRCALL +PREFIX(checkPiTarget)(const ENCODING *UNUSED_P(enc), const char *ptr, + const char *end, int *tokPtr) +{ + int upper = 0; + *tokPtr = XML_TOK_PI; + if (end - ptr != MINBPC(enc)*3) + return 1; + switch (BYTE_TO_ASCII(enc, ptr)) { + case ASCII_x: + break; + case ASCII_X: + upper = 1; + break; + default: + return 1; + } + ptr += MINBPC(enc); + switch (BYTE_TO_ASCII(enc, ptr)) { + case ASCII_m: + break; + case ASCII_M: + upper = 1; + break; + default: + return 1; + } + ptr += MINBPC(enc); + switch (BYTE_TO_ASCII(enc, ptr)) { + case ASCII_l: + break; + case ASCII_L: + upper = 1; + break; + default: + return 1; + } + if (upper) + return 0; + *tokPtr = XML_TOK_XML_DECL; + return 1; +} + +/* ptr points to character following "= end) + return XML_TOK_NONE; + if (MINBPC(enc) > 1) { + size_t n = end - ptr; + if (n & (MINBPC(enc) - 1)) { + n &= ~(MINBPC(enc) - 1); + if (n == 0) + return XML_TOK_PARTIAL; + end = ptr + n; + } + } + switch (BYTE_TYPE(enc, ptr)) { + case BT_RSQB: + ptr += MINBPC(enc); + REQUIRE_CHAR(enc, ptr, end); + if (!CHAR_MATCHES(enc, ptr, ASCII_RSQB)) + break; + ptr += MINBPC(enc); + REQUIRE_CHAR(enc, ptr, end); + if (!CHAR_MATCHES(enc, ptr, ASCII_GT)) { + ptr -= MINBPC(enc); + break; + } + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_CDATA_SECT_CLOSE; + case BT_CR: + ptr += MINBPC(enc); + REQUIRE_CHAR(enc, ptr, end); + if (BYTE_TYPE(enc, ptr) == BT_LF) + ptr += MINBPC(enc); + *nextTokPtr = ptr; + return XML_TOK_DATA_NEWLINE; + case BT_LF: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_DATA_NEWLINE; + INVALID_CASES(ptr, nextTokPtr) + default: + ptr += MINBPC(enc); + break; + } + while (HAS_CHAR(enc, ptr, end)) { + switch (BYTE_TYPE(enc, ptr)) { +#define LEAD_CASE(n) \ + case BT_LEAD ## n: \ + if (end - ptr < n || IS_INVALID_CHAR(enc, ptr, n)) { \ + *nextTokPtr = ptr; \ + return XML_TOK_DATA_CHARS; \ + } \ + ptr += n; \ + break; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_NONXML: + case BT_MALFORM: + case BT_TRAIL: + case BT_CR: + case BT_LF: + case BT_RSQB: + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + default: + ptr += MINBPC(enc); + break; + } + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; +} + +/* ptr points to character following "= end) + return XML_TOK_NONE; + if (MINBPC(enc) > 1) { + size_t n = end - ptr; + if (n & (MINBPC(enc) - 1)) { + n &= ~(MINBPC(enc) - 1); + if (n == 0) + return XML_TOK_PARTIAL; + end = ptr + n; + } + } + switch (BYTE_TYPE(enc, ptr)) { + case BT_LT: + return PREFIX(scanLt)(enc, ptr + MINBPC(enc), end, nextTokPtr); + case BT_AMP: + return PREFIX(scanRef)(enc, ptr + MINBPC(enc), end, nextTokPtr); + case BT_CR: + ptr += MINBPC(enc); + if (! HAS_CHAR(enc, ptr, end)) + return XML_TOK_TRAILING_CR; + if (BYTE_TYPE(enc, ptr) == BT_LF) + ptr += MINBPC(enc); + *nextTokPtr = ptr; + return XML_TOK_DATA_NEWLINE; + case BT_LF: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_DATA_NEWLINE; + case BT_RSQB: + ptr += MINBPC(enc); + if (! HAS_CHAR(enc, ptr, end)) + return XML_TOK_TRAILING_RSQB; + if (!CHAR_MATCHES(enc, ptr, ASCII_RSQB)) + break; + ptr += MINBPC(enc); + if (! HAS_CHAR(enc, ptr, end)) + return XML_TOK_TRAILING_RSQB; + if (!CHAR_MATCHES(enc, ptr, ASCII_GT)) { + ptr -= MINBPC(enc); + break; + } + *nextTokPtr = ptr; + return XML_TOK_INVALID; + INVALID_CASES(ptr, nextTokPtr) + default: + ptr += MINBPC(enc); + break; + } + while (HAS_CHAR(enc, ptr, end)) { + switch (BYTE_TYPE(enc, ptr)) { +#define LEAD_CASE(n) \ + case BT_LEAD ## n: \ + if (end - ptr < n || IS_INVALID_CHAR(enc, ptr, n)) { \ + *nextTokPtr = ptr; \ + return XML_TOK_DATA_CHARS; \ + } \ + ptr += n; \ + break; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_RSQB: + if (HAS_CHARS(enc, ptr, end, 2)) { + if (!CHAR_MATCHES(enc, ptr + MINBPC(enc), ASCII_RSQB)) { + ptr += MINBPC(enc); + break; + } + if (HAS_CHARS(enc, ptr, end, 3)) { + if (!CHAR_MATCHES(enc, ptr + 2*MINBPC(enc), ASCII_GT)) { + ptr += MINBPC(enc); + break; + } + *nextTokPtr = ptr + 2*MINBPC(enc); + return XML_TOK_INVALID; + } + } + /* fall through */ + case BT_AMP: + case BT_LT: + case BT_NONXML: + case BT_MALFORM: + case BT_TRAIL: + case BT_CR: + case BT_LF: + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + default: + ptr += MINBPC(enc); + break; + } + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; +} + +/* ptr points to character following "%" */ + +static int PTRCALL +PREFIX(scanPercent)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ + REQUIRE_CHAR(enc, ptr, end); + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr) + case BT_S: case BT_LF: case BT_CR: case BT_PERCNT: + *nextTokPtr = ptr; + return XML_TOK_PERCENT; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + while (HAS_CHAR(enc, ptr, end)) { + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) + case BT_SEMI: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_PARAM_ENTITY_REF; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + } + return XML_TOK_PARTIAL; +} + +static int PTRCALL +PREFIX(scanPoundName)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ + REQUIRE_CHAR(enc, ptr, end); + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr) + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + while (HAS_CHAR(enc, ptr, end)) { + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) + case BT_CR: case BT_LF: case BT_S: + case BT_RPAR: case BT_GT: case BT_PERCNT: case BT_VERBAR: + *nextTokPtr = ptr; + return XML_TOK_POUND_NAME; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + } + return -XML_TOK_POUND_NAME; +} + +static int PTRCALL +PREFIX(scanLit)(int open, const ENCODING *enc, + const char *ptr, const char *end, + const char **nextTokPtr) +{ + while (HAS_CHAR(enc, ptr, end)) { + int t = BYTE_TYPE(enc, ptr); + switch (t) { + INVALID_CASES(ptr, nextTokPtr) + case BT_QUOT: + case BT_APOS: + ptr += MINBPC(enc); + if (t != open) + break; + if (! HAS_CHAR(enc, ptr, end)) + return -XML_TOK_LITERAL; + *nextTokPtr = ptr; + switch (BYTE_TYPE(enc, ptr)) { + case BT_S: case BT_CR: case BT_LF: + case BT_GT: case BT_PERCNT: case BT_LSQB: + return XML_TOK_LITERAL; + default: + return XML_TOK_INVALID; + } + default: + ptr += MINBPC(enc); + break; + } + } + return XML_TOK_PARTIAL; +} + +static int PTRCALL +PREFIX(prologTok)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ + int tok; + if (ptr >= end) + return XML_TOK_NONE; + if (MINBPC(enc) > 1) { + size_t n = end - ptr; + if (n & (MINBPC(enc) - 1)) { + n &= ~(MINBPC(enc) - 1); + if (n == 0) + return XML_TOK_PARTIAL; + end = ptr + n; + } + } + switch (BYTE_TYPE(enc, ptr)) { + case BT_QUOT: + return PREFIX(scanLit)(BT_QUOT, enc, ptr + MINBPC(enc), end, nextTokPtr); + case BT_APOS: + return PREFIX(scanLit)(BT_APOS, enc, ptr + MINBPC(enc), end, nextTokPtr); + case BT_LT: + { + ptr += MINBPC(enc); + REQUIRE_CHAR(enc, ptr, end); + switch (BYTE_TYPE(enc, ptr)) { + case BT_EXCL: + return PREFIX(scanDecl)(enc, ptr + MINBPC(enc), end, nextTokPtr); + case BT_QUEST: + return PREFIX(scanPi)(enc, ptr + MINBPC(enc), end, nextTokPtr); + case BT_NMSTRT: + case BT_HEX: + case BT_NONASCII: + case BT_LEAD2: + case BT_LEAD3: + case BT_LEAD4: + *nextTokPtr = ptr - MINBPC(enc); + return XML_TOK_INSTANCE_START; + } + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + case BT_CR: + if (ptr + MINBPC(enc) == end) { + *nextTokPtr = end; + /* indicate that this might be part of a CR/LF pair */ + return -XML_TOK_PROLOG_S; + } + /* fall through */ + case BT_S: case BT_LF: + for (;;) { + ptr += MINBPC(enc); + if (! HAS_CHAR(enc, ptr, end)) + break; + switch (BYTE_TYPE(enc, ptr)) { + case BT_S: case BT_LF: + break; + case BT_CR: + /* don't split CR/LF pair */ + if (ptr + MINBPC(enc) != end) + break; + /* fall through */ + default: + *nextTokPtr = ptr; + return XML_TOK_PROLOG_S; + } + } + *nextTokPtr = ptr; + return XML_TOK_PROLOG_S; + case BT_PERCNT: + return PREFIX(scanPercent)(enc, ptr + MINBPC(enc), end, nextTokPtr); + case BT_COMMA: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_COMMA; + case BT_LSQB: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_OPEN_BRACKET; + case BT_RSQB: + ptr += MINBPC(enc); + if (! HAS_CHAR(enc, ptr, end)) + return -XML_TOK_CLOSE_BRACKET; + if (CHAR_MATCHES(enc, ptr, ASCII_RSQB)) { + REQUIRE_CHARS(enc, ptr, end, 2); + if (CHAR_MATCHES(enc, ptr + MINBPC(enc), ASCII_GT)) { + *nextTokPtr = ptr + 2*MINBPC(enc); + return XML_TOK_COND_SECT_CLOSE; + } + } + *nextTokPtr = ptr; + return XML_TOK_CLOSE_BRACKET; + case BT_LPAR: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_OPEN_PAREN; + case BT_RPAR: + ptr += MINBPC(enc); + if (! HAS_CHAR(enc, ptr, end)) + return -XML_TOK_CLOSE_PAREN; + switch (BYTE_TYPE(enc, ptr)) { + case BT_AST: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_CLOSE_PAREN_ASTERISK; + case BT_QUEST: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_CLOSE_PAREN_QUESTION; + case BT_PLUS: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_CLOSE_PAREN_PLUS; + case BT_CR: case BT_LF: case BT_S: + case BT_GT: case BT_COMMA: case BT_VERBAR: + case BT_RPAR: + *nextTokPtr = ptr; + return XML_TOK_CLOSE_PAREN; + } + *nextTokPtr = ptr; + return XML_TOK_INVALID; + case BT_VERBAR: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_OR; + case BT_GT: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_DECL_CLOSE; + case BT_NUM: + return PREFIX(scanPoundName)(enc, ptr + MINBPC(enc), end, nextTokPtr); +#define LEAD_CASE(n) \ + case BT_LEAD ## n: \ + if (end - ptr < n) \ + return XML_TOK_PARTIAL_CHAR; \ + if (IS_NMSTRT_CHAR(enc, ptr, n)) { \ + ptr += n; \ + tok = XML_TOK_NAME; \ + break; \ + } \ + if (IS_NAME_CHAR(enc, ptr, n)) { \ + ptr += n; \ + tok = XML_TOK_NMTOKEN; \ + break; \ + } \ + *nextTokPtr = ptr; \ + return XML_TOK_INVALID; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_NMSTRT: + case BT_HEX: + tok = XML_TOK_NAME; + ptr += MINBPC(enc); + break; + case BT_DIGIT: + case BT_NAME: + case BT_MINUS: +#ifdef XML_NS + case BT_COLON: +#endif + tok = XML_TOK_NMTOKEN; + ptr += MINBPC(enc); + break; + case BT_NONASCII: + if (IS_NMSTRT_CHAR_MINBPC(enc, ptr)) { + ptr += MINBPC(enc); + tok = XML_TOK_NAME; + break; + } + if (IS_NAME_CHAR_MINBPC(enc, ptr)) { + ptr += MINBPC(enc); + tok = XML_TOK_NMTOKEN; + break; + } + /* fall through */ + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + while (HAS_CHAR(enc, ptr, end)) { + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) + case BT_GT: case BT_RPAR: case BT_COMMA: + case BT_VERBAR: case BT_LSQB: case BT_PERCNT: + case BT_S: case BT_CR: case BT_LF: + *nextTokPtr = ptr; + return tok; +#ifdef XML_NS + case BT_COLON: + ptr += MINBPC(enc); + switch (tok) { + case XML_TOK_NAME: + REQUIRE_CHAR(enc, ptr, end); + tok = XML_TOK_PREFIXED_NAME; + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) + default: + tok = XML_TOK_NMTOKEN; + break; + } + break; + case XML_TOK_PREFIXED_NAME: + tok = XML_TOK_NMTOKEN; + break; + } + break; +#endif + case BT_PLUS: + if (tok == XML_TOK_NMTOKEN) { + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_NAME_PLUS; + case BT_AST: + if (tok == XML_TOK_NMTOKEN) { + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_NAME_ASTERISK; + case BT_QUEST: + if (tok == XML_TOK_NMTOKEN) { + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_NAME_QUESTION; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + } + return -tok; +} + +static int PTRCALL +PREFIX(attributeValueTok)(const ENCODING *enc, const char *ptr, + const char *end, const char **nextTokPtr) +{ + const char *start; + if (ptr >= end) + return XML_TOK_NONE; + else if (! HAS_CHAR(enc, ptr, end)) + return XML_TOK_PARTIAL; + start = ptr; + while (HAS_CHAR(enc, ptr, end)) { + switch (BYTE_TYPE(enc, ptr)) { +#define LEAD_CASE(n) \ + case BT_LEAD ## n: ptr += n; break; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_AMP: + if (ptr == start) + return PREFIX(scanRef)(enc, ptr + MINBPC(enc), end, nextTokPtr); + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + case BT_LT: + /* this is for inside entity references */ + *nextTokPtr = ptr; + return XML_TOK_INVALID; + case BT_LF: + if (ptr == start) { + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_DATA_NEWLINE; + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + case BT_CR: + if (ptr == start) { + ptr += MINBPC(enc); + if (! HAS_CHAR(enc, ptr, end)) + return XML_TOK_TRAILING_CR; + if (BYTE_TYPE(enc, ptr) == BT_LF) + ptr += MINBPC(enc); + *nextTokPtr = ptr; + return XML_TOK_DATA_NEWLINE; + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + case BT_S: + if (ptr == start) { + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_ATTRIBUTE_VALUE_S; + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + default: + ptr += MINBPC(enc); + break; + } + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; +} + +static int PTRCALL +PREFIX(entityValueTok)(const ENCODING *enc, const char *ptr, + const char *end, const char **nextTokPtr) +{ + const char *start; + if (ptr >= end) + return XML_TOK_NONE; + else if (! HAS_CHAR(enc, ptr, end)) + return XML_TOK_PARTIAL; + start = ptr; + while (HAS_CHAR(enc, ptr, end)) { + switch (BYTE_TYPE(enc, ptr)) { +#define LEAD_CASE(n) \ + case BT_LEAD ## n: ptr += n; break; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_AMP: + if (ptr == start) + return PREFIX(scanRef)(enc, ptr + MINBPC(enc), end, nextTokPtr); + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + case BT_PERCNT: + if (ptr == start) { + int tok = PREFIX(scanPercent)(enc, ptr + MINBPC(enc), + end, nextTokPtr); + return (tok == XML_TOK_PERCENT) ? XML_TOK_INVALID : tok; + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + case BT_LF: + if (ptr == start) { + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_DATA_NEWLINE; + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + case BT_CR: + if (ptr == start) { + ptr += MINBPC(enc); + if (! HAS_CHAR(enc, ptr, end)) + return XML_TOK_TRAILING_CR; + if (BYTE_TYPE(enc, ptr) == BT_LF) + ptr += MINBPC(enc); + *nextTokPtr = ptr; + return XML_TOK_DATA_NEWLINE; + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + default: + ptr += MINBPC(enc); + break; + } + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; +} + +#ifdef XML_DTD + +static int PTRCALL +PREFIX(ignoreSectionTok)(const ENCODING *enc, const char *ptr, + const char *end, const char **nextTokPtr) +{ + int level = 0; + if (MINBPC(enc) > 1) { + size_t n = end - ptr; + if (n & (MINBPC(enc) - 1)) { + n &= ~(MINBPC(enc) - 1); + end = ptr + n; + } + } + while (HAS_CHAR(enc, ptr, end)) { + switch (BYTE_TYPE(enc, ptr)) { + INVALID_CASES(ptr, nextTokPtr) + case BT_LT: + ptr += MINBPC(enc); + REQUIRE_CHAR(enc, ptr, end); + if (CHAR_MATCHES(enc, ptr, ASCII_EXCL)) { + ptr += MINBPC(enc); + REQUIRE_CHAR(enc, ptr, end); + if (CHAR_MATCHES(enc, ptr, ASCII_LSQB)) { + ++level; + ptr += MINBPC(enc); + } + } + break; + case BT_RSQB: + ptr += MINBPC(enc); + REQUIRE_CHAR(enc, ptr, end); + if (CHAR_MATCHES(enc, ptr, ASCII_RSQB)) { + ptr += MINBPC(enc); + REQUIRE_CHAR(enc, ptr, end); + if (CHAR_MATCHES(enc, ptr, ASCII_GT)) { + ptr += MINBPC(enc); + if (level == 0) { + *nextTokPtr = ptr; + return XML_TOK_IGNORE_SECT; + } + --level; + } + } + break; + default: + ptr += MINBPC(enc); + break; + } + } + return XML_TOK_PARTIAL; +} + +#endif /* XML_DTD */ + +static int PTRCALL +PREFIX(isPublicId)(const ENCODING *enc, const char *ptr, const char *end, + const char **badPtr) +{ + ptr += MINBPC(enc); + end -= MINBPC(enc); + for (; HAS_CHAR(enc, ptr, end); ptr += MINBPC(enc)) { + switch (BYTE_TYPE(enc, ptr)) { + case BT_DIGIT: + case BT_HEX: + case BT_MINUS: + case BT_APOS: + case BT_LPAR: + case BT_RPAR: + case BT_PLUS: + case BT_COMMA: + case BT_SOL: + case BT_EQUALS: + case BT_QUEST: + case BT_CR: + case BT_LF: + case BT_SEMI: + case BT_EXCL: + case BT_AST: + case BT_PERCNT: + case BT_NUM: +#ifdef XML_NS + case BT_COLON: +#endif + break; + case BT_S: + if (CHAR_MATCHES(enc, ptr, ASCII_TAB)) { + *badPtr = ptr; + return 0; + } + break; + case BT_NAME: + case BT_NMSTRT: + if (!(BYTE_TO_ASCII(enc, ptr) & ~0x7f)) + break; + default: + switch (BYTE_TO_ASCII(enc, ptr)) { + case 0x24: /* $ */ + case 0x40: /* @ */ + break; + default: + *badPtr = ptr; + return 0; + } + break; + } + } + return 1; +} + +/* This must only be called for a well-formed start-tag or empty + element tag. Returns the number of attributes. Pointers to the + first attsMax attributes are stored in atts. +*/ + +static int PTRCALL +PREFIX(getAtts)(const ENCODING *enc, const char *ptr, + int attsMax, ATTRIBUTE *atts) +{ + enum { other, inName, inValue } state = inName; + int nAtts = 0; + int open = 0; /* defined when state == inValue; + initialization just to shut up compilers */ + + for (ptr += MINBPC(enc);; ptr += MINBPC(enc)) { + switch (BYTE_TYPE(enc, ptr)) { +#define START_NAME \ + if (state == other) { \ + if (nAtts < attsMax) { \ + atts[nAtts].name = ptr; \ + atts[nAtts].normalized = 1; \ + } \ + state = inName; \ + } +#define LEAD_CASE(n) \ + case BT_LEAD ## n: START_NAME ptr += (n - MINBPC(enc)); break; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_NONASCII: + case BT_NMSTRT: + case BT_HEX: + START_NAME + break; +#undef START_NAME + case BT_QUOT: + if (state != inValue) { + if (nAtts < attsMax) + atts[nAtts].valuePtr = ptr + MINBPC(enc); + state = inValue; + open = BT_QUOT; + } + else if (open == BT_QUOT) { + state = other; + if (nAtts < attsMax) + atts[nAtts].valueEnd = ptr; + nAtts++; + } + break; + case BT_APOS: + if (state != inValue) { + if (nAtts < attsMax) + atts[nAtts].valuePtr = ptr + MINBPC(enc); + state = inValue; + open = BT_APOS; + } + else if (open == BT_APOS) { + state = other; + if (nAtts < attsMax) + atts[nAtts].valueEnd = ptr; + nAtts++; + } + break; + case BT_AMP: + if (nAtts < attsMax) + atts[nAtts].normalized = 0; + break; + case BT_S: + if (state == inName) + state = other; + else if (state == inValue + && nAtts < attsMax + && atts[nAtts].normalized + && (ptr == atts[nAtts].valuePtr + || BYTE_TO_ASCII(enc, ptr) != ASCII_SPACE + || BYTE_TO_ASCII(enc, ptr + MINBPC(enc)) == ASCII_SPACE + || BYTE_TYPE(enc, ptr + MINBPC(enc)) == open)) + atts[nAtts].normalized = 0; + break; + case BT_CR: case BT_LF: + /* This case ensures that the first attribute name is counted + Apart from that we could just change state on the quote. */ + if (state == inName) + state = other; + else if (state == inValue && nAtts < attsMax) + atts[nAtts].normalized = 0; + break; + case BT_GT: + case BT_SOL: + if (state != inValue) + return nAtts; + break; + default: + break; + } + } + /* not reached */ +} + +static int PTRFASTCALL +PREFIX(charRefNumber)(const ENCODING *UNUSED_P(enc), const char *ptr) +{ + int result = 0; + /* skip &# */ + ptr += 2*MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, ASCII_x)) { + for (ptr += MINBPC(enc); + !CHAR_MATCHES(enc, ptr, ASCII_SEMI); + ptr += MINBPC(enc)) { + int c = BYTE_TO_ASCII(enc, ptr); + switch (c) { + case ASCII_0: case ASCII_1: case ASCII_2: case ASCII_3: case ASCII_4: + case ASCII_5: case ASCII_6: case ASCII_7: case ASCII_8: case ASCII_9: + result <<= 4; + result |= (c - ASCII_0); + break; + case ASCII_A: case ASCII_B: case ASCII_C: + case ASCII_D: case ASCII_E: case ASCII_F: + result <<= 4; + result += 10 + (c - ASCII_A); + break; + case ASCII_a: case ASCII_b: case ASCII_c: + case ASCII_d: case ASCII_e: case ASCII_f: + result <<= 4; + result += 10 + (c - ASCII_a); + break; + } + if (result >= 0x110000) + return -1; + } + } + else { + for (; !CHAR_MATCHES(enc, ptr, ASCII_SEMI); ptr += MINBPC(enc)) { + int c = BYTE_TO_ASCII(enc, ptr); + result *= 10; + result += (c - ASCII_0); + if (result >= 0x110000) + return -1; + } + } + return checkCharRefNumber(result); +} + +static int PTRCALL +PREFIX(predefinedEntityName)(const ENCODING *UNUSED_P(enc), const char *ptr, + const char *end) +{ + switch ((end - ptr)/MINBPC(enc)) { + case 2: + if (CHAR_MATCHES(enc, ptr + MINBPC(enc), ASCII_t)) { + switch (BYTE_TO_ASCII(enc, ptr)) { + case ASCII_l: + return ASCII_LT; + case ASCII_g: + return ASCII_GT; + } + } + break; + case 3: + if (CHAR_MATCHES(enc, ptr, ASCII_a)) { + ptr += MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, ASCII_m)) { + ptr += MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, ASCII_p)) + return ASCII_AMP; + } + } + break; + case 4: + switch (BYTE_TO_ASCII(enc, ptr)) { + case ASCII_q: + ptr += MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, ASCII_u)) { + ptr += MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, ASCII_o)) { + ptr += MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, ASCII_t)) + return ASCII_QUOT; + } + } + break; + case ASCII_a: + ptr += MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, ASCII_p)) { + ptr += MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, ASCII_o)) { + ptr += MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, ASCII_s)) + return ASCII_APOS; + } + } + break; + } + } + return 0; +} + +static int PTRCALL +PREFIX(sameName)(const ENCODING *enc, const char *ptr1, const char *ptr2) +{ + for (;;) { + switch (BYTE_TYPE(enc, ptr1)) { +#define LEAD_CASE(n) \ + case BT_LEAD ## n: \ + if (*ptr1++ != *ptr2++) \ + return 0; + LEAD_CASE(4) LEAD_CASE(3) LEAD_CASE(2) +#undef LEAD_CASE + /* fall through */ + if (*ptr1++ != *ptr2++) + return 0; + break; + case BT_NONASCII: + case BT_NMSTRT: +#ifdef XML_NS + case BT_COLON: +#endif + case BT_HEX: + case BT_DIGIT: + case BT_NAME: + case BT_MINUS: + if (*ptr2++ != *ptr1++) + return 0; + if (MINBPC(enc) > 1) { + if (*ptr2++ != *ptr1++) + return 0; + if (MINBPC(enc) > 2) { + if (*ptr2++ != *ptr1++) + return 0; + if (MINBPC(enc) > 3) { + if (*ptr2++ != *ptr1++) + return 0; + } + } + } + break; + default: + if (MINBPC(enc) == 1 && *ptr1 == *ptr2) + return 1; + switch (BYTE_TYPE(enc, ptr2)) { + case BT_LEAD2: + case BT_LEAD3: + case BT_LEAD4: + case BT_NONASCII: + case BT_NMSTRT: +#ifdef XML_NS + case BT_COLON: +#endif + case BT_HEX: + case BT_DIGIT: + case BT_NAME: + case BT_MINUS: + return 0; + default: + return 1; + } + } + } + /* not reached */ +} + +static int PTRCALL +PREFIX(nameMatchesAscii)(const ENCODING *UNUSED_P(enc), const char *ptr1, + const char *end1, const char *ptr2) +{ + for (; *ptr2; ptr1 += MINBPC(enc), ptr2++) { + if (end1 - ptr1 < MINBPC(enc)) + return 0; + if (!CHAR_MATCHES(enc, ptr1, *ptr2)) + return 0; + } + return ptr1 == end1; +} + +static int PTRFASTCALL +PREFIX(nameLength)(const ENCODING *enc, const char *ptr) +{ + const char *start = ptr; + for (;;) { + switch (BYTE_TYPE(enc, ptr)) { +#define LEAD_CASE(n) \ + case BT_LEAD ## n: ptr += n; break; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_NONASCII: + case BT_NMSTRT: +#ifdef XML_NS + case BT_COLON: +#endif + case BT_HEX: + case BT_DIGIT: + case BT_NAME: + case BT_MINUS: + ptr += MINBPC(enc); + break; + default: + return (int)(ptr - start); + } + } +} + +static const char * PTRFASTCALL +PREFIX(skipS)(const ENCODING *enc, const char *ptr) +{ + for (;;) { + switch (BYTE_TYPE(enc, ptr)) { + case BT_LF: + case BT_CR: + case BT_S: + ptr += MINBPC(enc); + break; + default: + return ptr; + } + } +} + +static void PTRCALL +PREFIX(updatePosition)(const ENCODING *enc, + const char *ptr, + const char *end, + POSITION *pos) +{ + while (HAS_CHAR(enc, ptr, end)) { + switch (BYTE_TYPE(enc, ptr)) { +#define LEAD_CASE(n) \ + case BT_LEAD ## n: \ + ptr += n; \ + break; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_LF: + pos->columnNumber = (XML_Size)-1; + pos->lineNumber++; + ptr += MINBPC(enc); + break; + case BT_CR: + pos->lineNumber++; + ptr += MINBPC(enc); + if (HAS_CHAR(enc, ptr, end) && BYTE_TYPE(enc, ptr) == BT_LF) + ptr += MINBPC(enc); + pos->columnNumber = (XML_Size)-1; + break; + default: + ptr += MINBPC(enc); + break; + } + pos->columnNumber++; + } +} + +#undef DO_LEAD_CASE +#undef MULTIBYTE_CASES +#undef INVALID_CASES +#undef CHECK_NAME_CASE +#undef CHECK_NAME_CASES +#undef CHECK_NMSTRT_CASE +#undef CHECK_NMSTRT_CASES + +#endif /* XML_TOK_IMPL_C */ diff --git a/deps/EXPAT/expat/xmltok_ns.inc b/deps/EXPAT/expat/xmltok_ns.inc new file mode 100644 index 000000000..c3b88fdf4 --- /dev/null +++ b/deps/EXPAT/expat/xmltok_ns.inc @@ -0,0 +1,115 @@ +/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +/* This file is included! */ +#ifdef XML_TOK_NS_C + +const ENCODING * +NS(XmlGetUtf8InternalEncoding)(void) +{ + return &ns(internal_utf8_encoding).enc; +} + +const ENCODING * +NS(XmlGetUtf16InternalEncoding)(void) +{ +#if BYTEORDER == 1234 + return &ns(internal_little2_encoding).enc; +#elif BYTEORDER == 4321 + return &ns(internal_big2_encoding).enc; +#else + const short n = 1; + return (*(const char *)&n + ? &ns(internal_little2_encoding).enc + : &ns(internal_big2_encoding).enc); +#endif +} + +static const ENCODING * const NS(encodings)[] = { + &ns(latin1_encoding).enc, + &ns(ascii_encoding).enc, + &ns(utf8_encoding).enc, + &ns(big2_encoding).enc, + &ns(big2_encoding).enc, + &ns(little2_encoding).enc, + &ns(utf8_encoding).enc /* NO_ENC */ +}; + +static int PTRCALL +NS(initScanProlog)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ + return initScan(NS(encodings), (const INIT_ENCODING *)enc, + XML_PROLOG_STATE, ptr, end, nextTokPtr); +} + +static int PTRCALL +NS(initScanContent)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ + return initScan(NS(encodings), (const INIT_ENCODING *)enc, + XML_CONTENT_STATE, ptr, end, nextTokPtr); +} + +int +NS(XmlInitEncoding)(INIT_ENCODING *p, const ENCODING **encPtr, + const char *name) +{ + int i = getEncodingIndex(name); + if (i == UNKNOWN_ENC) + return 0; + SET_INIT_ENC_INDEX(p, i); + p->initEnc.scanners[XML_PROLOG_STATE] = NS(initScanProlog); + p->initEnc.scanners[XML_CONTENT_STATE] = NS(initScanContent); + p->initEnc.updatePosition = initUpdatePosition; + p->encPtr = encPtr; + *encPtr = &(p->initEnc); + return 1; +} + +static const ENCODING * +NS(findEncoding)(const ENCODING *enc, const char *ptr, const char *end) +{ +#define ENCODING_MAX 128 + char buf[ENCODING_MAX]; + char *p = buf; + int i; + XmlUtf8Convert(enc, &ptr, end, &p, p + ENCODING_MAX - 1); + if (ptr != end) + return 0; + *p = 0; + if (streqci(buf, KW_UTF_16) && enc->minBytesPerChar == 2) + return enc; + i = getEncodingIndex(buf); + if (i == UNKNOWN_ENC) + return 0; + return NS(encodings)[i]; +} + +int +NS(XmlParseXmlDecl)(int isGeneralTextEntity, + const ENCODING *enc, + const char *ptr, + const char *end, + const char **badPtr, + const char **versionPtr, + const char **versionEndPtr, + const char **encodingName, + const ENCODING **encoding, + int *standalone) +{ + return doParseXmlDecl(NS(findEncoding), + isGeneralTextEntity, + enc, + ptr, + end, + badPtr, + versionPtr, + versionEndPtr, + encodingName, + encoding, + standalone); +} + +#endif /* XML_TOK_NS_C */ diff --git a/deps/GLEW/GLEW.cmake b/deps/GLEW/GLEW.cmake index fcbdcd20c..285b9be4d 100644 --- a/deps/GLEW/GLEW.cmake +++ b/deps/GLEW/GLEW.cmake @@ -1,5 +1,6 @@ # We have to check for OpenGL to compile GLEW -find_package(OpenGL QUIET REQUIRED) +set(OpenGL_GL_PREFERENCE "LEGACY") # to prevent a nasty warning by cmake +find_package(OpenGL QUIET) prusaslicer_add_cmake_project( GLEW diff --git a/deps/OpenCSG/OpenCSG.cmake b/deps/OpenCSG/OpenCSG.cmake index d9de5e152..fda74cd45 100644 --- a/deps/OpenCSG/OpenCSG.cmake +++ b/deps/OpenCSG/OpenCSG.cmake @@ -6,8 +6,8 @@ prusaslicer_add_cmake_project(OpenCSG DEPENDS dep_GLEW ) -if (TARGET dep_ZLIB) - add_dependencies(dep_OpenCSG dep_ZLIB) +if (TARGET ${ZLIB_PKG}) + add_dependencies(dep_OpenCSG ${ZLIB_PKG}) endif() if (MSVC) diff --git a/deps/PNG/PNG.cmake b/deps/PNG/PNG.cmake new file mode 100644 index 000000000..01bd98499 --- /dev/null +++ b/deps/PNG/PNG.cmake @@ -0,0 +1,13 @@ +prusaslicer_add_cmake_project(PNG + GIT_REPOSITORY https://github.com/glennrp/libpng.git + GIT_TAG v1.6.35 + DEPENDS ${ZLIB_PKG} + CMAKE_ARGS + -DPNG_SHARED=OFF + -DPNG_STATIC=ON + -DPNG_TESTS=OFF +) + +if (MSVC) + add_debug_dep(dep_PNG) +endif () diff --git a/deps/deps-linux.cmake b/deps/deps-linux.cmake index 01f7ab21b..3ad3cca64 100644 --- a/deps/deps-linux.cmake +++ b/deps/deps-linux.cmake @@ -3,6 +3,13 @@ set(DEP_CMAKE_OPTS "-DCMAKE_POSITION_INDEPENDENT_CODE=ON") include("deps-unix-common.cmake") +find_package(PNG QUIET) +if (NOT PNG_FOUND) + message(WARNING "No PNG dev package found in system, building static library. You should install the system package.") +endif () + +#TODO UDEV + ExternalProject_Add(dep_boost EXCLUDE_FROM_ALL 1 URL "https://dl.bintray.com/boostorg/release/1.70.0/source/boost_1_70_0.tar.gz" @@ -93,45 +100,4 @@ ExternalProject_Add(dep_libcurl INSTALL_COMMAND make install "DESTDIR=${DESTDIR}" ) -if (DEP_WX_STABLE) - set(DEP_WX_TAG "v3.0.4") -else () - set(DEP_WX_TAG "v3.1.1-patched") -endif() - -if (DEP_WX_GTK3) - set(WX_GTK_VERSION "3") -else () - set(WX_GTK_VERSION "2") -endif() - -ExternalProject_Add(dep_wxwidgets - EXCLUDE_FROM_ALL 1 - GIT_REPOSITORY "https://github.com/prusa3d/wxWidgets" - GIT_TAG "${DEP_WX_TAG}" - BUILD_IN_SOURCE 1 - # PATCH_COMMAND "${CMAKE_COMMAND}" -E copy "${CMAKE_CURRENT_SOURCE_DIR}/wxwidgets-pngprefix.h" src/png/pngprefix.h - CONFIGURE_COMMAND ./configure - "--prefix=${DESTDIR}/usr/local" - --disable-shared - --with-gtk=${WX_GTK_VERSION} - --with-opengl - --enable-unicode - --enable-graphics_ctx - --with-regex=builtin - --with-libpng=builtin - --with-libxpm=builtin - --with-libjpeg=builtin - --with-libtiff=builtin - --with-zlib - --with-expat=builtin - --disable-precomp-headers - --enable-debug_info - --enable-debug_gdb - --disable-debug - --disable-debug_flag - BUILD_COMMAND make "-j${NPROC}" && make -C locale allmo - INSTALL_COMMAND make install -) - add_dependencies(dep_openvdb dep_boost) diff --git a/deps/deps-macos.cmake b/deps/deps-macos.cmake index 17300b247..ef00b80d5 100644 --- a/deps/deps-macos.cmake +++ b/deps/deps-macos.cmake @@ -86,30 +86,4 @@ ExternalProject_Add(dep_libcurl INSTALL_COMMAND make install "DESTDIR=${DESTDIR}" ) -ExternalProject_Add(dep_wxwidgets - EXCLUDE_FROM_ALL 1 - GIT_REPOSITORY "https://github.com/prusa3d/wxWidgets" - GIT_TAG v3.1.3-patched - BUILD_IN_SOURCE 1 -# PATCH_COMMAND "${CMAKE_COMMAND}" -E copy "${CMAKE_CURRENT_SOURCE_DIR}/wxwidgets-pngprefix.h" src/png/pngprefix.h - CONFIGURE_COMMAND env "CXXFLAGS=${DEP_WERRORS_SDK}" "CFLAGS=${DEP_WERRORS_SDK}" ./configure - "--prefix=${DESTDIR}/usr/local" - --disable-shared - --with-osx_cocoa - --with-macosx-sdk=${CMAKE_OSX_SYSROOT} - "--with-macosx-version-min=${DEP_OSX_TARGET}" - --with-opengl - --with-regex=builtin - --with-libpng=builtin - --with-libxpm=builtin - --with-libjpeg=builtin - --with-libtiff=builtin - --with-zlib - --with-expat=builtin - --disable-debug - --disable-debug_flag - BUILD_COMMAND make "-j${NPROC}" && PATH=/usr/local/opt/gettext/bin/:$ENV{PATH} make -C locale allmo - INSTALL_COMMAND make install -) - add_dependencies(dep_openvdb dep_boost) \ No newline at end of file diff --git a/deps/deps-mingw.cmake b/deps/deps-mingw.cmake index bfe5f9fe4..89b7e2b43 100644 --- a/deps/deps-mingw.cmake +++ b/deps/deps-mingw.cmake @@ -57,20 +57,4 @@ ExternalProject_Add(dep_libcurl -DCURL_DISABLE_GOPHER=ON -DCMAKE_INSTALL_PREFIX=${DESTDIR}/usr/local ${DEP_CMAKE_OPTS} -) - -ExternalProject_Add(dep_wxwidgets - EXCLUDE_FROM_ALL 1 - GIT_REPOSITORY "https://github.com/prusa3d/wxWidgets" - GIT_TAG v3.1.1-patched -# URL "https://github.com/wxWidgets/wxWidgets/releases/download/v3.1.1/wxWidgets-3.1.1.tar.bz2" -# URL_HASH SHA256=c925dfe17e8f8b09eb7ea9bfdcfcc13696a3e14e92750effd839f5e10726159e -# PATCH_COMMAND "${CMAKE_COMMAND}" -E copy "${CMAKE_CURRENT_SOURCE_DIR}\\wxwidgets-pngprefix.h" src\\png\\pngprefix.h - CMAKE_ARGS - -DBUILD_SHARED_LIBS=OFF - -DwxUSE_LIBPNG=builtin - -DwxUSE_ZLIB=builtin - -DwxUSE_OPENGL=ON - -DCMAKE_INSTALL_PREFIX=${DESTDIR}/usr/local - ${DEP_CMAKE_OPTS} ) \ No newline at end of file diff --git a/deps/deps-unix-common.cmake b/deps/deps-unix-common.cmake index 3f3a70c6a..46c9f8864 100644 --- a/deps/deps-unix-common.cmake +++ b/deps/deps-unix-common.cmake @@ -9,9 +9,15 @@ endif () find_package(ZLIB QUIET) if (NOT ZLIB_FOUND) - include(ZLIB/ZLIB.cmake) + message(WARNING "No ZLIB dev package found in system, building static library. You should install the system package.") endif () +# TODO Evaluate expat modifications in the bundled version and test with system versions in various distros and OSX SDKs +# find_package(EXPAT QUIET) +# if (NOT EXPAT_FOUND) +# message(WARNING "No EXPAT dev package found in system, building static library. Consider installing the system package.") +# endif () + ExternalProject_Add(dep_tbb EXCLUDE_FROM_ALL 1 URL "https://github.com/wjakob/tbb/archive/a0dc9bf76d0120f917b641ed095360448cabc85b.tar.gz" diff --git a/deps/deps-windows.cmake b/deps/deps-windows.cmake index 600168c1a..8be9888bc 100644 --- a/deps/deps-windows.cmake +++ b/deps/deps-windows.cmake @@ -149,38 +149,6 @@ ExternalProject_Add(dep_nlopt add_debug_dep(dep_nlopt) -include(ZLIB/ZLIB.cmake) -# ExternalProject_Add(dep_zlib -# EXCLUDE_FROM_ALL 1 -# URL "https://zlib.net/zlib-1.2.11.tar.xz" -# URL_HASH SHA256=4ff941449631ace0d4d203e3483be9dbc9da454084111f97ea0a2114e19bf066 -# CMAKE_GENERATOR "${DEP_MSVC_GEN}" -# CMAKE_GENERATOR_PLATFORM "${DEP_PLATFORM}" -# CMAKE_ARGS -# -DSKIP_INSTALL_FILES=ON # Prevent installation of man pages et al. -# "-DINSTALL_BIN_DIR=${CMAKE_CURRENT_BINARY_DIR}\\fallout" # I found no better way of preventing zlib from creating & installing DLLs :-/ -# -DCMAKE_POSITION_INDEPENDENT_CODE=ON -# "-DCMAKE_INSTALL_PREFIX:PATH=${DESTDIR}\\usr\\local" -# BUILD_COMMAND msbuild /m /P:Configuration=Release INSTALL.vcxproj -# INSTALL_COMMAND "" -# ) - -add_debug_dep(dep_ZLIB) - -# The following steps are unfortunately needed to remove the _static suffix on libraries -# ExternalProject_Add_Step(dep_zlib fix_static -# DEPENDEES install -# COMMAND "${CMAKE_COMMAND}" -E rename zlibstatic.lib zlib.lib -# WORKING_DIRECTORY "${DESTDIR}\\usr\\local\\lib\\" -# ) -# if (${DEP_DEBUG}) -# ExternalProject_Add_Step(dep_zlib fix_static_debug -# DEPENDEES install -# COMMAND "${CMAKE_COMMAND}" -E rename zlibstaticd.lib zlibd.lib -# WORKING_DIRECTORY "${DESTDIR}\\usr\\local\\lib\\" -# ) -# endif () - if (${DEPS_BITS} EQUAL 32) set(DEP_LIBCURL_TARGET "x86") else () @@ -243,36 +211,13 @@ endif () find_package(Git REQUIRED) -ExternalProject_Add(dep_wxwidgets - EXCLUDE_FROM_ALL 1 - GIT_REPOSITORY "https://github.com/prusa3d/wxWidgets" - GIT_TAG v3.1.1-patched -# URL "https://github.com/wxWidgets/wxWidgets/releases/download/v3.1.1/wxWidgets-3.1.1.tar.bz2" -# URL_HASH SHA256=c925dfe17e8f8b09eb7ea9bfdcfcc13696a3e14e92750effd839f5e10726159e - BUILD_IN_SOURCE 1 -# PATCH_COMMAND "${CMAKE_COMMAND}" -E copy "${CMAKE_CURRENT_SOURCE_DIR}\\wxwidgets-pngprefix.h" src\\png\\pngprefix.h - CONFIGURE_COMMAND "" - BUILD_COMMAND cd build\\msw && nmake /f makefile.vc BUILD=release SHARED=0 UNICODE=1 USE_GUI=1 "${DEP_WXWIDGETS_TARGET}" - INSTALL_COMMAND "${CMAKE_COMMAND}" -E copy_directory include "${DESTDIR}\\usr\\local\\include" - && "${CMAKE_COMMAND}" -E copy_directory "lib\\${DEP_WXWIDGETS_LIBDIR}" "${DESTDIR}\\usr\\local\\lib\\${DEP_WXWIDGETS_LIBDIR}" -) -if (${DEP_DEBUG}) - ExternalProject_Get_Property(dep_wxwidgets SOURCE_DIR) - ExternalProject_Add_Step(dep_wxwidgets build_debug - DEPENDEES build - DEPENDERS install - COMMAND cd build\\msw && nmake /f makefile.vc BUILD=debug SHARED=0 UNICODE=1 USE_GUI=1 "${DEP_WXWIDGETS_TARGET}" - WORKING_DIRECTORY "${SOURCE_DIR}" - ) -endif () - ExternalProject_Add(dep_blosc EXCLUDE_FROM_ALL 1 #URL https://github.com/Blosc/c-blosc/archive/v1.17.0.zip #URL_HASH SHA256=7463a1df566704f212263312717ab2c36b45d45cba6cd0dccebf91b2cc4b4da9 GIT_REPOSITORY https://github.com/Blosc/c-blosc.git GIT_TAG e63775855294b50820ef44d1b157f4de1cc38d3e #v1.17.0 - DEPENDS dep_ZLIB + DEPENDS ${ZLIB_PKG} CMAKE_GENERATOR "${DEP_MSVC_GEN}" CMAKE_GENERATOR_PLATFORM "${DEP_PLATFORM}" CMAKE_ARGS @@ -299,7 +244,7 @@ ExternalProject_Add(dep_openexr EXCLUDE_FROM_ALL 1 GIT_REPOSITORY https://github.com/openexr/openexr.git GIT_TAG eae0e337c9f5117e78114fd05f7a415819df413a #v2.4.0 - DEPENDS dep_ZLIB + DEPENDS ${ZLIB_PKG} CMAKE_GENERATOR "${DEP_MSVC_GEN}" CMAKE_GENERATOR_PLATFORM "${DEP_PLATFORM}" CMAKE_ARGS @@ -352,4 +297,4 @@ if (${DEP_DEBUG}) COMMAND msbuild /m /P:Configuration=Debug INSTALL.vcxproj WORKING_DIRECTORY "${BINARY_DIR}" ) -endif () +endif () \ No newline at end of file diff --git a/deps/wxWidgets/wxWidgets.cmake b/deps/wxWidgets/wxWidgets.cmake new file mode 100644 index 000000000..4f89fe82c --- /dev/null +++ b/deps/wxWidgets/wxWidgets.cmake @@ -0,0 +1,36 @@ +set(_wx_git_tag v3.1.3-patched) + +# set(_patch_command "") +set(_wx_toolkit "") +if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + set(_gtk_ver 2) + if (DEP_WX_GTK3) + set(_gtk_ver 3) + endif () + set(_wx_toolkit "-DwxBUILD_TOOLKIT=gtk${_gtk_ver}") +endif() + +prusaslicer_add_cmake_project(wxWidgets + GIT_REPOSITORY "https://github.com/prusa3d/wxWidgets" + GIT_TAG ${_wx_git_tag} + # PATCH_COMMAND "${_patch_command}" + DEPENDS ${PNG_PKG} ${ZLIB_PKG} ${EXPAT_PKG} + CMAKE_ARGS + -DwxBUILD_PRECOMP=ON + ${_wx_toolkit} + "-DCMAKE_DEBUG_POSTFIX:STRING=" + -DwxUSE_DETECT_SM=OFF + -DwxUSE_UNICODE=ON + -DwxUSE_OPENGL=ON + -DwxUSE_LIBPNG=sys + -DwxUSE_ZLIB=sys + -DwxUSE_REGEX=builtin + -DwxUSE_LIBXPM=builtin + -DwxUSE_LIBJPEG=builtin + -DwxUSE_LIBTIFF=builtin + -DwxUSE_EXPAT=sys +) + +if (MSVC) + add_debug_dep(dep_wxWidgets) +endif () \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b0eab9bcc..cdc8e0a13 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -59,6 +59,10 @@ if (SLIC3R_GUI) include(${wxWidgets_USE_FILE}) + list(FILTER wxWidgets_LIBRARIES EXCLUDE REGEX png) + list(FILTER wxWidgets_LIBRARIES EXCLUDE REGEX expat) + list(APPEND wxWidgets_LIBRARIES ${PNG_LIBRARIES} ${EXPAT_LIBRARIES}) + # list(REMOVE_ITEM wxWidgets_LIBRARIES oleacc) message(STATUS "wx libs: ${wxWidgets_LIBRARIES}") diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index eb0f87e50..30614100f 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -272,7 +272,8 @@ endif () encoding_check(libslic3r) target_compile_definitions(libslic3r PUBLIC -DUSE_TBB -DTBB_USE_CAPTURED_EXCEPTION=0) -target_include_directories(libslic3r PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${LIBNEST2D_INCLUDES} PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) +target_include_directories(libslic3r PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) +target_include_directories(libslic3r PUBLIC ${EXPAT_INCLUDE_DIRS}) target_link_libraries(libslic3r libnest2d admesh diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 2ac8a1ff0..3711b12a1 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -204,7 +204,7 @@ add_library(libslic3r_gui STATIC ${SLIC3R_GUI_SOURCES}) encoding_check(libslic3r_gui) -target_link_libraries(libslic3r_gui libslic3r avrdude cereal imgui GLEW::GLEW OpenGL::GL OpenGL::GLU hidapi libcurl ${wxWidgets_LIBRARIES}) +target_link_libraries(libslic3r_gui libslic3r avrdude cereal imgui GLEW::GLEW OpenGL::GL OpenGL::GLU hidapi libcurl ${wxWidgets_LIBRARIES} rt) if(APPLE) target_link_libraries(libslic3r_gui ${DISKARBITRATION_LIBRARY}) From ec81de75532fe45f880eb95ad2393cca1555101b Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 28 Apr 2020 17:19:11 +0200 Subject: [PATCH 06/16] Ironing and Monotonous infill - first working implementation. --- src/libslic3r/Fill/Fill.cpp | 3 +- src/libslic3r/Fill/FillRectilinear2.cpp | 684 ++++++++++++------------ src/libslic3r/PrintConfig.cpp | 2 +- src/libslic3r/ShortestPath.cpp | 5 +- 4 files changed, 342 insertions(+), 352 deletions(-) diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index ad1830e5f..3c16527f0 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -532,7 +532,8 @@ void Layer::make_ironing() fill.z = this->print_z; fill.overlap = 0; fill_params.density = 1.; - fill_params.dont_connect = true; +// fill_params.dont_connect = true; + fill_params.dont_connect = false; fill_params.monotonous = true; for (size_t i = 0; i < by_extruder.size(); ++ i) { diff --git a/src/libslic3r/Fill/FillRectilinear2.cpp b/src/libslic3r/Fill/FillRectilinear2.cpp index 6dd6bb883..e16d57ad6 100644 --- a/src/libslic3r/Fill/FillRectilinear2.cpp +++ b/src/libslic3r/Fill/FillRectilinear2.cpp @@ -160,19 +160,7 @@ struct SegmentIntersection enum class LinkQuality : uint8_t { Invalid, Valid, - // Valid link, to be followed when extruding. - // Link inside a monotonous region. - ValidMonotonous, - // Valid link, to be possibly followed when extruding. - // Link between two monotonous regions. - ValidNonMonotonous, - // Link from T to end of another contour. - FromT, - // Link from end of one contour to T. - ToT, - // Link from one T to another T, making a letter H. - H, - // Vertical segment + // Valid link, but too long to be followed. TooLong, }; @@ -231,6 +219,10 @@ struct SegmentIntersection int left_horizontal() const { return this->has_left_horizontal() ? this->prev_on_contour : -1; } int right_horizontal() const { return this->has_right_horizontal() ? this->next_on_contour : -1; } int horizontal(Side side) const { return side == Side::Left ? this->left_horizontal() : this->right_horizontal(); } + LinkQuality horizontal_quality(Side side) const { + assert(this->has_horizontal(side)); + return side == Side::Left ? this->prev_on_contour_quality : this->next_on_contour_quality; + } int left_vertical_up() const { return this->has_left_vertical_up() ? this->prev_on_contour : -1; } int left_vertical_down() const { return this->has_left_vertical_down() ? this->prev_on_contour : -1; } @@ -251,7 +243,6 @@ struct SegmentIntersection return this->has_left_vertical_up() ? this->left_vertical_up() : this->right_vertical_up(); } LinkQuality vertical_up_quality() const { - assert(this->has_left_vertical_up() != this->has_right_vertical_up()); return this->has_left_vertical_up() ? this->prev_on_contour_quality : this->next_on_contour_quality; } // Returns -1 if there is no link down. @@ -260,10 +251,10 @@ struct SegmentIntersection return this->has_left_vertical_down() ? this->left_vertical_down() : this->right_vertical_down(); } LinkQuality vertical_down_quality() const { - assert(this->has_left_vertical_down() != this->has_right_vertical_down()); return this->has_left_vertical_down() ? this->prev_on_contour_quality : this->next_on_contour_quality; } int vertical_outside() const { return this->is_low() ? this->vertical_down() : this->vertical_up(); } + LinkQuality vertical_outside_quality() const { return this->is_low() ? this->vertical_down_quality() : this->vertical_up_quality(); } // Compare two y intersection points given by rational numbers. // Note that the rational number is given as pos_p/pos_q, where pos_p is int64 and pos_q is uint32. @@ -463,25 +454,8 @@ static inline int distance_of_segmens(const Polygon &poly, size_t seg1, size_t s return d; } -enum IntersectionTypeOtherVLine { - // There is no connection point on the other vertical line. - INTERSECTION_TYPE_OTHER_VLINE_UNDEFINED = -1, - // Connection point on the other vertical segment was found - // and it could be followed. - INTERSECTION_TYPE_OTHER_VLINE_OK = 0, - // The connection segment connects to a middle of a vertical segment. - // Cannot follow. - INTERSECTION_TYPE_OTHER_VLINE_INNER, - // Cannot extend the contor to this intersection point as either the connection segment - // or the succeeding vertical segment were already consumed. - INTERSECTION_TYPE_OTHER_VLINE_CONSUMED, - // Not the first intersection along the contor. This intersection point - // has been preceded by an intersection point along the vertical line. - INTERSECTION_TYPE_OTHER_VLINE_NOT_FIRST, -}; - // Find an intersection on a previous line, but return -1, if the connecting segment of a perimeter was already extruded. -static inline IntersectionTypeOtherVLine intersection_type_on_prev_next_vertical_line( +static inline bool intersection_on_prev_next_vertical_line_valid( const std::vector &segs, size_t iVerticalLine, size_t iIntersection, @@ -492,10 +466,10 @@ static inline IntersectionTypeOtherVLine intersection_type_on_prev_next_vertical if (it_this.has_vertical(side)) // Not the first intersection along the contor. This intersection point // has been preceded by an intersection point along the vertical line. - return INTERSECTION_TYPE_OTHER_VLINE_NOT_FIRST; + return false; int iIntersectionOther = it_this.horizontal(side); if (iIntersectionOther == -1) - return INTERSECTION_TYPE_OTHER_VLINE_UNDEFINED; + return false; assert(side == SegmentIntersection::Side::Right ? (iVerticalLine + 1 < segs.size()) : (iVerticalLine > 0)); const SegmentedIntersectionLine &vline_other = segs[side == SegmentIntersection::Side::Right ? (iVerticalLine + 1) : (iVerticalLine - 1)]; const SegmentIntersection &it_other = vline_other.intersections[iIntersectionOther]; @@ -507,52 +481,50 @@ static inline IntersectionTypeOtherVLine intersection_type_on_prev_next_vertical if (it_other2.is_inner()) // Cannot follow a perimeter segment into the middle of another vertical segment. // Only perimeter segments connecting to the end of a vertical segment are followed. - return INTERSECTION_TYPE_OTHER_VLINE_INNER; + return false; assert(it_other.is_low() == it_other2.is_low()); + if (it_this.horizontal_quality(side) != SegmentIntersection::LinkQuality::Valid) + return false; if (side == SegmentIntersection::Side::Right ? it_this.consumed_perimeter_right : it_other.consumed_perimeter_right) // This perimeter segment was already consumed. - return INTERSECTION_TYPE_OTHER_VLINE_CONSUMED; + return false; if (it_other.is_low() ? it_other.consumed_vertical_up : vline_other.intersections[iIntersectionOther - 1].consumed_vertical_up) // This vertical segment was already consumed. - return INTERSECTION_TYPE_OTHER_VLINE_CONSUMED; - return INTERSECTION_TYPE_OTHER_VLINE_OK; + return false; +#if 0 + if (it_other.vertical_outside() != -1 && it_other.vertical_outside_quality() == SegmentIntersection::LinkQuality::Valid) + // Landed inside a vertical run. Stop here. + return false; +#endif + return true; } -static inline IntersectionTypeOtherVLine intersection_type_on_prev_vertical_line( +static inline bool intersection_on_prev_vertical_line_valid( const std::vector &segs, size_t iVerticalLine, size_t iIntersection) { - return intersection_type_on_prev_next_vertical_line(segs, iVerticalLine, iIntersection, SegmentIntersection::Side::Left); + return intersection_on_prev_next_vertical_line_valid(segs, iVerticalLine, iIntersection, SegmentIntersection::Side::Left); } -static inline IntersectionTypeOtherVLine intersection_type_on_next_vertical_line( +static inline bool intersection_on_next_vertical_line_valid( const std::vector &segs, size_t iVerticalLine, size_t iIntersection) { - return intersection_type_on_prev_next_vertical_line(segs, iVerticalLine, iIntersection, SegmentIntersection::Side::Right); + return intersection_on_prev_next_vertical_line_valid(segs, iVerticalLine, iIntersection, SegmentIntersection::Side::Right); } // Measure an Euclidian length of a perimeter segment when going from iIntersection to iIntersection2. -static inline coordf_t measure_perimeter_prev_next_segment_length( +static inline coordf_t measure_perimeter_horizontal_segment_length( const ExPolygonWithOffset &poly_with_offset, const std::vector &segs, size_t iVerticalLine, size_t iIntersection, - size_t iIntersection2, - bool dir_is_next) + size_t iIntersection2) { - size_t iVerticalLineOther = iVerticalLine; - if (dir_is_next) { - if (++ iVerticalLineOther == segs.size()) - // No successive vertical line. - return coordf_t(-1); - } else if (iVerticalLineOther -- == 0) { - // No preceding vertical line. - return coordf_t(-1); - } - + size_t iVerticalLineOther = iVerticalLine + 1; + assert(iVerticalLineOther < segs.size()); const SegmentedIntersectionLine &vline = segs[iVerticalLine]; const SegmentIntersection &it = vline.intersections[iIntersection]; const SegmentedIntersectionLine &vline2 = segs[iVerticalLineOther]; @@ -562,36 +534,14 @@ static inline coordf_t measure_perimeter_prev_next_segment_length( // const bool ccw = poly_with_offset.is_contour_ccw(vline.iContour); assert(it.type == it2.type); assert(it.iContour == it2.iContour); - assert(it.is_inner()); - const bool forward = it.is_low() == dir_is_next; Point p1(vline.pos, it.pos()); Point p2(vline2.pos, it2.pos()); - return forward ? + return it.is_low() ? segment_length(poly, it .iSegment, p1, it2.iSegment, p2) : segment_length(poly, it2.iSegment, p2, it .iSegment, p1); } -static inline coordf_t measure_perimeter_prev_segment_length( - const ExPolygonWithOffset &poly_with_offset, - const std::vector &segs, - size_t iVerticalLine, - size_t iIntersection, - size_t iIntersection2) -{ - return measure_perimeter_prev_next_segment_length(poly_with_offset, segs, iVerticalLine, iIntersection, iIntersection2, false); -} - -static inline coordf_t measure_perimeter_next_segment_length( - const ExPolygonWithOffset &poly_with_offset, - const std::vector &segs, - size_t iVerticalLine, - size_t iIntersection, - size_t iIntersection2) -{ - return measure_perimeter_prev_next_segment_length(poly_with_offset, segs, iVerticalLine, iIntersection, iIntersection2, true); -} - // Append the points of a perimeter segment when going from iIntersection to iIntersection2. // The first point (the point of iIntersection) will not be inserted, // the last point will be inserted. @@ -646,8 +596,7 @@ static inline coordf_t measure_perimeter_segment_on_vertical_line_length( const SegmentIntersection &itsct = il.intersections[iIntersection]; const SegmentIntersection &itsct2 = il.intersections[iIntersection2]; const Polygon &poly = poly_with_offset.contour(itsct.iContour); - assert(itsct.is_inner()); - assert(itsct2.is_inner()); + assert(itsct.is_inner() == itsct2.is_inner()); assert(itsct.type != itsct2.type); assert(itsct.iContour == itsct2.iContour); Point p1(il.pos, itsct.pos()); @@ -943,7 +892,9 @@ static std::vector slice_region_by_vertical_lines(con // Connect each contour / vertical line intersection point with another two contour / vertical line intersection points. // (fill in SegmentIntersection::{prev_on_contour, prev_on_contour_vertical, next_on_contour, next_on_contour_vertical}. // These contour points are either on the same vertical line, or on the vertical line left / right to the current one. -static void connect_segment_intersections_by_contours(const ExPolygonWithOffset &poly_with_offset, std::vector &segs) +static void connect_segment_intersections_by_contours( + const ExPolygonWithOffset &poly_with_offset, std::vector &segs, + const FillParams ¶ms, const coord_t link_max_length) { for (size_t i_vline = 0; i_vline < segs.size(); ++ i_vline) { SegmentedIntersectionLine &il = segs[i_vline]; @@ -1026,13 +977,88 @@ static void connect_segment_intersections_by_contours(const ExPolygonWithOffset itsct.next_on_contour_type = same_next ? (inext < i_intersection ? SegmentIntersection::LinkType::Down : SegmentIntersection::LinkType::Up) : SegmentIntersection::LinkType::Horizontal; + + if (same_prev) { + // Only follow a vertical perimeter segment if it skips just the outer intersections. + SegmentIntersection *it = &itsct; + SegmentIntersection *end = il.intersections.data() + iprev; + assert(it != end); + if (it > end) + std::swap(it, end); + for (++ it; it != end; ++ it) + if (it->is_inner()) { + itsct.prev_on_contour_quality = SegmentIntersection::LinkQuality::Invalid; + break; + } + } + + if (same_next) { + // Only follow a vertical perimeter segment if it skips just the outer intersections. + SegmentIntersection *it = &itsct; + SegmentIntersection *end = il.intersections.data() + inext; + assert(it != end); + if (it > end) + std::swap(it, end); + for (++ it; it != end; ++ it) + if (it->is_inner()) { + itsct.next_on_contour_quality = SegmentIntersection::LinkQuality::Invalid; + break; + } + } + + // If both iprev and inext are on this vline, then there must not be any intersection with the previous or next contour and we will + // not trace this contour when generating infill. + if (same_prev && same_next) { + assert(iprev != i_intersection); + assert(inext != i_intersection); + if ((iprev > i_intersection) == (inext > i_intersection)) { + // Both closest intersections of this contour are on the same vertical line and at the same side of this point. + // Ignore them when tracing the infill. + itsct.prev_on_contour_quality = SegmentIntersection::LinkQuality::Invalid; + itsct.next_on_contour_quality = SegmentIntersection::LinkQuality::Invalid; + } + } + + if (params.dont_connect) { + if (itsct.prev_on_contour_quality == SegmentIntersection::LinkQuality::Valid) + itsct.prev_on_contour_quality = SegmentIntersection::LinkQuality::TooLong; + if (itsct.next_on_contour_quality == SegmentIntersection::LinkQuality::Valid) + itsct.next_on_contour_quality = SegmentIntersection::LinkQuality::TooLong; + } else if (link_max_length > 0) { + // Measure length of the links. + if (itsct.prev_on_contour_quality == SegmentIntersection::LinkQuality::Valid && + (same_prev ? + measure_perimeter_segment_on_vertical_line_length(poly_with_offset, segs, i_vline, iprev, i_intersection, forward) : + measure_perimeter_horizontal_segment_length(poly_with_offset, segs, i_vline - 1, iprev, i_intersection)) > link_max_length) + itsct.prev_on_contour_quality = SegmentIntersection::LinkQuality::TooLong; + if (itsct.next_on_contour_quality == SegmentIntersection::LinkQuality::Valid && + (same_next ? + measure_perimeter_segment_on_vertical_line_length(poly_with_offset, segs, i_vline, i_intersection, inext, forward) : + measure_perimeter_horizontal_segment_length(poly_with_offset, segs, i_vline, i_intersection, inext)) > link_max_length) + itsct.next_on_contour_quality = SegmentIntersection::LinkQuality::TooLong; + } } + + // Make the LinkQuality::Invalid symmetric on vertical connections. + for (int i_intersection = 0; i_intersection < il.intersections.size(); ++ i_intersection) { + SegmentIntersection &it = il.intersections[i_intersection]; + if (it.has_left_vertical() && it.prev_on_contour_quality == SegmentIntersection::LinkQuality::Invalid) { + SegmentIntersection &it2 = il.intersections[it.left_vertical()]; + assert(it2.left_vertical() == i_intersection); + it2.prev_on_contour_quality = SegmentIntersection::LinkQuality::Invalid; + } + if (it.has_right_vertical() && it.next_on_contour_quality == SegmentIntersection::LinkQuality::Invalid) { + SegmentIntersection &it2 = il.intersections[it.right_vertical()]; + assert(it2.right_vertical() == i_intersection); + it2.next_on_contour_quality = SegmentIntersection::LinkQuality::Invalid; + } + } } #ifndef NDEBUG // Validate the connectivity. for (size_t i_vline = 0; i_vline + 1 < segs.size(); ++ i_vline) { - const SegmentedIntersectionLine &il_left = segs[i_vline]; + const SegmentedIntersectionLine &il_left = segs[i_vline]; const SegmentedIntersectionLine &il_right = segs[i_vline + 1]; for (const SegmentIntersection &it : il_left.intersections) { if (it.has_right_horizontal()) { @@ -1055,6 +1081,28 @@ static void connect_segment_intersections_by_contours(const ExPolygonWithOffset } } } + for (size_t i_vline = 0; i_vline < segs.size(); ++ i_vline) { + const SegmentedIntersectionLine &il = segs[i_vline]; + for (const SegmentIntersection &it : il.intersections) { + auto i_it = int(&it - il.intersections.data()); + if (it.has_left_vertical_up()) { + assert(il.intersections[it.left_vertical_up()].left_vertical_down() == i_it); + assert(il.intersections[it.left_vertical_up()].prev_on_contour_quality == it.prev_on_contour_quality); + } + if (it.has_left_vertical_down()) { + assert(il.intersections[it.left_vertical_down()].left_vertical_up() == i_it); + assert(il.intersections[it.left_vertical_down()].prev_on_contour_quality == it.prev_on_contour_quality); + } + if (it.has_right_vertical_up()) { + assert(il.intersections[it.right_vertical_up()].right_vertical_down() == i_it); + assert(il.intersections[it.right_vertical_up()].next_on_contour_quality == it.next_on_contour_quality); + } + if (it.has_right_vertical_down()) { + assert(il.intersections[it.right_vertical_down()].right_vertical_up() == i_it); + assert(il.intersections[it.right_vertical_down()].next_on_contour_quality == it.next_on_contour_quality); + } + } + } #endif /* NDEBUG */ } @@ -1104,161 +1152,6 @@ static SegmentIntersection& end_of_vertical_run(SegmentedIntersectionLine &il, S return const_cast(end_of_vertical_run(std::as_const(il), std::as_const(start))); } -static void classify_vertical_runs( - const ExPolygonWithOffset &poly_with_offset, const FillParams ¶ms, const coord_t link_max_length, - std::vector &segs, size_t i_vline) -{ - SegmentedIntersectionLine &vline = segs[i_vline]; - for (size_t i_intersection = 0; i_intersection + 1 < vline.intersections.size(); ++ i_intersection) { - if (vline.intersections[i_intersection].type == SegmentIntersection::OUTER_LOW) { - if (vline.intersections[++ i_intersection].type == SegmentIntersection::INNER_LOW) { - for (;;) { - SegmentIntersection &start = vline.intersections[i_intersection]; - SegmentIntersection &end = end_of_vertical_run_raw(start); - SegmentIntersection::LinkQuality link_quality = SegmentIntersection::LinkQuality::Valid; - // End of a contour starting at end and ending above end at the same vertical line. - int inext = end.vertical_outside(); - if (inext == -1) { - i_intersection = &end - vline.intersections.data() + 1; - break; - } - SegmentIntersection &start2 = vline.intersections[inext]; - if (params.dont_connect) - link_quality = SegmentIntersection::LinkQuality::TooLong; - else { - for (SegmentIntersection *it = &end + 1; it != &start2; ++ it) - if (it->is_inner()) { - link_quality = SegmentIntersection::LinkQuality::Invalid; - break; - } - if (link_quality == SegmentIntersection::LinkQuality::Valid && link_max_length > 0) { - // Measure length of the link. - coordf_t link_length = measure_perimeter_segment_on_vertical_line_length( - poly_with_offset, segs, i_vline, i_intersection, inext, end.has_right_vertical_outside()); - if (link_length > link_max_length) - link_quality = SegmentIntersection::LinkQuality::TooLong; - } - } - (end.has_left_vertical_up() ? end.prev_on_contour_quality : end.next_on_contour_quality) = link_quality; - (start2.has_left_vertical_down() ? start2.prev_on_contour_quality : start2.next_on_contour_quality) = link_quality; - if (link_quality != SegmentIntersection::LinkQuality::Valid) { - i_intersection = &end - vline.intersections.data() + 1; - break; - } - i_intersection = &start2 - vline.intersections.data(); - } - } else - ++ i_intersection; - } else - ++ i_intersection; - } -} - -static void classify_horizontal_links( - const ExPolygonWithOffset &poly_with_offset, const FillParams ¶ms, const coord_t link_max_length, - std::vector &segs, size_t i_vline) -{ - SegmentedIntersectionLine &vline_left = segs[i_vline]; - SegmentedIntersectionLine &vline_right = segs[i_vline + 1]; - - // Traverse both left and right together. - size_t i_intersection_left = 0; - size_t i_intersection_right = 0; - while (i_intersection_left + 1 < vline_left.intersections.size() && i_intersection_right + 1 < vline_right.intersections.size()) { - if (i_intersection_left < vline_left.intersections.size() && vline_left.intersections[i_intersection_left].type != SegmentIntersection::INNER_LOW) { - ++ i_intersection_left; - continue; - } - if (i_intersection_right < vline_right.intersections.size() && vline_right.intersections[i_intersection_right].type != SegmentIntersection::INNER_LOW) { - ++ i_intersection_right; - continue; - } - - if (i_intersection_left + 1 >= vline_left.intersections.size()) { - // Trace right only. - } else if (i_intersection_right + 1 >= vline_right.intersections.size()) { - // Trace left only. - } else { - // Trace both. - SegmentIntersection &start_left = vline_left.intersections[i_intersection_left]; - SegmentIntersection &end_left = end_of_vertical_run(vline_left, start_left); - SegmentIntersection &start_right = vline_right.intersections[i_intersection_right]; - SegmentIntersection &end_right = end_of_vertical_run(vline_right, start_right); - // Do these runs overlap? - int end_right_horizontal = end_left.right_horizontal(); - int end_left_horizontal = end_right.left_horizontal(); - if (end_right_horizontal != -1) { - if (end_right_horizontal < &start_right - vline_right.intersections.data()) { - // Left precedes the right segment. - } - } else if (end_left_horizontal != -1) { - if (end_left_horizontal < &start_left - vline_left.intersections.data()) { - // Right precedes the left segment. - } - } - } - } - -#if 0 - for (size_t i_intersection = 0; i_intersection + 1 < seg.intersections.size(); ++ i_intersection) { - if (segs.intersections[i_intersection].type == SegmentIntersection::OUTER_LOW) { - if (segs.intersections[++ i_intersection].type == SegmentIntersection::INNER_LOW) { - for (;;) { - SegmentIntersection &start = segs.intersections[i_intersection]; - SegmentIntersection &end = end_of_vertical_run_raw(start); - SegmentIntersection::LinkQuality link_quality = SegmentIntersection::LinkQuality::Valid; - // End of a contour starting at end and ending above end at the same vertical line. - int inext = end.vertical_outside(); - if (inext == -1) { - i_intersection = &end - segs.intersections.data() + 1; - break; - } - SegmentIntersection &start2 = segs.intersections[inext]; - if (params.dont_connect) - link_quality = SegmentIntersection::LinkQuality::TooLong; - else { - for (SegmentIntersection *it = &end + 1; it != &start2; ++ it) - if (it->is_inner()) { - link_quality = SegmentIntersection::LinkQuality::Invalid; - break; - } - if (link_quality == SegmentIntersection::LinkQuality::Valid && link_max_length > 0) { - // Measure length of the link. - coordf_t link_length = measure_perimeter_segment_on_vertical_line_length( - poly_with_offset, segs, i_vline, i_intersection, inext, intrsctn->has_right_vertical_outside()); - if (link_length > link_max_length) - link_quality = SegmentIntersection::LinkQuality::TooLong; - } - } - (end.has_left_vertical_up() ? end.prev_on_contour_quality : end.next_on_contour_quality) = link_quality; - (start2.has_left_vertical_down() ? start2.prev_on_contour_quality : start2.next_on_contour_quality) = link_quality; - if (link_quality != SegmentIntersection::LinkQuality::Valid) { - i_intersection = &end - segs.intersections.data() + 1; - break; - } - i_intersection = &start2 - segs.intersections.data(); - } - } else - ++ i_intersection; - } else - ++ i_intersection; - } -#endif -} - -static void disconnect_invalid_contour_links( - const ExPolygonWithOffset& poly_with_offset, const FillParams& params, const coord_t link_max_length, std::vector& segs) -{ - // Make the links symmetric! - - // Validate vertical runs including vertical contour links. - for (size_t i_vline = 0; i_vline < segs.size(); ++ i_vline) { - classify_vertical_runs(poly_with_offset, params, link_max_length, segs, i_vline); - if (i_vline > 0) - classify_horizontal_links(poly_with_offset, params, link_max_length, segs, i_vline - 1); - } -} - static void traverse_graph_generate_polylines( const ExPolygonWithOffset& poly_with_offset, const FillParams& params, const coord_t link_max_length, std::vector& segs, Polylines& polylines_out) { @@ -1392,44 +1285,30 @@ static void traverse_graph_generate_polylines( if (try_connect) { // Decide, whether to finish the segment, or whether to follow the perimeter. // 1) Find possible connection points on the previous / next vertical line. - IntersectionTypeOtherVLine intrsection_type_prev = intersection_type_on_prev_vertical_line(segs, i_vline, i_intersection); - IntersectionTypeOtherVLine intrsctn_type_next = intersection_type_on_next_vertical_line(segs, i_vline, i_intersection); + int i_prev = it->left_horizontal(); + int i_next = it->right_horizontal(); + bool intersection_prev_valid = intersection_on_prev_vertical_line_valid(segs, i_vline, i_intersection); + bool intersection_next_valid = intersection_on_next_vertical_line_valid(segs, i_vline, i_intersection); + bool intersection_horizontal_valid = intersection_prev_valid || intersection_next_valid; + // Mark both the left and right connecting segment as consumed, because one cannot go to this intersection point as it has been consumed. + if (i_prev != -1) + segs[i_vline - 1].intersections[i_prev].consumed_perimeter_right = true; + if (i_next != -1) + it->consumed_perimeter_right = true; + // Try to connect to a previous or next vertical line, making a zig-zag pattern. - if (intrsection_type_prev == INTERSECTION_TYPE_OTHER_VLINE_OK || intrsctn_type_next == INTERSECTION_TYPE_OTHER_VLINE_OK) { + if (intersection_horizontal_valid) { // A horizontal connection along the perimeter line exists. - int i_prev = it->left_horizontal(); - int i_next = it->right_horizontal(); - coordf_t dist_prev = (intrsection_type_prev != INTERSECTION_TYPE_OTHER_VLINE_OK) ? std::numeric_limits::max() : - measure_perimeter_prev_segment_length(poly_with_offset, segs, i_vline, i_intersection, i_prev); - coordf_t dist_next = (intrsctn_type_next != INTERSECTION_TYPE_OTHER_VLINE_OK) ? std::numeric_limits::max() : - measure_perimeter_next_segment_length(poly_with_offset, segs, i_vline, i_intersection, i_next); - // Take the shorter path. - //FIXME this may not be always the best strategy to take the shortest connection line now. - bool take_next = (intrsection_type_prev == INTERSECTION_TYPE_OTHER_VLINE_OK && intrsctn_type_next == INTERSECTION_TYPE_OTHER_VLINE_OK) ? - (dist_next < dist_prev) : - intrsctn_type_next == INTERSECTION_TYPE_OTHER_VLINE_OK; - assert(it->is_inner()); - bool skip = params.dont_connect || (link_max_length > 0 && (take_next ? dist_next : dist_prev) > link_max_length); - if (skip) { -#if 1 - // Just skip the connecting contour and start a new path. - goto dont_connect; -#else - polyline_current->points.emplace_back(vline.pos, it->pos()); - polylines_out.emplace_back(); - polyline_current = &polylines_out.back(); - const SegmentedIntersectionLine& il2 = segs[take_next ? (i_vline + 1) : (i_vline - 1)]; - polyline_current->points.emplace_back(il2.pos, il2.intersections[take_next ? i_next : i_prev].pos()); -#endif - } else { - polyline_current->points.emplace_back(vline.pos, it->pos()); - emit_perimeter_prev_next_segment(poly_with_offset, segs, i_vline, it->iContour, i_intersection, take_next ? i_next : i_prev, *polyline_current, take_next); - } - // Mark both the left and right connecting segment as consumed, because one cannot go to this intersection point as it has been consumed. - if (i_prev != -1) - segs[i_vline - 1].intersections[i_prev].consumed_perimeter_right = true; - if (i_next != -1) - it->consumed_perimeter_right = true; + assert(it->is_inner()); + bool take_next = intersection_next_valid; + if (intersection_prev_valid && intersection_next_valid) { + // Take the shorter segment. This greedy heuristics may not be the best. + coordf_t dist_prev = measure_perimeter_horizontal_segment_length(poly_with_offset, segs, i_vline - 1, i_prev, i_intersection); + coordf_t dist_next = measure_perimeter_horizontal_segment_length(poly_with_offset, segs, i_vline, i_intersection, i_next); + take_next = dist_next < dist_prev; + } + polyline_current->points.emplace_back(vline.pos, it->pos()); + emit_perimeter_prev_next_segment(poly_with_offset, segs, i_vline, it->iContour, i_intersection, take_next ? i_next : i_prev, *polyline_current, take_next); //FIXME consume the left / right connecting segments at the other end of this line? Currently it is not critical because a perimeter segment is not followed if the vertical segment at the other side has already been consumed. // Advance to the neighbor line. if (take_next) { @@ -1443,64 +1322,43 @@ static void traverse_graph_generate_polylines( continue; } - // 5) Try to connect to a previous or next point on the same vertical line. - if (int inext = it->vertical_outside(); inext != -1) { - bool valid = true; - // Verify, that there is no intersection with the inner contour up to the end of the contour segment. - // Verify, that the successive segment has not been consumed yet. - if (going_up) { - if (vline.intersections[inext].consumed_vertical_up) - valid = false; - else { - for (int i = i_intersection + 1; i < inext && valid; ++ i) - if (vline.intersections[i].is_inner()) - valid = false; - } - } else { - if (vline.intersections[inext - 1].consumed_vertical_up) - valid = false; - else { - for (int i = inext + 1; i < i_intersection && valid; ++ i) - if (vline.intersections[i].is_inner()) - valid = false; - } - } - if (valid) { - const Polygon &poly = poly_with_offset.contour(it->iContour); - assert(it->iContour == vline.intersections[inext].iContour); - // Skip this perimeter line? - bool skip = params.dont_connect; - bool dir_forward = it->has_right_vertical_outside(); - if (! skip && link_max_length > 0) { - coordf_t link_length = measure_perimeter_segment_on_vertical_line_length( - poly_with_offset, segs, i_vline, i_intersection, inext, dir_forward); - skip = link_length > link_max_length; - } - polyline_current->points.emplace_back(vline.pos, it->pos()); - if (skip) { - // Just skip the connecting contour and start a new path. - polylines_out.emplace_back(); - polyline_current = &polylines_out.back(); - polyline_current->points.emplace_back(vline.pos, vline.intersections[inext].pos()); - } else { - // Consume the connecting contour and the next segment. - emit_perimeter_segment_on_vertical_line(poly_with_offset, segs, i_vline, it->iContour, i_intersection, inext, *polyline_current, dir_forward); - } - // Mark both the left and right connecting segment as consumed, because one cannot go to this intersection point as it has been consumed. - // If there are any outer intersection points skipped (bypassed) by the contour, - // mark them as processed. - if (going_up) - for (int i = i_intersection; i < inext; ++ i) - vline.intersections[i].consumed_vertical_up = true; - else - for (int i = inext; i < i_intersection; ++ i) - vline.intersections[i].consumed_vertical_up = true; - // seg.intersections[going_up ? i_intersection : i_intersection - 1].consumed_vertical_up = true; - it->consumed_perimeter_right = true; - (going_up ? ++ it : -- it)->consumed_perimeter_right = true; - i_intersection = inext; - continue; + // Try to connect to a previous or next point on the same vertical line. + int i_vertical = it->vertical_outside(); + auto vertical_link_quality = (i_vertical == -1 || vline.intersections[i_vertical + (going_up ? 0 : -1)].consumed_vertical_up) ? + SegmentIntersection::LinkQuality::Invalid : it->vertical_outside_quality(); +#if 0 + if (vertical_link_quality == SegmentIntersection::LinkQuality::Valid || + // Follow the link if there is no horizontal link available. + (! intersection_horizontal_valid && vertical_link_quality != SegmentIntersection::LinkQuality::Invalid)) { +#else + if (vertical_link_quality != SegmentIntersection::LinkQuality::Invalid) { +#endif + assert(it->iContour == vline.intersections[i_vertical].iContour); + polyline_current->points.emplace_back(vline.pos, it->pos()); + if (vertical_link_quality == SegmentIntersection::LinkQuality::Valid) + // Consume the connecting contour and the next segment. + emit_perimeter_segment_on_vertical_line(poly_with_offset, segs, i_vline, it->iContour, i_intersection, i_vertical, + *polyline_current, going_up ? it->has_left_vertical_up() : it->has_right_vertical_down()); + else { + // Just skip the connecting contour and start a new path. + polylines_out.emplace_back(); + polyline_current = &polylines_out.back(); + polyline_current->points.emplace_back(vline.pos, vline.intersections[i_vertical].pos()); } + // Mark both the left and right connecting segment as consumed, because one cannot go to this intersection point as it has been consumed. + // If there are any outer intersection points skipped (bypassed) by the contour, + // mark them as processed. + if (going_up) + for (int i = i_intersection; i < i_vertical; ++i) + vline.intersections[i].consumed_vertical_up = true; + else + for (int i = i_vertical; i < i_intersection; ++i) + vline.intersections[i].consumed_vertical_up = true; + // seg.intersections[going_up ? i_intersection : i_intersection - 1].consumed_vertical_up = true; + it->consumed_perimeter_right = true; + (going_up ? ++it : --it)->consumed_perimeter_right = true; + i_intersection = i_vertical; + continue; } dont_connect: @@ -1553,10 +1411,16 @@ struct MonotonousRegion int left_intersection_point(bool region_flipped) const { return region_flipped ? left.high : left.low; } int right_intersection_point(bool region_flipped) const { return (region_flipped == flips) ? right.low : right.high; } +#if NDEBUG // Left regions are used to track whether all regions left to this one have already been printed. boost::container::small_vector left_neighbors; // Right regions are held to pick a next region to be extruded using the "Ant colony" heuristics. boost::container::small_vector right_neighbors; +#else + // For debugging, use the normal vector as it is better supported by debug visualizers. + std::vector left_neighbors; + std::vector right_neighbors; +#endif }; struct AntPath @@ -1607,7 +1471,7 @@ public: int i_right = vline_from.intersections[i_from].right_horizontal(); if (i_right == i_to && vline_from.intersections[i_from].next_on_contour_quality == SegmentIntersection::LinkQuality::Valid) { // Measure length along the contour. - path.length = unscale(measure_perimeter_next_segment_length(m_poly_with_offset, m_segs, region_from.right.vline, i_from, i_to)); + path.length = unscale(measure_perimeter_horizontal_segment_length(m_poly_with_offset, m_segs, region_from.right.vline, i_from, i_to)); } } if (path.length == -1.) { @@ -1785,6 +1649,24 @@ static std::vector generate_montonous_regions(std::vector monotonous_regions; +#ifndef NDEBUG + #define SLIC3R_DEBUG_MONOTONOUS_REGIONS +#endif + +#ifdef SLIC3R_DEBUG_MONOTONOUS_REGIONS + std::vector>> consumed(segs.size()); + auto test_overlap = [&consumed](int segment, int low, int high) { + for (const std::pair& interval : consumed[segment]) + if ((low >= interval.first && low <= interval.second) || + (interval.first >= low && interval.first <= high)) + return true; + consumed[segment].emplace_back(low, high); + return false; + }; +#else + auto test_overlap = [](int, int, int) { return false; }; +#endif + for (int i_vline_seed = 0; i_vline_seed < segs.size(); ++ i_vline_seed) { SegmentedIntersectionLine &vline_seed = segs[i_vline_seed]; for (int i_intersection_seed = 1; i_intersection_seed + 1 < vline_seed.intersections.size(); ) { @@ -1805,6 +1687,7 @@ static std::vector generate_montonous_regions(std::vectorconsumed_vertical_up = true; int num_lines = 1; while (++ i_vline < segs.size()) { @@ -1826,7 +1709,8 @@ static std::vector generate_montonous_regions(std::vectorconsumed_vertical_up = true; - ++ num_lines; + assert(! test_overlap(region.right.vline, region.right.low, region.right.high)); + ++ num_lines; left = right; } // Even number of lines makes the infill zig-zag to exit on the other side of the region than where it starts. @@ -1898,6 +1782,40 @@ static void connect_monotonous_regions(std::vector ®ions, s } } } + + // Sometimes a segment may indicate that it connects to a segment on the other side while the other does not. + // This may be a valid case if one side contains runs of OUTER_LOW, INNER_LOW, {INNER_HIGH, INNER_LOW}*, INNER_HIGH, OUTER_HIGH, + // where the part in the middle does not connect to the other side, but it will be extruded through. + for (MonotonousRegion ®ion : regions) { + std::sort(region.left_neighbors.begin(), region.left_neighbors.end()); + std::sort(region.right_neighbors.begin(), region.right_neighbors.end()); + } + for (MonotonousRegion ®ion : regions) { + for (MonotonousRegion *neighbor : region.left_neighbors) { + auto it = std::lower_bound(neighbor->right_neighbors.begin(), neighbor->right_neighbors.end(), ®ion); + if (it == neighbor->right_neighbors.end() || *it != ®ion) + neighbor->right_neighbors.insert(it, ®ion); + } + for (MonotonousRegion *neighbor : region.right_neighbors) { + auto it = std::lower_bound(neighbor->left_neighbors.begin(), neighbor->left_neighbors.end(), ®ion); + if (it == neighbor->left_neighbors.end() || *it != ®ion) + neighbor->left_neighbors.insert(it, ®ion); + } + } + +#ifndef NDEBUG + // Verify symmetry of the left_neighbors / right_neighbors. + for (MonotonousRegion ®ion : regions) { + for (MonotonousRegion *neighbor : region.left_neighbors) { + assert(std::count(region.left_neighbors.begin(), region.left_neighbors.end(), neighbor) == 1); + assert(std::find(neighbor->right_neighbors.begin(), neighbor->right_neighbors.end(), ®ion) != neighbor->right_neighbors.end()); + } + for (MonotonousRegion *neighbor : region.right_neighbors) { + assert(std::count(region.right_neighbors.begin(), region.right_neighbors.end(), neighbor) == 1); + assert(std::find(neighbor->left_neighbors.begin(), neighbor->left_neighbors.end(), ®ion) != neighbor->left_neighbors.end()); + } + } +#endif /* NDEBUG */ } // Raad Salman: Algorithms for the Precedence Constrained Generalized Travelling Salesperson Problem @@ -1936,8 +1854,8 @@ inline void print_ant(const std::string& fmt, TArgs&&... args) { static std::vector chain_monotonous_regions( std::vector ®ions, const ExPolygonWithOffset &poly_with_offset, const std::vector &segs, std::mt19937_64 &rng) { - // Number of left neighbors (regions that this region depends on, this region cannot be printed before the regions left of it are printed). - std::vector left_neighbors_unprocessed(regions.size(), 0); + // Number of left neighbors (regions that this region depends on, this region cannot be printed before the regions left of it are printed) + self. + std::vector left_neighbors_unprocessed(regions.size(), 1); // Queue of regions, which have their left neighbors already printed. std::vector queue; queue.reserve(regions.size()); @@ -1945,7 +1863,7 @@ static std::vector chain_monotonous_regions( if (region.left_neighbors.empty()) queue.emplace_back(®ion); else - left_neighbors_unprocessed[®ion - regions.data()] = int(region.left_neighbors.size()); + left_neighbors_unprocessed[®ion - regions.data()] += int(region.left_neighbors.size()); // Make copy of structures that need to be initialized at each ant iteration. auto left_neighbors_unprocessed_initial = left_neighbors_unprocessed; auto queue_initial = queue; @@ -1964,6 +1882,64 @@ static std::vector chain_monotonous_regions( }; std::vector next_candidates; + auto validate_unprocessed = +#ifdef NDEBUG + []() { return true; }; +#else + [®ions, &left_neighbors_unprocessed, &path, &queue]() { + std::vector regions_processed(regions.size(), false); + std::vector regions_in_queue(regions.size(), false); + for (const MonotonousRegion *region : queue) { + // This region is not processed yet, his predecessors are processed. + assert(left_neighbors_unprocessed[region - regions.data()] == 1); + regions_in_queue[region - regions.data()] = true; + } + for (const MonotonousRegionLink &link : path) { + assert(left_neighbors_unprocessed[link.region - regions.data()] == 0); + regions_processed[link.region - regions.data()] = true; + } + for (size_t i = 0; i < regions_processed.size(); ++ i) { + assert(! regions_processed[i] || ! regions_in_queue[i]); + const MonotonousRegion ®ion = regions[i]; + if (regions_processed[i] || regions_in_queue[i]) { + assert(left_neighbors_unprocessed[i] == (regions_in_queue[i] ? 1 : 0)); + // All left neighbors should be processed already. + for (const MonotonousRegion *left : region.left_neighbors) { + assert(regions_processed[left - regions.data()]); + assert(left_neighbors_unprocessed[left - regions.data()] == 0); + } + } else { + // Some left neihgbor should not be processed yet. + assert(left_neighbors_unprocessed[i] > 1); + size_t num_predecessors_unprocessed = 0; + bool has_left_last_on_path = false; + for (const MonotonousRegion* left : region.left_neighbors) { + size_t iprev = left - regions.data(); + if (regions_processed[iprev]) { + assert(left_neighbors_unprocessed[iprev] == 0); + if (left == path.back().region) { + // This region should actually be on queue, but to optimize the queue management + // this item will be processed in the next round by traversing path.back().region->right_neighbors before processing the queue. + assert(! has_left_last_on_path); + has_left_last_on_path = true; + ++ num_predecessors_unprocessed; + } + } else { + if (regions_in_queue[iprev]) + assert(left_neighbors_unprocessed[iprev] == 1); + else + assert(left_neighbors_unprocessed[iprev] > 1); + ++ num_predecessors_unprocessed; + } + } + assert(num_predecessors_unprocessed > 0); + assert(left_neighbors_unprocessed[i] == num_predecessors_unprocessed + 1); + } + } + return true; + }; +#endif /* NDEBUG */ + // How many times to repeat the ant simulation. constexpr int num_rounds = 10; // With how many ants each of the run will be performed? @@ -1999,13 +1975,16 @@ static std::vector chain_monotonous_regions( path.clear(); queue = queue_initial; left_neighbors_unprocessed = left_neighbors_unprocessed_initial; + assert(validate_unprocessed()); // Pick randomly the first from the queue at random orientation. int first_idx = std::uniform_int_distribution<>(0, int(queue.size()) - 1)(rng); path.emplace_back(MonotonousRegionLink{ queue[first_idx], rng() > rng.max() / 2 }); *(queue.begin() + first_idx) = std::move(queue.back()); queue.pop_back(); + -- left_neighbors_unprocessed[path.back().region - regions.data()]; assert(left_neighbors_unprocessed[path.back().region - regions.data()] == 0); - print_ant("\tRegion (%1%:%2%,%3%) (%4%:%5%,%6%)", + assert(validate_unprocessed()); + print_ant("\tRegion (%1%:%2%,%3%) (%4%:%5%,%6%)", path.back().region->left.vline, path.back().flipped ? path.back().region->left.high : path.back().region->left.low, path.back().flipped ? path.back().region->left.low : path.back().region->left.high, @@ -2014,7 +1993,7 @@ static std::vector chain_monotonous_regions( path.back().flipped == path.back().region->flips ? path.back().region->right.low : path.back().region->right.high); while (! queue.empty() || ! path.back().region->right_neighbors.empty()) { - // Chain. + // Chain. MonotonousRegion ®ion = *path.back().region; bool dir = path.back().flipped; // Sort by distance to pt. @@ -2022,8 +2001,8 @@ static std::vector chain_monotonous_regions( next_candidates.reserve(region.right_neighbors.size() * 2); for (MonotonousRegion *next : region.right_neighbors) { int &unprocessed = left_neighbors_unprocessed[next - regions.data()]; - assert(unprocessed > 0); - if (-- unprocessed == 0) { + assert(unprocessed > 1); + if (-- unprocessed == 1) { // Dependencies of the successive blocks are satisfied. AntPath &path1 = path_matrix(region, dir, *next, false); AntPath &path1_flipped = path_matrix(region, ! dir, *next, true); @@ -2038,6 +2017,7 @@ static std::vector chain_monotonous_regions( if (num_direct_neighbors == 0) { // Add the queue candidates. for (MonotonousRegion *next : queue) { + assert(left_neighbors_unprocessed[next - regions.data()] == 1); AntPath &path1 = path_matrix(region, dir, *next, false); AntPath &path1_flipped = path_matrix(region, ! dir, *next, true); AntPath &path2 = path_matrix(region, dir, *next, true); @@ -2068,13 +2048,13 @@ static std::vector chain_monotonous_regions( print_ant("\tTaking path at probability threshold %1% of %2%", probability_threshold, total_probability); } // Move the other right neighbors with satisified constraints to the queue. - bool direct_neighbor_taken = take_path - next_candidates.begin() < num_direct_neighbors; for (std::vector::iterator it_next_candidate = next_candidates.begin(); it_next_candidate != next_candidates.begin() + num_direct_neighbors; ++ it_next_candidate) if ((queue.empty() || it_next_candidate->region != queue.back()) && it_next_candidate->region != take_path->region) queue.emplace_back(it_next_candidate->region); if (take_path - next_candidates.begin() >= num_direct_neighbors) { // Remove the selected path from the queue. auto it = std::find(queue.begin(), queue.end(), take_path->region); + assert(it != queue.end()); *it = queue.back(); queue.pop_back(); } @@ -2084,6 +2064,8 @@ static std::vector chain_monotonous_regions( path.back().next = take_path->link; path.back().next_flipped = take_path->link_flipped; path.emplace_back(MonotonousRegionLink{ next_region, next_dir }); + assert(left_neighbors_unprocessed[next_region - regions.data()] == 1); + left_neighbors_unprocessed[next_region - regions.data()] = 0; print_ant("\tRegion (%1%:%2%,%3%) (%4%:%5%,%6%) length to prev %7%", next_region->left.vline, next_dir ? next_region->left.high : next_region->left.low, @@ -2103,7 +2085,8 @@ static std::vector chain_monotonous_regions( // Update pheromones along this link. take_path->link->pheromone = (1.f - pheromone_evaporation) * take_path->link->pheromone + pheromone_evaporation * pheromone_initial_deposit; - } + assert(validate_unprocessed()); + } // Perform 3-opt local optimization of the path. monotonous_3_opt(path, segs); @@ -2206,10 +2189,11 @@ static void polylines_from_paths(const std::vector &path, do { ++ it; iright = std::max(iright, it->right_horizontal()); - } while (it->type != SegmentIntersection::INNER_HIGH); + assert(it->is_inner()); + } while (it->type != SegmentIntersection::INNER_HIGH || (it + 1)->type != SegmentIntersection::OUTER_HIGH); polyline->points.emplace_back(vline.pos, it->pos()); int inext = it->vertical_up(); - if (inext == -1) + if (inext == -1 || it->vertical_up_quality() != SegmentIntersection::LinkQuality::Valid) break; const Polygon &poly = poly_with_offset.contour(it->iContour); assert(it->iContour == vline.intersections[inext].iContour); @@ -2218,17 +2202,18 @@ static void polylines_from_paths(const std::vector &path, } } else { // Going down. - assert(it->is_high()); - assert(i_intersection > 0); + assert(it->is_high()); + assert(i_intersection > 0); for (;;) { do { -- it; if (int iright_new = it->right_horizontal(); iright_new != -1) iright = iright_new; - } while (it->type != SegmentIntersection::INNER_LOW); + assert(it->is_inner()); + } while (it->type != SegmentIntersection::INNER_LOW || (it - 1)->type != SegmentIntersection::OUTER_LOW); polyline->points.emplace_back(vline.pos, it->pos()); int inext = it->vertical_down(); - if (inext == -1) + if (inext == -1 || it->vertical_down_quality() != SegmentIntersection::LinkQuality::Valid) break; const Polygon &poly = poly_with_offset.contour(it->iContour); assert(it->iContour == vline.intersections[inext].iContour); @@ -2347,7 +2332,8 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillP #endif /* SLIC3R_DEBUG */ std::vector segs = slice_region_by_vertical_lines(poly_with_offset, n_vlines, x0, line_spacing); - connect_segment_intersections_by_contours(poly_with_offset, segs); + // Connect by horizontal / vertical links, classify the links based on link_max_length as too long. + connect_segment_intersections_by_contours(poly_with_offset, segs, params, link_max_length); #ifdef SLIC3R_DEBUG // Paint the segments and finalize the SVG file. diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 9c7a31d3e..a9175ab0c 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -1123,7 +1123,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm/s"); def->min = 0; def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(60)); + def->set_default_value(new ConfigOptionFloat(15)); def = this->add("layer_gcode", coString); def->label = L("After layer change G-code"); diff --git a/src/libslic3r/ShortestPath.cpp b/src/libslic3r/ShortestPath.cpp index 01f39872f..314bbf716 100644 --- a/src/libslic3r/ShortestPath.cpp +++ b/src/libslic3r/ShortestPath.cpp @@ -43,6 +43,7 @@ std::vector> chain_segments_closest_point(std::vector> 1)); out.emplace_back(next_idx / 2, (next_idx & 1) != 0); this_idx = next_idx ^ 1; } @@ -165,7 +166,9 @@ std::vector> chain_segments_greedy_constrained_reversals EndPoint *first_point = nullptr; size_t first_point_idx = std::numeric_limits::max(); if (start_near != nullptr) { - size_t idx = find_closest_point(kdtree, start_near->template cast()); + size_t idx = find_closest_point(kdtree, start_near->template cast(), + // Don't start with a reverse segment, if flipping of the segment is not allowed. + [&could_reverse_func](size_t idx) { return (idx & 1) == 0 || could_reverse_func(idx >> 1); }); assert(idx < end_points.size()); first_point = &end_points[idx]; first_point->distance_out = 0.; From b8e02a54056dce138d17f7cc8449f3d8f88f6b5a Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 28 Apr 2020 18:00:42 +0200 Subject: [PATCH 07/16] Fixed handling of fill_pattern field if not all patterns are allowed for the internal infill. --- src/slic3r/GUI/Field.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index 12699c37f..186444f66 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -943,7 +943,7 @@ void Choice::set_value(const boost::any& value, bool change_event) } case coEnum: { int val = boost::any_cast(value); - if (m_opt_id == "top_fill_pattern" || m_opt_id == "bottom_fill_pattern") + if (m_opt_id == "top_fill_pattern" || m_opt_id == "bottom_fill_pattern" || m_opt_id == "fill_pattern") { if (!m_opt.enum_values.empty()) { std::string key; @@ -1013,7 +1013,7 @@ boost::any& Choice::get_value() if (m_opt.type == coEnum) { int ret_enum = field->GetSelection(); - if (m_opt_id == "top_fill_pattern" || m_opt_id == "bottom_fill_pattern") + if (m_opt_id == "top_fill_pattern" || m_opt_id == "bottom_fill_pattern" || m_opt_id == "fill_pattern") { if (!m_opt.enum_values.empty()) { std::string key = m_opt.enum_values[ret_enum]; @@ -1025,8 +1025,6 @@ boost::any& Choice::get_value() else m_value = static_cast(0); } - if (m_opt_id.compare("fill_pattern") == 0) - m_value = static_cast(ret_enum); else if (m_opt_id.compare("ironing_type") == 0) m_value = static_cast(ret_enum); else if (m_opt_id.compare("gcode_flavor") == 0) From ef89c73fd557ea2f7b67f62f00360b76de62f370 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 28 Apr 2020 18:28:11 +0200 Subject: [PATCH 08/16] fixing a compilation issue on a buggy GCC on R-PI --- src/libslic3r/Fill/FillRectilinear2.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libslic3r/Fill/FillRectilinear2.cpp b/src/libslic3r/Fill/FillRectilinear2.cpp index e16d57ad6..b88520b55 100644 --- a/src/libslic3r/Fill/FillRectilinear2.cpp +++ b/src/libslic3r/Fill/FillRectilinear2.cpp @@ -133,11 +133,11 @@ struct SegmentIntersection // but it could be intersected with OUTER_LOW, INNER_LOW, INNER_HIGH, OUTER_HIGH, // and there may be more than one pair of INNER_LOW, INNER_HIGH between OUTER_LOW, OUTER_HIGH. enum SegmentIntersectionType : char { - OUTER_LOW = 0, - OUTER_HIGH = 1, - INNER_LOW = 2, - INNER_HIGH = 3, - UNKNOWN = -1 + UNKNOWN, + OUTER_LOW, + OUTER_HIGH, + INNER_LOW, + INNER_HIGH, }; SegmentIntersectionType type { UNKNOWN }; From 985225cd387cc6c88549ade2bc81c10c87726070 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 28 Apr 2020 20:39:47 +0200 Subject: [PATCH 09/16] Try to fix build with old wxwidgets builtin png and expat --- src/CMakeLists.txt | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index cdc8e0a13..7509ad474 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -59,9 +59,17 @@ if (SLIC3R_GUI) include(${wxWidgets_USE_FILE}) - list(FILTER wxWidgets_LIBRARIES EXCLUDE REGEX png) - list(FILTER wxWidgets_LIBRARIES EXCLUDE REGEX expat) - list(APPEND wxWidgets_LIBRARIES ${PNG_LIBRARIES} ${EXPAT_LIBRARIES}) + string(REGEX MATCH "wxpng" WX_PNG_BUILTIN ${wxWidgets_LIBRARIES}) + if (PNG_FOUND AND NOT WX_PNG_BUILTIN) + list(FILTER wxWidgets_LIBRARIES EXCLUDE REGEX png) + list(APPEND wxWidgets_LIBRARIES ${PNG_LIBRARIES}) + endif () + + string(REGEX MATCH "wxexpat" WX_EXPAT_BUILTIN ${wxWidgets_LIBRARIES}) + if (EXPAT_FOUND AND NOT WX_EXPAT_BUILTIN) + list(FILTER wxWidgets_LIBRARIES EXCLUDE REGEX expat) + list(APPEND wxWidgets_LIBRARIES ${EXPAT_LIBRARIES}) + endif () # list(REMOVE_ITEM wxWidgets_LIBRARIES oleacc) message(STATUS "wx libs: ${wxWidgets_LIBRARIES}") @@ -182,13 +190,13 @@ if (WIN32) elseif (XCODE) # Because of Debug/Release/etc. configurations (similar to MSVC) the slic3r binary is located in an extra level add_custom_command(TARGET PrusaSlicer POST_BUILD - COMMAND ln -sf "${SLIC3R_RESOURCES_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/resources" + COMMAND ln -sfn "${SLIC3R_RESOURCES_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/resources" COMMENT "Symlinking the resources directory into the build tree" VERBATIM ) else () add_custom_command(TARGET PrusaSlicer POST_BUILD - COMMAND ln -sf "${SLIC3R_RESOURCES_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/../resources" + COMMAND ln -sfn "${SLIC3R_RESOURCES_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/../resources" COMMENT "Symlinking the resources directory into the build tree" VERBATIM ) From 98f0cc0dec4bce83db8e5a53a219032331a50ba2 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 28 Apr 2020 20:43:46 +0200 Subject: [PATCH 10/16] Follow up, make png non required for now --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 691e234ae..5e8726d08 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -383,7 +383,7 @@ if (NOT EXPAT_FOUND) set(EXPAT_LIBRARIES expat) endif () -find_package(PNG REQUIRED) +find_package(PNG) find_package(OpenGL REQUIRED) From 6f7fa4bc09ce174350186a47dfc21754bb659d85 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 28 Apr 2020 21:01:09 +0200 Subject: [PATCH 11/16] Fix librt linking for wxWidgets --- src/CMakeLists.txt | 6 ++++++ src/slic3r/CMakeLists.txt | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7509ad474..281f9e663 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -71,6 +71,12 @@ if (SLIC3R_GUI) list(APPEND wxWidgets_LIBRARIES ${EXPAT_LIBRARIES}) endif () + # This is an issue in the new wxWidgets cmake build, doesn't deal with librt + find_library(LIBRT rt) + if(LIBRT) + list(APPEND wxWidgets_LIBRARIES ${LIBRT}) + endif() + # list(REMOVE_ITEM wxWidgets_LIBRARIES oleacc) message(STATUS "wx libs: ${wxWidgets_LIBRARIES}") diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 3711b12a1..2ac8a1ff0 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -204,7 +204,7 @@ add_library(libslic3r_gui STATIC ${SLIC3R_GUI_SOURCES}) encoding_check(libslic3r_gui) -target_link_libraries(libslic3r_gui libslic3r avrdude cereal imgui GLEW::GLEW OpenGL::GL OpenGL::GLU hidapi libcurl ${wxWidgets_LIBRARIES} rt) +target_link_libraries(libslic3r_gui libslic3r avrdude cereal imgui GLEW::GLEW OpenGL::GL OpenGL::GLU hidapi libcurl ${wxWidgets_LIBRARIES}) if(APPLE) target_link_libraries(libslic3r_gui ${DISKARBITRATION_LIBRARY}) From 2a8c485b32c2342954771328bac27f78f10de444 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 28 Apr 2020 21:11:54 +0200 Subject: [PATCH 12/16] suppress unnecessary test output in release mode --- tests/libslic3r/test_marchingsquares.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/tests/libslic3r/test_marchingsquares.cpp b/tests/libslic3r/test_marchingsquares.cpp index 9912ff2ca..e9e016157 100644 --- a/tests/libslic3r/test_marchingsquares.cpp +++ b/tests/libslic3r/test_marchingsquares.cpp @@ -325,7 +325,7 @@ static void recreate_object_from_rasters(const std::string &objname, float lh) { sla::RasterBase::Resolution res{2560, 1440}; double disp_w = 120.96; double disp_h = 68.04; - + size_t cntr = 0; for (ExPolygons &layer : layers) { auto rst = create_raster(res, disp_w, disp_h); @@ -334,26 +334,31 @@ static void recreate_object_from_rasters(const std::string &objname, float lh) { rst.draw(island); } +#ifndef NDEBUG std::fstream out(objname + std::to_string(cntr) + ".png", std::ios::out); out << rst.encode(sla::PNGRasterEncoder{}); out.close(); +#endif ExPolygons layer_ = sla::raster_to_polygons(rst); // float delta = scaled(std::min(rst.pixel_dimensions().h_mm, // rst.pixel_dimensions().w_mm)) / 2; // layer_ = expolygons_simplify(layer_, delta); - + +#ifndef NDEBUG SVG svg(objname + std::to_string(cntr) + ".svg", BoundingBox(Point{0, 0}, Point{scaled(disp_w), scaled(disp_h)})); svg.draw(layer_); svg.draw(layer, "green"); svg.Close(); +#endif double layera = 0., layera_ = 0.; for (auto &p : layer) layera += p.area(); for (auto &p : layer_) layera_ += p.area(); - +#ifndef NDEBUG std::cout << cntr++ << std::endl; +#endif double diff = std::abs(layera_ - layera); REQUIRE((diff <= 0.1 * layera || diff < scaled(1.) * scaled(1.))); From 9cb59759564a0478f5b07bfa377930e284403d86 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 29 Apr 2020 09:44:46 +0200 Subject: [PATCH 13/16] bring back required switch for opengl with dep_GLEW --- deps/GLEW/GLEW.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/GLEW/GLEW.cmake b/deps/GLEW/GLEW.cmake index 285b9be4d..5916ce7eb 100644 --- a/deps/GLEW/GLEW.cmake +++ b/deps/GLEW/GLEW.cmake @@ -1,6 +1,6 @@ # We have to check for OpenGL to compile GLEW set(OpenGL_GL_PREFERENCE "LEGACY") # to prevent a nasty warning by cmake -find_package(OpenGL QUIET) +find_package(OpenGL QUIET REQUIRED) prusaslicer_add_cmake_project( GLEW From cd5d70d5e14561375f29fccc821fcaa55381d37b Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 29 Apr 2020 10:18:29 +0200 Subject: [PATCH 14/16] GCodeViewer -> Fixed z slider in initial preview --- src/slic3r/GUI/3DScene.cpp | 3 --- src/slic3r/GUI/3DScene.hpp | 4 ---- src/slic3r/GUI/GLCanvas3D.cpp | 12 +++++++----- src/slic3r/GUI/GLCanvas3D.hpp | 3 ++- src/slic3r/GUI/GUI_Preview.cpp | 12 ++++++++---- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 63cacdd45..cac0910e2 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -340,8 +340,6 @@ BoundingBoxf3 GLVolume::transformed_convex_hull_bounding_box(const Transform3d & bounding_box().transformed(trafo); } - -#if !ENABLE_GCODE_VIEWER void GLVolume::set_range(double min_z, double max_z) { this->qverts_range.first = 0; @@ -376,7 +374,6 @@ void GLVolume::set_range(double min_z, double max_z) } } } -#endif // !ENABLE_GCODE_VIEWER void GLVolume::render() const { diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index 28295a35f..70d6fb016 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -442,9 +442,7 @@ public: bool empty() const { return this->indexed_vertex_array.empty(); } -#if !ENABLE_GCODE_VIEWER void set_range(double low, double high); -#endif // !ENABLE_GCODE_VIEWER void render() const; #if !ENABLE_SLOPE_RENDERING @@ -562,9 +560,7 @@ public: void clear() { for (auto *v : volumes) delete v; volumes.clear(); } bool empty() const { return volumes.empty(); } -#if !ENABLE_GCODE_VIEWER void set_range(double low, double high) { for (GLVolume *vol : this->volumes) vol->set_range(low, high); } -#endif // !ENABLE_GCODE_VIEWER void set_print_box(float min_x, float min_y, float min_z, float max_x, float max_y, float max_z) { m_print_box_min[0] = min_x; m_print_box_min[1] = min_y; m_print_box_min[2] = min_z; diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index f8c0889fd..647994717 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -932,11 +932,7 @@ void GLCanvas3D::LegendTexture::fill_color_print_legend_items( const GLCanvas3D std::vector> cp_values; cp_values.reserve(custom_gcode_per_print_z.size()); -#if ENABLE_GCODE_VIEWER - const std::vector& print_zs = canvas.get_layers_zs(); -#else std::vector print_zs = canvas.get_current_print_zs(true); -#endif // ENABLE_GCODE_VIEWER for (auto custom_code : custom_gcode_per_print_z) { if (custom_code.gcode != ColorChangeCode) @@ -2327,11 +2323,16 @@ void GLCanvas3D::ensure_on_bed(unsigned int object_idx) #if ENABLE_GCODE_VIEWER -const std::vector& GLCanvas3D::get_layers_zs() const +const std::vector& GLCanvas3D::get_gcode_layers_zs() const { return m_gcode_viewer.get_layers_zs(); } +std::vector GLCanvas3D::get_volumes_print_zs(bool active_only) const +{ + return m_volumes.get_current_print_zs(active_only); +} + void GLCanvas3D::set_gcode_options_visibility_from_flags(unsigned int flags) { m_gcode_viewer.set_options_visibility_from_flags(flags); @@ -2350,6 +2351,7 @@ void GLCanvas3D::set_toolpath_view_type(GCodeViewer::EViewType type) void GLCanvas3D::set_toolpaths_z_range(const std::array& range) { m_gcode_viewer.set_layers_z_range(range); + m_volumes.set_range(range[0] - 1e-6, range[1] + 1e-6); } #else std::vector GLCanvas3D::get_current_print_zs(bool active_only) const diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index a5066d923..f057ea7d6 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -648,7 +648,8 @@ public: #if ENABLE_GCODE_VIEWER bool is_gcode_legend_enabled() const { return m_gcode_viewer.is_legend_enabled(); } GCodeViewer::EViewType get_gcode_view_type() const { return m_gcode_viewer.get_view_type(); } - const std::vector& get_layers_zs() const; + const std::vector& get_gcode_layers_zs() const; + std::vector get_volumes_print_zs(bool active_only) const; unsigned int get_gcode_options_visibility_flags() const { return m_gcode_viewer.get_options_visibility_flags(); } void set_gcode_options_visibility_from_flags(unsigned int flags); unsigned int get_toolpath_role_visibility_flags() const { return m_gcode_viewer.get_toolpath_role_visibility_flags(); } diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 72f40bb6b..7771484f5 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -1057,6 +1057,10 @@ void Preview::load_print_as_fff(bool keep_z_range) if (IsShown()) { +#if ENABLE_GCODE_VIEWER + std::vector zs; +#endif // ENABLE_GCODE_VIEWER + m_canvas->set_selected_extruder(0); if (gcode_preview_data_valid) { // Load the real G-code preview. @@ -1065,6 +1069,7 @@ void Preview::load_print_as_fff(bool keep_z_range) m_canvas->refresh_gcode_preview(*m_gcode_result, colors); GetSizer()->Show(m_bottom_toolbar_sizer); GetSizer()->Layout(); + zs = m_canvas->get_gcode_layers_zs(); #else m_canvas->load_gcode_preview(*m_gcode_preview_data, colors); #endif // ENABLE_GCODE_VIEWER @@ -1075,14 +1080,13 @@ void Preview::load_print_as_fff(bool keep_z_range) #if ENABLE_GCODE_VIEWER GetSizer()->Hide(m_bottom_toolbar_sizer); GetSizer()->Layout(); + zs = m_canvas->get_volumes_print_zs(true); #endif // ENABLE_GCODE_VIEWER } -#if ENABLE_GCODE_VIEWER - const std::vector& zs = m_canvas->get_layers_zs(); -#else +#if !ENABLE_GCODE_VIEWER show_hide_ui_elements(gcode_preview_data_valid ? "full" : "simple"); std::vector zs = m_canvas->get_current_print_zs(true); -#endif // ENABLE_GCODE_VIEWER +#endif // !ENABLE_GCODE_VIEWER if (zs.empty()) { // all layers filtered out reset_sliders(true); From d828a1e80b39eb7bffbcde7b7a48741791a56b43 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Wed, 29 Apr 2020 10:50:28 +0200 Subject: [PATCH 15/16] single slicer instance check for other instances during startup send message with command line arguments if found and terminate listen for those messages and load objects from paths in messages from them --- CMakeLists.txt | 3 + cmake/modules/FindDBus.cmake | 59 ++++ src/PrusaSlicer.cpp | 13 +- src/PrusaSlicer_app_msvc.cpp | 3 +- src/libslic3r/PrintConfig.cpp | 5 + src/slic3r/CMakeLists.txt | 8 + src/slic3r/GUI/AppConfig.cpp | 3 + src/slic3r/GUI/GUI_App.cpp | 72 +++-- src/slic3r/GUI/GUI_App.hpp | 5 +- src/slic3r/GUI/InstanceCheck.cpp | 495 +++++++++++++++++++++++++++++ src/slic3r/GUI/InstanceCheck.hpp | 91 ++++++ src/slic3r/GUI/InstanceCheckMac.h | 8 + src/slic3r/GUI/InstanceCheckMac.mm | 68 ++++ src/slic3r/GUI/MainFrame.cpp | 4 +- src/slic3r/GUI/Plater.cpp | 24 ++ src/slic3r/GUI/Preferences.cpp | 9 + 16 files changed, 841 insertions(+), 29 deletions(-) create mode 100644 cmake/modules/FindDBus.cmake create mode 100644 src/slic3r/GUI/InstanceCheck.cpp create mode 100644 src/slic3r/GUI/InstanceCheck.hpp create mode 100644 src/slic3r/GUI/InstanceCheckMac.h create mode 100644 src/slic3r/GUI/InstanceCheckMac.mm diff --git a/CMakeLists.txt b/CMakeLists.txt index 5e8726d08..48ad5033e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -160,6 +160,9 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Linux") # Boost on Raspberry-Pi does not link to pthreads. set(THREADS_PREFER_PTHREAD_FLAG ON) find_package(Threads REQUIRED) + + find_package(DBus REQUIRED) + include_directories(${DBUS_INCLUDE_DIRS}) endif() if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUXX) diff --git a/cmake/modules/FindDBus.cmake b/cmake/modules/FindDBus.cmake new file mode 100644 index 000000000..1d0f29dd7 --- /dev/null +++ b/cmake/modules/FindDBus.cmake @@ -0,0 +1,59 @@ +# - Try to find DBus +# Once done, this will define +# +# DBUS_FOUND - system has DBus +# DBUS_INCLUDE_DIRS - the DBus include directories +# DBUS_LIBRARIES - link these to use DBus +# +# Copyright (C) 2012 Raphael Kubo da Costa +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND ITS CONTRIBUTORS ``AS +# IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR ITS +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +FIND_PACKAGE(PkgConfig) +PKG_CHECK_MODULES(PC_DBUS QUIET dbus-1) + +FIND_LIBRARY(DBUS_LIBRARIES + NAMES dbus-1 + HINTS ${PC_DBUS_LIBDIR} + ${PC_DBUS_LIBRARY_DIRS} +) + +FIND_PATH(DBUS_INCLUDE_DIR + NAMES dbus/dbus.h + HINTS ${PC_DBUS_INCLUDEDIR} + ${PC_DBUS_INCLUDE_DIRS} +) + +GET_FILENAME_COMPONENT(_DBUS_LIBRARY_DIR ${DBUS_LIBRARIES} PATH) +FIND_PATH(DBUS_ARCH_INCLUDE_DIR + NAMES dbus/dbus-arch-deps.h + HINTS ${PC_DBUS_INCLUDEDIR} + ${PC_DBUS_INCLUDE_DIRS} + ${_DBUS_LIBRARY_DIR} + ${DBUS_INCLUDE_DIR} + PATH_SUFFIXES include +) + +SET(DBUS_INCLUDE_DIRS ${DBUS_INCLUDE_DIR} ${DBUS_ARCH_INCLUDE_DIR}) + +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(DBUS REQUIRED_VARS DBUS_INCLUDE_DIRS DBUS_LIBRARIES) \ No newline at end of file diff --git a/src/PrusaSlicer.cpp b/src/PrusaSlicer.cpp index 9efecb50c..dd4ee3b98 100644 --- a/src/PrusaSlicer.cpp +++ b/src/PrusaSlicer.cpp @@ -51,13 +51,14 @@ #include "slic3r/GUI/GUI.hpp" #include "slic3r/GUI/GUI_App.hpp" #include "slic3r/GUI/3DScene.hpp" + #include "slic3r/GUI/InstanceCheck.hpp" + #include "slic3r/GUI/AppConfig.hpp" #endif /* SLIC3R_GUI */ using namespace Slic3r; int CLI::run(int argc, char **argv) { - #ifdef __WXGTK__ // On Linux, wxGTK has no support for Wayland, and the app crashes on // startup if gtk3 is used. This env var has to be set explicitly to @@ -524,6 +525,16 @@ int CLI::run(int argc, char **argv) #ifdef SLIC3R_GUI // #ifdef USE_WX GUI::GUI_App *gui = new GUI::GUI_App(); + + bool gui_single_instance_setting = gui->app_config->get("single_instance") == "1"; + if (Slic3r::instance_check(argc, argv, gui_single_instance_setting)) { + //TODO: do we have delete gui and other stuff? + return -1; + } + + //gui->app_config = app_config; + //app_config = nullptr; + // gui->autosave = m_config.opt_string("autosave"); GUI::GUI_App::SetInstance(gui); gui->CallAfter([gui, this, &load_configs] { diff --git a/src/PrusaSlicer_app_msvc.cpp b/src/PrusaSlicer_app_msvc.cpp index b3d1e8bb4..712cff687 100644 --- a/src/PrusaSlicer_app_msvc.cpp +++ b/src/PrusaSlicer_app_msvc.cpp @@ -7,6 +7,8 @@ #include #include + + #ifdef SLIC3R_GUI extern "C" { @@ -216,7 +218,6 @@ int APIENTRY wWinMain(HINSTANCE /* hInstance */, HINSTANCE /* hPrevInstance */, int wmain(int argc, wchar_t **argv) { #endif - std::vector argv_extended; argv_extended.emplace_back(argv[0]); diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index a155dff01..43c83fe2b 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -39,6 +39,11 @@ void PrintConfigDef::init_common_params() { ConfigOptionDef* def; + def = this->add("single_instance", coBool); + def->label = L("Single Instance"); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + def = this->add("printer_technology", coEnum); def->label = L("Printer technology"); def->tooltip = L("Printer technology"); diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 2ac8a1ff0..f910c9602 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -161,6 +161,8 @@ set(SLIC3R_GUI_SOURCES GUI/DoubleSlider.hpp GUI/ObjectDataViewModel.cpp GUI/ObjectDataViewModel.hpp + GUI/InstanceCheck.cpp + GUI/InstanceCheck.hpp Utils/Http.cpp Utils/Http.hpp Utils/FixModelByWin10.cpp @@ -195,6 +197,8 @@ if (APPLE) GUI/RemovableDriveManagerMM.mm GUI/RemovableDriveManagerMM.h GUI/Mouse3DHandlerMac.mm + GUI/InstanceCheckMac.mm + GUI/InstanceCheckMac.h ) FIND_LIBRARY(DISKARBITRATION_LIBRARY DiskArbitration) @@ -206,6 +210,10 @@ encoding_check(libslic3r_gui) target_link_libraries(libslic3r_gui libslic3r avrdude cereal imgui GLEW::GLEW OpenGL::GL OpenGL::GLU hidapi libcurl ${wxWidgets_LIBRARIES}) +if (CMAKE_SYSTEM_NAME STREQUAL "Linux") + target_link_libraries(libslic3r_gui ${DBUS_LIBRARIES}) +endif() + if(APPLE) target_link_libraries(libslic3r_gui ${DISKARBITRATION_LIBRARY}) endif() diff --git a/src/slic3r/GUI/AppConfig.cpp b/src/slic3r/GUI/AppConfig.cpp index 616d95147..291949ce9 100644 --- a/src/slic3r/GUI/AppConfig.cpp +++ b/src/slic3r/GUI/AppConfig.cpp @@ -69,6 +69,9 @@ void AppConfig::set_defaults() set("use_retina_opengl", "1"); #endif + if (get("single_instance").empty()) + set("single_instance", "0"); + if (get("remember_output_path").empty()) set("remember_output_path", "1"); diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index aee71f16e..c04d7ca16 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -50,6 +50,7 @@ #include "UpdateDialogs.hpp" #include "Mouse3DController.hpp" #include "RemovableDriveManager.hpp" +#include "InstanceCheck.hpp" #ifdef __WXMSW__ #include @@ -209,6 +210,17 @@ static void register_win32_device_notification_event() } return false; }); + + wxWindow::MSWRegisterMessageHandler(WM_COPYDATA, [](wxWindow* win, WXUINT /* nMsg */, WXWPARAM wParam, WXLPARAM lParam) { + + COPYDATASTRUCT* copy_data_structure = { 0 }; + copy_data_structure = (COPYDATASTRUCT*)lParam; + if (copy_data_structure->dwData == 1) { + LPCWSTR arguments = (LPCWSTR)copy_data_structure->lpData; + Slic3r::GUI::wxGetApp().other_instance_message_handler()->handle_message(boost::nowide::narrow(arguments)); + } + return true; + }); } #endif // WIN32 @@ -253,7 +265,11 @@ GUI_App::GUI_App() , m_imgui(new ImGuiWrapper()) , m_wizard(nullptr) , m_removable_drive_manager(std::make_unique()) -{} + , m_other_instance_message_handler(std::make_unique()) +{ + //app config initializes early becasuse it is used in instance checking in PrusaSlicer.cpp + this->init_app_config(); +} GUI_App::~GUI_App() { @@ -284,6 +300,30 @@ bool GUI_App::init_opengl() } #endif // ENABLE_NON_STATIC_CANVAS_MANAGER +void GUI_App::init_app_config() +{ + // Profiles for the alpha are stored into the PrusaSlicer-alpha directory to not mix with the current release. + SetAppName(SLIC3R_APP_KEY); + //SetAppName(SLIC3R_APP_KEY "-beta"); + SetAppDisplayName(SLIC3R_APP_NAME); + + // Set the Slic3r data directory at the Slic3r XS module. + // Unix: ~/ .Slic3r + // Windows : "C:\Users\username\AppData\Roaming\Slic3r" or "C:\Documents and Settings\username\Application Data\Slic3r" + // Mac : "~/Library/Application Support/Slic3r" + + if (data_dir().empty()) + set_data_dir(wxStandardPaths::Get().GetUserDataDir().ToUTF8().data()); + + if (!app_config) + app_config = new AppConfig(); + + // load settings + app_conf_exists = app_config->exists(); + if (app_conf_exists) { + app_config->load(); + } +} bool GUI_App::OnInit() { try { @@ -301,34 +341,14 @@ bool GUI_App::on_init_inner() wxCHECK_MSG(wxDirExists(resources_dir), false, wxString::Format("Resources path does not exist or is not a directory: %s", resources_dir)); - // Profiles for the alpha are stored into the PrusaSlicer-alpha directory to not mix with the current release. - SetAppName(SLIC3R_APP_KEY); -// SetAppName(SLIC3R_APP_KEY "-beta"); - SetAppDisplayName(SLIC3R_APP_NAME); - -// Enable this to get the default Win32 COMCTRL32 behavior of static boxes. + // Enable this to get the default Win32 COMCTRL32 behavior of static boxes. // wxSystemOptions::SetOption("msw.staticbox.optimized-paint", 0); -// Enable this to disable Windows Vista themes for all wxNotebooks. The themes seem to lead to terrible -// performance when working on high resolution multi-display setups. + // Enable this to disable Windows Vista themes for all wxNotebooks. The themes seem to lead to terrible + // performance when working on high resolution multi-display setups. // wxSystemOptions::SetOption("msw.notebook.themed-background", 0); // Slic3r::debugf "wxWidgets version %s, Wx version %s\n", wxVERSION_STRING, wxVERSION; - - // Set the Slic3r data directory at the Slic3r XS module. - // Unix: ~/ .Slic3r - // Windows : "C:\Users\username\AppData\Roaming\Slic3r" or "C:\Documents and Settings\username\Application Data\Slic3r" - // Mac : "~/Library/Application Support/Slic3r" - if (data_dir().empty()) - set_data_dir(wxStandardPaths::Get().GetUserDataDir().ToUTF8().data()); - - app_config = new AppConfig(); - - // load settings - app_conf_exists = app_config->exists(); - if (app_conf_exists) { - app_config->load(); - } - + std::string msg = Http::tls_global_init(); wxRichMessageDialog dlg(nullptr, @@ -407,6 +427,8 @@ bool GUI_App::on_init_inner() if (! plater_) return; + //m_other_instance_message_handler->report(); + if (app_config->dirty() && app_config->get("autosave") == "1") app_config->save(); diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index bf75eaaa6..b2fcef48b 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -35,6 +35,7 @@ class PrintHostJobQueue; namespace GUI{ class RemovableDriveManager; +class OtherInstanceMessageHandler; enum FileType { FT_STL, @@ -108,7 +109,7 @@ class GUI_App : public wxApp std::unique_ptr m_imgui; std::unique_ptr m_printhost_job_queue; ConfigWizard* m_wizard; // Managed by wxWindow tree - + std::unique_ptr m_other_instance_message_handler; public: bool OnInit() override; bool initialized() const { return m_initialized; } @@ -196,6 +197,7 @@ public: std::vector tabs_list; RemovableDriveManager* removable_drive_manager() { return m_removable_drive_manager.get(); } + OtherInstanceMessageHandler* other_instance_message_handler() { return m_other_instance_message_handler.get(); } ImGuiWrapper* imgui() { return m_imgui.get(); } @@ -211,6 +213,7 @@ public: private: bool on_init_inner(); + void init_app_config(); void window_pos_save(wxTopLevelWindow* window, const std::string &name); void window_pos_restore(wxTopLevelWindow* window, const std::string &name, bool default_maximized = false); void window_pos_sanitize(wxTopLevelWindow* window); diff --git a/src/slic3r/GUI/InstanceCheck.cpp b/src/slic3r/GUI/InstanceCheck.cpp new file mode 100644 index 000000000..e408ce118 --- /dev/null +++ b/src/slic3r/GUI/InstanceCheck.cpp @@ -0,0 +1,495 @@ +#include "GUI_App.hpp" +#include "InstanceCheck.hpp" + +#include "boost/nowide/convert.hpp" +#include +#include + +#include +#include + +#if __linux__ +#include /* Pull in all of D-Bus headers. */ +#endif //__linux__ + +namespace Slic3r { +namespace instance_check_internal +{ + struct CommandLineAnalysis + { + bool should_send; + std::string cl_string; + }; + static CommandLineAnalysis process_command_line(int argc, char** argv) //d:\3dmodels\Klapka\Klapka.3mf + { + CommandLineAnalysis ret { false }; + if (argc < 2) + return ret; + ret.cl_string = argv[0]; + for (size_t i = 1; i < argc; i++) { + std::string token = argv[i]; + if (token == "--single-instance") { + ret.should_send = true; + } else { + ret.cl_string += " "; + ret.cl_string += token; + } + } + BOOST_LOG_TRIVIAL(debug) << "single instance: "<< ret.should_send << ". other params: " << ret.cl_string; + return ret; + } +} //namespace instance_check_internal + +#if _WIN32 + +namespace instance_check_internal +{ + static HWND l_prusa_slicer_hwnd; + static BOOL CALLBACK EnumWindowsProc(_In_ HWND hwnd, _In_ LPARAM lParam) + { + //checks for other instances of prusaslicer, if found brings it to front and return false to stop enumeration and quit this instance + //search is done by classname(wxWindowNR is wxwidgets thing, so probably not unique) and name in window upper panel + //other option would be do a mutex and check for its existence + TCHAR wndText[1000]; + TCHAR className[1000]; + GetClassName(hwnd, className, 1000); + GetWindowText(hwnd, wndText, 1000); + std::wstring classNameString(className); + std::wstring wndTextString(wndText); + if (wndTextString.find(L"PrusaSlicer") != std::wstring::npos && classNameString == L"wxWindowNR") { + l_prusa_slicer_hwnd = hwnd; + ShowWindow(hwnd, SW_SHOWMAXIMIZED); + SetForegroundWindow(hwnd); + return false; + } + return true; + } + static void send_message(const HWND hwnd) + { + LPWSTR command_line_args = GetCommandLine(); + //Create a COPYDATASTRUCT to send the information + //cbData represents the size of the information we want to send. + //lpData represents the information we want to send. + //dwData is an ID defined by us(this is a type of ID different than WM_COPYDATA). + COPYDATASTRUCT data_to_send = { 0 }; + data_to_send.dwData = 1; + data_to_send.cbData = sizeof(TCHAR) * (wcslen(command_line_args) + 1); + data_to_send.lpData = command_line_args; + + SendMessage(hwnd, WM_COPYDATA, 0, (LPARAM)&data_to_send); + } +} //namespace instance_check_internal + +bool instance_check(int argc, char** argv, bool app_config_single_instance) +{ + instance_check_internal::CommandLineAnalysis cla = instance_check_internal::process_command_line(argc, argv); + if (cla.should_send || app_config_single_instance) { + // Call EnumWidnows with own callback. cons: Based on text in the name of the window and class name which is generic. + if (!EnumWindows(instance_check_internal::EnumWindowsProc, 0)) { + BOOST_LOG_TRIVIAL(info) << "instance check: Another instance found. This instance will terminate."; + instance_check_internal::send_message(instance_check_internal::l_prusa_slicer_hwnd); + return true; + } + } + BOOST_LOG_TRIVIAL(info) << "instance check: Another instance not found or single-instance not set."; + return false; +} + +#elif defined(__APPLE__) + +namespace instance_check_internal +{ + static int get_lock() + { + struct flock fl; + int fdlock; + fl.l_type = F_WRLCK; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + fl.l_len = 1; + + if ((fdlock = open("/tmp/prusaslicer.lock", O_WRONLY | O_CREAT, 0666)) == -1) + return 0; + + if (fcntl(fdlock, F_SETLK, &fl) == -1) + return 0; + + return 1; + } +} //namespace instance_check_internal + +bool instance_check(int argc, char** argv, bool app_config_single_instance) +{ + instance_check_internal::CommandLineAnalysis cla = instance_check_internal::process_command_line(argc, argv); + if (!instance_check_internal::get_lock() && (cla.should_send || app_config_single_instance)) { + BOOST_LOG_TRIVIAL(info) << "instance check: Another instance found. This instance will terminate."; + send_message_mac(cla.cl_string); + return true; + } + BOOST_LOG_TRIVIAL(info) << "instance check: Another instance not found or single-instance not set."; + return false; +} + +#elif defined(__linux__) + +namespace instance_check_internal +{ + static int get_lock() + { + struct flock fl; + int fdlock; + fl.l_type = F_WRLCK; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + fl.l_len = 1; + + if ((fdlock = open("/tmp/prusaslicer.lock", O_WRONLY | O_CREAT, 0666)) == -1) + return 0; + + if (fcntl(fdlock, F_SETLK, &fl) == -1) + return 0; + + return 1; + } + + static void send_message(std::string message_text) + { + DBusMessage* msg; + DBusMessageIter args; + DBusConnection* conn; + DBusError err; + dbus_uint32_t serial = 0; + const char* sigval = message_text.c_str(); + std::string interface_name = "com.prusa3d.prusaslicer.InstanceCheck"; + std::string method_name = "AnotherInstace"; + std::string object_name = "/com/prusa3d/prusaslicer/InstanceCheck"; + + + // initialise the error value + dbus_error_init(&err); + + // connect to bus, and check for errors (use SESSION bus everywhere!) + conn = dbus_bus_get(DBUS_BUS_SESSION, &err); + if (dbus_error_is_set(&err)) { + BOOST_LOG_TRIVIAL(error) << "DBus Connection Error. Message to another instance wont be send."; + BOOST_LOG_TRIVIAL(error) << "DBus Connection Error: "<< err.message; + dbus_error_free(&err); + return; + } + if (NULL == conn) { + BOOST_LOG_TRIVIAL(error) << "DBus Connection is NULL. Message to another instance wont be send."; + return; + } + + //some sources do request interface ownership before constructing msg but i think its wrong. + + //create new method call message + msg = dbus_message_new_method_call(interface_name.c_str(), object_name.c_str(), interface_name.c_str(), method_name.c_str()); + if (NULL == msg) { + BOOST_LOG_TRIVIAL(error) << "DBus Message is NULL. Message to another instance wont be send."; + dbus_connection_unref(conn); + return; + } + //the AnotherInstace method is not sending reply. + dbus_message_set_no_reply(msg, TRUE); + + //append arguments to message + if (!dbus_message_append_args(msg, DBUS_TYPE_STRING, &sigval, DBUS_TYPE_INVALID)) { + BOOST_LOG_TRIVIAL(error) << "Ran out of memory while constructing args for DBus message. Message to another instance wont be send."; + dbus_message_unref(msg); + dbus_connection_unref(conn); + return; + } + + // send the message and flush the connection + if (!dbus_connection_send(conn, msg, &serial)) { + BOOST_LOG_TRIVIAL(error) << "Ran out of memory while sending DBus message."; + dbus_message_unref(msg); + dbus_connection_unref(conn); + return; + } + dbus_connection_flush(conn); + + BOOST_LOG_TRIVIAL(trace) << "DBus message sent."; + + // free the message and close the connection + dbus_message_unref(msg); + dbus_connection_unref(conn); + } +} //namespace instance_check_internal + +bool instance_check(int argc, char** argv, bool app_config_single_instance) +{ + instance_check_internal::CommandLineAnalysis cla = instance_check_internal::process_command_line(argc, argv); + if (!instance_check_internal::get_lock() && (cla.should_send || app_config_single_instance)) { + BOOST_LOG_TRIVIAL(info) << "instance check: Another instance found. This instance will terminate."; + instance_check_internal::send_message(cla.cl_string); + return true; + } + BOOST_LOG_TRIVIAL(info) << "instance check: Another instance not found or single-instance not set."; + return false; +} +#endif //_WIN32/__APPLE__/__linux__ + + + +namespace GUI { + +wxDEFINE_EVENT(EVT_LOAD_MODEL_OTHER_INSTANCE, LoadFromOtherInstanceEvent); +wxDEFINE_EVENT(EVT_INSTANCE_GO_TO_FRONT, InstanceGoToFrontEvent); + +void OtherInstanceMessageHandler::init(wxEvtHandler* callback_evt_handler) +{ + assert(!m_initialized); + assert(m_callback_evt_handler == nullptr); + if (m_initialized) + return; + + m_initialized = true; + m_callback_evt_handler = callback_evt_handler; + +#if _WIN32 + //create_listener_window(); +#endif //_WIN32 + +#if defined(__APPLE__) + this->register_for_messages(); +#endif //__APPLE__ + +#ifdef BACKGROUND_MESSAGE_LISTENER + m_thread = boost::thread((boost::bind(&OtherInstanceMessageHandler::listen, this))); +#endif //BACKGROUND_MESSAGE_LISTENER +} +void OtherInstanceMessageHandler::shutdown() +{ + BOOST_LOG_TRIVIAL(debug) << "message handler shutdown()."; + assert(m_initialized); + if (m_initialized) { +#if __APPLE__ + //delete macos implementation + this->unregister_for_messages(); +#endif //__APPLE__ +#ifdef BACKGROUND_MESSAGE_LISTENER + if (m_thread.joinable()) { + // Stop the worker thread, if running. + { + // Notify the worker thread to cancel wait on detection polling. + std::lock_guard lck(m_thread_stop_mutex); + m_stop = true; + } + m_thread_stop_condition.notify_all(); + // Wait for the worker thread to stop. + m_thread.join(); + m_stop = false; + } +#endif //BACKGROUND_MESSAGE_LISTENER + m_initialized = false; + } +} + +namespace MessageHandlerInternal +{ + // returns ::path to possible model or empty ::path if input string is not existing path + static boost::filesystem::path get_path(const std::string possible_path) + { + BOOST_LOG_TRIVIAL(debug) << "message part: " << possible_path; + + if (possible_path.empty() || possible_path.size() < 3) { + BOOST_LOG_TRIVIAL(debug) << "empty"; + return boost::filesystem::path(); + } + if (boost::filesystem::exists(possible_path)) { + BOOST_LOG_TRIVIAL(debug) << "is path"; + return boost::filesystem::path(possible_path); + } else if (possible_path[0] == '\"') { + if(boost::filesystem::exists(possible_path.substr(1, possible_path.size() - 2))) { + BOOST_LOG_TRIVIAL(debug) << "is path in quotes"; + return boost::filesystem::path(possible_path.substr(1, possible_path.size() - 2)); + } + } + BOOST_LOG_TRIVIAL(debug) << "is NOT path"; + return boost::filesystem::path(); + } +} //namespace MessageHandlerInternal + +void OtherInstanceMessageHandler::handle_message(const std::string message) { + std::vector paths; + auto next_space = message.find(' '); + size_t last_space = 0; + int counter = 0; + + BOOST_LOG_TRIVIAL(info) << "message from other instance: " << message; + + while (next_space != std::string::npos) + { + if (counter != 0) { + const std::string possible_path = message.substr(last_space, next_space - last_space); + boost::filesystem::path p = MessageHandlerInternal::get_path(possible_path); + if(!p.string().empty()) + paths.emplace_back(p); + } + last_space = next_space; + next_space = message.find(' ', last_space + 1); + counter++; + } + if (counter != 0 ) { + boost::filesystem::path p = MessageHandlerInternal::get_path(message.substr(last_space + 1)); + if (!p.string().empty()) + paths.emplace_back(p); + } + if (!paths.empty()) { + //wxEvtHandler* evt_handler = wxGetApp().plater(); //assert here? + //if (evt_handler) { + wxPostEvent(m_callback_evt_handler, LoadFromOtherInstanceEvent(GUI::EVT_LOAD_MODEL_OTHER_INSTANCE, std::vector(std::move(paths)))); + //} + } +} + +#ifdef BACKGROUND_MESSAGE_LISTENER + +namespace MessageHandlerDBusInternal +{ + //reply to introspect makes our DBus object visible for other programs like D-Feet + static void respond_to_introspect(DBusConnection *connection, DBusMessage *request) + { + DBusMessage *reply; + const char *introspection_data = + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " "; + + reply = dbus_message_new_method_return(request); + dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection_data, DBUS_TYPE_INVALID); + dbus_connection_send(connection, reply, NULL); + dbus_message_unref(reply); + } + //method AnotherInstance receives message from another PrusaSlicer instance + static void handle_method_another_instance(DBusConnection *connection, DBusMessage *request) + { + DBusError err; + char* text= ""; + wxEvtHandler* evt_handler; + + dbus_error_init(&err); + dbus_message_get_args(request, &err, DBUS_TYPE_STRING, &text, DBUS_TYPE_INVALID); + if (dbus_error_is_set(&err)) { + BOOST_LOG_TRIVIAL(trace) << "Dbus method AnotherInstance received with wrong arguments."; + dbus_error_free(&err); + return; + } + wxGetApp().other_instance_message_handler()->handle_message(text); + + evt_handler = wxGetApp().plater(); + if (evt_handler) { + wxPostEvent(evt_handler, InstanceGoToFrontEvent(EVT_INSTANCE_GO_TO_FRONT)); + } + } + //every dbus message received comes here + static DBusHandlerResult handle_dbus_object_message(DBusConnection *connection, DBusMessage *message, void *user_data) + { + const char* interface_name = dbus_message_get_interface(message); + const char* member_name = dbus_message_get_member(message); + + BOOST_LOG_TRIVIAL(trace) << "DBus message received: interface: " << interface_name << ", member: " << member_name; + + if (0 == strcmp("org.freedesktop.DBus.Introspectable", interface_name) && 0 == strcmp("Introspect", member_name)) { + respond_to_introspect(connection, message); + return DBUS_HANDLER_RESULT_HANDLED; + } else if (0 == strcmp("com.prusa3d.prusaslicer.InstanceCheck", interface_name) && 0 == strcmp("AnotherInstace", member_name)) { + handle_method_another_instance(connection, message); + return DBUS_HANDLER_RESULT_HANDLED; + } + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } +} //namespace MessageHandlerDBusInternal + +void OtherInstanceMessageHandler::listen() +{ + DBusConnection* conn; + DBusError err; + int name_req_val; + DBusObjectPathVTable vtable; + std::string interface_name = "com.prusa3d.prusaslicer.InstanceCheck"; + std::string object_name = "/com/prusa3d/prusaslicer/InstanceCheck"; + + dbus_error_init(&err); + + // connect to the bus and check for errors (use SESSION bus everywhere!) + conn = dbus_bus_get(DBUS_BUS_SESSION, &err); + if (dbus_error_is_set(&err)) { + BOOST_LOG_TRIVIAL(error) << "DBus Connection Error: "<< err.message; + BOOST_LOG_TRIVIAL(error) << "Dbus Messages listening terminating."; + dbus_error_free(&err); + return; + } + if (NULL == conn) { + BOOST_LOG_TRIVIAL(error) << "DBus Connection is NULL. Dbus Messages listening terminating."; + return; + } + + // request our name on the bus and check for errors + name_req_val = dbus_bus_request_name(conn, interface_name.c_str(), DBUS_NAME_FLAG_REPLACE_EXISTING , &err); + if (dbus_error_is_set(&err)) { + BOOST_LOG_TRIVIAL(error) << "DBus Request name Error: "<< err.message; + BOOST_LOG_TRIVIAL(error) << "Dbus Messages listening terminating."; + dbus_error_free(&err); + dbus_connection_unref(conn); + return; + } + if (DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER != name_req_val) { + BOOST_LOG_TRIVIAL(error) << "Not primary owner of DBus name - probably another PrusaSlicer instance is running."; + BOOST_LOG_TRIVIAL(error) << "Dbus Messages listening terminating."; + dbus_connection_unref(conn); + return; + } + + // Set callbacks. Unregister function should not be nessary. + vtable.message_function = MessageHandlerDBusInternal::handle_dbus_object_message; + vtable.unregister_function = NULL; + + // register new object - this is our access to DBus + dbus_connection_try_register_object_path(conn, object_name.c_str(), &vtable, NULL, &err); + if ( dbus_error_is_set(&err) ) { + BOOST_LOG_TRIVIAL(error) << "DBus Register object Error: "<< err.message; + BOOST_LOG_TRIVIAL(error) << "Dbus Messages listening terminating."; + dbus_connection_unref(conn); + dbus_error_free(&err); + return; + } + + BOOST_LOG_TRIVIAL(trace) << "Dbus object registered. Starting listening for messages."; + + for (;;) { + // Wait for 1 second + // Cancellable. + { + std::unique_lock lck(m_thread_stop_mutex); + m_thread_stop_condition.wait_for(lck, std::chrono::seconds(1), [this] { return m_stop; }); + } + if (m_stop) + // Stop the worker thread. + + break; + //dispatch should do all the work with incoming messages + //second parameter is blocking time that funciton waits for new messages + //that is handled here with our own event loop above + dbus_connection_read_write_dispatch(conn, 0); + } + + dbus_connection_unref(conn); +} +#endif //BACKGROUND_MESSAGE_LISTENER +} // namespace GUI +} // namespace Slic3r diff --git a/src/slic3r/GUI/InstanceCheck.hpp b/src/slic3r/GUI/InstanceCheck.hpp new file mode 100644 index 000000000..4207296a0 --- /dev/null +++ b/src/slic3r/GUI/InstanceCheck.hpp @@ -0,0 +1,91 @@ +#ifndef slic3r_InstanceCheck_hpp_ +#define slic3r_InstanceCheck_hpp_ + +#include "Event.hpp" + +#if _WIN32 +#include +#endif //_WIN32 + +#include + +#include + +#include +#include +#include + + + +namespace Slic3r { +// checks for other running instances and sends them argv, +// if there is --single-instance argument or AppConfig is set to single_instance=1 +// returns true if this instance should terminate +bool instance_check(int argc, char** argv, bool app_config_single_instance); + +#if __APPLE__ +// apple implementation of inner functions of instance_check +// in InstanceCheckMac.mm +void send_message_mac(const std::string msg); +#endif //__APPLE__ + +namespace GUI { + +#if __linux__ + #define BACKGROUND_MESSAGE_LISTENER +#endif // __linux__ + +using LoadFromOtherInstanceEvent = Event>; +wxDECLARE_EVENT(EVT_LOAD_MODEL_OTHER_INSTANCE, LoadFromOtherInstanceEvent); + +using InstanceGoToFrontEvent = SimpleEvent; +wxDECLARE_EVENT(EVT_INSTANCE_GO_TO_FRONT, InstanceGoToFrontEvent); + +class OtherInstanceMessageHandler +{ +public: + OtherInstanceMessageHandler() = default; + OtherInstanceMessageHandler(OtherInstanceMessageHandler const&) = delete; + void operator=(OtherInstanceMessageHandler const&) = delete; + ~OtherInstanceMessageHandler() { assert(!m_initialized); } + + // inits listening, on each platform different. On linux starts background thread + void init(wxEvtHandler* callback_evt_handler); + // stops listening, on linux stops the background thread + void shutdown(); + + //finds paths to models in message(= command line arguments, first should be prusaSlicer executable) + //and sends them to plater via LoadFromOtherInstanceEvent + //security of messages: from message all existing paths are proccesed to load model + // win32 - anybody who has hwnd can send message. + // mac - anybody who posts notification with name:@"OtherPrusaSlicerTerminating" + // linux - instrospectable on dbus + void handle_message(const std::string message); +private: + bool m_initialized { false }; + wxEvtHandler* m_callback_evt_handler { nullptr }; + +#ifdef BACKGROUND_MESSAGE_LISTENER + //worker thread to listen incoming dbus communication + boost::thread m_thread; + std::condition_variable m_thread_stop_condition; + mutable std::mutex m_thread_stop_mutex; + bool m_stop{ false }; + bool m_start{ true }; + + // background thread method + void listen(); +#endif //BACKGROUND_MESSAGE_LISTENER + +#if __APPLE__ + //implemented at InstanceCheckMac.mm + void register_for_messages(); + void unregister_for_messages(); + // Opaque pointer to RemovableDriveManagerMM + void* m_impl_osx; +#endif //__APPLE__ + +}; +} // namespace GUI +} // namespace Slic3r +#endif // slic3r_InstanceCheck_hpp_ diff --git a/src/slic3r/GUI/InstanceCheckMac.h b/src/slic3r/GUI/InstanceCheckMac.h new file mode 100644 index 000000000..9931bb679 --- /dev/null +++ b/src/slic3r/GUI/InstanceCheckMac.h @@ -0,0 +1,8 @@ +#import + +@interface OtherInstanceMessageHandlerMac : NSObject + +-(instancetype) init; +-(void) add_observer; +-(void) message_update:(NSNotification *)note; +@end diff --git a/src/slic3r/GUI/InstanceCheckMac.mm b/src/slic3r/GUI/InstanceCheckMac.mm new file mode 100644 index 000000000..cbc833c79 --- /dev/null +++ b/src/slic3r/GUI/InstanceCheckMac.mm @@ -0,0 +1,68 @@ +#import "InstanceCheck.hpp" +#import "InstanceCheckMac.h" +#import "GUI_App.hpp" + +@implementation OtherInstanceMessageHandlerMac + +-(instancetype) init +{ + self = [super init]; + return self; +} +-(void)add_observer +{ + NSLog(@"adding observer"); + [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(message_update:) name:@"OtherPrusaSlicerInstanceMessage" object:nil suspensionBehavior:NSNotificationSuspensionBehaviorDeliverImmediately]; +} + +-(void)message_update:(NSNotification *)msg +{ + //NSLog(@"recieved msg %@", msg); + //demiaturize all windows + for(NSWindow* win in [NSApp windows]) + { + if([win isMiniaturized]) + { + [win deminiaturize:self]; + } + } + //bring window to front + [[NSApplication sharedApplication] activateIgnoringOtherApps : YES]; + //pass message + Slic3r::GUI::wxGetApp().other_instance_message_handler()->handle_message(std::string([msg.userInfo[@"data"] UTF8String])); +} + +@end + +namespace Slic3r { + +void send_message_mac(const std::string msg) +{ + NSString *nsmsg = [NSString stringWithCString:msg.c_str() encoding:[NSString defaultCStringEncoding]]; + //NSLog(@"sending msg %@", nsmsg); + [[NSDistributedNotificationCenter defaultCenter] postNotificationName:@"OtherPrusaSlicerInstanceMessage" object:nil userInfo:[NSDictionary dictionaryWithObject:nsmsg forKey:@"data"] deliverImmediately:YES]; +} + +namespace GUI { +void OtherInstanceMessageHandler::register_for_messages() +{ + m_impl_osx = [[OtherInstanceMessageHandlerMac alloc] init]; + if(m_impl_osx) { + [m_impl_osx add_observer]; + } +} + +void OtherInstanceMessageHandler::unregister_for_messages() +{ + //NSLog(@"unreegistering other instance messages"); + if (m_impl_osx) { + [m_impl_osx release]; + m_impl_osx = nullptr; + } else { + NSLog(@"unreegister not required"); + } +} +}//namespace GUI +}//namespace Slicer + + diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index f7d7a6cac..21aad8987 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -26,6 +26,7 @@ #include "GUI_ObjectList.hpp" #include "Mouse3DController.hpp" #include "RemovableDriveManager.hpp" +#include "InstanceCheck.hpp" #include "I18N.hpp" #include @@ -234,7 +235,8 @@ void MainFrame::shutdown() // Stop the background thread of the removable drive manager, so that no new updates will be sent to the Plater. wxGetApp().removable_drive_manager()->shutdown(); - + //stop listening for messages from other instances + wxGetApp().other_instance_message_handler()->shutdown(); // Save the slic3r.ini.Usually the ini file is saved from "on idle" callback, // but in rare cases it may not have been called yet. wxGetApp().app_config->save(); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index db9e7e59a..d909442b9 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -74,6 +74,8 @@ #include "../Utils/FixModelByWin10.hpp" #include "../Utils/UndoRedo.hpp" #include "RemovableDriveManager.hpp" +#include "InstanceCheck.hpp" + #if ENABLE_NON_STATIC_CANVAS_MANAGER #ifdef __APPLE__ #include "Gizmos/GLGizmosManager.hpp" @@ -1946,6 +1948,28 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) // Initialize the Undo / Redo stack with a first snapshot. this->take_snapshot(_L("New Project")); + + this->q->Bind(EVT_LOAD_MODEL_OTHER_INSTANCE, [this](LoadFromOtherInstanceEvent &evt) { + BOOST_LOG_TRIVIAL(debug) << "received load from other instance event "; + this->load_files(evt.data, true, true); + }); + this->q->Bind(EVT_INSTANCE_GO_TO_FRONT, [this](InstanceGoToFrontEvent &) { + BOOST_LOG_TRIVIAL(debug) << "prusaslicer window going forward"; + //this code maximize app window on Fedora + wxGetApp().mainframe->Iconize(false); + if (wxGetApp().mainframe->IsMaximized()) + wxGetApp().mainframe->Maximize(true); + else + wxGetApp().mainframe->Maximize(false); + //this code (without code above) maximize window on Ubuntu + wxGetApp().mainframe->Restore(); + wxGetApp().GetTopWindow()->SetFocus(); // focus on my window + wxGetApp().GetTopWindow()->Raise(); // bring window to front + wxGetApp().GetTopWindow()->Show(true); // show the window + + }); + wxGetApp().other_instance_message_handler()->init(this->q); + } Plater::priv::~priv() diff --git a/src/slic3r/GUI/Preferences.cpp b/src/slic3r/GUI/Preferences.cpp index 1299d9a48..e2bccb1c7 100644 --- a/src/slic3r/GUI/Preferences.cpp +++ b/src/slic3r/GUI/Preferences.cpp @@ -100,6 +100,13 @@ void PreferencesDialog::build() option = Option (def,"show_incompatible_presets"); m_optgroup_general->append_single_option_line(option); + def.label = L("Single Instance"); + def.type = coBool; + def.tooltip = L("If this is enabled, when staring PrusaSlicer and another instance is running, that instance will be reactivated instead."); + def.set_default_value(new ConfigOptionBool{ app_config->has("single_instance") ? app_config->get("single_instance") == "1" : false }); + option = Option(def, "single_instance"); + m_optgroup_general->append_single_option_line(option); + #if __APPLE__ def.label = L("Use Retina resolution for the 3D scene"); def.type = coBool; @@ -177,6 +184,8 @@ void PreferencesDialog::accept() app_config->set(it->first, it->second); } + app_config->save(); + EndModal(wxID_OK); // Nothify the UI to update itself from the ini file. From b91c3d26e332ab828d269e6d2ea1e9d44f3d1e4a Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 29 Apr 2020 12:32:00 +0200 Subject: [PATCH 16/16] Fix build on OSX with new wxWidgets in deps --- src/CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 281f9e663..e170ea8d3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -77,6 +77,11 @@ if (SLIC3R_GUI) list(APPEND wxWidgets_LIBRARIES ${LIBRT}) endif() + # This fixes a OpenGL linking issue on OSX. wxWidgets cmake build includes + # wrong libs for opengl in the link line and it does not link to it by himself. + # libslic3r_gui will link to opengl anyway, so lets override wx + list(FILTER wxWidgets_LIBRARIES EXCLUDE REGEX OpenGL) + # list(REMOVE_ITEM wxWidgets_LIBRARIES oleacc) message(STATUS "wx libs: ${wxWidgets_LIBRARIES}")