// OpenSTA, Static Timing Analyzer // Copyright (c) 2024, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . #include "SdcNetwork.hh" #include "StringUtil.hh" #include "PatternMatch.hh" #include "ParseBus.hh" namespace sta { using std::to_string; static string escapeDividers(const char *token, const Network *network); static string escapeBrackets(const char *token, const Network *network); NetworkNameAdapter::NetworkNameAdapter(Network *network) : NetworkEdit(), network_(network), network_edit_(dynamic_cast(network)) { } bool NetworkNameAdapter::linkNetwork(const char *top_cell_name, bool make_black_boxes, Report *report, bool use_top_cell_name) { return network_->linkNetwork(top_cell_name, make_black_boxes, report, use_top_cell_name); } Instance * NetworkNameAdapter::topInstance() const { return network_->topInstance(); } LibertyLibrary * NetworkNameAdapter::defaultLibertyLibrary() const { return network_->defaultLibertyLibrary(); } LibraryIterator * NetworkNameAdapter::libraryIterator() const { return network_->libraryIterator(); } LibertyLibraryIterator * NetworkNameAdapter::libertyLibraryIterator() const { return network_->libertyLibraryIterator(); } Library * NetworkNameAdapter::findLibrary(const char *name) { return network_->findLibrary(name); } LibertyLibrary * NetworkNameAdapter::findLiberty(const char *name) { return network_->findLiberty(name); } LibertyLibrary * NetworkNameAdapter::findLibertyFilename(const char *filename) { return network_->findLibertyFilename(filename); } const char * NetworkNameAdapter::name(const Library *library) const { return network_->name(library); } ObjectId NetworkNameAdapter::id(const Library *library) const { return network_->id(library); } Cell * NetworkNameAdapter::findCell(const Library *library, const char *name) const { return network_->findCell(library, name); } CellSeq NetworkNameAdapter::findCellsMatching(const Library *library, const PatternMatch *pattern) const { return network_->findCellsMatching(library, pattern); } //////////////////////////////////////////////////////////////// const char * NetworkNameAdapter::name(const Cell *cell) const { return network_->name(cell); } ObjectId NetworkNameAdapter::id(const Cell *cell) const { return network_->id(cell); } string NetworkNameAdapter::getAttribute(const Cell *cell, const string &key) const { return network_->getAttribute(cell, key); } Library * NetworkNameAdapter::library(const Cell *cell) const { return network_->library(cell); } const char * NetworkNameAdapter::filename(const Cell *cell) { return network_->filename(cell); } LibertyCell * NetworkNameAdapter::libertyCell(Cell *cell) const { return network_->libertyCell(cell); } const LibertyCell * NetworkNameAdapter::libertyCell(const Cell *cell) const { return network_->libertyCell(cell); } Cell * NetworkNameAdapter::cell(LibertyCell *cell) const { return network_->cell(cell); } const Cell * NetworkNameAdapter::cell(const LibertyCell *cell) const { return network_->cell(cell); } Port * NetworkNameAdapter::findPort(const Cell *cell, const char *name) const { return network_->findPort(cell, name); } PortSeq NetworkNameAdapter::findPortsMatching(const Cell *cell, const PatternMatch *pattern) const { return network_->findPortsMatching(cell, pattern); } bool NetworkNameAdapter::isLeaf(const Cell *cell) const { return network_->isLeaf(cell); } CellPortIterator * NetworkNameAdapter::portIterator(const Cell *cell) const { return network_->portIterator(cell); } CellPortBitIterator * NetworkNameAdapter::portBitIterator(const Cell *cell) const { return network_->portBitIterator(cell); } int NetworkNameAdapter::portBitCount(const Cell *cell) const { return network_->portBitCount(cell); } //////////////////////////////////////////////////////////////// const char * NetworkNameAdapter::name(const Port *port) const { return network_->name(port); } ObjectId NetworkNameAdapter::id(const Port *port) const { return network_->id(port); } Cell * NetworkNameAdapter::cell(const Port *port) const { return network_->cell(port); } LibertyPort * NetworkNameAdapter::libertyPort(const Port *port) const { return network_->libertyPort(port); } PortDirection * NetworkNameAdapter::direction(const Port *port) const { return network_->direction(port); } VertexId NetworkNameAdapter::vertexId(const Pin *pin) const { return network_->vertexId(pin); } void NetworkNameAdapter::setVertexId(Pin *pin, VertexId id) { network_->setVertexId(pin, id); } void NetworkNameAdapter::location(const Pin *pin, // Return values. double &x, double &y, bool &exists) const { network_->location(pin, x, y, exists); } bool NetworkNameAdapter::isBundle(const Port *port) const { return network_->isBundle(port); } bool NetworkNameAdapter::isBus(const Port *port) const { return network_->isBus(port); } const char * NetworkNameAdapter::busName(const Port *port) const { return network_->busName(port); } Port * NetworkNameAdapter::findBusBit(const Port *port, int index) const { return network_->findMember(port, index); } int NetworkNameAdapter::size(const Port *port) const { return network_->size(port); } int NetworkNameAdapter::fromIndex(const Port *port) const { return network_->fromIndex(port); } int NetworkNameAdapter::toIndex(const Port *port) const { return network_->toIndex(port); } bool NetworkNameAdapter::hasMembers(const Port *port) const { return network_->hasMembers(port); } Port * NetworkNameAdapter::findMember(const Port *port, int index) const { return network_->findMember(port, index); } PortMemberIterator * NetworkNameAdapter::memberIterator(const Port *port) const { return network_->memberIterator(port); } //////////////////////////////////////////////////////////////// ObjectId NetworkNameAdapter::id(const Instance *instance) const { return network_->id(instance); } Cell * NetworkNameAdapter::cell(const Instance *instance) const { return network_->cell(instance); } string NetworkNameAdapter::getAttribute(const Instance *inst, const string &key) const { return network_->getAttribute(inst, key); } Instance * NetworkNameAdapter::parent(const Instance *instance) const { return network_->parent(instance); } bool NetworkNameAdapter::isLeaf(const Instance *instance) const { return network_->isLeaf(instance); } Pin * NetworkNameAdapter::findPin(const Instance *instance, const Port *port) const { return network_->findPin(instance, port); } Pin * NetworkNameAdapter::findPin(const Instance *instance, const LibertyPort *port) const { return network_->findPin(instance, port); } InstanceChildIterator * NetworkNameAdapter::childIterator(const Instance *instance) const { return network_->childIterator(instance); } InstancePinIterator * NetworkNameAdapter::pinIterator(const Instance *instance) const { return network_->pinIterator(instance); } InstanceNetIterator * NetworkNameAdapter::netIterator(const Instance *instance) const { return network_->netIterator(instance); } ObjectId NetworkNameAdapter::id(const Pin *pin) const { return network_->id(pin); } Port * NetworkNameAdapter::port(const Pin *pin) const { return network_->port(pin); } Instance * NetworkNameAdapter::instance(const Pin *pin) const { return network_->instance(pin); } Net * NetworkNameAdapter::net(const Pin *pin) const { return network_->net(pin); } Term * NetworkNameAdapter::term(const Pin *pin) const { return network_->term(pin); } PortDirection * NetworkNameAdapter::direction(const Pin *pin) const { return network_->direction(pin); } ObjectId NetworkNameAdapter::id(const Term *term) const { return network_->id(term); } Net * NetworkNameAdapter::net(const Term *term) const { return network_->net(term); } Pin * NetworkNameAdapter::pin(const Term *term) const { return network_->pin(term); } ObjectId NetworkNameAdapter::id(const Net *net) const { return network_->id(net); } Instance * NetworkNameAdapter::instance(const Net *net) const { return network_->instance(net); } NetPinIterator * NetworkNameAdapter::pinIterator(const Net *net) const { return network_->pinIterator(net); } NetTermIterator * NetworkNameAdapter::termIterator(const Net *net) const { return network_->termIterator(net); } bool NetworkNameAdapter::isPower(const Net *net) const { return network_->isPower(net); } bool NetworkNameAdapter::isGround(const Net *net) const { return network_->isGround(net); } ConstantPinIterator * NetworkNameAdapter::constantPinIterator() { return network_->constantPinIterator(); } char NetworkNameAdapter::pathDivider() const { return network_->pathDivider(); } void NetworkNameAdapter::setPathDivider(char divider) { network_->setPathDivider(divider); } char NetworkNameAdapter::pathEscape() const { return network_->pathEscape(); } void NetworkNameAdapter::setPathEscape(char escape) { network_->setPathEscape(escape); } bool NetworkNameAdapter::isEditable() const { return network_->isEditable(); } LibertyLibrary * NetworkNameAdapter::makeLibertyLibrary(const char *name, const char *filename) { return network_edit_->makeLibertyLibrary(name, filename); } Instance * NetworkNameAdapter::makeInstance(LibertyCell *cell, const char *name, Instance *parent) { return network_edit_->makeInstance(cell, name, parent); } void NetworkNameAdapter::makePins(Instance *inst) { network_edit_->makePins(inst); } void NetworkNameAdapter::mergeInto(Net *net, Net *into_net) { network_edit_->mergeInto(net, into_net); } Net * NetworkNameAdapter::mergedInto(Net *net) { return network_edit_->mergedInto(net); } Net * NetworkNameAdapter::makeNet(const char *name, Instance *parent) { return network_edit_->makeNet(name, parent); } void NetworkNameAdapter::replaceCell(Instance *inst, Cell *to_cell) { network_edit_->replaceCell(inst, to_cell); } Pin * NetworkNameAdapter::connect(Instance *inst, Port *port, Net *net) { return network_edit_->connect(inst, port, net); } Pin * NetworkNameAdapter::connect(Instance *inst, LibertyPort *port, Net *net) { return network_edit_->connect(inst, port, net); } void NetworkNameAdapter::disconnectPin(Pin *pin) { network_edit_->disconnectPin(pin); } void NetworkNameAdapter::deleteNet(Net *net) { network_edit_->deleteNet(net); } void NetworkNameAdapter::deletePin(Pin *pin) { network_edit_->deletePin(pin); } void NetworkNameAdapter::deleteInstance(Instance *inst) { network_edit_->deleteInstance(inst); } //////////////////////////////////////////////////////////////// Network * makeSdcNetwork(Network *network) { return new SdcNetwork(network); } SdcNetwork::SdcNetwork(Network *network) : NetworkNameAdapter(network) { } // Translate sta namespace to sdc namespace. // Remove all escapes. const char * SdcNetwork::staToSdc(const char *sta_name) const { char escape = pathEscape(); char *sdc_name = makeTmpString(strlen(sta_name) + 1); char *d = sdc_name; for (const char *s = sta_name; *s; s++) { char ch = s[0]; if (ch == escape) { char next_ch = s[1]; // Escaped escape. if (next_ch == escape) { *d++ = ch; *d++ = next_ch; s++; } } else // Non escape. *d++ = ch; } *d++ = '\0'; return sdc_name; } Port * SdcNetwork::findPort(const Cell *cell, const char *name) const { Port *port = network_->findPort(cell, name); if (port == nullptr) { // Look for matches after escaping brackets. bool is_bus; string bus_name; int index; parseBusName(name, '[', ']', pathEscape(), is_bus, bus_name, index); if (is_bus) { string escaped1 = escapeBrackets(name, this); port = network_->findPort(cell, escaped1.c_str()); if (port == nullptr) { // Try escaping base foo\[0\][1] string escaped2; string escaped_bus_name = escapeBrackets(bus_name.c_str(), this); stringPrint(escaped2, "%s[%d]", escaped_bus_name.c_str(), index); port = network_->findPort(cell, escaped2.c_str()); } } else { // Try escaping brackets foo\[0\].bar string escaped = escapeBrackets(name, this); port = network_->findPort(cell, escaped.c_str()); } } return port; } PortSeq SdcNetwork::findPortsMatching(const Cell *cell, const PatternMatch *pattern) const { PortSeq matches = network_->findPortsMatching(cell, pattern); if (matches.empty()) { // Look for matches after escaping brackets. bool is_bus; string bus_name; int index; parseBusName(pattern->pattern(), '[', ']', pathEscape(), is_bus, bus_name, index); if (is_bus) { string escaped1 = escapeBrackets(pattern->pattern(), this); PatternMatch escaped_pattern1(escaped1.c_str(), pattern); matches = network_->findPortsMatching(cell, &escaped_pattern1); if (matches.empty()) { // Try escaping base foo\[0\][1] string escaped_name = escapeBrackets(bus_name.c_str(), this); escaped_name += '['; escaped_name += to_string(index); escaped_name += ']'; PatternMatch escaped_pattern2(escaped_name.c_str(), pattern); matches = network_->findPortsMatching(cell, &escaped_pattern2); } } else { // Try escaping brackets foo\[0\].bar string escaped = escapeBrackets(pattern->pattern(), this); PatternMatch escaped_pattern(escaped.c_str(), pattern); matches = network_->findPortsMatching(cell, &escaped_pattern); } } return matches; } const char * SdcNetwork::name(const Port *port) const { return staToSdc(network_->name(port)); } const char * SdcNetwork::busName(const Port *port) const { return staToSdc(network_->busName(port)); } const char * SdcNetwork::name(const Instance *instance) const { return staToSdc(network_->name(instance)); } const char * SdcNetwork::pathName(const Instance *instance) const { return staToSdc(network_->pathName(instance)); } const char * SdcNetwork::pathName(const Pin *pin) const { return staToSdc(network_->pathName(pin)); } const char * SdcNetwork::portName(const Pin *pin) const { return staToSdc(network_->portName(pin)); } const char * SdcNetwork::name(const Net *net) const { return staToSdc(network_->name(net)); } const char * SdcNetwork::pathName(const Net *net) const { return staToSdc(network_->pathName(net)); } //////////////////////////////////////////////////////////////// Instance * SdcNetwork::findInstance(const char *path_name) const { const char *child_name; Instance *parent; parsePath(path_name, parent, child_name); if (parent == nullptr) parent = network_->topInstance(); Instance *child = findChild(parent, child_name); if (child == nullptr) { string escaped_name = escapeDividers(child_name, this); child = findChild(parent, escaped_name.c_str()); } return child; } InstanceSeq SdcNetwork::findInstancesMatching(const Instance *context, const PatternMatch *pattern) const { InstanceSeq matches; findInstancesMatching1(context, pattern, matches); return matches; } void SdcNetwork::findInstancesMatching1(const Instance *context, const PatternMatch *pattern, InstanceSeq &matches) const { visitMatches(context, pattern, [&](const Instance *instance, const PatternMatch *tail) { size_t match_count = matches.size(); network_->findChildrenMatching(instance, tail, matches); return matches.size() != match_count; }); } Instance * SdcNetwork::findChild(const Instance *parent, const char *name) const { Instance *child = network_->findChild(parent, name); if (child == nullptr) { string escaped = escapeBrackets(name, this); child = network_->findChild(parent, escaped.c_str()); } return child; } //////////////////////////////////////////////////////////////// Net * SdcNetwork::findNet(const char *path_name) const { const char *net_name; Instance *inst; parsePath(path_name, inst, net_name); if (inst == nullptr) inst = network_->topInstance(); return findNet(inst, net_name); } Net * SdcNetwork::findNet(const Instance *instance, const char *net_name) const { Net *net = network_->findNet(instance, net_name); if (net == nullptr) { string net_name1 = escapeBrackets(net_name, this); net = network_->findNet(instance, net_name1.c_str()); } return net; } NetSeq SdcNetwork::findNetsMatching(const Instance *parent, const PatternMatch *pattern) const { NetSeq matches; visitMatches(parent, pattern, [&](const Instance *instance, const PatternMatch *tail) { size_t match_count = matches.size(); network_->findInstNetsMatching(instance, tail, matches); return matches.size() != match_count; }); return matches; } void SdcNetwork::findInstNetsMatching(const Instance *instance, const PatternMatch *pattern, NetSeq &matches) const { network_->findInstNetsMatching(instance, pattern, matches); if (matches.empty()) { // Look for matches after escaping path dividers. string escaped_pattern = escapeDividers(pattern->pattern(), this); const PatternMatch escaped_dividers(escaped_pattern.c_str(), pattern); network_->findInstNetsMatching(instance, &escaped_dividers, matches); if (matches.empty()) { // Look for matches after escaping brackets. string escaped_pattern2 = escapeBrackets(pattern->pattern(),this); const PatternMatch escaped_brkts(escaped_pattern2.c_str(), pattern); network_->findInstNetsMatching(instance, &escaped_brkts, matches); } } } //////////////////////////////////////////////////////////////// Pin * SdcNetwork::findPin(const char *path_name) const { const char *port_name; Instance *inst; parsePath(path_name, inst, port_name); if (inst == nullptr) inst = network_->topInstance(); return findPin(inst, port_name); } Pin * SdcNetwork::findPin(const Instance *instance, const char *port_name) const { Pin *pin = network_->findPin(instance, port_name); if (pin == nullptr) { // Look for match after escaping brackets. bool is_bus; string bus_name; int index; parseBusName(port_name, '[', ']', pathEscape(), is_bus, bus_name, index); if (is_bus) { string escaped1 = escapeBrackets(port_name, this); pin = network_->findPin(instance, escaped1.c_str()); if (pin == nullptr) { // Try escaping base foo\[0\][1] string escaped_bus_name = escapeBrackets(bus_name.c_str(), this); string escaped2; stringPrint(escaped2, "%s[%d]", escaped_bus_name.c_str(), index); pin = network_->findPin(instance, escaped2.c_str()); } } else { // Try escaping port brackets foo\[0\].bar string escaped = escapeBrackets(port_name, this); pin = network_->findPin(instance, escaped.c_str()); } } return pin; } // Top level ports are not considered pins by get_pins. PinSeq SdcNetwork::findPinsMatching(const Instance *instance, const PatternMatch *pattern) const { PinSeq matches; if (stringEq(pattern->pattern(), "*")) { // Pattern of '*' matches all child instance pins. InstanceChildIterator *child_iter = childIterator(instance); while (child_iter->hasNext()) { Instance *child = child_iter->next(); InstancePinIterator *pin_iter = pinIterator(child); while (pin_iter->hasNext()) { const Pin *pin = pin_iter->next(); matches.push_back(pin); } delete pin_iter; } delete child_iter; } else visitMatches(instance, pattern, [&](const Instance *instance, const PatternMatch *tail) { return visitPinTail(instance, tail, matches); }); return matches; } bool SdcNetwork::visitPinTail(const Instance *instance, const PatternMatch *tail, PinSeq &matches) const { bool found_match = false; if (instance != network_->topInstance()) { Cell *cell = network_->cell(instance); CellPortIterator *port_iter = network_->portIterator(cell); while (port_iter->hasNext()) { Port *port = port_iter->next(); const char *port_name = network_->name(port); if (network_->hasMembers(port)) { bool bus_matches = tail->match(port_name); if (!bus_matches) { string escaped_name = escapeDividers(port_name, network_); bus_matches = tail->match(escaped_name); } PortMemberIterator *member_iter = network_->memberIterator(port); while (member_iter->hasNext()) { Port *member_port = member_iter->next(); const Pin *pin = network_->findPin(instance, member_port); if (pin) { if (bus_matches) { matches.push_back(pin); found_match = true; } else { const char *member_name = network_->name(member_port); bool member_matches = tail->match(member_name); if (!member_matches) { string escaped_name = escapeDividers(member_name, network_); member_matches = tail->match(escaped_name); } if (member_matches) { matches.push_back(pin); found_match = true; } } } } delete member_iter; } else { bool port_matches = tail->match(port_name); if (!port_matches) { string escaped_name = escapeDividers(port_name, network_); port_matches = tail->match(escaped_name); } if (port_matches) { Pin *pin = network_->findPin(instance, port); if (pin) { matches.push_back(pin); found_match = true; } } } } delete port_iter; } return found_match; } Instance * SdcNetwork::makeInstance(LibertyCell *cell, const char *name, Instance *parent) { string escaped_name = escapeDividers(name, this); return network_edit_->makeInstance(cell, escaped_name.c_str(), parent); } Net * SdcNetwork::makeNet(const char *name, Instance *parent) { string escaped_name = escapeDividers(name, this); return network_edit_->makeNet(escaped_name.c_str(), parent); } //////////////////////////////////////////////////////////////// // Helper to parse an instance path (with optional net/port tail). // Since dividers are not escaped in SDC, look for an instance for // each sub-section of the path. If none is found, escape the divider // and keep looking. For the path a/b/c this looks for instances // a // a\/b // a\/b\/c void SdcNetwork::parsePath(const char *path, // Return values. Instance *&inst, const char *&path_tail) const { int divider_count, path_length; scanPath(path, divider_count, path_length); if (divider_count > 0) parsePath(path, divider_count, path_length, inst, path_tail); else { inst = nullptr; path_tail = path; } } // Scan the path for unescaped dividers. void SdcNetwork::scanPath(const char *path, // Return values. // Unescaped divider count. int ÷r_count, int &path_length) const { divider_count = 0; path_length = 0; for (const char *s = path; *s; s++) { char ch = *s; if (ch == escape_) { // Make sure we don't skip the null if escape is the last char. if (s[1] != '\0') { s++; path_length++; } } else if (ch == divider_) divider_count++; path_length++; } } void SdcNetwork::parsePath(const char *path, int divider_count, int path_length, // Return values. Instance *&inst, const char *&path_tail) const { Instance *parent = topInstance(); // Leave room to escape all the dividers and '\0'. int inst_path_length = path_length + divider_count + 1; char *inst_path = new char[inst_path_length]; inst = nullptr; path_tail = path; char *p = inst_path; for (const char *s = path; *s; s++) { char ch = *s; if (ch == escape_) { // Make sure we don't skip the null if escape is the last char. if (s[1] != '\0') { *p++ = ch; *p++ = s[1]; s++; } } else if (ch == divider_) { // Terminate the sub-path up to this divider. *p = '\0'; Instance *child = findChild(parent, inst_path); if (child) { // Found an instance for the sub-path up to this divider. parent = inst = child; // Reset the instance path. p = inst_path; path_tail = s + 1; } else { // No match for sub-path. Escape the divider and keep looking. *p++ = escape_; *p++ = divider_; } } else *p++ = ch; if (p - inst_path + 1 > inst_path_length) report_->critical(1500, "inst path string lenth estimate busted"); } *p = '\0'; stringDelete(inst_path); } // Helper to visit instance path matches. // Since dividers are not escaped in SDC, look for instance matches for // each sub-section of the path. If none are found, escape the divider // and keep looking. For the path a/b/c this looks for instances // a // a\/b // a\/b\/c bool SdcNetwork::visitMatches(const Instance *parent, const PatternMatch *pattern, const std::function visit_tail) const { int divider_count, path_length; scanPath(pattern->pattern(), divider_count, path_length); // Leave room to escape all the dividers and '\0'. int inst_path_length = path_length + divider_count + 1; char *inst_path = new char[inst_path_length]; char *p = inst_path; bool has_brkts = false; bool found_match = false; for (const char *s = pattern->pattern(); *s; s++) { char ch = *s; if (ch == escape_) { // Make sure we don't skip the null if escape is the last char. if (s[1] != '\0') { *p++ = ch; *p++ = s[1]; s++; } } else if (ch == divider_) { // Terminate the sub-path up to this divider. *p = '\0'; PatternMatch matcher(inst_path, pattern); InstanceSeq matches; network_->findChildrenMatching(parent, &matcher, matches); if (has_brkts && matches.empty()) { // Look for matches after escaping brackets. string escaped_brkts = escapeBrackets(inst_path, this); const PatternMatch escaped_pattern(escaped_brkts, pattern); network_->findChildrenMatching(parent, &escaped_pattern, matches); } if (!matches.empty()) { // Found instance matches for the sub-path up to this divider. const PatternMatch tail_pattern(s + 1, pattern); InstanceSeq::Iterator match_iter(matches); while (match_iter.hasNext()) { const Instance *match = match_iter.next(); // Recurse to save the iterator state so we can iterate over // multiple nested partial matches. found_match |= visitMatches(match, &tail_pattern, visit_tail); } } // Escape the divider and keep looking. *p++ = escape_; *p++ = divider_; } else { if (ch == '[' || ch == ']') has_brkts = true; *p++ = ch; } if (p - inst_path + 1 > inst_path_length) report_->critical(1501, "inst path string lenth estimate exceeded"); } *p = '\0'; if (!found_match) { PatternMatch tail_pattern(inst_path, pattern); found_match |= visit_tail(parent, &tail_pattern); if (!found_match && has_brkts) { // Look for matches after escaping brackets. string escaped_path = escapeBrackets(inst_path, this); const PatternMatch escaped_tail(escaped_path, pattern); found_match |= visit_tail(parent, &escaped_tail); } } stringDelete(inst_path); return found_match; } //////////////////////////////////////////////////////////////// static string escapeDividers(const char *token, const Network *network) { return escapeChars(token, network->pathDivider(), '\0', network->pathEscape()); } static string escapeBrackets(const char *token, const Network *network) { return escapeChars(token, '[', ']', network->pathEscape()); } } // namespace