/* Return a list of lyrics lines loaded from a file, or NULL on error. */ lists_t_strs *lyrics_load_file (const char *filename) { FILE *lyrics_file = NULL; const char *mime; char *line; lists_t_strs *result; assert (filename); lyrics_message = "[No lyrics file!]"; if (!file_exists (filename)) return NULL; mime = file_mime_type (filename); if (mime && strncmp (mime, "text/plain", 10)) return NULL; lyrics_file = fopen (filename, "r"); if (lyrics_file == NULL) { lyrics_message = "[Lyrics file cannot be read!]"; logit ("Error reading '%s': %s", filename, strerror (errno)); return NULL; } result = lists_strs_new (0); while ((line = read_line (lyrics_file)) != NULL) lists_strs_push (result, line); fclose (lyrics_file); lyrics_message = NULL; return result; }
/* 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); }
/* Log the command line which launched MOC. */ static void log_command_line (int argc, char *argv[]) { lists_t_strs *cmdline; char *str; assert (argc >= 0); assert (argv != NULL); assert (argv[argc] == NULL); cmdline = lists_strs_new (argc); if (lists_strs_load (cmdline, argv) > 0) str = lists_strs_fmt (cmdline, "%s "); else str = xstrdup ("No command line available"); logit ("%s", str); free (str); lists_strs_free (cmdline); }
/* Read and return a line from 'stream' in a dynamically allocated buffer. * Return NULL at end of file. */ char *xgetline (FILE *stream) { static char buffer[64]; char *result; lists_t_strs *line; line = lists_strs_new (4); do { if (!fgets (buffer, sizeof (buffer), stream)) break; lists_strs_append (line, buffer); } while (buffer[strlen (buffer) - 1] != '\n'); result = lists_strs_cat (line); lists_strs_free (line); return result; }
static void load_extn_list () { const int counts[] = {SFC_GET_SIMPLE_FORMAT_COUNT, SFC_GET_FORMAT_MAJOR_COUNT}; const int formats[] = {SFC_GET_SIMPLE_FORMAT, SFC_GET_FORMAT_MAJOR}; supported_extns = lists_strs_new (16); for (size_t ix = 0; ix < ARRAY_SIZE(counts); ix += 1) { int limit; SF_FORMAT_INFO format_info; sf_command (NULL, counts[ix], &limit, sizeof (limit)); for (int iy = 0 ; iy < limit ; iy += 1) { format_info.format = iy ; sf_command (NULL, formats[ix], &format_info, sizeof (format_info)); if (!lists_strs_exists (supported_extns, format_info.extension)) lists_strs_append (supported_extns, format_info.extension); } } /* These are synonyms of supported extensions. */ if (lists_strs_exists (supported_extns, "aiff")) lists_strs_append (supported_extns, "aif"); if (lists_strs_exists (supported_extns, "au")) lists_strs_append (supported_extns, "snd"); if (lists_strs_exists (supported_extns, "wav")) { lists_strs_append (supported_extns, "nist"); lists_strs_append (supported_extns, "sph"); } if (lists_strs_exists (supported_extns, "iff")) lists_strs_append (supported_extns, "svx"); if (lists_strs_exists (supported_extns, "oga")) lists_strs_append (supported_extns, "ogg"); if (lists_strs_exists (supported_extns, "sf")) lists_strs_append (supported_extns, "ircam"); if (lists_strs_exists (supported_extns, "mat")) { lists_strs_append (supported_extns, "mat4"); lists_strs_append (supported_extns, "mat5"); } }
/* Add a new preference for an audio format. */ static void load_each_preference (const char *preference) { const char *prefix; lists_t_strs *tokens; decoder_t_preference *pref; assert (preference && preference[0]); tokens = lists_strs_new (4); lists_strs_split (tokens, preference, "(,)"); prefix = lists_strs_at (tokens, 0); pref = make_preference (prefix); #ifdef DEBUG pref->source = preference; #endif load_decoders (pref, tokens); pref->next = preferences; preferences = pref; lists_strs_free (tokens); }
/* 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; }
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; }
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; static void *formatter_data = NULL;
int main (int argc, char *argv[]) { struct parameters params; lists_t_strs *deferred_overrides; lists_t_strs *args; #ifdef HAVE_UNAME_SYSCALL int rc; struct utsname uts; #endif #ifdef PACKAGE_REVISION logit ("This is Music On Console (revision %s)", PACKAGE_REVISION); #else logit ("This is Music On Console (version %s)", PACKAGE_VERSION); #endif #ifdef CONFIGURATION logit ("Configured:%s", CONFIGURATION); #endif #ifdef HAVE_UNAME_SYSCALL rc = uname (&uts); if (rc == 0) logit ("Running on: %s %s %s", uts.sysname, uts.release, uts.machine); #endif log_command_line (argc, argv); files_init (); if (get_home () == NULL) fatal ("Could not determine user's home directory!"); memset (¶ms, 0, sizeof(params)); options_init (); deferred_overrides = lists_strs_new (4); /* set locale according to the environment variables */ if (!setlocale(LC_ALL, "")) logit ("Could not set locale!"); args = process_command_line (argc, argv, ¶ms, deferred_overrides); if (params.dont_run_iface && params.only_server) fatal ("-c, -a and -p options can't be used with --server!"); if (!params.config_file) params.config_file = xstrdup (create_file_name ("config")); options_parse (params.config_file); if (params.config_file) free (params.config_file); params.config_file = NULL; process_deferred_overrides (deferred_overrides); lists_strs_free (deferred_overrides); deferred_overrides = NULL; check_moc_dir (); io_init (); rcc_init (); decoder_init (params.debug); srand (time(NULL)); if (!params.only_server && params.dont_run_iface) server_command (¶ms, args); else start_moc (¶ms, args); lists_strs_free (args); options_free (); decoder_cleanup (); io_cleanup (); rcc_cleanup (); files_cleanup (); compat_cleanup (); exit (EXIT_SUCCESS); }
/* Process the command line options and arguments. */ static lists_t_strs *process_command_line (int argc, char *argv[], struct parameters *params, lists_t_strs *deferred) { int ret, opt_index = 0; const char *jump_type; lists_t_strs *result; struct option long_options[] = { { "version", 0, NULL, 'V' }, { "help", 0, NULL, 'h' }, #ifndef NDEBUG { "debug", 0, NULL, 'D' }, #endif { "server", 0, NULL, 'S' }, { "foreground", 0, NULL, 'F' }, { "sound-driver", 1, NULL, 'R' }, { "music-dir", 0, NULL, 'm' }, { "append", 0, NULL, 'a' }, { "enqueue", 0, NULL, 'q' }, { "clear", 0, NULL, 'c' }, { "play", 0, NULL, 'p' }, { "playit", 0, NULL, 'l' }, { "stop", 0, NULL, 's' }, { "next", 0, NULL, 'f' }, { "previous", 0, NULL, 'r' }, { "exit", 0, NULL, 'x' }, { "theme", 1, NULL, 'T' }, { "config", 1, NULL, 'C' }, { "set-option", 1, NULL, 'O' }, { "moc-dir", 1, NULL, 'M' }, { "pause", 0, NULL, 'P' }, { "unpause", 0, NULL, 'U' }, { "toggle-pause", 0, NULL, 'G' }, { "sync", 0, NULL, 'y' }, { "nosync", 0, NULL, 'n' }, { "ascii", 0, NULL, 'A' }, { "info", 0, NULL, 'i' }, { "recursively", 0, NULL, 'e' }, { "seek", 1, NULL, 'k' }, { "jump", 1, NULL, 'j' }, { "format", 1, NULL, 'Q' }, { "volume", 1, NULL, 'v' }, { "toggle", 1, NULL, 't' }, { "on", 1, NULL, 'o' }, { "off", 1, NULL, 'u' }, { "playnum", 1, NULL, 'N' }, { 0, 0, 0, 0 } }; assert (argc >= 0); assert (argv != NULL); assert (argv[argc] == NULL); assert (params != NULL); assert (deferred != NULL); while ((ret = getopt_long(argc, argv, "VhDSFR:macpsxT:C:O:M:PUynArfiGelk:j:v:t:o:u:Q:qN:", long_options, &opt_index)) != -1) { switch (ret) { case 'V': show_version (); exit (EXIT_SUCCESS); case 'h': show_usage (argv[0]); exit (EXIT_SUCCESS); #ifndef NDEBUG case 'D': params->debug = 1; break; #endif case 'S': params->only_server = 1; break; case 'F': params->foreground = 1; params->only_server = 1; break; case 'R': if (!options_check_list ("SoundDriver", optarg)) fatal ("No such sound driver: %s", optarg); options_set_list ("SoundDriver", optarg, false); options_ignore_config ("SoundDriver"); break; case 'm': options_set_int ("StartInMusicDir", 1); options_ignore_config ("StartInMusicDir"); break; case 'a': case 'e': params->append = 1; params->dont_run_iface = 1; break; case 'q': params->enqueue = 1; params->dont_run_iface = 1; break; case 'c': params->clear = 1; params->dont_run_iface = 1; break; case 'i': params->get_file_info = 1; params->dont_run_iface = 1; break; case 'p': params->play = 1; params->dont_run_iface = 1; break; case 'l': params->playit = 1; params->dont_run_iface = 1; break; case 's': params->stop = 1; params->dont_run_iface = 1; break; case 'f': params->next = 1; params->dont_run_iface = 1; break; case 'r': params->previous = 1; params->dont_run_iface = 1; break; case 'x': params->exit = 1; params->dont_run_iface = 1; break; case 'P': params->pause = 1; params->dont_run_iface = 1; break; case 'U': params->unpause = 1; params->dont_run_iface = 1; break; case 'T': options_set_str ("ForceTheme", optarg); break; case 'C': params->config_file = xstrdup (optarg); break; case 'O': override_config_option (optarg, deferred); break; case 'M': options_set_str ("MOCDir", optarg); options_ignore_config ("MOCDir"); break; case 'y': options_set_int ("SyncPlaylist", 1); options_ignore_config ("SyncPlaylist"); break; case 'n': options_set_int ("SyncPlaylist", 0); options_ignore_config ("SyncPlaylist"); break; case 'A': options_set_int ("ASCIILines", 1); options_ignore_config ("ASCIILines"); break; case 'G': params->toggle_pause = 1; params->dont_run_iface = 1; break; case 'k': params->seek_by = get_num_param (optarg, NULL); params->dont_run_iface = 1; break; case 'j': params->jump_to = get_num_param (optarg, &jump_type); if (*jump_type) if (!jump_type[1]) if (*jump_type == '%' || tolower (*jump_type) == 's') { params->jump_type = tolower (*jump_type); params->dont_run_iface = 1; break; } //TODO: Add message explaining the error show_usage (argv[0]); exit (EXIT_FAILURE); case 'v' : params->adj_volume = optarg; params->dont_run_iface = 1; break; case 't' : params->toggle = optarg; params->dont_run_iface = 1; break; case 'o' : params->on = optarg; params->dont_run_iface = 1; break; case 'u' : params->off = optarg; params->dont_run_iface = 1; break; case 'Q': params->formatted_into_param = optarg; params->get_formatted_info = 1; params->dont_run_iface = 1; break; case 'N' : params->play_num = optarg; params->dont_run_iface = 1; break; default: show_usage (argv[0]); exit (EXIT_FAILURE); } } result = lists_strs_new (argc - optind); lists_strs_load (result, argv + optind); return result; }