1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include <ctype.h>
5 #include <assert.h>
6 
7 #include <unistd.h>
8 #include <sys/types.h>
9 #include <sys/stat.h>
10 #include <fcntl.h>
11 #include <errno.h>
12 
13 #include "songdb.h"
14 #include "uadeconf.h"
15 #include "md5.h"
16 #include "unixatomic.h"
17 #include "ossupport.h"
18 #include "uadeconfig.h"
19 #include "support.h"
20 #include "uadeconstants.h"
21 
22 #define NORM_ID "n="
23 #define NORM_ID_LENGTH 2
24 
25 #define eserror(fmt, args...) do { fprintf(stderr, "song.conf error on line %zd: " fmt "\n", lineno, ## args); exit(-1); } while (0)
26 
27 
28 struct eaglesong {
29 	int flags;
30 	char md5[33];
31 	struct uade_attribute *attributes;
32 };
33 
34 struct persub {
35 	int sub;
36 	char *normalisation;
37 };
38 
39 static struct uade_content *contentchecksums;
40 static size_t nccused;		/* number of valid entries in content db */
41 static size_t nccalloc;		/* number of allocated entries for content db */
42 static int ccmodified;
43 static int cccorrupted;
44 
45 static int nsongs;
46 static struct eaglesong *songstore;
47 
48 static int escompare(const void *a, const void *b);
49 static struct uade_content *get_content(const char *md5);
50 
51 
add_sub_normalisation(struct uade_content * n,char * normalisation)52 static void add_sub_normalisation(struct uade_content *n, char *normalisation)
53 {
54 	struct persub *subinfo;
55 	char *endptr;
56 
57 	subinfo = malloc(sizeof(*subinfo));
58 	if (subinfo == NULL)
59 		uadeerror("Can't allocate memory for normalisation entry\n");
60 
61 	subinfo->sub = strtol(normalisation, &endptr, 10);
62 	if (*endptr != ',' || subinfo->sub < 0) {
63 		fprintf(stderr, "Invalid normalisation entry: %s\n", normalisation);
64 		return;
65 	}
66 
67 	subinfo->normalisation = strdup(endptr + 1);
68 	if (subinfo->normalisation == NULL)
69 		uadeerror("Can't allocate memory for normalisation string\n");
70 
71 	vplist_append(n->subs, subinfo);
72 }
73 
74 /* Compare function for bsearch() and qsort() to sort songs with respect
75    to their md5sums */
contentcompare(const void * a,const void * b)76 static int contentcompare(const void *a, const void *b)
77 {
78 	return strcasecmp(((struct uade_content *)a)->md5,
79 			  ((struct uade_content *)b)->md5);
80 }
81 
escompare(const void * a,const void * b)82 static int escompare(const void *a, const void *b)
83 {
84 	return strcasecmp(((struct eaglesong *)a)->md5,
85 			  ((struct eaglesong *)b)->md5);
86 }
87 
get_content(const char * md5)88 static struct uade_content *get_content(const char *md5)
89 {
90 	struct uade_content key;
91 
92 	if (contentchecksums == NULL)
93 		return NULL;
94 
95 	memset(&key, 0, sizeof key);
96 	strlcpy(key.md5, md5, sizeof key.md5);
97 
98 	return bsearch(&key, contentchecksums, nccused,
99 		       sizeof contentchecksums[0], contentcompare);
100 }
101 
create_content_checksum(const char * md5,uint32_t playtime)102 static struct uade_content *create_content_checksum(const char *md5,
103 						    uint32_t playtime)
104 {
105 	struct uade_content *n;
106 
107 	if (nccused == nccalloc) {
108 		nccalloc = MAX(nccalloc * 2, 16);
109 		n = realloc(contentchecksums,
110 			    nccalloc * sizeof(struct uade_content));
111 		if (n == NULL) {
112 			fprintf(stderr,
113 				"uade: No memory for new content checksums.\n");
114 			return NULL;
115 		}
116 		contentchecksums = n;
117 	}
118 
119 	n = &contentchecksums[nccused];
120 
121 	if (md5 == NULL)
122 		return n;
123 
124 	nccused++;
125 
126 	ccmodified = 1;
127 
128 	memset(n, 0, sizeof(*n));
129 	strlcpy(n->md5, md5, sizeof(n->md5));
130 	n->playtime = playtime;
131 
132 	n->subs = vplist_create(1);
133 
134 	return n;
135 }
136 
md5_from_buffer(char * dest,size_t destlen,uint8_t * buf,size_t bufsize)137 static void md5_from_buffer(char *dest, size_t destlen,
138 			    uint8_t * buf, size_t bufsize)
139 {
140 	uint8_t md5[16];
141 	int ret;
142 	MD5_CTX ctx;
143 	MD5Init(&ctx);
144 	MD5Update(&ctx, buf, bufsize);
145 	MD5Final(md5, &ctx);
146 	ret =
147 	    snprintf(dest, destlen,
148 		     "%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x",
149 		     md5[0], md5[1], md5[2], md5[3], md5[4], md5[5], md5[6],
150 		     md5[7], md5[8], md5[9], md5[10], md5[11], md5[12], md5[13],
151 		     md5[14], md5[15]);
152 	if (ret >= destlen || ret != 32) {
153 		fprintf(stderr, "md5 buffer error (%d/%zd)\n", ret, destlen);
154 		exit(1);
155 	}
156 }
157 
update_playtime(struct uade_content * n,uint32_t playtime)158 static void update_playtime(struct uade_content *n, uint32_t playtime)
159 {
160 	if (n->playtime != playtime) {
161 		ccmodified = 1;
162 		n->playtime = playtime;
163 	}
164 }
165 
sort_content_checksums(void)166 static void sort_content_checksums(void)
167 {
168 	if (contentchecksums == NULL)
169 		return;
170 
171 	qsort(contentchecksums, nccused, sizeof contentchecksums[0],
172 	      contentcompare);
173 }
174 
175 /* replace must be zero if content db is unsorted */
uade_add_playtime(const char * md5,uint32_t playtime)176 struct uade_content *uade_add_playtime(const char *md5, uint32_t playtime)
177 {
178 	struct uade_content *n;
179 
180 	/* If content db hasn't been read into memory already, it is not used */
181 	if (contentchecksums == NULL)
182 		return NULL;
183 
184 	/* Do not record song shorter than 3 secs */
185 	if (playtime < 3000)
186 		return NULL;
187 
188 	if (strlen(md5) != 32)
189 		return NULL;
190 
191 	n = get_content(md5);
192 	if (n != NULL) {
193 		update_playtime(n, playtime);
194 		return n;
195 	}
196 
197 	n = create_content_checksum(md5, playtime);
198 
199 	sort_content_checksums();
200 
201 	return n;
202 }
203 
uade_lookup_volume_normalisation(struct uade_state * state)204 void uade_lookup_volume_normalisation(struct uade_state *state)
205 {
206 	size_t i, nsubs;
207 	struct uade_effect *ue = &state->effects;
208 	struct uade_config *uc = &state->config;
209 	struct uade_song *us = state->song;
210 	struct uade_content *content = get_content(us->md5);
211 
212 	if (content != NULL) {
213 
214 		nsubs = vplist_len(content->subs);
215 
216 		for (i = 0; i < nsubs; i++) {
217 
218 			struct persub *subinfo = vplist_get(content->subs, i);
219 
220 			if (subinfo->sub == us->cur_subsong) {
221 				uade_set_config_option(uc, UC_NORMALISE,
222 						       subinfo->normalisation);
223 				uade_effect_normalise_unserialise(uc->
224 								  normalise_parameter);
225 				uade_effect_enable(ue, UADE_EFFECT_NORMALISE);
226 				break;
227 			}
228 		}
229 	}
230 }
231 
get_song_flags_and_attributes_from_songstore(struct uade_song * us)232 static void get_song_flags_and_attributes_from_songstore(struct uade_song *us)
233 {
234 	struct eaglesong key;
235 	struct eaglesong *es;
236 
237 	if (songstore != NULL) {
238 		/* Lookup md5 from the songdb */
239 		strlcpy(key.md5, us->md5, sizeof key.md5);
240 		es = bsearch(&key, songstore, nsongs, sizeof songstore[0], escompare);
241 
242 		if (es != NULL) {
243 			/* Found -> copy flags and attributes from database */
244 			us->flags |= es->flags;
245 			us->songattributes = es->attributes;
246 		}
247 	}
248 }
249 
uade_alloc_song(struct uade_state * state,const char * filename)250 int uade_alloc_song(struct uade_state *state, const char *filename)
251 {
252 	struct uade_song *us;
253 	struct uade_content *content;
254 
255 	state->song = NULL;
256 
257 	us = calloc(1, sizeof *us);
258 	if (us == NULL)
259 		goto error;
260 
261 	strlcpy(us->module_filename, filename, sizeof us->module_filename);
262 
263 	us->buf = atomic_read_file(&us->bufsize, filename);
264 	if (us->buf == NULL)
265 		goto error;
266 
267 	/* Compute an md5sum of the song */
268 	md5_from_buffer(us->md5, sizeof us->md5, us->buf, us->bufsize);
269 
270 	/* Needs us->md5 sum */
271 	get_song_flags_and_attributes_from_songstore(us);
272 
273 	/* Lookup playtime from content database */
274 	us->playtime = -1;
275 	content = get_content(us->md5);
276 	if (content != NULL && content->playtime > 0)
277 		us->playtime = content->playtime;
278 
279 	/* We can't know subsong numbers yet. The eagleplayer will report them
280 	 * in the playback state */
281 	us->min_subsong = us->max_subsong = us->cur_subsong = -1;
282 
283 	state->song = us;
284 	return 1;
285 
286       error:
287 	if (us != NULL) {
288 		free(us->buf);
289 		free(us);
290 	}
291 	return 0;
292 }
293 
uade_open_and_lock(const char * filename,int create)294 static int uade_open_and_lock(const char *filename, int create)
295 {
296 	int fd, ret;
297 	fd = open(filename, O_RDWR);
298 	if (fd < 0) {
299 		if (errno == ENOENT && create) {
300 			fd = open(filename, O_RDWR | O_CREAT,
301 				  S_IRUSR | S_IWUSR);
302 			if (fd < 0)
303 				return -1;
304 		} else {
305 			return -1;
306 		}
307 	}
308 #ifndef UADE_HAVE_CYGWIN
309 	ret = lockf(fd, F_LOCK, 0);
310 	if (ret) {
311 		fprintf(stderr, "uade: Unable to lock song.conf: %s (%s)\n",
312 			filename, strerror(errno));
313 		atomic_close(fd);
314 		return -1;
315 	}
316 #endif
317 
318 	return fd;
319 }
320 
321 
store_playtime(const char * md5,long playtime,int * newccmodified,size_t oldnccused)322 static struct uade_content *store_playtime(const char *md5, long playtime,
323 					   int *newccmodified,
324 					   size_t oldnccused)
325 {
326 	struct uade_content *n = NULL;
327 
328 	if (oldnccused > 0) {
329 		struct uade_content key;
330 		memset(&key, 0, sizeof key);
331 		strlcpy(key.md5, md5, sizeof key.md5);
332 
333 		/* We use "oldnccused" here as the length, while new entries
334 		   are added in unsorted manner to the end of the array */
335 		n = bsearch(&key, contentchecksums, oldnccused,
336 			    sizeof contentchecksums[0], contentcompare);
337 		if (n == NULL)
338 			/* new songs on disk db -> merge -> need saving */
339 			*newccmodified = 1;
340 	}
341 
342 	/* We value a playtime determined during run-time over
343 	   a database value */
344 	if (n == NULL) {
345 		/* Note, create_content_checksum() makes "ccmodified"
346 		   true, which we work-around later with the "newccmodified" */
347 		n = create_content_checksum(md5, (uint32_t) playtime);
348 	}
349 
350 	if (n == NULL) {
351 		/* No memory, fuck. We shouldn't save anything to
352 		   avoid losing data. */
353 		fprintf(stderr,
354 			"uade: Warning, no memory for the song database\n");
355 		cccorrupted = 1;
356 	}
357 
358 	return n;
359 }
360 
361 
362 
uade_read_content_db(const char * filename)363 int uade_read_content_db(const char *filename)
364 {
365 	char line[1024];
366 	FILE *f;
367 	size_t lineno = 0;
368 	long playtime;
369 	int i, j, nexti;
370 	char *id, *eptr;
371 	char numberstr[1024];
372 	char *md5;
373 
374 	/* We make backups of some variables because following loop will
375 	   make it always true, which is not what we want. The end result should
376 	   be that ccmodified is true in following cases only:
377 	   1. the in-memory db is already dirty
378 	   2. the in-memory db gets new data from disk db (merge operation)
379 	   Otherwise ccmodified should be false. */
380 	int newccmodified = ccmodified;
381 	size_t oldnccused = nccused;
382 	int fd;
383 	struct uade_content *n;
384 
385 	/* Try to create a database if it doesn't exist */
386 	if (contentchecksums == NULL
387 	    && create_content_checksum(NULL, 0) == NULL)
388 		return 0;
389 
390 	fd = uade_open_and_lock(filename, 0);
391 	if (fd < 0) {
392 		fprintf(stderr, "uade: Can not find %s\n", filename);
393 		return 0;
394 	}
395 
396 	f = fdopen(fd, "r");
397 	if (f == NULL) {
398 		fprintf(stderr, "uade: Can not create FILE structure for %s\n",
399 			filename);
400 		close(fd);
401 		return 0;
402 	}
403 
404 	while (xfgets(line, sizeof line, f) != NULL) {
405 		lineno++;
406 
407 		if (line[0] == '#')
408 			continue;
409 
410 		md5 = line;
411 		i = skip_and_terminate_word(line, 0);
412 		if (i < 0)
413 			continue; /* playtime doesn't exist */
414 
415 		for (j = 0; isxdigit(line[j]); j++);
416 
417 		if (j != 32)
418 			continue; /* is not a valid md5sum */
419 
420 		/* Grab and validate playtime (in milliseconds) */
421 		nexti = skip_and_terminate_word(line, i);
422 
423 		playtime = strtol(&line[i], &eptr, 10);
424 		if (*eptr != 0 || playtime < 0) {
425 			fprintf(stderr, "Invalid playtime for md5 %s on contentdb line %zd: %s\n", md5, lineno, numberstr);
426 			continue;
427 		}
428 
429 		n = store_playtime(md5, playtime, &newccmodified, oldnccused);
430 		if (n == NULL)
431 			continue;
432 
433 		i = nexti; /* Note, it could be that i < 0 */
434 
435 		/* Get rest of the directives in a loop */
436 		while (i >= 0) {
437 			id = &line[i];
438 			i = skip_and_terminate_word(line, i);
439 
440 			/* Subsong volume normalisation: n=sub1,XXX */
441 			if (strncmp(id, NORM_ID, NORM_ID_LENGTH) == 0) {
442 				id += NORM_ID_LENGTH;
443 				add_sub_normalisation(n, id);
444 			} else {
445 				fprintf(stderr,	"Unknown contentdb directive on line %zd: %s\n", lineno, id);
446 			}
447 		}
448 	}
449 	fclose(f);
450 
451 	ccmodified = newccmodified;
452 
453 	sort_content_checksums();
454 
455 	return 1;
456 }
457 
uade_read_song_conf(const char * filename)458 int uade_read_song_conf(const char *filename)
459 {
460 	FILE *f = NULL;
461 	struct eaglesong *s;
462 	size_t allocated;
463 	size_t lineno = 0;
464 	size_t i;
465 	int fd;
466 
467 	fd = uade_open_and_lock(filename, 1);
468 	/* open_and_lock() may fail without harm (it's actually supposed to
469 	   fail if the process does not have lock (write) permissions to
470 	   the song.conf file */
471 
472 	f = fopen(filename, "r");
473 	if (f == NULL)
474 		goto error;
475 
476 	nsongs = 0;
477 	allocated = 16;
478 	songstore = calloc(allocated, sizeof songstore[0]);
479 	if (songstore == NULL)
480 		eserror("No memory for song store.");
481 
482 	while (1) {
483 		char **items;
484 		size_t nitems;
485 
486 		items = read_and_split_lines(&nitems, &lineno, f,
487 					     UADE_WS_DELIMITERS);
488 		if (items == NULL)
489 			break;
490 
491 		assert(nitems > 0);
492 
493 		if (nsongs == allocated) {
494 			allocated *= 2;
495 			songstore = realloc(songstore, allocated * sizeof(songstore[0]));
496 			if (songstore == NULL)
497 				eserror("No memory for players.");
498 		}
499 
500 		s = &songstore[nsongs];
501 		nsongs++;
502 
503 		memset(s, 0, sizeof s[0]);
504 
505 		if (strncasecmp(items[0], "md5=", 4) != 0) {
506 			fprintf(stderr, "Line %zd must begin with md5= in %s\n",
507 				lineno, filename);
508 			free(items);
509 			continue;
510 		}
511 		if (strlcpy(s->md5, items[0] + 4, sizeof s->md5) !=
512 		    ((sizeof s->md5) - 1)) {
513 			fprintf(stderr,
514 				"Line %zd in %s has too long an md5sum.\n",
515 				lineno, filename);
516 			free(items);
517 			continue;
518 		}
519 
520 		for (i = 1; i < nitems; i++) {
521 			if (strncasecmp(items[i], "comment:", 7) == 0)
522 				break;
523 			if (uade_song_and_player_attribute(&s->attributes, &s->flags, items[i], lineno))
524 				continue;
525 			fprintf(stderr, "song option %s is invalid\n", items[i]);
526 		}
527 
528 		for (i = 0; items[i] != NULL; i++)
529 			free(items[i]);
530 
531 		free(items);
532 	}
533 
534 	fclose(f);
535 
536 	/* we may not have the file locked */
537 	if (fd >= 0)
538 		atomic_close(fd);	/* lock is closed too */
539 
540 	/* Sort MD5 sums for binary searching songs */
541 	qsort(songstore, nsongs, sizeof songstore[0], escompare);
542 	return 1;
543 
544       error:
545 	if (f)
546 		fclose(f);
547 	if (fd >= 0)
548 		atomic_close(fd);
549 	return 0;
550 }
551 
uade_save_content_db(const char * filename)552 void uade_save_content_db(const char *filename)
553 {
554 	int fd;
555 	FILE *f;
556 	size_t i;
557 
558 	if (ccmodified == 0 || cccorrupted)
559 		return;
560 
561 	fd = uade_open_and_lock(filename, 1);
562 	if (fd < 0) {
563 		fprintf(stderr, "uade: Can not write content db: %s\n",
564 			filename);
565 		return;
566 	}
567 
568 	f = fdopen(fd, "w");
569 	if (f == NULL) {
570 		fprintf(stderr,
571 			"uade: Can not create a FILE structure for content db: %s\n",
572 			filename);
573 		close(fd);
574 		return;
575 	}
576 
577 	for (i = 0; i < nccused; i++) {
578 		char str[1024];
579 		size_t subi, nsubs;
580 		size_t bindex, bleft;
581 		struct uade_content *n = &contentchecksums[i];
582 
583 		str[0] = 0;
584 
585 		bindex = 0;
586 		bleft = sizeof(str);
587 
588 		nsubs = vplist_len(n->subs);
589 
590 		for (subi = 0; subi < nsubs; subi++) {
591 			struct persub *sub = vplist_get(n->subs, subi);
592 			int ret;
593 			ret =
594 			    snprintf(&str[bindex], bleft, NORM_ID "%s ",
595 				     sub->normalisation);
596 			if (ret >= bleft) {
597 				fprintf(stderr,
598 					"Too much subsong infos for %s\n",
599 					n->md5);
600 				break;
601 			}
602 			bleft -= ret;
603 			bindex += ret;
604 		}
605 
606 		fprintf(f, "%s %u %s\n", n->md5, (unsigned int)n->playtime,
607 			str);
608 	}
609 
610 	ccmodified = 0;
611 
612 	fclose(f);
613 	fprintf(stderr, "uade: Saved %zd entries into content db.\n", nccused);
614 }
615 
uade_test_silence(void * buf,size_t size,struct uade_state * state)616 int uade_test_silence(void *buf, size_t size, struct uade_state *state)
617 {
618 	int i, s, exceptioncount;
619 	int16_t *sm;
620 	int nsamples;
621 	int64_t count = state->song->silence_count;
622 	int end = 0;
623 
624 	if (state->config.silence_timeout < 0)
625 		return 0;
626 
627 	exceptioncount = 0;
628 	sm = buf;
629 	nsamples = size / 2;
630 
631 	for (i = 0; i < nsamples; i++) {
632 		s = (sm[i] >= 0) ? sm[i] : -sm[i];
633 		if (s >= (32767 * 1 / 100)) {
634 			exceptioncount++;
635 			if (exceptioncount >= (size * 2 / 100)) {
636 				count = 0;
637 				break;
638 			}
639 		}
640 	}
641 
642 	if (i == nsamples) {
643 		count += size;
644 		if (count / (UADE_BYTES_PER_FRAME * state->config.frequency) >= state->config.silence_timeout) {
645 			count = 0;
646 			end = 1;
647 		}
648 	}
649 
650 	state->song->silence_count = count;
651 
652 	return end;
653 }
654 
uade_unalloc_song(struct uade_state * state)655 void uade_unalloc_song(struct uade_state *state)
656 {
657 	free(state->song->buf);
658 	state->song->buf = NULL;
659 
660 	free(state->song);
661 	state->song = NULL;
662 }
663 
uade_update_song_conf(const char * songconfin,const char * songconfout,const char * songname,const char * options)664 int uade_update_song_conf(const char *songconfin, const char *songconfout,
665 			  const char *songname, const char *options)
666 {
667 	int ret;
668 	int fd;
669 	char md5[33];
670 	void *mem = NULL;
671 	size_t filesize, newsize;
672 	int found = 0;
673 	size_t inputsize;
674 	char *input, *inputptr, *outputptr;
675 	size_t inputoffs;
676 	char newline[256];
677 	size_t i;
678 	int need_newline = 0;
679 
680 	if (strlen(options) > 128) {
681 		fprintf(stderr, "Too long song.conf options.\n");
682 		return 0;
683 	}
684 
685 	fd = uade_open_and_lock(songconfout, 1);
686 
687 	input = atomic_read_file(&inputsize, songconfin);
688 	if (input == NULL) {
689 		fprintf(stderr, "Can not read song.conf: %s\n", songconfin);
690 		atomic_close(fd);	/* closes the lock too */
691 		return 0;
692 	}
693 
694 	newsize = inputsize + strlen(options) + strlen(songname) + 64;
695 	mem = realloc(input, newsize);
696 	if (mem == NULL) {
697 		fprintf(stderr,
698 			"Can not realloc the input file buffer for song.conf.\n");
699 		free(input);
700 		atomic_close(fd);	/* closes the lock too */
701 		return 0;
702 	}
703 	input = mem;
704 
705 	mem = atomic_read_file(&filesize, songname);
706 	if (mem == NULL)
707 		goto error;
708 
709 	md5_from_buffer(md5, sizeof md5, mem, filesize);
710 
711 	inputptr = outputptr = input;
712 	inputoffs = 0;
713 
714 	while (inputoffs < inputsize) {
715 		if (inputptr[0] == '#')
716 			goto copyline;
717 
718 		if ((inputoffs + 37) >= inputsize)
719 			goto copyline;
720 
721 		if (strncasecmp(inputptr, "md5=", 4) != 0)
722 			goto copyline;
723 
724 		if (strncasecmp(inputptr + 4, md5, 32) == 0) {
725 			if (found) {
726 				fprintf(stderr,
727 					"Warning: dupe entry in song.conf: %s (%s)\n"
728 					"Need manual resolving.\n", songname,
729 					md5);
730 				goto copyline;
731 			}
732 			found = 1;
733 			snprintf(newline, sizeof newline, "md5=%s\t%s\n", md5,
734 				 options);
735 
736 			/* Skip this line. It will be appended later to the end of the buffer */
737 			for (i = inputoffs; i < inputsize; i++) {
738 				if (input[i] == '\n') {
739 					i = i + 1 - inputoffs;
740 					break;
741 				}
742 			}
743 			if (i == inputsize) {
744 				i = inputsize - inputoffs;
745 				found = 0;
746 				need_newline = 1;
747 			}
748 			inputoffs += i;
749 			inputptr += i;
750 			continue;
751 		}
752 
753 	      copyline:
754 		/* Copy the line */
755 		for (i = inputoffs; i < inputsize; i++) {
756 			if (input[i] == '\n') {
757 				i = i + 1 - inputoffs;
758 				break;
759 			}
760 		}
761 		if (i == inputsize) {
762 			i = inputsize - inputoffs;
763 			need_newline = 1;
764 		}
765 		memmove(outputptr, inputptr, i);
766 		inputoffs += i;
767 		inputptr += i;
768 		outputptr += i;
769 	}
770 
771 	if (need_newline) {
772 		snprintf(outputptr, 2, "\n");
773 		outputptr += 1;
774 	}
775 
776 	/* there is enough space */
777 	ret = snprintf(outputptr, PATH_MAX + 256, "md5=%s\t%s\tcomment %s\n",
778 		       md5, options, songname);
779 	outputptr += ret;
780 
781 	if (ftruncate(fd, 0)) {
782 		fprintf(stderr, "Can not truncate the file.\n");
783 		goto error;
784 	}
785 
786 	/* Final file size */
787 	i = (size_t) (outputptr - input);
788 
789 	if (atomic_write(fd, input, i) < i)
790 		fprintf(stderr,
791 			"Unable to write file contents back. Data loss happened. CRAP!\n");
792 
793       error:
794 	atomic_close(fd);	/* Closes the lock too */
795 	free(input);
796 	free(mem);
797 	return 1;
798 }
799