This repository has been archived on 2024-03-20. You can view files and clone it, but you cannot make any changes to it's state, such as pushing and creating new issues, pull requests or comments.
gtkslash/gtkslash.c
2024-03-20 09:24:54 -05:00

556 lines
13 KiB
C

/* gtkSlash - Slashdot headlines news-ticker
* (c) 1998 Troy Engel <tengel@sonic.net>
*
* Based on original code from slashes.pl
* (c) 1998 Alex Shnitman <alexsh@linux.org.il>
*
* Other cool code from grun
* (c) 1998 Southern Gold Development <tangomanrulz@geocities.com>
*
* This code is distributed under the terms of the GNU
* General Public License, etc etc. Use it, don't abuse it.
*/
/* just what it says - good for layout development
* just remember to turn it off :P */
/* #define _LOCAL_TEST_MODE */
#include <gtk/gtk.h>
#ifdef WIN32
#include <windows.h>
#define DIR_CHAR "\\"
#define DOT_CHAR "_"
#else
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#define DIR_CHAR "/"
#define DOT_CHAR "."
/* these two allow Win people to "hide" a cmdline
* app. Provided here just so it works (could be used
* for the same thing in 'startApp' on unix?)
*/
#define SW_SHOWDEFAULT 0
#define SW_HIDE 0
#endif
#include "rcfuncs.h"
#define MAX_BUFF 1024
#define APP_RC "gtkslashrc"
#define MSG_TITLE "Slashdot Headlines"
#define MSG_INIT "Initializing..."
#define MSG_LOAD "Loading ultramode.txt..."
#define MSG_LOAD_ERR "Error loading ultramode.txt!"
#define MSG_RETRIEVE "Retrieving latest ultramode.txt..."
#define MSG_RETR_ERR "Error retrieving ultramode.txt!"
#define MSG_LAUNCH "Launching web browser..."
#define MSG_LAUNCH_ERR "Error launching browser!"
#define MSG_IDLE "Status: Idle (gtkSlash 0.5.5)"
/* read from RC file */
gint refresh_timeout;
gchar browser_cmd[MAX_BUFF];
gchar new_browser_cmd[MAX_BUFF];
gchar curl_cmd[MAX_BUFF];
BOOL compact_mode;
gint num_fields;
BOOL comments_flat;
/* some ugly global vars */
GtkWidget *status;
/* retrieves a line from a linefeed delimited textfile */
static
gchar *app_getLine(FILE *file) {
char *tmp, in;
int cnt = 0, retIn;
if (feof(file)) {
return NULL;
}
else {
tmp = g_malloc(sizeof(char) * MAX_BUFF);
retIn = fread((void *) &in, sizeof(char), 1, file);
if (retIn != 1) {
g_free(tmp);
return NULL;
}
while ((in != '\n') && (cnt < MAX_BUFF)) {
tmp[cnt] = in;
cnt++;
retIn = fread((void *) &in, sizeof(char), 1, file);
if (retIn != 1) {
g_free(tmp);
return NULL;
}
}
if (cnt == MAX_BUFF) {
g_free(tmp);
return NULL;
}
tmp[cnt] = '\0';
return tmp;
}
}
/* loads ultramode.txt into our clist */
static
BOOL app_loadFile(GtkWidget *list, const gchar *fname) {
gchar *line;
gchar *article[4], *link;
FILE *fHnd;
gint i;
fHnd = fopen(fname, "rb");
if (!fHnd) {
return FALSE;
}
line = app_getLine(fHnd);
if (!line) {
fclose(fHnd);
return FALSE;
}
/* loop till we find the first record delim */
while (strncmp(line, "%%", 2) != 0) {
g_free(line);
line = app_getLine(fHnd);
}
gtk_clist_clear(GTK_CLIST(list));
gtk_clist_freeze(GTK_CLIST(list));
link = NULL;
while (line) {
gboolean bGotit = FALSE;
g_free(line); /* should be %% */
for (i = 0 ; i<num_fields; i++) {
line = app_getLine(fHnd);
if (line) {
bGotit = TRUE;
switch (i) {
case 0: /* title */
article[0] = line;
break;
case 1: /* link */
link = line;
break;
case 3: /* author */
article[1] = line;
break;
case 5: /* topic */
article[2] = line;
break;
case 6: /* numcomments */
article[3] = line;
break;
default: /* unused */
g_free(line);
break;
}
}
}
if (bGotit) {
gint index;
index = gtk_clist_append(GTK_CLIST(list), article);
gtk_clist_set_row_data(GTK_CLIST(list), index, link);
for (i=0; i<4; i++) {
if (article[i])
g_free(article[i]);
}
}
line = app_getLine(fHnd);
}
fclose(fHnd);
gtk_clist_thaw(GTK_CLIST(list));
return TRUE;
}
/* launches a given app */
static
BOOL app_startApp(const gchar *cmd, BOOL bWait, WORD wShowState) {
#ifndef WIN32
char **args, *work, *twrk;
int cnt, len, scnt, pid, sta;
pid = fork();
if (pid == 0) {
/* if not waiting for return of app, fork() again to
* detach completely */
if (!bWait) {
pid = fork();
}
if (pid == 0) {
len = strlen(cmd);
scnt = 1;
for (cnt = 0; cnt < len; cnt++) {
if (cmd[cnt] == ' ') {
scnt++;
}
}
args = g_malloc(sizeof(char *) * (scnt + 1));
args[scnt] = NULL;
if (scnt == 1) {
args[0] = g_malloc(sizeof(char) * (len + 1));
strcpy(args[0], cmd);
} else {
work = g_malloc(sizeof(char) * (len + 1));
strcpy(work, cmd);
twrk = (char *) strtok(work, " ");
args[0] = twrk;
cnt = 1;
while (twrk) {
twrk = (char *) strtok(NULL, " ");
args[cnt] = twrk;
cnt++;
}
args[cnt] = work;
}
execvp(args[0], args);
} else {
_exit(0);
}
} else {
if (bWait) {
pid = wait(&sta);
return WIFEXITED(sta);
} else {
return TRUE;
}
}
return TRUE;
#else /* Win32 */
STARTUPINFO startupinfo;
PROCESS_INFORMATION processinfo;
startupinfo.cb = sizeof (STARTUPINFO);
startupinfo.lpReserved = NULL;
startupinfo.lpDesktop = NULL;
startupinfo.lpTitle = NULL;
startupinfo.dwFlags = STARTF_USESHOWWINDOW;
startupinfo.wShowWindow = wShowState;
startupinfo.cbReserved2 = 0;
startupinfo.lpReserved2 = NULL;
if (!CreateProcess (NULL, (gchar *)cmd, NULL, NULL,
TRUE, NORMAL_PRIORITY_CLASS, NULL,
NULL, &startupinfo, &processinfo)) {
g_message ("CreateProcess failed\n");
return FALSE;
}
CloseHandle (processinfo.hThread);
if (bWait)
WaitForSingleObject(processinfo.hProcess, INFINITE);
return TRUE;
#endif /* WIN32 */
}
/* uses curl to get ultramode from the web */
static
BOOL app_getFile(const gchar *fname) {
gchar cmd[MAX_BUFF];
sprintf(cmd, curl_cmd, fname);
if (!app_startApp(cmd, TRUE, SW_HIDE))
return FALSE;
return TRUE;
}
static
BOOL app_isZeroByte(const gchar *fname) {
FILE *fHnd;
gint ch = 0;
fHnd = fopen(fname, "rb");
if (!fHnd) {
return TRUE;
}
ch = fgetc(fHnd);
if (ch == EOF) {
fclose(fHnd);
return TRUE;
}
fclose(fHnd);
return FALSE;
}
static
gint app_timer_idle(gpointer data) {
gtk_label_set(GTK_LABEL(status), MSG_IDLE);
return 0;
}
static
gint app_exit(GtkWidget *widget, GdkEvent *event, gpointer data) {
gtk_main_quit();
exit(0);
return FALSE;
}
static
gint app_timer_refresh(GtkWidget *list) {
gchar *home_env, *fname;
gint retval;
#ifdef WIN32
gchar mypath[MAX_BUFF];
#endif
home_env = g_get_home_dir();
#ifdef WIN32 /* no HOME dir */
if (home_env == NULL) {
gchar *cutme;
GetModuleFileName(NULL, mypath, sizeof(mypath));
cutme = strrchr(mypath, '\\');
mypath[cutme - mypath] = '\0';
home_env = mypath;
}
#endif
fname = g_malloc(sizeof(gchar) * (strlen(home_env) + 15));
strcpy(fname, home_env);
strcat(fname, DIR_CHAR);
strcat(fname, "ultramode.tmp");
retval = TRUE;
#ifndef _LOCAL_TEST_MODE
remove(fname);
gtk_label_set(GTK_LABEL(status), MSG_RETRIEVE);
if (!app_getFile(fname) || app_isZeroByte(fname)) {
gtk_label_set(GTK_LABEL(status), MSG_RETR_ERR);
retval = FALSE;
}
#endif
gtk_label_set(GTK_LABEL(status), MSG_LOAD);
if (retval && !app_loadFile(list, fname)) {
gtk_label_set(GTK_LABEL(status), MSG_LOAD_ERR);
retval = FALSE;
}
#ifndef _LOCAL_TEST_MODE
remove(fname);
#endif
g_free(fname);
if (retval)
gtk_label_set(GTK_LABEL(status), MSG_IDLE);
else
gtk_timeout_add(1000*5, app_timer_idle, NULL);
return retval;
}
static
gint app_refresh(GtkWidget *widget, GtkWidget *list) {
return app_timer_refresh(list);
}
/* launch our article */
static
void app_article_browse(GtkWidget *widget, gpointer data) {
gint rerr;
struct stat buff;
gchar *url, *fname, *home;
gchar cmd[MAX_BUFF];
url = gtk_clist_get_row_data(GTK_CLIST(data), GTK_CLIST(data)->focus_row);
if (comments_flat) {
gchar *tmp;
tmp = strrchr(url, '.');
url[tmp - url] = '\0';
strcat(url, "_F.shtml");
}
#ifndef WIN32
home = g_get_home_dir();
fname = g_malloc(sizeof(gchar) * (strlen(home) + 16));
strcpy(fname, home);
strcat(fname, "/.netscape/lock");
rerr = lstat(fname, &buff);
if (rerr == -1) {
sprintf(cmd, new_browser_cmd, url);
} else {
sprintf(cmd, browser_cmd, url);
}
g_free(fname);
#else
sprintf(cmd, browser_cmd, url);
#endif /* WIN32 */
gtk_label_set(GTK_LABEL(status), MSG_LAUNCH);
if (!app_startApp(cmd, FALSE, SW_SHOWDEFAULT))
gtk_label_set(GTK_LABEL(status), MSG_LAUNCH_ERR);
gtk_timeout_add(1000*5, app_timer_idle, NULL);
}
/* sorting */
static
void app_click_column(GtkCList *clist, gint column, gpointer data) {
if (column == clist->sort_column) {
clist->sort_type = (clist->sort_type == GTK_SORT_ASCENDING) ?
GTK_SORT_DESCENDING : GTK_SORT_ASCENDING;
} else {
gtk_clist_set_sort_column (clist, column);
}
gtk_clist_sort(clist);
}
static
BOOL app_parse_rc() {
gchar *home_env, *fname;
gint retval;
home_env = g_get_home_dir();
/* /home/foobar + /. + myrc + \0 */
fname = g_malloc(sizeof(gchar) * (strlen(home_env)+2+strlen(APP_RC)+1));
strcpy(fname, home_env);
strcat(fname, DIR_CHAR);
strcat(fname, DOT_CHAR);
strcat(fname, APP_RC);
retval = RCInit(fname);
g_free(fname);
/* if no personal file, look for system */
if (!retval) {
#ifdef WIN32
gchar tmp[MAX_BUFF];
GetWindowsDirectory(tmp, sizeof(tmp));
/* c:\windows + \ + myrc + \0 */
fname = g_malloc(sizeof(gchar) * (sizeof(tmp) + 1 + sizeof(APP_RC) + 1));
strcpy(fname, tmp);
#else
/* /etc + / + myrc + \0 */
fname = g_malloc(sizeof(gchar) * (4 + 1 + strlen(APP_RC) + 1));
strcpy(fname, "/etc");
#endif
strcat(fname, DIR_CHAR);
strcat(fname, APP_RC);
retval = RCInit(fname);
g_free(fname);
}
refresh_timeout = RCGetInt("timeout-refresh", 1800);
compact_mode = RCGetBool("compact-mode", FALSE);
num_fields = RCGetInt("num-fields", 9);
RCGetString("browser-cmd", browser_cmd, "", sizeof(browser_cmd));
RCGetString("new-browser-cmd", new_browser_cmd, "", sizeof(new_browser_cmd));
RCGetString("curl-cmd", curl_cmd, "", sizeof(curl_cmd));
comments_flat = RCGetBool("comments-flat", FALSE);
RCShutDown();
return retval;
}
static
void app_window() {
GtkWidget *window, *vbox, *clist, *scrolled_win, *hbox, *but;
gchar *titles[4] = {"Title", "Author", "Topic", "Comments"};
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), MSG_TITLE);
gtk_signal_connect(GTK_OBJECT(window), "destroy", (GtkSignalFunc) app_exit, NULL);
gtk_widget_set_usize(GTK_WIDGET(window), 550, 155);
gtk_window_set_policy(GTK_WINDOW (window), TRUE, TRUE, TRUE);
vbox = gtk_vbox_new(FALSE, 5);
gtk_container_border_width (GTK_CONTAINER(vbox), 5);
gtk_container_add(GTK_CONTAINER(window), vbox);
scrolled_win = gtk_scrolled_window_new (NULL, NULL);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_win),
GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
gtk_box_pack_start(GTK_BOX(vbox), scrolled_win, TRUE, TRUE, 0);
clist = gtk_clist_new_with_titles(4, titles);
gtk_clist_set_selection_mode(GTK_CLIST(clist), GTK_SELECTION_BROWSE);
gtk_clist_set_column_width(GTK_CLIST(clist), 0, 250);
gtk_clist_set_column_width(GTK_CLIST(clist), 1, 70);
gtk_clist_set_column_width(GTK_CLIST(clist), 2, 100);
gtk_clist_set_column_width(GTK_CLIST(clist), 3, 10);
gtk_signal_connect(GTK_OBJECT (clist), "click_column",
(GtkSignalFunc)app_click_column, NULL);
gtk_container_add(GTK_CONTAINER(scrolled_win), clist);
gtk_widget_show(clist);
gtk_widget_show(scrolled_win);
if (!compact_mode) {
hbox = gtk_hbox_new(FALSE, 0);
but = gtk_button_new_with_label("Refresh");
gtk_widget_set_usize(GTK_WIDGET(but), 70, 24);
gtk_signal_connect(GTK_OBJECT(but), "clicked", (GtkSignalFunc) app_refresh, clist);
gtk_box_pack_start(GTK_BOX(hbox), but, FALSE, FALSE, 0);
gtk_widget_show(but);
but = gtk_button_new_with_label("Read");
gtk_widget_set_usize(GTK_WIDGET(but), 70, 24);
gtk_signal_connect(GTK_OBJECT(but), "clicked", (GtkSignalFunc) app_article_browse, (gpointer)clist);
gtk_box_pack_start(GTK_BOX(hbox), but, FALSE, FALSE, 0);
gtk_widget_show(but);
status = gtk_label_new(MSG_INIT);
gtk_box_pack_start(GTK_BOX(hbox), status, FALSE, FALSE, 10);
gtk_widget_show(status);
but = gtk_button_new_with_label("Quit");
gtk_widget_set_usize(GTK_WIDGET(but), 70, 24);
gtk_signal_connect(GTK_OBJECT(but), "clicked", (GtkSignalFunc) app_exit, NULL);
gtk_box_pack_end(GTK_BOX(hbox), but, FALSE, FALSE, 0);
gtk_widget_show(but);
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
gtk_widget_show(hbox);
} else {
gtk_clist_column_titles_hide(GTK_CLIST(clist));
gtk_signal_connect(GTK_OBJECT (clist), "select_row", (GtkSignalFunc) app_article_browse, (gpointer)clist);
status = gtk_label_new("");
}
gtk_timeout_add(1000*refresh_timeout, (GtkFunction) app_timer_refresh, clist);
gtk_widget_show(vbox);
gtk_widget_show(window);
app_refresh(NULL, clist);
}
#ifdef WIN32
int PASCAL WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nShowCmd) {
#else
int main(int argc, char **argv) {
#endif
#ifdef WIN32
/* parse into a command line ala argv, argc */
char **argv, *work, *twrk;
int cnt, len, argc;
len = strlen(lpCmdLine);
argc = 0;
for (cnt = 0; cnt < len; cnt++) {
if (lpCmdLine[cnt] == ' ') {
argc++;
}
}
argv = g_malloc(sizeof(char *) * (argc + 1));
argv[argc] = NULL;
if (argc == 1) {
argv[0] = g_malloc(sizeof(char) * (len + 1));
strcpy(argv[0], lpCmdLine);
} else {
work = g_malloc(sizeof(char) * (len + 1));
strcpy(work, lpCmdLine);
twrk = (char *) strtok(work, " ");
argv[0] = twrk;
cnt = 1;
while (twrk) {
twrk = (char *) strtok(NULL, " ");
argv[cnt] = twrk;
cnt++;
}
argv[cnt] = work;
}
#endif /* WIN32 */
gtk_init(&argc, &argv);
app_parse_rc();
app_window();
gtk_main();
return(0) ;
}