Main Page   Modules   Class Hierarchy   Compound List   File List   Compound Members   File Members  

radiosched.cpp

00001 /* MuSE - Multiple Streaming Engine
00002  * (c) Copyright 2001 Eugen Melinte <ame01@gmx.net>
00003  *
00004  * This source code is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU Public License as published 
00006  * by the Free Software Foundation; either version 2 of the License,
00007  * or (at your option) any later version.
00008  *
00009  * This source code is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00012  * Please refer to the GNU Public License for more details.
00013  *
00014  * You should have received a copy of the GNU Public License along with
00015  * this source code; if not, write to:
00016  * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00017 
00018  "$Id: radiosched.cpp,v 1.12 2004/12/15 18:18:06 jaromil Exp $"
00019  
00020  */
00021 
00022 #include <config.h>
00023 #ifdef HAVE_SCHEDULER
00024 
00025 #include <iostream>
00026 #include <math.h>
00027 #include <stdio.h>
00028 #include <stdlib.h>
00029 #include <unistd.h>
00030 #include <dirent.h>
00031 #include <sys/ioctl.h>
00032 #include <sys/stat.h>
00033 #include <errno.h>
00034 #include <fcntl.h>
00035 #include <sys/soundcard.h>
00036 #include <string.h>
00037 #include <signal.h>
00038 #include <sys/types.h>
00039 #include <sys/wait.h>
00040 #include <termios.h>
00041 #include <stdarg.h>
00042 
00043 
00044 
00045 #include <jutils.h>
00046 #include <audioproc.h>
00047 #include <jmixer.h>
00048 #include <playlist.h>
00049 #include <inchannels.h>
00050 
00051 #ifdef HAVE_VORBIS
00052 #include <out_vorbis.h>
00053 #endif
00054 
00055 #ifdef HAVE_LAME
00056 #include <out_lame.h>
00057 #endif
00058 
00059 #include <glib.h> 
00060 
00061 #include <radiosched.h>
00062 
00063 
00064 
00065 Basic_scheduler *rscheduler = NULL;
00066 
00067 static char m_sched_file[FILENAME_MAX+1] = {0};
00068 
00069 
00070 /*Tags, attributes and values*/
00071 #define SF_S      "sched"
00072 #define SFI_S     "sched:item"
00073 # define SFIA_S    "start"
00074 # define SFIA_E    "end"
00075 # define SFIA_WKD  "weekday" //0-6
00076 #  define SUN     "Sun"      //0 
00077 #  define MON     "Mon"
00078 #  define TUE     "Tue"
00079 #  define WEN     "Wen"
00080 #  define THU     "Thu"
00081 #  define FRI     "Fri"
00082 #  define SAT     "Sat"
00083 # define SFIA_DAY  "day"   //1-31
00084 # define SFIA_MTH  "month" //1-12
00085 #  define JAN     "Jan"
00086 #  define FEB     "Feb"
00087 #  define MAR     "Mar"
00088 #  define APR     "Apr"
00089 #  define MAI     "Mai"
00090 #  define JUN     "Jun"
00091 #  define JUL     "Jul"
00092 #  define AUG     "Aug"
00093 #  define SEP     "Sep"
00094 #  define OCT     "Oct"
00095 #  define NOV     "Nov"
00096 #  define DEC     "Dec"
00097 # define SFIA_SRC  "source"
00098 # define SFIA_CMNT "comment"
00099 
00100 
00101 const char *
00102 sched_file_path(void)
00103 {
00104   if (*m_sched_file) return m_sched_file;
00105   //else fill it
00106   char *home = getenv("HOME");
00107   if (home) {
00108       snprintf(m_sched_file, FILENAME_MAX, "%s/.muse/" SCHEDFILE, home);
00109   }
00110   
00111   return m_sched_file;
00112 }
00113 
00114 
00115 static const char *
00116 _wkd(const char *d)
00117 {
00118     if (0==strcmp(d, SUN) || 0==strcmp(d, "0")) return "0";
00119     if (0==strcmp(d, MON) || 0==strcmp(d, "1")) return "1";
00120     if (0==strcmp(d, TUE) || 0==strcmp(d, "2")) return "2";
00121     if (0==strcmp(d, WEN) || 0==strcmp(d, "3")) return "3";
00122     if (0==strcmp(d, THU) || 0==strcmp(d, "4")) return "4";
00123     if (0==strcmp(d, FRI) || 0==strcmp(d, "5")) return "5";
00124     if (0==strcmp(d, SAT) || 0==strcmp(d, "6")) return "6";
00125         return NULL;
00126 }
00127 
00128 static const char *
00129 _mth(const char *d)
00130 {
00131     if (0==strcmp(d, JAN) || 0==strcmp(d, "1"))  return "1";
00132     if (0==strcmp(d, FEB) || 0==strcmp(d, "2"))  return "2";
00133     if (0==strcmp(d, MAR) || 0==strcmp(d, "3"))  return "3";
00134     if (0==strcmp(d, APR) || 0==strcmp(d, "4"))  return "4";
00135     if (0==strcmp(d, MAI) || 0==strcmp(d, "5"))  return "5";
00136     if (0==strcmp(d, JUN) || 0==strcmp(d, "6"))  return "6";
00137     if (0==strcmp(d, JUL) || 0==strcmp(d, "7"))  return "7";
00138     if (0==strcmp(d, AUG) || 0==strcmp(d, "8"))  return "8";
00139     if (0==strcmp(d, SEP) || 0==strcmp(d, "9"))  return "9";
00140     if (0==strcmp(d, OCT) || 0==strcmp(d, "10")) return "10";
00141     if (0==strcmp(d, NOV) || 0==strcmp(d, "11")) return "11";
00142     if (0==strcmp(d, DEC) || 0==strcmp(d, "12")) return "12";
00143         return NULL;
00144 }
00145 
00146 /* Extract next token from str and put it in buf.  Transform it in appropriate
00147    digit form.  Return new pointer in str.  Separators: -,:  */
00148 static const char *
00149 _token(const char *str, char *buf, int len)
00150 {
00151     const char *digits = NULL;
00152         char *b = buf;
00153         
00154     memset(buf, '\0', len);
00155         len--; //leave space for ending 0
00156         while (*str!='\0' && *str!='-' && *str!=',' && *str!=':' && len-->0) {
00157             *buf++ = *str++;
00158         }
00159         buf = b;
00160         func("radioscheduler:  _token: %s\n", buf);
00161         
00162         digits=_wkd(buf);
00163         if(digits) strcpy(buf, digits);
00164 
00165         digits=_mth(buf);
00166         if(digits) strcpy(buf, digits);
00167         func("radioscheduler:  _token: %s str '%s'\n", buf, str);
00168         return str;
00169 }
00170 
00171 /*atoi...*/
00172 static inline int
00173 _number(const char *str)
00174 {
00175   register int n;
00176   register char c;
00177 
00178   n = 0;
00179   while ((c = *str++) && (c >= '0') && (c <= '9')) n = (n * 10) + c - '0';
00180   return n;
00181 }
00182 
00183 
00184 Basic_scheduler::Basic_scheduler( const void *sched ) {
00185   func("Basic_scheduler::Basic_scheduler()\n");
00186   
00187   prev_time = (time_t) 0;    /* timekeeper: previous wakeup  */
00188   
00189   playlist = new Playlist();
00190   
00191   on = false;
00192   running = false;
00193   quit = true;
00194 
00195   playing   = NULL;
00196   
00197   channel = new Channel();
00198   channel->lock();
00199   channel->start();
00200   channel->wait();
00201   channel->unlock();
00202   
00203   _schedule = sched;
00204   mixer = NULL;
00205 
00206   _thread_init();
00207 }
00208 
00209 Basic_scheduler::~Basic_scheduler() {
00210   func("Basic_scheduler::~Basic_scheduler()\n");
00211 
00212   if (channel) {
00213       stop_channel();
00214       channel->quit = true;
00215       //FIXME
00216       jsleep(0,50);
00217       delete channel; channel = NULL;
00218   }
00219 
00220   /* paranoia */
00221   //stop();
00222   //  clean();
00223   quit = true;
00224   
00225   while(running) jsleep(0,20);
00226   
00227   delete playlist; 
00228 
00229   _thread_destroy();
00230 }
00231 
00232 
00233 void Basic_scheduler::_thread_init() {
00234 
00235   func("Basic_scheduler::thread_init()\n");
00236   if(pthread_mutex_init (&_mutex,NULL) == -1)
00237     error("error initializing POSIX thread mutex");
00238   if(pthread_cond_init (&_cond, NULL) == -1)
00239     error("error initializing POSIX thread condition"); 
00240   if(pthread_attr_init (&_attr) == -1)
00241     error("error initializing POSIX thread attribute");
00242   
00243   /* set the thread as detached
00244      see: man pthread_attr_init(3) */
00245   pthread_attr_setdetachstate(&_attr,PTHREAD_CREATE_DETACHED);
00246 }
00247 
00248 void Basic_scheduler::_thread_destroy() {
00249   /* we signal and then we check the thread
00250      exited by locking the conditional */
00251   while(running) {
00252     signal();
00253     lock(); unlock();
00254   }
00255 
00256   if(pthread_mutex_destroy(&_mutex) == -1)
00257     error("error destroying POSIX thread mutex");
00258   if(pthread_cond_destroy(&_cond) == -1)
00259     error("error destroying POSIX thread condition");
00260   if(pthread_attr_destroy(&_attr) == -1)
00261     error("error destroying POSIX thread attribute");
00262 }
00263 
00264 
00265 void Basic_scheduler::run() {
00266 
00267   //  register struct tm *tm;
00268   //  int status, i, pid;
00269   time_t clk;
00270   unsigned left; //time left to sleep
00271   
00272   func("Basic_scheduler::run()\n");
00273   lock();
00274   running = true;
00275   unlock();
00276   signal(); // signal to the parent thread we are born!
00277 
00278   quit = false;
00279 
00280   while(!quit) {
00281     if(on) {
00282       on_wakeup();
00283       (void) time(&clk);
00284           left = (unsigned) (60 - clk % 60); 
00285           while (left--) {
00286           jsleep(1,0);
00287                   // if channel not actually playing (ex.: http error) try restarting it
00288                   if (playing && !play_code) {
00289                       notice("Scheduler reloading %s", playing->path);
00290                           play_code = start_channel(playing);
00291                   }
00292                   if (quit) break;
00293           }
00294     } else { // if(on)
00295       // just hang on
00296       jsleep(1,0);
00297     }
00298   } // while(!quit)
00299   running = false;
00300 }
00301 
00302 
00303 bool Basic_scheduler::play() {
00304   if(on) return(true);
00305 
00306   if(!running) {
00307     error("%i:%s %s channel thread not launched",
00308           __LINE__,__FILE__,__FUNCTION__);
00309     return(false);
00310   }
00311   channel->play();
00312 
00313   on = true;
00314   return(on);
00315 }
00316 
00317 bool Basic_scheduler::stop() {
00318   //  lock();
00319   stop_channel();
00320   jsleep(0,50);
00321   on = false;
00322   return(!on);
00323 }
00324 
00325 
00326 void Basic_scheduler::dump() {
00327   if (!playlist) return;
00328   
00329   for (int i=1; i<=playlist->len(); i++ ) {
00330      Url *p = (Url*)playlist->pick(i);
00331      func( "schedule_record: \n" 
00332           "  Min: %s, Hr: %s, Day: %s, Mon: %s, WDay: %s \n"
00333           "  MnPlay %d,\n"
00334           "  Url %s, commnent '%s'\n",
00335           p->mn, p->hr, p->day, p->mon, p->wkd,
00336           p->mnplay, 
00337           p->path, p->comment
00338         );
00339   }
00340 }
00341 
00342 
00343 void Basic_scheduler::on_wakeup( void )
00344 {
00345     register struct tm *tm;
00346     time_t cur_time;
00347     //    int st, pid;
00348 
00349     func("Basic_scheduler::on_wakeup\n");
00350     
00351     if (playing) {
00352       playing->mnleft--;
00353       if (playing->mnleft <= 0) {
00354           stop_channel(); 
00355       }
00356     }
00357 
00358     load_schedule();
00359 
00360     time(&cur_time);
00361     tm = localtime(&cur_time);
00362 
00363     for (int i=1; i<=playlist->len(); i++) {
00364             Url *this_entry = (Url*)playlist->pick(i); 
00365         if ( match(this_entry->mn, tm->tm_min) 
00366           && match(this_entry->hr, tm->tm_hour) 
00367           && match(this_entry->day, tm->tm_mday) 
00368           && match(this_entry->mon, tm->tm_mon + 1) 
00369           && match(this_entry->wkd, tm->tm_wday)) {
00370               notice("==> Basic_scheduler: %02d/%02d-%02d:%02d  %s",
00371                    tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min,
00372                    this_entry->path);
00373               play_code = start_channel(this_entry);
00374         }
00375     }
00376 }
00377 
00378 
00379 bool Basic_scheduler::start_inner_channel( Url *rec )
00380 {
00381   stop_channel();
00382   func("Basic_scheduler::start_channel '%s'\r\n", rec->path);
00383   playing = rec;
00384   playing->mnleft = playing->mnplay; 
00385   channel->playlist->addurl( playing->path );
00386 
00387 //FIXME: channel complaints no song selected 
00388   channel->playlist->sel(1);
00389   playing->sel(true);
00390 
00391   return channel->play();
00392 
00393 }
00394 
00395 
00396 void Basic_scheduler::stop_inner_channel(void)
00397 {
00398   func("Basic_scheduler::stop_channel\r\n");
00399   channel->lock();
00400   channel->stop();
00401   channel->unlock();
00402   channel->lock();
00403   channel->playlist->cleanup();
00404   channel->clean(); 
00405   channel->unlock();
00406   channel->report();
00407 }
00408 
00409 #define MUSE_URL "muse://channel"
00410 bool Basic_scheduler::start_mixer_channel( Url *rec )
00411 {
00412     char *p;
00413         int  chan;
00414         bool ret = false;
00415         
00416     if (!rec || !rec->path || !mixer) return false;
00417         
00418         p = strstr(rec->path,MUSE_URL); 
00419         if (!p) return false;
00420         p += strlen(MUSE_URL);
00421         chan = atoi(p) - 1 ;
00422         notice("Basic_scheduler::start_mixer_channel %d", chan);
00423         
00424         if (chan>=0 && chan<MAX_CHANNELS) {
00425         mixer->set_channel(chan, 1/*pos*/);
00426         ret = mixer->play_channel(chan);
00427     }
00428         return ret;
00429 }
00430 
00431 void Basic_scheduler::stop_mixer_channel( Url *rec )
00432 {
00433     char *p;
00434         int  chan;
00435         
00436     if (!rec || !rec->path || !mixer) return;
00437         
00438         p = strstr(rec->path,MUSE_URL); 
00439         if (!p) return;
00440         p += strlen(MUSE_URL);
00441         chan = atoi(p) -1 ;
00442         notice("Basic_scheduler::stop_mixer_channel %d", chan);
00443         
00444         if (chan>0 &&chan<MAX_CHANNELS) {
00445         mixer->stop_channel(chan);
00446     }
00447 }
00448 
00449 bool Basic_scheduler::start_channel( Url *rec )
00450 {
00451     bool ret = false;
00452     if (strstr(rec->path,MUSE_URL)) {
00453         ret = start_mixer_channel(rec);
00454     } else {
00455         ret = start_inner_channel(rec);
00456     }
00457         return ret;
00458 }
00459 
00460 void Basic_scheduler::stop_channel(void)
00461 {
00462     if (playing && strstr(playing->path,MUSE_URL)) {
00463         stop_mixer_channel(playing);
00464     } else {
00465         stop_inner_channel();
00466     }
00467         playing = NULL;
00468 }
00469 
00470 bool Basic_scheduler::match( const char *left, int right )
00471 {
00472   register int n;
00473   //  register char c;
00474   char tok[16];
00475 
00476   //notice("match '%s' %d ", left, right);
00477   if (!left) return false;
00478 
00479   if (!strcmp(left, "*") || !strcmp(left, "any")) return true;
00480 
00481   left = _token(left, tok, 16); 
00482   n = _number(tok);
00483 
00484   switch (*left) {
00485       case '\0':
00486           return( right == n );
00487           /*NOTREACHED*/
00488           break;
00489       case ',':
00490           if (right == n) return true;
00491           do {
00492               n = 0;
00493                           left = _token(++left, tok, 16); 
00494                           n = _number(tok);
00495               if (right == n) return(1);
00496           } while (*left == ',');
00497           return false;
00498           /*NOTREACHED*/
00499           break;
00500       case '-':
00501           if (right < n) return(0);
00502           n = 0;
00503           left = _token(++left, tok, 16);  
00504                   n = _number(tok);
00505           return( right <= n );
00506           /*NOTREACHED*/
00507           break;
00508       default:
00509           break;
00510   }
00511   return false;
00512 }
00513 
00514 /*------------------------------------------------------------------*/
00515 
00516 const char empty_text_sched_file[] = 
00517 "#\n"
00518 "# $HOME/.muse/schedule\n"
00519 "# Mn  Hr Day Mth WDay MnPlay Url comment\n"
00520 "#\n"
00521 "# Sample: start at every minute\n"
00522 "#* * * * * 2 http://wwww.none.com:888/x.ogg comment starts here \n"
00523 "\n"
00524 "\n"
00525 ;
00526 
00527 
00528 void Scheduler_text::on_load_schedule() {
00529   int len, pos;
00530   FILE *cfp;
00531   struct stat buf;
00532   const char *cronf = (const char *)_schedule;
00533 
00534   func("Scheduler_text::on_load_schedule \n");
00535   if (!_schedule) return;
00536 
00537   if (stat(cronf, &buf)) {
00538       if (prev_time == (time_t) 0) warning("Can't stat crontab %s\n", cronf);
00539       prev_time = (time_t) 0;
00540       return;
00541   }
00542 
00543   if (buf.st_mtime <= prev_time) return;
00544 
00545   if ((cfp = fopen(cronf, "r")) == NULL) {
00546       if (prev_time == (time_t) 0) warning("Can't open crontab %s\n", cronf);
00547       prev_time = (time_t) 0;
00548       return;
00549   }
00550   prev_time = buf.st_mtime;
00551   len = pos = 0;
00552   playlist->cleanup();
00553 
00554   stop_channel();
00555   
00556   while (fgets(&crontab[pos], CRONSIZE - pos, cfp) != NULL) {
00557       if (  crontab[pos] == '#' || crontab[pos] == '\n' 
00558          || crontab[pos] == '\r') 
00559           continue;
00560       len = strlen(&crontab[pos]);
00561       if (crontab[pos + len - 1] == '\n') {
00562           len--;
00563           crontab[pos + len] = '\0';
00564       }
00565 
00566       addentry(&crontab[pos]);
00567 
00568       pos += ++len;
00569       if (pos >= CRONSIZE) break;
00570   }
00571   (void) fclose(cfp);
00572 
00573  // while (rec_ptr) {
00574  //     rec_ptr->mn = NULL;
00575  //     rec_ptr = rec_ptr->next;
00576  // }
00577   dump(); //FIXME: comment out 
00578 }
00579 
00580 
00581 /* Assign the field values to the crontab entry. */
00582 //# Mn  Hr Day Mth WDay MnPlay Url comment
00583 void Scheduler_text::addentry( char *line )
00584 {
00585     const char *mn, *hr, *day, *mon, *wkd, *cmnplay, *path, *comment;
00586         
00587     mn     = strtok(line, TSEPARATOR);
00588     hr     = strtok( (char *)NULL, TSEPARATOR);
00589     day    = strtok( (char *)NULL, TSEPARATOR);
00590     mon    = strtok( (char *)NULL, TSEPARATOR);
00591     wkd    = strtok( (char *)NULL, TSEPARATOR);
00592     cmnplay= strtok( (char *)NULL, TSEPARATOR);
00593     path   = strtok( (char *)NULL, TSEPARATOR);
00594     if (path) { 
00595             comment= strchr(path, '\0') + 1;
00596             playlist->addurl(path, mn, hr, day, mon, wkd, cmnplay, comment);
00597         }
00598 }
00599 
00600 
00601 /*------------------------------------------------------------------*/
00602 
00603 const char empty_xml_sched_file_header[] = 
00604 "<?xml version=\"1.0\" encoding=\"UTF-8\" ?> \n"
00605 "<sched xmlns:sched=\"/\">\n"
00606 "<!--\n"
00607 "  $HOME/.muse/schedule \n"
00608 "  Attributes: \n"
00609 "      start=\"hour:min\" in 24-hours format \n"
00610 "      end=\"hour:min\" \n"
00611 "      weekday=0-6 |  Sun(0), Mon, Tue, Wen, Thu, Fri, Sat \n"
00612 "      day=1-31 \n"
00613 "      month=1-12 | Jan,Feb,Mar,Apr,Mai,Jun,Jul,Aug,Sep,Oct,Nov,Dec \n"
00614 "  Wildcards: \n"
00615 "      * ex: start=\"*:*\" \n"
00616 "      periods: Sun-Sat \n"
00617 "      enumerations: Jan,Feb,Dec \n"
00618 "  Source: file.ogg, file.mp3, http://source, muse://channelX with X=1..6\n"
00619 "  Example: \n"
00620 "  <sched:item start=\"10:00\"  end=\"13:00\" weekday=\"Mon-Fri\" day=\"*\" month=\"*\" source=\"http://aud-one.kpfa.org:8090/kpfa.mp3\" comment=\"KPFA Democracy Now\" /> \n"
00621 "-->\n"
00622 "\n"
00623 ;
00624 
00625 const char empty_xml_sched_file_footer[] = 
00626 "</sched>"
00627 "\n"
00628 ;
00629 
00630 typedef struct _xml_user_data {
00631     sched_rec_callb callb; //GUI func
00632         void *udata;           //passed from GUI
00633         sched_rec *sr;         //filled by xml parser
00634 } xml_user_data;
00635 
00636 
00637 int
00638 create_xml_schedule_file(void)
00639 {
00640         gchar *home, *dir; 
00641         int ret;
00642         int fd;
00643 
00644         if(!(home = getenv("HOME"))) {
00645                 error("no $HOME found");
00646                 return FALSE;
00647         }
00648         
00649         dir = g_strconcat(home, "/.muse", NULL);
00650         if(!g_file_test(dir, (GFileTest) (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))) {
00651                 if((fd = creat(dir, S_IRWXU)) < 0) {
00652                         error("error during $HOME./muse directory creation");
00653                         return FALSE;
00654                 }
00655                 close(fd);
00656         }
00657         g_free(dir);
00658         
00659         if(g_file_test(sched_file_path(), (GFileTest) (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR))) { 
00660                 return TRUE;
00661         }
00662 
00663     notice("create_xml_schedule_file '%s'", m_sched_file);      
00664         ret = write_xml_schedule_file( "" ); 
00665         return ret;
00666 }
00667 
00668 char *
00669 format_xml_sched_rec(const sched_rec *rec)
00670 {
00671   gchar *ret = g_strconcat(
00672                            "<" SFI_S, 
00673                            " " SFIA_SRC "=\"", rec->src, "\"",
00674                            " " SFIA_CMNT "=\"", rec->comment, "\"",
00675                            " " SFIA_WKD "=\"", rec->wkd, "\"",
00676                            " " SFIA_S "=\"", rec->stime, "\"",
00677                            " " SFIA_E "=\"", rec->etime, "\"",
00678                            " " SFIA_DAY "=\"*\"",
00679                            " " SFIA_MTH "=\"*\"",
00680                            "/>\n", NULL
00681                            );
00682   return ret;
00683 
00684 }
00685 
00686 int 
00687 write_xml_schedule_file(const char *content)
00688 {
00689         FILE *f;
00690         
00691         if(!(f = fopen(sched_file_path(), "w"))) {
00692                 error("problem opening " SCHEDFILE " for writing");
00693                 return FALSE;
00694         }
00695         
00696         fputs(empty_xml_sched_file_header, f);
00697         fputs(content, f);
00698         fputs(empty_xml_sched_file_footer, f);
00699 
00700     fclose(f);
00701         
00702     return TRUE;
00703 }
00704 
00705 static void
00706 start_e(GMarkupParseContext *c, const gchar *ename,
00707         const gchar **anames, const gchar **avalues,
00708                 gpointer udata, GError **err)
00709 {
00710     xml_user_data *ud = (xml_user_data*)udata;
00711         
00712     //notice("<%s>", ename);
00713         //for (int i=0; anames[i]; i++) {
00714         //    notice("  '%s' == '%s'", anames[i], avalues[i]);
00715         //}
00716         
00717         if (0==strcmp(ename, SFI_S)) {
00718             for (int i=0; anames[i]; i++) {
00719                 if (0==strcmp(anames[i], SFIA_CMNT)) ud->sr->comment = avalues[i];
00720                 if (0==strcmp(anames[i], SFIA_SRC))  ud->sr->src = avalues[i];
00721                 if (0==strcmp(anames[i], SFIA_MTH))  ud->sr->month = avalues[i];
00722                 if (0==strcmp(anames[i], SFIA_DAY))  ud->sr->day = avalues[i];
00723                 if (0==strcmp(anames[i], SFIA_WKD))  ud->sr->wkd = avalues[i];
00724                 if (0==strcmp(anames[i], SFIA_S))    ud->sr->stime = avalues[i];
00725                 if (0==strcmp(anames[i], SFIA_E))    ud->sr->etime = avalues[i];
00726             }
00727                 //"fix" it
00728                 if (!ud->sr->comment) ud->sr->comment = "";
00729                 if (!ud->sr->src)     ud->sr->src = "";
00730                 if (!ud->sr->month)   ud->sr->month = "";
00731                 if (!ud->sr->day)     ud->sr->day = "";
00732                 if (!ud->sr->wkd)     ud->sr->wkd = "";
00733                 if (!ud->sr->stime)   ud->sr->stime = "";
00734                 if (!ud->sr->etime)   ud->sr->etime = "";
00735                 (*ud->callb)(ud->udata, ud->sr); 
00736                 //cleanup for next call
00737                 ud->sr->comment = NULL;
00738                 ud->sr->src = NULL;
00739                 ud->sr->month = NULL;
00740                 ud->sr->day = NULL;
00741                 ud->sr->wkd = NULL;
00742                 ud->sr->stime = NULL;
00743                 ud->sr->etime = NULL;
00744         }
00745 }
00746 
00747 
00748 static void
00749 end_e(GMarkupParseContext *c, const gchar *ename,
00750                 gpointer udata, GError **err)
00751 {
00752     //notice("</%s>", ename);
00753 }
00754 
00755 static void
00756 on_err(GMarkupParseContext *c, GError *err, gpointer udata)
00757 {
00758     notice("<on_err>");
00759 }
00760 
00761 int 
00762 parse_xml_sched_file( sched_rec_callb callb, void *udata, sched_rec *sr )
00763 {
00764     gchar *buf=NULL; 
00765         guint len;
00766         xml_user_data calldata = {callb, udata, sr};
00767         
00768         g_file_get_contents(sched_file_path(), &buf, &len, NULL); 
00769         if (len>0) {
00770             GMarkupParser xp = {start_e, end_e, NULL, NULL, on_err};
00771             GError *err = NULL; 
00772             GMarkupParseContext *parser = NULL;
00773             parser = g_markup_parse_context_new(&xp, (GMarkupParseFlags)0, &calldata, NULL);
00774                 if (!g_markup_parse_context_parse(parser, buf, len, &err)) {
00775                     error("parse_xml_sched_file g_markup_parse_context_parse error: %s\n", err->message);
00776                 }
00777                 g_markup_parse_context_end_parse(parser, &err);
00778                 g_markup_parse_context_free(parser);
00779                 if (err) {
00780                     warning("parse_xml_sched_file: '%s'", err->message);
00781                         //should free err?
00782                 }
00783         }
00784         
00785         g_free(buf); 
00786         return TRUE; 
00787 }
00788 
00789 void Scheduler_xml::on_load_schedule() {
00790   sched_rec sr = {0};
00791   struct stat buf;
00792   const char *cronf = (const char *)_schedule;
00793 
00794   func("Scheduler_xml::on_load_schedule \n");
00795   if (!_schedule) return;
00796 
00797   if (stat(cronf, &buf)) {
00798       if (prev_time == (time_t) 0) warning("Can't stat crontab %s\n", cronf);
00799       prev_time = (time_t) 0;
00800       return;
00801   }
00802 
00803   if (buf.st_mtime <= prev_time) return;
00804   prev_time = buf.st_mtime;
00805 
00806   playlist->cleanup();
00807   stop_channel();
00808   parse_xml_sched_file( addentry, this, &sr );
00809   
00810   dump(); //FIXME: comment out
00811 }
00812 
00813 
00814 /* Assign the field values to the crontab entry. */
00815 //# Mn  Hr Day Mth WDay MnPlay Url comment
00816 int Scheduler_xml::addentry( void *instance, sched_rec *sr )
00817 {
00818     char mn[16]={0}, hr[16]={0}, cmnplay[16]={0}; 
00819         int emn, smn, ehr, shr, mnplay;
00820     char tok[16];
00821         const char *p;
00822 
00823     //start hr
00824     p = _token(sr->stime, tok, 16); 
00825         strcpy(hr, tok);
00826     shr = _number(tok);
00827     //start mn
00828     p = _token(++p, tok, 16); 
00829         strcpy(mn, tok);
00830     smn = _number(tok);
00831     //end hr
00832     p = _token(sr->etime, tok, 16); 
00833     ehr = _number(tok);
00834     //end mn
00835     p = _token(++p, tok, 16); 
00836     emn = _number(tok);
00837         
00838         //calculate (c)mnplay
00839         if (*mn=='*') {
00840             mnplay = 1;
00841         } else if (*hr=='*') {
00842             mnplay = 60;
00843         } else {
00844             mnplay = 60*(ehr-shr) + (emn-smn);
00845         }
00846         sprintf(cmnplay, "%d", mnplay);
00847 
00848     if (sr->src) { 
00849             ((Scheduler_xml*)instance)->playlist->addurl(sr->src, mn, hr, sr->day, 
00850                     sr->month, sr->wkd, cmnplay, sr->comment);
00851         }
00852     return 1; 
00853 }
00854 
00855 #endif

Generated on Thu Dec 16 12:28:21 2004 for MuSE by doxygen1.3