2018-04-27 13:20:52 +00:00
|
|
|
// This file is part of Snownews - A lightweight console RSS newsreader
|
|
|
|
//
|
|
|
|
// Copyright (c) 2003-2004 Oliver Feiler <kiza@kcore.de>
|
|
|
|
//
|
|
|
|
// Snownews is free software: you can redistribute it and/or modify
|
|
|
|
// it under the terms of the GNU General Public License version 3
|
|
|
|
// as published by the Free Software Foundation.
|
|
|
|
//
|
|
|
|
// Snownews is distributed in the hope that it will be useful,
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty
|
|
|
|
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
// See the GNU General Public License for more details.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the GNU General Public License
|
|
|
|
// along with Snownews. If not, see http://www.gnu.org/licenses/.
|
|
|
|
|
2018-04-30 15:15:42 +00:00
|
|
|
#include "main.h"
|
2015-07-02 16:27:59 +00:00
|
|
|
#include "interface.h"
|
2018-04-30 01:08:06 +00:00
|
|
|
#include "io-internal.h"
|
2015-07-02 16:27:59 +00:00
|
|
|
#include "setup.h"
|
2018-04-30 01:08:06 +00:00
|
|
|
#include "ui-support.h"
|
|
|
|
#include <ncurses.h>
|
|
|
|
#include <signal.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/wait.h>
|
|
|
|
#include <unistd.h>
|
2015-07-02 16:27:59 +00:00
|
|
|
|
2018-04-30 15:15:42 +00:00
|
|
|
//----------------------------------------------------------------------
|
|
|
|
// Global variables
|
|
|
|
|
|
|
|
struct feed* _feed_list = NULL;
|
|
|
|
struct feed* _unfiltered_feed_list = NULL; // Backup first pointer for filter mode.
|
|
|
|
// Needs to be global so it can be used in the signal handler.
|
|
|
|
// Must be set to NULL by default and whenever it's not used anymore!
|
|
|
|
|
|
|
|
struct settings _settings = {
|
|
|
|
.keybindings = {
|
|
|
|
// Define default values for keybindings. If some are defined differently
|
|
|
|
// in the keybindings file they will be overwritten. If some are missing or broken/wrong
|
|
|
|
// these are sane defaults.
|
|
|
|
.about = 'A',
|
|
|
|
.addfeed = 'a',
|
|
|
|
.andxor = 'X',
|
|
|
|
.categorize = 'C',
|
|
|
|
.changefeedname = 'c',
|
|
|
|
.deletefeed = 'D',
|
|
|
|
.dfltbrowser = 'B',
|
|
|
|
.end = '>',
|
|
|
|
.enter = 'l',
|
|
|
|
.feedinfo = 'i',
|
|
|
|
.filter = 'f',
|
|
|
|
.filtercurrent = 'g',
|
|
|
|
.forcereload = 'T',
|
|
|
|
.help = 'h',
|
|
|
|
.home = '<',
|
|
|
|
.markallread = 'm',
|
|
|
|
.markread = 'm',
|
|
|
|
.markunread = 'M',
|
|
|
|
.movedown = 'N',
|
|
|
|
.moveup = 'P',
|
|
|
|
.newheadlines = 'H',
|
|
|
|
.next = 'n',
|
|
|
|
.nofilter = 'F',
|
|
|
|
.pdown = ' ',
|
|
|
|
.perfeedfilter = 'e',
|
|
|
|
.prev = 'p',
|
|
|
|
.prevmenu = 'q',
|
|
|
|
.pup = 'b',
|
|
|
|
.quit = 'q',
|
|
|
|
.reload = 'r',
|
|
|
|
.reloadall = 'R',
|
|
|
|
.sortfeeds = 's',
|
|
|
|
.typeahead = '/',
|
|
|
|
.urljump = 'o',
|
|
|
|
.urljump2 = 'O'
|
|
|
|
},
|
|
|
|
.color = {
|
|
|
|
.feedtitle = -1,
|
|
|
|
.feedtitlebold = 0,
|
|
|
|
.newitems = 5,
|
|
|
|
.newitemsbold = 0,
|
|
|
|
.urljump = 4,
|
|
|
|
.urljumpbold = 0
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------
|
2015-07-02 16:27:59 +00:00
|
|
|
|
|
|
|
static int last_signal = 0;
|
|
|
|
|
2018-04-30 15:15:42 +00:00
|
|
|
//----------------------------------------------------------------------
|
|
|
|
|
|
|
|
enum EPIDAction { pid_file_delete, pid_file_create };
|
2015-07-02 16:27:59 +00:00
|
|
|
|
2018-04-30 01:08:06 +00:00
|
|
|
static void modifyPIDFile (enum EPIDAction action) {
|
|
|
|
char pid_path [PATH_MAX];
|
2015-07-02 16:27:59 +00:00
|
|
|
snprintf(pid_path, sizeof(pid_path), "%s/.snownews/pid", getenv("HOME"));
|
|
|
|
if (action == pid_file_create) {
|
2018-04-30 01:08:06 +00:00
|
|
|
FILE* file = fopen(pid_path, "w");
|
|
|
|
if (!file) {
|
2015-07-02 16:27:59 +00:00
|
|
|
printf("Unable to write PID file %s!", pid_path);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
fprintf(file, "%d", getpid());
|
|
|
|
fclose(file);
|
2018-04-30 01:08:06 +00:00
|
|
|
} else
|
2015-07-02 16:27:59 +00:00
|
|
|
unlink(pid_path);
|
|
|
|
}
|
|
|
|
|
2018-04-30 01:08:06 +00:00
|
|
|
static void checkPIDFile (void) {
|
|
|
|
char pid_path [PATH_MAX];
|
|
|
|
snprintf (pid_path, sizeof(pid_path), "%s/.snownews/pid", getenv("HOME"));
|
|
|
|
|
|
|
|
FILE* pidfile = fopen(pid_path, "r");
|
|
|
|
if (!pidfile)
|
|
|
|
return;
|
|
|
|
char pidbuf[12];
|
|
|
|
fgets (pidbuf, sizeof(pidbuf), pidfile);
|
|
|
|
fclose (pidfile);
|
|
|
|
pid_t pid = atoi(pidbuf);
|
|
|
|
|
|
|
|
if (kill(pid, 0) == 0) {
|
|
|
|
printf("Snownews seems to be already running with process ID %d.\n", pid);
|
|
|
|
printf("A pid file exists at \"%s\".\n", pid_path);
|
|
|
|
exit(2);
|
|
|
|
} else {
|
|
|
|
printf("A pid file exists at \"%s\",\nbut Snownews doesn't seem to be running. Delete that file and start it again.\n", pid_path);
|
|
|
|
printf("Continue anyway? (y/n) ");
|
|
|
|
char ybuf[2] = {};
|
|
|
|
fgets (ybuf, sizeof(ybuf), stdin);
|
|
|
|
if (ybuf[0] != 'y')
|
|
|
|
exit(2);
|
|
|
|
modifyPIDFile (pid_file_delete);
|
2015-07-02 16:27:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-27 14:47:43 +00:00
|
|
|
// Deinit ncurses and quit.
|
2018-04-30 01:08:06 +00:00
|
|
|
_Noreturn void MainQuit (const char* func, const char* error) {
|
|
|
|
if (!error) // Only save settings if we didn't exit on error.
|
2015-07-02 16:27:59 +00:00
|
|
|
WriteCache();
|
2018-04-30 01:08:06 +00:00
|
|
|
endwin(); // Make sure no ncurses function is called after this point!
|
2015-07-02 16:27:59 +00:00
|
|
|
modifyPIDFile(pid_file_delete);
|
2018-04-30 01:08:06 +00:00
|
|
|
|
2015-07-02 16:27:59 +00:00
|
|
|
if (last_signal)
|
|
|
|
printf ("Exiting via signal %d.\n", last_signal);
|
2018-04-30 16:36:28 +00:00
|
|
|
if (error) {
|
2015-07-02 16:27:59 +00:00
|
|
|
printf (_("Aborting program execution!\nAn internal error occured. Snownews has quit, no changes has been saved!\n"));
|
2018-04-30 01:08:06 +00:00
|
|
|
printf (_("Please submit a bugreport at https://github.com/kouya/snownews/issues\n"));
|
2015-07-02 16:27:59 +00:00
|
|
|
printf ("----\n");
|
2018-04-27 14:47:43 +00:00
|
|
|
// Please don't localize! I don't want to receive Japanese error messages.
|
|
|
|
// Thanks. :)
|
2015-07-02 16:27:59 +00:00
|
|
|
printf ("While executing: %s\n", func);
|
|
|
|
printf ("Error as reported by the system: %s\n\n", error);
|
2018-04-30 01:08:06 +00:00
|
|
|
exit (EXIT_FAILURE);
|
2015-07-02 16:27:59 +00:00
|
|
|
}
|
2018-04-30 16:36:28 +00:00
|
|
|
exit (EXIT_SUCCESS);
|
2015-07-02 16:27:59 +00:00
|
|
|
}
|
|
|
|
|
2018-04-27 14:47:43 +00:00
|
|
|
// Signal handler function.
|
2018-04-30 16:36:28 +00:00
|
|
|
static _Noreturn void MainSignalHandler (int sig) {
|
2015-07-02 16:27:59 +00:00
|
|
|
last_signal = sig;
|
2018-04-30 01:08:06 +00:00
|
|
|
|
2018-04-30 15:15:42 +00:00
|
|
|
// If there is a _unfiltered_feed_list!=NULL a filter is set. Reset _feed_list
|
2018-04-27 14:47:43 +00:00
|
|
|
// so the correct list gets written on the disk when exisiting via SIGINT.
|
2018-04-30 15:15:42 +00:00
|
|
|
if (_unfiltered_feed_list)
|
|
|
|
_feed_list = _unfiltered_feed_list;
|
2018-04-30 01:08:06 +00:00
|
|
|
MainQuit (NULL, "Signal");
|
2015-07-02 16:27:59 +00:00
|
|
|
}
|
|
|
|
|
2018-04-27 14:47:43 +00:00
|
|
|
// Automatic child reaper.
|
2018-04-25 15:17:06 +00:00
|
|
|
static void sigChildHandler (int sig __attribute__((unused))) {
|
2018-04-27 14:47:43 +00:00
|
|
|
// Wait for any child without blocking
|
2018-04-25 15:17:06 +00:00
|
|
|
waitpid (-1, NULL, WNOHANG);
|
2015-07-02 16:27:59 +00:00
|
|
|
}
|
|
|
|
|
2018-04-30 01:08:06 +00:00
|
|
|
static void printHelp (void) {
|
|
|
|
printf (_("Snownews %s\n\n"), SNOWNEWS_VERSTRING);
|
2015-07-02 16:27:59 +00:00
|
|
|
printf (_("usage: snownews [-huV] [--help|--update|--version]\n\n"));
|
|
|
|
printf (_("\t--charset|-l\tForce using this charset.\n"));
|
|
|
|
printf (_("\t--cursor-on|-c\tForce cursor always visible.\n"));
|
|
|
|
printf (_("\t--help|-h\tPrint this help message.\n"));
|
|
|
|
printf (_("\t--update|-u\tAutomatically update every feed.\n"));
|
|
|
|
printf (_("\t--version|-V\tPrint version number and exit.\n"));
|
|
|
|
}
|
2018-04-30 01:08:06 +00:00
|
|
|
|
|
|
|
static void badOption (const char * arg) {
|
2015-07-02 16:27:59 +00:00
|
|
|
printf (_("Option %s requires an argument.\n"), arg);
|
|
|
|
}
|
|
|
|
|
|
|
|
int main (int argc, char *argv[]) {
|
2018-04-30 01:08:06 +00:00
|
|
|
#ifdef LOCALEPATH
|
|
|
|
setlocale (LC_ALL, "");
|
|
|
|
bindtextdomain ("snownews", LOCALEPATH);
|
|
|
|
textdomain ("snownews");
|
|
|
|
#endif
|
|
|
|
srand (time(0)); // Init the pRNG. See about.c for usages of rand() ;)
|
|
|
|
|
|
|
|
bool autoupdate = false; // Automatically update feeds on app start... or not if set to 0.
|
|
|
|
for (int i = 1; i < argc; ++i) {
|
|
|
|
char* arg = argv[i];
|
|
|
|
if (strcmp(arg, "--version") == 0 || strcmp(arg, "-V") == 0) {
|
|
|
|
printf (_("Snownews version %s\n\n"), SNOWNEWS_VERSION);
|
|
|
|
return EXIT_SUCCESS;
|
|
|
|
} else if (strcmp(arg, "-u") == 0 || strcmp(arg, "--update") == 0) {
|
|
|
|
autoupdate = true;
|
|
|
|
} else if (strcmp(arg, "-c") == 0 || strcmp(arg, "--cursor-on") == 0) {
|
2018-04-30 15:15:42 +00:00
|
|
|
_settings.cursor_always_visible = true;
|
2018-04-30 01:08:06 +00:00
|
|
|
} else if (strcmp(arg, "-l") == 0 || strcmp(arg, "--charset") == 0) {
|
|
|
|
const char* chset = argv[i++];
|
|
|
|
if (chset)
|
2018-04-30 15:15:42 +00:00
|
|
|
_settings.global_charset = chset;
|
2018-04-30 01:08:06 +00:00
|
|
|
else {
|
|
|
|
badOption(arg);
|
|
|
|
return EXIT_FAILURE;
|
2015-07-02 16:27:59 +00:00
|
|
|
}
|
2018-04-30 01:08:06 +00:00
|
|
|
} else if (strcmp(arg, "-h") == 0 || strcmp(arg, "--help") == 0) {
|
|
|
|
printHelp();
|
|
|
|
return EXIT_SUCCESS;
|
|
|
|
} else {
|
|
|
|
printf ("Unkown option given: \"%s\".\n", arg);
|
|
|
|
printHelp();
|
|
|
|
return EXIT_SUCCESS;
|
2015-07-02 16:27:59 +00:00
|
|
|
}
|
|
|
|
}
|
2018-04-30 01:08:06 +00:00
|
|
|
|
2018-04-27 14:47:43 +00:00
|
|
|
// Create PID file.
|
2018-04-30 01:08:06 +00:00
|
|
|
checkPIDFile();
|
|
|
|
modifyPIDFile (pid_file_create);
|
2015-07-02 16:27:59 +00:00
|
|
|
|
|
|
|
signal (SIGHUP, MainSignalHandler);
|
|
|
|
signal (SIGINT, MainSignalHandler);
|
|
|
|
signal (SIGTERM, MainSignalHandler);
|
2018-04-30 01:08:06 +00:00
|
|
|
signal (SIGABRT, MainSignalHandler);
|
|
|
|
signal (SIGSEGV, MainSignalHandler);
|
|
|
|
|
2018-04-27 14:47:43 +00:00
|
|
|
// Un-broken pipify
|
2015-07-02 16:27:59 +00:00
|
|
|
signal (SIGPIPE, SIG_IGN);
|
|
|
|
signal (SIGCHLD, sigChildHandler);
|
2018-04-30 01:08:06 +00:00
|
|
|
#ifdef SIGWINCH
|
|
|
|
signal (SIGWINCH, sig_winch);
|
|
|
|
#endif
|
2015-07-02 16:27:59 +00:00
|
|
|
|
|
|
|
InitCurses();
|
|
|
|
|
2018-04-30 01:08:06 +00:00
|
|
|
// Check if configfiles exist and create/read them.
|
|
|
|
LoadAllFeeds (Config());
|
2015-07-02 16:27:59 +00:00
|
|
|
if (autoupdate)
|
|
|
|
UpdateAllFeeds();
|
2018-04-30 01:08:06 +00:00
|
|
|
|
2018-04-27 14:47:43 +00:00
|
|
|
// Give control to main program loop.
|
2015-07-02 16:27:59 +00:00
|
|
|
UIMainInterface();
|
|
|
|
|
2018-04-27 14:47:43 +00:00
|
|
|
// We really shouldn't be here at all... ah well.
|
2018-04-30 01:08:06 +00:00
|
|
|
return EXIT_SUCCESS;
|
2015-07-02 16:27:59 +00:00
|
|
|
}
|