$ git clone http://tcclient.ion.nu/tc_client.git
commit 3b7097016d8ebf06f896852d071e8306b127bbae
Author: Alicia <...>
Date:   Tue Apr 7 06:49:01 2015 +0200

    Version 0.17

diff --git a/ChangeLog b/ChangeLog
index dd44c65..81e809c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+0.17:
+irchack: translate tc_client's "No such nick" to IRC's 401 (No such nick/channel)
+modbot: mark manually started videos as playing.
+modbot: fixed a memory error in list_del()
+Added a configure script to better handle platform differences (primarily that some systems need to link to libiconv, for others it is provided by libc)
+crossbuild.sh: build libiconv if necessary.
+modbot: added more responses to !approve to make it less confusing.
+modbot: pass -- to youtube-dl before the ID to avoid issues when IDs start with -
 0.16:
 Get and print the channel topic (contributed by Jade)
 irchack: fork and keep accepting connections.
diff --git a/Makefile b/Makefile
index 1a130b8..2e357fe 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,9 @@
-VERSION=0.16
+VERSION=0.17
 CFLAGS=-g3 -Wall $(shell curl-config --cflags)
 LIBS=-g3 $(shell curl-config --libs)
+ifneq ($(wildcard config.mk),)
+  include config.mk
+endif
 
 tc_client: client.o amfparser.o rtmp.o numlist.o amfwriter.o idlist.o colors.o endian.o
  $(CC) $(LDFLAGS) $^ $(LIBS) -o $@
@@ -11,4 +14,4 @@ clean:
  rm -f client.o amfparser.o rtmp.o numlist.o amfwriter.o idlist.o colors.o endian.o tc_client irchack modbot
 
 tarball:
- tar -cJf tc_client-$(VERSION).tar.xz --transform='s|^|tc_client-$(VERSION)/|' Makefile client.c amfparser.c rtmp.c numlist.c amfwriter.c idlist.c colors.c endian.c amfparser.h rtmp.h numlist.h amfwriter.h idlist.h colors.h endian.h LICENSE README ChangeLog crossbuild.sh irchack.c modbot.c
+ tar -cJf tc_client-$(VERSION).tar.xz --transform='s|^|tc_client-$(VERSION)/|' Makefile client.c amfparser.c rtmp.c numlist.c amfwriter.c idlist.c colors.c endian.c amfparser.h rtmp.h numlist.h amfwriter.h idlist.h colors.h endian.h LICENSE README ChangeLog crossbuild.sh irchack.c modbot.c configure
diff --git a/client.c b/client.c
index bd724fb..a8a25a5 100644
--- a/client.c
+++ b/client.c
@@ -339,8 +339,15 @@ int main(int argc, char** argv)
     {
       pfd[0].revents=0;
       unsigned char buf[2048];
-      unsigned int len=read(0, buf, 2047);
-      if(len<1){break;}
+      unsigned int len=0;
+      int r;
+      while(len<2047)
+      {
+        if((r=read(0, &buf[len], 1))!=1 || buf[len]=='\r' || buf[len]=='\n'){break;}
+        ++len;
+      }
+      if(r<1){break;}
+      if(len<1){continue;}
       while(len>0 && (buf[len-1]=='\n'||buf[len-1]=='\r')){--len;}
       if(!len){continue;} // Don't send empty lines
       buf[len]=0;
@@ -404,7 +411,8 @@ int main(int argc, char** argv)
           privfield=idlist_get((char*)&buf[5]);
           if(privfield<0)
           {
-            printf("No such nick\n");
+            buf[5+privlen]=0;
+            printf("No such nick: %s\n", &buf[5]);
             fflush(stdout);
             continue;
           }
diff --git a/configure b/configure
index 6d37dbf..d45512a 100755
--- a/configure
+++ b/configure
@@ -38,144 +38,4 @@ else
 fi
 rm -f iconvtest iconvtest.c
 
-printf 'Checking for gtk+-3.0... '
-libs="`pkg-config --libs gtk+-3.0 2> /dev/null`"
-if [ "x$libs" != "x" ]; then
-  echo "GTK_LIBS=${libs}" >> config.mk
-  echo "GTK_CFLAGS=`pkg-config --cflags gtk+-3.0`" >> config.mk
-  echo yes
-else
-  echo no
-  printf 'Checking for gtk+-2.0... '
-  libs="`pkg-config --libs gtk+-2.0 2> /dev/null`"
-  if [ "x$libs" != "x" ]; then
-    echo "GTK_LIBS=${libs}" >> config.mk
-    echo "GTK_CFLAGS=`pkg-config --cflags gtk+-2.0`" >> config.mk
-    echo yes
-  else
-    echo no
-  fi
-fi
-
-printf 'Checking for libavcodec... '
-libs="`pkg-config --libs libavcodec 2> /dev/null`"
-if [ "x$libs" != "x" ]; then
-  echo "AVCODEC_LIBS=${libs}" >> config.mk
-  echo "AVCODEC_CFLAGS=`pkg-config --cflags libavcodec`" >> config.mk
-  echo yes
-else
-  echo no
-fi
-
-printf 'Checking for libswscale... '
-libs="`pkg-config --libs libswscale 2> /dev/null`"
-if [ "x$libs" != "x" ]; then
-  echo "SWSCALE_LIBS=${libs}" >> config.mk
-  echo "SWSCALE_CFLAGS=`pkg-config --cflags libswscale`" >> config.mk
-  echo yes
-else
-  echo no
-fi
-
-printf 'Checking for libavutil... '
-libs="`pkg-config --libs libavutil 2> /dev/null`"
-if [ "x$libs" != "x" ]; then
-  echo "AVUTIL_LIBS=${libs}" >> config.mk
-  echo "AVUTIL_CFLAGS=`pkg-config --cflags libavutil`" >> config.mk
-  echo yes
-else
-  echo no
-fi
-
-# TODO: Figure out how to mix sound sources so that this doesn't sound horrible with more than one person on mic, having it disabled by default until then
-if [ "x${ENABLE_MIC}" != "x" ]; then
-printf 'Checking for libavresample... '
-libs="`pkg-config --libs libavresample 2> /dev/null`"
-if [ "x$libs" != "x" ]; then
-  echo "AVRESAMPLE_LIBS=${libs}" >> config.mk
-  echo "AVRESAMPLE_CFLAGS=`pkg-config --cflags libavresample`" >> config.mk
-  echo yes
-else
-  echo no
-  printf 'Checking for libswresample... '
-  libs="`pkg-config --libs libswresample 2> /dev/null`"
-  if [ "x$libs" != "x" ]; then
-    echo "SWRESAMPLE_LIBS=${libs}" >> config.mk
-    echo "SWRESAMPLE_CFLAGS=`pkg-config --cflags libswresample`" >> config.mk
-    echo yes
-  else
-    echo no
-  fi
-fi
-
-printf 'Checking for libao... '
-libs="`pkg-config --libs ao 2> /dev/null`"
-if [ "x$libs" != "x" ]; then
-  echo "AO_LIBS=${libs}" >> config.mk
-  echo "AO_CFLAGS=`pkg-config --cflags ao`" >> config.mk
-  echo yes
-else
-  echo no
-fi
-fi
-
-printf 'Checking for ncurses... '
-libs="`ncursesw5-config --libs 2> /dev/null || ncurses5-config --libs 2> /dev/null`"
-if [ "x$libs" != "x" ]; then
-  echo "CURSES_LIBS=${libs}" >> config.mk
-  echo "CURSES_CFLAGS=`ncursesw5-config --cflags 2> /dev/null || ncurses5-config --cflags 2> /dev/null`" >> config.mk
-  echo yes
-else
-  echo no
-fi
-
-printf 'Checking for readline... '
-echo '#include <stdio.h>' > readlinetest.c
-echo '#include <readline/readline.h>' >> readlinetest.c
-echo 'int main(){rl_initialize();return 0;}' >> readlinetest.c
-if "$CC" readlinetest.c -lreadline ${libs} -o readlinetest > /dev/null 2> /dev/null; then
-  echo "READLINE_LIBS=-lreadline" >> config.mk
-  echo yes
-else
-  echo no
-fi
-rm -f readlinetest.c readlinetest
-
-printf 'Checking for libv4l2... '
-libs="`pkg-config --libs libv4l2 2> /dev/null`"
-if [ "x$libs" != "x" ]; then
-  echo "LIBV4L2_LIBS=${libs}" >> config.mk
-  echo "LIBV4L2_CFLAGS=`pkg-config --cflags libv4l2`" >> config.mk
-  echo yes
-else
-  echo no
-fi
-
-# TODO: handle crosscompiling better
-printf 'Checking if endianness macros work... '
-echo '#include <stdio.h>' > endiantest.c
-echo 'int main(){' >> endiantest.c
-echo '#if defined(__ORDER_LITTLE_ENDIAN__) && defined(__ORDER_BIG_ENDIAN__) && defined(__BYTE_ORDER__)' >> endiantest.c
-echo '  if(__ORDER_LITTLE_ENDIAN__==__ORDER_BIG_ENDIAN__){return 1;}' >> endiantest.c
-echo '  if(__BYTE_ORDER__!=__ORDER_LITTLE_ENDIAN__ && __BYTE_ORDER__!=__ORDER_BIG_ENDIAN__){return 1;}' >> endiantest.c
-echo '  return 0;' >> endiantest.c
-echo '#else' >> endiantest.c
-echo '  return 1;' >> endiantest.c
-echo '#endif' >> endiantest.c
-echo '}' >> endiantest.c
-"$CC" endiantest.c -o endiantest > /dev/null 2> /dev/null
-if ./endiantest 2> /dev/null; then
-  echo yes
-else
-  echo no
-  printf 'Checking endianness and writing our own macros... '
-  echo '#include <stdio.h>' > endiantest.c
-  echo 'int main(){int num=1;char* x=(char*)&num;if(x[0]){printf("CFLAGS+=-D__ORDER_LITTLE_ENDIAN__=1 -D__ORDER_BIG_ENDIAN__=2 -D__BYTE_ORDER__=1");}else{printf("CFLAGS+=-D__ORDER_LITTLE_ENDIAN__=1 -D__ORDER_BIG_ENDIAN__=2 -D__BYTE_ORDER__=2");}return 0;}' >> endiantest.c
-  "$CC" endiantest.c -o endiantest > /dev/null 2> /dev/null
-  ./endiantest >> config.mk
-  echo >> config.mk
-  echo done
-fi
-rm -f endiantest.c endiantest
-
 echo Done
diff --git a/crossbuild.sh b/crossbuild.sh
index 13c3d48..481b401 100755
--- a/crossbuild.sh
+++ b/crossbuild.sh
@@ -5,6 +5,7 @@ if [ "$host" = "" ]; then
   if [ "$host" = "" ]; then host='i386-pc-linux-gnu'; fi
   echo "No target host specified (argv[1]), defaulting to ${host}"
 fi
+./configure --host="$host"
 here="`pwd`"
 if [ ! -e curlprefix ]; then
   wget -c http://curl.haxx.se/download/curl-7.40.0.tar.bz2
@@ -17,14 +18,19 @@ if [ ! -e curlprefix ]; then
   make install
   cd "$here"
 fi
-export PATH="${here}/curlprefix/bin:${PATH}"
-# Select a crosscompiler (if we can find one)
-if which "${host}-gcc" > /dev/null 2> /dev/null && [ "`which "${host}-gcc" 2> /dev/null`" != "" ]; then
-  export CC="${host}-gcc"
-elif which "${host}-clang" > /dev/null 2> /dev/null && [ "`which "${host}-clang" 2> /dev/null`" != "" ]; then
-  export CC="${host}-clang"
-elif which "${host}-cc" > /dev/null 2> /dev/null && [ "`which "${host}-cc" 2> /dev/null`" != "" ]; then
-  export CC="${host}-cc"
+if grep -q 'LIBS+=-liconv' config.mk && [ ! -e iconvprefix ]; then
+  wget -c http://ftp.gnu.org/gnu/libiconv/libiconv-1.14.tar.gz
+  tar -xzf libiconv-1.14.tar.gz
+  cd libiconv-1.14
+  mkdir -p build
+  cd build
+  ../configure --prefix="${here}/iconvprefix" --host="`echo "$host" | sed -e 's/android/gnu/'`" --enable-static --disable-shared CC="${host}-gcc" # libiconv does not handle android well, so we pretend it's GNU and specify the compiler
+  make
+  make install
+  cd "$here"
 fi
+export PATH="${here}/curlprefix/bin:${PATH}"
+echo "CFLAGS+=-I${here}/iconvprefix/include" >> config.mk
+echo "LDFLAGS+=-L${here}/iconvprefix/lib" >> config.mk
 make
 make utils
diff --git a/irchack.c b/irchack.c
index 26a67b9..dca0f14 100644
--- a/irchack.c
+++ b/irchack.c
@@ -277,6 +277,11 @@ printf("Got from tc_client: '%s'\n", buf);
         dprintf(sock, ":%s!user@host NICK :guest-%s\n", nick, &buf[10]);
         continue;
       }
+      if(!strncmp(buf, "No such nick: ", 14))
+      {
+        dprintf(sock, ":irchack 401 %s %s :No such nick/channel\n", nick, &buf[14]);
+        continue;
+      }
       char* space=strchr(buf, ' ');
       if(space && !strcmp(space, " is a moderator."))
       {
diff --git a/modbot.c b/modbot.c
index d375590..36d5ddf 100644
--- a/modbot.c
+++ b/modbot.c
@@ -47,7 +47,7 @@ void list_del(struct list* list, const char* item)
     {
       free(list->items[i]);
       --list->itemcount;
-      memmove(&list->items[i], &list->items[i+1], sizeof(char*)*list->itemcount);
+      memmove(&list->items[i], &list->items[i+1], sizeof(char*)*(list->itemcount-i));
     }
   }
 }
@@ -165,9 +165,8 @@ unsigned int getduration(const char* vid)
   {
     close(out[0]);
     dup2(out[1], 1);
-    close(2); // Ignore youtube-dl errors/warnings
     write(1, ":", 1);
-    execlp("youtube-dl", "youtube-dl", "--get-duration", vid, (char*)0);
+    execlp("youtube-dl", "youtube-dl", "--get-duration", "--", vid, (char*)0);
     perror("execlp(youtube-dl)");
     _exit(1);
   }
@@ -175,7 +174,7 @@ unsigned int getduration(const char* vid)
   close(out[1]);
   char timebuf[128];
   int len=read(out[0], timebuf, 127);
-  if(len<1){return 60;} // If using youtube-dl fails, assume all videos are 1 minute long
+  if(len<2){printf("Failed to get video duration using youtube-dl, assuming 60s\n"); return 60;} // If using youtube-dl fails, assume all videos are 1 minute long
   timebuf[len]=0;
   close(out[0]);
   // youtube-dl prints it out in hh:mm:ss format, convert it to plain seconds
@@ -317,6 +316,7 @@ int main(int argc, char** argv)
           if((pos=list_getpos(&queue, vid))>-1)
           {
             say(pm, "That video is already in queue (number %i)\n", pos);
+            continue;
           }
           if(list_contains(&mods, nick))
           {
@@ -399,24 +399,28 @@ int main(int argc, char** argv)
           {
             if(playing)
             {
+              if(list_contains(&goodvids, playing) && !list_contains(&badvids, playing)){say(pm, "'%s' is already approved, use !approve <ID> to approve another video\n", playing); continue;}
               list_add(&goodvids, playing);
               list_del(&badvids, playing);
               list_save(&goodvids, "goodvids.txt");
               list_save(&badvids, "badvids.txt");
             }else if(queue.itemcount>0){
+              if(list_contains(&goodvids, queue.items[0]) && !list_contains(&badvids, queue.items[0])){say(pm, "'%s' is already approved, use !approve <ID> to approve another video\n", queue.items[0]); continue;}
               list_add(&goodvids, queue.items[0]);
               list_del(&badvids, queue.items[0]);
               list_save(&goodvids, "goodvids.txt");
               list_save(&badvids, "badvids.txt");
               playnext(0);
-            }else{write(tc_client, "Approve what? please specify a video\n", 37);}
+            }else{say(pm, "Approve what? please specify a video\n");}
           }
           else if(!strncmp(msg, "!approve ", 9))
           {
             char* vid=getyoutube(&msg[9]);
             if(!vid[0]){vid=playing; if(!vid){continue;}}
             list_add(&goodvids, vid);
+            list_del(&badvids, vid);
             list_save(&goodvids, "goodvids.txt");
+            list_save(&badvids, "badvids.txt");
             if(!playing && queue.itemcount>0 && !strcmp(vid, queue.items[0])){playnext(0);} // Next in queue just got approved, so play it
           }
           else if(!strcmp(msg, "!badvid") || !strncmp(msg, "!badvid ", 8))
@@ -439,6 +443,8 @@ int main(int argc, char** argv)
             list_del(&queue, vid);
             list_add(&goodvids, vid);
             list_save(&goodvids, "goodvids.txt");
+            free(playing);
+            playing=strdup(vid);
             alarm(getduration(vid));
           }
           else if(!strcmp(msg, "/mbc youTube")){playnext(0);} // TODO: handle /mbsk (seek) too?