jv jv_get(jv t, jv k) { jv v; if (jv_get_kind(t) == JV_KIND_OBJECT && jv_get_kind(k) == JV_KIND_STRING) { v = jv_object_get(t, k); if (!jv_is_valid(v)) { jv_free(v); v = jv_null(); } } else if (jv_get_kind(t) == JV_KIND_ARRAY && jv_get_kind(k) == JV_KIND_NUMBER) { if(jv_is_integer(k)){ v = jv_array_get(t, (int)jv_number_value(k)); if (!jv_is_valid(v)) { jv_free(v); v = jv_null(); } } else { jv_free(t); jv_free(k); v = jv_null(); } } else if (jv_get_kind(t) == JV_KIND_ARRAY && jv_get_kind(k) == JV_KIND_OBJECT) { int start, end; if (parse_slice(t, k, &start, &end)) { v = jv_array_slice(t, start, end); } else { v = jv_invalid_with_msg(jv_string_fmt("Start and end indices of an array slice must be numbers")); jv_free(t); } } else if (jv_get_kind(t) == JV_KIND_STRING && jv_get_kind(k) == JV_KIND_OBJECT) { int start, end; if (parse_slice(t, k, &start, &end)) { v = jv_string_slice(t, start, end); } else { v = jv_invalid_with_msg(jv_string_fmt("Start and end indices of an string slice must be numbers")); jv_free(t); } } else if (jv_get_kind(t) == JV_KIND_STRING && jv_get_kind(k) == JV_KIND_STRING) { v = jv_string_indexes(t, k); } else if (jv_get_kind(t) == JV_KIND_ARRAY && jv_get_kind(k) == JV_KIND_ARRAY) { v = jv_array_indexes(t, k); } else if (jv_get_kind(t) == JV_KIND_NULL && (jv_get_kind(k) == JV_KIND_STRING || jv_get_kind(k) == JV_KIND_NUMBER || jv_get_kind(k) == JV_KIND_OBJECT)) { jv_free(t); jv_free(k); v = jv_null(); } else { v = jv_invalid_with_msg(jv_string_fmt("Cannot index %s with %s", jv_kind_name(jv_get_kind(t)), jv_kind_name(jv_get_kind(k)))); jv_free(t); jv_free(k); } return v; }
// assumes keys is a sorted array jv jv_dels(jv t, jv keys) { assert(jv_get_kind(keys) == JV_KIND_ARRAY); assert(jv_is_valid(t)); if (jv_get_kind(t) == JV_KIND_NULL || jv_array_length(jv_copy(keys)) == 0) { // no change } else if (jv_get_kind(t) == JV_KIND_ARRAY) { // extract slices, they must be handled differently jv orig_keys = keys; keys = jv_array(); jv new_array = jv_array(); jv starts = jv_array(), ends = jv_array(); jv_array_foreach(orig_keys, i, key) { if (jv_get_kind(key) == JV_KIND_NUMBER) { keys = jv_array_append(keys, key); } else if (jv_get_kind(key) == JV_KIND_OBJECT) { int start, end; if (parse_slice(t, key, &start, &end)) { starts = jv_array_append(starts, jv_number(start)); ends = jv_array_append(ends, jv_number(end)); } else { jv_free(new_array); jv_free(key); new_array = jv_invalid_with_msg(jv_string_fmt("Start and end indices of an array slice must be numbers")); goto arr_out; } } else { jv_free(new_array); new_array = jv_invalid_with_msg(jv_string_fmt("Cannot delete %s element of array", jv_kind_name(jv_get_kind(key)))); jv_free(key); goto arr_out; } } int kidx = 0; jv_array_foreach(t, i, elem) { int del = 0; while (kidx < jv_array_length(jv_copy(keys))) { int delidx = (int)jv_number_value(jv_array_get(jv_copy(keys), kidx)); if (i == delidx) { del = 1; } if (i < delidx) { break; } kidx++; } for (int sidx=0; !del && sidx<jv_array_length(jv_copy(starts)); sidx++) { if ((int)jv_number_value(jv_array_get(jv_copy(starts), sidx)) <= i && i < (int)jv_number_value(jv_array_get(jv_copy(ends), sidx))) { del = 1; } } if (!del) new_array = jv_array_append(new_array, elem); else jv_free(elem); }
int semver_parse (const char *str, semver_t *ver) { int valid, res; size_t len; char *buf; valid = semver_is_valid(str); if (!valid) return -1; len = strlen(str); buf = calloc(len + 1, sizeof(*buf)); if (buf == NULL) return -1; strcpy(buf, str); ver->metadata = parse_slice(buf, MT_DELIMITER[0]); ver->prerelease = parse_slice(buf, PR_DELIMITER[0]); res = semver_parse_version(buf, ver); free(buf); #if DEBUG > 0 printf("[debug] semver.c %s = %d.%d.%d, %s %s\n", str, ver->major, ver->minor, ver->patch, ver->prerelease, ver->metadata); #endif return res; }
static int expand_variables_internal(parser_t &parser, wchar_t * const in, std::vector<completion_t> &out, long last_idx) { int is_ok= 1; int empty=0; wcstring var_tmp; std::vector<long> var_idx_list; // CHECK( out, 0 ); for (long i=last_idx; (i>=0) && is_ok && !empty; i--) { const wchar_t c = in[i]; if ((c == VARIABLE_EXPAND) || (c == VARIABLE_EXPAND_SINGLE)) { long start_pos = i+1; long stop_pos; long var_len; int is_single = (c==VARIABLE_EXPAND_SINGLE); stop_pos = start_pos; while (1) { if (!(in[stop_pos ])) break; if (!(iswalnum(in[stop_pos]) || (wcschr(L"_", in[stop_pos])!= 0))) break; stop_pos++; } /* printf( "Stop for '%c'\n", in[stop_pos]);*/ var_len = stop_pos - start_pos; if (var_len == 0) { expand_variable_error(parser, in, stop_pos-1, -1); is_ok = 0; break; } var_tmp.append(in + start_pos, var_len); env_var_t var_val = expand_var(var_tmp.c_str()); if (! var_val.missing()) { int all_vars=1; wcstring_list_t var_item_list; if (is_ok) { tokenize_variable_array(var_val.c_str(), var_item_list); if (in[stop_pos] == L'[') { wchar_t *slice_end; all_vars=0; if (parse_slice(in + stop_pos, &slice_end, var_idx_list, var_item_list.size())) { parser.error(SYNTAX_ERROR, -1, L"Invalid index value"); is_ok = 0; break; } stop_pos = (slice_end-in); } if (!all_vars) { wcstring_list_t string_values(var_idx_list.size()); for (size_t j=0; j<var_idx_list.size(); j++) { long tmp = var_idx_list.at(j); /* Check that we are within array bounds. If not, truncate the list to exit. */ if (tmp < 1 || (size_t)tmp > var_item_list.size()) { parser.error(SYNTAX_ERROR, -1, ARRAY_BOUNDS_ERR); is_ok=0; var_idx_list.resize(j); break; } else { /* Replace each index in var_idx_list inplace with the string value at the specified index */ //al_set( var_idx_list, j, wcsdup((const wchar_t *)al_get( &var_item_list, tmp-1 ) ) ); string_values.at(j) = var_item_list.at(tmp-1); } } // string_values is the new var_item_list var_item_list.swap(string_values); } } if (is_ok) { if (is_single) { in[i]=0; wcstring res = in; res.push_back(INTERNAL_SEPARATOR); for (size_t j=0; j<var_item_list.size(); j++) { const wcstring &next = var_item_list.at(j); if (is_ok) { if (j != 0) res.append(L" "); res.append(next); } } res.append(in + stop_pos); is_ok &= expand_variables2(parser, res, out, i); } else { for (size_t j=0; j<var_item_list.size(); j++) { const wcstring &next = var_item_list.at(j); if (is_ok && (i == 0) && (!in[stop_pos])) { append_completion(out, next); } else { if (is_ok) { wcstring new_in; if (start_pos > 0) new_in.append(in, start_pos - 1); // at this point new_in.size() is start_pos - 1 if (start_pos>1 && new_in[start_pos-2]!=VARIABLE_EXPAND) { new_in.push_back(INTERNAL_SEPARATOR); } new_in.append(next); new_in.append(in + stop_pos); is_ok &= expand_variables2(parser, new_in, out, i); } } } } } return is_ok; } else { /* Expand a non-existing variable */ if (c == VARIABLE_EXPAND) { /* Regular expansion, i.e. expand this argument to nothing */ empty = 1; } else { /* Expansion to single argument. */ wcstring res; in[i] = 0; res.append(in); res.append(in + stop_pos); is_ok &= expand_variables2(parser, res, out, i); return is_ok; } } } } if (!empty) { append_completion(out, in); } return is_ok; }
/** Perform cmdsubst expansion */ static int expand_cmdsubst(parser_t &parser, const wcstring &input, std::vector<completion_t> &outList) { wchar_t *paran_begin=0, *paran_end=0; std::vector<wcstring> sub_res; size_t i, j; wchar_t *tail_begin = 0; const wchar_t * const in = input.c_str(); int parse_ret; switch (parse_ret = parse_util_locate_cmdsubst(in, ¶n_begin, ¶n_end, 0)) { case -1: parser.error(SYNTAX_ERROR, -1, L"Mismatched parenthesis"); return 0; case 0: outList.push_back(completion_t(input)); return 1; case 1: break; } const wcstring subcmd(paran_begin + 1, paran_end-paran_begin - 1); if (exec_subshell(subcmd, sub_res) == -1) { parser.error(CMDSUBST_ERROR, -1, L"Unknown error while evaulating command substitution"); return 0; } tail_begin = paran_end + 1; if (*tail_begin == L'[') { std::vector<long> slice_idx; wchar_t *slice_end; if (parse_slice(tail_begin, &slice_end, slice_idx, sub_res.size())) { parser.error(SYNTAX_ERROR, -1, L"Invalid index value"); return 0; } else { std::vector<wcstring> sub_res2; tail_begin = slice_end; for (i=0; i < slice_idx.size(); i++) { long idx = slice_idx.at(i); if (idx < 1 || (size_t)idx > sub_res.size()) { parser.error(SYNTAX_ERROR, -1, ARRAY_BOUNDS_ERR); return 0; } idx = idx-1; sub_res2.push_back(sub_res.at(idx)); // debug( 0, L"Pushing item '%ls' with index %d onto sliced result", al_get( sub_res, idx ), idx ); //sub_res[idx] = 0; // ?? } sub_res = sub_res2; } } /* Recursively call ourselves to expand any remaining command substitutions. The result of this recursive call using the tail of the string is inserted into the tail_expand array list */ std::vector<completion_t> tail_expand; expand_cmdsubst(parser, tail_begin, tail_expand); /* Combine the result of the current command substitution with the result of the recursive tail expansion */ for (i=0; i<sub_res.size(); i++) { wcstring sub_item = sub_res.at(i); wcstring sub_item2 = escape_string(sub_item, 1); for (j=0; j < tail_expand.size(); j++) { wcstring whole_item; wcstring tail_item = tail_expand.at(j).completion; //sb_append_substring( &whole_item, in, len1 ); whole_item.append(in, paran_begin-in); //sb_append_char( &whole_item, INTERNAL_SEPARATOR ); whole_item.push_back(INTERNAL_SEPARATOR); //sb_append_substring( &whole_item, sub_item2, item_len ); whole_item.append(sub_item2); //sb_append_char( &whole_item, INTERNAL_SEPARATOR ); whole_item.push_back(INTERNAL_SEPARATOR); //sb_append( &whole_item, tail_item ); whole_item.append(tail_item); //al_push( out, whole_item.buff ); outList.push_back(completion_t(whole_item)); } } return 1; }
jv jv_get(jv t, jv k) { jv v; if (jv_get_kind(t) == JV_KIND_OBJECT && jv_get_kind(k) == JV_KIND_STRING) { v = jv_object_get(t, k); if (!jv_is_valid(v)) { jv_free(v); v = jv_null(); } } else if (jv_get_kind(t) == JV_KIND_ARRAY && jv_get_kind(k) == JV_KIND_NUMBER) { if(jv_is_integer(k)){ int idx = (int)jv_number_value(k); if (idx < 0) idx += jv_array_length(jv_copy(t)); v = jv_array_get(t, idx); if (!jv_is_valid(v)) { jv_free(v); v = jv_null(); } } else { jv_free(t); jv_free(k); v = jv_null(); } } else if (jv_get_kind(t) == JV_KIND_ARRAY && jv_get_kind(k) == JV_KIND_OBJECT) { int start, end; if (parse_slice(t, k, &start, &end)) { v = jv_array_slice(t, start, end); } else { v = jv_invalid_with_msg(jv_string_fmt("Start and end indices of an array slice must be numbers")); jv_free(t); } } else if (jv_get_kind(t) == JV_KIND_STRING && jv_get_kind(k) == JV_KIND_OBJECT) { int start, end; if (parse_slice(t, k, &start, &end)) { v = jv_string_slice(t, start, end); } else { v = jv_invalid_with_msg(jv_string_fmt("Start and end indices of an string slice must be numbers")); jv_free(t); } } else if (jv_get_kind(t) == JV_KIND_STRING && jv_get_kind(k) == JV_KIND_STRING) { v = jv_string_indexes(t, k); } else if (jv_get_kind(t) == JV_KIND_ARRAY && jv_get_kind(k) == JV_KIND_ARRAY) { v = jv_array_indexes(t, k); } else if (jv_get_kind(t) == JV_KIND_NULL && (jv_get_kind(k) == JV_KIND_STRING || jv_get_kind(k) == JV_KIND_NUMBER || jv_get_kind(k) == JV_KIND_OBJECT)) { jv_free(t); jv_free(k); v = jv_null(); } else { /* * If k is a short string it's probably from a jq .foo expression or * similar, in which case putting it in the invalid msg may help the * user. The length 30 is arbitrary. */ if (jv_get_kind(k) == JV_KIND_STRING && jv_string_length_bytes(jv_copy(k)) < 30) { v = jv_invalid_with_msg(jv_string_fmt("Cannot index %s with string \"%s\"", jv_kind_name(jv_get_kind(t)), jv_string_value(k))); } else { v = jv_invalid_with_msg(jv_string_fmt("Cannot index %s with %s", jv_kind_name(jv_get_kind(t)), jv_kind_name(jv_get_kind(k)))); } jv_free(t); jv_free(k); } return v; }
jv jv_set(jv t, jv k, jv v) { if (!jv_is_valid(v)) { jv_free(t); jv_free(k); return v; } int isnull = jv_get_kind(t) == JV_KIND_NULL; if (jv_get_kind(k) == JV_KIND_STRING && (jv_get_kind(t) == JV_KIND_OBJECT || isnull)) { if (isnull) t = jv_object(); t = jv_object_set(t, k, v); } else if (jv_get_kind(k) == JV_KIND_NUMBER && (jv_get_kind(t) == JV_KIND_ARRAY || isnull)) { if (isnull) t = jv_array(); t = jv_array_set(t, (int)jv_number_value(k), v); } else if (jv_get_kind(k) == JV_KIND_OBJECT && (jv_get_kind(t) == JV_KIND_ARRAY || isnull)) { if (isnull) t = jv_array(); int start, end; if (parse_slice(t, k, &start, &end)) { if (jv_get_kind(v) == JV_KIND_ARRAY) { int array_len = jv_array_length(jv_copy(t)); assert(0 <= start && start <= end && end <= array_len); int slice_len = end - start; int insert_len = jv_array_length(jv_copy(v)); if (slice_len < insert_len) { // array is growing int shift = insert_len - slice_len; for (int i = array_len - 1; i >= end; i--) { t = jv_array_set(t, i + shift, jv_array_get(jv_copy(t), i)); } } else if (slice_len > insert_len) { // array is shrinking int shift = slice_len - insert_len; for (int i = end; i < array_len; i++) { t = jv_array_set(t, i - shift, jv_array_get(jv_copy(t), i)); } t = jv_array_slice(t, 0, array_len - shift); } for (int i=0; i < insert_len; i++) { t = jv_array_set(t, start + i, jv_array_get(jv_copy(v), i)); } jv_free(v); } else { jv_free(t); jv_free(v); t = jv_invalid_with_msg(jv_string_fmt("A slice of an array can only be assigned another array")); } } else { jv_free(t); jv_free(k); jv_free(v); t = jv_invalid_with_msg(jv_string_fmt("Start and end indices of an array slice must be numbers")); } } else { jv err = jv_invalid_with_msg(jv_string_fmt("Cannot update field at %s index of %s", jv_kind_name(jv_get_kind(k)), jv_kind_name(jv_get_kind(t)))); jv_free(t); jv_free(k); jv_free(v); t = err; } return t; }
/// Expand all environment variables in the string *ptr. /// /// This function is slow, fragile and complicated. There are lots of little corner cases, like /// $$foo should do a double expansion, $foo$bar should not double expand bar, etc. Also, it's easy /// to accidentally leak memory on array out of bounds errors an various other situations. All in /// all, this function should be rewritten, split out into multiple logical units and carefully /// tested. After that, it can probably be optimized to do fewer memory allocations, fewer string /// scans and overall just less work. But until that happens, don't edit it unless you know exactly /// what you are doing, and do proper testing afterwards. /// /// This function operates on strings backwards, starting at last_idx. /// /// Note: last_idx is considered to be where it previously finished procesisng. This means it /// actually starts operating on last_idx-1. As such, to process a string fully, pass string.size() /// as last_idx instead of string.size()-1. static bool expand_variables(const wcstring &instr, std::vector<completion_t> *out, size_t last_idx, parse_error_list_t *errors) { const size_t insize = instr.size(); // last_idx may be 1 past the end of the string, but no further. assert(last_idx <= insize && "Invalid last_idx"); if (last_idx == 0) { append_completion(out, instr); return true; } // Locate the last VARIABLE_EXPAND or VARIABLE_EXPAND_SINGLE bool is_single = false; size_t varexp_char_idx = last_idx; while (varexp_char_idx--) { const wchar_t c = instr.at(varexp_char_idx); if (c == VARIABLE_EXPAND || c == VARIABLE_EXPAND_SINGLE) { is_single = (c == VARIABLE_EXPAND_SINGLE); break; } } if (varexp_char_idx >= instr.size()) { // No variable expand char, we're done. append_completion(out, instr); return true; } // Get the variable name. const size_t var_name_start = varexp_char_idx + 1; size_t var_name_stop = var_name_start; while (var_name_stop < insize) { const wchar_t nc = instr.at(var_name_stop); if (nc == VARIABLE_EXPAND_EMPTY) { var_name_stop++; break; } if (!valid_var_name_char(nc)) break; var_name_stop++; } assert(var_name_stop >= var_name_start && "Bogus variable name indexes"); const size_t var_name_len = var_name_stop - var_name_start; // It's an error if the name is empty. if (var_name_len == 0) { if (errors) { parse_util_expand_variable_error(instr, 0 /* global_token_pos */, varexp_char_idx, errors); } return false; } // Get the variable name as a string, then try to get the variable from env. const wcstring var_name(instr, var_name_start, var_name_len); // Do a dirty hack to make sliced history fast (#4650). We expand from either a variable, or a // history_t. Note that "history" is read only in env.cpp so it's safe to special-case it in // this way (it cannot be shadowed, etc). history_t *history = nullptr; maybe_t<env_var_t> var{}; if (var_name == L"history") { // We do this only on the main thread, matching env.cpp. if (is_main_thread()) { history = reader_get_history(); } } else if (var_name != wcstring{VARIABLE_EXPAND_EMPTY}) { var = env_get(var_name); } // Parse out any following slice. // Record the end of the variable name and any following slice. size_t var_name_and_slice_stop = var_name_stop; bool all_values = true; const size_t slice_start = var_name_stop; // List of indexes, and parallel array of source positions of each index in the variable list. std::vector<long> var_idx_list; std::vector<size_t> var_pos_list; if (slice_start < insize && instr.at(slice_start) == L'[') { all_values = false; const wchar_t *in = instr.c_str(); wchar_t *slice_end; // If a variable is missing, behave as though we have one value, so that $var[1] always // works. size_t effective_val_count = 1; if (var) { effective_val_count = var->as_list().size(); } else if (history) { effective_val_count = history->size(); } size_t bad_pos = parse_slice(in + slice_start, &slice_end, var_idx_list, var_pos_list, effective_val_count); if (bad_pos != 0) { append_syntax_error(errors, slice_start + bad_pos, L"Invalid index value"); return false; } var_name_and_slice_stop = (slice_end - in); } if (!var && !history) { // Expanding a non-existent variable. if (!is_single) { // Normal expansions of missing variables successfully expand to nothing. return true; } else { // Expansion to single argument. // Replace the variable name and slice with VARIABLE_EXPAND_EMPTY. wcstring res(instr, 0, varexp_char_idx); if (!res.empty() && res.back() == VARIABLE_EXPAND_SINGLE) { res.push_back(VARIABLE_EXPAND_EMPTY); } res.append(instr, var_name_and_slice_stop, wcstring::npos); return expand_variables(res, out, varexp_char_idx, errors); } } // Ok, we have a variable or a history. Let's expand it. // Start by respecting the sliced elements. assert((var || history) && "Should have variable or history here"); wcstring_list_t var_item_list; if (all_values) { if (history) { history->get_history(var_item_list); } else { var->to_list(var_item_list); } } else { // We have to respect the slice. if (history) { // Ask history to map indexes to item strings. // Note this may have missing entries for out-of-bounds. auto item_map = history->items_at_indexes(var_idx_list); for (long item_index : var_idx_list) { auto iter = item_map.find(item_index); if (iter != item_map.end()) { var_item_list.push_back(iter->second); } } } else { const wcstring_list_t &all_var_items = var->as_list(); for (long item_index : var_idx_list) { // Check that we are within array bounds. If not, skip the element. Note: // Negative indices (`echo $foo[-1]`) are already converted to positive ones // here, So tmp < 1 means it's definitely not in. // Note we are 1-based. if (item_index >= 1 && size_t(item_index) <= all_var_items.size()) { var_item_list.push_back(all_var_items.at(item_index - 1)); } } } } if (is_single) { wcstring res(instr, 0, varexp_char_idx); if (!res.empty()) { if (res.back() != VARIABLE_EXPAND_SINGLE) { res.push_back(INTERNAL_SEPARATOR); } else if (var_item_list.empty() || var_item_list.front().empty()) { // First expansion is empty, but we need to recursively expand. res.push_back(VARIABLE_EXPAND_EMPTY); } } // Append all entries in var_item_list, separated by spaces. // Remove the last space. if (!var_item_list.empty()) { for (const wcstring &item : var_item_list) { res.append(item); res.push_back(L' '); } res.pop_back(); } res.append(instr, var_name_and_slice_stop, wcstring::npos); return expand_variables(res, out, varexp_char_idx, errors); } else { // Normal cartesian-product expansion. for (const wcstring &item : var_item_list) { if (varexp_char_idx == 0 && var_name_and_slice_stop == insize) { append_completion(out, item); } else { wcstring new_in(instr, 0, varexp_char_idx); if (!new_in.empty()) { if (new_in.back() != VARIABLE_EXPAND) { new_in.push_back(INTERNAL_SEPARATOR); } else if (item.empty()) { new_in.push_back(VARIABLE_EXPAND_EMPTY); } } new_in.append(item); new_in.append(instr, var_name_and_slice_stop, wcstring::npos); if (!expand_variables(new_in, out, varexp_char_idx, errors)) { return false; } } } } return true; }
void CSSParserBorderImage::parse(const std::string &name, const std::vector<CSSToken> &tokens, std::vector<std::unique_ptr<CSSPropertyValue> > &inout_values) { std::unique_ptr<CSSValueBorderImageSource> border_image_source(new CSSValueBorderImageSource()); std::unique_ptr<CSSValueBorderImageSlice> border_image_slice(new CSSValueBorderImageSlice()); std::unique_ptr<CSSValueBorderImageWidth> border_image_width(new CSSValueBorderImageWidth()); std::unique_ptr<CSSValueBorderImageOutset> border_image_outset(new CSSValueBorderImageOutset()); std::unique_ptr<CSSValueBorderImageRepeat> border_image_repeat(new CSSValueBorderImageRepeat()); if (tokens.size() == 1 && tokens[0].type == CSSToken::type_ident && equals(tokens[0].value, "inherit")) { border_image_source->type = CSSValueBorderImageSource::type_inherit; border_image_slice->type = CSSValueBorderImageSlice::type_inherit; border_image_width->type = CSSValueBorderImageWidth::type_inherit; border_image_outset->type = CSSValueBorderImageOutset::type_inherit; border_image_repeat->type = CSSValueBorderImageRepeat::type_inherit; inout_values.push_back(std::move(border_image_source)); inout_values.push_back(std::move(border_image_slice)); inout_values.push_back(std::move(border_image_width)); inout_values.push_back(std::move(border_image_outset)); inout_values.push_back(std::move(border_image_repeat)); return; } bool source_specified = false; bool slice_specified = false; bool repeat_specified = false; size_t pos = 0; do { if (!source_specified && parse_source(*border_image_source.get(), pos, tokens)) { source_specified = true; } else if (!slice_specified && parse_slice(*border_image_slice.get(), pos, tokens)) { slice_specified = true; size_t next_pos = pos; CSSToken token = next_token(next_pos, tokens); if (token.type == CSSToken::type_delim && token.value == "/") { pos = next_pos; if (parse_width(*border_image_width.get(), pos, tokens)) { next_pos = pos; CSSToken token = next_token(next_pos, tokens); if (token.type == CSSToken::type_delim && token.value == "/") { pos = next_pos; if (!parse_outset(*border_image_outset.get(), pos, tokens)) { return; } } } else if (!parse_outset(*border_image_outset.get(), pos, tokens)) { return; } } } else if (!repeat_specified && parse_repeat(*border_image_repeat.get(), pos, tokens)) { repeat_specified = true; } else { return; } } while (pos != tokens.size()); inout_values.push_back(std::move(border_image_source)); inout_values.push_back(std::move(border_image_slice)); inout_values.push_back(std::move(border_image_width)); inout_values.push_back(std::move(border_image_outset)); inout_values.push_back(std::move(border_image_repeat)); }