Example #1
0
Polygons
ExtrusionLoop::grow() const
{
    if (this->paths.empty()) return Polygons();
    
    // collect all the path widths
    std::vector<float> widths;
    for (ExtrusionPaths::const_iterator path = this->paths.begin(); path != this->paths.end(); ++path)
        widths.push_back(path->width);
    
    // grow this polygon with the minimum common width
    // (this ensures vertices are grown correctly, which doesn't happen if we just
    // union the paths grown individually)
    const float min_width = *std::min_element(widths.begin(), widths.end());
    const Polygon p = this->polygon();
    Polygons pp = diff(
        offset(p, +scale_(min_width/2)),
        offset(p, -scale_(min_width/2))
    );
    
    // if we have thicker segments, grow them
    if (min_width != *std::max_element(widths.begin(), widths.end())) {
        for (ExtrusionPaths::const_iterator path = this->paths.begin(); path != this->paths.end(); ++path)
            append_to(pp, path->grow());
    }
    
    return union_(pp);
}
Example #2
0
void edit_depot_dialog()
{
    Depot *the_depot;
    print_all_depots();
    prt(TYPE_DEPOT_NAME_TO_EDIT);
    scan_to_buf();
    if (is_zero()) {
        prt(CLS);
        return;
    }
    prt(CLS);
    the_depot = find_object_with_item_in(&depots, buffer, get_depot_name, names_cmp);
    if (!the_depot)
        return;

    prt(CURRENT);
    printf("%s\n", the_depot->name);
    prt(NEW);
    scan_to_buf();
    prt(CLS);
    /* if edited successfully - find new place for new name (sort) */
    if (edit_depot_name(the_depot, buffer)) {
        remove_from(&depots, the_depot, del_node_only);
        append_to(&depots, the_depot, depots_names_cmp);
        reappend_depot_assignments(the_depot);
    }
    modified = 1;
}
Example #3
0
inline Polygons
to_polygons(const SurfacesConstPtr &surfaces)
{
    Slic3r::Polygons pp;
    for (SurfacesConstPtr::const_iterator s = surfaces.begin(); s != surfaces.end(); ++s)
        append_to(pp, (Polygons)**s);
    return pp;
}
Example #4
0
int add_depot(char* depot_name)
{
    Depot *the_depot;
    the_depot = new_depot(depot_name);

    /* if setting up new depot failed */
    if (!the_depot)
        return 0;

    if(!append_to(&depots, the_depot, depots_names_cmp))
        free(the_depot);
    return 1;
}
Example #5
0
int add_bus(char* side_no, char* line_no, char* name, char* pesel)
{
    Bus *the_bus;
    the_bus = new_bus(side_no, line_no, name, pesel);

    /* if setting up new bus failed */
    if (!the_bus)
        return 0;

    if(!append_to(&buses, the_bus, buses_side_no_cmp))
        free(the_bus);
    return 1;
}
Example #6
0
static int
combine(const struct rom_s *roms, int nmembers, const char *outfile)
{
	int i;
	FILE *fout;
	FILE *fin;
	long cur_offset = 0;
	int ret;

	fout = fopen(outfile, "w");
	if (fout == NULL) {
		fprintf(stderr, "Could not open <%s> for writing: %s\n",
			outfile, strerror(errno));
		return EXIT_FAILURE;
	}

	for (i = 0; i < nmembers; i++) {
		assert(roms[i].rom_file != NULL);
		assert(cur_offset <= roms[i].offset);
#if 0 /* debug output */
		fprintf(stderr, "current offset=%lx; roms[i].offset=%lx\n", 
			cur_offset, roms[i].offset);
#endif
		ret = fseek(fout, roms[i].offset, SEEK_SET);
		assert(0 <= ret);

		fin = fopen(roms[i].rom_file, "r");
		if (fin == NULL) {
			fprintf(stderr, "Could not open <%s> for reading: "
				"%s\n", roms[i].rom_file, strerror(errno));
			fclose(fout);
			return EXIT_FAILURE;
		}

		append_to(fout, fin, &cur_offset);
		ret = fclose(fin);
		assert(ret == 0);
	}

	ret = fclose(fout);
	assert(ret == 0);

	return EXIT_SUCCESS;
}
Example #7
0
 void file_loader:: append_to( file_content &content, const char   *filename)
 {
     const string fn(filename);
     append_to(content,fn);
 }
Example #8
0
void
LayerRegion::process_external_surfaces(const Layer* lower_layer)
{
    const Surfaces &surfaces = this->fill_surfaces.surfaces;
    const double margin = scale_(EXTERNAL_INFILL_MARGIN);
    
    SurfaceCollection bottom;
    for (Surfaces::const_iterator surface = surfaces.begin(); surface != surfaces.end(); ++surface) {
        if (!surface->is_bottom()) continue;
        
        ExPolygons grown = offset_ex(surface->expolygon, +margin);
        
        /*  detect bridge direction before merging grown surfaces otherwise adjacent bridges
            would get merged into a single one while they need different directions
            also, supply the original expolygon instead of the grown one, because in case
            of very thin (but still working) anchors, the grown expolygon would go beyond them */
        double angle = -1;
        if (lower_layer != NULL) {
            BridgeDetector bd(
                surface->expolygon,
                lower_layer->slices,
                this->flow(frInfill, this->layer()->height, true).scaled_width()
            );
            
            #ifdef SLIC3R_DEBUG
            printf("Processing bridge at layer %zu:\n", this->layer()->id());
            #endif
            
            if (bd.detect_angle()) {
                angle = bd.angle;
            
                if (this->layer()->object()->config.support_material) {
                    Polygons coverage = bd.coverage();
                    this->bridged.insert(this->bridged.end(), coverage.begin(), coverage.end());
                    this->unsupported_bridge_edges.append(bd.unsupported_edges()); 
                }
            }
        }
        
        for (ExPolygons::const_iterator it = grown.begin(); it != grown.end(); ++it) {
            Surface s       = *surface;
            s.expolygon     = *it;
            s.bridge_angle  = angle;
            bottom.surfaces.push_back(s);
        }
    }
    
    SurfaceCollection top;
    for (Surfaces::const_iterator surface = surfaces.begin(); surface != surfaces.end(); ++surface) {
        if (surface->surface_type != stTop) continue;
        
        // give priority to bottom surfaces
        ExPolygons grown = diff_ex(
            offset(surface->expolygon, +margin),
            (Polygons)bottom
        );
        for (ExPolygons::const_iterator it = grown.begin(); it != grown.end(); ++it) {
            Surface s   = *surface;
            s.expolygon = *it;
            top.surfaces.push_back(s);
        }
    }
    
    /*  if we're slicing with no infill, we can't extend external surfaces
        over non-existent infill */
    SurfaceCollection fill_boundaries;
    if (this->region()->config.fill_density.value > 0) {
        fill_boundaries = SurfaceCollection(surfaces);
    } else {
        for (Surfaces::const_iterator it = surfaces.begin(); it != surfaces.end(); ++it) {
            if (it->surface_type != stInternal)
                fill_boundaries.surfaces.push_back(*it);
        }
    }
    
    // intersect the grown surfaces with the actual fill boundaries
    SurfaceCollection new_surfaces;
    {
        // merge top and bottom in a single collection
        SurfaceCollection tb = top;
        tb.append(bottom);
        
        // group surfaces
        std::vector<SurfacesConstPtr> groups;
        tb.group(&groups);
        
        for (std::vector<SurfacesConstPtr>::const_iterator g = groups.begin(); g != groups.end(); ++g) {
            Polygons subject;
            for (SurfacesConstPtr::const_iterator s = g->begin(); s != g->end(); ++s)
                append_to(subject, (Polygons)**s);
            
            ExPolygons expp = intersection_ex(
                subject,
                (Polygons)fill_boundaries,
                true // to ensure adjacent expolygons are unified
            );
            
            for (ExPolygons::const_iterator ex = expp.begin(); ex != expp.end(); ++ex) {
                Surface s = *g->front();
                s.expolygon = *ex;
                new_surfaces.surfaces.push_back(s);
            }
        }
    }
    
    /* subtract the new top surfaces from the other non-top surfaces and re-add them */
    {
        SurfaceCollection other;
        for (Surfaces::const_iterator s = surfaces.begin(); s != surfaces.end(); ++s) {
            if (s->surface_type != stTop && !s->is_bottom())
                other.surfaces.push_back(*s);
        }
        
        // group surfaces
        std::vector<SurfacesConstPtr> groups;
        other.group(&groups);
        
        for (std::vector<SurfacesConstPtr>::const_iterator g = groups.begin(); g != groups.end(); ++g) {
            Polygons subject;
            for (SurfacesConstPtr::const_iterator s = g->begin(); s != g->end(); ++s)
                append_to(subject, (Polygons)**s);
            
            ExPolygons expp = diff_ex(
                subject,
                (Polygons)new_surfaces
            );
            
            for (ExPolygons::const_iterator ex = expp.begin(); ex != expp.end(); ++ex) {
                Surface s = *g->front();
                s.expolygon = *ex;
                new_surfaces.surfaces.push_back(s);
            }
        }
    }
    
    this->fill_surfaces = new_surfaces;
}
Example #9
0
void edit_bus_dialog()
{
    int side_no, edit_type;
    Bus *the_bus;
    print_all_buses();
    prt(TYPE_BUS_SIDE_NO_TO_EDIT);
    scan_to_buf();
    if (is_zero()) {
        prt(CLS);
        return;
    }
    if (!is_number(buffer)) {
        prt(CLS);
        prt(NOT_A_NUMBER);
        return;
    }
    side_no = atoi(buffer);
    prt(CLS);
    the_bus = find_object_with_item_in(&buses, &side_no, get_side_no, side_no_cmp);
    if (!the_bus)
        return;

    while (1) {
        print_bus_labels();
        print_bus_info(the_bus);
        prt(LINE);
        prt(WHAT_U_WANT_TO_EDIT);
        prt(CHOOSE_OPT);
        scan_to_buf();
        if (is_zero()) {
            prt(CLS);
            return;
        }
        if (!is_number(buffer)){
            prt(CLS);
            prt(NOT_A_NUMBER);
            continue;
        }
        edit_type = atoi(buffer);

        switch (edit_type)
        {
        case 1:
            prt(CURRENT);
            printf("%04d\n", the_bus->side_no);
            prt(NEW);
            scan_to_buf();
            prt(CLS);
            /* if edited successfully - find new place for side_no name (sort) */
            if (edit_bus_side_no(the_bus, buffer)) {
                remove_from(&buses, the_bus, del_node_only);
                append_to(&buses, the_bus, buses_side_no_cmp);
                reappend_bus_memberships(the_bus);
            }
            break;
        case 2:
            prt(CURRENT);
            printf("%d\n", the_bus->line_no);
            prt(NEW);
            scan_to_buf();
            prt(CLS);
            edit_bus_line_no(the_bus, buffer);
            break;
        case 3:
            prt(CURRENT);
            printf("%s\n", the_bus->driver_pesel);
            prt(NEW);
            scan_to_buf();
            prt(CLS);
            edit_bus_driver_pesel(the_bus, buffer);
            break;
        case 4:
            prt(CURRENT);
            printf("%s\n", the_bus->driver_name);
            prt(NEW);
            scan_to_buf();
            prt(CLS);
            edit_bus_driver_name(the_bus, buffer);
            break;
        default:
            prt(CLS);
            prt(INVALID_OPTION);
            break;
        } /* switch */
        modified = 1;
    } /* while */
}
Example #10
0
/// The LayerRegion at this point of time may contain
/// surfaces of various types (internal/bridge/top/bottom/solid).
/// The infills are generated on the groups of surfaces with a compatible type.
/// Fills an array of ExtrusionPathCollection objects containing the infills generated now
/// and the thin fills generated by generate_perimeters().
void
LayerRegion::make_fill()
{
    this->fills.clear();
    
    const double fill_density          = this->region()->config.fill_density;
    const Flow   infill_flow           = this->flow(frInfill);
    const Flow   solid_infill_flow     = this->flow(frSolidInfill);
    const Flow   top_solid_infill_flow = this->flow(frTopSolidInfill);
    const coord_t perimeter_spacing    = this->flow(frPerimeter).scaled_spacing();

    SurfaceCollection surfaces;
    
    // merge adjacent surfaces
    // in case of bridge surfaces, the ones with defined angle will be attached to the ones
    // without any angle (shouldn't this logic be moved to process_external_surfaces()?)
    {
        Polygons polygons_bridged;
        polygons_bridged.reserve(this->fill_surfaces.surfaces.size());
        for (Surfaces::const_iterator it = this->fill_surfaces.surfaces.begin(); it != this->fill_surfaces.surfaces.end(); ++it)
            if (it->is_bridge() && it->bridge_angle >= 0)
                append_to(polygons_bridged, (Polygons)*it);
        
        // group surfaces by distinct properties (equal surface_type, thickness, thickness_layers, bridge_angle)
        // group is of type SurfaceCollection
        // FIXME: Use some smart heuristics to merge similar surfaces to eliminate tiny regions.
        std::vector<SurfacesConstPtr> groups;
        this->fill_surfaces.group(&groups);
        
        // merge compatible solid groups (we can generate continuous infill for them)
        {
            // cache flow widths and patterns used for all solid groups
            // (we'll use them for comparing compatible groups)
            std::vector<SurfaceGroupAttrib> group_attrib(groups.size());
            for (size_t i = 0; i < groups.size(); ++i) {
                const Surface &surface = *groups[i].front();
                // we can only merge solid non-bridge surfaces, so discard
                // non-solid or bridge surfaces
                if (!surface.is_solid() || surface.is_bridge()) continue;
                
                group_attrib[i].is_solid = true;
                group_attrib[i].fw = (surface.is_top()) ? top_solid_infill_flow.width : solid_infill_flow.width;
                group_attrib[i].pattern = surface.is_top() ? this->region()->config.top_infill_pattern.value
                    : surface.is_bottom() ? this->region()->config.bottom_infill_pattern.value
                    : ipRectilinear;
            }
            // Loop through solid groups, find compatible groups and append them to this one.
            for (size_t i = 0; i < groups.size(); ++i) {
                if (!group_attrib[i].is_solid)
                    continue;
                for (size_t j = i + 1; j < groups.size();) {
                    if (group_attrib[i] == group_attrib[j]) {
                        // groups are compatible, merge them
                        append_to(groups[i], groups[j]);
                        groups.erase(groups.begin() + j);
                        group_attrib.erase(group_attrib.begin() + j);
                    } else {
                        ++j;
                    }
                }
            }
        }
        
        // Give priority to oriented bridges. Process the bridges in the first round, the rest of the surfaces in the 2nd round.
        for (size_t round = 0; round < 2; ++ round) {
            for (std::vector<SurfacesConstPtr>::const_iterator it_group = groups.begin(); it_group != groups.end(); ++ it_group) {
                const SurfacesConstPtr &group = *it_group;
                const bool is_oriented_bridge = group.front()->is_bridge() && group.front()->bridge_angle >= 0;
                if (is_oriented_bridge != (round == 0))
                    continue;
                
                // Make a union of polygons defining the infiill regions of a group, use a safety offset.
                Polygons union_p = union_(to_polygons(group), true);
                
                // Subtract surfaces having a defined bridge_angle from any other, use a safety offset.
                if (!is_oriented_bridge && !polygons_bridged.empty())
                    union_p = diff(union_p, polygons_bridged, true);
                
                // subtract any other surface already processed
                //FIXME Vojtech: Because the bridge surfaces came first, they are subtracted twice!
                surfaces.append(
                    diff_ex(union_p, to_polygons(surfaces), true),
                    *group.front()  // template
                );
            }
        }
    }
    
    // we need to detect any narrow surfaces that might collapse
    // when adding spacing below
    // such narrow surfaces are often generated in sloping walls
    // by bridge_over_infill() and combine_infill() as a result of the
    // subtraction of the combinable area from the layer infill area,
    // which leaves small areas near the perimeters
    // we are going to grow such regions by overlapping them with the void (if any)
    // TODO: detect and investigate whether there could be narrow regions without
    // any void neighbors
    {
        coord_t distance_between_surfaces = std::max(
            std::max(infill_flow.scaled_spacing(), solid_infill_flow.scaled_spacing()),
            top_solid_infill_flow.scaled_spacing()
        );
        
        Polygons surfaces_polygons = (Polygons)surfaces;
        Polygons collapsed = diff(
            surfaces_polygons,
            offset2(surfaces_polygons, -distance_between_surfaces/2, +distance_between_surfaces/2),
            true
        );
            
        Polygons to_subtract;
        surfaces.filter_by_type((stInternal | stVoid), &to_subtract);
                
        append_to(to_subtract, collapsed);
        surfaces.append(
            intersection_ex(
                offset(collapsed, distance_between_surfaces),
                to_subtract,
                true
            ),
            (stInternal | stSolid)
        );
    }

    if (false) {
//        require "Slic3r/SVG.pm";
//        Slic3r::SVG::output("fill_" . $layerm->print_z . ".svg",
//            expolygons      => [ map $_->expolygon, grep !$_->is_solid, @surfaces ],
//            red_expolygons  => [ map $_->expolygon, grep  $_->is_solid, @surfaces ],
//        );
    }

    for (Surfaces::const_iterator surface_it = surfaces.surfaces.begin();
        surface_it != surfaces.surfaces.end(); ++surface_it) {
        
        const Surface &surface = *surface_it;
        if (surface.surface_type == (stInternal | stVoid))
            continue;
        
        InfillPattern fill_pattern = this->region()->config.fill_pattern.value;
        double density = fill_density;
        FlowRole role = (surface.is_top()) ? frTopSolidInfill
            : surface.is_solid() ? frSolidInfill
            : frInfill;
        const bool is_bridge = this->layer()->id() > 0 && surface.is_bridge();
        
        if (surface.is_solid()) {
            density = 100.;
            fill_pattern = (surface.is_top()) ? this->region()->config.top_infill_pattern.value
                : (surface.is_bottom() && !is_bridge) ? this->region()->config.bottom_infill_pattern.value
                : ipRectilinear;
        } else if (density <= 0)
            continue;
        
        // get filler object
        #if SLIC3R_CPPVER >= 11
            std::unique_ptr<Fill> f = std::unique_ptr<Fill>(Fill::new_from_type(fill_pattern));
        #else
            std::auto_ptr<Fill> f = std::auto_ptr<Fill>(Fill::new_from_type(fill_pattern));
        #endif
        
        // switch to rectilinear if this pattern doesn't support solid infill
        if (density > 99 && !f->can_solid())
            #if SLIC3R_CPPVER >= 11
                f = std::unique_ptr<Fill>(Fill::new_from_type(ipRectilinear));
            #else
                f = std::auto_ptr<Fill>(Fill::new_from_type(ipRectilinear));
            #endif
        
        f->bounding_box = this->layer()->object()->bounding_box();
        
        // calculate the actual flow we'll be using for this infill
        coordf_t h = (surface.thickness == -1) ? this->layer()->height : surface.thickness;
        Flow flow = this->region()->flow(
            role,
            h,
            is_bridge || f->use_bridge_flow(),  // bridge flow?
            this->layer()->id() == 0,           // first layer?
            -1,                                 // auto width
            *this->layer()->object()
        );
        
        // calculate flow spacing for infill pattern generation
        bool using_internal_flow = false;
        if (!surface.is_solid() && !is_bridge) {
            // it's internal infill, so we can calculate a generic flow spacing
            // for all layers, for avoiding the ugly effect of
            // misaligned infill on first layer because of different extrusion width and
            // layer height
            Flow internal_flow = this->region()->flow(
                frInfill,
                h,  // use the calculated surface thickness here for internal infill instead of the layer height to account for infill_every_layers
                false,  // no bridge
                false,  // no first layer
                -1,     // auto width
                *this->layer()->object()
            );
            f->min_spacing = internal_flow.spacing();
            using_internal_flow = true;
        } else {
            f->min_spacing = flow.spacing();
        }
        
        f->endpoints_overlap = scale_(this->region()->config.get_abs_value("infill_overlap",
            (unscale(perimeter_spacing) + (f->min_spacing))/2));
        f->layer_id = this->layer()->id();
        f->z        = this->layer()->print_z;
        f->angle    = Geometry::deg2rad(this->region()->config.fill_angle.value);
        
        // Maximum length of the perimeter segment linking two infill lines.
        f->link_max_length = (!is_bridge && density > 80)
            ? scale_(3 * f->min_spacing)
            : 0;
        
        // Used by the concentric infill pattern to clip the loops to create extrusion paths.
        f->loop_clipping = scale_(flow.nozzle_diameter) * LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER;
        
        // apply half spacing using this flow's own spacing and generate infill
        f->density = density/100;
        f->dont_adjust = false;
        /*
        std::cout << surface.expolygon.dump_perl() << std::endl
            << " layer_id: " << f->layer_id << " z: " << f->z
            << " angle: " << f->angle << " min-spacing: " << f->min_spacing
            << " endpoints_overlap: " << f->endpoints_overlap << std::endl << std::endl;
        */
        Polylines polylines = f->fill_surface(surface);
        if (polylines.empty())
            continue;

        // calculate actual flow from spacing (which might have been adjusted by the infill
        // pattern generator)
        if (using_internal_flow) {
            // if we used the internal flow we're not doing a solid infill
            // so we can safely ignore the slight variation that might have
            // been applied to f->spacing()
        } else {
            flow = Flow::new_from_spacing(f->spacing(), flow.nozzle_diameter, h, is_bridge || f->use_bridge_flow());
        }

        // Save into layer.
        ExtrusionEntityCollection* coll = new ExtrusionEntityCollection();
        coll->no_sort = f->no_sort();
        this->fills.entities.push_back(coll);
        
        {
            ExtrusionRole role;
            if (is_bridge) {
                role = erBridgeInfill;
            } else if (surface.is_solid()) {
                role = (surface.is_top()) ? erTopSolidInfill : erSolidInfill;
            } else {
                role = erInternalInfill;
            }
            
            ExtrusionPath templ(role);
            templ.mm3_per_mm    = flow.mm3_per_mm();
            templ.width         = flow.width;
            templ.height        = flow.height;
            
            coll->append(STDMOVE(polylines), templ);
        }
    }

    // add thin fill regions
    // thin_fills are of C++ Slic3r::ExtrusionEntityCollection, perl type Slic3r::ExtrusionPath::Collection
    // Unpacks the collection, creates multiple collections per path so that they will
    // be individually included in the nearest neighbor search.
    // The path type could be ExtrusionPath, ExtrusionLoop or ExtrusionEntityCollection.
    for (ExtrusionEntitiesPtr::const_iterator thin_fill = this->thin_fills.entities.begin(); thin_fill != this->thin_fills.entities.end(); ++ thin_fill) {
        ExtrusionEntityCollection* coll = new ExtrusionEntityCollection();
        this->fills.entities.push_back(coll);
        coll->append(**thin_fill);
    }
}
string comfy_keyword_public(string name, string source)
{
    struct slre_cap groups[2];
    string match_start;
    int start = 0;
    int bytes_read = 0;
    int source_len = strlen(source);

    string target;
    asprintf(&target, "");
    bool found = false;
    string end_of_match;

    while (0 < (bytes_read = match_re(source, start, groups, &match_start)))
    {
        found = true;
        const string docstr  = (const string) groups[0].ptr;
        int docstr_len = groups[0].len;
        const string mhead   = (const string) groups[1].ptr;
        int mhead_len  = groups[1].len;

        //string match_start = docstr ? docstr : mhead;
        string source_start = source + start;
        target = append_to(target, source_start, match_start - source_start);

        if (docstr)
        {
            string pubdoc = make_public_string(docstr, docstr_len);
            target = append_str_to(target, pubdoc);
        }

        string pub_mhead = make_public_string(mhead, mhead_len);
        target = append_str_to(target, "\n");

        target = append_str_to(target, pub_mhead);
        target = append_str_to(target, ";\n");
        target = append_to(target, mhead, mhead_len);

        end_of_match = mhead + mhead_len;
        start = end_of_match - source;

        /*
        int size_matched =  groups[1].ptr - source + groups[1].len;

        remove_whitespace(&groups[0]);
        remove_whitespace(&groups[1]);
        if (0 >= groups[0].len || 0 >= groups[1].len){
            return NULL;
        }

        string start_str = source + start;

        string append_target;
        asprintf(&append_target, "%s%.*s", target,
                (int)(groups[0].ptr - start_str),  start_str);

        string to_header;
        string to_source;

        asprintf(&to_header, "§%.*s\n%.*s;",
                groups[0].len, groups[0].ptr,
                groups[1].len, groups[1].ptr);
        string masked = string_replace_all_in(to_header, "\n", "\n§");

        asprintf(&to_source, "%s%s\n%.*s",
                append_target,
                masked, groups[1].len, groups[1].ptr);



        free(masked);
        free(append_target);
        free(to_header);

        free(target);
        target = to_source;

        end_of_match = source + start + bytes_read - 1;
*/
    }

    if (found){
        string append_tail;
        asprintf(&append_tail, "%s%s", target, end_of_match);
        free(target);
        return append_tail;
    } else {
        return NULL;
    }
}
static string append_str_to(string target, string str){
    return append_to(target, str, strlen(str));
}