public inbox for gcc-cvs@sourceware.org help / color / mirror / Atom feed
From: David Malcolm <dmalcolm@gcc.gnu.org> To: gcc-cvs@gcc.gnu.org Subject: [gcc r13-1563] Convert label_text to C++11 move semantics Date: Thu, 7 Jul 2022 19:56:31 +0000 (GMT) [thread overview] Message-ID: <20220707195632.0016E3853551@sourceware.org> (raw) https://gcc.gnu.org/g:a8dce13c076019688f6d6aaaa338a2911022b336 commit r13-1563-ga8dce13c076019688f6d6aaaa338a2911022b336 Author: David Malcolm <dmalcolm@redhat.com> Date: Thu Jul 7 15:50:26 2022 -0400 Convert label_text to C++11 move semantics libcpp's class label_text stores a char * for a string and a flag saying whether it owns the buffer. I added this class before we could use C++11, and so to avoid lots of copying it required an explicit call to label_text::maybe_free to potentially free the buffer. Now that we can use C++11, this patch removes label_text::maybe_free in favor of doing the cleanup in the destructor, and using C++ move semantics to avoid any copying. This allows lots of messy cleanup code to be eliminated in favor of implicit destruction (mostly in the analyzer). No functional change intended. gcc/analyzer/ChangeLog: * call-info.cc (call_info::print): Update for removal of label_text::maybe_free in favor of automatic memory management. * checker-path.cc (checker_event::dump): Likewise. (checker_event::prepare_for_emission): Likewise. (state_change_event::get_desc): Likewise. (superedge_event::should_filter_p): Likewise. (start_cfg_edge_event::get_desc): Likewise. (warning_event::get_desc): Likewise. (checker_path::dump): Likewise. (checker_path::debug): Likewise. * diagnostic-manager.cc (diagnostic_manager::prune_for_sm_diagnostic): Likewise. (diagnostic_manager::prune_interproc_events): Likewise. * program-state.cc (sm_state_map::to_json): Likewise. * region.cc (region::to_json): Likewise. * sm-malloc.cc (inform_nonnull_attribute): Likewise. * store.cc (binding_map::to_json): Likewise. (store::to_json): Likewise. * svalue.cc (svalue::to_json): Likewise. gcc/c-family/ChangeLog: * c-format.cc (range_label_for_format_type_mismatch::get_text): Update for removal of label_text::maybe_free in favor of automatic memory management. gcc/ChangeLog: * diagnostic-format-json.cc (json_from_location_range): Update for removal of label_text::maybe_free in favor of automatic memory management. * diagnostic-format-sarif.cc (sarif_builder::make_location_object): Likewise. * diagnostic-show-locus.cc (struct pod_label_text): New. (class line_label): Convert m_text from label_text to pod_label_text. (layout::print_any_labels): Move "text" to the line_label. * tree-diagnostic-path.cc (path_label::get_text): Update for removal of label_text::maybe_free in favor of automatic memory management. (event_range::print): Likewise. (default_tree_diagnostic_path_printer): Likewise. (default_tree_make_json_for_path): Likewise. libcpp/ChangeLog: * include/line-map.h: Include <utility>. (class label_text): Delete maybe_free method in favor of a destructor. Add move ctor and assignment operator. Add deletion of the copy ctor and copy-assignment operator. Rename field m_caller_owned to m_owned. Add std::move where necessary; add moved_from member function. Signed-off-by: David Malcolm <dmalcolm@redhat.com> Diff: --- gcc/analyzer/call-info.cc | 1 - gcc/analyzer/checker-path.cc | 97 +++++++++++++------------------------- gcc/analyzer/diagnostic-manager.cc | 8 ---- gcc/analyzer/program-state.cc | 1 - gcc/analyzer/region.cc | 1 - gcc/analyzer/sm-malloc.cc | 3 -- gcc/analyzer/store.cc | 3 -- gcc/analyzer/svalue.cc | 1 - gcc/c-family/c-format.cc | 1 - gcc/diagnostic-format-json.cc | 4 +- gcc/diagnostic-format-sarif.cc | 1 - gcc/diagnostic-show-locus.cc | 35 ++++++++++++-- gcc/tree-diagnostic-path.cc | 4 -- libcpp/include/line-map.h | 46 ++++++++++++++---- 14 files changed, 101 insertions(+), 105 deletions(-) diff --git a/gcc/analyzer/call-info.cc b/gcc/analyzer/call-info.cc index b3ff51e7460..e1142d743a3 100644 --- a/gcc/analyzer/call-info.cc +++ b/gcc/analyzer/call-info.cc @@ -76,7 +76,6 @@ call_info::print (pretty_printer *pp) const { label_text desc (get_desc (pp_show_color (pp))); pp_string (pp, desc.m_buffer); - desc.maybe_free (); } /* Implementation of custom_edge_info::add_events_to_path vfunc for diff --git a/gcc/analyzer/checker-path.cc b/gcc/analyzer/checker-path.cc index 953e192cd55..959ffdd853c 100644 --- a/gcc/analyzer/checker-path.cc +++ b/gcc/analyzer/checker-path.cc @@ -196,7 +196,6 @@ checker_event::dump (pretty_printer *pp) const label_text event_desc (get_desc (false)); pp_printf (pp, "\"%s\" (depth %i", event_desc.m_buffer, m_effective_depth); - event_desc.maybe_free (); if (m_effective_depth != m_original_depth) pp_printf (pp, " corrected from %i", @@ -235,7 +234,6 @@ checker_event::prepare_for_emission (checker_path *, m_emission_id = emission_id; label_text desc = get_desc (false); - desc.maybe_free (); } /* class debug_event : public checker_event. */ @@ -402,9 +400,8 @@ state_change_event::get_desc (bool can_colorize) const meaning.dump_to_pp (&meaning_pp); /* Append debug version. */ - label_text result; if (m_origin) - result = make_label_text + return make_label_text (can_colorize, "%s (state of %qE: %qs -> %qs, origin: %qE, meaning: %s)", custom_desc.m_buffer, @@ -414,7 +411,7 @@ state_change_event::get_desc (bool can_colorize) const origin, pp_formatted_text (&meaning_pp)); else - result = make_label_text + return make_label_text (can_colorize, "%s (state of %qE: %qs -> %qs, NULL origin, meaning: %s)", custom_desc.m_buffer, @@ -422,9 +419,6 @@ state_change_event::get_desc (bool can_colorize) const m_from->get_name (), m_to->get_name (), pp_formatted_text (&meaning_pp)); - - custom_desc.maybe_free (); - return result; } else return custom_desc; @@ -435,28 +429,24 @@ state_change_event::get_desc (bool can_colorize) const if (m_sval) { label_text sval_desc = m_sval->get_desc (); - label_text result; if (m_origin) { label_text origin_desc = m_origin->get_desc (); - result = make_label_text + return make_label_text (can_colorize, "state of %qs: %qs -> %qs (origin: %qs)", sval_desc.m_buffer, m_from->get_name (), m_to->get_name (), origin_desc.m_buffer); - origin_desc.maybe_free (); } else - result = make_label_text + return make_label_text (can_colorize, "state of %qs: %qs -> %qs (NULL origin)", sval_desc.m_buffer, m_from->get_name (), m_to->get_name ()); - sval_desc.maybe_free (); - return result; } else { @@ -522,7 +512,6 @@ superedge_event::should_filter_p (int verbosity) const gcc_assert (desc.m_buffer); if (desc.m_buffer[0] == '\0') return true; - desc.maybe_free (); } } break; @@ -605,56 +594,39 @@ label_text start_cfg_edge_event::get_desc (bool can_colorize) const { bool user_facing = !flag_analyzer_verbose_edges; - char *edge_desc = m_sedge->get_description (user_facing); + label_text edge_desc + = label_text::take (m_sedge->get_description (user_facing)); if (user_facing) { - if (edge_desc && strlen (edge_desc) > 0) + if (edge_desc.m_buffer && strlen (edge_desc.m_buffer) > 0) { label_text cond_desc = maybe_describe_condition (can_colorize); label_text result; if (cond_desc.m_buffer) - { - result = make_label_text (can_colorize, - "following %qs branch (%s)...", - edge_desc, cond_desc.m_buffer); - cond_desc.maybe_free (); - } + return make_label_text (can_colorize, + "following %qs branch (%s)...", + edge_desc.m_buffer, cond_desc.m_buffer); else - { - result = make_label_text (can_colorize, - "following %qs branch...", - edge_desc); - } - free (edge_desc); - return result; + return make_label_text (can_colorize, + "following %qs branch...", + edge_desc.m_buffer); } else - { - free (edge_desc); - return label_text::borrow (""); - } + return label_text::borrow (""); } else { - if (strlen (edge_desc) > 0) - { - label_text result - = make_label_text (can_colorize, - "taking %qs edge SN:%i -> SN:%i", - edge_desc, - m_sedge->m_src->m_index, - m_sedge->m_dest->m_index); - free (edge_desc); - return result; - } + if (strlen (edge_desc.m_buffer) > 0) + return make_label_text (can_colorize, + "taking %qs edge SN:%i -> SN:%i", + edge_desc.m_buffer, + m_sedge->m_src->m_index, + m_sedge->m_dest->m_index); else - { - free (edge_desc); - return make_label_text (can_colorize, - "taking edge SN:%i -> SN:%i", - m_sedge->m_src->m_index, - m_sedge->m_dest->m_index); - } + return make_label_text (can_colorize, + "taking edge SN:%i -> SN:%i", + m_sedge->m_src->m_index, + m_sedge->m_dest->m_index); } } @@ -1138,19 +1110,16 @@ warning_event::get_desc (bool can_colorize) const { if (m_sm && flag_analyzer_verbose_state_changes) { - label_text result; if (var) - result = make_label_text (can_colorize, - "%s (%qE is in state %qs)", - ev_desc.m_buffer, - var, m_state->get_name ()); + return make_label_text (can_colorize, + "%s (%qE is in state %qs)", + ev_desc.m_buffer, + var, m_state->get_name ()); else - result = make_label_text (can_colorize, - "%s (in global state %qs)", - ev_desc.m_buffer, - m_state->get_name ()); - ev_desc.maybe_free (); - return result; + return make_label_text (can_colorize, + "%s (in global state %qs)", + ev_desc.m_buffer, + m_state->get_name ()); } else return ev_desc; @@ -1196,7 +1165,6 @@ checker_path::dump (pretty_printer *pp) const pp_string (pp, ", "); label_text event_desc (e->get_desc (false)); pp_printf (pp, "\"%s\"", event_desc.m_buffer); - event_desc.maybe_free (); } pp_character (pp, ']'); } @@ -1237,7 +1205,6 @@ checker_path::debug () const i, event_kind_to_string (m_events[i]->m_kind), event_desc.m_buffer); - event_desc.maybe_free (); } } diff --git a/gcc/analyzer/diagnostic-manager.cc b/gcc/analyzer/diagnostic-manager.cc index 4adfda1af65..083f66bd739 100644 --- a/gcc/analyzer/diagnostic-manager.cc +++ b/gcc/analyzer/diagnostic-manager.cc @@ -2298,7 +2298,6 @@ diagnostic_manager::prune_for_sm_diagnostic (checker_path *path, log ("considering event %i (%s), with sval: %qs, state: %qs", idx, event_kind_to_string (base_event->m_kind), sval_desc.m_buffer, state->get_name ()); - sval_desc.maybe_free (); } else log ("considering event %i (%s), with global state: %qs", @@ -2366,8 +2365,6 @@ diagnostic_manager::prune_for_sm_diagnostic (checker_path *path, " switching var of interest from %qs to %qs", idx, sval_desc.m_buffer, origin_sval_desc.m_buffer); - sval_desc.maybe_free (); - origin_sval_desc.maybe_free (); } sval = state_change->m_origin; } @@ -2395,7 +2392,6 @@ diagnostic_manager::prune_for_sm_diagnostic (checker_path *path, else log ("filtering event %i: state change to %qs", idx, change_sval_desc.m_buffer); - change_sval_desc.maybe_free (); } else log ("filtering event %i: global state change", idx); @@ -2465,7 +2461,6 @@ diagnostic_manager::prune_for_sm_diagnostic (checker_path *path, " recording critical state for %qs at call" " from %qE in callee to %qE in caller", idx, sval_desc.m_buffer, callee_var, caller_var); - sval_desc.maybe_free (); } if (expr.param_p ()) event->record_critical_state (caller_var, state); @@ -2509,7 +2504,6 @@ diagnostic_manager::prune_for_sm_diagnostic (checker_path *path, " recording critical state for %qs at return" " from %qE in caller to %qE in callee", idx, sval_desc.m_buffer, callee_var, callee_var); - sval_desc.maybe_free (); } if (expr.return_value_p ()) event->record_critical_state (callee_var, state); @@ -2593,7 +2587,6 @@ diagnostic_manager::prune_interproc_events (checker_path *path) const log ("filtering events %i-%i:" " irrelevant call/entry/return: %s", idx, idx + 2, desc.m_buffer); - desc.maybe_free (); } path->delete_event (idx + 2); path->delete_event (idx + 1); @@ -2616,7 +2609,6 @@ diagnostic_manager::prune_interproc_events (checker_path *path) const log ("filtering events %i-%i:" " irrelevant call/return: %s", idx, idx + 1, desc.m_buffer); - desc.maybe_free (); } path->delete_event (idx + 1); path->delete_event (idx); diff --git a/gcc/analyzer/program-state.cc b/gcc/analyzer/program-state.cc index 295c6aeb185..90a56e3fba4 100644 --- a/gcc/analyzer/program-state.cc +++ b/gcc/analyzer/program-state.cc @@ -301,7 +301,6 @@ sm_state_map::to_json () const label_text sval_desc = sval->get_desc (); map_obj->set (sval_desc.m_buffer, e.m_state->to_json ()); - sval_desc.maybe_free (); /* This doesn't yet JSONify e.m_origin. */ } diff --git a/gcc/analyzer/region.cc b/gcc/analyzer/region.cc index a8286231d30..5b00e6a5f46 100644 --- a/gcc/analyzer/region.cc +++ b/gcc/analyzer/region.cc @@ -590,7 +590,6 @@ region::to_json () const { label_text desc = get_desc (true); json::value *reg_js = new json::string (desc.m_buffer); - desc.maybe_free (); return reg_js; } diff --git a/gcc/analyzer/sm-malloc.cc b/gcc/analyzer/sm-malloc.cc index 3bd40425919..553fcd80085 100644 --- a/gcc/analyzer/sm-malloc.cc +++ b/gcc/analyzer/sm-malloc.cc @@ -1008,7 +1008,6 @@ inform_nonnull_attribute (tree fndecl, int arg_idx) inform (DECL_SOURCE_LOCATION (fndecl), "argument %s of %qD must be non-null", arg_desc.m_buffer, fndecl); - arg_desc.maybe_free (); /* Ideally we would use the location of the parm and underline the attribute also - but we don't have the location_t values at this point in the middle-end. @@ -1072,7 +1071,6 @@ public: result = ev.formatted_print ("argument %s (%qE) could be NULL" " where non-null expected", arg_desc.m_buffer, ev.m_expr); - arg_desc.maybe_free (); return result; } @@ -1180,7 +1178,6 @@ public: result = ev.formatted_print ("argument %s (%qE) NULL" " where non-null expected", arg_desc.m_buffer, ev.m_expr); - arg_desc.maybe_free (); return result; } diff --git a/gcc/analyzer/store.cc b/gcc/analyzer/store.cc index 1b7c818051b..d558d477115 100644 --- a/gcc/analyzer/store.cc +++ b/gcc/analyzer/store.cc @@ -676,7 +676,6 @@ binding_map::to_json () const const svalue *value = *const_cast <map_t &> (m_map).get (key); label_text key_desc = key->get_desc (); map_obj->set (key_desc.m_buffer, value->to_json ()); - key_desc.maybe_free (); } return map_obj; @@ -2405,11 +2404,9 @@ store::to_json () const label_text base_reg_desc = base_reg->get_desc (); clusters_in_parent_reg_obj->set (base_reg_desc.m_buffer, cluster->to_json ()); - base_reg_desc.maybe_free (); } label_text parent_reg_desc = parent_reg->get_desc (); store_obj->set (parent_reg_desc.m_buffer, clusters_in_parent_reg_obj); - parent_reg_desc.maybe_free (); } store_obj->set ("called_unknown_fn", new json::literal (m_called_unknown_fn)); diff --git a/gcc/analyzer/svalue.cc b/gcc/analyzer/svalue.cc index 7bad3cea31b..78a6eeff05f 100644 --- a/gcc/analyzer/svalue.cc +++ b/gcc/analyzer/svalue.cc @@ -97,7 +97,6 @@ svalue::to_json () const { label_text desc = get_desc (true); json::value *sval_js = new json::string (desc.m_buffer); - desc.maybe_free (); return sval_js; } diff --git a/gcc/c-family/c-format.cc b/gcc/c-family/c-format.cc index 754780446ba..2faed0c1607 100644 --- a/gcc/c-family/c-format.cc +++ b/gcc/c-family/c-format.cc @@ -4625,7 +4625,6 @@ class range_label_for_format_type_mismatch suffix.fill_buffer (p); char *result = concat (text.m_buffer, p, NULL); - text.maybe_free (); return label_text::take (result); } diff --git a/gcc/diagnostic-format-json.cc b/gcc/diagnostic-format-json.cc index d1d8d3f2081..872c67e53ed 100644 --- a/gcc/diagnostic-format-json.cc +++ b/gcc/diagnostic-format-json.cc @@ -101,11 +101,9 @@ json_from_location_range (diagnostic_context *context, if (loc_range->m_label) { - label_text text; - text = loc_range->m_label->get_text (range_idx); + label_text text (loc_range->m_label->get_text (range_idx)); if (text.m_buffer) result->set ("label", new json::string (text.m_buffer)); - text.maybe_free (); } return result; diff --git a/gcc/diagnostic-format-sarif.cc b/gcc/diagnostic-format-sarif.cc index a7bb9fb639d..1e4ebc8ad38 100644 --- a/gcc/diagnostic-format-sarif.cc +++ b/gcc/diagnostic-format-sarif.cc @@ -584,7 +584,6 @@ sarif_builder::make_location_object (const diagnostic_event &event) label_text ev_desc = event.get_desc (false); json::object *message_obj = make_message_object (ev_desc.m_buffer); location_obj->set ("message", message_obj); - ev_desc.maybe_free (); return location_obj; } diff --git a/gcc/diagnostic-show-locus.cc b/gcc/diagnostic-show-locus.cc index 6eafe19785f..9cd7794d077 100644 --- a/gcc/diagnostic-show-locus.cc +++ b/gcc/diagnostic-show-locus.cc @@ -1867,6 +1867,31 @@ layout::print_annotation_line (linenum_type row, const line_bounds lbounds) print_newline (); } +/* A version of label_text that can live inside a vec. + Requires manual cleanup via maybe_free. */ + +struct pod_label_text +{ + pod_label_text () + : m_buffer (NULL), m_caller_owned (false) + {} + + pod_label_text (label_text &&other) + : m_buffer (other.m_buffer), m_caller_owned (other.m_owned) + { + other.moved_from (); + } + + void maybe_free () + { + if (m_caller_owned) + free (m_buffer); + } + + char *m_buffer; + bool m_caller_owned; +}; + /* Implementation detail of layout::print_any_labels. A label within the given row of source. */ @@ -1878,10 +1903,10 @@ public: int state_idx, int column, label_text text) : m_state_idx (state_idx), m_column (column), - m_text (text), m_label_line (0), m_has_vbar (true) + m_text (std::move (text)), m_label_line (0), m_has_vbar (true) { - const int bytes = strlen (text.m_buffer); - m_display_width = cpp_display_width (text.m_buffer, bytes, policy); + const int bytes = strlen (m_text.m_buffer); + m_display_width = cpp_display_width (m_text.m_buffer, bytes, policy); } /* Sorting is primarily by column, then by state index. */ @@ -1900,7 +1925,7 @@ public: int m_state_idx; int m_column; - label_text m_text; + pod_label_text m_text; size_t m_display_width; int m_label_line; bool m_has_vbar; @@ -1941,7 +1966,7 @@ layout::print_any_labels (linenum_type row) if (text.m_buffer == NULL) continue; - labels.safe_push (line_label (m_policy, i, disp_col, text)); + labels.safe_push (line_label (m_policy, i, disp_col, std::move (text))); } } diff --git a/gcc/tree-diagnostic-path.cc b/gcc/tree-diagnostic-path.cc index ae2f8a2d262..2f297faed34 100644 --- a/gcc/tree-diagnostic-path.cc +++ b/gcc/tree-diagnostic-path.cc @@ -66,7 +66,6 @@ class path_label : public range_label pp_show_color (&pp) = pp_show_color (global_dc->printer); diagnostic_event_id_t event_id (event_idx); pp_printf (&pp, "%@ %s", &event_id, event_text.m_buffer); - event_text.maybe_free (); label_text result = label_text::take (xstrdup (pp_formatted_text (&pp))); return result; } @@ -176,7 +175,6 @@ struct event_range pretty_printer *pp = dc->printer; pp_printf (pp, " %@: %s", &event_id, event_text.m_buffer); pp_newline (pp); - event_text.maybe_free (); } return; } @@ -484,7 +482,6 @@ default_tree_diagnostic_path_printer (diagnostic_context *context, else inform (event.get_location (), "%@ %s", &event_id, event_text.m_buffer); - event_text.maybe_free (); } } break; @@ -523,7 +520,6 @@ default_tree_make_json_for_path (diagnostic_context *context, event.get_location ())); label_text event_text (event.get_desc (false)); event_obj->set ("description", new json::string (event_text.m_buffer)); - event_text.maybe_free (); if (tree fndecl = event.get_fndecl ()) { const char *function diff --git a/libcpp/include/line-map.h b/libcpp/include/line-map.h index 80335721e03..c6379ce25b8 100644 --- a/libcpp/include/line-map.h +++ b/libcpp/include/line-map.h @@ -22,6 +22,8 @@ along with this program; see the file COPYING3. If not see #ifndef LIBCPP_LINE_MAP_H #define LIBCPP_LINE_MAP_H +#include <utility> + #ifndef GTY #define GTY(x) /* nothing */ #endif @@ -1836,43 +1838,71 @@ class label_text { public: label_text () - : m_buffer (NULL), m_caller_owned (false) + : m_buffer (NULL), m_owned (false) {} - void maybe_free () + ~label_text () + { + if (m_owned) + free (m_buffer); + } + + /* Move ctor. */ + label_text (label_text &&other) + : m_buffer (other.m_buffer), m_owned (other.m_owned) + { + other.moved_from (); + } + + /* Move assignment. */ + label_text & operator= (label_text &&other) { - if (m_caller_owned) + if (m_owned) free (m_buffer); + m_buffer = other.m_buffer; + m_owned = other.m_owned; + other.moved_from (); + return *this; } + /* Delete the copy ctor and copy-assignment operator. */ + label_text (const label_text &) = delete; + label_text & operator= (const label_text &) = delete; + /* Create a label_text instance that borrows BUFFER from a longer-lived owner. */ static label_text borrow (const char *buffer) { - return label_text (const_cast <char *> (buffer), false); + return std::move (label_text (const_cast <char *> (buffer), false)); } /* Create a label_text instance that takes ownership of BUFFER. */ static label_text take (char *buffer) { - return label_text (buffer, true); + return std::move (label_text (buffer, true)); } /* Take ownership of the buffer, copying if necessary. */ char *take_or_copy () { - if (m_caller_owned) + if (m_owned) return m_buffer; else return xstrdup (m_buffer); } + void moved_from () + { + m_buffer = NULL; + m_owned = false; + } + char *m_buffer; - bool m_caller_owned; + bool m_owned; private: label_text (char *buffer, bool owned) - : m_buffer (buffer), m_caller_owned (owned) + : m_buffer (buffer), m_owned (owned) {} };
reply other threads:[~2022-07-07 19:56 UTC|newest] Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=20220707195632.0016E3853551@sourceware.org \ --to=dmalcolm@gcc.gnu.org \ --cc=gcc-cvs@gcc.gnu.org \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: linkBe sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).