#!/usr/bin/env python
"""%prog <toolroot>
Build iftop packages for openwrt.  We build two packages:
   iftop -- iftop with the normal behavior, including pressing "!" on
      the keyboard giving you a subshell.
   iftop-bangless -- iftop with the "!" command disabled, so it might
      be safe let networking-ignorant people use it as a login shell.

The build is done in the current directory.  Temporary files, downloaded source
code, and the resulting packages are left in the current directory.
Subdirectories of the current directory may be overwritten during the build.

toolroot should be a directory that is the tool root.  It should have
subdirectories named usr, lib, include, and bin.  The bin subdirectory should
have a C compiler named mipsel-linux-uclibc-gcc.  This is easy to achieve by 
using the binary toolchain from 
"http://downloads.openwrt.org/nbd/gcc34/OpenWrt-SDK-Linux-i686-1.tar.bz2".
"""

# This script is released in to the public domain by Tim Freeman,
# tim@fungible.com, on 6 June 2005.

# The current version of iftop.  If this isn't 0.16, you'll have to touch the
# config.h text below.
version = "0.16"

# Increment buildnumber each time this script is run.
buildnumber = "3"

# You shouldn't have to change below here.
fullversion = version + "_" + buildnumber

import md5
import shutil
import os
from glob import glob
import sys

def write_file(name, data):
    f = file(name, "wb")
    try:
        f.write(data)
    finally:
        f.close()

def read_file(name):
    f = file(name, "rb")
    try:
        return f.read()
    finally:
        f.close()

def safe_system(cmd):
    """Do the given system command, printing messages and exiting appropriately
    on errors."""
    print cmd
    status = os.system(cmd)
    if status != 0:
        raise Exception("Got status %s when attempting to do the command %s" \
                        % (status, cmd))

def remove(path):
    """Delete the given pathname, even if it's a directory structure."""
    try:
        shutil.rmtree(path)
    except:
        if os.path.exists(path):
            raise
    if os.path.exists(path):
        raise Exception("Couldn't delete %s" % (path,))

def hash(file):
    m = md5.new()
    m.update(read_file(file))
    return m.hexdigest()

sourcefile = None
toolroot = None

def buildone(name, cflags, moretext):
    global toolroot
    remove("iftop-" + version)
    safe_system("tar xzf %s" % (sourcefile,))
    startdir = os.getcwd()
    os.chdir("iftop-%s" % (version,))
    write_file("patchfile", patch)
    safe_system("patch < patchfile")
    global config_h
    write_file("config.h", config_h)
    cc = toolroot+"/bin/mipsel-linux-uclibc-gcc"
    # No makefile.  The Makefile generated by config wants .deps to be
    # generated and I couldn't see how to do that.  We don't need incremental
    # builds anyway. 
    d = {"tr": toolroot}
    for sf in glob("*.c"):
        safe_system(cc + " -c " + sf + \
                    " -I%(tr)s/usr/include " % d + cflags)
    safe_system(cc + " " + " ".join(glob("*.o")) + " -o iftop " + \
                "-L%(tr)s/lib -L%(tr)s/usr/lib -lpcap -lm -lcurses -lpthread"
                " -s" % d)
    os.chdir(startdir)
    remove("package")
    os.makedirs("package/CONTROL")
    controltext = ("Package: " + name + "\n"
                   "Version: " + fullversion + "\n"
                   "Architecture: mipsel\n"
                   "Maintainer: Tim Freeman <tim@fungible.com>\n"
                   "Section: net\n"
                   "Priority: optional\n"
                   "Depends: libpcap, libncurses, libpthread\n"
                   "Description: Curses-based program for showing which\n"
                   " hosts and ports use the most network bandwidth.\n"
                   + moretext + 
                   "Source: http://www.ex-parrot.com/~pdw/iftop/download/iftop-0.16.tar.gz\n")
    write_file("package/CONTROL/control", controltext)
    shutil.copy(__file__, "package/CONTROL/build_iftop.py")
    os.makedirs("package/usr/bin")
    shutil.copy("iftop-"+version+"/iftop", "package/usr/bin/" + name)
    safe_system(toolroot+"/usr/bin/ipkg-build -c -o root -g root package")
    shutil.copy("package/CONTROL/control", "iftop-Packages")
    ipk_name = name + "_" + fullversion + "_mipsel.ipk"
    return (controltext +
            "Filename: "+ipk_name+"\n"
            "MD5Sum: "+hash(ipk_name)+"\n"
            "Size: "+str(os.stat(ipk_name).st_size)+"\n\n")

def main():
    if len(sys.argv) != 2:
        sys.stderr.write(__doc__)
        sys.exit(1)
    global toolroot
    toolroot = sys.argv[1]
    global sourcefile
    sourcefile ="iftop-%s.tar.gz" % (version,) 
    if not os.path.exists(sourcefile):
        sourceurl = "http://www.ex-parrot.com/~pdw/iftop/download/" + \
                    sourcefile
        safe_system("wget %s" % (sourceurl,))
    packages_file = "iftop-Packages"
    write_file(packages_file, buildone("iftop", "", "") +
               buildone(
        "iftop-bangless", "-DNO_SYSTEM",
        " This version has the \"!\" command disabled so you can't start\n"
        " subshells.  This is good if you want to use it as a login shell.\n"
        " Remember to add it to /etc/shells if you do this.\n"))
    print "You should insert the text in " + packages_file + \
          " into your Packages file."

# The following config.h has been manually hacked because the "configure"
# script for iftop doesn't support cross-compilation well.
config_h = """
/* config.h.  Generated by configure.  */
/* config.h.in.  Generated from configure.in by autoheader.  */

/* 7-argument gethostbyaddr_r returns struct hostent* */
/* #undef GETHOSTBYADDR_R_RETURNSHOSTENT_P */

/* 8-argument gethostbyaddr_r returns int */
#define GETHOSTBYADDR_R_RETURNS_INT 1

/* C99 fixed-width int types available */
#define HAVE_C99_INTS 1

/* Are we running on a STREAMS system with DLPI? */
/* #undef HAVE_DLPI */

/* Define to 1 if you have the `inet_aton' function. */
#define HAVE_INET_ATON 1

/* Define to 1 if you have the `inet_pton' function. */
#define HAVE_INET_PTON 1

/* Define to 1 if you have the <inttypes.h> header file. */
#define HAVE_INTTYPES_H 1

/* Define to 1 if you have the `nsl' library (-lnsl). */
/* #undef HAVE_LIBNSL */

/* Define to 1 if you have the `pcap' library (-lpcap). */
#define HAVE_LIBPCAP 1

/* Define to 1 if you have the <memory.h> header file. */
#define HAVE_MEMORY_H 1

/* Define to 1 if you have the <pcap.h> header file. */
#define HAVE_PCAP_H 1

/* Define to 1 if you have the <pcap/pcap.h> header file. */
/* #undef HAVE_PCAP_PCAP_H */

/* Define to 1 if you have the `regcomp' function. */
#define HAVE_REGCOMP 1

/* Define to 1 if you have the `select' function. */
#define HAVE_SELECT 1

/* Define to 1 if you have the <stdint.h> header file. */
#define HAVE_STDINT_H 1

/* Define to 1 if you have the <stdlib.h> header file. */
#define HAVE_STDLIB_H 1

/* Define to 1 if you have the `strdup' function. */
#define HAVE_STRDUP 1

/* Define to 1 if you have the `strerror' function. */
#define HAVE_STRERROR 1

/* Define to 1 if you have the <strings.h> header file. */
#define HAVE_STRINGS_H 1

/* Define to 1 if you have the <string.h> header file. */
#define HAVE_STRING_H 1

/* Define to 1 if you have the `strspn' function. */
#define HAVE_STRSPN 1

/* Define to 1 if you have the <sys/inttypes.h> header file. */
/* #undef HAVE_SYS_INTTYPES_H */

/* Define to 1 if you have the <sys/ioctl.h> header file. */
#define HAVE_SYS_IOCTL_H 1

/* Define to 1 if you have the <sys/sockio.h> header file. */
/* #undef HAVE_SYS_SOCKIO_H */

/* Define to 1 if you have the <sys/stat.h> header file. */
#define HAVE_SYS_STAT_H 1

/* Define to 1 if you have the <sys/time.h> header file. */
#define HAVE_SYS_TIME_H 1

/* Define to 1 if you have the <sys/types.h> header file. */
#define HAVE_SYS_TYPES_H 1

/* Define to 1 if you have the <unistd.h> header file. */
#define HAVE_UNISTD_H 1

/* The iftop version number */
#define IFTOP_VERSION \""""+version+"""\"

/* Enable default promiscuous mode to capture outgoing packets */
/* #undef NEED_PROMISCUOUS_FOR_OUTGOING */

/* Name of package */
#define PACKAGE "iftop"

/* Define to the address where bug reports for this package should be sent. */
#define PACKAGE_BUGREPORT ""

/* Define to the full name of this package. */
#define PACKAGE_NAME ""

/* Define to the full name and version of this package. */
#define PACKAGE_STRING ""

/* Define to the one symbol short name of this package. */
#define PACKAGE_TARNAME ""

/* Define to the version of this package. */
#define PACKAGE_VERSION ""

/* The size of a `unsigned int', as computed by sizeof. */
/* #undef SIZEOF_UNSIGNED_INT */

/* The size of a `unsigned long int', as computed by sizeof. */
/* #undef SIZEOF_UNSIGNED_LONG_INT */

/* The size of a `unsigned short int', as computed by sizeof. */
/* #undef SIZEOF_UNSIGNED_SHORT_INT */

/* size of u_int16_t */
#define SIZEOF_U_INT16_T 0

/* size of u_int32_t */
#define SIZEOF_U_INT32_T 0

/* size of u_int8_t */
#define SIZEOF_U_INT8_T 0

/* Define to 1 if you have the ANSI C header files. */
#define STDC_HEADERS 1

/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
#define TIME_WITH_SYS_TIME 1

/* use ARES for name resolution */
/* #undef USE_ARES */

/* use a REALLY SUCKY forking resolver for name resolution */
/* #undef USE_FORKING_RESOLVER */

/* use gethostbyaddr for name resolution */
/* #define USE_GETHOSTBYADDR 1*/

/* use gethostbyaddr_r for name resolution */
#define USE_GETHOSTBYADDR_R 1

/* use getnameinfo for name resolution */
/* #undef USE_GETNAMEINFO */

/* Version number of package */
#define VERSION \""""+version+"""\"

/* Define to empty if `const' does not conform to ANSI C. */
/* #undef const */

/* Define to `unsigned' if <sys/types.h> does not define. */
/* #undef size_t */
"""

patch = """
--- ui.c.orig	2004-02-05 14:53:19.000000000 -0800
+++ ui.c	2005-06-04 11:03:06.000000000 -0700
@@ -1045,6 +1045,7 @@
                 break;
             }
             case '!': {
+#ifndef NO_SYSTEM
                 char *s;
                 dontshowdisplay = 1;
                 if ((s = edline(0, "Command", "")) && s[strspn(s, " \t")]) {
@@ -1073,6 +1074,9 @@
                     xfree(s);
                 }
                 dontshowdisplay = 0;
+#else
+                showhelp("Sorry, subshells have been disabled.");
+#endif
                 break;
             }
             case 'T':
"""

main()
    
