./knews-1.0b.1/ 40755 1244 1244 0 6642134706 11531 5ustar kallekalle./knews-1.0b.1/COPYING100644 1244 1244 43076 6455455533 12721 0ustar kallekalle GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave, Cambridge, MA 02139, 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 Library 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 Appendix: 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) 19yy 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., 675 Mass Ave, Cambridge, MA 02139, 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) 19yy 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 Library General Public License instead of this License. ./knews-1.0b.1/INSTALL100644 1244 1244 1417 6455455533 12670 0ustar kallekalleTo compile do the following: 1. Read and edit knews.tmpl to make sure your compiler is OK. 2. Read and edit configure.h. This is for configuration of defaults, plus some other stuff. 3. If you skipped 1 or 2, go back to 1. 4. Execute the following command in the top directory: xmkmf make Makefiles make clean make all 5. If all goes well, this will leave an executable named knews in src/. 6. Read and edit the app-def file src/Knews.ad. 7. If you are root, you can install knews by executing 'make install' and 'make install.man'. This will also install the app-def file src/Knews.ad. 8. If you are not root you will have to install src/knews by hand. You will also have to install the app-def file src/Knews.ad. See that file for hints. ./knews-1.0b.1/Imakefile100644 1244 1244 1117 6455455533 13445 0ustar kallekalle#include "knews.tmpl" #include "configure.h" #undef DOMAIN_NAME #undef MAIL_COMMAND #undef EDIT_COMMAND #define IHaveSubdirs #if HAVE_POSIX_REGEXPS SUBDIRS = Widgets src #else SUBDIRS = Widgets regexp src #endif #undef PassCDebugFlasg #define PassCDebugFlags MakeSubdirs($(SUBDIRS)) DependSubdirs($(SUBDIRS)) squeaky: rm -f Widgets/Makefile Widgets/Makefile.bak rm -f regexp/Makefile regexp/Makefile.bak rm -f src/Makefile src/Makefile.bak rm -f util/knewsd/Makefile util/knewsd/Makefile.bak rm -f util/tcp_relay/Makefile util/tcp_relay/Makefile.bak rm -f Makefile Makefile.bak ./knews-1.0b.1/configure.h100644 1244 1244 11215 6642131446 13776 0ustar kallekalle/*********************************************** * MANDATORY CONFIGURATION * ***********************************************/ /* This is the command used to send mail. This should be a non-interactive * command that reads an rfc822 format message from standard input, adds * the necessary headers (Date, Message-ID, etc...) and dispatches it * according to the Cc: and To: headers. * * You'll probably want to leave this as sendmail, but: * * MAKE SURE THE PATH IS RIGHT. * * If you don't have sendmail, use /bin/false :-). */ /* #define MAIL_COMMAND "/usr/sbin/sendmail -t" */ /* #define MAIL_COMMAND "/usr/lib/sendmail -t" */ /* The following macros control the generation of email addresses. * (In particular, the part that goes after the @.) If DOMAIN_FILE * is defined, the first line of that file is taken as the domain * name. Otherwise, if DOMAIN_NAME is defined, it is taken as the * domin name. * * At no point will a domain name not containing a '.' be accepted. * * (Note: Filenames not beginning with / are relative to $HOME.) */ /* #define DOMAIN_FILE "/your/file/here" */ /* #define DOMAIN_NAME "your domain here" */ /*********************************************** * MISC CONFIGURATION * ***********************************************/ /* This is the deafult editor. This can be overridden with X resources, * so you need not change this here. * * %s is the name of the file to edit, and * %i is the line where the cursor will initially be positioned. * * Both %s and %i are optional, although the editor will not be much use * if you don't specify %s... */ #define DEFAULT_EDIT_COMMAND "xterm -e vi +%i %s" /* The default nntp server. Basically: don't set this, use $NNTPSERVER * or the resource Knews.nntpServer instead. */ /* #define DEFAULT_NNTPSERVER "your.server" */ /* If both the resource Knews.nntpServer and $NNTPSERVER are * unset, this will be used for nntp server. No need to set it. */ /* #define DEFAULT_DEFAULT_NNTPSERVER "your.server" */ /* The path of the standard shell, no need to set this unless you * have no /bin/sh. Don't put a bogus shell here. */ /* #define BIN_SH "/bin/sh" */ /* * Set these to 1 if you have libjpeg, libpng and/or libcompface * installed on your system. Knews will then be able to display * inline images and/or X-Faces. * * If you set either of these you will probably need to set up * library search paths in knews.tmpl for libjpeg, libpng and * libcompface. */ #define HAVE_JPEG 0 #define HAVE_PNG 0 #define HAVE_COMPFACE 0 /*************************************** * HACKS * ***************************************/ /* * Below are a few quick hacks for weird systems. No need to bother * with this unless you get errors during compilation. */ /* If you don't have POSIX.2 regexps, set the following to 0 to use * Henry Spencer's package provided in the directory regexp/. * * To see if your system has this, check for a global include file * that defines the type regex_t and the functions * regcomp() and regexec(). But that's no guarantee: To the best * of my knowledge, the Solaris 2.4 regex implementation is bogus, * so you will need this anyway. * * Note that the GNU regex package provides sufficient POSIX support * plus a few extensions, so you can get that and link against it * if you prefer. */ #define HAVE_POSIX_REGEXPS 1 /* If your C library doesn't have memmove (e.g. SunOS 4.1.x), set this to 0. */ #define HAVE_MEMMOVE 1 /* If you don't have an up-to-date Xmu library (i.e. R5 or later), * set this to 0. This library is needed for editres support (editres * is a nice application that allows you to set resources interactively). * This may be needed on some HP/UX X11R4/5 hybrids. */ #define HAVE_XMU 1 /* If your system has the Xpm library, set this to 1. The Xpm library * is needed for knews' string-to-xpm converter, which make things like * this possible: * * Knews*backgroundPixmap: ~/dir/texture.xpm */ #define HAVE_XPM 0 /* If your system doesn't have the POSIX sigaction() function, defining * this to 0 will make knews use the old, unreliable signal() funtion * instead. You'll need this if you get errors while compiling src/child.c. */ #define HAVE_SIGACTION 1 /* If your system doesn't have the (completely non-standard) socketpair() * function, you may set this to 0. This will make knews incapable of * talking to 'fake' servers, but that's no great loss. You'll need this * if you get errors while compiling src/server.c. */ #define HAVE_SOCKETPAIR 1 ./knews-1.0b.1/README100644 1244 1244 205 6455455534 12472 0ustar kallekalleFor more information, see the knews home page http://www.matematik.su.se/~kjj/knews.html. Karl-Johan Johnsson, kjj@matematik.su.se. ./knews-1.0b.1/Changes100644 1244 1244 34667 6642132340 13151 0ustar kallekalleContent-Type: text/plain; charset=iso-8859-1 Changed with 1.0b.1: + XFace support. + Misc. bugfixes based on patches from Matthias Scheler, Christian Bauernfeind and Greg Ubben. Changed with 1.0b.0: + GPL. + Changed the kill-{append|prepend} actions. + New action procedures: kill-{append|prepend}-global(). + New action procedure: tree-layout(). + Changed handling of domain name slightly. + Misc. bugfixes. Changed with 0.9.8: + Inline images: support for jpeg, gif and png. Though the dithering performed for grayscale images and gifs is very simpleminded. This has only been tested on 8 bit displays... + Action procedure forward-by-mail() plus option not to use MIME when forwarding. + Configurable forward menu + action proc forward-by-mail(). + Resource Knews.quoteEmpty that says whether to quote-prefix empty lines. + Cleaned up the menu hierarchy widgets. + Added per-group '[posted and mailed]' warning. + The article text widget handles inline images. Also made it horizontally scrollable, with line wrapping optional. The scrollbar 'textscrbar' was renamed to 'textvbar'. + Some resources to control the scrolling style of the lists. + 'Print' on the misc menu... + Removed 'hotish'. Hot now only affects unread articles. + Hack for iconification of second window: Knews.iconHack. + Support for AUTHINFO SIMPLE (but not for second connection). + New resources confirmCatchup and confirmQuitGroup. Changed with 0.9.7: + Completely new post popup with attachments and user-defined actions for e.g. spell-checking. + MIME decoding of quoted article, when charsets match. + A supersede option on the post-menu. + Decoding of rfc1522 in the thread list and article tree too. + Regexp matching for the find group popup. + New option quoteQuoteString. + Optimization of killing. + Handle needsterminal and copiousoutput. + Horizontal scrolling of lists. + Configurable list width: groupNameColumns and subjectColumns. + followupHeaders: similar to extraHeaders, but expands the same %'s as attribution. E.g. 'X-Comment-To: %r'. + The toggles on the save and search popups are app-def settable. + Proper handling of permissions for .newsrc etc. + Authentication for the second connection. + New action procedures untag-{subject,thread}. + confirmQuit now means verify for disconnect too; also ask for verification if there are outstanding posts. + Improved extraction of initials in attribution. + New algorithm for determining domain name. + Show how to set the initial state of the toggles on the save and search popup in the app-def file + made the Tab key work in the search popup. + Fixed checking for new groups to use the atime of the config file, plus a hack for bogus filesystems (e.g. AFS). + New resource Knews.autoSubscribe. + Slight improvement of the saveThreadInfo stuff. + Changed double-clicking in thread list to work for read threads. + New resource Knews.generatePath. + Preserve case when generating references. Changed with 0.9.6: + Substantially improved handling of the charset parameter for the content type text/*, with the ability to specify different fonts for different charsets. This will handle any 8 bit charset. + Reworked the article text widget to make it able to display wide-character fonts. Could easily be made to display inline images too. + The article tree now has a 'vertical' layout by popular demand. Made it the default too :-) + Decoding of 16 bit encoded charsets (highly experimental), support for: utf-7, hz-gb-2312, ksc-5601, big5 ... + Also a defaultCharset config option. + Rfc1522 decoding of headers. + Message/partial assembly. + Support for Content-Transfer-Encoding: [x-]uue[ncode[d]]. + More robust base64 decoding. + Added some warnings so the user is notified of garbage mime articles (there are lots of them...) + Mailcap file support, removed old viewer resources. needsterminal, copiousoutput and test will be ignored... + Reworked the 'thread ahead' to save thread data to temp files instead of keeping it in memory. Also allow these files to survive between sessions. + Added article prefetch cache and 'trailing' cache. + Optional caching of group descriptions in a file between sessions. + Simple 'xpm-file to pixmap' converter, for background textures using the backgroundPixmap resource. + New kill action 'hotish', like hot but faster in one case. Further optimizations in the future. + An option to have case-sensitive regexps. + Less restrictive locking of the interface: you may scroll articles, edit the kill file etc. while knews is busy. + Message-id lookup of articles. + Got rid of the Xmu library, except for editres. + %a for article number in file names. + Various %'s in attribution for date, time etc based on code from Mikhail Teterin. + The default uuDir should be News, not ~/News. + Removed AUTHINFO GENERIC. + The searching may now be restricted to threads, or tagged articles, and a 'stop' function for search (as opposed to 'abort'). + Proper handling of SIGHUP while updating newsrc and kill files. + Support for different visuals and depths(?) + private colormap. Changed with 0.9.5: + All remotely system dependent code has been stashed away in src/sysdeps.c, everything else should be (almost) POSIX.1 + regex. + New config option tryListActive, to make knews try the "LIST ACTIVE wildmat" nntp extension when readActiveFile is false. True by default. + Backup newsrc file with link(2), instead of the old, ugly way. + DEFAULT_DEFAULT_NNTPSERVER by request of Piete Brooks. + SIGBUS and a number of errno's not defined #if _POSIX_SOURCE, reported by Paul Tomblin and Mark Hannon. + XtEnumerateDatabase doesn't exist in X11R4, reported by Chris Siebenmann. + The stderr file descriptor was left blocking when forking. + Extra & in src/resource.c, leading to memory corruption. Reported and fixed by Ron Olsen. + Patch to src/thread.c from Dick Streefland: fall back to slow threading if XOVER doesn't give all articles, plus one or two bugs. + Some newsreaders backup .newsrc by link(2), which confused knews. Found by Piete Brooks. + The default quoteRegexp was wrong, reported by Henrik Delin. Changed with 0.9.4: + *** INCOMPATIBILITY WARNING *** The per server and per group X resources are gone, instead there is a new scheme with a configuration file, which should be more flexible and user friendly. Knews will warn on startup if it finds some of the old resource settings. + Sorting of threads. + Fixed clicking on URLs. Second mouse button in text widget. + A do-the-right-thing() action procedure. + Relization of fake-articles. (They're still fake, though.) + Fast way to add stuff to the kill file: the append-kill() and prepend-kill() action procedures, by default bound to the keys Ctrl-[kK] for thread/subthread kills and [Shift] F1...F4 for red, green, blue and yellow [thread] subthread hot entries. + Spiffed up the 3D effect for the widgets on monochrome displays. The old interface can be had by setting '*useLineShadows: True'. + Fixed race conditions in the scroll bar and the article tree scrolling: the tree scrolling should be smoother now. + Also a more serious race condition in the 'pane resizing'. + Made the subject reflect the followup status of the 'next' article, and removed the fixedAuthor resource. + Kill entries in the kill file now do not affect 'hot' articles. Note that the entries are executed in sequence, so you can put 'hard' kills at the beginning and 'soft' kills at the end. + Made 'Previous' mark the current article unread. + list-up() and list-down() now accepts relative values. + Stripped down knewsd to its bare bones, and made it able to accept posts by piping them to e.g. "inews -h". + Optional confirmation for quit: the Knews.confirmQuit resource. + New misc: mark-read-untagged and mark-read-cold. + Added a hot-counter to the status-line. + An "ask how many" feature. + New command line option +/-keep. + Made knews fetch the Xref header on the fly while reading an article if the Xref header was is not in the overview files. + Moved the layouts out from the app-def file. + Fixed a bug that made the editor "hang" sometimes. + Fixed a bug that made knews hang at 96% of threading occasionally, with assistance by Joao Cardoso. + Suggestions and code from Matthias Schuetze: icon, %r in attribution for real name, distributions, %i in quoteString for initials. + Improvement of 'pre-post' message, including listing of the groups the article is being posted to, based on code by Jörg Wunsch. + Fixed posting to handle lines with leading dots properly. + Don't close stdout by request of Mark Crimmins. Changes with 0.9.3: + XPAT searching. + Thread ahead: advance threading of groups in the background and an action procedure schedule-thread-ahead(). The default translation for this is the third mouse button in the group list. + New resources 'keepThreadInfo' and 'threadEmAll'. + Only check subscribed groups when readActiveFile is False. + A 'Find group' popup in the 'subscribed groups' and 'all groups` mode with tab completion, and an action procedure: popup-find-group(). + New option on the misc menu: 'mark read to current' and corresponding action procedure mark-read-to-current(). + Changed the forward option a bit. + Added a history mechanism and replaced the 'next breadth' button with a 'previous' button. + New resource 'mailName' for people with non-standard addresses. + Made the author name in the thread list change with the 'next' article, plus a resource 'fixedAuthor' if you dislike this. + Changed the 'All threads' button to a toggle. + The default fonts have been changed to a more sane size. + A 'uuProgram' resource to allow the user to use his/her own uudecoder. + Improved the algorithm for finding uuencoded parts a bit. + An extensible but obscure mechanism for using an arbitrary program as an nntp server via fork+exec. This can be used to read the spool dir and even mail folders. + A couple of bugs pointed out by Andrew R Tefft, Scott Stevens and Robert Campbell. Changes with 0.9.2: + The main window is now supposed to size itself according to the fonts. Remove your geometry settings, and use the preferredLines and preferredColumns resources in the appdef file instead. + New resource Knews.separateWindows to make knews use a separate top level window for the article text window. + Rudimentary internal MIME viewer: 'Save to file or pipe to shell'. The only MIME types now shown raw are text/* and message/* (not rfc822). + Asynchronous reporting of the exit status of MIME viewers. + Pipe now means save to temporary file + fork & exec with asynchronous reporting of exit status. + Improved reconnecting to server, with reissue of nntp command. + A couple of bugs pointed out by Andrew R. Tefft. + Installed Xlib error handler and X I/O error handler, so that X protocol errors are not fatal, and KillClients are handled gracefully. + Fixed a bug that let the user pull hot pixmaps out from under knews. Reported by Nathan J. Mehl. + New action procedures pipe(), save(), tag-thread() and tag-subject(), catchup(), subscribe(), unsubscribe(), change-size(). + Subjects with tagged articles are marked with an asterisk in the list. + Save/pipe understands ~ for $HOME and %n/%N/%g/%G for capitalized and/or slashed newsgroup name. Directories that don't exist will be created. + Forward option on the post menu. + Support for raw IP address in $NNTPSERVER. + Removed 4 line limit on sigs. + File completion for the FileSelWidget. + Optionally show number of lines of articles in thread tree. + It's now possible to subscribe/unsubscribe inside a group and subscribe/catchup in the 'subscribed groups' list. + The default editCommand now is xterm -e vi +%i %s + No limit on line length in newsrc files. + Optionally don't read the active file when connecting based on some diffs from Jan Andersson. + Option to write all groups knews knows about to the newsrc file. + Allow the user to specify a postingAgent used to post articles. You could set this to 'inews -h', if you want. Changes with 0.9.1: + Will now get HEAD from the server if it doesn't support XOVER. This is very slow. + Next depth/breadth now takes you out of the group if there are no more unread articles. + The gecos field of the passwd file is chopped of at the first ','. + Internal support for MIME content types text/plain, message/rfc822, multipart/mixed and multipart/digest. + Ability to specify external viewers for any type/subtypes, with extra resource subhierarchies for text/* and message/external-body. + 'Full header' now means turn off all MIME stuff. + Support for quoted-printable and base64 encodings. + Regexp searching of article text/body/subject/from and in the group list. + Lots of action procedures, and a few default keyboard translations. + Drag-and-drop in the group list and the kill list to reorder the .newsrc file and the kill file. + AUTHINFO GENERIC. I haven't been able to test this. + Lots of internal clean-up, including the save/pipe function, forking of editors. Will use poll(2) instead of select(2) is SYSV is defined. + Removed 'reopen'. + Cleaned up and fixed a bug in the Layout Widget, thanks to leo@marco.de. + Some provisions to be able to compile with R4. I'm not sure if it works. + When posting a new article, %i means start at the subject line. + Fixed a bug in save subject reported by Rainer Krienke. ./knews-1.0b.1/knews.tmpl100644 1244 1244 3457 6642134657 13671 0ustar kallekalle/* * This file is included into all Imakefiles. If your default compiler * is not ISO/ANSI compliant, you have to fix it here. Below are a few * example settings for systems I have access to. * * NOTE: The compilers on most platforms are not conformant by default. * Linux is a notable exception. * * Also note that if you change anything here you should start the build * process over from scratch with * * xmkmf ; make Makefiles ; make clean ; make all * * to make sure the changes take effect. */ /* Below are some compiler settings for different systems. * * CC is the name of the compiler. * CCOPTIONS are the flags needed to make it compile C (!). * CDEBUGFLAGS optimization/debug flags. */ /* gcc on decent systems */ #if 0 CC = gcc CCOPTIONS = -ansi -pedantic -Wall CDEBUGFLAGS = -O2 #endif /* gcc on SunOS 4.1.x (and maybe other non-decent systems) */ #if 0 CC = gcc CCOPTIONS = -ansi -pedantic -Wall -D__USE_FIXED_PROTOTYPES__ CDEBUGFLAGS = -O2 #endif /* Sun's acc compiler */ #if 0 CC = acc CCOPTIONS = -Xc CDEBUGFLAGS = -O2 #endif /* Dec's cc complier */ #if 0 CC = cc CCOPTIONS = -std1 CDEBUGFLAGS = -O2 #endif /* I think some SysV-ish systems want this */ #if 0 CC = cc CCOPTIONS = -Aa CDEBUGFLAGS = -O #endif /* Debugflags... */ #if 0 CDEBUGFLAGS = -O -g #endif /* * This is where you specify libraries and such for inline images. * These won't actually be used unless HAVE_JPEG, HAVE_PNG and/or * HAVE_COMPFACE are defined to 1 in configure.h. * * Note that libz may be libgz on some systems, so -lz may need to * be changed to -lgz. */ JPEG_LIB = -ljpeg PNG_LIB = -lpng -lz -lm COMPFACE_LIB = -lcompface /* * If you need to add include paths for e.g. libjpeg, libpng or libzlib * include files, do it here. */ KNEWS_INCLUDES = ./knews-1.0b.1/Widgets/ 40755 1244 1244 0 6633453670 13143 5ustar kallekalle./knews-1.0b.1/Widgets/ArtText.c100644 1244 1244 147231 6567776332 15060 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include #include #include #include #include "Util.h" #include "Compat.h" #include "ArtTextP.h" #define MAX_Y(w) ((w)->arttext.table[(w)->arttext.lines].y) static XtResource resources[] = { {XtNshadowWidth, XtCShadowWidth, XtRDimension, sizeof(Dimension), XtOffsetOf(ArtTextRec, shadow.shadow_width), XtRImmediate, (XtPointer)1}, #define offset(field) XtOffsetOf(ArtTextRec, arttext.field) {XtNfont, XtCFont, XtRFontStruct, sizeof(XFontStruct*), offset(font), XtRString, XtDefaultFont}, {XtNhighlightColor, XtCForeground, XtRPixel, sizeof(Pixel), offset(highlight_pixel), XtRString, XtDefaultForeground}, {XtNurlCallback, XtCCallback, XtRCallback, sizeof(XtCallbackList), offset(url_callback), XtRImmediate, (XtPointer)NULL}, {XtNmargin, XtCMargin, XtRDimension, sizeof(Dimension), offset(margin), XtRImmediate, (XtPointer)8}, {XtNimageMargin, XtCMargin, XtRDimension, sizeof(Dimension), offset(image_margin), XtRImmediate, (XtPointer)16}, {XtNseparatorMargin, XtCMargin, XtRDimension, sizeof(Dimension), offset(separator_margin), XtRImmediate, (XtPointer)12}, {XtNpreferredLines, XtCPreferredLines, XtRDimension, sizeof(Dimension), offset(preferred_lines), XtRImmediate, (XtPointer)32}, {XtNpreferredColumns, XtCPreferredColumns, XtRDimension, sizeof(Dimension), offset(preferred_columns), XtRImmediate, (XtPointer)80}, {XtNwrapLines, XtCWrapLines, XtRBoolean, sizeof(Boolean), offset(wrap_lines), XtRImmediate, (XtPointer)True}, #undef offset }; static void Initialize(Widget, Widget, ArgList, Cardinal*); static void Destroy(Widget); static void Realize(Widget, XtValueMask*, XSetWindowAttributes*); static void Resize(Widget); static void Redisplay(Widget, XEvent*, Region); static Boolean SetValues(Widget, Widget, Widget, ArgList, Cardinal*); static void SetVPos(ScrollableWidget, long); static XtGeometryResult QueryGeometry(Widget, XtWidgetGeometry*, XtWidgetGeometry*); static void select_start(Widget, XEvent*, String*, Cardinal*); static void select_extend(Widget, XEvent*, String*, Cardinal*); static void select_extend_start(Widget, XEvent*, String*, Cardinal*); static void select_end(Widget, XEvent*, String*, Cardinal*); static void click(Widget, XEvent*, String*, Cardinal*); static void call_url(Widget, XEvent*, String*, Cardinal*); static XtActionsRec actions[] = { {"select-start", select_start}, {"select-extend", select_extend}, {"select-extend-start", select_extend_start}, {"select-end", select_end}, {"click", click}, {"call-url", call_url}, }; static char translations[] = ": select-start() click() \n" ": select-extend() \n" ": select-end(PRIMARY) \n" ": call-url() click() \n" ": select-extend-start() click() \n" ": select-extend() \n" ": select-end(PRIMARY) \n"; ArtTextClassRec artTextClassRec = { { /* core fields */ (WidgetClass) &scrollableClassRec, /* superclass */ "ArtText", /* class_name */ sizeof(ArtTextRec), /* widget_size */ NULL, /* class_initialize */ NULL, /* class_part_initialize */ FALSE, /* class_inited */ Initialize, /* initialize */ NULL, /* initialize_hook */ Realize, /* realize */ actions, /* actions */ XtNumber(actions), /* num_actions */ resources, /* resources */ XtNumber(resources), /* num_resources */ NULLQUARK, /* xrm_class */ TRUE, /* compress_motion */ #if (XtSpecificationRelease < 4) TRUE, /* compress_exposure */ #else XtExposeCompressMaximal | XtExposeGraphicsExposeMerged, /* compress_exposure */ #endif TRUE, /* compress_enterleave */ FALSE, /* visible_interest */ Destroy, /* destroy */ Resize, /* resize */ Redisplay, /* expose */ SetValues, /* set_values */ NULL, /* set_values_hook */ XtInheritSetValuesAlmost, /* set_values_almost */ NULL, /* get_values_hook */ NULL, /* accept_focus */ XtVersion, /* version */ NULL, /* callback_private */ translations, /* tm_table */ QueryGeometry, /* query_geometry */ XtInheritDisplayAccelerator, /* display_accelerator */ NULL /* extension */ }, { /* shadow fields */ XtInheritPixelOffset, /* pixel_offset */ False, /* use_arm_for_background */ XtInheritAllocShadowColors, /* alloc_shadow_colors */ XtInheritAllocShadowPixmaps, /* alloc_shadow_pixmaps */ XtInheritAllocArmColor, /* alloc_arm_color */ XtInheritAllocArmPixmap, /* alloc_arm_pixmap */ XtInheritAllocGCs, /* alloc_gcs */ NULL, /* extension */ }, { /* scrollable fields */ XtInheritScrollableSetPos, /* set_hpos */ SetVPos, /* set_vpos */ NULL, /* suspend_hook */ NULL, /* extension */ }, { /* arttext fields */ 0 /* extension */ } }; WidgetClass artTextWidgetClass = (WidgetClass)&artTextClassRec; /*************************************************************************/ static Dimension preferred_width(ArtTextWidget w) { return w->arttext.max_width > 0 ? w->arttext.max_width : (w->arttext.font->max_bounds.width * w->arttext.preferred_columns + 2 * w->arttext.margin); } static Dimension preferred_height(ArtTextWidget w) { return w->arttext.preferred_lines * (w->arttext.font->ascent + w->arttext.font->descent); } static long coord_to_line(ArtTextWidget w, long y) { long n = w->arttext.first; if (n >= w->arttext.lines) n = w->arttext.lines - 1; if (n < 0) n = 0; while (w->arttext.table[n].y <= y) if (++n > w->arttext.lines) return w->arttext.lines; if (n > 0) do { n--; } while (n > 0 && w->arttext.table[n].y > y); return n; } /*************************************************************************/ static int draw_item(ArtTextWidget w, long line) { TSTable *table = w->arttext.table + line; TSNode *node = table->node; Display *disp = XtDisplay(w); Window win = XtWindow(w); GC gc = w->arttext.gc; XFontStruct *font; char *str; XChar2b *wstr; long len; int x, y = table->y - w->scrollable.pos_y; int width; #define SET_GC_FONT \ if (font->fid != w->arttext.gc_fid) { \ XSetFont(disp, gc, font->fid); \ w->arttext.gc_fid = font->fid; \ } #define SET_GC_FG(pixel) \ if (pixel != w->arttext.gc_fg) { \ XSetForeground(disp, gc, pixel); \ w->arttext.gc_fg = pixel; \ } switch (node->gen.type) { case LineTypeString: if (y >= (int)w->core.height) return False; font = node->str.font; x = w->arttext.margin; y += font->ascent; SET_GC_FONT; str = node->str.str + table->start; len = table->len; if (!w->arttext.sel_ok || line < w->arttext.sel_start_line || line > w->arttext.sel_stop_line) { SET_GC_FG(node->str.pixel); XDrawString(disp, win, gc, x, y, str, len); } else if (line > w->arttext.sel_start_line && line < w->arttext.sel_stop_line) { SET_GC_FG(w->core.background_pixel); XDrawImageString(disp, win, gc, x, y, str, len); x += XTextWidth(font, str, len); if (x < (int)w->core.width) { SET_GC_FG(w->arttext.highlight_pixel); XFillRectangle(disp, win, gc, x, y - font->ascent, w->core.width - x, font->ascent + font->descent); } } else { long tmp = 0; if (line == w->arttext.sel_start_line && w->arttext.sel_start_offset > 0) { tmp = w->arttext.sel_start_offset; if (tmp > len) tmp = len; SET_GC_FG(node->str.pixel); XDrawString(disp, win, gc, x, y, str, tmp); x += XTextWidth(font, str, tmp); str += tmp; len -= tmp; } if (line != w->arttext.sel_stop_line) { SET_GC_FG(w->core.background_pixel); XDrawImageString(disp, win, gc, x, y, str, len); x += XTextWidth(font, str, len); if (x < (int)w->core.width) { SET_GC_FG(w->arttext.highlight_pixel); XFillRectangle(disp, win, gc, x, y - font->ascent, w->core.width - x, font->ascent + font->descent); } } else { long tmp1; tmp = w->arttext.sel_stop_offset - tmp + 1; tmp1 = tmp < len ? tmp : len; if (tmp1 > 0) { SET_GC_FG(w->core.background_pixel); XDrawImageString(disp, win, gc, x, y, str, tmp1); x += XTextWidth(font, str, tmp1); } str += tmp; len -= tmp; if (len >= 0) { if (len > 0) { SET_GC_FG(node->str.pixel); XDrawString(disp, win, gc, x, y, str, len); } } else if (x < (int)w->core.width) { SET_GC_FG(w->arttext.highlight_pixel); XFillRectangle(disp, win, gc, x, y - font->ascent, w->core.width - x, font->ascent + font->descent); } } } break; case LineTypeWString: if (y >= (int)w->core.height) return False; font = node->wstr.font; x = w->arttext.margin; y += font->ascent; SET_GC_FONT; wstr = node->wstr.str + table->start; len = table->len; if (!w->arttext.sel_ok || line < w->arttext.sel_start_line || line > w->arttext.sel_stop_line) { SET_GC_FG(node->wstr.pixel); XDrawString16(disp, win, gc, x, y, wstr, len); } else if (line > w->arttext.sel_start_line && line < w->arttext.sel_stop_line) { SET_GC_FG(w->core.background_pixel); XDrawImageString16(disp, win, gc, x, y, wstr, len); x += XTextWidth16(font, wstr, len); if (x < (int)w->core.width) { SET_GC_FG(w->arttext.highlight_pixel); XFillRectangle(disp, win, gc, x, y - font->ascent, w->core.width - x, font->ascent + font->descent); } } else { long tmp = 0; if (line == w->arttext.sel_start_line && w->arttext.sel_start_offset > 0) { tmp = w->arttext.sel_start_offset; if (tmp > len) tmp = len; SET_GC_FG(node->wstr.pixel); XDrawString16(disp, win, gc, x, y, wstr, tmp); x += XTextWidth16(font, wstr, tmp); wstr += tmp; len -= tmp; } if (line != w->arttext.sel_stop_line) { SET_GC_FG(w->core.background_pixel); XDrawImageString16(disp, win, gc, x, y, wstr, len); x += XTextWidth16(font, wstr, len); if (x < (int)w->core.width) { SET_GC_FG(w->arttext.highlight_pixel); XFillRectangle(disp, win, gc, x, y - font->ascent, w->core.width - x, font->ascent + font->descent); } } else { long tmp1; tmp = w->arttext.sel_stop_offset - tmp + 1; tmp1 = tmp < len ? tmp : len; if (tmp1 > 0) { SET_GC_FG(w->core.background_pixel); XDrawImageString16(disp, win, gc, x, y, wstr, tmp1); x += XTextWidth16(font, wstr, tmp1); } wstr += tmp; len -= tmp; if (len >= 0) { if (len > 0) { SET_GC_FG(node->wstr.pixel); XDrawString16(disp, win, gc, x, y, wstr, len); } } else if (x < (int)w->core.width) { SET_GC_FG(w->arttext.highlight_pixel); XFillRectangle(disp, win, gc, x, y - font->ascent, w->core.width - x, font->ascent + font->descent); } } } break; case LineTypeSeparator: if (y >= (int)w->core.height) return False; x = w->arttext.separator_margin; width = w->core.width - 2 * x; if (width > 0) ShadowDrawShadows((ShadowWidget)w, x, y + node->sep.margin, width, node->sep.height, True); break; case LineTypeClickable: if (y >= (int)w->core.height) return False; font = node->cli.font; x = w->arttext.margin; y += font->ascent; SET_GC_FONT; SET_GC_FG(node->cli.pixel); len = strlen(node->cli.str); width = XTextWidth(font, node->cli.str, len); XDrawString(disp, win, gc, x, y, node->cli.str, len); XDrawLine(disp, win, gc, x, y + 1, width, y + 1); break; case LineTypeImage: if (y >= (int)w->core.height) return False; XCopyArea(disp, node->img.pixmap, win, gc, 0, 0, node->img.width, node->img.height, w->arttext.image_margin, y); break; } return True; } #undef SET_GC_FONT #undef SET_GC_BG #undef SET_GC_FG static void draw_items(ArtTextWidget w, long first, long last) { if (first < w->arttext.first) first = w->arttext.first; if (last >= w->arttext.lines) last = w->arttext.lines - 1; while (first <= last) if (!draw_item(w, first++)) break; } static void clear_items(ArtTextWidget w, long first, long last) { int y, height; if (first >= w->arttext.lines) first = w->arttext.lines - 1; if (first < 0) first = 0; if (last >= w->arttext.lines) last = w->arttext.lines - 1; if (last < 0) last = 0; y = w->arttext.table[first].y - w->scrollable.pos_y; height = w->arttext.table[last + 1].y - w->scrollable.pos_y - y; XClearArea(XtDisplay(w), XtWindow(w), 0, y, 0, height, False); } static void draw_pixels(ArtTextWidget w, int y, int height, Region region) { Display *disp = XtDisplay((Widget)w); if (region) XSetRegion(disp, w->arttext.gc, region); else { XRectangle rect; rect.x = 0; rect.y = y; rect.width = w->core.width; rect.height = height; XSetClipRectangles(disp, w->arttext.gc, 0, 0, &rect, 1, YXBanded); } draw_items(w, coord_to_line(w, y + w->scrollable.pos_y), coord_to_line(w, y + height + w->scrollable.pos_y)); XSetClipMask(disp, w->arttext.gc, None); } /*************************************************************************/ static char *tab_strdup(const char *str, long len, long *new_len) { char *res; if (!strchr(str, '\t')) { *new_len = len; res = XtMalloc(len + 1); memcpy(res, str, len); res[len] = '\0'; } else { /* this won't happen very often */ int i, j, n_alloced; n_alloced = len + 8; res = XtMalloc(n_alloced); for (i = 0, j = 0 ; i < len ; i++) { if (j + 10 > n_alloced) { n_alloced += 16; res = XtRealloc(res, n_alloced); } if (str[i] != '\t') res[j++] = str[i]; else { int m = 7 - (j % 8); while (m-- >= 0) res[j++] = ' '; } } res[j] = '\0'; *new_len = j; } return res; } static char *tab_strdupcat(char *str, long len, const char *cat, long *new_len) { long tmp = strlen(cat); str = XtRealloc(str, len + tmp + 1); memcpy(str + len, cat, tmp); str[len + tmp] = '\0'; *new_len = len + tmp; return str; } static void alloc_text_table(ArtTextWidget w, long n) { if (n > w->arttext.n_alloc - 2) { long i = w->arttext.n_alloc; w->arttext.n_alloc = 2 * (i + 1); w->arttext.table = (TSTable *)XtRealloc((char *)w->arttext.table, w->arttext.n_alloc * sizeof w->arttext.table[0]); while (i < w->arttext.n_alloc) { w->arttext.table[i].y = 0; w->arttext.table[i].node = NULL; w->arttext.table[i].start = 0; w->arttext.table[i].len = 0; i++; } } } static void call_clickable(ArtTextWidget w, TSNode *node, int click) { CallbackData *data; switch (node->gen.type) { case LineTypeClickable: data = node->cli.data; break; case LineTypeImage: data = node->img.data; break; default: return; } if (!data || !data->callback) return; data->callback((Widget)w, data->client_data, (XtPointer)&click); } static void free_text_data(ArtTextWidget w, long n) { TSNode *stream = w->arttext.stream; w->arttext.table = (TSTable *)XtRealloc((char *)w->arttext.table, n * sizeof w->arttext.table[0]); w->arttext.n_alloc = n; while (n-- > 0) { w->arttext.table[n].y = 0; w->arttext.table[n].node = NULL; w->arttext.table[n].start = 0; w->arttext.table[n].len = 0; } w->arttext.first = 0; w->scrollable.pos_y = 0; w->scrollable.height = 0; w->arttext.lines = 0; w->arttext.stream = NULL; w->arttext.last = NULL; while (stream) { TSNode *next = stream->gen.next; switch (stream->gen.type) { case LineTypeString: XtFree(stream->str.str); break; case LineTypeWString: XtFree((char *)stream->wstr.str); break; case LineTypeSeparator: break; case LineTypeClickable: call_clickable(w, stream, False); XtFree(stream->cli.str); XtFree((char *)stream->cli.data); break; case LineTypeImage: call_clickable(w, stream, False); XtFree((char *)stream->img.data); break; } XtFree((char *)stream); stream = next; } } static long build_and_append(ArtTextWidget w, TSNode *stream, long y) { long n = w->arttext.lines; XFontStruct *font; long pos, len; char *str; XChar2b *wstr; int width = w->core.width - w->arttext.margin; int max_width = 0; switch (stream->gen.type) { case LineTypeString: pos = 0; font = stream->str.font; len = stream->str.len; str = stream->str.str; if (w->arttext.wrap_lines) do { int tmp; tmp = MyXWidthToChars(font, str, len, width); if (tmp <= 0 && len > 0) tmp = 1; if (n + 8 > w->arttext.n_alloc) alloc_text_table(w, 2 * (n + 1)); w->arttext.table[n].y = y; w->arttext.table[n].node = stream; w->arttext.table[n].start = pos; w->arttext.table[n].len = tmp; y += font->ascent + font->descent; n++; str += tmp; pos += tmp; len -= tmp; } while (len > 0); else { max_width = XTextWidth(font, str, len) + 2 * w->arttext.margin; if (n + 8 > w->arttext.n_alloc) alloc_text_table(w, 2 * (n + 1)); w->arttext.table[n].y = y; w->arttext.table[n].node = stream; w->arttext.table[n].start = 0; w->arttext.table[n].len = len; y += font->ascent + font->descent; n++; } break; case LineTypeWString: pos = 0; font = stream->wstr.font; len = stream->wstr.len; wstr = stream->wstr.str; if (w->arttext.wrap_lines) do { int tmp; tmp = MyXWidthToWChars(font, wstr, len, width); if (tmp <= 0 && len > 0) tmp = 1; if (n + 8 > w->arttext.n_alloc) alloc_text_table(w, 2 * (n + 1)); w->arttext.table[n].y = y; w->arttext.table[n].node = stream; w->arttext.table[n].start = pos; w->arttext.table[n].len = tmp; y += font->ascent + font->descent; n++; wstr += tmp; pos += tmp; len -= tmp; } while (len > 0); else { max_width = XTextWidth16(font, wstr, len) + 2 * w->arttext.margin; if (n + 8 > w->arttext.n_alloc) alloc_text_table(w, 2 * (n + 1)); w->arttext.table[n].y = y; w->arttext.table[n].node = stream; w->arttext.table[n].start = 0; w->arttext.table[n].len = len; y += font->ascent + font->descent; n++; } break; case LineTypeClickable: if (n + 8 > w->arttext.n_alloc) alloc_text_table(w, 2 * (n + 8)); font = stream->cli.font; max_width = XTextWidth(font, stream->cli.str, strlen(stream->cli.str)) + 2 * w->arttext.margin; w->arttext.table[n].y = y; w->arttext.table[n].node = stream; w->arttext.table[n].start = 0; w->arttext.table[n].len = 0; y += font->ascent + font->descent; n++; break; case LineTypeSeparator: if (n + 8 > w->arttext.n_alloc) alloc_text_table(w, 2 * (n + 8)); w->arttext.table[n].y = y; w->arttext.table[n].node = stream; w->arttext.table[n].start = 0; w->arttext.table[n].len = 0; y += stream->sep.height + 2 * stream->sep.margin; n++; break; case LineTypeImage: if (n + 8 > w->arttext.n_alloc) alloc_text_table(w, 2 * (n + 8)); max_width = stream->img.width + 2 * w->arttext.image_margin; w->arttext.table[n].y = y; w->arttext.table[n].node = stream; w->arttext.table[n].start = 0; w->arttext.table[n].len = 0; y += stream->img.height; n++; break; } if (w->arttext.max_width < max_width) w->arttext.max_width = max_width; w->arttext.lines = n; return y; } static void build_text_table(ArtTextWidget w) { TSNode *stream = w->arttext.stream; long n, y = 0; alloc_text_table(w, 8); w->arttext.lines = 0; w->arttext.table[0].y = 0; w->arttext.table[0].node = NULL; while (stream) { y = build_and_append(w, stream, y); stream = stream->gen.next; } n = w->arttext.lines; w->arttext.table[n].y = y; w->arttext.table[n].node = NULL; w->scrollable.height = y; if (w->arttext.first >= n) w->arttext.first = n > 0 ? n - 1 : 0; } static void append_text_node(ArtTextWidget w, TSNode *node) { long n = w->arttext.lines; long y; int width = w->arttext.max_width; node->gen.next = NULL; if (w->arttext.last) w->arttext.last->gen.next = node; else w->arttext.stream = node; w->arttext.last = node; y = build_and_append(w, node, w->arttext.table[n].y); w->arttext.table[w->arttext.lines].y = y; w->arttext.table[w->arttext.lines].node = NULL; w->scrollable.height = y; if (w->arttext.table[n].y < w->scrollable.pos_y + (int)w->core.height) draw_items(w, n, w->arttext.lines - 1); if (w->arttext.max_width > width) { XtMakeResizeRequest((Widget)w, w->arttext.max_width, w->core.height, NULL, NULL); ScrollableFitHBar((ScrollableWidget)w); } } static void update_last_node(ArtTextWidget w) { long n = w->arttext.lines; long y; do { n--; } while (n >= 0 && w->arttext.table[n].node == w->arttext.last); w->arttext.lines = ++n; y = w->arttext.table[n].y; w->arttext.table[n].node = NULL; y = build_and_append(w, w->arttext.last, y); w->arttext.table[w->arttext.lines].y = y; w->arttext.table[w->arttext.lines].node = NULL; w->scrollable.height = y; draw_items(w, n, w->arttext.lines); /* * FIXME: resize if necessary? */ } /*************************************************************************/ static void coords_to_line_offset(ArtTextWidget w, int ex, int ey, long *line, long *offset) { long y, n; ex -= w->arttext.margin; y = ey + w->scrollable.pos_y; *offset = 0; if (y < 0 || w->arttext.lines <= 0) { *line = 0; return; } n = coord_to_line(w, y); *line = n; if (n >= 0 && n < w->arttext.lines) { TSTable *table = w->arttext.table + n; TSNode *node = table->node; XFontStruct *font; switch (node->gen.type) { case LineTypeString: font = node->str.font; *offset = MyXWidthToChars(font, node->str.str + table->start, table->len, ex); break; case LineTypeWString: font = node->wstr.font; *offset = MyXWidthToWChars(font, node->wstr.str + table->start, table->len, ex); break; } } } static void make_visible(ArtTextWidget w, long line) { long y; if (w->arttext.table[line].y < w->scrollable.pos_y) y = w->arttext.table[line].y; else if (line >= w->arttext.lines) return; else if (w->arttext.table[line + 1].y >= w->scrollable.pos_y + (int)w->core.height) y = w->arttext.table[line + 1].y - w->core.height; else return; if (y < 0) y = 0; ScrollableSetVPos((Widget)w, y); } /*************************************************************************/ static long get_sel_len(ArtTextWidget w) { TSTable *tstart, *tstop; TSNode *start, *stop; long len; long start_off; long stop_off; if (!w->arttext.sel_ok || w->arttext.sel_start_line > w->arttext.sel_stop_line) return 0; tstart = w->arttext.table + w->arttext.sel_start_line; tstop = w->arttext.table + w->arttext.sel_stop_line; start = tstart->node; stop = tstop->node; if (!start || !stop) return 0; start_off = tstart->start + w->arttext.sel_start_offset; stop_off = tstop->start + w->arttext.sel_stop_offset; if (start == stop) if (start->gen.type != LineTypeString) return 0; else return stop_off - start_off + 1; len = 0; if (start->gen.type == LineTypeString) { if (start_off < start->str.len) len += start->str.len - start_off; len++; } start = start->gen.next; while (start && start != stop) { if (start->gen.type == LineTypeString) len += start->str.len + 1; start = start->gen.next; } if (stop->gen.type == LineTypeString) { if (stop->str.len == 0) len++; else if (stop_off < stop->str.len) len += stop_off + 1; else len += stop->str.len + 1; } return len; } static long get_sel(ArtTextWidget w, char *buf) { TSTable *tstart, *tstop; TSNode *start, *stop; long len, tmp; long start_off; long stop_off; if (!w->arttext.sel_ok || w->arttext.sel_start_line > w->arttext.sel_stop_line) return 0; tstart = w->arttext.table + w->arttext.sel_start_line; tstop = w->arttext.table + w->arttext.sel_stop_line; start = tstart->node; stop = tstop->node; if (!start || !stop) return 0; start_off = tstart->start + w->arttext.sel_start_offset; stop_off = tstop->start + w->arttext.sel_stop_offset; if (start == stop) if (start->gen.type != LineTypeString) return 0; else { len = stop_off - start_off + 1; memcpy(buf, start->str.str + start_off, len); if (stop_off >= start->str.len) buf[len - 1] = '\n'; return len; } len = 0; if (start->gen.type == LineTypeString) { if (start_off < start->str.len) { len = start->str.len - start_off; memcpy(buf, start->str.str + start_off, len); } buf[len++] = '\n'; } while ((start = start->gen.next) && start != stop) if (start->gen.type == LineTypeString) { tmp = start->str.len; memcpy(buf + len, start->str.str, tmp); len += tmp; buf[len++] = '\n'; } if (stop->gen.type == LineTypeString) if (stop->str.len == 0) buf[len++] = '\n'; else if (stop_off < stop->str.len) { tmp = stop_off + 1; memcpy(buf + len, stop->str.str, tmp); len += tmp; } else { tmp = stop->str.len; memcpy(buf + len, stop->str.str, tmp); len += tmp; buf[len++] = '\n'; } return len; } static Boolean convert_selection(Widget gw, Atom *selection, Atom *target, Atom *type_return, XtPointer *value_return, unsigned long *length_return, int *format_return) { ArtTextWidget w = (ArtTextWidget)gw; Display *disp = XtDisplay(w); if (!w->arttext.sel_ok || *selection != w->arttext.curr_sel) return False; if (*target == XA_STRING || *target == intern_atom(disp, "TEXT")) { char *buffer; long len; len = get_sel_len(w); buffer = XtMalloc(len + 16); len = get_sel(w, buffer); buffer[len] = '\0'; *value_return = (XtPointer)buffer; *length_return = len; *type_return = XA_STRING; *format_return = 8; return True; } if (*target == intern_atom(disp, "TARGETS")) { Atom *std_targets, *atom; unsigned long std_length, n; if (!cvt_std_sel((Widget)w, w->arttext.sel_time, selection, target, type_return, (XPointer *)&std_targets, &std_length, format_return)) return False; *value_return = (XtPointer)XtMalloc((std_length + 8) * sizeof(Atom)); atom = (Atom *)*value_return; n = std_length; n++; *atom++ = XA_STRING; n++; *atom++ = intern_atom(disp, "TEXT"); n++; *atom++ = intern_atom(disp, "LENGTH"); n++; *atom++ = intern_atom(disp, "LIST_LENGTH"); memcpy(atom, std_targets, std_length * sizeof(Atom)); XtFree((char *)std_targets); *length_return = n; *type_return = intern_atom(disp, "ATOM"); *format_return = 32; return True; } if (*target == intern_atom(disp, "LENGTH")) { long *length; length = (long *)XtMalloc(sizeof(long)); *length = get_sel_len(w); *value_return = (XtPointer)length; *type_return = XA_INTEGER; *length_return = 1; *format_return = 32; return True; } if (*target == intern_atom(disp, "LIST_LENGTH")) { long *length = (long *)XtMalloc(sizeof(long)); *length = 1; *value_return = (XtPointer)length; *type_return = XA_INTEGER; *length_return = 1; *format_return = 32; return True; } return cvt_std_sel((Widget)w, w->arttext.sel_time, selection, target, type_return, (XPointer *)value_return, length_return, format_return); } static void lose_selection(Widget gw, Atom *selection) { ArtTextWidget w = (ArtTextWidget)gw; if (w->arttext.sel_ok) { w->arttext.sel_ok = False; clear_items(w, w->arttext.sel_start_line, w->arttext.sel_stop_line); draw_items(w, w->arttext.sel_start_line, w->arttext.sel_stop_line); } } static int change_selection(ArtTextWidget w, long start_line, int start_offset, long stop_line, int stop_offset) { int did_swap = False; long o_start_line = w->arttext.sel_start_line; long o_stop_line = w->arttext.sel_stop_line; int o_start_offset = w->arttext.sel_start_offset; int o_stop_offset = w->arttext.sel_stop_offset; if (o_start_line == start_line && o_stop_line == stop_line && o_start_offset == start_offset && o_stop_offset == stop_offset) return False; if (start_line > stop_line || (start_line == stop_line && start_offset > stop_offset)) { long tmp; #undef SWAP #define SWAP(a, b) (tmp = a, a = b, b = tmp) SWAP(start_line, stop_line); SWAP(start_offset, stop_offset); #undef SWAP did_swap = True; } w->arttext.sel_start_line = start_line; w->arttext.sel_stop_line = stop_line; w->arttext.sel_start_offset = start_offset; w->arttext.sel_stop_offset = stop_offset; if (o_start_line > start_line) draw_items(w, start_line, o_start_line); else if (o_start_line < start_line) { clear_items(w, o_start_line, start_line); draw_items(w, o_start_line, start_line); } else { if (o_start_offset < start_offset) clear_items(w, start_line, start_line); draw_item(w, start_line); } if (o_stop_line < stop_line) draw_items(w, o_stop_line, stop_line); else if (o_stop_line > stop_line) { clear_items(w, stop_line, o_stop_line); draw_items(w, stop_line, o_stop_line); } else { if (o_stop_offset > stop_offset) clear_items(w, stop_line, stop_line); draw_item(w, stop_line); } return did_swap; } /*************************************************************************/ static void click(Widget gw, XEvent *event, String *params, Cardinal *no_params) { ArtTextWidget w = (ArtTextWidget)gw; long line, offset; int x, y, button; TSNode *node; if (!get_event_xy(event, &x, &y)) { XBell(XtDisplay(w), 0); return; } coords_to_line_offset(w, x, y, &line, &offset); if (line < 0 || line >= w->arttext.lines) return; if (event->type != ButtonPress && event->type != ButtonRelease) button = 1; else button = event->xbutton.button; node = w->arttext.table[line].node; if (node) call_clickable(w, node, True); } static void select_start(Widget gw, XEvent *event, String *params, Cardinal *no_params) { ArtTextWidget w = (ArtTextWidget)gw; long line, offset; int x, y; if (!get_event_xy(event, &x, &y)) { XBell(XtDisplay(w), 0); return; } if (w->arttext.sel_ok) { w->arttext.sel_ok = False; clear_items(w, w->arttext.sel_start_line, w->arttext.sel_stop_line); draw_items(w, w->arttext.sel_start_line, w->arttext.sel_stop_line); if (w->arttext.curr_sel) { XtDisownSelection((Widget)w, w->arttext.curr_sel, w->arttext.sel_time); w->arttext.curr_sel = (Atom)0; } } coords_to_line_offset(w, x, y, &line, &offset); if (line < 0 || line >= w->arttext.lines || (w->arttext.table[line].node->gen.type != LineTypeString && w->arttext.table[line].node->gen.type != LineTypeWString)) return; w->arttext.sel_start_line = line; w->arttext.sel_stop_line = line; w->arttext.sel_start_offset = offset; w->arttext.sel_stop_offset = offset; w->arttext.sel_ok = (w->arttext.lines > 0); w->arttext.extending = True; w->arttext.extend_end = True; draw_item(w, line); } static void select_extend_start(Widget gw, XEvent *event, String *params, Cardinal *no_params) { ArtTextWidget w = (ArtTextWidget)gw; long line, offset; int x, y; Boolean extend_start; if (!w->arttext.sel_ok) { select_start(gw, event, params, no_params); return; } if (!get_event_xy(event, &x, &y)) { XBell(XtDisplay(w), 0); return; } coords_to_line_offset(w, x, y, &line, &offset); if (line < 0 || line >= w->arttext.lines || (w->arttext.table[line].node->gen.type != LineTypeString && w->arttext.table[line].node->gen.type != LineTypeWString)) return; if (line < w->arttext.sel_start_line) extend_start = True; else if (line > w->arttext.sel_stop_line) extend_start = False; else if (w->arttext.sel_start_line == w->arttext.sel_stop_line) { if (offset <= w->arttext.sel_start_offset) extend_start = True; else if (offset >= w->arttext.sel_stop_line) extend_start = False; else if (offset - w->arttext.sel_start_offset <= w->arttext.sel_stop_offset - offset) extend_start = True; else extend_start = False; } else { if (line - w->arttext.sel_start_line <= w->arttext.sel_stop_line - line) extend_start = True; else extend_start = False; } w->arttext.extend_end = !extend_start; w->arttext.extending = True; if (extend_start) change_selection(w, line, offset, w->arttext.sel_stop_line, w->arttext.sel_stop_offset); else change_selection(w, w->arttext.sel_start_line, w->arttext.sel_start_offset, line, offset); } static void select_extend(Widget gw, XEvent *event, String *params, Cardinal *no_params) { ArtTextWidget w = (ArtTextWidget)gw; long line, offset; int x, y, did_swap; if (!w->arttext.sel_ok || !w->arttext.extending) return; if (!get_event_xy(event, &x, &y)) { XBell(XtDisplay(w), 0); return; } coords_to_line_offset(w, x, y, &line, &offset); if (line < 0 || line >= w->arttext.lines || (w->arttext.table[line].node->gen.type != LineTypeString && w->arttext.table[line].node->gen.type != LineTypeWString)) return; make_visible(w, line); if (w->arttext.extend_end) did_swap = change_selection(w, w->arttext.sel_start_line, w->arttext.sel_start_offset, line, offset); else did_swap = change_selection(w, line, offset, w->arttext.sel_stop_line, w->arttext.sel_stop_offset); if (did_swap) w->arttext.extend_end = !w->arttext.extend_end; } static void select_end(Widget gw, XEvent *event, String *params, Cardinal *no_params) { ArtTextWidget w = (ArtTextWidget)gw; w->arttext.extending = False; if (w->arttext.sel_ok) { Atom atom = XA_PRIMARY; Time time_stamp = get_event_time(event); if (*no_params > 0) atom = intern_atom(XtDisplay(w), params[0]); if (XtOwnSelection((Widget)w, atom, time_stamp, convert_selection, lose_selection, NULL)) { w->arttext.curr_sel = atom; w->arttext.sel_time = time_stamp; } } } static void call_url(Widget gw, XEvent *event, String *params, Cardinal *no_params) { ArtTextWidget w = (ArtTextWidget)gw; XtCallbackList c_list = w->arttext.url_callback; ArtTextUrlReport report; long line, offset; int x, y; if (!get_event_xy(event, &x, &y)) { XBell(XtDisplay(w), 0); return; } coords_to_line_offset(w, x, y, &line, &offset); if (line < 0 || line >= w->arttext.lines || (w->arttext.table[line].node->gen.type != LineTypeString && w->arttext.table[line].node->gen.type != LineTypeWString)) return; if (w->arttext.sel_ok) { if (line == w->arttext.sel_start_line && line == w->arttext.sel_stop_line && offset >= w->arttext.sel_start_offset && offset <= w->arttext.sel_stop_offset) { report.sel_ok = True; report.start = w->arttext.sel_start_offset; report.stop = w->arttext.sel_stop_offset; } else { w->arttext.sel_ok = False; clear_items(w, w->arttext.sel_start_line, w->arttext.sel_stop_line); draw_items(w, w->arttext.sel_start_line, w->arttext.sel_stop_line); if (w->arttext.curr_sel) { XtDisownSelection((Widget)w, w->arttext.curr_sel, w->arttext.sel_time); w->arttext.curr_sel = 0; } } } if (!w->arttext.sel_ok) { report.sel_ok = False; report.start = offset; report.stop = offset; } if (line < 0 || w->arttext.table[line].node->gen.type != LineTypeString) { XBell(XtDisplay(w), 0); return; } report.line = w->arttext.table[line].node->str.str + w->arttext.table[line].start; if (c_list) XtCallCallbackList((Widget)w, c_list, (XtPointer)&report); if (!w->arttext.sel_ok && report.sel_ok) { Atom atom = XA_PRIMARY; Time time_stamp = get_event_time(event); long len = strlen(report.line); if (report.start >= 0 && report.stop <= len && report.stop >= report.start) { w->arttext.sel_start_line = w->arttext.sel_stop_line = line; w->arttext.sel_start_offset = report.start; w->arttext.sel_stop_offset = report.stop; w->arttext.sel_ok = True; w->arttext.extending = False; draw_item(w, line); if (*no_params > 0) atom = intern_atom(XtDisplay(w), params[0]); if (XtOwnSelection((Widget)w, atom, time_stamp, convert_selection, lose_selection, NULL)) { w->arttext.curr_sel = atom; w->arttext.sel_time = time_stamp; } } } } /*************************************************************************/ static void Initialize(Widget grequest, Widget gnew, ArgList args, Cardinal *no_args) { ArtTextWidget new = (ArtTextWidget)gnew; new->arttext.sel_ok = False; new->arttext.extending = False; new->arttext.curr_sel = (Atom)0; new->arttext.table = NULL; new->arttext.stream = NULL; new->arttext.n_alloc = 0; new->arttext.first = 0; new->arttext.lines = 0; new->arttext.gc_fid = None; new->arttext.max_width = 0; if (new->core.width == 0) new->core.width = preferred_width(new); if (new->core.height == 0) new->core.height = preferred_height(new); free_text_data(new, 256); } static void Destroy(Widget gw) { ArtTextWidget w = (ArtTextWidget)gw; if (w->arttext.sel_ok) { if (w->arttext.curr_sel) { XtDisownSelection((Widget)w, w->arttext.curr_sel, CurrentTime); w->arttext.curr_sel = (Atom)0; } w->arttext.sel_ok = False; } free_text_data(w, 0); w->arttext.first = 0; w->scrollable.pos_y = 0; w->arttext.lines = 0; w->arttext.table = NULL; w->arttext.n_alloc = 0; if (w->arttext.gc != 0) XFreeGC(XtDisplay(w), w->arttext.gc); } static void Realize(Widget gw, XtValueMask *mask, XSetWindowAttributes *attributes) { ArtTextWidget w = (ArtTextWidget)gw; XGCValues values; ScrollableHFromGeometry((ScrollableWidget)w); build_text_table(w); /* * FIXME: resize if necessary? */ w->scrollable.shown_y = w->core.height; w->scrollable.height = MAX_Y(w); scrollableWidgetClass->core_class.realize((Widget)w, mask, attributes); values.font = w->arttext.gc_fid = w->arttext.font->fid; values.foreground = w->arttext.gc_fg = 0; values.background = w->arttext.highlight_pixel; w->arttext.gc = XCreateGC(XtDisplay(w), XtWindow(w), GCFont|GCForeground|GCBackground, &values); } static void Resize(Widget gw) { ArtTextWidget w = (ArtTextWidget)gw; if (!XtIsRealized((Widget)w)) return; if (w->arttext.sel_ok) { if (w->arttext.curr_sel) { XtDisownSelection((Widget)w, w->arttext.curr_sel, CurrentTime); w->arttext.curr_sel = (Atom)0; } w->arttext.sel_ok = False; } ScrollableHFromGeometry((ScrollableWidget)w); build_text_table(w); w->scrollable.shown_y = w->core.height; w->scrollable.height = MAX_Y(w); ScrollableFitHBar((ScrollableWidget)w); ScrollableFitVBar((ScrollableWidget)w); } static void SetVPos(ScrollableWidget gw, long pos_y) { ArtTextWidget w = (ArtTextWidget)gw; Display *disp = XtDisplay(w); Window win = XtWindow(w); long d = w->scrollable.pos_y - pos_y; if (d == 0) return; w->arttext.first = coord_to_line(w, pos_y); w->scrollable.pos_y = pos_y; if (d < 0) { d = - d; if (d < (int)w->core.height) { XCopyArea(disp, win, win, w->arttext.gc, 0, d, w->core.width, w->core.height - d, 0, 0); XClearArea(disp, win, 0, w->core.height - d, 0, 0, False); draw_pixels(w, w->core.height - d, w->core.height, NULL); } else { XClearWindow(disp, win); draw_pixels(w, 0, w->core.height, NULL); } } else { if (d < (int)w->core.height) { XCopyArea(disp, win, win, w->arttext.gc, 0, 0, w->core.width, w->core.height - d, 0, d); XClearArea(disp, win, 0, 0, 0, d, False); draw_pixels(w, 0, d, NULL); } else { XClearWindow(disp, win); draw_pixels(w, 0, w->core.height, NULL); } } } static void Redisplay(Widget gw, XEvent *event, Region region) { ArtTextWidget w = (ArtTextWidget)gw; int y, height; if (!XtIsRealized((Widget)w)) return; if (event) switch (event->type) { case Expose: y = event->xexpose.y; height = event->xexpose.height; break; case GraphicsExpose: y = event->xgraphicsexpose.y; height = event->xgraphicsexpose.height; break; default: return; } else { y = 0; height = w->core.height; } draw_pixels(w, y, height, region); } static Boolean SetValues(Widget gcurrent, Widget grequest, Widget gnew, ArgList args, Cardinal *num_args) { ArtTextWidget current = (ArtTextWidget)gcurrent; ArtTextWidget new = (ArtTextWidget)gnew; int redisplay = False; int relayout = False; if (new->arttext.highlight_pixel != current->arttext.highlight_pixel) { if (new->arttext.gc != 0) XSetBackground(XtDisplay(new), new->arttext.gc, new->arttext.highlight_pixel); redisplay = True; } if (new->arttext.separator_margin != current->arttext.separator_margin) redisplay = True; if (new->arttext.image_margin != current->arttext.image_margin || new->arttext.margin != current->arttext.margin || new->arttext.wrap_lines != current->arttext.wrap_lines) redisplay = relayout = True; if (relayout) { build_text_table(new); new->core.width = new->arttext.max_width; } return redisplay; } static XtGeometryResult QueryGeometry(Widget gw, XtWidgetGeometry *intended, XtWidgetGeometry *preferred) { ArtTextWidget w = (ArtTextWidget)gw; Dimension intended_width; Dimension intended_height; preferred->request_mode = CWHeight | CWWidth; preferred->height = preferred_height(w); preferred->width = preferred_width(w); if (intended->request_mode & CWWidth) intended_width = intended->width; else intended_width = w->core.width; if (intended->request_mode & CWHeight) intended_height = intended->height; else intended_height = w->core.height; if (intended_width == preferred->width && intended_height == preferred->height) return XtGeometryYes; else if (preferred->width == w->core.width && preferred->height == w->core.height) return XtGeometryNo; else return XtGeometryAlmost; } /*************************************************************************/ static void rot_13(char *c, long len) { while (len-- > 0) { if ((*c >= 'a' && *c < 'a' + 13) || (*c >= 'A' && *c < 'A' + 13)) *c += 13; else if ((*c >= 'a' + 13 && *c <= 'z') || (*c >= 'A' + 13 && *c <= 'Z')) *c -= 13; c++; } } void ArtTextRot13(Widget gw) { ArtTextWidget w = (ArtTextWidget)gw; TSNode *stream; for (stream = w->arttext.stream ; stream ; stream = stream->gen.next) if (stream->gen.type == LineTypeString) rot_13(stream->str.str, stream->str.len); build_text_table(w); /* * FIXME: resize if necessary? */ if (XtIsRealized((Widget)w)) { XClearWindow(XtDisplay(w), XtWindow(w)); Redisplay((Widget)w, NULL, 0); } ScrollableFitVBar((ScrollableWidget)w); } void ArtTextClearLines(Widget gw) { ArtTextWidget w = (ArtTextWidget)gw; Widget parent = XtParent(w); if (w->arttext.sel_ok) { if (w->arttext.curr_sel) { XtDisownSelection((Widget)w, w->arttext.curr_sel, CurrentTime); w->arttext.curr_sel = (Atom)0; } w->arttext.sel_ok = False; } free_text_data(w, w->arttext.n_alloc); XClearWindow(XtDisplay(w), XtWindow(w)); w->arttext.max_width = 0; XtMakeResizeRequest((Widget)w, parent->core.width, parent->core.height, NULL, NULL); ScrollableFitVBar((ScrollableWidget)w); } void ArtTextAddLine(Widget gw, const char *str, XFontStruct *font, Pixel pixel) { ArtTextWidget w = (ArtTextWidget)gw; TSNode *node; if (!font) font = w->arttext.font; node = (TSNode *)XtMalloc(sizeof *node); node->str.type = LineTypeString; node->str.font = font; node->str.pixel = pixel; node->str.str = tab_strdup(str, strlen(str), &node->str.len); append_text_node(w, node); if (!w->scrollable.suspended) ScrollableFitVBar((ScrollableWidget)w); } void ArtTextAppendToLast(Widget gw, const char *str) { ArtTextWidget w = (ArtTextWidget)gw; TSNode *last = w->arttext.last; long n; if (!last) ArtTextAddLine((Widget)w, str, NULL, w->core.background_pixel); else switch (last->gen.type) { case LineTypeString: last->str.str = tab_strdupcat(last->str.str, last->str.len, str, &last->str.len); update_last_node(w); break; case LineTypeClickable: n = strlen(last->cli.str) + strlen(str) + 4; last->cli.str = XtRealloc(last->cli.str, n); strcat(last->cli.str, str); update_last_node(w); break; default: ArtTextAddLine((Widget)w, str, NULL, 0); break; } if (!w->scrollable.suspended) ScrollableFitVBar((ScrollableWidget)w); } void ArtTextAddSelected(Widget gw, const char *str, XFontStruct *font, Pixel pixel, long sel_start, long sel_stop) { ArtTextWidget w = (ArtTextWidget)gw; TSNode *node; long n, j = 0; for (n = 0 ; str[n] != '\0' ; n++) { if (str[n] == '\t') { long i = 7 - ((n + j) % 8); if (sel_start > n + j) sel_start += i; if (sel_stop > n + j) sel_stop += i; j += i; } } if (!font) font = w->arttext.font; node = (TSNode *)XtMalloc(sizeof *node); node->str.type = LineTypeString; node->str.font = font; node->str.pixel = pixel; node->str.str = tab_strdup(str, strlen(str), &node->str.len); n = w->arttext.lines; append_text_node(w, node); if (w->arttext.sel_ok) { if (w->arttext.curr_sel) { XtDisownSelection((Widget)w, w->arttext.curr_sel, CurrentTime); w->arttext.curr_sel = (Atom)0; } w->arttext.sel_ok = False; draw_items(w, w->arttext.sel_start_line, w->arttext.sel_stop_line); } if (sel_start > sel_stop) return; while (n < w->arttext.lines) if (w->arttext.table[n].start + w->arttext.table[n].len > sel_start) break; else n++; w->arttext.sel_start_line = n; sel_start -= w->arttext.table[n].start; while (n < w->arttext.lines) if (w->arttext.table[n].start + w->arttext.table[n].len > sel_stop) break; else n++; if (n == w->arttext.lines) return; w->arttext.sel_stop_line = n; sel_stop -= w->arttext.table[n].start; w->arttext.sel_start_offset = sel_start; w->arttext.sel_stop_offset = sel_stop; w->arttext.sel_ok = True; /* FIXME: own selection? */ if (w->arttext.table[w->arttext.sel_start_line+1].y > (int)w->core.height) ScrollableSetVPos((Widget)w, w->arttext.table[w->arttext.sel_start_line].y); else { draw_items(w, w->arttext.sel_start_line, w->arttext.sel_stop_line); ScrollableFitVBar((ScrollableWidget)w); } } void ArtTextAddSeparator(Widget gw, int height, int margin) { ArtTextWidget w = (ArtTextWidget)gw; TSNode *node; node = (TSNode *)XtMalloc(sizeof *node); node->sep.type = LineTypeSeparator; node->sep.pixel = 0; node->sep.height = height; node->sep.margin = margin; append_text_node(w, node); if (!w->scrollable.suspended) ScrollableFitVBar((ScrollableWidget)w); } void ArtTextAddClickable(Widget gw, const char *str, XFontStruct *font, Pixel pixel, XtCallbackProc callback, void *client_data) { ArtTextWidget w = (ArtTextWidget)gw; TSNode *node; if (!font) font = w->arttext.font; node = (TSNode *)XtMalloc(sizeof *node); node->cli.type = LineTypeClickable; node->cli.font = font; node->cli.pixel = pixel; node->cli.str = XtNewString(str); node->cli.data = (CallbackData *)XtMalloc(sizeof *node->cli.data); node->cli.data->callback = callback; node->cli.data->client_data = client_data; append_text_node(w, node); if (!w->scrollable.suspended) ScrollableFitVBar((ScrollableWidget)w); } void ArtTextAddImage(Widget gw, Pixmap pixmap, int width, int height, XtCallbackProc callback, XtPointer client_data) { ArtTextWidget w = (ArtTextWidget)gw; TSNode *node; node = (TSNode *)XtMalloc(sizeof *node); node->img.type = LineTypeImage; node->img.pixmap = pixmap; node->img.width = width; node->img.height = height; node->img.data = (CallbackData *)XtMalloc(sizeof *node->img.data); node->img.data->callback = callback; node->img.data->client_data = client_data; append_text_node(w, node); if (!w->scrollable.suspended) ScrollableFitVBar((ScrollableWidget)w); } int ArtTextDumpToFile(Widget gw, FILE *file) { ArtTextWidget w = (ArtTextWidget)gw; TSNode *node; char *c = NULL; for (node = w->arttext.stream ; node ; node = node->gen.next) { switch (node->gen.type) { case LineTypeString: c = node->str.str; break; case LineTypeWString: c = NULL; break; case LineTypeSeparator: c = "------------------------------------------------"; break; case LineTypeClickable: c = node->cli.str; break; case LineTypeImage: c = "[IMAGE]"; break; } if (c && fprintf(file, "%s\n", c) == EOF) return -1; } return 0; } void ArtTextAddWLine(Widget gw, XChar2b *wstr, long len, XFontStruct *font, Pixel pixel) { ArtTextWidget w = (ArtTextWidget)gw; TSNode *node; if (!font) font = w->arttext.font; node = (TSNode *)XtMalloc(sizeof *node); node->wstr.type = LineTypeWString; node->wstr.font = font; node->wstr.pixel = pixel; node->wstr.str = (XChar2b *)XtMalloc(len * sizeof wstr[0]); node->wstr.len = len; memcpy(node->wstr.str, wstr, len * sizeof wstr[0]); append_text_node(w, node); if (!w->scrollable.suspended) ScrollableFitVBar((ScrollableWidget)w); } void ArtTextWAppendToLast(Widget gw, XChar2b *wstr, long len) { ArtTextWidget w = (ArtTextWidget)gw; TSNode *last = w->arttext.last; if (!last || last->gen.type != LineTypeWString) { ArtTextAddWLine((Widget)w, wstr, len, NULL, 0); return; } last->wstr.str = (XChar2b *)XtRealloc((char *)last->wstr.str, (last->wstr.len + len) * sizeof wstr[0]); memcpy(last->wstr.str + last->wstr.len, wstr, len); last->wstr.len += len; update_last_node(w); if (!w->scrollable.suspended) ScrollableFitVBar((ScrollableWidget)w); } void ArtTextAllocLines(Widget gw, long n_alloc) { ArtTextWidget w = (ArtTextWidget)gw; long n = w->arttext.n_alloc; if (n_alloc <= n) return; w->arttext.n_alloc = n_alloc; w->arttext.table = (TSTable *)XtRealloc((char *)w->arttext.table, n_alloc * sizeof w->arttext.table[0]); while (n < n_alloc) { w->arttext.table[n].y = 0; w->arttext.table[n].node = NULL; w->arttext.table[n].start = 0; w->arttext.table[n].len = 0; n++; } } ./knews-1.0b.1/Widgets/ArtText.h100644 1244 1244 3644 6455455534 15016 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #ifndef ArtText_h #define ArtText_h #ifndef XtCMargin #define XtCMargin "Margin" #endif #ifndef XtCPreferredLines #define XtCPreferredLines "PreferredLines" #endif #ifndef XtCPreferredColumns #define XtCPreferredColumns "PreferredColumns" #endif #ifndef XtCWrapLines #define XtCWrapLines "WrapLines" #endif #ifndef XtNhighlightColor #define XtNhighlightColor "highlightColor" #endif #ifndef XtNseparatorMargin #define XtNseparatorMargin "separatorMargin" #endif #ifndef XtNpreferredLines #define XtNpreferredLines "preferredLines" #endif #ifndef XtNpreferredColumns #define XtNpreferredColumns "preferredColumns" #endif #ifndef XtNurlCallback #define XtNurlCallback "urlCallback" #endif #ifndef XtNwrapLines #define XtNwrapLines "wrapLines" #endif #ifndef XtNimageMargin #define XtNimageMargin "imageMargin" #endif #ifndef XtNmargin #define XtNmargin "margin" #endif typedef struct ArtTextClassRec* ArtTextWidgetClass; typedef struct ArtTextRec* ArtTextWidget; extern WidgetClass artTextWidgetClass; typedef struct { const char *line; int sel_ok; long start; long stop; } ArtTextUrlReport; extern void ArtTextRot13(Widget); extern void ArtTextClearLines(Widget); extern void ArtTextAddLine(Widget, const char*, XFontStruct*, Pixel); extern void ArtTextAddWLine(Widget, XChar2b*, long, XFontStruct*, Pixel); extern void ArtTextAppendToLast(Widget, const char*); extern void ArtTextWAppendToLast(Widget, XChar2b*, long); extern void ArtTextAddSelected(Widget, const char*, XFontStruct*, Pixel, long, long); extern void ArtTextAddSeparator(Widget, int, int); extern void ArtTextAddClickable(Widget, const char*, XFontStruct*, Pixel, XtCallbackProc, void*); extern void ArtTextAddImage(Widget, Pixmap, int, int, XtCallbackProc, XtPointer); extern int ArtTextDumpToFile(Widget, FILE*); extern void ArtTextAllocLines(Widget, long); #endif /* ArtText_h */ ./knews-1.0b.1/Widgets/TextField.c100644 1244 1244 204452 6455455534 15346 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include #include #include #include #include #include #include #include "Compat.h" #include "Util.h" #include "TextFieldP.h" static XtResource resources[] = { {XtNborderWidth, XtCBorderWidth, XtRDimension, sizeof(Dimension), XtOffsetOf(TextFieldRec, core.border_width), XtRImmediate, (XtPointer)2}, #define offset(field) XtOffsetOf(TextFieldRec, textfield.field) {XtNbuffer, XtCBuffer, XtRString, sizeof(String), offset(buffer), XtRImmediate, (XtPointer)""}, {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel), offset(fg_pixel), XtRString, XtDefaultForeground}, {XtNfocusColor, XtCForeground, XtRPixel, sizeof(Pixel), offset(focus_pixel), XtRString, XtDefaultForeground}, {XtNhighlightForeground, XtCBackground, XtRPixel, sizeof(Pixel), offset(highlight_fg), XtRString, XtDefaultBackground}, {XtNhighlightBackground, XtCForeground, XtRPixel, sizeof(Pixel), offset(highlight_bg), XtRString, XtDefaultForeground}, {XtNfont, XtCFont, XtRFontStruct, sizeof(XFontStruct *), offset(font), XtRString, XtDefaultFont}, {XtNinternalWidth, XtCInternalWidth, XtRDimension, sizeof(Dimension), offset(internal_width), XtRImmediate, (XtPointer)8}, {XtNinternalHeight, XtCInternalHeight, XtRDimension, sizeof(Dimension), offset(internal_height), XtRImmediate, (XtPointer)3}, {XtNpreferredChars, XtCPreferredChars, XtRInt, sizeof(int), offset(pref_chars), XtRImmediate, (XtPointer)20}, {XtNpreferredLines, XtCPreferredLines, XtRInt, sizeof(int), offset(pref_lines), XtRImmediate, (XtPointer)1}, {XtNsingleLine, XtCSingleLine, XtRBoolean, sizeof(Boolean), offset(single_line), XtRImmediate, (XtPointer)True}, {XtNcallback, XtCCallback, XtRCallback, sizeof(XtCallbackList), offset(callback), XtRCallback, (XtPointer)NULL}, {XtNtabCallback, XtCCallback, XtRCallback, sizeof(XtCallbackList), offset(tab_callback), XtRCallback, (XtPointer)NULL}, {XtNfocusCallback, XtCCallback, XtRCallback, sizeof(XtCallbackList), offset(focus_callback), XtRCallback, (XtPointer)NULL}, {XtNborderIn, XtCBorderIn, XtRBoolean, sizeof(Boolean), offset(border_in), XtRImmediate, (XtPointer)True}, {XtNdisplayCaret, XtCDisplayCaret, XtRBoolean, sizeof(Boolean), offset(display_caret), XtRImmediate, (XtPointer)False}, {XtNfocusRoot, XtCFocusRoot, XtRWidget, sizeof(Widget), offset(focus_root), XtRImmediate, (XtPointer)NULL}, {XtNfocusHack, XtCHack, XtRBoolean, sizeof(Boolean), offset(focus_hack), XtRImmediate, (XtPointer)True}, {XtNprintFocus, XtCDebug, XtRBoolean, sizeof(Boolean), offset(print_focus), XtRImmediate, (XtPointer)False}, {XtNechoOff, XtCEchoOff, XtRBoolean, sizeof(Boolean), offset(echo_off), XtRImmediate, (XtPointer)False}, #undef offset }; static void Initialize(Widget, Widget, ArgList, Cardinal*); static void Destroy(Widget); static void Redisplay(Widget, XEvent*, Region); static void Resize(Widget); static void Realize(Widget, XtValueMask*, XSetWindowAttributes*); static void SetHPos(ScrollableWidget, long); static void SetVPos(ScrollableWidget, long); static Boolean SetValues(Widget, Widget, Widget, ArgList, Cardinal*); static XtGeometryResult QueryGeometry(Widget, XtWidgetGeometry*, XtWidgetGeometry*); static void aquire_focus(Widget, XEvent*, String*, Cardinal*); static void nop(Widget, XEvent*, String*, Cardinal*); static void multiply(Widget, XEvent*, String*, Cardinal*); static void beginning_of_line(Widget, XEvent*, String*, Cardinal*); static void end_of_line(Widget, XEvent*, String*, Cardinal*); static void home(Widget, XEvent*, String*, Cardinal*); static void end(Widget, XEvent*, String*, Cardinal*); static void left(Widget, XEvent*, String*, Cardinal*); static void right(Widget, XEvent*, String*, Cardinal*); static void up(Widget, XEvent*, String*, Cardinal*); static void down(Widget, XEvent*, String*, Cardinal*); static void page(Widget, XEvent*, String*, Cardinal*); static void delete_next(Widget, XEvent*, String*, Cardinal*); static void delete_previous(Widget, XEvent*, String*, Cardinal*); static void kill_action(Widget, XEvent*, String*, Cardinal*); static void redraw(Widget, XEvent*, String*, Cardinal*); static void enter(Widget, XEvent*, String*, Cardinal*); static void tab(Widget, XEvent*, String*, Cardinal*); static void transpose(Widget, XEvent*, String*, Cardinal*); static void set_border_color(Widget, XEvent*, String*, Cardinal*); static void insert(Widget, XEvent*, String*, Cardinal*); static void insert_string(Widget, XEvent*, String*, Cardinal*); static void swap_select(Widget, XEvent*, String*, Cardinal*); static void select_start(Widget, XEvent*, String*, Cardinal*); static void extend_start(Widget, XEvent*, String*, Cardinal*); static void select_extend(Widget, XEvent*, String*, Cardinal*); static void select_end(Widget, XEvent*, String*, Cardinal*); static void kill_selection(Widget, XEvent*, String*, Cardinal*); static void insert_selection(Widget, XEvent*, String*, Cardinal*); static void disown_selection(Widget, XEvent*, String*, Cardinal*); static void display_caret(Widget, XEvent*, String*, Cardinal*); static XtActionsRec actions[] = { {"aquire-focus", aquire_focus}, {"nop", nop}, {"multiply", multiply}, {"beginning-of-line", beginning_of_line}, {"end-of-line", end_of_line}, {"home", home}, {"end", end}, {"left", left}, {"right", right}, {"up", up}, {"down", down}, {"page", page}, {"delete-next", delete_next}, {"delete-previous", delete_previous}, {"kill", kill_action}, {"redraw", redraw}, {"enter", enter}, {"tab", tab}, {"transpose", transpose}, {"set-border-color", set_border_color}, {"insert", insert}, {"insert-string", insert_string}, {"swap-select", swap_select}, {"select-start", select_start}, {"extend-start", extend_start}, {"select-extend", select_extend}, {"select-end", select_end}, {"kill-selection", kill_selection}, {"insert-selection", insert_selection}, {"disown-selection", disown_selection}, {"display-caret", display_caret}, }; static char translations[] = "CtrlA: beginning-of-line() \n" "CtrlB: left() \n" "CtrlC: nop() \n" "CtrlD: delete-next() \n" "CtrlE: end-of-line() \n" "CtrlF: right() \n" "CtrlG: multiply(0) disown-selection() \n" "CtrlH: delete-previous() \n" "CtrlI: tab() \n" "CtrlJ: enter() \n" "CtrlK: kill() \n" "CtrlL: redraw() \n" "CtrlM: enter() \n" "CtrlN: down() \n" "CtrlO: enter() left() \n" "CtrlP: up() \n" "CtrlT: transpose() \n" "CtrlU: multiply(4) \n" "CtrlV: page(+1.0) \n" "CtrlW: kill-selection(PRIMARY) \n" "CtrlX,CtrlX: swap-select(PRIMARY) \n" "Ctrlspace: select-start() \n" "Left: left() \n" "Right: right() \n" "Up: up() \n" "Down: down() \n" "Return: enter() \n" "BackSpace: delete-previous() \n" "Delete: delete-previous() \n" "Tab: tab() \n" "Home: home() \n" "End: end() \n" "Prior: page(-1.0) \n" "Next: page(+1.0) \n" ": set-border-color(focusColor) display-caret(on) \n" ": set-border-color(background) display-caret(off) \n" ": aquire-focus() select-start() \n" ": select-extend() \n" ": select-end(PRIMARY) \n" ": insert-selection(PRIMARY) \n" ": extend-start(PRIMARY) \n" ": select-extend() \n" ": select-end(PRIMARY) \n" "Ctrl0: multiply(0) \n" "Ctrl1: multiply(1) \n" "Ctrl2: multiply(2) \n" "Ctrl3: multiply(3) \n" "Ctrl4: multiply(4) \n" "Ctrl5: multiply(5) \n" "Ctrl6: multiply(6) \n" "Ctrl7: multiply(7) \n" "Ctrl8: multiply(8) \n" "Ctrl9: multiply(9) \n" ": insert() \n"; TextFieldClassRec textFieldClassRec = { { /* core fields */ (WidgetClass) &scrollableClassRec, /* superclass */ "TextField", /* class_name */ sizeof(TextFieldRec), /* widget_size */ NULL, /* class_initialize */ NULL, /* class_part_initialize */ FALSE, /* class_inited */ Initialize, /* initialize */ NULL, /* initialize_hook */ Realize, /* realize */ actions, /* actions */ XtNumber(actions), /* num_actions */ resources, /* resources */ XtNumber(resources), /* num_resources */ NULLQUARK, /* xrm_class */ TRUE, /* compress_motion */ #if (XtSpecificationRelease < 4) True, /* compress_exposure */ #elif (XtSpecificationRelease < 6) XtExposeCompressMaximal | XtExposeGraphicsExposeMerged, /* compress_exposure */ #else XtExposeCompressMaximal | XtExposeGraphicsExposeMerged | XtExposeNoRegion, /* compress_exposure*/ #endif FALSE, /* compress_enterleave */ FALSE, /* visible_interest */ Destroy, /* destroy */ Resize, /* resize */ Redisplay, /* expose */ SetValues, /* set_values */ NULL, /* set_values_hook */ XtInheritSetValuesAlmost, /* set_values_almost */ NULL, /* get_values_hook */ NULL, /* accept_focus */ XtVersion, /* version */ NULL, /* callback_private */ translations, /* tm_table */ QueryGeometry, /* query_geometry */ XtInheritDisplayAccelerator, /* display_accelerator */ NULL /* extension */ }, { /* shadow fields */ XtInheritPixelOffset, /* pixel_offset */ True, /* use_arm_for_background */ XtInheritAllocShadowColors, /* alloc_shadow_colors */ XtInheritAllocShadowPixmaps, /* alloc_shadow_pixmaps */ XtInheritAllocArmColor, /* alloc_arm_color */ NULL, /* alloc_arm_pixmap */ XtInheritAllocGCs, /* alloc_gcs */ NULL, /* extension */ }, { /* scrollable fields */ SetHPos, /* set_hpos */ SetVPos, /* set_vpos */ NULL, /* suspend_hook */ NULL /* extension */ }, { /* textfield fields */ 0 /* extension */ } }; WidgetClass textFieldWidgetClass = (WidgetClass)&textFieldClassRec; /*************************************************************************/ static void delete_sel_callback(Widget, XtPointer, Atom*, Atom*, XtPointer, unsigned long*, int*); static void insert_sel_callback(Widget, XtPointer, Atom*, Atom*, XtPointer, unsigned long*, int*); static Boolean convert_sel_proc(Widget, Atom*, Atom*, Atom*, XtPointer*, unsigned long*, int*); static void lose_sel_proc(Widget, Atom*); static void init_gcs(TextFieldWidget w) { XGCValues values; values.foreground = w->textfield.fg_pixel; values.background = w->core.background_pixel; values.font = w->textfield.font->fid; w->textfield.gc = XtGetGC((Widget)w, GCForeground | GCBackground | GCFont, &values); values.foreground = w->textfield.highlight_fg; values.background = w->textfield.highlight_bg; values.font = w->textfield.font->fid; w->textfield.h_gc = XtGetGC((Widget)w, GCForeground | GCBackground | GCFont, &values); } static void free_gcs(TextFieldWidget w) { XtReleaseGC((Widget)w, w->textfield.gc); XtReleaseGC((Widget)w, w->textfield.h_gc); } static void call_callbacks(TextFieldWidget w, XtCallbackList c_list) { char *buffer; if (!c_list) return; buffer = TextFieldGetBuffer((Widget)w); XtCallCallbackList((Widget)w, c_list, (XtPointer)buffer); XtFree(buffer); } static void free_lines(TextFieldWidget w) { long i; for (i = 0 ; i < w->scrollable.height ; i++) XtFree(w->textfield.lines[i].buf); XtFree((char *)w->textfield.lines); w->textfield.lines = NULL; w->textfield.n_lines = 0; w->scrollable.height = 0; w->scrollable.pos_y = 0; w->scrollable.width = 0; w->scrollable.pos_x = 0; } static void lines_from_buffer(TextFieldWidget w) { char *c = w->textfield.buffer; w->textfield.buffer = NULL; free_lines(w); if (w->textfield.single_line) w->textfield.n_lines = 1; else w->textfield.n_lines = 16; w->textfield.lines = (LineBuf *)XtMalloc(w->textfield.n_lines * sizeof w->textfield.lines[0]); if (!c) c = ""; if (w->textfield.single_line) { char *p = strchr(c, '\n'); long len = p ? p - c : strlen(c); w->textfield.lines[0].len = len + 1; w->textfield.lines[0].buf = strcpy(XtMalloc(len + 1), c); w->textfield.lines[0].buf[len] = '\0'; w->scrollable.width = len + 1; w->scrollable.height = 1; if (len < w->scrollable.shown_x) w->scrollable.pos_x = 0; else w->scrollable.pos_x = len - w->scrollable.shown_x + 1; w->scrollable.pos_y = 0; w->textfield.caret_x = len; w->textfield.caret_y = 0; } else { long n = 0; long max = 0; for (;;) { char *p = strchr(c, '\n'); long len = p ? p - c : strlen(c); if (n + 8 > w->textfield.n_lines) w->textfield.lines = (LineBuf *)XtRealloc((char *)w->textfield.lines, (w->textfield.n_lines = 2 * n) * sizeof w->textfield.lines[0]); w->textfield.lines[n].buf = memcpy(XtMalloc(len + 1), c, len); w->textfield.lines[n].buf[len] = '\0'; w->textfield.lines[n++].len = len + 1; if (max < len) max = len; if (!p) break; c = p + 1; } w->scrollable.width = max + 1; w->scrollable.height = n; w->scrollable.pos_x = 0; w->scrollable.pos_y = 0; w->textfield.caret_x = 0; w->textfield.caret_y = 0; while (n < w->textfield.n_lines) { w->textfield.lines[n].buf = NULL; w->textfield.lines[n].len = 0; n++; } } } static void open_lines(TextFieldWidget w, long at, long size) { long n = w->textfield.n_lines; if (w->scrollable.height + size + 8 < n) { w->textfield.lines = (LineBuf *)XtRealloc((char *)w->textfield.lines, (w->textfield.n_lines = 2 * (w->scrollable.height + size + 8)) * sizeof w->textfield.lines[0]); while (n < w->textfield.n_lines) { w->textfield.lines[n].buf = NULL; w->textfield.lines[n].len = 0; n++; } } n = w->scrollable.height; w->scrollable.height += size; ScrollableFitVBar((ScrollableWidget)w); if (at < n) memmove(w->textfield.lines + at + size, w->textfield.lines + at, (n - at) * sizeof w->textfield.lines[0]); n = at + size; while (at < n) { w->textfield.lines[at].buf = XtMalloc(1); w->textfield.lines[at].buf[0] = '\0'; w->textfield.lines[at].len = 1; at++; } } static void get_char_sizes(TextFieldWidget w) { XFontStruct *font = w->textfield.font; w->textfield.char_w = font->max_bounds.width; if (w->textfield.char_w <= 0) w->textfield.char_w = 1; w->textfield.char_h = font->ascent + font->descent; if (w->textfield.char_w != font->min_bounds.width) fputs("Warning: The TextFieldWidget " "only works with fixed width fonts.\n", stderr); } static void get_preferred_sizes(TextFieldWidget w, Dimension *width, Dimension *height) { if (w->textfield.pref_chars <= 0) w->textfield.pref_chars = 1; if (w->textfield.pref_lines <= 0) w->textfield.pref_lines = 1; *width = 2 * (w->textfield.internal_width + w->shadow.shadow_width) + w->textfield.pref_chars * w->textfield.char_w; *height = 2 * (w->textfield.internal_height + w->shadow.shadow_width) + w->textfield.pref_lines * w->textfield.char_h; } static int event_to_pos(TextFieldWidget w, XEvent *event, long *x, long *y) { int e_x, e_y; if (!get_event_xy(event, &e_x, &e_y)) return False; e_x -= w->shadow.shadow_width + w->textfield.internal_width; e_y -= w->shadow.shadow_width + w->textfield.internal_height; *x = w->scrollable.pos_x + e_x / w->textfield.char_w; *y = w->scrollable.pos_y + e_y / w->textfield.char_h; if (*x >= w->scrollable.width) *x = w->scrollable.width - 1; if (*x < 0) *x = 0; if (*y >= w->scrollable.height) *y = w->scrollable.height - 1; if (*y < 0) *y = 0; return True; } static void calc_shown(TextFieldWidget w) { long tmp; tmp = w->core.width; tmp -= 2 * (w->shadow.shadow_width + w->textfield.internal_width); if (tmp < 0) tmp = 0; w->scrollable.shown_x = tmp / w->textfield.char_w; tmp = w->core.height; tmp -= 2 * (w->shadow.shadow_width + w->textfield.internal_height); if (tmp < 0) tmp = 0; w->scrollable.shown_y = tmp / w->textfield.char_h; } static long max_width(TextFieldWidget w) { long n, max = 0; for (n = 0 ; n < w->scrollable.height ; n++) { long tmp = strlen(w->textfield.lines[n].buf); if (max < tmp) max = tmp; } return max; } static void sync_width(TextFieldWidget w, long old_w, long new_w) { long max, width = w->scrollable.width; old_w++; new_w++; if (new_w < width && old_w < width) return; max = 1; if (old_w < width || new_w <= old_w) max = max_width(w) + 1; if (max < new_w) max = new_w; if (max == width) return; w->scrollable.width = max; ScrollableFitHBar((ScrollableWidget)w); } static void clear_segment(TextFieldWidget w, long line, long pos, long width) { long x, y; line -= w->scrollable.pos_y; if (line < 0 || line >= w->scrollable.shown_y) return; pos -= w->scrollable.pos_x; if (pos >= w->scrollable.shown_x) return; if (pos < 0) width += pos, pos = 0; if (width > w->scrollable.shown_x - pos) width = w->scrollable.shown_x - pos; if (pos + width < 0) return; x = w->shadow.shadow_width + w->textfield.internal_width; y = w->shadow.shadow_width + w->textfield.internal_height; x += pos * w->textfield.char_w; y += line * w->textfield.char_h; width *= w->textfield.char_w; XClearArea(XtDisplay(w), XtWindow(w), x, y, width, w->textfield.char_h, False); } static void draw_stars(Display *disp, Window win, GC gc, long x, long y, long len) { char buffer[256]; char *c = buffer; if (len > sizeof buffer) c = XtMalloc(len); memset(c, '*', len); XDrawString(disp, win, gc, x, y, c, len); if (c != buffer) XtFree(c); } static void draw_rectangle(TextFieldWidget w, long x0, long y0, long width, long height, int clear) { Display *disp = XtDisplay(w); Window win = XtWindow(w); GC gc = w->textfield.gc; GC h_gc = w->textfield.h_gc; long x, y, i, tmp; tmp = x0 - w->scrollable.pos_x; if (tmp < 0) { x0 = w->scrollable.pos_x; width += tmp; } tmp = w->scrollable.pos_x + w->scrollable.shown_x - x0; if (width > tmp) width = tmp; if (width <= 0) return; tmp = y0 - w->scrollable.pos_y; if (tmp < 0) { y0 = w->scrollable.pos_y; height += tmp; } tmp = w->scrollable.pos_y + w->scrollable.shown_y - y0; if (height > tmp) height = tmp; tmp = w->scrollable.height - y0; if (height > tmp) height = tmp; x = w->shadow.shadow_width + w->textfield.internal_width; y = w->shadow.shadow_width + w->textfield.internal_height; x += (x0 - w->scrollable.pos_x) * w->textfield.char_w; y += (y0 - w->scrollable.pos_y) * w->textfield.char_h; if (clear) { long tmp; if (y0 + height < w->scrollable.height) tmp = height; else tmp = w->scrollable.shown_y + w->scrollable.pos_y - y0; if (tmp > 0) XClearArea(disp, win, x, y, width * w->textfield.char_w, tmp * w->textfield.char_h, False); } y += w->textfield.font->ascent; for (i = y0 ; i < y0 + height ; i++, y += w->textfield.char_h) { char *str = w->textfield.lines[i].buf; long len = strlen(str); len -= x0; if (len <= 0) continue; if (len > width) len = width; str += x0; if (w->textfield.echo_off) draw_stars(disp, win, gc, x, y, len); else if (!w->textfield.sel_set || i < w->textfield.sel_start_y || i > w->textfield.sel_stop_y) XDrawString(disp, win, gc, x, y, str, len); else if (i > w->textfield.sel_start_y && i < w->textfield.sel_stop_y) XDrawImageString(disp, win, h_gc, x, y, str, len); else { long n, pos = x0; long x_tmp = x; if (i == w->textfield.sel_start_y && (n = w->textfield.sel_start_x - pos) > 0) { if (n > len) n = len; XDrawString(disp, win, gc, x_tmp, y, str, n); x_tmp += n * w->textfield.char_w; str += n; pos += n; len -= n; } if (i == w->textfield.sel_stop_y) n = w->textfield.sel_stop_x - pos; else n = len; if (n > 0) { if (n > len) n = len; XDrawImageString(disp, win, h_gc, x_tmp, y, str, n); x_tmp += n * w->textfield.char_w; str += n; pos += n; len -= n; } if (len > 0) XDrawString(disp, win, gc, x_tmp, y, str, len); } } } static void draw_pixels(TextFieldWidget w, long x0, long y0, long width, long height, int clear) { long tmp; tmp = w->shadow.shadow_width + w->textfield.internal_width; x0 -= tmp; width += x0 + w->textfield.char_w - 1; if (x0 < 0) x0 = 0; else x0 /= w->textfield.char_w; width /= w->textfield.char_w; width -= x0; if (width <= 0) return; tmp = w->shadow.shadow_width + w->textfield.internal_height; y0 -= tmp; height += y0 + w->textfield.char_h - 1; if (y0 < 0) y0 = 0; else y0 /= w->textfield.char_h; height /= w->textfield.char_h; height -= y0; if (height <= 0) return; x0 += w->scrollable.pos_x; y0 += w->scrollable.pos_y; draw_rectangle(w, x0, y0, width, height, clear); } static void draw_caret(TextFieldWidget w) { long x, y, tmp; if (!w->textfield.display_caret || (w->textfield.sel_set && (w->textfield.sel_start_x != w->textfield.sel_stop_x || w->textfield.sel_start_y != w->textfield.sel_stop_y))) return; x = w->shadow.shadow_width + w->textfield.internal_width; y = w->shadow.shadow_width + w->textfield.internal_height; tmp = w->textfield.caret_x - w->scrollable.pos_x; if (tmp < 0 || tmp >= w->scrollable.shown_x) return; x += tmp * w->textfield.char_w; tmp = w->textfield.caret_y - w->scrollable.pos_y; if (tmp < 0 || tmp >= w->scrollable.shown_y) return; y += tmp * w->textfield.char_h; XDrawLine(XtDisplay(w), XtWindow(w), w->textfield.gc, x, y, x, y + w->textfield.char_h - 1); } static void undraw_caret(TextFieldWidget w) { if (!w->textfield.display_caret || (w->textfield.sel_set && (w->textfield.sel_start_x != w->textfield.sel_stop_x || w->textfield.sel_start_y != w->textfield.sel_stop_y))) return; draw_rectangle(w, w->textfield.caret_x, w->textfield.caret_y, 1, 1, True); } static void make_visible(TextFieldWidget w, long x, long y) { if (x < w->scrollable.pos_x) ScrollableSetHPos((Widget)w, x); else if (x >= w->scrollable.pos_x + w->scrollable.shown_x) ScrollableSetHPos((Widget)w, x - w->scrollable.shown_x + 1); if (y < w->scrollable.pos_y) ScrollableSetVPos((Widget)w, y); else if (y >= w->scrollable.pos_y + w->scrollable.shown_y) ScrollableSetVPos((Widget)w, y - w->scrollable.shown_y + 1); } static void order_sel(TextFieldWidget w) { long tmp; #define SWAP(a, b) (tmp = (a), (a) = (b), (b) = tmp) if (w->textfield.sel_start_y == w->textfield.sel_stop_y && w->textfield.sel_start_x > w->textfield.sel_stop_x) SWAP(w->textfield.sel_start_x, w->textfield.sel_stop_x); else if (w->textfield.sel_start_y > w->textfield.sel_stop_y) { SWAP(w->textfield.sel_start_x, w->textfield.sel_stop_x); SWAP(w->textfield.sel_start_y, w->textfield.sel_stop_y); } #undef SWAP } static void invalidate_selection(TextFieldWidget w) { long start = w->textfield.sel_start_y; long stop = w->textfield.sel_stop_y; long width = w->scrollable.width; if (w->textfield.sel_set) { w->textfield.sel_set = False; draw_rectangle(w, 0, start, width, stop - start + 1, True); } if (w->textfield.curr_sel != None) { XtDisownSelection((Widget)w, w->textfield.curr_sel, w->textfield.sel_time); w->textfield.curr_sel = None; } } static void change_pos(TextFieldWidget w, long caret_x, long caret_y) { long n; w->textfield.waiting_for_sel = False; w->textfield.multiply = 1; if (w->scrollable.height <= 0) return; if (caret_y < 0) caret_y = 0; if (caret_y >= w->scrollable.height) caret_y = w->scrollable.height - 1; if (caret_x < 0) caret_x = 0; n = strlen(w->textfield.lines[caret_y].buf); if (caret_x > n) caret_x = n; if (w->textfield.sel_set) { long start_x = w->textfield.sel_start_x; long start_y = w->textfield.sel_start_y; long stop_x = w->textfield.sel_stop_x; long stop_y = w->textfield.sel_stop_y; long o_x = w->textfield.caret_x; long o_y = w->textfield.caret_y; int extend_end; extend_end = (o_x == stop_x && o_y == stop_y); if (!extend_end && (o_x != start_x || o_y != start_y)) /* wierd... */ invalidate_selection(w); else if (caret_y == o_y) { if (extend_end) w->textfield.sel_stop_x = caret_x; else w->textfield.sel_start_x = caret_x; order_sel(w); if (w->textfield.sel_start_x == start_x) { long diff = w->textfield.sel_stop_x - stop_x; if (diff < 0) draw_rectangle(w, w->textfield.sel_stop_x, o_y, - diff, 1, True); else if (diff > 0) draw_rectangle(w, stop_x, o_y, diff, 1, False); } else if (w->textfield.sel_stop_x == stop_x) { long diff = w->textfield.sel_start_x - start_x; if (diff > 0) draw_rectangle(w, start_x, o_y, diff, 1, True); else if (diff < 0) draw_rectangle(w, w->textfield.sel_start_x, o_y, - diff, 1, False); } else { if (start_x > w->textfield.sel_start_x) start_x = w->textfield.sel_start_x; if (stop_x < w->textfield.sel_stop_x) stop_x = w->textfield.sel_stop_x; draw_rectangle(w, start_x, o_y, stop_x - start_x + 1, 1, True); } } else { long width = w->scrollable.width; if (extend_end) { w->textfield.sel_stop_x = caret_x; w->textfield.sel_stop_y = caret_y; } else { w->textfield.sel_start_x = caret_x; w->textfield.sel_start_y = caret_y; } order_sel(w); if (w->textfield.sel_start_x == start_x && w->textfield.sel_start_y == start_y) { long diff = w->textfield.sel_stop_y - stop_y; if (diff < 0) draw_rectangle(w, 0, w->textfield.sel_stop_y, width, - diff + 1, True); else if (diff > 0) draw_rectangle(w, 0, stop_y, width, diff + 1, False); } else if (w->textfield.sel_stop_x == stop_x && w->textfield.sel_stop_y == stop_y) { long diff = w->textfield.sel_start_y - start_y; if (diff > 0) draw_rectangle(w, 0, start_y, width, diff + 1, True); else if (diff < 0) draw_rectangle(w, 0, w->textfield.sel_start_y, width, - diff + 1, False); } else { if (start_y > w->textfield.sel_start_y) start_y = w->textfield.sel_start_y; if (stop_y < w->textfield.sel_stop_y) stop_y = w->textfield.sel_stop_y; draw_rectangle(w, 0, start_y, w->scrollable.width, stop_y - start_y + 1, True); } } } undraw_caret(w); w->textfield.caret_x = caret_x; w->textfield.caret_y = caret_y; draw_caret(w); make_visible(w, caret_x, caret_y); } static void merge_line_with_next(TextFieldWidget w, long line) { char *c; long pos, len, n; if (line < 0 || line >= w->scrollable.height - 1) return; n = --w->scrollable.height; c = w->textfield.lines[line + 1].buf; if (line < n) memmove(w->textfield.lines + line + 1, w->textfield.lines + line + 2, (n - line) * sizeof w->textfield.lines[0]); ScrollableFitVBar((ScrollableWidget)w); pos = strlen(w->textfield.lines[line].buf); len = strlen(c); sync_width(w, pos, pos + len); if (pos + len + 8 < w->textfield.lines[line].len) w->textfield.lines[line].buf = XtRealloc(w->textfield.lines[line].buf, (w->textfield.lines[line].len = pos + len + 8)); strcpy(w->textfield.lines[line].buf + pos, c); XtFree(c); draw_rectangle(w, pos, line, len, 1, False); draw_rectangle(w, 0, line + 1, w->scrollable.width, w->scrollable.height, True); change_pos(w, pos, line); } /*************************************************************************/ static void aquire_focus(Widget gw, XEvent *event, String *params, Cardinal *no_params) { TextFieldWidget w = (TextFieldWidget)gw; XtCallbackList c_list = w->textfield.focus_callback; if (!w->textfield.focus_root || !w->textfield.active) return; XtSetKeyboardFocus(w->textfield.focus_root, (Widget)w); if (c_list) XtCallCallbackList((Widget)w, c_list, NULL); } static void nop(Widget gw, XEvent *event, String *params, Cardinal *no_params) { TextFieldWidget w = (TextFieldWidget)gw; w->textfield.multiply = 1; w->textfield.waiting_for_sel = False; } static void multiply(Widget gw, XEvent *event, String *params, Cardinal *no_params) { TextFieldWidget w = (TextFieldWidget)gw; long factor = *no_params == 1 ? atol(params[0]) : 4; long new_mult = w->textfield.multiply * factor; if (new_mult <= 0 || new_mult > 16384) new_mult = 1; w->textfield.multiply = new_mult; w->textfield.waiting_for_sel = False; } static void beginning_of_line(Widget gw, XEvent *event, String *params, Cardinal *no_params) { TextFieldWidget w = (TextFieldWidget)gw; change_pos(w, 0, w->textfield.caret_y); } static void end_of_line(Widget gw, XEvent *event, String *params, Cardinal *no_params) { TextFieldWidget w = (TextFieldWidget)gw; change_pos(w, w->scrollable.width, w->textfield.caret_y); } static void home(Widget gw, XEvent *event, String *params, Cardinal *no_params) { TextFieldWidget w = (TextFieldWidget)gw; change_pos(w, 0, 0); } static void end(Widget gw, XEvent *event, String *params, Cardinal *no_params) { TextFieldWidget w = (TextFieldWidget)gw; change_pos(w, w->scrollable.width, w->scrollable.height); } static void left(Widget gw, XEvent *event, String *params, Cardinal *no_params) { TextFieldWidget w = (TextFieldWidget)gw; change_pos(w, w->textfield.caret_x - w->textfield.multiply, w->textfield.caret_y); } static void right(Widget gw, XEvent *event, String *params, Cardinal *no_params) { TextFieldWidget w = (TextFieldWidget)gw; change_pos(w, w->textfield.caret_x + w->textfield.multiply, w->textfield.caret_y); } static void up(Widget gw, XEvent *event, String *params, Cardinal *no_params) { TextFieldWidget w = (TextFieldWidget)gw; change_pos(w, w->textfield.caret_x, w->textfield.caret_y - w->textfield.multiply); } static void down(Widget gw, XEvent *event, String *params, Cardinal *no_params) { TextFieldWidget w = (TextFieldWidget)gw; change_pos(w, w->textfield.caret_x, w->textfield.caret_y + w->textfield.multiply); } static void page(Widget gw, XEvent *event, String *params, Cardinal *no_params) { TextFieldWidget w = (TextFieldWidget)gw; float amount; if (*no_params == 1 && sscanf(params[0], "%f", &amount) == 1) change_pos(w, w->textfield.caret_x, w->textfield.caret_y + w->scrollable.shown_y * amount * w->textfield.multiply); } static void delete_next(Widget gw, XEvent *event, String *params, Cardinal *no_params) { TextFieldWidget w = (TextFieldWidget)gw; long m = w->textfield.multiply; long line = w->textfield.caret_y; long pos = w->textfield.caret_x; char *c; long n; w->textfield.waiting_for_sel = False; w->textfield.multiply = 1; if (w->textfield.sel_set) invalidate_selection(w); if (pos < 0 || line < 0 || line >= w->scrollable.height) return; c = w->textfield.lines[line].buf; n = strlen(c); if (pos >= n) merge_line_with_next(w, line); else { clear_segment(w, line, pos, n); if (m > n - pos) m = n - pos; n -= m; memmove(c + pos, c + pos + m, n - pos + 1); draw_rectangle(w, pos, line, n - pos, 1, False); draw_caret(w); sync_width(w, n + m, n); } } static void delete_previous(Widget gw, XEvent *event, String *params, Cardinal *no_params) { TextFieldWidget w = (TextFieldWidget)gw; long m = w->textfield.multiply; long line = w->textfield.caret_y; long pos = w->textfield.caret_x; char *c; long n; w->textfield.waiting_for_sel = False; w->textfield.multiply = 1; if (w->textfield.sel_set) invalidate_selection(w); if (pos < 0 || line < 0 || line >= w->scrollable.height) return; c = w->textfield.lines[line].buf; n = strlen(c); if (pos > n) return; if (pos == 0) merge_line_with_next(w, line - 1); else { if (m > pos) m = pos; clear_segment(w, line, pos - m, n); memmove(c + pos - m, c + pos, n - pos); c[n - m] = '\0'; draw_rectangle(w, pos - m, line, n - pos + 1, 1, False); change_pos(w, pos - m, w->textfield.caret_y); sync_width(w, n, n - m); } } static void kill_action(Widget gw, XEvent *event, String *params, Cardinal *no_params) { TextFieldWidget w = (TextFieldWidget)gw; long line = w->textfield.caret_y; long pos = w->textfield.caret_x; char *c; long n; w->textfield.waiting_for_sel = False; w->textfield.multiply = 1; if (w->textfield.sel_set) invalidate_selection(w); if (line < 0 || line >= w->scrollable.height) return; c = w->textfield.lines[line].buf; n = strlen(c); if (pos >= n) merge_line_with_next(w, line); else { c[pos] = '\0'; clear_segment(w, line, pos, n - pos); draw_caret(w); sync_width(w, n, pos); } } static void redraw(Widget gw, XEvent *event, String *params, Cardinal *no_params) { TextFieldWidget w = (TextFieldWidget)gw; XClearArea(XtDisplay(w), XtWindow(w), 0, 0, 0, 0, True); w->textfield.waiting_for_sel = False; w->textfield.multiply = 1; } static void enter(Widget gw, XEvent *event, String *params, Cardinal *no_params) { TextFieldWidget w = (TextFieldWidget)gw; long m = w->textfield.multiply; long line = w->textfield.caret_y; long pos = w->textfield.caret_x; char *c; long len, n; w->textfield.waiting_for_sel = False; w->textfield.multiply = 1; if (w->textfield.sel_set) invalidate_selection(w); call_callbacks(w, w->textfield.callback); if (w->textfield.single_line || line < 0 || line >= w->scrollable.height) return; open_lines(w, line, m); c = w->textfield.lines[line + m].buf; len = strlen(c); if (pos > len) pos = len; w->textfield.lines[line].len = pos + 1; XtFree(w->textfield.lines[line].buf); w->textfield.lines[line].buf = memcpy(XtMalloc(pos + 1), c, pos); w->textfield.lines[line].buf[pos] = '\0'; n = len - pos; if (n > 0) memmove(c, c + pos, n); c[n] = '\0'; if (n < pos) n = pos; draw_rectangle(w, 0, line, w->scrollable.width, w->scrollable.height, True); sync_width(w, len, n); /* Don't sync before clear... */ change_pos(w, 0, line + m); } static void tab(Widget gw, XEvent *event, String *params, Cardinal *no_params) { TextFieldWidget w = (TextFieldWidget)gw; w->textfield.waiting_for_sel = False; w->textfield.multiply = 1; call_callbacks(w, w->textfield.tab_callback); } static void transpose(Widget gw, XEvent *event, String *params, Cardinal *no_params) { TextFieldWidget w = (TextFieldWidget)gw; long line = w->textfield.caret_y; long pos = w->textfield.caret_x; long m = w->textfield.multiply; char *c; long len; int ch; w->textfield.waiting_for_sel = False; w->textfield.multiply = 1; if (w->textfield.sel_set) invalidate_selection(w); if (line < 0 || line >= w->scrollable.height) return; c = w->textfield.lines[line].buf; len = strlen(c); if (pos <= 0 || pos >= len) return; if (pos + m >= len) m = len - pos; ch = c[pos - 1]; memmove(c + pos - 1, c + pos, m); c[pos - 1 + m] = ch; draw_rectangle(w, pos - 1, line, m + 1, 1, True); change_pos(w, pos + m, line); } static void set_border_color(Widget gw, XEvent *event, String *params, Cardinal *no_params) { TextFieldWidget w = (TextFieldWidget)gw; Arg arg; if (*no_params != 1) { XBell(XtDisplay(w), 0); return; } if (w->textfield.print_focus && (event->type == FocusIn || event->type == FocusOut)) { fprintf(stderr, "Focus%s : %s\n" " serial: %ld\n" " send_event: %s\n" " window: 0x%lx\n" " mode: ", event->type == FocusIn ? "In " : "Out", XtName((Widget)w), event->xfocus.serial, event->xfocus.send_event ? "True" : "False", event->xfocus.window); switch (event->xfocus.mode) { case NotifyNormal: fputs("NotifyNormal\n", stderr); break; case NotifyGrab: fputs("NotifyGrab\n", stderr); break; case NotifyUngrab: fputs("NotifyUngrab\n", stderr); break; default: fputc('\n', stderr); break; } fputs(" detail: ", stderr); switch (event->xfocus.detail) { case NotifyAncestor: fputs("NotifyAncestor\n", stderr); break; case NotifyVirtual: fputs("NotifyVirtual\n", stderr); break; case NotifyInferior: fputs("NotifyInferior\n", stderr); break; case NotifyNonlinear: fputs("NotifyNonlinear\n", stderr); break; case NotifyNonlinearVirtual: fputs("NotifyNonlinearVirtual\n", stderr); break; case NotifyPointer: fputs("NotifyPointer\n", stderr); break; case NotifyPointerRoot: fputs("NotifyPointerRoot\n", stderr); break; case NotifyDetailNone: fputs("NotifyDetailNone\n", stderr); break; default: fputc('\n', stderr); break; } } if (w->textfield.focus_hack && event->type == FocusIn && !event->xfocus.send_event) return; if (strlen(params[0]) < 15) { char buffer[16]; char *c; long i; c = params[0]; i = 0; do { buffer[i++] = isupper((unsigned char)*c) ? tolower((unsigned char)*c) : *c; } while (*c++ != '\0'); if (strcmp(buffer, XtNbackground) == 0) { XtSetArg(arg, XtNborderColor, w->core.background_pixel); XtSetValues((Widget)w, &arg, 1); return; } else if (strcmp(buffer, XtNforeground) == 0) { XtSetArg(arg, XtNborderColor, w->textfield.fg_pixel); XtSetValues((Widget)w, &arg, 1); return; } else if (strcmp(buffer, "focuscolor") == 0) { XtSetArg(arg, XtNborderColor, w->textfield.focus_pixel); XtSetValues((Widget)w, &arg, 1); return; } } XBell(XtDisplay(w), 0); } static void insert(Widget gw, XEvent *event, String *params, Cardinal *no_params) { TextFieldWidget w = (TextFieldWidget)gw; char buffer[16]; String param; Cardinal n_params; KeySym keysym; long len; if (!event || (event->type != KeyPress && event->type != KeyRelease)) return; len = XLookupString(&event->xkey, buffer, sizeof buffer, &keysym, NULL); if (len <= 0) return; buffer[len] = '\0'; param = buffer; n_params = 1; insert_string((Widget)w, event, ¶m, &n_params); } static void insert_string(Widget gw, XEvent *event, String *params, Cardinal *no_params) { TextFieldWidget w = (TextFieldWidget)gw; char *c; long m = w->textfield.multiply; long line = w->textfield.caret_y; long pos = w->textfield.caret_x; long n_pos, n, len; w->textfield.waiting_for_sel = False; w->textfield.multiply = 1; if (w->textfield.sel_set) invalidate_selection(w); if (*no_params != 1 || line < 0 || line >= w->scrollable.height) return; n = strlen(params[0]); if (n <= 0 || XTextWidth(w->textfield.font, params[0], n) <= 0) return; len = strlen(w->textfield.lines[line].buf); if (pos > len) pos = len; n_pos = pos + m * n; if (len + m * n + 8 > w->textfield.lines[line].len) w->textfield.lines[line].buf = XtRealloc(w->textfield.lines[line].buf, (w->textfield.lines[line].len = len + m * n + 8)); sync_width(w, len, len + m * n); c = w->textfield.lines[line].buf + pos; memmove(c + m * n, c, len - pos + 1); while (m-- > 0) { memcpy(c, params[0], n); c += n; } draw_rectangle(w, pos, line, w->scrollable.width, 1, True); change_pos(w, n_pos, w->textfield.caret_y); } static void swap_select(Widget gw, XEvent *event, String *params, Cardinal *no_params) { TextFieldWidget w = (TextFieldWidget)gw; Atom atom = XA_PRIMARY; Time time = get_event_time(event); long o_caret_x = w->textfield.caret_x; long o_caret_y = w->textfield.caret_y; w->textfield.waiting_for_sel = False; w->textfield.multiply = 1; if (w->textfield.sel_set) invalidate_selection(w); change_pos(w, w->textfield.mark_x, w->textfield.mark_y); w->textfield.sel_start_x = w->textfield.caret_x; w->textfield.sel_start_y = w->textfield.caret_y; w->textfield.mark_x = w->textfield.sel_stop_x = o_caret_x; w->textfield.mark_y = w->textfield.sel_stop_y = o_caret_y; order_sel(w); if (*no_params > 0 && strcmp(params[0], "PRIMARY") != 0) atom = intern_atom(XtDisplay(w), params[0]); if (XtOwnSelection((Widget)w, atom, time, convert_sel_proc, lose_sel_proc, NULL)) { long y, height; w->textfield.curr_sel = atom; w->textfield.sel_time = time; w->textfield.sel_set = True; height = w->textfield.sel_stop_y - w->textfield.sel_start_y + 1; y = w->textfield.sel_start_y; undraw_caret(w); draw_rectangle(w, 0, y, w->scrollable.width, height, False); } } static void select_start(Widget gw, XEvent *event, String *params, Cardinal *no_params) { TextFieldWidget w = (TextFieldWidget)gw; long x, y; w->textfield.waiting_for_sel = False; w->textfield.multiply = 1; if (w->textfield.sel_set) invalidate_selection(w); if (event->type == ButtonPress || event->type == ButtonRelease) { if (!event_to_pos(w, event, &x, &y)) return; change_pos(w, x, y); } w->textfield.sel_start_x = w->textfield.sel_stop_x = w->textfield.mark_x = w->textfield.caret_x; w->textfield.sel_start_y = w->textfield.sel_stop_y = w->textfield.mark_y = w->textfield.caret_y; w->textfield.sel_set = True; } static void extend_start(Widget gw, XEvent *event, String *params, Cardinal *no_params) { TextFieldWidget w = (TextFieldWidget)gw; long x, y, n; int extend_end; if (w->scrollable.height <= 0) return; w->textfield.waiting_for_sel = False; w->textfield.multiply = 1; if (!w->textfield.sel_set) { select_start((Widget)w, event, params, no_params); return; } if (!event_to_pos(w, event, &x, &y)) return; if (w->textfield.sel_start_y == w->textfield.sel_stop_y) if (y < w->textfield.sel_start_y) extend_end = False; else if (y > w->textfield.sel_start_y) extend_end = True; else if (2 * x < w->textfield.sel_start_x + w->textfield.sel_stop_x) extend_end = False; else extend_end = True; else if (y <= w->textfield.sel_start_y) extend_end = False; else if (y >= w->textfield.sel_stop_y) extend_end = True; else if (2 * y < w->textfield.sel_start_y + w->textfield.sel_stop_y) extend_end = False; else extend_end = True; if (y >= w->scrollable.height) y = w->scrollable.height - 1; if (y < 0) y = 0; n = strlen(w->textfield.lines[y].buf); if (x > n) x = n; if (x < 0) x = 0; invalidate_selection(w); undraw_caret(w); if (extend_end) { w->textfield.sel_stop_x = x; w->textfield.sel_stop_y = y; } else { w->textfield.sel_start_x = x; w->textfield.sel_start_y = y; } w->textfield.sel_set = True; w->textfield.caret_x = x; w->textfield.caret_y = y; y = w->textfield.sel_start_y; n = w->textfield.sel_stop_y - y + 1; draw_rectangle(w, 0, y, w->scrollable.width, n, True); draw_caret(w); } static void select_extend(Widget gw, XEvent *event, String *params, Cardinal *no_params) { TextFieldWidget w = (TextFieldWidget)gw; long x, y; w->textfield.waiting_for_sel = False; w->textfield.multiply = 1; if (!w->textfield.sel_set) return; if (!event_to_pos(w, event, &x, &y)) return; change_pos(w, x, y); } static void select_end(Widget gw, XEvent *event, String *params, Cardinal *no_params) { TextFieldWidget w = (TextFieldWidget)gw; Atom atom = XA_PRIMARY; Time time = get_event_time(event); w->textfield.waiting_for_sel = False; w->textfield.multiply = 1; if (!w->textfield.sel_set) return; if (w->textfield.curr_sel != None) XtDisownSelection((Widget)w, w->textfield.curr_sel, w->textfield.sel_time); w->textfield.curr_sel = None; if (w->textfield.sel_start_y == w->textfield.sel_stop_y && w->textfield.sel_start_x == w->textfield.sel_stop_x) return; if (*no_params > 0 && strcmp(params[0], "PRIMARY") != 0) atom = intern_atom(XtDisplay(w), params[0]); if (!XtOwnSelection((Widget)w, atom, time, convert_sel_proc, lose_sel_proc, NULL)) { invalidate_selection(w); return; } w->textfield.curr_sel = atom; w->textfield.sel_time = time; } static void kill_selection(Widget gw, XEvent *event, String *params, Cardinal *no_params) { TextFieldWidget w = (TextFieldWidget)gw; Display *disp = XtDisplay(w); Atom atom = XA_PRIMARY; Time time = get_event_time(event); w->textfield.multiply = 1; if (*no_params > 0 && strcmp(params[0], "PRIMARY") != 0) atom = intern_atom(disp, params[0]); w->textfield.waiting_for_sel = True; XtGetSelectionValue((Widget)w, atom, intern_atom(disp, "DELETE"), delete_sel_callback, NULL, time); } static void insert_selection(Widget gw, XEvent *event, String *params, Cardinal *no_params) { TextFieldWidget w = (TextFieldWidget)gw; Display *disp = XtDisplay(w); Atom atom = XA_PRIMARY; Time time = get_event_time(event); long x, y; w->textfield.waiting_for_sel = False; w->textfield.multiply = 1; if (!event_to_pos(w, event, &x, &y)) return; change_pos(w, x, y); w->textfield.waiting_for_sel = True; if (*no_params > 0 && strcmp(params[0], "PRIMARY") != 0) atom = intern_atom(disp, params[0]); XtGetSelectionValue((Widget)w, atom, XA_STRING, insert_sel_callback, NULL, time); } static void disown_selection(Widget gw, XEvent *event, String *params, Cardinal *no_params) { TextFieldWidget w = (TextFieldWidget)gw; w->textfield.waiting_for_sel = False; w->textfield.multiply = 1; if (w->textfield.sel_set) invalidate_selection(w); draw_rectangle(w, w->scrollable.pos_x, w->scrollable.pos_y, w->scrollable.shown_x, w->scrollable.shown_y, True); draw_caret(w); } static void display_caret(Widget gw, XEvent *event, String *params, Cardinal *no_params) { TextFieldWidget w = (TextFieldWidget)gw; w->textfield.waiting_for_sel = False; w->textfield.multiply = 1; if (w->textfield.focus_hack && event->type == FocusIn && !event->xfocus.send_event) return; if (*no_params == 1 && strlen(params[0]) < 15) { char buffer[16]; char *c; long i; c = params[0]; i = 0; do { buffer[i++] = isupper((unsigned char)*c) ? tolower((unsigned char)*c) : *c; } while (*c++ != '\0'); if (strcmp(buffer, "on") == 0 || strcmp(buffer, "true") == 0) { w->textfield.display_caret = True; draw_caret(w); return; } else if (strcmp(buffer, "off") == 0 || strcmp(buffer, "false") == 0) { undraw_caret(w); w->textfield.display_caret = False; return; } } XBell(XtDisplay(w), 0); } /*************************************************************************/ static void Initialize(Widget grequest, Widget gnew, ArgList args, Cardinal *no_args) { TextFieldWidget new = (TextFieldWidget)gnew; Dimension width, height; init_gcs(new); get_char_sizes(new); get_preferred_sizes(new, &width, &height); if (new->core.width == 0) new->core.width = width; if (new->core.height == 0) new->core.height = height; calc_shown(new); new->textfield.waiting_for_sel = False; new->textfield.active = True; new->textfield.sel_set = False; new->textfield.curr_sel = None; new->textfield.sel_time = 0; new->textfield.sel_start_x = 0; new->textfield.sel_start_y = 0; new->textfield.sel_stop_x = 0; new->textfield.sel_stop_y = 0; new->textfield.multiply = 1; new->textfield.caret_x = 0; new->textfield.caret_y = 0; new->textfield.mark_x = 0; new->textfield.mark_y = 0; new->textfield.lines = NULL; new->textfield.n_lines = 0; lines_from_buffer(new); if (new->textfield.single_line) new->textfield.pref_lines = 1; new->core.border_pixel = new->core.background_pixel; } static void Destroy(Widget gw) { TextFieldWidget w = (TextFieldWidget)gw; if (w->textfield.curr_sel != None) XtDisownSelection((Widget)w, w->textfield.curr_sel, w->textfield.sel_time); w->textfield.curr_sel = None; free_lines(w); free_gcs(w); } static void Resize(Widget gw) { TextFieldWidget w = (TextFieldWidget)gw; calc_shown(w); if (!XtIsRealized((Widget)w)) return; make_visible(w, w->textfield.caret_x, w->textfield.caret_y); ScrollableFitHBar((ScrollableWidget)w); ScrollableFitVBar((ScrollableWidget)w); } static void SetHPos(ScrollableWidget gw, long pos_x) { TextFieldWidget w = (TextFieldWidget)gw; Display *disp = XtDisplay(w); Window win = XtWindow(w); long old = w->scrollable.pos_x; long pos_y = w->scrollable.pos_y; long shown_x = w->scrollable.shown_x; long shown_y = w->scrollable.shown_y; long diff; if (pos_x > w->scrollable.width - shown_x) pos_x = w->scrollable.width - shown_x; if (pos_x < 0) pos_x = 0; diff = pos_x - old; if (diff == 0) return; undraw_caret(w); w->scrollable.pos_x = pos_x; if (diff <= - shown_x || diff >= shown_x) draw_rectangle(w, pos_x, pos_y, shown_x, shown_y, True); else { long y0 = w->shadow.shadow_width + w->textfield.internal_height; long x0 = w->shadow.shadow_width + w->textfield.internal_width; long height = (long)w->core.height - 2 * y0; long width, xdiff; GC gc = w->textfield.gc; if (height <= 0) return; if (diff < 0) { diff = -diff; width = (shown_x - diff) * w->textfield.char_w; xdiff = diff * w->textfield.char_w; XCopyArea(disp, win, win, gc, x0, y0, width, height, x0 + xdiff, y0); XClearArea(disp, win, x0, y0, xdiff, height, False); draw_rectangle(w, pos_x, pos_y, diff, shown_y, False); } else { width = (shown_x - diff) * w->textfield.char_w; xdiff = diff * w->textfield.char_w; XCopyArea(disp, win, win, gc, x0 + xdiff, y0, width, height, x0, y0); XClearArea(disp, win, x0 + width, y0, xdiff, height, False); draw_rectangle(w, pos_x + shown_x - diff, pos_y, diff, shown_y, False); } } draw_caret(w); } static void SetVPos(ScrollableWidget gw, long pos_y) { TextFieldWidget w = (TextFieldWidget)gw; Display *disp = XtDisplay(w); Window win = XtWindow(w); long old = w->scrollable.pos_y; long pos_x = w->scrollable.pos_x; long shown_x = w->scrollable.shown_x; long shown_y = w->scrollable.shown_y; long diff; if (pos_y > w->scrollable.height - shown_y) pos_y = w->scrollable.height - shown_y; if (pos_y < 0) pos_y = 0; diff = pos_y - old; if (diff == 0) return; undraw_caret(w); w->scrollable.pos_y = pos_y; if (diff <= - shown_y || diff >= shown_y) draw_rectangle(w, pos_x, pos_y, shown_x, shown_y, True); else { long y0 = w->shadow.shadow_width + w->textfield.internal_height; long x0 = w->shadow.shadow_width + w->textfield.internal_width; long width = (long)w->core.width - 2 * x0; long height, ydiff; GC gc = w->textfield.gc; if (width <= 0) return; if (diff < 0) { diff = -diff; height = (shown_y - diff) * w->textfield.char_h; ydiff = diff * w->textfield.char_h; XCopyArea(disp, win, win, gc, x0, y0, width, height, x0, y0 + ydiff); XClearArea(disp, win, x0, y0, width, ydiff, False); draw_rectangle(w, pos_x, pos_y, shown_x, diff, False); } else { height = (shown_y - diff) * w->textfield.char_h; ydiff = diff * w->textfield.char_h; XCopyArea(disp, win, win, gc, x0, y0 + ydiff, width, height, x0, y0); XClearArea(disp, win, x0, y0 + height, width, ydiff, False); draw_rectangle(w, pos_x, pos_y + shown_y - diff, shown_x, diff, False); } } draw_caret(w); } static void Realize(Widget gw, XtValueMask *mask, XSetWindowAttributes *attributes) { TextFieldWidget w = (TextFieldWidget)gw; calc_shown(w); if (w->textfield.single_line) { w->scrollable.pos_x = w->scrollable.width - w->scrollable.shown_x; if (w->scrollable.pos_x < 0) w->scrollable.pos_x = 0; } scrollableWidgetClass->core_class.realize((Widget)w, mask, attributes); } static void Redisplay(Widget gw, XEvent *event, Region region) { TextFieldWidget w = (TextFieldWidget)gw; int x, y, width, height; if (event) switch (event->type) { case Expose: x = event->xexpose.x; y = event->xexpose.y; width = event->xexpose.width; height = event->xexpose.height; break; case GraphicsExpose: x = event->xgraphicsexpose.x; y = event->xgraphicsexpose.y; width = event->xgraphicsexpose.width; height = event->xgraphicsexpose.height; break; default: return; } else { x = 0; y = 0; width = w->core.width; height = w->core.height; } ShadowDrawShadows((ShadowWidget)w, 0, 0, w->core.width, w->core.height, w->textfield.border_in); draw_pixels(w, x, y, width, height, False); draw_caret(w); } static Boolean SetValues(Widget gcurrent, Widget grequest, Widget gnew, ArgList args, Cardinal *num_args) { TextFieldWidget new = (TextFieldWidget)gnew; TextFieldWidget current = (TextFieldWidget)gcurrent; Boolean redisplay = False; if (new->textfield.single_line) new->textfield.pref_lines = 1; if (new->textfield.font != current->textfield.font || new->core.background_pixel != current->core.background_pixel || new->textfield.fg_pixel != current->textfield.fg_pixel || new->textfield.highlight_fg != current->textfield.highlight_fg || new->textfield.highlight_bg != current->textfield.highlight_bg) { free_gcs(new); init_gcs(new); redisplay = True; } if (new->textfield.buffer) { if (new->textfield.sel_set) invalidate_selection(new); lines_from_buffer(new); new->textfield.curr_sel = None; new->textfield.waiting_for_sel = False; ScrollableFitHBar((ScrollableWidget)new); ScrollableFitVBar((ScrollableWidget)new); redisplay = True; } if (new->textfield.font != current->textfield.font) get_char_sizes(new); if (new->textfield.font != current->textfield.font || new->textfield.internal_width != current->textfield.internal_width || new->textfield.internal_height != current->textfield.internal_height) { Dimension width, height; get_preferred_sizes(new, &width, &height); new->core.width = width; new->core.height = height; redisplay = True; } return redisplay; } static XtGeometryResult QueryGeometry(Widget gw, XtWidgetGeometry *intended, XtWidgetGeometry *preferred) { TextFieldWidget w = (TextFieldWidget)gw; Dimension width, height; Dimension intended_width, intended_height; get_preferred_sizes(w, &width, &height); preferred->request_mode = CWWidth | CWHeight; preferred->width = width; preferred->height = height; if (intended->request_mode & CWWidth) intended_width = intended->width; else intended_width = w->core.width; if (intended->request_mode & CWHeight) intended_height = intended->height; else intended_height = w->core.height; if (width == w->core.width && height == w->core.height) return XtGeometryNo; if (width == intended_width && height == intended_height) return XtGeometryYes; return XtGeometryAlmost; } /*************************************************************************/ void TextFieldSetActive(Widget gw, int active) { TextFieldWidget w = (TextFieldWidget)gw; w->textfield.active = active; } void TextFieldSetBuffer(Widget gw, char *buffer) { TextFieldWidget w = (TextFieldWidget)gw; if (w->textfield.sel_set) invalidate_selection(w); w->textfield.buffer = buffer; lines_from_buffer(w); ScrollableFitHBar((ScrollableWidget)w); ScrollableFitVBar((ScrollableWidget)w); XClearWindow(XtDisplay(w), XtWindow(w)); Redisplay((Widget)w, NULL, NULL); } char *TextFieldGetBuffer(Widget gw) { TextFieldWidget w = (TextFieldWidget)gw; char *buffer; long i, n; if (w->scrollable.height <= 0) return XtNewString(""); n = w->scrollable.height + 3; for (i = 0 ; i < w->scrollable.height ; i++) n += strlen(w->textfield.lines[i].buf); buffer = XtMalloc(n); strcpy(buffer, w->textfield.lines[0].buf); n = strlen(buffer); for (i = 1 ; i < w->scrollable.height ; i++) { buffer[n++] = '\n'; strcpy(buffer + n, w->textfield.lines[i].buf); n += strlen(buffer + n); } buffer[n] = '\0'; return buffer; } /*************************************************************************/ static long get_sel_len(TextFieldWidget w) { long start = w->textfield.sel_start_y; long stop = w->textfield.sel_stop_y; long len, n; if (w->textfield.curr_sel == None) return 0; if (start < 0 || start >= w->scrollable.height || stop < 0 || stop >= w->scrollable.height) return -1; if (start == stop) return w->textfield.sel_stop_x - w->textfield.sel_start_x; len = strlen(w->textfield.lines[start].buf) - w->textfield.sel_start_x + 1; for (n = start + 1 ; n < stop ; n++) len += strlen(w->textfield.lines[n].buf) + 1; len += w->textfield.sel_stop_x; return len; } static long get_sel(TextFieldWidget w, char *buffer) { long start = w->textfield.sel_start_y; long stop = w->textfield.sel_stop_y; long n, len; char *c; if (w->textfield.curr_sel == None || start < 0 || stop < 0 || start >= w->scrollable.height || stop >= w->scrollable.height) return -1; c = w->textfield.lines[start].buf; n = strlen(c); if (start == stop) { start = w->textfield.sel_start_x; stop = w->textfield.sel_stop_x; if (start > n) return -1; if (stop > n) stop = n; len = stop - start; if (len > 0) memcpy(buffer, c + start, len); buffer[len] = '\0'; return len; } if (w->textfield.sel_start_x > n) len = 0; else { strcpy(buffer, c + w->textfield.sel_start_x); len = strlen(buffer); } buffer[len++] = '\n'; for (n = start + 1 ; n < stop ; n++) { strcpy(buffer + len, w->textfield.lines[n].buf); len += strlen(buffer + len); buffer[len++] = '\n'; } c = w->textfield.lines[stop].buf; n = strlen(c); stop = w->textfield.sel_stop_x; if (stop > n) stop = n; if (stop > 0) memcpy(buffer + len, c, stop); len += stop; buffer[len] = '\0'; return len; } /*************************************************************************/ static void delete_sel_callback(Widget gw, XtPointer client_data, Atom *selection, Atom *type, XtPointer value, unsigned long *len, int *format) { TextFieldWidget w = (TextFieldWidget)gw; w->textfield.waiting_for_sel = False; XtFree((char *)value); } static void insert_sel_callback(Widget gw, XtPointer client_data, Atom *selection, Atom *type, XtPointer val, unsigned long *lenp, int *format) { TextFieldWidget w = (TextFieldWidget)gw; char *value = (char *)val; char *c, *p; long pos = w->textfield.caret_x; long line = w->textfield.caret_y; long len = *lenp; long m, n, i; int was_waiting = w->textfield.waiting_for_sel; if (w->textfield.sel_set) invalidate_selection(w); c = memchr(value, '\n', len); w->textfield.waiting_for_sel = False; if (!was_waiting || *type == XT_CONVERT_FAIL || !value || (c && w->textfield.single_line) || pos < 0 || line < 0 || line >= w->scrollable.height) { XtFree(value); return; } m = 0; p = c; while (p) { m++; p++; p = memchr(p, '\n', value + len - p); } if (m == 0) { n = strlen(w->textfield.lines[line].buf); if (n + len + 8 > w->textfield.lines[line].len) w->textfield.lines[line].buf = XtRealloc(w->textfield.lines[line].buf, (w->textfield.lines[line].len = n + len + 8)); if (pos > n) pos = n; p = w->textfield.lines[line].buf; memmove(p + pos + len, p + pos, n - pos + 1); memcpy(p + pos, value, len); sync_width(w, n, n + len); draw_rectangle(w, pos, line, w->scrollable.width, 1, True); change_pos(w, pos + len, line); } else { n = strlen(w->textfield.lines[line].buf); if (pos > n) pos = n; open_lines(w, line, m); n = c - value; XtFree(w->textfield.lines[line].buf); p = w->textfield.lines[line].buf = XtMalloc(w->textfield.lines[line].len = pos + n + 1); memcpy(p, w->textfield.lines[line + m].buf, pos); memcpy(p + pos, value, n); p[pos + n] = '\0'; c++; for (i = line + 1 ; i < line + m ; i++) { p = memchr(c, '\n', value + len - c); /* p != NULL */ n = p - c; XtFree(w->textfield.lines[i].buf); w->textfield.lines[i].buf = memcpy(XtMalloc(w->textfield.lines[i].len = n + 1), c, n); w->textfield.lines[i].buf[n] = '\0'; c = p + 1; } len = value + len - c; n = strlen(w->textfield.lines[line + m].buf) - pos; p = XtMalloc(w->textfield.lines[line + m].len = len + n + 1); memcpy(p, c, len); memcpy(p + len, w->textfield.lines[line + m].buf + pos, n); p[n + len] = '\0'; XtFree(w->textfield.lines[line + m].buf); w->textfield.lines[line + m].buf = p; draw_rectangle(w, 0, line, w->scrollable.width, w->scrollable.height, True); w->scrollable.width = max_width(w); ScrollableFitHBar((ScrollableWidget)w); change_pos(w, pos, line + m); } XtFree(value); } static int delete_selection(TextFieldWidget w) { long start = w->textfield.sel_start_y; long stop = w->textfield.sel_stop_y; long pos, len, n; char *c; if (!w->textfield.sel_set || w->textfield.curr_sel == None || (unsigned long)start >= w->scrollable.height || (unsigned long)stop >= w->scrollable.height) return False; invalidate_selection(w); if (start == stop) { pos = w->textfield.sel_start_x; c = w->textfield.lines[start].buf; n = strlen(c); if (pos > n) pos = n; len = w->textfield.sel_stop_x - pos; if (len > n - pos) len = n - pos; memmove(c + pos, c + pos + len, n - pos - len + 1); draw_rectangle(w, pos, start, w->scrollable.width, 1, True); sync_width(w, n, n - len); change_pos(w, pos, start); } else { /* * FIXME... (knews doesn't need it) */ fputs("TextField: Multiline DELETE not yet implemented.\n", stderr); return False; } return True; } static Boolean convert_sel_proc(Widget gw, Atom *sel, Atom *target, Atom *type, XtPointer *value, unsigned long *len, int *format) { TextFieldWidget w = (TextFieldWidget)gw; Display *disp = XtDisplay(w); if (w->textfield.curr_sel == None || *sel != w->textfield.curr_sel) return False; if (*target == XA_STRING || *target == intern_atom(disp, "TEXT")) { long length; char *buffer; length = get_sel_len(w); if (length < 0) return False; buffer = XtMalloc(length + 16); length = get_sel(w, buffer); if (length < 0) { XtFree(buffer); return False; } buffer[length] = '\0'; *len = length; *value = (XtPointer)buffer; *type = XA_STRING; *format = 8; return True; } if (*target == intern_atom(disp, "DELETE")) { if (!delete_selection(w)) return False; *value = NULL; *type = intern_atom(disp, "NULL"); *len = 0; *format = 32; return True; } if (*target == intern_atom(disp, "LENGTH")) { long *length = (long *)XtMalloc(sizeof(long)); *length = get_sel_len(w); *value = (XPointer)length; *type = XA_INTEGER; *len = 1; *format = 32; return True; } if (*target == intern_atom(disp, "TARGETS")) { Atom *std_targets, *atom; unsigned long std_length, n; if (!cvt_std_sel((Widget)w, w->textfield.sel_time, sel, target, type, (XPointer *)&std_targets, &std_length, format)) return False; *value = (XtPointer)XtMalloc((std_length + 8) * sizeof(Atom)); atom = (Atom *)*value; n = std_length; n++; *atom++ = XA_STRING; n++; *atom++ = intern_atom(disp, "TEXT"); n++; *atom++ = intern_atom(disp, "LENGTH"); n++; *atom++ = intern_atom(disp, "LIST_LENGTH"); n++; *atom++ = intern_atom(disp, "DELETE"); memcpy(atom, std_targets, std_length * sizeof(Atom)); XtFree((char *)std_targets); *len = n; *type = XA_ATOM; *format = 32; return True; } if (*target == intern_atom(disp, "LIST_LENGTH")) { long *length = (long *)XtMalloc(sizeof(long)); *length = 1; *value = (XPointer)length; *type = XA_INTEGER; *format = 32; return True; } return cvt_std_sel((Widget)w, w->textfield.sel_time, sel, target, type, (XPointer *)value, len, format); } static void lose_sel_proc(Widget gw, Atom *sel) { TextFieldWidget w = (TextFieldWidget)gw; w->textfield.curr_sel = None; if (w->textfield.sel_set) invalidate_selection(w); } ./knews-1.0b.1/Widgets/ArtTextP.h100644 1244 1244 4450 6455455534 15132 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #ifndef ArtTextP_h #define ArtTextP_h #include "ArtText.h" #include "ScrollableP.h" typedef struct { XtPointer extension; } ArtTextClassPart; typedef struct ArtTextClassRec { CoreClassPart core_class; ShadowClassPart shadow_class; ScrollableClassPart scrollable_class; ArtTextClassPart arttext_class; } ArtTextClassRec; extern ArtTextClassRec artTextClassRec; typedef enum { LineTypeString, LineTypeWString, LineTypeSeparator, LineTypeClickable, LineTypeImage } LineType; typedef struct { XtCallbackProc callback; void *client_data; } CallbackData; typedef union TSNode { struct { int type; union TSNode *next; } gen; struct { int type; union TSNode *next; Pixel pixel; XFontStruct *font; char *str; long len; } str; struct { int type; union TSNode *next; Pixel pixel; XFontStruct *font; XChar2b *str; long len; } wstr; struct { int type; union TSNode *next; Pixel pixel; XFontStruct *font; char *str; CallbackData *data; } cli; struct { int type; union TSNode *next; Pixel pixel; Dimension height; Dimension margin; } sep; struct { int type; union TSNode *next; Pixmap pixmap; Dimension width; Dimension height; CallbackData *data; } img; } TSNode; typedef struct { long y; TSNode *node; long start; long len; } TSTable; typedef struct { XFontStruct *font; Pixel highlight_pixel; XtCallbackList url_callback; Dimension margin; Dimension image_margin; Dimension separator_margin; Dimension preferred_lines; Dimension preferred_columns; Boolean wrap_lines; /* private data */ long first; long lines; long n_alloc; int max_width; TSNode *stream; TSNode *last; TSTable *table; GC gc; Font gc_fid; Pixel gc_fg; Atom curr_sel; Time sel_time; long sel_start_line; long sel_stop_line; int sel_start_offset; int sel_stop_offset; Boolean sel_ok; Boolean extending; Boolean extend_end; } ArtTextPart; typedef struct ArtTextRec { CorePart core; ShadowPart shadow; ScrollablePart scrollable; ArtTextPart arttext; } ArtTextRec; #endif /* ArtTextP_h */ ./knews-1.0b.1/Widgets/ArtTree.c100644 1244 1244 121464 6455455534 15025 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include #include #include "Compat.h" #include "Util.h" #include "ArtTreeP.h" static XtResource resources[] = { #define offset(field) XtOffsetOf(ArtTreeRec, arttree.field) /* {name, class, type, size, offset, default_type, default_addr}, */ {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel), offset(foreground_pixel), XtRString, XtDefaultForeground}, {XtNinnerColor, XtCForeground, XtRPixel, sizeof(Pixel), offset(inner_pixel), XtRString, XtDefaultForeground}, {XtNouterColor, XtCForeground, XtRPixel, sizeof(Pixel), offset(outer_pixel), XtRString, XtDefaultForeground}, {XtNrubberColor, XtCForeground, XtRPixel, sizeof(Pixel), offset(rubber_pixel), XtRString, XtDefaultForeground}, {XtNinnerDashed, XtCDashed, XtRBoolean, sizeof(Boolean), offset(inner_dashed), XtRImmediate, (XtPointer)False}, {XtNouterDashed, XtCDashed, XtRBoolean, sizeof(Boolean), offset(outer_dashed), XtRImmediate, (XtPointer)False}, {XtNfont, XtCFont, XtRFontStruct, sizeof(XFontStruct *), offset(font), XtRString, XtDefaultFont}, {XtNrowSpacing, XtCRowSpacing, XtRDimension, sizeof(Dimension), offset(row_spacing), XtRImmediate, (XtPointer)6}, {XtNcolumnSpacing, XtCColumnSpacing, XtRDimension, sizeof(Dimension), offset(column_spacing), XtRImmediate, (XtPointer)32}, {XtNinternalWidth, XtCInternalWidth, XtRDimension, sizeof(Dimension), offset(internal_width), XtRImmediate, (XtPointer)16}, {XtNinternalHeight, XtCInternalHeight, XtRDimension, sizeof(Dimension), offset(internal_height), XtRImmediate, (XtPointer)8}, {XtNinternalNodeWidth, XtCInternalNodeWidth, XtRDimension, sizeof(Dimension), offset(internal_node_width), XtRImmediate, (XtPointer)4}, {XtNinternalNodeHeight, XtCInternalNodeHeight, XtRDimension, sizeof(Dimension), offset(internal_node_height), XtRImmediate, (XtPointer)0}, {XtNnodeColumns, XtCNodeColumns, XtRDimension, sizeof(Dimension), offset(node_columns), XtRImmediate, (XtPointer)16}, {XtNnodeRows, XtCNodeRows, XtRDimension, sizeof(Dimension), offset(node_rows), XtRImmediate, (XtPointer)1}, {XtNpixmapWidth, XtCPixmapWidth, XtRDimension, sizeof(Dimension), offset(pixmap_width), XtRImmediate, (XtPointer)0}, {XtNpixmapHeight, XtCPixmapHeight, XtRDimension, sizeof(Dimension), offset(pixmap_height), XtRImmediate, (XtPointer)0}, {XtNpixmapSpacing, XtCPixmapSpacing, XtRDimension, sizeof(Dimension), offset(pixmap_spacing), XtRImmediate, (XtPointer)8}, {XtNdepthOne, XtCDepthOne, XtRBoolean, sizeof(Boolean), offset(depth_one), XtRImmediate, (XtPointer)True}, {XtNtree, XtCTree, XtRPointer, sizeof(ART_TREE_NODE*), offset(root), XtRImmediate, (XtPointer)NULL}, {XtNwarpPointer, XtCWarpPointer, XtRBoolean, sizeof(Boolean), offset(warp_pointer), XtRImmediate, (XtPointer)True}, {XtNvertical, XtCVertical, XtRBoolean, sizeof(Boolean), offset(vertical), XtRImmediate, (XtPointer)True}, {XtNinnerCallback, XtCCallback, XtRCallback, sizeof(XtCallbackList), offset(inner_callback), XtRCallback, (XtPointer)NULL}, {XtNouterCallback, XtCCallback, XtRCallback, sizeof(XtCallbackList), offset(outer_callback), XtRCallback, (XtPointer)NULL}, {XtNselectCallback, XtCCallback, XtRCallback, sizeof(XtCallbackList), offset(select_callback), XtRCallback, (XtPointer)NULL}, {XtNcallback, XtCCallback, XtRCallback, sizeof(XtCallbackList), offset(callback), XtRCallback, (XtPointer)NULL}, #undef offset }; static void Initialize(Widget, Widget, ArgList, Cardinal*); static void Destroy(Widget); static void Redisplay(Widget, XEvent*, Region); static void SuspendHook(ScrollableWidget); static Boolean SetValues(Widget, Widget, Widget, ArgList, Cardinal*); static void start_rubberband(Widget, XEvent*, String*, Cardinal*); static void move_rubberband(Widget, XEvent*, String*, Cardinal*); static void stop_rubberband(Widget, XEvent*, String*, Cardinal*); static void set_inner(Widget, XEvent*, String*, Cardinal*); static void reset_inner(Widget, XEvent*, String*, Cardinal*); static void toggle_inner(Widget, XEvent*, String*, Cardinal*); static void set_outer(Widget, XEvent*, String*, Cardinal*); static void reset_outer(Widget, XEvent*, String*, Cardinal*); static void toggle_outer(Widget, XEvent*, String*, Cardinal*); static void set_selected(Widget, XEvent*, String*, Cardinal*); static void reset_selected(Widget, XEvent*, String*, Cardinal*); static void toggle_selected(Widget, XEvent*, String*, Cardinal*); static void notify(Widget, XEvent*, String*, Cardinal*); static XtActionsRec actions[] = { {"start-rubberband", start_rubberband}, {"move-rubberband", move_rubberband}, {"stop-rubberband", stop_rubberband}, {"set-inner", set_inner}, {"reset-inner", reset_inner}, {"toggle-inner", toggle_inner}, {"set-outer", set_outer}, {"reset-outer", reset_outer}, {"toggle-outer", toggle_outer}, {"set-selected", set_selected}, {"reset-selected", reset_selected}, {"toggle-selected", toggle_selected}, {"notify", notify}, }; static char translations[] = ": start-rubberband() \n" ": stop-rubberband() \n" ": move-rubberband() \n"; ArtTreeClassRec artTreeClassRec = { { /* core fields */ (WidgetClass) &scrollableClassRec, /* superclass */ "ArtTree", /* class_name */ sizeof(ArtTreeRec), /* widget_size */ NULL, /* class_initialize */ NULL, /* class_part_initialize */ FALSE, /* class_inited */ Initialize, /* initialize */ NULL, /* initialize_hook */ XtInheritRealize, /* realize */ actions, /* actions */ XtNumber(actions), /* num_actions */ resources, /* resources */ XtNumber(resources), /* num_resources */ NULLQUARK, /* xrm_class */ TRUE, /* compress_motion */ #if (XtSpecificationRelease < 4) TRUE, /* compress_exposure */ #else XtExposeCompressMaximal, /* compress_exposure */ #endif TRUE, /* compress_enterleave */ FALSE, /* visible_interest */ Destroy, /* destroy */ XtInheritResize, /* resize */ Redisplay, /* expose */ SetValues, /* set_values */ NULL, /* set_values_hook */ XtInheritSetValuesAlmost, /* set_values_almost */ NULL, /* get_values_hook */ NULL, /* accept_focus */ XtVersion, /* version */ NULL, /* callback_private */ translations, /* tm_table */ NULL, /* query_geometry */ XtInheritDisplayAccelerator, /* display_accelerator */ NULL /* extension */ }, { /* shadow fields */ XtInheritPixelOffset, /* pixel_offset */ False, /* use_arm_for_background */ XtInheritAllocShadowColors, /* alloc_shadow_colors */ XtInheritAllocShadowPixmaps, /* alloc_shadow_pixmaps */ XtInheritAllocArmColor, /* alloc_arm_color */ XtInheritAllocArmPixmap, /* alloc_arm_pixmap */ XtInheritAllocGCs, /* alloc_gcs */ NULL, /* extension */ }, { /* scrollable fields */ XtInheritScrollableSetPos, /* set_hpos */ XtInheritScrollableSetPos, /* set_vpos */ SuspendHook, /* suspend_hook */ NULL, /* extension */ }, { /* art_tree fields */ 0 /* empty */ } }; WidgetClass artTreeWidgetClass = (WidgetClass)&artTreeClassRec; /*************************************************************************/ static ART_TREE_NODE *find_node_by_coordinates(ArtTreeWidget w, ART_TREE_NODE *root, int x, int y) { while (root) { ART_TREE_NODE *temp; int dx = x - root->hook->x; int dy = y - root->hook->y; if (dx >= 0 && dy >= 0 && dx <= w->arttree.node_width && dy <= w->arttree.node_height) return root; if (root->child1 && (temp = find_node_by_coordinates(w, root->child1, x, y)) ) return temp; root = root->sibling; } return NULL; } static void draw_rubberband(ArtTreeWidget w) { Widget pw = XtParent(w); long width = w->core.width; /* need to loose */ long height = w->core.height; /* unsignedness */ long pwidth = pw->core.width; long pheight = pw->core.height; int x, y; unsigned int dx, dy; x = - w->core.x - w->core.x * pwidth / width; dx = (pwidth - 1) * pwidth / width; y = - w->core.y - w->core.y * pheight / height; dy = (pheight - 1) * pheight / height; if (dx == 0) dx = 1; if (dy == 0) dy = 1; XDrawRectangle(XtDisplay(w), XtWindow(w), w->arttree.rubber_gc, x, y, dx, dy); } static void start_rubberband(Widget gw, XEvent *event, String *str, Cardinal *n) { ArtTreeWidget w = (ArtTreeWidget)gw; int x, y; if (w->arttree.warp_pointer) { Widget pw = XtParent(w); long width = w->core.width; long height = w->core.height; long pwidth = pw->core.width; long pheight = pw->core.height; x = (pwidth / 2 - w->core.x) * pwidth / width; w->arttree.ptr_init_x = x; y = (pheight / 2 - w->core.y) * pheight / height; w->arttree.ptr_init_y = y; XWarpPointer(XtDisplay(pw), None, XtWindow(pw), 0, 0, 0, 0, x, y); } else { if (!get_event_xy(event, &x, &y)) { XBell(XtDisplay(w), 0); return; } w->arttree.ptr_init_x = x + w->core.x; w->arttree.ptr_init_y = y + w->core.y; } w->arttree.rubberbanding = True; w->arttree.init_x = w->core.x; w->arttree.init_y = w->core.y; draw_rubberband(w); ScrollableFitHBar((ScrollableWidget)w); ScrollableFitVBar((ScrollableWidget)w); } static void stop_rubberband(Widget gw, XEvent *event, String *str, Cardinal *n) { ArtTreeWidget w = (ArtTreeWidget)gw; if (!w->arttree.rubberbanding) return; draw_rubberband(w); w->arttree.rubberbanding = False; } static void move_rubberband(Widget gw, XEvent *event, String *str, Cardinal *n) { ArtTreeWidget w = (ArtTreeWidget)gw; Widget pw = XtParent(w); long width = w->core.width; /* need to loose */ long height = w->core.height; /* unsignedness */ long pwidth = pw->core.width; long pheight = pw->core.height; XtWidgetGeometry req; int x, y; Window w1, w2; int i1, i2; unsigned int ui1; if (!w->arttree.rubberbanding) return; draw_rubberband(w); /* * Get rid of the race by explicitly querying the pointer. */ XQueryPointer(XtDisplay(w), XtWindow(pw), &w1, &w2, &i1, &i2, &x, &y, &ui1); x = w->arttree.init_x + (w->arttree.ptr_init_x - x) * width / pwidth; y = w->arttree.init_y + (w->arttree.ptr_init_y - y) * height / pheight; req.request_mode = CWX | CWY; req.x = x; req.y = y; XtMakeGeometryRequest((Widget)w, &req, NULL); draw_rubberband(w); ScrollableHFromGeometry((ScrollableWidget)w); ScrollableVFromGeometry((ScrollableWidget)w); ScrollableFitHBar((ScrollableWidget)w); ScrollableFitVBar((ScrollableWidget)w); } static void notify(Widget gw, XEvent *event, String *params, Cardinal *no_params) { ArtTreeWidget w = (ArtTreeWidget)gw; int x, y; ART_TREE_NODE *node; if (!w->arttree.active || !get_event_xy(event, &x, &y)) return; node = find_node_by_coordinates(w, w->arttree.root, x, y); if (node) { XtCallbackList c_list = w->arttree.callback; if (c_list) XtCallCallbackList((Widget)w, c_list, (XtPointer)node); } } static void set_inner(Widget gw, XEvent *event, String *params, Cardinal *no_params) { ArtTreeWidget w = (ArtTreeWidget)gw; int x, y; ART_TREE_NODE *node; if (!w->arttree.active || !get_event_xy(event, &x, &y)) return; node = find_node_by_coordinates(w, w->arttree.root, x, y); if (node) { XtCallbackList c_list = w->arttree.inner_callback; ArtTreeNodeSetInner((Widget)w, node, True); if (c_list) XtCallCallbackList((Widget)w, c_list, (XtPointer)node); } } static void reset_inner(Widget gw, XEvent *event, String *params, Cardinal *no_params) { ArtTreeWidget w = (ArtTreeWidget)gw; int x, y; ART_TREE_NODE *node; if (!w->arttree.active || !get_event_xy(event, &x, &y)) return; node = find_node_by_coordinates(w, w->arttree.root, x, y); if (node) { XtCallbackList c_list = w->arttree.inner_callback; ArtTreeNodeSetInner((Widget)w, node, False); if (c_list) XtCallCallbackList((Widget)w, c_list, (XtPointer)node); } } static void toggle_inner(Widget gw, XEvent *event, String *params, Cardinal *no_params) { ArtTreeWidget w = (ArtTreeWidget)gw; int x, y; ART_TREE_NODE *node; if (!w->arttree.active || !get_event_xy(event, &x, &y)) return; node = find_node_by_coordinates(w, w->arttree.root, x, y); if (node) { XtCallbackList c_list = w->arttree.inner_callback; ArtTreeNodeSetInner((Widget)w, node, !node->hook->inner); if (c_list) XtCallCallbackList((Widget)w, c_list, (XtPointer)node); } } static void set_outer(Widget gw, XEvent *event, String *params, Cardinal *no_params) { ArtTreeWidget w = (ArtTreeWidget)gw; int x, y; ART_TREE_NODE *node; if (!w->arttree.active || !get_event_xy(event, &x, &y)) return; node = find_node_by_coordinates(w, w->arttree.root, x, y); if (node) { XtCallbackList c_list = w->arttree.outer_callback; ArtTreeNodeSetOuter((Widget)w, node, True); if (c_list) XtCallCallbackList((Widget)w, c_list, (XtPointer)node); } } static void reset_outer(Widget gw, XEvent *event, String *params, Cardinal *no_params) { ArtTreeWidget w = (ArtTreeWidget)gw; int x, y; ART_TREE_NODE *node; if (!w->arttree.active || !get_event_xy(event, &x, &y)) return; node = find_node_by_coordinates(w, w->arttree.root, x, y); if (node) { XtCallbackList c_list = w->arttree.outer_callback; ArtTreeNodeSetOuter((Widget)w, node, False); if (c_list) XtCallCallbackList((Widget)w, c_list, (XtPointer)node); } } static void toggle_outer(Widget gw, XEvent *event, String *params, Cardinal *no_params) { ArtTreeWidget w = (ArtTreeWidget)gw; int x, y; ART_TREE_NODE *node; if (!w->arttree.active || !get_event_xy(event, &x, &y)) return; node = find_node_by_coordinates(w, w->arttree.root, x, y); if (node) { XtCallbackList c_list = w->arttree.outer_callback; ArtTreeNodeSetOuter((Widget)w, node, !node->hook->outer); if (c_list) XtCallCallbackList((Widget)w, c_list, (XtPointer)node); } } static void set_selected(Widget gw, XEvent *event, String *params, Cardinal *no_params) { ArtTreeWidget w = (ArtTreeWidget)gw; int x, y; ART_TREE_NODE *node; if (!w->arttree.active || !get_event_xy(event, &x, &y)) return; node = find_node_by_coordinates(w, w->arttree.root, x, y); if (node) { XtCallbackList c_list = w->arttree.select_callback; ArtTreeNodeSetSelected((Widget)w, node, True); if (c_list) XtCallCallbackList((Widget)w, c_list, (XtPointer)node); } } static void reset_selected(Widget gw, XEvent *event, String *params, Cardinal *no_params) { ArtTreeWidget w = (ArtTreeWidget)gw; int x, y; ART_TREE_NODE *node; if (!w->arttree.active || !get_event_xy(event, &x, &y)) return; node = find_node_by_coordinates(w, w->arttree.root, x, y); if (node) { XtCallbackList c_list = w->arttree.select_callback; ArtTreeNodeSetSelected((Widget)w, node, False); if (c_list) XtCallCallbackList((Widget)w, c_list, (XtPointer)node); } } static void toggle_selected(Widget gw, XEvent *event, String *params, Cardinal *no_params) { ArtTreeWidget w = (ArtTreeWidget)gw; int x, y; ART_TREE_NODE *node; if (!w->arttree.active || !get_event_xy(event, &x, &y)) return; node = find_node_by_coordinates(w, w->arttree.root, x, y); if (node) { XtCallbackList c_list = w->arttree.select_callback; ArtTreeNodeSetSelected((Widget)w, node, !node->hook->selected); if (c_list) XtCallCallbackList((Widget)w, c_list, (XtPointer)node); } } /***********************************************************************/ static void free_gcs(ArtTreeWidget w) { XtReleaseGC((Widget)w, w->arttree.default_gc); XtReleaseGC((Widget)w, w->arttree.bg_gc); XtReleaseGC((Widget)w, w->arttree.inner_gc); XtReleaseGC((Widget)w, w->arttree.outer_gc); XtReleaseGC((Widget)w, w->arttree.rubber_gc); } static void free_dashed_gcs(ArtTreeWidget w) { if (w->arttree.light_dashed_gc != 0) XtReleaseGC((Widget)w, w->arttree.light_dashed_gc); XtReleaseGC((Widget)w, w->arttree.dark_dashed_gc); } #define MIN(a,b) (unsigned short) (a < b) ? a : b static void init_gcs(ArtTreeWidget w) { XGCValues values; long mask; /* default gc */ values.foreground = w->arttree.foreground_pixel; values.font = w->arttree.font->fid; w->arttree.default_gc = XtGetGC((Widget)w, GCForeground | GCFont, &values); /* bg_gc */ values.foreground = w->core.background_pixel; w->arttree.bg_gc = XtGetGC((Widget)w, GCForeground, &values); /* inner_gc */ mask = GCForeground | GCLineWidth; values.foreground = w->arttree.inner_pixel; values.line_width = 1; if (w->arttree.inner_dashed) { mask |= GCLineStyle; values.line_style = LineOnOffDash; } w->arttree.inner_gc = XtGetGC((Widget)w, mask, &values); /* outer_gc */ mask = GCForeground | GCLineWidth; values.foreground = w->arttree.outer_pixel; values.line_width = 1; if (w->arttree.outer_dashed) { mask |= GCLineStyle; values.line_style = LineOnOffDash; } w->arttree.outer_gc = XtGetGC((Widget)w, mask, &values); /* rubber gc */ values.function = GXxor; values.foreground = w->core.background_pixel ^ w->arttree.rubber_pixel; w->arttree.rubber_gc = XtGetGC((Widget)w, GCFunction | GCForeground, &values); } static void init_dashed_gcs(ArtTreeWidget w) { XGCValues values; long mask; values.line_width = 1; values.line_style = LineOnOffDash; mask = GCLineWidth | GCLineStyle; if (w->shadow.alloced_shadow_pixels) { mask |= GCForeground; values.foreground = w->shadow.light_pixel; w->arttree.light_dashed_gc = XtGetGC((Widget)w, mask, &values); values.foreground = w->shadow.dark_pixel; w->arttree.dark_dashed_gc = XtGetGC((Widget)w, mask, &values); } else if (w->shadow.line_mode) { mask |= GCForeground; /* depth == 1 */ values.foreground = (w->core.background_pixel == 1) ? 0 : 1; w->arttree.light_dashed_gc = XtGetGC((Widget)w, mask, &values); w->arttree.dark_dashed_gc = 0; } else { Screen *scr = XtScreen(w); Visual *vis = get_visual((Widget)w); long temp_mask; Pixel black, white; black_and_white(scr, vis, &black, &white); if (w->shadow.light_pixmap == None) { /* never true */ temp_mask = mask | GCForeground; values.foreground = (w->core.background_pixel == black) ? white : black; } else { temp_mask = mask | GCTile | GCFillStyle; values.fill_style = FillTiled; values.tile = w->shadow.light_pixmap; } w->arttree.light_dashed_gc = XtGetGC((Widget)w, temp_mask, &values); if (w->shadow.dark_pixmap == None) { temp_mask = mask | GCForeground; values.foreground = (w->core.background_pixel == black) ? white : black; } else { temp_mask = mask | GCTile | GCFillStyle; values.fill_style = FillTiled; values.tile = w->shadow.dark_pixmap; } w->arttree.dark_dashed_gc = XtGetGC((Widget)w, temp_mask, &values); } } static void sub_layout_horiz(ArtTreeWidget w, ART_TREE_NODE *node, int x, int y, int *max_x, int *max_y) { for (;;) { node->hook->x = x; node->hook->y = y; if (node->child1) { int sub_x = *max_x; int sub_y = *max_y; sub_layout_horiz(w, node->child1, x + w->arttree.node_width + w->arttree.column_spacing, y, &sub_x, &sub_y); node->hook->bb_width = sub_x - node->child1->hook->x; node->hook->bb_height = sub_y - node->child1->hook->y; if (sub_x > *max_x) *max_x = sub_x; y = sub_y; } else { if (x + w->arttree.node_width > *max_x) *max_x = x + w->arttree.node_width; y += w->arttree.node_height; } node = node->sibling; if (node) y += w->arttree.row_spacing; else break; } *max_y = y; } static void sub_layout_vert(ArtTreeWidget w, ART_TREE_NODE *node, int x, int y, int *max_x, int *max_y) { node->hook->x = x; node->hook->y = y; y += w->arttree.node_height; for (;;) { if (node->child1) { Position x1 = x + 2 * w->arttree.column_spacing; sub_layout_vert(w, node->child1, x1, y + w->arttree.row_spacing, max_x, max_y); node->hook->bb_width = *max_x - x; node->hook->bb_height = *max_y - y + w->arttree.node_height; y = *max_y; } else { node->hook->bb_width = 0; node->hook->bb_height = 0; } node = node->sibling; if (!node) break; y += w->arttree.row_spacing; node->hook->x = x; node->hook->y = y; y += w->arttree.node_height; } *max_y = y; x += w->arttree.node_width; if (*max_x < x) *max_x = x; } static void layout(ArtTreeWidget w) { if (w->arttree.root) { int max_x = w->arttree.internal_width; int max_y = w->arttree.internal_height; if (w->arttree.vertical) sub_layout_vert(w, w->arttree.root, max_x, max_y, &max_x, &max_y); else sub_layout_horiz(w, w->arttree.root, max_x, max_y, &max_x, &max_y); max_x += w->arttree.internal_width; max_y += w->arttree.internal_height; XtMakeResizeRequest((Widget)w, max_x, max_y, NULL, NULL); ScrollableHFromGeometry((ScrollableWidget)w); ScrollableVFromGeometry((ScrollableWidget)w); ScrollableFitHBar((ScrollableWidget)w); ScrollableFitVBar((ScrollableWidget)w); } } static void init_node_sizes(ArtTreeWidget w) { w->arttree.node_width = 2 * (w->arttree.internal_node_width + w->shadow.shadow_width) + w->arttree.node_columns * (w->arttree.font->max_bounds.lbearing + w->arttree.font->max_bounds.rbearing); w->arttree.node_height = 1 + 2 * (w->arttree.internal_node_height + w->shadow.shadow_width) + w->arttree.node_rows * (w->arttree.font->ascent + w->arttree.font->descent); } /*************************************************************************/ static void draw_outer_border(ArtTreeWidget w, ART_TREE_NODE *node) { Display *disp = XtDisplay(w); Window win = XtWindow(w); XPoint points[6]; GC gc; gc = node->hook->outer ? w->arttree.outer_gc : w->arttree.bg_gc; if (node->child1) if (node->parent) if (w->arttree.vertical) { points[0].x = node->hook->x + w->arttree.column_spacing - 1; points[1].x = points[2].x = node->hook->x - 1; points[0].y = points[1].y = node->hook->y + w->arttree.node_height; points[2].y = node->hook->y + w->arttree.node_height / 2 + 1; XDrawLines(disp, win, gc, points, 3, CoordModeOrigin); points[0].x = points[1].x = node->hook->x - 1; points[2].x = points[3].x = node->hook->x + w->arttree.node_width; points[4].x = node->hook->x + w->arttree.column_spacing + 1; points[0].y = node->hook->y + w->arttree.node_height / 2 - 1; points[1].y = points[2].y = node->hook->y - 1; points[3].y = points[4].y = node->hook->y + w->arttree.node_height; XDrawLines(disp, win, gc, points, 5, CoordModeOrigin); } else { points[0].x = points[1].x = node->hook->x - 1; points[2].x = points[3].x = node->hook->x + w->arttree.node_width; points[0].y = points[3].y = node->hook->y + w->arttree.node_height / 2 - 1; points[1].y = points[2].y = node->hook->y - 1; XDrawLines(disp, win, gc, points, 4, CoordModeOrigin); points[0].y += 3; points[3].y += 3; points[1].y = points[2].y = node->hook->y + w->arttree.node_height; XDrawLines(disp, win, gc, points, 4, CoordModeOrigin); } else if (w->arttree.vertical) { points[0].x = node->hook->x + w->arttree.column_spacing - 1; points[1].x = points[2].x = node->hook->x - 1; points[3].x = points[4].x = node->hook->x + w->arttree.node_width; points[5].x = node->hook->x + w->arttree.column_spacing + 1; points[0].y = points[1].y = points[4].y = points[5].y = node->hook->y + w->arttree.node_height; points[2].y = points[3].y = node->hook->y - 1; XDrawLines(disp, win, gc, points, 6, CoordModeOrigin); } else { points[0].x = points[1].x = points[4].x = points[5].x = node->hook->x + w->arttree.node_width; points[2].x = points[3].x = node->hook->x - 1; points[0].y = node->hook->y + w->arttree.node_height / 2 - 1; points[5].y = points[0].y + 3; points[1].y = points[2].y = node->hook->y - 1; points[3].y = points[4].y = node->hook->y + w->arttree.node_height; XDrawLines(disp, win, gc, points, 6, CoordModeOrigin); } else if (!node->parent) XDrawRectangle(disp, win, gc, node->hook->x - 1, node->hook->y - 1, w->arttree.node_width + 1, w->arttree.node_height + 1); else { points[0].x = points[1].x = points[4].x = points[5].x = node->hook->x - 1; points[2].x = points[3].x = node->hook->x + w->arttree.node_width; points[0].y = node->hook->y + w->arttree.node_height / 2 - 2; points[5].y = points[0].y + 3; points[1].y = points[2].y = node->hook->y - 1; points[3].y = points[4].y = node->hook->y + w->arttree.node_height; XDrawLines(disp, win, gc, points, 6, CoordModeOrigin); } } static void draw_inner_border(ArtTreeWidget w, ART_TREE_NODE *node) { Display *disp = XtDisplay(w); Window win = XtWindow(w); int sw = w->shadow.shadow_width ; GC gc; if (node->hook->inner) gc = w->arttree.inner_gc; else if (node->hook->selected && w->shadow.arm_gc != 0) gc = w->shadow.arm_gc; else gc = w->arttree.bg_gc; XDrawRectangle(disp, win, gc, node->hook->x + sw, node->hook->y + sw, w->arttree.node_width - 2 * sw - 1, w->arttree.node_height - 2 * sw - 1); } static void draw_node(ArtTreeWidget w, ART_TREE_NODE *node) { Display *disp = XtDisplay(w); Window win = XtWindow(w); int x, y; if (node->hook->selected && w->shadow.arm_gc != 0) XFillRectangle(disp, win, w->shadow.arm_gc, node->hook->x, node->hook->y, w->arttree.node_width, w->arttree.node_height); ShadowDrawShadows((ShadowWidget)w, node->hook->x, node->hook->y, w->arttree.node_width, w->arttree.node_height, node->hook->selected); if (node->hook->inner) draw_inner_border(w, node); if (node->hook->outer) draw_outer_border(w, node); x = node->hook->x + w->arttree.internal_node_width + w->shadow.shadow_width; y = node->hook->y; if (node->hook->pixmap != None) { if (w->arttree.depth_one) XCopyPlane(disp, node->hook->pixmap, win, w->arttree.default_gc, 0, 0, w->arttree.pixmap_width, w->arttree.pixmap_height, x, y + (int)(w->arttree.node_height - w->arttree.pixmap_height) / 2 + (node->hook->selected ? 1 : 0), 1); else XCopyArea(disp, node->hook->pixmap, win, w->arttree.default_gc, 0, 0, w->arttree.pixmap_width, w->arttree.pixmap_height, x, y + (int)(w->arttree.node_height - w->arttree.pixmap_height) / 2 + (node->hook->selected ? 1 : 0)); x += w->arttree.pixmap_width + w->arttree.pixmap_spacing; } y += w->arttree.font->ascent + w->arttree.internal_node_height + w->shadow.shadow_width; if (node->hook->selected) y++; if (node->label) XDrawString(disp, win, w->arttree.default_gc, x, y, node->label, node->hook->label_len); } static void draw_subnodes(ArtTreeWidget w, ART_TREE_NODE *node, Region region) { while (node) { if (!region || XRectInRegion(region, node->hook->x - 1, node->hook->y - 1, w->arttree.node_width + 2, w->arttree.node_height + 2) != RectangleOut) draw_node(w, node); if (node->child1) { if (!region || XRectInRegion(region, node->child1->hook->x - 1, node->child1->hook->y - 1, node->hook->bb_width, node->hook->bb_height) != RectangleOut) draw_subnodes(w, node->child1, region); } node = node->sibling; } } static void draw_branches(ArtTreeWidget w, ART_TREE_NODE *node) { Display *disp = XtDisplay(w); Window win = XtWindow(w); ART_TREE_NODE *child = node->child1; GC gc1, gc2; int x, y, y0; if (!child) return; if (w->arttree.vertical) { x = node->hook->x + w->arttree.column_spacing; y0 = node->hook->y + w->arttree.node_height - 1; y = child->hook->y + w->arttree.node_height / 2; } else { x = (node->hook->x + w->arttree.node_width + child->hook->x)/2; y = y0 = node->hook->y + w->arttree.node_height/2; XDrawLine(disp, win, w->shadow.line_mode ? w->shadow.light_gc : w->shadow.dark_gc, node->hook->x + w->arttree.node_width, y - 1, x + 1, y - 1); XDrawLine(disp, win, w->shadow.light_gc, node->hook->x + w->arttree.node_width, y, x + 1, y); } for (;;) { if (child->hook->dashed) { if (w->shadow.line_mode) gc1 = w->arttree.light_dashed_gc; else gc1 = w->arttree.dark_dashed_gc; gc2 = w->arttree.light_dashed_gc; } else { if (w->shadow.line_mode) gc1 = w->shadow.light_gc; else gc1 = w->shadow.dark_gc; gc2 = w->shadow.light_gc; } XDrawLine(disp, win, gc1, x + 1, y - 1, child->hook->x, y - 1); XDrawLine(disp, win, gc2, x + 1, y, child->hook->x, y); child = child->sibling; if (!child) break; y = child->hook->y + w->arttree.node_height/2; } XDrawLine(disp, win, w->shadow.line_mode ? w->shadow.light_gc : w->shadow.dark_gc, x - 1, y0 + 1, x - 1, y + 1); XDrawLine(disp, win, w->shadow.light_gc, x, y0 + 1, x, y + 1); } static void draw_branches_of_subtree(ArtTreeWidget w, ART_TREE_NODE *node, Region region) { while (node) { if (node->child1) { if (!region || XRectInRegion(region, node->hook->x, node->hook->y, node->child1->hook->x - node->hook->x, node->hook->bb_height) != RectangleOut) draw_branches(w, node); if (!region || XRectInRegion(region, node->child1->hook->x, node->child1->hook->y, node->hook->bb_width, node->hook->bb_height) != RectangleOut) draw_branches_of_subtree(w, node->child1, region); } node = node->sibling; } } /***********************************************************************/ static void free_private_data(ART_TREE_NODE *node) { while (node) { if (node->hook) { XtFree((char *)node->hook); node->hook = NULL; } if (node->child1) free_private_data(node->child1); node = node->sibling; } } static void init_private_data(ArtTreeWidget w, ART_TREE_NODE *node) { while (node) { node->hook = (ART_TREE_PRIVATE *)XtMalloc(sizeof(ART_TREE_PRIVATE)); node->hook->pixmap = None; node->hook->selected = False; node->hook->dashed = False; node->hook->inner = False; node->hook->outer = False; if (node->label) node->hook->label_len = MyXWidthToChars(w->arttree.font, node->label, strlen(node->label), w->arttree.node_width - 2 * (w->arttree.internal_node_width + w->shadow.shadow_width)); else node->hook->label_len = 0; if (node->child1) init_private_data(w, node->child1); node = node->sibling; } } static void update_private_data(ArtTreeWidget w, ART_TREE_NODE *node) { while (node) { if (node->label) node->hook->label_len = MyXWidthToChars(w->arttree.font, node->label, strlen(node->label), w->arttree.node_width - 2 * (w->arttree.internal_node_width + w->shadow.shadow_width)); else node->hook->label_len = 0; if (node->child1) update_private_data(w, node->child1); node = node->sibling; } } /***********************************************************************/ static void Initialize(Widget grequest, Widget gnew, ArgList args, Cardinal *num_args) { ArtTreeWidget new = (ArtTreeWidget)gnew; init_gcs(new); init_dashed_gcs(new); new->arttree.rubberbanding = False; new->arttree.active = True; init_node_sizes(new); if (new->arttree.root) { init_private_data(new, new->arttree.root); layout(new); } if (new->core.height == 0) new->core.height = 1; if (new->core.width == 0) new->core.width = 1; } static void Destroy(Widget w) { free_gcs((ArtTreeWidget)w); free_dashed_gcs((ArtTreeWidget)w); } static void Redisplay(Widget gw, XEvent *event, Region region) { ArtTreeWidget w = (ArtTreeWidget)gw; if (!XtIsRealized((Widget)w)) return; if (w->arttree.rubberbanding) draw_rubberband(w); draw_subnodes(w, w->arttree.root, region); draw_branches_of_subtree(w, w->arttree.root, region); if (w->arttree.rubberbanding) draw_rubberband(w); } static void SuspendHook(ScrollableWidget w) { if (!w->scrollable.suspended) Redisplay((Widget)w, NULL, NULL); } static Boolean SetValues(Widget gcurrent, Widget grequest, Widget gnew, ArgList args, Cardinal *num_args) { ArtTreeWidget current = (ArtTreeWidget)gcurrent; ArtTreeWidget new = (ArtTreeWidget)gnew; Boolean redisplay = False; Boolean do_layout = False; if (new->arttree.vertical != current->arttree.vertical) do_layout = True; if (new->arttree.rubber_pixel != current->arttree.rubber_pixel || new->arttree.foreground_pixel != current->arttree.foreground_pixel || new->arttree.font != current->arttree.font || new->arttree.inner_pixel != current->arttree.inner_pixel || new->arttree.outer_pixel != current->arttree.outer_pixel || new->arttree.inner_dashed != current->arttree.inner_dashed || new->arttree.outer_dashed != current->arttree.outer_dashed) { free_gcs(new); init_gcs(new); redisplay = True; } if (new->arttree.font != current->arttree.font) { init_node_sizes(new); do_layout = True; } if (new->arttree.internal_node_width != current->arttree.internal_node_width || new->arttree.internal_node_height != current->arttree.internal_node_height || new->arttree.pixmap_spacing != current->arttree.pixmap_spacing || new->arttree.node_columns != current->arttree.node_columns || new->arttree.node_rows != current->arttree.node_rows) { init_node_sizes(new); if (new->arttree.root) update_private_data(new, new->arttree.root); do_layout = True; } if (new->arttree.row_spacing != current->arttree.row_spacing || new->arttree.internal_width != current->arttree.internal_width || new->arttree.internal_height != current->arttree.internal_height || new->shadow.shadow_width != current->shadow.shadow_width) { do_layout = True; redisplay = True; } if (new->arttree.root != current->arttree.root) { if (current->arttree.root) free_private_data(current->arttree.root); if (new->arttree.root) init_private_data(new, new->arttree.root); do_layout = True; if (!new->scrollable.suspended) redisplay = True; } if (do_layout) { layout(new); redisplay = True; } return redisplay; } /******************************************************************/ void ArtTreeRedraw(Widget gw) { ArtTreeWidget w = (ArtTreeWidget)gw; if (w->arttree.rubberbanding) { w->arttree.rubberbanding = False; draw_rubberband(w); } XClearArea(XtDisplay(w), XtWindow(w), 0, 0, w->core.width, w->core.height, True); } void ArtTreeSetTree(Widget gw, ART_TREE_NODE *root) { ArtTreeWidget w = (ArtTreeWidget)gw; if (w->arttree.rubberbanding) { draw_rubberband(w); w->arttree.rubberbanding = False; } if (w->arttree.root) free_private_data(w->arttree.root); w->arttree.root = root; if (w->arttree.root) init_private_data(w, w->arttree.root); layout(w); if (!w->scrollable.suspended && XtIsRealized((Widget)w)) ArtTreeRedraw((Widget)w); } void ArtTreeNodeCenter(Widget gw, ART_TREE_NODE *node) { ArtTreeWidget w = (ArtTreeWidget)gw; Widget pw = XtParent(w); XtWidgetGeometry req; if (!node->hook) return; if (w->arttree.rubberbanding) draw_rubberband(w); req.request_mode = CWX | CWY; req.x = (int)(pw->core.width - w->arttree.node_width) / 2 - node->hook->x; req.y = (int)(pw->core.height - w->arttree.node_height) / 2 - node->hook->y; XtMakeGeometryRequest((Widget)w, &req, NULL); if (w->arttree.rubberbanding) draw_rubberband(w); ScrollableHFromGeometry((ScrollableWidget)w); ScrollableVFromGeometry((ScrollableWidget)w); ScrollableFitHBar((ScrollableWidget)w); ScrollableFitVBar((ScrollableWidget)w); } void ArtTreeNodeMakeVisible(Widget gw, ART_TREE_NODE *node) { ArtTreeWidget w = (ArtTreeWidget)gw; Widget pw = XtParent(w); if (!node->hook) return; if (node->hook->x < -w->core.x || node->hook->y < -w->core.y || node->hook->x >= (int)pw->core.width - w->core.x - w->arttree.node_width || node->hook->y >= (int)pw->core.height - w->core.y - w->arttree.node_height) ArtTreeNodeCenter((Widget)w, node); } void ArtTreeNodeSetSelected(Widget gw, ART_TREE_NODE *node, int selected) { ArtTreeWidget w = (ArtTreeWidget)gw; if (!node->hook) return; node->hook->selected = selected; if (w->scrollable.suspended || !XtIsRealized((Widget)w)) return; if (w->arttree.rubberbanding) draw_rubberband(w); XClearArea(XtDisplay(w), XtWindow(w), node->hook->x, node->hook->y, w->arttree.node_width, w->arttree.node_height, False); draw_node(w, node); if (w->arttree.rubberbanding) draw_rubberband(w); XFlush(XtDisplay(w)); } void ArtTreeNodeSetDashed(Widget gw, ART_TREE_NODE *node, int dashed) { ART_TREE_NODE *parent = node->parent; ArtTreeWidget w = (ArtTreeWidget)gw; if (!parent || !node->hook) return; node->hook->dashed = dashed; if (w->scrollable.suspended || !XtIsRealized((Widget)w)) return; if (w->arttree.rubberbanding) draw_rubberband(w); if (dashed) if (w->arttree.vertical) /* FIXME... (knews doesn't need it) */; else XClearArea(XtDisplay(w), XtWindow(w), parent->hook->x + w->arttree.node_width, parent->hook->y, w->arttree.column_spacing, parent->hook->bb_height, False); draw_branches(w, parent); if (w->arttree.rubberbanding) draw_rubberband(w); XFlush(XtDisplay(w)); } void ArtTreeNodeSetInner(Widget gw, ART_TREE_NODE *node, int inner) { ArtTreeWidget w = (ArtTreeWidget)gw; if (!node->hook) return; node->hook->inner = inner; if (w->scrollable.suspended || !XtIsRealized((Widget)w)) return; if (w->arttree.rubberbanding) draw_rubberband(w); draw_inner_border(w, node); if (w->arttree.rubberbanding) draw_rubberband(w); XFlush(XtDisplay(w)); } void ArtTreeNodeSetOuter(Widget gw, ART_TREE_NODE *node, int outer) { ArtTreeWidget w = (ArtTreeWidget)gw; if (!node->hook) return; node->hook->outer = outer; if (w->scrollable.suspended || !XtIsRealized((Widget)w)) return; if (w->arttree.rubberbanding) draw_rubberband(w); draw_outer_border(w, node); if (w->arttree.rubberbanding) draw_rubberband(w); XFlush(XtDisplay(w)); } void ArtTreeNodeSetPixmap(Widget gw, ART_TREE_NODE *node, Pixmap pixmap) { ArtTreeWidget w = (ArtTreeWidget)gw; int width; if (!node->hook) return; node->hook->pixmap = pixmap; width = w->arttree.node_width - 2 * (w->arttree.internal_node_width + w->shadow.shadow_width); if (pixmap != None) width -= w->arttree.pixmap_width + w->arttree.pixmap_spacing; if (node->label) node->hook->label_len = MyXWidthToChars(w->arttree.font, node->label, strlen(node->label), width); else node->hook->label_len = 0; if (w->scrollable.suspended || !XtIsRealized((Widget)w)) return; if (w->arttree.rubberbanding) draw_rubberband(w); XClearArea(XtDisplay(w), XtWindow(w), node->hook->x, node->hook->y, w->arttree.node_width, w->arttree.node_height, False); draw_node(w, node); if (w->arttree.rubberbanding) draw_rubberband(w); XFlush(XtDisplay(w)); } int ArtTreeNodeGetSelected(Widget gw, ART_TREE_NODE *node) { return node->hook ? node->hook->selected : False; } int ArtTreeNodeGetDashed(Widget gw, ART_TREE_NODE *node) { return node->hook ? node->hook->dashed : False; } int ArtTreeNodeGetInner(Widget gw, ART_TREE_NODE *node) { return node->hook ? node->hook->inner : False; } int ArtTreeNodeGetOuter(Widget gw, ART_TREE_NODE *node) { return node->hook ? node->hook->outer : False; } Pixmap ArtTreeNodeGetPixmap(Widget gw, ART_TREE_NODE *node) { return node->hook ? node->hook->pixmap : None; } void ArtTreeNodeNotifyLabel(Widget gw, ART_TREE_NODE *node) { ArtTreeWidget w = (ArtTreeWidget)gw; int width; if (!node->hook) return; width = w->arttree.node_width - 2 * (w->arttree.internal_node_width + w->shadow.shadow_width); if (node->hook->pixmap != None) width -= w->arttree.pixmap_width + w->arttree.pixmap_spacing; if (node->label) node->hook->label_len = MyXWidthToChars(w->arttree.font, node->label, strlen(node->label), width); else node->hook->label_len = 0; if (w->scrollable.suspended || !XtIsRealized((Widget)w)) return; if (w->arttree.rubberbanding) draw_rubberband(w); XClearArea(XtDisplay(w), XtWindow(w), node->hook->x, node->hook->y, w->arttree.node_width, w->arttree.node_height, False); draw_node(w, node); if (w->arttree.rubberbanding) draw_rubberband(w); XFlush(XtDisplay(w)); } void ArtTreeSetActive(Widget gw, int active) { ArtTreeWidget w = (ArtTreeWidget)gw; w->arttree.active = active; } ./knews-1.0b.1/Widgets/ArtTree.h100644 1244 1244 7160 6455455534 14766 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #ifndef ArtTree_h #define ArtTree_h #include "ArtTreeNode.h" #ifndef XtCColumnSpacing #define XtCColumnSpacing "ColumnSpacing" #endif #ifndef XtCDashed #define XtCDashed "Dashed" #endif #ifndef XtCDepthOne #define XtCDepthOne "DepthOne" #endif #ifndef XtCInternalHeight #define XtCInternalHeight "InternalHeight" #endif #ifndef XtCInternalNodeHeight #define XtCInternalNodeHeight "InternalNodeHeight" #endif #ifndef XtCInternalNodeWidth #define XtCInternalNodeWidth "InternalNodeWidth" #endif #ifndef XtCInternalWidth #define XtCInternalWidth "InternalWidth" #endif #ifndef XtCNodeColumns #define XtCNodeColumns "NodeColumns" #endif #ifndef XtCNodeRows #define XtCNodeRows "NodeRows" #endif #ifndef XtCPixmapHeight #define XtCPixmapHeight "PixmapHeight" #endif #ifndef XtCPixmapSpacing #define XtCPixmapSpacing "PixmapSpacing" #endif #ifndef XtCPixmapWidth #define XtCPixmapWidth "PixmapWidth" #endif #ifndef XtCRowSpacing #define XtCRowSpacing "RowSpacing" #endif #ifndef XtCTree #define XtCTree "Tree" #endif #ifndef XtCWarpPointer #define XtCWarpPointer "WarpPointer" #endif #ifndef XtCVertical #define XtCVertical "Vertical" #endif #ifndef XtNcolumnSpacing #define XtNcolumnSpacing "columnSpacing" #endif #ifndef XtNdepthOne #define XtNdepthOne "depthOne" #endif #ifndef XtNinnerCallback #define XtNinnerCallback "innerCallback" #endif #ifndef XtNinnerColor #define XtNinnerColor "innerColor" #endif #ifndef XtNinnerDashed #define XtNinnerDashed "innerDashed" #endif #ifndef XtNinternalNodeHeight #define XtNinternalNodeHeight "internalNodeHeight" #endif #ifndef XtNinternalNodeWidth #define XtNinternalNodeWidth "internalNodeWidth" #endif #ifndef XtNnodeColumns #define XtNnodeColumns "nodeColumns" #endif #ifndef XtNnodeRows #define XtNnodeRows "nodeRows" #endif #ifndef XtNouterCallback #define XtNouterCallback "outerCallback" #endif #ifndef XtNouterColor #define XtNouterColor "outerColor" #endif #ifndef XtNouterDashed #define XtNouterDashed "outerDashed" #endif #ifndef XtNpixmapHeight #define XtNpixmapHeight "pixmapHeight" #endif #ifndef XtNpixmapSpacing #define XtNpixmapSpacing "pixmapSpacing" #endif #ifndef XtNpixmapWidth #define XtNpixmapWidth "pixmapWidth" #endif #ifndef XtNrowSpacing #define XtNrowSpacing "rowSpacing" #endif #ifndef XtNrubberColor #define XtNrubberColor "rubberColor" #endif #ifndef XtNselectCallback #define XtNselectCallback "selectCallback" #endif #ifndef XtNtree #define XtNtree "tree" #endif #ifndef XtNwarpPointer #define XtNwarpPointer "warpPointer" #endif #ifndef XtNvertical #define XtNvertical "vertical" #endif typedef struct ArtTreeClassRec* ArtTreeWidgetClass; typedef struct ArtTreeRec* ArtTreeWidget; extern WidgetClass artTreeWidgetClass; extern void ArtTreeSetTree(Widget, ART_TREE_NODE*); extern void ArtTreeRedraw(Widget); extern void ArtTreeNodeCenter(Widget, ART_TREE_NODE*); extern void ArtTreeNodeMakeVisible(Widget, ART_TREE_NODE*); extern void ArtTreeNodeSetSelected(Widget, ART_TREE_NODE*, int); extern void ArtTreeNodeSetDashed(Widget, ART_TREE_NODE*, int); extern void ArtTreeNodeSetInner(Widget, ART_TREE_NODE*, int); extern void ArtTreeNodeSetOuter(Widget, ART_TREE_NODE*, int); extern void ArtTreeNodeSetPixmap(Widget, ART_TREE_NODE*, Pixmap); extern Pixmap ArtTreeNodeGetPixmap(Widget, ART_TREE_NODE*); extern int ArtTreeNodeGetSelected(Widget, ART_TREE_NODE*); extern int ArtTreeNodeGetDashed(Widget, ART_TREE_NODE*); extern int ArtTreeNodeGetInner(Widget, ART_TREE_NODE*); extern int ArtTreeNodeGetOuter(Widget, ART_TREE_NODE*); extern void ArtTreeNodeNotifyLabel(Widget, ART_TREE_NODE*); extern void ArtTreeSetActive(Widget, int); #endif /* ArtTree_h */ ./knews-1.0b.1/Widgets/Layout.ps100644 1244 1244 152360 6455455534 15133 0ustar kallekalle%!PS-Adobe-1.0 %%BoundingBox: 0 0 612 792 %%Pages: 10 1 %%DocumentFonts: Times-Roman %%+ Times-Italic %%+ Courier %%+ Times-BoldItalic %%+ Symbol %%+ Times-Bold %%CreationDate: 2 Mar 93 %%Title: The Layout Widget %%EndComments % % FrameMaker PostScript Prolog 3.0, for use with FrameMaker 3.0 % Copyright (c) 1986,87,89,90,91 by Frame Technology Corporation. % All rights reserved. % /FMversion (3.0) def % Set up Color vs. Black-and-White /FMPrintInColor systemdict /colorimage known systemdict /currentcolortransfer known or def % Uncomment this line to force b&w on color printer % /FMPrintInColor false def /FrameDict 195 dict def systemdict /errordict known not {/errordict 10 dict def errordict /rangecheck {stop} put} if % The readline in 23.0 doesn't recognize cr's as nl's on AppleTalk FrameDict /tmprangecheck errordict /rangecheck get put errordict /rangecheck {FrameDict /bug true put} put FrameDict /bug false put mark % Some PS machines read past the CR, so keep the following 3 lines together! currentfile 5 string readline 00 0000000000 cleartomark errordict /rangecheck FrameDict /tmprangecheck get put FrameDict /bug get { /readline { /gstring exch def /gfile exch def /gindex 0 def { gfile read pop dup 10 eq {exit} if dup 13 eq {exit} if gstring exch gindex exch put /gindex gindex 1 add def } loop pop gstring 0 gindex getinterval true } def } if /FMVERSION { FMversion ne { /Times-Roman findfont 18 scalefont setfont 100 100 moveto (FrameMaker version does not match postscript_prolog!) dup = show showpage } if } def /FMLOCAL { FrameDict begin 0 def end } def /gstring FMLOCAL /gfile FMLOCAL /gindex FMLOCAL /orgxfer FMLOCAL /orgproc FMLOCAL /organgle FMLOCAL /orgfreq FMLOCAL /yscale FMLOCAL /xscale FMLOCAL /manualfeed FMLOCAL /paperheight FMLOCAL /paperwidth FMLOCAL /FMDOCUMENT { array /FMfonts exch def /#copies exch def FrameDict begin 0 ne dup {setmanualfeed} if /manualfeed exch def /paperheight exch def /paperwidth exch def /yscale exch def /xscale exch def currenttransfer cvlit /orgxfer exch def currentscreen cvlit /orgproc exch def /organgle exch def /orgfreq exch def setpapername manualfeed {true} {papersize} ifelse {manualpapersize} {false} ifelse {desperatepapersize} if end } def /pagesave FMLOCAL /orgmatrix FMLOCAL /landscape FMLOCAL /FMBEGINPAGE { FrameDict begin /pagesave save def 3.86 setmiterlimit /landscape exch 0 ne def landscape { 90 rotate 0 exch neg translate pop } {pop pop} ifelse xscale yscale scale /orgmatrix matrix def gsave } def /FMENDPAGE { grestore pagesave restore end showpage } def /FMFONTDEFINE { FrameDict begin findfont ReEncode 1 index exch definefont FMfonts 3 1 roll put end } def /FMFILLS { FrameDict begin array /fillvals exch def end } def /FMFILL { FrameDict begin fillvals 3 1 roll put end } def /FMNORMALIZEGRAPHICS { newpath 0.0 0.0 moveto 1 setlinewidth 0 setlinecap 0 0 0 sethsbcolor 0 setgray } bind def /fx FMLOCAL /fy FMLOCAL /fh FMLOCAL /fw FMLOCAL /llx FMLOCAL /lly FMLOCAL /urx FMLOCAL /ury FMLOCAL /FMBEGINEPSF { end /FMEPSF save def /showpage {} def FMNORMALIZEGRAPHICS [/fy /fx /fh /fw /ury /urx /lly /llx] {exch def} forall fx fy translate rotate fw urx llx sub div fh ury lly sub div scale llx neg lly neg translate } bind def /FMENDEPSF { FMEPSF restore FrameDict begin } bind def FrameDict begin /setmanualfeed { %%BeginFeature *ManualFeed True statusdict /manualfeed true put %%EndFeature } def /max {2 copy lt {exch} if pop} bind def /min {2 copy gt {exch} if pop} bind def /inch {72 mul} def /pagedimen { paperheight sub abs 16 lt exch paperwidth sub abs 16 lt and {/papername exch def} {pop} ifelse } def /papersizedict FMLOCAL /setpapername { /papersizedict 14 dict def papersizedict begin /papername /unknown def /Letter 8.5 inch 11.0 inch pagedimen /LetterSmall 7.68 inch 10.16 inch pagedimen /Tabloid 11.0 inch 17.0 inch pagedimen /Ledger 17.0 inch 11.0 inch pagedimen /Legal 8.5 inch 14.0 inch pagedimen /Statement 5.5 inch 8.5 inch pagedimen /Executive 7.5 inch 10.0 inch pagedimen /A3 11.69 inch 16.5 inch pagedimen /A4 8.26 inch 11.69 inch pagedimen /A4Small 7.47 inch 10.85 inch pagedimen /B4 10.125 inch 14.33 inch pagedimen /B5 7.16 inch 10.125 inch pagedimen end } def /papersize { papersizedict begin /Letter {lettertray letter} def /LetterSmall {lettertray lettersmall} def /Tabloid {11x17tray 11x17} def /Ledger {ledgertray ledger} def /Legal {legaltray legal} def /Statement {statementtray statement} def /Executive {executivetray executive} def /A3 {a3tray a3} def /A4 {a4tray a4} def /A4Small {a4tray a4small} def /B4 {b4tray b4} def /B5 {b5tray b5} def /unknown {unknown} def papersizedict dup papername known {papername} {/unknown} ifelse get end /FMdicttop countdictstack 1 add def statusdict begin stopped end countdictstack -1 FMdicttop {pop end} for } def /manualpapersize { papersizedict begin /Letter {letter} def /LetterSmall {lettersmall} def /Tabloid {11x17} def /Ledger {ledger} def /Legal {legal} def /Statement {statement} def /Executive {executive} def /A3 {a3} def /A4 {a4} def /A4Small {a4small} def /B4 {b4} def /B5 {b5} def /unknown {unknown} def papersizedict dup papername known {papername} {/unknown} ifelse get end stopped } def /desperatepapersize { statusdict /setpageparams known { paperwidth paperheight 0 1 statusdict begin {setpageparams} stopped pop end } if } def /savematrix { orgmatrix currentmatrix pop } bind def /restorematrix { orgmatrix setmatrix } bind def /dmatrix matrix def /dpi 72 0 dmatrix defaultmatrix dtransform dup mul exch dup mul add sqrt def /freq dpi 18.75 div 8 div round dup 0 eq {pop 1} if 8 mul dpi exch div def /sangle 1 0 dmatrix defaultmatrix dtransform exch atan def /DiacriticEncoding [ /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quotesingle /parenleft /parenright /asterisk /plus /comma /hyphen /period /slash /zero /one /two /three /four /five /six /seven /eight /nine /colon /semicolon /less /equal /greater /question /at /A /B /C /D /E /F /G /H /I /J /K /L /M /N /O /P /Q /R /S /T /U /V /W /X /Y /Z /bracketleft /backslash /bracketright /asciicircum /underscore /grave /a /b /c /d /e /f /g /h /i /j /k /l /m /n /o /p /q /r /s /t /u /v /w /x /y /z /braceleft /bar /braceright /asciitilde /.notdef /Adieresis /Aring /Ccedilla /Eacute /Ntilde /Odieresis /Udieresis /aacute /agrave /acircumflex /adieresis /atilde /aring /ccedilla /eacute /egrave /ecircumflex /edieresis /iacute /igrave /icircumflex /idieresis /ntilde /oacute /ograve /ocircumflex /odieresis /otilde /uacute /ugrave /ucircumflex /udieresis /dagger /.notdef /cent /sterling /section /bullet /paragraph /germandbls /registered /copyright /trademark /acute /dieresis /.notdef /AE /Oslash /.notdef /.notdef /.notdef /.notdef /yen /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /ordfeminine /ordmasculine /.notdef /ae /oslash /questiondown /exclamdown /logicalnot /.notdef /florin /.notdef /.notdef /guillemotleft /guillemotright /ellipsis /.notdef /Agrave /Atilde /Otilde /OE /oe /endash /emdash /quotedblleft /quotedblright /quoteleft /quoteright /.notdef /.notdef /ydieresis /Ydieresis /fraction /currency /guilsinglleft /guilsinglright /fi /fl /daggerdbl /periodcentered /quotesinglbase /quotedblbase /perthousand /Acircumflex /Ecircumflex /Aacute /Edieresis /Egrave /Iacute /Icircumflex /Idieresis /Igrave /Oacute /Ocircumflex /.notdef /Ograve /Uacute /Ucircumflex /Ugrave /dotlessi /circumflex /tilde /macron /breve /dotaccent /ring /cedilla /hungarumlaut /ogonek /caron ] def /ReEncode { dup length dict begin { 1 index /FID ne {def} {pop pop} ifelse } forall 0 eq {/Encoding DiacriticEncoding def} if currentdict end } bind def /graymode true def /bwidth FMLOCAL /bpside FMLOCAL /bstring FMLOCAL /onbits FMLOCAL /offbits FMLOCAL /xindex FMLOCAL /yindex FMLOCAL /x FMLOCAL /y FMLOCAL /setpattern { /bwidth exch def /bpside exch def /bstring exch def /onbits 0 def /offbits 0 def freq sangle landscape {90 add} if {/y exch def /x exch def /xindex x 1 add 2 div bpside mul cvi def /yindex y 1 add 2 div bpside mul cvi def bstring yindex bwidth mul xindex 8 idiv add get 1 7 xindex 8 mod sub bitshift and 0 ne {/onbits onbits 1 add def 1} {/offbits offbits 1 add def 0} ifelse } setscreen {} settransfer offbits offbits onbits add div FMsetgray /graymode false def } bind def /grayness { FMsetgray graymode not { /graymode true def orgxfer cvx settransfer orgfreq organgle orgproc cvx setscreen } if } bind def /HUE FMLOCAL /SAT FMLOCAL /BRIGHT FMLOCAL /Colors FMLOCAL FMPrintInColor { /HUE 0 def /SAT 0 def /BRIGHT 0 def % array of arrays Hue and Sat values for the separations [HUE BRIGHT] /Colors [[0 0 ] % black [0 0 ] % white [0.00 1.0] % red [0.37 1.0] % green [0.60 1.0] % blue [0.50 1.0] % cyan [0.83 1.0] % magenta [0.16 1.0] % comment / yellow ] def /BEGINBITMAPCOLOR { BITMAPCOLOR} def /BEGINBITMAPCOLORc { BITMAPCOLORc} def /BEGINBITMAPTRUECOLOR { BITMAPTRUECOLOR } def /BEGINBITMAPCOLORc { BITMAPTRUECOLORc } def /K { Colors exch get dup 0 get /HUE exch store 1 get /BRIGHT exch store HUE 0 eq BRIGHT 0 eq and {1.0 SAT sub setgray} {HUE SAT BRIGHT sethsbcolor} ifelse } def /FMsetgray { /SAT exch 1.0 exch sub store HUE 0 eq BRIGHT 0 eq and {1.0 SAT sub setgray} {HUE SAT BRIGHT sethsbcolor} ifelse } bind def } { /BEGINBITMAPCOLOR { BITMAPGRAY} def /BEGINBITMAPCOLORc { BITMAPGRAYc} def /BEGINBITMAPTRUECOLOR { BITMAPTRUEGRAY } def /BEGINBITMAPTRUECOLORc { BITMAPTRUEGRAYc } def /FMsetgray {setgray} bind def /K { pop } def } ifelse /normalize { transform round exch round exch itransform } bind def /dnormalize { dtransform round exch round exch idtransform } bind def /lnormalize { 0 dtransform exch cvi 2 idiv 2 mul 1 add exch idtransform pop } bind def /H { lnormalize setlinewidth } bind def /Z { setlinecap } bind def /fillvals FMLOCAL /X { fillvals exch get dup type /stringtype eq {8 1 setpattern} {grayness} ifelse } bind def /V { gsave eofill grestore } bind def /N { stroke } bind def /M {newpath moveto} bind def /E {lineto} bind def /D {curveto} bind def /O {closepath} bind def /n FMLOCAL /L { /n exch def newpath normalize moveto 2 1 n {pop normalize lineto} for } bind def /Y { L closepath } bind def /x1 FMLOCAL /x2 FMLOCAL /y1 FMLOCAL /y2 FMLOCAL /rad FMLOCAL /R { /y2 exch def /x2 exch def /y1 exch def /x1 exch def x1 y1 x2 y1 x2 y2 x1 y2 4 Y } bind def /RR { /rad exch def normalize /y2 exch def /x2 exch def normalize /y1 exch def /x1 exch def newpath x1 y1 rad add moveto x1 y2 x2 y2 rad arcto x2 y2 x2 y1 rad arcto x2 y1 x1 y1 rad arcto x1 y1 x1 y2 rad arcto closepath 16 {pop} repeat } bind def /C { grestore gsave R clip } bind def /FMpointsize FMLOCAL /F { FMfonts exch get FMpointsize scalefont setfont } bind def /Q { /FMpointsize exch def F } bind def /T { moveto show } bind def /RF { rotate 0 ne {-1 1 scale} if } bind def /TF { gsave moveto RF show grestore } bind def /P { moveto 0 32 3 2 roll widthshow } bind def /PF { gsave moveto RF 0 32 3 2 roll widthshow grestore } bind def /S { moveto 0 exch ashow } bind def /SF { gsave moveto RF 0 exch ashow grestore } bind def /B { moveto 0 32 4 2 roll 0 exch awidthshow } bind def /BF { gsave moveto RF 0 32 4 2 roll 0 exch awidthshow grestore } bind def /G { gsave newpath normalize translate 0.0 0.0 moveto dnormalize scale 0.0 0.0 1.0 5 3 roll arc closepath fill grestore } bind def /A { gsave savematrix newpath 2 index 2 div add exch 3 index 2 div sub exch normalize 2 index 2 div sub exch 3 index 2 div add exch translate scale 0.0 0.0 1.0 5 3 roll arc restorematrix stroke grestore } bind def /x FMLOCAL /y FMLOCAL /w FMLOCAL /h FMLOCAL /xx FMLOCAL /yy FMLOCAL /ww FMLOCAL /hh FMLOCAL /FMsaveobject FMLOCAL /FMoptop FMLOCAL /FMdicttop FMLOCAL /BEGINPRINTCODE { /FMdicttop countdictstack 1 add def /FMoptop count 4 sub def /FMsaveobject save def userdict begin /showpage {} def FMNORMALIZEGRAPHICS 3 index neg 3 index neg translate } bind def /ENDPRINTCODE { count -1 FMoptop {pop pop} for countdictstack -1 FMdicttop {pop end} for FMsaveobject restore } bind def /gn { 0 { 46 mul cf read pop 32 sub dup 46 lt {exit} if 46 sub add } loop add } bind def /str FMLOCAL /cfs { /str sl string def 0 1 sl 1 sub {str exch val put} for str def } bind def /ic [ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0223 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0223 0 {0 hx} {1 hx} {2 hx} {3 hx} {4 hx} {5 hx} {6 hx} {7 hx} {8 hx} {9 hx} {10 hx} {11 hx} {12 hx} {13 hx} {14 hx} {15 hx} {16 hx} {17 hx} {18 hx} {19 hx} {gn hx} {0} {1} {2} {3} {4} {5} {6} {7} {8} {9} {10} {11} {12} {13} {14} {15} {16} {17} {18} {19} {gn} {0 wh} {1 wh} {2 wh} {3 wh} {4 wh} {5 wh} {6 wh} {7 wh} {8 wh} {9 wh} {10 wh} {11 wh} {12 wh} {13 wh} {14 wh} {gn wh} {0 bl} {1 bl} {2 bl} {3 bl} {4 bl} {5 bl} {6 bl} {7 bl} {8 bl} {9 bl} {10 bl} {11 bl} {12 bl} {13 bl} {14 bl} {gn bl} {0 fl} {1 fl} {2 fl} {3 fl} {4 fl} {5 fl} {6 fl} {7 fl} {8 fl} {9 fl} {10 fl} {11 fl} {12 fl} {13 fl} {14 fl} {gn fl} ] def /sl FMLOCAL /val FMLOCAL /ws FMLOCAL /im FMLOCAL /bs FMLOCAL /cs FMLOCAL /len FMLOCAL /pos FMLOCAL /ms { /sl exch def /val 255 def /ws cfs /im cfs /val 0 def /bs cfs /cs cfs } bind def 400 ms /ip { is 0 cf cs readline pop { ic exch get exec add } forall pop } bind def /wh { /len exch def /pos exch def ws 0 len getinterval im pos len getinterval copy pop pos len } bind def /bl { /len exch def /pos exch def bs 0 len getinterval im pos len getinterval copy pop pos len } bind def /s1 1 string def /fl { /len exch def /pos exch def /val cf s1 readhexstring pop 0 get def pos 1 pos len add 1 sub {im exch val put} for pos len } bind def /hx { 3 copy getinterval cf exch readhexstring pop pop } bind def /h FMLOCAL /w FMLOCAL /d FMLOCAL /lb FMLOCAL /bitmapsave FMLOCAL /is FMLOCAL /cf FMLOCAL /wbytes { dup 8 eq {pop} {1 eq {7 add 8 idiv} {3 add 4 idiv} ifelse} ifelse } bind def /BEGINBITMAPBWc { 1 {} COMMONBITMAPc } bind def /BEGINBITMAPGRAYc { 8 {} COMMONBITMAPc } bind def /BEGINBITMAP2BITc { 2 {} COMMONBITMAPc } bind def /COMMONBITMAPc { /r exch def /d exch def gsave translate rotate scale /h exch def /w exch def /lb w d wbytes def sl lb lt {lb ms} if /bitmapsave save def r /is im 0 lb getinterval def ws 0 lb getinterval is copy pop /cf currentfile def w h d [w 0 0 h neg 0 h] {ip} image bitmapsave restore grestore } bind def /BEGINBITMAPBW { 1 {} COMMONBITMAP } bind def /BEGINBITMAPGRAY { 8 {} COMMONBITMAP } bind def /BEGINBITMAP2BIT { 2 {} COMMONBITMAP } bind def /COMMONBITMAP { /r exch def /d exch def gsave translate rotate scale /h exch def /w exch def /bitmapsave save def r /is w d wbytes string def /cf currentfile def w h d [w 0 0 h neg 0 h] {cf is readhexstring pop} image bitmapsave restore grestore } bind def /proc1 FMLOCAL /proc2 FMLOCAL /newproc FMLOCAL /Fmcc { /proc2 exch cvlit def /proc1 exch cvlit def /newproc proc1 length proc2 length add array def newproc 0 proc1 putinterval newproc proc1 length proc2 putinterval newproc cvx } bind def /ngrayt 256 array def /nredt 256 array def /nbluet 256 array def /ngreent 256 array def /gryt FMLOCAL /blut FMLOCAL /grnt FMLOCAL /redt FMLOCAL /indx FMLOCAL /cynu FMLOCAL /magu FMLOCAL /yelu FMLOCAL /k FMLOCAL /u FMLOCAL /colorsetup { currentcolortransfer /gryt exch def /blut exch def /grnt exch def /redt exch def 0 1 255 { /indx exch def /cynu 1 red indx get 255 div sub def /magu 1 green indx get 255 div sub def /yelu 1 blue indx get 255 div sub def /k cynu magu min yelu min def /u k currentundercolorremoval exec def nredt indx 1 0 cynu u sub max sub redt exec put ngreent indx 1 0 magu u sub max sub grnt exec put nbluet indx 1 0 yelu u sub max sub blut exec put ngrayt indx 1 k currentblackgeneration exec sub gryt exec put } for {255 mul cvi nredt exch get} {255 mul cvi ngreent exch get} {255 mul cvi nbluet exch get} {255 mul cvi ngrayt exch get} setcolortransfer {pop 0} setundercolorremoval {} setblackgeneration } bind def /tran FMLOCAL /fakecolorsetup { /tran 256 string def 0 1 255 {/indx exch def tran indx red indx get 77 mul green indx get 151 mul blue indx get 28 mul add add 256 idiv put} for currenttransfer {255 mul cvi tran exch get 255.0 div} exch Fmcc settransfer } bind def /BITMAPCOLOR { /d 8 def gsave translate rotate scale /h exch def /w exch def /bitmapsave save def colorsetup /is w d wbytes string def /cf currentfile def w h d [w 0 0 h neg 0 h] {cf is readhexstring pop} {is} {is} true 3 colorimage bitmapsave restore grestore } bind def /BITMAPCOLORc { /d 8 def gsave translate rotate scale /h exch def /w exch def /lb w d wbytes def sl lb lt {lb ms} if /bitmapsave save def colorsetup /is im 0 lb getinterval def ws 0 lb getinterval is copy pop /cf currentfile def w h d [w 0 0 h neg 0 h] {ip} {is} {is} true 3 colorimage bitmapsave restore grestore } bind def /BITMAPTRUECOLORc { gsave translate rotate scale /h exch def /w exch def /bitmapsave save def /is w string def ws 0 w getinterval is copy pop /cf currentfile def w h 8 [w 0 0 h neg 0 h] {ip} {gip} {bip} true 3 colorimage bitmapsave restore grestore } bind def /BITMAPTRUECOLOR { gsave translate rotate scale /h exch def /w exch def /bitmapsave save def /is w string def /gis w string def /bis w string def /cf currentfile def w h 8 [w 0 0 h neg 0 h] { cf is readhexstring pop } { cf gis readhexstring pop } { cf bis readhexstring pop } true 3 colorimage bitmapsave restore grestore } bind def /BITMAPTRUEGRAYc { gsave translate rotate scale /h exch def /w exch def /bitmapsave save def /is w string def ws 0 w getinterval is copy pop /cf currentfile def w h 8 [w 0 0 h neg 0 h] {ip gip bip w gray} image bitmapsave restore grestore } bind def /ww FMLOCAL /r FMLOCAL /g FMLOCAL /b FMLOCAL /i FMLOCAL /gray { /ww exch def /b exch def /g exch def /r exch def 0 1 ww 1 sub { /i exch def r i get .299 mul g i get .587 mul b i get .114 mul add add r i 3 -1 roll floor cvi put } for r } bind def /BITMAPTRUEGRAY { gsave translate rotate scale /h exch def /w exch def /bitmapsave save def /is w string def /gis w string def /bis w string def /cf currentfile def w h 8 [w 0 0 h neg 0 h] { cf is readhexstring pop cf gis readhexstring pop cf bis readhexstring pop w gray} image bitmapsave restore grestore } bind def /BITMAPGRAY { 8 {fakecolorsetup} COMMONBITMAP } bind def /BITMAPGRAYc { 8 {fakecolorsetup} COMMONBITMAPc } bind def /ENDBITMAP { } bind def end /ALDsave FMLOCAL /ALDmatrix matrix def ALDmatrix currentmatrix pop /StartALD { /ALDsave save def savematrix ALDmatrix setmatrix } bind def /InALD { restorematrix } bind def /DoneALD { ALDsave restore } bind def %%EndProlog %%BeginSetup (3.0) FMVERSION 1 1 612 792 0 1 15 FMDOCUMENT 0 0 /Times-Roman FMFONTDEFINE 1 0 /Times-Italic FMFONTDEFINE 2 0 /Courier FMFONTDEFINE 3 0 /Times-BoldItalic FMFONTDEFINE 4 1 /Symbol FMFONTDEFINE 5 0 /Times-Bold FMFONTDEFINE 32 FMFILLS 0 0 FMFILL 1 .1 FMFILL 2 .3 FMFILL 3 .5 FMFILL 4 .7 FMFILL 5 .9 FMFILL 6 .97 FMFILL 7 1 FMFILL 8 <0f1e3c78f0e1c387> FMFILL 9 <0f87c3e1f0783c1e> FMFILL 10 FMFILL 11 FMFILL 12 <8142241818244281> FMFILL 13 <03060c183060c081> FMFILL 14 <8040201008040201> FMFILL 16 1 FMFILL 17 .9 FMFILL 18 .7 FMFILL 19 .5 FMFILL 20 .3 FMFILL 21 .1 FMFILL 22 0.03 FMFILL 23 0 FMFILL 24 FMFILL 25 FMFILL 26 <3333333333333333> FMFILL 27 <0000ffff0000ffff> FMFILL 28 <7ebddbe7e7dbbd7e> FMFILL 29 FMFILL 30 <7fbfdfeff7fbfdfe> FMFILL %%EndSetup %%Page: "1" 1 %%BeginPaperSize: Letter %%EndPaperSize 612 792 0 FMBEGINPAGE 0 12 Q 0 X 0 K (Page 1) 504.69 28 T 1 24 Q (The Layout Widget) 221.92 652 T 1 18 Q (A Hierarchical Constraint Based Widget) 159.08 600 T 1 14 Q (Keith Packard) 262.64 543.07 T 1 11 Q (\240) 343.87 548.67 T 1 18 Q (Abstract) 72 484.4 T 0 12 Q 0.77 (The X Toolkit) 72 460.4 P 2 F 1.86 ([Swick90]) 145.95 460.4 P 0 F 0.77 ( geometry management process is extremely flexible and powerful;) 210.71 460.4 P -0.16 (however the existing composite widget classes make it difficult for the application developer both) 72 445.9 P -0.63 (to simply design an application layout, and even more important, to make the layout work in a wide) 72 431.4 P (variety of environments.) 72 416.9 T 0.75 0.5 (The Layout widget class is described which uses a stretch/shrink model similar to) 72 393.4 B 0.45 -0.7 (T) 517.76 393.4 B 0 14 Q 0.35 -1.6 (E) 524.39 388.9 B 0 12 Q 0.75 0.5 (X) 531.34 393.4 B 2 F 0.05 ([Knuth84]) 72 378.9 P 0 F 0.02 (to constrain the layout of an application in a manner which allows the geometry of) 144.01 378.9 P 0.5 (the children to respect the desires of the application designer, while adapting to its environment,) 72 364.4 P 0.33 (both in terms of the changing geometry allocated to the widget, and to the changing needs of the) 72 349.9 P 0.75 0.05 (child widgets. The specification of the child layout is entirely contained in a resource which is) 72 335.4 B (interpreted at run time.) 72 320.9 T 1 18 Q (Introduction) 72 278.4 T 1 14 Q (History) 72 245.07 T 0 12 Q -0.37 (Over a year ago, I was rewriting the user interface of a game program \050xmille\051 to use the X Toolkit) 72 222.4 P 1.38 (and Athena widget set instead of the original X10 based user interface. When it came time to) 72 207.9 P 0.21 (construct a widget tree which could represent the layout of the various widgets, I started with the) 72 193.4 P -0.53 (Athena composite widgets \050Paned, Box, and Form\051 and tried to come up with a general mechanism) 72 178.9 P -0.19 (which would automatically choose the correct size for the application, regardless of the fonts used) 72 164.4 P (and also allow the application to be resized in a useful manner.) 72 149.9 T 72 118.17 540 132.57 C 72 118.17 540 132.57 R 7 X 0 K V 72 123.57 252 123.57 2 L V 0.5 H 2 Z 0 X N 0 0 612 792 C 1 8 Q 0 X 0 K -0.21 (\240) 72 112.83 P 1 10 Q -0.27 (Keith Packard \050) 76 108.83 P 3 F -0.27 (keithp@ncd.com) 139.31 108.83 P 1 F -0.27 (\051 has been a member of the Network Computing Devices engineering staff since last) 207.87 108.83 P 0.16 (June. Prior to that, he was the senior staff member at the X Consortium. He has worked with and participated in the) 72 96.33 P (design of much of the X Window System since early days of the X Consortium.) 72 83.83 T FMENDPAGE %%EndPage: "1" 2 %%Page: "2" 2 612 792 0 FMBEGINPAGE 0 12 Q 0 X 0 K (Page 2) 504.69 28 T -0.29 (After a few hours of frustration, it became clear that no combination of the existing widgets would) 72 712 P -0.09 (do what I wanted. I gave up the project and went on to other things. In the back of my mind, I had) 72 697.5 P 0.81 (an idea that a new composite widget class which could solve the xmille problem would also be) 72 683 P 0.29 (generally useful for a wide variety of layout problems, so I started to design what would become) 72 668.5 P (the Layout widget.) 72 654 T -0.56 (What I wanted was a general 2D layout system which would take into consideration both some sort) 72 630.5 P 0.19 (of abstract geometrical description of the layout along with the sizes of the child widgets and \324do) 72 616 P (the right thing\325 to produce a reasonable presentation on the screen.) 72 601.5 T 1 14 Q (Goals) 72 568.67 T 0 12 Q (The Layout widget tries to meet the following goals:) 72 546 T (\245) 90 525.5 T (Flexible.) 108 525.5 T 0.29 ( The widget should be usable in every situation to which it might be applied. This relates both to) 72 502 P 1.44 (how the external system constrains the space available to the widget as well as how the child) 72 487.5 P -0.08 (widgets relate to their parent. For example, the user should be able to use a different font resource) 72 473 P 1.31 (and have the parent widget adapt automatically and reasonably, giving the child widgets more) 72 458.5 P -0.16 (space as needed. An application should be able to be run in an extremely limited amount of space,) 72 444 P -0.69 (and yet still provide useful information to the user. Widgets containing more important information) 72 429.5 P (should be given more space than others.) 72 415 T (\245) 90 394.5 T (Easy to use.) 108 394.5 T 2.03 (Constructing the widget layout should be straightforward and intuitive. The constraint model) 72 371 P 1.3 (should be easily understood by all developers. Some flexibility in what layouts were available) 72 356.5 P (could be limited in order to make the system substantially easier to use.) 72 342 T (\245) 90 321.5 T (Readily modifiable) 108 321.5 T 3.15 (When developing the user interface, the layout should be quickly modifiable to allow the) 72 298 P 2.25 (programmer to try alternative layouts without extensive work. In fact, by placing the layout) 72 283.5 P -0.72 (description in a resource attached to the widget, even the end user can adjust the layout as necessary) 72 269 P 2.14 (to work in different environments. This allows the user to reconstruct the appearance of the) 72 254.5 P (application.) 72 240 T (\245) 90 219.5 T (Predictable.) 108 219.5 T -0.29 (In every situation to which the layout could be subjected, the results on the screen should be easily) 72 196 P -0.13 (predicted from the layout description. If the results on the screen surprise the developer, just think) 72 181.5 P -0.65 (what they\325re going to do to the user. The developer is not going to run the application in all possible) 72 167 P 0.98 (environments. Therefore, they should be able to create a layout description which can run with) 72 152.5 P (almost any limitations.) 72 138 T FMENDPAGE %%EndPage: "2" 3 %%Page: "3" 3 612 792 0 FMBEGINPAGE 0 12 Q 0 X 0 K (Page 3) 504.69 28 T 1 18 Q (Solution) 72 708 T 1 14 Q (Introduction) 72 674.67 T 0 12 Q -0.04 (The design of the Layout widget was a long process; several iterations were required to transform) 72 652 P 0.22 (the idea of a general 2D layout system into something which is actually useful. I will go over the) 72 637.5 P -0.05 (basic requirements of any solution and how those requirements meet the goals stated above. Then) 72 623 P 0.37 (I will discuss the constraint system and how a hierarchical approach is useful. Finally I will give) 72 608.5 P (some complete working examples.) 72 594 T 1 14 Q (Requirements of the solution) 72 561.17 T 0 12 Q (These follow directly from the goals of the project.) 72 538.5 T (\245) 90 518 T (Constraint based.) 108 518 T -0.26 (This requirement follows from goals of flexibility and predictability. A constraint based system, if) 72 494.5 P 1.56 (correctly specified, can be modeled easily by the programmer and will automatically adapt to) 72 480 P -0.31 (changing circumstances. By always constructing the layout based on the constraints, the results on) 72 465.5 P (the screen are be predictable.) 72 451 T (\245) 90 430.5 T (Hierarchical) 108 430.5 T -0.6 (This means that sets of child widgets are grouped together and treated as a composite object \050which) 72 407 P 0.4 (can be grouped with other objects etc.\051. This allows the user interface to be constructed in larger) 72 392.5 P (pieces, making the interactions the developer has to understand limited to a single level.) 72 378 T (\245) 90 357.5 T (Respect child widget geometry) 108 357.5 T 0.96 (When a child widget requests a change it its geometry, the request should be honored as far as) 72 334 P 1.02 (possible. This allows the geometry to reflect not only the desires of the developer, but also the) 72 319.5 P (requirements for each widget.) 72 305 T (\245) 90 284.5 T (Be useful for almost all 2D layouts) 108 284.5 T -0.13 (Where this doesn\325t conflict with the goal of usability, the system should be designed to provide as) 72 261 P (much flexibility in describing the layout as possible.) 72 246.5 T 1 14 Q (The Pieces of the System) 72 213.67 T 0 12 Q 3.28 (The Layout widget consists of several cooperating ideas. First, the layout is be described) 72 191 P 3 (hierarchically. Next constraints are associated with each object. Finally, the constraints are) 72 176.5 P (propagated up the hierarchy to produce the desired layout.) 72 162 T 1 14 Q (Hierarchical construction) 72 129.17 T 0 12 Q 0.94 (The Layout widget composes objects into boxes stacking them either vertically or horizontally.) 72 106.5 P -0.04 (These boxes can then be composed along with other objects into other boxes. At the top level, the) 72 92 P 0.27 (Layout widget contains a single box. To specify the layout for a window, the proposed geometry) 72 77.5 P FMENDPAGE %%EndPage: "3" 4 %%Page: "4" 4 612 792 0 FMBEGINPAGE 0 12 Q 0 X 0 K (Page 4) 504.69 28 T 0.18 (is first broken down into boxes. Figure 1 shows an example of a simple dialog box which fits the) 72 712 P (hierarchical layout system quite nicely. Dashed lines indicate how the dialog can be broken down) 72 697.5 T (into boxes, each box containing objects aligned either vertically or horizontally.) 72 683 T 1 F (Figure 1: Example Dialog Box) 231.72 510.5 T 0 F (The hierarchy of these boxes is specified as:) 72 487 T (Vertical Box containing:) 108 466.5 T (Label Widget) 144 452 T (Text Insertion Field) 144 437.5 T (Horizontal Box containing:) 144 423 T (Button 1) 180 408.5 T (Button 2) 180 394 T 1.33 (By specifying the layout hierarchically, the problem of how to position objects relative to one) 72 370.5 P (another has been reduced to a problem of how to arrange objects next to one another in a box.) 72 356 T -0.1 (This system is in contrast to a constraint system using a flat topology, where every child widget is) 72 332.5 P 0.33 (treated at the same level and is placed in the constraint system individually. In such a system the) 72 318 P (above layout might be described as follows:) 72 303.5 T (Top of the Label is bound to the top of the composite widget) 108 268.5 T (Left edge of the Label is bound to the left side of the composite widget) 108 254 T (Right edge of the Label is bound to the right edge of the composite widget) 108 239.5 T (Bottom of the Label is bound to the top of the Text Insertion Field) 108 225 T (Left edge of the Text Insertion Field is bound to the left side of the composite widget) 108 210.5 T (Right edge of the Text Insertion Field is bound to the right side of the composite widget) 108 196 T (Bottom of the Text Insertion Field is bound to the top of Button 1) 108 181.5 T (Bottom of the Text Insertion Field is bound to the top of Button 2) 108 167 T (Bottom of Button 1 is bound to the bottom of the composite widget) 108 152.5 T (Bottom of Button 2 is bound to the bottom of the composite widget) 108 138 T (Left edge of Button 1 is bound to the left side of the composite widget) 108 123.5 T (Right edge of Button 1 is bound to the left edge of Button 2) 108 109 T (Right edge of Button 2 is bound to the right side of the composite widget) 108 94.5 T 72 72 540 720 C 163.12 527 448.88 655.5 C 167.62 528.5 446.62 650 R 7 X 0 K V 0.5 H 2 Z 0 X N 0 12 Q (Label Widget) 194.62 623 T 194.62 582.5 325.12 605 R 7 X V 0 X N (Text Insertion Field) 203.62 587 T 194.62 537.5 262.12 564.5 13.5 RR 7 X V 0 X N (Button 1) 208.12 546.5 T 338.62 537.5 410.62 564.5 13.5 RR 7 X V 0 X N (Button 2) 352.12 546.5 T 167.62 614 446.62 614 2 L 2 X V 8 X N 167.62 573.5 446.62 573.5 2 L N 298.12 573.5 298.12 528.5 2 L N 72 72 540 720 C 0 0 612 792 C FMENDPAGE %%EndPage: "4" 5 %%Page: "5" 5 612 792 0 FMBEGINPAGE 0 12 Q 0 X 0 K (Page 5) 504.69 28 T -0.29 (Unless some structure is given to these constraints, the number grows quickly with the complexity) 72 712 P 1.23 (of the layout. However, a non-hierarchical systems could also solve many problems which the) 72 697.5 P (Layout widget does not address. A layout like:) 72 683 T 1 F (Figure 2: An example of something Layout doesn\325t do) 176.42 493 T 0 F 0.96 (cannot be described using a hierarchy of horizontal and vertical boxes. As such layouts are not) 72 469.5 P -0.02 (really all that useful, and the ability to combine objects together into composite objects makes the) 72 455 P -0.25 (problem of describing the layout significantly easier, I decided to forgo the flat layout in lieu of an) 72 440.5 P (easier to use system.) 72 426 T 1 14 Q (The Layout Constraint System) 72 393.17 T 0 12 Q -0.51 (A Constraint system consists of a collection of statements \050constraints\051 about the relations between) 72 370.5 P 0.66 (the objects to be laid out which should be all made simultaneously true, whenever possible. For) 72 356 P 2.74 (example, referring to Figure 1 above, the following constraints could be used to guide the) 72 341.5 P (positioning of the objects:) 72 327 T (\245) 90 306.5 T (The Label, the Text Insertion Field and Button 2should be left aligned.) 108 306.5 T (\245) 90 286 T 0.35 (Those elements should be spaced about the same distance from the left edge as the Label) 108 286 P (is tall.) 108 271.5 T (\245) 90 251 T -0.28 (Button 2 should be spaced from the right edge the same amount as Button 1 is spaced from) 108 251 P (the left edge) 108 236.5 T (\245) 90 216 T (The buttons should be spaced about three times as far apart as they are from each edge.) 108 216 T (\245) 90 195.5 T 0.2 (The Text Insertion Field shouldn\325t shrink below a \324reasonable\325 size; however it should be) 108 195.5 P (given as much space as it needs; even if the dialog box must get larger.) 108 181 T (\245) 90 160.5 T -0.61 (The vertical spacing between all of the various fields should be about the same as the height) 108 160.5 P (of the Label.) 108 146 T -0.35 (Given this description of the constraints desired, a system for describing them is needed. There are) 72 122.5 P (several options, the one Layout uses is derived from the TeX \324Boxes and Glue\325 model.) 72 108 T 72 72 540 720 C 205.59 509.5 406.41 655.5 C 205.59 516 403.59 651 R 0.5 H 2 Z 0 X 0 K N 210.09 601.5 331.59 646.5 R N 336.09 561 399.09 646.5 R N 273.09 520.5 399.09 556.5 R N 210.09 520.5 268.59 597 R N 0 12 Q (Text Insertion Field) 219.09 615 T (Label) 349.59 601.5 T (Button 1) 214.59 556.5 T (Button 2) 300.09 534 T 72 72 540 720 C 0 0 612 792 C FMENDPAGE %%EndPage: "5" 6 %%Page: "6" 6 612 792 0 FMBEGINPAGE 0 12 Q 0 X 0 K (Page 6) 504.69 28 T 1 F (The Layout model) 72 712 T 0 F (In the Layout constraint system, the objects grouped into a box have three attributes) 72 689 T (\245) 90 668.5 T (Natural size) 108 668.5 T 2.01 (This is the size which this object would like to be. For widgets, this is the size reported by) 72 645 P (XtQueryGeometry.) 72 630.5 T (\245) 90 610 T (Stretch) 108 610 T ( This is a value which indicates the willingness of this object to grow beyond the its natural size.) 72 586.5 T (\245) 90 566 T (Shrink) 108 566 T -0.74 ( This is a value which indicates the willingness of this object to become smaller than its natural size.) 72 542.5 P 0.64 (Both stretch and shrink can be additionally attributed with an order of \324infinity\325. This works the) 72 519 P 0.2 (same way infinity works with TeX glue\321a value of one order of infinity is larger than any value) 72 504.5 P 0.36 (of any smaller order of infinity \050e.g. 1) 72 490 P 4 F 0.36 (\245) 259.39 490 P 0 10 Q 0.3 (2) 267.95 494.8 P 0 12 Q 0.36 ( > 1000) 272.94 490 P 4 F 0.36 (\245) 313.76 490 P 0 10 Q 0.3 (1) 322.31 494.8 P 0 12 Q 0.36 (\051. Each object has a natural size, stretch and) 327.31 490 P 2.38 (shrink in both dimensions. Shrink values of order zero are a special case; they indicate the) 72 475.5 P 0.37 (maximum amount that an object can be shrunk by. The Layout widget will not reduce the object) 72 461 P (by more than this amount.) 72 446.5 T -0.44 (Boxes are composed along either a vertical or horizontal axis. All of the objects in a box are placed) 72 423 P 0.2 (along this axis. Once the size of a box is determined, the size and placement of the objects inside) 72 408.5 P -0.09 (it can be computed. Normal to the axis of the box, each object is placed at the edge of the box and) 72 394 P 0.15 (made as large as the box. Along the axis of the box, each object is given a piece of the difference) 72 379.5 P -0.4 (between the actual size of the box and the natural size of the box. When) 72 365 P -0.4 ( has a smaller) 475.59 365 P -0.6 (order of infinity than) 72 350.5 P -0.6 (, the object is left in its natural size. Otherwise, if the box is smaller) 224.22 350.5 P (than its natural size, each object is shrunk in proportion to its shrink value:) 72 336 T -0.24 (Similarly, when the box is larger than its natural size, each object with stretch of the same order of) 72 240.5 P (infinity as the stretch value of the box is resized in the ratio:) 72 226 T (These equations are intended to be identical to the TeX model.) 72 130.5 T 413.67 358.37 475.59 374.2 C 1 12 Q 0 X 0 K (S) 414.67 365 T (h) 421.38 365 T (r) 428.08 365 T (i) 433.45 365 T (n) 437.49 365 T (k) 444.19 365 T 1 9 Q (o) 449.98 361.22 T (b) 455 361.22 T (j) 460.03 361.22 T (e) 463.05 361.22 T (c) 467.58 361.22 T (t) 472.1 361.22 T 0 0 612 792 C 172.88 343.87 224.22 359.7 C 1 12 Q 0 X 0 K (S) 173.88 350.5 T (h) 180.58 350.5 T (r) 187.28 350.5 T (i) 192.66 350.5 T (n) 196.7 350.5 T (k) 203.4 350.5 T 1 9 Q (b) 209.18 346.72 T (o) 214.21 346.72 T (x) 219.23 346.72 T 0 0 612 792 C 72 72 540 720 C 78 260 534 332 C 1 12 Q 0 X 0 K (S) 137.45 293.5 T (i) 144.15 293.5 T (z) 148.19 293.5 T (e) 153.57 293.5 T 1 9 Q (o) 159.35 289.72 T (b) 164.38 289.72 T (j) 169.4 289.72 T (e) 172.43 289.72 T (c) 176.95 289.72 T (t) 181.47 289.72 T 1 12 Q (N) 202.55 293.5 T (a) 211.25 293.5 T (t) 217.96 293.5 T (u) 222 293.5 T (r) 228.7 293.5 T (a) 234.07 293.5 T (l) 240.77 293.5 T 1 9 Q (o) 244.57 289.72 T (b) 249.59 289.72 T (j) 254.62 289.72 T (e) 257.64 289.72 T (c) 262.16 289.72 T (t) 266.68 289.72 T 1 12 Q (S) 282.77 303.72 T (h) 289.47 303.72 T (r) 296.17 303.72 T (i) 301.54 303.72 T (n) 305.58 303.72 T (k) 312.29 303.72 T 1 9 Q (o) 318.07 299.94 T (b) 323.1 299.94 T (j) 328.12 299.94 T (e) 331.15 299.94 T (c) 335.67 299.94 T (t) 340.18 299.94 T 1 12 Q (S) 288.05 285.89 T (h) 294.76 285.89 T (r) 301.46 285.89 T (i) 306.83 285.89 T (n) 310.87 285.89 T (k) 317.57 285.89 T 1 9 Q (b) 323.36 282.11 T (o) 328.38 282.11 T (x) 333.4 282.11 T 1 12 Q (N) 363.37 293.5 T (a) 372.07 293.5 T (t) 378.78 293.5 T (u) 382.82 293.5 T (r) 389.52 293.5 T (a) 394.89 293.5 T (l) 401.59 293.5 T 1 9 Q (b) 405.39 289.72 T (o) 410.41 289.72 T (x) 415.43 289.72 T 1 12 Q (S) 432.01 293.5 T (i) 438.71 293.5 T (z) 442.75 293.5 T (e) 448.12 293.5 T 1 9 Q (b) 453.91 289.72 T (o) 458.93 289.72 T (x) 463.96 289.72 T 4 12 Q (-) 422.43 293.5 T (\050) 358.27 293.5 T (\051) 468.55 293.5 T (\264) 346.68 293.5 T (-) 272.18 293.5 T (=) 189.96 293.5 T 282.77 296.09 342.43 296.09 2 L 0.33 H 0 Z N 72 72 540 720 C 0 0 612 792 C 72 72 540 720 C 78 150 534 222 C 1 12 Q 0 X 0 K (S) 138.18 183.5 T (i) 144.89 183.5 T (z) 148.93 183.5 T (e) 154.3 183.5 T 1 9 Q (o) 160.08 179.72 T (b) 165.11 179.72 T (j) 170.13 179.72 T (e) 173.16 179.72 T (c) 177.68 179.72 T (t) 182.2 179.72 T 1 12 Q (N) 203.28 183.5 T (a) 211.99 183.5 T (t) 218.69 183.5 T (u) 222.73 183.5 T (r) 229.43 183.5 T (a) 234.8 183.5 T (l) 241.51 183.5 T 1 9 Q (o) 245.3 179.72 T (b) 250.32 179.72 T (j) 255.35 179.72 T (e) 258.38 179.72 T (c) 262.89 179.72 T (t) 267.42 179.72 T 1 12 Q (S) 283.5 193.72 T (t) 290.2 193.72 T (r) 294.24 193.72 T (e) 299.61 193.72 T (t) 305.64 193.72 T (c) 309.68 193.72 T (h) 315.71 193.72 T 1 9 Q (o) 322.17 189.94 T (b) 327.2 189.94 T (j) 332.22 189.94 T (e) 335.25 189.94 T (c) 339.77 189.94 T (t) 344.29 189.94 T 1 12 Q (S) 283.5 175.89 T (t) 290.2 175.89 T (r) 294.24 175.89 T (e) 299.61 175.89 T (t) 305.64 175.89 T (c) 309.68 175.89 T (h) 315.71 175.89 T 1 9 Q (o) 322.17 172.11 T (b) 327.2 172.11 T (j) 332.22 172.11 T (e) 335.25 172.11 T (c) 339.77 172.11 T (t) 344.29 172.11 T 1 12 Q (S) 367.47 183.5 T (i) 374.17 183.5 T (z) 378.21 183.5 T (e) 383.58 183.5 T 1 9 Q (b) 389.37 179.72 T (o) 394.39 179.72 T (x) 399.42 179.72 T 1 12 Q (N) 411.16 183.5 T 4 F (-) 404.11 183.5 T 1 F (a) 419.86 183.5 T (t) 426.57 183.5 T (u) 430.61 183.5 T (r) 437.31 183.5 T (a) 442.68 183.5 T (l) 449.38 183.5 T 1 9 Q (b) 453.18 179.72 T (o) 458.2 179.72 T (x) 463.23 179.72 T 4 12 Q (\050) 362.37 183.5 T (\051) 467.82 183.5 T (\264) 350.78 183.5 T (+) 272.91 183.5 T (=) 190.7 183.5 T 283.5 186.09 346.53 186.09 2 L 0.33 H 0 Z N 72 72 540 720 C 0 0 612 792 C FMENDPAGE %%EndPage: "6" 7 %%Page: "7" 7 612 792 0 FMBEGINPAGE 0 12 Q 0 X 0 K (Page 7) 504.69 28 T 1 14 Q (Inherited Stretch and Shrink) 72 710.67 T 0 12 Q (Insanity is hereditary: you get it from your kids.) 72 688 T 1.2 (In a similar fashion, boxes inherit their natural size, stretch and shrink values from the objects) 72 664.5 P (contained in them. An alternative method which was tested and discarded had explicit stretch and) 72 650 T 0.78 (shrink values for these composite objects. This was not reasonable because it forced the user to) 72 635.5 P -0.08 (generate many more values for the objects, and it meant that the attributes of the primitive objects) 72 621 P 0.49 (could be overridden by the attributes of the composite objects above them in the tree. It is much) 72 606.5 P 1.02 (easier to have the Layout widget compute \324reasonable\325 values for the composite boxes and use) 72 592 P -0.24 (them instead. Some trouble was taken over the development of the mechanism for inheriting these) 72 577.5 P (values.) 72 563 T (Along the axis of the box, the simplest model also turns out to work exceedingly well:) 72 539.5 T (\245) 90 519 T (The natural size is the total of the natural sizes of the objects.) 108 519 T (\245) 90 498.5 T (The stretch is the sum of the child objects stretch values.) 108 498.5 T (\245) 90 478 T (The shrink is the sum of the child objects shrink values.) 108 478 T -0.58 (The sum of stretch and shrink values is complicated by the notion of infinity: the sum of two values) 72 454.5 P 1 F 0.34 (a) 72 440 P 0 F 0.34 (and) 81.33 440 P 1 F 0.34 (b) 101.98 440 P 0 F 0.34 (has order of the maximum of the orders of) 111.31 440 P 1 F 0.34 (a) 320.5 440 P 0 F 0.34 (and) 329.83 440 P 1 F 0.34 ( b) 347.15 440 P 0 F 0.34 (. If) 356.48 440 P 1 F 0.34 (a) 374.13 440 P 0 F 0.34 (and) 383.46 440 P 1 F 0.34 (b) 404.11 440 P 0 F 0.34 ( are of the same order then) 410.11 440 P -0.23 (the base value is the sum of the base values of) 72 425.5 P 1 F -0.23 (a) 293.62 425.5 P 0 F -0.23 (and) 302.39 425.5 P 1 F -0.23 (b.) 322.47 425.5 P 0 F -0.23 (Otherwise, it is the base value of the one of) 334.24 425.5 P (larger order.) 72 411 T 0.19 (Normal to the axis of the box, things are different and no obvious method jumps out. Because all) 72 387.5 P 0.22 (of the objects in the box are expanded to the size of the box along this dimension, the constraints) 72 373 P 1.12 (for the box must take all of the objects into account. The attributes are designed to respect the) 72 358.5 P 0.48 (requests of the most rigid child object; this will automatically satisfy the constraints of all of the) 72 344 P (other objects.) 72 329.5 T (\245) 90 309 T 0.62 (The natural size is the natural size of the object with the smallest order of infinity for its) 108 309 P 0.86 (shrink attribute. If more than one object qualifies, the largest natural size is used. Either) 108 294.5 P 0.52 (stretch or shrink could have been chosen here, but shrinking a box usually requires more) 108 280 P (care than stretching.) 108 265.5 T (\245) 90 245 T (The stretch is the minimum of all of the stretch values.) 108 245 T (\245) 90 224.5 T -0.6 (The shrink value is essentially the minimum of the shrink values. However, if the minimum) 108 224.5 P 0.57 (shrink order is zero, then the shrink value is the natural size of the box minus the largest) 108 210 P 0.13 (minimum size of objects of shrink order zero. This seems complicated, but the effect is to) 108 195.5 P 0.53 (honor the special semantics for shrink of order zero by not shrinking any object by more) 108 181 P (than its shrink value.) 108 166.5 T -0.25 (The minimum of stretch and shrink values is also complicated by infinite values. The minimum of) 72 143 P -0.12 (two values) 72 128.5 P 1 F -0.12 (a) 126.38 128.5 P 0 F -0.12 (and) 135.26 128.5 P 1 F -0.12 (b) 155.45 128.5 P 0 F -0.12 (is the value with minimal order, unless they have the same order in which case) 164.32 128.5 P (it is the value with a smaller base value.) 72 114 T FMENDPAGE %%EndPage: "7" 8 %%Page: "8" 8 612 792 0 FMBEGINPAGE 0 12 Q 0 X 0 K (Page 8) 504.69 28 T 1 14 Q (Glue) 72 710.67 T 0 12 Q 0.39 (The above discussion leaves out one piece of the puzzle: how to space widgets so that they have) 72 688 P -0.1 (blank areas between them. Glue is just another object which can be composed in a box along with) 72 673.5 P 0.48 (other boxes and widgets. It has the three attributes of natural size, stretch and shrink; however it) 72 659 P 0.63 (only has these attributes for the axis of the box in which it resides. Along the other axis, glue is) 72 644.5 P (simply ignored.) 72 630 T 1 14 Q (The Layout Language) 72 597.17 T 0 12 Q 1.41 (Instead of using the Xt constraint mechanism, which attaches constraint resources to the child) 72 574.5 P -0.08 (widgets, the Layout widget has a single resource which describes the geometry of the entire set of) 72 560 P 1.38 (widgets. This is because describing the hierarchy is most clearly done in a single place, using) 72 545.5 P 3.05 (indentation or some other syntactic sugar to clearly indicate the structure, and placing the) 72 531 P 1.75 (constraints separate from the hierarchy makes it difficult to associate the two. This is a BNF) 72 516.5 P (representation of the major elements of this language:) 72 502 T (Box) 108 481.5 T (:==) 180 481.5 T 5 F (vertical) 216 481.5 T ({) 257.63 481.5 T 0 F ( Objects) 262.36 481.5 T 5 F (}) 304.99 481.5 T 0 F (:==) 180 467 T 5 F (horizontal) 216 467 T ({) 271.64 467 T 0 F ( Objects) 276.36 467 T 5 F (}) 319 467 T 0 F (Object) 108 452.5 T (:==) 180 452.5 T (Box) 216 452.5 T (:==) 180 438 T (Widget) 216 438 T (:==) 180 423.5 T (Glue) 216 423.5 T (:==) 180 409 T (Variable) 216 409 T (Widget) 108 394.5 T (:==) 180 394.5 T 1 F (widget-name) 216 394.5 T 5 F (< +) 280.96 394.5 T 0 F (Value) 300.62 394.5 T 5 F (-) 332.26 394.5 T 0 F ( Value) 336.26 394.5 T 5 F (* +) 370.89 394.5 T 0 F ( Value) 386.73 394.5 T 5 F (-) 421.36 394.5 T 0 F ( Value) 425.36 394.5 T 5 F (>) 459.99 394.5 T 0 F (Glue) 108 380 T (:==) 180 380 T (Expression) 216 380 T 5 F (< +) 272.3 380 T 0 F (Value) 291.97 380 T 5 F (-) 323.61 380 T 0 F (Value >) 330.6 380 T (Variable) 108 365.5 T (:==) 180 365.5 T 1 F (variable-name) 216 365.5 T 5 F (=) 288.94 365.5 T 0 F (Expression) 298.78 365.5 T (Value) 108 351 T (:==) 180 351 T (Expression) 216 351 T 5 F (inff*) 272.3 351 T 0 F -0.46 (The constraints on a widget are expressed as < + horizontal-stretch - horizontal-shrink * + vertical-) 72 327.5 P 0.51 (stretch -vertical-shrink >, while those for glue are simply < +stretch -shrink >, the orientation of) 72 313 P -0.17 (the glue being inherited from the box it is contained in. When a stretch or shrink value, along with) 72 298.5 P 0.89 (the + or - character is not specified, it is taken as zero. For example, < +inf * -10 > specifies a) 72 284 P -0.33 (widget constraint of 1, infinity order 1 horizontal stretch, 0 horizontal shrink, 0 vertical stretch and) 72 269.5 P (-10 infinity order 0 shrink.) 72 255 T -0.63 (The order of infinity for a particular value is the number of) 72 231.5 P 5 F -0.63 (f) 350.22 231.5 P 0 F -0.63 ( characters in the) 354.22 231.5 P 5 F -0.63 (inf) 436.29 231.5 P 0 F -0.63 ( following the base) 450.28 231.5 P -0.16 (value. For example,) 72 217 P 2 F -0.37 (1inff) 169.8 217 P 0 F -0.16 ( has order 2. Expressions are simple arithmetic \050) 205.78 217 P 2 F -0.37 (+) 436.04 217 P 0 F -0.16 (,) 443.23 217 P 2 F -0.37 (-) 449.07 217 P 0 F -0.16 (,) 456.27 217 P 2 F -0.37 (*) 462.11 217 P 0 F -0.16 (,) 469.31 217 P 2 F -0.37 (/) 475.15 217 P 0 F -0.16 (\051 along with) 482.35 217 P (some special syntax:) 72 202.5 T (Expression) 108 182 T (:==) 180 182 T (Expression) 216 182 T 1 F (binary-op \050+, -, *, /\051) 272.3 182 T 0 F (Expression) 372.99 182 T (:==) 180 167.5 T 5 F (width) 216 167.5 T 1 F (widget-name) 248.32 167.5 T 0 F (:==) 180 153 T 5 F (height) 216 153 T 1 F (widget-name) 250.99 153 T 0 F (:==) 180 138.5 T (Expression) 216 138.5 T 5 F (% of) 272.3 138.5 T 0 F (Expression) 300.28 138.5 T (:==) 180 124 T (Expression) 216 124 T 5 F (%) 272.3 124 T 0 F (:==) 180 109.5 T 5 F ($) 216 109.5 T 1 F (variable-name) 224.99 109.5 T FMENDPAGE %%EndPage: "8" 9 %%Page: "9" 9 612 792 0 FMBEGINPAGE 0 12 Q 0 X 0 K (Page 9) 504.69 28 T 5 F -0 (Width) 72 712 P 0 F -0 ( and) 104.66 712 P 5 F -0 (height) 127.97 712 P 0 F -0 ( return the size of the named widget in the indicated direction.) 159.96 712 P 5 F -0 (% of) 460.72 712 P 0 F -0 (is a simple) 488.7 712 P 0.71 (shorthand for a / b * 100, while a) 72 697.5 P 5 F 0.71 (%) 240.31 697.5 P 0 F 0.71 ( standing alone returns that part of the size of the object to) 252.3 697.5 P (which the constraint applies.) 72 683 T -0.52 (Variables are a simple short hand for repetitive typing. Assign an expression to a variable, and then) 72 659.5 P -0.2 (use that variable in a Value and the expression will be evaluated in the context of where that value) 72 645 P (is used \050which doesn\325t really matter, except for the) 72 630.5 T 5 F (%) 318.48 630.5 T 0 F ( operator\051.) 330.47 630.5 T 1 14 Q (A complete example) 72 597.67 T 0 12 Q 1.9 (Looking back to Figure 1, the complete layout description for that widget which follows the) 72 575 P (constraints given above would be:) 72 560.5 T (vertical {) 108 540 T (space = height label) 144 525.5 T ($space < +inf -100% >) 144 511 T (horizontal {) 144 496.5 T ($space < -100% >) 180 482 T (label) 180 467.5 T ($space < +inf -100% >) 180 453 T (}) 144 438.5 T ($space < +inf -100% >) 144 424 T (horizontal {) 144 409.5 T ($space < -100% >) 180 395 T (textInsertionField) 180 380.5 T ($space < +inf -100% >) 180 366 T (}) 144 351.5 T ($space < +inf -100% >) 144 337 T (horizontal {) 144 322.5 T ($space < -100% >) 180 308 T (button1) 180 293.5 T (\0503 * $space\051 < +inf -100% >) 180 279 T (button2) 180 264.5 T ($space < -100% >) 180 250 T (}) 144 235.5 T ($space < +inf -100% >) 144 221 T (}) 108 206.5 T 1.02 (Note how some additional boxes were added to space the textInsertionField and label from the) 72 183 P (edges of the dialog. This same layout could be described in many other ways.) 72 168.5 T 0.18 (When this dialog box is resized by the user, the vertical white space between the various fields is) 72 145 P 0.74 (resized evenly, and the buttons gravitate to the edges of the dialog. When the textInsertionField) 72 130.5 P (requests more space, the dialog box will try to become larger.) 72 116 T FMENDPAGE %%EndPage: "9" 10 %%Page: "10" 10 612 792 0 FMBEGINPAGE 0 12 Q 0 X 0 K (Page 10) 498.7 28 T 1 18 Q (Summary) 72 708 T 0 12 Q 0.1 (The original problem of how to layout the xmille program has been resolved; xmille now runs on) 72 684 P 1.51 (everything from a 1280x1024 NCD terminal to a 640x480 PC; and even when the fonts used) 72 669.5 P -0.33 (internally are made larger \050to accommodate a visually impaired colleague\051, the layout continues to) 72 655 P (function well.) 72 640.5 T 0.29 (The original goals of the project have been met, and the resulting Layout system is simple to use) 72 617 P -0.73 (and powerful enough to solve a wide variety of user interface problems. Instead of spending several) 72 602.5 P -0.03 (hours trying different combinations of composite widgets; simply starting with the Layout widget) 72 588 P (and trying different specifications makes the development of new applications extremely rapid.) 72 573.5 T -0.24 (The Layout widget is not restricted to use with the Athena widgets; it can be integrated with Motif) 72 550 P 2.42 (or OLIT applications. It is available via anonymous FTP along with a collection of sample) 72 535.5 P (programs which use it from export.lcs.mit.edu as Layout.tar.Z.) 72 521 T 1 18 Q (References) 72 478.5 T 2 12 Q ([Knuth84]) 72 454.5 T 0 F 2.01 (Donald E. Knuth,) 180 454.5 P 1 F 2.01 (The) 273.63 454.5 P -0 -3 (T) 296.62 454.5 B 1 14 Q 1.4 -1.4 (E) 300.29 450 B 1 12 Q 2.01 (XBook) 307.44 454.5 P 0 F 2.01 (, Addison Wesley Publishing Company,) 339.41 454.5 P (1984.) 180 440 T 2 F ([Swick90]) 72 416.5 T 0 F 1.25 (Paul Ascente and Ralph Swick,) 180 416.5 P 1 F 1.25 (X Window System Toolkit) 340.17 416.5 P 0 F 1.25 (, Digital Press,) 466.86 416.5 P (1990.) 180 402 T 2 F ([Scheifler92]) 72 378.5 T 0 F 1.42 (Robert W. Scheifler and James Gettys,) 180 378.5 P 1 F 1.42 (X Window System) 377.73 378.5 P 0 F 1.42 (, Digital Press,) 466.52 378.5 P (1992.) 180 364 T FMENDPAGE %%EndPage: "10" 11 %%Trailer ./knews-1.0b.1/Widgets/ArtTreeNode.h100644 1244 1244 521 6455455534 15546 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #ifndef ArtTreeNode_h #define ArtTreeNode_h typedef struct art_tree_node { struct art_tree_private *hook; struct art_tree_node *parent; struct art_tree_node *sibling; struct art_tree_node *child1; char *label; } ART_TREE_NODE; #endif /* ArtTreeNode_h */ ./knews-1.0b.1/Widgets/ArtTreeP.h100644 1244 1244 4026 6455455534 15104 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #ifndef ArtTreeP_h #define ArtTreeP_h #include "ArtTree.h" #include "ArtTreeNode.h" #include "ScrollableP.h" typedef struct { XtPointer extension; } ArtTreeClassPart; typedef struct ArtTreeClassRec { CoreClassPart core_class; ShadowClassPart shadow_class; ScrollableClassPart scrollable_class; ArtTreeClassPart arttree_class; } ArtTreeClassRec; extern ArtTreeClassRec artTreeClassRec; typedef struct { Pixel foreground_pixel; Pixel inner_pixel; Pixel outer_pixel; Pixel rubber_pixel; Cursor cursor; XFontStruct *font; Dimension row_spacing; Dimension column_spacing; Dimension internal_width; Dimension internal_height; Dimension internal_node_width; Dimension internal_node_height; Dimension node_columns; Dimension node_rows; Dimension pixmap_width; Dimension pixmap_height; Dimension pixmap_spacing; Boolean inner_dashed; Boolean outer_dashed; Boolean depth_one; Boolean warp_pointer; Boolean vertical; XtCallbackList inner_callback; XtCallbackList outer_callback; XtCallbackList select_callback; XtCallbackList callback; ART_TREE_NODE *root; /* private state */ GC default_gc; GC inner_gc; GC outer_gc; GC bg_gc; GC light_dashed_gc; GC dark_dashed_gc; GC rubber_gc; short node_width; short node_height; short ptr_init_x; short ptr_init_y; short init_x; short init_y; Boolean rubberbanding; Boolean active; } ArtTreePart; typedef struct ArtTreeRec { CorePart core; ShadowPart shadow; ScrollablePart scrollable; ArtTreePart arttree; } ArtTreeRec; typedef struct art_tree_private { Pixmap pixmap; int label_len; short x; short y; short bb_width; short bb_height; Boolean selected; Boolean dashed; Boolean inner; Boolean outer; } ART_TREE_PRIVATE; #endif /* ArtTreeP_h */ ./knews-1.0b.1/Widgets/Compat.h100644 1244 1244 476 6455455534 14626 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #ifndef Compat_h #define Compat_h #if XtSpecificationRelease <= 4 # define XrmPermStringToQuark(string) XrmStringToQuark(string) # define XtScreenDatabase(screen) XtDatabase(DisplayOfScreen(screen)) typedef char *XPointer; #endif #endif /* Compat_h */ ./knews-1.0b.1/Widgets/Dialogue.c100644 1244 1244 22531 6455455534 15163 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include #include #include "Compat.h" #include "Knapp.h" #include "Layout.h" #include "Message.h" #include "TextField.h" #include "Util.h" #include "DialogueP.h" static XtResource resources[] = { #define offset(field) XtOffsetOf(DialogueRec, dialogue.field) {XtNcallback, XtCCallback, XtRCallback, sizeof(XtCallbackList), offset(callback), XtRCallback, (XtPointer)NULL}, {XtNmessage, XtCMessage, XtRString, sizeof(String), offset(message), XtRImmediate, (XtPointer)NULL}, {XtNbuffer, XtCBuffer, XtRString, sizeof(String), offset(buffer), XtRImmediate, (XtPointer)NULL}, {XtNleftLabel, XtCLabel, XtRBoolean, sizeof(String), offset(left_label), XtRImmediate, (XtPointer)NULL}, {XtNmiddleLabel, XtCLabel, XtRBoolean, sizeof(String), offset(middle_label), XtRImmediate, (XtPointer)NULL}, {XtNrightLabel, XtCLabel, XtRBoolean, sizeof(String), offset(right_label), XtRImmediate, (XtPointer)NULL}, #undef offset }; static void Initialize(Widget, Widget, ArgList, Cardinal*); static Boolean SetValues(Widget, Widget, Widget, ArgList, Cardinal*); static void close_dialogue(Widget); DialogueClassRec dialogueClassRec = { { /* core fields */ (WidgetClass) &closeShellClassRec, /* superclass */ "Dialogue", /* class_name */ sizeof(DialogueRec), /* widget_size */ NULL, /* class_initialize */ NULL, /* class_part_initialize */ FALSE, /* class_inited */ Initialize, /* initialize */ NULL, /* initialize_hook */ XtInheritRealize, /* realize */ NULL, /* actions */ 0, /* num_actions */ resources, /* resources */ XtNumber(resources), /* num_resources */ NULLQUARK, /* xrm_class */ TRUE, /* compress_motion */ FALSE, /* compress_exposure */ TRUE, /* compress_enterleave */ FALSE, /* visible_interest */ NULL, /* destroy */ XtInheritResize, /* resize */ NULL, /* expose */ SetValues, /* set_values */ NULL, /* set_values_hook */ XtInheritSetValuesAlmost, /* set_values_almost */ NULL, /* get_values_hook */ NULL, /* accept_focus */ XtVersion, /* version */ NULL, /* callback_private */ XtInheritTranslations, /* tm_table */ NULL, /* query_geometry */ XtInheritDisplayAccelerator, /* display_accelerator */ NULL /* extension */ }, { /* composite fields */ XtInheritGeometryManager, /* geometry_manager */ XtInheritChangeManaged, /* change_managed */ XtInheritInsertChild, /* insert_child */ XtInheritDeleteChild, /* delete_child */ NULL, /* extension */ }, { /* shell fields */ NULL, /* extension */ }, { /* wm shell fields */ NULL, /* extension */ }, { /* vendor shell fields */ NULL, /* extension */ }, { /* transient shell fields */ NULL, /* extension */ }, { close_dialogue, /* close_window */ NULL, /* extension */ }, { /* dialogue fields */ NULL, /* extension */ } }; WidgetClass dialogueWidgetClass = (WidgetClass)&dialogueClassRec; /*************************************************************************/ static const char *layout_string = "vertical { " " height left <+inf-inf> " " horizontal { " " height left <+inf-inf> " " message " " height left <+inf-inf> " " } " " height left <+inf-inf> " " horizontal { " " height left " " textfield <+inf-inf*> " " height left " " } " " height left <+inf-inf> " " horizontal { " " height left <+inf-inf> " " left " " height left <+3inf-inf> " " middle " " height left <+3inf-inf> " " right " " height left <+inf-inf> " " } " " height left <+inf-inf> " "} "; static void call_callbacks(DialogueWidget w, DialogueReply reply) { XtCallbackList c_list = w->dialogue.callback; DialogueReport report; if (!c_list) return; report.reply = reply; report.buffer = TextFieldGetBuffer(w->dialogue.text_field); XtCallCallbackList((Widget)w, c_list, (XtPointer)&report); XtFree(report.buffer); } static void close_dialogue(Widget gw) { DialogueWidget w = (DialogueWidget)gw; call_callbacks(w, DialogueReplyClose); } static void knapp_callback(Widget knapp, XtPointer client_data, XtPointer call_data) { DialogueWidget w = (DialogueWidget)client_data; if (knapp == w->dialogue.left_knapp) call_callbacks(w, DialogueReplyLeft); else if (knapp == w->dialogue.middle_knapp) call_callbacks(w, DialogueReplyMiddle); else if (knapp == w->dialogue.right_knapp) call_callbacks(w, DialogueReplyRight); } static void enter_callback(Widget gw, XtPointer client_data, XtPointer call_data) { DialogueWidget w = (DialogueWidget)client_data; XtCallbackList c_list = w->dialogue.callback; DialogueReport report; report.reply = DialogueReplyEnter; report.buffer = (String)call_data; if (c_list) XtCallCallbackList((Widget)w, c_list, (XtPointer)&report); } static void tab_callback(Widget gw, XtPointer client_data, XtPointer call_data) { DialogueWidget w = (DialogueWidget)client_data; XtCallbackList c_list = w->dialogue.callback; DialogueReport report; report.reply = DialogueReplyTab; report.buffer = (String)call_data; if (c_list) XtCallCallbackList((Widget)w, c_list, (XtPointer)&report); } /*************************************************************************/ static void Initialize(Widget grequest, Widget gnew, ArgList gargs, Cardinal *no_args) { DialogueWidget new = (DialogueWidget)gnew; Arg args[2]; new->dialogue.layout = XtVaCreateManagedWidget("layout", layoutWidgetClass, (Widget)new, XtVaTypedArg, XtNlayout, XtRString, layout_string, (int)sizeof(String), (void *)0); XtSetArg(args[0], XtNlabel, new->dialogue.left_label); new->dialogue.left_knapp = XtCreateManagedWidget("left", knappWidgetClass, new->dialogue.layout, args, 1); XtAddCallback(new->dialogue.left_knapp, XtNcallback, knapp_callback, (XtPointer)new); XtSetArg(args[0], XtNlabel, new->dialogue.middle_label); new->dialogue.middle_knapp = XtCreateManagedWidget("middle", knappWidgetClass, new->dialogue.layout, args, 1); XtAddCallback(new->dialogue.middle_knapp, XtNcallback, knapp_callback, (XtPointer)new); XtSetArg(args[0], XtNlabel, new->dialogue.right_label); new->dialogue.right_knapp = XtCreateManagedWidget("right", knappWidgetClass, new->dialogue.layout, args, 1); XtAddCallback(new->dialogue.right_knapp, XtNcallback, knapp_callback, (XtPointer)new); XtSetArg(args[0], XtNbuffer, new->dialogue.buffer); XtSetArg(args[1], XtNsingleLine, True); new->dialogue.text_field = XtCreateManagedWidget("textfield", textFieldWidgetClass, new->dialogue.layout, args, 2); XtAddCallback(new->dialogue.text_field, XtNcallback, enter_callback, (XtPointer)new); XtAddCallback(new->dialogue.text_field, XtNtabCallback, tab_callback, (XtPointer)new); XtSetArg(args[0], XtNbuffer, new->dialogue.message); new->dialogue.message_widget = XtCreateManagedWidget("message", messageWidgetClass, new->dialogue.layout, args, 1); new->dialogue.message = NULL; XtSetKeyboardFocus((Widget)new, new->dialogue.text_field); } static Boolean SetValues(Widget gcurrent, Widget grequest, Widget gnew, ArgList args, Cardinal *num_args) { Boolean redisplay = False; DialogueWidget new = (DialogueWidget)gnew; DialogueWidget current = (DialogueWidget)gcurrent; if (new->dialogue.message != current->dialogue.message) { Arg arg; XtSetArg(arg, XtNbuffer, new->dialogue.message); new->dialogue.message = NULL; XtSetValues(new->dialogue.message_widget, &arg, 1); } if (new->dialogue.buffer != current->dialogue.buffer) { Arg arg; XtSetArg(arg, XtNbuffer, new->dialogue.buffer); new->dialogue.buffer = NULL; XtSetValues(new->dialogue.text_field, &arg, 1); } return redisplay; } /*************************************************************************/ Widget DialogueGetTextField(Widget gw) { DialogueWidget w = (DialogueWidget)gw; return w->dialogue.text_field; } ./knews-1.0b.1/Widgets/Dialogue.h100644 1244 1244 2002 6455455534 15137 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #ifndef Dialogue_h #define Dialogue_h #ifndef XtCMessage #define XtCMessage "Message" #endif #ifndef XtCBuffer #define XtCBuffer "Buffer" #endif #ifndef XtCLabel #define XtCLabel "Label" #endif #ifndef XtNmessage #define XtNmessage "message" #endif #ifndef XtNbuffer #define XtNbuffer "buffer" #endif #ifndef XtNleftLabel #define XtNleftLabel "leftLabel" #endif #ifndef XtNmiddleLabel #define XtNmiddleLabel "middleLabel" #endif #ifndef XtNrightLabel #define XtNrightLabel "rightLabel" #endif typedef struct DialogueClassRec* DialogueWidgetClass; typedef struct DialogueRec* DialogueWidget; extern WidgetClass dialogueWidgetClass; typedef enum { DialogueReplyLeft, DialogueReplyMiddle, DialogueReplyRight, DialogueReplyEnter, DialogueReplyTab, DialogueReplyClose } DialogueReply; typedef struct { DialogueReply reply; String buffer; } DialogueReport; extern Widget DialogueGetTextField(Widget); #endif /* Dialogue_h */ ./knews-1.0b.1/Widgets/Imakefile100644 1244 1244 1574 6455455534 15063 0ustar kallekalle#include "../knews.tmpl" WIDGETS = \ ArtText.o ArtTree.o CloseSh.o Dialogue.o FileSel.o \ Login.o Manager.o Menu.o MenuG.o MenuKnapp.o MenuShell.o \ Notice.o Knapp.o Message.o PullRight.o Sash.o Scrollable.o \ ScrBar.o ScrList.o SeparatorG.o Shadow.o StringG.o \ TextField.o Toggle.o ToggleG.o Util.o LAYOUT = Layout.o laylex.o laygram.o NormalLibraryTarget(Widgets, $(WIDGETS) $(LAYOUT)) /* * Rules for Keith Packard's Layout widget */ depend:: laygram.c /*laylex.c*/ laygram.c: laygram.y $(YACC) -d laygram.y sed 's/yy/LayYY/g' y.tab.c > laygram.c sed 's/yy/LayYY/g' y.tab.h > laygram.h rm -f y.tab.c y.tab.h clean:: -rm -f laygram.c laygram.h /* *laylex.c: laylex.l * lex laylex.l * sed 's/yy/LayYY/g' lex.yy.c > laylex.c * rm lex.yy.c */ laylex.c: laylex_new.c laygram.c sed 's/yy/LayYY/g' laylex_new.c > laylex.c clean:: -rm -f laylex.c ./knews-1.0b.1/Widgets/DialogueP.h100644 1244 1244 2357 6455455535 15275 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #ifndef DialogueP_h #define DialogueP_h #include "Dialogue.h" #include "CloseShP.h" typedef struct { XtPointer empty; } DialogueClassPart; typedef struct DialogueClassRec { CoreClassPart core_class; CompositeClassPart composite_class; ShellClassPart shell_class; WMShellClassPart wm_shell_class; VendorShellClassPart vendor_shell_class; TransientShellClassPart transient_shell_class; CloseShellClassPart close_shell_class; DialogueClassPart dialogue_class; } DialogueClassRec; extern DialogueClassRec dialogueClassRec; typedef struct { XtCallbackList callback; String message; String buffer; String left_label; String middle_label; String right_label; /* private data */ Widget layout; Widget message_widget; Widget text_field; Widget left_knapp; Widget middle_knapp; Widget right_knapp; } DialoguePart; typedef struct DialogueRec { CorePart core; CompositePart composite; ShellPart shell; WMShellPart wm; VendorShellPart vendor; TransientShellPart transient; CloseShellPart close_shell; DialoguePart dialogue; } DialogueRec; #endif /* DialogueP_h */ ./knews-1.0b.1/Widgets/FileSel.c100644 1244 1244 52602 6455455535 14760 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include #include #include #include #include #include #include #include #include #include #include "Compat.h" #include "Knapp.h" #include "Layout.h" #include "Message.h" #include "Scrollable.h" #include "ScrBar.h" #include "ScrList.h" #include "Toggle.h" #include "TextField.h" #include "Util.h" #include "FileSelP.h" /* a few dirty hacks */ #if !defined(MAXPATHLEN) || (MAXPATHLEN < 1024) # undef MAXPATHLEN # define MAXPATHLEN 1024 #endif static XtResource resources[] = { {XtNinput, XtCInput, XtRBool, sizeof(Bool), XtOffsetOf(FileSelRec, wm.wm_hints.input), XtRImmediate, (XtPointer)True}, {XtNallowShellResize, XtCAllowShellResize, XtRBoolean, sizeof(Boolean), XtOffsetOf(FileSelRec, shell.allow_shell_resize), XtRImmediate, (XtPointer)True}, #define offset(field) XtOffsetOf(FileSelRec, filesel.field) {XtNcallback, XtCCallback, XtRCallback, sizeof(XtCallbackList), offset(callback), XtRCallback, (XtPointer)NULL}, {XtNcursor, XtCCursor, XtRCursor, sizeof(Cursor), offset(cursor), XtRString, (XtPointer)"top_left_arrow"}, {XtNbusyCursor, XtCCursor, XtRCursor, sizeof(Cursor), offset(busy_cursor), XtRString, (XtPointer)"watch"}, {XtNdirectory, XtCDirectory, XtRString, sizeof(String), offset(directory), XtRCallProc, NULL}, {XtNpreferredColumns, XtCPreferredColumns, XtRDimension, sizeof(Dimension), offset(pref_cols), XtRImmediate, (XtPointer)16}, {XtNshowDotFiles, XtCShowDotFiles, XtRBoolean, sizeof(Boolean), offset(show_dot_files), XtRImmediate, (XtPointer)True}, #undef offset }; static void Initialize(Widget, Widget, ArgList, Cardinal*); static void Destroy(Widget); static void Realize(Widget, XtValueMask*, XSetWindowAttributes*); static Boolean SetValues(Widget, Widget, Widget, ArgList, Cardinal*); static void close_filesel(Widget, XEvent*, String*, Cardinal*); static XtActionsRec actions[] = { {"close-filesel", close_filesel}, }; static char translations[] = "WM_PROTOCOLS: close-filesel() \n"; FileSelClassRec fileSelClassRec = { { /* core fields */ (WidgetClass) &topLevelShellClassRec, /* superclass */ "FileSel", /* class_name */ sizeof(FileSelRec), /* widget_size */ NULL, /* class_initialize */ NULL, /* class_part_initialize */ FALSE, /* class_inited */ Initialize, /* initialize */ NULL, /* initialize_hook */ Realize, /* realize */ actions, /* actions */ XtNumber(actions), /* num_actions */ resources, /* resources */ XtNumber(resources), /* num_resources */ NULLQUARK, /* xrm_class */ TRUE, /* compress_motion */ TRUE, /* compress exposure */ TRUE, /* compress_enterleave */ FALSE, /* visible_interest */ Destroy, /* destroy */ XtInheritResize, /* resize */ NULL, /* expose */ SetValues, /* set_values */ NULL, /* set_values_hook */ XtInheritSetValuesAlmost, /* set_values_almost */ NULL, /* get_values_hook */ NULL, /* accept_focus */ XtVersion, /* version */ NULL, /* callback_private */ translations, /* tm_table */ XtInheritQueryGeometry, /* query_geometry */ XtInheritDisplayAccelerator, /* display_accelerator */ NULL /* extension */ }, { /* composite fields */ XtInheritGeometryManager, /* geometry_manager */ XtInheritChangeManaged, /* change_managed */ XtInheritInsertChild, /* insert_child */ XtInheritDeleteChild, /* delete_child */ NULL, /* extension */ }, { /* shell fields */ NULL, /* extension */ }, { /* wm shell fields */ NULL, /* extension */ }, { /* vendor shell fields */ NULL, /* extension */ }, { /* transient shell fields */ NULL, /* extension */ }, { /* filesel fields */ NULL, /* extension */ } }; WidgetClass fileSelWidgetClass = (WidgetClass)&fileSelClassRec; /*************************************************************************/ typedef enum { FileTypeDirectory = 0, FileTypeExecutable = 1, FileTypeBrokenlink = 2, FileTypeDocument = 3, FileTypeSysdoc = 4, FileTypeDotdot = 5 } FileType; #define ICON_SIZE 16 static void init_pixmaps(FileSelWidget w) { static unsigned char icon_bits[MAX_FILE_TYPE + 1][ICON_SIZE * ICON_SIZE] = { /* FileTypeDirectory */ {0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x42, 0x00, 0x81, 0xff, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0xff, 0xff, 0x00, 0x00}, /* FileTypeExecutable */ {0xFF, 0xFF, 0x01, 0x80, 0x01, 0xB0, 0x01, 0xB0, 0x01, 0x80, 0xFD, 0xBF, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0xFF, 0xFF}, /* FileTypeBrokenlink */ {0xFF, 0xFF, 0x01, 0x80, 0x01, 0x80, 0xC1, 0x81, 0x21, 0x82, 0x21, 0x82, 0x01, 0x82, 0x01, 0x81, 0x81, 0x80, 0x81, 0x80, 0x01, 0x80, 0x81, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0xFF, 0xFF}, /* FileTypeDocument */ {0xE0, 0x7F, 0x30, 0x40, 0x28, 0x40, 0x24, 0x40, 0x3E, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0xFE, 0x7F}, /* FileTypeSysdoc */ {0xF8, 0x1F, 0x04, 0x20, 0x04, 0x20, 0x04, 0x20, 0x04, 0x20, 0x04, 0x20, 0x04, 0x20, 0x04, 0x20, 0x04, 0x20, 0x04, 0x20, 0xF8, 0x1F, 0xF8, 0x1F, 0x04, 0x20, 0x02, 0x40, 0x01, 0x80, 0xFF, 0xFF}, /* FileTypeDotdot */ {0x80, 0x01, 0xC0, 0x03, 0xE0, 0x07, 0xF0, 0x0F, 0xF8, 0x1F, 0xFC, 0x3F, 0xFE, 0x7F, 0xFF, 0xFF, 0xE0, 0x07, 0xE0, 0x07, 0xE0, 0x07, 0xE0, 0x07, 0xE0, 0x07, 0xE0, 0x07, 0xE0, 0x07, 0xE0, 0x07}, }; Display *disp = XtDisplay(w); Window root = RootWindow(disp, XScreenNumberOfScreen(XtScreen(w))); int i; for (i = 0 ; i <= MAX_FILE_TYPE ; i++) w->filesel.pixmap[i] = XCreateBitmapFromData(disp, root, (char *)icon_bits[i], 16, 16); } static const char *layout_string = "vertical {" " height choose " " horizontal { " " 2 " " vertical { " " 0 <+inf> " " directory " " 0 <+inf> " " } " " dirfield <+inf-inf*> " " 4 + width scrbar " " } " " (height choose / 2) " " horizontal { " " 2 " " list <+inf-inf*+inf-inf> " " 2 " " scrbar <*+inf> " " 2 " " } " " (height choose / 2) " " horizontal { " " 2 " " vertical { " " 0 <+inf> " " file " " 0 <+inf> " " } " " (width directory - width file) " " filefield <+inf-inf*> " " 4 + width scrbar " " } " " height choose " " horizontal { " " 2 <+inf-inf>" " choose " " 2 <+inf-inf> " " dotfiles " " 2 <+inf-inf> " " cancel " " 2 <+inf-inf> " " } " " height choose " "} "; #ifndef S_ISSOCK # if defined(S_IFSOCK) && defined(S_IFMT) /* suggested by Marc J. Fraioli */ # define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK) # else # define S_ISSOCK(mode) 0 # endif #endif #ifndef S_ISFIFO # if defined(S_IFFIFO) && defined(S_IFMT) # define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFFIFO) # else # define S_ISFIFO(mode) 0 # endif #endif static FileType stat_file(char *buffer) { struct stat stat_buf; if (stat(buffer, &stat_buf) < 0) return FileTypeBrokenlink; if (S_ISDIR(stat_buf.st_mode)) return FileTypeDirectory; if (stat_buf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) return FileTypeExecutable; if (S_ISREG(stat_buf.st_mode)) return FileTypeDocument; if (S_ISCHR(stat_buf.st_mode) || S_ISFIFO(stat_buf.st_mode) || S_ISBLK(stat_buf.st_mode) || S_ISSOCK(stat_buf.st_mode)) return FileTypeSysdoc; return FileTypeBrokenlink; } static int strp_comp(const void *s1, const void *s2) { return strcmp(*(const char **)s1, *(const char **)s2); } static int change_dir(FileSelWidget w, char *dir) { char buffer[MAXPATHLEN + 1]; char *new_dir = NULL; char **list = NULL; struct stat stat_buf; DIR *dirp; struct dirent *dp; long i, n, n_alloc, len; Arg arg; if (dir) if (*dir == '/') new_dir = XtNewString(dir); else if (*dir == '\0') new_dir = XtNewString(w->filesel.directory); else { new_dir = XtMalloc(strlen(w->filesel.directory) + strlen(dir) + 8); if (strcmp(w->filesel.directory, "/") == 0) sprintf(new_dir, "/%s", dir); else sprintf(new_dir, "%s/%s", w->filesel.directory, dir); } else if (strcmp(w->filesel.directory, "/") == 0) new_dir = XtNewString("/"); else { char *c; new_dir = XtNewString(w->filesel.directory); len = strlen(new_dir); if (new_dir[len-1] == '/') { new_dir[len-1] = '\0'; len--; } c = strrchr(new_dir, '/'); if (c) if (c == new_dir) new_dir[1] = '\0'; else *c = '\0'; } if (stat(new_dir, &stat_buf) < 0) { perror(new_dir); XtFree(new_dir); return -1; } dirp = opendir(new_dir); if (!dirp) { perror(new_dir); XtFree(new_dir); return -1; } if (XtIsRealized((Widget)w)) { XDefineCursor(XtDisplay(w), XtWindow(w), w->filesel.busy_cursor); XFlush(XtDisplay(w)); } n = 0; n_alloc = 0; while ( (dp = readdir(dirp)) ) { if (n > n_alloc - 2) { n_alloc = 2 * (n_alloc + 1); list = (char **)XtRealloc((char *)list, n_alloc * sizeof(char *)); } if (dp->d_name[0] != '.' || (dp->d_name[1] == '.' && dp->d_name[2] == '\0') || (w->filesel.show_dot_files && dp->d_name[1] != '\0')) { list[n] = XtNewString(dp->d_name); n++; } } qsort(list, n, sizeof(char *), strp_comp); XtFree(w->filesel.directory); w->filesel.directory = new_dir; sprintf(buffer, "%s/", new_dir); len = strlen(buffer); w->filesel.types = (unsigned char *)XtRealloc((char *)w->filesel.types, n); ScrListClearLines(w->filesel.list); ScrollableSuspend(w->filesel.list); for (i = 0 ; i < n ; i++) { FileType type; if (list[i][0] == '.' && list[i][1] == '.' && list[i][2] == '\0') type = FileTypeDotdot; else { sprintf(buffer + len, "%s", list[i]); type = stat_file(buffer); } ScrListAddLine(w->filesel.list, list[i], w->filesel.pixmap[type]); w->filesel.types[i] = type; XtFree(list[i]); } ScrollableResume(w->filesel.list); XtSetArg(arg, XtNbuffer, w->filesel.directory); XtSetValues(w->filesel.dir_field, &arg, 1); XtSetArg(arg, XtNbuffer, ""); XtSetValues(w->filesel.file_field, &arg, 1); if (XtIsRealized((Widget)w)) { XDefineCursor(XtDisplay(w), XtWindow(w), w->filesel.cursor); XFlush(XtDisplay(w)); } return 0; } static void call_callbacks(FileSelWidget w, long row) { XtCallbackList c_list = w->filesel.callback; char *file = ""; char *call_data; long len; int do_free = False; if (row >= 0) file = ScrListGetString(w->filesel.list, row); else { file = TextFieldGetBuffer(w->filesel.file_field); do_free = True; } if (!c_list || !file || !w->filesel.directory) { if (do_free) XtFree(file); return; } len = strlen(file) + strlen(w->filesel.directory) + 8; call_data = XtMalloc(len); if (file[0] == '/') sprintf(call_data, "%s%s", w->filesel.directory, file); else sprintf(call_data, "%s/%s", w->filesel.directory, file); XtCallCallbackList((Widget)w, c_list, (XtPointer)call_data); XtFree(call_data); if (do_free) XtFree(file); } static void dir_field_callback(Widget gw, XtPointer client_data, XtPointer call_data) { FileSelWidget w = (FileSelWidget)client_data; if (change_dir(w, (char *)call_data) < 0) XBell(XtDisplay(w), 0); } static void file_field_callback(Widget gw, XtPointer client_data, XtPointer call_data) { FileSelWidget w = (FileSelWidget)client_data; char *buffer = (char *)call_data; long n = ScrollableGetVSize(w->filesel.list); if (buffer) { long i; for (i = 0 ; i < n ; i++) { char *item = ScrListGetString(w->filesel.list, i); if (!item) break; if (strcmp(item, buffer) == 0) { Pixmap pixmap = ScrListGetPixmap(w->filesel.list, i); if (pixmap == w->filesel.pixmap[FileTypeDirectory]) { if (change_dir(w, buffer) < 0) XBell(XtDisplay(w), 0); } else if (pixmap == w->filesel.pixmap[FileTypeDotdot]) { if (change_dir(w, NULL) < 0) XBell(XtDisplay(w), 0); } else call_callbacks(w, i); return; } } } call_callbacks(w, -1); } static void file_field_tab_callback(Widget gw, XtPointer client_data, XtPointer call_data) { FileSelWidget w = (FileSelWidget)client_data; char *buffer = (char *)call_data; char *c = NULL; long n = ScrollableGetVSize(w->filesel.list); Arg arg; if (n > 0 && buffer) { long len = strlen(buffer); long first = 0; long last; if (len == 0) last = n; else { while (first < n) { int temp; char *string = ScrListGetString(w->filesel.list, first); temp = strncmp(string, buffer, len); if (temp == 0) break; if (temp > 0) return; first++; } if (first == n) return; last = first + 1; while (last < n && strncmp(ScrListGetString(w->filesel.list, last), buffer, len) == 0) last++; } c = ScrListGetString(w->filesel.list, first); for (;;) { if (c[len] == '\0') break; for (n = first + 1 ; n < last ; n++) if (ScrListGetString(w->filesel.list, n)[len] != c[len]) break; if (n != last) break; len++; } buffer = XtMalloc(len + 1); memcpy(buffer, c, len); buffer[len] = '\0'; XtSetArg(arg, XtNbuffer, buffer); XtSetValues(gw, &arg, 1); XtFree(buffer); } } static void list_callback(Widget gw, XtPointer client_data, XtPointer call_data) { FileSelWidget w = (FileSelWidget)client_data; long row = (long)call_data; Pixmap pixmap = ScrListGetPixmap(gw, row); char *file = ScrListGetString(gw, row); if (pixmap == w->filesel.pixmap[FileTypeDirectory]) { if (change_dir(w, file) < 0) XBell(XtDisplay(w), 0); } else if (pixmap == w->filesel.pixmap[FileTypeDotdot]) { if (change_dir(w, NULL) < 0) XBell(XtDisplay(w), 0); } else call_callbacks(w, row); } static void list_select_callback(Widget gw, XtPointer client_data, XtPointer call_data) { FileSelWidget w = (FileSelWidget)client_data; long row = (long)call_data; char *file = ScrListGetString(gw, row); Arg arg; XtSetArg(arg, XtNbuffer, file); XtSetValues(w->filesel.file_field, &arg, 1); } static void cancel_callback(Widget gw, XtPointer client_data, XtPointer call_data) { FileSelWidget w = (FileSelWidget)client_data; XtCallbackList c_list = w->filesel.callback; if (c_list) XtCallCallbackList((Widget)w, c_list, NULL); } static void choose_callback(Widget gw, XtPointer client_data, XtPointer call_data) { FileSelWidget w = (FileSelWidget)client_data; call_callbacks(w, -1); } static void show_callback(Widget gw, XtPointer client_data, XtPointer call_data) { FileSelWidget w = (FileSelWidget)client_data; Boolean *set = (Boolean *)call_data; if (!set) return; *set = w->filesel.show_dot_files = !w->filesel.show_dot_files; change_dir(w, ""); } static void popup_callback(Widget gw, XtPointer client_data, XtPointer call_data) { FileSelWidget w = (FileSelWidget)gw; long n; Arg arg; XtSetArg(arg, XtNbuffer, w->filesel.directory); XtSetValues(w->filesel.dir_field, &arg, 1); XtSetArg(arg, XtNbuffer, ""); XtSetValues(w->filesel.file_field, &arg, 1); ScrollableSetVPos(w->filesel.list, 0); n = ScrListGetFirstSelected(w->filesel.list); if (n >= 0) ScrListSetSelected(w->filesel.list, n, False); } static void close_filesel(Widget gw, XEvent *event, String *params, Cardinal *no_params) { FileSelWidget w = (FileSelWidget)gw; Display *disp = XtDisplay(w); Atom wm_delete_window, wm_protocols; XtCallbackList c_list = w->filesel.callback; if (event->type != ClientMessage) return; wm_delete_window = intern_atom(disp, "WM_DELETE_WINDOW"); wm_protocols = intern_atom(disp, "WM_PROTOCOLS"); if (event->xclient.message_type != wm_protocols || event->xclient.data.l[0] != wm_delete_window) return; if (c_list) XtCallCallbackList((Widget)w, c_list, NULL); } /*************************************************************************/ static void Initialize(Widget grequest, Widget gnew, ArgList args, Cardinal *no_args) { FileSelWidget new = (FileSelWidget)gnew; Arg arg[8]; Pixel pix; char *home = getenv("HOME"); Widget layout, temp; init_pixmaps(new); new->filesel.types = NULL; XtAddCallback((Widget)new, XtNpopupCallback, popup_callback, NULL); layout = XtVaCreateManagedWidget("layout", layoutWidgetClass, (Widget)new, XtVaTypedArg, XtNlayout, XtRString, layout_string, sizeof(String), (void *)0); XtSetArg(arg[0], XtNbackground, &pix); XtGetValues(layout, arg, 1); XtSetArg(arg[0], XtNborderColor, pix); XtSetArg(arg[1], XtNpreferredChars, 20); XtSetArg(arg[2], XtNfocusRoot, new); XtSetArg(arg[3], XtNsingleLine, True); new->filesel.dir_field = XtCreateManagedWidget("dirfield", textFieldWidgetClass, layout, arg, 4); XtAddCallback(new->filesel.dir_field, XtNcallback, dir_field_callback, (XtPointer)new); new->filesel.file_field = XtCreateManagedWidget("filefield", textFieldWidgetClass, layout, arg, 4); XtAddCallback(new->filesel.file_field, XtNcallback, file_field_callback, (XtPointer)new); XtAddCallback(new->filesel.file_field, XtNtabCallback, file_field_tab_callback, (XtPointer)new); XtCreateManagedWidget("directory", messageWidgetClass, layout, NULL, 0); XtCreateManagedWidget("file", messageWidgetClass, layout, NULL, 0); temp = XtCreateManagedWidget("scrbar", scrBarWidgetClass, layout, NULL, 0); XtSetArg(arg[0], XtNatLeastOne, False); XtSetArg(arg[1], XtNatMostOne, True); XtSetArg(arg[2], XtNdepthOne, True); XtSetArg(arg[3], XtNpixmapWidth, 16); XtSetArg(arg[4], XtNpixmapHeight, 16); XtSetArg(arg[5], XtNusePixmaps, True); XtSetArg(arg[6], XtNpreferredColumns, new->filesel.pref_cols); XtSetArg(arg[7], XtNvBar, temp); new->filesel.list = XtCreateManagedWidget("list", scrListWidgetClass, layout, arg, 8); XtAddCallback(new->filesel.list, XtNcallback, list_callback, (XtPointer)new); XtAddCallback(new->filesel.list, XtNselectCallback, list_select_callback, (XtPointer)new); temp = XtCreateManagedWidget("cancel", knappWidgetClass, layout, NULL, 0); XtAddCallback(temp, XtNcallback, cancel_callback, (XtPointer)new); temp = XtCreateManagedWidget("choose", knappWidgetClass, layout, NULL, 0); XtAddCallback(temp, XtNcallback, choose_callback, (XtPointer)new); XtSetArg(args[0], XtNset, new->filesel.show_dot_files); temp = XtCreateManagedWidget("dotfiles", toggleWidgetClass, layout, args, 1); XtAddCallback(temp, XtNcallback, show_callback, (XtPointer)new); if (!new->filesel.directory) if (home) new->filesel.directory = XtNewString(home); else new->filesel.directory = XtNewString("/"); else if (new->filesel.directory[0] == '/') new->filesel.directory = XtNewString(new->filesel.directory); else { char *dir = new->filesel.directory; if (!home) home = ""; new->filesel.directory = XtMalloc(strlen(home) + strlen(dir) + 4); sprintf(new->filesel.directory, "%s/%s", home, dir); } XtSetKeyboardFocus((Widget)new, new->filesel.file_field); if (change_dir(new, new->filesel.directory) < 0) if (home) change_dir(new, home); } static void Destroy(Widget gw) { FileSelWidget w = (FileSelWidget)gw; Display *disp = XtDisplay(w); int i; for (i = 0 ; i <= MAX_FILE_TYPE ; i++) XFreePixmap(disp, w->filesel.pixmap[i]); XtFree((char *)w->filesel.types); } static void Realize(Widget w, XtValueMask *mask, XSetWindowAttributes *attributes) { Display *disp = XtDisplay(w); Atom wm_delete_window; topLevelShellWidgetClass->core_class.realize(w, mask, attributes); wm_delete_window = intern_atom(disp, "WM_DELETE_WINDOW"); XSetWMProtocols(disp, XtWindow(w), &wm_delete_window, 1); } static Boolean SetValues(Widget gcurrent, Widget grequest, Widget gnew, ArgList args, Cardinal *num_args) { Boolean redisplay = False; return redisplay; } ./knews-1.0b.1/Widgets/FileSel.h100644 1244 1244 1174 6455455535 14743 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #ifndef FileSel_h #define FileSel_h #ifndef XtCDirectory #define XtCDirectory "Directory" #endif #ifndef XtCShowDotFiles #define XtCShowDotFiles "ShowDotFiles" #endif #ifndef XtNbusyCursor #define XtNbusyCursor "busyCursor" #endif #ifndef XtNcursor #define XtNcursor "cursor" #endif #ifndef XtNdirectory #define XtNdirectory "directory" #endif #ifndef XtNshowDotFiles #define XtNshowDotFiles "showDotFiles" #endif typedef struct FileSelClassRec* FileSelWidgetClass; typedef struct FileSelRec* FileSelWidget; extern WidgetClass fileSelWidgetClass; #endif /* FileSel_h */ ./knews-1.0b.1/Widgets/Util.c100644 1244 1244 36177 6455455535 14363 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ /* Copyright (c) 1989 X Consortium Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of the X Consortium shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from the X Consortium. */ #include #include #include #include #include #include #include #include #include #include "Compat.h" #include "Util.h" #include "UtilI.h" /* * This function is modified from the source to XTextWidth, * which is why I included the X Consortium copyright notice. */ int MyXWidthToChars(XFontStruct *font, const char *str, int len, int width) { XCharStruct *def; int singlerow = (font->max_byte1 == 0); int n; if (!str || width <= 0) return 0; if (singlerow) CI_GET_DEFAULT_INFO_1D(font, def); else CI_GET_DEFAULT_INFO_2D(font, def); for (n = 0 ; n < len ; n++) { XCharStruct *cs; int ch = (unsigned char)str[n]; if (singlerow) CI_GET_CHAR_INFO_1D(font, ch, def, cs); else /* * essentially the macro CI_GET_ROWZERO_CHAR_INFO_2D */ if (font->min_byte1 == 0 && ch >= font->min_char_or_byte2 && ch <= font->max_char_or_byte2) if (!font->per_char) cs = &font->min_bounds; else { cs = font->per_char + ch - font->min_char_or_byte2; if (CI_NONEXISTCHAR(cs)) cs = def; } else cs = def; if (cs) width -= cs->width; if (width < 0) break; } return n; } int MyXWidthToWChars(XFontStruct *font, const XChar2b *str, int len, int width) { XCharStruct *def; int singlerow = (font->max_byte1 == 0); int n; if (!font || width < 0) return 0; if (singlerow) CI_GET_DEFAULT_INFO_1D(font, def); else CI_GET_DEFAULT_INFO_2D(font, def); for (n = 0 ; n < len ; n++) { XCharStruct *cs; if (singlerow) { int ch = (str[n].byte1 << 8) | str[n].byte2; CI_GET_CHAR_INFO_1D(font, ch, def, cs); } else { int row = str[n].byte1; int col = str[n].byte2; CI_GET_CHAR_INFO_2D(font, row, col, def, cs); } if (cs) width -= cs->width; if (width < 0) break; } return n; } /*************************************************************************/ typedef struct { XtCallbackProc proc; XtPointer client_data; } CALLBACK_ENTRY; static void WM_DELETE_WINDOW_handler(Widget w, XtPointer client_data, XEvent *event, Boolean *cont) { Display *disp = XtDisplay(w); CALLBACK_ENTRY *entry = (CALLBACK_ENTRY *)client_data; if (event->type != ClientMessage || !entry->proc || event->xclient.message_type != intern_atom(disp, "WM_PROTOCOLS") || event->xclient.data.l[0] != intern_atom(disp, "WM_DELETE_WINDOW")) return; entry->proc(w, entry->client_data, (XtPointer)event); *cont = False; } static void destroy_callback(Widget w, XtPointer client_data, XtPointer call_data) { CALLBACK_ENTRY *entry = (CALLBACK_ENTRY *)client_data; entry->proc = NULL; entry->client_data = NULL; XtFree((char *)entry); } void add_WM_DELETE_WINDOW_callback(Widget w, XtCallbackProc proc, XtPointer client_data) { Display *disp = XtDisplay(w); Window win = XtWindow(w); Atom wm_delete_window; CALLBACK_ENTRY *entry; wm_delete_window = intern_atom(disp, "WM_DELETE_WINDOW"); XSetWMProtocols(disp, win, &wm_delete_window, 1); entry = (CALLBACK_ENTRY *)XtMalloc(sizeof *entry); entry->proc = proc; entry->client_data = client_data; XtAddEventHandler(w, 0, True, WM_DELETE_WINDOW_handler, (XtPointer)entry); XtAddCallback(w, XtNdestroyCallback, destroy_callback, (XtPointer)entry); } /*************************************************************************/ int get_event_xy(XEvent *event, int *x, int *y) { if (!event) return False; switch (event->type) { case ButtonPress: case ButtonRelease: *x = event->xbutton.x; *y = event->xbutton.y; return True; case KeyPress: case KeyRelease: *x = event->xkey.x; *y = event->xkey.y; return True; case EnterNotify: case LeaveNotify: *x = event->xcrossing.x; *y = event->xcrossing.y; return True; case MotionNotify: *x = event->xmotion.x; *y = event->xmotion.y; return True; } return False; } Time get_event_time(XEvent *event) { switch (event->type) { case ButtonPress: case ButtonRelease: return event->xbutton.time; case KeyPress: case KeyRelease: return event->xkey.time; case EnterNotify: case LeaveNotify: return event->xcrossing.time; case MotionNotify: return event->xmotion.time; case PropertyNotify: return event->xproperty.time; case SelectionClear: return event->xselectionclear.time; case SelectionNotify: return event->xselection.time; case SelectionRequest: return event->xselectionrequest.time; } return CurrentTime; } /*************************************************************************/ static int my_random(int n) { static int i = 1999; i *= 13; i = i % 2371; return (n * i) / 2371; } /*************************************************************************/ int is_popped_up(Widget w) { return (XtIsSubclass(w, shellWidgetClass) && ((ShellWidget)w)->shell.popped_up); } void popup_under_pointer(Widget w, XtGrabKind grab) { Widget parent = XtParent(w); Arg pos_args[2]; int x, y, x1, y1; Window w1, w2; unsigned int m; if (!XQueryPointer(XtDisplay(parent), XtWindow(parent), &w1, &w2, &x, &y, &x1, &y1, &m)) x = y = 0; x -= my_random(w->core.width); y -= my_random(w->core.height); if (x < 0) x = 0; else { int temp = WidthOfScreen(XtScreen(parent)) - w->core.width; if (temp < 0) x = 0; else if (x > temp) x = temp; } if (y < 0) y = 0; else { int temp = HeightOfScreen(XtScreen(parent)) - w->core.height; if (temp < 0) y = 0; else if (y > temp) y = temp; } XtSetArg(pos_args[0], XtNx, x); XtSetArg(pos_args[1], XtNy, y); XtSetValues(w, pos_args, XtNumber(pos_args)); XtPopup(w, grab); } /*********************************************************************/ static void ascii_lower(char *c) { while (*c != '\0') { if ((unsigned int)(*c - 'A') <= 'Z' - 'A') *c += 'a' - 'A'; c++; } } Boolean cvt_string_to_long(Display *disp, XrmValue *args, Cardinal *no_args, XrmValue *from, XrmValue *to, XtPointer *client_data) { static long l; if (sscanf((char *)from->addr, "%ld", &l) != 1) { XtStringConversionWarning((char *)from->addr, XtRLong); return False; } if (!to->addr) to->addr = (XPointer)&l; else { if (to->size < sizeof l) { to->size = sizeof l; return False; } *(long *)to->addr = l; } to->size = sizeof l; return True; } Boolean cvt_string_to_justify(Display *disp, XrmValue *args, Cardinal *no_args, XrmValue *from, XrmValue *to, XtPointer *client_data) { static JustifyType just; static XrmQuark left = 0, center, right; XrmQuark q; char *s = (char *)from->addr; char buffer[32]; if (!s || strlen(s) > 30) return False; if (!left) { left = XrmPermStringToQuark("left"); center = XrmPermStringToQuark("center"); right = XrmPermStringToQuark("right"); } if (to->addr && to->size < sizeof just) { to->size = sizeof just; return False; } strcpy(buffer, s); ascii_lower(buffer); q = XrmStringToQuark(buffer); if (q == left) just = JustifyTypeLeft; else if (q == center) just = JustifyTypeCenter; else if (q == right) just = JustifyTypeRight; else { XtStringConversionWarning((char *)from->addr, XtRJustify); return False; } to->size = sizeof just; if (to->addr) *(JustifyType *)to->addr = just; else to->addr = (XPointer)&just; return True; } /*********************************************************************/ Boolean cvt_std_sel(Widget w, Time t, Atom *sel, Atom *target, Atom *type_ret, XPointer *val_ret, unsigned long *len_ret, int *format_ret) { Display *disp = XtDisplay(w); #if 1 if (*target == intern_atom(disp, "TIMESTAMP")) { long l = t; int i = t; if (sizeof l != 4 && sizeof i != 4) return False; *val_ret = (XPointer)XtMalloc(4); if (sizeof l == 4) memcpy(*val_ret, &l, 4); else memcpy(*val_ret, &i, 4); *type_ret = XA_INTEGER; *len_ret = 1; *format_ret = 32; return True; } #endif if (*target == intern_atom(disp, "USER")) { char *name = getenv("USER"); if (!name) return False; *val_ret = (XtPointer)XtNewString(name); *type_ret = XA_STRING; *len_ret = strlen(name); *format_ret = 8; return True; } if (*target == intern_atom(disp, "CLASS")) { char *class; int len; while (w && !XtIsApplicationShell(w)) w = XtParent(w); if (!w) return False; class = ((ApplicationShellWidget)w)->application.class; len = strlen(w->core.name); *len_ret = len + strlen(class) + 2; *val_ret = (XPointer)XtMalloc(*len_ret); strcpy((char *)*val_ret, w->core.name); strcpy((char *)*val_ret + len + 1, class); *type_ret = XA_STRING; *format_ret = 8; return True; } if (*target == intern_atom(disp, "NAME")) { while (w && !XtIsWMShell(w)) w = XtParent(w); while (w && !XtIsWMShell(w)) w = XtParent(w); if (!w) return False; *val_ret = (XPointer)XtNewString(((WMShellWidget)w)->wm.title); *len_ret = strlen((char *)*val_ret); *type_ret = XA_STRING; *format_ret = 8; return True; } if (*target == intern_atom(disp, "CLIENT_WINDOW")) { Widget p; while ((p = XtParent(w))) w = p; *val_ret = (XPointer)XtMalloc(sizeof(Window)); *(Window *)*val_ret = w->core.window; *type_ret = XA_WINDOW; *len_ret = 1; *format_ret = 32; return True; } if (*target == intern_atom(disp, "TARGETS")) { Atom *std_targets; int n = 0; std_targets = (Atom *)XtMalloc(16 * sizeof *std_targets); #if 1 std_targets[n++] = intern_atom(disp, "TIMESTAMP"); #endif std_targets[n++] = intern_atom(disp, "USER"); std_targets[n++] = intern_atom(disp, "CLASS"); std_targets[n++] = intern_atom(disp, "NAME"); std_targets[n++] = intern_atom(disp, "CLIENT_WINDOW"); *val_ret = (XPointer)std_targets; *type_ret = XA_ATOM; *len_ret = n; *format_ret = 32; return True; } return False; } /*********************************************************************/ typedef struct StippleCache StippleCache; struct StippleCache { StippleCache *next; Screen *screen; Pixmap pixmap; unsigned int ref_cnt; }; static StippleCache *stipple_cache = NULL; Pixmap create_stipple(Screen *screen) { static char bits[] = {0x01, 0x02}; StippleCache *loop; for (loop = stipple_cache ; loop ; loop = loop->next) if (loop->screen == screen) { loop->ref_cnt++; return loop->pixmap; } loop = (StippleCache *)XtMalloc(sizeof *loop); loop->next = stipple_cache; stipple_cache = loop; loop->screen = screen; loop->ref_cnt = 1; loop->pixmap = XCreatePixmapFromBitmapData(DisplayOfScreen(screen), RootWindowOfScreen(screen), bits, 2, 2, 0, 1, 1); return loop->pixmap; } void release_stipple(Screen *screen, Pixmap pixmap) { StippleCache **loop, *tmp; if (!stipple_cache) return; for (loop = &stipple_cache ; *loop ; loop = &(*loop)->next) if ((*loop)->screen == screen) break; tmp = *loop; if (!tmp || --tmp->ref_cnt != 0) return; *loop = tmp->next; XtFree((char *)tmp); } /*********************************************************************/ Visual *get_visual(Widget w) { while (w && !XtIsShell(w)) w = XtParent(w); if (!w) return NULL; return ((ShellWidget)w)->shell.visual; } void black_and_white(Screen *screen, Visual *visual, Pixel *black, Pixel *white) { Display *disp = DisplayOfScreen(screen); XVisualInfo tmp, *info; int n; *black = BlackPixelOfScreen(screen); *white = WhitePixelOfScreen(screen); if (!visual) return; tmp.visualid = XVisualIDFromVisual(visual); info = XGetVisualInfo(disp, VisualIDMask, &tmp, &n); if (!info) return; switch (info->class) { case StaticGray: *black = 0; *white = info->colormap_size - 1; /* Is this right? */ break; case GrayScale: /* ?? */ break; case StaticColor: *black = 0; *white = info->red_mask | info->green_mask | info->blue_mask; break; case PseudoColor: /* ?? */ break; case TrueColor: *black = 0; *white = info->red_mask | info->green_mask | info->blue_mask; case DirectColor: /* ?? */ break; } XFree((char *)info); } Pixel get_black(Widget w) { Screen *screen = XtScreen(w); Visual *visual = get_visual(w); Pixel black, white; black_and_white(screen, visual, &black, &white); return black; } /*********************************************************************/ typedef struct AtomCache { struct AtomCache *next; Display *disp; XrmQuark name; Atom atom; } AtomCache; /* * This shouldn't grow to deep... */ static AtomCache *atom_cache = NULL; Atom intern_atom(Display *disp, char *name_str) { XrmQuark name = XrmStringToQuark(name_str); AtomCache *loop = atom_cache; for (loop = atom_cache ; loop ; loop = loop->next) if (loop->name == name && loop->disp == disp) return loop->atom; loop = (AtomCache *)XtMalloc(sizeof *loop); loop->next = atom_cache; loop->disp = disp; loop->name = name; loop->atom = XInternAtom(disp, name_str, False); atom_cache = loop; return loop->atom; } ./knews-1.0b.1/Widgets/FileSelP.h100644 1244 1244 2462 6455455535 15064 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #ifndef FileSelP_h #define FileSelP_h #include "FileSel.h" #include typedef struct { XtPointer empty; } FileSelClassPart; typedef struct FileSelClassRec { CoreClassPart core_class; CompositeClassPart composite_class; ShellClassPart shell_class; WMShellClassPart wm_shell_class; VendorShellClassPart vendor_shell_class; TopLevelShellClassPart top_level_shell_class; FileSelClassPart filesel_class; } FileSelClassRec; extern FileSelClassRec fileSelClassRec; #define MAX_FILE_TYPE 5 typedef struct { XtCallbackList callback; Cursor cursor; Cursor busy_cursor; String directory; Dimension pref_cols; Boolean show_dot_files; /* private data */ Widget layout; Widget dir_label; Widget dir_field; Widget file_label; Widget file_field; Widget list; Widget knapp_cancel; Widget knapp_choose; Widget show_toggle; unsigned char *types; Pixmap pixmap[MAX_FILE_TYPE + 1]; } FileSelPart; typedef struct FileSelRec { CorePart core; CompositePart composite; ShellPart shell; WMShellPart wm; VendorShellPart vendor; TopLevelShellPart top_level; FileSelPart filesel; } FileSelRec; #endif /* FileSelP_h */ ./knews-1.0b.1/Widgets/Knapp.h100644 1244 1244 1617 6455455535 14473 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #ifndef Knapp_h #define Knapp_h #ifndef XtCInternalHeight #define XtCInternalHeight "InternalHeight" #endif #ifndef XtCLeftMargin #define XtCLeftMargin "LeftMargin" #endif #ifndef XtCRightMargin #define XtCRightMargin "RightMargin" #endif #ifndef XtCResizable #define XtCResizable "Resizable" #endif #ifndef XtNleftMargin #define XtNleftMargin "leftMargin" #endif #ifndef XtNrightMargin #define XtNrightMargin "rightMargin" #endif #ifndef XtNresizable #define XtNresizable "resizable" #endif typedef struct KnappClassRec* KnappWidgetClass; typedef struct KnappRec* KnappWidget; extern WidgetClass knappWidgetClass; extern void KnappSetLabel(Widget, char*); extern void KnappSetActive(Widget, int); extern void KnappSetLabelNo(Widget, int, int); extern int KnappGetLabelNo(Widget); extern void KnappSetSensitive(Widget, int); #endif /* Knapp_h */ ./knews-1.0b.1/Widgets/Knapp.c100644 1244 1244 37333 6455455535 14512 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include #include #include "Compat.h" #include "Util.h" #include "KnappP.h" static XtResource resources[] = { {XtNborderWidth, XtCBorderWidth, XtRDimension, sizeof(Dimension), XtOffsetOf(KnappRec, core.border_width), XtRImmediate, (XtPointer)1}, #define offset(field) XtOffsetOf(KnappRec, knapp.field) {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel), offset(foreground_pixel), XtRString, XtDefaultForeground}, {XtNfont, XtCFont, XtRFontStruct, sizeof(XFontStruct *), offset(font), XtRString, XtDefaultFont}, {XtNlabel, XtCLabel, XtRString, sizeof(String), offset(label), XtRString, (XtPointer)NULL}, {XtNleftMargin, XtCLeftMargin, XtRDimension, sizeof(Dimension), offset(left_margin), XtRImmediate, (XtPointer)8}, {XtNrightMargin, XtCRightMargin, XtRDimension, sizeof(Dimension), offset(right_margin), XtRImmediate, (XtPointer)8}, {XtNinternalHeight, XtCInternalHeight, XtRDimension, sizeof(Dimension), offset(internal_height), XtRImmediate, (XtPointer)2}, {XtNcallback, XtCCallback, XtRCallback, sizeof(XtCallbackList), offset(callback), XtRCallback, (XtPointer)NULL}, {XtNresizable, XtCResizable, XtRBoolean, sizeof(Boolean), offset(resizable), XtRImmediate, (XtPointer)True}, {XtNjustify, XtCJustify, XtRJustify, sizeof(JustifyType), offset(justify), XtRImmediate, (XtPointer)JustifyTypeLeft}, #undef offset }; static void ClassInitialize(void); static void Initialize(Widget, Widget, ArgList, Cardinal*); static void Destroy(Widget); static void Resize(Widget); static void Redisplay(Widget, XEvent*, Region); static Boolean SetValues(Widget, Widget, Widget, ArgList, Cardinal*); static XtGeometryResult QueryGeometry(Widget, XtWidgetGeometry*, XtWidgetGeometry*); static void set(Widget, XEvent*, String*, Cardinal*); static void reset(Widget, XEvent*, String*, Cardinal*); static void abort_action(Widget, XEvent*, String*, Cardinal*); static void notify(Widget, XEvent*, String*, Cardinal*); static XtActionsRec actions[] = { {"set", set}, {"reset", reset}, {"abort", abort_action}, {"notify", notify}, }; static char translations[] = ": set() \n" ": notify() reset() \n" ": abort() \n"; KnappClassRec knappClassRec = { { /* core fields */ (WidgetClass) &shadowClassRec, /* superclass */ "Knapp", /* class_name */ sizeof(KnappRec), /* widget_size */ ClassInitialize, /* class_initialize */ NULL, /* class_part_initialize */ FALSE, /* class_inited */ Initialize, /* initialize */ NULL, /* initialize_hook */ XtInheritRealize, /* realize */ actions, /* actions */ XtNumber(actions), /* num_actions */ resources, /* resources */ XtNumber(resources), /* num_resources */ NULLQUARK, /* xrm_class */ TRUE, /* compress_motion */ #if (XtSpecificationRelease < 4) True, /* compress_exposure */ #elif (XtSpecificationRelease < 6) XtExposeCompressMaximal, /* compress_exposure */ #else XtExposeCompressMaximal | XtExposeNoRegion, /* compress_exposure*/ #endif TRUE, /* compress_enterleave */ FALSE, /* visible_interest */ Destroy, /* destroy */ Resize, /* resize */ Redisplay, /* expose */ SetValues, /* set_values */ NULL, /* set_values_hook */ XtInheritSetValuesAlmost, /* set_values_almost */ NULL, /* get_values_hook */ NULL, /* accept_focus */ XtVersion, /* version */ NULL, /* callback_private */ translations, /* tm_table */ QueryGeometry, /* query_geometry */ XtInheritDisplayAccelerator, /* display_accelerator */ NULL /* extension */ }, { /* shadow fields */ XtInheritPixelOffset, /* pixel_offset */ False, /* use_arm_for_background */ XtInheritAllocShadowColors, /* alloc_shadow_colors */ XtInheritAllocShadowPixmaps, /* alloc_shadow_pixmaps */ XtInheritAllocArmColor, /* alloc_arm_color */ XtInheritAllocArmPixmap, /* alloc_arm_pixmap */ XtInheritAllocGCs, /* alloc_gcs */ NULL, /* extension */ }, { /* knapp fields */ 0 /* extension */ } }; WidgetClass knappWidgetClass = (WidgetClass)&knappClassRec; /*************************************************************************/ static void set(Widget gw, XEvent *event, String *params, Cardinal *no_params) { KnappWidget w = (KnappWidget)gw; if (w->knapp.active) { w->knapp.set = True; XClearWindow(XtDisplay(w), XtWindow(w)); w->core.widget_class->core_class.expose((Widget)w, NULL, 0); } } static void abort_action(Widget gw, XEvent *event, String *params, Cardinal *no_params) { KnappWidget w = (KnappWidget)gw; if (!w->knapp.calling_callbacks && w->knapp.set) { w->knapp.set = False; XClearWindow(XtDisplay(w), XtWindow(w)); w->core.widget_class->core_class.expose((Widget)w, NULL, 0); } } static void reset(Widget gw, XEvent *event, String *params, Cardinal *no_params) { KnappWidget w = (KnappWidget)gw; if (!w->knapp.calling_callbacks && w->knapp.set) { w->knapp.set = False; XClearWindow(XtDisplay(w), XtWindow(w)); w->core.widget_class->core_class.expose((Widget)w, NULL, 0); } } static void notify(Widget gw, XEvent *event, String *params, Cardinal *no_params) { KnappWidget w = (KnappWidget)gw; XtCallbackList c_list = w->knapp.callback; if (!w->knapp.set || !w->knapp.active || !c_list) return; w->knapp.calling_callbacks = True; XtCallCallbackList((Widget)w, c_list, NULL); w->knapp.calling_callbacks = False; } /*************************************************************************/ static void init_gcs(KnappWidget w) { XGCValues values; values.foreground = w->knapp.foreground_pixel; values.font = w->knapp.font->fid; w->knapp.default_gc = XtGetGC((Widget)w, GCForeground | GCFont, &values); values.stipple = w->knapp.stipple; values.fill_style = FillStippled; w->knapp.gray_gc = XtGetGC((Widget)w, GCForeground | GCFont | GCFillStyle | GCStipple, &values); } static void free_gcs(KnappWidget w) { XtReleaseGC((Widget)w, w->knapp.default_gc); XtReleaseGC((Widget)w, w->knapp.gray_gc); } static void compute_label_x(KnappWidget w) { switch (w->knapp.justify) { case JustifyTypeCenter: w->knapp.label_x = (int)(w->core.width - w->knapp.label_width + w->knapp.left_margin - w->knapp.right_margin) / 2; break; case JustifyTypeRight: w->knapp.label_x = w->core.width - (w->shadow.shadow_width + w->knapp.right_margin + w->knapp.label_width); break; case JustifyTypeLeft: default: w->knapp.label_x = w->shadow.shadow_width + w->knapp.left_margin; break; } } static void init_labels(KnappWidget w, char *label) { int max = 0, n = 0; if (!label) label = w->core.name; do { char *c = strchr(label, '\n'); int len, tmp; if (c) len = c - label; else len = strlen(label); if (n + 2 > w->knapp.no_labels) { int i = w->knapp.no_labels; w->knapp.no_labels = 2 * (i + 2); w->knapp.labels = (char **)XtRealloc((char *)w->knapp.labels, w->knapp.no_labels * sizeof(char *)); while (i < w->knapp.no_labels) w->knapp.labels[i++] = NULL; } w->knapp.labels[n] = XtMalloc(len + 1); memcpy(w->knapp.labels[n], label, len); w->knapp.labels[n][len] = '\0'; n++; tmp = XTextWidth(w->knapp.font, label, len); if (tmp > max) max = tmp; label = c; if (label) label++; } while (label); w->knapp.no_labels = n; w->knapp.max_width = max; } void free_labels(KnappWidget w) { int n; for (n = 0 ; n < w->knapp.no_labels ; n++) XtFree(w->knapp.labels[n]); XtFree((char *)w->knapp.labels); w->knapp.labels = NULL; w->knapp.no_labels = 0; w->knapp.label_no = 0; } void compute_label_width(KnappWidget w) { int n = w->knapp.label_no; if (w->knapp.no_labels == 0) { w->knapp.label_width = 0; return; } if (n < 0 || n >= w->knapp.no_labels) n = 0; w->knapp.label_width = XTextWidth(w->knapp.font, w->knapp.labels[n], strlen(w->knapp.labels[n])); } /*************************************************************************/ static void ClassInitialize() { XtSetTypeConverter(XtRString, XtRJustify, cvt_string_to_justify, NULL, 0, XtCacheAll, NULL); } static void Initialize(Widget grequest, Widget gnew, ArgList args, Cardinal *no_args) { KnappWidget new = (KnappWidget)gnew; new->knapp.stipple = create_stipple(XtScreen(new)); init_gcs(new); new->knapp.set = False; new->knapp.calling_callbacks = False; new->knapp.active = True; new->knapp.labels = NULL; new->knapp.no_labels = 0; new->knapp.label_no = 0; init_labels(new, new->knapp.label); new->knapp.label = NULL; compute_label_width(new); if (new->core.width == 0) new->core.width = new->knapp.max_width + 2 * new->shadow.shadow_width + new->knapp.left_margin + new->knapp.right_margin; if (new->core.height == 0) new->core.height = 2 * (new->shadow.shadow_width + new->knapp.internal_height) + new->knapp.font->ascent + new->knapp.font->descent; compute_label_x(new); } static void Destroy(Widget gw) { KnappWidget w = (KnappWidget)gw; free_labels(w); free_gcs(w); release_stipple(XtScreen(w), w->knapp.stipple); } static void Resize(Widget gw) { KnappWidget w = (KnappWidget)gw; compute_label_x(w); } static void Redisplay(Widget gw, XEvent *event, Region region) { KnappWidget w = (KnappWidget)gw; Display *disp = XtDisplay(w); Window win = XtWindow(w); int sw = w->shadow.shadow_width; if (!XtIsRealized((Widget)w) || (int)w->core.width < 2 * sw || (int)w->core.height < 2 * sw) return; if (w->knapp.set && w->shadow.arm_gc != 0) XFillRectangle(disp, win, w->shadow.arm_gc, sw, sw, w->core.width - 2 * sw, w->core.height - 2 * sw); if (w->knapp.label_no >= 0 && w->knapp.label_no < w->knapp.no_labels) { char *label = w->knapp.labels[w->knapp.label_no]; XDrawString(disp, win, w->core.sensitive ? w->knapp.default_gc : w->knapp.gray_gc, w->knapp.label_x, (int)(w->core.height - w->knapp.font->descent + w->knapp.font->ascent) / 2 + (w->knapp.set ? 1 : 0), label, strlen(label)); } ShadowDrawShadows((ShadowWidget)w, 0, 0, w->core.width, w->core.height, w->knapp.set); } static Boolean SetValues(Widget gcurrent, Widget grequest, Widget gnew, ArgList args, Cardinal *num_args) { KnappWidget new = (KnappWidget)gnew; KnappWidget current = (KnappWidget)gcurrent; Boolean redisplay = False; if (new->shadow.shadow_width != current->shadow.shadow_width || new->knapp.font != current->knapp.font || new->knapp.label != current->knapp.label || new->knapp.left_margin != current->knapp.left_margin || new->knapp.right_margin != current->knapp.right_margin || new->knapp.internal_height != current->knapp.internal_height) { if (new->knapp.label != current->knapp.label) { free_labels(current); init_labels(new, new->knapp.label); new->knapp.label = NULL; compute_label_width(new); } compute_label_x(new); if (new->knapp.resizable) { new->core.width = new->knapp.max_width + 2 * new->shadow.shadow_width + new->knapp.left_margin + new->knapp.right_margin; new->core.height = 2 * (new->shadow.shadow_width + new->knapp.internal_height) + new->knapp.font->ascent + new->knapp.font->descent; } redisplay = True; } else if (new->knapp.justify != current->knapp.justify) { compute_label_x(new); redisplay = True; } if (XtIsSensitive((Widget)new) != XtIsSensitive((Widget)current)) { redisplay = True; if (!XtIsSensitive((Widget)new)) new->knapp.set = False; } return redisplay; } static XtGeometryResult QueryGeometry(Widget gw, XtWidgetGeometry *intended, XtWidgetGeometry *preferred) { KnappWidget w = (KnappWidget)gw; Dimension intended_width, intended_height; preferred->request_mode = CWWidth | CWHeight; preferred->width = w->knapp.max_width + 2 * w->shadow.shadow_width + w->knapp.left_margin + w->knapp.right_margin; preferred->height = w->knapp.font->ascent + w->knapp.font->descent + 2 * (w->shadow.shadow_width + w->knapp.internal_height); if (intended->request_mode & CWWidth) intended_width = intended->width; else intended_width = w->core.width; if (intended->request_mode & CWHeight) intended_height = intended->height; else intended_height = w->core.height; if (preferred->width == w->core.width && preferred->height == w->core.height) return XtGeometryNo; else if (preferred->width == intended_width && preferred->height == intended_height) return XtGeometryYes; else return XtGeometryAlmost; } /*************************************************************************/ void KnappSetLabel(Widget gw, char *label) { KnappWidget w = (KnappWidget)gw; init_labels(w, label); compute_label_width(w); compute_label_x(w); if (w->knapp.resizable) { Dimension width, height; width = w->knapp.max_width + 2 * w->shadow.shadow_width + w->knapp.left_margin + w->knapp.right_margin; height = 2 * (w->shadow.shadow_width + w->knapp.internal_height) + w->knapp.font->ascent + w->knapp.font->descent; (void)XtMakeResizeRequest((Widget)w, width, height, NULL, NULL); } if (XtIsRealized((Widget)w)) { XClearWindow(XtDisplay(w), XtWindow(w)); w->core.widget_class->core_class.expose((Widget)w, NULL, 0); } } void KnappSetSensitive(Widget gw, int sensitive) { KnappWidget w = (KnappWidget)gw; if (gw->core.sensitive == sensitive) return; gw->core.sensitive = sensitive; if (!sensitive) w->knapp.set = False; if (XtIsRealized((Widget)w)) { XClearWindow(XtDisplay(w), XtWindow(w)); w->core.widget_class->core_class.expose((Widget)w, NULL, 0); } } void KnappSetActive(Widget gw, int active) { KnappWidget w = (KnappWidget)gw; w->knapp.active = active; } void KnappSetLabelNo(Widget gw, int label_no, int sensitive) { KnappWidget w = (KnappWidget)gw; w->knapp.label_no = label_no; compute_label_width(w); compute_label_x(w); w->core.sensitive = sensitive; if (XtIsRealized((Widget)w)) { XClearWindow(XtDisplay(w), XtWindow(w)); w->core.widget_class->core_class.expose((Widget)w, NULL, 0); } } int KnappGetLabelNo(Widget gw) { KnappWidget w = (KnappWidget)gw; return w->knapp.label_no; } ./knews-1.0b.1/Widgets/ScrBar.c100644 1244 1244 71155 6455455535 14615 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include #include #include #include "Compat.h" #include "ScrBarP.h" #include "Util.h" static XtResource resources[] = { #define offset(field) XtOffsetOf(ScrBarRec, scrbar.field) {XtNminimumThumb, XtCMinimumThumb, XtRDimension, sizeof(Dimension), offset(minimum_thumb), XtRImmediate, (XtPointer)8}, {XtNpushThumb, XtCPushThumb, XtRBoolean, sizeof(Boolean), offset(push_thumb), XtRImmediate, (XtPointer)True}, {XtNcanvasLength, XtCCanvasLength, XtRLong, sizeof(long), offset(canvas_length), XtRImmediate, (XtPointer)0}, {XtNsliderLength, XtCSliderLength, XtRLong, sizeof(long), offset(slider_length), XtRImmediate, (XtPointer)1}, {XtNsliderPosition, XtCSliderPosition, XtRLong, sizeof(long), offset(slider_position), XtRImmediate, (XtPointer)0}, {XtNstepSize, XtCStepSize, XtRLong, sizeof(long), offset(step_size), XtRImmediate, (XtPointer)1}, {XtNthickness, XtCThickness, XtRDimension, sizeof(Dimension), offset(thickness), XtRImmediate, (XtPointer)15}, {XtNdecay, XtCDecay, XtRInt, sizeof(int), offset(decay), XtRImmediate, (XtPointer)20}, {XtNinitialDelay, XtCDelay, XtRInt, sizeof(int), offset(initial_delay), XtRImmediate, (XtPointer)200}, {XtNminimumDelay, XtCMinimumDelay, XtRInt, sizeof(int), offset(minimum_delay), XtRImmediate, (XtPointer)40}, {XtNvertical, XtCVertical, XtRBoolean, sizeof(Boolean), offset(vertical), XtRImmediate, (XtPointer)True}, {XtNallowOff, XtCAllocOff, XtRBoolean, sizeof(Boolean), offset(allow_off), XtRImmediate, (XtPointer)False}, {XtNsyncScroll, XtCSyncScroll, XtRBoolean, sizeof(Boolean), offset(sync_scroll), XtRImmediate, (XtPointer)True}, {XtNscrollCallback, XtCScrollCallback, XtRCallback, sizeof(XtCallbackList), offset(scroll_callback), XtRCallback, (XtPointer)NULL}, #undef offset }; static void ClassInitialize(void); static void Initialize(Widget, Widget, ArgList, Cardinal*); static void Destroy(Widget); static void Resize(Widget); static void Redisplay(Widget, XEvent*, Region); static Boolean SetValues(Widget, Widget, Widget, ArgList, Cardinal*); static XtGeometryResult QueryGeometry(Widget, XtWidgetGeometry*, XtWidgetGeometry*); static void move_thumb(Widget, XEvent*, String*, Cardinal*); static void start_scroll(Widget, XEvent*, String*, Cardinal*); static void cont_scroll(Widget, XEvent*, String*, Cardinal*); static void stop_scroll(Widget, XEvent*, String*, Cardinal*); static void page_scroll(Widget, XEvent*, String*, Cardinal*); static void abs_scroll(Widget, XEvent*, String*, Cardinal*); static void athena_scroll(Widget, XEvent*, String*, Cardinal*); static void nop(Widget, XEvent*, String*, Cardinal*); static XtActionsRec actions[] = { {"move-thumb", move_thumb}, {"start-scroll", start_scroll}, {"cont-scroll", cont_scroll}, {"stop-scroll", stop_scroll}, {"page-scroll", page_scroll}, {"abs-scroll", abs_scroll}, {"athena-scroll", athena_scroll}, {"nop", nop}, }; static char translations[] = ": start-scroll() \n" ": cont-scroll() \n" ": stop-scroll() \n" ": move-thumb() \n" ": move-thumb() \n"; ScrBarClassRec scrBarClassRec = { { /* core fields */ (WidgetClass) &shadowClassRec, /* superclass */ "ScrBar", /* class_name */ sizeof(ScrBarRec), /* widget_size */ ClassInitialize, /* class_initialize */ NULL, /* class_part_initialize */ FALSE, /* class_inited */ Initialize, /* initialize */ NULL, /* initialize_hook */ XtInheritRealize, /* realize */ actions, /* actions */ XtNumber(actions), /* num_actions */ resources, /* resources */ XtNumber(resources), /* num_resources */ NULLQUARK, /* xrm_class */ TRUE, /* compress_motion */ #if (XtSpecificationRelease < 4) TRUE, /* compress_exposure */ #elif (XtSpecificationRelease < 6) XtExposeCompressMaximal, /* compress_exposure */ #else XtExposeCompressMaximal | XtExposeNoRegion, /* compress_exposure*/ #endif TRUE, /* compress_enterleave */ FALSE, /* visible_interest */ Destroy, /* destroy */ Resize, /* resize */ Redisplay, /* expose */ SetValues, /* set_values */ NULL, /* set_values_hook */ XtInheritSetValuesAlmost, /* set_values_almost */ NULL, /* get_values_hook */ NULL, /* accept_focus */ XtVersion, /* version */ NULL, /* callback_private */ translations, /* tm_table */ QueryGeometry, /* query_geometry */ XtInheritDisplayAccelerator, /* display_accelerator */ NULL /* extension */ }, { /* shadow fields */ XtInheritPixelOffset, /* pixel_offset */ True, /* use_arm_for_background */ XtInheritAllocShadowColors, /* alloc_shadow_colors */ XtInheritAllocShadowPixmaps, /* alloc_shadow_pixmaps */ XtInheritAllocArmColor, /* alloc_arm_color */ XtInheritAllocArmPixmap, /* alloc_arm_pixmap */ XtInheritAllocGCs, /* alloc_gcs */ NULL, /* extension */ }, { /* scrbar fields */ 0 /* empty */ } }; WidgetClass scrBarWidgetClass = (WidgetClass)&scrBarClassRec; /*************************************************************************/ static void init_gcs(ScrBarWidget w) { XGCValues values; values.foreground = w->core.background_pixel; w->scrbar.bg_gc = XtGetGC((Widget)w, GCForeground, &values); } static void free_gcs(ScrBarWidget w) { XtReleaseGC((Widget)w, w->scrbar.bg_gc); } /*************************************************************************/ static void calc_scrbar_coords(ScrBarWidget w) { long length, thick; long c1, c2, c3, c4; if (w->scrbar.vertical) { length = w->core.height; thick = w->core.width; } else { length = w->core.width; thick = w->core.height; } c1 = thick; c4 = length - thick; if (c4 <= c1) c1 = c2 = c3 = c4 = length/2; else if (w->scrbar.canvas_length == 0) { c2 = c1; c3 = c4; } else { length -= 2 * thick; if (length > 0) { c2 = c1 + (w->scrbar.slider_position * length) / w->scrbar.canvas_length; c3 = c1 + (w->scrbar.slider_position + w->scrbar.slider_length) * length / w->scrbar.canvas_length; if (c3 > c4) c3 = c4; if (c3 - c2 < (int)w->scrbar.minimum_thumb) { long c = (c2 + c3)/2 - c1; long dc = w->scrbar.minimum_thumb/2; if (dc > length/2) dc = length/2; if (c - dc < 0) c = dc; if (c + dc > length) c = length - dc; c2 = c1 + c - dc; c3 = c1 + c + dc; } } else { c2 = c1; c3 = c4; } } w->scrbar.c1 = c1; w->scrbar.c2 = c2; w->scrbar.c3 = c3; w->scrbar.c4 = c4; } static void draw_up_arrow(ScrBarWidget w) { Display *disp = XtDisplay(w); Window win = XtWindow(w); short sw = w->shadow.shadow_width; if (w->shadow.line_mode) { XPoint points[4]; points[0].x = w->core.width/2; points[0].y = (3 * sw)/2 - 1; points[1].x = w->core.width - sw - 1; points[1].y = w->scrbar.c1 - 1; points[2].x = sw; points[2].y = w->scrbar.c1 - 1; points[3] = points[0]; if (w->scrbar.mode == ScrBarModeBackwardLine) XFillPolygon(disp, win, w->shadow.light_gc, points, 4, Convex, CoordModeOrigin); else { XFillPolygon(disp, win, w->scrbar.bg_gc, points, 4, Convex, CoordModeOrigin); XDrawLines(disp, win, w->shadow.light_gc, points, 4, CoordModeOrigin); } } else { XPoint points[6]; points[0].x = w->core.width/2; points[0].y = sw - 1; points[1].x = w->core.width/2; points[1].y = sw - 1 + (9 * sw)/4; points[2].x = w->core.width - sw - (3 * sw)/2 + 1; points[2].y = w->scrbar.c1 - sw; points[3].x = sw + (3 * sw)/2 - 1; points[3].y = w->scrbar.c1 - sw; points[4].x = sw - 1; points[4].y = w->scrbar.c1; points[5].x = w->core.width - sw; points[5].y = w->scrbar.c1; XFillPolygon(disp, win, (w->scrbar.mode == ScrBarModeBackwardLine) ? w->shadow.light_gc : w->shadow.dark_gc, points, 6, Complex, CoordModeOrigin); XFillPolygon(disp, win, w->scrbar.bg_gc, &points[1], 3, Convex, CoordModeOrigin); points[2] = points[3]; points[3] = points[4]; XFillPolygon(disp, win, (w->scrbar.mode == ScrBarModeBackwardLine) ? w->shadow.dark_gc : w->shadow.light_gc, points, 4, Complex, CoordModeOrigin); } } static void draw_down_arrow(ScrBarWidget w) { Display *disp = XtDisplay(w); Window win = XtWindow(w); short sw = w->shadow.shadow_width; if (w->shadow.line_mode) { XPoint points[4]; points[0].x = w->core.width/2; points[0].y = w->core.height - (3 * sw)/2; points[1].x = w->core.width - sw - 1; points[1].y = w->scrbar.c4; points[2].x = sw; points[2].y = w->scrbar.c4; points[3] = points[0]; if (w->scrbar.mode == ScrBarModeForwardLine) XFillPolygon(disp, win, w->shadow.light_gc, points, 4, Convex, CoordModeOrigin); else { XFillPolygon(disp, win, w->scrbar.bg_gc, points, 4, Convex, CoordModeOrigin); XDrawLines(disp, win, w->shadow.light_gc, points, 4, CoordModeOrigin); } } else { XPoint points[6]; points[0].x = w->core.width/2; points[0].y = w->core.height - sw; points[1].x = w->core.width/2; points[1].y = w->core.height - sw - (9 * sw)/4; points[2].x = sw + (3 * sw)/2 - 1; points[2].y = w->scrbar.c4 + sw; points[3].x = w->core.width - sw - (3 * sw)/2; points[3].y = w->scrbar.c4 + sw; points[4].x = w->core.width - sw; points[4].y = w->scrbar.c4; points[5].x = sw; points[5].y = w->scrbar.c4; XFillPolygon(disp, win, (w->scrbar.mode == ScrBarModeForwardLine) ? w->shadow.dark_gc : w->shadow.light_gc, points, 6, Complex, CoordModeOrigin); XFillPolygon(disp, win, w->scrbar.bg_gc, &points[1], 3, Convex, CoordModeOrigin); points[2] = points[3]; points[3] = points[4]; XFillPolygon(disp, win, (w->scrbar.mode == ScrBarModeForwardLine) ? w->shadow.light_gc : w->shadow.dark_gc, points, 4, Complex, CoordModeOrigin); } } static void draw_left_arrow(ScrBarWidget w) { Display *disp = XtDisplay(w); Window win = XtWindow(w); short sw = w->shadow.shadow_width; if (w->shadow.line_mode) { XPoint points[4]; points[0].x = (3 * sw)/2; points[0].y = w->core.height/2; points[1].x = w->scrbar.c1 - 1; points[1].y = w->core.height - sw - 1; points[2].x = w->scrbar.c1 - 1; points[2].y = sw; points[3] = points[0]; if (w->scrbar.mode == ScrBarModeBackwardLine) XFillPolygon(disp, win, w->shadow.light_gc, points, 4, Convex, CoordModeOrigin); else { XFillPolygon(disp, win, w->scrbar.bg_gc, points, 4, Convex, CoordModeOrigin); XDrawLines(disp, win, w->shadow.light_gc, points, 4, CoordModeOrigin); } } else { XPoint points[6]; points[0].x = sw; points[0].y = w->core.height/2; points[1].x = sw + (9 * sw)/4;/**/ points[1].y = w->core.height/2; points[2].x = w->scrbar.c1 - sw; points[2].y = w->core.height - sw - (3 * sw)/2; points[3].x = w->scrbar.c1 - sw; points[3].y = sw + (3 * sw)/2 - 1; points[4].x = w->scrbar.c1; points[4].y = sw - 1; points[5].x = w->scrbar.c1; points[5].y = w->core.height - sw; XFillPolygon(disp, win, (w->scrbar.mode == ScrBarModeBackwardLine) ? w->shadow.light_gc : w->shadow.dark_gc, points, 6, Complex, CoordModeOrigin); XFillPolygon(disp, win, w->scrbar.bg_gc, &points[1], 3, Convex, CoordModeOrigin); points[2] = points[3]; points[3] = points[4]; XFillPolygon(disp, win, (w->scrbar.mode == ScrBarModeBackwardLine) ? w->shadow.dark_gc : w->shadow.light_gc, points, 4, Complex, CoordModeOrigin); } } static void draw_right_arrow(ScrBarWidget w) { Display *disp = XtDisplay(w); Window win = XtWindow(w); short sw = w->shadow.shadow_width; if (w->shadow.line_mode) { XPoint points[4]; points[0].x = w->core.width - (3 * sw)/2; points[0].y = w->core.height/2; points[1].x = w->scrbar.c4; points[1].y = w->core.height - sw - 1; points[2].x = w->scrbar.c4; points[2].y = sw; points[3] = points[0]; if (w->scrbar.mode == ScrBarModeForwardLine) XFillPolygon(disp, win, w->shadow.light_gc, points, 4, Convex, CoordModeOrigin); else { XFillPolygon(disp, win, w->scrbar.bg_gc, points, 4, Convex, CoordModeOrigin); XDrawLines(disp, win, w->shadow.light_gc, points, 4, CoordModeOrigin); } } else { XPoint points[6]; points[0].x = w->core.width - sw; points[0].y = w->core.height/2; points[1].x = w->core.width - sw - (9 * sw)/4; points[1].y = w->core.height/2; points[2].x = w->scrbar.c4 + sw; points[2].y = sw + (3 * sw)/2 - 1; points[3].x = w->scrbar.c4 + sw; points[3].y = w->core.height - sw - (3 * sw)/2 + 1; points[4].x = w->scrbar.c4; points[4].y = w->core.height - sw; points[5].x = w->scrbar.c4; points[5].y = sw - 1; XFillPolygon(disp, win, (w->scrbar.mode == ScrBarModeForwardLine) ? w->shadow.dark_gc : w->shadow.light_gc, points, 6, Complex, CoordModeOrigin); XFillPolygon(disp, win, w->scrbar.bg_gc, &points[1], 3, Convex, CoordModeOrigin); points[2] = points[3]; points[3] = points[4]; XFillPolygon(disp, win, (w->scrbar.mode == ScrBarModeForwardLine) ? w->shadow.light_gc : w->shadow.dark_gc, points, 4, Complex, CoordModeOrigin); } } static void draw_thumb(ScrBarWidget w, Region region) { Display *disp = XtDisplay(w); Window win = XtWindow(w); int sw = w->shadow.shadow_width; int c1 = w->scrbar.c1; int c2 = w->scrbar.c2; int c3 = w->scrbar.c3; int c4 = w->scrbar.c4; if (w->scrbar.vertical) { int width = w->core.width - 2 * sw; if (c3 > c2 && (!region || XRectInRegion(region, sw, c2, width, c3 - c2) != RectangleOut)) { ShadowDrawShadows((ShadowWidget)w, sw, c2, width, c3 - c2, (w->scrbar.mode == ScrBarModeContinuous) && w->scrbar.push_thumb); if (w->shadow.line_mode && !(w->scrbar.mode == ScrBarModeContinuous && w->scrbar.push_thumb)) { if (c3 - c2 - 2 > 0) XFillRectangle(disp, win, w->scrbar.bg_gc, sw + 1, c2 + 1, width - 2, c3 - c2 - 2); } else { if (c3 - c2 - 2 * sw > 0) XFillRectangle(disp, win, w->scrbar.bg_gc, 2 * sw, c2 + sw, width - 2 * sw, c3 - c2 - 2 * sw); } } if (c2 > c1 && (!region || XRectInRegion(region, sw, c1, width, c2 - c1) != RectangleOut)) XClearArea(disp, win, sw, c1, width, c2 - c1, False); if (c4 > c3 && (!region || XRectInRegion(region, sw, c3, width, c4 - c3) != RectangleOut)) XClearArea(disp, win, sw, c3, width, c4 - c3, False); } else { int height = w->core.height - 2 * sw; if (!region || XRectInRegion(region, c2, sw, c3 - c2, height) != RectangleOut) { ShadowDrawShadows((ShadowWidget)w, c2, sw, c3 - c2, height, (w->scrbar.mode == ScrBarModeContinuous) && w->scrbar.push_thumb); if (w->shadow.line_mode && !(w->scrbar.mode == ScrBarModeContinuous && w->scrbar.push_thumb)) { if (c3 - c2 - 2 > 0) XFillRectangle(disp, win, w->scrbar.bg_gc, c2 + 1, sw + 1, c3 - c2 - 2, height - 2); } else if (c3 -c2 - 2 * sw > 0) XFillRectangle(disp, win, w->scrbar.bg_gc, c2 + sw, 2 * sw, c3 - c2 - 2 * sw, height - 2 * sw); } if (c2 > c1 && (!region || XRectInRegion(region, c1, sw, c2 - c1, height) != RectangleOut)) XClearArea(disp, win, c1, sw, c2 - c1, height, False); if (c4 > c3 && (!region || XRectInRegion(region, c3, sw, c4 - c3, height) != RectangleOut)) XClearArea(disp, win, c3, sw, c4 - c3, height, False); } } static void draw_arrows(ScrBarWidget w) { if (w->scrbar.vertical) { draw_up_arrow(w); draw_down_arrow(w); } else { draw_left_arrow(w); draw_right_arrow(w); } } static void fit_scrbar(ScrBarWidget w) { if (w->scrbar.slider_position < 0) w->scrbar.slider_position = 0; else if (w->scrbar.allow_off) { if (w->scrbar.slider_position >= w->scrbar.canvas_length) { if (w->scrbar.canvas_length == 0) w->scrbar.slider_position = 0; else w->scrbar.slider_position = w->scrbar.canvas_length - 1; } } else if (w->scrbar.slider_position + w->scrbar.slider_length > w->scrbar.canvas_length) { w->scrbar.slider_position = w->scrbar.canvas_length - w->scrbar.slider_length; if (w->scrbar.slider_position < 0) w->scrbar.slider_position = 0; } } static void send_scroll_report(ScrBarWidget w) { XtCallbackList c_list = w->scrbar.scroll_callback; if (c_list) { ScrollReport report; report.pos = w->scrbar.slider_position; report.shown = w->scrbar.slider_length; report.size = w->scrbar.canvas_length; XtCallCallbackList((Widget)w, c_list, (XtPointer)&report); } } /*************************************************************************/ static void scroll_timer_proc(XtPointer client_data, XtIntervalId *id) { ScrBarWidget w = (ScrBarWidget)client_data; w->scrbar.timer = 0; /* * We get rid of a race by explicitly quering * the pointer for the modifiers. */ if (w->scrbar.sync_scroll) { unsigned int mods; Window w1, w2; int x1, x2, y1, y2; XQueryPointer(XtDisplay(w), XtWindow(w), &w1, &w2, &x1, &y1, &x2, &y2, &mods); if (!mods) return; } switch (w->scrbar.mode) { case ScrBarModeForwardLine: w->scrbar.slider_position += w->scrbar.step_size; break; case ScrBarModeBackwardLine: w->scrbar.slider_position -= w->scrbar.step_size; break; case ScrBarModeForwardPage: w->scrbar.slider_position += w->scrbar.slider_length; break; case ScrBarModeBackwardPage: w->scrbar.slider_position -= w->scrbar.slider_length; break; default: XBell(XtDisplay(w), 0); return; } fit_scrbar(w); calc_scrbar_coords(w); draw_thumb(w, NULL); send_scroll_report(w); w->scrbar.this_delay -= w->scrbar.decay; if (w->scrbar.this_delay < w->scrbar.minimum_delay) w->scrbar.this_delay = w->scrbar.minimum_delay; w->scrbar.timer = XtAppAddTimeOut(XtWidgetToApplicationContext((Widget)w), (unsigned long)w->scrbar.this_delay, scroll_timer_proc, (XtPointer)w); } static void move_thumb(Widget gw, XEvent *event, String *params, Cardinal *no_params) { ScrBarWidget w = (ScrBarWidget)gw; int x, y, c, length; if (!get_event_xy(event, &x, &y) || w->scrbar.mode != ScrBarModeNone) { XBell(XtDisplay(w), 0); return; } length = w->scrbar.c4 - w->scrbar.c1; if (length <= 0) return; c = (w->scrbar.vertical ? y : x ) - w->scrbar.c1; w->scrbar.slider_position = c * w->scrbar.canvas_length / length; fit_scrbar(w); calc_scrbar_coords(w); draw_thumb(w, NULL); send_scroll_report(w); } static void cont_scroll(Widget gw, XEvent *event, String *params, Cardinal *no_params) { ScrBarWidget w = (ScrBarWidget)gw; int x, y, dc, length; if (w->scrbar.mode != ScrBarModeContinuous) return; if (!get_event_xy(event, &x, &y)) { XBell(XtDisplay(w), 0); return; } length = w->scrbar.c4 - w->scrbar.c1; if (length <= 0) return; dc = (w->scrbar.vertical ? y : x ) - w->scrbar.init_ptr_pos; w->scrbar.slider_position = w->scrbar.init_slider_pos + dc * w->scrbar.canvas_length / length; fit_scrbar(w); calc_scrbar_coords(w); draw_thumb(w, NULL); send_scroll_report(w); } static void start_scroll(Widget gw, XEvent *event, String *params, Cardinal *no_params) { ScrBarWidget w = (ScrBarWidget)gw; int c, x, y; ScrBarMode mode; if (w->scrbar.mode == ScrBarModeContinuous) return; if (!get_event_xy(event, &x, &y)) { XBell(XtDisplay(w), 0); return; } c = w->scrbar.vertical ? y : x; if (c <= w->scrbar.c1) { mode = ScrBarModeBackwardLine; w->scrbar.slider_position -= w->scrbar.step_size; } else if (c < w->scrbar.c2) { mode = ScrBarModeBackwardPage; w->scrbar.slider_position -= w->scrbar.slider_length; } else if (c >= w->scrbar.c4) { mode = ScrBarModeForwardLine; w->scrbar.slider_position += w->scrbar.step_size; } else if (c >= w->scrbar.c3) { mode = ScrBarModeForwardPage; w->scrbar.slider_position += w->scrbar.slider_length; } else { mode = ScrBarModeContinuous; w->scrbar.init_ptr_pos = c; w->scrbar.init_slider_pos = w->scrbar.slider_position; } w->scrbar.mode = mode; fit_scrbar(w); calc_scrbar_coords(w); if (mode == ScrBarModeBackwardLine) if (w->scrbar.vertical) draw_up_arrow(w); else draw_left_arrow(w); else if (mode == ScrBarModeForwardLine) if (w->scrbar.vertical) draw_down_arrow(w); else draw_right_arrow(w); draw_thumb(w, NULL); if (mode != ScrBarModeContinuous && w->scrbar.initial_delay != 0) { w->scrbar.this_delay = w->scrbar.initial_delay; w->scrbar.timer = XtAppAddTimeOut(XtWidgetToApplicationContext((Widget)w), (unsigned long)w->scrbar.this_delay, scroll_timer_proc, (XtPointer)w); } send_scroll_report(w); } static void stop_scroll(Widget gw, XEvent *event, String *params, Cardinal *no_params) { ScrBarWidget w = (ScrBarWidget)gw; ScrBarMode mode = w->scrbar.mode; w->scrbar.mode = ScrBarModeNone; if (w->scrbar.timer) { XtRemoveTimeOut(w->scrbar.timer); w->scrbar.timer = 0; } if (mode == ScrBarModeForwardLine || mode == ScrBarModeBackwardLine) draw_arrows(w); if (mode == ScrBarModeContinuous && w->scrbar.push_thumb) draw_thumb(w, NULL); } static void page_scroll(Widget gw, XEvent *event, String *params, Cardinal *no_params) { ScrBarWidget w = (ScrBarWidget)gw; float amount; if (w->scrbar.mode != ScrBarModeNone || *no_params != 1 || sscanf(params[0], "%f", &amount) != 1) { XBell(XtDisplay(w), 0); return; } w->scrbar.slider_position += amount * (float)w->scrbar.slider_length; fit_scrbar(w); calc_scrbar_coords(w); draw_thumb(w, NULL); send_scroll_report(w); } static void abs_scroll(Widget gw, XEvent *event, String *params, Cardinal *no_params) { ScrBarWidget w = (ScrBarWidget)gw; long amount; if (w->scrbar.mode != ScrBarModeNone || *no_params != 1 || sscanf(params[0], "%ld", &amount) != 1) { XBell(XtDisplay(w), 0); return; } w->scrbar.slider_position += amount; fit_scrbar(w); calc_scrbar_coords(w); draw_thumb(w, NULL); send_scroll_report(w); } static void athena_scroll(Widget gw, XEvent *event, String *params, Cardinal *no_params) { ScrBarWidget w = (ScrBarWidget)gw; int x, y; float fract; if (w->scrbar.mode != ScrBarModeNone || *no_params != 1 || !get_event_xy(event, &x, &y)) return; fract = (float)y / w->core.height; if (params[0][0] == 'u' || params[0][0] == 'U') fract = - fract; w->scrbar.slider_position += fract * w->scrbar.slider_length; fit_scrbar(w); calc_scrbar_coords(w); draw_thumb(w, NULL); send_scroll_report(w); } static void nop(Widget gw, XEvent *event, String *params, Cardinal *no_params) { } /*************************************************************************/ static void ClassInitialize(void) { XtSetTypeConverter(XtRString, XtRLong, cvt_string_to_long, NULL, 0, XtCacheAll, NULL); } static void Initialize(Widget greq, Widget gnew, ArgList args, Cardinal *no_args) { ScrBarWidget new = (ScrBarWidget)gnew; if (new->core.width == 0) new->core.width = new->scrbar.vertical ? new->scrbar.thickness : 1; if (new->core.height == 0) new->core.height = new->scrbar.vertical ? 1 : new->scrbar.thickness; init_gcs(new); new->scrbar.mode = ScrBarModeNone; new->scrbar.timer = 0; new->scrbar.this_delay = 0; calc_scrbar_coords(new); } static void Destroy(Widget gw) { ScrBarWidget w = (ScrBarWidget)gw; free_gcs(w); } static void Resize(Widget gw) { ScrBarWidget w = (ScrBarWidget)gw; calc_scrbar_coords(w); } static void Redisplay(Widget gw, XEvent *event, Region region) { ScrBarWidget w = (ScrBarWidget)gw; if (!XtIsRealized((Widget)w)) return; ShadowDrawShadows((ShadowWidget)w, 0, 0, w->core.width, w->core.height, !w->shadow.line_mode); if (w->scrbar.c1 == w->scrbar.c4) return; if (w->scrbar.vertical) { if (!region || XRectInRegion(region, 0, 0, w->core.width, w->scrbar.c1) != RectangleOut) draw_up_arrow(w); if (!region || XRectInRegion(region, 0, w->scrbar.c4, w->core.width, w->core.height - w->scrbar.c4) != RectangleOut) draw_down_arrow(w); } else { if (!region || XRectInRegion(region, 0, 0, w->scrbar.c1, w->core.height) != RectangleOut) draw_left_arrow(w); if (!region || XRectInRegion(region, w->scrbar.c4, 0, w->core.width - w->scrbar.c4, w->core.height) != RectangleOut) draw_right_arrow(w); } draw_thumb(w, region); } static Boolean SetValues(Widget gcurrent, Widget grequest, Widget gnew, ArgList args, Cardinal *no_args) { ScrBarWidget new = (ScrBarWidget)gnew; ScrBarWidget current = (ScrBarWidget)gcurrent; Boolean redisplay = False; if (new->shadow.shadow_width != current->shadow.shadow_width || new->scrbar.minimum_thumb != current->scrbar.minimum_thumb || new->scrbar.vertical != current->scrbar.vertical || new->scrbar.canvas_length != current->scrbar.canvas_length || new->scrbar.slider_length != current->scrbar.slider_length || new->scrbar.slider_position != current->scrbar.slider_position) redisplay = True; if (new->scrbar.thickness != current->scrbar.thickness) { if (new->scrbar.vertical) new->core.width = new->scrbar.thickness; else new->core.height = new->scrbar.thickness; } if (redisplay) { fit_scrbar(new); calc_scrbar_coords(new); } if (new->core.background_pixel != current->core.background_pixel) { free_gcs(new); init_gcs(new); redisplay = True; } return redisplay; } static XtGeometryResult QueryGeometry(Widget gw, XtWidgetGeometry *intended, XtWidgetGeometry *preferred) { ScrBarWidget w = (ScrBarWidget)gw; Dimension thickness = w->scrbar.thickness; Dimension curr_thick, intend_thick; int mask; if (w->scrbar.vertical) { mask = CWWidth; preferred->width = thickness; curr_thick = w->core.width; if (intended->request_mode & CWWidth) intend_thick = intended->width; else intend_thick = curr_thick; } else { mask = CWHeight; preferred->height = thickness; curr_thick = w->core.height; if (intended->request_mode & CWHeight) intend_thick = intended->height; else intend_thick = curr_thick; } preferred->request_mode = mask; if (intended->request_mode & mask) { if (intend_thick == thickness) return XtGeometryYes; else if (curr_thick == thickness) return XtGeometryNo; else return XtGeometryAlmost; } if (curr_thick == thickness) return XtGeometryYes; else return XtGeometryAlmost; } /*************************************************************************/ void ScrBarSetSliderPosition(Widget gw, long pos) { ScrBarWidget w = (ScrBarWidget)gw; w->scrbar.slider_position = pos; fit_scrbar(w); calc_scrbar_coords(w); if (XtIsRealized((Widget)w)) draw_thumb(w, NULL); } void ScrBarSetLengthsAndPos(Widget gw, long c_l, long s_l, long s_p) { ScrBarWidget w = (ScrBarWidget)gw; w->scrbar.canvas_length = c_l; w->scrbar.slider_length = s_l; w->scrbar.slider_position = s_p; fit_scrbar(w); calc_scrbar_coords(w); if (XtIsRealized((Widget)w)) draw_thumb(w, NULL); } ./knews-1.0b.1/Widgets/KnappP.h100644 1244 1244 2064 6455455535 14610 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #ifndef KnappP_h #define KnappP_h #include "Knapp.h" #include "Util.h" #include "ShadowP.h" typedef struct { XtPointer empty; } KnappClassPart; typedef struct KnappClassRec { CoreClassPart core_class; ShadowClassPart shadow_class; KnappClassPart knapp_class; } KnappClassRec; extern KnappClassRec knappClassRec; typedef struct { Pixel foreground_pixel; XFontStruct *font; String label; XtCallbackList callback; Dimension internal_height; Dimension left_margin; Dimension right_margin; JustifyType justify; Boolean resizable; /* private data */ GC default_gc; GC gray_gc; Pixmap stipple; char **labels; short no_labels; short label_x; short label_width; short max_width; short label_no; Boolean set; Boolean calling_callbacks; Boolean active; } KnappPart; typedef struct KnappRec { CorePart core; ShadowPart shadow; KnappPart knapp; } KnappRec; #endif /* KnappP_h */ ./knews-1.0b.1/Widgets/Layout.c100644 1244 1244 56170 6455455535 14716 0ustar kallekalle/* * $XConsortium: Layout.c,v 1.1 91/09/13 18:51:44 keith Exp $ * * Copyright 1991 Massachusetts Institute of Technology * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of M.I.T. not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. M.I.T. makes no representations about the * suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL M.I.T. * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Author: Keith Packard, MIT X Consortium */ #include #include #include "Compat.h" #include "LayoutP.h" #include #include /***************************************************************************** * * Full instance record declaration * ****************************************************************************/ #define offset(field) XtOffsetOf(LayoutRec, layout.field) static XtResource resources[] = { {XtNborderWidth, XtCBorderWidth, XtRDimension, sizeof(Dimension), XtOffsetOf(LayoutRec, core.border_width), XtRImmediate, (XtPointer)0}, {XtNlayout, XtCLayout, XtRLayout, sizeof (BoxPtr), offset(layout), XtRLayout, NULL }, {XtNdebug, XtCBoolean, XtRBoolean, sizeof(Boolean), offset(debug), XtRImmediate, (XtPointer) FALSE}, }; #undef offset static void ClassInitialize(void); static void Initialize(Widget, Widget, ArgList, Cardinal*); static void Resize(Widget); static Boolean SetValues(Widget, Widget, Widget, ArgList, Cardinal*); static void ChangeManaged(Widget); static void InsertChild(Widget); static void GetDesiredSize(Widget); static XtGeometryResult GeometryManager(Widget, XtWidgetGeometry*, XtWidgetGeometry*); static XtGeometryResult QueryGeometry(Widget, XtWidgetGeometry*, XtWidgetGeometry*); static void LayoutLayout(LayoutWidget, Boolean); static void LayoutGetNaturalSize(LayoutWidget, Dimension*, Dimension*); static void LayoutFreeLayout(BoxPtr); #define SuperClass ((ConstraintWidgetClass)&constraintClassRec) LayoutClassRec layoutClassRec = { { /* core class fields */ /* superclass */ (WidgetClass) SuperClass, /* class name */ "Layout", /* size */ sizeof(LayoutRec), /* class_initialize */ ClassInitialize, /* class_part init */ NULL, /* class_inited */ FALSE, /* initialize */ Initialize, /* initialize_hook */ NULL, /* realize */ XtInheritRealize, /* actions */ NULL, /* num_actions */ 0, /* resources */ resources, /* resource_count */ XtNumber(resources), /* xrm_class */ NULLQUARK, /* compress_motion */ FALSE, /* compress_exposure */ FALSE, /* compress_enterleave*/ FALSE, /* visible_interest */ FALSE, /* destroy */ NULL, /* resize */ Resize, /* expose */ NULL, /* set_values */ SetValues, /* set_values_hook */ NULL, /* set_values_almost */ XtInheritSetValuesAlmost, /* get_values_hook */ NULL, /* accept_focus */ NULL, /* version */ XtVersion, /* callback_private */ NULL, /* tm_table */ NULL, /* query_geometry */ QueryGeometry, /* display_accelerator*/ XtInheritDisplayAccelerator, /* extension */ NULL }, { /* composite class fields */ /* geometry_manager */ GeometryManager, /* change_managed */ ChangeManaged, /* insert_child */ InsertChild, /* delete_child */ XtInheritDeleteChild, /* extension */ NULL }, { /* constraint class fields */ /* subresources */ NULL, /* subresource_count */ 0, /* constraint_size */ sizeof(LayoutConstraintsRec), /* initialize */ NULL, /* destroy */ NULL, /* set_values */ NULL, /* extension */ NULL } }; WidgetClass layoutWidgetClass = (WidgetClass) &layoutClassRec; /************************************************************ * * Semi-public routines. * ************************************************************/ /* Function Name: ClassInitialize * Description: The Layout widgets class initialization proc. * Arguments: none. * Returns: none. */ /*ARGSUSED*/ static Boolean CvtStringToLayout(Display *dpy, XrmValue *args, Cardinal *num_args, XrmValue *from, XrmValue *to, XtPointer *converter_data) { LayYYsetsource ((char *) from->addr); LayYYsetdest ((BoxPtr *) to->addr); if (LayYYparse () == 0) return TRUE; else return FALSE; } /*ARGSUSED*/ static void DisposeLayout(XtAppContext app, XrmValue *to, XtPointer data, XrmValuePtr args, Cardinal *num_args) { LayoutFreeLayout (* (LayoutPtr *) to->addr); } static void ClassInitialize(void) { XtSetTypeConverter ( XtRString, XtRLayout, CvtStringToLayout, NULL, (Cardinal)0, XtCacheNone, DisposeLayout ); } /*ARGSUSED*/ static XtGeometryResult GeometryManager(Widget child, XtWidgetGeometry *request, XtWidgetGeometry *reply) { LayoutWidget w = (LayoutWidget) XtParent(child); SubInfoPtr p = SubInfo(child); int bw; Boolean changed, bwChanged; bw = p->naturalBw; changed = FALSE; bwChanged = FALSE; if (request->request_mode & CWBorderWidth && request->border_width != child->core.border_width) { p->naturalBw = bw; bw = request->border_width; changed = TRUE; bwChanged = TRUE; } if (bwChanged || ((request->request_mode & CWWidth) && request->width != child->core.width)) { p->naturalSize[LayoutHorizontal] = request->width + bw * 2; changed = TRUE; } if (bwChanged || ((request->request_mode & CWHeight) && request->height != child->core.height)) { p->naturalSize[LayoutVertical] = request->height + bw * 2; changed = TRUE; } if (changed) LayoutLayout (w, TRUE); return XtGeometryDone; } /* ARGSUSED */ static void Initialize(Widget request, Widget new, ArgList args, Cardinal *n) { } static void ChangeManaged(Widget gw) { LayoutWidget w = (LayoutWidget) gw; Widget *children; ForAllChildren (w, children) GetDesiredSize (*children); LayoutLayout ((LayoutWidget) w, TRUE); if (w->core.width == 0) w->core.width = 1; if (w->core.height == 0) w->core.height = 1; } static void GetDesiredSize(Widget child) { XtWidgetGeometry desired; SubInfoPtr p; XtQueryGeometry (child, (XtWidgetGeometry *) NULL, &desired); p = SubInfo (child); p->naturalBw = desired.border_width; p->naturalSize[LayoutHorizontal] = desired.width + desired.border_width * 2; p->naturalSize[LayoutVertical] = desired.height + desired.border_width * 2; } static void InsertChild(Widget child) { (*SuperClass->composite_class.insert_child) (child); GetDesiredSize (child); } static void Resize(Widget gw) { LayoutLayout ((LayoutWidget) gw, FALSE); } /* ARGSUSED */ static Boolean SetValues(Widget gold, Widget greq, Widget gnew, ArgList args, Cardinal *n) { LayoutWidget old = (LayoutWidget) gold, new = (LayoutWidget) gnew; if (old->layout.layout != new->layout.layout) LayoutLayout (new, TRUE); return FALSE; } /* SetValues */ static XtGeometryResult QueryGeometry (Widget gw, XtWidgetGeometry *request, XtWidgetGeometry *prefered_return) { LayoutWidget w = (LayoutWidget) gw; XtGeometryResult result; XtWidgetGeometry prefered_size; if (request && !(request->request_mode & (CWWidth|CWHeight))) return XtGeometryYes; LayoutGetNaturalSize (w, &prefered_size.width, &prefered_size.height); prefered_return->request_mode = 0; result = XtGeometryYes; if (!request) { prefered_return->width = prefered_size.width; prefered_return->height= prefered_size.height; if (prefered_size.width != w->core.width) { prefered_return->request_mode |= CWWidth; result = XtGeometryAlmost; } if (prefered_size.height != w->core.height) { prefered_return->request_mode |= CWHeight; result = XtGeometryAlmost; } } else { if (request->request_mode & CWWidth) { if (prefered_size.width > request->width) { if (prefered_size.width == w->core.width) result = XtGeometryNo; else if (result != XtGeometryNo) { result = XtGeometryAlmost; prefered_return->request_mode |= CWWidth; prefered_return->width = prefered_size.width; } } } if (request->request_mode & CWHeight) { if (prefered_size.height > request->height) { if (prefered_size.height == w->core.height) result = XtGeometryNo; else if (result != XtGeometryNo) { result = XtGeometryAlmost; prefered_return->request_mode |= CWHeight; prefered_return->height = prefered_size.height; } } } } return result; } /* * Layout section. Exports LayoutGetNaturalSize and * LayoutLayout to above section */ static void PrintGlue(GlueRec g) { if (g.order == 0 || g.value != 1.0) printf ("%g", g.value); if (g.order > 0) { printf (" inf"); if (g.order > 1) printf (" %d", g.order); } } static void PrintDirection(LayoutDirection dir) { switch (dir) { case LayoutHorizontal: printf ("horizontal"); break; case LayoutVertical: printf ("vertical"); break; default: printf ("Unknown layout direction %d\n", dir); break; } } static void TabTo(int level) { while (level--) printf (" "); } static void PrintBox(BoxPtr box, int level) { BoxPtr child; TabTo (level); printf ("< "); printf (" + "); PrintGlue (box->params.stretch[LayoutHorizontal]); printf (" - "); PrintGlue (box->params.shrink[LayoutHorizontal]); printf (" * "); printf (" + "); PrintGlue (box->params.stretch[LayoutVertical]); printf (" - "); PrintGlue (box->params.shrink[LayoutVertical]); printf (" >"); printf (" size: %d x %d", box->size[0], box->size[1]); printf (" natural: %d x %d ", box->natural[0], box->natural[1]); switch (box->type) { case BoxBox: PrintDirection (box->u.box.dir); printf ("\n"); for (child = box->u.box.firstChild; child; child = child->nextSibling) PrintBox (child, level+1); break; case WidgetBox: printf (" %s\n", XrmQuarkToString (box->u.widget.quark)); break; case GlueBox: printf (" glue\n"); break; case VariableBox: printf (" variable %s\n", XrmQuarkToString (box->u.variable.quark)); break; } } static ExprPtr LookupVariable(BoxPtr child, XrmQuark quark) { BoxPtr parent, box; while ( (parent = child->parent) ) { for (box = parent->u.box.firstChild; box != child; box = box->nextSibling) { if (box->type == VariableBox && box->u.variable.quark == quark) return box->u.variable.expr; } child = parent; } return 0; } static double Evaluate(LayoutWidget l, BoxPtr box, ExprPtr expr, double natural) { double left, right, down; Widget widget; SubInfoPtr info; switch (expr->type) { case Constant: return expr->u.constant; case Binary: left = Evaluate (l, box, expr->u.binary.left, natural); right = Evaluate (l, box, expr->u.binary.right, natural); switch (expr->u.binary.op) { case Plus: return left + right; case Minus: return left - right; case Times: return left * right; case Divide: return left / right; case Percent: return right * left / 100.0; } case Unary: down = Evaluate (l, box, expr->u.unary.down, natural); switch (expr->u.unary.op) { case Percent: return natural * down / 100.0; case Minus: return -down; case Plus: case Times: case Divide: break; } case Width: widget = QuarkToWidget (l, expr->u.width); if (!widget) return 0; info = SubInfo (widget); return info->naturalSize[LayoutHorizontal]; case Height: widget = QuarkToWidget (l, expr->u.height); if (!widget) return 0; info = SubInfo (widget); return info->naturalSize[LayoutVertical]; case Variable: expr = LookupVariable (box, expr->u.variable); if (!expr) { char buf[256]; sprintf (buf, "Layout: undefined variable %s\n", XrmQuarkToString (expr->u.variable)); XtError (buf); return 0.0; } return Evaluate (l, box, expr, natural); } return 0.0; } static void DisposeExpr(ExprPtr expr) { if (!expr) return; switch (expr->type) { case Constant: break; case Binary: DisposeExpr (expr->u.binary.left); DisposeExpr (expr->u.binary.right); break; case Unary: DisposeExpr (expr->u.unary.down); break; case Width: break; case Height: break; case Variable: break; } Dispose (expr); } #define CheckGlue(l, box, glue, n) { \ if (glue.expr) \ glue.value = Evaluate (l, box, glue.expr, n); \ if (glue.order == 0 && glue.value == 0) \ glue.order = -1; \ else if (glue.order == -1 && glue.value != 0) \ glue.order = 0; \ } #define DoStretch(l, box, dir) \ CheckGlue (l, box, box->params.stretch[dir], (double) box->natural[dir]); #define DoShrink(l, box, dir) \ CheckGlue (l, box, box->params.shrink[dir], (double) box->natural[dir]) /* compute the natural sizes of a box */ static void ComputeNaturalSizes(LayoutWidget l, BoxPtr box, LayoutDirection dir) { BoxPtr child; Widget w; SubInfoPtr info; int minStretchOrder, minShrinkOrder; LayoutDirection thisDir; switch (box->type) { case VariableBox: break; case WidgetBox: w = box->u.widget.widget = QuarkToWidget (l, box->u.widget.quark); if (!w) { box->natural[LayoutHorizontal] = 0; box->natural[LayoutVertical] = 0; } else { info = SubInfo (w); box->natural[LayoutHorizontal] = info->naturalSize[LayoutHorizontal]; box->natural[LayoutVertical] = info->naturalSize[LayoutVertical]; } DoStretch (l, box, dir); DoShrink (l, box, dir); DoStretch (l, box, !dir); DoShrink (l, box, !dir); break; case GlueBox: box->natural[dir] = Evaluate (l, box, box->u.glue.expr, 0.0); box->natural[!dir] = 0; DoStretch (l, box, dir); DoShrink (l, box, dir); break; case BoxBox: thisDir = box->u.box.dir; box->natural[0] = 0; box->natural[1] = 0; minStretchOrder = 100000; minShrinkOrder = 100000; ZeroGlue (box->params.shrink[thisDir]); ZeroGlue (box->params.stretch[thisDir]); box->params.shrink[!thisDir].order = 100000; box->params.stretch[!thisDir].order = 100000; for (child = box->u.box.firstChild; child; child = child->nextSibling) { ComputeNaturalSizes (l, child, thisDir); /* * along box axis: * normal size += child normal size * shrink += child shrink * stretch += child stretch */ box->natural[thisDir] += child->natural[thisDir]; AddGlue (box->params.shrink[thisDir], box->params.shrink[thisDir], child->params.shrink[thisDir]); AddGlue (box->params.stretch[thisDir], box->params.stretch[thisDir], child->params.stretch[thisDir]); /* * normal to box axis: * normal size = maximum child normal size of minimum shrink order * shrink = difference between normal size and minimum shrink * stretch = minimum child stretch */ if (box->natural[!thisDir] >= child->natural[!thisDir]) { if (child->params.stretch[!thisDir].order < minShrinkOrder) { box->natural[!thisDir] = child->natural[!thisDir]; minStretchOrder = child->params.stretch[!thisDir].order; if (child->params.shrink[!thisDir].order < minShrinkOrder) minShrinkOrder = child->params.shrink[!thisDir].order; } } else { if (child->params.shrink[!thisDir].order <= minStretchOrder) { box->natural[!thisDir] = child->natural[!thisDir]; minShrinkOrder = child->params.shrink[!thisDir].order; if (child->params.stretch[!thisDir].order < minStretchOrder) minStretchOrder = child->params.stretch[!thisDir].order; } } MinGlue (box->params.stretch[!thisDir], box->params.stretch[!thisDir], child->params.stretch[!thisDir]); MinGlue (box->params.shrink[!thisDir], box->params.shrink[!thisDir], child->params.shrink[!thisDir]); } if (box->params.shrink[!thisDir].order <= 0) { int minSize; int largestMinSize; largestMinSize = 0; for (child = box->u.box.firstChild; child; child = child->nextSibling) { if (child->params.shrink[!thisDir].order <= 0) { minSize = child->natural[!thisDir] - child->params.shrink[!thisDir].value; if (minSize > largestMinSize) largestMinSize = minSize; } } box->params.shrink[!thisDir].value = box->natural[!thisDir] - largestMinSize; if (box->params.shrink[!thisDir].value == 0) box->params.shrink[!thisDir].order = -1; else box->params.shrink[!thisDir].order = 0; } } } /* given the boxs geometry, set the geometry of the pieces */ #define GluePart(a,b,dist) ((a) ? ((int) (((a) * (dist)) / (b) + \ ((dist >= 0) ? 0.5 : -0.5))) : 0) static Boolean ComputeSizes(BoxPtr box) { LayoutDirection dir; BoxPtr child; GlueRec stretch; GlueRec shrink; GlueRec totalGlue[2]; double remainingGlue; GluePtr glue; int size; int totalSizes; int totalChange[2]; int change; int remainingChange; Boolean shrinking; Boolean happy; int i; int maxGlue; dir = box->u.box.dir; size = box->size[dir]; stretch = box->params.stretch[dir]; shrink = box->params.shrink[dir]; /* pick the correct adjustment parameters based on the change direction */ totalChange[0] = size - box->natural[dir]; shrinking = totalChange[0] < 0; totalChange[1] = 0; totalGlue[1].order = 100000; totalGlue[1].value = 0; maxGlue = 1; if (shrinking) { totalGlue[0] = shrink; /* for first-order infinites, shrink it to zero and then * shrink the zero-orders */ if (shrink.order == 1) { totalSizes = 0; remainingGlue = 0; for (child = box->u.box.firstChild; child; child = child->nextSibling) { switch (child->params.shrink[dir].order) { case 0: remainingGlue += child->params.shrink[dir].value; break; case 1: totalSizes += child->natural[dir]; break; } } if (totalSizes < -totalChange[0]) { totalGlue[1] = shrink; totalGlue[0].order = 0; totalGlue[0].value = remainingGlue; totalChange[1] = -totalSizes; totalChange[0] = totalChange[0] - totalChange[1]; maxGlue = 2; } } if (totalGlue[0].order <= 0 && totalChange[0] > totalGlue[0].value) { totalChange[0] = totalGlue[0].value; } } else totalGlue[0] = stretch; /* adjust each box */ totalSizes = 0; remainingGlue = totalGlue[0].value + totalGlue[1].value; remainingChange = totalChange[0] + totalChange[1]; happy = True; for (child = box->u.box.firstChild; child; child = child->nextSibling) { if (shrinking) glue = &child->params.shrink[dir]; else glue = &child->params.stretch[dir]; child->size[dir] = child->natural[dir]; for (i = 0; i < maxGlue; i++) { if (glue->order == totalGlue[i].order) { remainingGlue -= glue->value; if (remainingGlue <= 0) change = remainingChange; else change = GluePart (glue->value, totalGlue[i].value, totalChange[i]); child->size[dir] += change; remainingChange -= change; } } child->size[!dir] = box->size[!dir]; totalSizes += child->size[dir]; if (child->type == BoxBox) if (!ComputeSizes (child)) happy = False; } return totalSizes == box->size[dir] && happy; } static void SetSizes(BoxPtr box, Position x, Position y) { BoxPtr child; int width, height; int bw; Widget w; SubInfoPtr info; switch (box->type) { case VariableBox: /* ??? */ break; case WidgetBox: w = box->u.widget.widget; if (w) { info = (SubInfoPtr) w->core.constraints; width = box->size[LayoutHorizontal]; height = box->size[LayoutVertical]; bw = info->naturalBw; width -= bw * 2; height -= bw * 2; /* Widgets which grow too small are placed off screen */ if (width <= 0 || height <= 0) { width = 1; height = 1; bw = 0; x = -1; y = -1; } XtConfigureWidget (w, x, y, width, height, bw); } break; case GlueBox: break; case BoxBox: for (child = box->u.box.firstChild; child; child = child->nextSibling) { SetSizes (child, x, y); if (box->u.box.dir == LayoutHorizontal) x += child->size[LayoutHorizontal]; else y += child->size[LayoutVertical]; } break; } } static void LayoutFreeLayout(BoxPtr box) { BoxPtr child, next; switch (box->type) { case WidgetBox: case VariableBox: break; case BoxBox: for (child = box->u.box.firstChild; child; child = next) { next = child->nextSibling; LayoutFreeLayout (child); } break; case GlueBox: DisposeExpr (box->u.glue.expr); break; } DisposeExpr (box->params.stretch[LayoutHorizontal].expr); DisposeExpr (box->params.shrink[LayoutHorizontal].expr); DisposeExpr (box->params.stretch[LayoutVertical].expr); DisposeExpr (box->params.shrink[LayoutVertical].expr); Dispose (box); } static void LayoutGetNaturalSize(LayoutWidget l, Dimension *widthp, Dimension *heightp) { BoxPtr box; box = l->layout.layout; if (box) { ComputeNaturalSizes (l, box, LayoutHorizontal); *widthp = box->natural[LayoutHorizontal]; *heightp = box->natural[LayoutVertical]; } else { *widthp = 0; *heightp = 0; } } static void LayoutLayout(LayoutWidget l, Boolean attemptResize) { BoxPtr box; Dimension width, height; Dimension prefered_width, prefered_height; box = l->layout.layout; if (!box) return; LayoutGetNaturalSize (l, &prefered_width, &prefered_height); if (l->core.width == 0 || l->core.height == 0) { l->core.width = prefered_width; l->core.height = prefered_height; } box->size[LayoutHorizontal] = l->core.width; box->size[LayoutVertical] = l->core.height; if (!ComputeSizes (box) && attemptResize) { XtMakeResizeRequest ((Widget) l, prefered_width, prefered_height, &width, &height); if (width != box->size[LayoutHorizontal] || height != box->size[LayoutVertical]) { box->size[LayoutHorizontal] = width; box->size[LayoutVertical] = height; ComputeSizes (box); } } if (l->layout.debug) { PrintBox (box, 0); fflush (stdout); } SetSizes (box, 0, 0); } ./knews-1.0b.1/Widgets/Menu.c100644 1244 1244 24641 6455455535 14343 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include #include #include #include "Compat.h" #include "MenuP.h" static XtResource resources[] = { #define offset(field) XtOffsetOf(MenuRec, menu.field) {XtNcursor, XtCCursor, XtRCursor, sizeof(Cursor), offset(cursor), XtRImmediate, (XtPointer)None}, #undef offset }; static void Initialize(Widget, Widget, ArgList, Cardinal*); static void Destroy(Widget); static void Resize(Widget); static void Realize(Widget, XtValueMask*, XSetWindowAttributes*); static void Redisplay(Widget, XEvent*, Region); static Boolean SetValues(Widget, Widget, Widget, ArgList, Cardinal*); static XtGeometryResult QueryGeometry(Widget, XtWidgetGeometry*, XtWidgetGeometry*); static void motion(Widget, XEvent*, String*, Cardinal*); static void leave(Widget, XEvent*, String*, Cardinal*); static XtActionsRec actions[] = { {"motion", motion}, {"leave", leave}, }; static char translations[] = ": motion() \n" ": leave() \n"; MenuClassRec menuClassRec = { { /* core fields */ (WidgetClass) &shadowClassRec, /* superclass */ "Menu", /* class_name */ sizeof(MenuRec), /* widget_size */ NULL, /* class_initialize */ NULL, /* class_part_initialize*/ FALSE, /* class_inited */ Initialize, /* initialize */ NULL, /* initialize_hook */ Realize, /* realize */ actions, /* actions */ XtNumber(actions), /* num_actions */ resources, /* resources */ XtNumber(resources), /* resource_count */ NULLQUARK, /* xrm_class */ TRUE, /* compress_motion */ #if (XtSpecificationRelease < 4) TRUE, /* compress_exposure */ #elif (XtSpecificationRelease < 6) XtExposeCompressMaximal, /* compress_exposure */ #else XtExposeCompressMaximal | XtExposeNoRegion, /* compress_exposure*/ #endif TRUE, /* compress_enterleave */ TRUE, /* visible_interest */ Destroy, /* destroy */ Resize, /* resize */ Redisplay, /* expose */ SetValues, /* set_values */ NULL, /* set_values_hook */ XtInheritSetValuesAlmost, /* set_values_almost */ NULL, /* get_values_hook */ NULL, /* accept_focus */ XtVersion, /* version */ NULL, /* callback_private */ translations, /* tm_table */ QueryGeometry, /* query_geometry */ XtInheritDisplayAccelerator, /* display_accelerator */ NULL /* extension */ }, { /* shadow fields */ XtInheritPixelOffset, /* pixel offset */ False, /* use_arm_for_background */ XtInheritAllocShadowColors, /* alloc_shadow_colors */ XtInheritAllocShadowPixmaps, /* alloc_shadow_pixmaps */ XtInheritAllocArmColor, /* alloc_arm_color */ XtInheritAllocArmPixmap, /* alloc_arm_pixmap */ XtInheritAllocGCs, /* alloc_gcs */ NULL, /* extension */ }, { /* menu fields */ NULL /* extension */ } }; WidgetClass menuWidgetClass = (WidgetClass) &menuClassRec; /*************************************************************************/ static MenuGadget y_to_gadget(MenuWidget w, int y) { int i; for (i = 0 ; i < w->menu.num_children ; i++) { MenuGadget g = w->menu.children[i]; if ((unsigned int)(y - g->rectangle.y) < g->rectangle.height) return g; } return NULL; } static void change_hl(MenuWidget w, MenuGadget g, int hl) { if (!XtIsSensitive((Widget)g)) hl = False; g->menu_g.hl = hl; XClearArea(XtDisplay(w), XtWindow(w), g->rectangle.x, g->rectangle.y, g->rectangle.width, g->rectangle.height, False); if (g->object.widget_class->core_class.expose) g->object.widget_class->core_class.expose((Widget)g, NULL, 0); MenuGadgetChangeHlProc(g)(g); } static void motion(Widget gw, XEvent *event, String *params, Cardinal *no_params) { MenuWidget w = (MenuWidget)gw; MenuGadget old, new; if (!w->menu.active) return; if (event->type != MotionNotify) { fputs("Menu: Action highlight() only supported " "for Motion events.\n", stderr); return; } old = w->menu.current; new = y_to_gadget(w, event->xmotion.y); if (new) new->menu_g.inside = True; if (old != new) { if (old) change_hl(w, old, False); if (new) change_hl(w, new, True); w->menu.current = new; } } static void leave(Widget gw, XEvent *event, String *params, Cardinal *no_params) { MenuWidget w = (MenuWidget)gw; if (!w->menu.current || !w->menu.active) return; w->menu.current->menu_g.inside = False; if (MenuGadgetIgnoreLeave(w->menu.current)) return; change_hl(w, w->menu.current, False); w->menu.current = NULL; } static void layout(MenuWidget w) { MenuGadget *loop; int n = w->menu.num_children; int sw = w->shadow.shadow_width; int i, y, width = 0; for (y = sw, i = 0, loop = w->menu.children ; i < n ; i++, loop++) { if (!XtIsSubclass((Widget)*loop, menuGadgetClass)) fputs("Menu: Child of MenuWidget must be " "subclass of MenuGadgetClass.", stderr); y += (*loop)->rectangle.height; if (width < (int)(*loop)->rectangle.width) width = (int)(*loop)->rectangle.width; } y += sw; w->menu.pref_width = (Dimension)(width + 2 * sw); w->menu.pref_height = (Dimension)y; (void)XtMakeResizeRequest((Widget)w, w->menu.pref_width, w->menu.pref_height, NULL, NULL); for (y = sw, i = 0, loop = w->menu.children ; i < n ; i++, loop++) { XtConfigureWidget((Widget)*loop, sw, y, width, (*loop)->rectangle.height, 0); y += (*loop)->rectangle.height; } } /*************************************************************************/ static void Initialize (Widget grequest, Widget gnew, ArgList args, Cardinal *num_args) { MenuWidget new = (MenuWidget)gnew; new->menu.children = NULL; new->menu.num_children = 0; new->menu.num_slots = 0; new->menu.current = NULL; new->menu.active = True; layout(new); } static void Destroy(Widget gw) { MenuWidget w = (MenuWidget)gw; int i; for (i = 0 ; i < w->menu.num_children ; i++) XtDestroyWidget((Widget)w->menu.children[i]); } static void Resize (Widget gw) { /* we never expect to be resized */ } static void Redisplay(Widget gw, XEvent *event, Region reg) { MenuWidget w = (MenuWidget)gw; int n = w->menu.num_children; int i, y1, y2; if (event) switch (event->type) { case Expose: y1 = event->xexpose.y; y2 = y1 + event->xexpose.height; break; case GraphicsExpose: y1 = event->xgraphicsexpose.y; y2 = y1 + event->xgraphicsexpose.height; break; default: return; } else { y1 = 0; y2 = w->menu.pref_height; } for (i = 0 ; i < n ; i++) { MenuGadget g = w->menu.children[i]; if (g->rectangle.y <= y2 && y1 <= g->rectangle.y + (int)g->rectangle.height && g->object.widget_class->core_class.expose) g->object.widget_class->core_class.expose((Widget)g, event, reg); } ShadowDrawShadows((ShadowWidget)w, 0, 0, w->menu.pref_width, w->menu.pref_height, False); } static void Realize(Widget gw, XtValueMask *mask, XSetWindowAttributes *attributes) { MenuWidget w = (MenuWidget)gw; if (w->menu.cursor != None) { *mask |= CWCursor; attributes->cursor = w->menu.cursor; } shadowWidgetClass->core_class.realize((Widget)w, mask, attributes); } static Boolean SetValues(Widget gcurrent, Widget grequest, Widget gnew, ArgList args, Cardinal *num_args) { Boolean redisplay = False; MenuWidget new = (MenuWidget)gnew; MenuWidget current = (MenuWidget)gcurrent; if (XtIsRealized((Widget)new) && new->menu.cursor != current->menu.cursor) XDefineCursor(XtDisplay(new), XtWindow(new), new->menu.cursor); return redisplay; } static XtGeometryResult QueryGeometry(Widget gw, XtWidgetGeometry *intended, XtWidgetGeometry *preferred) { MenuWidget w = (MenuWidget)gw; Dimension intended_width, intended_height; if (intended->request_mode & CWWidth) intended_width = intended->width; else intended_width = w->core.width; if (intended->request_mode & CWHeight) intended_height = intended->height; else intended_height = w->core.height; preferred->request_mode = CWWidth | CWHeight; preferred->width = w->menu.pref_width; preferred->height = w->menu.pref_height; if (preferred->width == w->core.width && preferred->height == w->core.height) return XtGeometryNo; if (preferred->width == intended_width && preferred->height == intended_height) return XtGeometryYes; return XtGeometryAlmost; } /*************************************************************************/ Widget MenuCreateGadget(String name, WidgetClass class, Widget parent, ArgList args, Cardinal n) { MenuWidget w = (MenuWidget)parent; MenuGadget child; if (w->menu.num_children >= w->menu.num_slots) { w->menu.num_slots = 2 * (w->menu.num_slots + 1); w->menu.children = (MenuGadget *)XtRealloc((char *)w->menu.children, w->menu.num_slots * sizeof(MenuGadget)); } child = (MenuGadget)XtCreateWidget(name, class, (Widget)w, args, n); if (!XtIsSubclass((Widget)child, menuGadgetClass)) { fputs("MenuWidgetClass accepts children " "that are subclass of MenuGadgetClass only.", stderr); exit(1); } w->menu.children[w->menu.num_children] = child; w->menu.num_children++; layout(w); return (Widget)child; } void PopdownMenu(Widget gw) { MenuWidget w = (MenuWidget)gw; MenuGadget g = w->menu.current; w->menu.active = True; if (!g) return; if (g->menu_g.hl) { g->menu_g.hl = False; MenuGadgetChangeHlProc(g)(g); } MenuGadgetPopdownProc(g)(g); } int NotifyMenu(Widget gw) { MenuWidget w = (MenuWidget)gw; w->menu.active = False; if (!w->menu.current) return False; return MenuGadgetNotifyProc(w->menu.current)(w->menu.current); } int PostNotifyMenu(Widget gw) { MenuWidget w = (MenuWidget)gw; int tmp; if (!w->menu.current) return False; tmp = MenuGadgetPostNotifyProc(w->menu.current)(w->menu.current); w->menu.current = NULL; return tmp; } void SetActiveMenu(Widget gw, int active) { MenuWidget w = (MenuWidget)gw; int i; w->menu.active = active; for (i = 0 ; i < w->menu.num_children ; i++) { MenuGadget g = w->menu.children[i]; MenuGadgetSetActiveProc(g)(g, active); } } ./knews-1.0b.1/Widgets/Layout.h100644 1244 1244 6413 6455455535 14676 0ustar kallekalle/* * $XConsortium: Layout.h,v 1.2 92/01/22 18:03:05 keith Exp $ * * Copyright 1991 Massachusetts Institute of Technology * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of M.I.T. not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. M.I.T. makes no representations about the * suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL M.I.T. * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Author: Keith Packard, MIT X Consortium */ #ifndef Layout_h #define Layout_h #include #if XtSpecificationRelease > 4 #include #endif /**************************************************************** * * Layout Widget (SubClass of CompositeClass) * ****************************************************************/ /* RESOURCES: Name Class RepType Default Value ---- ----- ------- ------------- background Background Pixel XtDefaultBackground border BorderColor Pixel XtDefaultForeground borderWidth BorderWidth Dimension 1 cursor Cursor Cursor None destroyCallback Callback Pointer NULL height Height Dimension 0 mappedWhenManaged MappedWhenManaged Boolean True sensitive Sensitive Boolean True width Width Dimension 0 x Position Position 0 y Position Position 0 layout Layout Layout NULL */ /* * Syntax of layout resource * * *layout:\ * .,.: distance + stretch-factor\n\ * ... * where the null widget-name is taken to be the Layout widget * * e.g: * * *label-1.hStretch: 0 * *label-2.vStretch: 1 * *layout:\ * .left, label-1.left: 10 + 0\n\ * label-1.right, label-2.left: 10 + 1\n\ * label-2.right, .right: 10 + 0 * * This layout causes label-1 to be set 10 pixels from the left edge * and be whatever size the label widget requests, while label-2 will * be set 10 pixels from the right edge, and take up half of the remaining * space to 10 pixels from the right edge of label-1. */ /* New Fields */ #define XtNlayout "layout" #define XtCLayout "Layout" #define XtRLayout "Layout" #define XtNdebug "debug" /* Class record constant */ extern WidgetClass layoutWidgetClass; typedef struct LayoutClassRec *LayoutWidgetClass; typedef struct LayoutRec *LayoutWidget; #endif /* Layout_h */ /* DON'T ADD STUFF AFTER THIS #endif */ ./knews-1.0b.1/Widgets/Notice.c100644 1244 1244 24353 6455455535 14660 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include #include #include "Compat.h" #include "KnappP.h" #include "Layout.h" #include "Message.h" #include "Util.h" #include "NoticeP.h" static XtResource resources[] = { #define offset(field) XtOffsetOf(NoticeRec, notice.field) {XtNcallback, XtCCallback, XtRCallback, sizeof(XtPointer), offset(callback), XtRCallback, (XtPointer)NULL}, {XtNmessage, XtCMessage, XtRString, sizeof(String), offset(message), XtRImmediate, (XtPointer)NULL}, {XtNleftLabel, XtCLabel, XtRBoolean, sizeof(String), offset(left_label), XtRImmediate, (XtPointer)NULL}, {XtNmiddleLabel, XtCLabel, XtRBoolean, sizeof(String), offset(middle_label), XtRImmediate, (XtPointer)NULL}, {XtNrightLabel, XtCLabel, XtRBoolean, sizeof(String), offset(right_label), XtRImmediate, (XtPointer)NULL}, {XtNtimeout, XtCTimeout, XtRLong, sizeof(long), offset(timeout), XtRImmediate, (XtPointer)0}, {XtNleftKnapp, XtCWidget, XtRWidget, sizeof(Widget), offset(left_knapp), XtRImmediate, (XtPointer)NULL}, {XtNmiddleKnapp, XtCWidget, XtRWidget, sizeof(Widget), offset(middle_knapp), XtRImmediate, (XtPointer)NULL}, {XtNrightKnapp, XtCWidget, XtRWidget, sizeof(Widget), offset(right_knapp), XtRImmediate, (XtPointer)NULL}, #undef offset }; static void Initialize(Widget, Widget, ArgList, Cardinal*); static void Destroy(Widget); static Boolean SetValues(Widget, Widget, Widget, ArgList, Cardinal*); static void close_notice(Widget); NoticeClassRec noticeClassRec = { { /* core fields */ (WidgetClass) &closeShellClassRec, /* superclass */ "Notice", /* class_name */ sizeof(NoticeRec), /* widget_size */ NULL, /* class_initialize */ NULL, /* class_part_initialize */ FALSE, /* class_inited */ Initialize, /* initialize */ NULL, /* initialize_hook */ XtInheritRealize, /* realize */ NULL, /* actions */ 0, /* num_actions */ resources, /* resources */ XtNumber(resources), /* num_resources */ NULLQUARK, /* xrm_class */ TRUE, /* compress_motion */ FALSE, /* compress_exposure */ TRUE, /* compress_enterleave */ FALSE, /* visible_interest */ Destroy, /* destroy */ XtInheritResize, /* resize */ NULL, /* expose */ SetValues, /* set_values */ NULL, /* set_values_hook */ XtInheritSetValuesAlmost, /* set_values_almost */ NULL, /* get_values_hook */ NULL, /* accept_focus */ XtVersion, /* version */ NULL, /* callback_private */ XtInheritTranslations, /* tm_table */ NULL, /* query_geometry */ XtInheritDisplayAccelerator, /* display_accelerator */ NULL /* extension */ }, { /* composite fields */ XtInheritGeometryManager, /* geometry_manager */ XtInheritChangeManaged, /* change_managed */ XtInheritInsertChild, /* insert_child */ XtInheritDeleteChild, /* delete_child */ NULL, /* extension */ }, { /* shell fields */ NULL, /* extension */ }, { /* wm shell fields */ NULL, /* extension */ }, { /* vendor shell fields */ NULL, /* extension */ }, { /* transient shell fields */ NULL, /* extension */ }, { close_notice, /* close_window */ NULL, /* extension */ }, { /* notice fields */ NULL, /* extension */ } }; WidgetClass noticeWidgetClass = (WidgetClass)¬iceClassRec; /*************************************************************************/ static char *layout_string[] = { "vertical { " " message " "} ", "vertical { " " height knapp1 <+inf-inf> " " horizontal { " " height knapp1 <+inf-inf> " " message " " height knapp1 <+inf-inf> " " } " " height knapp1 <+inf-inf> " " horizontal { " " height knapp1 <+inff-inff> " " knapp1 " " height knapp1 <+inff-inff> " " } " " height knapp1 <+inf-inf> " "}", "vertical { " " height knapp1 <+inf-inf> " " horizontal { " " height knapp1 <+inf-inf> " " message " " height knapp1 <+inf-inf> " " } " " height knapp1 <+inf-inf> " " horizontal { " " height knapp1 <+inff-inff> " " knapp1 " " height knapp1 <+3inff-inff> " " knapp2 " " height knapp1 <+inff-inff> " " } " " height knapp1 <+inf-inf> " "}", "vertical { " " height knapp1 <+inf-inf> " " horizontal { " " height knapp1 <+inf-inf> " " message " " height knapp1 <+inf-inf> " " } " " height knapp1 <+inf-inf> " " horizontal { " " height knapp1 <+inff-inff> " " knapp1 " " height knapp1 <+3inff-inff> " " knapp2 " " height knapp1 <+3inff-inff> " " knapp3 " " height knapp1 <+inff-inff> " " } " " height knapp1 <+inf-inf> " "} "}; static void timeout_callback(XtPointer client_data, XtIntervalId *id) { NoticeWidget w = (NoticeWidget)client_data; XtCallbackList c_list = w->notice.callback; w->notice.timer = 0; if (c_list) XtCallCallbackList((Widget)w, c_list, (XtPointer)NoticeReplyTimeout); else XtDestroyWidget((Widget)w); } static void close_notice(Widget gw) { NoticeWidget w = (NoticeWidget)gw; XtCallbackList c_list = w->notice.callback; if (c_list) XtCallCallbackList((Widget)w, c_list, (XtPointer)NoticeReplyClose); else XtDestroyWidget((Widget)w); } static void knapp_callback(Widget knapp, XtPointer client_data, XtPointer call_data) { NoticeWidget w = (NoticeWidget)client_data; XtCallbackList c_list = w->notice.callback; if (!c_list) XtDestroyWidget((Widget)w); else if (knapp == w->notice.left_knapp) XtCallCallbackList((Widget)w, c_list, (XtPointer)NoticeReplyLeft); else if (knapp == w->notice.middle_knapp) XtCallCallbackList((Widget)w, c_list, (XtPointer)NoticeReplyMiddle); else if (knapp == w->notice.right_knapp) XtCallCallbackList((Widget)w, c_list, (XtPointer)NoticeReplyRight); } /*************************************************************************/ static void Initialize(Widget grequest, Widget gnew, ArgList gargs, Cardinal *no_args) { static char *name[] = {"knapp1", "knapp2", "knapp3"}; NoticeWidget new = (NoticeWidget)gnew; Arg args[2]; int n; n = 0; if (new->notice.left_label) n++; if (new->notice.middle_label) n++; if (new->notice.right_label) n++; new->notice.layout = XtVaCreateManagedWidget("layout", layoutWidgetClass, (Widget)new, XtVaTypedArg, XtNlayout, XtRString, layout_string[n], (int)sizeof(String), (void *)0); n = 0; if (!new->notice.left_label) new->notice.left_knapp = NULL; else { XtSetArg(args[0], XtNlabel, new->notice.left_label); new->notice.left_knapp = XtCreateManagedWidget(name[n], knappWidgetClass, new->notice.layout, args, 1); XtAddCallback(new->notice.left_knapp, XtNcallback, knapp_callback, (XtPointer)new); n++; } if (!new->notice.middle_label) new->notice.middle_knapp = NULL; else { XtSetArg(args[0], XtNlabel, new->notice.middle_label); new->notice.middle_knapp = XtCreateManagedWidget(name[n], knappWidgetClass, new->notice.layout, args, 1); XtAddCallback(new->notice.middle_knapp, XtNcallback, knapp_callback, (XtPointer)new); n++; } if (!new->notice.right_label) new->notice.right_knapp = NULL; else { XtSetArg(args[0], XtNlabel, new->notice.right_label); new->notice.right_knapp = XtCreateManagedWidget(name[n], knappWidgetClass, new->notice.layout, args, 1); XtAddCallback(new->notice.right_knapp, XtNcallback, knapp_callback, (XtPointer)new); n++; } XtSetArg(args[0], XtNbuffer, new->notice.message); XtSetArg(args[1], XtNborderWidth, 0); new->notice.message_widget = XtCreateManagedWidget("message", messageWidgetClass, new->notice.layout, args, 2); new->notice.message = NULL; if (new->notice.timeout != 0) new->notice.timer = XtAppAddTimeOut(XtWidgetToApplicationContext((Widget)new), (unsigned long)new->notice.timeout, timeout_callback, (XtPointer)new); else new->notice.timer = 0; } static void Destroy(Widget gw) { NoticeWidget w = (NoticeWidget)gw; if (w->notice.timer > 0) XtRemoveTimeOut(w->notice.timer); } static Boolean SetValues(Widget gcurrent, Widget grequest, Widget gnew, ArgList args, Cardinal *num_args) { Boolean redisplay = False; NoticeWidget new = (NoticeWidget)gnew; if (new->notice.message) { MessageSetAndRedraw(new->notice.message_widget, new->notice.message, False); new->notice.message = NULL; } return redisplay; } /*************************************************************************/ void NoticeSetMessage(Widget gw, String message) { NoticeWidget w = (NoticeWidget)gw; Arg arg; XtSetArg(arg, XtNbuffer, message); XtSetValues(w->notice.message_widget, &arg, 1); } void NoticeSetLeftLabel(Widget gw, String label) { NoticeWidget w = (NoticeWidget)gw; Arg arg; if (w->notice.left_knapp) { XtSetArg(arg, XtNlabel, label); XtSetValues(w->notice.left_knapp, &arg ,1); } } Widget NoticeMessageWidget(Widget gw) { NoticeWidget w = (NoticeWidget)gw; return w->notice.message_widget; } ./knews-1.0b.1/Widgets/LayoutP.h100644 1244 1244 13022 6455455535 15030 0ustar kallekalle/* * $XConsortium: LayoutP.h,v 1.2 92/01/22 18:03:08 keith Exp $ * * Copyright 1991 Massachusetts Institute of Technology * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of M.I.T. not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. M.I.T. makes no representations about the * suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL M.I.T. * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Author: Keith Packard, MIT X Consortium */ #ifndef LayoutP_h #define LayoutP_h #include "Layout.h" #define GlueEqual(a,b) ((a).order == (b).order && (a).value == (b).value) #define AddGlue(r,a,b) if (a.order == b.order) { \ r.order = a.order; \ r.value = a.value + b.value; \ } else { \ if (a.order > b.order) \ r = a; \ else \ r = b; \ } #define MinGlue(r,a,b) if (a.order == b.order) { \ r.order = a.order; \ if (a.value > b.value) \ r.value = b.value; \ else \ r.value = a.value; \ } else { \ if (a.order > b.order) \ r = b; \ else \ r = a; \ } #define SubGlue(r,a,b) if (a.order == b.order) { \ r.order = a.order; \ r.value = a.value - b.value; \ } else { \ if (a.order > b.order) \ r = a; \ else { \ r.order = b.order; \ r.value = -b.value; \ } \ } #define ZeroGlue(g) ((g).value = 0, (g).order = 0, (g).expr = 0) #define IsZeroGlue(g) ((g).value == 0) #define QuarkToWidget(l,q) XtNameToWidget((Widget) l, \ (char *) XrmQuarkToString(q)); typedef enum BoxType { BoxBox, WidgetBox, GlueBox, VariableBox } BoxType; typedef enum LayoutDirection { LayoutHorizontal = 0, LayoutVertical = 1 } LayoutDirection; typedef enum Operator { Plus, Minus, Times, Divide, Percent } Operator; typedef enum ExprType { Constant, Binary, Unary, Width, Height, Variable } ExprType; typedef struct Expr *ExprPtr; typedef struct Expr { ExprType type; union { double constant; struct { Operator op; ExprPtr left, right; } binary; struct { Operator op; ExprPtr down; } unary; XrmQuark width; XrmQuark height; XrmQuark variable; } u; } ExprRec; typedef struct Glue { int order; double value; ExprPtr expr; } GlueRec, *GluePtr; typedef struct BoxParams { GlueRec stretch[2]; GlueRec shrink[2]; } BoxParamsRec, *BoxParamsPtr; typedef struct Box *BoxPtr; typedef BoxPtr LayoutPtr; typedef struct Box { BoxPtr nextSibling; BoxPtr parent; BoxParamsRec params; int size[2]; int natural[2]; BoxType type; union { struct { BoxPtr firstChild; LayoutDirection dir; } box; struct { XrmQuark quark; Widget widget; } widget; struct { ExprPtr expr; } glue; struct { XrmQuark quark; ExprPtr expr; } variable; } u; } BoxRec; typedef struct SubInfo { int naturalSize[2]; int naturalBw; } SubInfoRec, *SubInfoPtr; #define New(t) (t*)XtCalloc(1, sizeof (t)) #define Dispose(p) XtFree((char *)p) #define Some(t,n) (t*) XtCalloc(sizeof(t) * n) #define ForAllChildren(pw, childP) \ for ( (childP) = (pw)->composite.children ; \ (childP) < (pw)->composite.children + (pw)->composite.num_children ; \ (childP)++ ) if (!XtIsManaged(*childP)) ; else extern int LayYYerror(char*); extern int LayYYlex(void); extern int LayYYsetsource(char*); extern void LayYYsetdest(LayoutPtr*); extern int LayYYparse(void); /********************************************************************* * * Layout Widget Private Data * *********************************************************************/ /* New fields for the Layout widget class record */ typedef struct LayoutClassPart { int foo; /* keep compiler happy. */ } LayoutClassPart; /* Full Class record declaration */ typedef struct LayoutClassRec { CoreClassPart core_class; CompositeClassPart composite_class; ConstraintClassPart constraint_class; LayoutClassPart layout_class; } LayoutClassRec; extern LayoutClassRec layoutClassRec; typedef struct LayoutConstraintsRec { SubInfoRec layout; } LayoutConstraintsRec, *LayoutConstraints; #define SubInfo(w) (&(((LayoutConstraints) (w)->core.constraints)->layout)) /* New Fields for the Layout widget record */ typedef struct { /* resources */ LayoutPtr layout; Boolean debug; } LayoutPart; /************************************************************************** * * Full instance record declaration * **************************************************************************/ typedef struct LayoutRec { CorePart core; CompositePart composite; ConstraintPart constraint; LayoutPart layout; } LayoutRec; #endif ./knews-1.0b.1/Widgets/ScrList.h100644 1244 1244 7464 6455455535 15013 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #ifndef ScrList_h #define ScrList_h #ifndef XtCAllowDnd #define XtCAllowDnd "AllowDnd" #endif #ifndef XtCAtLeastOne #define XtCAtLeastOne "AtLeastOne" #endif #ifndef XtCAtMostOne #define XtCAtMostOne "AtMostOne" #endif #ifndef XtCDepthOne #define XtCDepthOne "DepthOne" #endif #ifndef XtCHighlightColor #define XtCHighlightColor "HighlightColor" #endif #ifndef XtCIndentation #define XtCIndentation "Indentation" #endif #ifndef XtCInternalHeight #define XtCInternalHeight "InternalHeight" #endif #ifndef XtCInternalItemHeight #define XtCInternalItemHeight "InternalItemHeight" #endif #ifndef XtCInternalItemWidth #define XtCInternalItemWidth "InternalItemWidth" #endif #ifndef XtCInternalWidth #define XtCInternalWidth "InternalWidth" #endif #ifndef XtCNAlloc #define XtCNAlloc "NAlloc" #endif #ifndef XtCPixmapHeight #define XtCPixmapHeight "PixmapHeight" #endif #ifndef XtCPixmapSpacing #define XtCPixmapSpacing "PixmapSpacing" #endif #ifndef XtCPixmapWidth #define XtCPixmapWidth "PixmapWidth" #endif #ifndef XtCPreferredLines #define XtCPreferredLines "PreferredLines" #endif #ifndef XtCPreferredColumns #define XtCPreferredColumns "PreferredColumns" #endif #ifndef XtCRowSpacing #define XtCRowSpacing "RowSpacing" #endif #ifndef XtCUsePixmaps #define XtCUsePixmaps "UsePixmaps" #endif #ifndef XtCMargin #define XtCMargin "Margin" #endif #ifndef XtCPage #define XtCPage "Page" #endif #ifndef XtNallowDnd #define XtNallowDnd "allowDnd" #endif #ifndef XtNatLeastOne #define XtNatLeastOne "atLeastOne" #endif #ifndef XtNatMostOne #define XtNatMostOne "atMostOne" #endif #ifndef XtNsecondCallback #define XtNsecondCallback "secondCallback" #endif #ifndef XtNselectCallback #define XtNselectCallback "selectCallback" #endif #ifndef XtNdndCallback #define XtNdndCallback "dndCallback" #endif #ifndef XtNdndCursor #define XtNdndCursor "dndCursor" #endif #ifndef XtNdepthOne #define XtNdepthOne "depthOne" #endif #ifndef XtNhighlightColor #define XtNhighlightColor "highlightColor" #endif #ifndef XtNindentation #define XtNindentation "indentation" #endif #ifndef XtNinternalItemHeight #define XtNinternalItemHeight "internalItemHeight" #endif #ifndef XtNinternalItemWidth #define XtNinternalItemWidth "internalItemWidth" #endif #ifndef XtNnAlloc #define XtNnAlloc "nAlloc" #endif #ifndef XtNpixmapHeight #define XtNpixmapHeight "pixmapHeight" #endif #ifndef XtNpixmapSpacing #define XtNpixmapSpacing "pixmapSpacing" #endif #ifndef XtNpixmapWidth #define XtNpixmapWidth "pixmapWidth" #endif #ifndef XtNpreferredLines #define XtNpreferredLines "preferredLines" #endif #ifndef XtNpreferredColumns #define XtNpreferredColumns "preferredColumns" #endif #ifndef XtNrowSpacing #define XtNrowSpacing "rowSpacing" #endif #ifndef XtNusePixmaps #define XtNusePixmaps "usePixmaps" #endif #ifndef XtNmarginUp #define XtNmarginUp "marginUp" #endif #ifndef XtNmarginDown #define XtNmarginDown "marginDown" #endif #ifndef XtNpageUp #define XtNpageUp "pageUp" #endif #ifndef XtNpageDown #define XtNpageDown "pageDown" #endif typedef struct ScrListClassRec* ScrListWidgetClass; typedef struct ScrListRec* ScrListWidget; extern WidgetClass scrListWidgetClass; extern void ScrListClearLines(Widget); extern long ScrListAddLine(Widget, char*, Pixmap); extern void ScrListSetLine(Widget, long, char*, Pixmap); extern void ScrListDeleteLine(Widget, long); extern void ScrListSetSelected(Widget, long, int); extern void ScrListMakeVisible(Widget, long); extern int ScrListGetSelected(Widget, long); extern long ScrListGetFirstSelected(Widget); extern long ScrListGetNextSelected(Widget, long); extern char *ScrListGetString(Widget, long); extern Pixmap ScrListGetPixmap(Widget, long); extern void ScrListPurgePixmap(Widget, Pixmap); extern long ScrListEventToIndex(Widget, XEvent*); extern void ScrListSetActive(Widget, int); #endif /* ScrList_h */ ./knews-1.0b.1/Widgets/Manager.c100644 1244 1244 23376 6455455535 15015 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include #include #include "Compat.h" #include "ManagerP.h" static XtResource resources[] = { {XtNborderWidth, XtCBorderWidth, XtRDimension, sizeof(Dimension), XtOffsetOf(ManagerRec, core.border_width), XtRImmediate, (XtPointer)0}, #define offset(field) XtOffsetOf(ManagerRec, manager.field) {XtNresizeCallback, XtCCallback, XtRCallback, sizeof(XtCallbackList), offset(resize_callback), XtRCallback, (XtPointer)NULL}, {XtNpreferredWidth, XtCPreferredWidth, XtRDimension, sizeof(Dimension), offset(pref_width), XtRImmediate, (XtPointer)0}, {XtNpreferredHeight, XtCPreferredHeight, XtRDimension, sizeof(Dimension), offset(pref_height), XtRImmediate, (XtPointer)0}, #undef offset }; static XtResource sub_resources[] = { #define offset(field) XtOffsetOf(ManagerConstraintsRec, manager.field) {XtNcontainHoriz, XtCContain, XtRBoolean, sizeof(Boolean), offset(contain_horiz), XtRImmediate, (XtPointer)False}, {XtNcontainVert, XtCContain, XtRBoolean, sizeof(Boolean), offset(contain_vert), XtRImmediate, (XtPointer)False}, #undef offset }; static void Initialize(Widget, Widget, ArgList, Cardinal*); static void Resize(Widget); static XtGeometryResult GeometryManager(Widget, XtWidgetGeometry*, XtWidgetGeometry*); static void ChangeManaged(Widget); static XtGeometryResult QueryGeometry(Widget, XtWidgetGeometry*, XtWidgetGeometry*); static Boolean ConstraintSetValues(Widget, Widget, Widget, ArgList, Cardinal*); ManagerClassRec managerClassRec = { { /* core fields */ (WidgetClass) &constraintClassRec, /* superclass */ "Manager", /* class_name */ sizeof(ManagerRec), /* widget_size */ NULL, /* class_initialize */ NULL, /* class_part_initialize*/ FALSE, /* class_inited */ Initialize, /* initialize */ NULL, /* initialize_hook */ XtInheritRealize, /* realize */ NULL, /* actions */ 0, /* num_actions */ resources, /* resources */ XtNumber(resources), /* resource_count */ NULLQUARK, /* xrm_class */ TRUE, /* compress_motion */ TRUE, /* compress_exposure */ TRUE, /* compress_enterleave */ FALSE, /* visible_interest */ NULL, /* destroy */ Resize, /* resize */ NULL, /* expose */ NULL, /* set_values */ NULL, /* set_values_hook */ XtInheritSetValuesAlmost, /* set_values_almost */ NULL, /* get_values_hook */ NULL, /* accept_focus */ XtVersion, /* version */ NULL, /* callback_private */ NULL, /* tm_table */ QueryGeometry, /* query_geometry */ XtInheritDisplayAccelerator, /* display_accelerator */ NULL /* extension */ }, { /* composite fields */ GeometryManager, /* geometry manager */ ChangeManaged, /* change_managed */ XtInheritInsertChild, /* insert child */ XtInheritDeleteChild, /* delete child */ NULL /* extension */ }, { /* constraint fields */ sub_resources, /* resources */ XtNumber(sub_resources), /* num_resources */ sizeof(ManagerConstraintsRec), /* constraint_size */ NULL, /* initialize */ NULL, /* destroy */ ConstraintSetValues, /* set_values */ NULL, /* extension */ }, { /* manager fields */ NULL /* extension */ } }; WidgetClass managerWidgetClass = (WidgetClass) &managerClassRec; /*************************************************************************/ static int fit_coords(Dimension parent_size, Dimension *child_size, Position *child_pos, int contain) { if (contain) { if (*child_pos == 0 && *child_size == parent_size) return False; *child_pos = 0; *child_size = parent_size; return True; } if (*child_pos > 0) { *child_pos = 0; if (*child_size < parent_size) *child_size = parent_size; return True; } if (*child_pos < (int)parent_size - (int)*child_size) { *child_pos = (int)parent_size - (int)*child_size; if (*child_pos > 0) { *child_pos = 0; *child_size = parent_size; } return True; } return False; } static Widget find_child(ManagerWidget w) { Widget *loop = w->composite.children; int n = w->composite.num_children; while (n-- > 0) if (XtIsManaged(*loop)) return *loop; else loop++; return NULL; } static void fit_child(ManagerWidget w, Widget child) { XtWidgetGeometry intended, preferred; ManagerCons cons = (ManagerCons)child->core.constraints; Dimension width = child->core.width; Dimension height = child->core.height; Position x = child->core.x; Position y = child->core.y; fit_coords(w->core.width, &width, &x, cons->manager.contain_horiz); fit_coords(w->core.height, &height, &y, cons->manager.contain_vert); intended.request_mode = CWX | CWY | CWWidth | CWHeight; intended.x = x; intended.y = y; intended.width = width; intended.height = height; if (XtQueryGeometry(child, &intended, &preferred) == XtGeometryAlmost) { if (preferred.request_mode & CWX) x = preferred.x; if (preferred.request_mode & CWY) y = preferred.y; if (preferred.request_mode & CWWidth) width = preferred.width; if (preferred.request_mode & CWHeight) height = preferred.height; fit_coords(w->core.width, &width, &x, cons->manager.contain_horiz); fit_coords(w->core.height, &height, &y, cons->manager.contain_vert); } XtConfigureWidget(child, x, y, width, height, 0); } static void Initialize(Widget grequest, Widget gnew, ArgList args, Cardinal *num_args) { ManagerWidget new = (ManagerWidget)gnew; if (new->core.width == 0) if (new->manager.pref_width == 0) new->core.width = 1; else new->core.width = new->manager.pref_width; if (new->core.height == 0) if (new->manager.pref_height == 0) new->core.height = 1; else new->core.height = new->manager.pref_height; } static void Resize(Widget gw) { ManagerWidget w = (ManagerWidget)gw; Widget child = find_child(w); XtCallbackList c_list = w->manager.resize_callback; if (child) fit_child(w, child); if (c_list) XtCallCallbackList((Widget)w, c_list, NULL); } static void ChangeManaged(Widget gw) { ManagerWidget w = (ManagerWidget)gw; Widget child = find_child(w); if (child) fit_child(w, child); } static XtGeometryResult QueryGeometry(Widget gw, XtWidgetGeometry *intended, XtWidgetGeometry *preferred) { ManagerWidget w = (ManagerWidget)gw; Widget child = find_child(w); Dimension pref_width = w->manager.pref_width; Dimension pref_height = w->manager.pref_height; Dimension intended_width, intended_height; if (intended->request_mode & CWWidth) intended_width = intended->width; else intended_width = w->core.width; if (intended->request_mode & CWHeight) intended_height = intended->height; else intended_height = w->core.height; if (pref_width == 0 || pref_height == 0) { XtWidgetGeometry child_pref; XtGeometryResult result; if (!child) return XtGeometryNo; result = XtQueryGeometry(child, NULL, &child_pref); if (result == XtGeometryAlmost) { if ((child_pref.request_mode & CWWidth) && pref_width == 0) pref_width = child_pref.width; if ((child_pref.request_mode & CWHeight) && pref_height == 0) pref_height = child_pref.height; } if (pref_width == 0) pref_width = child->core.width; if (pref_height == 0) pref_height = child->core.height; } preferred->request_mode = CWWidth | CWHeight; preferred->width = pref_width; preferred->height = pref_height; if (pref_width == w->core.width && pref_height == w->core.height) return XtGeometryNo; if (pref_width == intended_width && pref_height == intended_height) return XtGeometryYes; return XtGeometryAlmost; } static XtGeometryResult GeometryManager(Widget child, XtWidgetGeometry *req, XtWidgetGeometry *reply) { ManagerWidget w = (ManagerWidget)XtParent(child); ManagerCons cons = (ManagerCons)child->core.constraints; Dimension width, height; Position x, y; Boolean ok = True; if (child != find_child(w)) return XtGeometryNo; if (req->request_mode & CWX) x = req->x; else x = child->core.x; if (req->request_mode & CWY) y = req->y; else y = child->core.y; if (req->request_mode & CWWidth) width = req->width; else width = child->core.width; if (req->request_mode & CWHeight) height = req->height; else height = child->core.height; if (!fit_coords(w->core.width, &width, &x, cons->manager.contain_horiz)) ok = False; if (!fit_coords(w->core.height, &height, &y, cons->manager.contain_vert)) ok = False; if (req->request_mode & XtCWQueryOnly) { reply->request_mode = CWX | CWY | CWWidth | CWHeight; reply->x = x; reply->y = y; reply->width = width; reply->height = height; return XtGeometryAlmost; } XtConfigureWidget(child, x, y, width, height, 0); return XtGeometryDone; } static Boolean ConstraintSetValues(Widget current, Widget request, Widget new, ArgList args, Cardinal *num_args) { ManagerWidget w = (ManagerWidget)XtParent(new); ManagerCons new_cons = (ManagerCons)new->core.constraints; ManagerCons curr_cons = (ManagerCons)current->core.constraints; if (new == find_child(w) && (curr_cons->manager.contain_horiz != new_cons->manager.contain_horiz || curr_cons->manager.contain_vert != new_cons->manager.contain_vert)) fit_child(w, new); return False; } /***************************************************************/ void Remanage(Widget w) { ChangeManaged(w); } ./knews-1.0b.1/Widgets/Manager.h100644 1244 1244 1543 6455455536 14773 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #ifndef Manager_h #define Manager_h #ifndef XtCContain #define XtCContain "Contain" #endif #ifndef XtCPreferredHeight #define XtCPreferredHeight "PreferredHeight" #endif #ifndef XtCPreferredWidth #define XtCPreferredWidth "PreferredWidth" #endif #ifndef XtNcontainHoriz #define XtNcontainHoriz "containHoriz" #endif #ifndef XtNcontainVert #define XtNcontainVert "containVert" #endif #ifndef XtNpreferredHeight #define XtNpreferredHeight "preferredHeight" #endif #ifndef XtNpreferredWidth #define XtNpreferredWidth "preferredWidth" #endif #ifndef XtNresizeCallback #define XtNresizeCallback "resizeCallback" #endif typedef struct ManagerRec *ManagerWidget; typedef struct ManagerClassRec *ManagerWidgetClass; extern WidgetClass managerWidgetClass; extern void Remanage(Widget); #endif /* Manager_h */ ./knews-1.0b.1/Widgets/ManagerP.h100644 1244 1244 1775 6455455536 15122 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #ifndef ManagerP_h #define ManagerP_h #include "Manager.h" #include typedef struct ManagerConstraintsPart { Boolean contain_horiz; Boolean contain_vert; } ManagerConstraintsPart; typedef struct ManagerConstraintsRec { ManagerConstraintsPart manager; } ManagerConstraintsRec, *ManagerCons; typedef struct ManagerPart { XtCallbackList resize_callback; Dimension pref_width; Dimension pref_height; /* private */ } ManagerPart; typedef struct ManagerRec { CorePart core; CompositePart composite; ConstraintPart constraint; ManagerPart manager; } ManagerRec; typedef struct { XtPointer extension; } ManagerClassPart; typedef struct ManagerClassRec { CoreClassPart core_class; CompositeClassPart composite_class; ConstraintClassPart constraint_class; ManagerClassPart manager_class; } ManagerClassRec; extern ManagerClassRec managerClassRec; #endif /* ManagerP_h */ ./knews-1.0b.1/Widgets/Menu.h100644 1244 1244 555 6455455536 14307 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #ifndef Menu_h #define Menu_h #ifndef XtNcursor #define XtNcursor "cursor" #endif typedef struct MenuRec *MenuWidget; typedef struct MenuClassRec *MenuWidgetClass; extern WidgetClass menuWidgetClass; extern Widget MenuCreateGadget(String, WidgetClass, Widget, ArgList, Cardinal); #endif /* Menu_h */ ./knews-1.0b.1/Widgets/MenuG.c100644 1244 1244 14171 6455455536 14450 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include #include #include "Compat.h" #include "MenuGP.h" static XtResource resources[] = { #define offset(field) XtOffsetOf(MenuGadgetRec, menu_g.field) {XtNcallback, XtCCallback, XtRCallback, sizeof(XtCallbackList), offset(callback), XtRImmediate, (XtPointer)NULL}, {XtNpostPopdownCallback, XtCCallback, XtRCallback, sizeof(XtCallbackList), offset(post_popdown_callback), XtRImmediate, (XtPointer)NULL}, {XtNlabel, XtCLabel, XtRString, sizeof(String), offset(label), XtRImmediate, (XtPointer)NULL}, #undef offset }; static void ClassPartInitialize(WidgetClass); static void Initialize(Widget, Widget, ArgList, Cardinal*); static void Destroy(Widget); static Boolean SetValues(Widget, Widget, Widget, ArgList, Cardinal*); static void GenericProc(MenuGadget); static int Notify(MenuGadget); static int PostNotify(MenuGadget); static void SetActive(MenuGadget, int); MenuGadgetClassRec menuGadgetClassRec = { { /* rectObj fields */ (WidgetClass) &rectObjClassRec, /* superclass */ "MenuGadget", /* class_name */ sizeof(MenuGadgetRec), /* widget_size */ NULL, /* class_initialize */ ClassPartInitialize, /* class_part_initialize */ FALSE, /* class_inited */ Initialize, /* initialize */ NULL, /* initialize_hook */ NULL, /* rect1 */ NULL, /* rect2 */ 0, /* rect3 */ resources, /* resources */ XtNumber(resources), /* num_resources */ NULLQUARK, /* xrm_class */ FALSE, /* rect4 */ FALSE, /* rect5 */ FALSE, /* rect6 */ FALSE, /* rect7 */ Destroy, /* destroy */ NULL, /* resize */ NULL, /* expose */ SetValues, /* set_values */ NULL, /* set_values_hook */ XtInheritSetValuesAlmost, /* set_values_almost */ NULL, /* get_values_hook */ NULL, /* rect9 */ XtVersion, /* version */ NULL, /* callback_private */ NULL, /* rect10 */ NULL, /* query_geometry */ NULL, /* rect11 */ NULL, /* extension */ }, { /* menu_g fields */ GenericProc, /* change_hl */ GenericProc, /* popdown */ Notify, /* notify */ PostNotify, /* post_notify */ SetActive, /* set_active */ False, /* ignore_leave */ NULL, /* extension */ } }; WidgetClass menuGadgetClass = (WidgetClass)&menuGadgetClassRec; /*************************************************************************/ static void ClassPartInitialize(WidgetClass gclass) { MenuGadgetClass class, super; class = (MenuGadgetClass)gclass; super = (MenuGadgetClass)class->rect_class.superclass; if (class->menu_g_class.change_hl == XtInheritChangeHl) class->menu_g_class.change_hl = super->menu_g_class.change_hl; if (class->menu_g_class.popdown == XtInheritPopdown) class->menu_g_class.popdown = super->menu_g_class.popdown; if (class->menu_g_class.notify == XtInheritNotify) class->menu_g_class.notify = super->menu_g_class.notify; if (class->menu_g_class.post_notify == XtInheritPostNotify) class->menu_g_class.post_notify = super->menu_g_class.post_notify; if (class->menu_g_class.set_active == XtInheritSetActive) class->menu_g_class.set_active = super->menu_g_class.set_active; } static void Initialize(Widget grequest, Widget gnew, ArgList args, Cardinal *no_args) { MenuGadget new = (MenuGadget)gnew; new->menu_g.hl = False; new->menu_g.inside = False; new->menu_g.active = False; new->menu_g.label = new->menu_g.label ? XtNewString(new->menu_g.label) : XtNewString(XtName((Widget)new)); } static void Destroy(Widget gw) { MenuGadget g = (MenuGadget)gw; XtFree(g->menu_g.label); } static Boolean SetValues(Widget gcurrent, Widget grequest, Widget gnew, ArgList args, Cardinal *num_args) { int redisplay = False; MenuGadget new = (MenuGadget)gnew; MenuGadget current = (MenuGadget)gcurrent; if (new->menu_g.label != current->menu_g.label) { XtFree(current->menu_g.label); new->menu_g.label = new->menu_g.label ? XtNewString(new->menu_g.label) : XtNewString(XtName((Widget)new)); redisplay = True; } return redisplay; } static void GenericProc(MenuGadget g) { } static int Notify(MenuGadget g) { XtCallbackList c_list = g->menu_g.callback; if (!g->menu_g.inside) return False; if (c_list) XtCallCallbackList((Widget)g, c_list, (XtPointer)g->menu_g.label); return True; } static int PostNotify(MenuGadget g) { XtCallbackList c_list = g->menu_g.post_popdown_callback; if (!g->menu_g.inside) return False; if (c_list) XtCallCallbackList((Widget)g, c_list, (XtPointer)g->menu_g.label); return True; } static void SetActive(MenuGadget g, int active) { g->menu_g.active = active; } ./knews-1.0b.1/Widgets/MenuG.h100644 1244 1244 673 6455455536 14417 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #ifndef MenuG_h #define MenuG_h #ifndef XtCLabel #define XtCLabel "Label" #endif #ifndef XtNlabel #define XtNlabel "label" #endif #ifndef XtNpostPopdownCallback #define XtNpostPopdownCallback "postPopdownCallback" #endif typedef struct MenuGadgetClassRec* MenuGadgetClass; typedef struct MenuGadgetRec* MenuGadget; extern WidgetClass menuGadgetClass; #endif /* MenuG_h */ ./knews-1.0b.1/Widgets/MenuGP.h100644 1244 1244 4021 6455455536 14546 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #ifndef MenuGP_h #define MenuGP_h #include "MenuG.h" #include #include "MenuP.h" #define MenuGadgetChangeHlProc(g) \ (((MenuGadgetClass)(g)->object.widget_class)->menu_g_class.change_hl) #define MenuGadgetNotifyProc(g) \ (((MenuGadgetClass)(g)->object.widget_class)->menu_g_class.notify) #define MenuGadgetPostNotifyProc(g) \ (((MenuGadgetClass)(g)->object.widget_class)->menu_g_class.post_notify) #define MenuGadgetPopdownProc(g) \ (((MenuGadgetClass)(g)->object.widget_class)->menu_g_class.popdown) #define MenuGadgetSetActiveProc(g) \ (((MenuGadgetClass)(g)->object.widget_class)->menu_g_class.set_active) #define MenuGadgetIgnoreLeave(g) \ (((MenuGadgetClass)(g)->object.widget_class)->menu_g_class.ignore_leave) typedef void (*MenuGadgetProc)(MenuGadget); typedef int (*MenuGadgetNotifyProc)(MenuGadget); typedef void (*MenuGadgetSetProc)(MenuGadget, int); #define XtInheritChangeHl ((MenuGadgetProc)_XtInherit) #define XtInheritPopdown ((MenuGadgetProc)_XtInherit) #define XtInheritNotify ((MenuGadgetNotifyProc)_XtInherit) #define XtInheritPostNotify ((MenuGadgetNotifyProc)_XtInherit) #define XtInheritSetActive ((MenuGadgetSetProc)_XtInherit) typedef struct { MenuGadgetProc change_hl; MenuGadgetProc popdown; MenuGadgetNotifyProc notify; MenuGadgetNotifyProc post_notify; MenuGadgetSetProc set_active; Boolean ignore_leave; XtPointer extension; } MenuGadgetClassPart; typedef struct MenuGadgetClassRec { RectObjClassPart rect_class; MenuGadgetClassPart menu_g_class; } MenuGadgetClassRec; extern MenuGadgetClassRec menuGadgetClassRec; typedef struct { XtCallbackList callback; XtCallbackList post_popdown_callback; String label; /* private data */ unsigned char hl; unsigned char inside; unsigned char active; } MenuGadgetPart; typedef struct MenuGadgetRec { ObjectPart object; RectObjPart rectangle; MenuGadgetPart menu_g; } MenuGadgetRec; #endif /* MenuGP_h */ ./knews-1.0b.1/Widgets/MenuKnapp.c100644 1244 1244 31764 6455455536 15342 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include #include #include #include #include "Compat.h" #include "MenuI.h" #include "MenuKnappP.h" #include "MenuShell.h" static XtResource resources[] = { {XtNrightMargin, XtCRightMargin, XtRDimension, sizeof(Dimension), XtOffsetOf(MenuKnappRec, knapp.right_margin), XtRImmediate, (XtPointer)24}, #define offset(field) XtOffsetOf(MenuKnappRec, menu_knapp.field) {XtNmenuName, XtCMenuName, XtRString, sizeof(String), offset(menu_name), XtRImmediate, (XtPointer)NULL}, {XtNarrowSize, XtCArrowSize, XtRDimension, sizeof(Dimension), offset(arrow_size), XtRImmediate, (XtPointer)6}, {XtNarrowOffset, XtCArrowOffset, XtRDimension, sizeof(Dimension), offset(arrow_offset), XtRImmediate, (XtPointer)8}, {XtNarrowShadowWidth, XtCShadowWidth, XtRDimension, sizeof(Dimension), offset(arrow_shadow_width), XtRImmediate, (XtPointer)1}, {XtNmultiClickTime, XtCMultiClickTime, XtRInt, sizeof(int), offset(multi_click_time), XtRImmediate, (XtPointer)200}, {XtNpopdownTime, XtCMultiClickTime, XtRInt, sizeof(int), offset(popdown_time), XtRImmediate, (XtPointer)200}, #undef offset }; static void Initialize(Widget, Widget, ArgList, Cardinal*); static void Redisplay(Widget, XEvent*, Region); static void Destroy(Widget); static Boolean SetValues(Widget, Widget, Widget, ArgList, Cardinal*); static void popup_and_grab(Widget, XEvent*, String*, Cardinal*); static void popdown_and_notify_if(Widget, XEvent*, String*, Cardinal*); static void set_unless(Widget, XEvent*, String*, Cardinal*); static XtActionsRec actions[] = { {"popup-and-grab", popup_and_grab}, {"popdown-and-notify-if", popdown_and_notify_if}, {"set-unless", set_unless}, }; static char translations[] = ": popup-and-grab() \n" ": popdown-and-notify-if() \n"; MenuKnappClassRec menuKnappClassRec = { { /* core fields */ (WidgetClass) &knappClassRec, /* superclass */ "MenuKnapp", /* class_name */ sizeof(MenuKnappRec), /* widget_size */ NULL, /* class_initialize */ NULL, /* class_part_initialize */ FALSE, /* class_inited */ Initialize, /* initialize */ NULL, /* initialize_hook */ XtInheritRealize, /* realize */ actions, /* actions */ XtNumber(actions), /* num_actions */ resources, /* resources */ XtNumber(resources), /* num_resources */ NULLQUARK, /* xrm_class */ TRUE, /* compress_motion */ #if (XtSpecificationRelease < 4) True, /* compress exposure */ #elif (XtSpecificationRelease < 6) XtExposeCompressMaximal, /* compress_exposure */ #else XtExposeCompressMaximal | XtExposeNoRegion, /* compress_exposure*/ #endif TRUE, /* compress_enterleave */ FALSE, /* visible_interest */ Destroy, /* destroy */ XtInheritResize, /* resize */ Redisplay, /* expose */ SetValues, /* set_values */ NULL, /* set_values_hook */ XtInheritSetValuesAlmost, /* set_values_almost */ NULL, /* get_values_hook */ NULL, /* accept_focus */ XtVersion, /* version */ NULL, /* callback_private */ translations, /* tm_table */ XtInheritQueryGeometry, /* query_geometry */ XtInheritDisplayAccelerator, /* display_accelerator */ NULL /* extension */ }, { /* shadow fields */ XtInheritPixelOffset, /* pixel_offset */ False, /* use_arm_for_background */ XtInheritAllocShadowColors, /* alloc_shadow_colors */ XtInheritAllocShadowPixmaps, /* alloc_shadow_pixmaps */ XtInheritAllocArmColor, /* alloc_arm_color */ XtInheritAllocArmPixmap, /* alloc_arm_pixmap */ XtInheritAllocGCs, /* alloc_gcs */ NULL, /* extension */ }, { /* knapp fields */ NULL, /* extension */ }, { /* menu_knapp fields */ NULL, /* extension */ } }; WidgetClass menuKnappWidgetClass = (WidgetClass)&menuKnappClassRec; /*************************************************************************/ static void draw_arrow(MenuKnappWidget w, int inverted) { Display *disp = XtDisplay(w); Window win = XtWindow(w); int s = w->menu_knapp.arrow_size; int y = ((int)w->core.height - s) / 2; int x; if (s <= 0) return; x = w->core.width + w->menu_knapp.arrow_offset - w->shadow.shadow_width - w->knapp.right_margin; if (w->shadow.line_mode) { short sw = w->shadow.shadow_width; XPoint point[4]; XClearArea(disp, win, x - sw, y - sw, s + 2 * sw, s + 2 * sw, False); if (inverted) sw = 0; else sw = (sw + 1)/2; point[0].x = point[3].x = x + s - sw; point[1].x = x + sw; point[2].x = x + s/2; point[0].y = point[1].y = point[3].y = y + sw; point[2].y = y + s - sw; XDrawLines(disp, win, inverted ? w->shadow.light_gc : w->shadow.dark_gc, point, 4, CoordModeOrigin); } else { XPoint point[6]; GC light_gc, dark_gc; short sw = w->menu_knapp.arrow_shadow_width; if (inverted) { light_gc = w->shadow.dark_gc; dark_gc = w->shadow.light_gc; } else { light_gc = w->shadow.light_gc; dark_gc = w->shadow.dark_gc; } if (sw == 0) return; else if (sw == 1) { point[0].x = point[3].x = x + s; point[1].x = x; point[2].x = (2 * x + s) / 2; point[0].y = point[1].y = point[3].y = y; point[2].y = y + s; point[3].y--; if (inverted && w->shadow.arm_gc != 0) XFillPolygon(disp, win, w->shadow.arm_gc, point, 3, Convex, CoordModeOrigin); XDrawLines(disp, win, light_gc, point, 3, CoordModeOrigin); XDrawLines(disp, win, dark_gc, &point[2], 2, CoordModeOrigin); } else { point[0].x = x + s; point[1].x = x; point[2].x = point[3].x = (2 * x + s) / 2; point[4].x = x + (3 * sw) / 2; point[5].x = x + s - (3 * sw) / 2; point[0].y = point[1].y = y; point[2].y = y + s; point[3].y = y + s - (9 * sw) / 4; point[4].y = point[5].y = y + sw; if (inverted && w->shadow.arm_gc != 0) XFillPolygon(disp, win, w->shadow.arm_gc, &point[3], 3, Convex, CoordModeOrigin); XFillPolygon(disp, win, light_gc, point, 6, Nonconvex, CoordModeOrigin); point[4] = point[5]; point[5] = point[0]; XFillPolygon(disp, win, dark_gc, &point[2], 4, Convex, CoordModeOrigin); } } } static Widget find_menu(MenuKnappWidget w) { Widget loop; if (w->menu_knapp.menu_name) for (loop = (Widget)w ; loop ; loop = XtParent(loop)) { Widget temp = XtNameToWidget(loop, w->menu_knapp.menu_name); if (temp) return temp; } return NULL; } static void set_unless(Widget gw, XEvent *event, String *params, Cardinal *no_params) { MenuKnappWidget w = (MenuKnappWidget)gw; if (w->menu_knapp.menu_state == MenuStateDown) XtCallActionProc((Widget)w, "set", event, params, *no_params); } static void popup_and_grab(Widget gw, XEvent *event, String *params, Cardinal *no_params) { MenuKnappWidget w = (MenuKnappWidget)gw; Widget menu = w->menu_knapp.menu; Screen *screen = XtScreen(w); Arg args[2]; Position x, y; int tmp; if (!w->knapp.active || event->type != ButtonPress || w->menu_knapp.menu_state != MenuStateDown) return; if (!menu && !(menu = w->menu_knapp.menu = find_menu(w))) { fprintf(stderr, "MenuKnapp: Couldn't find popup menu widget: %s\n", w->menu_knapp.menu_name ? w->menu_knapp.menu_name : "(null)"); return; } if (!XtIsSubclass(menu, menuShellWidgetClass)) { fprintf(stderr, "MenuKnapp: Widget %s is not a " "subclass of menuShell.\n", XtName(menu)); return; } XtAddGrab((Widget)w, True, True); if (XtGrabPointer((Widget)w, True, ButtonPressMask | ButtonReleaseMask | PointerMotionMask, GrabModeAsync, GrabModeAsync, None, None, event->xbutton.time) != GrabSuccess) { XtRemoveGrab((Widget)w); fputs("MenuKnapp: Failed to grab pointer.\n", stderr); return; } if (!XtIsRealized(menu)) XtRealizeWidget(menu); SetActiveMenuShell(menu, True); XtTranslateCoords((Widget)w, 0, 0, &x, &y); y += w->core.height + w->core.border_width; if (x < 0) x = 0; else { tmp = WidthOfScreen(screen) - menu->core.width; if (tmp >= 0 && x > tmp) x = tmp; } if (y < 0) y = 0; else { tmp = HeightOfScreen(screen) - menu->core.height; if (tmp >= 0 && y > tmp) y = tmp; } w->menu_knapp.menu_state = MenuStateUp; w->menu_knapp.start_time = event->xbutton.time; XtSetArg(args[0], XtNx, x); XtSetArg(args[1], XtNy, y); XtSetValues(menu, args, 2); XtPopup(menu, XtGrabNone); XtAddGrab(menu, False, False); XClearWindow(XtDisplay(w), XtWindow(w)); Redisplay((Widget)w, NULL, NULL); } static void popdown_timer(XtPointer client_data, XtIntervalId *id) { MenuKnappWidget w = (MenuKnappWidget)client_data; w->menu_knapp.timer = 0; w->menu_knapp.menu_state = MenuStateDown; PopdownMenuShell(w->menu_knapp.menu); #if 0 XtRemoveGrab((Widget)w); #endif PostNotifyMenuShell(w->menu_knapp.menu); draw_arrow(w, True); } static void popdown_and_notify_if(Widget gw, XEvent *event, String *params, Cardinal *no_params) { MenuKnappWidget w = (MenuKnappWidget)gw; unsigned int x, y; Time t; if (event->type != ButtonRelease || w->menu_knapp.menu_state != MenuStateUp) return; x = event->xbutton.x; y = event->xbutton.y; t = event->xbutton.time; if (event->xbutton.window == XtWindow(w) && x < w->core.width && y < w->core.height && t <= w->menu_knapp.start_time + w->menu_knapp.multi_click_time) return; /* * We wouldn't want an active grab when we're calling the callbacks... */ XtUngrabPointer((Widget)w, event->xbutton.time); XtRemoveGrab(w->menu_knapp.menu); XtRemoveGrab((Widget)w); SetActiveMenuShell(w->menu_knapp.menu, False); XFlush(XtDisplay(w)); if (!NotifyMenuShell(w->menu_knapp.menu)) { popdown_timer((XtPointer)w, 0); return; } w->menu_knapp.menu_state = MenuStateWaiting; w->menu_knapp.timer = XtAppAddTimeOut(XtWidgetToApplicationContext((Widget)w), w->menu_knapp.popdown_time, popdown_timer, (XtPointer)w); } /*************************************************************************/ static void Initialize(Widget grequest, Widget gnew, ArgList args, Cardinal *no_args) { MenuKnappWidget new = (MenuKnappWidget)gnew; new->menu_knapp.menu = find_menu(new); new->menu_knapp.start_time = 0; new->menu_knapp.menu_state = MenuStateDown; new->menu_knapp.timer = 0; } static void Redisplay(Widget gw, XEvent *event, Region region) { MenuKnappWidget w = (MenuKnappWidget)gw; knappWidgetClass->core_class.expose((Widget)w, NULL, NULL); if (w->menu_knapp.arrow_shadow_width > 0) draw_arrow(w, w->menu_knapp.menu_state == MenuStateDown); } static void Destroy(Widget gw) { MenuKnappWidget w = (MenuKnappWidget)gw; if (w->menu_knapp.timer) XtRemoveTimeOut(w->menu_knapp.timer); w->menu_knapp.timer = 0; w->menu_knapp.menu_state = MenuStateDown; } static Boolean SetValues(Widget gcurrent, Widget grequest, Widget gnew, ArgList args, Cardinal *num_args) { MenuKnappWidget new = (MenuKnappWidget)gnew; MenuKnappWidget current = (MenuKnappWidget)gcurrent; Boolean redisplay = False; if (new->menu_knapp.menu_name != current->menu_knapp.menu_name) { if (new->menu_knapp.menu_state == MenuStateWaiting) { if (new->menu_knapp.timer) XtRemoveTimeOut(new->menu_knapp.timer); popdown_timer((XtPointer)new, 0); } new->menu_knapp.menu = find_menu(new); } if (new->menu_knapp.arrow_shadow_width != current->menu_knapp.arrow_shadow_width || new->menu_knapp.arrow_size != current->menu_knapp.arrow_size || new->menu_knapp.arrow_offset != current->menu_knapp.arrow_offset) redisplay = True; return redisplay; } ./knews-1.0b.1/Widgets/MenuKnapp.h100644 1244 1244 1662 6455455536 15321 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #ifndef MenuKnapp_h #define MenuKnapp_h #ifndef XtCArrowSize #define XtCArrowSize "ArrowSize" #endif #ifndef XtCArrowOffset #define XtCArrowOffset "ArrowOffset" #endif #ifndef XtCMenuName #define XtCMenuName "MenuName" #endif #ifndef XtCMultiClickTime #define XtCMultiClickTime "MultiClickTime" #endif #ifndef XtNarrowSize #define XtNarrowSize "arrowSize" #endif #ifndef XtNarrowOffset #define XtNarrowOffset "arrowOffset" #endif #ifndef XtNarrowShadowWidth #define XtNarrowShadowWidth "arrowShadowWidth" #endif #ifndef XtNmenuName #define XtNmenuName "menuName" #endif #ifndef XtNmultiClickTime #define XtNmultiClickTime "multiClickTime" #endif #ifndef XtNpopdownTime #define XtNpopdownTime "popdownTime" #endif typedef struct MenuKnappClassRec* MenuKnappWidgetClass; typedef struct MenuKnappRec* MenuKnappWidget; extern WidgetClass menuKnappWidgetClass; #endif /* MenuKnapp_h */ ./knews-1.0b.1/Widgets/ScrBar.h100644 1244 1244 4117 6455455536 14575 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #ifndef ScrBar_h #define ScrBar_h #ifndef XtCAllowOff #define XtCAllocOff "AllowOff" #endif #ifndef XtCCanvasLength #define XtCCanvasLength "CanvasLength" #endif #ifndef XtCDecay #define XtCDecay "Decay" #endif #ifndef XtCDelay #define XtCDelay "Delay" #endif #ifndef XtCMinimumDelay #define XtCMinimumDelay "MinimumDelay" #endif #ifndef XtCMinimumThumb #define XtCMinimumThumb "MinimumThumb" #endif #ifndef XtCPushThumb #define XtCPushThumb "PushThumb" #endif #ifndef XtCScrollCallback #define XtCScrollCallback "ScrollCallback" #endif #ifndef XtCSliderLength #define XtCSliderLength "SliderLength" #endif #ifndef XtCSliderPosition #define XtCSliderPosition "SliderPosition" #endif #ifndef XtCStepSize #define XtCStepSize "StepSize" #endif #ifndef XtCSyncScroll #define XtCSyncScroll "SyncScroll" #endif #ifndef XtCVertical #define XtCVertical "Vertical" #endif #ifndef XtNallowOff #define XtNallowOff "allowOff" #endif #ifndef XtNcanvasLength #define XtNcanvasLength "canvasLength" #endif #ifndef XtNdecay #define XtNdecay "decay" #endif #ifndef XtNinitialDelay #define XtNinitialDelay "initialDelay" #endif #ifndef XtNminimumDelay #define XtNminimumDelay "minimumDelay" #endif #ifndef XtNminimumThumb #define XtNminimumThumb "minimumThumb" #endif #ifndef XtNpushThumb #define XtNpushThumb "pushThumb" #endif #ifndef XtNscrollCallback #define XtNscrollCallback "scrollCallback" #endif #ifndef XtNsliderLength #define XtNsliderLength "sliderLength" #endif #ifndef XtNsliderPosition #define XtNsliderPosition "sliderPosition" #endif #ifndef XtNstepSize #define XtNstepSize "stepSize" #endif #ifndef XtNsyncScroll #define XtNsyncScroll "syncScroll" #endif #ifndef XtNvertical #define XtNvertical "vertical" #endif typedef struct ScrBarClassRec* ScrBarWidgetClass; typedef struct ScrBarRec* ScrBarWidget; extern WidgetClass scrBarWidgetClass; typedef struct { long pos; long shown; long size; } ScrollReport; extern void ScrBarSetSliderPosition(Widget, long); extern void ScrBarSetLengthsAndPos(Widget, long, long, long); #endif /* ScrBar_h */ ./knews-1.0b.1/Widgets/MenuKnappP.h100644 1244 1244 1741 6455455536 15437 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #ifndef MenuKnappP_h #define MenuKnappP_h #include "MenuKnapp.h" #include "KnappP.h" typedef struct { XtPointer empty; } MenuKnappClassPart; typedef struct MenuKnappClassRec { CoreClassPart core_class; ShadowClassPart shadow_class; KnappClassPart knapp_class; MenuKnappClassPart menu_knapp_class; } MenuKnappClassRec; extern MenuKnappClassRec menuKnappClassRec; enum { MenuStateDown, MenuStateUp, MenuStateWaiting }; typedef struct { String menu_name; int multi_click_time; int popdown_time; Dimension arrow_size; Dimension arrow_offset; Dimension arrow_shadow_width; /* private data */ Widget menu; Time start_time; int menu_state; XtIntervalId timer; } MenuKnappPart; typedef struct MenuKnappRec { CorePart core; ShadowPart shadow; KnappPart knapp; MenuKnappPart menu_knapp; } MenuKnappRec; #endif /* MenuKnappP_h */ ./knews-1.0b.1/Widgets/MenuP.h100644 1244 1244 1400 6455455536 14435 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #ifndef MenuP_h #define MenuP_h #include "Menu.h" #include "ShadowP.h" #include "MenuGP.h" typedef struct { XtPointer extension; } MenuClassPart; typedef struct MenuClassRec { CoreClassPart core_class; ShadowClassPart shadow_class; MenuClassPart menu_class; } MenuClassRec; extern MenuClassRec menuClassRec; typedef struct MenuPart { Cursor cursor; /* private */ MenuGadget *children; MenuGadget current; Cardinal num_children; Cardinal num_slots; Dimension pref_width; Dimension pref_height; Boolean active; } MenuPart; typedef struct MenuRec { CorePart core; ShadowPart shadow; MenuPart menu; } MenuRec; #endif /* MenuP_h */ ./knews-1.0b.1/Widgets/Message.c100644 1244 1244 23002 6455455536 15012 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include #include #include #include "Compat.h" #include "MessageP.h" static XtResource resources[] = { {XtNborderWidth, XtCBorderWidth, XtRDimension, sizeof(Dimension), XtOffsetOf(MessageRec, core.border_width), XtRImmediate, (XtPointer)0}, #define offset(field) XtOffsetOf(MessageRec, message.field) {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel), offset(foreground_pixel), XtRString, XtDefaultForeground}, {XtNfont, XtCFont, XtRFontStruct, sizeof(XFontStruct *), offset(font), XtRString, XtDefaultFont}, {XtNbuffer, XtCBuffer, XtRString, sizeof(String), offset(buffer), XtRString, (XtPointer)NULL}, {XtNinternalWidth, XtCInternalWidth, XtRDimension, sizeof(Dimension), offset(internal_width), XtRImmediate, (XtPointer)8}, {XtNinternalHeight, XtCInternalHeight, XtRDimension, sizeof(Dimension), offset(internal_height), XtRImmediate, (XtPointer)2}, {XtNpreferredChars, XtCPreferredChars, XtRDimension, sizeof(Dimension), offset(pref_chars), XtRImmediate, (XtPointer)0}, {XtNcenter, XtCCenter, XtRBoolean, sizeof(Boolean), offset(center), XtRImmediate, (XtPointer)True}, #undef offset }; static void Initialize(Widget, Widget, ArgList, Cardinal*); static void Destroy(Widget); static void Redisplay(Widget, XEvent*, Region); static Boolean SetValues(Widget, Widget, Widget, ArgList, Cardinal*); static XtGeometryResult QueryGeometry(Widget, XtWidgetGeometry*, XtWidgetGeometry*); MessageClassRec messageClassRec = { { /* core fields */ (WidgetClass) &widgetClassRec, /* superclass */ "Message", /* class_name */ sizeof(MessageRec), /* widget_size */ NULL, /* class_initialize */ NULL, /* class_part_initialize */ FALSE, /* class_inited */ Initialize, /* initialize */ NULL, /* initialize_hook */ XtInheritRealize, /* realize */ NULL, /* actions */ 0, /* num_actions */ resources, /* resources */ XtNumber(resources), /* num_resources */ NULLQUARK, /* xrm_class */ TRUE, /* compress_motion */ #if (XtSpecificationRelease < 4) TRUE, /* compress_exposure */ #elif (XtSpecificationRelease < 6) XtExposeCompressMaximal, /* compress_exposure */ #else XtExposeCompressMaximal | XtExposeNoRegion, /* compress_exposure*/ #endif TRUE, /* compress_enterleave */ FALSE, /* visible_interest */ Destroy, /* destroy */ False, /* resize */ Redisplay, /* expose */ SetValues, /* set_values */ NULL, /* set_values_hook */ XtInheritSetValuesAlmost, /* set_values_almost */ NULL, /* get_values_hook */ NULL, /* accept_focus */ XtVersion, /* version */ NULL, /* callback_private */ NULL, /* tm_table */ QueryGeometry, /* query_geometry */ XtInheritDisplayAccelerator, /* display_accelerator */ NULL /* extension */ }, { /* message fields */ 0 /* extension */ } }; WidgetClass messageWidgetClass = (WidgetClass)&messageClassRec; /*************************************************************************/ static void init_gcs(MessageWidget w) { XGCValues values; values.foreground = w->message.foreground_pixel; values.font = w->message.font->fid; w->message.default_gc = XtGetGC((Widget)w, GCForeground | GCFont, &values); } static void free_gcs(MessageWidget w) { XtReleaseGC((Widget)w, w->message.default_gc); } static void copy_message(MessageWidget w, char *message) { char *c; long n, i; if (!message) message = ""; n = strlen(message); if (n + 8 > w->message.n_alloc) { w->message.n_alloc = n + 8; w->message.buffer = XtRealloc(w->message.buffer, n + 8); } memcpy(w->message.buffer, message, n + 1); c = strchr(w->message.buffer, '\n'); i = 1; while (c) { c = strchr(c + 1, '\n'); i++; } w->message.rows = i; } static void get_preferred_sizes(MessageWidget w, Dimension *width, Dimension *height) { char *c = w->message.buffer; *width = 0; if (w->message.pref_chars > 0) *width = w->message.pref_chars * w->message.font->max_bounds.width; else if (c) { while (True) { char *p = strchr(c, '\n'); int len; Dimension temp; if (p) len = p - c; else len = strlen(c); temp = XTextWidth(w->message.font, c, len); if (*width < temp) *width = temp; if (p) c = p + 1; else break; } } *width += 2 * w->message.internal_width; if (w->message.rows > 1) *height = w->message.rows * (w->message.font->ascent + w->message.font->descent); else *height = w->message.font->ascent + w->message.font->descent; *height += 2 * w->message.internal_height; } /*************************************************************************/ static void Initialize(Widget grequest, Widget gnew, ArgList args, Cardinal *no_args) { MessageWidget new = (MessageWidget)gnew; char *old = new->message.buffer; if (!old) old = XtName((Widget)new); new->message.rows = 1; new->message.buffer = NULL; new->message.n_alloc = 0; copy_message(new, old); init_gcs(new); if (new->core.width == 0 || new->core.height == 0) { Dimension width, height; get_preferred_sizes(new, &width, &height); if (new->core.width == 0) new->core.width = width; if (new->core.height == 0) new->core.height = height; } } static void Destroy(Widget gw) { MessageWidget w = (MessageWidget)gw; free_gcs(w); XtFree(w->message.buffer); } static void Redisplay(Widget gw, XEvent *event, Region region) { MessageWidget w = (MessageWidget)gw; Display *disp = XtDisplay(w); Window win = XtWindow(w); char *c = w->message.buffer; int x, y, dy; if (!XtIsRealized((Widget)w) || !c) return; x = w->message.internal_width; y = w->message.internal_height + w->message.font->ascent; dy = w->message.font->descent + w->message.font->ascent; for(;;) { char *p = strchr(c, '\n'); int len; if (p) len = p - c; else len = strlen(c); if (w->message.center) x = ((int)w->core.width - XTextWidth(w->message.font, c, len)) / 2; XDrawString(disp, win, w->message.default_gc, x, y, c, len); y += dy; if (!p) break; c = p + 1; } } static Boolean SetValues(Widget gcurrent, Widget grequest, Widget gnew, ArgList args, Cardinal *num_args) { MessageWidget new = (MessageWidget)gnew; MessageWidget current = (MessageWidget)gcurrent; Boolean redisplay = False; char *new_buffer = new->message.buffer; char *old_buffer = current->message.buffer; if (new_buffer != old_buffer) { new->message.buffer = old_buffer; copy_message(new, new_buffer); redisplay = True; } if (redisplay) { Dimension width, height; get_preferred_sizes(new, &width, &height); new->core.width = width; new->core.height = height; } if (new->message.font != current->message.font) redisplay = True; return redisplay; } static XtGeometryResult QueryGeometry(Widget gw, XtWidgetGeometry *intended, XtWidgetGeometry *preferred) { MessageWidget w = (MessageWidget)gw; Dimension width, height; Dimension intended_height, intended_width; get_preferred_sizes(w, &width, &height); preferred->request_mode = CWWidth | CWHeight; preferred->width = width; preferred->height = height; if (intended->request_mode & CWWidth) intended_width = intended->width; else intended_width = w->core.width; if (intended->request_mode & CWHeight) intended_height = intended->height; else intended_height = w->core.height; if (width == w->core.width && height == w->core.height) return XtGeometryNo; else if (width == intended_width && height == intended_height) return XtGeometryYes; else return XtGeometryAlmost; } /*************************************************************************/ void MessageSetAndRedraw(Widget gw, char *buffer, int bell) { MessageWidget w = (MessageWidget)gw; Display *disp = XtDisplay(w); Window win = XtWindow(w); copy_message(w, buffer); XClearWindow(disp, win); Redisplay((Widget)w, NULL, NULL); if (bell) XBell(disp, 0); XFlush(disp); } ./knews-1.0b.1/Widgets/Message.h100644 1244 1244 1473 6455455536 15007 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #ifndef Message_h #define Message_h #ifndef XtCBuffer #define XtCBuffer "Buffer" #endif #ifndef XtCCenter #define XtCCenter "Center" #endif #ifndef XtCInternalHeight #define XtCInternalHeight "InternalHeight" #endif #ifndef XtCInternalWidth #define XtCInternalWidth "InternalWidth" #endif #ifndef XtCPreferredChars #define XtCPreferredChars "PreferredChars" #endif #ifndef XtNbuffer #define XtNbuffer "buffer" #endif #ifndef XtNcenter #define XtNcenter "center" #endif #ifndef XtNpreferredChars #define XtNpreferredChars "preferredChars" #endif typedef struct MessageClassRec* MessageWidgetClass; typedef struct MessageRec* MessageWidget; extern WidgetClass messageWidgetClass; extern void MessageSetAndRedraw(Widget, char*, int); #endif /* Message_h */ ./knews-1.0b.1/Widgets/ScrList.c100644 1244 1244 100605 6455455536 15036 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include #include #include #include #include "Compat.h" #include "ScrListP.h" #include "Util.h" #define FIRST(w) ((w)->scrollable.pos_y) #define SHOWN(w) ((w)->scrollable.shown_y) #define LINES(w) ((w)->scrollable.height) static void grey90_default_proc(Widget, int, XrmValue*); static XtResource resources[] = { #define offset(field) XtOffsetOf(ScrListRec, scrlist.field) {XtNshadowWidth, XtCShadowWidth, XtRDimension, sizeof(Dimension), XtOffsetOf(ShadowRec, shadow.shadow_width), XtRImmediate, (XtPointer)1}, {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel), offset(foreground_pixel), XtRString, XtDefaultForeground}, {XtNfont, XtCFont, XtRFontStruct, sizeof(XFontStruct *), offset(font), XtRString, XtDefaultFont}, {XtNhighlightColor, XtCHighlightColor, XtRPixel, sizeof(Pixel), offset(highlight_pixel), XtRCallProc, (XtPointer)grey90_default_proc}, {XtNrowSpacing, XtCRowSpacing, XtRDimension, sizeof(Dimension), offset(row_spacing), XtRImmediate, (XtPointer)0}, {XtNnAlloc, XtCNAlloc, XtRLong, sizeof(long), offset(n_alloc), XtRImmediate, (XtPointer)0}, {XtNinternalWidth, XtCInternalWidth, XtRDimension, sizeof(Dimension), offset(internal_width), XtRImmediate, (XtPointer)8}, {XtNinternalHeight, XtCInternalHeight, XtRDimension, sizeof(Dimension), offset(internal_height), XtRImmediate, (XtPointer)4}, {XtNinternalItemWidth, XtCInternalItemWidth, XtRDimension, sizeof(Dimension), offset(internal_item_width), XtRImmediate, (XtPointer)8}, {XtNinternalItemHeight, XtCInternalItemHeight, XtRDimension, sizeof(Dimension), offset(internal_item_height), XtRImmediate, (XtPointer)1}, {XtNpixmapWidth, XtCPixmapWidth, XtRDimension, sizeof(Dimension), offset(pixmap_width), XtRImmediate, (XtPointer)0}, {XtNpixmapHeight, XtCPixmapHeight, XtRDimension, sizeof(Dimension), offset(pixmap_height), XtRImmediate, (XtPointer)0}, {XtNpixmapSpacing, XtCPixmapSpacing, XtRDimension, sizeof(Dimension), offset(pixmap_spacing), XtRImmediate, (XtPointer)8}, {XtNpreferredLines, XtCPreferredLines, XtRDimension, sizeof(Dimension), offset(preferred_lines), XtRImmediate, (XtPointer)12}, {XtNpreferredColumns, XtCPreferredColumns, XtRDimension, sizeof(Dimension), offset(preferred_columns), XtRImmediate, (XtPointer)80}, {XtNdepthOne, XtCDepthOne, XtRBoolean, sizeof(Boolean), offset(depth_one), XtRImmediate, (XtPointer)True}, {XtNatLeastOne, XtCAtLeastOne, XtRBoolean, sizeof(Boolean), offset(at_least_one), XtRImmediate, (XtPointer)False}, {XtNatMostOne, XtCAtMostOne, XtRBoolean, sizeof(Boolean), offset(at_most_one), XtRImmediate, (XtPointer)True}, {XtNallowDnd, XtCAllowDnd, XtRBoolean, sizeof(Boolean), offset(allow_dnd), XtRImmediate, (XtPointer)False}, {XtNusePixmaps, XtCUsePixmaps, XtRBoolean, sizeof(Boolean), offset(use_pixmaps), XtRImmediate, (XtPointer)False}, {XtNselectCallback, XtCCallback, XtRCallback, sizeof(XtCallbackList), offset(select_callback), XtRCallback, (XtPointer)NULL}, {XtNcallback, XtCCallback, XtRCallback, sizeof(XtCallbackList), offset(callback), XtRCallback, (XtPointer)NULL}, {XtNsecondCallback, XtCCallback, XtRCallback, sizeof(XtCallbackList), offset(second_callback), XtRCallback, (XtPointer)NULL}, {XtNdndCallback, XtCCallback, XtRCallback, sizeof(XtCallbackList), offset(dnd_callback), XtRCallback, (XtPointer)NULL}, {XtNdndCursor, XtCCursor, XtRCursor, sizeof(Cursor), offset(dnd_cursor), XtRString, (XtPointer)"hand2"}, {XtNmarginUp, XtCMargin, XtRDimension, sizeof(Dimension), offset(margin_up), XtRImmediate, (XtPointer)1}, {XtNmarginDown, XtCMargin, XtRDimension, sizeof(Dimension), offset(margin_down), XtRImmediate, (XtPointer)1}, {XtNpageUp, XtCPage, XtRBoolean, sizeof(Boolean), offset(page_up), XtRImmediate, (XtPointer)True}, {XtNpageDown, XtCPage, XtRBoolean, sizeof(Boolean), offset(page_down), XtRImmediate, (XtPointer)True}, #undef offset }; static void Initialize(Widget, Widget, ArgList, Cardinal*); static void Destroy(Widget); static void Redisplay(Widget, XEvent*, Region); static void Resize(Widget); static void Realize(Widget, XtValueMask*, XSetWindowAttributes*); static Boolean SetValues(Widget, Widget, Widget, ArgList, Cardinal*); static void SetVPos(ScrollableWidget, long); static XtGeometryResult QueryGeometry(Widget, XtWidgetGeometry*, XtWidgetGeometry*); static void notify(Widget, XEvent*, String*, Cardinal*); static void notify_second(Widget, XEvent*, String*, Cardinal*); static void select_action(Widget, XEvent*, String*, Cardinal*); static void drag_select(Widget, XEvent*, String*, Cardinal*); static void dnd_start(Widget, XEvent*, String*, Cardinal*); static void dnd_do(Widget, XEvent*, String*, Cardinal*); static void dnd_end(Widget, XEvent*, String*, Cardinal*); static XtActionsRec actions[] = { {"notify", notify}, {"notify-second", notify_second}, {"select", select_action}, {"drag-select", drag_select}, {"dnd-start", dnd_start}, {"dnd-do", dnd_do}, {"dnd-end", dnd_end}, }; static char translations[] = ": select() \n" "(2): notify() \n"; ScrListClassRec scrListClassRec = { { /* core_class fields */ (WidgetClass) &scrollableClassRec, /* superclass */ "ScrList", /* class_name */ sizeof(ScrListRec), /* widget_size */ NULL, /* class_initialize */ NULL, /* class_part_initialize */ FALSE, /* class_inited */ Initialize, /* initialize */ NULL, /* initialize_hook */ Realize, /* realize */ actions, /* actions */ XtNumber(actions), /* num_actions */ resources, /* resources */ XtNumber(resources), /* num_resources */ NULLQUARK, /* xrm_class */ TRUE, /* compress_motion */ #if (XtSpecificationRelease < 4) /* It really needs GraphicsExpose ... */ True, /* compress_exposure */ #elif (XtSpecificationRelease < 6) XtExposeCompressMaximal | XtExposeGraphicsExposeMerged, /* compress_exposure */ #else XtExposeCompressMaximal | XtExposeGraphicsExposeMerged | XtExposeNoRegion, /* compress_exposure */ #endif TRUE, /* compress_enterleave */ FALSE, /* visible_interest */ Destroy, /* destroy */ Resize, /* resize */ Redisplay, /* expose */ SetValues, /* set_values */ NULL, /* set_values_hook */ XtInheritSetValuesAlmost, /* set_values_almost */ NULL, /* get_values_hook */ NULL, /* accept_focus */ XtVersion, /* version */ NULL, /* callback_private */ translations, /* tm_table */ QueryGeometry, /* query_geometry */ XtInheritDisplayAccelerator, /* display_accelerator */ NULL /* extension */ }, { /* shadow fields */ XtOffsetOf(ScrListRec, scrlist.highlight_pixel), /* pixel_offset */ True, /* use_arm_for_background */ XtInheritAllocShadowColors, /* alloc_shadow_colors */ XtInheritAllocShadowPixmaps, /* alloc_shadow_pixmaps */ NULL, /* alloc_arm_color */ NULL, /* alloc_arm_pixmap */ XtInheritAllocGCs, /* alloc_gcs */ NULL, /* extension */ }, { /* scrollable fields */ XtInheritScrollableSetPos, /* set_hpos */ SetVPos, /* set_vpos */ NULL, /* suspend_hook */ NULL, /* extension */ }, { /* scrlist fields */ NULL /* extension */ } }; WidgetClass scrListWidgetClass = (WidgetClass)&scrListClassRec; /*************************************************************************/ static void grey90_default_proc(Widget w, int offset, XrmValue *to) { static Pixel grey90; Display *disp = XtDisplay(w); grey90 = w->core.background_pixel; if (w->core.depth != 1) { XColor col; col.red = col.green = col.blue = 0xe5e5; if (XAllocColor(disp, w->core.colormap, &col)) if ((col.red == 0 && col.green == 0 && col.blue == 0) || (col.red == 0xffff && col.green == 0xffff && col.blue == 0xffff)) XFreeColors(disp, w->core.colormap, &col.pixel, 1, 0); else grey90 = col.pixel; } to->addr = (XPointer)&grey90; } static void init_gcs(ScrListWidget w) { XGCValues values; values.foreground = w->scrlist.foreground_pixel; values.background = w->core.background_pixel; values.font = w->scrlist.font->fid; w->scrlist.default_gc = XtGetGC((Widget)w, GCForeground | GCBackground | GCFont, &values); values.background = w->scrlist.highlight_pixel; w->scrlist.selected_gc = XtGetGC((Widget)w, GCForeground | GCBackground, &values); if (w->shadow.line_mode) w->scrlist.highlight_gc = (GC)0; else { values.foreground = w->scrlist.highlight_pixel; w->scrlist.highlight_gc = XtGetGC((Widget)w, GCForeground, &values); } } static void free_gcs(ScrListWidget w) { XtReleaseGC((Widget)w, w->scrlist.default_gc); XtReleaseGC((Widget)w, w->scrlist.highlight_gc); XtReleaseGC((Widget)w, w->scrlist.selected_gc); } static void alloc_list(ScrListWidget w, long n_alloc) { long n = w->scrlist.n_alloc; int use_pixmaps = w->scrlist.use_pixmaps; if (n_alloc < LINES(w)) n_alloc = LINES(w); w->scrlist.strings = (char **)XtRealloc((char *)w->scrlist.strings, n_alloc * sizeof(char*)); w->scrlist.selected = (Boolean *)XtRealloc((char *)w->scrlist.selected, n_alloc * sizeof(Boolean)); if (use_pixmaps) w->scrlist.pixmaps = (Pixmap *)XtRealloc((char *)w->scrlist.pixmaps, n_alloc * sizeof(Pixmap)); w->scrlist.n_alloc = n_alloc; while (n < n_alloc) { w->scrlist.strings[n] = NULL; w->scrlist.selected[n] = False; if (use_pixmaps) w->scrlist.pixmaps[n] = None; n++; } } static void calc_shown(ScrListWidget w) { int h, dh = 0; h = w->core.height - 2 * w->scrlist.internal_height + w->scrlist.row_spacing; dh = w->scrlist.font->ascent + w->scrlist.font->descent; if (w->scrlist.use_pixmaps && (int)w->scrlist.pixmap_height > dh) dh = w->scrlist.pixmap_height; dh += w->scrlist.row_spacing + 2 * (w->scrlist.internal_item_height + w->shadow.shadow_width); SHOWN(w) = (dh <= 0) ? 0 : h/dh; } static void draw_list_item(ScrListWidget w, String *string, Pixmap *pixmap, Boolean *selected, int x, int y, int width, int height) { Display *disp = XtDisplay(w); Window win = XtWindow(w); int sw = w->shadow.shadow_width; if (*selected) { ShadowDrawShadows((ShadowWidget)w, (Position)x, (Position)y, (Dimension)width, (Dimension)height, False); if (!w->shadow.line_mode) XFillRectangle(disp, win, w->scrlist.highlight_gc, x + sw, y + sw, width - 2*sw, height - 2*sw); } x += w->scrlist.internal_item_width + w->shadow.shadow_width; if (pixmap) { if (*pixmap != None) { if (w->scrlist.depth_one) XCopyPlane(disp, *pixmap, win, *selected ? w->scrlist.selected_gc : w->scrlist.default_gc, 0, 0, w->scrlist.pixmap_width, w->scrlist.pixmap_height, x, y + (height - (int)w->scrlist.pixmap_height) / 2, 1); else XCopyArea(disp, *pixmap, win, w->scrlist.default_gc, 0, 0, w->scrlist.pixmap_width, w->scrlist.pixmap_height, x, y + (height - (int)w->scrlist.pixmap_height) / 2); } x += w->scrlist.pixmap_width + w->scrlist.pixmap_spacing; } if (string && *string != (String)NULL) XDrawString(disp, win, w->scrlist.default_gc, x, y + (height + w->scrlist.font->ascent - w->scrlist.font->descent)/2, *string, strlen(*string)); } static void redraw_items(ScrListWidget w, long start, long stop, Boolean clear) { String *string = w->scrlist.strings; Pixmap *pixmap = w->scrlist.pixmaps; int x = w->scrlist.internal_width; int width = w->core.width - 2 * w->scrlist.internal_width; int y = w->scrlist.internal_height; int height = 0; if (width <= 0 || LINES(w) == 0) return; if (start < FIRST(w)) start = FIRST(w); if (stop >= FIRST(w) + SHOWN(w)) stop = FIRST(w) + SHOWN(w) - 1; if (stop >= LINES(w) - 1) stop = LINES(w) - 1; if (start > stop) return; if (string) string += start; if (pixmap) pixmap += start; if (string) height = w->scrlist.font->ascent + w->scrlist.font->descent; if (pixmap && (int)w->scrlist.pixmap_height > height) height = w->scrlist.pixmap_height; height += 2 * (w->scrlist.internal_item_height + w->shadow.shadow_width); if (width <= 0 || height == 0) return; y += (start - FIRST(w)) * (height + w->scrlist.row_spacing); if (clear) { int h = (stop - start + 1) * (height + w->scrlist.row_spacing); XClearArea(XtDisplay(w), XtWindow(w), 0, y, w->core.width, h, False); } while (start <= stop) { draw_list_item(w, string, pixmap, &(w->scrlist.selected[start]), x, y, width, height); y += height + w->scrlist.row_spacing; if (string) string++; if (pixmap) pixmap++; start++; } } static Dimension preferred_width(ScrListWidget w) { int ret; ret = w->scrlist.max_width; if (ret <= 0) ret = w->scrlist.preferred_columns * w->scrlist.font->max_bounds.width; ret += 2 * (w->shadow.shadow_width + w->scrlist.internal_width + w->scrlist.internal_item_width); if (w->scrlist.use_pixmaps) ret += w->scrlist.pixmap_width + w->scrlist.pixmap_spacing; return ret; } static Dimension preferred_height(ScrListWidget w) { long height = 0; long n = w->scrlist.preferred_lines; if (w->scrlist.strings || !w->scrlist.pixmaps) height = w->scrlist.font->ascent + w->scrlist.font->descent; if (w->scrlist.pixmaps && height < (int)w->scrlist.pixmap_height) height = w->scrlist.pixmap_height; height += 2 * (w->scrlist.internal_item_height + w->shadow.shadow_width + w->scrlist.row_spacing); height *= n; height += 2 * w->scrlist.internal_height - w->scrlist.row_spacing; if (height > 32767) height = 32767; else if (height <= 0) height = 1; return height; } static void make_visible(ScrListWidget w, long i, int may_page) { long first = FIRST(w); if (LINES(w) <= 0) return; if (i >= LINES(w)) i = LINES(w) - 1; if (i < 0) i = 0; if (i < FIRST(w) + w->scrlist.margin_up) if (may_page && w->scrlist.page_up) first = i - SHOWN(w) + w->scrlist.margin_down + 1; else first = i - w->scrlist.margin_up; else if (i >= FIRST(w) + SHOWN(w) - w->scrlist.margin_down) if (may_page && w->scrlist.page_down) first = i - w->scrlist.margin_up; else first = i - SHOWN(w) + w->scrlist.margin_down + 1; if (i < first) i = first; else if (i >= first + SHOWN(w)) first = i - SHOWN(w) + 1; if (first < 0) first = 0; if (first != FIRST(w)) ScrollableSetVPos((Widget)w, first); } /*************************************************************************/ static long event_to_index(ScrListWidget w, XEvent *event) { int ex, ey; int y, h = 0; long i, n; if (!get_event_xy(event, &ex, &ey)) { XBell(XtDisplay(w), 0); return -1; } if (LINES(w) <= 0 || ex < (int)w->scrlist.internal_width || ex > (int)(w->core.width - w->scrlist.internal_width)) return -1; y = w->scrlist.internal_height; if (w->scrlist.strings) h = w->scrlist.font->ascent + w->scrlist.font->descent; if (w->scrlist.pixmaps && h < (int)w->scrlist.pixmap_height) h = w->scrlist.pixmap_height; h += 2 * (w->scrlist.internal_item_height + w->shadow.shadow_width); if (ey < y) return FIRST(w) > 0 ? FIRST(w) - 1 : 0; i = FIRST(w); n = FIRST(w) + SHOWN(w); if (n > LINES(w)) n = LINES(w); while (i < n) { if (y <= ey && ey < y + h) return i; y += h + w->scrlist.row_spacing; i++; } n = FIRST(w) + SHOWN(w); if (n >= LINES(w)) n = LINES(w) - 1; return n; } static void select_action(Widget gw, XEvent *event, String *params, Cardinal *no_params) { ScrListWidget w = (ScrListWidget)gw; XtCallbackList c_list = w->scrlist.select_callback; long i; if (!w->scrlist.active) return; i = event_to_index(w, event); if (i < 0 || i >= LINES(w)) return; ScrListSetSelected((Widget)w, i, !w->scrlist.selected[i]); if (c_list) XtCallCallbackList((Widget)w, c_list, (XtPointer)i); } static void notify(Widget gw, XEvent *event, String *params, Cardinal *no_params) { ScrListWidget w = (ScrListWidget)gw; XtCallbackList c_list = w->scrlist.callback; long i; if (!w->scrlist.active || !c_list) return; i = event_to_index(w, event); if (i < 0 || i >= LINES(w)) return; XtCallCallbackList((Widget)w, c_list, (XtPointer)i); } static void notify_second(Widget gw, XEvent *event, String *params, Cardinal *no_params) { ScrListWidget w = (ScrListWidget)gw; XtCallbackList c_list = w->scrlist.second_callback; long i; if (!w->scrlist.active || !c_list) return; i = event_to_index(w, event); if (i < 0 || i >= LINES(w)) return; XtCallCallbackList((Widget)w, c_list, (XtPointer)i); } static void drag_select(Widget gw, XEvent *event, String *params, Cardinal *no_params) { ScrListWidget w = (ScrListWidget)gw; XtCallbackList c_list = w->scrlist.select_callback; long i; if (!w->scrlist.active) return; i = event_to_index(w, event); if (i < 0 || i >= LINES(w)) return; if (!w->scrlist.selected[i]) { ScrListSetSelected((Widget)w, i, True); if (c_list) XtCallCallbackList((Widget)w, c_list, (XtPointer)i); } } static void dnd_start(Widget gw, XEvent *event, String *params, Cardinal *no_params) { ScrListWidget w = (ScrListWidget)gw; long i = event_to_index(w, event); if (!w->scrlist.allow_dnd || i < 0) { w->scrlist.dnd_start = -1; XBell(XtDisplay(w), 0); return; } w->scrlist.dnd_start = i; XDefineCursor(XtDisplay(w), XtWindow(w), w->scrlist.dnd_cursor); } static void dnd_do(Widget gw, XEvent *event, String *params, Cardinal *no_params) { ScrListWidget w = (ScrListWidget)gw; long i = event_to_index(w, event); if (w->scrlist.dnd_start < 0) { XBell(XtDisplay(w), 0); return; } if (LINES(w) <= 0) return; if (i >= LINES(w)) i = LINES(w) - 1; if (i < 0) i = 0; make_visible(w, i, False); } static void dnd_end(Widget gw, XEvent *event, String *params, Cardinal *no_params) { ScrListWidget w = (ScrListWidget)gw; XtCallbackList c_list = w->scrlist.dnd_callback; long index[3]; long i; int use_pixmaps = w->scrlist.use_pixmaps; char *tempstr = NULL; Pixmap temppix = None; Boolean tempsel = False; XDefineCursor(XtDisplay(w), XtWindow(w), None); index[0] = w->scrlist.dnd_start; w->scrlist.dnd_start = -1; index[1] = event_to_index(w, event); if (!w->scrlist.allow_dnd || index[0] == index[1] || index[0] < 0 || index[0] >= LINES(w) || index[1] < 0 || index[1] >= LINES(w)) { XBell(XtDisplay((Widget)w), 0); return; } if (c_list) { index[2] = False; XtCallCallbackList((Widget)w, c_list, (XtPointer)index); if (!index[2]) return; } tempstr = w->scrlist.strings[index[0]]; if (use_pixmaps) temppix = w->scrlist.pixmaps[index[0]]; tempsel = w->scrlist.selected[index[0]]; if (index[0] < index[1]) for (i = index[0] ; i < index[1] ; i++) { w->scrlist.strings[i] = w->scrlist.strings[i + 1]; if (use_pixmaps) w->scrlist.pixmaps[i] = w->scrlist.pixmaps[i + 1]; w->scrlist.selected[i] = w->scrlist.selected[i + 1]; } else for (i = index[0] ; i > index[1] ; i--) { w->scrlist.strings[i] = w->scrlist.strings[i - 1]; if (use_pixmaps) w->scrlist.pixmaps[i] = w->scrlist.pixmaps[i - 1]; w->scrlist.selected[i] = w->scrlist.selected[i - 1]; } w->scrlist.strings[index[1]] = tempstr; if (use_pixmaps) w->scrlist.pixmaps[index[1]] = temppix; w->scrlist.selected[index[1]] = tempsel; if (index[0] < index[1]) redraw_items(w, index[0], index[1], True); else redraw_items(w, index[1], index[0], True); } /*************************************************************************/ static void Initialize(Widget grequest, Widget gnew, ArgList args, Cardinal *num_args) { ScrListWidget new = (ScrListWidget)gnew; long n_alloc = new->scrlist.n_alloc; new->scrlist.active = True; new->scrlist.strings = NULL; new->scrlist.pixmaps = NULL; new->scrlist.selected = NULL; new->scrlist.n_sel = 0; new->scrlist.n_alloc = 0; new->scrlist.max_width = 0; init_gcs(new); calc_shown(new); alloc_list(new, n_alloc > 0 ? n_alloc : 1); new->scrlist.dnd_start = -1; if (new->scrlist.at_least_one) { new->scrlist.selected[0] = True; new->scrlist.n_sel = 1; } if (new->core.width == 0) new->core.width = preferred_width(new); if (new->core.height == 0) new->core.height = preferred_height(new); } static void Destroy(Widget gw) { ScrListWidget w = (ScrListWidget)gw; XtFree(w->scrlist.selected); free_gcs(w); } static void Redisplay(Widget gw, XEvent *event, Region region) { ScrListWidget w = (ScrListWidget)gw; long start, stop; int height = 0; if (!XtIsRealized((Widget)w) || LINES(w) == 0) return; if (w->scrlist.strings) height = w->scrlist.font->ascent + w->scrlist.font->descent; if (w->scrlist.use_pixmaps && (int)w->scrlist.pixmap_height > height) height = w->scrlist.pixmap_height; height += 2 * (w->scrlist.internal_item_height + w->shadow.shadow_width) + w->scrlist.row_spacing; if (height == 0) return; if (event && event->type == Expose) { int y = w->scrlist.internal_height; int y1 = event->xexpose.y - y; int y2 = y1 + event->xexpose.height; start = FIRST(w) + y1/height; stop = FIRST(w) + y2/height; } else if (event && event->type == GraphicsExpose) { int y = w->scrlist.internal_height; int y1 = event->xgraphicsexpose.y - y; int y2 = y1 + event->xgraphicsexpose.height; start = FIRST(w) + y1/height; stop = FIRST(w) + y2/height; } else { start = FIRST(w); stop = start + SHOWN(w) - 1; } redraw_items(w, start, stop, False); } static void Resize(Widget gw) { ScrListWidget w = (ScrListWidget)gw; ScrollableHFromGeometry((ScrollableWidget)w); calc_shown(w); ScrollableFitHBar((ScrollableWidget)w); ScrollableFitVBar((ScrollableWidget)w); } static void Realize(Widget gw, XtValueMask *mask, XSetWindowAttributes *attributes) { ScrListWidget w = (ScrListWidget)gw; ScrollableHFromGeometry((ScrollableWidget)w); calc_shown(w); scrollableWidgetClass->core_class.realize((Widget)w, mask, attributes); } static Boolean SetValues(Widget gcurrent, Widget grequest, Widget gnew, ArgList args, Cardinal *num_args) { ScrListWidget new = (ScrListWidget)gnew; ScrListWidget current = (ScrListWidget)gcurrent; Boolean redisplay = False; new->scrlist.use_pixmaps = current->scrlist.use_pixmaps; if (new->core.background_pixel != current->core.background_pixel || new->scrlist.foreground_pixel != current->scrlist.foreground_pixel || new->scrlist.highlight_pixel != current->scrlist.highlight_pixel || new->scrlist.font != current->scrlist.font) { free_gcs(current); init_gcs(new); redisplay = True; } if (new->scrlist.n_alloc != current->scrlist.n_alloc) { long n_alloc = new->scrlist.n_alloc; new->scrlist.n_alloc = current->scrlist.n_alloc; alloc_list(new, n_alloc); } if (new->scrlist.row_spacing != current->scrlist.row_spacing || new->shadow.shadow_width != current->shadow.shadow_width || new->scrlist.internal_width != current->scrlist.internal_width || new->scrlist.internal_height != current->scrlist.internal_height || new->scrlist.pixmap_width != current->scrlist.pixmap_width || new->scrlist.pixmap_height != current->scrlist.pixmap_height || new->scrlist.pixmap_spacing != current->scrlist.pixmap_spacing || new->scrlist.depth_one != current->scrlist.depth_one) redisplay = True; if (new->scrlist.internal_item_width != current->scrlist.internal_item_width || new->scrlist.internal_item_height != current->scrlist.internal_item_height) redisplay = True; if (new->scrlist.at_least_one != current->scrlist.at_least_one && new->scrlist.n_sel == 0 && LINES(new) > 0) { new->scrlist.selected[0] = True; redisplay = True; } if (new->scrlist.at_most_one != current->scrlist.at_most_one && new->scrlist.n_sel > 1) { Boolean *loop = new->scrlist.selected; int n = LINES(new); while (n-- > 0) if (*loop++) break; if (n > 0) memset(loop, 0, n); } if (new->scrlist.preferred_lines != current->scrlist.preferred_lines) (void)XtMakeResizeRequest((Widget)new, preferred_width(new), preferred_height(new), NULL, NULL); if (redisplay) calc_shown(new); return redisplay; } static void SetVPos(ScrollableWidget gw, long pos_y) { ScrListWidget w = (ScrListWidget)gw; Display *disp = XtDisplay(w); Window win = XtWindow(w); long old = FIRST(w); long diff; long n = SHOWN(w); int y = w->scrlist.internal_height; int dy = 0; if (LINES(w) <= 0) return; if (pos_y > LINES(w) - SHOWN(w)) pos_y = LINES(w) - SHOWN(w); if (pos_y < 0) pos_y = 0; diff = pos_y - old; FIRST(w) = pos_y; if (w->scrlist.strings) dy = w->scrlist.font->ascent + w->scrlist.font->descent; if (w->scrlist.pixmaps && (int)w->scrlist.pixmap_height > dy) dy = w->scrlist.pixmap_height; dy += 2 * (w->scrlist.internal_item_height + w->shadow.shadow_width) + w->scrlist.row_spacing; if (dy == 0 || diff == 0 || !XtIsRealized((Widget)w)) return; if (diff <= -n || diff >= n) { XClearWindow(disp, win); redraw_items(w, pos_y, pos_y + n - 1, False); } else if (diff < 0) { XCopyArea(disp, win, win, w->scrlist.default_gc, 0, y, w->core.width, (n + diff) * dy, 0, y - diff * dy); XClearArea(disp, win, 0, 0, 0, y - diff * dy, False); redraw_items(w, pos_y, pos_y - diff - 1, False); } else if (diff > 0) { XCopyArea(disp, win, win, w->scrlist.default_gc, 0, y + diff * dy, w->core.width, (n - diff) * dy, 0, y); XClearArea(disp, win, 0, y + (n - diff) * dy, 0, 0, False); redraw_items(w, pos_y + n - diff, pos_y + n - 1, False); } } static XtGeometryResult QueryGeometry(Widget gw, XtWidgetGeometry *intended, XtWidgetGeometry *preferred) { ScrListWidget w = (ScrListWidget)gw; Dimension intended_width; Dimension intended_height; preferred->request_mode = CWHeight | CWWidth; preferred->height = preferred_height(w); preferred->width = preferred_width(w); if (intended->request_mode & CWWidth) intended_width = intended->width; else intended_width = w->core.width; if (intended->request_mode & CWHeight) intended_height = intended->height; else intended_height = w->core.height; if (intended_width == preferred->width && intended_height == preferred->height) return XtGeometryYes; if (preferred->width == w->core.width && preferred->height == w->core.height) return XtGeometryNo; return XtGeometryAlmost; } /*************************************************************************/ void ScrListClearLines(Widget gw) { ScrListWidget w = (ScrListWidget)gw; long i; for (i = 0 ; i < LINES(w) ; i++) { XtFree(w->scrlist.strings[i]); w->scrlist.strings[i] = NULL; w->scrlist.selected[i] = False; if (w->scrlist.pixmaps) w->scrlist.pixmaps[i] = None; } XtFree((char *)w->scrlist.strings); w->scrlist.strings = NULL; XtFree((char *)w->scrlist.pixmaps); w->scrlist.pixmaps = NULL; XtFree((char *)w->scrlist.selected); w->scrlist.selected = NULL; w->scrlist.n_alloc = 0; LINES(w) = 0; FIRST(w) = 0; w->scrlist.dnd_start = -1; w->scrlist.n_sel = 0; w->scrlist.max_width = 0; if (XtIsRealized((Widget)w)) XClearWindow(XtDisplay(w), XtWindow(w)); ScrollableFitVBar((ScrollableWidget)w); } long ScrListAddLine(Widget gw, char *string, Pixmap pixmap) { ScrListWidget w = (ScrListWidget)gw; long n = LINES(w)++; int len, width; if (n + 3 > w->scrlist.n_alloc) alloc_list(w, 2 * (w->scrlist.n_alloc + 1)); len = strlen(string); w->scrlist.strings[n] = strcpy(XtMalloc(len + 1), string); if (w->scrlist.use_pixmaps) w->scrlist.pixmaps[n] = pixmap; width = XTextWidth(w->scrlist.font, string, len); if (width > w->scrlist.max_width) w->scrlist.max_width = width; if (n == 0 && w->scrlist.at_least_one) { w->scrlist.selected[0] = True; w->scrlist.n_sel = 1; } if (XtIsRealized((Widget)w) && n >= FIRST(w) && n < FIRST(w) + SHOWN(w)) redraw_items(w, n, n, False); if (!w->scrollable.suspended) ScrollableFitVBar((ScrollableWidget)w); return n; } void ScrListSetLine(Widget gw, long row, char *string, Pixmap pixmap) { ScrListWidget w = (ScrListWidget)gw; int len, width; if (row < 0 || row >= LINES(w)) return; XtFree(w->scrlist.strings[row]); len = strlen(string); w->scrlist.strings[row] = strcpy(XtMalloc(len + 1), string); if (w->scrlist.use_pixmaps) w->scrlist.pixmaps[row] = pixmap; width = XTextWidth(w->scrlist.font, string, len); if (width > w->scrlist.max_width) w->scrlist.max_width = width; if (XtIsRealized((Widget)w)) redraw_items(w, row, row, True); } void ScrListDeleteLine(Widget gw, long row) { ScrListWidget w = (ScrListWidget)gw; int use_pixmaps = w->scrlist.use_pixmaps; int was_sel; long i, n; if (row < 0 || row >= LINES(w)) return; was_sel = w->scrlist.selected[row]; if (was_sel) w->scrlist.n_sel--; XtFree(w->scrlist.strings[row]); n = --LINES(w); for (i = row ; i < n ; i++) { w->scrlist.strings[i] = w->scrlist.strings[i + 1]; if (use_pixmaps) w->scrlist.pixmaps[i] = w->scrlist.pixmaps[i + 1]; w->scrlist.selected[i] = w->scrlist.selected[i + 1]; } w->scrlist.strings[n] = NULL; if (was_sel && w->scrlist.at_least_one && w->scrlist.n_sel == 0 && LINES(w) > 0) { if (row < LINES(w) - 1) w->scrlist.selected[row + 1] = True; else w->scrlist.selected[LINES(w) - 1] = True; w->scrlist.n_sel = 1; } if (FIRST(w) >= LINES(w) && LINES(w) > 0) FIRST(w)--; if (XtIsRealized((Widget)w)) { XClearWindow(XtDisplay(w), XtWindow(w)); Redisplay((Widget)w, NULL, NULL); } if (!w->scrollable.suspended) ScrollableFitVBar((ScrollableWidget)w); } void ScrListSetSelected(Widget gw, long i, int select) { ScrListWidget w = (ScrListWidget)gw; if (i < 0 || i >= LINES(w)) return; select = !!select; if (w->scrlist.selected[i] == select) return; if (select) { if (w->scrlist.at_most_one && w->scrlist.n_sel > 0) { long j; Boolean *sel = w->scrlist.selected; for (j = 0 ; j < LINES(w) ; j++) if (sel[j]) { sel[j] = False; redraw_items(w, j, j, True); } w->scrlist.n_sel = 0; } w->scrlist.n_sel++; w->scrlist.selected[i] = True; redraw_items(w, i, i, True); } else if (!w->scrlist.at_least_one || w->scrlist.n_sel > 1) { w->scrlist.n_sel--; w->scrlist.selected[i] = False; redraw_items(w, i, i, True); } } void ScrListMakeVisible(Widget gw, long i) { make_visible((ScrListWidget)gw, i, True); } int ScrListGetSelected(Widget gw, long i) { ScrListWidget w = (ScrListWidget)gw; return i >= 0 && i < LINES(w) && w->scrlist.selected[i]; } long ScrListGetFirstSelected(Widget gw) { return ScrListGetNextSelected(gw, -1); } long ScrListGetNextSelected(Widget gw, long i) { ScrListWidget w = (ScrListWidget)gw; Boolean *loop = w->scrlist.selected; long n = LINES(w); i++; if (i < 0) return -1; loop += i; while (i < n) { if (*loop) return i; loop++; i++; } return -1; } char *ScrListGetString(Widget gw, long row) { ScrListWidget w = (ScrListWidget)gw; if (row < 0 || row >= LINES(w)) return NULL; return w->scrlist.strings[row]; } Pixmap ScrListGetPixmap(Widget gw, long row) { ScrListWidget w = (ScrListWidget)gw; if (!w->scrlist.use_pixmaps || row < 0 || row >= LINES(w)) return None; return w->scrlist.pixmaps[row]; } void ScrListPurgePixmap(Widget gw, Pixmap pixmap) { ScrListWidget w = (ScrListWidget)gw; long i; if (!w->scrlist.use_pixmaps) return; for (i = 0 ; i < LINES(w) ; i++) { if (w->scrlist.pixmaps[i] == pixmap) { w->scrlist.pixmaps[i] = None; redraw_items(w, i, i, True); } } } long ScrListEventToIndex(Widget gw, XEvent *event) { ScrListWidget w = (ScrListWidget)gw; return event_to_index(w, event); } void ScrListSetActive(Widget gw, int active) { ScrListWidget w = (ScrListWidget)gw; w->scrlist.active = active; } ./knews-1.0b.1/Widgets/MessageP.h100644 1244 1244 1415 6455455536 15123 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #ifndef MessageP_h #define MessageP_h #include "Message.h" #include typedef struct { XtPointer extension; } MessageClassPart; typedef struct MessageClassRec { CoreClassPart core_class; MessageClassPart message_class; } MessageClassRec; extern MessageClassRec messageClassRec; typedef struct { Pixel foreground_pixel; XFontStruct *font; String buffer; Dimension internal_height; Dimension internal_width; Dimension pref_chars; Boolean center; /* private data */ Cardinal rows; Cardinal n_alloc; GC default_gc; } MessagePart; typedef struct MessageRec { CorePart core; MessagePart message; } MessageRec; #endif /* MessageP_h */ ./knews-1.0b.1/Widgets/Notice.h100644 1244 1244 2330 6455455536 14635 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #ifndef Notice_h #define Notice_h #ifndef XtCMessage #define XtCMessage "Message" #endif #ifndef XtCLabel #define XtCLabel "Label" #endif #ifndef XtCTimeout #define XtCTimeout "Timeout" #endif #ifndef XtCWidget #define XtCWidget "Widget" #endif #ifndef XtNmessage #define XtNmessage "message" #endif #ifndef XtNleftLabel #define XtNleftLabel "leftLabel" #endif #ifndef XtNleftKnapp #define XtNleftKnapp "leftKnapp" #endif #ifndef XtNmiddleLabel #define XtNmiddleLabel "middleLabel" #endif #ifndef XtNmiddleKnapp #define XtNmiddleKnapp "middleKnapp" #endif #ifndef XtNrightLabel #define XtNrightLabel "rightLabel" #endif #ifndef XtNrightKnapp #define XtNrightKnapp "rightKnapp" #endif #ifndef XtNtimeout #define XtNtimeout "timeout" #endif typedef struct NoticeClassRec* NoticeWidgetClass; typedef struct NoticeRec* NoticeWidget; extern WidgetClass noticeWidgetClass; typedef enum { NoticeReplyLeft, NoticeReplyMiddle, NoticeReplyRight, NoticeReplyTimeout, NoticeReplyClose } NoticeReply; extern void NoticeSetMessage(Widget, String); extern void NoticeSetLeftLabel(Widget, String); extern Widget NoticeMessageWidget(Widget); #endif /* Notice_h */ ./knews-1.0b.1/Widgets/NoticeP.h100644 1244 1244 2335 6455455536 14762 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #ifndef NoticeP_h #define NoticeP_h #include "Notice.h" #include "CloseShP.h" typedef struct { XtPointer empty; } NoticeClassPart; typedef struct NoticeClassRec { CoreClassPart core_class; CompositeClassPart composite_class; ShellClassPart shell_class; WMShellClassPart wm_shell_class; VendorShellClassPart vendor_shell_class; TransientShellClassPart transient_shell_class; CloseShellClassPart close_shell; NoticeClassPart notice_class; } NoticeClassRec; extern NoticeClassRec noticeClassRec; typedef struct { XtCallbackList callback; String message; String left_label; String middle_label; String right_label; long timeout; /* read only */ Widget left_knapp; Widget middle_knapp; Widget right_knapp; /* private data */ Widget layout; Widget message_widget; XtIntervalId timer; } NoticePart; typedef struct NoticeRec { CorePart core; CompositePart composite; ShellPart shell; WMShellPart wm; VendorShellPart vendor; TransientShellPart transient; CloseShellPart close_shell; NoticePart notice; } NoticeRec; #endif /* NoticeP_h */ ./knews-1.0b.1/Widgets/MenuShell.h100644 1244 1244 417 6455455537 15275 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #ifndef MenuShell_h #define MenuShell_h typedef struct MenuShellClassRec* MenuShellWidgetClass; typedef struct MenuShellRec* MenuShellWidget; extern WidgetClass menuShellWidgetClass; #endif /* MenuShell_h */ ./knews-1.0b.1/Widgets/MenuShellP.h100644 1244 1244 1413 6455455537 15432 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #ifndef MenuShellP_h #define MenuShellP_h #include "MenuShell.h" #include typedef struct { XtPointer extension; } MenuShellClassPart; typedef struct MenuShellClassRec { CoreClassPart core_class; CompositeClassPart composite_class; ShellClassPart shell_class; OverrideShellClassPart override_shell_class; MenuShellClassPart menu_shell_class; } MenuShellClassRec; extern MenuShellClassRec menuShellClassRec; typedef struct { XtPointer extension; } MenuShellPart; typedef struct MenuShellRec { CorePart core; CompositePart composite; ShellPart shell; OverrideShellPart override; MenuShellPart menu_shell; } MenuShellRec; #endif /* MenuShellP_h */ ./knews-1.0b.1/Widgets/PullRight.c100644 1244 1244 24111 6455455537 15343 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include #include #include #include "Compat.h" #include "MenuI.h" #include "PullRightP.h" static XtResource resources[] = { #define offset(field) XtOffsetOf(PullRightGadgetRec, pull_right.field) {XtNmenuName, XtCMenuName, XtRString, sizeof(String), offset(menu_name), XtRImmediate, (XtPointer)NULL}, {XtNarrowSize, XtCArrowSize, XtRDimension, sizeof(Dimension), offset(arrow_size), XtRImmediate, (XtPointer)6}, {XtNarrowOffset, XtCArrowOffset, XtRDimension, sizeof(Dimension), offset(arrow_offset), XtRImmediate, (XtPointer)8}, {XtNarrowShadowWidth, XtCShadowWidth, XtRDimension, sizeof(Dimension), offset(arrow_shadow_width), XtRImmediate, (XtPointer)1}, #undef offset }; static void Initialize(Widget, Widget, ArgList, Cardinal*); static void Destroy(Widget); static Boolean SetValues(Widget, Widget, Widget, ArgList, Cardinal*); static void Redisplay(Widget, XEvent*, Region); static void ChangeHl(MenuGadget); static void Popdown(MenuGadget); static int Notify(MenuGadget); static int PostNotify(MenuGadget); static void SetActive(MenuGadget, int); PullRightGadgetClassRec pullRightGadgetClassRec = { { /* rectObj fields */ (WidgetClass) &stringGadgetClassRec, /* superclass */ "PullRightGadget", /* class_name */ sizeof(PullRightGadgetRec), /* widget_size */ NULL, /* class_initialize */ NULL, /* class_part_initialize */ FALSE, /* class_inited */ Initialize, /* initialize */ NULL, /* initialize_hook */ NULL, /* rect1 */ NULL, /* rect2 */ 0, /* rect3 */ resources, /* resources */ XtNumber(resources), /* num_resources */ NULLQUARK, /* xrm_class */ FALSE, /* rect4 */ FALSE, /* rect5 */ FALSE, /* rect6 */ FALSE, /* rect7 */ Destroy, /* destroy */ NULL, /* resize */ Redisplay, /* expose */ SetValues, /* set_values */ NULL, /* set_values_hook */ XtInheritSetValuesAlmost, /* set_values_almost */ NULL, /* get_values_hook */ NULL, /* rect9 */ XtVersion, /* version */ NULL, /* callback_private */ NULL, /* rect10 */ NULL, /* query_geometry */ NULL, /* rect11 */ NULL, /* extension */ }, { /* menu_g fields */ ChangeHl, /* change_highlighted */ Popdown, /* popdown */ Notify, /* notify */ PostNotify, /* post_notify */ SetActive, /* set_active */ True, /* ignore_leave */ NULL, /* extension */ }, { /* menu_str_g fields */ NULL, /* extension */ }, { /* pull_right fields */ NULL, /* extension */ }, }; WidgetClass pullRightGadgetClass = (WidgetClass)&pullRightGadgetClassRec; /*************************************************************************/ static void draw_arrow(PullRightGadget g) { MenuWidget parent = (MenuWidget)g->object.parent; Display *disp = XtDisplay(parent); Window win = XtWindow(parent); int s = g->pull_right.arrow_size; int x, y; if (s <= 0) return; x = g->rectangle.x + g->rectangle.width - g->pull_right.arrow_offset - parent->shadow.shadow_width; y = g->rectangle.y + g->rectangle.height/2; if (parent->shadow.line_mode) { short sw = parent->shadow.shadow_width; XPoint point[4]; XClearArea(disp, win, x - s - sw, y - s/2 - sw, s + 2 * sw, s + 2 * sw, False); if (!g->menu_g.hl) sw = 0; else { sw /= 2; s -= sw; } point[0].x = point[3].x = x - sw/2; point[1].x = point[2].x = x - s; point[0].y = point[3].y = y; point[1].y = y - s/2; point[2].y = y + s/2; XDrawLines(disp, win, !g->menu_g.hl ? parent->shadow.light_gc : parent->shadow.dark_gc, point, 4, CoordModeOrigin); } else { XPoint point[6]; short sw = g->pull_right.arrow_shadow_width; GC light_gc, dark_gc; if (g->menu_g.hl) { light_gc = parent->shadow.dark_gc; dark_gc = parent->shadow.light_gc; } else { light_gc = parent->shadow.light_gc; dark_gc = parent->shadow.dark_gc; } if (sw == 0) return; else if (sw == 1) { point[0].x = point[3].x = x; point[1].x = point[2].x = x - s; point[0].y = point[3].y = y; point[1].y = y - s/2; point[2].y = y + s/2; if (!g->menu_g.hl && parent->shadow.arm_gc != 0) XFillPolygon(disp, win, parent->shadow.arm_gc, point, 3, Convex, CoordModeOrigin); XDrawLines(disp, win, dark_gc, point, 3, CoordModeOrigin); XDrawLines(disp, win, light_gc, &point[2], 2, CoordModeOrigin); } else { point[0].x = x; point[1].x = point[2].x = x - s; point[3].x = point[4].x = x - s + sw; point[5].x = x - (9 * sw)/4; point[0].y = point[5].y = y; point[1].y = y - s/2; point[2].y = y + s/2; point[3].y = point[2].y - (3 * sw)/2; point[4].y = point[1].y + (3 * sw)/2; if (!g->menu_g.hl && parent->shadow.arm_gc != 0) XFillPolygon(disp, win, parent->shadow.arm_gc, &point[3], 3, Convex, CoordModeOrigin); XFillPolygon(disp, win, dark_gc, point, 6, Nonconvex, CoordModeOrigin); point[1] = point[0]; point[4] = point[5]; XFillPolygon(disp, win, light_gc, &point[1], 4, Convex, CoordModeOrigin); } } } static Widget find_menu(PullRightGadget g) { if (g->pull_right.menu_name) { Widget loop; for (loop = g->object.parent ; loop ; loop = XtParent(loop)) { Widget temp = XtNameToWidget(loop, g->pull_right.menu_name); if (temp && XtIsShell(temp)) return temp; } } return NULL; } /*************************************************************************/ static void Initialize(Widget grequest, Widget gnew, ArgList args, Cardinal *no_args) { PullRightGadget g = (PullRightGadget)gnew; if (!g->pull_right.menu_name) g->pull_right.menu = NULL; else { g->pull_right.menu_name = XtNewString(g->pull_right.menu_name); g->pull_right.menu = find_menu(g); } } static void Destroy(Widget gw) { PullRightGadget w = (PullRightGadget)gw; XtFree(w->pull_right.menu_name); } static void Redisplay(Widget gw, XEvent *event, Region region) { PullRightGadget w = (PullRightGadget)gw; stringGadgetClass->core_class.expose((Widget)w, event, region); draw_arrow(w); } static Boolean SetValues(Widget gcurrent, Widget grequest, Widget gnew, ArgList args, Cardinal *num_args) { Boolean redisplay = False; PullRightGadget new = (PullRightGadget)gnew; PullRightGadget current = (PullRightGadget)gcurrent; if (new->pull_right.menu_name != current->pull_right.menu_name) { XtFree(current->pull_right.menu_name); if (!new->pull_right.menu_name) new->pull_right.menu = NULL; else { new->pull_right.menu_name = XtNewString(new->pull_right.menu_name); new->pull_right.menu = find_menu(new); } } return redisplay; } static void ChangeHl(MenuGadget m) { PullRightGadget g = (PullRightGadget)m; Widget parent = g->object.parent; Widget menu = g->pull_right.menu; Screen *screen; Arg args[2]; Position x, y; Redisplay((Widget)g, NULL, NULL); if (!menu && !(menu = g->pull_right.menu = find_menu(g))) { fprintf(stderr, "PullRightGadget: Couldn't find menu: %s\n", g->pull_right.menu_name); return; } /* * Unhighlight. */ if (!g->menu_g.hl) { PopdownMenuShell(menu); return; } screen = XtScreen(menu); if (!XtIsRealized(menu)) XtRealizeWidget(menu); XtTranslateCoords(parent, parent->core.width, g->rectangle.y, &x, &y); if (x < 0) x = 0; else { int tmp = WidthOfScreen(screen) - menu->core.width; if (tmp >= 0 && x > tmp) x = tmp; } if (y < 0) y = 0; else { int tmp = HeightOfScreen(screen) - menu->core.height; if (tmp >= 0 && y > tmp) y = tmp; } XtSetArg(args[0], XtNx, x); XtSetArg(args[1], XtNy, y); XtSetValues(menu, args, 2); XtPopup(menu, XtGrabNone/*xclusive*/); XtAddGrab(menu, False, False); } static void Popdown(MenuGadget m) { PullRightGadget g = (PullRightGadget)m; if (g->pull_right.menu) PopdownMenuShell(g->pull_right.menu); } static int Notify(MenuGadget m) { PullRightGadget g = (PullRightGadget)m; if (!g->pull_right.menu || g->menu_g.inside) { XtCallCallbackList((Widget)g, g->menu_g.callback, NULL); return True; } return NotifyMenuShell(g->pull_right.menu); } static int PostNotify(MenuGadget m) { PullRightGadget g = (PullRightGadget)m; if (!g->pull_right.menu || g->menu_g.inside) { XtCallCallbackList((Widget)g, g->menu_g.post_popdown_callback, NULL); return True; } return PostNotifyMenuShell(g->pull_right.menu); } static void SetActive(MenuGadget m, int active) { PullRightGadget g = (PullRightGadget)m; g->menu_g.active = active; if (g->pull_right.menu) SetActiveMenuShell(g->pull_right.menu, active); } ./knews-1.0b.1/Widgets/PullRight.h100644 1244 1244 1530 6455455537 15330 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #ifndef PullRight_h #define PullRight_h #ifndef XtCMenuName #define XtCMenuName "MenuName" #endif #ifndef XtCArrowSize #define XtCArrowSize "ArrowSize" #endif #ifndef XtCArrowOffset #define XtCArrowOffset "ArrowOffset" #endif #ifndef XtCMenuName #define XtCMenuName "MenuName" #endif #ifndef XtNmenuName #define XtNmenuName "menuName" #endif #ifndef XtNarrowSize #define XtNarrowSize "arrowSize" #endif #ifndef XtNarrowOffset #define XtNarrowOffset "arrowOffset" #endif #ifndef XtNarrowShadowWidth #define XtNarrowShadowWidth "arrowShadowWidth" #endif #ifndef XtNmenuName #define XtNmenuName "menuName" #endif typedef struct PullRightGadgetClassRec* PullRightGadgetClass; typedef struct PullRightGadgetRec* PullRightGadget; extern WidgetClass pullRightGadgetClass; #endif /* PullRight_h */ ./knews-1.0b.1/Widgets/Sash.c100644 1244 1244 16356 6455455537 14343 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include #include #include "Compat.h" #include "SashP.h" static XtResource resources[] = { {XtNshadowWidth, XtCShadowWidth, XtRDimension, sizeof(Dimension), XtOffsetOf(SashRec, shadow.shadow_width), XtRImmediate, (XtPointer)1}, #define offset(field) XtOffsetOf(SashRec, sash.field) {XtNcallback, XtCCallback, XtRCallback, sizeof(XtCallbackList), offset(callback), XtRImmediate, (XtPointer)NULL}, {XtNcursor, XtCCursor, XtRCursor, sizeof(Cursor), offset(cursor), XtRString, (XtPointer)"hand2"}, {XtNpreferredWidth, XtCWidth, XtRDimension, sizeof(Dimension), offset(pref_width), XtRImmediate, (XtPointer)3}, {XtNpreferredHeight, XtCHeight, XtRDimension, sizeof(Dimension), offset(pref_height), XtRImmediate, (XtPointer)3}, #undef offset }; static void Initialize(Widget, Widget, ArgList, Cardinal*); static void Realize(Widget, XtValueMask*, XSetWindowAttributes*); static void Redisplay(Widget, XEvent*, Region); static Boolean SetValues(Widget, Widget, Widget, ArgList, Cardinal*); static XtGeometryResult QueryGeometry(Widget, XtWidgetGeometry*, XtWidgetGeometry*); static void sash_action(Widget, XEvent*, String*, Cardinal*); static XtActionsRec actions[] = { {"sash-action", sash_action}, }; static char translations[] = ": sash-action() \n" ": sash-action() \n"; SashClassRec sashClassRec = { { /* core fields */ (WidgetClass) &shadowClassRec, /* superclass */ "Sash", /* class_name */ sizeof(SashRec), /* widget_size */ NULL, /* class_initialize */ NULL, /* class_part_initialize */ FALSE, /* class_inited */ Initialize, /* initialize */ NULL, /* initialize_hook */ Realize, /* realize */ actions, /* actions */ XtNumber(actions), /* num_actions */ resources, /* resources */ XtNumber(resources), /* num_resources */ NULLQUARK, /* xrm_class */ TRUE, /* compress_motion */ #if (XtSpecificationRelease < 4) TRUE, /* compress_exposure */ #elif (XtSpecificationRelease < 6) XtExposeCompressMaximal, /* compress_exposure */ #else XtExposeCompressMaximal | XtExposeNoRegion, /* compress_exposure*/ #endif TRUE, /* compress_enterleave */ FALSE, /* visible_interest */ NULL, /* destroy */ NULL, /* resize */ Redisplay, /* expose */ SetValues, /* set_values */ NULL, /* set_values_hook */ XtInheritSetValuesAlmost, /* set_values_almost */ NULL, /* get_values_hook */ NULL, /* accept_focus */ XtVersion, /* version */ NULL, /* callback_private */ translations, /* tm_table */ QueryGeometry, /* query_geometry */ XtInheritDisplayAccelerator, /* display_accelerator */ NULL /* extension */ }, { /* shadow fields */ XtInheritPixelOffset, /* pixel offset */ True, /* use_arm_for_background */ XtInheritAllocShadowColors, /* alloc_shadow_colors */ XtInheritAllocShadowPixmaps, /* alloc_shadow_pixmaps */ XtInheritAllocArmColor, /* alloc_arm_color */ XtInheritAllocArmPixmap, /* alloc_arm_pixmap */ XtInheritAllocGCs, /* alloc_gcs */ NULL, /* extension */ }, { /* sash fields */ 0 /* empty */ } }; WidgetClass sashWidgetClass = (WidgetClass)&sashClassRec; /*************************************************************************/ static void sash_action(Widget gw, XEvent *event, String *params, Cardinal *no_params) { SashWidget w = (SashWidget)gw; XtCallbackList c_list = w->sash.callback; if (c_list) { SashCallDataRec call_data; call_data.event = event; call_data.params = params; call_data.no_params = no_params; XtCallCallbackList((Widget)w, c_list, (XtPointer)&call_data); } } /*************************************************************************/ static void Initialize(Widget request, Widget new, ArgList args, Cardinal *no_args) { if (new->core.height == 0) new->core.height = 1; if (new->core.width == 0) new->core.width = 1; } static void Realize(Widget gw, XtValueMask *mask, XSetWindowAttributes *attributes) { SashWidget w = (SashWidget)gw; if (w->sash.cursor != None) { attributes->cursor = w->sash.cursor; *mask |= CWCursor; } shadowWidgetClass->core_class.realize((Widget)w, mask, attributes); } static void Redisplay(Widget gw, XEvent *event, Region region) { SashWidget w = (SashWidget)gw; if (!XtIsRealized((Widget)w)) return; ShadowDrawShadows((ShadowWidget)w, 0, 0, w->core.width, w->core.height, True); } static Boolean SetValues(Widget gcurrent, Widget grequest, Widget gnew, ArgList args, Cardinal *num_args) { SashWidget current = (SashWidget)gcurrent; SashWidget new = (SashWidget)gnew; if (new->sash.cursor != current->sash.cursor && XtIsRealized((Widget)new)) XDefineCursor(XtDisplay(new), XtWindow(new), new->sash.cursor); return False; } static XtGeometryResult QueryGeometry(Widget gw, XtWidgetGeometry *intended, XtWidgetGeometry *preferred) { SashWidget w = (SashWidget)gw; Dimension intended_width, intended_height; preferred->request_mode = 0; if (w->sash.pref_width > 0) { preferred->width = w->sash.pref_width; preferred->request_mode |= CWWidth; } else { preferred->width = w->core.width; } if (w->sash.pref_height > 0) { preferred->height = w->sash.pref_height; preferred->request_mode |= CWHeight; } else { preferred->height = w->core.height; } if (intended->request_mode & CWWidth) intended_width = intended->width; else intended_width = w->core.width; if (intended->request_mode & CWHeight) intended_height = intended->height; else intended_height = w->core.height; if (preferred->width == w->core.width && preferred->height == w->core.height) return XtGeometryNo; else if (preferred->width == intended_width && preferred->height == intended_height) return XtGeometryYes; else return XtGeometryAlmost; } ./knews-1.0b.1/Widgets/PullRightP.h100644 1244 1244 1643 6455455537 15455 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #ifndef PullRightP_h #define PullRightgP_h #include "PullRight.h" #include "StringGP.h" typedef struct { XtPointer extension; } PullRightGadgetClassPart; typedef struct PullRightGadgetClassRec { RectObjClassPart rect_class; MenuGadgetClassPart menu_g_class; StringGadgetClassPart string_g_class; PullRightGadgetClassPart pull_right_class; } PullRightGadgetClassRec; extern PullRightGadgetClassRec pullRightGadgetClassRec; typedef struct { String menu_name; Dimension arrow_size; Dimension arrow_offset; Dimension arrow_shadow_width; /* private data */ Widget menu; } PullRightGadgetPart; typedef struct PullRightGadgetRec { ObjectPart object; RectObjPart rectangle; MenuGadgetPart menu_g; StringGadgetPart string_g; PullRightGadgetPart pull_right; } PullRightGadgetRec; #endif /* PullRightP_h */ ./knews-1.0b.1/Widgets/Sash.h100644 1244 1244 1061 6455455537 14313 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #ifndef Sash_h #define Sash_h #ifndef XtNcursor #define XtNcursor "cursor" #endif #ifndef XtNpreferredWidth #define XtNpreferredWidth "preferredWidth" #endif #ifndef XtNpreferredHeight #define XtNpreferredHeight "preferredHeight" #endif typedef struct SashClassRec* SashWidgetClass; typedef struct SashRec* SashWidget; extern WidgetClass sashWidgetClass; typedef struct { XEvent *event; String *params; Cardinal *no_params; } SashCallDataRec, *SashCallData; #endif /* Sash_h */ ./knews-1.0b.1/Widgets/SashP.h100644 1244 1244 1203 6455455537 14431 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #ifndef SashP_h #define SashP_h #include "Sash.h" #include "ShadowP.h" typedef struct { int empty; } SashClassPart; typedef struct SashClassRec { CoreClassPart core_class; ShadowClassPart shadow_class; SashClassPart sash_class; } SashClassRec; extern SashClassRec sashClassRec; typedef struct { XtCallbackList callback; Cursor cursor; /* private data */ Dimension pref_width; Dimension pref_height; } SashPart; typedef struct SashRec { CorePart core; ShadowPart shadow; SashPart sash; } SashRec; #endif /* SashP_h */ ./knews-1.0b.1/Widgets/StringG.c100644 1244 1244 16316 6455455537 15016 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include #include #include "Compat.h" #include "Util.h" #include "StringGP.h" static XtResource resources[] = { #define offset(field) XtOffsetOf(StringGadgetRec, string_g.field) {XtNcommand, XtCCommand, XtRString, sizeof(String), offset(command), XtRImmediate, (XtPointer)NULL}, {XtNfont, XtCFont, XtRFontStruct, sizeof(XFontStruct *), offset(font), XtRString, XtDefaultFont}, {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel), offset(foreground_pixel), XtRString, XtDefaultForeground}, {XtNleftMargin, XtCMargin, XtRDimension, sizeof(Dimension), offset(left_margin), XtRImmediate, (XtPointer)24}, {XtNrightMargin, XtCMargin, XtRDimension, sizeof(Dimension), offset(right_margin), XtRImmediate, (XtPointer)32}, {XtNinternalHeight, XtCInternalHeight, XtRDimension, sizeof(Dimension), offset(internal_height), XtRImmediate, (XtPointer)2}, {XtNshadowWidth, XtCShadowWidth, XtRDimension, sizeof(Dimension), offset(shadow_width), XtRImmediate, (XtPointer)2}, #undef offset }; static void Initialize(Widget, Widget, ArgList, Cardinal*); static void Destroy(Widget); static Boolean SetValues(Widget, Widget, Widget, ArgList, Cardinal*); static void Redisplay(Widget, XEvent*, Region); StringGadgetClassRec stringGadgetClassRec = { { /* rectObj fields */ (WidgetClass) &menuGadgetClassRec, /* superclass */ "StringGadget", /* class_name */ sizeof(StringGadgetRec), /* widget_size */ NULL, /* class_initialize */ NULL, /* class_part_initialize */ FALSE, /* class_inited */ Initialize, /* initialize */ NULL, /* initialize_hook */ NULL, /* rect1 */ NULL, /* rect2 */ 0, /* rect3 */ resources, /* resources */ XtNumber(resources), /* num_resources */ NULLQUARK, /* xrm_class */ FALSE, /* rect4 */ FALSE, /* rect5 */ FALSE, /* rect6 */ FALSE, /* rect7 */ Destroy, /* destroy */ NULL, /* resize */ Redisplay, /* expose */ SetValues, /* set_values */ NULL, /* set_values_hook */ XtInheritSetValuesAlmost, /* set_values_almost */ NULL, /* get_values_hook */ NULL, /* rect9 */ XtVersion, /* version */ NULL, /* callback_private */ NULL, /* rect10 */ NULL, /* query_geometry */ NULL, /* rect11 */ NULL, /* extension */ }, { /* menu_g fields */ XtInheritChangeHl, /* change_hl */ XtInheritPopdown, /* popdown */ XtInheritNotify, /* notify */ XtInheritPostNotify, /* post_notify */ XtInheritSetActive, /* set_actvive */ False, /* ignore_leave */ NULL, /* extension */ }, { /* string_g fields */ NULL, /* extension */ }, }; WidgetClass stringGadgetClass = (WidgetClass)&stringGadgetClassRec; /*************************************************************************/ static void set_preferred_size(StringGadget g) { g->rectangle.width = g->string_g.left_margin + g->string_g.right_margin + 2 * g->string_g.shadow_width; g->rectangle.width += XTextWidth(g->string_g.font, g->menu_g.label, strlen(g->menu_g.label)); g->rectangle.height = 2 * (g->string_g.internal_height + g->string_g.shadow_width) + g->string_g.font->ascent + g->string_g.font->descent; } static void init_gcs(StringGadget g) { XGCValues values; values.font = g->string_g.font->fid; values.foreground = g->string_g.foreground_pixel; values.stipple = g->string_g.stipple; values.fill_style = FillStippled; g->string_g.default_gc = XtGetGC(g->object.parent, GCForeground | GCFont, &values); g->string_g.gray_gc = XtGetGC(g->object.parent, GCForeground | GCFont | GCFillStyle | GCStipple, &values); } static void free_gcs(StringGadget g) { XtReleaseGC(g->object.parent, g->string_g.default_gc); XtReleaseGC(g->object.parent, g->string_g.gray_gc); } /*************************************************************************/ static void Initialize(Widget grequest, Widget gnew, ArgList args, Cardinal *no_args) { StringGadget new = (StringGadget)gnew; set_preferred_size(new); new->string_g.stipple = create_stipple(XtScreen(new->object.parent)); init_gcs(new); } static void Destroy(Widget gw) { StringGadget g = (StringGadget)gw; release_stipple(XtScreen(g->object.parent), g->string_g.stipple); free_gcs(g); } static void Redisplay(Widget gw, XEvent *event, Region region) { StringGadget g = (StringGadget)gw; ShadowWidget parent = (ShadowWidget)g->object.parent; Display *disp = XtDisplay(parent); Window win = XtWindow(parent); XDrawString(disp, win, XtIsSensitive((Widget)g) ? g->string_g.default_gc : g->string_g.gray_gc, g->rectangle.x + g->string_g.left_margin + g->string_g.shadow_width, g->rectangle.y + (int)(g->rectangle.height + g->string_g.font->ascent - g->string_g.font->descent) / 2, g->menu_g.label, strlen(g->menu_g.label)); if (g->menu_g.hl) { int old_sw = parent->shadow.shadow_width; parent->shadow.shadow_width = g->string_g.shadow_width; ShadowDrawShadows(parent, g->rectangle.x, g->rectangle.y, g->rectangle.width, g->rectangle.height, False); parent->shadow.shadow_width = old_sw; } } static Boolean SetValues(Widget gcurrent, Widget grequest, Widget gnew, ArgList args, Cardinal *num_args) { Boolean redisplay = False; StringGadget new = (StringGadget)gnew; StringGadget current = (StringGadget)gcurrent; if (new->string_g.font != current->string_g.font || new->string_g.foreground_pixel != current->string_g.foreground_pixel) { free_gcs(current); init_gcs(new); redisplay = True; } return redisplay; } /*********************************************************************/ char *StringGadgetCommand(Widget gw) { StringGadget w = (StringGadget)gw; return w->string_g.command; } ./knews-1.0b.1/Widgets/ScrBarP.h100644 1244 1244 2452 6455455537 14716 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #ifndef ScrBarP_h #define ScrBarP_h #include "ScrBar.h" #include "Util.h" #include "ShadowP.h" typedef enum { ScrBarModeNone, ScrBarModeForwardLine, ScrBarModeBackwardLine, ScrBarModeForwardPage, ScrBarModeBackwardPage, ScrBarModeContinuous } ScrBarMode; typedef struct { XtPointer extension; } ScrBarClassPart; typedef struct ScrBarClassRec { CoreClassPart core_class; ShadowClassPart shadow_class; ScrBarClassPart scrbar_class; } ScrBarClassRec; extern ScrBarClassRec scrBarClassRec; typedef struct { long canvas_length; long slider_length; long slider_position; long step_size; XtCallbackList scroll_callback; int initial_delay; int minimum_delay; int decay; Dimension minimum_thumb; Dimension thickness; Boolean vertical; Boolean push_thumb; Boolean allow_off; Boolean sync_scroll; /* private data */ GC bg_gc; Position c1, c2, c3, c4; Position init_ptr_pos; Position init_slider_pos; ScrBarMode mode; XtIntervalId timer; int this_delay; } ScrBarPart; typedef struct ScrBarRec { CorePart core; ShadowPart shadow; ScrBarPart scrbar; } ScrBarRec; #endif /* ScrBarP_h */ ./knews-1.0b.1/Widgets/ScrListP.h100644 1244 1244 3221 6455455537 15120 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #ifndef ScrlistP_h #define ScrlistP_h #include "ScrList.h" #include "ScrollableP.h" typedef struct { XtPointer extension; } ScrListClassPart; typedef struct ScrListClassRec { CoreClassPart core_class; ShadowClassPart shadow_class; ScrollableClassPart scrollable_class; ScrListClassPart scrlist_class; } ScrListClassRec; extern ScrListClassRec scrListClassRec; typedef struct { Pixel foreground_pixel; Pixel highlight_pixel; Cursor dnd_cursor; XFontStruct *font; XtCallbackList select_callback; XtCallbackList callback; XtCallbackList second_callback; XtCallbackList dnd_callback; long n_alloc; Dimension row_spacing; Dimension internal_width; Dimension internal_height; Dimension internal_item_width; Dimension internal_item_height; Dimension pixmap_width; Dimension pixmap_height; Dimension pixmap_spacing; Dimension preferred_lines; Dimension preferred_columns; Dimension margin_up; Dimension margin_down; Boolean depth_one; Boolean at_least_one; Boolean at_most_one; Boolean allow_dnd; Boolean use_pixmaps; Boolean page_up; Boolean page_down; /* private */ Boolean active; char **strings; Pixmap *pixmaps; Boolean *selected; long n_sel; long dnd_start; GC default_gc; GC selected_gc; GC highlight_gc; int max_width; } ScrListPart; typedef struct ScrListRec { CorePart core; ShadowPart shadow; ScrollablePart scrollable; ScrListPart scrlist; } ScrListRec; #endif /* ScrListP_h */ ./knews-1.0b.1/Widgets/Shadow.c100644 1244 1244 50507 6455455537 14666 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include #include #include "Compat.h" #include "ShadowP.h" #include "Util.h" static XtResource resources[] = { {XtNborderWidth, XtCBorderWidth, XtRDimension, sizeof(Dimension), XtOffsetOf(ShadowRec, core.border_width), XtRImmediate, (XtPointer)0}, #define offset(field) XtOffsetOf(ShadowRec, shadow.field) {XtNshadowWidth, XtCShadowWidth, XtRDimension, sizeof(Dimension), offset(shadow_width), XtRImmediate, (XtPointer)2}, {XtNallocArmColor, XtCAllocArmColor, XtRBoolean, sizeof(Boolean), offset(alloc_arm_color), XtRImmediate, (XtPointer)True}, {XtNallocArmPixmap, XtCAllocArmPixmap, XtRBoolean, sizeof(Boolean), offset(alloc_arm_pixmap), XtRImmediate, (XtPointer)True}, {XtNallocShadowColors, XtCAllocShadowColors, XtRBoolean, sizeof(Boolean), offset(alloc_shadow_colors), XtRImmediate, (XtPointer)True}, {XtNuseLineShadows, XtCUseLineShadows, XtRBoolean, sizeof(Boolean), offset(use_lines), XtRImmediate, (XtPointer)False}, #undef offset }; static void ClassPartInitialize(WidgetClass); static void Initialize(Widget, Widget, ArgList, Cardinal*); static void Destroy(Widget); static void Realize(Widget, XtValueMask*, XSetWindowAttributes*); static Boolean SetValues(Widget, Widget, Widget, ArgList, Cardinal*); static Boolean AllocShadowColors(ShadowWidget, XColor*); static void AllocShadowPixmaps(ShadowWidget, Pixel); static Boolean AllocArmColor(ShadowWidget, XColor*); static void AllocArmPixmap(ShadowWidget, Pixel); static void AllocGCs(ShadowWidget); ShadowClassRec shadowClassRec = { { /* core fields */ (WidgetClass) &widgetClassRec, /* superclass */ "Shadow", /* class_name */ sizeof(ShadowRec), /* widget_size */ NULL, /* class_initialize */ ClassPartInitialize, /* class_part_initialize */ FALSE, /* class_inited */ Initialize, /* initialize */ NULL, /* initialize_hook */ Realize, /* realize */ NULL, /* actions */ 0, /* num_actions */ resources, /* resources */ XtNumber(resources), /* num_resources */ NULLQUARK, /* xrm_class */ TRUE, /* compress_motion */ TRUE, /* compress_exposure */ TRUE, /* compress_enterleave */ FALSE, /* visible_interest */ Destroy, /* destroy */ NULL, /* resize */ NULL, /* expose */ SetValues, /* set_values */ NULL, /* set_values_hook */ XtInheritSetValuesAlmost, /* set_values_almost */ NULL, /* get_values_hook */ NULL, /* accept_focus */ XtVersion, /* version */ NULL, /* callback_private */ NULL, /* tm_table */ NULL, /* query_geometry */ XtInheritDisplayAccelerator, /* display_accelerator */ NULL /* extension */ }, { /* shadow fields */ XtOffsetOf(CoreRec, core.background_pixel), /* pixel_offset */ False, /* use_arm_for_background */ AllocShadowColors, /* alloc_shadow_colors */ AllocShadowPixmaps, /* alloc_shadow_pixmaps */ AllocArmColor, /* alloc_arm_color */ AllocArmPixmap, /* alloc_arm_pixmap */ AllocGCs, /* alloc_gcs */ NULL, /* extension */ } }; WidgetClass shadowWidgetClass = (WidgetClass)&shadowClassRec; /*************************************************************************/ static Pixel get_pixel(ShadowWidget w) { ShadowWidgetClass class = (ShadowWidgetClass)XtClass(w); Cardinal offset = class->shadow_class.pixel_offset; return *(Pixel *)((char *)w + offset); } typedef struct pixmap_cache_ref { struct pixmap_cache_ref *next; struct pixmap_cache_ref *prev; Screen *screen; Pixmap pixmap; Pixel fg; Pixel bg; int depth; int size; int ref_count; } PixmapCacheRef; static PixmapCacheRef *pixmap_cache; static Pixmap CacheCreatePixmap(ShadowWidget w, int size, Pixel fg, Pixel bg) { static char bitmap2by2_data[] = {0x01, 0x02}; static char bitmap4by4_data[] = {0x08, 0x02, 0x04, 0x01}; Display *disp = XtDisplay(w); Screen *screen = XtScreen(w); PixmapCacheRef *loop; char *bitmap_data; for (loop = pixmap_cache ; loop ; loop = loop->next) if (loop->screen == screen && loop->fg == fg && loop->bg == bg && loop->depth == w->core.depth && loop->size == size) { loop->ref_count++; return loop->pixmap; } switch (size) { case 2: bitmap_data = bitmap2by2_data; break; case 4: bitmap_data = bitmap4by4_data; break; default: return None; } loop = (PixmapCacheRef *)XtMalloc(sizeof(PixmapCacheRef)); loop->next = pixmap_cache; loop->prev = NULL; loop->screen = screen; loop->depth = w->core.depth; loop->size = size; loop->fg = fg; loop->bg = bg; loop->ref_count = 1; loop->pixmap = XCreatePixmapFromBitmapData(disp, RootWindowOfScreen(screen), bitmap_data, size, size, fg, bg, w->core.depth); if (pixmap_cache) pixmap_cache->prev = loop; pixmap_cache = loop; return loop->pixmap; } static void CacheFreePixmap(ShadowWidget w, Pixmap pixmap) { Display *disp = XtDisplay(w); Screen *screen = XtScreen(w); PixmapCacheRef *loop; for (loop = pixmap_cache ; loop ; loop = loop->next) if (loop->screen == screen && loop->pixmap == pixmap) { PixmapCacheRef *prev = loop->prev; loop->ref_count--; if (loop->ref_count != 0) break; XFreePixmap(disp, loop->pixmap); if (prev) { prev->next = loop->next; if (loop->next) loop->next->prev = prev; } else { pixmap_cache = loop->next; if (pixmap_cache) pixmap_cache->prev = NULL; } XtFree((char *)loop); break; } } /*************************************************************************/ #define MIN(a, b) ((a) < (b) ? (a) : (b)) static Boolean AllocShadowColors(ShadowWidget w, XColor *col) { Display *disp = XtDisplay(w); Colormap cmap = w->core.colormap; XColor sh_col; if ((col->red > 0xefff && col->green > 0xefff && col->blue > 0xefff) || (col->red < 0x1000 && col->green < 0x1000 && col->blue < 0x1000)) { sh_col.red = 65535L * 4 / 5; sh_col.green = 65535L * 4 / 5; sh_col.blue = 65535L * 4 / 5; if (!XAllocColor(disp, cmap, &sh_col)) return False; w->shadow.light_pixel = sh_col.pixel; sh_col.red = 65535L * 3 /5; sh_col.green = 65535L * 3 /5; sh_col.blue = 65535L * 3 /5; if (!XAllocColor(disp, cmap, &sh_col)) { unsigned long tmp = w->shadow.light_pixel; XFreeColors(disp, cmap, &tmp, 1, 0); return False; } w->shadow.dark_pixel = sh_col.pixel; } else { sh_col.red = MIN (65535, 6 * (long)col->red / 5); sh_col.green = MIN (65535, 6 * (long)col->green / 5); sh_col.blue = MIN (65535, 6 * (long)col->blue / 5); if (!XAllocColor(disp, cmap, &sh_col)) return False; w->shadow.light_pixel = sh_col.pixel; sh_col.red = MIN (65535, 3 * (long)col->red / 5); sh_col.green = MIN (65535, 3 * (long)col->green / 5); sh_col.blue = MIN (65535, 3 * (long)col->blue / 5); if (!XAllocColor(disp, cmap, &sh_col)) { unsigned long tmp = w->shadow.light_pixel; XFreeColors(disp, cmap, &tmp, 1, 0); return False; } w->shadow.dark_pixel = sh_col.pixel; } return True; } #undef MIN static void AllocShadowPixmaps(ShadowWidget w, Pixel pix) { if (w->core.depth == 1) w->shadow.light_pixmap = CacheCreatePixmap(w, 2, 0, 1); else { Screen *screen = XtScreen(w); Visual *visual = get_visual((Widget)w); Pixel black, white; black_and_white(screen, visual, &black, &white); w->shadow.light_pixmap = CacheCreatePixmap(w, 2, pix, white); w->shadow.dark_pixmap = CacheCreatePixmap(w, 2, pix, black); } } static Boolean AllocArmColor(ShadowWidget w, XColor *col) { Display *disp = XtDisplay(w); Colormap cmap = w->core.colormap; XColor arm_col; long t, r, g, b; t = col->red + col->green + col->blue; t /= 3; r = 3 * t / 4 + 3 * ((long)col->red - t) / 2; g = 3 * t / 4 + 3 * ((long)col->green - t) / 2; b = 3 * t / 4 + 3 * ((long)col->blue - t) / 2; if (r < 0) r = 0; else if (r > 65535) r = 65535; if (g < 0) g = 0; else if (g > 65535) g = 65535; if (b < 0) b = 0; else if (b > 65535) b = 65535; arm_col.red = r; arm_col.green = g; arm_col.blue = b; if (!XAllocColor(disp, cmap, &arm_col)) return False; w->shadow.arm_pixel = arm_col.pixel; return True; } static void AllocArmPixmap(ShadowWidget w, Pixel pix) { if (w->core.depth == 1) w->shadow.arm_pixmap = CacheCreatePixmap(w, 4, 0, 1); else w->shadow.arm_pixmap = CacheCreatePixmap(w, 4, get_black((Widget)w), pix); } static void AllocGCs(ShadowWidget w) { ShadowWidgetClass class = (ShadowWidgetClass)XtClass(w); XGCValues values; if (w->shadow.alloced_shadow_pixels) { values.line_width = 1; values.foreground = w->shadow.light_pixel; w->shadow.light_gc = XtGetGC((Widget)w, GCForeground | GCLineWidth, &values); values.foreground = w->shadow.dark_pixel; w->shadow.dark_gc = XtGetGC((Widget)w, GCForeground | GCLineWidth, &values); } else if (w->shadow.line_mode) { Pixel pix = get_pixel(w); /* depth == 1... */ values.foreground = (pix == 1) ? 0 : 1; values.line_width = 1; w->shadow.light_gc = XtGetGC((Widget)w, GCForeground | GCLineWidth, &values); values.line_width = w->shadow.shadow_width; w->shadow.dark_gc = XtGetGC((Widget)w, GCForeground | GCLineWidth, &values); } else { Screen *scr = XtScreen(w); Visual *vis = get_visual((Widget)w); Pixel black, white; black_and_white(scr, vis, &black, &white); values.line_width = 1; if (w->shadow.light_pixmap == None) { values.foreground = (w->core.background_pixel == black) ? white : black; w->shadow.light_gc = XtGetGC((Widget)w, GCLineWidth | GCForeground, &values); } else { values.fill_style = FillTiled; values.tile = w->shadow.light_pixmap; w->shadow.light_gc = XtGetGC((Widget)w, GCLineWidth | GCFillStyle | GCTile, &values); } if (w->shadow.dark_pixmap == None) { values.foreground = (w->core.background_pixel == black) ? white : black; w->shadow.dark_gc = XtGetGC((Widget)w, GCLineWidth | GCForeground, &values); } else { values.fill_style = FillTiled; values.tile = w->shadow.dark_pixmap; w->shadow.dark_gc = XtGetGC((Widget)w, GCLineWidth | GCFillStyle | GCTile, &values); } } if (class->shadow_class.use_arm_for_background) return; if (w->shadow.alloced_arm_pixel) { values.line_width = 1; values.foreground = w->shadow.arm_pixel; w->shadow.arm_gc = XtGetGC((Widget)w, GCForeground | GCLineWidth, &values); } else if (w->shadow.arm_pixmap != None) { values.line_width = 1; values.tile = w->shadow.arm_pixmap; values.fill_style = FillTiled; w->shadow.arm_gc = XtGetGC((Widget)w, GCFillStyle | GCTile| GCLineWidth, &values); } } /*************************************************************************/ static void ClassPartInitialize(WidgetClass gclass) { ShadowClassRec *class, *super; class = (ShadowClassRec *)gclass; super = (ShadowClassRec *)class->core_class.superclass; if (class->shadow_class.pixel_offset == XtInheritPixelOffset) class->shadow_class.pixel_offset = super->shadow_class.pixel_offset; if (class->shadow_class.alloc_shadow_colors == XtInheritAllocShadowColors) class->shadow_class.alloc_shadow_colors = super->shadow_class.alloc_shadow_colors; if (class->shadow_class.alloc_shadow_pixmaps == XtInheritAllocShadowPixmaps) class->shadow_class.alloc_shadow_pixmaps = super->shadow_class.alloc_shadow_pixmaps; if (class->shadow_class.alloc_arm_color == XtInheritAllocArmColor) class->shadow_class.alloc_arm_color = super->shadow_class.alloc_arm_color; if (class->shadow_class.alloc_arm_pixmap == XtInheritAllocArmPixmap) class->shadow_class.alloc_arm_pixmap = super->shadow_class.alloc_arm_pixmap; if (class->shadow_class.alloc_gcs == XtInheritAllocGCs) class->shadow_class.alloc_gcs = super->shadow_class.alloc_gcs; } static void Initialize(Widget grequest, Widget gnew, ArgList args, Cardinal *no_args) { ShadowWidget new = (ShadowWidget)gnew; ShadowWidgetClass class = (ShadowWidgetClass)XtClass(new); Display *disp = XtDisplay(new); Colormap cmap = new->core.colormap; XColor col; Boolean query_done = False; new->shadow.light_pixmap = None; new->shadow.dark_pixmap = None; new->shadow.arm_pixmap = None; new->shadow.light_gc = (GC)0; new->shadow.dark_gc = (GC)0; new->shadow.arm_gc = (GC)0; new->shadow.line_mode = False; new->shadow.alloced_shadow_pixels = False; new->shadow.alloced_arm_pixel = False; col.pixel = get_pixel(new); if (new->shadow.alloc_shadow_colors && new->core.depth != 1 && class->shadow_class.alloc_shadow_colors) { XQueryColor(disp, cmap, &col); query_done = True; if (class->shadow_class.alloc_shadow_colors(new, &col)) new->shadow.alloced_shadow_pixels = True; } if (!new->shadow.alloced_shadow_pixels) if (new->shadow.use_lines && new->core.depth == 1) new->shadow.line_mode = True; else if (class->shadow_class.alloc_shadow_pixmaps) class->shadow_class.alloc_shadow_pixmaps(new, col.pixel); if (new->shadow.alloc_arm_color && class->shadow_class.alloc_arm_color && new->core.depth != 1) { if (!query_done) XQueryColor(disp, cmap, &col); if (class->shadow_class.alloc_arm_color(new, &col)) new->shadow.alloced_arm_pixel = True; } if (!new->shadow.alloced_arm_pixel && new->shadow.alloc_arm_pixmap && class->shadow_class.alloc_arm_pixmap) class->shadow_class.alloc_arm_pixmap(new, col.pixel); if (class->shadow_class.alloc_gcs) class->shadow_class.alloc_gcs(new); } static void Realize(Widget gw, XtValueMask *mask, XSetWindowAttributes *attributes) { ShadowWidget w = (ShadowWidget)gw; ShadowWidgetClass class = (ShadowWidgetClass)XtClass(w); if (class->shadow_class.use_arm_for_background) if (w->shadow.arm_pixmap != None) { *mask |= CWBackPixmap; *mask &= ~CWBackPixel; attributes->background_pixmap = w->shadow.arm_pixmap; } else if (w->shadow.alloced_arm_pixel) { *mask |= CWBackPixel; attributes->background_pixel = w->shadow.arm_pixel; } coreWidgetClass->core_class.realize((Widget)w, mask, attributes); } static void Destroy(Widget gw) { ShadowWidget w = (ShadowWidget)gw; Display *disp = XtDisplay(w); unsigned long pixels[3]; int n = 0; if (w->shadow.light_gc != 0) XtReleaseGC((Widget)w, w->shadow.light_gc); if (w->shadow.dark_gc != 0) XtReleaseGC((Widget)w, w->shadow.dark_gc); if (w->shadow.arm_gc != 0) XtReleaseGC((Widget)w, w->shadow.arm_gc); if (w->shadow.light_pixmap != None) CacheFreePixmap(w, w->shadow.light_pixmap); if (w->shadow.dark_pixmap != None) CacheFreePixmap(w, w->shadow.dark_pixmap); if (w->shadow.arm_pixmap != None) CacheFreePixmap(w, w->shadow.arm_pixmap); if (w->shadow.alloced_shadow_pixels) { pixels[n] = w->shadow.light_pixel; n++; pixels[n] = w->shadow.dark_pixel; n++; } if (w->shadow.alloced_arm_pixel) { pixels[n] = w->shadow.arm_pixel; n++; } if (n != 0) { Colormap cmap = w->core.colormap; XFreeColors(disp, cmap, pixels, n, 0); } } static Boolean SetValues(Widget gcurrent, Widget grequest, Widget gnew, ArgList args, Cardinal *num_args) { Boolean redisplay = False; ShadowWidget new = (ShadowWidget)gnew; ShadowWidget current = (ShadowWidget)gcurrent; ShadowWidgetClass class = (ShadowWidgetClass)XtClass(new); Pixel new_pix = get_pixel(new); Pixel current_pix = get_pixel(current); if (new_pix != current_pix || (new->shadow.line_mode && new->shadow.shadow_width != current->shadow.shadow_width) || new->shadow.alloc_shadow_colors != current->shadow.alloc_shadow_colors || new->shadow.alloc_arm_color != current->shadow.alloc_arm_color || new->shadow.use_lines != current->shadow.use_lines || new->shadow.alloc_arm_pixmap != current->shadow.alloc_arm_pixmap) { Pixel pixels[3]; int n = 0; /* initialize first, then free, except for pixels... */ if (current->shadow.alloced_shadow_pixels) { pixels[n++] = current->shadow.light_pixel; pixels[n++] = current->shadow.dark_pixel; } if (current->shadow.alloced_arm_pixel) pixels[n++] = current->shadow.arm_pixel; if (n > 0) XFreeColors(XtDisplay(current), current->core.colormap, pixels, n, 0); Initialize(NULL, (Widget)new, NULL, NULL); if (current->shadow.light_gc != 0) XtReleaseGC((Widget)new, current->shadow.light_gc); if (current->shadow.dark_gc != 0) XtReleaseGC((Widget)new, current->shadow.dark_gc); if (current->shadow.arm_gc != 0) XtReleaseGC((Widget)new, current->shadow.arm_gc); if (current->shadow.light_pixmap != None) CacheFreePixmap(new, current->shadow.light_pixmap); if (current->shadow.dark_pixmap != None) CacheFreePixmap(new, current->shadow.dark_pixmap); if (current->shadow.arm_pixmap != None) CacheFreePixmap(new, current->shadow.arm_pixmap); redisplay = True; } if (class->shadow_class.use_arm_for_background && (redisplay || new->core.background_pixel != current->core.background_pixel)) { Display *disp = XtDisplay(new); Window win = XtWindow(new); if (new->shadow.alloced_arm_pixel) XSetWindowBackground(disp, win, new->shadow.arm_pixel); else if (new->shadow.arm_pixmap != None) XSetWindowBackgroundPixmap(disp, win, new->shadow.arm_pixmap); else XSetWindowBackground(disp, win, new->core.background_pixel); } return redisplay; } /*************************************************************************/ void ShadowDrawShadows(ShadowWidget w, Position x, Position y, Dimension width, Dimension height, Boolean swap) { Display *disp = XtDisplay(w); Window win = XtWindow(w); short sw = w->shadow.shadow_width; if (sw == 0 || !XtIsRealized((Widget)w)) return; if (w->shadow.line_mode) { if (swap) XDrawRectangle(disp, win, w->shadow.dark_gc, x + sw/2, y + sw/2, width - sw, height - sw); else XDrawRectangle(disp, win, w->shadow.light_gc, x, y, width - 1, height - 1); } else { GC light_gc, dark_gc; XPoint points[6]; if (swap) { light_gc = w->shadow.dark_gc; dark_gc = w->shadow.light_gc; } else { light_gc = w->shadow.light_gc; dark_gc = w->shadow.dark_gc; } points[0].x = x; points[0].y = y; points[1].x = x + width; points[1].y = y; points[2].x = x + width - sw; points[2].y = y + sw; points[3].x = x + sw; points[3].y = y + sw; points[4].x = x + sw; points[4].y = y + height - sw; points[5].x = x; points[5].y = y + height; XFillPolygon(disp, win, light_gc, points, 6, (sw > 1) ? Nonconvex : Complex, CoordModeOrigin); points[0].x = x + width; points[0].y = y + height; points[3].x = x + width - sw; points[3].y = y + height - sw; XFillPolygon(disp, win, dark_gc, points, 6, (sw > 1) ? Nonconvex : Complex, CoordModeOrigin); } } /* the widget must be able to handle NULL events and regions */ void ShadowRedrawWidget(Widget w) { if (XtIsRealized(w)) { XtExposeProc expose_proc = XtClass(w)->core_class.expose; if (expose_proc) expose_proc(w, NULL, NULL); } } ./knews-1.0b.1/Widgets/SeparatorG.c100644 1244 1244 11664 6455455537 15511 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include #include #include "Compat.h" #include "SeparatoGP.h" static XtResource resources[] = { #define offset(field) XtOffsetOf(SeparatorGadgetRec, separator_g.field) {XtNsize, XtCSize, XtRDimension, sizeof(Dimension), offset(size), XtRImmediate, (XtPointer)4}, {XtNinternalWidth, XtCInternalWidth, XtRDimension, sizeof(Dimension), offset(internal_width), XtRImmediate, (XtPointer)2}, {XtNinternalHeight, XtCInternalHeight, XtRDimension, sizeof(Dimension), offset(internal_height), XtRImmediate, (XtPointer)1}, {XtNshadowWidth, XtCShadowWidth, XtRDimension, sizeof(Dimension), offset(shadow_width), XtRImmediate, (XtPointer)1}, #undef offset }; static void Initialize(Widget, Widget, ArgList, Cardinal*); static void Redisplay(Widget, XEvent*, Region); SeparatorGadgetClassRec separatorGadgetClassRec = { { /* rectObj fields */ (WidgetClass) &menuGadgetClassRec, /* superclass */ "SeparatorGadget", /* class_name */ sizeof(SeparatorGadgetRec), /* widget_size */ NULL, /* class_initialize */ NULL, /* class_part_initialize */ FALSE, /* class_inited */ Initialize, /* initialize */ NULL, /* initialize_hook */ NULL, /* rect1 */ NULL, /* rect2 */ 0, /* rect3 */ resources, /* resources */ XtNumber(resources), /* num_resources */ NULLQUARK, /* xrm_class */ FALSE, /* rect4 */ FALSE, /* rect5 */ FALSE, /* rect6 */ FALSE, /* rect7 */ NULL, /* destroy */ NULL, /* resize */ Redisplay, /* expose */ NULL, /* set_values */ NULL, /* set_values_hook */ XtInheritSetValuesAlmost, /* set_values_almost */ NULL, /* get_values_hook */ NULL, /* rect9 */ XtVersion, /* version */ NULL, /* callback_private */ NULL, /* rect10 */ NULL, /* query_geometry */ NULL, /* rect11 */ NULL, /* extension */ }, { /* menu_g fields */ XtInheritChangeHl, /* change_highlighted */ XtInheritPopdown, /* popdown */ XtInheritNotify, /* notify */ XtInheritPostNotify, /* post_notify */ XtInheritSetActive, /* set_active */ True, /* unhighlight_on_leave */ NULL, /* extension */ } }; WidgetClass separatorGadgetClass = (WidgetClass)&separatorGadgetClassRec; /*************************************************************************/ static void Initialize(Widget grequest, Widget gnew, ArgList args, Cardinal *no_args) { SeparatorGadget new = (SeparatorGadget)gnew; new->rectangle.height = new->separator_g.size; new->rectangle.width = 2 * new->separator_g.internal_width; } static void Redisplay(Widget gw, XEvent *event, Region region) { SeparatorGadget g = (SeparatorGadget)gw; ShadowWidget parent = (ShadowWidget)g->object.parent; int x, width, y, height, sw, old_sw; x = g->rectangle.x + g->separator_g.internal_width; width = g->rectangle.width - 2 * g->separator_g.internal_width; y = g->rectangle.y + g->separator_g.internal_height; height = g->rectangle.height - 2 * g->separator_g.internal_height; sw = g->separator_g.shadow_width; if (width <= 0 || height <= 0) return; old_sw = parent->shadow.shadow_width; parent->shadow.shadow_width = sw; ShadowDrawShadows(parent, x, y, width, height, !parent->shadow.line_mode); parent->shadow.shadow_width = old_sw; width -= 2 * sw; height -= 2 * sw; if (width <= 0 || height <= 0 || parent->shadow.arm_gc == 0) return; XFillRectangle(XtDisplay(parent), XtWindow(parent), parent->shadow.arm_gc, x + sw, y + sw, width, height); } ./knews-1.0b.1/Widgets/SeparatoGP.h100644 1244 1244 1504 6455455537 15424 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #ifndef SeparatoGP_h #define SeparatoGP_h #include "SeparatorG.h" #include "MenuGP.h" typedef struct { XtPointer extension; } SeparatorGadgetClassPart; typedef struct SeparatorGadgetClassRec { RectObjClassPart rect_class; MenuGadgetClassPart menu_g_class; SeparatorGadgetClass separator_g_class; } SeparatorGadgetClassRec; extern SeparatorGadgetClassRec separatorGadgetClassRec; typedef struct { Dimension size; Dimension shadow_width; Dimension internal_width; Dimension internal_height; /* private data */ } SeparatorGadgetPart; typedef struct SeparatorGadgetRec { ObjectPart object; RectObjPart rectangle; MenuGadgetPart menu_g; SeparatorGadgetPart separator_g; } SeparatorGadgetRec; #endif /* SeparatoGP_h */ ./knews-1.0b.1/Widgets/SeparatorG.h100644 1244 1244 1235 6455455537 15467 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #ifndef SeparatorG_h #define SeparatorG_h #ifndef XtCSize #define XtCSize "Size" #endif #ifndef XtCShadowWidth #define XtCShadowWidth "ShadowWidth" #endif #ifndef XtCInternalWidth #define XtCInternalWidth "InternalWidth" #endif #ifndef XtCInternalHeight #define XtCInternalHeight "InternalHeight" #endif #ifndef XtNsize #define XtNsize "size" #endif #ifndef XtNShadowWidth #define XtNShadowWidth "ShadowWidth" #endif typedef struct SeparatorGadgetClassRec* SeparatorGadgetClass; typedef struct SeparatorGadgetRec* SeparatorGadget; extern WidgetClass separatorGadgetClass; #endif /* SeparatorG_h */ ./knews-1.0b.1/Widgets/Shadow.h100644 1244 1244 2032 6455455537 14641 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #ifndef Shadow_h #define Shadow_h #ifndef XtCAllocArmColor #define XtCAllocArmColor "AllocArmColor" #endif #ifndef XtCAllocArmPixmap #define XtCAllocArmPixmap "AllocArmPixmap" #endif #ifndef XtCAllocShadowColors #define XtCAllocShadowColors "AllocShadowColors" #endif #ifndef XtCShadowWidth #define XtCShadowWidth "ShadowWidth" #endif #ifndef XtCUseLineShadows #define XtCUseLineShadows "UseLineShadows" #endif #ifndef XtNallocArmColor #define XtNallocArmColor "allocArmColor" #endif #ifndef XtNallocArmPixmap #define XtNallocArmPixmap "allocArmPixmap" #endif #ifndef XtNallocShadowColors #define XtNallocShadowColors "allocShadowColors" #endif #ifndef XtNshadowWidth #define XtNshadowWidth "shadowWidth" #endif #ifndef XtNuseLineShadows #define XtNuseLineShadows "useLineShadows" #endif typedef struct ShadowClassRec* ShadowWidgetClass; typedef struct ShadowRec* ShadowWidget; extern WidgetClass shadowWidgetClass; extern void ShadowRedrawWidget(Widget); #endif /* Shadow_h */ ./knews-1.0b.1/Widgets/ShadowP.h100644 1244 1244 4137 6455455537 14771 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #ifndef ShadowP_h #define ShadowP_h #include "Shadow.h" #include typedef Boolean (*ShadowAllocShadowColorsProc)(ShadowWidget, XColor*); typedef void (*ShadowAllocShadowPixmapsProc)(ShadowWidget, Pixel); typedef Boolean (*ShadowAllocArmColorProc)(ShadowWidget, XColor*); typedef void (*ShadowAllocArmPixmapProc)(ShadowWidget, Pixel); typedef void (*ShadowAllocGCsProc)(ShadowWidget); #define XtInheritPixelOffset \ 0 /* this is a bit unortodox... */ #define XtInheritAllocShadowColors \ ((ShadowAllocShadowColorsProc)_XtInherit) #define XtInheritAllocShadowPixmaps \ ((ShadowAllocShadowPixmapsProc)_XtInherit) #define XtInheritAllocArmColor \ ((ShadowAllocArmColorProc)_XtInherit) #define XtInheritAllocArmPixmap \ ((ShadowAllocArmPixmapProc)_XtInherit) #define XtInheritAllocGCs \ ((ShadowAllocGCsProc)_XtInherit) typedef struct { Cardinal pixel_offset; Boolean use_arm_for_background; ShadowAllocShadowColorsProc alloc_shadow_colors; ShadowAllocShadowPixmapsProc alloc_shadow_pixmaps; ShadowAllocArmColorProc alloc_arm_color; ShadowAllocArmPixmapProc alloc_arm_pixmap; ShadowAllocGCsProc alloc_gcs; XtPointer extension; } ShadowClassPart; typedef struct ShadowClassRec { CoreClassPart core_class; ShadowClassPart shadow_class; } ShadowClassRec; extern ShadowClassRec shadowClassRec; typedef struct { Dimension shadow_width; Boolean alloc_shadow_colors; Boolean use_lines; Boolean alloc_arm_color; Boolean alloc_arm_pixmap; /* private data */ Pixel light_pixel; Pixmap light_pixmap; GC light_gc; Pixel dark_pixel; Pixmap dark_pixmap; GC dark_gc; Pixel arm_pixel; Pixmap arm_pixmap; GC arm_gc; Boolean line_mode; Boolean alloced_shadow_pixels; Boolean alloced_arm_pixel; } ShadowPart; typedef struct ShadowRec { CorePart core; ShadowPart shadow; } ShadowRec; extern void ShadowDrawShadows(ShadowWidget, Position, Position, Dimension, Dimension, Boolean); #endif /* ShadowP_h */ ./knews-1.0b.1/Widgets/StringG.h100644 1244 1244 1460 6455455540 14767 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #ifndef StringG_h #define StringG_h #ifndef XtCMargin #define XtCMargin "Margin" #endif #ifndef XtCInternalHeight #define XtCInternalHeight "InternalHeight" #endif #ifndef XtCShadowWidth #define XtCShadowWidth "ShadowWidth" #endif #ifndef XtCCommand #define XtCCommand "Command" #endif #ifndef XtNleftMargin #define XtNleftMargin "leftMargin" #endif #ifndef XtNrightMargin #define XtNrightMargin "rightMargin" #endif #ifndef XtNshadowWidth #define XtNshadowWidth "shadowWidth" #endif #ifndef XtNcommand #define XtNcommand "command" #endif typedef struct StringGadgetClassRec* StringGadgetClass; typedef struct StringGadgetRec* StringGadget; extern WidgetClass stringGadgetClass; extern char *StringGadgetCommand(Widget); #endif /* StringG_h */ ./knews-1.0b.1/Widgets/StringGP.h100644 1244 1244 1660 6455455540 15111 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #ifndef StringGP_h #define StringGP_h #include "StringG.h" #include "MenuGP.h" typedef struct { XtPointer extension; } StringGadgetClassPart; typedef struct StringGadgetClassRec { RectObjClassPart rect_class; MenuGadgetClassPart menu_g_class; StringGadgetClassPart string_g_class; } StringGadgetClassRec; extern StringGadgetClassRec stringGadgetClassRec; typedef struct { String command; /* user data */ XFontStruct *font; Pixel foreground_pixel; Dimension left_margin; Dimension right_margin; Dimension internal_height; Dimension shadow_width; /* private data */ GC default_gc; GC gray_gc; Pixmap stipple; } StringGadgetPart; typedef struct StringGadgetRec { ObjectPart object; RectObjPart rectangle; MenuGadgetPart menu_g; StringGadgetPart string_g; } StringGadgetRec; #endif /* StringGP_h */ ./knews-1.0b.1/Widgets/TextField.h100644 1244 1244 4214 6455455540 15302 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #ifndef TextField_h #define TextField_h #ifndef XtCBorderIn #define XtCBorderIn "BorderIn" #endif #ifndef XtCBuffer #define XtCBuffer "Buffer" #endif #ifndef XtCDisplayCaret #define XtCDisplayCaret "DisplayCaret" #endif #ifndef XtCInternalHeight #define XtCInternalHeight "InternalHeight" #endif #ifndef XtCInternalWidth #define XtCInternalWidth "InternalWidth" #endif #ifndef XtCPreferredChars #define XtCPreferredChars "PreferredChars" #endif #ifndef XtCPreferredLines #define XtCPreferredLines "PreferredLines" #endif #ifndef XtCFocusRoot #define XtCFocusRoot "FocusRoot" #endif #ifndef XtCHack #define XtCHack "Hack" #endif #ifndef XtCDebug #define XtCDebug "Debug" #endif #ifndef XtCEchoOff #define XtCEchoOff "EchoOff" #endif #ifndef XtCSingleLine #define XtCSingleLine "SingleLine" #endif #ifndef XtNborderIn #define XtNborderIn "borderIn" #endif #ifndef XtBbuffer #define XtNbuffer "buffer" #endif #ifndef XtNdisplayCaret #define XtNdisplayCaret "displayCaret" #endif #ifndef XtNfocusColor #define XtNfocusColor "focusColor" #endif #ifndef XtNpreferredChars #define XtNpreferredChars "preferredChars" #endif #ifndef XtNpreferredLines #define XtNpreferredLines "preferredLines" #endif #ifndef XtNhighlightForeground #define XtNhighlightForeground "highlightForeground" #endif #ifndef XtNhighlightBackground #define XtNhighlightBackground "highlightBackground" #endif #ifndef XtNfocusRoot #define XtNfocusRoot "focusRoot" #endif #ifndef XtNtabCallback #define XtNtabCallback "tabCallback" #endif #ifndef XtNfocusCallback #define XtNfocusCallback "focusCallback" #endif #ifndef XtNfocusHack #define XtNfocusHack "focusHack" #endif #ifndef XtNprintFocus #define XtNprintFocus "printFocus" #endif #ifndef XtNechoOff #define XtNechoOff "echoOff" #endif #ifndef XtNsingleLine #define XtNsingleLine "singleLine" #endif typedef struct TextFieldClassRec* TextFieldWidgetClass; typedef struct TextFieldRec* TextFieldWidget; extern WidgetClass textFieldWidgetClass; extern void TextFieldSetActive(Widget, int); extern void TextFieldSetBuffer(Widget, char*); extern char *TextFieldGetBuffer(Widget); #endif /* TextField_h */ ./knews-1.0b.1/Widgets/Layout.c.orig100644 1244 1244 56243 6455455540 15652 0ustar kallekalle/* * $XConsortium: Layout.c,v 1.1 91/09/13 18:51:44 keith Exp $ * * Copyright 1991 Massachusetts Institute of Technology * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of M.I.T. not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. M.I.T. makes no representations about the * suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL M.I.T. * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Author: Keith Packard, MIT X Consortium */ #include #include #include #include #include "LayoutP.h" #include #include /***************************************************************************** * * Full instance record declaration * ****************************************************************************/ #define offset(field) XtOffsetOf(LayoutRec, layout.field) static XtResource resources[] = { {XtNlayout, XtCLayout, XtRLayout, sizeof (BoxPtr), offset(layout), XtRLayout, NULL }, {XtNdebug, XtCBoolean, XtRBoolean, sizeof(Boolean), offset(debug), XtRImmediate, (XtPointer) FALSE}, }; #undef offset static void ClassInitialize(), Initialize(); static void Resize(); static Boolean SetValues(); static XtGeometryResult GeometryManager(); static void ChangeManaged(); static void InsertChild(); static XtGeometryResult QueryGeometry (); static Boolean ChildSetValues(); static Widget Widg (); static void GetDesiredSize (); static void LayoutLayout (); static void LayoutGetNaturalSize (); static void LayoutFreeLayout (); #define SuperClass ((ConstraintWidgetClass)&constraintClassRec) LayoutClassRec layoutClassRec = { { /* core class fields */ /* superclass */ (WidgetClass) SuperClass, /* class name */ "Layout", /* size */ sizeof(LayoutRec), /* class_initialize */ ClassInitialize, /* class_part init */ NULL, /* class_inited */ FALSE, /* initialize */ Initialize, /* initialize_hook */ NULL, /* realize */ XtInheritRealize, /* actions */ NULL, /* num_actions */ 0, /* resources */ resources, /* resource_count */ XtNumber(resources), /* xrm_class */ NULLQUARK, /* compress_motion */ FALSE, /* compress_exposure */ FALSE, /* compress_enterleave*/ FALSE, /* visible_interest */ FALSE, /* destroy */ NULL, /* resize */ Resize, /* expose */ NULL, /* set_values */ SetValues, /* set_values_hook */ NULL, /* set_values_almost */ XtInheritSetValuesAlmost, /* get_values_hook */ NULL, /* accept_focus */ NULL, /* version */ XtVersion, /* callback_private */ NULL, /* tm_table */ NULL, /* query_geometry */ QueryGeometry, /* display_accelerator*/ XtInheritDisplayAccelerator, /* extension */ NULL }, { /* composite class fields */ /* geometry_manager */ GeometryManager, /* change_managed */ ChangeManaged, /* insert_child */ InsertChild, /* delete_child */ XtInheritDeleteChild, /* extension */ NULL }, { /* constraint class fields */ /* subresources */ NULL, /* subresource_count */ 0, /* constraint_size */ sizeof(LayoutConstraintsRec), /* initialize */ NULL, /* destroy */ NULL, /* set_values */ NULL, /* extension */ NULL } }; WidgetClass layoutWidgetClass = (WidgetClass) &layoutClassRec; /************************************************************ * * Semi-public routines. * ************************************************************/ /* Function Name: ClassInitialize * Description: The Layout widgets class initialization proc. * Arguments: none. * Returns: none. */ /*ARGSUSED*/ static Boolean CvtStringToLayout (dpy, args, num_args, from, to, converter_data) Display *dpy; XrmValue *args; Cardinal *num_args; XrmValue *from, *to; XtPointer *converter_data; { LayYYsetsource ((char *) from->addr); LayYYsetdest ((BoxPtr *) to->addr); if (LayYYparse () == 0) return TRUE; else return FALSE; } /*ARGSUSED*/ static void DisposeLayout (app, to, data, args, num_args) XtAppContext app; XrmValue *to; XtPointer data; XrmValuePtr args; Cardinal *num_args; { LayoutFreeLayout (* (LayoutPtr *) to->addr); } static void ClassInitialize() { XtSetTypeConverter ( XtRString, XtRLayout, CvtStringToLayout, NULL, (Cardinal)0, XtCacheNone, DisposeLayout ); } /*ARGSUSED*/ static XtGeometryResult GeometryManager(child, request, reply) Widget child; XtWidgetGeometry *request, *reply; { LayoutWidget w = (LayoutWidget) XtParent(child); SubInfoPtr p = SubInfo(child); int bw; Bool changed, bwChanged; bw = p->naturalBw; changed = FALSE; bwChanged = FALSE; if (request->request_mode & CWBorderWidth && request->border_width != child->core.border_width) { p->naturalBw = bw; bw = request->border_width; changed = TRUE; bwChanged = TRUE; } if (bwChanged || request->request_mode & CWWidth && request->width != child->core.width) { p->naturalSize[LayoutHorizontal] = request->width + bw * 2; changed = TRUE; } if (bwChanged || request->request_mode & CWHeight && request->height != child->core.height) { p->naturalSize[LayoutVertical] = request->height + bw * 2; changed = TRUE; } if (changed) LayoutLayout (w, TRUE); return XtGeometryDone; } /* ARGSUSED */ static void Initialize(request, new) Widget request, new; { LayoutWidget w = (LayoutWidget)new; } static void ChangeManaged(gw) Widget gw; { LayoutWidget w = (LayoutWidget) gw; Widget *children; ForAllChildren (w, children) GetDesiredSize (*children); LayoutLayout ((LayoutWidget) w, TRUE); } static void GetDesiredSize (child) Widget child; { XtWidgetGeometry desired; SubInfoPtr p; XtQueryGeometry (child, (XtWidgetGeometry *) NULL, &desired); p = SubInfo (child); p->naturalBw = desired.border_width; p->naturalSize[LayoutHorizontal] = desired.width + desired.border_width * 2; p->naturalSize[LayoutVertical] = desired.height + desired.border_width * 2; } static void InsertChild (child) Widget child; { SubInfoPtr p; int bw; (*SuperClass->composite_class.insert_child) (child); GetDesiredSize (child); } static void Resize(gw) Widget gw; { LayoutLayout ((LayoutWidget) gw, FALSE); } /* ARGSUSED */ static Boolean SetValues(gold, greq, gnew) Widget gold, greq, gnew; { LayoutWidget old = (LayoutWidget) gold, req = (LayoutWidget) greq, new = (LayoutWidget) gnew; Boolean refigure = FALSE; if (old->layout.layout != new->layout.layout) LayoutLayout (new, TRUE); return FALSE; } /* SetValues */ static XtGeometryResult QueryGeometry (gw, request, prefered_return) Widget gw; XtWidgetGeometry *request, *prefered_return; { LayoutWidget w = (LayoutWidget) gw; XtGeometryResult result; XtWidgetGeometry prefered_size; if (request && !(request->request_mode & CWWidth|CWHeight)) return XtGeometryYes; LayoutGetNaturalSize (w, &prefered_size.width, &prefered_size.height); prefered_return->request_mode = 0; result = XtGeometryYes; if (!request) { prefered_return->width = prefered_size.width; prefered_return->height= prefered_size.height; if (prefered_size.width != w->core.width) { prefered_return->request_mode |= CWWidth; result = XtGeometryAlmost; } if (prefered_size.height != w->core.height) { prefered_return->request_mode |= CWHeight; result = XtGeometryAlmost; } } else { if (request->request_mode & CWWidth) { if (prefered_size.width > request->width) { if (prefered_size.width == w->core.width) result = XtGeometryNo; else if (result != XtGeometryNo) { result = XtGeometryAlmost; prefered_return->request_mode |= CWWidth; prefered_return->width = prefered_size.width; } } } if (request->request_mode & CWHeight) { if (prefered_size.height > request->height) { if (prefered_size.height == w->core.height) result = XtGeometryNo; else if (result != XtGeometryNo) { result = XtGeometryAlmost; prefered_return->request_mode |= CWHeight; prefered_return->height = prefered_size.height; } } } } return result; } /* * Layout section. Exports LayoutGetNaturalSize and * LayoutLayout to above section */ static void PrintGlue (g) GlueRec g; { if (g.order == 0 || g.value != 1.0) printf ("%g", g.value); if (g.order > 0) { printf (" inf"); if (g.order > 1); printf (" %d", g.order); } } static void PrintDirection (dir) LayoutDirection dir; { switch (dir) { case LayoutHorizontal: printf ("horizontal"); break; case LayoutVertical: printf ("vertical"); break; default: printf ("Unknown layout direction %d\n", dir); break; } } static void TabTo(level) int level; { while (level--) printf (" "); } static void PrintBox (box, level) BoxPtr box; int level; { BoxPtr child; Widget w; SubInfoPtr p; TabTo (level); printf ("< "); printf (" + "); PrintGlue (box->params.stretch[LayoutHorizontal]); printf (" - "); PrintGlue (box->params.shrink[LayoutHorizontal]); printf (" * "); printf (" + "); PrintGlue (box->params.stretch[LayoutVertical]); printf (" - "); PrintGlue (box->params.shrink[LayoutVertical]); printf (" >"); printf (" size: %d x %d", box->size[0], box->size[1]); printf (" natural: %d x %d ", box->natural[0], box->natural[1]); switch (box->type) { case BoxBox: PrintDirection (box->u.box.dir); printf ("\n"); for (child = box->u.box.firstChild; child; child = child->nextSibling) PrintBox (child, level+1); break; case WidgetBox: printf (" %s\n", XrmQuarkToString (box->u.widget.quark)); break; case GlueBox: printf (" glue\n"); break; case VariableBox: printf (" variable %s\n", XrmQuarkToString (box->u.variable.quark)); break; } } ExprPtr LookupVariable (child, quark) BoxPtr child; XrmQuark quark; { BoxPtr parent, box; while ( (parent = child->parent) ) { for (box = parent->u.box.firstChild; box != child; box = box->nextSibling) { if (box->type == VariableBox && box->u.variable.quark == quark) return box->u.variable.expr; } child = parent; } return 0; } static double Evaluate (l, box, expr, natural) LayoutWidget l; BoxPtr box; ExprPtr expr; double natural; { double left, right, down; Widget widget; SubInfoPtr info; switch (expr->type) { case Constant: return expr->u.constant; case Binary: left = Evaluate (l, box, expr->u.binary.left, natural); right = Evaluate (l, box, expr->u.binary.right, natural); switch (expr->u.binary.op) { case Plus: return left + right; case Minus: return left - right; case Times: return left * right; case Divide: return left / right; case Percent: return right * left / 100.0; } case Unary: down = Evaluate (l, box, expr->u.unary.down, natural); switch (expr->u.unary.op) { case Percent: return natural * down / 100.0; case Minus: return -down; } case Width: widget = QuarkToWidget (l, expr->u.width); if (!widget) return 0; info = SubInfo (widget); return info->naturalSize[LayoutHorizontal]; case Height: widget = QuarkToWidget (l, expr->u.height); if (!widget) return 0; info = SubInfo (widget); return info->naturalSize[LayoutVertical]; case Variable: expr = LookupVariable (box, expr->u.variable); if (!expr) { char buf[256]; sprintf (buf, "Layout: undefined variable %s\n", XrmQuarkToString (expr->u.variable)); XtError (buf); return 0.0; } return Evaluate (l, box, expr, natural); } } static void DisposeExpr (expr) ExprPtr expr; { if (!expr) return; switch (expr->type) { case Constant: break; case Binary: DisposeExpr (expr->u.binary.left); DisposeExpr (expr->u.binary.right); break; case Unary: DisposeExpr (expr->u.unary.down); break; case Width: break; case Height: break; } Dispose (expr); } #define CheckGlue(l, box, glue, n) { \ if (glue.expr) \ glue.value = Evaluate (l, box, glue.expr, n); \ if (glue.order == 0 && glue.value == 0) \ glue.order = -1; \ else if (glue.order == -1 && glue.value != 0) \ glue.order = 0; \ } #define DoStretch(l, box, dir) \ CheckGlue (l, box, box->params.stretch[dir], (double) box->natural[dir]); #define DoShrink(l, box, dir) \ CheckGlue (l, box, box->params.shrink[dir], (double) box->natural[dir]) /* compute the natural sizes of a box */ static void ComputeNaturalSizes (l, box, dir) LayoutWidget l; BoxPtr box; LayoutDirection dir; { BoxPtr child; Widget w; SubInfoPtr info; int minStretchOrder, minShrinkOrder; int shrinkSize; int stretchSize; LayoutDirection thisDir; switch (box->type) { case WidgetBox: w = box->u.widget.widget = QuarkToWidget (l, box->u.widget.quark); if (!w) { box->natural[LayoutHorizontal] = 0; box->natural[LayoutVertical] = 0; } else { info = SubInfo (w); box->natural[LayoutHorizontal] = info->naturalSize[LayoutHorizontal]; box->natural[LayoutVertical] = info->naturalSize[LayoutVertical]; } DoStretch (l, box, dir); DoShrink (l, box, dir); DoStretch (l, box, !dir); DoShrink (l, box, !dir); break; case GlueBox: box->natural[dir] = Evaluate (l, box, box->u.glue.expr, 0.0); box->natural[!dir] = 0; DoStretch (l, box, dir); DoShrink (l, box, dir); break; case BoxBox: thisDir = box->u.box.dir; box->natural[0] = 0; box->natural[1] = 0; minStretchOrder = 100000; minShrinkOrder = 100000; ZeroGlue (box->params.shrink[thisDir]); ZeroGlue (box->params.stretch[thisDir]); box->params.shrink[!thisDir].order = 100000; box->params.stretch[!thisDir].order = 100000; for (child = box->u.box.firstChild; child; child = child->nextSibling) { ComputeNaturalSizes (l, child, thisDir); /* * along box axis: * normal size += child normal size * shrink += child shrink * stretch += child stretch */ box->natural[thisDir] += child->natural[thisDir]; AddGlue (box->params.shrink[thisDir], box->params.shrink[thisDir], child->params.shrink[thisDir]); AddGlue (box->params.stretch[thisDir], box->params.stretch[thisDir], child->params.stretch[thisDir]); /* * normal to box axis: * normal size = maximum child normal size of minimum shrink order * shrink = difference between normal size and minimum shrink * stretch = minimum child stretch */ if (box->natural[!thisDir] >= child->natural[!thisDir]) { if (child->params.stretch[!thisDir].order < minShrinkOrder) { box->natural[!thisDir] = child->natural[!thisDir]; minStretchOrder = child->params.stretch[!thisDir].order; if (child->params.shrink[!thisDir].order < minShrinkOrder) minShrinkOrder = child->params.shrink[!thisDir].order; } } else { if (child->params.shrink[!thisDir].order <= minStretchOrder) { box->natural[!thisDir] = child->natural[!thisDir]; minShrinkOrder = child->params.shrink[!thisDir].order; if (child->params.stretch[!thisDir].order < minStretchOrder) minStretchOrder = child->params.stretch[!thisDir].order; } } MinGlue (box->params.stretch[!thisDir], box->params.stretch[!thisDir], child->params.stretch[!thisDir]); MinGlue (box->params.shrink[!thisDir], box->params.shrink[!thisDir], child->params.shrink[!thisDir]); } if (box->params.shrink[!thisDir].order <= 0) { int minSize; int largestMinSize; largestMinSize = 0; for (child = box->u.box.firstChild; child; child = child->nextSibling) { if (child->params.shrink[!thisDir].order <= 0) { minSize = child->natural[!thisDir] - child->params.shrink[!thisDir].value; if (minSize > largestMinSize) largestMinSize = minSize; } } box->params.shrink[!thisDir].value = box->natural[!thisDir] - largestMinSize; if (box->params.shrink[!thisDir].value == 0) box->params.shrink[!thisDir].order = -1; else box->params.shrink[!thisDir].order = 0; } } } /* given the boxs geometry, set the geometry of the pieces */ #define GluePart(a,b,dist) ((a) ? ((int) (((a) * (dist)) / (b) + \ ((dist >= 0) ? 0.5 : -0.5))) : 0) static Bool ComputeSizes (box) BoxPtr box; { LayoutDirection dir; BoxPtr child; GlueRec stretch; GlueRec shrink; GlueRec totalGlue[2]; double remainingGlue; GluePtr glue; int base; int size; int totalSizes; int totalChange[2]; int change; int remainingChange; Bool shrinking; Bool happy; int i; int maxGlue; dir = box->u.box.dir; size = box->size[dir]; stretch = box->params.stretch[dir]; shrink = box->params.shrink[dir]; /* pick the correct adjustment parameters based on the change direction */ totalChange[0] = size - box->natural[dir]; shrinking = totalChange[0] < 0; totalChange[1] = 0; totalGlue[1].order = 100000; totalGlue[1].value = 0; maxGlue = 1; if (shrinking) { totalGlue[0] = shrink; /* for first-order infinites, shrink it to zero and then * shrink the zero-orders */ if (shrink.order == 1) { totalSizes = 0; remainingGlue = 0; for (child = box->u.box.firstChild; child; child = child->nextSibling) { switch (child->params.shrink[dir].order) { case 0: remainingGlue += child->params.shrink[dir].value; break; case 1: totalSizes += child->natural[dir]; break; } } if (totalSizes < -totalChange[0]) { totalGlue[1] = shrink; totalGlue[0].order = 0; totalGlue[0].value = remainingGlue; totalChange[1] = -totalSizes; totalChange[0] = totalChange[0] - totalChange[1]; maxGlue = 2; } } if (totalGlue[0].order <= 0 && totalChange[0] > totalGlue[0].value) { totalChange[0] = totalGlue[0].value; } } else totalGlue[0] = stretch; /* adjust each box */ totalSizes = 0; remainingGlue = totalGlue[0].value + totalGlue[1].value; remainingChange = totalChange[0] + totalChange[1]; happy = True; for (child = box->u.box.firstChild; child; child = child->nextSibling) { if (shrinking) glue = &child->params.shrink[dir]; else glue = &child->params.stretch[dir]; child->size[dir] = child->natural[dir]; for (i = 0; i < maxGlue; i++) { if (glue->order == totalGlue[i].order) { remainingGlue -= glue->value; if (remainingGlue <= 0) change = remainingChange; else change = GluePart (glue->value, totalGlue[i].value, totalChange[i]); child->size[dir] += change; remainingChange -= change; } } child->size[!dir] = box->size[!dir]; totalSizes += child->size[dir]; if (child->type == BoxBox) if (!ComputeSizes (child)) happy = False; } return totalSizes == box->size[dir] && happy; } static void SetSizes (box, x, y) BoxPtr box; Position x, y; { BoxPtr child; int width, height; int bw; Widget w; SubInfoPtr info; switch (box->type) { case VariableBox: break; case WidgetBox: w = box->u.widget.widget; if (w) { info = (SubInfoPtr) w->core.constraints; width = box->size[LayoutHorizontal]; height = box->size[LayoutVertical]; bw = info->naturalBw; width -= bw * 2; height -= bw * 2; /* Widgets which grow too small are placed off screen */ if (width <= 0 || height <= 0) { width = 1; height = 1; bw = 0; x = -1; y = -1; } XtConfigureWidget (w, x, y, width, height, bw); } break; case GlueBox: break; case BoxBox: for (child = box->u.box.firstChild; child; child = child->nextSibling) { SetSizes (child, x, y); if (box->u.box.dir == LayoutHorizontal) x += child->size[LayoutHorizontal]; else y += child->size[LayoutVertical]; } break; } } static void LayoutFreeLayout (box) BoxPtr box; { BoxPtr child, next; switch (box->type) { case BoxBox: for (child = box->u.box.firstChild; child; child = next) { next = child->nextSibling; LayoutFreeLayout (child); } break; case GlueBox: DisposeExpr (box->u.glue.expr); break; } DisposeExpr (box->params.stretch[LayoutHorizontal].expr); DisposeExpr (box->params.shrink[LayoutHorizontal].expr); DisposeExpr (box->params.stretch[LayoutVertical].expr); DisposeExpr (box->params.shrink[LayoutVertical].expr); Dispose (box); } static void LayoutGetNaturalSize (l, widthp, heightp) LayoutWidget l; Dimension *widthp, *heightp; { BoxPtr box; SubInfoPtr p; Widget child, *children; XtWidgetGeometry desired; box = l->layout.layout; if (box) { ComputeNaturalSizes (l, box, LayoutHorizontal); *widthp = box->natural[LayoutHorizontal]; *heightp = box->natural[LayoutVertical]; } else { *widthp = 0; *heightp = 0; } } static void LayoutLayout (l, attemptResize) LayoutWidget l; Bool attemptResize; { BoxPtr box; Dimension width, height; Dimension prefered_width, prefered_height; box = l->layout.layout; if (!box) return; LayoutGetNaturalSize (l, &prefered_width, &prefered_height); if (l->core.width == 0 || l->core.height == 0) { l->core.width = prefered_width; l->core.height = prefered_height; } box->size[LayoutHorizontal] = l->core.width; box->size[LayoutVertical] = l->core.height; if (!ComputeSizes (box) && attemptResize) { XtMakeResizeRequest ((Widget) l, prefered_width, prefered_height, &width, &height); if (width != box->size[LayoutHorizontal] || height != box->size[LayoutVertical]) { box->size[LayoutHorizontal] = width; box->size[LayoutVertical] = height; ComputeSizes (box); } } if (l->layout.debug) { PrintBox (box, 0); fflush (stdout); } SetSizes (box, 0, 0); } ./knews-1.0b.1/Widgets/Toggle.c100644 1244 1244 20263 6455455540 14650 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include #include #include "Compat.h" #include "ToggleP.h" static XtResource resources[] = { {XtNleftMargin, XtCLeftMargin, XtRDimension, sizeof(Dimension), XtOffsetOf(ToggleRec, knapp.left_margin), XtRImmediate, (XtPointer)24}, {XtNborderWidth, XtCBorderWidth, XtRDimension, sizeof(Dimension), XtOffsetOf(ToggleRec, core.border_width), XtRImmediate, (XtPointer)0}, {XtNshadowWidth, XtCShadowWidth, XtRDimension, sizeof(Dimension), XtOffsetOf(ToggleRec, shadow.shadow_width), XtRImmediate, (XtPointer)1}, #define offset(field) XtOffsetOf(ToggleRec, toggle.field) {XtNtoggleSize, XtCToggleSize, XtRDimension, sizeof(Dimension), offset(toggle_size), XtRImmediate, (XtPointer)12}, {XtNtoggleOffset, XtCToggleOffset, XtRDimension, sizeof(Dimension), offset(toggle_offset), XtRImmediate, (XtPointer)8}, {XtNtoggleShadowWidth, XtCShadowWidth, XtRDimension, sizeof(Dimension), offset(toggle_shadow_width), XtRImmediate, (XtPointer)2}, {XtNset, XtCSet, XtRBoolean, sizeof(Boolean), offset(set), XtRImmediate, (XtPointer)False}, #undef offset }; static void Redisplay(Widget, XEvent*, Region); static Boolean SetValues(Widget, Widget, Widget, ArgList, Cardinal*); static void toggle(Widget, XEvent*, String*, Cardinal*); static void set(Widget, XEvent*, String*, Cardinal*); static void reset(Widget, XEvent*, String*, Cardinal*); static void notify(Widget, XEvent*, String*, Cardinal*); static XtActionsRec actions[] = { {"toggle", toggle}, {"set", set}, {"reset", reset}, {"notify", notify}, }; static char translations[] = ": notify() \n"; ToggleClassRec toggleClassRec = { { /* core fields */ (WidgetClass) &knappClassRec, /* superclass */ "Toggle", /* class_name */ sizeof(ToggleRec), /* widget_size */ NULL, /* class_initialize */ NULL, /* class_part_initialize */ FALSE, /* class_inited */ NULL, /* initialize */ NULL, /* initialize_hook */ XtInheritRealize, /* realize */ actions, /* actions */ XtNumber(actions), /* num_actions */ resources, /* resources */ XtNumber(resources), /* num_resources */ NULLQUARK, /* xrm_class */ TRUE, /* compress_motion */ #if (XtSpecificationRelease < 4) True, /* compress exposure */ #elif (XtSpecificationRelease < 6) XtExposeCompressMaximal, /* compress_exposure */ #else XtExposeCompressMaximal | XtExposeNoRegion, /* compress_exposure*/ #endif TRUE, /* compress_enterleave */ FALSE, /* visible_interest */ NULL, /* destroy */ XtInheritResize, /* resize */ Redisplay, /* expose */ SetValues, /* set_values */ NULL, /* set_values_hook */ XtInheritSetValuesAlmost, /* set_values_almost */ NULL, /* get_values_hook */ NULL, /* accept_focus */ XtVersion, /* version */ NULL, /* callback_private */ translations, /* tm_table */ XtInheritQueryGeometry, /* query_geometry */ XtInheritDisplayAccelerator, /* display_accelerator */ NULL /* extension */ }, { /* shadow fields */ XtInheritPixelOffset, /* pixel_offset */ False, /* use_arm_for_background */ XtInheritAllocShadowColors, /* alloc_shadow_colors */ XtInheritAllocShadowPixmaps, /* alloc_shadow_pixmaps */ XtInheritAllocArmColor, /* alloc_arm_color */ XtInheritAllocArmPixmap, /* alloc_arm_pixmap */ XtInheritAllocGCs, /* alloc_gcs */ NULL, /* extension */ }, { /* knapp fields */ NULL, /* extension */ }, { /* toggle fields */ NULL, /* extension */ } }; WidgetClass toggleWidgetClass = (WidgetClass)&toggleClassRec; /*************************************************************************/ static void draw_toggle(ToggleWidget w) { Dimension sw = w->shadow.shadow_width; int x, y; x = w->toggle.toggle_offset + w->shadow.shadow_width; y = w->core.height - w->toggle.toggle_size; y /= 2; if (w->shadow.arm_gc != 0) { Display *disp = XtDisplay(w); Window win = XtWindow(w); if (w->toggle.set) XFillRectangle(disp, win, w->shadow.arm_gc, x, y, w->toggle.toggle_size, w->toggle.toggle_size); else XClearArea(disp, win, x, y, w->toggle.toggle_size, w->toggle.toggle_size, False); } w->shadow.shadow_width = w->toggle.toggle_shadow_width; ShadowDrawShadows((ShadowWidget)w, x, y, w->toggle.toggle_size, w->toggle.toggle_size, w->toggle.set); w->shadow.shadow_width = sw; } static void call_callbacks(ToggleWidget w) { XtCallbackList c_list = w->knapp.callback; Boolean set = w->toggle.set; if (c_list) XtCallCallbackList((Widget)w, c_list, (XtPointer)&set); } /*************************************************************************/ static void toggle(Widget gw, XEvent *event, String *params, Cardinal *no_params) { ToggleWidget w = (ToggleWidget)gw; w->toggle.set = !w->toggle.set; draw_toggle(w); call_callbacks(w); } static void set(Widget gw, XEvent *event, String *params, Cardinal *no_params) { ToggleWidget w = (ToggleWidget)gw; w->toggle.set = True; draw_toggle(w); call_callbacks(w); } static void reset(Widget gw, XEvent *event, String *params, Cardinal *no_params) { ToggleWidget w = (ToggleWidget)gw; w->toggle.set = False; draw_toggle(w); call_callbacks(w); } static void notify(Widget gw, XEvent *event, String *params, Cardinal *no_params) { ToggleWidget w = (ToggleWidget)gw; XtCallbackList c_list = w->knapp.callback; if (c_list) { Boolean set = w->toggle.set; /* callbacks may change 'set' */ XtCallCallbackList((Widget)w, c_list, (XtPointer)&w->toggle.set); if (set != w->toggle.set) draw_toggle(w); } } /*************************************************************************/ static void Redisplay(Widget gw, XEvent *event, Region region) { ToggleWidget w = (ToggleWidget)gw; knappWidgetClass->core_class.expose((Widget)w, NULL, NULL); if (w->toggle.toggle_shadow_width > 0) draw_toggle(w); } static Boolean SetValues(Widget gcurrent, Widget grequest, Widget gnew, ArgList args, Cardinal *num_args) { ToggleWidget new = (ToggleWidget)gnew; ToggleWidget current = (ToggleWidget)gcurrent; Boolean redisplay = False; if (new->toggle.set != current->toggle.set || new->toggle.toggle_shadow_width != current->toggle.toggle_shadow_width || new->toggle.toggle_size != current->toggle.toggle_size || new->toggle.toggle_offset != current->toggle.toggle_offset) redisplay = True; return redisplay; } /*************************************************************************/ void ToggleSet(Widget gw, int set) { ToggleWidget w = (ToggleWidget)gw; set = !!set; if (w->toggle.set == set) return; w->toggle.set = set; if (XtIsRealized((Widget)w)) draw_toggle(w); } int ToggleGet(Widget gw) { ToggleWidget w = (ToggleWidget)gw; return w->toggle.set; } ./knews-1.0b.1/Widgets/Toggle.h100644 1244 1244 1362 6455455540 14634 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #ifndef Toggle_h #define Toggle_h #ifndef XtCToggleSize #define XtCToggleSize "ToggleSize" #endif #ifndef XtCToggleOffset #define XtCToggleOffset "ToggleOffset" #endif #ifndef XtCSet #define XtCSet "Set" #endif #ifndef XtNtoggleSize #define XtNtoggleSize "toggleSize" #endif #ifndef XtNtoggleOffset #define XtNtoggleOffset "toggleOffset" #endif #ifndef XtNtoggleShadowWidth #define XtNtoggleShadowWidth "toggleShadowWidth" #endif #ifndef XtNset #define XtNset "set" #endif typedef struct ToggleClassRec* ToggleWidgetClass; typedef struct ToggleRec* ToggleWidget; extern WidgetClass toggleWidgetClass; extern void ToggleSet(Widget, int); extern int ToggleGet(Widget); #endif /* Toggle_h */ ./knews-1.0b.1/Widgets/ToggleG.c100644 1244 1244 13737 6455455540 14767 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include #include #include "Compat.h" #include "ToggleGP.h" static XtResource resources[] = { #define offset(field) XtOffsetOf(ToggleGadgetRec, toggle_g.field) {XtNtoggleSize, XtCToggleSize, XtRDimension, sizeof(Dimension), offset(toggle_size), XtRImmediate, (XtPointer)10}, {XtNtoggleOffset, XtCToggleOffset, XtRDimension, sizeof(Dimension), offset(toggle_offset), XtRImmediate, (XtPointer)6}, {XtNtoggleShadowWidth, XtCToggleShadowWidth, XtRDimension, sizeof(Dimension), offset(toggle_shadow_width), XtRImmediate, (XtPointer)2}, {XtNset, XtCSet, XtRBoolean, sizeof(Boolean), offset(set), XtRImmediate, (XtPointer)False}, #undef offset }; static Boolean SetValues(Widget, Widget, Widget, ArgList, Cardinal*); static void Redisplay(Widget, XEvent*, Region); static int Notify(MenuGadget); ToggleGadgetClassRec toggleGadgetClassRec = { { /* rectObj fields */ (WidgetClass) &stringGadgetClassRec, /* superclass */ "ToggleGadget", /* class_name */ sizeof(ToggleGadgetRec), /* widget_size */ NULL, /* class_initialize */ NULL, /* class_part_initialize */ FALSE, /* class_inited */ NULL, /* initialize */ NULL, /* initialize_hook */ NULL, /* rect1 */ NULL, /* rect2 */ 0, /* rect3 */ resources, /* resources */ XtNumber(resources), /* num_resources */ NULLQUARK, /* xrm_class */ FALSE, /* rect4 */ FALSE, /* rect5 */ FALSE, /* rect6 */ FALSE, /* rect7 */ NULL, /* destroy */ NULL, /* resize */ Redisplay, /* expose */ SetValues, /* set_values */ NULL, /* set_values_hook */ XtInheritSetValuesAlmost, /* set_values_almost */ NULL, /* get_values_hook */ NULL, /* rect9 */ XtVersion, /* version */ NULL, /* callback_private */ NULL, /* rect10 */ NULL, /* query_geometry */ NULL, /* rect11 */ NULL, /* extension */ }, { /* menu_g fields */ XtInheritChangeHl, /* change_hl */ XtInheritPopdown, /* popdown */ Notify, /* notify */ XtInheritPostNotify, /* post_notify */ XtInheritSetActive, /* set_active */ False, /* ignore_leave */ NULL, /* extension */ }, { /* string_g fields */ NULL, /* extension */ }, { /* toggle_g fields */ NULL, /* extension */ }, }; WidgetClass toggleGadgetClass = (WidgetClass)&toggleGadgetClassRec; /*************************************************************************/ static void draw_toggle(ToggleGadget g, int clear) { ShadowWidget parent = (ShadowWidget)g->object.parent; Display *disp = XtDisplay(parent); Window win = XtWindow(parent); int sw = parent->shadow.shadow_width; int x, y, size = g->toggle_g.toggle_size; x = g->rectangle.x + g->toggle_g.toggle_offset + g->string_g.shadow_width; y = g->rectangle.y + (int)(g->rectangle.height - size) / 2; if (clear) XClearArea(disp, win, x, y, size, size, False); if (g->toggle_g.set && parent->shadow.arm_gc != 0) XFillRectangle(disp, win, parent->shadow.arm_gc, x, y, size, size); parent->shadow.shadow_width = g->toggle_g.toggle_shadow_width; ShadowDrawShadows(parent, x, y, size, size, g->toggle_g.set); parent->shadow.shadow_width = sw; } /*************************************************************************/ static void Redisplay(Widget gw, XEvent *event, Region region) { ToggleGadget g = (ToggleGadget)gw; stringGadgetClass->core_class.expose((Widget)g, event, region); draw_toggle(g, False); } static Boolean SetValues(Widget gcurrent, Widget grequest, Widget gnew, ArgList args, Cardinal *num_args) { Boolean redisplay = False; return redisplay; } static int Notify(MenuGadget m) { ToggleGadget g = (ToggleGadget)m; XtCallbackList c_list = g->menu_g.callback; Boolean set = !g->toggle_g.set; if (!g->menu_g.inside) return False; g->toggle_g.set = set; draw_toggle(g, !set); if (c_list) XtCallCallbackList((Widget)g, c_list, (XtPointer)&set); return True; } /*************************************************************************/ void ToggleGadgetSet(Widget w, int set) { ToggleGadget g = (ToggleGadget)w; Widget parent = g->object.parent; if (g->toggle_g.set == set) return; g->toggle_g.set = set; if (XtIsRealized(parent) && parent->core.visible) { XClearArea(XtDisplay(parent), XtWindow(parent), g->rectangle.x, g->rectangle.y, g->rectangle.width, g->rectangle.height, False); } } int ToggleGadgetGet(Widget w) { ToggleGadget g = (ToggleGadget)w; return g->toggle_g.set; } ./knews-1.0b.1/Widgets/ToggleG.h100644 1244 1244 1551 6455455540 14743 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #ifndef ToggleG_h #define ToggleG_h #ifndef XtCToggleSize #define XtCToggleSize "ToggleSize" #endif #ifndef XtCToggleOffset #define XtCToggleOffset "ToggleOffset" #endif #ifndef XtCToggleShadowWidth #define XtCToggleShadowWidth "ToggleShadowWidth" #endif #ifndef XtCSet #define XtCSet "Set" #endif #ifndef XtNtoggleSize #define XtNtoggleSize "toggleSize" #endif #ifndef XtNtoggleOffset #define XtNtoggleOffset "toggleOffset" #endif #ifndef XtNtoggleShadowWidth #define XtNtoggleShadowWidth "toggleShadowWidth" #endif #ifndef XtNset #define XtNset "set" #endif typedef struct ToggleGadgetClassRec* ToggleGadgetClass; typedef struct ToggleGadgetRec* ToggleGadget; extern WidgetClass toggleGadgetClass; extern void ToggleGadgetSet(Widget, int); extern int ToggleGadgetGet(Widget); #endif /* ToggleG_h */ ./knews-1.0b.1/Widgets/ToggleGP.h100644 1244 1244 1544 6455455540 15065 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #ifndef ToggleGP_h #define ToggleGP_h #include "ToggleG.h" #include "StringGP.h" typedef struct { XtPointer extension; } ToggleGadgetClassPart; typedef struct ToggleGadgetClassRec { RectObjClassPart rect_class; MenuGadgetClassPart menu_g_class; StringGadgetClassPart string_g_class; ToggleGadgetClassPart toggle_g_part; } ToggleGadgetClassRec; extern ToggleGadgetClassRec toggleGadgetClassRec; typedef struct { Dimension toggle_size; Dimension toggle_offset; Dimension toggle_shadow_width; Boolean set; /* private data */ } ToggleGadgetPart; typedef struct ToggleGadgetRec { ObjectPart object; RectObjPart rectangle; MenuGadgetPart menu_g; StringGadgetPart string_g; ToggleGadgetPart toggle_g; } ToggleGadgetRec; #endif /* ToggleGP_h */ ./knews-1.0b.1/Widgets/ToggleP.h100644 1244 1244 1334 6455455540 14753 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #ifndef ToggleP_h #define ToggleP_h #include "Toggle.h" #include "KnappP.h" typedef struct { XtPointer empty; } ToggleClassPart; typedef struct ToggleClassRec { CoreClassPart core_class; ShadowClassPart shadow_class; KnappClassPart knapp_class; ToggleClassPart toggle_class; } ToggleClassRec; extern ToggleClassRec toggleClassRec; typedef struct { Dimension toggle_size; Dimension toggle_offset; Dimension toggle_shadow_width; /* private data */ Boolean set; } TogglePart; typedef struct ToggleRec { CorePart core; ShadowPart shadow; KnappPart knapp; TogglePart toggle; } ToggleRec; #endif /* ToggleP_h */ ./knews-1.0b.1/Widgets/Util.h100644 1244 1244 2474 6455455540 14335 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #ifndef Util_h #define Util_h #include "Compat.h" #ifndef XtRLong #define XtRLong "Long" #endif #ifndef XtRJustify #define XtRJustify "Justify" #endif typedef enum { JustifyTypeLeft, JustifyTypeCenter, JustifyTypeRight } JustifyType; extern int MyXWidthToChars(XFontStruct*, const char*, int, int); extern int MyXWidthToWChars(XFontStruct*, const XChar2b*, int, int); extern void add_WM_DELETE_WINDOW_callback(Widget, XtCallbackProc, XtPointer); extern int get_event_xy(XEvent*, int*, int*); extern Time get_event_time(XEvent*); extern int is_popped_up(Widget); extern void popup_under_pointer(Widget, XtGrabKind); extern Boolean cvt_string_to_long(Display*, XrmValue*, Cardinal*, XrmValue*, XrmValue*, XtPointer*); extern Boolean cvt_string_to_justify(Display*, XrmValue*, Cardinal*, XrmValue*, XrmValue*, XtPointer*); extern Boolean cvt_std_sel(Widget, Time, Atom*, Atom*, Atom*, XPointer*, unsigned long*, int*); extern Pixmap create_stipple(Screen*); extern void release_stipple(Screen*, Pixmap); extern void black_and_white(Screen*, Visual*, Pixel*, Pixel*); extern Pixel get_black(Widget); extern Visual *get_visual(Widget); extern Atom intern_atom(Display*, char*); #endif /* Util_h */ ./knews-1.0b.1/Widgets/laygram.y100644 1244 1244 13417 6455455540 15114 0ustar kallekalle%{ #include #include #include #include #include #include #include #include "Compat.h" #include "LayoutP.h" static LayoutPtr *dest; %} %union { int ival; XrmQuark qval; BoxPtr bval; BoxParamsPtr pval; GlueRec gval; LayoutDirection lval; ExprPtr eval; Operator oval; } %type box boxes compositebox %type bothparams oneparams %type glue opStretch opShrink %type orientation %type signedExpr simpleExpr expr %token OC CC OA CA OP CP %token NAME %token NUMBER %token INFINITY %token VERTICAL HORIZONTAL %token EQUAL DOLLAR %left PLUS MINUS %left TIMES DIVIDE PERCENTOF %right PERCENT %nonassoc WIDTH HEIGHT %right UMINUS UPLUS %% layout : compositebox { *dest = $1; } ; box : NAME bothparams { BoxPtr box = New(BoxRec); box->nextSibling = 0; box->type = WidgetBox; box->params = *$2; Dispose ($2); box->u.widget.quark = $1; $$ = box; } | signedExpr oneparams { BoxPtr box = New(BoxRec); box->nextSibling = 0; box->type = GlueBox; box->params = *$2; Dispose ($2); box->u.glue.expr = $1; $$ = box; } | NAME EQUAL signedExpr { BoxPtr box = New(BoxRec); /* Thanks to leo@marco.de */ ZeroGlue(box->params.stretch[LayoutHorizontal]); ZeroGlue(box->params.shrink[LayoutHorizontal]); ZeroGlue(box->params.stretch[LayoutVertical]); ZeroGlue(box->params.shrink[LayoutVertical]); box->nextSibling = 0; box->type = VariableBox; box->u.variable.quark = $1; box->u.variable.expr = $3; $$ = box; } | compositebox { $$ = $1; } ; compositebox : orientation OC boxes CC { BoxPtr box = New(BoxRec); BoxPtr child; /* Thanks to leo@marco.de */ ZeroGlue(box->params.stretch[LayoutHorizontal]); ZeroGlue(box->params.shrink[LayoutHorizontal]); ZeroGlue(box->params.stretch[LayoutVertical]); ZeroGlue(box->params.shrink[LayoutVertical]); box->nextSibling = 0; box->parent = 0; box->type = BoxBox; box->u.box.dir = $1; box->u.box.firstChild = $3; for (child = $3; child; child = child->nextSibling) { if (child->type == GlueBox) { child->params.stretch[!$1].expr = 0; child->params.shrink[!$1].expr = 0; child->params.stretch[!$1].order = 100000; child->params.shrink[!$1].order = 100000; child->params.stretch[!$1].value = 1; child->params.shrink[!$1].value = 1; } child->parent = box; } $$ = box; } ; boxes : box boxes { $1->nextSibling = $2; $$ = $1; } | box { $$ = $1; } ; bothparams : OA opStretch opShrink TIMES opStretch opShrink CA { BoxParamsPtr p = New(BoxParamsRec); p->stretch[LayoutHorizontal] = $2; p->shrink[LayoutHorizontal] = $3; p->stretch[LayoutVertical] = $5; p->shrink[LayoutVertical] = $6; $$ = p; } | { BoxParamsPtr p = New(BoxParamsRec); ZeroGlue (p->stretch[LayoutHorizontal]); ZeroGlue (p->shrink[LayoutHorizontal]); ZeroGlue (p->stretch[LayoutVertical]); ZeroGlue (p->shrink[LayoutVertical]); $$ = p; } ; oneparams : OA opStretch opShrink CA { BoxParamsPtr p = New(BoxParamsRec); p->stretch[LayoutHorizontal] = $2; p->shrink[LayoutHorizontal] = $3; p->stretch[LayoutVertical] = $2; p->shrink[LayoutVertical] = $3; $$ = p; } | { BoxParamsPtr p = New(BoxParamsRec); ZeroGlue (p->stretch[LayoutHorizontal]); ZeroGlue (p->shrink[LayoutHorizontal]); ZeroGlue (p->stretch[LayoutVertical]); ZeroGlue (p->shrink[LayoutVertical]); $$ = p; } ; opStretch : PLUS glue { $$ = $2; } | { ZeroGlue ($$); } ; opShrink : MINUS glue { $$ = $2; } | { ZeroGlue ($$); } ; glue : simpleExpr INFINITY { $$.order = $2; $$.expr = $1; } | simpleExpr { $$.order = 0; $$.expr = $1; } | INFINITY { $$.order = $1; $$.expr = 0; $$.value = 1; } ; signedExpr : MINUS simpleExpr %prec UMINUS { $$ = New(ExprRec); $$->type = Unary; $$->u.unary.op = $1; $$->u.unary.down = $2; } | PLUS simpleExpr %prec UPLUS { $$ = $2; } | simpleExpr ; simpleExpr : WIDTH NAME { $$ = New(ExprRec); $$->type = Width; $$->u.width = $2; } | HEIGHT NAME { $$ = New(ExprRec); $$->type = Height; $$->u.height = $2; } | OP expr CP { $$ = $2; } | simpleExpr PERCENT { $$ = New(ExprRec); $$->type = Unary; $$->u.unary.op = $2; $$->u.unary.down = $1; } | NUMBER { $$ = New(ExprRec); $$->type = Constant; $$->u.constant = $1; } | DOLLAR NAME { $$ = New(ExprRec); $$->type = Variable; $$->u.variable = $2; } ; expr : expr PLUS expr { binary: ; $$ = New(ExprRec); $$->type = Binary; $$->u.binary.op = $2; $$->u.binary.left = $1; $$->u.binary.right = $3; } | expr MINUS expr { goto binary; } | expr TIMES expr { goto binary; } | expr DIVIDE expr { goto binary; } | expr PERCENTOF expr { goto binary; } | MINUS expr %prec UMINUS { $$ = New(ExprRec); $$->type = Unary; $$->u.unary.op = $1; $$->u.unary.down = $2; } | PLUS expr %prec UPLUS { $$ = $2; } | simpleExpr ; orientation : VERTICAL { $$ = LayoutVertical; } | HORIZONTAL { $$ = LayoutHorizontal; } ; %% int yywrap (void) { return 1; } void yysetdest(LayoutPtr *c) { dest = c; } ./knews-1.0b.1/Widgets/laylex.l100644 1244 1244 3631 6455455540 14716 0ustar kallekalle%{ #undef input #undef unput #include #include #include #include #include "LayoutP.h" #include "laygram.h" static char *yysourcebase, *yysource; #define input() (*yysource++) #define unput(c) (--yysource) %} %% vertical return VERTICAL; horizontal return HORIZONTAL; "{" return OC; "}" return CC; "(" return OP; ")" return CP; "<" return OA; ">" return CA; infinity { yylval.ival = 1; return INFINITY; } inff* { yylval.ival = count(yytext, 'f'); return INFINITY; } [0-9][0-9]* { yylval.ival = atoi(yytext); return NUMBER; } "=" { return EQUAL; } "$" { return DOLLAR; } "+" { yylval.oval = Plus; return PLUS; } "-" { yylval.oval = Minus; return MINUS; } "*" { yylval.oval = Times; return TIMES; } "/" { yylval.oval = Divide; return DIVIDE; } "%" { yylval.oval = Percent; return PERCENT; } %[ \t\n]*of { yylval.oval = Percent; return PERCENTOF; } width return WIDTH; height return HEIGHT; \\[a-zA-Z_][a-zA-Z0-9_]* { yytext[yyleng - 1] = '\0'; yylval.qval = XrmStringToQuark (yytext+1); return NAME; } [a-zA-Z_][a-zA-Z0-9_]* { yytext[yyleng - 1] = '\0'; yylval.qval = XrmStringToQuark (yytext); return NAME; } " " ; "\t" ; "\n" ; . fprintf (stderr, "ignoring %c\n", *yytext); %% static int count (s, c) char *s; char c; { int i = 0; while (*s) if (*s++ == c) i++; return i; } yysetsource(s) char *s; { yysourcebase = yysource = s; } yyerror(s) char *s; { char *t; fprintf (stderr, "%s\n", s); t = yysource - 50; if (t < yysourcebase) t = yysourcebase; while (*t && t < yysource + 50) { if (t == yysource) putc ('@', stderr); putc (*t++, stderr); } if (t == yysource) putc ('@', stderr); if (!*t) fprintf (stderr, ""); fprintf (stderr, "\n"); } ./knews-1.0b.1/Widgets/laylex_new.c100644 1244 1244 11476 6455455540 15604 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include #include #include #include #include #include #include "Compat.h" #include "LayoutP.h" #include "laygram.h" static char *yysourcebase, *yysource; #define input() (*yysource++) #define unput(c) (--yysource) static unsigned char is_word_sep[256] = {0,}; static unsigned int word_sep_inited = False; static int word_sep(unsigned char c) { return c < 256 && is_word_sep[c]; } static void init_word_sep(void) { word_sep_inited = True; is_word_sep['\0'] = True; is_word_sep['\t'] = True; is_word_sep['\n'] = True; is_word_sep[' '] = True; is_word_sep['$'] = True; is_word_sep['%'] = True; is_word_sep['('] = True; is_word_sep[')'] = True; is_word_sep['*'] = True; is_word_sep['+'] = True; is_word_sep['-'] = True; is_word_sep['/'] = True; is_word_sep['<'] = True; is_word_sep['='] = True; is_word_sep['>'] = True; is_word_sep['['] = True; is_word_sep['\\'] = True; is_word_sep[']'] = True; is_word_sep['{'] = True; is_word_sep['}'] = True; } int yylex(void) { char buffer[256]; char c; char *p; int n; for (;;) { /* so that we can do continue to skip e.g. whitespace */ c = input(); switch (c) { case '\0': return 0; case ' ': case '\t': case '\n': continue; case '{': return OC; case '}': return CC; case '(': return OP; case ')': return CP; case '<': return OA; case '>': return CA; case '=': return EQUAL; case '$': return DOLLAR; case '+': yylval.oval = Plus; return PLUS; case '-': yylval.oval = Minus; return MINUS; case '*': yylval.oval = Times; return TIMES; case '/': yylval.oval = Divide; return DIVIDE; case '%': yylval.oval = Percent; while (*yysource == ' ' || *yysource == '\t' || *yysource == '\n') yysource++; if (yysource[0] == 'o' && yysource[1] == 'f' && word_sep(yysource[2])) { yysource += 2; return PERCENTOF; } return PERCENT; case '_': case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': break; case 'h': if (strncmp(yysource, "orizontal", 9) == 0 && word_sep(yysource[9])) { yysource += 9; return HORIZONTAL; } if (strncmp(yysource, "eight", 5) == 0 && word_sep(yysource[5])) { yysource += 5; return HEIGHT; } break; case 'i': if (yysource[0] != 'n' || yysource[1] != 'f') break; if (strncmp(yysource, "nfinity", 7) == 0 && word_sep(yysource[7])) { yysource += 7; yylval.ival = 1; return INFINITY; } for (p = yysource + 2 ; *p == 'f' ; p++); if (word_sep(*p)) { yylval.ival = p - yysource - 1; yysource = p; return INFINITY; } break; case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': if (strncmp(yysource, "ertical", 7) == 0 && word_sep(yysource[7])) { yysource += 7; return VERTICAL; } break; case 'w': if (strncmp(yysource, "idth", 4) == 0 && word_sep(yysource[4])) { yysource += 4; return WIDTH; } break; case 'x': case 'y': case 'z': break; case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': break; case '\\': c = input(); break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': yylval.ival = atoi(yysource - 1); while (*yysource >= '0' && *yysource <= '9') yysource++; return NUMBER; default: fprintf(stderr, "ignoring %c\n", c); continue; } buffer[0] = c; for (n = 1 ; n < sizeof(buffer) ; n++) { c = input(); if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_') buffer[n] = c; else break; } if (n >= sizeof(buffer)) return 0; if (!word_sep(c)) return 0; unput(c); buffer[n] = '\0'; yylval.qval = XrmStringToQuark(buffer); return NAME; } } int yysetsource(char *s) { yysourcebase = yysource = s; if (!word_sep_inited) init_word_sep(); return 0; } int yyerror(char *s) { char *t; fprintf (stderr, "%s\n", s); t = yysource - 50; if (t < yysourcebase) t = yysourcebase; while (*t && t < yysource + 50) { if (t == yysource) fputc('@', stderr); fputc(*t++, stderr); } if (t == yysource) fputc('@', stderr); if (!*t) fputs("", stderr); fputc('\n', stderr); return 0; } ./knews-1.0b.1/Widgets/UtilI.h100644 1244 1244 10663 6455455540 14465 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. * * Modified from . */ /* Copyright (c) 1984, 1985, 1987, 1989 X Consortium Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of the X Consortium shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from the X Consortium. */ #define CI_NONEXISTCHAR(cs) ((cs)->width == 0 && \ ((cs)->rbearing | (cs)->lbearing | \ (cs)->ascent | (cs)->descent ) == 0) /* * CI_GET_CHAR_INFO_1D - return the charinfo struct for the indicated 8bit * character. If the character is in the column and exists, then return the * appropriate metrics (note that fonts with common per-character metrics will * return min_bounds). If none of these hold true, try again with the default * char. */ #define CI_GET_CHAR_INFO_1D(fs,col,def,cs) \ do { \ unsigned int tmp = col - fs->min_char_or_byte2; \ cs = def; \ if (tmp <= fs->max_char_or_byte2 - fs->min_char_or_byte2) \ if (fs->per_char == NULL) \ cs = &fs->min_bounds; \ else { \ cs = fs->per_char + tmp; \ if (CI_NONEXISTCHAR(cs)) \ cs = def; \ } \ } while (0) #define CI_GET_DEFAULT_INFO_1D(fs,cs) \ CI_GET_CHAR_INFO_1D (fs, fs->default_char, NULL, cs) /* * CI_GET_CHAR_INFO_2D - return the charinfo struct for the indicated row and * column. This is used for fonts that have more than row zero. */ #define CI_GET_CHAR_INFO_2D(fs,row,col,def,cs) \ do { \ unsigned int tmp1 = row - fs->min_byte1; \ unsigned int tmp2 = col - fs->min_char_or_byte2; \ cs = def; \ if (tmp1 <= fs->max_byte1 - fs->min_byte1 && \ tmp2 <= fs->max_char_or_byte2 - fs->min_char_or_byte2) \ if (fs->per_char == NULL) \ cs = &fs->min_bounds; \ else { \ cs = fs->per_char + tmp2 + tmp1 * \ (fs->max_char_or_byte2 - fs->min_char_or_byte2 + 1); \ if (CI_NONEXISTCHAR(cs)) \ cs = def; \ } \ } while (0) #define CI_GET_DEFAULT_INFO_2D(fs,cs) \ { \ unsigned int r = (fs->default_char >> 8); \ unsigned int c = (fs->default_char & 0xff); \ CI_GET_CHAR_INFO_2D (fs, r, c, NULL, cs); \ } ./knews-1.0b.1/Widgets/laygram.y.orig100644 1244 1244 12532 6455455540 16050 0ustar kallekalle%{ #include #include #include #include #include #include #include #include #include "LayoutP.h" static LayoutPtr *dest; %} %union { int ival; XrmQuark qval; BoxPtr bval; BoxParamsPtr pval; GlueRec gval; LayoutDirection lval; ExprPtr eval; Operator oval; } %type box boxes compositebox %type bothparams oneparams %type glue opStretch opShrink %type orientation %type signedExpr simpleExpr expr %token OC CC OA CA OP CP %token NAME %token NUMBER %token INFINITY %token VERTICAL HORIZONTAL %token EQUAL DOLLAR %left PLUS MINUS %left TIMES DIVIDE PERCENTOF %right PERCENT %nonassoc WIDTH HEIGHT %right UMINUS UPLUS %% layout : compositebox { *dest = $1; } ; box : NAME bothparams { BoxPtr box = New(BoxRec); box->nextSibling = 0; box->type = WidgetBox; box->params = *$2; Dispose ($2); box->u.widget.quark = $1; $$ = box; } | signedExpr oneparams { BoxPtr box = New(BoxRec); box->nextSibling = 0; box->type = GlueBox; box->params = *$2; Dispose ($2); box->u.glue.expr = $1; $$ = box; } | NAME EQUAL signedExpr { BoxPtr box = New(BoxRec); box->nextSibling = 0; box->type = VariableBox; box->u.variable.quark = $1; box->u.variable.expr = $3; $$ = box; } | compositebox { $$ = $1; } ; compositebox : orientation OC boxes CC { BoxPtr box = New(BoxRec); BoxPtr child; box->nextSibling = 0; box->parent = 0; box->type = BoxBox; box->u.box.dir = $1; box->u.box.firstChild = $3; for (child = $3; child; child = child->nextSibling) { if (child->type == GlueBox) { child->params.stretch[!$1].expr = 0; child->params.shrink[!$1].expr = 0; child->params.stretch[!$1].order = 100000; child->params.shrink[!$1].order = 100000; child->params.stretch[!$1].value = 1; child->params.shrink[!$1].value = 1; } child->parent = box; } $$ = box; } ; boxes : box boxes { $1->nextSibling = $2; $$ = $1; } | box { $$ = $1; } ; bothparams : OA opStretch opShrink TIMES opStretch opShrink CA { BoxParamsPtr p = New(BoxParamsRec); p->stretch[LayoutHorizontal] = $2; p->shrink[LayoutHorizontal] = $3; p->stretch[LayoutVertical] = $5; p->shrink[LayoutVertical] = $6; $$ = p; } | { BoxParamsPtr p = New(BoxParamsRec); ZeroGlue (p->stretch[LayoutHorizontal]); ZeroGlue (p->shrink[LayoutHorizontal]); ZeroGlue (p->stretch[LayoutVertical]); ZeroGlue (p->shrink[LayoutVertical]); $$ = p; } ; oneparams : OA opStretch opShrink CA { BoxParamsPtr p = New(BoxParamsRec); p->stretch[LayoutHorizontal] = $2; p->shrink[LayoutHorizontal] = $3; p->stretch[LayoutVertical] = $2; p->shrink[LayoutVertical] = $3; $$ = p; } | { BoxParamsPtr p = New(BoxParamsRec); ZeroGlue (p->stretch[LayoutHorizontal]); ZeroGlue (p->shrink[LayoutHorizontal]); ZeroGlue (p->stretch[LayoutVertical]); ZeroGlue (p->shrink[LayoutVertical]); $$ = p; } ; opStretch : PLUS glue { $$ = $2; } | { ZeroGlue ($$); } ; opShrink : MINUS glue { $$ = $2; } | { ZeroGlue ($$); } ; glue : simpleExpr INFINITY { $$.order = $2; $$.expr = $1; } | simpleExpr { $$.order = 0; $$.expr = $1; } | INFINITY { $$.order = $1; $$.expr = 0; $$.value = 1; } ; signedExpr : MINUS simpleExpr %prec UMINUS { $$ = New(ExprRec); $$->type = Unary; $$->u.unary.op = $1; $$->u.unary.down = $2; } | PLUS simpleExpr %prec UPLUS { $$ = $2; } | simpleExpr ; simpleExpr : WIDTH NAME { $$ = New(ExprRec); $$->type = Width; $$->u.width = $2; } | HEIGHT NAME { $$ = New(ExprRec); $$->type = Height; $$->u.height = $2; } | OP expr CP { $$ = $2; } | simpleExpr PERCENT { $$ = New(ExprRec); $$->type = Unary; $$->u.unary.op = $2; $$->u.unary.down = $1; } | NUMBER { $$ = New(ExprRec); $$->type = Constant; $$->u.constant = $1; } | DOLLAR NAME { $$ = New(ExprRec); $$->type = Variable; $$->u.variable = $2; } ; expr : expr PLUS expr { binary: ; $$ = New(ExprRec); $$->type = Binary; $$->u.binary.op = $2; $$->u.binary.left = $1; $$->u.binary.right = $3; } | expr MINUS expr { goto binary; } | expr TIMES expr { goto binary; } | expr DIVIDE expr { goto binary; } | expr PERCENTOF expr { goto binary; } | MINUS expr %prec UMINUS { unary: ; $$ = New(ExprRec); $$->type = Unary; $$->u.unary.op = $1; $$->u.unary.down = $2; } | PLUS expr %prec UPLUS { $$ = $2; } | simpleExpr ; orientation : VERTICAL { $$ = LayoutVertical; } | HORIZONTAL { $$ = LayoutHorizontal; } ; %% yywrap () { return 1; } yysetdest (c) LayoutPtr *c; { dest = c; } ./knews-1.0b.1/Widgets/CloseSh.h100644 1244 1244 676 6455455540 14742 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #ifndef CloseSh_h #define CloseSh_h #ifndef XtNcursor #define XtNcursor "cursor" #endif #ifndef XtNcloseCallback #define XtNcloseCallback "closeCallback" #endif typedef struct CloseShellClassRec* CloseShellWidgetClass; typedef struct CloseShellRec* CloseShellWidget; extern WidgetClass closeShellWidgetClass; extern void CloseShellSetCursor(Widget, Cursor); #endif /* CloseSh_h */ ./knews-1.0b.1/Widgets/TextFieldP.h100644 1244 1244 3302 6455455540 15417 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #ifndef TextFieldP_h #define TextFieldP_h #include "TextField.h" #include "ScrollableP.h" typedef struct { XtPointer empty; } TextFieldClassPart; typedef struct TextFieldClassRec { CoreClassPart core_class; ShadowClassPart shadow_class; ScrollableClassPart scrollable_class; TextFieldClassPart textfield_class; } TextFieldClassRec; extern TextFieldClassRec textFieldClassRec; typedef struct { char *buf; long len; } LineBuf; typedef struct { String buffer; /* pseudo resource */ Pixel fg_pixel; Pixel focus_pixel; Pixel highlight_fg; Pixel highlight_bg; XFontStruct *font; XtCallbackList callback; XtCallbackList tab_callback; XtCallbackList focus_callback; Widget focus_root; int pref_chars; int pref_lines; Dimension internal_height; Dimension internal_width; Boolean single_line; Boolean border_in; Boolean display_caret; Boolean focus_hack; Boolean print_focus; /* for debugging purposes */ Boolean echo_off; /* private data */ GC gc; GC h_gc; Atom curr_sel; Time sel_time; LineBuf *lines; long n_lines; long sel_start_x; long sel_start_y; long sel_stop_x; long sel_stop_y; long multiply; long caret_x; long caret_y; long mark_x; long mark_y; int char_w; int char_h; Boolean waiting_for_sel; Boolean active; Boolean sel_set; } TextFieldPart; typedef struct TextFieldRec { CorePart core; ShadowPart shadow; ScrollablePart scrollable; TextFieldPart textfield; } TextFieldRec; #endif /* TextFieldP_h */ ./knews-1.0b.1/Widgets/Scrollable.h100644 1244 1244 1522 6455455540 15473 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #ifndef Scrollable_h #define Scrollable_h #ifndef XtCScrBar #define XtCScrBar "ScrBar" #endif #ifndef XtNhBar #define XtNhBar "hBar" #endif #ifndef XtNvBar #define XtNvBar "vBar" #endif typedef struct ScrollableClassRec* ScrollableWidgetClass; typedef struct ScrollableRec* ScrollableWidget; extern WidgetClass scrollableWidgetClass; extern void ScrollableSetHBar(Widget, Widget); extern void ScrollableSetVBar(Widget, Widget); extern void ScrollableSuspend(Widget); extern void ScrollableResume(Widget); extern void ScrollableSetHPos(Widget, long); extern void ScrollableSetVPos(Widget, long); extern long ScrollableGetVPos(Widget); extern long ScrollableGetVShown(Widget); extern long ScrollableGetVSize(Widget); extern void ScrollablePage(Widget, float); #endif /* Scrollable_h */ ./knews-1.0b.1/Widgets/Login.h100644 1244 1244 2155 6455455540 14464 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #ifndef Login_h #define Login_h #ifndef XtCMessage #define XtCMessage "Message" #endif #ifndef XtNmessage #define XtNmessage "message" #endif #ifndef XtNuserNameBuffer #define XtNuserNameBuffer "userNameBuffer" #endif #ifndef XtNpassWordBuffer #define XtNpassWordBuffer "passWordBuffer" #endif #ifndef XtNuserNameLabel #define XtNuserNameLabel "userNameLabel" #endif #ifndef XtNpassWordLabel #define XtNpassWordLabel "passWordLabel" #endif #ifndef XtNleftLabel #define XtNleftLabel "leftLabel" #endif #ifndef XtNmiddleLabel #define XtNmiddleLabel "middleLabel" #endif #ifndef XtNrightLabel #define XtNrightLabel "rightLabel" #endif #ifndef XtNfieldWidth #define XtNfieldWidth "fieldWidth" #endif typedef struct LoginClassRec* LoginWidgetClass; typedef struct LoginRec* LoginWidget; extern WidgetClass loginWidgetClass; typedef enum { LoginReplyLeft, LoginReplyMiddle, LoginReplyRight, LoginReplyEnter, LoginReplyClose } LoginReply; typedef struct { LoginReply reply; char *username; char *password; } LoginReport; #endif /* Login_h */ ./knews-1.0b.1/Widgets/CloseShP.h100644 1244 1244 2013 6455455541 15066 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #ifndef CloseShP_h #define CloseShP_h #include "CloseSh.h" #include #define XtInheritCloseWindow ((XtWidgetProc)_XtInherit) typedef struct { XtWidgetProc close_window; XtPointer empty; } CloseShellClassPart; typedef struct CloseShellClassRec { CoreClassPart core_class; CompositeClassPart composite_class; ShellClassPart shell_class; WMShellClassPart wm_shell_class; VendorShellClassPart vendor_shell_class; TransientShellClassPart transient_shell_class; CloseShellClassPart close_shell_class; } CloseShellClassRec; extern CloseShellClassRec closeShellClassRec; typedef struct { Cursor cursor; XtCallbackList close_callback; } CloseShellPart; typedef struct CloseShellRec { CorePart core; CompositePart composite; ShellPart shell; WMShellPart wm; VendorShellPart vendor; TransientShellPart transient; CloseShellPart close_shell; } CloseShellRec; #endif /* CloseShP_h */ ./knews-1.0b.1/Widgets/CloseSh.c100644 1244 1244 15420 6455455541 14767 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include #include #include "Compat.h" #include "Util.h" #include "CloseShP.h" static XtResource resources[] = { {XtNinput, XtCInput, XtRBool, sizeof(Bool), XtOffsetOf(CloseShellRec, wm.wm_hints.input), XtRImmediate, (XtPointer)True}, {XtNallowShellResize, XtCAllowShellResize, XtRBoolean, sizeof(Boolean), XtOffsetOf(CloseShellRec, shell.allow_shell_resize), XtRImmediate, (XtPointer)True}, #define offset(field) XtOffsetOf(CloseShellRec, close_shell.field) {XtNcursor, XtCCursor, XtRCursor, sizeof(Cursor), offset(cursor), XtRString, (XtPointer)"top_left_arrow"}, {XtNcloseCallback, XtCCallback, XtRCallback, sizeof(XtCallbackList), offset(close_callback), XtRImmediate, (XtPointer)NULL}, #undef offset }; static void ClassPartInitialize(WidgetClass); static void Realize(Widget, XtValueMask*, XSetWindowAttributes*); static Boolean SetValues(Widget, Widget, Widget, ArgList, Cardinal*); static void close_window(Widget, XEvent*, String*, Cardinal*); static XtActionsRec actions[] = { {"close-window", close_window}, }; static char translations[] = "WM_PROTOCOLS: close-window() \n"; CloseShellClassRec closeShellClassRec = { { /* core fields */ (WidgetClass) &transientShellClassRec, /* superclass */ "CloseShell", /* class_name */ sizeof(CloseShellRec), /* widget_size */ NULL, /* class_initialize */ ClassPartInitialize, /* class_part_initialize */ FALSE, /* class_inited */ NULL, /* initialize */ NULL, /* initialize_hook */ Realize, /* realize */ actions, /* actions */ XtNumber(actions), /* num_actions */ resources, /* resources */ XtNumber(resources), /* num_resources */ NULLQUARK, /* xrm_class */ TRUE, /* compress_motion */ FALSE, /* compress_exposure */ TRUE, /* compress_enterleave */ FALSE, /* visible_interest */ NULL, /* destroy */ XtInheritResize, /* resize */ NULL, /* expose */ SetValues, /* set_values */ NULL, /* set_values_hook */ XtInheritSetValuesAlmost, /* set_values_almost */ NULL, /* get_values_hook */ NULL, /* accept_focus */ XtVersion, /* version */ NULL, /* callback_private */ translations, /* tm_table */ NULL, /* query_geometry */ XtInheritDisplayAccelerator, /* display_accelerator */ NULL /* extension */ }, { /* composite fields */ XtInheritGeometryManager, /* geometry_manager */ XtInheritChangeManaged, /* change_managed */ XtInheritInsertChild, /* insert_child */ XtInheritDeleteChild, /* delete_child */ NULL, /* extension */ }, { /* shell fields */ NULL, /* extension */ }, { /* wm shell fields */ NULL, /* extension */ }, { /* vendor shell fields */ NULL, /* extension */ }, { /* transient shell fields */ NULL, /* extension */ }, { /* close_shell fields */ NULL, /* close_window */ NULL, /* extension */ } }; WidgetClass closeShellWidgetClass = (WidgetClass)&closeShellClassRec; /*************************************************************************/ static void close_window(Widget gw, XEvent *event, String *params, Cardinal *no_params) { CloseShellWidget w = (CloseShellWidget)gw; CloseShellWidgetClass class; class = (CloseShellWidgetClass)XtClass(w); if (!class->close_shell_class.close_window) { XtCallCallbackList((Widget)w, w->close_shell.close_callback, NULL); return; } if (event->type == ClientMessage) { Display *disp = XtDisplay(w); Atom wm_delete_window = intern_atom(disp, "WM_DELETE_WINDOW"); Atom wm_protocols = intern_atom(disp, "WM_PROTOCOLS"); if (event->xclient.message_type == wm_protocols && event->xclient.data.l[0] == wm_delete_window) class->close_shell_class.close_window((Widget)w); } } /*************************************************************************/ static void ClassPartInitialize(WidgetClass gclass) { CloseShellWidgetClass class, super; class = (CloseShellWidgetClass)gclass; super = (CloseShellWidgetClass)class->core_class.superclass; if (class->close_shell_class.close_window == XtInheritCloseWindow) class->close_shell_class.close_window = super->close_shell_class.close_window; } static void Realize(Widget gw, XtValueMask *mask, XSetWindowAttributes *attributes) { CloseShellWidget w = (CloseShellWidget)gw; Display *disp = XtDisplay(w); Atom wm_delete_window; if (w->close_shell.cursor != None) { *mask |= CWCursor; attributes->cursor = w->close_shell.cursor; } transientShellWidgetClass->core_class.realize((Widget)w, mask, attributes); wm_delete_window = intern_atom(disp, "WM_DELETE_WINDOW"); XSetWMProtocols(disp, XtWindow(w), &wm_delete_window, 1); } static Boolean SetValues(Widget gcurrent, Widget grequest, Widget gnew, ArgList args, Cardinal *num_args) { Boolean redisplay = False; CloseShellWidget new = (CloseShellWidget)gnew; CloseShellWidget current = (CloseShellWidget)gcurrent; if (new->close_shell.cursor != current->close_shell.cursor) CloseShellSetCursor((Widget)new, new->close_shell.cursor); return redisplay; } /*************************************************************************/ void CloseShellSetCursor(Widget gw, Cursor cursor) { CloseShellWidget w = (CloseShellWidget)gw; Display *disp = XtDisplay(w); Window win = XtWindow(w); w->close_shell.cursor = cursor; if (XtIsRealized((Widget)w)) XDefineCursor(disp, win, cursor); } ./knews-1.0b.1/Widgets/ScrollableP.h100644 1244 1244 2575 6455455541 15625 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #ifndef ScrollableP_h #define ScrollableP_h #include "Scrollable.h" #include "ScrBar.h" #include "ShadowP.h" typedef void (*ScrollableSetPosProc)(ScrollableWidget, long); typedef void (*ScrollableSuspendHook)(ScrollableWidget); #define XtInheritScrollableSetPos ((ScrollableSetPosProc)_XtInherit) #define XtInheritScrollableSuspendHook ((ScrollableSuspendHook)_XtInherit) typedef struct { ScrollableSetPosProc set_hpos; ScrollableSetPosProc set_vpos; ScrollableSuspendHook suspend_hook; XtPointer extension; } ScrollableClassPart; typedef struct ScrollableClassRec { CoreClassPart core_class; ShadowClassPart shadow_class; ScrollableClassPart scrollable_class; } ScrollableClassRec; extern ScrollableClassRec scrollableClassRec; typedef struct { Widget h_bar; Widget v_bar; /* private */ long pos_x; long shown_x; long width; long pos_y; long shown_y; long height; int suspended; } ScrollablePart; typedef struct ScrollableRec { CorePart core; ShadowPart shadow; ScrollablePart scrollable; } ScrollableRec; extern void ScrollableFitHBar(ScrollableWidget); extern void ScrollableFitVBar(ScrollableWidget); extern void ScrollableHFromGeometry(ScrollableWidget); extern void ScrollableVFromGeometry(ScrollableWidget); #endif /* ScrollableP_h */ ./knews-1.0b.1/Widgets/LoginP.h100644 1244 1244 2531 6455455541 14603 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #ifndef LoginP_h #define LoginP_h #include "Login.h" #include "CloseShP.h" typedef struct { XtPointer empty; } LoginClassPart; typedef struct LoginClassRec { CoreClassPart core_class; CompositeClassPart composite_class; ShellClassPart shell_class; WMShellClassPart wm_shell_class; VendorShellClassPart vendor_shell_class; TransientShellClassPart transient_shell_class; CloseShellClassPart close_shell_class; LoginClassPart login_class; } LoginClassRec; extern LoginClassRec loginClassRec; typedef struct { XtCallbackList callback; String message; String username_buffer; String password_buffer; String username_label; String password_label; String left_label; String middle_label; String right_label; Cardinal field_width; /* private data */ Widget layout; Widget message_widget; Widget username_field; Widget password_field; Widget left_knapp; Widget middle_knapp; Widget right_knapp; } LoginPart; typedef struct LoginRec { CorePart core; CompositePart composite; ShellPart shell; WMShellPart wm; VendorShellPart vendor; TransientShellPart transient; CloseShellPart close_shell; LoginPart login; } LoginRec; #endif /* LoginP_h */ ./knews-1.0b.1/Widgets/Login.c100644 1244 1244 26633 6455455541 14507 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include #include #include "Compat.h" #include "Knapp.h" #include "Layout.h" #include "Message.h" #include "TextField.h" #include "Util.h" #include "LoginP.h" static XtResource resources[] = { #define offset(field) XtOffsetOf(LoginRec, login.field) {XtNcallback, XtCCallback, XtRCallback, sizeof(XtCallbackList), offset(callback), XtRCallback, (XtPointer)NULL}, {XtNmessage, XtCMessage, XtRString, sizeof(String), offset(message), XtRImmediate, (XtPointer)""}, {XtNuserNameBuffer, XtCBuffer, XtRString, sizeof(String), offset(username_buffer), XtRImmediate, (XtPointer)""}, {XtNpassWordBuffer, XtCBuffer, XtRString, sizeof(String), offset(password_buffer), XtRImmediate, (XtPointer)""}, {XtNuserNameLabel, XtCLabel, XtRString, sizeof(String), offset(username_label), XtRImmediate, (XtPointer)"Username:"}, {XtNpassWordLabel, XtCLabel, XtRString, sizeof(String), offset(password_label), XtRImmediate, (XtPointer)"Password:"}, {XtNleftLabel, XtCLabel, XtRBoolean, sizeof(String), offset(left_label), XtRImmediate, (XtPointer)"left"}, {XtNmiddleLabel, XtCLabel, XtRBoolean, sizeof(String), offset(middle_label), XtRImmediate, (XtPointer)"middle"}, {XtNrightLabel, XtCLabel, XtRBoolean, sizeof(String), offset(right_label), XtRImmediate, (XtPointer)"right"}, {XtNfieldWidth, XtCWidth, XtRCardinal, sizeof(Cardinal), offset(field_width), XtRImmediate, (XtPointer)16}, #undef offset }; static void Initialize(Widget, Widget, ArgList, Cardinal*); static Boolean SetValues(Widget, Widget, Widget, ArgList, Cardinal*); static void close_login(Widget); LoginClassRec loginClassRec = { { /* core fields */ (WidgetClass) &closeShellClassRec, /* superclass */ "Login", /* class_name */ sizeof(LoginRec), /* widget_size */ NULL, /* class_initialize */ NULL, /* class_part_initialize */ FALSE, /* class_inited */ Initialize, /* initialize */ NULL, /* initialize_hook */ XtInheritRealize, /* realize */ NULL, /* actions */ 0, /* num_actions */ resources, /* resources */ XtNumber(resources), /* num_resources */ NULLQUARK, /* xrm_class */ TRUE, /* compress_motion */ FALSE, /* compress_exposure */ TRUE, /* compress_enterleave */ FALSE, /* visible_interest */ NULL, /* destroy */ XtInheritResize, /* resize */ NULL, /* expose */ SetValues, /* set_values */ NULL, /* set_values_hook */ XtInheritSetValuesAlmost, /* set_values_almost */ NULL, /* get_values_hook */ NULL, /* accept_focus */ XtVersion, /* version */ NULL, /* callback_private */ XtInheritTranslations, /* tm_table */ NULL, /* query_geometry */ XtInheritDisplayAccelerator, /* display_accelerator */ NULL /* extension */ }, { /* composite fields */ XtInheritGeometryManager, /* geometry_manager */ XtInheritChangeManaged, /* change_managed */ XtInheritInsertChild, /* insert_child */ XtInheritDeleteChild, /* delete_child */ NULL, /* extension */ }, { /* shell fields */ NULL, /* extension */ }, { /* wm shell fields */ NULL, /* extension */ }, { /* vendor shell fields */ NULL, /* extension */ }, { /* transient shell fields */ NULL, /* extension */ }, { close_login, /* close_window */ NULL, /* extension */ }, { /* dialogue fields */ NULL, /* extension */ } }; WidgetClass loginWidgetClass = (WidgetClass)&loginClassRec; /*************************************************************************/ static const char *layout_string = "vertical { " " height left <+inf-inf> " " horizontal { " " height left <+inf-inf> " " message " " height left <+inf-inf> " " } " " height left <+inf-inf> " " horizontal { " " height left " " vertical { " " 0 <+inf> " " usernamemessage " " 0 <+inf> " " } " " height left " " (width passwordmessage - width usernamemessage) " " usernamefield <+inf-inf*> " " height left " " } " " (height left / 2) <+inf-inf> " " horizontal { " " height left " " vertical { " " 0 <+inf> " " passwordmessage " " 0 <+inf> " " } " " height left " " passwordfield <+inf-inf*> " " height left " " } " " height left <+inf-inf> " " horizontal { " " height left <+inf-inf> " " left " " height left <+3inf-inf> " " middle " " height left <+3inf-inf> " " right " " height left <+inf-inf> " " } " " height left <+inf-inf> " "} "; static void call_callbacks(LoginWidget w, LoginReply reply) { XtCallbackList c_list = w->login.callback; LoginReport report = {0, }; if (!c_list) return; report.reply = reply; report.username = TextFieldGetBuffer(w->login.username_field); report.password = TextFieldGetBuffer(w->login.password_field); XtCallCallbackList((Widget)w, c_list, (XtPointer)&report); XtFree(report.username); XtFree(report.password); } static void close_login(Widget gw) { LoginWidget w = (LoginWidget)gw; call_callbacks(w, LoginReplyClose); } static void knapp_callback(Widget knapp, XtPointer client_data, XtPointer call_data) { LoginWidget w = (LoginWidget)client_data; if (knapp == w->login.left_knapp) call_callbacks(w, LoginReplyLeft); else if (knapp == w->login.middle_knapp) call_callbacks(w, LoginReplyMiddle); else if (knapp == w->login.right_knapp) call_callbacks(w, LoginReplyRight); } static void username_callback(Widget gw, XtPointer client_data, XtPointer call_data) { LoginWidget w = (LoginWidget)client_data; XtSetKeyboardFocus((Widget)w, w->login.password_field); } static void password_callback(Widget gw, XtPointer client_data, XtPointer call_data) { LoginWidget w = (LoginWidget)client_data; call_callbacks(w, LoginReplyEnter); } static void tab_callback(Widget gw, XtPointer client_data, XtPointer call_data) { LoginWidget w = (LoginWidget)client_data; Widget focus = NULL; if (gw == w->login.password_field) focus = w->login.username_field; else if (gw == w->login.username_field) focus = w->login.password_field; if (focus) XtSetKeyboardFocus((Widget)w, focus); } /*************************************************************************/ static void Initialize(Widget grequest, Widget gnew, ArgList gargs, Cardinal *no_args) { LoginWidget new = (LoginWidget)gnew; Arg args[6]; new->login.layout = XtVaCreateManagedWidget("layout", layoutWidgetClass, (Widget)new, XtVaTypedArg, XtNlayout, XtRString, layout_string, (int)sizeof(String), (void *)0); XtSetArg(args[0], XtNlabel, new->login.left_label); new->login.left_knapp = XtCreateManagedWidget("left", knappWidgetClass, new->login.layout, args, 1); XtAddCallback(new->login.left_knapp, XtNcallback, knapp_callback, (XtPointer)new); XtSetArg(args[0], XtNlabel, new->login.middle_label); new->login.middle_knapp = XtCreateManagedWidget("middle", knappWidgetClass, new->login.layout, args, 1); XtAddCallback(new->login.middle_knapp, XtNcallback, knapp_callback, (XtPointer)new); XtSetArg(args[0], XtNlabel, new->login.right_label); new->login.right_knapp = XtCreateManagedWidget("right", knappWidgetClass, new->login.layout, args, 1); XtAddCallback(new->login.right_knapp, XtNcallback, knapp_callback, (XtPointer)new); XtSetArg(args[0], XtNfocusRoot, new); XtSetArg(args[1], XtNbuffer, new->login.username_buffer); XtSetArg(args[2], XtNpreferredChars, new->login.field_width); XtSetArg(args[3], XtNsingleLine, True); new->login.username_field = XtCreateManagedWidget("usernamefield", textFieldWidgetClass, new->login.layout, args, 4); XtAddCallback(new->login.username_field, XtNcallback, username_callback, (XtPointer)new); XtAddCallback(new->login.username_field, XtNtabCallback, tab_callback, (XtPointer)new); XtSetArg(args[0], XtNfocusRoot, new); XtSetArg(args[1], XtNbuffer, new->login.password_buffer); XtSetArg(args[2], XtNpreferredChars, new->login.field_width); XtSetArg(args[3], XtNsingleLine, True); XtSetArg(args[4], XtNechoOff, True); new->login.password_field = XtCreateManagedWidget("passwordfield", textFieldWidgetClass, new->login.layout, args, 5); XtAddCallback(new->login.password_field, XtNcallback, password_callback, (XtPointer)new); XtAddCallback(new->login.password_field, XtNtabCallback, tab_callback, (XtPointer)new); XtSetArg(args[0], XtNborderWidth, 0); XtSetArg(args[1], XtNbuffer, new->login.message); new->login.message_widget = XtCreateManagedWidget("message", messageWidgetClass, new->login.layout, args, 2); new->login.message = NULL; XtSetArg(args[0], XtNborderWidth, 0); XtSetArg(args[1], XtNbuffer, new->login.username_label); XtCreateManagedWidget("usernamemessage", messageWidgetClass, new->login.layout, args, 2); new->login.username_label = NULL; XtSetArg(args[0], XtNborderWidth, 0); XtSetArg(args[1], XtNbuffer, new->login.password_label); XtCreateManagedWidget("passwordmessage", messageWidgetClass, new->login.layout, args, 2); new->login.password_label = NULL; XtSetKeyboardFocus((Widget)new, new->login.username_field); } static Boolean SetValues(Widget gcurrent, Widget grequest, Widget gnew, ArgList args, Cardinal *num_args) { Boolean redisplay = False; LoginWidget new = (LoginWidget)gnew; LoginWidget current = (LoginWidget)gcurrent; if (new->login.message != current->login.message) { Arg arg; XtSetArg(arg, XtNbuffer, new->login.message); new->login.message = NULL; XtSetValues(new->login.message_widget, &arg, 1); } if (new->login.username_buffer != current->login.username_buffer) { Arg arg; XtSetArg(arg, XtNbuffer, new->login.username_buffer); new->login.username_buffer = NULL; XtSetValues(new->login.username_field, &arg, 1); } if (new->login.password_buffer != current->login.password_buffer) { Arg arg; XtSetArg(arg, XtNbuffer, new->login.password_buffer); new->login.password_buffer = NULL; XtSetValues(new->login.password_field, &arg, 1); } return redisplay; } ./knews-1.0b.1/Widgets/Scrollable.c100644 1244 1244 25160 6455455541 15513 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include #include #include "Compat.h" #include "Manager.h" #include "ScrollableP.h" #include "Util.h" static XtResource resources[] = { #define offset(field) XtOffsetOf(ScrollableRec, scrollable.field) {XtNhBar, XtCScrBar, XtRWidget, sizeof(Widget), offset(h_bar), XtRImmediate, (XtPointer)NULL}, {XtNvBar, XtCScrBar, XtRWidget, sizeof(Widget), offset(v_bar), XtRImmediate, (XtPointer)NULL}, #undef offset }; static void ClassPartInitialize(WidgetClass); static void Initialize(Widget, Widget, ArgList, Cardinal*); static void Resize(Widget); static void Realize(Widget, XtValueMask*, XSetWindowAttributes*); static Boolean SetValues(Widget, Widget, Widget, ArgList, Cardinal*); static void SetHPos(ScrollableWidget, long); static void SetVPos(ScrollableWidget, long); ScrollableClassRec scrollableClassRec = { { /* core_class fields */ (WidgetClass) &shadowClassRec, /* superclass */ "Scrollable", /* class_name */ sizeof(ScrollableRec), /* widget_size */ NULL, /* class_initialize */ ClassPartInitialize, /* class_part_initialize */ FALSE, /* class_inited */ Initialize, /* initialize */ NULL, /* initialize_hook */ Realize, /* realize */ NULL, /* actions */ 0, /* num_actions */ resources, /* resources */ XtNumber(resources), /* num_resources */ NULLQUARK, /* xrm_class */ TRUE, /* compress_motion */ TRUE, /* compress_exposure */ TRUE, /* compress_enterleave */ FALSE, /* visible_interest */ NULL, /* destroy */ Resize, /* resize */ NULL, /* expose */ SetValues, /* set_values */ NULL, /* set_values_hook */ XtInheritSetValuesAlmost, /* set_values_almost */ NULL, /* get_values_hook */ NULL, /* accept_focus */ XtVersion, /* version */ NULL, /* callback_private */ NULL, /* tm_table */ XtInheritQueryGeometry, /* query_geometry */ XtInheritDisplayAccelerator, /* display_accelerator */ NULL /* extension */ }, { /* shadow fields */ XtInheritPixelOffset, /* pixel_offset */ False, /* use_arm_for_background */ XtInheritAllocShadowColors, /* alloc_shadow_colors */ XtInheritAllocShadowPixmaps, /* alloc_shadow_pixmaps */ XtInheritAllocArmColor, /* alloc_arm_color */ XtInheritAllocArmPixmap, /* alloc_arm_pixmap */ XtInheritAllocGCs, /* alloc_gcs */ NULL, /* extension */ }, { /* scrollable fields */ SetHPos, /* set_hpos */ SetVPos, /* set_vpos */ NULL, /* suspend_hook */ NULL, /* extension */ } }; WidgetClass scrollableWidgetClass = (WidgetClass)&scrollableClassRec; /*************************************************************************/ static void hbar_callback(Widget scr_bar, XtPointer client_data, XtPointer call_data) { ScrollReport *rep = (ScrollReport *)call_data; ScrollableWidget w = (ScrollableWidget)client_data; ScrollableWidgetClass class = (ScrollableWidgetClass)XtClass(w); long n_pos, n_shown, n_size; if (!w->core.managed) return; class->scrollable_class.set_hpos(w, rep->pos); n_pos = w->scrollable.pos_x; n_shown = w->scrollable.shown_x; n_size = w->scrollable.width; if (rep->pos != n_pos || rep->shown != n_shown || rep->size != n_size) ScrBarSetLengthsAndPos(scr_bar, n_size, n_shown, n_pos); } static void vbar_callback(Widget scr_bar, XtPointer client_data, XtPointer call_data) { ScrollReport *rep = (ScrollReport *)call_data; ScrollableWidget w = (ScrollableWidget)client_data; ScrollableWidgetClass class = (ScrollableWidgetClass)XtClass(w); long n_pos, n_shown, n_size; if (!w->core.managed) return; class->scrollable_class.set_vpos(w, rep->pos); n_pos = w->scrollable.pos_y; n_shown = w->scrollable.shown_y; n_size = w->scrollable.height; if (rep->pos != n_pos || rep->shown != n_shown || rep->size != n_size) ScrBarSetLengthsAndPos(scr_bar, n_size, n_shown, n_pos); } static void remove_callback(ScrollableWidget w, Widget scr_bar, XtCallbackProc callback) { if (scr_bar) XtRemoveCallback(scr_bar, XtNscrollCallback, callback, (XtPointer)w); } static void add_callback(ScrollableWidget w, Widget scr_bar, XtCallbackProc callback) { if (scr_bar) XtAddCallback(scr_bar, XtNscrollCallback, callback, (XtPointer)w); } static void call_suspend_hook(ScrollableWidget w) { ScrollableWidgetClass class = (ScrollableWidgetClass)XtClass(w); if (class->scrollable_class.suspend_hook) class->scrollable_class.suspend_hook(w); } static void parent_resize_callback(Widget parent, XtPointer client_data, XtPointer call_data) { Widget w = (Widget)client_data; if (XtIsManaged(w) && w->core.widget_class->core_class.resize) w->core.widget_class->core_class.resize(w); } /*************************************************************************/ static void ClassPartInitialize(WidgetClass gclass) { ScrollableWidgetClass class, super; class = (ScrollableWidgetClass)gclass; super = (ScrollableWidgetClass)class->core_class.superclass; if (class->scrollable_class.set_hpos == XtInheritScrollableSetPos) class->scrollable_class.set_hpos = super->scrollable_class.set_hpos; if (class->scrollable_class.set_vpos == XtInheritScrollableSetPos) class->scrollable_class.set_vpos = super->scrollable_class.set_vpos; if (class->scrollable_class.suspend_hook == XtInheritScrollableSuspendHook) class->scrollable_class.suspend_hook = super->scrollable_class.suspend_hook; } static void Initialize(Widget grequest, Widget gnew, ArgList args, Cardinal *num_args) { ScrollableWidget new = (ScrollableWidget)gnew; Widget parent = XtParent(new); new->scrollable.pos_x = 0; new->scrollable.shown_x = 0; new->scrollable.width = 0; new->scrollable.pos_y = 0; new->scrollable.shown_y = 0; new->scrollable.height = 0; new->scrollable.suspended = False; add_callback(new, new->scrollable.h_bar, hbar_callback); add_callback(new, new->scrollable.v_bar, vbar_callback); if (XtIsSubclass(parent, managerWidgetClass)) XtAddCallback(parent, XtNresizeCallback, parent_resize_callback, (XtPointer)new); } static void Resize(Widget gw) { ScrollableWidget w = (ScrollableWidget)gw; ScrollableHFromGeometry(w); ScrollableVFromGeometry(w); ScrollableFitHBar(w); ScrollableFitVBar(w); } static void SetHPos(ScrollableWidget w, long x) { Arg arg; XtSetArg(arg, XtNx, - x); XtSetValues((Widget)w, &arg, 1); ScrollableHFromGeometry(w); } static void SetVPos(ScrollableWidget w, long y) { Arg arg; XtSetArg(arg, XtNy, - y); XtSetValues((Widget)w, &arg, 1); ScrollableVFromGeometry(w); } static void Realize(Widget gw, XtValueMask *mask, XSetWindowAttributes *attributes) { ScrollableWidget w = (ScrollableWidget)gw; ScrollableFitHBar(w); ScrollableFitVBar(w); shadowWidgetClass->core_class.realize((Widget)w, mask, attributes); } static Boolean SetValues(Widget gcurrent, Widget grequest, Widget gnew, ArgList args, Cardinal *num_args) { ScrollableWidget new = (ScrollableWidget)gnew; ScrollableWidget current = (ScrollableWidget)gcurrent; if (new->scrollable.h_bar != current->scrollable.h_bar) { remove_callback(new, current->scrollable.h_bar, hbar_callback); add_callback(new, new->scrollable.h_bar, hbar_callback); ScrollableFitHBar(new); } if (new->scrollable.v_bar != current->scrollable.v_bar) { remove_callback(new, current->scrollable.v_bar, vbar_callback); add_callback(new, new->scrollable.v_bar, vbar_callback); ScrollableFitVBar(new); } return False; } /*************************************************************************/ void ScrollableSetHBar(Widget gw, Widget h_bar) { ScrollableWidget w = (ScrollableWidget)gw; remove_callback(w, w->scrollable.h_bar, hbar_callback); w->scrollable.h_bar = h_bar; add_callback(w, w->scrollable.h_bar, hbar_callback); if (!w->scrollable.suspended) ScrollableFitHBar(w); } void ScrollableSetVBar(Widget gw, Widget v_bar) { ScrollableWidget w = (ScrollableWidget)gw; remove_callback(w, w->scrollable.v_bar, vbar_callback); w->scrollable.v_bar = v_bar; add_callback(w, w->scrollable.v_bar, vbar_callback); if (!w->scrollable.suspended) ScrollableFitVBar(w); } void ScrollableSuspend(Widget gw) { ScrollableWidget w = (ScrollableWidget)gw; w->scrollable.suspended = True; call_suspend_hook(w); } void ScrollableResume(Widget gw) { ScrollableWidget w = (ScrollableWidget)gw; w->scrollable.suspended = False; call_suspend_hook(w); ScrollableFitHBar(w); ScrollableFitVBar(w); } void ScrollableFitHBar(ScrollableWidget w) { if (!w->scrollable.h_bar || w->scrollable.suspended) return; ScrBarSetLengthsAndPos(w->scrollable.h_bar, w->scrollable.width, w->scrollable.shown_x, w->scrollable.pos_x); } void ScrollableFitVBar(ScrollableWidget w) { if (!w->scrollable.v_bar || w->scrollable.suspended) return; ScrBarSetLengthsAndPos(w->scrollable.v_bar, w->scrollable.height, w->scrollable.shown_y, w->scrollable.pos_y); } void ScrollableHFromGeometry(ScrollableWidget w) { w->scrollable.pos_x = -w->core.x; w->scrollable.shown_x = XtParent(w)->core.width; w->scrollable.width = w->core.width; } void ScrollableVFromGeometry(ScrollableWidget w) { w->scrollable.pos_y = -w->core.y; w->scrollable.shown_y = XtParent(w)->core.height; w->scrollable.height = w->core.height; } void ScrollableSetHPos(Widget gw, long pos_x) { ScrollableWidget w = (ScrollableWidget)gw; ScrollableWidgetClass class = (ScrollableWidgetClass)XtClass(w); class->scrollable_class.set_hpos(w, pos_x); ScrollableFitHBar(w); } void ScrollableSetVPos(Widget gw, long pos_y) { ScrollableWidget w = (ScrollableWidget)gw; ScrollableWidgetClass class = (ScrollableWidgetClass)XtClass(w); class->scrollable_class.set_vpos(w, pos_y); ScrollableFitVBar(w); } long ScrollableGetVPos(Widget gw) { ScrollableWidget w = (ScrollableWidget)gw; return w->scrollable.pos_y; } long ScrollableGetVSize(Widget gw) { ScrollableWidget w = (ScrollableWidget)gw; return w->scrollable.height; } long ScrollableGetVShown(Widget gw) { ScrollableWidget w = (ScrollableWidget)gw; return w->scrollable.shown_y; } void ScrollablePage(Widget gw, float amount) { ScrollableWidget w = (ScrollableWidget)gw; ScrollableSetVPos((Widget)w, w->scrollable.pos_y + amount * w->scrollable.shown_y); } ./knews-1.0b.1/Widgets/MenuI.h100644 1244 1244 634 6455455541 14412 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #ifndef MenuI_h #define MenuI_h extern int NotifyMenu(Widget); extern int NotifyMenuShell(Widget); extern void PopdownMenu(Widget); extern void PopdownMenuShell(Widget); extern int PostNotifyMenu(Widget); extern int PostNotifyMenuShell(Widget); extern void SetActiveMenu(Widget, int); extern void SetActiveMenuShell(Widget, int); #endif /* MenuI_h */ ./knews-1.0b.1/Widgets/LayoutP.h.orig100644 1244 1244 12674 6455455541 16000 0ustar kallekalle/* * $XConsortium: LayoutP.h,v 1.2 92/01/22 18:03:08 keith Exp $ * * Copyright 1991 Massachusetts Institute of Technology * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of M.I.T. not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. M.I.T. makes no representations about the * suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL M.I.T. * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Author: Keith Packard, MIT X Consortium */ #ifndef _LayoutP_h #define _LayoutP_h #include "Layout.h" #define GlueEqual(a,b) ((a).order == (b).order && (a).value == (b).value) #define AddGlue(r,a,b) if (a.order == b.order) { \ r.order = a.order; \ r.value = a.value + b.value; \ } else { \ if (a.order > b.order) \ r = a; \ else \ r = b; \ } #define MinGlue(r,a,b) if (a.order == b.order) { \ r.order = a.order; \ if (a.value > b.value) \ r.value = b.value; \ else \ r.value = a.value; \ } else { \ if (a.order > b.order) \ r = b; \ else \ r = a; \ } #define SubGlue(r,a,b) if (a.order == b.order) { \ r.order = a.order; \ r.value = a.value - b.value; \ } else { \ if (a.order > b.order) \ r = a; \ else { \ r.order = b.order; \ r.value = -b.value; \ } \ } #define ZeroGlue(g) ((g).value = 0, (g).order = 0, (g).expr = 0) #define IsZeroGlue(g) ((g).value == 0) #define QuarkToWidget(l,q) XtNameToWidget((Widget) l, \ (char *) XrmQuarkToString(q)); typedef enum _BoxType { BoxBox, WidgetBox, GlueBox, VariableBox } BoxType; typedef enum _LayoutDirection { LayoutHorizontal = 0, LayoutVertical = 1 } LayoutDirection; typedef enum _Operator { Plus, Minus, Times, Divide, Percent } Operator; typedef enum _ExprType { Constant, Binary, Unary, Width, Height, Variable } ExprType; typedef struct _Expr *ExprPtr; typedef struct _Expr { ExprType type; union { double constant; struct { Operator op; ExprPtr left, right; } binary; struct { Operator op; ExprPtr down; } unary; XrmQuark width; XrmQuark height; XrmQuark variable; } u; } ExprRec; typedef struct _Glue { int order; double value; ExprPtr expr; } GlueRec, *GluePtr; typedef struct _BoxParams { GlueRec stretch[2]; GlueRec shrink[2]; } BoxParamsRec, *BoxParamsPtr; typedef struct _Box *BoxPtr; typedef BoxPtr LayoutPtr; typedef struct _Box { BoxPtr nextSibling; BoxPtr parent; BoxParamsRec params; int size[2]; int natural[2]; BoxType type; union { struct { BoxPtr firstChild; LayoutDirection dir; } box; struct { XrmQuark quark; Widget widget; } widget; struct { ExprPtr expr; } glue; struct { XrmQuark quark; ExprPtr expr; } variable; } u; } BoxRec; typedef struct _SubInfo { int naturalSize[2]; int naturalBw; } SubInfoRec, *SubInfoPtr; #define New(t) (t *) malloc(sizeof (t)) #define Dispose(p) free((char *) p) #define Some(t,n) (t*) malloc(sizeof(t) * n) #define More(p,t,n) ((p)? (t *) realloc((char *) p, sizeof(t)*n):Some(t,n) #define ForAllChildren(pw, childP) \ for ( (childP) = (pw)->composite.children ; \ (childP) < (pw)->composite.children + (pw)->composite.num_children ; \ (childP)++ ) if (!XtIsManaged(*childP)) ; else /********************************************************************* * * Layout Widget Private Data * *********************************************************************/ /* New fields for the Layout widget class record */ typedef struct _LayoutClassPart { int foo; /* keep compiler happy. */ } LayoutClassPart; /* Full Class record declaration */ typedef struct _LayoutClassRec { CoreClassPart core_class; CompositeClassPart composite_class; ConstraintClassPart constraint_class; LayoutClassPart layout_class; } LayoutClassRec; extern LayoutClassRec layoutClassRec; typedef struct _LayoutConstraintsRec { SubInfoRec layout; } LayoutConstraintsRec, *LayoutConstraints; #define SubInfo(w) (&(((LayoutConstraints) (w)->core.constraints)->layout)) /* New Fields for the Layout widget record */ typedef struct { /* resources */ LayoutPtr layout; Boolean debug; } LayoutPart; /************************************************************************** * * Full instance record declaration * **************************************************************************/ typedef struct _LayoutRec { CorePart core; CompositePart composite; ConstraintPart constraint; LayoutPart layout; } LayoutRec; #endif ./knews-1.0b.1/Widgets/Layout.h.orig100644 1244 1244 6420 6455455541 15630 0ustar kallekalle/* * $XConsortium: Layout.h,v 1.2 92/01/22 18:03:05 keith Exp $ * * Copyright 1991 Massachusetts Institute of Technology * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of M.I.T. not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. M.I.T. makes no representations about the * suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL M.I.T. * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Author: Keith Packard, MIT X Consortium */ #ifndef _Layout_h #define _Layout_h #include #if XtSpecificationRelease > 4 #include #endif /**************************************************************** * * Layout Widget (SubClass of CompositeClass) * ****************************************************************/ /* RESOURCES: Name Class RepType Default Value ---- ----- ------- ------------- background Background Pixel XtDefaultBackground border BorderColor Pixel XtDefaultForeground borderWidth BorderWidth Dimension 1 cursor Cursor Cursor None destroyCallback Callback Pointer NULL height Height Dimension 0 mappedWhenManaged MappedWhenManaged Boolean True sensitive Sensitive Boolean True width Width Dimension 0 x Position Position 0 y Position Position 0 layout Layout Layout NULL */ /* * Syntax of layout resource * * *layout:\ * .,.: distance + stretch-factor\n\ * ... * where the null widget-name is taken to be the Layout widget * * e.g: * * *label-1.hStretch: 0 * *label-2.vStretch: 1 * *layout:\ * .left, label-1.left: 10 + 0\n\ * label-1.right, label-2.left: 10 + 1\n\ * label-2.right, .right: 10 + 0 * * This layout causes label-1 to be set 10 pixels from the left edge * and be whatever size the label widget requests, while label-2 will * be set 10 pixels from the right edge, and take up half of the remaining * space to 10 pixels from the right edge of label-1. */ /* New Fields */ #define XtNlayout "layout" #define XtCLayout "Layout" #define XtRLayout "Layout" #define XtNdebug "debug" /* Class record constant */ extern WidgetClass layoutWidgetClass; typedef struct _LayoutClassRec *LayoutWidgetClass; typedef struct _LayoutRec *LayoutWidget; #endif /* _Layout_h */ /* DON'T ADD STUFF AFTER THIS #endif */ ./knews-1.0b.1/Widgets/MenuShell.c100644 1244 1244 6442 6455455541 15307 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include #include #include #include "Compat.h" #include "Util.h" #include "MenuI.h" #include "MenuShellP.h" static XtResource resources[]= { {XtNborderWidth, XtCBorderWidth, XtRDimension, sizeof(Dimension), XtOffsetOf(MenuShellRec, core.border_width), XtRImmediate, (XtPointer)0}, {XtNallowShellResize, XtCAllowShellResize, XtRBoolean, sizeof(Boolean), XtOffsetOf(MenuShellRec, shell.allow_shell_resize), XtRImmediate, (XtPointer)True}, #define offset(field) XtOffsetOf(MenuShellRec, popdown.field) #undef offset }; MenuShellClassRec menuShellClassRec = { { /* core fields */ (WidgetClass) &overrideShellClassRec, /* superclass */ "MenuShell", /* class_name */ sizeof(MenuShellRec), /* widget_size */ NULL, /* class_initialize */ NULL, /* class_part_initialize*/ FALSE, /* class_inited */ NULL, /* initialize */ NULL, /* initialize_hook */ XtInheritRealize, /* realize */ NULL, /* actions */ 0, /* num_actions */ resources, /* resources */ XtNumber(resources), /* num_resources */ NULLQUARK, /* xrm_class */ FALSE, /* compress_motion */ TRUE, /* compress_exposure */ FALSE, /* compress_enterleave */ FALSE, /* visible_interest */ NULL, /* destroy */ XtInheritResize, /* resize */ NULL, /* expose */ NULL, /* set_values */ NULL, /* set_values_hook */ XtInheritSetValuesAlmost, /* set_values_almost */ NULL, /* get_values_hook */ NULL, /* accept_focus */ XtVersion, /* version */ NULL, /* callback_private */ NULL, /* tm_table */ XtInheritQueryGeometry, /* query_geometry */ XtInheritDisplayAccelerator, /* display_accelerator */ NULL, /* extension */ }, { /* composite fields */ XtInheritGeometryManager, /* geometry_manager */ XtInheritChangeManaged, /* change_managed */ XtInheritInsertChild, /* insert_child */ XtInheritDeleteChild, /* delete_child */ NULL, /* extension */ }, { /* shell fields */ NULL, /* extension */ }, { /* override_shell fields */ NULL, /* extension */ }, { /* menu_shell fields */ NULL, /* extension */ } }; WidgetClass menuShellWidgetClass = (WidgetClass)&menuShellClassRec; /******************************************************************/ static Widget get_child(MenuShellWidget w) { int i; for (i = 0 ; i < w->composite.num_children ; i++) if (XtIsManaged(w->composite.children[i])) return w->composite.children[i]; return NULL; } int NotifyMenuShell(Widget gw) { MenuShellWidget w = (MenuShellWidget)gw; Widget child = get_child(w); return !child || NotifyMenu(child); } int PostNotifyMenuShell(Widget gw) { MenuShellWidget w = (MenuShellWidget)gw; Widget child = get_child(w); return !child || PostNotifyMenu(child); } void PopdownMenuShell(Widget gw) { MenuShellWidget w = (MenuShellWidget)gw; Widget child = get_child(w); if (child) PopdownMenu(child); if (w->shell.popped_up) XtPopdown((Widget)w); } void SetActiveMenuShell(Widget gw, int active) { MenuShellWidget w = (MenuShellWidget)gw; Widget child = get_child(w); if (child) SetActiveMenu(child, active); } ./knews-1.0b.1/regexp/ 40755 1244 1244 0 6456667410 13031 5ustar kallekalle./knews-1.0b.1/regexp/COPYRIGHT100644 1244 1244 1666 6455455541 14431 0ustar kallekalleCopyright 1992, 1993, 1994 Henry Spencer. All rights reserved. This software is not subject to any license of the American Telephone and Telegraph Company or of the Regents of the University of California. Permission is granted to anyone to use this software for any purpose on any computer system, and to alter it and redistribute it, subject to the following restrictions: 1. The author is not responsible for the consequences of use of this software, no matter how awful, even if they arise from flaws in it. 2. The origin of this software must not be misrepresented, either by explicit claim or by omission. Since few users ever read sources, credits must appear in the documentation. 3. Altered versions must be plainly marked as such, and must not be misrepresented as being the original software. Since few users ever read sources, credits must appear in the documentation. 4. This notice may not be removed or altered. ./knews-1.0b.1/regexp/Imakefile100644 1244 1244 1402 6455455541 14733 0ustar kallekalle#include "../knews.tmpl" REGSRC = regcomp.c regexec.c regerror.c regfree.c OBJPRODN = regcomp.o regexec.o regerror.o regfree.o SRCS = $(REGSRC) OBJS = $(OBJPRODN) EXTRA_INCLUDES = -I. /* EXTRA_DEFINES = -DPOSIX_MISTAKE -DREDEBUG */ NormalLibraryTarget(regexp, $(OBJS)) squeakyclean: rm -f *.ih MKHFLAGS = REGEXH=regex.h REGEXHSRC=regex2.h $(REGSRC) headers: sh ./mkh $(MKHFLAGS) -i _REGEX_H_ $(REGEXHSRC) >regex.tmp cmp -s regex.tmp regex.h 2>/dev/null || cp regex.tmp regex.h rm -f regex.tmp sh ./mkh $(MKHFLAGS) -p regcomp.c >regcomp.ih sh ./mkh $(MKHFLAGS) -p engine.c >engine.ih sh ./mkh $(MKHFLAGS) -p regerror.c >regerror.ih # dependencies regcomp.o: cclass.h cname.h regex.h regex2.h regexec.o: engine.c engine.ih regerror.o: regerror.ih ./knews-1.0b.1/regexp/Makefile.orig100644 1244 1244 6067 6455455541 15535 0ustar kallekalle# You probably want to take -DREDEBUG out of CFLAGS, and put something like # -O in, *after* testing (-DREDEBUG strengthens testing by enabling a lot of # internal assertion checking and some debugging facilities). # Put -Dconst= in for a pre-ANSI compiler. # Do not take -DPOSIX_MISTAKE out. # REGCFLAGS isn't important to you (it's for my use in some special contexts). CFLAGS=-I. -DPOSIX_MISTAKE -DREDEBUG $(REGCFLAGS) # If you have a pre-ANSI compiler, put -o into MKHFLAGS. If you want # the Berkeley __P macro, put -b in. MKHFLAGS= # Flags for linking but not compiling, if any. LDFLAGS= # Extra libraries for linking, if any. LIBS= # Internal stuff, should not need changing. OBJPRODN=regcomp.o regexec.o regerror.o regfree.o OBJS=$(OBJPRODN) split.o debug.o main.o H=cclass.h cname.h regex2.h utils.h REGSRC=regcomp.c regerror.c regexec.c regfree.c ALLSRC=$(REGSRC) engine.c debug.c main.c split.c # Stuff that matters only if you're trying to lint the package. LINTFLAGS=-I. -Dstatic= -Dconst= -DREDEBUG LINTC=regcomp.c regexec.c regerror.c regfree.c debug.c main.c JUNKLINT=possible pointer alignment|null effect # arrangements to build forward-reference header files .SUFFIXES: .ih .h .c.ih: sh ./mkh $(MKHFLAGS) -p $< >$@ default: r lib: purge $(OBJPRODN) rm -f libregex.a ar crv libregex.a $(OBJPRODN) purge: rm -f *.o # stuff to build regex.h REGEXH=regex.h REGEXHSRC=regex2.h $(REGSRC) $(REGEXH): $(REGEXHSRC) mkh sh ./mkh $(MKHFLAGS) -i _REGEX_H_ $(REGEXHSRC) >regex.tmp cmp -s regex.tmp regex.h 2>/dev/null || cp regex.tmp regex.h rm -f regex.tmp # dependencies $(OBJPRODN) debug.o: utils.h regex.h regex2.h regcomp.o: cclass.h cname.h regcomp.ih regexec.o: engine.c engine.ih regerror.o: regerror.ih debug.o: debug.ih main.o: main.ih # tester re: $(OBJS) $(CC) $(CFLAGS) $(LDFLAGS) $(OBJS) $(LIBS) -o $@ # regression test r: re tests ./re &1 | egrep -v '$(JUNKLINT)' | tee lint fullprint: ti README WHATSNEW notes todo | list ti *.h | list list *.c list regex.3 regex.7 print: ti README WHATSNEW notes todo | list ti *.h | list list reg*.c engine.c mf.tmp: Makefile sed '/^REGEXH=/s/=.*/=regex.h/' Makefile | sed '/#DEL$$/d' >$@ DTRH=cclass.h cname.h regex2.h utils.h PRE=COPYRIGHT README WHATSNEW POST=mkh regex.3 regex.7 tests $(DTRH) $(ALLSRC) fake/*.[ch] FILES=$(PRE) Makefile $(POST) DTR=$(PRE) Makefile=mf.tmp $(POST) dtr: $(FILES) mf.tmp makedtr $(DTR) >$@ rm mf.tmp cio: $(FILES) cio $(FILES) rdf: $(FILES) rcsdiff -c $(FILES) 2>&1 | p # various forms of cleanup tidy: rm -f junk* core core.* *.core dtr *.tmp lint clean: tidy rm -f *.o *.s *.ih re libregex.a # don't do this one unless you know what you're doing spotless: clean rm -f mkh regex.h ./knews-1.0b.1/regexp/README100644 1244 1244 2303 6455455541 14003 0ustar kallekallealpha3.4 release. Thu Mar 17 23:17:18 EST 1994 henry@zoo.toronto.edu See WHATSNEW for change listing. installation notes: -------- Read the comments at the beginning of Makefile before running. Utils.h contains some things that just might have to be modified on some systems, as well as a nested include (ugh) of . The "fake" directory contains quick-and-dirty fakes for some header files and routines that old systems may not have. Note also that -DUSEBCOPY will make utils.h substitute bcopy() for memmove(). After that, "make r" will build regcomp.o, regexec.o, regfree.o, and regerror.o (the actual routines), bundle them together into a test program, and run regression tests on them. No output is good output. "make lib" builds just the .o files for the actual routines (when you're happy with testing and have adjusted CFLAGS for production), and puts them together into libregex.a. You can pick up either the library or *.o ("make lib" makes sure there are no other .o files left around to confuse things). Main.c, debug.c, split.c are used for regression testing but are not part of the RE routines themselves. Regex.h goes in /usr/include. All other .h files are internal only. -------- ./knews-1.0b.1/regexp/WHATSNEW100644 1244 1244 12260 6455455541 14331 0ustar kallekalleNew in alpha3.4: The complex bug alluded to below has been fixed (in a slightly kludgey temporary way that may hurt efficiency a bit; this is another "get it out the door for 4.4" release). The tests at the end of the tests file have accordingly been uncommented. The primary sign of the bug was that something like a?b matching ab matched b rather than ab. (The bug was essentially specific to this exact situation, else it would have shown up earlier.) New in alpha3.3: The definition of word boundaries has been altered slightly, to more closely match the usual programming notion that "_" is an alphabetic. Stuff used for pre-ANSI systems is now in a subdir, and the makefile no longer alludes to it in mysterious ways. The makefile has generally been cleaned up some. Fixes have been made (again!) so that the regression test will run without -DREDEBUG, at the cost of weaker checking. A workaround for a bug in some folks' has been added. And some more things have been added to tests, including a couple right at the end which are commented out because the code currently flunks them (complex bug; fix coming). Plus the usual minor cleanup. New in alpha3.2: Assorted bits of cleanup and portability improvement (the development base is now a BSDI system using GCC instead of an ancient Sun system, and the newer compiler exposed some glitches). Fix for a serious bug that affected REs using many [] (including REG_ICASE REs because of the way they are implemented), *sometimes*, depending on memory-allocation patterns. The header-file prototypes no longer name the parameters, avoiding possible name conflicts. The possibility that some clot has defined CHAR_MIN as (say) `-128' instead of `(-128)' is now handled gracefully. "uchar" is no longer used as an internal type name (too many people have the same idea). Still the same old lousy performance, alas. New in alpha3.1: Basically nothing, this release is just a bookkeeping convenience. Stay tuned. New in alpha3.0: Performance is no better, alas, but some fixes have been made and some functionality has been added. (This is basically the "get it out the door in time for 4.4" release.) One bug fix: regfree() didn't free the main internal structure (how embarrassing). It is now possible to put NULs in either the RE or the target string, using (resp.) a new REG_PEND flag and the old REG_STARTEND flag. The REG_NOSPEC flag to regcomp() makes all characters ordinary, so you can match a literal string easily (this will become more useful when performance improves!). There are now primitives to match beginnings and ends of words, although the syntax is disgusting and so is the implementation. The REG_ATOI debugging interface has changed a bit. And there has been considerable internal cleanup of various kinds. New in alpha2.3: Split change list out of README, and moved flags notes into Makefile. Macro-ized the name of regex(7) in regex(3), since it has to change for 4.4BSD. Cleanup work in engine.c, and some new regression tests to catch tricky cases thereof. New in alpha2.2: Out-of-date manpages updated. Regerror() acquires two small extensions -- REG_ITOA and REG_ATOI -- which avoid debugging kludges in my own test program and might be useful to others for similar purposes. The regression test will now compile (and run) without REDEBUG. The BRE \$ bug is fixed. Most uses of "uchar" are gone; it's all chars now. Char/uchar parameters are now written int/unsigned, to avoid possible portability problems with unpromoted parameters. Some unsigned casts have been introduced to minimize portability problems with shifting into sign bits. New in alpha2.1: Lots of little stuff, cleanup and fixes. The one big thing is that regex.h is now generated, using mkh, rather than being supplied in the distribution; due to circularities in dependencies, you have to build regex.h explicitly by "make h". The two known bugs have been fixed (and the regression test now checks for them), as has a problem with assertions not being suppressed in the absence of REDEBUG. No performance work yet. New in alpha2: Backslash-anything is an ordinary character, not an error (except, of course, for the handful of backslashed metacharacters in BREs), which should reduce script breakage. The regression test checks *where* null strings are supposed to match, and has generally been tightened up somewhat. Small bug fixes in parameter passing (not harmful, but technically errors) and some other areas. Debugging invoked by defining REDEBUG rather than not defining NDEBUG. New in alpha+3: full prototyping for internal routines, using a little helper program, mkh, which extracts prototypes given in stylized comments. More minor cleanup. Buglet fix: it's CHAR_BIT, not CHAR_BITS. Simple pre-screening of input when a literal string is known to be part of the RE; this does wonders for performance. New in alpha+2: minor bits of cleanup. Notably, the number "32" for the word width isn't hardwired into regexec.c any more, the public header file prototypes the functions if __STDC__ is defined, and some small typos in the manpages have been fixed. New in alpha+1: improvements to the manual pages, and an important extension, the REG_STARTEND option to regexec(). ./knews-1.0b.1/regexp/cclass.h100644 1244 1244 1625 6455455542 14553 0ustar kallekalle/* character-class table */ static struct cclass { char *name; char *chars; char *multis; } cclasses[] = { "alnum", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\ 0123456789", "", "alpha", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", "", "blank", " \t", "", "cntrl", "\007\b\t\n\v\f\r\1\2\3\4\5\6\16\17\20\21\22\23\24\ \25\26\27\30\31\32\33\34\35\36\37\177", "", "digit", "0123456789", "", "graph", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\ 0123456789!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", "", "lower", "abcdefghijklmnopqrstuvwxyz", "", "print", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\ 0123456789!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~ ", "", "punct", "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", "", "space", "\t\n\v\f\r ", "", "upper", "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "", "xdigit", "0123456789ABCDEFabcdef", "", NULL, 0, "" }; ./knews-1.0b.1/regexp/cname.h100644 1244 1244 3512 6455455542 14363 0ustar kallekalle/* character-name table */ static struct cname { char *name; char code; } cnames[] = { "NUL", '\0', "SOH", '\001', "STX", '\002', "ETX", '\003', "EOT", '\004', "ENQ", '\005', "ACK", '\006', "BEL", '\007', "alert", '\007', "BS", '\010', "backspace", '\b', "HT", '\011', "tab", '\t', "LF", '\012', "newline", '\n', "VT", '\013', "vertical-tab", '\v', "FF", '\014', "form-feed", '\f', "CR", '\015', "carriage-return", '\r', "SO", '\016', "SI", '\017', "DLE", '\020', "DC1", '\021', "DC2", '\022', "DC3", '\023', "DC4", '\024', "NAK", '\025', "SYN", '\026', "ETB", '\027', "CAN", '\030', "EM", '\031', "SUB", '\032', "ESC", '\033', "IS4", '\034', "FS", '\034', "IS3", '\035', "GS", '\035', "IS2", '\036', "RS", '\036', "IS1", '\037', "US", '\037', "space", ' ', "exclamation-mark", '!', "quotation-mark", '"', "number-sign", '#', "dollar-sign", '$', "percent-sign", '%', "ampersand", '&', "apostrophe", '\'', "left-parenthesis", '(', "right-parenthesis", ')', "asterisk", '*', "plus-sign", '+', "comma", ',', "hyphen", '-', "hyphen-minus", '-', "period", '.', "full-stop", '.', "slash", '/', "solidus", '/', "zero", '0', "one", '1', "two", '2', "three", '3', "four", '4', "five", '5', "six", '6', "seven", '7', "eight", '8', "nine", '9', "colon", ':', "semicolon", ';', "less-than-sign", '<', "equals-sign", '=', "greater-than-sign", '>', "question-mark", '?', "commercial-at", '@', "left-square-bracket", '[', "backslash", '\\', "reverse-solidus", '\\', "right-square-bracket", ']', "circumflex", '^', "circumflex-accent", '^', "underscore", '_', "low-line", '_', "grave-accent", '`', "left-brace", '{', "left-curly-bracket", '{', "vertical-line", '|', "right-brace", '}', "right-curly-bracket", '}', "tilde", '~', "DEL", '\177', NULL, 0, }; ./knews-1.0b.1/regexp/debug.c100644 1244 1244 11744 6455455542 14407 0ustar kallekalle#include #include #include #include #include #include #include #include "utils.h" #include "regex2.h" #include "debug.ih" /* - regprint - print a regexp for debugging == void regprint(regex_t *r, FILE *d); */ void regprint(r, d) regex_t *r; FILE *d; { register struct re_guts *g = r->re_g; register int i; register int c; register int last; int nincat[NC]; fprintf(d, "%ld states, %d categories", (long)g->nstates, g->ncategories); fprintf(d, ", first %ld last %ld", (long)g->firststate, (long)g->laststate); if (g->iflags&USEBOL) fprintf(d, ", USEBOL"); if (g->iflags&USEEOL) fprintf(d, ", USEEOL"); if (g->iflags&BAD) fprintf(d, ", BAD"); if (g->nsub > 0) fprintf(d, ", nsub=%ld", (long)g->nsub); if (g->must != NULL) fprintf(d, ", must(%ld) `%*s'", (long)g->mlen, (int)g->mlen, g->must); if (g->backrefs) fprintf(d, ", backrefs"); if (g->nplus > 0) fprintf(d, ", nplus %ld", (long)g->nplus); fprintf(d, "\n"); s_print(g, d); for (i = 0; i < g->ncategories; i++) { nincat[i] = 0; for (c = CHAR_MIN; c <= CHAR_MAX; c++) if (g->categories[c] == i) nincat[i]++; } fprintf(d, "cc0#%d", nincat[0]); for (i = 1; i < g->ncategories; i++) if (nincat[i] == 1) { for (c = CHAR_MIN; c <= CHAR_MAX; c++) if (g->categories[c] == i) break; fprintf(d, ", %d=%s", i, regchar(c)); } fprintf(d, "\n"); for (i = 1; i < g->ncategories; i++) if (nincat[i] != 1) { fprintf(d, "cc%d\t", i); last = -1; for (c = CHAR_MIN; c <= CHAR_MAX+1; c++) /* +1 does flush */ if (c <= CHAR_MAX && g->categories[c] == i) { if (last < 0) { fprintf(d, "%s", regchar(c)); last = c; } } else { if (last >= 0) { if (last != c-1) fprintf(d, "-%s", regchar(c-1)); last = -1; } } fprintf(d, "\n"); } } /* - s_print - print the strip for debugging == static void s_print(register struct re_guts *g, FILE *d); */ static void s_print(g, d) register struct re_guts *g; FILE *d; { register sop *s; register cset *cs; register int i; register int done = 0; register sop opnd; register int col = 0; register int last; register sopno offset = 2; # define GAP() { if (offset % 5 == 0) { \ if (col > 40) { \ fprintf(d, "\n\t"); \ col = 0; \ } else { \ fprintf(d, " "); \ col++; \ } \ } else \ col++; \ offset++; \ } if (OP(g->strip[0]) != OEND) fprintf(d, "missing initial OEND!\n"); for (s = &g->strip[1]; !done; s++) { opnd = OPND(*s); switch (OP(*s)) { case OEND: fprintf(d, "\n"); done = 1; break; case OCHAR: if (strchr("\\|()^$.[+*?{}!<> ", (char)opnd) != NULL) fprintf(d, "\\%c", (char)opnd); else fprintf(d, "%s", regchar((char)opnd)); break; case OBOL: fprintf(d, "^"); break; case OEOL: fprintf(d, "$"); break; case OBOW: fprintf(d, "\\{"); break; case OEOW: fprintf(d, "\\}"); break; case OANY: fprintf(d, "."); break; case OANYOF: fprintf(d, "[(%ld)", (long)opnd); cs = &g->sets[opnd]; last = -1; for (i = 0; i < g->csetsize+1; i++) /* +1 flushes */ if (CHIN(cs, i) && i < g->csetsize) { if (last < 0) { fprintf(d, "%s", regchar(i)); last = i; } } else { if (last >= 0) { if (last != i-1) fprintf(d, "-%s", regchar(i-1)); last = -1; } } fprintf(d, "]"); break; case OBACK_: fprintf(d, "(\\<%ld>", (long)opnd); break; case O_BACK: fprintf(d, "<%ld>\\)", (long)opnd); break; case OPLUS_: fprintf(d, "(+"); if (OP(*(s+opnd)) != O_PLUS) fprintf(d, "<%ld>", (long)opnd); break; case O_PLUS: if (OP(*(s-opnd)) != OPLUS_) fprintf(d, "<%ld>", (long)opnd); fprintf(d, "+)"); break; case OQUEST_: fprintf(d, "(?"); if (OP(*(s+opnd)) != O_QUEST) fprintf(d, "<%ld>", (long)opnd); break; case O_QUEST: if (OP(*(s-opnd)) != OQUEST_) fprintf(d, "<%ld>", (long)opnd); fprintf(d, "?)"); break; case OLPAREN: fprintf(d, "((<%ld>", (long)opnd); break; case ORPAREN: fprintf(d, "<%ld>))", (long)opnd); break; case OCH_: fprintf(d, "<"); if (OP(*(s+opnd)) != OOR2) fprintf(d, "<%ld>", (long)opnd); break; case OOR1: if (OP(*(s-opnd)) != OOR1 && OP(*(s-opnd)) != OCH_) fprintf(d, "<%ld>", (long)opnd); fprintf(d, "|"); break; case OOR2: fprintf(d, "|"); if (OP(*(s+opnd)) != OOR2 && OP(*(s+opnd)) != O_CH) fprintf(d, "<%ld>", (long)opnd); break; case O_CH: if (OP(*(s-opnd)) != OOR1) fprintf(d, "<%ld>", (long)opnd); fprintf(d, ">"); break; default: fprintf(d, "!%d(%d)!", OP(*s), opnd); break; } if (!done) GAP(); } } /* - regchar - make a character printable == static char *regchar(int ch); */ static char * /* -> representation */ regchar(ch) int ch; { static char buf[10]; if (isprint(ch) || ch == ' ') sprintf(buf, "%c", ch); else sprintf(buf, "\\%o", ch); return(buf); } ./knews-1.0b.1/regexp/engine.c100644 1244 1244 61646 6455455542 14574 0ustar kallekalle/* * The matching engine and friends. This file is #included by regexec.c * after suitable #defines of a variety of macros used herein, so that * different state representations can be used without duplicating masses * of code. */ #ifdef SNAMES #define matcher smatcher #define fast sfast #define slow sslow #define dissect sdissect #define backref sbackref #define step sstep #define print sprint #define at sat #define match smat #endif #ifdef LNAMES #define matcher lmatcher #define fast lfast #define slow lslow #define dissect ldissect #define backref lbackref #define step lstep #define print lprint #define at lat #define match lmat #endif /* another structure passed up and down to avoid zillions of parameters */ struct match { struct re_guts *g; int eflags; regmatch_t *pmatch; /* [nsub+1] (0 element unused) */ char *offp; /* offsets work from here */ char *beginp; /* start of string -- virtual NUL precedes */ char *endp; /* end of string -- virtual NUL here */ char *coldp; /* can be no match starting before here */ char **lastpos; /* [nplus+1] */ STATEVARS; states st; /* current states */ states fresh; /* states for a fresh start */ states tmp; /* temporary */ states empty; /* empty set of states */ }; #include "engine.ih" #ifdef REDEBUG #define SP(t, s, c) print(m, t, s, c, stdout) #define AT(t, p1, p2, s1, s2) at(m, t, p1, p2, s1, s2) #define NOTE(str) { if (m->eflags®_TRACE) printf("=%s\n", (str)); } #else #define SP(t, s, c) /* nothing */ #define AT(t, p1, p2, s1, s2) /* nothing */ #define NOTE(s) /* nothing */ #endif /* - matcher - the actual matching engine == static int matcher(register struct re_guts *g, char *string, \ == size_t nmatch, regmatch_t pmatch[], int eflags); */ static int /* 0 success, REG_NOMATCH failure */ matcher(g, string, nmatch, pmatch, eflags) register struct re_guts *g; char *string; size_t nmatch; regmatch_t pmatch[]; int eflags; { register char *endp; register int i; struct match mv; register struct match *m = &mv; register char *dp; const register sopno gf = g->firststate+1; /* +1 for OEND */ const register sopno gl = g->laststate; char *start; char *stop; /* simplify the situation where possible */ if (g->cflags®_NOSUB) nmatch = 0; if (eflags®_STARTEND) { start = string + pmatch[0].rm_so; stop = string + pmatch[0].rm_eo; } else { start = string; stop = start + strlen(start); } if (stop < start) return(REG_INVARG); /* prescreening; this does wonders for this rather slow code */ if (g->must != NULL) { for (dp = start; dp < stop; dp++) if (*dp == g->must[0] && stop - dp >= g->mlen && memcmp(dp, g->must, (size_t)g->mlen) == 0) break; if (dp == stop) /* we didn't find g->must */ return(REG_NOMATCH); } /* match struct setup */ m->g = g; m->eflags = eflags; m->pmatch = NULL; m->lastpos = NULL; m->offp = string; m->beginp = start; m->endp = stop; STATESETUP(m, 4); SETUP(m->st); SETUP(m->fresh); SETUP(m->tmp); SETUP(m->empty); CLEAR(m->empty); /* this loop does only one repetition except for backrefs */ for (;;) { endp = fast(m, start, stop, gf, gl); if (endp == NULL) { /* a miss */ STATETEARDOWN(m); return(REG_NOMATCH); } if (nmatch == 0 && !g->backrefs) break; /* no further info needed */ /* where? */ assert(m->coldp != NULL); for (;;) { NOTE("finding start"); endp = slow(m, m->coldp, stop, gf, gl); if (endp != NULL) break; assert(m->coldp < m->endp); m->coldp++; } if (nmatch == 1 && !g->backrefs) break; /* no further info needed */ /* oh my, he wants the subexpressions... */ if (m->pmatch == NULL) m->pmatch = (regmatch_t *)malloc((m->g->nsub + 1) * sizeof(regmatch_t)); if (m->pmatch == NULL) { STATETEARDOWN(m); return(REG_ESPACE); } for (i = 1; i <= m->g->nsub; i++) m->pmatch[i].rm_so = m->pmatch[i].rm_eo = -1; if (!g->backrefs && !(m->eflags®_BACKR)) { NOTE("dissecting"); dp = dissect(m, m->coldp, endp, gf, gl); } else { if (g->nplus > 0 && m->lastpos == NULL) m->lastpos = (char **)malloc((g->nplus+1) * sizeof(char *)); if (g->nplus > 0 && m->lastpos == NULL) { free(m->pmatch); STATETEARDOWN(m); return(REG_ESPACE); } NOTE("backref dissect"); dp = backref(m, m->coldp, endp, gf, gl, (sopno)0); } if (dp != NULL) break; /* uh-oh... we couldn't find a subexpression-level match */ assert(g->backrefs); /* must be back references doing it */ assert(g->nplus == 0 || m->lastpos != NULL); for (;;) { if (dp != NULL || endp <= m->coldp) break; /* defeat */ NOTE("backoff"); endp = slow(m, m->coldp, endp-1, gf, gl); if (endp == NULL) break; /* defeat */ /* try it on a shorter possibility */ #ifndef NDEBUG for (i = 1; i <= m->g->nsub; i++) { assert(m->pmatch[i].rm_so == -1); assert(m->pmatch[i].rm_eo == -1); } #endif NOTE("backoff dissect"); dp = backref(m, m->coldp, endp, gf, gl, (sopno)0); } assert(dp == NULL || dp == endp); if (dp != NULL) /* found a shorter one */ break; /* despite initial appearances, there is no match here */ NOTE("false alarm"); start = m->coldp + 1; /* recycle starting later */ assert(start <= stop); } /* fill in the details if requested */ if (nmatch > 0) { pmatch[0].rm_so = m->coldp - m->offp; pmatch[0].rm_eo = endp - m->offp; } if (nmatch > 1) { assert(m->pmatch != NULL); for (i = 1; i < nmatch; i++) if (i <= m->g->nsub) pmatch[i] = m->pmatch[i]; else { pmatch[i].rm_so = -1; pmatch[i].rm_eo = -1; } } if (m->pmatch != NULL) free((char *)m->pmatch); if (m->lastpos != NULL) free((char *)m->lastpos); STATETEARDOWN(m); return(0); } /* - dissect - figure out what matched what, no back references == static char *dissect(register struct match *m, char *start, \ == char *stop, sopno startst, sopno stopst); */ static char * /* == stop (success) always */ dissect(m, start, stop, startst, stopst) register struct match *m; char *start; char *stop; sopno startst; sopno stopst; { register int i; register sopno ss; /* start sop of current subRE */ register sopno es; /* end sop of current subRE */ register char *sp; /* start of string matched by it */ register char *stp; /* string matched by it cannot pass here */ register char *rest; /* start of rest of string */ register char *tail; /* string unmatched by rest of RE */ register sopno ssub; /* start sop of subsubRE */ register sopno esub; /* end sop of subsubRE */ register char *ssp; /* start of string matched by subsubRE */ register char *sep; /* end of string matched by subsubRE */ register char *oldssp; /* previous ssp */ register char *dp; AT("diss", start, stop, startst, stopst); sp = start; for (ss = startst; ss < stopst; ss = es) { /* identify end of subRE */ es = ss; switch (OP(m->g->strip[es])) { case OPLUS_: case OQUEST_: es += OPND(m->g->strip[es]); break; case OCH_: while (OP(m->g->strip[es]) != O_CH) es += OPND(m->g->strip[es]); break; } es++; /* figure out what it matched */ switch (OP(m->g->strip[ss])) { case OEND: assert(nope); break; case OCHAR: sp++; break; case OBOL: case OEOL: case OBOW: case OEOW: break; case OANY: case OANYOF: sp++; break; case OBACK_: case O_BACK: assert(nope); break; /* cases where length of match is hard to find */ case OQUEST_: stp = stop; for (;;) { /* how long could this one be? */ rest = slow(m, sp, stp, ss, es); assert(rest != NULL); /* it did match */ /* could the rest match the rest? */ tail = slow(m, rest, stop, es, stopst); if (tail == stop) break; /* yes! */ /* no -- try a shorter match for this one */ stp = rest - 1; assert(stp >= sp); /* it did work */ } ssub = ss + 1; esub = es - 1; /* did innards match? */ if (slow(m, sp, rest, ssub, esub) != NULL) { dp = dissect(m, sp, rest, ssub, esub); assert(dp == rest); } else /* no */ assert(sp == rest); sp = rest; break; case OPLUS_: stp = stop; for (;;) { /* how long could this one be? */ rest = slow(m, sp, stp, ss, es); assert(rest != NULL); /* it did match */ /* could the rest match the rest? */ tail = slow(m, rest, stop, es, stopst); if (tail == stop) break; /* yes! */ /* no -- try a shorter match for this one */ stp = rest - 1; assert(stp >= sp); /* it did work */ } ssub = ss + 1; esub = es - 1; ssp = sp; oldssp = ssp; for (;;) { /* find last match of innards */ sep = slow(m, ssp, rest, ssub, esub); if (sep == NULL || sep == ssp) break; /* failed or matched null */ oldssp = ssp; /* on to next try */ ssp = sep; } if (sep == NULL) { /* last successful match */ sep = ssp; ssp = oldssp; } assert(sep == rest); /* must exhaust substring */ assert(slow(m, ssp, sep, ssub, esub) == rest); dp = dissect(m, ssp, sep, ssub, esub); assert(dp == sep); sp = rest; break; case OCH_: stp = stop; for (;;) { /* how long could this one be? */ rest = slow(m, sp, stp, ss, es); assert(rest != NULL); /* it did match */ /* could the rest match the rest? */ tail = slow(m, rest, stop, es, stopst); if (tail == stop) break; /* yes! */ /* no -- try a shorter match for this one */ stp = rest - 1; assert(stp >= sp); /* it did work */ } ssub = ss + 1; esub = ss + OPND(m->g->strip[ss]) - 1; assert(OP(m->g->strip[esub]) == OOR1); for (;;) { /* find first matching branch */ if (slow(m, sp, rest, ssub, esub) == rest) break; /* it matched all of it */ /* that one missed, try next one */ assert(OP(m->g->strip[esub]) == OOR1); esub++; assert(OP(m->g->strip[esub]) == OOR2); ssub = esub + 1; esub += OPND(m->g->strip[esub]); if (OP(m->g->strip[esub]) == OOR2) esub--; else assert(OP(m->g->strip[esub]) == O_CH); } dp = dissect(m, sp, rest, ssub, esub); assert(dp == rest); sp = rest; break; case O_PLUS: case O_QUEST: case OOR1: case OOR2: case O_CH: assert(nope); break; case OLPAREN: i = OPND(m->g->strip[ss]); assert(0 < i && i <= m->g->nsub); m->pmatch[i].rm_so = sp - m->offp; break; case ORPAREN: i = OPND(m->g->strip[ss]); assert(0 < i && i <= m->g->nsub); m->pmatch[i].rm_eo = sp - m->offp; break; default: /* uh oh */ assert(nope); break; } } assert(sp == stop); return(sp); } /* - backref - figure out what matched what, figuring in back references == static char *backref(register struct match *m, char *start, \ == char *stop, sopno startst, sopno stopst, sopno lev); */ static char * /* == stop (success) or NULL (failure) */ backref(m, start, stop, startst, stopst, lev) register struct match *m; char *start; char *stop; sopno startst; sopno stopst; sopno lev; /* PLUS nesting level */ { register int i; register sopno ss; /* start sop of current subRE */ register char *sp; /* start of string matched by it */ register sopno ssub; /* start sop of subsubRE */ register sopno esub; /* end sop of subsubRE */ register char *ssp; /* start of string matched by subsubRE */ register char *dp; register size_t len; register int hard; register sop s; register regoff_t offsave; register cset *cs; AT("back", start, stop, startst, stopst); sp = start; /* get as far as we can with easy stuff */ hard = 0; for (ss = startst; !hard && ss < stopst; ss++) switch (OP(s = m->g->strip[ss])) { case OCHAR: if (sp == stop || *sp++ != (char)OPND(s)) return(NULL); break; case OANY: if (sp == stop) return(NULL); sp++; break; case OANYOF: cs = &m->g->sets[OPND(s)]; if (sp == stop || !CHIN(cs, *sp++)) return(NULL); break; case OBOL: if ( (sp == m->beginp && !(m->eflags®_NOTBOL)) || (sp < m->endp && *(sp-1) == '\n' && (m->g->cflags®_NEWLINE)) ) { /* yes */ } else return(NULL); break; case OEOL: if ( (sp == m->endp && !(m->eflags®_NOTEOL)) || (sp < m->endp && *sp == '\n' && (m->g->cflags®_NEWLINE)) ) { /* yes */ } else return(NULL); break; case OBOW: if (( (sp == m->beginp && !(m->eflags®_NOTBOL)) || (sp < m->endp && *(sp-1) == '\n' && (m->g->cflags®_NEWLINE)) || (sp > m->beginp && !ISWORD(*(sp-1))) ) && (sp < m->endp && ISWORD(*sp)) ) { /* yes */ } else return(NULL); break; case OEOW: if (( (sp == m->endp && !(m->eflags®_NOTEOL)) || (sp < m->endp && *sp == '\n' && (m->g->cflags®_NEWLINE)) || (sp < m->endp && !ISWORD(*sp)) ) && (sp > m->beginp && ISWORD(*(sp-1))) ) { /* yes */ } else return(NULL); break; case O_QUEST: break; case OOR1: /* matches null but needs to skip */ ss++; s = m->g->strip[ss]; do { assert(OP(s) == OOR2); ss += OPND(s); } while (OP(s = m->g->strip[ss]) != O_CH); /* note that the ss++ gets us past the O_CH */ break; default: /* have to make a choice */ hard = 1; break; } if (!hard) { /* that was it! */ if (sp != stop) return(NULL); return(sp); } ss--; /* adjust for the for's final increment */ /* the hard stuff */ AT("hard", sp, stop, ss, stopst); s = m->g->strip[ss]; switch (OP(s)) { case OBACK_: /* the vilest depths */ i = OPND(s); assert(0 < i && i <= m->g->nsub); if (m->pmatch[i].rm_eo == -1) return(NULL); assert(m->pmatch[i].rm_so != -1); len = m->pmatch[i].rm_eo - m->pmatch[i].rm_so; assert(stop - m->beginp >= len); if (sp > stop - len) return(NULL); /* not enough left to match */ ssp = m->offp + m->pmatch[i].rm_so; if (memcmp(sp, ssp, len) != 0) return(NULL); while (m->g->strip[ss] != SOP(O_BACK, i)) ss++; return(backref(m, sp+len, stop, ss+1, stopst, lev)); break; case OQUEST_: /* to null or not */ dp = backref(m, sp, stop, ss+1, stopst, lev); if (dp != NULL) return(dp); /* not */ return(backref(m, sp, stop, ss+OPND(s)+1, stopst, lev)); break; case OPLUS_: assert(m->lastpos != NULL); assert(lev+1 <= m->g->nplus); m->lastpos[lev+1] = sp; return(backref(m, sp, stop, ss+1, stopst, lev+1)); break; case O_PLUS: if (sp == m->lastpos[lev]) /* last pass matched null */ return(backref(m, sp, stop, ss+1, stopst, lev-1)); /* try another pass */ m->lastpos[lev] = sp; dp = backref(m, sp, stop, ss-OPND(s)+1, stopst, lev); if (dp == NULL) return(backref(m, sp, stop, ss+1, stopst, lev-1)); else return(dp); break; case OCH_: /* find the right one, if any */ ssub = ss + 1; esub = ss + OPND(s) - 1; assert(OP(m->g->strip[esub]) == OOR1); for (;;) { /* find first matching branch */ dp = backref(m, sp, stop, ssub, esub, lev); if (dp != NULL) return(dp); /* that one missed, try next one */ if (OP(m->g->strip[esub]) == O_CH) return(NULL); /* there is none */ esub++; assert(OP(m->g->strip[esub]) == OOR2); ssub = esub + 1; esub += OPND(m->g->strip[esub]); if (OP(m->g->strip[esub]) == OOR2) esub--; else assert(OP(m->g->strip[esub]) == O_CH); } break; case OLPAREN: /* must undo assignment if rest fails */ i = OPND(s); assert(0 < i && i <= m->g->nsub); offsave = m->pmatch[i].rm_so; m->pmatch[i].rm_so = sp - m->offp; dp = backref(m, sp, stop, ss+1, stopst, lev); if (dp != NULL) return(dp); m->pmatch[i].rm_so = offsave; return(NULL); break; case ORPAREN: /* must undo assignment if rest fails */ i = OPND(s); assert(0 < i && i <= m->g->nsub); offsave = m->pmatch[i].rm_eo; m->pmatch[i].rm_eo = sp - m->offp; dp = backref(m, sp, stop, ss+1, stopst, lev); if (dp != NULL) return(dp); m->pmatch[i].rm_eo = offsave; return(NULL); break; default: /* uh oh */ assert(nope); break; } /* "can't happen" */ assert(nope); /* NOTREACHED */ } /* - fast - step through the string at top speed == static char *fast(register struct match *m, char *start, \ == char *stop, sopno startst, sopno stopst); */ static char * /* where tentative match ended, or NULL */ fast(m, start, stop, startst, stopst) register struct match *m; char *start; char *stop; sopno startst; sopno stopst; { register states st = m->st; register states fresh = m->fresh; register states tmp = m->tmp; register char *p = start; register int c = (start == m->beginp) ? OUT : *(start-1); register int lastc; /* previous c */ register int flagch; register int i; register char *coldp; /* last p after which no match was underway */ CLEAR(st); SET1(st, startst); st = step(m->g, startst, stopst, st, NOTHING, st); ASSIGN(fresh, st); SP("start", st, *p); coldp = NULL; for (;;) { /* next character */ lastc = c; c = (p == m->endp) ? OUT : *p; if (EQ(st, fresh)) coldp = p; /* is there an EOL and/or BOL between lastc and c? */ flagch = '\0'; i = 0; if ( (lastc == '\n' && m->g->cflags®_NEWLINE) || (lastc == OUT && !(m->eflags®_NOTBOL)) ) { flagch = BOL; i = m->g->nbol; } if ( (c == '\n' && m->g->cflags®_NEWLINE) || (c == OUT && !(m->eflags®_NOTEOL)) ) { flagch = (flagch == BOL) ? BOLEOL : EOL; i += m->g->neol; } if (i != 0) { for (; i > 0; i--) st = step(m->g, startst, stopst, st, flagch, st); SP("boleol", st, c); } /* how about a word boundary? */ if ( (flagch == BOL || (lastc != OUT && !ISWORD(lastc))) && (c != OUT && ISWORD(c)) ) { flagch = BOW; } if ( (lastc != OUT && ISWORD(lastc)) && (flagch == EOL || (c != OUT && !ISWORD(c))) ) { flagch = EOW; } if (flagch == BOW || flagch == EOW) { st = step(m->g, startst, stopst, st, flagch, st); SP("boweow", st, c); } /* are we done? */ if (ISSET(st, stopst) || p == stop) break; /* NOTE BREAK OUT */ /* no, we must deal with this character */ ASSIGN(tmp, st); ASSIGN(st, fresh); assert(c != OUT); st = step(m->g, startst, stopst, tmp, c, st); SP("aft", st, c); assert(EQ(step(m->g, startst, stopst, st, NOTHING, st), st)); p++; } assert(coldp != NULL); m->coldp = coldp; if (ISSET(st, stopst)) return(p+1); else return(NULL); } /* - slow - step through the string more deliberately == static char *slow(register struct match *m, char *start, \ == char *stop, sopno startst, sopno stopst); */ static char * /* where it ended */ slow(m, start, stop, startst, stopst) register struct match *m; char *start; char *stop; sopno startst; sopno stopst; { register states st = m->st; register states empty = m->empty; register states tmp = m->tmp; register char *p = start; register int c = (start == m->beginp) ? OUT : *(start-1); register int lastc; /* previous c */ register int flagch; register int i; register char *matchp; /* last p at which a match ended */ AT("slow", start, stop, startst, stopst); CLEAR(st); SET1(st, startst); SP("sstart", st, *p); st = step(m->g, startst, stopst, st, NOTHING, st); matchp = NULL; for (;;) { /* next character */ lastc = c; c = (p == m->endp) ? OUT : *p; /* is there an EOL and/or BOL between lastc and c? */ flagch = '\0'; i = 0; if ( (lastc == '\n' && m->g->cflags®_NEWLINE) || (lastc == OUT && !(m->eflags®_NOTBOL)) ) { flagch = BOL; i = m->g->nbol; } if ( (c == '\n' && m->g->cflags®_NEWLINE) || (c == OUT && !(m->eflags®_NOTEOL)) ) { flagch = (flagch == BOL) ? BOLEOL : EOL; i += m->g->neol; } if (i != 0) { for (; i > 0; i--) st = step(m->g, startst, stopst, st, flagch, st); SP("sboleol", st, c); } /* how about a word boundary? */ if ( (flagch == BOL || (lastc != OUT && !ISWORD(lastc))) && (c != OUT && ISWORD(c)) ) { flagch = BOW; } if ( (lastc != OUT && ISWORD(lastc)) && (flagch == EOL || (c != OUT && !ISWORD(c))) ) { flagch = EOW; } if (flagch == BOW || flagch == EOW) { st = step(m->g, startst, stopst, st, flagch, st); SP("sboweow", st, c); } /* are we done? */ if (ISSET(st, stopst)) matchp = p; if (EQ(st, empty) || p == stop) break; /* NOTE BREAK OUT */ /* no, we must deal with this character */ ASSIGN(tmp, st); ASSIGN(st, empty); assert(c != OUT); st = step(m->g, startst, stopst, tmp, c, st); SP("saft", st, c); assert(EQ(step(m->g, startst, stopst, st, NOTHING, st), st)); p++; } return(matchp); } /* - step - map set of states reachable before char to set reachable after == static states step(register struct re_guts *g, sopno start, sopno stop, \ == register states bef, int ch, register states aft); == #define BOL (OUT+1) == #define EOL (BOL+1) == #define BOLEOL (BOL+2) == #define NOTHING (BOL+3) == #define BOW (BOL+4) == #define EOW (BOL+5) == #define CODEMAX (BOL+5) // highest code used == #define NONCHAR(c) ((c) > CHAR_MAX) == #define NNONCHAR (CODEMAX-CHAR_MAX) */ static states step(g, start, stop, bef, ch, aft) register struct re_guts *g; sopno start; /* start state within strip */ sopno stop; /* state after stop state within strip */ register states bef; /* states reachable before */ int ch; /* character or NONCHAR code */ register states aft; /* states already known reachable after */ { register cset *cs; register sop s; register sopno pc; register onestate here; /* note, macros know this name */ register sopno look; register int i; for (pc = start, INIT(here, pc); pc != stop; pc++, INC(here)) { s = g->strip[pc]; switch (OP(s)) { case OEND: assert(pc == stop-1); break; case OCHAR: /* only characters can match */ assert(!NONCHAR(ch) || ch != (char)OPND(s)); if (ch == (char)OPND(s)) FWD(aft, bef, 1); break; case OBOL: if (ch == BOL || ch == BOLEOL) FWD(aft, bef, 1); break; case OEOL: if (ch == EOL || ch == BOLEOL) FWD(aft, bef, 1); break; case OBOW: if (ch == BOW) FWD(aft, bef, 1); break; case OEOW: if (ch == EOW) FWD(aft, bef, 1); break; case OANY: if (!NONCHAR(ch)) FWD(aft, bef, 1); break; case OANYOF: cs = &g->sets[OPND(s)]; if (!NONCHAR(ch) && CHIN(cs, ch)) FWD(aft, bef, 1); break; case OBACK_: /* ignored here */ case O_BACK: FWD(aft, aft, 1); break; case OPLUS_: /* forward, this is just an empty */ FWD(aft, aft, 1); break; case O_PLUS: /* both forward and back */ FWD(aft, aft, 1); i = ISSETBACK(aft, OPND(s)); BACK(aft, aft, OPND(s)); if (!i && ISSETBACK(aft, OPND(s))) { /* oho, must reconsider loop body */ pc -= OPND(s) + 1; INIT(here, pc); } break; case OQUEST_: /* two branches, both forward */ FWD(aft, aft, 1); FWD(aft, aft, OPND(s)); break; case O_QUEST: /* just an empty */ FWD(aft, aft, 1); break; case OLPAREN: /* not significant here */ case ORPAREN: FWD(aft, aft, 1); break; case OCH_: /* mark the first two branches */ FWD(aft, aft, 1); assert(OP(g->strip[pc+OPND(s)]) == OOR2); FWD(aft, aft, OPND(s)); break; case OOR1: /* done a branch, find the O_CH */ if (ISSTATEIN(aft, here)) { for (look = 1; OP(s = g->strip[pc+look]) != O_CH; look += OPND(s)) assert(OP(s) == OOR2); FWD(aft, aft, look); } break; case OOR2: /* propagate OCH_'s marking */ FWD(aft, aft, 1); if (OP(g->strip[pc+OPND(s)]) != O_CH) { assert(OP(g->strip[pc+OPND(s)]) == OOR2); FWD(aft, aft, OPND(s)); } break; case O_CH: /* just empty */ FWD(aft, aft, 1); break; default: /* ooooops... */ assert(nope); break; } } return(aft); } #ifdef REDEBUG /* - print - print a set of states == #ifdef REDEBUG == static void print(struct match *m, char *caption, states st, \ == int ch, FILE *d); == #endif */ static void print(m, caption, st, ch, d) struct match *m; char *caption; states st; int ch; FILE *d; { register struct re_guts *g = m->g; register int i; register int first = 1; if (!(m->eflags®_TRACE)) return; fprintf(d, "%s", caption); if (ch != '\0') fprintf(d, " %s", pchar(ch)); for (i = 0; i < g->nstates; i++) if (ISSET(st, i)) { fprintf(d, "%s%d", (first) ? "\t" : ", ", i); first = 0; } fprintf(d, "\n"); } /* - at - print current situation == #ifdef REDEBUG == static void at(struct match *m, char *title, char *start, char *stop, \ == sopno startst, sopno stopst); == #endif */ static void at(m, title, start, stop, startst, stopst) struct match *m; char *title; char *start; char *stop; sopno startst; sopno stopst; { if (!(m->eflags®_TRACE)) return; printf("%s %s-", title, pchar(*start)); printf("%s ", pchar(*stop)); printf("%ld-%ld\n", (long)startst, (long)stopst); } #ifndef PCHARDONE #define PCHARDONE /* never again */ /* - pchar - make a character printable == #ifdef REDEBUG == static char *pchar(int ch); == #endif * * Is this identical to regchar() over in debug.c? Well, yes. But a * duplicate here avoids having a debugging-capable regexec.o tied to * a matching debug.o, and this is convenient. It all disappears in * the non-debug compilation anyway, so it doesn't matter much. */ static char * /* -> representation */ pchar(ch) int ch; { static char pbuf[10]; if (isprint(ch) || ch == ' ') sprintf(pbuf, "%c", ch); else sprintf(pbuf, "\\%o", ch); return(pbuf); } #endif #endif #undef matcher #undef fast #undef slow #undef dissect #undef backref #undef step #undef print #undef at #undef match ./knews-1.0b.1/regexp/main.c100644 1244 1244 25621 6455455542 14244 0ustar kallekalle#include #include #include #include #include #include "main.ih" char *progname; int debug = 0; int line = 0; int status = 0; int copts = REG_EXTENDED; int eopts = 0; regoff_t startoff = 0; regoff_t endoff = 0; extern int split(); extern void regprint(); /* - main - do the simple case, hand off to regress() for regression */ main(argc, argv) int argc; char *argv[]; { regex_t re; # define NS 10 regmatch_t subs[NS]; char erbuf[100]; int err; size_t len; int c; int errflg = 0; register int i; extern int optind; extern char *optarg; progname = argv[0]; while ((c = getopt(argc, argv, "c:e:S:E:x")) != EOF) switch (c) { case 'c': /* compile options */ copts = options('c', optarg); break; case 'e': /* execute options */ eopts = options('e', optarg); break; case 'S': /* start offset */ startoff = (regoff_t)atoi(optarg); break; case 'E': /* end offset */ endoff = (regoff_t)atoi(optarg); break; case 'x': /* Debugging. */ debug++; break; case '?': default: errflg++; break; } if (errflg) { fprintf(stderr, "usage: %s ", progname); fprintf(stderr, "[-c copt][-C][-d] [re]\n"); exit(2); } if (optind >= argc) { regress(stdin); exit(status); } err = regcomp(&re, argv[optind++], copts); if (err) { len = regerror(err, &re, erbuf, sizeof(erbuf)); fprintf(stderr, "error %s, %d/%d `%s'\n", eprint(err), len, sizeof(erbuf), erbuf); exit(status); } regprint(&re, stdout); if (optind >= argc) { regfree(&re); exit(status); } if (eopts®_STARTEND) { subs[0].rm_so = startoff; subs[0].rm_eo = strlen(argv[optind]) - endoff; } err = regexec(&re, argv[optind], (size_t)NS, subs, eopts); if (err) { len = regerror(err, &re, erbuf, sizeof(erbuf)); fprintf(stderr, "error %s, %d/%d `%s'\n", eprint(err), len, sizeof(erbuf), erbuf); exit(status); } if (!(copts®_NOSUB)) { len = (int)(subs[0].rm_eo - subs[0].rm_so); if (subs[0].rm_so != -1) { if (len != 0) printf("match `%.*s'\n", len, argv[optind] + subs[0].rm_so); else printf("match `'@%.1s\n", argv[optind] + subs[0].rm_so); } for (i = 1; i < NS; i++) if (subs[i].rm_so != -1) printf("(%d) `%.*s'\n", i, (int)(subs[i].rm_eo - subs[i].rm_so), argv[optind] + subs[i].rm_so); } exit(status); } /* - regress - main loop of regression test == void regress(FILE *in); */ void regress(in) FILE *in; { char inbuf[1000]; # define MAXF 10 char *f[MAXF]; int nf; int i; char erbuf[100]; size_t ne; char *badpat = "invalid regular expression"; # define SHORT 10 char *bpname = "REG_BADPAT"; regex_t re; while (fgets(inbuf, sizeof(inbuf), in) != NULL) { line++; if (inbuf[0] == '#' || inbuf[0] == '\n') continue; /* NOTE CONTINUE */ inbuf[strlen(inbuf)-1] = '\0'; /* get rid of stupid \n */ if (debug) fprintf(stdout, "%d:\n", line); nf = split(inbuf, f, MAXF, "\t\t"); if (nf < 3) { fprintf(stderr, "bad input, line %d\n", line); exit(1); } for (i = 0; i < nf; i++) if (strcmp(f[i], "\"\"") == 0) f[i] = ""; if (nf <= 3) f[3] = NULL; if (nf <= 4) f[4] = NULL; try(f[0], f[1], f[2], f[3], f[4], options('c', f[1])); if (opt('&', f[1])) /* try with either type of RE */ try(f[0], f[1], f[2], f[3], f[4], options('c', f[1]) &~ REG_EXTENDED); } ne = regerror(REG_BADPAT, (regex_t *)NULL, erbuf, sizeof(erbuf)); if (strcmp(erbuf, badpat) != 0 || ne != strlen(badpat)+1) { fprintf(stderr, "end: regerror() test gave `%s' not `%s'\n", erbuf, badpat); status = 1; } ne = regerror(REG_BADPAT, (regex_t *)NULL, erbuf, (size_t)SHORT); if (strncmp(erbuf, badpat, SHORT-1) != 0 || erbuf[SHORT-1] != '\0' || ne != strlen(badpat)+1) { fprintf(stderr, "end: regerror() short test gave `%s' not `%.*s'\n", erbuf, SHORT-1, badpat); status = 1; } ne = regerror(REG_ITOA|REG_BADPAT, (regex_t *)NULL, erbuf, sizeof(erbuf)); if (strcmp(erbuf, bpname) != 0 || ne != strlen(bpname)+1) { fprintf(stderr, "end: regerror() ITOA test gave `%s' not `%s'\n", erbuf, bpname); status = 1; } re.re_endp = bpname; ne = regerror(REG_ATOI, &re, erbuf, sizeof(erbuf)); if (atoi(erbuf) != (int)REG_BADPAT) { fprintf(stderr, "end: regerror() ATOI test gave `%s' not `%ld'\n", erbuf, (long)REG_BADPAT); status = 1; } else if (ne != strlen(erbuf)+1) { fprintf(stderr, "end: regerror() ATOI test len(`%s') = %ld\n", erbuf, (long)REG_BADPAT); status = 1; } } /* - try - try it, and report on problems == void try(char *f0, char *f1, char *f2, char *f3, char *f4, int opts); */ void try(f0, f1, f2, f3, f4, opts) char *f0; char *f1; char *f2; char *f3; char *f4; int opts; /* may not match f1 */ { regex_t re; # define NSUBS 10 regmatch_t subs[NSUBS]; # define NSHOULD 15 char *should[NSHOULD]; int nshould; char erbuf[100]; int err; int len; char *type = (opts & REG_EXTENDED) ? "ERE" : "BRE"; register int i; char *grump; char f0copy[1000]; char f2copy[1000]; strcpy(f0copy, f0); re.re_endp = (opts®_PEND) ? f0copy + strlen(f0copy) : NULL; fixstr(f0copy); err = regcomp(&re, f0copy, opts); if (err != 0 && (!opt('C', f1) || err != efind(f2))) { /* unexpected error or wrong error */ len = regerror(err, &re, erbuf, sizeof(erbuf)); fprintf(stderr, "%d: %s error %s, %d/%d `%s'\n", line, type, eprint(err), len, sizeof(erbuf), erbuf); status = 1; } else if (err == 0 && opt('C', f1)) { /* unexpected success */ fprintf(stderr, "%d: %s should have given REG_%s\n", line, type, f2); status = 1; err = 1; /* so we won't try regexec */ } if (err != 0) { regfree(&re); return; } strcpy(f2copy, f2); fixstr(f2copy); if (options('e', f1)®_STARTEND) { if (strchr(f2, '(') == NULL || strchr(f2, ')') == NULL) fprintf(stderr, "%d: bad STARTEND syntax\n", line); subs[0].rm_so = strchr(f2, '(') - f2 + 1; subs[0].rm_eo = strchr(f2, ')') - f2; } err = regexec(&re, f2copy, NSUBS, subs, options('e', f1)); if (err != 0 && (f3 != NULL || err != REG_NOMATCH)) { /* unexpected error or wrong error */ len = regerror(err, &re, erbuf, sizeof(erbuf)); fprintf(stderr, "%d: %s exec error %s, %d/%d `%s'\n", line, type, eprint(err), len, sizeof(erbuf), erbuf); status = 1; } else if (err != 0) { /* nothing more to check */ } else if (f3 == NULL) { /* unexpected success */ fprintf(stderr, "%d: %s exec should have failed\n", line, type); status = 1; err = 1; /* just on principle */ } else if (opts®_NOSUB) { /* nothing more to check */ } else if ((grump = check(f2, subs[0], f3)) != NULL) { fprintf(stderr, "%d: %s %s\n", line, type, grump); status = 1; err = 1; } if (err != 0 || f4 == NULL) { regfree(&re); return; } for (i = 1; i < NSHOULD; i++) should[i] = NULL; nshould = split(f4, should+1, NSHOULD-1, ","); if (nshould == 0) { nshould = 1; should[1] = ""; } for (i = 1; i < NSUBS; i++) { grump = check(f2, subs[i], should[i]); if (grump != NULL) { fprintf(stderr, "%d: %s $%d %s\n", line, type, i, grump); status = 1; err = 1; } } regfree(&re); } /* - options - pick options out of a regression-test string == int options(int type, char *s); */ int options(type, s) int type; /* 'c' compile, 'e' exec */ char *s; { register char *p; register int o = (type == 'c') ? copts : eopts; register char *legal = (type == 'c') ? "bisnmp" : "^$#tl"; for (p = s; *p != '\0'; p++) if (strchr(legal, *p) != NULL) switch (*p) { case 'b': o &= ~REG_EXTENDED; break; case 'i': o |= REG_ICASE; break; case 's': o |= REG_NOSUB; break; case 'n': o |= REG_NEWLINE; break; case 'm': o &= ~REG_EXTENDED; o |= REG_NOSPEC; break; case 'p': o |= REG_PEND; break; case '^': o |= REG_NOTBOL; break; case '$': o |= REG_NOTEOL; break; case '#': o |= REG_STARTEND; break; case 't': /* trace */ o |= REG_TRACE; break; case 'l': /* force long representation */ o |= REG_LARGE; break; case 'r': /* force backref use */ o |= REG_BACKR; break; } return(o); } /* - opt - is a particular option in a regression string? == int opt(int c, char *s); */ int /* predicate */ opt(c, s) int c; char *s; { return(strchr(s, c) != NULL); } /* - fixstr - transform magic characters in strings == void fixstr(register char *p); */ void fixstr(p) register char *p; { if (p == NULL) return; for (; *p != '\0'; p++) if (*p == 'N') *p = '\n'; else if (*p == 'T') *p = '\t'; else if (*p == 'S') *p = ' '; else if (*p == 'Z') *p = '\0'; } /* - check - check a substring match == char *check(char *str, regmatch_t sub, char *should); */ char * /* NULL or complaint */ check(str, sub, should) char *str; regmatch_t sub; char *should; { register int len; register int shlen; register char *p; static char grump[500]; register char *at = NULL; if (should != NULL && strcmp(should, "-") == 0) should = NULL; if (should != NULL && should[0] == '@') { at = should + 1; should = ""; } /* check rm_so and rm_eo for consistency */ if (sub.rm_so > sub.rm_eo || (sub.rm_so == -1 && sub.rm_eo != -1) || (sub.rm_so != -1 && sub.rm_eo == -1) || (sub.rm_so != -1 && sub.rm_so < 0) || (sub.rm_eo != -1 && sub.rm_eo < 0) ) { sprintf(grump, "start %ld end %ld", (long)sub.rm_so, (long)sub.rm_eo); return(grump); } /* check for no match */ if (sub.rm_so == -1 && should == NULL) return(NULL); if (sub.rm_so == -1) return("did not match"); /* check for in range */ if (sub.rm_eo > strlen(str)) { sprintf(grump, "start %ld end %ld, past end of string", (long)sub.rm_so, (long)sub.rm_eo); return(grump); } len = (int)(sub.rm_eo - sub.rm_so); shlen = (int)strlen(should); p = str + sub.rm_so; /* check for not supposed to match */ if (should == NULL) { sprintf(grump, "matched `%.*s'", len, p); return(grump); } /* check for wrong match */ if (len != shlen || strncmp(p, should, (size_t)shlen) != 0) { sprintf(grump, "matched `%.*s' instead", len, p); return(grump); } if (shlen > 0) return(NULL); /* check null match in right place */ if (at == NULL) return(NULL); shlen = strlen(at); if (shlen == 0) shlen = 1; /* force check for end-of-string */ if (strncmp(p, at, shlen) != 0) { sprintf(grump, "matched null at `%.20s'", p); return(grump); } return(NULL); } /* - eprint - convert error number to name == static char *eprint(int err); */ static char * eprint(err) int err; { static char epbuf[100]; size_t len; len = regerror(REG_ITOA|err, (regex_t *)NULL, epbuf, sizeof(epbuf)); assert(len <= sizeof(epbuf)); return(epbuf); } /* - efind - convert error name to number == static int efind(char *name); */ static int efind(name) char *name; { static char efbuf[100]; size_t n; regex_t re; sprintf(efbuf, "REG_%s", name); assert(strlen(efbuf) < sizeof(efbuf)); re.re_endp = efbuf; (void) regerror(REG_ATOI, &re, efbuf, sizeof(efbuf)); return(atoi(efbuf)); } ./knews-1.0b.1/regexp/mkh100644 1244 1244 3441 6455455542 13632 0ustar kallekalle#! /bin/sh # mkh - pull headers out of C source PATH=/bin:/usr/bin ; export PATH # egrep pattern to pick out marked lines egrep='^ =([ ]|$)' # Sed program to process marked lines into lines for the header file. # The markers have already been removed. Two things are done here: removal # of backslashed newlines, and some fudging of comments. The first is done # because -o needs to have prototypes on one line to strip them down. # Getting comments into the output is tricky; we turn C++-style // comments # into /* */ comments, after altering any existing */'s to avoid trouble. peel=' /\\$/N /\\\n[ ]*/s///g /\/\//s;\*/;* /;g /\/\//s;//\(.*\);/*\1 */;' for a do case "$a" in -o) # old (pre-function-prototype) compiler # add code to comment out argument lists peel="$peel "'/^\([^#\/][^\/]*[a-zA-Z0-9_)]\)(\(.*\))/s;;\1(/*\2*/);' shift ;; -b) # funny Berkeley __P macro peel="$peel "'/^\([^#\/][^\/]*[a-zA-Z0-9_)]\)(\(.*\))/s;;\1 __P((\2));' shift ;; -s) # compiler doesn't like `static foo();' # add code to get rid of the `static' peel="$peel "'/^static[ ][^\/]*[a-zA-Z0-9_)](.*)/s;static.;;' shift ;; -p) # private declarations egrep='^ ==([ ]|$)' shift ;; -i) # wrap in #ifndef, argument is name ifndef="$2" shift ; shift ;; *) break ;; esac done if test " $ifndef" != " " then echo "#ifndef $ifndef" echo "#define $ifndef /* never again */" fi echo "/* ========= begin header generated by $0 ========= */" echo '#ifdef __cplusplus' echo 'extern "C" {' echo '#endif' for f do echo echo "/* === $f === */" egrep "$egrep" $f | sed 's/^ ==*[ ]//;s/^ ==*$//' | sed "$peel" echo done echo '#ifdef __cplusplus' echo '}' echo '#endif' echo "/* ========= end header generated by $0 ========= */" if test " $ifndef" != " " then echo "#endif" fi exit 0 ./knews-1.0b.1/regexp/regcomp.c100644 1244 1244 110657 6455455542 15000 0ustar kallekalle#include #include #include #include #include #include #include #include "utils.h" #include "regex2.h" #include "cclass.h" #include "cname.h" /* * parse structure, passed up and down to avoid global variables and * other clumsinesses */ struct parse { char *next; /* next character in RE */ char *end; /* end of string (-> NUL normally) */ int error; /* has an error been seen? */ sop *strip; /* malloced strip */ sopno ssize; /* malloced strip size (allocated) */ sopno slen; /* malloced strip length (used) */ int ncsalloc; /* number of csets allocated */ struct re_guts *g; # define NPAREN 10 /* we need to remember () 1-9 for back refs */ sopno pbegin[NPAREN]; /* -> ( ([0] unused) */ sopno pend[NPAREN]; /* -> ) ([0] unused) */ }; #include "regcomp.ih" static char nuls[10]; /* place to point scanner in event of error */ /* * macros for use with parse structure * BEWARE: these know that the parse structure is named `p' !!! */ #define PEEK() (*p->next) #define PEEK2() (*(p->next+1)) #define MORE() (p->next < p->end) #define MORE2() (p->next+1 < p->end) #define SEE(c) (MORE() && PEEK() == (c)) #define SEETWO(a, b) (MORE() && MORE2() && PEEK() == (a) && PEEK2() == (b)) #define EAT(c) ((SEE(c)) ? (NEXT(), 1) : 0) #define EATTWO(a, b) ((SEETWO(a, b)) ? (NEXT2(), 1) : 0) #define NEXT() (p->next++) #define NEXT2() (p->next += 2) #define NEXTn(n) (p->next += (n)) #define GETNEXT() (*p->next++) #define SETERROR(e) seterr(p, (e)) #define REQUIRE(co, e) ((co) || SETERROR(e)) #define MUSTSEE(c, e) (REQUIRE(MORE() && PEEK() == (c), e)) #define MUSTEAT(c, e) (REQUIRE(MORE() && GETNEXT() == (c), e)) #define MUSTNOTSEE(c, e) (REQUIRE(!MORE() || PEEK() != (c), e)) #define EMIT(op, sopnd) doemit(p, (sop)(op), (size_t)(sopnd)) #define INSERT(op, pos) doinsert(p, (sop)(op), HERE()-(pos)+1, pos) #define AHEAD(pos) dofwd(p, pos, HERE()-(pos)) #define ASTERN(sop, pos) EMIT(sop, HERE()-pos) #define HERE() (p->slen) #define THERE() (p->slen - 1) #define THERETHERE() (p->slen - 2) #define DROP(n) (p->slen -= (n)) #ifndef NDEBUG static int never = 0; /* for use in asserts; shuts lint up */ #else #define never 0 /* some s have bugs too */ #endif /* - regcomp - interface for parser and compilation = extern int regcomp(regex_t *, const char *, int); = #define REG_BASIC 0000 = #define REG_EXTENDED 0001 = #define REG_ICASE 0002 = #define REG_NOSUB 0004 = #define REG_NEWLINE 0010 = #define REG_NOSPEC 0020 = #define REG_PEND 0040 = #define REG_DUMP 0200 */ int /* 0 success, otherwise REG_something */ regcomp(preg, pattern, cflags) regex_t *preg; const char *pattern; int cflags; { struct parse pa; register struct re_guts *g; register struct parse *p = &pa; register int i; register size_t len; #ifdef REDEBUG # define GOODFLAGS(f) (f) #else # define GOODFLAGS(f) ((f)&~REG_DUMP) #endif cflags = GOODFLAGS(cflags); if ((cflags®_EXTENDED) && (cflags®_NOSPEC)) return(REG_INVARG); if (cflags®_PEND) { if (preg->re_endp < pattern) return(REG_INVARG); len = preg->re_endp - pattern; } else len = strlen((char *)pattern); /* do the mallocs early so failure handling is easy */ g = (struct re_guts *)malloc(sizeof(struct re_guts) + (NC-1)*sizeof(cat_t)); if (g == NULL) return(REG_ESPACE); p->ssize = len/(size_t)2*(size_t)3 + (size_t)1; /* ugh */ p->strip = (sop *)malloc(p->ssize * sizeof(sop)); p->slen = 0; if (p->strip == NULL) { free((char *)g); return(REG_ESPACE); } /* set things up */ p->g = g; p->next = (char *)pattern; /* convenience; we do not modify it */ p->end = p->next + len; p->error = 0; p->ncsalloc = 0; for (i = 0; i < NPAREN; i++) { p->pbegin[i] = 0; p->pend[i] = 0; } g->csetsize = NC; g->sets = NULL; g->setbits = NULL; g->ncsets = 0; g->cflags = cflags; g->iflags = 0; g->nbol = 0; g->neol = 0; g->must = NULL; g->mlen = 0; g->nsub = 0; g->ncategories = 1; /* category 0 is "everything else" */ g->categories = &g->catspace[-(CHAR_MIN)]; (void) memset((char *)g->catspace, 0, NC*sizeof(cat_t)); g->backrefs = 0; /* do it */ EMIT(OEND, 0); g->firststate = THERE(); if (cflags®_EXTENDED) p_ere(p, OUT); else if (cflags®_NOSPEC) p_str(p); else p_bre(p, OUT, OUT); EMIT(OEND, 0); g->laststate = THERE(); /* tidy up loose ends and fill things in */ categorize(p, g); stripsnug(p, g); findmust(p, g); g->nplus = pluscount(p, g); g->magic = MAGIC2; preg->re_nsub = g->nsub; preg->re_g = g; preg->re_magic = MAGIC1; #ifndef REDEBUG /* not debugging, so can't rely on the assert() in regexec() */ if (g->iflags&BAD) SETERROR(REG_ASSERT); #endif /* win or lose, we're done */ if (p->error != 0) /* lose */ regfree(preg); return(p->error); } /* - p_ere - ERE parser top level, concatenation and alternation == static void p_ere(register struct parse *p, int stop); */ static void p_ere(p, stop) register struct parse *p; int stop; /* character this ERE should end at */ { register char c; register sopno prevback; register sopno prevfwd; register sopno conc; register int first = 1; /* is this the first alternative? */ for (;;) { /* do a bunch of concatenated expressions */ conc = HERE(); while (MORE() && (c = PEEK()) != '|' && c != stop) p_ere_exp(p); REQUIRE(HERE() != conc, REG_EMPTY); /* require nonempty */ if (!EAT('|')) break; /* NOTE BREAK OUT */ if (first) { INSERT(OCH_, conc); /* offset is wrong */ prevfwd = conc; prevback = conc; first = 0; } ASTERN(OOR1, prevback); prevback = THERE(); AHEAD(prevfwd); /* fix previous offset */ prevfwd = HERE(); EMIT(OOR2, 0); /* offset is very wrong */ } if (!first) { /* tail-end fixups */ AHEAD(prevfwd); ASTERN(O_CH, prevback); } assert(!MORE() || SEE(stop)); } /* - p_ere_exp - parse one subERE, an atom possibly followed by a repetition op == static void p_ere_exp(register struct parse *p); */ static void p_ere_exp(p) register struct parse *p; { register char c; register sopno pos; register int count; register int count2; register sopno subno; int wascaret = 0; assert(MORE()); /* caller should have ensured this */ c = GETNEXT(); pos = HERE(); switch (c) { case '(': REQUIRE(MORE(), REG_EPAREN); p->g->nsub++; subno = p->g->nsub; if (subno < NPAREN) p->pbegin[subno] = HERE(); EMIT(OLPAREN, subno); if (!SEE(')')) p_ere(p, ')'); if (subno < NPAREN) { p->pend[subno] = HERE(); assert(p->pend[subno] != 0); } EMIT(ORPAREN, subno); MUSTEAT(')', REG_EPAREN); break; #ifndef POSIX_MISTAKE case ')': /* happens only if no current unmatched ( */ /* * You may ask, why the ifndef? Because I didn't notice * this until slightly too late for 1003.2, and none of the * other 1003.2 regular-expression reviewers noticed it at * all. So an unmatched ) is legal POSIX, at least until * we can get it fixed. */ SETERROR(REG_EPAREN); break; #endif case '^': EMIT(OBOL, 0); p->g->iflags |= USEBOL; p->g->nbol++; wascaret = 1; break; case '$': EMIT(OEOL, 0); p->g->iflags |= USEEOL; p->g->neol++; break; case '|': SETERROR(REG_EMPTY); break; case '*': case '+': case '?': SETERROR(REG_BADRPT); break; case '.': if (p->g->cflags®_NEWLINE) nonnewline(p); else EMIT(OANY, 0); break; case '[': p_bracket(p); break; case '\\': REQUIRE(MORE(), REG_EESCAPE); c = GETNEXT(); ordinary(p, c); break; case '{': /* okay as ordinary except if digit follows */ REQUIRE(!MORE() || !isdigit(PEEK()), REG_BADRPT); /* FALLTHROUGH */ default: ordinary(p, c); break; } if (!MORE()) return; c = PEEK(); /* we call { a repetition if followed by a digit */ if (!( c == '*' || c == '+' || c == '?' || (c == '{' && MORE2() && isdigit(PEEK2())) )) return; /* no repetition, we're done */ NEXT(); REQUIRE(!wascaret, REG_BADRPT); switch (c) { case '*': /* implemented as +? */ /* this case does not require the (y|) trick, noKLUDGE */ INSERT(OPLUS_, pos); ASTERN(O_PLUS, pos); INSERT(OQUEST_, pos); ASTERN(O_QUEST, pos); break; case '+': INSERT(OPLUS_, pos); ASTERN(O_PLUS, pos); break; case '?': /* KLUDGE: emit y? as (y|) until subtle bug gets fixed */ INSERT(OCH_, pos); /* offset slightly wrong */ ASTERN(OOR1, pos); /* this one's right */ AHEAD(pos); /* fix the OCH_ */ EMIT(OOR2, 0); /* offset very wrong... */ AHEAD(THERE()); /* ...so fix it */ ASTERN(O_CH, THERETHERE()); break; case '{': count = p_count(p); if (EAT(',')) { if (isdigit(PEEK())) { count2 = p_count(p); REQUIRE(count <= count2, REG_BADBR); } else /* single number with comma */ count2 = INFINITY; } else /* just a single number */ count2 = count; repeat(p, pos, count, count2); if (!EAT('}')) { /* error heuristics */ while (MORE() && PEEK() != '}') NEXT(); REQUIRE(MORE(), REG_EBRACE); SETERROR(REG_BADBR); } break; } if (!MORE()) return; c = PEEK(); if (!( c == '*' || c == '+' || c == '?' || (c == '{' && MORE2() && isdigit(PEEK2())) ) ) return; SETERROR(REG_BADRPT); } /* - p_str - string (no metacharacters) "parser" == static void p_str(register struct parse *p); */ static void p_str(p) register struct parse *p; { REQUIRE(MORE(), REG_EMPTY); while (MORE()) ordinary(p, GETNEXT()); } /* - p_bre - BRE parser top level, anchoring and concatenation == static void p_bre(register struct parse *p, register int end1, \ == register int end2); * Giving end1 as OUT essentially eliminates the end1/end2 check. * * This implementation is a bit of a kludge, in that a trailing $ is first * taken as an ordinary character and then revised to be an anchor. The * only undesirable side effect is that '$' gets included as a character * category in such cases. This is fairly harmless; not worth fixing. * The amount of lookahead needed to avoid this kludge is excessive. */ static void p_bre(p, end1, end2) register struct parse *p; register int end1; /* first terminating character */ register int end2; /* second terminating character */ { register sopno start = HERE(); register int first = 1; /* first subexpression? */ register int wasdollar = 0; if (EAT('^')) { EMIT(OBOL, 0); p->g->iflags |= USEBOL; p->g->nbol++; } while (MORE() && !SEETWO(end1, end2)) { wasdollar = p_simp_re(p, first); first = 0; } if (wasdollar) { /* oops, that was a trailing anchor */ DROP(1); EMIT(OEOL, 0); p->g->iflags |= USEEOL; p->g->neol++; } REQUIRE(HERE() != start, REG_EMPTY); /* require nonempty */ } /* - p_simp_re - parse a simple RE, an atom possibly followed by a repetition == static int p_simp_re(register struct parse *p, int starordinary); */ static int /* was the simple RE an unbackslashed $? */ p_simp_re(p, starordinary) register struct parse *p; int starordinary; /* is a leading * an ordinary character? */ { register int c; register int count; register int count2; register sopno pos; register int i; register sopno subno; # define BACKSL (1<g->cflags®_NEWLINE) nonnewline(p); else EMIT(OANY, 0); break; case '[': p_bracket(p); break; case BACKSL|'{': SETERROR(REG_BADRPT); break; case BACKSL|'(': p->g->nsub++; subno = p->g->nsub; if (subno < NPAREN) p->pbegin[subno] = HERE(); EMIT(OLPAREN, subno); /* the MORE here is an error heuristic */ if (MORE() && !SEETWO('\\', ')')) p_bre(p, '\\', ')'); if (subno < NPAREN) { p->pend[subno] = HERE(); assert(p->pend[subno] != 0); } EMIT(ORPAREN, subno); REQUIRE(EATTWO('\\', ')'), REG_EPAREN); break; case BACKSL|')': /* should not get here -- must be user */ case BACKSL|'}': SETERROR(REG_EPAREN); break; case BACKSL|'1': case BACKSL|'2': case BACKSL|'3': case BACKSL|'4': case BACKSL|'5': case BACKSL|'6': case BACKSL|'7': case BACKSL|'8': case BACKSL|'9': i = (c&~BACKSL) - '0'; assert(i < NPAREN); if (p->pend[i] != 0) { assert(i <= p->g->nsub); EMIT(OBACK_, i); assert(p->pbegin[i] != 0); assert(OP(p->strip[p->pbegin[i]]) == OLPAREN); assert(OP(p->strip[p->pend[i]]) == ORPAREN); (void) dupl(p, p->pbegin[i]+1, p->pend[i]); EMIT(O_BACK, i); } else SETERROR(REG_ESUBREG); p->g->backrefs = 1; break; case '*': REQUIRE(starordinary, REG_BADRPT); /* FALLTHROUGH */ default: ordinary(p, c &~ BACKSL); break; } if (EAT('*')) { /* implemented as +? */ /* this case does not require the (y|) trick, noKLUDGE */ INSERT(OPLUS_, pos); ASTERN(O_PLUS, pos); INSERT(OQUEST_, pos); ASTERN(O_QUEST, pos); } else if (EATTWO('\\', '{')) { count = p_count(p); if (EAT(',')) { if (MORE() && isdigit(PEEK())) { count2 = p_count(p); REQUIRE(count <= count2, REG_BADBR); } else /* single number with comma */ count2 = INFINITY; } else /* just a single number */ count2 = count; repeat(p, pos, count, count2); if (!EATTWO('\\', '}')) { /* error heuristics */ while (MORE() && !SEETWO('\\', '}')) NEXT(); REQUIRE(MORE(), REG_EBRACE); SETERROR(REG_BADBR); } } else if (c == (unsigned char)'$') /* $ (but not \$) ends it */ return(1); return(0); } /* - p_count - parse a repetition count == static int p_count(register struct parse *p); */ static int /* the value */ p_count(p) register struct parse *p; { register int count = 0; register int ndigits = 0; while (MORE() && isdigit(PEEK()) && count <= DUPMAX) { count = count*10 + (GETNEXT() - '0'); ndigits++; } REQUIRE(ndigits > 0 && count <= DUPMAX, REG_BADBR); return(count); } /* - p_bracket - parse a bracketed character list == static void p_bracket(register struct parse *p); * * Note a significant property of this code: if the allocset() did SETERROR, * no set operations are done. */ static void p_bracket(p) register struct parse *p; { register char c; register cset *cs = allocset(p); register int invert = 0; /* Dept of Truly Sickening Special-Case Kludges */ if (p->next + 5 < p->end && strncmp(p->next, "[:<:]]", 6) == 0) { EMIT(OBOW, 0); NEXTn(6); return; } if (p->next + 5 < p->end && strncmp(p->next, "[:>:]]", 6) == 0) { EMIT(OEOW, 0); NEXTn(6); return; } if (EAT('^')) invert++; /* make note to invert set at end */ if (EAT(']')) CHadd(cs, ']'); else if (EAT('-')) CHadd(cs, '-'); while (MORE() && PEEK() != ']' && !SEETWO('-', ']')) p_b_term(p, cs); if (EAT('-')) CHadd(cs, '-'); MUSTEAT(']', REG_EBRACK); if (p->error != 0) /* don't mess things up further */ return; if (p->g->cflags®_ICASE) { register int i; register int ci; for (i = p->g->csetsize - 1; i >= 0; i--) if (CHIN(cs, i) && isalpha(i)) { ci = othercase(i); if (ci != i) CHadd(cs, ci); } if (cs->multis != NULL) mccase(p, cs); } if (invert) { register int i; for (i = p->g->csetsize - 1; i >= 0; i--) if (CHIN(cs, i)) CHsub(cs, i); else CHadd(cs, i); if (p->g->cflags®_NEWLINE) CHsub(cs, '\n'); if (cs->multis != NULL) mcinvert(p, cs); } assert(cs->multis == NULL); /* xxx */ if (nch(p, cs) == 1) { /* optimize singleton sets */ ordinary(p, firstch(p, cs)); freeset(p, cs); } else EMIT(OANYOF, freezeset(p, cs)); } /* - p_b_term - parse one term of a bracketed character list == static void p_b_term(register struct parse *p, register cset *cs); */ static void p_b_term(p, cs) register struct parse *p; register cset *cs; { register char c; register char start, finish; register int i; /* classify what we've got */ switch ((MORE()) ? PEEK() : '\0') { case '[': c = (MORE2()) ? PEEK2() : '\0'; break; case '-': SETERROR(REG_ERANGE); return; /* NOTE RETURN */ break; default: c = '\0'; break; } switch (c) { case ':': /* character class */ NEXT2(); REQUIRE(MORE(), REG_EBRACK); c = PEEK(); REQUIRE(c != '-' && c != ']', REG_ECTYPE); p_b_cclass(p, cs); REQUIRE(MORE(), REG_EBRACK); REQUIRE(EATTWO(':', ']'), REG_ECTYPE); break; case '=': /* equivalence class */ NEXT2(); REQUIRE(MORE(), REG_EBRACK); c = PEEK(); REQUIRE(c != '-' && c != ']', REG_ECOLLATE); p_b_eclass(p, cs); REQUIRE(MORE(), REG_EBRACK); REQUIRE(EATTWO('=', ']'), REG_ECOLLATE); break; default: /* symbol, ordinary character, or range */ /* xxx revision needed for multichar stuff */ start = p_b_symbol(p); if (SEE('-') && MORE2() && PEEK2() != ']') { /* range */ NEXT(); if (EAT('-')) finish = '-'; else finish = p_b_symbol(p); } else finish = start; /* xxx what about signed chars here... */ REQUIRE(start <= finish, REG_ERANGE); for (i = start; i <= finish; i++) CHadd(cs, i); break; } } /* - p_b_cclass - parse a character-class name and deal with it == static void p_b_cclass(register struct parse *p, register cset *cs); */ static void p_b_cclass(p, cs) register struct parse *p; register cset *cs; { register char *sp = p->next; register struct cclass *cp; register size_t len; register char *u; register char c; while (MORE() && isalpha(PEEK())) NEXT(); len = p->next - sp; for (cp = cclasses; cp->name != NULL; cp++) if (strncmp(cp->name, sp, len) == 0 && cp->name[len] == '\0') break; if (cp->name == NULL) { /* oops, didn't find it */ SETERROR(REG_ECTYPE); return; } u = cp->chars; while ((c = *u++) != '\0') CHadd(cs, c); for (u = cp->multis; *u != '\0'; u += strlen(u) + 1) MCadd(p, cs, u); } /* - p_b_eclass - parse an equivalence-class name and deal with it == static void p_b_eclass(register struct parse *p, register cset *cs); * * This implementation is incomplete. xxx */ static void p_b_eclass(p, cs) register struct parse *p; register cset *cs; { register char c; c = p_b_coll_elem(p, '='); CHadd(cs, c); } /* - p_b_symbol - parse a character or [..]ed multicharacter collating symbol == static char p_b_symbol(register struct parse *p); */ static char /* value of symbol */ p_b_symbol(p) register struct parse *p; { register char value; REQUIRE(MORE(), REG_EBRACK); if (!EATTWO('[', '.')) return(GETNEXT()); /* collating symbol */ value = p_b_coll_elem(p, '.'); REQUIRE(EATTWO('.', ']'), REG_ECOLLATE); return(value); } /* - p_b_coll_elem - parse a collating-element name and look it up == static char p_b_coll_elem(register struct parse *p, int endc); */ static char /* value of collating element */ p_b_coll_elem(p, endc) register struct parse *p; int endc; /* name ended by endc,']' */ { register char *sp = p->next; register struct cname *cp; register int len; register char c; while (MORE() && !SEETWO(endc, ']')) NEXT(); if (!MORE()) { SETERROR(REG_EBRACK); return(0); } len = p->next - sp; for (cp = cnames; cp->name != NULL; cp++) if (strncmp(cp->name, sp, len) == 0 && cp->name[len] == '\0') return(cp->code); /* known name */ if (len == 1) return(*sp); /* single character */ SETERROR(REG_ECOLLATE); /* neither */ return(0); } /* - othercase - return the case counterpart of an alphabetic == static char othercase(int ch); */ static char /* if no counterpart, return ch */ othercase(ch) int ch; { assert(isalpha(ch)); if (isupper(ch)) return(tolower(ch)); else if (islower(ch)) return(toupper(ch)); else /* peculiar, but could happen */ return(ch); } /* - bothcases - emit a dualcase version of a two-case character == static void bothcases(register struct parse *p, int ch); * * Boy, is this implementation ever a kludge... */ static void bothcases(p, ch) register struct parse *p; int ch; { register char *oldnext = p->next; register char *oldend = p->end; char bracket[3]; assert(othercase(ch) != ch); /* p_bracket() would recurse */ p->next = bracket; p->end = bracket+2; bracket[0] = ch; bracket[1] = ']'; bracket[2] = '\0'; p_bracket(p); assert(p->next == bracket+2); p->next = oldnext; p->end = oldend; } /* - ordinary - emit an ordinary character == static void ordinary(register struct parse *p, register int ch); */ static void ordinary(p, ch) register struct parse *p; register int ch; { register cat_t *cap = p->g->categories; if ((p->g->cflags®_ICASE) && isalpha(ch) && othercase(ch) != ch) bothcases(p, ch); else { EMIT(OCHAR, (unsigned char)ch); if (cap[ch] == 0) cap[ch] = p->g->ncategories++; } } /* - nonnewline - emit REG_NEWLINE version of OANY == static void nonnewline(register struct parse *p); * * Boy, is this implementation ever a kludge... */ static void nonnewline(p) register struct parse *p; { register char *oldnext = p->next; register char *oldend = p->end; char bracket[4]; p->next = bracket; p->end = bracket+3; bracket[0] = '^'; bracket[1] = '\n'; bracket[2] = ']'; bracket[3] = '\0'; p_bracket(p); assert(p->next == bracket+3); p->next = oldnext; p->end = oldend; } /* - repeat - generate code for a bounded repetition, recursively if needed == static void repeat(register struct parse *p, sopno start, int from, int to); */ static void repeat(p, start, from, to) register struct parse *p; sopno start; /* operand from here to end of strip */ int from; /* repeated from this number */ int to; /* to this number of times (maybe INFINITY) */ { register sopno finish = HERE(); # define N 2 # define INF 3 # define REP(f, t) ((f)*8 + (t)) # define MAP(n) (((n) <= 1) ? (n) : ((n) == INFINITY) ? INF : N) register sopno copy; if (p->error != 0) /* head off possible runaway recursion */ return; assert(from <= to); switch (REP(MAP(from), MAP(to))) { case REP(0, 0): /* must be user doing this */ DROP(finish-start); /* drop the operand */ break; case REP(0, 1): /* as x{1,1}? */ case REP(0, N): /* as x{1,n}? */ case REP(0, INF): /* as x{1,}? */ /* KLUDGE: emit y? as (y|) until subtle bug gets fixed */ INSERT(OCH_, start); /* offset is wrong... */ repeat(p, start+1, 1, to); ASTERN(OOR1, start); AHEAD(start); /* ... fix it */ EMIT(OOR2, 0); AHEAD(THERE()); ASTERN(O_CH, THERETHERE()); break; case REP(1, 1): /* trivial case */ /* done */ break; case REP(1, N): /* as x?x{1,n-1} */ /* KLUDGE: emit y? as (y|) until subtle bug gets fixed */ INSERT(OCH_, start); ASTERN(OOR1, start); AHEAD(start); EMIT(OOR2, 0); /* offset very wrong... */ AHEAD(THERE()); /* ...so fix it */ ASTERN(O_CH, THERETHERE()); copy = dupl(p, start+1, finish+1); assert(copy == finish+4); repeat(p, copy, 1, to-1); break; case REP(1, INF): /* as x+ */ INSERT(OPLUS_, start); ASTERN(O_PLUS, start); break; case REP(N, N): /* as xx{m-1,n-1} */ copy = dupl(p, start, finish); repeat(p, copy, from-1, to-1); break; case REP(N, INF): /* as xx{n-1,INF} */ copy = dupl(p, start, finish); repeat(p, copy, from-1, to); break; default: /* "can't happen" */ SETERROR(REG_ASSERT); /* just in case */ break; } } /* - seterr - set an error condition == static int seterr(register struct parse *p, int e); */ static int /* useless but makes type checking happy */ seterr(p, e) register struct parse *p; int e; { if (p->error == 0) /* keep earliest error condition */ p->error = e; p->next = nuls; /* try to bring things to a halt */ p->end = nuls; return(0); /* make the return value well-defined */ } /* - allocset - allocate a set of characters for [] == static cset *allocset(register struct parse *p); */ static cset * allocset(p) register struct parse *p; { register int no = p->g->ncsets++; register size_t nc; register size_t nbytes; register cset *cs; register size_t css = (size_t)p->g->csetsize; register int i; if (no >= p->ncsalloc) { /* need another column of space */ p->ncsalloc += CHAR_BIT; nc = p->ncsalloc; assert(nc % CHAR_BIT == 0); nbytes = nc / CHAR_BIT * css; if (p->g->sets == NULL) p->g->sets = (cset *)malloc(nc * sizeof(cset)); else p->g->sets = (cset *)realloc((char *)p->g->sets, nc * sizeof(cset)); if (p->g->setbits == NULL) p->g->setbits = (uch *)malloc(nbytes); else { p->g->setbits = (uch *)realloc((char *)p->g->setbits, nbytes); /* xxx this isn't right if setbits is now NULL */ for (i = 0; i < no; i++) p->g->sets[i].ptr = p->g->setbits + css*(i/CHAR_BIT); } if (p->g->sets != NULL && p->g->setbits != NULL) (void) memset((char *)p->g->setbits + (nbytes - css), 0, css); else { no = 0; SETERROR(REG_ESPACE); /* caller's responsibility not to do set ops */ } } assert(p->g->sets != NULL); /* xxx */ cs = &p->g->sets[no]; cs->ptr = p->g->setbits + css*((no)/CHAR_BIT); cs->mask = 1 << ((no) % CHAR_BIT); cs->hash = 0; cs->smultis = 0; cs->multis = NULL; return(cs); } /* - freeset - free a now-unused set == static void freeset(register struct parse *p, register cset *cs); */ static void freeset(p, cs) register struct parse *p; register cset *cs; { register int i; register cset *top = &p->g->sets[p->g->ncsets]; register size_t css = (size_t)p->g->csetsize; for (i = 0; i < css; i++) CHsub(cs, i); if (cs == top-1) /* recover only the easy case */ p->g->ncsets--; } /* - freezeset - final processing on a set of characters == static int freezeset(register struct parse *p, register cset *cs); * * The main task here is merging identical sets. This is usually a waste * of time (although the hash code minimizes the overhead), but can win * big if REG_ICASE is being used. REG_ICASE, by the way, is why the hash * is done using addition rather than xor -- all ASCII [aA] sets xor to * the same value! */ static int /* set number */ freezeset(p, cs) register struct parse *p; register cset *cs; { register uch h = cs->hash; register int i; register cset *top = &p->g->sets[p->g->ncsets]; register cset *cs2; register size_t css = (size_t)p->g->csetsize; /* look for an earlier one which is the same */ for (cs2 = &p->g->sets[0]; cs2 < top; cs2++) if (cs2->hash == h && cs2 != cs) { /* maybe */ for (i = 0; i < css; i++) if (!!CHIN(cs2, i) != !!CHIN(cs, i)) break; /* no */ if (i == css) break; /* yes */ } if (cs2 < top) { /* found one */ freeset(p, cs); cs = cs2; } return((int)(cs - p->g->sets)); } /* - firstch - return first character in a set (which must have at least one) == static int firstch(register struct parse *p, register cset *cs); */ static int /* character; there is no "none" value */ firstch(p, cs) register struct parse *p; register cset *cs; { register int i; register size_t css = (size_t)p->g->csetsize; for (i = 0; i < css; i++) if (CHIN(cs, i)) return((char)i); assert(never); return(0); /* arbitrary */ } /* - nch - number of characters in a set == static int nch(register struct parse *p, register cset *cs); */ static int nch(p, cs) register struct parse *p; register cset *cs; { register int i; register size_t css = (size_t)p->g->csetsize; register int n = 0; for (i = 0; i < css; i++) if (CHIN(cs, i)) n++; return(n); } /* - mcadd - add a collating element to a cset == static void mcadd(register struct parse *p, register cset *cs, \ == register char *cp); */ static void mcadd(p, cs, cp) register struct parse *p; register cset *cs; register char *cp; { register size_t oldend = cs->smultis; cs->smultis += strlen(cp) + 1; if (cs->multis == NULL) cs->multis = malloc(cs->smultis); else cs->multis = realloc(cs->multis, cs->smultis); if (cs->multis == NULL) { SETERROR(REG_ESPACE); return; } (void) strcpy(cs->multis + oldend - 1, cp); cs->multis[cs->smultis - 1] = '\0'; } /* - mcsub - subtract a collating element from a cset == static void mcsub(register cset *cs, register char *cp); */ static void mcsub(cs, cp) register cset *cs; register char *cp; { register char *fp = mcfind(cs, cp); register size_t len = strlen(fp); assert(fp != NULL); (void) memmove(fp, fp + len + 1, cs->smultis - (fp + len + 1 - cs->multis)); cs->smultis -= len; if (cs->smultis == 0) { free(cs->multis); cs->multis = NULL; return; } cs->multis = realloc(cs->multis, cs->smultis); assert(cs->multis != NULL); } /* - mcin - is a collating element in a cset? == static int mcin(register cset *cs, register char *cp); */ static int mcin(cs, cp) register cset *cs; register char *cp; { return(mcfind(cs, cp) != NULL); } /* - mcfind - find a collating element in a cset == static char *mcfind(register cset *cs, register char *cp); */ static char * mcfind(cs, cp) register cset *cs; register char *cp; { register char *p; if (cs->multis == NULL) return(NULL); for (p = cs->multis; *p != '\0'; p += strlen(p) + 1) if (strcmp(cp, p) == 0) return(p); return(NULL); } /* - mcinvert - invert the list of collating elements in a cset == static void mcinvert(register struct parse *p, register cset *cs); * * This would have to know the set of possibilities. Implementation * is deferred. */ static void mcinvert(p, cs) register struct parse *p; register cset *cs; { assert(cs->multis == NULL); /* xxx */ } /* - mccase - add case counterparts of the list of collating elements in a cset == static void mccase(register struct parse *p, register cset *cs); * * This would have to know the set of possibilities. Implementation * is deferred. */ static void mccase(p, cs) register struct parse *p; register cset *cs; { assert(cs->multis == NULL); /* xxx */ } /* - isinsets - is this character in any sets? == static int isinsets(register struct re_guts *g, int c); */ static int /* predicate */ isinsets(g, c) register struct re_guts *g; int c; { register uch *col; register int i; register int ncols = (g->ncsets+(CHAR_BIT-1)) / CHAR_BIT; register unsigned uc = (unsigned char)c; for (i = 0, col = g->setbits; i < ncols; i++, col += g->csetsize) if (col[uc] != 0) return(1); return(0); } /* - samesets - are these two characters in exactly the same sets? == static int samesets(register struct re_guts *g, int c1, int c2); */ static int /* predicate */ samesets(g, c1, c2) register struct re_guts *g; int c1; int c2; { register uch *col; register int i; register int ncols = (g->ncsets+(CHAR_BIT-1)) / CHAR_BIT; register unsigned uc1 = (unsigned char)c1; register unsigned uc2 = (unsigned char)c2; for (i = 0, col = g->setbits; i < ncols; i++, col += g->csetsize) if (col[uc1] != col[uc2]) return(0); return(1); } /* - categorize - sort out character categories == static void categorize(struct parse *p, register struct re_guts *g); */ static void categorize(p, g) struct parse *p; register struct re_guts *g; { register cat_t *cats = g->categories; register int c; register int c2; register cat_t cat; /* avoid making error situations worse */ if (p->error != 0) return; for (c = CHAR_MIN; c <= CHAR_MAX; c++) if (cats[c] == 0 && isinsets(g, c)) { cat = g->ncategories++; cats[c] = cat; for (c2 = c+1; c2 <= CHAR_MAX; c2++) if (cats[c2] == 0 && samesets(g, c, c2)) cats[c2] = cat; } } /* - dupl - emit a duplicate of a bunch of sops == static sopno dupl(register struct parse *p, sopno start, sopno finish); */ static sopno /* start of duplicate */ dupl(p, start, finish) register struct parse *p; sopno start; /* from here */ sopno finish; /* to this less one */ { register sopno ret = HERE(); register sopno len = finish - start; assert(finish >= start); if (len == 0) return(ret); enlarge(p, p->ssize + len); /* this many unexpected additions */ assert(p->ssize >= p->slen + len); (void) memcpy((char *)(p->strip + p->slen), (char *)(p->strip + start), (size_t)len*sizeof(sop)); p->slen += len; return(ret); } /* - doemit - emit a strip operator == static void doemit(register struct parse *p, sop op, size_t opnd); * * It might seem better to implement this as a macro with a function as * hard-case backup, but it's just too big and messy unless there are * some changes to the data structures. Maybe later. */ static void doemit(p, op, opnd) register struct parse *p; sop op; size_t opnd; { /* avoid making error situations worse */ if (p->error != 0) return; /* deal with oversize operands ("can't happen", more or less) */ assert(opnd < 1<slen >= p->ssize) enlarge(p, (p->ssize+1) / 2 * 3); /* +50% */ assert(p->slen < p->ssize); /* finally, it's all reduced to the easy case */ p->strip[p->slen++] = SOP(op, opnd); } /* - doinsert - insert a sop into the strip == static void doinsert(register struct parse *p, sop op, size_t opnd, sopno pos); */ static void doinsert(p, op, opnd, pos) register struct parse *p; sop op; size_t opnd; sopno pos; { register sopno sn; register sop s; register int i; /* avoid making error situations worse */ if (p->error != 0) return; sn = HERE(); EMIT(op, opnd); /* do checks, ensure space */ assert(HERE() == sn+1); s = p->strip[sn]; /* adjust paren pointers */ assert(pos > 0); for (i = 1; i < NPAREN; i++) { if (p->pbegin[i] >= pos) { p->pbegin[i]++; } if (p->pend[i] >= pos) { p->pend[i]++; } } memmove((char *)&p->strip[pos+1], (char *)&p->strip[pos], (HERE()-pos-1)*sizeof(sop)); p->strip[pos] = s; } /* - dofwd - complete a forward reference == static void dofwd(register struct parse *p, sopno pos, sop value); */ static void dofwd(p, pos, value) register struct parse *p; register sopno pos; sop value; { /* avoid making error situations worse */ if (p->error != 0) return; assert(value < 1<strip[pos] = OP(p->strip[pos]) | value; } /* - enlarge - enlarge the strip == static void enlarge(register struct parse *p, sopno size); */ static void enlarge(p, size) register struct parse *p; register sopno size; { register sop *sp; if (p->ssize >= size) return; sp = (sop *)realloc(p->strip, size*sizeof(sop)); if (sp == NULL) { SETERROR(REG_ESPACE); return; } p->strip = sp; p->ssize = size; } /* - stripsnug - compact the strip == static void stripsnug(register struct parse *p, register struct re_guts *g); */ static void stripsnug(p, g) register struct parse *p; register struct re_guts *g; { g->nstates = p->slen; g->strip = (sop *)realloc((char *)p->strip, p->slen * sizeof(sop)); if (g->strip == NULL) { SETERROR(REG_ESPACE); g->strip = p->strip; } } /* - findmust - fill in must and mlen with longest mandatory literal string == static void findmust(register struct parse *p, register struct re_guts *g); * * This algorithm could do fancy things like analyzing the operands of | * for common subsequences. Someday. This code is simple and finds most * of the interesting cases. * * Note that must and mlen got initialized during setup. */ static void findmust(p, g) struct parse *p; register struct re_guts *g; { register sop *scan; sop *start; register sop *newstart; register sopno newlen; register sop s; register char *cp; register sopno i; /* avoid making error situations worse */ if (p->error != 0) return; /* find the longest OCHAR sequence in strip */ newlen = 0; scan = g->strip + 1; do { s = *scan++; switch (OP(s)) { case OCHAR: /* sequence member */ if (newlen == 0) /* new sequence */ newstart = scan - 1; newlen++; break; case OPLUS_: /* things that don't break one */ case OLPAREN: case ORPAREN: break; case OQUEST_: /* things that must be skipped */ case OCH_: scan--; do { scan += OPND(s); s = *scan; /* assert() interferes w debug printouts */ if (OP(s) != O_QUEST && OP(s) != O_CH && OP(s) != OOR2) { g->iflags |= BAD; return; } } while (OP(s) != O_QUEST && OP(s) != O_CH); /* fallthrough */ default: /* things that break a sequence */ if (newlen > g->mlen) { /* ends one */ start = newstart; g->mlen = newlen; } newlen = 0; break; } } while (OP(s) != OEND); if (g->mlen == 0) /* there isn't one */ return; /* turn it into a character string */ g->must = malloc((size_t)g->mlen + 1); if (g->must == NULL) { /* argh; just forget it */ g->mlen = 0; return; } cp = g->must; scan = start; for (i = g->mlen; i > 0; i--) { while (OP(s = *scan++) != OCHAR) continue; assert(cp < g->must + g->mlen); *cp++ = (char)OPND(s); } assert(cp == g->must + g->mlen); *cp++ = '\0'; /* just on general principles */ } /* - pluscount - count + nesting == static sopno pluscount(register struct parse *p, register struct re_guts *g); */ static sopno /* nesting depth */ pluscount(p, g) struct parse *p; register struct re_guts *g; { register sop *scan; register sop s; register sopno plusnest = 0; register sopno maxnest = 0; if (p->error != 0) return(0); /* there may not be an OEND */ scan = g->strip + 1; do { s = *scan++; switch (OP(s)) { case OPLUS_: plusnest++; break; case O_PLUS: if (plusnest > maxnest) maxnest = plusnest; plusnest--; break; } } while (OP(s) != OEND); if (plusnest != 0) g->iflags |= BAD; return(maxnest); } ./knews-1.0b.1/regexp/regerror.c100644 1244 1244 6116 6455455542 15125 0ustar kallekalle#include #include #include #include #include #include #include #include "utils.h" #include "regerror.ih" /* = #define REG_NOMATCH 1 = #define REG_BADPAT 2 = #define REG_ECOLLATE 3 = #define REG_ECTYPE 4 = #define REG_EESCAPE 5 = #define REG_ESUBREG 6 = #define REG_EBRACK 7 = #define REG_EPAREN 8 = #define REG_EBRACE 9 = #define REG_BADBR 10 = #define REG_ERANGE 11 = #define REG_ESPACE 12 = #define REG_BADRPT 13 = #define REG_EMPTY 14 = #define REG_ASSERT 15 = #define REG_INVARG 16 = #define REG_ATOI 255 // convert name to number (!) = #define REG_ITOA 0400 // convert number to name (!) */ static struct rerr { int code; char *name; char *explain; } rerrs[] = { REG_NOMATCH, "REG_NOMATCH", "regexec() failed to match", REG_BADPAT, "REG_BADPAT", "invalid regular expression", REG_ECOLLATE, "REG_ECOLLATE", "invalid collating element", REG_ECTYPE, "REG_ECTYPE", "invalid character class", REG_EESCAPE, "REG_EESCAPE", "trailing backslash (\\)", REG_ESUBREG, "REG_ESUBREG", "invalid backreference number", REG_EBRACK, "REG_EBRACK", "brackets ([ ]) not balanced", REG_EPAREN, "REG_EPAREN", "parentheses not balanced", REG_EBRACE, "REG_EBRACE", "braces not balanced", REG_BADBR, "REG_BADBR", "invalid repetition count(s)", REG_ERANGE, "REG_ERANGE", "invalid character range", REG_ESPACE, "REG_ESPACE", "out of memory", REG_BADRPT, "REG_BADRPT", "repetition-operator operand invalid", REG_EMPTY, "REG_EMPTY", "empty (sub)expression", REG_ASSERT, "REG_ASSERT", "\"can't happen\" -- you found a bug", REG_INVARG, "REG_INVARG", "invalid argument to regex routine", 0, "", "*** unknown regexp error code ***", }; /* - regerror - the interface to error numbers = extern size_t regerror(int, const regex_t *, char *, size_t); */ /* ARGSUSED */ size_t regerror(errcode, preg, errbuf, errbuf_size) int errcode; const regex_t *preg; char *errbuf; size_t errbuf_size; { register struct rerr *r; register size_t len; register int target = errcode &~ REG_ITOA; register char *s; char convbuf[50]; if (errcode == REG_ATOI) s = regatoi(preg, convbuf); else { for (r = rerrs; r->code != 0; r++) if (r->code == target) break; if (errcode®_ITOA) { if (r->code != 0) (void) strcpy(convbuf, r->name); else sprintf(convbuf, "REG_0x%x", target); assert(strlen(convbuf) < sizeof(convbuf)); s = convbuf; } else s = r->explain; } len = strlen(s) + 1; if (errbuf_size > 0) { if (errbuf_size > len) (void) strcpy(errbuf, s); else { (void) strncpy(errbuf, s, errbuf_size-1); errbuf[errbuf_size-1] = '\0'; } } return(len); } /* - regatoi - internal routine to implement REG_ATOI == static char *regatoi(const regex_t *preg, char *localbuf); */ static char * regatoi(preg, localbuf) const regex_t *preg; char *localbuf; { register struct rerr *r; register size_t siz; register char *p; for (r = rerrs; r->code != 0; r++) if (strcmp(r->name, preg->re_endp) == 0) break; if (r->code == 0) return("0"); sprintf(localbuf, "%d", r->code); return(localbuf); } ./knews-1.0b.1/regexp/regex.3100644 1244 1244 34570 6455455542 14355 0ustar kallekalle.TH REGEX 3 "17 May 1993" .BY "Henry Spencer" .de ZR .\" one other place knows this name: the SEE ALSO section .IR regex (7) \\$1 .. .SH NAME regcomp, regexec, regerror, regfree \- regular-expression library .SH SYNOPSIS .ft B .\".na #include .br #include .HP 10 int regcomp(regex_t\ *preg, const\ char\ *pattern, int\ cflags); .HP int\ regexec(const\ regex_t\ *preg, const\ char\ *string, size_t\ nmatch, regmatch_t\ pmatch[], int\ eflags); .HP size_t\ regerror(int\ errcode, const\ regex_t\ *preg, char\ *errbuf, size_t\ errbuf_size); .HP void\ regfree(regex_t\ *preg); .\".ad .ft .SH DESCRIPTION These routines implement POSIX 1003.2 regular expressions (``RE''s); see .ZR . .I Regcomp compiles an RE written as a string into an internal form, .I regexec matches that internal form against a string and reports results, .I regerror transforms error codes from either into human-readable messages, and .I regfree frees any dynamically-allocated storage used by the internal form of an RE. .PP The header .I declares two structure types, .I regex_t and .IR regmatch_t , the former for compiled internal forms and the latter for match reporting. It also declares the four functions, a type .IR regoff_t , and a number of constants with names starting with ``REG_''. .PP .I Regcomp compiles the regular expression contained in the .I pattern string, subject to the flags in .IR cflags , and places the results in the .I regex_t structure pointed to by .IR preg . .I Cflags is the bitwise OR of zero or more of the following flags: .IP REG_EXTENDED \w'REG_EXTENDED'u+2n Compile modern (``extended'') REs, rather than the obsolete (``basic'') REs that are the default. .IP REG_BASIC This is a synonym for 0, provided as a counterpart to REG_EXTENDED to improve readability. .IP REG_NOSPEC Compile with recognition of all special characters turned off. All characters are thus considered ordinary, so the ``RE'' is a literal string. This is an extension, compatible with but not specified by POSIX 1003.2, and should be used with caution in software intended to be portable to other systems. REG_EXTENDED and REG_NOSPEC may not be used in the same call to .IR regcomp . .IP REG_ICASE Compile for matching that ignores upper/lower case distinctions. See .ZR . .IP REG_NOSUB Compile for matching that need only report success or failure, not what was matched. .IP REG_NEWLINE Compile for newline-sensitive matching. By default, newline is a completely ordinary character with no special meaning in either REs or strings. With this flag, `[^' bracket expressions and `.' never match newline, a `^' anchor matches the null string after any newline in the string in addition to its normal function, and the `$' anchor matches the null string before any newline in the string in addition to its normal function. .IP REG_PEND The regular expression ends, not at the first NUL, but just before the character pointed to by the .I re_endp member of the structure pointed to by .IR preg . The .I re_endp member is of type .IR const\ char\ * . This flag permits inclusion of NULs in the RE; they are considered ordinary characters. This is an extension, compatible with but not specified by POSIX 1003.2, and should be used with caution in software intended to be portable to other systems. .PP When successful, .I regcomp returns 0 and fills in the structure pointed to by .IR preg . One member of that structure (other than .IR re_endp ) is publicized: .IR re_nsub , of type .IR size_t , contains the number of parenthesized subexpressions within the RE (except that the value of this member is undefined if the REG_NOSUB flag was used). If .I regcomp fails, it returns a non-zero error code; see DIAGNOSTICS. .PP .I Regexec matches the compiled RE pointed to by .I preg against the .IR string , subject to the flags in .IR eflags , and reports results using .IR nmatch , .IR pmatch , and the returned value. The RE must have been compiled by a previous invocation of .IR regcomp . The compiled form is not altered during execution of .IR regexec , so a single compiled RE can be used simultaneously by multiple threads. .PP By default, the NUL-terminated string pointed to by .I string is considered to be the text of an entire line, minus any terminating newline. The .I eflags argument is the bitwise OR of zero or more of the following flags: .IP REG_NOTBOL \w'REG_STARTEND'u+2n The first character of the string is not the beginning of a line, so the `^' anchor should not match before it. This does not affect the behavior of newlines under REG_NEWLINE. .IP REG_NOTEOL The NUL terminating the string does not end a line, so the `$' anchor should not match before it. This does not affect the behavior of newlines under REG_NEWLINE. .IP REG_STARTEND The string is considered to start at \fIstring\fR\ + \fIpmatch\fR[0].\fIrm_so\fR and to have a terminating NUL located at \fIstring\fR\ + \fIpmatch\fR[0].\fIrm_eo\fR (there need not actually be a NUL at that location), regardless of the value of .IR nmatch . See below for the definition of .IR pmatch and .IR nmatch . This is an extension, compatible with but not specified by POSIX 1003.2, and should be used with caution in software intended to be portable to other systems. Note that a non-zero \fIrm_so\fR does not imply REG_NOTBOL; REG_STARTEND affects only the location of the string, not how it is matched. .PP See .ZR for a discussion of what is matched in situations where an RE or a portion thereof could match any of several substrings of .IR string . .PP Normally, .I regexec returns 0 for success and the non-zero code REG_NOMATCH for failure. Other non-zero error codes may be returned in exceptional situations; see DIAGNOSTICS. .PP If REG_NOSUB was specified in the compilation of the RE, or if .I nmatch is 0, .I regexec ignores the .I pmatch argument (but see below for the case where REG_STARTEND is specified). Otherwise, .I pmatch points to an array of .I nmatch structures of type .IR regmatch_t . Such a structure has at least the members .I rm_so and .IR rm_eo , both of type .I regoff_t (a signed arithmetic type at least as large as an .I off_t and a .IR ssize_t ), containing respectively the offset of the first character of a substring and the offset of the first character after the end of the substring. Offsets are measured from the beginning of the .I string argument given to .IR regexec . An empty substring is denoted by equal offsets, both indicating the character following the empty substring. .PP The 0th member of the .I pmatch array is filled in to indicate what substring of .I string was matched by the entire RE. Remaining members report what substring was matched by parenthesized subexpressions within the RE; member .I i reports subexpression .IR i , with subexpressions counted (starting at 1) by the order of their opening parentheses in the RE, left to right. Unused entries in the array\(emcorresponding either to subexpressions that did not participate in the match at all, or to subexpressions that do not exist in the RE (that is, \fIi\fR\ > \fIpreg\fR\->\fIre_nsub\fR)\(emhave both .I rm_so and .I rm_eo set to \-1. If a subexpression participated in the match several times, the reported substring is the last one it matched. (Note, as an example in particular, that when the RE `(b*)+' matches `bbb', the parenthesized subexpression matches each of the three `b's and then an infinite number of empty strings following the last `b', so the reported substring is one of the empties.) .PP If REG_STARTEND is specified, .I pmatch must point to at least one .I regmatch_t (even if .I nmatch is 0 or REG_NOSUB was specified), to hold the input offsets for REG_STARTEND. Use for output is still entirely controlled by .IR nmatch ; if .I nmatch is 0 or REG_NOSUB was specified, the value of .IR pmatch [0] will not be changed by a successful .IR regexec . .PP .I Regerror maps a non-zero .I errcode from either .I regcomp or .I regexec to a human-readable, printable message. If .I preg is non-NULL, the error code should have arisen from use of the .I regex_t pointed to by .IR preg , and if the error code came from .IR regcomp , it should have been the result from the most recent .I regcomp using that .IR regex_t . .RI ( Regerror may be able to supply a more detailed message using information from the .IR regex_t .) .I Regerror places the NUL-terminated message into the buffer pointed to by .IR errbuf , limiting the length (including the NUL) to at most .I errbuf_size bytes. If the whole message won't fit, as much of it as will fit before the terminating NUL is supplied. In any case, the returned value is the size of buffer needed to hold the whole message (including terminating NUL). If .I errbuf_size is 0, .I errbuf is ignored but the return value is still correct. .PP If the .I errcode given to .I regerror is first ORed with REG_ITOA, the ``message'' that results is the printable name of the error code, e.g. ``REG_NOMATCH'', rather than an explanation thereof. If .I errcode is REG_ATOI, then .I preg shall be non-NULL and the .I re_endp member of the structure it points to must point to the printable name of an error code; in this case, the result in .I errbuf is the decimal digits of the numeric value of the error code (0 if the name is not recognized). REG_ITOA and REG_ATOI are intended primarily as debugging facilities; they are extensions, compatible with but not specified by POSIX 1003.2, and should be used with caution in software intended to be portable to other systems. Be warned also that they are considered experimental and changes are possible. .PP .I Regfree frees any dynamically-allocated storage associated with the compiled RE pointed to by .IR preg . The remaining .I regex_t is no longer a valid compiled RE and the effect of supplying it to .I regexec or .I regerror is undefined. .PP None of these functions references global variables except for tables of constants; all are safe for use from multiple threads if the arguments are safe. .SH IMPLEMENTATION CHOICES There are a number of decisions that 1003.2 leaves up to the implementor, either by explicitly saying ``undefined'' or by virtue of them being forbidden by the RE grammar. This implementation treats them as follows. .PP See .ZR for a discussion of the definition of case-independent matching. .PP There is no particular limit on the length of REs, except insofar as memory is limited. Memory usage is approximately linear in RE size, and largely insensitive to RE complexity, except for bounded repetitions. See BUGS for one short RE using them that will run almost any system out of memory. .PP A backslashed character other than one specifically given a magic meaning by 1003.2 (such magic meanings occur only in obsolete [``basic''] REs) is taken as an ordinary character. .PP Any unmatched [ is a REG_EBRACK error. .PP Equivalence classes cannot begin or end bracket-expression ranges. The endpoint of one range cannot begin another. .PP RE_DUP_MAX, the limit on repetition counts in bounded repetitions, is 255. .PP A repetition operator (?, *, +, or bounds) cannot follow another repetition operator. A repetition operator cannot begin an expression or subexpression or follow `^' or `|'. .PP `|' cannot appear first or last in a (sub)expression or after another `|', i.e. an operand of `|' cannot be an empty subexpression. An empty parenthesized subexpression, `()', is legal and matches an empty (sub)string. An empty string is not a legal RE. .PP A `{' followed by a digit is considered the beginning of bounds for a bounded repetition, which must then follow the syntax for bounds. A `{' \fInot\fR followed by a digit is considered an ordinary character. .PP `^' and `$' beginning and ending subexpressions in obsolete (``basic'') REs are anchors, not ordinary characters. .SH SEE ALSO grep(1), regex(7) .PP POSIX 1003.2, sections 2.8 (Regular Expression Notation) and B.5 (C Binding for Regular Expression Matching). .SH DIAGNOSTICS Non-zero error codes from .I regcomp and .I regexec include the following: .PP .nf .ta \w'REG_ECOLLATE'u+3n REG_NOMATCH regexec() failed to match REG_BADPAT invalid regular expression REG_ECOLLATE invalid collating element REG_ECTYPE invalid character class REG_EESCAPE \e applied to unescapable character REG_ESUBREG invalid backreference number REG_EBRACK brackets [ ] not balanced REG_EPAREN parentheses ( ) not balanced REG_EBRACE braces { } not balanced REG_BADBR invalid repetition count(s) in { } REG_ERANGE invalid character range in [ ] REG_ESPACE ran out of memory REG_BADRPT ?, *, or + operand invalid REG_EMPTY empty (sub)expression REG_ASSERT ``can't happen''\(emyou found a bug REG_INVARG invalid argument, e.g. negative-length string .fi .SH HISTORY Written by Henry Spencer at University of Toronto, henry@zoo.toronto.edu. .SH BUGS This is an alpha release with known defects. Please report problems. .PP There is one known functionality bug. The implementation of internationalization is incomplete: the locale is always assumed to be the default one of 1003.2, and only the collating elements etc. of that locale are available. .PP The back-reference code is subtle and doubts linger about its correctness in complex cases. .PP .I Regexec performance is poor. This will improve with later releases. .I Nmatch exceeding 0 is expensive; .I nmatch exceeding 1 is worse. .I Regexec is largely insensitive to RE complexity \fIexcept\fR that back references are massively expensive. RE length does matter; in particular, there is a strong speed bonus for keeping RE length under about 30 characters, with most special characters counting roughly double. .PP .I Regcomp implements bounded repetitions by macro expansion, which is costly in time and space if counts are large or bounded repetitions are nested. An RE like, say, `((((a{1,100}){1,100}){1,100}){1,100}){1,100}' will (eventually) run almost any existing machine out of swap space. .PP There are suspected problems with response to obscure error conditions. Notably, certain kinds of internal overflow, produced only by truly enormous REs or by multiply nested bounded repetitions, are probably not handled well. .PP Due to a mistake in 1003.2, things like `a)b' are legal REs because `)' is a special character only in the presence of a previous unmatched `('. This can't be fixed until the spec is fixed. .PP The standard's definition of back references is vague. For example, does `a\e(\e(b\e)*\e2\e)*d' match `abbbd'? Until the standard is clarified, behavior in such cases should not be relied on. .PP The implementation of word-boundary matching is a bit of a kludge, and bugs may lurk in combinations of word-boundary matching and anchoring. ./knews-1.0b.1/regexp/regex.7100644 1244 1244 22740 6455455542 14355 0ustar kallekalle.TH REGEX 7 "7 Feb 1994" .BY "Henry Spencer" .SH NAME regex \- POSIX 1003.2 regular expressions .SH DESCRIPTION Regular expressions (``RE''s), as defined in POSIX 1003.2, come in two forms: modern REs (roughly those of .IR egrep ; 1003.2 calls these ``extended'' REs) and obsolete REs (roughly those of .IR ed ; 1003.2 ``basic'' REs). Obsolete REs mostly exist for backward compatibility in some old programs; they will be discussed at the end. 1003.2 leaves some aspects of RE syntax and semantics open; `\(dg' marks decisions on these aspects that may not be fully portable to other 1003.2 implementations. .PP A (modern) RE is one\(dg or more non-empty\(dg \fIbranches\fR, separated by `|'. It matches anything that matches one of the branches. .PP A branch is one\(dg or more \fIpieces\fR, concatenated. It matches a match for the first, followed by a match for the second, etc. .PP A piece is an \fIatom\fR possibly followed by a single\(dg `*', `+', `?', or \fIbound\fR. An atom followed by `*' matches a sequence of 0 or more matches of the atom. An atom followed by `+' matches a sequence of 1 or more matches of the atom. An atom followed by `?' matches a sequence of 0 or 1 matches of the atom. .PP A \fIbound\fR is `{' followed by an unsigned decimal integer, possibly followed by `,' possibly followed by another unsigned decimal integer, always followed by `}'. The integers must lie between 0 and RE_DUP_MAX (255\(dg) inclusive, and if there are two of them, the first may not exceed the second. An atom followed by a bound containing one integer \fIi\fR and no comma matches a sequence of exactly \fIi\fR matches of the atom. An atom followed by a bound containing one integer \fIi\fR and a comma matches a sequence of \fIi\fR or more matches of the atom. An atom followed by a bound containing two integers \fIi\fR and \fIj\fR matches a sequence of \fIi\fR through \fIj\fR (inclusive) matches of the atom. .PP An atom is a regular expression enclosed in `()' (matching a match for the regular expression), an empty set of `()' (matching the null string)\(dg, a \fIbracket expression\fR (see below), `.' (matching any single character), `^' (matching the null string at the beginning of a line), `$' (matching the null string at the end of a line), a `\e' followed by one of the characters `^.[$()|*+?{\e' (matching that character taken as an ordinary character), a `\e' followed by any other character\(dg (matching that character taken as an ordinary character, as if the `\e' had not been present\(dg), or a single character with no other significance (matching that character). A `{' followed by a character other than a digit is an ordinary character, not the beginning of a bound\(dg. It is illegal to end an RE with `\e'. .PP A \fIbracket expression\fR is a list of characters enclosed in `[]'. It normally matches any single character from the list (but see below). If the list begins with `^', it matches any single character (but see below) \fInot\fR from the rest of the list. If two characters in the list are separated by `\-', this is shorthand for the full \fIrange\fR of characters between those two (inclusive) in the collating sequence, e.g. `[0-9]' in ASCII matches any decimal digit. It is illegal\(dg for two ranges to share an endpoint, e.g. `a-c-e'. Ranges are very collating-sequence-dependent, and portable programs should avoid relying on them. .PP To include a literal `]' in the list, make it the first character (following a possible `^'). To include a literal `\-', make it the first or last character, or the second endpoint of a range. To use a literal `\-' as the first endpoint of a range, enclose it in `[.' and `.]' to make it a collating element (see below). With the exception of these and some combinations using `[' (see next paragraphs), all other special characters, including `\e', lose their special significance within a bracket expression. .PP Within a bracket expression, a collating element (a character, a multi-character sequence that collates as if it were a single character, or a collating-sequence name for either) enclosed in `[.' and `.]' stands for the sequence of characters of that collating element. The sequence is a single element of the bracket expression's list. A bracket expression containing a multi-character collating element can thus match more than one character, e.g. if the collating sequence includes a `ch' collating element, then the RE `[[.ch.]]*c' matches the first five characters of `chchcc'. .PP Within a bracket expression, a collating element enclosed in `[=' and `=]' is an equivalence class, standing for the sequences of characters of all collating elements equivalent to that one, including itself. (If there are no other equivalent collating elements, the treatment is as if the enclosing delimiters were `[.' and `.]'.) For example, if o and \o'o^' are the members of an equivalence class, then `[[=o=]]', `[[=\o'o^'=]]', and `[o\o'o^']' are all synonymous. An equivalence class may not\(dg be an endpoint of a range. .PP Within a bracket expression, the name of a \fIcharacter class\fR enclosed in `[:' and `:]' stands for the list of all characters belonging to that class. Standard character class names are: .PP .RS .nf .ta 3c 6c 9c alnum digit punct alpha graph space blank lower upper cntrl print xdigit .fi .RE .PP These stand for the character classes defined in .IR ctype (3). A locale may provide others. A character class may not be used as an endpoint of a range. .PP There are two special cases\(dg of bracket expressions: the bracket expressions `[[:<:]]' and `[[:>:]]' match the null string at the beginning and end of a word respectively. A word is defined as a sequence of word characters which is neither preceded nor followed by word characters. A word character is an .I alnum character (as defined by .IR ctype (3)) or an underscore. This is an extension, compatible with but not specified by POSIX 1003.2, and should be used with caution in software intended to be portable to other systems. .PP In the event that an RE could match more than one substring of a given string, the RE matches the one starting earliest in the string. If the RE could match more than one substring starting at that point, it matches the longest. Subexpressions also match the longest possible substrings, subject to the constraint that the whole match be as long as possible, with subexpressions starting earlier in the RE taking priority over ones starting later. Note that higher-level subexpressions thus take priority over their lower-level component subexpressions. .PP Match lengths are measured in characters, not collating elements. A null string is considered longer than no match at all. For example, `bb*' matches the three middle characters of `abbbc', `(wee|week)(knights|nights)' matches all ten characters of `weeknights', when `(.*).*' is matched against `abc' the parenthesized subexpression matches all three characters, and when `(a*)*' is matched against `bc' both the whole RE and the parenthesized subexpression match the null string. .PP If case-independent matching is specified, the effect is much as if all case distinctions had vanished from the alphabet. When an alphabetic that exists in multiple cases appears as an ordinary character outside a bracket expression, it is effectively transformed into a bracket expression containing both cases, e.g. `x' becomes `[xX]'. When it appears inside a bracket expression, all case counterparts of it are added to the bracket expression, so that (e.g.) `[x]' becomes `[xX]' and `[^x]' becomes `[^xX]'. .PP No particular limit is imposed on the length of REs\(dg. Programs intended to be portable should not employ REs longer than 256 bytes, as an implementation can refuse to accept such REs and remain POSIX-compliant. .PP Obsolete (``basic'') regular expressions differ in several respects. `|', `+', and `?' are ordinary characters and there is no equivalent for their functionality. The delimiters for bounds are `\e{' and `\e}', with `{' and `}' by themselves ordinary characters. The parentheses for nested subexpressions are `\e(' and `\e)', with `(' and `)' by themselves ordinary characters. `^' is an ordinary character except at the beginning of the RE or\(dg the beginning of a parenthesized subexpression, `$' is an ordinary character except at the end of the RE or\(dg the end of a parenthesized subexpression, and `*' is an ordinary character if it appears at the beginning of the RE or the beginning of a parenthesized subexpression (after a possible leading `^'). Finally, there is one new type of atom, a \fIback reference\fR: `\e' followed by a non-zero decimal digit \fId\fR matches the same sequence of characters matched by the \fId\fRth parenthesized subexpression (numbering subexpressions by the positions of their opening parentheses, left to right), so that (e.g.) `\e([bc]\e)\e1' matches `bb' or `cc' but not `bc'. .SH SEE ALSO regex(3) .PP POSIX 1003.2, section 2.8 (Regular Expression Notation). .SH BUGS Having two kinds of REs is a botch. .PP The current 1003.2 spec says that `)' is an ordinary character in the absence of an unmatched `('; this was an unintentional result of a wording error, and change is likely. Avoid relying on it. .PP Back references are a dreadful botch, posing major problems for efficient implementations. They are also somewhat vaguely defined (does `a\e(\e(b\e)*\e2\e)*d' match `abbbd'?). Avoid using them. .PP 1003.2's specification of case-independent matching is vague. The ``one case implies all cases'' definition given above is current consensus among implementors as to the right interpretation. .PP The syntax for word boundaries is incredibly ugly. ./knews-1.0b.1/regexp/regex.h100644 1244 1244 3451 6455455542 14414 0ustar kallekalle#ifndef _REGEX_H_ #define _REGEX_H_ /* never again */ /* ========= begin header generated by ./mkh ========= */ #ifdef __cplusplus extern "C" { #endif /* === regex2.h === */ typedef off_t regoff_t; typedef struct { int re_magic; size_t re_nsub; /* number of parenthesized subexpressions */ const char *re_endp; /* end pointer for REG_PEND */ struct re_guts *re_g; /* none of your business :-) */ } regex_t; typedef struct { regoff_t rm_so; /* start of match */ regoff_t rm_eo; /* end of match */ } regmatch_t; /* === regcomp.c === */ extern int regcomp(regex_t *, const char *, int); #define REG_BASIC 0000 #define REG_EXTENDED 0001 #define REG_ICASE 0002 #define REG_NOSUB 0004 #define REG_NEWLINE 0010 #define REG_NOSPEC 0020 #define REG_PEND 0040 #define REG_DUMP 0200 /* === regexec.c === */ extern int regexec(const regex_t *, const char *, size_t, regmatch_t [], int); #define REG_NOTBOL 00001 #define REG_NOTEOL 00002 #define REG_STARTEND 00004 #define REG_TRACE 00400 /* tracing of execution */ #define REG_LARGE 01000 /* force large representation */ #define REG_BACKR 02000 /* force use of backref code */ /* === regerror.c === */ #define REG_NOMATCH 1 #define REG_BADPAT 2 #define REG_ECOLLATE 3 #define REG_ECTYPE 4 #define REG_EESCAPE 5 #define REG_ESUBREG 6 #define REG_EBRACK 7 #define REG_EPAREN 8 #define REG_EBRACE 9 #define REG_BADBR 10 #define REG_ERANGE 11 #define REG_ESPACE 12 #define REG_BADRPT 13 #define REG_EMPTY 14 #define REG_ASSERT 15 #define REG_INVARG 16 #define REG_ATOI 255 /* convert name to number (!) */ #define REG_ITOA 0400 /* convert number to name (!) */ extern size_t regerror(int, const regex_t *, char *, size_t); /* === regfree.c === */ extern void regfree(regex_t *); #ifdef __cplusplus } #endif /* ========= end header generated by ./mkh ========= */ #endif ./knews-1.0b.1/regexp/regex2.h100644 1244 1244 12457 6455455542 14524 0ustar kallekalle/* * First, the stuff that ends up in the outside-world include file = typedef off_t regoff_t; = typedef struct { = int re_magic; = size_t re_nsub; // number of parenthesized subexpressions = const char *re_endp; // end pointer for REG_PEND = struct re_guts *re_g; // none of your business :-) = } regex_t; = typedef struct { = regoff_t rm_so; // start of match = regoff_t rm_eo; // end of match = } regmatch_t; */ /* * internals of regex_t */ #define MAGIC1 ((('r'^0200)<<8) | 'e') /* * The internal representation is a *strip*, a sequence of * operators ending with an endmarker. (Some terminology etc. is a * historical relic of earlier versions which used multiple strips.) * Certain oddities in the representation are there to permit running * the machinery backwards; in particular, any deviation from sequential * flow must be marked at both its source and its destination. Some * fine points: * * - OPLUS_ and O_PLUS are *inside* the loop they create. * - OQUEST_ and O_QUEST are *outside* the bypass they create. * - OCH_ and O_CH are *outside* the multi-way branch they create, while * OOR1 and OOR2 are respectively the end and the beginning of one of * the branches. Note that there is an implicit OOR2 following OCH_ * and an implicit OOR1 preceding O_CH. * * In state representations, an operator's bit is on to signify a state * immediately *preceding* "execution" of that operator. */ typedef unsigned long sop; /* strip operator */ typedef long sopno; #define OPRMASK 0xf8000000 #define OPDMASK 0x07ffffff #define OPSHIFT ((unsigned)27) #define OP(n) ((n)&OPRMASK) #define OPND(n) ((n)&OPDMASK) #define SOP(op, opnd) ((op)|(opnd)) /* operators meaning operand */ /* (back, fwd are offsets) */ #define OEND (1ul< uch [csetsize] */ uch mask; /* bit within array */ uch hash; /* hash code */ size_t smultis; char *multis; /* -> char[smulti] ab\0cd\0ef\0\0 */ } cset; /* note that CHadd and CHsub are unsafe, and CHIN doesn't yield 0/1 */ #define CHadd(cs, c) ((cs)->ptr[(uch)(c)] |= (cs)->mask, (cs)->hash += (c)) #define CHsub(cs, c) ((cs)->ptr[(uch)(c)] &= ~(cs)->mask, (cs)->hash -= (c)) #define CHIN(cs, c) ((cs)->ptr[(uch)(c)] & (cs)->mask) #define MCadd(p, cs, cp) mcadd(p, cs, cp) /* regcomp() internal fns */ #define MCsub(p, cs, cp) mcsub(p, cs, cp) #define MCin(p, cs, cp) mcin(p, cs, cp) /* stuff for character categories */ typedef unsigned char cat_t; /* * main compiled-expression structure */ struct re_guts { int magic; # define MAGIC2 ((('R'^0200)<<8)|'E') sop *strip; /* malloced area for strip */ int csetsize; /* number of bits in a cset vector */ int ncsets; /* number of csets in use */ cset *sets; /* -> cset [ncsets] */ uch *setbits; /* -> uch[csetsize][ncsets/CHAR_BIT] */ int cflags; /* copy of regcomp() cflags argument */ sopno nstates; /* = number of sops */ sopno firststate; /* the initial OEND (normally 0) */ sopno laststate; /* the final OEND */ int iflags; /* internal flags */ # define USEBOL 01 /* used ^ */ # define USEEOL 02 /* used $ */ # define BAD 04 /* something wrong */ int nbol; /* number of ^ used */ int neol; /* number of $ used */ int ncategories; /* how many character categories */ cat_t *categories; /* ->catspace[-CHAR_MIN] */ char *must; /* match must contain this string */ int mlen; /* length of must */ size_t nsub; /* copy of re_nsub */ int backrefs; /* does it use back references? */ sopno nplus; /* how deep does it nest +s? */ /* catspace must be last */ cat_t catspace[1]; /* actually [NC] */ }; /* misc utilities */ #define OUT (CHAR_MAX+1) /* a non-character value */ #define ISWORD(c) (isalnum(c) || (c) == '_') ./knews-1.0b.1/regexp/regexec.c100644 1244 1244 10146 6455455542 14736 0ustar kallekalle/* * the outer shell of regexec() * * This file includes engine.c *twice*, after muchos fiddling with the * macros that code uses. This lets the same code operate on two different * representations for state sets. */ #include #include #include #include #include #include #include #include "utils.h" #include "regex2.h" static int nope = 0; /* for use in asserts; shuts lint up */ /* macros for manipulating states, small version */ #define states long #define states1 states /* for later use in regexec() decision */ #define CLEAR(v) ((v) = 0) #define SET0(v, n) ((v) &= ~(1 << (n))) #define SET1(v, n) ((v) |= 1 << (n)) #define ISSET(v, n) ((v) & (1 << (n))) #define ASSIGN(d, s) ((d) = (s)) #define EQ(a, b) ((a) == (b)) #define STATEVARS int dummy /* dummy version */ #define STATESETUP(m, n) /* nothing */ #define STATETEARDOWN(m) /* nothing */ #define SETUP(v) ((v) = 0) #define onestate int #define INIT(o, n) ((o) = (unsigned)1 << (n)) #define INC(o) ((o) <<= 1) #define ISSTATEIN(v, o) ((v) & (o)) /* some abbreviations; note that some of these know variable names! */ /* do "if I'm here, I can also be there" etc without branches */ #define FWD(dst, src, n) ((dst) |= ((unsigned)(src)&(here)) << (n)) #define BACK(dst, src, n) ((dst) |= ((unsigned)(src)&(here)) >> (n)) #define ISSETBACK(v, n) ((v) & ((unsigned)here >> (n))) /* function names */ #define SNAMES /* engine.c looks after details */ #include "engine.c" /* now undo things */ #undef states #undef CLEAR #undef SET0 #undef SET1 #undef ISSET #undef ASSIGN #undef EQ #undef STATEVARS #undef STATESETUP #undef STATETEARDOWN #undef SETUP #undef onestate #undef INIT #undef INC #undef ISSTATEIN #undef FWD #undef BACK #undef ISSETBACK #undef SNAMES /* macros for manipulating states, large version */ #define states char * #define CLEAR(v) memset(v, 0, m->g->nstates) #define SET0(v, n) ((v)[n] = 0) #define SET1(v, n) ((v)[n] = 1) #define ISSET(v, n) ((v)[n]) #define ASSIGN(d, s) memcpy(d, s, m->g->nstates) #define EQ(a, b) (memcmp(a, b, m->g->nstates) == 0) #define STATEVARS int vn; char *space #define STATESETUP(m, nv) { (m)->space = malloc((nv)*(m)->g->nstates); \ if ((m)->space == NULL) return(REG_ESPACE); \ (m)->vn = 0; } #define STATETEARDOWN(m) { free((m)->space); } #define SETUP(v) ((v) = &m->space[m->vn++ * m->g->nstates]) #define onestate int #define INIT(o, n) ((o) = (n)) #define INC(o) ((o)++) #define ISSTATEIN(v, o) ((v)[o]) /* some abbreviations; note that some of these know variable names! */ /* do "if I'm here, I can also be there" etc without branches */ #define FWD(dst, src, n) ((dst)[here+(n)] |= (src)[here]) #define BACK(dst, src, n) ((dst)[here-(n)] |= (src)[here]) #define ISSETBACK(v, n) ((v)[here - (n)]) /* function names */ #define LNAMES /* flag */ #include "engine.c" /* - regexec - interface for matching = extern int regexec(const regex_t *, const char *, size_t, \ = regmatch_t [], int); = #define REG_NOTBOL 00001 = #define REG_NOTEOL 00002 = #define REG_STARTEND 00004 = #define REG_TRACE 00400 // tracing of execution = #define REG_LARGE 01000 // force large representation = #define REG_BACKR 02000 // force use of backref code * * We put this here so we can exploit knowledge of the state representation * when choosing which matcher to call. Also, by this point the matchers * have been prototyped. */ int /* 0 success, REG_NOMATCH failure */ regexec(preg, string, nmatch, pmatch, eflags) const regex_t *preg; const char *string; size_t nmatch; regmatch_t pmatch[]; int eflags; { register struct re_guts *g = preg->re_g; #ifdef REDEBUG # define GOODFLAGS(f) (f) #else # define GOODFLAGS(f) ((f)&(REG_NOTBOL|REG_NOTEOL|REG_STARTEND)) #endif if (preg->re_magic != MAGIC1 || g->magic != MAGIC2) return(REG_BADPAT); assert(!(g->iflags&BAD)); if (g->iflags&BAD) /* backstop for no-debug case */ return(REG_BADPAT); eflags = GOODFLAGS(eflags); if (g->nstates <= CHAR_BIT*sizeof(states1) && !(eflags®_LARGE)) return(smatcher(g, (char *)string, nmatch, pmatch, eflags)); else return(lmatcher(g, (char *)string, nmatch, pmatch, eflags)); } ./knews-1.0b.1/regexp/regfree.c100644 1244 1244 1321 6455455542 14706 0ustar kallekalle#include #include #include #include #include "utils.h" #include "regex2.h" /* - regfree - free everything = extern void regfree(regex_t *); */ void regfree(preg) regex_t *preg; { register struct re_guts *g; if (preg->re_magic != MAGIC1) /* oops */ return; /* nice to complain, but hard */ g = preg->re_g; if (g == NULL || g->magic != MAGIC2) /* oops again */ return; preg->re_magic = 0; /* mark it invalid */ g->magic = 0; /* mark it invalid */ if (g->strip != NULL) free((char *)g->strip); if (g->sets != NULL) free((char *)g->sets); if (g->setbits != NULL) free((char *)g->setbits); if (g->must != NULL) free(g->must); free((char *)g); } ./knews-1.0b.1/regexp/split.c100644 1244 1244 15625 6455455542 14456 0ustar kallekalle#include #include /* - split - divide a string into fields, like awk split() = int split(char *string, char *fields[], int nfields, char *sep); */ int /* number of fields, including overflow */ split(string, fields, nfields, sep) char *string; char *fields[]; /* list is not NULL-terminated */ int nfields; /* number of entries available in fields[] */ char *sep; /* "" white, "c" single char, "ab" [ab]+ */ { register char *p = string; register char c; /* latest character */ register char sepc = sep[0]; register char sepc2; register int fn; register char **fp = fields; register char *sepp; register int trimtrail; /* white space */ if (sepc == '\0') { while ((c = *p++) == ' ' || c == '\t') continue; p--; trimtrail = 1; sep = " \t"; /* note, code below knows this is 2 long */ sepc = ' '; } else trimtrail = 0; sepc2 = sep[1]; /* now we can safely pick this up */ /* catch empties */ if (*p == '\0') return(0); /* single separator */ if (sepc2 == '\0') { fn = nfields; for (;;) { *fp++ = p; fn--; if (fn == 0) break; while ((c = *p++) != sepc) if (c == '\0') return(nfields - fn); *(p-1) = '\0'; } /* we have overflowed the fields vector -- just count them */ fn = nfields; for (;;) { while ((c = *p++) != sepc) if (c == '\0') return(fn); fn++; } /* not reached */ } /* two separators */ if (sep[2] == '\0') { fn = nfields; for (;;) { *fp++ = p; fn--; while ((c = *p++) != sepc && c != sepc2) if (c == '\0') { if (trimtrail && **(fp-1) == '\0') fn++; return(nfields - fn); } if (fn == 0) break; *(p-1) = '\0'; while ((c = *p++) == sepc || c == sepc2) continue; p--; } /* we have overflowed the fields vector -- just count them */ fn = nfields; while (c != '\0') { while ((c = *p++) == sepc || c == sepc2) continue; p--; fn++; while ((c = *p++) != '\0' && c != sepc && c != sepc2) continue; } /* might have to trim trailing white space */ if (trimtrail) { p--; while ((c = *--p) == sepc || c == sepc2) continue; p++; if (*p != '\0') { if (fn == nfields+1) *p = '\0'; fn--; } } return(fn); } /* n separators */ fn = 0; for (;;) { if (fn < nfields) *fp++ = p; fn++; for (;;) { c = *p++; if (c == '\0') return(fn); sepp = sep; while ((sepc = *sepp++) != '\0' && sepc != c) continue; if (sepc != '\0') /* it was a separator */ break; } if (fn < nfields) *(p-1) = '\0'; for (;;) { c = *p++; sepp = sep; while ((sepc = *sepp++) != '\0' && sepc != c) continue; if (sepc == '\0') /* it wasn't a separator */ break; } p--; } /* not reached */ } #ifdef TEST_SPLIT /* * test program * pgm runs regression * pgm sep splits stdin lines by sep * pgm str sep splits str by sep * pgm str sep n splits str by sep n times */ int main(argc, argv) int argc; char *argv[]; { char buf[512]; register int n; # define MNF 10 char *fields[MNF]; if (argc > 4) for (n = atoi(argv[3]); n > 0; n--) { (void) strcpy(buf, argv[1]); } else if (argc > 3) for (n = atoi(argv[3]); n > 0; n--) { (void) strcpy(buf, argv[1]); (void) split(buf, fields, MNF, argv[2]); } else if (argc > 2) dosplit(argv[1], argv[2]); else if (argc > 1) while (fgets(buf, sizeof(buf), stdin) != NULL) { buf[strlen(buf)-1] = '\0'; /* stomp newline */ dosplit(buf, argv[1]); } else regress(); exit(0); } dosplit(string, seps) char *string; char *seps; { # define NF 5 char *fields[NF]; register int nf; nf = split(string, fields, NF, seps); print(nf, NF, fields); } print(nf, nfp, fields) int nf; int nfp; char *fields[]; { register int fn; register int bound; bound = (nf > nfp) ? nfp : nf; printf("%d:\t", nf); for (fn = 0; fn < bound; fn++) printf("\"%s\"%s", fields[fn], (fn+1 < nf) ? ", " : "\n"); } #define RNF 5 /* some table entries know this */ struct { char *str; char *seps; int nf; char *fi[RNF]; } tests[] = { "", " ", 0, { "" }, " ", " ", 2, { "", "" }, "x", " ", 1, { "x" }, "xy", " ", 1, { "xy" }, "x y", " ", 2, { "x", "y" }, "abc def g ", " ", 5, { "abc", "def", "", "g", "" }, " a bcd", " ", 4, { "", "", "a", "bcd" }, "a b c d e f", " ", 6, { "a", "b", "c", "d", "e f" }, " a b c d ", " ", 6, { "", "a", "b", "c", "d " }, "", " _", 0, { "" }, " ", " _", 2, { "", "" }, "x", " _", 1, { "x" }, "x y", " _", 2, { "x", "y" }, "ab _ cd", " _", 2, { "ab", "cd" }, " a_b c ", " _", 5, { "", "a", "b", "c", "" }, "a b c_d e f", " _", 6, { "a", "b", "c", "d", "e f" }, " a b c d ", " _", 6, { "", "a", "b", "c", "d " }, "", " _~", 0, { "" }, " ", " _~", 2, { "", "" }, "x", " _~", 1, { "x" }, "x y", " _~", 2, { "x", "y" }, "ab _~ cd", " _~", 2, { "ab", "cd" }, " a_b c~", " _~", 5, { "", "a", "b", "c", "" }, "a b_c d~e f", " _~", 6, { "a", "b", "c", "d", "e f" }, "~a b c d ", " _~", 6, { "", "a", "b", "c", "d " }, "", " _~-", 0, { "" }, " ", " _~-", 2, { "", "" }, "x", " _~-", 1, { "x" }, "x y", " _~-", 2, { "x", "y" }, "ab _~- cd", " _~-", 2, { "ab", "cd" }, " a_b c~", " _~-", 5, { "", "a", "b", "c", "" }, "a b_c-d~e f", " _~-", 6, { "a", "b", "c", "d", "e f" }, "~a-b c d ", " _~-", 6, { "", "a", "b", "c", "d " }, "", " ", 0, { "" }, " ", " ", 2, { "", "" }, "x", " ", 1, { "x" }, "xy", " ", 1, { "xy" }, "x y", " ", 2, { "x", "y" }, "abc def g ", " ", 4, { "abc", "def", "g", "" }, " a bcd", " ", 3, { "", "a", "bcd" }, "a b c d e f", " ", 6, { "a", "b", "c", "d", "e f" }, " a b c d ", " ", 6, { "", "a", "b", "c", "d " }, "", "", 0, { "" }, " ", "", 0, { "" }, "x", "", 1, { "x" }, "xy", "", 1, { "xy" }, "x y", "", 2, { "x", "y" }, "abc def g ", "", 3, { "abc", "def", "g" }, "\t a bcd", "", 2, { "a", "bcd" }, " a \tb\t c ", "", 3, { "a", "b", "c" }, "a b c d e ", "", 5, { "a", "b", "c", "d", "e" }, "a b\tc d e f", "", 6, { "a", "b", "c", "d", "e f" }, " a b c d e f ", "", 6, { "a", "b", "c", "d", "e f " }, NULL, NULL, 0, { NULL }, }; regress() { char buf[512]; register int n; char *fields[RNF+1]; register int nf; register int i; register int printit; register char *f; for (n = 0; tests[n].str != NULL; n++) { (void) strcpy(buf, tests[n].str); fields[RNF] = NULL; nf = split(buf, fields, RNF, tests[n].seps); printit = 0; if (nf != tests[n].nf) { printf("split `%s' by `%s' gave %d fields, not %d\n", tests[n].str, tests[n].seps, nf, tests[n].nf); printit = 1; } else if (fields[RNF] != NULL) { printf("split() went beyond array end\n"); printit = 1; } else { for (i = 0; i < nf && i < RNF; i++) { f = fields[i]; if (f == NULL) f = "(NULL)"; if (strcmp(f, tests[n].fi[i]) != 0) { printf("split `%s' by `%s', field %d is `%s', not `%s'\n", tests[n].str, tests[n].seps, i, fields[i], tests[n].fi[i]); printit = 1; } } } if (printit) print(nf, RNF, fields); } } #endif ./knews-1.0b.1/regexp/tests100644 1244 1244 27737 6455455542 14253 0ustar kallekalle# regular expression test set # Lines are at least three fields, separated by one or more tabs. "" stands # for an empty field. First field is an RE. Second field is flags. If # C flag given, regcomp() is expected to fail, and the third field is the # error name (minus the leading REG_). # # Otherwise it is expected to succeed, and the third field is the string to # try matching it against. If there is no fourth field, the match is # expected to fail. If there is a fourth field, it is the substring that # the RE is expected to match. If there is a fifth field, it is a comma- # separated list of what the subexpressions should match, with - indicating # no match for that one. In both the fourth and fifth fields, a (sub)field # starting with @ indicates that the (sub)expression is expected to match # a null string followed by the stuff after the @; this provides a way to # test where null strings match. The character `N' in REs and strings # is newline, `S' is space, `T' is tab, `Z' is NUL. # # The full list of flags: # - placeholder, does nothing # b RE is a BRE, not an ERE # & try it as both an ERE and a BRE # C regcomp() error expected, third field is error name # i REG_ICASE # m ("mundane") REG_NOSPEC # s REG_NOSUB (not really testable) # n REG_NEWLINE # ^ REG_NOTBOL # $ REG_NOTEOL # # REG_STARTEND (see below) # p REG_PEND # # For REG_STARTEND, the start/end offsets are those of the substring # enclosed in (). # basics a & a a abc & abc abc abc|de - abc abc a|b|c - abc a # parentheses and perversions thereof a(b)c - abc abc a\(b\)c b abc abc a( C EPAREN a( b a( a( a\( - a( a( a\( bC EPAREN a\(b bC EPAREN a(b C EPAREN a(b b a(b a(b # gag me with a right parenthesis -- 1003.2 goofed here (my fault, partly) a) - a) a) ) - ) ) # end gagging (in a just world, those *should* give EPAREN) a) b a) a) a\) bC EPAREN \) bC EPAREN a()b - ab ab a\(\)b b ab ab # anchoring and REG_NEWLINE ^abc$ & abc abc a^b - a^b a^b b a^b a^b a$b - a$b a$b b a$b a$b ^ & abc @abc $ & abc @ ^$ & "" @ $^ - "" @ \($\)\(^\) b "" @ # stop retching, those are legitimate (although disgusting) ^^ - "" @ $$ - "" @ b$ & abNc b$ &n abNc b ^b$ & aNbNc ^b$ &n aNbNc b ^$ &n aNNb @Nb ^$ n abc ^$ n abcN @ $^ n aNNb @Nb \($\)\(^\) bn aNNb @Nb ^^ n^ aNNb @Nb $$ n aNNb @NN ^a ^ a a$ $ a ^a ^n aNb ^b ^n aNb b a$ $n bNa b$ $n bNa b a*(^b$)c* - b b a*\(^b$\)c* b b b # certain syntax errors and non-errors | C EMPTY | b | | * C BADRPT * b * * + C BADRPT ? C BADRPT "" &C EMPTY () - abc @abc \(\) b abc @abc a||b C EMPTY |ab C EMPTY ab| C EMPTY (|a)b C EMPTY (a|)b C EMPTY (*a) C BADRPT (+a) C BADRPT (?a) C BADRPT ({1}a) C BADRPT \(\{1\}a\) bC BADRPT (a|*b) C BADRPT (a|+b) C BADRPT (a|?b) C BADRPT (a|{1}b) C BADRPT ^* C BADRPT ^* b * * ^+ C BADRPT ^? C BADRPT ^{1} C BADRPT ^\{1\} bC BADRPT # metacharacters, backslashes a.c & abc abc a[bc]d & abd abd a\*c & a*c a*c a\\b & a\b a\b a\\\*b & a\*b a\*b a\bc & abc abc a\ &C EESCAPE a\\bc & a\bc a\bc \{ bC BADRPT a\[b & a[b a[b a[b &C EBRACK # trailing $ is a peculiar special case for the BRE code a$ & a a a$ & a$ a\$ & a a\$ & a$ a$ a\\$ & a a\\$ & a$ a\\$ & a\$ a\\$ & a\ a\ # back references, ugh a\(b\)\2c bC ESUBREG a\(b\1\)c bC ESUBREG a\(b*\)c\1d b abbcbbd abbcbbd bb a\(b*\)c\1d b abbcbd a\(b*\)c\1d b abbcbbbd ^\(.\)\1 b abc a\([bc]\)\1d b abcdabbd abbd b a\(\([bc]\)\2\)*d b abbccd abbccd a\(\([bc]\)\2\)*d b abbcbd # actually, this next one probably ought to fail, but the spec is unclear a\(\(b\)*\2\)*d b abbbd abbbd # here is a case that no NFA implementation does right \(ab*\)[ab]*\1 b ababaaa ababaaa a # check out normal matching in the presence of back refs \(a\)\1bcd b aabcd aabcd \(a\)\1bc*d b aabcd aabcd \(a\)\1bc*d b aabd aabd \(a\)\1bc*d b aabcccd aabcccd \(a\)\1bc*[ce]d b aabcccd aabcccd ^\(a\)\1b\(c\)*cd$ b aabcccd aabcccd # ordinary repetitions ab*c & abc abc ab+c - abc abc ab?c - abc abc a\(*\)b b a*b a*b a\(**\)b b ab ab a\(***\)b bC BADRPT *a b *a *a **a b a a ***a bC BADRPT # the dreaded bounded repetitions { & { { {abc & {abc {abc {1 C BADRPT {1} C BADRPT a{b & a{b a{b a{1}b - ab ab a\{1\}b b ab ab a{1,}b - ab ab a\{1,\}b b ab ab a{1,2}b - aab aab a\{1,2\}b b aab aab a{1 C EBRACE a\{1 bC EBRACE a{1a C EBRACE a\{1a bC EBRACE a{1a} C BADBR a\{1a\} bC BADBR a{,2} - a{,2} a{,2} a\{,2\} bC BADBR a{,} - a{,} a{,} a\{,\} bC BADBR a{1,x} C BADBR a\{1,x\} bC BADBR a{1,x C EBRACE a\{1,x bC EBRACE a{300} C BADBR a\{300\} bC BADBR a{1,0} C BADBR a\{1,0\} bC BADBR ab{0,0}c - abcac ac ab\{0,0\}c b abcac ac ab{0,1}c - abcac abc ab\{0,1\}c b abcac abc ab{0,3}c - abbcac abbc ab\{0,3\}c b abbcac abbc ab{1,1}c - acabc abc ab\{1,1\}c b acabc abc ab{1,3}c - acabc abc ab\{1,3\}c b acabc abc ab{2,2}c - abcabbc abbc ab\{2,2\}c b abcabbc abbc ab{2,4}c - abcabbc abbc ab\{2,4\}c b abcabbc abbc ((a{1,10}){1,10}){1,10} - a a a,a # multiple repetitions a** &C BADRPT a++ C BADRPT a?? C BADRPT a*+ C BADRPT a*? C BADRPT a+* C BADRPT a+? C BADRPT a?* C BADRPT a?+ C BADRPT a{1}{1} C BADRPT a*{1} C BADRPT a+{1} C BADRPT a?{1} C BADRPT a{1}* C BADRPT a{1}+ C BADRPT a{1}? C BADRPT a*{b} - a{b} a{b} a\{1\}\{1\} bC BADRPT a*\{1\} bC BADRPT a\{1\}* bC BADRPT # brackets, and numerous perversions thereof a[b]c & abc abc a[ab]c & abc abc a[^ab]c & adc adc a[]b]c & a]c a]c a[[b]c & a[c a[c a[-b]c & a-c a-c a[^]b]c & adc adc a[^-b]c & adc adc a[b-]c & a-c a-c a[b &C EBRACK a[] &C EBRACK a[1-3]c & a2c a2c a[3-1]c &C ERANGE a[1-3-5]c &C ERANGE a[[.-.]--]c & a-c a-c a[1- &C ERANGE a[[. &C EBRACK a[[.x &C EBRACK a[[.x. &C EBRACK a[[.x.] &C EBRACK a[[.x.]] & ax ax a[[.x,.]] &C ECOLLATE a[[.one.]]b & a1b a1b a[[.notdef.]]b &C ECOLLATE a[[.].]]b & a]b a]b a[[:alpha:]]c & abc abc a[[:notdef:]]c &C ECTYPE a[[: &C EBRACK a[[:alpha &C EBRACK a[[:alpha:] &C EBRACK a[[:alpha,:] &C ECTYPE a[[:]:]]b &C ECTYPE a[[:-:]]b &C ECTYPE a[[:alph:]] &C ECTYPE a[[:alphabet:]] &C ECTYPE [[:alnum:]]+ - -%@a0X- a0X [[:alpha:]]+ - -%@aX0- aX [[:blank:]]+ - aSSTb SST [[:cntrl:]]+ - aNTb NT [[:digit:]]+ - a019b 019 [[:graph:]]+ - Sa%bS a%b [[:lower:]]+ - AabC ab [[:print:]]+ - NaSbN aSb [[:punct:]]+ - S%-&T %-& [[:space:]]+ - aSNTb SNT [[:upper:]]+ - aBCd BC [[:xdigit:]]+ - p0f3Cq 0f3C a[[=b=]]c & abc abc a[[= &C EBRACK a[[=b &C EBRACK a[[=b= &C EBRACK a[[=b=] &C EBRACK a[[=b,=]] &C ECOLLATE a[[=one=]]b & a1b a1b # complexities a(((b)))c - abc abc a(b|(c))d - abd abd a(b*|c)d - abbd abbd # just gotta have one DFA-buster, of course a[ab]{20} - aaaaabaaaabaaaabaaaab aaaaabaaaabaaaabaaaab # and an inline expansion in case somebody gets tricky a[ab][ab][ab][ab][ab][ab][ab][ab][ab][ab][ab][ab][ab][ab][ab][ab][ab][ab][ab][ab] - aaaaabaaaabaaaabaaaab aaaaabaaaabaaaabaaaab # and in case somebody just slips in an NFA... a[ab][ab][ab][ab][ab][ab][ab][ab][ab][ab][ab][ab][ab][ab][ab][ab][ab][ab][ab][ab](wee|week)(knights|night) - aaaaabaaaabaaaabaaaabweeknights aaaaabaaaabaaaabaaaabweeknights # fish for anomalies as the number of states passes 32 12345678901234567890123456789 - a12345678901234567890123456789b 12345678901234567890123456789 123456789012345678901234567890 - a123456789012345678901234567890b 123456789012345678901234567890 1234567890123456789012345678901 - a1234567890123456789012345678901b 1234567890123456789012345678901 12345678901234567890123456789012 - a12345678901234567890123456789012b 12345678901234567890123456789012 123456789012345678901234567890123 - a123456789012345678901234567890123b 123456789012345678901234567890123 # and one really big one, beyond any plausible word width 1234567890123456789012345678901234567890123456789012345678901234567890 - a1234567890123456789012345678901234567890123456789012345678901234567890b 1234567890123456789012345678901234567890123456789012345678901234567890 # fish for problems as brackets go past 8 [ab][cd][ef][gh][ij][kl][mn] - xacegikmoq acegikm [ab][cd][ef][gh][ij][kl][mn][op] - xacegikmoq acegikmo [ab][cd][ef][gh][ij][kl][mn][op][qr] - xacegikmoqy acegikmoq [ab][cd][ef][gh][ij][kl][mn][op][q] - xacegikmoqy acegikmoq # subtleties of matching abc & xabcy abc a\(b\)?c\1d b acd aBc i Abc Abc a[Bc]*d i abBCcd abBCcd 0[[:upper:]]1 &i 0a1 0a1 0[[:lower:]]1 &i 0A1 0A1 a[^b]c &i abc a[^b]c &i aBc a[^b]c &i adc adc [a]b[c] - abc abc [a]b[a] - aba aba [abc]b[abc] - abc abc [abc]b[abd] - abd abd a(b?c)+d - accd accd (wee|week)(knights|night) - weeknights weeknights (we|wee|week|frob)(knights|night|day) - weeknights weeknights a[bc]d - xyzaaabcaababdacd abd a[ab]c - aaabc abc abc s abc abc a* & b @b # Let's have some fun -- try to match a C comment. # first the obvious, which looks okay at first glance... /\*.*\*/ - /*x*/ /*x*/ # but... /\*.*\*/ - /*x*/y/*z*/ /*x*/y/*z*/ # okay, we must not match */ inside; try to do that... /\*([^*]|\*[^/])*\*/ - /*x*/ /*x*/ /\*([^*]|\*[^/])*\*/ - /*x*/y/*z*/ /*x*/ # but... /\*([^*]|\*[^/])*\*/ - /*x**/y/*z*/ /*x**/y/*z*/ # and a still fancier version, which does it right (I think)... /\*([^*]|\*+[^*/])*\*+/ - /*x*/ /*x*/ /\*([^*]|\*+[^*/])*\*+/ - /*x*/y/*z*/ /*x*/ /\*([^*]|\*+[^*/])*\*+/ - /*x**/y/*z*/ /*x**/ /\*([^*]|\*+[^*/])*\*+/ - /*x****/y/*z*/ /*x****/ /\*([^*]|\*+[^*/])*\*+/ - /*x**x*/y/*z*/ /*x**x*/ /\*([^*]|\*+[^*/])*\*+/ - /*x***x/y/*z*/ /*x***x/y/*z*/ # subexpressions a(b)(c)d - abcd abcd b,c a(((b)))c - abc abc b,b,b a(b|(c))d - abd abd b,- a(b*|c|e)d - abbd abbd bb a(b*|c|e)d - acd acd c a(b*|c|e)d - ad ad @d a(b?)c - abc abc b a(b?)c - ac ac @c a(b+)c - abc abc b a(b+)c - abbbc abbbc bbb a(b*)c - ac ac @c (a|ab)(bc([de]+)f|cde) - abcdef abcdef a,bcdef,de # the regression tester only asks for 9 subexpressions a(b)(c)(d)(e)(f)(g)(h)(i)(j)k - abcdefghijk abcdefghijk b,c,d,e,f,g,h,i,j a(b)(c)(d)(e)(f)(g)(h)(i)(j)(k)l - abcdefghijkl abcdefghijkl b,c,d,e,f,g,h,i,j,k a([bc]?)c - abc abc b a([bc]?)c - ac ac @c a([bc]+)c - abc abc b a([bc]+)c - abcc abcc bc a([bc]+)bc - abcbc abcbc bc a(bb+|b)b - abb abb b a(bbb+|bb+|b)b - abb abb b a(bbb+|bb+|b)b - abbb abbb bb a(bbb+|bb+|b)bb - abbb abbb b (.*).* - abcdef abcdef abcdef (a*)* - bc @b @b # do we get the right subexpression when it is used more than once? a(b|c)*d - ad ad - a(b|c)*d - abcd abcd c a(b|c)+d - abd abd b a(b|c)+d - abcd abcd c a(b|c?)+d - ad ad @d a(b|c?)+d - abcd abcd @d a(b|c){0,0}d - ad ad - a(b|c){0,1}d - ad ad - a(b|c){0,1}d - abd abd b a(b|c){0,2}d - ad ad - a(b|c){0,2}d - abcd abcd c a(b|c){0,}d - ad ad - a(b|c){0,}d - abcd abcd c a(b|c){1,1}d - abd abd b a(b|c){1,1}d - acd acd c a(b|c){1,2}d - abd abd b a(b|c){1,2}d - abcd abcd c a(b|c){1,}d - abd abd b a(b|c){1,}d - abcd abcd c a(b|c){2,2}d - acbd acbd b a(b|c){2,2}d - abcd abcd c a(b|c){2,4}d - abcd abcd c a(b|c){2,4}d - abcbd abcbd b a(b|c){2,4}d - abcbcd abcbcd c a(b|c){2,}d - abcd abcd c a(b|c){2,}d - abcbd abcbd b a(b+|((c)*))+d - abd abd @d,@d,- a(b+|((c)*))+d - abcd abcd @d,@d,- # check out the STARTEND option [abc] &# a(b)c b [abc] &# a(d)c [abc] &# a(bc)d b [abc] &# a(dc)d c . &# a()c b.*c &# b(bc)c bc b.* &# b(bc)c bc .*c &# b(bc)c bc # plain strings, with the NOSPEC flag abc m abc abc abc m xabcy abc abc m xyz a*b m aba*b a*b a*b m ab "" mC EMPTY # cases involving NULs aZb & a a aZb &p a aZb &p# (aZb) aZb aZ*b &p# (ab) ab a.b &# (aZb) aZb a.* &# (aZb)c aZb # word boundaries (ick) [[:<:]]a & a a [[:<:]]a & ba [[:<:]]a & -a a a[[:>:]] & a a a[[:>:]] & ab a[[:>:]] & a- a [[:<:]]a.c[[:>:]] & axcd-dayc-dazce-abc abc [[:<:]]a.c[[:>:]] & axcd-dayc-dazce-abc-q abc [[:<:]]a.c[[:>:]] & axc-dayc-dazce-abc axc [[:<:]]b.c[[:>:]] & a_bxc-byc_d-bzc-q bzc [[:<:]].x..[[:>:]] & y_xa_-_xb_y-_xc_-axdc _xc_ [[:<:]]a_b[[:>:]] & x_a_b # past problems, and suspected problems (A[1])|(A[2])|(A[3])|(A[4])|(A[5])|(A[6])|(A[7])|(A[8])|(A[9])|(A[A]) - A1 A1 abcdefghijklmnop i abcdefghijklmnop abcdefghijklmnop abcdefghijklmnopqrstuv i abcdefghijklmnopqrstuv abcdefghijklmnopqrstuv (ALAK)|(ALT[AB])|(CC[123]1)|(CM[123]1)|(GAMC)|(LC[23][EO ])|(SEM[1234])|(SL[ES][12])|(SLWW)|(SLF )|(SLDT)|(VWH[12])|(WH[34][EW])|(WP1[ESN]) - CC11 CC11 CC[13]1|a{21}[23][EO][123][Es][12]a{15}aa[34][EW]aaaaaaa[X]a - CC11 CC11 Char \([a-z0-9_]*\)\[.* b Char xyz[k Char xyz[k xyz a?b - ab ab -\{0,1\}[0-9]*$ b -5 -5 ./knews-1.0b.1/regexp/utils.h100644 1244 1244 1027 6455455542 14437 0ustar kallekalle/* utility definitions */ #ifdef _POSIX2_RE_DUP_MAX #define DUPMAX _POSIX2_RE_DUP_MAX /* xxx is this right? */ #else #define DUPMAX 255 #endif #define INFINITY (DUPMAX + 1) #define NC (CHAR_MAX - CHAR_MIN + 1) typedef unsigned char uch; /* switch off assertions (if not already off) if no REDEBUG */ #ifndef REDEBUG #ifndef NDEBUG #define NDEBUG /* no assertions please */ #endif #endif #include /* for old systems with bcopy() but no memmove() */ #ifdef USEBCOPY #define memmove(d, s, c) bcopy(s, d, c) #endif ./knews-1.0b.1/regexp/utils.h.orig100644 1244 1244 724 6455455543 15362 0ustar kallekalle/* utility definitions */ #define DUPMAX _POSIX2_RE_DUP_MAX /* xxx is this right? */ #define INFINITY (DUPMAX + 1) #define NC (CHAR_MAX - CHAR_MIN + 1) typedef unsigned char uch; /* switch off assertions (if not already off) if no REDEBUG */ #ifndef REDEBUG #ifndef NDEBUG #define NDEBUG /* no assertions please */ #endif #endif #include /* for old systems with bcopy() but no memmove() */ #ifdef USEBCOPY #define memmove(d, s, c) bcopy(s, d, c) #endif ./knews-1.0b.1/regexp/regex2.h.orig100644 1244 1244 12407 6455455543 15457 0ustar kallekalle/* * First, the stuff that ends up in the outside-world include file = typedef off_t regoff_t; = typedef struct { = int re_magic; = size_t re_nsub; // number of parenthesized subexpressions = const char *re_endp; // end pointer for REG_PEND = struct re_guts *re_g; // none of your business :-) = } regex_t; = typedef struct { = regoff_t rm_so; // start of match = regoff_t rm_eo; // end of match = } regmatch_t; */ /* * internals of regex_t */ #define MAGIC1 ((('r'^0200)<<8) | 'e') /* * The internal representation is a *strip*, a sequence of * operators ending with an endmarker. (Some terminology etc. is a * historical relic of earlier versions which used multiple strips.) * Certain oddities in the representation are there to permit running * the machinery backwards; in particular, any deviation from sequential * flow must be marked at both its source and its destination. Some * fine points: * * - OPLUS_ and O_PLUS are *inside* the loop they create. * - OQUEST_ and O_QUEST are *outside* the bypass they create. * - OCH_ and O_CH are *outside* the multi-way branch they create, while * OOR1 and OOR2 are respectively the end and the beginning of one of * the branches. Note that there is an implicit OOR2 following OCH_ * and an implicit OOR1 preceding O_CH. * * In state representations, an operator's bit is on to signify a state * immediately *preceding* "execution" of that operator. */ typedef unsigned long sop; /* strip operator */ typedef long sopno; #define OPRMASK 0xf8000000 #define OPDMASK 0x07ffffff #define OPSHIFT ((unsigned)27) #define OP(n) ((n)&OPRMASK) #define OPND(n) ((n)&OPDMASK) #define SOP(op, opnd) ((op)|(opnd)) /* operators meaning operand */ /* (back, fwd are offsets) */ #define OEND (1< uch [csetsize] */ uch mask; /* bit within array */ uch hash; /* hash code */ size_t smultis; char *multis; /* -> char[smulti] ab\0cd\0ef\0\0 */ } cset; /* note that CHadd and CHsub are unsafe, and CHIN doesn't yield 0/1 */ #define CHadd(cs, c) ((cs)->ptr[(uch)(c)] |= (cs)->mask, (cs)->hash += (c)) #define CHsub(cs, c) ((cs)->ptr[(uch)(c)] &= ~(cs)->mask, (cs)->hash -= (c)) #define CHIN(cs, c) ((cs)->ptr[(uch)(c)] & (cs)->mask) #define MCadd(p, cs, cp) mcadd(p, cs, cp) /* regcomp() internal fns */ #define MCsub(p, cs, cp) mcsub(p, cs, cp) #define MCin(p, cs, cp) mcin(p, cs, cp) /* stuff for character categories */ typedef unsigned char cat_t; /* * main compiled-expression structure */ struct re_guts { int magic; # define MAGIC2 ((('R'^0200)<<8)|'E') sop *strip; /* malloced area for strip */ int csetsize; /* number of bits in a cset vector */ int ncsets; /* number of csets in use */ cset *sets; /* -> cset [ncsets] */ uch *setbits; /* -> uch[csetsize][ncsets/CHAR_BIT] */ int cflags; /* copy of regcomp() cflags argument */ sopno nstates; /* = number of sops */ sopno firststate; /* the initial OEND (normally 0) */ sopno laststate; /* the final OEND */ int iflags; /* internal flags */ # define USEBOL 01 /* used ^ */ # define USEEOL 02 /* used $ */ # define BAD 04 /* something wrong */ int nbol; /* number of ^ used */ int neol; /* number of $ used */ int ncategories; /* how many character categories */ cat_t *categories; /* ->catspace[-CHAR_MIN] */ char *must; /* match must contain this string */ int mlen; /* length of must */ size_t nsub; /* copy of re_nsub */ int backrefs; /* does it use back references? */ sopno nplus; /* how deep does it nest +s? */ /* catspace must be last */ cat_t catspace[1]; /* actually [NC] */ }; /* misc utilities */ #define OUT (CHAR_MAX+1) /* a non-character value */ #define ISWORD(c) (isalnum(c) || (c) == '_') ./knews-1.0b.1/regexp/regcomp.ih100644 1244 1244 5226 6455455543 15112 0ustar kallekalle/* ========= begin header generated by ./mkh ========= */ #ifdef __cplusplus extern "C" { #endif /* === regcomp.c === */ static void p_ere(register struct parse *p, int stop); static void p_ere_exp(register struct parse *p); static void p_str(register struct parse *p); static void p_bre(register struct parse *p, register int end1, register int end2); static int p_simp_re(register struct parse *p, int starordinary); static int p_count(register struct parse *p); static void p_bracket(register struct parse *p); static void p_b_term(register struct parse *p, register cset *cs); static void p_b_cclass(register struct parse *p, register cset *cs); static void p_b_eclass(register struct parse *p, register cset *cs); static char p_b_symbol(register struct parse *p); static char p_b_coll_elem(register struct parse *p, int endc); static char othercase(int ch); static void bothcases(register struct parse *p, int ch); static void ordinary(register struct parse *p, register int ch); static void nonnewline(register struct parse *p); static void repeat(register struct parse *p, sopno start, int from, int to); static int seterr(register struct parse *p, int e); static cset *allocset(register struct parse *p); static void freeset(register struct parse *p, register cset *cs); static int freezeset(register struct parse *p, register cset *cs); static int firstch(register struct parse *p, register cset *cs); static int nch(register struct parse *p, register cset *cs); static void mcadd(register struct parse *p, register cset *cs, register char *cp); static void mcsub(register cset *cs, register char *cp); static int mcin(register cset *cs, register char *cp); static char *mcfind(register cset *cs, register char *cp); static void mcinvert(register struct parse *p, register cset *cs); static void mccase(register struct parse *p, register cset *cs); static int isinsets(register struct re_guts *g, int c); static int samesets(register struct re_guts *g, int c1, int c2); static void categorize(struct parse *p, register struct re_guts *g); static sopno dupl(register struct parse *p, sopno start, sopno finish); static void doemit(register struct parse *p, sop op, size_t opnd); static void doinsert(register struct parse *p, sop op, size_t opnd, sopno pos); static void dofwd(register struct parse *p, sopno pos, sop value); static void enlarge(register struct parse *p, sopno size); static void stripsnug(register struct parse *p, register struct re_guts *g); static void findmust(register struct parse *p, register struct re_guts *g); static sopno pluscount(register struct parse *p, register struct re_guts *g); #ifdef __cplusplus } #endif /* ========= end header generated by ./mkh ========= */ ./knews-1.0b.1/regexp/engine.ih100644 1244 1244 2535 6455455543 14723 0ustar kallekalle/* ========= begin header generated by ./mkh ========= */ #ifdef __cplusplus extern "C" { #endif /* === engine.c === */ static int matcher(register struct re_guts *g, char *string, size_t nmatch, regmatch_t pmatch[], int eflags); static char *dissect(register struct match *m, char *start, char *stop, sopno startst, sopno stopst); static char *backref(register struct match *m, char *start, char *stop, sopno startst, sopno stopst, sopno lev); static char *fast(register struct match *m, char *start, char *stop, sopno startst, sopno stopst); static char *slow(register struct match *m, char *start, char *stop, sopno startst, sopno stopst); static states step(register struct re_guts *g, sopno start, sopno stop, register states bef, int ch, register states aft); #define BOL (OUT+1) #define EOL (BOL+1) #define BOLEOL (BOL+2) #define NOTHING (BOL+3) #define BOW (BOL+4) #define EOW (BOL+5) #define CODEMAX (BOL+5) /* highest code used */ #define NONCHAR(c) ((c) > CHAR_MAX) #define NNONCHAR (CODEMAX-CHAR_MAX) #ifdef REDEBUG static void print(struct match *m, char *caption, states st, int ch, FILE *d); #endif #ifdef REDEBUG static void at(struct match *m, char *title, char *start, char *stop, sopno startst, sopno stopst); #endif #ifdef REDEBUG static char *pchar(int ch); #endif #ifdef __cplusplus } #endif /* ========= end header generated by ./mkh ========= */ ./knews-1.0b.1/regexp/regerror.ih100644 1244 1244 413 6455455543 15256 0ustar kallekalle/* ========= begin header generated by ./mkh ========= */ #ifdef __cplusplus extern "C" { #endif /* === regerror.c === */ static char *regatoi(const regex_t *preg, char *localbuf); #ifdef __cplusplus } #endif /* ========= end header generated by ./mkh ========= */ ./knews-1.0b.1/src/ 40755 1244 1244 0 6642131451 12312 5ustar kallekalle./knews-1.0b.1/src/bg.c100644 1244 1244 17423 6455455543 13207 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include "global.h" #include "bg.h" #include "codes.h" #include "resource.h" #include "server.h" #include "sysdeps.h" #include "widgets.h" #include "xutil.h" #include "../Widgets/ScrList.h" typedef enum { BgStateDisconn, BgStateConnWait, BgStateSentModeReader, BgStateSentGroup, BgStateSentAuthUser, BgStateSentAuthPass, BgStateBusy, BgStateIdle } BgState; static SERVER *bg_server = NULL; static BgProc bg_proc = NULL; static BgState bg_state = BgStateDisconn; static GROUP *bg_group = NULL; static long first = 0; static long last = 0; static long total = 1; static XtInputId input_id = 0; static XtIntervalId timeout_id = 0; static unsigned long timeout = 0; static int bg_start_connect(void); static int bg_start_auth(void); #define BACK_OFF 1.25 #define MIN_TIMEOUT ( 200ul) #define MID_TIMEOUT ( 2*1000ul) #define MAX_TIMEOUT (5*60*1000ul) #define MAX_PROCS 2 static BgProc procs[MAX_PROCS] = {0, }; static int n_procs = 0; static void put_proc(BgProc proc) { int i; if (n_procs >= MAX_PROCS) return; for (i = 0 ; i < n_procs ; i++) if (procs[i] == proc) return; procs[n_procs++] = proc; } static void set_timeout(void); static void bg_schedule(void) { int i; bg_state = BgStateBusy; for (i = 0 ; i < n_procs ; i++) { bg_proc = procs[i]; if (bg_proc(bg_server)) { timeout /= BACK_OFF; return; } } bg_state = BgStateIdle; bg_proc = NULL; timeout *= BACK_OFF; set_timeout(); } static void timeout_callback(XtPointer client_data, XtIntervalId *id) { timeout_id = 0; if (bg_state == BgStateIdle) bg_schedule(); } static void remove_timeout(void) { if (timeout_id) XtRemoveTimeOut(timeout_id); timeout_id = 0; } static void set_timeout(void) { remove_timeout(); if (timeout < MIN_TIMEOUT) timeout = MIN_TIMEOUT; else if (timeout > MAX_TIMEOUT) { if (bg_state == BgStateIdle) { bg_shutdown(); set_message("Closed second connection to server.", False); return; } timeout = MAX_TIMEOUT; } timeout_id = XtAppAddTimeOut(app_cont, timeout, timeout_callback, NULL); } static void bg_read_callback(XtPointer client_data, int *fd, XtInputId *id) { char message[128]; char *err; long i; int tmp, busy; i = server_read_raw(bg_server); if (i > 0) { char *buffer; switch (bg_state) { case BgStateDisconn: break; case BgStateConnWait: buffer = server_get_line(bg_server); if (!buffer) return; if (atoi(buffer) != NNTP_OK_CANPOST && atoi(buffer) != NNTP_OK_NOPOST) { if (strlen(buffer) > 80) buffer[80] = '\0'; sprintf(message, "Failed to open second connection, " "message from server is: %s", buffer); set_message(message, True); break; } busy = global.busy; if (!busy) global.busy = True; tmp = server_write(bg_server, "MODE READER\r\n"); if (!busy) global.busy = False; if (tmp == 0) { bg_state = BgStateSentModeReader; return; /* OK */ } set_message("Failed to open second connection to server!", True); break; case BgStateSentModeReader: buffer = server_get_line(bg_server); if (!buffer) return; if (atoi(buffer) == NNTP_ERR_NEED_AUTH && bg_start_auth()) return; bg_state = BgStateIdle; bg_schedule(); return; case BgStateSentGroup: buffer = server_get_line(bg_server); if (!buffer) return; if (atoi(buffer) == NNTP_ERR_NEED_AUTH && bg_start_auth()) return; if (sscanf(buffer, "%d%ld%ld%ld", &tmp, &total, &first, &last) != 4 || tmp != NNTP_OK_GROUP) { if (bg_group) fprintf(stderr, "bg: Failed to enter %s: %s!\n", bg_group->name, buffer); bg_group = NULL; } bg_state = BgStateIdle; bg_schedule(); return; case BgStateSentAuthUser: buffer = server_get_line(bg_server); if (!buffer) return; if (atoi(buffer) != NNTP_CONT_AUTH) fprintf(stderr, "bg: Couldn't authenticate second " "connection: %s\n", buffer); /* Still expecting reply to AUTHINFO PASS */ bg_state = BgStateSentAuthPass; return; case BgStateSentAuthPass: buffer = server_get_line(bg_server); if (!buffer) return; if (atoi(buffer) != NNTP_OK_AUTH) { fprintf(stderr, "bg: Couldn't authenticate second " "connection: %s\n", buffer); bg_group = NULL; } bg_state = BgStateIdle; bg_schedule(); return; case BgStateBusy: if (!bg_proc(bg_server)) { bg_proc = NULL; bg_state = BgStateIdle; if (timeout > MID_TIMEOUT) timeout = MID_TIMEOUT; set_timeout(); } return; case BgStateIdle: break; } } err = NULL; if (i < 0) { if (errno == EINTR || would_block(server_get_fd(bg_server), errno)) return; /* Should never happen... */ perror("knews: read"); err = error_string(errno); } if (!err) err = "Broken pipe"; sprintf(message, "Second connection to server: %s!", err); set_message(message, True); bg_shutdown(); bg_start_connect(); } static int bg_start_connect(void) { int tmp; if (!bg_server) bg_server = server_create(-1); set_message("Opening second connection to server...", False); if (global.nntp_server[0] != '#') tmp = server_open(bg_server, global.serv_addr, 0); else tmp = server_fork(bg_server, NULL, 0); if (tmp < 0) { set_message("Failed to open second connection to server!", True); server_close(bg_server); return False; } input_id = XtAppAddInput(app_cont, server_get_fd(bg_server), (XtPointer)XtInputReadMask, bg_read_callback, NULL); bg_state = BgStateConnWait; return True; } void bg_nudge(BgProc proc) { put_proc(proc); switch (bg_state) { case BgStateDisconn: bg_start_connect(); break; case BgStateIdle: if (timeout < MID_TIMEOUT) timeout /= BACK_OFF; else { timeout = MID_TIMEOUT; set_timeout(); } break; default: timeout /= BACK_OFF; break; } } void bg_shutdown(void) { bg_group = NULL; if (bg_server) server_close(bg_server); bg_state = BgStateDisconn; if (input_id) XtRemoveInput(input_id); input_id = 0; remove_timeout(); if (bg_proc) bg_proc(NULL); bg_proc = NULL; } GROUP *bg_in_group(long *totalp, long *firstp, long *lastp) { if (totalp) *totalp = total; if (firstp) *firstp = first; if (lastp) *lastp = last; return bg_group; } void bg_start_group(GROUP *group) { char command[512]; int tmp, busy; if (bg_state != BgStateBusy) return; bg_group = group; bg_state = BgStateSentGroup; sprintf(command, "GROUP %s\r\n", group->name); busy = global.busy; if (!busy) global.busy = True; tmp = server_write(bg_server, command); if (!busy) global.busy = False; if (tmp < 0) { fprintf(stderr, "knews: bg failed to enter %s.\n", group->name); bg_state = BgStateIdle; bg_group = NULL; if (bg_proc) bg_proc(NULL); bg_proc = NULL; } } static int bg_start_auth(void) { char command[1024]; char *auth_info_user = res_auth_info_user(); char *auth_info_pass = res_auth_info_pass(); int tmp, busy; if (!auth_info_user || !auth_info_pass) return False; if (strlen(auth_info_user) > 480 || strlen(auth_info_pass) > 480) { fputs("knews: authInfoUser or authInfoPass too long\n", stderr); return False; } sprintf(command, "AUTHINFO USER %s\r\nAUTHINFO PASS %s\r\n", auth_info_user, auth_info_pass); busy = global.busy; if (!busy) global.busy = True; tmp = server_write(bg_server, command); if (!busy) global.busy = False; if (tmp < 0) { bg_state = BgStateIdle; bg_group = NULL; if (bg_proc) bg_proc(NULL); bg_proc = NULL; return False; } bg_state = BgStateSentAuthUser; return True; } ./knews-1.0b.1/src/misc.h100644 1244 1244 3343 6455455543 13533 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ extern void update_misc_menu(NewsMode); extern void create_misc_menu1(Widget); extern void create_misc_menu2(Widget); extern void popdown_msgid_dialogue(void); extern void action_mark_read_article(Widget, XEvent*, String*, Cardinal*); extern void action_mark_read_subject(Widget, XEvent*, String*, Cardinal*); extern void action_mark_read_thread(Widget, XEvent*, String*, Cardinal*); extern void action_mark_read_subthread(Widget, XEvent*, String*, Cardinal*); extern void action_mark_read_tagged(Widget, XEvent*, String*, Cardinal*); extern void action_mark_read_all(Widget, XEvent*, String*, Cardinal*); extern void action_mark_read_to_current(Widget, XEvent*, String*, Cardinal*); extern void action_mark_read_non_tagged(Widget, XEvent*, String*, Cardinal*); extern void action_mark_read_cold(Widget, XEvent*, String*, Cardinal*); extern void action_mark_unread_article(Widget, XEvent*, String*, Cardinal*); extern void action_mark_unread_subject(Widget, XEvent*, String*, Cardinal*); extern void action_mark_unread_thread(Widget, XEvent*, String*, Cardinal*); extern void action_mark_unread_subthread(Widget, XEvent*, String*, Cardinal*); extern void action_mark_unread_tagged(Widget, XEvent*, String*, Cardinal*); extern void action_mark_unread_all(Widget, XEvent*, String*, Cardinal*); extern void action_mark_unread_killed(Widget, XEvent*, String*, Cardinal*); extern void action_clear_tagged(Widget, XEvent*, String*, Cardinal*); extern void action_catchup(Widget, XEvent*, String*, Cardinal*); extern void action_subscribe(Widget, XEvent*, String*, Cardinal*); extern void action_unsubscribe(Widget, XEvent*, String*, Cardinal*); extern void action_tag_hot(Widget, XEvent*, String*, Cardinal*); ./knews-1.0b.1/src/parse.c100644 1244 1244 36114 6624760103 13714 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include "global.h" #include "decode.h" #include "parse.h" #include "util.h" const char month_names[] = "JanFebMarAprMayJunJulAugSepOctNovDec"; char *time_t_to_date(time_t t, char *buffer) { struct tm *tm; tm = gmtime(&t); if (!tm) { *buffer = '\0'; return buffer; } sprintf(buffer, "%d %3.3s %4d %02d:%02d:%02d GMT", tm->tm_mday, month_names + 3 * tm->tm_mon, 1900 + tm->tm_year, tm->tm_hour, tm->tm_min, tm->tm_sec); return buffer; } /* * tm_sec + tm_min*60 + tm_hour*3600 + tm_yday*86400+ * (tm_year-70)*31536000 + ((tm_year-69)/4)*86400 */ static long tm_to_secs(struct tm *tm) { static int acc_month_days[] = { 0, 31, 31 + 28, 31 + 28 + 31, 31 + 28 + 31 + 30, 31 + 28 + 31 + 30 + 31, 31 + 28 + 31 + 30 + 31 + 30, 31 + 28 + 31 + 30 + 31 + 30 + 31, 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31, 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30, 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31, 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30, }; long result = 0; unsigned i; i = tm->tm_year - 70; if (tm->tm_year < 70) return 0; #if 1 result = 365 * (i % 4); if (i % 4 >= 2) result += 1; #else switch ((unsigned int)i % 4) { case 0: result = 0; break; case 1: result = 365; break; case 2: /* Leap year, except 2000 */ if (i != 30 && tm->tm_mon > 1) result = 365 + 365 + 1; else result = 365 + 365; break; case 3: result = 365 + 365 + 365 + 1; break; } if (i > 30) /* past 2000 => we got one day too much ??? */ result--; #endif result += (i / 4) * (365 + 365 + 365 + 365 + 1); result += acc_month_days[(unsigned int)tm->tm_mon % 12]; result += tm->tm_mday - 1; result = 24 * result + tm->tm_hour; result = 60 * result + tm->tm_min; result = 60 * result + tm->tm_sec; return result; } time_t parsedate(char *str) { struct tm tm; long offset; unsigned int c0, c1, c2, c3; while (*str == ' ') str++; if (*str < '0') return PARSEDATE_ERROR; if (*str > '9') { if ((!IS_UPPER(str[0]) && !IS_LOWER(str[0])) || (!IS_UPPER(str[1]) && !IS_LOWER(str[1])) || (!IS_UPPER(str[2]) && !IS_LOWER(str[2]))) return PARSEDATE_ERROR; str += 3; while (*str == ' ') str++; if (*str == ',') { str++; while (*str == ' ') str++; } if (!IS_DIGIT(*str)) return PARSEDATE_ERROR; } tm.tm_mday = *str++ - '0'; while (IS_DIGIT(*str)) { tm.tm_mday *= 10; tm.tm_mday += *str++ - '0'; } while (*str == ' ') str++; if ((c0 = TO_UPPER(str[0])) == '\0' || (c1 = TO_UPPER(str[1])) == '\0' || (c2 = TO_UPPER(str[2])) == '\0') return PARSEDATE_ERROR; switch (c0) { case 'J': if (c1 == 'A') if (c2 == 'N') tm.tm_mon = 0; else return PARSEDATE_ERROR; else if (c1 == 'U') if (c2 == 'N') tm.tm_mon = 5; else if (c2 == 'L') tm.tm_mon = 6; else return PARSEDATE_ERROR; else return PARSEDATE_ERROR; break; case 'F': if (c1 == 'E' && c2 == 'B') tm.tm_mon = 1; else return PARSEDATE_ERROR; break; case 'M': if (c1 == 'A') if (c2 == 'R') tm.tm_mon = 2; else if (c2 == 'Y') tm.tm_mon = 4; else return PARSEDATE_ERROR; else return PARSEDATE_ERROR; break; case 'A': if (c1 == 'P') if (c2 == 'R') tm.tm_mon = 3; else return PARSEDATE_ERROR; else if (c1 == 'U') if (c2 == 'G') tm.tm_mon = 7; else return PARSEDATE_ERROR; else return PARSEDATE_ERROR; break; case 'S': if (c1 == 'E' && c2 == 'P') tm.tm_mon = 8; else return PARSEDATE_ERROR; break; case 'O': if (c1 == 'C' && c2 == 'T') tm.tm_mon = 9; else return PARSEDATE_ERROR; break; case 'N': if (c1 == 'O' && c2 == 'V') tm.tm_mon = 10; else return PARSEDATE_ERROR; break; case 'D': if (c1 == 'E' && c2 == 'C') tm.tm_mon = 11; else return PARSEDATE_ERROR; break; default: return PARSEDATE_ERROR; } str += 3; while (*str == ' ') str++; if ((c0 = str[0] - '0') > 9u || (c1 = str[1] - '0') > 9u) return PARSEDATE_ERROR; c2 = str[2] - '0'; if (c2 > 9u) { tm.tm_year = 10 * c0 + c1; str += 2; } else { c3 = str[3] - '0'; if (c3 > 9u) return PARSEDATE_ERROR; tm.tm_year = 1000 * c0 + 100 * c1 + 10 * c2 + c3 - 1900; str += 4; } while (*str == ' ') str++; if ((c0 = str[0] - '0') > 9u || (c1 = str[1] - '0') > 9u || str[2] != ':') return PARSEDATE_ERROR; tm.tm_hour = 10 * c0 + c1; str += 3; if ((c0 = str[0] - '0') > 9u || (c1 = str[1] - '0') > 9u || (str[2] != ':' && str[2] != '\0' && str[2] != ' ')) return PARSEDATE_ERROR; tm.tm_min = 10 * c0 + c1; str += 2; if (*str == '\0') { tm.tm_sec = 0; return tm_to_secs(&tm); } if (*str != ':') tm.tm_sec = 0; else { str++; if ((c0 = str[0] - '0') > 9u || (c1 = str[1] - '0') > 9u || (str[2] != ' ' && str[2] != '\0')) return PARSEDATE_ERROR; tm.tm_sec = 10 * c0 + c1; str += 2; } while (*str == ' ') str++; if (*str == '\0') return tm_to_secs(&tm); if (*str == '+' || *str == '-') { if ((c0 = str[1] - '0') > 9u || (c1 = str[2] - '0') > 9u || (c2 = str[3] - '0') > 9u || (c3 = str[4] - '0') > 9u) return PARSEDATE_ERROR; offset = 3600 * (10 * c0 + c1) + 60 * c2 + c3; if (*str == '-') offset = - offset; } else { if ((c0 = str[0], !IS_UPPER(c0)) || (c1 = str[1], !IS_UPPER(c1)) || (c2 = str[2], !IS_UPPER(c2))) return PARSEDATE_ERROR; switch (c0) { case 'C': if (c2 == 'T') /* CST, CDT, CET */ if (c1 == 'S') offset = - 6 * 3600; else if (c1 == 'D') offset = - 5 * 3600; else if (c1 == 'E') offset = 3600; else return PARSEDATE_ERROR; else if (c2 == 'S') /* CES */ if (c1 == 'E') offset = 3600; else return PARSEDATE_ERROR; else return PARSEDATE_ERROR; break; case 'E': if (c2 == 'T') /* EST, EDT, EET */ if (c1 == 'S') offset = - 5 * 3600; else if (c1 == 'D') offset = - 4 * 3600; else if (c1 == 'E') offset = 2 * 3600; else return PARSEDATE_ERROR; else return PARSEDATE_ERROR; break; case 'M': if (c2 == 'T') /* MST, MDT, MET */ if (c1 == 'S') offset = - 7 * 3600; else if (c1 == 'D') offset = - 6 * 3600; else if (c1 == 'E') offset = 3600; else return PARSEDATE_ERROR; else if (c2 == 'Z') /* MEZ */ if (c1 == 'E') offset = 3600; else return PARSEDATE_ERROR; else return PARSEDATE_ERROR; break; case 'P': if (c2 == 'T') /* PST, PDT */ if (c1 == 'S') offset = - 8 * 3600; else if (c1 == 'D') offset = - 7 * 3600; else return PARSEDATE_ERROR; else return PARSEDATE_ERROR; break; default: offset = 0; break; } } return tm_to_secs(&tm) - offset; } /*************************************************************************/ char *eat_re(char *str) { long n; while ((str[0] == 'R' || str[0] == 'r') && (str[1] == 'E' || str[1] == 'e')) { if (str[2] == ':') str += 3; else if (str[2] == '^' && IS_DIGIT(str[3]) && str[4] == ':') str += 5; else break; while (*str == ' ') str++; } n = strlen(str) - 1; while (n > 0 && IS_SPACE(str[n])) str[n--] = '\0'; return str; } const char *parse_author(const char *from, long *len) { const char *c1, *c2; if ((c2 = strchr(from, '<'))) { c1 = from; while (c1 < c2 && (IS_SPACE(*c1) || *c1 == '"')) c1++; do { c2--; } while (c2 > c1 && (IS_SPACE(*c2) || *c2 == '"')); *len = c2 - c1 + 1; if (*len > 0) return c1; } if ((c1 = strchr(from, '(')) && (c2 = strrchr(c1, ')'))) { do { c1++; } while (c1 < c2 && (IS_SPACE(*c1) || *c1 == '"')); do { c2--; } while (c2 > c1 && (IS_SPACE(*c2) || *c2 == '"')); *len = c2 - c1 + 1; if (*len > 0) return c1; } c1 = from; while (IS_SPACE(*c1)) c1++; if (*c1 != '\0') { c2 = c1 + strlen(c1); do { c2--; } while (c2 > c1 && IS_SPACE(*c1)); *len = c2 - c1 + 1; if (*len > 0) return c1; } *len = 6; return ""; } /*********************************************************************/ static int get_token(char **c, char **p) { int len; if (**c == '"') { *p = ++*c; while (**p != '\0' && **p != '"') (*p)++; if (**p == '\0') len = -1; else len = (*p)++ - *c; } else { *p = *c; while (**p != '\0' && **p != ' ' && **p != '\t' && **p != '/' && **p != ';' && **p != '(' && **p != '=' && **p != '"') (*p)++; len = *p - *c; } return len; } int parse_content_enc(char **headers) { char *c, *p; int comment, len; c = headers[0] + sizeof "Content-Transfer-Encoding:" - 1; comment = 0; do { for (;;) { while (IS_SPACE(*c)) c++; if (*c == '\0') break; if (*c == '(') comment++; if (comment) { if (*c == ')') comment--; c++; continue; } len = get_token(&c, &p); #define IS_ENC(enc) \ (len == sizeof enc - 1 && \ case_lstrncmp(c, enc, sizeof enc - 1) == 0) switch (TO_UPPER(*c)) { case '7': if (IS_ENC("7bit")) return MimeEncNone; break; case '8': if (IS_ENC("8bit")) return MimeEncNone; break; case 'B': if (IS_ENC("binary")) return MimeEncNone; else if (IS_ENC("base64")) return MimeEncBase64; break; case 'Q': if (IS_ENC("quoted-printable")) return MimeEncQP; break; case 'U': if (IS_ENC("uue") || IS_ENC("uuencode") || IS_ENC("uuencoded")) return MimeEncUue; break; case 'X': if (IS_ENC("x-uue") || IS_ENC("x-uuencode") || IS_ENC("x-uuencoded")) return MimeEncUue; break; } #undef IS_ENC return -1; } c = *++headers; } while (c && IS_SPACE(*c)); return -1; } char *parse_content_disp(char **headers) { enum { CDLookingForType, CDLookingForSemicolon, CDLookingForName, CDLookingForEqual, CDLookingForValue } state = CDLookingForType; char *c, *p; int len, comment, was_filename = False; c = headers[0] + sizeof "Content-Disposition:" - 1; comment = 0; do { for (;;) { while (IS_SPACE(*c)) c++; if (*c == '\0') break; if (*c == '(') comment++; if (comment) { if (*c == ')') comment--; c++; continue; } switch (state) { case CDLookingForType: len = get_token(&c, &p); if (len < 0) return NULL; /* Check whether inline or attahcment? */ c = p; state = CDLookingForSemicolon; break; case CDLookingForSemicolon: if (*c++ != ';') return NULL; state = CDLookingForName; break; case CDLookingForName: len = get_token(&c, &p); if (len < 0) return NULL; if (len == 8 && case_lstrncmp(c, "filename", 8) == 0) was_filename = True; c = p; state = CDLookingForEqual; break; case CDLookingForEqual: if (*c++ != '=') return NULL; state = CDLookingForValue; break; case CDLookingForValue: len = get_token(&c, &p); if (len < 0) return NULL; if (was_filename) { p = XtMalloc(len + 1); memcpy(p, c, len); p[len] = '\0'; return p; } c = p; state = CDLookingForSemicolon; break; } } c = *++headers; } while (c && IS_SPACE(*c)); return NULL; } int parse_content_type(char **headers, char *type_buf, int type_len, char *subtype_buf, int subtype_len, MimeArg *args, int n_args, int strict) { enum { CTLookingForType, CTLookingForSlash, CTLookingForSubtype, CTLookingForSemicolon, CTLookingForName, CTLookingForEqual, CTLookingForValue } state = CTLookingForType; char *c, *p; int len, n, comment; c = headers[0] + sizeof "Content-Type:" - 1; n = 0; comment = 0; do { for (;;) { while (IS_SPACE(*c)) c++; if (*c == '\0') break; if (*c == '(') comment++; if (comment) { if (*c == ')') comment--; c++; continue; } switch (state) { case CTLookingForType: len = get_token(&c, &p); if (len < 0 || len + 4 > type_len) return False; memcpy_lower(type_buf, c, len); type_buf[len] = '\0'; c = p; state = CTLookingForSlash; break; case CTLookingForSlash: if (*c++ != '/') return False; state = CTLookingForSubtype; break; case CTLookingForSubtype: len = get_token(&c, &p); if (len < 0 || len + 4 > subtype_len) return False; memcpy_lower(subtype_buf, c, len); subtype_buf[len] = '\0'; c = p; state = CTLookingForSemicolon; break; case CTLookingForSemicolon: if (*c++ != ';') return True; /* lenience */ state = CTLookingForName; break; case CTLookingForName: if (n >= n_args) return True; len = get_token(&c, &p); if (len < 0) return !strict; /* lenience */ args[n].name = XtMalloc(len + 1); memcpy_lower(args[n].name, c, len); args[n].name[len] = '\0'; c = p; state = CTLookingForEqual; break; case CTLookingForEqual: if (*c++ != '=') return !strict; /* lenience */ state = CTLookingForValue; break; case CTLookingForValue: len = get_token(&c, &p); if (len < 0) return !strict; /* lenience */ args[n].value = XtMalloc(len + 1); memcpy(args[n].value, c, len); args[n++].value[len] = '\0'; c = p; state = CTLookingForSemicolon; break; } } c = *++headers; } while (c && IS_SPACE(*c)); if (state ==CTLookingForSemicolon || state == CTLookingForName) return True; else if (state == CTLookingForEqual || state == CTLookingForValue) return !strict; else return False; } char *get_charset(MimeArg *args) { while (args->value) if (strcmp(args->name, "charset") == 0) return args->value; else args++; return NULL; } char *next_enc_word(char *header, EncWordData *data) { for (header = strstr(header, "=?") ; header ; header = strstr(header + 1, "=?")) { char *c1, *c2; c1 = strchr(header + 2, '?'); if (!c1 || (c1[1] != 'q' && c1[1] != 'Q' && c1[1] != 'b' && c1[1] != 'B') || c1[2] != '?') continue; c2 = strstr(c1 + 3, "?="); if (!c2) continue; data->word = c1 + 3; data->end = c2 + 2; data->len = c2 - c1 - 3; data->ch_len = c1 - (header + 2); data->is_qp = (c1[1] == 'Q' || c1[1] == 'q'); break; } return header; } void decode_rfc1522(char *src, const char *charset) { EncWordData data; char *dest = NULL; long n; if (!charset) return; for (;;) { char *next; for (next = next_enc_word(src, &data) ; next ; next = next_enc_word(next + 1, &data)) { int tmp; (next + 2)[data.ch_len] = '\0'; tmp = case_strcmp(next + 2, charset); (next + 2)[data.ch_len] = '?'; if (tmp == 0) { if (!dest) dest = next; else { n = next - src; memmove(dest, src, n); dest += n; } break; } } if (!next) break; if (data.is_qp) n = decode_qp(dest, data.word, data.len, NULL, True); else { B64Context bc = {0, }; n = decode_base64(&bc, dest, data.word, data.len); } if (n >= 0) dest += n; src = data.end; } if (dest) memmove(dest, src, strlen(src) + 1); } ./knews-1.0b.1/src/read.c100644 1244 1244 66123 6625552355 13531 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include "global.h" #include "cache.h" #include "charset.h" #include "codes.h" #include "connect.h" #include "decode.h" #include "expand.h" #include "mailcap.h" #include "parse.h" #include "partial.h" #include "font.h" #include "read.h" #include "resource.h" #include "save.h" #include "server.h" #include "tag.h" #include "util.h" #include "viewer.h" #include "widgets.h" #include "xutil.h" #include "../Widgets/ArtText.h" #include "../Widgets/ArtTree.h" #include "../Widgets/Dialogue.h" #include "../Widgets/Scrollable.h" #define STARTS_AS_BOUNDARY(buffer, boundary, len) \ (boundary && buffer[0] == '-' && buffer[1] =='-' && \ strncmp(buffer + 2, boundary, len) == 0) #define IS_BOUNDARY(buffer, boundary, len) \ (STARTS_AS_BOUNDARY(buffer, boundary, len) && \ (buffer[len + 2] == '\0' || \ (buffer[len + 2] == '-' && buffer[len + 3] == '-' && \ buffer[len + 4] == '\0'))) #define IS_LAST_BOUNDARY(buffer, boundary, len) \ (STARTS_AS_BOUNDARY(buffer, boundary, len) && \ buffer[len + 2] == '-' && buffer[len + 3] == '-' && \ buffer[len + 4] == '\0') #define N_ARGS 8 #define MIME_BUF_LEN 127 static void decode_header(char *header) { EncWordData data; XFontStruct *font = NULL; int append = False; char *c; long len; for (;;) { for (c = next_enc_word(header, &data) ; c ; c = next_enc_word(c + 1, &data)) { MimeFont *f; (c + 2)[data.ch_len] = '\0'; f = get_font(c + 2); (c + 2)[data.ch_len] = '?'; if (f && f->header_font && (!f->funcs || f->head_enc_hack)) { if (font && font->fid != f->header_font->fid && append) ArtTextAddLine(main_widgets.text, " ", f->header_font, global.header_pixel); font = f->header_font; break; } } if (!c) break; if (c > header) { *c = '\0'; if (append) ArtTextAppendToLast(main_widgets.text, header); else { ArtTextAddLine(main_widgets.text, header, font, global.header_pixel); append = True; } } if (data.is_qp) len = decode_qp(c, data.word, data.len, NULL, True); else { B64Context bc = {0, }; len = decode_base64(&bc, c, data.word, data.len); /* ignore error */ } if (len < 0) len = 0; c[len] = '\0'; if (append) ArtTextAppendToLast(main_widgets.text, c); else { ArtTextAddLine(main_widgets.text, c, font, global.header_pixel); append = True; } header = data.end; } if (!font) font = default_font->header_font; if (!font) font = ascii_font->header_font; if (append) ArtTextAppendToLast(main_widgets.text, header); else ArtTextAddLine(main_widgets.text, header, font, global.header_pixel); } static int insert_headers(char **headers, int n, int full_header) { MimeFont *f = default_font; char **loop = res_header_format(); int added_some = False; int i, hlen; if (loop && *loop && !full_header) { if (f->funcs && !f->head_enc_hack) { void *dec_data; XChar2b *wbuf; long len; dec_data = f->funcs->init(); do { hlen = strlen(*loop); for (i = 0 ; i < n ; i++) if (case_strncmp(*loop, headers[i], hlen) == 0) break; if (i == n) continue; do { len = f->funcs->decode(dec_data, headers[i], strlen(headers[i]), True, &wbuf); if (len >= 0) { ArtTextAddWLine(main_widgets.text, wbuf, len, f->header_font, global.header_pixel); added_some = True; } } while (++i < n && IS_SPACE(headers[i][0])); } while (*++loop); f->funcs->end(dec_data); } else { do { hlen = strlen(*loop); for (i = 0 ; i < n ; i++) if (case_strncmp(*loop, headers[i], hlen) == 0) break; if (i == n) continue; if (isupper((unsigned char)**loop)) do decode_header(headers[i]); while (++i < n && IS_SPACE(headers[i][0])); else do ArtTextAddLine(main_widgets.text, headers[i], f->header_font, global.header_pixel); while (++i < n && IS_SPACE(headers[i][0])); added_some = True; } while (*++loop); } } else if (f->funcs && !f->head_enc_hack) { void *dec_data; XChar2b *wbuf; long len; dec_data = f->funcs->init(); for (i = 0 ; i < n ; i++) { len = f->funcs->decode(dec_data, headers[i], strlen(headers[i]), True, &wbuf); if (len >= 0) { ArtTextAddWLine(main_widgets.text, wbuf, len, f->header_font, global.header_pixel); added_some = True; } } f->funcs->end(dec_data); } else { if (n > 0) added_some = True; for (i = 0 ; i < n ; i++) ArtTextAddLine(main_widgets.text, headers[i], f->header_font, global.header_pixel); } if (added_some) if (!f->funcs || f->head_enc_hack) ArtTextAddLine(main_widgets.text, "", f->header_font, global.header_pixel); else { XChar2b tmp; ArtTextAddWLine(main_widgets.text, &tmp, 0, f->header_font, global.header_pixel); } if (global.show_xfaces) { Pixmap pixmap; long width; long height; pixmap = do_xface(headers, n, &width, &height); if (pixmap) { ArtTextAddImage(main_widgets.text, pixmap, width, height, destroy_pixmap_callback, (void *)pixmap); ArtTextAddLine(main_widgets.text, "", f->header_font, global.header_pixel); } } return added_some; } static char *get_mime_body(SERVER *server, char *buffer, char **datap, long *lenp, int enc, char *bound, long bound_len) { long alloced = 65536; char *data = XtMalloc(alloced); long data_len = 0; long n; int tmp, soft; #define REALLOC(data, alloced, data_len, n) \ if (n + data_len + 8 >= alloced) { \ alloced = 2 * alloced + n + data_len + 8; \ data = XtRealloc(data, alloced); \ } switch (enc) { default: ArtTextAddLine(main_widgets.text, "[knews: unrecoginzed Content-Transfer-Encoding.]", ascii_font->body_font, global.alert_pixel); /* * Fall through. */ case MimeEncNone: while (buffer && !IS_DOT(buffer) && !IS_BOUNDARY(buffer, bound, bound_len)) { if (*buffer == '.') buffer++; n = strlen(buffer); buffer[n++] = '\n'; REALLOC(data, alloced, data_len, n); memcpy(data + data_len, buffer, n); data_len += n; buffer = server_read(server); } break; case MimeEncBase64: { B64Context bc = {0, }; while (buffer && !IS_DOT(buffer) && !IS_BOUNDARY(buffer, bound, bound_len)) { n = strlen(buffer); REALLOC(data, alloced, data_len, n); n = decode_base64(&bc, data + data_len, buffer, n); if (n > 0) data_len += n; buffer = server_read(server); } n = decode_base64(&bc, data + data_len, NULL, 0); if (n >= 0) data_len += n; tmp = base64_status(&bc); if (tmp & BASE64_ERROR) ArtTextAddLine(main_widgets.text, "[knews: base64 decode error.]", ascii_font->body_font, global.alert_pixel); if (tmp & BASE64_TRAILING_GARBAGE) ArtTextAddLine(main_widgets.text, "[knews: trailing garbage in base64 data.]", ascii_font->body_font, global.alert_pixel); } break; case MimeEncQP: while (buffer && !IS_DOT(buffer) && !IS_BOUNDARY(buffer, bound, bound_len)) { if (*buffer == '.') buffer++; n = strlen(buffer); REALLOC(data, alloced, data_len, n); data_len += decode_qp(data + data_len, buffer, n, &soft, False); if (!soft) data[data_len++] = '\n'; buffer = server_read(server); } break; case MimeEncUue: { UueContext uc = {0, }; while (buffer && !IS_DOT(buffer) && !IS_BOUNDARY(buffer, bound, bound_len)) { n = strlen(buffer); REALLOC(data, alloced, data_len, n); n = decode_uue(&uc, data + data_len, buffer, n); if (n > 0) data_len += n; buffer = server_read(server); } tmp = uue_status(&uc); if (tmp & UUE_NO_BEGIN) ArtTextAddLine(main_widgets.text, "[knews: uudecode error: no begin line]", ascii_font->body_font, global.alert_pixel); else if (tmp & UUE_NO_END) ArtTextAddLine(main_widgets.text, "[knews: uudecode error: no end line]", ascii_font->body_font, global.alert_pixel); else if (tmp & UUE_ERROR) ArtTextAddLine(main_widgets.text, "[knews: uudecode error]", ascii_font->body_font, global.alert_pixel); } break; } #undef REALLOC if (!buffer) { XtFree(data); return NULL; } *datap = data; *lenp = data_len; return buffer; } static void add_body_line(char *buffer, regex_t *re, MimeFont *f, void *dec_data, int is_lf_term) { if (!f->funcs) if (re && regexec(re, buffer, 0, NULL, 0) == 0) ArtTextAddLine(main_widgets.text, buffer, f->quote_font, global.quote_pixel); else ArtTextAddLine(main_widgets.text, buffer, f->body_font, global.pixel); else { XFontStruct *font; XChar2b *wbuf; long len; Pixel pixel; if (re && regexec(re, buffer, 0, NULL, 0) == 0) { font = f->quote_font; pixel = global.quote_pixel; } else { font = f->body_font; pixel = global.pixel; } len = f->funcs->decode(dec_data, buffer, strlen(buffer), is_lf_term, &wbuf); if (len >= 0) ArtTextAddWLine(main_widgets.text, wbuf, len, font, pixel); } } static void add_body_line_multi(char *buffer, regex_t *re, MimeFont *f, void *dec_data, int append) { do { char *c = strchr(buffer, '\n'); if (c) if (c > buffer && c[-1] == '\r') c[-1] = '\0'; else c[0] = '\0'; if (!append) add_body_line(buffer, re, f, dec_data, c != NULL); else if (!f->funcs) ArtTextAppendToLast(main_widgets.text, buffer); else { XChar2b *wbuf; long len; len = f->funcs->decode(dec_data, buffer, strlen(buffer), c != NULL, &wbuf); if (len >= 0) ArtTextWAppendToLast(main_widgets.text, wbuf, len); } append = False; buffer = c; if (buffer) buffer++; } while (buffer); } static char *insert_body(SERVER *server, char *buffer, MimeFont *f, int enc, char *bound, int len) { regex_t *re = res_quote_regexp(); long n; int soft, append, tmp; void *dec_data = NULL; if (f->funcs) dec_data = f->funcs->init(); switch (enc) { default: ArtTextAddLine(main_widgets.text, "[knews: unrecoginzed Content-Transfer-Encoding.]", ascii_font->body_font, global.alert_pixel); /* * Fall through */ case MimeEncNone: while (buffer && !IS_DOT(buffer) && !IS_BOUNDARY(buffer, bound, len)) { add_body_line(*buffer == '.' ? buffer + 1 : buffer, re, f, dec_data, True); buffer = server_read(server); } break; case MimeEncQP: append = False; while (buffer && !IS_DOT(buffer) && !IS_BOUNDARY(buffer, bound, len)) { n = decode_qp(buffer, buffer, strlen(buffer), &soft, False); if (n == 0) append = soft; else if (n > 0) { buffer[n] = '\0'; add_body_line_multi(buffer, re, f, dec_data, append); append = soft; } buffer = server_read(server); } break; case MimeEncBase64: { B64Context bc = {0, }; char *dest = NULL; long dest_len = 0; append = False; while (buffer && !IS_DOT(buffer) && !IS_BOUNDARY(buffer, bound, len)) { n = strlen(buffer); if (n + 8 > dest_len) { dest_len = n + 8; dest = XtRealloc(dest, dest_len); } n = decode_base64(&bc, dest, buffer, n); if (n > 0) { dest[n] = '\0'; add_body_line_multi(dest, re, f, dec_data, append); append = True; } buffer = server_read(server); } n = decode_base64(&bc, dest, NULL, 0); if (n > 0) { dest[n] = '\0'; add_body_line_multi(dest, re, f, dec_data, append); } XtFree(dest); tmp = base64_status(&bc); if (tmp & BASE64_ERROR) ArtTextAddLine(main_widgets.text, "[knews: base64 decode error.]", ascii_font->body_font, global.alert_pixel); if (tmp & BASE64_TRAILING_GARBAGE) ArtTextAddLine(main_widgets.text, "[knews: trailing garbage in base64 data.]", ascii_font->body_font, global.alert_pixel); } break; case MimeEncUue: { UueContext uc = {0, }; append = False; while (buffer && !IS_DOT(buffer) && !IS_BOUNDARY(buffer, bound, len)) { n = decode_uue(&uc, buffer, buffer, strlen(buffer)); if (n > 0) { add_body_line_multi(buffer, re, f, dec_data, append); append = True; } buffer = server_read(server); } tmp = uue_status(&uc); if (tmp & UUE_NO_BEGIN) ArtTextAddLine(main_widgets.text, "[knews: uudecode error: no begin line]", ascii_font->body_font, global.alert_pixel); else if (tmp & UUE_NO_END) ArtTextAddLine(main_widgets.text, "[knews: uudecode error: no end line]", ascii_font->body_font, global.alert_pixel); else if (tmp & UUE_ERROR) ArtTextAddLine(main_widgets.text, "[knews: uudecode error]", ascii_font->body_font, global.alert_pixel); } break; } if (f->funcs) f->funcs->end(dec_data); return buffer; } static char *do_multipart(SERVER *server, char *buffer, char *bound, int default_to_rfc822, int do_line, char *ext_bound, int ext_len) { int n; n = strlen(bound); while (buffer && !IS_DOT(buffer) && !IS_BOUNDARY(buffer, bound, n) && !IS_BOUNDARY(buffer, ext_bound, ext_len)) buffer = server_read(server); if (buffer) if (IS_DOT(buffer)) ArtTextAddLine(main_widgets.text, "[knews: premature EOF in multipart.]", ascii_font->body_font, global.alert_pixel); else if (IS_BOUNDARY(buffer, ext_bound, ext_len)) ArtTextAddLine(main_widgets.text, "[knews: outer boundary in nested multipart.]", ascii_font->body_font, global.alert_pixel); else if (IS_LAST_BOUNDARY(buffer, bound, n)) ArtTextAddLine(main_widgets.text, "[knews: empty multipart.]", ascii_font->body_font, global.alert_pixel); else { do { if (do_line) ArtTextAddSeparator(main_widgets.text, 2, 4); do_line = True; buffer = do_mime(NULL, server, server_read(server), default_to_rfc822, bound, n, NULL); } while (buffer && !IS_DOT(buffer) && !IS_LAST_BOUNDARY(buffer, bound, n)); if (!buffer) return NULL; if (IS_DOT(buffer)) ArtTextAddLine(main_widgets.text, "[knews: premature EOF in multipart.]", ascii_font->body_font, global.alert_pixel); while (buffer && !IS_DOT(buffer) && !IS_BOUNDARY(buffer, ext_bound, ext_len)) buffer = server_read(server); } return buffer; } char *do_mime(ARTICLE *art, SERVER *server, char *buffer, int default_to_rfc822, char *ext_bound, int ext_len, char **mime_hack) { int enc = MimeEncNone; char **headers; char type_buf[MIME_BUF_LEN + 1], subtype_buf[MIME_BUF_LEN + 1]; MimeArg args[N_ARGS + 1] = {{0, },}; int no_alloc, n, i, ct_index, cd_index; n = 0; no_alloc = 16; headers = (char **)XtMalloc(no_alloc * sizeof(char *)); for (i = 0 ; i < no_alloc ; i++) headers[i] = NULL; while (buffer && buffer[0] != '\0' && !IS_DOT(buffer) && !IS_BOUNDARY(buffer, ext_bound, ext_len)) { if (n + 3 > no_alloc) { i = no_alloc; no_alloc *= 2; headers = (char **)XtRealloc((char *)headers, no_alloc * sizeof(char *)); while (i < no_alloc) headers[i++] = NULL; } headers[n] = XtNewString(buffer); n++; buffer = server_read(server); } ct_index = -1; cd_index = -1; for (i = 0 ; i < n ; i++) if (case_lstrncmp(headers[i], "content-", 8) == 0) if (case_lstrncmp(headers[i] + 8, "type:", 5) == 0) ct_index = i; else if (case_lstrncmp(headers[i] + 8, "transfer-encoding:", 18) == 0) enc = parse_content_enc(headers + i); else if (case_lstrncmp(headers[i] + 8, "disposition:", 12)== 0) cd_index = i; if (mime_hack) enc = parse_content_enc(mime_hack + 2); if (buffer && buffer[0] == '\0') buffer = server_read(server); if (!ext_bound) { /* top level */ if (art && !art->from) realize_fake(art, headers, n); if (art && !art->xref && !art->read && res_process_xrefs()) fake_xref(art, headers, n); ArtTextClearLines(main_widgets.text); ScrollableSuspend(main_widgets.text); } if (mime_hack) { if (parse_content_type(mime_hack, type_buf, sizeof type_buf, subtype_buf, sizeof subtype_buf, args, N_ARGS, False)) ct_index = 0; } else if (ct_index >= 0) { if (!parse_content_type(headers + ct_index, type_buf, sizeof type_buf, subtype_buf, sizeof subtype_buf, args, N_ARGS, False)) ct_index = -1; } else if (default_to_rfc822) { strcpy(type_buf, "message"); strcpy(subtype_buf, "rfc822"); ct_index = 0; /* hack */ } if (ct_index < 0) { if (buffer) { insert_headers(headers, n, False); buffer = insert_body(server, buffer, default_font, MimeEncNone, ext_bound, ext_len); } } else do { /* one pass loop to continue out of */ const MailcapData *mailcap; int do_line; char *charset, *bound; MimeFont *f; ascii_lower(type_buf); ascii_lower(subtype_buf); do_line = insert_headers(headers, n, False); mailcap = mailcap_lookup(type_buf, subtype_buf); switch (type_buf[0]) { case 't': if (strcmp(type_buf, "text") != 0) break; if (strcmp(subtype_buf, "plain") != 0) { if (mailcap) break; ArtTextAddLine(main_widgets.text, "[knews: no mailcap viewer for 'text/", ascii_font->body_font, global.alert_pixel); ArtTextAppendToLast(main_widgets.text, subtype_buf); ArtTextAppendToLast(main_widgets.text, "'.]"); } charset = get_charset(args); if (!charset) f = default_font; else { f = get_font(charset); if (!f) { ArtTextAddLine(main_widgets.text, "[knews: no font for charset '", ascii_font->body_font, global.alert_pixel); ArtTextAppendToLast(main_widgets.text, charset); ArtTextAppendToLast(main_widgets.text, "'.]"); if (default_font->funcs) f = ascii_font; else f = default_font; } } buffer = insert_body(server, buffer, f, enc, ext_bound, ext_len); continue; case 'm': if (strcmp(type_buf, "multipart") == 0) { if (mailcap && strcmp(subtype_buf, "mixed") != 0 && strcmp(subtype_buf, "digest") != 0) break; /* * treat all subtypes as multipart/mixed, * alternative is just too much trouble */ bound = NULL; for (i = 0 ; args[i].value ; i++) if (case_lstrcmp(args[i].name, "boundary") == 0) { bound = args[i].value; break; } if (bound) { buffer = do_multipart(server, buffer, bound, strcmp(subtype_buf, "digest") == 0, do_line, ext_bound, ext_len); } else { /* no boundary, do raw text */ ArtTextAddLine(main_widgets.text, "[knews: multipart/* without a " "boundary parameter.]", ascii_font->body_font, global.alert_pixel); buffer = insert_body(server, buffer, default_font, enc, ext_bound, ext_len); } continue; } else if (strcmp(type_buf, "message") == 0) { if (strcmp(subtype_buf, "rfc822") == 0) { buffer = do_mime(art, server, buffer, False, ext_bound, ext_len, NULL); continue; } else if (strcmp(subtype_buf, "partial") == 0) { buffer = insert_body(server, buffer, default_font, MimeEncNone, ext_bound, ext_len); if (art && res_assemble_partials()) partial_cache_hook(art->no, args, True); continue; } else if (!mailcap && strcmp(subtype_buf, "external-body") == 0) { ArtTextAddLine(main_widgets.text, "[knews: no mailcap entry for " "Content-Type: message/external-body.]", ascii_font->body_font, global.alert_pixel); buffer = insert_body(server, buffer, default_font, MimeEncNone, ext_bound, ext_len); continue; } } break; } { char *view_cmd = NULL; char *file_name = NULL; char *data; long data_len; if (mailcap && mailcap->view_command) view_cmd = expand_view_command(mailcap->view_command, type_buf, subtype_buf, args, mailcap->needsterminal, mailcap->copiousoutput); if (cd_index >= 0 && (file_name = parse_content_disp(headers + cd_index))) file_name = XtNewString(file_name); if (!mime_hack) { i = ct_index; do { ArtTextAddLine(main_widgets.text, headers[i], ascii_font->header_font, global.header_pixel); } while (headers[++i] && IS_SPACE(headers[i][0])); ArtTextAddLine(main_widgets.text, "", ascii_font->body_font, global.pixel); } if (mailcap && mailcap->description) { ArtTextAddLine(main_widgets.text, "[mailcap description: ", ascii_font->header_font, global.header_pixel); ArtTextAppendToLast(main_widgets.text, mailcap->description); ArtTextAppendToLast(main_widgets.text, "]"); } buffer = get_mime_body(server, buffer, &data, &data_len, enc, ext_bound, ext_len); if (buffer) do_viewer(type_buf, subtype_buf, view_cmd, file_name, data, data_len); } } while (0); if (!ext_bound) /* toplevel */ ScrollableResume(main_widgets.text); for (i = 0 ; i < N_ARGS ; i++) { XtFree(args[i].name); XtFree(args[i].value); } for (i = 0 ; i < no_alloc ; i++) XtFree(headers[i]); XtFree((char *)headers); return buffer; } static char *get_article(ARTICLE *art, int full_header, long *sel_data, char **mime_hack) { SERVER *server = main_server; FILE *cf = NULL; char command[256]; char *buffer; if (art->no > 0) { server = cache_get_server(art->no, True); if (server) buffer = CODE_TO_STR(NNTP_OK_ARTICLE); else { cf = cache_get_file(art->no); server = main_server; sprintf(command, "ARTICLE %ld\r\n", art->no); buffer = server_comm(server, command, True); } } else if (art->hash_len < 240) { sprintf(command, "ARTICLE <%s>\r\n", art->msgid); buffer = server_comm(server, command, True); } else { char *temp = XtMalloc(art->hash_len + 16); sprintf(temp, "ARTICLE <%s>\r\n", art->msgid); buffer = server_comm(server, temp, True); XtFree(temp); } if (!buffer || atoi(buffer) != NNTP_OK_ARTICLE) { if (cf) { fclose(cf); cache_fetch_failed(art->no); } ArtTextClearLines(main_widgets.text); if (server == main_server) return buffer; server_free(server); return CODE_TO_STR(NNTP_OK_ARTICLE); } if (art->lines > 0) ArtTextAllocLines(main_widgets.text, art->lines + 32); if (cf) server_set_bs(server, cf); buffer = server_read(server); if (!full_header) buffer = do_mime(art, server, buffer, False, NULL, 0, mime_hack); else { long i; ArtTextClearLines(main_widgets.text); ScrollableSuspend(main_widgets.text); i = -1; while (buffer && buffer[0] != '\0' && !IS_DOT(buffer)) { if (sel_data && sel_data[0] == i) ArtTextAddSelected(main_widgets.text, buffer, ascii_font->header_font, global.header_pixel, sel_data[1], sel_data[2]); else ArtTextAddLine(main_widgets.text, buffer, ascii_font->header_font, global.header_pixel); i--; buffer = server_read(server); } if (buffer && buffer[0] == '\0') { regex_t *re = res_quote_regexp(); ArtTextAddLine(main_widgets.text, "", ascii_font->body_font, global.pixel); buffer = server_read(server); i = 0; while (buffer && !IS_DOT(buffer)) { XFontStruct *font; Pixel pixel; if (re && regexec(re, buffer, 0, NULL, 0) == 0) { font = ascii_font->quote_font; pixel = global.quote_pixel; } else { font = ascii_font->body_font; pixel = global.pixel; } if (sel_data && i == sel_data[0]) ArtTextAddSelected(main_widgets.text, buffer, font, pixel, sel_data[1], sel_data[2]); else ArtTextAddLine(main_widgets.text, buffer, font, pixel); buffer = server_read(server); i++; } } } ScrollableResume(main_widgets.text); if (cf) { server_set_bs(server, NULL); if (fclose(cf) == 0 && buffer) cache_fetch_done(art->no); else cache_fetch_failed(art->no); } if (server != main_server) server_free(server); else if (!buffer) return NULL; if (art->from && !art->read) { art->read = True; art->subject->no_unread--; global.curr_group->no_unread--; if (global.mode == NewsModeThread) { ArtTreeNodeSetInner(main_widgets.arttree, (ART_TREE_NODE *)art, False); ArtTreeNodeSetPixmap(main_widgets.arttree, (ART_TREE_NODE *)art, None); } if (art->pixmap != None) { global.n_hot--; update_subj_hot_value(art->subject); } update_subj_entry(art->subject); if (res_process_xrefs()) process_xref(art); } return CODE_TO_STR(NNTP_OK_ARTICLE); } int read_article(ARTICLE *art, int full_header, long *sel_data, char **mime_hack) { char *reply; if (art) { set_busy(True); reply = get_article(art, full_header || res_full_header(), sel_data, mime_hack); if (!reply) { reconnect_server(True); unset_busy(); return False; } unset_busy(); if (atoi(reply) == NNTP_OK_ARTICLE) { set_standard_message(); if (history_peek() != art) history_push(art); } else { char message[256]; if (strlen(reply) > 200) reply[200] ='\0'; sprintf(message, "Error! Message from server is: %s", reply); set_message(message, True); } } else { ArtTextClearLines(main_widgets.text); set_standard_message(); } return True; } /*********************************************************************/ void action_read_article(Widget w, XEvent *event, String *params, Cardinal *no_params) { if (global.busy || (global.mode != NewsModeGroup && global.mode != NewsModeThread)) return; if (global.curr_art) read_article(global.curr_art, *no_params != 0, NULL, NULL); } void action_mime_hack(Widget w, XEvent *event, String *params, Cardinal *no_params) { char *mime_hack[4]; char *ct, *cte; if (global.busy || (global.mode != NewsModeGroup && global.mode != NewsModeThread)) return; if (!global.curr_art) { set_message("No selected article!", True); return; } else if (*no_params != 2) { set_message("Wrong number of parameters to mime-hack()!", True); return; } ct = XtMalloc(16 + strlen(params[0])); cte = XtMalloc(32 + strlen(params[1])); sprintf(ct, "Content-Type: %s", params[0]); sprintf(cte, "Content-Transfer-Encoding: %s", params[1]); mime_hack[0] = ct; mime_hack[1] = NULL; mime_hack[2] = cte; mime_hack[3] = NULL; read_article(global.curr_art, False, NULL, mime_hack); XtFree(ct); XtFree(cte); } ./knews-1.0b.1/src/p_popup.c100644 1244 1244 71425 6455455543 14303 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include "global.h" #include #include #include "child.h" #include "file.h" #include "mailcap.h" #include "p_I.h" #include "p_attach.h" #include "p_check.h" #include "p_popup.h" #include "p_post.h" #include "p_setup.h" #include "parse.h" #include "resource.h" #include "widgets.h" #include "xutil.h" #include "../Widgets/ArtText.h" #include "../Widgets/Knapp.h" #include "../Widgets/FileSel.h" #include "../Widgets/Layout.h" #include "../Widgets/Manager.h" #include "../Widgets/MenuKnapp.h" #include "../Widgets/Message.h" #include "../Widgets/Sash.h" #include "../Widgets/Scrollable.h" #include "../Widgets/ScrBar.h" #include "../Widgets/ScrList.h" #include "../Widgets/StringG.h" #include "../Widgets/TextField.h" #include "../Widgets/Toggle.h" #include "../Widgets/Util.h" typedef struct PostWidgets { Widget shell; Widget text; Widget message; Widget post_knapp; Widget edit_knapp; Widget misc_knapp; Widget attach_knapp; Widget detach_knapp; Widget cancel_knapp; /* */ Widget list_manager; Widget list; /* */ Widget type_field; Widget inline_toggle; Widget attach_toggle; Widget name_field; Widget descr_field; Widget type_knapp; /* */ Widget none_toggle; Widget base64_toggle; Widget uue_toggle; Widget qp_toggle; /* */ Widget file_sel; } PostWidgets; static void post_knapp_callback(Widget, XtPointer, XtPointer); static void edit_knapp_callback(Widget, XtPointer, XtPointer); static void attach_knapp_callback(Widget, XtPointer, XtPointer); static void detach_knapp_callback(Widget, XtPointer, XtPointer); static void cancel_knapp_callback(Widget, XtPointer, XtPointer); static void attach_list_callback(Widget, XtPointer, XtPointer); static void tab_callback(Widget, XtPointer, XtPointer); static void type_field_callback(Widget, XtPointer, XtPointer); static void name_field_callback(Widget, XtPointer, XtPointer); static void descr_field_callback(Widget, XtPointer, XtPointer); static void disp_toggle_callback(Widget, XtPointer, XtPointer); static void enc_toggle_callback(Widget, XtPointer, XtPointer); static void misc_menu_callback(Widget, XtPointer, XtPointer); void destroy_post_widgets(PostWidgets *w) { if (is_popped_up(w->shell)) XtPopdown(w->shell); XtDestroyWidget(w->shell); } static void create_post_widgets(PostContext *context) { PostWidgets *w; Widget layout, hbar, vbar, mgr; Arg args[8]; w = (PostWidgets *)XtMalloc(sizeof *w); context->widgets = w; w->file_sel = NULL; XtSetArg(args[0], XtNcolormap, global.cmap); XtSetArg(args[1], XtNvisual, global.visual); XtSetArg(args[2], XtNdepth, global.depth); XtSetArg(args[3], XtNinput, True); w->shell = XtCreatePopupShell("postpopup", topLevelShellWidgetClass, main_widgets.shell, args, 4); layout = XtVaCreateManagedWidget("postlayout", layoutWidgetClass, w->shell, XtVaTypedArg, XtNlayout, XtRString, #include "layouts/post.h" (int)sizeof(String), (void *)0); XtCreateManagedWidget("posttitle", messageWidgetClass, layout, NULL, 0); mgr = XtCreateManagedWidget("posttextmgr", managerWidgetClass, layout, NULL, 0); XtSetArg(args[0], XtNvertical, False); hbar = XtCreateManagedWidget("texthbar", scrBarWidgetClass, layout, args, 1); vbar = XtCreateManagedWidget("textvbar", scrBarWidgetClass, layout, NULL, 0); XtSetArg(args[0], XtNvBar, vbar); XtSetArg(args[1], XtNhBar, hbar); w->text = XtCreateManagedWidget("posttext", artTextWidgetClass, mgr, args, 2); XtSetArg(args[0], XtNbuffer, ""); XtSetArg(args[1], XtNcenter, False); w->message = XtCreateManagedWidget("postmessage", messageWidgetClass, layout, args, 2); w->post_knapp = XtCreateManagedWidget("post", knappWidgetClass, layout, NULL, 0); XtAddCallback(w->post_knapp, XtNcallback, post_knapp_callback, (XtPointer)context); w->edit_knapp = XtCreateManagedWidget("edit", knappWidgetClass, layout, NULL, 0); XtAddCallback(w->edit_knapp, XtNcallback, edit_knapp_callback, (XtPointer)context); w->attach_knapp = XtCreateManagedWidget("attach", knappWidgetClass, layout, NULL, 0); XtAddCallback(w->attach_knapp, XtNcallback, attach_knapp_callback, (XtPointer)context); w->detach_knapp = XtCreateManagedWidget("detach", knappWidgetClass, layout, NULL, 0); XtAddCallback(w->detach_knapp, XtNcallback, detach_knapp_callback, (XtPointer)context); w->cancel_knapp = XtCreateManagedWidget("cancel", knappWidgetClass, layout, NULL, 0); XtAddCallback(w->cancel_knapp, XtNcallback, cancel_knapp_callback, (XtPointer)context); XtSetArg(args[0], XtNmenuName, "miscshell"); w->misc_knapp = XtCreateManagedWidget("misc", menuKnappWidgetClass, layout, args, 1); XtCreateManagedWidget("attachtitle", messageWidgetClass, layout, NULL, 0); w->list_manager = XtCreateManagedWidget("manager", managerWidgetClass, layout, NULL, 0); vbar = XtCreateManagedWidget("listvbar", scrBarWidgetClass, layout, NULL, 0); XtSetArg(args[0], XtNvertical, False); hbar = XtCreateManagedWidget("listhbar", scrBarWidgetClass, layout, args, 1); XtSetArg(args[0], XtNatMostOne, True); XtSetArg(args[1], XtNallowDnd, False); XtSetArg(args[2], XtNcontainHoriz, False); XtSetArg(args[3], XtNcontainVert, True); XtSetArg(args[4], XtNhBar, hbar); XtSetArg(args[5], XtNvBar, vbar); w->list = XtCreateManagedWidget("attachlist", scrListWidgetClass, w->list_manager, args, 6); XtAddCallback(w->list, XtNselectCallback, attach_list_callback, (XtPointer)context); XtCreateManagedWidget("typetitle", messageWidgetClass, layout, NULL, 0); XtCreateManagedWidget("nametitle", messageWidgetClass, layout, NULL, 0); XtCreateManagedWidget("descrtitle", messageWidgetClass, layout, NULL, 0); XtCreateManagedWidget("disptitle", messageWidgetClass, layout, NULL, 0); XtCreateManagedWidget("enctitle", messageWidgetClass, layout, NULL, 0); XtSetArg(args[0], XtNfocusRoot, w->shell); XtSetArg(args[1], XtNsingleLine, True); w->type_field = XtCreateManagedWidget("typefield", textFieldWidgetClass, layout, args, 2); XtAddCallback(w->type_field, XtNcallback, type_field_callback, (XtPointer)context); XtAddCallback(w->type_field, XtNtabCallback, tab_callback, (XtPointer)context); w->name_field = XtCreateManagedWidget("namefield", textFieldWidgetClass, layout, args, 2); XtAddCallback(w->name_field, XtNcallback, name_field_callback, (XtPointer)context); XtAddCallback(w->name_field, XtNtabCallback, tab_callback, (XtPointer)context); w->descr_field = XtCreateManagedWidget("descrfield", textFieldWidgetClass, layout, args, 2); XtAddCallback(w->descr_field, XtNcallback, descr_field_callback, (XtPointer)context); XtAddCallback(w->descr_field, XtNtabCallback, tab_callback, (XtPointer)context); w->inline_toggle = XtCreateManagedWidget("inlinetoggle", toggleWidgetClass, layout, NULL, 0); XtAddCallback(w->inline_toggle, XtNcallback, disp_toggle_callback, (XtPointer)context); w->attach_toggle = XtCreateManagedWidget("attachtoggle", toggleWidgetClass, layout, NULL, 0); XtAddCallback(w->attach_toggle, XtNcallback, disp_toggle_callback, (XtPointer)context); w->none_toggle = XtCreateManagedWidget("nonetoggle", toggleWidgetClass, layout, NULL, 0); XtAddCallback(w->none_toggle, XtNcallback, enc_toggle_callback, (XtPointer)context); w->base64_toggle = XtCreateManagedWidget("base64toggle", toggleWidgetClass, layout, NULL, 0); XtAddCallback(w->base64_toggle, XtNcallback, enc_toggle_callback, (XtPointer)context); w->uue_toggle = XtCreateManagedWidget("uuetoggle", toggleWidgetClass, layout, NULL, 0); XtAddCallback(w->uue_toggle, XtNcallback, enc_toggle_callback, (XtPointer)context); w->qp_toggle = XtCreateManagedWidget("qptoggle", toggleWidgetClass, layout, NULL, 0); XtAddCallback(w->qp_toggle, XtNcallback, enc_toggle_callback, (XtPointer)context); XtSetArg(args[0], XtNmenuName, "typeshell"); w->type_knapp = XtCreateManagedWidget("type", menuKnappWidgetClass, layout, args, 1); create_simple_menu(w->type_knapp, "type", global.type_menu_size, type_field_callback, context); XtCreateManagedWidget("sash1", sashWidgetClass, layout, NULL, 0); XtCreateManagedWidget("sash2", sashWidgetClass, layout, NULL, 0); XtCreateManagedWidget("sash3", sashWidgetClass, layout, NULL, 0); XtCreateManagedWidget("sash4", sashWidgetClass, layout, NULL, 0); create_simple_menu(w->misc_knapp, "misc", global.post_misc_menu_size, misc_menu_callback, context); XtRealizeWidget(w->shell); add_WM_DELETE_WINDOW_callback(w->shell, cancel_knapp_callback, (XtPointer)context); } static void post_set_busy(PostContext *context) { PostWidgets *w = context->widgets; if (context->busy) { fputs("knews: internal error: post context already busy.\n", stderr); return; } context->busy = True; if (!w) return; XDefineCursor(display, XtWindow(w->shell), global.busy_cursor); KnappSetActive(w->post_knapp, False); KnappSetActive(w->edit_knapp, False); KnappSetActive(w->misc_knapp, False); KnappSetActive(w->attach_knapp, False); KnappSetActive(w->detach_knapp, False); KnappSetActive(w->cancel_knapp, False); KnappSetActive(w->type_knapp, False); TextFieldSetActive(w->type_field, False); TextFieldSetActive(w->name_field, False); TextFieldSetActive(w->descr_field, False); XtSetSensitive(w->type_field, False); XtSetSensitive(w->name_field, False); XtSetSensitive(w->descr_field, False); ScrListSetActive(w->list, False); } static void post_unset_busy(PostContext *context) { PostWidgets *w = context->widgets; if (!context->busy) { fputs("knews: internal error: post context not busy.\n", stderr); return; } context->busy = False; if (!w) return; XDefineCursor(display, XtWindow(w->shell), global.cursor); KnappSetActive(w->post_knapp, True); KnappSetActive(w->edit_knapp, True); KnappSetActive(w->misc_knapp, True); KnappSetActive(w->attach_knapp, True); KnappSetActive(w->detach_knapp, True); KnappSetActive(w->cancel_knapp, True); KnappSetActive(w->type_knapp, True); TextFieldSetActive(w->type_field, True); TextFieldSetActive(w->name_field, True); TextFieldSetActive(w->descr_field, True); XtSetSensitive(w->type_field, True); XtSetSensitive(w->name_field, True); XtSetSensitive(w->descr_field, True); ScrListSetActive(w->list, True); } static void set_ready_message(PostContext*, int); static void post_set_message(PostContext *context, char *message, int beep) { if (message[0] == '\0') set_ready_message(context, beep); else MessageSetAndRedraw(context->widgets->message, message, beep && global.bell); } static void set_post_knapp_label(PostContext *context) { int label; if (!(context->flags & POST) || (context->flags & POST_DONE)) label = 1; else if (!(context->flags & MAIL) || (context->flags & MAIL_DONE)) label = 0; else label = 2; KnappSetLabelNo(context->widgets->post_knapp, label, context->flags & OK_TO_POST); } static void set_ready_message(PostContext *context, int beep) { char *msg; if (!(context->flags & OK_TO_POST)) if (beep) msg = "Error in article/mail!"; else msg = "Error in article/mail..."; else { beep = False; if (!(context->flags & POST) || (context->flags & POST_DONE)) msg = "Ready to mail."; else if (!(context->flags & MAIL) || (context->flags & MAIL_DONE)) msg = "Ready to post."; else msg = "Ready to post and mail."; } post_set_message(context, msg, beep); } static void set_enc_toggles(PostWidgets *w, PostAttachment *pa) { int enc = attach_get_enc(pa); ToggleSet(w->none_toggle, enc == MimeEncNone); ToggleSet(w->base64_toggle, enc == MimeEncBase64); ToggleSet(w->qp_toggle, enc == MimeEncQP); ToggleSet(w->uue_toggle, enc == MimeEncUue); } static void set_disp_toggles(PostWidgets *w, PostAttachment *pa) { int is_inline, is_attach; is_inline = pa ? attach_is_inline(pa) : False; is_attach = pa ? !is_inline : False; ToggleSet(w->inline_toggle, is_inline); ToggleSet(w->attach_toggle, is_attach); } static void set_attach_widgets(PostWidgets *w, PostAttachment *pa) { set_enc_toggles(w, pa); set_disp_toggles(w, pa); TextFieldSetBuffer(w->type_field, attach_get_type(pa)); TextFieldSetBuffer(w->name_field, attach_get_name(pa)); TextFieldSetBuffer(w->descr_field, attach_get_descr(pa)); TextFieldSetActive(w->type_field, pa != NULL); TextFieldSetActive(w->name_field, pa != NULL); TextFieldSetActive(w->descr_field, pa != NULL); if (!pa) XtSetKeyboardFocus(w->shell, NULL); XtSetSensitive(w->type_field, pa != NULL); XtSetSensitive(w->name_field, pa != NULL); XtSetSensitive(w->descr_field, pa != NULL); if (pa) XtSetKeyboardFocus(w->shell, w->type_field); KnappSetSensitive(w->detach_knapp, pa != NULL); KnappSetSensitive(w->type_knapp, pa != NULL); } static void do_art_check(PostContext *context) { PostWidgets *w = context->widgets; char *art = NULL; int fd; context->flags &= ~OK_TO_POST; if (context->art) { free((char *)context->art); /* :-( */ context->art = NULL; } fd = open(context->file_name, O_RDONLY); if (fd < 0) perror(context->file_name); else { art = snarf_file(fd, NULL); close(fd); } context->art = art; ArtTextClearLines(context->widgets->text); if (!art) ArtTextAddLine(context->widgets->text, "Couldn't open file!", NULL, global.alert_pixel); else check_article_to_post(context, w->text); set_post_knapp_label(context); set_ready_message(context, True); if (!is_popped_up(context->widgets->shell)) popup_under_pointer(context->widgets->shell, XtGrabNone); } /*************************************************************************/ static void post_knapp_callback(Widget w, XtPointer client_data, XtPointer call_data) { PostContext *context = (PostContext *)client_data; char *agent = res_posting_agent(); FILE *fp = NULL; int ok = True; int do_post, do_mail; do_post = (context->flags & POST) && !(context->flags & POST_DONE); do_mail = (context->flags & MAIL) && !(context->flags & MAIL_DONE); if (global.busy || context->busy || !(context->flags & OK_TO_POST) || (!do_post && !do_mail)) { XBell(display, 0); return; } if (global.mode == NewsModeDisconnected && do_post && !agent) { post_set_message(context, "Not connected!", True); return; } fp = dump_art_to_file(context); if (!fp) { post_set_message(context, "Couldn't create temp file!", True); return; } post_set_busy(context); if (do_post) { post_set_message(context, "Posting article...", False); ok = agent ? post_to_agent(agent, fp) : post_article(fp); if (ok) context->flags |= POST_DONE; else { post_set_message(context, "Posting failed!", False); set_message("Posting failed!", True); } } if (ok && do_mail) { post_set_message(context, "Mailing article...", False); ok = post_to_agent(MAIL_COMMAND, fp); if (ok) context->flags |= MAIL_DONE; else { char *msg; if (do_post) msg = "Article posted, but mailing failed!"; else msg = "Mailing failed!"; post_set_message(context, msg, False); set_message(msg, True); } } post_unset_busy(context); fclose(fp); if (!ok) return; if (do_mail && do_post) set_message("Post and mail were successful.", False); else if (do_post) set_message("Post was successful.", False); else set_message("Mail was successful.", False); free_post_context(context); } static void edit_knapp_callback(Widget w, XtPointer client_data, XtPointer call_data) { PostContext *context = (PostContext *)client_data; if (context->busy) { XBell(display, 0); return; } post_set_message(context, "Started editor...", False); post_set_busy(context); fork_editor(context); } static void file_sel_callback(Widget gw, XtPointer client_data, XtPointer call_data) { PostContext *context = (PostContext *)client_data; PostWidgets *w = context->widgets; char *file_name = (char *)call_data; PostAttachment *pa; char buffer[256]; if (!context->busy) { XBell(display, 0); return; } XtPopdown(w->file_sel); post_unset_busy(context); if (!file_name) { set_ready_message(context, False); return; } pa = create_attachment(file_name, buffer); post_set_message(context, buffer, !pa); if (pa) { int n = ++context->n_attachments; context->attachments = (PostAttachment **)XtRealloc((char *)context->attachments, n * sizeof context->attachments[0]); context->attachments[n - 1] = pa; print_attach_info(pa, buffer); ScrListAddLine(w->list, buffer, None); Remanage(w->list_manager); ScrListSetSelected(w->list, n - 1, True); ScrListMakeVisible(w->list, n - 1); set_attach_widgets(w, pa); } } static void attach_knapp_callback(Widget gw, XtPointer client_data, XtPointer call_data) { PostContext *context = (PostContext *)client_data; PostWidgets *w = context->widgets; if (context->busy) { XBell(display, 0); return; } post_set_message(context, "Choose a file...", False); post_set_busy(context); if (!w->file_sel) { Arg args[4]; XtSetArg(args[0], XtNcolormap, global.cmap); XtSetArg(args[1], XtNvisual, global.visual); XtSetArg(args[2], XtNdepth, global.depth); w->file_sel = XtCreatePopupShell("filesel", fileSelWidgetClass, w->shell, args, 3); XtAddCallback(w->file_sel, XtNcallback, file_sel_callback, (XtPointer)context); XtRealizeWidget(w->file_sel); } popup_under_pointer(w->file_sel, XtGrabNone); } static void detach_knapp_callback(Widget gw, XtPointer client_data, XtPointer call_data) { PostContext *context = (PostContext *)client_data; PostWidgets *w = context->widgets; PostAttachment *pa; long row = ScrListGetFirstSelected(w->list); long n = context->n_attachments; if (context->busy || row < 0 || row > n) { XBell(display, 0); return; } pa = context->attachments[row]; free_attachment(pa); ScrListDeleteLine(w->list, row); Remanage(w->list_manager); if (--context->n_attachments <= 0) { XtFree((char *)context->attachments); context->attachments = NULL; } else { if (row < n + 1) memmove(context->attachments + row, context->attachments + row + 1, (n - row - 1) * sizeof context->attachments[0]); } post_set_message(context, "Attachment removed.", False); set_attach_widgets(w, NULL); } static void cancel_knapp_callback(Widget w, XtPointer client_data, XtPointer call_data) { PostContext *context = (PostContext *)client_data; if (context->busy) { XBell(display, 0); return; } set_message("Post/Mail aborted.", True); free_post_context(context); } static void attach_list_callback(Widget w, XtPointer client_data, XtPointer call_data) { PostContext *context = (PostContext *)client_data; long row = (long)call_data; PostAttachment *pa; if (context->busy) return; if (row >= 0 && row < context->n_attachments && ScrListGetSelected(w, row)) pa = context->attachments[row]; else pa = NULL; set_attach_widgets(context->widgets, pa); set_ready_message(context, False); } static void tab_callback(Widget field, XtPointer client_data, XtPointer call_data) { PostContext *context = (PostContext *)client_data; PostWidgets *w = context->widgets; Widget new = NULL; char *buffer = NULL; PostAttachment *pa; long row; row = ScrListGetFirstSelected(w->list); if (row < 0 || row >= context->n_attachments) pa = NULL; else pa = context->attachments[row]; if (field == w->type_field) { new = w->descr_field; buffer = attach_get_type(pa); } else if (field == w->descr_field) { new = w->name_field; buffer = attach_get_descr(pa); } else if (field == w->name_field) { new = w->type_field; buffer = attach_get_name(pa); } if (new) XtSetKeyboardFocus(w->shell, new); if (buffer) TextFieldSetBuffer(field, buffer); } static void type_field_callback(Widget gw, XtPointer client_data, XtPointer call_data) { PostContext *context = (PostContext *)client_data; char *buffer = (char *)call_data; PostWidgets *w = context->widgets; int sel = ScrListGetFirstSelected(w->list); PostAttachment *pa; char msg[256]; int ok; if (context->busy || sel < 0) return; pa = context->attachments[sel]; ok = attach_set_type(pa, buffer, msg); post_set_message(context, msg, !ok); if (ok) { char *tmp; print_attach_info(pa, msg); ScrListSetLine(w->list, sel, msg, None); tmp = attach_get_type(pa); TextFieldSetBuffer(w->type_field, tmp); XtSetKeyboardFocus(w->shell, w->descr_field); } } static void descr_field_callback(Widget gw, XtPointer client_data, XtPointer call_data) { PostContext *context = (PostContext *)client_data; char *buffer = (char *)call_data; PostWidgets *w = context->widgets; int sel = ScrListGetFirstSelected(w->list); PostAttachment *pa; char msg[256]; int ok; if (context->busy || sel < 0) return; pa = context->attachments[sel]; ok = attach_set_descr(pa, buffer, msg); post_set_message(context, msg, !ok); if (ok) { char *tmp; print_attach_info(pa, msg); ScrListSetLine(w->list, sel, msg, None); tmp = attach_get_descr(pa); TextFieldSetBuffer(w->descr_field, tmp); XtSetKeyboardFocus(w->shell, w->name_field); } } static void name_field_callback(Widget gw, XtPointer client_data, XtPointer call_data) { PostContext *context = (PostContext *)client_data; char *buffer = (char *)call_data; PostWidgets *w = context->widgets; int sel = ScrListGetFirstSelected(w->list); PostAttachment *pa; char msg[256]; int ok; if (context->busy || sel < 0) return; pa = context->attachments[sel]; ok = attach_set_name(pa, buffer, msg); post_set_message(context, msg, !ok); if (ok) { char *tmp; print_attach_info(pa, msg); ScrListSetLine(w->list, sel, msg, None); tmp = attach_get_name(pa); TextFieldSetBuffer(w->name_field, tmp); XtSetKeyboardFocus(w->shell, w->type_field); } } static void disp_toggle_callback(Widget gw, XtPointer client_data, XtPointer call_data) { PostContext *context = (PostContext *)client_data; PostWidgets *w = context->widgets; int sel = ScrListGetFirstSelected(w->list); PostAttachment *pa; char msg[256]; int is_inline = gw == w->inline_toggle; int ok; if (context->busy || sel < 0) return; pa = context->attachments[sel]; ok = attach_set_inline(pa, is_inline, msg); post_set_message(context, msg, !ok); if (ok) { print_attach_info(pa, msg); ScrListSetLine(w->list, sel, msg, None); } set_disp_toggles(w, pa); } static void enc_toggle_callback(Widget gw, XtPointer client_data, XtPointer call_data) { PostContext *context = (PostContext *)client_data; PostWidgets *w = context->widgets; int sel = ScrListGetFirstSelected(w->list); PostAttachment *pa; char msg[256]; int ok, enc; if (context->busy || sel < 0) return; if (gw == w->none_toggle) enc = MimeEncNone; else if (gw == w->base64_toggle) enc = MimeEncBase64; else if (gw == w->uue_toggle) enc = MimeEncUue; else if (gw == w->qp_toggle) enc = MimeEncQP; else { XBell(display, 0); return; } pa = context->attachments[sel]; ok = attach_set_enc(pa, enc, msg); post_set_message(context, msg, !ok); if (ok) { print_attach_info(pa, msg); ScrListSetLine(w->list, sel, msg, None); } set_enc_toggles(w, pa); } /*********************************************************************/ static void edit_exit_callback(void *client_data, int status, char *stderr_buf) { PostContext *context = (PostContext *)client_data; char message[128]; int ok = False; if (WIFEXITED(status)) switch (WEXITSTATUS(status)) { case 0: ok = True; break; case 127: strcpy(message, "Failed to start editor!"); break; default: strcpy(message, "Editor exited abnormally!"); break; } else if (WIFSIGNALED(status)) sprintf(message, "Editor caught %s!", signal_string(WTERMSIG(status))); else strcpy(message, "Unknown problem with editor!"); post_unset_busy(context); if (!ok) { set_message(message, True); stderr_popup(stderr_buf, 0); if (!context->widgets) { free_post_context(context); return; } } if (!context->widgets) { create_post_widgets(context); set_attach_widgets(context->widgets, NULL); } do_art_check(context); if (!ok) post_set_message(context, message, True); } void fork_editor(PostContext *context) { pid_t pid; pid = fork_nicely(context, edit_exit_callback, True); if (pid < 0) { /* fork failed */ popup_notice("notice", "Failed to start editor:\n\nfork failed", NULL, NULL, NULL, 5000, NULL, NULL, XtGrabNone); return; } if (pid == 0) { /* child */ char editcmd[512]; char *p1, *p2; p1 = strstr(global.edit_command, "%s"); p2 = strstr(global.edit_command, "%i"); if (p1) if (p2) if (p1 < p2) sprintf(editcmd, global.edit_command, context->file_name, context->line); else sprintf(editcmd, global.edit_command, context->line, context->file_name); else sprintf(editcmd, global.edit_command, context->file_name); else if (p2) sprintf(editcmd, global.edit_command, context->line); else sprintf(editcmd, global.edit_command); execl(BIN_SH, "sh", "-c", editcmd, (char *)NULL); perror("knews: execl " BIN_SH); _exit(127); } /* * Parent */ if (global.mode == NewsModeDisconnected) set_message("Editor started. Warning: not connected.", False); else if (global.posting_allowed || !(context->flags & POST)) set_message("Editor started.", False); else set_message("Editor started. Warning: this server " "doesn't allow posting.", False); } /*********************************************************************/ typedef struct { PostContext *context; char *command; int is_ro; } CmdContext; static void command_exit_callback(void *client_data, int status, char *stderr_buf) { CmdContext *cmd = client_data; PostContext *context = cmd->context; char *command = cmd->command; int is_ro = cmd->is_ro; char message[256]; XtFree((char *)cmd); cmd = NULL; post_unset_busy(context); status = WIFEXITED(status) ? WEXITSTATUS(status) : 1; if (strlen(command) > 200) command[200] = '\0'; if (status == 0) { sprintf(message, "'%s' exited OK.", command); popup_title_notice(NULL, message, False); } else { sprintf(message, "'%s' exited abnormally", command); popup_title_notice(NULL, message, True); stderr_popup(stderr_buf, 0); } if (is_ro) set_ready_message(context, False); else do_art_check(context); XtFree(command); } static void misc_menu_callback(Widget w, XtPointer client_data, XtPointer call_data) { PostContext *context = (PostContext *)client_data; char *command = StringGadgetCommand(w); char *expn[3]; CmdContext *cmd; pid_t pid; int is_ro; if (context->busy) { XBell(display, 0); return; } if (!command) { post_set_message(context, "No command associated with that " "menu entry!", True); return; } is_ro = !strstr(command, "%s"); expn[0] = context->file_name; expn[1] = context->q_str; expn[2] = context->qq_str; command = expn_tmpl(command, 3, "sqQ", expn); cmd = (CmdContext *)XtMalloc(sizeof *cmd); cmd->context = context; cmd->command = command; cmd->is_ro = is_ro; pid = fork_nicely(cmd, command_exit_callback, True); if (pid < 0) { post_set_message(context, "Fork failed!", True); XtFree(command); XtFree((char *)cmd); return; } if (pid == 0) { /* child */ if (is_ro) { int fd; fd = open(context->file_name, O_RDONLY); if (fd < 0) { perror(context->file_name); _exit(126); } if (fd != STDIN_FILENO) { if (dup2(fd, STDIN_FILENO) != STDIN_FILENO) { perror("dup2"); _exit(127); } close(fd); } } execl(BIN_SH, "sh", "-c", command, (char *)NULL); perror("knews: execl " BIN_SH); _exit(127); } /* * Parent. */ post_set_message(context, "Starting command...", False); post_set_busy(context); } ./knews-1.0b.1/src/Imakefile100644 1244 1244 3251 6625556331 14232 0ustar kallekalle#include "../knews.tmpl" #include "../configure.h" #undef DOMAIN_NAME #undef MAIL_COMMAND #undef EDIT_COMMAND XMU_LIB = XPM_LIB = REGEXP_LIB = REGEXP_INCLUDES = #if !HAVE_POSIX_REGEXPS REGEXP_INCLUDES = -I../regexp REGEXP_LIB = -L../regexp -lregexp #endif #if HAVE_XMU XMU_LIB = -lXmu #endif #if HAVE_XPM XPM_LIB = -lXpm #endif #if !HAVE_JPEG JPEG_LIB = #endif #if !HAVE_PNG PNG_LIB = #endif #if !HAVE_COMPFACE COMPFACE_LIB = #endif INCLUDES = -I../Widgets $(REGEXP_INCLUDES) $(KNEWS_INCLUDES) LOCAL_LIBRARIES = \ -L../Widgets -lWidgets $(REGEXP_LIB) $(JPEG_LIB) $(PNG_LIB) \ $(COMPFACE_LIB) $(XMU_LIB) $(XPM_LIB) $(XTOOLLIB) $(XLIB) DEPLIBS = $(DEPXLIBS) ../Widgets/libWidgets.a SRCS = \ actions.c ahead.c bg.c cache.c charset.c child.c color.c \ connect.c decode.c domain.c expand.c file.c font.c ftp.c \ gif.c jpeg.c k_action.c k_edit.c k_file.c k_kill.c k_node.c \ mailcap.c main.c misc.c newsrc.c p_attach.c p_check.c p_menu.c \ p_popup.c p_post.c p_setup.c parse.c partial.c png.c procs.c \ read.c resource.c save.c search.c server.c sort.c sysdeps.c \ tag.c thread.c util.c uudecode.c viewer.c widgets.c xface.c xutil.c OBJS = \ actions.o ahead.o bg.o cache.o charset.o child.o color.o \ connect.o decode.o domain.o expand.o file.o font.o ftp.o \ gif.o jpeg.o k_action.o k_edit.o k_file.o k_kill.o k_node.o \ mailcap.o main.o misc.o newsrc.o p_attach.o p_check.o p_menu.o \ p_popup.o p_post.o p_setup.o parse.o partial.o png.o procs.o \ read.o resource.o save.o search.o server.o sort.o sysdeps.o \ tag.o thread.o util.o uudecode.o viewer.o widgets.o xface.o xutil.o ComplexProgramTarget(knews) InstallAppDefaults(Knews) ./knews-1.0b.1/src/sysdeps.c100644 1244 1244 27175 6642131434 14302 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include "../configure.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "server.h" #include "sysdeps.h" #undef PURIFY_HACK /*#define PURIFY_HACK*/ #ifndef USE_POLL # if defined(SYSV) && !defined(__hpux) # define USE_POLL 1 # else # define USE_POLL 0 # endif #endif #if USE_POLL # include #endif /* * Some kind of weird hack... */ #ifdef __hpux # define SELECT_HACK #endif /* * Some systems don't support Posix style non-blocking I/O * on sockets; we need to use the FIONBIO ioctl instead. */ #ifndef USE_FIONBIO # if defined(sco) && sco # define USE_FIONBIO 1 # else # define USE_FIONBIO 0 # endif #endif #if USE_FIONBIO # include #endif static long set_nonblock(int fd) { #if !USE_FIONBIO long flags; flags = fcntl(fd, F_GETFL); if (flags >= 0) flags = fcntl(fd, F_SETFL, flags | O_NONBLOCK); if (flags < 0) perror("knews: fcntl"); return flags; #else int yes = 1; yes = ioctl(fd, FIONBIO, &yes); if (yes < 0) perror("knews: ioctl"); return yes; #endif } /* * FreeBSD doesn't define EINPROGRESS #if _POSIX_SOURCE... */ int would_block(int fd, int err_no) { return ( #ifdef EWOULDBLOCK err_no == EWOULDBLOCK || #endif #ifdef EINPROGRESS err_no == EINPROGRESS || #endif err_no == EAGAIN); } int timed_out(int err_no) { #ifdef ETIMEDOUT return err_no == ETIMEDOUT; #else return False; #endif } char *error_string(int err_no) { switch (err_no) { #ifdef ECONNREFUSED case ECONNREFUSED: return "Connection refused"; #endif #ifdef ENETUNREACH case ENETUNREACH: return "Network unreachable"; #endif #ifdef ETIMEDOUT case ETIMEDOUT: return "Connection timed out"; #endif #ifdef EADDRNOTAVAIL case EADDRNOTAVAIL: return "Address not available"; #endif #ifdef EHOSTUNREACH case EHOSTUNREACH: return "Host is unreachable"; #endif default: break; } return NULL; } /********************************************************************/ extern XtAppContext app_cont; extern Display *display; struct wait_data { int fd; QuitFunc func; void *data; }; static struct wait_data *wait_data; #define MASK (XtIMXEvent | XtIMTimer | XtIMAlternateInput) int do_wait(int *fd, int rd, void (*quit_func)(void*), void *data) { struct wait_data wd; int tmp; int disp_fd = ConnectionNumber(display); #if USE_POLL struct pollfd fds[2]; #else fd_set read_fds; fd_set write_fds; int maxfdp1; maxfdp1 = (*fd > disp_fd ? *fd : disp_fd) + 1; #endif wd.fd = *fd; wd.func = quit_func; wd.data = data; for (;;) { while (XtAppPending(app_cont) & MASK) { wait_data = &wd; XtAppProcessEvent(app_cont, MASK); wait_data = NULL; if (*fd < 0) return -1; } #if USE_POLL fds[0].fd = *fd; fds[0].events = rd ? POLLIN : POLLOUT; fds[0].revents = 0; fds[1].fd = disp_fd; fds[1].events = POLLIN; fds[1].revents = 0; do { tmp = poll(fds, 2, -1); } while (tmp < 0 && errno == EINTR); if (tmp < 0) { perror("knews: poll"); return -1; } if (fds[0].revents) break; if (fds[1].revents) { wait_data = &wd; XtAppProcessEvent(app_cont, MASK); wait_data = NULL; if (*fd < 0) return -1; } #else FD_ZERO(&read_fds); FD_ZERO(&write_fds); FD_SET(disp_fd, &read_fds); if (rd) FD_SET(*fd, &read_fds); else FD_SET(*fd, &write_fds); do { tmp = select(maxfdp1, #ifdef SELECT_HACK /* don't ask */ (int *)&read_fds, rd ? NULL : (int *)&write_fds, #else &read_fds, rd ? NULL : &write_fds, #endif NULL, NULL); } while (tmp < 0 && errno == EINTR); if (tmp < 0) { perror("knews: select"); return -1; } if (FD_ISSET(*fd, &read_fds) || FD_ISSET(*fd, &write_fds)) break; if (FD_ISSET(disp_fd, &read_fds)) { wait_data = &wd; XtAppProcessEvent(app_cont, MASK); wait_data = NULL; if (*fd < 0) return -1; } #endif } return 0; } void abort_callback(Widget w, XtPointer client_data, XtPointer call_data) { if (!wait_data || wait_data->fd < 0 || !wait_data->func) return; wait_data->func(wait_data->data); wait_data = NULL; } /***********************************************************************/ struct SERV_ADDR { struct in_addr addr; unsigned short port; }; /* * host: either "hostname:port" or "ip.ip.ip.ip:port", * where port is optional. * def_port: port to use is 'host' doesn't have one. * byte_swap: whether to apply htons() on def_port. * * Returns malloced memory: caller must free. */ SERV_ADDR *get_host(char *host, unsigned short def_port, int byte_swap) { SERV_ADDR *ret; struct in_addr addr; unsigned short port; char *c; #ifdef PURIFY_HACK static unsigned char news[] = {130, 237, 72, 211}; ret = (SERV_ADDR *)XtMalloc(sizeof *ret); ret->port = htons(119); memcpy(&ret->addr, news, 4); return ret; #endif port = 0; c = strchr(host, ':'); if (c) { *c = '\0'; if (c[1] >= '0' && c[1] <= '9') port = atoi(c + 1); } if (port != 0) port = htons(port); else if (byte_swap) port = htons(def_port); else port = def_port; if ((unsigned int)-1 == 0xffffu) *(volatile char *)0; addr.s_addr = inet_addr(host); if (addr.s_addr == -1) { struct hostent *hp; #ifdef h_addr # define ADDR(hp) ((hp)->h_addr) #else # define ADDR(hp) ((hp)->h_addr_list[0]) #endif hp = gethostbyname(host); if (!hp || !ADDR(hp)) { if (c) *c = ':'; return NULL; } memcpy(&addr, ADDR(hp), hp->h_length); #undef ADDR } if (c) *c = ':'; ret = (SERV_ADDR *)XtMalloc(sizeof *ret); memset(ret, 0, sizeof *ret); ret->addr = addr; ret->port = port; return ret; } int open_socket(void) { int fd, tmp; do { fd = socket(PF_INET, SOCK_STREAM, 0); } while (fd < 0 && errno == EINTR); if (fd < 0) { perror("knews: socket"); return -1; } if (set_nonblock(fd) < 0) { close(fd); return -1; } if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0) perror("fcntl"); do { int a = 1; tmp = setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *)&a, sizeof a); } while (tmp < 0 && errno == EINTR); if (tmp < 0) { perror("knews: setsockopt"); close(fd); return -1; } return fd; } int connect_socket(int fd, SERV_ADDR *addr) { struct sockaddr_in serv_addr; int tmp; if (!addr) { fputs("knews: connect_socket: addr is NULL!!!\n", stderr); return -1; } memset(&serv_addr, 0, sizeof serv_addr); serv_addr.sin_family = AF_INET; serv_addr.sin_port = addr->port; serv_addr.sin_addr = addr->addr; do { tmp = connect(fd, (struct sockaddr *)&serv_addr, sizeof serv_addr); } while (tmp < 0 && errno == EINTR); return tmp; } #if 0 /* Misc stuff for ftp routines */ /* * Creates a socket bound to an arbitrary port, and listens. */ int bind_and_listen(int backlog) { struct sockaddr_in addr; int fd, tmp; fd = open_socket(); if (fd < 0) return -1; memset(&addr, 0, sizeof addr); addr.sin_family = AF_INET; addr.sin_port = 0; addr.sin_addr.s_addr = INADDR_ANY; /* ??? */ do { tmp = bind(fd, &addr, sizeof addr); } while (tmp < 0 && errno == EINTR); if (tmp < 0) { perror("knews: bind"); close(fd); return -1; } do { tmp = listen(fd, backlog); } while (tmp < 0 && errno == EINTR); if (tmp < 0) { perror("knews: listen"); close(fd); return -1; } return fd; } /* * Returns the address of a socket. */ SERV_ADDR *get_sock_name(int fd) { struct sockaddr_in addr; SERV_ADDR *ret; int tmp, len = sizeof addr; do { tmp = getsockname(fd, &addr, &len); } while (tmp < 0 && errno == EINTR); if (tmp < 0) { perror("knews_ getsockname"); return NULL; } ret = (SERV_ADDR *)XtMalloc(sizeof *ret); ret->addr = addr.sin_addr; ret->port = addr.sin_port; return ret; } static void accept_abort(void *data) { int *as = data; if (as && *as >= 0) { close(*as); *as = -1; } } /* * Accepts a connection on a socket returned from bind_and_listen. * Will return -1 with errno == EINTR if user aborts, in which case * the accepting socket has been closed and *as == 1. */ int do_accept(int *as) { struct sockaddr_in addr; int tmp, len; for (;;) { tmp = accept(*as, (struct sockaddr *)&addr, &len); if (tmp >= 0) break; if (errno == EINTR) continue; if (!would_block(*as, errno)) break; if (do_wait(as, True, accept_abort, as) == 0) continue; if (*as < 0) errno = EINTR; break; } return tmp; } /* * Prints an address on the form h1,h2,h3,h4,p1,p2 */ void print_addr_ftp(SERV_ADDR *addr, char *buf) { unsigned long l = addr->addr.s_addr; unsigned short p = addr->port; sprintf(buf, "%lu,%lu,%lu,%lu,%lu,%lu", (l >> 24) & 0xfful, (l >> 16) & 0xfful, (l >> 8) & 0xfful, (l ) & 0xfful, (p >> 8) & 0xfful, (p ) & 0xfful); } #endif int open_duplex(int *fd) { #if !defined(HAVE_SOCKETPAIR) || HAVE_SOCKETPAIR int tmp; do { tmp = socketpair(PF_UNIX, SOCK_STREAM, 0, fd); } while (tmp < 0 && errno == EINTR); if (tmp < 0) { perror("knews: socketpair"); return -1; } if (fcntl(fd[0], F_SETFD, FD_CLOEXEC) < 0) perror("fcntl"); if (set_nonblock(fd[0]) < 0) { close(fd[0]); close(fd[1]); return -1; } return 0; #else fputs("knews: compiled without HAVE_SOCKETPAIR, " "can't open connection!\n", stderr); return -1; #endif } /*************************************************************************/ char *get_mailhostname(void) { struct utsname un = {{0,},}; char *host = NULL, *domain; #ifdef PURIFY_HACK return NULL; #endif if (uname(&un) < 0) { perror("uname"); return NULL; } if (un.nodename[0] == '\0') return NULL; if (strchr(un.nodename, '.')) host = XtNewString(un.nodename); else { struct hostent *hent; char **loop; hent = gethostbyname(un.nodename); if (!hent) return NULL; if (hent->h_name && strchr(hent->h_name, '.')) host = XtNewString(hent->h_name); if (!host && hent->h_aliases) for (loop = hent->h_aliases ; *loop ; loop++) if (strchr(*loop, '.')) { host = XtNewString(*loop); break; } } return host; } /*************************************************************************/ /* * Dirty stinking hack for Sun's C library. */ #if defined(HAVE_MEMMOVE) && !HAVE_MEMMOVE void *memmove(void *dest, const void *src, size_t n) { bcopy(src, dest, n); return dest; } #endif /*************************************************************************/ void sigusr1_handler(int s) { int o_errno = errno; int i; for (i = 0 ; i < 256 ; i++) { struct stat stat_buf; char *type; if (fstat(i, &stat_buf) < 0) { if (errno != EBADF) { char buf[16]; sprintf(buf, "fd %d", i); perror(buf); } continue; } if (S_ISDIR(stat_buf.st_mode)) type = "Directory"; else if (S_ISREG(stat_buf.st_mode)) type = "Regular file"; #ifdef S_ISSOCK else if (S_ISSOCK(stat_buf.st_mode)) type = "Socket"; #endif #ifdef S_ISCHR else if (S_ISCHR(stat_buf.st_mode)) type = "Character special file"; #endif #ifdef S_ISFIFO else if (S_ISFIFO(stat_buf.st_mode)) type = "Pipe"; #endif else type = "Unknown type"; fprintf(stderr, "fd %d: %s\n", i, type); } errno = o_errno; } ./knews-1.0b.1/src/newsrc.c100644 1244 1244 35333 6624756430 14115 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include "global.h" #include "child.h" #include "codes.h" #include "expand.h" #include "file.h" #include "newsrc.h" #include "resource.h" #include "server.h" #include "util.h" #include "widgets.h" #include "xutil.h" #define NO_GROUPS 4096 #define NEW_GROUPS 256 #define MAX_GROUP_LEN 1024 static int comp_group_nodes(const void *a, const void *b) { return strcmp( (*((GROUP **)a))->name, (*((GROUP **)b))->name); } int get_newsgroups(void) { char *buffer; long status; buffer = server_comm(main_server, "LIST\r\n", False); if (!buffer) return -1; sscanf(buffer, "%ld", &status); if (status != NNTP_OK_GROUPS) return status; global.no_groups = 0; while ((buffer = server_read(main_server)) && !IS_DOT(buffer)) { char *c; long first, last; char mod; if (global.no_groups > global.max_groups - 2) { long i = global.max_groups; global.max_groups += NO_GROUPS; global.groups = (GROUP **)XtRealloc((char *)global.groups, global.max_groups * sizeof(GROUP *)); while (i < global.max_groups) global.groups[i++] = NULL; } c = strchr(buffer, ' '); if (c) { GROUP *group; int k; *(c++) = '\0'; k = sscanf(c, "%ld%ld%*c%c", &last, &first, &mod); if (k < 2) continue; if (k == 2) mod = 'y'; if (mod == '=' || mod == 'x') continue; group = (GROUP *)XtMalloc(sizeof(GROUP)); global.groups[global.no_groups++] = group; group->description = NULL; group->read_arts = NULL; group->subscribed = False; group->found_in_newsrc = False; group->ahead_flag = False; group->moderated = (mod == 'm'); group->name = XtNewString(buffer); group->first_art = first; group->last_art = last; } } if (!buffer) { long i; for (i = 0 ; i < global.no_groups ; i++) XtFree(global.groups[i]->name); XtFree((char *)global.groups); global.groups = NULL; global.no_groups = 0; global.max_groups = 0; return -1; } qsort(global.groups, global.no_groups, sizeof(GROUP *), comp_group_nodes); return NNTP_OK_GROUPS; } /* the grouplist must be sorted here */ GROUP *find_group(char *name) { long first = 0; long last = global.no_groups - 1; while (first <= last && global.groups[first]->subscribed) { if (strcmp(name, global.groups[first]->name) == 0) return global.groups[first]; first++; } while (first <= last) { long mid = (first + last) / 2; int comp = strcmp(name, global.groups[mid]->name); if (comp == 0) return global.groups[mid]; if (comp < 0) last = mid - 1; else first = mid + 1; } return NULL; } int check_for_new_groups(void) { GROUP **list = NULL; long l_max = 0; struct tm *tm; char command[64]; char *buffer; long status, n = 0; if (!res_check_for_new_groups() || global.last_time == 0) return True; global.new_groups = NULL; global.no_new_groups = 0; tm = gmtime(&global.last_time); sprintf(command, "NEWGROUPS %02d%02d%02d %02d%02d%02d GMT\r\n", tm->tm_year % 100u, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); buffer = server_comm(main_server, command, False); if (!buffer) return -1; status = atoi(buffer); if (status != NNTP_OK_NEWGROUPS) { fprintf(stderr, "check_for_new_groups failed: Message from server is: %s\n", buffer); return status; } l_max = NEW_GROUPS; list = (GROUP **)XtMalloc(l_max * sizeof(GROUP *)); while ((buffer = server_read(main_server)) && !IS_DOT(buffer)) { char mod; char *c; if (n >= l_max) { l_max += NEW_GROUPS; list = (GROUP **)XtRealloc((char *)list, l_max * sizeof(GROUP *)); } c = strchr(buffer, ' '); if (c) { *c++ = '\0'; if (sscanf(c, "%*d%*d%*c%c", &mod) == 1 && (mod == '=' || mod == 'x')) continue; } list[n] = find_group(buffer); if (!list[n]) list[n] = create_group(buffer); n++; } sort_groups(); if (!buffer) { XtFree((char *)list); return False; } if (n <= 0) XtFree((char *)list); else { global.new_groups = list; global.no_new_groups = n; } return NNTP_OK_NEWGROUPS; } /* this is a weak point */ static void put_subscribed_groups_first(long n) { GROUP **subs; long i, j; if (n == 0) return; subs = (GROUP **)XtMalloc(n * sizeof(GROUP *)); for (i = j = global.no_groups - 1 ; i >= 0 ; i--) { if (global.groups[i]->subscribed) subs[global.groups[i]->disp] = global.groups[i]; else global.groups[j--] = global.groups[i]; } for (i = 0 ; i < n ; i++) global.groups[i] = subs[i]; XtFree((char *)subs); } void sort_groups(void) { long i, n; for (n = 0 ; n < global.no_groups ; n++) if (!global.groups[n]->subscribed) break; for (i = n ; i < global.no_groups ; i++) if (global.groups[i]->subscribed) { GROUP *temp = global.groups[n]; global.groups[n] = global.groups[i]; global.groups[i] = temp; n++; } if (n > 0 && global.sort_groups) qsort(global.groups, n, sizeof global.groups[0], comp_group_nodes); if (n < global.no_groups) qsort(global.groups + n, global.no_groups - n, sizeof global.groups[0], comp_group_nodes); } static int did_rename = False; int update_newsrc(void) { char *newsrc_file = res_newsrc_file(); int fill = res_fill_newsrc_file(); char *path; FILE *fp; long i; int ok; if (!newsrc_file) { fputs("knews: newsrcFile is NULL!!\n", stderr); return False; } path = expand_path(newsrc_file); if (!path) return False; block_sighup(); if (!did_rename) { char *old_newsrc_file = res_old_newsrc_file(); char *opath; opath = old_newsrc_file ? expand_path(old_newsrc_file) : NULL; if (opath) { if (rename(path, opath) < 0) perror("rename"); XtFree(opath); } did_rename = True; } fp = fopen_mkdir(path, "w", True); XtFree(path); if (!fp) { unblock_sighup(); return False; } for (i = 0 ; i < global.no_groups ; i++) if (fill || global.groups[i]->read_arts || global.groups[i]->subscribed) { ART_LIST_NODE *loop = global.groups[i]->read_arts; fprintf(fp, "%s%c", global.groups[i]->name, global.groups[i]->subscribed ? ':' : '!'); if (loop) fputc(' ', fp); while (loop) { fprintf(fp, "%ld", loop->first); if (loop->first != loop->last) fprintf(fp, "-%ld", loop->last); loop = loop->next; if (loop) fputc(',', fp); } fputc('\n', fp); } ok = fclose(fp) == 0; unblock_sighup(); return ok; } int get_descriptions(void) { char *file_name = res_descriptions_file(); FILE *fp = NULL; SERVER *server; char *buffer; long n; if (res_retrieve_descriptions()) { set_message("Retrieving group descriptions...", False); server = main_server; buffer = server_comm(main_server, "LIST NEWSGROUPS\r\n", False); if (!buffer) return -1; sscanf(buffer, "%ld", &n); if (n != NNTP_OK_GROUPS) return n; if (file_name) fp = fopen_expand(file_name, "w", True); server_set_bs(server, fp); } else { int fd; if (!file_name) return 0; fd = open_expand(file_name, O_RDONLY, True); if (fd < 0) return 0; set_message("Reading group descriptions from file...", False); server = server_create(fd); } while ((buffer = server_read(server)) && !IS_DOT(buffer)) { GROUP *group; char *c; c = buffer; while (*c != '\0' && *c != '\t' && *c != ' ') c++; if (*c == '\0') continue; *c++ = '\0'; while (*c == ' ' || *c == '\t') c++; group = find_group(buffer); if (group) { if (group->description) { long len = strlen(group->description); /* if duplicate descriptions, take the longest */ if (len >= strlen(c)) continue; XtFree(group->description); } group->description = XtNewString(c); } } if (fp) { server_set_bs(server, NULL); if (fclose(fp) < 0 || !buffer) unlink_expand(file_name); } if (server != main_server) server_free(server); else if (!buffer) return -1; return NNTP_OK_GROUPS; } GROUP *create_group(char *name) { GROUP *group; global.groups = (GROUP **)XtRealloc((char *)global.groups, (global.no_groups + 1) * sizeof global.groups[0]); group = global.groups[global.no_groups++] = (GROUP *)XtMalloc(sizeof(GROUP)); group->name = XtNewString(name); group->description = NULL; group->no_unread = 0; group->first_art = 0; group->last_art = 0; group->read_arts = NULL; group->disp = -1; group->subscribed = False; group->moderated = False; group->found_in_newsrc = False; group->ahead_flag = False; return group; } static void check_read_arts_sanity(GROUP *temp) { ART_LIST_NODE *loop; for (loop = temp->read_arts ; loop ; loop = loop->next) if (loop->first > loop->last || (loop->next && loop->last > loop->next->first)) { fprintf(stderr, "knews: Bad list of read articles in .newsrc file, " "group %s. Marking all articles unread.\n", temp->name); free_read_arts_list(temp); temp->read_arts = NULL; return; } } static ART_LIST_NODE *parse_read_arts_list(char *list) { ART_LIST_NODE *result = NULL; long first = 0, last = 0; while (IS_SPACE(*list)) list++; if (*list == '\0') return NULL; while (IS_DIGIT(*list)) { first *= 10; first += *list - '0'; list++; } if (*list == ',' || *list == '\0') last = first; else if (*list == '-') { list++; while (IS_DIGIT(*list)) { last *= 10; last += *list - '0'; list++; } } result = (ART_LIST_NODE *)XtMalloc(sizeof *result); result->first = first; result->last = last; result->next = NULL; if (*list == ',') result->next = parse_read_arts_list(list + 1); else if (*list != '\0') fputs("Parse error in newsrc.\n", stderr); return result; } static void create_new_newsrc(char *path) { FILE *fp; int c; fp = fopen_mkdir(path, "w", True); popup_title_notice(fp ? "Created newsrc file" : "Failed to create newsrc file", path, fp == NULL); if (!fp) return; if (global.auto_subscribe) if (global.auto_subscribe[0] != '/') fputs(global.auto_subscribe, fp); else { FILE *ng = fopen(global.auto_subscribe, "r"); if (!ng) perror(global.auto_subscribe); else { while ((c = getc(ng)) != EOF) putc(c, fp); fclose(ng); } } fclose(fp); } void parse_newsrc(int create) { char *new = res_newsrc_file(); char *buffer, *path; SERVER *s; long pos; int fd; did_rename = False; if (!new) { fputs("knews: newsrcFile is NULL!\n", stderr); return; } path = expand_path(new); if (!path) return; fd = open(path, O_RDONLY); if (fd < 0) { perror(path); create_new_newsrc(path); fd = open(path, O_RDONLY); } XtFree(path); if (fd < 0) return; pos = 0; s = server_create(fd); while ((buffer = server_read(s))) { int n = 0, subscribed; GROUP *temp; while (buffer[n] != '\0' && buffer[n] != ':' && buffer[n] != '!') n++; if (buffer[n] == '\0') { fprintf(stderr, "Garbage line in newsrc: %s\n", buffer); continue; } subscribed = buffer[n] == ':'; buffer[n] = '\0'; temp = find_group(buffer); if (!temp && create) temp = create_group(buffer); if (!temp) { fprintf(stderr, "Bogus group in newsrc file: %s\n", buffer); continue; } if (temp->found_in_newsrc) { fprintf(stderr, "Group %s appeared twice in newsrc.\n", buffer); continue; } temp->subscribed = subscribed; temp->found_in_newsrc = True; if (subscribed) temp->disp = pos++; temp->read_arts = parse_read_arts_list(buffer + n + 1); check_read_arts_sanity(temp); } server_free(s); put_subscribed_groups_first(pos); sort_groups(); } int get_newsgroups_from_newsrc(void) { char *tmp; parse_newsrc(True); tmp = rescan(); if (!tmp) return -1; return atoi(tmp); } char *rescan(void) { char message[128]; char *buffer, *p; long n; for (n = 0 ; n < global.no_groups ; n++) if (!global.groups[n]->subscribed) break; if (n == 0) return CODE_TO_STR(NNTP_OK_GROUPS); strcpy(message, "Rescan in progress... "); p = message + strlen(message); if (res_read_active_file()) { long i, j, k, m; buffer = server_comm(main_server, "LIST\r\n", True); if (!buffer || atoi(buffer) != NNTP_OK_GROUPS) return buffer; j = m = global.no_groups / 16; i = 0; while ((buffer = server_read(main_server)) && !IS_DOT(buffer)) { char *c = strchr(buffer, ' '); if (c) { *c++ = '\0'; for (k = 0 ; k < n ; k++) { if (strcmp(buffer, global.groups[k]->name) == 0) { sscanf(c, "%ld%ld", &global.groups[k]->last_art, &global.groups[k]->first_art); break; } } } if (j-- <= 0) { sprintf(p, "%3ld%%", i / global.no_groups); set_message(message, False); j = m; } i += 100; } if (!buffer) return NULL; } else { int list = res_try_list_active(); char command[1024]; long i, j, first, last; char m; for (i = 0, j = 0 ; j < global.no_groups ; j++) { if (!global.groups[j]->subscribed) break; if (strlen(global.groups[j]->name) > 480) { fprintf(stderr, "Group name too long: %s\n", global.groups[j]->name); continue; } if (list) { sprintf(command, "LIST ACTIVE %s\r\n", global.groups[j]->name); server_write(main_server, command); buffer = server_read(main_server); if (!buffer) return NULL; if (atol(buffer) == NNTP_OK_GROUPS) { while ((buffer = server_read(main_server)) && !IS_DOT(buffer)) { char *c = strchr(buffer, ' '); if (c) { *c++ = '\0'; m = 'y'; if (case_strcmp(global.groups[j]->name, buffer) == 0 && sscanf(c, "%ld%ld%*c%c", &last, &first, &m) >= 2 && m != '-' && m != 'x') { global.groups[j]->first_art = first; global.groups[j]->last_art = last; global.groups[j]->moderated = (m == 'm'); } } } if (!buffer) return NULL; } else { #if 0 fprintf(stderr, "knews: 'LIST ACTIVE wildmat' failed, message " "from server is: \n" " %s\n" " Falling back to GROUP\n", buffer); #endif list = False; } } if (!list) { sprintf(command, "GROUP %s\r\n", global.groups[j]->name); server_write(main_server, command); buffer = server_read(main_server); if (!buffer) return NULL; if (atoi(buffer) != NNTP_OK_GROUP) fprintf(stderr, "knews: Bogus group: %s\n", global.groups[j]->name); else sscanf(buffer, "%*d%*d%ld%ld", &global.groups[j]->first_art, &global.groups[j]->last_art); } i += 100; sprintf(p, "%ld%%", i / n); set_message(message, False); } } return CODE_TO_STR(NNTP_OK_GROUPS); } ./knews-1.0b.1/src/file.c100644 1244 1244 10663 6455455543 13535 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include "global.h" #include #include "expand.h" #include "file.h" #if 0 # define FILE_MASK 0666 # define DIR_MASK 0777 #else # define FILE_MASK (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH) # define DIR_MASK ((FILE_MASK)|S_IXUSR|S_IXGRP|S_IXOTH) #endif #ifndef O_ACCMODE # define O_ACCMODE 3 #endif int open_mkdir(char *path, int flags, int report) { struct stat stat_buf; char *c; int fd; if (flags & O_CREAT) fd = open(path, flags, FILE_MASK); else fd = open(path, flags); if (fd >= 0) return fd; if (errno == ENOENT && (flags & O_ACCMODE) != O_RDONLY) { if (path[0] == '/') c = strchr(path + 1, '/'); else c = strchr(path, '/'); while (c) { *c = '\0'; if (stat(path, &stat_buf) < 0 && (errno != ENOENT || mkdir(path, DIR_MASK) < 0)) { *c++ = '/'; return -1; } *c++ = '/'; c = strchr(c, '/'); } } if (flags & O_CREAT) fd = open(path, flags, FILE_MASK); else fd = open(path, flags); if (fd < 0 && (report || errno != ENOENT)) { int oerrno = errno; perror(path); errno = oerrno; } return fd; } int open_expand(char *file_name, int flags, int report) { char *path; int fd; path = expand_path(file_name); if (!path) return -1; fd = open_mkdir(path, flags, report); XtFree(path); return fd; } FILE *fopen_mkdir(char *path, char *mode, int report) { int fd, flags; FILE *ret; switch (*mode) { case 'a': flags = O_WRONLY|O_APPEND|O_CREAT; break; case 'r': flags = O_RDONLY; break; case 'w': flags = O_WRONLY|O_TRUNC|O_CREAT; break; default: fputs("knews: invalid mode to fopen_mkdir.\n", stderr); return NULL; } fd = open_mkdir(path, flags, report); if (fd < 0) return NULL; ret = fdopen(fd, mode); if (!ret) { perror("fdopen"); close(fd); } return ret; } FILE *fopen_expand(char *file_name, char *mode, int report) { char *path; FILE *fp; path = expand_path(file_name); if (!path) return NULL; fp = fopen_mkdir(path, mode, report); XtFree(path); return fp; } int unlink_expand(char *file_name) { char *path; int ret; path = expand_path(file_name); if (!path) return -1; ret = unlink(path); XtFree(path); return ret; } int chdir_mkdir(char *path) { char *c, *p; if (path[0] == '~' && path[1] == '/') path += 2; if (chdir(path) == 0) return 0; if (errno != ENOENT) { perror(path); return -1; } c = path; p = strchr(c, '/'); for (;;) { int tmp; if (p) *p = '\0'; tmp = chdir(c); if (tmp < 0 && errno == ENOENT && mkdir(c, DIR_MASK) == 0) tmp = chdir(c); if (p) *p++ = '/'; if (tmp < 0) { perror(path); return -1; } c = p; if (!c) break; p = strchr(c, '\n'); } return 0; } int create_temp_fd(char **name) { int fd; *name = tmpnam(NULL); if (!*name) fd = -1; else { unlink(*name); fd = open(*name, O_RDWR|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR); if (fd < 0) *name = NULL; } return fd; } FILE *create_temp_file(char **name) { int fd; fd = create_temp_fd(name); if (fd < 0) return NULL; return fdopen(fd, "w+"); } char *snarf_file(int fd, long *lenp) { struct stat stat_buf; char *buffer; long len, pos, n; if (fstat(fd, &stat_buf) < 0) { perror("fstat"); return NULL; } if (!S_ISREG(stat_buf.st_mode)) { fputs("snarf_file: not a regular file!\n", stderr); errno = EINVAL; return NULL; } pos = 0; len = stat_buf.st_size + 256; buffer = malloc(len + 1); if (!buffer) { perror("malloc"); return NULL; } while ((n = read(fd, buffer + pos, len - pos)) != 0) if (n < 0) if (errno == EINTR) continue; else { perror("read"); free(buffer); return NULL; } else { pos += n; if (pos == len) { char *tmp; len *= 2; tmp = realloc(buffer, len + 1); if (!tmp) { perror("realloc"); free(buffer); return NULL; } buffer = tmp; } } buffer[pos] = '\0'; if (lenp) *lenp = pos; return buffer; } int writen(int fd, char *buf, long n) { long i; while (n > 0) switch ((i = write(fd, buf, n))) { case -1: perror("write"); return -1; case 0: fputs("knews: write(..) = 0, weird...\n", stderr); return -1; default: buf += i; n -= i; break; } return 0; } ./knews-1.0b.1/src/widgets.c100644 1244 1244 64041 6570013015 14241 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include "global.h" #include #include "ahead.h" #include "misc.h" #include "newsrc.h" #include "p_menu.h" #include "procs.h" #include "resource.h" #include "save.h" #include "tag.h" #include "thread.h" #include "util.h" #include "widgets.h" #include "xutil.h" #include "../Widgets/ArtText.h" #include "../Widgets/ArtTree.h" #include "../Widgets/Knapp.h" #include "../Widgets/Layout.h" #include "../Widgets/Manager.h" #include "../Widgets/MenuKnapp.h" #include "../Widgets/Message.h" #include "../Widgets/Sash.h" #include "../Widgets/Scrollable.h" #include "../Widgets/ScrBar.h" #include "../Widgets/ScrList.h" #include "../Widgets/Util.h" #include "sysdeps.h" #if HAVE_XMU && XtSpecificationRelease > 4 #include #endif static Widget top_vbar, top_hbar, message_line; static Widget text_hbar, text_vbar; void set_message(char *message, int beep) { MessageSetAndRedraw(message_line, message, beep && global.bell); } #define DESCRIPTION(desc) ((desc) ? (desc) : "") #define ARTTREE_STEPSIZE 16 #define LINE_LEN 256 static int group_name_len = 1; static int subject_len = 1; struct main_widgets main_widgets; static void print_group_info_subscr(char *buffer, int len, GROUP *group) { int n; *buffer++ = group->ahead_flag ? thread_ahead_char(group) : ' '; *buffer++ = ' '; len -= 2; n = strlen(group->name); if (n >= group_name_len) n = group_name_len; memcpy(buffer, group->name, n); if (n < group_name_len) memset(buffer + n, ' ', group_name_len - n); buffer += group_name_len; len -= group_name_len; sprintf(buffer, "%6ld", group->no_unread); n = strlen(buffer); buffer += n; *buffer++ = ' '; len -= n + 1; if (group->description) { n = strlen(group->description); if (n > len - 2) n = len - 2; memcpy(buffer, group->description, n); buffer += n; } *buffer = '\0'; } static void print_group_info_all(char *buffer, int len, GROUP *group) { long num; int n; num = group->last_art - group->first_art + 1; if (num < 0) num = 0; memcpy(buffer, group->subscribed ? "[s] " : " ", 4); buffer += 4; *buffer++ = group->ahead_flag ? thread_ahead_char(group) : ' '; *buffer++ = ' '; len -= 6; n = strlen(group->name); if (n >= group_name_len) n = group_name_len; memcpy(buffer, group->name, n); if (n < group_name_len) memset(buffer + n, ' ', group_name_len - n); buffer += group_name_len; len -= group_name_len; sprintf(buffer, "%6ld", num); n = strlen(buffer); buffer += n; *buffer++ = ' '; len -= n + 1; if (group->description) { n = strlen(group->description); if (n > len - 2) n = len - 2; memcpy(buffer, group->description, n); buffer += n; } *buffer = '\0'; } static void print_subj_info(char *buffer, int len, SUBJECT *subj) { int indent = False; SUBJECT *loop = subj->prev; ARTICLE *first = first_unread_article_with_subject(subj); int n, i; if (subj->no_unread == 0) strcpy(buffer, " "); else sprintf(buffer, "%3ld", subj->no_unread); n = strlen(buffer); buffer += n; len -= n; *buffer++ = subj->has_tagged ? '*' : ' '; *buffer++ = ' '; for (loop = subj->prev ; loop && loop->thread == subj->thread ; loop = loop->prev) if (loop->disp >= 0) { indent = True; break; } n = subject_len; if (indent) { *buffer++ = ' '; *buffer++ = ' '; len -= 2; n -= 2; } if (first && A_PARENT(first) && A_PARENT(first)->subject == subj) { memcpy(buffer, "Re: ", 4); buffer += 4; len -= 4; n -= 4; } i = strlen(subj->subject); if (i >= n) i = n; memcpy(buffer, subj->subject, i); if (i < n) memset(buffer + i, ' ', n - i); buffer += n; *buffer++ = ' '; len -= subject_len + 1; if (first && first->tree_data.label) { char *author = first->tree_data.label; n = strlen(author); if (n > len - 2) n = len - 2; memcpy(buffer, author, n); buffer += n; } *buffer = '\0'; } static void knapp_set_mode(NewsMode mode) { Arg arg; switch (mode) { case NewsModeDisconnected: XtSetMappedWhenManaged(main_widgets.knapp[2], False); XtSetMappedWhenManaged(main_widgets.knapp[6], False); XtSetMappedWhenManaged(main_widgets.knapp[7], False); XtSetMappedWhenManaged(main_widgets.knapp[8], False); KnappSetLabelNo(main_widgets.knapp[0], 0, True); KnappSetLabelNo(main_widgets.knapp[1], 0, True); KnappSetSensitive(main_widgets.knapp[3], False); KnappSetSensitive(main_widgets.knapp[5], False); KnappSetSensitive(main_widgets.knapp[10], False); KnappSetSensitive(main_widgets.knapp[11], False); XtSetArg(arg, XtNmenuName, "miscshell2"); break; case NewsModeConnected: XtSetMappedWhenManaged(main_widgets.knapp[2], True); XtSetMappedWhenManaged(main_widgets.knapp[6], True); XtSetMappedWhenManaged(main_widgets.knapp[7], True); XtSetMappedWhenManaged(main_widgets.knapp[8], True); KnappSetLabelNo(main_widgets.knapp[0], 0, True); KnappSetLabelNo(main_widgets.knapp[1], 1, True); KnappSetLabelNo(main_widgets.knapp[2], 0, True); KnappSetLabelNo(main_widgets.knapp[6], 0, True); KnappSetLabelNo(main_widgets.knapp[7], 0, True); KnappSetLabelNo(main_widgets.knapp[8], 0, True); KnappSetSensitive(main_widgets.knapp[3], True); KnappSetSensitive(main_widgets.knapp[5], True); KnappSetSensitive(main_widgets.knapp[10], False); KnappSetSensitive(main_widgets.knapp[11], False); XtSetArg(arg, XtNmenuName, "miscshell2"); break; case NewsModeGroup: XtSetMappedWhenManaged(main_widgets.knapp[2], True); XtSetMappedWhenManaged(main_widgets.knapp[6], True); XtSetMappedWhenManaged(main_widgets.knapp[7], True); XtSetMappedWhenManaged(main_widgets.knapp[8], True); KnappSetLabelNo(main_widgets.knapp[0], 1, True); KnappSetLabelNo(main_widgets.knapp[1], 2, True); KnappSetLabelNo(main_widgets.knapp[2], 1, True); KnappSetLabelNo(main_widgets.knapp[6], 1, True); KnappSetLabelNo(main_widgets.knapp[7], 1, True); KnappSetLabelNo(main_widgets.knapp[8], 1, True); KnappSetSensitive(main_widgets.knapp[3], True); KnappSetSensitive(main_widgets.knapp[5], True); KnappSetSensitive(main_widgets.knapp[10], True); KnappSetSensitive(main_widgets.knapp[11], True); XtSetArg(arg, XtNmenuName, "miscshell1"); break; case NewsModeThread: XtSetMappedWhenManaged(main_widgets.knapp[2], True); XtSetMappedWhenManaged(main_widgets.knapp[6], True); XtSetMappedWhenManaged(main_widgets.knapp[7], True); XtSetMappedWhenManaged(main_widgets.knapp[8], True); KnappSetLabelNo(main_widgets.knapp[0], 1, False); KnappSetLabelNo(main_widgets.knapp[1], 3, True); KnappSetLabelNo(main_widgets.knapp[2], 1, False); KnappSetLabelNo(main_widgets.knapp[6], 1, False); KnappSetLabelNo(main_widgets.knapp[7], 1, True); KnappSetLabelNo(main_widgets.knapp[8], 1, True); KnappSetSensitive(main_widgets.knapp[3], True); KnappSetSensitive(main_widgets.knapp[5], True); KnappSetSensitive(main_widgets.knapp[10], True); KnappSetSensitive(main_widgets.knapp[11], True); XtSetArg(arg, XtNmenuName, "miscshell1"); break; case NewsModeAllgroups: case NewsModeSomegroups: XtSetMappedWhenManaged(main_widgets.knapp[2], True); XtSetMappedWhenManaged(main_widgets.knapp[6], True); XtSetMappedWhenManaged(main_widgets.knapp[7], True); XtSetMappedWhenManaged(main_widgets.knapp[8], False); KnappSetLabelNo(main_widgets.knapp[0], 1, True); KnappSetLabelNo(main_widgets.knapp[1], 4, True); KnappSetLabelNo(main_widgets.knapp[2], 2, True); KnappSetLabelNo(main_widgets.knapp[6], 0, True); KnappSetLabelNo(main_widgets.knapp[7], 2, True); KnappSetSensitive(main_widgets.knapp[3], True); KnappSetSensitive(main_widgets.knapp[5], True); KnappSetSensitive(main_widgets.knapp[10], False); KnappSetSensitive(main_widgets.knapp[11], True); XtSetArg(arg, XtNmenuName, "miscshell2"); break; case NewsModeNewgroups: XtSetMappedWhenManaged(main_widgets.knapp[2], True); XtSetMappedWhenManaged(main_widgets.knapp[6], False); XtSetMappedWhenManaged(main_widgets.knapp[7], False); XtSetMappedWhenManaged(main_widgets.knapp[8], False); KnappSetLabelNo(main_widgets.knapp[0], 1, True); KnappSetLabelNo(main_widgets.knapp[1], 4, True); KnappSetLabelNo(main_widgets.knapp[2], 2, True); KnappSetSensitive(main_widgets.knapp[3], False); KnappSetSensitive(main_widgets.knapp[5], True); KnappSetSensitive(main_widgets.knapp[10], False); KnappSetSensitive(main_widgets.knapp[11], False); XtSetArg(arg, XtNmenuName, "miscshell2"); break; } XtSetValues(main_widgets.knapp[3], &arg, 1); } void setNewsModeDisconnected(void) { global.mode = NewsModeDisconnected; ArtTextClearLines(main_widgets.text); ScrListClearLines(main_widgets.thread_list); ScrListClearLines(main_widgets.group_list); XtUnmanageChild(main_widgets.arttree); XtUnmanageChild(main_widgets.thread_list); XtUnmanageChild(main_widgets.group_list); knapp_set_mode(NewsModeDisconnected); XtSetSensitive(top_vbar, False); XtSetSensitive(top_hbar, False); ScrBarSetLengthsAndPos(top_hbar, 0, 0, 0); ScrBarSetLengthsAndPos(top_vbar, 0, 0, 0); } void update_group_entry(GROUP *group) { char buffer[LINE_LEN]; switch (global.mode) { case NewsModeConnected: if (group->disp >= 0) { print_group_info_subscr(buffer, sizeof buffer, group); ScrListSetLine(main_widgets.group_list, group->disp, buffer, None); } break; case NewsModeAllgroups: case NewsModeSomegroups: if (group->disp >= 0) { print_group_info_all(buffer, sizeof buffer, group); ScrListSetLine(main_widgets.group_list, group->disp, buffer, None); } break; case NewsModeDisconnected: case NewsModeNewgroups: case NewsModeGroup: case NewsModeThread: break; } } void setNewsModeConnected(void) { Arg args[4]; long n, sel = -1; int flag = False; group_name_len = res_group_name_columns(); if (group_name_len <= 0 || group_name_len > 128) group_name_len = 128; global.mode = NewsModeConnected; XtUnmanageChild(main_widgets.arttree); XtUnmanageChild(main_widgets.thread_list); ArtTextClearLines(main_widgets.text); ScrListClearLines(main_widgets.thread_list); ScrListClearLines(main_widgets.group_list); XtSetSensitive(top_hbar, True); XtSetSensitive(top_vbar, True); XtSetArg(args[0], XtNstepSize, 1); XtSetArg(args[1], XtNsensitive, True); XtSetValues(top_vbar, args, 2); ScrBarSetLengthsAndPos(top_hbar, 0, 0, 0); ScrBarSetLengthsAndPos(top_vbar, 0, 0, 0); knapp_set_mode(NewsModeConnected); update_misc_menu(NewsModeConnected); XtSetArg(args[0], XtNatMostOne, True); XtSetArg(args[1], XtNatLeastOne, True); XtSetArg(args[2], XtNallowDnd, False); XtSetValues(main_widgets.group_list, args, 3); XtManageChild(main_widgets.group_list); ScrollableSuspend(main_widgets.group_list); for (n = 0 ; n < global.no_groups ; n++) { GROUP *group = global.groups[n]; char buffer[LINE_LEN]; if (!group->subscribed) break; calc_no_unread(group); if (group == global.curr_group) flag = True; if (group->no_unread <= 0) group->disp = -1; else { print_group_info_subscr(buffer, sizeof buffer, group); group->disp = ScrListAddLine(main_widgets.group_list, buffer, None); if (flag) { sel = group->disp; flag = False; } } } ScrollableResume(main_widgets.group_list); while (n < global.no_groups) global.groups[n++]->disp = -1; if (sel < 0) { global.curr_group = NULL; for (n = 0 ; n < global.no_groups ; n++) { if (!global.groups[n]->subscribed) { sel = 0; break; } else if (global.groups[n]->disp == 0) { global.curr_group = global.groups[n]; sel = 0; break; } } } if (sel >= 0) { ScrListSetSelected(main_widgets.group_list, sel, True); ScrListMakeVisible(main_widgets.group_list, sel); } Remanage(main_widgets.top_manager); set_standard_message(); } void update_subj_entry(SUBJECT *subj) { long row = subj->disp; char buffer[LINE_LEN]; if (row >= 0) { print_subj_info(buffer, sizeof buffer, subj); ScrListSetLine(main_widgets.thread_list, row, buffer, subj->pixmap); } } void setNewsModeGroup(int show_all) { SUBJECT *loop; long n; Arg args[2]; XtSetSensitive(top_hbar, True); XtSetSensitive(top_vbar, True); XtSetArg(args[0], XtNstepSize, 1); XtSetValues(top_vbar, args, 1); if (global.mode != NewsModeThread) { subject_len = res_subject_columns(); if (subject_len <= 0 || subject_len > 128) subject_len = 128; global.mode = NewsModeGroup; ScrListClearLines(main_widgets.group_list); ScrListClearLines(main_widgets.thread_list); XtUnmanageChild(main_widgets.arttree); XtUnmanageChild(main_widgets.group_list); XtManageChild(main_widgets.thread_list); ArtTreeSetTree(main_widgets.arttree, NULL); knapp_set_mode(NewsModeGroup); update_misc_menu(NewsModeGroup); ScrollableSuspend(main_widgets.thread_list); for (n = 0, loop = get_subjects(main_thr) ; loop ; loop = loop->next) { char buffer[LINE_LEN]; if (!show_all && loop->no_unread == 0) { loop->disp = -1; continue; } print_subj_info(buffer, sizeof buffer, loop); loop->disp = ScrListAddLine(main_widgets.thread_list, buffer, loop->pixmap); if (n == 0) global.curr_subj = loop; n++; } ScrollableResume(main_widgets.thread_list); Remanage(main_widgets.top_manager); /* message already set by get_articles */ } else { global.mode = NewsModeGroup; XtUnmanageChild(main_widgets.arttree); ArtTreeSetTree(main_widgets.arttree, NULL); XtManageChild(main_widgets.thread_list); ScrollableResume(main_widgets.thread_list); set_curr_subj(global.curr_subj); knapp_set_mode(NewsModeGroup); set_standard_message(); } } void setNewsModeThread(void) { Arg args[4]; global.mode = NewsModeThread; XtUnmanageChild(main_widgets.group_list); XtUnmanageChild(main_widgets.thread_list); ArtTreeSetTree(main_widgets.arttree, NULL); ScrollableSuspend(main_widgets.arttree); XtSetArg(args[0], XtNtree, global.curr_subj->thread); XtSetArg(args[1], XtNx, 0); XtSetArg(args[2], XtNy, 0); XtSetValues(main_widgets.arttree, args, 3); set_tree_stuff(global.curr_subj->thread); mark_tagged_articles(global.curr_subj->thread); XtSetArg(args[0], XtNstepSize, ARTTREE_STEPSIZE); XtSetArg(args[1], XtNsensitive, True); XtSetValues(top_hbar, args, 2); XtSetValues(top_vbar, args, 2); if (global.curr_art) { ArtTreeNodeMakeVisible(main_widgets.arttree, (ART_TREE_NODE *)global.curr_art); ArtTreeNodeSetSelected(main_widgets.arttree, (ART_TREE_NODE *)global.curr_art, True); } XtManageChild(main_widgets.arttree); ScrollableResume(main_widgets.arttree); knapp_set_mode(NewsModeThread); set_standard_message(); } void setNewsModeAllgroups(regex_t *re) { Arg args[8]; long n, first = -1; group_name_len = res_group_name_columns(); if (group_name_len <= 0 || group_name_len > 128) group_name_len = 128; if (re) global.mode = NewsModeSomegroups; else global.mode = NewsModeAllgroups; XtSetArg(args[0], XtNnAlloc, global.no_groups + 8); XtSetValues(main_widgets.group_list, args, 1); XtSetSensitive(top_vbar, True); XtSetSensitive(top_hbar, True); ScrListClearLines(main_widgets.thread_list); ArtTextClearLines(main_widgets.text); XtUnmanageChild(main_widgets.arttree); XtUnmanageChild(main_widgets.thread_list); knapp_set_mode(global.mode); update_misc_menu(global.mode); ScrListClearLines(main_widgets.group_list); XtSetArg(args[0], XtNatMostOne, False); XtSetArg(args[1], XtNatLeastOne, False); XtSetArg(args[2], XtNallowDnd, re == NULL); XtSetValues(main_widgets.group_list, args, 3); XtManageChild(main_widgets.group_list); XtSetArg(args[0], XtNstepSize, 1); XtSetValues(top_vbar, args, 1); ScrBarSetLengthsAndPos(top_hbar, 0, 0, 0); ScrollableSuspend(main_widgets.group_list); for (n = 0 ; n < global.no_groups ; n++) { GROUP *group = global.groups[n]; char buffer[LINE_LEN]; if (re && regexec(re, group->name, 0, NULL, 0) != 0) group->disp = -1; else { print_group_info_all(buffer, sizeof buffer, group); group->disp = ScrListAddLine(main_widgets.group_list, buffer, None); if (group == global.curr_group) first = group->disp; } } ScrollableResume(main_widgets.group_list); if (first >= 0) { ScrListSetSelected(main_widgets.group_list, first, True); ScrListMakeVisible(main_widgets.group_list, first); } Remanage(main_widgets.top_manager); set_standard_message(); } void setNewsModeNewgroups(void) { Arg args[8]; long n; group_name_len = res_group_name_columns(); if (group_name_len <= 0 || group_name_len > 128) group_name_len = 128; global.mode = NewsModeNewgroups; for (n = 0 ; n < global.no_groups ; n++) { global.groups[n]->disp = -1; calc_no_unread(global.groups[n]); } XtSetArg(args[0], XtNnAlloc, global.no_new_groups + 8); XtSetValues(main_widgets.group_list, args, 1); XtSetSensitive(top_vbar, True); XtSetSensitive(top_hbar, True); ScrListClearLines(main_widgets.thread_list); ScrListClearLines(main_widgets.group_list); ArtTextClearLines(main_widgets.text); XtUnmanageChild(main_widgets.arttree); XtUnmanageChild(main_widgets.thread_list); knapp_set_mode(NewsModeNewgroups); update_misc_menu(NewsModeNewgroups); XtSetArg(args[0], XtNatMostOne, False); XtSetArg(args[1], XtNatLeastOne, False); XtSetArg(args[2], XtNallowDnd, False); XtSetValues(main_widgets.group_list, args, 3); XtManageChild(main_widgets.group_list); XtSetArg(args[0], XtNstepSize, 1); XtSetValues(top_vbar, args, 1); ScrBarSetLengthsAndPos(top_hbar, 0, 0, 0); ScrollableSuspend(main_widgets.group_list); for (n = 0 ; n < global.no_new_groups ; n++) { GROUP *group = global.new_groups[n]; char buffer[LINE_LEN]; print_group_info_all(buffer, sizeof buffer, group); group->disp = ScrListAddLine(main_widgets.group_list, buffer, None); } ScrollableResume(main_widgets.group_list); Remanage(main_widgets.top_manager); set_message("New groups found.", False); } void create_main_widgets(void) { static char *knapp_name[] = { "knapp0", "knapp1", "knapp2", "misc", "post", "kill", "knapp6", "knapp7", "knapp8", "abort", "save", "search", }; Widget main_layout, knapp_layout, text_layout; Widget temp, text_manager; Arg args[10]; int one_win = !global.separate_windows; int i; if (one_win) { main_widgets.second_shell = NULL; main_layout = XtVaCreateManagedWidget("singlelayout", layoutWidgetClass, main_widgets.shell, XtVaTypedArg, XtNlayout, XtRString, #include "layouts/single.h" (int)sizeof(String), (void *)0); } else { XtSetArg(args[0], XtNinput, True); XtSetArg(args[1], XtNcolormap, global.cmap); XtSetArg(args[2], XtNvisual, global.visual); XtSetArg(args[3], XtNdepth, global.depth); main_widgets.second_shell = XtCreatePopupShell("second", topLevelShellWidgetClass, main_widgets.shell, args, 4); main_layout = XtVaCreateManagedWidget("doublelayout", layoutWidgetClass, main_widgets.shell, XtVaTypedArg, XtNlayout, XtRString, #include "layouts/double.h" (int)sizeof(String), (void *)0); } text_layout = XtVaCreateManagedWidget("textlayout", layoutWidgetClass, main_widgets.second_shell ? main_widgets.second_shell : main_layout, XtVaTypedArg, XtNlayout, XtRString, #include "layouts/text.h" (int)sizeof(String), (void *)0); main_widgets.top_layout = XtVaCreateManagedWidget("toplayout", layoutWidgetClass, main_layout, XtVaTypedArg, XtNlayout, XtRString, #include "layouts/top.h" (int)sizeof(String), (void *)0); XtSetArg(args[0], XtNcenter, False); message_line = XtCreateManagedWidget("message", messageWidgetClass, main_layout, args, 1); knapp_layout = XtVaCreateManagedWidget("knapplayout", layoutWidgetClass, main_layout, XtVaTypedArg, XtNlayout, XtRString, #include "layouts/knapp.h" (int)sizeof(String), (void *)0); main_widgets.top_manager = XtCreateManagedWidget("topmanager", managerWidgetClass, main_widgets.top_layout, NULL, 0); temp = XtCreateManagedWidget("sash1", sashWidgetClass, main_layout, NULL, 0); XtAddCallback(temp, XtNcallback, sash_callback, NULL); temp = XtCreateManagedWidget("sash2", sashWidgetClass, main_layout, NULL, 0); XtAddCallback(temp, XtNcallback, sash_callback, NULL); if (one_win) { temp = XtCreateManagedWidget("sash3", sashWidgetClass, main_layout, NULL, 0); XtAddCallback(temp, XtNcallback, sash_callback, NULL); } top_vbar = XtCreateManagedWidget("topvbar", scrBarWidgetClass, main_widgets.top_layout, NULL, 0); XtSetArg(args[0], XtNvertical, False); top_hbar = XtCreateManagedWidget("tophbar", scrBarWidgetClass, main_widgets.top_layout, args, 1); XtSetArg(args[0], XtNcontainHoriz, False); XtSetArg(args[1], XtNcontainVert, False); XtSetArg(args[2], XtNpixmapWidth, HOT_PIXMAP_SIZE); XtSetArg(args[3], XtNpixmapHeight, HOT_PIXMAP_SIZE); XtSetArg(args[4], XtNdepthOne, False); XtSetArg(args[5], XtNhBar, top_hbar); XtSetArg(args[6], XtNvBar, top_vbar); main_widgets.arttree = XtCreateWidget("arttree", artTreeWidgetClass, main_widgets.top_manager, args, 7); XtSetArg(args[1], XtNcontainVert, True); XtSetArg(args[7], XtNusePixmaps, False); main_widgets.group_list = XtCreateManagedWidget("grouplist", scrListWidgetClass, main_widgets.top_manager, args, 8); XtSetArg(args[7], XtNusePixmaps, True); XtSetArg(args[8], XtNatLeastOne, True); XtSetArg(args[9], XtNatMostOne, True); main_widgets.thread_list = XtCreateWidget("threadlist", scrListWidgetClass, main_widgets.top_manager, args, 9); text_vbar = XtCreateManagedWidget("textvbar", scrBarWidgetClass, text_layout, NULL, 0); XtSetArg(args[0], XtNvertical, False); text_hbar = XtCreateManagedWidget("texthbar", scrBarWidgetClass, text_layout, args, 1); text_manager = XtCreateManagedWidget("textmanager", managerWidgetClass, text_layout, NULL, 0); XtSetArg(args[0], XtNhBar, text_hbar); XtSetArg(args[1], XtNvBar, text_vbar); XtSetArg(args[2], XtNcontainVert, True); XtSetArg(args[3], XtNcontainHoriz, False); main_widgets.text = XtCreateManagedWidget("text", artTextWidgetClass, text_manager, args, 4); XtSetArg(args[0], XtNresizable, False); for (i = 0 ; i < 12 ; i++) { int has_menu = False; if (i == 3) { XtSetArg(args[1], XtNmenuName, NULL); has_menu = True; } else if (i == 4) { XtSetArg(args[1], XtNmenuName, "postshell"); has_menu = True; } main_widgets.knapp[i] = XtCreateManagedWidget(knapp_name[i], has_menu ? menuKnappWidgetClass : knappWidgetClass, knapp_layout, args, has_menu ? 2 : 1); } XtRealizeWidget(main_widgets.shell); add_WM_DELETE_WINDOW_callback(main_widgets.shell, delete_window_callback, NULL); XtInstallAllAccelerators(main_widgets.shell, main_widgets.shell); if (main_widgets.second_shell) { XtRealizeWidget(main_widgets.second_shell); add_WM_DELETE_WINDOW_callback(main_widgets.second_shell, delete_window_callback, NULL); XtInstallAllAccelerators(main_widgets.second_shell, main_widgets.shell); } create_misc_menu1(main_widgets.shell); create_misc_menu2(main_widgets.shell); create_post_menu(main_widgets.shell); XtAddCallback(main_widgets.knapp[0], XtNcallback, knapp0_callback, NULL); XtAddCallback(main_widgets.knapp[1], XtNcallback, knapp1_callback, NULL); XtAddCallback(main_widgets.knapp[2], XtNcallback, knapp2_callback, NULL); XtAddCallback(main_widgets.knapp[5], XtNcallback, knapp5_callback, NULL); XtAddCallback(main_widgets.knapp[6], XtNcallback, knapp6_callback, NULL); XtAddCallback(main_widgets.knapp[7], XtNcallback, knapp7_callback, NULL); XtAddCallback(main_widgets.knapp[8], XtNcallback, knapp8_callback, NULL); XtAddCallback(main_widgets.knapp[9], XtNcallback, abort_callback, NULL); XtAddCallback(main_widgets.knapp[10], XtNcallback, knapp10_callback, NULL); XtAddCallback(main_widgets.knapp[11], XtNcallback, knapp11_callback, NULL); XtAddCallback(main_widgets.text, XtNurlCallback, text_url_callback, NULL); XtAddCallback(main_widgets.arttree, XtNselectCallback, arttree_sel_callback, NULL); XtAddCallback(main_widgets.arttree, XtNouterCallback, arttree_tag_callback, NULL); XtAddCallback(main_widgets.thread_list, XtNselectCallback, thread_list_sel_callback, NULL); XtAddCallback(main_widgets.thread_list, XtNcallback, thread_list_callback, NULL); XtAddCallback(main_widgets.group_list, XtNcallback, group_list_callback, NULL); XtAddCallback(main_widgets.group_list, XtNselectCallback, group_list_sel_callback, NULL); XtAddCallback(main_widgets.group_list, XtNdndCallback, group_list_dnd_callback, NULL); #if HAVE_XMU && (XtSpecificationRelease > 4) XtAddEventHandler(main_widgets.shell, (EventMask)0, True, _XEditResCheckMessages, NULL); if (main_widgets.second_shell) XtAddEventHandler(main_widgets.second_shell, (EventMask)0, True, _XEditResCheckMessages, NULL); #endif } void purge_hot(Pixmap pixmap) { SUBJECT *subj; ARTICLE *art; if (global.mode != NewsModeGroup && global.mode != NewsModeThread) return; ScrListPurgePixmap(main_widgets.thread_list, pixmap); for (art = get_articles(main_thr) ; art ; art = art->next) if (art->pixmap == pixmap) { art->pixmap = 0; global.n_hot--; } for (subj = get_subjects(main_thr) ; subj ; subj = subj->next) if (subj->pixmap == pixmap) update_subj_hot_value(subj); if (global.mode == NewsModeThread && global.curr_subj) { for (art = global.curr_subj->thread ; art ; art = next_in_thread_preorder(art)) if (ArtTreeNodeGetPixmap(main_widgets.arttree, (ART_TREE_NODE *)art) == pixmap) ArtTreeNodeSetPixmap(main_widgets.arttree, (ART_TREE_NODE *)art, None); update_subj_hot_value(global.curr_subj); } } ./knews-1.0b.1/src/k_file.h100644 1244 1244 707 6455455544 14013 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ struct KILL_FILE; extern void read_global_kill_file(void); extern int update_kill_files(void); extern void kill_exit_group(GROUP*); extern void kill_cleanup(void); extern void kill_articles(GROUP*); extern void kill_edit_popup(GROUP*); extern int add_kill_node(struct KILL_FILE*, int, int, int, int, char*, char*, char*); extern struct KILL_FILE *get_kill_file(GROUP*); ./knews-1.0b.1/src/xutil.c100644 1244 1244 46063 6455455544 13767 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include "global.h" #include #include "ahead.h" #include "bg.h" #include "cache.h" #include "codes.h" #include "connect.h" #include "expand.h" #include "file.h" #include "k_file.h" #include "newsrc.h" #include "partial.h" #include "p_setup.h" #include "resource.h" #include "save.h" #include "search.h" #include "server.h" #include "tag.h" #include "thread.h" #include "util.h" #include "widgets.h" #include "xutil.h" #include "../Widgets/ArtTree.h" #include "../Widgets/Dialogue.h" #include "../Widgets/Knapp.h" #include "../Widgets/Menu.h" #include "../Widgets/MenuShell.h" #include "../Widgets/Notice.h" #include "../Widgets/Scrollable.h" #include "../Widgets/ScrList.h" #include "../Widgets/Shadow.h" #include "../Widgets/StringG.h" #include "../Widgets/Util.h" #if HAVE_XPM #include #endif void set_curr_art(ARTICLE *art, int center) { if (global.mode == NewsModeThread) { if (global.curr_art && global.curr_art != art) ArtTreeNodeSetSelected(main_widgets.arttree, (ART_TREE_NODE *)global.curr_art, False); if (art) { ArtTreeNodeSetSelected(main_widgets.arttree, (ART_TREE_NODE *)art, True); if (center) ArtTreeNodeMakeVisible(main_widgets.arttree, (ART_TREE_NODE*)art); } } global.curr_art = art; if (art) set_curr_subj(art->subject); } void set_curr_subj(SUBJECT *subj) { global.curr_subj = subj; if (subj && subj->disp >= 0) { ScrListMakeVisible(main_widgets.thread_list, subj->disp); ScrListSetSelected(main_widgets.thread_list, subj->disp, True); } } void set_tree_stuff(ARTICLE *thr) { while (thr) { if (thr->from && !thr->read) { ArtTreeNodeSetInner(main_widgets.arttree, (ART_TREE_NODE *)thr, True); if (!thr->read && thr->pixmap != None) ArtTreeNodeSetPixmap(main_widgets.arttree, (ART_TREE_NODE *)thr, thr->pixmap); } if (A_PARENT(thr) && A_PARENT(thr)->subject != thr->subject) { ArtTreeNodeSetDashed(main_widgets.arttree, (ART_TREE_NODE *)thr, True); } if (A_CHILD1(thr)) set_tree_stuff(A_CHILD1(thr)); thr = A_SIBLING(thr); } } void set_curr_group(void) { long i, n; switch (global.mode) { case NewsModeConnected: case NewsModeAllgroups: case NewsModeSomegroups: case NewsModeNewgroups: n = ScrListGetFirstSelected(main_widgets.group_list); if (!global.curr_group || global.curr_group->disp != n) { global.curr_group = NULL; if (n < 0) break; for (i = 0 ; i < global.no_groups ; i++) if (global.groups[i]->disp == n) { global.curr_group = global.groups[i]; break; } } break; case NewsModeGroup: case NewsModeThread: case NewsModeDisconnected: break; } } void realize_fake(ARTICLE *art, char **headers, int n) { if (art->from || art->tree_data.label) return; while (n-- > 0) if (case_lstrncmp(*headers, "from:", 5) != 0) headers++; else { char *c = *headers + 5; while (*c == ' ' || *c == '\t') c++; if (*c != '\0') { fix_author(art, c, res_show_number_lines()); if (global.mode == NewsModeThread) ArtTreeNodeNotifyLabel(main_widgets.arttree, (ART_TREE_NODE *)art); } break; } } /*****************************************************************************/ void change_interruptible(int interruptible) { XtSetSensitive(main_widgets.knapp[9], interruptible); ShadowRedrawWidget(main_widgets.knapp[9]); } void set_busy(int interruptible) { if (global.busy) { fputs("knews: internal error: set_busy when already busy!\n", stderr); return; } global.busy = True; XDefineCursor(display, XtWindow(main_widgets.shell), global.busy_cursor); if (main_widgets.second_shell) XDefineCursor(display, XtWindow(main_widgets.second_shell), global.busy_cursor); set_busy_save(True); set_busy_search(True); if (interruptible) KnappSetSensitive(main_widgets.knapp[9], True); KnappSetActive(main_widgets.knapp[0], False); KnappSetActive(main_widgets.knapp[1], global.mode == NewsModeGroup || global.mode == NewsModeThread); KnappSetActive(main_widgets.knapp[2], False); KnappSetActive(main_widgets.knapp[3], False); KnappSetActive(main_widgets.knapp[4], False); KnappSetActive(main_widgets.knapp[6], False); KnappSetActive(main_widgets.knapp[7], False); KnappSetActive(main_widgets.knapp[8], False); KnappSetActive(main_widgets.knapp[11], global.mode != NewsModeAllgroups); ArtTreeSetActive(main_widgets.arttree, False); ScrListSetActive(main_widgets.group_list, False); ScrListSetActive(main_widgets.thread_list, False); XFlush(display); } void unset_busy(void) { if (!global.busy) { fputs("knews: internal error: unset_busy when not busy\n", stderr); return; } global.busy = False; XDefineCursor(display, XtWindow(main_widgets.shell), global.cursor); if (main_widgets.second_shell) XDefineCursor(display, XtWindow(main_widgets.second_shell), global.cursor); set_busy_save(False); set_busy_search(False); KnappSetSensitive(main_widgets.knapp[9], False); KnappSetActive(main_widgets.knapp[0], True); KnappSetActive(main_widgets.knapp[1], True); KnappSetActive(main_widgets.knapp[2], True); KnappSetActive(main_widgets.knapp[3], True); KnappSetActive(main_widgets.knapp[4], True); KnappSetActive(main_widgets.knapp[6], True); KnappSetActive(main_widgets.knapp[7], True); KnappSetActive(main_widgets.knapp[8], True); KnappSetActive(main_widgets.knapp[11], True); ArtTreeSetActive(main_widgets.arttree, True); ScrListSetActive(main_widgets.group_list, True); ScrListSetActive(main_widgets.thread_list, True); XFlush(display); } void set_standard_message(void) { char message[512]; long n; switch (global.mode) { case NewsModeDisconnected: set_message("Knews " KNEWS_VERSION ". " "Copyright 1995, 1996 Karl-Johan Johnsson.", False); break; case NewsModeConnected: if (global.groups && global.groups[0] && global.groups[0]->subscribed) set_message("Subscribed groups " "with unread articles displayed.", False); else set_message("No subscribed groups.", False); break; case NewsModeGroup: n = ScrollableGetVSize(main_widgets.thread_list); sprintf(message, "%s with %ld unread articles, %ld subjects. %ld hot.", global.curr_group->name, global.curr_group->no_unread, n, global.n_hot); set_message(message, False); break; case NewsModeThread: if (!global.curr_subj) return; sprintf(message, "%ld unread articles with Subject: ", global.curr_subj->no_unread); strncat(message, global.curr_subj->subject, 128); set_message(message, False); break; case NewsModeAllgroups: set_message("All groups displayed.", False); break; case NewsModeSomegroups: set_message("Groups matching regexp displayed.", False); break; case NewsModeNewgroups: set_message("New groups displayed.", False); break; } } /*****************************************************************************/ char *do_update(void) { int newsrc_ok = update_newsrc(); int kill_ok = update_kill_files(); if (newsrc_ok) if (kill_ok) return NULL; else return "Failed to update kill files! Newsrc file updated."; else if (kill_ok) return "Failed to update newsrc! Kill files updated."; else return "Failed to update newsrc and kill files!"; } static void update_failed_callback(Widget w, XtPointer client_data, XtPointer call_data) { NoticeReply reply = (NoticeReply)call_data; int allow_abort = (int)client_data; XtPopdown(w); XtDestroyWidget(w); switch (reply) { case NoticeReplyLeft: /* Retry */ global_cleanup(True, allow_abort); break; case NoticeReplyMiddle: /* Disconnect */ global_cleanup(False, allow_abort); break; case NoticeReplyTimeout: case NoticeReplyRight: case NoticeReplyClose: /* abort */ if (!allow_abort) exit(1); break; } } int global_cleanup(int try_to_update, int allow_abort) { QuitFunc qf = main_server ? server_get_quit_func(main_server) : NULL; long i; char *err_msg; if (global.mode == NewsModeGroup || global.mode == NewsModeThread) { free_read_arts_list(global.curr_group); global.curr_group->read_arts = create_read_arts_list(); } if (global.mode == NewsModeDisconnected) return True; if (try_to_update && (err_msg = do_update())) { char message[128]; set_message(err_msg, True); sprintf(message, "%s\n\nReally disconnect?", err_msg); popup_notice("verify", message, "Retry", "Disconnect", allow_abort ? "Abort" : NULL, 0, update_failed_callback, (XtPointer)allow_abort, XtGrabExclusive); return False; } if (qf) qf(main_server); server_close(main_server); XtFree(global.serv_addr); global.serv_addr = NULL; clear_thread_context(main_thr); bg_shutdown(); thread_ahead_shutdown(); cache_leave_group(); popdown_find_group(); kill_cleanup(); popdown_save(); popdown_search(); partial_clear_cache(); clear_tagged_articles(); clear_history(); for (i = 0 ; i < global.no_groups ; i++) { XtFree(global.groups[i]->name); XtFree(global.groups[i]->description); free_read_arts_list(global.groups[i]); XtFree((char *)global.groups[i]); } XtFree((char *)global.groups); global.groups = NULL; global.no_groups = 0; global.max_groups = 0; global.curr_group = NULL; XtFree((char *)global.new_groups); global.new_groups = NULL; global.no_new_groups = 0; global.curr_art = NULL; global.curr_subj = NULL; setNewsModeDisconnected(); return True; } static void disconnect_verify_callback(Widget w, XtPointer client_data, XtPointer call_data) { NoticeReply reply = (NoticeReply)call_data; long do_exit = (long)client_data; XtPopdown(w); XtDestroyWidget(w); if (reply == NoticeReplyLeft && global_cleanup(True, True) && do_exit) exit(0); set_standard_message(); } static void have_posts_callback(Widget w, XtPointer client_data, XtPointer call_data) { NoticeReply reply = (NoticeReply)call_data; long do_exit = (long)client_data; XtPopdown(w); XtDestroyWidget(w); if (reply == NoticeReplyLeft) if (do_exit) exit(0); else if (global_cleanup(True, True)) set_standard_message(); } void disconnect(int do_exit) { if (outstanding_posts()) { char message[128]; sprintf(message, "You have not posted all articles!\n\n%s anyway?", do_exit ? "Quit" : "Disconnect"); popup_notice("verify", message, do_exit ? "Quit" : "Disconnect", NULL, "Abort", 0, have_posts_callback, (XtPointer)do_exit, XtGrabExclusive); return; } if (global.confirm_quit) { popup_notice("disconnectverify", do_exit ? "Really quit?" : "Really disconnect?", do_exit ? "Quit" : "Disconnect", NULL, "Abort", 0, disconnect_verify_callback, (XtPointer)do_exit, XtGrabExclusive); return; } if (global.mode == NewsModeDisconnected) exit(0); if (!global_cleanup(True, True)) return; if (do_exit) exit(0); set_standard_message(); } /*****************************************************************************/ static XtIntervalId rescan_timeout_id = 0; static int rescan_due = False; static void rescan_timeout_callback(XtPointer client_data, XtIntervalId *id) { char *buffer; rescan_timeout_id = 0; rescan_due = False; if (global.mode == NewsModeDisconnected || res_rescan_timeout() <= 0) return; if (global.mode != NewsModeConnected || global.busy) { rescan_due = True; return; /* no op */ } set_busy(True); set_message("Automatic rescan in progress...", False); buffer = rescan(); if (!buffer) { reconnect_server(True); unset_busy(); return; } unset_busy(); if (atoi(buffer) == NNTP_OK_GROUPS) setNewsModeConnected(); else { char message[128]; if (strlen(buffer) > 80) buffer[80] = '\0'; sprintf(message, "Error! Message from server is: %s", buffer); set_message(message, True); } } void add_rescan_timeout(void) { int timeout = res_rescan_timeout(); rescan_due = False; if (rescan_timeout_id != 0) XtRemoveTimeOut(rescan_timeout_id); if (timeout > 0) rescan_timeout_id = XtAppAddTimeOut(app_cont, timeout * 60000L, rescan_timeout_callback, NULL); else rescan_timeout_id = 0; } void remove_rescan_timeout(void) { if (rescan_timeout_id != 0) XtRemoveTimeOut(rescan_timeout_id); rescan_timeout_id = 0; rescan_due = False; } void check_if_rescan_due(void) { int timeout = res_rescan_timeout(); if (timeout <= 0) return; if (rescan_timeout_id == 0) { if (rescan_due) rescan_timeout_id = XtAppAddTimeOut(app_cont, 2000, rescan_timeout_callback, NULL); else add_rescan_timeout(); } } /*********************************************************************/ void destroy_pixmap(XtAppContext app_cont, XrmValue *to, XtPointer data, XrmValue *args, Cardinal *no_args) { /* ... */ } Boolean cvt_string_to_pixmap(Display *disp, XrmValue *args, Cardinal *n_args, XrmValue *from, XrmValue *to, XtPointer *closure) { #if !HAVE_XPM return False; #else static Pixmap pixmap; char *path; char *file_name = (char *)from->addr; XpmAttributes attr; int tmp; if (!file_name) return False; if (to->addr && to->size < sizeof pixmap) { to->size = sizeof pixmap; return False; } path = expand_path(file_name); if (!path) return False; attr.valuemask = XpmVisual | XpmColormap | XpmDepth; attr.visual = global.visual; attr.colormap = global.cmap; attr.depth = global.depth; tmp = XpmReadFileToPixmap(disp, DefaultRootWindow(disp), path, &pixmap, NULL, &attr); if (tmp < 0) { char *msg = "unknown error"; switch (tmp) { #ifdef XpmOpenFailed case XpmOpenFailed: msg = "couldn't open file"; break; #endif #ifdef XpmFileInvalid case XpmFileInvalid: msg = "file invalid"; break; #endif #ifdef XpmNoMemory case XpmNoMemory: msg = "no memory"; break; #endif #ifdef XpmColorFailed case XpmColorFailed: msg = "color error"; break; #endif default: break; } fprintf(stderr, "XpmReadFileToPixmap failed on %s: %s\n", path, msg); XtFree(path); return False; } #ifdef XpmColorError if (tmp == XpmColorError) fprintf(stderr, "XpmReadFileToPixmap warning %s: color error\n", path); #endif XtFree(path); to->size = sizeof pixmap; if (to->addr) *(Pixmap *)to->addr = pixmap; else to->addr = (XPointer)&pixmap; return True; #endif } /*************************************************************************/ static void notice_callback(Widget w, XtPointer client_data, XtPointer call_data) { XtPopdown(w); XtDestroyWidget(w); } Widget popup_notice(char *name, char *message, char *left, char *middle, char *right, long timeout, XtCallbackProc callback, XtPointer client_data, XtGrabKind grab) { Widget result; Arg args[10]; int n; n = 0; XtSetArg(args[n], XtNmessage, message); n++; XtSetArg(args[n], XtNleftLabel, left); n++; XtSetArg(args[n], XtNmiddleLabel, middle); n++; XtSetArg(args[n], XtNrightLabel, right); n++; XtSetArg(args[n], XtNtimeout, timeout); n++; XtSetArg(args[n], XtNcolormap, global.cmap); n++; XtSetArg(args[n], XtNvisual, global.visual); n++; XtSetArg(args[n], XtNdepth, global.depth); n++; XtSetArg(args[n], XtNtransientFor, main_widgets.shell); n++; result = XtCreatePopupShell(name, noticeWidgetClass, main_widgets.shell, args, n); XtRealizeWidget(result); if (!callback) callback = notice_callback; XtAddCallback(result, XtNcallback, callback, client_data); popup_under_pointer(result, grab); return result; } Widget popup_dialogue(char *name, char *message, char *left, char *middle, char *right, XtCallbackProc callback, XtPointer client_data, XtGrabKind grab) { Widget result; Arg args[10]; int n; n = 0; XtSetArg(args[n], XtNmessage, message); n++; XtSetArg(args[n], XtNleftLabel, left); n++; XtSetArg(args[n], XtNmiddleLabel, middle); n++; XtSetArg(args[n], XtNrightLabel, right); n++; XtSetArg(args[n], XtNcolormap, global.cmap); n++; XtSetArg(args[n], XtNvisual, global.visual); n++; XtSetArg(args[n], XtNdepth, global.depth); n++; XtSetArg(args[n], XtNtransientFor, main_widgets.shell); n++; result = XtCreatePopupShell(name, dialogueWidgetClass, main_widgets.shell, args, n); XtRealizeWidget(result); if (callback) XtAddCallback(result, XtNcallback, callback, client_data); popup_under_pointer(result, grab); return result; } void popup_colornotice(int bad_color) { if (bad_color && global.bell) XBell(display, 0); popup_notice("colornotice", bad_color ? "Error: No such color." : "Warning: Cannot allocate colormap entry.", "OK", NULL, NULL, 2000, NULL, NULL, XtGrabNone); } void popup_regexpnotice(int errcode, const regex_t *re) { char buffer[1024]; long len; strcpy(buffer, "Parse error in regexp:\n\n"); len = strlen(buffer); regerror(errcode, re, buffer + len, sizeof buffer - len); popup_notice("regexpnotice", buffer, "OK", NULL, NULL, 3000, NULL, NULL, XtGrabNone); } void popup_title_notice(char *title, char *body, int beep) { char buffer[1024]; if (!title) buffer[0] = '\0'; else { strcpy(buffer, title); strcat(buffer, ":\n\n"); } strncat(buffer, body, 500); popup_notice("notice", buffer, "OK", NULL, NULL, 10000, NULL, NULL, XtGrabNone); if (beep && global.bell) XBell(display, 0); } void stderr_popup(char *stderr_buf, long timeout) { if (timeout < 0) timeout = global.stderr_timeout; if (stderr_buf && stderr_buf[0] != '\0' && timeout >= 0) popup_notice("stderr", stderr_buf, "Ok", NULL, NULL, timeout, NULL, NULL, XtGrabNone); } /*************************************************************************/ Widget create_simple_menu(Widget parent, char *prefix, int size, XtCallbackProc callback, void *client_data) { char name[128]; Widget shell, menu, temp; Arg args[4]; int i; if (size <= 0) return NULL; XtSetArg(args[0], XtNcolormap, global.cmap); XtSetArg(args[1], XtNvisual, global.visual); XtSetArg(args[2], XtNdepth, global.depth); sprintf(name, "%sshell", prefix); shell = XtCreatePopupShell(name, menuShellWidgetClass, parent, args, 3); sprintf(name, "%smenu", prefix); menu = XtCreateManagedWidget(name, menuWidgetClass, shell, NULL, 0); for (i = 0 ; i < size ; i++) { sprintf(name, "%s%d", prefix, i); temp = MenuCreateGadget(name, stringGadgetClass, menu, NULL, 0); XtAddCallback(temp, XtNcallback, callback, client_data); } return shell; } ./knews-1.0b.1/src/ahead.h100644 1244 1244 647 6455455544 13627 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ extern char thread_ahead_char(GROUP*); extern void thread_ahead_shutdown(void); extern int thread_ahead_check(GROUP*); extern void thread_ahead_leave_group(GROUP*); extern void thread_ahead_init(void); extern int thread_ahead_todo(void); extern void action_schedule_thread_ahead(Widget w, XEvent *event, String *params, Cardinal *no_params); ./knews-1.0b.1/src/color.c100644 1244 1244 34341 6455455544 13734 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include "global.h" #include "../Widgets/Compat.h" #include "color.h" #include "util.h" #include "widgets.h" static void set_defaults(Screen *screen) { global.visual = DefaultVisualOfScreen(screen); global.cmap = DefaultColormapOfScreen(screen); global.depth = DefaultDepthOfScreen(screen); } static int find_visual(Display *disp, Screen *screen, int class, int depth) { XVisualInfo info; int screen_no = XScreenNumberOfScreen(screen); if (!XMatchVisualInfo(disp, screen_no, depth, class, &info)) return False; global.visual = info.visual; return True; } static char *get_resource(XrmDatabase db, char *res_name, char *res_class) { XrmValue val; XrmRepresentation rep; XrmQuark name[3], class[3]; name[0] = XrmPermStringToQuark("knews"); name[1] = XrmPermStringToQuark(res_name); name[2] = 0; class[0] = XrmPermStringToQuark("Knews"); class[1] = XrmPermStringToQuark(res_class); class[2] = 0; if (XrmQGetResource(db, name, class, &rep, &val)) return (char *)val.addr; return NULL; } static int str_to_class(char *class) { #define DO(name) if (strcmp(class, #name) == 0) return name; DO(StaticGray); DO(GrayScale); DO(StaticColor); DO(PseudoColor); DO(TrueColor); DO(DirectColor); #undef DO return -1; } void color_init(Display *disp) { Screen *screen = DefaultScreenOfDisplay(disp); XrmDatabase db = XtScreenDatabase(screen); char *visual_class = NULL; char *visual_depth = NULL; char *install = NULL; int depth, class; set_defaults(screen); if (!db) return; visual_class = get_resource(db, "visualClass", "VisualClass"); visual_depth = get_resource(db, "visualDepth", "VisualDepth"); if (!visual_depth) depth = global.depth; else if (sscanf(visual_depth, "%d", &depth) != 1) { fprintf(stderr, "knews: visualDepth is not a number: %s\n", visual_depth); return; } if (!visual_class) class = -1; else if ((class = str_to_class(visual_class)) < 0) { fprintf(stderr, "knews: no visual type called %s.\n", visual_class); return; } if (visual_class) { if (!find_visual(disp, screen, class, depth)) { fprintf(stderr, "knews: couldn't find %d-bit %s visual, using default.\n", depth, visual_class); return; } } else if (visual_depth) { fputs("knews: visualDepth given without visualClass, ignoring.\n", stderr); return; } else { install = get_resource(db, "installCmap", "InstallCmap"); if (!install || case_lstrcmp(install, "true") != 0) return; } global.cmap = XCreateColormap(disp, RootWindowOfScreen(screen), global.visual, AllocNone); } /*********************************************************************/ CMAP_ENTRY *cmap = NULL; int cmap_size = 0; static unsigned int r_sh_l = 0; static unsigned int r_sh_r = 0; static unsigned int g_sh_l = 0; static unsigned int g_sh_r = 0; static unsigned int b_sh_l = 0; static unsigned int b_sh_r = 0; static unsigned long red_mask; static unsigned long green_mask; static unsigned long blue_mask; #define RGB_TO_PIXEL(r, g, b) ((((r) >> r_sh_r) << r_sh_l) | \ (((g) >> g_sh_r) << g_sh_l) | \ (((b) >> b_sh_r) << b_sh_l)) #define N_GREYS 17 static void disable_images(char *msg) { fprintf(stderr, "knews: %s, disabling inline images.\n", msg); global.inline_images = False; } static void free_pixels(CMAP_ENTRY *cols, unsigned int n) { unsigned long pixels[256]; unsigned int i; if (n == 0) return; for (i = 0 ; i < n ; i++) pixels[i] = cols[i].pixel; XFreeColors(display, global.cmap, pixels, n, 0); } static void set_shifts(unsigned long mask, unsigned int *lp, unsigned int *rp) { unsigned int n_ones = 0, n_zeroes = 0; while (!(mask & 1)) { mask >>= 1; n_zeroes++; } mask++; while (!(mask & 1)) { mask >>= 1; n_ones++; } if (n_ones < 8) { *rp = 8 - n_ones; *lp = n_zeroes; } else { *rp = 0; *lp = n_zeroes + n_ones - 8; } } static void query_cmap(void) { XColor cols[256]; unsigned int i; cmap_size = global.visual->map_entries; if (cmap_size > 256) /* should not happen */ cmap_size = 256; for (i = 0 ; i < cmap_size ; i++) { cols[i].pixel = i; cols[i].flags = DoRed|DoGreen|DoBlue; } XQueryColors(display, global.cmap, cols, cmap_size); cmap = (CMAP_ENTRY *)XtMalloc(cmap_size * sizeof cmap[0]); for (i = 0 ; i < cmap_size ; i++) { cmap[i].pixel = cols[i].pixel; cmap[i].r = cols[i].red / 256; cmap[i].g = cols[i].green / 256; cmap[i].b = cols[i].blue / 256; } } static int alloc_greys(CMAP_ENTRY *cmap, unsigned int n) { XColor cols[256]; unsigned int i; for (i = 0 ; i < n ; i++) { cols[i].red = cols[i].green = cols[i].blue = i * 65535ul / (n - 1); cols[i].flags = DoRed|DoGreen|DoBlue; } for (i = 0 ; i < n ; i++) if (XAllocColor(display, global.cmap, cols + i)) { cmap[i].pixel = cols[i].pixel; cmap[i].r = cmap[i].g = cmap[i].b = (cols[i].red + cols[i].green + cols[i].blue) / (3 * 256); } else { free_pixels(cmap, i); return False; } return True; } static int alloc_grey_ramp(unsigned int max_size) { cmap_size = global.visual->map_entries; if (cmap_size > max_size) cmap_size = max_size; if (cmap_size > 256) cmap_size = 256; cmap = (CMAP_ENTRY *)XtMalloc(cmap_size * sizeof cmap[0]); while (cmap_size >= N_GREYS) if (alloc_greys(cmap, cmap_size)) break; else cmap_size -= 8; if (cmap_size >= N_GREYS) return True; disable_images("Couldn't allocate greys"); XtFree((char *)cmap); cmap = NULL; cmap_size = 0; return False; } static unsigned long rgb_dist(const CMAP_ENTRY *c1, const CMAP_ENTRY *c2) { unsigned long d = 0; int tmp; tmp = (int)c1->r - (int)c2->r; d += tmp * tmp; tmp = (int)c1->g - (int)c2->g; d += tmp * tmp; tmp = (int)c1->b - (int)c2->b; d += tmp * tmp; return d; } static void compute_dither(unsigned char *dither, CMAP_ENTRY *from, unsigned int n_from, CMAP_ENTRY *to, unsigned int n_to) { unsigned int i, j; for (i = 0 ; i < n_from ; i++) { unsigned int min = 0; unsigned long d = rgb_dist(from + i, to); for (j = 1 ; j < n_to ; j++) { unsigned long d1 = rgb_dist(from + i, to + j); if (d1 < d) { d = d1; min = j; } } dither[i] = to[min].pixel; } } static int alloc_rgb_cube(CMAP_ENTRY *cols, unsigned int n_reds, unsigned int n_greens, unsigned int n_blues) { XColor col; unsigned int r, g, b, n = 0; n_reds--; n_greens--; n_blues--; for (r = 0 ; r <= n_reds ; r++) for (g = 0 ; g <= n_greens ; g++) for (b = 0 ; b <= n_blues ; b++) { col.red = r * 65535ul / n_reds; col.green = g * 65535ul / n_greens; col.blue = b * 65535ul / n_blues; if (col.red == col.green && col.green == col.blue) continue; if (!XAllocColor(display, global.cmap, &col)) { free_pixels(cols, n); #if 0 fprintf(stderr, "knews: Failed to alloc %ux%ux%u RGB " "cube.\n", ++n_reds, ++n_greens, ++n_blues); #endif return 0; } cols[n].pixel = col.pixel; cols[n].r = col.red / 256; cols[n].g = col.green / 256; cols[n].b = col.blue / 256; n++; } return n; } static int alloc_rgb_colors(int max_size) { static struct { unsigned char n_r, n_g, n_b; } c[] = { {7, 8, 4}, {6, 6, 6}, {6, 7, 5}, {4, 8, 4}, {5, 5, 5}, {4, 4, 4}, {4, 4, 3}, {3, 4, 3}, {3, 3, 3}, }; XColor col; unsigned int i, n = 0; cmap = (CMAP_ENTRY *)XtMalloc(256 * sizeof cmap[0]); for (n = 0 ; n < N_GREYS ; n++) { col.red = col.green = col.blue = n * 65535ul / (N_GREYS - 1); col.flags = DoRed|DoGreen|DoBlue; if (!XAllocColor(display, global.cmap, &col)) { free_pixels(cmap, n); XtFree((char *)cmap); cmap = NULL; return False; } cmap[n].pixel = col.pixel; cmap[n].r = col.red / 256; cmap[n].g = col.green / 256; cmap[n].b = col.blue / 256; } max_size -= n; for (i = 0 ; i < XtNumber(c) ; i++) { int cn = c[i].n_r * c[i].n_g * c[i].n_b; if (cn > max_size) continue; cn = alloc_rgb_cube(cmap + n, c[i].n_r, c[i].n_g, c[i].n_b); if (cn == 0) continue; n += cn; break; } cmap_size = n; cmap = (CMAP_ENTRY *)XtRealloc((char *)cmap, cmap_size * sizeof cmap[0]); return True; } static void color_hack(unsigned int max_size) { XColor cols[256]; unsigned int i; cmap_size = max_size; if (cmap_size > 256) cmap_size = 256; for (i = 0 ; i < cmap_size ; i++) { cols[i].pixel = i; cols[i].flags = DoRed|DoGreen|DoBlue; } XQueryColors(display, global.cmap, cols, cmap_size); cmap = (CMAP_ENTRY *)XtMalloc(cmap_size * sizeof cmap[0]); for (i = 0 ; i < cmap_size ; i++) { (void)XAllocColor(display, global.cmap, cols + i); cmap[i].pixel = cols[i].pixel; cmap[i].r = cols[i].red / 256; cmap[i].g = cols[i].green / 256; cmap[i].b = cols[i].blue / 256; } } void alloc_colors(void) { red_mask = global.visual->red_mask; green_mask = global.visual->green_mask; blue_mask = global.visual->blue_mask; if (!global.inline_images || global.n_cols <= 0) { global.n_cols = 0; global.inline_images = False; return; } if (global.depth < 4) { disable_images("display depth too small"); return; } if (global.n_cols > 256) global.n_cols = 256; switch (global.visual->class) { case StaticGray: if (global.depth > 8) { disable_images("StaticGray visual deeper than 8 bits"); return; } query_cmap(); break; case GrayScale: if (global.color_hack) color_hack(global.n_cols); else if (!alloc_grey_ramp(global.n_cols)) { fputs("knews: Couldn't allocate sufficient colors, " "trying colorHack.\n", stderr); color_hack(global.n_cols); } break; case StaticColor: if (global.depth > 8) { disable_images("StaticColor visual deeper than 8 bits"); return; } query_cmap(); break; case PseudoColor: if (global.color_hack) color_hack(global.n_cols); else if (!alloc_rgb_colors(global.n_cols)) { fputs("knews: Couldn't allocate sufficient colors, " "trying colorHack.\n", stderr); color_hack(global.n_cols); } break; case TrueColor: set_shifts(red_mask, &r_sh_l, &r_sh_r); set_shifts(green_mask, &g_sh_l, &g_sh_r); set_shifts(blue_mask, &b_sh_l, &b_sh_r); break; case DirectColor: disable_images("DirectColor visuals are weird"); break; } #if 0 if (cmap_size > 0) fprintf(stderr, "knews: allocated %d colors.\n", cmap_size); #endif } /*********************************************************************/ Pixmap put_8_image(unsigned char *pic, long w, long h) { Pixmap pixmap; XImage image; /* * Hope this works... */ image.width = w; image.height = h; image.xoffset = 0; image.format = ZPixmap; image.data = (char *)pic; image.byte_order = ImageByteOrder(display); image.bitmap_unit = BitmapUnit(display); image.bitmap_bit_order = BitmapBitOrder(display); image.bitmap_pad = 8; image.depth = global.depth; image.bytes_per_line = w; image.bits_per_pixel = 8; image.red_mask = red_mask; image.green_mask = green_mask; image.blue_mask = blue_mask; image.obdata = NULL; image.f.create_image = NULL; image.f.destroy_image = NULL; image.f.get_pixel = NULL; image.f.put_pixel = NULL; image.f.sub_image = NULL; image.f.add_pixel = NULL; pixmap = XCreatePixmap(display, XtWindow(main_widgets.shell), w, h, global.depth); XPutImage(display, pixmap, global.gc, &image, 0, 0, 0, 0, w, h); return pixmap; } static XImage *alloc_ximage(long w, long h) { XImage *img; img = XCreateImage(display, global.visual, global.depth, ZPixmap, 0, NULL, w, h, BitmapPad(display), 0); img->data = XtMalloc(h * img->bytes_per_line); return img; } static Pixmap image_to_pixmap(XImage *img, long w, long h) { Window win = XtWindow(main_widgets.shell); Pixmap pixmap = XCreatePixmap(display, win, w, h, global.depth); XPutImage(display, pixmap, global.gc, img, 0, 0, 0, 0, w, h); XDestroyImage(img); return pixmap; } /* * Put TrueColor image. */ Pixmap put_24_image(unsigned char *pic, long w, long h) { XImage *img = alloc_ximage(w, h); long x, y; for (y = 0 ; y < h ; y++) for (x = 0 ; x < w ; x++, pic += 3) XPutPixel(img, x, y, RGB_TO_PIXEL(pic[0], pic[1], pic[2])); return image_to_pixmap(img, w, h); } Pixmap put_grey_image(unsigned char *pic, long w, long h) { if (cmap) { unsigned char dither[256]; CMAP_ENTRY greys[256]; long i, n = w * h; for (i = 0 ; i < 256 ; i++) { greys[i].pixel = i; greys[i].r = greys[i].g = greys[i].b = i; } compute_dither(dither, greys, 256, cmap, cmap_size); for (i = 0 ; i < n ; i++) pic[i] = dither[pic[i]]; return put_8_image(pic, w, h); } else { XImage *img = alloc_ximage(w, h); long x, y; for (y = 0 ; y < h ; y++) for (x = 0 ; x < w ; x++, pic++) XPutPixel(img, x, y, RGB_TO_PIXEL(*pic, *pic, *pic)); return image_to_pixmap(img, w, h); } } Pixmap put_cmapped_image(unsigned char *pic, long w, long h, CMAP_ENTRY *pal, unsigned int pal_size) { if (cmap) { long i, n = w * h; unsigned char dither[256] = {0, }; compute_dither(dither, pal, pal_size, cmap, cmap_size); for (i = 0 ; i < n ; i++) pic[i] = dither[pic[i]]; return put_8_image(pic, w, h); } else { XImage *img = alloc_ximage(w, h); long x, y; for (y = 0 ; y < h ; y++) for (x = 0 ; x < w ; x++, pic++) XPutPixel(img, x, y, RGB_TO_PIXEL(pal[*pic].r, pal[*pic].g, pal[*pic].b)); return image_to_pixmap(img, w, h); } } Pixel get_closest_color(XColor *col) { CMAP_ENTRY rgb; unsigned char dither; if (!global.inline_images) return global.default_hot_pixel; rgb.r = col->red / 256; rgb.g = col->green / 256; rgb.b = col->blue / 256; if (!cmap) return RGB_TO_PIXEL(rgb.r, rgb.g, rgb. b); else { compute_dither(&dither, &rgb, 1, cmap, cmap_size); return dither; } } ./knews-1.0b.1/src/color.h100644 1244 1244 1127 6455455544 13715 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ typedef struct { unsigned char pixel; unsigned char r; unsigned char g; unsigned char b; } CMAP_ENTRY; extern CMAP_ENTRY *cmap; extern int cmap_size; extern void color_init(Display*); extern void alloc_colors(void); extern Pixmap put_8_image(unsigned char*, long, long); extern Pixmap put_24_image(unsigned char*, long, long); extern Pixmap put_grey_image(unsigned char*, long, long); extern Pixmap put_cmapped_image(unsigned char*, long, long, CMAP_ENTRY*, unsigned int); extern Pixel get_closest_color(XColor*); ./knews-1.0b.1/src/decode.c100644 1244 1244 13115 6455455544 14035 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include "global.h" #include "decode.h" #include "util.h" static int rest_is_whitespace(const char *rest, long len) { while (len-- > 0) { if (!IS_SPACE(*rest)) return False; rest++; } return True; } static int get_hex(char *c) { int hex_no; if (IS_DIGIT(*c)) hex_no = *c - '0'; else if ('A' <= *c && *c <= 'F') hex_no = *c - 'A' + 10; else return -1; c++; hex_no *= 16; if (IS_DIGIT(*c)) hex_no += *c - '0'; else if ('A' <= *c && *c <= 'F') hex_no += *c - 'A' + 10; else return -1; return hex_no; } long decode_qp(char *dest, char *src, long len, int *soft, int underscore) { long src_pos, dest_pos; int hex_no; if (soft) *soft = False; src_pos = dest_pos = 0; while (len > 0) { if (src[src_pos] == '=') { if (len >= 2 && (hex_no = get_hex(src + src_pos + 1)) >= 0) { dest[dest_pos++] = hex_no; src += 3; len -= 3; continue; } else if (rest_is_whitespace(src + src_pos + 1, len - 1)) { if (soft) *soft = True; break; } } else if (src[src_pos] == '_') { if (underscore) { dest[dest_pos++] = ' '; src++; len--; continue; } } else if (IS_SPACE(src[src_pos]) && rest_is_whitespace(src + src_pos + 1, len - 1)) break; dest[dest_pos++] = src[src_pos++]; len--; } return dest_pos; } /*************************************************************************/ const char base64_alpha[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; const char inv_base64_alpha[256] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, }; long decode_base64(B64Context *cont, char *dest, char *src, long len) { unsigned long data; unsigned int n; char *base = dest; if (cont->end) { if (src && *src != '\0') cont->trailing_garbage = True; return 0; } data = cont->data; n = cont->n; if (!src) cont->end = True; while (len-- > 0 && !cont->end) { int c = (unsigned char)*src++; if (c == '=') { cont->end = True; break; } c = inv_base64_alpha[c]; if (c < 0) { cont->err = True; continue; } data <<= 6; data |= c; n++; if (n == 4) { *dest++ = (data >> 16) & 0xff; *dest++ = (data >> 8) & 0xff; *dest++ = data & 0xff; n = 0; data = 0; } } if (cont->end) { switch (n) { case 3: *dest++ = (data >> 10) & 0xff; *dest++ = (data >> 2) & 0xff; break; case 2: *dest++ = (data >> 4) & 0xff; break; case 0: break; default: cont->err = True; break; } cont->data = 0; cont->n = 0; } else { cont->data = data; cont->n = n; } return dest - base; } int base64_status(B64Context *cont) { int res = 0; if (cont->err) res |= BASE64_ERROR; if (cont->trailing_garbage) res |= BASE64_TRAILING_GARBAGE; return res; } /*********************************************************************/ enum { LookingForBegin = 0, Uudecoding, FoundEnd }; long decode_uue(UueContext *cont, char *dest, char *src, long len) { char *base = dest; unsigned long data; unsigned int n; switch (cont->state) { case LookingForBegin: if (len >= 6 && strncmp(src, "begin ", 6) == 0) cont->state = Uudecoding; break; case Uudecoding: if (len == 3 && strncmp(src, "end", 3) == 0) { cont->state = FoundEnd; break; } if (len == 0 || *src < ' ' || *src > ' ' + 64) { cont->err = True; break; } n = *src++ - ' '; n &= 63; if (n * 4 / 3 > len - 1) { cont->err = True; break; } len = n; data = 0; n = 0; while (len > 0) { unsigned int tmp = (*src++ - ' ') & 63; data <<= 6; data |= tmp; n++; if (n == 4) { if (len < 3) break; *dest++ = (data >> 16) & 0xff; *dest++ = (data >> 8) & 0xff; *dest++ = data & 0xff; data = 0; n = 0; len -= 3; } } if (!cont->err) switch (len) { case 2: if (n < 3) cont->err = True; else { *dest++ = (data >> 16) & 0xff; *dest++ = (data >> 8) & 0xff; } break; case 1: if (n < 2) cont->err = True; else { if (n == 2) data <<= 6; *dest++ = (data >> 16) & 0xff; } break; } *dest = '\0'; return dest - base; case FoundEnd: break; } return -1; } int uue_status(UueContext *cont) { int res = 0; switch (cont->state) { case LookingForBegin: res = UUE_NO_BEGIN; break; case Uudecoding: res = UUE_NO_END; break; } if (cont->err) res |= UUE_ERROR; return res; } ./knews-1.0b.1/src/decode.h100644 1244 1244 1361 6455455544 14022 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ extern const char base64_alpha[64]; extern const char inv_base64_alpha[256]; #define BASE64_ERROR 1u #define BASE64_TRAILING_GARBAGE 2u #define UUE_NO_BEGIN 1u #define UUE_NO_END 2u #define UUE_ERROR 4u typedef struct { unsigned char state; unsigned char err; } UueContext; typedef struct { unsigned long data; unsigned int n; unsigned char end; unsigned char err; unsigned char trailing_garbage; } B64Context; extern long decode_qp(char*, char*, long, int*, int); extern long decode_base64(B64Context*, char*, char*, long); extern int base64_status(B64Context*); extern long decode_uue(UueContext*, char*, char*, long); extern int uue_status(UueContext*); ./knews-1.0b.1/src/xutil.h100644 1244 1244 2452 6455455544 13746 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ extern void set_curr_art(ARTICLE*, int); extern void set_curr_subj(SUBJECT*); extern void set_curr_group(void); extern void set_tree_stuff(ARTICLE*); extern void set_standard_message(void); extern void set_busy(int); extern void unset_busy(void); extern void change_interruptible(int); extern int global_cleanup(int, int); extern void stderr_popup(char*, long); extern void add_rescan_timeout(void); extern void remove_rescan_timeout(void); extern void check_if_rescan_due(void); extern void disconnect(int); extern char *do_update(void); extern void realize_fake(ARTICLE*, char**, int); extern Boolean cvt_string_to_pixmap(Display*, XrmValue*, Cardinal*, XrmValue*, XrmValue*, XtPointer*); extern void destroy_pixmap(XtAppContext, XrmValue*, XtPointer, XrmValue*, Cardinal*); extern Widget popup_notice(char*, char*, char*, char*, char*, long, XtCallbackProc, XtPointer, XtGrabKind); extern Widget popup_dialogue(char*, char*, char*, char*, char*, XtCallbackProc, XtPointer, XtGrabKind); extern void popup_colornotice(int); extern void popup_regexpnotice(int, const regex_t*); extern void popup_title_notice(char*, char*, int); extern Widget create_simple_menu(Widget, char*, int, XtCallbackProc, void*); ./knews-1.0b.1/src/misc.c100644 1244 1244 76540 6570003001 13527 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include "global.h" #include #include "actions.h" #include "cache.h" #include "codes.h" #include "connect.h" #include "misc.h" #include "newsrc.h" #include "partial.h" #include "procs.h" #include "read.h" #include "resource.h" #include "save.h" #include "search.h" #include "server.h" #include "tag.h" #include "thread.h" #include "util.h" #include "uudecode.h" #include "widgets.h" #include "xutil.h" #include "../Widgets/ArtText.h" #include "../Widgets/ArtTree.h" #include "../Widgets/Dialogue.h" #include "../Widgets/Menu.h" #include "../Widgets/MenuShell.h" #include "../Widgets/Message.h" #include "../Widgets/Notice.h" #include "../Widgets/PullRight.h" #include "../Widgets/ScrBar.h" #include "../Widgets/ScrList.h" #include "../Widgets/Scrollable.h" #include "../Widgets/SeparatorG.h" #include "../Widgets/StringG.h" #include "../Widgets/ToggleG.h" #include "../Widgets/Util.h" #undef EXTRA_MENU #define EXTRA_MENU 0 typedef enum { MarkScopeArticle, MarkScopeSubject, MarkScopeThread, MarkScopeSubthread, MarkScopeTagged, MarkScopeAllarticles, MarkScopeKilled, MarkScopeToCurrent, MarkScopeNonTagged, MarkScopeCold } MarkScope; static Widget misc_menu1; static Widget misc_menu2; static Widget head_toggle; static Widget keep_thr_toggle; static Widget subs_unsubs; static Widget ask_how_many; static Widget msgid_lookup_dialogue; static void msgid_dialogue_callback(Widget, XtPointer, XtPointer); void update_misc_menu(NewsMode mode) { int subscr; switch (mode) { case NewsModeDisconnected: break; case NewsModeConnected: case NewsModeAllgroups: case NewsModeSomegroups: case NewsModeNewgroups: ToggleGadgetSet(ask_how_many, res_ask_how_many()); break; case NewsModeGroup: case NewsModeThread: if (global.curr_group) subscr = global.curr_group->subscribed; else subscr = False; ToggleGadgetSet(head_toggle, res_full_header()); ToggleGadgetSet(keep_thr_toggle, res_keep_thread_info(subscr)); if (global.curr_group) { Arg arg; arg.name = XtNlabel; if (global.curr_group->subscribed) arg.value = (XtArgVal)"Unsubscribe"; else arg.value = (XtArgVal)"Subscribe"; XtSetValues(subs_unsubs, &arg, 1); } break; } } static void toggle_subs_callback(Widget w, XtPointer client_data, XtPointer call_data) { int temp; if (global.busy || !global.curr_group || (global.mode != NewsModeGroup && global.mode != NewsModeThread)) return; temp = global.curr_group->subscribed; global.curr_group->subscribed = !temp; sort_groups(); if (temp) set_message("Unsubscribed.", False); else set_message("Subscribed.", False); update_misc_menu(global.mode); } static void print_callback(Widget w, XtPointer client_data, XtPointer call_data) { String params[3]; Cardinal no_params; if (global.busy || (global.mode != NewsModeGroup && global.mode != NewsModeThread)) return; if (!global.curr_art) { set_message("No selected article!", True); return; } params[0] = global.print_command; params[1] = "heb"; no_params = 2; action_pipe(w, NULL, params, &no_params); } static void full_header_callback(Widget w, XtPointer client_data, XtPointer call_data) { if (global.busy || (global.mode != NewsModeGroup && global.mode != NewsModeThread)) return; if (global.curr_art) read_article(global.curr_art, True, NULL, NULL); else set_message("No selected article!", True); } static void rot13_callback(Widget w, XtPointer client_data, XtPointer call_data) { if (global.busy || (global.mode != NewsModeGroup && global.mode != NewsModeThread)) return; ArtTextRot13(main_widgets.text); } static void clear_tags_callback(Widget w, XtPointer client_data, XtPointer call_data) { if (global.busy ||(global.mode != NewsModeGroup && global.mode != NewsModeThread)) return; clear_tagged_articles(); set_message("Cleared tagged articles.", False); } static void tag_hot_callback(Widget w, XtPointer client_data, XtPointer call_data) { if (global.busy || (global.mode != NewsModeGroup && global.mode != NewsModeThread)) return; tag_hot_articles(); set_standard_message(); } static void show_cache_callback(Widget w, XtPointer client_data, XtPointer call_data) { popup_cache_stats(); } static void toggle_head_callback(Widget w, XtPointer client_data, XtPointer call_data) { if (global.busy || !call_data || (global.mode != NewsModeGroup && global.mode != NewsModeThread)) return; res_set_full_header(*(Boolean *)call_data); } static void keep_thr_callback(Widget w, XtPointer client_data, XtPointer call_data) { if (global.busy || !call_data || (global.mode != NewsModeGroup && global.mode == NewsModeThread)) return; res_set_keep_thread_info(*(Boolean *)call_data); } static void mark_read_callback(Widget w, XtPointer client_data, XtPointer call_data) { MarkScope scope = (MarkScope)client_data; ARTICLE *art = global.curr_art; ARTICLE **arts; SUBJECT *subj; long n; int xref = res_process_xrefs(); if (global.busy || (global.mode != NewsModeGroup && global.mode != NewsModeThread)) return; if (art) subj = art->subject; else subj = global.curr_subj; set_busy(False); switch (scope) { case MarkScopeArticle: if (!art) set_message("No selected article!", True); else if (!art->from) set_message("That's a fake article!", True); else { if (!art->read) { art->read = True; subj->no_unread--; global.curr_group->no_unread--; if (xref) process_xref(art); if (art->pixmap != None) { global.n_hot--; update_subj_hot_value(subj); } update_subj_entry(subj); if (global.mode == NewsModeThread) { ArtTreeNodeSetInner(main_widgets.arttree, (ART_TREE_NODE *)art, False); ArtTreeNodeSetPixmap(main_widgets.arttree, (ART_TREE_NODE *)art, None); } } set_standard_message(); } break; case MarkScopeSubject: if (!subj) set_message("No selected subject!", True); else { mark_subject_read(subj, xref, False); update_subj_hot_value(subj); update_subj_entry(subj); if (global.mode == NewsModeThread) setNewsModeThread(); set_standard_message(); } break; case MarkScopeThread: if (!subj) set_message("No selected subject!", True); else { art = subj->thread; while (subj->prev && subj->prev->thread == art) subj = subj->prev; while (subj && subj->thread == art) { mark_subject_read(subj, xref, False); update_subj_hot_value(subj); update_subj_entry(subj); subj = subj->next; } if (global.mode == NewsModeThread) setNewsModeThread(); set_standard_message(); } break; case MarkScopeSubthread: if (!art) set_message("No selected article!", True); else if (!art->from) set_message("That's a fake article!", True); else { SUBJECT *loop = subj; while (loop->prev && loop->prev->thread == subj->thread) loop = loop->prev; while (loop && loop->thread == subj->thread) { mark_sub_subject_read(art, loop, xref, False); update_subj_hot_value(loop); update_subj_entry(loop); loop = loop->next; } if (global.mode == NewsModeThread) setNewsModeThread(); set_standard_message(); } break; case MarkScopeTagged: n = no_tagged_articles(); if (n <= 0) set_message("No tagged articles!", True); else { long i; arts = get_tagged_articles(); for (i = 0 ; i < n ; i++) if (arts[i]->from && !arts[i]->read) { arts[i]->read = True; arts[i]->subject->no_unread--; global.curr_group->no_unread--; if (xref) process_xref(arts[i]); if (arts[i]->pixmap != None) { global.n_hot--; update_subj_hot_value(arts[i]->subject); } update_subj_entry(arts[i]->subject); if (global.mode == NewsModeThread) { ArtTreeNodeSetInner(main_widgets.arttree, (ART_TREE_NODE *)arts[i], False); ArtTreeNodeSetPixmap(main_widgets.arttree, (ART_TREE_NODE *)arts[i], None); } } clear_tagged_articles(); set_standard_message(); } break; case MarkScopeAllarticles: for (art = get_articles(main_thr) ; art ; art = art->next) { if (xref) process_xref(art); art->read = True; } for (subj = get_subjects(main_thr) ; subj ; subj = subj->next) { subj->no_unread = 0; subj->pixmap = None; } global.curr_group->no_unread = 0; global.curr_subj = NULL; global.curr_art = NULL; global.n_hot = 0; global.mode = NewsModeGroup; /* in case we're in NewsModeThread */ setNewsModeGroup(False); set_standard_message(); break; case MarkScopeKilled: /*notreached*/ break; case MarkScopeToCurrent: if (!subj) set_message("No selected subject!", True); else { SUBJECT *loop; art = subj->thread; while (subj && subj->thread == art) subj = subj->next; for (loop = get_subjects(main_thr) ; loop && loop != subj ; loop = loop->next) { mark_subject_read(loop, xref, False); update_subj_hot_value(loop); update_subj_entry(loop); } if (global.mode == NewsModeThread) setNewsModeThread(); set_standard_message(); } break; case MarkScopeNonTagged: n = no_tagged_articles(); arts = get_tagged_articles(); for (subj = get_subjects(main_thr) ; subj ; subj = subj->next) { if (!subj->has_tagged) mark_subject_read(subj, xref, False); else { long no = 0; for (art = subj->thread ; art ; art = next_in_thread_preorder(art)) { long i; if (!art->from || art->subject != subj || art->read) continue; for (i = 0 ; i < n ; i++) if (arts[i] == art) break; if (i == n) { art->read = True; if (art->pixmap != None) global.n_hot--; no++; } } subj->no_unread -= no; global.curr_group->no_unread -= no; } update_subj_hot_value(subj); update_subj_entry(subj); } if (global.mode == NewsModeThread) setNewsModeThread(); set_standard_message(); break; case MarkScopeCold: n = 0; for (art = get_articles(main_thr) ; art ; art = art->next) if (art->from && !art->read && art->pixmap == None) { art->read = True; art->subject->no_unread--; n++; } global.curr_group->no_unread -= n; for (subj = get_subjects(main_thr) ; subj ; subj = subj->next) { update_subj_hot_value(subj); update_subj_entry(subj); } if (global.mode == NewsModeThread) setNewsModeThread(); set_standard_message(); break; } unset_busy(); } static void mark_unread_callback(Widget w, XtPointer client_data, XtPointer call_data) { MarkScope scope = (MarkScope)client_data; ARTICLE *art = global.curr_art; SUBJECT *subj = global.curr_subj; long n; if (global.busy || (global.mode != NewsModeGroup && global.mode != NewsModeThread)) return; if (art) subj = art->subject; set_busy(False); switch (scope) { case MarkScopeArticle: if (!art) set_message("No selected article!", True); else if (!art->from) set_message("That's a fake article!", True); else { if (art->read) { art->read = False; subj->no_unread++; global.curr_group->no_unread++; if (art->pixmap != None) { global.n_hot++; update_subj_hot_value(subj); } update_subj_entry(subj); if (global.mode == NewsModeThread) { ArtTreeNodeSetInner(main_widgets.arttree, (ART_TREE_NODE *)art, True); if (art->pixmap != None) ArtTreeNodeSetPixmap(main_widgets.arttree, (ART_TREE_NODE *)art, art->pixmap); } } set_standard_message(); } break; case MarkScopeSubject: if (!subj) set_message("No selected subject!", True); else { mark_subject_unread(subj); update_subj_hot_value(subj); update_subj_entry(subj); if (global.mode == NewsModeThread) setNewsModeThread(); set_standard_message(); } break; case MarkScopeThread: if (!subj) set_message("No selected subject!", True); else { art = subj->thread; while (subj->prev && subj->prev->thread == art) subj = subj->prev; while (subj && subj->thread == art) { mark_subject_unread(subj); update_subj_hot_value(subj); update_subj_entry(subj); subj = subj->next; } if (global.mode == NewsModeThread) setNewsModeThread(); set_standard_message(); } break; case MarkScopeSubthread: if (!art) set_message("No selected article!", True); else if (!art->from) set_message("That's a fake article!", True); else { SUBJECT *loop = subj; while (loop->prev && loop->prev->thread == subj->thread) loop = loop->prev; while (loop && loop->thread == subj->thread) { mark_sub_subject_unread(art, loop); update_subj_hot_value(loop); update_subj_entry(loop); loop = loop->next; } if (global.mode == NewsModeThread) setNewsModeThread(); set_standard_message(); } break; case MarkScopeTagged: n = no_tagged_articles(); if (n <= 0) set_message("No tagged articles!", True); else { ARTICLE **arts = get_tagged_articles(); long i; for (i = 0 ; i < n ; i++) if (arts[i]->from && arts[i]->read) { arts[i]->read = False; arts[i]->subject->no_unread++; global.curr_group->no_unread++; if (arts[i]->pixmap != None) { global.n_hot++; update_subj_hot_value(arts[i]->subject); } update_subj_entry(arts[i]->subject); if (global.mode == NewsModeThread) { ArtTreeNodeSetInner(main_widgets.arttree, (ART_TREE_NODE *)arts[i], True); if (arts[i]->pixmap != None) ArtTreeNodeSetPixmap(main_widgets.arttree, (ART_TREE_NODE *)arts[i], arts[i]->pixmap); } } clear_tagged_articles(); set_standard_message(); } break; case MarkScopeAllarticles: for (subj = get_subjects(main_thr) ; subj ; subj = subj->next) { subj->no_unread = 0; subj->pixmap = 0; } global.n_hot = 0; for (art = get_articles(main_thr) ; art ; art = art->next) art->read = True; for (n = 0, subj = get_subjects(main_thr) ; subj ; subj = subj->next) { if (subj->prev && subj->prev->thread == subj->thread) continue; for (art = subj->thread ; art ; art = next_in_thread_wrap(art)) { if (art->from && art->read) { art->read = False; art->subject->no_unread++; n++; if (art->pixmap != None) { global.n_hot++; if (art->subject->pixmap == None) art->subject->pixmap = art->pixmap; } } } } global.curr_group->no_unread = n; global.curr_subj = NULL; global.curr_art = NULL; global.mode = NewsModeGroup; /* in case we're in NewsModeThread */ setNewsModeGroup(False); set_standard_message(); break; case MarkScopeKilled: for (n = 0, art = get_articles(main_thr) ; art ; art = art->next) { if (art->read && art->killed && art->from) { art->read = False; if (art->pixmap != None) global.n_hot++; /* notreached? */ art->subject->no_unread++; n++; } } global.curr_group->no_unread += n; global.curr_subj = NULL; global.curr_art = NULL; global.mode = NewsModeGroup; /* in case we're in NewsModeThread */ setNewsModeGroup(False); set_standard_message(); break; case MarkScopeToCurrent: /* not reached */ case MarkScopeNonTagged: case MarkScopeCold: break; } unset_busy(); } static void unsubscribe_callback(Widget w, XtPointer client_data, XtPointer call_data) { action_unsubscribe(w, NULL, NULL, NULL); } static void catchup_callback(Widget w, XtPointer client_data, XtPointer call_data) { action_catchup(w, NULL, NULL, NULL); } static void msgid_lookup_callback(Widget w, XtPointer client_data, XtPointer call_data) { if (global.busy || (global.mode != NewsModeGroup && global.mode != NewsModeThread)) return; if (!msgid_lookup_dialogue) msgid_lookup_dialogue = popup_dialogue("msgidlookup", "Find article by Message-ID", "Find", "Clear", "Close", msgid_dialogue_callback, NULL, XtGrabNone); else if (!is_popped_up(msgid_lookup_dialogue)) XtPopup(msgid_lookup_dialogue, XtGrabNone); } static void add_to_partial_callback(Widget w, XtPointer client_data, XtPointer call_data) { if (global.busy || (global.mode != NewsModeGroup && global.mode != NewsModeThread)) return; partial_build_cache(); } static void findgroup_callback(Widget w, XtPointer client_data, XtPointer call_data) { if (!w) return; popup_find_group(); } static void ask_how_many_callback(Widget w, XtPointer client_data, XtPointer call_data) { Boolean *set = (Boolean *)call_data; if (global.busy || (global.mode != NewsModeConnected && global.mode != NewsModeNewgroups && global.mode != NewsModeAllgroups)) return; if (set) res_set_ask_how_many(*set); } #if EXTRA_MENU static void extra_menu_callback(Widget w, XtPointer client_data, XtPointer call_data) { char *action = StringGadgetCommand(w); if (!action) { set_message("No action specified for that menu entry!", True); return; } popup_title_notice("extra", "Not yet implemented", 3); } #endif void create_misc_menu1(Widget parent) { Widget menu, subshell, submenu; Widget temp; Arg args[4]; XtSetArg(args[0], XtNcolormap, global.cmap); XtSetArg(args[1], XtNvisual, global.visual); XtSetArg(args[2], XtNdepth, global.depth); misc_menu1 = XtCreatePopupShell("miscshell1", menuShellWidgetClass, parent, args, 3); menu = XtCreateManagedWidget("miscmenu1", menuWidgetClass, misc_menu1, NULL, 0); #if EXTRA_MENU if (global.extra_menu_size > 0) { XtSetArg(args[0], XtNmenuName, "extrashell"); temp = MenuCreateGadget("extra", pullRightGadgetClass, menu, args, 1); create_simple_menu(parent, "extra", global.extra_menu_size, extra_menu_callback, NULL); MenuCreateGadget("separator", separatorGadgetClass, menu, NULL, 0); } #endif temp = MenuCreateGadget("fullheader", stringGadgetClass, menu, NULL, 0); XtAddCallback(temp, XtNcallback, full_header_callback, NULL); temp = MenuCreateGadget("rot13", stringGadgetClass, menu, NULL, 0); XtAddCallback(temp, XtNcallback, rot13_callback, NULL); temp = MenuCreateGadget("cleartags", stringGadgetClass, menu, NULL, 0); XtAddCallback(temp, XtNcallback, clear_tags_callback, NULL); temp = MenuCreateGadget("uudecode", stringGadgetClass, menu, NULL, 0); XtAddCallback(temp, XtNcallback, uudecode_callback, NULL); if (global.print_command && global.print_command != '\0') { temp = MenuCreateGadget("print", stringGadgetClass, menu, NULL, 0); XtAddCallback(temp, XtNcallback, print_callback, NULL); } XtSetArg(args[0], XtNlabel, "Unsubscribe"); subs_unsubs = MenuCreateGadget("unsubs", stringGadgetClass, menu, args, 1); XtAddCallback(subs_unsubs, XtNcallback, toggle_subs_callback, NULL); temp = MenuCreateGadget("taghot", stringGadgetClass, menu, NULL, 0); XtAddCallback(temp, XtNcallback, tag_hot_callback, NULL); temp = MenuCreateGadget("addtompcache", stringGadgetClass, menu, NULL, 0); XtAddCallback(temp, XtNcallback, add_to_partial_callback, NULL); temp = MenuCreateGadget("msgidlookup", stringGadgetClass, menu, NULL, 0); XtAddCallback(temp, XtNcallback, msgid_lookup_callback, NULL); temp = MenuCreateGadget("showcache", stringGadgetClass, menu, NULL, 0); XtAddCallback(temp, XtNcallback, show_cache_callback, NULL); MenuCreateGadget("separator", separatorGadgetClass, menu, NULL, 0); XtSetArg(args[0], XtNmenuName, "markreadshell"); MenuCreateGadget("markread", pullRightGadgetClass, menu, args, 1); XtSetArg(args[0], XtNmenuName, "markunreadshell"); MenuCreateGadget("markunread", pullRightGadgetClass, menu, args, 1); MenuCreateGadget("separator", separatorGadgetClass, menu, NULL, 0); head_toggle = MenuCreateGadget("headertoggle", toggleGadgetClass, menu, NULL, 0); XtAddCallback(head_toggle, XtNcallback, toggle_head_callback, NULL); keep_thr_toggle = MenuCreateGadget("thrinfo", toggleGadgetClass, menu, NULL, 0); XtAddCallback(keep_thr_toggle, XtNcallback, keep_thr_callback, NULL); XtSetArg(args[0], XtNcolormap, global.cmap); XtSetArg(args[1], XtNvisual, global.visual); XtSetArg(args[2], XtNdepth, global.depth); subshell = XtCreatePopupShell("markreadshell", menuShellWidgetClass, parent, args, 3); submenu = XtCreateManagedWidget("markreadmenu", menuWidgetClass, subshell, NULL, 0); temp = MenuCreateGadget("article", stringGadgetClass, submenu, NULL, 0); XtAddCallback(temp, XtNcallback, mark_read_callback, (XtPointer)MarkScopeArticle); temp = MenuCreateGadget("subject", stringGadgetClass, submenu, NULL, 0); XtAddCallback(temp, XtNcallback, mark_read_callback, (XtPointer)MarkScopeSubject); temp = MenuCreateGadget("thread", stringGadgetClass, submenu, NULL, 0); XtAddCallback(temp, XtNcallback, mark_read_callback, (XtPointer)MarkScopeThread); temp = MenuCreateGadget("subthread", stringGadgetClass, submenu, NULL, 0); XtAddCallback(temp, XtNcallback, mark_read_callback, (XtPointer)MarkScopeSubthread); temp = MenuCreateGadget("tagged", stringGadgetClass, submenu, NULL, 0); XtAddCallback(temp, XtNcallback, mark_read_callback, (XtPointer)MarkScopeTagged); temp = MenuCreateGadget("allarticles", stringGadgetClass, submenu, NULL, 0); XtAddCallback(temp, XtNcallback, mark_read_callback, (XtPointer)MarkScopeAllarticles); temp = MenuCreateGadget("tocurrent", stringGadgetClass, submenu, NULL, 0); XtAddCallback(temp, XtNcallback, mark_read_callback, (XtPointer)MarkScopeToCurrent); temp = MenuCreateGadget("nontagged", stringGadgetClass, submenu, NULL, 0); XtAddCallback(temp, XtNcallback, mark_read_callback, (XtPointer)MarkScopeNonTagged); temp = MenuCreateGadget("cold", stringGadgetClass, submenu, NULL, 0); XtAddCallback(temp, XtNcallback, mark_read_callback, (XtPointer)MarkScopeCold); XtSetArg(args[0], XtNcolormap, global.cmap); XtSetArg(args[1], XtNvisual, global.visual); XtSetArg(args[2], XtNdepth, global.depth); subshell = XtCreatePopupShell("markunreadshell", menuShellWidgetClass, parent, args, 3); submenu = XtCreateManagedWidget("markunreadmenu", menuWidgetClass, subshell, NULL, 0); temp = MenuCreateGadget("article", stringGadgetClass, submenu, NULL, 0); XtAddCallback(temp, XtNcallback, mark_unread_callback, (XtPointer)MarkScopeArticle); temp = MenuCreateGadget("subject", stringGadgetClass, submenu, NULL, 0); XtAddCallback(temp, XtNcallback, mark_unread_callback, (XtPointer)MarkScopeSubject); temp = MenuCreateGadget("thread", stringGadgetClass, submenu, NULL, 0); XtAddCallback(temp, XtNcallback, mark_unread_callback, (XtPointer)MarkScopeThread); temp = MenuCreateGadget("subthread", stringGadgetClass, submenu, NULL, 0); XtAddCallback(temp, XtNcallback, mark_unread_callback, (XtPointer)MarkScopeSubthread); temp = MenuCreateGadget("tagged", stringGadgetClass, submenu, NULL, 0); XtAddCallback(temp, XtNcallback, mark_unread_callback, (XtPointer)MarkScopeTagged); temp = MenuCreateGadget("allarticles", stringGadgetClass, submenu, NULL, 0); XtAddCallback(temp, XtNcallback, mark_unread_callback, (XtPointer)MarkScopeAllarticles); temp = MenuCreateGadget("killed", stringGadgetClass, submenu, NULL, 0); XtAddCallback(temp, XtNcallback, mark_unread_callback, (XtPointer)MarkScopeKilled); } void create_misc_menu2(Widget parent) { Widget menu, temp; Arg args[4]; XtSetArg(args[0], XtNcolormap, global.cmap); XtSetArg(args[1], XtNvisual, global.visual); XtSetArg(args[2], XtNdepth, global.depth); misc_menu2 = XtCreatePopupShell("miscshell2", menuShellWidgetClass, parent, args, 3); menu = XtCreateManagedWidget("miscmenu2", menuWidgetClass, misc_menu2, NULL, 0); temp = MenuCreateGadget("unsubscribe", stringGadgetClass, menu, NULL, 0); XtAddCallback(temp, XtNcallback, unsubscribe_callback, NULL); temp = MenuCreateGadget("catchup", stringGadgetClass, menu, NULL, 0); XtAddCallback(temp, XtNcallback, catchup_callback, NULL); temp = MenuCreateGadget("findgroup", stringGadgetClass, menu, NULL, 0); XtAddCallback(temp, XtNcallback, findgroup_callback, NULL); ask_how_many = MenuCreateGadget("askhowmany", toggleGadgetClass, menu, NULL, 0); XtAddCallback(ask_how_many, XtNcallback, ask_how_many_callback, NULL); } void action_mark_read_article(Widget w, XEvent *event, String *params, Cardinal *no_params) { mark_read_callback(w, (XtPointer)MarkScopeArticle, NULL); } void action_mark_read_subject(Widget w, XEvent *event, String *params, Cardinal *no_params) { mark_read_callback(w, (XtPointer)MarkScopeSubject, NULL); } void action_mark_read_thread(Widget w, XEvent *event, String *params, Cardinal *no_params) { mark_read_callback(w, (XtPointer)MarkScopeThread, NULL); } void action_mark_read_subthread(Widget w, XEvent *event, String *params, Cardinal *no_params) { mark_read_callback(w, (XtPointer)MarkScopeSubthread, NULL); } void action_mark_read_tagged(Widget w, XEvent *event, String *params, Cardinal *no_params) { mark_read_callback(w, (XtPointer)MarkScopeTagged, NULL); } void action_mark_read_all(Widget w, XEvent *event, String *params, Cardinal *no_params) { mark_read_callback(w, (XtPointer)MarkScopeAllarticles, NULL); } void action_mark_read_to_current(Widget w, XEvent *event, String *params, Cardinal *no_params) { mark_read_callback(w, (XtPointer)MarkScopeToCurrent, NULL); } void action_mark_read_non_tagged(Widget w, XEvent *event, String *params, Cardinal *no_params) { mark_read_callback(w, (XtPointer)MarkScopeNonTagged, NULL); } void action_mark_read_cold(Widget w, XEvent *event, String *params, Cardinal *no_params) { mark_read_callback(w, (XtPointer)MarkScopeCold, NULL); } void action_mark_unread_article(Widget w, XEvent *event, String *params, Cardinal *no_params) { mark_unread_callback(w, (XtPointer)MarkScopeArticle, NULL); } void action_mark_unread_subject(Widget w, XEvent *event, String *params, Cardinal *no_params) { mark_unread_callback(w, (XtPointer)MarkScopeSubject, NULL); } void action_mark_unread_thread(Widget w, XEvent *event, String *params, Cardinal *no_params) { mark_unread_callback(w, (XtPointer)MarkScopeThread, NULL); } void action_mark_unread_subthread(Widget w, XEvent *event, String *params, Cardinal *no_params) { mark_unread_callback(w, (XtPointer)MarkScopeSubthread, NULL); } void action_mark_unread_tagged(Widget w, XEvent *event, String *params, Cardinal *no_params) { mark_unread_callback(w, (XtPointer)MarkScopeTagged, NULL); } void action_mark_unread_all(Widget w, XEvent *event, String *params, Cardinal *no_params) { mark_unread_callback(w, (XtPointer)MarkScopeAllarticles, NULL); } void action_mark_unread_killed(Widget w, XEvent *event, String *params, Cardinal *no_params) { mark_unread_callback(w, (XtPointer)MarkScopeKilled, NULL); } void action_clear_tagged(Widget w, XEvent *event, String *params, Cardinal *no_params) { clear_tags_callback(w, NULL, NULL); } void action_catchup(Widget w, XEvent *event, String *params, Cardinal *no_params) { GROUP *g; long n; if (global.busy) return; switch (global.mode) { case NewsModeDisconnected: break; case NewsModeConnected: set_curr_group(); g = global.curr_group; if (!g) { set_message ("No selected group!", True); break; } list_all_arts_read(g); setNewsModeConnected(); break; case NewsModeGroup: case NewsModeThread: knapp6_callback(w, NULL, NULL); break; case NewsModeAllgroups: n = ScrListGetFirstSelected(main_widgets.group_list); if (n < 0) { set_message("No selected groups.", True); return; } while (n >= 0) { list_all_arts_read(global.groups[n]); n = ScrListGetNextSelected(main_widgets.group_list, n); } set_message("Catched up.", False); break; case NewsModeNewgroups: case NewsModeSomegroups: break; } } void action_unsubscribe(Widget w, XEvent *event, String *params, Cardinal *no_params) { if (global.busy) return; switch (global.mode) { case NewsModeDisconnected: break; case NewsModeConnected: set_curr_group(); if (!global.curr_group) { set_message("No selected group!", True); return; } global.curr_group->subscribed = False; sort_groups(); setNewsModeConnected(); break; case NewsModeGroup: case NewsModeThread: set_curr_group(); if (global.curr_group && global.curr_group->subscribed) toggle_subs_callback(w, NULL, NULL); break; case NewsModeAllgroups: case NewsModeNewgroups: case NewsModeSomegroups: knapp2_callback(w, NULL, NULL); break; } } void action_subscribe(Widget w, XEvent *event, String *params, Cardinal *no_params) { if (global.busy) return; switch (global.mode) { case NewsModeDisconnected: case NewsModeConnected: break; case NewsModeGroup: case NewsModeThread: if (global.curr_group && !global.curr_group->subscribed) toggle_subs_callback(w, NULL, NULL); break; case NewsModeAllgroups: case NewsModeNewgroups: case NewsModeSomegroups: knapp1_callback(w, NULL, NULL); break; } } void action_tag_hot(Widget w, XEvent *event, String *params, Cardinal *no_params) { tag_hot_callback(w, NULL, NULL); } /*********************************************************************/ static void msgid_dialogue_callback(Widget w, XtPointer client_data, XtPointer call_data) { char buffer[512]; DialogueReport *report = (DialogueReport *)call_data; char *msgid = report->buffer; char *reply, *c; long n; ARTICLE *art; Arg arg; if (global.busy || (global.mode != NewsModeGroup && global.mode != NewsModeThread)) return; switch (report->reply) { case DialogueReplyRight: case DialogueReplyClose: popdown_msgid_dialogue(); break; case DialogueReplyMiddle: XtSetArg(arg, XtNbuffer, ""); XtSetValues(w, &arg, 1); break; case DialogueReplyLeft: case DialogueReplyEnter: case DialogueReplyTab: popdown_msgid_dialogue(); if (!msgid || msgid[0] == '\0') break; if (*msgid == '<') msgid++; n = strlen(msgid); if (n > 480) { XBell(display, 0); break; } msgid = memcpy(XtMalloc(n + 1), msgid, n + 1); if (msgid[n - 1] == '>') msgid[--n] = '\0'; c = strchr(msgid, '@'); if (c) ascii_lower(c); art = find_article(msgid, n); set_curr_art(art, True); if (art) { if (read_article(art, False, NULL, NULL) && global.mode == NewsModeThread) setNewsModeThread(); XtFree(msgid); break; } set_busy(True); set_message("Asking server for article...", False); sprintf(buffer, "ARTICLE <%s>\r\n", msgid); XtFree(msgid); reply = server_comm(main_server, buffer, True); if (reply) if (atoi(reply) == NNTP_OK_ARTICLE) { reply = do_mime(NULL, main_server, server_read(main_server), False, NULL, 0, NULL); set_standard_message(); } else { if (strlen(reply) > 200) reply[200] = '\0'; sprintf(buffer, "Error, message from server is: %s", reply); set_message(buffer, True); } if (!reply) { reconnect_server(True); unset_busy(); return; } unset_busy(); break; } } void popdown_msgid_dialogue(void) { if (msgid_lookup_dialogue && is_popped_up(msgid_lookup_dialogue)) XtPopdown(msgid_lookup_dialogue); } ./knews-1.0b.1/src/p_post.h100644 1244 1244 316 6455455544 14062 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ struct PostContext; extern int post_article(FILE*); extern int post_to_agent(char*, FILE*); extern FILE *dump_art_to_file(struct PostContext*); ./knews-1.0b.1/src/p_setup.c100644 1244 1244 43354 6570011615 14262 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include "global.h" #include "cache.h" #include "codes.h" #include "connect.h" #include "decode.h" #include "file.h" #include "p_I.h" #include "p_attach.h" #include "p_popup.h" #include "p_setup.h" #include "parse.h" #include "resource.h" #include "server.h" #include "util.h" #include "xutil.h" static int n_post_contexts = 0; int outstanding_posts(void) { return n_post_contexts > 0; } void free_post_context(PostContext *context) { n_post_contexts--; if (context->file_name) { unlink(context->file_name); XtFree(context->file_name); context->file_name = NULL; } free((char *)context->art); /* :-( */ context->art = NULL; XtFree(context->charset); context->charset = NULL; if (context->widgets) { destroy_post_widgets(context->widgets); context->widgets = NULL; } if (context->attachments) { int i, n = context->n_attachments; for (i = 0 ; i < n ; i++) free_attachment(context->attachments[i]); XtFree((char *)context->attachments); context->attachments = NULL; context->n_attachments = 0; } XtFree((char *)context); } PostContext *create_post_context(int flags, char *file_name) { PostContext *context; char *charset; n_post_contexts++; charset = res_default_charset(); if (!charset) charset = "iso-8859-1"; if (!(flags & POST)) flags |= ORIG_MAIL; context = (PostContext *)XtMalloc(sizeof *context); context->file_name = XtNewString(file_name); context->art = NULL; context->charset = XtNewString(charset); context->line = 0; context->n_attachments = 0; context->flags = flags; context->busy = True; context->has_8bit = False; context->widgets = NULL; context->attachments = NULL; context->q_str = res_quote_string(); context->qq_str = res_quote_quote_string(); return context; } void append_signature(FILE *fp) { char *sig_file_name = res_signature_file(); FILE *sig; int c; if (!sig_file_name) return; sig = fopen_expand(sig_file_name, "r", False); if (!sig) return; fputs("\n-- \n", fp); while ((c = getc(sig)) != EOF) putc(c, fp); fclose(sig); } static int print_references_header(FILE *fp, char *msgid, char *refs) { char *c; int lines = 1; int may_fold = False; int col; col = fprintf(fp, "References:"); if (refs) while ((refs = strchr(refs, '<')) && (c = strchr(++refs, '>'))) { int len = c - refs; *c = '\0'; if (may_fold && col + len > 72) { col = fprintf(fp, "\n ") - 1; lines++; } col += fprintf(fp, " <%s>", refs); may_fold = True; *c = '>'; } if (may_fold && col + strlen(msgid) > 72) { fprintf(fp, "\n "); lines++; } fprintf(fp, " %s\n", msgid); return lines; } static char *skip_line_count(char *c) { while (IS_SPACE(*c)) c++; while (IS_DIGIT(*c)) c++; while (IS_SPACE(*c)) c++; return c; } static int extract_initials(ARTICLE *art, char *buffer, int maxlen, int cap) { char *c; int pos = 0; maxlen -= 4; if (art && art->from && (c = art->tree_data.label)) { if (res_show_number_lines()) c = skip_line_count(c); while (*c != '\0') { while (*c != '\0' && !IS_ALPHA(*c)) c++; if (*c == '\0' || pos > maxlen) break; buffer[pos++] = (cap && IS_LOWER(*c)) ? UPPER(*c) : *c; while (*c != '\0' && IS_ALPHA(*c)) c++; } } buffer[pos] = '\0'; return pos; } static void expand_quote_string(ARTICLE *art, char *quote_string, char *buffer, int len) { int pos = 0; *buffer = '\0'; len -= 4; if (!quote_string) return; while (*quote_string != '\0' && pos < len) if (*quote_string != '%') buffer[pos++] = *quote_string++; else { int cap = False; switch (*++quote_string) { case 'I': cap = True; /* fall through */ case 'i': pos += extract_initials(art, buffer + pos, len, cap); break; case '%': buffer[pos++] = '%'; break; default: continue; } quote_string++; } buffer[pos] = '\0'; } static int tm_helper(int *inited, struct tm *tm, time_t date, FILE *fp) { int first = !*inited; struct tm *tmp; *inited = True; if (date == PARSEDATE_ERROR) { if (first) fprintf(fp, ""); return False; } tmp = gmtime(&date); if (!tmp) return False; *tm = *tmp; return True; } static void print_with_percent(FILE *fp, char *fmt, ARTICLE *art) { struct tm tm; int tm_inited = False; char *c; if (!fp || !fmt || fmt[0] == '\0' || !art) return; #define FILL_TM tm_helper(&tm_inited, &tm, art->date, fp) while (*fmt != '\0') if (*fmt != '%') fputc(*fmt++, fp); else { unsigned char a = *++fmt; if (isupper(a)) a = tolower(a); switch (a) { case '\0': continue; case '%': /* literal % */ fputc('%', fp); break; case 'd': /* date: dd mmm */ if (FILL_TM && (unsigned)tm.tm_mon < 12) fprintf(fp, "%2.2d %3.3s", tm.tm_mday, month_names + 3 * tm.tm_mon); break; case 'f': /* from line */ fputs(art->from, fp); break; case 'i': /* initials */ if (art) { char buffer[32]; extract_initials(art, buffer, sizeof buffer, *fmt == 'I'); fputs(buffer, fp); } break; case 'm': /* message-id */ fprintf(fp, "<%s>", art->msgid); break; case 'n': /* newsgroup */ if ((global.mode == NewsModeGroup || global.mode == NewsModeThread) && global.curr_group) fputs(global.curr_group->name, fp); break; case 'r': /* real name */ c = art->tree_data.label; if (c && res_show_number_lines()) c = skip_line_count(c); if (c) fputs(c, fp); break; case 's': /* subject */ fprintf(fp, "%s%s", PARENT(art) ? "Re: " : "", art->subject->subject); break; case 't': /* time */ if (FILL_TM) fprintf(fp, "%02d:%02d:%02d", tm.tm_hour, tm.tm_min, tm.tm_sec); break; case 'w': /* week day */ if (FILL_TM && 0 <= tm.tm_wday && tm.tm_wday < 7) { static char *wday_names[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", }; fputs(wday_names[tm.tm_wday], fp); } break; case 'y': /* year */ if (FILL_TM) fprintf(fp, "%4d", 1900 + tm.tm_year); break; default: /* bogus */ putc('%', fp); putc(a, fp); break; } fmt++; } #undef FILL_TM } int insert_extra_headers(FILE *fp, ARTICLE *art) { char *reply_to = res_reply_to(); char *organization = res_organization(); char *extra_headers = res_extra_headers(); char *followup_headers = res_followup_headers(); char *distr = res_distribution(); int result = 0; if (global.generate_path) { fprintf(fp, "Path: %s!%s\n", global.domain_name, global.mail_name); result++; } fprintf(fp, "%s\n", "X-Newsreader: knews " KNEWS_VERSION); result++; if (distr) { fprintf(fp, "Distribution: %s\n", distr); result++; } if (reply_to) { fprintf(fp, "Reply-To: %s\n", reply_to); result++; } if (organization) { fprintf(fp, "Organization: %s\n", organization); result++; } if (extra_headers && extra_headers[0] != '\0') { char *c; fputs(extra_headers, fp); for (c = strchr(extra_headers, '\n') ; c ; c = strchr(c + 1, '\n')) result++; if (extra_headers[strlen(extra_headers) - 1] != '\n') { result++; fputc('\n', fp); } } if (art && followup_headers && followup_headers[0] != '\0') { char *c; print_with_percent(fp, followup_headers, art); for (c = strchr(followup_headers,'\n') ; c ; c = strchr(c + 1, '\n')) result++; if (followup_headers[strlen(followup_headers) - 1] != '\n') { result++; fputc('\n', fp); } } return result; } typedef struct { char *subject; char *newsgroups; char *followup_to; char *from; char *reply_to; char *message_id; char *references; char *content_type; char *content_enc; } PostHeaders; static void free_headers(PostHeaders *h) { XtFree(h->subject); XtFree(h->newsgroups); XtFree(h->followup_to); XtFree(h->from); XtFree(h->reply_to); XtFree(h->message_id); XtFree(h->references); XtFree(h->content_type); XtFree(h->content_enc); } static int get_headers(SERVER *server, PostHeaders *h) { char *buffer; char **curr = NULL; while ((buffer = server_read(server)) && buffer[0] != '\0' && !IS_DOT(buffer)) if (IS_SPACE(buffer[0])) { long n, m; if (!curr || !*curr) continue; n = strlen(*curr); buffer[0] = ' '; m = strlen(buffer); *curr = XtRealloc(*curr, m + n + 4); memcpy(*curr + n, buffer, m + 1); } else { char *c; int len; int incl_name = False; curr = NULL; c = strchr(buffer, ':'); if (!c) continue; len = c++ - buffer; while (len > 0 && IS_SPACE(buffer[len - 1])) len--; while (IS_SPACE(*c)) c++; #define IS_HEADER(header) \ (len == sizeof header - 1 && \ case_lstrncmp(buffer, header, sizeof header - 1) == 0) if (IS_HEADER("subject")) curr = &h->subject; else if (IS_HEADER("newsgroups")) curr = &h->newsgroups; else if (IS_HEADER("followup-to")) curr = &h->followup_to; else if (IS_HEADER("from")) curr = &h->from; else if (IS_HEADER("reply-to")) curr = &h->reply_to; else if (IS_HEADER("message-id")) curr = &h->message_id; else if (IS_HEADER("references")) curr = &h->references; else if (IS_HEADER("content-type")) curr = &h->content_type, incl_name = True; else if (IS_HEADER("content-transfer-encoding")) curr = &h->content_enc, incl_name = True; else continue; #undef IS_HEADER *curr = incl_name ? XtNewString(buffer) : XtNewString(c); } if (!buffer) { free_headers(h); return False; } #define CHECK(header) if (!h->header) h->header = XtNewString("") CHECK(subject); CHECK(newsgroups); CHECK(from); CHECK(message_id); #undef CHECK return True; } static char *q_string(char *line, regex_t *re, char *q_str, char *qq_str) { if (*line == '\0' && !global.quote_empty) return ""; return re && regexec(re, line, 0, NULL, 0) == 0 ? qq_str : q_str; } static int print_multi_line(FILE *fp, regex_t *re, char *q_str, char *qq_str, char *line, int append) { char *c; while ((c = strchr(line, '\n'))) { if (c > line && c[-1] == '\r') c[-1] = '\0'; *c++ = '\0'; if (!append) fputs(q_string(line, re, q_str, qq_str), fp); fprintf(fp, "%s\n", line); append = False; line = c; } if (*line != '\0') { if (!append) fputs(q_string(line, re, q_str, qq_str), fp); fprintf(fp, "%s", line); append = True; } return append; } static char *do_quote(SERVER *server, FILE *fp, char *q_str, char *qq_str, char *charset, char *c_type, char *c_enc, int quote_sig) { regex_t *re = res_quote_regexp(); char *buffer; int enc = MimeEncNone; long n; int in_sig = False; if (c_type && c_enc) { MimeArg args[5] = {{0, }, }; char *buf[2]; char type[128], subtype[128], *c; int i; buf[0] = c_type; buf[1] = NULL; if (parse_content_type(buf, type, sizeof type, subtype, sizeof subtype, args, XtNumber(args) - 1, False) && strcmp(type, "text") == 0 && (c = get_charset(args)) && case_strcmp(c, charset) == 0) { buf[0] = c_enc; buf[1] = NULL; enc = parse_content_enc(buf); if (enc < 0) enc = MimeEncNone; } for (i = 0 ; i < XtNumber(args) && args[i].value ; i++) { XtFree(args[i].name); XtFree(args[i].value); } } #define IS_SIG_DELIM(c) \ ((c)[0] == '-' && (c)[1] == '-' && \ (c)[2] == ' ' && (c)[3] == '\0') switch (enc) { default: case MimeEncNone: while ((buffer = server_read(server)) && !IS_DOT(buffer)) { char *c = buffer + (*buffer == '.' ? 1 : 0); if (IS_SIG_DELIM(c)) in_sig = True; if (quote_sig || !in_sig) fprintf(fp, "%s%s\n", q_string(c, re, q_str, qq_str), c); } break; case MimeEncQP: { int append = False; int soft; while ((buffer = server_read(server)) && !IS_DOT(buffer)) { n = decode_qp(buffer, buffer + (*buffer == '.' ? 1 : 0), strlen(buffer), &soft, False); if (n > 0) { buffer[n] = '\0'; if (!append && IS_SIG_DELIM(buffer)) in_sig = True; if (quote_sig || !in_sig) print_multi_line(fp, re, q_str, qq_str, buffer, append); if (!soft) fputc('\n', fp); } append = soft; } if (append) fputc('\n', fp); } break; case MimeEncBase64: { B64Context bc = {0, }; long dest_len = 128; char *dest = XtMalloc(dest_len); int append = False; while ((buffer = server_read(server)) && !IS_DOT(buffer)) { n = strlen(buffer); if (n + 8 > dest_len) dest = XtRealloc(dest, (dest_len = n + 8)); n = decode_base64(&bc, dest, buffer, n); if (n > 0) { dest[n] = '\0'; append = print_multi_line(fp, re, q_str, qq_str, dest, append); } } n = decode_base64(&bc, dest, NULL, 0); if (n > 0) { dest[n] = '\0'; append = print_multi_line(fp, re, q_str, qq_str, dest, append); } if (append) fputc('\n', fp); XtFree(dest); } break; case MimeEncUue: { UueContext uc = {0, }; int append = False; while ((buffer = server_read(server)) && !IS_DOT(buffer)) { n = decode_uue(&uc, buffer, buffer, strlen(buffer)); if (n > 0) { buffer[n] = '\0'; append = print_multi_line(fp, re, q_str, qq_str, buffer, append); } } } break; } #undef IS_SIG_DELIM return buffer; } static int setup_file(PostContext *context, FILE *fp, ARTICLE *art, int quote, int quote_sig, int supersede) { PostHeaders h = {0, }; SERVER *server = NULL; char *full_name = res_full_name(); char *posted_and_mailed = res_posted_and_mailed(); char *buffer = ""; int line = 1; if (!full_name) full_name = ""; if (supersede) quote = quote_sig = True; server = cache_get_server(art->no, False); if (!server) { char command[64]; int status; server = main_server; sprintf(command, "%s %ld\r\n", quote ? "ARTICLE" : "HEAD", art->no); buffer = server_comm(server, command, True); if (!buffer) return -1; status = atoi(buffer); if (status != NNTP_OK_HEAD && status != NNTP_OK_ARTICLE) { popup_title_notice("Couldn't retrieve article, \n" "message from server is", buffer, True); return False; } } if (!get_headers(server, &h)) { if (server == main_server) return -1; popup_title_notice(NULL, "Couldn't retrieve article, \n" "error with article cache.", True); server_free(server); return False; } line += insert_extra_headers(fp, art); line += print_references_header(fp, h.message_id, h.references); if (supersede) { fprintf(fp, "Supersedes: %s\n", h.message_id); line++; } if ((context->flags & MAIL) && !(context->flags & POST)) { fprintf(fp, "In-Reply-To: %s\n", h.message_id); line++; } fprintf(fp, "From: %s@%s (%s)\n", global.mail_name, global.domain_name, full_name); line++; fprintf(fp, "Subject: Re: %s\n", eat_re(h.subject)); line++; if (h.followup_to) { char *c = h.followup_to; int n; /* Strip leading and trailing whitespace... */ while (IS_SPACE(*c)) c++; n = strlen(c); while (n > 0 && IS_SPACE(c[n - 1])) c[--n] = '\0'; if (case_lstrcmp(h.followup_to, "poster") != 0) { XtFree(h.newsgroups); h.newsgroups = h.followup_to; h.followup_to = NULL; } else if (context->flags & POST) { context->flags &= ~POST; popup_notice("notice", "The original author has requested\n" "that followups be directed to email.", "Close", NULL, NULL, 5000, NULL, NULL, XtGrabNone); context->flags &= ~POST; context->flags |= MAIL; } } fprintf(fp, "%sNewsgroups: %s\n", context->flags & POST ? "" : "X-Original-", h.newsgroups); line++; if (context->flags & MAIL) { char *c = h.reply_to ? h.reply_to : h.from; fprintf(fp, "To: %s\n", c); line++; } line++; fputc('\n', fp); if ((context->flags & POST) && (context->flags & MAIL) && posted_and_mailed && posted_and_mailed[0] != '\0') { fprintf(fp, "%s\n", posted_and_mailed); if (posted_and_mailed[strlen(posted_and_mailed) - 1] != '\n') fputc('\n', fp); line += 2; } context->line = line; if (quote) { char q_buf[32], qq_buf[32]; q_buf[0] = '\0'; qq_buf[0] = '\0'; if (!supersede) { print_with_percent(fp, res_attribution(), art); fputc('\n', fp); expand_quote_string(art, res_quote_string(), q_buf, sizeof q_buf); expand_quote_string(art, res_quote_quote_string(), qq_buf, sizeof qq_buf); } buffer = do_quote(server, fp, q_buf, qq_buf, context->charset, h.content_type, h.content_enc, quote_sig); } free_headers(&h); if (!supersede) append_signature(fp); if (!buffer) if (server == main_server) return -1; else { popup_title_notice(NULL, "Couldn't retrieve article, \n" "error with article cache.", True); server_free(server); return False; } if (server != main_server) server_free(server); return True; } void post_setup(PostContext *context, FILE *fp, ARTICLE *art, int quote, int quote_sig, int supersede) { int tmp; set_busy(True); tmp = setup_file(context, fp, art, quote, quote_sig, supersede); if (tmp <= 0) { free_post_context(context); if (tmp < 0) reconnect_server(True); unset_busy(); return; } unset_busy(); fork_editor(context); } ./knews-1.0b.1/src/server.h100644 1244 1244 2064 6455455544 14106 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #ifndef SERVER_H #define SERVER_H typedef struct SERVER SERVER; typedef void (*QuitFunc)(void*); struct SERV_ADDR; extern SERVER *server_create(int); extern void server_free(SERVER*); extern void server_close(SERVER*); extern int server_open(SERVER*, struct SERV_ADDR*, int); extern int server_fork(SERVER*, char*, int); extern long server_write_raw(SERVER*, char*, long); extern int server_write(SERVER*, char*); extern long server_read_raw(SERVER*); extern char *server_get_line(SERVER*); extern char *server_get_chunk(SERVER*); extern char *server_read(SERVER*); extern char *server_read_chunk(SERVER*); extern char *server_comm(SERVER*, char*, int); extern int server_get_fd(SERVER*); extern void server_set_fd(SERVER*, int); extern void server_set_bs(SERVER*, FILE*); extern void server_set_quit_func(SERVER*, QuitFunc); extern QuitFunc server_get_quit_func(SERVER*); extern int server_aborted(SERVER*); extern void nntp_quit(void*); extern void nntp_just_close(void*); #endif /* SERVER_H */ ./knews-1.0b.1/src/resource.h100644 1244 1244 4263 6455455544 14432 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ extern void res_initialize(void); extern int res_load(char*); extern void res_enter_group(char*); extern void res_set_pw_name(char*); extern char *res_newsrc_file(void); extern char *res_old_newsrc_file(void); extern char *res_kill_file(void); extern char *res_remote_newsrc_file(void); extern char *res_remote_kill_file(void); extern char *res_auto_subscribe(void); extern char *res_auth_info_user(void); extern char *res_auth_info_pass(void); extern char *res_posting_agent(void); extern char *res_cache_dir(void); extern char *res_descriptions_file(void); extern char **res_thread_ahead_groups(void); extern long res_rescan_timeout(void); extern int res_retrieve_descriptions(void); extern int res_check_for_new_groups(void); extern int res_read_active_file(void); extern int res_ask_how_many(void); extern int res_fill_newsrc_file(void); extern int res_try_list_active(void); extern int res_save_thread_info(void); extern int res_group_name_columns(void); extern char **res_header_format(void); extern regex_t *res_quote_regexp(void); extern char *res_quote_string(void); extern char *res_quote_quote_string(void); extern char *res_attribution(void); extern char *res_full_name(void); extern char *res_signature_file(void); extern char *res_organization(void); extern char *res_reply_to(void); extern char *res_extra_headers(void); extern char *res_followup_headers(void); extern char *res_uu_dir(void); extern char *res_uu_program(void); extern char *res_distribution(void); extern char *res_sort_threads(void); extern char *res_default_charset(void); extern char *res_posted_and_mailed(void); extern int res_full_header(void); extern int res_process_xrefs(void); extern int res_show_number_lines(void); extern int res_keep_thread_info(int); extern int res_expire_kills(void); extern int res_assemble_partials(void); extern int res_cache_ahead_size(void); extern int res_cache_trail_size(void); extern int res_subject_columns(void); extern void res_set_ask_how_many(int); extern void res_set_full_header(int); extern void res_set_keep_thread_info(int); extern void res_set_default_charset(char*); ./knews-1.0b.1/src/file.h100644 1244 1244 732 6455455544 13477 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ struct stat; extern int open_mkdir(char*, int, int); extern int open_expand(char*, int, int); extern FILE *fopen_mkdir(char*, char*, int); extern FILE *fopen_expand(char*, char*, int); extern int unlink_expand(char*); extern int chdir_mkdir(char*); extern int create_temp_fd(char**); extern FILE *create_temp_file(char**); extern char *snarf_file(int, long*); extern int writen(int, char*, long); ./knews-1.0b.1/src/mailcap.c100644 1244 1244 15306 6455455544 14224 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include "global.h" #include "file.h" #include "mailcap.h" #include "util.h" #define MAILCAP_HASH_SIZE 17 typedef struct MailCapEntry{ struct MailCapEntry *next; char *type; char *subtype; MailcapData data; } MailcapEntry; static MailcapEntry *mailcaps[MAILCAP_HASH_SIZE] = {0, }; static unsigned int hash(char *c) { unsigned int result = 0; while (*c != '\0') result += (unsigned char)*c++; return result % MAILCAP_HASH_SIZE; } static char *token_strip(char *token, int lower) { int n; while (IS_SPACE(*token)) token++; n = strlen(token); while (n > 0 && IS_SPACE(token[n - 1])) token[--n] = '\0'; if (n == 0) return NULL; if (lower) ascii_nlower(token, n); return token; } static MailcapEntry *mailcap_hash_create(char *type, char *subtype) { static const MailcapEntry zero = {0, }; MailcapEntry *entry, **loop; type = token_strip(type, True); if (!type) return NULL; if (subtype) { subtype = token_strip(subtype, True); if (strcmp(subtype, "*") == 0) subtype = NULL; } loop = mailcaps + hash(type); while (*loop) loop = &(*loop)->next; entry = (MailcapEntry *)XtMalloc(sizeof *entry); *loop = entry; *entry = zero; entry->type = XtNewString(type); if (subtype) entry->subtype = XtNewString(subtype); return entry; } char *expn_tmpl(char *src, int n, const char *tmpl, char **expn) { long dest_len = 128; char *dest = XtMalloc(dest_len); long dest_pos = 0; while (*src != '\0') { if (*src == '%') { int i; for (i = 0 ; i < n ; i++) if (src[1] == tmpl[i]) break; if (i < n) { int expn_len = strlen(expn[i]); if (dest_len < dest_pos + expn_len + 8) { dest_len = dest_pos + expn_len + 8; dest = XtRealloc(dest, dest_len); } memcpy(dest + dest_pos, expn[i], expn_len + 1); dest_pos += expn_len; src += 2; continue; } } if (dest_len < dest_pos + 8) { dest_len = 2 * (dest_pos + 8); dest = XtRealloc(dest, dest_len); } dest[dest_pos++] = *src++; } dest[dest_pos] = '\0'; dest = XtRealloc(dest, dest_pos + 1); return dest; } static char *parse_mailcap_mtext(char *mtext) { while (IS_SPACE(*mtext)) mtext++; return mtext; } static int parse_mailcap_flag(MailcapEntry *entry, char *flag) { char first = *flag; switch (first) { case 'c': if (strcmp(flag, "copiousoutput") == 0) { entry->data.copiousoutput = True; return True; } break; case 'n': if (strcmp(flag, "needsterminal") == 0) { entry->data.needsterminal = True; return True; } break; } return False; } static int parse_mailcap_named_field(MailcapEntry *entry, char *fieldname, char *mtext) { char first = *fieldname; char **field = NULL; mtext = parse_mailcap_mtext(mtext); if (!mtext) return False; switch (first) { case 'c': if (strcmp(fieldname, "compose") == 0) field = &entry->data.compose; else if (strcmp(fieldname, "composetyped") == 0) field = &entry->data.compose_typed; break; case 'd': if (strcmp(fieldname, "description") == 0) field = &entry->data.description; break; case 'e': if (strcmp(fieldname, "edit") == 0) field = &entry->data.edit; break; case 'p': if (strcmp(fieldname, "print") == 0) field = &entry->data.print; break; case 't': if (strcmp(fieldname, "test") == 0) field = &entry->data.test; else if (strcmp(fieldname, "textualnewlines") == 0) { long tmp; if (sscanf(mtext, "%ld", &tmp) != 1) return False; entry->data.textualnewlines = (tmp != 0); return True; } break; case 'x': if (strcmp(fieldname, "x11-bitmap") == 0) field = &entry->data.x11_bitmap; break; } if (!field) return False; XtFree(*field); *field = XtNewString(mtext); return True; } static int parse_mailcap_line(char *line) { static const char tmpl = 'C'; MailcapEntry *entry; char *type, *subtype; char *view_cmd = NULL, *p; int n; p = strchr(line, ';'); if (!p) return False; *p++ = '\0'; type = line; line = strchr(line, '/'); if (!line) subtype = NULL; else { *line++ = '\0'; subtype = line; } line = p; entry = mailcap_hash_create(type, subtype); if (!entry) return False; for (n = 0 ; ; n++) { char *end; p = NULL; for (end = line ; *end != '\0' && *end != ';' ; end++) if (n != 0 && end[0] == '=') p = end; else if (end[0] == '\\' && end[1] != '\0') end++; if (*end == ';') *end++ = '\0'; if (p) { *p++ = '\0'; line = token_strip(line, True); if (line) parse_mailcap_named_field(entry, line, p); } else if (n == 0) { while (IS_SPACE(*line)) line++; view_cmd = line; } else { line = token_strip(line, True); if (line) parse_mailcap_flag(entry, line); } if (*end != '\0') line = end; else break; } if (!view_cmd) entry->data.view_command = XtNewString("echo mailcap error"); else if (entry->data.needsterminal && global.needs_terminal) entry->data.view_command = expn_tmpl(global.needs_terminal, 1, &tmpl, &view_cmd); else if (entry->data.copiousoutput && global.copious_output) entry->data.view_command = expn_tmpl(global.copious_output, 1, &tmpl, &view_cmd); else entry->data.view_command = XtNewString(view_cmd); return True; } static void parse_mailcap_file(char *buffer) { char *p; for (p = strchr(buffer, '\n') ; p ; p = strchr(p + 1, '\n')) if (p > buffer && p[-1] == '\\') { p[-1] = ' '; p[0] = ' '; } while ((p = strchr(buffer, '\n'))) { *p++ = '\0'; if (buffer[0] != '#' && buffer[0] != '\0') parse_mailcap_line(buffer); buffer = p; } } void mailcap_init(void) { char *paths = ".mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap"; char *c; c = getenv("MAILCAPS"); if (c) { paths = c; if (paths[0] == '\0') return; } paths = XtNewString(paths); c = paths; for (;;) { char *p = strchr(c, ':'); int fd; if (p) *p = '\0'; fd = open(c, O_RDONLY); if (fd < 0) { if (errno != ENOENT) perror(c); } else { char *buffer; buffer = snarf_file(fd, NULL); if (!buffer) perror(c); else parse_mailcap_file(buffer); close(fd); XtFree(buffer); } if (!p) break; *p++ = ':'; c = p; } XtFree(paths); } const MailcapData *mailcap_lookup(char *type, char *subtype) { MailcapEntry *loop; for (loop = mailcaps[hash(type)] ; loop ; loop = loop->next) if (strcmp(type, loop->type) == 0 && (!loop->subtype || strcmp(subtype, loop->subtype) == 0)) return &loop->data; return NULL; } ./knews-1.0b.1/src/k_edit.c100644 1244 1244 56400 6455455544 14055 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include "global.h" #include #include "color.h" #include "expand.h" #include "k_I.h" #include "k_edit.h" #include "k_file.h" #include "k_node.h" #include "util.h" #include "widgets.h" #include "xutil.h" #include "../Widgets/Compat.h" #include "../Widgets/Knapp.h" #include "../Widgets/Layout.h" #include "../Widgets/Manager.h" #include "../Widgets/Menu.h" #include "../Widgets/Message.h" #include "../Widgets/MenuKnapp.h" #include "../Widgets/MenuShell.h" #include "../Widgets/Scrollable.h" #include "../Widgets/ScrBar.h" #include "../Widgets/ScrList.h" #include "../Widgets/SeparatorG.h" #include "../Widgets/StringG.h" #include "../Widgets/TextField.h" #include "../Widgets/Toggle.h" #include "../Widgets/Util.h" typedef struct KILL_WIDGETS { Widget shell; Widget listmgr; Widget list; /***/ Widget field_knapp; Widget messageid_field; Widget subject_field; Widget from_field; Widget xref_field; /***/ Widget scope_knapp; Widget article_scope; Widget subject_scope; Widget thread_scope; Widget subthread_scope; /***/ Widget action_knapp; Widget expr_knapp; Widget group_knapp; /***/ Widget group_field; Widget expr_field; Widget color_field; /***/ Widget stayup; Widget add_knapp; Widget delete_knapp; } KILL_WIDGETS; #define SAME_TYPE(f1, f2) (((f1) == KillFieldMsgid) == \ ((f2) == KillFieldMsgid)) static void set_field(KILL_WIDGETS *w, int field) { KnappSetLabelNo(w->field_knapp, field, True); KnappSetLabelNo(w->expr_knapp, field == KillFieldMsgid, True); } static void set_field_sens(KILL_WIDGETS *w, int field) { int msgid = field == KillFieldMsgid; int all_sens = field < 0; XtSetSensitive(w->messageid_field, all_sens || msgid); XtSetSensitive(w->subject_field, all_sens || !msgid); XtSetSensitive(w->from_field, all_sens || !msgid); XtSetSensitive(w->xref_field, all_sens || !msgid); } static void set_scope(KILL_WIDGETS *w, int scope) { KnappSetLabelNo(w->scope_knapp, scope, True); } static void set_action(KILL_WIDGETS *w, int hot, char *color) { if (!hot) color = NULL; else if (!color) color = ""; ToggleSet(w->action_knapp, hot); KnappSetLabelNo(w->action_knapp, hot, True); XtSetSensitive(w->color_field, color != NULL); TextFieldSetBuffer(w->color_field, color); } static void set_knapp_sens(KILL_WIDGETS *w, long n) { KnappSetSensitive(w->add_knapp, n < 0); KnappSetSensitive(w->delete_knapp, n >= 0); } static void set_kill_controls(KILL_FILE *file, long n) { KILL_WIDGETS *w = file->w; KILL_NODE *node = n < 0 ? NULL : file->nodes[n]; set_field(w, node ? node->field : KillFieldFrom); set_field_sens(w, node ? node->field : -1); set_scope(w, node ? node->scope : KillScopeArticle); set_action(w, node ? node->hot : False, node ? node->color : NULL); if (w->group_field) { char *grp; if (node) grp = node->group_str; else if (global.curr_group) grp = regexp_escape_string(global.curr_group->name, True); else grp = NULL; TextFieldSetBuffer(w->group_field, grp); if (!node) XtFree(grp); } TextFieldSetBuffer(w->expr_field, node ? node->expr_str : NULL); if (n < 0) { n = ScrListGetFirstSelected(w->list); if (n >= 0) ScrListSetSelected(w->list, n, False); } set_knapp_sens(w, ScrListGetFirstSelected(w->list)); } static void update_list_entry(KILL_FILE *file, long n) { char buffer[256]; sprint_kill_node(file->nodes[n], buffer, sizeof buffer); ScrListSetLine(file->w->list, n, buffer, file->nodes[n]->pixmap); } /*********************************************************************/ static void list_callback(Widget gw, XtPointer client_data, XtPointer call_data) { KILL_FILE *file = (KILL_FILE *)client_data; KILL_WIDGETS *w = file->w; long n = (long)call_data; if (ScrListGetSelected(w->list, n)) set_kill_controls(file, n); else { KnappSetSensitive(w->add_knapp, True); KnappSetSensitive(w->delete_knapp, False); } } static void list_dnd_callback(Widget gw, XtPointer client_data, XtPointer call_data) { KILL_FILE *file = (KILL_FILE *)client_data; long *index = (long *)call_data; long n; KILL_NODE *temp; if (!index || index[0] < 0 || index[1] < 0 || index[0] >= file->n || index[1] >= file->n) return; file->dirty = True; index[2] = True; temp = file->nodes[index[0]]; if (index[0] < index[1]) for (n = index[0] ; n < index[1] ; n++) file->nodes[n] = file->nodes[n + 1]; else for (n = index[0] ; n > index[1] ; n--) file->nodes[n] = file->nodes[n - 1]; file->nodes[index[1]] = temp; } static void add_callback(Widget gw, XtPointer client_data, XtPointer call_data) { KILL_FILE *file = (KILL_FILE *)client_data; KILL_WIDGETS *w = file->w; char *expr, *group, *color; int hot = ToggleGet(w->action_knapp); expr = TextFieldGetBuffer(w->expr_field); group = w->group_field ? TextFieldGetBuffer(w->group_field) : NULL; color = hot ? TextFieldGetBuffer(w->color_field) : NULL; add_kill_node(file, False, KnappGetLabelNo(w->field_knapp), KnappGetLabelNo(w->scope_knapp), hot, color, expr, group); XtFree(expr); XtFree(group); XtFree(color); } static void delete_callback(Widget gw, XtPointer client_data, XtPointer call_data) { KILL_FILE *file = (KILL_FILE *)client_data; KILL_WIDGETS *w = file->w; long n = ScrListGetFirstSelected(w->list); KILL_NODE *node = n < 0 ? NULL : file->nodes[n]; if (!node) return; file->dirty = True; if (node->pixmap != None) purge_hot(node->pixmap); free_kill_node(node); file->n--; if (n < file->n) memmove(file->nodes + n, file->nodes + n + 1, (file->n - n) * sizeof file->nodes[0]); file->nodes[file->n] = NULL; ScrListDeleteLine(w->list, n); set_knapp_sens(w, -1); set_standard_message(); } static void clear_callback(Widget gw, XtPointer client_data, XtPointer call_data) { KILL_FILE *file = (KILL_FILE *)client_data; set_kill_controls(file, -1); } static void close_callback(Widget gw, XtPointer client_data, XtPointer call_data) { KILL_FILE *file = (KILL_FILE *)client_data; XtPopdown(file->w->shell); } static void stayup_callback(Widget gw, XtPointer client_data, XtPointer call_data) { KILL_FILE *file = (KILL_FILE *)client_data; Boolean *set = (Boolean *)call_data; if (set) file->stay_up = *set = !*set; } static void field_callback(Widget gw, XtPointer client_data, XtPointer call_data) { KILL_FILE *file = (KILL_FILE *)client_data; long n = ScrListGetFirstSelected(file->w->list); KILL_NODE *node = n < 0 ? NULL : file->nodes[n]; int field; if (gw == file->w->messageid_field) field = KillFieldMsgid; else if (gw == file->w->subject_field) field = KillFieldSubject; else if (gw == file->w->from_field) field = KillFieldFrom; else if (gw == file->w->xref_field) field = KillFieldXref; else return; if (node && !SAME_TYPE(field, node->field)) return; set_field(file->w, field); if (node) { file->dirty = True; node->field = field; update_list_entry(file, n); } } static void scope_callback(Widget gw, XtPointer client_data, XtPointer call_data) { KILL_FILE *file = (KILL_FILE *)client_data; long n = ScrListGetFirstSelected(file->w->list); KILL_NODE *node = n < 0 ? NULL : file->nodes[n]; int scope; if (gw == file->w->article_scope) scope = KillScopeArticle; else if (gw == file->w->subject_scope) scope = KillScopeSubject; else if (gw == file->w->thread_scope) scope = KillScopeThread; else if (gw == file->w->subthread_scope) scope = KillScopeSubthread; else return; set_scope(file->w, scope); if (node) { file->dirty = True; node->scope = scope; update_list_entry(file, n); } } static void action_callback(Widget gw, XtPointer client_data, XtPointer call_data) { KILL_FILE *file = (KILL_FILE *)client_data; Boolean *set = (Boolean *)call_data; KILL_WIDGETS *w = file->w; long n = ScrListGetFirstSelected(w->list); KILL_NODE *node = n < 0 ? NULL : file->nodes[n]; int hot; if (!set) return; hot = *set = !*set; KnappSetLabelNo(w->action_knapp, hot, True); if (!hot) { TextFieldSetBuffer(w->color_field, ""); XtSetKeyboardFocus(w->shell, w->expr_field); } XtSetSensitive(w->color_field, hot); if (hot) XtSetKeyboardFocus(w->shell, w->color_field); if (node) { file->dirty = True; node->hot = hot; if (hot) { if (!node->alloced_pixel) node->pixel = global.default_hot_pixel; fix_node_pixmap(node); } else { if (node->alloced_pixel) { unsigned long pixel = node->pixel; XFreeColors(display, global.cmap, &pixel, 1, 0); node->alloced_pixel = False; node->pixel = global.default_hot_pixel; } if (node->pixmap != None) { purge_hot(node->pixmap); XFreePixmap(display, node->pixmap); node->pixmap = None; } } update_list_entry(file, n); } set_standard_message(); } static void tab_callback(Widget gw, XtPointer client_data, XtPointer call_data) { KILL_FILE *file = (KILL_FILE *)client_data; KILL_WIDGETS *w = file->w; Widget focus; if (gw == w->color_field) focus = w->group_field; else if (w->group_field && gw == w->group_field) focus = w->expr_field; else if (gw == w->expr_field) focus = ToggleGet(w->action_knapp) ? w->color_field : w->group_field; else return; if (!focus) /* group_field == NULL */ focus = w->expr_field; XtSetKeyboardFocus(w->shell, focus); } static void group_field_callback(Widget gw, XtPointer client_data, XtPointer call_data) { KILL_FILE *file = (KILL_FILE *)client_data; char *group = (char *)call_data; KILL_WIDGETS *w = file->w; long n = ScrListGetFirstSelected(w->list); KILL_NODE *node = n < 0 ? NULL : file->nodes[n]; regex_t re; int code; code = regcomp(&re, group, REGEXP_COMPILE_FLAGS); if (code != 0) { popup_regexpnotice(code, &re); return; } if (!node) regfree(&re); else { file->dirty = True; XtFree(node->group_str); node->group_str = XtNewString(group); if (node->group_re) regfree(node->group_re); else node->group_re = (regex_t *)XtMalloc(sizeof *node->group_re); memcpy(node->group_re, &re, sizeof re); update_list_entry(file, n); } } static void group_knapp_callback(Widget gw, XtPointer client_data, XtPointer call_data) { KILL_FILE *file = (KILL_FILE *)client_data; KILL_WIDGETS *w = file->w; if (!global.curr_group) XBell(display, 0); else { char *grp = regexp_escape_string(global.curr_group->name, True); XtSetKeyboardFocus(w->shell, w->group_field); TextFieldSetBuffer(w->group_field, grp); group_field_callback(w->group_field, (XtPointer)file, (XtPointer)grp); XtFree(grp); } } static void expr_field_callback(Widget gw, XtPointer client_data, XtPointer call_data) { KILL_FILE *file = (KILL_FILE *)client_data; KILL_NODE *node; char *buffer = (char *)call_data; long n = ScrListGetFirstSelected(file->w->list); int field; if (!buffer) buffer = ""; node = (n < 0 || n >= file->n) ? NULL : file->nodes[n]; field = node ? node->field : KnappGetLabelNo(file->w->field_knapp); if (!node) if (buffer[0] != '\0') set_field_sens(file->w, field); else { set_field_sens(file->w, -1); return; } if (field == KillFieldMsgid) { if (buffer[0] != '<' || buffer[strlen(buffer) - 1] != '>') { set_message("Bad Message-ID!", True); return; } if (node) { file->dirty = True; XtFree(node->expr_str); node->expr_str = XtNewString(buffer); update_list_entry(file, n); } } else { regex_t re; int code; code = regcomp(&re, buffer, REGEXP_COMPILE_FLAGS); if (code != 0) { popup_regexpnotice(code, &re); return; } if (!node) regfree(&re); else { file->dirty = True; XtFree(node->expr_str); node->expr_str = XtNewString(buffer); if (node->expr_re) regfree(node->expr_re); else node->expr_re = (regex_t *)XtMalloc(sizeof *node->expr_re); memcpy(node->expr_re, &re, sizeof re); update_list_entry(file, n); } } } static void expr_knapp_callback(Widget gw, XtPointer client_data, XtPointer call_data) { KILL_FILE *file = (KILL_FILE *)client_data; KILL_WIDGETS *w = file->w; char *expr = NULL; if (global.mode != NewsModeGroup && global.mode != NewsModeThread) return; switch (KnappGetLabelNo(w->field_knapp)) { case KillFieldMsgid: if (global.curr_art) { expr = XtMalloc(global.curr_art->hash_len + 4); sprintf(expr, "<%s>", global.curr_art->msgid); } break; case KillFieldSubject: if (global.curr_subj) expr = regexp_escape_string(global.curr_subj->subject, True); break; case KillFieldFrom: if (global.curr_art) expr = regexp_escape_string(global.curr_art->from, True); break; case KillFieldXref: if (global.curr_art && global.curr_art->xref) expr = regexp_escape_string(global.curr_art->xref, True); break; default: return; } if (!expr) XBell(display, 0); else { XtSetKeyboardFocus(w->shell, w->expr_field); TextFieldSetBuffer(w->expr_field, expr); expr_field_callback(w->expr_field, (XtPointer)file, (XtPointer)expr); XtFree(expr); } } static void color_callback(Widget gw, XtPointer client_data, XtPointer call_data) { KILL_FILE *file = (KILL_FILE *)client_data; char *color = (char *)call_data; KILL_WIDGETS *w = file->w; long n = ScrListGetFirstSelected(w->list); KILL_NODE *node = n < 0 ? NULL : file->nodes[n]; XColor col; if (!XParseColor(display, global.cmap, color, &col)) { popup_colornotice(True); return; } if (node) { if (!node->hot) return; file->dirty = True; XtFree(node->color); node->color = XtNewString(color); if (node->alloced_pixel) { unsigned long pixel = node->pixel; XFreeColors(display, global.cmap, &pixel, 1, 0); node->alloced_pixel = False; } if (XAllocColor(display, global.cmap, &col)) { node->alloced_pixel = True; node->pixel = col.pixel; } else { popup_colornotice(False); node->pixel = get_closest_color(&col); } fix_node_pixmap(node); update_list_entry(file, n); } XtSetKeyboardFocus(w->shell, w->expr_field); } /*********************************************************************/ static void init_list(KILL_FILE *file) { KILL_WIDGETS *w = file->w; char buffer[256]; long n; ScrListClearLines(w->list); for (n = 0 ; n < file->n ; n++) { sprint_kill_node(file->nodes[n], buffer, sizeof buffer); ScrListAddLine(w->list, buffer, file->nodes[n]->pixmap); } Remanage(w->listmgr); } static void create_kill_widgets(KILL_FILE *file) { KILL_WIDGETS *w; Widget layout, temp, vbar, hbar; Arg args[12]; file->w = w = (KILL_WIDGETS *)XtMalloc(sizeof *w); XtSetArg(args[0], XtNcolormap, global.cmap); XtSetArg(args[1], XtNvisual, global.visual); XtSetArg(args[2], XtNdepth, global.depth); XtSetArg(args[3], XtNinput, True); w->shell = XtCreatePopupShell("killeditor", topLevelShellWidgetClass, main_widgets.shell, args, 4); layout = XtVaCreateManagedWidget("killayout", layoutWidgetClass, w->shell, XtVaTypedArg, XtNlayout, XtRString, file->group ? #include "layouts/kill.h" : #include "layouts/killg.h" (int)sizeof(char*), (void *)0); XtSetArg(args[0], XtNbuffer, file->group ? file->group->name : "Global kill file"); XtCreateManagedWidget("killtitle", messageWidgetClass, layout, args, 1); XtSetArg(args[0], XtNvertical, False); hbar = XtCreateManagedWidget("hbar", scrBarWidgetClass, layout, args, 1); vbar = XtCreateManagedWidget("vbar", scrBarWidgetClass, layout, NULL, 0); w->listmgr = XtCreateManagedWidget("killistmgr", managerWidgetClass, layout, NULL, 0); XtSetArg(args[0], XtNhBar, hbar); XtSetArg(args[1], XtNvBar, vbar); XtSetArg(args[2], XtNpixmapWidth, HOT_PIXMAP_SIZE); XtSetArg(args[3], XtNpixmapHeight, HOT_PIXMAP_SIZE); XtSetArg(args[4], XtNdepthOne, False); XtSetArg(args[5], XtNallowDnd, True); XtSetArg(args[6], XtNusePixmaps, True); XtSetArg(args[7], XtNatMostOne, True); XtSetArg(args[8], XtNatLeastOne, False); XtSetArg(args[9], XtNcontainHoriz, False); XtSetArg(args[10], XtNcontainVert, True); w->list = XtCreateManagedWidget("killist", scrListWidgetClass, w->listmgr, args, 11); XtAddCallback(w->list, XtNselectCallback, list_callback, (XtPointer)file); XtAddCallback(w->list, XtNdndCallback, list_dnd_callback, (XtPointer)file); w->add_knapp = XtCreateManagedWidget("add", knappWidgetClass, layout, NULL, 0); XtAddCallback(w->add_knapp, XtNcallback, add_callback, (XtPointer)file); w->delete_knapp = XtCreateManagedWidget("delete", knappWidgetClass, layout, NULL, 0); XtAddCallback(w->delete_knapp, XtNcallback, delete_callback, (XtPointer)file); temp = XtCreateManagedWidget("clear", knappWidgetClass, layout, NULL, 0); XtAddCallback(temp, XtNcallback, clear_callback, (XtPointer)file); temp = XtCreateManagedWidget("close", knappWidgetClass, layout, NULL, 0); XtAddCallback(temp, XtNcallback, close_callback, (XtPointer)file); if (!file->group) w->stayup = NULL; else { w->stayup = XtCreateManagedWidget("stayup", toggleWidgetClass, layout, NULL, 0); XtAddCallback(w->stayup, XtNcallback, stayup_callback, (XtPointer)file); file->stay_up = ToggleGet(w->stayup); } XtCreateManagedWidget("fieldmessage", messageWidgetClass, layout, NULL, 0); XtSetArg(args[0], XtNmenuName, "fieldshell"); XtSetArg(args[1], XtNresizable, False); w->field_knapp = XtCreateManagedWidget("fieldknapp", menuKnappWidgetClass, layout, args, 2); XtCreateManagedWidget("scopemessage", messageWidgetClass, layout, NULL, 0); XtSetArg(args[0], XtNmenuName, "scopeshell"); XtSetArg(args[1], XtNresizable, False); w->scope_knapp = XtCreateManagedWidget("scopeknapp", menuKnappWidgetClass, layout, args, 2); XtSetArg(args[0], XtNresizable, False); w->action_knapp = XtCreateManagedWidget("actionknapp", toggleWidgetClass, layout, args, 1); XtAddCallback(w->action_knapp, XtNcallback, action_callback, (XtPointer)file); XtSetArg(args[0], XtNcolormap, global.cmap); XtSetArg(args[1], XtNvisual, global.visual); XtSetArg(args[2], XtNdepth, global.depth); temp = XtCreatePopupShell("fieldshell", menuShellWidgetClass, w->shell, args, 3); temp = XtCreateManagedWidget("fieldmenu", menuWidgetClass, temp, NULL, 0); w->messageid_field = MenuCreateGadget("messageid", stringGadgetClass, temp, NULL, 0); XtAddCallback(w->messageid_field, XtNcallback, field_callback, (XtPointer)file); MenuCreateGadget("sep", separatorGadgetClass, temp, NULL, 0); w->subject_field = MenuCreateGadget("subject", stringGadgetClass, temp, NULL, 0); XtAddCallback(w->subject_field, XtNcallback, field_callback, (XtPointer)file); w->from_field = MenuCreateGadget("from", stringGadgetClass, temp, NULL, 0); XtAddCallback(w->from_field, XtNcallback, field_callback, (XtPointer)file); w->xref_field = MenuCreateGadget("xref", stringGadgetClass, temp, NULL, 0); XtAddCallback(w->xref_field, XtNcallback, field_callback, (XtPointer)file); XtSetArg(args[0], XtNcolormap, global.cmap); XtSetArg(args[1], XtNvisual, global.visual); XtSetArg(args[2], XtNdepth, global.depth); temp = XtCreatePopupShell("scopeshell", menuShellWidgetClass, w->shell, args, 3); temp = XtCreateManagedWidget("scopemenu", menuWidgetClass, temp, NULL, 0); w->article_scope = MenuCreateGadget("article", stringGadgetClass, temp, NULL, 0); XtAddCallback(w->article_scope, XtNcallback, scope_callback, (XtPointer)file); w->subject_scope = MenuCreateGadget("subject", stringGadgetClass, temp, NULL, 0); XtAddCallback(w->subject_scope, XtNcallback, scope_callback, (XtPointer)file); w->thread_scope = MenuCreateGadget("thread", stringGadgetClass, temp, NULL, 0); XtAddCallback(w->thread_scope, XtNcallback, scope_callback, (XtPointer)file); w->subthread_scope = MenuCreateGadget("subthread", stringGadgetClass, temp, NULL, 0); XtAddCallback(w->subthread_scope, XtNcallback, scope_callback, (XtPointer)file); if (file->group) w->group_field = NULL; else { XtSetArg(args[0], XtNresizable, False); w->group_knapp = XtCreateManagedWidget("groupknapp", knappWidgetClass, layout, args, 1); XtAddCallback(w->group_knapp, XtNcallback, group_knapp_callback, (XtPointer)file); XtSetArg(args[0], XtNfocusRoot, w->shell); XtSetArg(args[1], XtNsingleLine, True); w->group_field = XtCreateManagedWidget("groupfield", textFieldWidgetClass, layout, args, 2); XtAddCallback(w->group_field, XtNcallback, group_field_callback, (XtPointer)file); XtAddCallback(w->group_field, XtNtabCallback, tab_callback, (XtPointer)file); } XtSetArg(args[0], XtNresizable, False); XtSetArg(args[1], XtNjustify, JustifyTypeLeft); w->expr_knapp = XtCreateManagedWidget("exprknapp", knappWidgetClass, layout, args, 2); XtAddCallback(w->expr_knapp, XtNcallback, expr_knapp_callback, (XtPointer)file); XtSetArg(args[0], XtNfocusRoot, w->shell); XtSetArg(args[1], XtNsingleLine, True); w->expr_field = XtCreateManagedWidget("exprfield", textFieldWidgetClass, layout, args, 2); XtAddCallback(w->expr_field, XtNcallback, expr_field_callback, (XtPointer)file); XtAddCallback(w->expr_field, XtNtabCallback, tab_callback, (XtPointer)file); XtCreateManagedWidget("colormessage", messageWidgetClass, layout, NULL, 0); XtSetArg(args[0], XtNfocusRoot, w->shell); XtSetArg(args[1], XtNsingleLine, True); XtSetArg(args[2], XtNpreferredChars, 12); w->color_field = XtCreateManagedWidget("colorfield", textFieldWidgetClass, layout, args, 3); XtAddCallback(w->color_field, XtNcallback, color_callback, (XtPointer)file); XtAddCallback(w->color_field, XtNtabCallback, tab_callback, (XtPointer)file); XtSetKeyboardFocus(w->shell, w->expr_field); XtRealizeWidget(w->shell); XtInstallAllAccelerators(w->shell, w->shell); add_WM_DELETE_WINDOW_callback(w->shell, close_callback, (XtPointer)file); init_list(file); set_kill_controls(file, -1); } void destroy_kill_widgets(KILL_WIDGETS *w) { if (is_popped_up(w->shell)) XtPopdown(w->shell); XtDestroyWidget(w->shell); XtFree((char *)w); } void popup_kill_editor(KILL_FILE *file) { if (!file->w) create_kill_widgets(file); if (!is_popped_up(file->w->shell)) XtPopup(file->w->shell, XtGrabNone); } void popdown_kill_editor(KILL_WIDGETS *w) { if (is_popped_up(w->shell)) XtPopdown(w->shell); } void kill_editor_notify_add(KILL_FILE *file, int append) { if (!append) { init_list(file); set_kill_controls(file, 0); } else { char buffer[256]; KILL_NODE *node = file->nodes[append ? file->n - 1 : 0]; sprint_kill_node(node, buffer, sizeof buffer); ScrListAddLine(file->w->list, buffer, node->pixmap); Remanage(file->w->listmgr); } } ./knews-1.0b.1/src/knews.man100644 1244 1244 122544 6455455545 14322 0ustar kallekalle.TH KNEWS 1 "1996" .SH NAME knews \- Karl's threaded newsreader for X .SH SYNOPSIS .B knews [ options ] .SH DESCRIPTION Knews is a threaded newsreader with an X Window interface that uses NNTP to get news. This manual is intended to explain things that are not apparent from the interface, so if you just want to get started, you probably don't need to read this. .SH OPTIONS In addition to the standard X Toolkit options knews accepts the following options, which set various X resources. For an explanation of what the resources do, see the sections on resources and the config file. You don't have to type out the entire name of the option, as long as it is unique. .B -nntpServer hostname .RS Sets the resource 'Knews.nntpServer' to 'hostname'. This will cause knews to connect to this server on startup. .RE .B +/-bell .RS Sets the resource 'Knews.bell' to True/False. .RE .B +/-separate .RS Sets the resource 'Knews.separateWindows' to True/False. .RE .B +/-active .RS Sets the resource 'Knews.readActiveFile' to True/False. .RE .B +/-descriptions .RS Sets the resource 'Knews.retrieveDescriptions' to True/False. .RE .B +/-fill .RS Sets the resource 'Knews.fillNewsrcFile' to True/False. .RE .B +/-keep .RS Sets the resource 'Knews.keepThreadInfo' to True/False. .RE .B -install .RS This will make knews install its own colormap. .RE .nf .B -visual class .B -depth bits .fi .RS These will set the resources 'Knews.visualClass' and 'Knews.visualDepth'. .RE .B +/-icase .RS Will set the resource 'Knews.icaseRegexps' to True/False. .RE .B +/-images .RS This will turn on/off inline images and color allocation. .RE .B -ncols number .RS Sets the resource Knews.nCols to 'number', which is the max number of colors knews will allocate for inline images. .RE .B -version .RS Prints out the version and compile time to stderr and quits. .RE .SH "THE THREAD TREE" The articles in a thread are displayed as nodes in a tree. A border inside a node indicates that the article is unread, a border outside a node indicates that the article is 'tagged', and a dashed branch in the tree indicates a change of subject. By default, you can scroll around in the tree with the middle mouse button. An empty box in the tree indicates a 'fake' article, i.e. one that has expired on the server, has not yet arrived or was posted in a different newsgroup. .SH "TAGGING ARTICLES" Articles in the thread tree can be tagged with the third mouse button. Clicking on a subject with the third mouse button causes the unread articles in that thread to be tagged in preorder (depth first). Once you have tagged the articles, you can mark then read/unread, save or pipe them in order, and so on. .SH "CLICKING ON URLS" Knews supports a simple form of clicking on URLs. It works by selecting a piece of text in the article text window and clicking on it with the middle mouse button. If there is no selection, knews will make a crude guess as to what the URL might be. The resource Knews.urlCommand must be set for this to work, see the section on X resources for details. .SH "DRAG AND DROP" The all groups list and the kill list can be reordered by drag'n'drop. The default translation is the second mouse button. .SH ABORT You may abort the interaction with the NNTP server at any time. This causes the connection to be closed, and a new one to be opened. Note that this puts a certain load on the server. .SH "THREAD AHEAD" Knews is capable of threading groups in the background while you are e.g. reading another group. To do this you click on one or more groups in the group list with the right mouse button. Knews then opens a second connection to the server and uses it to thread the selected groups. The status of the thread ahead is shown in the group list as a character: .nf - The group is scheduled for thread ahead. * The group is being threaded. + The group has been threaded. .fi .SH "REGULAR EXPRESSIONS" The regular expressions used by knews are POSIX.2 extended regular expressions, similar to those used by egrep(1), by default case insensitive. Note that these are not anchored by default, so that e.g. the expression 'alt' will match any string containing the three character sequence 'alt'. See Henry Spencer's excellent man-page for details, regex(7). .SH SEARCHING Article heads and bodies may be searched for regular expressions. Searching applies to read or unread articles as specified with the 'only unread' toggle, and starts with the 'next' article. The newsgroup list may also be searched. Note: the 'Stop' button stops the search as soon as the current article has been retrieved from the server. This is different from 'Abort' which requires closing and reopening the connection to the server. It is also possible to use XPAT searching, if the nntp server supports it. To do this, you fill in the 'Header' field on the search popup with the header you're interested in (e.g. 'Content-Type') and the wildcard field with a wildcard expression. Pressing 'Submit' then sends this to the server, and after a while it responds with a list of matching articles. You can then move between those articles with the 'Next' and 'First' buttons on the search popup. .SH "FILE NAME EXPANSION" In most places where knews uses file names, such as the save/pipe popup, the save/pipe action procedures, and the newsrcFile options etc, ~ is expanded to $HOME, and the following %'s are expanded: .nf %% % %n The name of the current group. %N The name of the current group, capitalized. %g The name of the current group, slashed. %G The name of the current group, capitalized and slashed. %s The name of the nntp server. %p The number of the port the server listens to. %a The number of the currently selected article. .fi Slashed means that the dots are replaced with slashes. Note that if you save an entire thread to a file continaing %a, the number will not change with the article. .SH "THE KILL FILE" The kill file may be used to 'kill' (mark read) or 'hot' (mark interesting) articles, subjects and threads based on different criteria. Each line in the kill file specifies an entry according to the following syntax: .nf (F)(S)(A)[Col] || Group regexp || Field expression .fi .B (F) .RS This is a character specifying to which header this entry applies. Legal values are: .nf \'M' The 'Message-ID:' header, by far the most efficient. \'S' The 'Subject:' header. \'F' The 'From:' header. \'X' The 'Xref:' header. .fi Note that 'X' only works if the 'Xref:' header is included in the overview files from the server. Also note that the 'Re: ' prefix is not considered part of the subject. Entries applying to a message-id automatically expire when the relevant article has expired. .RE .B (S) .RS This is a character specifying the scope of the entry, i.e. what articles are killed/'hotted' when this entry applies to an article. Legal values are: .nf \'A' This article. \'S' All articles with the same subject. \'T' The entire thread. \'t' The subthread starting with this article. .fi .RE .B (A) .RS This is a character specifying the action of the entry. Legal values are: .nf \'K' Kill, which means mark read. \'H' Hot, which means mark interesting. .fi .RE .B [col] .RS In 'hot' entries, this is the color used to mark the relevant articles with. .RE .B || .RS This two character sequence is used as a separator. .RE .B Group regexp .RS Only newsgroups matching this regular expression will be affected by this entry. This field is empty in the per-group kill files. .RE .B Field expression .RS If the header field is 'S', 'F' or 'X', this is a regular expression, and the entry applies to all matching articles. If the header field is 'M', this is a message id; this id is probably the fastest type of kill entry, since it can be checked with a single hash lookup. .RE To see what articles were killed: when you have read all unread articles, or marked them read, use the 'mark unread, killed' feature on the misc menu. When a kill rule is applied, hot articles are not killed. Since the entries in the kill file are processed in order, it is possible to put kills at the beginning, so that the articles are killed before they have a chance to become hot. There is one global kill file (~/.knews/.kill-%s by default) and one kill file per group (~/.knews/%s/%g/KILL by default). The rules in the global kill file are applied before the per-group kill file. .SH "MIME VIEWERS AND MAILCAP FILES" Knews has internal support for content-types text/plain, message/rfc822, message/partial, multipart/mixed and multipart/digest. For other types, knews will look for a mailcap entry for that type (see mailcap(5)). If one is found, a clickable line will be inserted in the article text window that is used to start the viewer. If there is no viewer, then if the type is a subtype of text, knews will display it, if it is a subtype of multipart, knews will treat it as multipart/mixed, and otherwise knews will give the user opportunity to 'Save or Pipe'. Mailcap files are found via the environment variable MAILCAPS, which is a colon separated list of path names. If this variable is not set, a default list of .nf $HOME/.mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap .fi will be used. Note that all files found will be merged to produce the mailcap database. As a hack around miss/over designed mailcap files, knews will ignore entries for text/plain and multipart/mixed. .SH "TEXT/PLAIN ARTICLES" The following resources determine how knews will display an article of type text/plain with a particular charset: .nf Knews.charset.headerFont Knews.charset.bodyFont Knews.charset.quoteFont Knews.charset.listFont Knews.charset.treeFont Knews.charset.encoding Knews.charset.headEncHack .fi The header, body and quote fonts are used for displaying headers, ordinary text and quoted text in the article window. Knews also has some support for encoded 16-bit charsets, this is specified with the encoding resource. Legal values for this are: .nf utf-7 The encoding specified for unicode in rfc 1642. 16-bit Straight 16-bit network byte order. hz-gb-2312 The encoding for chinese described in rfc 1842. ksc-5601 Also called iso-2022-kr, described in rfc 1557. big5 Another encoding for chinese. .fi Note that due to the authors non-existent understanding of asian languages these encodings have not been extensively tested. Lots of guess work here. For every newsgroup it is possible to specify a default charset (see the section on THE CONFIG FILE). The fonts for this charset will be used to display articles without proper MIME-headers. Also, the listFont and treeFont (which may not be encoded) will be used in the thread list and article tree, respectively. When specifying an encoded charset as defaultCharset, it may be desirable to still assume that headers are ascii. This may be accomplished by setting the 'headEncHack' resource listed above, and setting the headerFont to an ascii (superset) font. .SH "MESSAGE/PARTIAL" Unless the config option assemblePartials is False (see the section on THE CONFIG FILE), when knews encounters an article of type message/partial, it will be rememberered. When all the parts have been seen, a notice will be popped up offering to assemble the parts. There is also an entry on the misc menu that allows a number of articles to be tagged and processed to look for message/partial articles. .SH "POSTING MIME" When you post an article containing 8 bit characters that doesn't have a Content-Type header, knews will add such a header with charset equal to the value of the defaultCharset config option. Also, when quoting articles with Content-Type: text/plain and charset equal to the defaultCharset, knews will decode the article before quoting. In both these contexts, the charset iso-8859-1 will be used if defaultCharset is not set. .SH "READING THE SPOOLDIR" Knews supports a rather obscure mechanism for communicating with an arbitrary program instead of an nntp server. Using knewsd(1), this can be used for reading news (in)directly from the spool dir, or even mail folders if they are in the correct format. This is how it works: If the nntp server is given as #str, where str is an arbitrary string not containing white space or the characters '/', '.' or '*', knews finds the value of the resource .B Knews.#str and tries to execute that program. The program has its standard input and output connected to knews. If you use this resource setting: .nf Knews.#spool: knewsd -spool /var/spool/news \\ -active /usr/local/news/active .fi with the appropriate paths for your system, you can read the spool directory by specifying the nntp server as '#spool' in e.g. the connect dialogue. This could easily be used to read mh(1) style mail folders too. .SH "X RESOURCES" There are a number of X resources that affect the behaviour and appearance of knews. Most widgets in the widget hierarchy has resources named .B background , .B foreground , .B shadowWidth , and so on. For more information see the application defaults file Knews.ad included in the distribution. To get a feeling for resources, try editres(1). Knews contains a small xpm-file to pixmap converter, so it is possible to use settings like e.g. .nf Knews*backgroundPixmap: ~/pixmaps/texture.xpm .fi .B Knews.nntpServer .RS If this resource is set, knews automatically connects to this nntp server on startup. This overrides the NNTPSERVER environment variable. If the server listens to a non-standard port, you may specify this e.g as foo.bar:1234. If you don't want knews to autoconnect, don't set this or $NNTPSERVER. .RE .B Knews.editCommand .RS This resource specifies the editor used to edit posts. Possible values include: .nf Knews.editCommand: xemacs +%i %s Knews.editCommand: emacs +%i %s Knews.editCommand: xterm -e vi +%i %s .fi %s stands for the name of a temporary file, and %i for the line where editing should begin. The default value of this resource is a compile time option. .RE .B Knews.urlCommand .RS This command is used for clicking on URLs. %s is expanded to the URL, and the result is passed to the shell. As a simple security measure, URLs will not be allowed to contain quotes, parentheses, whitespace or ampersands. .RE .B Knews.printCommand .RS If this resource is set, the misc menu will have a print option which invokes this command. .RE .nf .B Knews.needsTerminal .B Knews.copiousOutput .fi .RS These are two shell command templates that will be used for mailcap viewers that have the .B needsterminal or the .B copiousoutput flag set, respectively. In these, %C will be expanded to the relevant mailcap command. An example should explain it: .nf Knews.needsTerminal: exec xterm -e /bin/sh -c '%C' Knews.copiousOuptut: exec xterm -e /bin/sh '(%C) | less' .fi .RE .B Knews.mimeTypes .RS This should point to a file whose contents maps filename extensions to mime types. The syntax of this file is examplified by the following list of compiled in types: .nf image/jpeg jpg jpeg image/gif gif application/postscript ps .fi This is used to guess the Content-Type of attachments. .RE .B Knews.bell .RS Setting this resource to False will turn off the bell. .RE .B Knews.sortGroups .RS If this is set to True, knews will keep newsgroups alphabetically sorted when new groups are subscribed. .RE .B Knews.separateWindows .RS Setting this resource to True will make knews use a different top level window for the article text widget. .RE .B Knews.stderrTimeout .RS When knews starts a pipe or similar, it captures the standard error output and displays it in a notice popup. This resource is the time in milliseconds this popup should stay up. The default is 10000. Setting this to 0 means stay up indefinitely, and negative means no popups. .RE .B Knews.showCache .RS If this is True, knews will show the state of the article caches in a small popup. See the config options cacheAheadSize and cacheTrailSize for details. .RE .B Knews.mailName .RS If you have a mail address which isn't of the form 'userid@domain.name', you can set this to the part of the address that goes before the '@', e.g. .nf Knews.mailName: FirstName.LastName .fi Note that your userid will still be used for the 'Sender' header if necessary. .RE .B Knews.useIcon .RS If this is set to True (the default), knews will use an icon. .RE .B Knews.confirmQuit .RS Setting this to True will make knews ask for confirmation before disconnecting or quitting. .RE .B Knews.confirmCatchup .RS If this is set to True, knews will ask for confirmation before catching up a group. .RE .B Knews.confirmQuitGroup .RS If this is set to 'True', knews will ask for confirmation before leaving a group. If set to 'tagged', knews will ask confirmation when exiting a group if there are tagged articles. .RE .nf .B Knews.visualClass class .B Knews.visualDepth bits .fi .RS If these are set knews will use a visual of the specified class and depth. Typical values for depth are 8 or 24. Legal values for class are 'StaticGray)', 'GrayScale', 'StaticColor', \'PseudoColor', 'TrueColor' and 'DirectColor'. The depth will be ignored if no class is specified. .RE .B Knews.installCmap .RS If this is set to True, knews will create its own colormap. .RE .B Knews.inlineImages .RS This boolean resource turns on/off inline images and color allocation. Knews can show jpeg, gif and png images (if compiled with support for this). .RE .B Knews.nCols .RS This is the maximum number of colors knews will allocate for inline images. The default is 137 (17 greys and a 5x5x5 color cube minus the 5 greys in that cube). .RE .B Knews.colorHack .RS If this is set to True, knews try to allocate the same colors as other programs have already allocated, thus increasing the chances for color sharing. The number of colors is controlled by the resource Knews.nCols. .RE The following resources can be used to change various color, font and geometry settings: .nf .B Knews*grouplist.preferredLines .B Knews*grouplist.preferredColumns .fi .RS These resources specify the number of lines and columns the grouplist widget will start up with. The default is 14 for lines and 84 for columns. .RE .nf .B Knews*text.preferredLines .B Knews*text.preferredColumns .fi .RS These resources specify the number of lines and columns the article text widget start up with. The default is 32 for lines and 84 for columns. .RE .B Knews*ArtTree.nodeColumns .RS The width of the nodes in the article tree in characters. The default is 16. .RE .B Knews*rubberColor .RS The color used for rubberbanding in one or two widgets. The default is red. .RE .nf .B Knews.headerColor .B Knews.bodyColor .B Knews.quoteColor .fi .RS The colors used for headers, ordinary text and quoted text respectiely in the article window. .RE .nf .B Knews*innerColor .B Knews*innerDashed .fi .RS The color and line style used for the border of unread articles in the article tree. The defaults are Red and False. .RE .nf .B Knews*outerColor .B Knews*outerDashed .fi .RS The color and line style used for the border of tagged articles in the article tree. The defaults are foreground and False. .RE .B Knews*ScrList.font .RS The font used in the lists. .RE .B Knews.defaultHotColor .RS The color used for hot entries in the kill file when the specified color is invalid or cannot be allocated. .RE Some miscellaneous resources: .B Knews.icaseRegexps .RS Setting this to False will make regular expressions case sensitive. They are case insensitive by default. .RE .nf .B Knews.readActiveFile .B Knews.retrieveDescriptions .B Knews.fillNewsrcFile .B Knews.showNumberLines .B Knews.keepThreadInfo .B Knews.checkForNewGroups .fi .RS These set the default values for the corresponding configuration options. Their main purpose is to allow command line arguments. Read the section on the config file for details. .RE .nf .B Knews.newsrcTemplate .B Knews.oldNewsrcTemplate .fi .RS These set the default values for the configuration options newsrcFile and oldNewsrcFile, the default values are ~/.newsrc-%s and ~/.oldnewsrc-%s. A value not containing %s will not be accepted; if you want the traditional ~/.newsrc for a particular server, see the resource Knews.configNntpServer below. .RE .B Knews.killFileTemplate .RS This sets the default value of the config option killFile. The default value is ~/.knews/.kill-%s. .RE .B Knews.groupKillFileTemplate .RS This is the template for the per-group kill file. The default is ~/.knews/%s/%g/KILL, so that e.g. the newsgroup news.software.readers will have ~/.knews/%s/news/software/readers/KILL as kill file, where %s is the name of the server, as usual. .RE .B Knews.configFile .RS The configuration file used by knews. The default value is ~/.knews/config-%s, a value not containing %s will not be accepted. .RE .nf .B Knews.configNntpServer .B Knews.configPostingAgent .fi .RS Setting configNntpServer will make knews Do The Right Thing when the user first connects to this server, which means setting the newsrc file for this server to ~/.newsrc when creating the config file. If the configPostingAgent is set this will be used for the postingAgent config option for the configNntpServer. .RE .B Knews.generatePath .RS If this is set to True, knews will generate a Path header for articles. The header will be 'Path: d!u' where d and u are such that the From header generated by knews would be 'From: u@d'. .RE .B Knews.autoSubscribe .RS The value of this resource will be used when creating a new newsrc file. If it starts with a '/', it is taken as a pathname of a file whose contents will be inserted into the new newsrc file, otherwise the literal value of this resource will be inserted into the newsrc file. The default value is .nf news.answers:\\nnews.newusers.questions:\\n .fi .RE .B Knews.bogusFileSystem .RS When knews checks for new groups, it uses the atime (time of last access) of the config file. Some filesystems (e.g. AFS) have no concept of atime, but fakes it with mtime (time of last modification) instead. Setting this resource to True will make knews forcibly update the mtime of the config file. .RE .SH THE CONFIG FILE When knews connects to an NNTP server it reads a configuration file that will affect its behavior. This file is by default called ~/.knews/config-%s where %s expands to the name of the server, but this may be changed with the Knews.configFile resource. The syntax of the config file is the same as for X resource files. When knews can't find the configure file, a new one will be created containing some default settings and a few examples settings that should be sufficient to clue you in as to how it works. It is possible to used #include statements in the config file to include other files. Relative pathnames are considered relative to the current working directory, which for knews is always $HOME. ~ pathnames are not handled in #includes (if you want that you have to hack Xlib). The following global (i.e. per server) options exist. .nf .B newsrcFile .B oldNewsrcFile .fi .RS These specify the newsrc file and oldnewsrc file for this server. ~ pathnames and the same % expansions as for saving are handled. If oldnewsrc is set to an empty string, no backup of the newsrc file will be created. The default values for these are the values of the resources Knews.newsrcTemplate and Knews.oldNewsrcTemplate, whose default values are ~/.newsrc-%s and ~/.oldnewsrc-%s, respectively. For a way of automatically using the standard file ~/.newsrc for a specific server, see the resource Knews.configNntpServer above. .RE .B killFile .RS The kill file. The default value is the value of the resource Knews.killFileTemplate, whose default value is ~/.kill-%s. .RE .B cacheDir .RS This directory is used for storing cached articles and thread data for groups. The default is ~/.knews/cache-%s. .RE .B readActiveFile .RS Setting this to False will stop knews from reading the active file when connecting, using the groups in the newsrc file instead. This will speed up connection on slow lines if you don't have too many subscribed groups. The default is the value of the resource Knews.readActiveFile, whose default is True. .RE .B retrieveDescriptions .RS A boolean option indicating whether to retrieve newsgroup descriptions from the server. The default is the value of the resource Knews.retrieveDescriptions, whose default is True. Setting this to False may slightly speed up connection time. .RE .B descriptionsFile .RS If this is set, the given file will be used to cache group descriptions: when retrieveDescriptions is True, knews saves the descriptions to this file and when retrieveDescriptions is False, knews reads descriptions from this file instead of from the server. .RE .B fillNewsrcFile .RS Setting this to True will make knews write all groups it knows about to the newsrc file, which may be a good idea if the option readActiveFile is set to False. The default is the value of the resource Knews.fillNewsrcFile, whose default is False, which means only put information in the newsrc file. .RE .B tryListActive .RS When this is True (the default) and readActiveFile is False, knews will try the "LIST ACTIVE wildmat" nntp extension. If this fails knews will complain and fall back to the old "GROUP" stuff. .RE .B checkForNewGroups .RS This is a boolean option indicating whether to check for new groups when connecting to this server. The default is True. The atime (time of last access) of the config file will be used for the check. .RE .B threadAheadGroups .RS This is a white-space separated list of groups to be automatically scheduled for thread ahead when connecting. The special values 'all' and 'All' may be used to designate all subscribed groups with unread articles and all subscribed groups, respectively. .RE .B saveThreadInfo .RS Setting this to True will allow 'thread ahead' data to be saved between sessions: knews will not remove the files with this data when quitting, and when connecting knews will check for these files for all subscribed groups. If this is set, the options threadAheadGroups and keepThreadInfo will be ignored. .RE .B rescanTimeout .RS This indicates the time in minutes between automatic rescans. The default is 60 minutes, 0 means no automatic rescans. Regardless of this, rescans will only be performed at special points, to prevent a 'rescan-idle-rescan' loop. .RE .B groupNameColumns .RS The width of the group name in the group list, default is 42. .RE .B askHowMany .RS Setting this to True will make knews ask at which article the threading of a group should start. A hack. .RE .B postingAgent .RS If this is specified, knews will use this for posting, instead of posting via NNTP. If your server requires some kind of authentication that only inews understands, you could set this to 'inews -h'. Note that inews appends the signature, so you don't want knews to add one too. Also see the resource Knews.configPostingAgent above. .RE .nf .B authInfoPass .B authInfoUser .fi .RS These are used to implement the NNTP AUTHINFO USER/PASS protocol if required by the server. These exist mostly for backward compatibility; authentication is usually only required when posting, and then it is better to use inews for postingAgent, since presumably inews knows all about the required authentication. .RE Here is an example of some settings that will improve things over a slow network connection: .nf readActiveFile: False retrieveDescriptions: False descriptionsFile: ~/.knews/cache-%s/descriptions fillNewsrcFile: True .fi but note that you will probably want to read the active file and group descriptions at least the first time you connect to a server. The following resources may be set on a per group basis; they should be prefixed with the name of the group they apply to. .B keepThreadInfo .RS This tells knews whether to keep thread information in memory after the group is exited. This will make reentering the group fast. Legal values are: True, Subscribed and False. 'Subscribed' means only do it if the group is subscribed, and exists to allow settings such as: .nf *keepThreadInfo: Subscribed .fi to keep thread info for all subscribed groups. The default value for this option is 'Subscribed' if the resource Knews.keepThreadInfo is set to True and 'False' otherwise. .RE .nf .B cacheAheadSize .B cacheTrailSize .fi .RS These two set the sizes of the two article caches, the defaults are 0. The 'ahead cache' is used to prefetch articles from the server in the background using a second connection. The 'trail cache' is used to keep articles that you have already read, so that going back, saving or uudecoding will be faster. The maximum values for these are 32. .RE .B sortThreads .RS This indicates how the threads should be sorted. A thread consists of several subjects. These are sorted within the thread according to the order they occur. Then the threads are sorted according to the setting of this option. The legal values and their meanings are: .nf subject Alphabetically by the first subject in the thread. size Number of unread articles in the thread. full-size Number of articles in the thread. hot Number of hot articles in the thread. date The date of the first unread article. average-date The average date of unread articles in the thread. author Alphabetically by the first From: line in the thread. none No sorting. .fi All these values may be prefixed with a minus sign to indicate a reversal of the order, or a plus sign which is a no-op. The default value is none. .RE .B expireKills .RS Setting this to False will stop expirations from the kill file. The default is True, which means that Message-id kills will expire when you enter a group where the kill entry would have been applicable, but the article with that message-id was not found. .RE .B attribution .RS This string is used to attribute quotations when you post a followup. The default is .nf In article %m,\\n %f writes: .fi where \\n is a newline. The following %'s are expanded: .nf %% % %d The date of the quoted article in the form 01 Jan. %f The From: line of the article replied to. %i The initials of the previous poster. %I The initials of the previous poster, capitalized. %m The message-id of the article replied to. %n The current newsgroup. %r The real name of the previous poster. %s The subject of the quoted article. %t The time of the quoted article in the form 18:24:02. %w The week day of the quoted article. %y The year of the quoted article. .fi Thus '%w, %d %y %t GMT' will give the date in standard rfc822 format. .RE .B fullName .RS This is the full name used in the 'From:' header in the articles you post. The default is $NAME, if set, otherwise the gecos field from the password file, suitably truncated. .RE .B headerFormat .RS A colon and white-space separated list specifying which headers to show in the article window, and in what order. The default is .nf Subject:Newsgroups:Followup-To:Reply-To:\\ Content-Description:Date:Organization:From: .fi If the name of the header starts with a captial letter (From: as opposed to from:), knews will decode rfc1522 encoded words encountered in this header. (Those are the weird things that look like =?iso-8859-1?q?stuff_here?=.) Encoded 16-bit charsets are not decoded in headers yet. .RE .B assemblePartials .RS This boolean tells whether message/partial articles will be remembered and offered for assembly. .RE .B quoteRegexp .RS Lines in an article matching this regular expression will be considered quoted lines, and may be marked with a different color and font. The default is .nf ^[ \\t]*[:>|] .fi which matches lines beginning with an arbitrary amount of white space (the \\t denotes a tab, note that \\t won't actually work) followed by a >, : or | character. You will probably want to have this expression anchored... .RE .B defaultCharset .RS The fonts for this charset will be used to display articles that lack MIME-headers. If this is not set, us-ascii will be assumed. Also, rfc1522 encodings of this charset in the From and Subject header will be decoded when displayed in the article tree and the thread list. In this case, iso-8859-1 is the default. .RE .B showNumberLines .RS A boolean option indicating whether to show the number of lines in articles in the thread tree. The default is the value of the resource Knews.showNumberLines, whose default is False. .RE .B signatureFile .RS The contents of this file will be used to sign the articles you post (before editing). The default is ~/.signature. .RE .B subjectColumns .RS The width of the subject in the thread list, default is 56. .RE .nf .B quoteString .B quoteQuoteString .fi .RS These strings are used for quoting when posting a followup article; the first one are used to quote lines that were not quoted in the original article, and the second one is used for lines that were already quoted. What lines are considered quoted is determined by the quoteRegexp. The defaults are "> " and ">" respectively. In these strings, %i is expanded to the initials of the previous author and %I to the initials, capitalized. .RE .B postedAndMailed .RS This string will be instered into articles that are also mailed to the previous author. The default is "[posted and mailed]". .RE .B distribution .RS If this is set, it will be used as the content of a 'Distribution' header. The default value is the value of the environment variable DEFNEWSDIS if set, otherwise empty. .RE .B fullHeader .RS A boolean specifying whether to show all headers in the article window. It also means turn off all MIME transformations. The default is False. .RE .B replyTo .RS A string used to construct the 'Reply-To:' header in the articles you post. The default is the value of the environment variable REPLYTO if set, otherwise empty. .RE .B organization .RS A string used to construct the 'Organization:' header in the articles you post. The default is $NEWSORG if set, otherwise $ORGANIZATION if set, otherwise nothing. .RE .B processXrefs .RS If this boolean is True, as it is by default, articles that are crossposted will be marked read in all groups when you read them, mark them read, kill them or catch them up. Note that this only applies to subscribed groups, and will only work if the server's overview file contains the Xref: headers. .RE .B extraHeaders .RS This string is inserted into the head of all articles you post (before editing). The default is empty. This could be used to put in Mime headers, like this: .nf *extraHeaders: Content-Type: text/plain; charset=iso-8859-1 .fi .RE .B followupHeaders .RS This is a combination of extraHeaders and attribution: it is inserted in the headers of replies and followups and the same %'s as in attribution are expanded. An example: .nf *followupHeaders: X-Comment-To: %r .fi .RE .B uuDir .RS Uudecoded files will end up in this directory. Or rather, the forked off uudecoding process will be given this as its current working directory. The default is ~/News. .RE .B uuProgram .RS This program will be used to uudecode files: it will be given the bodies of the relevant articles on standard input. The default is NULL, which means that knews will do its best to filter out garbage and then pipe the rest to 'uudecode'. .RE Here is an example of how to set different signatures for different newsgroups: .nf swnet*signatureFile: ~/.signature-svensk de*signatureFile: ~/.signature-deutsch *linux*signatureFile: ~/.signature-linux .fi The first sets the file ~/.signature-svensk for all groups in the swnet hierarchy, the second one sets the file ~/.signature-deutsch for the de hierarchy, and the last one sets the file ~/.signature-linux for any group containing linux as a component (not merely a substring). The file ~/.signature will be used for all other groups. .SH ACTIONS Knews defines a number of actions that can be tied to keys and buttons via translations. For the default translations, see the application defaults file. .B do-the-right-thing() .RS Does the right thing. .RE .nf .B kill-append(field, scope [, color]) .B kill-prepend(field, scope [, color]) .fi .RS These action procedures append and prepend respectively entries to the kill file for the current group according to the currently selected article. Valid values for the 'field' parameter are "From", "Subject" and "Message-Id", and valid values for 'scope' are "Thread", "Subthread" and "Subject". If the color parameter is present, the entry added is a hot-entry with that color, otherwise it is a kill-entry. It is probably a good idea to use these with field = "message-id", since message-id kills are very efficient and expire with the corresponding article. .RE .B popup-kill([group]) .RS This will popup a kill file editor for the supplied group. If no group is given, the editor for the global kill file is popped up. .RE .B mime-hack(content-type, content-transfer-encoding) .RS This action procedure reloads the current article, pretending it had the specified Content-Type and Content-Transfer-Encoding headers. For example mime-hack(image/jpeg, uue) reloads the current article pretending it has Content-Type image/jpeg and is uuencoded, thus makeing knews display it as an inline image. The default key-bindings have the following: .nf ctrl-J mime-hack(image/jpeg, uue) ctrl-G mime-hack(image/gif, uue) ctrl-P mime-hack(image/png, uue) .fi .RE .nf .B tree-up(arg) .B tree-down(arg) .B tree-left(arg) .B tree-right(arg) .B tree-down-right(arg) .fi .RS These move around in the thread tree. If the arg is 'read', they will also read in the relevant article, if arg is 'fake', they will also try to read 'fake' articles. .RE .nf .B list-up(arg) .B list-down(arg) .fi .RS These move up and down in the lists. If arg is given, it is the number of steps, or if it contains a '.', the fraction of the window to move. .RE .nf .B enter-mode() .B exit-mode() .fi .RS These two actions moves between modes. .RE .nf .B tree-or-list-up(arg) .B tree-or-list-down(arg) .B tree-left-or-exit-mode(arg) .B tree-right-or-enter-mode(arg) .fi .RS These are combination actions. E.g. tree-or-list-up(arg) does tree-up or list-up(1), depending on which is relevant. .RE .B read-article(arg) .RS This rereads the current article. If arg is given, the article is displayed with full header and no MIME transformations. .RE .B view-thread(arg) .RS This moves between the subject list and the thread tree. If arg is 'toggle', it toggles, if arg is 'yes' it goes to the tree, and if arg is 'no', it goes to the subject list. .RE .nf .B followup(arg) .B reply(arg) .B followup-and-reply(arg) .B post-new() .fi .RS These correspond to the options on the post menu. If arg is given as 'yes' or 'no', it indicates whether to include quoted text. .RE .nf .B uudecode() .B clear-tagged() .B mark-read-article() .B mark-read-subject() .B mark-read-thread() .B mark-read-subthread() .B mark-read-to-current() .B mark-read-all() .B mark-read-tagged() .B mark-read-non-tagged() .B mark-read-cold() .B mark-unread-article() .B mark-unread-subject() .B mark-unread-thread() .B mark-unread-subthread() .B mark-unread-all() .B mark-unread-tagged() .B mark-unread-killed() .fi .RS These perform the corresponding functions on the misc menu. .RE .nf .B pipe(command, parts [, scope]) .B save(filename, parts [, scope]) .fi .RS Pipe and save actions. The argument 'parts' is a combination of the characters 'f', 's', 'h', 'b', 'e', corresponding to the 'bogus from', 'bogus subject', 'head', 'body' and 'empty line' options on the save popup window. The 'scope' parameter is optional, and is one of 'window', 'article', 'subject', 'thread', 'subthread' and 'tagged', corresponding to those options on the save popup. .RE .nf .B tag-thread([all]) .B tag-subject([all]) .fi .RS These tag the unread articles in a thread or subject. If 'all' is specified, they tag all articles in the thread or subject. .RE .nf .B untag-thread() .B untag-subject() .fi .RS These untag the tagged articles in a thread or subject. .RE .B tag-hot() .RS This action tags all unread hot articles, same as on the misc menu. .RE .nf .B catchup() .B unsubscribe() .B subscribe() .fi .RS Guess what. .RE .B change-size(pixels) .RS Changes the size of the upper portion of the main window by the specified number of pixels. .RE .B schedule-thread-ahead() .RS Causes a group to be scheduled for 'thread ahead'. .RE .B popup-find-group() .RS Popups the 'find group' popup, same as the 'find group' option on the misc menu. .RE .SH WIDGETS The X interface of knews is built with its own widget set plus the Layout Widget. You are welcome to use it if you like. Unfortunately there is no documentation. .SH AUTHOR This software is Copyright 1995, 1996 by Karl-Johan Johnsson. .SH ACKNOWLEDGMENTS The threading algorithm was inspired from trn. Thanks to Wayne Davison. Knews uses Keith Packard's Layout Widget. The distribution includes Henry Spencer's regex package for environments that do not have the POSIX.2 regular expression functions. Thanks to Mattias Jonsson for ardent testing. .nf From the gif89a spec: "The Graphics Interchange Format(c) is the Copyright property of CompuServe Incorporated. GIF(sm) is a Service Mark property of CompuServe Incorporated." .fi Any problems are of course entirely due to me. .SH "SEE ALSO" egrep(1), knewsd(1), trn(1), uudecode(1), mailcap(5), newsrc(5), regex(7). .SH "KNOWN PROBLEMS" If the server doesn't support XOVER, threading will be very slow. The uudecode function may not recognize or correctly handle all cases. The dithering algorithm used for grayscale images and color gifs (essentially 'closest match') is very poor. AUTHINFO SIMPLE doesn't work for the second connection. When the last article in a group has been cancelled, the number of unread articles may be incorrect. .SH BUGS Send bug reports to kjj@matematik.su.se ./knews-1.0b.1/src/font.c100644 1244 1244 15146 6455455545 13567 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include "global.h" #include "charset.h" #include "font.h" #include "resource.h" #include "util.h" #include "widgets.h" #define FONT_HASH_SIZE 7 typedef struct FontHashNode { struct FontHashNode *next; char *name; MimeFont f; } FontHashNode; static FontHashNode *font_table[FONT_HASH_SIZE]; MimeFont *ascii_font; MimeFont *default_font; static unsigned int hash(char *name) { unsigned int result = 0; while (*name != '\0') result += (unsigned char)*name++; return result % FONT_HASH_SIZE; } static MimeFont *font_hash_find(char *name) { FontHashNode *loop; loop = font_table[hash(name)]; while (loop) { if (loop->name[0] == name[0] && strcmp(loop->name, name) == 0) return &loop->f; loop = loop->next; } return NULL; } static MimeFont *font_hash_insert(char *name, MimeFont *font) { FontHashNode *node, *loop; unsigned int index; index = hash(name); node = (FontHashNode *)XtMalloc(sizeof *node); node->next = NULL; node->name = XtNewString(name); node->f = *font; loop = font_table[index]; if (!loop) font_table[index] = node; else { while (loop->next) loop = loop->next; loop->next = node; } return &node->f; } void init_fonts(Widget top) { static XtResource ascii_spec[] = { #define offset(field) XtOffsetOf(MimeFont, field) {"bodyFont", "Font", XtRFontStruct, sizeof(XFontStruct*), offset(body_font), XtRString, (XtPointer)XtDefaultFont}, {"quoteFont", "Font", XtRFontStruct, sizeof(XFontStruct*), offset(quote_font), XtRString, (XtPointer)XtDefaultFont}, {"headerFont", "Font", XtRFontStruct, sizeof(XFontStruct*), offset(header_font), XtRString, (XtPointer)XtDefaultFont}, {"listFont", "Font", XtRFontStruct, sizeof(XFontStruct*), offset(list_font), XtRString, (XtPointer)XtDefaultFont}, {"treeFont", "Font", XtRFontStruct, sizeof(XFontStruct*), offset(tree_font), XtRString, (XtPointer)XtDefaultFont}, #undef offset }; MimeFont f = {NULL, }; XtGetSubresources(top, &f, "us-ascii", "US-ASCII", ascii_spec, XtNumber(ascii_spec), NULL, 0); ascii_font = font_hash_insert("us-ascii", &f); if (!ascii_font->body_font) { fputs("knews: Resource knews.us-ascii.font is NULL! " "This ain't happening!\n", stderr); exit(1); } if (!ascii_font->quote_font) ascii_font->quote_font = ascii_font->body_font; if (!ascii_font->header_font) ascii_font->header_font = ascii_font->body_font; if (!ascii_font->list_font) ascii_font->list_font = ascii_font->body_font; if (!ascii_font->header_font) ascii_font->tree_font = ascii_font->body_font; } static MimeFont *load_font(char *name) { struct FontNames { String body_font; String quote_font; String header_font; String list_font; String tree_font; String encoding; String head_enc_hack; } font_res = {0, }; static XtResource font_spec[] = { #define offset(field) XtOffsetOf(struct FontNames, field) {"bodyFont", NULL, XtRString, sizeof(String), offset(body_font), XtRImmediate, (XtPointer)NULL}, {"quoteFont", NULL, XtRString, sizeof(String), offset(quote_font), XtRImmediate, (XtPointer)NULL}, {"headerFont", NULL, XtRString, sizeof(String), offset(header_font), XtRImmediate, (XtPointer)NULL}, {"listFont", NULL, XtRString, sizeof(String), offset(list_font), XtRImmediate, (XtPointer)NULL}, {"treeFont", NULL, XtRString, sizeof(String), offset(tree_font), XtRImmediate, (XtPointer)NULL}, {"encoding", "Encoding", XtRString, sizeof(String), offset(encoding), XtRImmediate, (XtPointer)NULL}, {"headEncHack", "HeadEncHack", XtRString, sizeof(String), offset(head_enc_hack), XtRImmediate, (XtPointer)NULL}, #undef offset }; MimeFont font; XFontStruct *def; char class[16]; char *c; int iso_num; int i; if (strncmp(name, "iso-8859-", 9) == 0 && name[9] >= '1' && name[9] <= '9' && name[10] == '\0') { iso_num = name[9] - '0'; sprintf(class, "iso-latin-%d", iso_num); c = "IsoFont"; } else { iso_num = 0; c = "MimeFont"; } for (i = 0 ; i < 5 ; i++) font_spec[i].resource_class = c; XtGetSubresources(main_widgets.shell, &font_res, name, iso_num > 0 ? class : name, font_spec, XtNumber(font_spec), NULL, 0); #define DO(name) \ if (!font_res.name) \ font.name = NULL; \ else { \ font.name = XLoadQueryFont(display, font_res.name); \ if (!font.name) \ fprintf(stderr, "knews: couldn't load font %s.\n", \ font_res.name); \ } DO(body_font); DO(quote_font); DO(header_font); DO(list_font); DO(tree_font); font.head_enc_hack = font_res.head_enc_hack != NULL; font.funcs = get_decode_funcs(font_res.encoding); if (font.funcs && !font.funcs->init) fprintf(stderr, "knews: unknown encoding %s for charset %s\n", font_res.encoding, name); def = font.body_font; if (!def) def = font.quote_font; if (!def && !font.head_enc_hack) def = font.header_font; if (def) { if (!font.body_font) font.body_font = def; if (!font.quote_font) font.quote_font = def; if (!font.header_font) font.header_font = def; } return font_hash_insert(name, &font); } static MimeFont *find_font(char *name) { MimeFont *font; if (!name || *name == '\0') return NULL; ascii_lower(name); font = font_hash_find(name); if (!font) font = load_font(name); return font; } MimeFont *get_font(char *name) { MimeFont *font; font = find_font(name); if (font && (!font->body_font || (font->funcs && !font->funcs->init))) font = NULL; return font; } void font_enter_group(void) { char *charset = res_default_charset(); XFontStruct *font; Arg arg; if (!charset) default_font = ascii_font; else { default_font = find_font(charset); if (!default_font) { fprintf(stderr, "knews: no font for charset %s, " "falling back to us-ascii.\n", charset); res_set_default_charset(NULL); default_font = ascii_font; } } font = default_font->list_font; if (!font) font = ascii_font->list_font; XtSetArg(arg, XtNfont, font); XtSetValues(main_widgets.thread_list, &arg, 1); font = default_font->tree_font; if (!font) font = ascii_font->tree_font; XtSetArg(arg, XtNfont, font); XtSetValues(main_widgets.arttree, &arg, 1); if (!default_font->body_font) default_font = ascii_font; } ./knews-1.0b.1/src/sort.h100644 1244 1244 133 6455455545 13543 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ extern void sort_threads(void); ./knews-1.0b.1/src/p_post.c100644 1244 1244 16452 6455455545 14126 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include "global.h" #include #include #include "child.h" #include "codes.h" #include "connect.h" #include "decode.h" #include "file.h" #include "newsrc.h" #include "p_I.h" #include "p_attach.h" #include "p_popup.h" #include "p_post.h" #include "resource.h" #include "server.h" #include "thread.h" #include "util.h" #include "widgets.h" #include "xutil.h" static int do_post(int fd, char **msg) { char buffer[16834]; char *reply; long n; int status; set_message("Sending posting request...", False); reply = server_comm(main_server, "POST\r\n", True); if (!reply) { *msg = "Connection to server broken while sending\n" " post request. The article was not posted."; return -1; } status = atoi(reply); if (status != NNTP_CONT_POST) { popup_title_notice("Posting failed, message from server is", reply, False); return False; } set_message("Sending article...", False); while ((n = read(fd, buffer, sizeof buffer - 1)) > 0) { buffer[n] = '\0'; if (server_write(main_server, buffer) < 0) { *msg = "Connection to server broken while sending\n" "article. The article may or may not have\n" "been accepted by the server."; return -1; } } if (n < 0) { server_close(main_server); *msg = "Error with temp file. Post status unknown..."; return -1; } set_message("Article posted, waiting for reply...", False); reply = server_read(main_server); if (!reply) { *msg = "Connection to server broken while waiting\n" "for result of posting. The article may have\n" "been posted."; return -1; } if (atoi(reply) == NNTP_OK_POSTED) return True; popup_title_notice("Posting failed, message from server is", reply, False); return False; } int post_article(FILE *fp) { int fd = fileno(fp); char *msg = NULL; int ret; if (lseek(fd, 0, SEEK_SET) < 0) { perror("lseek"); popup_title_notice(NULL, "Error with temp file! Article not posted.", False); return False; } set_busy(True); server_set_quit_func(main_server, nntp_just_close); ret = do_post(fd, &msg); server_set_quit_func(main_server, nntp_quit); if (ret < 0) { popup_title_notice(NULL, msg, False); reconnect_server(True); ret = False; } unset_busy(); return ret; } int post_to_agent(char *agent, FILE *fp) { char stderr_buf[STDERR_BUFFLEN]; char message[256]; pid_t pid, wpid; int status; int fd = fileno(fp); pid = fork_nicely(NULL, NULL, True); if (pid < 0) { popup_title_notice(NULL, "Fork failed!", False); return False; } if (pid == 0) { if (lseek(fd, 0, SEEK_SET) < 0) { perror("knews: lseek"); _exit(127); } if (fd != STDIN_FILENO) { if (dup2(fd, STDIN_FILENO) != STDIN_FILENO) { perror("knews: dup2"); _exit(126); } close(fd); } execl(BIN_SH, "sh", "-c", agent, (char *)0); perror("knews: execl " BIN_SH); _exit(127); } set_busy(False); wpid = wait_for_pid(pid, &status, stderr_buf); unset_busy(); if (wpid != pid) { popup_title_notice(NULL, "wpid != pid, this ain't happening!", True); return False; } message[0] = '\0'; if (WIFEXITED(status)) { status = WEXITSTATUS(status); switch (status) { case 0: break; case 127: strcpy(message, "Failed to start "); strncat(message, agent, 200); break; default: strncat(message, agent, 200); strcat(message, " exited abnormally!"); break; } } else if (WIFSIGNALED(status)) { strncat(message, agent, 200); sprintf(message + strlen(message), " caught %s!", signal_string(WTERMSIG(status))); status = 1; } else { strncat(message, agent, 200); strcat(message, " exited wierdly..."); status = 1; } if (status != 0) { stderr_popup(stderr_buf, 0); popup_title_notice(NULL, message, False); return False; } return True; } /*************************************************************************/ static void generate_mime_boundary(char *boundary) { int n; srand(3ul * rand() + 17ul * time(NULL)); strcpy(boundary, "=-=-=__"); n = strlen(boundary); while (n < 32) boundary[n++] = base64_alpha[(rand() / 13u) % 64u]; strcpy(boundary + n, "__=-=-="); } static const char *skip_header(const char *c) { while (*c != '\0') if (*c == '\n' && !IS_SPACE(*(c+1))) return c + 1; else c++; return c; } static const char *print_header(FILE *fp, const char *c) { /* * rfc1522 encoding of headers would go here (but it sucks). */ while (*c != '\0') { if (*c != '\n') putc(*c, fp); else { fputs("\r\n", fp); if (!IS_SPACE(*(c+1))) return c + 1; } c++; } return c; } static int do_dump(FILE *fp, PostContext *context) { PostAttachment **a = context->attachments; int na = context->n_attachments; const char *art = context->art; const char *ct = NULL; const char *cte = NULL; char buf[80], *boundary = NULL; fputs("Mime-Version: 1.0\r\n", fp); if (context->flags & NEEDS_SENDER) fprintf(fp, "Sender: %s@%s\r\n", global.user_id, global.domain_name); while (*art != '\0' && *art != '\n') { #define IS_HEADER(h) (case_lstrncmp(art, h, sizeof h - 1) == 0) if (IS_HEADER("mime-version:")) { art = skip_header(art); continue; } else if (IS_HEADER("content-type:")) { ct = art; art = skip_header(art); continue; } else if (IS_HEADER("content-transfer-encoding:")) { cte = art; art = skip_header(art); continue; } #undef IS_HEADER art = print_header(fp, art); } if (*art == '\n') art++; if (!a) { if (ct) print_header(fp, ct); else fprintf(fp, "Content-Type: text/plain; charset=%s\r\n", context->has_8bit ? context->charset : "us-ascii"); if (cte) print_header(fp, cte); else if (context->has_8bit) fputs("Content-Transfer-Encoding: 8bit\r\n", fp); fprintf(fp, "\r\n"); } else if (*art != '\0' || na != 1) { generate_mime_boundary(buf); boundary = buf; fprintf(fp, "Content-Transfer-Encoding: 8bit\r\n" "Content-Type: multipart/mixed;\r\n" " boundary=\"%s\"\r\n" "\r\n", boundary); if (*art != '\0') { fprintf(fp, "--%s\r\n", boundary); if (cte) print_header(fp, cte); if (ct) print_header(fp, ct); fputs("\r\n", fp); } } if (*art != '\0') { int bol = True; while (*art != '\0') { if (*art == '\n') putc('\r', fp); else if (bol && *art == '.') putc('.', fp); putc(*art, fp); bol = *art == '\n'; art++; } if (!bol) fputs("\r\n", fp); } if (a) { int i; for (i = 0 ; i < na ; i++) { if (boundary) fprintf(fp, "--%s\r\n", boundary); if (!print_attachment(fp, a[i])) return False; } if (boundary) fprintf(fp, "--%s--\r\n", boundary); } fputs(".\r\n", fp); if (fflush(fp) < 0) { perror("fflush"); return False; } return True; } FILE *dump_art_to_file(PostContext *context) { FILE *fp = NULL; char *tmp = NULL; if (!context->art) return NULL; fp = create_temp_file(&tmp); if (!fp) { popup_title_notice(NULL, "Failed to create temp file!", True); return NULL; } if (tmp) unlink(tmp); if (!do_dump(fp, context)) { fclose(fp); fp = NULL; } return fp; } ./knews-1.0b.1/src/mailcap.h100644 1244 1244 732 6455455545 14167 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ typedef struct { char *view_command; char *compose; char *compose_typed; char *print; char *edit; char *test; char *x11_bitmap; char *description; char needsterminal; char copiousoutput; char textualnewlines; } MailcapData; extern const MailcapData *mailcap_lookup(char*, char*); extern void mailcap_init(void); extern char *expn_tmpl(char*, int, const char*, char**); ./knews-1.0b.1/src/big5.h100644 1244 1244 4507 6455455545 13433 0ustar kallekalle{0xa1, 0x40}, /* " " */ {0xa1, 0x49}, /* "!" */ {0xa1, 0xa8}, /* """ */ {0xa1, 0xad}, /* "#" */ {0xa2, 0x43}, /* "$" */ {0xa2, 0x48}, /* "%" */ {0xa1, 0xae}, /* "&" */ {0xa1, 0xac}, /* "'" */ {0xa1, 0x65}, /* "(" */ {0xa1, 0x66}, /* ")" */ {0xa1, 0xce}, /* "*" */ {0xa1, 0xcf}, /* "+" */ {0xa1, 0x4d}, /* "," */ {0xa1, 0xd0}, /* "-" */ {0xa1, 0x44}, /* "." */ {0xa2, 0x41}, /* "/" */ {0xa2, 0xaf}, /* "0" */ {0xa2, 0xb0}, /* "1" */ {0xa2, 0xb1}, /* "2" */ {0xa2, 0xb2}, /* "3" */ {0xa2, 0xb3}, /* "4" */ {0xa2, 0xb4}, /* "5" */ {0xa2, 0xb5}, /* "6" */ {0xa2, 0xb6}, /* "7" */ {0xa2, 0xb7}, /* "8" */ {0xa2, 0xb8}, /* "9" */ {0xa1, 0x47}, /* ":" */ {0xa1, 0x46}, /* ";" */ {0xa1, 0xd5}, /* "<" */ {0xa1, 0xd7}, /* "=" */ {0xa1, 0xd6}, /* ">" */ {0xa1, 0x48}, /* "?" */ {0xa2, 0x49}, /* "@" */ {0xa2, 0xcf}, /* "A" */ {0xa2, 0xd0}, /* "B" */ {0xa2, 0xd1}, /* "C" */ {0xa2, 0xd2}, /* "D" */ {0xa2, 0xd3}, /* "E" */ {0xa2, 0xd4}, /* "F" */ {0xa2, 0xd5}, /* "G" */ {0xa2, 0xd6}, /* "H" */ {0xa2, 0xd7}, /* "I" */ {0xa2, 0xd8}, /* "J" */ {0xa2, 0xd9}, /* "K" */ {0xa2, 0xda}, /* "L" */ {0xa2, 0xdb}, /* "M" */ {0xa2, 0xdc}, /* "N" */ {0xa2, 0xdd}, /* "O" */ {0xa2, 0xde}, /* "P" */ {0xa2, 0xdf}, /* "Q" */ {0xa2, 0xe0}, /* "R" */ {0xa2, 0xe1}, /* "S" */ {0xa2, 0xe2}, /* "T" */ {0xa2, 0xe3}, /* "U" */ {0xa2, 0xe4}, /* "V" */ {0xa2, 0xe5}, /* "W" */ {0xa2, 0xe6}, /* "X" */ {0xa2, 0xe7}, /* "Y" */ {0xa2, 0xe8}, /* "Z" */ {0xa1, 0x69}, /* "[" */ {0xa2, 0x40}, /* "\" */ {0xa1, 0x6a}, /* "]" */ {0xa1, 0x73}, /* "^" */ {0xa1, 0xc4}, /* "_" */ {0xa1, 0xab}, /* "`" */ {0xa2, 0xe9}, /* "a" */ {0xa2, 0xea}, /* "b" */ {0xa2, 0xeb}, /* "c" */ {0xa2, 0xec}, /* "d" */ {0xa2, 0xed}, /* "e" */ {0xa2, 0xee}, /* "f" */ {0xa2, 0xef}, /* "g" */ {0xa2, 0xf0}, /* "h" */ {0xa2, 0xf1}, /* "i" */ {0xa2, 0xf2}, /* "j" */ {0xa2, 0xf3}, /* "k" */ {0xa2, 0xf4}, /* "l" */ {0xa2, 0xf5}, /* "m" */ {0xa2, 0xf6}, /* "n" */ {0xa2, 0xf7}, /* "o" */ {0xa2, 0xf8}, /* "p" */ {0xa2, 0xf9}, /* "q" */ {0xa2, 0xfa}, /* "r" */ {0xa2, 0xfb}, /* "s" */ {0xa2, 0xfc}, /* "t" */ {0xa2, 0xfd}, /* "u" */ {0xa2, 0xfe}, /* "v" */ {0xa3, 0x40}, /* "w" */ {0xa3, 0x41}, /* "x" */ {0xa3, 0x42}, /* "y" */ {0xa3, 0x43}, /* "z" */ {0xa1, 0x61}, /* "{" */ {0xa1, 0x57}, /* "|" */ {0xa1, 0x62}, /* "}" */ {0xa1, 0xe3}, /* "~" */ ./knews-1.0b.1/src/thread.h100644 1244 1244 1352 6455455545 14047 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #ifndef THREAD_H #define THREAD_H typedef struct THREAD_CONTEXT THREAD_CONTEXT; extern THREAD_CONTEXT *main_thr; extern THREAD_CONTEXT *create_thread_context(void); extern void clear_thread_context(THREAD_CONTEXT*); extern ARTICLE *get_articles(THREAD_CONTEXT*); extern SUBJECT *get_subjects(THREAD_CONTEXT*); extern void set_subjects(THREAD_CONTEXT*, SUBJECT*); extern char *get_refs(THREAD_CONTEXT*); extern ARTICLE *find_article(const char*, long); extern void read_group(char*, int, long); extern ARTICLE *parse_head(long, THREAD_CONTEXT*, char*); extern void fix_author(ARTICLE*, char*, int); extern char *thread_from_file(struct SERVER*, long); #endif /* THREAD_H */ ./knews-1.0b.1/src/tag.c100644 1244 1244 17313 6455455545 13372 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include "global.h" #include "thread.h" #include "util.h" #include "widgets.h" #include "../Widgets/ArtTree.h" static ARTICLE **tagged_articles = NULL; static long no_tagged = 0; static long no_alloc = 0; static void add_tag(ARTICLE *art) { if (no_alloc < no_tagged + 3) { long i = no_alloc; no_alloc = 2 * (no_alloc + 1); tagged_articles = (ARTICLE **)XtRealloc((char *)tagged_articles, no_alloc * sizeof(ARTICLE*)); while (i < no_alloc) tagged_articles[i++] = NULL; } tagged_articles[no_tagged++] = art; art->subject->has_tagged = True; update_subj_entry(art->subject); } static void remove_tag(ARTICLE *art) { long i, n = no_tagged; int found_subj = False; for (i = 0 ; i < n ; i++) { if (tagged_articles[i] == art) { while (i < n) { tagged_articles[i] = tagged_articles[i + 1]; if (tagged_articles[i] && tagged_articles[i]->subject == art->subject) found_subj = True; i++; } no_tagged--; art->subject->has_tagged = found_subj; if (!found_subj) update_subj_entry(art->subject); return; } if (tagged_articles[i]->subject == art->subject) found_subj = True; } } static int is_tagged(ARTICLE *art) { ARTICLE **tagged = tagged_articles; long n = no_tagged; if (art->subject->has_tagged) while (n > 0) { if (*tagged == art) return True; tagged++; n--; } return False; } /*************************************************************************/ void mark_tagged_articles(ARTICLE *thread) { long i, n = no_tagged; for (i = 0 ; i < n ; i++) if (tagged_articles[i]->subject->thread == thread) ArtTreeNodeSetOuter(main_widgets.arttree, (ART_TREE_NODE *)tagged_articles[i], True); } void clear_tagged_articles(void) { SUBJECT *subj; ARTICLE *thread; long i; switch (global.mode) { case NewsModeThread: /* clear the tree */ if (global.curr_subj) thread = global.curr_subj->thread; else if (global.curr_art) thread = global.curr_art->subject->thread; else thread = NULL; for (i = 0 ; i < no_tagged ; i++) if (tagged_articles[i]->subject->thread == thread) ArtTreeNodeSetOuter(main_widgets.arttree, (ART_TREE_NODE *)tagged_articles[i], False); /* fall through */ case NewsModeGroup: /* clear the list */ for (subj = get_subjects(main_thr) ; subj ; subj = subj->next) { if (subj->has_tagged) { subj->has_tagged = False; update_subj_entry(subj); } } break; default: break; } XtFree((char *)tagged_articles); tagged_articles = NULL; no_tagged = 0; no_alloc = 0; } void tag_hot_articles(void) { ARTICLE *art; for (art = get_articles(main_thr) ; art ; art = art->next) if (art->from && !art->read && art->pixmap != None) add_tag(art); } void untag_article(ARTICLE *art) { remove_tag(art); } ARTICLE **get_tagged_articles(void) { return tagged_articles; } long no_tagged_articles(void) { return no_tagged; } void arttree_tag_callback(Widget gw, XtPointer client_data, XtPointer call_data) { ARTICLE *art = (ARTICLE *)call_data; if (!art) XBell(display, 0); else if (!art->from) set_message("Can't tag a fake article!", True); else { int tagged = ArtTreeNodeGetOuter(main_widgets.arttree, (ART_TREE_NODE *)art); if (tagged) add_tag(art); else remove_tag(art); if (no_tagged == 1) set_message("1 tagged article.", False); else { char buffer[80]; sprintf(buffer, "%ld tagged articles.", no_tagged); set_message(buffer, False); } } } void action_tag_subject(Widget w, XEvent *event, String *params, Cardinal *no_params) { ARTICLE *art; int only_unread = True; char message[80]; if (global.busy || (global.mode != NewsModeGroup && global.mode != NewsModeThread)) return; if (!global.curr_subj) { set_message("No selected subject!", True); return; } if (*no_params > 0) only_unread = False; for (art = global.curr_subj->thread ; art ; art = next_in_thread_preorder(art)) { if (art->from && !(only_unread && art->read) && !is_tagged(art) && art->subject == global.curr_subj) { add_tag(art); if (global.mode == NewsModeThread) ArtTreeNodeSetOuter(main_widgets.arttree, (ART_TREE_NODE *)art, True); } } update_subj_entry(global.curr_subj); sprintf(message, "%ld tagged articles", no_tagged); set_message(message, False); } void action_untag_subject(Widget w, XEvent *event, String *params, Cardinal *no_params) { ARTICLE *art; char message[80]; if (global.busy || (global.mode != NewsModeGroup && global.mode != NewsModeThread)) return; if (!global.curr_subj) { set_message("No selected subject!", True); return; } for (art = global.curr_subj->thread ; art ; art = next_in_thread_preorder(art)) { if (art->from && art->subject == global.curr_subj) { remove_tag(art); if (global.mode == NewsModeThread) ArtTreeNodeSetOuter(main_widgets.arttree, (ART_TREE_NODE *)art, False); } } update_subj_entry(global.curr_subj); sprintf(message, "%ld tagged articles", no_tagged); set_message(message, False); } void action_tag_thread(Widget w, XEvent *event, String *params, Cardinal *no_params) { ARTICLE *art; SUBJECT *subj; int only_unread = True; char message[80]; if (global.busy || (global.mode != NewsModeGroup && global.mode != NewsModeThread)) return; if (!global.curr_subj) { set_message("No selected subject!", True); return; } if (*no_params > 0) only_unread = False; for (art = global.curr_subj->thread ; art ; art = next_in_thread_preorder(art)) { if (art->from && !(only_unread && art->read) && !is_tagged(art)) { add_tag(art); if (global.mode == NewsModeThread) ArtTreeNodeSetOuter(main_widgets.arttree, (ART_TREE_NODE *)art, True); } } subj = global.curr_subj; art = subj->thread; while (subj->prev && subj->prev->thread == art) subj = subj->prev; while (subj && subj->thread == art) { update_subj_entry(subj); subj = subj->next; } sprintf(message, "%ld tagged articles", no_tagged); set_message(message, False); } void action_untag_thread(Widget w, XEvent *event, String *params, Cardinal *no_params) { ARTICLE *art; SUBJECT *subj = global.curr_subj; char message[80]; if (global.busy || (global.mode != NewsModeGroup && global.mode != NewsModeThread)) return; if (!subj) { set_message("No selected subject!", True); return; } for (art = subj->thread ; art ; art = next_in_thread_preorder(art)) { if (art->from) { remove_tag(art); if (global.mode == NewsModeThread) ArtTreeNodeSetOuter(main_widgets.arttree, (ART_TREE_NODE *)art, False); } } art = subj->thread; while (subj->prev && subj->prev->thread == art) subj = subj->prev; while (subj && subj->thread == art) { update_subj_entry(subj); subj = subj->next; } sprintf(message, "%ld tagged articles", no_tagged); set_message(message, False); } /*************************************************************************/ #define HIST_SIZE 32 static ARTICLE *history[HIST_SIZE]; static unsigned int h_end = 0; static unsigned int h_size = 0; void clear_history(void) { h_end = h_size = 0; } ARTICLE *history_pop(void) { if (h_size == 0) return NULL; h_size--; h_end--; h_end %= HIST_SIZE; return history[h_end]; } ARTICLE *history_peek(void) { if (h_size == 0) return NULL; return history[(h_end - 1) % HIST_SIZE]; } void history_push(ARTICLE *art) { if (h_size < HIST_SIZE) h_size++; history[h_end++] = art; h_end %= HIST_SIZE; } ./knews-1.0b.1/src/util.c100644 1244 1244 33047 6570012504 13554 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include "global.h" #include "util.h" #include "resource.h" #include "thread.h" ARTICLE *next_in_thread_preorder(ARTICLE *art) { if (A_CHILD1(art)) return A_CHILD1(art); while (!A_SIBLING(art)) if (!(art = A_PARENT(art))) return NULL; return A_SIBLING(art); } ARTICLE *next_in_subthread_preorder(ARTICLE *art, ARTICLE *thread) { if (A_CHILD1(art)) return A_CHILD1(art); if (art == thread) return NULL; while (!A_SIBLING(art)) { if (!(art = A_PARENT(art))) return NULL; if (art == thread) return NULL; } return A_SIBLING(art); } ARTICLE *preorder_skip_subthread(ARTICLE *art) { while (!A_SIBLING(art)) if (!(art = A_PARENT(art))) return NULL; return A_SIBLING(art); } ARTICLE *next_in_thread_wrap(ARTICLE *art) { int height = 0; int flag = FALSE; do { while (!A_SIBLING(art) && A_PARENT(art)) { art = A_PARENT(art); height++; } if (A_SIBLING(art)) art = A_SIBLING(art); else { if (flag) return NULL; art = art->subject->thread; flag = True; height++; } while (height > 0 && A_CHILD1(art)) { art = A_CHILD1(art); height--; } } while (height > 0); return art; } ARTICLE *next_in_subthread_wrap(ARTICLE *art, ARTICLE *thread) { int height = 0; int flag = FALSE; if (art == thread) return A_CHILD1(thread); do { while (!A_SIBLING(art) && A_PARENT(art) != thread) { art = A_PARENT(art); height++; } if (A_SIBLING(art)) return A_SIBLING(art); else { if (flag) return NULL; flag = True; if (thread) art = A_CHILD1(thread); else art = art->subject->thread; height++; } while (height > 0 && A_CHILD1(art)) { art = A_CHILD1(art); height--; } } while (height > 0); return art; } ARTICLE *next_in_thread_dont_wrap(ARTICLE *art) { int height = 0; do { while (!A_SIBLING(art) && A_PARENT(art)) { art = A_PARENT(art); height++; } if (A_SIBLING(art)) art = A_SIBLING(art); else return NULL; while (height > 0 && A_CHILD1(art)) { art = A_CHILD1(art); height--; } } while (height > 0); return art; } static ARTICLE *prev_sibling(ARTICLE *art) { ARTICLE *result = A_PARENT(art); if (result) result = A_CHILD1(result); else result = art->subject->thread; while (result && A_SIBLING(result) != art) result = A_SIBLING(result); return result; } ARTICLE *prev_in_thread_dont_wrap(ARTICLE *art) { int height = 0; ARTICLE *prev; do { while (!(prev = prev_sibling(art)) && A_PARENT(art)) { art = A_PARENT(art); height++; } if (prev) art = prev; else return NULL; while (height > 0 && A_CHILD1(art)) { art = A_CHILD1(art); while (A_SIBLING(art)) art = A_SIBLING(art); height--; } } while (height > 0); return art; } void update_subj_hot_value(SUBJECT *subj) { ARTICLE *art; subj->pixmap = None; for (art = subj->thread ; art ; art = next_in_thread_preorder(art)) { if (art->subject == subj && art->from && !art->read && art->pixmap != None) { subj->pixmap = art->pixmap; break; } } } long mark_subject_unread(SUBJECT *subj) { ARTICLE *art = subj->thread; long n = 0; while (art) { if (art->subject == subj && art->from && art->read) { art->read = FALSE; if (art->pixmap != None) global.n_hot++; art->subject->no_unread++; n++; } art = next_in_thread_preorder(art); } global.curr_group->no_unread += n; return n; } long mark_sub_subject_unread(ARTICLE *thread, SUBJECT *subj) { ARTICLE *art = thread; long n = 0; while (art) { if (art->subject == subj && art->from && art->read) { art->read = FALSE; if (art->pixmap != None) global.n_hot++; subj->no_unread++; n++; } art = next_in_subthread_preorder(art, thread); } global.curr_group->no_unread += n; return n; } long mark_subject_read(SUBJECT *subj, int xref, int kill) { ARTICLE *art = subj->thread; long n = 0; while (art) { if (art->subject == subj && art->from && !art->read && (art->pixmap == None || !kill)) { art->read = TRUE; if (xref) process_xref(art); if (kill) art->killed = TRUE; if (art->pixmap != None) global.n_hot--; art->subject->no_unread--; n++; } art = next_in_thread_preorder(art); } global.curr_group->no_unread -= n; return n; } long mark_sub_subject_read(ARTICLE *thread, SUBJECT *subj, int xref, int kill) { ARTICLE *art = thread; long n = 0; while (art) { if (art->subject == subj && art->from && !art->read && (art->pixmap == None || !kill)) { art->read = TRUE; if (xref) process_xref(art); if (kill) art->killed = TRUE; if (art->pixmap != None) global.n_hot--; subj->no_unread--; n++; } art = next_in_subthread_preorder(art, thread); } global.curr_group->no_unread -= n; return n; } long mark_thread_read(ARTICLE *art, int xref, int kill) { long n = 0; while (art) { if (art->from && !art->read && (art->pixmap == None || !kill)) { art->read = TRUE; if (xref) process_xref(art); if (kill) art->killed = TRUE; if (art->pixmap != None) global.n_hot--; art->subject->no_unread--; n++; } art = next_in_thread_preorder(art); } global.curr_group->no_unread -= n; return n; } long mark_subthread_read(ARTICLE *art, int xref, int kill) { ARTICLE *thread = art; long n = 0; while (art) { if (art->from && !art->read && (art->pixmap == None || !kill)) { art->read = TRUE; if (xref) process_xref(art); if (kill) art->killed = TRUE; if (art->pixmap != None) global.n_hot--; art->subject->no_unread--; n++; } art = next_in_subthread_preorder(art, thread); } global.curr_group->no_unread -= n; return n; } long mark_subject_hot(SUBJECT *subj, Pixmap pixmap) { ARTICLE *art = subj->thread; long n = 0; while (art) { if (art->from && art->subject == subj && art->pixmap == None) { art->pixmap = pixmap; if (!art->read) n++; } art = next_in_thread_preorder(art); } return n; } long mark_thread_hot(ARTICLE *art, Pixmap pixmap) { long n = 0; while (art) { if (art->from && art->pixmap == None) { art->pixmap = pixmap; if (!art->read) n++; } art = next_in_thread_preorder(art); } return n; } long mark_subthread_hot(ARTICLE *art, Pixmap pixmap) { ARTICLE *thread = art; long n = 0; while (art) { if (art->from && art->pixmap == None) { art->pixmap = pixmap; if (!art->read) n++; } art = next_in_subthread_preorder(art, thread); } return n; } static void add_to_read_arts(GROUP *group, long no) { ART_LIST_NODE *loop, *prev; for (prev = NULL, loop = group->read_arts ; loop ; prev = loop, loop = loop->next) if (no <= loop->last) break; if (loop && no >= loop->first) return; if (loop && no == loop->first - 1) { loop->first--; if (prev && prev->last + 1 == loop->first) { prev->last = loop->last; prev->next = loop->next; XtFree((char *)loop); } } else if (prev && no == prev->last + 1) { prev->last++; /* no need to check for merging */ } else { ART_LIST_NODE *temp; temp = (ART_LIST_NODE *)XtMalloc(sizeof *temp); temp->first = temp->last = no; temp->next = loop; if (prev) prev->next = temp; else group->read_arts = temp; } } void process_xref(ARTICLE *art) { char *xref; char *c1, *c2; long i, art_no; c1 = xref = art->xref; art->xref = NULL; while (c1 && *c1 == ' ') c1++; while (c1 && (c2 = strchr(c1, ':')) ) { *(c2++) = '\0'; for (i = 0 ; i < global.no_groups ; i++) { if (!global.groups[i]->subscribed) { if ( (c1 = strchr(c2, ' ')) ) c1++; break; } if (case_strcmp(global.groups[i]->name, c1) == 0 && global.groups[i] != global.curr_group) { if ( (c1 = strchr(c2, ' ')) ) *(c1++) = '\0'; art_no = atol(c2); add_to_read_arts(global.groups[i], art_no); break; } } while (c1 && *c1 == ' ') c1++; } XtFree(xref); } void fake_xref(ARTICLE *art, char **headers, int n) { if (art->xref) return; while (n-- > 0) if (case_strncmp(*headers, "Xref:", 5) != 0) headers++; else { char *c = *headers + 5; while (*c == ' ' || *c == '\t') c++; c = strchr(c, ' '); while (*c == ' ' || *c == '\t') c++; if (*c != '\0') art->xref = XtNewString(c); break; } } void free_read_arts_list(GROUP *group) { ART_LIST_NODE *next, *loop = group->read_arts; group->read_arts = NULL; while (loop) { next = loop->next; XtFree((char *)loop); loop = next; } } void calc_no_unread(GROUP *group) { ART_LIST_NODE *loop; if (group->first_art == 0 && group->last_art == 0) { group->no_unread = 0; return; } group->no_unread = group->last_art - group->first_art + 1; for (loop = group->read_arts ; loop ; loop = loop->next) { if (loop->last > group->last_art) { if (loop->first <= group->last_art) group->no_unread -= (group->last_art - loop->first + 1); return; } else if (loop->first < group->first_art) { if (loop->last >= group->first_art) group->no_unread -= (loop->last - group->first_art +1); } else { group->no_unread -= (loop->last - loop->first + 1); } } } void list_all_arts_read(GROUP *g) { long first = g->first_art; long last = g->last_art; free_read_arts_list(g); if (last >= first && last > 0) { ART_LIST_NODE *temp; temp = g->read_arts = (ART_LIST_NODE *)XtMalloc(sizeof(ART_LIST_NODE)); temp->first = 1; temp->last = last; temp->next = NULL; } } static ART_LIST_NODE *get_read_arts_list(ARTICLE *art) { ART_LIST_NODE *temp; while (art->next && !art->next->read && art->next->no == art->no + 1) art = art->next; if (!art->next) return NULL; temp = (ART_LIST_NODE *)XtMalloc(sizeof *temp); temp->first = art->no + 1; if (art->next->read) { while (art->next && art->next->read) art = art->next; if (art->next) { temp->last = art->next->no - 1; temp->next = get_read_arts_list(art->next); } else { temp->last = global.curr_group->last_art; temp->next = NULL; } } else { temp->last = art->next->no - 1; temp->next = get_read_arts_list(art->next); } return temp; } ART_LIST_NODE *create_read_arts_list(void) { ARTICLE *art, *arts = get_articles(main_thr); ART_LIST_NODE *list, *last; art = arts; while (art && !art->read) art = art->next; if (!art) { if (!res_ask_how_many() || !arts || arts->no <= 1) return NULL; list = (ART_LIST_NODE *)XtMalloc(sizeof *list); list->first = 1; list->last = arts->no - 1; list->next = NULL; return list; } art = arts; if (art->no == 1 && !art->read) list = get_read_arts_list(art); else { list = (ART_LIST_NODE *)XtMalloc(sizeof *list); list->first = 1; while (art && art->read) art = art->next; if (art) { list->last = art->no - 1; list->next = get_read_arts_list(art); } else { list->last = global.curr_group->last_art; list->next = NULL; } } if (!list || !arts) return NULL; /* * Hack in case last articles(s) cancelled... */ last = list; while (last->next) last = last->next; art = arts; while (art->next) art = art->next; if (art->no < global.curr_group->last_art) if (last->last == art->no) last->last = global.curr_group->last_art; else if (last->last < art->no) { last->next = (ART_LIST_NODE *)XtMalloc(sizeof *last); last = last->next; last->first = art->no + 1; last->last = global.curr_group->last_art; last->next = NULL; } return list; } ARTICLE *first_unread_article_with_subject(SUBJECT *subj) { ARTICLE *art, *first = NULL; for (art = subj->thread ; art ; art = next_in_thread_preorder(art)) if (art->tree_data.label && art->subject == subj) if (!art->read) return art; else if (!first) first = art; return first; } /*********************************************************************/ void ascii_nlower(char *c, long n) { while (n-- > 0) { if (IS_UPPER(*c)) *c = LOWER(*c); c++; } } void ascii_lower(char *c) { while (*c != '\0') { if (IS_UPPER(*c)) *c = LOWER(*c); c++; } } void memcpy_lower(char *dest, char *src, long n) { while (n-- > 0) { *dest++ = TO_LOWER(*src); src++; } } int case_strncmp(const char *c1, const char *c2, long n) { while (n--) { int tmp = TO_LOWER(*c1) - TO_LOWER(*c2); if (tmp != 0) return tmp; if (*c1 == '\0') return 0; c1++; c2++; } return 0; } int case_strcmp(const char *c1, const char *c2) { for (;;) { int tmp = TO_LOWER(*c1) - TO_LOWER(*c2); if (tmp != 0) return tmp; if (*c1 == '\0') return 0; c1++; c2++; } } int case_lstrncmp(const char *c1, const char *c2, long n) { while (n--) { int tmp = TO_LOWER(*c1) - (unsigned char)*c2; if (tmp != 0) return tmp; if (*c1 == '\0') return 0; c1++; c2++; } return 0; } int case_lstrcmp(const char *c1, const char *c2) { for (;;) { int tmp = TO_LOWER(*c1) - (unsigned char)*c2; if (tmp != 0) return tmp; if (*c1 == '\0') return 0; c1++; c2++; } } int case_lhassub(const char *haystack, const char *needle) { int ch = *needle++; int n = strlen(needle); while (*haystack != '\0') if (TO_LOWER(*haystack) == ch && case_lstrncmp(haystack + 1, needle, n) == 0) return True; else haystack++; return False; } ./knews-1.0b.1/src/bg.h100644 1244 1244 440 6455455545 13145 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ typedef int (*BgProc)(struct SERVER*); extern void bg_nudge(BgProc); extern void bg_shutdown(void); extern GROUP *bg_in_group(long*, long*, long*); extern void bg_start_group(GROUP*); extern void close_bg_server(void); ./knews-1.0b.1/src/ahead.c100644 1244 1244 34770 6613405410 13645 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include "global.h" #include #include "ahead.h" #include "bg.h" #include "codes.h" #include "expand.h" #include "file.h" #include "newsrc.h" #include "parse.h" #include "resource.h" #include "server.h" #include "thread.h" #include "util.h" #include "widgets.h" #include "xutil.h" #include "../Widgets/ScrList.h" typedef enum { AheadStateNone, AheadStateSentGroup, AheadStateSentXover, AheadStateDoingXover, AheadStateSentHead, AheadStateDoingHead, AheadStateSentNext } AheadState; enum { AheadFlagNoop, AheadFlagScheduled, AheadFlagThreading, AheadFlagDone }; typedef struct GROUP_AHEAD_NODE GROUP_AHEAD_NODE; struct GROUP_AHEAD_NODE { GROUP_AHEAD_NODE *next; GROUP *group; char *file_name; unsigned char flag; unsigned char needs_update; }; static GROUP_AHEAD_NODE *schedule = NULL, *current = NULL; static AheadState ahead_state = AheadStateNone; static THREAD_CONTEXT *bogus_context = NULL; static long n_to_put = 0; static long n_pending = 0; static long total = 1; static long n_done = 0; static long hit_end; static long curr_art_no = -1; static FILE *fp = NULL; static void close_fp(void) { if (fp) fclose(fp); fp = NULL; } static void print_refs(FILE *fp, ARTICLE *art) { if (!art) return; print_refs(fp, A_PARENT(art)); fprintf(fp, "<%s> ", art->msgid); } static void print_xover_line(FILE *fp, ARTICLE *art, char *refs) { char time_buf[32]; fprintf(fp, "%ld\t%s\t%s\t%s\t<%s>\t", art->no, art->subject->subject, art->from, time_t_to_date(art->date, time_buf), art->msgid); if (refs) fputs(refs, fp); else print_refs(fp, A_PARENT(art)); fprintf(fp, "\t\t%hu", art->lines); if (art->xref) fprintf(fp, "\tXref: %s\r\n", art->xref); else fputs("\r\n", fp); } static char *get_tmp_filename(char *group_name) { char *cache_dir = res_cache_dir(); char *ret; ret = XtMalloc(strlen(cache_dir) + strlen(group_name) + 2); sprintf(ret, "%s/%s", cache_dir, group_name); return ret; } static void clear_bogus_context(int create) { if (bogus_context) clear_thread_context(bogus_context); else if (create) bogus_context = create_thread_context(); } static GROUP_AHEAD_NODE *create_ahead_node(GROUP *group, int flag) { GROUP_AHEAD_NODE *temp, *last = schedule; temp = (GROUP_AHEAD_NODE *)XtMalloc(sizeof *temp); temp->next = NULL; temp->group = group; temp->file_name = get_tmp_filename(group->name); temp->flag = flag; temp->needs_update = False; group->ahead_flag = True; if (!last) schedule = temp; else { while (last->next) last = last->next; last->next = temp; } return temp; } static void remove_ahead_node(GROUP_AHEAD_NODE *node, int do_unlink) { GROUP *group = node->group; if (schedule) if (node == schedule) schedule = schedule->next; else { GROUP_AHEAD_NODE *prev = schedule; while (prev->next && prev->next != node) prev = prev->next; if (prev->next) prev->next = prev->next->next; } if (node->file_name) { if (do_unlink) unlink_expand(node->file_name); XtFree(node->file_name); } node->next = NULL; if (node->group) node->group->ahead_flag = False; node->group = NULL; node->file_name = NULL; XtFree((char *)node); update_group_entry(group); } static void remove_current_node(void) { clear_bogus_context(False); if (current) remove_ahead_node(current, True); current = NULL; close_fp(); } static GROUP_AHEAD_NODE *get_scheduled_node(void) { GROUP_AHEAD_NODE *node; for (node = schedule ; node ; node = node->next) if (node->flag == AheadFlagScheduled) return node; return NULL; } static GROUP_AHEAD_NODE *find_ahead_node(GROUP *group) { GROUP_AHEAD_NODE *node; for (node = schedule ; node ; node = node->next) if (node->group == group) return node; return NULL; } #define HEAD_NEXT 12 #define MAX_HEAD_NEXT 16 static int put_head_next(SERVER *server) { char buffer[MAX_HEAD_NEXT * HEAD_NEXT + 1]; char *c; long i, n = n_to_put; int busy; if (hit_end || n_pending >= MAX_HEAD_NEXT) return True; if (n == 0) n = 1; else { if (n > MAX_HEAD_NEXT) n = MAX_HEAD_NEXT; n_to_put -= n; } n_pending += n; buffer[0] = '\0'; for (i = 0, c = buffer ; i < n ; i++, c += HEAD_NEXT) strcpy(c, "HEAD\r\nNEXT\r\n"); busy = global.busy; if (!busy) global.busy = True; i = server_write(server, buffer); if (!busy) global.busy = False; return i == 0; } static int thread_ahead_start_head(SERVER *server) { clear_bogus_context(True); n_pending = 0; n_to_put = total; hit_end = False; return put_head_next(server); } static int start_xover(SERVER *server) { long first, last; GROUP *group; GROUP_AHEAD_NODE *node; ahead_state = AheadStateNone; group = bg_in_group(&total, &first, &last); if (!group) return False; node = find_ahead_node(group); if (!node || node->flag != AheadFlagThreading) return False; if (total <= 0) total = 1; if (global.xover_supported) { char command[512]; int tmp, busy = global.busy; sprintf(command, "XOVER %ld-%ld\r\n", first, last); if (!busy) global.busy = True; tmp = server_write(server, command); if (!busy) global.busy = False; if (tmp == 0) { ahead_state = AheadStateSentXover; return True; } current->flag = AheadFlagScheduled; update_group_entry(current->group); ahead_state = AheadStateNone; return False; } ahead_state = AheadStateSentHead; thread_ahead_start_head(server); /* check return? */ return True; } static int start_group(SERVER *server) { char message[512]; GROUP_AHEAD_NODE *node; GROUP *group; n_to_put = 0; n_pending = 0; n_done = 0; ahead_state = AheadStateNone; while ((node = get_scheduled_node())) { fp = fopen_expand(node->file_name, "w", True); if (fp) break; } if (!node) return False; current = node; node->flag = AheadFlagThreading; update_group_entry(node->group); group = bg_in_group(NULL, NULL, NULL); if (total <= 0) total = 1; if (group && group == node->group) return start_xover(server); bg_start_group(node->group); ahead_state = AheadStateSentGroup; sprintf(message, "Threading %s...", node->group->name); set_message(message, False); return True; } static int thread_ahead_proc(SERVER *server) { char *buffer = NULL; long status; int tmp; ARTICLE *art; if (!server) { remove_current_node(); ahead_state = AheadStateNone; return False; } do { switch (ahead_state) { case AheadStateNone: return start_group(server); case AheadStateSentGroup: return start_xover(server); case AheadStateSentXover: buffer = server_get_line(server); if (!buffer) break; if (atoi(buffer) == NNTP_OK_XOVER) { ahead_state = AheadStateDoingXover; break; } /* fall back to head */ thread_ahead_start_head(server); /* check return? */ return True; case AheadStateDoingXover: buffer = server_get_line(server); if (!buffer) break; n_done++; if (fp) fprintf(fp, "%s\r\n", buffer); if (IS_DOT(buffer)) { current->flag = AheadFlagDone; update_group_entry(current->group); current = NULL; close_fp(); ahead_state = AheadStateNone; return False; } break; case AheadStateSentHead: buffer = server_get_line(server); if (!buffer) break; tmp = sscanf(buffer, "%ld%ld", &status, &curr_art_no); if (tmp <= 0 || status != NNTP_OK_HEAD) ahead_state = AheadStateSentNext; else { ahead_state = AheadStateDoingHead; if (tmp == 1 || hit_end) curr_art_no = -1; } if (!hit_end) { n_done++; if (n_pending < MAX_HEAD_NEXT && !put_head_next(server)) { ahead_state = AheadStateNone; return False; } } break; case AheadStateDoingHead: buffer = server_get_chunk(server); if (!buffer) break; if (curr_art_no > 0) { art = parse_head(curr_art_no, bogus_context, buffer); if (art && fp) print_xover_line(fp, art, get_refs(bogus_context)); } curr_art_no = -1; ahead_state = AheadStateSentNext; break; case AheadStateSentNext: buffer = server_get_line(server); if (!buffer) break; n_pending--; if (!hit_end) { status = atoi(buffer); if (status != NNTP_OK_NOTEXT) /* maybe better checking */ hit_end = True; } if (hit_end && n_pending <= 0) { current->flag = AheadFlagDone; update_group_entry(current->group); current = NULL; close_fp(); ahead_state = AheadStateNone; return False; } ahead_state = AheadStateSentHead; break; } } while (buffer); return True; } void action_schedule_thread_ahead(Widget w, XEvent *event, String *params, Cardinal *no_params) { GROUP *group = NULL; GROUP_AHEAD_NODE *node; long n, i; switch (global.mode) { case NewsModeConnected: if (w != main_widgets.group_list) group = global.curr_group; else { n = ScrListEventToIndex(w, event); if (n < 0) break; for (i = 0 ; i < global.no_groups ; i++) if (global.groups[i]->disp == n) { group = global.groups[i]; break; } } break; case NewsModeAllgroups: if (w != main_widgets.group_list) group = global.curr_group; else { n = ScrListEventToIndex(w, event); if (n >= 0 && n < global.no_groups) group = global.groups[n]; } break; case NewsModeSomegroups: n = ScrListEventToIndex(w, event); if (n < 0) break; for (i = 0 ; i < global.no_groups ; i++) if (global.groups[i]->disp == n) { group = global.groups[i]; break; } break; case NewsModeDisconnected: case NewsModeGroup: case NewsModeThread: case NewsModeNewgroups: if (global.bell) XBell(display, 0); return; } if (!group) { if (global.bell) XBell(display, 0); return; } node = find_ahead_node(group); if (node) { char message[128]; switch (node->flag) { case AheadFlagNoop: break; case AheadFlagDone: set_message("That group has already been threaded!", True); return; case AheadFlagScheduled: set_message("That group is already scheduled for " "thread ahead.", True); return; case AheadFlagThreading: sprintf(message, "That group is beeing threaded, %ld%% done!", 100 * n_done / total); set_message(message, False); return; } } if (!node) create_ahead_node(group, AheadFlagScheduled); update_group_entry(group); bg_nudge(thread_ahead_proc); } char thread_ahead_char(GROUP *group) { GROUP_AHEAD_NODE *node = find_ahead_node(group); if (node) switch (node->flag) { case AheadFlagNoop: return ' '; case AheadFlagScheduled: return '-'; case AheadFlagThreading: return '*'; case AheadFlagDone: return '+'; } return ' '; } void thread_ahead_shutdown(void) { int save = res_save_thread_info(); n_pending = 0; n_to_put = 0; curr_art_no = -1; clear_bogus_context(False); close_fp(); while (schedule) remove_ahead_node(schedule, !save); } int thread_ahead_check(GROUP *group) { GROUP_AHEAD_NODE *node = find_ahead_node(group); char message[128]; int threaded = False; SERVER *server; int fd; if (node) switch (node->flag) { case AheadFlagNoop: break; case AheadFlagThreading: sprintf(message, "Thread ahead in progress %ld%% done, try later.", 100 * n_done / total); set_message(message, False); return -1; case AheadFlagScheduled: node->flag = AheadFlagNoop; update_group_entry(group); break; case AheadFlagDone: fd = open_expand(node->file_name, O_RDONLY, True); if (fd < 0) break; set_message("Threading from file...", False); set_busy(False); server = server_create(fd); thread_from_file(server, group->first_art); server_free(server); unset_busy(); threaded = True; break; } return threaded; } void thread_ahead_leave_group(GROUP *group) { GROUP_AHEAD_NODE *node = find_ahead_node(group); int keep = res_keep_thread_info(group->subscribed); int save = group->subscribed && res_save_thread_info(); if (!save && !keep) { if (node) remove_ahead_node(node, True); } else { if (!node) node = create_ahead_node(group, AheadFlagNoop); if (node->flag == AheadFlagNoop || node->flag == AheadFlagScheduled || (node->flag == AheadFlagDone && save && node->needs_update)) { FILE *fp; fp = fopen_expand(node->file_name, "w", True); if (!fp) fputs("knews: couldn't create temp file " "for thread data.\n", stderr); else { ARTICLE *art; for (art = get_articles(main_thr) ; art ; art = art->next) if (art->no >= group->first_art) print_xover_line(fp, art, NULL); fclose(fp); node->flag = AheadFlagDone; node->needs_update = False; } } } clear_thread_context(main_thr); global.curr_group = group; } static int scan_group(GROUP *group, int scan) { struct stat stat_buf; GROUP_AHEAD_NODE *node; char *path; int tmp; node = create_ahead_node(group, AheadFlagScheduled); if (!scan || !(path = expand_path(node->file_name))) return True; tmp = stat(path, &stat_buf); if (tmp < 0 && errno != ENOENT) perror(path); XtFree(path); if (tmp < 0) return True; node->flag = AheadFlagDone; if (scan) node->needs_update = True; return False; } void thread_ahead_init(void) { char **groups = res_thread_ahead_groups(); int scan = res_save_thread_info(); int doit = False; if ((!groups || !*groups) && !scan) return; if (scan || case_lstrcmp(*groups, "all") == 0) { int all = scan || **groups == 'A'; int n; for (n = 0 ; n < global.no_groups ; n++) { if (!global.groups[n]->subscribed) break; if (!all && global.groups[n]->no_unread <= 0) continue; doit |= scan_group(global.groups[n], scan); update_group_entry(global.groups[n]); if (scan) XFlush(display); } } else { while (*groups) { GROUP *group = find_group(*groups); if (!group) fprintf(stderr, "thread_ahead: couldn't find group %s\n", *groups); else { doit |= scan_group(group, False); update_group_entry(group); if (scan) XFlush(display); } groups++; } } if (doit) bg_nudge(thread_ahead_proc); } int thread_ahead_todo(void) { return get_scheduled_node() != NULL; } ./knews-1.0b.1/src/resource.c100644 1244 1244 65365 6455455545 14460 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include "global.h" #include #include #include #include "file.h" #include "resource.h" #include "util.h" #include "widgets.h" #include "xutil.h" #include "../Widgets/Compat.h" typedef enum { KeepTypeNo = False, KeepTypeYes = True, KeepTypeSubscribed } KeepType; #define DEFAULT_QUOTE_REGEXP "^[ \t]*[:>|]" #define DEFAULT_ATTRIBUTION "In article %m,\n\t%f writes:"; #define DEFAULT_DB_SIZE 4096 static const char *bad_regex_msg = "knews: regcomp() couldn't compile the default quote regexp:\n" " \"" DEFAULT_QUOTE_REGEXP "\"\n" " Chances are your system's regex implementaton sucks\n" " You might want to recompile knews with HAVE_POSIX_REGEXPS\n" " set to 0. See the file configure.h for details.\n"; static XrmDatabase db = 0; static char *pw_name = NULL; static char *default_header_format[] = { "Subject", "Newsgroups", "Followup-To", "Reply-To:", "Content-Description", "Date", "Organization", "From", NULL, }; static regex_t default_quote_re; static regex_t quote_re; struct per_server{ struct { char *newsrc_file; char *old_newsrc_file; char *kill_file; char *remote_newsrc_file; char *remote_kill_file; char *auth_info_user; char *auth_info_pass; char *rescan_timeout; char *retrieve_descriptions; char *check_for_new_groups; char *read_active_file; char *thread_ahead_groups; char *ask_how_many; char *posting_agent; char *fill_newsrc_file; char *try_list_active; char *cache_dir; char *descriptions_file; char *save_thread_info; char *group_name_columns; } str; struct { char **thread_ahead_groups; long rescan_timeout; int group_name_columns; char retrieve_descriptions; char check_for_new_groups; char read_active_file; char ask_how_many; char fill_newsrc_file; char try_list_active; char save_thread_info; } conv; }; static struct per_server per_server, per_server_default = {{0, }, }; struct per_group { struct { char *header_format; char *quote_regexp; char *quote_string; char *quote_quote_string; char *attribution; char *full_name; char *signature_file; char *organization; char *reply_to; char *extra_headers; char *followup_headers; char *posted_and_mailed; char *uu_dir; char *uu_program; char *distribution; char *full_header; char *process_xrefs; char *show_number_lines; char *keep_thread_info; char *sort_threads; char *expire_kills; char *default_charset; char *assemble_partials; char *cache_ahead_size; char *cache_trail_size; char *subject_columns; } str; struct { char **header_format; regex_t *quote_regexp; int cache_ahead_size; int cache_trail_size; int subject_columns; char full_header; char process_xrefs; char show_number_lines; char keep_thread_info; char expire_kills; char assemble_partials; } conv; }; static struct per_group per_group, per_group_default = {{0, }, }; typedef union { char *str; XrmQuark q; } RES_NAME; typedef struct { RES_NAME name; RES_NAME class; const int offset; } RES_DESC; static RES_DESC per_server_desc[] = { #define offset(field) XtOffsetOf(struct per_server, str.field) {{"newsrcFile"}, {"NewsrcFile"}, offset(newsrc_file)}, {{"oldNewsrcFile"}, {"OldNewsrcFile"}, offset(old_newsrc_file)}, {{"killFile"}, {"KillFile"}, offset(kill_file)}, {{"remoteNewsrcFile"}, {"RemoteNewsrcFile"}, offset(remote_newsrc_file)}, {{"remoteKillFile"}, {"RemoteKillFile"}, offset(remote_kill_file)}, {{"authInfoUser"}, {"AuthInfoUser"}, offset(auth_info_user)}, {{"authInfoPass"}, {"AuthInfoPass"}, offset(auth_info_pass)}, {{"postingAgent"}, {"PostingAgent"}, offset(posting_agent)}, {{"rescanTimeout"}, {"RescanTimeout"}, offset(rescan_timeout)}, {{"retrieveDescriptions"}, {"RetrieveDescriptions"}, offset(retrieve_descriptions)}, {{"checkForNewGroups"}, {"CheckForNewGroups"}, offset(check_for_new_groups)}, {{"readActiveFile"}, {"ReadActiveFile"}, offset(read_active_file)}, {{"fillNewsrcFile"}, {"FillNewsrcFile"}, offset(fill_newsrc_file)}, {{"threadAheadGroups"}, {"ThreadAheadGroups"}, offset(thread_ahead_groups)}, {{"askHowMany"}, {"AskHowMany"}, offset(ask_how_many)}, {{"tryListActive"}, {"TryListActive"}, offset(try_list_active)}, {{"cacheDir"}, {"CacheDir"}, offset(cache_dir)}, {{"descriptionsFile"}, {"DescriptionsFile"}, offset(descriptions_file)}, {{"saveThreadInfo"}, {"SaveThreadInfo"}, offset(save_thread_info)}, {{"groupNameColumns"}, {"GroupNameColumns"}, offset(group_name_columns)}, #undef offset }; static RES_DESC per_group_desc[] = { #define offset(field) XtOffsetOf(struct per_group, str.field) {{"fullName"}, {"FullName"}, offset(full_name)}, {{"organization"}, {"Organization"}, offset(organization)}, {{"replyTo"}, {"ReplyTo"}, offset(reply_to)}, {{"distribution"}, {"Distribution"}, offset(distribution)}, {{"headerFormat"}, {"HeaderFormat"}, offset(header_format)}, {{"quoteRegexp"}, {"QuoteRegexp"}, offset(quote_regexp)}, {{"quoteString"}, {"QuoteString"}, offset(quote_string)}, {{"quoteQuoteString"}, {"QuoteString"}, offset(quote_quote_string)}, {{"attribution"}, {"Attribution"}, offset(attribution)}, {{"signatureFile"}, {"SignatureFile"}, offset(signature_file)}, {{"extraHeaders"}, {"ExtraHeaders"}, offset(extra_headers)}, {{"followupHeaders"}, {"FollowupHeaders"}, offset(followup_headers)}, {{"postedAndMailed"}, {"PostedAndMailed"}, offset(posted_and_mailed)}, {{"uuDir"}, {"UuDir"}, offset(uu_dir)}, {{"uuProgram"}, {"UuProgram"}, offset(uu_program)}, {{"fullHeader"}, {"FullHeader"}, offset(full_header)}, {{"processXrefs"}, {"ProcessXrefs"}, offset(process_xrefs)}, {{"showNumberLines"}, {"ShowNumberLines"}, offset(show_number_lines)}, {{"keepThreadInfo"}, {"KeepThreadInfo"}, offset(keep_thread_info)}, {{"sortThreads"}, {"SortThreads"}, offset(sort_threads)}, {{"expireKills"}, {"ExpireKills"}, offset(expire_kills)}, {{"defaultCharset"}, {"DefaultCharset"}, offset(default_charset)}, {{"assemblePartials"}, {"AssemblePartials"}, offset(assemble_partials)}, {{"cacheAheadSize"}, {"CacheSize"}, offset(cache_ahead_size)}, {{"cacheTrailSize"}, {"CacheSize"}, offset(cache_trail_size)}, {{"subjectColumns"}, {"SubjectColumns"}, offset(subject_columns)}, #undef offset }; static void clear_per_server(void) { if (per_server.conv.thread_ahead_groups) { XtFree(per_server.conv.thread_ahead_groups[0]); XtFree((char *)per_server.conv.thread_ahead_groups); } per_server = per_server_default; } static void clear_per_group(void) { if (per_group.conv.header_format && per_group.conv.header_format != default_header_format) { XtFree(per_group.conv.header_format[0]); XtFree((char *)per_group.conv.header_format); } if (per_group.conv.quote_regexp) { if (per_group.conv.quote_regexp != &default_quote_re) { regfree(per_group.conv.quote_regexp); memset(per_group.conv.quote_regexp, 0, sizeof(regex_t)); } per_group.conv.quote_regexp = NULL; } per_group = per_group_default; } static char **str_to_list(char *list, char *sep) { char **res; int n_alloc, n; if (!list || !sep || *sep == '\0') return NULL; while (*list != '\0' && strchr(sep, *list)) list++; if (*list == '\0') { res = (char **)XtMalloc(sizeof res[0]); res[0] = NULL; return res; } list = XtNewString(list); n_alloc = 8; res = (char **)XtMalloc(n_alloc * sizeof res[0]); n = 0; do { if (n + 4 > n_alloc) { n_alloc *= 2; res = (char **)XtRealloc((char *)res, n_alloc * sizeof res[0]); } res[n++] = list; while (*list != '\0' && !strchr(sep, *list)) list++; if (*list == '\0') break; *list++ = '\0'; while (*list != '\0' && strchr(sep, *list)) list++; } while (*list != '\0'); res[n] = NULL; return res; } static int str_to_bool(char *str, int def) { static struct { char *str; char len; char val; } values[] = { {"true", 4, True}, {"false", 5, False}, {"yes", 3, True}, {"no", 2, False}, {"on", 2, True}, {"off", 3, False}, }; int len; int i; if (!str) return def; len = strlen(str); for (i = 0 ; i < XtNumber(values) ; i++) if (len == values[i].len && case_lstrcmp(str, values[i].str) == 0) return values[i].val; fprintf(stderr, "knews: failed to convert \"%s\" to a boolean.\n", str); return def; } static int str_to_keep(char *str, int def) { if (!str) return def; if ((*str == 's' || *str == 'S') && case_lstrcmp(str, "subscribed") == 0) return KeepTypeSubscribed; return str_to_bool(str, def); } static long str_to_long(char *str, long def) { long res; if (!str) return def; if (sscanf(str, "%ld", &res) == 1) return res; fprintf(stderr, "knews: failed to convert \"%s\" to an integer.\n", str); return def; } /*************************************************************************/ static long print_tabs(char *dest, long from, long to) { long n = 0; while (from < to) { dest[n++] = '\t'; from += 8; } return n; } static long print_res(char *dest, char *name, char *set_val, char *def_val) { long len; if (set_val) len = 0; else { dest[0] = '!'; dest[1] = ' '; len = 2; if (def_val) set_val = def_val; else set_val = ""; } strcpy(dest + len, name); len = strlen(dest); dest[len++] = ':'; len += print_tabs(dest + len, len, 32); strcpy(dest + len, set_val); len += strlen(dest + len); dest[len++] = '\n'; dest[len] = '\0'; return len; } static long print_bool(char *dest, char *name, char *set_val, int def_val) { return print_res(dest, name, set_val, def_val ? "True" : "False"); } static void get_default_database(char *str, long len) { int is_config_server; is_config_server = global.config_nntp_server && global.nntp_server && strcmp(global.config_nntp_server, global.nntp_server) == 0; strcpy(str, "! Automatically generated knews config file. Rows beginning\n" "! with ! are comments. Below are some commented-out defaults.\n" "! Change and uncomment some of them if you wish.\n" "!\n" "! The environment variables below are only for illustrative\n" "! purposes; they won't work here since there is no shell to\n" "! expand them. On the other hand, knews will expand ~ file\n" "! names in most cases, but not in #include's, since it's Xlib\n" "! that handles those.\n" "\n" "\n"); str += strlen(str); if (is_config_server) { str += print_res(str, "newsrcFile", "~/.newsrc", per_server_default.str.newsrc_file); str += print_res(str, "oldNewsrcFile", "~/.oldnewsrc", per_server_default.str.old_newsrc_file); } else { str += print_res(str, "newsrcFile", global.newsrc_templ, per_server_default.str.newsrc_file); str += print_res(str, "oldNewsrcFile", global.old_newsrc_templ, per_server_default.str.old_newsrc_file); } str += print_res(str, "killFile", NULL, "~/.kill-%s"); str += print_bool(str, "readActiveFile", global.read_active_file, per_server_default.conv.read_active_file); str += print_bool(str, "retrieveDescriptions", global.retrieve_descr, per_server_default.conv.retrieve_descriptions); str += print_bool(str, "fillNewsrcFile", global.fill_newsrc_file, per_server_default.conv.fill_newsrc_file); str += print_res(str, "tryListActive", NULL, "True"); str += print_bool(str, "checkForNewGroups", global.check_for_new_groups, per_server_default.conv.check_for_new_groups); str += print_res(str, "rescanTimeout", NULL, "60"); str += print_res(str, "askHowMany", NULL, "False"); str += print_res(str, "postingAgent", is_config_server ? global.config_posting_agent : NULL, NULL); str += print_res(str, "threadAheadGroups", NULL, "white space separated list of groups"); str += print_res(str, "cacheDir", NULL, per_server_default.str.cache_dir); str += print_res(str, "descriptionsFile", NULL, per_server_default.str.descriptions_file); str += print_res(str, "saveThreadInfo", NULL, "False"); str += print_res(str, "groupNameColumns", NULL, "42"); strcpy(str, "\n" "\n" "! The resources below may be set on a per-group basis.\n" "\n"); str += strlen(str); str += print_res(str, "*keepThreadInfo", global.keep_thread_info, "False"); str += print_res(str, "*sortThreads", NULL, "none"); str += print_res(str, "*headerFormat", NULL, "Subject:Newsgroups:Followup-To:Reply-To:\\\n" "! Content-Description:Date:Organization:From:"); str += print_res(str, "*quoteRegexp", NULL, "^[ ]*[:>|]"); str += print_res(str, "*quoteString", NULL, "> "); str += print_res(str, "*quotequoteString", NULL, ">"); str += print_bool(str, "*showNumberLines", global.show_number_lines, per_group_default.conv.show_number_lines); str += print_res(str, "*attribution", NULL, "In article %m,\\n %f writes:"); str += print_res(str, "*signatureFile", NULL, "~/.signature"); str += print_res(str, "*organization", NULL, "${NEWSORG:-${ORGANIZATION}}"); str += print_res(str, "*fullName", NULL, "$NAME"); str += print_res(str, "*replyTo", NULL, "$REPLYTO"); str += print_res(str, "*distribution", NULL, "$DEFNEWSDIS"); str += print_res(str, "*extraHeaders", NULL, NULL); str += print_res(str, "*followupHeaders", NULL, NULL); str += print_res(str, "*postedAndMailed", NULL, per_group_default.str.posted_and_mailed); str += print_res(str, "*uuDir", NULL, "~/News"); str += print_res(str, "*uuProgram", NULL, NULL); str += print_bool(str, "*expireKills", NULL, per_group_default.conv.expire_kills); str += print_bool(str, "*assemblePartials", NULL, per_group_default.conv.assemble_partials); str += print_res(str, "*defaultCharset", NULL, "us-ascii"); str += print_res(str, "*cacheAheadSize", NULL, "0"); str += print_res(str, "*cacheTrailSize", NULL, "0"); str += print_res(str, "*groupNameColumns", NULL, "56"); strcpy(str, "\n" "! Here are a few examples to illustrate bindings. '*' is\n" "! a loose binding, i.e. it matches any number of components\n" "! of a group name.\n" "\n" "! swnet*signatureFile: ~/.signature-svensk\n" "! de*signatureFile: ~/.signature-deutsch\n" "! *linux*signatureFile: ~/.signature-linux\n"); str += strlen(str); } static XrmDatabase load_database(char *path) { XrmDatabase db; struct stat stat_buf; char *buffer; int fd; if (!path) return NULL; fd = open(path, O_RDONLY); if (fd >= 0 && fstat(fd, &stat_buf) >= 0) global.last_time = stat_buf.st_atime; else global.last_time = 0; if (fd < 0) { char default_db[4096]; global.last_time = 0; if (errno != ENOENT) { perror(path); popup_title_notice("Error with confir file", path, True); return NULL; } fprintf(stderr, "Knews: creating config file %s\n", path); fd = open_mkdir(path, O_WRONLY|O_TRUNC|O_EXCL|O_CREAT, True); if (fd < 0) { perror(path); popup_title_notice("Failed to create config file", path, True); return NULL; } get_default_database(default_db, sizeof default_db); if (writen(fd, default_db, strlen(default_db)) < 0) { close(fd); return NULL; } lseek(fd, 0, SEEK_SET); popup_title_notice("Created config file", path, False); } buffer = snarf_file(fd, NULL); close(fd); if (!buffer) return NULL; db = XrmGetStringDatabase(buffer); free(buffer); if (global.bogus_file_system) if (utime(path, NULL) < 0) perror("utime"); return db; } static XrmQuark *quarkify(char *str, int cap) { int n, n_alloc = 16; XrmQuark *res; char *c; n_alloc = 16; res = (XrmQuark *)XtMalloc(n_alloc * sizeof res[0]); n = 0; do { int ch = *str; if (n + 4 > n_alloc) { n_alloc *= 2; res = (XrmQuark *)XtRealloc((char *)res, n_alloc * sizeof res[0]); } c = strchr(str, '.'); if (c) *c = '\0'; if (cap && IS_LOWER(ch)) *str = TO_UPPER(ch); res[n++] = XrmStringToQuark(str); if (cap) *str = ch; if (c) *c++ = '.'; str = c; } while (str); return res; } void res_initialize(void) { char *tmp; int i; for (i = 0 ; i < XtNumber(per_server_desc) ; i++) { per_server_desc[i].name.q = XrmPermStringToQuark(per_server_desc[i].name.str); per_server_desc[i].class.q = XrmPermStringToQuark(per_server_desc[i].class.str); } for (i = 0 ; i < XtNumber(per_group_desc) ; i++) { per_group_desc[i].name.q = XrmPermStringToQuark(per_group_desc[i].name.str); per_group_desc[i].class.q = XrmPermStringToQuark(per_group_desc[i].class.str); } if (regcomp(&default_quote_re, DEFAULT_QUOTE_REGEXP, REGEXP_COMPILE_FLAGS & ~REG_NOSUB) != 0) fputs(bad_regex_msg, stderr); per_server_default.str.newsrc_file = global.newsrc_templ ? global.newsrc_templ : "~/.newsrc-%s"; per_server_default.str.old_newsrc_file = global.old_newsrc_templ ? global.old_newsrc_templ : "~/.oldnewsrc-%s"; per_server_default.str.kill_file = global.kill_file_templ ? global.kill_file_templ : "~/.kill-%s"; per_server_default.str.retrieve_descriptions = global.retrieve_descr; per_server_default.str.read_active_file = global.read_active_file; per_server_default.str.fill_newsrc_file = global.fill_newsrc_file; per_server_default.str.check_for_new_groups = global.check_for_new_groups; per_server_default.str.cache_dir = "~/.knews/cache-%s"; per_server_default.conv.rescan_timeout = 60; per_server_default.conv.retrieve_descriptions = True; per_server_default.conv.check_for_new_groups = True; per_server_default.conv.ask_how_many = False; per_server_default.conv.read_active_file = True; per_server_default.conv.fill_newsrc_file = False; per_server_default.conv.try_list_active = True; per_server_default.conv.save_thread_info = False; per_server_default.conv.group_name_columns = 42; per_group_default.str.quote_string = "> "; per_group_default.str.quote_quote_string = ">"; per_group_default.str.attribution = DEFAULT_ATTRIBUTION; per_group_default.str.signature_file = "~/.signature"; per_group_default.str.uu_dir = "~/News"; per_group_default.str.posted_and_mailed = "[Posted and mailed]"; per_group_default.str.show_number_lines = global.show_number_lines; per_group_default.str.keep_thread_info = global.keep_thread_info; per_group_default.conv.full_header = False; per_group_default.conv.process_xrefs = True; per_group_default.conv.show_number_lines = False; per_group_default.conv.header_format = default_header_format; per_group_default.conv.keep_thread_info = KeepTypeNo; per_group_default.conv.expire_kills = True; per_group_default.conv.assemble_partials = True; per_group_default.conv.quote_regexp = &default_quote_re; per_group_default.conv.subject_columns = 56; tmp = getenv("NAME"); if (tmp) tmp = XtNewString(tmp); else tmp = pw_name; per_group_default.str.full_name = tmp; tmp = getenv("NEWSORG"); if (!tmp) tmp = getenv("ORGANIZATION"); if (tmp) tmp = XtNewString(tmp); per_group_default.str.organization = tmp; tmp = getenv("REPLYTO"); if (tmp) tmp = XtNewString(tmp); per_group_default.str.reply_to = tmp; tmp = getenv("DEFNEWSDIS"); if (tmp) tmp = XtNewString(tmp); per_group_default.str.distribution = tmp; per_server = per_server_default; per_group = per_group_default; } void res_set_pw_name(char *name) { if (name) pw_name = XtNewString(name); } int res_load(char *path) { clear_per_group(); clear_per_server(); if (db) { XrmDestroyDatabase(db); db = 0; } global.last_time = 0; db = load_database(path); if (db) { XrmQuark name[2]; XrmQuark class[2]; XrmQuark rep; int i; name[1] = class[1] = 0; for (i = 0 ; i < XtNumber(per_server_desc) ; i++) { XrmValue val; name[0] = per_server_desc[i].name.q; class[0] = per_server_desc[i].class.q; if (XrmQGetResource(db, name, class, &rep, &val) && val.addr) *((char **)((char *)&per_server + per_server_desc[i].offset)) = (char *)val.addr; } } per_server.conv.thread_ahead_groups = str_to_list(per_server.str.thread_ahead_groups, " \t"); per_server.conv.rescan_timeout = str_to_long(per_server.str.rescan_timeout, per_server.conv.rescan_timeout); per_server.conv.group_name_columns = str_to_long(per_server.str.group_name_columns, per_server.conv.group_name_columns); per_server.conv.retrieve_descriptions = str_to_bool(per_server.str.retrieve_descriptions, per_server.conv.retrieve_descriptions); per_server.conv.check_for_new_groups = str_to_bool(per_server.str.check_for_new_groups, per_server.conv.check_for_new_groups); per_server.conv.read_active_file = str_to_bool(per_server.str.read_active_file, per_server.conv.read_active_file); per_server.conv.ask_how_many = str_to_bool(per_server.str.ask_how_many, per_server.conv.ask_how_many); per_server.conv.fill_newsrc_file = str_to_bool(per_server.str.fill_newsrc_file, per_server.conv.fill_newsrc_file); per_server.conv.try_list_active = str_to_bool(per_server.str.try_list_active, per_server.conv.try_list_active); per_server.conv.save_thread_info = str_to_bool(per_server.str.save_thread_info, per_server.conv.save_thread_info); res_enter_group("none"); return 0; } void res_enter_group(char *group) { clear_per_group(); if (db) { XrmQuark *name; XrmQuark *class; XrmHashTable *list = NULL; int n = 4; group = XtNewString(group); name = quarkify(group, False); class = quarkify(group, True); do { n *= 2; XtFree((char *)list); list = (XrmHashTable *)XtMalloc(n * sizeof list[0]); } while (!XrmQGetSearchList(db, name, class, list, n)); for (n = 0 ; n < XtNumber(per_group_desc) ; n++) { XrmValue val; XrmQuark rep; if (XrmQGetSearchResource(list, per_group_desc[n].name.q, per_group_desc[n].class.q, &rep, &val) && val.addr) *((char **)((char *)&per_group + per_group_desc[n].offset)) = (char *)val.addr; } XtFree((char *)list); XtFree((char *)name); XtFree((char *)class); XtFree(group); group = NULL; } if (per_group.str.header_format) per_group.conv.header_format = str_to_list(per_group.str.header_format, " \t:"); if (per_group.str.quote_regexp) if (regcomp("e_re, per_group.str.quote_regexp, REGEXP_COMPILE_FLAGS & ~REG_NOSUB) == 0) per_group.conv.quote_regexp = "e_re; else fprintf(stderr, "Warning: failed to compile regexp \"%s\".\n", per_group.str.quote_regexp); per_group.conv.full_header = str_to_bool(per_group.str.full_header, per_group.conv.full_header); per_group.conv.process_xrefs = str_to_bool(per_group.str.process_xrefs, per_group.conv.process_xrefs); per_group.conv.show_number_lines = str_to_bool(per_group.str.show_number_lines, per_group.conv.show_number_lines); per_group.conv.keep_thread_info = str_to_keep(per_group.str.keep_thread_info, per_group.conv.keep_thread_info); per_group.conv.assemble_partials = str_to_bool(per_group.str.assemble_partials, per_group.conv.assemble_partials); per_group.conv.cache_ahead_size = str_to_long(per_group.str.cache_ahead_size, per_group.conv.cache_ahead_size); per_group.conv.cache_trail_size = str_to_long(per_group.str.cache_trail_size, per_group.conv.cache_trail_size); per_group.conv.subject_columns = str_to_long(per_group.str.subject_columns, per_group.conv.subject_columns); } #define PER_SERVER_STR_FUNC(field) \ char *res_##field(void) \ { \ return per_server.str.field; \ } PER_SERVER_STR_FUNC(newsrc_file) PER_SERVER_STR_FUNC(old_newsrc_file) PER_SERVER_STR_FUNC(kill_file) PER_SERVER_STR_FUNC(remote_newsrc_file) PER_SERVER_STR_FUNC(remote_kill_file) PER_SERVER_STR_FUNC(auth_info_user) PER_SERVER_STR_FUNC(auth_info_pass) PER_SERVER_STR_FUNC(posting_agent) PER_SERVER_STR_FUNC(cache_dir) PER_SERVER_STR_FUNC(descriptions_file) #define PER_SERVER_FUNC(type, field) \ type res_##field(void) \ { \ return per_server.conv.field; \ } PER_SERVER_FUNC(char**, thread_ahead_groups) PER_SERVER_FUNC(long, rescan_timeout) PER_SERVER_FUNC(int, retrieve_descriptions) PER_SERVER_FUNC(int, check_for_new_groups) PER_SERVER_FUNC(int, read_active_file) PER_SERVER_FUNC(int, ask_how_many) PER_SERVER_FUNC(int, fill_newsrc_file) PER_SERVER_FUNC(int, try_list_active) PER_SERVER_FUNC(int, save_thread_info) PER_SERVER_FUNC(int, group_name_columns) #define PER_GROUP_STR_FUNC(field) \ char *res_##field(void) \ { \ return per_group.str.field; \ } PER_GROUP_STR_FUNC(quote_string) PER_GROUP_STR_FUNC(quote_quote_string) PER_GROUP_STR_FUNC(attribution) PER_GROUP_STR_FUNC(full_name) PER_GROUP_STR_FUNC(signature_file) PER_GROUP_STR_FUNC(organization) PER_GROUP_STR_FUNC(reply_to) PER_GROUP_STR_FUNC(extra_headers) PER_GROUP_STR_FUNC(followup_headers) PER_GROUP_STR_FUNC(posted_and_mailed) PER_GROUP_STR_FUNC(uu_dir) PER_GROUP_STR_FUNC(uu_program) PER_GROUP_STR_FUNC(distribution) PER_GROUP_STR_FUNC(sort_threads) PER_GROUP_STR_FUNC(default_charset) #define PER_GROUP_FUNC(type, field) \ type res_##field(void) \ { \ return per_group.conv.field; \ } PER_GROUP_FUNC(char**, header_format) PER_GROUP_FUNC(int, full_header) PER_GROUP_FUNC(int, process_xrefs) PER_GROUP_FUNC(int, show_number_lines) PER_GROUP_FUNC(int, expire_kills) PER_GROUP_FUNC(regex_t*, quote_regexp) PER_GROUP_FUNC(int, assemble_partials) PER_GROUP_FUNC(int, cache_ahead_size) PER_GROUP_FUNC(int, cache_trail_size) PER_GROUP_FUNC(int, subject_columns) int res_keep_thread_info(int subscribed) { KeepType keep = per_group.conv.keep_thread_info; if (keep == KeepTypeNo) return False; if (keep == KeepTypeYes) return True; return subscribed; } void res_set_keep_thread_info(int keep) { per_group.conv.keep_thread_info = keep ? KeepTypeYes : KeepTypeNo; } void res_set_full_header(int full) { per_group.conv.full_header = full; } void res_set_ask_how_many(int ask) { per_server.conv.ask_how_many = ask; } void res_set_default_charset(char *charset) { per_group.str.default_charset = charset; } ./knews-1.0b.1/src/p_I.h100644 1244 1244 1446 6455455545 13313 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #ifndef MAIL_COMMAND #error "You need to define MAIL_COMMAND in configure.h" #endif #define POST (1<<0) #define MAIL (1<<1) #define POST_DONE (1<<2) #define MAIL_DONE (1<<3) #define OK_TO_POST (1<<4) #define NEEDS_SENDER (1<<5) #define ORIG_MAIL (1<<6) /* article originally intended as email only */ typedef struct PostContext { char *file_name; const char *art; char *charset; int line; int n_attachments; unsigned short flags; unsigned char busy; unsigned char has_8bit; struct PostWidgets *widgets; struct PostAttachment **attachments; char *q_str; char *qq_str; } PostContext; typedef struct PostAttachment PostAttachment; ./knews-1.0b.1/src/global.h100644 1244 1244 10664 6625550654 14062 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #undef _POSIX_SOURCE #define _POSIX_SOURCE 1 #undef _POSIX_C_SOURCE #define _POSIX_C_SOURCE 2 #define KNEWS_VERSION "1.0b.1" #include "../configure.h" #ifndef BIN_SH # define BIN_SH "/bin/sh" #endif #include #include #include #include #include #include #include #include #include #include #include #include #include "../Widgets/ArtTreeNode.h" extern XtAppContext app_cont; extern Display *display; typedef enum { NewsModeDisconnected, NewsModeConnected, NewsModeGroup, NewsModeThread, NewsModeAllgroups, NewsModeSomegroups, NewsModeNewgroups } NewsMode; typedef struct subj_node { struct subj_node *hash_next; struct subj_node *next; struct subj_node *prev; struct art_node *thread; char *subject; long disp; long no_unread; Pixmap pixmap; unsigned short hash_len; unsigned char has_tagged; } SUBJECT; typedef struct art_node { ART_TREE_NODE tree_data; struct art_node *hash_next; struct art_node *next; char *msgid; SUBJECT *subject; char *from; char *xref; time_t date; long no; Pixmap pixmap; unsigned short hash_len; unsigned short lines; #if 0 long bytes; #endif unsigned char read; unsigned char killed; } ARTICLE; #define PARENT(art) ((art)->tree_data.parent) #define CHILD1(art) ((art)->tree_data.child1) #define SIBLING(art) ((art)->tree_data.sibling) #define A_PARENT(art) ((ARTICLE *)PARENT(art)) #define A_CHILD1(art) ((ARTICLE *)CHILD1(art)) #define A_SIBLING(art) ((ARTICLE *)SIBLING(art)) #define REGEXP_COMPILE_FLAGS \ ((global.icase_regexps ? REG_ICASE : 0) | REG_NOSUB | REG_EXTENDED) #define HOT_PIXMAP_SIZE 8 typedef struct art_list_node { long first; long last; struct art_list_node *next; } ART_LIST_NODE; typedef struct group { char *name; char *description; long no_unread; long first_art; long last_art; ART_LIST_NODE *read_arts; long disp; char subscribed; char moderated; char found_in_newsrc; char ahead_flag; } GROUP; extern struct Global { String nntp_server; String config_nntp_server; String config_posting_agent; String edit_command; String url_command; String print_command; String needs_terminal; String copious_output; Cursor cursor; Cursor busy_cursor; String version; String mail_name; String config_file; String newsrc_templ; String old_newsrc_templ; String kill_file_templ; String group_kill_file_templ; String auto_subscribe; String mime_types; String retrieve_descr; String read_active_file; String fill_newsrc_file; String show_number_lines; String keep_thread_info; String check_for_new_groups; String confirm_quit_group; Pixel default_hot_pixel; Pixel pixel; Pixel quote_pixel; Pixel header_pixel; Pixel alert_pixel; Pixel clickable_pixel; long stderr_timeout; int chunk_size; int post_misc_menu_size; int extra_menu_size; int type_menu_size; int forward_menu_size; int n_cols; Boolean separate_windows; Boolean bell; Boolean head_debug; Boolean use_icon; Boolean confirm_quit; Boolean confirm_catchup; Boolean icase_regexps; Boolean show_cache; Boolean bogus_file_system; Boolean generate_path; Boolean mime_forward; Boolean quote_empty; Boolean sort_groups; Boolean inline_images; Boolean show_xfaces; Boolean color_hack; /* private */ char *user_id; char *domain_name; void *serv_addr; GROUP **groups; long no_groups; long max_groups; GROUP **new_groups; long no_new_groups; SUBJECT *curr_subj; ARTICLE *curr_art; GROUP *curr_group; long n_hot; long n_killed; time_t last_time; GC gc; Visual *visual; Colormap cmap; Cardinal depth; NewsMode mode; unsigned int busy; Boolean posting_allowed; Boolean xover_supported; Boolean list_active_supported; } global; extern struct SERVER *main_server; extern struct THREAD_CONTEXT *main_thr; ./knews-1.0b.1/src/p_attach.c100644 1244 1244 33547 6455455545 14411 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include "global.h" #include "decode.h" #include "expand.h" #include "file.h" #include "font.h" #include "mailcap.h" #include "p_I.h" #include "p_attach.h" #include "parse.h" #include "util.h" #include "xutil.h" struct PostAttachment { char *file_name; char *type; char *name; char *descr; unsigned char is_inline; unsigned char enc; unsigned char needs_enc; unsigned char has_8bit; }; static const char *get_mime_type(const char*); static int check_file(PostAttachment *pa, char *message) { FILE *fp; long n_ascii = 0; long n_ctrl = 0; long n_8bit = 0; int ch; const char *c; fp = fopen(pa->file_name, "r"); if (!fp) { perror(pa->file_name); strcpy(message, "Couldn't open file!"); return False; } while ((ch = getc(fp)) != EOF) if (ch >= 128 + 32) n_8bit++; else if (ch >= 128) n_ctrl++; else if (ch >= 32) n_ascii++; else switch (ch) { case '\t': /* 9 */ case '\n': /* 10 */ case '\f': /* 12 */ case '\r': /* 13 */ case 14: /* SO */ case 15: /* SI */ case 27: /* ESC */ n_ascii++; break; default: n_ctrl++; break; } fclose(fp); c = strrchr(pa->file_name, '.'); if (c) { c = get_mime_type(c + 1); if (c) pa->type = XtNewString(c); } if (n_ctrl > 0) { /* binary file */ pa->enc = MimeEncBase64; pa->needs_enc = True; pa->has_8bit = True; if (!pa->type) strcpy(message, "Couldn't guess content-type type."); } else { pa->enc = n_8bit > n_ascii ? MimeEncBase64 : MimeEncNone; pa->needs_enc = False; pa->has_8bit = n_8bit > 0; if (!pa->type) { pa->type = XtNewString("text/plain"); strcpy(message, "Maybe text/plain? " "Please give a charset parameter."); } } return True; } /*************************************************************************/ static int enc_file_none(PostAttachment *pa, FILE *fin, FILE *fout) { int c, bol = True; while ((c = getc(fin)) != EOF) { if (c == '\n') putc('\r', fout); else if (bol && c == '.') putc('.', fout); putc(c, fout); bol = c == '\n'; } if (!bol) fputs("\r\n", fout); return True; } static int enc_file_base64(PostAttachment *pa, FILE *fin, FILE *fout) { unsigned char buf[4]; unsigned long acc; int n, pos = 0; while ((n = fread(buf, 1, 3, fin)) == 3) { acc = (buf[0] << 16) | (buf[1] << 8) | buf[2]; buf[0] = base64_alpha[(acc >> 18) & 0x3fu]; buf[1] = base64_alpha[(acc >> 12) & 0x3fu]; buf[2] = base64_alpha[(acc >> 6) & 0x3fu]; buf[3] = base64_alpha[(acc ) & 0x3fu]; fwrite(buf, 1, 4, fout); pos += 4; if (pos >= 76) { fwrite("\r\n", 1, 2, fout); pos = 0; } } switch (n) { case 1: acc = buf[0] << 16; buf[0] = base64_alpha[(acc >> 18) & 0x3ful]; buf[1] = base64_alpha[(acc >> 12) & 0x3ful]; buf[2] = '='; buf[3] = '='; fwrite(buf, 1, 4, fout); break; case 2: acc = (buf[0] << 16) | (buf[1] << 8); buf[0] = base64_alpha[(acc >> 18) & 0x3fu]; buf[1] = base64_alpha[(acc >> 12) & 0x3fu]; buf[2] = base64_alpha[(acc >> 6) & 0x3fu]; buf[3] = '='; fwrite(buf, 1, 4, fout); break; } if (pos != 0) fwrite("\r\n", 1, 2, fout); fwrite("\r\n", 1, 2, fout); return True; } static int enc_file_uue(PostAttachment *pa, FILE *fin, FILE *fout) { char *name = pa->name; char buf[45]; int n; if (!name) { name = strrchr(pa->file_name, '/'); if (name) name++; else name = pa->file_name; } fprintf(fout, "begin 0644 %s\r\n", name); #if 0 # define UU_CHAR(a) ((unsigned char)(' ' + ((a) & 0x3fu))) #else # define UU_CHAR(a) ((unsigned char)(' ' + 1 + (((a) - 1) & 0x3fu))) #endif while ((n = fread(buf, 1, sizeof buf, fin)) > 0) { unsigned long acc; unsigned char *c = (unsigned char *)buf; if (' ' + n == '.') putc('.', fout); putc(' ' + n, fout); while (n > 2) { acc = (c[0] << 16) | (c[1] << 8) | c[2]; fprintf(fout, "%c%c%c%c", UU_CHAR(acc >> 18), UU_CHAR(acc >> 12), UU_CHAR(acc >> 6), UU_CHAR(acc )); /* (unsigned char)(' ' + ((acc >> 18) & 0x3fu)), (unsigned char)(' ' + ((acc >> 12) & 0x3fu)), (unsigned char)(' ' + ((acc >> 6) & 0x3fu)), (unsigned char)(' ' + ((acc ) & 0x3fu))); */ n -= 3; c += 3; } switch (n) { case 1: acc = c[0] << 16; fprintf(fout, "%c%c", UU_CHAR(acc >> 18), UU_CHAR(acc >> 12)); /* (unsigned char)(' ' + ((acc >> 18) & 0x3fu)), (unsigned char)(' ' + ((acc >> 12) & 0x3fu))); */ break; case 2: acc = (c[0] << 16) | (c[1] << 8); fprintf(fout, "%c%c%c", UU_CHAR(acc >> 18), UU_CHAR(acc >> 12), UU_CHAR(acc >> 6)); /* (unsigned char)(' ' + ((acc >> 18) & 0x3fu)), (unsigned char)(' ' + ((acc >> 12) & 0x3fu)), (unsigned char)(' ' + ((acc >> 6) & 0x3fu))); */ break; } fputs("\r\n", fout); } fputs("`\r\nend\r\n\r\n", fout); return True; } static int enc_file_qp(PostAttachment *pa, FILE *fin, FILE *fout) { static const unsigned char hex_char[16] = "0123456789ABCDEF"; int ch, next, do_enc, col; next = getc(fin); col = 0; while ((ch = next) != EOF) { next = getc(fin); if (ch == '\n') { fputs("\r\n", fout); col = 0; continue; } if (col > 68) { fputs("=\r\n", fout); col = 0; } if (ch < 32 || ch > 126) do_enc = True; else switch (ch) { case ' ': do_enc = next == '\n' || next == EOF; break; case '.': case 'F': do_enc = col == 0; break; case '=': do_enc = True; break; default: do_enc = False; break; } if (!do_enc) { putc(ch, fout); col++; } else { putc('=', fout); putc(hex_char[(ch >> 4) & 0xfu], fout); putc(hex_char[(ch ) & 0xfu], fout); col += 3; } } fputs("\r\n\r\n" + (col == 0 ? 2 : 0), fout); return True; } /*************************************************************************/ void free_attachment(PostAttachment *pa) { XtFree(pa->file_name); XtFree(pa->type); XtFree(pa->name); XtFree(pa->descr); pa->file_name = NULL; pa->type = NULL; pa->name = NULL; pa->descr = NULL; XtFree((char *)pa); } PostAttachment *create_attachment(char *file_name, char *message) { PostAttachment *pa; char *c; message[0] = '\0'; file_name = expand_path(file_name); if (!file_name) { strcpy(message, "File name error!"); return NULL; } pa = (PostAttachment *)XtMalloc(sizeof *pa); pa->file_name = file_name; pa->type = NULL; pa->name = NULL; pa->descr = NULL; pa->is_inline = False; pa->enc = -1; pa->needs_enc = False; pa->has_8bit = False; if (!check_file(pa, message)) { free_attachment(pa); return NULL; } c = strrchr(file_name, '/'); if (c) c++; else c = file_name; pa->name = XtNewString(c); return pa; } void print_attach_info(PostAttachment *pa, char *buffer) { char *enc = NULL; int n; if (!pa->type) strcpy(buffer, "[unknown]"); else { n = strlen(pa->type); if (n < 18) strcpy(buffer, pa->type); else { memcpy(buffer, pa->type, 15); strcpy(buffer + 15, "..."); } } n = strlen(buffer); while (n < 20) buffer[n++] = ' '; buffer[n] = '\0'; switch (pa->enc) { case MimeEncNone: enc = " "; break; case MimeEncBase64: enc = "b64 "; break; case MimeEncUue: enc = "uue "; break; case MimeEncQP: enc = "Q-P "; break; } if (enc) strcpy(buffer + n, enc); n += strlen(buffer + n); strncat(buffer + n, pa->file_name, 248 - n); } int print_attachment(FILE *fout, PostAttachment *pa) { FILE *fin; int ok; if (!pa->type) { popup_title_notice("Unknown Content-Type", pa->file_name, False); return False; } fin = fopen(pa->file_name, "r"); if (!fin) { popup_title_notice("Couldn't open file", pa->file_name, False); return False; } fprintf(fout, "Content-Type: %s\r\n", pa->type); if (pa->descr && pa->descr[0] != '\0') fprintf(fout, "Content-Description: %s\r\n", pa->descr); fprintf(fout, "Content-Disposition: %s", pa->is_inline ? "inline" : "attachment"); if (pa->name && pa->name[0] != '\0') fprintf(fout, "; filename=\"%s\"", pa->name); fputs("\r\n", fout); switch (pa->enc) { case MimeEncNone: if (pa->has_8bit) fputs("Content-Transfer-Encoding: 8bit\r\n", fout); fputs("\r\n", fout); ok = enc_file_none(pa, fin, fout); break; case MimeEncBase64: fputs("Content-Transfer-Encoding: base64\r\n\r\n", fout); ok = enc_file_base64(pa, fin, fout); break; case MimeEncUue: fputs("Content-Transfer-Encoding: x-uue\r\n\r\n", fout); ok = enc_file_uue(pa, fin, fout); break; case MimeEncQP: fputs("Content-Transfer-Encoding: quoted-printable\r\n\r\n", fout); ok = enc_file_qp(pa, fin, fout); break; default: popup_title_notice("Bad encoding", pa->file_name, False); ok = False; break; } if (fclose(fin) < 0) { perror(pa->file_name); if (ok) { popup_title_notice("File error", pa->file_name, False); ok = False; } } return ok; } int attach_get_enc(PostAttachment *pa) { return pa ? pa->enc : -1; } int attach_is_inline(PostAttachment *pa) { return pa ? pa->is_inline : False; } char *attach_get_type(PostAttachment *pa) { return pa && pa->type ? pa->type : ""; } char *attach_get_name(PostAttachment *pa) { return pa && pa->name ? pa->name : ""; } char *attach_get_descr(PostAttachment *pa) { return pa && pa->descr ? pa->descr : ""; } int attach_set_enc(PostAttachment *pa, int enc, char *message) { message[0] = '\0'; switch (enc) { case MimeEncNone: if (pa->needs_enc) { strcpy(message, "Can't send binary file without encoding."); return False; } break; case MimeEncBase64: case MimeEncUue: if (!pa->needs_enc) strcpy(message, "Warning, unnecessary: text file needs no encoding."); break; case MimeEncQP: if (pa->needs_enc) strcpy(message, "QP for binary file? If you say so..."); else strcpy(message, "Quoted-printable = quoted-unreadable."); break; default: strcpy(message, "Error!"); return False; } pa->enc = enc; return True; } int attach_set_inline(PostAttachment *pa, int is_inline, char *message) { message[0] = '\0'; pa->is_inline = is_inline; return True; } int attach_set_type(PostAttachment *pa, char *type, char *message) { char *header[2]; char type_buf[80], subtype_buf[80]; MimeArg args[4] = {{0, }, }; int i, ok; if (!type) type = ""; message[0] = '\0'; header[0] = XtMalloc(strlen(type) + 32); sprintf(header[0], "Content-Type: %s", type); header[1] = NULL; ok = parse_content_type(header, type_buf, sizeof type_buf, subtype_buf, sizeof subtype_buf, args, XtNumber(args), True); if (!ok) strcpy(message, "Parse error!"); else if (case_lstrcmp(type_buf, "text") == 0) { char *charset = get_charset(args); if (!charset) strcpy(message, "Consider sepcifying a charset parameter."); else { MimeFont *font; font = get_font(charset); if (!font) strcpy(message, "Warning: No font for that charset."); } } else { const MailcapData *mcap; mcap = mailcap_lookup(type_buf, subtype_buf); if (!mcap) strcpy(message, "Warning: No mailcap entry for that type."); } XtFree(header[0]); for (i = 0 ; i < XtNumber(args) ; i++) { XtFree(args[i].name); XtFree(args[i].value); } if (ok) { XtFree(pa->type); pa->type = XtNewString(type); } return ok; } int attach_set_name(PostAttachment *pa, char *name, char *message) { message[0] = '\0'; XtFree(pa->name); pa->name = XtNewString(name); return True; } int attach_set_descr(PostAttachment *pa, char *descr, char *message) { message[0] = '\0'; XtFree(pa->descr); pa->descr = XtNewString(descr); return True; } /*************************************************************************/ typedef struct MimeType { const char *type; const char *suffix; } MimeType; static MimeType *mime_types = NULL; static long n_types = -1; static void load_mime_types(void) { static char *buffer = NULL; char *c; long n_alloc; n_types = 0; if (global.mime_types) { int fd = open(global.mime_types, O_RDONLY); if (fd < 0) perror(global.mime_types); else { buffer = snarf_file(fd, NULL); close(fd); } } n_alloc = 8; mime_types = (MimeType *)XtMalloc(n_alloc * sizeof mime_types[0]); c = buffer; while (c) { char *end = strchr(c, '\n'); char *type, *suffix; if (end) *end++ = '\0'; type = strtok(c, " \t"); if (type) while ((suffix = strtok(NULL, " \t"))) { if (n_types + 8 > n_alloc) { n_alloc = 2 * (n_types + 8); mime_types = (MimeType *)XtRealloc((char *)mime_types, n_alloc * sizeof mime_types[0]); } ascii_lower(suffix); mime_types[n_types].type = type; mime_types[n_types].suffix = suffix; n_types++; } c = end; } mime_types[n_types].type = "image/jpeg"; mime_types[n_types].suffix = "jpeg"; n_types++; mime_types[n_types].type = "image/jpeg"; mime_types[n_types].suffix = "jpg"; n_types++; mime_types[n_types].type = "image/gif"; mime_types[n_types].suffix = "gif"; n_types++; mime_types[n_types].type = "image/png"; mime_types[n_types].suffix = "png"; n_types++; mime_types[n_types].type = "application/postscript"; mime_types[n_types].suffix = "ps"; n_types++; mime_types = (MimeType *)XtRealloc((char *)mime_types, n_types * sizeof mime_types[0]); } static const char *get_mime_type(const char *suffix) { long n; if (n_types < 0) load_mime_types(); for (n = 0 ; n < n_types ; n++) if (case_lstrcmp(suffix, mime_types[n].suffix) == 0) return mime_types[n].type; return NULL; } ./knews-1.0b.1/src/charset.c100644 1244 1244 24664 6455455546 14260 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include "global.h" #include "charset.h" #include "decode.h" #include "util.h" static const XChar2b ascii_to_big5_table[] = { #include "big5.h" }; #define ESC 27 #define SO 14 #define SI 15 /* * Generic XChar2b buffer handling. */ typedef struct { XChar2b *wstr; long len; } WideCharBuf; #define REALLOC_BUF(buf, new_len) \ (buf).wstr = (XChar2b *)XtRealloc((char *)(buf).wstr, \ ((buf).len = new_len) * \ sizeof (buf).wstr[0]) typedef struct { WideCharBuf buf; unsigned long pad[4]; } GenericContext; static void *gen_init(void) { GenericContext *cont; cont = (GenericContext *)XtMalloc(sizeof *cont); memset(cont, 0, sizeof *cont); cont->buf.len = 256; cont->buf.wstr = (XChar2b *)XtMalloc(cont->buf.len * sizeof cont->buf.wstr[0]); return cont; } static void gen_end(void *gcont) { GenericContext *cont = gcont; XtFree((char *)cont->buf.wstr); cont->buf.wstr = NULL; cont->buf.len = 0; XtFree((char *)cont); } /* * Unicode-1-1-UTF-7 encoding, rfc 1642. */ enum { Utf7Ascii = 0, Utf7EncWord, Utf7ReadPlus } Utf7State; typedef struct { WideCharBuf buf; unsigned long data; unsigned int n; unsigned char state; } Utf7Context; static XChar2b pull_wide_char(Utf7Context *cont) { XChar2b ret; ret.byte1 = (cont->data >> (cont->n - 8)) & 0xff; ret.byte2 = (cont->data >> (cont->n - 16)) & 0xff; cont->n -= 16; return ret; } static long utf7_decode(void *gcont, char *src, long len, int is_lf_term, XChar2b **wstr) { Utf7Context *cont = gcont; WideCharBuf *buf = &cont->buf; long pos = 0; int tmp; if (len + 8 > buf->len) REALLOC_BUF(*buf, len + 8); while (len-- > 0) { switch (cont->state) { case Utf7Ascii: if (*src == '+') cont->state = Utf7ReadPlus; else { buf->wstr[pos].byte1 = 0; buf->wstr[pos].byte2 = *src; pos++; } break; case Utf7EncWord: tmp = inv_base64_alpha[(unsigned char)*src]; if (tmp < 0) { if (*src == '-') { len--; src++; } while (cont->n >= 16) buf->wstr[pos++] = pull_wide_char(cont); cont->state = Utf7Ascii; } else { cont->data <<= 6; cont->data |= tmp; cont->n += 6; if (cont->n >= 16) buf->wstr[pos++] = pull_wide_char(cont); } break; case Utf7ReadPlus: if (*src == '-') { buf->wstr[pos].byte1 = 0; buf->wstr[pos].byte2 = '+'; pos++; } else { cont->state = Utf7EncWord; cont->data = 0; cont->n = 0; } break; } src++; } if (is_lf_term) { if (cont->state == Utf7EncWord) while (cont->n >= 16) buf->wstr[pos++] = pull_wide_char(cont); cont->state = Utf7Ascii; cont->data = 0; cont->n = 0; } *wstr = cont->buf.wstr; return pos; } /* * Straight 16 bit, network byte order. (Don't know if it exists...) */ typedef struct { WideCharBuf buf; unsigned char push_back; unsigned char pb_ok; } Str16Context; static long str16_decode(void *gcont, char *src, long len, int is_lf_term, XChar2b **wstr) { Str16Context *cont = gcont; WideCharBuf *buf = &cont->buf; long pos = 0; if (len == 0) { if (is_lf_term) cont->pb_ok = False; } else { if (len + 8 > buf->len) REALLOC_BUF(*buf, len + 8); if (cont->pb_ok) { buf->wstr[0].byte1 = cont->push_back; buf->wstr[0].byte2 = *src++; pos++; len--; cont->pb_ok = False; } while (len >= 2) { buf->wstr[pos].byte1 = *src++; buf->wstr[pos].byte2 = *src++; pos++; len -= 2; } if (len == 0 || is_lf_term) cont->pb_ok = False; else { cont->push_back = *src; cont->pb_ok = True; } } *wstr = buf->wstr; return pos; } /* * Hz-gb-2312 encoding, rfc 1842. */ typedef struct { WideCharBuf buf; unsigned char not_in_ascii; unsigned char read_tilde; unsigned char push_back; unsigned char pb_ok; } HzGbContext; /* * This is pure guessing. */ static XChar2b ascii_to_gb(unsigned char ch) { XChar2b ret; if (ch == ' ') { ret.byte1 = 0x21; ret.byte2 = 0x21; } else { ret.byte1 = 0x23; if (0x21u <= ch && ch <= 0x7eu) ret.byte2 = ch; else ret.byte2 = '@'; } return ret; } static XChar2b bytes_to_gb(unsigned char ch1, unsigned char ch2) { XChar2b ret; if (ch1 < 0x21u || 0x77u < ch1 || ch2 < 0x21u || 0x7eu < ch2 ) return ascii_to_gb('@'); ret.byte1 = ch1; ret.byte2 = ch2; return ret; } static long hzgb_decode(void *gcont, char *src, long len, int is_lf_term, XChar2b **wstr) { HzGbContext *cont = gcont; WideCharBuf *buf = &cont->buf; long pos = 0; if (len + 8 > buf->len) REALLOC_BUF(*buf, len + 8); while (len-- > 0) { if (!cont->not_in_ascii) if (cont->read_tilde) { if (*src == '{') cont->not_in_ascii = True; else if (*src == '~') buf->wstr[pos++] = ascii_to_gb('~'); else buf->wstr[pos++] = ascii_to_gb('@'); cont->read_tilde = False; } else if (*src == '~') cont->read_tilde = True; else buf->wstr[pos++] = ascii_to_gb(*src); else if (cont->read_tilde) { if (*src == '}') cont->not_in_ascii = False; else buf->wstr[pos++] = ascii_to_gb('@'); cont->read_tilde = False; } else if (cont->pb_ok) { buf->wstr[pos++] = bytes_to_gb(cont->push_back, *src); cont->pb_ok = False; } else if (*src == '~') cont->read_tilde = True; else { cont->push_back = *src; cont->pb_ok = True; } src++; } if (is_lf_term) { if (cont->read_tilde) buf->wstr[pos++] = ascii_to_gb('@'); cont->read_tilde = False; cont->not_in_ascii = False; } *wstr = buf->wstr; return pos; } /* * KSC 5601, rfc 1557. */ typedef struct { WideCharBuf buf; unsigned char n_escs; unsigned char not_in_ascii; unsigned char push_back; unsigned char pb_ok; } KscContext; static XChar2b ascii_to_ksc(unsigned char ch) { XChar2b ret; if (ch == ' ') { ret.byte1 = 0x21; ret.byte2 = 0x21; } else { ret.byte1 = 0x23; if (0x21u <= ch && ch <= 0x7eu) ret.byte2 = ch; else ret.byte2 = '@'; } return ret; } static XChar2b bytes_to_ksc(unsigned char ch1, unsigned char ch2) { XChar2b ret; if (ch1 < 0x21u || 0x7eu < ch1 || ch2 < 0x21u || 0x7eu < ch2) return ascii_to_ksc('@'); ret.byte1 = ch1; ret.byte2 = ch2; return ret; } static long ksc_decode(void *gcont, char *src, long len, int is_lf_term, XChar2b **wstr) { KscContext *cont = gcont; WideCharBuf *buf = &cont->buf; long pos = 0; if (len + 8 > buf->len) REALLOC_BUF(*buf, len + 8); /* assume we're at beginning of line... */ if (cont->n_escs != 4) { static const char escbuf[] = {ESC, '$', ')', 'C'}; const char *escs = escbuf; unsigned int n; n = cont->n_escs; escs += n; while (len > 0 && n < 4) if (*src != *escs) break; else { src++; escs++; len--; n++; } if (n == 4) cont->n_escs = 4; else if (len == 0 && !is_lf_term) { cont->n_escs = n; *wstr = buf->wstr; return 0; } else { len += n; src -= n; cont->n_escs = 0; } } while (len-- > 0) { if (!cont->not_in_ascii) { if (*src == SO && cont->n_escs == 4) cont->not_in_ascii = True; else buf->wstr[pos++] = ascii_to_ksc(*src); } else if (*src == SI) { if (cont->pb_ok) { cont->pb_ok = False; buf->wstr[pos++] = ascii_to_ksc('@'); } cont->not_in_ascii = False; } else if (cont->pb_ok) { buf->wstr[pos++] = bytes_to_ksc(cont->push_back, *src); cont->pb_ok = False; } else { cont->push_back = *src; cont->pb_ok = True; } src++; } if (is_lf_term) { if (cont->not_in_ascii && cont->pb_ok) buf->wstr[pos++] = ascii_to_ksc('@'); cont->not_in_ascii = False; cont->pb_ok = False; } *wstr = buf->wstr; return pos; } /* * Big5 encoding. Pure guesswork. */ typedef struct { WideCharBuf buf; unsigned char push_back; unsigned char pb_ok; } Big5Context; static XChar2b ascii_to_big5(unsigned char ch) { ch -= ' '; if (ch >= (unsigned char)(127 - ' ')) ch = '@' - ' '; return ascii_to_big5_table[ch]; } static XChar2b bytes_to_big5(unsigned char ch1, unsigned char ch2) { XChar2b ret; if (ch1 < 0xa1u || 0xf9u < ch1 || ch2 < 0x40u || 0xfeu < ch2) return ascii_to_big5('@'); ret.byte1 = ch1; ret.byte2 = ch2; return ret; } static long big5_decode(void *gcont, char *src, long len, int is_lf_term, XChar2b **wstr) { Big5Context *cont = gcont; WideCharBuf *buf = &cont->buf; long pos = 0; if (len + 8 > buf->len) REALLOC_BUF(*buf, len + 8); if (len > 0 && cont->pb_ok) { buf->wstr[pos++] = bytes_to_big5(cont->push_back, *src); src++; len--; cont->pb_ok = False; } while (len-- > 0) { if ((unsigned char)*src < 128) buf->wstr[pos++] = ascii_to_big5(*src); else if (len > 0) { buf->wstr[pos++] = bytes_to_big5(src[0], src[1]); src++; len--; } else { cont->push_back = *src; cont->pb_ok = True; } src++; } if (is_lf_term && cont->pb_ok) { cont->pb_ok = False; buf->wstr[pos++] = ascii_to_big5('@'); } *wstr = buf->wstr; return pos; } /*********************************************************************/ static const DecodeFuncs err_funcs = {0, }; static const DecodeFuncs str16_funcs = {gen_init, str16_decode, gen_end}; static const DecodeFuncs utf7_funcs = {gen_init, utf7_decode, gen_end}; static const DecodeFuncs hzgb_funcs = {gen_init, hzgb_decode, gen_end}; static const DecodeFuncs ksc_funcs = {gen_init, ksc_decode, gen_end}; static const DecodeFuncs big5_funcs = {gen_init, big5_decode, gen_end}; const DecodeFuncs *get_decode_funcs(char *encoding) { if (!encoding) return NULL; else if (case_lstrcmp(encoding, "utf-7") == 0 || case_lstrcmp(encoding, "rfc-1642") == 0) return &utf7_funcs; else if (case_lstrcmp(encoding, "16-bit") == 0) return &str16_funcs; else if (case_lstrcmp(encoding, "hz-gb-2312") == 0 || case_lstrcmp(encoding, "rfc-1842") == 0) return &hzgb_funcs; else if (case_lstrcmp(encoding, "ksc-5601") == 0 || case_lstrcmp(encoding, "iso-2022-kr") == 0 || case_lstrcmp(encoding, "rfc-1557") == 0) return &ksc_funcs; else if (case_lstrcmp(encoding, "big5") == 0) return &big5_funcs; else if (case_lstrcmp(encoding, "none") == 0 || case_lstrcmp(encoding, "8-bit") == 0) return NULL; else return &err_funcs; } ./knews-1.0b.1/src/partial.c100644 1244 1244 27615 6455455546 14262 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include "global.h" #include "cache.h" #include "codes.h" #include "connect.h" #include "file.h" #include "font.h" #include "parse.h" #include "partial.h" #include "read.h" #include "resource.h" #include "server.h" #include "tag.h" #include "thread.h" #include "util.h" #include "widgets.h" #include "xutil.h" #include "../Widgets/ArtText.h" #include "../Widgets/Notice.h" #include "../Widgets/Scrollable.h" #define PARTIAL_HASH_SIZE 23 typedef struct PCE PCE; struct PCE { PCE *next; char *id; int tot; int n_alloc; long *arts; Widget notice; }; static PCE *cache[PARTIAL_HASH_SIZE] = {0, }; void partial_clear_cache(void) { PCE *loop, *next; int i; for (i = 0 ; i < PARTIAL_HASH_SIZE ; i++) { for (loop = cache[i] ; loop ; loop = next) { next = loop->next; loop->next = NULL; if (loop->notice) { XtPopdown(loop->notice); XtDestroyWidget(loop->notice); loop->notice = NULL; } XtFree(loop->id); XtFree((char *)loop->arts); XtFree((char *)loop); } cache[i] = NULL; } } static void get_cache_stats(long *n_complete, long *n_fragmented) { long n_comp = 0, n_frag = 0; PCE *loop; int i, j; for (i = 0 ; i < PARTIAL_HASH_SIZE ; i++) for (loop = cache[i] ; loop ; loop = loop->next) if (loop->tot <= 0) n_frag++; else { for (j = loop->tot ; j > 0 ; j--) if (loop->arts[j] <= 0) { n_frag++; break; } if (j == 0) n_comp++; } *n_complete = n_comp; *n_fragmented = n_frag; } static unsigned int hash(char *c) { unsigned int result = 0; while (*c != '\0') result += (unsigned char)*c++; return result % PARTIAL_HASH_SIZE; } static PCE **partial_cache_find(char *id) { PCE **cp = cache + hash(id); while (*cp && strcmp((*cp)->id, id) != 0) cp = &(*cp)->next; return cp; } static void remove_cache_entry(PCE *c) { PCE **cp = partial_cache_find(c->id); if (*cp != c) return; *cp = c->next; c->next = NULL; XtFree(c->id); c->id = NULL; XtFree((char *)c->arts); c->arts = NULL; XtFree((char *)c); } static void partial_dialogue_callback(Widget, XtPointer, XtPointer); static void add_to_arts(PCE *c, int num, int no) { int i; if (num >= c->n_alloc) { i = c->n_alloc; if (c->tot > 0) c->n_alloc = c->tot + 1; else c->n_alloc = num + 1; c->arts = (long *)XtRealloc((char *)c->arts, c->n_alloc * sizeof c->arts[0]); while (i < c->n_alloc) c->arts[i++] = 0; } if (c->arts[num] > 0) { if (c->arts[num] != no) fprintf(stderr, "knews: duplicate part %d in " "message/partial with id=\"%s\".\n", num, c->id); return; } c->arts[num] = no; } static void create_cache_notice(PCE *c) { char message[512]; ARTICLE *art; art = get_articles(main_thr); while (art && art->no < c->arts[1]) art = art->next; if (art->no != c->arts[1]) art = NULL; if (!art || strlen(art->subject->subject) > 200) strcpy(message, "Have all parts of message/partial."); else { if (!art->from || strlen(art->from) > 200) sprintf(message, "Have all parts of message/partial article.\n\n" "Subject: %s", art->subject->subject); else sprintf(message, "Have all parts of message/partial article.\n\n" "From: %s\n\n" "Subject: %s", art->from, art->subject->subject); } c->notice = popup_notice("partialnotice", message, "Assemble", NULL, "Forget", 0, partial_dialogue_callback, (XtPointer)c, XtGrabNone); } static void add_to_cache(long no, char *id, int num, int tot, int notify) { PCE **cp, *c; int i; cp = partial_cache_find(id); c = *cp; if (c) { if (tot > 0) if (c->tot <= 0) c->tot = tot; else if (tot != c->tot) { fputs("knews: message/partial with conflicting " "'total' parameters, ignoring last one.\n", stderr); return; } if (c->tot > 0 && num > c->tot) { fputs("knews: message/partial with conflicting " "'total' and 'number' parameters, ignoring.\n", stderr); return; } } else { c = *cp = (PCE *)XtMalloc(sizeof **cp); c->next = NULL; c->arts = NULL; c->n_alloc = 0; c->id = XtNewString(id); c->tot = tot; c->notice = NULL; } add_to_arts(c, num, no); if (!notify || c->tot <= 0 || c->notice) return; for (i = 1 ; i <= c->tot ; i++) if (c->arts[i] <= 0) return; create_cache_notice(c); } void partial_cache_hook(long no, struct MimeArg *args, int notify) { char *id = NULL; char *total = NULL; char *number = NULL; int num, tot = 0; while (args->value) { switch (args->name[0]) { case 'i': if (strcmp(args->name, "id") == 0) id = args->value; break; case 'n': if (strcmp(args->name, "number") == 0) number = args->value; break; case 't': if (strcmp(args->name, "total") == 0) total = args->value; break; } args++; } if (!id) fputs("knews: message/partial without " "'id' parameter, ignoring.\n", stderr); else if (!number) fputs("knews: message/partial without " "'number' parameter, ignoring.\n", stderr); else if (sscanf(number, "%d", &num) != 1 || num <= 0) fputs("knews: message/partial with bad " "'number' parameter, ignoring.\n", stderr); else if (total && (sscanf(total, "%d", &tot) != 1 || tot <= 0)) fputs("knews: massage/partial with bad " "'total' parameter, ignoring.\n", stderr); else add_to_cache(no, id, num, tot, notify); } static int is_mime_header(char *header) { unsigned char c = tolower((unsigned char)*header++); switch (c) { case 'c': return case_lstrncmp(header, "ontent-", 7) == 0; case 'e': return case_lstrncmp(header, "ncrypted:", 9) == 0; case 'm': return (case_lstrncmp(header, "ime-version:", 12) == 0 || case_lstrncmp(header, "essage-id:", 10) == 0); } return False; } static char *dump_art(SERVER *server, FILE *fp, int is_first) { char *reply; int doit = is_first; reply = server_read(server); while (reply && reply[0] != '\0' && !IS_DOT(reply)) { if (!IS_SPACE(reply[0])) doit = is_first && !is_mime_header(reply); if (doit) fprintf(fp, "%s\r\n", reply); reply = server_read(server); } if (!reply || IS_DOT(reply)) return reply; if (is_first) { doit = True; reply = server_read(server); while (reply && reply[0] != '\0' && !IS_DOT(reply)) { if (!IS_SPACE(reply[0])) doit = is_mime_header(reply); if (doit) fprintf(fp, "%s\r\n", reply); reply = server_read(server); } if (!reply || IS_DOT(reply)) return reply; fputs("\r\n", fp); } reply = server_read(server); while (reply && !IS_DOT(reply)) { fprintf(fp, "%s\r\n", reply); reply = server_read(server); } return reply; } static char *snarf_articles(FILE *fp, long *arts, int n) { int i; for (i = 0 ; i < n ; i++) { SERVER *server; char *reply; server = cache_get_server(arts[i], False); if (!server) { char command[128]; server = main_server; sprintf(command, "ARTICLE %ld\r\n", arts[i]); reply = server_comm(server, command, True); if (!reply || atoi(reply) != NNTP_OK_ARTICLE) return reply; } reply = dump_art(server, fp, i == 0); if (server != main_server) server_free(server); else if (!reply) return NULL; } fputs(".\r\n", fp); return CODE_TO_STR(NNTP_OK_ARTICLE); } static void partial_dialogue_callback(Widget w, XtPointer client_data, XtPointer call_data) { NoticeReply reply = (NoticeReply)call_data; PCE *c = (PCE *)client_data; if (global.busy || (global.mode != NewsModeThread && global.mode != NewsModeGroup)) return; if (reply == NoticeReplyLeft) { SERVER *server; char *buffer; FILE *fp; fp = create_temp_file(&buffer); if (!fp) { set_message("Failed to create temp file!", True); return; } unlink(buffer); set_busy(True); buffer = snarf_articles(fp, c->arts + 1, c->tot); if (!buffer) { fclose(fp); reconnect_server(True); unset_busy(); return; } unset_busy(); if (atoi(buffer) != NNTP_OK_ARTICLE) { char message[256]; if (strlen(buffer) > 128) buffer[128] = '\0'; sprintf(message, "Couldn't get all parts, message from server is: %s", buffer); set_message(message, True); fclose(fp); return; } if (fflush(fp) != 0 || fseek(fp, SEEK_SET, 0) != 0) { perror("knews: fseek"); set_message("Error: couldn't rewind temp file!", True); fclose(fp); return; } set_curr_art(NULL, False); server = server_create(fileno(fp)); buffer = server_read(server); if (!res_full_header()) buffer = do_mime(NULL, server, buffer, False, NULL, 0, NULL); else { XFontStruct *font = ascii_font->header_font; Pixel pixel = global.header_pixel; ArtTextClearLines(main_widgets.text); ScrollableSuspend(main_widgets.text); while (buffer && !IS_DOT(buffer)) { ArtTextAddLine(main_widgets.text, buffer, font, pixel); if (buffer[0] == '\0') { font = ascii_font->body_font; pixel = global.pixel; } buffer = server_read(server); } ScrollableResume(main_widgets.text); } if (!buffer) set_message("Error with temp file!", True); else set_standard_message(); fclose(fp); server_set_fd(server, -1); server_free(server); } XtPopdown(c->notice); XtDestroyWidget(c->notice); c->notice = NULL; remove_cache_entry(c); } #define N_ARGS 8 static char *build_cache(ARTICLE **arts, long n_arts) { char message[128], *msg_end; MimeArg args[N_ARGS + 1]; char type[128], subtype[128]; char *headers[2]; char command[256]; char *reply = "", *c, *p; long i, j; strcpy(message, "Building message/partial cache... "); msg_end = message + strlen(message); for (i = 0 ; i < n_arts ; i++) { sprintf(msg_end, "%ld/%ld", i, n_arts); set_message(message, False); if (!arts[i]->from) continue; sprintf(command, "HEAD %ld\r\n", arts[i]->no); reply = server_comm(main_server, command, True); if (!reply) break; if (atoi(reply) != NNTP_OK_HEAD) continue; reply = server_read_chunk(main_server); if (!reply) break; c = reply; while (case_lstrncmp(c, "content-type:", 13) != 0) { c = strchr(c, '\n'); if (c) c++; else break; } if (!c) continue; for (p = strchr(c, '\n') ; p ; p = strchr(p, '\n')) if (IS_SPACE(p[1])) { if (p > c && p[-1] == '\r') p[-1] = ' '; *p++ = ' '; } else { p[0] = '\0'; break; } for (j = 0 ; j < N_ARGS + 1 ; j++) args[j].name = args[j].value = NULL; headers[0] = c; headers[1] = NULL; if (parse_content_type(headers, type, sizeof type, subtype, sizeof subtype, args, N_ARGS, False)) { if (strcmp(type, "message") == 0 && strcmp(subtype, "partial") == 0) partial_cache_hook(arts[i]->no, args, True); for (j = 0 ; j < N_ARGS ; j++) { XtFree(args[j].name); XtFree(args[j].value); args[j].name = args[j].value = NULL; } } } if (!reply) return NULL; return CODE_TO_STR(NNTP_OK_HEAD); } void partial_build_cache(void) { char message[256]; ARTICLE **arts = get_tagged_articles(); long n_arts = no_tagged_articles(); char *reply; if (!arts || n_arts <= 0) { set_message("No tagged articles!", True); return; } set_busy(True); reply = build_cache(arts, n_arts); if (!reply) { reconnect_server(True); unset_busy(); partial_clear_cache(); } unset_busy(); if (atoi(reply) == NNTP_OK_HEAD) { long n_complete, n_fragmented; get_cache_stats(&n_complete, &n_fragmented); sprintf(message, "Cache built: %ld complete articles, " "%ld partially complete.", n_complete, n_fragmented); set_message(message, False); clear_tagged_articles(); } else { if (strlen(reply) > 200) reply[200] = '\0'; sprintf(message, "Error, message from server is: %s", reply); set_message(message, True); } } ./knews-1.0b.1/src/partial.h100644 1244 1244 327 6455455546 14216 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ struct MimeArg; extern void partial_build_cache(void); extern void partial_clear_cache(void); extern void partial_cache_hook(long, struct MimeArg*, int); ./knews-1.0b.1/src/uudecode.c100644 1244 1244 20745 6455455546 14420 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include "global.h" #include "child.h" #include "connect.h" #include "file.h" #include "resource.h" #include "save.h" #include "server.h" #include "util.h" #include "uudecode.h" #include "widgets.h" #include "xutil.h" #include "../Widgets/ArtTree.h" #define UU_REGEXP \ "([^ \t]+)" /* 1: the file name */ \ "([[({ \t]|part|-)*" /* 2: skip garbage */ \ "([0-9]+)" /* 3: which part */ \ "[ \t]*(/|of)[ \t]*" /* 4: skip separator & garbage */ \ "([0-9]+)" /* 5: number of parts */ #define UU_NAME_OFFSET 1 #define UU_PART_OFFSET 3 #define UU_NOPARTS_OFFSET 5 #define UU_NO_OFFSETS 6 static int uu_parse_subject(char *subject, char **file_name, int *no_parts, int *part) { static regex_t re; static int inited = False; regmatch_t pmatch[UU_NO_OFFSETS]; if (!inited) { if (regcomp(&re, UU_REGEXP, REG_EXTENDED | REG_ICASE) != 0) { fputs("knews: internal error, failed to compile" " regexp to use for uudecoding.", stderr); regfree(&re); return False; } inited = True; } if (regexec(&re, subject, UU_NO_OFFSETS, pmatch, 0) != 0) return False; if (no_parts) { int i; *no_parts = 0; for (i = pmatch[UU_NOPARTS_OFFSET].rm_so ; i < pmatch[UU_NOPARTS_OFFSET].rm_eo ; i++) { *no_parts *= 10; *no_parts += subject[i] - '0'; } } if (part) { int i; *part = 0; for (i = pmatch[UU_PART_OFFSET].rm_so ; i < pmatch[UU_PART_OFFSET].rm_eo ; i++) { *part *= 10; *part += subject[i] - '0'; } } if (file_name) { int len; len = pmatch[UU_NAME_OFFSET].rm_eo - pmatch[UU_NAME_OFFSET].rm_so; *file_name = XtMalloc(len + 1); memcpy(*file_name, &subject[pmatch[UU_NAME_OFFSET].rm_so], len); (*file_name)[len] = '\0'; } return True; } static void perror_exit(const char *msg) { perror(msg); _exit(127); } static void uufilter(int in, int out) { char out_buf[16384]; SERVER *server; char *buffer; enum { UuStateSearching, UuStateDoing, UuStateSkipping } state = UuStateSearching; long n, pos; server = server_create(in); pos = 0; while ((buffer = server_read(server))) switch (state) { case UuStateSearching: if (strncmp(buffer, "BEGIN", 5) == 0) { state = UuStateDoing; break; } if (strncmp(buffer, "begin ", 6) == 0 && isdigit(buffer[6]) && isdigit(buffer[7]) && isdigit(buffer[8])) state = UuStateDoing; /* fall through */ else break; case UuStateDoing: if (strncmp(buffer, "END", 3) == 0) { state = UuStateSkipping; break; } n = strlen(buffer); if (n > sizeof out_buf / 2) { fputs("knews: buffer overflow\n", stderr); _exit(1); } if (n + pos + 1 > sizeof out_buf) { if (writen(out, out_buf, pos) < 0) _exit(1); pos = 0; } memcpy(out_buf + pos, buffer, n); pos += n; out_buf[pos++] = '\n'; break; case UuStateSkipping: if (strncmp(buffer, "BEGIN", 5) == 0) state = UuStateDoing; break; } if (writen(out, out_buf, pos) < 0) _exit(1); } static void uudecode_child(int in_fd) { int fd[2]; char *uu_dir = res_uu_dir(); char *uu_program = res_uu_program(); pid_t pid; if (uu_dir) chdir_mkdir(uu_dir); if (lseek(in_fd, SEEK_SET, 0) < 0) perror_exit("lseek"); if (uu_program) { if (in_fd != STDIN_FILENO) if (dup2(in_fd, STDIN_FILENO) == STDIN_FILENO) close(in_fd); else { perror("knews: dup2"); _exit(127); } execl(BIN_SH, "sh", "-c", uu_program, (char *)NULL); perror("knews: execl " BIN_SH); _exit(127); } if (pipe(fd) < 0) { perror("knews: pipe"); _exit(127); } pid = fork(); if (pid < 0) { perror("knews: fork"); _exit(127); } if (pid == 0) { close(fd[0]); uufilter(in_fd, fd[1]); _exit(0); } close(fd[1]); if (fd[0] != STDIN_FILENO) if (dup2(fd[0], STDIN_FILENO) == STDIN_FILENO) close(fd[0]); else { perror("knews: dup2"); _exit(127); } execlp("uudecode", "uudecode", (char *)0); perror("knews: execlp(\"uudecode\")"); _exit(127); } static int do_decode(ARTICLE **arts, int no_parts) { char message[128]; FILE *fp; char *file_name; pid_t pid; long result[2]; fp = create_temp_file(&file_name); if (!fp) { set_message("Failed to create temp file!", True); return False; } unlink(file_name); strcpy(message, "Saving to temp file... "); set_busy(True); if (save_to_file(fp, message, arts, no_parts, SAVE_BODY, result) < 0) { reconnect_server(True); unset_busy(); return False; } unset_busy(); if (result[0] < 0) { set_message("Error with temp file!", True); fclose(fp); return False; } pid = fork_nicely(XtNewString("uudecode"), pipe_context_callback, global.stderr_timeout >= 0); if (pid < 0) { fclose(fp); set_message("Failed to start 'uudecode' program!", True); return False; } if (pid == 0) uudecode_child(fileno(fp)); fclose(fp); /* Already flushed */ set_message("Started uudecode program.", False); return True; } void uudecode_callback(Widget w, XtPointer client_data, XtPointer call_data) { char *file_name = NULL; int is_uu, no_parts, part; ARTICLE **arts; SUBJECT *subj1, *subj2; if (global.busy || (global.mode != NewsModeGroup && global.mode != NewsModeThread)) return; if (global.curr_art) is_uu = uu_parse_subject(global.curr_art->subject->subject, &file_name, &no_parts, NULL); else if (global.curr_subj) is_uu = uu_parse_subject(global.curr_subj->subject, &file_name, &no_parts, NULL); else { set_message("No selected subject!", True); return; } if (!is_uu || no_parts <= 0 || !file_name) { XtFree(file_name); set_message("This doesn't appear to be a uuencoded article.", True); return; } arts = (ARTICLE **)XtMalloc((no_parts + 2) * sizeof(ARTICLE *)); for (part = 0 ; part < no_parts + 2 ; part++) arts[part] = NULL; #define FIND_PART(subj) \ if (strstr(subj->subject, file_name) && \ uu_parse_subject(subj->subject, NULL, NULL, &part)) { \ if (part > no_parts) { \ set_message("Error: I'm confused by that subject...", True); \ XtFree((char *)arts); \ XtFree(file_name); \ return; \ } \ if (part >= 0 && !arts[part]) { \ ARTICLE *art; \ for (art = subj->thread ; art ; \ art = next_in_thread_wrap(art)) \ if (art->from && art->subject == subj) { \ arts[part] = art; \ break; \ } \ } \ } subj1 = global.curr_subj; subj2 = subj1->prev; while (subj1 || subj2) { if (subj1) { FIND_PART(subj1); subj1 = subj1->next; } if (subj2) { FIND_PART(subj2); subj2 = subj2->prev; } } #undef FIND_PART XtFree(file_name); for (part = 1 ; part <= no_parts ; part++) if (!arts[part]) { XtFree((char *)arts); set_message("Error: Couldn't find all parts!", True); return; } if (do_decode(arts + 1, no_parts)) for (part = 0 ; part <= no_parts ; part++) { if (arts[part] && arts[part]->from && !arts[part]->read) { arts[part]->read = True; arts[part]->subject->no_unread--; global.curr_group->no_unread--; if (res_process_xrefs()) process_xref(arts[part]); if (arts[part]->pixmap != None) { global.n_hot--; update_subj_hot_value(arts[part]->subject); } update_subj_entry(arts[part]->subject); if (global.mode == NewsModeThread) { ArtTreeNodeSetInner(main_widgets.arttree, (ART_TREE_NODE *)arts[part], False); ArtTreeNodeSetPixmap(main_widgets.arttree, (ART_TREE_NODE *)arts[part], None); } } } XtFree((char *)arts); } void action_uudecode(Widget w, XEvent *event, String *params, Cardinal *no_params) { uudecode_callback(w, NULL, NULL); } ./knews-1.0b.1/src/newsrc.h100644 1244 1244 627 6570004710 14043 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ extern int get_newsgroups(void); extern int get_newsgroups_from_newsrc(void); extern int get_descriptions(void); extern char *rescan(void); extern int update_newsrc(void); extern int check_for_new_groups(void); extern void parse_newsrc(int); extern void sort_groups(void); extern GROUP *create_group(char*); extern GROUP *find_group(char*); ./knews-1.0b.1/src/sort.c100644 1244 1244 16644 6455455546 13615 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include "global.h" #include #include "parse.h" #include "resource.h" #include "sort.h" #include "thread.h" #include "util.h" typedef union { char *str; unsigned long n; } SORT_VAL; typedef struct { SUBJECT *first; SUBJECT *last; SORT_VAL val; } SORT_NODE; typedef int (*CompFunc)(const void*, const void*); typedef SORT_VAL (*SortValFunc)(const SORT_NODE*); typedef struct { CompFunc comp_func; SortValFunc val_func; } SortFuncs; static void sub_sort(SORT_NODE*); static SortFuncs get_sort_funcs(char*); void sort_threads(void) { SORT_NODE *sort; SUBJECT *subj; long n, i; char *sort_name; subj = get_subjects(main_thr); if (!subj) return; n = 0; while (subj) { ARTICLE *thr = subj->thread; n++; while (subj && subj->thread == thr) subj = subj->next; } sort = (SORT_NODE *)XtMalloc(n * sizeof sort[0]); subj = get_subjects(main_thr); i = 0; while (subj) { ARTICLE *thr = subj->thread; subj->disp = 0; sort[i].first = subj; while (subj->next && subj->next->thread == thr) { subj = subj->next; subj->disp = 0; } sort[i].last = subj; subj = subj->next; sort[i++].last->next = NULL; } for (i = 0 ; i < n ; i++) sub_sort(sort + i); sort_name = res_sort_threads(); if (sort_name && case_lstrcmp(sort_name, "none") != 0) { SortFuncs funcs = get_sort_funcs(sort_name); if (!funcs.comp_func) fprintf(stderr, "knews: unknown sort method: %s\n", sort_name); else { if (funcs.val_func) for (i = 0 ; i < n ; i++) sort[i].val = funcs.val_func(sort + i); qsort(sort, n, sizeof sort[0], funcs.comp_func); } } set_subjects(main_thr, sort[0].first); for (i = 0 ; i < n - 1 ; i++) sort[i].last->next = sort[i+1].first; sort[n-1].last->next = NULL; XtFree((char *)sort); subj = get_subjects(main_thr); subj->prev = NULL; subj->disp = -1; while (subj->next) { subj->next->prev = subj; subj = subj->next; subj->disp = -1; } } static void sub_sort(SORT_NODE *node) { SUBJECT *subj = node->first; SUBJECT *first = NULL, *last = subj; ARTICLE *art; long n = 1; for (art = subj->thread ; art ; art = next_in_thread_preorder(art)) if (art->from && art->subject->disp == 0 && !art->read) art->subject->disp = n++; for (art = subj->thread ; art ; art = next_in_thread_preorder(art)) if (art->from && art->subject->disp == 0) art->subject->disp = n++; while (n-- > 1) { SUBJECT *sn = subj; if (sn->disp == n) subj = subj->next; else { SUBJECT *tmp; while (sn->next->disp != n) sn = sn->next; tmp = sn->next; sn->next = sn->next->next; sn = tmp; } if (!first) last = sn; sn->next = first; first = sn; } node->first = first; node->last = last; } /*************************************************************************/ static SORT_VAL val_thrsize_unread(const SORT_NODE *node) { SUBJECT *subj = node->first; SORT_VAL val; val.n = subj->no_unread; while (subj->next && subj->next->thread == subj->thread) { subj = subj->next; val.n += subj->no_unread; } return val; } static SORT_VAL val_thrsize(const SORT_NODE *node) { SUBJECT *subj = node->first; SORT_VAL val; ARTICLE *art; val.n = 0; for (art = subj->thread ; art ; art = next_in_thread_preorder(art)) if (art->from) val.n++; return val; } static SORT_VAL val_avgdate(const SORT_NODE *node) { SUBJECT *subj = node->first; SORT_VAL val; long n = 0; ARTICLE *art; if (sizeof(long) * CHAR_BIT > 60) { /* ~ 64 bit longs */ val.n = 0; for (art = subj->thread ; art ; art = next_in_thread_preorder(art)) if (art->from && !art->read && art->date != PARSEDATE_ERROR) { val.n += art->date; n++; } if (n <= 0) n = 1; val.n /= n; } else { unsigned long upper = 0; unsigned long lower = 0; for (art = subj->thread ; art ; art = next_in_thread_preorder(art)) if (art->from && !art->read && art->date != PARSEDATE_ERROR) { unsigned long date = art->date; lower += date & 0xffff; date >>= 16; upper += date; n++; } if (n <= 0) n = 1; val.n = ((upper / n) << 16) + lower / n; } return val; } static SORT_VAL val_nhot(const SORT_NODE *node) { SUBJECT *subj = node->first; SORT_VAL val; ARTICLE *art; val.n = 0; for (art = subj->thread ; art ; art = next_in_thread_preorder(art)) if (art->from && !art->read && art->pixmap != None) val.n++; return val; } static SORT_VAL val_subject(const SORT_NODE *node) { SUBJECT *subj = node->first; SORT_VAL val; val.str = subj->subject; return val; } static ARTICLE *first_unread(ARTICLE*); static SORT_VAL val_author(const SORT_NODE *node) { SUBJECT *subj = node->first; SORT_VAL val; ARTICLE *art = first_unread(subj->thread); if (!art) val.str = ""; else { char *c = art->tree_data.label; if (res_show_number_lines()) { while ((unsigned)*c - '0' < 10) c++; while (*c == ' ') c++; } val.str = c; } return val; } static SORT_VAL val_date(const SORT_NODE *node) { SUBJECT *subj = node->first; SORT_VAL val; ARTICLE *art = first_unread(subj->thread); if (art) val.n = art->date; else val.n = PARSEDATE_ERROR; return val; } /*************************************************************************/ static int cmp_str(const void *v1, const void *v2) { const SORT_NODE *s1 = v1; const SORT_NODE *s2 = v2; return case_strcmp(s1->val.str, s2->val.str); } static int cmp_str_neg(const void *v1, const void *v2) { const SORT_NODE *s1 = v1; const SORT_NODE *s2 = v2; return - case_strcmp(s1->val.str, s2->val.str); } static int cmp_n(const void *v1, const void *v2) { const SORT_NODE *s1 = v1; const SORT_NODE *s2 = v2; if (s1->val.n < s2->val.n) return 1; if (s1->val.n > s2->val.n) return -1; return 0; } static int cmp_n_neg(const void *v1, const void *v2) { const SORT_NODE *s1 = v1; const SORT_NODE *s2 = v2; if (s1->val.n < s2->val.n) return -1; if (s1->val.n > s2->val.n) return 1; return 0; } /*************************************************************************/ static SortFuncs get_sort_funcs(char *sort) { static struct { const char *name; CompFunc pos; CompFunc neg; SortValFunc val; } funcs[] = { {"author", cmp_str, cmp_str_neg, val_author}, {"average-date", cmp_n_neg, cmp_n, val_avgdate}, {"date", cmp_n_neg, cmp_n, val_date}, {"full-size", cmp_n, cmp_n_neg, val_thrsize}, {"hot", cmp_n, cmp_n_neg, val_nhot}, {"size", cmp_n, cmp_n_neg, val_thrsize_unread}, {"subject", cmp_str, cmp_str_neg, val_subject}, }; SortFuncs res = {NULL, }; int i = XtNumber(funcs); int neg = False; if (*sort == '+') sort++; else if (*sort == '-') { sort++; neg = True; } while (i-- > 0) if (case_lstrcmp(sort, funcs[i].name) == 0) { res.comp_func = neg ? funcs[i].neg : funcs[i].pos; res.val_func = funcs[i].val; break; } return res; } static ARTICLE *first_unread(ARTICLE *thread) { ARTICLE *first = NULL; while (thread) { if (thread->from) { if (!thread->read) return thread; if (!first) first = thread; } thread = next_in_thread_preorder(thread); } return first; } ./knews-1.0b.1/src/Knews.ad100644 1244 1244 51043 6641706030 14027 0ustar kallekalle! If you're a mere user without root privilegies the easiest way to install ! this file is to rename it 'Knews', copy it to a directory, and set the ! environment variable XAPPLRESDIR to point to that directory. If you're ! root, 'make install' should do the trick. Note that XAPPLRESDIR won't ! work if you have XUSERFILESEARCHPATH set. ! ! Note: if your using R4, you'll have to change all 'baseTranslations' to ! 'translations'. Knews.knewsVersion: 1.0b.1 ! This overrides the NNTPSERVER environment variable (no need to set this) ! !Knews.nntpServer: your.nntp.server ! If you set this resource, knews will Do The Right Thing when a ! user first connects to this server, i.e. setting the newsrc file ! to ~/.newsrc instead of ~/.newsrc-%s. ! !Knews.configNntpServer: your.nntp.server ! If you set this, this will become the default postingAgent, ! which is written into the config file the first time the ! user connects to configNntpServer. ! !Knews.configPostingAgent: inews -h ! This is the command used to edit articles to be posted. ! %s is the filename, %i is the line number where editing starts; ! they are both optional. ! !Knews.editCommand: xterm -e $EDITOR +%i %s !Knews.editCommand: xemacs +%i %s !Knews.editCommand: xterm -e vi +%i %s ! ! You might need a few stty settings for vi: ! !Knews.editCommand: xterm -e /bin/sh -c 'stty ^D eof ; vi +%i %s' ! The command used for clicking on URLs. As a simple security ! measure, knews will not allow quotes, parentheses, white space ! or ampersands in the url. ! !Knews.urlCommand: netscape -remote 'openUrl(%s)' ! Command for printing. ! Knews.printCommand: /bin/false ! Whether knews should generate a Path header. ! !Knews.generatePath: False ! Used for mailcap viewers with the flags needsterminal and copiousoutput. ! Knews.needsTerminal: exec xterm -e /bin/sh -c '%C' Knews.copiousOutput: exec xterm -e /bin/sh -c '(%C) | less' ! Stuff for the misc menu on the post popup. ! %q expands to quoteString and %Q to quoteQuoteString. ! Knews.postMiscMenuSize: 3 *misc0.label: reload article *misc0.command: exec /bin/true %s *misc1.label: edit: vi *misc1.command: exec xterm -e vi %s *misc2.label: edit: xemacs *misc2.command: exec xemacs %s ! The type menu on the post popup. ! Knews.typeMenuSize: 8 *type0.label: text/plain; *type1.label: text/plain; charset=iso-8859-1 *type2.label: image/jpeg *type3.label: image/gif *type4.label: audio/basic *type5.label: video/mpeg *type6.label: application/octet-stream *type7.label: application/postscript ! The forward menu. ! !Knews.forwardMenuSize: 2 !*forward0.label: mail address 1 !*forward1.label: mail address 2 ! The proper way to set the geometry is to set the following resources, ! and let knews size itself according to the fonts: ! *grouplist*PreferredLines: 14 *text*PreferredLines: 32 *killist*PreferredLines: 8 *killist*preferredColumns: 48 *FileSel*PreferredColumns: 16 *PreferredColumns: 84 ! The layout of the article tree. The nodeRows and nodeColumns values ! are in characters, the others in pixels. ! !*ArtTree.vertical: True !*ArtTree.nodeColumns: 16 !*ArtTree.nodeRows: 1 !*ArtTree.rowSpacing: 6 !*ArtTree.columnSpacing: 32 !*ArtTree.pixmapSpacing: 8 !*ArtTree.internalWidth: 16 !*ArtTree.internalHeight: 8 !*ArtTree.internalNodeWidth: 4 !*ArtTree.internalNodeHeight: 0 ! Some layout parameters for the article etxt widget. ! !*text.wrapLines: True !*text.margin: 8 !*text.imageMargin: 16 ! Scrolling styles for the thread list and all other lists, respectively. ! I.e. whether the list should 'page scroll' and how many lines should ! be visible between the selected line and the top/bottom. ! *threadlist.marginUp: 1 *threadlist.marginDown: 1 *threadlist.pageUp: False *threadlist.pageDown: True *ScrList.marginUp: 1 *ScrList.marginDown: 1 *ScrList.pageUp: True *ScrList.pageDown: True ! This will make the scrollbars use Athena style mouse bindings. ! !*ScrBar.translations: #override \n\ ! : nop() \n\ ! : athena-scroll(up) \n\ ! : athena-scroll(down) ! Per charset fonts for 8 bit charsets: ! Knews.us-ascii.bodyFont: \ -b&h-lucidatypewriter-medium-r-normal-*-*-120-*-*-*-*-iso8859-1 Knews.us-ascii.quoteFont: \ -b&h-lucidatypewriter-bold-r-normal-*-*-120-*-*-*-*-iso8859-1 Knews.us-ascii.headerFont: \ -b&h-lucidatypewriter-bold-r-normal-*-*-120-*-*-*-*-iso8859-1 Knews.us-ascii.listFont: \ -b&h-lucidatypewriter-medium-r-normal-*-*-120-*-*-*-*-iso8859-1 Knews.us-ascii.treeFont: \ -b&h-lucidatypewriter-bold-r-normal-*-*-120-*-*-*-*-iso8859-1 Knews.iso-8859-1.bodyFont: \ -b&h-lucidatypewriter-medium-r-normal-*-*-120-*-*-*-*-iso8859-1 Knews.iso-8859-1.quoteFont: \ -b&h-lucidatypewriter-bold-r-normal-*-*-120-*-*-*-*-iso8859-1 Knews.iso-8859-1.headerFont: \ -b&h-lucidatypewriter-bold-r-normal-*-*-120-*-*-*-*-iso8859-1 Knews.iso-8859-2.bodyFont: \ -*-*-medium-r-*-*-*-*-*-*-*-*-iso8859-2 Knews.iso-8859-2.quoteFont: \ -*-*-bold-r-*-*-*-*-*-*-*-*-iso8859-2 Knews.iso-8859-2.headerFont: \ -*-*-bold-r-*-*-*-*-*-*-*-*-iso8859-2 Knews.koi8.bodyFont: \ -*-*-medium-r-*-*-*-*-*-*-*-*-koi8-r Knews.koi8.quoteFont: \ -*-*-bold-r-*-*-*-*-*-*-*-*-koi8-r Knews.koi8.headerFont: \ -*-*-bold-r-*-*-*-*-*-*-*-*-koi8-r Knews.koi8-r.bodyFont: \ -*-*-medium-r-*-*-*-*-*-*-*-*-koi8-r Knews.koi8-r.quoteFont: \ -*-*-bold-r-*-*-*-*-*-*-*-*-koi8-r Knews.koi8-r.headerFont: \ -*-*-bold-r-*-*-*-*-*-*-*-*-koi8-r ! Per charset fonts for encoded 16-bit charsets: highly experimental ! Knews.iso-2022-kr.bodyFont: \ -*-*-*-*-*-*-*-*-*-*-*-*-ksc5601* Knews.iso-2022-kr.encoding: ksc-5601 Knews.gb2312.bodyFont: \ -*-*-*-*-*-*-*-*-*-*-*-*-gb2312* Knews.gb2312.encoding: hz-gb-2312 Knews.hz-gb-2312.bodyFont: \ -*-*-*-*-*-*-*-*-*-*-*-*-gb2312* Knews.hz-gb-2312.encoding: hz-gb-2312 Knews.big5.bodyFont: \ -*-*-*-*-*-*-*-*-*-*-*-*-*big5* Knews.big5.encoding: big5 ! General fonts: ! *ArtTree*Font: -b&h-lucidatypewriter-medium-r-normal-*-*-120-*-*-*-*-iso8859-1 *ArtText*Font: -b&h-lucidatypewriter-medium-r-normal-*-*-120-*-*-*-*-iso8859-1 *ScrList*Font: -b&h-lucidatypewriter-medium-r-normal-*-*-120-*-*-*-*-iso8859-1 *posttext.font: -b&h-lucidatypewriter-bold-r-normal-*-*-120-*-*-*-*-iso8859-1 *TextField.font:-adobe-courier-medium-r-normal-*-*-120-*-*-*-*-iso8859-1 *Font: -b&h-lucidatypewriter-bold-r-normal-*-*-120-*-*-*-*-iso8859-1 ! If you change this to reflect your setup, you can use knews to read ! the spooldir simply by specifying the NNTPSERVER as '#spool'. (This ! is assuming you have knewsd in your path.) No need to bother with ! this if you have access to a real server. ! !Knews.#spool: knewsd -spool /var/spool/news \ ! -active /usr/local/news/active \ ! -newsgroups /usr/local/news/newsgroups \ ! -overview /var/spool/news/over.view \ ! -post 'exec inews -h' ! Some people want double clicking on a subject to go directly into thread ! mode. This will do it. ! !*threadlist.translations: #override \ ! (2): notify() view-thread(true) ! The initial directory of the filechooser. Make sure it exists ! !*FileSel.directory: News ! The save/pipe popup will start up with the following in the text fields: ! *shellfield.buffer: \ tmp=/tmp/.knews.$$; cat >$tmp; xterm -e less $tmp; rm -f $tmp *filefield.buffer: ~/News/%N ! The search popup will start with these in the text fields: ! *regexpfield.buffer: Default search regexp *headerfield.buffer: Content-Type *wildcardfield.buffer: *image/* ! These will make the key F1 in the 'shell-command' text field do the obvious ! thing. The same could be done for other function keys. ! *shellfield.translations: #override \n\ F1: beginning-of-line() kill() \ insert-string("You haven't configured the app-def file.") *filefield.translations: #override \n\ F1: beginning-of-line() kill() \ insert-string("You haven't configured the app-def file.") ! 'less'-like bindings for the article text scrollbar: ! *textvbar.accelerators: #override \n\ c p: abs-scroll(-13) \n\ c n: abs-scroll(13) \n\ c f: page-scroll(0.95) \n\ c b: page-scroll(-0.95) *textvbar.stepSize: 13 ! These will make the tab key act the same as the 'Read group'/'Next unread' ! push button and the delete/backspace act the same as 'Previous'/'Rescan': ! *knapp7.accelerators: #override \n\ ~s ~c Tab: set() notify() reset() \n\ ~c n: set() notify() reset() *knapp8.accelerators: #override \n\ ~s ~c Delete: set() notify() reset() \n\ ~s ~c BackSpace: set() notify() reset() ! Accelerators for the horizontal and vertical scrollbars for the tree. ! *tophbar.accelerators: #override \n\ c Left: abs-scroll(-32) \n\ c KP_4: abs-scroll(-32) \n\ c Right: abs-scroll(32) \n\ c KP_6: abs-scroll(32) *topvbar.accelerators: #override \n\ c Up: abs-scroll(-32) \n\ c KP_8: abs-scroll(-32) \n\ c Down: abs-scroll(32) \n\ c KP_2: abs-scroll(32) ! An example: this will make ^S popup the search popup ('knapp11' is the ! 'Search' button). ! *search.accelerators: #override \n\ c s: set() notify() reset() ! Here are a few experimental translations. ! ! ~s A means A without shift ! s A means shift-A ! ~c A means A without control ! c A means control-A ! A means A with or without shift and control ! ! The kill-append stuff is intended as an illustration; the ! colors could be more imaginative. ! Knews.baseTranslations: #override \n\ space: do-the-right-thing() \n\ ~c ~s Left: tree-left-or-exit-mode(read) \n\ ~c ~s KP_4: tree-left-or-exit-mode(read) \n\ ~c ~s Right: tree-right-or-enter-mode(read) \n\ ~c ~s KP_6: tree-right-or-enter-mode(read) \n\ ~c ~s Up: tree-or-list-up(read) \n\ ~c ~s KP_8: tree-or-list-up(read) \n\ ~c ~s Down: tree-or-list-down(read) \n\ ~c ~s KP_2: tree-or-list-down(read) \n\ c KP_5: read-article(full) \n\ ~c KP_5: read-article() \n\ KP_3: list-down(0.9) \n\ KP_9: list-up(0.9) \n\ ~c s Tab: goto-next-hot() \n\ ~c ~s g: popup-find-group() \n\ ~c l: view-thread(toggle) \n\ ~c f: followup() \n\ ~c r: reply() \n\ ~c p: post-new() \n\ ~c u: uudecode() \n\ ~c ~s |: pipe("tmp=/tmp/.knews.$$; cat >$tmp; \ xterm -e less $tmp; rm -f $tmp", heb, window) \n\ ~c ~s F1: kill-append(message-id, subthread, Red) \n\ ~c ~s F2: kill-append(message-id, subthread, Green) \n\ ~c ~s F3: kill-append(message-id, subthread, Blue) \n\ ~c ~s F4: kill-append(message-id, subthread, Yellow) \n\ ~c s F1: kill-append(message-id, thread, Red) \n\ ~c s F2: kill-append(message-id, thread, Green) \n\ ~c s F3: kill-append(message-id, thread, Blue) \n\ ~c s F4: kill-append(message-id, thread, Yellow) \n\ c ~s k: kill-append(message-id, subthread) \n\ c s k: kill-append(message-id, thread) \n\ c j: mime-hack(image/jpeg, uue) \n\ c g: mime-hack(image/gif, uue) \n\ c p: mime-hack(image/png, uue) \n\ ~s t: tree-layout(True, 16) \n\ s t: tree-layout(False, 6) ! Same for the second window, if it exists ! Knews.second.baseTranslations: #override \n\ space: do-the-right-thing() \n\ ~c ~s Left: tree-left-or-exit-mode(read) \n\ ~c ~s KP_4: tree-left-or-exit-mode(read) \n\ ~c ~s Right: tree-right-or-enter-mode(read) \n\ ~c ~s KP_6: tree-right-or-enter-mode(read) \n\ ~c ~s Up: tree-or-list-up(read) \n\ ~c ~s KP_8: tree-or-list-up(read) \n\ ~c ~s Down: tree-or-list-down(read) \n\ ~c ~s KP_2: tree-or-list-down(read) \n\ c KP_5: read-article(full) \n\ ~c KP_5: read-article() \n\ KP_3: list-down(0.9) \n\ KP_9: list-up(0.9) \n\ ~c s Tab: goto-next-hot() \n\ ~c ~s g: popup-find-group() \n\ ~c l: view-thread(toggle) \n\ ~c f: followup() \n\ ~c r: reply() \n\ ~c p: post-new() \n\ ~c u: uudecode() \n\ ~c ~s |: pipe("tmp=/tmp/.knews.$$; cat >$tmp; \ xterm -e less $tmp; rm -f $tmp", heb, window) \n\ ~c ~s F1: kill-append(message-id, subthread, Red) \n\ ~c ~s F2: kill-append(message-id, subthread, Green) \n\ ~c ~s F3: kill-append(message-id, subthread, Blue) \n\ ~c ~s F4: kill-append(message-id, subthread, Yellow) \n\ ~c s F1: kill-append(message-id, thread, Red) \n\ ~c s F2: kill-append(message-id, thread, Green) \n\ ~c s F3: kill-append(message-id, thread, Blue) \n\ ~c s F4: kill-append(message-id, thread, Yellow) \n\ c ~s k: kill-append(message-id, subthread) \n\ c s k: kill-append(message-id, thread) \n\ c j: mime-hack(image/jpeg, uue) \n\ c g: mime-hack(image/gif, uue) \n\ c p: mime-hack(image/png, uue) ! Some miscellaneous resources: ! *textvbar.allowOff: True *Foreground: Black *Background: Bisque *BorderColor: Black *rubberColor: Red *quoteColor: Medium Blue *headerColor: #000090 *innerColor: Red *clickableColor: #0000e0 *knapplayout.abort.Foreground: Red *alertColor: Red ! The highlight color of the lists, dynamically set to grey or white: !*ScrList.highlightColor: blah *ArtTree.useLineShadows: True *toplayout.skipAdjust: True *Knapp.Justify: center *MenuKnapp.Justify: center *quotetoggle.set: True *stderr*message.center: False *arttree.baseTranslations: #augment \n\ : set-selected() \n\ : toggle-outer() *grouplist.baseTranslations: #augment \n\ : dnd-start() \n\ : dnd-do() \n\ : dnd-end() \n\ : schedule-thread-ahead() *killist.baseTranslations: #augment \n\ : dnd-start() \n\ : dnd-do() \n\ : dnd-end() *threadlist.baseTranslations: #augment \n\ ~c : select() mark-read-thread() \n\ ~c : select() mark-read-thread() \n\ c : select() mark-unread-thread() \n\ : select() tag-thread() ! The toggles on the search popup. ! *searchshell*unreadtoggle.set: True *searchshell*fromtoggle.set: False *searchshell*subjecttoggle.set: True *searchshell*headtoggle.set: False *searchshell*bodytoggle.set: False *searchshell*allscope.set: True *searchshell*threadscope.set: False *searchshell*subthreadscope.set: False *searchshell*tagged.set: False ! The toggles on the save popup. ! *saveshell*artwin.set: False *saveshell*article.set: True *saveshell*subject.set: False *saveshell*thread.set: False *saveshell*subthread.set: False *saveshell*tagged.set: False *saveshell*bogusfrom.set: True *saveshell*bogussubj.set: False *saveshell*header.set: True *saveshell*body.set: True *saveshell*empty.set: True ! Various labels. ! *knapplayout.knapp0.label: Quit\nDone *knapplayout.knapp1.label: \ Connect...\nDisconnect\nView thread\nBack\nSubscribe *knapplayout.knapp2.label: All groups\nAll threads\nUnsubscribe *knapplayout.misc.label: Misc *knapplayout.post.label: Post *knapplayout.kill.label: Kill... *knapplayout.knapp6.label: Update\nCatchup *knapplayout.knapp7.label: Read Group\nNext unread\nGoto group *knapplayout.knapp8.label: Rescan\nPrevious *knapplayout.abort.label: Abort *knapplayout.save.label: Save... *knapplayout.search.label: Search... *close.label: Close *miscmenu1.print.label: Print *miscmenu1.fullheader.label: Full Header *miscmenu1.rot13.label: Rot 13 *miscmenu1.cleartags.label: Clear tags *miscmenu1.uudecode.label: Uudecode *miscmenu1.taghot.label: Tag hot articles *miscmenu1.addtompcache.label: Add To M/P cache *miscmenu1.msgidlookup.label: Message-Id Lookup *miscmenu1.showcache.label: Show Cache Stats *miscmenu1.markread.label: Mark Read *miscmenu1.markunread.label: Mark Unread *miscmenu1.headertoggle.label: Full Header *miscmenu1.thrinfo.label: Keep Thread Info *miscmenu1.greyjpegs.label: Grey Jpegs *miscmenu2.unsubscribe.label: Unsubscribe *miscmenu2.catchup.label: Catchup *miscmenu2.findgroup.label: Find Group *miscmenu2.askhowmany.label: Ask How Many *markreadmenu.article.label: Article *markreadmenu.subject.label: Subject *markreadmenu.thread.label: Thread *markreadmenu.subthread.label: Subthread *markreadmenu.tagged.label: Tagged *markreadmenu.allarticles.label: All articles *markreadmenu.tocurrent.label: To current *markreadmenu.nontagged.label: Non-tagged *markreadmenu.cold.label: Cold *markunreadmenu.article.label: Article *markunreadmenu.subject.label: Subject *markunreadmenu.thread.label: Thread *markunreadmenu.subthread.label: Subthread *markunreadmenu.tagged.label: Tagged *markunreadmenu.allarticles.label: All articles *markunreadmenu.killed.label: Killed *postmenu.followup.label: Post a followup *postmenu.mailreply.label: Reply by mail *postmenu.followupreply.label: Followup and reply *postmenu.postnew.label: Post a new article *postmenu.cancel.label: Cancel article *postmenu.supersede.label: Supersede article *postmenu.forward.label: Forward by mail *postmenu.quotetoggle.label: Include quoted text *postmenu.quotesig.label: Quote signature !*postmenu.quotesig.set: False *postshell*post.label: Post\nMail\nPost&Mail *killeditor.title: knews: kill file editor *killeditor.iconName: kill editor *killeditor*fieldknapp.label: Message-Id\nSubject\nFrom\nXref *killeditor*scopeknapp.label: Article\nSubject\nThread\nSubthread *killeditor*actionknapp.label: Kill\nHot *killeditor*exprknapp.label: Regexp:\nMessage-id: *killeditor*groupknapp.label: Newsgroup regexp: *killeditor*add.label: Add new *killeditor*delete.label: Delete *killeditor*clear.label: Clear *killeditor*stayup.label: Stay Up *fieldmenu.messageid.label: Message-Id *fieldmenu.subject.label: Subject *fieldmenu.from.label: From *fieldmenu.xref.label: Xref *scopemenu.article.label: Article *scopemenu.subject.label: Subject *scopemenu.thread.label: Thread *scopemenu.subthread.label: Subthread *actionmenu.kill.label: Kill *actionmenu.hot.label: Hot !*connect.buffer: some.nntp.server *fieldmessage.buffer: Header: *scopemessage.buffer: Scope: *colormessage.buffer: Color: *saveshell*message.buffer: Save to file or pipe to shell *saveshell*shellmessage.buffer: Shell-command: *saveshell*filemessage.buffer: File: *saveshell*choose.label: Choose... *saveshell*ok.label: Save\nPipe *saveshell*bogusfrom.label: Bogus 'From' *saveshell*bogussubj.label: Bogus 'Subject' *saveshell*header.label: Header *saveshell*body.label: Body *saveshell*empty.label: Empty Line *saveshell*artwin.label: Window *saveshell*article.label: Article *saveshell*subject.label: Subject *saveshell*thread.label: Thread *saveshell*subthread.label: Subthread *saveshell*tagged.label: Tagged *searchshell.title: knews: search *searchshell.iconName: search *searchshell*regexptitle.buffer: Regular expression search *searchshell*xpattitle.buffer: XPAT wildcard search *searchshell*regexpmessage.buffer: Regexp: *searchshell*wildcardmessage.buffer: Wildcard: *searchshell*headermessage.buffer: Header: *searchshell*search.label: Search *searchshell*submit.label: Submit *searchshell*next.label: Next *searchshell*first.label: First *searchshell*stop.label: Stop *searchshell*clear.label: Clear *searchshell*fromtoggle.label: From: header *searchshell*subjecttoggle.label: Subject: header *searchshell*headtoggle.label: Article head *searchshell*bodytoggle.label: Article body *searchshell*unreadtoggle.label: Only unread *searchshell*allscope.label: All articles *searchshell*threadscope.label: Thread *searchshell*subthreadscope.label: Subthread *searchshell*taggedscope.label: Tagged *postpopup*posttitle.buffer: Post/Mail Manager *postpopup*attachtitle.buffer: Attachments: *postpopup*typetitle.buffer: Content-Type: *postpopup*descrtitle.buffer: Content-Description: *postpopup*disptitle.buffer: Content-Disposition: *postpopup*enctitle.buffer: Content-Transfer-Encoding: *postpopup*nametitle.buffer: filename= *postpopup*post.label: Post\nMail\nPost&Mail *postpopup*edit.label: Edit *postpopup*misc.label: Misc *postpopup*attach.label: Attach... *postpopup*detach.label: Detach *postpopup*cancel.label: Cancel *postpopup*type.label: Type *postpopup*inlinetoggle.label: inline *postpopup*attachtoggle.label: attachment *postpopup*nonetoggle.label: None *postpopup*base64toggle.label: Base 64 *postpopup*uuetoggle.label: Uuencode *postpopup*qptoggle.label: Quoted-printable *postpopup*posttext.preferredLines: 8 *postpopup*posttext.preferredColumns: 64 *postpopup*attachlist.preferredLines: 3 *postpopup*attachlist.preferredColumns: 64 *FileSel*choose.label: Choose *FileSel*cancel.label: Cancel *FileSel*file.buffer: File: *FileSel*directory.buffer: Directory: *FileSel*dotfiles.label: Dot files ./knews-1.0b.1/src/viewer.c100644 1244 1244 13721 6625557270 14114 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include "global.h" #include "child.h" #include "file.h" #include "font.h" #include "gif.h" #include "jpeg.h" #include "mailcap.h" #include "png.h" #include "save.h" #include "viewer.h" #include "widgets.h" #include "xutil.h" #include "../Widgets/ArtText.h" #include "../Widgets/Dialogue.h" #include "../Widgets/TextField.h" typedef struct { char *data; Pixmap pixmap; long len; char *view_cmd; char *file_name; int ref_count; } VIEWER; static void viewer_destroy(VIEWER *v) { if (--v->ref_count > 0) return; XtFree(v->data); XtFree(v->view_cmd); XtFree(v->file_name); if (v->pixmap != None) { XFreePixmap(display, v->pixmap); v->pixmap = None; } v->data = NULL; v->len = 0; v->view_cmd = NULL; v->file_name = NULL; v->ref_count = 1; XtFree((char *)v); } static void mime_dialogue_callback(Widget w, XtPointer client_data, XtPointer call_data) { DialogueReport *report = (DialogueReport *)call_data; VIEWER *v = (VIEWER *)client_data; char *cmd; long n; int fd; pid_t pid; switch (report->reply) { case DialogueReplyLeft: /* save */ if (!report->buffer || report->buffer[0] == '\0') break; fd = open_expand(report->buffer, O_WRONLY|O_CREAT|O_TRUNC, True); if (fd < 0) { set_message("Error: Failed to open file!", True); return; } n = writen(fd, v->data, v->len); close(fd); if (n < 0) { set_message("Error: Failed to write file!", True); return; } set_message("Saved OK.", False); break; case DialogueReplyMiddle: /* pipe */ if (!report->buffer || report->buffer[0] == '\0') break; cmd = XtNewString(report->buffer); pid = fork_nicely(cmd, pipe_context_callback, global.stderr_timeout >= 0); if (pid < 0) { set_message("Error: fork failed!", True); XtFree(cmd); return; } if (pid == 0) { /* child */ char *file_name; fd = create_temp_fd(&file_name); if (fd < 0) { perror("knews: open"); _exit(127); } if (writen(fd, v->data, v->len) < 0) _exit(127); unlink(file_name); if (lseek(fd, SEEK_SET, 0) < 0) { perror("knews: lseek"); _exit(127); } if (fd != STDIN_FILENO) { if (dup2(fd, STDIN_FILENO) < 0) { perror("knews: dup2"); _exit(127); } close(fd); } execl(BIN_SH, "sh", "-c", report->buffer, (char *)NULL); perror("knews: execl " BIN_SH); _exit(127); } /* * Parent. */ set_message("Pipe started.", False); break; case DialogueReplyEnter: /* do nothing */ /* maybe we should default to either save or pipe ? */ return; /* don't fall through */ case DialogueReplyTab: return; /* don't fall through */ case DialogueReplyRight: case DialogueReplyClose: /* cancel*/ break; } viewer_destroy(v); XtDestroyWidget(w); } static void click_callback(Widget w, XtPointer client_data, XtPointer call_data) { VIEWER *v = (VIEWER *)client_data; int *button = (int *)call_data; char *cmd; pid_t pid; if (!button || *button <= 0) { viewer_destroy(v); return; } if (!v->view_cmd || *button == 3) { Widget w; v->ref_count++; w = popup_dialogue("mimedialogue", "Save to file or pipe to shell:", "Save", "Pipe", "Cancel", mime_dialogue_callback, (XtPointer)v, XtGrabNone); if (v->file_name) { w = DialogueGetTextField(w); TextFieldSetBuffer(w, v->file_name); } return; } cmd = XtNewString(v->view_cmd); pid = fork_nicely(cmd, pipe_context_callback, True); if (pid < 0) { perror("knews: fork"); set_message("Failed to start viewer!", True); return; } if (pid == 0) { /* child */ char *file_name; char *view_cmd = v->view_cmd; int fd; fd = create_temp_fd(&file_name); if (fd < 0) { perror("knews: open"); _exit(127); } if (writen(fd, v->data, v->len) < 0) _exit(127); if (strstr(v->view_cmd, "%s")) { char tmpl = 's'; view_cmd = expn_tmpl(v->view_cmd, 1, &tmpl, &file_name); } else { unlink(file_name); if (lseek(fd, SEEK_SET, 0) < 0) { perror("knews: lseek"); _exit(127); } if (fd != STDIN_FILENO) { if (dup2(fd, STDIN_FILENO) < 0) { perror("knews: dup2"); _exit(127); } close(fd); } } execl(BIN_SH, "sh", "-c", view_cmd, (char *)NULL); perror("knews: execl " BIN_SH); _exit(127); } /* parent */ set_message("Viewer started.", False); } void do_viewer(char *type, char *subtype, char *view_cmd, char *file_name, char *data, long len) { VIEWER *v; long w = 0, h = 0; v = (VIEWER *)XtMalloc(sizeof *v); v->pixmap = None; v->data = data; v->len = len; v->view_cmd = view_cmd; v->file_name = file_name; v->ref_count = 1; if (global.inline_images && strcmp(type, "image") == 0) if (strcmp(subtype, "jpeg") == 0) v->pixmap = do_jpeg(data, len, &w, &h); else if (strcmp(subtype, "gif") == 0) v->pixmap = do_gif(data, len, &w, &h); else if (strcmp(subtype, "png") == 0) v->pixmap = do_png(data, len, &w, &h); if (v->pixmap) ArtTextAddImage(main_widgets.text, v->pixmap, w, h, click_callback, v); else { if (!v->view_cmd) ArtTextAddClickable(main_widgets.text, "[knews: no mailcap entry.] Save or pipe.", ascii_font->header_font, global.clickable_pixel, click_callback, v); else { ArtTextAddClickable(main_widgets.text, "View with: ", ascii_font->header_font, global.clickable_pixel, click_callback, v); ArtTextAppendToLast(main_widgets.text, v->view_cmd); } ArtTextAddLine(main_widgets.text, "", ascii_font->body_font, global.pixel); } } void destroy_pixmap_callback(Widget w, XtPointer client_data, XtPointer call_data) { const int *button = (const int *)call_data; Pixmap pixmap = (Pixmap)client_data; if (button && !*button) XFreePixmap(display, (Pixmap)pixmap); } ./knews-1.0b.1/src/procs.c100644 1244 1244 43716 6570007653 13742 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include "global.h" #include "ahead.h" #include "cache.h" #include "codes.h" #include "connect.h" #include "ftp.h" #include "k_file.h" #include "newsrc.h" #include "partial.h" #include "read.h" #include "resource.h" #include "save.h" #include "search.h" #include "server.h" #include "tag.h" #include "thread.h" #include "util.h" #include "widgets.h" #include "xutil.h" #include "../Widgets/ArtText.h" #include "../Widgets/ArtTree.h" #include "../Widgets/Notice.h" #include "../Widgets/Sash.h" #include "../Widgets/ScrList.h" static int confirm_quit_group(void) { char *c = global.confirm_quit_group; if (!c) return False; if (case_lstrcmp(c, "true") == 0) return True; if (case_lstrcmp(c, "tagged") == 0) return no_tagged_articles() != 0; if (case_lstrcmp(c, "false") == 0) return True; return True; } static void leave_group(int catchup) { set_busy(False); popdown_save(); popdown_search(); partial_clear_cache(); clear_tagged_articles(); clear_history(); free_read_arts_list(global.curr_group); if (!catchup) global.curr_group->read_arts = create_read_arts_list(); else { ARTICLE *art; list_all_arts_read(global.curr_group); if (res_process_xrefs()) for (art = get_articles(main_thr) ; art ; art = art->next) if (!art->read) process_xref(art); } cache_leave_group(); thread_ahead_leave_group(global.curr_group); kill_exit_group(global.curr_group); global.curr_art = NULL; global.curr_subj = NULL; res_enter_group("none"); if (catchup || global.curr_group->subscribed) setNewsModeConnected(); else setNewsModeAllgroups(NULL); unset_busy(); if (global.mode == NewsModeConnected) check_if_rescan_due(); } static void confirm_callback(Widget w, XtPointer client_data, XtPointer call_data) { NoticeReply reply = (NoticeReply)call_data; int catchup = (int)client_data; unset_busy(); XtPopdown(w); XtDestroyWidget(w); if (reply != NoticeReplyLeft) return; leave_group(catchup); } static void do_confirm(char *msg, int catchup) { set_busy(False); popup_notice("confirm", msg, "Yes", "No", NULL, 0, confirm_callback, (XtPointer)catchup, XtGrabExclusive); } /* Quit Done */ void knapp0_callback(Widget gw, XtPointer client_data, XtPointer call_data) { if (global.busy) return; switch (global.mode) { case NewsModeDisconnected: /* Quit */ disconnect(True); break; case NewsModeConnected: /* Quit */ disconnect(True); break; case NewsModeGroup: /* Done */ if (confirm_quit_group()) do_confirm("Really exit group?", False); else leave_group(False); break; case NewsModeAllgroups: /* Done */ case NewsModeSomegroups: /* Done */ popdown_search(); setNewsModeConnected(); check_if_rescan_due(); break; case NewsModeNewgroups: /* Done */ XtFree((char *)global.new_groups); global.new_groups = NULL; global.no_new_groups = 0; sort_groups(); setNewsModeConnected(); check_if_rescan_due(); break; case NewsModeThread: break; } } /* Connect Disconnect Subscribe View Thread Back */ void knapp1_callback(Widget gw, XtPointer client_data, XtPointer call_data) { long i, j; switch (global.mode) { case NewsModeConnected: /* Disconnect */ if (global.busy) return; if (!ftp_put()) return; disconnect(False); break; case NewsModeDisconnected: /* Connect */ if (global.busy) return; set_standard_message(); popup_connect_dialogue(); break; case NewsModeThread: /* View Thread */ setNewsModeGroup(False /* not used */); break; case NewsModeGroup: /* Back */ if (global.curr_subj) setNewsModeThread(); break; case NewsModeAllgroups: /* Subscribe */ if (global.busy) return; i = ScrListGetFirstSelected(main_widgets.group_list); if (i < 0) { set_message("No selected groups!", True); return; } set_busy(False); do { global.groups[i]->subscribed = True; i = ScrListGetNextSelected(main_widgets.group_list, i); } while (i >= 0); sort_groups(); global.curr_group = NULL; setNewsModeAllgroups(NULL); set_standard_message(); unset_busy(); return; case NewsModeSomegroups: /* Subscribe */ if (global.busy) return; i = ScrListGetFirstSelected(main_widgets.group_list); if (i < 0) { set_message("No selected groups!", True); return; } set_busy(False); j = 0; do { for ( ; j < global.no_groups ; j++) if (global.groups[j]->disp == i) { global.groups[j]->subscribed = True; break; } i = ScrListGetNextSelected(main_widgets.group_list, i); } while (i >= 0); sort_groups(); global.curr_group = NULL; setNewsModeConnected(); set_standard_message(); unset_busy(); break; case NewsModeNewgroups: /* Subscribe */ if (global.busy) return; i = ScrListGetFirstSelected(main_widgets.group_list); if (i < 0) { set_message("No selected groups!", True); return; } set_busy(False); do { for (j = 0 ; j < global.no_groups ; j++) if (global.groups[j]->disp == i) { global.groups[j]->subscribed = True; break; } i = ScrListGetNextSelected(main_widgets.group_list, i); } while (i >= 0); setNewsModeNewgroups(); unset_busy(); break; } } /* Unsubscribe All threads All groups */ void knapp2_callback(Widget gw, XtPointer client_data, XtPointer call_data) { long i, j; SUBJECT *subj; if (global.busy) return; switch (global.mode) { case NewsModeConnected: /* All groups */ set_busy(False); global.curr_group = NULL; setNewsModeAllgroups(NULL); unset_busy(); break; case NewsModeGroup: /* All threads */ for (subj = get_subjects(main_thr) ; subj ; subj = subj->next) if (subj->disp < 0) break; set_curr_art(NULL, False); set_curr_subj(NULL); setNewsModeGroup(subj != NULL); set_standard_message(); break; case NewsModeAllgroups: /* Unsubscribe */ i = ScrListGetFirstSelected(main_widgets.group_list); if (i < 0) { set_message("No selected groups!", True); return; } set_busy(False); do { global.groups[i]->subscribed = False; i = ScrListGetNextSelected(main_widgets.group_list, i); } while (i >= 0); sort_groups(); global.curr_group = NULL; setNewsModeAllgroups(NULL); set_standard_message(); unset_busy(); break; case NewsModeSomegroups: /* Unsubscribe */ if (global.busy) return; i = ScrListGetFirstSelected(main_widgets.group_list); if (i < 0) { set_message("No selected groups!", True); return; } set_busy(False); j = 0; do { for ( ; j < global.no_groups ; j++) if (global.groups[j]->disp == i) { global.groups[j]->subscribed = True; break; } i = ScrListGetNextSelected(main_widgets.group_list, i); } while (i >= 0); sort_groups(); global.curr_group = NULL; setNewsModeConnected(); set_standard_message(); unset_busy(); break; case NewsModeNewgroups: /* Unsubscribe */ i = ScrListGetFirstSelected(main_widgets.group_list); if (i < 0) { set_message("No selected groups!", True); return; } set_busy(False); do { for (j = 0 ; j < global.no_groups ; j++) if (global.groups[j]->disp == i) { global.groups[j]->subscribed = False; break; } i = ScrListGetNextSelected(main_widgets.group_list, i); } while (i >= 0); setNewsModeNewgroups(); unset_busy(); break; case NewsModeDisconnected: case NewsModeThread: break; } } /* Kill... */ void knapp5_callback(Widget gw, XtPointer client_data, XtPointer call_data) { switch (global.mode) { case NewsModeDisconnected: break; case NewsModeConnected: case NewsModeAllgroups: case NewsModeSomegroups: case NewsModeNewgroups: kill_edit_popup(NULL); break; case NewsModeGroup: case NewsModeThread: kill_edit_popup(global.curr_group); break; } } /* Update Catchup */ void knapp6_callback(Widget gw, XtPointer client_data, XtPointer call_data) { char *msg; if (global.busy) return; switch (global.mode) { case NewsModeDisconnected: case NewsModeThread: break; case NewsModeGroup: /* Catchup */ if (global.confirm_catchup && global.curr_group->no_unread > 0) do_confirm("Really catchup?", True); else leave_group(True); break; case NewsModeConnected: /* Update */ case NewsModeSomegroups: case NewsModeAllgroups: case NewsModeNewgroups: set_busy(False); msg = do_update(); set_message(msg ? msg : "Newsrc and kill files updated.", !!msg); unset_busy(); break; } } /* Read/goto group Next unread */ void knapp7_callback(Widget gw, XtPointer client_data, XtPointer call_data) { ARTICLE *art; int new_thread; if (global.busy) return; switch (global.mode) { case NewsModeConnected: /* Read group */ set_curr_group(); /* * FALL THROUGH */ case NewsModeAllgroups: case NewsModeSomegroups: if (!global.curr_group) { set_message("No selected group!", True); return; } popdown_find_group(); popdown_search(); read_group(NULL, True, 0); break; case NewsModeGroup: /* Next unread */ case NewsModeThread: art = global.curr_art; new_thread = False; if (!art) { if (global.curr_subj) art = global.curr_subj->thread; new_thread = True; } while (art) { if (art->from && !art->read) break; art = next_in_thread_preorder(art); } if (!art) { SUBJECT *subj = NULL; ARTICLE *stop = NULL; new_thread = True; if (global.curr_subj) { stop = global.curr_subj->thread; subj = global.curr_subj->next; while (subj && subj->thread == stop) subj = subj->next; } if (!subj) { subj = get_subjects(main_thr); stop = NULL; } if (subj) while (subj->thread != stop) { for (art = subj->thread ; art ; art = next_in_thread_preorder(art)) if (art->from && !art->read) break; if (art) break; do { subj = subj->next; } while (subj && subj->prev->thread == subj->thread); if (!subj) if (stop) subj = get_subjects(main_thr); else break; } } set_curr_art(art, True); if (art) { if (global.mode == NewsModeThread && new_thread) setNewsModeThread(); read_article(art, False, NULL, NULL); } else { if (global.mode == NewsModeThread) setNewsModeGroup(False); else knapp0_callback(NULL, NULL, NULL); /* Done */ } break; case NewsModeDisconnected: case NewsModeNewgroups: break; } } /* Rescan Previous */ void knapp8_callback(Widget gw, XtPointer client_data, XtPointer call_data) { ARTICLE *art, *old; int new_thread; char *buffer; if (global.busy) return; switch (global.mode) { case NewsModeGroup: /* Previous */ case NewsModeThread: art = history_pop(); old = global.curr_art; if (old && art == old) { art = history_pop(); if (!art) history_push(old); } if (!art) { set_message("No previous article!", True); return; } if (old && old->from && old->read) { old->read = False; old->subject->no_unread++; global.curr_group->no_unread++; if (old->pixmap != None) { global.n_hot++; update_subj_hot_value(old->subject); } update_subj_entry(old->subject); if (global.mode == NewsModeThread) { ArtTreeNodeSetInner(main_widgets.arttree, (ART_TREE_NODE *)old, True); if (old->pixmap != None) ArtTreeNodeSetPixmap(main_widgets.arttree, (ART_TREE_NODE *)old, old->pixmap); } } if (old && old->subject->thread == art->subject->thread) new_thread = False; else new_thread = True; set_curr_art(art, True); if (read_article(art, False, NULL, NULL)) { if (global.mode == NewsModeThread) { if (new_thread) setNewsModeThread(); } else set_curr_art(art, False); } break; case NewsModeConnected: /* Rescan */ remove_rescan_timeout(); set_busy(True); set_message("Server contacted, waiting for response...", False); buffer = rescan(); if (!buffer) { reconnect_server(True); unset_busy(); return; } unset_busy(); if (atoi(buffer) == NNTP_OK_GROUPS) setNewsModeConnected(); else { char message[128]; if (strlen(buffer) > 80) buffer[80] = '\0'; sprintf(message, "Error! Message from server is: %s", buffer); set_message(message, True); } break; case NewsModeDisconnected: case NewsModeAllgroups: case NewsModeSomegroups: case NewsModeNewgroups: break; } } /* Save... */ void knapp10_callback(Widget gw, XtPointer client_data, XtPointer call_data) { switch (global.mode) { case NewsModeGroup: case NewsModeThread: popup_save(); break; case NewsModeDisconnected: case NewsModeConnected: case NewsModeAllgroups: case NewsModeSomegroups: case NewsModeNewgroups: if (global.bell) XBell(display, 0); break; } } /* Search... */ void knapp11_callback(Widget gw, XtPointer client_data, XtPointer call_data) { switch (global.mode) { case NewsModeGroup: case NewsModeThread: case NewsModeAllgroups: popup_search(); break; case NewsModeDisconnected: case NewsModeConnected: case NewsModeSomegroups: case NewsModeNewgroups: if (global.bell) XBell(display, 0); break; } } void thread_list_sel_callback(Widget gw, XtPointer client_data, XtPointer call_data) { long row = (long)call_data; SUBJECT *loop = global.curr_subj; if (global.busy || global.mode != NewsModeGroup) return; if (!loop || loop->disp != row) for (loop = get_subjects(main_thr) ; loop ; loop = loop->next) if (loop->disp == row) break; if (!loop || !global.curr_art || global.curr_art->subject->thread != loop->thread) global.curr_art = NULL; global.curr_subj = loop; } void thread_list_callback(Widget gw, XtPointer client_data, XtPointer call_data) { long row = (long)call_data; SUBJECT *subj; ARTICLE *art = NULL, *first = NULL; if (global.busy || global.mode != NewsModeGroup) return; if (global.curr_subj && global.curr_subj->disp == row) subj = global.curr_subj; else { subj = get_subjects(main_thr); while (subj) if (subj->disp == row) break; else subj = subj->next; if (!subj) return; global.curr_subj = subj; } for (art = subj->thread ; art ; art = next_in_thread_preorder(art)) if (art->subject == subj && art->from) if (!art->read) break; else if (!first) first = art; if (!art) { art = global.curr_art; if (!art || art->subject != subj) art = first; else while ((art = next_in_thread_preorder(art))) if (art->from && art->subject == subj) break; } global.curr_art = art; read_article(art, False, NULL, NULL); if (!art && global.bell) XBell(display, 0); } void arttree_sel_callback(Widget gw, XtPointer client_data, XtPointer call_data) { ARTICLE *art = (ARTICLE *)call_data; if (global.busy || global.mode != NewsModeThread) return; set_curr_art(art, False); read_article(art, False, NULL, NULL); } void group_list_callback(Widget gw, XtPointer client_data, XtPointer call_data) { long row = (long)call_data; long n; if (global.busy) return; global.curr_group = NULL; switch (global.mode) { case NewsModeDisconnected: case NewsModeGroup: case NewsModeThread: case NewsModeNewgroups: break; case NewsModeAllgroups: popdown_search(); global.curr_group = global.groups[row]; break; case NewsModeConnected: case NewsModeSomegroups: for (n = 0 ; n < global.no_groups ; n++) if (global.groups[n]->disp == row) { global.curr_group = global.groups[n]; break; } break; } if (!global.curr_group) return; read_group(NULL, True, 0); } void group_list_sel_callback(Widget w, XtPointer client_data, XtPointer call_data) { long row, i; if (global.busy) return; switch (global.mode) { case NewsModeDisconnected: case NewsModeNewgroups: case NewsModeGroup: case NewsModeThread: break; case NewsModeConnected: case NewsModeSomegroups: row = (long)call_data; global.curr_group = NULL; if (row < 0) break; for (i = 0 ; i < global.no_groups ; i++) if (global.groups[i]->disp == row) { global.curr_group = global.groups[i]; return; } break; case NewsModeAllgroups: row = (long)call_data; if (row >= 0 && row < global.no_groups) global.curr_group = global.groups[row]; else global.curr_group = NULL; break; } } void group_list_dnd_callback(Widget w, XtPointer client_data, XtPointer call_data) { long *index = (long *)call_data; GROUP *temp; long i; index[2] = False; switch (global.mode) { case NewsModeAllgroups: if (global.groups[index[0]]->subscribed && global.groups[index[1]]->subscribed) { index[2] = True; temp = global.groups[index[0]]; if (index[0] < index[1]) for (i = index[0] ; i < index[1] ; i++) global.groups[i] = global.groups[i + 1]; else for (i = index[0] ; i > index[1] ; i--) global.groups[i] = global.groups[i - 1]; global.groups[index[1]] = temp; break; } /* * FALL THROUGH */ case NewsModeDisconnected: case NewsModeConnected: case NewsModeGroup: case NewsModeThread: case NewsModeSomegroups: case NewsModeNewgroups: if (global.bell) XBell(display, 0); break; } } void delete_window_callback(Widget w, XtPointer client_data, XtPointer call_data) { if (global.busy) return; disconnect(True); } void sash_callback(Widget w, XtPointer client_data, XtPointer call_data) { int y, i1, i2, i3; unsigned int mods; Window w1, w2; Arg arg; Dimension height; /* * Avoid race condition. */ XQueryPointer(display, XtWindow(w), &w1, &w2, &i1, &i2, &i3, &y, &mods); if (y == 0 || mods == 0) return; XtSetArg(arg, XtNheight, &height); XtGetValues(main_widgets.top_layout, &arg, 1); y += height; if (y <= 0) y = 1; else if (y > 32767) y = 32767; XtSetArg(arg, XtNheight, y); XtSetValues(main_widgets.top_layout, &arg, 1); } ./knews-1.0b.1/src/p_popup.h100644 1244 1244 403 6455455546 14237 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ struct PostContext; struct PostWidgets; extern void fork_editor(struct PostContext*); extern void check_article_and_popup(struct PostContext*); extern void destroy_post_widgets(struct PostWidgets*); ./knews-1.0b.1/src/procs.h100644 1244 1244 2124 6455455546 13725 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ extern void knapp0_callback(Widget, XtPointer, XtPointer); extern void knapp1_callback(Widget, XtPointer, XtPointer); extern void knapp2_callback(Widget, XtPointer, XtPointer); extern void knapp5_callback(Widget, XtPointer, XtPointer); extern void knapp6_callback(Widget, XtPointer, XtPointer); extern void knapp7_callback(Widget, XtPointer, XtPointer); extern void knapp8_callback(Widget, XtPointer, XtPointer); extern void knapp10_callback(Widget, XtPointer, XtPointer); extern void knapp11_callback(Widget, XtPointer, XtPointer); extern void thread_list_sel_callback(Widget, XtPointer, XtPointer); extern void thread_list_callback(Widget, XtPointer, XtPointer); extern void arttree_sel_callback(Widget, XtPointer, XtPointer); extern void group_list_callback(Widget, XtPointer, XtPointer); extern void group_list_sel_callback(Widget, XtPointer, XtPointer); extern void group_list_dnd_callback(Widget, XtPointer, XtPointer); extern void delete_window_callback(Widget, XtPointer, XtPointer); extern void sash_callback(Widget, XtPointer, XtPointer); ./knews-1.0b.1/src/main.c100644 1244 1244 53673 6625550631 13543 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include "global.h" #include #include "../Widgets/Util.h" #include "actions.h" #include "ahead.h" #include "child.h" #include "color.h" #include "connect.h" #include "domain.h" #include "file.h" #include "font.h" #include "k_action.h" #include "mailcap.h" #include "misc.h" #include "p_menu.h" #include "procs.h" #include "read.h" #include "resource.h" #include "save.h" #include "server.h" #include "tag.h" #include "thread.h" #include "uudecode.h" #include "widgets.h" #include "xutil.h" #include "sysdeps.h" struct Global global = {0, }; XtAppContext app_cont; Display *display = NULL; struct SERVER *main_server = NULL; /* * Icon courtesy of Matthias Schuetze */ static void set_icon(Widget shell) { Arg arg[2]; Pixmap icon, mask; #include "knews_icon.xbm" #include "knews_mask.xbm" icon = XCreateBitmapFromData(display, DefaultRootWindow(display), (char *)knews_icon_bits, knews_icon_width, knews_icon_height); mask = XCreateBitmapFromData(display, DefaultRootWindow(display), (char *)knews_mask_bits, knews_mask_width, knews_mask_height); XtSetArg(arg[0], XtNiconPixmap, icon); XtSetArg(arg[1], XtNiconMask, mask); XtSetValues(shell, arg, 2); #undef knews_icon_width #undef knews_icon_height #undef knews_mask_width #undef knews_mask_height } static int disp_io_error_handler(Display *disp) { char *msg; block_sighup(); perror("knews: Fatal I/O error or KillClient"); fputs(" Updating newsrc and kill files... ", stderr); msg = do_update(); if (!msg) msg = "OK"; fprintf(stderr, "%s\n", msg); exit(1); } static int disp_error_handler(Display *disp, XErrorEvent *event) { char buffer[256]; char number[32]; XGetErrorText(disp, event->error_code, buffer, sizeof(buffer)); fprintf(stderr, "X Error of failed request : %s\n", buffer); sprintf(number, "%d", event->request_code); XGetErrorDatabaseText(disp, "XRequest", number, "", buffer, sizeof(buffer)); fprintf(stderr, " Major opcode of failed request : %d (%s)\n", event->request_code, buffer); switch (event->error_code) { case BadValue: fprintf(stderr, " Resource id in failed request : "); break; case BadAtom: fprintf(stderr, " Value in failed request : "); break; default: fprintf(stderr, " Atom id in failed request : "); break; } fprintf(stderr, "0x%lx\n", event->resourceid); fputs("knews: exiting!\n", stderr); exit(1); } static void init_timer(XtPointer client_data, XtIntervalId *id) { if (main_widgets.second_shell) XtPopup(main_widgets.second_shell, XtGrabNone); unset_busy(); if (global.nntp_server) connect_server(); else res_load(NULL); } int main(int argc, char *argv[]) { #ifndef DEFAULT_NNTPSERVER #define DEFAULT_NNTPSERVER 0 #endif #ifndef DEFAULT_EDIT_COMMAND #define DEFAULT_EDIT_COMMAND "xterm -e vi +%i %s" #endif static XtResource resource_spec[] = { #define offset(field) XtOffsetOf(struct Global, field) {"nntpServer", "NntpServer", XtRString, sizeof(String), offset(nntp_server), XtRImmediate, (XtPointer)DEFAULT_NNTPSERVER}, {"configNntpServer", "ConfigNntpServer", XtRString, sizeof(String), offset(config_nntp_server), XtRImmediate, (XtPointer)NULL}, {"editCommand", "EditCommand", XtRString, sizeof(String), offset(edit_command), XtRImmediate, (XtPointer)DEFAULT_EDIT_COMMAND}, {"urlCommand", "UrlCommand", XtRString, sizeof(String), offset(url_command), XtRImmediate, (XtPointer)NULL}, {"printCommand", "PrintCommand", XtRString, sizeof(String), offset(print_command), XtRImmediate, (XtPointer)NULL}, {"needsTerminal", "NeedsTerminal", XtRString, sizeof(String), offset(needs_terminal), XtRImmediate, (XtPointer)NULL}, {"copiousOutput", "CopiousOutput", XtRString, sizeof(String), offset(copious_output), XtRImmediate, (XtPointer)NULL}, {"mailName", "MailName", XtRString, sizeof(String), offset(mail_name), XtRImmediate, (XtPointer)NULL}, {"configFile", "ConfigFile", XtRString, sizeof(String), offset(config_file), XtRImmediate, (XtPointer)NULL}, {"busyCursor", "BusyCursor", XtRCursor, sizeof(Cursor), offset(busy_cursor), XtRString, (XtPointer)"watch"}, {"cursor", "Cursor", XtRCursor, sizeof(Cursor), offset(cursor), XtRString, (XtPointer)"top_left_arrow"}, {"stderrTimeout", "StderrTimeout", XtRLong, sizeof(long), offset(stderr_timeout), XtRImmediate, (XtPointer)10000}, {"chunkSize", "ChunkSize", XtRInt, sizeof(int), offset(chunk_size), XtRImmediate, (XtPointer)16}, {"postMiscMenuSize", "PostMiscMenuSize", XtRInt, sizeof(int), offset(post_misc_menu_size), XtRImmediate, (XtPointer)1}, {"extraMenuSize", "ExtraMenuSize", XtRInt, sizeof(int), offset(extra_menu_size), XtRImmediate, (XtPointer)0}, {"typeMenuSize", "TypeMenuSize", XtRInt, sizeof(int), offset(type_menu_size), XtRImmediate, (XtPointer)0}, {"forwardMenuSize", "ForwardMenuSize", XtRInt, sizeof(int), offset(forward_menu_size), XtRImmediate, (XtPointer)0}, {"nCols", "NCols", XtRInt, sizeof(int), offset(n_cols), XtRImmediate, (XtPointer)(5*5*5 - 5 + 17)}, {"knewsVersion", "KnewsVersion", XtRString, sizeof(String), offset(version), XtRImmediate, (XtPointer)NULL}, {"separateWindows", "SeparateWindows", XtRBoolean, sizeof(Boolean), offset(separate_windows), XtRImmediate, (XtPointer)False}, {"bell", "Bell", XtRBoolean, sizeof(Boolean), offset(bell), XtRImmediate, (XtPointer)True}, {"headDebug", "Debug", XtRBoolean, sizeof(Boolean), offset(head_debug), XtRImmediate, (XtPointer)False}, {"useIcon", "UseIcon", XtRBoolean, sizeof(Boolean), offset(use_icon), XtRImmediate, (XtPointer)True}, {"defaultHotColor", "DefaultHotColor", XtRPixel, sizeof(Pixel), offset(default_hot_pixel), XtRString, XtDefaultForeground}, {"retrieveDescriptions", "RetrieveDescriptions", XtRString, sizeof(String), offset(retrieve_descr), XtRImmediate, (XtPointer)NULL}, {"readActiveFile", "ReadActiveFile", XtRString, sizeof(String), offset(read_active_file), XtRImmediate, (XtPointer)NULL}, {"fillNewsrcFile", "FillNewsrcFile", XtRString, sizeof(String), offset(fill_newsrc_file), XtRImmediate, (XtPointer)NULL}, {"showNumberLines", "ShowNumberLines", XtRString, sizeof(String), offset(show_number_lines), XtRImmediate, (XtPointer)NULL}, {"keepThreadInfo", "KeepThreadInfo", XtRString, sizeof(String), offset(keep_thread_info), XtRImmediate, (XtPointer)NULL}, {"checkForNewGroups", "CheckForNewGroups", XtRString, sizeof(String), offset(check_for_new_groups), XtRImmediate, (XtPointer)NULL}, {"confirmQuit", "Confirm", XtRBoolean, sizeof(Boolean), offset(confirm_quit), XtRImmediate, (XtPointer)False}, {"confirmCatchup", "Confirm", XtRBoolean, sizeof(Boolean), offset(confirm_catchup), XtRImmediate, (XtPointer)False}, {"confirmQuitGroup", "ConfirmQuit", XtRString, sizeof(String), offset(confirm_quit_group), XtRImmediate, (XtPointer)NULL}, {"icaseRegexps", "IcaseRegexps", XtRBoolean, sizeof(Boolean), offset(icase_regexps), XtRImmediate, (XtPointer)True}, {"showCache", "ShowCache", XtRBoolean, sizeof(Boolean), offset(show_cache), XtRImmediate, (XtPointer)False}, {"bogusFileSystem", "BogusFileSystem", XtRBoolean, sizeof(Boolean), offset(bogus_file_system), XtRImmediate, (XtPointer)False}, {"generatePath", "GeneratePath", XtRBoolean, sizeof(Boolean), offset(generate_path), XtRImmediate, (XtPointer)False}, {"quoteEmpty", "QuoteEmpty", XtRBoolean, sizeof(Boolean), offset(quote_empty), XtRImmediate, (XtPointer)True}, {"sortGroups", "SortGroups", XtRBoolean, sizeof(Boolean), offset(sort_groups), XtRImmediate, (XtPointer)False}, {"inlineImages", "InlineImages", XtRBoolean, sizeof(Boolean), offset(inline_images), XtRImmediate, (XtPointer)True}, {"showXfaces", "InlineImages", XtRBoolean, sizeof(Boolean), offset(show_xfaces), XtRImmediate, (XtPointer)True}, {"colorHack", "Hack", XtRBoolean, sizeof(Boolean), offset(color_hack), XtRImmediate, (XtPointer)False}, {"mimeForward", "MimeForward", XtRBoolean, sizeof(Boolean), offset(mime_forward), XtRImmediate, (XtPointer)True}, {"newsrcTemplate", "NewsrcTemplate", XtRString, sizeof(String), offset(newsrc_templ), XtRImmediate, (XtPointer)NULL}, {"oldNewsrcTemplate", "OldNewsrcTemplate", XtRString, sizeof(String), offset(old_newsrc_templ), XtRImmediate, (XtPointer)NULL}, {"killFileTemplate", "KillFileTemplate", XtRString, sizeof(String), offset(kill_file_templ), XtRImmediate, (XtPointer)"~/.kill-%s"}, {"groupKillFileTemplate", "GroupKillFileTemplate", XtRString, sizeof(String), offset(group_kill_file_templ), XtRImmediate, (XtPointer)"~/.knews/%s/%g/KILL"}, {"autoSubscribe", "AutoSubscribe", XtRString, sizeof(String), offset(auto_subscribe), XtRImmediate, (XtPointer)"news.answers:\nnews.newusers.questions:\n"}, {"mimeTypes", "MimeTypes", XtRString, sizeof(String), offset(mime_types), XtRImmediate, (XtPointer)NULL}, /******/ {"foreground", "Foreground", XtRPixel, sizeof(Pixel), offset(pixel), XtRString, XtDefaultForeground}, {"quoteColor", "Foreground", XtRPixel, sizeof(Pixel), offset(quote_pixel), XtRString, XtDefaultForeground}, {"headerColor", "Foreground", XtRPixel, sizeof(Pixel), offset(header_pixel), XtRString, XtDefaultForeground}, {"alertColor", "Foreground", XtRPixel, sizeof(Pixel), offset(alert_pixel), XtRString, XtDefaultForeground}, {"clickableColor", "ClickableColor", XtRPixel, sizeof(Pixel), offset(clickable_pixel), XtRString, XtDefaultForeground}, #undef offset }; static XtActionsRec actions[] = { {"tree-up", action_tree_up}, {"tree-down", action_tree_down}, {"tree-left", action_tree_left}, {"tree-right", action_tree_right}, {"tree-down-right", action_tree_down_right}, {"followup", action_followup}, {"reply", action_reply}, {"followup-and-reply", action_followup_and_reply}, {"post-new", action_post_new}, {"forward-by-mail", action_forward_by_mail}, {"list-up", action_list_up}, {"list-down", action_list_down}, {"tree-or-list-up", action_tree_or_list_up}, {"tree-or-list-down", action_tree_or_list_down}, {"enter-mode", action_enter_mode}, {"exit-mode", action_exit_mode}, {"tree-left-or-exit-mode", action_tree_left_or_exit_mode}, {"tree-right-or-enter-mode", action_tree_right_or_enter_mode}, {"read-article", action_read_article}, {"mime-hack", action_mime_hack}, {"goto-next-hot", action_goto_next_hot}, {"view-thread", action_view_thread}, {"mark-read-article", action_mark_read_article}, {"mark-read-subject", action_mark_read_subject}, {"mark-read-thread", action_mark_read_thread}, {"mark-read-subthread", action_mark_read_subthread}, {"mark-read-tagged", action_mark_read_tagged}, {"mark-read-all", action_mark_read_all}, {"mark-read-to-current", action_mark_read_to_current}, {"mark-read-non-tagged", action_mark_read_non_tagged}, {"mark-read-cold", action_mark_read_cold}, {"mark-unread-article", action_mark_unread_article}, {"mark-unread-subject", action_mark_unread_subject}, {"mark-unread-thread", action_mark_unread_thread}, {"mark-unread-subthread", action_mark_unread_subthread}, {"mark-unread-tagged", action_mark_unread_tagged}, {"mark-unread-all", action_mark_unread_all}, {"mark-unread-killed", action_mark_unread_killed}, {"uudecode", action_uudecode}, {"clear-tagged", action_clear_tagged}, {"save", action_save}, {"pipe", action_pipe}, {"tag-thread", action_tag_thread}, {"tag-subject", action_tag_subject}, {"untag-thread", action_untag_thread}, {"untag-subject", action_untag_subject}, {"tag-hot", action_tag_hot}, {"catchup", action_catchup}, {"unsubscribe", action_unsubscribe}, {"subscribe", action_subscribe}, {"change-size", action_change_size}, {"schedule-thread-ahead", action_schedule_thread_ahead}, {"popup-find-group", action_popup_find_group}, {"do-the-right-thing", action_do_the_right_thing}, {"kill-append", action_kill_append}, {"kill-prepend", action_kill_prepend}, {"popup-kill", action_popup_kill}, {"tree-layout", action_tree_layout}, }; static String fallback_resources[] = { "*grouplist*PreferredLines: 14", "*killist*PreferredLines: 14", "*PreferredColumns: 84", "*ArtTree.useLineShadows: True", "*textscrbar.allowOff: True", "*Knapp.Justify: center", "*MenuKnapp.Justify: center", "*quotetoggle.set: True", "*stderr*message.center: False", "*textscrbar.stepSize: 13", "*unreadtoggle.set: True", "*postpopup*posttext*preferredLines: 8", "*postpopup*posttext*preferredColumns: 64", "*postpopup*attachlist*preferredLines: 3", "*postpopup*attachlist*preferredColumns: 64", /* colors */ "*Foreground: Black", "*Background: Bisque", "*BorderColor: Black", "*rubberColor: Red", "*quoteColor: Medium Blue", "*headerColor: #000090", "*innerColor: Red", "*abort.Foreground: Red", /* fonts */ "Knews.us-ascii.bodyFont:" " -b&h-lucidatypewriter-medium-r-normal-*-*-120-*-*-*-*-iso8859-1", "Knews.us-ascii.quoteFont:" " -b&h-lucidatypewriter-bold-r-normal-*-*-120-*-*-*-*-iso8859-1", "Knews.us-ascii.headerFont:" " -b&h-lucidatypewriter-bold-r-normal-*-*-120-*-*-*-*-iso8859-1", "Knews.us-ascii.treeFont:" " -b&h-lucidatypewriter-bold-r-normal-*-*-120-*-*-*-*-iso8859-1", "Knews.us-ascii.listFont:" " -b&h-lucidatypewriter-medium-r-normal-*-*-120-*-*-*-*-iso8859-1", "*ScrList*Font:" " -b&h-lucidatypewriter-medium-r-normal-*-*-120-*-*-*-*-iso8859-1", "*Font:" " -b&h-lucidatypewriter-bold-r-normal-*-*-120-*-*-*-*-iso8859-1", /* translations */ "*arttree.baseTranslations: #augment \\n" " : set-selected()\\n" " : toggle-outer()", "*grouplist.baseTranslations: #augment \\n" " : dnd-start() \\n" " : dnd-do() \\n" " : dnd-end()", "*killist.baseTranslations: #augment \\n" " : dnd-start() \\n" " : dnd-do() \\n" " : dnd-end()", "*threadlist.baseTranslations: #augment \\n" " ~Ctrl : select() mark-read-thread() \\n" " ~Ctrl : select() mark-read-thread() \\n" " Ctrl : select() mark-unread-thread() \\n" " : select() tag-thread()", /* labels */ "*knapplayout.knapp0.label: Quit\\nDone", "*knapplayout.knapp1.label: " " Connect...\\nDisconnect\\nView thread\\nBack\\nSubscribe", "*knapplayout.knapp2.label: " " All groups\\nAll threads\\nUnsubscribe", "*knapplayout.misc.label: Misc", "*knapplayout.post.label: Post", "*knapplayout.kill.label: Kill...", "*knapplayout.knapp6.label: Update\\nCatchup", "*knapplayout.knapp7.label: Read Group\\nNext unread\\nGoto group", "*knapplayout.knapp8.label: Rescan\\nPrevious", "*knapplayout.abort.label: Abort", "*knapplayout.save.label: Save...", "*knapplayout.search.label: Search...", "*killeditor*fieldknapp.label: Message-Id\\nSubject\\nFrom\\nXref", "*killeditor*scopeknapp.label: " " Article\\nSubject\\nThread\\nSubthread", "*killeditor*actionknapp.label: Kill\\nHot", "*postpopup*post.label: Post\\nMail\\nPost&Mail", "*saveshell*ok.label: Save\\nPipe", /* "buffers" */ "*fieldmessage.buffer: Header:", "*scopemessage.buffer: Scope:", "*groupmessage.buffer: Group regexp:", "*exprmessage.buffer: Regexp/Message-Id:", "*colormessage.buffer: Color:", "*saveshell*message.buffer: Save to file or pipe to shell", "*saveshell*shellmessage.buffer: Shell-command:", "*saveshell*filemessage.buffer: File:", "*searchshell*regexptitle.buffer: Regular expression searching", "*searchshell*xpattitle.buffer: XPAT wildcard searching", "*searchshell*regexpmessage.buffer: Regexp:", "*searchshell*wildcardmessage.buffer: Wildcard:", "*searchshell*headermessage.buffer: Header:", "*postpopup*posttitle.buffer: Post/Mail Manager", "*postpopup*attachtitle.buffer: Attachments:", "*postpopup*typetitle.buffer: Content-Type:", "*postpopup*descrtitle.buffer: Content-Description:", "*postpopup*disptitle.buffer: Content-Disposition:", "*postpopup*enctitle.buffer: Content-Transfer-Encoding:", "*postpopup*nametitle.buffer: filename=", "*postpopup*inlinetoggle.label: inline", "*postpopup*attachtoggle.label: attachment", "*postpopup*nonetoggle.label: None", "*postpopup*base64toggle.label: Base 64", "*postpopup*uuetoggle.label: Uuencode", "*postpopup*qptoggle.label: Quoted-printable", NULL, }; static XrmOptionDescRec options[] = { {"-nntpServer", ".nntpServer", XrmoptionSepArg, (XtPointer)NULL}, {"-ncols", ".nCols", XrmoptionSepArg, (XtPointer)NULL}, {"-bg", "*Background", XrmoptionSepArg, (XtPointer)NULL}, {"-fg", "*Foreground", XrmoptionSepArg, (XtPointer)NULL}, {"-separate", ".separateWindows", XrmoptionNoArg, (XtPointer)"False"}, {"+separate", ".separateWindows", XrmoptionNoArg, (XtPointer)"True"}, {"-descriptions", ".retrieveDescriptions", XrmoptionNoArg, (XtPointer)"False"}, {"+descriptions", ".retrieveDescriptions", XrmoptionNoArg, (XtPointer)"True"}, {"-active", ".readActiveFile", XrmoptionNoArg, (XtPointer)"False"}, {"+active", ".readActiveFile", XrmoptionNoArg, (XtPointer)"True"}, {"-fill", ".fillNewsrcFile", XrmoptionNoArg, (XtPointer)"False"}, {"+fill", ".fillNewsrcFile", XrmoptionNoArg, (XtPointer)"True"}, {"-lines", ".showNumberLines", XrmoptionNoArg, (XtPointer)"False"}, {"+lines", ".showNumberLines", XrmoptionNoArg, (XtPointer)"True"}, {"-bell", ".bell", XrmoptionNoArg, (XtPointer)"False"}, {"+bell", ".bell", XrmoptionNoArg, (XtPointer)"True"}, {"+keep", ".keepThreadInfo", XrmoptionNoArg, (XtPointer)"Subscribed"}, {"-keep", ".keepThreadInfo", XrmoptionNoArg, (XtPointer)"False"}, {"-visual", ".visualClass", XrmoptionSepArg, (XtPointer)NULL}, {"-depth", ".visualDepth", XrmoptionSepArg, (XtPointer)NULL}, {"-install", ".installCmap", XrmoptionNoArg, (XtPointer)"True"}, {"+icase", ".icaseRegexps", XrmoptionNoArg, (XtPointer)"True"}, {"-icase", ".icaseRegexps", XrmoptionNoArg, (XtPointer)"False"}, {"+images", ".inlineImages", XrmoptionNoArg, (XtPointer)"True"}, {"-images", ".inlineImages", XrmoptionNoArg, (XtPointer)"False"}, }; Arg args[8]; char *home = getenv("HOME"); if (!home) { fputs("knews: couldn't getenv(\"HOME\")\n", stderr); exit(1); } if (chdir(home) < 0) { int oerrno = errno; fprintf(stderr, "knews: couldn't chdir(%s)", home); errno = oerrno; perror(NULL); exit(1); } freopen("/dev/null", "r", stdin); srand(3ul * time(NULL) + 5ul * getuid() + 7ul * getpid() + 11ul * getppid()); main_thr = create_thread_context(); main_server = server_create(-1); server_set_quit_func(main_server, nntp_quit); #if (XtSpecificationRelease > 4) XtSetLanguageProc(NULL, NULL, NULL); #endif XtSetTypeConverter(XtRString, XtRPixmap, cvt_string_to_pixmap, NULL, 0, XtCacheAll, destroy_pixmap); XtSetTypeConverter(XtRString, XtRLong, cvt_string_to_long, NULL, 0, XtCacheAll, NULL); XSetErrorHandler(disp_error_handler); XSetIOErrorHandler(disp_io_error_handler); XtToolkitInitialize(); app_cont = XtCreateApplicationContext(); XtAppSetFallbackResources(app_cont, fallback_resources); display = XtOpenDisplay(app_cont, NULL, NULL, "Knews", options, XtNumber(options), &argc, argv); if (argc != 1) { int n = strlen(argv[1]); if (strncmp(argv[1], "-version", n) == 0) fputs("knews: version " KNEWS_VERSION " compiled " "on " __DATE__ " " __TIME__ ".\n", stderr); else fputs("knews: Bad command line arguments, " "see the man-page for usage.\n", stderr); exit(1); } if (!display) exit(1); color_init(display); XtSetArg(args[0], XtNinput, True); XtSetArg(args[1], XtNcolormap, global.cmap); XtSetArg(args[2], XtNvisual, global.visual); XtSetArg(args[3], XtNdepth, global.depth); XtSetArg(args[4], XtNmappedWhenManaged, False); main_widgets.shell = XtAppCreateShell(NULL, "Knews", #if XtSpecificationRelease >= 6 sessionShellWidgetClass, #else applicationShellWidgetClass, #endif display, args, 5); if (!main_widgets.shell) exit(1); init_child_contexts(); XtAppAddActions(app_cont, actions, XtNumber(actions)); XtGetApplicationResources(main_widgets.shell, (XtPointer)&global, resource_spec, XtNumber(resource_spec), NULL, 0); init_fonts(main_widgets.shell); mailcap_init(); if (global.newsrc_templ && !strstr(global.newsrc_templ, "%s")) { fputs("knews: no %s in newsrcTemplate, ignoring it.\n", stderr); global.newsrc_templ = NULL; } if (global.config_file && !strstr(global.config_file, "%s")) { fputs("knews: no %s in configFile, ignoring it.\n", stderr); global.config_file = NULL; } if (!global.config_file) global.config_file = "~/.knews/config-%s"; if (global.use_icon) set_icon(main_widgets.shell); fix_domain_stuff(); res_initialize(); if (global.nntp_server && global.nntp_server[0] != '\0') global.nntp_server = XtNewString(global.nntp_server); else { char *env_var = getenv("NNTPSERVER"); #ifndef DEFAULT_DEFAULT_NNTPSERVER #define DEFAULT_DEFAULT_NNTPSERVER 0 #endif if (env_var) if (env_var[0] == '\0') global.nntp_server = NULL; else global.nntp_server = XtNewString(env_var); else { global.nntp_server = DEFAULT_DEFAULT_NNTPSERVER; if (global.nntp_server) global.nntp_server = XtNewString(global.nntp_server); } } if (global.chunk_size <= 0) global.chunk_size = 16; if (global.post_misc_menu_size <= 0) global.post_misc_menu_size = 1; if (!global.version) fputs("knews: Application defaults file " "not properly installed.\n", stderr); else if (strncmp(global.version, KNEWS_VERSION, sizeof KNEWS_VERSION - 1) != 0) fputs("knews: Incompatible version of " "application defaults file.\n", stderr); create_main_widgets(); global.gc = DefaultGCOfScreen(XtScreen(main_widgets.shell)); alloc_colors(); if (fcntl(ConnectionNumber(display), F_SETFD, FD_CLOEXEC) < 0) perror("fcntl"); setNewsModeDisconnected(); set_standard_message(); set_busy(False); XtAppAddTimeOut(app_cont, 500, init_timer, NULL); XtMapWidget(main_widgets.shell); XtAppMainLoop(app_cont); return 0; } ./knews-1.0b.1/src/p_menu.h100644 1244 1244 672 6455455546 14050 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ extern void create_post_menu(Widget); extern void action_followup(Widget, XEvent*, String*, Cardinal*); extern void action_reply(Widget, XEvent*, String*, Cardinal*); extern void action_followup_and_reply(Widget, XEvent*, String*, Cardinal*); extern void action_post_new(Widget, XEvent*, String*, Cardinal*); extern void action_forward_by_mail(Widget, XEvent*, String*, Cardinal*); ./knews-1.0b.1/src/actions.c100644 1244 1244 27757 6455455546 14275 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include "global.h" #include "actions.h" #include "procs.h" #include "read.h" #include "search.h" #include "server.h" #include "thread.h" #include "util.h" #include "widgets.h" #include "xutil.h" #include "../Widgets/ArtText.h" #include "../Widgets/ArtTree.h" #include "../Widgets/Scrollable.h" #include "../Widgets/ScrList.h" #include "../Widgets/Util.h" void action_tree_up(Widget w, XEvent *event, String *params, Cardinal *no_params) { ARTICLE *art; if (global.busy || (global.mode != NewsModeGroup && global.mode != NewsModeThread)) return; art = global.curr_art; if (!art) { if (global.bell) XBell(display, 0); return; } art = prev_in_thread_dont_wrap(art); if (art) { set_curr_art(art, True); if (*no_params != 1 || (!art->from && params[0][0] != 'f')) art = NULL; read_article(art, False, NULL, NULL); } } void action_tree_down(Widget w, XEvent *event, String *params, Cardinal *no_params) { ARTICLE *art; if (global.busy || (global.mode != NewsModeGroup && global.mode != NewsModeThread)) return; art = global.curr_art; if (!art) { if (global.bell) XBell(display, 0); return; } art = next_in_thread_dont_wrap(art); if (art) { set_curr_art(art, True); if (*no_params != 1 || (!art->from && params[0][0] != 'f')) art = NULL; read_article(art, False, NULL, NULL); } } void action_tree_left(Widget w, XEvent *event, String *params, Cardinal *no_params) { ARTICLE *art; if (global.busy || (global.mode != NewsModeGroup && global.mode != NewsModeThread)) return; art = global.curr_art; if (!art) { if (global.bell) XBell(display, 0); return; } art = A_PARENT(art); if (art) { set_curr_art(art, True); if (*no_params != 1 || (!art->from && params[0][0] != 'f')) art = NULL; read_article(art, False, NULL, NULL); } } void action_tree_right(Widget w, XEvent *event, String *params, Cardinal *no_params) { ARTICLE *art; if (global.busy || (global.mode != NewsModeGroup && global.mode != NewsModeThread)) return; art = global.curr_art; if (!art) { if (global.bell) XBell(display, 0); return; } art = A_CHILD1(art); if (art) { set_curr_art(art, True); if (*no_params != 1 || (!art->from && params[0][0] != 'f')) art = NULL; read_article(art, False, NULL, NULL); } } void action_tree_down_right(Widget w, XEvent *event, String *params, Cardinal *no_params) { ARTICLE *art; if (global.busy || (global.mode != NewsModeGroup && global.mode != NewsModeThread)) return; art = global.curr_art; if (!art) { if (global.bell) XBell(display, 0); return; } art = A_CHILD1(art); if (art) { while (A_SIBLING(art)) art = A_SIBLING(art); set_curr_art(art, True); if (*no_params != 1 || (!art->from && params[0][0] != 'f')) art = NULL; read_article(art, False, NULL, NULL); } } static void do_list(long step) { long sel, n; Widget w; SUBJECT *loop; switch (global.mode) { case NewsModeGroup: w = main_widgets.thread_list; global.curr_subj = NULL; global.curr_art = NULL; break; case NewsModeAllgroups: case NewsModeConnected: w = main_widgets.group_list; global.curr_group = NULL; break; default: return; } sel = ScrListGetFirstSelected(w); if (sel < 0) sel = 0; else { ScrListSetSelected(w, sel, False); sel += step; } if (sel < 0) sel = 0; else { n = ScrollableGetVSize(w); if (n > 0 && sel >= n) sel = n - 1; } ScrListMakeVisible(w, sel); ScrListSetSelected(w, sel, True); sel = ScrListGetFirstSelected(w); if (sel < 0) return; switch (global.mode) { case NewsModeConnected: for (n = 0 ; n < global.no_groups ; n++) if (!global.groups[n]->subscribed) break; else if (global.groups[n]->disp == sel) { global.curr_group = global.groups[n]; break; } break; case NewsModeGroup: for (loop = get_subjects(main_thr) ; loop ; loop = loop->next) if (loop->disp == sel) break; global.curr_subj = loop; global.curr_art = NULL; break; case NewsModeAllgroups: if (sel <= global.no_groups) global.curr_group = global.groups[sel]; break; case NewsModeSomegroups: for (n = 0 ; n < global.no_groups ; n++) if (global.groups[n]->disp == sel) { global.curr_group = global.groups[n]; break; } break; default: break; } } void action_list_up(Widget w, XEvent *event, String *params, Cardinal *no_params) { long step = 1; if (global.busy) return; if (no_params && *no_params == 1 && params[0][0] >= '0' && params[0][0] <= '9') if (!strchr(params[0], '.')) step = atol(params[0]); else { step = ScrollableGetVShown(global.mode == NewsModeGroup ? main_widgets.thread_list : main_widgets.group_list); step *= atof(params[0]); } do_list(-step); } void action_list_down(Widget w, XEvent *event, String *params, Cardinal *no_params) { long step = 1; if (global.busy) return; if (no_params && *no_params == 1 && params[0][0] >= '0' && params[0][0] <= '9') if (!strchr(params[0], '.')) step = atol(params[0]); else { step = ScrollableGetVShown(global.mode == NewsModeGroup ? main_widgets.thread_list : main_widgets.group_list); step *= atof(params[0]); } do_list(step); } void action_tree_or_list_up(Widget w, XEvent *event, String *params, Cardinal *no_params) { if (global.mode == NewsModeThread) action_tree_up(w, event, params, no_params); else action_list_up(w, NULL, NULL, NULL); } void action_tree_or_list_down(Widget w, XEvent *event, String *params, Cardinal *no_params) { if (global.mode == NewsModeThread) action_tree_down(w, event, params, no_params); else action_list_down(w, NULL, NULL, NULL); } void action_exit_mode(Widget w, XEvent *event, String *params, Cardinal *no_params) { if (global.busy) return; switch (global.mode) { case NewsModeDisconnected: case NewsModeGroup: case NewsModeAllgroups: case NewsModeSomegroups: case NewsModeNewgroups: knapp0_callback(w, NULL, NULL); break; case NewsModeThread: case NewsModeConnected: knapp1_callback(w, NULL, NULL); break; } } void action_enter_mode(Widget w, XEvent *event, String *params, Cardinal *no_params) { if (global.busy) return; switch (global.mode) { case NewsModeGroup: if (global.curr_subj && (!global.curr_art || global.curr_art->subject->thread != global.curr_subj->thread)) { ARTICLE *art = global.curr_subj->thread; set_curr_art(art, False); if (*no_params != 1 || (!art->from && params[0][0] != 'f')) art = NULL; read_article(art, False, NULL, NULL); } knapp1_callback(w, NULL, NULL); break; case NewsModeDisconnected: knapp1_callback(w, NULL, NULL); break; case NewsModeConnected: case NewsModeSomegroups: case NewsModeAllgroups: knapp7_callback(w, NULL, NULL); break; case NewsModeNewgroups: case NewsModeThread: break; } } void action_tree_left_or_exit_mode(Widget w, XEvent *event, String *params, Cardinal *no_params) { if (global.busy) return; if (global.mode == NewsModeThread && global.curr_art && A_PARENT(global.curr_art)) action_tree_left(w, event, params, no_params); else action_exit_mode(w, event, params, no_params); } void action_tree_right_or_enter_mode(Widget w, XEvent *event, String *params, Cardinal *no_params) { if (global.busy) return; if (global.mode == NewsModeThread && global.curr_art && A_CHILD1(global.curr_art)) action_tree_right(w, event, params, no_params); else action_enter_mode(w, event, params, no_params); } void action_goto_next_hot(Widget w, XEvent *event, String *params, Cardinal *no_params) { ARTICLE *art; SUBJECT *subj; int new_thread; if (global.busy || (global.mode != NewsModeGroup && global.mode != NewsModeThread)) return; new_thread = global.curr_subj == NULL; art = global.curr_art; if (art) subj = art->subject; else subj = global.curr_subj; if (!subj) { set_message("No thread selected!", True); return; } if (art) { while ((art = next_in_thread_preorder(art))) if (art->from && !art->read && art->pixmap != None) break; if (!art) { while (subj->next && subj->next->thread == subj->thread) subj = subj->next; subj = subj->next; } } if (!art) { new_thread = True; while (subj) { for (art = subj->thread ; art ; art = next_in_thread_preorder(art)) if (art->from && ! art->read && art->pixmap != None) break; if (art) break; while (subj->next && subj->next->thread == subj->thread) subj = subj->next; subj = subj->next; } } set_curr_art(art, True); if (!art) set_message("No more hot articles.", True); else { if (global.mode == NewsModeThread && new_thread) setNewsModeThread(); read_article(art, *no_params != 0, NULL, NULL); } } void action_view_thread(Widget w, XEvent *event, String *params, Cardinal *no_params) { if (*no_params == 0 || params[0][0] == 't' || params[0][0] == 'T') { if (global.mode != NewsModeGroup && global.mode != NewsModeThread) return; } else if (params[0][0] == 'y' || params[0][0] == 'Y') { if (global.mode != NewsModeGroup) return; } else if (params[0][0] == 'n' || params[0][0] == 'N') { if (global.mode != NewsModeThread) return; } knapp1_callback(w, NULL, NULL); } void action_change_size(Widget w, XEvent *event, String *params, Cardinal *no_params) { Dimension height; long y; Arg arg; if (*no_params != 1 || (!IS_DIGIT(params[0][0]) && params[0][0] != '-')) { if (global.bell) XBell(display, 0); return; } y = atoi(params[0]); XtSetArg(arg, XtNheight, &height); XtGetValues(main_widgets.top_layout, &arg, 1); y += height; if (y < 0) y = 1; else if (y > 32767) y = 32767; XtSetArg(arg, XtNheight, y); XtSetValues(main_widgets.top_layout, &arg, 1); } void action_popup_find_group(Widget w, XEvent *event, String *params, Cardinal *no_params) { if (global.busy) return; popup_find_group(); } void action_do_the_right_thing(Widget w, XEvent *event, String *params, Cardinal *no_params) { long n, shown, pos; switch (global.mode) { case NewsModeDisconnected: if (global.busy) return; knapp1_callback(w, NULL, NULL); break; case NewsModeConnected: if (global.busy) return; n = ScrollableGetVSize(main_widgets.group_list); if (n <= 0) /* no unread groups; rescan */ knapp8_callback(w, NULL, NULL); else knapp7_callback(w, NULL, NULL); break; case NewsModeGroup: case NewsModeThread: n = ScrollableGetVSize(main_widgets.text); shown = ScrollableGetVShown(main_widgets.text); pos = ScrollableGetVPos(main_widgets.text); if (global.curr_art && n > 0 && pos + shown < n) ScrollablePage(main_widgets.text, *no_params > 0 ? atof(params[0]) : 0.95); else if (!global.busy) knapp7_callback(w, NULL, NULL); break; case NewsModeAllgroups: case NewsModeSomegroups: if (global.busy) return; knapp7_callback(w, NULL, NULL); break; case NewsModeNewgroups: break; } } void action_tree_layout(Widget w, XEvent *event, String *params, Cardinal *no_params) { Arg args[3]; int n = *no_params; int vertical; if (global.mode != NewsModeThread && global.mode != NewsModeGroup) return; if (n < 1) return; vertical = case_lstrcmp(params[0], "true") == 0 || case_lstrcmp(params[0], "yes") == 0 || case_lstrcmp(params[0], "on") == 0; XtSetArg(args[0], XtNvertical, vertical); if (*no_params > 1) XtSetArg(args[1], XtNnodeColumns, atoi(params[1])); if (*no_params > 2) XtSetArg(args[2], XtNcolumnSpacing, atoi(params[2])); if (*no_params > 3) XtSetArg(args[3], XtNrowSpacing, atoi(params[3])); XtSetValues(main_widgets.arttree, args, n); } ./knews-1.0b.1/src/read.h100644 1244 1244 523 6455455546 13473 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ extern int read_article(ARTICLE*, int, long*, char**); extern char *do_mime(ARTICLE*, struct SERVER*, char *, int, char*, int, char**); extern void action_read_article(Widget, XEvent*, String*, Cardinal*); extern void action_mime_hack(Widget, XEvent*, String*, Cardinal*); ./knews-1.0b.1/src/search.c100644 1244 1244 100000 6455455546 14067 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include "global.h" #include #include "codes.h" #include "connect.h" #include "newsrc.h" #include "read.h" #include "search.h" #include "server.h" #include "tag.h" #include "thread.h" #include "util.h" #include "widgets.h" #include "xutil.h" #include "../Widgets/Dialogue.h" #include "../Widgets/FileSel.h" #include "../Widgets/Knapp.h" #include "../Widgets/Layout.h" #include "../Widgets/Message.h" #include "../Widgets/Notice.h" #include "../Widgets/Sash.h" #include "../Widgets/Scrollable.h" #include "../Widgets/ScrBar.h" #include "../Widgets/ScrList.h" #include "../Widgets/TextField.h" #include "../Widgets/Toggle.h" #include "../Widgets/Util.h" typedef enum { SearchModeRegexp, SearchModeXpat, SearchModeNewsgroups } SearchMode; typedef enum { SearchScopeAll, SearchScopeThread, SearchScopeSubthread, SearchScopeTagged } SearchScope; static SearchMode search_mode = SearchModeRegexp; static SearchScope search_scope = SearchScopeAll; static int only_unread = True; static int stopped = False; static int doing_xpat = False; static long *xpats = NULL; static long n_xpats = 0; static long curr_xpat = -1; static struct { Widget shell; /***/ Widget regexp_field; Widget wildcard_field; Widget header_field; /***/ Widget from_toggle; Widget subject_toggle; Widget head_toggle; Widget body_toggle; Widget unread_toggle; /***/ Widget all_scope; Widget thread_scope; Widget subthread_scope; Widget tagged_scope; /***/ Widget search_knapp; Widget submit_knapp; Widget clear_knapp; Widget next_knapp; Widget first_knapp; Widget stop_knapp; } search_widgets; static ARTICLE *next_in_order(ARTICLE*); static void set_no_art_message(void); /*************************************************************************/ static void clear_xpat_data(void) { XtFree((char *)xpats); xpats = NULL; n_xpats = 0; curr_xpat = -1; } static void set_xpat_message(int error) { if (curr_xpat >= 0 && n_xpats > 0) { char message[80]; if (error) sprintf(message, "Couldn't find match %ld", curr_xpat + 1); else sprintf(message, "Match %ld of %ld", curr_xpat + 1, n_xpats); set_message(message, error); } } static ARTICLE *no_to_art(long no, int even_if_read) { ARTICLE *art; for (art = get_articles(main_thr) ; art ; art = art->next) if (art->no == no) { if (art->read && !even_if_read) return NULL; else return art; } return NULL; } static void set_scope_toggle(SearchScope scope, int set) { Widget w = NULL; switch (scope) { case SearchScopeAll: w = search_widgets.all_scope; break; case SearchScopeThread: w = search_widgets.thread_scope; break; case SearchScopeSubthread: w = search_widgets.subthread_scope; break; case SearchScopeTagged: w = search_widgets.tagged_scope; break; default: return; } ToggleSet(w, set); } static void do_subj_toggle(int set) { XtSetSensitive(search_widgets.unread_toggle, !set); XtSetSensitive(search_widgets.all_scope, !set); XtSetSensitive(search_widgets.thread_scope, !set); XtSetSensitive(search_widgets.subthread_scope, !set); XtSetSensitive(search_widgets.tagged_scope, !set); } static void set_search_mode(SearchMode mode) { int tmp; search_mode = mode; if (mode != SearchModeXpat) clear_xpat_data(); XtSetSensitive(search_widgets.header_field, mode != SearchModeNewsgroups); XtSetSensitive(search_widgets.wildcard_field, mode != SearchModeNewsgroups); XtSetSensitive(search_widgets.from_toggle, mode == SearchModeRegexp); XtSetSensitive(search_widgets.subject_toggle, mode == SearchModeRegexp); XtSetSensitive(search_widgets.head_toggle, mode == SearchModeRegexp); XtSetSensitive(search_widgets.body_toggle, mode == SearchModeRegexp); if (mode == SearchModeRegexp) tmp = !ToggleGet(search_widgets.subject_toggle); else tmp = False; XtSetSensitive(search_widgets.unread_toggle, tmp); XtSetSensitive(search_widgets.all_scope, tmp); XtSetSensitive(search_widgets.thread_scope, tmp); XtSetSensitive(search_widgets.subthread_scope, tmp); XtSetSensitive(search_widgets.tagged_scope, tmp); KnappSetSensitive(search_widgets.search_knapp, mode != SearchModeXpat); KnappSetSensitive(search_widgets.submit_knapp, mode == SearchModeXpat); KnappSetSensitive(search_widgets.clear_knapp, mode == SearchModeXpat && xpats != NULL); KnappSetSensitive(search_widgets.next_knapp, mode == SearchModeXpat && curr_xpat >= 0 && curr_xpat < n_xpats - 1); KnappSetSensitive(search_widgets.first_knapp, mode == SearchModeXpat && n_xpats > 0); } static int valid_header(char *c) { while (*c != '\0') if (!isalpha((unsigned char)*c) && !isdigit((unsigned char)*c) && *c != '-' && *c != '/') return False; else c++; return True; } /*************************************************************************/ static void do_group_search(regex_t *re) { long i; i = ScrListGetFirstSelected(main_widgets.group_list); if (i < 0) i = 0; else ScrListSetSelected(main_widgets.group_list, i++, False); while (i < global.no_groups) { if (regexec(re, global.groups[i]->name, 0, NULL, 0) == 0) break; i++; } if (i >= global.no_groups) set_message("No match!", True); else { global.curr_group = global.groups[i]; ScrListSetSelected(main_widgets.group_list, i, True); ScrListMakeVisible(main_widgets.group_list, i); } } static void do_subj_search(regex_t *re) { SUBJECT *subj; subj = global.curr_subj; if (subj) subj = subj->next; if (!subj) subj = get_subjects(main_thr); while (subj) { if (subj->disp >= 0 && regexec(re, subj->subject, 0, NULL, 0) == 0) break; subj = subj->next; } if (!subj) set_message("No matches!", True); else { global.curr_art = NULL; if (global.mode != NewsModeThread) set_curr_subj(subj); else { global.curr_subj = subj; setNewsModeGroup(False); } } } static void do_from_search(regex_t *re) { ARTICLE *art; art = next_in_order(NULL); if (!art) { set_no_art_message(); return; } do { if (art->from && regexec(re, art->from, 0, NULL, 0) == 0) break; art = next_in_order(art); } while (art); if (!art) set_message("No matches!", True); else { set_curr_art(art, True); if (global.mode == NewsModeThread) setNewsModeThread(); read_article(art, False, NULL, NULL); } } static void do_art_search(regex_t *re) { char command[80], message[80]; char *cp, *mp, *reply; regmatch_t pmatch[1]; ARTICLE *art; long sel_data[3]; long n = 0, n_arts = 0; int head = True; int in_head = False; if (ToggleGet(search_widgets.head_toggle)) if (ToggleGet(search_widgets.body_toggle)) strcpy(command, "ARTICLE "); else strcpy(command, "HEAD "); else if (ToggleGet(search_widgets.body_toggle)) { strcpy(command, "BODY "); head = False; } else { set_message("Nothing to search!", True); return; } cp = command + strlen(command); art = next_in_order(NULL); if (!art) { set_no_art_message(); return; } strcpy(message, "Searching... "); mp = message + strlen(message); set_busy(True); stopped = False; XtSetSensitive(search_widgets.stop_knapp, True); do { sprintf(mp, "%ld", n_arts++); set_message(message, False); sprintf(cp, "%ld\r\n", art->no); reply = server_comm(main_server, command, True); if (!reply) break; n = atoi(reply); if (n != NNTP_OK_ARTICLE && n != NNTP_OK_HEAD && n != NNTP_OK_BODY) fprintf(stderr, "knews: Failed to get article %ld, " "message from server is: %s\n", art->no, reply); else { n = 0; in_head = head; reply = server_read(main_server); while (reply && !IS_DOT(reply)) { if (regexec(re, reply, 1, pmatch, 0) == 0) break; n++; if (in_head && reply[0] == '\0') { in_head = False; n = 0; } reply = server_read(main_server); } if (!reply || !IS_DOT(reply)) break; } art = next_in_order(art); } while (!stopped && art); while (reply && !IS_DOT(reply)) reply = server_read(main_server); XtSetSensitive(search_widgets.stop_knapp, False); if (!reply) { reconnect_server(True); unset_busy(); return; } unset_busy(); if (!art) { set_message("No matches!", True); return; } if (stopped) { set_message("Search stopped!", False); return; } if (in_head) n = - n - 1; sel_data[0] = n; sel_data[1] = pmatch[0].rm_so; sel_data[2] = pmatch[0].rm_eo - 1; set_curr_art(art, True); if (global.mode == NewsModeThread) setNewsModeThread(); read_article(art, True, sel_data, NULL); } static void do_xpat_search(char *header, char *wildcard) { char command[512]; char *buffer, *c; long n_alloc = 0; clear_xpat_data(); KnappSetSensitive(search_widgets.first_knapp, False); KnappSetSensitive(search_widgets.clear_knapp, False); KnappSetSensitive(search_widgets.next_knapp, False); if (strlen(header) + strlen(wildcard) > sizeof(command) - 64) { set_message("Header or wildcard too long!", True); return; } if (!global.curr_group || global.curr_group->first_art > global.curr_group->last_art) { set_message("No articles!", True); return; } sprintf(command, "XPAT %s %ld-%ld %s\r\n", header, global.curr_group->first_art, global.curr_group->last_art, wildcard); set_busy(True); set_message("Server contacted, waiting for response, " "this may take some time...", False); buffer = server_comm(main_server, command, True); if (!buffer) { reconnect_server(True); unset_busy(); return; } if (atoi(buffer) != NNTP_OK_HEAD) { unset_busy(); if (strlen(buffer) > 100) buffer[100] = '\0'; sprintf(command, "Error! Message from server is: %s", buffer); set_message(command, True); return; } sprintf(command, "Number of matches: "); c = command + strlen(command); n_xpats = 0; doing_xpat = True; buffer = server_read(main_server); while (buffer && !IS_DOT(buffer)) { long no; if (sscanf(buffer, "%ld", &no) == 1 && no_to_art(no, False)) { if (n_xpats > n_alloc - 2) { n_alloc = 2 * (n_alloc + 1); xpats = (long *)XtRealloc((char *)xpats, n_alloc * sizeof(long)); } xpats[n_xpats++] = no; sprintf(c, "%ld", n_xpats); set_message(command, False); } buffer = server_read(main_server); } doing_xpat = False; if (!buffer) { clear_xpat_data(); reconnect_server(True); unset_busy(); return; } unset_busy(); if (n_xpats == 0) set_message("No matches!", True); else { ARTICLE *art = no_to_art(xpats[0], True); curr_xpat = 0; set_curr_art(art, True); if (global.mode == NewsModeThread) setNewsModeThread(); read_article(art, False, NULL, NULL); set_xpat_message(art == NULL); KnappSetSensitive(search_widgets.submit_knapp, False); KnappSetSensitive(search_widgets.first_knapp, True); KnappSetSensitive(search_widgets.next_knapp, n_xpats > 1); KnappSetSensitive(search_widgets.clear_knapp, True); } } static void submit_knapp_callback(Widget w, XtPointer client_data, XtPointer call_data) { if (global.busy) return; if (search_mode == SearchModeXpat) { char *header, *wildcard; if (global.mode != NewsModeGroup && global.mode != NewsModeThread) return; header = TextFieldGetBuffer(search_widgets.header_field); wildcard = TextFieldGetBuffer(search_widgets.wildcard_field); if (header && wildcard) if (valid_header(header)) do_xpat_search(header, wildcard); else set_message("Invalid header name!", True); else if (global.bell) XBell(display, 0); XtFree(header); XtFree(wildcard); } } static void search_knapp_callback(Widget w, XtPointer client_data, XtPointer call_data) { char *buffer; regex_t re; int code; if (global.busy || global.mode == SearchModeXpat) return; buffer = TextFieldGetBuffer(search_widgets.regexp_field); code = regcomp(&re, buffer, (global.icase_regexps ? REG_ICASE : 0) | REG_EXTENDED); XtFree(buffer); if (code != 0) { popup_regexpnotice(code, &re); return; } if (search_mode == SearchModeNewsgroups) do_group_search(&re); else if (ToggleGet(search_widgets.subject_toggle)) do_subj_search(&re); else if (ToggleGet(search_widgets.from_toggle)) do_from_search(&re); else do_art_search(&re); regfree(&re); } static void close_knapp_callback(Widget w, XtPointer client_data, XtPointer call_data) { if (!doing_xpat) popdown_search(); } static void stop_knapp_callback(Widget w, XtPointer client_data, XtPointer call_data) { stopped = True; XtSetSensitive(w, False); set_message("Search stopped, waiting for last article...", True); } static void clear_knapp_callback(Widget w, XtPointer client_data, XtPointer call_data) { if (!global.busy && search_mode == SearchModeXpat) { clear_xpat_data(); KnappSetSensitive(search_widgets.submit_knapp, True); KnappSetSensitive(search_widgets.clear_knapp, False); KnappSetSensitive(search_widgets.next_knapp, False); KnappSetSensitive(search_widgets.first_knapp, False); } } static void first_knapp_callback(Widget w, XtPointer client_data, XtPointer call_data) { if (!global.busy && search_mode == SearchModeXpat && n_xpats > 0) { ARTICLE *art = no_to_art(xpats[0], True); curr_xpat = 0; set_curr_art(art, True); if (global.mode == NewsModeThread) setNewsModeThread(); read_article(art, False, NULL, NULL); set_xpat_message(art == NULL); KnappSetSensitive(search_widgets.next_knapp, n_xpats > 1); } } static void next_knapp_callback(Widget w, XtPointer client_data, XtPointer call_data) { if (!global.busy && search_mode == SearchModeXpat && curr_xpat >= 0 && curr_xpat + 1 < n_xpats) { ARTICLE *art = no_to_art(xpats[++curr_xpat], True); set_curr_art(art, True); if (global.mode == NewsModeThread) setNewsModeThread(); read_article(art, False, NULL, NULL); set_xpat_message(art == NULL); KnappSetSensitive(search_widgets.next_knapp, curr_xpat + 1 < n_xpats); } } static void from_toggle_callback(Widget w, XtPointer client_data, XtPointer call_data) { Boolean *set = (Boolean *)call_data; if (global.busy || !set) return; *set = !*set; if (*set) { ToggleSet(search_widgets.subject_toggle, False); ToggleSet(search_widgets.head_toggle, False); ToggleSet(search_widgets.body_toggle, False); do_subj_toggle(False); } } static void subject_toggle_callback(Widget w, XtPointer client_data, XtPointer call_data) { Boolean *set = (Boolean *)call_data; if (global.busy || !set) return; *set = !*set; if (*set) { ToggleSet(search_widgets.from_toggle, False); ToggleSet(search_widgets.head_toggle, False); ToggleSet(search_widgets.body_toggle, False); } do_subj_toggle(*set); } static void head_toggle_callback(Widget w, XtPointer client_data, XtPointer call_data) { Boolean *set = (Boolean *)call_data; if (global.busy || !set) return; *set = !*set; if (*set) { ToggleSet(search_widgets.from_toggle, False); ToggleSet(search_widgets.subject_toggle, False); do_subj_toggle(False); } } static void body_toggle_callback(Widget w, XtPointer client_data, XtPointer call_data) { Boolean *set = (Boolean *)call_data; if (global.busy || !set) return; *set = !*set; if (*set) { ToggleSet(search_widgets.from_toggle, False); ToggleSet(search_widgets.subject_toggle, False); do_subj_toggle(False); } } static void xpat_field_callback(Widget w, XtPointer client_data, XtPointer call_data) { if (global.busy) return; if (w == search_widgets.wildcard_field) XtSetKeyboardFocus(search_widgets.shell, search_widgets.header_field); else XtSetKeyboardFocus(search_widgets.shell, search_widgets.wildcard_field); } static void focus_callback(Widget w, XtPointer client_data, XtPointer call_data) { if (global.busy) return; if (global.mode == NewsModeAllgroups) set_search_mode(SearchModeNewsgroups); else if (w == search_widgets.regexp_field) set_search_mode(SearchModeRegexp); else set_search_mode(SearchModeXpat); } static void scope_callback(Widget w, XtPointer client_data, XtPointer call_data) { SearchScope scope = (SearchScope)client_data; if (global.busy || scope == search_scope) return; set_scope_toggle(search_scope, False); search_scope = scope; set_scope_toggle(search_scope, True); } static void unread_toggle_callback(Widget w, XtPointer client_data, XtPointer call_data) { Boolean *set = (Boolean *)call_data; if (global.busy || !set) return; *set = !*set; only_unread = *set; } static void tab_callback(Widget w, XtPointer client_data, XtPointer call_data) { if (global.busy || (global.mode != NewsModeGroup && global.mode != NewsModeThread)) return; if (ToggleGet(search_widgets.from_toggle)) { ToggleSet(search_widgets.from_toggle, False); ToggleSet(search_widgets.subject_toggle, True); do_subj_toggle(True); } else if (ToggleGet(search_widgets.subject_toggle)) { ToggleSet(search_widgets.subject_toggle, False); ToggleSet(search_widgets.head_toggle, True); do_subj_toggle(False); } else if (ToggleGet(search_widgets.head_toggle)) { if (ToggleGet(search_widgets.body_toggle)) ToggleSet(search_widgets.head_toggle, False); else ToggleSet(search_widgets.body_toggle, True); } else { ToggleSet(search_widgets.body_toggle, False); ToggleSet(search_widgets.from_toggle, True); } } /*************************************************************************/ static void create_search_widgets(void) { Arg args[8]; Widget layout; Widget w; XtSetArg(args[0], XtNallowShellResize, True); XtSetArg(args[1], XtNinput, True); XtSetArg(args[2], XtNcolormap, global.cmap); XtSetArg(args[3], XtNvisual, global.visual); XtSetArg(args[4], XtNdepth, global.depth); search_widgets.shell = XtCreatePopupShell("searchshell", topLevelShellWidgetClass, main_widgets.shell, args, 5); layout = XtVaCreateManagedWidget("searchlayout", layoutWidgetClass, search_widgets.shell, XtVaTypedArg, XtNlayout, XtRString, #include "layouts/search.h" (int)sizeof(String), (void *)0); XtCreateManagedWidget("regexptitle", messageWidgetClass, layout, NULL, 0); XtCreateManagedWidget("xpattitle", messageWidgetClass, layout, NULL, 0); XtCreateManagedWidget("sash", sashWidgetClass, layout, NULL, 0); XtSetArg(args[0], XtNcenter, False); XtCreateManagedWidget("regexpmessage", messageWidgetClass, layout, args, 1); XtCreateManagedWidget("wildcardmessage", messageWidgetClass, layout, args, 1); XtCreateManagedWidget("headermessage", messageWidgetClass, layout, args, 1); XtSetArg(args[0], XtNfocusRoot, search_widgets.shell); XtSetArg(args[1], XtNsingleLine, True); search_widgets.regexp_field = XtCreateManagedWidget("regexpfield", textFieldWidgetClass, layout, args, 2); XtAddCallback(search_widgets.regexp_field, XtNcallback, search_knapp_callback, NULL); XtAddCallback(search_widgets.regexp_field, XtNtabCallback, tab_callback, NULL); XtAddCallback(search_widgets.regexp_field, XtNfocusCallback, focus_callback, NULL); search_widgets.wildcard_field = XtCreateManagedWidget("wildcardfield", textFieldWidgetClass, layout, args, 2); XtAddCallback(search_widgets.wildcard_field, XtNcallback, xpat_field_callback, NULL); XtAddCallback(search_widgets.wildcard_field, XtNtabCallback, xpat_field_callback, NULL); XtAddCallback(search_widgets.wildcard_field, XtNfocusCallback, focus_callback, NULL); search_widgets.header_field = XtCreateManagedWidget("headerfield", textFieldWidgetClass, layout, args, 2); XtAddCallback(search_widgets.header_field, XtNcallback, xpat_field_callback, NULL); XtAddCallback(search_widgets.header_field, XtNtabCallback, xpat_field_callback, NULL); XtAddCallback(search_widgets.header_field, XtNfocusCallback, focus_callback, NULL); search_widgets.search_knapp = XtCreateManagedWidget("search", knappWidgetClass, layout, NULL, 0); XtAddCallback(search_widgets.search_knapp, XtNcallback, search_knapp_callback, NULL); search_widgets.submit_knapp = XtCreateManagedWidget("submit", knappWidgetClass, layout, NULL, 0); XtAddCallback(search_widgets.submit_knapp, XtNcallback, submit_knapp_callback, NULL); search_widgets.clear_knapp = XtCreateManagedWidget("clear", knappWidgetClass, layout, NULL, 0); XtAddCallback(search_widgets.clear_knapp, XtNcallback, clear_knapp_callback, NULL); search_widgets.next_knapp = XtCreateManagedWidget("next", knappWidgetClass, layout, NULL, 0); XtAddCallback(search_widgets.next_knapp, XtNcallback, next_knapp_callback, NULL); search_widgets.first_knapp = XtCreateManagedWidget("first", knappWidgetClass, layout, NULL, 0); XtAddCallback(search_widgets.first_knapp, XtNcallback, first_knapp_callback, NULL); w = XtCreateManagedWidget("close", knappWidgetClass, layout, NULL, 0); XtAddCallback(w, XtNcallback, close_knapp_callback, NULL); XtSetArg(args[0], XtNsensitive, False); search_widgets.stop_knapp = XtCreateManagedWidget("stop", knappWidgetClass, layout, args, 1); XtAddCallback(search_widgets.stop_knapp, XtNcallback, stop_knapp_callback, NULL); search_widgets.unread_toggle = XtCreateManagedWidget("unreadtoggle", toggleWidgetClass, layout, NULL, 0); XtAddCallback(search_widgets.unread_toggle, XtNcallback, unread_toggle_callback, NULL); /***/ search_widgets.from_toggle = XtCreateManagedWidget("fromtoggle", toggleWidgetClass, layout, NULL, 0); XtAddCallback(search_widgets.from_toggle, XtNcallback, from_toggle_callback, NULL); search_widgets.subject_toggle = XtCreateManagedWidget("subjecttoggle", toggleWidgetClass, layout, NULL, 0); XtAddCallback(search_widgets.subject_toggle, XtNcallback, subject_toggle_callback, NULL); search_widgets.head_toggle = XtCreateManagedWidget("headtoggle", toggleWidgetClass, layout, NULL, 0); XtAddCallback(search_widgets.head_toggle, XtNcallback, head_toggle_callback, NULL); search_widgets.body_toggle = XtCreateManagedWidget("bodytoggle", toggleWidgetClass, layout, NULL, 0); XtAddCallback(search_widgets.body_toggle, XtNcallback, body_toggle_callback, NULL); if (ToggleGet(search_widgets.head_toggle) || ToggleGet(search_widgets.body_toggle)) { ToggleSet(search_widgets.from_toggle, False); ToggleSet(search_widgets.subject_toggle, False); } else { ToggleSet(search_widgets.head_toggle, False); ToggleSet(search_widgets.body_toggle, False); ToggleSet(search_widgets.subject_toggle, !ToggleGet(search_widgets.from_toggle)); } /***/ search_widgets.all_scope = XtCreateManagedWidget("allscope", toggleWidgetClass, layout, NULL, 0); XtAddCallback(search_widgets.all_scope, XtNcallback, scope_callback, (XtPointer)SearchScopeAll); search_widgets.thread_scope = XtCreateManagedWidget("threadscope", toggleWidgetClass, layout, NULL, 0); XtAddCallback(search_widgets.thread_scope, XtNcallback, scope_callback, (XtPointer)SearchScopeThread); search_widgets.subthread_scope = XtCreateManagedWidget("subthreadscope", toggleWidgetClass, layout, NULL, 0); XtAddCallback(search_widgets.subthread_scope, XtNcallback, scope_callback, (XtPointer)SearchScopeSubthread); search_widgets.tagged_scope = XtCreateManagedWidget("taggedscope", toggleWidgetClass, layout, NULL, 0); XtAddCallback(search_widgets.tagged_scope, XtNcallback, scope_callback, (XtPointer)SearchScopeTagged); XtSetKeyboardFocus(search_widgets.shell,search_widgets.regexp_field); XtRealizeWidget(search_widgets.shell); XtInstallAllAccelerators(search_widgets.shell, search_widgets.shell); add_WM_DELETE_WINDOW_callback(search_widgets.shell, close_knapp_callback, NULL); if (global.busy) set_busy_search(True); } void popup_search(void) { if (global.busy && global.mode == NewsModeAllgroups) return; if (!search_widgets.shell) { create_search_widgets(); set_search_mode(SearchModeRegexp); } if (global.mode == NewsModeAllgroups) set_search_mode(SearchModeNewsgroups); else if (search_mode == SearchModeNewsgroups) set_search_mode(SearchModeRegexp); XtPopup(search_widgets.shell, XtGrabNone); if (global.busy) set_busy_search(True); } void popdown_search(void) { if (!search_widgets.shell) return; if (search_mode == SearchModeXpat) { clear_xpat_data(); set_search_mode(SearchModeXpat); } XtPopdown(search_widgets.shell); } void set_busy_search(int busy) { if (!search_widgets.shell) return; XDefineCursor(display, XtWindow(search_widgets.shell), busy ? global.busy_cursor : global.cursor); KnappSetActive(search_widgets.search_knapp, !busy); KnappSetActive(search_widgets.submit_knapp, !busy); KnappSetActive(search_widgets.clear_knapp, !busy); KnappSetActive(search_widgets.next_knapp, !busy); KnappSetActive(search_widgets.first_knapp, !busy); TextFieldSetActive(search_widgets.regexp_field, !busy); TextFieldSetActive(search_widgets.header_field, !busy); TextFieldSetActive(search_widgets.wildcard_field, !busy); } /*************************************************************************/ static Widget find_group_widget = NULL; static long getnmatching(char *c1, char *c2) { long n = 0; while (*c1 == *c2 && *c1 != '\0') { c1++; c2++; n++; } return n; } static void find_group_callback(Widget w, XtPointer client_data, XtPointer call_data) { DialogueReport *report = (DialogueReport *)call_data; Arg arg; if (global.busy || !report || (global.mode != NewsModeConnected && global.mode != NewsModeAllgroups && global.mode != NewsModeSomegroups)) return; switch (report->reply) { case DialogueReplyEnter: /* find group */ case DialogueReplyLeft: if (!report->buffer || report->buffer[0] == '\0') XtPopdown(w); else if (!report->buffer || strlen(report->buffer) > 500) { if (global.bell) XBell(display, 0); } else { char command[512]; char *reply; set_busy(True); set_message("Asking server about group...", False); sprintf(command, "GROUP %s\r\n", report->buffer); reply = server_comm(main_server, command, True); if (!reply) { reconnect_server(True); unset_busy(); break; } unset_busy(); if (atoi(reply) == NNTP_OK_GROUP) { XtPopdown(w); global.curr_group = find_group(report->buffer); if (!global.curr_group) { global.curr_group = create_group(report->buffer); sort_groups(); } if (global.mode == NewsModeAllgroups) setNewsModeAllgroups(NULL); /* if we're aborted later */ read_group(reply, True, 0); } else { if (strlen(reply) > 80) reply[80] = '\0'; sprintf(command, "Error! Message from server is: %s", reply); set_message(command, True); } } break; case DialogueReplyMiddle: /* regexp */ { regex_t re; int code; code = regcomp(&re, report->buffer, REGEXP_COMPILE_FLAGS); if (code != 0) { popup_regexpnotice(code, &re); return; } XtPopdown(w); setNewsModeAllgroups(&re); regfree(&re); } break; case DialogueReplyTab: /* complete */ if (report->buffer && report->buffer[0] != '\0') { long len = strlen(report->buffer); long first, last, n, sub, first_sub; char *c; for (first = 0 ; first < global.no_groups ; first++) if (!global.groups[first]->subscribed) break; while (first < global.no_groups) { if (strncmp(global.groups[first]->name, report->buffer, len) == 0) break; first++; } for (last = first + 1 ; last < global.no_groups ; last++) if (strncmp(global.groups[last]->name, report->buffer, len) != 0) break; if (first == global.no_groups) { c = NULL; n = 0; first = -1; } else { c = global.groups[first]->name; n = getnmatching(c, global.groups[last-1]->name); } first_sub = -1; for (sub = 0 ; sub < global.no_groups ; sub++) if (!global.groups[sub]->subscribed) break; else if (strncmp(global.groups[sub]->name, report->buffer, len) == 0) { if (c) { long temp; temp = getnmatching(c, global.groups[sub]->name); if (temp < n) n = temp; } else { c = global.groups[sub]->name; n = strlen(c); } if (first_sub < 0) first_sub = sub; } if (!c || n > 500) { if (global.bell) XBell(display, 0); } else { char buffer[512]; memcpy(buffer, c, n); buffer[n] = '\0'; XtSetArg(arg, XtNbuffer, buffer); XtSetValues(w, &arg, 1); if (first < 0) first = first_sub; if (global.mode == NewsModeAllgroups) ScrollableSetVPos(main_widgets.group_list, first); } } break; case DialogueReplyClose: /* cancel */ case DialogueReplyRight: XtPopdown(w); break; } } void popup_find_group(void) { if (global.busy || (global.mode != NewsModeConnected && global.mode != NewsModeAllgroups && global.mode != NewsModeSomegroups)) return; if (find_group_widget) popup_under_pointer(find_group_widget, XtGrabNone); else find_group_widget = popup_dialogue("findgroup", "Find group(s) by name/regexp", "Goto group", "Regexp", "Cancel", find_group_callback, NULL, XtGrabNone); } void popdown_find_group(void) { if (find_group_widget) XtPopdown(find_group_widget); } /*************************************************************************/ static int want_subj(SUBJECT *subj) { int visible = False; int has_unread = !only_unread; ARTICLE *thr = subj->thread; do { if (subj->disp >= 0) { visible = True; if (has_unread) return True; } if (subj->no_unread > 0) { has_unread = True; if (visible) return True; } subj = subj->next; } while (subj && subj->thread == thr); return False; } static SUBJECT *next_subject(SUBJECT *subj) { do { while (subj->next && subj->next->thread == subj->thread) subj = subj->next; subj = subj->next; } while (subj && !want_subj(subj)); return subj; } static ARTICLE *next_in_order(ARTICLE *art) { SUBJECT *subj; ARTICLE **arts; long n; switch (search_scope) { case SearchScopeAll: if (!art) art = global.curr_art; if (art) { subj = art->subject; art = next_in_thread_preorder(art); } else { subj = global.curr_subj; if (subj) art = subj->thread; else return NULL; } while (subj) { while (art && (!art->from || (only_unread && art->read))) art = next_in_thread_preorder(art); if (art) break; subj = next_subject(subj); if (subj) art = subj->thread; } break; case SearchScopeThread: if (art) art = next_in_thread_preorder(art); else { art = global.curr_art; if (art) art = art->next; else if (global.curr_subj) art = global.curr_subj->thread; } while (art && (!art->from || (only_unread && art->read))) art = next_in_thread_preorder(art); break; case SearchScopeSubthread: { static ARTICLE *thread; if (art) art = next_in_subthread_preorder(art, thread); else art = thread = global.curr_art; while (art && (!art->from || (only_unread && art->read))) art = next_in_subthread_preorder(art, thread); } break; case SearchScopeTagged: n = no_tagged_articles(); if (n <= 0) return NULL; arts = get_tagged_articles(); while (n-- > 0) { art = *arts++; if (art->from && (!only_unread || !art->read)) { untag_article(art); return art; } } return NULL; } return art; } static void set_no_art_message(void) { char *p = "No more articles to search!"; if (search_scope == SearchScopeTagged && no_tagged_articles() <= 0) p = "No tagged articles!"; set_message(p, True); } ./knews-1.0b.1/src/search.h100644 1244 1244 351 6455455546 14024 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ extern void popup_search(void); extern void popdown_search(void); extern void popup_find_group(void); extern void popdown_find_group(void); extern void set_busy_search(int); ./knews-1.0b.1/src/server.c100644 1244 1244 22542 6455455546 14126 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include "global.h" #include "child.h" #include "codes.h" #include "connect.h" #include "file.h" #include "resource.h" #include "server.h" #include "util.h" #include "widgets.h" #include "xutil.h" #include "sysdeps.h" struct SERVER { int fd; char *buffer; long curr_pos; long len; FILE *bs; /* backing store */ QuitFunc quit_func; int aborted; }; #define BUFFERLEN (8192 - 16) SERVER *server_create(int fd) { SERVER *server; server = (SERVER *)XtMalloc(sizeof *server); server->fd = fd; server->buffer = XtMalloc(BUFFERLEN + 3); server->len = BUFFERLEN; server->curr_pos = 0; server->bs = NULL; server->quit_func = NULL; server->buffer[0] = '\0'; return server; } void server_free(SERVER *server) { server_close(server); XtFree(server->buffer); server->buffer = NULL; server->len = 0; server->bs = NULL; server->quit_func = NULL; server->aborted = False; XtFree((char *)server); } void server_close(SERVER *server) { server->curr_pos = 0; server->buffer[0] = '\0'; server->bs = NULL; server->aborted = False; if (server->fd >= 0 && close(server->fd) < 0) perror("knews: close"); server->fd = -1; } int server_get_fd(SERVER *server) { return server->fd; } void server_set_fd(SERVER *server, int fd) { server->fd = fd; } void server_set_bs(SERVER *server, FILE *bs) { server->bs = bs; } void server_set_quit_func(SERVER *server, QuitFunc quit_func) { server->quit_func = quit_func; } QuitFunc server_get_quit_func(SERVER *server) { return server->quit_func; } int server_aborted(SERVER *server) { return server->aborted; } void nntp_quit(void *data) { SERVER *server = data; if (server->fd < 0) return; server_write_raw(server, "\r\nQUIT\r\n", 8); server_close(server); server->aborted = True; } void nntp_just_close(void *data) { SERVER *server = data; if (server->fd < 0) return; server_close(server); server->aborted = True; } /*************************************************************************/ /* * Will block iff tell > 0. * * tell != 0 means tell about errors * tell > 1 means say what you're doing */ int server_open(SERVER *server, struct SERV_ADDR *addr, int tell) { long tmp; server_close(server); server->fd = open_socket(); if (server->fd < 0) { set_message("Error: Failed to create socket!", True); return -1; } if (tell > 1) set_message("Server contacted, waiting for response...", False); tmp = connect_socket(server->fd, addr); if (tmp < 0 && would_block(server->fd, errno)) { if (tell == 0) return 0; /* don't block */ tmp = 0; do_wait(&server->fd, True, server->quit_func, server); if (server->fd < 0) return -1; } server->buffer[0] = '\0'; if (tmp >= 0) do { tmp = read(server->fd, server->buffer, server->len); } while (tmp < 0 && errno == EINTR); if (tmp >= 0) { server->buffer[tmp] = '\0'; return 0; } if (would_block(server->fd, errno)) return 0; perror("knews: read"); if (tell) { char message[128]; char *tmp = error_string(errno); if (!tmp) tmp = "Connection failed"; sprintf(message, "Error: %s!", tmp); set_message(message, True); } return -1; } int server_fork(SERVER *server, char *command, int tell) { static char *cmd = NULL; pid_t pid; int fd[2]; if (command) cmd = command; server_close(server); if (open_duplex(fd) < 0) { if (tell) set_message("Error! Failed to open duplex connection!", True); return -1; } pid = fork_nicely(NULL, NULL, False); if (pid < 0) { close(fd[0]); close(fd[1]); if (tell) set_message("Error! Fork failed!", True); return -1; } if (pid == 0) { char *c; c = CODE_TO_STR(NNTP_ERR_FAULT) "%d This ain't happening, man!\r\n"; if (fd[1] != STDIN_FILENO) { if (dup2(fd[1], STDIN_FILENO) != STDIN_FILENO) { perror("knews: dup2"); write(STDOUT_FILENO, c, strlen(c)); _exit(0); } close(fd[1]); } if (dup2(STDIN_FILENO, STDOUT_FILENO) != STDOUT_FILENO) { perror("knews: dup2"); write(STDOUT_FILENO, c, strlen(c)); _exit(0); } execl(BIN_SH, "sh", "-c", cmd, (char *)0); perror("knews: execl " BIN_SH); write(STDOUT_FILENO, c, strlen(c)); _exit(0); } close(fd[1]); server->fd = fd[0]; return 0; } /*************************************************************************/ long server_write_raw(SERVER *server, char *message, long n) { long i = 0; if (server->fd < 0) return -1; if (n > 0) do { i = write(server->fd, message, n); } while (i < 0 && errno == EINTR); return 0; } int server_write(SERVER *server, char *message) { long n = strlen(message); if (server->fd < 0) return -1; while (n > 0) { long i; do { i = write(server->fd, message, n); } while (i < 0 && errno == EINTR); if (i < 0) { if (would_block(server->fd, errno)) { if (do_wait(&server->fd, False, server->quit_func, server) < 0) return -1; continue; } perror("knews: write"); return -1; } if (i == 0) return -1; n -= i; message += i; } return 0; } /*************************************************************************/ long server_read_raw(SERVER *server) { long n, len, i; n = server->curr_pos + strlen(server->buffer + server->curr_pos); len = server->len - n; if (len <= 0) { len += server->len; server->len *= 2; server->buffer = XtRealloc(server->buffer, server->len + 3); } i = read(server->fd, server->buffer + n, len); if (i >= 0) server->buffer[n + i] = '\0'; return i; } char *server_get_line(SERVER *server) { char *c = server->buffer + server->curr_pos; char *tmp; long len; tmp = strchr(c, '\n'); if (tmp) { if (*(tmp-1) == '\r') *(tmp-1) = '\0'; *tmp++ = '\0'; server->curr_pos = tmp - server->buffer; if (server->bs) fprintf(server->bs, "%s\r\n", c); return c; } len = strlen(c); memmove(server->buffer, c, len + 1); server->curr_pos = 0; return NULL; } char *server_read(SERVER *server) { char *c; while (!(c = server_get_line(server))) { long i; do { i = server_read_raw(server); } while (i < 0 && errno == EINTR); if (i == 0) return NULL; if (i < 0) { if (would_block(server->fd, errno)) { if (do_wait(&server->fd, True, server->quit_func, server) < 0) return NULL; continue; } perror("knews: read"); return NULL; } } return c; } static char *find_crlf_dot_crlf(char *buffer) { char *c; if (buffer[0] == '.' && buffer[1] == '\r' && buffer[2] == '\n') return buffer; c = strstr(buffer, "\r\n.\r\n"); if (c) return c + 2; return NULL; } char *server_get_chunk(SERVER *server) { char *c = server->buffer + server->curr_pos; char *dot; long len; dot = find_crlf_dot_crlf(c); if (dot) { *dot++ = '\0'; if (*dot++ == '\r') dot++; server->curr_pos = dot - server->buffer; return c; } len = strlen(c); memmove(server->buffer, c, len + 1); server->curr_pos = 0; return NULL; } char *server_read_chunk(SERVER *server) { char *c; if (server->fd < 0) return NULL; while (!(c = server_get_chunk(server))) { long i; do { i = server_read_raw(server); } while (i < 0 && errno == EINTR); if (i == 0) return NULL; if (i < 0) { if (would_block(server->fd, errno)) { if (do_wait(&server->fd, True, server->quit_func, server) < 0) return NULL; continue; } perror("knews: read"); return NULL; } } return c; } char *server_comm(SERVER *server, char *command, int reconnect) { char *reply; int i = 0; do { if (server_write(server, command) < 0 && (!reconnect || server->aborted || reconnect_server(False) < 0 || server_write(server, command) < 0)) return NULL; reply = server_read(server); } while ((!reply || (reply[0] == '5' && case_lhassub(reply, "timeout"))) && i++ < 1); if (!reply) return NULL; i = atoi(reply); if (i == 450) { char buffer[1024]; char *auth_user = res_auth_info_user(); char *auth_pass = res_auth_info_pass(); if (!auth_user || !auth_pass || strlen(auth_user) > 500 || strlen(auth_pass) > 500) return reply; server_write(server, "AUTHINFO SIMPLE"); reply = server_read(server); if (!reply || atoi(reply) != 350) return reply; sprintf(buffer, "%s %s\r\n", auth_user, auth_pass); server_write(server, buffer); reply = server_read(server); if (!reply || atoi(reply) != 250) return reply; server_write(server, command); reply = server_read(server); } else if (i == NNTP_ERR_NEED_AUTH) { char buffer[1024]; char *auth_user = res_auth_info_user(); char *auth_pass = res_auth_info_pass(); if (!auth_user || !auth_pass || strlen(auth_user) > 500 || strlen(auth_pass) > 500) return reply; sprintf(buffer, "AUTHINFO USER %s\r\n", auth_user); server_write(server, buffer); reply = server_read(server); if (!reply || atoi(reply) != NNTP_CONT_AUTH) return reply; sprintf(buffer, "AUTHINFO PASS %s\r\n", auth_pass); server_write(server, buffer); reply = server_read(server); if (!reply || atoi(reply) != NNTP_OK_AUTH) return reply; server_write(server, command); reply = server_read(server); } return reply; } ./knews-1.0b.1/src/child.c100644 1244 1244 26134 6455455547 13705 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include "global.h" #include #include #include "child.h" #include "sysdeps.h" #define WRITE_STDERR(msg) write(2, msg, sizeof msg - 1) static volatile int sighup_may_exit = True; static volatile int caught_sighup = False; static void sighup_handler(int sig) { int oerrno = errno; if (sighup_may_exit) { if (sig == 0) WRITE_STDERR("exiting.\n"); else WRITE_STDERR("knews: exiting on SIGHUP!\n"); _exit(1); } caught_sighup = True; WRITE_STDERR("knews: Caught SIGHUP... [wait for it!] "); errno = oerrno; } void block_sighup(void) { sighup_may_exit = False; } void unblock_sighup(void) { sighup_may_exit = True; if (caught_sighup) sighup_handler(0); } /*********************************************************************/ void sigfpe_handler(int sig) { int oerrno = errno; WRITE_STDERR("knews: caught SIGFPE!\n"); errno = oerrno; } /*********************************************************************/ typedef struct CHILD_CONTEXT CHILD_CONTEXT; struct CHILD_CONTEXT { CHILD_CONTEXT *next; pid_t pid; volatile int exited; volatile int status; void *data; ChildCallback proc; char *stderr_buf; int stderr_fd; XtInputId stderr_id; }; static CHILD_CONTEXT *contexts = NULL; static int pipes[2]; static XtInputId pipe_id = 0; static void sigchld_handler(int sig) { int done_some = False; int oerrno = errno; int tmp; for (;;) { pid_t pid; int status; CHILD_CONTEXT *loop; do { pid = waitpid(-1, &status, WNOHANG); } while (pid < 0 && errno == EINTR); if (pid <= 0) break; for (loop = contexts ; loop ; loop = loop->next) if (loop->pid == pid) { done_some = True; loop->exited = True; loop->status = status; break; } } if (done_some) do { tmp = write(pipes[1], &done_some, 1); } while (tmp < 0 && errno == EINTR); errno = oerrno; } #if !defined(HAVE_SIGACTION) || HAVE_SIGACTION static void install_sig_handlers(void) { struct sigaction sig_act; int flags; flags = 0; #ifdef SA_RESTART flags |= SA_RESTART; #endif sig_act.sa_handler = sigchld_handler; sigemptyset(&sig_act.sa_mask); sigaddset(&sig_act.sa_mask, SIGCHLD); sig_act.sa_flags = flags; #ifdef SA_NOCLDSTOP sig_act.sa_flags |= SA_NOCLDSTOP; #endif if (sigaction(SIGCHLD, &sig_act, NULL) < 0) perror("knews: sigaction"); /* * The Layout widget sometimes generates a SIGFPE. This may be fixed * now, thanks to leo@marco.de, but I might as well keeps this. */ sig_act.sa_handler = SIG_IGN; sigemptyset(&sig_act.sa_mask); sig_act.sa_flags = flags; if (sigaction(SIGPIPE, &sig_act, NULL) < 0) perror("knews: sigaction(SIGPIPE)"); sig_act.sa_handler = sigfpe_handler; sigemptyset(&sig_act.sa_mask); sigaddset(&sig_act.sa_mask, SIGFPE); sig_act.sa_flags = flags; if (sigaction(SIGFPE, &sig_act, NULL) < 0) perror("knews: sigaction(SIGFPE)"); sig_act.sa_handler = sighup_handler; sigemptyset(&sig_act.sa_mask); sigaddset(&sig_act.sa_mask, SIGHUP); sig_act.sa_flags = flags; if (sigaction(SIGHUP, &sig_act, NULL) < 0) perror("knews: sigaction(SIGHUP)"); sig_act.sa_handler = sigusr1_handler; sigemptyset(&sig_act.sa_mask); sigaddset(&sig_act.sa_mask, SIGUSR1); sig_act.sa_flags = flags; if (sigaction(SIGUSR1, &sig_act, NULL) < 0) perror("knews: sigaction(SIGHUP)"); } static void unblock_sigpipe(void) { struct sigaction sig_act; sig_act.sa_handler = SIG_DFL; sigemptyset(&sig_act.sa_mask); sig_act.sa_flags = 0; if (sigaction(SIGPIPE, &sig_act, NULL) < 0) perror("knews: siagction(SIGPIPE)"); } static void unblock_sigchld(int reap) { sigset_t set; sigemptyset(&set); sigaddset(&set, SIGCHLD); sigprocmask(SIG_UNBLOCK, &set, NULL); } static void block_sigchld(void) { sigset_t set; sigemptyset(&set); sigaddset(&set, SIGCHLD); sigprocmask(SIG_BLOCK, &set, NULL); } #else /* !HAVE_SIGACTION: potentially unreliable signals */ static void install_sig_handlers(void) { if (signal(SIGCHLD, sigchld_handler) == SIG_ERR) perror("knews: signal(SIGCHLD)"); if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) perror("knews: signal(SIGPIPE)"); if (signal(SIGFPE, sigfpe_handler) == SIG_ERR) perror("knews: signal(SIGFPE)"); if (signal(SIGHUP, sighup_handler) == SIG_ERR) perror("knews: signal(SIGHUP)"); if (signal(SIGUSR1, sigusr1_handler) == SIG_ERR) perror("knews: signal(SIGUSR1)"); } static void unblock_sigpipe(void) { if (signal(SIGPIPE, SIG_DFL) == SIG_ERR) perror("knews: signal(SIGPIPE)"); } static void unblock_sigchld(int reap) { if (signal(SIGCHLD, sigchld_handler) == SIG_ERR) perror("knews: signal(SIGCHLD)"); if (reap) sigchld_handler(0); } static void block_sigchld(void) { if (signal(SIGCHLD, SIG_DFL) == SIG_ERR) perror("knews: signal(SIGCHLD)"); } #endif static void stderr_finalize(CHILD_CONTEXT *context) { if (context->stderr_fd >= 0) { int i, n; i = strlen(context->stderr_buf); n = STDERR_BUFFLEN - i - 1; if (n > 0) { char *c = context->stderr_buf + i; i = read(context->stderr_fd, c, n); if (i >= 0) c[i] = '\0'; else c[0] = '\0'; } close(context->stderr_fd); context->stderr_fd = -1; } if (context->stderr_id != 0) { XtRemoveInput(context->stderr_id); context->stderr_id = 0; } } static void pipe_read_callback(XtPointer client_data, int *fd, XtInputId *id) { CHILD_CONTEXT *loop, *prev; char c; block_sigchld(); (void)read(pipes[0], &c, 1); loop = contexts; prev = NULL; while (loop) { if (loop->exited) { CHILD_CONTEXT *next = loop->next; stderr_finalize(loop); if (loop->proc) loop->proc(loop->data, loop->status, loop->stderr_buf); if (prev) prev->next = next; else contexts = next; XtFree(loop->stderr_buf); XtFree((char *)loop); loop = next; } else { prev = loop; loop = loop->next; } } unblock_sigchld(True); } void suspend_child_contexts(void) { if (pipe_id != 0) { XtRemoveInput(pipe_id); pipe_id = 0; } } void resume_child_contexts(void) { if (pipe_id == 0) pipe_id = XtAppAddInput(app_cont, pipes[0], (XtPointer)XtInputReadMask, pipe_read_callback, NULL); } void init_child_contexts(void) { long flags; if (pipe(pipes) < 0) { perror("knews: pipe"); exit(1); } if ((flags = fcntl(pipes[0], F_GETFL)) < 0 || fcntl(pipes[0], F_SETFL, flags | O_NONBLOCK) < 0 || (flags = fcntl(pipes[1], F_GETFL)) < 0 || fcntl(pipes[1], F_SETFL, flags | O_NONBLOCK) < 0 || fcntl(pipes[0], F_SETFD, FD_CLOEXEC) < 0 || fcntl(pipes[1], F_SETFD, FD_CLOEXEC) < 0) { perror("knews: fcntl"); exit(1); } resume_child_contexts(); install_sig_handlers(); } static void stderr_read_callback(XtPointer client_data, int *fd, XtInputId *id) { CHILD_CONTEXT *context = (CHILD_CONTEXT *)client_data; int n, i; i = strlen(context->stderr_buf); n = STDERR_BUFFLEN - i - 1; if (n > 0) { char *c = context->stderr_buf + i; i = read(context->stderr_fd, c, n); if (i > 0) { c[i] = '\0'; n -= i; if (n > 0) return; } else { c[0] = '\0'; if (i < 0) { perror("knews: read"); if ( errno == EINTR || errno == EAGAIN #ifdef EWOULDBLOCK || errno == EWOULDBLOCK #endif ) return; } } } XtRemoveInput(context->stderr_id); context->stderr_id = 0; close(context->stderr_fd); context->stderr_fd = -1; } static CHILD_CONTEXT *create_child_context(pid_t pid, void *data, ChildCallback proc) { CHILD_CONTEXT *temp; temp = (CHILD_CONTEXT *)XtMalloc(sizeof(CHILD_CONTEXT)); temp->pid = pid; temp->exited = False; temp->status = 0; temp->data = data; temp->proc = proc; temp->stderr_buf = NULL; temp->stderr_fd = -1; temp->stderr_id = 0; temp->next = contexts; contexts = temp; return temp; } pid_t fork_nicely(void *data, ChildCallback proc, int use_stderr) { CHILD_CONTEXT *context; pid_t pid; int stderr_fd[2]; if (!use_stderr || pipe(stderr_fd) < 0) { stderr_fd[0] = -1; stderr_fd[1] = -1; } block_sigchld(); pid = fork(); if (pid == 0) { /* child */ unblock_sigchld(False); setsid(); if (!freopen("/dev/null", "w", stdout)) perror("/dev/null"); if (stderr_fd[0] >= 0) close(stderr_fd[0]); if (stderr_fd[1] >= 0 && stderr_fd[1] != STDERR_FILENO) if (dup2(stderr_fd[1], STDERR_FILENO) < 0) perror("knews: dup2"); unblock_sigpipe(); return 0; } /* * parent or error */ if (stderr_fd[1] >= 0) close(stderr_fd[1]); if (pid < 0) { perror("knews: fork"); if (stderr_fd[0] >= 0) close(stderr_fd[0]); unblock_sigchld(True); return -1; } context = create_child_context(pid, data, proc); if (stderr_fd[0] >= 0) { long flags; if (fcntl(stderr_fd[0], F_SETFD, FD_CLOEXEC) < 0) perror("fcntl"); if ((flags = fcntl(stderr_fd[0], F_GETFL)) < 0 || fcntl(stderr_fd[0], F_SETFL, flags | O_NONBLOCK) < 0) { perror("knews: fcntl"); close(stderr_fd[0]); stderr_fd[0] = -1; } else { context->stderr_buf = XtMalloc(STDERR_BUFFLEN); context->stderr_buf[0] = '\0'; context->stderr_fd = stderr_fd[0]; context->stderr_id = XtAppAddInput(app_cont, stderr_fd[0], (XtPointer)XtInputReadMask, stderr_read_callback, (XtPointer)context); } } unblock_sigchld(True); return pid; } pid_t wait_for_pid(pid_t pid, int *status, char *stderr_buf) { CHILD_CONTEXT *context; pid_t result; for (context = contexts ; context ; context = context->next) if (context->pid == pid) break; if (!context) { errno = ECHILD; return -1; } suspend_child_contexts(); while (!context->exited) { (void)do_wait(&pipes[0], True, NULL, NULL); (void)read(pipes[0], &result, 1); } stderr_finalize(context); result = context->pid; if (stderr_buf) strcpy(stderr_buf, context->stderr_buf); if (status) *status = context->status; resume_child_contexts(); pipe_read_callback(NULL, NULL, NULL); return result; } char *signal_string(int sig) { #define DO(x) if (sig == x) return #x #ifdef SIGHUP DO(SIGHUP); #endif #ifdef SIGINT DO(SIGINT); #endif #ifdef SIGQUIT DO(SIGQUIT); #endif #ifdef SIGABRT DO(SIGABRT); #endif #ifdef SIGBUS DO(SIGBUS); #endif #ifdef SIGFPE DO(SIGFPE); #endif #ifdef SIGKILL DO(SIGKILL); #endif #ifdef SIGSEGV DO(SIGSEGV); #endif #ifdef SIGPIPE DO(SIGPIPE); #endif #ifdef SIGALRM DO(SIGALRM); #endif #ifdef SIGTERM DO(SIGTERM); #endif #ifdef SIGCHLD DO(SIGCHLD); #endif #ifdef SIGCONT DO(SIGCONT); #endif #ifdef SIGSTOP DO(SIGSTOP); #endif #ifdef SIGTSTP DO(SIGTSTP); #endif #ifdef SIGTTIN DO(SIGTTIN); #endif #ifdef SIGTTOU DO(SIGTTOU); #endif #ifdef SIGURG DO(SIGURG); #endif #ifdef SIGWINCH DO(SIGWINCH); #endif #ifdef SIGIO DO(SIGIO); #endif #ifdef SIGPOLL DO(SIGPOLL); #endif return "unknown signal"; } ./knews-1.0b.1/src/widgets.h100644 1244 1244 1356 6455455547 14254 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ struct main_widgets { Widget shell; Widget second_shell; Widget top_manager; Widget top_layout; Widget arttree; Widget group_list; Widget thread_list; Widget knapp[12]; Widget text; }; extern struct main_widgets main_widgets; extern void create_main_widgets(void); extern void setNewsModeDisconnected(void); extern void setNewsModeConnected(void); extern void setNewsModeGroup(int); extern void setNewsModeThread(void); extern void setNewsModeAllgroups(regex_t*); extern void setNewsModeNewgroups(void); extern void update_subj_entry(SUBJECT*); extern void update_group_entry(GROUP*); extern void purge_hot(Pixmap); extern void set_message(char*, int); ./knews-1.0b.1/src/thread.c100644 1244 1244 72112 6570023026 14043 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ /* * The threading algorithm was inspired by trn. */ #include "global.h" #include #include "ahead.h" #include "cache.h" #include "codes.h" #include "connect.h" #include "font.h" #include "k_file.h" #include "parse.h" #include "read.h" #include "resource.h" #include "server.h" #include "sort.h" #include "thread.h" #include "util.h" #include "widgets.h" #include "xutil.h" #include "../Widgets/Dialogue.h" #include "../Widgets/Message.h" #include "../Widgets/Util.h" #define HASH_SIZE 237 #undef HEAD_DEBUG #define HEAD_DEBUG 0 #undef ART_BYTES #define ART_BYTES 0 #undef DATE_ERRORS #define DATE_ERRORS 0 struct THREAD_CONTEXT { ARTICLE *articles; SUBJECT *subjects; ARTICLE *art_ht[HASH_SIZE]; SUBJECT *subj_ht[HASH_SIZE]; ARTICLE *last_art; SUBJECT *last_subj; SUBJECT *fake_had_subject; char *refs; long total; long n_done; char *charset; }; THREAD_CONTEXT *main_thr = NULL; /*************************************************************************/ static long str_hash(const char *str, long len) { unsigned long hash = 0; while (len-- > 0) hash += *str++ & 0xdfu; return hash % HASH_SIZE; } static void art_hash_table_clear(ARTICLE **art_ht) { ARTICLE *loop, *next; long i; for (i = 0 ; i < HASH_SIZE ; i++) { for (loop = art_ht[i] ; loop ; loop = next) { next = loop->hash_next; XtFree(loop->msgid); XtFree(loop->xref); XtFree(loop->from); XtFree(loop->tree_data.label); XtFree((char *)loop); } art_ht[i] = NULL; } } static ARTICLE **art_hash_find(ARTICLE **art_ht, const char *msgid, long len) { ARTICLE *loop, *prev = NULL; ARTICLE **hash_ptr; hash_ptr = &art_ht[str_hash(msgid, len)]; for (loop = *hash_ptr ; loop ; prev = loop, loop = loop->hash_next) if (len == loop->hash_len && memcmp(msgid, loop->msgid, len) == 0) break; return prev ? &prev->hash_next : hash_ptr; } static void subj_hash_table_clear(SUBJECT **subj_ht) { SUBJECT *loop, *next; long i; for (i = 0 ; i < HASH_SIZE ; i++) { for (loop = subj_ht[i] ; loop ; loop = next) { next = loop->hash_next; XtFree(loop->subject); XtFree((char *)loop); } subj_ht[i] = NULL; } } static SUBJECT **subj_hash_find(SUBJECT **subj_ht, const char *subject, long len) { SUBJECT *loop, *prev = NULL; SUBJECT **hash_ptr; hash_ptr = &subj_ht[str_hash(subject, len)]; for (loop = *hash_ptr ; loop ; prev = loop, loop = loop->hash_next) if (loop->hash_len == len && strncmp(subject, loop->subject, len) == 0) break; return prev ? &prev->hash_next : hash_ptr; } void clear_thread_context(THREAD_CONTEXT *context) { context->articles = NULL; context->subjects = NULL; art_hash_table_clear(context->art_ht); subj_hash_table_clear(context->subj_ht); context->last_art = NULL; context->last_subj = NULL; context->fake_had_subject = NULL; context->refs = NULL; context->total = 1; context->n_done = 0; context->charset = NULL; } static void nullify_thread_context(THREAD_CONTEXT *context) { int i; context->articles = NULL; context->subjects = NULL; context->last_art = NULL; context->last_subj = NULL; context->fake_had_subject = NULL; context->refs = NULL; context->total = 1; context->n_done = 0; context->charset = NULL; for (i = 0 ; i < HASH_SIZE ; i++) { context->art_ht[i] = NULL; context->subj_ht[i] = NULL; } } THREAD_CONTEXT *create_thread_context(void) { THREAD_CONTEXT *ret; ret = (THREAD_CONTEXT *)XtMalloc(sizeof *main_thr); nullify_thread_context(ret); return ret; } ARTICLE *get_articles(THREAD_CONTEXT *context) { return context->articles; } SUBJECT *get_subjects(THREAD_CONTEXT *context) { return context->subjects; } void set_subjects(THREAD_CONTEXT *context, SUBJECT *subjects) { context->subjects = subjects; } ARTICLE *find_article(const char *msgid, long msgid_len) { return *art_hash_find(main_thr->art_ht, msgid, msgid_len); } char *get_refs(THREAD_CONTEXT *context) { return context->refs; } static ARTICLE *parse_xover_line(THREAD_CONTEXT*, char*); static void thread_article(THREAD_CONTEXT*, ARTICLE*); char *thread_from_file(SERVER *server, long first_art) { char *buffer; main_thr->charset = res_default_charset(); if (!main_thr->charset) main_thr->charset = "iso-8859-1"; while ((buffer = server_read(server)) && !IS_DOT(buffer)) { ARTICLE *art; if (atol(buffer) < first_art) continue; art = parse_xover_line(main_thr, buffer); if (art) { if (main_thr->last_art) main_thr->last_art->next = art; else main_thr->articles = art; main_thr->last_art = art; thread_article(main_thr, art); } } return buffer; } /*************************************************************************/ static char *get_msgid(char *beg, char *end) { char *mid; while (*beg == ' ') beg++; if (beg >= end || *beg++ != '<') return NULL; while (end > beg && *end == ' ') *end-- = '\0'; if (*end != '>') return NULL; *end = '\0'; mid = strchr(beg, '@'); if (mid) while (++mid < end) if (IS_UPPER(*mid)) *mid = LOWER(*mid); return beg; } /* replaces ascii 0-32 & 127 and iso-8859-x 128-159 & 255 with ' ' */ static void munge_nonprintable(char *str) { unsigned char *c = (unsigned char *)str; while (*c != '\0') { if ((*c & 0x60) == 0 || *c == '\b') *c = ' '; c++; } } static void overview_error(long art_no, char *string) { fputs("Bad overview record", stderr); if (art_no) fprintf(stderr, ", article no %ld", art_no); fprintf(stderr, "; %s\n", string); } static void unlink_child(ARTICLE *child) { ARTICLE *last = A_PARENT(child); if (last) { if (child == A_CHILD1(last)) CHILD1(last) = SIBLING(child); else { last = A_CHILD1(last); while (child != A_SIBLING(last)) last = A_SIBLING(last); SIBLING(last) = SIBLING(child); } } else { SUBJECT *subj = child->subject; if ((last = subj->thread) == child) { do { subj->thread = A_SIBLING(child); subj = subj->prev; } while (subj && subj->thread == last); subj = child->subject->next; while (subj && subj->thread == last) { subj->thread = A_SIBLING(child); subj = subj->next; } } else { while (child != A_SIBLING(last)) last = A_SIBLING(last); SIBLING(last) = SIBLING(child); } } } static void link_child(ARTICLE *child) { ARTICLE *art = A_PARENT(child); if (art) { art = A_CHILD1(art); if (!art || child->date < art->date) { SIBLING(child) = (ART_TREE_NODE *)art; CHILD1(A_PARENT(child)) = (ART_TREE_NODE *)child; } else { while (A_SIBLING(art) && A_SIBLING(art)->date <= child->date) art = A_SIBLING(art); SIBLING(child) = SIBLING(art); SIBLING(art) = (ART_TREE_NODE *)child; } } else { SUBJECT *subj = child->subject; art = subj->thread; if (!art || child->date < art->date) { do { subj->thread = child; subj = subj->prev; } while (subj && subj->thread == art); subj = child->subject->next; while (subj && subj->thread == art) { subj->thread = child; subj = subj->next; } SIBLING(child) = (ART_TREE_NODE *)art; } else { while (A_SIBLING(art) && A_SIBLING(art)->date <= child->date) art = A_SIBLING(art); SIBLING(child) = SIBLING(art); SIBLING(art) = (ART_TREE_NODE *)child; } } } static void merge_threads(THREAD_CONTEXT *context, SUBJECT *s1, SUBJECT *s2) { SUBJECT *s; ARTICLE *t1 = s1->thread; ARTICLE *t2 = s2->thread; while (s1->next && s1->next->thread == t1) s1 = s1->next; while (s2->prev && s2->prev->thread == t2) s2 = s2->prev; s = s2; s->thread = t1; while (s->next && s->next->thread == t2) { s = s->next; s->thread = t1; } if (s2->prev) s2->prev->next = s->next; else context->subjects = s->next; if (s->next) s->next->prev = s2->prev; if ( (s->next = s1->next) ) s->next->prev = s; s1->next = s2; s2->prev = s1; for (t1 = t2 ; t1 ; t1 = t2) { t2 = A_SIBLING(t2); link_child(t1); } } static SUBJECT *get_subject(THREAD_CONTEXT *context, char *subj) { SUBJECT **sp; decode_rfc1522(subj, context->charset); munge_nonprintable(subj); subj = eat_re(subj); if (!*(sp = subj_hash_find(context->subj_ht, subj, strlen(subj)))) { (*sp) = (SUBJECT *)XtMalloc(sizeof(SUBJECT)); (*sp)->hash_next = NULL; (*sp)->next = NULL; (*sp)->prev = NULL; (*sp)->thread = NULL; (*sp)->no_unread = 0; (*sp)->disp = 0; (*sp)->pixmap = None; (*sp)->has_tagged = False; (*sp)->subject = XtNewString(subj); (*sp)->hash_len = strlen(subj); if (context->subjects) { if (!context->last_subj || context->last_subj->next) { /* subjects have been reordered by merge_threads */ context->last_subj = context->subjects; while (context->last_subj->next) context->last_subj = context->last_subj->next; } (*sp)->prev = context->last_subj; context->last_subj->next = *sp; context->last_subj = *sp; } else { context->subjects = *sp; context->last_subj = context->subjects; } } return *sp; } static ARTICLE *parse_xover_line(THREAD_CONTEXT *context, char *buffer) { ARTICLE *art; ARTICLE **fake_p; char *c1, *c2; long art_no, lines; char *subject, *from, *msgid; time_t date; long msgid_len; #if ART_BYTES long bytes; #endif if (!(c1 = strchr(buffer, '\t')) || !IS_DIGIT(buffer[0])) { overview_error(0, "no article number."); return NULL; } *c1++ = '\0'; art_no = atol(buffer); if (!(c2 = strchr(c1, '\t'))) { overview_error(art_no, "no Subject: field."); return NULL; } *c2++ = '\0'; subject = c1; if (!(c1 = strchr(c2, '\t'))) { overview_error(art_no, "no From: field."); return NULL; } *c1++ = '\0'; decode_rfc1522(c2, context->charset); munge_nonprintable(c2); from = c2; if (!(c2 = strchr(c1, '\t'))) { overview_error(art_no, "no Date: field."); return NULL; } *c2++ = '\0'; date = parsedate(c1); #if DATE_ERRORS if (date == PARSEDATE_ERROR) fprintf(stderr, "Bad date: %s\n", c1); #endif if (!(c1 = strchr(c2, '\t'))) { overview_error(art_no, "no Message-Id: field."); return NULL; } *c1 = '\0'; if (!(msgid = get_msgid(c2, c1 - 1))) { overview_error(art_no, "syntax error in Message-Id: field."); return NULL; } msgid_len = strlen(msgid); c1++; if (!(c2 = strchr(c1, '\t'))) { context->refs = NULL; overview_error(art_no, "no References: field."); return NULL; } *c2++ = '\0'; context->refs = c1; if (!(c1 = strchr(c2, '\t'))) { overview_error(art_no, "no Bytes: field."); return NULL; } *c1++ = '\0'; #if ART_BYTES bytes = atol(c2); #endif if ( (c2 = strchr(c1, '\t')) ) *(c2++) = '\0'; lines = atol(c1); if ( (art = *(fake_p = art_hash_find(context->art_ht, msgid, msgid_len))) ) { if (art->from) { fprintf(stderr, "Bad overview record, articles %ld and %ld have " "identical message-id's:\n\t<%s>\n", art_no, art->no, msgid); return NULL; /* duplicate Message-ID */ } context->fake_had_subject = art->subject; } else { art = *fake_p = (ARTICLE *)XtMalloc(sizeof(ARTICLE)); art->tree_data.hook = NULL; art->tree_data.label = NULL; PARENT(art) = NULL; SIBLING(art) = NULL; CHILD1(art) = NULL; art->hash_next = NULL; art->next = NULL; art->xref = NULL; art->pixmap = None; art->read = FALSE; art->killed = FALSE; art->msgid = memcpy(XtMalloc(msgid_len + 1), msgid, msgid_len + 1); art->hash_len = msgid_len; context->fake_had_subject = NULL; } art->no = art_no; art->subject = get_subject(context, subject); art->from = XtNewString(from); art->date = date; art->lines = lines; #if ART_BYTES art->bytes = bytes; #endif while (c2) { c1 = c2; if ( (c2 = strchr(c1, '\t')) ) *(c2++) = '\0'; if (case_lstrncmp(c1, "xref: ", 6) == 0) { c1 = strchr(c1 + 6, ' '); if (c1) { c1++; art->xref = XtNewString(c1); } break; } } return art; } static void thread_article(THREAD_CONTEXT *context, ARTICLE *art) { ARTICLE *a, *last; if (context->fake_had_subject) { ARTICLE *stop; if (context->fake_had_subject->thread != art->subject->thread) merge_threads(context, context->fake_had_subject, art->subject); a = A_PARENT(art); while (a && !a->from && !A_SIBLING(A_CHILD1(a))) a = A_PARENT(a); stop = a; unlink_child(art); for (a = A_PARENT(art) ; a != stop ; a = last) { unlink_child(a); last = A_PARENT(a); a->date = 0; a->subject = NULL; PARENT(a) = NULL; } PARENT(art) = NULL; SIBLING(art) = NULL; } if (context->refs) { ARTICLE **ap; char *c, *end; last = art; a = NULL; end = context->refs + strlen(context->refs) - 1; for (;;) { char *msgid; long msgid_len; for (c = end ; c >= context->refs && *c != '<' ; c--); if (!(msgid = get_msgid(c, end))) break; msgid_len = strlen(msgid); ap = art_hash_find(context->art_ht, msgid, msgid_len); a = *ap; if (a) { if ((a->date != 0 && !a->subject) || a == art) { if ((a = last) == art) a = NULL; continue; } } else { a = *ap = (ARTICLE *)XtMalloc(sizeof(ARTICLE)); a->tree_data.hook = NULL; a->tree_data.label = NULL; PARENT(a) = NULL; SIBLING(a) = NULL; CHILD1(a) = NULL; a->hash_next = NULL; a->next = NULL; a->from = NULL; a->xref = NULL; a->subject = NULL; a->no = 0; a->pixmap = None; a->lines = 0; #if ART_BYTES a->bytes = 0; #endif a->read = FALSE; a->killed = FALSE; a->hash_len = msgid_len; a->msgid = memcpy(XtMalloc(msgid_len + 1), msgid, msgid_len + 1); } PARENT(last) = (ART_TREE_NODE *)a; link_child(last); if (a->subject) break; a->date = art->date; last = a; end = c - 1; } if (!a) { /* no references */ link_child(art); return; } if (a->subject) { if (art->subject->thread != a->subject->thread) merge_threads(context, art->subject, a->subject); } else { a->subject = art->subject; link_child(a); } for (a = A_PARENT(art) ; a && !a->subject ; a = A_PARENT(a)) a->subject = art->subject; while (a && art != A_PARENT(a)) a = A_PARENT(a); if (a) { unlink_child(a); PARENT(a) = NULL; link_child(a); } } else { link_child(art); } } static void mark_read_articles(THREAD_CONTEXT *context, GROUP *group) { SUBJECT *subj = context->subjects; ARTICLE *art = context->articles; long n = 0; ART_LIST_NODE *loop = group->read_arts; while (subj) { subj->no_unread = 0; subj = subj->next; } while (art && loop) { while (art && art->no < loop->first) { art->read = False; if (art->from) { n++; art->subject->no_unread++; } art = art->next; } while (art && art->no >= loop->first && art->no <= loop->last) { if (art->from) art->read = True; art = art->next; } loop = loop->next; } while (art) { art->read = False; if (art->from) { n++; art->subject->no_unread++; } art = art->next; } group->no_unread = n; } void fix_author(ARTICLE *art, char *from, int show_no_lines) { const char *author; char buffer[160]; char *c; long len = 0; if (!show_no_lines) c = buffer; else { sprintf(buffer, "%4hu ", art->lines); c = buffer + strlen(buffer); } author = parse_author(from, &len); if (len > sizeof buffer - 16) len = sizeof buffer - 16; memcpy(c, author, len); c[len] = '\0'; art->tree_data.label = XtNewString(buffer); } static void fix_authors(THREAD_CONTEXT *context) { ARTICLE *art; int show_number_lines = res_show_number_lines(); for (art = context->articles ; art ; art = art->next) if (art->from) fix_author(art, art->from, show_number_lines); } static char *xover_articles(void) { char command[512]; char *buffer; ARTICLE *art; long i, j, m; j = m = main_thr->total / 32; i = main_thr->n_done * 100; while ((buffer = server_read(main_server)) && !IS_DOT(buffer)) { art = parse_xover_line(main_thr, buffer); if (art) { if (main_thr->last_art) main_thr->last_art->next = art; else main_thr->articles = art; main_thr->last_art = art; thread_article(main_thr, art); } if (j-- <= 0) { sprintf(command, "Reading group... %3ld%%", i / main_thr->total); set_message(command, False); j = m; } i += 100; } if (!buffer) return NULL; return CODE_TO_STR(NNTP_OK_GROUP); } /*************************************************************************/ #define MAX_CHUNK_SIZE 64 static int put_chunk_head(long max) { char command[64 * 12 + 1]; char *c; int n; if (max <= 0) return 0; if (max > global.chunk_size) max = global.chunk_size; if (max > MAX_CHUNK_SIZE) max = MAX_CHUNK_SIZE; for (n = 0, c = command ; n < max ; n++, c += 12) sprintf(c, "HEAD\r\nNEXT\r\n"); *c = '\0'; server_write(main_server, command); return max; } static ARTICLE *gather_headers(long art_no, THREAD_CONTEXT *context, char *subject, char *from, time_t date, char *msgid, long lines, char *xref) { long msgid_len = strlen(msgid); ARTICLE **fake_p = art_hash_find(context->art_ht, msgid, msgid_len); ARTICLE *art = *fake_p; if (art) { if (art->from) { overview_error(art_no, "duplicate Message-Id."); return NULL; } context->fake_had_subject = art->subject; } else { *fake_p = (ARTICLE *)XtMalloc(sizeof(ARTICLE)); art = *fake_p; art->tree_data.hook = NULL; art->tree_data.label = NULL; PARENT(art) = NULL; SIBLING(art) = NULL; CHILD1(art) = NULL; art->hash_next = NULL; art->next = NULL; art->msgid = memcpy(XtMalloc(msgid_len + 1), msgid, msgid_len + 1); art->hash_len = msgid_len; context->fake_had_subject = NULL; } art->subject = get_subject(context, subject); decode_rfc1522(from, context->charset); munge_nonprintable(from); art->from = XtNewString(from); art->xref = xref ? XtNewString(xref) : NULL; art->date = date; art->lines = lines; #if ART_BYTES art->bytes = 0; #endif art->pixmap = None; art->read = False; art->killed = False; art->no = art_no; return art; } static char *join_cont_lines(char *buffer) { while ((buffer = strchr(buffer, '\n'))) if (IS_SPACE(buffer[1])) { if (buffer[-1] == '\r') buffer[-1] = ' '; *buffer++ = ' '; } else { if (buffer[-1] == '\r') buffer[-1] = '\0'; *buffer++ = '\0'; if (*buffer == '\0') return NULL; else return buffer; break; } return NULL; } ARTICLE *parse_head(long art_no, THREAD_CONTEXT *context, char *buffer) { char *from = NULL, *subject = NULL, *xref = NULL, *refs = NULL; char *msgid = NULL; long /*bytes = -1,*/ lines = -1; time_t date = -1; while (buffer) { unsigned char tmp; int skip = False; tmp = *buffer; if (islower(tmp)) tmp = toupper(tmp); switch (*buffer) { #if ART_BYTES case 'B': if (case_lstrncmp(buffer, "bytes:", 6) == 0) { buffer += 6; bytes = atol(buffer); } break; #endif case 'D': if (case_lstrncmp(buffer, "date:", 5) == 0) { buffer += 5; while (IS_SPACE(*buffer)) buffer++; date = parsedate(buffer); } break; case 'F': if (!from && case_lstrncmp(buffer, "from:", 5) == 0) { buffer += 5; while (IS_SPACE(*buffer)) buffer++; from = buffer; skip = False; } break; case 'L': if (case_lstrncmp(buffer, "lines:", 6) == 0) { buffer += 6; lines = atol(buffer); } break; case 'M': if (case_lstrncmp(buffer, "message-id:", 11) == 0) { char *p; buffer += 11; while (IS_SPACE(*buffer)) buffer++; p = strchr(buffer, '\n'); if (!p || *--p != '\r') break; *p = '\0'; msgid = get_msgid(buffer, p - 1); *p = '\r'; buffer = p; } break; case 'R': if (!refs && case_lstrncmp(buffer, "references:", 11) == 0) { buffer += 11; while (IS_SPACE(*buffer)) buffer++; refs = buffer; skip = False; } break; case 'S': if (!subject && case_lstrncmp(buffer, "subject:", 8) == 0) { buffer += 8; while (IS_SPACE(*buffer)) buffer++; subject = buffer; skip = False; } break; case 'X': if (!xref && case_lstrncmp(buffer, "xref:", 5) == 0) { char *c; buffer += 5; while (IS_SPACE(*buffer)) buffer++; c = strchr(buffer, ' '); if (c) { xref = c + 1; skip = False; } } break; } buffer = join_cont_lines(buffer); } if (!from || !subject || !msgid) return NULL; munge_nonprintable(from); context->refs = refs; return gather_headers(art_no, context, subject, from, date, msgid, lines, xref); } static char *head_articles(void) { char command[512]; char *buffer; ARTICLE *art = NULL; long n_pending, n_to_put; long max_count, count; n_pending = put_chunk_head(main_thr->total); n_to_put = main_thr->total - n_pending; count = max_count = main_thr->total / 32; for (;;) { n_pending--; if (HEAD_DEBUG && global.head_debug) fprintf(stderr, "n_pending=%ld, n_to_put=%ld\n", n_pending, n_to_put); if (n_pending <= 0) { n_pending = 1; n_to_put = 0; server_write(main_server, "HEAD\r\nNEXT\r\n"); } else if (n_pending < global.chunk_size && n_to_put > 0) { long temp; temp = put_chunk_head(n_to_put); n_pending += temp; n_to_put -= temp; } buffer = server_read(main_server); if (!buffer) return NULL; if (HEAD_DEBUG && global.head_debug) fprintf(stderr, "%s\n", buffer); if (atoi(buffer) == NNTP_OK_HEAD) { long art_no, status; int tmp; tmp = sscanf(buffer, "%ld%ld", &status, &art_no); if (tmp > 0 && status == NNTP_OK_HEAD) { buffer = server_read_chunk(main_server); if (tmp >= 2 && art_no > 0 && (art = parse_head(art_no, main_thr, buffer))) { if (main_thr->last_art) main_thr->last_art->next = art; else main_thr->articles = art; main_thr->last_art = art; thread_article(main_thr, art); main_thr->refs = NULL; } } } buffer = server_read(main_server); if (!buffer) break; if (HEAD_DEBUG && global.head_debug) fprintf(stderr, "%s\n", buffer); if (atoi(buffer) != NNTP_OK_NOTEXT) break; main_thr->n_done++; if (count-- <= 0) { sprintf(command, "Reading group slowly... %3ld%%", (100 * main_thr->n_done) / main_thr->total); set_message(command, False); count = max_count; } } if (!buffer) return NULL; if (HEAD_DEBUG && global.head_debug && n_pending > 0) fprintf(stderr, "%ld pending, snarfing them.\n", n_pending); while (n_pending-- > 0) { /* next returned no next article */ buffer = server_read(main_server); if (!buffer) return NULL; if (HEAD_DEBUG && global.head_debug) fprintf(stderr, "%s\n", buffer); if (atoi(buffer) == NNTP_OK_HEAD) { buffer = server_read_chunk(main_server); if (!buffer) return NULL; } buffer = server_read(main_server); /* reply to next */ if (!buffer) return NULL; if (HEAD_DEBUG && global.head_debug) fprintf(stderr, "%s\n", buffer); } return CODE_TO_STR(NNTP_OK_GROUP); } static char *nntp_enter_group(char *reply) { char command[512]; long first = 0; long last = 0; if (!reply) { sprintf(command, "GROUP %s\r\n", global.curr_group->name); reply = server_comm(main_server, command, True); } if (!reply || atoi(reply) != NNTP_OK_GROUP) return reply; if (sscanf(reply, "%*d%ld%ld%ld", &main_thr->total, &first, &last) == 3) { global.curr_group->first_art = first; #if 1 /* Fix for 'last article cancelled.' */ if (last < global.curr_group->last_art) last = global.curr_group->last_art; #endif global.curr_group->last_art = last; } if (main_thr->total <= 0) main_thr->total = 1; global.curr_art = NULL; global.curr_subj = NULL; return reply; } static char *thread_articles(long asked_first) { char command[512]; char *buffer; long first = global.curr_group->first_art; long last = global.curr_group->last_art; main_thr->charset = res_default_charset(); if (!main_thr->charset) main_thr->charset = "iso-8859-1"; if (asked_first < 0 || asked_first > last) asked_first = last; if (global.xover_supported) { if (main_thr->last_art) first = main_thr->last_art->no + 1; else if (asked_first != 0) first = asked_first; if (first > last) return CODE_TO_STR(NNTP_OK_GROUP); sprintf(command, "XOVER %ld-%ld\r\n", first, last); buffer = server_comm(main_server, command, True); if (!buffer) return NULL; if (atoi(buffer) == NNTP_OK_XOVER) { buffer = xover_articles(); if (!buffer || atoi(buffer) != NNTP_OK_GROUP || !main_thr->last_art || main_thr->last_art->no >= last) return buffer; /* else fall through to get the last few by HEAD */ } } if (main_thr->last_art || asked_first != 0) { /* stat the first article we want */ long n; if (main_thr->last_art) first = main_thr->last_art->no + 1; else first = asked_first; /* this should succeed on the first pass */ for (n = first ; n <= last ; n++) { int ret; sprintf(command, "STAT %ld\r\n", first); server_write(main_server, command); buffer = server_read(main_server); if (!buffer) return NULL; ret = atoi(buffer); if (ret == NNTP_OK_NOTEXT) break; if (ret == NNTP_ERR_COMMAND) return CODE_TO_STR(NNTP_OK_GROUP); /* Hack for leafnode... */ if (ret != NNTP_ERR_NOART && ret != NNTP_ERR_NOARTIG) return buffer; } if (n > last) return CODE_TO_STR(NNTP_OK_GROUP); /* weird... */ } return head_articles(); } static void popup_ask_how_many(void); void read_group(char *reply, int may_ask, long first) { char message[256]; int threaded; if (!global.curr_group || (threaded = thread_ahead_check(global.curr_group)) < 0) return; res_enter_group(global.curr_group->name); font_enter_group(); set_busy(True); if (may_ask) { set_message("Server contacted, waiting for response...", False); reply = nntp_enter_group(reply); } if (reply && atoi(reply) == NNTP_OK_GROUP) { if (!threaded && may_ask && res_ask_how_many()) { unset_busy(); popup_ask_how_many(); return; } reply = thread_articles(first); } if (!reply) { clear_thread_context(main_thr); reconnect_server(True); unset_busy(); } else if (atoi(reply) == NNTP_OK_GROUP) { change_interruptible(False); set_message("Killing...", False); mark_read_articles(main_thr, global.curr_group); fix_authors(main_thr); kill_articles(global.curr_group); set_message("Sorting...", False); sort_threads(); unset_busy(); sprintf(message, "Group %s with %ld unread articles, " "%ld killed, %ld hot.", global.curr_group->name, global.curr_group->no_unread, global.n_killed, global.n_hot); set_message(message, False); setNewsModeGroup(False); cache_enter_group(); } else { unset_busy(); if (strlen(reply) > 200) reply[200] = '\0'; sprintf(message, "Error: message from server is %s", reply); set_message(message, True); clear_thread_context(main_thr); } } /*************************************************************************/ static void ask_dialogue_callback(Widget w, XtPointer client_data, XtPointer call_data) { DialogueReport *report = (DialogueReport *)call_data; long first; XtPopdown(w); XtDestroyWidget(w); XFlush(display); switch (report->reply) { case DialogueReplyRight: /* Cancel */ case DialogueReplyClose: set_standard_message(); break; case DialogueReplyMiddle: /* All */ set_message("Reading group...", False); read_group(CODE_TO_STR(NNTP_OK_GROUP), False, 0); break; case DialogueReplyLeft: /* OK */ case DialogueReplyEnter: case DialogueReplyTab: if (sscanf(report->buffer, "%ld", &first) != 1) { XBell(display, 0); return; } set_message("Reading group...", False); read_group(CODE_TO_STR(NNTP_OK_GROUP), False, first); break; } } static void popup_ask_how_many(void) { Widget w = NULL; char message[128]; char first_buf[32]; Arg arg[8]; long first; if (global.curr_group->read_arts) first = global.curr_group->read_arts->last + 1; else first = global.curr_group->first_art; sprintf(first_buf, "%ld", first); sprintf(message, "Range of available articles: %ld - %ld\n" "First unread article: %ld\n\n" "Start threading at which article?", global.curr_group->first_art, global.curr_group->last_art, first); XtSetArg(arg[0], XtNmessage, message); XtSetArg(arg[1], XtNbuffer, first_buf); XtSetArg(arg[2], XtNleftLabel, "OK"); XtSetArg(arg[3], XtNmiddleLabel, "All"); XtSetArg(arg[4], XtNrightLabel, "Cancel"); XtSetArg(arg[5], XtNcolormap, global.cmap); XtSetArg(arg[6], XtNvisual, global.visual); XtSetArg(arg[7], XtNdepth, global.depth); w = XtCreatePopupShell("askhowmany", dialogueWidgetClass, main_widgets.shell, arg, 8); XtAddCallback(w, XtNcallback, ask_dialogue_callback, NULL); popup_under_pointer(w, XtGrabExclusive); } ./knews-1.0b.1/src/save.h100644 1244 1244 1127 6455455547 13540 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #define SAVE_BOGUS_FROM (1<<0) #define SAVE_BOGUS_SUBJ (1<<1) #define SAVE_HEAD (1<<2) #define SAVE_BODY (1<<3) #define SAVE_EMPTY (1<<4) extern void popup_save(void); extern void popdown_save(void); extern void action_save(Widget, XEvent*, String*, Cardinal*); extern void action_pipe(Widget, XEvent*, String*, Cardinal*); extern void pipe_context_callback(void*, int, char*); extern void set_busy_save(int); extern int save_to_file(FILE*, char*, ARTICLE**, long, int, long*); extern void text_url_callback(Widget, XtPointer, XtPointer); ./knews-1.0b.1/src/k_edit.h100644 1244 1244 500 6455455547 14013 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ struct KILL_FILE; struct KILL_WIDGETS; extern void destroy_kill_widgets(struct KILL_WIDGETS*); extern void popup_kill_editor(struct KILL_FILE*); extern void popdown_kill_editor(struct KILL_WIDGETS*); extern void kill_editor_notify_add(struct KILL_FILE*, int); ./knews-1.0b.1/src/k_action.c100644 1244 1244 7662 6455455547 14376 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include "global.h" #include "expand.h" #include "k_I.h" #include "k_action.h" #include "k_file.h" #include "k_node.h" #include "newsrc.h" #include "util.h" #include "widgets.h" #include "xutil.h" static const char *scope_str[] = {"article", "subject", "thread", "subthread"}; static const char *field_str[] = {"Message-Id", "Subject", "From", "Xref"}; static void add_kill(String *params, int no_params, int append, int global_kill_file) { KILL_FILE *file; char buffer[128]; int scope, field, hot; char *expr = NULL; char *group = NULL; if (global.busy || (global.mode != NewsModeGroup && global.mode != NewsModeThread)) { XBell(display, 0); return; } if (no_params != 2 && no_params != 3) { if (append) set_message("kill-append() called with " "wrong number of arguments!", True); else set_message("kill-prepend() called with " "wrong number of arguments!", True); return; } if (case_lstrcmp(params[0], "from") == 0) { field = KillFieldFrom; if (global.curr_art) expr = regexp_escape_string(global.curr_art->from, True); } else if (case_lstrcmp(params[0], "subject") == 0) { field = KillFieldSubject; if (global.curr_art) expr = regexp_escape_string(global.curr_art->subject->subject, True); else if (global.curr_subj) expr = regexp_escape_string(global.curr_subj->subject, True); } else if (case_lstrcmp(params[0], "message-id") == 0) { field = KillFieldMsgid; if (global.curr_art) { expr = XtMalloc(global.curr_art->hash_len + 4); sprintf(expr, "<%s>", global.curr_art->msgid); } } else { sprintf(buffer, "kill-%s(): unknown field: ", append ? "append" : "prepend"); strncat(buffer, params[0], 40); set_message(buffer, True); return; } if (case_lstrcmp(params[1], "article") == 0) scope = KillScopeArticle; else if (case_lstrcmp(params[1], "thread") == 0) scope = KillScopeThread; else if (case_lstrcmp(params[1], "subthread") == 0) scope = KillScopeSubthread; else if (case_lstrcmp(params[1], "subject") == 0) scope = KillScopeSubject; else { sprintf(buffer, "kill-%s(): unknown scope: ", append ? "append" : "prepend"); strncat(buffer, params[1], 40); set_message(buffer, True); XtFree(expr); return; } if (!expr) { set_message("No selected article!", True); return; } if (!global_kill_file) file = get_kill_file(global.curr_group); else { group = "."; file = get_kill_file(NULL); } hot = no_params == 3; if (add_kill_node(file, append, field, scope, hot, hot ? params[2] : NULL, expr, group)) { sprintf(buffer, "%s %s-scope %s %s entry to %skill file.", append ? "Appended" : "Prepended", scope_str[scope], field_str[field], hot ? "hot" : "kill", global_kill_file ? "global " : ""); set_message(buffer, False); } XtFree(expr); } void action_kill_append(Widget w, XEvent *event, String *params, Cardinal *no_params) { add_kill(params, *no_params, True, False); } void action_kill_prepend(Widget w, XEvent *event, String *params, Cardinal *no_params) { add_kill(params, *no_params, False, False); } void action_kill_append_global(Widget w, XEvent *event, String *params, Cardinal *no_params) { add_kill(params, *no_params, True, True); } void action_kill_prepend_global(Widget w, XEvent *event, String *params, Cardinal *no_params) { add_kill(params, *no_params, False, True); } void action_popup_kill(Widget w, XEvent *event, String *params, Cardinal *no_params) { GROUP *group; if (global.busy || global.mode == NewsModeDisconnected) return; if (*no_params != 1) group = NULL; else { group = find_group(params[0]); if (!group) { set_message("No such group!", True); return; } } kill_edit_popup(group); } ./knews-1.0b.1/src/uudecode.h100644 1244 1244 272 6455455547 14357 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ extern void uudecode_callback(Widget, XtPointer, XtPointer); extern void action_uudecode(Widget, XEvent*, String*, Cardinal*); ./knews-1.0b.1/src/util.h100644 1244 1244 4307 6570004717 13545 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ extern ARTICLE *next_in_thread_preorder(ARTICLE*); extern ARTICLE *next_in_subthread_preorder(ARTICLE*, ARTICLE*); extern ARTICLE *preorder_skip_subthread(ARTICLE*); extern ARTICLE *next_in_thread_wrap(ARTICLE*); extern ARTICLE *next_in_subthread_wrap(ARTICLE*, ARTICLE*); extern ARTICLE *next_in_thread_dont_wrap(ARTICLE*); extern ARTICLE *prev_in_thread_dont_wrap(ARTICLE*); extern void update_subj_hot_value(SUBJECT*); extern long mark_subject_unread(SUBJECT*); extern long mark_sub_subject_unread(ARTICLE*, SUBJECT*); extern long mark_subject_read(SUBJECT*, int, int); extern long mark_sub_subject_read(ARTICLE*, SUBJECT*, int, int); extern long mark_subthread_read(ARTICLE*, int, int); extern long mark_thread_read(ARTICLE*, int, int); extern long mark_subject_hot(SUBJECT*, Pixmap); extern long mark_thread_hot(ARTICLE*, Pixmap); extern long mark_subthread_hot(ARTICLE*, Pixmap); extern void process_xref(ARTICLE*); extern void fake_xref(ARTICLE*, char**, int); extern void calc_no_unread(GROUP*); extern void free_read_arts_list(GROUP*); extern ART_LIST_NODE *create_read_arts_list(void); extern void list_all_arts_read(GROUP*); extern ARTICLE *first_unread_article_with_subject(SUBJECT*); extern void ascii_lower(char*); extern void ascii_nlower(char*, long); extern void memcpy_lower(char*, char*, long); extern int case_strcmp(const char*, const char*); extern int case_strncmp(const char*, const char*, long); extern int case_lstrcmp(const char*, const char*); extern int case_lstrncmp(const char*, const char*, long); extern int case_lhassub(const char*, const char*); #define IS_LOWER(c) \ ((unsigned int)((c) - 'a') <= 'z' - 'a') #define IS_UPPER(c) \ ((unsigned int)((c) - 'A') <= 'Z' - 'A') #define IS_ALPHA(c) \ (IS_UPPER(c) || IS_LOWER(c)) #define LOWER(u) \ ((unsigned char)(u) + ('a' - 'A')) #define UPPER(u) \ ((unsigned char)(u) - ('a' - 'A')) #define TO_LOWER(c) \ (IS_UPPER(c) ? LOWER(c) : (unsigned char)(c)) #define TO_UPPER(c) \ (IS_LOWER(c) ? UPPER(c) : (unsigned char)(c)) #define IS_DIGIT(c) \ ((unsigned int)((c) - '0') < 10u) #define IS_SPACE(c) \ ((c) == ' ' || (c) == '\t') #define IS_DOT(buffer) \ ((buffer)[0] == '.' && (buffer)[1] == '\0') ./knews-1.0b.1/src/viewer.h100644 1244 1244 276 6625552256 14061 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ extern void do_viewer(char*, char*, char*, char*, char*, long); extern void destroy_pixmap_callback(Widget, XtPointer, XtPointer); ./knews-1.0b.1/src/save.c100644 1244 1244 64553 6567773352 13570 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include "global.h" #include #include #include "cache.h" #include "child.h" #include "codes.h" #include "connect.h" #include "file.h" #include "save.h" #include "server.h" #include "tag.h" #include "util.h" #include "widgets.h" #include "xutil.h" #include "../Widgets/ArtText.h" #include "../Widgets/FileSel.h" #include "../Widgets/Knapp.h" #include "../Widgets/Layout.h" #include "../Widgets/Message.h" #include "../Widgets/TextField.h" #include "../Widgets/Toggle.h" #include "../Widgets/Util.h" typedef enum { SaveScopeArtwin, SaveScopeArticle, SaveScopeSubject, SaveScopeThread, SaveScopeSubthread, SaveScopeTagged } SaveScope; static struct { Widget shell; /***/ Widget file_message; Widget file_field; Widget shell_message; Widget shell_field; Widget file_chooser; Widget choose_knapp; Widget ok_knapp; Widget close_knapp; /***/ Widget bogus_from_toggle; Widget bogus_subj_toggle; Widget head_toggle; Widget body_toggle; Widget empty_toggle; /***/ Widget artwin_toggle; Widget article_toggle; Widget subject_toggle; Widget thread_toggle; Widget subthread_toggle; Widget tagged_toggle; } save_widgets; static int is_save = True; /*************************************************************************/ static void set_save_label(int to_pipe) { is_save = !to_pipe; KnappSetLabelNo(save_widgets.ok_knapp, !is_save, True); } static void get_email(ARTICLE *art, char *buf, int max_len) { char *c = art->from; char *p1, *p2; int len; if ((p1 = strchr(c, '<')) && (p2 = strchr(p1, '>'))) { len = p2 - p1 - 1; if (len > max_len) len = max_len; memcpy(buf, p1 + 1, len); buf[len] = '\0'; return; } p1 = strchr(c, '('); if (p1) { do { p1--; } while (p1 > c && (*p1 == ' ' || *p1 == '\t')); if (p1 > c) { len = p1 - c + 1; if (len > max_len) len = max_len; memcpy(buf, c, len); buf[len] = '\0'; return; } } len = strlen(c); if (len > max_len) len = max_len; memcpy(buf, c, len); buf[len] = '\0'; } static ARTICLE **get_art_list(SaveScope scope, long *n_arts) { ARTICLE *art = NULL, *top = NULL; ARTICLE **result = NULL; SUBJECT *subj = NULL; int n, n_alloc; switch (scope) { case SaveScopeArtwin: result = (ARTICLE **)XtMalloc(sizeof result[0]); *result = NULL; *n_arts = 0; return result; case SaveScopeArticle: if (!global.curr_art) set_message("No selected article!", True); else { result = (ARTICLE **)XtMalloc(sizeof result[0]); result[0] = global.curr_art; *n_arts = 1; } return result; case SaveScopeTagged: { ARTICLE **tagged = get_tagged_articles(); n_alloc = no_tagged_articles(); if (n_alloc <= 0) { set_message("No tagged articles!", True); return NULL; } result = (ARTICLE **)XtMalloc(n_alloc * sizeof result[0]); for (n = 0 ; n < n_alloc ; n++) result[n] = tagged[n]; *n_arts = n; return result; } case SaveScopeSubject: subj = global.curr_subj; if (!subj) { set_message("No subject selected!", True); return NULL; } art = subj->thread; break; case SaveScopeThread: if (!global.curr_subj) { set_message("No thread selected!", True); return NULL; } art = global.curr_subj->thread; break; case SaveScopeSubthread: if (!global.curr_art) { set_message("No article selected!", True); return NULL; } top = art = global.curr_art; break; } while (art && (!art->from || (subj && art->subject != subj))) if (top) art = next_in_subthread_preorder(art, top); else art = next_in_thread_preorder(art); if (!art) return NULL; n = 0; n_alloc = 16; result = (ARTICLE **)XtMalloc(n_alloc * sizeof result[0]); while (art) { if (n + 8 > n_alloc) { n_alloc += 32; result = (ARTICLE **)XtRealloc((char *)result, n_alloc * sizeof result[0]); } result[n++] = art; do { if (top) art = next_in_subthread_preorder(art, top); else art = next_in_thread_preorder(art); } while (art && (!art->from || (subj && art->subject != subj))); } *n_arts = n; return result; } static char *save_article(FILE *fp, SERVER *server, ARTICLE *art, int what) { char *buffer; if (what & SAVE_BOGUS_FROM) { char email[128]; time_t t = art->date; char *c = ctime(&t); get_email(art, email, sizeof email - 1); fprintf(fp, "From %s %s", email, c); } if (what & SAVE_BOGUS_SUBJ) fprintf(fp, "Subject: %s\n", art->subject->subject); while ((buffer = server_read(server)) && buffer[0] != '\0' && !IS_DOT(buffer)) if (what & SAVE_HEAD) fprintf(fp, "%s\n", buffer); if (!buffer || IS_DOT(buffer)) return buffer; if ((what & (SAVE_HEAD | SAVE_BODY)) == (SAVE_HEAD | SAVE_BODY)) fputc('\n', fp); while ((buffer = server_read(server)) && !IS_DOT(buffer)) if (what & SAVE_BODY) { if ((what & SAVE_BOGUS_FROM) && memcmp(buffer, "From ", 5) == 0) fputc('>', fp); fprintf(fp, "%s\n", buffer); } if (what & SAVE_EMPTY) fputc('\n', fp); return buffer; } int save_to_file(FILE *fp, char *message, ARTICLE **arts, long n, int what, long *result) { char *p = message + strlen(message); long i; result[1] = 0; for (i = 0 ; i < n ; i++) { char *buffer; SERVER *server; server = cache_get_server(arts[i]->no, False); if (!server) { char command[32]; server = main_server; sprintf(command, "ARTICLE %ld\r\n", arts[i]->no); buffer = server_comm(server, command, True); if (!buffer) return -1; if (atoi(buffer) != NNTP_OK_ARTICLE) { result[1]++; continue; } } buffer = save_article(fp, server, arts[i], what); if (server != main_server) server_free(server); else if (!buffer) return -1; sprintf(p, "%ld", i + 1); set_message(message, False); } if (fflush(fp) < 0) result[0] = -1; else result[0] = n - result[1]; return 0; } static int get_what(void) { int bogus_from = ToggleGet(save_widgets.bogus_from_toggle); int bogus_subj = ToggleGet(save_widgets.bogus_subj_toggle); int head = ToggleGet(save_widgets.head_toggle); int body = ToggleGet(save_widgets.body_toggle); int empty = ToggleGet(save_widgets.empty_toggle); int what = 0; if (bogus_from) what |= SAVE_BOGUS_FROM; if (bogus_subj) what |= SAVE_BOGUS_SUBJ; if (head) what |= SAVE_HEAD; if (body) what |= SAVE_BODY; if (empty) what |= SAVE_EMPTY; return what; } static int get_scope(void) { if (ToggleGet(save_widgets.artwin_toggle)) return SaveScopeArtwin; if (ToggleGet(save_widgets.article_toggle)) return SaveScopeArticle; if (ToggleGet(save_widgets.subject_toggle)) return SaveScopeSubject; if (ToggleGet(save_widgets.thread_toggle)) return SaveScopeThread; if (ToggleGet(save_widgets.subthread_toggle)) return SaveScopeSubthread; if (ToggleGet(save_widgets.tagged_toggle)) return SaveScopeTagged; return SaveScopeArticle; } static void do_save(char *file_name, SaveScope scope, int what) { FILE *file; ARTICLE **art_list = NULL; long result[2], n; int status, fclose_status; char message[128]; if (global.busy) return; if (global.mode != NewsModeGroup && global.mode != NewsModeThread) { set_message("Not in a newsgroup!", True); return; } else if (!file_name || file_name[0] == '\0') { set_message("No file specified!", True); return; } else if (!(what & (SAVE_HEAD | SAVE_BODY))) { set_message("Nothing to save (neither head or body selected)!", True); return; } art_list = get_art_list(scope, &n); if (!art_list) return; file = fopen_expand(file_name, "a", True); if (!file) { set_message("Failed to open file!", True); XtFree((char *)art_list); return; } sprintf(message, "Saving... "); if (scope == SaveScopeArtwin) { status = ArtTextDumpToFile(main_widgets.text, file); result[0] = 1; result[1] = 0; } else { set_busy(True); status = save_to_file(file, message, art_list, n, what, result); unset_busy(); } if ((fclose_status = fclose(file)) < 0) perror("knews: fclose"); XtFree((char *)art_list); if (status < 0) { set_busy(True); reconnect_server(True); unset_busy(); return; } else if (result[0] < 0 || fclose_status < 0) { set_message("File error!", True); return; } else if (result[1] > 0) { sprintf(message, "%ld articles saved, failed to get %ld articles.", result[0], result[1]); set_message(message, True); } else { sprintf(message, "%ld articles saved.", result[0]); set_message(message, False); } if (scope == SaveScopeTagged) clear_tagged_articles(); } void pipe_context_callback(void *data, int status, char *stderr_buf) { char message[128]; char *cmd = data; int ok = False; if (strlen(cmd) > 100) cmd[100] = '\0'; if (WIFEXITED(status)) switch (WEXITSTATUS(status)) { case 0: sprintf(message, "'%s' exited ok.", cmd); ok = True; break; case 127: sprintf(message, "Failed to start '%s'!", cmd); break; default: sprintf(message, "'%s' exited abnormally!", cmd); break; } else if (WIFSIGNALED(status)) sprintf(message, "'%s' caught %s!", cmd, signal_string(WTERMSIG(status))); else sprintf(message, "Unknown problem with '%s'!", cmd); if (!ok) stderr_popup(stderr_buf, -1); set_message(message, !ok); XtFree(cmd); } static void do_pipe(char *command, SaveScope scope, int what) { FILE *file = NULL; char *file_name; ARTICLE **art_list = NULL; long result[2], n; int status, fflush_status; char message[128]; pid_t pid; char *temp; if (global.busy) return; if (global.mode != NewsModeGroup && global.mode != NewsModeThread) { set_message("Not in a newsgroup!", True); return; } else if (!command || command[0] == '\0') { set_message("No command specified!", True); return; } else if (!(what & (SAVE_HEAD | SAVE_BODY))) { set_message("Nothing to pipe (neither head or body selected)!", True); return; } art_list = get_art_list(scope, &n); if (!art_list) return; file = create_temp_file(&file_name); if (!file) { set_message("Failed to create temporary file!", True); XtFree((char *)art_list); return; } unlink(file_name); sprintf(message, "Saving to temp file... "); if (scope == SaveScopeArtwin) { status = ArtTextDumpToFile(main_widgets.text, file); result[0] = 1; result[1] = 0; } else { set_busy(True); status = save_to_file(file, message, art_list, n, what, result); unset_busy(); } if ((fflush_status = fflush(file)) < 0) perror("knews: fflush"); XtFree((char *)art_list); if (status < 0) { fclose(file); set_busy(True); reconnect_server(True); unset_busy(); return; } else if (result[0] < 0 || fflush_status < 0) { fclose(file); set_message("Error with temp file!", True); return; } else if (result[1] > 0) { sprintf(message, "%ld articles saved to temp file, failed to get %ld " "articles. Pipe started.", result[0], result[1]); set_message(message, True); } else { sprintf(message, "%ld articles saved to temp file. Pipe started.", result[0]); set_message(message, False); } if (scope == SaveScopeTagged) clear_tagged_articles(); temp = XtNewString(command); pid = fork_nicely(temp, pipe_context_callback, global.stderr_timeout >= 0); if (pid < 0) { set_message("Fork failed!", True); XtFree(temp); fclose(file); return; } if (pid == 0) { /* child */ int fd; fd = fileno(file); if (fd != STDIN_FILENO) { fd = dup2(fd, STDIN_FILENO); if (fd < 0) { perror("knews: dup2"); _exit(127); } } if (lseek(fd, SEEK_SET, 0) < 0) { perror("knews: lseek"); _exit(127); } execl(BIN_SH, "sh", "-c", command, (char *)NULL); perror("knews: execl " BIN_SH); _exit(127); } set_message("Pipe started.", False); fclose(file); } static void save_callback(Widget w, XtPointer client_data, XtPointer call_data) { char *file_name = TextFieldGetBuffer(save_widgets.file_field); do_save(file_name, get_scope(), get_what()); XtFree(file_name); } static void pipe_callback(Widget w, XtPointer client_data, XtPointer call_data) { char *command = TextFieldGetBuffer(save_widgets.shell_field); do_pipe(command, get_scope(), get_what()); XtFree(command); } static void set_toggles_sensitive(int sens) { XtSetSensitive(save_widgets.bogus_from_toggle, sens); XtSetSensitive(save_widgets.bogus_subj_toggle, sens); XtSetSensitive(save_widgets.head_toggle, sens); XtSetSensitive(save_widgets.body_toggle, sens); XtSetSensitive(save_widgets.empty_toggle, sens); } static void file_chooser_callback(Widget w, XtPointer client_data, XtPointer call_data) { char *buffer = (char *)call_data; Arg arg; XtPopdown(w); if (!buffer) return; XtSetArg(arg, XtNbuffer, buffer); XtSetValues(save_widgets.file_field, &arg, 1); XtSetKeyboardFocus(save_widgets.shell, save_widgets.file_field); set_save_label(False); } static void choose_knapp_callback(Widget gw, XtPointer client_data, XtPointer call_data) { if (!save_widgets.file_chooser) { Arg args[4]; XtSetArg(args[0], XtNcolormap, global.cmap); XtSetArg(args[1], XtNvisual, global.visual); XtSetArg(args[2], XtNdepth, global.depth); save_widgets.file_chooser = XtCreatePopupShell("filechooser", fileSelWidgetClass, main_widgets.shell, args, 3); XtAddCallback(save_widgets.file_chooser, XtNcallback, file_chooser_callback, NULL); } XtPopup(save_widgets.file_chooser, XtGrabExclusive); } static void close_knapp_callback(Widget gw, XtPointer client_data, XtPointer call_data) { XtPopdown(save_widgets.shell); } static void toggle_callback(Widget gw, XtPointer client_data, XtPointer call_data) { Boolean *set = (Boolean *)call_data; if (set) *set = !*set; } static void scope_toggle_callback(Widget w, XtPointer client_data, XtPointer call_data) { Boolean *set = (Boolean *)call_data; if (!set || *set) return; ToggleSet(save_widgets.artwin_toggle, False); ToggleSet(save_widgets.article_toggle, False); ToggleSet(save_widgets.subject_toggle, False); ToggleSet(save_widgets.thread_toggle, False); ToggleSet(save_widgets.subthread_toggle, False); ToggleSet(save_widgets.tagged_toggle, False); ToggleSet(w, True); *set = True; set_toggles_sensitive(w != save_widgets.artwin_toggle); } static void knapp_callback(Widget w, XtPointer client_data, XtPointer call_data) { (is_save ? save_callback : pipe_callback)(w, client_data, call_data); } static void focus_callback(Widget w, XtPointer client_data, XtPointer call_data) { if (w == save_widgets.shell_field) set_save_label(True); else if (w == save_widgets.file_field) set_save_label(False); } static void tab_callback(Widget w, XtPointer client_data, XtPointer call_data) { if (w == save_widgets.shell_field) { XtSetKeyboardFocus(save_widgets.shell, save_widgets.file_field); set_save_label(True); } else if (w == save_widgets.file_field) { XtSetKeyboardFocus(save_widgets.shell, save_widgets.shell_field); set_save_label(False); } } /*************************************************************************/ static void create_save_widgets(void) { Arg args[8]; Widget layout, w; XtSetArg(args[0], XtNallowShellResize, True); XtSetArg(args[1], XtNinput, True); XtSetArg(args[2], XtNtitle, "knews: save/pipe"); XtSetArg(args[3], XtNiconName, "save/pipe"); XtSetArg(args[4], XtNcolormap, global.cmap); XtSetArg(args[5], XtNvisual, global.visual); XtSetArg(args[6], XtNdepth, global.depth); save_widgets.shell = XtCreatePopupShell("saveshell", topLevelShellWidgetClass, main_widgets.shell, args, 7); save_widgets.file_chooser = NULL; layout = XtVaCreateManagedWidget("savelayout", layoutWidgetClass, save_widgets.shell, XtVaTypedArg, XtNlayout, XtRString, #include "layouts/save.h" (int)sizeof(String), (void *)0); XtSetArg(args[0], XtNcenter, True); XtCreateManagedWidget("message", messageWidgetClass, layout, args, 1); XtSetArg(args[0], XtNcenter, False); save_widgets.shell_message = XtCreateManagedWidget("shellmessage", messageWidgetClass, layout, args, 1); XtSetArg(args[0], XtNfocusRoot, save_widgets.shell); XtSetArg(args[1], XtNsingleLine, True); save_widgets.shell_field = XtCreateManagedWidget("shellfield", textFieldWidgetClass, layout, args, 2); XtAddCallback(save_widgets.shell_field, XtNcallback, pipe_callback, False); XtAddCallback(save_widgets.shell_field, XtNfocusCallback, focus_callback, NULL); XtAddCallback(save_widgets.shell_field, XtNtabCallback, tab_callback, NULL); XtSetArg(args[0], XtNcenter, False); save_widgets.file_message = XtCreateManagedWidget("filemessage", messageWidgetClass, layout, args, 1); XtSetArg(args[0], XtNfocusRoot, save_widgets.shell); XtSetArg(args[1], XtNsingleLine, True); save_widgets.file_field = XtCreateManagedWidget("filefield", textFieldWidgetClass, layout, args, 2); XtAddCallback(save_widgets.file_field, XtNcallback, save_callback, NULL); XtAddCallback(save_widgets.file_field, XtNfocusCallback, focus_callback, NULL); XtAddCallback(save_widgets.file_field, XtNtabCallback, tab_callback, NULL); XtSetArg(args[0], XtNresizable, False); save_widgets.ok_knapp = XtCreateManagedWidget("ok", knappWidgetClass, layout, args, 1); XtAddCallback(save_widgets.ok_knapp, XtNcallback, knapp_callback, NULL); save_widgets.close_knapp = XtCreateManagedWidget("close", knappWidgetClass, layout, NULL, 0); XtAddCallback(save_widgets.close_knapp, XtNcallback, close_knapp_callback, NULL); save_widgets.choose_knapp = XtCreateManagedWidget("choose", knappWidgetClass, layout, NULL, 0); XtAddCallback(save_widgets.choose_knapp, XtNcallback, choose_knapp_callback, NULL); /***/ save_widgets.bogus_from_toggle = XtCreateManagedWidget("bogusfrom", toggleWidgetClass, layout, NULL, 0); XtAddCallback(save_widgets.bogus_from_toggle, XtNcallback, toggle_callback, NULL); save_widgets.bogus_subj_toggle = XtCreateManagedWidget("bogussubj", toggleWidgetClass, layout, NULL, 0); XtAddCallback(save_widgets.bogus_subj_toggle, XtNcallback, toggle_callback, NULL); save_widgets.head_toggle = XtCreateManagedWidget("header", toggleWidgetClass, layout, NULL, 0); XtAddCallback(save_widgets.head_toggle, XtNcallback, toggle_callback, NULL); save_widgets.body_toggle = XtCreateManagedWidget("body", toggleWidgetClass, layout, NULL, 0); XtAddCallback(save_widgets.body_toggle, XtNcallback, toggle_callback, NULL); save_widgets.empty_toggle = XtCreateManagedWidget("empty", toggleWidgetClass, layout, NULL, 0); XtAddCallback(save_widgets.empty_toggle, XtNcallback, toggle_callback, NULL); /***/ save_widgets.artwin_toggle = XtCreateManagedWidget("artwin", toggleWidgetClass, layout, NULL, 0); XtAddCallback(save_widgets.artwin_toggle, XtNcallback, scope_toggle_callback, NULL); save_widgets.article_toggle = XtCreateManagedWidget("article", toggleWidgetClass, layout, NULL, 0); XtAddCallback(save_widgets.article_toggle, XtNcallback, scope_toggle_callback, NULL); save_widgets.subject_toggle = XtCreateManagedWidget("subject", toggleWidgetClass, layout, NULL, 0); XtAddCallback(save_widgets.subject_toggle, XtNcallback, scope_toggle_callback, NULL); save_widgets.thread_toggle = XtCreateManagedWidget("thread", toggleWidgetClass, layout, NULL, 0); XtAddCallback(save_widgets.thread_toggle, XtNcallback, scope_toggle_callback, NULL); save_widgets.subthread_toggle = XtCreateManagedWidget("subthread", toggleWidgetClass, layout, NULL, 0); XtAddCallback(save_widgets.subthread_toggle, XtNcallback, scope_toggle_callback, NULL); save_widgets.tagged_toggle = XtCreateManagedWidget("tagged", toggleWidgetClass, layout, NULL, 0); XtAddCallback(save_widgets.tagged_toggle, XtNcallback, scope_toggle_callback, NULL); w = save_widgets.article_toggle; if (ToggleGet(save_widgets.artwin_toggle)) w = save_widgets.artwin_toggle; if (ToggleGet(save_widgets.subject_toggle)) w = save_widgets.subject_toggle; if (ToggleGet(save_widgets.thread_toggle)) w = save_widgets.thread_toggle; if (ToggleGet(save_widgets.subthread_toggle)) w = save_widgets.subthread_toggle; if (ToggleGet(save_widgets.tagged_toggle)) w = save_widgets.tagged_toggle; ToggleSet(save_widgets.artwin_toggle, False); ToggleSet(save_widgets.article_toggle, False); ToggleSet(save_widgets.subject_toggle, False); ToggleSet(save_widgets.thread_toggle, False); ToggleSet(save_widgets.subthread_toggle, False); ToggleSet(save_widgets.tagged_toggle, False); ToggleSet(w, True); XtSetKeyboardFocus(save_widgets.shell, save_widgets.file_field); XtRealizeWidget(save_widgets.shell); XtInstallAllAccelerators(save_widgets.shell, save_widgets.shell); add_WM_DELETE_WINDOW_callback(save_widgets.shell, close_knapp_callback, NULL); if (global.busy) set_busy_save(True); } void popup_save(void) { if (!save_widgets.shell) create_save_widgets(); XtPopup(save_widgets.shell, XtGrabNone); if (global.busy) set_busy_save(True); } void popdown_save(void) { if (save_widgets.shell) XtPopdown(save_widgets.shell); } static void do_action(int save, String *params, Cardinal n) { int what = 0; char *c; SaveScope scope = SaveScopeArticle; if (n == 0) { if (save) set_message("No filename specified in save/pipe action!", True); else set_message("No shell command specified in save/pipe action!", True); return; } else if (n == 1) { set_message("To few arguments to save/pipe action!", True); return; } c = params[1]; while (*c != '\0') { switch (*c++) { case 'f': case 'F': what |= SAVE_BOGUS_FROM; break; case 'S': case 's': what |= SAVE_BOGUS_SUBJ; break; case 'H': case 'h': what |= SAVE_HEAD; break; case 'B': case 'b': what |= SAVE_BODY; break; case 'E': case 'e': what |= SAVE_EMPTY; break; } } if (!(what & (SAVE_BODY|SAVE_HEAD)) || !params[0] || params[0][0] == '\0') { if (save) set_message("Nothing to save!", True); else set_message("Nothing to pipe!", True); return; } if (n >= 3) { switch (params[2][0]) { case 'W': case 'w': scope = SaveScopeArtwin; break; case 'A': case 'a': scope = SaveScopeArticle; break; case 'S': case 's': if ((params[2][1] == 'U' || params[2][1] == 'u') && (params[2][2] == 'B' || params[2][2] == 'b')) { switch (params[2][3]) { case 'J': case 'j': scope = SaveScopeSubject; break; case 'T': case 't': scope = SaveScopeSubthread; break; } } break; case 'T': case 't': switch (params[2][1]) { case 'A': case 'a': scope = SaveScopeTagged; break; case 'H': case 'h': scope = SaveScopeThread; break; } break; } } if (save) do_save(params[0], scope, what); else do_pipe(params[0], scope, what); } void action_save(Widget w, XEvent *string, String *params, Cardinal *no_params) { do_action(True, params, *no_params); } void action_pipe(Widget w, XEvent *string, String *params, Cardinal *no_params) { do_action(False, params, *no_params); } /*************************************************************************/ void set_busy_save(int busy) { if (!save_widgets.shell) return; XDefineCursor(display, XtWindow(save_widgets.shell), busy ? global.busy_cursor : global.cursor); KnappSetActive(save_widgets.ok_knapp, !busy); } /************************************************************************/ /* * Lots of people enclose URLs in <> or "". Also, don't allow * quotes for sequrity reasons, since we're passing it to the shell. */ #define IS_URL_CHAR(c) (!strchr(" \t<>\"'`\\()[]{}&", (c))) #define MIN_URL_LEN 4 void text_url_callback(Widget w, XtPointer client_data, XtPointer call_data) { ArtTextUrlReport *report = (ArtTextUrlReport *)call_data; char *url_cmd = global.url_command; char *temp; long i; pid_t pid; if (!url_cmd) { set_message("Error: Resource Knews.urlCommand not set!", True); return; } /* * Very crude parser... */ if (!report->sel_ok) { const char *c; c = report->line + report->start; if (*c != '\0' && IS_URL_CHAR(*c)) { while (*++c != '\0' && IS_URL_CHAR(*c)) report->stop++; c = report->line + report->start; while (report->start > 0 && IS_URL_CHAR(*--c)) report->start--; if (report->start + 4 < report->stop && strncmp(report->line + report->start, "URL:", 4) == 0) report->start += 4; if (report->start < report->stop && report->line[report->stop] == '.') report->stop--; } } if (report->stop - report->start < MIN_URL_LEN) { set_message("Url too short!", True); return; } for (i = report->start ; i <= report->stop ; i++) if (!IS_URL_CHAR(report->line[i])) { set_message("Illegal character in URL!", True); return; } report->sel_ok = True; temp = XtNewString("urlCommand"); pid = fork_nicely(temp, pipe_context_callback, global.stderr_timeout >= 0); if (pid < 0) { set_message("Error: fork failed!", True); XtFree(temp); return; } if (pid == 0) { char *prog, *url; long i, n, url_len; url_len = report->stop - report->start + 1; url = malloc(url_len + 8); n = 1024; prog = malloc(1024); if (!url || !prog) { fputs("knews: out of memory!\n", stderr); _exit(127); } memcpy(url, report->line + report->start, url_len); url[url_len] = '\0'; for (i = 0 ; *url_cmd != '\0' ; url_cmd++) { if (i + url_len + 8 > n) { n += i + url_len + 8; prog = realloc(prog, n); if (!prog) { fputs("knews: out of memory!\n", stderr); _exit(127); } } if (*url_cmd == '%' && *++url_cmd == 's') { memcpy(prog + i, url, url_len); i += url_len; } else { prog[i++] = *url_cmd; } } prog[i] = '\0'; execl(BIN_SH, "sh", "-c", prog, (char *)0); perror("knews: execl " BIN_SH); _exit(127); } set_message("Executing urlCommand.", False); } ./knews-1.0b.1/src/p_check.c100644 1244 1244 32562 6455455547 14220 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include "global.h" #include #include "file.h" #include "newsrc.h" #include "p_I.h" #include "p_check.h" #include "resource.h" #include "thread.h" #include "util.h" #include "../Widgets/ArtText.h" static int has_8bit(const char *art) { while (*art != '\0') if ((unsigned char)*art++ & 0x80) return True; return False; } static void add_line(Widget w, const char *c, int error) { Pixel pixel = error ? global.alert_pixel : global.pixel; ArtTextAddLine(w, c, NULL, pixel); } static int max_len(const char *c, char sep) { int max = 0, len; const char *p; do { p = strchr(c, sep); if (!p) len = strlen(c); else { len = p - c; c = p + 1; } if (len > max) max = len; } while (p); return max; } static void check_quoting(const char *art, char *prefix, long *n_lines, long *n_quoted) { int n = prefix ? strlen(prefix) : 0; *n_lines = 0; *n_quoted = 0; for (;;) { const char *p = strchr(art, '\n'); ++*n_lines; if (n > 0 && strncmp(prefix, art, n) == 0) ++*n_quoted; if (!p) break; art = p + 1; } } static int an_admin(void) { static int result = -1; struct passwd *pwd; uid_t uid; if (result < 0) result = (uid = getuid()) == 0 || ((pwd = getpwnam("news")) && uid == pwd->pw_uid); return result; } static ARTICLE *get_article(const char *field, int len) { char *msgid, *alpha; ARTICLE *art; msgid = memcpy(XtMalloc(len + 1), field, len); msgid[len] = '\0'; alpha = strchr(msgid, '@'); if (alpha) ascii_lower(alpha + 1); art = find_article(msgid, len); XtFree(msgid); return art; } static int check_cancel(Widget w, const char *field, const char *end) { while (field < end) { ARTICLE *art; const char *c; if (*field++ != '<') { add_line(w, "Syntax error in control message.", True); return False; } c = field; while (c < end && *c != '\0' && *c != '\n' && *c != '>' && *c != '<') c++; if (c >= end || *c != '>') { add_line(w, "Syntax error in control message.", True); return False; } if ((global.mode != NewsModeGroup && global.mode != NewsModeThread) || !(art = get_article(field, c - field))) { add_line(w, "Error! Failed to authenticate cancel message:", True); add_line(w, " Couldn't find referenced article.", True); return False; } /* possible security hole */ if (!strstr(art->from, global.mail_name) || !strstr(art->from, global.domain_name)) { add_line(w, "Error! You cannot cancel somebody else's article.", True); return False; } field = c + 1; while (field < end && (IS_SPACE(*field) || *field == '\n')) field++; } return True; } static char *copy_field(const char *start, const char *end) { char *ret, *c; long n = end - start; ret = XtMalloc(n + 1); memcpy(ret, start, n); ret[n] = '\0'; for (c = strchr(ret, '\n') ; c ; c = strchr(c, '\n')) *c++ = ' '; return ret; } static int check_article(PostContext *context, const char *art, Widget w, char **newsgroups, char **followupto, char **subject, char **to, char **cc, int *has_ct, int *line_len, long *n_lines, long *n_quoted) { char buffer[512]; int has_cte = False; int has_from = False; *newsgroups = NULL; *followupto = NULL; *subject = NULL; *to = NULL; *cc = NULL; *has_ct = False; *line_len = 0; *n_lines = 0; *n_quoted = 0; #define RETURN_ERROR(msg) \ do { \ add_line(w, msg, True); \ return False; \ } while (0) if (*art == '\0') RETURN_ERROR("Error! Empty article."); if (*art == '\n') RETURN_ERROR("Error! No headers in article."); /* * Check headers. */ context->line = 1; while (*art != '\n') { const char *field = strchr(art, ':'); const char *end, *tmp; int lines, len; unsigned char ch; if (!field) { add_line(w, "Syntax error in article header near", True); add_line(w, "", True); buffer[0] = '\0'; strncat(buffer, art, 128); add_line(w, buffer, True); add_line(w, "", True); add_line(w, "Remember to put an EMPTY line between the", True); add_line(w, "headers and the body (the article/mail text)!", True); return False; } len = field - art; if (((tmp = strchr(art, ' ')) && tmp < field) || ((tmp = strchr(art, '\t')) && tmp < field) || ((tmp = strchr(art, '\n')) && tmp < field)) { strcpy(buffer, "Syntax error in '"); if (len > 128) len = 128; strncat(buffer, art, len); strcat(buffer, "' header."); add_line(w, buffer, True); add_line(w, "Remember to put an EMPTY line between the", True); add_line(w, "headers and the body (the article/mail text)!", True); return False; } field++; if (*field++ != ' ') { strcpy(buffer, "Syntax error in '"); if (len > 128) len = 128; strncat(buffer, art, len); strcat(buffer, "' header."); add_line(w, buffer, True); add_line(w, "There must be a space after the colon!", True); return False; } while (IS_SPACE(*field)) field++; lines = 1; for (end = strchr(field, '\n') ; end && IS_SPACE(end[1]) ; end = strchr(end + 1, '\n')) lines++; if (!end) RETURN_ERROR("Error: article is all headers!"); ch = *art; if (isupper(ch)) ch = tolower(ch); #define IS_HEADER(header) \ (len == sizeof(header) - 1 && \ case_lstrncmp(art, header, len) == 0) switch (ch) { case 'a': if (IS_HEADER("also-control") && !an_admin()) if (case_lstrncmp(field, "cancel ", 7) != 0) RETURN_ERROR("Error! No permission to " "send control message."); else if (!check_cancel(w, field + 7, end)) return False; break; case 'c': if (IS_HEADER("cc") && !*cc) *cc = copy_field(field, end); else if (IS_HEADER("content-type")) if (*has_ct) RETURN_ERROR("Duplicate 'Content-Type:' header."); else *has_ct = True; else if (IS_HEADER("content-transfer-encoding")) if (has_cte) RETURN_ERROR("Duplicate 'Content-Transfer-Encoding:' " " header."); else has_cte = True; else if (IS_HEADER("control") && !an_admin()) if (case_lstrncmp(field, "cancel ", 7) != 0) RETURN_ERROR("Error! No permission " "to send control message."); else if (!check_cancel(w, field + 7, end)) return False; break; case 'f': if (IS_HEADER("from")) { if (has_from) RETURN_ERROR("Error! Duplicate 'From:' header."); has_from = True; if (!(tmp = strstr(field, global.domain_name)) || tmp > end || !(tmp = strstr(field, global.user_id)) || tmp > end) context->flags |= NEEDS_SENDER; } else if (IS_HEADER("followup-to")) { if (*followupto) RETURN_ERROR("Error! Duplicate 'Followup-To:' header"); if (lines > 1) { add_line(w, "Error! The 'Followup-To:' header", True); add_line(w, "cannot be continued on several lines", True); return False; } *followupto = copy_field(field, end); if (*field == '\n' || *field == '\0') RETURN_ERROR("Error! Empty 'Followup-To:' header"); if (((tmp = strchr(field, ' ')) && tmp < end) || ((tmp = strchr(field, '\t')) && tmp < end)) { add_line(w, "Error! There can be no spaces between", True); add_line(w, "the commas in the 'Followup-To:' header", True); return False; } break; } case 'n': if (IS_HEADER("newsgroups")) { if (*newsgroups) RETURN_ERROR("Error! Duplicate 'Newsgroups:' header."); if (lines > 1) { add_line(w, "Error! The 'Newsgroups:' header", True); add_line(w, "cannot be continued on several lines.", True); return False; } *newsgroups = copy_field(field, end); if (*field == '\n' || *field == '\0') RETURN_ERROR("Error! Empty 'Newsgroups:' header"); if (((tmp = strchr(field, ' ')) && tmp < end) || ((tmp = strchr(field, '\t')) && tmp < end)) { add_line(w, "Error! There can be no spaces between", True); add_line(w, "the commas in the 'Newsgroups:' header", True); return False; } } break; case 's': if (IS_HEADER("sender")) RETURN_ERROR("Error! You cannot specify a 'Sender:' header."); else if (IS_HEADER("supersedes") && !an_admin() && !check_cancel(w, field, end)) return False; else if (IS_HEADER("subject")) { if (*subject) RETURN_ERROR("Error! Duplicate 'Subject:' header."); if (*field == '\n' || *field == '\0') RETURN_ERROR("Error! Empty 'Subject:' header."); *subject = copy_field(field, end); } break; case 't': if (IS_HEADER("to")) *to = copy_field(field, end); break; } #undef IS_HEADER context->line += lines; art = end + 1; } art++; if (!*subject) RETURN_ERROR("Error! Article/mail has no 'Subject:' header."); if (!has_from) RETURN_ERROR("Error! Article/mail has no 'From:' header."); if (!*newsgroups && !*to && !*cc) RETURN_ERROR("Error! Article/mail has no " "'Newsgroups:', 'To:' or 'Cc:' header."); context->line += 2; *line_len = max_len(art, '\n'); check_quoting(art, res_quote_string(), n_lines, n_quoted); if (*line_len > 1024) { add_line(w, "Error! The article/mail contains lines", True); add_line(w, "longer than 1024 characters.", True); add_line(w, "Posting will not be allowed.", True); return False; } return True; } static int n_commas(const char *c) { int n = 0; for (c = strchr(c, ',') ; c ; c = strchr(c + 1, ',')) n++; return n; } static void format_ok_message(Widget w, char *subject, char *newsgroups, char *followupto, char *to, char *cc, char *charset, int line_len, long n_lines, long n_quoted) { char buffer[1024]; char *art_or_mail = newsgroups ? "article" : "mail"; int consider_edit = False; int added_note = False; if (!to) { to = cc; cc = NULL; } sprintf(buffer, "Subject: %s", subject); add_line(w, buffer, False); if (charset) { sprintf(buffer, " (No Content-Type header, assuming " "Content-Type: text/plain; charset=%s)", charset); add_line(w, buffer, True); } add_line(w, "", False); if (line_len > 76) { sprintf(buffer, "Note: This %s contains %d character lines.", art_or_mail, line_len); add_line(w, buffer, True); consider_edit = True; added_note = True; } if (n_lines > 16 && 2 * n_quoted > n_lines) { char *tmp = res_quote_string(); sprintf(buffer, "Note: This %s has %ld%% quoted text.", art_or_mail, 100 * n_quoted / n_lines); add_line(w, buffer, True); if (tmp && *tmp != '>') add_line(w, " [You can fool the server, " "but you can't fool me.]", True); consider_edit = True; added_note = True; } if (consider_edit) add_line(w, " You should consider editing it.", True); if (added_note) add_line(w, "", False); if (to) { add_line(w, "Mailing to:", False); add_line(w, to, False); if (cc) add_line(w, cc, False); add_line(w, "", False); } if (newsgroups) { int n; if (global.mode == NewsModeDisconnected) add_line(w, "Posting to: " "[can't check them: not connected]", False); else add_line(w, "Posting to:", False); n = n_commas(newsgroups); if (n > 6 && (!followupto || n_commas(followupto) > 6)) { sprintf(buffer, "%s: Posting to %d groups.", n > 12 ? "WARNING" : "Warning", n); add_line(w, buffer, True); if (!followupto) add_line(w, " Consider adding a Followup-To header.", True); else if (n_commas(followupto) > 6) add_line(w, " Consider cutting down the Followup-To " "header.", True); } for (;;) { char *p; GROUP *group = NULL; char *status; int ok; p = strchr(newsgroups, ','); if (p) *p++ = '\0'; if (global.mode == NewsModeDisconnected) ok = True, status = "[...]"; else if (!(group = find_group(newsgroups))) ok = False, status = "[???]"; else if (group->moderated) ok = True, status = "[mod]"; else ok = True, status = "[ok ]"; if (strlen(newsgroups) > 512) newsgroups[512] = '\0'; sprintf(buffer, "%s %s", status, newsgroups); add_line(w, buffer, !ok); if (!p) break; newsgroups = p; } } } void check_article_to_post(PostContext *context, Widget w) { const char *art = context->art; char *newsgroups, *followupto, *subject, *to, *cc; int tmp, has_ct, line_len; long n_lines, n_quoted; context->flags &= ~OK_TO_POST; if (!art) return; tmp = check_article(context, art, w, &newsgroups, &followupto, &subject, &to, &cc, &has_ct, &line_len, &n_lines, &n_quoted); if (tmp) { char *charset = NULL; context->has_8bit = has_8bit(art); if (!has_ct && context->has_8bit) charset = context->charset; format_ok_message(w, subject, newsgroups, followupto, to, cc, charset, line_len, n_lines, n_quoted); if (to || cc) context->flags |= MAIL; else context->flags &= ~MAIL; if (newsgroups) context->flags |= POST; else context->flags &= ~POST; context->flags |= OK_TO_POST; } XtFree(newsgroups); XtFree(subject); XtFree(to); XtFree(cc); } ./knews-1.0b.1/src/font.h100644 1244 1244 720 6455455547 13526 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ typedef struct { XFontStruct *body_font; XFontStruct *quote_font; XFontStruct *header_font; XFontStruct *list_font; XFontStruct *tree_font; const struct DecodeFuncs *funcs; int head_enc_hack; } MimeFont; extern MimeFont *default_font; extern MimeFont *ascii_font; extern void init_fonts(Widget); extern MimeFont *get_font(char*); extern void font_enter_group(void); ./knews-1.0b.1/src/codes.h100644 1244 1244 4535 6455455547 13705 0ustar kallekalle#define STRINGIFY(a) #a #define CODE_TO_STR(a) (a - a) + STRINGIFY(a) #define NNTP_INF_HELP 100 /* Help text on way */ #define NNTP_INF_DEBUG 199 /* Debug output */ #define NNTP_OK_CANPOST 200 /* Hello; you can post */ #define NNTP_OK_NOPOST 201 /* Hello; you can't post */ #define NNTP_OK_SLAVE 202 /* Slave status noted */ #define NNTP_OK_GOODBYE 205 /* Closing connection */ #define NNTP_OK_GROUP 211 /* Group selected */ #define NNTP_OK_GROUPS 215 /* Newsgroups follow */ #define NNTP_OK_ARTICLE 220 /* Article (head & body) follows */ #define NNTP_OK_HEAD 221 /* Head follows */ #define NNTP_OK_BODY 222 /* Body follows */ #define NNTP_OK_NOTEXT 223 /* No text sent -- stat, next, last */ #define NNTP_OK_XOVER 224 /* XOVER OK */ #define NNTP_OK_NEWNEWS 230 /* New articles by message-id follow */ #define NNTP_OK_NEWGROUPS 231 /* New newsgroups follow */ #define NNTP_OK_XFERED 235 /* Article transferred successfully */ #define NNTP_OK_POSTED 240 /* Article posted successfully */ #define NNTP_OK_AUTH 281 /* Authentication accepted */ #define NNTP_CONT_XFER 335 /* Continue to send article */ #define NNTP_CONT_POST 340 /* Continue to post article */ #define NNTP_CONT_AUTH 381 /* More authentication needed */ #define NNTP_ERR_GOODBYE 400 /* Have to hang up for some reason */ #define NNTP_ERR_NOGROUP 411 /* No such newsgroup */ #define NNTP_ERR_NCING 412 /* Not currently in newsgroup */ #define NNTP_ERR_NOCRNT 420 /* No current article selected */ #define NNTP_ERR_NONEXT 421 /* No next article in this group */ #define NNTP_ERR_NOPREV 422 /* No previous article in this group */ #define NNTP_ERR_NOARTIG 423 /* No such article in this group */ #define NNTP_ERR_NOART 430 /* No such article at all */ #define NNTP_ERR_GOTIT 435 /* Already got that article */ #define NNTP_ERR_XFERFAIL 436 /* Transfer failed */ #define NNTP_ERR_XFERRJCT 437 /* Article rejected, don't resend */ #define NNTP_ERR_NOPOST 440 /* Posting not allowed */ #define NNTP_ERR_POSTFAIL 441 /* Posting failed */ #define NNTP_ERR_NEED_AUTH 480 /* Authentication required */ #define NNTP_ERR_FAIL_AUTH 481 /* Authentication rejected */ #define NNTP_ERR_COMMAND 500 /* Command not recognized */ #define NNTP_ERR_CMDSYN 501 /* Command syntax error */ #define NNTP_ERR_ACCESS 502 /* Access to server denied */ #define NNTP_ERR_FAULT 503 /* Program fault, cpmd not performed */ ./knews-1.0b.1/src/knews_mask.xbm100644 1244 1244 6300 6455455547 15301 0ustar kallekalle#define knews_mask_width 64 #define knews_mask_height 64 static unsigned char knews_mask_bits[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xff, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xff, 0xff, 0x07, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0xc0, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0xf0, 0xff, 0xff, 0xff, 0x1f, 0x00, 0x00, 0x00, 0xfc, 0xff, 0xff, 0xff, 0x3f, 0x00, 0x00, 0x00, 0xfe, 0xff, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff, 0x7f, 0x00, 0x00, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, 0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x07, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xc0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xc0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x00, 0x00, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0xfc, 0xff, 0xff, 0xff, 0xff, 0x03, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, 0x3f, 0x00, 0x00, 0x00, 0xf0, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0xf0, 0xff, 0xff, 0xff, 0x07, 0x00, 0x00, 0x00, 0xe0, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0xc0, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xff, 0xff, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0xff, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; ./knews-1.0b.1/src/layouts/ 40755 1244 1244 0 6455455547 14033 5ustar kallekalle./knews-1.0b.1/src/layouts/save.h100644 1244 1244 2474 6455455547 15246 0ustar kallekalle"vertical { " " height close <+inf-inf> " " horizontal { " " height close <+inf-inf> " " message " " height close <+inf-inf> " " } " " height close <+inf-inf> " " horizontal { " " height close <-inf> " " horizontal { " " vertical { " " 0 <+inf> " " shellmessage " " 0 <+inf> " " } " " } " " shellfield <+inf-inf*> " " height close <-inf> " " } " " (height close / 2) <+inf-inf> " " horizontal { " " height close <-inf> " " horizontal { " " vertical { " " 0 <+inf> " " filemessage " " 0 <+inf> " " } " " width shellmessage - width filemessage " " } " " filefield <+inf-inf*> " " height close <-inf> " " } " " (height close / 2) <+inf-inf> " " horizontal { " " height close <+inff-inff> " " vertical { " " artwin <+inf*> " " article <+inf*> " " subject <+inf*> " " thread <+inf*> " " subthread <+inf*> " " tagged <+inf*> " " 0 <+inf> " " } " " height close <+inff-inff> " " vertical { " " bogusfrom <+inf*> " " bogussubj <+inf*> " " header <+inf*> " " body <+inf*> " " empty <+inf*> " " 0 <+inf> " " } " " height close <+inff-inff> " " } " " height close <+inf> " " horizontal { " " height close <+inf-inf> " " ok " " height close <+inf-inf> " " choose " " height close <+inf-inf> " " close " " height close <+inf-inf> " " } " " height close <+inf-inf> " "} ", ./knews-1.0b.1/src/layouts/text.h100644 1244 1244 317 6455455550 15240 0ustar kallekalle"vertical { " " 2 " " horizontal { " " textmanager <+inf-inf*+inf-inf> " " 2 " " textvbar <*+inf-inf> " " } " " 2 " " horizontal { " " texthbar <+inf-inf*> " " 2 " " width textvbar " " } " " 2 " "} ", ./knews-1.0b.1/src/layouts/single.h100644 1244 1244 444 6455455550 15536 0ustar kallekalle"horizontal { " " 2 " " vertical { " " 2 " " toplayout <+inf-inf*+inf-inf>" " 2 " " sash1 <+inf-inf*> " " 2 " " message <+inf-inf*> " " 2 " " sash2 <+inf-inf*> " " 2 " " knapplayout <+inf-inf*> " " 2 " " sash3 <+inf-inf*> " " textlayout <+inf-inf*+inff-inff> " " } " " 2 " "} ", ./knews-1.0b.1/src/layouts/kill.h100644 1244 1244 2562 6455455550 15233 0ustar kallekalle"vertical { " " 4 " " horizontal { " " 0 <+inf> " " killtitle <+inf-inf*> " " stayup " " 0 <+inf> " " } " " 2 " " horizontal { " " 4 " " killistmgr <+inf-inf*+inf-inf> " " 2 " " vbar <*+inf-inf> " " 4 " " } " " 2 " " horizontal { " " 4 " " hbar <+inf-inf*> " " 6 + width vbar " " } " " (height close / 2) " " horizontal { " " height close " " vertical { " " 0 <+inf> " " fieldmessage " " 0 <+inf> " " } " " vertical { " " 0 <+inf> " " fieldknapp " " 0 <+inf> " " } " " height close <+inf> " " vertical { " " 0 <+inf> " " scopemessage " " 0 <+inf> " " } " " vertical { " " 0 <+inf> " " scopeknapp " " 0 <+inf> " " } " " height close <+inf> " " (4 + width vbar) " " } " " (height close / 2) " " horizontal { " " (4 + 2 + height close + width exprknapp) " " vertical { " " 0 <+inf> " " actionknapp " " 0 <+inf> " " } " " vertical { " " 0 <+inf> " " colormessage " " 0 <+inf> " " } " " colorfield <+inf-inf*> " " 4 + width vbar " " } " " (height close / 2) " " horizontal { " " height close " " vertical { " " 0 <+inf> " " exprknapp " " 0 <+inf> " " } " " 4 " " exprfield <+inf-inf*> " " 4 + width vbar " " } " " (height close / 2) " " horizontal { " " height close " " add " " 4 <+inf-inf> " " delete " " 4 <+inf-inf> " " clear " " 4 <+inf-inf> " " close " " height close " " } " " height close <-inf> " "} " ./knews-1.0b.1/src/layouts/top.h100644 1244 1244 271 6455455550 15055 0ustar kallekalle"vertical { " " horizontal { " " topmanager <+inf-inf*+inf-inf> " " 2 " " topvbar <*+inf-inf> " " } " " horizontal { " " tophbar <+inf-inf*> " " 2 " " width topvbar " " } " "} ", ./knews-1.0b.1/src/layouts/knapp.h100644 1244 1244 1162 6455455550 15404 0ustar kallekalle"horizontal { " " 0 <+inf> " " vertical { " " 2 <+inf> " " knapp0 " " 2 <+inf> " " knapp6 " " 2 <+inf> " " } " " 0 <+inf> " " vertical { " " 2 <+inf> " " knapp1 " " 2 <+inf> " " knapp7 " " 2 <+inf> " " } " " 0 <+inf> " " vertical { " " 2 <+inf> " " knapp2 " " 2 <+inf> " " knapp8 " " 2 <+inf> " " } " " 0 <+inf> " " vertical { " " 2 <+inf> " " misc " " 2 <+inf> " " abort " " 2 <+inf> " " } " " 0 <+inf> " " vertical { " " 2 <+inf> " " post " " 2 <+inf> " " save " " 2 <+inf> " " } " " 0 <+inf> " " vertical { " " 2 <+inf> " " kill " " 2 <+inf> " " search " " 2 <+inf> " " } " " 0 <+inf> " "} ", ./knews-1.0b.1/src/layouts/search.h100644 1244 1244 4036 6455455550 15543 0ustar kallekalle"vertical { " " (height close / 2) <+inf-inf> " " horizontal { " " 0 <+inf> " " regexptitle " " 0 <+inf> " " } " " (height close / 2) <+inf-inf> " " horizontal { " " height close <-inf> " " vertical { " " 0 <+inf> " " regexpmessage " " 0 <+inf> " " } " " regexpfield <+inf-inf*> " " height close <-inf> " " } " " (height close / 2) <+inf-inf> " " horizontal { " " height close <+inff-inf> " " vertical { " " fromtoggle <+inf*> " " subjecttoggle <+inf*> " " headtoggle <+inf*> " " bodytoggle <+inf*> " " 0 <+inf> " " } " " (2 * height close) <+inf-inf> " " vertical { " " allscope <+inf*> " " threadscope <+inf*> " " subthreadscope <+inf*> " " taggedscope <+inf*> " " 0 <+inf> " " } " " height close <+inff-inf> " " } " " (height close / 2) <+inf-inf> " " horizontal { " " height close <+inf-inf> " " unreadtoggle " " height close <+inf-inf> " " } " " (height close / 2) <+inf-inf> " " horizontal { " " height close <+inf-inf>" " search " " height close <+4inf-inf> " " stop " " height close <+4inf-inf> " " close " " height close <+inf-inf> " " } " " (height close / 2) <+inf-inf> " " horizontal { " " 4 " " sash <+inf-inf*> " " 4 " " } " " (height close / 2) <+inf-inf> " " horizontal { " " 0 <+inf> " " xpattitle " " 0 <+inf> " " } " " (height close / 2) <+inf-inf> " " horizontal { " " height close <-inf> " " vertical { " " 0 <+inf> " " headermessage " " 0 <+inf> " " } " " horizontal { " " width wildcardmessage - width headermessage " " headerfield <+inf-inf*> " " } " " height close <-inf> " " } " " (height close / 2) <+inf-inf> " " horizontal { " " height close <-inf> " " vertical { " " 0 <+inf> " " wildcardmessage " " 0 <+inf> " " } " " wildcardfield <+inf-inf*>" " height close <-inf> " " } " " (height close / 2) <+inf-inf> " " horizontal { " " height close <+inf-inf> " " submit " " height close <+inf-inf> " " next " " height close <+inf-inf> " " first " " height close <+inf-inf> " " clear " " height close <+inf-inf> " " } " " (height close / 2) <+inf-inf> " "} ", ./knews-1.0b.1/src/layouts/post.h100644 1244 1244 4326 6455455550 15265 0ustar kallekalle"vertical { " " 8 " " posttitle <+inf-inf*> " " 4 " " horizontal { " " 4 " " sash1 <+inf-inf*> " " 4 " " } " " 4 " " horizontal { " " 8 " " posttextmgr <+inf-inf*+inff-inf> " " 2 " " textvbar <*+inf-inf> " " 4 " " } " " horizontal { " " 8 " " texthbar <+inf-inf*> " " 6 + width textvbar " " } " " 4 " " horizontal { " " 4 " " sash2 <+inf-inf*> " " 4 " " } " " 4 " " postmessage <+inf-inf*> " " 4 " " horizontal { " " 4 " " sash3 <+inf-inf*> " " 4 " " } " " 4 " " horizontal { " " 8 <+inf-inf> " " post " " 4 <+inf-inf> " " edit " " 4 <+inf-inf> " " misc " " 4 <+inf-inf> " " attach " " 4 <+inf-inf> " " detach " " 4 <+inf-inf> " " cancel " " 8 <+inf-inf> " " } " " 4 " " horizontal { " " 4 " " sash4 <+inf-inf*> " " 4 " " } " " 4 " " attachtitle <+inf-inf*> " " 4 " " horizontal { " " 8 " " vertical { " " manager <+inf-inf*+inff-inf> " " 2 " " listhbar <+inf-inf*> " " } " " 2 " " vertical { " " listvbar <*+inff-inf> " " 2 " " height listhbar " " } " " 4 " " } " " 4 " " horizontal { " " 8 " " vertical { " " 0 <+inf> " " typetitle " " 0 <+inf> " " } " " ((width enctitle - width typetitle - width type)/2) " " vertical { " " 0 <+inf-inf> " " type <*-inf> " " 0 <+inf-inf> " " } " " ((width enctitle - width typetitle - width type)/2) " " typefield <+inf-inf*> " " 4 + width listvbar " " } " " 4 " " horizontal { " " 8 " " vertical { " " 0 <+inf> " " enctitle " " 0 <+inf> " " } " " 2 " " nonetoggle " " base64toggle " " uuetoggle " " qptoggle " " 2 + 4 + width listvbar " " } " " 4 " " horizontal { " " 8 " " vertical { " " 0 <+inf> " " descrtitle " " 0 <+inf> " " } " " (width enctitle - width descrtitle) " " descrfield <+inf-inf*+0-0> " " 4 + width listvbar " " } " " 4 " " horizontal { " " 8 " " vertical { " " 0 <+inf> " " disptitle " " 0 <+inf> " " } " " (width enctitle - width disptitle) " " 2 " " vertical { " " 0 <+inf> " " inlinetoggle " " 0 <+inf> " " } " " vertical { " " 0 <+inf> " " attachtoggle " " 0 <+inf> " " } " " 4 " " vertical { " " 0 <+inf> " " nametitle " " 0 <+inf> " " } " " vertical { " " 0 <+inf> " " namefield <+inf-inff*> " " 0 <+inf> " " } " " 4 + width listvbar " " } " " 8 " "} ", ./knews-1.0b.1/src/layouts/double.h100644 1244 1244 347 6455455550 15531 0ustar kallekalle"horizontal { " " 2 " " vertical { " " 2 " " toplayout <+inf-inf*+inf-inf>" " 2 " " sash1 <+inf-inf*> " " 2 " " message <+inf-inf*> " " 2 " " sash2 <+inf-inf*> " " 2 " " knapplayout <+inf-inf*> " " 2 " " } " " 2 " "} ", ./knews-1.0b.1/src/layouts/killg.h100644 1244 1244 3130 6455455550 15372 0ustar kallekalle"vertical { " " 4 " " horizontal { " " 0 <+inf> " " killtitle <+inf-inf*> " " stayup " " 0 <+inf> " " } " " 2 " " horizontal { " " 4 " " killistmgr <+inf-inf*+inf-inf> " " 2 " " vbar <*+inf-inf> " " 4 " " } " " 2 " " horizontal { " " 4 " " hbar <+inf-inf*> " " 6 + width vbar " " } " " (height close / 2) " " horizontal { " " height close " " vertical { " " 0 <+inf> " " fieldmessage " " 0 <+inf> " " } " " vertical { " " 0 <+inf> " " fieldknapp " " 0 <+inf> " " } " " height close <+inf> " " vertical { " " 0 <+inf> " " scopemessage " " 0 <+inf> " " } " " vertical { " " 0 <+inf> " " scopeknapp " " 0 <+inf> " " } " " height close <+inf> " " (4 + width vbar) " " } " " (height close / 2) " " horizontal { " " (height close + width groupknapp + 4 + 2) " " vertical { " " 0 <+inf> " " actionknapp " " 0 <+inf> " " } " " vertical { " " 0 <+inf> " " colormessage " " 0 <+inf> " " } " " colorfield <+inf-inf*> " " 4 + width vbar " " } " " (height close / 2) " " horizontal { " " height close " " vertical { " " 0 <+inf> " " groupknapp " " 0 <+inf> " " } " " 4 " " groupfield <+inf-inf*> " " 4 + width vbar " " } " " (height close / 2) " " horizontal { " " height close " " vertical { " " 0 <+inf> " " exprknapp " " 0 <+inf> " " } " " (width groupknapp - width exprknapp + 4) " " exprfield <+inf-inf*> " " 4 + width vbar " " } " " (height close / 2) " " horizontal { " " height close " " add " " 4 <+inf-inf> " " delete " " 4 <+inf-inf> " " clear " " 4 <+inf-inf> " " close " " height close " " } " " height close <-inf> " "} ", ./knews-1.0b.1/src/k_action.h100644 1244 1244 412 6455455550 14337 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ extern void action_kill_append(Widget, XEvent*, String*, Cardinal*); extern void action_kill_prepend(Widget, XEvent*, String*, Cardinal*); extern void action_popup_kill(Widget, XEvent*, String*, Cardinal*); ./knews-1.0b.1/src/sysdeps.h100644 1244 1244 1356 6455455550 14272 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ typedef struct SERV_ADDR SERV_ADDR; #define NNTP_PORT 119 #define FTP_PORT 21 extern int do_wait(int*, int, void (*)(void*), void*); extern int would_block(int, int); extern int timed_out(int); extern char *error_string(int); extern void abort_callback(Widget, XtPointer, XtPointer); extern SERV_ADDR *get_host(char*, unsigned short, int); extern int open_socket(void); extern int connect_socket(int, SERV_ADDR*); extern int open_duplex(int*); extern int bind_and_listen(int); extern SERV_ADDR *get_sock_name(int); extern int do_accept(int*); extern void print_addr_ftp(SERV_ADDR*, char*); extern char *get_mailhostname(void); extern void sigusr1_handler(int); ./knews-1.0b.1/src/charset.h100644 1244 1244 546 6455455550 14211 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ typedef void* (*DecodeInit)(void); typedef long (*DecodeDecode)(void*, char*, long, int, XChar2b**); typedef void (*DecodeEnd)(void*); typedef struct DecodeFuncs { DecodeInit init; DecodeDecode decode; DecodeEnd end; } DecodeFuncs; extern const DecodeFuncs *get_decode_funcs(char*); ./knews-1.0b.1/src/knews_icon.xbm100644 1244 1244 6351 6455455550 15276 0ustar kallekalle#define knews_icon_width 64 #define knews_icon_height 64 static unsigned char knews_icon_bits[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x30, 0x80, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x40, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x03, 0xd0, 0x0e, 0x08, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x10, 0x09, 0x08, 0x00, 0x00, 0x00, 0x30, 0x00, 0x24, 0x08, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x2c, 0x0c, 0x21, 0x00, 0x00, 0x00, 0x02, 0xc0, 0x3c, 0xc3, 0x42, 0x00, 0x00, 0x80, 0x01, 0x98, 0x65, 0xb0, 0x46, 0x00, 0x00, 0x60, 0x00, 0x2c, 0x47, 0xe8, 0x85, 0x00, 0x00, 0x18, 0x20, 0x44, 0x04, 0xbe, 0x0a, 0x01, 0x00, 0x06, 0x60, 0x64, 0x84, 0x11, 0x12, 0x02, 0x80, 0x01, 0x4e, 0x1c, 0xc0, 0xea, 0x2b, 0x02, 0x60, 0x80, 0x9c, 0x88, 0x30, 0x00, 0x20, 0x04, 0x10, 0x80, 0xa8, 0xd0, 0xac, 0xaa, 0x6a, 0x08, 0x20, 0x88, 0xc8, 0x61, 0x02, 0x1f, 0x80, 0x10, 0x24, 0x50, 0x10, 0x01, 0xea, 0xea, 0xaa, 0x11, 0x48, 0x50, 0x10, 0x02, 0x24, 0x9f, 0x00, 0x21, 0x48, 0xe0, 0x20, 0x00, 0xa8, 0xbf, 0xba, 0x42, 0x90, 0x20, 0x23, 0x10, 0x28, 0x80, 0x48, 0x44, 0x10, 0x41, 0x0c, 0x0c, 0xf0, 0xea, 0xba, 0x43, 0x20, 0x41, 0x00, 0x23, 0x20, 0x1f, 0xc0, 0x40, 0x40, 0x82, 0xc0, 0x18, 0xc0, 0xaa, 0x3a, 0x60, 0x40, 0x04, 0x30, 0x46, 0x40, 0x38, 0x0c, 0x10, 0x80, 0x04, 0x8c, 0x31, 0x80, 0xaa, 0x02, 0x0c, 0x00, 0x09, 0x63, 0x0c, 0x00, 0xb9, 0x01, 0x03, 0x00, 0x89, 0x18, 0x03, 0x00, 0x6a, 0xc0, 0x00, 0x00, 0x12, 0xc6, 0x00, 0x01, 0x1a, 0x30, 0x00, 0x00, 0x22, 0x31, 0xc6, 0x00, 0x04, 0x0c, 0x00, 0x00, 0x24, 0x8c, 0x31, 0x00, 0x00, 0x03, 0x00, 0x00, 0x48, 0x62, 0x0c, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x88, 0x18, 0x43, 0x00, 0x30, 0x00, 0x00, 0x00, 0x90, 0xc4, 0x30, 0x08, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x31, 0x0c, 0x06, 0x02, 0x00, 0x00, 0x00, 0x20, 0x09, 0x83, 0x81, 0x01, 0x00, 0x00, 0x00, 0x40, 0xc2, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00, 0x40, 0x24, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x80, 0x04, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x89, 0x81, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; ./knews-1.0b.1/src/domain.c100644 1244 1244 2765 6570022727 14041 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include "global.h" #include "domain.h" #include "resource.h" #include "widgets.h" #include #include "sysdeps.h" static void fix_domain_name(void) { #ifdef DOMAIN_FILE char *fn = DOMAIN_FILE; FILE *fp; fp = fopen(fn, "r"); if (fp) { char buffer[256]; if (fgets(buffer, sizeof buffer - 1, fp)) { char *c = strchr(buffer, '\n'); if (c) *c = '\0'; if (strchr(buffer, '.')) global.domain_name = XtNewString(buffer); } fclose(fp); } #endif #ifdef DOMAIN_NAME if (!global.domain_name) { char *dn = DOMAIN_NAME; if (strchr(dn, '.')) global.domain_name = XtNewString(dn); } #endif if (!global.domain_name) global.domain_name = get_mailhostname(); } static void fix_user_id(void) { struct passwd *pw; pw = getpwuid(getuid()); if (pw) { if (pw->pw_name) global.user_id = XtNewString(pw->pw_name); if (!getenv("NAME") && pw->pw_gecos) { char *c = strchr(pw->pw_gecos, ','); if (c) *c = '\0'; res_set_pw_name(pw->pw_gecos); } } } void fix_domain_stuff() { fix_domain_name(); fix_user_id(); if (!global.domain_name) fputs("knews: Couldn't determine domain name. " "Posting will not be possible.\n", stderr); else if (!global.user_id) fputs("knews: Couldn't determain user id. " "Posting will not be possible.\n", stderr); if (!global.mail_name || global.mail_name[0] == ' ') global.mail_name = global.user_id; } ./knews-1.0b.1/src/k_file.c100644 1244 1244 17173 6455455550 14050 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include "global.h" #include "child.h" #include "expand.h" #include "file.h" #include "k_I.h" #include "k_edit.h" #include "k_file.h" #include "k_kill.h" #include "k_node.h" #include "resource.h" #include "server.h" #include "util.h" #include "thread.h" #include "widgets.h" #include "xutil.h" #include "../Widgets/Compat.h" #include "../Widgets/Util.h" static long n_files = 0; static long n_alloc = 0; static KILL_FILE **kill_files = NULL; static KILL_FILE *global_kill_file = NULL; static KILL_FILE *read_kill_file(GROUP *group) { SERVER *s; KILL_FILE *file; char *line; char *file_name; long n = 0; int fd; if (group) file_name = global.group_kill_file_templ; else { file_name = res_kill_file(); if (!file_name) file_name = global.kill_file_templ; } file = (KILL_FILE *)XtMalloc(sizeof *file); file->n = 0; file->nodes = NULL; file->group = group; file->file_name = expand_path(file_name); file->w = NULL; file->expire = res_expire_kills() && !res_ask_how_many(); file->stay_up = False; file->dirty = False; if (!file->file_name) return file; fd = open(file->file_name, O_RDONLY); if (fd < 0) { if (errno != ENOENT) perror(file->file_name); return file; } s = server_create(fd); while ((line = server_read(s))) { KILL_NODE *node = parse_kill_line(line, group == NULL); if (!node) continue; if (n < file->n + 8) file->nodes = (KILL_NODE **)XtRealloc((char *)file->nodes, (n = file->n + 8) * sizeof file->nodes[0]); file->nodes[file->n++] = node; } server_free(s); return file; } KILL_FILE *get_kill_file(GROUP *group) { long n; if (!group) return global_kill_file; for (n = 0 ; n < n_files ; n++) if (kill_files[n]->group == group) return kill_files[n]; if (n_alloc < n_files + 8) kill_files = (KILL_FILE **)XtRealloc((char *)kill_files, (n_alloc = n_files + 8) * sizeof *kill_files); return kill_files[n_files++] = read_kill_file(group); } static void free_kill_file(KILL_FILE *file) { long n; if (file->w) { destroy_kill_widgets(file->w); file->w = NULL; } for (n = 0 ; n < file->n ; n++) free_kill_node(file->nodes[n]); XtFree((char *)file->nodes); XtFree(file->file_name); file->nodes = NULL; file->file_name = NULL; XtFree((char *)file); } static int update_kill_file(KILL_FILE *file) { FILE *fp; long n; int ok; if (!file->file_name) return False; unlink(file->file_name); if (file->n <= 0) return True; fp = fopen_mkdir(file->file_name, "w", True); if (!fp) { perror(file->file_name); return False; } for (n = 0 ; n < file->n ; n++) fprint_kill_node(fp, file->nodes[n], file->expire); ok = (fclose(fp) >= 0); if (ok) file->dirty = False; return ok; } void read_global_kill_file(void) { global_kill_file = read_kill_file(NULL); } int update_kill_files(void) { int ok = True; long n; if (global_kill_file && global_kill_file->dirty && !update_kill_file(global_kill_file)) ok = False; for (n = 0 ; n < n_files ; n++) if (kill_files[n]->dirty) if (!update_kill_file(kill_files[n])) ok = False; return ok; } void kill_exit_group(GROUP *group) { KILL_FILE *file; long n; for (n = 0 ; n < n_files ; n++) if (kill_files[n]->group == group) break; if (n >= n_files) return; file = kill_files[n]; if (file->stay_up) return; if (file->w) popdown_kill_editor(file->w); if (group->subscribed) return; if (file->dirty) if (!update_kill_file(file)) { popup_title_notice(NULL, "Failed to update kill file!", True); return; } free_kill_file(file); n_files--; if (n < n_files) memmove(kill_files + n, kill_files + n + 1, (n_files - n) * sizeof kill_files[0]); kill_files[n_files] = NULL; } void kill_cleanup(void) { long n; free_kill_file(global_kill_file); global_kill_file = NULL; for (n = 0 ; n < n_files ; n++) free_kill_file(kill_files[n]); XtFree((char *)kill_files); kill_files = NULL; n_files = 0; n_alloc = 0; } void kill_articles(GROUP *group) { long n_killed = 0; long n_hot = 0; ARTICLE *arts = get_articles(main_thr); SUBJECT *subjs = get_subjects(main_thr); SUBJECT *subj; long i, n; KILL_FILE *file = get_kill_file(group); KILL_NODE **nodes; n = global_kill_file->n; nodes = global_kill_file->nodes; for (i = 0 ; i < n ; i++) { KILL_NODE *node = nodes[i]; if (node->expired || (node->group_str && (!node->group_re || regexec(node->group_re, group->name, 0, NULL, 0) != 0))) continue; if (node->hot) n_hot += hot_funcs[node->field][node->scope](node, arts, subjs); else n_killed += kill_funcs[node->field][node->scope](node, arts, subjs); if (file->expire && node->expired) file->dirty = True; } n = file->n; nodes = file->nodes; for (i = 0 ; i < n ; i++) { KILL_NODE *node = nodes[i]; if (node->expired) continue; if (node->hot) n_hot += hot_funcs[node->field][node->scope](node, arts, subjs); else n_killed += kill_funcs[node->field][node->scope](node, arts, subjs); if (file->expire && node->expired) file->dirty = True; } for (subj = subjs ; subj ; subj = subj->next) { subj->pixmap = None; update_subj_hot_value(subj); } global.n_killed = n_killed; global.n_hot = n_hot; } void kill_edit_popup(GROUP *group) { KILL_FILE *file = get_kill_file(group); popup_kill_editor(file); } /*********************************************************************/ int add_kill_node(KILL_FILE *file, int append, int field, int scope, int hot, char *color, char *expr, char *group) { KILL_NODE *node; regex_t *expr_re, *group_re; int code; if (field == KillFieldMsgid) { expr_re = NULL; if (expr[0] != '<' || expr[strlen(expr) - 1] != '>') { set_message("Bad Message-Id!", True); return False; } } else { expr_re = (regex_t *)XtMalloc(sizeof *expr_re); code = regcomp(expr_re, expr, REGEXP_COMPILE_FLAGS); if (code != 0) { popup_regexpnotice(code, expr_re); XtFree((char *)expr_re); return False; } } if (!group) group_re = NULL; else { group_re = (regex_t *)XtMalloc(sizeof *group_re); code = regcomp(group_re, group, REGEXP_COMPILE_FLAGS); if (code != 0) { popup_regexpnotice(code, group_re); XtFree((char *)expr_re); XtFree((char *)group_re); return False; } } node = (KILL_NODE *)XtMalloc(sizeof *node); node->expr_str = XtNewString(expr); node->expr_re = expr_re; node->group_str = group ? XtNewString(group) : NULL; node->group_re = group_re; node->color = color ? XtNewString(color) : NULL; node->pixel = global.default_hot_pixel; node->pixmap = None; node->field = field; node->scope = scope; node->hot = hot; node->expired = False; node->alloced_pixel = False; if (node->color) alloc_hot_pixel(node, True); file->dirty = True; file->nodes = (KILL_NODE **)XtRealloc((char *)file->nodes, (file->n + 1) * sizeof file->nodes[0]); if (append) file->nodes[file->n++] = node; else { if (file->n > 0) memmove(file->nodes + 1, file->nodes, file->n * sizeof file->nodes[0]); file->n++; file->nodes[0] = node; } if (file->w) kill_editor_notify_add(file, append); return True; } ./knews-1.0b.1/src/cache.c100644 1244 1244 23105 6455455550 13652 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include "global.h" #include "bg.h" #include "cache.h" #include "codes.h" #include "file.h" #include "resource.h" #include "server.h" #include "thread.h" #include "util.h" #include "widgets.h" #include "xutil.h" #include "../Widgets/Message.h" #include "../Widgets/Notice.h" #include "../Widgets/Util.h" #define MAX_CACHE 32 typedef struct { long no; char *file_name; unsigned char status; } ART_CACHE_NODE; enum { StatusEmpty, StatusOk, StatusDoing }; static ART_CACHE_NODE ahead_cache[MAX_CACHE] = {{0, }, }; static ART_CACHE_NODE trail_cache[MAX_CACHE] = {{0, }, }; static int ahead_size; static int trail_size; static char *cache_dir; static int cache_dir_len; static int cache_proc(SERVER *server); static Widget cache_notice = NULL; static void show_cache_stats(void); static char *get_fn(void) { static unsigned long count = 0; char *fn; fn = XtMalloc(cache_dir_len + 8); sprintf(fn, "%s/%lu", cache_dir, count++); return fn; } static void clear_cache_node(ART_CACHE_NODE *node) { if (node->file_name) { unlink_expand(node->file_name); XtFree(node->file_name); } node->file_name = NULL; node->no = 0; node->status = StatusEmpty; } static int cache_find(ART_CACHE_NODE *cache, int size, long no) { int i; for (i = 0 ; i < size ; i++) if (cache[i].no == no) return i; return -1; } static int make_room(ART_CACHE_NODE *cache, int size) { if (size <= 0) return False; if (cache[0].file_name) { if (cache[size - 1].no > 0) clear_cache_node(cache + size - 1); if (size - 1 > 0) memmove(cache + 1, cache, (size - 1) * sizeof cache[0]); cache[0].file_name = NULL; } cache[0].no = 0; cache[0].status = StatusEmpty; return True; } static void cache_clear(void) { int i; for (i = 0 ; i < MAX_CACHE ; i++) { clear_cache_node(ahead_cache + i); clear_cache_node(trail_cache + i); } ahead_size = 0; trail_size = 0; cache_dir = NULL; } FILE *cache_get_file(long art_no) { FILE *fp; char *fn; if (cache_find(ahead_cache, ahead_size, art_no) >= 0 || cache_find(trail_cache, trail_size, art_no) >= 0) return NULL; if (!make_room(trail_cache, trail_size)) { if (cache_notice) show_cache_stats(); return NULL; } fn = get_fn(); fp = fopen_expand(fn, "w", True); if (!fp) { if (cache_notice) show_cache_stats(); XtFree(fn); return NULL; } trail_cache[0].file_name = fn; trail_cache[0].no = art_no; trail_cache[0].status = StatusDoing; if (cache_notice) show_cache_stats(); return fp; } SERVER *cache_get_server(long art_no, int is_read) { int i, fd = -1; if (trail_size > 0) { i = cache_find(trail_cache, trail_size, art_no); if (i >= 0) { fd = open_expand(trail_cache[i].file_name, O_RDONLY, True); return fd < 0 ? NULL : server_create(fd); } } if (ahead_size <= 0) return NULL; i = cache_find(ahead_cache, ahead_size, art_no); if (i < 0) { ART_CACHE_NODE *node = ahead_cache + ahead_size - 1; if (!is_read || (ahead_size == 1 && node->status == StatusDoing)) return NULL; clear_cache_node(node); bg_nudge(cache_proc); return NULL; } if (ahead_cache[i].status != StatusOk) return NULL; fd = open_expand(ahead_cache[i].file_name, O_RDONLY, True); if (trail_size <= 0 || !make_room(trail_cache, trail_size)) clear_cache_node(ahead_cache + i); else { trail_cache[0] = ahead_cache[i]; ahead_cache[i].file_name = NULL; ahead_cache[i].no = 0; ahead_cache[i].status = StatusEmpty; } if (cache_notice) show_cache_stats(); return fd < 0 ? NULL : server_create(fd); } void cache_fetch_done(long art_no) { int i; i = cache_find(trail_cache, trail_size, art_no); if (i >= 0) trail_cache[i].status = StatusOk; else fprintf(stderr, "done but no node %ld\n", art_no); if (cache_notice) show_cache_stats(); } void cache_fetch_failed(long art_no) { int i; i = cache_find(trail_cache, trail_size, art_no); if (i >= 0) clear_cache_node(trail_cache + i); if (cache_notice) show_cache_stats(); } /**************************************************************************/ typedef enum { CacheStateNone, CacheStateSentArticle, CacheStateDoingArticle } CacheState; static CacheState state = CacheStateNone; static int snarfing = False; static FILE *fp = NULL; static void current_done(int ok) { if (!fp) return; fclose(fp); fp = NULL; if (!snarfing) ahead_cache[0].status = ok ? StatusOk : StatusEmpty; if (cache_notice) show_cache_stats(); } static int cached(long no) { int i; for (i = 0 ; i < ahead_size ; i++) if (ahead_cache[i].no == no) return True; for (i = 0 ; i < trail_size ; i++) if (trail_cache[i].no == no) return True; return False; } static long find_art_to_read(void) { SUBJECT *subj; ARTICLE *art; if (global.mode != NewsModeGroup && global.mode != NewsModeThread) return 0; art = global.curr_art; if (!art) return 0; subj = art->subject; art = next_in_thread_preorder(art); do { while (art && (!art->from || art->read || cached(art->no))) art = next_in_thread_preorder(art); if (art) break; while (subj->next && subj->next->thread == subj->thread) subj = subj->next; subj = subj->next; if (subj) art = subj->thread; } while (subj); return art ? art->no : 0; } static int start_article(SERVER *server) { char command[32]; long no; int tmp, busy; state = CacheStateNone; if ((global.mode != NewsModeGroup && global.mode != NewsModeThread) || ahead_size <= 0 || (no = find_art_to_read()) <= 0 || ahead_cache[ahead_size - 1].status != StatusEmpty || !make_room(ahead_cache, ahead_size)) return False; if (bg_in_group(NULL, NULL, NULL) != global.curr_group) { bg_start_group(global.curr_group); return True; } ahead_cache[0].file_name = get_fn(); ahead_cache[0].no = no; ahead_cache[0].status = StatusEmpty; fp = fopen_expand(ahead_cache[0].file_name, "w", True); sprintf(command, "ARTICLE %ld\r\n", no); busy = global.busy; if (!busy) global.busy = True; tmp = server_write(server, command); if (!busy) global.busy = False; if (tmp < 0) { if (cache_notice) show_cache_stats(); return False; } ahead_cache[0].status = StatusDoing; state = CacheStateSentArticle; if (cache_notice) show_cache_stats(); return True; } static int cache_proc(SERVER *server) { char *buffer = NULL; if (!server) { current_done(False); snarfing = False; state = CacheStateNone; return False; } do { switch (state) { case CacheStateNone: return start_article(server); case CacheStateSentArticle: buffer = server_get_line(server); if (!buffer) break; if (atoi(buffer) != NNTP_OK_ARTICLE) { if (snarfing) snarfing = False; else current_done(False); return start_article(server); } state = CacheStateDoingArticle; break; case CacheStateDoingArticle: buffer = server_get_line(server); if (!buffer) break; if (fp) fprintf(fp, "%s\r\n", buffer); if (!IS_DOT(buffer)) break; if (snarfing) snarfing = False; else current_done(True); state = CacheStateNone; return False; } } while (buffer); return True; } void cache_leave_group(void) { if (state != CacheStateNone) { current_done(False); snarfing = True; } cache_clear(); if (cache_notice) show_cache_stats(); } void cache_enter_group(void) { int tmp; tmp = res_cache_ahead_size(); if (tmp > MAX_CACHE) tmp = MAX_CACHE; else if (tmp < 0) tmp = 0; ahead_size = tmp; tmp = res_cache_trail_size(); if (tmp > MAX_CACHE) tmp = MAX_CACHE; else if (tmp < 0) tmp = 0; trail_size = tmp; cache_dir = res_cache_dir(); if (!cache_dir) cache_dir = "/tmp"; cache_dir_len = strlen(cache_dir); if (global.show_cache) popup_cache_stats(); if (ahead_size > 0) bg_nudge(cache_proc); } int cache_todo(void) { if (global.mode != NewsModeGroup && global.mode != NewsModeThread) return False; return find_art_to_read() > 0; } /**************************************************************************/ static Widget message_widget = NULL; #define TRAIL_PREFIX "Trail cache: " #define AHEAD_PREFIX "Ahead cache: " static char cache_message[] = TRAIL_PREFIX " \n" AHEAD_PREFIX " "; #define TRAIL_OFFSET (sizeof TRAIL_PREFIX - 1) #define AHEAD_OFFSET (sizeof AHEAD_PREFIX - 1 + sizeof cache_message / 2) static void cache_notice_callback(Widget w, XtPointer client_data, XtPointer call_data) { XtPopdown(w); } void popup_cache_stats(void) { if (!cache_notice) cache_notice = popup_notice("cachestats", cache_message, NULL, NULL, NULL, 0, cache_notice_callback, NULL, XtGrabNone); else if (is_popped_up(cache_notice)) return; else XtPopup(cache_notice, XtGrabNone); message_widget = NoticeMessageWidget(cache_notice); show_cache_stats(); } static void show_cache_stats(void) { int i; if (!message_widget) return; for (i = 0 ; i < trail_size ; i++) cache_message[TRAIL_OFFSET + i] = "-+*"[trail_cache[i].status]; while (i < MAX_CACHE) cache_message[TRAIL_OFFSET + i++] = ' '; for (i = 0 ; i < ahead_size ; i++) cache_message[AHEAD_OFFSET + i] = "-+*"[ahead_cache[i].status]; while (i < MAX_CACHE) cache_message[AHEAD_OFFSET + i++] = ' '; MessageSetAndRedraw(message_widget, cache_message, False); } ./knews-1.0b.1/src/connect.h100644 1244 1244 251 6455455550 14202 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ extern void connect_server(void); extern int reconnect_server(int); extern void popup_connect_dialogue(void); ./knews-1.0b.1/src/domain.h100644 1244 1244 137 6455455551 14024 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ extern void fix_domain_stuff(void); ./knews-1.0b.1/src/k_kill.c100644 1244 1244 30674 6455455551 14066 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include "global.h" #include "child.h" #include "file.h" #include "k_I.h" /*#include "k_file.h"*/ #include "k_kill.h" #include "resource.h" #include "util.h" #include "thread.h" #define THREAD_HAS_UNREAD(subj) \ ((subj)->no_unread != 0 || thread_has_unread((subj))) static int thread_has_unread(SUBJECT *subj) { ARTICLE *thr = subj->thread; for (subj = subj->next ; subj && subj->thread == thr ; subj = subj->next) if (subj->no_unread != 0) return True; return False; } /*********************************************************************/ static ARTICLE *get_by_msgid(KILL_NODE *node) { ARTICLE *art; art = find_article(node->expr_str + 1, strlen(node->expr_str) - 2); node->expired = !art; return art; } static long m_a_kill(KILL_NODE *node, ARTICLE *articles, SUBJECT *subj) { ARTICLE *art = get_by_msgid(node); if (!art || !art->from || art->read || art->pixmap != None) return 0; art->read = True; art->killed = True; global.curr_group->no_unread--; art->subject->no_unread--; return 1; } static long m_a_hot(KILL_NODE *node, ARTICLE *articles, SUBJECT *subj) { ARTICLE *art = get_by_msgid(node); if (!art || !art->from || art->read || art->pixmap != None) return 0; art->pixmap = node->pixmap; return 1; } static long m_s_kill(KILL_NODE *node, ARTICLE *articles, SUBJECT *subj) { ARTICLE *art = get_by_msgid(node); if (!art || art->subject->no_unread == 0) return 0; return mark_subject_read(art->subject, False, True); } static long m_s_hot(KILL_NODE *node, ARTICLE *articles, SUBJECT *subj) { ARTICLE *art = get_by_msgid(node); if (!art) return 0; return mark_subject_hot(art->subject, node->pixmap); } static long m_T_kill(KILL_NODE *node, ARTICLE *articles, SUBJECT *subj) { ARTICLE *art = get_by_msgid(node); if (!art) return 0; return mark_thread_read(art->subject->thread, False, True); } static long m_T_hot(KILL_NODE *node, ARTICLE *articles, SUBJECT *subj) { ARTICLE *art = get_by_msgid(node); if (!art) return 0; return mark_thread_hot(art->subject->thread, node->pixmap); } static long m_t_kill(KILL_NODE *node, ARTICLE *articles, SUBJECT *subj) { ARTICLE *art = get_by_msgid(node); if (!art) return 0; return mark_subthread_read(art, False, True); } static long m_t_hot(KILL_NODE *node, ARTICLE *articles, SUBJECT *subj) { ARTICLE *art = get_by_msgid(node); if (!art) return 0; return mark_subthread_hot(art, node->pixmap); } /*********************************************************************/ static long s_s_kill(KILL_NODE *node, ARTICLE *articles, SUBJECT *subj) { long n = 0; while (subj) { if (subj->no_unread != 0 && regexec(node->expr_re, subj->subject, 0, NULL, 0) == 0) n += mark_subject_read(subj, False, True); subj = subj->next; } return n; } static long s_s_hot(KILL_NODE *node, ARTICLE *articles, SUBJECT *subj) { long n = 0; while (subj) { if (subj->no_unread != 0 && regexec(node->expr_re, subj->subject, 0, NULL, 0) == 0) n += mark_subject_hot(subj, node->pixmap); subj = subj->next; } return n; } static long s_T_kill(KILL_NODE *node, ARTICLE *articles, SUBJECT *subj) { long n = 0; while (subj) { ARTICLE *thr = subj->thread; int has_unread = THREAD_HAS_UNREAD(subj); do { if (has_unread && regexec(node->expr_re, subj->subject, 0, NULL, 0) == 0) { n += mark_thread_read(subj->thread, False, True); has_unread = False; } subj = subj->next; } while (subj && subj->thread == thr); } return n; } static long s_T_hot(KILL_NODE *node, ARTICLE *articles, SUBJECT *subj) { long n = 0; while (subj) { ARTICLE *thr = subj->thread; int has_unread = THREAD_HAS_UNREAD(subj); do { if (has_unread && regexec(node->expr_re, subj->subject, 0, NULL, 0) == 0) { n += mark_thread_hot(subj->thread, node->pixmap); has_unread = False; } subj = subj->next; } while (subj && subj->thread == thr); } return n; } static long s_t_kill(KILL_NODE *node, ARTICLE *articles, SUBJECT *subj) { long n = 0; while (subj) { if (regexec(node->expr_re, subj->subject, 0, NULL, 0) == 0) { ARTICLE *art = subj->thread; while (art) if (art->subject != subj) art = next_in_thread_preorder(art); else { n += mark_subthread_read(art, False, True); art = preorder_skip_subthread(art); } } subj = subj->next; } return n; } static long s_t_hot(KILL_NODE *node, ARTICLE *articles, SUBJECT *subj) { long n = 0; while (subj) { if (regexec(node->expr_re, subj->subject, 0, NULL, 0) == 0) { ARTICLE *art = subj->thread; while (art) if (art->subject != subj) art = next_in_thread_preorder(art); else { n += mark_subthread_hot(art, node->pixmap); art = preorder_skip_subthread(art); } } subj = subj->next; } return n; } /*********************************************************************/ static long f_a_kill(KILL_NODE *node, ARTICLE *articles, SUBJECT *subj) { long n = 0; ARTICLE *art; for (art = articles ; art ; art = art->next) { if (art->from && !art->read && art->pixmap == None && regexec(node->expr_re, art->from, 0, NULL, 0) == 0) { art->read = True; art->killed = True; n++; global.curr_group->no_unread--; art->subject->no_unread--; } } return n; } static long f_a_hot(KILL_NODE *node, ARTICLE *articles, SUBJECT *subj) { long n = 0; ARTICLE *art; for (art = articles ; art ; art = art->next) if (art->from && art->pixmap == None && !art->read && regexec(node->expr_re, art->from, 0, NULL, 0) == 0) { art->pixmap = node->pixmap; n++; } return n; } static long f_s_kill(KILL_NODE *node, ARTICLE *articles, SUBJECT *subj) { long n = 0; ARTICLE *art; for (art = articles ; art ; art = art->next) if (art->from && regexec(node->expr_re, art->from, 0, NULL, 0) == 0) n += mark_subject_read(art->subject, False, True); return n; } static long f_s_hot(KILL_NODE *node, ARTICLE *articles, SUBJECT *subj) { long n = 0; ARTICLE *art; for (art = articles ; art ; art = art->next) if (art->from && regexec(node->expr_re, art->from, 0, NULL, 0) == 0) n += mark_subject_hot(art->subject, node->pixmap); return n; } static long f_T_kill(KILL_NODE *node, ARTICLE *articles, SUBJECT *subj) { long n = 0; ARTICLE *art, *thr; while (subj) { thr = subj->thread; if (THREAD_HAS_UNREAD(subj)) for (art = thr ; art ; art = next_in_thread_preorder(art)) if (art->from && regexec(node->expr_re, art->from, 0, NULL, 0) == 0) { n += mark_thread_read(subj->thread, False, True); break; } do { subj = subj->next; } while (subj && subj->thread == thr); } return n; } static long f_T_hot(KILL_NODE *node, ARTICLE *articles, SUBJECT *subj) { long n = 0; ARTICLE *art, *thr; while (subj) { thr = subj->thread; if (THREAD_HAS_UNREAD(subj)) for (art = thr ; art ; art = next_in_thread_preorder(art)) if (art->from && regexec(node->expr_re, art->from, 0, NULL, 0) == 0) { n += mark_thread_hot(subj->thread, node->pixmap); break; } do { subj = subj->next; } while (subj && subj->thread == thr); } return n; } static long f_t_kill(KILL_NODE *node, ARTICLE *articles, SUBJECT *subj) { long n = 0; ARTICLE *art, *thr; while (subj) { thr = art = subj->thread; while (art) if (art->from && regexec(node->expr_re, art->from, 0, NULL, 0) == 0) { n += mark_subthread_read(art, False, True); art = preorder_skip_subthread(art); } else art = next_in_thread_preorder(art); do { subj = subj->next; } while (subj && subj->thread == thr); } return n; } static long f_t_hot(KILL_NODE *node, ARTICLE *articles, SUBJECT *subj) { long n = 0; ARTICLE *art, *thr; while (subj) { thr = art = subj->thread; while (art) if (art->from && regexec(node->expr_re, art->from, 0, NULL, 0) == 0) { n += mark_subthread_hot(art, node->pixmap); art = preorder_skip_subthread(art); } else art = next_in_thread_preorder(art); do { subj = subj->next; } while (subj && subj->thread == thr); } return n; } /*********************************************************************/ static long x_a_kill(KILL_NODE *node, ARTICLE *articles, SUBJECT *subj) { long n = 0; ARTICLE *art; for (art = articles ; art ; art = art->next) { if (art->xref && !art->read && art->pixmap == None && regexec(node->expr_re, art->xref, 0, NULL, 0) == 0) { art->read = True; art->killed = True; global.curr_group->no_unread--; art->subject->no_unread--; n++; } } return n; } static long x_a_hot(KILL_NODE *node, ARTICLE *articles, SUBJECT *subj) { long n = 0; ARTICLE *art; for (art = articles ; art ; art = art->next) if (art->xref && art->pixmap == None && !art->read && regexec(node->expr_re, art->xref, 0, NULL, 0) == 0) { art->pixmap = node->pixmap; n++; } return n; } static long x_s_kill(KILL_NODE *node, ARTICLE *articles, SUBJECT *subj) { long n = 0; ARTICLE *art; while (subj) { if (subj->no_unread != 0) for (art = subj->thread ; art ; art = next_in_thread_preorder(art)) { if (art->subject == subj && art->xref && regexec(node->expr_re, art->xref, 0, NULL, 0) == 0) { n += mark_subject_read(subj, False, True); break; } } subj = subj->next; } return n; } static long x_s_hot(KILL_NODE *node, ARTICLE *articles, SUBJECT *subj) { long n = 0; ARTICLE *art; while (subj) { if (subj->no_unread != 0) for (art = subj->thread ; art ; art = next_in_thread_preorder(art)) { if (art->subject == subj && art->xref && regexec(node->expr_re, art->xref, 0, NULL, 0) == 0) { n += mark_subject_hot(subj, node->pixmap); break; } } subj = subj->next; } return n; } static long x_T_kill(KILL_NODE *node, ARTICLE *articles, SUBJECT *subj) { long n = 0; ARTICLE *art, *thr; while (subj) { thr = subj->thread; if (THREAD_HAS_UNREAD(subj)) for (art = thr ; art ; art = next_in_thread_preorder(art)) if (art->xref && regexec(node->expr_re, art->xref, 0, NULL, 0) == 0) { n += mark_thread_read(subj->thread, False, True); break; } do { subj = subj->next; } while (subj && subj->thread == thr); } return n; } static long x_T_hot(KILL_NODE *node, ARTICLE *articles, SUBJECT *subj) { long n = 0; ARTICLE *art, *thr; while (subj) { thr = subj->thread; if (THREAD_HAS_UNREAD(subj)) for (art = thr ; art ; art = next_in_thread_preorder(art)) if (art->xref && regexec(node->expr_re, art->xref, 0, NULL, 0) == 0) { n += mark_thread_hot(subj->thread, node->pixmap); break; } do { subj = subj->next; } while (subj && subj->thread == thr); } return n; } static long x_t_kill(KILL_NODE *node, ARTICLE *articles, SUBJECT *subj) { long n = 0; ARTICLE *art, *thr; while (subj) { thr = art = subj->thread; if (THREAD_HAS_UNREAD(subj)) while (art) if (art->xref && regexec(node->expr_re, art->xref, 0, NULL, 0) == 0) { n += mark_subthread_read(art, False, True); art = preorder_skip_subthread(art); } else art = next_in_thread_preorder(art); do { subj = subj->next; } while (subj && subj->thread == thr); } return n; } static long x_t_hot(KILL_NODE *node, ARTICLE *articles, SUBJECT *subj) { long n = 0; ARTICLE *art, *thr; while (subj) { thr = art = subj->thread; if (THREAD_HAS_UNREAD(subj)) while (art) if (art->xref && regexec(node->expr_re, art->xref, 0, NULL, 0) == 0) { n += mark_subthread_hot(art, node->pixmap); art = preorder_skip_subthread(art); } else art = next_in_thread_preorder(art); do { subj = subj->next; } while (subj && subj->thread == thr); } return n; } /*********************************************************************/ const KillFunc kill_funcs[4][4] = { {m_a_kill, m_s_kill, m_T_kill, m_t_kill}, {s_s_kill, s_s_kill, s_T_kill, s_t_kill}, {f_a_kill, f_s_kill, f_T_kill, f_t_kill}, {x_a_kill, x_s_kill, x_T_kill, x_t_kill}, }; const KillFunc hot_funcs[4][4] = { {m_a_hot, m_s_hot, m_T_hot, m_t_hot}, {s_s_hot, s_s_hot, s_T_hot, s_t_hot}, {f_a_hot, f_s_hot, f_T_hot, f_t_hot}, {x_a_hot, x_s_hot, x_T_hot, x_t_hot}, }; ./knews-1.0b.1/src/cache.h100644 1244 1244 564 6455455551 13624 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ extern void cache_enter_group(void); extern void cache_leave_group(void); extern FILE *cache_get_file(long); extern struct SERVER *cache_get_server(long, int); extern void cache_fetch_done(long); extern void cache_fetch_failed(long); extern int cache_todo(void); extern void popup_cache_stats(void); ./knews-1.0b.1/src/tag.h100644 1244 1244 1640 6455455554 13353 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ extern void arttree_tag_callback(Widget, XtPointer, XtPointer); extern void thread_list_tag_callback(Widget, XtPointer, XtPointer); extern void clear_tagged_articles(void); extern ARTICLE **get_tagged_articles(void); extern long no_tagged_articles(void); extern void mark_tagged_articles(ARTICLE*); extern void tag_hot_articles(void); extern void untag_article(ARTICLE*); extern void history_push(ARTICLE*); extern ARTICLE *history_pop(void); extern ARTICLE *history_peek(void); extern void clear_history(void); extern void action_tag_thread(Widget, XEvent*, String*, Cardinal*); extern void action_tag_subject(Widget, XEvent*, String*, Cardinal*); extern void action_untag_thread(Widget, XEvent*, String*, Cardinal*); extern void action_untag_subject(Widget, XEvent*, String*, Cardinal*); ./knews-1.0b.1/src/child.h100644 1244 1244 724 6455455554 13645 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ typedef void (*ChildCallback)(void*, int, char*); extern void init_child_contexts(void); extern void suspend_child_contexts(void); extern void resume_child_contexts(void); extern pid_t fork_nicely(void*, ChildCallback, int); extern pid_t wait_for_pid(pid_t, int*, char*); extern char *signal_string(int); extern void block_sighup(void); extern void unblock_sighup(void); #define STDERR_BUFFLEN 1024 ./knews-1.0b.1/src/connect.c100644 1244 1244 22337 6455455554 14252 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include "global.h" #include "ahead.h" #include "child.h" #include "codes.h" #include "connect.h" #include "expand.h" #include "ftp.h" #include "k_file.h" #include "newsrc.h" #include "resource.h" #include "file.h" #include "server.h" #include "sysdeps.h" #include "util.h" #include "widgets.h" #include "xutil.h" #include "../Widgets/Compat.h" #include "../Widgets/Dialogue.h" #include "../Widgets/Notice.h" #include "../Widgets/Util.h" static int nntp_init(void) { char message[256]; char *buffer; int status; buffer = server_read(main_server); if (!buffer) { if (server_aborted(main_server)) set_message("Aborted.", True); else set_message("Error: Connection to server broken!", True); return False; } status = atoi(buffer); if (status != NNTP_OK_CANPOST && status != NNTP_OK_NOPOST) { if (strlen(buffer) > 100) buffer[100] = '\0'; sprintf(message, "Error: Message from server is: %s", buffer); set_message(message, True); return False; } global.posting_allowed = (status == NNTP_OK_CANPOST); buffer = server_comm(main_server, "MODE READER\r\n", False); if (!buffer) { if (server_aborted(main_server)) set_message("Aborted.", True); else set_message("Error: Connection to server broken!", True); return False; } status = atoi(buffer); if (status == NNTP_OK_CANPOST) global.posting_allowed = True; else if (status == NNTP_OK_NOPOST) global.posting_allowed = False; else if (status == NNTP_ERR_ACCESS) { if (strlen(buffer) > 100) buffer[100] = '\0'; sprintf(message, "Error: Message from server is: %s", buffer); set_message(message, True); return False; } buffer = server_comm(main_server, "XOVER\r\n", False); if (!buffer) { if (server_aborted(main_server)) set_message("Aborted.", True); else set_message("Error: Connection to server broken!", True); return False; } status = atoi(buffer); if (status == NNTP_ERR_COMMAND) global.xover_supported = False; else { global.xover_supported = True; if (status == NNTP_OK_XOVER) { while (buffer && !IS_DOT(buffer)) buffer = server_read(main_server); if (!buffer) { if (server_aborted(main_server)) set_message("Aborted.", True); else set_message("Error: Connection to server broken!", True); return False; } } } if (global.head_debug) global.xover_supported = False; return True; } static char *get_server_command(char *res_name) { XrmName name_list[3]; XrmClass class_list[3]; XrmQuark rep; XrmDatabase db; XrmValue val; while (*res_name == ' ' || *res_name == '\t') res_name++; if (*res_name == '\0' || strchr(res_name, '.') || strchr(res_name, '*') || strchr(res_name, ' ') || strchr(res_name, '\t') || strchr(res_name, '/')) return NULL; class_list[0] = XrmPermStringToQuark("Knews"); class_list[1] = XrmPermStringToQuark("Server"); class_list[2] = NULLQUARK; name_list[0] = XrmStringToQuark(XtName(main_widgets.shell)); name_list[1] = XrmStringToQuark(res_name); name_list[2] = NULLQUARK; db = XtScreenDatabase(XtScreen(main_widgets.shell)); if (!XrmQGetResource(db, name_list, class_list, &rep, &val)) return NULL; return (char *)val.addr; } void connect_server(void) { char *path; int status = -1; int read_active; set_busy(True); suspend_child_contexts(); server_close(main_server); if (global.nntp_server[0] != '#') { set_message("Looking up hostname...", False); global.serv_addr = get_host(global.nntp_server, NNTP_PORT, True); if (!global.serv_addr) set_message("Error! No such host!", True); else status = server_open(main_server, global.serv_addr, 2); } else { char *cmd = get_server_command(global.nntp_server); if (cmd) status = server_fork(main_server, cmd, 2); else set_message("Error! No server command specified!", True); } if (status < 0) { if (server_aborted(main_server)) set_message("Aborted!", True); unset_busy(); resume_child_contexts(); return; } path = expand_path(global.config_file); if (!path) res_load(NULL); else { if (res_load(path) < 0) perror(path); XtFree(path); } (void)ftp_get(); status = nntp_init(); if (!status) { global_cleanup(False, False); unset_busy(); resume_child_contexts(); return; } read_active = res_read_active_file(); if (!read_active) { if (global.posting_allowed) set_message("Connected to server; " "retrieving groups from newsrc...", False); else set_message("Connected to server, posting not allowed; " "retrieving groups from newsrc...", False); status = get_newsgroups_from_newsrc(); if (status != NNTP_OK_GROUPS && status != -1) read_active = True; } if (read_active) { if (global.posting_allowed) set_message("Connected to server; reading active file...", False); else set_message("Connected to server, posting not allowed; " "reading active file", False); status = get_newsgroups(); } if (status == NNTP_OK_GROUPS && get_descriptions() < 0) status = -1; if (read_active && status == NNTP_OK_GROUPS) { set_message("Reading newsrc file...", False); parse_newsrc(False); } if (status == NNTP_OK_GROUPS) { set_message("Reading global kill file...", False); read_global_kill_file(); if (res_check_for_new_groups()) { set_message("Checking for new groups...", False); status = check_for_new_groups(); if (status < 0) { if (server_aborted(main_server)) set_message("Aborted.", True); else set_message("Connection to server broken!", True); global_cleanup(False, False); unset_busy(); resume_child_contexts(); return; } } if (global.new_groups) setNewsModeNewgroups(); else setNewsModeConnected(); thread_ahead_init(); } else { if (status < 0) if (server_aborted(main_server)) set_message("Aborted.", True); else set_message("Connection to server broken!", True); else if (read_active) set_message("Failed to read active file!", True); else set_message("Failed to read newsrc file!", True); global_cleanup(False, False); } unset_busy(); resume_child_contexts(); add_rescan_timeout(); } int reconnect_server(int cleanup_on_error) { int status = -1; if (server_aborted(main_server)) set_message("Aborted, reconnecting to server...", True); else set_message("Reconnecting to server...", False); suspend_child_contexts(); /* may happen twice */ server_close(main_server); if (global.nntp_server[0] != '#') { int i = 4; status = server_open(main_server, global.serv_addr, 1); for (i = 0 ; status < 0 && i < 4 && timed_out(errno) ; i++) { set_message("Connection timed out, still trying...", True); status = server_open(main_server, global.serv_addr, 1); } } else { char *cmd = get_server_command(global.nntp_server); if (cmd) status = server_fork(main_server, cmd, 2); else set_message("Error! No server command specified!", True); } if (status < 0) { if (server_aborted(main_server)) set_message("Aborted!", True); /* else server_open will have done it */ resume_child_contexts(); if (cleanup_on_error) global_cleanup(True, False); return -1; } status = nntp_init(); if (!status) { resume_child_contexts(); if (cleanup_on_error) global_cleanup(True, False); /* nntp_init will have set_message */ return -1; } if (global.mode == NewsModeGroup || global.mode == NewsModeThread) { char message[512]; char *buffer; set_message("Reentering newsgroup...", False); sprintf(message, "GROUP %s\r\n", global.curr_group->name); buffer = server_comm(main_server, message, False); if (!buffer) { set_message("Failed to reenter to server!", True); resume_child_contexts(); if (cleanup_on_error) global_cleanup(True, False); return -1; } status = atoi(buffer); if (status != NNTP_OK_GROUP) setNewsModeConnected(); } if (cleanup_on_error) set_standard_message(); resume_child_contexts(); return 0; } /*************************************************************************/ static void connect_dialogue_callback(Widget w, XtPointer client_data, XtPointer call_data) { DialogueReport *report = (DialogueReport *)call_data; Arg arg; if (global.busy) return; switch (report->reply) { case DialogueReplyMiddle: XtSetArg(arg, XtNbuffer, ""); XtSetValues(w, &arg, 1); break; case DialogueReplyLeft: case DialogueReplyEnter: XtFree(global.nntp_server); global.nntp_server = NULL; XtPopdown(w); if (report->buffer && report->buffer[0] != '\0') { global.nntp_server = XtNewString(report->buffer); connect_server(); } break; case DialogueReplyRight: case DialogueReplyClose: XtPopdown(w); break; case DialogueReplyTab: break; } } static Widget connect_dialogue = NULL; void popup_connect_dialogue(void) { if (!connect_dialogue) connect_dialogue = popup_dialogue("connect", "Connect to nntpserver:", "Connect", "Clear", "Cancel", connect_dialogue_callback, NULL, XtGrabNone); else if (!is_popped_up(connect_dialogue)) popup_under_pointer(connect_dialogue, XtGrabNone); } ./knews-1.0b.1/src/p_menu.c100644 1244 1244 27317 6455455554 14107 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include "global.h" #include #include "cache.h" #include "codes.h" #include "connect.h" #include "file.h" #include "p_I.h" #include "p_menu.h" #include "p_popup.h" #include "p_post.h" #include "p_setup.h" #include "resource.h" #include "server.h" #include "util.h" #include "widgets.h" #include "xutil.h" #include "../Widgets/ArtText.h" #include "../Widgets/Dialogue.h" #include "../Widgets/Menu.h" #include "../Widgets/MenuG.h" #include "../Widgets/MenuShell.h" #include "../Widgets/Notice.h" #include "../Widgets/SeparatorG.h" #include "../Widgets/StringG.h" #include "../Widgets/ToggleG.h" #include "../Widgets/PullRight.h" static Widget include_toggle, quote_sig_toggle; static int generic_post_check(int needs_art, FILE **fp, char **file_name) { if (global.busy) XBell(display, 0); else if (!global.domain_name) set_message("Domain name not available, posting will not be allowed!", True); else if (!global.user_id) set_message("User id not available, posting will not be allowed!", True); else if (needs_art && global.mode != NewsModeGroup && global.mode != NewsModeThread) set_message("Not in a newsgroup!", True); else if (needs_art && !global.curr_art) set_message("No selected article!", True); else if (needs_art && !global.curr_art->from) set_message("That's a fake article!", True); else if (fp && !(*fp = create_temp_file(file_name))) set_message("Failed to create temporary file!", True); else return True; return False; } static void forward_dialogue_callback(Widget w, XtPointer client_data, XtPointer call_data) { DialogueReport *report = (DialogueReport *)call_data; String param_buf[2]; String *params = param_buf; Cardinal no_params; XtPopdown(w); XtDestroyWidget(w); if (!report || (report->reply == DialogueReplyClose || report->reply == DialogueReplyRight)) return; if (report->buffer) param_buf[0] = report->buffer; else param_buf[0] = ""; if (report->reply != DialogueReplyMiddle) no_params = 1; else { no_params = 2; param_buf[1] = "edit"; } action_forward_by_mail(NULL, NULL, params, &no_params); } static void post_menu_callback(Widget gw, XtPointer client_data, XtPointer call_data) { PostContext *context; int flags = (long)client_data; FILE *fp = NULL; char *file_name = NULL; int quote = ToggleGadgetGet(include_toggle); int quote_sig = ToggleGadgetGet(quote_sig_toggle); if (!generic_post_check(True, &fp, &file_name)) return; context = create_post_context(flags, file_name); post_setup(context, fp, global.curr_art, quote, quote_sig, False); fclose(fp); } static void post_new_callback(Widget w, XtPointer client_data, XtPointer call_data) { PostContext *context; char *file_name = NULL; FILE *fp = NULL; char *full_name = res_full_name(); if (!generic_post_check(False, &fp, &file_name)) return; context = create_post_context(POST, file_name); if (!full_name) full_name = ""; context->line = 1 + insert_extra_headers(fp, NULL); fprintf(fp, "From: %s@%s (%s)\n" "Subject: \n" "Newsgroups: %s\n" "\n", global.mail_name, global.domain_name, full_name, (global.mode == NewsModeGroup || global.mode == NewsModeThread) ? global.curr_group->name : ""); context->line++; append_signature(fp); fclose(fp); fork_editor(context); } static void fwd_default_callback(Widget w, XtPointer client_data, XtPointer call_data) { if (!generic_post_check(True, NULL, NULL)) return; popup_dialogue("forward", "Forward article to:", "Mail", "Edit", "Cancel", forward_dialogue_callback, NULL, XtGrabExclusive); } static void fwd_menu_callback(Widget w, XtPointer client_data, XtPointer call_data) { char *to = (char *)call_data; Cardinal no_params; String params[2]; if (!to) { XBell(display, 0); return; } params[0] = to; params[1] = StringGadgetCommand(w); no_params = params[1] ? 2 : 1; action_forward_by_mail(w, NULL, params, &no_params); } static void cancel_post_callback(Widget w, XtPointer client_data, XtPointer call_data) { PostContext *context; FILE *fp = NULL; char *file_name = NULL; char *full_name = res_full_name(); if (!generic_post_check(True, &fp, &file_name)) return; context = create_post_context(POST, file_name); context->line = insert_extra_headers(fp, global.curr_art); fprintf(fp, "From: %s@%s (%s)\n" "Subject: cmsg cancel <%s>\n" "Control: cancel <%s>\n" "Newsgroups: %s\n" "\n" "Article canceled from within knews. [put reason here]\n", global.mail_name, global.domain_name, full_name ? full_name : "", global.curr_art->msgid, global.curr_art->msgid, global.curr_group->name); context->line += 6; fclose(fp); fork_editor(context); } static void supersede_callback(Widget w, XtPointer client_data, XtPointer call_data) { PostContext *context; FILE *fp = NULL; char *file_name = NULL; int quote = ToggleGadgetGet(include_toggle); int quote_sig = ToggleGadgetGet(quote_sig_toggle); if (!generic_post_check(True, &fp, &file_name)) return; context = create_post_context(POST, file_name); post_setup(context, fp, global.curr_art, quote, quote_sig, True); fclose(fp); } /*************************************************************************/ void create_post_menu(Widget main_shell) { Widget post_menu, menu, temp, fwd; Arg args[4]; XtSetArg(args[0], XtNcolormap, global.cmap); XtSetArg(args[1], XtNvisual, global.visual); XtSetArg(args[2], XtNdepth, global.depth); post_menu = XtCreatePopupShell("postshell", menuShellWidgetClass, main_shell, args, 3); menu = XtCreateManagedWidget("postmenu", menuWidgetClass, post_menu, NULL, 0); temp = MenuCreateGadget("followup", stringGadgetClass, menu, NULL, 0); XtAddCallback(temp, XtNcallback, post_menu_callback, (XtPointer)POST); temp = MenuCreateGadget("mailreply", stringGadgetClass, menu, NULL, 0); XtAddCallback(temp, XtNcallback, post_menu_callback, (XtPointer)MAIL); temp = MenuCreateGadget("followupreply", stringGadgetClass, menu, NULL, 0); XtAddCallback(temp, XtNcallback, post_menu_callback, (XtPointer)(POST | MAIL)); temp = MenuCreateGadget("postnew", stringGadgetClass, menu, NULL, 0); XtAddCallback(temp, XtNcallback, post_new_callback, NULL); temp = MenuCreateGadget("cancel", stringGadgetClass, menu, NULL, 0); XtAddCallback(temp, XtNcallback, cancel_post_callback, NULL); temp = MenuCreateGadget("supersede", stringGadgetClass, menu, NULL, 0); XtAddCallback(temp, XtNcallback, supersede_callback, NULL); if (global.forward_menu_size <= 0) fwd = MenuCreateGadget("forward", stringGadgetClass, menu, NULL, 0); else { XtSetArg(args[0], XtNmenuName, "forwardshell"); fwd = MenuCreateGadget("forward", pullRightGadgetClass, menu, args, 1); create_simple_menu(menu, "forward", global.forward_menu_size, fwd_menu_callback, NULL); } XtAddCallback(fwd, XtNpostPopdownCallback, fwd_default_callback, NULL); MenuCreateGadget("separator", separatorGadgetClass, menu, NULL, 0); include_toggle = MenuCreateGadget("quotetoggle", toggleGadgetClass, menu, NULL, 0); quote_sig_toggle = MenuCreateGadget("quotesig", toggleGadgetClass, menu, NULL, 0); } static void do_incl(String *params, Cardinal *no_params) { if (*no_params > 0) { if (case_lstrcmp(params[0], "true") == 0 || case_lstrcmp(params[0], "yes")) ToggleGadgetSet(include_toggle, True); else if (case_lstrcmp(params[0], "false") == 0 || case_lstrcmp(params[0], "no") == 0) ToggleGadgetSet(include_toggle, False); } } void action_followup(Widget w, XEvent *event, String *params, Cardinal *no_params) { int old = ToggleGadgetGet(include_toggle); do_incl(params, no_params); post_menu_callback(w, (XtPointer)POST, NULL); ToggleGadgetSet(include_toggle, old); } void action_reply(Widget w, XEvent *event, String *params, Cardinal *no_params) { int old = ToggleGadgetGet(include_toggle); do_incl(params, no_params); post_menu_callback(w, (XtPointer)MAIL, NULL); ToggleGadgetSet(include_toggle, old); } void action_followup_and_reply(Widget w, XEvent *event, String *params, Cardinal *no_params) { int old = ToggleGadgetGet(include_toggle); do_incl(params, no_params); post_menu_callback(w, (XtPointer)(POST|MAIL), NULL); ToggleGadgetSet(include_toggle, old); } void action_post_new(Widget w, XEvent *event, String *params, Cardinal *no_params) { post_new_callback(w, NULL, NULL); } void action_forward_by_mail(Widget w, XEvent *event, String *params, Cardinal *no_params) { PostContext *context; ARTICLE *art; SERVER *server; FILE *fp = NULL; char *file_name = NULL; char *full_name = res_full_name(); char *to = *no_params <= 0 ? NULL : params[0]; int edit; char *eol, *buffer; int ok; if (*no_params <= 1) edit = False; else { int n = strlen(params[1]); if (case_lstrncmp(params[1], "edit", n) == 0) edit = True; else if (case_lstrncmp(params[1], "mail", n) == 0) edit = False; else { fprintf(stderr, "knews: bad second parameter to forward-by-mail() \n" " action procedure: '%s'\n", params[1]); return; } } eol = edit ? "\n" : "\r\n"; if (!generic_post_check(True, &fp, &file_name)) return; context = create_post_context(MAIL | ORIG_MAIL, file_name); art = global.curr_art; if (!edit && context->file_name) unlink(context->file_name); if (!full_name) full_name = ""; fprintf(fp, "X-Newsreader: knews " KNEWS_VERSION "%s" "From: %s@%s (%s)%s" "Subject: %s%s (fwd)%s" "To: %s%s", eol, global.mail_name, global.domain_name, full_name, eol, PARENT(art) ? "Re: " : "", art->subject->subject, eol, to ? to : "", eol); context->line += 4; if (global.mime_forward) { fprintf(fp, "Mime-Version: 1.0%s" "Content-Type: message/rfc822%s" "Content-Transfer-Encoding: 8bit%s" "%s", eol, eol, eol, eol); context->line += 4; } else { fprintf(fp, "%s---Forwarded message---%s", eol, eol); context->line += 2; } set_busy(True); server = cache_get_server(art->no, False); if (!server) { char command[64]; server = main_server; sprintf(command, "ARTICLE %ld\r\n", art->no); buffer = server_comm(server, command, True); if (!buffer || atoi(buffer) != NNTP_OK_ARTICLE) { if (!buffer) reconnect_server(True); else popup_title_notice("Couldn't retrieve article, \n" "message from server is", buffer, True); unset_busy(); fclose(fp); free_post_context(context); return; } } while ((buffer = server_read(server)) && !IS_DOT(buffer)) { if (*buffer == '.' && !edit) buffer++; fprintf(fp, "%s%s", buffer, eol); } if (server != main_server) server_free(server); else if (!buffer) { fclose(fp); reconnect_server(True); unset_busy(); free_post_context(context); return; } unset_busy(); if (fflush(fp) < 0) { set_message("Error with file!", True); free_post_context(context); fclose(fp); return; } if (edit) { fclose(fp); fork_editor(context); return; } set_message("Mailing...", False); ok = post_to_agent(MAIL_COMMAND, fp); if (ok) set_message("Mailing was successful.", False); else set_message("Mail failed!", True); fclose(fp); free_post_context(context); } ./knews-1.0b.1/src/k_node.c100644 1244 1244 13442 6455455554 14055 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include "global.h" #include "color.h" #include "k_I.h" #include "k_node.h" #include "widgets.h" #include "xutil.h" void alloc_hot_pixel(KILL_NODE *node, int popup) { XColor col; if (!node->color || !node->hot) return; if (!XParseColor(display, global.cmap, node->color, &col)) { if (popup) popup_colornotice(True); else fprintf(stderr, "knews: Bogus color in kill" "file: \"%s\"\n", node->color); node->pixel = global.default_hot_pixel; node->alloced_pixel = False; } else if (XAllocColor(display, global.cmap, &col)) { node->pixel = col.pixel; node->alloced_pixel = True; } else { if (popup) popup_colornotice(False); else fprintf(stderr, "knews: Cannot allocate " "colormap entry for \"%s\"\n", node->color); node->pixel = get_closest_color(&col); node->alloced_pixel = False; } fix_node_pixmap(node); } void fix_node_pixmap(KILL_NODE *node) { GC gc = DefaultGCOfScreen(XtScreen(main_widgets.shell)); if (node->pixmap == None) node->pixmap = XCreatePixmap(display, XtWindow(main_widgets.shell), HOT_PIXMAP_SIZE, HOT_PIXMAP_SIZE, global.depth); XSetForeground(display, gc, node->pixel); XFillRectangle(display, node->pixmap, gc, 0, 0, HOT_PIXMAP_SIZE, HOT_PIXMAP_SIZE); } KILL_NODE *parse_kill_line(char *line, int is_global) { KILL_NODE *node; char *group, *expr; regex_t *expr_re, *group_re; int field, scope, hot; if (!(group = strstr(line, "||")) || !(expr = strstr(group + 2, "||"))) { fprintf(stderr, "Parse error in kill entry: %s\n", line); return NULL; } switch (line[0]) { case 'M': case 'm': field = KillFieldMsgid; break; case 'S': case 's': field = KillFieldSubject; break; case 'F': case 'f': field = KillFieldFrom; break; case 'X': case 'x': field = KillFieldXref; break; default: fprintf(stderr, "Bad field in kill entry: %s\n", line); return NULL; } switch (line[1]) { case 'A': case 'a': scope = KillScopeArticle; break; case 'S': case 's': scope = KillScopeSubject; break; case 'T': scope = KillScopeThread; break; case 't': scope = KillScopeSubthread; break; default: fprintf(stderr, "Bad scope in kill entry: %s\n", line); return NULL; } if (line[2] == '\0') { fprintf(stderr, "Bad action in kill entry: %s\n", line); return NULL; } hot = (line[2] == 'h' || line[2] == 'H'); *group++ = '\0'; *group++ = '\0'; *expr++ = '\0'; *expr++ = '\0'; if (*group == '\0' || !is_global) group = NULL; if (field == KillFieldMsgid) { expr_re = NULL; if (expr[0] != '<' || expr[strlen(expr) - 1] != '>') { fprintf(stderr, "Bad message-id in kill file: %s\n", expr); return NULL; } } else { expr_re = (regex_t *)XtMalloc(sizeof *expr_re); if (regcomp(expr_re, expr, REGEXP_COMPILE_FLAGS) != 0) { fprintf(stderr, "Parse error in expression regexp: %s\n", expr); XtFree((char *)expr_re); return NULL; } } if (!group) group_re = NULL; else { group_re = (regex_t *)XtMalloc(sizeof *group_re); if (regcomp(group_re, group, REGEXP_COMPILE_FLAGS) != 0) { fprintf(stderr, "Parse error in group regexp: %s\n", group); XtFree((char *)expr_re); XtFree((char *)group_re); return NULL; } } node = (KILL_NODE *)XtMalloc(sizeof *node); node->expr_str = XtNewString(expr); node->expr_re = expr_re; node->group_str = group ? XtNewString(group) : NULL; node->group_re = group_re; node->color = hot ? XtNewString(line + 3) : NULL; node->pixel = global.default_hot_pixel; node->pixmap = None; node->field = field; node->scope = scope; node->hot = hot; node->expired = False; node->alloced_pixel = False; if (hot) alloc_hot_pixel(node, False); return node; } void fprint_kill_node(FILE *fp, KILL_NODE *node, int expire) { if (expire && node->expired) return; fprintf(fp, "%c%c%c%s||%s||%s\n", "MSFX"[node->field], "ASTt"[node->scope], "KH"[node->hot], node->color ? node->color : "", node->group_str ? node->group_str : "", node->expr_str ? node->expr_str : ""); } void sprint_kill_node(KILL_NODE *node, char *buffer, long len) { long n; len--; buffer[0] = node->expired ? '!' : ' '; buffer[1] = "MSFX"[node->field]; buffer[2] = "ASTt"[node->scope]; buffer[3] = "KH"[node->hot]; buffer[4] = ' '; buffer[5] = '\0'; buffer += 5; len -= 5; #define MAX_GROUP_LEN 20 if (node->group_str) { n = strlen(node->group_str); if (n < MAX_GROUP_LEN) { memcpy(buffer, node->group_str, n); memset(buffer + n, ' ', MAX_GROUP_LEN - n); } else { memcpy(buffer, node->group_str, MAX_GROUP_LEN - 3); memcpy(buffer + MAX_GROUP_LEN - 3, "...", 3); } buffer += MAX_GROUP_LEN; *buffer++ = ' '; *buffer = '\0'; len -= MAX_GROUP_LEN + 1; } if (node->expr_str) { n = strlen(node->expr_str); if (n > len) n = len; memcpy(buffer, node->expr_str, n); buffer[n] = '\0'; } } void free_kill_node(KILL_NODE *node) { XtFree(node->expr_str); XtFree(node->group_str); XtFree(node->color); node->expr_str = NULL; node->group_str = NULL; node->color = NULL; if (node->expr_re) { regfree(node->expr_re); XtFree((char *)node->expr_re); node->expr_re = NULL; } if (node->group_re) { regfree(node->group_re); XtFree((char *)node->group_re); node->group_re = NULL; } if (node->alloced_pixel) { unsigned long pixel = node->pixel; XFreeColors(display, global.cmap, &pixel, 1, 0); node->alloced_pixel = False; } if (node->pixmap != None) { XFreePixmap(display, node->pixmap); node->pixmap = None; } XtFree((char *)node); } ./knews-1.0b.1/src/k_kill.h100644 1244 1244 337 6455455554 14027 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ struct KILL_NODE; typedef long (*KillFunc)(struct KILL_NODE*, ARTICLE*, SUBJECT*); extern const KillFunc kill_funcs[4][4]; extern const KillFunc hot_funcs[4][4]; ./knews-1.0b.1/src/actions.h100644 1244 1244 2605 6455455554 14242 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ extern void action_tree_up(Widget, XEvent*, String*, Cardinal*); extern void action_tree_down(Widget, XEvent*, String*, Cardinal*); extern void action_tree_left(Widget, XEvent*, String*, Cardinal*); extern void action_tree_right(Widget, XEvent*, String*, Cardinal*); extern void action_tree_down_right(Widget, XEvent*, String*, Cardinal*); extern void action_list_up(Widget, XEvent*, String*, Cardinal*); extern void action_list_down(Widget, XEvent*, String*, Cardinal*); extern void action_tree_or_list_up(Widget, XEvent*, String*, Cardinal*); extern void action_tree_or_list_down(Widget, XEvent*, String*, Cardinal*); extern void action_exit_mode(Widget, XEvent*, String*, Cardinal*); extern void action_enter_mode(Widget, XEvent*, String*, Cardinal*); extern void action_tree_left_or_exit_mode(Widget, XEvent*, String*, Cardinal*); extern void action_tree_right_or_enter_mode(Widget, XEvent*, String*, Cardinal*); extern void action_goto_next_hot(Widget, XEvent*, String*, Cardinal*); extern void action_view_thread(Widget, XEvent*, String*, Cardinal*); extern void action_change_size(Widget, XEvent*, String*, Cardinal*); extern void action_popup_find_group(Widget, XEvent*, String*, Cardinal*); extern void action_do_the_right_thing(Widget, XEvent*, String*, Cardinal*); extern void action_tree_layout(Widget, XEvent*, String*, Cardinal*); ./knews-1.0b.1/src/k_I.h100644 1244 1244 1517 6455455554 13305 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ enum { KillFieldMsgid = 0, KillFieldSubject = 1, KillFieldFrom = 2, KillFieldXref = 3 }; enum { KillScopeArticle = 0, KillScopeSubject = 1, KillScopeThread = 2, KillScopeSubthread = 3 }; typedef struct KILL_NODE { char *expr_str; regex_t *expr_re; char *group_str; regex_t *group_re; char *color; Pixel pixel; Pixmap pixmap; unsigned int field : 2, scope : 2, hot : 1, expired : 1, alloced_pixel : 1; } KILL_NODE; typedef struct KILL_FILE { long n; struct KILL_NODE **nodes; GROUP *group; char *file_name; struct KILL_WIDGETS *w; unsigned char expire; unsigned char stay_up; unsigned char dirty; } KILL_FILE; ./knews-1.0b.1/src/p_setup.h100644 1244 1244 623 6455455554 14237 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ struct PostContext; extern struct PostContext *create_post_context(int, char*); extern void free_post_context(struct PostContext*); extern int outstanding_posts(void); extern void post_setup(struct PostContext*, FILE*, ARTICLE*, int, int, int); extern void append_signature(FILE*); extern int insert_extra_headers(FILE*, ARTICLE*); ./knews-1.0b.1/src/p_attach.h100644 1244 1244 1642 6455455554 14365 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ struct PostAttachment; extern struct PostAttachment *create_attachment(char*, char*); extern void free_attachment(struct PostAttachment*); extern int print_attachment(FILE*, struct PostAttachment*); extern void print_attach_info(struct PostAttachment*, char*); extern int attach_get_enc(struct PostAttachment*); extern int attach_is_inline(struct PostAttachment*); extern char *attach_get_type(struct PostAttachment*); extern char *attach_get_name(struct PostAttachment*); extern char *attach_get_descr(struct PostAttachment*); extern int attach_set_enc(struct PostAttachment*, int, char*); extern int attach_set_inline(struct PostAttachment*, int, char*); extern int attach_set_type(struct PostAttachment*, char*, char*); extern int attach_set_name(struct PostAttachment*, char*, char*); extern int attach_set_descr(struct PostAttachment*, char*, char*); ./knews-1.0b.1/src/gif.c100644 1244 1244 17273 6455455554 13371 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ /* * From the gif89a spec: * * "The Graphics Interchange Format(c) is the Copyright property of * CompuServe Incorporated. GIF(sm) is a Service Mark property of * CompuServe Incorporated." */ #include "global.h" #include "color.h" #include "font.h" #include "gif.h" #include "gif_I.h" #include "widgets.h" #include "../Widgets/ArtText.h" Pixmap do_gif(char *data, long len, long *wp, long *hp) { Pixmap pixmap; unsigned char *pic; struct gif_info g_str; long w, h, n, size; gif_init(&g_str, (unsigned char *)data, len); if (gif_read_header(&g_str) < 0) { ArtTextAddLine(main_widgets.text, "[knews: not a gif.]", ascii_font->body_font, global.alert_pixel); return None; } if (g_str.cmap_size == 0) { ArtTextAddLine(main_widgets.text, "[knews: no colormap in gif.]", ascii_font->body_font, global.alert_pixel); return None; } w = *wp = g_str.width; h = *hp = g_str.height; size = w * h; pic = (unsigned char *)XtMalloc(size); n = gif_read_image(&g_str, pic); if (n < size) if (!g_str.err_str) ArtTextAddLine(main_widgets.text, "[knews: truncated gif stream.]", ascii_font->body_font, global.alert_pixel); else { ArtTextAddLine(main_widgets.text, "[knews: ", ascii_font->body_font, global.alert_pixel); ArtTextAppendToLast(main_widgets.text, g_str.err_str); ArtTextAppendToLast(main_widgets.text, ".]"); } pixmap = put_cmapped_image(pic, w, h, g_str.cmap, g_str.cmap_size); XtFree((char *)pic); return pixmap; } /********************************************************************/ static int init_decoder(struct gif_info *g) { unsigned int i; if (g->src_len <= 0) return -1; g->min_code_size = NEXT_BYTE(g); if (g->min_code_size < 2 || g->min_code_size > 11) { g->err_str = "nonsensical code size in gif image"; return -1; } g->clear_code = 1u << g->min_code_size; g->end_code = g->clear_code + 1; g->code_size = g->min_code_size + 1; g->code_mask = (1u << g->code_size) - 1; g->table_size = g->end_code + 1; g->prev_code = NULL_CODE; g->bits = 0; g->n_bits = 0; g->n_buf = 0; g->sp = 0; for (i = 0 ; i < TABLE_SIZE ; i++) { g->table[i].prefix = NULL_CODE; g->table[i].suffix = i; } return 0; } static int push_string(struct gif_info *g, unsigned int code) { unsigned int i = 0; while (code < TABLE_SIZE && g->table[code].prefix != NULL_CODE) { if (++i == 0xffffu) { g->err_str = "gif decoder infinite loop"; return -1; } if (g->sp >= STACK_SIZE) break; g->stack[g->sp++] = g->table[code].suffix; code = g->table[code].prefix; } if (code >= TABLE_SIZE) { g->err_str = "gif decode table overflow"; return -1; } if (g->sp >= STACK_SIZE) { g->err_str = "gif decode stack overflow"; return -1; } return g->stack[g->sp++] = g->table[code].suffix; } static int get_byte(struct gif_info *g) { int code; while (g->sp == 0) { while (g->n_bits < g->code_size) { if (g->n_buf == 0) { if (g->src_len <= 0) return -1; g->n_buf = NEXT_BYTE(g); if (g->n_buf == 0) return -1; /* end of data */ g->buf = g->src_buf; g->src_buf += g->n_buf; g->src_len -= g->n_buf; } g->n_buf--; g->bits |= *g->buf++ << g->n_bits; g->n_bits += 8; } code = g->bits & g->code_mask; g->bits >>= g->code_size; g->n_bits -= g->code_size; if (code == g->end_code) return -1; if (code == g->clear_code) { g->code_size = g->min_code_size + 1; g->code_mask = (1u << g->code_size) - 1; g->prev_code = NULL_CODE; g->table_size = g->end_code + 1; } else if (g->prev_code == NULL_CODE) { if (push_string(g, code) < 0) return -1; g->prev_code = code; } else { int first, sp; if (code < g->table_size) first = push_string(g, code); else { sp = g->sp; if (g->sp >= STACK_SIZE) { g->err_str = "gif decode stack overflow"; return -1; } g->stack[g->sp++] = -1; first = push_string(g, g->prev_code); if (first < 0) return -1; g->stack[sp] = first; } if (g->table_size >= TABLE_SIZE) { g->err_str = "gif decode table overflow"; return -1; } g->table[g->table_size].prefix = g->prev_code; g->table[g->table_size].suffix = first; if (g->table_size == g->code_mask && g->code_size < 12) { g->code_size++; g->code_mask = (1u << g->code_size) - 1; } g->table_size++; g->prev_code = code; } } return g->stack[--g->sp]; } static int read_cmap(struct gif_info *g, int misc_bits) { unsigned int i; if (!(misc_bits & 0x80)) return 0; g->cmap_size = 1u << ((misc_bits & 0x07) + 1); g->src_len -= 3 * g->cmap_size; if (g->src_len < 0) return -1; for (i = 0 ; i < g->cmap_size ; i++) { g->cmap[i].r = *g->src_buf++; g->cmap[i].g = *g->src_buf++; g->cmap[i].b = *g->src_buf++; } return 0; } static int read_image_header(struct gif_info *g) { unsigned int misc_bits; if (g->src_len < 9) return -1; (void)NEXT_BYTE(g); (void)NEXT_BYTE(g); /* image x pos */ (void)NEXT_BYTE(g); (void)NEXT_BYTE(g); /* image y pos */ g->width = NEXT_BYTE(g); g->width += NEXT_BYTE(g) << 8; g->height = NEXT_BYTE(g); g->height += NEXT_BYTE(g) << 8; misc_bits = NEXT_BYTE(g); g->interlaced = (misc_bits & 0x40) ? True : False; if (read_cmap(g, misc_bits) < 0) /* local colormap */ return -1; return 0; } static int skip_extension(struct gif_info *g) { unsigned int n; (void)NEXT_BYTE(g); /* type of extension */ if (g->src_len < 0) return -1; while ((n = NEXT_BYTE(g)) != 0) { g->src_buf += n; g->src_len -= n; if (g->src_len <= 0) return -1; } return 0; } int gif_read_header(struct gif_info *g) { unsigned int misc_bits; if (g->src_len < 13 || (memcmp(g->src_buf, "GIF89a", 6) != 0 && memcmp(g->src_buf, "GIF87a", 6) != 0)) return -1; g->src_buf += 6; g->src_len -= 6; /* * Ignore junk in screen descriptor. */ (void)NEXT_BYTE(g); (void)NEXT_BYTE(g); /* screen width */ (void)NEXT_BYTE(g); (void)NEXT_BYTE(g); /* screen height */ misc_bits = NEXT_BYTE(g); (void)NEXT_BYTE(g); /* background */ (void)NEXT_BYTE(g); /* aspect ratio */ if (read_cmap(g, misc_bits) < 0) /* global colormap */ return -1; while (g->src_len-- > 0) switch (*g->src_buf++) { case ';': /* Terminator */ return -1; case '!': /* Extension */ if (skip_extension(g) < 0) return -1; break; case ',': /* Start of first image */ return read_image_header(g); default: /* Invalid */ return -1; } return -1; } long gif_read_image(struct gif_info *g, unsigned char *dest) { unsigned int w = g->width; unsigned int h = g->height; long n = 0; int byte; if (init_decoder(g) < 0) return 0; if (!g->interlaced) { long size = w * h; while (n < size) { byte = GET_BYTE(g); if (byte < 0) return n; dest[n++] = byte; } } else { static const char start[] = {0, 4, 2, 1}; static const char step[] = {8, 8, 4, 2}; unsigned int pass, x, y; long n_step; for (pass = 0 ; pass < 4 ; pass++) { n = start[pass] * w; n_step = (step[pass] - 1) * w; for (y = start[pass] ; y < h ; y += step[pass], n += n_step) for (x = 0 ; x < w ; x++) { byte = get_byte(g); if (byte < 0) return n; dest[n++] = byte; } } } return n; } void gif_init(struct gif_info *g, const unsigned char *data, long len) { g->src_buf = data; g->src_len = len; g->err_str = NULL; } ./knews-1.0b.1/src/p_check.h100644 1244 1244 220 6455455555 14146 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ struct PostContext; extern void check_article_to_post(struct PostContext*, Widget); ./knews-1.0b.1/src/gif.h100644 1244 1244 154 6455455555 13325 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ extern Pixmap do_gif(char*, long, long*, long*); ./knews-1.0b.1/src/parse.h100644 1244 1244 1614 6455455555 13714 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #define PARSEDATE_ERROR ((time_t)(-1)) extern time_t parsedate(char *); extern char *time_t_to_date(time_t, char*); extern char *eat_re(char*); extern const char *parse_author(const char*, long*); extern const char month_names[]; typedef struct MimeArg { char *name; char *value; } MimeArg; typedef enum { MimeEncNone, /* 7bit, 8bit and binary */ MimeEncBase64, MimeEncQP, MimeEncUue } MimeEnc; extern int parse_content_enc(char**); extern int parse_content_type(char**, char*, int, char*, int, MimeArg*, int, int); extern char *get_charset(MimeArg*); extern char *parse_content_disp(char**); typedef struct { char *word; char *end; short len; short ch_len; short is_qp; } EncWordData; extern char *next_enc_word(char*, EncWordData*); extern void decode_rfc1522(char*, const char*); ./knews-1.0b.1/src/jpeg.c100644 1244 1244 11041 6455455555 13535 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include "global.h" #include #include "color.h" #include "font.h" #include "jpeg.h" #include "widgets.h" #include "../Widgets/ArtText.h" #if !HAVE_JPEG Pixmap do_jpeg(char *data, long len, long *wp, long *hp) { return None; } #else #include #if BITS_IN_JSAMPLE != 8 #error "BITS_IN_JSAMPLE must be 8" #endif /* * libjpeg can't dither to greyscale colormap... */ static unsigned char *jpg_cmap[3] = {0, }; static unsigned int jpg_cols_inited = False; static void init_jpg_cmap(void) { unsigned int n; if (jpg_cols_inited) return; jpg_cols_inited = True; if (!cmap) /* true color */ return; jpg_cmap[0] = (unsigned char *)XtMalloc(cmap_size); jpg_cmap[1] = (unsigned char *)XtMalloc(cmap_size); jpg_cmap[2] = (unsigned char *)XtMalloc(cmap_size); for (n = 0 ; n < cmap_size ; n++) { jpg_cmap[0][n] = cmap[n].r; jpg_cmap[1][n] = cmap[n].g; jpg_cmap[2][n] = cmap[n].b; } } /*********************************************************************/ struct jpg_error_mgr { struct jpeg_error_mgr jerr; jmp_buf jump_buffer; }; static void jpg_error_exit(j_common_ptr cinfo) { struct jpg_error_mgr *err_mgr = (struct jpg_error_mgr *)cinfo->err; longjmp(err_mgr->jump_buffer, 1); } static void jpg_no_op(j_decompress_ptr cinfo) { } static boolean jpg_fill_input_buffer(j_decompress_ptr cinfo) { struct jpg_error_mgr *err_mgr = (struct jpg_error_mgr *)cinfo->err; longjmp(err_mgr->jump_buffer, 1); } static void jpg_skip_input_data(j_decompress_ptr cinfo, long num_bytes) { if (num_bytes < 0) return; if (num_bytes >= cinfo->src->bytes_in_buffer) num_bytes = cinfo->src->bytes_in_buffer; cinfo->src->bytes_in_buffer -= num_bytes; cinfo->src->next_input_byte += num_bytes; } Pixmap do_jpeg(char *data, long len, long *wp, long *hp) { struct jpeg_decompress_struct cinfo; struct jpg_error_mgr err_mgr; struct jpeg_source_mgr src_mgr; Pixmap pixmap; void *volatile vol_pic = NULL; volatile long vol_w = 0; volatile long vol_h = 0; volatile int vol_did = False; volatile int grey = False; init_jpg_cmap(); cinfo.err = jpeg_std_error(&err_mgr.jerr); err_mgr.jerr.error_exit = jpg_error_exit; if (setjmp(err_mgr.jump_buffer)) { char buf[JMSG_LENGTH_MAX]; cinfo.err->format_message((j_common_ptr)&cinfo, buf); ArtTextAddLine(main_widgets.text, "[knews: jpeg error: ", ascii_font->body_font, global.alert_pixel); ArtTextAppendToLast(main_widgets.text, buf); ArtTextAppendToLast(main_widgets.text, "]"); } else { long w, h; int col, did; unsigned char *pic, *row; src_mgr.next_input_byte = (unsigned char *)data; src_mgr.bytes_in_buffer = len; src_mgr.init_source = jpg_no_op; src_mgr.fill_input_buffer = jpg_fill_input_buffer; src_mgr.skip_input_data = jpg_skip_input_data; src_mgr.resync_to_restart = jpeg_resync_to_restart; src_mgr.term_source = jpg_no_op; jpeg_create_decompress(&cinfo); cinfo.src = &src_mgr; jpeg_read_header(&cinfo, True); if (cinfo.jpeg_color_space == JCS_GRAYSCALE) /* * libjpeg can't convert from grayscale to rgb... */ grey = True; else { cinfo.out_color_space = JCS_RGB; if (jpg_cmap[0]) { cinfo.quantize_colors = True; cinfo.colormap = jpg_cmap; cinfo.actual_number_of_colors = cmap_size; } } jpeg_start_decompress(&cinfo); vol_w = w = cinfo.output_width; vol_h = h = cinfo.output_height; if (cinfo.output_components != 1 && cinfo.output_components != 3) { fprintf(stderr, "knews: output_components=%d\n", cinfo.output_components); longjmp(err_mgr.jump_buffer, 1); } col = cinfo.output_components == 3; did = False; vol_pic = pic = (unsigned char *)XtMalloc(w * h * (col ? 3 : 1)); while (cinfo.output_scanline < h) { row = pic + cinfo.output_scanline * w * (col ? 3 : 1); jpeg_read_scanlines(&cinfo, &row, 1); if (!did) vol_did = did = True; } jpeg_finish_decompress(&cinfo); } if (!vol_did) pixmap = None; else { unsigned char *pic = vol_pic; unsigned int w = vol_w; unsigned int h = vol_h; *wp = w; *hp = h; if (grey) pixmap = put_grey_image(pic, w, h); else if (!jpg_cmap[0]) pixmap = put_24_image(pic, w, h); else { long i, n = w * h; for (i = 0 ; i < n ; i++) pic[i] = cmap[pic[i]].pixel; pixmap = put_8_image(pic, w, h); } } jpeg_destroy_decompress(&cinfo); XtFree((char *)vol_pic); return pixmap; } #endif ./knews-1.0b.1/src/expand.c100644 1244 1244 10363 6455455555 14075 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include "global.h" #include "expand.h" #include "parse.h" #include "util.h" char *regexp_escape_string(char *src, int anchor) { char *result, *dest; dest = result = XtMalloc(2 * strlen(src) + 3); if (anchor) *dest++ = '^'; while (*src != '\0') { if (strchr("^.[$()|*+?{\\", *src)) *dest++ = '\\'; *dest++ = *src++; } if (anchor) *dest++ = '$'; *dest = '\0'; return XtRealloc(result, strlen(result) + 1); } char *expand_view_command(const char *src, char *type, char *subtype, MimeArg *args, int needs_term, int copious) { char *dest; const char *p; long len, pos, i, n; pos = 0; len = 120; dest = XtMalloc(len + 3); dest[0] = '\0'; while (*src != '\0') if (*src == '%') { char c = *++src; switch (c) { case '\0': continue; case 's': src++; dest[pos++] = '%'; dest[pos++] = 's'; continue; case 't': n = strlen(type) + strlen(subtype) + 8; if (pos + n + 8 > len) { len += n + 8; dest = XtRealloc(dest, len + 3); } sprintf(dest + pos, "%s/%s", type, subtype); pos += strlen(dest + pos); continue; case '{': p = strchr(src, '}'); if (!p) break; src++; for (i = 0 ; args[i].value ; i++) if (strlen(args[i].name) == p - src && case_strncmp(src, args[i].name, p - src) == 0) break; src = p + 1; if (!args[i].value) continue; n = strlen(args[i].value); if (pos + n + 8 > len) { len += n + 8; dest = XtRealloc(dest, len + 3); } strcpy(dest + pos, args[i].value); pos += strlen(dest + pos); continue; } dest[pos++] = c; src++; } else { if (pos + 8 > len) { len *= 2; dest = XtRealloc(dest, len + 3); } if (*src != '\\') dest[pos++] = *src++; else if (*++src != '\0') dest[pos++] = *src++; } dest[pos] = '\0'; return dest; } char *expand_path(char *file_name) { char *path = NULL; long len = 0, pos = 0; char ch; if (file_name[0] == '~' && file_name[1] == '/') file_name += 2; for (ch = *file_name++ ; ch != '\0' ; ch = *file_name++) { if (pos + 8 > len) { len = pos + 256; path = XtRealloc(path, len); } if (ch != '%') path[pos++] = ch; else { char *p, *c = NULL; int cap = False; int slash = False; int clen = 0; ch = *file_name++; switch (ch) { case '%': path[pos++] = '%'; continue; /* don't fall through */ case 'a': case 'A': if (global.mode != NewsModeGroup && global.mode != NewsModeThread) { fputs("knews: Not in a newsgroup!\n", stderr); XtFree(path); return NULL; } if (!global.curr_art) { fputs("knews: No selected article!\n", stderr); XtFree(path); return NULL; } sprintf(path + pos, "%ld", global.curr_art->no); pos += strlen(path + pos); continue; case 'g': slash = True; break; case 'G': cap = True; slash = True; break; case 'n': break; case 'N': cap = True; break; case 'p': case 'P': c = global.nntp_server; if (c) c = strchr(c, ':'); if (!c) continue; clen = strlen(c); break; case 's': case 'S': c = global.nntp_server; if (!c) { fputs("knews: nntp_server is NULL!\n", stderr); XtFree(path); return NULL; } p = strchr(c, ':'); if (p) clen = p - c; else clen = strlen(c); break; default: fprintf(stderr, "knews: %%%c: Unknown format specifier.\n", ch); XtFree(path); return NULL; } if (!c) if (global.curr_group && global.curr_group->name) { c = global.curr_group->name; clen = strlen(c); } else { fputs("knews: Not in a newsgroup.\n", stderr); XtFree(path); return NULL; } if (clen == 0) continue; if (pos + clen + 8 > len) { len = pos + clen + 256; path = XtRealloc(path, len); } ch = *c++; clen--; if (cap && islower((unsigned char)ch)) ch = toupper((unsigned char)ch); path[pos++] = ch; while (clen-- > 0) { ch = *c++; if (ch == '.' && slash) ch ='/'; path[pos++] = ch; } path[pos] = '\0'; } } path[pos] = '\0'; return path; } ./knews-1.0b.1/src/ftp.h100644 1244 1244 64 6455455555 13331 0ustar kallekalleextern int ftp_get(void); extern int ftp_put(void); ./knews-1.0b.1/src/expand.h100644 1244 1244 375 6455455555 14044 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ struct MimeArg; extern char *regexp_escape_string(char*, int); extern char *expand_view_command(const char*, char*, char*, struct MimeArg*, int, int); extern char *expand_path(char*); ./knews-1.0b.1/src/ftp.c100644 1244 1244 144 6455455555 13343 0ustar kallekalle#include "global.h" int ftp_get(void) { return True; } int ftp_put(void) { return True; } ./knews-1.0b.1/src/k_node.h100644 1244 1244 667 6455455555 14030 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ extern struct KILL_NODE *parse_kill_line(char*, int); extern void fprint_kill_node(FILE*, struct KILL_NODE*, int); extern void sprint_kill_node(struct KILL_NODE*, char*, long); extern void free_kill_node(struct KILL_NODE*); extern long apply_kill(struct KILL_NODE*); extern void fix_node_pixmap(struct KILL_NODE*); extern void alloc_hot_pixel(struct KILL_NODE*, int); ./knews-1.0b.1/src/jpeg.h100644 1244 1244 155 6455455555 13506 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ extern Pixmap do_jpeg(char*, long, long*, long*); ./knews-1.0b.1/src/png.c100644 1244 1244 10562 6625552121 13365 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include "global.h" #include "color.h" #include "file.h" #include "font.h" #include "png.h" #include "widgets.h" #include "../Widgets/ArtText.h" #if !HAVE_PNG Pixmap do_png(char *data, long len, long *wp, long *hp) { return None; } #else #include #include static unsigned int p_cmap_inited = False; static png_color *p_cmap = NULL; static void init_png_cmap(void) { unsigned int i; if (p_cmap_inited) return; p_cmap_inited = True; if (!cmap) return; p_cmap = (png_color *)XtMalloc(cmap_size * sizeof p_cmap[0]); for (i = 0 ; i < cmap_size ; i++) { p_cmap[i].red = cmap[i].r; p_cmap[i].green = cmap[i].g; p_cmap[i].blue = cmap[i].b; } } /* * libpng can only read from files... */ static FILE *dump_for_png(char *data, long len) { FILE *fp; char *fname; int fd; fd = create_temp_fd(&fname); if (fd < 0) return NULL; unlink(fname); if (writen(fd, data, len) < 0) { close(fd); return NULL; } if (lseek(fd, 0, SEEK_SET) < 0) { perror("lseek"); close(fd); return NULL; } fp = fdopen(fd, "r"); if (!fp) { perror("fdopen"); close(fd); return NULL; } return fp; } Pixmap do_png(char *data, long len, long *wp, long *hp) { png_struct p_str; png_info p_info; Pixmap pixmap; FILE *volatile vol_fp = NULL; void *volatile vol_pic = NULL; void *volatile vol_pal = NULL; volatile int vol_pn = 0; volatile long vol_w = 0; volatile long vol_h = 0; volatile int vol_did = False; volatile int grey = False; init_png_cmap(); if (!(vol_fp = dump_for_png(data, len))) { ArtTextAddLine(main_widgets.text, "[knews: temp file error.]", ascii_font->body_font, global.alert_pixel); return None; } if (setjmp(p_str.jmpbuf)) ArtTextAddLine(main_widgets.text, "[knews: png error.]", ascii_font->body_font, global.alert_pixel); else { unsigned char *pic, *row; long w, h; int did; unsigned int per_line = 0; unsigned int i, j, pass; png_read_init(&p_str); png_info_init(&p_info); png_init_io(&p_str, vol_fp); png_read_info(&p_str, &p_info); vol_w = w = p_info.width; vol_h = h = p_info.height; if (p_info.bit_depth == 16) png_set_strip_16(&p_str); else if (p_info.bit_depth < 8) png_set_packing(&p_str); if (p_info.valid & PNG_INFO_bKGD) png_set_background(&p_str, &p_info.background, PNG_BACKGROUND_GAMMA_FILE, True, 1.0); else { static png_color_16 bg = {0, }; png_set_background(&p_str, &bg, PNG_BACKGROUND_GAMMA_SCREEN, False, 1.0); } per_line = w; if (!(p_info.color_type & PNG_COLOR_MASK_COLOR)) { /* grey image */ grey = True; png_set_expand(&p_str); } else if (!p_cmap) { /* true color visual */ if (p_info.color_type == PNG_COLOR_TYPE_PALETTE) png_set_expand(&p_str); per_line *= 3; } else if (p_info.color_type & PNG_COLOR_MASK_PALETTE) { CMAP_ENTRY *pal; int i, pn; pn = p_info.num_palette; pal = (CMAP_ENTRY *)XtMalloc(pn * sizeof *pal); for (i = 0 ; i < pn ; i++) { pal[i].r = p_info.palette[i].red; pal[i].g = p_info.palette[i].green; pal[i].b = p_info.palette[i].blue; } vol_pal = pal; vol_pn = pn; } else { png_set_dither(&p_str, p_cmap, cmap_size, cmap_size, NULL, True); } pass = png_set_interlace_handling(&p_str); png_start_read_image(&p_str); vol_pic = pic = (unsigned char *)XtMalloc(h * per_line); did = False; for (i = 0 ; i < pass ; i++) { row = pic; for (j = 0 ; j < h ; j++) { png_read_row(&p_str, NULL, row); if (!did) vol_did = did = True; row += per_line; } } png_read_end(&p_str, NULL); } if (!vol_did) pixmap = None; else { unsigned char *pic = vol_pic; CMAP_ENTRY *pal = vol_pal; unsigned int w = vol_w; unsigned int h = vol_h; *wp = w; *hp = h; if (grey) pixmap = put_grey_image(pic, w, h); else if (!p_cmap) pixmap = put_24_image(pic, w, h); else if (pal) pixmap = put_cmapped_image(pic, w, h, pal, vol_pn); else { long i, n = w * h; for (i = 0 ; i < n ; i++) pic[i] = cmap[pic[i]].pixel; pixmap = put_8_image(pic, w, h); } } png_read_destroy(&p_str, &p_info, NULL); fclose((FILE *)vol_fp); XtFree((char *)vol_pic); XtFree((char *)vol_pal); return pixmap; } #endif ./knews-1.0b.1/src/png.h100644 1244 1244 154 6455455555 13344 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ extern Pixmap do_png(char*, long, long*, long*); ./knews-1.0b.1/src/gif_I.h100644 1244 1244 2213 6455455555 13613 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #define NEXT_BYTE(g) ((g)->src_len--, *(g)->src_buf++) #define GET_BYTE(g) ((g)->sp == 0 ? get_byte(g) : (g)->stack[--(g)->sp]) #define STACK_SIZE 1024 #define TABLE_SIZE 4096 #define NULL_CODE 0xffffu struct gif_info { const unsigned char *src_buf; long src_len; char *err_str; /**/ unsigned short width; unsigned short height; unsigned short cmap_size; unsigned char interlaced; CMAP_ENTRY cmap[256]; /**/ unsigned int min_code_size; unsigned int clear_code; unsigned int end_code; unsigned int code_size; unsigned int code_mask; unsigned int prev_code; unsigned int table_size; struct { unsigned short prefix; unsigned short suffix; } table[TABLE_SIZE]; unsigned int sp; unsigned char stack[STACK_SIZE]; const unsigned char *buf; unsigned int n_buf; unsigned long bits; unsigned int n_bits; }; extern int gif_read_header(struct gif_info*); extern long gif_read_image(struct gif_info*, unsigned char*); extern void gif_init(struct gif_info*, const unsigned char*, long); ./knews-1.0b.1/src/xface.h100644 1244 1244 177 6625551371 13643 0ustar kallekalle/* * Copyright (C) 1998 Karl-Johan Johnsson. */ extern Pixmap do_xface(char **headers, int n, long *width, long *height); ./knews-1.0b.1/src/xface.c100644 1244 1244 4216 6625563772 13664 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include "global.h" #include "xface.h" #if !HAVE_COMPFACE Pixmap do_xface(char **headers, int n, long *width, long *height) { return None; } #else #include "util.h" #include #include #define FACE_SIZE 48 static int fill_buf(char **headers, int n, char *buf, int size) { int pos = 0; char *c; c = *headers++ + 7; /* Skip 'X-Face:' */ n--; do { int h_len = strlen(c); if (h_len + pos >= size) return False; while (h_len-- > 0) buf[pos++] = *c++; buf[pos] = '\0'; } while (n-- > 0 && (c = *headers++) && IS_SPACE(*c)); return True; } static int hex_digit(int ch) { if (ch >= '0' && ch <= '9') return ch - '0'; if (ch >= 'A' && ch <= 'F') return ch - 'A' + 10; if (ch >= 'a' && ch <= 'f') return ch - 'a' + 10; return -1; } static int parse_buf(const char *buf, unsigned char *image) { int i, j, k; for (i = 0 ; i < FACE_SIZE ; i++) for (j = 0 ; j < FACE_SIZE / 16 ; j++) { int d1, d2, d3, d4; unsigned val, mask; while (IS_SPACE(*buf) || *buf == '\n' || *buf == ',') buf++; if (*buf++ != '0' || (*buf++ != 'x' && *buf != 'X')) return False; if ((d1 = hex_digit(*buf++)) < 0 || (d2 = hex_digit(*buf++)) < 0 || (d3 = hex_digit(*buf++)) < 0 || (d4 = hex_digit(*buf++)) < 0) return False; val = (d1 << 12) | (d2 << 8) | (d3 << 4) | d4; for (mask = 0x8000 ; mask != 0 ; mask >>= 1) *image++ = (val & mask) ? 0 : 0xff; } return True; } static Pixmap conv_xface(char **headers, int n) { char buf[4000]; unsigned char image[FACE_SIZE * FACE_SIZE]; if (!fill_buf(headers, n, buf, sizeof buf - 2)) return None; if (uncompface(buf) < 0) return None; if (!parse_buf(buf, image)) return None; return put_grey_image(image, FACE_SIZE, FACE_SIZE); } Pixmap do_xface(char **headers, int n, long *width, long *height) { while (n > 0 && case_lstrncmp(*headers, "x-face:", 7) != 0) { headers++; n--; } if (n <= 0) return 0; else { *width = FACE_SIZE; *height = FACE_SIZE; return conv_xface(headers, n); } } #endif ./knews-1.0b.1/util/ 40755 1244 1244 0 6455455555 12520 5ustar kallekalle./knews-1.0b.1/util/tcp_relay/ 40755 1244 1244 0 6455455555 14502 5ustar kallekalle./knews-1.0b.1/util/tcp_relay/tcp_relay.man100644 1244 1244 1166 6455455556 17263 0ustar kallekalle.TH KNEWS 1 "1996" .SH NAME tcp_relay \- a tcp relay .SH SYNOPSIS .B tcp_relay -p port [-s] -h host ... remotehost[:port] .SH DESCRIPTION For more details, read the source. .SH OPTIONS .B -p port .RS The port tcp_relay will listen for connections on. .RE .B -s .RS If this option is given, only a single connection at a time will be handled. Otherwise tcp_relay will fork a new copy of itself for each incomming connection and go on to accept new connections. .RE .B -h host .RS Connections from this host will be allowed. Several of these may be supplied. .RE .SH AUTHOR This software is Copyright 1996 by Karl-Johan Johnsson. ./knews-1.0b.1/util/tcp_relay/Imakefile100644 1244 1244 74 6455455556 16352 0ustar kallekalle#include "../../knews.tmpl" SimpleProgramTarget(tcp_relay) ./knews-1.0b.1/util/tcp_relay/tcp_relay.c100644 1244 1244 14572 6455455556 16757 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #include #include #include #include #include #include #include #include #include #include #include #include #undef False #define False 0 #undef True #define True 1 #define DEFAULT_REMOTE_PORT 119 /* * Define this if doesn't declare getopt() and friends. */ #define BOGUS_UNISTD static void perror_exit(const char *msg) { perror(msg); _exit(1); } static void* xrealloc(void *ptr, size_t size) { if (!ptr) ptr = malloc(size); /* Hack for broken C libraries */ else ptr = realloc(ptr, size); if (!ptr) perror_exit("malloc"); return ptr; } static struct in_addr *allowed_hosts = NULL; static long n_hosts = 0; static long n_alloced = 0; static void allow_host(char *host) { struct hostent *hent; hent = gethostbyname(host); if (!hent) { fprintf(stderr, "No such host: %s\n", host); return; } if (n_hosts >= n_alloced) allowed_hosts = xrealloc(allowed_hosts, (n_alloced = 2 * (n_alloced + 1)) * sizeof allowed_hosts[0]); memcpy(&allowed_hosts[n_hosts++], hent->h_addr, hent->h_length); } static int is_allowed(struct sockaddr_in *addr) { long i; for (i = 0 ; i < n_hosts ; i++) if (memcmp(&addr->sin_addr, &allowed_hosts[i], sizeof addr->sin_addr) == 0) return True; return False; } static struct sockaddr_in get_remote_host(char *host) { struct sockaddr_in addr; struct hostent *hent; char *c; memset(&addr, 0, sizeof addr); addr.sin_family = PF_INET; addr.sin_port = htons(DEFAULT_REMOTE_PORT); c = strchr(host, ':'); if (c) { *c++ = '\0'; /* FIXME: error check? */ addr.sin_port = htons(atoi(c)); } hent = gethostbyname(host); if (!hent) { fprintf(stderr, "No such host: %s\n", host); _exit(1); } memcpy(&addr.sin_addr, hent->h_addr, hent->h_length); return addr; } static int do_rw(int from, int to) { static char buffer[16384]; char *c; long i, j, n; do { n = read(from, buffer, sizeof buffer); } while (n < 0 && errno == EINTR); c = buffer; i = n; while (i > 0) { j = write(to, c, i); if (j < 0) if (errno == EINTR) continue; else return -1; if (j == 0) return 0; c += j; i -= j; } return n; } static int open_serv_socket(struct sockaddr_in *serv_addr) { int ss, tmp; ss = socket(PF_INET, SOCK_STREAM, 0); if (ss < 0) return -1; do { tmp = connect(ss, (struct sockaddr *)serv_addr, sizeof *serv_addr); } while (tmp < 0 && errno == EINTR); if (tmp < 0) { perror("connect"); close(ss); return -1; } return ss; } static int child(struct sockaddr_in *serv_addr, int cs) { fd_set fds; int max, ss, tmp = 0; ss = open_serv_socket(serv_addr); if (ss < 0) return -1; FD_ZERO(&fds); FD_SET(cs, &fds); FD_SET(ss, &fds); max = (cs > ss ? cs : ss) + 1; for (;;) { fd_set rd_fds = fds; tmp = select(max, &rd_fds, NULL, NULL, NULL); if (tmp < 0) if (errno == EINTR) continue; else break; if (FD_ISSET(cs, &rd_fds)) { tmp = do_rw(cs, ss); if (tmp <= 0) break; } if (FD_ISSET(ss, &rd_fds)) { tmp = do_rw(ss, cs); if (tmp <= 0) break; } } if (tmp < 0) perror("read/write"); close(ss); return tmp; } static void doit(struct sockaddr_in *serv_addr, int port, int single) { struct sockaddr_in local_addr, cli_addr; int as; int yes = 1; memset(&local_addr, 0, sizeof local_addr); local_addr.sin_family = PF_INET; local_addr.sin_port = port; as = socket(PF_INET, SOCK_STREAM, 0); if (as < 0) perror_exit("socket"); if (setsockopt(as, SOL_SOCKET, SO_REUSEADDR, (char *)&yes, sizeof yes) < 0) perror_exit("setsockopt(SO_REUSEADDR)"); if (bind(as, (struct sockaddr *)&local_addr, sizeof local_addr) < 0) perror_exit("bind"); if (listen(as, 5) < 0) perror_exit("listen"); for (;;) { int cs, len; do { len = sizeof cli_addr; cs = accept(as, (struct sockaddr *)&cli_addr, &len); } while (cs < 0 && errno == EINTR); if (cs < 0) perror_exit("accept"); if (!is_allowed(&cli_addr)) { static char deny[] = "502 You have no permission to talk!\r\n"; if (serv_addr->sin_port == htons(119)) write(cs, deny, sizeof deny - 1); shutdown(cs, 2); close(cs); continue; } if (single) child(serv_addr, cs); else switch (fork()) { case -1: perror_exit("fork"); break; case 0: if (child(serv_addr, cs) < 0) _exit(1); _exit(0); default: break; } close(cs); } } /************************************************************************/ static void sig_chld(int no) { while (waitpid(-1, NULL, WNOHANG) > 0); } static void install_sig_handlers(void) { struct sigaction sig_act; sig_act.sa_handler = sig_chld; sig_act.sa_flags = 0; sigfillset(&sig_act.sa_mask); if (sigaction(SIGCHLD, &sig_act, NULL) < 0) perror_exit("sigaction(SIGCHLD)"); sig_act.sa_handler = SIG_IGN; sig_act.sa_flags = 0; sigemptyset(&sig_act.sa_mask); if (sigaction(SIGPIPE, &sig_act, NULL) < 0) perror_exit("sigaction(SIGPIPE)"); } static void usage(char *name) { fprintf(stderr, "Usage: %s -p port [-s] -h host [...] remotehost:port\n", name); _exit(1); } int main(int argc, char *argv[]) { struct sockaddr_in serv_addr; int single = False; int port = 0; int c; #ifdef BOGUS_UNISTD extern int getopt(int, char *const[], const char*); extern char *optarg; extern int optind, opterr; #endif freopen("/dev/null", "r", stdin); freopen("/dev/null", "w", stdout); install_sig_handlers(); opterr = 0; while ((c = getopt(argc, argv, "p:h:s")) != -1) switch (c) { case 'p': /* port to listen on */ if (sscanf(optarg, "%d", &port) != 1 || port < 0) usage(argv[0]); port = htons(port); break; case 's': single = True; break; case 'h': allow_host(optarg); break; default: usage(argv[0]); break; } if (optind != argc - 1 || port == 0) usage(argv[0]); if (n_hosts <= 0) { fprintf(stderr, "No allowed hosts!\n"); _exit(1); } serv_addr = get_remote_host(argv[optind]); doit(&serv_addr, port, single); return 0; } ./knews-1.0b.1/util/README100644 1244 1244 20 6455455556 13426 0ustar kallekalleKnews utilities ./knews-1.0b.1/util/knewsd/ 40755 1244 1244 0 6633453651 14004 5ustar kallekalle./knews-1.0b.1/util/knewsd/README100644 1244 1244 1031 6455455556 14764 0ustar kallekalleKnewsd is a program used by knews to read the spool directory. There is really no need to bother with this if you have access to a real server. It's mostly intended for e.g. a small single-user Linux system sucking a small news feed into a local spool directory over a slow modem line. Note: This program only implements exactly thos parts of the NNTP protocol that knews needs, there is no guarantee it will work with different news readers. See the file knewsd.h for compile time configuration, and the Makefile for how to compile. ./knews-1.0b.1/util/knewsd/knewsd.c100644 1244 1244 54311 6572523665 15571 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ #undef _POSIX_SOURCE #define _POSIX_SOURCE 1 #undef _POSIX_SOURCE #define _POSIX_SOURCE 2 #include #include #include #include #include #include #include #include #include #include #include #include "codes.h" #include "knewsd.h" #define IS_SPACE(c) ((c) == ' ' || (c) == '\t') #undef True #define True 1 #undef False #define False 0 static int make_over_rec(int, long, FILE*); static long *arts = NULL; static long n_arts = 0; static long curr_art_ind = -1; static char *group_path = NULL; static int updating_overview = False; #ifndef NEWSGROUPS_FILE # define NEWSGROUPS_FILE 0 #endif #ifndef OVERVIEW_DIR # define OVERVIEW_DIR 0 #endif #ifndef POSTING_AGENT # define POSTING_AGENT 0 #endif static char *spool_dir = SPOOL_DIR; static char *active = ACTIVE_FILE; static char *newsgroups = NEWSGROUPS_FILE; static char *overview_dir = OVERVIEW_DIR; static char *posting_agent = POSTING_AGENT; static int update_overview = False; static pid_t inews_pid; static void sigpipe_handler(int sig) { if (updating_overview && (!overview_dir || chdir(overview_dir) == 0) && chdir(group_path) == 0) unlink(".overview"); _exit(0); } static void handle_sigpipe(void) { struct sigaction sig_act; sig_act.sa_handler = sigpipe_handler; sigemptyset(&sig_act.sa_mask); sig_act.sa_flags = 0; if (sigaction(SIGPIPE, &sig_act, NULL) < 0) perror("knews: sigaction"); } static void ignore_sigpipe(void) { struct sigaction sig_act; sig_act.sa_handler = SIG_IGN; sigemptyset(&sig_act.sa_mask); sig_act.sa_flags = 0; if (sigaction(SIGPIPE, &sig_act, NULL) < 0) perror("knewsd: sigaction"); } static void *xrealloc(void *ptr, size_t n) { if (ptr) /* hack for broken C libraries */ ptr = realloc(ptr, n); else ptr = malloc(n); if (!ptr) { printf(CODE_TO_STR(NNTP_ERR_FAULT) " Malloc failed.\r\n"); exit(0); } return ptr; } static char *snarf_file(int fd) { static char *buf = 0; static long len = 0; struct stat stat_buf; long n; if (fstat(fd, &stat_buf) < 0) { perror("fstat"); return 0; } n = stat_buf.st_size; if (len <= n) buf = xrealloc(buf, len = n + 1); n = read(fd, buf, n); if (n < 0) { perror("read"); free(buf); return 0; } buf[n] = '\0'; return buf; } static int only_digits(char *c) { while ((unsigned)(*c - '0') < 10) c++; return *c == '\0'; } static int long_cmp(const void *c1, const void *c2) { const long *l1 = c1; const long *l2 = c2; if (*l1 < *l2) return -1; if (*l1 > *l2) return 1; return 0; } static long art_search(long *arts, long n_arts, long no) { long i; for (i = 0 ; i < n_arts ; i++) if (arts[i] == no) return i; return -1; } static void get_article(char *args, int head, int body) { char art[32]; char *c; long no; int fd; if (!group_path) { printf(CODE_TO_STR(NNTP_ERR_NCING) " Not in a newsgroup.\r\n"); return; } if (!args) { no = curr_art_ind; if (no < 0) { printf(CODE_TO_STR(NNTP_ERR_NOCRNT) " No current article.\r\n"); return; } } else if (!only_digits(args)) { if (args[0] == '<') printf(CODE_TO_STR(NNTP_ERR_FAULT) " Message-id lookup not implemented.\r\n"); else printf(CODE_TO_STR(NNTP_ERR_CMDSYN) " Syntax error.\r\n"); return; } else if (sscanf(args, "%ld", &no) != 1) { printf(CODE_TO_STR(NNTP_ERR_CMDSYN) " Syntax error.\r\n"); return; } else { no = art_search(arts, n_arts, no); if (no < 0) { printf(CODE_TO_STR(NNTP_ERR_NOART) " No such article.\r\n"); return; } } sprintf(art, "%ld", arts[no]); fd = open(art, O_RDONLY); if (fd < 0) { printf(CODE_TO_STR(NNTP_ERR_NOART) " No such article.\r\n"); return; } c = snarf_file(fd); close(fd); if (!c) { printf(CODE_TO_STR(NNTP_ERR_NOART) " No such article.\r\n"); return; } curr_art_ind = no; no = arts[no]; if (head && body) printf(CODE_TO_STR(NNTP_OK_ARTICLE) " %ld article.\r\n", no); else if (head) printf(CODE_TO_STR(NNTP_OK_HEAD) " %ld head.\r\n", no); else printf(CODE_TO_STR(NNTP_OK_BODY) " %ld body.\r\n", no); if (strncmp(c, "From ", 5) == 0) { char *end = strchr(c, '\n'); if (end) c = end + 1; } if (!body || !head) { char *mid = strstr(c, "\n\n"); if (mid) if (body) c = mid + 2; else mid[1] = '\0'; } do { char *end = strchr(c, '\n'); if (end) *end++ = '\0'; if (*c == '.') putchar('.'); printf("%s\r\n", c); c = end; } while (c); printf(".\r\n"); } static void kill_inews(char *msg) { if (inews_pid == 0) return; fprintf(stderr, "knewsd: %s\n Killing '%s' just to make sure... ", msg, posting_agent); if (kill(inews_pid, SIGKILL) < 0) /* kill that sucker */ perror("kill"); else fprintf(stderr, "Got the sucker!\n"); inews_pid = 0; } static int get_char(void) { int ch = getchar(); if (ch == '\r') ch = getchar(); if (ch == EOF) { kill_inews("Unexpected end-of-file while posting!"); _exit(1); } return ch; } /************************************************************************/ static void do_article(char *args) { get_article(args, True, True); } static void do_body(char *args) { get_article(args, False, True); } static void do_group(char *args) { char path[1024]; DIR *dir; char *p; long n_alloc; struct dirent *dp; curr_art_ind = -1; if (!args) { printf(CODE_TO_STR(NNTP_ERR_FAULT) " No newsgroup specified.\r\n"); return; } if (strlen(args) > 512) { printf(CODE_TO_STR(NNTP_ERR_FAULT) " Group name too long.\r\n"); return; } strcpy(path, spool_dir); p = path + strlen(path); *p++ = '/'; strcpy(p, args); while (*p != '\0') if (*p == '.') *p++ = '/'; else p++; if (chdir(path) < 0 || !(dir = opendir("."))) { perror(path); printf(CODE_TO_STR(NNTP_ERR_NOGROUP) " No such newsgroup.\r\n"); return; } n_alloc = n_arts; group_path = xrealloc(group_path, strlen(args) + 1); strcpy(group_path, args); for (p = strchr(group_path, '.') ; p ; p = strchr(p + 1, '.')) *p = '/'; n_arts = 0; while ( (dp = readdir(dir)) ) if (only_digits(dp->d_name)) { if (n_arts > n_alloc - 2) { n_alloc = 2 * (n_alloc + 1); arts = xrealloc(arts, n_alloc * sizeof(long)); } arts[n_arts++] = atol(dp->d_name); } closedir(dir); if (n_arts > 0) { qsort(arts, n_arts, sizeof(long), long_cmp); curr_art_ind = 0; } printf(CODE_TO_STR(NNTP_OK_GROUP) " %ld %ld %ld.\r\n", n_arts + 7, n_arts > 0 ? arts[0] : 0l, n_arts > 0 ? arts[n_arts - 1] : 0l); } static void do_head(char *args) { get_article(args, True, False); } static void do_help(char *args) { printf(CODE_TO_STR(NNTP_INF_HELP) " Legal commands\r\n" " article [Number]\r\n" " body [Number]\r\n" " group newsgroup\r\n" " head [Number]\r\n" " help\r\n" " list [active|newsgroups]\r\n" " next\r\n" " post\r\n" " quit\r\n" " stat [Number]\r\n" " xover [range]\r\n" ".\r\n"); } static void do_list(char *args) { FILE *fp = NULL; int c, prev; if (!args || strcmp(args, "active") == 0) { if (!active) { printf(CODE_TO_STR(NNTP_ERR_FAULT) " Active file not available.\r\n"); return; } fp = fopen(active, "r"); if (fp) printf(CODE_TO_STR(NNTP_OK_GROUPS) " Active file follows.\r\n"); else perror(active); } else if (strcmp(args, "newsgroups") == 0) { if (!newsgroups) { printf(CODE_TO_STR(NNTP_ERR_FAULT) " Newsgroups file not available.\r\n"); return; } fp = fopen(newsgroups, "r"); if (fp) printf(CODE_TO_STR(NNTP_OK_GROUPS) " Descriptions follow.\r\n"); else perror(newsgroups); } else { printf(CODE_TO_STR(NNTP_ERR_CMDSYN) " Syntax error.\r\n"); return; } if (!fp) { printf(CODE_TO_STR(NNTP_ERR_FAULT) " Couldn't open file.\r\n"); return; } prev = 0; while ((c = getc(fp)) != EOF) { prev = c; if (c == '\n') putchar('\r'); putchar(c); } if (prev == '\n') printf(".\r\n"); else printf("\r\n.\r\n"); fclose(fp); } static void do_next(char *args) { if (!group_path) { printf(CODE_TO_STR(NNTP_ERR_NCING) " Not in a newsgroup.\r\n"); return; } if (curr_art_ind < 0) { printf(CODE_TO_STR(NNTP_ERR_NOCRNT) " No current article.\r\n"); return; } if (curr_art_ind >= n_arts - 1) { printf(CODE_TO_STR(NNTP_ERR_NONEXT) " No next article.\r\n"); return; } curr_art_ind++; printf(CODE_TO_STR(NNTP_OK_NOTEXT) " %ld\r\n", arts[curr_art_ind]); } static void do_not_impl(char *args) { printf(CODE_TO_STR(NNTP_ERR_FAULT) " Command not implemented.\r\n"); } static void do_post(char *args) { int fd[2]; FILE *fp; int bol; int status, temp; if (!posting_agent) { printf(CODE_TO_STR(NNTP_ERR_NOPOST) " knewsd not configured for posting.\r\n"); return; } if (pipe(fd) < 0) { perror("knewsd: pipe"); printf(CODE_TO_STR(NNTP_ERR_FAULT) " pipe failed.\r\n"); return; } inews_pid = fork(); if (inews_pid < 0) { perror("knewsd: fork"); printf(CODE_TO_STR(NNTP_ERR_FAULT) " fork failed.\r\n"); close(fd[0]); close(fd[1]); return; } if (inews_pid == 0) { /* child */ close(fd[1]); if (fd[0] != STDIN_FILENO) { if (dup2(fd[0], STDIN_FILENO) != STDIN_FILENO) { perror("knewsd: dup2"); _exit(126); } close(fd[0]); } if (dup2(STDOUT_FILENO, STDERR_FILENO) != STDERR_FILENO) { perror("knews: dup2"); _exit(126); } execl("/bin/sh", "sh", "-c", posting_agent, (char *)0); perror("knewsd: execl"); _exit(127); } /* parent */ close(fd[0]); fp = fdopen(fd[1], "w"); if (!fp) { perror("knewsd: fdopen"); close(fd[1]); printf(CODE_TO_STR(NNTP_ERR_FAULT) " Internal error.\r\n"); kill_inews("Internal error while posting!"); return; } printf(CODE_TO_STR(NNTP_CONT_POST) " Ok.\r\n"); fflush(stdout); ignore_sigpipe(); bol = True; for (;;) { int ch = get_char(); if (bol && ch == '.') { ch = get_char(); if (ch == '\n') break; putc('.', fp); } putc(ch, fp); bol = (ch == '\n'); } fclose(fp); handle_sigpipe(); do { temp = waitpid(inews_pid, &status, 0); } while (temp < 0 && errno == EINTR); if (temp < 0) { perror("knewsd: waitpid"); printf(CODE_TO_STR(NNTP_ERR_FAULT) " Internal error.\r\n"); kill_inews("Internal error while posting!"); return; } if (WIFEXITED(status)) switch (WEXITSTATUS(status)) { case 0: printf(CODE_TO_STR(NNTP_OK_POSTED) " Article posted.\r\n"); break; case 126: printf(CODE_TO_STR(NNTP_ERR_POSTFAIL) " Internal error.\r\n"); break; case 127: printf(CODE_TO_STR(NNTP_ERR_POSTFAIL) " Failed to start %s.\r\n", posting_agent); break; default: printf(CODE_TO_STR(NNTP_ERR_POSTFAIL) " %s didn't accept your article.\r\n", posting_agent); break; } else if (WIFSIGNALED(status)) printf(CODE_TO_STR(NNTP_ERR_FAULT) " %s caught signal %d.\r\n", posting_agent, WTERMSIG(status)); else printf(CODE_TO_STR(NNTP_ERR_FAULT) " %s terminated abnormally: " "beats the hell out of me.\r\n", posting_agent); } static void do_stat(char *args) { long no; if (!group_path) { printf(CODE_TO_STR(NNTP_ERR_NCING) " Not in a newsgroup.\r\n"); return; } if (!args || sscanf(args, "%ld", &no) != 1) { printf(CODE_TO_STR(NNTP_ERR_CMDSYN) " Syntax error.\r\n"); return; } no = art_search(arts, n_arts, no); if (no < 0) { printf(CODE_TO_STR(NNTP_ERR_NOART) " No such article.\r\n"); return; } curr_art_ind = no; printf(CODE_TO_STR(NNTP_OK_NOTEXT) " %ld\r\n", arts[no]); } static void do_quit(char *args) { printf(CODE_TO_STR(NNTP_OK_GOODBYE) " Goodbye.\r\n"); exit(0); } static void do_xover(char *args) { char buffer[1024]; FILE *fp = NULL; long first, last, no; char *c; if (!group_path) { printf(CODE_TO_STR(NNTP_ERR_NCING) " Not in a newsgroup.\r\n"); return; } if (!args) { printf(CODE_TO_STR(NNTP_ERR_NOCRNT) " XOVER can't handle current article.\r\n"); return; } c = strchr(args, '-'); if (c) { *c++ = '\0'; if (sscanf(args, "%ld", &first) != 1 || sscanf(c, "%ld", &last) != 1) { printf(CODE_TO_STR(NNTP_ERR_CMDSYN) " Syntax error.\r\n"); return; } } else { if (sscanf(args, "%ld", &first) != 1) { printf(CODE_TO_STR(NNTP_ERR_CMDSYN) " Syntax error.\r\n"); return; } last = first; } if (overview_dir) sprintf(buffer, "%s/%s/.overview", overview_dir, group_path); else sprintf(buffer, ".overview"); fp = fopen(buffer, "r"); if (!fp) perror(buffer); printf(CODE_TO_STR(NNTP_OK_XOVER) " Overview file follows.\r\n"); no = 0; if (fp) { while (fgets(buffer, sizeof(buffer), fp)) { if (buffer[0] < '0' || buffer[0] > '9' || (no = atol(buffer)) < first) { if (!strchr(buffer, '\n')) { int ch; do { ch = getc(fp); } while (ch != EOF && ch != '\n'); if (ch == EOF) break; } continue; } if (no > last) break; fputs(buffer, stdout); if (!strchr(buffer, '\n')) { int ch; while ((ch = getc(fp)) != EOF && ch != '\n') putchar(ch); printf("\r\n"); } } fclose(fp); fp = NULL; } if (no < last) { long n = 0; if (no < first) no = first; else if (update_overview) { if (overview_dir) sprintf(buffer, "%s/%s/.overview", overview_dir, group_path); else sprintf(buffer, ".overview"); fp = fopen(buffer, "a"); if (fp) updating_overview = True; else perror(".overview"); } while (n < n_arts && arts[n] < no) n++; while (n < n_arts && arts[n] <= last) { int fd; sprintf(buffer, "%ld", arts[n]); fd = open(buffer, O_RDONLY); if (fd < 0) perror("open"); else { make_over_rec(fd, arts[n], fp); close(fd); } n++; } updating_overview = False; if (fp) fclose(fp); } printf(".\r\n"); } /*************************************************************************/ int main(int argc, char **argv) { #define N_COMMANDS (sizeof commands / sizeof commands[0]) static struct { char *name; void (*proc)(char*); } commands[] = { {"article", do_article}, {"authinfo", do_not_impl}, {"body", do_body}, {"date", do_not_impl}, {"group", do_group}, {"head", do_head}, {"help", do_help}, {"ihave", do_not_impl}, {"last", do_not_impl}, {"list", do_list}, {"listgroup", do_not_impl}, {"next", do_next}, {"newgroups", do_not_impl}, {"newnews", do_not_impl}, {"post", do_post}, {"quit", do_quit}, {"sendme", do_not_impl}, {"slave", do_not_impl}, {"stat", do_stat}, {"xgtitle", do_not_impl}, {"xhdr", do_not_impl}, {"xover", do_xover}, {"xpat", do_not_impl}, {"xpath", do_not_impl}, }; char command[512]; handle_sigpipe(); while (--argc > 0 && **++argv == '-') { int n = strlen(*argv) - 1; #define IS_OPTION(o, has_arg) \ (strncmp(*argv, o, n) == 0 && \ n < sizeof(o) && (!has_arg || argc > 0)) switch (argv[0][1]) { case 's': if (IS_OPTION("-spool", True)) { spool_dir = *++argv; argc--; continue; } break; case 'a': if (IS_OPTION("-active", True)) { active = *++argv; argc--; continue; } break; case 'n': if (IS_OPTION("-newsgroups", True)) { newsgroups = *++argv; argc--; continue; } break; case 'o': if (IS_OPTION("-overview", True)) { overview_dir = *++argv; argc--; continue; } break; case 'p': if (IS_OPTION("-postingagent", True)) { posting_agent = *++argv; argc--; continue; } break; case 'u': if (IS_OPTION("-update", False)) { update_overview = True; continue; } break; default: break; } #undef IS_OPTION break; } if (argc != 0) { printf(CODE_TO_STR(NNTP_ERR_FAULT) " Bad command line arguments.\r\n"); exit(1); } if (strlen(spool_dir) > 480) { printf(CODE_TO_STR(NNTP_ERR_FAULT) " Spool dir name to long.\r\n"); exit(1); } if (overview_dir && strlen(overview_dir) > 480) { printf(CODE_TO_STR(NNTP_ERR_FAULT) " Overview dir name to long.\r\n"); exit(1); } if (chdir(spool_dir) < 0) { perror(spool_dir); printf(CODE_TO_STR(NNTP_ERR_FAULT) " Couldn't chdir to spool dir.\r\n"); exit(1); } printf("%d knews, copyright 1995, 1996 Karl-Johan Johnsson, %s.\r\n", posting_agent ? NNTP_OK_CANPOST : NNTP_OK_NOPOST, posting_agent ? "posting ok" : "no posting"); if (posting_agent && !strstr(posting_agent, "exec ")) fprintf(stderr, "Warning: posting_agent should be execed!\n"); for (;;) { char *c, *arg; int i; fflush(stdout); if (!fgets(command, sizeof command, stdin)) break; c = strchr(command, '\n'); if (c) { *c = '\0'; if (*--c == '\r') *c = '\0'; } else { int ch; printf(CODE_TO_STR(NNTP_ERR_FAULT) " Input buffer overflow.\r\n"); do { ch = getchar(); } while (ch != EOF && ch != '\n'); if (ch == EOF) break; else continue; } for (c = command, arg = NULL ; *c != '\0' ; c++) if (*c >= 'A' && *c <= 'Z') *c -= 'A' - 'a'; else if (*c == ' ') { *c = '\0'; if (!arg) arg = c + 1; } for (i = 0 ; i < N_COMMANDS ; i++) if (command[0] == commands[i].name[0] && strcmp(command, commands[i].name) == 0) { commands[i].proc(arg); break; } if (i == N_COMMANDS) printf(CODE_TO_STR(NNTP_ERR_COMMAND) " Command not recognized.\r\n"); } return 0; } /*************************************************************************/ static char *find_lflf(char *c) { for (c = strchr(c, '\n') ; c ; c = strchr(c, '\n')) if (*++c == '\n') return c; return NULL; } static char *get_header(int fd) { static char buffer[16384 + 3]; char *c, *p; long n; n = sizeof(buffer); c = buffer; do { long i = read(fd, c, 1024); if (i <= 0) { if (i < 0) perror("read"); return NULL; } c[i] = '\0'; c += i; n -= i; if ( (p = find_lflf(buffer)) ) { *p = '\0'; return buffer; } } while (n > 0); return NULL; } static char *hack_inreplyto(char *inreplyto) { char *c = strchr(inreplyto, '<'); char *p = c; if (!c) return NULL; for (;;) switch (*p++) { case '>': *p = '\0'; if (!strchr(c, '@')) return NULL; return c; case '\0': case '\t': case ' ': return NULL; } /* not reached */ } static char *stat_bytes(int fd) { static char result[32]; struct stat st; if (fstat(fd, &st) < 0) return ""; sprintf(result, "%ld", (long)st.st_size); return result; } #define ISUPPER(c) \ ((unsigned int)((c) - 'A') <= 'Z' - 'A') #define LOWER(u) \ ((unsigned char)(u) + ('a' - 'A')) #define TOLOWER(c) \ (ISUPPER(c) ? LOWER(c) : (unsigned char)(c)) static int case_lstrncmp(const char *c1, const char *c2, long n) { while (n--) { int tmp = TOLOWER(*c1) - (unsigned char)*c2; if (tmp != 0) return tmp; if (*c1 == '\0') return 0; c1++; c2++; } return 0; } static int make_over_rec(int fd, long no, FILE *fp) { register char *header; char *subject = ""; char *from = ""; char *date = ""; char *messageid = NULL; char *refs = NULL; char *bytes = NULL; char *lines = ""; char *xref = NULL; char *inreplyto = NULL; header = get_header(fd); if (!header) return -1; while (*header != '\0') { unsigned char ch = *header; int skip = True; switch (TOLOWER(ch)) { case 'b': if (case_lstrncmp(header, "bytes:", 6) == 0) { header += 6; while (IS_SPACE(*header)) header++; bytes = header; skip = False; } break; case 'd': if (case_lstrncmp(header, "date:", 5) == 0) { header += 5; while (IS_SPACE(*header)) header++; date = header; skip = False; } break; case 'f': if (case_lstrncmp(header, "from:", 5) == 0) { header += 5; while (IS_SPACE(*header)) header++; from = header; skip = False; } break; case 'i': if (case_lstrncmp(header, "in-reply-to:", 12) == 0) { header += 12; while (IS_SPACE(*header)) header++; inreplyto = header; skip = False; } break; case 'l': if (case_lstrncmp(header, "lines:", 6) == 0) { header += 6; while (IS_SPACE(*header)) header++; lines = header; skip = False; } break; case 'm': if (case_lstrncmp(header, "message-id:", 11) == 0) { header += 11; while (IS_SPACE(*header)) header++; messageid = header; skip = False; } break; case 'r': if (case_lstrncmp(header, "references:", 11) == 0) { header += 11; while (IS_SPACE(*header)) header++; refs = header; skip = False; } break; case 's': if (case_lstrncmp(header, "subject:", 8) == 0) { header += 8; while (IS_SPACE(*header)) header++; subject = header; skip = False; } break; case 'x': if (case_lstrncmp(header, "xref:", 5) == 0) { xref = header; skip = False; } break; } if (skip) { header = strchr(header, '\n'); while (header && IS_SPACE(header[1])) header = strchr(header + 1, '\n'); if (!header) break; header++; } else { char *src, *dest; src = strchr(header, '\n'); if (!src) break; dest = src; for (;;) { if (*src == '\0') break; else if (*src == '\n') { src++; if (!IS_SPACE(*src)) break; *dest++ = ' '; } else if (*src == '\t') { src++; *dest++ = ' '; } else { *dest++ = *src++; } } *dest = '\0'; header = src; } } if (!messageid) { fprintf(stderr, "%ld: no message-id, skipping.\n", no); return -1; } if (!refs && inreplyto) refs = hack_inreplyto(inreplyto); if (!refs) refs = ""; if (!bytes) bytes = stat_bytes(fd); printf("%ld\t%s\t%s\t%s\t%s\t%s\t%s\t%s", no, subject, from, date, messageid, refs, bytes, lines); if (xref) printf("\t%s", xref); putchar('\n'); if (fp) { fprintf(fp, "%ld\t%s\t%s\t%s\t%s\t%s\t%s\t%s", no, subject, from, date, messageid, refs, bytes, lines); if (xref) fprintf(fp, "\t%s", xref); putc('\n', fp); } return 0; } ./knews-1.0b.1/util/knewsd/knewsd.h100644 1244 1244 1035 6455455556 15554 0ustar kallekalle/* * Copyright (C) 1995, 1996 Karl-Johan Johnsson. */ /* * Configuration: all of these can set at runtime instead */ /* This is mandatory */ #define SPOOL_DIR "/var/spool/news" /* This is advisable */ #define ACTIVE_FILE "/usr/local/news/active" /* Entirely voluntary */ /* #define NEWSGROUPS_FILE "/usr/local/news/newsgroups"*/ /* May be necessary */ /* #define OVERVIEW_DIR "/var/spool/news/over.view"*/ /* Define this to be able to post. SHOULD be an exec'ed shell command. */ /* #define POSTING_AGENT "exec inews -h"*/ ./knews-1.0b.1/util/knewsd/knewsd.man100644 1244 1244 5255 6455455556 16110 0ustar kallekalle.TH KNEWSD 1 "1996" .SH NAME knewsd \- a tiny nntp demon .SH SYNOPSIS .B knewsd [ .B options ] .SH DESCRIPTION Knewsd is intended to be used with knews(1) to read news (in)directly from the spool directory or even from a mail folder hierarchy. It reads NNTP commands from standard input and writes replies to standard output. The following commands are implemented: .nf .B ARTICLE [number] .B BODY [number] .B HEAD [number] .fi .RS Return the head and/or body of the current or the specified article. Message-id lookups are not supported. .RE .B GROUP newsgroup .RS Enters the given newsgroup. .RE .B HELP .RS Gives a list of the implemented commands. .RE .B LIST [active | newsgroups] .RS Lists the active file or the newsgroups file. .RE .B NEXT .RS Changes the current article pointer to the next article .RE .B POST .RS Post an article via the specified posting agent. If none has been specified, the reply will be '440 Posting not implemented.' .RE .B QUIT .RS Makes knewsd exit. .RE .B STAT number .RS Sets the current article pointer. .RE .B XOVER range .RS Returns the overview data for the specified range of articles. .RE Knewsd implements the 'xover' command either by using the overview files if present, or creating overview records on the fly, or a combination of both if the overview file is incomplete. In the latter case, if the \-update flag is given, knewsd will update the overview files. .SH OPTIONS There is no need to spell out the entire option name, it is sufficient to give a unique prefix, such as \-a for \-active. .TP .B \-spool directory This is the spool directory. It must be specified, either at compile time or at run time. .TP .B \-active file This is the active file containing the newsgroups and the high and low article counts. If this is not specifed, knews must be told not to try to read the active file. .TP .B \-newsgroups file This is the newsgroups file containing the group descriptions. It's entirely optional. .TP .B \-overview dir This can be used if the overview files reside in a different directory hierarchy than the spool directory. .TP .B \-update If this is set, knewsd will (try to) update any incomplete overview files it encounters. Not a good idea if you're reading a system wide spool directory. .TP .B \-postingagent agent If this is set, the 'agent' will be used to post articles. A possible agent is 'exec inews -h'. This command is interpreted by the shell, and it is important that it is exec'ed, so that knewsd may kill it on a broken pipe to prevent posting of duplicate or truncated articles. Knewsd will warn about this on startup. .SH AUTHOR This software is Copyright 1995, 1996 by Karl-Johan Johnsson. .SH "SEE ALSO" knews(1), inews(1) ./knews-1.0b.1/util/knewsd/codes.h100644 1244 1244 4535 6455455556 15366 0ustar kallekalle#define STRINGIFY(a) #a #define CODE_TO_STR(a) (a - a) + STRINGIFY(a) #define NNTP_INF_HELP 100 /* Help text on way */ #define NNTP_INF_DEBUG 199 /* Debug output */ #define NNTP_OK_CANPOST 200 /* Hello; you can post */ #define NNTP_OK_NOPOST 201 /* Hello; you can't post */ #define NNTP_OK_SLAVE 202 /* Slave status noted */ #define NNTP_OK_GOODBYE 205 /* Closing connection */ #define NNTP_OK_GROUP 211 /* Group selected */ #define NNTP_OK_GROUPS 215 /* Newsgroups follow */ #define NNTP_OK_ARTICLE 220 /* Article (head & body) follows */ #define NNTP_OK_HEAD 221 /* Head follows */ #define NNTP_OK_BODY 222 /* Body follows */ #define NNTP_OK_NOTEXT 223 /* No text sent -- stat, next, last */ #define NNTP_OK_XOVER 224 /* XOVER OK */ #define NNTP_OK_NEWNEWS 230 /* New articles by message-id follow */ #define NNTP_OK_NEWGROUPS 231 /* New newsgroups follow */ #define NNTP_OK_XFERED 235 /* Article transferred successfully */ #define NNTP_OK_POSTED 240 /* Article posted successfully */ #define NNTP_OK_AUTH 281 /* Authentication accepted */ #define NNTP_CONT_XFER 335 /* Continue to send article */ #define NNTP_CONT_POST 340 /* Continue to post article */ #define NNTP_CONT_AUTH 381 /* More authentication needed */ #define NNTP_ERR_GOODBYE 400 /* Have to hang up for some reason */ #define NNTP_ERR_NOGROUP 411 /* No such newsgroup */ #define NNTP_ERR_NCING 412 /* Not currently in newsgroup */ #define NNTP_ERR_NOCRNT 420 /* No current article selected */ #define NNTP_ERR_NONEXT 421 /* No next article in this group */ #define NNTP_ERR_NOPREV 422 /* No previous article in this group */ #define NNTP_ERR_NOARTIG 423 /* No such article in this group */ #define NNTP_ERR_NOART 430 /* No such article at all */ #define NNTP_ERR_GOTIT 435 /* Already got that article */ #define NNTP_ERR_XFERFAIL 436 /* Transfer failed */ #define NNTP_ERR_XFERRJCT 437 /* Article rejected, don't resend */ #define NNTP_ERR_NOPOST 440 /* Posting not allowed */ #define NNTP_ERR_POSTFAIL 441 /* Posting failed */ #define NNTP_ERR_NEED_AUTH 480 /* Authentication required */ #define NNTP_ERR_FAIL_AUTH 481 /* Authentication rejected */ #define NNTP_ERR_COMMAND 500 /* Command not recognized */ #define NNTP_ERR_CMDSYN 501 /* Command syntax error */ #define NNTP_ERR_ACCESS 502 /* Access to server denied */ #define NNTP_ERR_FAULT 503 /* Program fault, cpmd not performed */ ./knews-1.0b.1/util/knewsd/Imakefile100644 1244 1244 71 6455455556 15660 0ustar kallekalle#include "../../knews.tmpl" SimpleProgramTarget(knewsd) ./knews-1.0b.1/COPYRIGHT100644 1244 1244 1330 6455455556 13131 0ustar kallekalleCopyright (C) 1995,1996 Karl-Johan Johnsson 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., 675 Mass Ave, Cambridge, MA 02139, USA.