slrnface-2.1.1/0040755002170200001450000000000007532000406012613 5ustar davearsdigitslrnface-2.1.1/COPYING0100644002170200001450000004310507437520624013663 0ustar davearsdigit GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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. slrnface-2.1.1/Makefile0100644002170200001450000000146407532000405014254 0ustar davearsdigitCC = cc X_INCL = # location of X include files, if needed # A lot of popular terminals are brain damaged, so we cannot use X # resources for configuration. In those cases Xt routines won't even be # called, so you might want to use lazy loading for Xt if your OS supports it. X_LIBS = -lXt -lX11 # X libs XF_LIB = -L/usr/local/lib -lcompface # compface library C_FLAGS = -O prefix = /usr/local # No need to configure anything below. CFLAGS = $(C_FLAGS) $(X_INCL) LDFLAGS = $(XF_LIB) $(X_LIBS) slrnface: slrnface.c $(CC) $(CFLAGS) -o slrnface slrnface.c $(LDFLAGS) clean: rm -f *.o core slrnface install: slrnface mkdir -p $(prefix)/bin mkdir -p $(prefix)/man/man1 mkdir -p $(prefix)/share/slrn/macros cp slrnface $(prefix)/bin cp slrnface.1 $(prefix)/man/man1 cp slrnface.sl $(prefix)/share/slrn/macros slrnface-2.1.1/README0100644002170200001450000000474307531551543013514 0ustar davearsdigitThis is slrnface, a small helper utility which displays X-Faces on behalf of programs like slrn and mutt when they are run in the X11 terminal emulator. Requirements: a) X11. b) Terminal emulator which sets WINDOWID environment variable. Most of the ones in common use will set it. If you're using some terminal emulator which doesn't have this capability, you'll have to set it yourself. How to build: a) Install libcompface. I recommend getting the source from the nearest Debian GNU/Linux mirror because there's a patch which adds the ability to output XBM image, as well as the historic brain dead format. Slrnface doesn't use that feature, but it might be handy for something else. b) Edit Makefile. c) Invoke make. d) Install with "make install". Upgrading from version 1.x: If you have used one of the older versions of slrnface, you probably have .slrnface file in your home directory. You can delete it, because the new version doesn't use it any more. Instead, the pipes will be created in $HOME/.slrnfaces directory. There are no other user visible changes. Using slrnface with slrn: This version requires S-Lang 1.4 or later and slrn 0.9.7.4. It might work with slrn 0.9.7.3, but I haven't tested that configuration. It will not work properly with older versions. In case you can't or won't upgrade, take a look at the slrnface home page. You'll find older versions which might work with what you have. Take a look at slrnface.sl, edit if you want and then include it in your .slrnrc, like this: interpret slrnface.sl Run slrn. Using slrnface with mutt: Mutt doesn't have a way to use some kind of embeded interpreter, like S-Lang. Since Mutt's current capabilities are not good enough for our purposes, a patch for the source is provided. It has been tested with mutt 1.4, but it should work with the 1.3.x series, if x is high enough. Uncompress mutt 1.4 source and apply mutt.patch from the slrnface distribution. Compile mutt as usual. Edit your ~/.muttrc and add: set xface=yes Run mutt. If you think X-Faces are not placed at the appropriate location on your terminal emulator window or you don't like the colors, set some X resources. Read the man page for more information. Additional documentation is in the doc directory. Licence: GPL. See file called COPYING. Special thanks to Mark R. Bowyer for proofreading the man page. Home page: http://dave.willfork.com/slrnface/ dave@willfork.com slrnface-2.1.1/mutt.patch0100644002170200001450000002612107531776733014652 0ustar davearsdigit--- globals.h.orig Sat Aug 24 10:10:36 2002 +++ globals.h Thu May 30 23:13:46 2002 @@ -171,6 +171,8 @@ WHERE ALIAS *Aliases INITVAL (0); WHERE LIST *UserHeader INITVAL (0); +WHERE int slrnface_fd INITVAL (-1); + #ifdef DEBUG WHERE FILE *debugfile INITVAL (0); WHERE int debuglevel INITVAL (0); --- init.c.orig Mon Feb 11 10:58:54 2002 +++ init.c Sat Aug 24 07:28:13 2002 @@ -47,6 +47,8 @@ #include #include #include +#include +#include #include void toggle_quadoption (int opt) @@ -1966,4 +1968,132 @@ if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0) return c->data; return 0; +} + +void mutt_start_slrnface(void) +{ + char *fifo; + int pathlen, status; + pid_t pid, pidst; + struct utsname u; + + if (!option(OPTXFACE)) + return; + + /* + * If we don't have display, there's no point. The user probably knows, + * so fail silently. + */ + if (!getenv("DISPLAY")) + return; + /* If there is no WINDOWID, complain. */ + if (!getenv ("WINDOWID")) + { + mutt_error (_("Cannot run slrnface: WINDOWID not found in environment.")); + return; + } + + uname (&u); + pathlen = strlen (Homedir) + sizeof("/.slrnfaces/") + + strlen (u.nodename) + 30; + fifo = safe_malloc (pathlen); + sprintf (fifo, "%s/.slrnfaces", Homedir); + if (mkdir (fifo, 0700)) + { + if (errno != EEXIST) + { + mutt_error (_("Cannot run slrnface: failed to create %s: %s."), + fifo, strerror(errno)); + return; + } + } + else + { + FILE *fp; + + /* We'll abuse fifo filename memory here. It's long enough. */ + sprintf (fifo, "%s/.slrnfaces/README", Homedir); + if ((fp = fopen (fifo, "w")) != NULL) + { + fputs (_( +"This directory is used to create named pipes for communication between\n" +"slrnface and its parent process. It should normally be empty because\n" +"the pipe is deleted right after it has been opened by both processes.\n\n" +"File names generated by slrnface have the form \"hostname.pid\". It is\n" +"probably an error if they linger here longer than a fraction of a second.\n\n" +"However, if the directory is mounted from an NFS server, you might see\n" +"special files created by your NFS server while slrnface is running.\n" +"Do not try to remove them.\n"), fp); + fclose (fp); + } + } + + status = snprintf (fifo, pathlen, "%s/.slrnfaces/%s.%ld", Homedir, + u.nodename, (long)getpid()); + if (status < 0) + goto clean_face; + + unlink (fifo); + if (mkfifo (fifo, 0600) < 0) + { + mutt_error (_("Cannot run slrnface, failed to create %s: %s."), fifo, + strerror(errno)); + goto clean_face; + } + + pid = fork(); + switch (pid) + { + case -1: break; + case 0: execlp ("slrnface", "slrnface", fifo, (char *)0); + /* This is child, exit on error. */ + _exit (10); + default: do { + pidst = waitpid (pid, &status, 0); + } while (pidst == -1 && errno == EINTR); + + if (!WIFEXITED (status)) + mutt_error (_("Slrnface abnormaly exited, code %d."), status); + else + { + char *message; + + switch (WEXITSTATUS (status)) + { + case 0: /* All fine, open the pipe */ + slrnface_fd = open (fifo, O_WRONLY, 0600); + write (slrnface_fd, "start\n", sizeof "start"); + goto clean_face; + case 1: message = "couldn't connect to display"; + break; + case 2: message = "WINDOWID not found in environment"; + break; + case 3: message = "couldn't find controlling terminal"; + break; + case 4: message = "terminal doesn't export width and height"; + break; + case 5: message = "cannot open FIFO"; + break; + case 6: message = "fork() failed"; + break; + case 10: message = "executable not found"; + break; + default: message = "unknown error"; + } + mutt_error (_("Slrnface failed: %s."), message); + } + } + +clean_face: + unlink (fifo); + free (fifo); +} + +void mutt_stop_slrnface(void) +{ + if (slrnface_fd >= 0) + close(slrnface_fd); + slrnface_fd = -1; + + /* FIFO has been unlinked in the startup function. */ } --- init.h.orig Thu Apr 25 15:26:37 2002 +++ init.h Thu May 30 23:13:46 2002 @@ -2366,6 +2366,12 @@ ** Controls whether mutt writes out the Bcc header when preparing ** messages to be sent. Exim users may wish to use this. */ + { "xface", DT_BOOL, R_NONE, OPTXFACE, 0 }, + /* + ** .pp + ** Controls whether mutt uses slrnface to display X-Faces when run + ** in an X11 terminal emulator. + */ /*--*/ { NULL } }; --- main.c.orig Sat Aug 24 10:11:53 2002 +++ main.c Thu May 30 23:13:46 2002 @@ -835,6 +835,8 @@ mutt_folder_hook (folder); + mutt_start_slrnface(); + if((Context = mx_open_mailbox (folder, ((flags & M_RO) || option (OPTREADONLY)) ? M_READONLY : 0, NULL)) || !explicit_folder) { @@ -843,6 +845,8 @@ safe_free ((void **)&Context); } mutt_endwin (Errorbuf); + + mutt_stop_slrnface(); } exit (0); --- mutt.h.orig Sat Aug 24 10:12:08 2002 +++ mutt.h Thu May 30 23:13:46 2002 @@ -410,6 +410,7 @@ OPTWRAP, OPTWRAPSEARCH, OPTWRITEBCC, /* write out a bcc header? */ + OPTXFACE, OPTXMAILER, /* PGP options */ @@ -526,6 +527,7 @@ char *x_label; LIST *references; /* message references (in reverse order) */ LIST *in_reply_to; /* in-reply-to header content */ + LIST *x_face; /* X-Face header content */ LIST *userhdrs; /* user defined headers */ } ENVELOPE; --- muttlib.c.orig Sat Aug 24 10:12:24 2002 +++ muttlib.c Thu May 30 23:13:47 2002 @@ -650,6 +650,7 @@ safe_free ((void **) &(*p)->date); mutt_free_list (&(*p)->references); mutt_free_list (&(*p)->in_reply_to); + mutt_free_list (&(*p)->x_face); mutt_free_list (&(*p)->userhdrs); safe_free ((void **) p); } --- pager.c.orig Sat Aug 24 10:12:57 2002 +++ pager.c Sat Aug 24 23:39:17 2002 @@ -1431,6 +1431,66 @@ return cur; } +static void +mutt_display_xface (HEADER *hdr) +{ + LIST *face; + char buf[2000]; + + if (slrnface_fd < 0) + return; + + if (!hdr) + return; + + face = hdr->env->x_face; + + if (face == NULL || face->data == NULL) + write(slrnface_fd, "clear\n", sizeof "clear"); + else + do { + int len; + + len = snprintf (buf, sizeof (buf), "xface %s\n", face->data); + if (len <= sizeof (buf)) + { + write (slrnface_fd, buf, len); + break; + } + /* + * slrnface will ignore X-Faces larger than approx. 2000 chars, so + * try the next one, if it exists. + */ + } while (face = face->next); +} + +static void +mutt_clear_xface (void) +{ + if (slrnface_fd < 0) + return; + + write(slrnface_fd, "clear\n", sizeof "clear"); +} + +static void +mutt_suppress_xface (void) +{ + if (slrnface_fd < 0) + return; + + write(slrnface_fd, "suppress\n", sizeof "suppress"); +} + +static void +mutt_show_xface (void) +{ + if (slrnface_fd < 0) + return; + + write(slrnface_fd, "show\n", sizeof "show"); +} + static struct mapping_t PagerHelp[] = { { N_("Exit"), OP_EXIT }, { N_("PrevPg"), OP_PREV_PAGE }, @@ -1535,6 +1595,9 @@ snprintf (helpstr, sizeof (helpstr), "%s %s", tmphelp, buffer); } + if (IsHeader (extra)) + mutt_display_xface(extra->hdr); + while (ch != -1) { mutt_curs_set (0); @@ -2035,7 +2098,9 @@ if (! InHelp) { InHelp = 1; + mutt_suppress_xface (); mutt_help (MENU_PAGER); + mutt_show_xface (); redraw = REDRAW_FULL; InHelp = 0; } @@ -2327,59 +2392,71 @@ case OP_MAIL: CHECK_MODE(IsHeader (extra) && !IsAttach (extra)); CHECK_ATTACH; + mutt_suppress_xface (); ci_send_message (0, NULL, NULL, NULL, NULL); + mutt_show_xface (); redraw = REDRAW_FULL; break; case OP_REPLY: CHECK_MODE(IsHeader (extra) || IsMsgAttach (extra)); CHECK_ATTACH; + mutt_suppress_xface(); if (IsMsgAttach (extra)) mutt_attach_reply (extra->fp, extra->hdr, extra->idx, extra->idxlen, extra->bdy, SENDREPLY); else ci_send_message (SENDREPLY, NULL, NULL, extra->ctx, extra->hdr); + mutt_show_xface (); redraw = REDRAW_FULL; break; case OP_RECALL_MESSAGE: CHECK_MODE(IsHeader (extra)); CHECK_ATTACH; + mutt_suppress_xface(); ci_send_message (SENDPOSTPONED, NULL, NULL, extra->ctx, extra->hdr); + mutt_show_xface (); redraw = REDRAW_FULL; break; case OP_GROUP_REPLY: CHECK_MODE(IsHeader (extra) || IsMsgAttach (extra)); CHECK_ATTACH; + mutt_suppress_xface(); if (IsMsgAttach (extra)) mutt_attach_reply (extra->fp, extra->hdr, extra->idx, extra->idxlen, extra->bdy, SENDREPLY|SENDGROUPREPLY); else ci_send_message (SENDREPLY | SENDGROUPREPLY, NULL, NULL, extra->ctx, extra->hdr); + mutt_suppress_xface (); redraw = REDRAW_FULL; break; case OP_LIST_REPLY: CHECK_MODE(IsHeader (extra) || IsMsgAttach (extra)); CHECK_ATTACH; + mutt_suppress_xface(); if (IsMsgAttach (extra)) mutt_attach_reply (extra->fp, extra->hdr, extra->idx, extra->idxlen, extra->bdy, SENDREPLY|SENDLISTREPLY); else ci_send_message (SENDREPLY | SENDLISTREPLY, NULL, NULL, extra->ctx, extra->hdr); + mutt_show_xface (); redraw = REDRAW_FULL; break; case OP_FORWARD_MESSAGE: CHECK_MODE(IsHeader (extra) || IsMsgAttach (extra)); CHECK_ATTACH; + mutt_suppress_xface(); if (IsMsgAttach (extra)) mutt_attach_forward (extra->fp, extra->hdr, extra->idx, extra->idxlen, extra->bdy); else ci_send_message (SENDFORWARD, NULL, NULL, extra->ctx, extra->hdr); + mutt_show_xface (); redraw = REDRAW_FULL; break; @@ -2428,7 +2505,9 @@ break; case OP_SHELL_ESCAPE: + mutt_suppress_xface (); mutt_shell_escape (); + mutt_show_xface (); MAYBE_REDRAW (redraw); break; @@ -2563,5 +2642,6 @@ safe_free ((void **) &lineInfo); if (index) mutt_menuDestroy(&index); + mutt_clear_xface (); return (rc != -1 ? rc : 0); } --- parse.c.orig Tue Jan 29 11:05:20 2002 +++ parse.c Thu May 30 23:13:47 2002 @@ -90,6 +90,27 @@ /* not reached */ } +static LIST *mutt_add_x_face (LIST *lst, char *face) +{ + LIST *n; + + n = safe_malloc(sizeof(LIST)); + n->data = safe_strdup(face); + n->next = NULL; + + if (lst) + { + LIST *l; + + for(l = lst; l->next; l = l->next); + l->next = n; + } + else + lst = n; + + return lst; +} + static LIST *mutt_parse_references (char *s, int in_reply_to) { LIST *t, *lst = NULL; @@ -1205,6 +1226,11 @@ else if (ascii_strcasecmp (line+1, "-label") == 0) { e->x_label = safe_strdup(p); + matched = 1; + } + else if (ascii_strcasecmp (line+1, "-face") == 0) + { + e->x_face = mutt_add_x_face (e->x_face, p); matched = 1; } --- sendlib.c.orig Sat Aug 24 00:05:52 2002 +++ sendlib.c Sat Aug 24 00:16:54 2002 @@ -1663,6 +1663,15 @@ fputc ('\n', fp); } + /* Add X-Face headers */ + if (env->x_face) + { + LIST *face; + + for (face = env->x_face; face; face = face->next) + fprintf (fp, "X-Face: %s\n", face->data); + } + if (mode == 0 && !privacy && option (OPTXMAILER)) { /* Add a vanity header */ slrnface-2.1.1/slrnface.10100644002170200001450000001633207531642677014521 0ustar davearsdigit'\" t .TH slrnface 1 "28 Feb 2002" .GH "28 Feb 2002" .SH NAME slrnface \- show .I X\-Faces in X11 terminal emulator .SH SYNOPSIS .B slrnface [ .B \-xOffsetChar .I x_char_offset ] [ .B \-yOffsetChar .I y_char_offset ] [ .B \-xOffsetPix .I x_pixel_offset ] [ .B \-yOffsetPix .I y_pixel_offset ] [ .B \-XFacePad .I left_padding ] [ .B \-ink .I fg_color ] [ .B \-paper .I bg_color ] [ .B \-padColor .I pad_color ] .SH DESCRIPTION The .B slrnface helper utility can be used from .BR slrn (1), .BR mutt (1) or similar programs to show .I X\-Faces in Usenet articles or mail messages when those programs are run in an X11 terminal emulator. This utility is not intended to be run directly from the command line. Instead, the master programs should be configured to invoke .B slrnface when appropriate. .LP Different terminal emulators have different screen layouts. Some might have scroolbars (on either side), a menubar or something else. Terminal window which .B slrnface uses might contain some of these "decorations," but it is impossible to determine if that is the case at run time. Therefore it is impossible to determine the exact location at which the X11 window with the .I X\-Face image should be placed. Default hardcoded values are appropriate for several terminal emulators, but not for all of them. The placement can be controlled by command line arguments, but it is suggested to use X resources for this task. .LP While .B slrnface is running and showing .IR X\-Face , the left mouse button can be used to move the window. .SH RESOURCES The .B slrnface helper is controlled by the resources set for the terminal emulator in which it runs, not for the .B slrnface class or instance. That is because correct execution entirely depends on the master program and the geometry of the terminal emulator in use and has little to do with .B slrnface itself. All resources have a command line parameter equivalent. .LP However, a lot of popular terminal emulators have a certain amount of hardcoded brain damage and setting X resources will not have any effect with them. This usually happens because of illegal characters in the WM_CLASS property. In those cases command line arguments are the only remaining option. .LP .TS center; cf3 cf3 cf3 l l l . Resource Command line parameter Default value _ slrnface.xOffsetChar -xOffsetChar 0 slrnface.yOffsetChar -yOffsetChar 1 slrnface.xOffsetPix -xOffsetPix 0 slrnface.yOffsetPix -yOffsetPix 2 slrnface.XFacePad -XFacePad 0 slrnface.ink -ink black slrnface.paper -paper white slrnface.padColor -padColor black .TE .SS xOffsetChar The horizontal offset for the .I X\-Face window from the terminal's upper right corner expressed in character units. .SS yOffsetChar The vertical offset for the .I X\-Face window from the terminal's upper right corner expressed in character units. The exact value in pixels will be calculated at run time, as well as the pixel equivalent for .BR xOffestChar . There are some lousily coded terminals which are not very helpful with this, so the calculation might not be perfect. .SS xOffsetPix The horizontal offset for the .I X\-Face window from the terminal's upper right corner expressed in pixels. .SS yOffsetPix The vertical offset for the .I X\-Face window from the terminal's upper right corner expressed in pixels. This value is added to the character offset provided by the .B yOffsetChar resource and the resulting sum is substracted from the vertical coordinate of the terminal's upper right corner. The equivalent calculation for the horizontal coordinate is done with the .B xOffsetChar and .B xOffsetPix resources. In both cases, pixel value resources are provided for finer control. .SS XFacePad This value tells .B slrnface how many pixels should be cleared on the left side of the .I X\-Face image. In the case of long subject lines the image may be displayed over characters and having some spacing between them would be beneficial. .SS ink This is the foreground color for the .I X\-Face image. The more common name for this resource is .IR foreground . However, that name is not used because there is a high probability that it would be inherited from the terminal's resources. A lot of users use light foreground and dark background for terminals. Since .I X\-Faces should normally be viewed with dark foreground on light background, inheriting the colors from the terminal setup would present a negative image by default. In order to avoid that, .B slrnface uses different resource name. .SS paper This is the background color for the .I X\-Face image. .SS padColor This is the color for the padding region set with .B XFacePad resource. .SH EXAMPLES The following example demonstrates how to set resources for xterm. .SS Example 1: horizontal offsets for xterm Since .B slrnface looks at the resources defined for the terminal's class and name, an example setting is: .br .nf xterm.slrnface.xOffsetChar: 1 xterm.slrnface.xOffsetPix: 2 .fi It is better to use the application name, rather than class, because some other terminals use XTerm as their class, since they try to be feature compatible. However, they might not have the same screen layout, so resources set for the terminal class might not yield the desired effect in all terminals. .SH ENVIRONMENT VARIABLES .TP 15 .SB DISPLAY The name of the display where the terminal runs. This environment variable is the only way to pass it to .BR slrnface . .TP 15 .SB WINDOWID Used for determining terminal's X window id. It must be set by a terminal emulator. .TP 15 .SB LANG, LC_ALL, LC_MESSAGES These environment variables will be used when determining the location of the appropriate resource file. .SH EXIT STATUS The .B slrnface helper doesn't print any diagnostic, because it doesn't know if that is the appropriate action. In cases when diagnostic output is appropriate, it can't guarantee that the output would be visible to the user. Therefore it returns distinct error status for every kind of problem and relies on the parent process to take the appropriate action. .LP It should be noted that .B slrnface forks early in its execution. The parent process exits immediately, returning success status to its parent. The child process continues execution, reading commands from the FIFO in the .B $HOME/.slrnfaces directory and executing them as appropriate. In the case where there is a problem with the process' startup, the fork doesn't happen and .B slrnface exits immediately, returning the appropriate error status. The following exit values are returned: .TP 4 .B 0 Successful completion. .TP 4 .B 1 .B slrnface couldn't connect to the display. .TP 4 .B 2 Terminal emulator didn't set the .B WINDOWID environment variable or its value was invalid. .TP 4 .B 3 .B slrnface couldn't find its controlling terminal. .TP 4 .B 4 Terminal's width or height are not set. .TP 4 .B 5 There was a problem with the FIFO setup. .TP 4 .B 6 .BR fork (2) failed. .SH FILES .PD 0 .TP 20 .B $HOME/.slrnfaces/* A named pipe used for communication between .B slrnface and the master proces. .TP .B slrnface.sl An example S-Lang hooks for setting up .B slrnface with .BR slrn (1). .PD .SH SEE ALSO .BR slrn (1), .BR mutt (1), .BR X11 (5), .BR xprop (1), .BR xterm (1), .BR fork (2) .SH BUGS Multiple .I X\-Faces are not supported. .SH AUTHOR Drazen Kacar .LP Home page: http://dave.willfork.com/slrnface/ slrnface-2.1.1/slrnface.c0100644002170200001450000007204707531775700014602 0ustar davearsdigit/* slrnface - feeble attempt at delivering X-Faces to slrn and mutt users * Copyright (C) 2000, 2001, 2002 Drazen Kacar * * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __linux #include #include #include #else #include #endif #define countof(x) (sizeof(x)/sizeof(x[0])) extern void uncompface (char *); extern int errno; /* * The values in optvalues[] array are just fallbacks if appropriate * X resources are not found. You can set them here if you like, but * you should really use X resources for that. Command line parameters * should also work. */ typedef enum { val_int, val_color } OptionType; struct optval { OptionType type; union { unsigned long intval; char *string; } value; }; struct optval optvalues[] = { { val_int, { 0 }}, { val_int, { 1 }}, { val_int, { 0 }}, { val_int, { 2 }}, { val_int, { 0 }}, { val_color, { 0 }}, { val_color, { 0 }}, { val_color, { 0 }} }; #define X_OFFSET_CHAR (optvalues[0].value.intval) #define Y_OFFSET_CHAR (optvalues[1].value.intval) #define X_OFFSET_PIX (optvalues[2].value.intval) #define Y_OFFSET_PIX (optvalues[3].value.intval) #define XFACE_PAD (optvalues[4].value.intval) /* Very unconsistent */ #define INK (optvalues[5]) #define PAPER (optvalues[6]) #define PADCOLOR (optvalues[7]) XrmOptionDescRec my_options[] = { { "-xOffsetChar", "*slrnface.xOffsetChar", XrmoptionSepArg, NULL }, { "-yOffsetChar", "*slrnface.yOffsetChar", XrmoptionSepArg, NULL }, { "-xOffsetPix", "*slrnface.xOffsetPix", XrmoptionSepArg, NULL }, { "-yOffsetPix", "*slrnface.yOffsetPix", XrmoptionSepArg, NULL }, { "-XFacePad", "*slrnface.XFacePad", XrmoptionSepArg, NULL }, { "-ink", "*slrnface.ink", XrmoptionSepArg, NULL }, { "-paper", "*slrnface.paper", XrmoptionSepArg, NULL }, { "-padColor", "*slrnface.padColor", XrmoptionSepArg, NULL } }; /* * MAXRESOURCESIZE should be the size of the longest resource string in the * above array. It can be a bit larger, of course. If you're adding * resources, make sure it's not smaller. Wouldn't it be nice if preprocessor * was able to calculate that? */ #define MAXRESOURCESIZE 30 Display *d = NULL; Window win = 0, winid; /* win is X-Face window, winid is the parent */ int winchild; Cursor cursor_move; int x_pos, y_pos; /* X-Face window position */ Colormap cmap; GC gc; XGCValues gcvals; unsigned long gcmask; unsigned long pad_color; Pixmap bitmap = 0; int fifo_fd, term_fd; struct winsize winsz; int is_mapped = 0; /* Whether X-Face window is mapped */ int buffered_face_len = -1; /* Length of last X-Face data */ int pixmap_stale = 1; /* If set, X-Face data in the buffer doesn't match pixmap, ie. pixmap needs to be regenerated. */ int face_cleared = 1; /* If set, the last command told us to clear X-Face. We'll still keep the pixmap and the buffer data (if we had any), just in case the next X-Face will be the same, but this means we'll not show X-Face until we get a command with the new X-Face data. */ char last_face[8000]; /* Buffer for X-Face data. We keep the last one here for comparison. If the new X-Face turns out to be the same as the previous one, we can save some round trips. */ struct pollfd fifo_pollfd; /* * Flags for signals. We can't use bits in the same variable because bit * operations are not atomic. */ volatile sig_atomic_t got_sigterm = 0, got_sigstop = 0, got_sigcont = 0; volatile sig_atomic_t got_sigwinch = 0; /* Signal handlers */ /* ARGSUSED */ void handle_sigterm (int sig) { got_sigterm = 1; } /* ARGSUSED */ void handle_sigstop (int sig) { got_sigstop = 1; } /* ARGSUSED */ void handle_sigcont (int sig) { got_sigcont = 1; } /* ARGSUSED */ void handle_sigwinch (int sig) { got_sigwinch = 1; } void cleanup (void) { if (d) { if (bitmap) XFreePixmap (d, bitmap); if (win) { XFreeCursor (d, cursor_move); XDestroyWindow (d, win); } XCloseDisplay (d); } } void suspend_me (void) { if (is_mapped) { XUnmapWindow (d, win); XFlush (d); } got_sigstop = 0; /* * And we'll proceed to sleep in poll(). In case we get a command through * the FIFO we'll process it, so something might show up on the screen. * I'm not sure if this is a bug or a feature. :-) */ } void restart_me (void) { if (is_mapped) { XMapWindow (d, win); XFlush (d); } got_sigcont = 0; } /* * We don't know if we got top level window id or an id for a child * window. A child window typically won't have WM_CLASS set, so we need * to traverse up the window hierarchy until we find a window with that * property. * * Input: w - window id where the search starts * hint - pointer to XClassHint structure * Output: 0 - WM_CLASS not found * 1 - WM_CLASS found on window w * 2 - WM_CLASS found on one of w's parents * * If WM_CLASS was found, it's stored in h. */ int get_class_hint (Window w, XClassHint *hint) { Window root, parent, *children; unsigned int nchld; int childwin = 0; do { if (XGetClassHint (d, w, hint)) return childwin + 1; if (!XQueryTree (d, w, &root, &parent, &children, &nchld)) return 0; if (children) XFree (children); childwin = 1; w = parent; } while (parent != root); return 0; } /* * Calculate the position at which X-Face window should be placed. * * Input: global variables X_OFFSET_CHAR, X_OFFSET_PIX, Y_OFFSET_CHAR, * Y_OFFSET_PIX and XFACE_PAD * Output: global variables x_pos and y_pos * Uses: global struct winsz and variables winid and winchild */ void get_face_position (void) { /* * The problem with terminals is that they might have scrollbars on * the unknown side. But if we detected WM_CLASS on a parent window, * we can reasonably assume that our window id is just the text window * and nothing else. */ if (!winsz.ws_xpixel || !winsz.ws_ypixel) /* Bastards... */ { Window root_return; int x_return, y_return; unsigned width_return, height_return; unsigned border_width_return; unsigned depth_return; XGetGeometry (d, winid, &root_return, &x_return, &y_return, &width_return, &height_return, &border_width_return, &depth_return); if (winchild > 1) x_pos = width_return - X_OFFSET_CHAR * (width_return / winsz.ws_col) - X_OFFSET_PIX; else { /* * First try to substract scrollbar and hope the rest are * just characters on the screen. */ width_return -= X_OFFSET_PIX; x_pos = width_return - X_OFFSET_CHAR * (width_return / winsz.ws_col); } y_pos = Y_OFFSET_CHAR * (height_return / winsz.ws_row) + Y_OFFSET_PIX; } else { x_pos = winsz.ws_xpixel - X_OFFSET_CHAR * (winsz.ws_xpixel / winsz.ws_col) - X_OFFSET_PIX; y_pos = Y_OFFSET_CHAR * (winsz.ws_ypixel / winsz.ws_row) + Y_OFFSET_PIX; } x_pos -= 48 + XFACE_PAD; /* X-Face window width. */ } /* * We need to allocate black and white colors instead of using * BlackPixelOfScreen and WhitePixelOfScreen macros because we'll want to * deallocate them in case we get a command to change one of the colors. * Using macros and trying to deallocate would result in BadAccess. */ unsigned long alloc_named_color (char *name) { XColor col; if (XParseColor (d, cmap, name, &col) && XAllocColor (d, cmap, &col)) return col.pixel; /* * XXX This isn't kosher in the general case, but we're currently * using this only for black & white, so it's not going to fail. */ return 0; } void parse_resource(int idx, char *resource) { if (optvalues[idx].type == val_int) optvalues[idx].value.intval = strtol (resource, NULL, 0); else /* val_color */ { XColor col; if (XParseColor (d, cmap, resource, &col) && XAllocColor (d, cmap, &col)) { optvalues[idx].value.intval = col.pixel; /* * Change type to val_int, so we know the value is set here. */ optvalues[idx].type = val_int; } } } void force_cmd_line (int argc, char **argv) { int i, j; for (i = 1; i < argc; ++i) { if (*argv[i] != '-') continue; for (j = 0; j < countof(my_options); ++j) if (!strcmp (argv[i], my_options[j].option)) { if (argv[i + 1]) { parse_resource (j, argv[i + 1]); i++; } break; } } } void setup (int *argc, char **argv) { int i, free_fifo = 0, status; char *winenv, *fifo_file, *home; XWindowAttributes winattrs; XSetWindowAttributes winsetattrs; unsigned long winmask; struct stat inode; XClassHint terminal; /* See if we can connect to the display */ if (!(d = XOpenDisplay (NULL))) exit (1); /* Then close because we'll fork. We'll reopen it in the child. Hopefully it won't run away. */ XCloseDisplay (d); /* Terminal Window ID. We must have this. */ if (!(winenv = getenv ("WINDOWID")) || !(winid = strtol (winenv, NULL, 0))) exit (2); /* Stupid method for determining controlling terminal. It should be one of those. But it doesn't have to. FIXME */ for (i = 0; i < 3; ++i) if (isatty (term_fd = i)) break; if (i == 3) exit (3); ioctl (term_fd, TIOCGWINSZ, &winsz); /* We could work without this, but we're just picky. */ if (!winsz.ws_row || !winsz.ws_col) exit (4); /* See if we got FIFO name as the argument. */ fifo_file = NULL; for (i = 1; i < *argc && argv[i]; i += 2) { if (*argv[i] != '-') { fifo_file = argv[i]; break; } } if (!fifo_file) { /* FIFO not in argument; construct the name */ struct utsname u; size_t maxlen; if (!(home = getenv ("HOME"))) { struct passwd *pw; pw = getpwuid (geteuid ()); if (!pw || !(home = pw->pw_dir)) exit (5); } if (uname (&u) < 0) exit (5); maxlen = strlen (home) + sizeof ("/.slrnfaces/") + strlen (u.nodename) + 30; fifo_file = malloc (maxlen); if (!fifo_file) exit (5); free_fifo = 1; if (snprintf(fifo_file, maxlen, "%s/.slrnfaces/%s.%ld", home, u.nodename, (long) getppid ()) < 0) exit (5); } /* * The FIFO should have been created by the parent. If it doesn't exist * we'll create it here, but it's a bad omen. If the file exists, but * is not a FIFO, we need to return an error now, because we won't have * the chance later. Parent could block waiting for us or waiting on a * FIFO without a reader. We can't allow that. This whole show would have * been unnecessary if slang could open a pipe. A simple, unnamed, * unidirectional pipe. Is that too much to ask? */ /* Open non-blocking FIFO now and remove that flag after the fork. */ if (stat (fifo_file, &inode)) if (errno == ENOENT) { if (mkfifo (fifo_file, 0600)) exit (5); } else exit (5); else if (!S_ISFIFO (inode.st_mode)) exit (5); if ((fifo_fd = open (fifo_file, O_RDONLY | O_NONBLOCK)) < 0) exit (5); if (free_fifo) free (fifo_file); switch (fork ()) { case -1: exit (6); case 0: break; /* Child. Just continue. */ default: exit (0); /* Parent. Return success and make its parent happy. */ } fcntl (fifo_fd, F_SETFL, 0); /* * Fill this here. The structure will be used for the normal processing * as well, which is why it's a global thing. */ fifo_pollfd.fd = fifo_fd; fifo_pollfd.events = POLLIN; /* Sync with the parent. */ do { status = poll (&fifo_pollfd, 1, -1); } while (status == -1 && errno == EINTR); if (!(d = XOpenDisplay (NULL))) exit (1); /* Get colormap, preferably the one used by the parent terminal window. */ if (XGetWindowAttributes (d, winid, &winattrs) == Success) cmap = winattrs.colormap; else cmap = DefaultColormap (d, DefaultScreen (d)); /* X Resources stuff. Suggested by a user's demented mind. */ winchild = get_class_hint (winid, &terminal); if (winchild) { /* * It seems there's a lot of idiots who are putting dots in the name * property. Examples are gnome-terminal and Eterm. If you run into * this problem, quote them Xlib manual for me: Note that the name set in this property may differ from the name set as WM_NAME. That is, WM_NAME specifies what should be displayed in the title bar and, therefore, can contain temporal information (for example, the name of a file currently in an editor's buffer). On the other hand, the name specified as part of WM_CLASS is the formal name of the application that should be used when retrieving the application's resources from the resource database. * We'll try to work around the brain damage by parsing command * line parameters directly, so at least that will work. */ if (strpbrk (terminal.res_name, ".*?") || strpbrk (terminal.res_class, ".*?")) { force_cmd_line (*argc, argv); } else { XtAppContext app_context; XrmDatabase res; size_t name_len, class_len; int i; char *name_str, *class_str; char *name_start, *class_start; char *type_str; XrmValue value; XtToolkitInitialize (); app_context = XtCreateApplicationContext (); XtDisplayInitialize (app_context, d, terminal.res_name, terminal.res_class, my_options, countof (my_options), argc, argv); res = XtScreenDatabase (DefaultScreenOfDisplay (d)); name_len = strlen (terminal.res_name); name_str = malloc (name_len + MAXRESOURCESIZE); memcpy (name_str, terminal.res_name, name_len); name_str[name_len] = '.'; name_start = name_str + name_len + 1; class_len = strlen (terminal.res_class); class_str = malloc (class_len + MAXRESOURCESIZE); memcpy (class_str, terminal.res_class, class_len); class_str[class_len] = '.'; class_start = class_str + class_len + 1; for (i = 0; i < countof (my_options); ++i) { strcpy (name_start, my_options[i].specifier + 1); strcpy (class_start, my_options[i].specifier + 1); if(XrmGetResource(res, name_str, class_str, &type_str, &value) == True) { /* We don't have generic strings yet, so this will do. */ char *resource = malloc (value.size + 1); memcpy (resource, value.addr, value.size); resource[value.size] = 0; parse_resource (i, resource); free (resource); } } free (name_str); free (class_str); XrmDestroyDatabase (res); } XFree (terminal.res_name); XFree (terminal.res_class); } else force_cmd_line (*argc, argv); /* Calculate X-Face window position. */ get_face_position (); /* Set up background color and create our window. */ if (PADCOLOR.type == val_color) pad_color = alloc_named_color ("black"); else pad_color = PADCOLOR.value.intval; winsetattrs.background_pixel = pad_color; winsetattrs.win_gravity = NorthEastGravity; winsetattrs.colormap = cmap; winmask = CWBackPixel | CWWinGravity | CWColormap; win = XCreateWindow (d, winid, x_pos, y_pos, 48 + XFACE_PAD, 48, 0, CopyFromParent, CopyFromParent, CopyFromParent, winmask, &winsetattrs); /* Then set up colors in the GC. */ if (INK.type == val_color) gcvals.foreground = alloc_named_color ("black"); else gcvals.foreground = INK.value.intval; if (PAPER.type == val_color) gcvals.background = alloc_named_color ("white"); else gcvals.background = PAPER.value.intval; gcvals.graphics_exposures = False; gcmask = GCForeground | GCBackground | GCGraphicsExposures; gc = XCreateGC(d, win, gcmask, &gcvals); /* And the last piece of crap. */ cursor_move = XCreateFontCursor (d, XC_fleur); XGrabButton (d, Button1, AnyModifier, win, False, ButtonPressMask | ButtonReleaseMask | Button1MotionMask, GrabModeSync, GrabModeAsync, win, cursor_move); XSelectInput (d, win, ExposureMask | StructureNotifyMask); /* We are done. Amazing. */ } #define MAXCOLORLEN 40 unsigned long chk_alloc_color (char *color, unsigned long pixel, unsigned long *pixel_return) { char *p, *s, clr[MAXCOLORLEN]; XColor col; p = color; while (*p != '\n' && isspace (*(unsigned char *)p)) ++p; if (*p == '\n') return 0; for (s = clr; !isspace (*(unsigned char *)p) && s < clr + MAXCOLORLEN; ++p, ++s) *s = *p; if (s == clr + MAXCOLORLEN) return 0; *s = 0; if (!XParseColor (d, cmap, clr, &col) || !XAllocColor (d, cmap, &col)) return 0; XFreeColors (d, cmap, &pixel, 1, 0); if (col.pixel == pixel) return 0; *pixel_return = col.pixel; return 1; } int chk_get_naturalnum (char *buf) { long num; char *endptr; errno = 0; num = strtol (buf, &endptr, 0); if (!errno && num >= 0 && *endptr == '\n' && num < INT_MAX) return num; else return -1; } typedef enum { xface_eof, /* EOF or error on FIFO */ xface_noop, /* Do nothing */ xface_flush, /* Flush the display */ xface_unmap, /* Unmap window */ xface_map, /* Map window */ xface_display /* Map window and paint X-Face */ } XFaceState; /* Read data from FIFO and return state. Reimplementing stdio is always fun. */ XFaceState read_fifo (void) { int n, status, refresh_gc = 0, refresh_pad = 0, reposition_face = 0; size_t toread; int state, skip_junk = 0; char *command, *bufstart, *bufend, *bufp; int xoffsetchar = -1, yoffsetchar = -1, xoffsetpix = -1, yoffsetpix = -1; char buf[8000]; do { begin: toread = sizeof (buf); bufstart = command = buf; readmore: do { n = read (fifo_fd, bufstart, toread); } while (n < 0 && errno == EINTR); if (n <= 0) return xface_eof; bufend = bufstart + n; bufp = bufstart; state = xface_noop; while (bufp < bufend) { /* Find the end of the command. */ while (bufp < bufend && *bufp != '\n') ++bufp; if (*bufp != '\n') { /* Partial command in the buffer. Try to read some more. */ toread = sizeof(buf) - (bufend - buf); if (toread) { bufstart = bufend; goto readmore; } if (command == buf) { /* * The command doesn't fit in the buffer. It must be * some junk, so discard it and resync on newline. */ skip_junk = 1; goto begin; } /* * Copy what we have to the beginning of the buffer and read * some more. */ memmove (buf, command, bufend - command); bufstart = buf + (bufend - command); toread = sizeof (buf) - (bufend - command) + 1; goto readmore; } /* * See if we were supposed to skip the junk. This can happen * only with the first chunk in the buffer. */ if (skip_junk) { skip_junk = 0; if (bufend == bufp + 1) goto begin; /* The next command needs our attention. */ command = ++bufp; continue; } if (!memcmp (command, "clear\n", sizeof "clear")) { state = xface_unmap; face_cleared = 1; } else if (!memcmp (command, "suppress\n", sizeof "suppress")) state = xface_unmap; else if (!memcmp (command, "show\n", sizeof "show")) { if (!face_cleared) if (pixmap_stale) state = xface_display; else state = xface_map; } else if (!memcmp (command, "xface ", sizeof "xface")) { int face_len; char *face_start; face_start = command + sizeof "xface"; face_len = bufp - face_start; /* * We need to do all sanity checking here because of supress * command. Our caller cannot afford to ignore certain kinds * of data because it wouldn't know what the previous good * data was. * * We are ignoring X-Faces longer than 2000 bytes because * uncompface() uses the same buffer for input and output and * it's concievable that this might lead to buffer overruns, * since there's no way to pass the buffer size to it. * * TODO: mmap a chunk of memory somewhere, make sure there is * a gap above it, catch SIGSEGV temporarily and do uncompressing * there. It's the only safe way. */ if (face_len <= 2000) { if (face_len == buffered_face_len && !memcmp (face_start, last_face, face_len)) if (pixmap_stale) state = xface_display; else state = xface_map; else { memcpy (last_face, face_start, face_len); pixmap_stale = 1; buffered_face_len = face_len; state = xface_display; } face_cleared = 0; } } else if (!memcmp (command, "ink ", sizeof "ink")) { unsigned long pixel; if (chk_alloc_color (command + sizeof "ink", gcvals.foreground, &pixel)) { gcvals.foreground = pixel; refresh_gc = 1; } } else if (!memcmp (command, "paper ", sizeof "paper")) { unsigned long pixel; if (chk_alloc_color (command + sizeof "paper", gcvals.background, &pixel)) { gcvals.background = pixel; refresh_gc = 1; } } else if (!memcmp (command, "padcolor ", sizeof "padcolor")) { unsigned long pixel; if (chk_alloc_color (command + sizeof "padcolor", pad_color, &pixel)) { pad_color = pixel; refresh_pad = 1; } } else if (!memcmp (command, "xoffsetchar ", sizeof "xoffsetchar")) { xoffsetchar = chk_get_naturalnum (command + sizeof "xoffsetchar"); if (xoffsetchar >= 0) reposition_face = 1; } else if (!memcmp (command, "yoffsetchar ", sizeof "yoffsetchar")) { yoffsetchar = chk_get_naturalnum (command + sizeof "yoffsetchar"); if (yoffsetchar >= 0) reposition_face = 1; } else if (!memcmp (command, "xoffsetpix ", sizeof "xoffsetpix")) { xoffsetpix = chk_get_naturalnum (command + sizeof "xoffsetpix"); if (xoffsetpix >= 0) reposition_face = 1; } else if (!memcmp (command, "yoffsetpix ", sizeof "yoffsetpix")) { yoffsetpix = chk_get_naturalnum (command + sizeof "yoffsetpix"); if (yoffsetpix >= 0) reposition_face = 1; } /* Otherwise it's unrecognized command. Just skip it. */ command = ++bufp; } /* * Wait 10 milliseconds in case the parent has something else to send. * The user is not likely to notice this pause and we might avoid * certain amount of processing and roundtrips. */ do { status = poll (&fifo_pollfd, 1, 10); } while (status == -1 && errno == EINTR); } while (status > 0); if (refresh_gc) { XChangeGC (d, gc, gcmask, &gcvals); pixmap_stale = 1; if (!face_cleared) state = xface_display; } if (refresh_pad) { XSetWindowBackground (d, win, pad_color); XClearArea (d, win, 0, 0, XFACE_PAD, 48, False); if (state == xface_noop) state = xface_flush; } if (reposition_face) { if (got_sigwinch) { ioctl (term_fd, TIOCGWINSZ, &winsz); got_sigwinch = 0; } if (xoffsetchar >= 0) X_OFFSET_CHAR = xoffsetchar; if (yoffsetchar >= 0) Y_OFFSET_CHAR = yoffsetchar; if (xoffsetpix >= 0) X_OFFSET_PIX = xoffsetpix; if (yoffsetpix >= 0) Y_OFFSET_PIX = yoffsetpix; get_face_position (); XMoveWindow (d, win, x_pos, y_pos); if (state == xface_noop) state = xface_flush; } return state; } char ls[] = { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 }; int process_command (void) { int i, n; unsigned char *datap; unsigned char data[288]; char uncompfacebuf[15000]; switch (read_fifo ()) { case xface_eof: return 1; case xface_unmap: if (is_mapped) { XUnmapWindow (d, win); is_mapped = 0; XFlush (d); } break; case xface_map: if (!is_mapped) { XMapWindow (d, win); is_mapped = 1; XFlush (d); } break; case xface_display: memcpy (uncompfacebuf, last_face, buffered_face_len); uncompfacebuf[buffered_face_len] = 0; pixmap_stale = 0; uncompface (uncompfacebuf); /* Are we alive? No buffer overruns? Fuck. */ for (i = 0, n = 0, datap = data; i < 48; ++i) { int i1, i2, i3, len; sscanf (uncompfacebuf + n, "%i,%i,%i,%n", &i1, &i2, &i3, &len); n += len; /* Who invented that all-bits-are-reversed format? */ *datap++ = ls[i1 >> 12 & 15] | ls[i1 >> 8 & 15] << 4; *datap++ = ls[i1 >> 4 & 15] | ls[i1 & 15] << 4; *datap++ = ls[i2 >> 12 & 15] | ls[i2 >> 8 & 15] << 4; *datap++ = ls[i2 >> 4 & 15] | ls[i2 & 15] << 4; *datap++ = ls[i3 >> 12 & 15] | ls[i3 >> 8 & 15] << 4; *datap++ = ls[i3 >> 4 & 15] | ls[i3 & 15] << 4; } if (bitmap) XFreePixmap (d, bitmap); bitmap = XCreateBitmapFromData (d, win, (char *) data, 48, 48); if (is_mapped) XCopyPlane(d, bitmap, win, gc, 0, 0, 48, 48, XFACE_PAD, 0, 1); else XMapWindow (d, win); /* It will be filled on expose. */ XFlush (d); break; case xface_flush: XFlush (d); } return 0; } void process_xevent (void) { static int x_pointer, y_pointer; static int grab = AnyButton; int x, y; XEvent event; while (XPending (d)) { XNextEvent (d, &event); switch (event.type) { case MapNotify: case Expose: is_mapped = 1; XCopyPlane (d, bitmap, win, gc, 0, 0, 48, 48, XFACE_PAD, 0, 1); XFlush (d); break; case ButtonPress: switch (((XButtonEvent *) &event)->button) { case Button1: if (grab == AnyButton) { grab = Button1; x_pointer = ((XButtonEvent *) &event)->x_root; y_pointer = ((XButtonEvent *) &event)->y_root; } XAllowEvents (d, SyncPointer, CurrentTime); break; } break; case ButtonRelease: if(grab == ((XButtonEvent *) &event)->button) grab = AnyButton; XAllowEvents (d, AsyncPointer, CurrentTime); break; case MotionNotify: x = ((XMotionEvent *) &event)->x_root; y = ((XMotionEvent *) &event)->y_root; x_pos += x - x_pointer; y_pos += y - y_pointer; x_pointer = x; y_pointer = y; XMoveWindow (d, win, x_pos, y_pos); break; case GravityNotify: x = ((XGravityEvent *) &event)->x; y = ((XGravityEvent *) &event)->y; if (grab != AnyButton) { x_pointer += x - x_pos; y_pointer += y - y_pos; } x_pos = x; y_pos = y; break; } } } int main (int argc, char **argv) { struct pollfd p[2]; struct sigaction sigact; /* * We don't need setlocale() call, because nothing here depends on the * locale. The only effect it might have is loading and initializing * one or more shared libraries. We don't need that. * * setlocale (LC_ALL, ""); * */ setup (&argc, argv); /* child returns, FIFO set and ready, window created */ /* * We'll ignore Ctrl-C because our action should depend on parent's * action. If the parent decides to exit it will close the pipe and * then we'll exit as well. If it doesn't exit, we are not supposed * to exit either. So we need to ignore this signal. */ signal (SIGINT, SIG_IGN); /* But we need to handle these. */ sigemptyset (&sigact.sa_mask); sigact.sa_flags = SA_RESTART; /* Not that it matters. */ sigact.sa_handler = handle_sigterm; sigaction (SIGTERM, &sigact, NULL); sigact.sa_handler = handle_sigstop; sigaction (SIGTSTP, &sigact, NULL); sigact.sa_handler = handle_sigcont; sigaction (SIGCONT, &sigact, NULL); sigact.sa_handler = handle_sigwinch; sigaction (SIGWINCH, &sigact, NULL); /* Set up poll struct. */ p[0].fd = ConnectionNumber (d); p[1].fd = fifo_fd; p[0].events = p[1].events = POLLIN; while (1) { int status; status = poll (p, 2, -1); if (status < 0) { /* A signal was caught, most likely. */ if (got_sigterm) break; if (got_sigstop) suspend_me (); if (got_sigcont) restart_me (); } else { if ((p[0].revents | p[1].revents) & (POLLERR | POLLHUP | POLLNVAL)) break; if (p[0].revents) process_xevent (); if (p[1].revents) if (process_command ()) break; } } cleanup (); return 0; } slrnface-2.1.1/doc/0040755002170200001450000000000007532000007013355 5ustar davearsdigitslrnface-2.1.1/doc/Protocol0100644002170200001450000000730307532000007015101 0ustar davearsdigitThis is the description of the protocol used by slrnface and the parent process. It is provided to save you from looking at the source in case you want to write your own scripts for communication. However, the protocol is unstable and subject to change in the future releases. That's why it's not in the man page. 1. Setup The last parameter passed to the slrnface should be the name of the FIFO which the parent has created. If it's not passed slrnface will create the FIFO, but this can work reliably only if slrnface was started by a fork/exec sequence. If it has been started by a system() call, slrnface has no way of knowing that its parent process is a shell and not the actual parent process. FIFO name constructed by slrnface is: $HOME/.slrnfaces/$nodename.$ppid where: $HOME is the value of HOME environment variable $nodename is the nodename member of struct utsname returned by uname(2) $ppid is the parent proces id Once the FIFO has been opened by both parties slrnface will wait until the parent writes a short string. The string will be discarded and can be anything. The length must be less than PIPE_BUF, as defined in . 2. Commands All commands are terminated by a newline character. Unrecognized commands are discarded. Slrnface understands the following commands: - clear causes slrnface to clear X-Face window - xface argument causes slrnface to prepare the new image as specified by the argument and map the X-Face window. The argument needs to be 48x48x1 image in the format produced by the compface(1) command. It can contain whitespace at arbitrary positions between non-whitespace characters. If the argument is longer than 2000 bytes, the command is discarded. - suppress causes slrnface to temporarily remove X-Face window. It can be shown again by using "show" command or by supplying a new X-Face with "xface" command. - show the action depends on whether "clear" or "xface" command preceded this one. If it was "clear", nothing happens. If it was "xface", the X-Face window will appear on the screen, containing the last image sent with the "xface" command. In case one of the below commands was specified between "suppress" and "show", the new X-Face window will reflect changes they were asking for. - ink argument causes slrnface to change X-Face foreground color to the one specified in the argument. The argument must be in the format recognized by XParseColor(). - paper argument causes slrnface to change X-Face background color to the one specified in the argument. The argument must be in the format recognized by XParseColor(). - padcolor argument causes slrnface to change X-Face pad color to the one specified in the argument. The argument must be in the format recognized by XParseColor(). - xoffsetchar number - xoffsetpix number - yoffsetchar number - yoffsetpix number cause slrnface to move X-Face window to the location specified with these commands. If any of the arguments is negative, greater than INT_MAX or not a number, it is ignored. If one of these commands is not specified when moving the window, previously specified value is used. This can cause unwanted effects if the user has moved the window manually. There is no way for slrnface to notify the parent about successful or unsuccessful completion of the command. 3. Shutdown Slrnface will exit in case of a fatal error or when it detects that the parent process has closed the FIFO. For the list of exit codes refer to the slrnface man page. slrnface-2.1.1/doc/Problems0100644002170200001450000001515007531565252015103 0ustar davearsdigitHere's a list of problems you might encounter. There usually isn't a satisfactory solution, but at least you won't live in ignorance. 1. Terminal emulator doesn't set WINDOWID Most terminal emulators set that environment variable for you. It contains a number which uniquely identifies terminal's X window. Slrnface needs it to know which window the terminal emulator is using and cannot operate without this information. If your terminal emulator doesn't set this variable, you're out of luck. You can set it manually, but it's a cumbersome procedure: a) Start "xwininfo" b) Click somewhere in the terminal window c) You'll get several lines of output. The first one will be something like: xwininfo: Window id: 0x900003 "xterm" That's your window identifier. Put it in the environment variable. For sh compatible shells: WINDOWID=0x900003; export WINDOWID For csh compatible shells: setenv WINDOWID 0x900003 Slrnface should be happy now, but I suppose this isn't a procedure you'd like to execute often. You might try to upgrade to the newer version of the terminal emulator, if there is one, in hope that it got this feature. If that proves to be futile, the only remaining option is to use another terminal emulator. 2. WINDOWID is not forwarded along telnet/rlogin/ssh/whatever session For a start, read the documentation for your client program. It might have an option to forward environment variables. For example, some telnet clients can be asked to do that in ~/.telnetrc file. If your client has this feature and you've told it to use it, but it still doesn't work, perhaps the server doesn't support it. In that case you're out of luck. Most notably, OpenSSH cannot forward environment variables. You might try to do something like: ssh -t user@remotehost "export TERM=$TERM; export WINDOWID=$WINDOWID; slrn" but that will only start a remote slrn session, not the shell session. You might get around the problem if you write a wrapper for your communication command. Since most of the communication protocols and tools support forwarding the TERM variable, you could overload it with the WINDOWID info. For example, the wrapper could set it to $WINDOWID::$TERM before invoking the real command. Then your shell startup script on the server side should check if the TERM variable contains "::", and if so, initialize both WINDOWID and TERM to the proper components. It's messy, but it works. And, of course, you could mail OpenSSH folks and nicely ask them to do something about environment variable forwarding. 3. X resources don't have any effect It could be a bug in slrnface or it could be a buggy terminal emulator. Slrnface is somewhat equipped to work around problematic terminals, but the support is far from ideal. The problem comes from the way certain terminals identify themselves to the X Window system. To check if your terminal has a problem, execute: xprop | grep WM_CLASS and click on the terminal window. You might get something like: WM_CLASS(STRING) = "Eterm 0.9.2", "Eterm" The dot in any of those strings is a problem. X resources use dots (as well as stars and question marks) as separators between fields. When slrnface asks for a resource property, it first constructs the full resource name from the name which terminal window has registered with the window system and the property name its interested in. So with the above example, the full name might be "Eterm 0.9.2.slrnface.xOffsetChar". Then it calls certain X function which retrieves the value. But the poor X function is utterly confused with this and usually won't return a match, no matter what you try to do. Even worse are terminal emulators which can open several windows from a single process, but then they give them different names, such as "Terminal.0", "Terminal.1" and so on. In case when X resource matching function doesn't return a match, slrnface will take a look at the command line arguments and use the values set there[1]. That might be good enough for you. If you're using slrnface with slrn, you can just edit the S-Lang script and add command line options for the slrnface invocation. If you want to make it work with different broken terminals, you'll have to read them from a file, I suppose. In case you are using slrnface with mutt, you'll have to edit the mutt patch and recompile mutt. In any case, don't forget to send the bug report the the terminal maintainers. [1] Normally, that same X function takes care of the command line arguments too, so X applications don't have to process them, but the poor function gets so much confused with this business that it completely ignores them. So slrnface code base had to grow somewhat. 4. slrn and slrnface don't agree about color shades Suppose you configured slrn to show certain kind of text in red and you configured slrnface to paint X-Faces in red. Chances are that those two red colors won't look the same. Almost, but not quite. Color terminals usually have 8 or 16 colors. There is no good reason for this limitation and terminfo capability base provides ways for a terminal to advertise most of the features you can think of, but most terminal emulators just don't implement nice features. Sometimes users can configure the colors, but not always. When you can configure them, it's usually by using X resources or by storing the values in some file. If you don't configure them, the terminal will use hard-coded fallback values. If the color values are in a file, slrnface has no way to know which file and what the format might be. If the X resources are used, slrnface has no way to know the names of those resources. If the user hasn't configured anything, slrnface has no way to know which defaults were used when the terminal application was compiled. So slrnface cannot adjust itself to the terminal. Instead, slrnface is using the color database which is supposed to be used by all X applications. The mapping from color names to RGB values is usually stored in a file called rgb.txt[2], somewhere in your X directories. X server knows where it is and all applications are supposed to ask X server about the mapping, so all applications should show the same shade when the user asks for "red". If you can configure the colors used by your terminal, you have a good chance that "red" will mean the same shade it means to slrnface. If you keep using the defaults, tough luck. [2] Some systems also use a file called Xcms.txt for device independent color schemes, but that also doesn't help us. slrnface-2.1.1/slrnface.sl0100644002170200001450000001265607531636415014775 0ustar davearsdigit% slrnface - feeble attempt at delivering X-Faces to slrn users % Copyright (C) 2000, 2001, 2002 Drazen Kacar % % 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. implements ("slrnface"); variable fp; % Pipe to slrnface define clear () { % This if is in case somebody's been messing with our private % variables. It shouldn't happen, but you never know. if (fp != NULL) { fputs ("clear\n", fp); fflush (fp); } } define read () { variable xface; if (fp != NULL) { xface = extract_article_header ("X-Face"); if (xface != "") fputs ("xface " + xface + "\n", fp); else clear (); fflush (fp); } } define my_hide_article () { % Too bad slrn doesn't have a way to return function bound to a % certain key. If some other macro package binds its own function to % the key 'h' it will never be called, or this function will never be % called, depending on who made definekey call last. % % And this functionality doesn't have to be bound to 'h' in the first % place. We'd actually like to know which key will call hide_article % and then bind this function to that key. A hook would serve the % purpose better, though. call ("hide_article"); if (is_article_visible ()) read (); else { if (fp != NULL) { fputs ("clear\n", fp); fflush (fp); } } } define startup () { variable success, home, reason, envvar; fp = NULL; home = make_home_filename (".slrnfaces"); if (home == NULL) { message_now ("Cannot run slrnface: home directory undefined."); return; } % If DISPLAY is not set slrnface can't run, but the user probably % knows that, so fail silently. envvar = getenv ("DISPLAY"); if (envvar == NULL) return; % On the other hand, yell if WINDOWID is not set. envvar = getenv ("WINDOWID"); if (envvar == NULL) { message_now ("Cannot run slrnface: $WINDOWID undefined."); return; } if (mkdir (home, 0700) != 0) { if (errno != EEXIST) { reason = "cannot create " + home + ": " + errno_string(errno); message_now ("Cannot run slrnface: " + reason); return; } } else { variable fpm, message; fpm = fopen (home + "/README", "w"); if (fpm != NULL) { message = "This directory is used to create named pipes for communication between\n" + "slrnface and its parent process. It should normally be empty because\n" + "the pipe is deleted right after it has been opened by both processes.\n\n" + "File names generated by slrnface have the form \"hostname.pid\". It is\n" + "probably an error if they linger here longer than a fraction of a second.\n" + "\nHowever, if the directory is mounted from an NFS server, you might see\n" + "special files created by your NFS server while slrnface is running.\n" + "Do not try to remove them.\n"; fputs (message, fpm); fclose (fpm); } } % This is our FIFO. home = home + "/" + uname().nodename + "." + string (getpid ()); % If it already exists, we'll delete it first. Because it really % shouldn't exist. remove (home); if (mkfifo (home, 0600) != 0) { reason = "cannot create FIFO: " + errno_string(errno); message_now ("Cannot run slrnface: " + reason); return; } success = system ("slrnface " + home) shr 8; switch (success) { case 0: fp = fopen (home, "w"); if (fp != NULL) { fputs ("start\n", fp); fflush (fp); remove (home); % Seems everything's peachy keen, so we can register hooks. register_hook ("followup_hook", "slrnface->clear"); register_hook ("forward_hook", "slrnface->clear"); register_hook ("group_mode_hook", "slrnface->clear"); register_hook ("post_hook", "slrnface->clear"); register_hook ("reply_hook", "slrnface->clear"); register_hook ("supersede_hook", "slrnface->clear"); register_hook ("read_article_hook", "slrnface->read"); definekey ("slrnface->my_hide_article", "h", "article"); return; } else reason = "cannot open " + home + ": " + errno_string(errno); } % case 1 means DISPLAY is set, but slrnface couldn't connect. Probably % because X server refused. We need to inform the user about this. { case 1: reason = "couldn't connect to display."; } { case 2: reason = "WINDOWID not found in environment."; } { case 3: reason = "couldn't find controlling terminal."; } { case 4: reason = "terminal doesn't export width and height."; } { case 5: reason = "cannot open " + home + " as FIFO."; } { case 6: reason = "fork() failed."; } { reason = "error " + string (success) + " (unknown reason)."; } % Delete the FIFO. remove (home); message_now ("Slrnface failed: " + reason); } register_hook ("startup_hook", "slrnface->startup");