urlscan-0.5.6/0000755000000000000000000000000010767056240010066 5ustar urlscan-0.5.6/COPYING0000644000000000000000000004310310537127673011126 0ustar GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. urlscan-0.5.6/README0000644000000000000000000000526010537112015010735 0ustar urlscan Daniel Burrows 0) Purpose and Requirements urlscan is a small program that is designed to integrate with the "mutt" mailreader to allow you to easily launch a Web browser for URLs contained in email messages. It is a replacement for the "urlview" program. urlscan requires Python and the python-urwid library, as well as sensible-browser from the debianutils package. 1) Features urlscan parses an email message passed on standard input and scans it for URLs. It then displays the URLs and their context within the message, and allows you to choose one or more URLs to send to your Web browser. Relative to urlview, urlscan has the following additional features: (1) Support for emails in quoted-printable and base64 encodings. No more stripping out =40D from URLs by hand! (2) The context of each URL is provided along with the URL. For HTML mails, a crude parser is used to render the HTML into text. 2) Setting up urlscan To set up urlscan, install the Debian "urlscan" package (or use setup.py to install the program). Once urlscan is installed, add the following lines to your .muttrc: macro index,pager \cb " urlscan" "call urlscan to extract URLs out of a message" macro attach,compose \cb " urlscan" "call urlscan to extract URLs out of a message" Once this is done, Control-b while reading mail in mutt will automatically invoke urlscan on the message. urlscan uses sensible-browser to invoke the default Web browser of the current environment. To choose a particular browser, set the environment variable BROWSER; e.g., export BROWSER=/usr/bin/epiphany . 3) Known bugs and limitations (1) Because the Python curses module does not support wide characters (see Debian bug #336861), non-ASCII characters can cause unpredictable results in urlscan. This problem will go away if Python and urwid are patched to support wide characters. (6) Running urlscan sometimes "messes up" the terminal background. This seems to be an urwid bug, but I haven't tracked down just what's going on. (2) Extraction of context from HTML messages leaves something to be desired. Probably the ideal solution would be to extract context on a word basis rather than on a paragraph basis. (3) The HTML message handling is a bit kludgy in general. (4) multipart/alternative sections are handled by descending into all the sub-parts, rather than just picking one, which may lead to URLs and context appearing twice. (5) Configurability is more than a little bit lacking. urlscan-0.5.6/urlscan.10000644000000000000000000000362110561031021011577 0ustar .\" Hey, EMACS: -*- nroff -*- .TH URLSCAN 1 "December 10, 2006" .SH NAME urlscan \- browse the URLs in an email message from a terminal .SH SYNOPSIS \fBurlscan\fR [options] < .I message \fBurlscan\fR [options] .I message .SH DESCRIPTION \fBurlscan\fR accepts a single email message on standard input, then displays a terminal-based list of the URLs in the given message. Selecting a URL will invoke \fBsensible-browser\fR(1) on it (and hence any browser specified in the \fBBROWSER\fR environment variable). \fBurlscan\fR is primarily intended to be used with the .B mutt (1) mailreader, but it should work well with any terminal-based mail program. \fBurlscan\fR is similar to \fBurlview\fR(1), but has the following additional features: \fB1.\fR Support for more message encodings, such as quoted-printable and base64. \fB2.\fR Extraction and display of the context surrounding each URL. .SH OPTIONS .TP .B \-b, \-\-background Run the Web browser in the background, so you can select another URL without closing it (this will not work with terminal-based Web browsers such as lynx, links, or w3m). .TP .B \-c, \-\-compact Display a simple list of the extracted URLs, instead of showing the context of each URL. .SH MUTT INTEGRATION To integrate urlscan with mutt, include the following two commands in \fB~/.muttrc\fR: .ad l macro index,pager \\cb " urlscan" "call urlscan to extract URLs out of a message" macro attach,compose \\cb " urlscan" "call urlscan to extract URLs out of a message" .ad b Once these lines are in your mutt configuration file, pressing Control-b will allow you to browse and open the URLs in the currently selected message. .SH SEE ALSO \fI/usr/share/doc/urlscan/README\fR, \fBsensible-browser\fR(1), \fBurlview\fR(1), \fBmutt\fR(1) .SH AUTHOR This manual page was written by Daniel Burrows . urlscan-0.5.6/_darcs/0000755000000000000000000000000010561406350011312 5ustar urlscan-0.5.6/_darcs/inventories/0000755000000000000000000000000010561406316013661 5ustar urlscan-0.5.6/_darcs/inventories/20061211004026-3fcb1-19f8e5888bb8dbdc0539480763c2e6fa6d173693.gz0000644000000000000000000001250510561406316024025 0ustar [Add simple code to extract URLs from emails. Daniel Burrows **20061113053419] [Add a simple wrapper module to start a Web browser. Daniel Burrows **20061113053437] [Add a first cut at an urwid-based lister for the URLs extracted from a message. Daniel Burrows **20061203053211] [Fix a docstring typo. Daniel Burrows **20061203061302] [Correctly handle the case of a message containing a line that just has a URL. Daniel Burrows **20061203061321] [Improve the markup used to render URL references. Daniel Burrows **20061204051855] [Rewrite the URL scanning algorithm to (a) be correct, and (b) not interpolate URLs into the text up-front. The latter point means that the display code can later interpolate URL footnotes with proper markup/active behavior (and it starts to now). Daniel Burrows **20061208051841] [Use proper grammar when saying how many URLs we have. Daniel Burrows **20061208052004] [For debugging, make repr(chunk) print something readable. Daniel Burrows **20061208052016] [Make the URL regexp more permissive, to include some common CGI metacharacters (= and ;). Daniel Burrows **20061208052215] [Use buttons instead of Text objects to display urls, so they can actually be selected (yay). Daniel Burrows **20061209041111] [Always focus the first button (if there is one). Daniel Burrows **20061209042206] [Fix browseto: spawnl needs its first argument to indicate whether to wait for the subprocess. Daniel Burrows **20061209042244 Three cheers for untyped anguages where bugs like this can happen! ] [Add basic support for chunks with associated markup that's different from the URL; will be used to support HTML emails. Daniel Burrows **20061209184846] [Disable an old debugging message. Daniel Burrows **20061210052542] [Disable another old debugging message. Daniel Burrows **20061210052556] [Handle the case where the last line/paragraph in the file contains a URL. Daniel Burrows **20061210052605] [Add basic support for text/html parts. Daniel Burrows **20061210053027] [Remember whether the first/last lines of the message were used for each chunk-group, and suppress ellipses if they were. Daniel Burrows **20061210053039] [Add support for images in HTML emails. Daniel Burrows **20061210053643] [Add support for

and friends in HTML email (hacky!) Daniel Burrows **20061210054521] [When a list (level) ends, terminate the current paragraph. Daniel Burrows **20061210054536] [Pop header styles off the stack when the header terminates, instead of leaving the header sitting around forever. Daniel Burrows **20061210203502] [Add a quick-and-dirty README file. Daniel Burrows **20061210210750] [Break the internal modules out into a modules directory. Daniel Burrows **20061210213925] [Add a simple distutils setup script. Daniel Burrows **20061210224839] [Recognize https urls. Daniel Burrows **20061210225613] [Don't report spurious matches in matchless emails. Daniel Burrows **20061210225621] [Recognize "+" characters in URLs (perhaps I should do this on an exclusion basis?) Daniel Burrows **20061210230443] [Don't recognize ? or ; characters at the ends of URLs in text (since they could end a sentence). Daniel Burrows **20061210230547] [Document another bug. Daniel Burrows **20061210231109] [Fix a runoff error in parsing email messages. Daniel Burrows **20061210231116] [Clear the screen every time that we draw it (avoids some problems where junk gets left behind). Daniel Burrows **20061210232544] [Force a screen redraw when C-l is pressed. Daniel Burrows **20061210232604] [Debianize urlscan as a native package. Daniel Burrows **20061210232828] [Really clean up everything after a build. Daniel Burrows **20061210232910] [Update the standards-version to make lintian happy. Daniel Burrows **20061210233638] [Fix the python-central substvar. Daniel Burrows **20061210233649] [Make sure to invoke dh_compress (so that the manpage gets properly compressed). Daniel Burrows **20061210233659] [Use \fB/\fR instead of .B/.R, since .R make lintian complain. Daniel Burrows **20061210233719] [Be less awkward in the manpage. Daniel Burrows **20061210233746] urlscan-0.5.6/_darcs/inventories/20070105053047-3fcb1-341f792dbab40a1c693f1dedfefcf9c011e70956.gz0000644000000000000000000000435410561406316024363 0ustar Starting with tag: [TAG 0.5.2 Daniel Burrows **20061218045638] [Extend the URL regexp to recognize URLs that have internal (not trailing) colons. Daniel Burrows **20061218050450] [Add a changelog entry for 0.5.3. Daniel Burrows **20061218050504] [Don't crash on ordered HTML lists. Daniel Burrows **20061224155145] [When building the selection list for the chooser, merge adjacent chunks that have the same attached URL (this lets us collect, eg, adjacent text chunks generated by HTML entity references). Daniel Burrows **20061224160230] [Always include the current anchor in all generated chunks (the new url-merging code in the frontend uses and needs this information to produce good results). Daniel Burrows **20061224160406] [TAG 0.5.3 Daniel Burrows **20061224160446] [Recognize … as an ellipsis. Daniel Burrows **20061224160531] [Recognize internal commas in URLs as part of the URL. Daniel Burrows **20061224161043] [Recognize internal and terminal # characters as part of a URL. Daniel Burrows **20061224161201] [Recognize internal ~ characters as part of a URL (Closes: #403713). Daniel Burrows **20061224161225] [Spawn the child browser in the foreground (P_WAIT) instead of the background (P_NOWAIT) (Closes: #403644). Daniel Burrows **20061224161459] [Add a changelog entry for 0.5.4. Daniel Burrows **20061224161647] [TAG 0.5.4 Daniel Burrows **20061224161657] [Add support for command-line arguments using optparse; accept a single message filename on the command-line; accept --compact to suppress URL context (patch thanks to Frederic Peters ); document all this in the manpage. (Closes: #404605) Daniel Burrows **20070105052530] [Add a changelog entry for 0.5.5. Daniel Burrows **20070105053036] urlscan-0.5.6/_darcs/inventories/20061218045638-3fcb1-c7b0dbcffa96af43396bbd2f76000577f0aeeb70.gz0000644000000000000000000000043710561406316024450 0ustar Starting with tag: [TAG 0.5.1 Daniel Burrows **20061218045436] [Fix stupid commenting bug. Daniel Burrows **20061218045437] [Update changelog for 0.5.2. Daniel Burrows **20061218045633] urlscan-0.5.6/_darcs/inventories/20061218045436-3fcb1-7cc4d813536ddd884d00add23f27eb75c45ec372.gz0000644000000000000000000000060610561406316024232 0ustar Starting with tag: [TAG 0.5 Daniel Burrows **20061211004026] [Add copyright notices everywhere. Daniel Burrows **20061211011230] [Suggest mutt and www-browser. Daniel Burrows **20061211011322] [Update changelog for 0.5.1. Daniel Burrows **20061218045422] urlscan-0.5.6/_darcs/patches/0000755000000000000000000000000010561406350012741 5ustar urlscan-0.5.6/_darcs/patches/20061210232544-3fcb1-61c7e9c33c10d6f52b082802a62ff884170e159a.gz0000644000000000000000000000031110537113612022764 0ustar ]J1D+汭nZ"LMl6)&]n'e8g`^#xB!x\Pڂpb'=$5<!i^l x"yR<?XL?C2tuZumt7|5mcv5RU6]{9+]sQƃXb|7?urlscan-0.5.6/_darcs/patches/20070203015531-3fcb1-4a4847523362a60660cb7babd1d8291514307a7a.gz0000644000000000000000000000037610561031020022661 0ustar MAK0sm-^Dd٣YHmhy u)y|̼y{&CUE=Sz<b] όGDVvِ4}zXuu[7M|[ FȻkEA|yβñ,8$B L%Mq 9l1 x= { hunk ./modules/urlscan/urlchoose.py 36 first = True firstbutton=0 self.urls = [] + self.items.append(urwid.Text([('msgtext', 'Foo'), ' ', ('anchor', 'Bar'), + ('urlref:number:braces', '['), + ('urlref:number', '5'), + ('urlref:number:braces', ']'), + ' ', + ('anchor', 'Baz'), + ('urlref:number:braces', '['), + ('urlref:number', '6'), + ('urlref:number:braces', ']')])) + self.items.append(urwid.Divider(div_char = '-', top=1, bottom=1)) for group, usedfirst, usedlast in extractedurls: if first: first = False hunk ./modules/urlscan/urlchoose.py 70 groupurls.append(chunk.url) # Collect all immediately adjacent # chunks with the same URL. - tmpmarkup = [] + appended = False if chunk.markup: hunk ./modules/urlscan/urlchoose.py 72 - tmpmarkup.append(chunk.markup) + appended = True + markup.append(chunk.markup) while i < len(chunks) and chunks[i].url == chunk.url: if chunks[i].markup: hunk ./modules/urlscan/urlchoose.py 76 - tmpmarkup.append(chunks[i].markup) + appended = True + markup.append(chunks[i].markup) i += 1 hunk ./modules/urlscan/urlchoose.py 79 - markup += [tmpmarkup or '', - ('urlref:number:braces', ' ['), + if not appended: + markup.append('') + markup += [('urlref:number:braces', ' ['), ('urlref:number', `len(self.urls)`), ('urlref:number:braces', ']')] markup += '\n' hunk ./modules/urlscan/urlchoose.py 88 if not usedlast: markup += [('msgtext:ellipses', '...\n\n')] + print markup + self.items.append(urwid.Text(markup)) i = len(self.urls) - len(groupurls) } Context: [TAG 0.5.4 Daniel Burrows **20061224161657] Patch bundle hash: e039c0f5a0513cc77cd84d8f1849ca1fbd1b7091 urlscan-0.5.6/_darcs/patches/20061224161225-3fcb1-7b495f124e7dfb1ad8f70a1cafcf2470c80b48cd.gz0000644000000000000000000000043110543523372023514 0ustar N0E"8!Σ<*!"ؑ @ 3wFŒY[*Z[@5T .5xrLvnhk|0su-~ 0[q|4'%9/N g:2뚊 ZV rP ײ[<5H6e!ge3 &Y4^*∌p]nrFyvE/ jbKd) ŸI4urlscan-0.5.6/_darcs/patches/20061208052215-3fcb1-b27375221571ba3ab289b015488ab6b6cea6bc3a.gz0000644000000000000000000000043110536173240023171 0ustar MK@sf٤L"x1 aM46~iz}x ݀'|@СU33 u5BoBm2.Au#9)[8֚f[vWZGqQ0KOQ G%|m_ ͻ7ɤ-v r6u$)D~?$JA~OVOPBLv-5ԗѲmU7- OpoUFRurlscan-0.5.6/_darcs/patches/20061209042206-3fcb1-6db78cecf4cd0593ab99c7b5daa62c0733d4cd6c.gz0000644000000000000000000000032510536434775023617 0ustar mQk@Wcbz(MBD#G;ÊM27{ w6#)F3 r)>jg=Oom:8#qu2)*ZYQ$53ӹx C_RӳQWm" jyGK.cZH+ZdyQ^xZurlscan-0.5.6/_darcs/patches/20061210233649-3fcb1-752b369f59c553f27015477bbe93c49a204421cc.gz0000644000000000000000000000025010537115032022722 0ustar m @Fwrt4%"Bz;2?Y`jy>||RO;q+[p ֚n\;*̀T,M 4b[^`$]DFqLZ3 D*XKA%e| _oN xdurlscan-0.5.6/_darcs/patches/20061218045437-3fcb1-abfcd1df962bea63762d7f8caa3e1ec2f9355b3f.gz0000644000000000000000000000020710541417424023673 0ustar vˬP(.)-LQHM+KWH*MrILQp*-*//V|ĜܼL$ 'O/5NKvwIb;^U:>09Ƿh.`ѠMQpK*ToHc*N0 MTJurlscan-0.5.6/_darcs/patches/20061210231116-3fcb1-c1b0ef0e1fdf3733fde482666de6a5cd81866a3b.gz0000644000000000000000000000033110537112037023426 0ustar jAuǝx/!"ۻ;dgTB=n4KUEBBҀ;H G6$fAMӔ[)Il:?7]zurlscan-0.5.6/_darcs/patches/20061210224839-3fcb1-6f733a06775f4c5907c6a6e9b283b326fc609974.gz0000644000000000000000000000046110537107310022754 0ustar mPN0+p4ICđ\rmcG~;!Ti3ɳ Q(IOʁCps*4`8x쀟 &^@sq4ʳf<Ž )n7ol1KNOlZNQ,aܚz+cZ뷦kδU:` tF"*1K7AhZ Dh"{c~M,X mDjw$bpqmdP,}sg_urlscan-0.5.6/_darcs/patches/20061210210750-3fcb1-527fda92bc6857ad222f64f9af17f39189764884.gz0000644000000000000000000000256010537073462023045 0ustar V]o7|~V}qmb4$hkRF/KTC} 1> 0]NR,E$E8jL-(% $MҶ>LIЯzY4?PCKA8|t_ihӷdVtƭ#`NYZ\P=}׹|(x}ͼ BG@>{BվA`q#b 8ϟ_ j\MnP@%4 f!$1 Z<" C77lD0Muȍ.(2X 4O[hE9:%=wV)nq5 SRPu*㎢GjFjߊ d{ ԀQvL ΌW5<=29q9T[ Wej_y[f;CI3%J/GSm+ U ޱrofА-Vs>iQڨ`d6_^}t}y#j1t(pTw@*YӚ1MԵQ Ixi}9b,èkŭFd' o8!ǑjnL]< `_ lY7V{R[,/rIu1 c9(iqofEĠ;ؚ_oP=KV o5_+_wbe|D@YGjx8,i>[c]ESgQakxX ŋ[ χ[X^*\67+$Zi' M},r҂(CaҒQ1Ws?A嘨Ë\< $-Z !H_;4戛50NZf5x5d$”1xSԋ?g urlscan-0.5.6/_darcs/patches/20061211004026-3fcb1-19f8e5888bb8dbdc0539480763c2e6fa6d173693.gz0000644000000000000000000000334110537124372023107 0ustar X]oF|%rj$%Iv(yHDċH{wݣ$;mZ ׾Eۧ/D2\FJ\t֚'YuuG3<4#Ut<Ȓ$4I&Iv8xtBJ9'zTM+jQl<>P8y% abtq<>N7k\"x-nb3H)7t cQ~ n?pזBy'ZkZeؾȜNs}ڍ/M3U㭬A&][H8)b7\)iwj^ʶQk%+*Zpn nBνBY".Zv&J͐MV.GH DFzBervŞ JrT#*\u_+ 1p'Ʉ]6/B k)@.pJՎR?w2Rs/f r6L@vυ-H+2[RWFM1MS&+w5EƠ6XZĞ]͂D6E^J+s $T$cT7@2|N|UF`A Du-O%c 0.i537{滘qak"-%s\Z3 `>εu< %س %P>ZKP!dn#.OUn?LߎL> Ku"TuEuJ2&SNTm]wZyaQA!ⲁ7aNcDؐ(H񂦱oSdn'$z'TQڗC_@2@&>q|\w!UUp[`%,Xd +M%#xx *tN9#R%Zf09 a2MTS(DMf10z_D5p9Ŏ%-faq [2OvknK[|fbzpXY7l@֚6! _L~8ݭ=`ꦅ[@Z)*lXhk+L$xo#?2y> c~eOQX9' D=2zxZ1j8`Y¿ T)WёvV}ch1mبT Oʚz2XruACR n5ihxUX7viH`HPcBҦ ^ZbCТ[gpRE0uqk[}:trb"{oEڿ gi:gIL1'rTtCŝ2`qOEg=>d}3YJ0 Xe?$qb9:"B*$r*|Ҁh2i@#=/:"3҈g I<:6wǫ ${"o4ufd|*E5kݒ~UL4tFXyzY%zh[Btjn-I/Y=gm~5]Zl;0xL|Ib|_6v8:>-eEH)GvD#fO5rHlv"IVr^9(6;w9d+ Zb=@$h[5`-Pc,`r~RxkB0Yk`-]Gv~z`3EŞ|uDn3B+LvVk89ni2r7xCcUFyuǴNEĚʒhJ,J̫'+|N,:c/j\Blp#Xv, 8fC)NNCau1zi F'O+HO8!VE{OKY !BcI7#8eYX2Fn  1F8XT\5QY17r Qy7<æ{^ߨ1wԠ'a˘עmM5^W[=ǹ7ncZFWGK7#D{! ;@ dmG/9r[2,96 &#6}U`(x |lXaMw.Lm;(ڈ;R!_K@J=([v՜׬ 2N*3c*vW}C>*>/ %4b)!CTu pIחXVα0zpq 6/p 9i8"[$V_L!mOhPeO^;pZ UcHf_UhU3t30/A`|$Y&qE I3W=vs﷋zA%9U /S3v7*X#۵īxJک K4ԊF0u.9)աw.;cPʲ#x:VAEc4rH}h~h"afDN[5h<<*aܶ -Eº_ ~zG 9/dH@U:E$Kff^~[Ub2)!=`kK%{w}7Ʊ7$y`rt鞎B96s9LU7'N4]6 AJMq|#W>]/G*LDNCaIC:υd,ɥI,vw.XCOGb 묙Z-x%gĦҨAh:Snbz#I䄇wg^T$Rs(U//o|,6~_W N:Ʃӳ"^H;\$L!́vYa|i?ɠExo}o3X-urlscan-0.5.6/_darcs/patches/20061224160446-3fcb1-2b98dad03b25d34eaf84ed233f15b7019d2ba628.gz0000644000000000000000000000033410543522512023266 0ustar N0D9B:QUVTp!dcɬ1@fFX}yqL1SUc1wH=qwHoH!om>!޼'l{|P.@ܖV8'SxXW¼ =(ttytPwg8K>k)V֚} - D^۽4O{ng-*} [Ru+܌畕wMV`3SRؒx?!Ƹ]x_a?Dd1IaU;hHh IXRxEqKdY5I%UG1s .F/<ȢCJ`LLX,J+A/m6ne MCڞ0t䑰F8Ҧ_#Ŀj_kj!`C0a8ֳ>"&j` U.v̛UJKnI[^J0M_;,n=y1lD6rڄ@*z~0urlscan-0.5.6/_darcs/patches/20061210214616-3fcb1-96339459ceb52ecf38809bb7f37f233ffee3aaad.gz0000644000000000000000000000045710537100067023464 0ustar mN0~% r r@ .UU9]ձ#;!T̮ٳ Q*IOʁCZps*4`9x⤟ &^uBsq2fYOly1R.}o:/ 9K./lZNQo,aҚf㵱~`+-,`nX٢udten|8;;k7AhF+Dl̹HVMIHhnjtlycXQŧ9]#Lurlscan-0.5.6/_darcs/patches/20070203015408-3fcb1-74d89bb1079c0a0f0bb04b0d604afc3337da72da.gz0000644000000000000000000000024510561031020023304 0ustar M0@}b0B)Xb;cJH\ǚTt a4H޲;LI8%B_N@OB(D)RlO`&"z;*u~; WrEF扈d b0PZki*/lud+ kDقbp FQYF4B6DܨT!R[x WH[ZhI o{+sR3 Te ZMC_I%RX,Aq/,_.dY{J3]<,v8'tsZ,|;maHW") ͓ZJw3,9 >9 s#%Y=/V uIl0jV+T7(l;:9Is,,Q]()ry.%D̋Ggd\!3vTG萰KI4՟~ꄿaΕιBC{fVVˤd6}Lί]hԻܼm8^zYHDA4detr7`rC`rzD=F7t4QcJIpM,Q܉u\A, &1-歑܍Q[FFlűxU/Hߑs 1%V+b&F BW:Db4sqhmZtmGNt^;4yvAvm &D<`؞Z?N09)yPZs`5EMU{ixgjՔqNLe}ϡ/)-@],2F/j[v8hf(?+Ya[vg;֑QwzO[{aǟb]ůu5wə_3kX[-Ο(u݇upx~}u9-y0ʚR&=rݢedܥe@9rX'<]'+Lgh̝vˑ=wi|NW=v{=n'a/TC#B$,7W`C G귵d4NWkֈw6D6- ]v~\G$H<}bջ1,lEx\j{hq^H%ٵ3~^Ң(Ј~#R(} fl.#5W Gן[+{et =;eaπB&ڵuqڽӲ`H@0o9Y͓6w0=eЏ I<&R@3+o).k4QC\ ܶ[ ,w\6\xxϴ߼!:f6r:j4GkxDv p%e,bG?!?1v=)*"jbue6\2<EܸjLQEm}0k%^( ⱢCq^Te3o5 M 9P-`93W8H܀ .Ntf`]9`ٓP}$kfJ\UC>w1 f\(om~]MAWGtr(w}-6eurlscan-0.5.6/_darcs/patches/20061113052140-3fcb1-edef29b4233d9dfb73275d7c1623da4363b58455.gz0000644000000000000000000000365210526000362023137 0ustar Xko_ ؊uҴt),1 ZcȤJRqu}KR8b"ޟB?ܗ%rSWi&^c?럖0Ჲ/\IQcֲ_(/rQ6ggf}1xY$lhݨ+?b)Esk?vߪ/Rlu 1掭*+a"Փe|F;QNj#KPFTxgNZ~5M|2:C6ٝ7}GXBXاjta5T WiI\IEa E8&Ktgd7aի, 1t/J2- heIx_\~L@"'Gx'@iSuހ8o\)1S{#/򚾤~n])U=. ,;wcL8dWxd~E@3lQ &[`AcfQqkُ|ęh'XH%bZQ,BZj'ҏwLd>@pv#BŒkp{ʊtãsp7[#Boj!5Izwugw]k꫋>~b:ٛy (N_]`~ㄨ,KXG ssζA<gJLbTl]IPƘz,CWZM;٩ʄm)@v>m_ IuE8l%U)@o#pIZ+Iy]t}Un>feIāNg8VU'"ʬlp%TߔiU,z0hIVh/g+ݲ Pϩ}U+@hAxZiF@V5mJ&Ƌ0quؠV ZG46pJH`ƶkNh(|݁LS/+mɖHz _2||Bn3"?Ӛ816FX]gh$ 1>⤚iE88x h6<㼰ovVhwϡ+gv1,!{*֟Mޮ` K.K4Y<"}UvKQ[v~Ϯex_@ {  2LaV@WYN00?wcz5!YK oqDiQ"{ʮ;pRt>Nm;S$v_SP'ybO+ESA 劇pyeSpt-D GöܯFHxv|j31Uy@L/N8p+J ؊6BRK-3Kfe7(j>lVUcׁ]Zh2/5g;t:d$.d]NFp 1<zϸ@ 5p KnXxx2:vksЂ'pW}pH[G+z0C tmBtF#xd}8dC-q.Q+M=Cʇΐ S&y7xMsT8)u WA㈸}9*^M!f=ve7DͯeDπq':nE$uoPiurlscan-0.5.6/_darcs/patches/20061224160531-3fcb1-1035ba3c5fbb534848b37345416073e65e32b9c6.gz0000644000000000000000000000025410543522530022710 0ustar mK 0yVq`mjD8) LoKCbv[]G27 ^&>@hkMCQdo`땲"W/Hr )"DN΀qUjֺBl2) À WVD ؃=*urlscan-0.5.6/_darcs/patches/20070204164158-3fcb1-868ce540768ae4f78a2cdf1e6813a445355abdca.gz0000644000000000000000000000056410561406350023321 0ustar ]Qn1}8j_ڄaTl*<U*Ǘsyi"O݌חmqHqV^sqd=#y(W!FΒDU`ޯɔw|f3v G2XP*V$WX"6ɶ4[tl2P>xp|hqMcUݲ9f~jzurlscan-0.5.6/_darcs/patches/20061218050450-3fcb1-5684dc6db03458566ebd4ccf6c1551b8b04cd9a1.gz0000644000000000000000000000043410541420577023304 0ustar AO@زJZXJ5zdŖ6q%ˮ֪݉ɼLfs\5`;w`xwX֭/fc(&Wڂ5LHZZԻfJp W2ՑL^ (o*$9dI o^и׍|cw+܋ Z7SȖXQvcE!FU_e˞aI׬ΎcB7V(䒐U1%^ٲ }l-urlscan-0.5.6/_darcs/patches/20061210233746-3fcb1-5e7f2e0140851f8e482a46fe0289ccc04bf868b3.gz0000644000000000000000000000031310537115122023145 0ustar A0 E9_3jh B ԴF] N*zCV0 O򠆵:Xvbwc× DsE盢,rڮ7gOTn7Ê*w8;Ν#xg-rʉcQPQJa>dd 笵 DZ>FȱLv''Aopr`[%0]庽AMW~_p6]Aurlscan-0.5.6/_darcs/patches/20061224155145-3fcb1-b37f4aa7be1c45dfa2881d4209f56229c7ea21be.gz0000644000000000000000000000036310543521047023360 0ustar uKk@+. R.ZW݅ ;2DJ{.t6;+Cr+]X~.w$%XkN淼@%qTi0QT$&|"`T&jK_~eO"KE{@4фtc;gFhj-wjMFF6}~ F9urlscan-0.5.6/_darcs/patches/20061210052626-3fcb1-854d2d0a8139ed1887ffb203d673ebf4a3d5bc2c.gz0000644000000000000000000000341010536715203023355 0ustar Xmo6_AH%ǮkZdIn؀m|3 +C"UwGQo&t{xwj} U<&dDyoiU4zC%gPESz^(o%6 #Njb~rrq8]OH,DfbQ'^s6L3RlT&Yx&0y4TZ#(~/¡T Ӟ L[!ڨP1d0KH\ &q&5z9 'Mg-ƴ \T5")AsM„Y*J6|$ 2wqhoM:roBöx5Ƃ*e~DVHARMndLJ{_23mJ+1D6?85H)0F0LyzS<`ѴIؖ\r^m^>{=!W).ԌNNB$/4j.*>PYj+_BfXm $a q!#8vq$y]W"Uh8,T\%*1T(UӰ) qwt8Y|j$syP˖c$@0kB$Rb[7.(";XACS5?*;WDA2$yJ7LCɊ`Uoȋh\wT,c5ql#NSQ 5O :!sC嫥 V5@"ؒXͦ, n@xMUA0DzD$\:yd*',W5)5a@@u dD{ ̠6R^{|EwLNN%9Dc>% doF` dy3b2%^\_ u XҎnUCG$wu6#;M4!{U/uÑ? -heiU>'JF~>HS0hDJ`rgy|(Cjx43Z)>L i39r cՂ鲐ԭTi7oψJ&ǔ~`F ItWxt<(ncHjm2SB `k#ik_A2ƁC)GD( j3C{=#:at8l 50NCb< 1"ņkOIp5V#;qݙ!6sy}Z]}tH?^_cg]unX|I\ڌ"nn iG.'Ggt˅ݘ#FGz@ G="ef׽E샾C5\mwm|2Kdd2HEt+U}mQ-x8h6Gc q?G[1Tc,2FGNv0]*epTӵrczz =r~+xY,wel{+lMq֛ +pQosGGGu]iurlscan-0.5.6/_darcs/patches/20061210233719-3fcb1-84a4e7a514736d466f20c523bb28427eec775f3d.gz0000644000000000000000000000025110537115111023062 0ustar - 0}bUO@ >A5T[fOjTZ5h}R)%ڂ4:Zxǣ38&q4쬇пʿǎ&0{'Iek,/]-(a؊)̞F)SJK_ urlscan-0.5.6/_darcs/patches/20061210225621-3fcb1-d4753ee59f1962103867184e1733aca8a74048fa.gz0000644000000000000000000000037210537110240022720 0ustar MNMO0 WXⰏNYMo@GI;⿓C=~ogxHцУ41X?AG@=ZZ[rb '\3{L6KST1S÷?^M:E 3JkHpBӗLVh2[eȖhC`9'#xIi 8 }S`P#)l#;mo:Տ5Zurlscan-0.5.6/_darcs/patches/20070203015132-3fcb1-a6853ef775b5f7a2b5f9cde5fe7bc7b50f8f4f89.gz0000644000000000000000000000034510561031020023542 0ustar 5K0+VXhvEEԣEO"K6&cݝ~09>";yH"c}@X?Bq2p ~xN},u,dʻMByQ=Koic"n?񃜲V^PWmۮݴweaZz\T 쀺iN+jXD!x*%m#{^C8(Aurlscan-0.5.6/_darcs/patches/pending0000644000000000000000000000000410561406350014302 0ustar { } urlscan-0.5.6/_darcs/patches/20061210054536-3fcb1-581b3a4b8f54f23936d288510fc2c4a5c8a77d54.gz0000644000000000000000000000025010536717413023073 0ustar Mν @b$yI,Dše Fp)f;M ;?@7 h6:G`@gblflw˯hcFG.P΋\BGEg},BL/Itxȉ=urlscan-0.5.6/_darcs/patches/20061210052542-3fcb1-0a3227e7bff19f5898534c225f9960bd171a6cea.gz0000644000000000000000000000023010536715203023142 0ustar -M 0@}N1K-`]"25CNK~hېcفx@)'Z@ H:\ B4r*k̮1m/ޔ醢'4ZdHc^>2urlscan-0.5.6/_darcs/patches/20061210230547-3fcb1-c1d261f5a6defc4cb10a4f77ebcd85303b05c185.gz0000644000000000000000000000044410537111336023417 0ustar KO0{SIã/#'$.2B,CW-W8XofF=XYca [ԍw7oR'aj1P{ɒk #/ 1؄yUey1euZjxOڠ_iBW-T(#[Y Hl<'&4E*/u_MuKޫ#nv\*o@4c8CN?&d$O}&_f{urlscan-0.5.6/_darcs/patches/20061210233638-3fcb1-563c29efcf58db89868e66764d24b44bb7723ca5.gz0000644000000000000000000000025210537115020023171 0ustar mK 0yVqǕi;ȭ 4)y("BőÏ|5F5"FC~7>XG -E-㓳=5=`=4FMI!RB*U^M/i(zCï4W@ \7CGurlscan-0.5.6/_darcs/patches/20061209041111-3fcb1-0945c10ad1df58ecd753e8f434cfc684a8858433.gz0000644000000000000000000000051510536433566023165 0ustar RN0+IM"*@Tr 5u8HM%bwgfE(sZY:dt @X9 Nojpb X< wK(y`J7F, >g7J2"wIY_Y |W[hm,H*V=ע!)I9l @H$5%g0a.DzP?j6/_GT8l,emNpڏ,n6IBCZWd G(Oύ]z=(ሲ~kI6^=xs2=焎 V?sO̎w7ø9urlscan-0.5.6/_darcs/patches/20061218045405-3fcb1-88d697e9f0af13592987020d7d2b53added3a484.gz0000644000000000000000000000026410541417355023174 0ustar  @Fw-4h!ӋLDO˳΁g@sNV3W>4eA !8,e5 zg-nd BR΍"kdTZЃU@/2oƊ qlqa2]ه}jurlscan-0.5.6/_darcs/patches/20061211011322-3fcb1-de18daffec1c6c9094e3fd775d9a827a50c4ee5b.gz0000644000000000000000000000022010537130271023572 0ustar .MOO-.Q--)QHKQ(//M*//N-rILQp*-(@PCbNin^Hy^jJQB5WFi^~JjRfb~r~^IQ~6b+:Vsrurlscan-0.5.6/_darcs/patches/20061210213849-3fcb1-582dd29e7e86e1eb7e9e3bca27ea8fa9a9b979c7.gz0000644000000000000000000000051210537077173023601 0ustar QN0Sw*uƨQcoA*=0l!{?t>7j&A:HX_ o얠70 ֚/O-~e2(-{:L&eeC߈uLrt ]#e6<)qKځho̢1ơl`MܿNW%% 6C>ÍQ< Ƒ5 H3Gny^n+?У. ?#f%X1G!%1vޓ@5j?2t-w)$pAJ^B0LJJYY{vpB?urlscan-0.5.6/_darcs/patches/20061210232910-3fcb1-01eead9da442d16a5f5f49194c451697738d63ab.gz0000644000000000000000000000023410537114125023140 0ustar eA 0@ѵ9Ŭ+M҈PDmpKQxwswP@[o\=%@4rm%FS?;(>^#MK,x(B,, iT`]MUDqI:|b]GO/Am>ڰurlscan-0.5.6/_darcs/patches/20061210231109-3fcb1-ba449134cb7860b27068f38476da7ce3b97de62b.gz0000644000000000000000000000035210537112023023136 0ustar 5AK0Jl+ t,D$m6n3$c7E{/mgyNZÎ&۩gsS )xSªD*_>V@L;6:ӟ^<=.!ʈ"]V\xFIȱ(–x=h:aKZ}Murlscan-0.5.6/_darcs/patches/20061210053027-3fcb1-ad890000c763668b927a42147129e86b71de2806.gz0000644000000000000000000000340310536715575022574 0ustar Xmo6_AH%ǮjkSdIn؀m|3 +M"UwGQDInBH{' YSc< M6YA4z+HN *9䇲(">GE!")T[X(?3(aFTOJŎQ GE5s"@C1Fai4-m:ФyMڈ% doF` d1]v1sӒN/:,\waNLy!#:W&Yj*ĺDAҟ}QiubѲvI[xz~#͖}?X1Lbx2OI%03<>!jx43Z)> i9rcՂ鲐ԮTi7oωJ&ǔ~`F mr:jv(Z5B3xab!kuYi6ȭ5cK 'fBl C{rIim.MSօwn ?*TNlVA{h0Bh*xLދItWxxQ|V? yvL 5خk|&@-՝p8@݅wL."BFWT.8Mq>H7щk'~S wBs4G,-6\Lם*^b1G קGN `8iS$8ԙa I+iph36oHA pCCxS 2HttKHM?w!wƠ>9Q'8].Ƭ%y(*7>9 mmE鶒ڕOޗt^{s{pnyG1,Ó²J@=_8rk"K[T,G=GOxwcqCuxEX֨W}SɎk&]ܸKe[fJWA5]+w >|z^[G@.|/;v;%vԖfmqڇ9Nf8w>Eurlscan-0.5.6/_darcs/patches/20070203015220-3fcb1-2f4ff6732fecacd425203e984d9ee5d4455fcf34.gz0000644000000000000000000000047710561031020023361 0ustar QMK1W x~tEb^ԃA6mh,ɆRn]l-J7o23m9+Km*TՂPd"[k%S 3m`=H@,|܂1˕ [g{\mISҭn7KӋ4KOY` NMvI Xt`ˍE/t;- Y"Nf7n{NEBeߞ'>Z&)J,)+ IQ#: ʬwh5ϥ%^YNrm{AۑvSd_X$urlscan-0.5.6/_darcs/patches/20070105154736-3fcb1-b7888e73633e42e1a35d933494b49a3ad2b2f25b.gz0000644000000000000000000000035510547471430023103 0ustar PAj0{+ Ċ )~=6&RXIPq_vfؙZ18V`?/Cp, (ZmnZ}p%@!.gfcĦJGg0'8 %FY2hJ{2HT]&Np,{BN"{x-,RS״꺑k~?& urlscan-0.5.6/_darcs/patches/20061218045510-3fcb1-830b5795821b841a371fdb456dd446c8d05b779b.gz0000644000000000000000000000034410541417463023021 0ustar ]J0ҵ6Kձx%"i` n7>8<JWd!Bל9c '~;yO37]E-ĵJ\5b}egZhw#Y"\4&zA֐|8@G.[ip):'PHN~lV[wC[)% vFzuӬD%VBc[urlscan-0.5.6/_darcs/patches/20061208052016-3fcb1-9ec26176b9e37c8151fb9eff0c0f424d84c9a1ad.gz0000644000000000000000000000026710536173034023376 0ustar - @)Υ?DIJQUκDD730O Vv0pbI4ү5S rvSV'^N?_ռZrl9Ir!,Q"+aaޓq|~CV _6>nABn }Ž urlscan-0.5.6/_darcs/patches/20061210052605-3fcb1-e43df1a4d85add1c7e5f90ff3b5b4aa559ec68b5.gz0000644000000000000000000000030410536715203023600 0ustar M AA+ޥq ,,f2|$w}zF49By:x* ,-̑,Sb&%`͇҉ܖRҨV˔괲Rk܄I|l&BN,+=N!P>GԿ*.j_?]<Kurlscan-0.5.6/_darcs/patches/20070203015258-3fcb1-a4a17cb71c7da5b056e9235aa5cfdfdb2d2971f2.gz0000644000000000000000000000047010561031020023470 0ustar QMO0 WXu[CE|BV-M|iEh%r+% ԦmQT5t1='tda6$7޼[0W&.x|BHh{sU\ Cyp{AC0wPL*<^eV2+g W m鷯Nd$llN.LUhM/"uU<;k2KSjΚE16xWI&>$YVf|‡OltXWn/` X9ۂ*&+,fs[&gXӽ4lp Z]~ϬUW9Z6küWH\:S;IBmg.l2-2⳰mty"1漡$ߚ/fSe6OHժ($FNvrxRwH=[ptȯ&́ls_fݭQnXʳ9E_ eWTi,iGcݔq0>ڤ"tWɣt&- qhUV $Q=bq.yJv6}$*u9' Ob{=l˿ ѻ*mK/>Ѐ˹Tr'aًh)/EADҴ+;Đ9>F--kNdHd.:](.9-u!E̓zOUASIy L:0q%S۲Ji<,*UDE'˳G00 [ٍD-}h{bDiB$)_f!q֘k5~14^Q9dC ;Vxb&D>5鱫 g"6/@5yP-"o eז@IcNvB\MX6ZD8_quR u&!7 .z ,y(2>ِ#`# $)".}̳$]C4Ã7擵2R/h@#DIv=JHHfXBh>_l2-xg HV5ӒD"V2r1obJ&Q\Yl}*F2M[xOGjkaimASm<f&S}K~FI%kkE_d#F1I?̓I@&U8!b/I'D\s S}lVUC I "RS>yBUXH{zB_d^,)j/DIH^)}!Ct.x|e0??9t(B >WP٫L(Jx Fĉv& !5# +\YU@4.ɇpv R% V5U5R\^aXՈ&D|Roye )37I(xg Ǥ5N_eg}H^2O}mW{QcZܨ !UZ5BsÒ(@ ̯TZqѬy-%%97Q=${VQEU92g;{-G9%%DNw?WN<YM-%%ή*JdeM ZnӬN1O)+87bෟ'm=Pz}B4c oNwMxmo[TS#Q{CN>,R'װ`n1jҗoQ}3QQ{K|zs+Ij_lj*{x] qz<&Y*! ~ʞmk[DMBPĶ0^VN4)uQd5YPt*Xy{Į0{F`!ˎ)fR4'9^Ƴ 4웠  <T\ EַW< D ò_frgC6T̤1Cա[6F*m&X!Czp;T3{F̸3)\`}Y78X<+m$Y$I+橯 2LTʹ t#=T p eI/Ma$zKGo=m)6n2RHJCGS/H% 98qtNX'gv3=Mӟ6&W$u[Wqr[cAuk O`!# <5Z!qFԉvY-8ؿfg3;ctFL _ԜFz!L]?"j _ h%S`A %%) }r ݴ?ZReŜ|)>4Sc 0zbXdV%=Qiy0b&ͨs}>eEi ؐ(1bY@:I=6oYgk4AGO|sSdp.o(; ѬH}JTMeo?p@T`nE(iN|NWJpJY 4ߒB)QNC:;c-^HZ?AYW xcL?I n/#{ՂySk8s /m-vIMmo_ XuLx`*nY?ܡ.+EЪ.S ߿0!]U.uXqs/eƮtFcΫHdEnZoFK\͆N\$,%a%#Z~̱שEj[[wc U3UXGthg`"~ȇXN#JƎ'͡Jr_YAh4ͥ:ZQIAAhJ.6Cʅ=E{Gh+SI $r)L@"ؾ1#(!(0I"4@OBy`+KF*)2APGA~lf K igx\f%\2 4:u^;fI8wӏfiAK㟊 ;÷: Kw*m3<Q!7Ǹf܃ ۭ (j xDs!bģo2YC4+#maK \M@E`FF-lOb3=-|35> ]<.')( JS{LQ+?3MfxR]}Kaݱ?塛y8F&yE1,<caƭX{9 hA)28sH)zU ;V+-xIdafv?Iu՞(Ǿ<Gh~|&5)̶MK-{Geo%m~ >Y,Asf.D=i6liRm]vP8a]FLjK\bDo䥑hj80|h]jsV,&t { )c=d9z~&g huolqB-ۤ%•#KWJxaakxE4V:ɀ\љQt8;'̖̀kH< D\h4:MQI9ᑦLh<g(Ck-d%ы79)6aفyډz`ӏFˆbo83i[6EYM&g۱c] $C-,x~5bmvmSta~[Pcs=_4Cdbl@pC%@SLu !%U='Xh <ɶq0 }s6<Mfnhn7oǃf4r<Ksn0~;s!=ѢFd:7Fx4~DǣpC`y~p邓p~d>net}3Sv<@ilFߎϯ.x $o6N7,'v@~8 hzExRz ̟] dH!#@/fͩtz7 by?>9Nڱ_],$~54tGP>f2(:v0!xLdnyݧC ?2]_іÿaSϨQޑHt?Ԯ|h=Ƽˠ FhupvC8G#f T.ol;h/=3J3zGXywztdu^Y7h"+̈́LgCz|<ضwc=Ao,ot'ҞٸG o],ls9]ݍ K@D.JPIU.;=C2{q6c#rEȀϑFI0 0y噛 =NJ w ޓ+* D5t.|bndFB*H_V$5̝R *Lp{E#EۑBdDSit+6t}iUhAQa*#M)j@*Fd#k4%roѾg妍N:F<ڭˀbfp猈q+0 h8Bk1l ΞxZy49'~H*oHbe ~! @; 0Hd)auIX4` 61:7g17tz~\ ͬkf0jE{Uӈ2}?$'y܊f*h03lOgYy@À7ᚉ6+񨣟H%|N4v9d|bdFՁL6Sy3њbAfX4SC#VTrXH'g)lKvec_r-bjk5g_IHeOOVK<o9:مU06<9Yis"zvp6PUSmz^#CuMxha3ZĻ5! e)ԽYvC"ٚQv#W}iZw7KnhY;َ*[.P׏`]z9% 4.@<|R5fK:.sᾌ 6?dj-^< Wk[qTȤǒ˨>&aSk5rsgh窩2g.4^_zeA}srBJwJA"A8^Gj.P'x'?/// / o2s@*o>i=@Dȴq'7?S`Hh|%`HD1hh跿 ̗!CB5[!r~7jZ(I|T2}| fRo}DZN &q[RjM_5zkW[jK-$ΗΟmO [ DUurlscan-0.5.6/_darcs/patches/20070105053047-3fcb1-341f792dbab40a1c693f1dedfefcf9c011e70956.gz0000644000000000000000000000047610547361207023451 0ustar QMK1-RZxS&m0$nUjoyü>lnUE.:k?550dL <f?A̶)A4mdGvͬN6I蜳>¶-9o62 X@ @!kB`4l x?r4>6HoF8$4/ߺ$+wɧXK)4f5^b ub#p)ѥ9y5>"طsju^e^u՟~g+ Ourlscan-0.5.6/_darcs/patches/20061210052456-3fcb1-9836f938171fd16903b591eff842d5a6a3f75521.gz0000644000000000000000000000342110536715124022751 0ustar Xmo6_qHJN\5v_5źula8DG\JF;$ItG:=<;xefZ,*?P5SKbEr3Y_)A%<_OO'O.A^w<+EyR٣]qt/iu-%7m. d;цM4^+^, IXƪw*J1p׾5D:e χ; Rj^p)6FN.Y2\ٕ<9DЍ:$iB[*AƊx%ɂeq"poO&gCPDdf(+N?Yy`?;8I%ڲH ;m׏ꡂəKx @5W)bRhbܾhB+(ԥbeZ'H7Z %jk.7}{NwwwW6psHN!RjtRTD, ޯ!S .X BJpDG>ಌ£bF JHJ,IjZ6mvN'K5O>iq,\AT0Y+){)ْBފ-o[ܬ3;O[~T&lAc2 $e`) bXUoKd)69liXmCq'RhOyugkd,,ٕ5X-WI` σ\Z@?m3qDy5f +Kx呭d`ll#@)&Q|.Կ"VoaFu忰򸧜*p/4> ?~tB]ҚȲOvfL(N!#%si[r=E.I)W5ApB2e_2DG74&QVGV+G,[ EW6l9!C: 8Ir7xyQV0yL5nٮtkz&Bľ-5p4@݄wCDB" ߅h6R <3 xd}*~0 MQߍr 4D,ynXakd:Fġp"uˎg\&kx]MƜ3wAk7rn8.9]xKꄜ9- "7T6ZxP~Uo \-/t&VPurlscan-0.5.6/_darcs/patches/20061224161043-3fcb1-55e7c652aa365ac8ab67d6004fe3fbbd6ab1a593.gz0000644000000000000000000000041110543523215023417 0ustar OK@s&&hl*=YlAia#urlscan-0.5.6/_darcs/patches/20061218045638-3fcb1-c7b0dbcffa96af43396bbd2f76000577f0aeeb70.gz0000644000000000000000000000024610541417606023532 0ustar  0{a̩˃HE iDLk6[XO||; 1A[1c5n"X,P' %4-I^%wa%XGiuH;k j>6_GƢzt'curlscan-0.5.6/_darcs/patches/20061218045436-3fcb1-7cc4d813536ddd884d00add23f27eb75c45ec372.gz0000644000000000000000000000030610541417414023310 0ustar A 0}N1kU\X<*"B417_|[vF74p ~GYT5am;nd'U#4kmЅ_yh z<-89HUZ {[' 2wқ 4BA]}knǷeY"ud6N$b$Q5Y( \=E0kԖ[yB?__Eurlscan-0.5.6/_darcs/patches/20061224161647-3fcb1-dd27c17a2daf8f808e0b9954d808d23d2bf8589e.gz0000644000000000000000000000070410543523765023350 0ustar ]RnAWd)aֻ식(felG0!9U]]]?*\5ce]^B&O99'AذΗ pN*{aQ*帚lyJR z^Vf茗9"bir]@ o~ ˰N#}9:gM-HBXBQ tpT&:IhR^H=lŎZ> .D*kc Σ> ™Eܑk4 z{fDxs1k^҃nRZ2{Kt*ƒMӢ/_n.= 0A.$W٣n+mtO"<؉ܺz|8._Shc6Pm؋mE#| [eJ-.9F4'Z莮De={m؎ַd1pvK|svQurlscan-0.5.6/_darcs/patches/20061210053643-3fcb1-78612d5cdc4d6a7df69c58694933a23b2124a5ec.gz0000644000000000000000000000060410536716366023164 0ustar }R]k0}ﯸԯ́X`̽loHlS%Iqc/Zys97!I@VeYi!xE$P/7 9L:0x(6uU9R!I5vG;Fk?_+a%1w<FSPxA+4̹c$v90ײIYQ#1S$,uR%t5{I~ _:nKҽ)ØζRybpr7)UN ]F50B,eIxbkZ <Y腰o%:G'ÁC g=D&X:Ԓ(;D%ҿfE~LC=+<urlscan-0.5.6/_darcs/patches/20061218050504-3fcb1-a1181d555a22414af2a1a45fad920448c5ffecc2.gz0000644000000000000000000000035310541420606023245 0ustar ]J1E'MFjQU+$4_ 3Tۂ wu.ws0Þb o)CɩȔx*|}%Yڣs[F͔}8w*urlscan-0.5.6/_darcs/patches/20061224161123-3fcb1-80ff1b2fb682f5b1f3f198ff70d0ac63b0a4768e.gz0000644000000000000000000000042110543523275023362 0ustar AK@6MJt4u&lU M7=xF45Bd-Ⱥ G&h@0p^X:Jhך>!vn,I)c8Xp}OJurlscan-0.5.6/_darcs/patches/20061209042244-3fcb1-17f2c8dc15ec9f13ad8b4283d05341749a2298b3.gz0000644000000000000000000000041310536435103023060 0ustar OMK@x&1""PAlivvvvE&/vNoD^(אNDz о[rm+`ȏ^ۀu;`CXvH$Q-9QR&IOnaV}IŧTŖf]F_^^ϋ#ǩgurlscan-0.5.6/_darcs/patches/20070120180732-3fcb1-801a0cc9564e952ee8b5094397caa0bc4a1e73fe.gz0000644000000000000000000000025510554455171023305 0ustar mA 0D9' U4.**" ܊Hl-IMw .lf5?aT㜂p ) '*ca![ELJWb*~F2IȤȦ"=8GHÛ9pkMmdŁlΎ$M4Φ; N˹ *4%i[vLzbY~@AJKٞ%Өt1F_GB vݦPS>}Fo_0OY/K47@Π?"Ca.Dm./uL:ҴxDX 'daa;pc<;$?>OU$Zm(I[iy>wIS\TxJ206(B\LNKzgC$=S+TcP-l (7:#I[UEV /qy$ZV>7GW!C}LXsJ(*ݨ.Msl1P9y{CW;xx/2v\z;38)c h$/;ft`_B9/Aurlscan-0.5.6/_darcs/patches/20061203053211-ac53b-42eab90f0d45dac258ca96cd1e9e8a8d9bd94960.gz0000644000000000000000000000252210534735040023445 0ustar Wmo6_q`N~mEfRCxvˤFRa}wzeZ&A$wZ)H~ n\zTi:?}dpvM\b2x# ^ٍWj^?pI[drz1y>yb:Re郵<fۃ`Euq%Jd)BT*PTGq<8*( 'JF^nYߧM/:3Gg^ ؠٔ5[?pPvJD^P7''0 e"}^TK؀QTN9~;,3+PZ7&^&R o {].* Ek]=VN<0:rJ8Me%t20og:+K5Y\y<- dp:>.Թ̨ -^>GFIswn͝|p UsikEW'M":g`7qX̩>˖VqKZfMZXnJ-;7AI]nKԒ,MҘv?'9?_M>Ęm *e6$ ]hS":WxNgh~ӛD*xhԶ 賑s/j(IOx̏[ٴ;G RE~!j fTwDl(u;?D8#яӏ JMr{dZM<pͥvY w6wބ;urlscan-0.5.6/_darcs/patches/20061210213925-3fcb1-c49cf041b2d944297390a5673eade719c756ab10.gz0000644000000000000000000000051610537077220023066 0ustar }QN0S|w*uD#߂,MeUCCn ]3#󝟝-О[$(;0G,Za{ i%,{k͗ 2+-{|ꪬo6$tk=#e<1qKvڑhfuX62t+8%ILdQ*FUu TSʤ*?h-!3\a|9~o/Yqd|GL;8~U6SOM*?Г1EPM IOmlga7֨*r>Aurlscan-0.5.6/_darcs/patches/20061203061302-ac53b-f248b874325e11366fd97dd8a7b614bbaf97ff87.gz0000644000000000000000000000025610534735040023246 0ustar MK 0o]/!.ӤE3b>}tdPQGؘv[|E-庪eO 2: VX@OCtub{C7AdE C29;BNss1kk%eurlscan-0.5.6/_darcs/patches/20061209184846-3fcb1-9946f1d39886d3bbb57b8ffe6be754300769680f.gz0000644000000000000000000000222010536602443023144 0ustar W[o6~80Ȫ-Nbc]mS`$D\h%^0R[ʺT/H߼+ X0+r~R^[ fs 3~ n\b%7\! rEӵ_|ÄY'7F,\&Fl*ㅟzu~vv9>?{;~srIȾFk-϶z;G,Ai˄E̤]2nƮ%L7P?LV|.햫bq-mgڧꂷݨ~QO' {,&+JQT&4ve ,AwAEښ͒Q\IAHg7͒Q70R×eBz6vp\OaZI&GTʹ-9]hZv/+n-[\9 +hTĤVxMaֈ7Aх\+ҞѼs@0e :(?{ET xp"g8^ *b5Rt+-ǽd x&*QM9_R F?qY{6xyK_I>sb'n 2VGȎ rʒ+GBX̭Zn<~\=mEy~M2~01񠱄Wq(C9G+WY_xz`s- urlscan-0.5.6/_darcs/patches/20070105053036-3fcb1-4768d639792f582e2641d6bfb251f7ca6b5b91ff.gz0000644000000000000000000000045410547361205023173 0ustar ]KK@aiTZ|ĕJD&5 NyPߝb8w{n e#&k;ŧ6IN %;oX\AH;n"N$LE.X,5**Zɳ'y1Ao}XͷiSUUS^j3^tfOыAiCF]ׇ Dz>=PNģ^׵!Vg?'ǯ]L쿆curlscan-0.5.6/_darcs/patches/20070116153426-3fcb1-738423210b438cdf152232cda456e289432e72d1.gz0000644000000000000000000000031710553170221022624 0ustar ;@،]BQdp KVヒ osp>ؘx>@QW(BRLEH`xu4nO@\m]^3"V;8[gTt7MLIꨆP~x]/6k~tQ;w(urlscan-0.5.6/_darcs/patches/20061210230443-3fcb1-1b282c429e8ab54e0ef18092b5ee8dec75eabf58.gz0000644000000000000000000000043710537111237023445 0ustar QK0+.]4bݖ)> |q+%k IIԩ&{sYԋ%"T/G ŸF&@5pQ& h[oF4=YB4MItYpɕ su67V}ƥK`Pbw5`? {#ރl7b':-MUFf~DR,U?FWDH?~%SkET$c,ח l8G|Eˋ^㒐 ?;y[_.*gHS7Q.M [[?6Y^d >e1rhNӲ镮9v۱k]W;6x!Kx=~dnq$K|Zurlscan-0.5.6/_darcs/patches/20061113053419-3fcb1-4ec3276c8d7f81146d95dddec699483821aa0ac0.gz0000644000000000000000000000365510526001740023237 0ustar Xko_ ؊}hKn mQbà%:L$[$eKsZ!F,y/}Y2+u%XKf^8li5Oʕ1Fo,5/*es{~~2b:~6L\JH/SقUlzrO5 .[i%VU}bÌ+^PwlUY ,`6ډr\_6ē?sv[KՀXmӓ 1Ft6o򟸱t[R%,}JɮN.68.rsn#~?n^h`mkf% J:Dv vrh0Ս$<Ɯ^ӝo+˴7iD $}r}cp4DONƫ2cg69K[q.\)1T;#/򚾤Cn])U=. ,B &I-2` ^< UJ?"N[(|s0@Sh>L ϥn>O#@!-yi~GIޞu&2zO8@bɵMvEeq p7[#Bk!5Izwugw]+>ߗdݟ,  .v?;qLY%Y999g[ |lEg1*s(kLR@+ e¶HyYx5w*"u8$@H弮t>G78BuR8q {2i6hO 2k-)\ 7SZv ""ZRJͣLu[V!^<5^^Uah`+ 4l[Ѧ4hZh8N ic@W7 juKc4@fl0N-9rlm.NG'6p=%Ӿ>Ɂ/x(_hc}uV im5vZrfÂG"mKi UR2|e|_І"KGd=n!*9dZt'IjQI " ATZ1?Wޱk0 gRnPSD=+]b -(-ʞso@uNזǩ ^>tp*ĎK չ_` j$oXpWX"?|s6HSU[`33"\01Zvl Q!|؆ڈ1. ]bFf*HCb{阝NC &ZQcբeN}rݬlE͇6=Ҝزj*kKze#}Z ]&ElQNFGڅ_='5A9zQ(œ'a  Ydz2v `Z #㵏ItEfHT~wdۙl%T:xIgHRax:QVWi*?r_CG6rX:v+gQūq:4E!,H:~]1}^=;93`oǮO|YB~K>PsP>}T&zHkQv|g"슙uP&a>īm$WTL ky.Xm"U;ٳܺm+6,@F9|@GL =sB%}0҆qd?m{݆Η${6`׬Y}O')xH&PR@Hx-qmǍ7~ֈAl AɟAި0GTISkMakZE[yurlscan-0.5.6/_darcs/patches/20061224160406-3fcb1-b61162330970ccaa20e187755a61d48180351fa1.gz0000644000000000000000000000105210543522511022603 0ustar TMo@W"Y-UCCժв;h] t/zf޼fkf W!po *LJ JThCBr_)kUB!V*ޢ%4A(,аf\)Yh9BKg,zT#p+&Rق.*Coa`_D7k "P ,JMoY[DU^im A`bW1{hmlI)1^)_>GHDgI2Y!C`C9oob?ʨ݆ (F=r~D%dWKYY6l]E؁92H_1Q"kcnɁi0.Oh|4HɀՊ&yptzQ+L=SjNAJOӚLń7ڃwG]% ӪO_@ >ȏ􃗰[Nx9wS+WA ]:IS`GRNQ2=""ӏŜ f|ѡ&m$@0v?8p~FO߮urlscan-0.5.6/_darcs/patches/20070201032037-3fcb1-0d613721e1a93c8601f883213812d22d0a5dc037.gz0000644000000000000000000000043410561031020022565 0ustar KK@m&q&cRƦ`'f>t!..~'[{iŖFk`ޘ<Q48\`h4 BD .nT{0?rq%c 1^ (h@h0agc oj\d&!a!I5QS'fLQ1g= Ncg / I3kxv&0!.>3u5&&sYޟ T<7Qurlscan-0.5.6/_darcs/patches/20061204051855-ac53b-9d314c5077537dab591f5792f435807ac2e83646.gz0000644000000000000000000000073510534735040022673 0ustar S]@}W rVF}1h|@b݁n6ߝ^iil:g̙f3u( 8 X-|%5Ecl>z/0\ƮLFn4Ny*͌q;B8TXz]̋8Dn⃏*"ϺҺI2EBn~- db7y o#{L!ܭL˪M80})0}J!m47+1A'hgcF٠ENFF ڬrF/ס9q,ݤ*|2i(@<|)rD"u7Q_*% ׯ_pܫ}EǣS؎mE y3Mk~&TN ڥ,urlscan-0.5.6/_darcs/pristine/0000755000000000000000000000000010561031020013133 5ustar urlscan-0.5.6/_darcs/pristine/COPYING0000644000000000000000000004310310537127673014216 0ustar GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. urlscan-0.5.6/_darcs/pristine/README0000644000000000000000000000526010537112015014025 0ustar urlscan Daniel Burrows 0) Purpose and Requirements urlscan is a small program that is designed to integrate with the "mutt" mailreader to allow you to easily launch a Web browser for URLs contained in email messages. It is a replacement for the "urlview" program. urlscan requires Python and the python-urwid library, as well as sensible-browser from the debianutils package. 1) Features urlscan parses an email message passed on standard input and scans it for URLs. It then displays the URLs and their context within the message, and allows you to choose one or more URLs to send to your Web browser. Relative to urlview, urlscan has the following additional features: (1) Support for emails in quoted-printable and base64 encodings. No more stripping out =40D from URLs by hand! (2) The context of each URL is provided along with the URL. For HTML mails, a crude parser is used to render the HTML into text. 2) Setting up urlscan To set up urlscan, install the Debian "urlscan" package (or use setup.py to install the program). Once urlscan is installed, add the following lines to your .muttrc: macro index,pager \cb " urlscan" "call urlscan to extract URLs out of a message" macro attach,compose \cb " urlscan" "call urlscan to extract URLs out of a message" Once this is done, Control-b while reading mail in mutt will automatically invoke urlscan on the message. urlscan uses sensible-browser to invoke the default Web browser of the current environment. To choose a particular browser, set the environment variable BROWSER; e.g., export BROWSER=/usr/bin/epiphany . 3) Known bugs and limitations (1) Because the Python curses module does not support wide characters (see Debian bug #336861), non-ASCII characters can cause unpredictable results in urlscan. This problem will go away if Python and urwid are patched to support wide characters. (6) Running urlscan sometimes "messes up" the terminal background. This seems to be an urwid bug, but I haven't tracked down just what's going on. (2) Extraction of context from HTML messages leaves something to be desired. Probably the ideal solution would be to extract context on a word basis rather than on a paragraph basis. (3) The HTML message handling is a bit kludgy in general. (4) multipart/alternative sections are handled by descending into all the sub-parts, rather than just picking one, which may lead to URLs and context appearing twice. (5) Configurability is more than a little bit lacking. urlscan-0.5.6/_darcs/pristine/urlscan.10000644000000000000000000000362110561031021014667 0ustar .\" Hey, EMACS: -*- nroff -*- .TH URLSCAN 1 "December 10, 2006" .SH NAME urlscan \- browse the URLs in an email message from a terminal .SH SYNOPSIS \fBurlscan\fR [options] < .I message \fBurlscan\fR [options] .I message .SH DESCRIPTION \fBurlscan\fR accepts a single email message on standard input, then displays a terminal-based list of the URLs in the given message. Selecting a URL will invoke \fBsensible-browser\fR(1) on it (and hence any browser specified in the \fBBROWSER\fR environment variable). \fBurlscan\fR is primarily intended to be used with the .B mutt (1) mailreader, but it should work well with any terminal-based mail program. \fBurlscan\fR is similar to \fBurlview\fR(1), but has the following additional features: \fB1.\fR Support for more message encodings, such as quoted-printable and base64. \fB2.\fR Extraction and display of the context surrounding each URL. .SH OPTIONS .TP .B \-b, \-\-background Run the Web browser in the background, so you can select another URL without closing it (this will not work with terminal-based Web browsers such as lynx, links, or w3m). .TP .B \-c, \-\-compact Display a simple list of the extracted URLs, instead of showing the context of each URL. .SH MUTT INTEGRATION To integrate urlscan with mutt, include the following two commands in \fB~/.muttrc\fR: .ad l macro index,pager \\cb " urlscan" "call urlscan to extract URLs out of a message" macro attach,compose \\cb " urlscan" "call urlscan to extract URLs out of a message" .ad b Once these lines are in your mutt configuration file, pressing Control-b will allow you to browse and open the URLs in the currently selected message. .SH SEE ALSO \fI/usr/share/doc/urlscan/README\fR, \fBsensible-browser\fR(1), \fBurlview\fR(1), \fBmutt\fR(1) .SH AUTHOR This manual page was written by Daniel Burrows . urlscan-0.5.6/_darcs/pristine/setup.py0000644000000000000000000000050710537107304014663 0ustar #!/usr/bin/env python from distutils.core import setup setup(name="urlscan", version="0.5", description="View the URLs in an email message", author="Daniel Burrows", author_email="dburrows@debian.org", package_dir={'' : 'modules'}, packages=['urlscan'], scripts=['urlscan'], ) urlscan-0.5.6/_darcs/pristine/modules/0000755000000000000000000000000010537077173014631 5ustar urlscan-0.5.6/_darcs/pristine/modules/urlscan/0000755000000000000000000000000010561406316016270 5ustar urlscan-0.5.6/_darcs/pristine/modules/urlscan/__init__.py0000644000000000000000000000005610537076527020414 0ustar __all__ = ['browser', 'urlchoose', 'urlscan'] urlscan-0.5.6/_darcs/pristine/modules/urlscan/urlscan.py0000644000000000000000000002776610561031021020316 0ustar # Copyright (C) 2006-2007 Daniel Burrows # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 2 of the # License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, Inc., 59 Temple Place - Suite 330, # Boston, MA 02111-1307, USA. '''Contains the backend logic that scans messages for URLs and context.''' import re import HTMLParser class Chunk: '''Represents a chunk of (marked-up) text that may or may not be linked to a URL. Attributes: markup - how this chunk will be rendered via urwid. This may be None if url is set, indicating that a URL footnote should be autogenerated. url - the URL to which this text is linked, or None if no URL link is present.''' def __init__(self, markup, url): self.markup = markup self.url = url def __str__(self): return 'Chunk(markup=%s, url=%s)'%(`self.markup`, `self.url`) def __repr__(self): return self.__str__() def isheadertag(t): return len(t) == 2 and t[0] == 'h' and t[1].isdigit() class HTMLChunker(HTMLParser.HTMLParser): '''An HTMLParser that generates a sequence of lists of chunks. Each list represents a single paragraph.''' def __init__(self): HTMLParser.HTMLParser.__init__(self) # This attribute is the current output list. self.rval = [] # If this attribute is True, the next chunk added will start a # new list. self.at_para_start = True self.trailing_space = False self.style_stack = [set()] self.anchor_stack = [None] self.list_stack = [] # either 'ul' or 'ol' entries. # Styled text uses the named attribute # msgtext:style1style2... where the styles # are always sorted in alphabetic order. # (if the text is in an anchor, substitute # "msgtext:anchor" for "msgtext") tag_styles = { 'b' : 'bold', 'i' : 'italic' } ul_tags = [ '*', '+', '-' ] def cur_url(self): return self.anchor_stack[-1] def add_chunk(self, chunk): if self.at_para_start: self.rval.append([]) elif self.trailing_space: self.rval[-1].append(Chunk(' ', self.cur_url())) self.rval[-1].append(chunk) self.at_para_start = False self.trailing_space = False def end_para(self): if self.at_para_start: self.rval.append([]) else: self.at_para_start = True self.trailing_space = False if len(self.list_stack) > 0: self.add_chunk(Chunk(' '*3*len(self.list_stack), self.cur_url())) def end_list_para(self): if self.at_para_start: self.rval.append([]) if len(self.list_stack) > 0: tp = self.list_stack[-1][0] if tp == 'ul': depth = len([t for t in self.list_stack if t[0] == tp]) ul_tags = HTMLChunker.ul_tags chunk = Chunk('%s '%(ul_tags[depth % len(ul_tags)]), self.cur_url()) else: counter = self.list_stack[-1][1] self.list_stack[-1] = (tp, counter + 1) chunk = Chunk("%2d."%counter, self.cur_url()) self.add_chunk(chunk) else: self.end_para() def findattr(self, attrs, searchattr): for attr, val in attrs: if attr == searchattr: return val return None # TODO: should have better formatting. def handle_starttag(self, tag, attrs): if tag == 'a': self.anchor_stack.append(self.findattr(attrs, 'href')) elif tag == 'ul' or tag == 'ol': self.list_stack.append((tag, 1)) self.end_para() elif tag in HTMLChunker.tag_styles: self.style_stack.append(self.style_stack[-1] | set([HTMLChunker.tag_styles[tag]])) elif isheadertag(tag): self.style_stack.append(self.style_stack[-1] | set(['bold'])) elif tag == 'p' or tag == 'br': self.end_para() elif tag == 'img': # Since we expect HTML *email*, image links # should be external (naja?) alt = self.findattr(attrs, 'alt') if alt == None: alt = '[IMG]' src = self.findattr(attrs, 'src') if src <> None and src[:7] <> 'http://': src = None if src <> None: self.anchor_stack.append(src) self.handle_data(alt) del self.anchor_stack[-1] else: self.handle_data(alt) elif tag == 'li': self.end_list_para() def handle_startendtag(self, tag, attrs): if tag in set(['p', 'br', 'li', 'img']): self.handle_starttag(tag, attrs) def handle_endtag(self, tag): if tag == 'a': del self.anchor_stack[-1] elif tag in HTMLChunker.tag_styles: del self.style_stack[-1] elif tag == 'ul' or tag == 'ol': del self.list_stack[-1] self.end_para() elif isheadertag(tag): del self.style_stack[-1] self.end_para() def handle_data(self, data): future_trailing_space = False if len(data) > 0: if data[0].isspace(): self.trailing_space = True if data[-1].isspace(): future_trailing_space = True data = ' '.join(data.split()) if self.anchor_stack[-1] == None: style = 'msgtext' else: style = 'anchor' if len(self.style_stack[-1]) > 0: stylelist = list(self.style_stack[-1]) stylelist.sort() style = style + ':' + ''.join(stylelist) self.add_chunk(Chunk((style, data), self.cur_url())) self.trailing_space = future_trailing_space extrachars = { 8212 : "--", 8217 : "'", 8220 : "``", 8221 : "''", 8230 : "..." } def handle_charref(self, name): n = int(name) if n < 256: name = chr(n) elif n in HTMLChunker.extrachars: name = HTMLChunker.extrachars[n] else: name = '&#%s;'%name self.handle_data(name) entities = { 'nbsp' : ' ', 'lt' : '<', 'gt' : '>', 'amp' : '&', 'ldquo' : '``', 'rdquo' : "''", 'apos' : "'" } def handle_entityref(self, name): if name in HTMLChunker.entities: self.handle_data(HTMLChunker.entities[name]) else: # If you see a reference, it needs to be # added above. self.handle_data('&%s;'%name) urlinternalpattern=r'[{}a-zA-Z/\-_0-9%?&.=:;+,#~]' urltrailingpattern=r'[{}a-zA-Z/\-_0-9%&=+#]' httpurlpattern = r'(?:https?://' + urlinternalpattern + r'*' + urltrailingpattern + r')' # Used to guess that blah.blah.blah.TLD is a URL. tlds=['biz', 'com', 'edu', 'info', 'org'] guessedurlpattern=r'(?:[a-zA-Z0-9_\-%]+(?:\.[a-zA-Z0-9_\-%]+)*\.(?:' + '|'.join(tlds) + '))' urlre = re.compile(r'(?:<(?:URL:)?)?(' + httpurlpattern + '|' + guessedurlpattern + '|(?:mailto:[a-zA-Z0-9\-_]*@[0-9a-zA-Z_\-.]*[0-9a-zA-Z_\-]))>?') # Poor man's test cases. assert(urlre.match('')) assert(urlre.match('http://linuxtoday.com')) assert(re.compile(guessedurlpattern).match('example.biz')) assert(urlre.match('example.biz')) assert(urlre.match('linuxtoday.com')) assert(urlre.match('master.wizard.edu')) assert(urlre.match('blah.bar.info')) assert(urlre.match('goodpr.org')) assert(not urlre.match('blah..org')) def parse_text_urls(s): '''Parse a block of text, splitting it into its url and non-url components.''' rval = [] loc = 0 for match in urlre.finditer(s): if loc < match.start(): rval.append(Chunk(s[loc:match.start()], None)) rval.append(Chunk(None, match.group(1))) loc = match.end() if loc < len(s): rval.append(Chunk(s[loc:], None)) return rval def extract_with_context(lst, pred, before_context, after_context): rval = [] start = 0 length = 0 while start < len(lst): usedfirst = False usedlast = False # Extend to the next match. while start + length < len(lst) and length < before_context + 1 and not pred(lst[start + length]): length += 1 # Slide to the next match. while start + length < len(lst) and not pred(lst[start + length]): start += 1 # If there was no next match, abort here (it's easier # to do this than to try to detect this case later). if start + length == len(lst): break # Now extend repeatedly until we can't find anything. while start + length < len(lst) and pred(lst[start + length]): extendlength = 1 # Read in the 'after' context and see if it holds a URL. while extendlength < after_context + 1 and start + length + extendlength < len(lst) and not pred(lst[start + length + extendlength]): extendlength += 1 length += extendlength if start + length < len(lst) and not pred(lst[start + length]): # Didn't find a matching line, so we either # hit the end or extended to after_context + 1.. # # Now read in possible 'before' context # from the next URL; if we don't find one, # we discard the readahead. extendlength = 1 while extendlength < before_context and start + length + extendlength < len(lst) and not pred(lst[start + length + extendlength]): extendlength += 1 if start + length + extendlength < len(lst) and pred(lst[start + length + extendlength]): length += extendlength if length > 0 and start + length <= len(lst): if start == 0: usedfirst = True if start + length == len(lst): usedlast = True rval.append((lst[start:start + length], usedfirst, usedlast)) start += length length = 0 return rval nlre = re.compile('\r\n|\n|\r') def extracturls(s): '''Given a text message, extract all the URLs found in the message, along with their surrounding context. The output is a list of sequences of Chunk objects, corresponding to the contextual regions extracted from the string.''' rval = [] lines = nlre.split(s) # The number of lines of context above to provide. above_context = 1 # The number of lines of context below to provide. below_context = 1 # Plan here is to first transform lines into the form # [line_fragments] where each fragment is a chunk as # seen by parse_text_urls. Note that this means that # lines with more than one entry or one entry that's # a URL are the only lines containing URLs. linechunks = [parse_text_urls(l) for l in lines] return extract_with_context(linechunks, lambda chunk:len(chunk) > 1 or (len(chunk) == 1 and chunk[0].url <> None), 1, 1) def extracthtmlurls(s): c = HTMLChunker() c.feed(s) c.close() above_context = 1 below_context = 1 def somechunkisurl(chunks): for chunk in chunks: if chunk.url <> None: return True return False return extract_with_context(c.rval, somechunkisurl, 1, 1) urlscan-0.5.6/_darcs/pristine/modules/urlscan/urlchoose.py0000644000000000000000000001417210561406316020652 0ustar # Copyright (C) 2006-2007 Daniel Burrows # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 2 of the # License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, Inc., 59 Temple Place - Suite 330, # Boston, MA 02111-1307, USA. '''An urwid listview-based widget that lets you choose a URL from a list of URLs.''' import urwid import urwid.curses_display import browser def mkbrowseto(url, background): return lambda *args:browser.browseto(url, background) # Based on urwid examples. class URLChooser: def __init__(self, extractedurls, compact_mode = False, background = False): self.compact_mode = compact_mode self.items = [ ] urlidx = 1 first = True firstbutton=0 self.urls = [] for group, usedfirst, usedlast in extractedurls: if first: first = False elif not self.compact_mode: self.items.append(urwid.Divider(div_char = '-', top = 1, bottom = 1)) groupurls = [] markup = [] if self.compact_mode: lasturl = None for chunks in group: for chunk in chunks: if chunk.url and chunk.url != lasturl: groupurls.append(chunk.url) self.urls.append(chunk.url) lasturl = chunk.url else: if not usedfirst: markup.append(('msgtext:ellipses', '...\n')) for chunks in group: i = 0 while i < len(chunks): chunk = chunks[i] i += 1 if chunk.url == None: markup.append(chunk.markup) else: self.urls.append(chunk.url) groupurls.append(chunk.url) # Collect all immediately adjacent # chunks with the same URL. tmpmarkup = [] if chunk.markup: tmpmarkup.append(chunk.markup) while i < len(chunks) and chunks[i].url == chunk.url: if chunks[i].markup: tmpmarkup.append(chunks[i].markup) i += 1 markup += [tmpmarkup or '', ('urlref:number:braces', ' ['), ('urlref:number', `len(self.urls)`), ('urlref:number:braces', ']')] markup += '\n' if not usedlast: markup += [('msgtext:ellipses', '...\n\n')] self.items.append(urwid.Text(markup)) i = len(self.urls) - len(groupurls) for url in groupurls: if firstbutton == 0 and not self.compact_mode: firstbutton = len(self.items) i += 1 markup = [ ('urlref:number:braces', '['), ('urlref:number', `i`), ('urlref:number:braces', ']'), ' ', ('urlref:url', url) ] self.items.append(urwid.Button(markup, mkbrowseto(url, background))) self.listbox = urwid.ListBox(self.items) self.listbox.set_focus(firstbutton) if len(self.urls) == 1: header = 'Found 1 url.' else: header = 'Found %d urls.'%len(self.urls) headerwid = urwid.AttrWrap(urwid.Text(header), 'header') self.top = urwid.Frame(self.listbox, headerwid) def main(self): self.ui = urwid.curses_display.Screen() self.ui.register_palette([ ('header', 'white', 'dark blue', 'standout'), ('msgtext', 'light gray', 'black'), ('msgtext:bullet', 'white', 'black', 'standout'), ('msgtext:bold', 'white', 'black' , 'standout'), ('msgtext:italic', 'dark cyan', 'black', 'standout'), ('msgtext:bolditalic', 'light cyan', 'black', 'standout'), ('anchor', 'yellow', 'black', 'standout'), ('anchor:bold', 'yellow', 'black', 'standout'), ('anchor:italic', 'yellow', 'black', 'standout'), ('anchor:bolditalic', 'yellow', 'black', 'standout'), ('msgtext:ellipses', 'light gray', 'black'), ('urlref:number:braces', 'light gray', 'black'), ('urlref:number', 'yellow', 'black', 'standout'), ('urlref:url', 'white', 'black', 'standout') ]) return self.ui.run_wrapper(self.run) def run(self): size = self.ui.get_cols_rows() try: while True: self.ui.s.erase() self.draw_screen(size) keys = self.ui.get_input() for k in keys: if k == 'window resize': size = self.ui.get_cols_rows() elif k == 'q': return None elif k == 'ctrl l': self.ui.s.clear() else: self.top.keypress(size, k) except KeyboardInterrupt: return None def draw_screen(self, size): canvas = self.top.render(size, focus = True) self.ui.draw_screen(size, canvas) urlscan-0.5.6/_darcs/pristine/modules/urlscan/browser.py0000644000000000000000000000205610561031021020313 0ustar # Copyright (C) 2006-2007 Daniel Burrows # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 2 of the # License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, Inc., 59 Temple Place - Suite 330, # Boston, MA 02111-1307, USA. '''Contains logic to invoke the default system browser.''' import os def browseto(url, background = False): if background: mode = os.P_NOWAIT else: mode = os.P_WAIT os.spawnl(mode, '/usr/bin/sensible-browser', '/usr/bin/sensible-browser', url) urlscan-0.5.6/_darcs/pristine/debian/0000755000000000000000000000000010561406350014371 5ustar urlscan-0.5.6/_darcs/pristine/debian/compat0000644000000000000000000000000210537106160015566 0ustar 5 urlscan-0.5.6/_darcs/pristine/debian/rules0000644000000000000000000000077310537114325015456 0ustar #!/usr/bin/make -f clean: dh_testdir ./setup.py clean -a dh_clean build: install: dh_testdir dh_testroot dh_clean -k ./setup.py install --no-compile --prefix=$(CURDIR)/debian/urlscan/usr binary-indep: install dh_testdir dh_testroot dh_installchangelogs dh_installdocs README dh_installman urlscan.1 dh_fixperms dh_pycentral dh_compress dh_installdeb dh_gencontrol dh_md5sums dh_builddeb binary-arch: binary: binary-indep .PHONY: clean binary-indep binary-arch binary install build urlscan-0.5.6/_darcs/pristine/debian/control0000644000000000000000000000162210537130246015776 0ustar Source: urlscan Section: mail Priority: optional Maintainer: Daniel Burrows Build-Depends: debhelper (>= 5.0.37.2), python-central (>= 0.5) Build-Depends-Indep: debhelper (>= 5.0.37.2), python-central (>= 0.5), python (>= 2.3.0), python-dev (>= 2.3.0) Standards-Version: 3.7.2 Package: urlscan Architecture: all Depends: ${python:Depends}, python-urwid Suggests: mutt, www-browser XS-Python-Version: current XB-Python-Version: ${python:Versions} Description: Extract and browse the URLs contained in an email (urlview replacement) urlscan searches for URLs in email messages, then displays a list of them in the current terminal. It is primarily meant as a replacement for urlview, which it improves upon in the following ways: . * urlscan understands email encodings such as quoted-printable; urlview does not. * urlscan extracts and displays the context surrounding each URL. urlscan-0.5.6/_darcs/pristine/debian/changelog0000644000000000000000000000365410561406323016253 0ustar urlscan (0.5.6) unstable; urgency=low * Render HTML entities #8212 and "apos" sensibly. * Recognize braces as valid URL characters (Closes: #407423). * Add an option to allow the browser to be invoked in the background (Closes: #406083). * Guess that FOO.tld is probably a URL (Closes: #408684). -- Daniel Burrows Sun, 4 Feb 2007 08:41:55 -0800 urlscan (0.5.5) unstable; urgency=low * Add support for accepting a single message to scan on the command-line. * Add a --compact option that suppresses URL context. (Closes: #404605) -- Daniel Burrows Thu, 4 Jan 2007 21:29:25 -0800 urlscan (0.5.4) unstable; urgency=low * Don't crash on ordered lists. (Closes: #404137) * Make the URL detection regexp cover more cases. (Closes: #403713) * Spawn the browser in the foreground, needed for text browsers that run in the same terminal (Closes: #403644). * Recognize … as an ellipsis. * Merge immediately adjacent references to the same URL (the HTML parser tends to generate these if contains markup or entities). -- Daniel Burrows Sun, 24 Dec 2006 08:07:13 -0800 urlscan (0.5.3) unstable; urgency=low * Recognize URLs containing internal colons. -- Daniel Burrows Sun, 17 Dec 2006 21:02:20 -0800 urlscan (0.5.2) unstable; urgency=low * Fix dumb typo in copyright notice. (famous last words: "I'm just changing a comment. What could POSSIBLY go wrong with that?) (Closes: #403179) -- Daniel Burrows Sun, 17 Dec 2006 20:55:01 -0800 urlscan (0.5.1) unstable; urgency=low * Add explicit copyright notices. * Suggest mutt and www-browser. -- Daniel Burrows Sun, 10 Dec 2006 17:13:49 -0800 urlscan (0.5) unstable; urgency=low * Initial Upload. -- Daniel Burrows Sun, 10 Dec 2006 14:47:05 -0800 urlscan-0.5.6/_darcs/pristine/debian/copyright0000644000000000000000000000153710537106223016331 0ustar Upstream Author: Daniel Burrows Copyright: This package is free software; you can redistribute it and/or mo dify it under the terms of the GNU General Public License as publishe d by the Free Software Foundation; version 2 dated June, 1991. This package is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public Licen se along with this program; if not, write to the Free Software Foundatio n, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. On Debian systems, the complete text of the GNU General Public License can be found in `/usr/share/common-licenses/GPL'. urlscan-0.5.6/_darcs/pristine/urlscan0000644000000000000000000000613510561031021014533 0ustar #!/usr/bin/python # # A simple urlview replacement that handles things like quoted-printable properly. # aka "urlview minus teh suck" # # Copyright (C) 2006-2007 Daniel Burrows # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 2 of the # License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, Inc., 59 Temple Place - Suite 330, # Boston, MA 02111-1307, USA. import os import sys import re import optparse import email.Parser optparser = optparse.OptionParser(usage='%prog [options] [<] message') optparser.add_option('-c', '--compact', action='store_true', dest="compact", default=False, help='Don\'t display the context of each URL.') optparser.add_option('-b', '--background', action='store_true', dest="background", default=False, help='Run the Web browser as a background process.') options, args = optparser.parse_args() homedir = os.path.dirname(sys.argv[0]) moduledir = os.path.join(homedir, 'modules') if os.path.isdir(moduledir): sys.path = [moduledir] + sys.path from urlscan import urlchoose from urlscan import urlscan # Written as a generator so I can easily choose only # one subpart in the future (e.g., for # multipart/alternative). Actually, I might even add # a browser for the message structure? def msgurls(msg, urlidx = 1): if msg.is_multipart(): for part in msg.get_payload(): for chunk in msgurls(part, urlidx): urlidx += 1 yield chunk elif msg.get_content_type() == 'text/plain': for chunk in urlscan.extracturls(msg.get_payload(decode = True)): urlidx += 1 yield chunk elif msg.get_content_type() == 'text/html': for chunk in urlscan.extracthtmlurls(msg.get_payload(decode = True)): urlidx += 1 yield chunk def main(msg): global options ui = urlchoose.URLChooser(msgurls(msg), compact_mode = options.compact, background = options.background) ui.main() # TODO: accept messages on the command-line? if len(args) > 1: sys.stderr.write('Too many messages.\n') elif len(args) == 1: msg = email.Parser.Parser().parse(file(args[0], 'r')) main(msg) else: msg = email.Parser.Parser().parse(sys.stdin) if not os.isatty(0): fd = os.open('/dev/tty', os.O_RDONLY) if fd < 0: sys.stderr.write('Unable to open an input tty.\n') sys.exit(-1) else: os.dup2(fd, 0) os.close(fd) main(msg) urlscan-0.5.6/_darcs/prefs/0000755000000000000000000000000010561031025012422 5ustar urlscan-0.5.6/_darcs/prefs/boring0000644000000000000000000000102610525656715013647 0ustar # Boring file regexps: \.hi$ \.o$ \.o\.cmd$ # *.ko files aren't boring by default because they might # be Korean translations rather than kernel modules. # \.ko$ \.ko\.cmd$ \.mod\.c$ (^|/)\.tmp_versions($|/) (^|/)CVS($|/) (^|/)RCS($|/) ~$ #(^|/)\.[^/] (^|/)_darcs($|/) \.bak$ \.BAK$ \.orig$ (^|/)vssver\.scc$ \.swp$ (^|/)MT($|/) (^|/)\{arch\}($|/) (^|/).arch-ids($|/) (^|/), \.class$ \.prof$ (^|/)\.DS_Store$ (^|/)BitKeeper($|/) (^|/)ChangeSet($|/) (^|/)\.svn($|/) \.py[co]$ \# \.cvsignore$ (^|/)Thumbs\.db$ (^|/)autom4te\.cache($|/) urlscan-0.5.6/_darcs/prefs/binaries0000644000000000000000000000064710525656715014173 0ustar # Binary file regexps: \.png$ \.PNG$ \.gz$ \.GZ$ \.pdf$ \.PDF$ \.jpg$ \.JPG$ \.jpeg$ \.JPEG$ \.gif$ \.GIF$ \.tif$ \.TIF$ \.tiff$ \.TIFF$ \.pnm$ \.PNM$ \.pbm$ \.PBM$ \.pgm$ \.PGM$ \.ppm$ \.PPM$ \.bmp$ \.BMP$ \.mng$ \.MNG$ \.tar$ \.TAR$ \.bz2$ \.BZ2$ \.z$ \.Z$ \.zip$ \.ZIP$ \.jar$ \.JAR$ \.so$ \.SO$ \.a$ \.A$ \.tgz$ \.TGZ$ \.mpg$ \.MPG$ \.mpeg$ \.MPEG$ \.iso$ \.ISO$ \.exe$ \.EXE$ \.doc$ \.DOC$ \.elc$ \.ELC$ \.pyc$ \.PYC$ urlscan-0.5.6/_darcs/prefs/defaultrepo0000644000000000000000000000001410561031025014652 0ustar ../outgoing urlscan-0.5.6/_darcs/prefs/motd0000644000000000000000000000000010525656715013321 0ustar urlscan-0.5.6/_darcs/prefs/repos0000644000000000000000000000007110561031025013473 0ustar ../outgoing alpaca:/home/daniel/programming/urlscan/head urlscan-0.5.6/_darcs/inventory0000644000000000000000000000333710561406350013300 0ustar Starting with tag: [TAG 0.5.5 Daniel Burrows **20070105053047] [Recognize HTML entity #8212 as a long hyphen. Daniel Burrows **20070116153426] [Recognize "'" as an apostrophe. Daniel Burrows **20070120180732] [Recognize braces as valid URL characters (Closes: #407423). Daniel Burrows **20070201032037] [Retroactively fix a totally wrong changelog entry. Daniel Burrows **20070203014645] [Remove some cruft left behind by applying both my changes for options.compact and the submitter's changes. Daniel Burrows **20070203015132] [Add support to the browser-spawning code for running the browser either in the foreground or in the background. Daniel Burrows **20070203015220] [Add support to the URL chooser for running the browser either in the foreground or in the background. Daniel Burrows **20070203015241] [Add a command-line option that causes the browser to be invoked in the background. (Closes: #406083) Daniel Burrows **20070203015258] [Document both the short and the long form of --compact. Daniel Burrows **20070203015408] [Document -b and --background. Daniel Burrows **20070203015531] [Clean up the URL expression a bit and add support for guessing that BLAH.tld is likely a URL. (Closes: #408684) Daniel Burrows **20070203021227] [Add a changelog entry for 0.5.6. Daniel Burrows **20070204164158] urlscan-0.5.6/setup.py0000755000000000000000000000050710537107304011576 0ustar #!/usr/bin/env python from distutils.core import setup setup(name="urlscan", version="0.5", description="View the URLs in an email message", author="Daniel Burrows", author_email="dburrows@debian.org", package_dir={'' : 'modules'}, packages=['urlscan'], scripts=['urlscan'], ) urlscan-0.5.6/modules/0000755000000000000000000000000010537076460011537 5ustar urlscan-0.5.6/modules/urlscan/0000755000000000000000000000000010561406401013173 5ustar urlscan-0.5.6/modules/urlscan/urlchoose.pyc0000644000000000000000000001144210561233126015717 0ustar m 2Ec@sAdZdkZdkZdkZdZdfdYZdS(sNAn urwid listview-based widget that lets you choose a URL from a list of URLs.Ncs dS(NcstiS(N(tbrowsertbrowsetoturlt background(targs(RR(tB/home/daniel/programming/urlscan/head/modules/urlscan/urlchoose.pyts((RR((RRRt mkbrowsetost URLChoosercBs2tZeedZdZdZdZRS(Nc Cs||_g|_d}t}d} g|_x#|D]\} }} |o t }n7|ip,|ii tiddddddng}g}|iovd} x| D]a}xX|D]P} | io@| i| jo0|i | i|ii | i| i} qqWqWn|p|i dnxG| D]?}d}x&|t|jo||} |d7}| idjo|i | iqD|ii | i|i | ig}| io|i | inx_|t|joK||i| ijo4||io|i ||in|d7}qW||pd dd t|i fdg7}qDW|d7}q5W| p|dg7}n|ii ti|t|it|}x|D]}| djo|i ot|i} n|d7}dd | fddd|fg}|ii ti|t||qWq4Wti|i|_|ii | t|idjo d}ndt|i}ti"ti|d}ti$|i||_%dS(Niitdiv_chart-ttoptbottomsmsgtext:ellipsess... ssurlref:number:bracess [s urlref:numbert]s s... t[t s urlref:urls Found 1 url.sFound %d urls.theader(smsgtext:ellipsess... (surlref:number:bracess [(surlref:number:bracesR (smsgtext:ellipsess... (surlref:number:bracesR(surlref:number:bracesR (&t compact_modetselftitemsturlidxtTruetfirstt firstbuttonturlst extractedurlstgroupt usedfirsttusedlasttFalsetappendturwidtDividert groupurlstmarkuptNonetlasturltchunkstchunkRtitlent tmpmarkuptTexttButtonRRtListBoxtlistboxt set_focusRtAttrWrapt headerwidtFrameR (RRRRRR)R0RR%RRR&RR$R!R'R"RRR((Rt__init__s         *2 "- cCs_tii|_|iidddddddddd d!d"d#d$g|ii|iS(%NRtwhites dark bluetstandouttmsgtexts light graytblacksmsgtext:bullets msgtext:boldsmsgtext:italics dark cyansmsgtext:bolditalics light cyantanchortyellows anchor:bolds anchor:italicsanchor:bolditalicsmsgtext:ellipsessurlref:number:bracess urlref:numbers urlref:url(sheaderR3s dark blueR4(R5s light grayR6(smsgtext:bulletR3R6R4(s msgtext:boldR3R6R4(smsgtext:italics dark cyanR6R4(smsgtext:bolditalics light cyanR6R4(R7R8R6R4(s anchor:boldR8R6R4(s anchor:italicR8R6R4(sanchor:bolditalicR8R6R4(smsgtext:ellipsess light grayR6(surlref:number:bracess light grayR6(s urlref:numberR8R6R4(s urlref:urlR3R6R4(Rt raw_displaytScreenRtuitregister_palettet run_wrappertrun(R((Rtmainns:cCs|ii}yxto|iii|i||ii}xw|D]o}|djo|ii}qO|djodSqO|djo|iii qO|i i||qOWqWWntj o dSnXdS(Ns window resizetqsctrl l(RR;t get_cols_rowstsizeRtsteraset draw_screent get_inputtkeystkR#tclearR tkeypresstKeyboardInterrupt(RRGRHRB((RR>s$     cCs/|ii|dt}|ii||dS(Ntfocus(RR trenderRBRtcanvasR;RE(RRBRN((RREs(t__name__t __module__RR2R?R>RE(((RRsP  (t__doc__Rturwid.raw_displayRRR(RRRR((Rt?s     urlscan-0.5.6/modules/urlscan/__init__.py0000644000000000000000000000005610537076527015324 0ustar __all__ = ['browser', 'urlchoose', 'urlscan'] urlscan-0.5.6/modules/urlscan/urlscan.pyc0000644000000000000000000002435710561233126015374 0ustar m 2Ec@sdZdkZdkZdfdYZdZdeifdYZdZdZd ed ed Zd d dddgZ ddi e dZ ei dede dZ e idpte idptei e idpte idpte idpte idpte idpte idpte id ptdZdZei d Zd!Zd"ZdS(#sDContains the backend logic that scans messages for URLs and context.NtChunkcBs)tZdZdZdZdZRS(s{Represents a chunk of (marked-up) text that may or may not be linked to a URL. Attributes: markup - how this chunk will be rendered via urwid. This may be None if url is set, indicating that a URL footnote should be autogenerated. url - the URL to which this text is linked, or None if no URL link is present.cCs||_||_dS(N(tmarkuptselfturl(RRR((t@/home/daniel/programming/urlscan/head/modules/urlscan/urlscan.pyt__init__"s cCsd|i |i fS(NsChunk(markup=%s, url=%s)(RRR(R((Rt__str__&scCs |iS(N(RR(R((Rt__repr__)s(t__name__t __module__t__doc__RRR(((RRs   cCs2t|djo|ddjo|diS(Niithi(tlentttisdigit(R ((Rt isheadertag,st HTMLChunkercBstZdZdZhdd<ddR0R<R;((Rthandle_starttag~s2  / (       cCs7|tddddgjo|i||ndS(NR8R9R=R:(R>RRRAR0(RR>R0((Rthandle_startendtagscCs|djo|id=nv|tijo|id=nX|djp |djo|id=|in&t|o|id=|indS(NR5iR)R7( R>RRRR?RRR(R(RR>((Rt handle_endtags    cCst}t|djo=|dio t|_n|dio t}qVndi|i }|i ddjo d}nd}t|i ddjo8t|i d}|i|ddi|}n|it||f|i||_dS(NiiR$tmsgtexttanchort:t(Rtfuture_trailing_spaceR tdatatisspaceRRRtjointsplitRRtstyleRtlistt stylelisttsortR'RR#(RRIRHRMRO((RR@s    "i s--i t'i s``i s''i& s...cCset|}|djot|}n,|tijoti|}n d|}|i|dS(Nis&#%s;(tinttnametntchrRt extracharsRR@(RRSRT((Rthandle_charrefs   tnbspR$tltttampt&tldquotrdquotaposcCs=|tijo|iti|n|id|dS(Ns&%s;(RSRtentitiesRR@(RRS((Rthandle_entityrefs(RR R RR?R-R#R'R(R/R4RARBRCR@RVRWRbRc(((RR/s"        3 E s[{}a-zA-Z/\-_0-9%?&.=:;+,#~]s[{}a-zA-Z/\-_0-9%&=+#]s (?:https?://R t)tbiztcomtedutinfotorgs/(?:[a-zA-Z0-9_\-%]+(?:\.[a-zA-Z0-9_\-%]+)*\.(?:t|s))s(?:<(?:URL:)?)?(s=|(?:mailto:[a-zA-Z0-9\-_]*@[0-9a-zA-Z_\-.]*[0-9a-zA-Z_\-]))>?sshttp://linuxtoday.coms example.bizslinuxtoday.comsmaster.wizard.edus blah.bar.infos goodpr.orgs blah..orgcCsg}d}x|ti|D]k}||ijo'|it|||i!dn|itd|i d|i }qW|t |jo|it||dn|S(sHParse a block of text, splitting it into its url and non-url components.iiN( RtlocturlretfinditertstmatchtstartR%RRtgrouptendR (RnRkRRo((Rtparse_text_urlss'c Csg}d}d}x|t|jot}t}xM||t|jo5||djo$|||| o|d7}q7Wx<||t|jo$|||| o|d7}qW||t|joPnxf||t|joN||||o9d} xU| |djoC||| t|jo(|||||  o| d7} qW|| 7}||t|jo|||| od} xQ| |joC||| t|jo(|||||  o| d7} qW||| t|jo'||||| o|| 7}qCqqW|djot||t|jo]|djo t }n||t|jo t }n|i ||||!||fn||7}d}qW|S(Nii(RRptlengthR tlstRt usedfirsttusedlasttbefore_contexttpredt extendlengtht after_contextRR%( RuRyRxR{RvRwRRtRpRz((Rtextract_with_context sL>-,F -B4$   %  s | | cCs^g}ti|}d}d}g}|D]}|t |q,~}t |dddS(sGiven a text message, extract all the URLs found in the message, along with their surrounding context. The output is a list of sequences of Chunk objects, corresponding to the contextual regions extracted from the string.icCs7t|djp$t|djo|didjS(Nii(R R&RR(R&((RtZsN( RtnlreRLRntlinest above_contextt below_contextR+tlRst linechunksR|(RnRRRRR+RR((Rt extracturlsBs'cCsKt}|i||id}d}d}t|i |ddS(NicCs-x&|D]}|idjotSqqWtS(N(tchunksR&RRRR(RR&((Rtsomechunkisurlds  ( RtctfeedRntcloseRRRR|R(RnRRRR((Rtextracthtmlurls]s     (R treRRRRturlinternalpatternturltrailingpatternthttpurlpatternttldsRKtguessedurlpatterntcompileRlRotAssertionErrorRsR|R~RR(RRR|R~RRRRRRRRsRRlRR((Rt?s2      5 urlscan-0.5.6/modules/urlscan/__init__.pyc0000644000000000000000000000027310537077115015462 0ustar m W}|Ec@sdddgZdS(tbrowsert urlchooseturlscanN(t__all__(R((t%./urlscan-modules/urlscan/__init__.pyt?surlscan-0.5.6/modules/urlscan/urlscan.py0000644000000000000000000002776610561031021015226 0ustar # Copyright (C) 2006-2007 Daniel Burrows # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 2 of the # License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, Inc., 59 Temple Place - Suite 330, # Boston, MA 02111-1307, USA. '''Contains the backend logic that scans messages for URLs and context.''' import re import HTMLParser class Chunk: '''Represents a chunk of (marked-up) text that may or may not be linked to a URL. Attributes: markup - how this chunk will be rendered via urwid. This may be None if url is set, indicating that a URL footnote should be autogenerated. url - the URL to which this text is linked, or None if no URL link is present.''' def __init__(self, markup, url): self.markup = markup self.url = url def __str__(self): return 'Chunk(markup=%s, url=%s)'%(`self.markup`, `self.url`) def __repr__(self): return self.__str__() def isheadertag(t): return len(t) == 2 and t[0] == 'h' and t[1].isdigit() class HTMLChunker(HTMLParser.HTMLParser): '''An HTMLParser that generates a sequence of lists of chunks. Each list represents a single paragraph.''' def __init__(self): HTMLParser.HTMLParser.__init__(self) # This attribute is the current output list. self.rval = [] # If this attribute is True, the next chunk added will start a # new list. self.at_para_start = True self.trailing_space = False self.style_stack = [set()] self.anchor_stack = [None] self.list_stack = [] # either 'ul' or 'ol' entries. # Styled text uses the named attribute # msgtext:style1style2... where the styles # are always sorted in alphabetic order. # (if the text is in an anchor, substitute # "msgtext:anchor" for "msgtext") tag_styles = { 'b' : 'bold', 'i' : 'italic' } ul_tags = [ '*', '+', '-' ] def cur_url(self): return self.anchor_stack[-1] def add_chunk(self, chunk): if self.at_para_start: self.rval.append([]) elif self.trailing_space: self.rval[-1].append(Chunk(' ', self.cur_url())) self.rval[-1].append(chunk) self.at_para_start = False self.trailing_space = False def end_para(self): if self.at_para_start: self.rval.append([]) else: self.at_para_start = True self.trailing_space = False if len(self.list_stack) > 0: self.add_chunk(Chunk(' '*3*len(self.list_stack), self.cur_url())) def end_list_para(self): if self.at_para_start: self.rval.append([]) if len(self.list_stack) > 0: tp = self.list_stack[-1][0] if tp == 'ul': depth = len([t for t in self.list_stack if t[0] == tp]) ul_tags = HTMLChunker.ul_tags chunk = Chunk('%s '%(ul_tags[depth % len(ul_tags)]), self.cur_url()) else: counter = self.list_stack[-1][1] self.list_stack[-1] = (tp, counter + 1) chunk = Chunk("%2d."%counter, self.cur_url()) self.add_chunk(chunk) else: self.end_para() def findattr(self, attrs, searchattr): for attr, val in attrs: if attr == searchattr: return val return None # TODO: should have better formatting. def handle_starttag(self, tag, attrs): if tag == 'a': self.anchor_stack.append(self.findattr(attrs, 'href')) elif tag == 'ul' or tag == 'ol': self.list_stack.append((tag, 1)) self.end_para() elif tag in HTMLChunker.tag_styles: self.style_stack.append(self.style_stack[-1] | set([HTMLChunker.tag_styles[tag]])) elif isheadertag(tag): self.style_stack.append(self.style_stack[-1] | set(['bold'])) elif tag == 'p' or tag == 'br': self.end_para() elif tag == 'img': # Since we expect HTML *email*, image links # should be external (naja?) alt = self.findattr(attrs, 'alt') if alt == None: alt = '[IMG]' src = self.findattr(attrs, 'src') if src <> None and src[:7] <> 'http://': src = None if src <> None: self.anchor_stack.append(src) self.handle_data(alt) del self.anchor_stack[-1] else: self.handle_data(alt) elif tag == 'li': self.end_list_para() def handle_startendtag(self, tag, attrs): if tag in set(['p', 'br', 'li', 'img']): self.handle_starttag(tag, attrs) def handle_endtag(self, tag): if tag == 'a': del self.anchor_stack[-1] elif tag in HTMLChunker.tag_styles: del self.style_stack[-1] elif tag == 'ul' or tag == 'ol': del self.list_stack[-1] self.end_para() elif isheadertag(tag): del self.style_stack[-1] self.end_para() def handle_data(self, data): future_trailing_space = False if len(data) > 0: if data[0].isspace(): self.trailing_space = True if data[-1].isspace(): future_trailing_space = True data = ' '.join(data.split()) if self.anchor_stack[-1] == None: style = 'msgtext' else: style = 'anchor' if len(self.style_stack[-1]) > 0: stylelist = list(self.style_stack[-1]) stylelist.sort() style = style + ':' + ''.join(stylelist) self.add_chunk(Chunk((style, data), self.cur_url())) self.trailing_space = future_trailing_space extrachars = { 8212 : "--", 8217 : "'", 8220 : "``", 8221 : "''", 8230 : "..." } def handle_charref(self, name): n = int(name) if n < 256: name = chr(n) elif n in HTMLChunker.extrachars: name = HTMLChunker.extrachars[n] else: name = '&#%s;'%name self.handle_data(name) entities = { 'nbsp' : ' ', 'lt' : '<', 'gt' : '>', 'amp' : '&', 'ldquo' : '``', 'rdquo' : "''", 'apos' : "'" } def handle_entityref(self, name): if name in HTMLChunker.entities: self.handle_data(HTMLChunker.entities[name]) else: # If you see a reference, it needs to be # added above. self.handle_data('&%s;'%name) urlinternalpattern=r'[{}a-zA-Z/\-_0-9%?&.=:;+,#~]' urltrailingpattern=r'[{}a-zA-Z/\-_0-9%&=+#]' httpurlpattern = r'(?:https?://' + urlinternalpattern + r'*' + urltrailingpattern + r')' # Used to guess that blah.blah.blah.TLD is a URL. tlds=['biz', 'com', 'edu', 'info', 'org'] guessedurlpattern=r'(?:[a-zA-Z0-9_\-%]+(?:\.[a-zA-Z0-9_\-%]+)*\.(?:' + '|'.join(tlds) + '))' urlre = re.compile(r'(?:<(?:URL:)?)?(' + httpurlpattern + '|' + guessedurlpattern + '|(?:mailto:[a-zA-Z0-9\-_]*@[0-9a-zA-Z_\-.]*[0-9a-zA-Z_\-]))>?') # Poor man's test cases. assert(urlre.match('')) assert(urlre.match('http://linuxtoday.com')) assert(re.compile(guessedurlpattern).match('example.biz')) assert(urlre.match('example.biz')) assert(urlre.match('linuxtoday.com')) assert(urlre.match('master.wizard.edu')) assert(urlre.match('blah.bar.info')) assert(urlre.match('goodpr.org')) assert(not urlre.match('blah..org')) def parse_text_urls(s): '''Parse a block of text, splitting it into its url and non-url components.''' rval = [] loc = 0 for match in urlre.finditer(s): if loc < match.start(): rval.append(Chunk(s[loc:match.start()], None)) rval.append(Chunk(None, match.group(1))) loc = match.end() if loc < len(s): rval.append(Chunk(s[loc:], None)) return rval def extract_with_context(lst, pred, before_context, after_context): rval = [] start = 0 length = 0 while start < len(lst): usedfirst = False usedlast = False # Extend to the next match. while start + length < len(lst) and length < before_context + 1 and not pred(lst[start + length]): length += 1 # Slide to the next match. while start + length < len(lst) and not pred(lst[start + length]): start += 1 # If there was no next match, abort here (it's easier # to do this than to try to detect this case later). if start + length == len(lst): break # Now extend repeatedly until we can't find anything. while start + length < len(lst) and pred(lst[start + length]): extendlength = 1 # Read in the 'after' context and see if it holds a URL. while extendlength < after_context + 1 and start + length + extendlength < len(lst) and not pred(lst[start + length + extendlength]): extendlength += 1 length += extendlength if start + length < len(lst) and not pred(lst[start + length]): # Didn't find a matching line, so we either # hit the end or extended to after_context + 1.. # # Now read in possible 'before' context # from the next URL; if we don't find one, # we discard the readahead. extendlength = 1 while extendlength < before_context and start + length + extendlength < len(lst) and not pred(lst[start + length + extendlength]): extendlength += 1 if start + length + extendlength < len(lst) and pred(lst[start + length + extendlength]): length += extendlength if length > 0 and start + length <= len(lst): if start == 0: usedfirst = True if start + length == len(lst): usedlast = True rval.append((lst[start:start + length], usedfirst, usedlast)) start += length length = 0 return rval nlre = re.compile('\r\n|\n|\r') def extracturls(s): '''Given a text message, extract all the URLs found in the message, along with their surrounding context. The output is a list of sequences of Chunk objects, corresponding to the contextual regions extracted from the string.''' rval = [] lines = nlre.split(s) # The number of lines of context above to provide. above_context = 1 # The number of lines of context below to provide. below_context = 1 # Plan here is to first transform lines into the form # [line_fragments] where each fragment is a chunk as # seen by parse_text_urls. Note that this means that # lines with more than one entry or one entry that's # a URL are the only lines containing URLs. linechunks = [parse_text_urls(l) for l in lines] return extract_with_context(linechunks, lambda chunk:len(chunk) > 1 or (len(chunk) == 1 and chunk[0].url <> None), 1, 1) def extracthtmlurls(s): c = HTMLChunker() c.feed(s) c.close() above_context = 1 below_context = 1 def somechunkisurl(chunks): for chunk in chunks: if chunk.url <> None: return True return False return extract_with_context(c.rval, somechunkisurl, 1, 1) urlscan-0.5.6/modules/urlscan/urlchoose.py0000644000000000000000000001417210561406316015562 0ustar # Copyright (C) 2006-2007 Daniel Burrows # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 2 of the # License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, Inc., 59 Temple Place - Suite 330, # Boston, MA 02111-1307, USA. '''An urwid listview-based widget that lets you choose a URL from a list of URLs.''' import urwid import urwid.curses_display import browser def mkbrowseto(url, background): return lambda *args:browser.browseto(url, background) # Based on urwid examples. class URLChooser: def __init__(self, extractedurls, compact_mode = False, background = False): self.compact_mode = compact_mode self.items = [ ] urlidx = 1 first = True firstbutton=0 self.urls = [] for group, usedfirst, usedlast in extractedurls: if first: first = False elif not self.compact_mode: self.items.append(urwid.Divider(div_char = '-', top = 1, bottom = 1)) groupurls = [] markup = [] if self.compact_mode: lasturl = None for chunks in group: for chunk in chunks: if chunk.url and chunk.url != lasturl: groupurls.append(chunk.url) self.urls.append(chunk.url) lasturl = chunk.url else: if not usedfirst: markup.append(('msgtext:ellipses', '...\n')) for chunks in group: i = 0 while i < len(chunks): chunk = chunks[i] i += 1 if chunk.url == None: markup.append(chunk.markup) else: self.urls.append(chunk.url) groupurls.append(chunk.url) # Collect all immediately adjacent # chunks with the same URL. tmpmarkup = [] if chunk.markup: tmpmarkup.append(chunk.markup) while i < len(chunks) and chunks[i].url == chunk.url: if chunks[i].markup: tmpmarkup.append(chunks[i].markup) i += 1 markup += [tmpmarkup or '', ('urlref:number:braces', ' ['), ('urlref:number', `len(self.urls)`), ('urlref:number:braces', ']')] markup += '\n' if not usedlast: markup += [('msgtext:ellipses', '...\n\n')] self.items.append(urwid.Text(markup)) i = len(self.urls) - len(groupurls) for url in groupurls: if firstbutton == 0 and not self.compact_mode: firstbutton = len(self.items) i += 1 markup = [ ('urlref:number:braces', '['), ('urlref:number', `i`), ('urlref:number:braces', ']'), ' ', ('urlref:url', url) ] self.items.append(urwid.Button(markup, mkbrowseto(url, background))) self.listbox = urwid.ListBox(self.items) self.listbox.set_focus(firstbutton) if len(self.urls) == 1: header = 'Found 1 url.' else: header = 'Found %d urls.'%len(self.urls) headerwid = urwid.AttrWrap(urwid.Text(header), 'header') self.top = urwid.Frame(self.listbox, headerwid) def main(self): self.ui = urwid.curses_display.Screen() self.ui.register_palette([ ('header', 'white', 'dark blue', 'standout'), ('msgtext', 'light gray', 'black'), ('msgtext:bullet', 'white', 'black', 'standout'), ('msgtext:bold', 'white', 'black' , 'standout'), ('msgtext:italic', 'dark cyan', 'black', 'standout'), ('msgtext:bolditalic', 'light cyan', 'black', 'standout'), ('anchor', 'yellow', 'black', 'standout'), ('anchor:bold', 'yellow', 'black', 'standout'), ('anchor:italic', 'yellow', 'black', 'standout'), ('anchor:bolditalic', 'yellow', 'black', 'standout'), ('msgtext:ellipses', 'light gray', 'black'), ('urlref:number:braces', 'light gray', 'black'), ('urlref:number', 'yellow', 'black', 'standout'), ('urlref:url', 'white', 'black', 'standout') ]) return self.ui.run_wrapper(self.run) def run(self): size = self.ui.get_cols_rows() try: while True: self.ui.s.erase() self.draw_screen(size) keys = self.ui.get_input() for k in keys: if k == 'window resize': size = self.ui.get_cols_rows() elif k == 'q': return None elif k == 'ctrl l': self.ui.s.clear() else: self.top.keypress(size, k) except KeyboardInterrupt: return None def draw_screen(self, size): canvas = self.top.render(size, focus = True) self.ui.draw_screen(size, canvas) urlscan-0.5.6/modules/urlscan/browser.py0000644000000000000000000000205610561031021015223 0ustar # Copyright (C) 2006-2007 Daniel Burrows # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 2 of the # License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, Inc., 59 Temple Place - Suite 330, # Boston, MA 02111-1307, USA. '''Contains logic to invoke the default system browser.''' import os def browseto(url, background = False): if background: mode = os.P_NOWAIT else: mode = os.P_WAIT os.spawnl(mode, '/usr/bin/sensible-browser', '/usr/bin/sensible-browser', url) urlscan-0.5.6/modules/urlscan/browser.pyc0000644000000000000000000000102310561233126015371 0ustar m 2Ec@sdZdkZedZdS(s4Contains logic to invoke the default system browser.NcCs7|o ti}n ti}ti|dd|dS(Ns/usr/bin/sensible-browser(t backgroundtostP_NOWAITtmodetP_WAITtspawnlturl(RRR((t@/home/daniel/programming/urlscan/head/modules/urlscan/browser.pytbrowsetos  (t__doc__RtFalseR(RR((Rt?s urlscan-0.5.6/debian/0000755000000000000000000000000012162361144011301 5ustar urlscan-0.5.6/debian/compat0000644000000000000000000000000210537106160012476 0ustar 5 urlscan-0.5.6/debian/rules0000755000000000000000000000101612162361144012357 0ustar #!/usr/bin/make -f clean: dh_testdir ./setup.py clean -a dh_clean build: install: dh_testdir dh_testroot dh_clean -k ./setup.py install --no-compile --prefix=$(CURDIR)/debian/urlscan/usr --install-layout=deb binary-indep: install dh_testdir dh_testroot dh_installchangelogs dh_installdocs README dh_installman urlscan.1 dh_fixperms dh_python2 dh_compress dh_installdeb dh_gencontrol dh_md5sums dh_builddeb binary-arch: binary: binary-indep .PHONY: clean binary-indep binary-arch binary install build urlscan-0.5.6/debian/control0000644000000000000000000000141412162361342012704 0ustar Source: urlscan Section: mail Priority: optional Maintainer: Daniel Burrows Build-Depends: debhelper (>= 5.0.37.2) Build-Depends-Indep: python (>= 2.6.6-3~), python-dev (>= 2.6.6-3~) Standards-Version: 3.7.3 Package: urlscan Architecture: all Depends: ${python:Depends}, python-urwid Suggests: mutt, www-browser Description: Extract and browse the URLs contained in an email (urlview replacement) urlscan searches for URLs in email messages, then displays a list of them in the current terminal. It is primarily meant as a replacement for urlview, which it improves upon in the following ways: . * urlscan understands email encodings such as quoted-printable; urlview does not. * urlscan extracts and displays the context surrounding each URL. urlscan-0.5.6/debian/changelog0000644000000000000000000000502112162361144013151 0ustar urlscan (0.5.6-0.2) unstable; urgency=low * Non-maintainer upload. * Convert to dh_python2, thanks to Dan Chen (Closes: #617138). -- Andrea Colangelo Sun, 09 Jun 2013 09:13:53 +0200 urlscan (0.5.6-0.1) unstable; urgency=low * Non-maintainer upload. * Move XS-Python-Version to the source section (Closes: #445424). * Bump required python-central version to 0.6 (due to new modules location). * Duplicated python-central and debhelper entries removed from build dependencies to satisfy Lintian -- Piotr Ożarowski Sun, 16 Mar 2008 00:37:40 +0100 urlscan (0.5.6) unstable; urgency=low * Render HTML entities #8212 and "apos" sensibly. * Recognize braces as valid URL characters (Closes: #407423). * Add an option to allow the browser to be invoked in the background (Closes: #406083). * Guess that FOO.tld is probably a URL (Closes: #408684). -- Daniel Burrows Sun, 4 Feb 2007 08:41:55 -0800 urlscan (0.5.5) unstable; urgency=low * Add support for accepting a single message to scan on the command-line. * Add a --compact option that suppresses URL context. (Closes: #404605) -- Daniel Burrows Thu, 4 Jan 2007 21:29:25 -0800 urlscan (0.5.4) unstable; urgency=low * Don't crash on ordered lists. (Closes: #404137) * Make the URL detection regexp cover more cases. (Closes: #403713) * Spawn the browser in the foreground, needed for text browsers that run in the same terminal (Closes: #403644). * Recognize … as an ellipsis. * Merge immediately adjacent references to the same URL (the HTML parser tends to generate these if contains markup or entities). -- Daniel Burrows Sun, 24 Dec 2006 08:07:13 -0800 urlscan (0.5.3) unstable; urgency=low * Recognize URLs containing internal colons. -- Daniel Burrows Sun, 17 Dec 2006 21:02:20 -0800 urlscan (0.5.2) unstable; urgency=low * Fix dumb typo in copyright notice. (famous last words: "I'm just changing a comment. What could POSSIBLY go wrong with that?) (Closes: #403179) -- Daniel Burrows Sun, 17 Dec 2006 20:55:01 -0800 urlscan (0.5.1) unstable; urgency=low * Add explicit copyright notices. * Suggest mutt and www-browser. -- Daniel Burrows Sun, 10 Dec 2006 17:13:49 -0800 urlscan (0.5) unstable; urgency=low * Initial Upload. -- Daniel Burrows Sun, 10 Dec 2006 14:47:05 -0800 urlscan-0.5.6/debian/copyright0000644000000000000000000000153710537106223013241 0ustar Upstream Author: Daniel Burrows Copyright: This package is free software; you can redistribute it and/or mo dify it under the terms of the GNU General Public License as publishe d by the Free Software Foundation; version 2 dated June, 1991. This package is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public Licen se along with this program; if not, write to the Free Software Foundatio n, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. On Debian systems, the complete text of the GNU General Public License can be found in `/usr/share/common-licenses/GPL'. urlscan-0.5.6/urlscan0000755000000000000000000000613510561031021011446 0ustar #!/usr/bin/python # # A simple urlview replacement that handles things like quoted-printable properly. # aka "urlview minus teh suck" # # Copyright (C) 2006-2007 Daniel Burrows # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 2 of the # License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, Inc., 59 Temple Place - Suite 330, # Boston, MA 02111-1307, USA. import os import sys import re import optparse import email.Parser optparser = optparse.OptionParser(usage='%prog [options] [<] message') optparser.add_option('-c', '--compact', action='store_true', dest="compact", default=False, help='Don\'t display the context of each URL.') optparser.add_option('-b', '--background', action='store_true', dest="background", default=False, help='Run the Web browser as a background process.') options, args = optparser.parse_args() homedir = os.path.dirname(sys.argv[0]) moduledir = os.path.join(homedir, 'modules') if os.path.isdir(moduledir): sys.path = [moduledir] + sys.path from urlscan import urlchoose from urlscan import urlscan # Written as a generator so I can easily choose only # one subpart in the future (e.g., for # multipart/alternative). Actually, I might even add # a browser for the message structure? def msgurls(msg, urlidx = 1): if msg.is_multipart(): for part in msg.get_payload(): for chunk in msgurls(part, urlidx): urlidx += 1 yield chunk elif msg.get_content_type() == 'text/plain': for chunk in urlscan.extracturls(msg.get_payload(decode = True)): urlidx += 1 yield chunk elif msg.get_content_type() == 'text/html': for chunk in urlscan.extracthtmlurls(msg.get_payload(decode = True)): urlidx += 1 yield chunk def main(msg): global options ui = urlchoose.URLChooser(msgurls(msg), compact_mode = options.compact, background = options.background) ui.main() # TODO: accept messages on the command-line? if len(args) > 1: sys.stderr.write('Too many messages.\n') elif len(args) == 1: msg = email.Parser.Parser().parse(file(args[0], 'r')) main(msg) else: msg = email.Parser.Parser().parse(sys.stdin) if not os.isatty(0): fd = os.open('/dev/tty', os.O_RDONLY) if fd < 0: sys.stderr.write('Unable to open an input tty.\n') sys.exit(-1) else: os.dup2(fd, 0) os.close(fd) main(msg)