/* vim: set sw=4 ts=4 sts=4 et: */ #include "l4arg.h" #include "l4array.h" #include <stdlib.h> #include <string.h> LbsStrv* lbs_arg_parse (const char* str, const char* delim, const char* esc, const LbsArgQuote* q, LbsArray** detail_ptr) { LbsStrv* strv = lbs_strv_new (); if (strv == NULL) { return NULL; } /* goto label => free_strv */ // string length cache int qlen; // user-supplied quoting string table length for (qlen = 0; q[qlen].left != NULL && q[qlen].right != NULL; qlen++); // qlen will not be too long, so we can use VLA int qlen_p = qlen <= 0 ? 1 : qlen; #if __STDC_NO_VLA__ int* qllen = malloc (sizeof (int) * qlen_p); if (qllen == NULL) { lbs_strv_unref (strv); return NULL; } int* qrlen = malloc (sizeof (int) * qlen_p); if (qrlen == NULL) { lbs_strv_unref (strv); return NULL; } #else int qllen[qlen_p]; // left quoting string length int qrlen[qlen_p]; // right quoting string length #endif for (int i = 0; i < qlen; i++) { // empty strings are not allowed qllen[i] = strlen (q[i].left); if (qllen[i] <= 0) { goto free_strv; } qrlen[i] = strlen (q[i].right); if (qrlen[i] <= 0) { goto free_strv; } } LbsArray* detail; if (detail_ptr != NULL) { detail = lbs_array_new (sizeof (int)); if (detail == NULL) { goto free_strv; } if (lbs_array_push_back (detail, &(int){-1}) < 0) { goto free_detail; } } else { detail = NULL; } /* goto label => free_detail */ bool is_delimed = true; bool is_escaped = false; bool is_quoted = false; bool ignore_esc = false; int stri = 0; // strv index int qi; // quoting string index currently used const char* p = str; for (; *p != '\0'; p++) { loop_start: if (is_escaped) { if (lbs_strv_append_char (strv, stri, *p) < 0) { goto free_detail; } is_escaped = false; continue; } if (is_quoted) { if (strncmp (p, q[qi].right, qrlen[qi]) == 0) { is_quoted = false; is_escaped = false; ignore_esc = false; p += qrlen[qi] - 1; } else { if (!ignore_esc && strchr (esc, *p)) { is_escaped = true; } else { if (lbs_strv_append_char (strv, stri, *p) < 0) { goto free_detail; } } } continue; } if (strchr (delim, *p)) { if (is_delimed) { continue; } if (lbs_strv_append_str_empty (strv) < 0) { goto free_detail; } if (detail != NULL && lbs_array_push_back (detail, &(int){-1}) < 0) { goto free_detail; } stri++; is_delimed = true; continue; } if (strchr (esc, *p)) { is_escaped = true; continue; } is_delimed = false; for (int i = 0; i < qlen; i++) { if (strncmp (p, q[i].left, qllen[i]) == 0) { is_quoted = true; ignore_esc = q[i].super; qi = i; p += qllen[qi]; // p++ will be skipped, so do not minus 1 here if (detail != NULL) { lbs_array_v (detail, int, stri) = qi; } goto loop_start; // restart the loop } } if (lbs_strv_append_char (strv, stri, *p) < 0) { goto free_detail; } } if (is_delimed && lbs_strv_get_str_len (strv, stri) == 0) { lbs_strv_remove_str (strv); lbs_array_pop_back (detail); } if (detail_ptr != NULL) { *detail_ptr = detail; } #if __STDC_NO_VLA__ free (qllen); free (qrlen); #endif return strv; /* Error-handling goto label */ free_detail: if (detail != NULL) { lbs_array_unref (detail); } free_strv: #if __STDC_NO_VLA__ free (qllen); free (qrlen); #endif lbs_strv_unref (strv); return NULL; } LbsArgQopt* lbs_arg_qopt_new (const char* str) { LbsStrv* strv; LbsArray* detail; strv = lbs_arg_parse (str, ",", "\\", (LbsArgQuote[]) { { "\"", "\"", false }, { "\'", "\'", true }, { NULL, NULL, false}}, &detail); if (strv == NULL || detail == NULL) { return NULL; } /* goto label => free_detail_and_strv */ size_t strv_len = lbs_strv_get_len (strv); LbsArgQopt* qopt = malloc (sizeof (LbsArgQopt) + sizeof (LbsArgQoptItem) * (strv_len + 1)); if (qopt == NULL) { goto free_detail_and_strv; } qopt->len = strv_len; qopt->strv = NULL; qopt->detail = NULL; qopt->opts[strv_len] = (LbsArgQoptItem) { NULL, NULL }; for (size_t i = 0; i < strv_len; i++) { qopt->opts[i].name = lbs_strv_dup_str (strv, i); if (qopt->opts[i].name == NULL) { goto free_qopt; } char* pos = strchr (qopt->opts[i].name, '='); if (pos == NULL) { qopt->opts[i].value = NULL; } else { *pos = '\0'; qopt->opts[i].value = pos + 1; } } qopt->strv = strv; qopt->detail = detail; return qopt; free_qopt: lbs_arg_qopt_free (qopt); free_detail_and_strv: lbs_strv_unref (strv); lbs_array_unref (detail); return NULL; } void lbs_arg_qopt_free_generic (void* qopt_generic) { if (qopt_generic == NULL) { return; } LbsArgQopt* qopt = qopt_generic; lbs_strv_unref (qopt->strv); lbs_array_unref (qopt->detail); for (int i = 0; qopt->opts[i].name != NULL; i++) { free (qopt->opts[i].name); } free (qopt); }