Busybox: Upgrade to 1.21.1 (stable). lsof active.
[tomato.git] / release / src / router / minidlna / utils.c
blob09e2007fb25ca4eb8d48e1c6634d31a49c2813a2
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 inline void
49 strncpyt(char *dst, const char *src, size_t len)
51 strncpy(dst, src, len);
52 dst[len-1] = '\0';
55 int
56 ends_with(const char * haystack, const char * needle)
58 const char * end;
59 int nlen = strlen(needle);
60 int hlen = strlen(haystack);
62 if( nlen > hlen )
63 return 0;
64 end = haystack + hlen - nlen;
66 return (strcasecmp(end, needle) ? 0 : 1);
69 char *
70 trim(char *str)
72 int i;
73 int len;
75 if (!str)
76 return(NULL);
78 len = strlen(str);
79 for (i=len-1; i >= 0 && isspace(str[i]); i--)
81 str[i] = '\0';
82 len--;
84 while (isspace(*str))
86 str++;
87 len--;
90 if (str[0] == '"' && str[len-1] == '"')
92 str[0] = '\0';
93 str[len-1] = '\0';
94 str++;
97 return str;
100 /* Find the first occurrence of p in s, where s is terminated by t */
101 char *
102 strstrc(const char *s, const char *p, const char t)
104 char *endptr;
105 size_t slen, plen;
107 endptr = strchr(s, t);
108 if (!endptr)
109 return strstr(s, p);
111 plen = strlen(p);
112 slen = endptr - s;
113 while (slen >= plen)
115 if (*s == *p && strncmp(s+1, p+1, plen-1) == 0)
116 return (char*)s;
117 s++;
118 slen--;
121 return NULL;
124 char *
125 strcasestrc(const char *s, const char *p, const char t)
127 char *endptr;
128 size_t slen, plen;
130 endptr = strchr(s, t);
131 if (!endptr)
132 return strcasestr(s, p);
134 plen = strlen(p);
135 slen = endptr - s;
136 while (slen >= plen)
138 if (*s == *p && strncasecmp(s+1, p+1, plen-1) == 0)
139 return (char*)s;
140 s++;
141 slen--;
144 return NULL;
147 char *
148 modifyString(char * string, const char * before, const char * after, short like)
150 int oldlen, newlen, chgcnt = 0;
151 char *s, *p, *t;
153 oldlen = strlen(before);
154 newlen = strlen(after);
155 if( newlen+like > oldlen )
157 s = string;
158 while( (p = strstr(s, before)) )
160 chgcnt++;
161 s = p+oldlen;
163 s = realloc(string, strlen(string)+((newlen-oldlen)*chgcnt)+1+like);
164 /* If we failed to realloc, return the original alloc'd string */
165 if( s )
166 string = s;
167 else
168 return string;
171 s = string;
172 while( s )
174 p = strcasestr(s, before);
175 if( !p )
176 return string;
177 memmove(p + newlen, p + oldlen, strlen(p + oldlen) + 1);
178 memcpy(p, after, newlen);
179 if( like )
181 t = p+newlen;
182 while( isspace(*t) )
183 t++;
184 if( *t == '"' )
186 if( like == 2 )
188 memmove(t+2, t+1, strlen(t+1)+1);
189 *++t = '%';
191 while( *++t != '"' )
192 continue;
193 memmove(t+1, t, strlen(t)+1);
194 *t = '%';
197 s = p + newlen;
200 return string;
203 char *
204 escape_tag(const char *tag, int force_alloc)
206 char *esc_tag = NULL;
208 if( strchr(tag, '&') || strchr(tag, '<') || strchr(tag, '>') || strchr(tag, '"') )
210 esc_tag = strdup(tag);
211 esc_tag = modifyString(esc_tag, "&", "&amp;amp;", 0);
212 esc_tag = modifyString(esc_tag, "<", "&amp;lt;", 0);
213 esc_tag = modifyString(esc_tag, ">", "&amp;gt;", 0);
214 esc_tag = modifyString(esc_tag, "\"", "&amp;quot;", 0);
216 else if( force_alloc )
217 esc_tag = strdup(tag);
219 return esc_tag;
222 void
223 strip_ext(char * name)
225 char * period;
227 period = strrchr(name, '.');
228 if( period )
229 *period = '\0';
232 /* Code basically stolen from busybox */
234 make_dir(char * path, mode_t mode)
236 char * s = path;
237 char c;
238 struct stat st;
240 do {
241 c = '\0';
243 /* Bypass leading non-'/'s and then subsequent '/'s. */
244 while (*s) {
245 if (*s == '/') {
246 do {
247 ++s;
248 } while (*s == '/');
249 c = *s; /* Save the current char */
250 *s = '\0'; /* and replace it with nul. */
251 break;
253 ++s;
256 if (mkdir(path, mode) < 0) {
257 /* If we failed for any other reason than the directory
258 * already exists, output a diagnostic and return -1.*/
259 if (errno != EEXIST || (stat(path, &st) < 0 || !S_ISDIR(st.st_mode))) {
260 DPRINTF(E_WARN, L_GENERAL, "make_dir: cannot create directory '%s'\n", path);
261 if (c)
262 *s = c;
263 return -1;
266 if (!c)
267 return 0;
269 /* Remove any inserted nul from the path. */
270 *s = c;
272 } while (1);
275 /* Simple, efficient hash function from Daniel J. Bernstein */
276 unsigned int
277 DJBHash(const char *str, int len)
279 unsigned int hash = 5381;
280 unsigned int i = 0;
282 for(i = 0; i < len; str++, i++)
284 hash = ((hash << 5) + hash) + (*str);
287 return hash;
291 is_video(const char * file)
293 return (ends_with(file, ".mpg") || ends_with(file, ".mpeg") ||
294 ends_with(file, ".avi") || ends_with(file, ".divx") ||
295 ends_with(file, ".asf") || ends_with(file, ".wmv") ||
296 ends_with(file, ".mp4") || ends_with(file, ".m4v") ||
297 ends_with(file, ".mts") || ends_with(file, ".m2ts") ||
298 ends_with(file, ".m2t") || ends_with(file, ".mkv") ||
299 ends_with(file, ".vob") || ends_with(file, ".ts") ||
300 ends_with(file, ".flv") || ends_with(file, ".xvid") ||
301 #ifdef TIVO_SUPPORT
302 ends_with(file, ".TiVo") ||
303 #endif
304 ends_with(file, ".mov") || ends_with(file, ".3gp"));
308 is_audio(const char * file)
310 return (ends_with(file, ".mp3") || ends_with(file, ".flac") ||
311 ends_with(file, ".wma") || ends_with(file, ".asf") ||
312 ends_with(file, ".fla") || ends_with(file, ".flc") ||
313 ends_with(file, ".m4a") || ends_with(file, ".aac") ||
314 ends_with(file, ".mp4") || ends_with(file, ".m4p") ||
315 ends_with(file, ".wav") || ends_with(file, ".ogg") ||
316 ends_with(file, ".pcm") || ends_with(file, ".3gp"));
320 is_image(const char * file)
322 return (ends_with(file, ".jpg") || ends_with(file, ".jpeg"));
326 is_playlist(const char * file)
328 return (ends_with(file, ".m3u") || ends_with(file, ".pls"));
332 is_album_art(const char * name)
334 struct album_art_name_s * album_art_name;
336 /* Check if this file name matches one of the default album art names */
337 for( album_art_name = album_art_names; album_art_name; album_art_name = album_art_name->next )
339 if( album_art_name->wildcard )
341 if( strncmp(album_art_name->name, name, strlen(album_art_name->name)) == 0 )
342 break;
344 else
346 if( strcmp(album_art_name->name, name) == 0 )
347 break;
351 return (album_art_name ? 1 : 0);
355 resolve_unknown_type(const char * path, enum media_types dir_type)
357 struct stat entry;
358 unsigned char type = TYPE_UNKNOWN;
359 char str_buf[PATH_MAX];
360 ssize_t len;
362 if( lstat(path, &entry) == 0 )
364 if( S_ISLNK(entry.st_mode) )
366 if( (len = readlink(path, str_buf, PATH_MAX-1)) > 0 )
368 str_buf[len] = '\0';
369 //DEBUG DPRINTF(E_DEBUG, L_GENERAL, "Checking for recursive symbolic link: %s (%s)\n", path, str_buf);
370 if( strncmp(path, str_buf, strlen(str_buf)) == 0 )
372 DPRINTF(E_DEBUG, L_GENERAL, "Ignoring recursive symbolic link: %s (%s)\n", path, str_buf);
373 return type;
376 stat(path, &entry);
379 if( S_ISDIR(entry.st_mode) )
381 type = TYPE_DIR;
383 else if( S_ISREG(entry.st_mode) )
385 switch( dir_type )
387 case ALL_MEDIA:
388 if( is_image(path) ||
389 is_audio(path) ||
390 is_video(path) ||
391 is_playlist(path) )
392 type = TYPE_FILE;
393 break;
394 case AUDIO_ONLY:
395 if( is_audio(path) ||
396 is_playlist(path) )
397 type = TYPE_FILE;
398 break;
399 case VIDEO_ONLY:
400 if( is_video(path) )
401 type = TYPE_FILE;
402 break;
403 case IMAGES_ONLY:
404 if( is_image(path) )
405 type = TYPE_FILE;
406 break;
407 default:
408 break;
412 return type;
415 void
416 begin_scan()
418 FILE * flag;
420 #ifdef READYNAS
421 flag = fopen("/ramfs/.upnp-av_scan", "w");
422 if( flag )
423 fclose(flag);
424 #else
425 mkdir("/var/notice", 0755);
426 flag = fopen("/var/notice/dlna", "w");
427 if( flag )
429 fprintf(flag, "Scan in progress");
430 fclose(flag);
432 #endif
435 void
436 end_scan()
438 #ifdef READYNAS
439 if( access("/ramfs/.rescan_done", F_OK) == 0 )
440 system("/bin/sh /ramfs/.rescan_done");
441 unlink("/ramfs/.upnp-av_scan");
442 #else
443 unlink("/var/notice/dlna");
444 #endif