#include "internal.h" int command(const char* begin, const char* end, const var_config_t* config, const char nameclass[256], var_cb_t lookup, void* lookup_context, int force_expand, tokenbuf* data) { const char* p = begin; tokenbuf tmptokbuf; tokenbuf search, replace, flags; tokenbuf number1, number2; int isrange; int rc; init_tokenbuf(&tmptokbuf); init_tokenbuf(&search); init_tokenbuf(&replace); init_tokenbuf(&flags); init_tokenbuf(&number1); init_tokenbuf(&number2); if (begin == end) return 0; switch (tolower(*p)) { case 'l': /* Turn data to lowercase. */ if (data->begin) { char* ptr; /* If the buffer does not life in an allocated buffer, we have to copy it before modifying the contents. */ if (data->buffer_size == 0) { if (!assign_to_tokenbuf(data, data->begin, data->end - data->begin)) { rc = VAR_OUT_OF_MEMORY; goto error_return; } } for (ptr = (char*)data->begin; ptr != data->end; ++ptr) *ptr = tolower(*ptr); } ++p; break; case 'u': /* Turn data to uppercase. */ if (data->begin) { char* ptr; if (data->buffer_size == 0) { if (!assign_to_tokenbuf(data, data->begin, data->end - data->begin)) { rc = VAR_OUT_OF_MEMORY; goto error_return; } } for (ptr = (char*)data->begin; ptr != data->end; ++ptr) *ptr = toupper(*ptr); } ++p; break; case 'o': /* Cut out substrings. */ ++p; rc = number(p, end); if (rc == 0) { rc = VAR_MISSING_START_OFFSET; goto error_return; } else { number1.begin = p; number1.end = p + rc; number1.buffer_size = 0; p += rc; } if (*p == ',') { isrange = 0; ++p; } else if (*p == '-') { isrange = 1; ++p; } else { rc = VAR_INVALID_OFFSET_DELIMITER; goto error_return; } rc = number(p, end); number2.begin = p; number2.end = p + rc; number2.buffer_size = 0; p += rc; if (data->begin) { rc = cut_out_offset(data, &number1, &number2, isrange); if (rc < 0) goto error_return; } break; case '#': /* Substitute length of the string. */ if (data->begin) { char buf[1024]; sprintf(buf, "%d", data->end - data->begin); free_tokenbuf(data); if (!assign_to_tokenbuf(data, buf, strlen(buf))) { rc = VAR_OUT_OF_MEMORY; goto error_return; } } ++p; break; case '-': /* Substitute parameter if data is empty. */ ++p; rc = exptext_or_variable(p, end, config, nameclass, lookup, lookup_context, force_expand, &tmptokbuf); if (rc < 0) goto error_return; else if (rc == 0) { rc = VAR_MISSING_PARAMETER_IN_COMMAND; goto error_return; } else p += rc; if (data->begin != NULL && data->begin == data->end) { free_tokenbuf(data); move_tokenbuf(&tmptokbuf, data); } break; case '*': /* Return "" if data is not empty, parameter otherwise. */ ++p; rc = exptext_or_variable(p, end, config, nameclass, lookup, lookup_context, force_expand, &tmptokbuf); if (rc < 0) goto error_return; else if (rc == 0) { rc = VAR_MISSING_PARAMETER_IN_COMMAND; goto error_return; } else p += rc; if (data->begin != NULL) { if (data->begin == data->end) { free_tokenbuf(data); move_tokenbuf(&tmptokbuf, data); } else { free_tokenbuf(data); data->begin = data->end = ""; data->buffer_size = 0; } } break; case '+': /* Substitute parameter if data is not empty. */ ++p; rc = exptext_or_variable(p, end, config, nameclass, lookup, lookup_context, force_expand, &tmptokbuf); if (rc < 0) goto error_return; else if (rc == 0) { rc = VAR_MISSING_PARAMETER_IN_COMMAND; goto error_return; } else p += rc; if (data->begin != NULL) { if (data->begin != data->end) { free_tokenbuf(data); move_tokenbuf(&tmptokbuf, data); } } break; case 's': /* Search and replace. */ ++p; if (*p != '/') return VAR_MALFORMATTED_REPLACE; else ++p; rc = substext_or_variable(p, end, config, nameclass, lookup, lookup_context, force_expand, &search); if (rc < 0) goto error_return; else p += rc; if (*p != '/') { rc = VAR_MALFORMATTED_REPLACE; goto error_return; } else ++p; rc = substext_or_variable(p, end, config, nameclass, lookup, lookup_context, force_expand, &replace); if (rc < 0) goto error_return; else p += rc; if (*p != '/') { rc = VAR_MALFORMATTED_REPLACE; goto error_return; } else ++p; rc = exptext(p, end, config); if (rc < 0) goto error_return; else { flags.begin = p; flags.end = p + rc; flags.buffer_size = 0; p += rc; } if (data->begin) { rc = search_and_replace(data, &search, &replace, &flags); if (rc < 0) goto error_return; } break; case 'y': /* Transpose characters from class A to class B. */ ++p; if (*p != '/') return VAR_MALFORMATTED_TRANSPOSE; else ++p; rc = substext_or_variable(p, end, config, nameclass, lookup, lookup_context, force_expand, &search); if (rc < 0) goto error_return; else p += rc; if (*p != '/') { rc = VAR_MALFORMATTED_TRANSPOSE; goto error_return; } else ++p; rc = substext_or_variable(p, end, config, nameclass, lookup, lookup_context, force_expand, &replace); if (rc < 0) goto error_return; else p += rc; if (*p != '/') { rc = VAR_MALFORMATTED_TRANSPOSE; goto error_return; } else ++p; if (data->begin) { rc = transpose(data, &search, &replace); if (rc < 0) goto error_return; } break; case 'p': /* Padding. */ ++p; if (*p != '/') return VAR_MALFORMATTED_PADDING; else ++p; rc = number(p, end); if (rc == 0) { rc = VAR_MISSING_PADDING_WIDTH; goto error_return; } else { number1.begin = p; number1.end = p + rc; number1.buffer_size = 0; p += rc; } if (*p != '/') { rc = VAR_MALFORMATTED_PADDING; goto error_return; } else ++p; rc = substext_or_variable(p, end, config, nameclass, lookup, lookup_context, force_expand, &replace); if (rc < 0) goto error_return; else p += rc; if (*p != '/') { rc = VAR_MALFORMATTED_PADDING; goto error_return; } else ++p; if (*p != 'l' && *p != 'c' && *p != 'r') { rc = VAR_MALFORMATTED_PADDING; goto error_return; } else ++p; if (data->begin) { rc = padding(data, &number1, &replace, p[-1]); if (rc < 0) goto error_return; } break; default: return VAR_UNKNOWN_COMMAND_CHAR; } /* Exit gracefully. */ free_tokenbuf(&tmptokbuf); free_tokenbuf(&search); free_tokenbuf(&replace); free_tokenbuf(&flags); free_tokenbuf(&number1); free_tokenbuf(&number2); return p - begin; error_return: free_tokenbuf(data); free_tokenbuf(&tmptokbuf); free_tokenbuf(&search); free_tokenbuf(&replace); free_tokenbuf(&flags); free_tokenbuf(&number1); free_tokenbuf(&number2); return rc; }