+ free(string);
+}
+
+/* Parses 's' as a set of OpenFlow actions and appends the actions to
+ * 'actions'.
+ *
+ * Prints an error on stderr and aborts the program if 's' syntax is
+ * invalid. */
+void
+parse_ofpacts(const char *s_, struct ofpbuf *ofpacts)
+{
+ char *s = xstrdup(s_);
+ str_to_ofpacts(NULL, s, ofpacts);
+ free(s);
+}
+
+/* Parses 'string' as an OFPT_FLOW_MOD or NXT_FLOW_MOD with command 'command'
+ * (one of OFPFC_*) into 'fm'. */
+void
+parse_ofp_flow_mod_str(struct ofputil_flow_mod *fm, const char *string,
+ uint16_t command, bool verbose)
+{
+ struct cls_rule rule_copy;
+
+ parse_ofp_str(fm, command, string, verbose);
+
+ /* Normalize a copy of the rule. This ensures that non-normalized flows
+ * get logged but doesn't affect what gets sent to the switch, so that the
+ * switch can do whatever it likes with the flow. */
+ rule_copy = fm->cr;
+ ofputil_normalize_rule(&rule_copy);
+}
+
+void
+parse_ofp_flow_mod_file(const char *file_name, uint16_t command,
+ struct ofputil_flow_mod **fms, size_t *n_fms)
+{
+ size_t allocated_fms;
+ FILE *stream;
+ struct ds s;
+
+ stream = !strcmp(file_name, "-") ? stdin : fopen(file_name, "r");
+ if (stream == NULL) {
+ ovs_fatal(errno, "%s: open", file_name);
+ }
+
+ allocated_fms = *n_fms;
+ ds_init(&s);
+ while (!ds_get_preprocessed_line(&s, stream)) {
+ if (*n_fms >= allocated_fms) {
+ *fms = x2nrealloc(*fms, &allocated_fms, sizeof **fms);
+ }
+ parse_ofp_flow_mod_str(&(*fms)[*n_fms], ds_cstr(&s), command, false);
+ *n_fms += 1;
+ }
+ ds_destroy(&s);
+
+ if (stream != stdin) {
+ fclose(stream);
+ }
+}
+
+void
+parse_ofp_flow_stats_request_str(struct ofputil_flow_stats_request *fsr,
+ bool aggregate, const char *string)
+{
+ struct ofputil_flow_mod fm;
+
+ parse_ofp_str(&fm, -1, string, false);
+ fsr->aggregate = aggregate;
+ fsr->cookie = fm.cookie;
+ fsr->cookie_mask = fm.cookie_mask;
+ fsr->match = fm.cr;
+ fsr->out_port = fm.out_port;
+ fsr->table_id = fm.table_id;
+}
+
+/* Parses a specification of a flow from 's' into 'flow'. 's' must take the
+ * form FIELD=VALUE[,FIELD=VALUE]... where each FIELD is the name of a
+ * mf_field. Fields must be specified in a natural order for satisfying
+ * prerequisites.
+ *
+ * Returns NULL on success, otherwise a malloc()'d string that explains the
+ * problem. */
+char *
+parse_ofp_exact_flow(struct flow *flow, const char *s)
+{
+ char *pos, *key, *value_s;
+ char *error = NULL;
+ char *copy;
+
+ memset(flow, 0, sizeof *flow);
+
+ pos = copy = xstrdup(s);
+ while (ofputil_parse_key_value(&pos, &key, &value_s)) {
+ const struct protocol *p;
+ if (parse_protocol(key, &p)) {
+ if (flow->dl_type) {
+ error = xasprintf("%s: Ethernet type set multiple times", s);
+ goto exit;
+ }
+ flow->dl_type = htons(p->dl_type);
+
+ if (p->nw_proto) {
+ if (flow->nw_proto) {
+ error = xasprintf("%s: network protocol set "
+ "multiple times", s);
+ goto exit;
+ }
+ flow->nw_proto = p->nw_proto;
+ }
+ } else {
+ const struct mf_field *mf;
+ union mf_value value;
+ char *field_error;
+
+ mf = mf_from_name(key);
+ if (!mf) {
+ error = xasprintf("%s: unknown field %s", s, key);
+ goto exit;
+ }
+
+ if (!mf_are_prereqs_ok(mf, flow)) {
+ error = xasprintf("%s: prerequisites not met for setting %s",
+ s, key);
+ goto exit;
+ }
+
+ if (!mf_is_zero(mf, flow)) {
+ error = xasprintf("%s: field %s set multiple times", s, key);
+ goto exit;
+ }
+
+ field_error = mf_parse_value(mf, value_s, &value);
+ if (field_error) {
+ error = xasprintf("%s: bad value for %s (%s)",
+ s, key, field_error);
+ free(field_error);
+ goto exit;
+ }
+
+ mf_set_flow_value(mf, &value, flow);
+ }
+ }
+
+exit:
+ free(copy);