Merge pull request #1129 from jmarshallnz/remove_smb_auth_details_in_add_source
[vuplus_xbmc] / lib / cmyth / libcmyth / proglist.c
1 /*
2  *  Copyright (C) 2004-2010, Eric Lund
3  *  http://www.mvpmc.org/
4  *
5  *  This library is free software; you can redistribute it and/or
6  *  modify it under the terms of the GNU Lesser General Public
7  *  License as published by the Free Software Foundation; either
8  *  version 2.1 of the License, or (at your option) any later version.
9  *
10  *  This library is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  *  Lesser General Public License for more details.
14  *
15  *  You should have received a copy of the GNU Lesser General Public
16  *  License along with this library; if not, write to the Free Software
17  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  */
19
20 /*
21  * proglist.c - functions to manage MythTV timestamps.  Primarily,
22  *               these allocate timestamps and convert between string
23  *               and cmyth_proglist_t and between long long and
24  *               cmyth_proglist_t.
25  */
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <errno.h>
29 #include <string.h>
30 #include <cmyth_local.h>
31
32 /*
33  * cmyth_proglist_destroy(void)
34  * 
35  * Scope: PRIVATE (static)
36  *
37  * Description
38  *
39  * Destroy and free a timestamp structure.  This should only be called
40  * by ref_release().  All others should use
41  * ref_release() to release references to time stamps.
42  *
43  * Return Value:
44  *
45  * None.
46  */
47 static void
48 cmyth_proglist_destroy(cmyth_proglist_t pl)
49 {
50         int i;
51
52         cmyth_dbg(CMYTH_DBG_DEBUG, "%s\n", __FUNCTION__);
53         if (!pl) {
54                 return;
55         }
56         for (i  = 0; i < pl->proglist_count; ++i) {
57                 if (pl->proglist_list[i]) {
58                         ref_release(pl->proglist_list[i]);
59                 }
60                 pl->proglist_list[i] = NULL;
61         }
62         if (pl->proglist_list) {
63                 free(pl->proglist_list);
64         }
65 }
66
67 /*
68  * cmyth_proglist_create(void)
69  * 
70  * Scope: PUBLIC
71  *
72  * Description
73  *
74  * Create a timestamp structure and return a pointer to the structure.
75  *
76  * Return Value:
77  *
78  * Success: A non-NULL cmyth_proglist_t (this type is a pointer)
79  *
80  * Failure: A NULL cmyth_proglist_t
81  */
82 cmyth_proglist_t
83 cmyth_proglist_create(void)
84 {
85         cmyth_proglist_t ret;
86
87         cmyth_dbg(CMYTH_DBG_DEBUG, "%s\n", __FUNCTION__);
88         ret = ref_alloc(sizeof(*ret));
89         if (!ret) {
90                 return(NULL);
91         }
92         ref_set_destroy(ret, (ref_destroy_t)cmyth_proglist_destroy);
93
94         ret->proglist_list = NULL;
95         ret->proglist_count = 0;
96         return ret;
97 }
98
99 /*
100  * cmyth_proglist_get_item(cmyth_proglist_t pl, int index)
101  *
102  * Scope: PUBLIC
103  *
104  * Description:
105  *
106  * Retrieve the program information structure found at index 'index'
107  * in the list in 'pl'.  Return the program information structure
108  * held.  Before forgetting the reference to this program info structure
109  * the caller must call ref_release().
110  *
111  * Return Value:
112  *
113  * Success: A non-null cmyth_proginfo_t (this is a pointer type)
114  *
115  * Failure: A NULL cmyth_proginfo_t
116  */
117 cmyth_proginfo_t
118 cmyth_proglist_get_item(cmyth_proglist_t pl, int index)
119 {
120         if (!pl) {
121                 cmyth_dbg(CMYTH_DBG_ERROR, "%s: NULL program list\n",
122                           __FUNCTION__);
123                 return NULL;
124         }
125         if (!pl->proglist_list) {
126                 cmyth_dbg(CMYTH_DBG_ERROR, "%s: NULL list\n",
127                           __FUNCTION__);
128                 return NULL;
129         }
130         if ((index < 0) || (index >= pl->proglist_count)) {
131                 cmyth_dbg(CMYTH_DBG_ERROR, "%s: index %d out of range\n",
132                           __FUNCTION__, index);
133                 return NULL;
134         }
135         ref_hold(pl->proglist_list[index]);
136         return pl->proglist_list[index];
137 }
138
139 int
140 cmyth_proglist_delete_item(cmyth_proglist_t pl, cmyth_proginfo_t prog)
141 {
142         int i;
143         cmyth_proginfo_t old;
144         int ret = -EINVAL;
145
146         if (!pl) {
147                 cmyth_dbg(CMYTH_DBG_ERROR, "%s: NULL program list\n",
148                           __FUNCTION__);
149                 return -EINVAL;
150         }
151         if (!prog) {
152                 cmyth_dbg(CMYTH_DBG_ERROR, "%s: NULL program item\n",
153                           __FUNCTION__);
154                 return -EINVAL;
155         }
156
157         pthread_mutex_lock(&mutex);
158
159         for (i=0; i<pl->proglist_count; i++) {
160                 if (cmyth_proginfo_compare(prog, pl->proglist_list[i]) == 0) {
161                         old = pl->proglist_list[i];
162                         memmove(pl->proglist_list+i,
163                                 pl->proglist_list+i+1,
164                                 (pl->proglist_count-i-1)*sizeof(cmyth_proginfo_t));
165                         pl->proglist_count--;
166                         ref_release(old);
167                         ret = 0;
168                         goto out;
169                 }
170         }
171
172  out:
173         pthread_mutex_unlock(&mutex);
174
175         return ret;
176 }
177
178 /*
179  * cmyth_proglist_get_count(cmyth_proglist_t pl)
180  *
181  * Scope: PUBLIC
182  *
183  * Description:
184  *
185  * Retrieve the number of elements in the program information
186  * structure in 'pl'.
187  *
188  * Return Value:
189  *
190  * Success: A number >= 0 indicating the number of items in 'pl'
191  *
192  * Failure: -errno
193  */
194 int
195 cmyth_proglist_get_count(cmyth_proglist_t pl)
196 {
197         if (!pl) {
198                 cmyth_dbg(CMYTH_DBG_ERROR, "%s: NULL program list\n",
199                           __FUNCTION__);
200                 return -EINVAL;
201         }
202         return pl->proglist_count;
203 }
204
205 /*
206  * cmyth_proglist_get_list(cmyth_conn_t conn,
207  *                         cmyth_proglist_t proglist,
208  *                         char *msg, char *func)
209  * 
210  * Scope: PRIVATE (static)
211  *
212  * Description
213  *
214  * Obtain a program list from the query specified in 'msg' from the
215  * function 'func'.  Make the query on 'conn' and put the results in
216  * 'proglist'.
217  *
218  * Return Value:
219  *
220  * Success: 0
221  *
222  * Failure: -(ERRNO)
223  */
224 static int
225 cmyth_proglist_get_list(cmyth_conn_t conn,
226                         cmyth_proglist_t proglist,
227                         char *msg, const char *func)
228 {
229         int err = 0;
230         int count;
231         int ret;
232
233         if (!conn) {
234                 cmyth_dbg(CMYTH_DBG_ERROR, "%s: no connection\n", func);
235                 return -EINVAL;
236         }
237         if (!proglist) {
238                 cmyth_dbg(CMYTH_DBG_ERROR, "%s: no program list\n", func);
239                 return -EINVAL;
240         }
241
242         pthread_mutex_lock(&mutex);
243
244         if ((err = cmyth_send_message(conn, msg)) < 0) {
245                 cmyth_dbg(CMYTH_DBG_ERROR,
246                           "%s: cmyth_send_message() failed (%d)\n",
247                           func, err);
248                 ret = err;
249                 goto out;
250         }
251         count = cmyth_rcv_length(conn);
252         if (count < 0) {
253                 cmyth_dbg(CMYTH_DBG_ERROR,
254                           "%s: cmyth_rcv_length() failed (%d)\n",
255                           func, count);
256                 ret = count;
257                 goto out;
258         }
259         if (strcmp(msg, "QUERY_GETALLPENDING") == 0) {
260                 long c;
261                 int r;
262                 if ((r=cmyth_rcv_long(conn, &err, &c, count)) < 0) {
263                         cmyth_dbg(CMYTH_DBG_ERROR,
264                                   "%s: cmyth_rcv_length() failed (%d)\n",
265                                   __FUNCTION__, r);
266                         ret = err;
267                         goto out;
268                 }
269                 count -= r;
270         }
271         if (cmyth_rcv_proglist(conn, &err, proglist, count) != count) {
272                 cmyth_dbg(CMYTH_DBG_ERROR,
273                           "%s: cmyth_rcv_proglist() < count\n",
274                           func);
275         }
276         if (err) {
277                 cmyth_dbg(CMYTH_DBG_ERROR,
278                           "%s: cmyth_rcv_proglist() failed (%d)\n",
279                           func, err);
280                 ret = -1 * err;
281                 goto out;
282         }
283
284         ret = 0;
285
286     out:
287         pthread_mutex_unlock(&mutex);
288
289         return ret;
290 }
291
292 /*
293  * cmyth_proglist_get_all_recorded(cmyth_conn_t control,
294  *                                 cmyth_proglist_t *proglist)
295  * 
296  * Scope: PUBLIC
297  *
298  * Description
299  *
300  * Make a request on the control connection 'control' to obtain a list
301  * of completed or in-progress recordings.  Build a list of program
302  * information structures and put a malloc'ed pointer to the list (an
303  * array of pointers) in proglist.
304  *
305  * Return Value:
306  *
307  * Success: A held, noon-NULL cmyth_proglist_t
308  *
309  * Failure: NULL
310  */
311 cmyth_proglist_t
312 cmyth_proglist_get_all_recorded(cmyth_conn_t control)
313 {
314         char query[32];
315         cmyth_proglist_t proglist = cmyth_proglist_create();
316
317         if (proglist == NULL) {
318                 cmyth_dbg(CMYTH_DBG_ERROR,
319                           "%s: cmyth_proglist_create() failed\n",
320                           __FUNCTION__);
321                 return NULL;
322         }
323
324         if (control->conn_version < 65) {
325                 strcpy(query, "QUERY_RECORDINGS Play");
326         }
327         else {
328                 strcpy(query, "QUERY_RECORDINGS Ascending");
329         }
330         if (cmyth_proglist_get_list(control, proglist,
331                                     query,
332                                     __FUNCTION__) < 0) {
333                 cmyth_dbg(CMYTH_DBG_ERROR,
334                           "%s: cmyth_proglist_get_list() failed\n",
335                           __FUNCTION__);
336                 ref_release(proglist);
337                 return NULL;
338         }
339         return proglist;
340 }
341
342 /*
343  * cmyth_proglist_get_all_pending(cmyth_conn_t control,
344  *                                cmyth_proglist_t *proglist)
345  * 
346  * Scope: PUBLIC
347  *
348  * Description
349  *
350  * Make a request on the control connection 'control' to obtain a list
351  * of pending recordings.  Build a list of program information
352  * structures and put a malloc'ed pointer to the list (an array of
353  * pointers) in proglist.
354  *
355  * Return Value:
356  *
357  * Success: A held, noon-NULL cmyth_proglist_t
358  *
359  * Failure: NULL
360  */
361 cmyth_proglist_t
362 cmyth_proglist_get_all_pending(cmyth_conn_t control)
363 {
364         cmyth_proglist_t proglist = cmyth_proglist_create();
365
366         if (proglist == NULL) {
367                 cmyth_dbg(CMYTH_DBG_ERROR,
368                           "%s: cmyth_proglist_create() failed\n",
369                           __FUNCTION__);
370                 return NULL;
371         }
372
373         if (cmyth_proglist_get_list(control, proglist,
374                                     "QUERY_GETALLPENDING",
375                                     __FUNCTION__) < 0) {
376                 cmyth_dbg(CMYTH_DBG_ERROR,
377                           "%s: cmyth_proglist_get_list() failed\n",
378                           __FUNCTION__);
379                 ref_release(proglist);
380                 return NULL;
381         }
382         return proglist;
383 }
384
385 /*
386  * cmyth_proglist_get_all_scheduled(cmyth_conn_t control,
387  *                                  cmyth_proglist_t *proglist)
388  * 
389  * Scope: PUBLIC
390  *
391  * Description
392  *
393  * Make a request on the control connection 'control' to obtain a list
394  * of scheduled recordings.  Build a list of program information
395  * structures and put a malloc'ed pointer to the list (an array of
396  * pointers) in proglist.
397  *
398  * Return Value:
399  *
400  * Success: A held, noon-NULL cmyth_proglist_t
401  *
402  * Failure: NULL
403  */
404 cmyth_proglist_t
405 cmyth_proglist_get_all_scheduled(cmyth_conn_t control)
406 {
407         cmyth_proglist_t proglist = cmyth_proglist_create();
408
409         if (proglist == NULL) {
410                 cmyth_dbg(CMYTH_DBG_ERROR,
411                           "%s: cmyth_proglist_create() failed\n",
412                           __FUNCTION__);
413                 return NULL;
414         }
415
416         if (cmyth_proglist_get_list(control, proglist,
417                                     "QUERY_GETALLSCHEDULED",
418                                     __FUNCTION__) < 0) {
419                 cmyth_dbg(CMYTH_DBG_ERROR,
420                           "%s: cmyth_proglist_get_list() failed\n",
421                           __FUNCTION__);
422                 ref_release(proglist);
423                 return NULL;
424         }
425         return proglist;
426 }
427
428 /*
429  * cmyth_proglist_get_conflicting(cmyth_conn_t control,
430  *                                cmyth_proglist_t *proglist)
431  * 
432  * Scope: PUBLIC
433  *
434  * Description
435  *
436  * Make a request on the control connection 'control' to obtain a list
437  * of conflicting recordings.  Build a list of program information
438  * structures and put a malloc'ed pointer to the list (an array of
439  * pointers) in proglist.
440  *
441  * Return Value:
442  *
443  * Success: A held, noon-NULL cmyth_proglist_t
444  *
445  * Failure: NULL
446  */
447 cmyth_proglist_t
448 cmyth_proglist_get_conflicting(cmyth_conn_t control)
449 {
450         cmyth_proglist_t proglist = cmyth_proglist_create();
451
452         if (proglist == NULL) {
453                 cmyth_dbg(CMYTH_DBG_ERROR,
454                           "%s: cmyth_proglist_create() failed\n",
455                           __FUNCTION__);
456                 return NULL;
457         }
458
459         if (cmyth_proglist_get_list(control, proglist,
460                                     "QUERY_GETCONFLICTING",
461                                     __FUNCTION__) < 0) {
462                 cmyth_dbg(CMYTH_DBG_ERROR,
463                           "%s: cmyth_proglist_get_list() failed\n",
464                           __FUNCTION__);
465                 ref_release(proglist);
466                 return NULL;
467         }
468         return proglist;
469 }
470
471 /*
472  * sort_timestamp(const void *a, const void *b)
473  *
474  * Scope: PRIVATE
475  *
476  * Description
477  *
478  * Return an integer value to specify the relative position of the timestamp
479  * This is a helper function for the sort function called by qsort.  It will
480  * sort any of the timetstamps for the qsort functions 
481  *
482  * Return Value:
483  *
484  * Same Date/Time: 0
485  * Date a > b: 1
486  * Date a < b: -1
487  *
488  */
489 static int sort_timestamp(cmyth_timestamp_t X, cmyth_timestamp_t Y)
490 {
491
492         if (X->timestamp_year > Y->timestamp_year)
493                 return 1;
494         else if (X->timestamp_year < Y->timestamp_year)
495                 return -1;
496         else /* X->timestamp_year == Y->timestamp_year */
497         {
498                 if (X->timestamp_month > Y->timestamp_month)
499                         return 1;
500                 else if (X->timestamp_month < Y->timestamp_month)
501                         return -1;
502                 else /* X->timestamp_month == Y->timestamp_month */
503                 {
504                         if (X->timestamp_day > Y->timestamp_day)
505                                 return 1;
506                         else if (X->timestamp_day < Y->timestamp_day)
507                                 return -1;
508                         else /* X->timestamp_day == Y->timestamp_day */ 
509                         {
510                                 if (X->timestamp_hour > Y->timestamp_hour)
511                                         return 1;
512                                 else if (X->timestamp_hour < Y->timestamp_hour)
513                                         return -1;
514                                 else /* X->timestamp_hour == Y->timestamp_hour */
515                                 {
516                                         if (X->timestamp_minute > Y->timestamp_minute)
517                                                 return 1;
518                                         else if (X->timestamp_minute < Y->timestamp_minute)
519                                                 return -1;
520                                         else /* X->timestamp_minute == Y->timestamp_minute */
521                                         {
522                                                 if (X->timestamp_second > Y->timestamp_second)
523                                                         return 1;
524                                                 else if (X->timestamp_second < Y->timestamp_second)
525                                                         return -1;
526                                                 else /* X->timestamp_second == Y->timestamp_second */
527                                                         return 0;
528                                         }
529                                 }
530                         }
531                 }
532         }
533 }
534
535 /*
536  * recorded_compare(const void *a, const void *b)
537  *
538  * Scope: PRIVATE
539  *
540  * Description
541  *
542  * Return an integer value to a qsort function to specify the relative
543  * position of the recorded date 
544  *
545  * Return Value:
546  * 
547  * Same Day: 0
548  * Date a > b: 1
549  * Date a < b: -1
550  *
551  */
552 static int
553 recorded_compare(const void *a, const void *b)
554 {
555         const cmyth_proginfo_t x = *(cmyth_proginfo_t *)a;
556         const cmyth_proginfo_t y = *(cmyth_proginfo_t *)b;
557         cmyth_timestamp_t X = x->proginfo_rec_start_ts;
558         cmyth_timestamp_t Y = y->proginfo_rec_start_ts;
559
560         return sort_timestamp(X, Y);
561 }
562
563 /*
564  * airdate_compare(const void *a, const void *b)
565  *
566  * Scope: PRIVATE
567  *
568  * Description
569  *
570  * Return an integer value to a qsort function to specify the relative
571  * position of the original airdate
572  *
573  * Return Value:
574  * 
575  * Same Day: 0
576  * Date a > b: 1
577  * Date a < b: -1
578  *
579  */
580 static int
581 airdate_compare(const void *a, const void *b)
582 {
583         const cmyth_proginfo_t x = *(cmyth_proginfo_t *)a;
584         const cmyth_proginfo_t y = *(cmyth_proginfo_t *)b;
585         const cmyth_timestamp_t X = x->proginfo_originalairdate;
586         const cmyth_timestamp_t Y = y->proginfo_originalairdate;
587
588         return sort_timestamp(X, Y);
589 }
590
591 /*
592  * cmyth_proglist_sort(cmyth_proglist_t pl, int count, int sort)
593  *
594  * Scope: PUBLIC
595  *
596  * Description
597  *
598  * Sort the epispde list by mythtv_sort setting. Check to ensure that the 
599  * program list is not null and pass the proglist_list to the qsort function
600  *
601  * Return Value:
602  * 
603  * Success = 0
604  * Failure = -1
605  */ 
606 int 
607 cmyth_proglist_sort(cmyth_proglist_t pl, int count, cmyth_proglist_sort_t sort)
608 {
609         if (!pl) {
610                 cmyth_dbg(CMYTH_DBG_ERROR, "%s: NULL program list\n",
611                           __FUNCTION__);
612                 return -1;
613         }
614         if (!pl->proglist_list) {
615                 cmyth_dbg(CMYTH_DBG_ERROR, "%s: NULL list\n",
616                           __FUNCTION__);
617                 return -1;
618         }
619
620         cmyth_dbg(CMYTH_DBG_ERROR,
621                           "cmyth_proglist_sort\n");
622
623         switch (sort) {
624                 case MYTHTV_SORT_DATE_RECORDED: /* Default Date Recorded */
625                         qsort((cmyth_proginfo_t)pl->proglist_list, count, sizeof(pl->proglist_list) , recorded_compare);
626                         break;
627                 case MYTHTV_SORT_ORIGINAL_AIRDATE: /*Default Date Recorded */
628                         qsort((cmyth_proginfo_t)pl->proglist_list, count, sizeof(pl->proglist_list) , airdate_compare);
629                         break;
630                 default: 
631                         printf("Unsupported MythTV sort type\n");
632         }
633
634         cmyth_dbg(CMYTH_DBG_ERROR,
635                           "end cmyth_proglist_sort\n");
636         return 0;
637 }