/* make a copy of the string that replaces the units with numbers * this is used before parsing * This is only used when evaluating user input and can afford to be a bit slower * * This is to be used before python evaluation so.. * 10.1km -> 10.1*1000.0 * ...will be resolved by python. * * values will be split by a comma's * 5'2" -> 5'0.0254, 2*0.3048 * * str_prev is optional, when valid it is used to get a base unit when none is set. * * return true of a change was made. */ int bUnit_ReplaceString(char *str, int len_max, const char *str_prev, double scale_pref, int system, int type) { bUnitCollection *usys = unit_get_system(system, type); bUnitDef *unit; char str_tmp[TEMP_STR_SIZE]; int changed = 0; if (usys == NULL || usys->units[0].name == NULL) { return 0; } /* make lowercase */ BLI_ascii_strtolower(str, len_max); for (unit = usys->units; unit->name; unit++) { /* in case there are multiple instances */ while (unit_replace(str, len_max, str_tmp, scale_pref, unit)) changed = true; } unit = NULL; { /* try other unit systems now, so we can evaluate imperial when metric is set for eg. */ bUnitCollection *usys_iter; int system_iter; for (system_iter = 0; system_iter < UNIT_SYSTEM_TOT; system_iter++) { if (system_iter != system) { usys_iter = unit_get_system(system_iter, type); if (usys_iter) { for (unit = usys_iter->units; unit->name; unit++) { int ofs = 0; /* in case there are multiple instances */ while ((ofs = unit_replace(str + ofs, len_max - ofs, str_tmp, scale_pref, unit))) changed = true; } } } } } unit = NULL; if (changed == 0) { /* no units given so infer a unit from the previous string or default */ if (str_prev) { /* see which units the original value had */ for (unit = usys->units; unit->name; unit++) { if (unit_find(str_prev, unit)) break; } } if (unit == NULL || unit->name == NULL) unit = unit_default(usys); /* add the unit prefix and re-run, use brackets in case there was an expression given */ if (BLI_snprintf(str_tmp, sizeof(str_tmp), "(%s)%s", str, unit->name) < sizeof(str_tmp)) { strncpy(str, str_tmp, len_max); return bUnit_ReplaceString(str, len_max, NULL, scale_pref, system, type); } else { /* BLI_snprintf would not fit into str_tmp, cant do much in this case * check for this because otherwise bUnit_ReplaceString could call its self forever */ return 0; } } /* replace # with commas when there is no operator between it and the next number * * "1*1# 3*100# * 3" -> "1 *1, 3 *100 * 3" * * */ { char *str_found = str; char *ch = str; while ((str_found = strchr(str_found, SEP_CHR))) { int op_found = 0; /* any operators after this?*/ for (ch = str_found + 1; *ch != '\0'; ch++) { if (*ch == ' ' || *ch == '\t') { /* do nothing */ } else if (ch_is_op(*ch) || *ch == ',') { /* found an op, no need to insert a ',' */ op_found = 1; break; } else { /* found a non-op character */ op_found = 0; break; } } *str_found++ = op_found ? ' ' : ','; } } return changed; }
/* make a copy of the string that replaces the units with numbers * this is used before parsing * This is only used when evaluating user input and can afford to be a bit slower * * This is to be used before python evaluation so.. * 10.1km -> 10.1*1000.0 * ...will be resolved by python. * * values will be split by an add sign * 5'2" -> 5*0.3048 + 2*0.0254 * * str_prev is optional, when valid it is used to get a base unit when none is set. * * return true of a change was made. */ bool bUnit_ReplaceString(char *str, int len_max, const char *str_prev, double scale_pref, int system, int type) { bUnitCollection *usys = unit_get_system(system, type); bUnitDef *unit = NULL, *default_unit; double scale_pref_base = scale_pref; char str_tmp[TEMP_STR_SIZE]; bool changed = false; if (usys == NULL || usys->units[0].name == NULL) { return changed; } /* make lowercase */ BLI_str_tolower_ascii(str, len_max); /* Try to find a default unit from current or previous string. */ default_unit = unit_detect_from_str(usys, str, str_prev); /* We apply the default unit to the whole expression (default unit is now the reference '1.0' one). */ scale_pref_base *= default_unit->scalar; /* Apply the default unit on the whole expression, this allows to handle nasty cases like '2+2in'. */ if (BLI_snprintf(str_tmp, sizeof(str_tmp), "(%s)*%.9g", str, default_unit->scalar) < sizeof(str_tmp)) { strncpy(str, str_tmp, len_max); } else { /* BLI_snprintf would not fit into str_tmp, cant do much in this case * check for this because otherwise bUnit_ReplaceString could call its self forever */ return changed; } for (unit = usys->units; unit->name; unit++) { /* in case there are multiple instances */ while (unit_replace(str, len_max, str_tmp, scale_pref_base, unit)) changed = true; } unit = NULL; { /* try other unit systems now, so we can evaluate imperial when metric is set for eg. */ /* Note that checking other systems at that point means we do not support their units as 'default' one. * In other words, when in metrics, typing '2+2in' will give 2 meters 2 inches, not 4 inches. * I do think this is the desired behavior! */ bUnitCollection *usys_iter; int system_iter; for (system_iter = 0; system_iter < UNIT_SYSTEM_TOT; system_iter++) { if (system_iter != system) { usys_iter = unit_get_system(system_iter, type); if (usys_iter) { for (unit = usys_iter->units; unit->name; unit++) { int ofs = 0; /* in case there are multiple instances */ while ((ofs = unit_replace(str + ofs, len_max - ofs, str_tmp, scale_pref_base, unit))) changed = true; } } } } } unit = NULL; /* replace # with add sign when there is no operator between it and the next number * * "1*1# 3*100# * 3" -> "1*1+ 3*100 * 3" * * */ { char *str_found = str; const char *ch = str; while ((str_found = strchr(str_found, SEP_CHR))) { bool op_found = false; /* any operators after this? */ for (ch = str_found + 1; *ch != '\0'; ch++) { if (*ch == ' ' || *ch == '\t') { continue; } op_found = (ch_is_op(*ch) || ELEM(*ch, ',', ')')); break; } /* If found an op, comma or closing parenthesis, no need to insert a '+', else we need it. */ *str_found++ = op_found ? ' ' : '+'; } } return changed; }