MiniDLNA update: 1.0.19.1 to 1.0.20
[tomato.git] / release / src / router / minidlna / utils.c
blob92bf86daecf9073965fc1d320139385ec1bf1325
1 /* MiniDLNA media server
2 * Copyright (C) 2008-2009 Justin Maggard
4 * This file is part of MiniDLNA.
6 * MiniDLNA is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
10 * MiniDLNA 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
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with MiniDLNA. If not, see <http://www.gnu.org/licenses/>.
18 #include <stdio.h>
19 #include <ctype.h>
20 #include <string.h>
21 #include <stdlib.h>
22 #include <linux/limits.h>
23 #include <sys/stat.h>
24 #include <unistd.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <fcntl.h>
28 #include <errno.h>
30 #include "minidlnatypes.h"
31 #include "upnpglobalvars.h"
32 #include "log.h"
34 inline int
35 strcatf(struct string_s *str, const char *fmt, ...)
37 int ret;
38 va_list ap;
40 va_start(ap, fmt);
41 ret = vsnprintf(str->data + str->off, str->size - str->off, fmt, ap);
42 str->off += ret;
43 va_end(ap);
45 return ret;
48 int
49 ends_with(const char * haystack, const char * needle)
51 const char * end;
52 int nlen = strlen(needle);
53 int hlen = strlen(haystack);
55 if( nlen > hlen )
56 return 0;
57 end = haystack + hlen - nlen;
59 return (strcasecmp(end, needle) ? 0 : 1);
62 char *
63 trim(char *str)
65 if (!str)
66 return(NULL);
67 int i;
68 for (i=0; i <= strlen(str) && (isspace(str[i]) || str[i] == '"'); i++) {
69 str++;
71 for (i=(strlen(str)-1); i >= 0 && (isspace(str[i]) || str[i] == '"'); i--) {
72 str[i] = '\0';
74 return str;
77 /* Find the first occurrence of p in s, where s is terminated by t */
78 char *
79 strstrc(const char *s, const char *p, const char t)
81 char *endptr;
82 size_t slen, plen;
84 endptr = strchr(s, t);
85 if (!endptr)
86 return NULL;
88 plen = strlen(p);
89 slen = endptr - s;
90 while (slen >= plen)
92 if (*s == *p && strncmp(s+1, p+1, plen-1) == 0)
93 return (char*)s;
94 s++;
95 slen--;
98 return NULL;
101 char *
102 modifyString(char * string, const char * before, const char * after, short like)
104 int oldlen, newlen, chgcnt = 0;
105 char *s, *p, *t;
107 oldlen = strlen(before);
108 newlen = strlen(after);
109 if( newlen+like > oldlen )
111 s = string;
112 while( (p = strstr(s, before)) )
114 chgcnt++;
115 s = p+oldlen;
117 s = realloc(string, strlen(string)+((newlen-oldlen)*chgcnt)+1+like);
118 /* If we failed to realloc, return the original alloc'd string */
119 if( s )
120 string = s;
121 else
122 return string;
125 s = string;
126 while( s )
128 p = strcasestr(s, before);
129 if( !p )
130 return string;
131 memmove(p + newlen, p + oldlen, strlen(p + oldlen) + 1);
132 memcpy(p, after, newlen);
133 if( like )
135 t = p+newlen;
136 while( isspace(*t) )
137 t++;
138 if( *t == '"' )
140 if( like == 2 )
142 memmove(t+2, t+1, strlen(t+1)+1);
143 *++t = '%';
145 while( *++t != '"' )
146 continue;
147 memmove(t+1, t, strlen(t)+1);
148 *t = '%';
151 s = p + newlen;
154 return string;
157 char *
158 escape_tag(const char *tag, int force_alloc)
160 char *esc_tag = NULL;
162 if( strchr(tag, '&') || strchr(tag, '<') || strchr(tag, '>') )
164 esc_tag = strdup(tag);
165 esc_tag = modifyString(esc_tag, "&", "&amp;amp;", 0);
166 esc_tag = modifyString(esc_tag, "<", "&amp;lt;", 0);
167 esc_tag = modifyString(esc_tag, ">", "&amp;gt;", 0);
169 else if( force_alloc )
170 esc_tag = strdup(tag);
172 return esc_tag;
175 void
176 strip_ext(char * name)
178 char * period;
180 period = strrchr(name, '.');
181 if( period )
182 *period = '\0';
185 /* Code basically stolen from busybox */
187 make_dir(char * path, mode_t mode)
189 char * s = path;
190 char c;
191 struct stat st;
193 do {
194 c = 0;
196 /* Bypass leading non-'/'s and then subsequent '/'s. */
197 while (*s) {
198 if (*s == '/') {
199 do {
200 ++s;
201 } while (*s == '/');
202 c = *s; /* Save the current char */
203 *s = 0; /* and replace it with nul. */
204 break;
206 ++s;
209 if (mkdir(path, mode) < 0) {
210 /* If we failed for any other reason than the directory
211 * already exists, output a diagnostic and return -1.*/
212 if (errno != EEXIST
213 || (stat(path, &st) < 0 || !S_ISDIR(st.st_mode))) {
214 break;
217 if (!c)
218 return 0;
220 /* Remove any inserted nul from the path. */
221 *s = c;
223 } while (1);
225 DPRINTF(E_WARN, L_GENERAL, "make_dir: cannot create directory '%s'\n", path);
226 return -1;
230 is_video(const char * file)
232 return (ends_with(file, ".mpg") || ends_with(file, ".mpeg") ||
233 ends_with(file, ".avi") || ends_with(file, ".divx") ||
234 ends_with(file, ".asf") || ends_with(file, ".wmv") ||
235 ends_with(file, ".mp4") || ends_with(file, ".m4v") ||
236 ends_with(file, ".mts") || ends_with(file, ".m2ts") ||
237 ends_with(file, ".m2t") || ends_with(file, ".mkv") ||
238 ends_with(file, ".vob") || ends_with(file, ".ts") ||
239 ends_with(file, ".flv") || ends_with(file, ".xvid") ||
240 #ifdef TIVO_SUPPORT
241 ends_with(file, ".TiVo") ||
242 #endif
243 ends_with(file, ".mov") || ends_with(file, ".3gp"));
247 is_audio(const char * file)
249 return (ends_with(file, ".mp3") || ends_with(file, ".flac") ||
250 ends_with(file, ".wma") || ends_with(file, ".asf") ||
251 ends_with(file, ".fla") || ends_with(file, ".flc") ||
252 ends_with(file, ".m4a") || ends_with(file, ".aac") ||
253 ends_with(file, ".mp4") || ends_with(file, ".m4p") ||
254 ends_with(file, ".wav") || ends_with(file, ".ogg") ||
255 ends_with(file, ".pcm") || ends_with(file, ".3gp"));
259 is_image(const char * file)
261 return (ends_with(file, ".jpg") || ends_with(file, ".jpeg"));
265 is_playlist(const char * file)
267 return (ends_with(file, ".m3u") || ends_with(file, ".pls"));
271 is_album_art(const char * name)
273 struct album_art_name_s * album_art_name;
275 /* Check if this file name matches one of the default album art names */
276 for( album_art_name = album_art_names; album_art_name; album_art_name = album_art_name->next )
278 if( album_art_name->wildcard )
280 if( strncmp(album_art_name->name, name, strlen(album_art_name->name)) == 0 )
281 break;
283 else
285 if( strcmp(album_art_name->name, name) == 0 )
286 break;
290 return (album_art_name ? 1 : 0);
294 resolve_unknown_type(const char * path, enum media_types dir_type)
296 struct stat entry;
297 unsigned char type = TYPE_UNKNOWN;
298 char str_buf[PATH_MAX];
299 ssize_t len;
301 if( lstat(path, &entry) == 0 )
303 if( S_ISLNK(entry.st_mode) )
305 if( (len = readlink(path, str_buf, PATH_MAX-1)) > 0 )
307 str_buf[len] = '\0';
308 //DEBUG DPRINTF(E_DEBUG, L_GENERAL, "Checking for recursive symbolic link: %s (%s)\n", path, str_buf);
309 if( strncmp(path, str_buf, strlen(str_buf)) == 0 )
311 DPRINTF(E_DEBUG, L_GENERAL, "Ignoring recursive symbolic link: %s (%s)\n", path, str_buf);
312 return type;
315 stat(path, &entry);
318 if( S_ISDIR(entry.st_mode) )
320 type = TYPE_DIR;
322 else if( S_ISREG(entry.st_mode) )
324 switch( dir_type )
326 case ALL_MEDIA:
327 if( is_image(path) ||
328 is_audio(path) ||
329 is_video(path) ||
330 is_playlist(path) )
331 type = TYPE_FILE;
332 break;
333 case AUDIO_ONLY:
334 if( is_audio(path) ||
335 is_playlist(path) )
336 type = TYPE_FILE;
337 break;
338 case VIDEO_ONLY:
339 if( is_video(path) )
340 type = TYPE_FILE;
341 break;
342 case IMAGES_ONLY:
343 if( is_image(path) )
344 type = TYPE_FILE;
345 break;
346 default:
347 break;
351 return type;
354 void
355 begin_scan()
357 FILE * flag;
359 #ifdef READYNAS
360 flag = fopen("/ramfs/.upnp-av_scan", "w");
361 if( flag )
362 fclose(flag);
363 #else
364 mkdir("/var/notice", 0755);
365 flag = fopen("/var/notice/dlna", "w");
366 if( flag )
368 fprintf(flag, "Scan in progress");
369 fclose(flag);
371 #endif
374 void
375 end_scan()
377 #ifdef READYNAS
378 if( access("/ramfs/.rescan_done", F_OK) == 0 )
379 system("/bin/sh /ramfs/.rescan_done");
380 unlink("/ramfs/.upnp-av_scan");
381 #else
382 unlink("/var/notice/dlna");
383 #endif