1 /*
2 * Copyright 2008-2013 Various Authors
3 * Copyright 2004-2005 Timo Hirvonen
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of the
8 * License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include "misc.h"
20 #include "prog.h"
21 #include "xmalloc.h"
22 #include "xstrjoin.h"
23 #include "ui_curses.h"
24 #include "config/libdir.h"
25 #include "config/datadir.h"
26
27 #include <string.h>
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <unistd.h>
31 #include <errno.h>
32 #include <sys/stat.h>
33 #include <sys/types.h>
34 #include <dirent.h>
35 #include <stdarg.h>
36 #include <pwd.h>
37
38 const char *cmus_config_dir = NULL;
39 const char *cmus_playlist_dir = NULL;
40 const char *cmus_socket_path = NULL;
41 const char *cmus_data_dir = NULL;
42 const char *cmus_lib_dir = NULL;
43 const char *home_dir = NULL;
44
get_words(const char * text)45 char **get_words(const char *text)
46 {
47 char **words;
48 int i, j, count;
49
50 while (*text == ' ')
51 text++;
52
53 count = 0;
54 i = 0;
55 while (text[i]) {
56 count++;
57 while (text[i] && text[i] != ' ')
58 i++;
59 while (text[i] == ' ')
60 i++;
61 }
62 words = xnew(char *, count + 1);
63
64 i = 0;
65 j = 0;
66 while (text[i]) {
67 int start = i;
68
69 while (text[i] && text[i] != ' ')
70 i++;
71 words[j++] = xstrndup(text + start, i - start);
72 while (text[i] == ' ')
73 i++;
74 }
75 words[j] = NULL;
76 return words;
77 }
78
strptrcmp(const void * a,const void * b)79 int strptrcmp(const void *a, const void *b)
80 {
81 const char *as = *(char **)a;
82 const char *bs = *(char **)b;
83
84 return strcmp(as, bs);
85 }
86
strptrcoll(const void * a,const void * b)87 int strptrcoll(const void *a, const void *b)
88 {
89 const char *as = *(char **)a;
90 const char *bs = *(char **)b;
91
92 return strcoll(as, bs);
93 }
94
escape(const char * str)95 const char *escape(const char *str)
96 {
97 static char *buf = NULL;
98 static size_t alloc = 0;
99 size_t len = strlen(str);
100 size_t need = len * 2 + 1;
101 int s, d;
102
103 if (need > alloc) {
104 alloc = (need + 16) & ~(16 - 1);
105 buf = xrealloc(buf, alloc);
106 }
107
108 d = 0;
109 for (s = 0; str[s]; s++) {
110 if (str[s] == '\\') {
111 buf[d++] = '\\';
112 buf[d++] = '\\';
113 continue;
114 }
115 if (str[s] == '\n') {
116 buf[d++] = '\\';
117 buf[d++] = 'n';
118 continue;
119 }
120 buf[d++] = str[s];
121 }
122 buf[d] = 0;
123 return buf;
124 }
125
unescape(const char * str)126 const char *unescape(const char *str)
127 {
128 static char *buf = NULL;
129 static size_t alloc = 0;
130 size_t need = strlen(str) + 1;
131 int do_escape = 0;
132 int s, d;
133
134 if (need > alloc) {
135 alloc = (need + 16) & ~(16 - 1);
136 buf = xrealloc(buf, alloc);
137 }
138
139 d = 0;
140 for (s = 0; str[s]; s++) {
141 if (!do_escape && str[s] == '\\')
142 do_escape = 1;
143 else {
144 buf[d++] = (do_escape && str[s] == 'n') ? '\n' : str[s];
145 do_escape = 0;
146 }
147 }
148 buf[d] = 0;
149 return buf;
150 }
151
dir_exists(const char * dirname)152 static int dir_exists(const char *dirname)
153 {
154 DIR *dir;
155
156 dir = opendir(dirname);
157 if (dir == NULL) {
158 if (errno == ENOENT)
159 return 0;
160 return -1;
161 }
162 closedir(dir);
163 return 1;
164 }
165
make_dir(const char * dirname)166 static void make_dir(const char *dirname)
167 {
168 int rc;
169
170 rc = dir_exists(dirname);
171 if (rc == 1)
172 return;
173 if (rc == -1)
174 die_errno("error: opening `%s'", dirname);
175 rc = mkdir(dirname, 0700);
176 if (rc == -1)
177 die_errno("error: creating directory `%s'", dirname);
178 }
179
get_non_empty_env(const char * name)180 static char *get_non_empty_env(const char *name)
181 {
182 const char *val;
183
184 val = getenv(name);
185 if (val == NULL || val[0] == 0)
186 return NULL;
187 return xstrdup(val);
188 }
189
get_filename(const char * path)190 const char *get_filename(const char *path)
191 {
192 const char *file = strrchr(path, '/');
193 if (!file)
194 file = path;
195 else
196 file++;
197 if (!*file)
198 return NULL;
199 return file;
200 }
201
move_old_playlist(void)202 static void move_old_playlist(void)
203 {
204 char *default_playlist = xstrjoin(cmus_playlist_dir, "/default");
205 char *old_playlist = xstrjoin(cmus_config_dir, "/playlist.pl");
206 int rc = rename(old_playlist, default_playlist);
207 if (rc && errno != ENOENT)
208 die_errno("error: unable to move %s to playlist directory",
209 old_playlist);
210 free(default_playlist);
211 free(old_playlist);
212 }
213
misc_init(void)214 int misc_init(void)
215 {
216 char *xdg_runtime_dir = get_non_empty_env("XDG_RUNTIME_DIR");
217
218 home_dir = get_non_empty_env("HOME");
219 if (home_dir == NULL)
220 die("error: environment variable HOME not set\n");
221
222 cmus_config_dir = get_non_empty_env("CMUS_HOME");
223 if (cmus_config_dir == NULL) {
224 char *cmus_home = xstrjoin(home_dir, "/.cmus");
225 int cmus_home_exists = dir_exists(cmus_home);
226
227 if (cmus_home_exists == 1) {
228 cmus_config_dir = xstrdup(cmus_home);
229 } else if (cmus_home_exists == -1) {
230 die_errno("error: opening `%s'", cmus_home);
231 } else {
232 char *xdg_config_home = get_non_empty_env("XDG_CONFIG_HOME");
233 if (xdg_config_home == NULL) {
234 xdg_config_home = xstrjoin(home_dir, "/.config");
235 }
236
237 make_dir(xdg_config_home);
238 cmus_config_dir = xstrjoin(xdg_config_home, "/cmus");
239
240 free(xdg_config_home);
241 }
242
243 free(cmus_home);
244 }
245 make_dir(cmus_config_dir);
246
247 cmus_playlist_dir = get_non_empty_env("CMUS_PLAYLIST_DIR");
248 if (!cmus_playlist_dir)
249 cmus_playlist_dir = xstrjoin(cmus_config_dir, "/playlists");
250
251 int playlist_dir_is_new = dir_exists(cmus_playlist_dir) == 0;
252 make_dir(cmus_playlist_dir);
253 if (playlist_dir_is_new)
254 move_old_playlist();
255
256 cmus_socket_path = get_non_empty_env("CMUS_SOCKET");
257 if (cmus_socket_path == NULL) {
258 if (xdg_runtime_dir == NULL) {
259 cmus_socket_path = xstrjoin(cmus_config_dir, "/socket");
260 } else {
261 cmus_socket_path = xstrjoin(xdg_runtime_dir, "/cmus-socket");
262 }
263 }
264
265 cmus_lib_dir = getenv("CMUS_LIB_DIR");
266 if (!cmus_lib_dir)
267 cmus_lib_dir = LIBDIR "/cmus";
268
269 cmus_data_dir = getenv("CMUS_DATA_DIR");
270 if (!cmus_data_dir)
271 cmus_data_dir = DATADIR "/cmus";
272
273 free(xdg_runtime_dir);
274 return 0;
275 }
276
replaygain_decode(unsigned int field,int * gain)277 int replaygain_decode(unsigned int field, int *gain)
278 {
279 unsigned int name_code, originator_code, sign_bit, val;
280
281 name_code = (field >> 13) & 0x7;
282 if (!name_code || name_code > 2)
283 return 0;
284 originator_code = (field >> 10) & 0x7;
285 if (!originator_code)
286 return 0;
287 sign_bit = (field >> 9) & 0x1;
288 val = field & 0x1ff;
289 if (sign_bit && !val)
290 return 0;
291 *gain = (sign_bit ? -1 : 1) * val;
292 return name_code;
293 }
294
get_home_dir(const char * username)295 static char *get_home_dir(const char *username)
296 {
297 struct passwd *passwd;
298
299 if (username == NULL)
300 return xstrdup(home_dir);
301 passwd = getpwnam(username);
302 if (passwd == NULL)
303 return NULL;
304 /* don't free passwd */
305 return xstrdup(passwd->pw_dir);
306 }
307
expand_filename(const char * name)308 char *expand_filename(const char *name)
309 {
310 if (name[0] == '~') {
311 char *slash;
312
313 slash = strchr(name, '/');
314 if (slash) {
315 char *username, *home;
316
317 if (slash - name - 1 > 0) {
318 /* ~user/... */
319 username = xstrndup(name + 1, slash - name - 1);
320 } else {
321 /* ~/... */
322 username = NULL;
323 }
324 home = get_home_dir(username);
325 free(username);
326 if (home) {
327 char *expanded;
328
329 expanded = xstrjoin(home, slash);
330 free(home);
331 return expanded;
332 } else {
333 return xstrdup(name);
334 }
335 } else {
336 if (name[1] == 0) {
337 return xstrdup(home_dir);
338 } else {
339 char *home;
340
341 home = get_home_dir(name + 1);
342 if (home)
343 return home;
344 return xstrdup(name);
345 }
346 }
347 } else {
348 return xstrdup(name);
349 }
350 }
351
shuffle_array(void * array,size_t n,size_t size)352 void shuffle_array(void *array, size_t n, size_t size)
353 {
354 char tmp[size];
355 char *arr = array;
356 for (ssize_t i = 0; i < (ssize_t)n - 1; ++i) {
357 size_t rnd = (size_t) rand();
358 size_t j = i + rnd / (RAND_MAX / (n - i) + 1);
359 memcpy(tmp, arr + j * size, size);
360 memcpy(arr + j * size, arr + i * size, size);
361 memcpy(arr + i * size, tmp, size);
362 }
363 }
364