char *lists_strs_fmt (const lists_t_strs *list, const char *fmt) { int len, ix, rc; char *result, *ptr; assert (list); assert (strstr (fmt, "%s")); result = NULL; if (!lists_strs_empty (list)) { len = 0; for (ix = 0; ix < lists_strs_size (list); ix += 1) len += strlen (lists_strs_at (list, ix)); len += ix * (strlen (fmt) - 2); ptr = result = xmalloc (len + 1); for (ix = 0; ix < lists_strs_size (list); ix += 1) { rc = snprintf (ptr, len + 1, fmt, lists_strs_at (list, ix)); if (rc > len) fatal ("Allocated string area was too small!"); len -= rc; ptr += rc; } } return result; }
/* Reload saved strings into a list. The reloaded strings are appended * to the list. The number of items reloaded is returned. */ int lists_strs_load (lists_t_strs *list, const char **saved) { int size; assert (list); assert (saved); size = lists_strs_size (list); while (*saved) lists_strs_append (list, *saved++); return lists_strs_size (list) - size; }
/* Load all preferences given by the user in PreferredDecoders. */ static void load_preferences () { int ix; const char *preference; lists_t_strs *list; list = options_get_list ("PreferredDecoders"); for (ix = 0; ix < lists_strs_size (list); ix += 1) { preference = lists_strs_at (list, ix); load_each_preference (preference); } #ifdef DEBUG { char *names; decoder_t_preference *pref; for (pref = preferences; pref; pref = pref->next) { names = list_decoder_names (pref->decoder_list, pref->decoders); debug ("%s:%s", pref->source, names); free (names); } } #endif }
static void process_deferred_overrides (lists_t_strs *deferred) { int ix; bool cleared; const char marker[] = "*Marker*"; char **config_decoders; lists_t_strs *decoders_option; /* We need to shuffle the PreferredDecoders list into the * right order as we load any deferred overriding options. */ decoders_option = options_get_list ("PreferredDecoders"); lists_strs_reverse (decoders_option); config_decoders = lists_strs_save (decoders_option); lists_strs_clear (decoders_option); lists_strs_append (decoders_option, marker); for (ix = 0; ix < lists_strs_size (deferred); ix += 1) override_config_option (lists_strs_at (deferred, ix), NULL); cleared = lists_strs_empty (decoders_option) || strcmp (lists_strs_at (decoders_option, 0), marker) != 0; lists_strs_reverse (decoders_option); if (!cleared) { char **override_decoders; free (lists_strs_pop (decoders_option)); override_decoders = lists_strs_save (decoders_option); lists_strs_clear (decoders_option); lists_strs_load (decoders_option, config_decoders); lists_strs_load (decoders_option, override_decoders); free (override_decoders); } free (config_decoders); }
/* Put something into the log */ void internal_logit (const char *file, const int line, const char *function, const char *format, ...) { int len; char *msg, time_str[20]; struct timeval utc_time; va_list va; struct tm tm_time; const char fmt[] = "%s.%06u: %s:%d %s(): %s\n"; if (!logfp) { switch (logging_state) { case UNINITIALISED: buffered_log = lists_strs_new (128); logging_state = BUFFERING; break; case BUFFERING: /* Don't let storage run away on us. */ if (lists_strs_size (buffered_log) < lists_strs_capacity (buffered_log)) break; log_records_spilt += 1; return; case LOGGING: return; } } va_start (va, format); len = vsnprintf (NULL, 0, format, va) + 1; va_end (va); msg = xmalloc (len); va_start (va, format); vsnprintf (msg, len, format, va); va_end (va); gettimeofday (&utc_time, NULL); localtime_r (&utc_time.tv_sec, &tm_time); strftime (time_str, sizeof (time_str), "%b %e %T", &tm_time); if (logfp) { fprintf (logfp, fmt, time_str, (unsigned)utc_time.tv_usec, file, line, function, msg); fflush (logfp); } else { char *str; len = snprintf (NULL, 0, fmt, time_str, (unsigned)utc_time.tv_usec, file, line, function, msg); str = xmalloc (len + 1); snprintf (str, len + 1, fmt, time_str, (unsigned)utc_time.tv_usec, file, line, function, msg); lists_strs_push (buffered_log, str); } free (msg); }
/* Given a string, return true iff it exists in the list. */ bool lists_strs_exists (lists_t_strs *list, const char *sought) { bool result = false; assert (list); assert (sought); if (lists_strs_find (list, sought) < lists_strs_size (list)) result = true; return result; }
/* Given a string, return the index of the first list entry which matches * it. If not found, return the total number of entries. * The comparison is case-insensitive. */ int lists_strs_find (lists_t_strs *list, const char *sought) { int result; assert (list); assert (sought); for (result = 0; result < lists_strs_size (list); result += 1) { if (!strcasecmp (lists_strs_at (list, result), sought)) break; } return result; }
/* Build a preference's decoder list. */ static void load_decoders (decoder_t_preference *pref, lists_t_strs *tokens) { int ix, dx, asterisk_at; int decoder[PLUGINS_NUM]; const char *name; assert (pref); assert (tokens); asterisk_at = -1; /* Add the index of each known decoder to the decoders list. * Note the position following the first asterisk. */ for (ix = 1; ix < lists_strs_size (tokens); ix += 1) { name = lists_strs_at (tokens, ix); if (strcmp (name, "*")) load_each_decoder (pref, name); else if (asterisk_at == -1) asterisk_at = pref->decoders; } if (asterisk_at == -1) return; dx = 0; /* Find decoders not already listed. */ for (ix = 0; ix < plugins_num; ix += 1) { if (!is_listed_decoder (pref, ix)) decoder[dx++] = ix; } /* Splice asterisk decoders into the decoder list. */ for (ix = 0; ix < dx; ix += 1) { pref->decoder_list[pref->decoders++] = pref->decoder_list[asterisk_at + ix]; pref->decoder_list[asterisk_at + ix] = decoder[ix]; } assert (RANGE(0, pref->decoders, plugins_num)); }
/* Return a string of concatenated driver names. */ static char *list_decoder_names (int *decoder_list, int count) { int ix; char *result; lists_t_strs *names; if (count == 0) return xstrdup (""); names = lists_strs_new (count); for (ix = 0; ix < count; ix += 1) lists_strs_append (names, plugins[decoder_list[ix]].name); if (have_tremor) { ix = lists_strs_find (names, "vorbis"); if (ix < lists_strs_size (names)) lists_strs_replace (names, ix, "vorbis(tremor)"); } result = lists_strs_fmt (names, " %s"); lists_strs_free (names); return result; }
/* Initialize logging stream */ void log_init_stream (FILE *f, const char *fn) { logfp = f; if (logging_state == BUFFERING) { if (logfp) { int ix; for (ix = 0; ix < lists_strs_size (buffered_log); ix += 1) fprintf (logfp, "%s", lists_strs_at (buffered_log, ix)); fflush (logfp); } lists_strs_free (buffered_log); buffered_log = NULL; } logging_state = LOGGING; logit ("Writing log to: %s", fn); if (log_records_spilt > 0) logit ("%d log records spilt", log_records_spilt); }
static void on_song_change () { static char *last_file = NULL; static lists_t_strs *on_song_change = NULL; int ix; bool same_file, unpaused; char *curr_file, **args; struct file_tags *curr_tags; lists_t_strs *arg_list; /* We only need to do OnSongChange tokenisation once. */ if (on_song_change == NULL) { char *command; on_song_change = lists_strs_new (4); command = options_get_str ("OnSongChange"); if (command) lists_strs_tokenise (on_song_change, command); } if (lists_strs_empty (on_song_change)) return; curr_file = audio_get_sname (); if (curr_file == NULL) return; same_file = (last_file && !strcmp (last_file, curr_file)); unpaused = (audio_get_prev_state () == STATE_PAUSE); if (same_file && (unpaused || !options_get_bool ("RepeatSongChange"))) { free (curr_file); return; } curr_tags = tags_cache_get_immediate (tags_cache, curr_file, TAGS_COMMENTS | TAGS_TIME); arg_list = lists_strs_new (lists_strs_size (on_song_change)); for (ix = 0; ix < lists_strs_size (on_song_change); ix += 1) { char *arg, *str; arg = lists_strs_at (on_song_change, ix); if (arg[0] != '%') lists_strs_append (arg_list, arg); else if (!curr_tags) lists_strs_append (arg_list, ""); else { switch (arg[1]) { case 'a': str = curr_tags->artist ? curr_tags->artist : ""; lists_strs_append (arg_list, str); break; case 'r': str = curr_tags->album ? curr_tags->album : ""; lists_strs_append (arg_list, str); break; case 't': str = curr_tags->title ? curr_tags->title : ""; lists_strs_append (arg_list, str); break; case 'n': if (curr_tags->track >= 0) { str = (char *) xmalloc (sizeof (char) * 4); snprintf (str, 4, "%d", curr_tags->track); lists_strs_push (arg_list, str); } else lists_strs_append (arg_list, ""); break; case 'f': lists_strs_append (arg_list, curr_file); break; case 'D': if (curr_tags->time >= 0) { str = (char *) xmalloc (sizeof (char) * 10); snprintf (str, 10, "%d", curr_tags->time); lists_strs_push (arg_list, str); } else lists_strs_append (arg_list, ""); break; case 'd': if (curr_tags->time >= 0) { str = (char *) xmalloc (sizeof (char) * 12); sec_to_min (str, curr_tags->time); lists_strs_push (arg_list, str); } else lists_strs_append (arg_list, ""); break; default: lists_strs_append (arg_list, arg); } } } tags_free (curr_tags); #ifndef NDEBUG { char *cmd; cmd = lists_strs_fmt (arg_list, " %s"); debug ("Running command: %s", cmd); free (cmd); } #endif switch (fork ()) { case 0: args = lists_strs_save (arg_list); execve (args[0], args, environ); exit (EXIT_FAILURE); case -1: log_errno ("Failed to fork()", errno); } lists_strs_free (arg_list); free (last_file); last_file = curr_file; }
strncpy (result, line, max); len = max; } strcpy (&result[len], "\n"); return result; } /* Centre all the lines in the lyrics. */ static lists_t_strs *centre_style (lists_t_strs *lines, int height ATTR_UNUSED, int width, void *data ATTR_UNUSED) { lists_t_strs *result; int ix, size; size = lists_strs_size (lines); result = lists_strs_new (size); for (ix = 0; ix < size; ix += 1) { char *old_line, *new_line; old_line = lists_strs_at (lines, ix); new_line = centre_line (old_line, width); lists_strs_push (result, new_line); } return result; } /* Formatting function information. */ static lyrics_t_formatter *lyrics_formatter = centre_style; static lyrics_t_reaper *formatter_reaper = NULL;