OpenTv

#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;
}