static int S_render_node(cmark_renderer *renderer, cmark_node *node, cmark_event_type ev_type, int options) { int list_number; char list_number_string[20]; bool entering = (ev_type == CMARK_EVENT_ENTER); cmark_list_type list_type; const char* roman_numerals[] = { "", "i", "ii", "iii", "iv", "v", "vi", "vii", "viii", "ix", "x" }; // avoid warning about unused parameter: (void)(options); switch (node->type) { case CMARK_NODE_DOCUMENT: break; case CMARK_NODE_BLOCK_QUOTE: if (entering) { LIT("\\begin{quote}"); CR(); } else { LIT("\\end{quote}"); BLANKLINE(); } break; case CMARK_NODE_LIST: list_type = cmark_node_get_list_type(node); if (entering) { LIT("\\begin{"); LIT(list_type == CMARK_ORDERED_LIST ? "enumerate" : "itemize"); LIT("}"); CR(); list_number = cmark_node_get_list_start(node); if (list_number > 1) { sprintf(list_number_string, "%d", list_number); LIT("\\setcounter{enum"); LIT((char *)roman_numerals[S_get_enumlevel(node)]); LIT("}{"); OUT(list_number_string, false, NORMAL); LIT("}"); CR(); } } else { LIT("\\end{"); LIT(list_type == CMARK_ORDERED_LIST ? "enumerate" : "itemize"); LIT("}"); BLANKLINE(); } break; case CMARK_NODE_ITEM: if (entering) { LIT("\\item "); } else { CR(); } break; case CMARK_NODE_HEADER: if (entering) { switch (cmark_node_get_header_level(node)) { case 1: LIT("\\section"); break; case 2: LIT("\\subsection"); break; case 3: LIT("\\subsubsection"); break; case 4: LIT("\\paragraph"); break; case 5: LIT("\\subparagraph"); break; } LIT("{"); } else { LIT("}"); BLANKLINE(); } break; case CMARK_NODE_CODE_BLOCK: CR(); LIT("\\begin{verbatim}"); CR(); OUT(cmark_node_get_literal(node), false, LITERAL); CR(); LIT("\\end{verbatim}"); BLANKLINE(); break; case CMARK_NODE_HTML: break; case CMARK_NODE_HRULE: BLANKLINE(); LIT("\\begin{center}\\rule{0.5\\linewidth}{\\linethickness}\\end{center}"); BLANKLINE(); break; case CMARK_NODE_PARAGRAPH: if (!entering) { BLANKLINE(); } break; case CMARK_NODE_TEXT: OUT(cmark_node_get_literal(node), true, NORMAL); break; case CMARK_NODE_LINEBREAK: LIT("\\\\"); CR(); break; case CMARK_NODE_SOFTBREAK: if (renderer->width == 0) { CR(); } else { OUT(" ", true, NORMAL); } break; case CMARK_NODE_CODE: LIT("\\texttt{"); OUT(cmark_node_get_literal(node), false, NORMAL); LIT("}"); break; case CMARK_NODE_INLINE_HTML: break; case CMARK_NODE_STRONG: if (entering) { LIT("\\textbf{"); } else { LIT("}"); } break; case CMARK_NODE_EMPH: if (entering) { LIT("\\emph{"); } else { LIT("}"); } break; case CMARK_NODE_LINK: if (entering) { const char* url = cmark_node_get_url(node); // requires \usepackage{hyperref} switch(get_link_type(node)) { case URL_AUTOLINK: LIT("\\url{"); OUT(url, false, URL); break; case EMAIL_AUTOLINK: LIT("\\href{"); OUT(url, false, URL); LIT("}\\nolinkurl{"); break; case NORMAL_LINK: LIT("\\href{"); OUT(url, false, URL); LIT("}{"); break; case NO_LINK: LIT("{"); // error? } } else { LIT("}"); } break; case CMARK_NODE_IMAGE: if (entering) { LIT("\\protect\\includegraphics{"); // requires \include{graphicx} OUT(cmark_node_get_url(node), false, URL); LIT("}"); return 0; } break; default: assert(false); break; } return 1; }
static int S_render_node(cmark_renderer *renderer, cmark_node *node, cmark_event_type ev_type, int options) { int list_number; int enumlevel; char list_number_string[LIST_NUMBER_STRING_SIZE]; bool entering = (ev_type == CMARK_EVENT_ENTER); cmark_list_type list_type; bool allow_wrap = renderer->width > 0 && !(CMARK_OPT_NOBREAKS & options); // avoid warning about unused parameter: (void)(options); switch (node->type) { case CMARK_NODE_DOCUMENT: break; case CMARK_NODE_BLOCK_QUOTE: if (entering) { LIT("\\begin{quote}"); CR(); } else { LIT("\\end{quote}"); BLANKLINE(); } break; case CMARK_NODE_LIST: list_type = cmark_node_get_list_type(node); if (entering) { LIT("\\begin{"); LIT(list_type == CMARK_ORDERED_LIST ? "enumerate" : "itemize"); LIT("}"); CR(); list_number = cmark_node_get_list_start(node); if (list_number > 1) { enumlevel = S_get_enumlevel(node); // latex normally supports only five levels if (enumlevel >= 1 && enumlevel <= 5) { snprintf(list_number_string, LIST_NUMBER_STRING_SIZE, "%d", list_number); LIT("\\setcounter{enum"); switch(enumlevel) { case 1: LIT("i"); break; case 2: LIT("ii"); break; case 3: LIT("iii"); break; case 4: LIT("iv"); break; case 5: LIT("v"); break; default: LIT("i"); break; } LIT("}{"); OUT(list_number_string, false, NORMAL); LIT("}"); } CR(); } } else { LIT("\\end{"); LIT(list_type == CMARK_ORDERED_LIST ? "enumerate" : "itemize"); LIT("}"); BLANKLINE(); } break; case CMARK_NODE_ITEM: if (entering) { LIT("\\item "); } else { CR(); } break; case CMARK_NODE_HEADING: if (entering) { switch (cmark_node_get_heading_level(node)) { case 1: LIT("\\section"); break; case 2: LIT("\\subsection"); break; case 3: LIT("\\subsubsection"); break; case 4: LIT("\\paragraph"); break; case 5: LIT("\\subparagraph"); break; } LIT("{"); } else { LIT("}"); BLANKLINE(); } break; case CMARK_NODE_CODE_BLOCK: CR(); LIT("\\begin{verbatim}"); CR(); OUT(cmark_node_get_literal(node), false, LITERAL); CR(); LIT("\\end{verbatim}"); BLANKLINE(); break; case CMARK_NODE_HTML_BLOCK: break; case CMARK_NODE_CUSTOM_BLOCK: CR(); OUT(entering ? cmark_node_get_on_enter(node) : cmark_node_get_on_exit(node), false, LITERAL); CR(); break; case CMARK_NODE_THEMATIC_BREAK: BLANKLINE(); LIT("\\begin{center}\\rule{0.5\\linewidth}{\\linethickness}\\end{center}"); BLANKLINE(); break; case CMARK_NODE_PARAGRAPH: if (!entering) { BLANKLINE(); } break; case CMARK_NODE_TEXT: OUT(cmark_node_get_literal(node), allow_wrap, NORMAL); break; case CMARK_NODE_LINEBREAK: LIT("\\\\"); CR(); break; case CMARK_NODE_SOFTBREAK: if (options & CMARK_OPT_HARDBREAKS) { LIT("\\\\"); CR(); } else if (renderer->width == 0 && !(CMARK_OPT_NOBREAKS & options)) { CR(); } else { OUT(" ", allow_wrap, NORMAL); } break; case CMARK_NODE_CODE: LIT("\\texttt{"); OUT(cmark_node_get_literal(node), false, NORMAL); LIT("}"); break; case CMARK_NODE_HTML_INLINE: break; case CMARK_NODE_CUSTOM_INLINE: OUT(entering ? cmark_node_get_on_enter(node) : cmark_node_get_on_exit(node), false, LITERAL); break; case CMARK_NODE_STRONG: if (entering) { LIT("\\textbf{"); } else { LIT("}"); } break; case CMARK_NODE_EMPH: if (entering) { LIT("\\emph{"); } else { LIT("}"); } break; case CMARK_NODE_LINK: if (entering) { const char *url = cmark_node_get_url(node); // requires \usepackage{hyperref} switch (get_link_type(node)) { case URL_AUTOLINK: LIT("\\url{"); OUT(url, false, URL); LIT("}"); return 0; // Don't process further nodes to avoid double-rendering artefacts case EMAIL_AUTOLINK: LIT("\\href{"); OUT(url, false, URL); LIT("}\\nolinkurl{"); break; case NORMAL_LINK: LIT("\\href{"); OUT(url, false, URL); LIT("}{"); break; case INTERNAL_LINK: LIT("\\protect\\hyperlink{"); OUT(url + 1, false, URL); LIT("}{"); break; case NO_LINK: LIT("{"); // error? } } else { LIT("}"); } break; case CMARK_NODE_IMAGE: if (entering) { LIT("\\protect\\includegraphics{"); // requires \include{graphicx} OUT(cmark_node_get_url(node), false, URL); LIT("}"); return 0; } break; default: assert(false); break; } return 1; }
void nl_bridge::update_vlans(rtnl_link *old_link, rtnl_link *new_link) { assert(sw); assert(bridge); // already checked rtnl_link_bridge_vlan *old_br_vlan, *new_br_vlan; rtnl_link *_link; if (old_link == nullptr) { // link added old_br_vlan = &empty_br_vlan; new_br_vlan = rtnl_link_bridge_get_port_vlan(new_link); _link = nl->get_link(rtnl_link_get_ifindex(new_link), AF_UNSPEC); } else if (new_link == nullptr) { // link deleted old_br_vlan = rtnl_link_bridge_get_port_vlan(old_link); new_br_vlan = &empty_br_vlan; _link = nl->get_link(rtnl_link_get_ifindex(old_link), AF_UNSPEC); } else { // link updated old_br_vlan = rtnl_link_bridge_get_port_vlan(old_link); new_br_vlan = rtnl_link_bridge_get_port_vlan(new_link); _link = nl->get_link(rtnl_link_get_ifindex(new_link), AF_UNSPEC); } if (old_br_vlan == nullptr) { old_br_vlan = &empty_br_vlan; } if (new_br_vlan == nullptr) { new_br_vlan = &empty_br_vlan; } if (_link == nullptr) { // XXX FIXME in case a vxlan has been deleted the vxlan_domain and // vxlan_dom_bitmap need an update, maybe this can be handled already from // the link_deleted of the vxlan itself? LOG(WARNING) << __FUNCTION__ << ": could not get parent link of bridge interface. This " "case needs further checks if everything got already " "deleted."; return; } // check for vid changes if (br_vlan_equal(old_br_vlan, new_br_vlan)) { VLOG(2) << __FUNCTION__ << ": vlans did not change"; return; } link_type lt = get_link_type(_link); uint32_t pport_no = 0; uint32_t tunnel_id = -1; std::deque<rtnl_link *> bridge_ports; if (lt == LT_VXLAN) { assert(nl); nl->get_bridge_ports(rtnl_link_get_master(_link), &bridge_ports); if (vxlan->get_tunnel_id(_link, nullptr, &tunnel_id) != 0) { LOG(ERROR) << __FUNCTION__ << ": failed to get vni of link " << OBJ_CAST(_link); } } else { pport_no = nl->get_port_id(rtnl_link_get_ifindex(_link)); if (pport_no == 0) { LOG(ERROR) << __FUNCTION__ << ": invalid pport_no=0 of link: " << OBJ_CAST(_link); return; } } for (int k = 0; k < RTNL_LINK_BRIDGE_VLAN_BITMAP_LEN; k++) { int base_bit; uint32_t a = old_br_vlan->vlan_bitmap[k]; uint32_t b = new_br_vlan->vlan_bitmap[k]; uint32_t vlan_diff = a ^ b; #if 0 // untagged change not yet implemented uint32_t c = old_br_vlan->untagged_bitmap[k]; uint32_t d = new_br_vlan->untagged_bitmap[k]; uint32_t untagged_diff = c ^ d; #endif // 0 base_bit = k * 32; int i = -1; int done = 0; while (!done) { int j = find_next_bit(i, vlan_diff); if (j > 0) { // vlan added or removed int vid = j - 1 + base_bit; bool egress_untagged = false; // check if egress is untagged if (new_br_vlan->untagged_bitmap[k] & 1 << (j - 1)) { egress_untagged = true; #if 0 // untagged change not yet implemented // clear untagged_diff bit untagged_diff &= ~((uint32_t)1 << (j - 1)); #endif // 0 } if (new_br_vlan->vlan_bitmap[k] & 1 << (j - 1)) { // vlan added if (lt == LT_VXLAN) { // update vxlan domain if (!is_vid_set(vid, vxlan_dom_bitmap)) { VLOG(1) << __FUNCTION__ << ": new vxlan domain vid=" << vid << ", tunnel_id=" << tunnel_id; vxlan_domain.emplace(vid, tunnel_id); set_vid(vid, vxlan_dom_bitmap); } else { // XXX TODO check the map } // update all bridge ports to be access ports update_access_ports(_link, new_link ? new_link : old_link, vid, tunnel_id, bridge_ports, true); } else { assert(pport_no); if (is_vid_set(vid, vxlan_dom_bitmap)) { // configure as access port std::string port_name = std::string(rtnl_link_get_name(_link)); auto vxd_it = vxlan_domain.find(vid); if (vxd_it != vxlan_domain.end()) { vxlan->create_access_port((new_link) ? new_link : old_link, vxd_it->second, port_name, pport_no, vid, egress_untagged, nullptr); } else { LOG(FATAL) << __FUNCTION__ << ": should not happen, something is broken"; } } else { // normal vlan port VLOG(3) << __FUNCTION__ << ": add vid=" << vid << " on pport_no=" << pport_no << " link: " << OBJ_CAST(_link); sw->egress_bridge_port_vlan_add(pport_no, vid, egress_untagged); sw->ingress_port_vlan_add(pport_no, vid, new_br_vlan->pvid == vid); } } } else { // vlan removed if (lt == LT_VXLAN) { unset_vid(vid, vxlan_dom_bitmap); vxlan_domain.erase(vid); // update all bridge ports to be normal bridge ports update_access_ports(_link, new_link ? new_link : old_link, vid, tunnel_id, bridge_ports, false); } else { VLOG(3) << __FUNCTION__ << ": remove vid=" << vid << " on pport_no=" << pport_no << " link: " << OBJ_CAST(_link); sw->ingress_port_vlan_remove(pport_no, vid, old_br_vlan->pvid == vid); // delete all FM pointing to this group first sw->l2_addr_remove_all_in_vlan(pport_no, vid); std::unique_ptr<rtnl_neigh, decltype(&rtnl_neigh_put)> filter( rtnl_neigh_alloc(), rtnl_neigh_put); rtnl_neigh_set_ifindex(filter.get(), rtnl_link_get_ifindex(bridge)); rtnl_neigh_set_master(filter.get(), rtnl_link_get_master(bridge)); rtnl_neigh_set_family(filter.get(), AF_BRIDGE); rtnl_neigh_set_vlan(filter.get(), vid); rtnl_neigh_set_flags(filter.get(), NTF_MASTER | NTF_EXT_LEARNED); rtnl_neigh_set_state(filter.get(), NUD_REACHABLE); nl_cache_foreach_filter(l2_cache.get(), OBJ_CAST(filter.get()), [](struct nl_object *o, void *arg) { VLOG(3) << "l2_cache remove object " << o; nl_cache_remove(o); }, nullptr); sw->egress_bridge_port_vlan_remove(pport_no, vid); } } i = j; } else { done = 1; } } #if 0 // not yet implemented the update done = 0; i = -1; while (!done) { // vlan is existing, but swapping egress tagged/untagged int j = find_next_bit(i, untagged_diff); if (j > 0) { // egress untagged changed int vid = j - 1 + base_bit; bool egress_untagged = false; // check if egress is untagged if (new_br_vlan->untagged_bitmap[k] & 1 << (j-1)) { egress_untagged = true; } // XXX implement update fm_driver.update_port_vid_egress(devname, vid, egress_untagged); i = j; } else { done = 1; } } #endif } }