1 /*
2  * MOC - music on console
3  * Copyright (C) 2004 - 2005 Damian Pietras <daper@daper.net>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  */
11 
12 #ifdef HAVE_CONFIG_H
13 # include "config.h"
14 # undef malloc
15 #endif
16 
17 #include <stdio.h>
18 #include <stdarg.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <strings.h>
22 #include <ctype.h>
23 #include <assert.h>
24 #include <unistd.h>
25 #include <sys/types.h>
26 #include <pwd.h>
27 #include <errno.h>
28 #ifdef HAVE_SYSLOG
29 #include <syslog.h>
30 #endif
31 
32 #include "common.h"
33 #include "server.h"
34 #include "interface.h"
35 #include "interface_elements.h"
36 #include "log.h"
37 #include "options.h"
38 
39 static int im_server = 0; /* Am I the server? */
40 
error(const char * format,...)41 void error (const char *format, ...)
42 {
43 	va_list va;
44 	char *msg;
45 
46 	va_start (va, format);
47 	msg = format_msg_va (format, va);
48 	va_end (va);
49 
50 	if (im_server)
51 		server_error (msg);
52 	else
53 		interface_error (msg);
54 
55 	free (msg);
56 }
57 
58 /* End program with a message. Use when an error occurs and we can't recover.
59  * If we're the server, then also log the message to the system log. */
internal_fatal(const char * file ATTR_UNUSED,int line ATTR_UNUSED,const char * function ATTR_UNUSED,const char * format,...)60 void internal_fatal (const char *file ATTR_UNUSED, int line ATTR_UNUSED,
61                  const char *function ATTR_UNUSED, const char *format, ...)
62 {
63 	va_list va;
64 	char *msg;
65 
66 	windows_reset ();
67 
68 	va_start (va, format);
69 	msg = format_msg_va (format, va);
70 	fprintf (stderr, "\nFATAL_ERROR: %s\n\n", msg);
71 #ifndef NDEBUG
72 	internal_logit (file, line, function, "FATAL ERROR: %s", msg);
73 #endif
74 	va_end (va);
75 
76 	log_close ();
77 
78 #ifdef HAVE_SYSLOG
79 	if (im_server)
80 		syslog (LOG_USER|LOG_ERR, "%s", msg);
81 #endif
82 
83 	free (msg);
84 
85 	exit (EXIT_FATAL);
86 }
87 
xmalloc(size_t size)88 void *xmalloc (size_t size)
89 {
90 	void *p;
91 
92 #ifndef HAVE_MALLOC
93 	size = MAX(1, size);
94 #endif
95 
96 	if ((p = malloc(size)) == NULL)
97 		fatal ("Can't allocate memory!");
98 	return p;
99 }
100 
xcalloc(size_t nmemb,size_t size)101 void *xcalloc (size_t nmemb, size_t size)
102 {
103 	void *p;
104 
105 	if ((p = calloc(nmemb, size)) == NULL)
106 		fatal ("Can't allocate memory!");
107 	return p;
108 }
109 
xrealloc(void * ptr,const size_t size)110 void *xrealloc (void *ptr, const size_t size)
111 {
112 	void *p;
113 
114 	if ((p = realloc(ptr, size)) == NULL && size != 0)
115 		fatal ("Can't allocate memory!");
116 	return p;
117 }
118 
xstrdup(const char * s)119 char *xstrdup (const char *s)
120 {
121 	char *n;
122 
123 	if (s && (n = strdup(s)) == NULL)
124 		fatal ("Can't allocate memory!");
125 
126 	return s ? n : NULL;
127 }
128 
set_me_server()129 void set_me_server ()
130 {
131 	im_server = 1;
132 }
133 
str_repl(char * target,const char * oldstr,const char * newstr)134 char *str_repl (char *target, const char *oldstr, const char *newstr)
135 {
136 	size_t oldstr_len = strlen(oldstr);
137 	size_t newstr_len = strlen(newstr);
138 	size_t target_len = strlen(target);
139 	size_t target_max = target_len;
140 	size_t s, p;
141 	char *needle;
142 	for (s = 0; (needle = strstr(target + s, oldstr)) != NULL; s = p + newstr_len) {
143 		target_len += newstr_len - oldstr_len;
144 		p = needle - target;
145 		if (target_len + 1 > target_max) {
146 			target_max = MAX(target_len + 1, target_max * 2);
147 			target = xrealloc(target, target_max);
148 		}
149 		memmove(target + p + newstr_len, target + p + oldstr_len, target_len - p - newstr_len + 1);
150 		memcpy(target + p, newstr, newstr_len);
151 	}
152 	target = xrealloc(target, target_len + 1);
153 	return target;
154 }
155 
156 /* Extract a substring starting at 'src' for length 'len' and remove
157  * any leading and trailing whitespace.  Return NULL if unable.  */
trim(const char * src,size_t len)158 char *trim (const char *src, size_t len)
159 {
160 	char *result;
161 	const char *first, *last;
162 
163 	for (last = &src[len - 1]; last >= src; last -= 1) {
164 		if (!isspace (*last))
165 			break;
166 	}
167 	if (last < src)
168 		return NULL;
169 
170 	for (first = src; first <= last; first += 1) {
171 		if (!isspace (*first))
172 			break;
173 	}
174 	if (first > last)
175 		return NULL;
176 
177 	last += 1;
178 	result = xcalloc (last - first + 1, sizeof (char));
179 	strncpy (result, first, last - first);
180 	result[last - first] = 0x00;
181 
182 	return result;
183 }
184 
185 /* Format argument values according to 'format' and return it as a
186  * malloc()ed string. */
format_msg(const char * format,...)187 char *format_msg (const char *format, ...)
188 {
189 	char *result;
190 	va_list va;
191 
192 	va_start (va, format);
193 	result = format_msg_va (format, va);
194 	va_end (va);
195 
196 	return result;
197 }
198 
199 /* Format a vararg list according to 'format' and return it as a
200  * malloc()ed string. */
format_msg_va(const char * format,va_list va)201 char *format_msg_va (const char *format, va_list va)
202 {
203 	int len;
204 	char *result;
205 	va_list va_copy;
206 
207 	va_copy (va_copy, va);
208 	len = vsnprintf (NULL, 0, format, va_copy) + 1;
209 	va_end (va_copy);
210 	result = xmalloc (len);
211 	vsnprintf (result, len, format, va);
212 
213 	return result;
214 }
215 
216 /* Return true iff the argument would be a syntactically valid symbol.
217  * (Note that the so-called "peculiar indentifiers" are disallowed here.) */
is_valid_symbol(const char * candidate)218 bool is_valid_symbol (const char *candidate)
219 {
220 	size_t len;
221 	bool result;
222 	const char *first = "+-.0123456789@";
223 	const char *valid = "abcdefghijklmnopqrstuvwxyz"
224 	                    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
225 	                    "0123456789"
226 	                    "@?!.+-*/<=>:$%^&_~";
227 
228 	result = false;
229 	len = strlen (candidate);
230 	if (len > 0 && len == strspn (candidate, valid) &&
231 	               strchr (first, candidate[0]) == NULL)
232 		result = true;
233 
234 	return result;
235 }
236 
237 /* Return path to a file in MOC config directory. NOT THREAD SAFE */
create_file_name(const char * file)238 char *create_file_name (const char *file)
239 {
240 	static char fname[PATH_MAX];
241 	char *moc_dir = options_get_str ("MOCDir");
242 
243 	if (moc_dir[0] == '~') {
244 		if (snprintf(fname, sizeof(fname), "%s/%s/%s", get_home (),
245 				(moc_dir[1] == '/') ? moc_dir + 2 : moc_dir + 1,
246 				file)
247 				>= (int)sizeof(fname))
248 			fatal ("Path too long!");
249 	}
250 	else if (snprintf(fname, sizeof(fname), "%s/%s", moc_dir, file)
251 			>= (int)sizeof(fname))
252 		fatal ("Path too long!");
253 
254 	return fname;
255 }
256 
257 /* Convert time in second to min:sec text format. buff must be 6 chars long. */
sec_to_min(char * buff,const int seconds)258 void sec_to_min (char *buff, const int seconds)
259 {
260 	assert (seconds >= 0);
261 
262 	if (seconds < 6000) {
263 
264 		/* the time is less than 99:59 */
265 		int min, sec;
266 
267 		min = seconds / 60;
268 		sec = seconds % 60;
269 
270 		snprintf (buff, 6, "%02d:%02d", min, sec);
271 	}
272 	else if (seconds < 10000 * 60)
273 
274 		/* the time is less than 9999 minutes */
275 		snprintf (buff, 6, "%4dm", seconds/60);
276 	else
277 		strcpy (buff, "!!!!!");
278 }
279 
280 /* Determine and return the path of the user's home directory. */
get_home()281 const char *get_home ()
282 {
283 	static const char *home = NULL;
284 	struct passwd *passwd;
285 
286 	if (home == NULL) {
287 		home = xstrdup (getenv ("HOME"));
288 		if (home == NULL) {
289 			errno = 0;
290 			passwd = getpwuid (geteuid ());
291 			if (passwd)
292 				home = xstrdup (passwd->pw_dir);
293 			else
294 				if (errno != 0)
295 					logit ("getpwuid(%d): %s", geteuid (), strerror (errno));
296 		}
297 	}
298 
299 	return home;
300 }
301