Merge branch 'gregoa' of colleen:bti into gregoa
authorgregor herrmann <gregoa@debian.org>
Sun, 19 Dec 2010 12:44:56 +0000 (13:44 +0100)
committergregor herrmann <gregoa@debian.org>
Sun, 19 Dec 2010 12:44:56 +0000 (13:44 +0100)
13 files changed:
ChangeLog
Makefile.am
RELEASE-NOTES
bti-bashcompletion
bti-shrink-urls
bti-shrink-urls.xml
bti.1
bti.c
bti.example
bti.xml
configure.ac
oath.keys [new file with mode: 0644]
scripts/checkpatch.pl

index 88a68ab4c89cd1297425b649260385090648108f..ef2183ac89f5df84cf4ce8274961698a3952397f 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,70 @@
+Summary of changes from v027 to v028
+============================================
+
+Diego Elio 'Flameeyes' Pettenò (7):
+      build system: use consistently pkg-config to check for dependencies
+      build system: use AC_SEARCH_LIBS rather than assuming -ldl is needed.
+      Remove autoscan-generated bad tests.
+      Use arrays rather than pointers for static strings.
+      Typo fix.
+      Split --background from --bash, and make the latter imply the former.
+      Collapse more fprintf() calls.
+
+Greg Kroah-Hartman (7):
+      Fixup typo in Makefile.am found by @applehq
+      new version of checkpatch.pl from 2.6.35-rc1 kernel
+      whitespace cleanups for bti.c
+      more formatting cleanups
+      long line cleanups
+      Update my copyright date.
+      Reduce the number of calls to fprintf.
+
+
+Summary of changes from v026 to v027
+============================================
+
+Amir Mohammad Saied (4):
+      OAuth for both twitter, and identi.ca
+      HTTP basic auth for StatusNet instances
+      Leftovers from last commit, accepting account and, password from options
+      - Cosmetics     - Escaping tweet before POSTing it
+
+Daniel Cordero (4):
+      Verbose mode shows status IDs
+      Support for replying to notices
+      Add a replyto configuration option
+      Fix a warning: unused variable message
+
+Greg Kroah-Hartman (5):
+      add oath.keys from twitter.com
+      Merge branch 'master' into oath
+      error out if liboauth is not present
+      Merge branch 'master' into oath-test
+      fix up the autoconf checking for liboauth and other libraries
+
+Michel Alexandre Salim (1):
+      Fix Implicit linking against -ldl
+
+
+Summary of changes from v025 to v026
+============================================
+
+Greg Kroah-Hartman (7):
+      minor coding style cleanup.
+      updated version of checkpatch.pl
+      another minor codingstyle fix
+      update bti.1 from the xml changes
+      init the readline function after parsing the command line options
+      add a configfile option to the core data structure
+      Add ability to override default config file option
+
+Pete Zaitcev (2):
+      bti: suppress prompt if fed from a pipe
+      bti: fix a crash for echo -n|bti
+
+Reuben Thomas (1):
+      Typo fixes for bti(1)
+
 Summary of changes from v024 to v025
 ============================================
 
index 1e8f12ff73af7eb79b0a372f87ad56f7c831c88e..7b5953e07384a2e0740e3f77c0d0d399ad0f0c8f 100644 (file)
@@ -7,6 +7,18 @@ bin_SCRIPTS = \
 bti_SOURCES = \
        bti.c
 
+bti_CFLAGS = \
+       $(LIBCURL_CFLAGS) \
+       $(XML_CFLAGS) \
+       $(LIBPCRE_CFLAGS) \
+       $(LIBOAUTH_CFLAGS)
+
+bti_LDADD = \
+       $(LIBCURL_LIBS) \
+       $(XML_LIBS) \
+       $(LIBPCRE_LIBS) \
+       $(LIBOAUTH_LIBS)
+
 dist_man_MANS = \
        bti.1 \
        bti-shrink-urls.1
index 8d405ce2eef68c3d4f1c64fd8c6a50b5a165158b..ef981638f94f0c0c505cb1818539dc20481a71a2 100644 (file)
@@ -1,3 +1,16 @@
+bti 028
+=============
+Build fixes thanks to Flameeyes and other minor tweaks.
+
+bti 027
+=============
+OAUTH support
+
+bti 026
+=============
+added ability to specify config file from command line
+some readline fixups from Pete
+
 bti 025
 =============
 remove readline link-time dependancy to make the distro's lives easier
index ff5d9a28921cf4814ec1b33e6dc59a2dc63c921b..e627ae21c19195aa04b563a196b797d0476a74e8 100644 (file)
@@ -5,10 +5,10 @@ _bti()
        cur="${COMP_WORDS[COMP_CWORD]}"
        prev="${COMP_WORDS[COMP_CWORD-1]}"
        if [[ "${cur}" == -* ]] ; then
-               COMPREPLY=( $(compgen -W "-a -A -p -P -H -b -d -v -s -n -g -h
+               COMPREPLY=( $(compgen -W "-a -A -p -P -H -b -d -v -s -n -g -h -r
                        --account --action --password --proxy --host --bash \
                        --user --debug --dry-run --shrink-urls --page --version --verbose \
-                       --help" -- ${cur}) )
+                       --help --replyto --retweet" -- ${cur}) )
        fi
 
        if [[ "${prev}" == "--host" ]] ; then
index aa9eb57db7f94a16a8f2bd9035b9112c2dac5848..36a45de35843b97b118c16c2003825416a0939ff 100755 (executable)
@@ -16,6 +16,9 @@
 
 needs_escape=true
 
+shrink_host=2tu.us
+test -f ~/.bti && . ~/.bti
+
 while test -n "$1" ; do
         word="$1"
         shift
@@ -29,7 +32,7 @@ bti-shrink-urls - convert URLs to a shorter form using a web service
 
     $0 [--escaped] [<url>]
 
-Currently only http://2tu.us/ is supported.
+Currently supported: 2tu.us (default), bit.ly, j.mp.
 END
                 exit 0
                 ;;
@@ -69,10 +72,25 @@ function convert_url() {
                                         -e 's/]/%5D/g')
         fi
 
-        # http://2tu.us/
-        local submit="http://2tu.us/?save=y&url=$url"
+        case $shrink_host in
+        2tu.us)
+            local submit="http://2tu.us/?save=y&url=$url"
+            local res=$(wget -q -O - "$submit" | awk -F"'" '/Your tight URL is:/ { print $2 }')
+            ;;
+        bit.ly|j.mp)
+            if [ -z "$shrink_bitly_login" -o -z "$shrink_bitly_key" ]; then
+                echo "To use $shrink_host you must set 'shrink_bitly_login' and 'shrink_bitly_key' in ~/.bti" >&2
+                exit 1
+            fi
+            local submit="http://api.bit.ly/v3/shorten?format=txt&login=$shrink_bitly_login&apiKey=$shrink_bitly_key&domain=$shrink_host&longUrl=$url"
+            local res=$(wget -q -O - "$submit")
+            ;;
+        *)
+            echo "Shrinking with $shrink_host is not supported." >&2
+            exit 1
+            ;;
+        esac
 
-        local res=$(wget -q -O - "$submit" | awk -F"'" '/Your tight URL is:/ { print $2 }')
         if test "${res%%:*}" = 'http' -a "${#res}" -lt "$urllen" ; then
                 echo $res
                 return 0
index 491743bec4f1831c09aceafa0b2e81c0b363aae3..9a7c77042483478a270db1473d8627c32a92a4e3 100644 (file)
@@ -37,7 +37,7 @@
          bti-shrink-urls converts URLs to a shorter form using a web service.
         </para>
         <para>
-          Currently only http://2tu.us/ is supported.
+          Currently http://2tu.us/ (default) and http://bit.ly / http://j.mp are supported.
         </para>
       </refsect1>
 
         </variablelist>
       </refsect1>
 
+      <refsect1><title>CONFIGURATION</title>
+        <para>
+         bti-shrink-urls is configured by setting some values in ~/.bti:
+        </para>
+        <variablelist>
+          <varlistentry>
+            <term><option>shrink_host</option></term>
+            <listitem>
+              <para>
+               Possible values: 2tu.us (default), bit.ly, j.mp
+             </para>
+            </listitem>
+          </varlistentry>
+          <varlistentry>
+            <term><option>shrink_bitly_login</option></term>
+            <listitem>
+              <para>
+               API login for bit.ly, j.mp, required if shrink_host is set to  bit.ly or j.mp.
+               See https://code.google.com/p/bitly-api/wiki/ApiDocumentation
+              </para>
+            </listitem>
+          </varlistentry>
+          <varlistentry>
+            <term><option>shrink_bitly_key</option></term>
+            <listitem>
+              <para>
+               API key for bit.ly, j.mp, required if shrink_host is set to  bit.ly or j.mp.
+               See https://code.google.com/p/bitly-api/wiki/ApiDocumentation
+              </para>
+            </listitem>
+          </varlistentry>
+        </variablelist>
+      </refsect1>
+
        <refsect1><title>AUTHOR</title>
          <para>Written by Bart Trojanowski <email>bart@jukie.net</email>.</para>
        </refsect1>
diff --git a/bti.1 b/bti.1
index 86546860584e333760893a637966140d7997f4b2..778d58de851cba2082f527a159d15864015b9ec1 100644 (file)
--- a/bti.1
+++ b/bti.1
@@ -1,6 +1,7 @@
+'\" t
 .\"     Title: bti
 .\"    Author: [see the "AUTHOR" section]
-.\" Generator: DocBook XSL Stylesheets v1.74.0 <http://docbook.sf.net/>
+.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
 .\"      Date: May 2008
 .\"    Manual: bti
 .\"    Source: bti
@@ -8,155 +9,6 @@
 .\"
 .TH "BTI" "1" "May 2008" "bti" "bti"
 .\" -----------------------------------------------------------------
-.\" * (re)Define some macros
-.\" -----------------------------------------------------------------
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.\" toupper - uppercase a string (locale-aware)
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.de toupper
-.tr aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ
-\\$*
-.tr aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz
-..
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.\" SH-xref - format a cross-reference to an SH section
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.de SH-xref
-.ie n \{\
-.\}
-.toupper \\$*
-.el \{\
-\\$*
-.\}
-..
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.\" SH - level-one heading that works better for non-TTY output
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.de1 SH
-.\" put an extra blank line of space above the head in non-TTY output
-.if t \{\
-.sp 1
-.\}
-.sp \\n[PD]u
-.nr an-level 1
-.set-an-margin
-.nr an-prevailing-indent \\n[IN]
-.fi
-.in \\n[an-margin]u
-.ti 0
-.HTML-TAG ".NH \\n[an-level]"
-.it 1 an-trap
-.nr an-no-space-flag 1
-.nr an-break-flag 1
-\." make the size of the head bigger
-.ps +3
-.ft B
-.ne (2v + 1u)
-.ie n \{\
-.\" if n (TTY output), use uppercase
-.toupper \\$*
-.\}
-.el \{\
-.nr an-break-flag 0
-.\" if not n (not TTY), use normal case (not uppercase)
-\\$1
-.in \\n[an-margin]u
-.ti 0
-.\" if not n (not TTY), put a border/line under subheading
-.sp -.6
-\l'\n(.lu'
-.\}
-..
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.\" SS - level-two heading that works better for non-TTY output
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.de1 SS
-.sp \\n[PD]u
-.nr an-level 1
-.set-an-margin
-.nr an-prevailing-indent \\n[IN]
-.fi
-.in \\n[IN]u
-.ti \\n[SN]u
-.it 1 an-trap
-.nr an-no-space-flag 1
-.nr an-break-flag 1
-.ps \\n[PS-SS]u
-\." make the size of the head bigger
-.ps +2
-.ft B
-.ne (2v + 1u)
-.if \\n[.$] \&\\$*
-..
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.\" BB/BE - put background/screen (filled box) around block of text
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.de BB
-.if t \{\
-.sp -.5
-.br
-.in +2n
-.ll -2n
-.gcolor red
-.di BX
-.\}
-..
-.de EB
-.if t \{\
-.if "\\$2"adjust-for-leading-newline" \{\
-.sp -1
-.\}
-.br
-.di
-.in
-.ll
-.gcolor
-.nr BW \\n(.lu-\\n(.i
-.nr BH \\n(dn+.5v
-.ne \\n(BHu+.5v
-.ie "\\$2"adjust-for-leading-newline" \{\
-\M[\\$1]\h'1n'\v'+.5v'\D'P \\n(BWu 0 0 \\n(BHu -\\n(BWu 0 0 -\\n(BHu'\M[]
-.\}
-.el \{\
-\M[\\$1]\h'1n'\v'-.5v'\D'P \\n(BWu 0 0 \\n(BHu -\\n(BWu 0 0 -\\n(BHu'\M[]
-.\}
-.in 0
-.sp -.5v
-.nf
-.BX
-.in
-.sp .5v
-.fi
-.\}
-..
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.\" BM/EM - put colored marker in margin next to block of text
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.de BM
-.if t \{\
-.br
-.ll -2n
-.gcolor red
-.di BX
-.\}
-..
-.de EM
-.if t \{\
-.br
-.di
-.ll
-.gcolor
-.nr BH \\n(dn
-.ne \\n(BHu
-\M[\\$1]\D'P -.75n 0 0 \\n(BHu -(\\n[.i]u - \\n(INu - .75n) 0 0 -\\n(BHu'\M[]
-.in 0
-.nf
-.BX
-.in
-.fi
-.\}
-..
-.\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
 .\" disable hyphenation
 .\" -----------------------------------------------------------------
 .\" * MAIN CONTENT STARTS HERE *
 .\" -----------------------------------------------------------------
-.SH "Name"
-bti \- send a tweet to twitter\&.com or identi\&.ca (or any custom laconi\&.ca installation) from the command line
-.SH "Synopsis"
-.fam C
+.SH "NAME"
+bti \- send a tweet to twitter\&.com or identi\&.ca from the command line
+.SH "SYNOPSIS"
 .HP \w'\fBbti\fR\ 'u
-\fBbti\fR [\fB\-\-account\ account\fR] [\fB\-\-password\ password\fR] [\fB\-\-action\ action\fR] [\fB\-\-user\ screenname\fR] [\fB\-\-host\ HOST_NAME\fR] [\fB\-\-proxy\ PROXY:PORT\fR] [\fB\-\-logfile\ LOGFILE\fR] [\fB\-\-page\ PAGENUMBER\fR] [\fB\-\-bash\fR] [\fB\-\-shrink\-urls\fR] [\fB\-\-debug\fR] [\fB\-\-dry\-run\fR]  [\fB\-\-verbose\fR] [\fB\-\-version\fR] [\fB\-\-help\fR]
-.fam
+\fBbti\fR [\fB\-\-account\ account\fR] [\fB\-\-password\ password\fR] [\fB\-\-action\ action\fR] [\fB\-\-user\ screenname\fR] [\fB\-\-host\ HOST_NAME\fR] [\fB\-\-proxy\ PROXY:PORT\fR] [\fB\-\-logfile\ LOGFILE\fR] [\fB\-\-config\ CONFIGFILE\fR] [\fB\-\-replyto\ ID\fR] [\fB\-\-page\ PAGENUMBER\fR] [\fB\-\-bash\fR] [\fB\-\-shrink\-urls\fR] [\fB\-\-debug\fR] [\fB\-\-dry\-run\fR] [\fB\-\-verbose\fR] [\fB\-\-version\fR] [\fB\-\-help\fR]
 .SH "DESCRIPTION"
 .PP
 bti sends a tweet message to twitter\&.com or identi\&.ca\&.
@@ -190,21 +40,17 @@ Specify the password of your twitter\&.com or identi\&.ca account\&.
 .PP
 \fB\-\-action action\fR
 .RS 4
-Specify the action which you want to perform\&. Valid options are "update" to send a message, "friends" to see your friends timeline, "public" to track public timeline, "replies" to see replies to your messages and "user" to see a specific user\'s timeline\&.
+Specify the action which you want to perform\&. Valid options are "update" to send a message, "friends" to see your friends timeline, "public" to track public timeline, "replies" to see replies to your messages and "user" to see a specific user\'s timeline\&. Default is "update"\&.
 .RE
 .PP
 \fB\-\-user screenname\fR
 .RS 4
-Specify the user you want to see his/her messages while the action is "user"\&.
-.sp
-If no screenname is specified, the default is account name\&.
+Specify the user whose messages you want to see when the action is "user"\&.
 .RE
 .PP
 \fB\-\-host HOST_NAME\fR
 .RS 4
-Specify the host which you want to send your message to\&. Valid options are "twitter" to send to twitter\&.com and "identica" to send to identi\&.ca\&.
-.sp
-If you want to send the message to a custom laconi\&.ca installation, you should specify the API URI\&. For example identi\&.ca\'s URI is: https://identi\&.ca/api/statuses\&.
+Specify the host which you want to send your message to\&. Valid options are "twitter" to send to twitter\&.com and "identica" to send to identi\&.ca\&. If you want to send the message to a custom laconi\&.ca installation, you should specify the API URI\&. For example identi\&.ca\'s URI is: https://identi\&.ca/api/statuses
 .sp
 If no host is specified, the default is to send to twitter\&.com\&.
 .RE
@@ -221,6 +67,20 @@ If \-\-proxy is not specified but the environment variable \'http_proxy\' is set
 Specify a logfile for bti to write status messages to\&. LOGFILE is in relation to the user\'s home directory, not an absolute path to a file\&.
 .RE
 .PP
+\fB\-\-config CONFIGFILE\fR
+.RS 4
+Specify a config file for bti to read from\&. By default, bti looks in the ~/\&.bti file for config values\&. This default location can be overridden by setting a specific file with this option\&.
+.RE
+.PP
+\fB\-\-replyto ID\fR
+.RS 4
+Status ID of a single post to which you want to create a threaded reply to\&.
+.sp
+For twitter, this is ignored unless the message starts with the @name of the owner of the post with the status ID\&.
+.sp
+For status\&.net, this can link any two messages into context with each other\&. Status\&.net will also link a message that contains an @name without this without regard to context\&.
+.RE
+.PP
 \fB\-\-shrink\-urls\fR
 .RS 4
 Scans the tweet text for valid URL patterns and passes each through the supplied bti\-shrink\-urls script\&. The script will pass the URL to a web service that shrinks the URLs, making it more suitable for micro\-blogging\&.
@@ -245,14 +105,20 @@ Performs all steps that would normally be done for a given action, but will not
 .PP
 \fB\-\-verbose\fR
 .RS 4
-Verbose mode
+Verbose mode\&. Print status IDs and timestamps\&.
 .RE
 .PP
 \fB\-\-bash\fR
 .RS 4
 Add the working directory and a \'$\' in the tweet message to help specify it is coming from a command line\&. Don\'t put the working directory and the \'$\' in the tweet message\&.
 .sp
-This mode also does not report back any errors that might have happened when sending the message, and it sends it in the background, returning immediately, allowing the process to continue on\&.
+This option implies
+\fB\-\-background\fR\&.
+.RE
+.PP
+\fB\-\-background\fR
+.RS 4
+Do not report back any errors that might have happened when sending the message, and send it in the background, returning immediately, allowing the user to continue on\&.
 .RE
 .PP
 \fB\-\-version\fR
@@ -268,21 +134,20 @@ Print help text\&.
 .PP
 bti provides an easy way to send tweet messages direct from the command line or any script\&. It reads the message on standard input and uses the account and password settings either from the command line options, or from a config file, to send the message out\&.
 .PP
-It\'s primary focus is to allow you to log everything that you type into a bash shell, in a crazy, "this is what I\'m doing right now!" type of way, letting the world follow along with you constant moving between directories and refreshing your email queue to see if there\'s anything interesting going on\&.
+Its primary focus is to allow you to log everything that you type into a bash shell, in a crazy, "this is what I\'m doing right now!" type of way, letting the world follow along with you constant moving between directories and refreshing your email queue to see if there\'s anything interesting going on\&.
 .PP
 To hook bti up to your bash shell, export the following variable:
 .PP
 
-\FC PROMPT_COMMAND=\'history 1 | sed \-e "s/^\es*[0\-9]*\es*//" | bti \-\-bash\'\F[]
+PROMPT_COMMAND=\'history 1 | sed \-e "s/^\es*[0\-9]*\es*//" | bti \-\-bash\'
 .PP
 This example assumes that you have the
-\FC~/\&.bti\F[]
+~/\&.bti
 set up with your account and password information already in it, otherwise you can specify them as an option\&.
 .SH "CONFIGURATION"
 .PP
 The account and password can be stored in a configuration file in the users home directory in a file named
-\FC\&.bti\F[]
-The structure of this file is as follows:
+\&.bti\&. The structure of this file is as follows:
 .PP
 \fBaccount\fR
 .RS 4
@@ -294,21 +159,19 @@ The twitter\&.com or identi\&.ca account name you wish to use to send this messa
 The twitter\&.com or identi\&.ca password for the account you wish to use to send this message with\&.
 .RE
 .PP
-\fBaction action\fR
+\fB\-\-action action\fR
 .RS 4
 Specify the action which you want to perform\&. Valid options are "update" to send a message, "friends" to see your friends timeline, "public" to track public timeline, "replies" to see replies to your messages and "user" to see a specific user\'s timeline\&.
 .RE
 .PP
-\fBuser screenname\fR
+\fB\-\-user screenname\fR
 .RS 4
 Specify the user you want to see his/her messages while the action is "user"\&.
 .RE
 .PP
 \fBhost\fR
 .RS 4
-The host you want to use to send the message to\&. Valid options are either "twitter" or "identica" to send to twitter\&.com or identi\&.ca respectively\&.
-.sp
-If you want to send the message to a custom laconi\&.ca installation, you should specify the API URI\&. For example identi\&.ca\'s URI is: https://identi\&.ca/api/statuses\&.
+The host you want to use to send the message to\&. Valid options are either "twitter" or "identica" to send to twitter\&.com or identi\&.ca respectively\&. If you want to send the message to a custom laconi\&.ca installation, you should specify the API URI\&. For example identi\&.ca\'s URI is: https://identi\&.ca/api/statuses\&.
 .RE
 .PP
 \fBproxy\fR
@@ -321,6 +184,13 @@ The http proxy needed to send data out to the Internet\&.
 The logfile name for bti to write what happened to\&. This file is relative to the user\'s home directory\&. If this file is not specified here or on the command line, no logging will be written to the disk\&.
 .RE
 .PP
+\fBreplyto\fR
+.RS 4
+The status ID to which all notices will be linked to\&.
+.sp
+There is no sane reason for a need to have this set in a config file\&. One such reason is to have all your messages as children to a particular status\&.
+.RE
+.PP
 \fBshrink\-urls\fR
 .RS 4
 Setting this variable to \'true\' or \'yes\' will enable the URL shrinking feature\&. This is equivalent to using the \-\-shrink\-urls option\&.
@@ -328,11 +198,11 @@ Setting this variable to \'true\' or \'yes\' will enable the URL shrinking featu
 .PP
 \fBverbose\fR
 .RS 4
-Setting this variable to \'true\' or \'yes\' will enable the Verbose mode\&.
+Setting this variable to \'true\' or \'yes\' will enable the verbose mode\&.
 .RE
 .PP
 There is an example config file called
-\FCbti\&.example\F[]
+bti\&.example
 in the source tree that shows the structure of the file if you need an example to work off of\&.
 .PP
 Configuration options have the following priority:
@@ -349,9 +219,7 @@ config file option
 environment variables
 .RE
 .PP
-For example, command line options always override any config file option, or any environment variables\&.
+For example, command line options always override any config file option, or any environment variables\&. Unless a config file is specified by the command line\&. At that point, the new config file is read, and any previous options set by a command line option, would be overridden\&.
 .SH "AUTHOR"
 .PP
-Written by Greg Kroah\-Hartman
-<\FCgreg@kroah\&.com\F[]> and Amir Mohammad Saied
-<\FCamirsaied@gmail\&.com\F[]>\&.
+Written by Greg Kroah\-Hartman <greg@kroah\&.com> and Amir Mohammad Saied <amirsaied@gmail\&.com>\&.
diff --git a/bti.c b/bti.c
index 02b8aea5c0d8b231871ede345e68384b301f044f..0229a9a75fdf7a09fa0bc86a5fb177aa72909ae2 100644 (file)
--- a/bti.c
+++ b/bti.c
@@ -1,7 +1,7 @@
 /*
- * Copyright (C) 2008 Greg Kroah-Hartman <greg@kroah.com>
+ * Copyright (C) 2008-2010 Greg Kroah-Hartman <greg@kroah.com>
  * Copyright (C) 2009 Bart Trojanowski <bart@jukie.net>
- * Copyright (C) 2009 Amir Mohammad Saied <amirsaied@gmail.com>
+ * Copyright (C) 2009-2010 Amir Mohammad Saied <amirsaied@gmail.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
@@ -39,6 +39,7 @@
 #include <pcre.h>
 #include <termios.h>
 #include <dlfcn.h>
+#include <oauth.h>
 
 
 #define zalloc(size)   calloc(size, 1)
@@ -67,12 +68,17 @@ enum action {
        ACTION_REPLIES = 4,
        ACTION_PUBLIC  = 8,
        ACTION_GROUP   = 16,
-       ACTION_UNKNOWN = 32
+       ACTION_RETWEET = 32,
+       ACTION_UNKNOWN = 64
 };
 
 struct session {
        char *password;
        char *account;
+       char *consumer_key;
+       char *consumer_secret;
+       char *access_token_key;
+       char *access_token_secret;
        char *tweet;
        char *proxy;
        char *time;
@@ -82,11 +88,16 @@ struct session {
        char *group;
        char *hosturl;
        char *hostname;
+       char *configfile;
+       char *replyto;
+       char *retweet;
        int bash;
+       int background;
        int interactive;
        int shrink_urls;
        int dry_run;
        int page;
+       int no_oauth;
        enum host host;
        enum action action;
        void *readline_handle;
@@ -101,29 +112,32 @@ struct bti_curl_buffer {
 
 static void display_help(void)
 {
-       fprintf(stdout, "bti - send tweet to twitter or identi.ca\n");
-       fprintf(stdout, "Version: " VERSION "\n");
-       fprintf(stdout, "Usage:\n");
-       fprintf(stdout, "  bti [options]\n");
-       fprintf(stdout, "options are:\n");
-       fprintf(stdout, "  --account accountname\n");
-       fprintf(stdout, "  --password password\n");
-       fprintf(stdout, "  --action action\n");
-       fprintf(stdout, "    ('update', 'friends', 'public', 'replies', "
-               "'group' or 'user')\n");
-       fprintf(stdout, "  --user screenname\n");
-       fprintf(stdout, "  --group groupname\n");
-       fprintf(stdout, "  --proxy PROXY:PORT\n");
-       fprintf(stdout, "  --host HOST\n");
-       fprintf(stdout, "  --logfile logfile\n");
-       fprintf(stdout, "  --shrink-urls\n");
-       fprintf(stdout, "  --page PAGENUMBER\n");
-       fprintf(stdout, "  --bash\n");
-       fprintf(stdout, "  --debug\n");
-       fprintf(stdout, "  --verbose\n");
-       fprintf(stdout, "  --dry-run\n");
-       fprintf(stdout, "  --version\n");
-       fprintf(stdout, "  --help\n");
+       fprintf(stdout, "bti - send tweet to twitter or identi.ca\n"
+               "Version: %s\n"
+               "Usage:\n"
+               "  bti [options]\n"
+               "options are:\n"
+               "  --account accountname\n"
+               "  --password password\n"
+               "  --action action\n"
+               "    ('update', 'friends', 'public', 'replies', or 'user')\n"
+               "  --user screenname\n"
+               "  --group groupname\n"
+               "  --proxy PROXY:PORT\n"
+               "  --host HOST\n"
+               "  --logfile logfile\n"
+               "  --config configfile\n"
+               "  --replyto ID\n"
+               "  --retweet ID\n"
+               "  --shrink-urls\n"
+               "  --page PAGENUMBER\n"
+               "  --bash\n"
+               "  --background\n"
+               "  --debug\n"
+               "  --verbose\n"
+               "  --dry-run\n"
+               "  --version\n"
+               "  --help\n", VERSION);
 }
 
 static void display_version(void)
@@ -144,7 +158,8 @@ static char *get_string(const char *name)
        if (!fgets(string, 999, stdin))
                return NULL;
        temp = strchr(string, '\n');
-       *temp = '\0';
+       if (temp)
+               *temp = '\0';
        return string;
 }
 
@@ -189,7 +204,8 @@ static void session_readline_init(struct session *session)
                        *next++ = 0;
                if (*cp == 0)
                        continue;
-               if ((handle = dlopen(cp, RTLD_NOW))) {
+               handle = dlopen(cp, RTLD_NOW);
+               if (handle) {
                        dbg("Using %s for readline library\n", cp);
                        break;
                }
@@ -232,7 +248,6 @@ static struct session *session_alloc(void)
        session = zalloc(sizeof(*session));
        if (!session)
                return NULL;
-       session_readline_init(session);
        return session;
 }
 
@@ -240,9 +255,14 @@ static void session_free(struct session *session)
 {
        if (!session)
                return;
-       session_readline_cleanup(session);
+       free(session->retweet);
+       free(session->replyto);
        free(session->password);
        free(session->account);
+       free(session->consumer_key);
+       free(session->consumer_secret);
+       free(session->access_token_key);
+       free(session->access_token_secret);
        free(session->tweet);
        free(session->proxy);
        free(session->time);
@@ -251,6 +271,7 @@ static void session_free(struct session *session)
        free(session->group);
        free(session->hosturl);
        free(session->hostname);
+       free(session->configfile);
        free(session);
 }
 
@@ -282,17 +303,26 @@ static void bti_curl_buffer_free(struct bti_curl_buffer *buffer)
        free(buffer);
 }
 
-static const char *twitter_host  = "https://twitter.com/statuses";
-static const char *identica_host = "https://identi.ca/api/statuses";
-static const char *twitter_name  = "twitter";
-static const char *identica_name = "identi.ca";
-
-static const char *user_uri    = "/user_timeline/";
-static const char *update_uri  = "/update.xml";
-static const char *public_uri  = "/public_timeline.xml";
-static const char *friends_uri = "/friends_timeline.xml";
-static const char *replies_uri = "/replies.xml";
-static const char *group_uri = "/../laconica/groups/timeline/";
+static const char twitter_host[]  = "http://api.twitter.com/1/statuses";
+static const char identica_host[] = "https://identi.ca/api/statuses";
+static const char twitter_name[]  = "twitter";
+static const char identica_name[] = "identi.ca";
+
+static const char twitter_request_token_uri[]  = "http://twitter.com/oauth/request_token";
+static const char twitter_access_token_uri[]   = "http://twitter.com/oauth/access_token";
+static const char twitter_authorize_uri[]      = "http://twitter.com/oauth/authorize?oauth_token=";
+static const char identica_request_token_uri[] = "http://identi.ca/api/oauth/request_token?oauth_callback=oob";
+static const char identica_access_token_uri[]  = "http://identi.ca/api/oauth/access_token";
+static const char identica_authorize_uri[]     = "http://identi.ca/api/oauth/authorize?oauth_token=";
+
+static const char user_uri[]     = "/user_timeline/";
+static const char update_uri[]   = "/update.xml";
+static const char public_uri[]   = "/public_timeline.xml";
+static const char friends_uri[]  = "/friends_timeline.xml";
+static const char mentions_uri[] = "/mentions.xml";
+static const char replies_uri[]  = "/replies.xml";
+static const char retweet_uri[]  = "/retweet/";
+static const char group_uri[]    = "/../statusnet/groups/timeline/";
 
 static CURL *curl_init(void)
 {
@@ -314,6 +344,7 @@ static void parse_statuses(xmlDocPtr doc, xmlNodePtr current)
        xmlChar *text = NULL;
        xmlChar *user = NULL;
        xmlChar *created = NULL;
+       xmlChar *id = NULL;
        xmlNodePtr userinfo;
 
        current = current->xmlChildrenNode;
@@ -323,6 +354,8 @@ static void parse_statuses(xmlDocPtr doc, xmlNodePtr current)
                                created = xmlNodeListGetString(doc, current->xmlChildrenNode, 1);
                        if (!xmlStrcmp(current->name, (const xmlChar *)"text"))
                                text = xmlNodeListGetString(doc, current->xmlChildrenNode, 1);
+                       if (!xmlStrcmp(current->name, (const xmlChar *)"id"))
+                               id = xmlNodeListGetString(doc, current->xmlChildrenNode, 1);
                        if (!xmlStrcmp(current->name, (const xmlChar *)"user")) {
                                userinfo = current->xmlChildrenNode;
                                while (userinfo != NULL) {
@@ -335,19 +368,21 @@ static void parse_statuses(xmlDocPtr doc, xmlNodePtr current)
                                }
                        }
 
-                       if (user && text && created) {
+                       if (user && text && created && id) {
                                if (verbose)
-                                       printf("[%s] (%.16s) %s\n",
-                                               user, created, text);
+                                       printf("[%s] {%s} (%.16s) %s\n",
+                                               user, id, created, text);
                                else
                                        printf("[%s] %s\n",
                                                user, text);
                                xmlFree(user);
                                xmlFree(text);
                                xmlFree(created);
+                               xmlFree(id);
                                user = NULL;
                                text = NULL;
                                created = NULL;
+                               id = NULL;
                        }
                }
                current = current->next;
@@ -418,9 +453,125 @@ static size_t curl_callback(void *buffer, size_t size, size_t nmemb,
        return buffer_size;
 }
 
+static int parse_osp_reply(const char *reply, char **token, char **secret)
+{
+       int rc;
+       int retval = 1;
+       char **rv = NULL;
+       rc = oauth_split_url_parameters(reply, &rv);
+       qsort(rv, rc, sizeof(char *), oauth_cmpstringp);
+       if (rc == 2 || rc == 4) {
+               if (!strncmp(rv[0], "oauth_token=", 11) &&
+                   !strncmp(rv[1], "oauth_token_secret=", 18)) {
+                       if (token)
+                               *token = strdup(&(rv[0][12]));
+                       if (secret)
+                               *secret = strdup(&(rv[1][19]));
+
+                       retval = 0;
+               }
+       } else if (rc == 3) {
+               if (!strncmp(rv[1], "oauth_token=", 11) &&
+                   !strncmp(rv[2], "oauth_token_secret=", 18)) {
+                       if (token)
+                               *token = strdup(&(rv[1][12]));
+                       if (secret)
+                               *secret = strdup(&(rv[2][19]));
+
+                       retval = 0;
+               }
+       }
+
+       dbg("token: %s\n", *token);
+       dbg("secret: %s\n", *secret);
+
+       if (rv)
+               free(rv);
+
+       return retval;
+}
+
+static int request_access_token(struct session *session)
+{
+       char *post_params = NULL;
+       char *request_url = NULL;
+       char *reply       = NULL;
+       char *at_key      = NULL;
+       char *at_secret   = NULL;
+       char *verifier    = NULL;
+       char at_uri[90];
+
+       if (!session)
+               return -EINVAL;
+
+       if (session->host == HOST_TWITTER)
+               request_url = oauth_sign_url2(
+                               twitter_request_token_uri, NULL,
+                               OA_HMAC, NULL, session->consumer_key,
+                               session->consumer_secret, NULL, NULL);
+       else if (session->host == HOST_IDENTICA)
+               request_url = oauth_sign_url2(
+                               identica_request_token_uri, NULL,
+                               OA_HMAC, NULL, session->consumer_key,
+                               session->consumer_secret, NULL, NULL);
+       reply = oauth_http_get(request_url, post_params);
+
+       if (request_url)
+               free(request_url);
+
+       if (post_params)
+               free(post_params);
+
+       if (!reply)
+               return 1;
+
+       if (parse_osp_reply(reply, &at_key, &at_secret))
+               return 1;
+
+       free(reply);
+
+       fprintf(stdout,
+               "Please open the following link in your browser, and "
+               "allow 'bti' to access your account. Then paste "
+               "back the provided PIN in here.\n");
+       if (session->host == HOST_TWITTER) {
+               fprintf(stdout, "%s%s\nPIN: ", twitter_authorize_uri, at_key);
+               verifier = session->readline(NULL);
+               sprintf(at_uri, "%s?oauth_verifier=%s",
+                       twitter_access_token_uri, verifier);
+       } else if (session->host == HOST_IDENTICA) {
+               fprintf(stdout, "%s%s\nPIN: ", identica_authorize_uri, at_key);
+               verifier = session->readline(NULL);
+               sprintf(at_uri, "%s?oauth_verifier=%s",
+                       identica_access_token_uri, verifier);
+       }
+       request_url = oauth_sign_url2(at_uri, NULL, OA_HMAC, NULL,
+                                     session->consumer_key,
+                                     session->consumer_secret,
+                                     at_key, at_secret);
+       reply = oauth_http_get(request_url, post_params);
+
+       if (!reply)
+               return 1;
+
+       if (parse_osp_reply(reply, &at_key, &at_secret))
+               return 1;
+
+       free(reply);
+
+       fprintf(stdout,
+               "Please put these two lines in your bti "
+               "configuration file (~/.bti):\n"
+               "access_token_key=%s\n"
+               "access_token_secret=%s\n",
+               at_key, at_secret);
+
+       return 0;
+}
+
 static int send_request(struct session *session)
 {
-       char endpoint[100];
+       char endpoint[500];
        char user_password[500];
        char data[500];
        struct bti_curl_buffer *curl_buf;
@@ -429,110 +580,200 @@ static int send_request(struct session *session)
        struct curl_httppost *formpost = NULL;
        struct curl_httppost *lastptr = NULL;
        struct curl_slist *slist = NULL;
+       char *req_url = NULL;
+       char *reply = NULL;
+       char *postarg = NULL;
+       char *escaped_tweet = NULL;
+       int is_post = 0;
 
        if (!session)
                return -EINVAL;
 
-       curl_buf = bti_curl_buffer_alloc(session->action);
-       if (!curl_buf)
-               return -ENOMEM;
-
-       curl = curl_init();
-       if (!curl)
-               return -EINVAL;
-
        if (!session->hosturl)
                session->hosturl = strdup(twitter_host);
 
-       switch (session->action) {
-       case ACTION_UPDATE:
-               snprintf(user_password, sizeof(user_password), "%s:%s",
-                        session->account, session->password);
-               snprintf(data, sizeof(data), "status=\"%s\"", session->tweet);
-               curl_formadd(&formpost, &lastptr,
-                            CURLFORM_COPYNAME, "status",
-                            CURLFORM_COPYCONTENTS, session->tweet,
-                            CURLFORM_END);
-
-               curl_formadd(&formpost, &lastptr,
-                            CURLFORM_COPYNAME, "source",
-                            CURLFORM_COPYCONTENTS, "bti",
-                            CURLFORM_END);
-
-               curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);
-               slist = curl_slist_append(slist, "Expect:");
-               curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist);
-
-               sprintf(endpoint, "%s%s", session->hosturl, update_uri);
-               curl_easy_setopt(curl, CURLOPT_URL, endpoint);
-               curl_easy_setopt(curl, CURLOPT_USERPWD, user_password);
+       if (session->no_oauth) {
+               curl_buf = bti_curl_buffer_alloc(session->action);
+               if (!curl_buf)
+                       return -ENOMEM;
 
-               break;
-       case ACTION_FRIENDS:
-               snprintf(user_password, sizeof(user_password), "%s:%s",
-                        session->account, session->password);
-               sprintf(endpoint, "%s%s?page=%d", session->hosturl,
-                       friends_uri, session->page);
-               curl_easy_setopt(curl, CURLOPT_URL, endpoint);
-               curl_easy_setopt(curl, CURLOPT_USERPWD, user_password);
+               curl = curl_init();
+               if (!curl)
+                       return -EINVAL;
 
-               break;
-       case ACTION_USER:
-               sprintf(endpoint, "%s%s%s.xml?page=%d", session->hosturl,
-                       user_uri, session->user, session->page);
-               curl_easy_setopt(curl, CURLOPT_URL, endpoint);
+               if (!session->hosturl)
+                       session->hosturl = strdup(twitter_host);
 
-               break;
-       case ACTION_REPLIES:
-               snprintf(user_password, sizeof(user_password), "%s:%s",
-                        session->account, session->password);
-               sprintf(endpoint, "%s%s?page=%d", session->hosturl, replies_uri,
-                       session->page);
-               curl_easy_setopt(curl, CURLOPT_URL, endpoint);
-               curl_easy_setopt(curl, CURLOPT_USERPWD, user_password);
+               switch (session->action) {
+               case ACTION_UPDATE:
+                       snprintf(user_password, sizeof(user_password), "%s:%s",
+                                session->account, session->password);
+                       snprintf(data, sizeof(data), "status=\"%s\"",
+                                session->tweet);
+                       curl_formadd(&formpost, &lastptr,
+                                    CURLFORM_COPYNAME, "status",
+                                    CURLFORM_COPYCONTENTS, session->tweet,
+                                    CURLFORM_END);
+
+                       curl_formadd(&formpost, &lastptr,
+                                    CURLFORM_COPYNAME, "source",
+                                    CURLFORM_COPYCONTENTS, "bti",
+                                    CURLFORM_END);
+
+                       if (session->replyto)
+                               curl_formadd(&formpost, &lastptr,
+                                            CURLFORM_COPYNAME, "in_reply_to_status_id",
+                                            CURLFORM_COPYCONTENTS, session->replyto,
+                                            CURLFORM_END);
+
+                       curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);
+                       slist = curl_slist_append(slist, "Expect:");
+                       curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist);
+
+                       sprintf(endpoint, "%s%s", session->hosturl, update_uri);
+                       curl_easy_setopt(curl, CURLOPT_URL, endpoint);
+                       curl_easy_setopt(curl, CURLOPT_USERPWD, user_password);
+                       break;
 
-               break;
-       case ACTION_PUBLIC:
-               sprintf(endpoint, "%s%s?page=%d", session->hosturl, public_uri,
-                       session->page);
-               curl_easy_setopt(curl, CURLOPT_URL, endpoint);
+               case ACTION_FRIENDS:
+                       snprintf(user_password, sizeof(user_password), "%s:%s",
+                                session->account, session->password);
+                       sprintf(endpoint, "%s%s?page=%d", session->hosturl,
+                                       friends_uri, session->page);
+                       curl_easy_setopt(curl, CURLOPT_URL, endpoint);
+                       curl_easy_setopt(curl, CURLOPT_USERPWD, user_password);
+                       break;
 
-               break;
-       case ACTION_GROUP:
-               sprintf(endpoint, "%s%s%s.xml?page=%d", session->hosturl,
-                               group_uri, session->group, session->page);
-               curl_easy_setopt(curl, CURLOPT_URL, endpoint);
+               case ACTION_USER:
+                       sprintf(endpoint, "%s%s%s.xml?page=%d", session->hosturl,
+                               user_uri, session->user, session->page);
+                       curl_easy_setopt(curl, CURLOPT_URL, endpoint);
+                       break;
 
-               break;
-       default:
-               break;
-       }
+               case ACTION_REPLIES:
+                       snprintf(user_password, sizeof(user_password), "%s:%s",
+                                session->account, session->password);
+                       sprintf(endpoint, "%s%s?page=%d", session->hosturl,
+                               replies_uri, session->page);
+                       curl_easy_setopt(curl, CURLOPT_URL, endpoint);
+                       curl_easy_setopt(curl, CURLOPT_USERPWD, user_password);
+                       break;
 
-       if (session->proxy)
-               curl_easy_setopt(curl, CURLOPT_PROXY, session->proxy);
+               case ACTION_PUBLIC:
+                       sprintf(endpoint, "%s%s?page=%d", session->hosturl,
+                               public_uri, session->page);
+                       curl_easy_setopt(curl, CURLOPT_URL, endpoint);
+                       break;
 
-       if (debug)
-               curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
-
-       dbg("user_password = %s\n", user_password);
-       dbg("data = %s\n", data);
-       dbg("proxy = %s\n", session->proxy);
-
-       curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_callback);
-       curl_easy_setopt(curl, CURLOPT_WRITEDATA, curl_buf);
-       if (!session->dry_run) {
-               res = curl_easy_perform(curl);
-               if (res && !session->bash) {
-                       fprintf(stderr, "error(%d) trying to perform "
-                               "operation\n", res);
-                       return -EINVAL;
+               case ACTION_GROUP:
+                       sprintf(endpoint, "%s%s%s.xml?page=%d",
+                               session->hosturl, group_uri, session->group,
+                               session->page);
+                       curl_easy_setopt(curl, CURLOPT_URL, endpoint);
+                       break;
+
+               default:
+                       break;
                }
-       }
 
-       curl_easy_cleanup(curl);
-       if (session->action == ACTION_UPDATE)
-               curl_formfree(formpost);
-       bti_curl_buffer_free(curl_buf);
+               if (session->proxy)
+                       curl_easy_setopt(curl, CURLOPT_PROXY, session->proxy);
+
+               if (debug)
+                       curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
+
+               dbg("user_password = %s\n", user_password);
+               dbg("data = %s\n", data);
+               dbg("proxy = %s\n", session->proxy);
+
+               curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_callback);
+               curl_easy_setopt(curl, CURLOPT_WRITEDATA, curl_buf);
+               if (!session->dry_run) {
+                       res = curl_easy_perform(curl);
+                       if (res && !session->background) {
+                               fprintf(stderr, "error(%d) trying to perform "
+                                               "operation\n", res);
+                               return -EINVAL;
+                       }
+               }
+
+               curl_easy_cleanup(curl);
+               if (session->action == ACTION_UPDATE)
+                       curl_formfree(formpost);
+               bti_curl_buffer_free(curl_buf);
+       } else {
+               switch (session->action) {
+               case ACTION_UPDATE:
+                       escaped_tweet = oauth_url_escape(session->tweet);
+                       if (session->replyto) {
+                               sprintf(endpoint,
+                                       "%s%s?status=%s&in_reply_to_status_id=%s",
+                                       session->hosturl, update_uri,
+                                       escaped_tweet, session->replyto);
+                       } else {
+                               sprintf(endpoint, "%s%s?status=%s",
+                                       session->hosturl, update_uri,
+                                       escaped_tweet);
+                       }
+
+                       is_post = 1;
+                       break;
+               case ACTION_USER:
+                       sprintf(endpoint, "%s%s%s.xml?page=%d",
+                               session->hosturl, user_uri, session->user,
+                               session->page);
+                       break;
+               case ACTION_REPLIES:
+                       sprintf(endpoint, "%s%s?page=%d", session->hosturl,
+                               mentions_uri, session->page);
+                       break;
+               case ACTION_PUBLIC:
+                       sprintf(endpoint, "%s%s?page=%d", session->hosturl,
+                               public_uri, session->page);
+                       break;
+               case ACTION_GROUP:
+                       sprintf(endpoint, "%s%s%s.xml?page=%d",
+                               session->hosturl, group_uri, session->group,
+                               session->page);
+                       break;
+               case ACTION_FRIENDS:
+                       sprintf(endpoint, "%s%s?page=%d", session->hosturl,
+                               friends_uri, session->page);
+                       break;
+               case ACTION_RETWEET:
+                       sprintf(endpoint, "%s%s%s.xml", session->hosturl,
+                               retweet_uri, session->retweet);
+                       is_post = 1;
+                       break;
+               default:
+                       break;
+               }
+
+               if (is_post) {
+                       req_url = oauth_sign_url2(endpoint, &postarg, OA_HMAC,
+                                                 NULL, session->consumer_key,
+                                                 session->consumer_secret,
+                                                 session->access_token_key,
+                                                 session->access_token_secret);
+                       reply = oauth_http_post(req_url, postarg);
+               } else {
+                       req_url = oauth_sign_url2(endpoint, NULL, OA_HMAC, NULL,
+                                                 session->consumer_key,
+                                                 session->consumer_secret,
+                                                 session->access_token_key,
+                                                 session->access_token_secret);
+                       reply = oauth_http_get(req_url, postarg);
+               }
+
+               dbg("%s\n", req_url);
+               dbg("%s\n", reply);
+               if (req_url)
+                       free(req_url);
+
+               if ((session->action != ACTION_UPDATE) &&
+                               (session->action != ACTION_RETWEET))
+                       parse_timeline(reply);
+       }
        return 0;
 }
 
@@ -543,20 +784,20 @@ static void parse_configfile(struct session *session)
        size_t len = 0;
        char *account = NULL;
        char *password = NULL;
+       char *consumer_key = NULL;
+       char *consumer_secret = NULL;
+       char *access_token_key = NULL;
+       char *access_token_secret = NULL;
        char *host = NULL;
        char *proxy = NULL;
        char *logfile = NULL;
        char *action = NULL;
        char *user = NULL;
-       char *file;
+       char *replyto = NULL;
+       char *retweet = NULL;
        int shrink_urls = 0;
 
-       /* config file is ~/.bti  */
-       file = alloca(strlen(session->homedir) + 7);
-
-       sprintf(file, "%s/.bti", session->homedir);
-
-       config_file = fopen(file, "r");
+       config_file = fopen(session->configfile, "r");
 
        /* No error if file does not exist or is unreadable.  */
        if (config_file == NULL)
@@ -590,6 +831,26 @@ static void parse_configfile(struct session *session)
                        c += 9;
                        if (c[0] != '\0')
                                password = strdup(c);
+               } else if (!strncasecmp(c, "consumer_key", 12) &&
+                          (c[12] == '=')) {
+                       c += 13;
+                       if (c[0] != '\0')
+                               consumer_key = strdup(c);
+               } else if (!strncasecmp(c, "consumer_secret", 15) &&
+                          (c[15] == '=')) {
+                       c += 16;
+                       if (c[0] != '\0')
+                               consumer_secret = strdup(c);
+               } else if (!strncasecmp(c, "access_token_key", 16) &&
+                          (c[16] == '=')) {
+                       c += 17;
+                       if (c[0] != '\0')
+                               access_token_key = strdup(c);
+               } else if (!strncasecmp(c, "access_token_secret", 19) &&
+                          (c[19] == '=')) {
+                       c += 20;
+                       if (c[0] != '\0')
+                               access_token_secret = strdup(c);
                } else if (!strncasecmp(c, "host", 4) &&
                           (c[4] == '=')) {
                        c += 5;
@@ -605,6 +866,11 @@ static void parse_configfile(struct session *session)
                        c += 8;
                        if (c[0] != '\0')
                                logfile = strdup(c);
+               } else if (!strncasecmp(c, "replyto", 7) &&
+                          (c[7] == '=')) {
+                       c += 8;
+                       if (c[0] != '\0')
+                               replyto = strdup(c);
                } else if (!strncasecmp(c, "action", 6) &&
                           (c[6] == '=')) {
                        c += 7;
@@ -627,6 +893,11 @@ static void parse_configfile(struct session *session)
                        if (!strncasecmp(c, "true", 4) ||
                                        !strncasecmp(c, "yes", 3))
                                verbose = 1;
+               } else if (!strncasecmp(c,"retweet", 7) &&
+                               (c[7] == '=')) {
+                       c += 8;
+                       if (c[0] != '\0')
+                               retweet = strdup(c);
                }
        } while (!feof(config_file));
 
@@ -634,6 +905,14 @@ static void parse_configfile(struct session *session)
                session->password = password;
        if (account)
                session->account = account;
+       if (consumer_key)
+               session->consumer_key = consumer_key;
+       if (consumer_secret)
+               session->consumer_secret = consumer_secret;
+       if (access_token_key)
+               session->access_token_key = access_token_key;
+       if (access_token_secret)
+               session->access_token_secret = access_token_secret;
        if (host) {
                if (strcasecmp(host, "twitter") == 0) {
                        session->host = HOST_TWITTER;
@@ -657,6 +936,10 @@ static void parse_configfile(struct session *session)
        }
        if (logfile)
                session->logfile = logfile;
+       if (replyto)
+               session->replyto = replyto;
+       if (retweet)
+               session->retweet = retweet;
        if (action) {
                if (strcasecmp(action, "update") == 0)
                        session->action = ACTION_UPDATE;
@@ -708,7 +991,8 @@ static void log_session(struct session *session, int retval)
                                session->time, session->hostname);
                else
                        fprintf(log_file, "%s: host=%s tweet=%s\n",
-                               session->time, session->hostname, session->tweet);
+                               session->time, session->hostname,
+                               session->tweet);
                break;
        case ACTION_FRIENDS:
                fprintf(log_file, "%s: host=%s retrieving friends timeline\n",
@@ -749,7 +1033,8 @@ static char *get_string_from_stdin(void)
        if (!fgets(string, 999, stdin))
                return NULL;
        temp = strchr(string, '\n');
-       *temp = '\0';
+       if (temp)
+               *temp = '\0';
        return string;
 }
 
@@ -1057,14 +1342,19 @@ int main(int argc, char *argv[], char *envp[])
                { "shrink-urls", 0, NULL, 's' },
                { "help", 0, NULL, 'h' },
                { "bash", 0, NULL, 'b' },
+               { "background", 0, NULL, 'B' },
                { "dry-run", 0, NULL, 'n' },
                { "page", 1, NULL, 'g' },
                { "version", 0, NULL, 'v' },
+               { "config", 1, NULL, 'c' },
+               { "replyto", 1, NULL, 'r' },
+               { "retweet", 1, NULL, 'w' },
                { }
        };
        struct session *session;
        pid_t child;
        char *tweet;
+       char *retweet;
        static char password[80];
        int retval = 0;
        int option;
@@ -1086,9 +1376,12 @@ int main(int argc, char *argv[], char *envp[])
        session->time = strdup(ctime(&t));
        session->time[strlen(session->time)-1] = 0x00;
 
+       /* Get the home directory so we can try to find a config file */
        session->homedir = strdup(getenv("HOME"));
 
-       curl_global_init(CURL_GLOBAL_ALL);
+       /* set up a default config file location (traditionally ~/.bti) */
+       session->configfile = zalloc(strlen(session->homedir) + 7);
+       sprintf(session->configfile, "%s/.bti", session->homedir);
 
        /* Set environment variables first, before reading command line options
         * or config file values. */
@@ -1103,7 +1396,8 @@ int main(int argc, char *argv[], char *envp[])
        parse_configfile(session);
 
        while (1) {
-               option = getopt_long_only(argc, argv, "dp:P:H:a:A:u:hg:G:snVv",
+               option = getopt_long_only(argc, argv,
+                                         "dp:P:H:a:A:u:c:hg:G:sr:nVv",
                                          options, NULL);
                if (option == -1)
                        break;
@@ -1125,6 +1419,14 @@ int main(int argc, char *argv[], char *envp[])
                        dbg("page = %d\n", page_nr);
                        session->page = page_nr;
                        break;
+               case 'r':
+                       session->replyto = strdup(optarg);
+                       dbg("in_reply_to_status_id = %s\n", session->replyto);
+                       break;
+               case 'w':
+                       session->retweet = strdup(optarg);
+                       dbg("Retweet ID = %s\n", session->retweet);
+                       break;
                case 'p':
                        if (session->password)
                                free(session->password);
@@ -1150,6 +1452,8 @@ int main(int argc, char *argv[], char *envp[])
                                session->action = ACTION_PUBLIC;
                        else if (strcasecmp(optarg, "group") == 0)
                                session->action = ACTION_GROUP;
+                       else if (strcasecmp(optarg,"retweet") == 0)
+                               session->action = ACTION_RETWEET;
                        else
                                session->action = ACTION_UNKNOWN;
                        dbg("action = %d\n", session->action);
@@ -1198,6 +1502,22 @@ int main(int argc, char *argv[], char *envp[])
                        break;
                case 'b':
                        session->bash = 1;
+                       /* fall-through intended */
+               case 'B':
+                       session->background = 1;
+                       break;
+               case 'c':
+                       if (session->configfile)
+                               free(session->configfile);
+                       session->configfile = strdup(optarg);
+                       dbg("configfile = %s\n", session->configfile);
+
+                       /*
+                        * read the config file now.  Yes, this could override
+                        * previously set options from the command line, but
+                        * the user asked for it...
+                        */
+                       parse_configfile(session);
                        break;
                case 'h':
                        display_help();
@@ -1214,6 +1534,7 @@ int main(int argc, char *argv[], char *envp[])
                }
        }
 
+       session_readline_init(session);
        /*
         * Show the version to make it easier to determine what
         * is going on here
@@ -1221,15 +1542,45 @@ int main(int argc, char *argv[], char *envp[])
        if (debug)
                display_version();
 
-       if (session->action == ACTION_UNKNOWN) {
-               fprintf(stderr, "Unknown action, valid actions are:\n");
-               fprintf(stderr, "'update', 'friends', 'public', "
-                       "'replies', 'group' or 'user'.\n");
-               goto exit;
+       if (session->host == HOST_TWITTER) {
+               if (!session->consumer_key || !session->consumer_secret) {
+                       fprintf(stderr,
+                               "Twitter no longer supports HTTP basic authentication.\n"
+                               "Both consumer key, and consumer secret are required"
+                               " for bti in order to behave as an OAuth consumer.\n");
+                       goto exit;
+               }
+               if (session->action == ACTION_GROUP) {
+                       fprintf(stderr, "Groups only work in Identi.ca.\n");
+                       goto exit;
+               }
+       } else {
+               if (!session->consumer_key || !session->consumer_secret)
+                       session->no_oauth = 1;
+       }
+
+       if (session->no_oauth) {
+               if (!session->account) {
+                       fprintf(stdout, "Enter account for %s: ",
+                               session->hostname);
+                       session->account = session->readline(NULL);
+               }
+               if (!session->password) {
+                       read_password(password, sizeof(password),
+                                     session->hostname);
+                       session->password = strdup(password);
+               }
+       } else {
+               if (!session->access_token_key ||
+                   !session->access_token_secret) {
+                       request_access_token(session);
+                       goto exit;
+               }
        }
 
-       if (session->host == HOST_TWITTER && session->action == ACTION_GROUP) {
-               fprintf(stderr, "Groups only work in Identi.ca.\n");
+       if (session->action == ACTION_UNKNOWN) {
+               fprintf(stderr, "Unknown action, valid actions are:\n"
+                       "'update', 'friends', 'public', 'replies', 'group' or 'user'.\n");
                goto exit;
        }
 
@@ -1238,18 +1589,23 @@ int main(int argc, char *argv[], char *envp[])
                session->group = session->readline(NULL);
        }
 
-       if (!session->account) {
-               fprintf(stdout, "Enter account for %s: ", session->hostname);
-               session->account = session->readline(NULL);
-       }
+       if (session->action == ACTION_RETWEET) {
+               fprintf(stdout, "Status ID to retweet: ");
+               retweet = get_string_from_stdin();
 
-       if (!session->password) {
-               read_password(password, sizeof(password), session->hostname);
-               session->password = strdup(password);
+               if (!retweet || strlen(retweet) == 0) {
+                       dbg("no retweet?\n");
+                       return -1;
+               }
+
+               session->retweet = zalloc(strlen(retweet) + 10);
+               sprintf(session->retweet,"%s", retweet);
+               free(retweet);
+               dbg("retweet ID = %s\n", session->retweet);
        }
 
        if (session->action == ACTION_UPDATE) {
-               if (session->bash || !session->interactive)
+               if (session->background || !session->interactive)
                        tweet = get_string_from_stdin();
                else
                        tweet = session->readline("tweet: ");
@@ -1272,20 +1628,16 @@ int main(int argc, char *argv[], char *envp[])
                dbg("tweet = %s\n", session->tweet);
        }
 
-       if (!session->user)
-               session->user = strdup(session->account);
-
        if (session->page == 0)
                session->page = 1;
-       dbg("account = %s\n", session->account);
-       dbg("password = %s\n", session->password);
+       dbg("config file = %s\n", session->configfile);
        dbg("host = %d\n", session->host);
        dbg("action = %d\n", session->action);
 
        /* fork ourself so that the main shell can get on
         * with it's life as we try to connect and handle everything
         */
-       if (session->bash) {
+       if (session->background) {
                child = fork();
                if (child) {
                        dbg("child is %d\n", child);
@@ -1294,11 +1646,12 @@ int main(int argc, char *argv[], char *envp[])
        }
 
        retval = send_request(session);
-       if (retval && !session->bash)
+       if (retval && !session->background)
                fprintf(stderr, "operation failed\n");
 
        log_session(session, retval);
 exit:
+       session_readline_cleanup(session);
        session_free(session);
        return retval;;
 }
index e6ad4899d8d81924e9702ce33bba262f6eb4aecc..615cc1081027981790ad3e5882e842cc1c0f4924 100644 (file)
@@ -13,3 +13,7 @@ logfile=.bti.log
 #user=gregkh
 #proxy=http://localhost:8080
 #shrink-urls=yes
+# Example of using bit.ly in bti-shrink-urls
+#shrink_host=bit.ly
+#shrink_bitly_login=bitlyuser
+#shrink_bitly_key=R_deadbeef
diff --git a/bti.xml b/bti.xml
index 251f476c054f9434e38a7d0373bf8e17078df378..ae08e4f8b47949b6c2267a4f53fd92394de6a552 100644 (file)
--- a/bti.xml
+++ b/bti.xml
@@ -33,6 +33,9 @@
           <arg><option>--host HOST_NAME</option></arg>
           <arg><option>--proxy PROXY:PORT</option></arg>
           <arg><option>--logfile LOGFILE</option></arg>
+          <arg><option>--config CONFIGFILE</option></arg>
+          <arg><option>--replyto ID</option></arg>
+          <arg><option>--retweet ID</option></arg>
           <arg><option>--page PAGENUMBER</option></arg>
           <arg><option>--bash</option></arg>
           <arg><option>--shrink-urls</option></arg>
@@ -75,7 +78,7 @@
                are "update" to send a message, "friends" to see your friends
                timeline, "public" to track public timeline, "replies" to see
                replies to your messages and "user" to see a specific user's
-               timeline.
+               timeline.  Default is "update".
               </para>
             </listitem>
           </varlistentry>
@@ -83,7 +86,7 @@
             <term><option>--user screenname</option></term>
             <listitem>
               <para>
-               Specify the user you want to see his/her messages while the
+               Specify the user whose messages you want to see when the
                action is "user".
               </para>
             </listitem>
               </para>
             </listitem>
           </varlistentry>
+          <varlistentry>
+            <term><option>--config CONFIGFILE</option></term>
+            <listitem>
+              <para>
+               Specify a config file for bti to read from.  By default, bti
+               looks in the ~/.bti file for config values.  This default
+               location can be overridden by setting a specific file with this
+               option.
+              </para>
+            </listitem>
+          </varlistentry>
+          <varlistentry>
+            <term><option>--replyto ID</option></term>
+            <listitem>
+              <para>
+               Status ID of a single post to which you want to create a threaded
+               reply to.
+             </para>
+             <para>
+               For twitter, this is ignored unless the message starts with the
+               @name of the owner of the post with the status ID.
+             </para>
+             <para>
+               For status.net, this can link any two messages into context with
+               each other. Status.net will also link a message that contains an
+               @name without this without regard to context.
+              </para>
+            </listitem>
+          </varlistentry>
+          <varlistentry>
+            <term><option>--retweet ID</option></term>
+            <listitem>
+              <para>
+               Status ID of a single post which you want to retweet.
+             </para>
+            </listitem>
+          </varlistentry>
           <varlistentry>
             <term><option>--shrink-urls</option></term>
             <listitem>
                 more suitable for micro-blogging.
               </para>
               <para>
-                Currently, only http://2tu.us/ is used as a URL shrinking service.
+                The following URL shrinking services are available:
+                http://2tu.us/ (default) and http://bit.ly / http://j.mp
+              </para>
+              <para>
+                See the documentation for bti-shrink-urls for the configuration options.
               </para>
             </listitem>
           </varlistentry>
             <term><option>--verbose</option></term>
             <listitem>
               <para>
-                Verbose mode
+                Verbose mode. Print status IDs and timestamps.
               </para>
             </listitem>
           </varlistentry>
                 help specify it is coming from a command line.  Don't put the
                 working directory and the '$' in the tweet message.
              </para>
+
+              <para>
+                 This option implies <option>--background</option>.
+              </para>
+            </listitem>
+          </varlistentry>
+          <varlistentry>
+            <term><option>--background</option></term>
+            <listitem>
              <para>
-                This mode also does not report back any errors that might have
-                happened when sending the message, and it sends it in the
-                background, returning immediately, allowing the process to
-                continue on.
+                Do not report back any errors that might have
+                happened when sending the message, and send it in the
+                background, returning immediately, allowing the user
+                to continue on.
               </para>
             </listitem>
           </varlistentry>
           out.
         </para>
         <para>
-          It's primary focus is to allow you to log everything that you
+          Its primary focus is to allow you to log everything that you
           type into a bash shell, in a crazy, "this is what I'm doing right
           now!" type of way, letting the world follow along with you
           constant moving between directories and refreshing your email
          <para>
            The account and password can be stored in a configuration file
            in the users home directory in a file named
-           <filename>.bti</filename>  The structure of this file is as
+           <filename>.bti</filename>.  The structure of this file is as
            follows:
          </para>
          <variablelist>
                </para>
              </listitem>
            </varlistentry>
+           <varlistentry>
+             <term><option>replyto</option></term>
+             <listitem>
+               <para>
+                The status ID to which all notices will be linked to.
+               </para>
+              <para>
+                There is no sane reason for a need to have this set in a
+                config file. One such reason is to have all your messages
+                as children to a particular status.
+              </para>
+             </listitem>
+           </varlistentry>
            <varlistentry>
              <term><option>shrink-urls</option></term>
              <listitem>
          </variablelist>
         <para>
           For example, command line options always override any config file
-          option, or any environment variables.
+          option, or any environment variables.  Unless a config file is
+          specified by the command line.  At that point, the new config file is
+          read, and any previous options set by a command line option, would be
+          overridden.
          </para>
        </refsect1>
 
index 6e16ce0ed57038a3305d917fff11ddc782b3f398..4b5fd3af81d35069af4a75740e63f29932be2ca5 100644 (file)
@@ -1,8 +1,7 @@
-AC_INIT([bti], [025], [greg@kroah.com])
+AC_INIT([bti], [028], [greg@kroah.com])
 AC_PREREQ(2.60)
 
-AM_INIT_AUTOMAKE(bti, 025)
-dnl AM_CONFIG_HEADER([check-news foreign 1.9 dist-bzip2])
+AM_INIT_AUTOMAKE(bti, 028)
 
 m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
 
@@ -13,45 +12,14 @@ AC_CONFIG_MACRO_DIR([m4])
 
 AC_PATH_PROG([XSLTPROC], [xsltproc])
 
-dnl FIXME: Replace `main' with a function in `-lnsl':
-AC_CHECK_LIB([nsl], [main])
-
-AC_CHECK_LIB([pcre], [main])
-
-# CURL
-LIBCURL_CHECK_CONFIG([yes], [], [have_libcurl="yes"], [have_libcurl="no"])
-if test "${have_libcurl}" != yes; then
-       AC_MSG_ERROR([libcurl library not found, please install it])
-else
-       CPPFLAGS="$CPPFLAGS $LIBCURL_CPPFLAGS"
-       LIBS="${LIBS} ${LIBCURL}"
-fi
-
-# libxml2
-AM_PATH_XML2([], [have_libxml="yes"], [have_libxml="no"])
-if test "${have_libxml}" != yes; then
-       AC_MSG_ERROR([libxml not found, please install it])
-else
-       CPPFLAGS="$CPPFLAGS $XML_CPPFLAGS"
-       LIBS="$LIBS $XML_LIBS"
-fi
-
-
-
-dnl Checks for header files.
-#AC_CHECK_HEADERS([fcntl.h stddef.h stdlib.h string.h unistd.h])
-
-dnl Checks for typedefs, structures, and compiler characteristics.
-#AC_TYPE_PID_T
-#AC_TYPE_SIZE_T
-#AC_TYPE_SSIZE_T
-
-dnl Checks for library functions.
-#AC_FUNC_ALLOCA
-#AC_FUNC_FORK
-#AC_FUNC_MALLOC
-#AC_FUNC_REALLOC
-#AC_CHECK_FUNCS([strcasecmp strchr strdup strncasecmp strndup])
+PKG_PROG_PKG_CONFIG()
+
+PKG_CHECK_MODULES(LIBOAUTH, oauth)
+PKG_CHECK_MODULES(LIBPCRE, libpcre)
+PKG_CHECK_MODULES([LIBCURL], [libcurl])
+PKG_CHECK_MODULES([XML], [libxml-2.0])
+
+AC_SEARCH_LIBS([dlopen], [dl])
 
 AC_CONFIG_FILES([Makefile])
 
diff --git a/oath.keys b/oath.keys
new file mode 100644 (file)
index 0000000..4003a4d
--- /dev/null
+++ b/oath.keys
@@ -0,0 +1,16 @@
+# Consumer key
+cZy8DdioswAfu3LJYg6E2w
+
+# Consumer secret
+fnIGGU0T12mMWKjmThUdSeKN32NLWfmnwapwubVQ
+
+# Request token URL
+http://twitter.com/oauth/request_token
+
+# Access token URL
+http://twitter.com/oauth/access_token
+
+# Authorize URL
+http://twitter.com/oauth/authorize
+
+
index 6971bf078d1338ff3f7c7f2b5b72b7f874c94fbc..2039acdf51222b3961b06291d6a02f4a8105a240 100755 (executable)
@@ -1,7 +1,8 @@
 #!/usr/bin/perl -w
-# (c) 2001, Dave Jones. <davej@codemonkey.org.uk> (the file handling bit)
+# (c) 2001, Dave Jones. (the file handling bit)
 # (c) 2005, Joel Schopp <jschopp@austin.ibm.com> (the ugly bit)
-# (c) 2007, Andy Whitcroft <apw@uk.ibm.com> (new conditions, test suite, etc)
+# (c) 2007,2008, Andy Whitcroft <apw@uk.ibm.com> (new conditions, test suite)
+# (c) 2008,2009, Andy Whitcroft <apw@canonical.com>
 # Licensed under the terms of the GNU GPL License version 2
 
 use strict;
@@ -9,7 +10,7 @@ use strict;
 my $P = $0;
 $P =~ s@.*/@@g;
 
-my $V = '0.19';
+my $V = '0.30';
 
 use Getopt::Long qw(:config no_auto_abbrev);
 
@@ -17,7 +18,6 @@ my $quiet = 0;
 my $tree = 1;
 my $chk_signoff = 1;
 my $chk_patch = 1;
-my $tst_type = 0;
 my $tst_only;
 my $emacs = 0;
 my $terse = 0;
@@ -28,6 +28,41 @@ my $mailback = 0;
 my $summary_file = 0;
 my $root;
 my %debug;
+my $help = 0;
+
+sub help {
+       my ($exitcode) = @_;
+
+       print << "EOM";
+Usage: $P [OPTION]... [FILE]...
+Version: $V
+
+Options:
+  -q, --quiet                quiet
+  --no-tree                  run without a kernel tree
+  --no-signoff               do not check for 'Signed-off-by' line
+  --patch                    treat FILE as patchfile (default)
+  --emacs                    emacs compile window format
+  --terse                    one line per report
+  -f, --file                 treat FILE as regular source file
+  --subjective, --strict     enable more subjective tests
+  --root=PATH                PATH to the kernel tree root
+  --no-summary               suppress the per-file summary
+  --mailback                 only produce a report in case of warnings/errors
+  --summary-file             include the filename in summary
+  --debug KEY=[0|1]          turn on/off debugging of KEY, where KEY is one of
+                             'values', 'possible', 'type', and 'attr' (default
+                             is all off)
+  --test-only=WORD           report only warnings/errors containing WORD
+                             literally
+  -h, --help, --version      display this help and exit
+
+When FILE is - read standard input.
+EOM
+
+       exit($exitcode);
+}
+
 GetOptions(
        'q|quiet+'      => \$quiet,
        'tree!'         => \$tree,
@@ -35,7 +70,7 @@ GetOptions(
        'patch!'        => \$chk_patch,
        'emacs!'        => \$emacs,
        'terse!'        => \$terse,
-       'file!'         => \$file,
+       'f|file!'       => \$file,
        'subjective!'   => \$check,
        'strict!'       => \$check,
        'root=s'        => \$root,
@@ -44,31 +79,28 @@ GetOptions(
        'summary-file!' => \$summary_file,
 
        'debug=s'       => \%debug,
-       'test-type!'    => \$tst_type,
        'test-only=s'   => \$tst_only,
-) or exit;
+       'h|help'        => \$help,
+       'version'       => \$help
+) or help(1);
+
+help(0) if ($help);
 
 my $exit = 0;
 
 if ($#ARGV < 0) {
-       print "usage: $P [options] patchfile\n";
-       print "version: $V\n";
-       print "options: -q               => quiet\n";
-       print "         --no-tree        => run without a kernel tree\n";
-       print "         --terse          => one line per report\n";
-       print "         --emacs          => emacs compile window format\n";
-       print "         --file           => check a source file\n";
-       print "         --strict         => enable more subjective tests\n";
-       print "         --root           => path to the kernel tree root\n";
-       print "         --no-summary     => suppress the per-file summary\n";
-       print "         --summary-file   => include the filename in summary\n";
+       print "$P: no input files\n";
        exit(1);
 }
 
 my $dbg_values = 0;
 my $dbg_possible = 0;
+my $dbg_type = 0;
+my $dbg_attr = 0;
 for my $key (keys %debug) {
-       eval "\${dbg_$key} = '$debug{$key}';"
+       ## no critic
+       eval "\${dbg_$key} = '$debug{$key}';";
+       die "$@" if ($@);
 }
 
 if ($terse) {
@@ -98,7 +130,10 @@ if ($tree) {
 
 my $emitted_corrupt = 0;
 
-our $Ident       = qr{[A-Za-z_][A-Za-z\d_]*};
+our $Ident     = qr{
+                       [A-Za-z_][A-Za-z\d_]*
+                       (?:\s*\#\#\s*[A-Za-z_][A-Za-z\d_]*)*
+               }x;
 our $Storage   = qr{extern|static|asmlinkage};
 our $Sparse    = qr{
                        __user|
@@ -107,13 +142,21 @@ our $Sparse       = qr{
                        __iomem|
                        __must_check|
                        __init_refok|
-                       __kprobes
+                       __kprobes|
+                       __ref
                }x;
+
+# Notes to $Attribute:
+# We need \b after 'init' otherwise 'initconst' will cause a false positive in a check
 our $Attribute = qr{
                        const|
                        __read_mostly|
                        __kprobes|
-                       __(?:mem|cpu|dev|)(?:initdata|init)
+                       __(?:mem|cpu|dev|)(?:initdata|initconst|init\b)|
+                       ____cacheline_aligned|
+                       ____cacheline_aligned_in_smp|
+                       ____cacheline_internodealigned_in_smp|
+                       __weak
                  }x;
 our $Modifier;
 our $Inline    = qr{inline|__always_inline|noinline};
@@ -122,6 +165,7 @@ our $Lval   = qr{$Ident(?:$Member)*};
 
 our $Constant  = qr{(?:[0-9]+|0x[0-9a-fA-F]+)[UL]*};
 our $Assignment        = qr{(?:\*\=|/=|%=|\+=|-=|<<=|>>=|&=|\^=|\|=|=)};
+our $Compare    = qr{<=|>=|==|!=|<|>};
 our $Operators = qr{
                        <=|>=|==|!=|
                        =>|->|<<|>>|<|>|!|~|
@@ -143,6 +187,19 @@ our $UTF8  = qr {
        |  \xF4[\x80-\x8F][\x80-\xBF]{2}     # plane 16
 }x;
 
+our $typeTypedefs = qr{(?x:
+       (?:__)?(?:u|s|be|le)(?:8|16|32|64)|
+       atomic_t
+)};
+
+our $logFunctions = qr{(?x:
+       printk|
+       pr_(debug|dbg|vdbg|devel|info|warning|err|notice|alert|crit|emerg|cont)|
+       (dev|netdev|netif)_(printk|dbg|vdbg|info|warn|err|notice|alert|crit|emerg|WARN)|
+       WARN|
+       panic
+)};
+
 our @typeList = (
        qr{void},
        qr{(?:unsigned\s+)?char},
@@ -156,7 +213,6 @@ our @typeList = (
        qr{float},
        qr{double},
        qr{bool},
-       qr{(?:__)?(?:u|s|be|le)(?:8|16|32|64)},
        qr{struct\s+$Ident},
        qr{union\s+$Ident},
        qr{enum\s+$Ident},
@@ -168,25 +224,31 @@ our @modifierList = (
        qr{fastcall},
 );
 
+our $allowed_asm_includes = qr{(?x:
+       irq|
+       memory
+)};
+# memory.h: ARM has a custom one
+
 sub build_types {
-       my $mods = "(?:  \n" . join("|\n  ", @modifierList) . "\n)";
-       my $all = "(?:  \n" . join("|\n  ", @typeList) . "\n)";
+       my $mods = "(?x:  \n" . join("|\n  ", @modifierList) . "\n)";
+       my $all = "(?x:  \n" . join("|\n  ", @typeList) . "\n)";
+       $Modifier       = qr{(?:$Attribute|$Sparse|$mods)};
        $NonptrType     = qr{
-                       (?:const\s+)?
-                       (?:$mods\s+)?
+                       (?:$Modifier\s+|const\s+)*
                        (?:
                                (?:typeof|__typeof__)\s*\(\s*\**\s*$Ident\s*\)|
+                               (?:$typeTypedefs\b)|
                                (?:${all}\b)
                        )
-                       (?:\s+$Sparse|\s+const)*
+                       (?:\s+$Modifier|\s+const)*
                  }x;
        $Type   = qr{
                        $NonptrType
-                       (?:\s*\*+\s*const|\s*\*+|(?:\s*\[\s*\])+)?
-                       (?:\s+$Inline|\s+$Sparse|\s+$Attribute|\s+$mods)*
+                       (?:[\s\*]+\s*const|[\s\*]+|(?:\s*\[\s*\])+)?
+                       (?:\s+$Inline|\s+$Modifier)*
                  }x;
        $Declare        = qr{(?:$Storage\s+)?$Type};
-       $Modifier       = qr{(?:$Attribute|$Sparse|$mods)};
 }
 build_types();
 
@@ -196,9 +258,9 @@ my @dep_includes = ();
 my @dep_functions = ();
 my $removal = "Documentation/feature-removal-schedule.txt";
 if ($tree && -f "$root/$removal") {
-       open(REMOVE, "<$root/$removal") ||
+       open(my $REMOVE, '<', "$root/$removal") ||
                                die "$P: $removal: open failed - $!\n";
-       while (<REMOVE>) {
+       while (<$REMOVE>) {
                if (/^Check:\s+(.*\S)/) {
                        for my $entry (split(/[, ]+/, $1)) {
                                if ($entry =~ m@include/(.*)@) {
@@ -210,17 +272,21 @@ if ($tree && -f "$root/$removal") {
                        }
                }
        }
+       close($REMOVE);
 }
 
 my @rawlines = ();
 my @lines = ();
 my $vname;
 for my $filename (@ARGV) {
+       my $FILE;
        if ($file) {
-               open(FILE, "diff -u /dev/null $filename|") ||
+               open($FILE, '-|', "diff -u /dev/null $filename") ||
                        die "$P: $filename: diff failed - $!\n";
+       } elsif ($filename eq '-') {
+               open($FILE, '<&STDIN');
        } else {
-               open(FILE, "<$filename") ||
+               open($FILE, '<', "$filename") ||
                        die "$P: $filename: open failed - $!\n";
        }
        if ($filename eq '-') {
@@ -228,11 +294,11 @@ for my $filename (@ARGV) {
        } else {
                $vname = $filename;
        }
-       while (<FILE>) {
+       while (<$FILE>) {
                chomp;
                push(@rawlines, $_);
        }
-       close(FILE);
+       close($FILE);
        if (!process($filename)) {
                $exit = 1;
        }
@@ -333,12 +399,19 @@ sub sanitise_line {
                        $off++;
                        next;
                }
-               if (substr($line, $off, 2) eq '*/') {
+               if ($sanitise_quote eq '*/' && substr($line, $off, 2) eq '*/') {
                        $sanitise_quote = '';
                        substr($res, $off, 2, "$;$;");
                        $off++;
                        next;
                }
+               if ($sanitise_quote eq '' && substr($line, $off, 2) eq '//') {
+                       $sanitise_quote = '//';
+
+                       substr($res, $off, 2, $sanitise_quote);
+                       $off++;
+                       next;
+               }
 
                # A \ in a string means ignore the next character.
                if (($sanitise_quote eq "'" || $sanitise_quote eq '"') &&
@@ -359,9 +432,11 @@ sub sanitise_line {
                        }
                }
 
-               #print "SQ:$sanitise_quote\n";
+               #print "c<$c> SQ<$sanitise_quote>\n";
                if ($off != 0 && $sanitise_quote eq '*/' && $c ne "\t") {
                        substr($res, $off, 1, $;);
+               } elsif ($off != 0 && $sanitise_quote eq '//' && $c ne "\t") {
+                       substr($res, $off, 1, $;);
                } elsif ($off != 0 && $sanitise_quote && $c ne "\t") {
                        substr($res, $off, 1, 'X');
                } else {
@@ -369,6 +444,10 @@ sub sanitise_line {
                }
        }
 
+       if ($sanitise_quote eq '//') {
+               $sanitise_quote = '';
+       }
+
        # The pathname on a #include may be surrounded by '<' and '>'.
        if ($res =~ /^.\s*\#\s*include\s+\<(.*)\>/) {
                my $clean = 'X' x length($1);
@@ -395,17 +474,21 @@ sub ctx_statement_block {
 
        my $type = '';
        my $level = 0;
+       my @stack = ();
        my $p;
        my $c;
        my $len = 0;
 
        my $remainder;
        while (1) {
+               @stack = (['', 0]) if ($#stack == -1);
+
                #warn "CSB: blk<$blk> remain<$remain>\n";
                # If we are about to drop off the end, pull in more
                # context.
                if ($off >= $len) {
                        for (; $remain > 0; $line++) {
+                               last if (!defined $lines[$line]);
                                next if ($lines[$line] =~ /^-/);
                                $remain--;
                                $loff = $len;
@@ -425,6 +508,16 @@ sub ctx_statement_block {
                $remainder = substr($blk, $off);
 
                #warn "CSB: c<$c> type<$type> level<$level> remainder<$remainder> coff_set<$coff_set>\n";
+
+               # Handle nested #if/#else.
+               if ($remainder =~ /^#\s*(?:ifndef|ifdef|if)\s/) {
+                       push(@stack, [ $type, $level ]);
+               } elsif ($remainder =~ /^#\s*(?:else|elif)\b/) {
+                       ($type, $level) = @{$stack[$#stack - 1]};
+               } elsif ($remainder =~ /^#\s*endif\b/) {
+                       ($type, $level) = @{pop(@stack)};
+               }
+
                # Statement ends at the ';' or a close '}' at the
                # outermost level.
                if ($level == 0 && $c eq ';') {
@@ -465,12 +558,17 @@ sub ctx_statement_block {
                        $type = ($level != 0)? '{' : '';
 
                        if ($level == 0) {
+                               if (substr($blk, $off + 1, 1) eq ';') {
+                                       $off++;
+                               }
                                last;
                        }
                }
                $off++;
        }
+       # We are truly at the end, so shuffle to the next line.
        if ($off == $len) {
+               $loff = $len + 1;
                $line++;
                $remain--;
        }
@@ -569,11 +667,22 @@ sub ctx_block_get {
        my @res = ();
 
        my $level = 0;
+       my @stack = ($level);
        for ($line = $start; $remain > 0; $line++) {
                next if ($rawlines[$line] =~ /^-/);
                $remain--;
 
                $blk .= $rawlines[$line];
+
+               # Handle nested #if/#else.
+               if ($rawlines[$line] =~ /^.\s*#\s*(?:ifndef|ifdef|if)\s/) {
+                       push(@stack, $level);
+               } elsif ($rawlines[$line] =~ /^.\s*#\s*(?:else|elif)\b/) {
+                       $level = $stack[$#stack - 1];
+               } elsif ($rawlines[$line] =~ /^.\s*#\s*endif\b/) {
+                       $level = pop(@stack);
+               }
+
                foreach my $c (split(//, $rawlines[$line])) {
                        ##print "C<$c>L<$level><$open$close>O<$off>\n";
                        if ($off > 0) {
@@ -631,7 +740,7 @@ sub ctx_locate_comment {
        my ($first_line, $end_line) = @_;
 
        # Catch a comment on the end of the line itself.
-       my ($current_comment) = ($rawlines[$end_line - 1] =~ m@.*(/\*.*\*/)\s*$@);
+       my ($current_comment) = ($rawlines[$end_line - 1] =~ m@.*(/\*.*\*/)\s*(?:\\\s*)?$@);
        return $current_comment if (defined $current_comment);
 
        # Look through the context and try and figure out if there is a
@@ -669,6 +778,22 @@ sub ctx_has_comment {
        return ($cmt ne '');
 }
 
+sub raw_line {
+       my ($linenr, $cnt) = @_;
+
+       my $offset = $linenr - 1;
+       $cnt++;
+
+       my $line;
+       while ($cnt) {
+               $line = $rawlines[$offset++];
+               next if (defined($line) && $line =~ /^-/);
+               $cnt--;
+       }
+
+       return $line;
+}
+
 sub cat_vet {
        my ($vet) = @_;
        my ($res, $coded);
@@ -689,17 +814,20 @@ sub cat_vet {
 my $av_preprocessor = 0;
 my $av_pending;
 my @av_paren_type;
+my $av_pend_colon;
 
 sub annotate_reset {
        $av_preprocessor = 0;
        $av_pending = '_';
        @av_paren_type = ('E');
+       $av_pend_colon = 'O';
 }
 
 sub annotate_values {
        my ($stream, $type) = @_;
 
        my $res;
+       my $var = '_' x length($stream);
        my $cur = $stream;
 
        print "$stream\n" if ($dbg_values > 1);
@@ -715,10 +843,14 @@ sub annotate_values {
                                $av_preprocessor = 0;
                        }
 
-               } elsif ($cur =~ /^($Type)/) {
+               } elsif ($cur =~ /^($Type)\s*(?:$Ident|,|\)|\()/) {
                        print "DECLARE($1)\n" if ($dbg_values > 1);
                        $type = 'T';
 
+               } elsif ($cur =~ /^($Modifier)\s*/) {
+                       print "MODIFIER($1)\n" if ($dbg_values > 1);
+                       $type = 'T';
+
                } elsif ($cur =~ /^(\#\s*define\s*$Ident)(\(?)/o) {
                        print "DEFINE($1,$2)\n" if ($dbg_values > 1);
                        $av_preprocessor = 1;
@@ -775,12 +907,17 @@ sub annotate_values {
                        }
                        $type = 'N';
 
-               } elsif ($cur =~ /^(if|while|typeof|__typeof__|for)\b/o) {
+               } elsif ($cur =~ /^(if|while|for)\b/o) {
                        print "COND($1)\n" if ($dbg_values > 1);
-                       $av_pending = 'N';
+                       $av_pending = 'E';
+                       $type = 'N';
+
+               } elsif ($cur =~/^(case)/o) {
+                       print "CASE($1)\n" if ($dbg_values > 1);
+                       $av_pend_colon = 'C';
                        $type = 'N';
 
-               } elsif ($cur =~/^(return|case|else)/o) {
+               } elsif ($cur =~/^(return|else|goto|typeof|__typeof__)\b/o) {
                        print "KEYWORD($1)\n" if ($dbg_values > 1);
                        $type = 'N';
 
@@ -800,10 +937,20 @@ sub annotate_values {
                                print "PAREN('$1')\n" if ($dbg_values > 1);
                        }
 
-               } elsif ($cur =~ /^($Ident)\(/o) {
+               } elsif ($cur =~ /^($Ident)\s*\(/o) {
                        print "FUNC($1)\n" if ($dbg_values > 1);
+                       $type = 'V';
                        $av_pending = 'V';
 
+               } elsif ($cur =~ /^($Ident\s*):(?:\s*\d+\s*(,|=|;))?/) {
+                       if (defined $2 && $type eq 'C' || $type eq 'T') {
+                               $av_pend_colon = 'B';
+                       } elsif ($type eq 'E') {
+                               $av_pend_colon = 'L';
+                       }
+                       print "IDENT_COLON($1,$type>$av_pend_colon)\n" if ($dbg_values > 1);
+                       $type = 'V';
+
                } elsif ($cur =~ /^($Ident|$Constant)/o) {
                        print "IDENT($1)\n" if ($dbg_values > 1);
                        $type = 'V';
@@ -815,11 +962,44 @@ sub annotate_values {
                } elsif ($cur =~/^(;|{|})/) {
                        print "END($1)\n" if ($dbg_values > 1);
                        $type = 'E';
+                       $av_pend_colon = 'O';
+
+               } elsif ($cur =~/^(,)/) {
+                       print "COMMA($1)\n" if ($dbg_values > 1);
+                       $type = 'C';
+
+               } elsif ($cur =~ /^(\?)/o) {
+                       print "QUESTION($1)\n" if ($dbg_values > 1);
+                       $type = 'N';
 
-               } elsif ($cur =~ /^(;|\?|:|\[)/o) {
+               } elsif ($cur =~ /^(:)/o) {
+                       print "COLON($1,$av_pend_colon)\n" if ($dbg_values > 1);
+
+                       substr($var, length($res), 1, $av_pend_colon);
+                       if ($av_pend_colon eq 'C' || $av_pend_colon eq 'L') {
+                               $type = 'E';
+                       } else {
+                               $type = 'N';
+                       }
+                       $av_pend_colon = 'O';
+
+               } elsif ($cur =~ /^(\[)/o) {
                        print "CLOSE($1)\n" if ($dbg_values > 1);
                        $type = 'N';
 
+               } elsif ($cur =~ /^(-(?![->])|\+(?!\+)|\*|\&\&|\&)/o) {
+                       my $variant;
+
+                       print "OPV($1)\n" if ($dbg_values > 1);
+                       if ($type eq 'V') {
+                               $variant = 'B';
+                       } else {
+                               $variant = 'U';
+                       }
+
+                       substr($var, length($res), 1, $variant);
+                       $type = 'N';
+
                } elsif ($cur =~ /^($Operators)/o) {
                        print "OP($1)\n" if ($dbg_values > 1);
                        if ($1 ne '++' && $1 ne '--') {
@@ -835,18 +1015,30 @@ sub annotate_values {
                }
        }
 
-       return $res;
+       return ($res, $var);
 }
 
 sub possible {
        my ($possible, $line) = @_;
-
-       print "CHECK<$possible> ($line)\n" if ($dbg_possible > 1);
-       if ($possible !~ /^(?:$Storage|$Type|DEFINE_\S+)$/ &&
-           $possible ne 'goto' && $possible ne 'return' &&
-           $possible ne 'case' && $possible ne 'else' &&
-           $possible ne 'asm' &&
-           $possible !~ /^(typedef|struct|enum)\b/) {
+       my $notPermitted = qr{(?:
+               ^(?:
+                       $Modifier|
+                       $Storage|
+                       $Type|
+                       DEFINE_\S+
+               )$|
+               ^(?:
+                       goto|
+                       return|
+                       case|
+                       else|
+                       asm|__asm__|
+                       do
+               )(?:\s|$)|
+               ^(?:typedef|struct|enum)\b
+           )}x;
+       warn "CHECK<$possible> ($line)\n" if ($dbg_possible > 2);
+       if ($possible !~ $notPermitted) {
                # Check for modifiers.
                $possible =~ s/\s*$Storage\s*//g;
                $possible =~ s/\s*$Sparse\s*//g;
@@ -854,14 +1046,20 @@ sub possible {
 
                } elsif ($possible =~ /\s/) {
                        $possible =~ s/\s*$Type\s*//g;
-                       warn "MODIFIER: $possible ($line)\n" if ($dbg_possible);
-                       push(@modifierList, $possible);
+                       for my $modifier (split(' ', $possible)) {
+                               if ($modifier !~ $notPermitted) {
+                                       warn "MODIFIER: $modifier ($possible) ($line)\n" if ($dbg_possible);
+                                       push(@modifierList, $modifier);
+                               }
+                       }
 
                } else {
                        warn "POSSIBLE: $possible ($line)\n" if ($dbg_possible);
                        push(@typeList, $possible);
                }
                build_types();
+       } else {
+               warn "NOTPOSS: $possible ($line)\n" if ($dbg_possible > 1);
        }
 }
 
@@ -901,6 +1099,33 @@ sub CHK {
        }
 }
 
+sub check_absolute_file {
+       my ($absolute, $herecurr) = @_;
+       my $file = $absolute;
+
+       ##print "absolute<$absolute>\n";
+
+       # See if any suffix of this path is a path within the tree.
+       while ($file =~ s@^[^/]*/@@) {
+               if (-f "$root/$file") {
+                       ##print "file<$file>\n";
+                       last;
+               }
+       }
+       if (! -f _)  {
+               return 0;
+       }
+
+       # It is, so see if the prefix is acceptable.
+       my $prefix = $absolute;
+       substr($prefix, -length($file)) = '';
+
+       ##print "prefix<$prefix>\n";
+       if ($prefix ne ".../") {
+               WARN("use relative pathname instead of absolute in changelog text\n" . $herecurr);
+       }
+}
+
 sub process {
        my $filename = shift;
 
@@ -933,11 +1158,14 @@ sub process {
        my $in_comment = 0;
        my $comment_edge = 0;
        my $first_line = 0;
+       my $p1_prefix = '';
 
        my $prev_values = 'E';
 
        # suppression flags
        my %suppress_ifbraces;
+       my %suppress_whiletrailers;
+       my %suppress_export;
 
        # Pre-scan the patch sanitizing the lines.
        # Pre-scan the patch looking for any __setup documentation.
@@ -972,10 +1200,18 @@ sub process {
                        # edge is a close comment then we must be in a comment
                        # at context start.
                        my $edge;
-                       for (my $ln = $linenr + 1; $ln < ($linenr + $realcnt); $ln++) {
-                               next if ($line =~ /^-/);
-                               ($edge) = ($rawlines[$ln - 1] =~ m@(/\*|\*/)@);
-                               last if (defined $edge);
+                       my $cnt = $realcnt;
+                       for (my $ln = $linenr + 1; $cnt > 0; $ln++) {
+                               next if (defined $rawlines[$ln - 1] &&
+                                        $rawlines[$ln - 1] =~ /^-/);
+                               $cnt--;
+                               #print "RAW<$rawlines[$ln - 1]>\n";
+                               last if (!defined $rawlines[$ln - 1]);
+                               if ($rawlines[$ln - 1] =~ m@(/\*|\*/)@ &&
+                                   $rawlines[$ln - 1] !~ m@"[^"]*(?:/\*|\*/)[^"]*"@) {
+                                       ($edge) = $1;
+                                       last;
+                               }
                        }
                        if (defined $edge && $edge eq '*/') {
                                $in_comment = 1;
@@ -985,7 +1221,7 @@ sub process {
                        # is the start of a diff block and this line starts
                        # ' *' then it is very likely a comment.
                        if (!defined $edge &&
-                           $rawlines[$linenr] =~ m@^.\s* \*(?:\s|$)@)
+                           $rawlines[$linenr] =~ m@^.\s*(?:\*\*+| \*)(?:\s|$)@)
                        {
                                $in_comment = 1;
                        }
@@ -1037,6 +1273,8 @@ sub process {
                        $prev_values = 'E';
 
                        %suppress_ifbraces = ();
+                       %suppress_whiletrailers = ();
+                       %suppress_export = ();
                        next;
 
 # track the line number as we move through the hunk, note that
@@ -1060,6 +1298,8 @@ sub process {
                        $realcnt--;
                }
 
+               my $hunk_line = ($realcnt != 0);
+
 #make up the handle for any error we report on this line
                $prefix = "$filename:$realline: " if ($emacs && $file);
                $prefix = "$filename:$linenr: " if ($emacs && !$file);
@@ -1070,9 +1310,15 @@ sub process {
                # extract the filename as it passes
                if ($line=~/^\+\+\+\s+(\S+)/) {
                        $realfile = $1;
-                       $realfile =~ s@^[^/]*/@@;
+                       $realfile =~ s@^([^/]*)/@@;
 
-                       if ($realfile =~ m@include/asm/@) {
+                       $p1_prefix = $1;
+                       if (!$file && $tree && $p1_prefix ne '' &&
+                           -e "$root/$p1_prefix") {
+                               WARN("patch prefix '$p1_prefix' exists, appears to be a -p0 patch\n");
+                       }
+
+                       if ($realfile =~ m@^include/asm/@) {
                                ERROR("do not modify files in include/asm, change architecture specific files in include/asm-<architecture>\n" . "$here$rawline\n");
                        }
                        next;
@@ -1106,6 +1352,20 @@ sub process {
                                $herecurr) if (!$emitted_corrupt++);
                }
 
+# Check for absolute kernel paths.
+               if ($tree) {
+                       while ($line =~ m{(?:^|\s)(/\S*)}g) {
+                               my $file = $1;
+
+                               if ($file =~ m{^(.*?)(?::\d+)+:?$} &&
+                                   check_absolute_file($1, $herecurr)) {
+                                       #
+                               } else {
+                                       check_absolute_file($file, $herecurr);
+                               }
+                       }
+               }
+
 # UTF-8 regex found at http://www.w3.org/International/questions/qa-forms-utf-8.en.php
                if (($realfile =~ /^$/ || $line =~ /^\+/) &&
                    $rawline !~ m/^$UTF8*$/) {
@@ -1118,11 +1378,8 @@ sub process {
                        ERROR("Invalid UTF-8, patch and commit message should be encoded in UTF-8\n" . $hereptr);
                }
 
-#ignore lines being removed
-               if ($line=~/^-/) {next;}
-
-# check we are in a valid source file if not then ignore this hunk
-               next if ($realfile !~ /\.(h|c|s|S|pl|sh)$/);
+# ignore non-hunk lines and lines being removed
+               next if (!$hunk_line || $line =~ /^-/);
 
 #trailing whitespace
                if ($line =~ /^\+.*\015/) {
@@ -1133,20 +1390,59 @@ sub process {
                        my $herevet = "$here\n" . cat_vet($rawline) . "\n";
                        ERROR("trailing whitespace\n" . $herevet);
                }
+
+# check for Kconfig help text having a real description
+               if ($realfile =~ /Kconfig/ &&
+                   $line =~ /\+?\s*(---)?help(---)?$/) {
+                       my $length = 0;
+                       for (my $l = $linenr; defined($lines[$l]); $l++) {
+                               my $f = $lines[$l];
+                               $f =~ s/#.*//;
+                               $f =~ s/^\s+//;
+                               next if ($f =~ /^$/);
+                               last if ($f =~ /^\s*config\s/);
+                               $length++;
+                       }
+                       WARN("please write a paragraph that describes the config symbol fully\n" . $herecurr) if ($length < 4);
+               }
+
+# check we are in a valid source file if not then ignore this hunk
+               next if ($realfile !~ /\.(h|c|s|S|pl|sh)$/);
+
 #80 column limit
                if ($line =~ /^\+/ && $prevrawline !~ /\/\*\*/ &&
-                   $rawline !~ /^.\s*\*\s*\@$Ident\s/ && $length > 80)
+                   $rawline !~ /^.\s*\*\s*\@$Ident\s/ &&
+                   !($line =~ /^\+\s*$logFunctions\s*\(\s*(?:(KERN_\S+\s*|[^"]*))?"[X\t]*"\s*(?:,|\)\s*;)\s*$/ ||
+                   $line =~ /^\+\s*"[^"]*"\s*(?:\s*|,|\)\s*;)\s*$/) &&
+                   $length > 80)
                {
                        WARN("line over 80 characters\n" . $herecurr);
                }
 
+# check for spaces before a quoted newline
+               if ($rawline =~ /^.*\".*\s\\n/) {
+                       WARN("unnecessary whitespace before a quoted newline\n" . $herecurr);
+               }
+
 # check for adding lines without a newline.
                if ($line =~ /^\+/ && defined $lines[$linenr] && $lines[$linenr] =~ /^\\ No newline at end of file/) {
                        WARN("adding a line without newline at end of file\n" . $herecurr);
                }
 
-# check we are in a valid source file *.[hc] if not then ignore this hunk
-               next if ($realfile !~ /\.[hc]$/);
+# Blackfin: use hi/lo macros
+               if ($realfile =~ m@arch/blackfin/.*\.S$@) {
+                       if ($line =~ /\.[lL][[:space:]]*=.*&[[:space:]]*0x[fF][fF][fF][fF]/) {
+                               my $herevet = "$here\n" . cat_vet($line) . "\n";
+                               ERROR("use the LO() macro, not (... & 0xFFFF)\n" . $herevet);
+                       }
+                       if ($line =~ /\.[hH][[:space:]]*=.*>>[[:space:]]*16/) {
+                               my $herevet = "$here\n" . cat_vet($line) . "\n";
+                               ERROR("use the HI() macro, not (... >> 16)\n" . $herevet);
+                       }
+               }
+
+# check we are in a valid source file C or perl if not then ignore this hunk
+               next if ($realfile !~ /\.(h|c|pl)$/);
 
 # at the beginning of a line any tabs must come first and anything
 # more than 8 must use tabs.
@@ -1156,19 +1452,54 @@ sub process {
                        ERROR("code indent should use tabs where possible\n" . $herevet);
                }
 
+# check for space before tabs.
+               if ($rawline =~ /^\+/ && $rawline =~ / \t/) {
+                       my $herevet = "$here\n" . cat_vet($rawline) . "\n";
+                       WARN("please, no space before tabs\n" . $herevet);
+               }
+
+# check for spaces at the beginning of a line.
+               if ($rawline =~ /^\+ / && $rawline !~ /\+ +\*/)  {
+                       my $herevet = "$here\n" . cat_vet($rawline) . "\n";
+                       WARN("please, no space for starting a line, \
+                               excluding comments\n" . $herevet);
+               }
+
+# check we are in a valid C source file if not then ignore this hunk
+               next if ($realfile !~ /\.(h|c)$/);
+
 # check for RCS/CVS revision markers
                if ($rawline =~ /^\+.*\$(Revision|Log|Id)(?:\$|)/) {
                        WARN("CVS style keyword markers, these will _not_ be updated\n". $herecurr);
                }
 
+# Blackfin: don't use __builtin_bfin_[cs]sync
+               if ($line =~ /__builtin_bfin_csync/) {
+                       my $herevet = "$here\n" . cat_vet($line) . "\n";
+                       ERROR("use the CSYNC() macro in asm/blackfin.h\n" . $herevet);
+               }
+               if ($line =~ /__builtin_bfin_ssync/) {
+                       my $herevet = "$here\n" . cat_vet($line) . "\n";
+                       ERROR("use the SSYNC() macro in asm/blackfin.h\n" . $herevet);
+               }
+
 # Check for potential 'bare' types
-               my ($stat, $cond);
+               my ($stat, $cond, $line_nr_next, $remain_next, $off_next,
+                   $realline_next);
                if ($realcnt && $line =~ /.\s*\S/) {
-                       ($stat, $cond) = ctx_statement_block($linenr,
-                                                               $realcnt, 0);
+                       ($stat, $cond, $line_nr_next, $remain_next, $off_next) =
+                               ctx_statement_block($linenr, $realcnt, 0);
                        $stat =~ s/\n./\n /g;
                        $cond =~ s/\n./\n /g;
 
+                       # Find the real next line.
+                       $realline_next = $line_nr_next;
+                       if (defined $realline_next &&
+                           (!defined $lines[$realline_next - 1] ||
+                            substr($lines[$realline_next - 1], $off_next) =~ /^\s*$/)) {
+                               $realline_next++;
+                       }
+
                        my $s = $stat;
                        $s =~ s/{.*$//s;
 
@@ -1178,19 +1509,21 @@ sub process {
                        # Ignore functions being called
                        } elsif ($s =~ /^.\s*$Ident\s*\(/s) {
 
+                       } elsif ($s =~ /^.\s*else\b/s) {
+
                        # declarations always start with types
-                       } elsif ($prev_values eq 'E' && $s =~ /^.\s*(?:$Storage\s+)?(?:$Inline\s+)?(?:const\s+)?((?:\s*$Ident)+)\b(?:\s+$Sparse)?\s*\**\s*(?:$Ident|\(\*[^\)]*\))\s*(?:;|=|,|\()/s) {
+                       } elsif ($prev_values eq 'E' && $s =~ /^.\s*(?:$Storage\s+)?(?:$Inline\s+)?(?:const\s+)?((?:\s*$Ident)+?)\b(?:\s+$Sparse)?\s*\**\s*(?:$Ident|\(\*[^\)]*\))(?:\s*$Modifier)?\s*(?:;|=|,|\()/s) {
                                my $type = $1;
                                $type =~ s/\s+/ /g;
                                possible($type, "A:" . $s);
 
                        # definitions in global scope can only start with types
-                       } elsif ($s =~ /^.(?:$Storage\s+)?(?:$Inline\s+)?(?:const\s+)?($Ident)\b/s) {
+                       } elsif ($s =~ /^.(?:$Storage\s+)?(?:$Inline\s+)?(?:const\s+)?($Ident)\b\s*(?!:)/s) {
                                possible($1, "B:" . $s);
                        }
 
                        # any (foo ... *) is a pointer cast, and foo is a type
-                       while ($s =~ /\(($Ident)(?:\s+$Sparse)*\s*\*+\s*\)/sg) {
+                       while ($s =~ /\(($Ident)(?:\s+$Sparse)*[\s\*]+\s*\)/sg) {
                                possible($1, "C:" . $s);
                        }
 
@@ -1246,17 +1579,22 @@ sub process {
                        my $pre_ctx = "$1$2";
 
                        my ($level, @ctx) = ctx_statement_level($linenr, $realcnt, 0);
-                       my $ctx_ln = $linenr + $#ctx + 1;
                        my $ctx_cnt = $realcnt - $#ctx - 1;
                        my $ctx = join("\n", @ctx);
 
-                       ##warn "realcnt<$realcnt> ctx_cnt<$ctx_cnt>\n";
+                       my $ctx_ln = $linenr;
+                       my $ctx_skip = $realcnt;
 
-                       # Skip over any removed lines in the context following statement.
-                       while (defined($lines[$ctx_ln - 1]) && $lines[$ctx_ln - 1] =~ /^-/) {
+                       while ($ctx_skip > $ctx_cnt || ($ctx_skip == $ctx_cnt &&
+                                       defined $lines[$ctx_ln - 1] &&
+                                       $lines[$ctx_ln - 1] =~ /^-/)) {
+                               ##print "SKIP<$ctx_skip> CNT<$ctx_cnt>\n";
+                               $ctx_skip-- if (!defined $lines[$ctx_ln - 1] || $lines[$ctx_ln - 1] !~ /^-/);
                                $ctx_ln++;
                        }
-                       ##warn "pre<$pre_ctx>\nline<$line>\nctx<$ctx>\nnext<$lines[$ctx_ln - 1]>\n";
+
+                       #print "realcnt<$realcnt> ctx_cnt<$ctx_cnt>\n";
+                       #print "pre<$pre_ctx>\nline<$line>\nctx<$ctx>\nnext<$lines[$ctx_ln - 1]>\n";
 
                        if ($ctx !~ /{\s*/ && defined($lines[$ctx_ln -1]) && $lines[$ctx_ln - 1] =~ /^\+\s*{/) {
                                ERROR("that open brace { should be on the previous line\n" .
@@ -1274,14 +1612,102 @@ sub process {
                        }
                }
 
+# Check relative indent for conditionals and blocks.
+               if ($line =~ /\b(?:(?:if|while|for)\s*\(|do\b)/ && $line !~ /^.\s*#/ && $line !~ /\}\s*while\s*/) {
+                       my ($s, $c) = ($stat, $cond);
+
+                       substr($s, 0, length($c), '');
+
+                       # Make sure we remove the line prefixes as we have
+                       # none on the first line, and are going to readd them
+                       # where necessary.
+                       $s =~ s/\n./\n/gs;
+
+                       # Find out how long the conditional actually is.
+                       my @newlines = ($c =~ /\n/gs);
+                       my $cond_lines = 1 + $#newlines;
+
+                       # We want to check the first line inside the block
+                       # starting at the end of the conditional, so remove:
+                       #  1) any blank line termination
+                       #  2) any opening brace { on end of the line
+                       #  3) any do (...) {
+                       my $continuation = 0;
+                       my $check = 0;
+                       $s =~ s/^.*\bdo\b//;
+                       $s =~ s/^\s*{//;
+                       if ($s =~ s/^\s*\\//) {
+                               $continuation = 1;
+                       }
+                       if ($s =~ s/^\s*?\n//) {
+                               $check = 1;
+                               $cond_lines++;
+                       }
+
+                       # Also ignore a loop construct at the end of a
+                       # preprocessor statement.
+                       if (($prevline =~ /^.\s*#\s*define\s/ ||
+                           $prevline =~ /\\\s*$/) && $continuation == 0) {
+                               $check = 0;
+                       }
+
+                       my $cond_ptr = -1;
+                       $continuation = 0;
+                       while ($cond_ptr != $cond_lines) {
+                               $cond_ptr = $cond_lines;
+
+                               # If we see an #else/#elif then the code
+                               # is not linear.
+                               if ($s =~ /^\s*\#\s*(?:else|elif)/) {
+                                       $check = 0;
+                               }
+
+                               # Ignore:
+                               #  1) blank lines, they should be at 0,
+                               #  2) preprocessor lines, and
+                               #  3) labels.
+                               if ($continuation ||
+                                   $s =~ /^\s*?\n/ ||
+                                   $s =~ /^\s*#\s*?/ ||
+                                   $s =~ /^\s*$Ident\s*:/) {
+                                       $continuation = ($s =~ /^.*?\\\n/) ? 1 : 0;
+                                       if ($s =~ s/^.*?\n//) {
+                                               $cond_lines++;
+                                       }
+                               }
+                       }
+
+                       my (undef, $sindent) = line_stats("+" . $s);
+                       my $stat_real = raw_line($linenr, $cond_lines);
+
+                       # Check if either of these lines are modified, else
+                       # this is not this patch's fault.
+                       if (!defined($stat_real) ||
+                           $stat !~ /^\+/ && $stat_real !~ /^\+/) {
+                               $check = 0;
+                       }
+                       if (defined($stat_real) && $cond_lines > 1) {
+                               $stat_real = "[...]\n$stat_real";
+                       }
+
+                       #print "line<$line> prevline<$prevline> indent<$indent> sindent<$sindent> check<$check> continuation<$continuation> s<$s> cond_lines<$cond_lines> stat_real<$stat_real> stat<$stat>\n";
+
+                       if ($check && (($sindent % 8) != 0 ||
+                           ($sindent <= $indent && $s ne ''))) {
+                               WARN("suspect code indent for conditional statements ($indent, $sindent)\n" . $herecurr . "$stat_real\n");
+                       }
+               }
+
                # Track the 'values' across context and added lines.
                my $opline = $line; $opline =~ s/^./ /;
-               my $curr_values = annotate_values($opline . "\n", $prev_values);
+               my ($curr_values, $curr_vars) =
+                               annotate_values($opline . "\n", $prev_values);
                $curr_values = $prev_values . $curr_values;
                if ($dbg_values) {
                        my $outline = $opline; $outline =~ s/\t/ /g;
                        print "$linenr > .$outline\n";
                        print "$linenr > $curr_values\n";
+                       print "$linenr >  $curr_vars\n";
                }
                $prev_values = substr($curr_values, -1);
 
@@ -1289,14 +1715,27 @@ sub process {
                if ($line=~/^[^\+]/) {next;}
 
 # TEST: allow direct testing of the type matcher.
-               if ($tst_type && $line =~ /^.$Declare$/) {
-                       ERROR("TEST: is type $Declare\n" . $herecurr);
+               if ($dbg_type) {
+                       if ($line =~ /^.\s*$Declare\s*$/) {
+                               ERROR("TEST: is type\n" . $herecurr);
+                       } elsif ($dbg_type > 1 && $line =~ /^.+($Declare)/) {
+                               ERROR("TEST: is not type ($1 is)\n". $herecurr);
+                       }
+                       next;
+               }
+# TEST: allow direct testing of the attribute matcher.
+               if ($dbg_attr) {
+                       if ($line =~ /^.\s*$Modifier\s*$/) {
+                               ERROR("TEST: is attr\n" . $herecurr);
+                       } elsif ($dbg_attr > 1 && $line =~ /^.+($Modifier)/) {
+                               ERROR("TEST: is not attr ($1 is)\n". $herecurr);
+                       }
                        next;
                }
 
 # check for initialisation to aggregates open brace on the next line
-               if ($prevline =~ /$Declare\s*$Ident\s*=\s*$/ &&
-                   $line =~ /^.\s*{/) {
+               if ($line =~ /^.\s*{/ &&
+                   $prevline =~ /(?:^|[^=])=\s*$/) {
                        ERROR("that open brace { should be on the previous line\n" . $hereprev);
                }
 
@@ -1321,28 +1760,48 @@ sub process {
                $line =~ s@//.*@@;
                $opline =~ s@//.*@@;
 
-#EXPORT_SYMBOL should immediately follow its function closing }.
-               if (($line =~ /EXPORT_SYMBOL.*\((.*)\)/) ||
-                   ($line =~ /EXPORT_UNUSED_SYMBOL.*\((.*)\)/)) {
+# EXPORT_SYMBOL should immediately follow the thing it is exporting, consider
+# the whole statement.
+#print "APW <$lines[$realline_next - 1]>\n";
+               if (defined $realline_next &&
+                   exists $lines[$realline_next - 1] &&
+                   !defined $suppress_export{$realline_next} &&
+                   ($lines[$realline_next - 1] =~ /EXPORT_SYMBOL.*\((.*)\)/ ||
+                    $lines[$realline_next - 1] =~ /EXPORT_UNUSED_SYMBOL.*\((.*)\)/)) {
                        my $name = $1;
-                       if (($prevline !~ /^}/) &&
-                          ($prevline !~ /^\+}/) &&
-                          ($prevline !~ /^ }/) &&
-                          ($prevline !~ /^.DECLARE_$Ident\(\Q$name\E\)/) &&
-                          ($prevline !~ /^.LIST_HEAD\(\Q$name\E\)/) &&
-                          ($prevline !~ /^.$Type\s*\(\s*\*\s*\Q$name\E\s*\)\s*\(/) &&
-                          ($prevline !~ /\b\Q$name\E(?:\s+$Attribute)?\s*(?:;|=|\[)/)) {
-                               WARN("EXPORT_SYMBOL(foo); should immediately follow its function/variable\n" . $herecurr);
+                       if ($stat !~ /(?:
+                               \n.}\s*$|
+                               ^.DEFINE_$Ident\(\Q$name\E\)|
+                               ^.DECLARE_$Ident\(\Q$name\E\)|
+                               ^.LIST_HEAD\(\Q$name\E\)|
+                               ^.(?:$Storage\s+)?$Type\s*\(\s*\*\s*\Q$name\E\s*\)\s*\(|
+                               \b\Q$name\E(?:\s+$Attribute)*\s*(?:;|=|\[|\()
+                           )/x) {
+#print "FOO A<$lines[$realline_next - 1]> stat<$stat> name<$name>\n";
+                               $suppress_export{$realline_next} = 2;
+                       } else {
+                               $suppress_export{$realline_next} = 1;
                        }
                }
+               if (!defined $suppress_export{$linenr} &&
+                   $prevline =~ /^.\s*$/ &&
+                   ($line =~ /EXPORT_SYMBOL.*\((.*)\)/ ||
+                    $line =~ /EXPORT_UNUSED_SYMBOL.*\((.*)\)/)) {
+#print "FOO B <$lines[$linenr - 1]>\n";
+                       $suppress_export{$linenr} = 2;
+               }
+               if (defined $suppress_export{$linenr} &&
+                   $suppress_export{$linenr} == 2) {
+                       WARN("EXPORT_SYMBOL(foo); should immediately follow its function/variable\n" . $herecurr);
+               }
 
-# check for external initialisers.
+# check for global initialisers.
                if ($line =~ /^.$Type\s*$Ident\s*(?:\s+$Modifier)*\s*=\s*(0|NULL|false)\s*;/) {
-                       ERROR("do not initialise externals to 0 or NULL\n" .
+                       ERROR("do not initialise globals to 0 or NULL\n" .
                                $herecurr);
                }
 # check for static initialisers.
-               if ($line =~ /\s*static\s.*=\s*(0|NULL|false)\s*;/) {
+               if ($line =~ /\bstatic\s.*=\s*(0|NULL|false)\s*;/) {
                        ERROR("do not initialise statics to 0 or NULL\n" .
                                $herecurr);
                }
@@ -1350,28 +1809,47 @@ sub process {
 # check for new typedefs, only function parameters and sparse annotations
 # make sense.
                if ($line =~ /\btypedef\s/ &&
-                   $line !~ /\btypedef\s+$Type\s+\(\s*\*?$Ident\s*\)\s*\(/ &&
+                   $line !~ /\btypedef\s+$Type\s*\(\s*\*?$Ident\s*\)\s*\(/ &&
                    $line !~ /\btypedef\s+$Type\s+$Ident\s*\(/ &&
+                   $line !~ /\b$typeTypedefs\b/ &&
                    $line !~ /\b__bitwise(?:__|)\b/) {
                        WARN("do not add new typedefs\n" . $herecurr);
                }
 
 # * goes on variable not on type
-               if ($line =~ m{\($NonptrType(\*+)(?:\s+const)?\)}) {
-                       ERROR("\"(foo$1)\" should be \"(foo $1)\"\n" .
-                               $herecurr);
+               # (char*[ const])
+               if ($line =~ m{\($NonptrType(\s*(?:$Modifier\b\s*|\*\s*)+)\)}) {
+                       my ($from, $to) = ($1, $1);
 
-               } elsif ($line =~ m{\($NonptrType\s+(\*+)(?!\s+const)\s+\)}) {
-                       ERROR("\"(foo $1 )\" should be \"(foo $1)\"\n" .
-                               $herecurr);
+                       # Should start with a space.
+                       $to =~ s/^(\S)/ $1/;
+                       # Should not end with a space.
+                       $to =~ s/\s+$//;
+                       # '*'s should not have spaces between.
+                       while ($to =~ s/\*\s+\*/\*\*/) {
+                       }
 
-               } elsif ($line =~ m{$NonptrType(\*+)(?:\s+(?:$Attribute|$Sparse))?\s+[A-Za-z\d_]+}) {
-                       ERROR("\"foo$1 bar\" should be \"foo $1bar\"\n" .
-                               $herecurr);
+                       #print "from<$from> to<$to>\n";
+                       if ($from ne $to) {
+                               ERROR("\"(foo$from)\" should be \"(foo$to)\"\n" .  $herecurr);
+                       }
+               } elsif ($line =~ m{\b$NonptrType(\s*(?:$Modifier\b\s*|\*\s*)+)($Ident)}) {
+                       my ($from, $to, $ident) = ($1, $1, $2);
 
-               } elsif ($line =~ m{$NonptrType\s+(\*+)(?!\s+(?:$Attribute|$Sparse))\s+[A-Za-z\d_]+}) {
-                       ERROR("\"foo $1 bar\" should be \"foo $1bar\"\n" .
-                               $herecurr);
+                       # Should start with a space.
+                       $to =~ s/^(\S)/ $1/;
+                       # Should not end with a space.
+                       $to =~ s/\s+$//;
+                       # '*'s should not have spaces between.
+                       while ($to =~ s/\*\s+\*/\*\*/) {
+                       }
+                       # Modifiers should have spaces.
+                       $to =~ s/(\b$Modifier$)/$1 /;
+
+                       #print "from<$from> to<$to> ident<$ident>\n";
+                       if ($from ne $to && $ident !~ /^$Modifier$/) {
+                               ERROR("\"foo${from}bar\" should be \"foo${to}bar\"\n" .  $herecurr);
+                       }
                }
 
 # # no BUG() or BUG_ON()
@@ -1421,6 +1899,19 @@ sub process {
                        ERROR("open brace '{' following $1 go on the same line\n" . $hereprev);
                }
 
+# check for spacing round square brackets; allowed:
+#  1. with a type on the left -- int [] a;
+#  2. at the beginning of a line for slice initialisers -- [0...10] = 5,
+#  3. inside a curly brace -- = { [0...10] = 5 }
+               while ($line =~ /(.*?\s)\[/g) {
+                       my ($where, $prefix) = ($-[1], $1);
+                       if ($prefix !~ /$Type\s+$/ &&
+                           ($where != 0 || $prefix !~ /^.\s+$/) &&
+                           $prefix !~ /{\s+$/) {
+                               ERROR("space prohibited before open square bracket '['\n" . $herecurr);
+                       }
+               }
+
 # check for spaces between functions and their parentheses.
                while ($line =~ /($Ident)\s+\(/g) {
                        my $name = $1;
@@ -1457,7 +1948,8 @@ sub process {
                                <<=|>>=|<=|>=|==|!=|
                                \+=|-=|\*=|\/=|%=|\^=|\|=|&=|
                                =>|->|<<|>>|<|>|=|!|~|
-                               &&|\|\||,|\^|\+\+|--|&|\||\+|-|\*|\/|%
+                               &&|\|\||,|\^|\+\+|--|&|\||\+|-|\*|\/|%|
+                               \?|:
                        }x;
                        my @elements = split(/($ops|;)/, $opline);
                        my $off = 0;
@@ -1492,7 +1984,7 @@ sub process {
                                        $c = 'C' if ($elements[$n + 2] =~ /^$;/);
                                        $c = 'B' if ($elements[$n + 2] =~ /^(\)|\]|;)/);
                                        $c = 'O' if ($elements[$n + 2] eq '');
-                                       $c = 'E' if ($elements[$n + 2] =~ /\s*\\$/);
+                                       $c = 'E' if ($elements[$n + 2] =~ /^\s*\\$/);
                                } else {
                                        $c = 'E';
                                }
@@ -1504,22 +1996,11 @@ sub process {
                                my $ptr = substr($blank, 0, $off) . "^";
                                my $hereptr = "$hereline$ptr\n";
 
-                               # Classify operators into binary, unary, or
-                               # definitions (* only) where they have more
-                               # than one mode.
+                               # Pull out the value of this operator.
                                my $op_type = substr($curr_values, $off + 1, 1);
-                               my $op_left = substr($curr_values, $off, 1);
-                               my $is_unary;
-                               if ($op_type eq 'T') {
-                                       $is_unary = 2;
-                               } elsif ($op_left eq 'V') {
-                                       $is_unary = 0;
-                               } else {
-                                       $is_unary = 1;
-                               }
-                               #if ($op eq '-' || $op eq '&' || $op eq '*') {
-                               #       print "UNARY: <$op_left$op_type $is_unary $a:$op:$c> <$ca:$op:$cc> <$unary_ctx>\n";
-                               #}
+
+                               # Get the full operator variant.
+                               my $opv = $op . substr($curr_vars, $off, 1);
 
                                # Ignore operators passed as parameters.
                                if ($op_type ne 'V' &&
@@ -1538,8 +2019,10 @@ sub process {
                                # // is a comment
                                } elsif ($op eq '//') {
 
-                               # -> should have no spaces
-                               } elsif ($op eq '->') {
+                               # No spaces for:
+                               #   ->
+                               #   :   when part of a bitfield
+                               } elsif ($op eq '->' || $opv eq ':B') {
                                        if ($ctx =~ /Wx.|.xW/) {
                                                ERROR("spaces prohibited around that '$op' $at\n" . $hereptr);
                                        }
@@ -1551,18 +2034,19 @@ sub process {
                                        }
 
                                # '*' as part of a type definition -- reported already.
-                               } elsif ($op eq '*' && $is_unary == 2) {
+                               } elsif ($opv eq '*_') {
                                        #warn "'*' is part of type\n";
 
                                # unary operators should have a space before and
                                # none after.  May be left adjacent to another
                                # unary operator, or a cast
                                } elsif ($op eq '!' || $op eq '~' ||
-                                        ($is_unary && ($op eq '*' || $op eq '-' || $op eq '&'))) {
+                                        $opv eq '*U' || $opv eq '-U' ||
+                                        $opv eq '&U' || $opv eq '&&U') {
                                        if ($ctx !~ /[WEBC]x./ && $ca !~ /(?:\)|!|~|\*|-|\&|\||\+\+|\-\-|\{)$/) {
                                                ERROR("space required before that '$op' $at\n" . $hereptr);
                                        }
-                                       if ($op  eq '*' && $cc =~/\s*const\b/) {
+                                       if ($op eq '*' && $cc =~/\s*$Modifier\b/) {
                                                # A unary '*' may be const
 
                                        } elsif ($ctx =~ /.xW/) {
@@ -1595,11 +2079,33 @@ sub process {
                                                        $hereptr);
                                        }
 
+                               # A colon needs no spaces before when it is
+                               # terminating a case value or a label.
+                               } elsif ($opv eq ':C' || $opv eq ':L') {
+                                       if ($ctx =~ /Wx./) {
+                                               ERROR("space prohibited before that '$op' $at\n" . $hereptr);
+                                       }
+
                                # All the others need spaces both sides.
                                } elsif ($ctx !~ /[EWC]x[CWE]/) {
+                                       my $ok = 0;
+
                                        # Ignore email addresses <foo@bar>
-                                       if (!($op eq '<' && $cb =~ /$;\S+\@\S+>/) &&
-                                           !($op eq '>' && $cb =~ /<\S+\@\S+$;/)) {
+                                       if (($op eq '<' &&
+                                            $cc =~ /^\S+\@\S+>/) ||
+                                           ($op eq '>' &&
+                                            $ca =~ /<\S+\@\S+$/))
+                                       {
+                                               $ok = 1;
+                                       }
+
+                                       # Ignore ?:
+                                       if (($opv eq ':O' && $ca =~ /\?$/) ||
+                                           ($op eq '?' && $cc =~ /^:/)) {
+                                               $ok = 1;
+                                       }
+
+                                       if ($ok == 0) {
                                                ERROR("spaces required around that '$op' $at\n" . $hereptr);
                                        }
                                }
@@ -1669,8 +2175,13 @@ sub process {
                        my $spacing = $1;
                        my $value = $2;
 
-                       # Flatten any parentheses and braces
-                       while ($value =~ s/\([^\(\)]*\)/1/) {
+                       # Flatten any parentheses
+                       $value =~ s/\)\(/\) \(/g;
+                       while ($value =~ s/\[[^\{\}]*\]/1/ ||
+                              $value !~ /(?:$Ident|-?$Constant)\s*
+                                            $Compare\s*
+                                            (?:$Ident|-?$Constant)/x &&
+                              $value =~ s/\([^\(\)]*\)/1/) {
                        }
 
                        if ($value =~ /^(?:$Ident|-?$Constant)$/) {
@@ -1686,11 +2197,31 @@ sub process {
                        ERROR("space required before the open parenthesis '('\n" . $herecurr);
                }
 
-# Check for illegal assignment in if conditional.
-               if ($line =~ /\bif\s*\(/) {
+# Check for illegal assignment in if conditional -- and check for trailing
+# statements after the conditional.
+               if ($line =~ /do\s*(?!{)/) {
+                       my ($stat_next) = ctx_statement_block($line_nr_next,
+                                               $remain_next, $off_next);
+                       $stat_next =~ s/\n./\n /g;
+                       ##print "stat<$stat> stat_next<$stat_next>\n";
+
+                       if ($stat_next =~ /^\s*while\b/) {
+                               # If the statement carries leading newlines,
+                               # then count those as offsets.
+                               my ($whitespace) =
+                                       ($stat_next =~ /^((?:\s*\n[+-])*\s*)/s);
+                               my $offset =
+                                       statement_rawlines($whitespace) - 1;
+
+                               $suppress_whiletrailers{$line_nr_next +
+                                                               $offset} = 1;
+                       }
+               }
+               if (!defined $suppress_whiletrailers{$linenr} &&
+                   $line =~ /\b(?:if|while|for)\s*\(/ && $line !~ /^.\s*#/) {
                        my ($s, $c) = ($stat, $cond);
 
-                       if ($c =~ /\bif\s*\(.*[^<>!=]=[^=].*/) {
+                       if ($c =~ /\bif\s*\(.*[^<>!=]=[^=].*/s) {
                                ERROR("do not use assignment in if condition\n" . $herecurr);
                        }
 
@@ -1699,10 +2230,21 @@ sub process {
                        substr($s, 0, length($c), '');
                        $s =~ s/\n.*//g;
                        $s =~ s/$;//g;  # Remove any comments
-                       if (length($c) && $s !~ /^\s*({|;|)\s*\\*\s*$/ &&
-                           $c !~ /^.\s*\#\s*if/)
+                       if (length($c) && $s !~ /^\s*{?\s*\\*\s*$/ &&
+                           $c !~ /}\s*while\s*/)
                        {
-                               ERROR("trailing statements should be on next line\n" . $herecurr);
+                               # Find out how long the conditional actually is.
+                               my @newlines = ($c =~ /\n/gs);
+                               my $cond_lines = 1 + $#newlines;
+                               my $stat_real = '';
+
+                               $stat_real = raw_line($linenr, $cond_lines)
+                                                       . "\n" if ($cond_lines);
+                               if (defined($stat_real) && $cond_lines > 1) {
+                                       $stat_real = "[...]\n$stat_real";
+                               }
+
+                               ERROR("trailing statements should be on next line\n" . $herecurr . $stat_real);
                        }
                }
 
@@ -1729,6 +2271,20 @@ sub process {
                                ERROR("trailing statements should be on next line\n" . $herecurr);
                        }
                }
+# if should not continue a brace
+               if ($line =~ /}\s*if\b/) {
+                       ERROR("trailing statements should be on next line\n" .
+                               $herecurr);
+               }
+# case and default should not have general statements after them
+               if ($line =~ /^.\s*(?:case\s*.*|default\s*):/g &&
+                   $line !~ /\G(?:
+                       (?:\s*$;*)(?:\s*{)?(?:\s*$;*)(?:\s*\\)?\s*$|
+                       \s*return\s+
+                   )/xg)
+               {
+                       ERROR("trailing statements should be on next line\n" . $herecurr);
+               }
 
                # Check for }<nl>else {, these must be at the same
                # indent level to be relevant to each other.
@@ -1765,19 +2321,25 @@ sub process {
 
 #warn if <asm/foo.h> is #included and <linux/foo.h> is available (uses RAW line)
                if ($tree && $rawline =~ m{^.\s*\#\s*include\s*\<asm\/(.*)\.h\>}) {
-                       my $checkfile = "include/linux/$1.h";
-                       if (-f "$root/$checkfile" && $realfile ne $checkfile &&
-                           $1 ne 'irq')
+                       my $file = "$1.h";
+                       my $checkfile = "include/linux/$file";
+                       if (-f "$root/$checkfile" &&
+                           $realfile ne $checkfile &&
+                           $1 !~ /$allowed_asm_includes/)
                        {
-                               WARN("Use #include <linux/$1.h> instead of <asm/$1.h>\n" .
-                                       $herecurr);
+                               if ($realfile =~ m{^arch/}) {
+                                       CHK("Consider using #include <linux/$file> instead of <asm/$file>\n" . $herecurr);
+                               } else {
+                                       WARN("Use #include <linux/$file> instead of <asm/$file>\n" . $herecurr);
+                               }
                        }
                }
 
 # multi-statement macros should be enclosed in a do while loop, grab the
 # first statement and ensure its the whole macro if its not enclosed
 # in a known good container
-               if ($line =~ /^.\s*\#\s*define\s*$Ident(\()?/) {
+               if ($realfile !~ m@/vmlinux.lds.h$@ &&
+                   $line =~ /^.\s*\#\s*define\s*$Ident(\()?/) {
                        my $ln = $linenr;
                        my $cnt = $realcnt;
                        my ($off, $dstat, $dcond, $rest);
@@ -1791,30 +2353,26 @@ sub process {
                                $lines[$ln - 1] =~ /^(?:-|..*\\$)/)
                        {
                                $ctx .= $rawlines[$ln - 1] . "\n";
+                               $cnt-- if ($lines[$ln - 1] !~ /^-/);
                                $ln++;
-                               $cnt--;
                        }
                        $ctx .= $rawlines[$ln - 1];
 
                        ($dstat, $dcond, $ln, $cnt, $off) =
                                ctx_statement_block($linenr, $ln - $linenr + 1, 0);
                        #print "dstat<$dstat> dcond<$dcond> cnt<$cnt> off<$off>\n";
-                       #print "LINE<$lines[$ln]> len<" . length($lines[$ln]) . "\n";
+                       #print "LINE<$lines[$ln-1]> len<" . length($lines[$ln-1]) . "\n";
 
                        # Extract the remainder of the define (if any) and
                        # rip off surrounding spaces, and trailing \'s.
                        $rest = '';
-                       if (defined $lines[$ln - 1] &&
-                           $off > length($lines[$ln - 1]))
-                       {
-                               $ln++;
-                               $cnt--;
-                               $off = 0;
-                       }
-                       while ($cnt > 0) {
-                               $rest .= substr($lines[$ln - 1], $off) . "\n";
+                       while ($off != 0 || ($cnt > 0 && $rest =~ /\\\s*$/)) {
+                               #print "ADDING cnt<$cnt> $off <" . substr($lines[$ln - 1], $off) . "> rest<$rest>\n";
+                               if ($off != 0 || $lines[$ln - 1] !~ /^-/) {
+                                       $rest .= substr($lines[$ln - 1], $off) . "\n";
+                                       $cnt--;
+                               }
                                $ln++;
-                               $cnt--;
                                $off = 0;
                        }
                        $rest =~ s/\\\n.//g;
@@ -1827,14 +2385,16 @@ sub process {
                        } else {
                                $dstat =~ s/^.\s*\#\s*define\s+$Ident\s*//;
                        }
+                       $dstat =~ s/$;//g;
                        $dstat =~ s/\\\n.//g;
                        $dstat =~ s/^\s*//s;
                        $dstat =~ s/\s*$//s;
 
                        # Flatten any parentheses and braces
-                       while ($dstat =~ s/\([^\(\)]*\)/1/) {
-                       }
-                       while ($dstat =~ s/\{[^\{\}]*\}/1/) {
+                       while ($dstat =~ s/\([^\(\)]*\)/1/ ||
+                              $dstat =~ s/\{[^\{\}]*\}/1/ ||
+                              $dstat =~ s/\[[^\{\}]*\]/1/)
+                       {
                        }
 
                        my $exceptions = qr{
@@ -1843,8 +2403,13 @@ sub process {
                                MODULE_PARAM_DESC|
                                DECLARE_PER_CPU|
                                DEFINE_PER_CPU|
-                               __typeof__\(
+                               __typeof__\(|
+                               union|
+                               struct|
+                               \.$Ident\s*=\s*|
+                               ^\"|\"$
                        }x;
+                       #print "REST<$rest> dstat<$dstat>\n";
                        if ($rest ne '') {
                                if ($rest !~ /while\s*\(/ &&
                                    $dstat !~ /$exceptions/)
@@ -1856,6 +2421,7 @@ sub process {
                                if ($dstat ne '' &&
                                    $dstat !~ /^(?:$Ident|-?$Constant)$/ &&
                                    $dstat !~ /$exceptions/ &&
+                                   $dstat !~ /^\.$Ident\s*=/ &&
                                    $dstat =~ /$Operators/)
                                {
                                        ERROR("Macros with complex values should be enclosed in parenthesis\n" . "$here\n$ctx\n");
@@ -1863,6 +2429,15 @@ sub process {
                        }
                }
 
+# make sure symbols are always wrapped with VMLINUX_SYMBOL() ...
+# all assignments may have only one of the following with an assignment:
+#      .
+#      ALIGN(...)
+#      VMLINUX_SYMBOL(...)
+               if ($realfile eq 'vmlinux.lds.h' && $line =~ /(?:(?:^|\s)$Ident\s*=|=\s*$Ident(?:\s|$))/) {
+                       WARN("vmlinux.lds.h needs VMLINUX_SYMBOL() around C-visible symbols\n" . $herecurr);
+               }
+
 # check for redundant bracing round if etc
                if ($line =~ /(^.*)\bif\b/ && $1 !~ /else\s*$/) {
                        my ($level, $endln, @chunks) =
@@ -1956,10 +2531,10 @@ sub process {
                        }
                        if ($level == 0 && $block =~ /^\s*\{/ && !$allowed) {
                                my $herectx = $here . "\n";;
-                               my $end = $linenr + statement_rawlines($block) - 1;
+                               my $cnt = statement_rawlines($block);
 
-                               for (my $ln = $linenr - 1; $ln < $end; $ln++) {
-                                       $herectx .= $rawlines[$ln] . "\n";;
+                               for (my $n = 0; $n < $cnt; $n++) {
+                                       $herectx .= raw_line($linenr, $n) . "\n";;
                                }
 
                                WARN("braces {} are not necessary for single statement blocks\n" . $herectx);
@@ -2001,7 +2576,29 @@ sub process {
                if ($prevline =~ /\bif\s*\(([^\)]*)\)/) {
                        my $expr = $1;
                        if ($line =~ /\bkfree\(\Q$expr\E\);/) {
-                               WARN("kfree(NULL) is safe this check is probabally not required\n" . $hereprev);
+                               WARN("kfree(NULL) is safe this check is probably not required\n" . $hereprev);
+                       }
+               }
+# check for needless usb_free_urb() checks
+               if ($prevline =~ /\bif\s*\(([^\)]*)\)/) {
+                       my $expr = $1;
+                       if ($line =~ /\busb_free_urb\(\Q$expr\E\);/) {
+                               WARN("usb_free_urb(NULL) is safe this check is probably not required\n" . $hereprev);
+                       }
+               }
+
+# prefer usleep_range over udelay
+               if ($line =~ /\budelay\s*\(\s*(\w+)\s*\)/) {
+                       # ignore udelay's < 10, however
+                       if (! (($1 =~ /(\d+)/) && ($1 < 10)) ) {
+                               CHK("usleep_range is preferred over udelay; see Documentation/timers/timers-howto.txt\n" . $line);
+                       }
+               }
+
+# warn about unexpectedly long msleep's
+               if ($line =~ /\bmsleep\s*\((\d+)\);/) {
+                       if ($1 < 20) {
+                               WARN("msleep < 20ms can sleep for up to 20ms; see Documentation/timers/timers-howto.txt\n" . $line);
                        }
                }
 
@@ -2036,6 +2633,11 @@ sub process {
                        CHK("architecture specific defines should be avoided\n" .  $herecurr);
                }
 
+# Check that the storage class is at the beginning of a declaration
+               if ($line =~ /\b$Storage\b/ && $line !~ /^.\s*$Storage\b/) {
+                       WARN("storage class should be at the beginning of the declaration\n" . $herecurr)
+               }
+
 # check the location of the inline attribute, that it is between
 # storage class and type.
                if ($line =~ /\b$Type\s+$Inline\b/ ||
@@ -2048,6 +2650,11 @@ sub process {
                        WARN("plain inline is preferred over $1\n" . $herecurr);
                }
 
+# check for sizeof(&)
+               if ($line =~ /\bsizeof\s*\(\s*\&/) {
+                       WARN("sizeof(& should be avoided\n" . $herecurr);
+               }
+
 # check for new externs in .c files.
                if ($realfile =~ /\.c$/ && defined $stat &&
                    $stat =~ /^.\s*(?:extern\s+)?$Type\s+($Ident)(\s*)\(/s)
@@ -2101,11 +2708,59 @@ sub process {
 # check for semaphores used as mutexes
                if ($line =~ /^.\s*init_MUTEX_LOCKED\s*\(/) {
                        WARN("consider using a completion\n" . $herecurr);
+
                }
 # recommend strict_strto* over simple_strto*
                if ($line =~ /\bsimple_(strto.*?)\s*\(/) {
                        WARN("consider using strict_$1 in preference to simple_$1\n" . $herecurr);
                }
+# check for __initcall(), use device_initcall() explicitly please
+               if ($line =~ /^.\s*__initcall\s*\(/) {
+                       WARN("please use device_initcall() instead of __initcall()\n" . $herecurr);
+               }
+# check for various ops structs, ensure they are const.
+               my $struct_ops = qr{acpi_dock_ops|
+                               address_space_operations|
+                               backlight_ops|
+                               block_device_operations|
+                               dentry_operations|
+                               dev_pm_ops|
+                               dma_map_ops|
+                               extent_io_ops|
+                               file_lock_operations|
+                               file_operations|
+                               hv_ops|
+                               ide_dma_ops|
+                               intel_dvo_dev_ops|
+                               item_operations|
+                               iwl_ops|
+                               kgdb_arch|
+                               kgdb_io|
+                               kset_uevent_ops|
+                               lock_manager_operations|
+                               microcode_ops|
+                               mtrr_ops|
+                               neigh_ops|
+                               nlmsvc_binding|
+                               pci_raw_ops|
+                               pipe_buf_operations|
+                               platform_hibernation_ops|
+                               platform_suspend_ops|
+                               proto_ops|
+                               rpc_pipe_ops|
+                               seq_operations|
+                               snd_ac97_build_ops|
+                               soc_pcmcia_socket_ops|
+                               stacktrace_ops|
+                               sysfs_ops|
+                               tty_operations|
+                               usb_mon_operations|
+                               wd_ops}x;
+               if ($line !~ /\bconst\b/ &&
+                   $line =~ /\bstruct\s+($struct_ops)\b/) {
+                       WARN("struct $1 should normally be const\n" .
+                               $herecurr);
+               }
 
 # use of NR_CPUS is usually wrong
 # ignore definitions of NR_CPUS and usage to define arrays as likely right
@@ -2123,11 +2778,31 @@ sub process {
                my $string;
                while ($line =~ /(?:^|")([X\t]*)(?:"|$)/g) {
                        $string = substr($rawline, $-[1], $+[1] - $-[1]);
+                       $string =~ s/%%/__/g;
                        if ($string =~ /(?<!%)%L[udi]/) {
                                WARN("\%Ld/%Lu are not-standard C, use %lld/%llu\n" . $herecurr);
                                last;
                        }
                }
+
+# whine mightly about in_atomic
+               if ($line =~ /\bin_atomic\s*\(/) {
+                       if ($realfile =~ m@^drivers/@) {
+                               ERROR("do not use in_atomic in drivers\n" . $herecurr);
+                       } elsif ($realfile !~ m@^kernel/@) {
+                               WARN("use of in_atomic() is incorrect outside core kernel code\n" . $herecurr);
+                       }
+               }
+
+# check for lockdep_set_novalidate_class
+               if ($line =~ /^.\s*lockdep_set_novalidate_class\s*\(/ ||
+                   $line =~ /__lockdep_no_validate__\s*\)/ ) {
+                       if ($realfile !~ m@^kernel/lockdep@ &&
+                           $realfile !~ m@^include/linux/lockdep@ &&
+                           $realfile !~ m@^drivers/base/core@) {
+                               ERROR("lockdep_no_validate class is reserved for device->mutex.\n" . $herecurr);
+                       }
+               }
        }
 
        # If we have no input at all, then there is nothing to report on