/* write statefile */ void BarSettingsWrite (PianoStation_t *station, BarSettings_t *settings) { char path[PATH_MAX]; FILE *fd; assert (settings != NULL); BarGetXdgConfigDir (PACKAGE "/state", path, sizeof (path)); if ((fd = fopen (path, "w")) == NULL) { return; } fputs ("# do not edit this file\n", fd); fprintf (fd, "volume = %i\n", settings->volume); if (station != NULL) { fprintf (fd, "autostart_station = %s\n", station->id); } fclose (fd); }
/* write statefile */ void BarSettingsWrite (PianoStation_t *station, BarSettings_t *settings) { FILE *fd; assert (settings != NULL); char * const path = BarGetXdgConfigDir (PACKAGE "/state"); assert (path != NULL); if ((fd = fopen (path, "w")) == NULL) { free (path); return; } fputs ("# do not edit this file\n", fd); fprintf (fd, "volume = %i\n", settings->volume); if (station != NULL) { fprintf (fd, "autostart_station = %s\n", station->id); } fclose (fd); free (path); }
/* read app settings from file; format is: key = value\n * @param where to save these settings * @return nothing yet */ void BarSettingsRead (BarSettings_t *settings) { char configfile[MAXPATHLEN], key[256], val[256]; FILE *configfd = NULL; static const char *formatMsgPrefix = "format_msg_"; assert (sizeof (settings->keys) / sizeof (*settings->keys) == sizeof (dispatchActions) / sizeof (*dispatchActions)); /* apply defaults */ settings->audioQuality = PIANO_AQ_HIGH; settings->autoselect = true; settings->history = 5; settings->volume = 0; settings->sortOrder = BAR_SORT_NAME_AZ; settings->loveIcon = strdup (" <3"); settings->banIcon = strdup (" </3"); settings->atIcon = strdup (" @ "); settings->npSongFormat = strdup ("\"%t\" by \"%a\" on \"%l\"%r%@%s"); settings->npStationFormat = strdup ("Station \"%n\" (%i)"); settings->listSongFormat = strdup ("%i) %a - %t%r"); settings->rpcHost = strdup (PIANO_RPC_HOST); settings->partnerUser = strdup ("android"); settings->partnerPassword = strdup ("AC7IBG09A3DTSYM4R41UJWL07VLN8JI7"); settings->device = strdup ("android-generic"); settings->inkey = strdup ("R=U!LH$O2B#"); settings->outkey = strdup ("6#26FRL$ZWD"); settings->fifo = malloc (PATH_MAX * sizeof (*settings->fifo)); BarGetXdgConfigDir (PACKAGE "/ctl", settings->fifo, PATH_MAX); memcpy (settings->tlsFingerprint, "\xA2\xA0\xBE\x8A\x37\x92\x39\xAE" "\x2B\x2E\x71\x4C\x56\xB3\x8B\xC1\x2A\x9B\x4B\x77", sizeof (settings->tlsFingerprint)); settings->msgFormat[MSG_NONE].prefix = NULL; settings->msgFormat[MSG_NONE].postfix = NULL; settings->msgFormat[MSG_INFO].prefix = strdup ("(i) "); settings->msgFormat[MSG_INFO].postfix = NULL; settings->msgFormat[MSG_PLAYING].prefix = strdup ("|> "); settings->msgFormat[MSG_PLAYING].postfix = NULL; settings->msgFormat[MSG_TIME].prefix = strdup ("# "); settings->msgFormat[MSG_TIME].postfix = NULL; settings->msgFormat[MSG_ERR].prefix = strdup ("/!\\ "); settings->msgFormat[MSG_ERR].postfix = NULL; settings->msgFormat[MSG_QUESTION].prefix = strdup ("[?] "); settings->msgFormat[MSG_QUESTION].postfix = NULL; settings->msgFormat[MSG_LIST].prefix = strdup ("\t"); settings->msgFormat[MSG_LIST].postfix = NULL; settings->useSpaces = false; settings->embedCover = true; for (size_t i = 0; i < BAR_KS_COUNT; i++) { settings->keys[i] = dispatchActions[i].defaultKey; } BarGetXdgConfigDir (PACKAGE "/config", configfile, sizeof (configfile)); /* read config file */ if ((configfd = fopen (configfile, "r")) != NULL) { while (1) { char lwhite, rwhite; int scanRet = fscanf (configfd, "%255s%c=%c%255[^\n]", key, &lwhite, &rwhite, val); if (scanRet == EOF) { break; } else if (scanRet != 4 || lwhite != ' ' || rwhite != ' ') { /* invalid config line */ continue; } if (streq ("control_proxy", key)) { settings->controlProxy = strdup (val); } else if (streq ("proxy", key)) { settings->proxy = strdup (val); } else if (streq ("user", key)) { settings->username = strdup (val); } else if (streq ("password", key)) { settings->password = strdup (val); } else if (streq ("rpc_host", key)) { free (settings->rpcHost); settings->rpcHost = strdup (val); } else if (streq ("partner_user", key)) { free (settings->partnerUser); settings->partnerUser = strdup (val); } else if (streq ("partner_password", key)) { free (settings->partnerPassword); settings->partnerPassword = strdup (val); } else if (streq ("device", key)) { free (settings->device); settings->device = strdup (val); } else if (streq ("encrypt_password", key)) { free (settings->outkey); settings->outkey = strdup (val); } else if (streq ("decrypt_password", key)) { free (settings->inkey); settings->inkey = strdup (val); } else if (memcmp ("act_", key, 4) == 0) { size_t i; /* keyboard shortcuts */ for (i = 0; i < BAR_KS_COUNT; i++) { if (streq (dispatchActions[i].configKey, key)) { if (streq (val, "disabled")) { settings->keys[i] = BAR_KS_DISABLED; } else { settings->keys[i] = val[0]; } } } } else if (streq ("audio_format", key)) { if (streq (val, "aacplus")) { settings->audioFormat = PIANO_AF_AACPLUS; } else if (streq (val, "mp3")) { settings->audioFormat = PIANO_AF_MP3; } else if (streq (val, "mp3-hifi")) { settings->audioFormat = PIANO_AF_MP3_HI; } } else if (streq ("audio_quality", key)) { if (streq (val, "low")) { settings->audioQuality = PIANO_AQ_LOW; } else if (streq (val, "medium")) { settings->audioQuality = PIANO_AQ_MEDIUM; } else if (streq (val, "high")) { settings->audioQuality = PIANO_AQ_HIGH; } } else if (streq ("autostart_station", key)) { settings->autostartStation = strdup (val); } else if (streq ("event_command", key)) { settings->eventCmd = strdup (val); } else if (streq ("history", key)) { settings->history = atoi (val); } else if (streq ("audio_file_dir", key)) { free (settings->audioFileDir); settings->audioFileDir = strdup(val); } else if (streq ("audio_file_name", key)) { free (settings->audioFileName); settings->audioFileName = strdup(val); } else if (streq ("use_spaces", key)) { if (streq ("true", val)) { settings->useSpaces = true; } } else if (streq ("embed_cover", key)) { if (!streq ("true", val)) { settings->embedCover = false; } } else if (streq ("autostart_station", key)) { free (settings->autostartStation); settings->autostartStation = strdup (val); } else if (streq ("event_command", key)) { free (settings->eventCmd); settings->eventCmd = strdup (val); } else if (streq ("history", key)) { settings->history = atoi (val); } else if (streq ("sort", key)) { size_t i; static const char *mapping[] = {"name_az", "name_za", "quickmix_01_name_az", "quickmix_01_name_za", "quickmix_10_name_az", "quickmix_10_name_za", }; for (i = 0; i < BAR_SORT_COUNT; i++) { if (streq (mapping[i], val)) { settings->sortOrder = i; break; } } } else if (streq ("love_icon", key)) { free (settings->loveIcon); settings->loveIcon = strdup (val); } else if (streq ("ban_icon", key)) { free (settings->banIcon); settings->banIcon = strdup (val); } else if (streq ("at_icon", key)) { free (settings->atIcon); settings->atIcon = strdup (val); } else if (streq ("volume", key)) { settings->volume = atoi (val); } else if (streq ("format_nowplaying_song", key)) { free (settings->npSongFormat); settings->npSongFormat = strdup (val); } else if (streq ("format_nowplaying_station", key)) { free (settings->npStationFormat); settings->npStationFormat = strdup (val); } else if (streq ("format_list_song", key)) { free (settings->listSongFormat); settings->listSongFormat = strdup (val); } else if (streq ("fifo", key)) { free (settings->fifo); settings->fifo = strdup (val); } else if (streq ("autoselect", key)) { settings->autoselect = atoi (val); } else if (streq ("tls_fingerprint", key)) { /* expects 40 byte hex-encoded sha1 */ if (strlen (val) == 40) { for (size_t i = 0; i < 20; i++) { char hex[3]; memcpy (hex, &val[i*2], 2); hex[2] = '\0'; settings->tlsFingerprint[i] = strtol (hex, NULL, 16); } } } else if (strncmp (formatMsgPrefix, key, strlen (formatMsgPrefix)) == 0) { static const char *mapping[] = {"none", "info", "nowplaying", "time", "err", "question", "list"}; const char *typeStart = key + strlen (formatMsgPrefix); for (size_t i = 0; i < sizeof (mapping) / sizeof (*mapping); i++) { if (streq (typeStart, mapping[i])) { const char *formatPos = strstr (val, "%s"); /* keep default if there is no format character */ if (formatPos != NULL) { BarMsgFormatStr_t *format = &settings->msgFormat[i]; free (format->prefix); free (format->postfix); const size_t prefixLen = formatPos - val; format->prefix = calloc (prefixLen + 1, sizeof (*format->prefix)); memcpy (format->prefix, val, prefixLen); const size_t postfixLen = strlen (val) - (formatPos-val) - 2; format->postfix = calloc (postfixLen + 1, sizeof (*format->postfix)); memcpy (format->postfix, formatPos+2, postfixLen); } break; } } } } } /* check environment variable if proxy is not set explicitly */ if (settings->proxy == NULL) { char *tmpProxy = getenv ("http_proxy"); if (tmpProxy != NULL && strlen (tmpProxy) > 0) { settings->proxy = strdup (tmpProxy); } } /* Use a local ./mp3 directory if no audio file directory was set. */ if (settings->audioFileDir == NULL) { settings->audioFileDir = strdup(BAR_DEFAULT_AUDIO_FILE_DIR); } /* Use the default file name if none was set. */ if (settings->audioFileName == NULL) { settings->audioFileName = strdup(BAR_DEFAULT_AUDIO_FILE_NAME); } if (configfd != NULL) { fclose (configfd); } return; }
/* read app settings from file; format is: key = value\n * @param where to save these settings * @return nothing yet */ void BarSettingsRead (BarSettings_t *settings) { /* FIXME: what is the max length of a path? */ char configfile[1024], key[256], val[256]; FILE *configfd; /* _must_ have same order as in BarKeyShortcutId_t */ static const char defaultKeys[] = {'?', '+', '-', 'a', 'c', 'd', 'e', 'g', 'h', 'i', 'j', 'm', 'n', 'p', 'q', 'r', 's', 't', 'u', 'x', '$', 'b', }; static const char *shortcutFileKeys[] = { "act_help", "act_songlove", "act_songban", "act_stationaddmusic", "act_stationcreate", "act_stationdelete", "act_songexplain", "act_stationaddbygenre", "act_history", "act_songinfo", "act_addshared", "act_songmove", "act_songnext", "act_songpause", "act_quit", "act_stationrename", "act_stationchange", "act_songtired", "act_upcoming", "act_stationselectquickmix", "act_debug", "act_bookmark", }; /* apply defaults */ #ifdef ENABLE_FAAD settings->audioFormat = PIANO_AF_AACPLUS; #else #ifdef ENABLE_MAD settings->audioFormat = PIANO_AF_MP3; #endif #endif settings->history = 5; settings->sortOrder = BAR_SORT_NAME_AZ; memcpy (settings->keys, defaultKeys, sizeof (defaultKeys)); settings->loveIcon = strdup ("<3"); settings->banIcon = strdup ("</3"); BarGetXdgConfigDir (PACKAGE "/config", configfile, sizeof (configfile)); if ((configfd = fopen (configfile, "r")) == NULL) { return; } /* read config file */ while (1) { int scanRet = fscanf (configfd, "%255s = %255[^\n]", key, val); if (scanRet == EOF) { break; } else if (scanRet != 2) { /* invalid config line */ continue; } if (streq ("control_proxy", key)) { settings->controlProxy = strdup (val); } else if (streq ("proxy", key)) { settings->proxy = strdup (val); } else if (streq ("user", key)) { settings->username = strdup (val); } else if (streq ("password", key)) { settings->password = strdup (val); } else if (strcmp ("lastfm_user", key) == 0) { settings->lastfmUser = strdup (val); } else if (strcmp ("lastfm_password", key) == 0) { settings->lastfmPassword = strdup (val); } else if (strcmp ("lastfm_scrobble_percent", key) == 0) { settings->lastfmScrobblePercent = atoi (val); } else if (memcmp ("act_", key, 4) == 0) { size_t i; /* keyboard shortcuts */ for (i = 0; i < BAR_KS_COUNT; i++) { if (streq (shortcutFileKeys[i], key)) { settings->keys[i] = val[0]; break; } } } else if (streq ("audio_format", key)) { if (streq (val, "aacplus")) { settings->audioFormat = PIANO_AF_AACPLUS; } else if (streq (val, "mp3")) { settings->audioFormat = PIANO_AF_MP3; } else if (streq (val, "mp3-hifi")) { settings->audioFormat = PIANO_AF_MP3_HI; } } else if (streq ("autostart_station", key)) { settings->autostartStation = strdup (val); } else if (streq ("event_command", key)) { settings->eventCmd = strdup (val); } else if (streq ("history", key)) { settings->history = atoi (val); } else if (streq ("sort", key)) { size_t i; static const char *mapping[] = {"name_az", "name_za", "quickmix_01_name_az", "quickmix_01_name_za", "quickmix_10_name_az", "quickmix_10_name_za", }; for (i = 0; i < BAR_SORT_COUNT; i++) { if (streq (mapping[i], val)) { settings->sortOrder = i; break; } } } else if (streq ("love_icon", key)) { free (settings->loveIcon); settings->loveIcon = strdup (val); } else if (streq ("ban_icon", key)) { free (settings->banIcon); settings->banIcon = strdup (val); } } /* some checks */ /* last.fm requests tracks to be played at least 50% */ if (settings->lastfmScrobblePercent < 50 || settings->lastfmScrobblePercent > 100) { settings->lastfmScrobblePercent = 50; } /* only scrobble tracks if username and password are set */ if (settings->lastfmUser != NULL && settings->lastfmPassword != NULL) { settings->enableScrobbling = 1; } /* check environment variable if proxy is not set explicitly */ if (settings->proxy == NULL) { char *tmpProxy = getenv ("http_proxy"); if (tmpProxy != NULL && strlen (tmpProxy) > 0) { settings->proxy = strdup (tmpProxy); } } fclose (configfd); }
int main (int argc, char **argv) { static BarApp_t app; pthread_t playerThread; /* FIXME: max path length? */ char ctlPath[1024]; FILE *ctlFd = NULL; struct timeval selectTimeout; int maxFd, selectFds[2]; fd_set readSet, readSetCopy; char buf = '\0'; /* terminal attributes _before_ we started messing around with ~ECHO */ struct termios termOrig; memset (&app, 0, sizeof (app)); /* save terminal attributes, before disabling echoing */ BarTermSave (&termOrig); BarTermSetEcho (0); BarTermSetBuffer (0); /* init some things */ ao_initialize (); PianoInit (&app.ph); WaitressInit (&app.waith); strncpy (app.waith.host, PIANO_RPC_HOST, sizeof (app.waith.host)-1); strncpy (app.waith.port, PIANO_RPC_PORT, sizeof (app.waith.port)-1); BarSettingsInit (&app.settings); BarSettingsRead (&app.settings); BarUiMsg (MSG_NONE, "Welcome to " PACKAGE "! Press %c for a list of commands.\n", app.settings.keys[BAR_KS_HELP]); /* init fds */ FD_ZERO(&readSet); selectFds[0] = fileno (stdin); FD_SET(selectFds[0], &readSet); maxFd = selectFds[0] + 1; BarGetXdgConfigDir (PACKAGE "/ctl", ctlPath, sizeof (ctlPath)); /* FIXME: why is r_+_ required? */ ctlFd = fopen (ctlPath, "r+"); if (ctlFd != NULL) { selectFds[1] = fileno (ctlFd); FD_SET(selectFds[1], &readSet); /* assuming ctlFd is always > stdin */ maxFd = selectFds[1] + 1; BarUiMsg (MSG_INFO, "Control fifo at %s opened\n", ctlPath); } if (app.settings.username == NULL) { char nameBuf[100]; BarUiMsg (MSG_QUESTION, "Username: "******"Password: "******"Login... "); if (!BarUiPianoCall (&app.ph, PIANO_REQUEST_LOGIN, &app.waith, &reqData, &pRet, &wRet)) { BarTermRestore (&termOrig); return 0; } } { PianoReturn_t pRet; WaitressReturn_t wRet; BarUiMsg (MSG_INFO, "Get stations... "); if (!BarUiPianoCall (&app.ph, PIANO_REQUEST_GET_STATIONS, &app.waith, NULL, &pRet, &wRet)) { BarTermRestore (&termOrig); return 0; } } /* try to get autostart station */ if (app.settings.autostartStation != NULL) { app.curStation = PianoFindStationById (app.ph.stations, app.settings.autostartStation); if (app.curStation == NULL) { BarUiMsg (MSG_ERR, "Error: Autostart station not found.\n"); } } /* no autostart? ask the user */ if (app.curStation == NULL) { app.curStation = BarUiSelectStation (&app.ph, "Select station: ", app.settings.sortOrder, stdin); } if (app.curStation != NULL) { BarUiPrintStation (app.curStation); } /* little hack, needed to signal: hey! we need a playlist, but don't * free anything (there is nothing to be freed yet) */ memset (&app.player, 0, sizeof (app.player)); while (!app.doQuit) { /* song finished playing, clean up things/scrobble song */ if (app.player.mode == PLAYER_FINISHED_PLAYBACK) { BarUiStartEventCmd (&app.settings, "songfinish", app.curStation, app.playlist, &app.player, PIANO_RET_OK, WAITRESS_RET_OK); /* FIXME: pthread_join blocks everything if network connection * is hung up e.g. */ void *threadRet; pthread_join (playerThread, &threadRet); /* don't continue playback if thread reports error */ if (threadRet != (void *) PLAYER_RET_OK) { app.curStation = NULL; } memset (&app.player, 0, sizeof (app.player)); } /* check whether player finished playing and start playing new * song */ if (app.player.mode >= PLAYER_FINISHED_PLAYBACK || app.player.mode == PLAYER_FREED) { if (app.curStation != NULL) { /* what's next? */ if (app.playlist != NULL) { if (app.settings.history != 0) { /* prepend song to history list */ PianoSong_t *tmpSong = app.songHistory; app.songHistory = app.playlist; /* select next song */ app.playlist = app.playlist->next; app.songHistory->next = tmpSong; /* limit history's length */ /* start with 1, so we're stopping at n-1 and have the * chance to set ->next = NULL */ unsigned int i = 1; tmpSong = app.songHistory; while (i < app.settings.history && tmpSong != NULL) { tmpSong = tmpSong->next; ++i; } /* if too many songs in history... */ if (tmpSong != NULL) { PianoSong_t *delSong = tmpSong->next; tmpSong->next = NULL; if (delSong != NULL) { PianoDestroyPlaylist (delSong); } } } else { /* don't keep history */ app.playlist = app.playlist->next; } } if (app.playlist == NULL) { PianoReturn_t pRet; WaitressReturn_t wRet; PianoRequestDataGetPlaylist_t reqData; reqData.station = app.curStation; reqData.format = app.settings.audioFormat; BarUiMsg (MSG_INFO, "Receiving new playlist... "); if (!BarUiPianoCall (&app.ph, PIANO_REQUEST_GET_PLAYLIST, &app.waith, &reqData, &pRet, &wRet)) { app.curStation = NULL; } else { app.playlist = reqData.retPlaylist; if (app.playlist == NULL) { BarUiMsg (MSG_INFO, "No tracks left.\n"); app.curStation = NULL; } } BarUiStartEventCmd (&app.settings, "stationfetchplaylist", app.curStation, app.playlist, &app.player, pRet, wRet); } /* song ready to play */ if (app.playlist != NULL) { BarUiPrintSong (app.playlist, app.curStation->isQuickMix ? PianoFindStationById (app.ph.stations, app.playlist->stationId) : NULL); if (app.playlist->audioUrl == NULL) { BarUiMsg (MSG_ERR, "Invalid song url.\n"); } else { /* setup player */ memset (&app.player, 0, sizeof (app.player)); WaitressInit (&app.player.waith); WaitressSetUrl (&app.player.waith, app.playlist->audioUrl); /* set up global proxy, player is NULLed on songfinish */ if (app.settings.proxy != NULL) { char tmpPath[2]; WaitressSplitUrl (app.settings.proxy, app.player.waith.proxyHost, sizeof (app.player.waith.proxyHost), app.player.waith.proxyPort, sizeof (app.player.waith.proxyPort), tmpPath, sizeof (tmpPath)); } app.player.gain = app.playlist->fileGain; app.player.audioFormat = app.playlist->audioFormat; /* throw event */ BarUiStartEventCmd (&app.settings, "songstart", app.curStation, app.playlist, &app.player, PIANO_RET_OK, WAITRESS_RET_OK); /* prevent race condition, mode must _not_ be FREED if * thread has been started */ app.player.mode = PLAYER_STARTING; /* start player */ pthread_create (&playerThread, NULL, BarPlayerThread, &app.player); } /* end if audioUrl == NULL */ } /* end if playlist != NULL */ } /* end if curStation != NULL */ } /* select modifies its arguments => copy the set */ memcpy (&readSetCopy, &readSet, sizeof (readSet)); selectTimeout.tv_sec = 1; selectTimeout.tv_usec = 0; /* in the meantime: wait for user actions */ if (select (maxFd, &readSetCopy, NULL, NULL, &selectTimeout) > 0) { FILE *curFd = NULL; if (FD_ISSET(selectFds[0], &readSetCopy)) { curFd = stdin; } else if (FD_ISSET(selectFds[1], &readSetCopy)) { curFd = ctlFd; } buf = fgetc (curFd); size_t i; for (i = 0; i < BAR_KS_COUNT; i++) { if (app.settings.keys[i] == buf) { static const BarKeyShortcutFunc_t idToF[] = {BarUiActHelp, BarUiActLoveSong, BarUiActBanSong, BarUiActAddMusic, BarUiActCreateStation, BarUiActDeleteStation, BarUiActExplain, BarUiActStationFromGenre, BarUiActHistory, BarUiActSongInfo, BarUiActAddSharedStation, BarUiActMoveSong, BarUiActSkipSong, BarUiActPause, BarUiActQuit, BarUiActRenameStation, BarUiActSelectStation, BarUiActTempBanSong, BarUiActPrintUpcoming, BarUiActSelectQuickMix, BarUiActDebug, BarUiActBookmark}; idToF[i] (&app, curFd); break; } } } /* show time */ if (app.player.mode >= PLAYER_SAMPLESIZE_INITIALIZED && app.player.mode < PLAYER_FINISHED_PLAYBACK) { /* Ugly: songDuration is unsigned _long_ int! Lets hope this won't * overflow */ int songRemaining = (signed long int) (app.player.songDuration - app.player.songPlayed) / BAR_PLAYER_MS_TO_S_FACTOR; char pos = 0; if (songRemaining < 0) { /* Use plus sign if song is longer than expected */ pos = 1; songRemaining = -songRemaining; } BarUiMsg (MSG_TIME, "%c%02i:%02i/%02i:%02i\r", (pos ? '+' : '-'), songRemaining / 60, songRemaining % 60, app.player.songDuration / BAR_PLAYER_MS_TO_S_FACTOR / 60, app.player.songDuration / BAR_PLAYER_MS_TO_S_FACTOR % 60); } } /* destroy everything (including the world...) */ if (app.player.mode != PLAYER_FREED) { pthread_join (playerThread, NULL); } if (ctlFd != NULL) { fclose (ctlFd); } PianoDestroy (&app.ph); PianoDestroyPlaylist (app.songHistory); PianoDestroyPlaylist (app.playlist); ao_shutdown(); BarSettingsDestroy (&app.settings); /* restore terminal attributes, zsh doesn't need this, bash does... */ BarTermRestore (&termOrig); return 0; }
/* read app settings from file; format is: key = value\n * @param where to save these settings * @return nothing yet */ void BarSettingsRead (BarSettings_t *settings) { char * const configfiles[] = { PACKAGE_STATE, PACKAGE_CONFIG }; char * const userhome = BarSettingsGetHome (); assert (userhome != NULL); /* set xdg config path (if not set) */ char * const defaultxdg = malloc (strlen ("XDG_CONFIG_HOME=") + strlen (userhome) + 1); sprintf (defaultxdg, "XDG_CONFIG_HOME=%s", userhome); _putenv (defaultxdg); free (defaultxdg); assert (sizeof (settings->keys) / sizeof (*settings->keys) == sizeof (dispatchActions) / sizeof (*dispatchActions)); /* apply defaults */ settings->audioQuality = PIANO_AQ_HIGH; settings->autoselect = true; settings->history = 5; settings->volume = 0; settings->timeout = 30; /* seconds */ settings->gainMul = 1.0; /* should be > 4, otherwise expired audio urls (403) can stop playback */ settings->maxRetry = 5; settings->sortOrder = BAR_SORT_NAME_AZ; settings->loveIcon = strdup (" <3"); settings->banIcon = strdup (" </3"); settings->tiredIcon = strdup (" zZ"); settings->atIcon = strdup (" @ "); settings->npSongFormat = strdup ("\"%t\" by \"%a\" on \"%l\"%r%@%s"); settings->npStationFormat = strdup ("Station \"%n\" (%i)"); settings->listSongFormat = strdup ("%i) %a - %t%r"); settings->titleFormat = strdup (TITLE " - \"%t\" by \"%a\" on \"%l\"%r%@%s"); settings->player = NULL; settings->rpcHost = strdup (PIANO_RPC_HOST); settings->rpcTlsPort = strdup ("443"); settings->partnerUser = strdup ("android"); settings->partnerPassword = strdup ("AC7IBG09A3DTSYM4R41UJWL07VLN8JI7"); settings->device = strdup ("android-generic"); settings->inkey = strdup ("R=U!LH$O2B#"); settings->outkey = strdup ("6#26FRL$ZWD"); settings->fifo = BarGetXdgConfigDir (PACKAGE_PIPE); assert (settings->fifo != NULL); settings->msgFormat[MSG_NONE].prefix = NULL; settings->msgFormat[MSG_NONE].postfix = NULL; settings->msgFormat[MSG_INFO].prefix = strdup ("(i) "); settings->msgFormat[MSG_INFO].postfix = NULL; settings->msgFormat[MSG_PLAYING].prefix = strdup ("|> "); settings->msgFormat[MSG_PLAYING].postfix = NULL; settings->msgFormat[MSG_TIME].prefix = strdup ("# "); settings->msgFormat[MSG_TIME].postfix = NULL; settings->msgFormat[MSG_ERR].prefix = strdup ("/!\\ "); settings->msgFormat[MSG_ERR].postfix = NULL; settings->msgFormat[MSG_QUESTION].prefix = strdup ("[?] "); settings->msgFormat[MSG_QUESTION].postfix = NULL; settings->msgFormat[MSG_LIST].prefix = strdup ("\t"); settings->msgFormat[MSG_LIST].postfix = NULL; settings->msgFormat[MSG_DEBUG].prefix = NULL; settings->msgFormat[MSG_DEBUG].postfix = NULL; for (size_t i = 0; i < BAR_KS_COUNT; i++) { settings->keys[i] = dispatchActions[i].defaultKey; } /* read config files */ for (size_t j = 0; j < sizeof (configfiles) / sizeof (*configfiles); j++) { static const char *formatMsgPrefix = "format_msg_"; FILE *configfd; char line[512]; size_t lineNum = 0; char * const path = BarGetXdgConfigDir (configfiles[j]); assert (path != NULL); if ((configfd = fopen (path, "r")) == NULL) { free (path); continue; } while (1) { ++lineNum; char * const ret = fgets (line, sizeof (line), configfd); if (ret == NULL) { /* EOF or error */ break; } if (strchr (line, '\n') == NULL && !feof (configfd)) { BarUiMsg (settings, MSG_INFO, "Line %s:%zu too long, " "ignoring\n", path, lineNum); continue; } /* parse lines that match "^\s*(.*?)\s?=\s?(.*)$". Windows and Unix * line terminators are supported. */ char *key = line; /* skip leading spaces */ while (isspace ((unsigned char) key[0])) { ++key; } /* skip comments */ if (key[0] == '#') { continue; } /* search for delimiter and split key-value pair */ char *val = strchr (line, '='); if (val == NULL) { /* no warning for empty lines */ if (key[0] != '\0') { BarUiMsg (settings, MSG_INFO, "Invalid line at %s:%zu\n", path, lineNum); } /* invalid line */ continue; } *val = '\0'; ++val; /* drop spaces at the end */ char *keyend = &key[strlen (key)-1]; while (keyend >= key && isspace ((unsigned char) *keyend)) { *keyend = '\0'; --keyend; } /* strip at most one space, legacy cruft, required for values with * leading spaces like love_icon */ if (isspace ((unsigned char) val[0])) { ++val; } /* drop trailing cr/lf */ char *valend = &val[strlen (val)-1]; while (valend >= val && (*valend == '\r' || *valend == '\n')) { *valend = '\0'; --valend; } if (streq ("control_proxy", key)) { settings->controlProxy = strdup (val); } else if (streq ("proxy", key)) { settings->proxy = strdup (val); } else if (streq ("bind_to", key)) { settings->bindTo = strdup (val); } else if (streq ("user", key)) { settings->username = strdup (val); } else if (streq ("password", key)) { settings->password = strdup (val); } else if (streq ("password_command", key)) { settings->passwordCmd = strdup (val); } else if (streq ("rpc_host", key)) { free (settings->rpcHost); settings->rpcHost = strdup (val); } else if (streq ("rpc_tls_port", key)) { free (settings->rpcTlsPort); settings->rpcTlsPort = strdup (val); } else if (streq ("partner_user", key)) { free (settings->partnerUser); settings->partnerUser = strdup (val); } else if (streq ("partner_password", key)) { free (settings->partnerPassword); settings->partnerPassword = strdup (val); } else if (streq ("device", key)) { free (settings->device); settings->device = strdup (val); } else if (streq ("encrypt_password", key)) { free (settings->outkey); settings->outkey = strdup (val); } else if (streq ("decrypt_password", key)) { free (settings->inkey); settings->inkey = strdup (val); } else if (streq ("ca_bundle", key)) { free (settings->caBundle); settings->caBundle = strdup (val); } else if (memcmp ("act_", key, 4) == 0) { size_t i; /* keyboard shortcuts */ for (i = 0; i < BAR_KS_COUNT; i++) { if (streq (dispatchActions[i].configKey, key)) { if (streq (val, "disabled")) { settings->keys[i] = BAR_KS_DISABLED; } else { settings->keys[i] = val[0]; } break; } } } else if (streq ("audio_quality", key)) { if (streq (val, "low")) { settings->audioQuality = PIANO_AQ_LOW; } else if (streq (val, "medium")) { settings->audioQuality = PIANO_AQ_MEDIUM; } else if (streq (val, "high")) { settings->audioQuality = PIANO_AQ_HIGH; } } else if (streq ("autostart_station", key)) { free (settings->autostartStation); settings->autostartStation = strdup (val); } else if (streq ("event_command", key)) { settings->eventCmd = BarSettingsExpandTilde (val, userhome); } else if (streq ("history", key)) { settings->history = atoi (val); } else if (streq ("max_retry", key)) { settings->maxRetry = atoi (val); } else if (streq ("timeout", key)) { settings->timeout = atoi (val); } else if (streq ("sort", key)) { size_t i; static const char *mapping[] = {"name_az", "name_za", "quickmix_01_name_az", "quickmix_01_name_za", "quickmix_10_name_az", "quickmix_10_name_za", }; for (i = 0; i < BAR_SORT_COUNT; i++) { if (streq (mapping[i], val)) { settings->sortOrder = i; break; } } } else if (streq ("love_icon", key)) { free (settings->loveIcon); settings->loveIcon = strdup (val); } else if (streq ("ban_icon", key)) { free (settings->banIcon); settings->banIcon = strdup (val); } else if (streq ("tired_icon", key)) { free (settings->tiredIcon); settings->tiredIcon = strdup (val); } else if (streq ("at_icon", key)) { free (settings->atIcon); settings->atIcon = strdup (val); } else if (streq ("volume", key)) { settings->volume = atoi (val); } else if (streq ("gain_mul", key)) { settings->gainMul = (float)atof (val); } else if (streq ("format_nowplaying_song", key)) { free (settings->npSongFormat); settings->npSongFormat = strdup (val); } else if (streq ("format_nowplaying_station", key)) { free (settings->npStationFormat); settings->npStationFormat = strdup (val); } else if (streq ("format_list_song", key)) { free (settings->listSongFormat); settings->listSongFormat = strdup (val); } else if (streq ("format_title", key)) { free (settings->titleFormat); settings->titleFormat = strdup (val); } else if (streq ("player", key)) { free (settings->player); settings->player = strdup (val); } else if (streq ("fifo", key)) { free (settings->fifo); settings->fifo = BarSettingsExpandTilde (val, userhome); } else if (streq ("autoselect", key)) { settings->autoselect = atoi (val); } else if (strncmp (formatMsgPrefix, key, strlen (formatMsgPrefix)) == 0) { static const char *mapping[] = {"none", "info", "nowplaying", "time", "err", "question", "list", "debug"}; const char *typeStart = key + strlen (formatMsgPrefix); for (size_t i = 0; i < sizeof (mapping) / sizeof (*mapping); i++) { if (streq (typeStart, mapping[i])) { const char *formatPos = strstr (val, "%s"); /* keep default if there is no format character */ if (formatPos != NULL) { BarMsgFormatStr_t *format = &settings->msgFormat[i]; free (format->prefix); free (format->postfix); const size_t prefixLen = formatPos - val; format->prefix = calloc (prefixLen + 1, sizeof (*format->prefix)); memcpy (format->prefix, val, prefixLen); const size_t postfixLen = strlen (val) - (formatPos-val) - 2; format->postfix = calloc (postfixLen + 1, sizeof (*format->postfix)); memcpy (format->postfix, formatPos+2, postfixLen); } break; } } } } fclose (configfd); free (path); } /* check environment variable if proxy is not set explicitly */ if (settings->proxy == NULL) { char *tmpProxy = getenv ("http_proxy"); if (tmpProxy != NULL && strlen (tmpProxy) > 0) { settings->proxy = strdup (tmpProxy); } } free (userhome); }
/* read app settings from file; format is: key = value\n * @param where to save these settings * @return nothing yet */ void BarSettingsRead (BarSettings_t *settings) { char configfile[PATH_MAX], key[256], val[256]; FILE *configfd; assert (sizeof (settings->keys) / sizeof (*settings->keys) == sizeof (dispatchActions) / sizeof (*dispatchActions)); /* apply defaults */ #ifdef ENABLE_FAAD settings->audioFormat = PIANO_AF_AACPLUS; #else #ifdef ENABLE_MAD settings->audioFormat = PIANO_AF_MP3; #endif #endif settings->history = 5; settings->volume = 0; settings->sortOrder = BAR_SORT_NAME_AZ; settings->loveIcon = strdup ("<3"); settings->banIcon = strdup ("</3"); for (size_t i = 0; i < BAR_KS_COUNT; i++) { settings->keys[i] = dispatchActions[i].defaultKey; } BarGetXdgConfigDir (PACKAGE "/config", configfile, sizeof (configfile)); if ((configfd = fopen (configfile, "r")) == NULL) { return; } /* read config file */ while (1) { int scanRet = fscanf (configfd, "%255s = %255[^\n]", key, val); if (scanRet == EOF) { break; } else if (scanRet != 2) { /* invalid config line */ continue; } if (streq ("control_proxy", key)) { settings->controlProxy = strdup (val); } else if (streq ("proxy", key)) { settings->proxy = strdup (val); } else if (streq ("user", key)) { settings->username = strdup (val); } else if (streq ("password", key)) { settings->password = strdup (val); } else if (memcmp ("act_", key, 4) == 0) { size_t i; /* keyboard shortcuts */ for (i = 0; i < BAR_KS_COUNT; i++) { if (streq (dispatchActions[i].configKey, key)) { if (streq (val, "disabled")) { settings->keys[i] = BAR_KS_DISABLED; } else { settings->keys[i] = val[0]; } break; } } } else if (streq ("audio_format", key)) { if (streq (val, "aacplus")) { settings->audioFormat = PIANO_AF_AACPLUS; } else if (streq (val, "mp3")) { settings->audioFormat = PIANO_AF_MP3; } else if (streq (val, "mp3-hifi")) { settings->audioFormat = PIANO_AF_MP3_HI; } } else if (streq ("autostart_station", key)) { settings->autostartStation = strdup (val); } else if (streq ("event_command", key)) { settings->eventCmd = strdup (val); } else if (streq ("history", key)) { settings->history = atoi (val); } else if (streq ("sort", key)) { size_t i; static const char *mapping[] = {"name_az", "name_za", "quickmix_01_name_az", "quickmix_01_name_za", "quickmix_10_name_az", "quickmix_10_name_za", }; for (i = 0; i < BAR_SORT_COUNT; i++) { if (streq (mapping[i], val)) { settings->sortOrder = i; break; } } } else if (streq ("love_icon", key)) { free (settings->loveIcon); settings->loveIcon = strdup (val); } else if (streq ("ban_icon", key)) { free (settings->banIcon); settings->banIcon = strdup (val); } else if (streq ("volume", key)) { settings->volume = atoi (val); } } /* check environment variable if proxy is not set explicitly */ if (settings->proxy == NULL) { char *tmpProxy = getenv ("http_proxy"); if (tmpProxy != NULL && strlen (tmpProxy) > 0) { settings->proxy = strdup (tmpProxy); } } fclose (configfd); }
int main (int argc, char **argv) { static BarApp_t app; char ctlPath[PATH_MAX]; /* terminal attributes _before_ we started messing around with ~ECHO */ struct termios termOrig; memset (&app, 0, sizeof (app)); /* save terminal attributes, before disabling echoing */ BarTermSave (&termOrig); BarTermSetEcho (0); BarTermSetBuffer (0); /* init some things */ ao_initialize (); PianoInit (&app.ph); WaitressInit (&app.waith); app.waith.url.host = strdup (PIANO_RPC_HOST); app.waith.url.port = strdup (PIANO_RPC_PORT); BarSettingsInit (&app.settings); BarSettingsRead (&app.settings); BarUiMsg (&app.settings, MSG_NONE, "Welcome to " PACKAGE " (" VERSION ")! "); if (app.settings.keys[BAR_KS_HELP] == BAR_KS_DISABLED) { BarUiMsg (&app.settings, MSG_NONE, "\n"); } else { BarUiMsg (&app.settings, MSG_NONE, "Press %c for a list of commands.\n", app.settings.keys[BAR_KS_HELP]); } /* init fds */ FD_ZERO(&app.input.set); app.input.fds[0] = STDIN_FILENO; FD_SET(app.input.fds[0], &app.input.set); BarGetXdgConfigDir (PACKAGE "/ctl", ctlPath, sizeof (ctlPath)); /* open fifo read/write so it won't EOF if nobody writes to it */ assert (sizeof (app.input.fds) / sizeof (*app.input.fds) >= 2); app.input.fds[1] = open (ctlPath, O_RDWR); if (app.input.fds[1] != -1) { FD_SET(app.input.fds[1], &app.input.set); BarUiMsg (&app.settings, MSG_INFO, "Control fifo at %s opened\n", ctlPath); } app.input.maxfd = app.input.fds[0] > app.input.fds[1] ? app.input.fds[0] : app.input.fds[1]; ++app.input.maxfd; BarMainLoop (&app); if (app.input.fds[1] != -1) { close (app.input.fds[1]); } PianoDestroy (&app.ph); PianoDestroyPlaylist (app.songHistory); PianoDestroyPlaylist (app.playlist); ao_shutdown(); BarSettingsDestroy (&app.settings); /* restore terminal attributes, zsh doesn't need this, bash does... */ BarTermRestore (&termOrig); return 0; }
/* read app settings from file; format is: key = value\n * @param where to save these settings * @return nothing yet */ void BarSettingsRead (BarSettings_t *settings) { char configfile[PATH_MAX], key[256], val[256]; FILE *configfd = NULL; static const char *formatMsgPrefix = "format_msg_"; assert (sizeof (settings->keys) / sizeof (*settings->keys) == sizeof (dispatchActions) / sizeof (*dispatchActions)); /* apply defaults */ #ifdef ENABLE_FAAD settings->audioFormat = PIANO_AF_AACPLUS; #else #ifdef ENABLE_MAD settings->audioFormat = PIANO_AF_MP3; #endif #endif settings->history = 5; settings->volume = 0; settings->sortOrder = BAR_SORT_NAME_AZ; settings->loveIcon = strdup (" <3"); settings->banIcon = strdup (" </3"); settings->atIcon = strdup (" @ "); settings->npSongFormat = strdup ("\"%t\" by \"%a\" on \"%l\"%r%@%s"); settings->npStationFormat = strdup ("Station \"%n\" (%i)"); settings->msgFormat[MSG_NONE].prefix = NULL; settings->msgFormat[MSG_NONE].postfix = NULL; settings->msgFormat[MSG_INFO].prefix = strdup ("(i) "); settings->msgFormat[MSG_INFO].postfix = NULL; settings->msgFormat[MSG_PLAYING].prefix = strdup ("|> "); settings->msgFormat[MSG_PLAYING].postfix = NULL; settings->msgFormat[MSG_TIME].prefix = strdup ("# "); settings->msgFormat[MSG_TIME].postfix = NULL; settings->msgFormat[MSG_ERR].prefix = strdup ("/!\\ "); settings->msgFormat[MSG_ERR].postfix = NULL; settings->msgFormat[MSG_QUESTION].prefix = strdup ("[?] "); settings->msgFormat[MSG_QUESTION].postfix = NULL; settings->msgFormat[MSG_LIST].prefix = strdup ("\t"); settings->msgFormat[MSG_LIST].postfix = NULL; settings->useSpaces = false; settings->embedCover = true; for (size_t i = 0; i < BAR_KS_COUNT; i++) { settings->keys[i] = dispatchActions[i].defaultKey; } BarGetXdgConfigDir (PACKAGE "/config", configfile, sizeof (configfile)); /* read config file */ if ((configfd = fopen (configfile, "r")) != NULL) { while (1) { int scanRet = fscanf (configfd, "%255s = %255[^\n]", key, val); if (scanRet == EOF) { break; } else if (scanRet != 2) { /* invalid config line */ continue; } if (streq ("control_proxy", key)) { free (settings->controlProxy); settings->controlProxy = strdup (val); } else if (streq ("proxy", key)) { free (settings->proxy); settings->proxy = strdup (val); } else if (streq ("user", key)) { free (settings->username); settings->username = strdup (val); } else if (streq ("password", key)) { free (settings->password); settings->password = strdup (val); } else if (memcmp ("act_", key, 4) == 0) { size_t i; /* keyboard shortcuts */ for (i = 0; i < BAR_KS_COUNT; i++) { if (streq (dispatchActions[i].configKey, key)) { if (streq (val, "disabled")) { settings->keys[i] = BAR_KS_DISABLED; } else { settings->keys[i] = val[0]; } break; } } } else if (streq ("audio_format", key)) { if (streq (val, "aacplus")) { settings->audioFormat = PIANO_AF_AACPLUS; } else if (streq (val, "mp3")) { settings->audioFormat = PIANO_AF_MP3; } else if (streq (val, "mp3-hifi")) { settings->audioFormat = PIANO_AF_MP3_HI; } } else if (streq ("audio_file_dir", key)) { free (settings->audioFileDir); settings->audioFileDir = strdup(val); } else if (streq ("audio_file_name", key)) { free (settings->audioFileName); settings->audioFileName = strdup(val); } else if (streq ("use_spaces", key)) { if (streq ("true", val)) { settings->useSpaces = true; } } else if (streq ("embed_cover", key)) { if (!streq ("true", val)) { settings->embedCover = false; } } else if (streq ("autostart_station", key)) { free (settings->autostartStation); settings->autostartStation = strdup (val); } else if (streq ("event_command", key)) { free (settings->eventCmd); settings->eventCmd = strdup (val); } else if (streq ("history", key)) { settings->history = atoi (val); } else if (streq ("sort", key)) { size_t i; static const char *mapping[] = {"name_az", "name_za", "quickmix_01_name_az", "quickmix_01_name_za", "quickmix_10_name_az", "quickmix_10_name_za", }; for (i = 0; i < BAR_SORT_COUNT; i++) { if (streq (mapping[i], val)) { settings->sortOrder = i; break; } } } else if (streq ("love_icon", key)) { free (settings->loveIcon); settings->loveIcon = strdup (val); } else if (streq ("ban_icon", key)) { free (settings->banIcon); settings->banIcon = strdup (val); } else if (streq ("at_icon", key)) { free (settings->atIcon); settings->atIcon = strdup (val); } else if (streq ("volume", key)) { settings->volume = atoi (val); } else if (streq ("format_nowplaying_song", key)) { free (settings->npSongFormat); settings->npSongFormat = strdup (val); } else if (streq ("format_nowplaying_station", key)) { free (settings->npStationFormat); settings->npStationFormat = strdup (val); } else if (strncmp (formatMsgPrefix, key, strlen (formatMsgPrefix)) == 0) { static const char *mapping[] = {"none", "info", "nowplaying", "time", "err", "question", "list"}; const char *typeStart = key + strlen (formatMsgPrefix); for (size_t i = 0; i < sizeof (mapping) / sizeof (*mapping); i++) { if (streq (typeStart, mapping[i])) { const char *formatPos = strstr (val, "%s"); /* keep default if there is no format character */ if (formatPos != NULL) { BarMsgFormatStr_t *format = &settings->msgFormat[i]; free (format->prefix); free (format->postfix); const size_t prefixLen = formatPos - val; format->prefix = calloc (prefixLen + 1, sizeof (*format->prefix)); memcpy (format->prefix, val, prefixLen); const size_t postfixLen = strlen (val) - (formatPos-val) - 2; format->postfix = calloc (postfixLen + 1, sizeof (*format->postfix)); memcpy (format->postfix, formatPos+2, postfixLen); } break; } } } } } /* check environment variable if proxy is not set explicitly */ if (settings->proxy == NULL) { char *tmpProxy = getenv ("http_proxy"); if (tmpProxy != NULL && strlen (tmpProxy) > 0) { settings->proxy = strdup (tmpProxy); } } /* Use a local ./mp3 directory if no audio file directory was set. */ if (settings->audioFileDir == NULL) { settings->audioFileDir = strdup(BAR_DEFAULT_AUDIO_FILE_DIR); } /* Use the default file name if none was set. */ if (settings->audioFileName == NULL) { settings->audioFileName = strdup(BAR_DEFAULT_AUDIO_FILE_NAME); } if (configfd != NULL) { fclose (configfd); } return; }
int main (int argc, char **argv) { /* handles */ PianoHandle_t ph; static struct audioPlayer player; BarSettings_t settings; pthread_t playerThread; /* playlist; first item is current song */ PianoSong_t *playlist = NULL; PianoSong_t *songHistory = NULL; PianoStation_t *curStation = NULL; char doQuit = 0; /* FIXME: max path length? */ char ctlPath[1024]; FILE *ctlFd = NULL; struct timeval selectTimeout; int maxFd, selectFds[2]; fd_set readSet, readSetCopy; char buf = '\0'; /* terminal attributes _before_ we started messing around with ~ECHO */ struct termios termOrig; BarUiMsg (MSG_NONE, "Welcome to " PACKAGE "!\n"); /* save terminal attributes, before disabling echoing */ BarTermSave (&termOrig); BarTermSetEcho (0); BarTermSetBuffer (0); /* init some things */ ao_initialize (); PianoInit (&ph); BarSettingsInit (&settings); BarSettingsRead (&settings); /* init fds */ FD_ZERO(&readSet); selectFds[0] = fileno (stdin); FD_SET(selectFds[0], &readSet); maxFd = selectFds[0] + 1; BarGetXdgConfigDir (PACKAGE "/ctl", ctlPath, sizeof (ctlPath)); /* FIXME: why is r_+_ required? */ ctlFd = fopen (ctlPath, "r+"); if (ctlFd != NULL) { selectFds[1] = fileno (ctlFd); FD_SET(selectFds[1], &readSet); /* assuming ctlFd is always > stdin */ maxFd = selectFds[1] + 1; BarUiMsg (MSG_INFO, "Control fifo at %s opened\n", ctlPath); } if (settings.username == NULL) { char nameBuf[100]; BarUiMsg (MSG_QUESTION, "Username: "******"Password: "******"Login... "); if (BarUiPrintPianoStatus (PianoConnect (&ph, settings.username, settings.password)) != PIANO_RET_OK) { BarTermRestore (&termOrig); return 0; } BarUiMsg (MSG_INFO, "Get stations... "); if (BarUiPrintPianoStatus (PianoGetStations (&ph)) != PIANO_RET_OK) { BarTermRestore (&termOrig); return 0; } /* try to get autostart station */ if (settings.autostartStation != NULL) { curStation = PianoFindStationById (ph.stations, settings.autostartStation); if (curStation == NULL) { BarUiMsg (MSG_ERR, "Error: Autostart station not found.\n"); } } /* no autostart? ask the user */ if (curStation == NULL) { curStation = BarUiSelectStation (&ph, "Select station: ", stdin); } if (curStation != NULL) { BarUiPrintStation (curStation); } /* little hack, needed to signal: hey! we need a playlist, but don't * free anything (there is nothing to be freed yet) */ memset (&player, 0, sizeof (player)); while (!doQuit) { /* song finished playing, clean up things/scrobble song */ if (player.mode == PLAYER_FINISHED_PLAYBACK) { BarUiStartEventCmd (&settings, "songfinish", curStation, playlist, &player, PIANO_RET_OK); /* FIXME: pthread_join blocks everything if network connection * is hung up e.g. */ void *threadRet; pthread_join (playerThread, &threadRet); /* don't continue playback if thread reports error */ if (threadRet != NULL) { curStation = NULL; } memset (&player, 0, sizeof (player)); } /* check whether player finished playing and start playing new * song */ if (player.mode >= PLAYER_FINISHED_PLAYBACK || player.mode == PLAYER_FREED) { if (curStation != NULL) { /* what's next? */ if (playlist != NULL) { if (settings.history != 0) { /* prepend song to history list */ PianoSong_t *tmpSong = songHistory; songHistory = playlist; /* select next song */ playlist = playlist->next; songHistory->next = tmpSong; /* limit history's length */ /* start with 1, so we're stopping at n-1 and have the * chance to set ->next = NULL */ unsigned int i = 1; tmpSong = songHistory; while (i < settings.history && tmpSong != NULL) { tmpSong = tmpSong->next; ++i; } /* if too many songs in history... */ if (tmpSong != NULL) { PianoSong_t *delSong = tmpSong->next; tmpSong->next = NULL; if (delSong != NULL) { PianoDestroyPlaylist (delSong); } } } else { /* don't keep history */ playlist = playlist->next; } } if (playlist == NULL) { PianoReturn_t pRet = PIANO_RET_ERR; BarUiMsg (MSG_INFO, "Receiving new playlist... "); if ((pRet = BarUiPrintPianoStatus (PianoGetPlaylist (&ph, curStation->id, settings.audioFormat, &playlist))) != PIANO_RET_OK) { curStation = NULL; } else { if (playlist == NULL) { BarUiMsg (MSG_INFO, "No tracks left.\n"); curStation = NULL; } } BarUiStartEventCmd (&settings, "stationfetchplaylist", curStation, playlist, &player, pRet); } /* song ready to play */ if (playlist != NULL) { BarUiPrintSong (playlist, curStation->isQuickMix ? PianoFindStationById (ph.stations, playlist->stationId) : NULL); if (playlist->audioUrl == NULL) { BarUiMsg (MSG_ERR, "Invalid song url.\n"); } else { /* setup player */ memset (&player, 0, sizeof (player)); WaitressInit (&player.waith); WaitressSetUrl (&player.waith, playlist->audioUrl); player.gain = playlist->fileGain; player.audioFormat = playlist->audioFormat; /* Setup dump directories. */ prepare_dump_name(player.dump_filename, playlist); /* Setup dump handle. If we can read the file, * then it already exists so don't re-write. */ if (access(player.dump_filename, R_OK) != 0) { player.dump_handle = fopen(player.dump_filename, "w"); BarUiMsg(MSG_INFO, "Will dump song...\n"); } else { player.dump_handle = NULL; BarUiMsg(MSG_INFO, "Dump file found, will not dump!\n"); } /* throw event */ BarUiStartEventCmd (&settings, "songstart", curStation, playlist, &player, PIANO_RET_OK); /* start player */ pthread_create (&playerThread, NULL, BarPlayerThread, &player); } /* end if audioUrl == NULL */ } /* end if playlist != NULL */ } /* end if curStation != NULL */ } /* select modifies its arguments => copy the set */ memcpy (&readSetCopy, &readSet, sizeof (readSet)); selectTimeout.tv_sec = 1; selectTimeout.tv_usec = 0; /* in the meantime: wait for user actions */ if (select (maxFd, &readSetCopy, NULL, NULL, &selectTimeout) > 0) { FILE *curFd = NULL; if (FD_ISSET(selectFds[0], &readSetCopy)) { curFd = stdin; } else if (FD_ISSET(selectFds[1], &readSetCopy)) { curFd = ctlFd; } buf = fgetc (curFd); size_t i; for (i = 0; i < BAR_KS_COUNT; i++) { if (settings.keys[i] == buf) { BarKeyShortcutFunc_t idToF[] = {BarUiActHelp, BarUiActLoveSong, BarUiActBanSong, BarUiActAddMusic, BarUiActCreateStation, BarUiActDeleteStation, BarUiActExplain, BarUiActStationFromGenre, BarUiActHistory, BarUiActSongInfo, BarUiActAddSharedStation, BarUiActMoveSong, BarUiActSkipSong, BarUiActPause, BarUiActQuit, BarUiActRenameStation, BarUiActSelectStation, BarUiActTempBanSong, BarUiActPrintUpcoming, BarUiActSelectQuickMix, BarUiActDebug, BarUiActBookmark}; idToF[i] (&ph, &player, &settings, &playlist, &curStation, &songHistory, &doQuit, curFd); break; } } } /* show time */ if (player.mode >= PLAYER_SAMPLESIZE_INITIALIZED && player.mode < PLAYER_FINISHED_PLAYBACK) { /* Ugly: songDuration is unsigned _long_ int! Lets hope this won't * overflow */ int songRemaining = (signed long int) (player.songDuration - player.songPlayed) / BAR_PLAYER_MS_TO_S_FACTOR; char pos = 0; if (songRemaining < 0) { /* Use plus sign if song is longer than expected */ pos = 1; songRemaining = -songRemaining; } BarUiMsg (MSG_TIME, "%c%02i:%02i/%02i:%02i\r", (pos ? '+' : '-'), songRemaining / 60, songRemaining % 60, player.songDuration / BAR_PLAYER_MS_TO_S_FACTOR / 60, player.songDuration / BAR_PLAYER_MS_TO_S_FACTOR % 60); } } /* destroy everything (including the world...) */ if (player.mode != PLAYER_FREED) { pthread_join (playerThread, NULL); } if (ctlFd != NULL) { fclose (ctlFd); } PianoDestroy (&ph); PianoDestroyPlaylist (songHistory); PianoDestroyPlaylist (playlist); ao_shutdown(); BarSettingsDestroy (&settings); /* restore terminal attributes, zsh doesn't need this, bash does... */ BarTermRestore (&termOrig); return 0; }
/* read app settings from file; format is: key = value\n * @param where to save these settings * @return nothing yet */ void BarSettingsRead (BarSettings_t *settings) { char * const configfiles[] = { PACKAGE_STATE, PACKAGE_CONFIG }; char * const userhome = BarSettingsGetHome (); assert (userhome != NULL); /* set xdg config path (if not set) */ char * const defaultxdg = malloc (strlen ("XDG_CONFIG_HOME=") + strlen (userhome) + 1); sprintf (defaultxdg, "XDG_CONFIG_HOME=%s", userhome); _putenv (defaultxdg); free (defaultxdg); assert (sizeof (settings->keys) / sizeof (*settings->keys) == sizeof (dispatchActions) / sizeof (*dispatchActions)); /* apply defaults */ settings->audioQuality = PIANO_AQ_HIGH; settings->autoselect = true; settings->history = 5; settings->volume = 0; settings->maxPlayerErrors = 5; settings->sortOrder = BAR_SORT_NAME_AZ; settings->loveIcon = strdup (" <3"); settings->banIcon = strdup (" </3"); settings->atIcon = strdup (" @ "); settings->npSongFormat = strdup ("\"%t\" by \"%a\" on \"%l\"%r%@%s"); settings->npStationFormat = strdup ("Station \"%n\" (%i)"); settings->listSongFormat = strdup ("%i) %a - %t%r"); settings->titleFormat = strdup (TITLE " - \"%t\" by \"%a\" on \"%l\"%r%@%s"); settings->player = NULL; settings->rpcHost = strdup (PIANO_RPC_HOST); settings->rpcTlsPort = NULL; settings->partnerUser = strdup ("android"); settings->partnerPassword = strdup ("AC7IBG09A3DTSYM4R41UJWL07VLN8JI7"); settings->device = strdup ("android-generic"); settings->inkey = strdup ("R=U!LH$O2B#"); settings->outkey = strdup ("6#26FRL$ZWD"); settings->fifo = BarGetXdgConfigDir (PACKAGE_PIPE); assert (settings->fifo != NULL); settings->msgFormat[MSG_NONE].prefix = NULL; settings->msgFormat[MSG_NONE].postfix = NULL; settings->msgFormat[MSG_INFO].prefix = strdup ("(i) "); settings->msgFormat[MSG_INFO].postfix = NULL; settings->msgFormat[MSG_PLAYING].prefix = strdup ("|> "); settings->msgFormat[MSG_PLAYING].postfix = NULL; settings->msgFormat[MSG_TIME].prefix = strdup ("# "); settings->msgFormat[MSG_TIME].postfix = NULL; settings->msgFormat[MSG_ERR].prefix = strdup ("/!\\ "); settings->msgFormat[MSG_ERR].postfix = NULL; settings->msgFormat[MSG_QUESTION].prefix = strdup ("[?] "); settings->msgFormat[MSG_QUESTION].postfix = NULL; settings->msgFormat[MSG_LIST].prefix = strdup ("\t"); settings->msgFormat[MSG_LIST].postfix = NULL; settings->msgFormat[MSG_DEBUG].prefix = NULL; settings->msgFormat[MSG_DEBUG].postfix = NULL; for (size_t i = 0; i < BAR_KS_COUNT; i++) { settings->keys[i] = dispatchActions[i].defaultKey; } /* read config files */ for (size_t j = 0; j < sizeof (configfiles) / sizeof (*configfiles); j++) { static const char *formatMsgPrefix = "format_msg_"; char key[256], val[256]; FILE *configfd; char * const path = BarGetXdgConfigDir (configfiles[j]); assert (path != NULL); if ((configfd = fopen (path, "r")) == NULL) { free (path); continue; } while (1) { char lwhite, rwhite; int scanRet = fscanf (configfd, "%255s%c=%c%255[^\n]", key, &lwhite, &rwhite, val); if (scanRet == EOF) { break; } else if (scanRet != 4 || lwhite != ' ' || rwhite != ' ') { /* invalid config line */ continue; } if (streq ("control_proxy", key)) { settings->controlProxy = strdup (val); } else if (streq ("proxy", key)) { settings->proxy = strdup (val); } else if (streq ("user", key)) { settings->username = strdup (val); } else if (streq ("password", key)) { settings->password = strdup (val); } else if (streq ("password_command", key)) { settings->passwordCmd = strdup (val); } else if (streq ("rpc_host", key)) { free (settings->rpcHost); settings->rpcHost = strdup (val); } else if (streq ("rpc_tls_port", key)) { free (settings->rpcTlsPort); settings->rpcTlsPort = strdup (val); } else if (streq ("partner_user", key)) { free (settings->partnerUser); settings->partnerUser = strdup (val); } else if (streq ("partner_password", key)) { free (settings->partnerPassword); settings->partnerPassword = strdup (val); } else if (streq ("device", key)) { free (settings->device); settings->device = strdup (val); } else if (streq ("encrypt_password", key)) { free (settings->outkey); settings->outkey = strdup (val); } else if (streq ("decrypt_password", key)) { free (settings->inkey); settings->inkey = strdup (val); } else if (streq ("ca_bundle", key)) { free (settings->caBundle); settings->caBundle = strdup (val); } else if (memcmp ("act_", key, 4) == 0) { size_t i; /* keyboard shortcuts */ for (i = 0; i < BAR_KS_COUNT; i++) { if (streq (dispatchActions[i].configKey, key)) { if (streq (val, "disabled")) { settings->keys[i] = BAR_KS_DISABLED; } else { settings->keys[i] = val[0]; } break; } } } else if (streq ("audio_quality", key)) { if (streq (val, "low")) { settings->audioQuality = PIANO_AQ_LOW; } else if (streq (val, "medium")) { settings->audioQuality = PIANO_AQ_MEDIUM; } else if (streq (val, "high")) { settings->audioQuality = PIANO_AQ_HIGH; } } else if (streq ("autostart_station", key)) { free (settings->autostartStation); settings->autostartStation = strdup (val); } else if (streq ("event_command", key)) { settings->eventCmd = BarSettingsExpandTilde (val, userhome); } else if (streq ("history", key)) { settings->history = atoi (val); } else if (streq ("max_player_errors", key)) { settings->maxPlayerErrors = atoi (val); } else if (streq ("sort", key)) { size_t i; static const char *mapping[] = {"name_az", "name_za", "quickmix_01_name_az", "quickmix_01_name_za", "quickmix_10_name_az", "quickmix_10_name_za", }; for (i = 0; i < BAR_SORT_COUNT; i++) { if (streq (mapping[i], val)) { settings->sortOrder = i; break; } } } else if (streq ("love_icon", key)) { free (settings->loveIcon); settings->loveIcon = strdup (val); } else if (streq ("ban_icon", key)) { free (settings->banIcon); settings->banIcon = strdup (val); } else if (streq ("at_icon", key)) { free (settings->atIcon); settings->atIcon = strdup (val); } else if (streq ("volume", key)) { settings->volume = atoi (val); } else if (streq ("format_nowplaying_song", key)) { free (settings->npSongFormat); settings->npSongFormat = strdup (val); } else if (streq ("format_nowplaying_station", key)) { free (settings->npStationFormat); settings->npStationFormat = strdup (val); } else if (streq ("format_list_song", key)) { free (settings->listSongFormat); settings->listSongFormat = strdup (val); } else if (streq ("format_title", key)) { free (settings->titleFormat); settings->titleFormat = strdup (val); } else if (streq ("player", key)) { free (settings->player); settings->player = strdup (val); } else if (streq ("fifo", key)) { free (settings->fifo); settings->fifo = BarSettingsExpandTilde (val, userhome); } else if (streq ("autoselect", key)) { settings->autoselect = atoi (val); } else if (strncmp (formatMsgPrefix, key, strlen (formatMsgPrefix)) == 0) { static const char *mapping[] = {"none", "info", "nowplaying", "time", "err", "question", "list", "debug"}; const char *typeStart = key + strlen (formatMsgPrefix); for (size_t i = 0; i < sizeof (mapping) / sizeof (*mapping); i++) { if (streq (typeStart, mapping[i])) { const char *formatPos = strstr (val, "%s"); /* keep default if there is no format character */ if (formatPos != NULL) { BarMsgFormatStr_t *format = &settings->msgFormat[i]; free (format->prefix); free (format->postfix); const size_t prefixLen = formatPos - val; format->prefix = calloc (prefixLen + 1, sizeof (*format->prefix)); memcpy (format->prefix, val, prefixLen); const size_t postfixLen = strlen (val) - (formatPos-val) - 2; format->postfix = calloc (postfixLen + 1, sizeof (*format->postfix)); memcpy (format->postfix, formatPos+2, postfixLen); } break; } } } } fclose (configfd); free (path); } /* check environment variable if proxy is not set explicitly */ if (settings->proxy == NULL) { char *tmpProxy = getenv ("http_proxy"); if (tmpProxy != NULL && strlen (tmpProxy) > 0) { settings->proxy = strdup (tmpProxy); } } free (userhome); }
/* read app settings from file; format is: key = value\n * @param where to save these settings * @return nothing yet */ void BarSettingsRead (BarSettings_t *settings) { /* FIXME: what is the max length of a path? */ char configfile[1024], key[256], val[256]; size_t i; FILE *configfd; /* _must_ have same order as in BarKeyShortcutId_t */ const char defaultKeys[] = {'?', '+', '-', 'a', 'c', 'd', 'e', 'g', 'h', 'i', 'j', 'm', 'n', 'p', 'q', 'r', 's', 't', 'u', 'x', '$', 'b', '.' }; const char *shortcutFileKeys[] = { "act_help", "act_songlove", "act_songban", "act_stationaddmusic", "act_stationcreate", "act_stationdelete", "act_songexplain", "act_stationaddbygenre", "act_history", "act_songinfo", "act_addshared", "act_songmove", "act_songnext", "act_songpause", "act_quit", "act_stationrename", "act_stationchange", "act_songtired", "act_upcoming", "act_stationselectquickmix", "act_debug", "act_bookmark", "act_download" }; /* apply defaults */ /* #ifdef ENABLE_FAAD settings->audioFormat = PIANO_AF_AACPLUS; #else #ifdef ENABLE_MAD settings->audioFormat = PIANO_AF_MP3; #endif #endif*/ settings->audioFormat = PIANO_AF_AACPLUS; settings->history = 5; memcpy (settings->keys, defaultKeys, sizeof (defaultKeys)); BarGetXdgConfigDir (PACKAGE "/config", configfile, sizeof (configfile)); if ((configfd = fopen (configfile, "r")) == NULL) { return; } /* read config file */ while (1) { int scanRet = fscanf (configfd, "%255s = %255[^\n]", key, val); if (scanRet == EOF) { break; } else if (scanRet != 2) { /* invalid config line */ continue; } if (strcmp ("control_proxy", key) == 0) { settings->controlProxy = strdup (val); } else if (strcmp ("proxy", key) == 0) { settings->proxy = strdup (val); } else if (strcmp ("user", key) == 0) { settings->username = strdup (val); } else if (strcmp ("password", key) == 0) { settings->password = strdup (val); } else if (strcmp ("download_dir", key) == 0) { settings->downloadDir = strdup (val); } else if (memcmp ("act_", key, 4) == 0) { /* keyboard shortcuts */ for (i = 0; i < BAR_KS_COUNT; i++) { if (strcmp (shortcutFileKeys[i], key) == 0) { settings->keys[i] = val[0]; break; } } } else if (strcmp ("audio_format", key) == 0) { if (strcmp (val, "aacplus") == 0) { settings->audioFormat = PIANO_AF_AACPLUS; } else if (strcmp (val, "mp3") == 0) { settings->audioFormat = PIANO_AF_MP3; } else if (strcmp (val, "mp3-hifi") == 0) { settings->audioFormat = PIANO_AF_MP3_HI; } } else if (strcmp ("autostart_station", key) == 0) { settings->autostartStation = strdup (val); } else if (strcmp ("event_command", key) == 0) { settings->eventCmd = strdup (val); } else if (strcmp ("history", key) == 0) { settings->history = atoi (val); } } if (settings->downloadDir == NULL) { settings->downloadDir = "/tmp"; } /* check environment variable if proxy is not set explicitly */ if (settings->proxy == NULL) { char *tmpProxy = getenv ("http_proxy"); if (tmpProxy != NULL && strlen (tmpProxy) > 0) { settings->proxy = strdup (tmpProxy); } } fclose (configfd); }
/* read app settings from file; format is: key = value\n * @param where to save these settings * @return nothing yet */ void BarSettingsRead (BarSettings_t *settings) { char *configfiles[] = {PACKAGE "/state", PACKAGE "/config"}; assert (sizeof (settings->keys) / sizeof (*settings->keys) == sizeof (dispatchActions) / sizeof (*dispatchActions)); /* apply defaults */ settings->audioQuality = PIANO_AQ_HIGH; settings->autoselect = true; settings->history = 5; settings->volume = 0; settings->maxPlayerErrors = 5; settings->sortOrder = BAR_SORT_NAME_AZ; settings->loveIcon = strdup (" <3"); settings->banIcon = strdup (" </3"); settings->atIcon = strdup (" @ "); settings->npSongFormat = strdup ("\"%t\" by \"%a\" on \"%l\"%r%@%s"); settings->npStationFormat = strdup ("Station \"%n\" (%i)"); settings->listSongFormat = strdup ("%i) %a - %t%r"); settings->rpcHost = strdup (PIANO_RPC_HOST); settings->rpcTlsPort = NULL; settings->partnerUser = strdup ("android"); settings->partnerPassword = strdup ("AC7IBG09A3DTSYM4R41UJWL07VLN8JI7"); settings->device = strdup ("android-generic"); settings->inkey = strdup ("R=U!LH$O2B#"); settings->outkey = strdup ("6#26FRL$ZWD"); settings->fifo = malloc (PATH_MAX * sizeof (*settings->fifo)); BarGetXdgConfigDir (PACKAGE "/ctl", settings->fifo, PATH_MAX); memcpy (settings->tlsFingerprint, "\x2D\x0A\xFD\xAF\xA1\x6F\x4B\x5C\x0A" "\x43\xF3\xCB\x1D\x47\x52\xF9\x53\x55\x07\xC0", sizeof (settings->tlsFingerprint)); settings->msgFormat[MSG_NONE].prefix = NULL; settings->msgFormat[MSG_NONE].postfix = NULL; settings->msgFormat[MSG_INFO].prefix = strdup ("(i) "); settings->msgFormat[MSG_INFO].postfix = NULL; settings->msgFormat[MSG_PLAYING].prefix = strdup ("|> "); settings->msgFormat[MSG_PLAYING].postfix = NULL; settings->msgFormat[MSG_TIME].prefix = strdup ("# "); settings->msgFormat[MSG_TIME].postfix = NULL; settings->msgFormat[MSG_ERR].prefix = strdup ("/!\\ "); settings->msgFormat[MSG_ERR].postfix = NULL; settings->msgFormat[MSG_QUESTION].prefix = strdup ("[?] "); settings->msgFormat[MSG_QUESTION].postfix = NULL; settings->msgFormat[MSG_LIST].prefix = strdup ("\t"); settings->msgFormat[MSG_LIST].postfix = NULL; settings->download = 0; settings->downloadSafeFilename = false; settings->downloadSeparator = strdup("---"); settings->downloadCleanup = true; for (size_t i = 0; i < BAR_KS_COUNT; i++) { settings->keys[i] = dispatchActions[i].defaultKey; } /* read config files */ for (size_t j = 0; j < sizeof (configfiles) / sizeof (*configfiles); j++) { static const char *formatMsgPrefix = "format_msg_"; char key[256], val[256], path[PATH_MAX]; FILE *configfd; BarGetXdgConfigDir (configfiles[j], path, sizeof (path)); if ((configfd = fopen (path, "r")) == NULL) { continue; } while (1) { char lwhite, rwhite; int scanRet = fscanf (configfd, "%255s%c=%c%255[^\n]", key, &lwhite, &rwhite, val); if (scanRet == EOF) { break; } else if (scanRet != 4 || lwhite != ' ' || rwhite != ' ') { /* invalid config line */ continue; } if (streq ("control_proxy", key)) { settings->controlProxy = strdup (val); } else if (streq ("proxy", key)) { settings->proxy = strdup (val); } else if (streq ("user", key)) { settings->username = strdup (val); } else if (streq ("password", key)) { settings->password = strdup (val); } else if (streq ("password_command", key)) { settings->passwordCmd = strdup (val); } else if (streq ("rpc_host", key)) { free (settings->rpcHost); settings->rpcHost = strdup (val); } else if (streq ("rpc_tls_port", key)) { free (settings->rpcTlsPort); settings->rpcTlsPort = strdup (val); } else if (streq ("partner_user", key)) { free (settings->partnerUser); settings->partnerUser = strdup (val); } else if (streq ("partner_password", key)) { free (settings->partnerPassword); settings->partnerPassword = strdup (val); } else if (streq ("device", key)) { free (settings->device); settings->device = strdup (val); } else if (streq ("encrypt_password", key)) { free (settings->outkey); settings->outkey = strdup (val); } else if (streq ("decrypt_password", key)) { free (settings->inkey); settings->inkey = strdup (val); } else if (memcmp ("act_", key, 4) == 0) { size_t i; /* keyboard shortcuts */ for (i = 0; i < BAR_KS_COUNT; i++) { if (streq (dispatchActions[i].configKey, key)) { if (streq (val, "disabled")) { settings->keys[i] = BAR_KS_DISABLED; } else { settings->keys[i] = val[0]; } break; } } } else if (streq ("audio_quality", key)) { if (streq (val, "low")) { settings->audioQuality = PIANO_AQ_LOW; } else if (streq (val, "medium")) { settings->audioQuality = PIANO_AQ_MEDIUM; } else if (streq (val, "high")) { settings->audioQuality = PIANO_AQ_HIGH; } } else if (streq ("autostart_station", key)) { free (settings->autostartStation); settings->autostartStation = strdup (val); } else if (streq ("event_command", key)) { settings->eventCmd = strdup (val); } else if (streq ("history", key)) { settings->history = atoi (val); } else if (streq ("max_player_errors", key)) { settings->maxPlayerErrors = atoi (val); } else if (streq ("sort", key)) { size_t i; static const char *mapping[] = {"name_az", "name_za", "quickmix_01_name_az", "quickmix_01_name_za", "quickmix_10_name_az", "quickmix_10_name_za", }; for (i = 0; i < BAR_SORT_COUNT; i++) { if (streq (mapping[i], val)) { settings->sortOrder = i; break; } } } else if (streq ("love_icon", key)) { free (settings->loveIcon); settings->loveIcon = strdup (val); } else if (streq ("ban_icon", key)) { free (settings->banIcon); settings->banIcon = strdup (val); } else if (streq ("at_icon", key)) { free (settings->atIcon); settings->atIcon = strdup (val); } else if (streq ("volume", key)) { settings->volume = atoi (val); } else if (streq ("format_nowplaying_song", key)) { free (settings->npSongFormat); settings->npSongFormat = strdup (val); } else if (streq ("format_nowplaying_station", key)) { free (settings->npStationFormat); settings->npStationFormat = strdup (val); } else if (streq ("format_list_song", key)) { free (settings->listSongFormat); settings->listSongFormat = strdup (val); } else if (streq ("fifo", key)) { free (settings->fifo); settings->fifo = strdup (val); } else if (streq ("autoselect", key)) { settings->autoselect = atoi (val); } else if (streq ("tls_fingerprint", key)) { /* expects 40 byte hex-encoded sha1 */ if (strlen (val) == 40) { for (size_t i = 0; i < 20; i++) { char hex[3]; memcpy (hex, &val[i*2], 2); hex[2] = '\0'; settings->tlsFingerprint[i] = strtol (hex, NULL, 16); } } } else if (strncmp (formatMsgPrefix, key, strlen (formatMsgPrefix)) == 0) { static const char *mapping[] = {"none", "info", "nowplaying", "time", "err", "question", "list"}; const char *typeStart = key + strlen (formatMsgPrefix); for (size_t i = 0; i < sizeof (mapping) / sizeof (*mapping); i++) { if (streq (typeStart, mapping[i])) { const char *formatPos = strstr (val, "%s"); /* keep default if there is no format character */ if (formatPos != NULL) { BarMsgFormatStr_t *format = &settings->msgFormat[i]; free (format->prefix); free (format->postfix); const size_t prefixLen = formatPos - val; format->prefix = calloc (prefixLen + 1, sizeof (*format->prefix)); memcpy (format->prefix, val, prefixLen); const size_t postfixLen = strlen (val) - (formatPos-val) - 2; format->postfix = calloc (postfixLen + 1, sizeof (*format->postfix)); memcpy (format->postfix, formatPos+2, postfixLen); } break; } } } else if (streq ("download", key)) { settings->download = strdup (val); } else if (streq ("download_safe_filename", key)) { settings->downloadSafeFilename = ( !streq( val, "0" ) && !streq( val, "false" ) && strlen( val ) ) ? true : false; } else if (streq ("download_separator", key)) { settings->downloadSeparator = strdup (val); } else if (streq ("download_cleanup", key)) { settings->downloadCleanup = ( !streq( val, "0" ) && !streq( val, "false" ) && strlen( val ) ) ? true : false; } } fclose (configfd); } /* check environment variable if proxy is not set explicitly */ if (settings->proxy == NULL) { char *tmpProxy = getenv ("http_proxy"); if (tmpProxy != NULL && strlen (tmpProxy) > 0) { settings->proxy = strdup (tmpProxy); } } }