/* * ezstream - source client for Icecast with external en-/decoder support * Copyright (C) 2003, 2004, 2005, 2006 Ed Zaleski * Copyright (C) 2007, 2009, 2015 Moritz Grimm * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include "compat.h" #include #include #include #include #include "ezconfig0.h" #include "log.h" #include "util.h" #include #define XML_CHAR(s) (const xmlChar *)(s) #define STD_CHAR(s) (const char *)(s) static EZCONFIG ezConfig; static const char *blankString = ""; unsigned int checkDecoderLine(const char *, const char *, long); unsigned int checkEncoderLine(const char *, const char *, long); unsigned int checkFormatLine(const char *, const char *, long); EZCONFIG * getEZConfig(void) { return (&ezConfig); } const char * getFormatEncoder(const char *format) { int i; for (i = 0; i < ezConfig.numEncoderDecoders; i++) { if (ezConfig.encoderDecoders[i] != NULL && ezConfig.encoderDecoders[i]->format != NULL && strcmp(ezConfig.encoderDecoders[i]->format, format) == 0) { if (ezConfig.encoderDecoders[i]->encoder != NULL) return (ezConfig.encoderDecoders[i]->encoder); else return (blankString); } } return (blankString); } const char * getFormatDecoder(const char *match) { int i; for (i = 0; i < ezConfig.numEncoderDecoders; i++) { if (ezConfig.encoderDecoders[i] != NULL && ezConfig.encoderDecoders[i]->match != NULL && strcmp(ezConfig.encoderDecoders[i]->match, match) == 0) { if (ezConfig.encoderDecoders[i]->decoder != NULL) return (ezConfig.encoderDecoders[i]->decoder); else return (blankString); } } return (blankString); } int parseConfig(const char *fileName) { xmlDocPtr doc; xmlNodePtr cur; xmlChar *ls_xmlContentPtr; int program_set, reconnect_set, shuffle_set, streamOnce_set, svrinfopublic_set, refresh_set; unsigned int config_error; xmlLineNumbersDefault(1); if ((doc = xmlParseFile(fileName)) == NULL) { log_error("%s: Parse error (not well-formed XML.)\n", fileName); return (0); } cur = xmlDocGetRootElement(doc); if (cur == NULL) { log_error("%s: Parse error (empty XML document.)\n", fileName); xmlFreeDoc(doc); return (0); } memset(&ezConfig, 0, sizeof(ezConfig)); ezConfig.metadataRefreshInterval = -1; config_error = 0; program_set = 0; reconnect_set = 0; refresh_set = 0; shuffle_set = 0; streamOnce_set = 0; svrinfopublic_set = 0; for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) { if (!xmlStrcmp(cur->name, (const xmlChar *)"url")) { if (ezConfig.URL != NULL) { log_error("%s[%ld]: Error: Cannot have multiple elements\n", fileName, xmlGetLineNo(cur)); config_error++; continue; } if (cur->xmlChildrenNode != NULL) { ls_xmlContentPtr = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); ezConfig.URL = strdup(STD_CHAR(ls_xmlContentPtr)); xmlFree(ls_xmlContentPtr); } } if (!xmlStrcmp(cur->name, (const xmlChar *)"sourceuser")) { if (ezConfig.username != NULL) { log_error("%s[%ld]: Error: Cannot have multiple elements\n", fileName, xmlGetLineNo(cur)); config_error++; continue; } if (cur->xmlChildrenNode != NULL) { ls_xmlContentPtr = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); ezConfig.username = strdup(STD_CHAR(ls_xmlContentPtr)); xmlFree(ls_xmlContentPtr); } } if (!xmlStrcmp(cur->name, (const xmlChar *)"sourcepassword")) { if (ezConfig.password != NULL) { log_error("%s[%ld]: Error: Cannot have multiple elements\n", fileName, xmlGetLineNo(cur)); config_error++; continue; } if (cur->xmlChildrenNode != NULL) { ls_xmlContentPtr = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); ezConfig.password = strdup(STD_CHAR(ls_xmlContentPtr)); xmlFree(ls_xmlContentPtr); } } if (!xmlStrcmp(cur->name, (const xmlChar *)"format")) { if (ezConfig.format != NULL) { log_error("%s[%ld]: Error: Cannot have multiple elements\n", fileName, xmlGetLineNo(cur)); config_error++; continue; } if (cur->xmlChildrenNode != NULL) { char *p; ls_xmlContentPtr = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); ezConfig.format = strdup(STD_CHAR(ls_xmlContentPtr)); xmlFree(ls_xmlContentPtr); for (p = ezConfig.format; *p != '\0'; p++) *p = (char)toupper((int)*p); } } if (!xmlStrcmp(cur->name, (const xmlChar *)"filename")) { if (ezConfig.fileName != NULL) { log_error("%s[%ld]: Error: Cannot have multiple elements\n", fileName, xmlGetLineNo(cur)); config_error++; continue; } if (cur->xmlChildrenNode != NULL) { ls_xmlContentPtr = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); if (strlen(STD_CHAR(ls_xmlContentPtr)) > PATH_MAX - 1) { log_error("%s[%ld]: Error: Path or filename in is too long\n", fileName, xmlGetLineNo(cur)); config_error++; continue; } ezConfig.fileName = strdup(STD_CHAR(ls_xmlContentPtr)); xmlFree(ls_xmlContentPtr); } } if (!xmlStrcmp(cur->name, (const xmlChar *)"metadata_progname")) { if (ezConfig.metadataProgram != NULL) { log_error("%s[%ld]: Error: Cannot have multiple elements\n", fileName, xmlGetLineNo(cur)); config_error++; continue; } if (cur->xmlChildrenNode != NULL) { ls_xmlContentPtr = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); if (strlen(STD_CHAR(ls_xmlContentPtr)) > PATH_MAX - 1) { log_error("%s[%ld]: Error: Path or filename in is too long\n", fileName, xmlGetLineNo(cur)); config_error++; continue; } ezConfig.metadataProgram = strdup(STD_CHAR(ls_xmlContentPtr)); xmlFree(ls_xmlContentPtr); } } if (!xmlStrcmp(cur->name, (const xmlChar *)"metadata_format")) { if (ezConfig.metadataFormat != NULL) { log_error("%s[%ld]: Error: Cannot have multiple elements\n", fileName, xmlGetLineNo(cur)); config_error++; continue; } if (cur->xmlChildrenNode != NULL) { unsigned int ret; ls_xmlContentPtr = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); ezConfig.metadataFormat = strdup(STD_CHAR(ls_xmlContentPtr)); xmlFree(ls_xmlContentPtr); if ((ret = checkFormatLine(ezConfig.metadataFormat, fileName, xmlGetLineNo(cur))) > 0) { config_error += ret; continue; } } } if (!xmlStrcmp(cur->name, (const xmlChar *)"metadata_refreshinterval")) { if (refresh_set) { log_error("%s[%ld]: Error: Cannot have multiple elements\n", fileName, xmlGetLineNo(cur)); config_error++; continue; } if (cur->xmlChildrenNode != NULL) { const char *errstr; ls_xmlContentPtr = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); ezConfig.metadataRefreshInterval = (int)strtonum(STD_CHAR(ls_xmlContentPtr), -1LL, (long long)INT_MAX, &errstr); if (errstr) { log_error("%s[%ld]: Error: In : '%s' is %s\n", fileName, xmlGetLineNo(cur), STD_CHAR(ls_xmlContentPtr), errstr); config_error++; continue; } xmlFree(ls_xmlContentPtr); refresh_set = 1; } } if (!xmlStrcmp(cur->name, (const xmlChar *)"playlist_program")) { if (program_set) { log_error("%s[%ld]: Error: Cannot have multiple elements\n", fileName, xmlGetLineNo(cur)); config_error++; continue; } if (cur->xmlChildrenNode != NULL) { const char *errstr; ls_xmlContentPtr = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); ezConfig.fileNameIsProgram = (int)strtonum(STD_CHAR(ls_xmlContentPtr), 0LL, 1LL, &errstr); if (errstr) { log_error("%s[%ld]: Error: may only contain 1 or 0\n", fileName, xmlGetLineNo(cur)); config_error++; continue; } xmlFree(ls_xmlContentPtr); program_set = 1; } } if (!xmlStrcmp(cur->name, (const xmlChar *)"shuffle")) { if (shuffle_set) { log_error("%s[%ld]: Error: Cannot have multiple elements\n", fileName, xmlGetLineNo(cur)); config_error++; continue; } if (cur->xmlChildrenNode != NULL) { const char *errstr; ls_xmlContentPtr = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); ezConfig.shuffle = (int)strtonum(STD_CHAR(ls_xmlContentPtr), 0LL, 1LL, &errstr); if (errstr) { log_error("%s[%ld]: Error: may only contain 1 or 0\n", fileName, xmlGetLineNo(cur)); config_error++; continue; } xmlFree(ls_xmlContentPtr); shuffle_set = 1; } } if (!xmlStrcmp(cur->name, (const xmlChar *)"stream_once")) { if (streamOnce_set) { log_error("%s[%ld]: Error: Cannot have multiple elements\n", fileName, xmlGetLineNo(cur)); config_error++; continue; } if (cur->xmlChildrenNode != NULL) { const char *errstr; ls_xmlContentPtr = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); ezConfig.streamOnce = (int)strtonum(STD_CHAR(ls_xmlContentPtr), 0LL, 1LL, &errstr); if (errstr) { log_error("%s[%ld]: Error: may only contain 1 or 0\n", fileName, xmlGetLineNo(cur)); config_error++; continue; } xmlFree(ls_xmlContentPtr); streamOnce_set = 1; } } if (!xmlStrcmp(cur->name, (const xmlChar *)"reconnect_tries")) { if (reconnect_set) { log_error("%s[%ld]: Error: Cannot have multiple elements\n", fileName, xmlGetLineNo(cur)); config_error++; continue; } if (cur->xmlChildrenNode != NULL) { const char *errstr; ls_xmlContentPtr = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); ezConfig.reconnectAttempts = (unsigned int)strtonum(STD_CHAR(ls_xmlContentPtr), 0LL, (long long)UINT_MAX, &errstr); if (errstr) { log_error("%s[%ld]: Error: In : '%s' is %s\n", fileName, xmlGetLineNo(cur), STD_CHAR(ls_xmlContentPtr), errstr); config_error++; continue; } xmlFree(ls_xmlContentPtr); reconnect_set = 1; } } if (!xmlStrcmp(cur->name, (const xmlChar *)"svrinfoname")) { if (ezConfig.serverName != NULL) { log_error("%s[%ld]: Error: Cannot have multiple elements\n", fileName, xmlGetLineNo(cur)); config_error++; continue; } if (cur->xmlChildrenNode != NULL) { ls_xmlContentPtr = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); ezConfig.serverName = strdup(STD_CHAR(ls_xmlContentPtr)); xmlFree(ls_xmlContentPtr); } } if (!xmlStrcmp(cur->name, (const xmlChar *)"svrinfourl")) { if (ezConfig.serverURL != NULL) { log_error("%s[%ld]: Error: Cannot have multiple elements\n", fileName, xmlGetLineNo(cur)); config_error++; continue; } if (cur->xmlChildrenNode != NULL) { ls_xmlContentPtr = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); ezConfig.serverURL = strdup(STD_CHAR(ls_xmlContentPtr)); xmlFree(ls_xmlContentPtr); } } if (!xmlStrcmp(cur->name, (const xmlChar *)"svrinfogenre")) { if (ezConfig.serverGenre != NULL) { log_error("%s[%ld]: Error: Cannot have multiple elements\n", fileName, xmlGetLineNo(cur)); config_error++; continue; } if (cur->xmlChildrenNode != NULL) { ls_xmlContentPtr = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); ezConfig.serverGenre = strdup(STD_CHAR(ls_xmlContentPtr)); xmlFree(ls_xmlContentPtr); } } if (!xmlStrcmp(cur->name, (const xmlChar *)"svrinfodescription")) { if (ezConfig.serverDescription != NULL) { log_error("%s[%ld]: Error: Cannot have multiple elements\n", fileName, xmlGetLineNo(cur)); config_error++; continue; } if (cur->xmlChildrenNode != NULL) { ls_xmlContentPtr = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); ezConfig.serverDescription = strdup(STD_CHAR(ls_xmlContentPtr)); xmlFree(ls_xmlContentPtr); } } if (!xmlStrcmp(cur->name, (const xmlChar *)"svrinfobitrate")) { if (ezConfig.serverBitrate != NULL) { log_error("%s[%ld]: Error: Cannot have multiple elements\n", fileName, xmlGetLineNo(cur)); config_error++; continue; } if (cur->xmlChildrenNode != NULL) { ls_xmlContentPtr = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); ezConfig.serverBitrate = strdup(STD_CHAR(ls_xmlContentPtr)); xmlFree(ls_xmlContentPtr); } } if (!xmlStrcmp(cur->name, (const xmlChar *)"svrinfochannels")) { if (ezConfig.serverChannels != NULL) { log_error("%s[%ld]: Error: Cannot have multiple elements\n", fileName, xmlGetLineNo(cur)); config_error++; continue; } if (cur->xmlChildrenNode != NULL) { ls_xmlContentPtr = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); ezConfig.serverChannels = strdup(STD_CHAR(ls_xmlContentPtr)); xmlFree(ls_xmlContentPtr); } } if (!xmlStrcmp(cur->name, (const xmlChar *)"svrinfosamplerate")) { if (ezConfig.serverSamplerate != NULL) { log_error("%s[%ld]: Error: Cannot have multiple elements\n", fileName, xmlGetLineNo(cur)); config_error++; continue; } if (cur->xmlChildrenNode != NULL) { ls_xmlContentPtr = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); ezConfig.serverSamplerate = strdup(STD_CHAR(ls_xmlContentPtr)); xmlFree(ls_xmlContentPtr); } } if (!xmlStrcmp(cur->name, (const xmlChar *)"svrinfoquality")) { if (ezConfig.serverQuality != NULL) { log_error("%s[%ld]: Error: Cannot have multiple elements\n", fileName, xmlGetLineNo(cur)); config_error++; continue; } if (cur->xmlChildrenNode != NULL) { ls_xmlContentPtr = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); ezConfig.serverQuality = strdup(STD_CHAR(ls_xmlContentPtr)); xmlFree(ls_xmlContentPtr); } } if (!xmlStrcmp(cur->name, (const xmlChar *)"svrinfopublic")) { if (svrinfopublic_set) { log_error("%s[%ld]: Error: Cannot have multiple elements\n", fileName, xmlGetLineNo(cur)); config_error++; continue; } if (cur->xmlChildrenNode != NULL) { const char *errstr; ls_xmlContentPtr = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); ezConfig.serverPublic = (int)strtonum(STD_CHAR(ls_xmlContentPtr), 0LL, 1LL, &errstr); if (errstr) { log_error("%s[%ld]: Error: may only contain 1 or 0\n", fileName, xmlGetLineNo(cur)); config_error++; continue; } xmlFree(ls_xmlContentPtr); svrinfopublic_set = 1; } } if (!xmlStrcmp(cur->name, (const xmlChar *)"reencode")) { xmlNodePtr cur2; int enable_set; enable_set = 0; for (cur2 = cur->xmlChildrenNode; cur2 != NULL; cur2 = cur2->next) { if (!xmlStrcmp(cur2->name, (const xmlChar *)"enable")) { if (enable_set) { log_error("%s[%ld]: Error: Cannot have multiple elements\n", fileName, xmlGetLineNo(cur)); config_error++; continue; } if (cur2->xmlChildrenNode != NULL) { const char *errstr; ls_xmlContentPtr = xmlNodeListGetString(doc, cur2->xmlChildrenNode, 1); ezConfig.reencode = (int)strtonum(STD_CHAR(ls_xmlContentPtr), 0LL, 1LL, &errstr); if (errstr) { log_error("%s[%ld]: Error: may only contain 1 or 0\n", fileName, xmlGetLineNo(cur)); config_error++; continue; } xmlFree(ls_xmlContentPtr); enable_set = 1; } } if (!xmlStrcmp(cur2->name, (const xmlChar *)"encdec")) { xmlNodePtr cur3; FORMAT_ENCDEC *pformatEncDec; pformatEncDec = calloc(1UL, sizeof(FORMAT_ENCDEC)); for (cur3 = cur2->xmlChildrenNode; cur3 != NULL; cur3 = cur3->next) { if (!xmlStrcmp(cur3->name, (const xmlChar *)"format")) { if (pformatEncDec->format != NULL) { log_error("%s[%ld]: Error: Cannot have multiple elements\n", fileName, xmlGetLineNo(cur3)); config_error++; continue; } if (cur3->xmlChildrenNode != NULL) { char *p; ls_xmlContentPtr = xmlNodeListGetString(doc, cur3->xmlChildrenNode, 1); pformatEncDec->format = strdup(STD_CHAR(ls_xmlContentPtr)); xmlFree(ls_xmlContentPtr); for (p = pformatEncDec->format; *p != '\0'; p++) *p = (char)toupper((int)*p); } } if (!xmlStrcmp(cur3->name, (const xmlChar *)"match")) { if (pformatEncDec->match != NULL) { log_error("%s[%ld]: Error: Cannot have multiple elements\n", fileName, xmlGetLineNo(cur3)); config_error++; continue; } if (cur3->xmlChildrenNode != NULL) { char *p; ls_xmlContentPtr = xmlNodeListGetString(doc, cur3->xmlChildrenNode, 1); pformatEncDec->match = strdup(STD_CHAR(ls_xmlContentPtr)); xmlFree(ls_xmlContentPtr); for (p = pformatEncDec->match; *p != '\0'; p++) *p = (char)tolower((int)*p); } } if (!xmlStrcmp(cur3->name, (const xmlChar *)"decode")) { if (pformatEncDec->decoder != NULL) { log_error("%s[%ld]: Error: Cannot have multiple elements\n", fileName, xmlGetLineNo(cur3)); config_error++; continue; } if (cur3->xmlChildrenNode != NULL) { unsigned int ret; ls_xmlContentPtr = xmlNodeListGetString(doc, cur3->xmlChildrenNode, 1); pformatEncDec->decoder = strdup(STD_CHAR(ls_xmlContentPtr)); xmlFree(ls_xmlContentPtr); if ((ret = checkDecoderLine(pformatEncDec->decoder, fileName, xmlGetLineNo(cur3))) > 0) { config_error += ret; continue; } } } if (!xmlStrcmp(cur3->name, (const xmlChar *)"encode")) { if (pformatEncDec->encoder != NULL) { log_error("%s[%ld]: Error: Cannot have multiple elements\n", fileName, xmlGetLineNo(cur3)); config_error++; continue; } if (cur3->xmlChildrenNode != NULL) { unsigned int ret; ls_xmlContentPtr = xmlNodeListGetString(doc, cur3->xmlChildrenNode, 1); pformatEncDec->encoder = strdup(STD_CHAR(ls_xmlContentPtr)); xmlFree(ls_xmlContentPtr); if ((ret = checkEncoderLine(pformatEncDec->encoder, fileName, xmlGetLineNo(cur3))) > 0) { config_error += ret; continue; } } } } ezConfig.encoderDecoders[ezConfig.numEncoderDecoders] = pformatEncDec; ezConfig.numEncoderDecoders++; } } } } xmlFreeDoc(doc); if (config_error == 0) return (1); freeConfig(&ezConfig); log_error("%u configuration error(s) in %s\n", config_error, fileName); return (0); } void freeConfig(EZCONFIG *cfg) { unsigned int i; if (cfg == NULL) return; free(cfg->URL); free(cfg->password); free(cfg->format); free(cfg->fileName); free(cfg->metadataProgram); free(cfg->metadataFormat); free(cfg->serverName); free(cfg->serverURL); free(cfg->serverGenre); free(cfg->serverDescription); free(cfg->serverBitrate); free(cfg->serverChannels); free(cfg->serverSamplerate); free(cfg->serverQuality); for (i = 0; i < MAX_FORMAT_ENCDEC; i++) { if (NULL == cfg->encoderDecoders[i]) continue; free(cfg->encoderDecoders[i]->format); free(cfg->encoderDecoders[i]->match); free(cfg->encoderDecoders[i]->encoder); free(cfg->encoderDecoders[i]->decoder); free(cfg->encoderDecoders[i]); } memset(cfg, 0, sizeof(*cfg)); } unsigned int checkDecoderLine(const char *str, const char *file, long line) { unsigned int errors; char *p; int have_track = 0; errors = 0; if ((p = strstr(str, STRING_PLACEHOLDER)) != NULL) { log_error("%s[%ld]: Error: `%s' placeholder not allowed in decoder command\n", file, line, STRING_PLACEHOLDER); errors++; } if ((p = strstr(str, TRACK_PLACEHOLDER)) != NULL) { p += strlen(TRACK_PLACEHOLDER); if ((p = strstr(p, TRACK_PLACEHOLDER)) != NULL) { log_error("%s[%ld]: Error: Multiple `%s' placeholders in decoder command\n", file, line, TRACK_PLACEHOLDER); errors++; } else have_track = 1; } if ((p = strstr(str, METADATA_PLACEHOLDER)) != NULL) { p += strlen(METADATA_PLACEHOLDER); if ((p = strstr(p, METADATA_PLACEHOLDER)) != NULL) { log_error("%s[%ld]: Error: Multiple `%s' placeholders in decoder command\n", file, line, METADATA_PLACEHOLDER); errors++; } } if ((p = strstr(str, ARTIST_PLACEHOLDER)) != NULL) { p += strlen(ARTIST_PLACEHOLDER); if ((p = strstr(p, ARTIST_PLACEHOLDER)) != NULL) { log_error("%s[%ld]: Error: Multiple `%s' placeholders in decoder command\n", file, line, ARTIST_PLACEHOLDER); errors++; } } if ((p = strstr(str, TITLE_PLACEHOLDER)) != NULL) { p += strlen(TITLE_PLACEHOLDER); if ((p = strstr(p, TITLE_PLACEHOLDER)) != NULL) { log_error("%s[%ld]: Error: Multiple `%s' placeholders in decoder command\n", file, line, TITLE_PLACEHOLDER); errors++; } } if (!have_track) { log_error("%s[%ld]: Error: The decoder command requires the '%s' track placeholder\n", file, line, TRACK_PLACEHOLDER); errors++; } return (errors); } unsigned int checkEncoderLine(const char *str, const char *file, long line) { unsigned int errors; char *p; errors = 0; if ((p = strstr(str, TRACK_PLACEHOLDER)) != NULL) { log_error("%s[%ld]: Error: `%s' placeholder not allowed in encoder command\n", file, line, TRACK_PLACEHOLDER); errors++; } if ((p = strstr(str, STRING_PLACEHOLDER)) != NULL) { log_error("%s[%ld]: Error: `%s' placeholder not allowed in encoder command\n", file, line, STRING_PLACEHOLDER); errors++; } if ((p = strstr(str, METADATA_PLACEHOLDER)) != NULL) { p += strlen(METADATA_PLACEHOLDER); if ((p = strstr(p, METADATA_PLACEHOLDER)) != NULL) { log_error("%s[%ld]: Error: Multiple `%s' placeholders in encoder command\n", file, line, METADATA_PLACEHOLDER); errors++; } } if ((p = strstr(str, ARTIST_PLACEHOLDER)) != NULL) { p += strlen(ARTIST_PLACEHOLDER); if ((p = strstr(p, ARTIST_PLACEHOLDER)) != NULL) { log_error("%s[%ld]: Error: Multiple `%s' placeholders in encoder command\n", file, line, ARTIST_PLACEHOLDER); errors++; } } if ((p = strstr(str, TITLE_PLACEHOLDER)) != NULL) { p += strlen(TITLE_PLACEHOLDER); if ((p = strstr(p, TITLE_PLACEHOLDER)) != NULL) { log_error("%s[%ld]: Error: Multiple `%s' placeholders in encoder command\n", file, line, TITLE_PLACEHOLDER); errors++; } } return (errors); } unsigned int checkFormatLine(const char *str, const char *file, long line) { unsigned int errors; char *p; errors = 0; if ((p = strstr(str, METADATA_PLACEHOLDER)) != NULL) { log_error("%s[%ld]: Error: `%s' placeholder not allowed in \n", file, line, METADATA_PLACEHOLDER); errors++; } if ((p = strstr(str, TRACK_PLACEHOLDER)) != NULL) { p += strlen(TRACK_PLACEHOLDER); if ((p = strstr(p, TRACK_PLACEHOLDER)) != NULL) { log_error("%s[%ld]: Error: Multiple `%s' placeholders in \n", file, line, TRACK_PLACEHOLDER); errors++; } } if ((p = strstr(str, STRING_PLACEHOLDER)) != NULL) { p += strlen(STRING_PLACEHOLDER); if ((p = strstr(p, STRING_PLACEHOLDER)) != NULL) { log_error("%s[%ld]: Error: Multiple `%s' placeholders in \n", file, line, STRING_PLACEHOLDER); errors++; } } if ((p = strstr(str, ARTIST_PLACEHOLDER)) != NULL) { p += strlen(ARTIST_PLACEHOLDER); if ((p = strstr(p, ARTIST_PLACEHOLDER)) != NULL) { log_error("%s[%ld]: Error: Multiple `%s' placeholders in \n", file, line, ARTIST_PLACEHOLDER); errors++; } } if ((p = strstr(str, TITLE_PLACEHOLDER)) != NULL) { p += strlen(TITLE_PLACEHOLDER); if ((p = strstr(p, TITLE_PLACEHOLDER)) != NULL) { log_error("%s[%ld]: Error: Multiple `%s' placeholders in \n", file, line, TITLE_PLACEHOLDER); errors++; } } return (errors); }