static int parse_line(const char * line, char ** key, char ** value) { const char * line_begin = line; while (*line != '\0' && (*line != ' ' && *line != '\t' && *line != '=')) line ++; size_t key_len = line - line_begin; while (*line != '\0' && *line != '=') line ++; if (*line == '\0') return -1; assert(*line == '='); *key = mstrncpy(line_begin, key_len); line ++; while (*line != '\0' && (*line == ' ' || *line =='\t')) line ++; if (*line == '\0') { free(*key); return -1; } *value = mstrcpy(line); return 0; }
static void copy_rule_result(mchar * dest, mchar * query_string, regmatch_t * pmatch, int idx) { debug_printf("copy_rule_result: idx=%d\n", idx); if (idx > 0 && idx <= MAX_SUBMATCHES) { mstrncpy(dest, query_string + pmatch[idx].rm_so, pmatch[idx].rm_eo - pmatch[idx].rm_so + 1); } }
static void copy_rule_result(mchar * dest, GMatchInfo * match_info, int idx) { gchar *match = g_match_info_fetch(match_info, idx); if (!match) return; debug_printf("copy_rule_result: idx=%d\n", idx); if (idx > 0 && idx <= MAX_SUBMATCHES) { mstrncpy(dest, match, MAX_METADATA_LEN); } }
/** * Parse the pattern part of a leaf. */ static int c2_parse_pattern(session_t *ps, const char *pattern, int offset, c2_ptr_t *presult) { c2_l_t * const pleaf = presult->l; // Exists operator cannot have pattern if (!pleaf->op) return offset; C2H_SKIP_SPACES(); char *endptr = NULL; // Check for boolean patterns if (!strcmp_wd("true", &pattern[offset])) { pleaf->ptntype = C2_L_PTINT; pleaf->ptnint = true; offset += strlen("true"); } else if (!strcmp_wd("false", &pattern[offset])) { pleaf->ptntype = C2_L_PTINT; pleaf->ptnint = false; offset += strlen("false"); } // Check for integer patterns else if (pleaf->ptnint = strtol(pattern + offset, &endptr, 0), pattern + offset != endptr) { pleaf->ptntype = C2_L_PTINT; offset = endptr - pattern; // Make sure we are stopping at the end of a word if (isalnum(pattern[offset])) c2_error("Trailing characters after a numeric pattern."); } // Check for string patterns else { bool raw = false; char delim = '\0'; // String flags if ('r' == tolower(pattern[offset])) { raw = true; ++offset; C2H_SKIP_SPACES(); } // Check for delimiters if ('\"' == pattern[offset] || '\'' == pattern[offset]) { pleaf->ptntype = C2_L_PTSTRING; delim = pattern[offset]; ++offset; } if (C2_L_PTSTRING != pleaf->ptntype) c2_error("Invalid pattern type."); // Parse the string now // We can't determine the length of the pattern, so we use the length // to the end of the pattern string -- currently escape sequences // cannot be converted to a string longer than itself. char *tptnstr = malloc((strlen(pattern + offset) + 1) * sizeof(char)); char *ptptnstr = tptnstr; pleaf->ptnstr = tptnstr; for (; pattern[offset] && delim != pattern[offset]; ++offset) { // Handle escape sequences if it's not a raw string if ('\\' == pattern[offset] && !raw) { switch(pattern[++offset]) { case '\\': *(ptptnstr++) = '\\'; break; case '\'': *(ptptnstr++) = '\''; break; case '\"': *(ptptnstr++) = '\"'; break; case 'a': *(ptptnstr++) = '\a'; break; case 'b': *(ptptnstr++) = '\b'; break; case 'f': *(ptptnstr++) = '\f'; break; case 'n': *(ptptnstr++) = '\n'; break; case 'r': *(ptptnstr++) = '\r'; break; case 't': *(ptptnstr++) = '\t'; break; case 'v': *(ptptnstr++) = '\v'; break; case 'o': case 'x': { char *tstr = mstrncpy(pattern + offset + 1, 2); char *pstr = NULL; long val = strtol(tstr, &pstr, ('o' == pattern[offset] ? 8: 16)); free(tstr); if (pstr != &tstr[2] || val <= 0) c2_error("Invalid octal/hex escape sequence."); assert(val < 256 && val >= 0); *(ptptnstr++) = val; offset += 2; break; } default: c2_error("Invalid escape sequence."); } } else { *(ptptnstr++) = pattern[offset]; } } if (!pattern[offset]) c2_error("Premature end of pattern string."); ++offset; *ptptnstr = '\0'; pleaf->ptnstr = mstrcpy(tptnstr); free(tptnstr); } C2H_SKIP_SPACES(); if (!pleaf->ptntype) c2_error("Invalid pattern type."); // Check if the type is correct if (!(((C2_L_TSTRING == pleaf->type || C2_L_TATOM == pleaf->type) && C2_L_PTSTRING == pleaf->ptntype) || ((C2_L_TCARDINAL == pleaf->type || C2_L_TWINDOW == pleaf->type || C2_L_TDRAWABLE == pleaf->type) && C2_L_PTINT == pleaf->ptntype))) c2_error("Pattern type incompatible with target type."); if (C2_L_PTINT == pleaf->ptntype && pleaf->match) c2_error("Integer/boolean pattern cannot have operator qualifiers."); if (C2_L_PTINT == pleaf->ptntype && pleaf->match_ignorecase) c2_error("Integer/boolean pattern cannot have flags."); if (C2_L_PTSTRING == pleaf->ptntype && (C2_L_OGT == pleaf->op || C2_L_OGTEQ == pleaf->op || C2_L_OLT == pleaf->op || C2_L_OLTEQ == pleaf->op)) c2_error("String pattern cannot have an arithmetic operator."); return offset; }
/** * Parse the target part of a rule. */ static int c2_parse_target(session_t *ps, const char *pattern, int offset, c2_ptr_t *presult) { // Initialize leaf presult->isbranch = false; presult->l = malloc(sizeof(c2_l_t)); if (!presult->l) c2_error("Failed to allocate memory for new leaf."); c2_l_t * const pleaf = presult->l; memcpy(pleaf, &leaf_def, sizeof(c2_l_t)); // Parse negation marks while ('!' == pattern[offset]) { pleaf->neg = !pleaf->neg; ++offset; C2H_SKIP_SPACES(); } // Copy target name out unsigned tgtlen = 0; for (; pattern[offset] && (isalnum(pattern[offset]) || '_' == pattern[offset]); ++offset) { ++tgtlen; } if (!tgtlen) c2_error("Empty target."); pleaf->tgt = mstrncpy(&pattern[offset - tgtlen], tgtlen); // Check for predefined targets for (unsigned i = 1; i < sizeof(C2_PREDEFS) / sizeof(C2_PREDEFS[0]); ++i) { if (!strcmp(C2_PREDEFS[i].name, pleaf->tgt)) { pleaf->predef = i; pleaf->type = C2_PREDEFS[i].type; pleaf->format = C2_PREDEFS[i].format; break; } } // Alias for predefined targets if (!pleaf->predef) { #define TGTFILL(pdefid) \ (pleaf->predef = pdefid, \ pleaf->type = C2_PREDEFS[pdefid].type, \ pleaf->format = C2_PREDEFS[pdefid].format) // if (!strcmp("WM_NAME", tgt) || !strcmp("_NET_WM_NAME", tgt)) // TGTFILL(C2_L_PNAME); #undef TGTFILL // Alias for custom properties #define TGTFILL(target, type, format) \ (pleaf->target = mstrcpy(target), \ pleaf->type = type, \ pleaf->format = format) // if (!strcmp("SOME_ALIAS")) // TGTFILL("ALIAS_TEXT", C2_L_TSTRING, 32); #undef TGTFILL } C2H_SKIP_SPACES(); // Parse target-on-frame flag if ('@' == pattern[offset]) { pleaf->tgt_onframe = true; ++offset; C2H_SKIP_SPACES(); } // Parse index if ('[' == pattern[offset]) { offset++; C2H_SKIP_SPACES(); int index = -1; char *endptr = NULL; index = strtol(pattern + offset, &endptr, 0); if (!endptr || pattern + offset == endptr) c2_error("No index number found after bracket."); if (index < 0) c2_error("Index number invalid."); if (pleaf->predef) c2_error("Predefined targets can't have index."); pleaf->index = index; offset = endptr - pattern; C2H_SKIP_SPACES(); if (']' != pattern[offset]) c2_error("Index end marker not found."); ++offset; C2H_SKIP_SPACES(); } // Parse target type and format if (':' == pattern[offset]) { ++offset; C2H_SKIP_SPACES(); // Look for format bool hasformat = false; int format = 0; { char *endptr = NULL; format = strtol(pattern + offset, &endptr, 0); assert(endptr); if ((hasformat = (endptr && endptr != pattern + offset))) offset = endptr - pattern; C2H_SKIP_SPACES(); } // Look for type enum c2_l_type type = C2_L_TUNDEFINED; { switch (pattern[offset]) { case 'w': type = C2_L_TWINDOW; break; case 'd': type = C2_L_TDRAWABLE; break; case 'c': type = C2_L_TCARDINAL; break; case 's': type = C2_L_TSTRING; break; case 'a': type = C2_L_TATOM; break; default: c2_error("Invalid type character."); } if (type) { if (pleaf->predef) { printf_errf("(): Warning: Type specified for a default target will be ignored."); } else { if (pleaf->type && type != pleaf->type) printf_errf("(): Warning: Default type overridden on target."); pleaf->type = type; } } offset++; C2H_SKIP_SPACES(); } // Default format if (!pleaf->format) { switch (pleaf->type) { case C2_L_TWINDOW: case C2_L_TDRAWABLE: case C2_L_TATOM: pleaf->format = 32; break; case C2_L_TSTRING: pleaf->format = 8; break; default: break; } } // Write format if (hasformat) { if (pleaf->predef) printf_errf("(): Warning: Format \"%d\" specified on a default target will be ignored.", format); else if (C2_L_TSTRING == pleaf->type) printf_errf("(): Warning: Format \"%d\" specified on a string target will be ignored.", format); else { if (pleaf->format && pleaf->format != format) printf_err("Warning: Default format %d overridden on target.", pleaf->format); pleaf->format = format; } } } if (!pleaf->type) c2_error("Target type cannot be determined."); // if (!pleaf->predef && !pleaf->format && C2_L_TSTRING != pleaf->type) // c2_error("Target format cannot be determined."); if (pleaf->format && 8 != pleaf->format && 16 != pleaf->format && 32 != pleaf->format) c2_error("Invalid format."); return offset; }
void parse_metadata(RIP_MANAGER_INFO * rmi, TRACK_INFO * ti) { int i; int eflags; int rc; int matched; mchar query_string[MAX_TRACK_LEN]; Parse_Rule *rulep; /* Has any m/.../s rule matched? */ BOOL save_track_matched = FALSE; /* Has any m/.../x rule matched? */ BOOL exclude_track_matched = FALSE; ti->artist[0] = 0; ti->title[0] = 0; ti->album[0] = 0; ti->composed_metadata[0] = 0; ti->save_track = TRUE; /* Loop through rules, if we find a matching rule, then use it */ /* For now, only default rules supported with ascii regular expressions. */ debug_printf("Converting query string to wide\n"); gstring_from_string(rmi, query_string, MAX_TRACK_LEN, ti->raw_metadata, CODESET_METADATA); for (rulep = rmi->parse_rules; rulep->cmd; rulep++) { #if !defined (USE_GLIB_REGEX) regmatch_t pmatch[MAX_SUBMATCHES + 1]; #endif eflags = 0; if (rulep->cmd == PARSERULE_CMD_MATCH) { debug_mprintf(m_("Testing match rule: ") m_S m_(" vs. ") m_S m_("\n"), query_string, rulep->match); if (rulep->flags & PARSERULE_SKIP) { #if defined (USE_GLIB_REGEX) rc = g_regex_match(rulep->reg, query_string, 0, NULL); matched = rc; #else rc = mregexec(rulep->reg, query_string, 0, NULL, eflags); matched = !rc; #endif if (!matched) { continue; } /* GCS FIX: We need to return to the caller that the metadata should be dropped. */ debug_printf("Skip rule matched\n"); ti->save_track = FALSE; ti->have_track_info = 0; return; } else if (rulep->flags & PARSERULE_SAVE) { #if defined (USE_GLIB_REGEX) rc = g_regex_match(rulep->reg, query_string, 0, NULL); matched = rc; #else rc = mregexec(rulep->reg, query_string, 0, NULL, eflags); matched = !rc; #endif if (!matched) { if (!save_track_matched) ti->save_track = FALSE; continue; } if (!exclude_track_matched) { ti->save_track = TRUE; save_track_matched = TRUE; } } else if (rulep->flags & PARSERULE_EXCLUDE) { #if defined (USE_GLIB_REGEX) rc = g_regex_match(rulep->reg, query_string, 0, NULL); matched = rc; #else rc = mregexec(rulep->reg, query_string, 0, NULL, eflags); matched = !rc; #endif if (matched && !save_track_matched) { /* Rule matched => Exclude track */ ti->save_track = FALSE; exclude_track_matched = TRUE; } } else { #if defined (USE_GLIB_REGEX) GMatchInfo *match_info; gint nmatch; rc = g_regex_match(rulep->reg, query_string, 0, &match_info); if (rc == 0) { /* Didn't match rule. */ continue; } nmatch = g_match_info_get_match_count(match_info); debug_printf("Got %d matches\n", nmatch); for (i = 0; i < nmatch; i++) { gchar *match = g_match_info_fetch(match_info, i); debug_printf("[%d] = %s\n", i, match); g_free(match); } copy_rule_result(ti->artist, match_info, rulep->artist_idx); copy_rule_result(ti->title, match_info, rulep->title_idx); copy_rule_result(ti->album, match_info, rulep->album_idx); copy_rule_result(ti->track_p, match_info, rulep->trackno_idx); copy_rule_result(ti->year, match_info, rulep->year_idx); g_match_info_free(match_info); #else eflags = 0; rc = mregexec(rulep->reg, query_string, MAX_SUBMATCHES + 1, pmatch, eflags); if (rc != 0) { /* Didn't match rule. */ continue; } for (i = 0; i < MAX_SUBMATCHES + 1; i++) { debug_printf("pmatch[%d]: (so,eo) = (%d,%d)\n", i, pmatch[i].rm_so, pmatch[i].rm_eo); } copy_rule_result(ti->artist, query_string, pmatch, rulep->artist_idx); copy_rule_result(ti->title, query_string, pmatch, rulep->title_idx); copy_rule_result(ti->album, query_string, pmatch, rulep->album_idx); copy_rule_result(ti->track_p, query_string, pmatch, rulep->trackno_idx); copy_rule_result(ti->year, query_string, pmatch, rulep->year_idx); #endif ti->have_track_info = 1; compose_metadata(rmi, ti); debug_mprintf(m_("Parsed track info.\n") m_("ARTIST: ") m_S m_("\n") m_("TITLE: ") m_S m_("\n") m_("ALBUM: ") m_S m_("\n") m_("TRACK: ") m_S m_("\n") m_("YEAR: ") m_S m_("\n"), ti->artist, ti->title, ti->album, ti->track_p, ti->year); return; } } else if (rulep->cmd == PARSERULE_CMD_SUBST) { #if defined (USE_GLIB_REGEX) GMatchInfo *match_info; gint start_pos, end_pos; gchar *tmp, *subst_string; debug_mprintf(m_("Testing subst rule: ") m_S m_(" vs. ") m_S m_("\n"), query_string, rulep->match); rc = g_regex_match(rulep->reg, query_string, 0, &match_info); if (rc == 0) { /* Didn't match rule. */ continue; } rc = g_match_info_fetch_pos(match_info, 0, &start_pos, &end_pos); if (!rc) { debug_printf("g_match_info_fetch_pos returned 0\n"); g_match_info_free(match_info); continue; } debug_printf("Matched at (%d,%d)\n", start_pos, end_pos); if (start_pos == -1) { g_match_info_free(match_info); continue; } tmp = g_strndup(query_string, start_pos); tmp[start_pos] = 0; subst_string = g_strconcat(tmp, rulep->subst, &tmp[end_pos], NULL); g_free(tmp); g_match_info_free(match_info); mstrncpy(query_string, subst_string, MAX_TRACK_LEN); #else mchar subst_string[MAX_TRACK_LEN]; int used, left; debug_mprintf(m_("Testing subst rule: ") m_S m_(" vs. ") m_S m_("\n"), query_string, rulep->match); rc = mregexec(rulep->reg, query_string, 1, pmatch, eflags); if (rc != 0) { /* Didn't match rule. */ continue; } /* Update the query string and continue. */ debug_printf("Matched at (%d,%d)\n", pmatch[0].rm_so, pmatch[0].rm_eo); mstrncpy(subst_string, query_string, pmatch[0].rm_so + 1); debug_mprintf(m_("(1) subst_string = ") m_S m_("\n"), subst_string); used = pmatch[0].rm_so; left = MAX_TRACK_LEN - used; mstrncpy(subst_string + used, rulep->subst, left); debug_mprintf(m_("(2) subst_string = ") m_S m_("\n"), subst_string); used += mstrlen(rulep->subst); left = MAX_TRACK_LEN - used; mstrncpy(subst_string + used, query_string + pmatch[0].rm_eo, left); debug_mprintf(m_("(3) subst_string = ") m_S m_("\n"), subst_string); mstrncpy(query_string, subst_string, MAX_TRACK_LEN); debug_mprintf(m_("(4) query_string = ") m_S m_("\n"), query_string); #endif } } debug_printf("Fell through while parsing data...\n"); mstrncpy(ti->title, query_string, MAX_TRACK_LEN); ti->have_track_info = 1; compose_metadata(rmi, ti); }