#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
/**
* This is a very simple program that would parse ASX files and extract
* the URL to be played from the first entry in the file.
* This file format (ASX) is a XML based format that is used in the
* IP tivi broadcasting for specifying the URL to be used when downloading
* an IP TV stream.
* Generally and ASX file would have the following format:
* <ASX version = "3.0">
* <ENTRY>
* <TITLE>CityTV live (720x576)</TITLE>
* <AUTHOR>CityTV</AUTHOR>
* <COPYRIGHT>CityTV</COPYRIGHT>
* <REF HREF = "http://traffic.iptv.bg:8080/IPTV.bg/..." />
* </ENTRY>
* </ASX>
* This program will aceept one parameter which is the local path to the
* ASX file and will parse it to obtain the HREF value from the REF
* tag. After that it will print the value ot this attribute to the
* standard output.
* This can be later used in a shell script to start a video playing
* program on that URL. FOr examle one could create a shell script to
* invoke
* mplayer `opentv file.asx`
*/
/**
* A list of command line parameters which will trigger this program to
* show the supported command line options.
*/
char* HELP[] = {
"-help", "--help", "-h", "--h", "-?", "--?",
"/help", "/h", "/?", NULL
};
/**
* A list of command line parameters, which could be used to specify the
* local file path to the asx file.
*/
char* URL[] = {
"-url", "--url", "-u", "--u", "/url", "/u", NULL
};
/**
* A static variable that holds a reference to the location of the asx file
* to be parsed.
*/
static char* url = NULL;
/**
* This function is used in command line arguments parsing. The first
* parameter of this function would usually be the current command line
* parameter. The second parameter would usually be one of the string lists
* containig supported command line parameters, such as HELP or URL.
* In that case this function will loop through the whole list of parameters
* specified as second parameter and will check whether the first
* parameter equals to at least one of them.
* It will return 1 in case a match is found and 0 in case there is no
* match.
* Imput
* str - a string to be compared with every element in the match array
* match - an array of strings. Each of the elements of this array will
* be compared with the str parameter.
* Output
* 1 - in case a match is found. 1 means true.
* 0 - in case there is no match. 0 means false.
*/
int matches(char* str, char* match[]) {
int j = 0;
if (str == NULL) {
return 0;
}
if (match == NULL) {
return 0;
}
for (j = 0; 1; j++) {
if (match[j] == NULL || match[j][0] == '\0') {
return 0;
}
if (strcmp(match[j], str) == 0) {
return 1;
}
}
return 0;
}
/**
* This function prints the command line help screen of this program.
* The help screen would be printed on the standard output.
*/
void printHelp()
{
printf("Help\r\n");
}
/**
* This function loops through all of the command line parameters and
* compares them with the supported command line arguments. The main
* purpose of that function is to determine the URL of the local ASX file
* to be parsed.
* The URL will be set in the static global variable 'url'.
* Input
* argc - The number of command line parameters.
* argv - The array that contains the command line arguments.
*/
void parseArguments(int argc, char* argv[])
{
int i =0;
for (i = 0; i < argc; i++)
{
if (matches(argv[i], HELP) == 1) {
printHelp();
return;
} else if (matches(argv[i], URL) == 1) {
if (i + 1 < argc) {
url = argv[i+1];
i++;
} else {
fprintf(stderr, "ERROR: Need argument after parameter \"%s\".", argv[i]);
printHelp();
fprintf(stderr, "ERROR: Need argument after parameter \"%s\".", argv[i]);
}
} else {
url = argv[i];
}
}
}
/**
* This function is used when parsing the content of the ASX file.
* It will skip any character untill it reaches a beginning of a new
* XML tag ('<').
* This function will return a pointer to the '<' character.
* In case the end of the string (file content) is reached before
* a tag ('<') appears, then this function will just simply return NULL
* reference.
* Input
* str - A pointer to a character string that contains the content
* of the ASX file or some porting of it (starting at some
* position within the content will the end of the stream).
* Output
* NULL - If the str parameter is NULL or if the the end of the stream
* is reached before reaching a '<' character.
* A pointer to the first occurance of the '<' character.
*/
char* skipToNextTag(char* str) {
char* p = str;
if (p == NULL || *p == '\0') {
return NULL;
}
while (*p != '\0' && *p != '<') {
p++;
}
if (*p == '\0') {
return NULL;
}
return p;
}
/**
* The result from this function will be allocated in the heap so you
* have to release it once you have finished using it. Please use
* the free function to release it. It would work OK even if the result
* is a NULL pointer.
*/
char* getTagName(char* str) {
char* p = str;
char* q = str;
char* result = NULL;
if (str == NULL || *str == '\0') {
return NULL;
}
if (*p != '<') {
return NULL;
}
p++;
q++;
do {
q++;
} while (*q != '\0' && !isblank(*q) && (isalpha(*q) || isdigit(*q) || *q == ':' ) );
if (q == p) {
return NULL;
}
if (q < p) {
return NULL;
}
int len = q - p;
result = (char*)malloc((len + 1 ) * sizeof(char));
if (result == NULL) {
fprintf(stderr, "ERROR: Cannot allocate space for %li characters to store the name of the element being parsed!", (long int) len);
return NULL;
}
strncpy(result, p, len);
result[len] = '\0';
return result;
}
/**
* This function expects that hte str parameter points to the '<' character
* in a begging of a XML tag and will skip all the characters till it
* reaches the end ofthe tag (the closing '>' cgaracter).
* The result will be a pointer just after the '>' character.
* Input
* str - a pinter to a '<' character wintin a character stream (string)
* Output
* NULL - If the str parameter is NULL or
* the function reaches the end of the stream before it reaches
* the closing '>' characcter.
* A pointer to the next character after the closing '>' character.
*/
char* skipTag(char* str) {
char* p = str;
if (p == NULL || *p == '\0') {
return NULL;
}
do {
p++;
} while (*p != '\0' && *p != '>');
if (*p == '\0') {
return NULL;
} else {
p++;
if (*p == '\0') {
return NULL;
}
}
return p;
}
/**
* This funtion expects the the str parameter is pointing to a beginning
* of a XML tag ( the starting '<' character). The second parameter is a
* name of a XML attribute.
* The function will try to parse the XML tag and obtain the value of the
* given attribute and return it as a result.
* Input
* str - The XML tag to be parsed.
* attrName - The name of the attribute we are interesed in.
* Output
* NULL - If the str parameter is NULL or if we reach either the end of
* the stream or the closing '>' character before we find an
* attribute that matches the given attribute name.
* A string containg the value of the given attribute.
*/
char* getValueOfAttribute(char* str, char* attrName) {
char* p = str;
char* q = p;
char* attr = NULL;
char* value = NULL;
int len = 0;
int state = 0;
if (p == NULL || *p == '\0') {
return NULL;
}
if (*p == '<') {
p++;
}
while (*p != '\0' && *p != '>' && ! isblank(*p) && ( isalpha(*p) || isdigit(*p) || *p == ':')) {
p++;
}
if (*p == '\0' || *p == '>') {
return NULL;
}
q = p;
while (1) {
switch (state) {
case 0: while (*q != '\0' && *q != '>' && ( isblank(*q) || ! (isalpha(*q) || isdigit(*q) || *q == ':'))) { q++;}
if (*q == '\0' || *q == '>') {
return NULL;
}
state = 1;
break;
case 1: p = q;
while (*q != '\0' && *q != '>' && !isblank(*q) && ( isalpha(*q) || isdigit(*q) || *q == ':')) { q++; }
if (*q == '\0' || *q == '>') {
return NULL;
}
len = q - p;
if (len <=0) {
state = 0;
break;
}
attr = (char*)malloc((len + 1) * sizeof(char));
if (attr == NULL) {
fprintf(stderr, "ERROR: Cannot allocate %li characters for stroting attribute.", (long int)len);
return NULL;
}
strncpy(attr, p, len);
attr[len] = '\0';
state = 2;
break;
case 2: while (*q != '\0' && *q != '>' && *q!= '=' && ( isblank(*q) || ! (isalpha(*q) || isdigit(*q) || *q == ':'))) { q++;}
if (*q == '\0' || *q == '>') {
free(attr);
attr = NULL;
return NULL;
}
state = 3;
break;
case 3: if (*q != '=') {
free(attr);
attr = NULL;
state = 0;
break;
} else {
q++;
if (*q == '\0' || *q == '>') {
free(attr);
attr = NULL;
return NULL;
}
state = 4;
}
break;
case 4: while (*q != '\0' && *q != '>' && *q != '"' && *q != '\'' && ( isblank(*q) || ! (isalpha(*q) || isdigit(*q) || *q == ':'))) { q++;}
if (*q == '\0' || *q == '>') {
free(attr);
attr = NULL;
return NULL;
}
state = 5;
break;
case 5: p = q;
if (*q == '"') {
q++;
while (*q != '\0' && *q != '"' ) { q++; }
if (*q != '\0') { q++;}
} else if (*q == '\'') {
q++;
while (*q != '\0' && *q != '\'' ) { q++; }
if (*q != '\0') { q++;}
} else {
while (*q != '\0' && *q != '>' && !isblank(*q) && ( isalpha(*q) || isdigit(*q) || *q == ':')) { q++; }
}
if (*q == '\0' || *q == '>') {
return NULL;
}
len = q - p;
if (len <=0) {
state = 0;
break;
}
value = (char*)malloc((len + 1) * sizeof(char));
if (value == NULL) {
fprintf(stderr, "ERROR: Cannot allocate %li characters for stroting attribute.", (long int)len);
return NULL;
}
strncpy(value, p, len);
value[len] = '\0';
if (strcmp(attr, attrName) == 0) {
free(attr);
attr = NULL;
return value;
} else {
free(attr);
attr = NULL;
free(value);
value = NULL;
state = 0;
}
break;
}
};
}
/**
* The result of this function will be something allocated in the heap, so
* you will have to free that after you finish your work.
*/
char* parseUrl(char* url) {
char* p = url;
char* tag = NULL;
if (url == NULL) {
return NULL;
}
while (1) {
p = skipToNextTag(p);
if (p== NULL) {
return NULL;
}
tag = getTagName(p);
if (tag != NULL && strcmp(tag, "REF") == 0) {
return getValueOfAttribute(p, "HREF");
} else {
p = skipTag(p);
if (p == NULL) {
return NULL;
}
}
}
}
/**
*/
char* readFileCompletely(char* fn) {
int i = 0;
off_t size = 0;
struct stat buf;
char* b = NULL;
int fd = -1;
if (fn == NULL) {
return NULL;
}
i = stat(fn, &buf);
if (i != 0) {
fprintf(stderr, "ERROR: Canot get length of file \"%s\".", fn);
return NULL;
}
size = buf.st_size;
b = (char*)malloc((size + 1) * sizeof(char));
if (b == NULL) {
fprintf(stderr, "ERROR: Cannot allocate buffer of %li bytes to completely read file \"%s\".", (long int)size, fn);
return NULL;
}
fd = open(fn, O_RDONLY);
if (fd == -1) {
fprintf(stderr, "ERROR: Cannot open file \"%s\" for reading.", fn);
free(b);
b = NULL;
return NULL;
}
i = read(fd, b, size);
if (i != size) {
fprintf(stderr, "ERROR: Attempted to read %li bytes from file \"%s\", but only %li bytes were read.", (long int)size, fn, (long int)i);
free(b);
b = NULL;
return NULL;
}
b[size] = '\0';
close(fd);
return b;
}
int main(int argc, char* argv[])
{
char* fileContent = NULL;
char* urlToPlay = NULL;
char* p = NULL;
int len = 0;
parseArguments(argc, argv);
if (url == NULL) {
fprintf(stderr, "ERROR: No URL specified. Do not know what to parse and play.");
printHelp();
fprintf(stderr, "ERROR: `No URL specified. Do not know what to parse and play.");
}
fileContent = readFileCompletely(url);
if (fileContent == NULL) {
return 0;
}
urlToPlay = parseUrl(fileContent);
if (urlToPlay == NULL) {
return 0;
}
p = urlToPlay;
len = strlen(p);
if (len > 0 && p[0] == '"' && p[len -1] == '"') {
p[len-1] = '\0';
p++;
} else if (len > 0 && p[0] == '\'' && p[len -1] == '\'') {
p[len-1] = '\0';
p++;
}
printf("%s", p);
free(urlToPlay);
return 0;
}