diff options
| -rw-r--r-- | .gitignore | 17 | ||||
| -rw-r--r-- | Makefile | 8 | ||||
| -rw-r--r-- | Makefile.am | 7 | ||||
| -rw-r--r-- | config.h | 0 | ||||
| -rw-r--r-- | configure.ac | 54 | ||||
| -rw-r--r-- | configure.sh | 2 | ||||
| -rw-r--r-- | gui/Makefile.am | 3 | ||||
| -rw-r--r-- | gui/viewtorrents.glade | 6 | ||||
| -rw-r--r-- | m4/ax_cflags_warn_all.m4 | 158 | ||||
| -rw-r--r-- | m4/pkg.m4 | 158 | ||||
| -rw-r--r-- | src/.gitignore | 1 | ||||
| -rw-r--r-- | src/Makefile | 30 | ||||
| -rw-r--r-- | src/Makefile.am | 17 | ||||
| -rw-r--r-- | src/main.c | 155 | ||||
| -rw-r--r-- | test/.gitignore | 3 | ||||
| -rw-r--r-- | test/Makefile.am | 13 | ||||
| -rw-r--r-- | test/test_hashlist.c | 284 |
17 files changed, 861 insertions, 55 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c9f7d12 --- /dev/null +++ b/.gitignore @@ -0,0 +1,17 @@ +Makefile +Makefile.in +.deps +*.o +/aclocal.m4 +/autom4te.cache +/compile +/config.h +/config.h.in +/config.log +/config.status +/configure +/depcomp +/install-sh +/missing +/stamp-h1 + diff --git a/Makefile b/Makefile deleted file mode 100644 index f0e2ef0..0000000 --- a/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -all: - $(MAKE) -C src all - -check: - $(MAKE) -C test check - -clean: - $(MAKE) -C src clean diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..18f84ae --- /dev/null +++ b/Makefile.am @@ -0,0 +1,7 @@ +ACLOCAL_AMFLAGS = -I m4 + +MAINTAINERCLEANFILES = Makefile.in aclocal.m4 config.guess config.h.in \ + config.sub configure depcomp install-sh ltmain.sh \ + missing config.rpath mkinstalldirs + +SUBDIRS = gui test src diff --git a/config.h b/config.h deleted file mode 100644 index e69de29..0000000 --- a/config.h +++ /dev/null diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..546e8e9 --- /dev/null +++ b/configure.ac @@ -0,0 +1,54 @@ +AC_INIT([viewtorrents], [0.1], [the_jk@yahoo.com]) +AC_CONFIG_MACRO_DIR([m4]) + +AM_INIT_AUTOMAKE([dist-bzip2 foreign color-tests parallel-tests]) + +m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) + +AC_ISC_POSIX +AM_PROG_CC_STDC +AM_PROG_CC_C_O +AC_HEADER_STDC +AC_C_CONST +AC_C_INLINE + +# Defines + +DEFINES="" +AX_CFLAGS_WARN_ALL(DEFINES) + +AC_ARG_ENABLE([debug], AC_HELP_STRING([compile with debug options]), + if test "x$enableval" = "xyes"; then + DEFINES="$DEFINES -g -DDEBUG" + fi,) + +AC_SUBST(DEFINES) + +# fabs + +AC_SEARCH_LIBS([fabs], [m],, AC_MSG_ERROR([No fabs found])) + +# GTK + +PKG_CHECK_MODULES([GTK], [gtk+-2.0 >= 2.12 glib-2.0 >= 2.28]) + +# xmlrpc-c + +AC_PATH_PROG([XMLRPC_C_CONFIG], [xmlrpc-c-config]) + +AS_IF([test -x "$XMLRPC_C_CONFIG"], + [ + XMLRPC_C_VERSION=`xmlrpc-c-config --version` + XMLRPC_C_CFLAGS=`xmlrpc-c-config client --cflags` + XMLRPC_C_LIBS=`xmlrpc-c-config client --libs` + AS_VERSION_COMPARE([$XMLRPC_C_VERSION],[1.05], + [AC_MSG_ERROR([Need xmlrpc-c >= 1.05])]) + AC_SUBST([XMLRPC_C_CFLAGS]) + AC_SUBST([XMLRPC_C_LIBS]) + ], + [AC_MSG_ERROR([Need xmlrpc-c >= 1.05])]) + +# End + +AM_CONFIG_HEADER([config.h]) +AC_OUTPUT([Makefile gui/Makefile test/Makefile src/Makefile]) diff --git a/configure.sh b/configure.sh deleted file mode 100644 index b4dbe28..0000000 --- a/configure.sh +++ /dev/null @@ -1,2 +0,0 @@ -glib >= 2.28 -xmlrpc-c >= 1.05
\ No newline at end of file diff --git a/gui/Makefile.am b/gui/Makefile.am new file mode 100644 index 0000000..744fbcb --- /dev/null +++ b/gui/Makefile.am @@ -0,0 +1,3 @@ +MAINTAINERCLEANFILES = Makefile.in + +dist_pkgdata_DATA = viewtorrents.glade diff --git a/gui/viewtorrents.glade b/gui/viewtorrents.glade index db80a9c..0068407 100644 --- a/gui/viewtorrents.glade +++ b/gui/viewtorrents.glade @@ -42,7 +42,6 @@ <property name="visible">True</property> <property name="can_focus">True</property> <property name="can_default">True</property> - <property name="has_default">True</property> <property name="receives_default">True</property> <property name="use_action_appearance">False</property> <property name="use_stock">True</property> @@ -114,6 +113,7 @@ If authentication is needed, enter username and password as well.</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="invisible_char">●</property> + <property name="activates_default">True</property> <property name="primary_icon_activatable">False</property> <property name="secondary_icon_activatable">False</property> <property name="primary_icon_sensitive">True</property> @@ -129,6 +129,7 @@ If authentication is needed, enter username and password as well.</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="invisible_char">●</property> + <property name="activates_default">True</property> <property name="primary_icon_activatable">False</property> <property name="secondary_icon_activatable">False</property> <property name="primary_icon_sensitive">True</property> @@ -147,6 +148,7 @@ If authentication is needed, enter username and password as well.</property> <property name="can_focus">True</property> <property name="visibility">False</property> <property name="invisible_char">●</property> + <property name="activates_default">True</property> <property name="primary_icon_activatable">False</property> <property name="secondary_icon_activatable">False</property> <property name="primary_icon_sensitive">True</property> @@ -436,7 +438,6 @@ If authentication is needed, enter username and password as well.</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="can_default">True</property> - <property name="has_default">True</property> <property name="receives_default">True</property> <property name="use_action_appearance">False</property> <property name="use_stock">True</property> @@ -473,6 +474,7 @@ If authentication is needed, enter username and password as well.</property> <property name="can_focus">True</property> <property name="visibility">False</property> <property name="invisible_char">●</property> + <property name="activates_default">True</property> <property name="primary_icon_activatable">False</property> <property name="secondary_icon_activatable">False</property> <property name="primary_icon_sensitive">True</property> diff --git a/m4/ax_cflags_warn_all.m4 b/m4/ax_cflags_warn_all.m4 new file mode 100644 index 0000000..48f9eca --- /dev/null +++ b/m4/ax_cflags_warn_all.m4 @@ -0,0 +1,158 @@ +##### http://autoconf-archive.cryp.to/ax_cflags_warn_all.html +# +# SYNOPSIS +# +# AX_CFLAGS_WARN_ALL [(shellvar [,default, [A/NA]])] +# +# DESCRIPTION +# +# Try to find a compiler option that enables most reasonable +# warnings. This macro is directly derived from VL_PROG_CC_WARNINGS +# which is split up into two AX_CFLAGS_WARN_ALL and +# AX_CFLAGS_WARN_ALL_ANSI +# +# For the GNU CC compiler it will be -Wall (and -ansi -pedantic) The +# result is added to the shellvar being CFLAGS by default. +# +# Currently this macro knows about GCC, Solaris C compiler, Digital +# Unix C compiler, C for AIX Compiler, HP-UX C compiler, IRIX C +# compiler, NEC SX-5 (Super-UX 10) C compiler, and Cray J90 (Unicos +# 10.0.0.8) C compiler. +# +# - $1 shell-variable-to-add-to : CFLAGS +# - $2 add-value-if-not-found : nothing +# - $3 action-if-found : add value to shellvariable +# - $4 action-if-not-found : nothing +# +# LAST MODIFICATION +# +# 2006-12-12 +# +# COPYLEFT +# +# Copyright (c) 2006 Guido U. Draheim <guidod@gmx.de> +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of the +# License, or (at your option) any later version. +# +# This program 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 this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. +# +# As a special exception, the respective Autoconf Macro's copyright +# owner gives unlimited permission to copy, distribute and modify the +# configure scripts that are the output of Autoconf when processing +# the Macro. You need not follow the terms of the GNU General Public +# License when using or distributing such scripts, even though +# portions of the text of the Macro appear in them. The GNU General +# Public License (GPL) does govern all other use of the material that +# constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the +# Autoconf Macro released by the Autoconf Macro Archive. When you +# make and distribute a modified version of the Autoconf Macro, you +# may extend this special exception to the GPL to apply to your +# modified version as well. + +AC_DEFUN([AX_CFLAGS_WARN_ALL],[dnl +AS_VAR_PUSHDEF([FLAGS],[CFLAGS])dnl +AS_VAR_PUSHDEF([VAR],[ac_cv_cflags_warn_all])dnl +AC_CACHE_CHECK([m4_ifval($1,$1,FLAGS) for maximum warnings], +VAR,[VAR="no, unknown" + AC_LANG_SAVE + AC_LANG_C + ac_save_[]FLAGS="$[]FLAGS" +for ac_arg dnl +in "-pedantic % -Wall" dnl GCC + "-xstrconst % -v" dnl Solaris C + "-std1 % -verbose -w0 -warnprotos" dnl Digital Unix + "-qlanglvl=ansi % -qsrcmsg -qinfo=all:noppt:noppc:noobs:nocnd" dnl AIX + "-ansi -ansiE % -fullwarn" dnl IRIX + "+ESlit % +w1" dnl HP-UX C + "-Xc % -pvctl[,]fullmsg" dnl NEC SX-5 (Super-UX 10) + "-h conform % -h msglevel 2" dnl Cray C (Unicos) + # +do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'` + AC_TRY_COMPILE([],[return 0;], + [VAR=`echo $ac_arg | sed -e 's,.*% *,,'` ; break]) +done + FLAGS="$ac_save_[]FLAGS" + AC_LANG_RESTORE +]) +case ".$VAR" in + .ok|.ok,*) m4_ifvaln($3,$3) ;; + .|.no|.no,*) m4_ifvaln($4,$4,[m4_ifval($2,[ + AC_RUN_LOG([: m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $2"]) + m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $2"])]) ;; + *) m4_ifvaln($3,$3,[ + if echo " $[]m4_ifval($1,$1,FLAGS) " | grep " $VAR " 2>&1 >/dev/null + then AC_RUN_LOG([: m4_ifval($1,$1,FLAGS) does contain $VAR]) + else AC_RUN_LOG([: m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $VAR"]) + m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $VAR" + fi ]) ;; +esac +AS_VAR_POPDEF([VAR])dnl +AS_VAR_POPDEF([FLAGS])dnl +]) + +dnl the only difference - the LANG selection... and the default FLAGS + +AC_DEFUN([AX_CXXFLAGS_WARN_ALL],[dnl +AS_VAR_PUSHDEF([FLAGS],[CXXFLAGS])dnl +AS_VAR_PUSHDEF([VAR],[ac_cv_cxxflags_warn_all])dnl +AC_CACHE_CHECK([m4_ifval($1,$1,FLAGS) for maximum warnings], +VAR,[VAR="no, unknown" + AC_LANG_SAVE + AC_LANG_CPLUSPLUS + ac_save_[]FLAGS="$[]FLAGS" +for ac_arg dnl +in "-pedantic % -Wall" dnl GCC + "-xstrconst % -v" dnl Solaris C + "-std1 % -verbose -w0 -warnprotos" dnl Digital Unix + "-qlanglvl=ansi % -qsrcmsg -qinfo=all:noppt:noppc:noobs:nocnd" dnl AIX + "-ansi -ansiE % -fullwarn" dnl IRIX + "+ESlit % +w1" dnl HP-UX C + "-Xc % -pvctl[,]fullmsg" dnl NEC SX-5 (Super-UX 10) + "-h conform % -h msglevel 2" dnl Cray C (Unicos) + # +do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'` + AC_TRY_COMPILE([],[return 0;], + [VAR=`echo $ac_arg | sed -e 's,.*% *,,'` ; break]) +done + FLAGS="$ac_save_[]FLAGS" + AC_LANG_RESTORE +]) +case ".$VAR" in + .ok|.ok,*) m4_ifvaln($3,$3) ;; + .|.no|.no,*) m4_ifvaln($4,$4,[m4_ifval($2,[ + AC_RUN_LOG([: m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $2"]) + m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $2"])]) ;; + *) m4_ifvaln($3,$3,[ + if echo " $[]m4_ifval($1,$1,FLAGS) " | grep " $VAR " 2>&1 >/dev/null + then AC_RUN_LOG([: m4_ifval($1,$1,FLAGS) does contain $VAR]) + else AC_RUN_LOG([: m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $VAR"]) + m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $VAR" + fi ]) ;; +esac +AS_VAR_POPDEF([VAR])dnl +AS_VAR_POPDEF([FLAGS])dnl +]) + +dnl implementation tactics: +dnl the for-argument contains a list of options. The first part of +dnl these does only exist to detect the compiler - usually it is +dnl a global option to enable -ansi or -extrawarnings. All other +dnl compilers will fail about it. That was needed since a lot of +dnl compilers will give false positives for some option-syntax +dnl like -Woption or -Xoption as they think of it is a pass-through +dnl to later compile stages or something. The "%" is used as a +dnl delimimiter. A non-option comment can be given after "%%" marks +dnl which will be shown but not added to the respective C/CXXFLAGS. diff --git a/m4/pkg.m4 b/m4/pkg.m4 new file mode 100644 index 0000000..bfe1dd1 --- /dev/null +++ b/m4/pkg.m4 @@ -0,0 +1,158 @@ +# pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- +# +# Copyright © 2004 Scott James Remnant <scott@netsplit.com>. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program 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 this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# PKG_PROG_PKG_CONFIG([MIN-VERSION]) +# ---------------------------------- +AC_DEFUN([PKG_PROG_PKG_CONFIG], +[m4_pattern_forbid([^_?PKG_[A-Z_]+$]) +m4_pattern_allow([^PKG_CONFIG(_PATH)?$]) +AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility])dnl +if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then + AC_PATH_TOOL([PKG_CONFIG], [pkg-config]) +fi +if test -n "$PKG_CONFIG"; then + _pkg_min_version=m4_default([$1], [0.9.0]) + AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version]) + if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + PKG_CONFIG="" + fi + +fi[]dnl +])# PKG_PROG_PKG_CONFIG + +# PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +# +# Check to see whether a particular set of modules exists. Similar +# to PKG_CHECK_MODULES(), but does not set variables or print errors. +# +# +# Similar to PKG_CHECK_MODULES, make sure that the first instance of +# this or PKG_CHECK_MODULES is called, or make sure to call +# PKG_CHECK_EXISTS manually +# -------------------------------------------------------------- +AC_DEFUN([PKG_CHECK_EXISTS], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl +if test -n "$PKG_CONFIG" && \ + AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then + m4_ifval([$2], [$2], [:]) +m4_ifvaln([$3], [else + $3])dnl +fi]) + + +# _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES]) +# --------------------------------------------- +m4_define([_PKG_CONFIG], +[if test -n "$PKG_CONFIG"; then + if test -n "$$1"; then + pkg_cv_[]$1="$$1" + else + PKG_CHECK_EXISTS([$3], + [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null`], + [pkg_failed=yes]) + fi +else + pkg_failed=untried +fi[]dnl +])# _PKG_CONFIG + +# _PKG_SHORT_ERRORS_SUPPORTED +# ----------------------------- +AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG]) +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no +fi[]dnl +])# _PKG_SHORT_ERRORS_SUPPORTED + + +# PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], +# [ACTION-IF-NOT-FOUND]) +# +# +# Note that if there is a possibility the first call to +# PKG_CHECK_MODULES might not happen, you should be sure to include an +# explicit call to PKG_PROG_PKG_CONFIG in your configure.ac +# +# +# -------------------------------------------------------------- +AC_DEFUN([PKG_CHECK_MODULES], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl +AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl +AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl + +pkg_failed=no +AC_MSG_CHECKING([for $2]) + +_PKG_CONFIG([$1][_CFLAGS], [cflags], [$2]) +_PKG_CONFIG([$1][_LIBS], [libs], [$2]) + +m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS +and $1[]_LIBS to avoid the need to call pkg-config. +See the pkg-config man page for more details.]) + +if test $pkg_failed = yes; then + _PKG_SHORT_ERRORS_SUPPORTED + if test $_pkg_short_errors_supported = yes; then + $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --errors-to-stdout --print-errors "$2"` + else + $1[]_PKG_ERRORS=`$PKG_CONFIG --errors-to-stdout --print-errors "$2"` + fi + # Put the nasty error message in config.log where it belongs + echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD + + ifelse([$4], , [AC_MSG_ERROR(dnl +[Package requirements ($2) were not met: + +$$1_PKG_ERRORS + +Consider adjusting the PKG_CONFIG_PATH environment variable if you +installed software in a non-standard prefix. + +_PKG_TEXT +])], + [$4]) + AC_MSG_RESULT([no]) +elif test $pkg_failed = untried; then + ifelse([$4], , [AC_MSG_FAILURE(dnl +[The pkg-config script could not be found or is too old. Make sure it +is in your PATH or set the PKG_CONFIG environment variable to the full +path to pkg-config. + +_PKG_TEXT + +To get pkg-config, see <http://www.freedesktop.org/software/pkgconfig>.])], + [$4]) + AC_MSG_RESULT([no]) +else + $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS + $1[]_LIBS=$pkg_cv_[]$1[]_LIBS + AC_MSG_RESULT([yes]) + ifelse([$3], , :, [$3]) +fi[]dnl +])# PKG_CHECK_MODULES diff --git a/src/.gitignore b/src/.gitignore index c1753e9..0e69789 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -1,2 +1 @@ /viewtorrents -*.o
\ No newline at end of file diff --git a/src/Makefile b/src/Makefile deleted file mode 100644 index 022a873..0000000 --- a/src/Makefile +++ /dev/null @@ -1,30 +0,0 @@ -CC=gcc -CFLAGS=-Wall -Wextra -DDEBUG -DHAVE_CONFIG_H -g -I.. -DDATAROOTDIR='"/sw/share/"' -GTK_CFLAGS=`pkg-config gtk+-2.0 --cflags` -XMLRPC_CFLAGS=`xmlrpc-c-config client --cflags` -LDFLAGS= -GTK_LIBS=`pkg-config gtk+-2.0 --libs` -XMLRPC_LIBS=`xmlrpc-c-config client --libs` - -all: viewtorrents - -clean: - rm -f *.o viewtorrents - -viewtorrents: main.o customcellrendererstate.o customcellrendererprogress.o customcellrendererrate.o customcellrendererleft.o - $(CC) $(CFLAGS) $(GTK_CFLAGS) $(XMLRPC_CFLAGS) -o $@ $^ $(LDFLAGS) $(GTK_LIBS) $(XMLRPC_LIBS) - -main.o: main.c customcellrendererstate.h customcellrendererprogress.h customcellrendererrate.h customcellrendererleft.h common.h - $(CC) -c $(CFLAGS) $(GTK_CFLAGS) $(XMLRPC_CFLAGS) -o $@ $< - -customcellrendererstate.o: customcellrendererstate.c customcellrendererstate.h common.h - $(CC) -c $(CFLAGS) $(GTK_CFLAGS) -o $@ $< - -customcellrendererprogress.o: customcellrendererprogress.c customcellrendererprogress.h common.h - $(CC) -c $(CFLAGS) $(GTK_CFLAGS) -o $@ $< - -customcellrendererrate.o: customcellrendererrate.c customcellrendererrate.h common.h - $(CC) -c $(CFLAGS) $(GTK_CFLAGS) -o $@ $< - -customcellrendererleft.o: customcellrendererleft.c customcellrendererleft.h common.h - $(CC) -c $(CFLAGS) $(GTK_CFLAGS) -o $@ $< diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..e555a0b --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,17 @@ +MAINTAINERCLEANFILES = Makefile.in + +INCLUDES = -I$(top_srcdir) + +AM_CFLAGS = @DEFINES@ @GTK_CFLAGS@ @XMLRPC_C_CFLAGS@ -DDATAROOTDIR='"${pkgdatadir}"' + +bin_PROGRAMS = viewtorrents + +viewtorrents_SOURCES = main.c common.h \ + customcellrendererstate.c customcellrendererstate.h \ + customcellrendererprogress.c \ + customcellrendererprogress.h \ + customcellrendererrate.c customcellrendererrate.h \ + customcellrendererleft.c customcellrendererleft.h + +viewtorrents_LDADD = @GTK_LIBS@ @XMLRPC_C_LIBS@ + @@ -1,10 +1,12 @@ #include "common.h" #include <gtk/gtk.h> +#include <glib.h> #include <xmlrpc-c/base.h> #include <xmlrpc-c/client.h> #include <string.h> #include <errno.h> +#include <math.h> #include "customcellrendererstate.h" #include "customcellrendererprogress.h" @@ -226,13 +228,20 @@ static void status(master_t* master, const char* format, ...); static void noop_destroy(gpointer value); static void torrent_destroy(gpointer value); +static void liststore_sort_column_changed(GtkTreeSortable* sortable, + gpointer user_data); +static gint liststore_default_compare_func(GtkTreeModel* model, + GtkTreeIter* a, + GtkTreeIter* b, + gpointer user_data); + int main(int argc, char** argv) { const gchar* gladefile; GtkBuilder* builder; GError* error = NULL; guint ret; - GtkWidget* listview, * pwddlg; + GtkWidget* pwddlg; GtkCellRenderer* cell; GtkTreeViewColumn* column; GThread* worker; @@ -294,10 +303,15 @@ int main(int argc, char** argv) gtk_widget_set_sensitive(master.disconnectmenuitem, FALSE); pwddlg = GTK_WIDGET(gtk_builder_get_object(builder, "pwddlg")); + gtk_dialog_set_default_response(GTK_DIALOG(pwddlg), GTK_RESPONSE_OK); - listview = GTK_WIDGET(gtk_builder_get_object(builder, "treeview")); master.liststore = GTK_LIST_STORE(gtk_builder_get_object(builder, "liststore")); + g_signal_connect(master.liststore, "sort-column-changed", + G_CALLBACK(liststore_sort_column_changed), &master); + gtk_tree_sortable_set_default_sort_func(GTK_TREE_SORTABLE(master.liststore), + liststore_default_compare_func, + &master, NULL); cell = custom_cell_renderer_state_new(); column = GTK_TREE_VIEW_COLUMN(gtk_builder_get_object(builder, "statecolumn")); @@ -335,9 +349,30 @@ int main(int argc, char** argv) G_KEY_FILE_KEEP_TRANSLATIONS, NULL); + { + gint column = + g_key_file_get_integer(master.config, "view", "sort_column", NULL); + gboolean desc = + g_key_file_get_boolean(master.config, "view", "sort_desc", NULL); + if (column > 0) + { + gtk_tree_sortable_set_sort_column_id( + GTK_TREE_SORTABLE(master.liststore), column, + desc ? GTK_SORT_DESCENDING : GTK_SORT_ASCENDING); + } + else + { + gtk_tree_sortable_set_sort_column_id( + GTK_TREE_SORTABLE(master.liststore), + GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, GTK_SORT_ASCENDING); + } + } + gtk_widget_show_all(master.top); master.connectdlg = GTK_WIDGET(gtk_builder_get_object(builder, "connectdlg")); + gtk_dialog_set_default_response(GTK_DIALOG(master.connectdlg), + GTK_RESPONSE_OK); master.connectdlg_url = GTK_WIDGET(gtk_builder_get_object(builder, "connectdlg_url")); master.connectdlg_user = GTK_WIDGET(gtk_builder_get_object(builder, "connectdlg_user")); master.connectdlg_pwd = GTK_WIDGET(gtk_builder_get_object(builder, "connectdlg_pwd")); @@ -646,7 +681,7 @@ void do_connect(GtkMenuItem* menuitem, gpointer data) pass = NULL; } - gtk_widget_set_sensitive(master->connectmenuitem, FALSE); + gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE); status(master, "Connecting to %s...", url); g_key_file_set_string(master->config, "connect", "url", url); if (user != NULL) @@ -677,7 +712,7 @@ void do_disconnect(GtkMenuItem* menuitem, gpointer data) g_source_remove(master->sync_timeout); master->sync_timeout = 0; } - gtk_widget_set_sensitive(master->disconnectmenuitem, FALSE); + gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE); status(master, "Disconnecting..."); g_hash_table_remove_all(master->torrents); g_async_queue_push(master->queue, msg_disconnect()); @@ -718,7 +753,7 @@ void torrent_update(torrent_t* torrent, torrent_data_t* data) COLUMN_LEFT, data->left, COLUMN_PROGRESSSORT, data->downloaded < 100.0f ? data->downloaded : - data->seeded + 1000.0f, + 1000.0f + data->seeded, -1); } @@ -1008,14 +1043,14 @@ gpointer worker_main(gpointer _data) break; } done = (guint64)tmpi64; - if (torrent_data.size > 0) + if (done >= torrent_data.size) + { + torrent_data.downloaded = 100.0f; + } + else if (torrent_data.size > 0) { torrent_data.downloaded = ((gfloat)done * 100.0f) / (gfloat)torrent_data.size; - if (torrent_data.downloaded > 100.0f) - { - torrent_data.downloaded = 100.0f; - } } else { @@ -1380,7 +1415,7 @@ void hashlist_free(hashlist_t* hlist) g_free(hlist->mark); } -gboolean hashlist_resize(hashlist_t* hlist, gsize* a, gsize* r) +gboolean hashlist_resize(hashlist_t* hlist, G_GNUC_UNUSED gsize* a, gsize* r) { gsize ns = hlist->size * 2; gchar** tmp1; @@ -1530,7 +1565,7 @@ gboolean hashlist_sync(hashlist_t* hlist, xmlrpc_env * const env, return TRUE; } -void noop_destroy(gpointer key) +void noop_destroy(G_GNUC_UNUSED gpointer key) { } @@ -1614,3 +1649,99 @@ gboolean get_bool_xmlrpc(xmlrpc_env* env, xmlrpc_value* value) } } } + +void liststore_sort_column_changed(GtkTreeSortable* sortable, + gpointer user_data) +{ + master_t* master = user_data; + gint column; + GtkSortType order; + if (gtk_tree_sortable_get_sort_column_id(sortable, &column, &order)) + { + g_key_file_set_integer(master->config, "view", "sort_column", + column + 1); + g_key_file_set_boolean(master->config, "view", "sort_desc", + order == GTK_SORT_DESCENDING); + } + else + { + if (!g_key_file_remove_key(master->config, + "view", "sort_column", NULL) && + !g_key_file_remove_key(master->config, "view", "sort_desc", NULL)) + { + return; + } + } + save_config(master); +} + +#ifndef G_VALUE_INIT +# define G_VALUE_INIT { 0 } +#endif + +gint liststore_default_compare_func(GtkTreeModel* model, + GtkTreeIter* a, + GtkTreeIter* b, + G_GNUC_UNUSED gpointer user_data) +{ + GValue value = G_VALUE_INIT; + gfloat af, bf; + gint ai, bi; + const gchar* as, * bs; + gtk_tree_model_get_value(model, a, COLUMN_DOWN, &value); + af = g_value_get_float(&value); + g_value_unset(&value); + gtk_tree_model_get_value(model, b, COLUMN_DOWN, &value); + bf = g_value_get_float(&value); + g_value_unset(&value); + if (fabs(af - bf) > 1e-4) + { + return af > bf ? -1 : 1; + } + + gtk_tree_model_get_value(model, a, COLUMN_UP, &value); + af = g_value_get_float(&value); + g_value_unset(&value); + gtk_tree_model_get_value(model, b, COLUMN_UP, &value); + bf = g_value_get_float(&value); + g_value_unset(&value); + if (fabs(af - bf) > 1e-4) + { + return af > bf ? -1 : 1; + } + + gtk_tree_model_get_value(model, a, COLUMN_STATE, &value); + ai = g_value_get_int(&value); + g_value_unset(&value); + gtk_tree_model_get_value(model, b, COLUMN_STATE, &value); + bi = g_value_get_int(&value); + g_value_unset(&value); + if (ai != bi) + { + return ai > bi ? -1 : 1; + } + + gtk_tree_model_get_value(model, a, COLUMN_PROGRESSSORT, &value); + af = g_value_get_float(&value); + g_value_unset(&value); + gtk_tree_model_get_value(model, b, COLUMN_PROGRESSSORT, &value); + bf = g_value_get_float(&value); + g_value_unset(&value); + if (fabs(af - bf) > 1e-3) + { + return af < bf ? -1 : 1; + } + + gtk_tree_model_get_value(model, a, COLUMN_TITLE, &value); + as = g_value_get_string(&value); + { + GValue value2 = G_VALUE_INIT; + gint ret; + gtk_tree_model_get_value(model, b, COLUMN_TITLE, &value2); + bs = g_value_get_string(&value2); + ret = strcmp(as, bs); + g_value_unset(&value); + g_value_unset(&value2); + return ret; + } +} diff --git a/test/.gitignore b/test/.gitignore new file mode 100644 index 0000000..7146b2b --- /dev/null +++ b/test/.gitignore @@ -0,0 +1,3 @@ +/test-suite.log +/test_hashlist +/test_hashlist.log diff --git a/test/Makefile.am b/test/Makefile.am new file mode 100644 index 0000000..2e6b3c4 --- /dev/null +++ b/test/Makefile.am @@ -0,0 +1,13 @@ +MAINTAINERCLEANFILES = Makefile.in + +INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/src + +AM_CFLAGS = @DEFINES@ @GTK_CFLAGS@ + +TESTS = test_hashlist + +check_PROGRAMS = $(TESTS) + +test_hashlist_SOURCES = test_hashlist.c +test_hashlist_LDADD = @GTK_LIBS@ + diff --git a/test/test_hashlist.c b/test/test_hashlist.c new file mode 100644 index 0000000..a0dbbf0 --- /dev/null +++ b/test/test_hashlist.c @@ -0,0 +1,284 @@ +#include "common.h" + +#include <glib.h> +#include <stdio.h> +#include <string.h> + +typedef struct _hashlist_t +{ + gchar** data; + gboolean* mark; + gsize size, fill; + + const gchar** added; + const gchar** removed; +} hashlist_t; + +static void hashlist_init(hashlist_t* hlist); +static void hashlist_clear(hashlist_t* hlist); +static void hashlist_free(hashlist_t* hlist); +static gboolean hashlist_sync(hashlist_t* hlist, + const gchar** list, gsize count); + +static gboolean check(const gchar* name, hashlist_t* hlist, ...); + +#define CHECK(name, args...) \ + do { \ + ++tot; ok += check(name, &list, args) ? 1 : 0; \ + } while (0) + +int main(int argc, char** argv) +{ + static const gchar* data[] = { "foo", "bar", "meh", "muh" }; + int tot = 0, ok = 0; + hashlist_t list; + + hashlist_init(&list); + + hashlist_sync(&list, data, 0); + CHECK("nop sync", NULL, NULL); + + hashlist_sync(&list, data, 2); + CHECK("add foo,bar", "foo", "bar", NULL, NULL); + + hashlist_sync(&list, data, 4); + CHECK("add meh,muh", "meh", "muh", NULL, NULL); + + hashlist_sync(&list, data + 2, 2); + CHECK("remove foo,bar", NULL, "foo", "bar", NULL); + + hashlist_sync(&list, data, 4); + CHECK("add foo,bar", "foo", "bar", NULL, NULL); + + hashlist_sync(&list, data, 3); + CHECK("remove muh", NULL, "muh", NULL); + + hashlist_sync(&list, data + 1, 1); + CHECK("remove foo,meh", NULL, "meh", "foo", NULL); + + hashlist_sync(&list, data + 2, 2); + CHECK("remove bar, add meh,muh", "meh", "muh", NULL, "bar", NULL); + + hashlist_clear(&list); + CHECK("clear", NULL, NULL); + + hashlist_free(&list); + + fprintf(stdout, "OK %d/%d\n", ok, tot); + return EXIT_SUCCESS; +} + +void hashlist_init(hashlist_t* hlist) +{ + hlist->fill = 0; + hlist->size = 10; + hlist->data = g_new0(gchar*, hlist->size); + hlist->mark = g_new0(gboolean, hlist->size); + hlist->added = (const gchar**)hlist->data; + hlist->removed = (const gchar**)hlist->data + hlist->size - 1; +} + +void hashlist_clear(hashlist_t* hlist) +{ + gsize i, a, r; + for (a = hlist->added - (const gchar**)hlist->data; hlist->data[a]; ++a); + r = hlist->removed - (const gchar**)hlist->data; + for (i = 0; i < a; ++i) + { + g_free(hlist->data[i]); + } + for (i = r; hlist->data[i]; ++i) + { + g_free(hlist->data[i]); + } + hlist->data[0] = NULL; + hlist->fill = 0; + hlist->added = (const gchar**)hlist->data; + hlist->removed = (const gchar**)hlist->data + hlist->size - 1; +} + +void hashlist_free(hashlist_t* hlist) +{ + hashlist_clear(hlist); + g_free(hlist->data); + g_free(hlist->mark); +} + +gboolean hashlist_resize(hashlist_t* hlist, gsize* a, gsize* r) +{ + gsize ns = hlist->size * 2; + gchar** tmp; + g_assert(*r == hlist->size - 1); + if (ns < 10) ns = 10; + tmp = realloc(hlist->data, ns * sizeof(gchar**)); + if (tmp == NULL) + { + hlist->added = (const gchar**)hlist->data + hlist->fill; + hlist->removed = (const gchar**)hlist->data + *r; + return FALSE; + } + memset(tmp + hlist->size, 0, (ns - hlist->size) * sizeof(gchar*)); + hlist->data = tmp; + hlist->size = ns; + *r = hlist->size - 1; + return TRUE; +} + +gboolean hashlist_sync(hashlist_t* hlist, const gchar** list, gsize count) +{ + gsize i, j, a, r; + gsize found = 0; + g_assert((const gchar**)hlist->data + hlist->fill == hlist->added); + for (a = hlist->added - (const gchar**)hlist->data; hlist->data[a]; ++a); + hlist->fill = a; + hlist->added = (const gchar**)hlist->data + a; + for (r = hlist->removed - (const gchar**)hlist->data; hlist->data[r]; ++r) + { + g_free(hlist->data[r]); + } + g_assert(r == hlist->size - 1); + if (a + 5 > r) + { + gsize ns = a + 10; + gchar** tmp = realloc(hlist->data, ns * sizeof(gchar*)); + if (tmp != NULL) + { + hlist->data = tmp; + memset(hlist->data + hlist->size, 0, + (ns - hlist->size) * sizeof(gchar*)); + hlist->size = ns; + r = hlist->size - 1; + } + } + memset(hlist->mark, 0, hlist->fill * sizeof(gboolean)); + for (i = 0; i < count; ++i) + { + const gchar* hash = list[i]; + if (found == hlist->fill) + { + if (a + 1 == r && + !hashlist_resize(hlist, &a, &r)) + { + return FALSE; + } + hlist->data[a++] = g_strdup(hash); + continue; + } + /* Quick path */ + if (i < hlist->fill && !hlist->mark[i] && + strcmp(hlist->data[i], hash) == 0) + { + hlist->mark[i] = TRUE; + found++; + continue; + } + for (j = i + 1; j != i; ++j) + { + if (j >= hlist->fill) + { + j = 0; + if (i == 0) + { + break; + } + } + if (hlist->mark[j]) + { + continue; + } + if (strcmp(hlist->data[j], hash) == 0) + { + break; + } + } + if (j != i) + { + hlist->mark[j] = TRUE; + found++; + } + else + { + /* New */ + if (a + 1 == r && + !hashlist_resize(hlist, &a, &r)) + { + return FALSE; + } + hlist->data[a++] = g_strdup(hash); + } + } + if (found < hlist->fill) + { + gsize n = hlist->fill - found; + for (j = hlist->fill - 1; n > 0; --j) + { + if (!hlist->mark[j]) + { + hlist->data[--r] = hlist->data[j]; + a--; + hlist->fill--; + memmove(hlist->data + j, hlist->data + j + 1, + (a - j) * sizeof(gchar*)); + hlist->data[a] = NULL; + n--; + } + } + } + hlist->added = (const gchar**)hlist->data + hlist->fill; + hlist->removed = (const gchar**)hlist->data + r; + return TRUE; +} + +gboolean check(const gchar* name, hashlist_t* hlist, ...) +{ + va_list args; + const gchar* x, **a, **r; + va_start(args, hlist); + a = hlist->added; + while ((x = va_arg(args, const gchar*)) != NULL) + { + if (*a == NULL) + { + fprintf(stderr, "%s: Expected added `%s` got EOL\n", + name, x); + return FALSE; + } + if (strcmp(*a, x) != 0) + { + fprintf(stderr, "%s: Expected added `%s` got `%s`\n", name, x, *a); + return FALSE; + } + ++a; + } + if (*a != NULL) + { + fprintf(stderr, "%s: Expected added EOL got `%s`\n", + name, *a); + return FALSE; + } + r = hlist->removed; + while ((x = va_arg(args, const gchar*)) != NULL) + { + if (*r == NULL) + { + fprintf(stderr, "%s: Expected removed `%s` got EOL\n", + name, x); + return FALSE; + } + if (strcmp(*r, x) != 0) + { + fprintf(stderr, "%s: Expected removed `%s` got `%s`\n", + name, x, *r); + return FALSE; + } + ++r; + } + va_end(args); + if (*r != NULL) + { + fprintf(stderr, "%s: Expected removed EOL got `%s`\n", + name, *r); + return FALSE; + } + return TRUE; +} |
