diff -u -d -r -N -P 8.02/ButtonBox.c 9.00/ButtonBox.c --- 8.02/ButtonBox.c Fri Dec 22 05:27:21 1995 +++ 9.00/ButtonBox.c Thu Jun 5 07:11:34 1997 @@ -1,11 +1,20 @@ -#include -#include -#include -#include -#include +#ifdef MOTIF +# include +# include +# include +# include +#else +# include +# include +# include +# include +# include +#endif #include "config.h" +#include "utils.h" #include "xrn.h" +#include "ButtonBox.h" Widget ButtonBoxCreate(name, parent) String name; @@ -13,12 +22,25 @@ { Widget w; +#ifdef MOTIF + w = XtVaCreateWidget(name, xmRowColumnWidgetClass, parent, + XmNpacking, XmPACK_TIGHT, + XmNorientation, XmHORIZONTAL, + XmNallowResize, True, + XmNskipAdjust, True, + /* nothing for motif here, i think - kb + XtNresizeToPreferred, True, + XtNshowGrip, False, + */ + 0); +#else w = XtVaCreateWidget(name, boxWidgetClass, parent, XtNallowResize, True, XtNresizeToPreferred, True, XtNshowGrip, False, XtNskipAdjust, True, 0); +#endif return w; } @@ -30,8 +52,13 @@ { Widget w; +#ifdef MOTIF + w = XtVaCreateManagedWidget(name, xmPushButtonWidgetClass, parent, + XmNactivateCallback, callbacks, 0); +#else w = XtVaCreateManagedWidget(name, commandWidgetClass, parent, XtNcallback, callbacks, 0); +#endif return w; } @@ -39,17 +66,9 @@ void ButtonBoxDoneAdding(w) Widget w; { - XtWidgetGeometry intended, ret; - + if (XtIsRealized(XtParent(w))) + XtRealizeWidget(w); XtManageChild(w); - - /* - I'm not really sure why this is necessary, but it is. - */ - XtVaGetValues(w, XtNwidth, &intended.width, 0); - intended.request_mode = CWWidth | XtCWQueryOnly; - XtQueryGeometry(w, &intended, &ret); - XtVaSetValues(w, XtNheight, ret.height, 0); } void ButtonBoxDestroy(w) diff -u -d -r -N -P 8.02/Buttons.c 9.00/Buttons.c --- 8.02/Buttons.c Wed Dec 31 19:00:00 1969 +++ 9.00/Buttons.c Sun Jun 29 13:30:11 1997 @@ -0,0 +1,719 @@ + +#if !defined(lint) && !defined(SABER) && !defined(GCC_WALL) +static char XRNrcsid[] = "$Id: Buttons.c,v 1.4 1997/06/29 17:30:11 jik Exp $"; +#endif + +/* + * xrn - an X-based NNTP news reader + * + * Copyright (c) 1988-1993, Ellen M. Sentovich and Rick L. Spickelmier. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, 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 the University of California not + * be used in advertising or publicity pertaining to distribution of + * the software without specific, written prior permission. The University + * of California makes no representations about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + * + * THE UNIVERSITY OF CALIFORNIA DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA 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. + */ + +/* + * buttons.c: create and handle the buttons + * + */ + +#include "copyright.h" +#include "config.h" +#include "utils.h" +#include +#include + +#ifdef MOTIF +# include +# include +#else +# include +# include +# include +# include +# include +# include +# include +#endif + +#include "compose.h" +#include "cursor.h" +#include "mesg.h" +#include "dialogs.h" +#include "modes.h" +#include "resources.h" +#include "news.h" +#include "internals.h" +#include "save.h" +#include "xmisc.h" +#include "error_hnds.h" +#include "xthelper.h" +#include "xrn.h" +#include "cancel.h" +#include "buttons.h" +#include "butdefs.h" +#include "mesg_strings.h" +#include "newsrcfile.h" +#include "butexpl.h" +#include "Text.h" +#include "Frame.h" +#include "ButtonBox.h" +#include "ngMode.h" +#include "addMode.h" +#include "artMode.h" +#include "allMode.h" +#include "InfoLine.h" + +#ifndef O_RDONLY +#define O_RDONLY 0 +#endif + +/* Action to take when a confirmation box is clicked in */ +static void (*ConfirmAction) _ARGUMENTS((void)); + + +int CurrentMode = NO_MODE; /* current mode */ +int PreviousMode = NO_MODE; /* previous mode, what buttons to */ + /* remove */ + +#define XRN_NO 0 +#define XRN_YES 1 + +/* the user is in a command - eat type ahead */ +int inCommand = 0; +int inSubCommand = 0; + +void doTheRightThing _ARGUMENTS((Widget, XEvent *,String *,Cardinal *)); +void doPrefetch _ARGUMENTS((Widget, XEvent *, String *, Cardinal *)); + +static XtActionsRec TopActions[] = { + {"doTheRightThing", doTheRightThing}, + {"doPrefetch", doPrefetch}, +}; + +static char TopNonButtonInfo[LABEL_SIZE]; +static char BottomNonButtonInfo[LABEL_SIZE]; + + +/* + * handle the Enter and Leave events for the buttons + * + * upon entering a button, get it's info string and put in the Question label + * upon leaving a button, restore the old info string + * + */ +/*ARGSUSED*/ +#if XtSpecificationRelease > 4 +static void topInfoHandler _ARGUMENTS((Widget, XtPointer, XEvent *, + Boolean *)); + +static void topInfoHandler(widget, client_data, event, dispatch) +#else +static void topInfoHandler _ARGUMENTS((Widget, XtPointer, XEvent *)); + +static void topInfoHandler(widget, client_data, event) +#endif /* XtSpecificationRelease > 4 */ + Widget widget; + XtPointer client_data; + XEvent *event; +#if XtSpecificationRelease > 4 + Boolean *dispatch; +#endif /* XtSpecificationRelease > 4 */ +{ + if (event->type == LeaveNotify) + INFO(TopNonButtonInfo); + else if (event->type == EnterNotify) + INFO(client_data); + + return; +} + +/* + * handle the Enter and Leave events for the buttons + * + * upon entering a button, get it's info string and put in the Question label + * upon leaving a button, restore the old info string + * + */ +/*ARGSUSED*/ +#if XtSpecificationRelease > 4 +static void bottomInfoHandler _ARGUMENTS((Widget, XtPointer, XEvent *, + Boolean *)); + +static void bottomInfoHandler(widget, client_data, event, dispatch) +#else +static void bottomInfoHandler _ARGUMENTS((Widget, XtPointer, XEvent *)); + +static void bottomInfoHandler(widget, client_data, event) +#endif /* XtSpecificationRelease > 4 */ + Widget widget; + XtPointer client_data; + XEvent *event; +#if XtSpecificationRelease > 4 + Boolean *dispatch; +#endif /* XtSpecificationRelease > 4 */ +{ + char *s = NULL; + + if (! BottomInfoLine) + return; + + /* kb - force thru InfoLineSet() instead of set-values */ + if (event->type == LeaveNotify) + s = BottomNonButtonInfo; + else if (event->type == EnterNotify) + s = client_data; + + if (s) + InfoLineSet(BottomInfoLine, s); + + return; +} + +static void setTopInfoLineHandler _ARGUMENTS((Widget, char *)); + +static void setTopInfoLineHandler(widget, message) + Widget widget; + char *message; +{ + XtAddEventHandler(widget, + (EventMask) (EnterWindowMask|LeaveWindowMask), + False, + (XtEventHandler) topInfoHandler, + (XtPointer) message); + return; +} + + +static void setBottomInfoLineHandler _ARGUMENTS((Widget, char *)); + +static void setBottomInfoLineHandler(widget, message) + Widget widget; + char *message; +{ + XtAddEventHandler(widget, + (EventMask) (EnterWindowMask|LeaveWindowMask), + False, + (XtEventHandler) bottomInfoHandler, + (XtPointer) message); + return; +} + + +#ifdef SWITCH_TOP_AND_BOTTOM +#define setTopInfoLine setBottomInfoLine +#endif + +void setTopInfoLine(message) + char *message; +{ + INFO(message); + (void) strcpy(TopNonButtonInfo, message); + return; +} + +#undef setTopInfoLine + +#ifdef SWITCH_TOP_AND_BOTTOM +#define setBottomInfoLine setTopInfoLine +#endif + +void setBottomInfoLine(message) + char *message; +{ + (void) strcpy(BottomNonButtonInfo, message); + + if (! BottomInfoLine) + return; + + InfoLineSet(BottomInfoLine, message); + return; +} + +#undef setBottomInfoLine + +void setButtonSensitive( + _ANSIDECL(Widget, box), + _ANSIDECL(char *, name), + _ANSIDECL(Boolean, sensitive) + ) + _KNRDECL(Widget, box) + _KNRDECL(char *, name) + _KNRDECL(Boolean, sensitive) +{ + Widget w; + + if (! (w = XtNameToWidget(box, name))) + return; + + XtSetSensitive(w, sensitive); +} + +/* + NOTE: The named button MUST APPEAR in the specified button list. + */ +void setButtonActive( + _ANSIDECL(ButtonList *, list), + _ANSIDECL(char *, name), + _ANSIDECL(Boolean, active) + ) + _KNRDECL(ButtonList *, list) + _KNRDECL(char *, name) + _KNRDECL(Boolean, active) +{ + while (strcmp(list->name, name)) + list++; + list->active = active; +} + +void doButtons(resource, box, buttonList, size, infoLine) + char *resource; + Widget box; + ButtonList *buttonList; + int *size; + int infoLine; +{ + char *ptr, *token; + int j, i = 0; + Widget button; + + if (resource) { + ptr = resource; + + while ((token = strtok(ptr, ", \t\n")) != NIL(char)) { + /* find name */ + for (j = 0; j < *size; j++) { + if (STREQ(token, (char *) buttonList[j].name)) { + if (buttonList[j].active) { + button = ButtonBoxAddButton(buttonList[j].name, + buttonList[j].callbacks, box); + if (infoLine == TOP) { + setTopInfoLineHandler(button, buttonList[j].message); + } else { + setBottomInfoLineHandler(button, + buttonList[j].message); + } + i++; + } + break; + } + } + if (j == *size) { + mesgPane(XRN_SERIOUS, 0, BAD_BUTTON_NAME_MSG, token); + } + ptr = NIL(char); + } + *size = i; + + } else { + for (i = 0; i < *size; i++) { + if (buttonList[i].active) { + button = ButtonBoxAddButton(buttonList[i].name, + buttonList[i].callbacks, box); + if (infoLine == TOP) { + setTopInfoLineHandler(button, buttonList[i].message); + } else { + setBottomInfoLineHandler(button, buttonList[i].message); + } + } + } + } + ButtonBoxDoneAdding(box); + return; +} + + +void createButtons() +{ + XtAppAddActions(TopContext, TopActions, XtNumber(TopActions)); + XtAppAddActions(TopContext, AllActions, AllActionsCount); + XtAppAddActions(TopContext, NgActions, NgActionsCount); + XtAppAddActions(TopContext, ArtActions, ArtActionsCount); + XtAppAddActions(TopContext, AddActions, AddActionsCount); + + return; +} + + +void hideGenericWidgets() +{ + DestroyMainFrame(); +} + +void swapMode() +/* + * change the buttons displayed in the TopButtonBox (switch modes) + */ +{ + if (PreviousMode == CurrentMode) { + return; + } + + /* + * NONE -> ADD + * manage add in top box + * manage art in bottom box + * desensitize bottom box + * install add actions in top box + */ + if ((PreviousMode == NO_MODE) && (CurrentMode == ADD_MODE)) { + hideGenericWidgets(); + + displayAddWidgets(); + /* + * NONE -> NG + * manage ng in top box + * manage art in bottom box + * desensitize bottom box + * install ng actions in top box + */ + } else if ((PreviousMode == NO_MODE) && (CurrentMode == NEWSGROUP_MODE)) { + hideGenericWidgets(); + + displayNewsgroupWidgets(); + /* + * ADD -> NG + * unmanage add in top box + * manage ng in top box + * install ng actions in top box + */ + } else if ((PreviousMode == ADD_MODE) && (CurrentMode == NEWSGROUP_MODE)) { + hideAddWidgets(); + + displayNewsgroupWidgets(); + /* + * NG -> ART + * unmanage ng in top box + * manage art in top box + * sensitize bottom box + * install art actions in top box + * install art actions in bottom box + */ + } else if ((PreviousMode == NEWSGROUP_MODE) && (CurrentMode == ARTICLE_MODE)) { + hideNewsgroupWidgets(); + + displayArticleWidgets(); + /* + * NG -> ADD + * unmanage ng in top box + * manage add in top box + * install add actions in top box + */ + } else if ((PreviousMode == NEWSGROUP_MODE) && (CurrentMode == ADD_MODE)) { + hideNewsgroupWidgets(); + + displayAddWidgets(); + /* + * NG -> ALL + * unmanage ng in top box + * unmanage ng in bottom box + * manage all in bottom box + * sensitize bottom box + * install all actions in bottom box + */ + } else if ((PreviousMode == NEWSGROUP_MODE) && (CurrentMode == ALL_MODE)) { + hideNewsgroupWidgets(); + + displayAllWidgets(); + /* + * ART -> NG + * desensitize bottom box + * unmanage art in top box + * manage ng in top box + * install ng actions in top box + */ + } else if ((PreviousMode == ARTICLE_MODE) && (CurrentMode == NEWSGROUP_MODE)) { + hideArticleWidgets(); + + displayNewsgroupWidgets(); + /* + * ALL -> NG + * manage ng in top box + * unmanage all in bottom box + * manage art in bottom box + * desensitize bottom box + */ + } else if ((PreviousMode == ALL_MODE) && (CurrentMode == NEWSGROUP_MODE)) { + hideAllWidgets(); + + displayNewsgroupWidgets(); + /* + * ART -> ALL (going back to previous ALL_MODE) + * unmanage art in bottom box + * unmanage art in top box + * manage all in bottom box + * manage ng in top box + * desensitize top box + * install all actions in bottom box + */ + } else if ((PreviousMode == ARTICLE_MODE) && (CurrentMode == ALL_MODE)) { + hideArticleWidgets(); + + displayAllWidgets(); + /* + * ALL -> ART + * manage art in top box + * unmanage all in bottom box + * manage art in bottom box + * install art actions in bottom box + */ + } else if ((PreviousMode == ALL_MODE) && (CurrentMode == ARTICLE_MODE)) { + hideAllWidgets(); + + displayArticleWidgets(); + } else { + (void) sprintf(error_buffer, ERROR_UNSUP_TRANS_MSG , + PreviousMode, CurrentMode); + ehErrorExitXRN(error_buffer); + } + + return; +} + +static int XRNAbort = 0; + +int abortP() +{ + xthHandleAllPendingEvents(); + return XRNAbort; +} + +void abortSet() +{ + XRNAbort = 1; + return; +} + +void abortClear() +{ + XRNAbort = 0; + return; +} + + + +/*ARGSUSED*/ +void doTheRightThing(widget, event, string, count) + Widget widget; + XEvent *event; + String *string; + Cardinal *count; +{ + if (inCommand) { + return; + } + inCommand = 1; + xrnBusyCursor(); + switch (CurrentMode) { + case ALL_MODE: + allDoTheRightThing(widget, event, string, count); + break; + + case NEWSGROUP_MODE: + ngDoTheRightThing(widget, event, string, count); + break; + + case ARTICLE_MODE: + artDoTheRightThing(widget, event, string, count); + break; + } + xrnUnbusyCursor(); + inCommand = 0; + return; +} + + + +Boolean watchingGroup(newsgroup) + char *newsgroup; +{ + static int inited = 0; +#ifdef POSIX_REGEX + static regex_t *GroupList; +#else + static char **GroupList; +#endif + static int GroupListCount; + int p; + + if (! inited) { + GroupList = parseRegexpList(app_resources.watchList, "watchUnread", + &GroupListCount); + inited++; + } + + if (newsgroup == 0) + return False; + if (! GroupList) + return True; + + for (p = 0; p < GroupListCount; p++) { +#ifdef POSIX_REGEX + if (! regexec(&GroupList[p], newsgroup, 0, 0, 0)) + return True; +#else +# ifdef SYSV_REGEX + if (regex(GroupList[p], newsgroup)) + return True; +# else + re_comp(GroupList[p]); + if (re_exec(newsgroup)) + return True; +# endif +#endif + } + + return False; +} + + +String anyIterator( + _ANSIDECL(Widget, w), + _ANSIDECL(String, string), + _ANSIDECL(Boolean, group), + _ANSIDECL(Boolean, start), + _ANSIDECL(Boolean, delete), + _ANSIDECL(long *, out_left) + ) + _KNRDECL(Widget, w) + _KNRDECL(String, string) + _KNRDECL(Boolean, group) + _KNRDECL(Boolean, start) + _KNRDECL(Boolean, delete) + _KNRDECL(long *, out_left) +{ + static char *name = 0; + static long left, right; + Boolean ret; + + if (start) { + ret = TextGetSelectedOrCurrentLines(w, &left, &right); + if (out_left) + *out_left = left; + if (ret) { + TextUnsetSelection(w); + if (! name) + name = XtRealloc(name, 1); + return name; + } + return 0; + } + + if (out_left) + *out_left = left; + + if (left >= right) + return 0; + + if (group) + currentGroup(CurrentMode, string, &name, left); + else { + /* Will we ever have an article number longer than 10 chars long? + Yeah, right. */ + name = XtRealloc(name, 11); + (void) sprintf(name, "%ld", atol(&string[left] + 2)); + } + + if (delete) { + long new = left; + + if (moveCursor(FORWARD, string, &new)) + right -= new - left; + else + right = left; + TextRemoveLine(w, left); + removeLine(string, &left); + } + else if (! moveCursor(FORWARD, string, &left)) + left = right + 1; + return name; +} + +static Widget ConfirmBox = 0; + +/*ARGSUSED*/ +static void generalHandler _ARGUMENTS((Widget, XtPointer, XtPointer)); + +static void generalHandler(widget, client_data, call_data) + Widget widget; + XtPointer client_data; + XtPointer call_data; +{ + if (inCommand) { + return; + } + inCommand = 1; + xrnBusyCursor(); + PopDownDialog(ConfirmBox); + ConfirmBox = 0; + + if ((int) client_data == XRN_YES) + (*ConfirmAction)(); + + xrnUnbusyCursor(); + inCommand = 0; + return; +} + +void confirmBox(message, mode, flag, handler) + String message; + int mode, flag; + void (*handler) _ARGUMENTS((void)); +{ + static struct DialogArg args[] = { + {NO_STRING, generalHandler, (XtPointer) XRN_NO}, + {YES_STRING, generalHandler, (XtPointer) XRN_YES}, + }; + + if (CurrentMode != mode) + return; + + if (app_resources.confirmMode & flag) { + ConfirmAction = handler; + if (! ConfirmBox) { + ConfirmBox = CreateDialog(TopLevel, message, DIALOG_NOTEXT, + args, XtNumber(args)); + PopUpDialog(ConfirmBox); + } + + return; + } + (*handler)(); +} + + +/* + * determine the initial mode and set up Text, TopButtonBox, and Question + */ +void determineMode( + _ANSIDECL(Boolean, do_newgroups) + ) + _KNRDECL(Boolean, do_newgroups) +{ + String string; + + if (do_newgroups && (string = newGroups())) { + switchToAddMode(string); + FREE(string); + } + else + switchToNewsgroupMode(False); + + return; +} diff -u -d -r -N -P 8.02/COMMON-PROBLMS 9.00/COMMON-PROBLMS --- 8.02/COMMON-PROBLMS Mon May 6 07:32:53 1996 +++ 9.00/COMMON-PROBLMS Sun Oct 19 18:28:50 1997 @@ -1,7 +1,38 @@ -$Id: COMMON-PROBLMS,v 1.9 1995/09/05 19:16:27 jik Exp $ +$Id: COMMON-PROBLMS,v 1.12 1997/10/19 22:28:50 jik Exp $ List of Common Problems: + +*. XRN is crashing because of long lines in your newsrc file. + + On some UNIX platforms, the "lex" pre-compiler, which XRN uses to + produce the code which parses newsrc files, has limit on line + lengths which is too small for some lines in newsrc files. + + If XRN crashes or performs unpredictably when you run it, and you + have long lines in your newsrc file (200 characters long is the + most common limit), then you may be running into this problem. If + so, the easiest way to fix it is to install the GNU "flex" package + and use it, rather than lex, when compiling XRN. After installing + flex, you can use it when compiling XRN by adding "LEX=flex" to + the end of the "make" command you use to compile XRN. + +*. XRN compiled and installed just fine, and you can use it with the + mouse, but none of the key bindings work. + + You are using an old version of the X11 libraries (e.g., you're + using SunOS 4.x). As noted in the README file, XRN requires at + least X11R5 to work properly. You need to get a newer version of + X11 (e.g., the X11R6.1 release from the X Consortium), compile and + install it, and link XRN against it. + +*. You are linking XRN against INN's libraries, and you get errors + which mention "nntp_port" at compile- or run-time. + + You need to either upgrade your INN installation to the current + version or compile XRN without INN support. Note that XRN can + talk to an INN News server just fine, even when it is compiled + without INN support. *. Part of the Subject index occasionally fails to redraw properly in article mode. diff -u -d -r -N -P 8.02/ChangeLog 9.00/ChangeLog --- 8.02/ChangeLog Thu May 9 14:15:45 1996 +++ 9.00/ChangeLog Wed Jan 14 22:03:41 1998 @@ -1,3 +1,186 @@ +1998-01-14 Jonathan I. Kamens + + *** Significant user-visible changes in XRN 9.00 (some changes are + not listed; consult the XRN man page for additional information + about these changes): + + + ** Enhancements: + + * Article threading and sorting + + The article subject list can be sorted by thread, in which case + indentation is used to show thread nesting (see "sortedSubjects" + in the man page). A button/command has been added to find an + article's parent. Articles can be killed by thread or subthread. + + Also, the article subject list can now be sorted by date as well + as by subject. + + Article sorting by subject has been sped up significantly. + + * Improved caching of fetched articles + + Article files are now stored in a rotating article cache whose + size and number of files can be configured by the user (see + "cacheFilesMaxFiles" and "cacheFilesMaxSize" in the man page). + This means that XRN will exit newsgroups more quickly because it + doesn't have to remove all of a newsgroup's article files when + exiting a group. It also means that articles won't be + unnecessarily fetched multiple times from the server (for example, + the current article no longer has to be refetched if you tell XRN + to save or print it). + + * Improved authentication support + + Username/password authentication, using the "AUTHINFO USER" and + "AUTHINFO PASS" NNTP commands, is now supported. A new + compile-time config.h option, ALLOW_RESOURCE_PASSWORDS, controls + whether the user is allowed to encode passwords in X resources. + + * Improved-kill support + + Kill-file entries can now compare against the "Newsgroups", + "Date", "Message-ID", "References", and "Xref" header fields in + addition to the "From" and "Subject" fields. Furthermore, the 'h' + kill-file entry modifier can be used to compare against all of + those fields + + Rather than having separate buttons for "session kill", "local + kill", and "global kill", there is only one kill button for each + field being killed, and different most buttons and/or modifier + keys are used when hitting the button to signify whether the kill + is for this session, the local kill file, or the global kill file. + + Automatic expiration of old kill-file entries has been added. See + "killTimeout" and "KILL FILE FORMAT" in the man page. + + A kill file can now include other kill files. + + * Enhancements to Article mode + + An error message is now displayed when the user attempts to cancel + a message he is not authorized to cancel. + + A new "resort" command has been added, to allow the user to resort + the article list. Also, a button binding of ctrl-o was added to + "unsort" the article list, i.e., to arrange the list the articles + in numerical order. + + The "List old" button in article mode has been modified to allow + the user to specify how far back to list, by holding down the ctrl + key when executing the command. + + A new "artSub" button, which isn't in the default button list but + which has a default key binding of '+', has been added. This + command subscribes the user to the current group if he isn't + already subscribed (i.e., if he entered an unsubscribed group from + All mode). + + * Enhancements to All mode + + A "Search" button and key binding ('/') has been added. + + A "Limit" button and corresponding 'l' binding has been added, to + allow the user to limit the list to only newsgroups matching a + specified regular expression. + + * Enhancements to multiple modes + + Newsgroup and subject list widths are now adjusted automatically + when the XRN window is resized. + + Outgoing messages and postings can now be saved automatically into + a folder. See "saveSentMail" and "saveSentPostings" in the man + page. + + Buttons whose functionality is disabled (e.g., the "Post" button, + when the user is not allowed to post articles to the NNTP server) + are no longer displayed, even if they are listed in a button list. + + A new "validNewsgroups" resource, complementing the old + "ignoreNewsgroups" resource, has been added. + + XRN won't choke on NNTP servers which send multi-line responses + for individual articles to XHDR requests. A server shouldn't do + that, but XRN tries to handle those that do. + + + ** Bug fixes: + + * Don't falsly claim that there are no articles in a newsgroup. + + * Don't hang forever when prefetching a newsgroup with lots of + unread but unavailable articles in it. + + * Don't hang for a long time when the user attempts to visit a + newsgroup that no longer exists with many unread articles in its + active-file cache entry. + + * Don't do anything with the regular expressions of kill-file + entries which contain unknown commands. Previously, such regular + expressions were compiled and compared against all unkilled + articles, even though it didn't know what to do about any articles + that matched. + + * Include "Sender:" in cancel messages when necessary. + + * When an external editor is being used for message composition + and the user aborts the message, save the latest version of the + article, rather than an old version, in the dead.letter file. + + * Handle lines longer than 1024 characters sent by the NNTP + server. + + * Don't process individual articles' "Xref" headers multiple + times. + + * Don't claim that an article was canceled when in fact its cancel + message was mailed to a newsgroup moderator. + + * Inclusion of article text in a followup and reply will now + always work, even if the article was canceled or the user has + exited the newsgroup in which the original article is located. + + * Don't coredump when encountering newsgroups with really long + names. + + + ** Changes in functionality: + + * To improve performance, NNTP_REREADS_ACTIVE_FILE is now defined + by default in config.h. Sites running very old NNTP servers which + do not reread the active file each time the client sends a LIST + command should comment out this definition before compiling XRN. + + * When comparing a kill file entry against the author of an + article, the full contents of the article's "From" line are now + used. Previously, only the portion of the "From" line that was + displayed in the Subject list was used. This means, e.g., that + you can set the authorFullName resource to "True" and still kill + articles by user name, site name, etc. + + * Article mode is now more conservative about when the subject + list is regenerated and which subject lines are added to it. For + example, when you attempt to go to an article that is not + currently displayed in the subject list, only that article, rather + than all articles after it, will be added to the list. + + * The IDENTIFY_VERSION_IN_MESSAGES compile-time configuration + option is now enabled by default. + + * New newsgroups no longer show up after automatic rescans. In + order to discover new newsgroups, you have to execute the rescan + command explicitly. + + * When displayLocalTime is true and the user asks to see the full + article header, the article's actual "Date" field, rather than the + converted date, is now displayed. + + * Conversion of backspace characters, pagebreak characters, or the + "Date" field no longer takes place when an article is being saved + or piped. + Thu May 9 17:48:38 1996 Jonathan Kamens *** Significant user-visible changes in XRN 8.02: @@ -891,4 +1074,4 @@ Always include article text when calling an external editor command. Fix from per@erix.ericsson.se (Per Hedeland). -# $Id: ChangeLog,v 1.80 1996/05/09 18:15:25 jik Exp $ +# $Id: ChangeLog,v 1.122 1998/01/15 03:03:41 jik Exp $ diff -u -d -r -N -P 8.02/Frame.c 9.00/Frame.c --- 8.02/Frame.c Wed Dec 31 19:00:00 1969 +++ 9.00/Frame.c Thu Jun 5 07:11:34 1997 @@ -0,0 +1,85 @@ +/* Frame.c */ +/* and some general X code from xrn.c for the main screen */ + +#ifdef MOTIF +# include +# include +#else +# include +# include +# include +# include +# include +# include +#endif + +#include "compose.h" +#include "error_hnds.h" + +static Widget Frame; +static Arg frameArgs[] = { /* main window description */ + {XtNx, (XtArgVal) 10}, + {XtNy, (XtArgVal) 10}, + {XtNheight, (XtArgVal) 800}, + {XtNwidth, (XtArgVal) 680}, +}; + +void +GetMainFrameSize(Widget shell, char *geometry) +{ + int bmask; + bmask = XParseGeometry(geometry, /* geometry specification */ + (int *) &frameArgs[0].value, /* x */ + (int *) &frameArgs[1].value, /* y */ + (unsigned int *) &frameArgs[3].value, /* width */ + (unsigned int *) &frameArgs[2].value); /* height */ + + /* handle negative x and y values */ + if ((bmask & XNegative) == XNegative) { + frameArgs[0].value += (XtArgVal) DisplayWidth(XtDisplay(shell), + DefaultScreen(XtDisplay(shell))); + frameArgs[0].value -= (int) frameArgs[3].value; + } + if ((bmask & YNegative) == YNegative) { + frameArgs[1].value += (XtArgVal) DisplayHeight(XtDisplay(shell), + DefaultScreen(XtDisplay(shell))); + frameArgs[1].value -= (int) frameArgs[2].value; + } +} + +Widget +CreateMainFrame(Widget shell) +{ + Frame = XtCreateManagedWidget("vpane", +#ifdef MOTIF + xmPanedWindowWidgetClass, +#else + panedWidgetClass, +#endif + shell, frameArgs, XtNumber(frameArgs)); + return (Frame); +} + +void +DestroyMainFrame(void) +{ + XtDestroyWidget(Frame); +} + +/*===========================================================================*/ + +void +XrnAddInput(XtAppContext app_context, int source) +{ + XtAppAddInput(app_context, source, (XtPointer)XtInputReadMask, + processMessage, (XtPointer) 0); +} + +void +XrnAddCloseCallbacks(Widget shell) +{ +#if XtSpecificationRelease > 5 + XtAddCallback(shell, XtNsaveCallback, saveNewsrcCB, NULL); + XtAddCallback(shell, XtNdieCallback, ehDieCB, NULL); +#endif /* X11R6 or greater */ +} diff -u -d -r -N -P 8.02/Frame.h 9.00/Frame.h --- 8.02/Frame.h Wed Dec 31 19:00:00 1969 +++ 9.00/Frame.h Thu Jun 5 07:11:34 1997 @@ -0,0 +1,13 @@ +/* Frame.h */ + +#ifndef FRAME_H +#define FRAME_H + +Widget CreateMainFrame _ARGUMENTS((Widget shell)); +void DestroyMainFrame _ARGUMENTS((void)); +void GetMainFrameSize _ARGUMENTS((Widget Shell, char *geometry)); + +void XrnAddInput _ARGUMENTS((XtAppContext app_context, int source)); +void XrnAddCloseCallbacks _ARGUMENTS((Widget shell)); + +#endif /* FRAME_H */ diff -u -d -r -N -P 8.02/Imakefile 9.00/Imakefile --- 8.02/Imakefile Thu May 2 08:10:57 1996 +++ 9.00/Imakefile Thu Jan 15 21:57:57 1998 @@ -1,5 +1,5 @@ /* - * $Id: Imakefile,v 1.97 1996/05/02 12:10:37 jik Exp $ + * $Id: Imakefile,v 1.123 1998/01/16 02:57:57 jik Exp $ * * xrn - an X-based NNTP news reader * @@ -31,13 +31,27 @@ */ /* + * If you want to compile the MOTIF version of XRN, define MOTIF here. + * + * *** NOTE WELL *** that the Motif version of XRN is not yet + * completely implemented, it probably doesn't work properly, and it's + * unsupported. If you want to help finish it, please let me know. + * Otherwise, you probably shouldn't try to compile it. + */ +/* #define MOTIF */ + +/* * If you want the program to be called something other than "xrn", * change PROGRAMS. If you want the app-defaults class to be something * other than XRn, change APPDEFAULTS. */ PROGRAMS = xrn +#ifdef MOTIF +APPDEFAULTS = XRn_Motif +#else APPDEFAULTS = XRn +#endif /* * What language do you want messages to be in? Legal values are @@ -71,7 +85,7 @@ * system. If in doubt, leave it alone and see what happens. */ -#if defined(XmacIIServer) || defined(HPArchitecture) || defined(SGIArchitecture) +#if defined(XmacIIServer) || defined(SGIArchitecture) XRN_LOAD_FLAGS = -lPW #endif @@ -104,14 +118,29 @@ * You probably won't need to change most of this. */ -X_LIBRARIES = XawClientLibs -DEPLIBS = XawClientDepLibs +#ifdef MOTIF +# ifndef XMLIB + XMLIB = -lXm +# endif + # set this to whatever your system needs, might should put this above + # in the O.S. sections + XM_LIB_PATH = -L/usr/dt/lib + X_LIBRARIES = $(XM_LIB_PATH) $(XMLIB) $(XAWLIB) $(XMULIB) $(XTOOLLIB) $(XLIB) $(XMEXTRA_LIBS) + DEPLIBS = $(DEPXMLIB) $(DEPXAWLIB) $(DEPXTOOLLIB) $(DEPXLIB) +#else + X_LIBRARIES = XawClientLibs + DEPLIBS = XawClientDepLibs +#endif /* * Special compiler flags for your platform. If in doubt, just leave * them alone and see what happens. */ +#if defined(HPArchitecture) +XRN_DEFINES = -D_HPUX_SOURCE +#endif /* HPArchitecture */ + #if defined(aiws) XRN_DEFINES = -a -Nn3000 -Nd4000 #else @@ -133,28 +162,18 @@ */ #ifdef OV_CAMBRIDGE -SITE_DEFINES += -DOV_CAMBRIDGE +LEX = flex +SITE_DEFINES += -DOV_CAMBRIDGE -I. IMAKE_DEFINES += -DOV_CAMBRIDGE -#ifdef LinuxArchitecture -#define INN /usr/build/inn-1.4sec/libinn.a -INNINC += -I/usr/build/inn-1.4sec/include -LEX = flex -CDEBUGFLAGS += -g3 -#else -# ifdef SolarisArchitecture -CC = gcc -fpcc-struct-return -CCOPTIONS = -OPENWINHOME = /usr/openwin -CONFIGDIR = $(OPENWINHOME)/lib/config -# else -#define INN /afs/gza.com/development/news/build/inn/libinn.a -INNINC += -I/afs/gza.com/development/news/build/inn/include -# endif /* ! SolarisArchitecture */ -#endif /* LinuxArchitecture */ +ifeq ($(findstring gcc,$(CC)),gcc) CDEBUGFLAGS = -g -Wall -DGCC_WALL -pedantic -ifdef PURIFY -CC = purify -ignore-signals=SIGPIPE gcc -fpcc-struct-return endif +REGEX_OBJS = /tools/lib/gnuregex.o + +depend:: regex.h + +regex.h: /tools/include/gnuregex.h + ln -s /tools/include/gnuregex.h regex.h #endif /* OV_CAMBRIDGE */ #ifdef FreeBSDArchitecture @@ -163,11 +182,11 @@ #ifdef LINUX_DIST IMAKE_DEFINES += -DLINUX_DIST -SITE_DEFINES += -I/usr/build/regex-0.12 -SITE_DEFINES += -DCONFIG_H_IS_OK +SITE_DEFINES += -I/b/build/regex-0.12 +SITE_DEFINES += -DCONFIG_H_IS_OK -D_GNU_SOURCE LEX = flex CDEBUGFLAGS += -Wall -DGCC_WALL -g -pedantic -REGEX_OBJS = /usr/build/regex-0.12/regex.o +REGEX_OBJS = /b/build/regex-0.12/regex.o #endif #if defined(INN) @@ -184,34 +203,42 @@ * You probably don't need to edit anything below this line. */ +#ifdef MOTIF +GUI_DEFINES = -DMOTIF +#endif + #if HasVoidSignalReturn SIGNAL_DEFINES = -DVOID_SIGNAL #endif LOCAL_LIBRARIES = $(X_LIBRARIES) $(XRN_LOAD_FLAGS) $(INN_LOAD_FLAGS) -DEFINES = $(XRN_DEFINES) $(SIGNAL_DEFINES) $(INN_DEFINES) $(SITE_DEFINES) \ - -DXRN_LANG_$(LANGUAGE) -DXRN_APP_CLASS=\"$(APPDEFAULTS)\" -DXRN +DEFINES = $(XRN_DEFINES) $(GUI_DEFINES) $(SIGNAL_DEFINES) $(INN_DEFINES) \ + $(SITE_DEFINES) -DXRN_LANG_$(LANGUAGE) \ + -DXRNAPPDIR=\"$(XAPPLOADDIR)\" \ + -DXRN_APP_CLASS=\"$(APPDEFAULTS)\" -DXRN HDRS = mesg_strings.h -SRCS = avl.c buttons.c $(CLIENTSRC) compose.c cursor.c dialogs.c \ +SRCS = avl.c $(CLIENTSRC) compose.c cursor.c dialogs.c \ refile.c mesg.c error_hnds.c newsrcfile.c resources.c \ - internals.c save.c server.c utils.c xmisc.c xrn.c \ - xthelper.c y.tab.c cancel.c vprintf.c tempnam.c strstr.c \ + internals.c save.c server.c utils.c xrn.c \ + xthelper.c rcyacc.c cancel.c vprintf.c tempnam.c strstr.c \ strtok.c mesg_strings.c Text.c ngMode.c artMode.c allMode.c \ - addMode.c snapshot.c artstruct.c InfoLine.c ButtonBox.c \ - busyCursor.c varfile.c activecache.c -LOCAL_OBJS = avl.o buttons.o $(CLIENTOBJ) compose.o cursor.o dialogs.o \ + addMode.c snapshot.c artstruct.c Buttons.c InfoLine.c ButtonBox.c \ + busyCursor.c varfile.c activecache.c sort.c getdate2.c hash.c \ + killfile.c InfoDialog.c Frame.c Xmisc.c file_cache.c +LOCAL_OBJS = avl.o $(CLIENTOBJ) compose.o cursor.o dialogs.o \ refile.o mesg.o error_hnds.o newsrcfile.o resources.o \ - internals.o save.o server.o utils.o xmisc.o xrn.o \ - xthelper.o y.tab.o cancel.o vprintf.o tempnam.o strstr.o \ + internals.o save.o server.o utils.o xrn.o \ + xthelper.o rcyacc.o cancel.o vprintf.o tempnam.o strstr.o \ strtok.o mesg_strings.o Text.o ngMode.o artMode.o allMode.o \ - addMode.o snapshot.o artstruct.o InfoLine.o ButtonBox.o \ - busyCursor.o varfile.o activecache.o + addMode.o snapshot.o artstruct.o Buttons.o InfoLine.o ButtonBox.o \ + busyCursor.o varfile.o activecache.o sort.o getdate2.o hash.o \ + killfile.o InfoDialog.o Frame.o Xmisc.o file_cache.o OBJS = $(LOCAL_OBJS) $(REGEX_OBJS) all:: mesg_strings.h -depend:: lex.yy.c y.tab.c mesg_strings.h +depend:: rclex.c rcyacc.c mesg_strings.h getdate2.c ComplexProgramTarget($(PROGRAMS)) InstallAppDefaults($(APPDEFAULTS)) @@ -232,26 +259,20 @@ all:: mesg_strings.h #if HasSaberC == YES -# saber_xrn:: lex.yy.c -# osaber_xrn:: lex.yy.c +# saber_xrn:: rclex.c +# osaber_xrn:: rclex.c #endif /* * If you are using flex instead of lex (for example, Linux uses flex * by default instead of lex), you should NOT be giving flex the "-l" - * argument. If you see "flex -l newsrc.l" or "lex -l newsrc.l" when + * argument. If you see "flex -l rclex.l" or "lex -l rclex.l" when * building, then you should probably add "LEX=flex" or "LEX=lex" * below. */ -lex.yy.c: newsrc.l - $(LEX) newsrc.l - -y.tab.c: newsrc.y - -rm -f y.tab.c newsrc.tab.c - $(YACC) newsrc.y - (test -f newsrc.tab.c && mv newsrc.tab.c y.tab.c) || true +rclex.c: rclex.l -y.tab.o: lex.yy.c avl.h news.h newsrcfile.h utils.h +rcyacc.o: rclex.c avl.h news.h newsrcfile.h utils.h mesg_strings.h: mesg_strings.c mesg_str.awk -rm -f mesg_str.tmp @@ -266,12 +287,11 @@ $(LOCAL_OBJS): config.h clean:: - $(RM) y.tab.c lex.yy.c mesg_strings.h mesg_str.tmp + $(RM) mesg_strings.h mesg_str.tmp xrn-man.c: xrn-man.sym config.h $(RM) xrn-man.tmp xrn-man.c - awk 'BEGIN {printf("#include \"config.h\"\n");} \ - {printf("#ifdef %s\nx%sx value %s value\n#else\nx%sx\n#endif\n",$$0,$$0,$$0,$$0);}' \ + awk 'BEGIN {printf("#include \"config.h\"\n");} {printf("#ifdef %s\nx%sx value %s value\n#else\nx%sx\n#endif\n",$$0,$$0,$$0,$$0);}' \ xrn-man.sym > xrn-man.tmp mv xrn-man.tmp xrn-man.c @@ -303,9 +323,24 @@ clean:: $(RM) xrn-man.tmp xrn-man.c xrn-man.cpp xrn-man.sed $(PROGRAMS).man +#ifdef MOTIF +TKSED= -e 's/MOTIF//' -e '/XAW/d' -e 's/notify()/Activate()/' \ + -e 's/unset()/Disarm()/' -e 's/set()/Arm()/' \ + -e 's/\([*.]\)label:/\1labelString:/' \ + -e 's/no-op(RingBell/beep(/' \ + -e 's/\.\([^.]*\)\.baseTranslations/*\1*translations/' \ + -e 's/baseTranslations/translations/' \ + -e 's/>Down/>osfDown/' -e 's/>Up/>osfUp/' \ + -e 's/>Left/>osfLeft/' -e 's/>Right/>osfRight/' +#else +TKSED= -e 's/XAW//' -e '/MOTIF/d' +#endif +TKSED2= -e 's/XawSet()/set()/' -e 's/XawNotify()/notify()/' \ + -e 's/XawUnSet()/unset()/' + $(APPDEFAULTS).ad: XRn.src -rm -f $(APPDEFAULTS).tmp $(APPDEFAULTS).ad - sed -e 's/^LANG_$(LANGUAGE)//' -e '/^LANG_/d' \ + sed -e 's/LANG_$(LANGUAGE)//' -e '/LANG_/d' $(TKSED) $(TKSED2) \ -e 's/xAPP_CLASSx/$(APPDEFAULTS)/' \ XRn.src > $(APPDEFAULTS).tmp mv $(APPDEFAULTS).tmp $(APPDEFAULTS).ad @@ -314,3 +349,10 @@ clean:: $(RM) $(APPDEFAULTS).ad $(APPDEFAULTS).tmp + +getdate2.c: getdate.c + sed -e 's/yy/getdate_yy/g' getdate.c > $@.tmp + mv $@.tmp $@ + +clean:: + $(RM) getdate.c getdate2.c.tmp getdate2.c rclex.c rcyacc.c diff -u -d -r -N -P 8.02/InfoDialog.c 9.00/InfoDialog.c --- 8.02/InfoDialog.c Wed Dec 31 19:00:00 1969 +++ 9.00/InfoDialog.c Thu Jun 5 07:11:34 1997 @@ -0,0 +1,169 @@ +/* InfoDialog.c */ + +#ifdef MOTIF +# include +# include +# include +#else +# include +# include +# include +# include +# include +# include +# include +# include +#endif + +#include "butdefs.h" +#include "ButtonBox.h" +#include "InfoLine.h" +#include "Text.h" +#include "InfoDialog.h" +#include "ngMode.h" + +/* entire widget */ +static Widget MesgTopLevel = (Widget) 0; +/* text window */ +static Widget MesgText = (Widget) 0; +/* amount of text currently in the window */ +static long current_length = 0; + + +long +MesgLength() +{ + return (current_length); +} + +#ifdef MOTIF +static void mesgDismissXmCallback _ARGUMENTS((Widget,XtPointer,XtPointer)); +static void mesgClearXmCallback _ARGUMENTS((Widget,XtPointer,XtPointer)); +#else +static void mesgDismissFunction _ARGUMENTS((Widget,XEvent*,String*,Cardinal*)); +static void mesgClearFunction _ARGUMENTS((Widget,XEvent*,String*,Cardinal*)); +BUTTON(mesgDismiss,dismiss); +BUTTON(mesgClear,clear); +#endif + +void +InfoDialogCreate(Widget parent) +{ + Widget pane, buttonBox, label, button; + static Arg shellArgs[] = { + {XtNinput, (XtArgVal) True}, + }; + + if (MesgTopLevel) + return; + +#ifdef MOTIF + MesgTopLevel = XmCreateTemplateDialog(parent, "Information", + shellArgs, XtNumber(shellArgs)); + + pane = XmCreatePanedWindow(MesgTopLevel, "pane", NULL, 0); + label = InfoLineCreate("label", 0, pane); + MesgText = TextCreate("text", True, pane); + + XtAddCallback(MesgTopLevel, XmNcancelCallback, + mesgDismissXmCallback, NULL); + XtAddCallback(MesgTopLevel, XmNokCallback, mesgClearXmCallback, NULL); + + XtRealizeWidget(MesgTopLevel); + XtManageChild(pane); + XtManageChild(MesgTopLevel); +#else + MesgTopLevel = XtCreatePopupShell("Information", topLevelShellWidgetClass, + parent, shellArgs, XtNumber(shellArgs)); + + pane = XtVaCreateManagedWidget("pane", panedWidgetClass, MesgTopLevel, + NULL); + + label = InfoLineCreate("label", 0, pane); + + MesgText = TextCreate("text", True, pane); + + buttonBox = ButtonBoxCreate("box", pane); + + button = ButtonBoxAddButton("mesgDismiss", mesgDismissCallbacks, + buttonBox); + makeDefaultButton(button); + + (void) ButtonBoxAddButton("mesgClear", mesgClearCallbacks, + buttonBox); + + ButtonBoxDoneAdding(buttonBox); + + XtRealizeWidget(MesgTopLevel); + XtSetKeyboardFocus(MesgTopLevel, MesgText); + XtInstallAccelerators(MesgText, button); + + XDefineCursor(XtDisplay(MesgTopLevel), XtWindow(MesgTopLevel), + XCreateFontCursor(XtDisplay(MesgTopLevel), XC_left_ptr)); + + XtPopup(MesgTopLevel, XtGrabNone); +#endif +} + + +void displayMesgString(new_string) + char *new_string; +{ + long newlen = strlen(new_string); + + if (! MesgText) + return; + + TextReplace(MesgText, new_string, newlen, current_length, current_length); + current_length += newlen; + TextSetInsertionPoint(MesgText, current_length); +} + +/*ARGSUSED*/ +#ifdef MOTIF +static void mesgDismissXmCallback(widget, client, cbsp) + Widget widget; + XtPointer client; + XtPointer cbsp; +{ + XtDestroyWidget(XtParent(MesgTopLevel)); + MesgTopLevel = (Widget) 0; + MesgText = (Widget) 0; + current_length = 0; +} + +static void mesgClearXmCallback(widget, client, cbsp) + Widget widget; + XtPointer client; + XtPointer cbsp; +{ + TextClear(MesgText); + current_length = 0; +} + +#else + +static void mesgDismissFunction(widget, event, string, count) + Widget widget; + XEvent *event; + String *string; + Cardinal *count; +{ + XtPopdown(MesgTopLevel); + TextDestroy(MesgText); + XtDestroyWidget(MesgTopLevel); + MesgTopLevel = (Widget) 0; + MesgText = (Widget) 0; + current_length = 0; +} + +static void mesgClearFunction(widget, event, string, count) + Widget widget; + XEvent *event; + String *string; + Cardinal *count; +{ + TextClear(MesgText); + current_length = 0; +} +#endif diff -u -d -r -N -P 8.02/InfoDialog.h 9.00/InfoDialog.h --- 8.02/InfoDialog.h Wed Dec 31 19:00:00 1969 +++ 9.00/InfoDialog.h Thu Jun 5 07:11:34 1997 @@ -0,0 +1,8 @@ +#ifndef _INFODIALOG_H +#define _INFODIALOG_H + +extern void InfoDialogCreate _ARGUMENTS((Widget)); +extern long MesgLength _ARGUMENTS((void)); +extern void displayMesgString _ARGUMENTS((char *)); + +#endif /* _INFODIALOG_H */ diff -u -d -r -N -P 8.02/InfoLine.c 9.00/InfoLine.c --- 8.02/InfoLine.c Fri Dec 22 05:27:28 1995 +++ 9.00/InfoLine.c Thu Jun 5 07:11:34 1997 @@ -1,7 +1,12 @@ -#include -#include -#include -#include +#ifdef MOTIF +# include +# include +#else +# include +# include +# include +# include +#endif #include "config.h" #include "InfoLine.h" @@ -14,13 +19,28 @@ Widget w; Dimension height; +#ifdef MOTIF + w = XtVaCreateManagedWidget(name, xmLabelWidgetClass, parent, + XmNskipAdjust, True, + 0); + + if (initial_text) + InfoLineSet(w, initial_text); + + XtVaGetValues(w, XmNheight, &height, 0); + + XtVaSetValues(w, + XmNpaneMinimum, height, + XmNpaneMaximum, height, + 0); +#else w = XtVaCreateManagedWidget(name, labelWidgetClass, parent, XtNskipAdjust, True, XtNshowGrip, False, 0); if (initial_text) - XtVaSetValues(w, XtNlabel, initial_text, 0); + InfoLineSet(w, initial_text); XtVaGetValues(w, XtNheight, &height, 0); @@ -30,6 +50,7 @@ XtNpreferredPaneSize, height, XtNresizeToPreferred, True, 0); +#endif return(w); } @@ -38,7 +59,16 @@ Widget w; String text; { +#ifdef MOTIF + XmString x; + + /* Yes, I know this is an old function and not the preferred way, but for now...kb */ + x = XmStringCreateSimple(text); + XtVaSetValues(w, XmNlabelString, x, 0); + XmStringFree(x); +#else XtVaSetValues(w, XtNlabel, text, 0); +#endif } void InfoLineDestroy(widget) diff -u -d -r -N -P 8.02/Makefile 9.00/Makefile --- 8.02/Makefile Thu May 2 08:39:51 1996 +++ 9.00/Makefile Thu Jan 15 21:58:19 1998 @@ -1,39 +1,51 @@ # Makefile generated by imake - do not edit! -# $XConsortium: imake.c,v 1.91 95/01/12 16:15:47 kaleb Exp $ +# $TOG: imake.c /main/97 1997/06/20 20:23:51 kaleb $ # ---------------------------------------------------------------------- -# Makefile generated from "Imake.tmpl" and -# $XConsortium: Imake.tmpl,v 1.224.1.1 95/06/19 17:51:01 gildea Exp $ -# $XFree86: xc/config/cf/Imake.tmpl,v 3.18 1995/07/12 15:27:23 dawes Exp $ +# Makefile generated from "Imake.tmpl" and +# $TOG: Imake.tmpl /main/245 1997/05/20 10:05:47 kaleb $ # +# +# +# +# $XFree86: xc/config/cf/Imake.tmpl,v 3.32.2.5 1997/07/06 07:27:59 dawes Exp $ +# ---------------------------------------------------------------------- + +all:: .SUFFIXES: .i -# $XConsortium: Imake.cf,v 1.19 95/01/05 19:24:32 kaleb Exp $ -# $XFree86: xc/config/cf/Imake.cf,v 3.15 1995/06/08 06:18:39 dawes Exp $ +# $TOG: Imake.cf /main/28 1997/06/25 08:31:36 barstow $ +# $XFree86: xc/config/cf/Imake.cf,v 3.34.2.3 1997/07/27 02:41:02 dawes Exp $ # ----------------------------------------------------------------------- # site-specific configuration parameters that need to come before # the platform-specific parameters - edit site.def to change -# site: $XConsortium: site.sample,v 1.9 94/04/08 17:02:06 rws Exp $ -# site: $XFree86: xc/config/cf/site.def,v 3.8 1995/05/27 01:56:50 dawes Exp $ +# site: $XConsortium: site.def /main/revisionist/4 1996/12/31 08:02:07 kaleb $ +# site: $XFree86: xc/config/cf/site.def,v 3.17.2.1 1997/06/22 10:32:21 dawes Exp $ -# $XFree86: xc/config/cf/xf86site.def,v 3.48 1995/07/22 04:11:45 dawes Exp $ +# $XFree86: xc/config/cf/xf86site.def,v 3.101.2.11 1997/06/22 10:32:22 dawes Exp $ -# ----------------------------------------------------------------------- +# ---------------------------------------------------------------------- # platform-specific configuration parameters - edit linux.cf to change -# platform: $XConsortium: linux.cf,v 1.11 95/01/23 18:32:03 kaleb Exp $ -# platform: $XFree86: xc/config/cf/linux.cf,v 3.26 1995/07/12 15:27:34 dawes Exp $ +# platform: $TOG: linux.cf /main/36 1997/06/16 22:21:03 kaleb $ +# platform: $XFree86: xc/config/cf/linux.cf,v 3.57.2.10 1997/07/28 14:17:25 dawes Exp $ -# $XConsortium: lnxLib.rules,v 1.8 95/01/16 21:11:00 kaleb Exp $ -# $XFree86: xc/config/cf/lnxLib.rules,v 3.14 1995/06/01 08:18:56 dawes Exp $ +# operating system: Linux 2.0.33 i586 [ELF] (2.0.33) +# libc: (5.1073770848.0) +# binutils: (28) -# $XConsortium: xfree86.cf,v 1.9 95/01/25 16:42:40 kaleb Exp $ -# $XFree86: xc/config/cf/xfree86.cf,v 3.66 1995/07/22 04:11:47 dawes Exp $ +# $XConsortium: lnxLib.rules /main/13 1996/09/28 16:11:01 rws $ +# $XFree86: xc/config/cf/lnxLib.rules,v 3.28.2.3 1997/06/22 10:32:20 dawes Exp $ + +# $XFree86: xc/config/cf/xfree86.cf,v 3.129.2.14 1997/07/06 07:28:00 dawes Exp $ + +# $XConsortium: xfree86.cf /main/34 1996/12/06 11:45:18 rws $ LINKKITDIR = $(USRLIBDIR)/Server +XF98LINKKITDIR = $(USRLIBDIR)/Server XF86SRC = $(SERVERSRC)/hw/xfree86 XF86ACCELSRC = $(XF86SRC)/accel @@ -46,29 +58,62 @@ VGA2DRIVERSRC = $(XF86SRC)/vga2/drivers MONODRIVERSRC = $(XF86SRC)/mono/drivers S3DRIVERSRC = $(XF86SRC)/accel/s3/drivers + S3VDRIVERSRC = $(XF86SRC)/accel/s3_virge/drivers + + XF68SRC = $(SERVERSRC)/hw/xfree68 + XF68COMSRC = $(XF68SRC)/common + XF68CONFIGSRC = $(XF68COMSRC) + XF68OSSRC = $(XF68SRC)/os-support + + XF98SRC = $(SERVERSRC)/hw/xfree98 + XF98ACCELSRC = $(XF98SRC)/accel + XF98COMSRC = $(XF98SRC)/common + XF98CONFIGSRC = $(XF98COMSRC) + XF98HWSRC = $(XF98SRC)/common_hw/generic + XF98HWNECSRC = $(XF98SRC)/common_hw/nec + XF98HWPWSKBSRC = $(XF98SRC)/common_hw/pwskb + XF98HWPWLBSRC = $(XF98SRC)/common_hw/pwlb + XF98HWGA968SRC = $(XF98SRC)/common_hw/ga968 + XF98OSSRC = $(XF98SRC)/os-support + XF98VGADRIVERSRC = $(XF98SRC)/vga256/drivers +XF98VGA16DRIVERSRC = $(XF98SRC)/vga16/drivers + XF98VGA2DRIVERSRC = $(XF98SRC)/vga2/drivers + XF98MONODRIVERSRC = $(XF98SRC)/mono/drivers +XF98NECS3DRIVERSRC = $(XF98SRC)/accel/s3nec/drivers +XF98PWSKBDRIVERSRC = $(XF98SRC)/accel/s3pwskb/drivers + XF98PWLBDRIVERSRC = $(XF98SRC)/accel/s3pwlb/drivers +XF98GA968DRIVERSRC = $(XF98SRC)/accel/s3ga968/drivers XFREE86DOCDIR = $(LIBDIR)/doc XFREE86PSDOCDIR = $(XFREE86DOCDIR)/PostScript XFREE86HTMLDOCDIR = $(XFREE86DOCDIR)/html XFREE86JAPANESEDOCDIR = $(XFREE86DOCDIR)/Japanese -# $XConsortium: xf86.rules,v 1.7 95/01/25 16:34:39 kaleb Exp $ -# $XFree86: xc/config/cf/xf86.rules,v 3.9 1995/07/12 15:27:38 dawes Exp $ +# $XConsortium: xf86.rules /main/9 1996/10/31 14:54:26 kaleb $ +# $XFree86: xc/config/cf/xf86.rules,v 3.16.2.1 1997/05/18 12:00:01 dawes Exp $ -# ----------------------------------------------------------------------- +# ---------------------------------------------------------------------- # site-specific configuration parameters that go after # the platform-specific parameters - edit site.def to change -# site: $XConsortium: site.sample,v 1.9 94/04/08 17:02:06 rws Exp $ -# site: $XFree86: xc/config/cf/site.def,v 3.8 1995/05/27 01:56:50 dawes Exp $ +# site: $XConsortium: site.def /main/revisionist/4 1996/12/31 08:02:07 kaleb $ +# site: $XFree86: xc/config/cf/site.def,v 3.17.2.1 1997/06/22 10:32:21 dawes Exp $ -# ----------------------------------------------------------------------- +# --------------------------------------------------------------------- # Imake rules for building libraries, programs, scripts, and data files -# rules: $XConsortium: Imake.rules,v 1.197.1.1 95/06/19 18:01:48 gildea Exp $ -# rules: $XFree86: xc/config/cf/Imake.rules,v 3.18 1995/07/22 09:39:32 dawes Exp $ +# rules: $TOG: Imake.rules /main/222 1997/07/17 20:04:40 kaleb $ +# rules: $XFree86: xc/config/cf/Imake.rules,v 3.33.2.5 1997/07/19 04:59:07 dawes Exp $ _NULLCMD_ = @ echo -n +TKLIBNAME = + +TKLIBDIR = + +TCLLIBNAME = + +TCLIBDIR = + PATHSEP = / SHELL = /bin/sh @@ -78,16 +123,14 @@ IMAKE = imake DEPEND = gccmakedep MKDIRHIER = mkdir -p + EXPORTLISTGEN = CONFIGSRC = $(TOP)/config IMAKESRC = $(CONFIGSRC)/imake DEPENDSRC = $(CONFIGSRC)/util - IXXSRC = $(UNSUPPORTEDSRC)/programs/ixx - IXX = ixx - IXXFLAGS = -s BaseObject -m TypeObj -r RequestObj -p Xf - IXXINCLUDES = -i '' INCROOT = /usr/X11R6/include USRLIBDIR = /usr/X11R6/lib + VARLIBDIR = /var/lib SHLIBDIR = /usr/X11R6/lib LINTLIBDIR = $(USRLIBDIR)/lint MANPATH = /usr/X11R6/man @@ -101,20 +144,23 @@ CC = gcc AS = as -.SUFFIXES: .cxx +.SUFFIXES: .cc - CXX = g++ - CXXDEBUGFLAGS = -O2 -m486 + CXX = c++ + CXXFILT = c++filt + CXXLIB = + CXXDEBUGFLAGS = -O2 -fno-strength-reduce +CXXDEPENDINCLUDES = CXXEXTRA_DEFINES = CXXEXTRA_INCLUDES = - CXXIDL_INCLUDES = -I$(TOP)/include - CXXSTD_DEFINES = -Dlinux -D__i386__ -D_POSIX_SOURCE -D_BSD_SOURCE -D_GNU_SOURCE -DX_LOCALE + CXXSTD_DEFINES = -Dlinux -D__i386__ -D_POSIX_SOURCE -D_BSD_SOURCE -D_SVID_SOURCE -DX_LOCALE $(CXXPROJECT_DEFINES) CXXOPTIONS = - CXXINCLUDES = $(INCLUDES) $(TOP_INCLUDES) $(CXXEXTRA_INCLUDES) $(CXXIDL_INCLUDES) - CXXDEFINES = $(CXXINCLUDES) $(CXXSTD_DEFINES) $(THREADS_CXXDEFINES) $(CXXEXTRA_DEFINES) + CXXINCLUDES = $(INCLUDES) $(TOP_INCLUDES) $(CXXEXTRA_INCLUDES) + CXXDEFINES = $(CXXINCLUDES) $(CXXSTD_DEFINES) $(THREADS_CXXDEFINES) $(CXXEXTRA_DEFINES) $(DEFINES) CXXFLAGS = $(CXXDEBUGFLAGS) $(CXXOPTIONS) $(THREADS_CXXFLAGS) $(CXXDEFINES) COMPRESS = compress + GZIPCMD = gzip CPP = /lib/cpp $(STD_CPP_DEFINES) PREPROCESSCMD = gcc -E $(STD_CPP_DEFINES) INSTALL = install @@ -129,7 +175,7 @@ LINTOPTS = -axz LN = ln -s MAKE = make - MV = mv + MV = mv -f CP = cp RANLIB = ranlib @@ -140,48 +186,52 @@ LIBMANSUFFIX = 3x FILEMANSUFFIX = 5x TROFF = psroff + NROFF = nroff MSMACROS = -ms + MANMACROS = -man TBL = tbl EQN = eqn + NEQN = neqn + COL = col DVIPS = dvips LATEX = latex STD_INCLUDES = - STD_CPP_DEFINES = -traditional -Dlinux -D__i386__ -D_POSIX_SOURCE -D_BSD_SOURCE -D_GNU_SOURCE -DX_LOCALE - STD_DEFINES = -Dlinux -D__i386__ -D_POSIX_SOURCE -D_BSD_SOURCE -D_GNU_SOURCE -DX_LOCALE + STD_CPP_DEFINES = -traditional -Dlinux -D__i386__ -D_POSIX_SOURCE -D_BSD_SOURCE -D_SVID_SOURCE -DX_LOCALE $(PROJECT_DEFINES) + STD_DEFINES = -Dlinux -D__i386__ -D_POSIX_SOURCE -D_BSD_SOURCE -D_SVID_SOURCE -DX_LOCALE $(PROJECT_DEFINES) EXTRA_LOAD_FLAGS = EXTRA_LDOPTIONS = EXTRA_LIBRARIES = TAGS = ctags + PARALLELMFLAGS = + SHAREDCODEDEF = SHLIBDEF = - SHLIBLDFLAGS = + SHLIBLDFLAGS = -shared - PICFLAGS = -B/usr/bin/jump + PICFLAGS = -fPIC - CXXPICFLAGS = -B/usr/bin/jump + CXXPICFLAGS = -fPIC PROTO_DEFINES = -DFUNCPROTO=15 -DNARROWPROTO INSTPGMFLAGS = -s INSTBINFLAGS = -m 0755 - INSTUIDFLAGS = -m 4755 + INSTUIDFLAGS = -m 4711 INSTLIBFLAGS = -m 0644 INSTINCFLAGS = -m 0444 INSTMANFLAGS = -m 0444 INSTDATFLAGS = -m 0444 - INSTKMEMFLAGS = -m 4755 + INSTKMEMFLAGS = -m 4711 PROJECTROOT = /usr/X11R6 - TOP_INCLUDES = -I$(INCROOT) - - CDEBUGFLAGS = -O2 -m486 - CCOPTIONS = -ansi + CDEBUGFLAGS = -O2 -fno-strength-reduce + CCOPTIONS = ALLINCLUDES = $(INCLUDES) $(EXTRA_INCLUDES) $(TOP_INCLUDES) $(STD_INCLUDES) ALLDEFINES = $(ALLINCLUDES) $(STD_DEFINES) $(EXTRA_DEFINES) $(PROTO_DEFINES) $(THREADS_DEFINES) $(DEFINES) @@ -189,10 +239,10 @@ LINTFLAGS = $(LINTOPTS) -DLINT $(ALLDEFINES) $(DEPEND_DEFINES) LDPRELIB = -L$(USRLIBDIR) LDPOSTLIB = - LDOPTIONS = $(CDEBUGFLAGS) $(CCOPTIONS) $(EXTRA_LDOPTIONS) $(THREADS_LDFLAGS) $(LOCAL_LDFLAGS) $(LDPRELIB) - CXXLDOPTIONS = $(CXXDEBUGFLAGS) $(CXXOPTIONS) $(EXTRA_LDOPTIONS) $(THREADS_CXXLDFLAGS) $(LOCAL_LDFLAGS) $(LDPRELIB) + LDOPTIONS = $(CDEBUGFLAGS) $(CCOPTIONS) $(EXTRA_LDOPTIONS) $(THREADS_LDFLAGS) $(LOCAL_LDFLAGS) $(LDPRELIBS) + CXXLDOPTIONS = $(CXXDEBUGFLAGS) $(CXXOPTIONS) $(EXTRA_LDOPTIONS) $(THREADS_CXXLDFLAGS) $(LOCAL_LDFLAGS) $(LDPRELIBS) - LDLIBS = $(LDPOSTLIB) $(THREADS_LIBS) $(SYS_LIBRARIES) $(EXTRA_LIBRARIES) + LDLIBS = $(LDPOSTLIBS) $(THREADS_LIBS) $(SYS_LIBRARIES) $(EXTRA_LIBRARIES) CCLINK = $(CC) @@ -202,6 +252,16 @@ LDCOMBINEFLAGS = -r DEPENDFLAGS = +# Not sure this belongs here + TKLIBDIR = + TKINCDIR = + TKLIBNAME = + TKLIBRARY = -L$(TKLIBDIR) -l$(TKLIBNAME) + TCLLIBDIR = + TCLINCDIR = + TCLLIBNAME = + TCLLIBRARY = -L$(TCLLIBDIR) -l$(TCLLIBNAME) + MACROFILE = linux.cf RM_CMD = $(RM) @@ -210,34 +270,52 @@ IRULESRC = $(CONFIGDIR) IMAKE_CMD = $(IMAKE) -DUseInstalled -I$(IRULESRC) $(IMAKE_DEFINES) - ICONFIGFILES = $(IRULESRC)/Imake.tmpl $(IRULESRC)/Project.tmpl $(IRULESRC)/site.def $(IRULESRC)/$(MACROFILE) $(IRULESRC)/xfree86.cf $(IRULESRC)/xf86.rules $(IRULESRC)/xf86site.def $(IRULESRC)/host.def $(EXTRA_ICONFIGFILES) + ICONFIGFILES = $(IRULESRC)/Imake.tmpl $(IRULESRC)/X11.tmpl $(IRULESRC)/site.def $(IRULESRC)/$(MACROFILE) $(IRULESRC)/xfree86.cf $(IRULESRC)/xf86.rules $(IRULESRC)/xf86site.def $(IRULESRC)/host.def $(EXTRA_ICONFIGFILES) + +# $TOG: X11.rules /main/4 1997/04/30 15:23:24 kaleb $ # ---------------------------------------------------------------------- # X Window System Build Parameters and Rules -# $XConsortium: Project.tmpl,v 1.249 95/05/23 21:36:40 matt Exp $ -# $XFree86: xc/config/cf/Project.tmpl,v 3.18 1995/07/22 04:11:42 dawes Exp $ +# $TOG: X11.tmpl /main/292 1997/05/20 10:05:59 kaleb $ +# +# +# +# +# $XFree86: xc/config/cf/X11.tmpl,v 1.8.2.3 1997/05/21 15:02:13 dawes Exp $ # ----------------------------------------------------------------------- # X Window System make variables; these need to be coordinated with rules + XTOP = $(TOP) BINDIR = /usr/X11R6/bin - BUILDINCROOT = $(TOP) - BUILDINCDIR = $(BUILDINCROOT)/X11 - BUILDINCTOP = .. - BUILDLIBDIR = $(TOP)/usrlib - BUILDLIBTOP = .. - INCDIR = $(INCROOT)/X11 + BUILDINCROOT = $(TOP)/exports + BUILDINCDIR = $(BUILDINCROOT)/include + BUILDINCTOP = ../.. + BUILDLIBDIR = $(TOP)/exports/lib + BUILDLIBTOP = ../.. + BUILDBINDIR = $(TOP)/exports/bin + BUILDBINTOP = ../.. + XBUILDINCROOT = $(XTOP)/exports + XBUILDINCDIR = $(XBUILDINCROOT)/include/X11 + XBUILDINCTOP = ../../.. + XBUILDBINDIR = $(XBUILDINCROOT)/bin + INCDIR = $(INCROOT) ADMDIR = /usr/adm LIBDIR = $(USRLIBDIR)/X11 + TOP_X_INCLUDES = -I$(XPROJECTROOT)/include FONTDIR = $(LIBDIR)/fonts XINITDIR = $(LIBDIR)/xinit XDMDIR = $(LIBDIR)/xdm + XDMVARDIR = $(VARLIBDIR)/xdm TWMDIR = $(LIBDIR)/twm XSMDIR = $(LIBDIR)/xsm NLSDIR = $(LIBDIR)/nls XLOCALEDIR = $(LIBDIR)/locale PEXAPIDIR = $(LIBDIR)/PEX + LBXPROXYDIR = $(LIBDIR)/lbxproxy + PROXYMANAGERDIR = $(LIBDIR)/proxymngr + XPRINTDIR = $(LIBDIR) XAPPLOADDIR = $(LIBDIR)/app-defaults FONTCFLAGS = -t @@ -247,32 +325,38 @@ FONTC = bdftopcf MKFONTDIR = mkfontdir - DOCUTILSRC = $(TOP)/doc/util + DOCUTILSRC = $(XTOP)/doc/util + CLIENTSRC = $(TOP)/clients + DEMOSRC = $(TOP)/demos XDOCMACROS = $(DOCUTILSRC)/macros.t XIDXMACROS = $(DOCUTILSRC)/indexmacros.t PROGRAMSRC = $(TOP)/programs - LIBSRC = $(TOP)/lib - FONTSRC = $(TOP)/fonts - INCLUDESRC = $(TOP)/X11 - SERVERSRC = $(TOP)/programs/Xserver - CONTRIBSRC = $(TOP)/../contrib - UNSUPPORTEDSRC = $(TOP)/unsupported - DOCSRC = $(TOP)/doc - RGBSRC = $(TOP)/programs/rgb + LIBSRC = $(XTOP)/lib + FONTSRC = $(XTOP)/fonts + INCLUDESRC = $(BUILDINCROOT)/include + XINCLUDESRC = $(INCLUDESRC)/X11 + SERVERSRC = $(XTOP)/programs/Xserver + CONTRIBSRC = $(XTOP)/../contrib + UNSUPPORTEDSRC = $(XTOP)/unsupported + DOCSRC = $(XTOP)/doc + RGBSRC = $(XTOP)/programs/rgb BDFTOPCFSRC = $(PROGRAMSRC)/bdftopcf MKFONTDIRSRC = $(PROGRAMSRC)/mkfontdir FONTSERVERSRC = $(PROGRAMSRC)/xfs - FONTINCSRC = $(TOP)/include/fonts - EXTINCSRC = $(TOP)/include/extensions + FONTINCSRC = $(XTOP)/include/fonts + EXTINCSRC = $(XTOP)/include/extensions TRANSCOMMSRC = $(LIBSRC)/xtrans TRANS_INCLUDES = -I$(TRANSCOMMSRC) + XENVLIBDIR = $(USRLIBDIR) + CLIENTENVSETUP = LD_LIBRARY_PATH=$(XENVLIBDIR) + # $XConsortium: lnxLib.tmpl,v 1.5 95/01/11 21:44:44 kaleb Exp $ -# $XFree86: xc/config/cf/lnxLib.tmpl,v 3.7 1995/04/09 13:39:23 dawes Exp $ +# $XFree86: xc/config/cf/lnxLib.tmpl,v 3.9 1996/02/24 04:32:52 dawes Exp $ XLIBSRC = $(LIBSRC)/X11 -SOXLIBREV = 6.0 +SOXLIBREV = 6.1 DEPXONLYLIB = XONLYLIB = -lX11 @@ -281,35 +365,52 @@ XLIBONLY = $(XONLYLIB) XEXTLIBSRC = $(LIBSRC)/Xext - LBXXEXTLIBSRC = $(TOP)/workInProgress/lbx/lib/Xext - XEXEXTLIBSRC = $(LIBSRC)/XExExt +SOXEXTREV = 6.3 +DEPEXTENSIONLIB = +EXTENSIONLIB = -lXext -SOXEXTREV = 6.0 -DEPXEXTLIB = -XEXTLIB = -lXext +LINTEXTENSION = $(LINTLIBDIR)/llib-lXext.ln -LINTXEXT = $(LINTLIBDIR)/llib-lXext.ln +LINTEXTENSIONLIB = $(LINTEXTENSION) + DEPXLIB = $(DEPEXTENSIONLIB) $(DEPXONLYLIB) + XLIB = $(EXTENSIONLIB) $(XONLYLIB) + LINTXLIB = $(LINTXONLYLIB) -SOXEXTREV = 6.0 -DEPLBXXEXTLIB = -LBXXEXTLIB = -llbxXext + XSSLIBSRC = $(LIBSRC)/Xss -LINTLBXXEXT = $(LINTLIBDIR)/llib-llbxXext.ln +DEPXSSLIB = $(USRLIBDIR)/libXss.a +XSSLIB = -lXss -DEPXEXEXTLIB = $(USRLIBDIR)/libXExExt.a -XEXEXTLIB = -lXExExt +LINTXSS = $(LINTLIBDIR)/llib-lXss.ln -LINTXEXEXT = $(LINTLIBDIR)/llib-lXExExt.ln + XXF86MISCLIBSRC = $(LIBSRC)/Xxf86misc - EXTENSIONLIB = $(XEXEXTLIB) $(XEXTLIB) - DEPEXTENSIONLIB = $(DEPXEXEXTLIB) $(DEPXEXTLIB) - LINTEXTENSION = $(LINTXEXEXT) $(LINTXEXT) +DEPXXF86MISCLIB = $(USRLIBDIR)/libXxf86misc.a +XXF86MISCLIB = -lXxf86misc - LINTEXTENSIONLIB = $(LINTEXTENSION) - DEPXLIB = $(DEPEXTENSIONLIB) $(DEPXONLYLIB) - XLIB = $(EXTENSIONLIB) $(XONLYLIB) - LINTXLIB = $(LINTXONLYLIB) +LINTXXF86MISC = $(LINTLIBDIR)/llib-lXxf86misc.ln + + XXF86VMLIBSRC = $(LIBSRC)/Xxf86vm + +DEPXXF86VMLIB = $(USRLIBDIR)/libXxf86vm.a +XXF86VMLIB = -lXxf86vm + +LINTXXF86VM = $(LINTLIBDIR)/llib-lXxf86vm.ln + + XXF86DGALIBSRC = $(LIBSRC)/Xxf86dga + +DEPXXF86DGALIB = $(USRLIBDIR)/libXxf86dga.a +XXF86DGALIB = -lXxf86dga + +LINTXXF86DGA = $(LINTLIBDIR)/llib-lXxf86dga.ln + + XDPMSLIBSRC = $(LIBSRC)/Xdpms + +DEPXDPMSLIB = $(USRLIBDIR)/libXdpms.a +XDPMSLIB = -lXdpms + +LINTXDPMS = $(LINTLIBDIR)/llib-lXdpms.ln XAUTHSRC = $(LIBSRC)/Xau @@ -340,6 +441,14 @@ LINTOLDX = $(LINTLIBDIR)/llib-loldX.ln + XPLIBSRC = $(LIBSRC)/Xp + +SOXPREV = 6.2 +DEPXPLIB = +XPLIB = -lXp + +LINTXP = $(LINTLIBDIR)/llib-lXp.ln + TOOLKITSRC = $(LIBSRC)/Xt SOXTREV = 6.0 @@ -352,30 +461,22 @@ XTOOLLIB = $(XTOOLONLYLIB) $(SMLIB) $(ICELIB) LINTXTOOLLIB = $(LINTXTOOLONLYLIB) + XALIBSRC = $(LIBSRC)/Xa + +SOXAREV = 1.0 +DEPXALIB = +XALIB = -lXa + +LINTXA = $(LINTLIBDIR)/llib-lXa.ln + AWIDGETSRC = $(LIBSRC)/Xaw -SOXAWREV = 6.0 +SOXAWREV = 6.1 DEPXAWLIB = XAWLIB = -lXaw LINTXAW = $(LINTLIBDIR)/llib-lXaw.ln - XTFSRC = $(TOP)/workInProgress/Xtf - -SOXTFREV = 0.7 -DEPXTFLIB = -XTFLIB = -lXtf - -LINTXTF = $(LINTLIBDIR)/llib-lXtf.ln - - FRESCOSRC = $(TOP)/workInProgress/Fresco - -SOFRESCOREV = 0.7 -DEPFRESCOLIB = -FRESCOLIB = -lFresco - -LINTFRESCO = $(LINTLIBDIR)/llib-lFresco.ln - XILIBSRC = $(LIBSRC)/Xi SOXINPUTREV = 6.0 @@ -386,7 +487,7 @@ XTESTLIBSRC = $(LIBSRC)/Xtst -SOXTESTREV = 6.0 +SOXTESTREV = 6.1 DEPXTESTLIB = XTESTLIB = -lXtst @@ -422,7 +523,7 @@ ICESRC = $(LIBSRC)/ICE -SOICEREV = 6.0 +SOICEREV = 6.3 DEPICELIB = ICELIB = -lICE @@ -436,6 +537,14 @@ LINTSM = $(LINTLIBDIR)/llib-lSM.ln + XKEYSRC = $(LIBSRC)/Xkey + +SOXKEYREV = 6.0 +DEPXKEYLIB = +XKEYLIB = -lXkey + +LINTXKEY = $(LINTLIBDIR)/llib-lXkey.ln + FSLIBSRC = $(LIBSRC)/FS DEPFSLIB = $(USRLIBDIR)/libFS.a @@ -450,26 +559,66 @@ LINTFONT = $(LINTLIBDIR)/llib-lfont.ln + XPMLIBSRC = $(LIBSRC)/Xpm + +DEPXPMLIB = $(USRLIBDIR)/libXpm.a +XPMLIB = -lXpm + +LINTXPM = $(LINTLIBDIR)/llib-lXpm.ln + + XKBFILELIBSRC = $(LIBSRC)/xkbfile + +DEPXKBFILELIB = $(USRLIBDIR)/libxkbfile.a +XKBFILELIB = -lxkbfile + +LINTXKBFILE = $(LINTLIBDIR)/llib-lxkbfile.ln + + XKBCOMPCMD = xkbcomp + + XKBUILIBSRC = $(LIBSRC)/xkbui + +DEPXKBUILIB = $(USRLIBDIR)/libxkbui.a +XKBUILIB = -lxkbui + +LINTXKBUI = $(LINTLIBDIR)/llib-lxkbui.ln + DEPLIBS = $(DEPXAWLIB) $(DEPXMULIB) $(DEPXTOOLLIB) $(DEPXLIB) DEPLIBS1 = $(DEPLIBS) DEPLIBS2 = $(DEPLIBS) DEPLIBS3 = $(DEPLIBS) + DEPLIBS4 = $(DEPLIBS) + DEPLIBS5 = $(DEPLIBS) + DEPLIBS6 = $(DEPLIBS) + DEPLIBS7 = $(DEPLIBS) + DEPLIBS8 = $(DEPLIBS) + DEPLIBS9 = $(DEPLIBS) + DEPLIBS10 = $(DEPLIBS) -XMULIB = -lXmu $(XLIB) +XMULIBONLY = -lXmu +XMULIB = $(XMULIBONLY) $(XTOOLLIB) $(XLIB) CONFIGDIR = $(LIBDIR)/config -# ----------------------------------------------------------------------- + USRLIBDIRPATH = $(USRLIBDIR) + LDPRELIBS = -L$(USRLIBDIR) + LDPOSTLIBS = + TOP_INCLUDES = -I$(INCROOT) $(TOP_X_INCLUDES) + PROJECT_DEFINES = + +CXXPROJECT_DEFINES = + +# ---------------------------------------------------------------------- # start of Imakefile PROGRAMS = xrn + APPDEFAULTS = XRn LANGUAGE= english -X_LIBRARIES = $(XAWLIB) $(XMULIB) $(XTOOLLIB) $(XLIB) -DEPLIBS = $(DEPXAWLIB) $(DEPXMULIB) $(DEPXTOOLLIB) $(DEPXLIB) + X_LIBRARIES = $(XAWLIB) $(XMULIBONLY) $(XTOOLLIB) $(XLIB) + DEPLIBS = $(DEPXAWLIB) $(DEPXMULIB) $(DEPXTOOLLIB) $(DEPXLIB) CLIENTSRC = clientlib.c CLIENTOBJ = clientlib.o @@ -477,18 +626,18 @@ SIGNAL_DEFINES = -DVOID_SIGNAL LOCAL_LIBRARIES = $(X_LIBRARIES) $(XRN_LOAD_FLAGS) $(INN_LOAD_FLAGS) -DEFINES = $(XRN_DEFINES) $(SIGNAL_DEFINES) $(INN_DEFINES) $(SITE_DEFINES) -DXRN_LANG_$(LANGUAGE) -DXRN_APP_CLASS=\"$(APPDEFAULTS)\" -DXRN +DEFINES = $(XRN_DEFINES) $(GUI_DEFINES) $(SIGNAL_DEFINES) $(INN_DEFINES) $(SITE_DEFINES) -DXRN_LANG_$(LANGUAGE) -DXRNAPPDIR=\"$(XAPPLOADDIR)\" -DXRN_APP_CLASS=\"$(APPDEFAULTS)\" -DXRN HDRS = mesg_strings.h -SRCS = avl.c buttons.c $(CLIENTSRC) compose.c cursor.c dialogs.c refile.c mesg.c error_hnds.c newsrcfile.c resources.c internals.c save.c server.c utils.c xmisc.c xrn.c xthelper.c y.tab.c cancel.c vprintf.c tempnam.c strstr.c strtok.c mesg_strings.c Text.c ngMode.c artMode.c allMode.c addMode.c snapshot.c artstruct.c InfoLine.c ButtonBox.c busyCursor.c varfile.c activecache.c +SRCS = avl.c $(CLIENTSRC) compose.c cursor.c dialogs.c refile.c mesg.c error_hnds.c newsrcfile.c resources.c internals.c save.c server.c utils.c xrn.c xthelper.c rcyacc.c cancel.c vprintf.c tempnam.c strstr.c strtok.c mesg_strings.c Text.c ngMode.c artMode.c allMode.c addMode.c snapshot.c artstruct.c Buttons.c InfoLine.c ButtonBox.c busyCursor.c varfile.c activecache.c sort.c getdate2.c hash.c killfile.c InfoDialog.c Frame.c Xmisc.c file_cache.c -LOCAL_OBJS = avl.o buttons.o $(CLIENTOBJ) compose.o cursor.o dialogs.o refile.o mesg.o error_hnds.o newsrcfile.o resources.o internals.o save.o server.o utils.o xmisc.o xrn.o xthelper.o y.tab.o cancel.o vprintf.o tempnam.o strstr.o strtok.o mesg_strings.o Text.o ngMode.o artMode.o allMode.o addMode.o snapshot.o artstruct.o InfoLine.o ButtonBox.o busyCursor.o varfile.o activecache.o +LOCAL_OBJS = avl.o $(CLIENTOBJ) compose.o cursor.o dialogs.o refile.o mesg.o error_hnds.o newsrcfile.o resources.o internals.o save.o server.o utils.o xrn.o xthelper.o rcyacc.o cancel.o vprintf.o tempnam.o strstr.o strtok.o mesg_strings.o Text.o ngMode.o artMode.o allMode.o addMode.o snapshot.o artstruct.o Buttons.o InfoLine.o ButtonBox.o busyCursor.o varfile.o activecache.o sort.o getdate2.o hash.o killfile.o InfoDialog.o Frame.o Xmisc.o file_cache.o OBJS = $(LOCAL_OBJS) $(REGEX_OBJS) all:: mesg_strings.h -depend:: lex.yy.c y.tab.c mesg_strings.h +depend:: rclex.c rcyacc.c mesg_strings.h getdate2.c PROGRAM = $(PROGRAMS) @@ -526,15 +675,9 @@ all:: mesg_strings.h -lex.yy.c: newsrc.l - $(LEX) newsrc.l - -y.tab.c: newsrc.y - -rm -f y.tab.c newsrc.tab.c - $(YACC) newsrc.y - (test -f newsrc.tab.c && mv newsrc.tab.c y.tab.c) || true +rclex.c: rclex.l -y.tab.o: lex.yy.c avl.h news.h newsrcfile.h utils.h +rcyacc.o: rclex.c avl.h news.h newsrcfile.h utils.h mesg_strings.h: mesg_strings.c mesg_str.awk -rm -f mesg_str.tmp @@ -544,11 +687,11 @@ $(LOCAL_OBJS): config.h clean:: - $(RM) y.tab.c lex.yy.c mesg_strings.h mesg_str.tmp + $(RM) mesg_strings.h mesg_str.tmp xrn-man.c: xrn-man.sym config.h $(RM) xrn-man.tmp xrn-man.c - awk 'BEGIN {printf("#include \"config.h\"\n");} {printf("#ifdef %s\nx%sx value %s value\n#else\nx%sx\n#endif\n",$$0,$$0,$$0,$$0);}' xrn-man.sym > xrn-man.tmp + awk 'BEGIN {printf("#include \"config.h\"\n");} {printf("#ifdef %s\nx%sx value %s value\n#else\nx%sx\n#endif\n",$$0,$$0,$$0,$$0);}' xrn-man.sym > xrn-man.tmp mv xrn-man.tmp xrn-man.c @@ -575,9 +718,13 @@ clean:: $(RM) xrn-man.tmp xrn-man.c xrn-man.cpp xrn-man.sed $(PROGRAMS).man +TKSED= -e 's/XAW//' -e '/MOTIF/d' + +TKSED2= -e 's/XawSet()/set()/' -e 's/XawNotify()/notify()/' -e 's/XawUnSet()/unset()/' + $(APPDEFAULTS).ad: XRn.src -rm -f $(APPDEFAULTS).tmp $(APPDEFAULTS).ad - sed -e 's/^LANG_$(LANGUAGE)//' -e '/^LANG_/d' -e 's/xAPP_CLASSx/$(APPDEFAULTS)/' XRn.src > $(APPDEFAULTS).tmp + sed -e 's/LANG_$(LANGUAGE)//' -e '/LANG_/d' $(TKSED) $(TKSED2) -e 's/xAPP_CLASSx/$(APPDEFAULTS)/' XRn.src > $(APPDEFAULTS).tmp mv $(APPDEFAULTS).tmp $(APPDEFAULTS).ad @@ -586,17 +733,24 @@ clean:: $(RM) $(APPDEFAULTS).ad $(APPDEFAULTS).tmp -# ----------------------------------------------------------------------- +getdate2.c: getdate.c + sed -e 's/yy/getdate_yy/g' getdate.c > $@.tmp + mv $@.tmp $@ + +clean:: + $(RM) getdate.c getdate2.c.tmp getdate2.c rclex.c rcyacc.c + +# ---------------------------------------------------------------------- # common rules for all Makefiles - do not edit .c.i: $(RM) $@ - $(CC) -E $(CFLAGS) $(_NOOP_) $*.c > $@ + $(CC) -E $(CFLAGS) $(_NOOP_) $*.c > $@ emptyrule:: clean:: - $(RM_CMD) *.CKP *.ln *.BAK *.bak *.o core errs ,* *~ *.a .emacs_* tags TAGS make.log MakeOut "#"* + $(RM) *.CKP *.ln *.BAK *.bak *.o core errs ,* *~ *.a .emacs_* tags TAGS make.log MakeOut "#"* Makefile:: -@if [ -f Makefile ]; then set -x; \ @@ -608,7 +762,9 @@ $(TAGS) -w *.[ch] $(TAGS) -xw *.[ch] > TAGS -# ----------------------------------------------------------------------- +man_keywords:: + +# ---------------------------------------------------------------------- # empty rules for directories that do not have SUBDIRS - do not edit install:: @@ -626,6 +782,6 @@ depend:: -# ----------------------------------------------------------------------- +# ---------------------------------------------------------------------- # dependencies generated by makedepend diff -u -d -r -N -P 8.02/README 9.00/README --- 8.02/README Mon Nov 6 15:07:42 1995 +++ 9.00/README Thu Jan 15 08:33:19 1998 @@ -6,35 +6,34 @@ servers nowadays do; if yours doesn't, XRN will complain when you try to run it). - Versions of XRN prior to 7.00 were maintained by Ellen Santovich and -Rick Spickelmier. XRN is currently maintained by Jonathan Kamens. -But reports or comments about it should be sent to bug-xrn@cam.ov.com. + XRN is currently maintained by Jonathan Kamens. Bug reports or +comments about it should be sent to bug-xrn@kamens.brookline.ma.us. Please do *not* write to that address with basic questions about compiling the program; if you are unfamiliar with compiling X applications and the instructions below are not sufficient, you should seek help at your site. + Versions of XRN prior to 7.00 were maintained by Ellen Santovich and +Rick Spickelmier. + Where to get XRN - The source code for the most recent production release of XRN can -always be retrieved from . -The most recent beta-test release of XRN (if there is a beta release -more recent than the last production release) can always be retrieved -from . the file - contains a binary XRN -release for Linux, based on the current production or beta release, -whichever is more recent. These files are GNU tar archives compressed -with GNU zip. + The XRN home page is . + + Source code for the most recent production release of XRN can always +be found at . The most recent +beta-test release of XRN (if there is a beta release more recent than +the last production release) can be retrieved from +. The file + contains a binary XRN +release for Linux, based on the most recent production release. These +files are GNU tar archives compressed with GNU zip. The current production release and the current Linux binary release are also available in the X Consortium's contributed software archive, in the directory . - Current production-release XRN source code is also available (or -will be soon, if it isn't already) in archives of the comp.sources.x -newsgroup. - Interesting files in the XRN distribution @@ -76,8 +75,6 @@ it are left over from before I took over maintenance of XRN, and some of them may be inaccurate. -Xresources.sam A sample of how you might customize XRN in - your own X resources. contrib/* Various scripts, hints, etc. contributed by XRN users and not "officially" part of XRN. * doc/rfc977.txt The Internet RFC governing NNTP, the Network @@ -105,6 +102,13 @@ Other successful platforms reported by users include SunOS 4.1.3, OSF/1, HP-UX, AIX, Solaris 2.x, BSD/386, and SGI. + XRN probably won't work with X11R4. R4 is a very old and obsolete +release, and I do not have the resources to continue to support it +while doing on-going development which takes advantage of features +found only in newer X releases. Therefore, please do not send me bug +reports about XRN compiled with X11R4; if you want to use XRN, you'll +have to get a newer version of X with which to compile it. + Rick Murphy has a version of XRN that runs on VMS, available as . His version of XRN also will compile with a DECwindows interface on @@ -119,7 +123,7 @@ Previous versions of XRN, up to and including version 7.00, included support for building XRN with Motif instead of the Athena widget set ("Xaw"). However, the Motif support has been removed (temporarily, I -hope) in 7.01. There are two primary reasons for this: +hope) in later releases. There are two primary reasons for this: 1) The implementation of Motif support was not parallel to the Xaw implementation. As a result, the Motif interface behaved @@ -127,29 +131,26 @@ and furthermore, it was extremely difficult to keep the Motif interface in sync with the Xaw interface when changes were made. -2) Release 7.01 of XRN includes, among other things, a significantly - clean-up of the XRN code base. This clean-up was necessary in - order to introduce much of the new functionality that is included - in release 7.01. It was also necessary because, to be honest, the - code base was disgusting. The bits of Motif code scattered all - over the XRN code base were making it virtually impossible to do - the code clean-up, so they had to be removed. +2) Newer releases of XRN include a significant clean-up of the XRN + code base. This clean-up was necessary in order to introduce much + of the new functionality that is included in new releases. It was + also necessary because, to be honest, the code base was disgusting. + The bits of Motif code scattered all over the XRN code base were + making it virtually impossible to do the code clean-up, so they had + to be removed. -I hope to eventually put Motif support back into XRN. To that end, I -am in the process of encapsulating all calls to Xaw in a small number -of files, so that only those files will have to be modified in order -to support Motif (or, for that matter, any other widget set). -However, considering that the Xaw interface works just fine (even if -some people do think it's ugly), and that there is significant -functionality still waiting on the TODO list, it is unlikely that the -Motif interface will be put back in the near future. It would help a -lot for someone with extensive Motif programming experience to -volunteer to implement and maintain the Motif interface. +A volunteer is in the process of restoring the Motif support to XRN, +and in fact, current beta releases have rudimentary Motif support in +it. However, this support is not yet complete or supported, and I do +not suggest that people use it at this time. Furthermore, the +volunteer has run into some problems and currently does not anticipate +being able to finish the work. If you are interested in helping to +finish it, please contact me. - If you *must* have a Motif interface, XRN release 7.00 is available -in . -However, release 7.00 is unspported, and I will reject any bug reports -or enhancement requests pertaining to it. I do not recommend that you + If you must have a Motif interface, XRN release 7.00 is available in +. However, +release 7.00 is unspported, and I will reject any bug reports or +enhancement requests pertaining to it. I do not recommend that you use XRN 7.00. @@ -219,7 +220,7 @@ If you have problems compiling or running XRN, you should check the COMMON-PROBLMS file in the XRN distribution to see if your problem is -documented there. If not, send E-mail to bug-xrn@cam.ov.com for +documented there. If not, write to bug-xrn@kamens.brookline.ma.us for assistance. If you write to me about a problem which is documented in @@ -233,16 +234,34 @@ If you would like to be informed of new releases of XRN and of any major bugs that are discovered in between releases, you can subscribe -to the xrn-users@cam.ov.com mailing list. It's an announcement list, -not a discussion list, so the traffic on it is very low. If you're -interested, send mail to xrn-users-request@cam.ov.com and ask to be -added to the list. +to the xrn-users@kamens.brookline.ma.us mailing list. It's an +announcement list, not a discussion list, so the traffic on it is very +low. Send mail to xrn-users-request@kamens.brookline.ma.us to ask to +be added to the list. + + + The future of XRN + + Release 9.00 of XRN is the first general-availability release in +over a year and a half. The next major release may take just as long, +or even longer. I have not had as much time as I would have liked to +work on XRN, and I will probably have even less in the future. + + However, I do not want XRN to be orphaned or to deteriorate. +Therefore, I will continue to make bug-fix releases as necessary, and +I will devote as much time as I can to making enhancements. + + If you are interested in volunteering to take over, I'd love to hear +from you, but I'm only going to hand off XRN to someone whom I'm +convinced will do a good job, because I've invested a lot of effort +into it, and because the folks who maintained it before me allowed it +to stagnate for years; I do not want that to happen again. I hope you find XRN useful! Jonathan Kamens - jik@cam.ov.com + jik@kamens.brookline.ma.us -# $Id: README,v 1.42 1995/11/06 20:07:42 jik Exp $ +# $Id: README,v 1.49 1998/01/15 13:33:19 jik Exp $ diff -u -d -r -N -P 8.02/TODO 9.00/TODO --- 8.02/TODO Thu May 9 13:48:59 1996 +++ 9.00/TODO Wed Dec 24 19:19:41 1997 @@ -1,12 +1,27 @@ -$Id: TODO,v 1.154 1996/05/09 17:48:38 jik Exp $ +$Id: TODO,v 1.192 1997/12/25 00:19:41 jik Exp $ The Ever growing list of things that should be done and things that would be nice if they were done.... (and even worse, it's not up to date) +- Support a separate colormap for XRN. +- XRN should notice when the contents of a THRU line makes no sense, + and ignore it if so. +- XRN should include the NNTP server name on the THRU line, so that + the same KILL file can be used with multiple servers. +- Don't redraw the newsgroup list immediately upon rescan -- only + redraw it if it changes. +- For dialog boxes that prompt for file names, either (a) allow a + "file selection widget" type of thing to be used, or (b) keep track + of previously specified file names and allow them to be selected + from a menu in addition to allowing a new filename to be specified, + or (c) some combination of both of these. +- There are various places where XRN notices that newsgroup which + previously existed doesn't anymore, but then doesn't proceed to + follow through on that and remove the newsgroup from the .newsrc, + cache, newsgroup list, etc. These should be fixed. - Make the Makefile insert the version number into xrn.man and XRn.ad so that it only have to be hard-coded in one place -- patchlevel.h. -- Update sample X resources. - Document accelerators for the text window of article mode in the man page. - Add a facility to allow users to add customized headers to outgoing @@ -14,9 +29,6 @@ make them different for mail and news messages, with the same class so people who want them to be the same don't have to put them in their resources twice). -- Bug: Start to post a followup/reply. Click on "Toggle header" in - the main window. Try to include the followup/reply in the article. - It won't work. - Add a "Save" button to the dialog that comes up after a composition is edited with an external editor, asking the user to send, abort or reedit. @@ -75,7 +87,6 @@ which is filled in by each of the callers. - Shouldn't warn about article being mailed to moderator when it includes an Approved line? -- Document the widget tree hierarchy in the man page. - Make the attribution lines in followups and replies customizable. - Line-break attribution lines if they're too long. (suggested by Paul Menage ). @@ -98,7 +109,7 @@ - Make it possible to configure the composition pane buttons with a buttonlist resource. - Add allScrollBeginning, allScrollEnd, allScrollLine, - allScrollLineBack, allPrev, allNext buttons, actions, bindings. + allScrollLineBack buttons, actions, bindings. - Add a resource to specify the KILL-file directory, so that the user can store KILL files and save files in separate directories if he chooses to do so. @@ -125,10 +136,6 @@ - Modularize all the other Xaw usages in the code into simplified interfaces. - Put back Motif support. -- Notice when the XRN window changes size, and call - adjustMinMaxLines() to make the cursor visible again. Also, redraw - the index window, and the all mode list if in all mode, based on the - new window width. - Investigate bug reported by uri@watson.ibm.com, that article mode exits to newsgroup mode if it encounters an unavailable article. - Send in a bug report about the fact that XawTextEnableRedisplay @@ -219,24 +226,6 @@ them to use XtVaSetValues and XtVaGetValues whereever possible, to eliminate unnecessary argument arrays. Same thing for the Create*Widget functions. -- Fix the way buttons are laid out in the Information window and the - dialog windows in the Motif interface. The problem is that - designating one button as the default in the Information window - causes its active area to shrink while the other button stays the - same size, rather than causing its active area to grow. The - resulting active area of the default is too small. Let me take this - opportunity to say that the Motif API is brain-damaged, stupid, - completely non-intuitive, and never does what you want it to do. - What kind of widget library, when you tell it to highlight a button - as the default, does so by shrinking the drawing area for the button - so that its text no longer fits in it properly, instead of by - growing the button to make room for the default shadow? Xaw may be - ugly, but at least it's straightforward and does what you expect it - to and what you tell it to. - - I've spent literally hours trying to figure out the correct way to - make it grow the button instead of shrinking it, but I can't figure - it out. The Motif manual is no help. Ugh. - Make sure all temporary files are being deleted properly (I think there may be a temporary file leak somewhere). - Allow the number of articles to be retrieved to be specified upon @@ -286,12 +275,6 @@ and fix all calls to them -- calls with strings with % in them need to have the %'s escaped, and calls that use sprintf into a buffer first can get rid of the buffer. -- Add a "saveSentMessages" option and command line flag - ("-saveSentMessages" to disable, "+saveSentMessages" to enable, - disabled by default) to save all successfully sent messages - automatically in app_resources.savePostings, or in some other file - specified for an X resource. - - Make the format in which files are saved in savePostings, in the current code and in any new code, dependent on saveMode. Also, make it possible to cause a different format to be used by specifying the @@ -307,6 +290,7 @@ - read updated .newsrc file (XRN detects this, but should allow you to reread) - prefetch should occur at end of list, not largest number (sorted subject...) - clean up KILL file support + - Including one KILL file inside another one. - handle '{' correctly The regular expression \{,\} is supposed to match between m and n occurences of the previous character. To kill an expression @@ -314,23 +298,13 @@ the other hand complains if the braces are quoted. Rn, on the other hand complains if the braces are quoted since the expression is not valid. Test: /{foo}/:j - - allow field names (Subject/From) in an entry - prompt for KILL expression in article mode - local kill (current subject and typein) - global kill (current subject and typein) - local author kill (current subject and typein) - global author kill (current subject and typein) - - kill author globally... - edit kill file - closer to RN - Support KILLLOCAL and KILLGLOBAL environment variables a la trn. - "Author search". -- remove tmp files as you go, not at the end of the group - (necessary for large groups with large postings) - replace popen of mailer with something better so status code can be checked - option to capitalize, not-cap the group name when saving. -- don't refetch article for saving -- thread support - XHDR message on server timeout - handle =ng in active file - command to skip quoted/included sections @@ -343,3 +317,6 @@ - local server (clientlib.c replaced with something that accesses local files) - reading an MH folder as a newsgroup (suggested by Luigi Semenzato ). +- Allow arbitrary articles/newsgroups to be selected when it makes + sense to do so, rather than only allowing selection of contiguous + ranges. diff -u -d -r -N -P 8.02/Text.c 9.00/Text.c --- 8.02/Text.c Sun Feb 19 23:07:24 1995 +++ 9.00/Text.c Sun Jun 29 11:38:36 1997 @@ -1,20 +1,27 @@ -#include -#include -#include -#include -#include +#ifdef MOTIF +# include +# include +# include +# include +#else +# include +# include +# include +# include +# include +#endif #include "config.h" #include "utils.h" #include "cursor.h" #include "Text.h" -#ifndef XawFmt8Bit +#if !defined(XawFmt8Bit) && !defined(MOTIF) #define XawFmt8Bit FMT8BIT #endif -#define REDISPLAY_NUM 5 +#define REDISPLAY_NUM 10 typedef struct { Widget w; @@ -49,7 +56,7 @@ if (! redisplay_array[i].w) { redisplay_array[i].w = w; redisplay_array[i].count = 0; - redisplay_array[i].changed = 0; + redisplay_array[i].changed = False; return redisplay_array + i; } @@ -74,30 +81,50 @@ if (! (ptr = find_redisplay(w))) return; - if (! ptr->changed++) + if (! ptr->changed) { +#ifdef MOTIF + XmTextDisableRedisplay(w); +#else XawTextDisableRedisplay(w); +#endif + ptr->changed = True; + } } /* Create a new text widget and return it. It's initially empty. */ -Widget TextCreate(name, read_only, parent) - String name; - Boolean read_only; - Widget parent; +Widget TextCreate( + _ANSIDECL(String, name), + _ANSIDECL(Boolean, read_only), + _ANSIDECL(Widget, parent) + ) + _KNRDECL(String, name) + _KNRDECL(Boolean, read_only) + _KNRDECL(Widget, parent) { Widget w; Arg args[2]; Cardinal num_args = 0; - XtSetArg(args[0], XtNstring, ""); num_args++; +#ifdef MOTIF + XtSetArg(args[num_args], XmNvalue, ""); num_args++; + XtSetArg(args[num_args], XmNeditable, !read_only); num_args++; + + XtSetArg(args[num_args], XmNeditMode, XmMULTI_LINE_EDIT); num_args++; + w = XmCreateScrolledText(parent, name, args, num_args); + XtManageChild(w); +#else + + XtSetArg(args[num_args], XtNstring, ""); num_args++; if (! read_only) { - XtSetArg(args[1], XtNeditType, XawtextEdit); num_args++; + XtSetArg(args[num_args], XtNeditType, XawtextEdit); num_args++; } w = XtCreateManagedWidget(name, asciiTextWidgetClass, parent, args, num_args); +#endif return w; } @@ -136,8 +163,12 @@ { set_changed(w); XtVaSetValues(w, +#ifdef MOTIF + XmNvalue, string, +#else XtNstring, string, XtNtype, XawAsciiString, +#endif 0); } @@ -155,7 +186,14 @@ String TextGetString(w) Widget w; { - String s, ptr; + String s; + +#ifdef MOTIF + s = XmTextGetString(w); + if (s == NULL) + s = XtNewString(""); +#else + String ptr; long right; XawTextPosition total_read; Widget source; @@ -177,6 +215,7 @@ ptr += b.length; } *ptr = '\0'; +#endif return s; } @@ -188,11 +227,15 @@ long TextGetLength(w) Widget w; { +#ifdef MOTIF + return XmTextGetLastPosition(w); +#else String str = TextGetString(w); long len = strlen(str); XtFree(str); return len; +#endif } @@ -209,6 +252,12 @@ Widget w; String file; { +#ifdef MOTIF + FILE *fs; + char buffer[BUFSIZ + 1]; + size_t size, end = 0; +#endif + /* This is necessary because of a bug in the AsciiText widget -- it redraws the new file twice when the file being displayed is @@ -217,10 +266,29 @@ */ TextClear(w); set_changed(w); + +#ifdef MOTIF + /* XXX This needs to be changed. - jik 5/28/97 */ + if (! (fs = fopen(file, "r"))) { + sprintf(buffer, "Can't open article file %s", file); + XmTextSetString(w, buffer); + return; + } + XtUnmanageChild(w); + while ((size = fread(buffer, 1, BUFSIZ, fs))) { + buffer[size] = '\0'; /* make sure we're nul terminated */ + XmTextInsert(w, end, buffer); + end += size; + } + fclose(fs); + TextSetInsertionPoint(w, 0); + XtManageChild(w); +#else XtVaSetValues(w, XtNstring, file, XtNtype, XawAsciiFile, 0); +#endif } /* @@ -236,11 +304,15 @@ String TextGetFile(w) Widget w; { +#ifdef MOTIF + return XmTextGetString(w); +#else String file; XtVaGetValues(w, XtNstring, &file, 0); return XtNewString(file); +#endif } @@ -253,6 +325,16 @@ int length; long left, right; { +#ifdef MOTIF + char save_char; + + set_changed(w); + + save_char = string[length]; + string[length] = '\0'; + XmTextReplace(w, left, right, string); + string[length] = save_char; +#else XawTextBlock b; XawTextEditType type; @@ -266,6 +348,7 @@ XtVaSetValues(w, XtNeditType, XawtextEdit, 0); XawTextReplace(w, (XawTextPosition) left, (XawTextPosition) right, &b); XtVaSetValues(w, XtNeditType, type, 0); +#endif } /* @@ -280,7 +363,16 @@ String string; long left, right; { +#ifdef MOTIF + char save; + + save = string[right]; + string[right] = '\0'; TextReplace(w, string + left, right - left, left, right); + string[right] = save; +#else + TextReplace(w, string + left, right - left, left, right); +#endif } @@ -297,6 +389,18 @@ Widget w; long *left_ptr, *right_ptr; { +#ifdef MOTIF + XmTextPosition left, right; + XmTextSource source; + + left = right = TextGetInsertionPoint(w); + + source = XmTextGetSource(w); + + left = source->Scan(source, left, XmSELECT_LINE, XmsdLeft, 1, False); + right = source->Scan(source, right, XmSELECT_LINE, XmsdRight, 1, True); + +#else Widget source; long left, right; @@ -308,6 +412,7 @@ XawstEOL, XawsdLeft, 1, False); right = XawTextSourceScan(source, (XawTextPosition) right, XawstEOL, XawsdRight, 1, True); +#endif *left_ptr = left; *right_ptr = right; @@ -331,6 +436,22 @@ Widget w; long *left, *right; { +#ifdef MOTIF + XmTextPosition left_ret, right_ret; + XmTextSource source; + XmTextPosition right2; + + XmTextGetSelectionPosition(w, &left_ret, &right_ret); + + if (left_ret == right_ret) { + *left = *right = left_ret; + return False; + } + + source = XmTextGetSource(w); + left_ret = source->Scan(source, left_ret, XmSELECT_LINE, XmsdLeft, 1, False); + right2 = source->Scan(source, right_ret, XmSELECT_LINE, XmsdRight, 1, False); +#else XawTextPosition left_ret, right_ret; Widget source; XawTextPosition right2; @@ -351,6 +472,7 @@ /* i.e., the end of the selection isn't already at a line beginning */ right_ret = XawTextSourceScan(source, (XawTextPosition) right_ret, XawstEOL, XawsdRight, 1, True); +#endif if (left_ret == right_ret) { *left = *right = left_ret; @@ -389,7 +511,11 @@ if (TextGetSelectedLines(w, &garbage, &garbage)) { set_changed(w); +#ifdef MOTIF + XmTextClearSelection(w, XtLastTimestampProcessed(XtDisplay(w))); +#else XawTextUnsetSelection(w); +#endif } } @@ -400,7 +526,11 @@ long TextGetTopPosition(w) Widget w; { +#ifdef MOTIF + return XmTextGetTopCharacter(w); +#else return XawTextTopPosition(w); +#endif } /* @@ -428,7 +558,11 @@ if (top == pos) return; set_changed(w); +#ifdef MOTIF + XmTextSetTopCharacter(w, pos); +#else XtVaSetValues(w, XtNdisplayPosition, (XawTextPosition) pos, 0); +#endif } @@ -462,7 +596,11 @@ if (! --ptr->count) { if (ptr->changed) +#ifdef MOTIF + XmTextEnableRedisplay(w); +#else XawTextEnableRedisplay(w); +#endif free_redisplay(ptr); } } @@ -479,7 +617,11 @@ return; if (ptr->changed) { +#ifdef MOTIF + XmTextEnableRedisplay(w); +#else XawTextEnableRedisplay(w); +#endif ptr->changed = False; } } @@ -491,7 +633,11 @@ long TextGetInsertionPoint(w) Widget w; { +#ifdef MOTIF + return XmTextGetInsertionPosition(w); +#else return XawTextGetInsertionPoint(w); +#endif } /* @@ -508,7 +654,11 @@ return; set_changed(w); +#ifdef MOTIF + XmTextSetInsertionPosition(w, pos); +#else XawTextSetInsertionPoint(w, (XawTextPosition) pos); +#endif } @@ -521,6 +671,14 @@ Widget w; long position; { +#ifdef MOTIF + XmTextPosition left, right; + XmTextSource source; + + source = XmTextGetSource(w); + left = source->Scan(source, position, XmSELECT_LINE, XmsdLeft, 1, False); + right = source->Scan(source, position, XmSELECT_LINE, XmsdRight, 1, True); +#else Widget source; long left, right; @@ -530,6 +688,7 @@ XawstEOL, XawsdLeft, 1, False); right = XawTextSourceScan(source, (XawTextPosition) position, XawstEOL, XawsdRight, 1, True); +#endif if (left != right) TextReplace(w, 0, 0, left, right); @@ -560,7 +719,7 @@ } /* - Scroll forward or back a single ine in a text widget. + Scroll forward or back a single line in a text widget. */ void TextScrollLine(w, direction) Widget w; @@ -592,6 +751,9 @@ Widget w; int lines; { +#ifdef MOTIF + XtVaSetValues(w, XmNrows, (short)lines, NULL); +#else Widget sink; int height; Position tm, bm; @@ -603,6 +765,7 @@ 0); height = XawTextSinkMaxHeight(sink, lines) + tm + bm; XtVaSetValues(w, XtNheight, (Dimension) height, 0); +#endif } /* @@ -611,6 +774,12 @@ int TextGetLines(w) Widget w; { +#ifdef MOTIF + short rows; + + XtVaGetValues(w, XmNrows, &rows, NULL); + return rows; +#else Widget sink; Dimension height; Position tm, bm; @@ -622,6 +791,7 @@ XtNtopMargin, &tm, 0); return XawTextSinkMaxLines(sink, height - tm - bm); +#endif } @@ -633,6 +803,12 @@ int TextGetColumns(w) Widget w; { +#ifdef MOTIF + short columns; + + XtVaGetValues(w, XmNcolumns, &columns, NULL); + return columns; +#else XFontStruct *font; Dimension lm, width; Position rm; @@ -645,6 +821,7 @@ 0); return((width - lm - rm) / font->max_bounds.width); +#endif } @@ -659,7 +836,20 @@ always return true when it should have, because I can't figure out any way to reliably determine with an Xaw text widget whether or not we're on the last page, without modifying the text widget (and - therefore causing screen flickers). */ + therefore causing screen flickers). +*/ +/* In Motif, we could do this by (steps 3&4 emulate goto last line on screen): + * turning display updates off, + * getting our position and save it, + * jump to the to of the "page", + * go down the number of rows showing minus 1, + * goto the end-of-line, + * get the position comparing it to XmTextGetLastPosition, + * put ourselves back to the stored position before we started this mess, + * turning display updates on, + * and return the value of the compare. + But is it really worth the trouble? (I see no equivalent in Xaw either.) +*/ Boolean TextLastPage(w) Widget w; { @@ -674,7 +864,7 @@ Boolean TextPastLastPage(w) Widget w; { - XawTextPosition top = TextGetTopPosition(w); + long top = TextGetTopPosition(w); String string = TextGetString(w), ptr; Boolean ret; @@ -706,9 +896,16 @@ void TextSetLineSelections(w) Widget w; { +#ifdef MOTIF + static XmTextScanType array[] = {XmSELECT_LINE}; + + XtVaSetValues(w, XmNselectionArrayCount, XtNumber(array), + XmNselectionArray, array, 0); +#else static XawTextSelectType array[] = {XawselectLine, XawselectNull}; XtVaSetValues(w, XtNselectTypes, array, 0); +#endif } /* @@ -717,12 +914,21 @@ void TextSetAllSelections(w) Widget w; { +#ifdef MOTIF + static XmTextScanType array[] = { + XmSELECT_POSITION, XmSELECT_WORD, XmSELECT_LINE, XmSELECT_ALL + }; + + XtVaSetValues(w, XmNselectionArrayCount, XtNumber(array), + XmNselectionArray, array, 0); +#else static XawTextSelectType array[] = { XawselectPosition, XawselectChar, XawselectWord, XawselectLine, XawselectParagraph, XawselectAll, XawselectNull }; XtVaSetValues(w, XtNselectTypes, array, 0); +#endif } @@ -737,7 +943,12 @@ len = TextGetLength(w); set_changed(w); + +#ifdef MOTIF + XmTextSetSelection(w, 0, len + 1, XtLastTimestampProcessed(XtDisplay(w))); +#else XawTextSetSelection(w, (XawTextPosition) 0, (XawTextPosition) (len + 1)); +#endif } @@ -755,6 +966,15 @@ TextDirection direction; String string; { +#ifdef MOTIF + Boolean rc; + XmTextPosition position; + + rc = XmTextFindString(w, (XmTextPosition)start, string, + (direction == TextSearchLeft) ? + XmTEXT_BACKWARD : XmTEXT_FORWARD, &position); + return (long)(rc ? position : -1); +#else XawTextBlock b; Widget source; XawTextPosition ret; @@ -773,8 +993,37 @@ return -1; else return ret; +#endif } +/* + Do an interactive search of the contents of the Text widget. + */ +void TextSearchInteractive(w, e, start, direction, initial) + Widget w; + XEvent *e; + long start; + TextDirection direction; + String initial; +{ + String params[2]; + Cardinal num_params = 1; + + if (direction == TextSearchRight) + params[0] = "forward"; + else + params[0] = "forward"; + + if (initial) { + params[1] = initial; + num_params++; + } + + if (start > 0) + TextSetInsertionPoint(w, start); + + XtCallActionProc(w, "search", e, params, num_params); +} /* Enable word wrap on a Text widget. @@ -782,7 +1031,11 @@ void TextEnableWordWrap(w) Widget w; { +#ifdef MOTIF + XtVaSetValues(w, XmNwordWrap, True, 0); +#else XtVaSetValues(w, XtNwrap, XawtextWrapWord, 0); +#endif } /* @@ -791,5 +1044,9 @@ void TextDisableWordWrap(w) Widget w; { +#ifdef MOTIF + XtVaSetValues(w, XmNwordWrap, False, 0); +#else XtVaSetValues(w, XtNwrap, XawtextWrapNever, 0); +#endif } diff -u -d -r -N -P 8.02/Text.h 9.00/Text.h --- 8.02/Text.h Mon Feb 20 14:02:27 1995 +++ 9.00/Text.h Sun Jun 29 11:31:44 1997 @@ -3,10 +3,16 @@ #include +#ifdef MOTIF +# define TEXT_PANE_CHILD(w) XtParent(w) +#else +# define TEXT_PANE_CHILD(w) w +#endif + typedef enum { TextSearchLeft, TextSearchRight } TextDirection; extern Widget TextCreate _ARGUMENTS((String, - /* Boolean */ int, + Boolean, Widget)); extern void TextDestroy _ARGUMENTS((Widget)); extern void TextClear _ARGUMENTS((Widget)); @@ -48,6 +54,9 @@ extern void TextSelectAll _ARGUMENTS((Widget)); extern long TextSearch _ARGUMENTS((Widget, long, TextDirection, + String)); +extern void TextSearchInteractive _ARGUMENTS((Widget, XEvent *, + long, TextDirection, String)); extern void TextEnableWordWrap _ARGUMENTS((Widget)); extern void TextDisableWordWrap _ARGUMENTS((Widget)); diff -u -d -r -N -P 8.02/XRn.src 9.00/XRn.src --- 8.02/XRn.src Thu May 9 14:16:03 1996 +++ 9.00/XRn.src Thu Jan 15 21:36:45 1998 @@ -1,7 +1,8 @@ ! -! $Id: XRn.src,v 1.109 1996/05/09 18:15:43 jik Exp $ +! $Id: XRn.src,v 1.168 1998/01/16 02:36:44 jik Exp $ ! -xAPP_CLASSx.version: 8.02 +MOTIFxAPP_CLASSx.version: 9.00 (Motif) +XAWxAPP_CLASSx.version: 9.00 xAPP_CLASSx.Geometry: 680x700 *breakLength: 0 *font: 8x13 @@ -9,8 +10,10 @@ *Command.font: 9x15 *Dialog.borderWidth: 1 *Dialog.default.accelerators: #override \n\ - Linefeed: set() notify() unset() \n\ - Return: set() notify() unset() + Linefeed: XawSet() XawNotify() XawUnSet() \n\ + Return: XawSet() XawNotify() XawUnSet() +MOTIF*XmLabel.fontList: 7x13 +MOTIF*XmPushButton.fontList: 7x13bold *lockFile: ~/.xrnlock @@ -28,9 +31,12 @@ *authenticatorCommand: xterm -geometry 30x5+1+1 -T 'News Authentication' -e "%s" ! Composition pane stuff -*Composition.pane.text.scrollVertical: always -*Composition.pane.text.wrap: never -*Composition.pane.text.autoFill: true +XAW*Composition.pane.text.scrollVertical: always +XAW*Composition.pane.text.wrap: never +XAW*Composition.pane.text.autoFill: true +XAW*Composition*showGrip: false +MOTIF*Composition.pane*text.scrollVertical: true +MOTIF*Composition.pane*text.wrap: false ! button keybindings @@ -46,8 +52,9 @@ Right: no-op(RingBell) \n *ngFrame.newsgroups.baseTranslations: #override \n\ - :: extend-end(PRIMARY, CUT_BUFFER0) doPrefetch() \n\ - :: extend-end(PRIMARY, CUT_BUFFER0) doPrefetch() \n\ + :: extend-end() doPrefetch() \n\ +XAW :: extend-end() doPrefetch() \n\ +MOTIF Shift: extend-end() doPrefetch() \n\ :: select-start() extend-end() doTheRightThing(jump) \n\ q: ngQuit() \n\ space: ngRead() \n\ @@ -80,15 +87,15 @@ Left: no-op(RingBell) \n\ Right: no-op(RingBell) \n -*ngUnsub.baseTranslations: #override \n\ +*ngUnsub*baseTranslations: #override \n\ : notify() unset() doPrefetch() -*ngCatchUp.baseTranslations: #override \n\ +*ngCatchUp*baseTranslations: #override \n\ : notify() unset() doPrefetch() -*ngNext.baseTranslations: #override \n\ +*ngNext*baseTranslations: #override \n\ : notify() unset() doPrefetch() -*ngScroll.baseTranslations: #override \n\ +*ngScroll*baseTranslations: #override \n\ : notify() unset() doPrefetch() -*ngScrollBack.baseTranslations: #override \n\ +*ngScrollBack*baseTranslations: #override \n\ : notify() unset() doPrefetch() *artFrame.subjects.baseTranslations: #override \n\ @@ -97,7 +104,7 @@ q: artQuit() \n\ :~Ctrl ~Metan: artNextUnread() \n\ :~Ctrl ~MetaN: artNext() \n\ - ~Ctrlp: artPrev() \n\ + ~Ctrl~Metap: artPrev() \n\ -: artLast() \n\ Linefeed: artCurrent() \n\ Return: artCurrent() \n\ @@ -109,14 +116,16 @@ ~Shift.: artGotoArticle() \n\ j: artMarkRead() \n\ ~Shiftm: artMarkUnread() \n\ + +: artSub() \n\ u: artUnsub() \n\ Ctrln: artSubNext() \n\ Ctrlp: artSubPrev() \n\ + Metap: artThreadParent() \n\ ShiftL: artListOld() \n\ - :~Ctrl ~Metak: artKillSession() \n\ - :~Ctrl ~MetaK: artKillLocal() \n\ - Ctrlk: artKillGlobal() \n\ + ~Metak: artKillSubject() \n\ Metak: artKillAuthor() \n\ + ~Metat: artKillThread() \n\ + Metat: artKillSubthread() \n\ ~Meta/: artSubSearch() \n\ Meta/: artContinue() \n\ ~Shift Ctrlv: artScroll() \n\ @@ -150,7 +159,9 @@ ShiftX: artRot13() \n\ ~Ctrl ~Metav: artHeader() \n\ Left: no-op(RingBell) \n\ - Right: no-op(RingBell) \n + Right: no-op(RingBell) \n\ + ~Ctrlo: artResort() \n\ + Ctrlo: artResort(false) \n *allFrame.list.baseTranslations: #override \n\ : select-start() extend-end() allGoto() \n\ @@ -163,6 +174,8 @@ Next: allScroll() \n\ Metav: allScrollBack() \n\ Prior: allScrollBack() \n\ + /: allSearch() \n\ + l: allLimit() \n\ ~Shifts: allSub() \n\ \^: allFirst() \n\ \$: allLast() \n\ @@ -180,33 +193,54 @@ Left: no-op(RingBell) \n\ Right: no-op(RingBell) \n +! Kill button actions + +*artKillSubject*baseTranslations: #override \n\ + : set() \n\ + : notify() unset() \n + +*artKillAuthor*baseTranslations: #override \n\ + : set() \n\ + : notify() unset() \n + +*artKillThread*baseTranslations: #override \n\ + : set() \n\ + : notify() unset() \n + +*artKillSubthread*baseTranslations: #override \n\ + : set() \n\ + : notify() unset() \n + ! Default button lists -*addButtonList: addQuit addFirst addLast addAfter addUnsub +*addButtonList: addQuit addIgnoreRest addFirst addLast addAfter \ + addUnsub addIgnore *ngButtonList: ngQuit ngRead ngNext ngPrev ngCatchUp ngSubscribe \ ngUnsub ngGoto ngAllGroups ngRescan ngPrevGroup ngListOld \ ngSelect ngMove ngExit ngCheckPoint ngGripe ngPost \ ngPostAndMail -*allButtonList: allQuit allNext allPrev allSub allFirst allLast \ - allAfter allGoto allSelect allMove allToggle allPost \ - allPostAndMail +*allButtonList: allQuit allNext allPrev allSearch allLimit allSub \ + allFirst allLast allAfter allUnsub allIgnore allGoto \ + allSelect allMove allToggle allPost allPostAndMail *artButtonList: artQuit artNextUnread artNext artPrev artLast \ artNextGroup artCatchUp artFedUp artGotoArticle artMarkRead \ - artMarkUnread artUnsub artSubNext artSubPrev artListOld \ - artKillSession artKillLocal artKillGlobal artKillAuthor \ - artSubSearch artContinue artPost artPostAndMail artExit \ - artCheckPoint + artMarkUnread artUnsub artSubNext artSubPrev artThreadParent \ + artListOld artResort artKillSubject artKillAuthor \ + artKillThread artKillSubthread artSubSearch \ + artContinue artPost artPostAndMail artExit artCheckPoint *artSpecButtonList: artSave artReply artForward artFollowup \ artFollowupAndReply artCancel artRot13 artXlate artHeader \ artPrint ! scrollbars, resize, wrap -*popup.dialog.value.scrollVertical: never -*popup.dialog.value.scrollHorizontal: never +XAW*popup.dialog.value.scrollVertical: never +XAW*popup.dialog.value.scrollHorizontal: never +MOTIF*popup.dialog*value.scrollVertical: False +MOTIF*popup.dialog*value.scrollHorizontal: False ! A few directions for vpane resizing... @@ -219,27 +253,41 @@ ! Add mode -*addFrame.list.scrollVertical: always -*addFrame.list.cursor: left_ptr +XAW*addFrame.list.scrollVertical: always +MOTIF*addFrame*list.scrollVertical: True +*addFrame*list.cursor: left_ptr ! All mode -*allFrame.list.scrollVertical: always -*allFrame.list.cursor: left_ptr +XAW*allFrame.list.scrollVertical: always +MOTIF*allFrame*list.scrollVertical: True +*allFrame*list.cursor: left_ptr ! Article mode -*artFrame.subjects.scrollVertical: always -*artFrame.subjects.resizeToPreferred: True -*artFrame.subjects.skipAdjust: True -*artFrame.subjects.cursor: left_ptr -*artFrame.text.scrollVertical: always -*artFrame.text.wrap: word +MOTIF*artFrame.subjectsSW.resizeToPreferred: True +MOTIF*artFrame.subjectsSW.skipAdjust: False + +XAW*artFrame*subjects.scrollVertical: always +MOTIF*artFrame*subjects.scrollVertical: True +*artFrame*subjects.resizeToPreferred: True +*artFrame*subjects.skipAdjust: True +*artFrame*subjects.cursor: left_ptr +MOTIF*artFrame*subjects.editable: False + +XAW*artFrame*text.scrollVertical: always +XAW*artFrame*text.wrap: word +MOTIF*artFrame*text.scrollVertical: True +MOTIF*artFrame*text.wrap: True +MOTIF*artFrame*text.editable: false ! Newsgroup mode -*ngFrame.newsgroups.scrollVertical: always -*ngFrame.newsgroups.cursor: left_ptr +XAW*ngFrame*newsgroups.scrollVertical: always +XAW*ngFrame*newsgroups.cursor: left_ptr +MOTIF*ngFrame*newsgroups.scrollVertical: True +MOTIF*ngFrame*newsgroups.cursor: left_ptr +MOTIF*ngFrame*newsgroups.editable: false ! button names LANG_english*addQuit.label: Quit @@ -334,6 +382,10 @@ LANG_german*allScroll.label: Seite nach unten LANG_english*allScrollBack.label: Scroll backward LANG_german*allScrollBack.label: Seite nach oben +LANG_english*allSearch.label: Search +LANG_german*allSearch.label: Search +LANG_english*allLimit.label: Limit list +LANG_german*allLimit.label: Limit list LANG_english*allPost.label: Post LANG_german*allPost.label: Artikel ver\366ffentlichen LANG_english*allPostAndMail.label: Post & Mail @@ -387,18 +439,22 @@ LANG_german*artMarkUnread.label: Artikel nicht gelesen LANG_english*artUnsub.label: Unsubscribe LANG_german*artUnsub.label: Gruppe verwerfen +LANG_english*artSub.label: Subscribe +LANG_german*artSub.label: Gruppe abonnieren LANG_english*artSubNext.label: Subject next LANG_german*artSubNext.label: Suche vorw\344rts LANG_english*artSubPrev.label: Subject prev LANG_german*artSubPrev.label: Suche zur\374ck -LANG_english*artKillSession.label: Session kill -LANG_german*artKillSession.label: f\374r Sitzung l\366schen -LANG_english*artKillLocal.label: Local kill -LANG_german*artKillLocal.label: f\374r Gruppe l\366schen -LANG_english*artKillGlobal.label: Global kill -LANG_german*artKillGlobal.label: f\374r immer l\366schen +LANG_english*artThreadParent.label: Goto parent +LANG_german*artThreadParent.label: Goto parent +LANG_english*artKillSubject.label: Subject kill +LANG_german*artKillSubject.label: Subject kill LANG_english*artKillAuthor.label: Author kill LANG_german*artKillAuthor.label: Verfasser l\366schen +LANG_english*artKillThread.label: Thread kill +LANG_german*artKillThread.label: Thread kill +LANG_english*artKillSubthread.label: Subthread kill +LANG_german*artKillSubthread.label: Subthread kill LANG_english*artSubSearch.label: Subject search LANG_german*artSubSearch.label: Suchen Thema LANG_english*artContinue.label: Continue @@ -415,6 +471,8 @@ LANG_german*artGripe.label: Nachricht an XRN-Betreuer LANG_english*artListOld.label: List old LANG_german*artListOld.label: alle Artikel +LANG_english*artResort.label: Resort list +LANG_german*artResort.label: Resort list LANG_english*artCheckPoint.label: Checkpoint LANG_german*artCheckPoint.label: Aktualisieren @@ -456,20 +514,35 @@ LANG_english*CancelListOld*label: Cancel LANG_german*CancelListOld*label: Abbruch -*Information.pane.label.showGrip: False -LANG_english*Information.pane.label.label: Information (can be left up or dismissed) -LANG_german*Information.pane.label.label: Informationen (Fenster kann ge\366ffnet bleiben) -*Information.pane.text.showGrip: False +LANG_english*CancelThreadParent*label: Cancel +LANG_german*CancelThreadParent*label: Abbruch + +XAW*Information.geometry: 600x150 +XAW*Information.pane.label.showGrip: False +XAW*Information.pane.text.showGrip: False +XAW*Information.pane.box.skipAdjust: True +XAW*Information*mesgDismiss.accelerators: #override \n\ +XAW Linefeed: set() notify() unset() \n\ +XAW Return: set() notify() unset() +XAWLANG_english*Information*mesgDismiss*label: Dismiss +XAWLANG_german*Information*mesgDismiss*label: Verwerfen +XAWLANG_english*Information*mesgClear*label: Clear +XAWLANG_german*Information*mesgClear*label: L\366schen + +MOTIF*Information.autoUnmanage: False +MOTIF*Information*text.width: 500 +MOTIF*Information*text.height: 150 +MOTIFLANG_english*Information*cancelLabelString: Dismiss +MOTIFLANG_german*Information*cancelLabelString: Verwerfen +MOTIFLANG_english*Information*okLabelString: Clear +MOTIFLANG_german*Information*okLabelString: L\366schen + +LANG_english*Information*label.label: Information (can be left up or dismissed) +LANG_german*Information*label.label: Informationen (Fenster kann ge\366ffnet bleiben) *Information.saveUnder: False -*Information.geometry: 600x150 -*Information.pane.text.displayCaret: False -*Information.pane.text.scrollVertical: always -*Information.pane.text.wrap: word -*Information.pane.box.skipAdjust: True -*Information*mesgDismiss.accelerators: #override \n\ - Linefeed: set() notify() unset() \n\ - Return: set() notify() unset() -LANG_english*Information*mesgDismiss*label: Dismiss -LANG_german*Information*mesgDismiss*label: Verwerfen -LANG_english*Information*mesgClear*label: Clear -LANG_german*Information*mesgClear*label: L\366schen +*Information*text.displayCaret: False +XAW*Information*text.scrollVertical: always +XAW*Information*text.wrap: word +MOTIF*Information*text.scrollVertical: True +MOTIF*Information*text.wordWrap: True +MOTIF*Information*text.editable: false diff -u -d -r -N -P 8.02/Xmisc.c 9.00/Xmisc.c --- 8.02/Xmisc.c Wed Dec 31 19:00:00 1969 +++ 9.00/Xmisc.c Thu Jun 5 07:11:39 1997 @@ -0,0 +1,237 @@ + +#if !defined(lint) && !defined(SABER) && !defined(GCC_WALL) +static char XRNrcsid[] = "$Id: Xmisc.c,v 1.2 1997/03/30 15:33:56 jik Exp $"; +#endif + +/* + * xrn - an X-based NNTP news reader + * + * Copyright (c) 1988-1993, Ellen M. Sentovich and Rick L. Spickelmier. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, 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 the University of California not + * be used in advertising or publicity pertaining to distribution of + * the software without specific, written prior permission. The University + * of California makes no representations about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + * + * THE UNIVERSITY OF CALIFORNIA DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA 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. + */ + +/* + * xmisc.c: routines for handling miscellaneous x functions + * + */ + +#include "copyright.h" +#include "config.h" +#include "utils.h" +#include +#include +#include +#include +#include +#include +#include "news.h" +#include "xthelper.h" +#include "resources.h" +#include "internals.h" +#include "xrn.h" +#include "xmisc.h" +#include "busyCursor.h" + +/* XRN icon */ + +#if SUPPORT_SILLY_CALVIN_ICON +#include "calvin.icon" +#endif +#include "xrn.icon" + +static Pixmap +getpm() +{ + unsigned int width, height; + unsigned char *bits; + +#if SUPPORT_SILLY_CALVIN_ICON + if (app_resources.calvin) { + width = calvin_width; + height = calvin_height; + bits = calvin_bits; + } else { +#endif + width = xrn_width; + height = xrn_height; + bits = xrn_bits; +#if SUPPORT_SILLY_CALVIN_ICON + } +#endif + + return XCreateBitmapFromData(XtDisplay(TopLevel), XtScreen(TopLevel)->root, + (char *) bits, width, height); +} + + +void +xmSetIconAndName(it) +IconType it; +{ + static char *PrevName = NULL, *OldName = NULL; + static Pixmap PrevPm = None, OldPm = None; + char *name WALL(= 0); + Pixmap pm WALL(= 0); + Arg arg; + + if (OldPm == None) { + XtSetArg(arg, XtNiconPixmap, &OldPm); + XtGetValues(TopLevel, &arg, 1); + PrevPm = OldPm; + } + + if (app_resources.iconPixmap == None) { + app_resources.iconPixmap = getpm(); + } + + if (app_resources.unreadIconPixmap == None) { + app_resources.unreadIconPixmap = app_resources.iconPixmap; + } + + switch (it) { + case InitIcon : + PrevPm = pm = app_resources.iconPixmap; + PrevName = name = OldName = app_resources.iconName; + break; + case UnreadIcon : + PrevPm = pm = app_resources.unreadIconPixmap; + PrevName = name = app_resources.unreadIconName; + break; + case ReadIcon : + PrevPm = pm = app_resources.iconPixmap; + PrevName = name = app_resources.iconName; + break; + case BusyIcon : + if ((pm = app_resources.busyIconPixmap) == None) + pm = OldPm; + if ((name = app_resources.busyIconName) == NULL) + name = OldName; + break; + case PrevIcon : + pm = PrevPm; + name = PrevName; + break; + } + + if (OldPm != pm) { + XtSetArg(arg, XtNiconPixmap, pm); + XtSetValues(TopLevel, &arg, 1); + OldPm = pm; + } + + if (name == NULL) { + name = app_resources.iconName; + } + + if (OldName != name) { + XSetIconName(XtDisplay(TopLevel),XtWindow(TopLevel),name); + XFlush(XtDisplay(TopLevel)); + OldName = name; + } +} + + +void +xmIconCreate() +{ + xmSetIconAndName(InitIcon); + if (app_resources.iconGeometry != NIL(char)) { + int scr, x, y, junk; + Arg args[2]; + + for(scr = 0; /* yyuucchh */ + XtScreen(TopLevel) != ScreenOfDisplay(XtDisplay(TopLevel), scr); + scr++); + + XGeometry(XtDisplay(TopLevel), scr, app_resources.iconGeometry, + "", 0, 0, 0, 0, 0, &x, &y, &junk, &junk); + XtSetArg(args[0], XtNiconX, x); + XtSetArg(args[1], XtNiconY, y); + XtSetValues(TopLevel, args, XtNumber(args)); + } + return; +} + + +/* + * create the normal and busy xrn cursors + */ + +void xrnBusyCursor() +{ + BusyCursor(TopLevel, True); + xmSetIconAndName(BusyIcon); + + return; +} + +void xrnUnbusyCursor() +{ + UnbusyCursor(TopLevel, True); + xmSetIconAndName(PrevIcon); + + return; +} + +/*ARGSUSED*/ +void CBbusyCursor(widget, client_data, call_data) + Widget widget; + XtPointer client_data; + XtPointer call_data; +{ + xrnBusyCursor(); + return; +} + +/*ARGSUSED*/ +void CBunbusyCursor(widget, client_data, call_data) + Widget widget; + XtPointer client_data; + XtPointer call_data; +{ + xrnUnbusyCursor(); + return; +} + +/* + A Command widget is passed in. Its border width/shadow and related + stuff is grown so that it's larger than other buttons (so that it is + visibly the default button). + */ + +void makeDefaultButton(widget) + Widget widget; +{ + Dimension border_width, highlight_thickness; + + XtVaGetValues(widget, + XtNborderWidth, (XtArgVal) &border_width, + XtNhighlightThickness, (XtArgVal) &highlight_thickness, + 0); + + border_width++; + highlight_thickness++; + + XtVaSetValues(widget, + XtNborderWidth, (XtArgVal) border_width, + XtNhighlightThickness, (XtArgVal) highlight_thickness, + 0); +} diff -u -d -r -N -P 8.02/Xresources.sam 9.00/Xresources.sam --- 8.02/Xresources.sam Wed Nov 30 11:26:47 1994 +++ 9.00/Xresources.sam Tue Dec 16 22:10:56 1997 @@ -1,98 +0,0 @@ -! -! $Id: Xresources.sam,v 1.2 1994/11/23 01:42:47 jik Exp $ -! -xrn.newsrcFile: ~/.newsrc -xrn.nntpServer: news-server-machine -xrn.editorCommand: xterm -e vi %s -xrn.iconGeometry: +64+521 -xrn.leaveHeaders: subject,from,organization -xrn.Gripe.geometry: +50+50 -! -xrn.ngButtonList: ngQuit,ngRead,ngCatchUp,ngGoto,ngRescan,\ - ngSubscribe,ngPost,ngGripe,ngAllGroups -xrn.artButtonList: artQuit,artNextUnread,artNext,artPrev,artCatchUp,\ - artPost,artGripe,artNextGroup -! -xrn*index.accelerators: #override \n\ - Down: next-line() \n\ - Up: previous-line() -! -xrn.ngBindings: \ - Q: ngQuit() \n\ - N: ngRead() \n\ - P: ngPrev() -xrn.artBindings: \ - Q: artQuit() \n\ - N: artNextUnread() \n\ - P: artPrev() -! -xrn*background: plum -xrn*foreground: red -xrn*font: 9x15 -xrn*border: LightGray -! -xrn.Information.geometry: +400+400 -! -xrn*breakLength: 0 -xrn*Composition*Text*wrap: never -xrn*Composition*Text*autoFill: true -xrn*Composition.geometry: 660+0+0 -! -xrn*Text*background: DarkSlateGray -xrn*Text*foreground: yellow -xrn*Text*Background: DarkSlateGray -xrn*Text*Foreground: yellow -! -xrn.vpane.index.thickness: 22 -xrn.vpane.articleText.thickness: 10 -! -xrn*Label.background: cyan -xrn*Label.foreground: blue -xrn*Label.font: -adobe-times-bold-r-normal--14-140-75-75-p-77-iso8859-1 - -xrn*Command.foreground: White -xrn*Command.background: coral -xrn*Command.font: -adobe-itc avant garde gothic-book-r-normal--12-120-75-75-p-70-iso8859-1 -! -xrn*Box.ngQuit.foreground: Black -xrn*Box.ngQuit.background: red -xrn*Box.ngExit.foreground: yellow -xrn*Box.ngExit.background: orange -xrn*Box.ngRescan.foreground: cyan -xrn*Box.ngRescan.background: violet -xrn*Box.ngUnsub.foreground: green -xrn*Box.ngUnsub.background: blue -xrn*Box.ngSubscribe.foreground: blue -xrn*Box.ngSubscribe.background: green -xrn*Box.artQuit.foreground: Black -xrn*Box.artQuit.background: red -xrn*Box.artSave.foreground: NavyBlue -xrn*Box.artSave.background: LimeGreen -xrn*Box.artNextUnread.font: 9x15 -xrn*Box.addQuit.foreground: Black -xrn*Box.addQuit.background: red -! -xrn*dialog*font: -adobe-helvetica-bold-r-normal--12-120-75-75-p-70-iso8859-1 -xrn*dialog*background: LimeGreen -xrn*dialog*foreground: CornflowerBlue -xrn*dialog*Label.foreground: NavyBlue -xrn*dialog*Command.foreground: Black -xrn*dialog*Command.background: yellow -xrn*dialog*Text*background: white -xrn*dialog*Text*foreground: black -xrn*dialog*borderWidth: 2 -! -xrn.confirm: ngCatchUp,artCatchUp -xrn.signatureFile: ~/.signature -xrn.geometry: =760x770+25+25 -xrn.deadLetters: ~/dead.letters -xrn.savePostings: ~/Articles -xrn.topLines: 9 -xrn.saveMode: mailbox,headers -xrn.minLines: 3 -xrn.maxLines: 6 -xrn.tmpDir: /tmp -xrn.mailer: /usr/lib/sendmail -oi -t -!xrn.organization: your organization name here -!xrn.replyTo: somebody@somehost.edu -! diff -u -d -r -N -P 8.02/activecache.c 9.00/activecache.c --- 8.02/activecache.c Wed Nov 15 08:54:45 1995 +++ 9.00/activecache.c Thu Jun 5 07:11:40 1997 @@ -76,16 +76,22 @@ mesgPane(XRN_SERIOUS, 0, ERROR_WRITING_SAVE_FILE_MSG, tmpfile, errmsg(errno)); \ (void) fclose(input); \ (void) fclose(output); \ + XtFree(tmpfile); \ return -1; \ } \ } -int active_cache_write(filename, Newsrc, num_groups, write_entries) -char *filename; -struct newsgroup **Newsrc; -ng_num num_groups; -Boolean write_entries; +int active_cache_write( + _ANSIDECL(char *, filename), + _ANSIDECL(struct newsgroup **, Newsrc), + _ANSIDECL(ng_num, num_groups), + _ANSIDECL(Boolean, write_entries) + ) + _KNRDECL(char *, filename) + _KNRDECL(struct newsgroup **, Newsrc) + _KNRDECL(ng_num, num_groups) + _KNRDECL(Boolean, write_entries) { FILE *input, *output; char *tmpfile; @@ -97,6 +103,7 @@ if (! (output = fopen(tmpfile, "w"))) { mesgPane(XRN_SERIOUS, 0, CANT_OPEN_TEMP_MSG, tmpfile, errmsg(errno)); + XtFree(tmpfile); return -1; } @@ -137,6 +144,7 @@ mesgPane(XRN_SERIOUS, 0, ERROR_WRITING_SAVE_FILE_MSG, tmpfile, errmsg(errno)); (void) fclose(output); + XtFree(tmpfile); return -1; } } @@ -145,15 +153,18 @@ if (fclose(output) == EOF) { mesgPane(XRN_SERIOUS, 0, ERROR_WRITING_SAVE_FILE_MSG, tmpfile, errmsg(errno)); + XtFree(tmpfile); return -1; } if (rename(tmpfile, filename)) { mesgPane(XRN_SERIOUS, 0, ERROR_RENAMING_MSG, tmpfile, filename, errmsg(errno)); + XtFree(tmpfile); return -1; } + XtFree(tmpfile); return 0; } diff -u -d -r -N -P 8.02/activecache.h 9.00/activecache.h --- 8.02/activecache.h Sun Nov 5 09:23:05 1995 +++ 9.00/activecache.h Thu Jun 5 07:11:40 1997 @@ -6,6 +6,6 @@ extern void active_cache_read _ARGUMENTS((char *)); extern int active_cache_write _ARGUMENTS((char *, struct newsgroup **, - /* ng_num */ int, /* Boolean */ int)); + ng_num, Boolean)); #endif /* _ACTIVECACHE_H_ */ diff -u -d -r -N -P 8.02/addMode.c 9.00/addMode.c --- 8.02/addMode.c Fri Dec 22 05:27:35 1995 +++ 9.00/addMode.c Thu Jun 5 07:11:40 1997 @@ -1,6 +1,12 @@ #include -#include +#ifdef MOTIF +# include +#else +# include +#endif +/* #include +*/ #include "config.h" #include "buttons.h" @@ -43,17 +49,17 @@ int AddActionsCount = XtNumber(AddActions); -ButtonList AddButtonList[] = { - {"addQuit", addQuitCallbacks, ADDQUIT_EXSTR}, - {"addIgnoreRest", addIgnoreRestCallbacks, ADDIGNORE_REST_EXSTR}, - {"addFirst", addFirstCallbacks, ADDFIRST_EXSTR}, - {"addLast", addLastCallbacks, ADDLAST_EXSTR}, - {"addAfter", addAfterCallbacks, ADDAFTER_EXSTR}, - {"addUnsub", addUnsubCallbacks, ADDUNSUB_EXSTR}, - {"addIgnore", addIgnoreCallbacks, ADDIGNORE_EXSTR}, +static ButtonList AddButtonList[] = { + {"addQuit", addQuitCallbacks, ADDQUIT_EXSTR, True}, + {"addIgnoreRest", addIgnoreRestCallbacks, ADDIGNORE_REST_EXSTR, True}, + {"addFirst", addFirstCallbacks, ADDFIRST_EXSTR, True}, + {"addLast", addLastCallbacks, ADDLAST_EXSTR, True}, + {"addAfter", addAfterCallbacks, ADDAFTER_EXSTR, True}, + {"addUnsub", addUnsubCallbacks, ADDUNSUB_EXSTR, True}, + {"addIgnore", addIgnoreCallbacks, ADDIGNORE_EXSTR, True}, }; -int AddButtonListCount = XtNumber(AddButtonList); +static int AddButtonListCount = XtNumber(AddButtonList); /* * release storage associated with add mode and go to newsgroup mode @@ -372,21 +378,27 @@ void displayAddWidgets() { if (! AddFrame) { - AddFrame = XtCreateManagedWidget("addFrame", panedWidgetClass, + AddFrame = XtCreateManagedWidget("addFrame", +#ifdef MOTIF + xmPanedWindowWidgetClass, +#else + panedWidgetClass, +#endif TopLevel, 0, 0); +#ifndef MOTIF XawPanedSetRefigureMode(AddFrame, False); +#endif /* MOTIF */ + + if (app_resources.fullNewsrc) { + setButtonActive(AddButtonList, "addIgnoreRest", False); + setButtonActive(AddButtonList, "addIgnore", False); + } - /* - The Box widget is managed only after - its children have been placed in them because there is a - bug in the Xaw Box widget (as of 05/06/95). - */ #define BUTTON_BOX() {\ AddButtonBox = ButtonBoxCreate("buttons", AddFrame);\ doButtons(app_resources.addButtonList, AddButtonBox,\ AddButtonList, &AddButtonListCount, TOP);\ - XtManageChild(AddButtonBox);\ } #define INFO_LINE() {\ @@ -418,9 +430,12 @@ setButtonSensitive(AddButtonBox, "addIgnore", False); } +#ifdef MOTIF + XmProcessTraversal(AddFrame, XmTRAVERSE_CURRENT); +#else XawPanedSetRefigureMode(AddFrame, True); - XtSetKeyboardFocus(AddFrame, AddText); +#endif } else { TopInfoLine = AddInfoLine; diff -u -d -r -N -P 8.02/allMode.c 9.00/allMode.c --- 8.02/allMode.c Fri Dec 22 05:27:39 1995 +++ 9.00/allMode.c Fri Jul 18 08:57:35 1997 @@ -28,11 +28,15 @@ static Widget AllFrame, AllText, AllInfoLine, AllButtonBox; static long First, Last; +static char *LimitString = NULL; + BUTTON(allQuit,quit); BUTTON(allNext,next); BUTTON(allPrev,prev); BUTTON(allScroll,scroll forward); BUTTON(allScrollBack,scroll backward); +BUTTON(allSearch,search); +BUTTON(allLimit,limit); BUTTON(allSub,subscribe); BUTTON(allFirst,subscribe first); BUTTON(allLast,subscribe last); @@ -53,6 +57,8 @@ {"allPrev", allPrevAction}, {"allScroll", allScrollAction}, {"allScrollBack", allScrollBackAction}, + {"allSearch", allSearchAction}, + {"allLimit", allLimitAction}, {"allSub", allSubAction}, {"allFirst", allFirstAction}, {"allLast", allLastAction}, @@ -71,27 +77,29 @@ int AllActionsCount = XtNumber(AllActions); static ButtonList AllButtonList[] = { - {"allQuit", allQuitCallbacks, ALLQUIT_EXSTR}, - {"allNext", allNextCallbacks, NGNEXT_EXSTR}, - {"allPrev", allPrevCallbacks, NGPREV_EXSTR}, - {"allScroll", allScrollCallbacks, ALLSCROLL_EXSTR}, - {"allScrollBack", allScrollBackCallbacks, ALLSCROLLBACK_EXSTR}, - {"allSub", allSubCallbacks, ALLSUB_EXSTR}, - {"allFirst", allFirstCallbacks, ALLFIRST_EXSTR}, - {"allLast", allLastCallbacks, ALLLAST_EXSTR}, - {"allAfter", allAfterCallbacks, ALLAFTER_EXSTR}, - {"allUnsub", allUnsubCallbacks, ALLUNSUB_EXSTR}, - {"allIgnore", allIgnoreCallbacks, ALLIGNORE_EXSTR}, - {"allGoto", allGotoCallbacks, ALLGOTO_EXSTR}, - {"allSelect", allSelectCallbacks, ALLSELECT_EXSTR}, - {"allMove", allMoveCallbacks, ALLMOVE_EXSTR}, - {"allToggle", allToggleCallbacks, ALLTOGGLE_EXSTR}, - {"allPost", allPostCallbacks, ALLPOST_EXSTR}, - {"allPostAndMail", allPostAndMailCallbacks, ALLPOST_AND_MAIL_EXSTR}, - {"allMail", allMailCallbacks, MAIL_EXSTR}, + {"allQuit", allQuitCallbacks, ALLQUIT_EXSTR, True}, + {"allNext", allNextCallbacks, NGNEXT_EXSTR, True}, + {"allPrev", allPrevCallbacks, NGPREV_EXSTR, True}, + {"allScroll", allScrollCallbacks, ALLSCROLL_EXSTR, True}, + {"allScrollBack", allScrollBackCallbacks, ALLSCROLLBACK_EXSTR, True}, + {"allSearch", allSearchCallbacks, ALLSEARCH_EXSTR, True}, + {"allLimit", allLimitCallbacks, ALLLIMIT_EXSTR, True}, + {"allSub", allSubCallbacks, ALLSUB_EXSTR, True}, + {"allFirst", allFirstCallbacks, ALLFIRST_EXSTR, True}, + {"allLast", allLastCallbacks, ALLLAST_EXSTR, True}, + {"allAfter", allAfterCallbacks, ALLAFTER_EXSTR, True}, + {"allUnsub", allUnsubCallbacks, ALLUNSUB_EXSTR, True}, + {"allIgnore", allIgnoreCallbacks, ALLIGNORE_EXSTR, True}, + {"allGoto", allGotoCallbacks, ALLGOTO_EXSTR, True}, + {"allSelect", allSelectCallbacks, ALLSELECT_EXSTR, True}, + {"allMove", allMoveCallbacks, ALLMOVE_EXSTR, True}, + {"allToggle", allToggleCallbacks, ALLTOGGLE_EXSTR, True}, + {"allPost", allPostCallbacks, ALLPOST_EXSTR, True}, + {"allPostAndMail", allPostAndMailCallbacks, ALLPOST_AND_MAIL_EXSTR, True}, + {"allMail", allMailCallbacks, MAIL_EXSTR, True}, }; -int AllButtonListCount = XtNumber(AllButtonList); +static int AllButtonListCount = XtNumber(AllButtonList); static void allResetSelection() { @@ -110,7 +118,10 @@ return; } - new = getStatusString(TextGetColumns(AllText), AllStatus); + while (! (new = getStatusString(TextGetColumns(AllText), + AllStatus, LimitString))) { + FREE(LimitString); + } if (!AllGroupsString || strcmp(AllGroupsString, new)) { allResetSelection(); @@ -146,7 +157,8 @@ void switchToAllMode() { FREE(AllGroupsString); - + FREE(LimitString); /* also done in allQuitFunction, but it + doesn't hurt to be cautious */ PreviousMode = CurrentMode; CurrentMode = ALL_MODE; /* switch buttons */ @@ -154,7 +166,8 @@ setBottomInfoLine(VIEW_ALLNG_SUB_MSG); /* create the screen */ - AllGroupsString = getStatusString(TextGetColumns(AllText), AllStatus); + AllGroupsString = getStatusString(TextGetColumns(AllText), AllStatus, + LimitString); TextSetString(AllText, AllGroupsString); @@ -176,6 +189,9 @@ } FREE(AllGroupsString); + FREE(LimitString); /* also done in switchToAllMode, but it doesn't + hurt to be cautious */ + switchToNewsgroupMode(False); } @@ -267,7 +283,7 @@ XtFree(current_group); } - + /* * Make the selected group(s) subscribed to, and leave them in * their current position in the newsrc file. @@ -360,7 +376,7 @@ if (CurrentMode != ALL_MODE) { return; } - + if (AllBox == (Widget) 0) { AllBox = CreateDialog(TopLevel, BEHIND_WHAT_GROUP_MSG , DIALOG_TEXT, args, XtNumber(args)); @@ -433,6 +449,86 @@ } /* + Search the group list, using the search functionality built into the Text + widget. + */ +void allSearchFunction(widget, event, string, count) + Widget widget; + XEvent *event; + String *string; + Cardinal *count; +{ + if (CurrentMode != ALL_MODE) + return; + + TextSearchInteractive(AllText, + event ? event : XtLastEventProcessed(XtDisplay(widget)), + -1, TextSearchRight, + (count && *count) ? string[0] : NULL); +} + +static Widget LimitBox = (Widget) 0; + +static void limitHandler _ARGUMENTS((Widget, XtPointer, XtPointer)); + +/*ARGSUSED*/ +static void limitHandler(widget, client_data, call_data) + Widget widget; + XtPointer client_data; + XtPointer call_data; +{ + if (inCommand) + return; + + inCommand = 1; + xrnBusyCursor(); + + if (strcmp(DOIT_STRING, (char *) client_data) == 0) { + FREE(LimitString); + + if ((LimitString = GetDialogValue(LimitBox))) + if (! *LimitString) + LimitString = NULL; + else + LimitString = XtNewString(LimitString); + + redrawAllWidget(); + } + + if (LimitBox) { + PopDownDialog(LimitBox); + LimitBox = 0; + } + + inCommand = 0; + xrnUnbusyCursor(); + return; +} + +/*ARGSUSED*/ +void allLimitFunction(widget, event, string, count) + Widget widget; + XEvent *event; + String *string; + Cardinal *count; +{ + static struct DialogArg args[] = { + {ABORT_STRING,limitHandler, (XtPointer) ABORT_STRING}, + {DOIT_STRING, limitHandler, (XtPointer) DOIT_STRING}, + }; + + if (CurrentMode != ALL_MODE) { + return; + } + if (LimitBox == (Widget) 0) { + LimitBox = CreateDialog(TopLevel, REGULAR_EXPR_MSG, + DIALOG_TEXT, args, XtNumber(args)); + } + PopUpDialog(LimitBox); + return; +} + +/* * Go to the current newsgroup. The current * group is either the first group of a selection, * or, if there is no selection, the group the cursor @@ -451,7 +547,7 @@ if (CurrentMode != ALL_MODE) { return; } - + /* get the current group name */ if (! (allNewsgroupIterator(True, 0) && @@ -505,7 +601,7 @@ if (CurrentMode != ALL_MODE) { goto done; } - + /* get the current group name */ if (! (allNewsgroupIterator(True, 0) && @@ -586,7 +682,7 @@ if (CurrentMode != ALL_MODE) { return; } - + if (TextGetSelectedOrCurrentLines(AllText, &First, &Last)) TextUnsetSelection(AllText); @@ -680,7 +776,7 @@ return; } -/* +/* * Change the order the groups appear on the screen. */ /*ARGSUSED*/ @@ -701,6 +797,21 @@ return; } +static void resizeAllText _ARGUMENTS((Widget, XtPointer, XEvent *, + Boolean *)); + +static void resizeAllText(widget, client_data, event, + continue_to_dispatch) + Widget widget; + XtPointer client_data; + XEvent *event; + Boolean *continue_to_dispatch; +{ + if (event->type == ConfigureNotify) { + redrawAllWidget(); + } +} + void displayAllWidgets() { if (! AllFrame) { @@ -709,16 +820,15 @@ XawPanedSetRefigureMode(AllFrame, False); - /* - The Box widget is managed only after - its children have been placed in them because there is a - bug in the Xaw Box widget (as of 05/06/95). - */ + setButtonActive(AllButtonList, "allPost", PostingAllowed); + setButtonActive(AllButtonList, "allPostAndMail", PostingAllowed); + if (app_resources.fullNewsrc) + setButtonActive(AllButtonList, "allIgnore", False); + #define BUTTON_BOX() {\ AllButtonBox = ButtonBoxCreate("buttons", AllFrame);\ doButtons(app_resources.allButtonList, AllButtonBox,\ AllButtonList, &AllButtonListCount, TOP);\ - XtManageChild(AllButtonBox);\ } #define INFO_LINE() {\ @@ -745,14 +855,12 @@ TopInfoLine = AllInfoLine; - setButtonSensitive(AllButtonBox, "allPost", PostingAllowed); - setButtonSensitive(AllButtonBox, "allPostAndMail", PostingAllowed); - if (app_resources.fullNewsrc) - setButtonSensitive(AllButtonBox, "allIgnore", False); - XawPanedSetRefigureMode(AllFrame, True); XtSetKeyboardFocus(AllFrame, AllText); + + XtAddEventHandler(AllText, StructureNotifyMask, FALSE, + resizeAllText, NULL); } else { TopInfoLine = AllInfoLine; diff -u -d -r -N -P 8.02/allMode.h 9.00/allMode.h --- 8.02/allMode.h Thu Sep 7 11:20:15 1995 +++ 9.00/allMode.h Sun Jun 29 11:53:04 1997 @@ -12,6 +12,8 @@ BUTDECL(allPrev); BUTDECL(allScroll); BUTDECL(allScrollBack); +BUTDECL(allSearch); +BUTDECL(allLimit); BUTDECL(allFirst); BUTDECL(allLast); BUTDECL(allAfter); diff -u -d -r -N -P 8.02/artMode.c 9.00/artMode.c --- 8.02/artMode.c Thu May 2 04:11:42 1996 +++ 9.00/artMode.c Sun Dec 28 11:35:27 1997 @@ -29,8 +29,13 @@ #include "allMode.h" #include "snapshot.h" #include "utils.h" +#include "artstruct.h" +#include "killfile.h" +#include "sort.h" +#include "file_cache.h" static int getPrevious _ARGUMENTS((art_num *)); +static long findArticle _ARGUMENTS((char *, art_num, Boolean)); /* The string to which the list of newsgroups is moved when switching @@ -66,6 +71,9 @@ static char *SaveString = 0; /* last input to save box */ +/* Is the article list currently sorted? */ +static Boolean list_sorted_p; + /* article mode "modes" ... determines what to do: jump out of article */ /* mode, change the subject string, or do nothing */ #define art_DONE 0 @@ -87,6 +95,8 @@ #define art_UNREAD (1<<4) /* retrieve only unread articles */ static Boolean cursorOnCurrent _ARGUMENTS((void)); +static void setListSorted _ARGUMENTS((Boolean)); +static void setListed _ARGUMENTS((art_num, art_num, Boolean)); BUTTON(artQuit,quit); BUTTON(artNextUnread,next unread); @@ -102,6 +112,7 @@ BUTTON(artFedUp,fed up); BUTTON(artMarkRead,mark read); BUTTON(artMarkUnread,mark unread); +BUTTON(artSub,subscribe); BUTTON(artUnsub,unsubscribe); BUTTON(artScroll,scroll forward); BUTTON(artScrollBack,scroll backward); @@ -113,10 +124,11 @@ BUTTON(artScrollIndexBack, scroll index back); BUTTON(artSubNext,subject next); BUTTON(artSubPrev,subject prev); -BUTTON(artKillSession,session kill); -BUTTON(artKillLocal,local kill); -BUTTON(artKillGlobal,global kill); +BUTTON(artThreadParent,parent); +BUTTON(artKillSubject,subject kill); BUTTON(artKillAuthor,author kill); +BUTTON(artKillThread,thread kill); +BUTTON(artKillSubthread,subthread kill); BUTTON(artSubSearch,subject search); BUTTON(artContinue,continue); BUTTON(artPost,post); @@ -126,6 +138,7 @@ BUTTON(artCheckPoint,checkpoint); BUTTON(artGripe,gripe); BUTTON(artListOld,list old); +BUTTON(artResort, resort); BUTTON(artSave,save); BUTTON(artReply,reply); @@ -155,6 +168,7 @@ {"artFedUp", artFedUpAction}, {"artMarkRead", artMarkReadAction}, {"artMarkUnread", artMarkUnreadAction}, + {"artSub", artSubAction}, {"artUnsub", artUnsubAction}, {"artScroll", artScrollAction}, {"artScrollBack", artScrollBackAction}, @@ -166,10 +180,11 @@ {"artScrollIndexBack", artScrollIndexBackAction}, {"artSubNext", artSubNextAction}, {"artSubPrev", artSubPrevAction}, - {"artKillSession", artKillSessionAction}, - {"artKillLocal", artKillLocalAction}, - {"artKillGlobal", artKillGlobalAction}, + {"artThreadParent", artThreadParentAction}, + {"artKillSubject", artKillSubjectAction}, {"artKillAuthor", artKillAuthorAction}, + {"artKillThread", artKillThreadAction}, + {"artKillSubthread", artKillSubthreadAction}, {"artSubSearch", artSubSearchAction}, {"artContinue", artContinueAction}, {"artPost", artPostAction}, @@ -179,6 +194,7 @@ {"artCheckPoint", artCheckPointAction}, {"artGripe", artGripeAction}, {"artListOld", artListOldAction}, + {"artResort", artResortAction}, {"artSave", artSaveAction}, {"artReply", artReplyAction}, {"artForward", artForwardAction}, @@ -195,65 +211,68 @@ int ArtActionsCount = XtNumber(ArtActions); -ButtonList ArtButtonList[] = { - {"artQuit", artQuitCallbacks, ARTQUIT_EXSTR}, - {"artNextUnread", artNextUnreadCallbacks, ARTNEXTUNREAD_EXSTR}, - {"artNext", artNextCallbacks, ARTNEXT_EXSTR}, - {"artPrev", artPrevCallbacks, ARTPREV_EXSTR}, - {"artLast", artLastCallbacks, ARTLAST_EXSTR}, - {"artCurrent", artCurrentCallbacks, ARTCURRENT_EXSTR}, - {"artUp", artUpCallbacks, ARTUP_EXSTR}, - {"artDown", artDownCallbacks, ARTDOWN_EXSTR}, - {"artNextGroup", artNextGroupCallbacks, ARTNEXTGROUP_EXSTR}, - {"artGotoArticle", artGotoArticleCallbacks, ARTGOTOARTICLE_EXSTR}, - {"artCatchUp", artCatchUpCallbacks, ARTCATCHUP_EXSTR}, - {"artFedUp", artFedUpCallbacks, ARTFEEDUP_EXSTR}, - {"artMarkRead", artMarkReadCallbacks, ARTMARKREAD_EXSTR}, - {"artMarkUnread", artMarkUnreadCallbacks, ARTMARKUNREAD_EXSTR}, - {"artUnsub", artUnsubCallbacks, ARTUNSUB_EXSTR}, - {"artScroll", artScrollCallbacks, ARTSCROLL_EXSTR}, - {"artScrollBack", artScrollBackCallbacks, ARTSCROLLBACK_EXSTR}, - {"artScrollLine", artScrollLineCallbacks, ARTSCROLLLINE_EXSTR}, - {"artScrollBackLine", artScrollBackLineCallbacks, ARTSCROLLBACKLINE_EXSTR}, - {"artScrollEnd", artScrollEndCallbacks, ARTSCROLLEND_EXSTR}, - {"artScrollBeginning", artScrollBeginningCallbacks, ARTSCROLLBEGINNING_EXSTR}, - {"artScrollIndex", artScrollIndexCallbacks, ARTSCROLLINDEX_EXSTR}, - {"artScrollIndexBack", artScrollIndexBackCallbacks, ARTSCROLLINDEXBACK_EXSTR}, - {"artSubNext", artSubNextCallbacks, ARTSUBNEXT_EXSTR}, - {"artSubPrev", artSubPrevCallbacks, ARTSUBPREV_EXSTR}, - {"artKillSession", artKillSessionCallbacks, ARTKILLSESSION_EXSTR}, - {"artKillLocal", artKillLocalCallbacks, ARTKILLLOCAL_EXSTR}, - {"artKillGlobal", artKillGlobalCallbacks, ARTKILLGLOBAL_EXSTR}, - {"artKillAuthor", artKillAuthorCallbacks, ARTKILLAUTHOR_EXSTR}, - {"artSubSearch", artSubSearchCallbacks, ARTSUBSEARCH_EXSTR}, - {"artContinue", artContinueCallbacks, ARTCONTINUE_EXSTR}, - {"artPost", artPostCallbacks, ARTPOST_EXSTR}, - {"artPostAndMail", artPostAndMailCallbacks, ARTPOST_AND_MAIL_EXSTR}, - {"artMail", artMailCallbacks, MAIL_EXSTR}, - {"artExit", artExitCallbacks, ARTEXIT_EXSTR}, - {"artCheckPoint", artCheckPointCallbacks, ARTCHECKPOINT_EXSTR}, - {"artGripe", artGripeCallbacks, ARTGRIPE_EXSTR}, - {"artListOld", artListOldCallbacks, ARTLISTOLD_EXSTR}, +static ButtonList ArtButtonList[] = { + {"artQuit", artQuitCallbacks, ARTQUIT_EXSTR, True}, + {"artNextUnread", artNextUnreadCallbacks, ARTNEXTUNREAD_EXSTR, True}, + {"artNext", artNextCallbacks, ARTNEXT_EXSTR, True}, + {"artPrev", artPrevCallbacks, ARTPREV_EXSTR, True}, + {"artLast", artLastCallbacks, ARTLAST_EXSTR, True}, + {"artCurrent", artCurrentCallbacks, ARTCURRENT_EXSTR, True}, + {"artUp", artUpCallbacks, ARTUP_EXSTR, True}, + {"artDown", artDownCallbacks, ARTDOWN_EXSTR, True}, + {"artNextGroup", artNextGroupCallbacks, ARTNEXTGROUP_EXSTR, True}, + {"artGotoArticle", artGotoArticleCallbacks, ARTGOTOARTICLE_EXSTR, True}, + {"artCatchUp", artCatchUpCallbacks, ARTCATCHUP_EXSTR, True}, + {"artFedUp", artFedUpCallbacks, ARTFEEDUP_EXSTR, True}, + {"artMarkRead", artMarkReadCallbacks, ARTMARKREAD_EXSTR, True}, + {"artMarkUnread", artMarkUnreadCallbacks, ARTMARKUNREAD_EXSTR, True}, + {"artSub", artSubCallbacks, ARTSUB_EXSTR, True}, + {"artUnsub", artUnsubCallbacks, ARTUNSUB_EXSTR, True}, + {"artScroll", artScrollCallbacks, ARTSCROLL_EXSTR, True}, + {"artScrollBack", artScrollBackCallbacks, ARTSCROLLBACK_EXSTR, True}, + {"artScrollLine", artScrollLineCallbacks, ARTSCROLLLINE_EXSTR, True}, + {"artScrollBackLine", artScrollBackLineCallbacks, ARTSCROLLBCKLN_EXSTR, True}, + {"artScrollEnd", artScrollEndCallbacks, ARTSCROLLEND_EXSTR, True}, + {"artScrollBeginning", artScrollBeginningCallbacks, ARTSCROLLBEG_EXSTR, True}, + {"artScrollIndex", artScrollIndexCallbacks, ARTSCROLLINDEX_EXSTR, True}, + {"artScrollIndexBack", artScrollIndexBackCallbacks, ARTSCROLLINDBCK_EXSTR, True}, + {"artSubNext", artSubNextCallbacks, ARTSUBNEXT_EXSTR, True}, + {"artSubPrev", artSubPrevCallbacks, ARTSUBPREV_EXSTR, True}, + {"artThreadParent", artThreadParentCallbacks, ARTPARENT_EXSTR, True}, + {"artKillSubject", artKillSubjectCallbacks, ARTKILLSUBJECT_EXSTR, True}, + {"artKillAuthor", artKillAuthorCallbacks, ARTKILLAUTHOR_EXSTR, True}, + {"artKillThread", artKillThreadCallbacks, ARTKILLTHREAD_EXSTR, True}, + {"artKillSubthread", artKillSubthreadCallbacks, ARTKILLSUBTHREAD_EXSTR,True}, + {"artSubSearch", artSubSearchCallbacks, ARTSUBSEARCH_EXSTR, True}, + {"artContinue", artContinueCallbacks, ARTCONTINUE_EXSTR, True}, + {"artPost", artPostCallbacks, ARTPOST_EXSTR, True}, + {"artPostAndMail", artPostAndMailCallbacks, ARTPOST_MAIL_EXSTR, True}, + {"artMail", artMailCallbacks, MAIL_EXSTR, True}, + {"artExit", artExitCallbacks, ARTEXIT_EXSTR, True}, + {"artCheckPoint", artCheckPointCallbacks, ARTCHECKPOINT_EXSTR, True}, + {"artGripe", artGripeCallbacks, ARTGRIPE_EXSTR, True}, + {"artListOld", artListOldCallbacks, ARTLISTOLD_EXSTR, True}, + {"artResort", artResortCallbacks, ARTRESORT_EXSTR, True}, }; -int ArtButtonListCount = XtNumber(ArtButtonList); +static int ArtButtonListCount = XtNumber(ArtButtonList); -ButtonList ArtSpecButtonList[] = { - {"artSave", artSaveCallbacks, ARTSAVE_EXSTR}, - {"artReply", artReplyCallbacks, ARTREPLY_EXSTR}, - {"artForward", artForwardCallbacks, ARTFORWARD_EXSTR}, - {"artFollowup", artFollowupCallbacks, ARTFOLLOWUP_EXSTR}, - {"artFollowupAndReply", artFollowupAndReplyCallbacks, ARTFOLLOWUPANDREPLY_EXSTR}, - {"artCancel", artCancelCallbacks, ARTCANCEL_EXSTR}, - {"artRot13", artRot13Callbacks, ARTROT13_EXSTR}, +static ButtonList ArtSpecButtonList[] = { + {"artSave", artSaveCallbacks, ARTSAVE_EXSTR, True}, + {"artReply", artReplyCallbacks, ARTREPLY_EXSTR, True}, + {"artForward", artForwardCallbacks, ARTFORWARD_EXSTR, True}, + {"artFollowup", artFollowupCallbacks, ARTFOLLOWUP_EXSTR, True}, + {"artFollowupAndReply", artFollowupAndReplyCallbacks, ARTFOLLOWREPL_EXSTR, True}, + {"artCancel", artCancelCallbacks, ARTCANCEL_EXSTR, True}, + {"artRot13", artRot13Callbacks, ARTROT13_EXSTR, True}, #ifdef XLATE - {"artXlate", artXlateCallbacks, ARTXLATE_EXSTR}, + {"artXlate", artXlateCallbacks, ARTXLATE_EXSTR, True}, #endif /*XLATE */ - {"artHeader", artHeaderCallbacks, ARTHEADER_EXSTR}, - {"artPrint", artPrintCallbacks, ARTPRINT_EXSTR}, + {"artHeader", artHeaderCallbacks, ARTHEADER_EXSTR, True}, + {"artPrint", artPrintCallbacks, ARTPRINT_EXSTR, True}, }; -int ArtSpecButtonListCount = XtNumber(ArtSpecButtonList); +static int ArtSpecButtonListCount = XtNumber(ArtSpecButtonList); /* Adjust the upper text window so that the number of lines above the @@ -320,7 +339,7 @@ for (numLines = 1; numLines < def; numLines++) { if (! moveCursor(BACK, SubjectString, &top)) break; - + } for (below += numLines; below <= height; below++) { if (! moveCursor(BACK, SubjectString, &top)) @@ -342,13 +361,20 @@ constraints are met, if update_top is true. */ static void updateSubjectWidget _ARGUMENTS((String, long, - long, /* Boolean */ int, - /* Boolean */ int)); - -static void updateSubjectWidget(string, left, right, update_top, preserve_top) - String string; - long left, right; - Boolean update_top, preserve_top; + long, Boolean, Boolean)); + +static void updateSubjectWidget( + _ANSIDECL(String, string), + _ANSIDECL(long, left), + _ANSIDECL(long, right), + _ANSIDECL(Boolean, update_top), + _ANSIDECL(Boolean, preserve_top) + ) + _KNRDECL(String, string) + _KNRDECL(long, left) + _KNRDECL(long, right) + _KNRDECL(Boolean, update_top) + _KNRDECL(Boolean, preserve_top) { Boolean disable = False; long top WALL(= 0); @@ -386,11 +412,12 @@ article, depending on the value of status. Return the filename, question and number of the article obtained. */ -static int getNearbyArticle _ARGUMENTS((int, char **, char **, long *)); +static int getNearbyArticle _ARGUMENTS((int, file_cache_file **, char **, long *)); -static int getNearbyArticle(status, filename, question, artNum) +static int getNearbyArticle(status, file, question, artNum) int status; - char **filename, **question; + file_cache_file **file; + char **question; long *artNum; { int mesg_name = newMesgPaneName(); @@ -431,16 +458,16 @@ *artNum = atol(&SubjectString[ArtPosition + 2]); - gotoArticle(*artNum); - - if (getArticle(filename, question) != XRN_OKAY) { + if (getArticle(CurrentGroup, *artNum, file, question) != XRN_OKAY) { mesgPane(XRN_SERIOUS, mesg_name, ART_NOT_AVAIL_MSG, *artNum); TextRemoveLine(SubjectText, ArtPosition); removeLine(SubjectString, &ArtPosition); + setListed(*artNum, *artNum, False); if (status & art_FORWARD) move = False; continue; } + CurrentGroup->current = *artNum; ret = art_CHANGE; break; } @@ -449,20 +476,56 @@ return ret; } +static void setListSorted( + _ANSIDECL(Boolean, sorted) + ) + _KNRDECL(Boolean, sorted) +{ + setButtonSensitive(SubjectButtonBox, "artResort", !sorted); + list_sorted_p = sorted; +} + +static void setListed( + _ANSIDECL(art_num, first), + _ANSIDECL(art_num, last), + _ANSIDECL(Boolean, listed) + ) + _KNRDECL(art_num, first) + _KNRDECL(art_num, last) + _KNRDECL(Boolean, listed) +{ + art_num i; + + if (! first) + return; + + for (i = first; i <= last; i++) { + struct article *art = artStructGet(CurrentGroup, i, True); + if (IS_MAYBE_LISTED(art) || IS_LISTED(art)) { + /* Clear MAYBE_LISTED */ + SET_UNLISTED(art); + /* Maybe set LISTED */ + if (listed) + SET_LISTED(art); + } + artStructSet(CurrentGroup, &art); + } + return; +} + #define CHANGE 0 /* subject window has changed */ #define NOCHANGE 1 /* subject window has not changed */ #define DONE 2 /* no new article was found */ /* EXIT is already defined, it implies */ /* there are no articles left at all */ -/* - * - */ -static int isPrevSubject _ARGUMENTS((char *, char **, char **, art_num *)); +static int isPrevSubject _ARGUMENTS((char *, file_cache_file **, char **, + art_num *)); -static int isPrevSubject(subject, filename, question, artNum) +static int isPrevSubject(subject, file, question, artNum) char *subject; - char **filename, **question; + file_cache_file **file; + char **question; art_num *artNum; { long ArtPosition = TextGetInsertionPoint(SubjectText); @@ -474,10 +537,11 @@ int count = 0; int ret; int line_length = TextGetColumns(SubjectText); + art_num first_new = 0, last_new = 0; startSearch(); abortClear(); - + for (;;) { count++; @@ -502,14 +566,16 @@ if (! (newsubject = getSubject(*artNum))) goto art_unavailable; if (utSubjectCompare(newsubject, subject) == 0) { - gotoArticle(*artNum); - if (getArticle(filename, question) != XRN_OKAY) { + if (getArticle(CurrentGroup, *artNum, file, question) + != XRN_OKAY) { art_unavailable: mesgPane(XRN_SERIOUS, 0, ART_NOT_AVAIL_MSG, *artNum); TextRemoveLine(SubjectText, ArtPosition); removeLine(SubjectString, &ArtPosition); + setListed(*artNum, *artNum, False); continue; } + CurrentGroup->current = *artNum; cancelDestroy(); TextSetInsertionPoint(SubjectText, ArtPosition); ret = NOCHANGE; @@ -517,11 +583,13 @@ } continue; } else { + struct article *art; + if ((newLine = getPrevSubject(line_length)) == NIL(char)) { failedSearch(); cancelDestroy(); + setListed(first_new, last_new, False); ret = DONE; - FREE(newSubjects); goto done; } *artNum = atol(&newLine[2]); @@ -529,6 +597,15 @@ mesgPane(XRN_SERIOUS, 0, ART_NOT_AVAIL_MSG, *artNum); continue; } + + if (! last_new) + first_new = last_new = *artNum; + else + first_new = *artNum; + + art = artStructGet(CurrentGroup, *artNum, True); + SET_MAYBE_LISTED(art); + if (newSubjects) { tmp = XtMalloc(utStrlen(newSubjects) + utStrlen(newLine) + 1); (void) strcpy(tmp, newLine); @@ -541,12 +618,17 @@ if (utSubjectCompare(newsubject, subject) == 0) { /* found a match, go with it */ - gotoArticle(*artNum); - if (getArticle(filename, question) != XRN_OKAY) { + if (getArticle(CurrentGroup, *artNum, file, question) + != XRN_OKAY) { mesgPane(XRN_SERIOUS, 0, ART_NOT_AVAIL_MSG, *artNum); (void) strcpy(newSubjects, index(newSubjects, '\n')); + setListed(*artNum, *artNum, False); } else { + CurrentGroup->current = *artNum; + setListed(first_new, last_new, True); + setListSorted(False); + tmp = XtMalloc(utStrlen(newSubjects) + utStrlen(SubjectString) + 1); (void) strcpy(tmp, newSubjects); @@ -571,6 +653,7 @@ } } done: + FREE(newSubjects); return ret; } @@ -578,12 +661,14 @@ /* * */ -static int isNextSubject _ARGUMENTS((char *, long, char **, char **, art_num *)); +static int isNextSubject _ARGUMENTS((char *, long, file_cache_file **, + char **, art_num *)); -static int isNextSubject(subject, ArtPosition, filename, question, artNum) +static int isNextSubject(subject, ArtPosition, file, question, artNum) char *subject; long ArtPosition; - char **filename, **question; + file_cache_file **file; + char **question; art_num *artNum; { char *newsubject; @@ -591,7 +676,7 @@ int ret; abortClear(); - + for (;;) { count++; @@ -618,14 +703,15 @@ if (! (newsubject = getSubject(*artNum))) goto art_unavailable; if (utSubjectCompare(newsubject, subject) == 0) { - gotoArticle(*artNum); - if (getArticle(filename, question) != XRN_OKAY) { + if (getArticle(CurrentGroup, *artNum, file, question) != XRN_OKAY) { art_unavailable: mesgPane(XRN_SERIOUS, 0, ART_NOT_AVAIL_MSG, *artNum); TextRemoveLine(SubjectText, ArtPosition); removeLine(SubjectString, &ArtPosition); + setListed(*artNum, *artNum, False); continue; } + CurrentGroup->current = *artNum; cancelDestroy(); TextSetInsertionPoint(SubjectText, ArtPosition); ret = NOCHANGE; @@ -651,6 +737,8 @@ int line_length = TextGetColumns(SubjectText); if ((newLine = getPrevSubject(line_length)) != NIL(char)) { + struct article *art; + newString = XtMalloc(utStrlen(SubjectString) + utStrlen(newLine) + 1); (void) strcpy(newString, newLine); (void) strcat(newString, SubjectString); @@ -660,9 +748,15 @@ TextSetString(SubjectText, SubjectString); *artNum = atol(newLine + 2); FirstListedArticle = *artNum; + + art = artStructGet(CurrentGroup, *artNum, True); + SET_LISTED(art); + artStructSet(CurrentGroup, &art); + setListSorted(False); + return TRUE; } - + return FALSE; } @@ -691,13 +785,16 @@ */ int switchToArticleMode() { + struct newsgroup *newsgroup = CurrentGroup; int oldMode; + struct article *art; + int i; FREE(SubjectString); - FirstListedArticle = currentArticle(); + FirstListedArticle = newsgroup->current; oldMode = PreviousMode; - + PreviousMode = CurrentMode; CurrentMode = ARTICLE_MODE; @@ -721,21 +818,24 @@ PreviousMode = oldMode; } - SubjectString = getSubjects(TextGetColumns(SubjectText), - UNREAD, FirstListedArticle); + art = artStructGet(newsgroup, FirstListedArticle, True); + SET_LISTED(art); + artStructSet(newsgroup, &art); - if (! (SubjectString && *SubjectString)) { - /* - We must be entering a newsgroup with no unread articles -- - display the last read article. - */ - FREE(SubjectString); - SubjectString = getSubjects(TextGetColumns(SubjectText), - ALL, FirstListedArticle); + for (i = FirstListedArticle + 1; i <= newsgroup->last; i++) { + art = artStructGet(newsgroup, i, False); + if (IS_AVAIL(art) && IS_UNREAD(art)) { + struct article copy = *art; + SET_LISTED(©); + artStructReplace(newsgroup, &art, ©, i); + } } + SubjectString = getSubjects(TextGetColumns(SubjectText), FirstListedArticle, + False); + if (! (SubjectString && *SubjectString)) { - mesgPane(XRN_INFO, 0, PROBABLY_EXPIRED_MSG, CurrentGroup->name); + mesgPane(XRN_INFO, 0, PROBABLY_EXPIRED_MSG, newsgroup->name); CurrentMode = PreviousMode; swapMode(); /* @@ -757,18 +857,19 @@ XtFree(SaveString); } SaveString = XtNewString(app_resources.saveString); - } + } /* XXX need to detect when height changes and update the variable */ server_page_height = TextGetLines(ArticleText); setTopInfoLine(OPEARATION_APPLY_CURSOR_MSG); - if (! (app_resources.leaveHeaders || app_resources.stripHeaders)) - setButtonSensitive(ArtSpecButtonBox, "artHeader", False); setButtonSensitive(SubjectButtonBox, "artLast", False); setButtonSensitive(SubjectButtonBox, "artContinue", False); setButtonSensitive(SubjectButtonBox, "artListOld", True); + setButtonSensitive(SubjectButtonBox, "artSub", !IS_SUBSCRIBED(newsgroup)); + + setListSorted(True); TextDisableRedisplay(SubjectText); @@ -788,15 +889,16 @@ * window and redraw the mode line */ -static void redrawArticleWidget _ARGUMENTS((char *, char *)); +static void redrawArticleWidget _ARGUMENTS((file_cache_file *, char *)); -static void redrawArticleWidget(filename, question) - char *filename, *question; +static void redrawArticleWidget(file, question) + file_cache_file *file; + char *question; { String old = TextGetFile(ArticleText); - - if ((! old) || strcmp(old, filename)) { - TextSetFile(ArticleText, filename); + + if ((! old) || strcmp(old, file_cache_file_name(FileCache, *file))) { + TextSetFile(ArticleText, file_cache_file_name(FileCache, *file)); setBottomInfoLine(question); @@ -809,7 +911,7 @@ /* force the screen to update before prefetching */ xthHandlePendingExposeEvents(); - + prefetchNextArticle(); } FREE(old); @@ -824,6 +926,8 @@ PrevArticle = CurrentArticle = 0; FREE(SubjectString); + TextSetString(SubjectText, ""); + TextSetString(ArticleText, ""); releaseNewsgroupResources(CurrentGroup); if (app_resources.updateNewsrc == TRUE) { @@ -861,18 +965,25 @@ /* * Display new article, mark as read. */ -static void foundArticle _ARGUMENTS((char *, char *)); +static void foundArticle _ARGUMENTS((file_cache_file *, char *)); static void foundArticle(file, ques) - char *file, *ques; + file_cache_file *file; + char *ques; { long ArtPosition = TextGetInsertionPoint(SubjectText); - if ((PrevArticle = CurrentArticle)) + if ((PrevArticle = CurrentArticle)) { + struct article *art = artStructGet(CurrentGroup, PrevArticle, False); + setButtonSensitive(SubjectButtonBox, "artLast", True); - gotoArticle(markStringRead(SubjectString, ArtPosition)); - CurrentArticle = currentArticle(); + if (art->file) + file_cache_file_unlock(FileCache, *art->file); + } + + CurrentGroup->current = markStringRead(SubjectString, ArtPosition); + CurrentArticle = CurrentGroup->current; updateSubjectWidget(SubjectString, ArtPosition, ArtPosition + 1, True, False); @@ -898,7 +1009,8 @@ { long ArtPosition = TextGetInsertionPoint(SubjectText); - char *filename, *question; + file_cache_file *file; + char *question; long artNum; long left = 0; @@ -912,7 +1024,7 @@ (void) moveCursor(BACK, SubjectString, &left); updateSubjectWidget(SubjectString, 0, ArtPosition, True, False); if (getNearbyArticle(art_FORWARD | art_CURRENT | art_WRAP | art_UNREAD, - &filename, &question, &artNum) == + &file, &question, &artNum) == art_DONE) { if (exit_mode) exitArticleMode(); @@ -920,8 +1032,8 @@ artNextGroupFunction(NULL, NULL, NULL, NULL); return; } - foundArticle(filename, question); - + foundArticle(file, question); + return; } @@ -961,7 +1073,7 @@ { unsubscribe(); maybeExitArticleMode(); - + return; } @@ -980,7 +1092,7 @@ return; } exitArticleMode(); - + return; } @@ -994,7 +1106,7 @@ String *string; Cardinal *count; { - char *filename; /* name of the article file */ + file_cache_file *file; /* the article file */ char *question; /* question to put in the question box */ long artNum; @@ -1007,13 +1119,12 @@ if (selectedArticle() || !cursorOnCurrent()) ArtStatus |= art_CURRENT; - if (getNearbyArticle(ArtStatus, &filename, &question, - &artNum) == art_DONE) { + if (getNearbyArticle(ArtStatus, &file, &question, &artNum) == art_DONE) { maybeExitArticleMode(); goto done; } /* update the text window */ - foundArticle(filename, question); + foundArticle(file, question); ArtStatus = art_FORWARD; @@ -1032,7 +1143,7 @@ String *string; Cardinal *count; { - char *filename; /* name of the article file */ + file_cache_file *file; /* name of the article file */ char *question; /* question to put in the question box */ long artNum; @@ -1049,13 +1160,13 @@ ArtStatus |= art_CURRENT; - if (getNearbyArticle(ArtStatus, &filename, &question, + if (getNearbyArticle(ArtStatus, &file, &question, &artNum) == art_DONE) { maybeExitArticleMode(); goto done; } /* update the text window */ - foundArticle(filename, question); + foundArticle(file, question); ArtStatus = art_FORWARD; @@ -1221,14 +1332,14 @@ return False; under_cursor = atoi(SubjectString + cursor_position + 2); - return(under_cursor == currentArticle()); + return(under_cursor == CurrentGroup->current); } - - + + /* * called when the user wants to go to the next unread news * article in the current newsgroup - * + * */ /*ARGSUSED*/ void artNextUnreadFunction(widget, event, string, count) @@ -1257,7 +1368,8 @@ Cardinal *count; { art_num artNum; - char *filename, *question; + file_cache_file *file; + char *question; if (CurrentMode != ARTICLE_MODE) { return; @@ -1270,11 +1382,11 @@ if (selectedArticle() || !cursorOnCurrent()) ArtStatus |= art_CURRENT; - if (getNearbyArticle(ArtStatus, &filename, &question, &artNum) == + if (getNearbyArticle(ArtStatus, &file, &question, &artNum) == art_DONE) { goto done; } - foundArticle(filename, question); + foundArticle(file, question); done: TextEnableRedisplay(SubjectText); @@ -1455,6 +1567,32 @@ } /* + * called when the user wants to subscribe to the current group + */ +/*ARGSUSED*/ +void artSubFunction(widget, event, string, count) + Widget widget; + XEvent *event; + String *string; + Cardinal *count; +{ + struct newsgroup *newsgroup = CurrentGroup; + + if (CurrentMode != ARTICLE_MODE) + return; + + if (IS_SUBSCRIBED(newsgroup)) + return; + + if (IS_NOENTRY(newsgroup)) + addToNewsrcEnd(newsgroup->name, SUBSCRIBE); + + SET_SUB(newsgroup); + + mesgPane(XRN_INFO, 0, SUB_DONE_MSG, newsgroup->name); +} + +/* * called when the user wants to unsubscribe to the current group */ /*ARGSUSED*/ @@ -1470,20 +1608,22 @@ /* * Get selection region, mark articles, redisplay subject window. */ -static void markFunction _ARGUMENTS((/* char */ int)); +static void markFunction _ARGUMENTS((char)); -static void markFunction(marker) - char marker; +static void markFunction( + _ANSIDECL(char, marker) + ) + _KNRDECL(char, marker) { long left, right; - long ArtPosition = 0; + long ArtPosition; TextDisableRedisplay(SubjectText); if (TextGetSelectedOrCurrentLines(SubjectText, &left, &right)) TextUnsetSelection(SubjectText); markArticles(SubjectString, left, right, marker); - findArticle(SubjectString, currentArticle(), &ArtPosition); + ArtPosition = findArticle(SubjectString, CurrentGroup->current, False); TextSetInsertionPoint(SubjectText, ArtPosition); updateSubjectWidget(SubjectString, left, right, app_resources.subjectScrollBack, @@ -1544,7 +1684,7 @@ return; } post(True); - + return; } @@ -1564,7 +1704,7 @@ return; } post_and_mail(True); - + return; } @@ -1583,7 +1723,7 @@ return; } mail(); - + return; } @@ -1599,7 +1739,8 @@ Cardinal *count; { long ArtPosition; - char *filename, *question; + file_cache_file *file; + char *question; char *subject = 0; art_num artNum; int status; @@ -1611,7 +1752,7 @@ TextDisableRedisplay(SubjectText); if (selectedArticle() || !cursorOnCurrent()) { - if (getNearbyArticle(art_FORWARD | art_CURRENT | art_WRAP, &filename, + if (getNearbyArticle(art_FORWARD | art_CURRENT | art_WRAP, &file, &question, &artNum) == art_DONE) maybeExitArticleMode(); } @@ -1631,7 +1772,7 @@ (void) moveCursor(FORWARD, SubjectString, &ArtPosition); - status = isNextSubject(subject, ArtPosition, &filename, &question, + status = isNextSubject(subject, ArtPosition, &file, &question, &artNum); } @@ -1643,14 +1784,14 @@ case NOCHANGE: (void) sprintf(error_buffer, ERROR_SUBJ_SEARCH_MSG, subject); INFO(error_buffer); - foundArticle(filename, question); + foundArticle(file, question); goto done; case DONE: infoNow(ERROR_SUBJ_EXH_MSG); TextSetInsertionPoint(SubjectText, 0); if (getNearbyArticle(art_FORWARD | art_CURRENT | art_WRAP | - art_UNREAD, &filename, + art_UNREAD, &file, &question, &artNum) == art_DONE) { maybeExitArticleMode(); goto done; @@ -1663,7 +1804,7 @@ } } - foundArticle(filename, question); + foundArticle(file, question); done: TextEnableRedisplay(SubjectText); @@ -1683,7 +1824,8 @@ { char *subject = 0; art_num artNum; - char *filename, *question; + file_cache_file *file; + char *question; int status; long ArtPosition; @@ -1694,7 +1836,7 @@ TextDisableRedisplay(SubjectText); if (selectedArticle() || !cursorOnCurrent()) { - if (getNearbyArticle(art_FORWARD | art_CURRENT | art_WRAP, &filename, + if (getNearbyArticle(art_FORWARD | art_CURRENT | art_WRAP, &file, &question, &artNum) == art_DONE) maybeExitArticleMode(); } @@ -1703,7 +1845,7 @@ if (! SubjectString[ArtPosition]) goto done; - + artNum = atol(&SubjectString[ArtPosition+2]); if (! (subject = getSubject(artNum))) { mesgPane(XRN_SERIOUS, 0, ART_NOT_AVAIL_MSG, artNum); @@ -1712,7 +1854,7 @@ subject = XtNewString(subject); - status = isPrevSubject(subject, &filename, &question, &artNum); + status = isPrevSubject(subject, &file, &question, &artNum); switch (status) { case ABORT: infoNow(ERROR_SUBJ_ABORT_MSG); @@ -1731,7 +1873,7 @@ } } - foundArticle(filename, question); + foundArticle(file, question); done: FREE(subject); @@ -1739,21 +1881,164 @@ return; } +void artThreadParentFunction(widget, event, string, count) + Widget widget; + XEvent *event; + String *string; + Cardinal *count; +{ + long left, right; + art_num artNum, parent = 0; + struct article *art; + char *refs = 0, *begin_id, *end_id; + file_cache_file *file; + char *question; + Boolean cancel_created = False; + Boolean prefer_listed = True; + + if (CurrentMode != ARTICLE_MODE) + return; + + if (event || (widget && (event = XtLastEventProcessed(XtDisplay(widget))))) { + unsigned int *state = 0; + if ((event->type == KeyPress) || (event->type == KeyRelease)) + state = &event->xkey.state; + else if ((event->type == ButtonPress) || (event->type == ButtonRelease)) + state = &event->xbutton.state; + assert(state); + if (*state & ShiftMask) + prefer_listed = False; + } + + TextDisableRedisplay(SubjectText); + + if (TextGetSelectedOrCurrentLines(SubjectText, &left, &right)) { + TextUnsetSelection(SubjectText); + TextSetInsertionPoint(SubjectText, left); + } + else + goto done; + + artNum = atol(&SubjectString[left+2]); + + art = artStructGet(CurrentGroup, artNum, False); + assert(art); + + if (prefer_listed && (parent = art->parent) && + articleIsAvailable(CurrentGroup, parent)) + goto done; + + if (! ((refs = XtNewString(art->references)) && *refs)) + goto done; + + for (end_id = strchr(refs, '\0'); end_id > refs; end_id--) { + art_num block_end, block_start, checkee; + + while ((end_id > refs) && (*end_id != '>')) + end_id--; + if (end_id == refs) + break; + end_id[1] = '\0'; + for (begin_id = end_id; (begin_id >= refs) && (*begin_id != '<'); begin_id--) + /* empty */; + if (begin_id < refs) + break; + if ((parent = getArticleNumberFromIdXref(CurrentGroup, begin_id))) + if ((parent < 0) || !articleIsAvailable(CurrentGroup, parent)) { + parent = 0; + continue; + } + else + break; + if (! cancel_created) { + abortClear(); + cancelCreate("CancelThreadParent"); + cancel_created = True; + } + for (block_end = CurrentGroup->last; block_end >= CurrentGroup->first; + block_end = block_start - 1) { + block_start = MAX(block_end - 10, CurrentGroup->first); + (void) getidlist(CurrentGroup, block_start, block_end, False, 0); + for (checkee = block_end ; checkee >= block_start; checkee--) { + art = artStructGet(CurrentGroup, checkee, False); + if (art->id && !strcmp(art->id, begin_id) && + articleIsAvailable(CurrentGroup, checkee)) { + parent = checkee; + break; + } + } + if (abortP()) { + parent = -1; + break; + } + } + } + +done: + if (cancel_created) + cancelDestroy(); + if (parent > 0) { + long ArtPosition; + + (void) fillUpArray(CurrentGroup, parent, parent, False, False); + art = artStructGet(CurrentGroup, parent, True); + SET_LISTED(art); + artStructSet(CurrentGroup, &art); + + if ((ArtPosition = findArticle(SubjectString, parent, True)) < 0) { + FREE(SubjectString); + FirstListedArticle = MIN(FirstListedArticle, parent); + SubjectString = getSubjects(TextGetColumns(SubjectText), FirstListedArticle, + True); + TextSetString(SubjectText, SubjectString); + ArtPosition = findArticle(SubjectString, parent, False); + } + TextSetInsertionPoint(SubjectText, ArtPosition); + + if (getArticle(CurrentGroup, parent, &file, &question) != XRN_OKAY) { + mesgPane(XRN_SERIOUS, 0, ART_NOT_AVAIL_MSG, parent); + TextRemoveLine(SubjectText, ArtPosition); + removeLine(SubjectString, &ArtPosition); + setListed(parent, parent, False); + } + else { + foundArticle(file, question); + } + } + else if (parent == 0) { + infoNow((refs && *refs) ? ERROR_PARENT_UNAVAIL_MSG : ERROR_NO_PARENT_MSG); + } + XtFree(refs); + TextEnableRedisplay(SubjectText); + return; +} + +typedef char * (*_fixfunction) _ARGUMENTS((char *)); + /* Mark all articles with the current subject or author as read. */ -static String _artKillSession _ARGUMENTS((Widget, /* Boolean */ int)); +static String artKillSession _ARGUMENTS((int, _fixfunction, int, Boolean)); /*ARGSUSED*/ -static String _artKillSession(widget, author) - Widget widget; - Boolean author; +static String artKillSession( + _ANSIDECL(int, source_offset), + _ANSIDECL(_fixfunction, source_fix_function), + _ANSIDECL(int, target_offset), + _ANSIDECL(Boolean, show_error) + ) + _KNRDECL(int, source_offset) + _KNRDECL(_fixfunction, source_fix_function) + _KNRDECL(int, target_offset) + _KNRDECL(Boolean, show_error) { long left, right; - char *subject = 0; - char *cursubject; - char *filename, *question; + char *match_val = 0; + char *cur_val; + file_cache_file *file; + char *question; art_num artNum; + struct article *art; if (CurrentMode != ARTICLE_MODE) { return 0; @@ -1767,10 +2052,17 @@ } else goto done; - + artNum = atol(&SubjectString[left+2]); - subject = author ? getAuthor(artNum) : getSubject(artNum); - if (! subject) { + + art = artStructGet(CurrentGroup, artNum, False); + assert(art); + + match_val = *(char **)((char *)art + source_offset); + if (source_fix_function) + match_val = (*source_fix_function)(match_val); + if (! match_val) { + if (show_error) mesgPane(XRN_SERIOUS, 0, ART_NOT_AVAIL_MSG, artNum); } else { @@ -1781,17 +2073,18 @@ char *reRet; #endif - subject = stringToRegexp(subject); + match_val = stringToRegexp(match_val, + MAX_KILL_PATTERN_VALUE_LENGTH); #ifdef POSIX_REGEX - reRet = regcomp(&reStruct, subject, REG_NOSUB); + reRet = regcomp(&reStruct, match_val, REG_NOSUB); assert (! reRet); #else /* ! POSIX_REGEX */ # ifdef SYSV_REGEX - reRet = regcmp(subject, 0); + reRet = regcmp(match_val, 0); assert(reRet); # else /* ! SYSV_REGEX */ - reRet = re_comp(subject); + reRet = re_comp(match_val); assert(! reRet); # endif /* SYSV_REGEX */ #endif /* POSIX_REGEX */ @@ -1800,20 +2093,26 @@ while (SubjectString[left] != '\0') { if (SubjectString[left] != UNREAD_MARKER) { artNum = atol(&SubjectString[left+2]); - cursubject = author ? getAuthor(artNum) : getSubject(artNum); - if (! cursubject) { + art = artStructGet(CurrentGroup, artNum, False); + assert(art); + cur_val = *(char **)((char *)art + target_offset); + if (! cur_val) { + /* XXX See above. */ + struct article copy = *art; mesgPane(XRN_SERIOUS, 0, ART_NOT_AVAIL_MSG, artNum); TextRemoveLine(SubjectText, left); removeLine(SubjectString, &left); + SET_UNLISTED(©); + artStructReplace(CurrentGroup, &art, ©, artNum); } else if #ifdef POSIX_REGEX - (! regexec(&reStruct, cursubject, 0, 0, 0)) + (! regexec(&reStruct, cur_val, 0, 0, 0)) #else # ifdef SYSV_REGEX - (regex(reRet, cursubject)) + (regex(reRet, cur_val)) # else - (re_exec(cursubject)) + (re_exec(cur_val)) # endif #endif { @@ -1831,160 +2130,369 @@ regfree(&reStruct); #else # ifdef SYSV_REGEX - XtFree(reRet); + free(reRet); # endif #endif - infoNow(author ? ERROR_AUTHOR_KILL_MSG : ERROR_SUB_KILL_MSG); - } + if (getNearbyArticle(art_FORWARD | art_CURRENT | art_WRAP | art_UNREAD, + &file, &question, &artNum) + == art_DONE) { + maybeExitArticleMode(); + goto done; + } - if (getNearbyArticle(art_FORWARD | art_CURRENT | art_WRAP | art_UNREAD, - &filename, &question, &artNum) - == art_DONE) { - maybeExitArticleMode(); - goto done; + foundArticle(file, question); } - foundArticle(filename, question); - done: TextEnableRedisplay(SubjectText); - return subject; + return match_val; } - -/*ARGSUSED*/ -void artKillSessionFunction(widget, event, string, count) - Widget widget; - XEvent *event; - String *string; - Cardinal *count; + +static Boolean do_field_kill _ARGUMENTS((Widget, char *, int, _fixfunction, int, + XEvent *, String *, Cardinal *, Boolean)); + +static Boolean do_field_kill( + _ANSIDECL(Widget, widget), + _ANSIDECL(char *, field_name), + _ANSIDECL(int, source_offset), + _ANSIDECL(_fixfunction, source_fix_function), + _ANSIDECL(int, target_offset), + _ANSIDECL(XEvent *, event), + _ANSIDECL(String *, string), + _ANSIDECL(Cardinal *, count), + _ANSIDECL(Boolean, show_error) + ) + _KNRDECL(Widget, widget) + _KNRDECL(char *, field_name) + _KNRDECL(int, source_offset) + _KNRDECL(_fixfunction, source_fix_function) + _KNRDECL(int, target_offset) + _KNRDECL(XEvent *, event) + _KNRDECL(String *, string) + _KNRDECL(Cardinal *, count) + _KNRDECL(Boolean, show_error) { - String SubjectKilled; + struct newsgroup *newsgroup = CurrentGroup; + int what = KILL_SESSION; + unsigned int *state = 0, *button = 0; + char *value_killed; - if (CurrentMode != ARTICLE_MODE) - return; + if (CurrentMode != ARTICLE_MODE) + return True; - SubjectKilled = _artKillSession(widget, False); - return; + if (count && *count) { + if (! strcasecmp(string[0], "session")) + what = KILL_SESSION; + else if (! strcasecmp(string[0], "local")) + what = KILL_LOCAL; + else if (! strcasecmp(string[0], "global")) + what = KILL_GLOBAL; + else { + mesgPane(XRN_SERIOUS, 0, UNKNOWN_KILL_TYPE_MSG, string[0], + field_name); + return True; + } + } + else if (event || (widget && + (event = XtLastEventProcessed(XtDisplay(widget))))) { + if ((event->type == KeyPress) || (event->type == KeyRelease)) { + state = &event->xkey.state; + } + else if ((event->type == ButtonPress) || + (event->type == ButtonRelease)) { + state = &event->xbutton.state; + button = &event->xbutton.button; + } + assert(state); + + if ((*state & ShiftMask) || (button && (*button == 3))) + what = KILL_LOCAL; + else if ((*state & ControlMask) || (button && *button == 2)) + what = KILL_GLOBAL; + } + + value_killed = artKillSession(source_offset, source_fix_function, + target_offset, show_error); + if (value_killed && (what != KILL_SESSION)) + add_kill_entry(newsgroup, what, field_name, value_killed); + return value_killed ? True : False; } -/* - * Allow user to mark all articles with the current author as read - * - * XXX get author, kill using data structures, rebuild SubjectString - * XXX merge this with artKillSession - */ -/*ARGSUSED*/ -void artKillAuthorFunction(widget, event, string, count) +#define SUBJ_OFFSET XtOffsetOf(struct article, subject) +#define FROM_OFFSET XtOffsetOf(struct article, from) +#define REFS_OFFSET XtOffsetOf(struct article, references) +#define ID_OFFSET XtOffsetOf(struct article, id) + +void artKillSubjectFunction(widget, event, string, count) Widget widget; XEvent *event; String *string; Cardinal *count; { - String AuthorKilled; - - if (CurrentMode != ARTICLE_MODE) - return; - - AuthorKilled = _artKillSession(widget, True); + (void) do_field_kill(widget, "Subject", SUBJ_OFFSET, subjectStrip, SUBJ_OFFSET, + event, string, count, True); } -/*ARGSUSED*/ -void artKillLocalFunction(widget, event, string, count) +void artKillAuthorFunction(widget, event, string, count) Widget widget; XEvent *event; String *string; Cardinal *count; { - struct newsgroup *newsgroup = CurrentGroup; - String SubjectKilled; + (void) do_field_kill(widget, "From", FROM_OFFSET, 0, FROM_OFFSET, + event, string, count, True); +} - if (CurrentMode != ARTICLE_MODE) { - return; - } +static char *getFirstReference _ARGUMENTS((char *)); - if ((SubjectKilled = _artKillSession(widget, False))) - killItem(newsgroup, SubjectKilled, KILL_LOCAL); - return; +static char *getFirstReference(references) + char *references; +{ + static char *ref_buf = NULL; + static int buf_size = 0; + char *lbrace, *rbrace; + + if (! (references && *references)) + return NULL; + + if (! (lbrace = strchr(references, '<'))) + return NULL; + + if (! (rbrace = strchr(lbrace, '>'))) + return NULL; + + rbrace++; + + if (rbrace - lbrace + 1 > buf_size) { + buf_size = rbrace - lbrace + 1; + ref_buf = XtRealloc(ref_buf, buf_size); + } + + strncpy(ref_buf, lbrace, rbrace - lbrace); + ref_buf[rbrace - lbrace] = '\0'; + + return ref_buf; } -/*ARGSUSED*/ -void artKillGlobalFunction(widget, event, string, count) - Widget widget; - XEvent *event; - String *string; - Cardinal *count; +void artKillThreadFunction(widget, event, string, count) + Widget widget; + XEvent *event; + String *string; + Cardinal *count; { - struct newsgroup *newsgroup = CurrentGroup; - String SubjectKilled; + if (! art_sort_need_threads()) + return; - if (CurrentMode != ARTICLE_MODE) { - return; - } + if (! do_field_kill(widget, "References", REFS_OFFSET, getFirstReference, + REFS_OFFSET, event, string, count, False)) + (void) do_field_kill(widget, "References", ID_OFFSET, getFirstReference, + REFS_OFFSET, event, string, count, True); +} - if ((SubjectKilled = _artKillSession(widget, False))) - killItem(newsgroup, SubjectKilled, KILL_GLOBAL); +void artKillSubthreadFunction(widget, event, string, count) + Widget widget; + XEvent *event; + String *string; + Cardinal *count; +{ + if (! art_sort_need_threads()) return; -} + (void) do_field_kill(widget, "References", ID_OFFSET, getFirstReference, + REFS_OFFSET, event, string, count, True); +} #define XRNgotoArticle_ABORT 0 #define XRNgotoArticle_DOIT 1 +#define XRNgotoArticle_DEFAULT 2 static Widget GotoArticleBox = (Widget) 0; -/*ARGSUSED*/ -void artListOldFunction(widget, event, string, count) +void artListOldHandler(widget, client_data, call_data) Widget widget; - XEvent *event; - String *string; - Cardinal *count; + XtPointer client_data; + XtPointer call_data; { - long ArtPosition = 0; + long ArtPosition; int finished; + art_num artNum; + char *numberstr; + if (inCommand && ((int) client_data != XRNgotoArticle_DEFAULT)) + return; + inCommand = 1; xrnBusyCursor(); + TextUnsetSelection(SubjectText); + if ((int) client_data == XRNgotoArticle_ABORT) { + goto finished; + } + else if ((int) client_data == XRNgotoArticle_DEFAULT) { + artNum = CurrentGroup->first; + } + else { + numberstr = GetDialogValue(GotoArticleBox); + if (! (numberstr && *numberstr)) + artNum = FirstListedArticle; + else { + if (*numberstr == '+') + artNum = atol(numberstr + 1); + else + artNum = atol(numberstr); + if (! artNum) { + mesgPane(XRN_SERIOUS, 0, BAD_ART_NUM_MSG, numberstr); + goto finished; + } + if (*numberstr == '+') + artNum = MAX(FirstListedArticle - artNum, CurrentGroup->first); + } + } + abortClear(); cancelCreate("CancelListOld"); - finished = ((! abortP()) && (fillUpArray(firstArticle(), True) != ABORT)); - + finished = ((! abortP()) && + (fillUpArray(CurrentGroup, artNum, 0, True, False) != ABORT)); + if (finished) { - /* - This is cheating, perhaps, but it works. Set the current - article to the first article, so that getSubjects() will - retrieve all articles in the newsgroup. - */ + int i; + struct article *art, copy; + FREE(SubjectString); - FirstListedArticle = firstArticle(); - SubjectString = getSubjects(TextGetColumns(SubjectText), ALL, - FirstListedArticle); + + for (i = artNum; i <= CurrentGroup->last; i++) { + art = artStructGet(CurrentGroup, i, False); + if (IS_AVAIL(art) && !IS_LISTED(art)) { + copy = *art; + SET_LISTED(©); + artStructReplace(CurrentGroup, &art, ©, i); + FirstListedArticle = MIN(FirstListedArticle, i); + } + } + SubjectString = getSubjects(TextGetColumns(SubjectText), + FirstListedArticle, True); } - findArticle(SubjectString, currentArticle(), &ArtPosition); + ArtPosition = findArticle(SubjectString, CurrentGroup->current, False); TextDisableRedisplay(SubjectText); if (finished) { TextSetString(SubjectText, SubjectString); TextSetInsertionPoint(SubjectText, ArtPosition); } - + adjustMinMaxLines(); TextEnableRedisplay(SubjectText); if (finished) { - setButtonSensitive(SubjectButtonBox, "artListOld", False); + if (artNum == CurrentGroup->first) + setButtonSensitive(SubjectButtonBox, "artListOld", False); + setListSorted(True); } +finished: + if (GotoArticleBox) { + PopDownDialog(GotoArticleBox); + GotoArticleBox = 0; + } xrnUnbusyCursor(); cancelDestroy(); + inCommand = 0; return; } +void artListOldFunction(widget, event, string, count) + Widget widget; + XEvent *event; + String *string; + Cardinal *count; +{ + static struct DialogArg args[] = { + {ABORT_STRING, artListOldHandler, (XtPointer) XRNgotoArticle_ABORT}, + {DOIT_STRING, artListOldHandler, (XtPointer) XRNgotoArticle_DOIT}, + }; + unsigned int *state = 0; + + if (CurrentMode != ARTICLE_MODE) + return; + + if (event || (widget && (event = XtLastEventProcessed(XtDisplay(widget))))) { + if ((event->type == KeyPress) || (event->type == KeyRelease)) + state = &event->xkey.state; + else if ((event->type == ButtonPress) || (event->type == ButtonRelease)) + state = &event->xbutton.state; + assert(state); + } + + if ((state && (*state & ControlMask)) || (count && *count)) { + if (! GotoArticleBox) + GotoArticleBox = CreateDialog(TopLevel, LIST_OLD_NUMBER_MSG, + DIALOG_TEXT, args, XtNumber(args)); + PopUpDialog(GotoArticleBox); + } + else + artListOldHandler(0, (XtPointer) XRNgotoArticle_DEFAULT, 0); +} + +void artResortFunction(widget, event, string, count) + Widget widget; + XEvent *event; + String *string; + Cardinal *count; +{ + long ArtPosition; + char *newsort = NULL; + + if (CurrentMode != ARTICLE_MODE) + return; + + if (count && *count) { + int i, len; + for (len = 0, i = 0; i < *count; i++) + len += strlen(string[i]) + 1; + newsort = XtMalloc(len); + for (len = 0, i = 0; i < *count; i++) { + (void) strcpy(&newsort[len], string[i]); + len += strlen(string[i]); + newsort[len++] = ' '; + } + newsort[--len] = '\0'; + } + + if (list_sorted_p && !newsort) + return; + + xrnBusyCursor(); + + if (newsort) + art_sort_parse_sortlist(newsort); + + TextUnsetSelection(SubjectText); + TextDisableRedisplay(SubjectText); + FREE(SubjectString); + SubjectString = getSubjects(TextGetColumns(SubjectText), + FirstListedArticle, True); + TextSetString(SubjectText, SubjectString); + + ArtPosition = findArticle(SubjectString, CurrentArticle, False); + TextSetInsertionPoint(SubjectText, ArtPosition); + + adjustMinMaxLines(); + + if (newsort) { + XtFree(newsort); + art_sort_parse_sortlist(app_resources.sortedSubjects); + setListSorted(False); + } else + setListSorted(True); + + TextEnableRedisplay(SubjectText); +} + /* - * update the .newsrc file + * Update the .newsrc file */ /*ARGSUSED*/ void artCheckPointFunction(widget, event, string, count) @@ -2012,7 +2520,8 @@ XtPointer call_data; { char *numberstr; - char *filename, *question; + file_cache_file *file; + char *question; int status; long artNum; String new; @@ -2032,7 +2541,7 @@ return; } numberstr = GetDialogValue(GotoArticleBox); - if (numberstr == NIL(char)) { + if (! (numberstr && *numberstr)) { mesgPane(XRN_INFO, 0, NO_ART_NUM_MSG); PopDownDialog(GotoArticleBox); GotoArticleBox = 0; @@ -2050,8 +2559,8 @@ inCommand = 0; return; } - - status = moveToArticle(artNum, &filename, &question); + + status = moveToArticle(CurrentGroup, artNum, &file, &question); switch (status) { @@ -2063,23 +2572,25 @@ case MATCH: TextDisableRedisplay(SubjectText); - FirstListedArticle = MIN(FirstListedArticle, artNum); - new = getSubjects(TextGetColumns(SubjectText), ALL, FirstListedArticle); - if (strcmp(SubjectString, new)) { - FREE(SubjectString); - SubjectString = new; - TextSetString(SubjectText, SubjectString); - } - else { - FREE(new); + if ((ArtPosition = findArticle(SubjectString, artNum, True)) == -1) { + struct article *art = artStructGet(CurrentGroup, artNum, True); + SET_LISTED(art); + artStructSet(CurrentGroup, &art); + FirstListedArticle = MIN(FirstListedArticle, artNum); + new = getSubjects(TextGetColumns(SubjectText), FirstListedArticle, + True); + FREE(SubjectString); + SubjectString = new; + TextSetString(SubjectText, SubjectString); + + setListSorted(True); } - ArtPosition = 0; - findArticle(SubjectString, artNum, &ArtPosition); + ArtPosition = findArticle(SubjectString, artNum, False); TextSetInsertionPoint(SubjectText, ArtPosition); - foundArticle(filename, question); + foundArticle(file, question); TextEnableRedisplay(SubjectText); @@ -2104,7 +2615,7 @@ {ABORT_STRING, gotoArticleHandler, (XtPointer) XRNgotoArticle_ABORT}, {DOIT_STRING, gotoArticleHandler, (XtPointer) XRNgotoArticle_DOIT}, }; - + if (CurrentMode != ARTICLE_MODE) { return; } @@ -2116,76 +2627,64 @@ return; } -static int subjectSearch _ARGUMENTS((int, long *, char *, char **, char **, - art_num *)); +static int subjectSearch _ARGUMENTS((int, long *, char *, file_cache_file **, + char **, art_num *)); static int subjectSearch(dir, position, expr, file, ques, artNum) int dir; /* direction, either FORWARD or BACK */ long *position; /* cursor position */ char *expr; /* regular expression to search for */ - char **file, **ques; /* filename and status line for - new article */ + file_cache_file **file; + char **ques; /* status line for new article */ art_num *artNum; /* number of new article */ { #ifdef POSIX_REGEX - static regex_t reStruct; + regex_t reStruct; int reRet; #else # ifdef SYSV_REGEX - static char *reRet = 0; + char *reRet = 0; # else char *reRet; /* returned by re_comp/regcmp */ # endif #endif -#ifndef NDEBUG - static int done_search = 0; -#endif char *newsubject; /* subject of current line */ char *oldString = 0, *newString; /* strings used to build up new text string */ char *newLine; int ret; int line_length = TextGetColumns(SubjectText); - if (expr != NIL(char)) { + assert(expr); + + if ( #ifdef POSIX_REGEX - regfree(&reStruct); - if ((reRet = regcomp(&reStruct, expr, REG_NOSUB))) + (reRet = regcomp(&reStruct, expr, REG_NOSUB)) #else # ifdef SYSV_REGEX - FREE(reRet); - if ((reRet = regcmp(expr, NULL)) == NULL) + ! (reRet = regcmp(expr, NULL)) # else - if ((reRet = re_comp(expr)) != NULL) + (reRet = re_comp(expr)) # endif #endif - { - /* bad regular expression */ + ) { + /* bad regular expression */ #ifdef SYSV_REGEX - mesgPane(XRN_SERIOUS, 0, UNKNOWN_REGEXP_ERROR_MSG, expr); + mesgPane(XRN_SERIOUS, 0, UNKNOWN_REGEXP_ERROR_MSG, expr); #else # ifdef POSIX_REGEX - regerror(reRet, &reStruct, error_buffer, - sizeof(error_buffer)); + regerror(reRet, &reStruct, error_buffer, + sizeof(error_buffer)); # endif - mesgPane(XRN_SERIOUS, 0, KNOWN_REGEXP_ERROR_MSG, expr, + mesgPane(XRN_SERIOUS, 0, KNOWN_REGEXP_ERROR_MSG, expr, # ifdef POSIX_REGEX - error_buffer + error_buffer # else - reRet + reRet # endif /* POSIX_REGEX */ - ); + ); #endif /* SYSV_REGEX */ - return ERROR; - } -#ifndef NDEBUG - done_search++; -#endif - } -#ifndef NDEBUG - else { - assert(done_search); + return ERROR; } -#endif abortClear(); cancelCreate(0); @@ -2221,6 +2720,7 @@ mesgPane(XRN_SERIOUS, 0, ART_NOT_AVAIL_MSG, *artNum); TextRemoveLine(SubjectText, *position); removeLine(SubjectString, position); + setListed(*artNum, *artNum, False); continue; } @@ -2236,16 +2736,16 @@ { /* found a match to the regular expression */ - gotoArticle(*artNum); - if (getArticle(file, ques) != XRN_OKAY) { + if (getArticle(CurrentGroup, *artNum, file, ques) != XRN_OKAY) { /* the matching article was invalid */ - mesgPane(XRN_SERIOUS, 0, ART_NOT_AVAIL_MSG, *artNum); TextRemoveLine(SubjectText, *position); removeLine(SubjectString, position); + setListed(*artNum, *artNum, False); continue; } + CurrentGroup->current = *artNum; cancelDestroy(); TextSetInsertionPoint(SubjectText, *position); ret = MATCH; @@ -2253,6 +2753,8 @@ } } } else { + art_num first_new = 0, last_new = 0; + startSearch(); for (;;) { if (abortP()) { @@ -2263,6 +2765,7 @@ failedSearch(); cancelDestroy(); FREE(oldString); + setListed(first_new, last_new, False); ret = ABORT; goto done; } @@ -2270,6 +2773,7 @@ /* no more articles remain, return to Newgroup mode */ FREE(oldString); + setListed(first_new, last_new, False); cancelDestroy(); ret = EXIT; goto done; @@ -2286,6 +2790,7 @@ TextRemoveLine(SubjectText, *position); removeLine(SubjectString, position); + setListed(*artNum, *artNum, False); continue; } @@ -2301,34 +2806,36 @@ { /* an article matching the regular expression was found */ - gotoArticle(*artNum); - if (getArticle(file, ques) != XRN_OKAY) { + if (getArticle(CurrentGroup, *artNum, file, ques) != XRN_OKAY) { /* article is invalid, remove it from the text string*/ - mesgPane(XRN_SERIOUS, 0, ART_NOT_AVAIL_MSG, *artNum); TextRemoveLine(SubjectText, *position); removeLine(SubjectString, position); + setListed(*artNum, *artNum, False); continue; } + CurrentGroup->current = *artNum; cancelDestroy(); TextSetInsertionPoint(SubjectText, *position); ret = MATCH; goto done; } } else { + struct article *art; /* must query the news server for articles not shown */ /* on the current subject screen */ if ((newLine = getPrevSubject(line_length)) == NIL(char)) { - + /* all articles have been exhausted, reset variables */ /* to what they were before the search was started */ failedSearch(); cancelDestroy(); FREE(oldString); + setListed(first_new, last_new, False); ret = NOMATCH; goto done; } @@ -2356,6 +2863,13 @@ newString = XtNewString(newLine); } + art = artStructGet(CurrentGroup, *artNum, True); + SET_MAYBE_LISTED(art); + if (! last_new) + first_new = last_new = *artNum; + else + first_new = *artNum; + #ifdef POSIX_REGEX if (! regexec(&reStruct, newsubject, 0, 0, 0)) #else @@ -2366,12 +2880,18 @@ # endif #endif { - gotoArticle(*artNum); - if (getArticle(file, ques) != XRN_OKAY) { + if (getArticle(CurrentGroup, *artNum, file, ques) != XRN_OKAY) { mesgPane(XRN_SERIOUS, 0, ART_NOT_AVAIL_MSG, *artNum); removeLine(newString, 0); + SET_UNLISTED(art); + artStructSet(CurrentGroup, &art); continue; } + + CurrentGroup->current = *artNum; + setListed(first_new, last_new, True); + setListSorted(False); + TextReplace(SubjectText, newString, strlen(newString), 0, 0); cancelDestroy(); oldString = newString; @@ -2395,6 +2915,13 @@ } done: +#ifdef POSIX_REGEX + regfree(&reStruct); +#else +# ifdef SYSV_REGEX + free(reRet); +# endif /* SYSV_REGEX */ +#endif /* POSIX_REGEX */ return ret; } @@ -2412,7 +2939,8 @@ int direction; { long ArtPosition; - char *filename, *question; + file_cache_file *file; + char *question; art_num artNum; int status; @@ -2422,7 +2950,7 @@ ArtPosition = TextGetInsertionPoint(SubjectText); status = subjectSearch(direction, &ArtPosition, - regexp, &filename, &question, &artNum); + regexp, &file, &question, &artNum); switch (status) { case ABORT: infoNow(ERROR_SUBJ_ABORT_MSG); @@ -2430,16 +2958,16 @@ case NOMATCH: (void) sprintf(error_buffer, ERROR_SUBJ_EXPR_MSG, - regexp ? regexp : LastRegexp); + regexp); infoNow(error_buffer); case ERROR: break; case MATCH: (void) sprintf(error_buffer, ERROR_SEARCH_MSG, - regexp ? regexp : LastRegexp); + regexp); infoNow(error_buffer); - foundArticle(filename, question); + foundArticle(file, question); break; case EXIT: @@ -2518,7 +3046,7 @@ {FORWARD_STRING, subSearchHandler, (XtPointer) XRNsubSearch_FORWARD}, {BACK_STRING, subSearchHandler, (XtPointer) XRNsubSearch_BACK}, }; - + if (CurrentMode != ARTICLE_MODE) { return; } @@ -2550,9 +3078,9 @@ return; } - doSubSearch(0, LastSearch); + doSubSearch(LastRegexp, LastSearch); } - + /* * Display the article accessed before the current one */ @@ -2563,7 +3091,8 @@ String *string; Cardinal *count; { - char *filename, *question; + file_cache_file *file; + char *question; long ArtPosition; if (CurrentMode != ARTICLE_MODE) { @@ -2577,16 +3106,16 @@ TextDisableRedisplay(SubjectText); - ArtPosition = 0; - findArticle(SubjectString, PrevArticle, &ArtPosition); - gotoArticle(PrevArticle); - if (getArticle(&filename, &question) != XRN_OKAY) { + ArtPosition = findArticle(SubjectString, PrevArticle, False); + if (getArticle(CurrentGroup, PrevArticle, &file, &question) != XRN_OKAY) { mesgPane(XRN_SERIOUS, 0, ART_NOT_AVAIL_MSG, PrevArticle); TextRemoveLine(SubjectText, ArtPosition); removeLine(SubjectString, &ArtPosition); + setListed(PrevArticle, PrevArticle, False); } else { + CurrentGroup->current = PrevArticle; TextSetInsertionPoint(SubjectText, ArtPosition); - foundArticle(filename, question); + foundArticle(file, question); } TextEnableRedisplay(SubjectText); @@ -2641,11 +3170,10 @@ SubjectString[left + 1] = (printing ? PRINTED_MARKER : SAVED_MARKER); TextInvalidate(SubjectText, SubjectString, left + 1, left + 2); - + } } - left = 0; - findArticle(SubjectString, currentArticle(), &left); + left = findArticle(SubjectString, CurrentGroup->current, False); TextSetInsertionPoint(SubjectText, left); if (app_resources.subjectScrollBack) adjustMinMaxLines(); @@ -2692,7 +3220,7 @@ xrnUnbusyCursor(); inCommand = 0; return; -} +} /* * query the user about saving an article @@ -2718,7 +3246,7 @@ return; } - + if (num_params && *num_params == 1) { doSave(params[0], False); } @@ -2747,14 +3275,15 @@ String *string; Cardinal *count; { - char *filename, *question; + file_cache_file *file; + char *question; if (CurrentMode != ARTICLE_MODE) { return; } - if (toggleXlation(&filename, &question) == XRN_OKAY) { + if (toggleXlation(&file, &question) == XRN_OKAY) { TextClear(ArticleText); - redrawArticleWidget(filename, question); + redrawArticleWidget(file, question); } return; } @@ -2892,14 +3421,15 @@ String *string; Cardinal *count; { - char *filename, *question; + file_cache_file *file; + char *question; if (CurrentMode != ARTICLE_MODE) { return; } - if (toggleRotation(&filename, &question) == XRN_OKAY) { + if (toggleRotation(&file, &question) == XRN_OKAY) { TextClear(ArticleText); - redrawArticleWidget(filename, question); + redrawArticleWidget(file, question); } return; } @@ -2911,18 +3441,50 @@ String *string; Cardinal *count; { - char *filename, *question; + file_cache_file *file; + char *question; if (CurrentMode != ARTICLE_MODE) { return; } - if (toggleHeaders(&filename, &question) == XRN_OKAY) { + if (toggleHeaders(&file, &question) == XRN_OKAY) { TextClear(ArticleText); - redrawArticleWidget(filename, question); + redrawArticleWidget(file, question); } return; } +static void resizeSubjectText _ARGUMENTS((Widget, XtPointer, XEvent *, + Boolean *)); + +static void resizeSubjectText(widget, client_data, event, + continue_to_dispatch) + Widget widget; + XtPointer client_data; + XEvent *event; + Boolean *continue_to_dispatch; +{ + long ArtPosition; + + if (event->type == ConfigureNotify && CurrentArticle) { + TextDisableRedisplay(SubjectText); + FREE(SubjectString); + SubjectString = getSubjects(TextGetColumns(SubjectText), + FirstListedArticle, False); + TextSetString(SubjectText, SubjectString); + + ArtPosition = findArticle(SubjectString, CurrentArticle, False); + + TextSetInsertionPoint(SubjectText, ArtPosition); + + adjustMinMaxLines(); + + setListSorted(True); + + TextEnableRedisplay(SubjectText); + } +} + void displayArticleWidgets() { ArticleNewsGroupsString = getNewsgroupString(); @@ -2935,6 +3497,19 @@ XawPanedSetRefigureMode(ArticleFrame, False); + setButtonActive(ArtButtonList, "artPost", PostingAllowed); + setButtonActive(ArtButtonList, "artPostAndMail", PostingAllowed); + setButtonActive(ArtButtonList, "artKillThread", art_sort_need_threads()); + setButtonActive(ArtButtonList, "artKillSubthread", art_sort_need_threads()); + setButtonActive(ArtSpecButtonList, "artFollowup", PostingAllowed); + setButtonActive(ArtSpecButtonList, "artFollowupAndReply", PostingAllowed); + setButtonActive(ArtSpecButtonList, "artCancel", PostingAllowed); + setButtonActive(ArtSpecButtonList, "artHeader", + app_resources.displayLocalTime || + app_resources.leaveHeaders || app_resources.stripHeaders); + setButtonActive(ArtButtonList, "artResort", app_resources.sortedSubjects && + *app_resources.sortedSubjects); + /* This Box widget and the one below are managed only after their children have been placed in them because there is a @@ -2948,7 +3523,6 @@ SubjectButtonBox = ButtonBoxCreate("buttons", ArticleFrame);\ doButtons(app_resources.artButtonList, SubjectButtonBox,\ ArtButtonList, &ArtButtonListCount, TOP);\ - XtManageChild(SubjectButtonBox);\ } #define TOP_INFO_LINE() {\ @@ -2959,18 +3533,17 @@ ArtSpecButtonBox = ButtonBoxCreate("artButtons", ArticleFrame);\ doButtons(app_resources.artSpecButtonList, ArtSpecButtonBox,\ ArtSpecButtonList, &ArtSpecButtonListCount, BOTTOM);\ - XtManageChild(ArtSpecButtonBox);\ } #define BOTTOM_INFO_LINE() {\ ArticleInfoLine = InfoLineCreate("artInfo", 0, ArticleFrame);\ } - + if (app_resources.buttonsOnTop) { TOP_BUTTON_BOX(); TOP_INFO_LINE(); } - + SubjectText = TextCreate("subjects", True, ArticleFrame); if (app_resources.buttonsOnTop) { @@ -2981,7 +3554,7 @@ TOP_INFO_LINE(); TOP_BUTTON_BOX(); } - + ArticleText = TextCreate("text", True, ArticleFrame); if (! app_resources.buttonsOnTop) { @@ -2996,27 +3569,22 @@ TextSetLineSelections(SubjectText); TextDisableWordWrap(SubjectText); - XawPanedAllowResize(SubjectText, True); + XawPanedAllowResize(TEXT_PANE_CHILD(SubjectText), True); TextSetLines(SubjectText, app_resources.topLines); - XawPanedAllowResize(SubjectText, False); + XawPanedAllowResize(TEXT_PANE_CHILD(SubjectText), False); XtVaGetValues(SubjectText, XtNheight, &height, 0); XtVaSetValues(SubjectText, XtNpreferredPaneSize, height, 0); TopInfoLine = SubjectInfoLine; - - setButtonSensitive(SubjectButtonBox, "artPost", PostingAllowed); - setButtonSensitive(SubjectButtonBox, "artPostAndMail", PostingAllowed); - BottomInfoLine = ArticleInfoLine; - setButtonSensitive(ArtSpecButtonBox, "artFollowup", PostingAllowed); - setButtonSensitive(ArtSpecButtonBox, "artFollowupAndReply", - PostingAllowed); - XawPanedSetRefigureMode(ArticleFrame, True); XtInstallAccelerators(SubjectText, ArticleText); XtSetKeyboardFocus(ArticleFrame, SubjectText); + + XtAddEventHandler(SubjectText, StructureNotifyMask, FALSE, + resizeSubjectText, NULL); } else { TopInfoLine = SubjectInfoLine; @@ -3103,4 +3671,41 @@ void resetArticleNewsgroupsList() { FREE(ArticleNewsGroupsString); +} + +/* + Move the cursor to the position of the article "num". If if the + article is not found, exit XRN with an error if allow_failure is + False, or return -1 otherwise. + */ +static long findArticle( + _ANSIDECL(char *, tstring), + _ANSIDECL(art_num, num), + _ANSIDECL(Boolean, allow_failure) + ) + _KNRDECL(char *, tstring) + _KNRDECL(art_num, num) + _KNRDECL(Boolean, allow_failure) +{ + long artNum; /* number of current article */ + long position = 0; + long pos; + + while (TRUE) { + pos = position + 1; + /* move over S[aved] / P[rinted] marking */ + if ((tstring[pos] == SAVED_MARKER) || (tstring[pos] == PRINTED_MARKER)) { + pos++; + } + artNum = atol(&tstring[pos]); + if (artNum == num) + break; + if (! (moveCursor(FORWARD, tstring, &position) && tstring[position])) { + if (allow_failure) + return -1; + else + ehErrorExitXRN( ERROR_FINDARTICLE_MSG ); + } + } + return position; } diff -u -d -r -N -P 8.02/artMode.h 9.00/artMode.h --- 8.02/artMode.h Mon Oct 2 12:36:05 1995 +++ 9.00/artMode.h Fri Jul 18 08:00:28 1997 @@ -21,6 +21,7 @@ BUTDECL(artFedUp); BUTDECL(artMarkRead); BUTDECL(artMarkUnread); +BUTDECL(artSub); BUTDECL(artUnsub); BUTDECL(artScroll); BUTDECL(artScrollBack); @@ -32,10 +33,11 @@ BUTDECL(artScrollIndexBack); BUTDECL(artSubNext); BUTDECL(artSubPrev); -BUTDECL(artKillSession); -BUTDECL(artKillLocal); -BUTDECL(artKillGlobal); +BUTDECL(artThreadParent); +BUTDECL(artKillSubject); BUTDECL(artKillAuthor); +BUTDECL(artKillThread); +BUTDECL(artKillSubthread); BUTDECL(artSubSearch); BUTDECL(artContinue); BUTDECL(artPost); @@ -45,6 +47,7 @@ BUTDECL(artCheckPoint); BUTDECL(artGripe); BUTDECL(artListOld); +BUTDECL(artResort); BUTDECL(artSave); BUTDECL(artReply); diff -u -d -r -N -P 8.02/artstruct.c 9.00/artstruct.c --- 8.02/artstruct.c Fri Dec 22 05:27:55 1995 +++ 9.00/artstruct.c Tue Jul 1 18:57:48 1997 @@ -5,6 +5,7 @@ #include "config.h" #include "news.h" #include "artstruct.h" +#include "file_cache.h" static Boolean artsame _ARGUMENTS((struct article *, struct article *)); static void freeartstruct _ARGUMENTS((struct article *)); @@ -52,17 +53,83 @@ } +int artStructNumChildren(art) + struct article *art; +{ + int i; + art_num *ptr; + + if (! art->children) + return 0; + + for (ptr = art->children, i = 0; *ptr; ptr++) + i++; + + return i; +} + +void artStructAddChild(art, child) + struct article *art; + art_num child; +{ + int i = artStructNumChildren(art); + + art->children = (art_num *) XtRealloc((char *) art->children, + (i + 2) * sizeof(*art->children)); + + art->children[i++] = child; + art->children[i] = 0; +} + +void artStructRemoveChild(art, child) + struct article *art; + art_num child; +{ + art_num *ptr, *ptr2; + + if (! art->children) + return; + + for (ptr = art->children; *ptr; ptr++) { + if (*ptr == child) { + for (ptr2 = ptr + 1; *ptr2; ptr++, ptr2++) + *ptr = *ptr2; + *ptr = 0; + } + } + + return; +} + + +static art_num *copy_art_list _ARGUMENTS((struct article *)); + +static art_num *copy_art_list(art) + struct article *art; +{ + int i; + art_num *new; + + if (! art->children) + return 0; + + i = artStructNumChildren(art); + + new = (art_num *) XtCalloc(i + 1, sizeof(*new)); + + (void) memcpy((char *) new, (char *) art->children, (i + 1) * sizeof(*new)); + + return new; +} + /* Free an article structure. */ static void freeartstruct(art) struct article *art; { - XtFree(art->subject); - XtFree(art->author); - XtFree(art->lines); - CLEAR_FILE(art); - XtFree((char *) art); + CLEAR_ALL(art); + XtFree((char *) art); } @@ -94,10 +161,14 @@ If "writeable" is true, than the returned structure can be modified by the caller. Otherwise, it shouldn't be. */ -struct article *artStructGet(newsgroup, artnum, writeable) - struct newsgroup *newsgroup; - art_num artnum; - Boolean writeable; +struct article *artStructGet( + _ANSIDECL(struct newsgroup *, newsgroup), + _ANSIDECL(art_num, artnum), + _ANSIDECL(Boolean, writeable) + ) + _KNRDECL(struct newsgroup *, newsgroup) + _KNRDECL(art_num, artnum) + _KNRDECL(Boolean, writeable) { struct article *reference = newsgroup->ref_art; @@ -135,9 +206,18 @@ XtMalloc(sizeof(struct article)); *new = *reference; COPY_FIELD(subject); + COPY_FIELD(from); COPY_FIELD(author); COPY_FIELD(lines); - COPY_FIELD(filename); + COPY_FIELD(newsgroups); + COPY_FIELD(date); + COPY_FIELD(id); + COPY_FIELD(xref); + COPY_FIELD(references); + new->file = reference->file; + new->base_file = reference->base_file; + new->parent = reference->parent; + new->children = copy_art_list(reference); new->first = artnum + 1; new->previous = reference; if (reference->next) @@ -154,9 +234,18 @@ XtMalloc(sizeof(struct article)); *new = *reference; COPY_FIELD(subject); + COPY_FIELD(from); COPY_FIELD(author); COPY_FIELD(lines); - COPY_FIELD(filename); + COPY_FIELD(newsgroups); + COPY_FIELD(date); + COPY_FIELD(id); + COPY_FIELD(xref); + COPY_FIELD(references); + new->file = reference->file; + new->base_file = reference->base_file; + new->parent = reference->parent; + new->children = copy_art_list(reference); new->first = artnum; new->previous = reference; if (reference->next) @@ -172,6 +261,12 @@ } +#define CHECK(field, staying, going) \ + if ((staying)->field != (going)->field) { \ + XtFree((char *)(going)->field); \ + (going)->field = (staying)->field; \ + } + /* Given an article structure which was previously returned by artStructGet with "writeable" True and which may have been modified @@ -188,24 +283,52 @@ struct article **art; { if (artsame(*art, (*art)->previous)) { - struct article *tmp = *art; - (*art)->previous->next = (*art)->next; - if ((*art)->next) - (*art)->next->previous = (*art)->previous; - *art = (*art)->previous; - XtFree((char *) tmp); + struct article *tmp = *art; + + CHECK(subject, (*art)->previous, *art); + CHECK(from, (*art)->previous, *art); + CHECK(author, (*art)->previous, *art); + CHECK(lines, (*art)->previous, *art); + CHECK(newsgroups, (*art)->previous, *art); + CHECK(date, (*art)->previous, *art); + CHECK(children, (*art)->previous, *art); + /* "file" doesn't need checking because artsame() says it's the + same in both structures. */ + CHECK(id, (*art)->previous, *art); + CHECK(xref, (*art)->previous, *art); + CHECK(references, (*art)->previous, *art); + + (*art)->previous->next = (*art)->next; + if ((*art)->next) + (*art)->next->previous = (*art)->previous; + *art = (*art)->previous; + XtFree((char *) tmp); } if (artsame(*art, (*art)->next)) { - struct article *tmp = (*art)->next; - (*art)->next->first = (*art)->first; - if ((*art)->previous) - (*art)->previous->next = tmp; - else - newsgroup->articles = tmp; - (*art)->next->previous = (*art)->previous; - XtFree((char *) *art); - *art = tmp; + struct article *tmp = (*art)->next; + + CHECK(subject, (*art)->next, *art); + CHECK(from, (*art)->next, *art); + CHECK(author, (*art)->next, *art); + CHECK(lines, (*art)->next, *art); + CHECK(newsgroups, (*art)->next, *art); + CHECK(date, (*art)->next, *art); + CHECK(children, (*art)->next, *art); + /* "file" doesn't need checking because artsame() says it's the + same in both structures. */ + CHECK(id, (*art)->next, *art); + CHECK(xref, (*art)->next, *art); + CHECK(references, (*art)->next, *art); + + (*art)->next->first = (*art)->first; + if ((*art)->previous) + (*art)->previous->next = tmp; + else + newsgroup->articles = tmp; + (*art)->next->previous = (*art)->previous; + XtFree((char *) *art); + *art = tmp; } newsgroup->ref_art = *art; @@ -230,23 +353,30 @@ if (! artsame(*original, copy)) { *original = artStructGet(newsgroup, artnum, True); (*original)->status = copy->status; -#define CHECK(field) \ - if ((*original)->field != copy->field) { \ - XtFree((*original)->field); \ - (*original)->field = copy->field; \ - } - CHECK(subject); - CHECK(author); - CHECK(lines); - if ((*original)->filename != copy->filename) { + CHECK(subject, copy, *original); + CHECK(from, copy, *original); + CHECK(author, copy, *original); + CHECK(lines, copy, *original); + CHECK(newsgroups, copy, *original); + CHECK(date, copy, *original); + (*original)->parent = copy->parent; + CHECK(children, copy, *original); + if ((*original)->file != copy->file) { CLEAR_FILE(*original); - CHECK(filename); + CHECK(file, copy, *original); } -#undef CHECK + if ((*original)->base_file != copy->base_file) { + CLEAR_BASE_FILE(*original); + CHECK(base_file, copy, *original); + } + CHECK(id, copy, *original); + CHECK(xref, copy, *original); + CHECK(references, copy, *original); artStructSet(newsgroup, original); } } +#undef CHECK /* Return the next article structure in an article list, as well as the @@ -385,6 +515,8 @@ static Boolean artsame(art1, art2) struct article *art1, *art2; { + int num_children; + if (! (art1 && art2)) return False; @@ -403,12 +535,33 @@ return False; ARTSTRCMP(subject); + ARTSTRCMP(from); ARTSTRCMP(author); ARTSTRCMP(lines); - ARTSTRCMP(filename); + ARTSTRCMP(newsgroups); + ARTSTRCMP(date); + ARTSTRCMP(id); + ARTSTRCMP(xref); + ARTSTRCMP(references); + + if (art1->file != art2->file) + return False; + + if (art1->base_file != art2->base_file) + return False; + + if (art1->parent != art2->parent) + return False; + + if ((art1->children != art2->children) || + ((num_children = artStructNumChildren(art1)) != + artStructNumChildren(art2)) || + (art1->children && + memcmp((void *)art1->children, (void *)art2->children, + (num_children + 1) * sizeof(*art1->children)))) + return False; #undef ARTSTRCMP return True; } - diff -u -d -r -N -P 8.02/artstruct.h 9.00/artstruct.h --- 8.02/artstruct.h Mon Jan 30 08:17:32 1995 +++ 9.00/artstruct.h Thu Jun 5 07:11:40 1997 @@ -4,8 +4,7 @@ void artListInit _ARGUMENTS((struct newsgroup *)); void artListSet _ARGUMENTS((struct newsgroup *)); void artListFree _ARGUMENTS((struct newsgroup *)); -struct article *artStructGet _ARGUMENTS((struct newsgroup *, art_num, - /* Boolean */ int)); +struct article *artStructGet _ARGUMENTS((struct newsgroup *, art_num, Boolean)); void artStructSet _ARGUMENTS((struct newsgroup *, struct article **)); void artStructReplace _ARGUMENTS((struct newsgroup *, struct article **, struct article *, @@ -17,5 +16,9 @@ art_num *)); struct article *artListFirst _ARGUMENTS((struct newsgroup *, art_num *, art_num *)); struct article *artListLast _ARGUMENTS((struct newsgroup *, art_num *, art_num *)); + +int artStructNumChildren _ARGUMENTS((struct article *)); +void artStructAddChild _ARGUMENTS((struct article *, art_num)); +void artStructRemoveChild _ARGUMENTS((struct article *, art_num)); #endif /* _ARTSTRUCT_H_ */ diff -u -d -r -N -P 8.02/busyCursor.c 9.00/busyCursor.c --- 8.02/busyCursor.c Fri Dec 22 05:28:02 1995 +++ 9.00/busyCursor.c Thu Jun 5 07:11:40 1997 @@ -118,9 +118,12 @@ } -void BusyCursor(root, do_popups) - Widget root; - Boolean do_popups; +void BusyCursor( + _ANSIDECL(Widget, root), + _ANSIDECL(Boolean, do_popups) + ) + _KNRDECL(Widget, root) + _KNRDECL(Boolean, do_popups) { static Cursor the_cursor = 0; cursor_info *info_p; @@ -166,9 +169,12 @@ XFlush(XtDisplay(root)); } -void UnbusyCursor(root, do_popups) - Widget root; - Boolean do_popups; +void UnbusyCursor( + _ANSIDECL(Widget, root), + _ANSIDECL(Boolean, do_popups) + ) + _KNRDECL(Widget, root) + _KNRDECL(Boolean, do_popups) { cursor_info *info_p; static int in_unbusy = 0; diff -u -d -r -N -P 8.02/busyCursor.h 9.00/busyCursor.h --- 8.02/busyCursor.h Sun Nov 5 09:15:23 1995 +++ 9.00/busyCursor.h Thu Jun 5 07:11:40 1997 @@ -15,7 +15,7 @@ #endif #endif /* !_ARGUMENTS */ -extern void BusyCursor _ARGUMENTS((Widget, /* Boolean */ int)); -extern void UnbusyCursor _ARGUMENTS((Widget, /* Boolean */ int)); +extern void BusyCursor _ARGUMENTS((Widget, Boolean)); +extern void UnbusyCursor _ARGUMENTS((Widget, Boolean)); #endif /* _BUSYCURSOR_H_ */ diff -u -d -r -N -P 8.02/butexpl.h 9.00/butexpl.h --- 8.02/butexpl.h Mon Nov 6 08:57:23 1995 +++ 9.00/butexpl.h Fri Jul 18 08:10:46 1997 @@ -44,9 +44,10 @@ * german * * - * The german section was created and translated by - * K.Marquardt (K.Marquardt@zhv.basf-ag.de) Nov/23/94 - * based on a version from Jansohn@zxt.basf-ag.de + * The German section was created and translated by K.Marquardt + * (K.Marquardt@zhv.basf-ag.de) based on a version from + * Jansohn@zxt.basf-ag.de. Some revisions were provided by by T.Foks + * (foks@hub.de). * * ............................................................... * Note: all items should appear in all sections, if you add a item, @@ -129,6 +130,8 @@ #define ALLTOGGLE_EXSTR "Change order of groups: alphabetical/.newsrc order" #define ALLSCROLL_EXSTR "Scroll the ALL groups screen forward" #define ALLSCROLLBACK_EXSTR "Scroll the ALL groups screen backward" +#define ALLSEARCH_EXSTR "Search the group list" +#define ALLLIMIT_EXSTR "Display a subset of the group list" #define ALLPOST_EXSTR "Post to the current newsgroup" #define ALLPOST_AND_MAIL_EXSTR "Post to the current newsgroup and mail the posting to someone" @@ -146,40 +149,43 @@ #define ARTSCROLL_EXSTR "Scroll current article forward" #define ARTSCROLLBACK_EXSTR "Scroll current article backward" #define ARTSCROLLLINE_EXSTR "Scroll current article one line forward" -#define ARTSCROLLBACKLINE_EXSTR "Scroll current article one line backward" +#define ARTSCROLLBCKLN_EXSTR "Scroll current article one line backward" #define ARTSCROLLEND_EXSTR "Scroll to end of current article" -#define ARTSCROLLBEGINNING_EXSTR "Scroll to beginning of current article" +#define ARTSCROLLBEG_EXSTR "Scroll to beginning of current article" #define ARTSCROLLINDEX_EXSTR "Scroll index forward one page" -#define ARTSCROLLINDEXBACK_EXSTR "Scroll index back one page" +#define ARTSCROLLINDBCK_EXSTR "Scroll index back one page" #define ARTPREV_EXSTR "Read the previous article" #define ARTLAST_EXSTR "Go back to the last article displayed" #define ARTNEXTGROUP_EXSTR "Go to the next unread newsgroup, skipping newsgroup mode" -#define ARTCATCHUP_EXSTR "Mark all articles (up to the middle-button selected article) in the group as read" +#define ARTCATCHUP_EXSTR "Mark all articles (up to the selected article, if any) in the group as read" #define ARTFEEDUP_EXSTR "Mark all articles in the current group as read and go to the next unread newsgroup " #define ARTGOTOARTICLE_EXSTR "Go to the specified article number in the current group" #define ARTMARKREAD_EXSTR "Mark selected article(s) as read" #define ARTMARKUNREAD_EXSTR "Mark selected article(s) as unread" +#define ARTSUB_EXSTR "Subscribe to the current group" #define ARTUNSUB_EXSTR "Unsubscribe to the current group" #define ARTSUBNEXT_EXSTR "Search for the next article with the selected subject" #define ARTSUBPREV_EXSTR "Search for the previous article with the selected subject" -#define ARTKILLSESSION_EXSTR "Mark all articles with this subject as read, this session only" -#define ARTKILLLOCAL_EXSTR "Mark all articles with this subject as read in this group for this and all future sessions" -#define ARTKILLGLOBAL_EXSTR "Mark all articles with this subject as read for all groups for this and all future sessions" -#define ARTKILLAUTHOR_EXSTR "Mark all articles with this author as read, this session only" +#define ARTPARENT_EXSTR "Search for the parent of the current or selected article" +#define ARTKILLSUBJECT_EXSTR "Mark all articles with this subject as read" +#define ARTKILLAUTHOR_EXSTR "Mark all articles with this author as read" +#define ARTKILLTHREAD_EXSTR "Mark all articles in this thread as read" +#define ARTKILLSUBTHREAD_EXSTR "Mark all articles in this subthread as read" #define ARTSUBSEARCH_EXSTR "Search the subject lines for a regular expression" #define ARTCONTINUE_EXSTR "Continue the regular expression subject search" #define ARTPOST_EXSTR "Post an article to this newsgroup" -#define ARTPOST_AND_MAIL_EXSTR "Post an article to this newsgroup and mail it too" +#define ARTPOST_MAIL_EXSTR "Post an article to this newsgroup and mail it too" #define MAIL_EXSTR "Send a mail message" #define ARTEXIT_EXSTR "Return to newsgroup mode, marking all articles as unread" #define ARTCHECKPOINT_EXSTR "Update the .newsrc file" #define ARTGRIPE_EXSTR "Mail a gripe to the XRN maintainers" #define ARTLISTOLD_EXSTR "List all articles in the current group (may be slow)" +#define ARTRESORT_EXSTR "Resort the article list" #define ARTSAVE_EXSTR "Save the current article in a file" #define ARTREPLY_EXSTR "Mail a reply to the author of the current article" #define ARTFORWARD_EXSTR "Forward an article to a user(s)" #define ARTFOLLOWUP_EXSTR "Post a followup to the current article" -#define ARTFOLLOWUPANDREPLY_EXSTR "Post and mail a single response to the current article" +#define ARTFOLLOWREPL_EXSTR "Post and mail a single response to the current article" #define ARTCANCEL_EXSTR "Cancel the current article" #define ARTROT13_EXSTR "Decrypt an encrypted joke" #define ARTXLATE_EXSTR "Translate the current article" @@ -196,9 +202,10 @@ * -------------- * section GERMAN * -------------- - * The GERMAN section was created and translated by - * K.Marquardt (K.Marquardt@zhv.basf-ag.de) Nov/23/94 - * based on a version from Jansohn@zxt.basf-ag.de + * The German section was created and translated by K.Marquardt + * (K.Marquardt@zhv.basf-ag.de) based on a version from + * Jansohn@zxt.basf-ag.de. Some revisions were provided by by T.Foks + * (foks@hub.de). * * german version (iso8859-1), use LANGUAGE= german in Imakefile/Makefile * @@ -209,12 +216,12 @@ * sz = \337 */ -#define ADDQUIT_EXSTR "Gruppieren verlassen und verbleibende Gruppen als `nicht abboniert' kennzeichnen." +#define ADDQUIT_EXSTR "Gruppieren verlassen und verbleibende Gruppen als `nicht abonniert' kennzeichnen." #define ADDIGNORE_REST_EXSTR "Hinzuf\374gen verlassen und alle verbleibenden Gruppen ignorieren." #define ADDFIRST_EXSTR "Gew\344hlte Newsgruppe(n) an den Anfang der Datei .newsrc setzen." #define ADDLAST_EXSTR "Gew\344hlte Newsgruppe(n) an das Ende der Datei .newsrc setzen." -#define ADDAFTER_EXSTR "Gew\344hlte Newsgruppe(n) hinter eine andere in Newsgruppe setzen." -#define ADDUNSUB_EXSTR "Gew\344hlte Newsgruppe(n) als `nicht abboniert' kennzeichnen." +#define ADDAFTER_EXSTR "Gew\344hlte Newsgruppe(n) hinter eine andere in der Datei .newsrc setzen." +#define ADDUNSUB_EXSTR "Gew\344hlte Newsgruppe(n) als `nicht abonniert' kennzeichnen." #define ADDIGNORE_EXSTR "Ignoriere die ausgew\344hlte(n) Gruppe(n)." /* @@ -223,18 +230,18 @@ #define NGQUIT_EXSTR "Programm XRN beenden." #define NGREAD_EXSTR "Artikel der aktuellen Newsgruppe lesen." -#define NGNEXT_EXSTR "n\344chste Newsgruppe ausw\344hlen." -#define NGPREV_EXSTR "Vorhergehende Newsgruppe aus\344ehlen." +#define NGNEXT_EXSTR "N\344chste Newsgruppe ausw\344hlen." +#define NGPREV_EXSTR "Vorhergehende Newsgruppe aus\344hlen." #define NGCATCHUP_EXSTR "Alle Artikel der aktuellen Newsgruppe als `gelesen' kennzeichnen." -#define NGSUBSCRIBE_EXSTR "Newsgruppe als `abboniert' kennzeichnen." -#define NGUNSUB_EXSTR "Aktuelle Newsgruppe als `nicht abboniert' kennzeichnen." -#define NGGOTO_EXSTR "Zur gew\344hlten Newsgruppe gehen (and subscribe, if necessary)." -#define NGALLGROUPS_EXSTR "Alle verf\374gbaren Newsgruppen anzeigen - auch die nicht abbonierten." -#define NGLISTOLD_EXSTR "Alle abbonierten Newsgruppen anzeigen - auch wenn keine neuen Artikel (toggle)." +#define NGSUBSCRIBE_EXSTR "Newsgruppe als `abonniert' kennzeichnen." +#define NGUNSUB_EXSTR "Aktuelle Newsgruppe als `nicht abonniert' kennzeichnen." +#define NGGOTO_EXSTR "Zur gew\344hlten Newsgruppe gehen (und abonnieren, falls n\366tig)." +#define NGALLGROUPS_EXSTR "Alle verf\374gbaren Newsgruppen anzeigen - auch die nicht abonnierten." +#define NGLISTOLD_EXSTR "Alle abonnierten Newsgruppen anzeigen - auch wenn keine neuen Artikel (toggle)." #define NGRESCAN_EXSTR "Den News-Server abfragen, ob neue Artikel oder Newsgruppen existieren." -#define NGGETLIST_EXSTR "Eine komplette Liste der Gruppen vom News-Server holen." -#define NGPREVGROUP_EXSTR "Zur zuletzt gelesenen Newgruppe zur\374ckkehren (and subscribe, if necessary)." -#define NGSELECT_EXSTR "Aktuelle Auswahl fuer eine nachfolgende Verschiebung merken." +#define NGGETLIST_EXSTR "Eine komplette Liste der Newsgruppen vom News-Server holen." +#define NGPREVGROUP_EXSTR "Zur zuletzt gelesenen Newsgruppe zur\374ckkehren (und abonnieren, falls n\366tig)." +#define NGSELECT_EXSTR "Aktuelle Auswahl f\374r eine nachfolgende Verschiebung merken." #define NGMOVE_EXSTR "Gemerkte Auswahl vor die gew\344hlte Newsgruppe setzen." #define NGEXIT_EXSTR "Programm XRN verlassen, ohne die Datei .newsrc zu aktualisieren." #define NGCHECKPOINT_EXSTR "Die Datei .newsrc aktualisieren." @@ -249,11 +256,11 @@ */ #define ALLQUIT_EXSTR "Die Datei .newsrc aktualisieren und zur Gruppenauswahl zur\374ckkehren." -#define ALLSUB_EXSTR "Aktuelle Newsgruppe als `abboniert' kennzeichnen ohne die Reihenfolge zu \344ndern." -#define ALLFIRST_EXSTR "Gew\344hlte Newsgruppe(n) als `abboniert' kennzeichnen und an den Anfang setzen." -#define ALLLAST_EXSTR "Gew\344hlte Newsgruppe(n) als `abonniert' kennzeichnen und an das Ende setzen." +#define ALLSUB_EXSTR "Aktuelle Newsgruppe als `abonniert' kennzeichnen ohne die Reihenfolge zu \344ndern." +#define ALLFIRST_EXSTR "Gew\344hlte Newsgruppe(n) als `abonniert' kennzeichnen und an den Anfang setzen." +#define ALLLAST_EXSTR "Gew\344hlte Newsgruppe(n) als `abonniert' kennzeichnen und ans Ende setzen." #define ALLAFTER_EXSTR "Gew\344hlte Newsgruppe(n) als `abonniert' kennzeichnen und plazieren." -#define ALLUNSUB_EXSTR "Gew\344hlte Newsgruppe(n) als `nicht abboniert' kennzeichnen" +#define ALLUNSUB_EXSTR "Gew\344hlte Newsgruppe(n) als `nicht abonniert' kennzeichnen" #define ALLIGNORE_EXSTR "Ausgew\344hlte Newsgruppe(n) ignorieren." #define ALLGOTO_EXSTR "Zur gew\344hlten Newsgruppe gehen" #define ALLSELECT_EXSTR "Aktuelle Auswahl f\374r eine nachfolgende Verschiebung merken." @@ -261,6 +268,8 @@ #define ALLTOGGLE_EXSTR "Reihenfolge der Anzeige \344ndern - alphabtisch/.newsrc ." #define ALLSCROLL_EXSTR "Liste der Newsgruppen nach oben schieben." #define ALLSCROLLBACK_EXSTR "Liste der Newsgruppen nach unten schieben." +#define ALLSEARCH_EXSTR "Search the group list" +#define ALLLIMIT_EXSTR "Display a subset of the group list" #define ALLPOST_EXSTR "Einen Artikel zu einer Newsgruppe ver\366ffentlichen." #define ALLPOST_AND_MAIL_EXSTR "Einen Artikel zu einer Newsgruppe ver\366ffentlichen und ihn an jemanden senden." @@ -278,44 +287,47 @@ #define ARTSCROLL_EXSTR "Aktuellen Artikel nach oben schieben." #define ARTSCROLLBACK_EXSTR "Aktuellen Artikel nach unten schieben." #define ARTSCROLLLINE_EXSTR "Aktuellen Artikel eine Zeile nach oben schieben." -#define ARTSCROLLBACKLINE_EXSTR "Aktuellen Artikel eine Zeile nach unten schieben." -#define ARTSCROLLEND_EXSTR "Zum Ende des Aktuellen Artikel gehen." -#define ARTSCROLLBEGINNING_EXSTR "Zum Anfang des Aktuellen Artikel gehen." +#define ARTSCROLLBCKLN_EXSTR "Aktuellen Artikel eine Zeile nach unten schieben." +#define ARTSCROLLEND_EXSTR "Zum Ende des Aktuellen Artikels gehen." +#define ARTSCROLLBEG_EXSTR "Zum Anfang des Aktuellen Artikels gehen." #define ARTSCROLLINDEX_EXSTR "Liste der Artikel nach oben schieben." -#define ARTSCROLLINDEXBACK_EXSTR "Liste der Artikel nach unten schieben." +#define ARTSCROLLINDBCK_EXSTR "Liste der Artikel nach unten schieben." #define ARTPREV_EXSTR "Den vorhergehenden Artikel lesen." #define ARTLAST_EXSTR "Den zuletzt angezeigten Artikel lesen." -#define ARTNEXTGROUP_EXSTR "Zur n\344chsten noch nicht gelesenen News-Gruppe gehen, dabei Gruppenauswahl \374bergehen." +#define ARTNEXTGROUP_EXSTR "Zur n\344chsten noch nicht gelesenen Newsgruppe gehen, dabei Gruppenauswahl \374bergehen." #define ARTCATCHUP_EXSTR "Alle Artikel (bis zum aktuellen) als `gelesen' kennzeichnen und Newsgruppe verlassen." #define ARTFEEDUP_EXSTR "Alle Artikel als `gelesen' kennzeichnen und zur n\344chsten noch nicht gelesenen Newsgruppe gehen." #define ARTGOTOARTICLE_EXSTR "Einen bestimmten Artikel in der aktuellen Newsgruppe lesen." #define ARTMARKREAD_EXSTR "Gew\344hlte Artikel als `gelesen' kennzeichnen." #define ARTMARKUNREAD_EXSTR "Gew\344hlte Artikel als `nicht gelesen' kennzeichnen." -#define ARTUNSUB_EXSTR "Aktuelle Newsgruppe als `nicht abboniert' kennzeichnen." +#define ARTSUB_EXSTR "Subscribe to the current group" +#define ARTUNSUB_EXSTR "Aktuelle Newsgruppe als `nicht abonniert' kennzeichnen." #define ARTSUBNEXT_EXSTR "Den n\344chsten Artikel mit dem gleichen Thema suchen." #define ARTSUBPREV_EXSTR "Einen vorhergehenden Artikel mit dem gleichen Thema suchen." -#define ARTKILLSESSION_EXSTR "Alle Artikel mit diesem Thema in dieser Sitzung als `gelesen' kennzeichnen." -#define ARTKILLLOCAL_EXSTR "Alle Artikel mit diesem Thema, in dieser Gruppe, fuer alle Sitzungen als `gelesen' kennzeichnen." -#define ARTKILLGLOBAL_EXSTR "Alle Artikel mit diesem Thema fuer alle Sitzungen als `gelesen' kennzeichnen." -#define ARTKILLAUTHOR_EXSTR "Alle Artikel dieses Autors in dieser Sitzung als `gelesen' kennzeichnen." +#define ARTPARENT_EXSTR "Search for the parent of the current or selected article" +#define ARTKILLSUBJECT_EXSTR "Markiere alle Artikel mit diesem Betreff als gelesen." +#define ARTKILLAUTHOR_EXSTR "Markiere alle Artikel mit diesem Autor als gelesen." +#define ARTKILLTHREAD_EXSTR "Mark all articles in this thread as read" +#define ARTKILLSUBTHREAD_EXSTR "Mark all articles in this subthread as read" #define ARTSUBSEARCH_EXSTR "Artikel zu einem bestimmten Thema suchen." #define ARTCONTINUE_EXSTR "Suchen nach einem bestimmten Thema fortsetzen." #define ARTPOST_EXSTR "Einen Artikel zu dieser Newsgruppe ver\366ffentlichen." -#define ARTPOST_AND_MAIL_EXSTR "Einen Artikel zu dieser Newsgruppe ver\366ffentlichen und gleichzeitig versenden." +#define ARTPOST_MAIL_EXSTR "Einen Artikel zu dieser Newsgruppe ver\366ffentlichen und gleichzeitig versenden." #define MAIL_EXSTR "Versende eine E-Mail." #define ARTEXIT_EXSTR "Zur Gruppenauswahl zur\374ckkehren und alle Artikel als `nicht gelesen' kennzeichnen." #define ARTCHECKPOINT_EXSTR "Die Datei .newsrc aktualisieren." -#define ARTGRIPE_EXSTR "Eine Nachricht an die XRN-Systembetreuer senden." -#define ARTLISTOLD_EXSTR "Alle Artikel der aktuellen Gruppe zeigen (evtl. langsam)." +#define ARTGRIPE_EXSTR "Eine Nachricht an den XRN-Systembetreuer senden." +#define ARTLISTOLD_EXSTR "Alle Artikel der aktuellen Newsgruppe zeigen (evtl. langsam)." +#define ARTRESORT_EXSTR "Resort the article list" #define ARTSAVE_EXSTR "Aktuellen Artikel in eine Datei speichern." #define ARTREPLY_EXSTR "Eine Nachricht an den Verfasser des aktuellen Artikels senden." #define ARTFORWARD_EXSTR "Aktuellen Artikel an weitere Benutzer senden." #define ARTFOLLOWUP_EXSTR "Einen, zum aktuellen Artikel bezugnehmenden, Artikel verfassen." -#define ARTFOLLOWUPANDREPLY_EXSTR "Einen bezugnehmenden Artikel verfassen und diesen auch an den Verfasser senden." +#define ARTFOLLOWREPL_EXSTR "Einen bezugnehmenden Artikel verfassen und diesen auch an den Verfasser senden." #define ARTCANCEL_EXSTR "Aktuellen, eigenen Artikel zur\374ckziehen." #define ARTROT13_EXSTR "Aktuellen Artikel entschl\374sseln." #define ARTXLATE_EXSTR "Aktuellen Artikel an Zeichensatz anpassen." -#define ARTHEADER_EXSTR "Den kompletten/gek\374rzten Artikelkopf ausgeben (toggle)." +#define ARTHEADER_EXSTR "Den kompletten/gek\374rzten Artikelkopf ausgeben (wechselweise)." #define ARTPRINT_EXSTR "Aktuellen Artikel auf dem Drucker ausgeben." #endif /* XRN_LANG_german */ diff -u -d -r -N -P 8.02/buttons.c 9.00/buttons.c --- 8.02/buttons.c Wed Jun 7 22:05:25 1995 +++ 9.00/buttons.c Wed Dec 31 19:00:00 1969 @@ -1,677 +0,0 @@ - -#if !defined(lint) && !defined(SABER) && !defined(GCC_WALL) -static char XRNrcsid[] = "$Id: buttons.c,v 1.66 1995/06/08 02:05:07 jik Exp $"; -#endif - -/* - * xrn - an X-based NNTP news reader - * - * Copyright (c) 1988-1993, Ellen M. Sentovich and Rick L. Spickelmier. - * - * Permission to use, copy, modify, and distribute this software and its - * documentation for any purpose and without fee is hereby granted, 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 the University of California not - * be used in advertising or publicity pertaining to distribution of - * the software without specific, written prior permission. The University - * of California makes no representations about the suitability of this - * software for any purpose. It is provided "as is" without express or - * implied warranty. - * - * THE UNIVERSITY OF CALIFORNIA DISCLAIMS ALL WARRANTIES WITH REGARD TO - * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS, IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA 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. - */ - -/* - * buttons.c: create and handle the buttons - * - */ - -#include "copyright.h" -#include "config.h" -#include "utils.h" -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "compose.h" -#include "cursor.h" -#include "mesg.h" -#include "dialogs.h" -#include "modes.h" -#include "resources.h" -#include "news.h" -#include "internals.h" -#include "save.h" -#include "xmisc.h" -#include "error_hnds.h" -#include "xthelper.h" -#include "xrn.h" -#include "cancel.h" -#include "buttons.h" -#include "butdefs.h" -#include "mesg_strings.h" -#include "newsrcfile.h" -#include "butexpl.h" -#include "Text.h" -#include "ButtonBox.h" -#include "ngMode.h" -#include "addMode.h" -#include "artMode.h" -#include "allMode.h" - -#ifndef O_RDONLY -#define O_RDONLY 0 -#endif - -/* Action to take when a confirmation box is clicked in */ -static void (*ConfirmAction) _ARGUMENTS((void)); - - -int CurrentMode = NO_MODE; /* current mode */ -int PreviousMode = NO_MODE; /* previous mode, what buttons to */ - /* remove */ - -#define XRN_NO 0 -#define XRN_YES 1 - -/* the user is in a command - eat type ahead */ -int inCommand = 0; -int inSubCommand = 0; - -void doTheRightThing _ARGUMENTS((Widget, XEvent *,String *,Cardinal *)); -void doPrefetch _ARGUMENTS((Widget, XEvent *, String *, Cardinal *)); - -static XtActionsRec TopActions[] = { - {"doTheRightThing", doTheRightThing}, - {"doPrefetch", doPrefetch}, -}; - -static char TopNonButtonInfo[LABEL_SIZE]; -static char BottomNonButtonInfo[LABEL_SIZE]; - - -/* - * handle the Enter and Leave events for the buttons - * - * upon entering a button, get it's info string and put in the Question label - * upon leaving a button, restore the old info string - * - */ -/*ARGSUSED*/ -#if XtSpecificationRelease > 4 -static void topInfoHandler _ARGUMENTS((Widget, XtPointer, XEvent *, - Boolean *)); - -static void topInfoHandler(widget, client_data, event, dispatch) -#else -static void topInfoHandler _ARGUMENTS((Widget, XtPointer, XEvent *)); - -static void topInfoHandler(widget, client_data, event) -#endif /* XtSpecificationRelease > 4 */ - Widget widget; - XtPointer client_data; - XEvent *event; -#if XtSpecificationRelease > 4 - Boolean *dispatch; -#endif /* XtSpecificationRelease > 4 */ -{ - if (event->type == LeaveNotify) - INFO(TopNonButtonInfo); - else if (event->type == EnterNotify) - INFO(client_data); - - return; -} - -/* - * handle the Enter and Leave events for the buttons - * - * upon entering a button, get it's info string and put in the Question label - * upon leaving a button, restore the old info string - * - */ -/*ARGSUSED*/ -#if XtSpecificationRelease > 4 -static void bottomInfoHandler _ARGUMENTS((Widget, XtPointer, XEvent *, - Boolean *)); - -static void bottomInfoHandler(widget, client_data, event, dispatch) -#else -static void bottomInfoHandler _ARGUMENTS((Widget, XtPointer, XEvent *)); - -static void bottomInfoHandler(widget, client_data, event) -#endif /* XtSpecificationRelease > 4 */ - Widget widget; - XtPointer client_data; - XEvent *event; -#if XtSpecificationRelease > 4 - Boolean *dispatch; -#endif /* XtSpecificationRelease > 4 */ -{ - Arg infoLineArg[1]; - - if (! BottomInfoLine) - return; - - if (event->type == LeaveNotify) { - XtSetArg(infoLineArg[0], XtNlabel, BottomNonButtonInfo); - } else if (event->type == EnterNotify) { - XtSetArg(infoLineArg[0], XtNlabel, client_data); - } - XtSetValues(BottomInfoLine, infoLineArg, XtNumber(infoLineArg)); - return; -} - -static void setTopInfoLineHandler _ARGUMENTS((Widget, char *)); - -static void setTopInfoLineHandler(widget, message) - Widget widget; - char *message; -{ - XtAddEventHandler(widget, - (EventMask) (EnterWindowMask|LeaveWindowMask), - False, - (XtEventHandler) topInfoHandler, - (XtPointer) message); - return; -} - - -static void setBottomInfoLineHandler _ARGUMENTS((Widget, char *)); - -static void setBottomInfoLineHandler(widget, message) - Widget widget; - char *message; -{ - XtAddEventHandler(widget, - (EventMask) (EnterWindowMask|LeaveWindowMask), - False, - (XtEventHandler) bottomInfoHandler, - (XtPointer) message); - return; -} - - -#ifdef SWITCH_TOP_AND_BOTTOM -#define setTopInfoLine setBottomInfoLine -#endif - -void setTopInfoLine(message) - char *message; -{ - INFO(message); - (void) strcpy(TopNonButtonInfo, message); - return; -} - -#undef setTopInfoLine - -#ifdef SWITCH_TOP_AND_BOTTOM -#define setBottomInfoLine setTopInfoLine -#endif - -void setBottomInfoLine(message) - char *message; -{ - (void) strcpy(BottomNonButtonInfo, message); - - if (! BottomInfoLine) - return; - - XtVaSetValues(BottomInfoLine, XtNlabel, message, 0); - return; -} - -#undef setBottomInfoLine - -void setButtonSensitive(box, name, sensitive) - Widget box; - char *name; - Boolean sensitive; -{ - Widget w; - - if (! (w = XtNameToWidget(box, name))) - return; - - XtVaSetValues(w, XtNsensitive, sensitive, 0); -} - - -void doButtons(resource, box, buttonList, size, infoLine) - char *resource; - Widget box; - ButtonList *buttonList; - int *size; - int infoLine; -{ - char *ptr, *token; - int j, i = 0; - Widget button; - - if (resource) { - ptr = resource; - - while ((token = strtok(ptr, ", \t\n")) != NIL(char)) { - /* find name */ - for (j = 0; j < *size; j++) { - if (STREQ(token, (char *) buttonList[j].name)) { - button = ButtonBoxAddButton(buttonList[j].name, - buttonList[j].callbacks, box); - if (infoLine == TOP) { - setTopInfoLineHandler(button, buttonList[j].message); - } else { - setBottomInfoLineHandler(button, - buttonList[j].message); - } - i++; - break; - } - } - if (j == *size) { - mesgPane(XRN_SERIOUS, 0, BAD_BUTTON_NAME_MSG, token); - } - ptr = NIL(char); - } - *size = i; - - } else { - for (i = 0; i < *size; i++) { - button = ButtonBoxAddButton(buttonList[i].name, - buttonList[i].callbacks, box); - if (infoLine == TOP) { - setTopInfoLineHandler(button, buttonList[i].message); - } else { - setBottomInfoLineHandler(button, buttonList[i].message); - } - } - } - ButtonBoxDoneAdding(box); - return; -} - - -void createButtons() -{ -#define SETTRANSLATIONS(w, index, mode, bind) \ - Translations[index].widget = w; \ - Translations[index].unparsed[mode] = bind; - - XtAppAddActions(TopContext, TopActions, XtNumber(TopActions)); - XtAppAddActions(TopContext, AllActions, AllActionsCount); - XtAppAddActions(TopContext, NgActions, NgActionsCount); - XtAppAddActions(TopContext, ArtActions, ArtActionsCount); - XtAppAddActions(TopContext, AddActions, AddActionsCount); - - return; -} - - -void hideGenericWidgets() -{ - XtDestroyWidget(Frame); -} - -void swapMode() -/* - * change the buttons displayed in the TopButtonBox (switch modes) - */ -{ - if (PreviousMode == CurrentMode) { - return; - } - - /* - * NONE -> ADD - * manage add in top box - * manage art in bottom box - * desensitize bottom box - * install add actions in top box - */ - if ((PreviousMode == NO_MODE) && (CurrentMode == ADD_MODE)) { - hideGenericWidgets(); - - displayAddWidgets(); - /* - * NONE -> NG - * manage ng in top box - * manage art in bottom box - * desensitize bottom box - * install ng actions in top box - */ - } else if ((PreviousMode == NO_MODE) && (CurrentMode == NEWSGROUP_MODE)) { - hideGenericWidgets(); - - displayNewsgroupWidgets(); - /* - * ADD -> NG - * unmanage add in top box - * manage ng in top box - * install ng actions in top box - */ - } else if ((PreviousMode == ADD_MODE) && (CurrentMode == NEWSGROUP_MODE)) { - hideAddWidgets(); - - displayNewsgroupWidgets(); - /* - * NG -> ART - * unmanage ng in top box - * manage art in top box - * sensitize bottom box - * install art actions in top box - * install art actions in bottom box - */ - } else if ((PreviousMode == NEWSGROUP_MODE) && (CurrentMode == ARTICLE_MODE)) { - hideNewsgroupWidgets(); - - displayArticleWidgets(); - /* - * NG -> ADD - * unmanage ng in top box - * manage add in top box - * install add actions in top box - */ - } else if ((PreviousMode == NEWSGROUP_MODE) && (CurrentMode == ADD_MODE)) { - hideNewsgroupWidgets(); - - displayAddWidgets(); - /* - * NG -> ALL - * unmanage ng in top box - * unmanage ng in bottom box - * manage all in bottom box - * sensitize bottom box - * install all actions in bottom box - */ - } else if ((PreviousMode == NEWSGROUP_MODE) && (CurrentMode == ALL_MODE)) { - hideNewsgroupWidgets(); - - displayAllWidgets(); - /* - * ART -> NG - * desensitize bottom box - * unmanage art in top box - * manage ng in top box - * install ng actions in top box - */ - } else if ((PreviousMode == ARTICLE_MODE) && (CurrentMode == NEWSGROUP_MODE)) { - hideArticleWidgets(); - - displayNewsgroupWidgets(); - /* - * ALL -> NG - * manage ng in top box - * unmanage all in bottom box - * manage art in bottom box - * desensitize bottom box - */ - } else if ((PreviousMode == ALL_MODE) && (CurrentMode == NEWSGROUP_MODE)) { - hideAllWidgets(); - - displayNewsgroupWidgets(); - /* - * ART -> ALL (going back to previous ALL_MODE) - * unmanage art in bottom box - * unmanage art in top box - * manage all in bottom box - * manage ng in top box - * desensitize top box - * install all actions in bottom box - */ - } else if ((PreviousMode == ARTICLE_MODE) && (CurrentMode == ALL_MODE)) { - hideArticleWidgets(); - - displayAllWidgets(); - /* - * ALL -> ART - * manage art in top box - * unmanage all in bottom box - * manage art in bottom box - * install art actions in bottom box - */ - } else if ((PreviousMode == ALL_MODE) && (CurrentMode == ARTICLE_MODE)) { - hideAllWidgets(); - - displayArticleWidgets(); - } else { - (void) sprintf(error_buffer, ERROR_UNSUP_TRANS_MSG , - PreviousMode, CurrentMode); - ehErrorExitXRN(error_buffer); - } - - return; -} - -static int XRNAbort = 0; - -int abortP() -{ - xthHandleAllPendingEvents(); - return XRNAbort; -} - -void abortSet() -{ - XRNAbort = 1; - return; -} - -void abortClear() -{ - XRNAbort = 0; - return; -} - - - -/*ARGSUSED*/ -void doTheRightThing(widget, event, string, count) - Widget widget; - XEvent *event; - String *string; - Cardinal *count; -{ - if (inCommand) { - return; - } - inCommand = 1; - xrnBusyCursor(); - switch (CurrentMode) { - case ALL_MODE: - allDoTheRightThing(widget, event, string, count); - break; - - case NEWSGROUP_MODE: - ngDoTheRightThing(widget, event, string, count); - break; - - case ARTICLE_MODE: - artDoTheRightThing(widget, event, string, count); - break; - } - xrnUnbusyCursor(); - inCommand = 0; - return; -} - - - -Boolean watchingGroup(newsgroup) - char *newsgroup; -{ - static int inited = 0; -#ifdef POSIX_REGEX - static regex_t *GroupList; -#else - static char **GroupList; -#endif - static int GroupListCount; - int p; - - if (! inited) { - GroupList = parseRegexpList(app_resources.watchList, "watchUnread", - &GroupListCount); - inited++; - } - - if (newsgroup == 0) - return False; - if (! GroupList) - return True; - - for (p = 0; p < GroupListCount; p++) { -#ifdef POSIX_REGEX - if (! regexec(&GroupList[p], newsgroup, 0, 0, 0)) - return True; -#else -# ifdef SYSV_REGEX - if (regex(GroupList[p], newsgroup)) - return True; -# else - re_comp(GroupList[p]); - if (re_exec(newsgroup)) - return True; -# endif -#endif - } - - return False; -} - - -String anyIterator(w, string, group, start, delete, out_left) - Widget w; - String string; - Boolean group, start, delete; - long *out_left; -{ - static char *name = 0; - static long left, right; - Boolean ret; - - if (start) { - ret = TextGetSelectedOrCurrentLines(w, &left, &right); - if (out_left) - *out_left = left; - if (ret) { - TextUnsetSelection(w); - if (! name) - name = XtRealloc(name, 1); - return name; - } - return 0; - } - - if (out_left) - *out_left = left; - - if (left >= right) - return 0; - - if (group) - currentGroup(CurrentMode, string, &name, left); - else { - /* Will we ever have an article number longer than 10 chars long? - Yeah, right. */ - name = XtRealloc(name, 11); - (void) sprintf(name, "%ld", atol(&string[left] + 2)); - } - - if (delete) { - long new = left; - - if (moveCursor(FORWARD, string, &new)) - right -= new - left; - else - right = left; - TextRemoveLine(w, left); - removeLine(string, &left); - } - else if (! moveCursor(FORWARD, string, &left)) - left = right + 1; - return name; -} - -static Widget ConfirmBox = 0; - -/*ARGSUSED*/ -static void generalHandler _ARGUMENTS((Widget, XtPointer, XtPointer)); - -static void generalHandler(widget, client_data, call_data) - Widget widget; - XtPointer client_data; - XtPointer call_data; -{ - if (inCommand) { - return; - } - inCommand = 1; - xrnBusyCursor(); - PopDownDialog(ConfirmBox); - ConfirmBox = 0; - - if ((int) client_data == XRN_YES) - (*ConfirmAction)(); - - xrnUnbusyCursor(); - inCommand = 0; - return; -} - -void confirmBox(message, mode, flag, handler) - String message; - int mode, flag; - void (*handler) _ARGUMENTS((void)); -{ - static struct DialogArg args[] = { - {NO_STRING, generalHandler, (XtPointer) XRN_NO}, - {YES_STRING, generalHandler, (XtPointer) XRN_YES}, - }; - - if (CurrentMode != mode) - return; - - if (app_resources.confirmMode & flag) { - ConfirmAction = handler; - if (! ConfirmBox) { - ConfirmBox = CreateDialog(TopLevel, message, DIALOG_NOTEXT, - args, XtNumber(args)); - PopUpDialog(ConfirmBox); - } - - return; - } - (*handler)(); -} - - -void determineMode() -/* - * determine the initial mode and set up Text, TopButtonBox, and Question - */ -{ - String string; - - if ((string = newGroups())) { - switchToAddMode(string); - FREE(string); - } - else - switchToNewsgroupMode(False); - - return; -} diff -u -d -r -N -P 8.02/buttons.h 9.00/buttons.h --- 8.02/buttons.h Tue May 9 22:19:18 1995 +++ 9.00/buttons.h Sun Jun 29 13:30:14 1997 @@ -6,7 +6,7 @@ #include "utils.h" /* - * $Id: buttons.h,v 1.12 1995/05/10 02:17:34 jik Exp $ + * $Id: buttons.h,v 1.16 1997/06/29 17:30:14 jik Exp $ */ /* @@ -39,17 +39,27 @@ * */ -extern void determineMode _ARGUMENTS((void)); +typedef struct buttonList { + String name; + XtCallbackRec *callbacks; + char *message; + Boolean active; +} ButtonList; + +#define TOP 0 +#define BOTTOM 1 + +extern void determineMode _ARGUMENTS((Boolean)); extern Boolean watchingGroup _ARGUMENTS((char *)); extern void doTheRightThing _ARGUMENTS((Widget, XEvent *, String *, Cardinal *)); extern void createButtons _ARGUMENTS((void)); -extern void setButtonSensitive _ARGUMENTS((Widget, char *, /* Boolean */ int)); +extern void setButtonSensitive _ARGUMENTS((Widget, char *, Boolean)); +extern void setButtonActive _ARGUMENTS((ButtonList *, char *, Boolean)); extern void confirmBox _ARGUMENTS((String, int, int, void (*) _ARGUMENTS((void)))); -extern String anyIterator _ARGUMENTS((Widget, String, /* Boolean */ int, - /* Boolean */ int, /* Boolean */ int, - long *)); +extern String anyIterator _ARGUMENTS((Widget, String, Boolean, + Boolean, Boolean, long *)); extern void setTopInfoLine _ARGUMENTS((char *)); extern void setBottomInfoLine _ARGUMENTS((char *)); extern void swapMode _ARGUMENTS((void)); @@ -57,15 +67,6 @@ extern int abortP _ARGUMENTS((void)); extern void abortSet _ARGUMENTS((void)); extern void abortClear _ARGUMENTS((void)); - -typedef struct buttonList { - String name; - XtCallbackRec *callbacks; - char *message; -} ButtonList; - -#define TOP 0 -#define BOTTOM 1 extern void doButtons _ARGUMENTS((char *, Widget, ButtonList *, int *, int)); diff -u -d -r -N -P 8.02/clientlib.c 9.00/clientlib.c --- 8.02/clientlib.c Thu May 4 17:00:42 1995 +++ 9.00/clientlib.c Thu Jun 5 07:11:40 1997 @@ -1,6 +1,6 @@ #if !defined(lint) && !defined(SABER) && !defined(GCC_WALL) -static char XRNrcsid[] = "$Id: clientlib.c,v 1.14 1995/05/04 21:00:42 jik Exp $"; +static char XRNrcsid[] = "$Id: clientlib.c,v 1.18 1997/01/08 22:24:28 jik Exp $"; #endif /* #define DEBUG */ @@ -57,15 +57,11 @@ #include "internals.h" #include "clientlib.h" -#ifdef BSD_BFUNCS -#define memset(_Str_, _Chr_, _Len_) bzero(_Str_, _Len_) -#define memcpy(_To_, _From_, _Len_) bcopy(_From_, _To_, _Len_) -#endif - FILE *ser_rd_fp = NULL; FILE *ser_wr_fp = NULL; char *server_init_msg = NULL; +char *nntp_port = NULL; #ifdef DECNET static int get_dnet_socket _ARGUMENTS((char *)); @@ -304,17 +300,26 @@ #ifdef h_addr register char **cp; #endif /* h_addr */ + static int port = 0; struct sockaddr_in sin; struct servent *getservbyname _ARGUMENTS((CONST char *, CONST char *)); struct servent *sp; struct hostent *gethostbyname _ARGUMENTS((CONST char *)), *hp; (void) memset((char *) &sin, 0, sizeof(sin)); - if ((sp = getservbyname("nntp", "tcp")) == NULL) { - (void) fprintf(stderr, "nntp/tcp: Unknown service.\n"); - return (-1); - } - sin.sin_port = sp->s_port; + + if (! port) + if (nntp_port) + port = htons(atoi(nntp_port)); + else { + if ((sp = getservbyname("nntp", "tcp")) == NULL) { + (void) fprintf(stderr, "nntp/tcp: Unknown service.\n"); + return (-1); + } + port = sp->s_port; + } + sin.sin_port = port; + if ((sin.sin_addr.s_addr = inet_addr(machine)) != -1) { sin.sin_family = AF_INET; return(get_tcp_socket1(&sin)); @@ -570,10 +575,10 @@ } } #endif - if ((cp = index(string, '\r')) != NULL) - *cp = '\0'; - else if ((cp = index(string, '\n')) != NULL) - *cp = '\0'; + + cp = &string[strlen(string)]; + if ((cp >= &string[2]) && (cp[-2] == '\r') && (cp[-1] == '\n')) + cp[-2] = '\0'; #ifdef DEBUG (void) fprintf(stderr, "<<< %s\n", string); #endif diff -u -d -r -N -P 8.02/clientlib.h 9.00/clientlib.h --- 8.02/clientlib.h Mon Jan 30 07:49:17 1995 +++ 9.00/clientlib.h Thu Jun 5 07:11:40 1997 @@ -2,7 +2,7 @@ #define CLIENTLIB_H /* - * $Id: clientlib.h,v 1.2 1994/11/23 01:51:01 jik Exp $ + * $Id: clientlib.h,v 1.3 1997/01/02 23:14:15 jik Exp $ */ /* @@ -35,5 +35,7 @@ */ extern int handle_server_response _ARGUMENTS((int, char *)); + +extern char *nntp_port; #endif /* CLIENTLIB_H */ diff -u -d -r -N -P 8.02/compose.c 9.00/compose.c --- 8.02/compose.c Thu May 2 08:36:20 1996 +++ 9.00/compose.c Sun Dec 28 11:29:44 1997 @@ -1,5 +1,5 @@ #if !defined(lint) && !defined(SABER) && !defined(GCC_WALL) -static char XRNrcsid[] = "$Id: compose.c,v 1.91 1996/05/02 12:35:59 jik Exp $"; +static char XRNrcsid[] = "$Id: compose.c,v 1.111 1997/12/28 16:29:44 jik Exp $"; #endif /* @@ -39,6 +39,7 @@ #include #include #include +#include #include "error_hnds.h" #ifdef RESOLVER #include @@ -49,6 +50,7 @@ #include "ngMode.h" #include "Text.h" #include "ButtonBox.h" +#include "file_cache.h" #if defined(aiws) || defined(DGUX) struct passwd *getpwuid(); @@ -93,10 +95,12 @@ #include "mesg_strings.h" #ifdef INN +#include #include #endif static void saveDeadLetter _ARGUMENTS((char *)); +static int trim_references _ARGUMENTS((char *)); #ifdef GENERATE_EXTRA_FIELDS static char *gen_id _ARGUMENTS((void)); @@ -131,7 +135,7 @@ EditStatus editing_status = NoEdit; extern int inchannel; -static int Call_Editor _ARGUMENTS((/* Boolean */ int save_message)); +static int Call_Editor _ARGUMENTS((Boolean save_message)); /* End stuff used for editorCommand support. @@ -156,13 +160,14 @@ struct header { art_num article; - char *artFile; + file_cache_file artFile; char *newsgroups; char *subject; - char *messageId; + char *id; char *followupTo; char *references; char *from; + char *sender; char *replyTo; char *distribution; char *keywords; @@ -261,14 +266,14 @@ Here's how they're used: real_host: - * Message-ID: field (if GENERATE_EXTRA_FIELDS is defined) + * Message-ID: field (if GENERATE_EXTRA_FIELDS is defined), + * Verification that the user is allowed to cancel an article sender_host: - * Sender: field, if necessary, + * Sender: field, if necessary - Host: - * From: field, - * Verification that the user is allowed to cancel an article + host: + * From: field path: * Path: field @@ -288,285 +293,283 @@ static int getHeader _ARGUMENTS((art_num, struct header *)); static int getHeader(article, Header) - art_num article; - struct header *Header; + art_num article; + struct header *Header; { - struct newsgroup *newsgroup = CurrentGroup; - struct passwd *pwd; - char host[HOST_NAME_SIZE], buffer[BUFFER_SIZE], *ptr; - char real_host[HOST_NAME_SIZE]; + struct newsgroup *newsgroup = CurrentGroup; + struct passwd *pwd; + char host[HOST_NAME_SIZE], buffer[BUFFER_SIZE], *ptr; + char real_host[HOST_NAME_SIZE]; #ifndef INEWS - char sender_host[HOST_NAME_SIZE]; + char sender_host[HOST_NAME_SIZE]; #endif #ifdef INN - char *inn_org; + char *inn_org; #else - char path[HOST_NAME_SIZE]; + char path[HOST_NAME_SIZE]; #endif - (void) memset((char *)Header, 0, sizeof(*Header)); + (void) memset((char *)Header, 0, sizeof(*Header)); - if (article > 0) { - Header->article = article; - xhdr(newsgroup, article, "newsgroups", &Header->newsgroups); - xhdr(newsgroup, article, "subject", &Header->subject); - xhdr(newsgroup, article, "message-id", &Header->messageId); - xhdr(newsgroup, article, "followup-to", &Header->followupTo); - xhdr(newsgroup, article, "references", &Header->references); - xhdr(newsgroup, article, "from", &Header->from); - xhdr(newsgroup, article, "reply-to", &Header->replyTo); - xhdr(newsgroup, article, "distribution", &Header->distribution); - xhdr(newsgroup, article, "keywords", &Header->keywords); - } else { - /* information for 'post' */ - if (newsgroup) { - Header->newsgroups = XtNewString(newsgroup->name); - } else { - Header->newsgroups = XtNewString(""); - } - } + if (article > 0) { +#define CHECK(field,name) if (art->field) Header->field = XtNewString(art->field); else xhdr(newsgroup, article, name, &Header->field); - /* - * since I'm lazy down below, I'm going to replace NIL pointers with "" - */ - if (Header->newsgroups == NIL(char)) { - Header->newsgroups = XtNewString(""); - } - if (Header->subject == NIL(char)) { - Header->subject = XtNewString(""); - } - if (Header->messageId == NIL(char)) { - Header->messageId = XtNewString(""); - } - if (Header->followupTo == NIL(char)) { - Header->followupTo = XtNewString(""); - } - if (Header->references == NIL(char)) { - Header->references = XtNewString(""); - } - if (Header->from == NIL(char)) { - Header->from = XtNewString(""); - } - if (Header->replyTo == NIL(char)) { - Header->replyTo = XtNewString(""); - } - if (Header->distribution == NIL(char)) { - Header->distribution = XtNewString(""); - } - if (Header->keywords == NIL(char)) { - Header->keywords = XtNewString(""); - } + struct article *art = artStructGet(newsgroup, article, False); - real_host[0] = '\0'; - real_host[sizeof(real_host)-1] = '\0'; + Header->article = article; + CHECK(newsgroups, "newsgroups"); + CHECK(subject, "subject"); + CHECK(references, "references"); + CHECK(from, "from"); + CHECK(id, "message-id"); + xhdr(newsgroup, article, "followup-to", &Header->followupTo); + xhdr(newsgroup, article, "sender", &Header->sender); + xhdr(newsgroup, article, "reply-to", &Header->replyTo); + xhdr(newsgroup, article, "distribution", &Header->distribution); + xhdr(newsgroup, article, "keywords", &Header->keywords); + +#undef CHECK + } else + /* information for 'post' */ + if (newsgroup) + Header->newsgroups = XtNewString(newsgroup->name); + else + Header->newsgroups = XtNewString(""); + + /* + * since I'm lazy down below, I'm going to replace NIL pointers with "" + */ + if (Header->newsgroups == NIL(char)) + Header->newsgroups = XtNewString(""); + if (Header->subject == NIL(char)) + Header->subject = XtNewString(""); + if (Header->id == NIL(char)) + Header->id = XtNewString(""); + if (Header->followupTo == NIL(char)) + Header->followupTo = XtNewString(""); + if (Header->references == NIL(char)) + Header->references = XtNewString(""); + if (Header->from == NIL(char)) + Header->from = XtNewString(""); + if (Header->sender == NIL(char)) + Header->sender = XtNewString(""); + if (Header->replyTo == NIL(char)) + Header->replyTo = XtNewString(""); + if (Header->distribution == NIL(char)) + Header->distribution = XtNewString(""); + if (Header->keywords == NIL(char)) + Header->keywords = XtNewString(""); + + real_host[0] = '\0'; + real_host[sizeof(real_host)-1] = '\0'; #ifndef INEWS - sender_host[0] = '\0'; - sender_host[sizeof(sender_host)-1] = '\0'; + sender_host[0] = '\0'; + sender_host[sizeof(sender_host)-1] = '\0'; #endif - host[0] = '\0'; - host[sizeof(host)-1] = '\0'; + host[0] = '\0'; + host[sizeof(host)-1] = '\0'; #ifndef INN - path[0] = '\0'; - path[sizeof(path)-1] = '\0'; + path[0] = '\0'; + path[sizeof(path)-1] = '\0'; #endif #ifdef UUCPNAME - { - FILE * uup; + { + FILE * uup; - if ((uup = fopen(UUCPNAME, "r")) != NULL) { - char *p; - char xbuf[BUFSIZ]; + if ((uup = fopen(UUCPNAME, "r")) != NULL) { + char *p; + char xbuf[BUFSIZ]; - while (fgets(xbuf, sizeof(xbuf), uup) != NULL) { - if (*xbuf <= ' ' || *xbuf == '#') { - continue; - } - break; - } - if (p = index(xbuf, '\n')) { - *p = 0; - } - (void) strncpy(real_host, xbuf, sizeof(real_host)-1); - (void) fclose(uup); - } + while (fgets(xbuf, sizeof(xbuf), uup) != NULL) { + if (*xbuf <= ' ' || *xbuf == '#') { + continue; + } + break; + } + if (p = index(xbuf, '\n')) { + *p = 0; + } + (void) strncpy(real_host, xbuf, sizeof(real_host)-1); + (void) fclose(uup); } + } #endif #ifdef REAL_HOST - if (! real_host[0]) - (void) strncpy(real_host, REAL_HOST, sizeof(real_host)-1); + if (! real_host[0]) + (void) strncpy(real_host, REAL_HOST, sizeof(real_host)-1); #endif - if (! real_host[0]) { + if (! real_host[0]) { # ifdef RESOLVER - struct hostent *hent; + struct hostent *hent; # endif - (void) gethostname(real_host, sizeof(real_host)); + (void) gethostname(real_host, sizeof(real_host)); # ifdef RESOLVER - if ((hent = gethostbyname(real_host))) - (void) strncpy(real_host, hent->h_name, sizeof(real_host)-1); + if ((hent = gethostbyname(real_host))) + (void) strncpy(real_host, hent->h_name, sizeof(real_host)-1); # endif - } + } #ifndef INEWS #ifdef SENDER_HOST - if (! sender_host[0]) - (void) strncpy(sender_host, SENDER_HOST, sizeof(sender_host)-1); + if (! sender_host[0]) + (void) strncpy(sender_host, SENDER_HOST, sizeof(sender_host)-1); #endif - if (! sender_host[0]) - strcpy(sender_host, real_host); + if (! sender_host[0]) + strcpy(sender_host, real_host); #endif /* ! INEWS */ - if (! host[0]) - if ((ptr = getenv("HIDDENHOST"))) - (void) strncpy(host, ptr, sizeof(host)-1); + if (! host[0]) + if ((ptr = getenv("HIDDENHOST"))) + (void) strncpy(host, ptr, sizeof(host)-1); - if ((! host[0]) && app_resources.hiddenHost && *app_resources.hiddenHost) - (void) strncpy(host, app_resources.hiddenHost, sizeof(host)-1); + if ((! host[0]) && app_resources.hiddenHost && *app_resources.hiddenHost) + (void) strncpy(host, app_resources.hiddenHost, sizeof(host)-1); #ifdef INN - if (! host[0]) { - /* always let values in inn.conf take precedence */ - char *inn_fromhost = GetConfigValue("fromhost"); - if (inn_fromhost) - (void) strncpy(host, inn_fromhost, sizeof(host)-1); - } + if (! host[0]) { + /* always let values in inn.conf take precedence */ + char *inn_fromhost = GetConfigValue("fromhost"); + if (inn_fromhost) + (void) strncpy(host, inn_fromhost, sizeof(host)-1); + } #endif /* INN */ #ifdef HIDDEN_FILE - if (! host[0]) - if (ptr = getinfofromfile(HIDDEN_FILE)) - (void) strncpy(host, ptr, sizeof(host)-1); + if (! host[0]) + if (ptr = getinfofromfile(HIDDEN_FILE)) + (void) strncpy(host, ptr, sizeof(host)-1); #endif /* HIDDEN_FILE */ #ifdef RETURN_HOST - if (! host[0]) - (void) strncpy(host, RETURN_HOST, sizeof(host)-1); + if (! host[0]) + (void) strncpy(host, RETURN_HOST, sizeof(host)-1); #endif - if (! host[0]) - (void) strcpy(host, real_host); + if (! host[0]) + (void) strcpy(host, real_host); #ifndef INN - if (! path[0]) - if ((ptr = getenv("HIDDENPATH"))) - (void) strncpy(path, ptr, sizeof(path)-1); + if (! path[0]) + if ((ptr = getenv("HIDDENPATH"))) + (void) strncpy(path, ptr, sizeof(path)-1); # ifdef PATH_FILE - if (! path[0]) - if (ptr = getinfofromfile(PATH_FILE)) - (void) strncpy(path, ptr, sizeof(path)-1); + if (! path[0]) + if (ptr = getinfofromfile(PATH_FILE)) + (void) strncpy(path, ptr, sizeof(path)-1); # endif - if (! path[0]) - (void) strcpy(path, real_host); + if (! path[0]) + (void) strcpy(path, real_host); #endif /* ! INN */ - /* If the host name is not a full domain name, put in the domain */ - if (index (host, '.') == NIL(char)) { - char *domain = app_resources.domainName; + /* If the host name is not a full domain name, put in the domain */ + if (index (host, '.') == NIL(char)) { + char *domain = app_resources.domainName; - if (! domain) - domain = getenv("DOMAIN"); + if (! domain) + domain = getenv("DOMAIN"); #ifdef INN - if (! domain) - domain = GetFileConfigValue("domain"); + if (! domain) + domain = GetFileConfigValue("domain"); #endif #ifdef DOMAIN_FILE - if (! domain) - domain = getinfofromfile(DOMAIN_FILE); + if (! domain) + domain = getinfofromfile(DOMAIN_FILE); #endif #ifdef DOMAIN_NAME - if (! domain) - domain = DOMAIN_NAME; + if (! domain) + domain = DOMAIN_NAME; #endif - if (! domain) { - mesgPane(XRN_SERIOUS, 0, NO_DOMAIN_MSG); - freeHeader(Header); - return XRN_ERROR; - } - - (void) strncat(host, domain, sizeof(host) - strlen(host)); + if (! domain) { + mesgPane(XRN_SERIOUS, 0, NO_DOMAIN_MSG); + freeHeader(Header); + return XRN_ERROR; } - Header->host = XtNewString(host); - Header->real_host = XtNewString(real_host); + (void) strncat(host, domain, sizeof(host) - strlen(host)); + } + + Header->host = XtNewString(host); + Header->real_host = XtNewString(real_host); #ifndef INEWS - Header->sender_host = XtNewString(sender_host); + Header->sender_host = XtNewString(sender_host); #endif #ifndef INN - Header->path = XtNewString(path); + Header->path = XtNewString(path); #endif /* INN */ - if (app_resources.organization != NIL(char)) { - Header->organization = XtNewString(app_resources.organization); + if (app_resources.organization != NIL(char)) { + Header->organization = XtNewString(app_resources.organization); #ifndef apollo - } else if ((ptr = getenv ("ORGANIZATION")) != NIL(char)) { + } else if ((ptr = getenv ("ORGANIZATION")) != NIL(char)) { #else - } else if ((ptr = getenv ("NEWSORG")) != NIL(char)) { + } else if ((ptr = getenv ("NEWSORG")) != NIL(char)) { #endif - Header->organization = XtNewString(ptr); + Header->organization = XtNewString(ptr); #ifdef INN - } else if ((inn_org = GetConfigValue("organization"))) { - Header->organization = XtNewString(inn_org); + } else if ((inn_org = GetConfigValue("organization"))) { + Header->organization = XtNewString(inn_org); #endif /* INN */ #ifdef ORG_FILE - } else if ((ptr = getinfofromfile(ORG_FILE)) != NULL) { - Header->organization = XtNewString(ptr); + } else if ((ptr = getinfofromfile(ORG_FILE)) != NULL) { + Header->organization = XtNewString(ptr); #endif /* ORG_FILE */ - } else { + } else { #ifdef ORG_NAME - Header->organization = XtNewString(ORG_NAME); + Header->organization = XtNewString(ORG_NAME); #else - Header->organization = XtNewString(""); + Header->organization = XtNewString(""); #endif /* ORG_NAME */ - } + } - Header->user = getUser(); + Header->user = getUser(); - pwd = getpwuid(getuid()); + pwd = getpwuid(getuid()); - if ((Header->real_user = pwd->pw_name)) { - Header->real_user = XtNewString(Header->real_user); - } else { - Header->real_user = XtNewString(""); - } + if ((Header->real_user = pwd->pw_name)) { + Header->real_user = XtNewString(Header->real_user); + } else { + Header->real_user = XtNewString(""); + } - if ((Header->fullname = getenv("FULLNAME"))) { - Header->fullname = XtNewString(Header->fullname); - } else if ((Header->fullname = pwd->pw_gecos)) { - Header->fullname = XtNewString(Header->fullname); + if ((Header->fullname = getenv("FULLNAME"))) { + Header->fullname = XtNewString(Header->fullname); + } else if ((Header->fullname = pwd->pw_gecos)) { + Header->fullname = XtNewString(Header->fullname); - ptr = index(Header->fullname, ','); - if (ptr != NIL(char)) { - *ptr = '\0'; - } + ptr = index(Header->fullname, ','); + if (ptr != NIL(char)) { + *ptr = '\0'; + } - /* & expansion */ - ptr = index(Header->fullname, '&'); - if (ptr != NIL(char)) { - char *p = buffer + (ptr - Header->fullname); + /* & expansion */ + ptr = index(Header->fullname, '&'); + if (ptr != NIL(char)) { + char *p = buffer + (ptr - Header->fullname); - buffer[0] = '\0'; - *ptr = '\0'; - (void) strcpy(buffer, Header->fullname); - (void) strcat(buffer, Header->user); - if (isascii(*p)) { - *p = toupper(*p); - } - ptr++; - (void) strcat(buffer, ptr); - FREE(Header->fullname); - Header->fullname = XtNewString(buffer); - } - } else { - Header->fullname = XtNewString(""); + buffer[0] = '\0'; + *ptr = '\0'; + (void) strcpy(buffer, Header->fullname); + (void) strcat(buffer, Header->user); + if (isascii(*p)) { + *p = toupper(*p); + } + ptr++; + (void) strcat(buffer, ptr); + FREE(Header->fullname); + Header->fullname = XtNewString(buffer); } - return XRN_OKAY; + } else { + Header->fullname = XtNewString(""); + } + return XRN_OKAY; } /* @@ -758,13 +761,14 @@ static void freeHeader(Header) struct header *Header; { - FREE(Header->artFile); + file_cache_file_release(FileCache, Header->artFile); FREE(Header->newsgroups); FREE(Header->subject); - FREE(Header->messageId); + FREE(Header->id); FREE(Header->followupTo); FREE(Header->references); FREE(Header->from); + FREE(Header->sender); FREE(Header->replyTo); FREE(Header->distribution); FREE(Header->keywords); @@ -834,14 +838,23 @@ #define buildSender(Header,msg,msg_size,msg_total_size) \ _buildFrom(Header,(msg), True, True, (msg_size), (msg_total_size)) -static void _buildFrom _ARGUMENTS((struct header *, char **, /* Boolean */ int, - /* Boolean */ int, int *, int *)); +static void _buildFrom _ARGUMENTS((struct header *, char **, Boolean, + Boolean, int *, int *)); -static void _buildFrom(Header, msg, real_addr, sender, msg_size, msg_total_size) - struct header *Header; - char **msg; - Boolean real_addr, sender; - int *msg_size, *msg_total_size; +static void _buildFrom( + _ANSIDECL(struct header *, Header), + _ANSIDECL(char **, msg), + _ANSIDECL(Boolean, real_addr), + _ANSIDECL(Boolean, sender), + _ANSIDECL(int *, msg_size), + _ANSIDECL(int *, msg_total_size) + ) + _KNRDECL(struct header *, Header) + _KNRDECL(char **, msg) + _KNRDECL(Boolean, real_addr) + _KNRDECL(Boolean, sender) + _KNRDECL(int *, msg_size) + _KNRDECL(int *, msg_total_size) #endif /* INEWS */ { char *message = *msg; @@ -915,11 +928,16 @@ char *message = *msg; int message_size = *msg_size; int message_total_size = *msg_total_size; + int msg_len, ref_len, new_ref_len; CHECK_SIZE(sizeof("References: \n") - 1 + strlen(Header->references) + - strlen(Header->messageId)); - (void) sprintf(&message[strlen(message)], "References: %s %s\n", - Header->references, Header->messageId); + strlen(Header->id)); + (void) sprintf(&message[(msg_len = strlen(message))], "References: %s %s\n", + Header->references, Header->id); + + ref_len = strlen(&message[msg_len]); + new_ref_len = trim_references(&message[msg_len]); + message_size -= (ref_len - new_ref_len); *msg = message; *msg_size = message_size; @@ -1068,11 +1086,14 @@ #endif /* GENERATE_EXTRA_FIELDS */ -static void compAbortUtil _ARGUMENTS((int, /* Boolean */ int)); +static void compAbortUtil _ARGUMENTS((int, Boolean)); -static void compAbortUtil(mesg_name, save) - int mesg_name; - Boolean save; +static void compAbortUtil( + _ANSIDECL(int, mesg_name), + _ANSIDECL(Boolean, save) + ) + _KNRDECL(int, mesg_name) + _KNRDECL(Boolean, save) { char *ptr; char *msg WALL(= 0); @@ -1126,7 +1147,7 @@ { FILE *fp; char *file = utTildeExpand(fname); -#ifdef __osf__ +#if defined(__osf__) || defined(__hpux) time_t clock; #else long clock; @@ -1259,7 +1280,7 @@ char *ptr; int mode, i, j, comma; unsigned long newsgroups_status WALL(= 0); - int tries = 1; + int tries = 1, saved = 0; int retry_editing = 0; int saved_dead = 0; @@ -1318,8 +1339,8 @@ buffer_size = 0; CHECK_SIZE_EXTENDED(buffer, buffer_size, buffer_total_size, sizeof("References: \n") + - strlen(Header.messageId)); - (void) sprintf(buffer, "References: %s\n", Header.messageId); + strlen(Header.id)); + (void) sprintf(buffer, "References: %s\n", Header.id); addField(buffer); } } @@ -1623,12 +1644,30 @@ } break; - case POST_OKAY: + case POST_OKAY: + { + char *file, *alt_file; + + if (mode == XRN_NEWS) { + file = app_resources.saveSentPostings; + alt_file = app_resources.saveSentMail; + } + else { + file = app_resources.saveSentMail; + alt_file = app_resources.saveSentPostings; + } + if (file && ((! alt_file) || + (strcmp(file, alt_file) != 0) || + (! saved))) { + saveMessage(file, ptr); + saved++; + } mesgPane(XRN_INFO, mesg_name, (mode == XRN_NEWS) ? ((newsgroups_status & NG_MODERATED) ? MAILED_TO_MODERATOR_MSG : ARTICLE_POSTED_MSG) : MAIL_MESSAGE_SENT_MSG); break; + } } tries--; if ((mode == XRN_NEWS) && @@ -1660,19 +1699,16 @@ { char *text, *prefix, input[256]; int size = BUFFER_SIZE, prefix_size, cur_size; - FILE *infile; + FILE *infile = NULL; text = XtMalloc(size); if (PostingMode == REPLY) { - (void) sprintf(text, REPLY_YOU_WRITE_MSG, - Header.messageId ); + (void) sprintf(text, REPLY_YOU_WRITE_MSG, Header.id); } else if (PostingMode == FORWARD) { - (void) sprintf(text, FORWARDED_ARTIKEL_MSG, - Header.messageId, Header.from); + (void) sprintf(text, FORWARDED_ARTIKEL_MSG, Header.id, Header.from); } else { - (void) sprintf(text, FOLLOWUP_AUTHOR_WRITE_MSG, - Header.messageId, Header.from); + (void) sprintf(text, FOLLOWUP_AUTHOR_WRITE_MSG, Header.id, Header.from); } cur_size = strlen(text); @@ -1680,9 +1716,13 @@ if (app_resources.includeCommand && PostingMode != FORWARD) { char cmdbuf[1024]; - sprintf(cmdbuf, app_resources.includeCommand, - app_resources.includePrefix, Header.artFile); - infile = xrn_popen(cmdbuf, "r"); + if (Header.artFile) { + sprintf(cmdbuf, app_resources.includeCommand, + app_resources.includePrefix, + file_cache_file_name(FileCache, Header.artFile)); + infile = xrn_popen(cmdbuf, "r"); + } + if (! infile) { mesgPane(XRN_SERIOUS, 0, CANT_INCLUDE_CMD_MSG); FREE(text); @@ -1693,7 +1733,8 @@ prefix_size = 0; } else { - infile = fopen(Header.artFile, "r"); + if (Header.artFile) + infile = fopen(file_cache_file_name(FileCache, Header.artFile), "r"); if (! infile) { mesgPane(XRN_SERIOUS, 0, CANT_OPEN_ART_MSG, errmsg(errno)); FREE(text); @@ -1988,6 +2029,9 @@ ptr[buf.st_size] = '\0'; (void) fclose(filefp); + TextSetString(ComposeText, ptr); + FREE(ptr); + /* pop up a confirm box */ if (ConfirmationBox(TopLevel, confirm1, 0, 0, False) == XRN_CB_ABORT) { @@ -1998,13 +2042,9 @@ } else { Call_Editor(False); } - FREE(ptr); return; } - TextSetString(ComposeText, ptr); - FREE(ptr); - compSendFunction(0, 0, 0, 0); return; } @@ -2039,8 +2079,10 @@ static int -Call_Editor(save_message) - Boolean save_message; +Call_Editor( + _ANSIDECL(Boolean, save_message) + ) + _KNRDECL(Boolean, save_message) { char *dsp, *file; char buffer[1024], buffer2[1024]; @@ -2055,9 +2097,8 @@ if ((editing_status == NoEdit) || header) { editing_status = InProgress; if(EditorFileName != NIL(char)) - FREE(EditorFileName); + utTempnamFree(EditorFileName); EditorFileName = utTempnam(app_resources.tmpDir, "xrn"); - EditorFileName = XtNewString(EditorFileName); if ((filefp = fopen(EditorFileName, "w")) == NULL) { mesgPane(XRN_SERIOUS, mesg_name, CANT_OPEN_TEMP_MSG, EditorFileName, errmsg(errno)); @@ -2275,7 +2316,7 @@ (char *) 0); XawPanedSetMinMax(label, (int) height_val, (int) height_val); - XawPanedAllowResize(ComposeText, True); + XawPanedAllowResize(TEXT_PANE_CHILD(ComposeText), True); { static Cursor compCursor = (Cursor) 0; @@ -2424,7 +2465,9 @@ sigfile, (newsgroup ? newsgroup->name : "NIL"), PostingModeStrings[PostingMode], - (Header.artFile ? Header.artFile : "NIL")); + (Header.artFile ? + file_cache_file_name(FileCache, Header.artFile) : + "NIL")); infofp = xrn_popen(cmdbuf, "r"); close_func = xrn_pclose; if (!infofp) { @@ -2488,7 +2531,8 @@ return; art = artStructGet(newsgroup, current, False); - Header.artFile = XtNewString(art->filename); + if (art->file) + file_cache_file_copy(FileCache, *art->file, &Header.artFile); if (followup) { if (! strcmp(Header.followupTo, "poster")) { @@ -2596,10 +2640,10 @@ if (! followup) { CHECK_SIZE(sizeof("X-Newsgroups: \nIn-reply-to: %s\n") - 1 + - strlen(Header.newsgroups) + strlen(Header.messageId)); + strlen(Header.newsgroups) + strlen(Header.id)); (void) sprintf(&message[strlen(message)], "X-Newsgroups: %s\nIn-reply-to: %s\n", - Header.newsgroups, Header.messageId); + Header.newsgroups, Header.id); } } @@ -2705,7 +2749,8 @@ return; art = artStructGet(newsgroup, current, False); - Header.artFile = XtNewString(art->filename); + if (art->file) + file_cache_file_copy(FileCache, *art->file, &Header.artFile); PostingMode = FORWARD; @@ -2888,8 +2933,10 @@ } -void post(ingroupp) - Boolean ingroupp; +void post( + _ANSIDECL(Boolean, ingroupp) + ) + _KNRDECL(Boolean, ingroupp) { post_or_mail(ingroupp, True, False); } @@ -2899,33 +2946,43 @@ post_or_mail(False, False, True); } -void post_and_mail(ingroupp) - Boolean ingroupp; +void post_and_mail( + _ANSIDECL(Boolean, ingroupp) + ) + _KNRDECL(Boolean, ingroupp) { post_or_mail(ingroupp, True, True); } - -static Boolean _canCancelArticle() +static Boolean addressMatches(art_line, user, host) + char *art_line, *user, *host; { - char buffer[BUFFER_SIZE], *bufptr; + char *at, *line_user, *line_host; - /* verify that the user can cancel the article */ - if ((bufptr = index(CancelHeader.from, '@'))) { - bufptr++; - (void) strcpy(buffer, bufptr); - if ((bufptr = index(buffer, ' '))) - *bufptr = '\0'; - if (strncmp(CancelHeader.host, buffer, utStrlen(CancelHeader.host)) - || (strncmp(CancelHeader.user, CancelHeader.from, - utStrlen(CancelHeader.user)) - && strcmp(CancelHeader.user, "root"))) - return False; - } + assert(art_line && user && host); - return True; + for (at = strchr(art_line, '@'); at; at = strchr(at + 1, '@')) { + line_user = at - 1; + while ((line_user > art_line) && + !isspace(*line_user) && (*line_user != '<')) + line_user--; + line_host = at + 1; + if (! ((strncmp(line_user, user, at - line_user) && + strncmp(line_user, "root", at - line_user)) || + strncmp(line_host, host, strlen(host)))) + return True; + } + return False; } + +static Boolean _canCancelArticle() +{ + return(addressMatches(CancelHeader.from, + CancelHeader.real_user, CancelHeader.real_host) || + addressMatches(CancelHeader.sender, + CancelHeader.real_user, CancelHeader.real_host)); +} Boolean canCancelArticle() { @@ -2956,6 +3013,7 @@ /* verify that the user can cancel the article */ if (! _canCancelArticle()) { freeHeader(&CancelHeader); + mesgPane(XRN_SERIOUS, mesg_name, USER_CANT_CANCEL_MSG); return; } @@ -2964,12 +3022,27 @@ message_size = 0; buildFrom(&CancelHeader, &message, &message_size, &message_total_size); +#ifndef INEWS + { + char *buf = 0; + int buf_size = 0, buf_total_size = 0; + + CHECK_SIZE_EXTENDED(buf, buf_size, buf_total_size, 1); + *buf = '\0'; + buf_size = 0; + + buildRealFrom(&CancelHeader, &buf, &buf_size, &buf_total_size); + if (strcmp(message, buf)) + buildSender(&CancelHeader, &message, &message_size, &message_total_size); + } +#endif /* ! INEWS */ + buildReplyTo(&message, &message_size, &message_total_size); buildPath(&CancelHeader, &message, &message_size, &message_total_size); - CHECK_SIZE(sizeof("Subject: cancel \n") - 1 + strlen(CancelHeader.messageId)); + CHECK_SIZE(sizeof("Subject: cancel \n") - 1 + strlen(CancelHeader.id)); (void) sprintf(&message[strlen(message)], "Subject: cancel %s\n", - CancelHeader.messageId); + CancelHeader.id); CHECK_SIZE(sizeof("Newsgroups: \n") - 1 + strlen(CancelHeader.newsgroups)); @@ -2984,11 +3057,9 @@ CancelHeader.distribution); } - CHECK_SIZE(sizeof("Control: cancel \n") - 1 + strlen(CancelHeader.messageId)); + CHECK_SIZE(sizeof("Control: cancel \n") - 1 + strlen(CancelHeader.id)); (void) sprintf(&message[strlen(message)], "Control: cancel %s\n", - CancelHeader.messageId); - - freeHeader(&CancelHeader); + CancelHeader.id); switch (postArticle(message, XRN_NEWS,&ErrMessage)) { case POST_FAILED: @@ -3008,13 +3079,108 @@ mesgPane(XRN_SERIOUS | XRN_SAME_LINE, mesg_name, CANCEL_ABORTED_MSG); break; - case POST_OKAY: - mesgPane(XRN_INFO, mesg_name, CANCELLED_ART_MSG); + case POST_OKAY: { + char *groups = XtNewString(CancelHeader.newsgroups); + unsigned long status = newsgroupsStatusUnion(groups); + + XtFree(groups); + + if (status & NG_MODERATED) + mesgPane(XRN_INFO, mesg_name, CANCEL_TO_MODERATOR_MSG); + else + mesgPane(XRN_INFO, mesg_name, CANCELLED_ART_MSG); + break; + } } + freeHeader(&CancelHeader); FREE(message); return; +} + +/* + Trim the "References:" field starting at the input string, so that + it is 512 characters or less long, including the field name and the + newline and null at the end. Do this by first reducing all + whitespace sequences to a single space, then trimming off any + garbage at the beginning of the line, then copying the first valid + message ID and trimming subsequent message ID's until the whole + thing will be short enough. + + Returns the new string length of the header, not including the final + null. + + Doesn't modify the input string if it's already short enough. + */ +static int trim_references(refs) + char *refs; +{ + char *outptr, *inptr; + int len; + + if ((len = strlen(refs)) < 512) + return len; + + inptr = outptr = refs; + while (*inptr) { + if (isspace(*inptr)) { + *outptr++ = ' '; + inptr++; + while (*inptr && isspace(*inptr)) { + inptr++; + len--; + } + } + else + *outptr++ = *inptr++; + } + + inptr = outptr = strchr(refs, ':') + 1; + if (*inptr && isspace(*inptr)) { + outptr++; + inptr++; + } + + /* Skip garbage at the beginning (e.g., because of a References: + line that was truncated at the beginning improperly by some other + software) */ + while (*inptr && *inptr != '<') { + inptr++; + len--; + } + if (! *inptr) + goto finished; + + /* Copy the first message ID */ + while (*inptr && (*inptr != '>')) + *outptr++ = *inptr++; + if (! *inptr) + goto finished; + /* Copy the final '>' and the space after it. */ + *outptr++ = *inptr++; + if (*inptr && isspace(*inptr)) + *outptr++ = *inptr++; + + while (len >= 512) { + char *newptr = strchr(inptr + 1, '<'); + if (newptr) { + len -= (newptr - inptr); + inptr = newptr; + continue; + } + else { + len -= strlen(inptr); + break; + } + } + + while (*inptr) + *outptr++ = *inptr++; + +finished: + *outptr = '\0'; + return len; } diff -u -d -r -N -P 8.02/compose.h 9.00/compose.h --- 8.02/compose.h Tue Sep 5 14:57:32 1995 +++ 9.00/compose.h Thu Jun 5 07:11:40 1997 @@ -4,7 +4,7 @@ #include "butdefs.h" /* - * $Id: compose.h,v 1.10 1995/09/05 18:57:22 jik Exp $ + * $Id: compose.h,v 1.11 1997/01/12 03:41:22 jik Exp $ */ /* @@ -36,9 +36,9 @@ * compose.h: functions for composing and sending messages */ -extern void post _ARGUMENTS((/* Boolean */ int ingroupp)); +extern void post _ARGUMENTS((Boolean ingroupp)); extern void mail _ARGUMENTS((void)); -extern void post_and_mail _ARGUMENTS((/* Boolean */ int ingroupp)); +extern void post_and_mail _ARGUMENTS((Boolean ingroupp)); extern void followup _ARGUMENTS((void)); extern void reply _ARGUMENTS((void)); extern void followup_and_reply _ARGUMENTS((void)); diff -u -d -r -N -P 8.02/config.h 9.00/config.h --- 8.02/config.h Thu May 2 08:28:10 1996 +++ 9.00/config.h Tue Dec 16 21:30:46 1997 @@ -2,7 +2,7 @@ #define CONFIG_H /* - * $Id: config.h,v 1.65 1996/05/02 12:27:50 jik Exp $ + * $Id: config.h,v 1.80 1997/12/17 02:30:46 jik Exp $ */ /* @@ -58,7 +58,23 @@ * from users at your site that you are capable of answering. */ #ifndef GRIPES -#define GRIPES "bug-xrn@cam.ov.com" +#define GRIPES "bug-xrn@kamens.brookline.ma.us" +#endif + +/* + If you are installing XRN on a multi-user system, on which it might + not be safe for users to put their NNTP passwords in their + .Xdefaults files, you should probably set ALLOW_RESOURCE_PASSWORDS + to 0, which will cause XRN to always prompt for the password. Set + it to 1 if you want to allow users to specify their passwords in X + resources or on the command line. + + I'm using 0 vs 1 rather then undefined vs. defined, so that people + who want to configure XRN without editing config.h can put the + correct definition in the Imakefile. + */ +#ifndef ALLOW_RESOURCE_PASSWORDS +#define ALLOW_RESOURCE_PASSWORDS 1 #endif /* @@ -102,7 +118,7 @@ * Name of the organization is in this file. Optional. If the file * exists and is non-epty, it overrides ORG_NAME. */ -/* #define ORG_FILE "/usr/local/lib/news/organization" */ +/* #define ORG_FILE "/usr/local/news/organization" */ /* * Name of the nntp server is in this file - can be overridden @@ -272,7 +288,7 @@ * of xrn you are using, to be included in outgoing postings and mail * messages, define this. */ -/* #define IDENTIFY_VERSION_IN_MESSAGES */ +#define IDENTIFY_VERSION_IN_MESSAGES /* * maximum size of a signature file (in bytes), if file is bigger than this, @@ -351,8 +367,6 @@ * flaw in X11R4's (and X11R3's) handling of application defaults file -- * there is no way for the programmer to suggest to the toolkit * where to look for the file without mucking with environment variables. - * - * (Jonathan I. Kamens ) */ /* #define XFILESEARCHPATH "path" */ @@ -397,11 +411,7 @@ * for each LIST command. If you are using 1.5.11 or greater, or INN * define NNTP_REREADS_ACTIVE_FILE */ - -#ifdef INN #define NNTP_REREADS_ACTIVE_FILE -#endif /* INN */ -/* #define NNTP_REREADS_ACTIVE_FILE */ /* * Do you want to use inews for postings? For INN, it is suggested that you do. @@ -423,24 +433,6 @@ */ /* #define DO_NOT_EAT_TYPE_AHEAD */ -/* - * display articles with local time rather than GMT - * if you are running SunOS 3.5, get rid of the '|| defined(sun)' - * define USE_LOCALTIME if you want to use this feature - */ - -/* #define USE_LOCALTIME */ - -#ifdef USE_LOCALTIME -# if defined(SYSV) || defined(ultrix) || defined(apollo) || defined(sun) || defined(linux) -#define REALLY_USE_LOCALTIME -#endif /* systems on which USE_LOCALTIME is supported */ -#ifndef linux /* I do not know what other systems support mktime() */ -#define NO_MKTIME -#endif -#endif - - #ifndef PRINTCOMMAND # ifdef SYSV # define PRINTCOMMAND "lp -sc" @@ -500,16 +492,13 @@ */ /* POSIX regex routines */ -#ifdef linux +#if defined(linux) || defined(hpux) || defined(__hpux) || defined(__osf__) #define POSIX_REGEX #endif /* SYSTEM V regex package */ -#if !defined(POSIX_REGEX) && !defined(__uxp__) && (defined(macII) || defined(aiws) || (defined(hpux) || defined(__hpux)) || (defined(SYSV) && !defined(i386) && !defined(_IBMR2)) || defined(SVR4) || defined(SCO)) +#if !defined(POSIX_REGEX) && !defined(__uxp__) && (defined(macII) || defined(aiws) || (defined(SYSV) && !defined(i386) && !defined(_IBMR2)) || defined(SVR4) || defined(SCO)) #define SYSV_REGEX -#ifndef SYSV -#define SYSV -#endif #endif #if defined(mips) || (defined(hpux) || defined(__hpux)) || defined(sun) || (defined(i386) && !defined(sequent)) || defined(_IBMR2) || defined(DGUX) || defined(__uxp__) || defined(SVR4) @@ -526,10 +515,23 @@ #define POPEN_USES_INEXPENSIVE_FORK #endif -/* bsd b* functions */ -#if defined(sequent) || !defined(SYSV) -#define BSD_BFUNCS -#endif +/* + BSD_BFUNCS means that you don't have memset(), memcpy(), or + memmove(). NO_MEMMOVE means that you have memset(), memcpy() and + bcopy(), but not memmove(). Don't define them both. + + If you are on a system which has memset(), memcpy() and bcopy(), but + not memmove(), and you have problems compiling because of the + BSD_BFUNCS definitions in utils.h, then try undefining BSD_BFUNCS + and defining NO_MEMMOVE instead. + */ +#if defined(sequent) || !(defined(SYSV) || defined(sun)) +# define BSD_BFUNCS +#else +# if defined(sun) && !defined(SOLARIS) +# define NO_MEMMOVE +# endif /* sun && !SOLARIS */ +#endif /* sequent || !(SYSV || sun) */ /* v{s,f}printf functions */ #if defined(sequent) || defined(ibm032) || (defined(sony) && !defined(SYSV) && !defined(_ANSI_C_SOURCE)) @@ -597,6 +599,7 @@ #define RETURN_HOST "cam.ov.com" #define SENDER_HOST "cam.ov.com" #define ORG_NAME "OpenVision Technologies, Inc." +#define SERVER_FILE "/tools/share/news/server" #endif #endif /* CONFIG_H */ diff -u -d -r -N -P 8.02/cursor.c 9.00/cursor.c --- 8.02/cursor.c Fri Oct 27 03:46:52 1995 +++ 9.00/cursor.c Sun Jun 29 22:53:17 1997 @@ -1,6 +1,6 @@ #if !defined(lint) && !defined(SABER) && !defined(GCC_WALL) -static char XRNrcsid[] = "$Id: cursor.c,v 1.27 1995/10/27 07:46:52 jik Exp $"; +static char XRNrcsid[] = "$Id: cursor.c,v 1.35 1997/06/30 02:53:17 jik Exp $"; #endif /* @@ -53,6 +53,7 @@ #include "mesg_strings.h" #include "Text.h" #include "buttons.h" +#include "file_cache.h" /* * Move the cursor to the beginning of the current line. @@ -335,10 +336,16 @@ * Mark a group of articles between left and right as read or unread. * Marks the articles in the text string, and marks them internally. */ -void markArticles(tstring, left, right, marker) - char *tstring; /* text string */ - long left, right; /* boundaries of articles to be marked */ - char marker; +void markArticles( + _ANSIDECL(char *, tstring), /* text string */ + _ANSIDECL(long, left), /* boundaries of */ + _ANSIDECL(long, right), /* articles to be marked */ + _ANSIDECL(char, marker) + ) + _KNRDECL(char *, tstring) + _KNRDECL(long, left) + _KNRDECL(long, right) + _KNRDECL(char, marker) { long artNum; /* number of current article to be marked */ @@ -369,54 +376,21 @@ (*newString)[last - first] = '\0'; } -/* - Move the cursor to the position of the article "num". "position" - is an input/output variable; it should contain the position to start - searching at when it is called, and will contain the new position - when the function returns. - */ -void findArticle(tstring, num, position) - char *tstring; /* text string */ - art_num num; /* article number to search for */ - long *position; /* cursor position */ -{ - long artNum; /* number of current article */ - long pos = *position + 1; - - /* move over S[aved] / P[rinted] marking */ - if ((tstring[pos] == SAVED_MARKER) || (tstring[pos] == PRINTED_MARKER)) { - pos++; - } - artNum = atol(&tstring[pos]); - while (artNum != num) { - if (!moveCursor(FORWARD, tstring, position)) { - ehErrorExitXRN( ERROR_FINDARTICLE_MSG ); - } - pos = *position + 1; - /* move over S[aved] / P[rinted] marking */ - if ((tstring[pos] == SAVED_MARKER) || (tstring[pos] == PRINTED_MARKER)) { - pos++; - } - artNum = atol(&tstring[pos]); - } - return; -} - - -int moveToArticle(artNum, file, ques) +int moveToArticle(newsgroup, artNum, file, ques) + struct newsgroup *newsgroup; long artNum; /* number of new article */ - char **file, **ques; /* filename and status line for new article */ + file_cache_file **file; /* cache file for new article */ + char **ques; /* status line for new article */ { - (void) fillUpArray(artNum, False); + (void) fillUpArray(newsgroup, artNum, 0, False, False); - if (checkArticle(artNum) != XRN_OKAY) { + if (checkArticle(artNum) != XRN_OKAY) return NOMATCH; - } - gotoArticle(artNum); - if (getArticle(file, ques) != XRN_OKAY) { + if (getArticle(newsgroup, artNum, file, ques) != XRN_OKAY) return ERROR; - } + + newsgroup->current = artNum; return MATCH; } diff -u -d -r -N -P 8.02/cursor.h 9.00/cursor.h --- 8.02/cursor.h Sun Feb 19 12:45:23 1995 +++ 9.00/cursor.h Sun Jun 29 22:53:14 1997 @@ -1,8 +1,11 @@ #ifndef CURSOR_H #define CURSOR_H +#include "news.h" +#include "file_cache.h" + /* - * $Id: cursor.h,v 1.10 1995/02/19 17:44:44 jik Exp $ + * $Id: cursor.h,v 1.14 1997/06/30 02:53:14 jik Exp $ */ /* @@ -50,11 +53,10 @@ extern void currentMode _ARGUMENTS((char *,char **,int *,long)); extern int markStringRead _ARGUMENTS((char *,long)); extern void markAllString _ARGUMENTS((char *,long, char *)); -extern void markArticles _ARGUMENTS((char *, long, long, - /* char */ int)); +extern void markArticles _ARGUMENTS((char *, long, long, char)); extern void buildString _ARGUMENTS((char **,long,long, char *)); -extern void findArticle _ARGUMENTS((char *,long,long *)); -extern int moveToArticle _ARGUMENTS((long, char **, char **)); +extern int moveToArticle _ARGUMENTS((struct newsgroup *, long, + file_cache_file **, char **)); #endif /* CURSOR_H */ diff -u -d -r -N -P 8.02/dialogs.c 9.00/dialogs.c --- 8.02/dialogs.c Thu May 2 04:26:45 1996 +++ 9.00/dialogs.c Thu Jun 5 07:11:41 1997 @@ -1,6 +1,6 @@ #if !defined(lint) && !defined(SABER) && !defined(GCC_WALL) -static char XRNrcsid[] = "$Id: dialogs.c,v 1.18 1996/05/02 08:26:24 jik Exp $"; +static char XRNrcsid[] = "$Id: dialogs.c,v 1.21 1997/04/07 02:07:46 jik Exp $"; /* Modified 2/20/92 dbrooks@osf.org to clean up dialog layout */ #endif @@ -44,6 +44,7 @@ #include #include +#include #include "xthelper.h" #include "xmisc.h" @@ -223,10 +224,18 @@ * Always returns XRN_CB_ABORT if X isn't up (according to the * XRN_X_UP bit in the XRNState variable). */ -int ConfirmationBox(parent, message, button1, button2, continue_first) - Widget parent; - char *message, *button1, *button2; - Boolean continue_first; +int ConfirmationBox( + _ANSIDECL(Widget, parent), + _ANSIDECL(char *, message), + _ANSIDECL(char *, button1), + _ANSIDECL(char *, button2), + _ANSIDECL(Boolean, continue_first) + ) + _KNRDECL(Widget, parent) + _KNRDECL(char *, message) + _KNRDECL(char *, button1) + _KNRDECL(char *, button2) + _KNRDECL(Boolean, continue_first) { int retval; @@ -291,11 +300,59 @@ for (;;) { XtAppNextEvent(app, &ev); - (void) XtDispatchEvent(&ev); + (void) MyDispatchEvent(&ev); if (retVal != -1) { PopDownDialog(widget); XtFree((char *) dialog_args); return(retVal); } } +} + +static int password_result; +static char *dialog_password; + +static void passwordHandler _ARGUMENTS((Widget, XtPointer, + XtPointer)); + +static void passwordHandler(widget, client_data, call_data) + Widget widget; + XtPointer client_data, call_data; +{ + Widget dialog = XtParent(XtParent(widget)); + + password_result = (int) client_data; + if (password_result == XRN_CB_CONTINUE) + dialog_password = XtNewString(GetDialogValue(dialog)); + PopDownDialog(dialog); + return; +} + +String PasswordBox(widget, prompt) + Widget widget; + String prompt; +{ + Widget dialog; + static struct DialogArg args[] = { + {ABORT_STRING, passwordHandler, (XtPointer) XRN_CB_ABORT}, + {DOIT_STRING, passwordHandler, (XtPointer) XRN_CB_CONTINUE}, + }; + + password_result = -1; + + dialog = CreateDialog(TopLevel, prompt, DIALOG_TEXT, args, XtNumber(args)); + XtVaSetValues(XtNameToWidget(dialog, "dialog.value"), XtNecho, 0, 0); + PopUpDialog(dialog); + + while (password_result < 0) { + XEvent ev; + + XtAppNextEvent(TopContext, &ev); + MyDispatchEvent(&ev); + } + + if (password_result == XRN_CB_CONTINUE) + return dialog_password; + else + return 0; } diff -u -d -r -N -P 8.02/dialogs.h 9.00/dialogs.h --- 8.02/dialogs.h Thu May 2 04:26:49 1996 +++ 9.00/dialogs.h Thu Jun 5 07:11:41 1997 @@ -2,7 +2,7 @@ #define DIALOGS_H /* - * $Id: dialogs.h,v 1.6 1996/05/02 08:26:29 jik Exp $ + * $Id: dialogs.h,v 1.8 1997/01/12 03:41:22 jik Exp $ */ /* @@ -55,10 +55,11 @@ extern char *GetDialogValue _ARGUMENTS((Widget)); -extern int ConfirmationBox _ARGUMENTS((Widget,char *, char *, char *, - /* Boolean */ int)); +extern int ConfirmationBox _ARGUMENTS((Widget,char *, char *, char *, Boolean)); extern int ChoiceBox _VARARGUMENTS((Widget, char *, int, ...)); +String PasswordBox _ARGUMENTS((Widget, String)); + #define XRN_CB_ABORT 0 #define XRN_CB_CONTINUE 1 diff -u -d -r -N -P 8.02/error_hnds.c 9.00/error_hnds.c --- 8.02/error_hnds.c Fri Oct 27 03:08:27 1995 +++ 9.00/error_hnds.c Sun Jun 29 22:52:41 1997 @@ -1,6 +1,6 @@ #if !defined(lint) && !defined(SABER) && !defined(GCC_WALL) -static char XRNrcsid[] = "$Id: error_hnds.c,v 1.15 1995/10/27 07:08:05 jik Exp $"; +static char XRNrcsid[] = "$Id: error_hnds.c,v 1.20 1997/06/30 02:52:41 jik Exp $"; #endif /* @@ -50,6 +50,8 @@ #include "error_hnds.h" #include "resources.h" #include "newsrcfile.h" +#include "file_cache.h" +#include "mesg_strings.h" /* @@ -230,7 +232,7 @@ while (!die) { XtAppNextEvent(TopContext, &ev); - XtDispatchEvent(&ev); + MyDispatchEvent(&ev); } return; @@ -266,7 +268,7 @@ while (!retry && !die) { XtAppNextEvent(TopContext, &ev); - XtDispatchEvent(&ev); + MyDispatchEvent(&ev); } PopDownDialog(dialog); @@ -285,33 +287,38 @@ int status; { static int beenHere = 0; + int do_exit = 0; /* * immediate exit, exitXRN was called as a result of something in * itself */ - if (beenHere == 1) { - exit(-1); + if (beenHere) { + do_exit++; } - - beenHere = 1; + else { + beenHere++; - if ((XRNState & XRN_NEWS_UP) == XRN_NEWS_UP) { + if ((XRNState & XRN_NEWS_UP) == XRN_NEWS_UP) { /* XXX is this really needed? does free files... */ releaseNewsgroupResources(CurrentGroup); cancelPrefetch(); + cancelRescanBackground(); + (void) file_cache_destroy(FileCache); + FileCache = 0; if (status != XRN_NORMAL_EXIT_BUT_NO_UPDATE) { - if (status == XRN_NORMAL_EXIT) { - while (!updatenewsrc()) { - ehErrorRetryXRN("Cannot update the newsrc file", True); - } - } else { - if (!updatenewsrc()) { - fprintf(stderr, "xrn: .newsrc file update failed\n"); - } + if (status == XRN_NORMAL_EXIT) { + while (!updatenewsrc()) { + ehErrorRetryXRN("Cannot update the newsrc file", True); } + } else { + if (!updatenewsrc()) { + fprintf(stderr, "xrn: .newsrc file update failed\n"); + } + } } + } } /* clean up the lock */ @@ -320,7 +327,8 @@ /* close down the NNTP server */ close_server(); - return; + if (do_exit) + exit(-1); } @@ -345,9 +353,12 @@ exit(-1); } -int ehErrorRetryXRN(message, save) - char *message; - Boolean save; +int ehErrorRetryXRN( + _ANSIDECL(char *, message), + _ANSIDECL(Boolean, save) + ) + _KNRDECL(char *, message) + _KNRDECL(Boolean, save) { int retry; diff -u -d -r -N -P 8.02/error_hnds.h 9.00/error_hnds.h --- 8.02/error_hnds.h Fri Mar 29 01:01:50 1996 +++ 9.00/error_hnds.h Thu Dec 18 08:04:04 1997 @@ -2,7 +2,7 @@ #define ERROR_HANDLERS_H /* - * $Id: error_hnds.h,v 1.8 1996/03/29 06:00:51 jik Exp $ + * $Id: error_hnds.h,v 1.10 1997/12/18 13:04:03 jik Exp $ */ /* @@ -39,7 +39,7 @@ extern void ehSignalExitXRN _ARGUMENTS((char *message)); extern void ehCleanExitXRN _ARGUMENTS((void)); extern void ehNoUpdateExitXRN _ARGUMENTS((void)); -extern int ehErrorRetryXRN _ARGUMENTS((char *message, /* Boolean */ int save)); +extern int ehErrorRetryXRN _ARGUMENTS((char *message, Boolean save)); #if XtSpecificationRelease > 5 extern void saveNewsrcCB _ARGUMENTS((Widget w, XtPointer client_d, @@ -54,7 +54,7 @@ /* install the signal handlers */ extern void ehInstallSignalHandlers _ARGUMENTS((void)); -#if !defined(__NetBSD__) && !defined(__FreeBSD__) +#if !defined(__GNU_LIBRARY__) && !defined(__NetBSD__) && !defined(__FreeBSD__) extern int errno, sys_nerr; extern char *sys_errlist[]; #endif diff -u -d -r -N -P 8.02/file_cache.c 9.00/file_cache.c --- 8.02/file_cache.c Wed Dec 31 19:00:00 1969 +++ 9.00/file_cache.c Sun Dec 28 11:22:45 1997 @@ -0,0 +1,532 @@ +/* + A file cache implementation. + */ + +#include + +#ifdef XRN + +#include "config.h" +#include "utils.h" +#include "error_hnds.h" +#define MALLOC(size) XtMalloc(size) +#define CALLOC(nelems, size) XtCalloc(nelems, size) +#define strerror errmsg + +#else /* ! XRN */ + +#include +#include +#include +#include +#define MALLOC(size) malloc(size) +#define CALLOC(nelems, size) calloc(nelems, size) +#define FREE(ptr) free(ptr), ptr = NULL +#define utTempnam tempnam +#define utTempnamFree free + +#ifndef _ARGUMENTS +#ifdef __STDC__ +#define _ARGUMENTS(a) a +#else +#define _ARGUMENTS(a) () +#endif /* __STDC __ */ + +#endif /* ! _ARGUMENTS */ + +#endif /* XRN else */ + +#include "file_cache.h" + +#define MAX_FILES 100 /* if not specified, use this as the maximum + number of files in the cache by default */ + +#define FILE_LOCKED (1<<0) +#define FILE_OPEN (1<<1) +#define FILE_VALID (1<<2) + +struct _file_cache_file { + char *name; + size_t size; + char flags; + file_cache_file *self_ref; +}; + +struct _file_cache { + char *dir, *prefix; + file_cache_file files; + int current; + int num_files; + size_t max_bytes; + size_t current_bytes; +} file_cache_struct; + +static char *file_cache_name_get _ARGUMENTS((file_cache)); + + + +/* + Create a new file cache. max_files specifies the maximum number of + files in the cache at any given time; max_bytes specifies the + maximum amount of space taken up by the files in the cache. If + max_files is not specified (i.e., it's 0), it defaults to MAX_FILES; + if max_bytes is not specified, it defaults to unlimited size. + + Returns NULL on failure (out of memory) or a cache handle on + success. Can't return NULL when XRN is defined. + */ + +file_cache file_cache_create(dir, prefix, max_files, max_bytes) + char *dir, *prefix; + int max_files; + size_t max_bytes; +{ + file_cache cache; + + if (! (cache = (file_cache) MALLOC(sizeof *cache))) { +#ifdef DEBUG + fprintf(stderr, + "file_cache_create: couldn't allocate space for cache structure\n"); +#endif + return NULL; + } + + if (! max_files) + max_files = MAX_FILES; + + cache->dir = dir; + cache->prefix = prefix; + + if (! (cache->files = (file_cache_file) CALLOC(max_files, + sizeof(*cache->files)))) { +#ifdef DEBUG + fprintf(stderr, "file_cache_create: couldn't allocate space for %d files", + max_files); +#endif + FREE(cache); + return NULL; + } + + cache->current = 0; + cache->num_files = max_files; + cache->max_bytes = max_bytes; + cache->current_bytes = 0; + +#ifdef DEBUG + fprintf(stderr, + "file_cache_create: created cache 0x%x with %d files, max_bytes %d\n", + (unsigned) cache, cache->num_files, cache->max_bytes); +#endif + + return cache; +} + +/* + Destroy a file cache and all the files stored in it. Returns true + on success, or false if any of the files in the cache were in use + (in which case the cache and the files that were in use aren't + destroyed). + */ + +int file_cache_destroy(cache) + file_cache cache; +{ + int i, in_use = 0; + + assert(cache); + + for (i = 0; i < cache->num_files; i++) { + if (! (cache->files[i].flags & FILE_VALID)) + continue; + if (cache->files[i].flags & (FILE_LOCKED|FILE_OPEN)) { + in_use++; + continue; + } + file_cache_file_destroy(cache, &cache->files[i]); + } + + if (! in_use) { +#ifdef DEBUG + fprintf(stderr, "file_cache_destroy: destroyed cache 0x%x\n", + (unsigned) cache); +#endif + FREE(cache->files); + FREE(cache); + } + else { +#ifdef DEBUG + fprintf(stderr, "file_cache_destroy: %d files in use after destroy\n", + in_use); +#endif + } + + return(! in_use); +} + +/* + Create a new file in the cache, returning a file handle to the file + opened for writing. Puts the handle to the cache entry into "file". + Returns NULL if out of memory, or if out of disk space and couldn't + free up space by deleting old cache entries, or if all files in the + cache are locked or open. + */ + +FILE *file_cache_file_open(cache, file) + file_cache cache; + file_cache_file *file; +{ + int i; + file_cache_file which +#ifdef GCC_WALL + = NULL +#endif + ; + FILE *fp; + size_t space_needed = 1; + + assert(cache); + + for (i = cache->current; i < cache->current + cache->num_files; i++) { + which = &cache->files[i % cache->num_files]; + if ((which->flags & FILE_VALID) && + (which->flags & (FILE_LOCKED|FILE_OPEN))) + continue; + break; + } + + if (i == cache->current + cache->num_files) { +#ifdef DEBUG + fprintf(stderr, "file_cache_file_open: no free slots!\n"); +#endif + return NULL; + } + + if (which->flags & FILE_VALID) + file_cache_file_destroy(cache, which); + + if (! (which->name = file_cache_name_get(cache))) { +#ifdef DEBUG + fprintf(stderr, "file_cache_file_open: file_cache_name_get failed!\n"); +#endif + return NULL; + } + + which->size = 0; + + while ((! (fp = fopen(which->name, "w+"))) && + file_cache_free_space(cache, space_needed)) + space_needed *= 2; + + if (! fp) { +#ifdef DEBUG + fprintf(stderr, "file_cache_file_open: couldn't create file %s: %s\n", + which->name, strerror(errno)); +#endif + utTempnamFree(which->name); + return NULL; + } + + which->flags = FILE_VALID|FILE_OPEN; + which->self_ref = file; + + *file = which; + +#ifdef DEBUG + fprintf(stderr, "file_cache_file_open: created file %s in slot %d (0x%x)\n", + which->name, which - cache->files, (unsigned) which); +#endif + + cache->current = (i + 1) % cache->num_files; + + return fp; +} + +/* + Close a file, add its size to the total cache size, lock it, and + free up old cache files as necessary to bring the cache below its + maximum size. + + The file handle should have already been closed with fclose() before + this function was called. + + Returns true on success, or false on fatal error. + */ +int file_cache_file_close(cache, file) + file_cache cache; + file_cache_file file; +{ + struct stat sb; + + assert(cache); + + file->flags &= ~FILE_OPEN; + file->flags |= FILE_LOCKED; + + if (stat(file->name, &sb) < 0) { +#ifdef DEBUG + fprintf(stderr, "file_cache_file_close: couldn't stat %s: %s\n", + file->name, strerror(errno)); +#endif + return 0; + } + + cache->current_bytes += (file->size = sb.st_size); + +#ifdef DEBUG + fprintf(stderr, + "file_cache_file_close: closed file %s in slot %d (0x%x), size %d\n", + file->name, file - cache->files, (unsigned) file, file->size); +#endif + + (void) file_cache_free_space(cache, 0); + + return 1; +} + +/* + Destroy a file in the cache. Make sure to close it with fclose() + before calling this. + */ +void file_cache_file_destroy(cache, file) + file_cache cache; + file_cache_file file; +{ + assert(cache); + + if (! file) { +#ifdef DEBUG + fprintf(stderr, "file_cache_file_destroy: null file\n"); +#endif + return; + } + +#ifdef DEBUG + fprintf(stderr, "file_cache_file_destroy: destroying file %s " + "in slot %d (0x%x), size %d\n", + file->name, file - cache->files, (unsigned) file, file->size); +#endif + + file_cache_file_release(cache, file); + (void) unlink(file->name); + utTempnamFree(file->name); + cache->current_bytes -= file->size; + file->flags = 0; +} + +/* + Release a file in the cache. Make sure to close it with fclose() + before calling this. After a file in the cache is released, the + caller shouldn't use it anymore, and the file_class package will no + longer try to reference the memory its handle was stored in + (although it will erase it as part of releasing the handle). A + released file isn't necessary deleted until its space is needed. + */ +void file_cache_file_release(cache, file) + file_cache cache; + file_cache_file file; +{ + assert(cache); + + if (! file) { +#ifdef DEBUG + fprintf(stderr, "file_cache_file_release: null file\n"); +#endif + return; + } + +#ifdef DEBUG + fprintf(stderr, + "file_cache_file_release: releasing file %s in slot %d (0x%x), size %d\n", + file->name, file - cache->files, (unsigned) file, file->size); +#endif + + file->flags &= ~(FILE_OPEN|FILE_LOCKED); + if (file->self_ref) { + *file->self_ref = NULL; + file->self_ref = NULL; + } +} + +/* + Lock a file in the cache. + */ +void file_cache_file_lock(cache, file) + file_cache cache; + file_cache_file file; +{ +#ifdef DEBUG + char old_flags = file->flags; +#endif + + assert(cache); + + file->flags |= FILE_LOCKED; + +#ifdef DEBUG + fprintf(stderr, + "file_cache_file_lock: locked file %s in slot %d (0x%x), " + "old flags 0x%x, new 0x%x\n", + file->name, file - cache->files, (unsigned) file, old_flags, file->flags); +#endif +} + +/* + Unlock a file in the cache. + + When a file is closed and unlocked, it may be removed from the cache + at any point, in which case the contents of the file_cache_file + pointer passed into file_cache_open will be nulled to tell the + caller that the cache file has been removed. + */ +void file_cache_file_unlock(cache, file) + file_cache cache; + file_cache_file file; +{ +#ifdef DEBUG + char old_flags = file->flags; +#endif + + assert(cache); + + file->flags &= ~FILE_LOCKED; + +#ifdef DEBUG + fprintf(stderr, "file_cache_file_unlock: unlocked file %s in slot %d (0x%x), " + "old flags 0x%x, new 0x%x\n", + file->name, file - cache->files, (unsigned) file, old_flags, file->flags); +#endif +} + +/* + Get the name of a file in the cache. + */ +char *file_cache_file_name(cache, file) + file_cache cache; + file_cache_file file; +{ + assert(cache); + + return file->name; +} + +/* + If possible, free up at least space_needed bytes of space in the + cache, and at the same time bring the amount of space taken up by + the cache below its maximum size if it's currently above it. + Returns true if any space at all was freed, or false if none + could be freed. + */ +int file_cache_free_space(cache, space_needed) + file_cache cache; + size_t space_needed; +{ + int i; + size_t space_freed = 0; + + assert(cache); + + for (i = (cache->current + cache->num_files - 1) % cache->num_files; + i != cache->current; i = (i + cache->num_files - 1) % cache->num_files) { + if ((!cache->max_bytes || cache->current_bytes <= cache->max_bytes) && + (space_freed >= space_needed)) + break; + if (! (cache->files[i].flags & FILE_VALID)) + continue; + if (cache->files[i].flags & (FILE_OPEN|FILE_LOCKED)) + continue; + space_freed += cache->files[i].size; +#ifdef DEBUG + fprintf(stderr, "file_cache_free_space: need %d, freed %d, over by %d\n", + space_needed, space_freed, cache->max_bytes ? + (cache->current_bytes - cache->max_bytes) : 0); +#endif + file_cache_file_destroy(cache, &cache->files[i]); + } + + return space_freed ? 1 : 0; +} + +/* + Copy a file in the file cache to another file, so that the second + one will be preserved even if the first one is destroyed. + */ +void file_cache_file_copy(cache, file, new_file) + file_cache cache; + file_cache_file file; + file_cache_file *new_file; +{ + FILE *fp; + char *old_name, *new_name; + char old_flags; + + /* This function doesn't copy the "size" field from the old cache + file to the new one because that would cause the space taken up by + the file in the cache to be counted twice; in fact, a hard-linked + file only takes up space once. + */ + + assert(cache); + + if (! (file && (old_name = file->name))) { +#ifdef DEBUG + fprintf(stderr, "file_cache_file_copy: attempt to copy null file!\n"); +#endif + *new_file = NULL; + return; + } + + old_flags = file->flags; + + file->flags |= FILE_LOCKED; + + if (! (fp = file_cache_file_open(cache, new_file))) { + file->flags = old_flags; + *new_file = NULL; + return; + } + + file->flags = old_flags; + + new_name = (*new_file)->name; + + (void) fclose(fp); + (void) unlink(new_name); + + if (link(old_name, new_name) < 0) { + file_cache_file_destroy(cache, *new_file); + *new_file = NULL; + return; + } + + if (! file_cache_file_close(cache, *new_file)) { + file_cache_file_destroy(cache, *new_file); + *new_file = NULL; + return; + } + +#ifdef DEBUG + fprintf(stderr, "file_cache_file_copy: copied file %s in slot %d (0x%x) " + "into file %s in slot %d (0x%x)\n", + file->name, file - cache->files, (unsigned) file, + *new_file->name, *new_file - cache->files, (unsigned) *new_file); +#endif + + return; +} + +char *file_cache_dir_get(cache) + file_cache cache; +{ + assert(cache); + + return(cache->dir); +} + + +static char *file_cache_name_get(cache) + file_cache cache; +{ + assert(cache); + + return utTempnam(cache->dir, cache->prefix); +} diff -u -d -r -N -P 8.02/file_cache.h 9.00/file_cache.h --- 8.02/file_cache.h Wed Dec 31 19:00:00 1969 +++ 9.00/file_cache.h Sun Dec 28 11:22:03 1997 @@ -0,0 +1,22 @@ +#ifndef _FILE_CACHE_H_ +#define _FILE_CACHE_H_ + +typedef struct _file_cache *file_cache; +typedef struct _file_cache_file *file_cache_file; + +extern file_cache file_cache_create _ARGUMENTS((char *, char *, int, size_t)); +extern int file_cache_destroy _ARGUMENTS((file_cache)); +extern char *file_cache_dir_get _ARGUMENTS((file_cache)); +extern FILE * file_cache_file_open _ARGUMENTS((file_cache, file_cache_file *)); +extern int file_cache_file_close _ARGUMENTS((file_cache, file_cache_file)); +extern void file_cache_file_destroy _ARGUMENTS((file_cache, file_cache_file)); +extern void file_cache_file_release _ARGUMENTS((file_cache, file_cache_file)); +extern void file_cache_file_lock _ARGUMENTS((file_cache, file_cache_file)); +extern void file_cache_file_unlock _ARGUMENTS((file_cache, file_cache_file)); +extern void file_cache_file_copy _ARGUMENTS((file_cache, file_cache_file, + file_cache_file *)); +extern char *file_cache_file_name _ARGUMENTS((file_cache, file_cache_file)); +extern int file_cache_free_space _ARGUMENTS((file_cache, size_t)); + +#endif /* _FILE_CACHE_H_ */ + diff -u -d -r -N -P 8.02/getdate.h 9.00/getdate.h --- 8.02/getdate.h Wed Dec 31 19:00:00 1969 +++ 9.00/getdate.h Thu Jun 5 07:11:41 1997 @@ -0,0 +1 @@ +time_t get_date _ARGUMENTS((char *)); diff -u -d -r -N -P 8.02/getdate.y 9.00/getdate.y --- 8.02/getdate.y Wed Dec 31 19:00:00 1969 +++ 9.00/getdate.y Thu Jun 5 07:11:41 1997 @@ -0,0 +1,898 @@ +%{ +/* +** Originally written by Steven M. Bellovin while +** at the University of North Carolina at Chapel Hill. Later tweaked by +** a couple of people on Usenet. Completely overhauled by Rich $alz +** and Jim Berets in August, 1990; +** send any email to Rich. +** +** This grammar has nine shift/reduce conflicts. +** +** This code is in the public domain and has no copyright. +*/ +/* SUPPRESS 287 on yaccpar_sccsid *//* Unusd static variable */ +/* SUPPRESS 288 on yyerrlab *//* Label unused */ + +#include +#include +#include +#include + +#include "config.h" +#include "utils.h" +#include "getdate.h" + +static int yylex (); +static int yyerror (); + + +#define EPOCH 1970 +#define HOUR(x) ((time_t)((x) * 60)) +#define SECSPERDAY (24L * 60L * 60L) + + +/* +** An entry in the lexical lookup table. +*/ +typedef struct _TABLE { + char *name; + int type; + time_t value; +} TABLE; + + +/* +** Daylight-savings mode: on, off, or not yet known. +*/ +typedef enum _DSTMODE { + DSTon, DSToff, DSTmaybe +} DSTMODE; + +/* +** Meridian: am, pm, or 24-hour style. +*/ +typedef enum _MERIDIAN { + MERam, MERpm, MER24 +} MERIDIAN; + + +/* +** Global variables. We could get rid of most of these by using a good +** union as the yacc stack. (This routine was originally written before +** yacc had the %union construct.) Maybe someday; right now we only use +** the %union very rarely. +*/ +static char *yyInput; +static DSTMODE yyDSTmode; +static time_t yyDayOrdinal; +static time_t yyDayNumber; +static int yyHaveDate; +static int yyHaveDay; +static int yyHaveRel; +static int yyHaveTime; +static int yyHaveZone; +static time_t yyTimezone; +static time_t yyDay; +static time_t yyHour; +static time_t yyMinutes; +static time_t yyMonth; +static time_t yySeconds; +static time_t yyYear; +static MERIDIAN yyMeridian; +static time_t yyRelMonth; +static time_t yyRelSeconds; + +%} + +%union { + time_t Number; + enum _MERIDIAN Meridian; +} + +%token tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT +%token tSEC_UNIT tSNUMBER tUNUMBER tZONE tDST + +%type tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT +%type tSEC_UNIT tSNUMBER tUNUMBER tZONE +%type tMERIDIAN o_merid + +%% + +spec : /* NULL */ + | spec item + ; + +item : time { + yyHaveTime++; + } + | zone { + yyHaveZone++; + } + | date { + yyHaveDate++; + } + | day { + yyHaveDay++; + } + | rel { + yyHaveRel++; + } + | number + ; + +time : tUNUMBER tMERIDIAN { + yyHour = $1; + yyMinutes = 0; + yySeconds = 0; + yyMeridian = $2; + } + | tUNUMBER ':' tUNUMBER o_merid { + yyHour = $1; + yyMinutes = $3; + yySeconds = 0; + yyMeridian = $4; + } + | tUNUMBER ':' tUNUMBER tSNUMBER { + yyHour = $1; + yyMinutes = $3; + yyMeridian = MER24; + yyDSTmode = DSToff; + yyTimezone = - ($4 % 100 + ($4 / 100) * 60); + } + | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid { + yyHour = $1; + yyMinutes = $3; + yySeconds = $5; + yyMeridian = $6; + } + | tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER { + yyHour = $1; + yyMinutes = $3; + yySeconds = $5; + yyMeridian = MER24; + yyDSTmode = DSToff; + yyTimezone = - ($6 % 100 + ($6 / 100) * 60); + } + ; + +zone : tZONE { + yyTimezone = $1; + yyDSTmode = DSToff; + } + | tDAYZONE { + yyTimezone = $1; + yyDSTmode = DSTon; + } + | + tZONE tDST { + yyTimezone = $1; + yyDSTmode = DSTon; + } + /* GMTh, GMThh, GMT[+-]h, GMT[+-]hh, GMT[+-]hhmm */ + | + tZONE tUNUMBER { + yyTimezone = $1 + HOUR($2); + yyDSTmode = DSToff; + } + | + tDAYZONE tUNUMBER { + yyTimezone = $1 + HOUR($2); + yyDSTmode = DSTon; + } + | + tZONE tSNUMBER { + if ($2 >= 100) + yyTimezone = $1 + HOUR((int)($2/100)) + ($2 % 100); + else + yyTimezone = $1 + HOUR($2); + yyDSTmode = DSToff; + } + | + tDAYZONE tSNUMBER { + if ($2 >= 100) + yyTimezone = $1 + HOUR((int)($2/100)) + ($2 % 100); + else + yyTimezone = $1 + HOUR($2); + yyDSTmode = DSTon; + } + ; + +day : tDAY { + yyDayOrdinal = 1; + yyDayNumber = $1; + } + | tDAY ',' { + yyDayOrdinal = 1; + yyDayNumber = $1; + } + | tUNUMBER tDAY { + yyDayOrdinal = $1; + yyDayNumber = $2; + } + ; + +date : tUNUMBER '/' tUNUMBER { + yyMonth = $1; + yyDay = $3; + } + | tUNUMBER '/' tUNUMBER '/' tUNUMBER { + yyMonth = $1; + yyDay = $3; + yyYear = $5; + } + | tUNUMBER tSNUMBER tSNUMBER { + /* ISO 8601 format. yyyy-mm-dd. */ + yyYear = $1; + yyMonth = -$2; + yyDay = -$3; + } + | tUNUMBER tMONTH tSNUMBER { + /* e.g. 17-JUN-1992. */ + yyDay = $1; + yyMonth = $2; + yyYear = -$3; + } + | tMONTH tUNUMBER { + yyMonth = $1; + yyDay = $2; + } + | tMONTH tUNUMBER ',' tUNUMBER { + yyMonth = $1; + yyDay = $2; + yyYear = $4; + } + | tUNUMBER tMONTH { + yyMonth = $2; + yyDay = $1; + } + | tUNUMBER tMONTH tUNUMBER { + yyMonth = $2; + yyDay = $1; + yyYear = $3; + } + ; + +rel : relunit tAGO { + yyRelSeconds = -yyRelSeconds; + yyRelMonth = -yyRelMonth; + } + | relunit + ; + +relunit : tUNUMBER tMINUTE_UNIT { + yyRelSeconds += $1 * $2 * 60L; + } + | tSNUMBER tMINUTE_UNIT { + yyRelSeconds += $1 * $2 * 60L; + } + | tMINUTE_UNIT { + yyRelSeconds += $1 * 60L; + } + | tSNUMBER tSEC_UNIT { + yyRelSeconds += $1; + } + | tUNUMBER tSEC_UNIT { + yyRelSeconds += $1; + } + | tSEC_UNIT { + yyRelSeconds++; + } + | tSNUMBER tMONTH_UNIT { + yyRelMonth += $1 * $2; + } + | tUNUMBER tMONTH_UNIT { + yyRelMonth += $1 * $2; + } + | tMONTH_UNIT { + yyRelMonth += $1; + } + ; + +number : tUNUMBER { + if (yyHaveTime && yyHaveDate && !yyHaveRel) + yyYear = $1; + else { + if($1>10000) { + yyHaveDate++; + yyDay= ($1)%100; + yyMonth= ($1/100)%100; + yyYear = $1/10000; + } + else { + yyHaveTime++; + if ($1 < 100) { + yyHour = $1; + yyMinutes = 0; + } + else { + yyHour = $1 / 100; + yyMinutes = $1 % 100; + } + yySeconds = 0; + yyMeridian = MER24; + } + } + } + ; + +o_merid : /* NULL */ { + $$ = MER24; + } + | tMERIDIAN { + $$ = $1; + } + ; + +%% + +/* Month and day table. */ +static TABLE CONST MonthDayTable[] = { + { "january", tMONTH, 1 }, + { "february", tMONTH, 2 }, + { "march", tMONTH, 3 }, + { "april", tMONTH, 4 }, + { "may", tMONTH, 5 }, + { "june", tMONTH, 6 }, + { "july", tMONTH, 7 }, + { "august", tMONTH, 8 }, + { "september", tMONTH, 9 }, + { "sept", tMONTH, 9 }, + { "october", tMONTH, 10 }, + { "november", tMONTH, 11 }, + { "december", tMONTH, 12 }, + { "sunday", tDAY, 0 }, + { "monday", tDAY, 1 }, + { "tuesday", tDAY, 2 }, + { "tues", tDAY, 2 }, + { "wednesday", tDAY, 3 }, + { "wednes", tDAY, 3 }, + { "thursday", tDAY, 4 }, + { "thur", tDAY, 4 }, + { "thurs", tDAY, 4 }, + { "friday", tDAY, 5 }, + { "saturday", tDAY, 6 }, + { NULL } +}; + +/* Time units table. */ +static TABLE CONST UnitsTable[] = { + { "year", tMONTH_UNIT, 12 }, + { "month", tMONTH_UNIT, 1 }, + { "fortnight", tMINUTE_UNIT, 14 * 24 * 60 }, + { "week", tMINUTE_UNIT, 7 * 24 * 60 }, + { "day", tMINUTE_UNIT, 1 * 24 * 60 }, + { "hour", tMINUTE_UNIT, 60 }, + { "minute", tMINUTE_UNIT, 1 }, + { "min", tMINUTE_UNIT, 1 }, + { "second", tSEC_UNIT, 1 }, + { "sec", tSEC_UNIT, 1 }, + { NULL } +}; + +/* Assorted relative-time words. */ +static TABLE CONST OtherTable[] = { + { "tomorrow", tMINUTE_UNIT, 1 * 24 * 60 }, + { "yesterday", tMINUTE_UNIT, -1 * 24 * 60 }, + { "today", tMINUTE_UNIT, 0 }, + { "now", tMINUTE_UNIT, 0 }, + { "last", tUNUMBER, -1 }, + { "this", tMINUTE_UNIT, 0 }, + { "next", tUNUMBER, 2 }, + { "first", tUNUMBER, 1 }, +/* { "second", tUNUMBER, 2 }, */ + { "third", tUNUMBER, 3 }, + { "fourth", tUNUMBER, 4 }, + { "fifth", tUNUMBER, 5 }, + { "sixth", tUNUMBER, 6 }, + { "seventh", tUNUMBER, 7 }, + { "eighth", tUNUMBER, 8 }, + { "ninth", tUNUMBER, 9 }, + { "tenth", tUNUMBER, 10 }, + { "eleventh", tUNUMBER, 11 }, + { "twelfth", tUNUMBER, 12 }, + { "ago", tAGO, 1 }, + { NULL } +}; + +/* The timezone table. */ +/* Some of these are commented out because a time_t can't store a float. */ +static TABLE CONST TimezoneTable[] = { +#ifdef XRN + /* This is really gross. There are some misconfigured News readers + which put "LOCAL" or "UNDEFINED" as the timezone in their Date: + fields. Rather than displaying an error, I'm going to have XRN + just sort and display those articles in GMT. */ + { "local", tZONE, HOUR( 0) }, /* Greenwich Mean */ + { "undefined", tZONE, HOUR( 0) }, /* Greenwich Mean */ +#endif /* XRN */ + { "gmt", tZONE, HOUR( 0) }, /* Greenwich Mean */ + { "ut", tZONE, HOUR( 0) }, /* Universal (Coordinated) */ + { "utc", tZONE, HOUR( 0) }, + { "wet", tZONE, HOUR( 0) }, /* Western European */ + { "bst", tDAYZONE, HOUR( 0) }, /* British Summer */ + { "wat", tZONE, HOUR( 1) }, /* West Africa */ + { "at", tZONE, HOUR( 2) }, /* Azores */ +#if 0 + /* For completeness. BST is also British Summer, and GST is + * also Guam Standard. */ + { "bst", tZONE, HOUR( 3) }, /* Brazil Standard */ + { "gst", tZONE, HOUR( 3) }, /* Greenland Standard */ +#endif + { "nft", tZONE, HOUR(3.5) }, /* Newfoundland */ + { "nst", tZONE, HOUR(3.5) }, /* Newfoundland Standard */ + { "ndt", tDAYZONE, HOUR(3.5) }, /* Newfoundland Daylight */ + { "ast", tZONE, HOUR( 4) }, /* Atlantic Standard */ + { "adt", tDAYZONE, HOUR( 4) }, /* Atlantic Daylight */ + { "est", tZONE, HOUR( 5) }, /* Eastern Standard */ + { "edt", tDAYZONE, HOUR( 5) }, /* Eastern Daylight */ + { "cst", tZONE, HOUR( 6) }, /* Central Standard */ + { "cdt", tDAYZONE, HOUR( 6) }, /* Central Daylight */ + { "mst", tZONE, HOUR( 7) }, /* Mountain Standard */ + { "mdt", tDAYZONE, HOUR( 7) }, /* Mountain Daylight */ + { "pst", tZONE, HOUR( 8) }, /* Pacific Standard */ + { "pdt", tDAYZONE, HOUR( 8) }, /* Pacific Daylight */ + { "yst", tZONE, HOUR( 9) }, /* Yukon Standard */ + { "ydt", tDAYZONE, HOUR( 9) }, /* Yukon Daylight */ + { "hst", tZONE, HOUR(10) }, /* Hawaii Standard */ + { "hdt", tDAYZONE, HOUR(10) }, /* Hawaii Daylight */ + { "cat", tZONE, HOUR(10) }, /* Central Alaska */ + { "ahst", tZONE, HOUR(10) }, /* Alaska-Hawaii Standard */ + { "nt", tZONE, HOUR(11) }, /* Nome */ + { "idlw", tZONE, HOUR(12) }, /* International Date Line West */ + { "cet", tZONE, -HOUR(1) }, /* Central European Standard */ + { "mez", tZONE, -HOUR(1) }, /* deprecated version of cet */ + { "ced", tDAYZONE, -HOUR(1) }, /* Central European Daylight */ + { "met", tZONE, -HOUR(1) }, /* Middle European */ + { "mewt", tZONE, -HOUR(1) }, /* Middle European Winter */ + { "mest", tDAYZONE, -HOUR(1) }, /* Middle European Summer */ + { "swt", tZONE, -HOUR(1) }, /* Swedish Winter */ + { "sst", tDAYZONE, -HOUR(1) }, /* Swedish Summer */ + { "fwt", tZONE, -HOUR(1) }, /* French Winter */ + { "fst", tDAYZONE, -HOUR(1) }, /* French Summer */ + { "eet", tZONE, -HOUR(2) }, /* Eastern Europe, USSR Zone 1 */ + { "bt", tZONE, -HOUR(3) }, /* Baghdad, USSR Zone 2 */ + { "it", tZONE, -HOUR(3.5) },/* Iran */ + { "zp4", tZONE, -HOUR(4) }, /* USSR Zone 3 */ + { "zp5", tZONE, -HOUR(5) }, /* USSR Zone 4 */ + { "ist", tZONE, -HOUR(5.5) },/* Indian Standard */ + { "zp6", tZONE, -HOUR(6) }, /* USSR Zone 5 */ +#if 0 + /* For completeness. NST is also Newfoundland Stanard, and SST is + * also Swedish Summer. */ + { "nst", tZONE, -HOUR(6.5) },/* North Sumatra */ + { "sst", tZONE, -HOUR(7) }, /* South Sumatra, USSR Zone 6 */ +#endif /* 0 */ + { "wast", tZONE, -HOUR(7) }, /* West Australian Standard */ + { "wadt", tDAYZONE, -HOUR(7) }, /* West Australian Daylight */ + { "jt", tZONE, -HOUR(7.5) },/* Java (3pm in Cronusland!) */ + { "wst", tZONE, -HOUR(8) }, /* Australia Western timezone */ + { "cct", tZONE, -HOUR(8) }, /* China Coast, USSR Zone 7 */ + { "jst", tZONE, -HOUR(9) }, /* Japan Standard, USSR Zone 8 */ + { "kst", tZONE, -HOUR(9) }, /* Korean Standard */ + { "cast", tZONE, -HOUR(9.5) },/* Central Australian Standard */ + { "cadt", tDAYZONE, -HOUR(9.5) },/* Central Australian Daylight */ + { "east", tZONE, -HOUR(10) }, /* Eastern Australian Standard */ + { "eadt", tDAYZONE, -HOUR(10) }, /* Eastern Australian Daylight */ + { "gst", tZONE, -HOUR(10) }, /* Guam Standard, USSR Zone 9 */ + { "kdt", tZONE, -HOUR(10) }, /* Korean Daylight */ + { "nzt", tZONE, -HOUR(12) }, /* New Zealand */ + { "nzst", tZONE, -HOUR(12) }, /* New Zealand Standard */ + { "nzdt", tDAYZONE, -HOUR(12) }, /* New Zealand Daylight */ + { "idle", tZONE, -HOUR(12) }, /* International Date Line East */ + { NULL } +}; + +/* Military timezone table. */ +static TABLE CONST MilitaryTable[] = { + { "a", tZONE, HOUR( 1) }, + { "b", tZONE, HOUR( 2) }, + { "c", tZONE, HOUR( 3) }, + { "d", tZONE, HOUR( 4) }, + { "e", tZONE, HOUR( 5) }, + { "f", tZONE, HOUR( 6) }, + { "g", tZONE, HOUR( 7) }, + { "h", tZONE, HOUR( 8) }, + { "i", tZONE, HOUR( 9) }, + { "k", tZONE, HOUR( 10) }, + { "l", tZONE, HOUR( 11) }, + { "m", tZONE, HOUR( 12) }, + { "n", tZONE, HOUR(- 1) }, + { "o", tZONE, HOUR(- 2) }, + { "p", tZONE, HOUR(- 3) }, + { "q", tZONE, HOUR(- 4) }, + { "r", tZONE, HOUR(- 5) }, + { "s", tZONE, HOUR(- 6) }, + { "t", tZONE, HOUR(- 7) }, + { "u", tZONE, HOUR(- 8) }, + { "v", tZONE, HOUR(- 9) }, + { "w", tZONE, HOUR(-10) }, + { "x", tZONE, HOUR(-11) }, + { "y", tZONE, HOUR(-12) }, + { "z", tZONE, HOUR( 0) }, + { NULL } +}; + + + + +/* ARGSUSED */ +static int +yyerror(s) + char *s; +{ + return 0; +} + + +static time_t +ToSeconds(Hours, Minutes, Seconds, Meridian) + time_t Hours; + time_t Minutes; + time_t Seconds; + MERIDIAN Meridian; +{ + if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59) + return -1; + switch (Meridian) { + case MER24: + if (Hours < 0 || Hours > 23) + return -1; + return (Hours * 60L + Minutes) * 60L + Seconds; + case MERam: + if (Hours < 1 || Hours > 12) + return -1; + return (Hours * 60L + Minutes) * 60L + Seconds; + case MERpm: + if (Hours < 1 || Hours > 12) + return -1; + return ((Hours + 12) * 60L + Minutes) * 60L + Seconds; + default: + abort (); + } + /* NOTREACHED */ +} + + +static time_t +Convert(Month, Day, Year, Hours, Minutes, Seconds, Meridian, DSTmode) + time_t Month; + time_t Day; + time_t Year; + time_t Hours; + time_t Minutes; + time_t Seconds; + MERIDIAN Meridian; + DSTMODE DSTmode; +{ + static int DaysInMonth[12] = { + 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 + }; + time_t tod; + time_t Julian; + int i; + + if (Year < 0) + Year = -Year; + if (Year < 100) + Year += 1900; + DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0) + ? 29 : 28; + if (Year < EPOCH || Year > 1999 + || Month < 1 || Month > 12 + /* Lint fluff: "conversion from long may lose accuracy" */ + || Day < 1 || Day > DaysInMonth[(int)--Month]) + return -1; + + for (Julian = Day - 1, i = 0; i < Month; i++) + Julian += DaysInMonth[i]; + for (i = EPOCH; i < Year; i++) + Julian += 365 + (i % 4 == 0); + Julian *= SECSPERDAY; + Julian += yyTimezone * 60L; + if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0) + return -1; + Julian += tod; + if (DSTmode == DSTon + || (DSTmode == DSTmaybe && localtime(&Julian)->tm_isdst)) + Julian -= 60 * 60; + return Julian; +} + + +static time_t +DSTcorrect(Start, Future) + time_t Start; + time_t Future; +{ + time_t StartDay; + time_t FutureDay; + + StartDay = (localtime(&Start)->tm_hour + 1) % 24; + FutureDay = (localtime(&Future)->tm_hour + 1) % 24; + return (Future - Start) + (StartDay - FutureDay) * 60L * 60L; +} + + +static time_t +RelativeDate(Start, DayOrdinal, DayNumber) + time_t Start; + time_t DayOrdinal; + time_t DayNumber; +{ + struct tm *tm; + time_t now; + + now = Start; + tm = localtime(&now); + now += SECSPERDAY * ((DayNumber - tm->tm_wday + 7) % 7); + now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1); + return DSTcorrect(Start, now); +} + + +static time_t +RelativeMonth(Start, RelMonth) + time_t Start; + time_t RelMonth; +{ + struct tm *tm; + time_t Month; + time_t Year; + + if (RelMonth == 0) + return 0; + tm = localtime(&Start); + Month = 12 * tm->tm_year + tm->tm_mon + RelMonth; + Year = Month / 12; + Month = Month % 12 + 1; + return DSTcorrect(Start, + Convert(Month, (time_t)tm->tm_mday, Year, + (time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec, + MER24, DSTmaybe)); +} + + +static int +LookupWord(buff) + char *buff; +{ + register char *p; + register char *q; + register CONST TABLE *tp; + int i; + int abbrev; + + /* Make it lowercase. */ + for (p = buff; *p; p++) + if (isupper(*p)) + *p = tolower(*p); + + if (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0) { + yylval.Meridian = MERam; + return tMERIDIAN; + } + if (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0) { + yylval.Meridian = MERpm; + return tMERIDIAN; + } + + /* See if we have an abbreviation for a month. */ + if (strlen(buff) == 3) + abbrev = 1; + else if (strlen(buff) == 4 && buff[3] == '.') { + abbrev = 1; + buff[3] = '\0'; + } + else + abbrev = 0; + + for (tp = MonthDayTable; tp->name; tp++) { + if (abbrev) { + if (strncmp(buff, tp->name, 3) == 0) { + yylval.Number = tp->value; + return tp->type; + } + } + else if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + } + + for (tp = TimezoneTable; tp->name; tp++) + if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + + if (strcmp(buff, "dst") == 0) + return tDST; + + for (tp = UnitsTable; tp->name; tp++) + if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + + /* Strip off any plural and try the units table again. */ + i = strlen(buff) - 1; + if (buff[i] == 's') { + buff[i] = '\0'; + for (tp = UnitsTable; tp->name; tp++) + if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + buff[i] = 's'; /* Put back for "this" in OtherTable. */ + } + + for (tp = OtherTable; tp->name; tp++) + if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + + /* Military timezones. */ + if (buff[1] == '\0' && isalpha(*buff)) { + for (tp = MilitaryTable; tp->name; tp++) + if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + } + + /* Drop out any periods and try the timezone table again. */ + for (i = 0, p = q = buff; *q; q++) + if (*q != '.') + *p++ = *q; + else + i++; + *p = '\0'; + if (i) + for (tp = TimezoneTable; tp->name; tp++) + if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + + return tID; +} + + +static int +yylex() +{ + register char c; + register char *p; + char buff[20]; + int Count; + int sign; + + for ( ; ; ) { + while (isspace(*yyInput)) + yyInput++; + + if (isdigit(c = *yyInput) || c == '-' || c == '+') { + if (c == '-' || c == '+') { + sign = c == '-' ? -1 : 1; + if (!isdigit(*++yyInput)) + /* skip the '-' sign */ + continue; + } + else + sign = 0; + for (yylval.Number = 0; isdigit(c = *yyInput++); ) + yylval.Number = 10 * yylval.Number + c - '0'; + yyInput--; + if (sign < 0) + yylval.Number = -yylval.Number; + return sign ? tSNUMBER : tUNUMBER; + } + if (isalpha(c)) { + for (p = buff; isalpha(c = *yyInput++) || c == '.'; ) + if (p < &buff[sizeof buff - 1]) + *p++ = c; + *p = '\0'; + yyInput--; + return LookupWord(buff); + } + if (c != '(') + return *yyInput++; + Count = 0; + do { + c = *yyInput++; + if (c == '\0') + return c; + if (c == '(') + Count++; + else if (c == ')') + Count--; + } while (Count > 0); + } +} + + +time_t +get_date(p) + char *p; +{ + struct tm *tm; + time_t Start; + time_t tod, now; + + yyInput = p; + + now = time(0); + tm = localtime(&now); + yyYear = tm->tm_year; + yyMonth = tm->tm_mon + 1; + yyDay = tm->tm_mday; + yyTimezone = 0; + yyDSTmode = DSTmaybe; + yyHour = 0; + yyMinutes = 0; + yySeconds = 0; + yyMeridian = MER24; + yyRelSeconds = 0; + yyRelMonth = 0; + yyHaveDate = 0; + yyHaveDay = 0; + yyHaveRel = 0; + yyHaveTime = 0; + yyHaveZone = 0; + + if (yyparse() + || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1) + return -1; + + if (yyHaveDate || yyHaveTime || yyHaveDay) { + Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds, + yyMeridian, yyDSTmode); + if (Start < 0) + return -1; + } + else { + Start = now; + if (!yyHaveRel) + Start -= ((tm->tm_hour * 60L + tm->tm_min) * 60L) + tm->tm_sec; + } + + Start += yyRelSeconds; + Start += RelativeMonth(Start, yyRelMonth); + + if (yyHaveDay && !yyHaveDate) { + tod = RelativeDate(Start, yyDayOrdinal, yyDayNumber); + Start += tod; + } + + /* Have to do *something* with a legitimate -1 so it's distinguishable + * from the error return value. (Alternately could set errno on error.) */ + return Start == -1 ? 0 : Start; +} + + +#if defined(TEST) + +/* ARGSUSED */ +int main(ac, av) + int ac; + char *av[]; +{ + char buff[128]; + time_t d; + + (void)printf("Enter date, or blank line to exit.\n\t> "); + (void)fflush(stdout); + while (gets(buff) && buff[0]) { + d = get_date(buff); + if (d == -1) + (void)printf("Bad format - couldn't convert.\n"); + else + (void)printf("%s", ctime(&d)); + (void)printf("\t> "); + (void)fflush(stdout); + } + exit(0); + /* NOTREACHED */ +} +#endif /* defined(TEST) */ diff -u -d -r -N -P 8.02/hash.c 9.00/hash.c --- 8.02/hash.c Wed Dec 31 19:00:00 1969 +++ 9.00/hash.c Thu Jun 5 07:11:41 1997 @@ -0,0 +1,437 @@ +#include +#include +#include + +#ifdef XRN +#include "config.h" +#include "utils.h" +#else +#include +#endif + +#ifndef _ARGUMENTS +#define _ARGUMENTS(a) a +#endif + +#include "hash.h" + +#ifdef DEBUG +static int hash_debug = 0; +#endif + +#define SIZE_FACTOR 2.0 +/* + This is the preferred number of bits to shift left after each + character is XORed into the hash of a string. However, it will be + changed in the hash function if it is a factor of the number of bits + in the table size. + */ +#define CHAR_SIG_BITS 4 + +#ifdef DEBUG +float SIZE_MULTIPLIER = SIZE_FACTOR; +#else +#define SIZE_MULTIPLIER SIZE_FACTOR +#endif + +#ifdef XRN +#define HASH_MALLOC(foo) XtMalloc(foo) +#define HASH_FREE(foo) XtFree((char *)foo) +#define HASH_CALLOC(foo,bar) XtCalloc((foo),(bar)) +#define HASH_REALLOC(foo,bar) XtRealloc((char *)(foo),(bar)) +#else +#define HASH_MALLOC(foo) malloc(foo) +#define HASH_FREE(foo) free(foo) +#define HASH_CALLOC(foo,bar) calloc((foo),(bar)) +#define HASH_REALLOC(foo,bar) realloc((foo),(bar)) +#endif /* XRN */ + +#define HASH_ADDED 1 +#define HASH_DELETED 2 + +#define NEXT_HASH(hash,size) (((hash)+1)%(size)) +#define PREV_HASH(hash,size) (((hash)+(size)-1)%(size)) + +struct hash_entry { + void *key; + void *value; + char status; +}; + +struct hash_table { + int size, size_bits; + hash_calc_func calc; + hash_compare_func key_compare, value_compare; + hash_free_func free_key, free_value; +#ifdef DEBUG + int items; + int stores, store_collisions; + int retrieves, retrieve_collisions; + int deletes, delete_collisions; +#endif + struct hash_entry *entries; +}; + +/* + Create a new hash table with room for at most nitems elements, with + hash function calc and comparison functions key_compare and + value_compare. + + Note that nitems *must* be correct, and that the behavior of the + functions in this file is undefined if it isn't. Note furthermore + that even items that are deleted from the hash table may count + against nitems, so the total number of items ever added to the table + must be less than nitems. + + For generic string hashes, the functions hash_string_calc and + hash_string_compare are available. + */ +hash_table_object hash_table_create(nitems, calc, key_compare, value_compare, + free_key, free_value) + int nitems; + hash_calc_func calc; + hash_compare_func key_compare, value_compare; + hash_free_func free_key, free_value; +{ + int i, size_bits = 1; + struct hash_table *table; + + /* + Figure out how big to make the hash table. Multiply it by our + multiplier, and then round up to the nearest power of two, since + modulus by powers of two is faster. + + The table needs to be at least one bigger than the maximum number + of items, so that searches will always terminate eventually by + reaching an empty cell in the table. + */ + nitems = (int)((float)(nitems + 1) * SIZE_MULTIPLIER); + for (i = 1; i < nitems; i *= 2) size_bits++; + nitems = i; + + table = (struct hash_table *) HASH_MALLOC(sizeof(*table)); + + table->size = nitems; + table->size_bits = size_bits; + table->calc = calc; + table->key_compare = key_compare; + table->value_compare = value_compare; + table->free_key = free_key; + table->free_value = free_value; +#ifdef DEBUG + table->items = 0; + table->stores = table->store_collisions = 0; + table->retrieves = table->retrieve_collisions = 0; + table->deletes = table->delete_collisions = 0; +#endif + table->entries = (struct hash_entry *) + HASH_CALLOC(nitems, sizeof(*table->entries)); + + return (hash_table_object)table; +} + +/* + Add a new element to the table. + + If unique is true, then the element must be unique. Otherwise, + multiple elements which compare equal are allowed to be in the table + (and are retrieved in the order they were inserted). + + This function will always succeed unless unique is true and the key + is non-unique. + + The key and value arguments are not copied. The caller should + ensure that they point to persistent values. + + Returns true on success, false on failure. + */ +int hash_table_insert(table_p, key, value, unique) + hash_table_object table_p; + void *key; + void *value; + int unique; +{ + struct hash_table *table = (struct hash_table *)table_p; + int hash; + + for (hash = (*table->calc)(table->size, table->size_bits, key); + table->entries[hash].status; + hash = NEXT_HASH(hash,table->size)) { + if (unique && + (table->entries[hash].status != HASH_DELETED) && + ! (*table->key_compare)(key, table->entries[hash].key)) + return 0; +#ifdef DEBUG + if ((*table->key_compare)(key, table->entries[hash].key)) + table->store_collisions++; +#endif + } + + table->entries[hash].status = HASH_ADDED; + table->entries[hash].key = key; + table->entries[hash].value = value; +#ifdef DEBUG + table->stores++; + table->items++; +#endif + + return 1; +} + +/* + Retrieve an element from the table. + + If "reference" is non-null, then it is (a) used to determine where + to start the search and (b) filled in with where the next search for + the same key should be started. This is used to retrieve multiple + items with the same key from the table. The first time this + function is called for a particular value, the contents of reference + should be HASH_NO_VALUE. + + Returns the value retrieved, or HASH_NO_VALUE there are none (or + no more). + */ +void *hash_table_retrieve(table_p, key, reference) + hash_table_object table_p; + void *key; + void **reference; +{ + struct hash_table *table = (struct hash_table *)table_p; + void *dummy_reference = HASH_NO_VALUE; + int hash; + + if (! reference) + reference = &dummy_reference; + + if (*reference == HASH_NO_VALUE) + hash = (*table->calc)(table->size, table->size_bits, key); + else + hash = NEXT_HASH((int)*reference, table->size); + + for ( ; table->entries[hash].status; hash = NEXT_HASH(hash,table->size)) { + if ((table->entries[hash].status == HASH_DELETED) || + (*table->key_compare)(key, table->entries[hash].key)) { +#ifdef DEBUG + table->retrieve_collisions++; +#endif + continue; + } + *reference = (void *)hash; +#ifdef DEBUG + table->retrieves++; +#endif + return table->entries[hash].value; + } + + return HASH_NO_VALUE; +} + + +/* + Delete an entry from the hash table. + + Value is optional; if it is null, then all entries in the table with + the given key are deleted. + */ +void hash_table_delete(table_p, key, value) + hash_table_object table_p; + void *key; + void *value; +{ + struct hash_table *table = (struct hash_table *)table_p; + int hash = (*table->calc)(table->size, table->size_bits, key); + + for ( ; table->entries[hash].status; hash = NEXT_HASH(hash,table->size)) { + if (table->entries[hash].status == HASH_DELETED) { +#ifdef DEBUG + table->delete_collisions++; +#endif + continue; + } + if ((*table->key_compare)(key, table->entries[hash].key) || + ((value != HASH_NO_VALUE) && + (*table->value_compare)(value, table->entries[hash].value))) { + int new_hash = (*table->calc)(table->size, table->size_bits, + table->entries[hash].key); + if (table->entries[new_hash].status == HASH_DELETED) { + table->entries[new_hash] = table->entries[hash]; + table->entries[hash].status = HASH_DELETED; + } +#ifdef DEBUG + table->delete_collisions++; +#endif + continue; + } + table->entries[hash].status = HASH_DELETED; + if (table->free_key) + (*table->free_key)(table->entries[hash].key); + if (table->free_value) + (*table->free_value)(table->entries[hash].value); +#ifdef DEBUG + table->deletes++; + table->items--; +#endif + } + for (hash = PREV_HASH(hash, table->size); + table->entries[hash].status == HASH_DELETED; + hash = PREV_HASH(hash, table->size)) { + table->entries[hash].status = 0; + } +} + + +/* + Destroy a hash table. + */ +void hash_table_destroy(table_p) + hash_table_object table_p; +{ + struct hash_table *table = (struct hash_table *)table_p; + int hash; + + for (hash = 0; hash < table->size; hash++) { + if (table->entries[hash].status == HASH_ADDED) { + if (table->free_key) + (*table->free_key)(table->entries[hash].key); + if (table->free_value) + (*table->free_value)(table->entries[hash].value); + } + } + + HASH_FREE(table->entries); + HASH_FREE(table); +} + +int hash_string_calc(size, size_bits, key) + int size; + int size_bits; + void *key; +{ + static int stashed_size = 0; + static int shift_bits; + + unsigned int hash = 0; + unsigned char *ptr; + int cur_pos = 0; + + if (size != stashed_size) { + stashed_size = size; + for (shift_bits = CHAR_SIG_BITS; shift_bits < CHAR_SIG_BITS+8; + shift_bits++) { + if (! (shift_bits % 8)) + continue; + if (size_bits % (shift_bits % 8)) + break; + } + shift_bits %= 8; + } + for (ptr = (unsigned char *)key; *ptr; ptr++) { + hash = hash ^ (*ptr << cur_pos); + cur_pos = (cur_pos + shift_bits) % size_bits; + } + + hash %= size; + +#ifdef DEBUG + if (hash_debug & HASH_PRINT_HASHES) + printf("Key:\t%s\tHash:\t%d\n", (char *)key, hash); +#endif /* DEBUG */ + + return hash; +} + +int hash_string_compare(key1, key2) + void *key1, *key2; +{ + return strcmp((char *)key1, (char *)key2); +} + +int hash_int_calc(size, size_bits, key) + int size; + int size_bits; + void *key; +{ + return (int)key % size; +} + +int hash_int_compare(key1, key2) + void *key1, *key2; +{ + return (int)key1 - (int)key2; +} + +#ifdef DEBUG +#define PERCENT(a,b) ((b)?(100*(a)/(b)):0) + +void hash_print_stats(table_p) + hash_table_object table_p; +{ + struct hash_table *table = (struct hash_table *)table_p; + + printf("Size:\t%d\tItems:\t%d\n", table->size, table->items); + printf("Stores:\t%d\tCollisions:\t%d (%d%%)\n", + table->stores, table->store_collisions, + PERCENT(table->store_collisions, table->stores)); + printf("Retrieves:\t%d\tCollisions:\t%d (%d%%)\n", table->retrieves, + table->retrieve_collisions, + PERCENT(table->retrieve_collisions, table->retrieves)); + printf("Deletes:\t%d\tCollisions:\t%d (%d%%)\n", table->deletes, + table->delete_collisions, + PERCENT(table->delete_collisions, table->deletes)); +} +#endif /* DEBUG */ + +#ifdef TEST +#include +#include + +int main(argc, argv) + int argc; + char *argv[]; +{ + int c; + int size = 0; + hash_table_object table_p; + char buf[BUFSIZ]; + char *value = 0; + void *reference = HASH_NO_VALUE; + + int getopt(); + extern char *optarg; + + while ((c = getopt(argc, argv, "m:s:")) != EOF) { + switch (c) { + case 'm': + SIZE_MULTIPLIER = atof(optarg); + break; + case 's': + size = atoi(optarg); + break; + default: + exit(1); + } + } + + if (! size) { + fprintf(stderr, "%s; Must specify size with -s\n", argv[0]); + exit(1); + } + + table_p = hash_table_create(size, hash_string_calc, + hash_string_compare, + hash_string_compare, + (hash_free_func)free, + 0); + + c = 0; + while (fgets(buf, sizeof(buf), stdin)) { + c++; + hash_table_insert(table_p, (void *)strdup(buf), (void *)c, 0); + } + + hash_print_stats(table_p); + hash_table_destroy(table_p); + + exit(0); +} +#endif /* TEST */ diff -u -d -r -N -P 8.02/hash.h 9.00/hash.h --- 8.02/hash.h Wed Dec 31 19:00:00 1969 +++ 9.00/hash.h Thu Jun 5 07:11:41 1997 @@ -0,0 +1,45 @@ +#ifndef _HASH_H +#define _HASH_H + +#ifndef _ARGUMENTS +#ifdef __STDC__ +#define _ARGUMENTS(a) a +#else +#define _ARGUMENTS(a) () +#endif +#endif + +typedef int (*hash_calc_func) _ARGUMENTS((int, int, void *)); +typedef int (*hash_compare_func) _ARGUMENTS((void *, void *)); +typedef void (*hash_free_func) _ARGUMENTS((void *)); + +typedef void *hash_table_object; + +hash_table_object hash_table_create + _ARGUMENTS((int, hash_calc_func, + hash_compare_func, hash_compare_func, + hash_free_func, hash_free_func)); + +int hash_table_insert _ARGUMENTS((hash_table_object, void *, void *, int)); + +void *hash_table_retrieve _ARGUMENTS((hash_table_object, void *, void **)); + +void hash_table_delete _ARGUMENTS((hash_table_object, void *, void *)); + +void hash_table_destroy _ARGUMENTS((hash_table_object)); + +int hash_string_calc _ARGUMENTS((int, int, void *)); +int hash_string_compare _ARGUMENTS((void *, void *)); + +int hash_int_calc _ARGUMENTS((int, int, void *)); +int hash_int_compare _ARGUMENTS((void *, void *)); + +#ifdef DEBUG +void hash_print_stats _ARGUMENTS((hash_table_object)); + +#define HASH_PRINT_HASHES (1<<0) +#endif + +#define HASH_NO_VALUE (void *)-1 + +#endif /* _HASH_H */ diff -u -d -r -N -P 8.02/internals.c 9.00/internals.c --- 8.02/internals.c Thu May 2 03:27:15 1996 +++ 9.00/internals.c Sun Dec 28 11:36:37 1997 @@ -1,5 +1,5 @@ #if !defined(lint) && !defined(SABER) && !defined(GCC_WALL) -static char XRNrcsid[] = "$Id: internals.c,v 1.140 1996/05/02 07:26:55 jik Exp $"; +static char XRNrcsid[] = "$Id: internals.c,v 1.215 1997/12/28 16:36:37 jik Exp $"; #endif /* @@ -41,6 +41,7 @@ #include #include #include +#include #include "avl.h" #include "news.h" #include "artstruct.h" @@ -63,14 +64,16 @@ #include "varfile.h" #include "dialogs.h" #include "activecache.h" +#include "sort.h" +#include "killfile.h" +#include "file_cache.h" +#include "hash.h" #ifndef R_OK #define R_OK 4 #endif -static char *strip _ARGUMENTS((char *, /* Boolean */ int)); - #define BUFFER_SIZE 1024 #define FATAL 0 #define OKAY 1 @@ -271,11 +274,6 @@ return True; } - if (rescan_group_number < 0) { - rescan_id = 0; - return True; - } - if (rescan_group_number == MaxGroupNumber) { char *buf = XtMalloc(strlen(RESCANNING_BACKGROUND_MSG) + strlen(DONE_MSG) + 2); @@ -338,12 +336,25 @@ rescan_id = XtAppAddWorkProc(TopContext, rescanWorkProc, 0); } +void cancelRescanBackground() +{ +#ifdef DEBUG + fprintf(stderr, "cancelRescanBackground()\n"); +#endif + + if (rescan_id) { + XtRemoveWorkProc(rescan_id); + rescan_id = 0; + } +} /* * get the active file again and grow the Newsrc array if necessary */ -void rescanServer(force_list) -Boolean force_list; +void rescanServer( + _ANSIDECL(Boolean, force_list) + ) + _KNRDECL(Boolean, force_list) { cancelPrefetch(); @@ -440,60 +451,65 @@ the newsgroup as a side effect, so any outstanding article structures in the caller should be considered invalid. */ -static Boolean articleIsAvailable _ARGUMENTS((struct newsgroup *, art_num)); - -static Boolean articleIsAvailable(newsgroup, i) +Boolean articleIsAvailable(newsgroup, i) struct newsgroup *newsgroup; art_num i; { - Boolean ret; struct article *art; art = artStructGet(newsgroup, i, True); - if (art->filename && !IS_ALL_HEADERS(art) && !IS_ROTATED(art) && - !IS_XLATED(art) && (access(art->filename, R_OK) == 0)) { + if (art->file && *art->file && !IS_ALL_HEADERS(art) && !IS_ROTATED(art) && + !IS_XLATED(art) && + (access(file_cache_file_name(FileCache, *art->file), R_OK) == 0)) { artStructSet(newsgroup, &art); return True; } - FREE(art->subject); + CLEAR_SUBJECT(art); artStructSet(newsgroup, &art); - (void) getsubjectlist(newsgroup, i, i, False, 1); - - art = artStructGet(newsgroup, i, True); - if (art->subject) { - ret = True; - } - else { - ret = False; - CLEAR_FILE(art); - CLEAR_SUBJECT(art); - CLEAR_AUTHOR(art); - CLEAR_LINES(art); - SET_UNAVAIL(art); - } - artStructSet(newsgroup, &art); + (void) fillUpArray(newsgroup, i, i, False, False); - return ret; + art = artStructGet(newsgroup, i, False); + if (art->subject) + return True; + else + return False; } /* Find the first unread article, or the last article if unread_only is false and there are no unread articles, in a group and set the - group's 'current' to it. + group's 'current' to it. If "check_available" is true, actually + contacts the NNTP server to verify that the article is available. - Returns True if the group's current article was modified, False + Returns True if the group's current article was modified in a way + which might require the group to be prefetched again, False otherwise. + + If the "finished" parameter is non-null, then this function will + only make "max" attempts to find a current article by contacting the + NNTP server. If it succeeds, the parameter will be filled in True; + otherwise, it'll be filled in False. */ static Boolean setCurrentArticle _ARGUMENTS((struct newsgroup *, - /* Boolean */ int)); + Boolean, Boolean, + Boolean *, int)); -static Boolean setCurrentArticle(newsgroup, unread_only) - struct newsgroup *newsgroup; - Boolean unread_only; +static Boolean setCurrentArticle( + _ANSIDECL(struct newsgroup *, newsgroup), + _ANSIDECL(Boolean, unread_only), + _ANSIDECL(Boolean, check_available), + _ANSIDECL(Boolean *, finished), + _ANSIDECL(int, max) + ) + _KNRDECL(struct newsgroup *, newsgroup) + _KNRDECL(Boolean, unread_only) + _KNRDECL(Boolean, check_available) + _KNRDECL(Boolean *, finished) + _KNRDECL(int, max) { struct article *art; art_num i, start, avail = 0; @@ -517,10 +533,17 @@ for (i = start; i<= newsgroup->last; i++) { art = artStructGet(newsgroup, i, False); - if (IS_UNREAD(art) && IS_AVAIL(art) && - articleIsAvailable(newsgroup, i)) { + if (IS_UNREAD(art) && IS_AVAIL(art)) { + if (!check_available || articleIsAvailable(newsgroup, i)) { + if (finished) + *finished = True; newsgroup->current = i; goto done; + } + else if (finished && ! --max) { + *finished = False; + goto done; + } } } @@ -530,16 +553,25 @@ else { for (i = newsgroup->last; i >= newsgroup->first; i--) { art = artStructGet(newsgroup, i, False); - if (IS_AVAIL(art) && articleIsAvailable(newsgroup, i)) { + if (IS_AVAIL(art)) { + if (!check_available || articleIsAvailable(newsgroup, i)) { + if (finished) + *finished = True; newsgroup->current = i; goto done; + } + else if (finished && ! --max) { + *finished = False; + goto done; + } } } newsgroup->current = newsgroup->last + 1; } done: - return (orig != newsgroup->current); + return ((orig != newsgroup->current) && + (newsgroup->current <= newsgroup->last)); } /* @@ -705,7 +737,8 @@ int line_length; int mode; /* 0 for unread groups, 1 for all subscribed to groups */ { - char dummy[LINE_LENGTH]; + static char *dummy = NULL; + static int dummy_size = 0, padding_size = 0; struct newsgroup *newsgroup; ng_num i; int unread, j; @@ -718,12 +751,25 @@ ar = ARRAYALLOC(char *, MaxGroupNumber); + if (! dummy_size) { + dummy_size = LINE_LENGTH; + dummy = XtMalloc(dummy_size); + padding_size = strlen(NEWSGROUPS_INDEX_MSG) + strlen(UNREAD_MSG) + + strlen(NEWS_IN_MSG) + 10 /* for unread count */ + strlen(NOT_ONE_MSG) + + 10 /* for old count */ + 1 /* for null */; + } + for (i = 0; i < MaxGroupNumber; i++) { newsgroup = Newsrc[i]; if (IS_SUBSCRIBED(newsgroup) && (((unread = unreadArticleCount(newsgroup)) > 0) || mode)) { int total = totalArticleCount(newsgroup); + while (dummy_size < padding_size + + MAX(newsgroup_length, strlen(newsgroup->name))) { + dummy_size *= 2; + dummy = XtRealloc(dummy, dummy_size); + } (void) sprintf(dummy, NEWSGROUPS_INDEX_MSG, (unread > 0 ? UNREAD_MSG : ""), (total > 0 ? NEWS_IN_MSG : ""), @@ -873,6 +919,11 @@ groupSnapshotFree(newsgroup); + if (newsgroup->thread_table) { + hash_table_destroy(newsgroup->thread_table); + newsgroup->thread_table = 0; + } + if ((newsgroup->last == 0) || EMPTY_GROUP(newsgroup)) { return; } @@ -881,15 +932,13 @@ original = artStructGet(newsgroup, artnum, False); copy = *original; - copy.subject = 0; - copy.author = 0; - copy.lines = 0; - copy.filename = 0; + CLEAR_ALL_NO_FREE(©); SET_UNMARKED(©); SET_STRIPPED_HEADERS(©); SET_UNROTATED(©); SET_UNXLATED(©); + SET_UNLISTED(©); artStructReplace(newsgroup, &original, ©, artnum); } @@ -948,12 +997,10 @@ artListSet(newsgroup); } else { - copy.subject = 0; - copy.author = 0; - copy.lines = 0; - copy.filename = 0; - copy.status = ART_CLEAR_READ; + + CLEAR_ALL_NO_FREE(©); + SET_UNAVAIL(©); for (i = newsgroup->first; i < first; i++) { art = artStructGet(newsgroup, i, False); @@ -1005,7 +1052,7 @@ if (mode) { (void) mkdir(buffer, 0777); } - (void) strcpy(&buffer[i], "/KILL"); + (void) sprintf(&buffer[i], "/%s", app_resources.killFileName); return buffer; } @@ -1017,8 +1064,8 @@ if (!createNewsDir()) { return NIL(char); } - (void) strcpy(buffer, app_resources.expandedSaveDir); - (void) strcat(buffer, "/KILL"); + (void) sprintf(buffer, "%s/%s", app_resources.expandedSaveDir, + app_resources.killFileName); return buffer; } @@ -1031,12 +1078,14 @@ The returned string is static memory which is not valid after the next call to this function. */ -char *stringToRegexp(input) +char *stringToRegexp(input, max_length) char *input; { static char output[BUFFER_SIZE]; char *inptr, *outptr; + max_length -= 2; /* to make room for braces */ + /* quote or get rid of all magic characters */ for (inptr = input, outptr = output; (*inptr && (outptr - output < sizeof(output) - 4)); @@ -1092,6 +1141,8 @@ *outptr = *inptr; break; } + if (outptr - output >= max_length) + break; } *outptr = '\0'; return output; @@ -1099,96 +1150,30 @@ /* - * add a kill subject/author entry to a kill file - */ -void killItem(newsgroup, item, type) - struct newsgroup *newsgroup; - char *item; - int type; -{ - FILE *fp; - char *file; - - if (type == KILL_LOCAL) { - file = localKillFile(newsgroup, 1); - } else { - file = globalKillFile(); - } - - if ((fp = fopen(file, "a")) == NULL) { - mesgPane(XRN_SERIOUS, 0, CANT_OPEN_KILL_MSG, - ((type == KILL_LOCAL) ? "local" : "global"), - file, newsgroup->name, errmsg(errno)); - return; - } - - fprintf(fp, "/%s/:j\n", item); - (void) fclose(fp); - return; -} - -/* - Return true if the indicated character, which should be in the - string pointed to by start, is backslash-quoted, or false otherwise. - Deals correctly with quoted backslashes immediately following a - non-quoted character. - */ -static Boolean isQuoted _ARGUMENTS((char *, char *)); - -static Boolean isQuoted(character, start) - char *character, *start; -{ - /* - Figure out how many backslashes there are between the indicated - character and either the first non-backslash or the beginning of - the string. - - If the number of backslashes is odd, then return True. - Otherwise, return False. - */ - Boolean quoted = False; - - for (character--; - (character >= start) && (*character == '\\'); - character--) - quoted = ! quoted; - - return quoted; -} - - - -/* * kill all subjects in the newsgroup that match the kill * orders in fp. * * Returns True if it gets to the end of the group, False otherwise. */ /* XXX THIS ROUTINE REALLY NEEDS TO BE CLEANED UP */ -static Boolean killArticles _ARGUMENTS((struct newsgroup *, FILE *, - FILE *, int)); +static Boolean killArticles _ARGUMENTS((struct newsgroup *, int)); -static Boolean killArticles(newsgroup, fp1, fp2, max) +static Boolean killArticles(newsgroup, max) struct newsgroup *newsgroup; - FILE *fp1, *fp2; int max; { - struct article *art, copy; - char string[BUFFER_SIZE], pattern[BUFFER_SIZE], commands[BUFFER_SIZE]; - char dummy[BUFFER_SIZE]; + struct article *art; art_num i, start, last; - char *subject, *author, *subj, *ptr, *pptr; - int scount = 0, kcount = 0, ucount = 0, mcount = 0; -#ifdef POSIX_REGEX - int reRet; - regex_t reStruct; -#else - char *reRet; -#endif - char type; + char *subject, *from, *newsgroups, *date, *id, *references, *xref; + int scount = 0, kcount = 0, mcount = 0; int mesg_name; + kill_entry *entry = 0; + kill_file *kf; + int cur_mode = KILL_LOCAL; - start = newsgroup->current; + kf = (kill_file *)newsgroup->kill_file; + + start = MAX(newsgroup->current, MIN(kf->thru + 1, newsgroup->last)); do { art = artStructGet(newsgroup, start, False); if (IS_KILLED(art)) @@ -1206,209 +1191,121 @@ last = MIN(newsgroup->last, last + 1); } - if (start > last) - return True; - - /* XXX don't reprocess global each time, keep in persistent hash table */ - - while (((fp1 && fgets(string, sizeof(string), fp1)) || (fp1 = 0)) || - ((fp2 && fgets(string, sizeof(string), fp2)) || (fp2 = 0))) { - mesg_name = newMesgPaneName(); - - /* - * see if there is a 'THRU artnum' line, if so, - * only compare subjects from that article on - * XXX need to update THRU - */ - if (STREQN(string, "THRU", 4)) { - i = atol(&string[5]); - /* if THRU is less than current, ignore it */ - start = MAX(i + 1, start); - continue; - } - - if (*string == '&') { - /* 'rn' switch setting, ignore */ - continue; - } - - if ((ptr = index(string, '\n'))) - *ptr = '\0'; - - if (index(app_resources.verboseKill, 'l')) { - mesgPane(XRN_INFO, mesg_name, KILL_LINE_MSG, string, - newsgroup->name); - } - - /* - * process kill file request should be in the form - */ - ptr = string + 1; - pptr = pattern; + if (kf->count) + kf->flags |= KF_CHANGED; - while (*ptr && ((*ptr != '/') || isQuoted(ptr, string + 1))) { - *pptr++ = *ptr; - ptr++; - } - *pptr = '\0'; + kf->thru = MAX(kf->thru, MIN(start, last)); - if (!*ptr) { - mesgPane(XRN_SERIOUS, mesg_name, MALFORMED_KILL_ENTRY_MSG, string, - newsgroup->name, ERROR_REGEX_NOSLASH_MSG); - continue; - } + if (start > last) + return True; - /* rn puts ': *' in front of patterns, xrn doesn't */ - if (strncmp(pattern, ": *", 3) == 0) { - /* deal with overlapping strings */ - (void) strcpy(dummy, pattern + 3); - (void) strcpy(pattern, dummy); - } + kf->thru = last; - /* XXX kludge to deal with :.*checks */ - if (*pattern == ':') { - /* deal with overlapping strings */ - (void) strcpy(dummy, pattern + 1); - (void) strcpy(pattern, dummy); - } + while (1) { + if (! (entry = kill_file_iter(newsgroup, cur_mode, entry))) { + if (cur_mode != KILL_LOCAL) + break; + cur_mode = KILL_GLOBAL; + continue; + } -#ifdef POSIX_REGEX - if ((reRet = regcomp(&reStruct, pattern, REG_NOSUB))) -#else -# ifdef SYSV_REGEX - if ((reRet = regcmp(pattern, NULL)) == NULL) -# else - if ((reRet = re_comp(pattern)) != NIL(char)) -# endif -#endif - { -#ifdef SYSV_REGEX - mesgPane(XRN_SERIOUS, mesg_name, UNKNOWN_KILL_REGEXP_ERROR_MSG, string); -#else -#ifdef POSIX_REGEX - regerror(reRet, &reStruct, error_buffer, - sizeof(error_buffer)); -#endif - mesgPane(XRN_SERIOUS, mesg_name, KNOWN_KILL_REGEXP_ERROR_MSG, string, -# ifdef POSIX_REGEX - error_buffer -# else - reRet -# endif - ); -#endif - continue; - } + if (entry->type != KILL_ENTRY) + continue; - ptr++; /* skip past the slash */ - (void) strcpy(commands, ptr); - if ((ptr = index(commands, ':')) == NIL(char)) { - mesgPane(XRN_SERIOUS, mesg_name, MALFORMED_KILL_ENTRY_MSG, string, - newsgroup->name, ERROR_REGEX_NOCOLON_MSG); -#ifdef POSIX_REGEX - regfree(&reStruct); -#else -# ifdef SYSV_REGEX - FREE(reRet); -# endif -#endif - continue; - } - ptr++; /* skip past the colon */ - type = *ptr; + mesg_name = newMesgPaneName(); - switch (type) { - case 'j': - case 'm': - case 's': - break; - default: - mesgPane(XRN_INFO, mesg_name, MALFORMED_KILL_ENTRY_MSG, string, - newsgroup->name, - ERROR_REGEX_UNKNOWN_COMMAND_MSG ); - break; - } + for (i = start; i <= last; i++) { + int field_count = 0; - mesg_name = newMesgPaneName(); + art = artStructGet(newsgroup, i, False); - for (i = start; i <= last; i++) { - art = artStructGet(newsgroup, i, False); + /* short cut */ + if (IS_KILLED(art) || IS_UNAVAIL(art) || + ((entry->entry.action_flags & KILL_JUNK) && IS_READ(art)) || + ((entry->entry.action_flags & KILL_MARK) && IS_UNREAD(art))) + continue; - /* short cut */ - if (IS_KILLED(art) || IS_UNAVAIL(art) || - ((type == 'j') && IS_READ(art)) || - ((type == 'm') && IS_UNREAD(art))) - continue; +#define CHECK_FIELD(flag, name) \ + if (entry->entry.check_flags & flag) { \ + if ((name = art->name)) \ + field_count++; \ + } \ + else \ + name = 0; - if (art->subject || art->author) { - copy = *art; + CHECK_FIELD(KILL_SUBJECT, subject); + CHECK_FIELD(KILL_AUTHOR, from); + CHECK_FIELD(KILL_NEWSGROUPS, newsgroups); + CHECK_FIELD(KILL_DATE, date); + CHECK_FIELD(KILL_ID, id); + CHECK_FIELD(KILL_REFERENCES, references); + CHECK_FIELD(KILL_XREF, xref); - subject = art->subject; - author = art->author; + if (field_count) { + struct article copy; + unsigned char changed = FALSE; - if (subject) { - subj = strip(subject, FALSE); - } + copy = *art; #ifdef POSIX_REGEX - if ((subject && (! regexec(&reStruct, subj, 0, 0, 0))) || - (author && (! regexec(&reStruct, author, 0, 0, 0)))) +# define KILL_MATCH(a) ((a) && (! regexec(&entry->entry.reStruct, (a), 0, 0, 0))) #else # ifdef SYSV_REGEX - if ((subject && (regex(reRet, subj) != NULL)) || - (author && (regex(reRet, author) != NULL))) +# define KILL_MATCH(a) ((a) && (regex(entry->entry.reStruct, (a)) != NULL)) # else - if ((subject && re_exec(subj)) || - (author && re_exec(author))) +# define KILL_MATCH(a) ((a) && re_exec(a)) # endif #endif - { - switch (type) { - case 'j': - SET_READ(©); - if (index(app_resources.verboseKill, 'j')) { - mesgPane(XRN_INFO, mesg_name, KILL_KILLED_MSG, - subject); - } - kcount++; - break; - case 'm': - SET_UNREAD(©); - if (index(app_resources.verboseKill, 'm')) { - mesgPane(XRN_INFO, mesg_name, KILL_UNREAD_MSG, - subject); - } - mcount++; - break; + if (KILL_MATCH(subject) || KILL_MATCH(from) || + KILL_MATCH(date) || KILL_MATCH(newsgroups) || + KILL_MATCH(id) || KILL_MATCH(references) || + KILL_MATCH(xref)) { + kill_update_last_used(kf, entry); - case 's': - (void) saveArticle(NIL(char), newsgroup, i, False, - False); - if (index(app_resources.verboseKill, 's')) { - mesgPane(XRN_INFO, mesg_name, KILL_SAVED_MSG, - subject); - } - scount++; - break; + switch (entry->entry.action_flags) { + case KILL_JUNK: + SET_READ(©); + if (index(app_resources.verboseKill, 'j')) { + mesgPane(XRN_INFO, mesg_name, KILL_KILLED_MSG, + art->subject); + } + kcount++; + changed = TRUE; + break; - default: - ucount++; - break; - } - } + case KILL_MARK: + SET_UNREAD(©); + if (index(app_resources.verboseKill, 'm')) { + mesgPane(XRN_INFO, mesg_name, KILL_UNREAD_MSG, + art->subject); + } + mcount++; + changed = TRUE; + break; - artStructReplace(newsgroup, &art, ©, i); + case KILL_SAVE: + (void) saveArticle(NIL(char), newsgroup, i, False, + False); + if (index(app_resources.verboseKill, 's')) { + mesgPane(XRN_INFO, mesg_name, KILL_SAVED_MSG, + art->subject); + } + scount++; + changed = TRUE; + break; + default: + mesgPane(XRN_SERIOUS, mesg_name, UNKNOWN_FUNC_RESPONSE_MSG, + entry->entry.action_flags, "kill_file_iter(action_flags)", + "killArticles"); + break; } - } + } -#ifdef POSIX_REGEX - regfree(&reStruct); -#else -# ifdef SYSV_REGEX - FREE(reRet); -# endif -#endif + if (changed) + artStructReplace(newsgroup, &art, ©, i); + } + } } for (i = start; i <= last; i++) { @@ -1420,7 +1317,7 @@ mesg_name = newMesgPaneName(); #define printcount(c,cmd,m) \ - if (c && ((! cmd) || index(app_resources.verboseKill, cmd))) { \ + if (c && index(app_resources.verboseKill, cmd)) { \ mesgPane(XRN_INFO, mesg_name, m, c, ((c==1) ? "" : NOT_ONE_MSG), \ newsgroup->name); \ } @@ -1428,7 +1325,6 @@ printcount(kcount, 'j', COUNT_KILLED_MSG); printcount(mcount, 'm', COUNT_UNREAD_MSG); printcount(scount, 's', COUNT_SAVED_MSG); - printcount(ucount, '\0', COUNT_MATCHED_MSG); #undef printcount @@ -1442,29 +1338,39 @@ /* * mark articles as read if in the kill files */ -static Boolean checkKillFiles _ARGUMENTS((struct newsgroup *, - /* Boolean */ int)); +static Boolean checkKillFiles _ARGUMENTS((struct newsgroup *, Boolean)); -static Boolean checkKillFiles(newsgroup, prefetching) - struct newsgroup *newsgroup; - Boolean prefetching; +static Boolean checkKillFiles( + _ANSIDECL(struct newsgroup *, newsgroup), + _ANSIDECL(Boolean, prefetching) + ) + _KNRDECL(struct newsgroup *, newsgroup) + _KNRDECL(Boolean, prefetching) { - FILE *gfp, *lfp; static int chunk_size; long start WALL(= 0), end; static struct newsgroup *last_group = 0; char dummy[LABEL_SIZE]; Boolean finished = True; + unsigned char fetched = newsgroup->fetch; #ifdef DEBUG fprintf(stderr, "checkKillFiles(%s, %d)\n", newsgroup->name, prefetching); #endif - gfp = fopen(globalKillFile(), "r"); - lfp = fopen(localKillFile(newsgroup, 0), "r"); + /* + Make sure the local and global kill files for the group are read in. + */ + read_local_kill_file(newsgroup); + read_global_kill_file(newsgroup); - if (! (gfp || lfp)) - goto done; + if (fetched != newsgroup->fetch) { + finished = False; + goto done; + } + + if (! has_kill_files(newsgroup)) + goto done; (void) sprintf(dummy, PROCESS_KILL_FOR_MSG, newsgroup->name); maybeInfoNow(dummy); @@ -1481,11 +1387,7 @@ if (prefetching) start = time(0); - finished = killArticles(newsgroup, gfp, lfp, prefetching ? chunk_size : 0); - if (gfp) - (void) fclose(gfp); - if (lfp) - (void) fclose(lfp); + finished = killArticles(newsgroup, prefetching ? chunk_size : 0); if (prefetching && !finished) { end = time(0); @@ -1499,7 +1401,7 @@ INFO(dummy); } - done: +done: #ifdef DEBUG fprintf(stderr, "checkKillFiles(%s, %d) = %d\n", newsgroup->name, prefetching, finished); @@ -1511,12 +1413,14 @@ void finishPrefetch() { + struct newsgroup *our_group = PrefetchingGroup; + #ifdef DEBUG fprintf(stderr, "finishPrefetch()\n"); #endif FinishingPrefetch = True; - while (PrefetchingGroup) + while (our_group == PrefetchingGroup) (*PrefetchingProc)(PrefetchingGroup); FinishingPrefetch = False; @@ -1573,6 +1477,305 @@ #endif } +static int art_num_compare _ARGUMENTS((void *, void *)); + +static int art_num_compare(key1, key2) + void *key1, *key2; +{ + return (art_num)key1 - (art_num)key2; +} + +static char *backTo _ARGUMENTS((char *, char *, char)); + +static char *backTo( + _ANSIDECL(char *, start), + _ANSIDECL(char *, ptr), + _ANSIDECL(char, character) + ) + _KNRDECL(char *, start) + _KNRDECL(char *, ptr) + _KNRDECL(char, character) +{ + for (ptr--; ptr >= start; ptr--) + if (*ptr == character) + return ptr; + return 0; +} + +static Boolean threadIncremental _ARGUMENTS((struct newsgroup *, + int *, + art_num, art_num)); + +static Boolean threadIncremental( + _ANSIDECL(struct newsgroup *, newsgroup), + _ANSIDECL(int *, stage), + _ANSIDECL(art_num, first), + _ANSIDECL(art_num, last) + ) + _KNRDECL(struct newsgroup *, newsgroup) + _KNRDECL(int *, stage) + _KNRDECL(art_num, first) + _KNRDECL(art_num, last) +{ + static int chunk_size = PREFETCH_CHUNK; + time_t start_time WALL (= 0), end_time; + art_num i, done_count = 0; + char dummy[LABEL_SIZE]; + + if (! (newsgroup->fetch & FETCH_THREADS)) + return True; + + if (stage) { + if (*stage != PREFETCH_THREAD_STAGE) + return True; + start_time = time(0); + } + + if (! newsgroup->thread_table) { + (void) sprintf(dummy, THREADING_FOR_MSG, newsgroup->name); + maybeInfoNow(dummy); + + newsgroup->thread_table = hash_table_create(last - first + 1, + hash_string_calc, + hash_string_compare, + art_num_compare, + 0, 0); + + for (i = first; i <= last; i++) { + struct article *art = artStructGet(newsgroup, i, False); + if (art->id && IS_LISTED(art)) { + int ret = hash_table_insert(newsgroup->thread_table, + (void *)art->id, (void *)i, 0); + assert(ret); + } + } + + if (stage) + return False; + } + + + for (i = first; (i <= last) && (! stage || (done_count < chunk_size)); i++) { + struct article *art = artStructGet(newsgroup, i, True); + char *references, *ptr; + art_num parent; + struct article *parent_struct; + + if (art->parent || !IS_LISTED(art)) + continue; + + if (! (art->references && *art->references)) { + art->parent = (art_num)-1; + continue; + } + + done_count++; + + references = XtNewString(art->references); + + for (ptr = strrchr(references, '>'); ptr; + ptr = backTo(references, ptr, '>')) { + *(ptr + 1) = '\0'; + ptr = backTo(references, ptr, '<'); + + if (! ptr) + break; + + if ((parent = (art_num)hash_table_retrieve(newsgroup->thread_table, + (void *)ptr, 0)) != + (art_num)HASH_NO_VALUE) { + art->parent = parent; + parent_struct = artStructGet(newsgroup, parent, True); + artStructAddChild(parent_struct, i); + artStructSet(newsgroup, &parent_struct); + break; + } + } + + XtFree(references); + + if (! art->parent) + art->parent = (art_num)-1; + artStructSet(newsgroup, &art); + } + + if (i > last) { + for (i = first; i <= last; i++) { + struct article *art = artStructGet(newsgroup, i, False); + if (art->parent == (art_num)-1) { + struct article copy; + copy = *art; + CLEAR_PARENT(©); + artStructReplace(newsgroup, &art, ©, i); + } + } + hash_table_destroy(newsgroup->thread_table); + newsgroup->thread_table = 0; + (void) sprintf(dummy, THREADING_FOR_MSG, newsgroup->name); + (void) strcat(dummy, " "); + (void) strcat(dummy, DONE_MSG); + INFO(dummy); + + return True; + } + + end_time = time(0); + adjustPrefetchChunk(end_time - start_time, + PREFETCH_CHUNK_TIME, &chunk_size); + return False; +} + +static void checkThreading _ARGUMENTS((struct newsgroup *, art_num)); + +static void checkThreading( + _ANSIDECL(struct newsgroup *, newsgroup), + _ANSIDECL(art_num, first) + ) + _KNRDECL(struct newsgroup *, newsgroup) + _KNRDECL(art_num, first) +{ + art_num i; + struct article *art, *parent, copy; + + for (i = first; i <= newsgroup->last; i++) { + art = artStructGet(newsgroup, i, False); + if (art->parent) { + parent = artStructGet(newsgroup, art->parent, False); + if ((art->parent < first) || !IS_LISTED(parent) || !IS_LISTED(art)) { + copy = *parent; + artStructRemoveChild(©, i); + artStructReplace(newsgroup, &parent, ©, art->parent); + copy = *art; + CLEAR_PARENT(©); + artStructReplace(newsgroup, &art, ©, i); + } + } + } +} + + +static void rethreadGroup _ARGUMENTS((struct newsgroup *, art_num, + Boolean)); + +static void rethreadGroup( + _ANSIDECL(struct newsgroup *, newsgroup), + _ANSIDECL(art_num, art), + _ANSIDECL(Boolean, rethread) + ) + _KNRDECL(struct newsgroup *, newsgroup) + _KNRDECL(art_num, art) + _KNRDECL(Boolean, rethread) +{ + art_num i; + + if (rethread) { + /* + We're generating a new list after "List Old" or "Goto article", + which means that articles which weren't displayed before are + displayed now, so we have to reset everything. + + Otherwise, the only thing that might have happened is some + articles getting marked read as a result of cross-posts after + the group was already prefetched, and those will be detected by + checkThreading(), so we don't have to worry about them here. + */ + for (i = art; i <= newsgroup->last; i++) { + struct article *art = artStructGet(newsgroup, i, True); + CLEAR_CHILDREN(art); + CLEAR_PARENT(art); + artStructSet(newsgroup, &art); + } + } + else { + checkThreading(newsgroup, art); + } + + (void) threadIncremental(newsgroup, 0, art, newsgroup->last); +} + +static Boolean fetchHeadersIncremental _ARGUMENTS((struct newsgroup *, + int *, art_num, art_num, + Boolean, Boolean)); + +static Boolean fetchHeadersIncremental( + _ANSIDECL(struct newsgroup *, newsgroup), + _ANSIDECL(int *, stage), + _ANSIDECL(art_num, first), + _ANSIDECL(art_num, last), + _ANSIDECL(Boolean, unread_only), + _ANSIDECL(Boolean, kill_files) + ) + _KNRDECL(struct newsgroup *, newsgroup) + _KNRDECL(int *, stage) + _KNRDECL(art_num, first) + _KNRDECL(art_num, last) + _KNRDECL(Boolean, unread_only) + _KNRDECL(Boolean, kill_files) +{ + static int chunk_size = PREFETCH_CHUNK; + time_t start_time WALL (= 0), end_time; + Boolean finished = True; + + if (stage) { + if ((*stage < PREFETCH_START_HEADERS_STAGE) || + (*stage > PREFETCH_LAST_HEADERS_STAGE)) + return True; + start_time = time(0); + } + + if (! stage || *stage == PREFETCH_SUBJECT_STAGE) + finished = getsubjectlist(newsgroup, first, last, + unread_only, + (stage && ! FinishingPrefetch) + ? chunk_size : 0); + if (! stage || *stage == PREFETCH_AUTHOR_STAGE) + finished = getauthorlist(newsgroup, first, last, + unread_only, + (stage && ! FinishingPrefetch) + ? chunk_size : 0); + if (! stage || *stage == PREFETCH_LINES_STAGE) + finished = getlineslist(newsgroup, first, last, + unread_only, + (stage && ! FinishingPrefetch) + ? chunk_size : 0); + if ((newsgroup->fetch & FETCH_NEWSGROUPS) && kill_files && + (! stage || *stage == PREFETCH_NEWSGROUPS_STAGE)) + finished = getnewsgroupslist(newsgroup, first, last, + unread_only, + (stage && ! FinishingPrefetch) + ? chunk_size : 0); + if ((newsgroup->fetch & FETCH_DATES) && + (! stage || *stage == PREFETCH_DATE_STAGE)) + finished = getdatelist(newsgroup, first, last, + unread_only, + (stage && ! FinishingPrefetch) + ? chunk_size : 0); + if ((newsgroup->fetch & FETCH_IDS) && + (! stage || *stage == PREFETCH_IDS_STAGE)) + finished = getidlist(newsgroup, first, last, + unread_only, + (stage && ! FinishingPrefetch) + ? chunk_size : 0); + if ((newsgroup->fetch & FETCH_XREF) && + (! stage || *stage == PREFETCH_XREF_STAGE)) + finished = getxreflist(newsgroup, first, last, + unread_only, + (stage && ! FinishingPrefetch) + ? chunk_size : 0); + if ((newsgroup->fetch & FETCH_REFS) && + (! stage || *stage == PREFETCH_REFS_STAGE)) + finished = getreflist(newsgroup, first, last, + unread_only, + (stage && ! FinishingPrefetch) + ? chunk_size : 0); + if (! finished) { /* that means it was a full chunk */ + end_time = time(0); + adjustPrefetchChunk(end_time - start_time, + PREFETCH_CHUNK_TIME, &chunk_size); + } + + return finished; +} /* * The reason this is divided into "stages" is so that it can be used @@ -1580,15 +1783,28 @@ * divided up so a single work procedure invocation doesn't take too long. */ static Boolean setUpGroupIncremental _ARGUMENTS((struct newsgroup *, int *, - /* Boolean */ int, - /* Boolean */ int, - /* Boolean */ int)); + Boolean, Boolean, + Boolean, Boolean, + art_num, art_num)); -static Boolean setUpGroupIncremental(newsgroup, stage, update_last, - kill_files, unread_only) - struct newsgroup *newsgroup; - int *stage; - Boolean update_last, kill_files, unread_only; +static Boolean setUpGroupIncremental( + _ANSIDECL(struct newsgroup *, newsgroup), + _ANSIDECL(int *, stage), + _ANSIDECL(Boolean, update_last), + _ANSIDECL(Boolean, kill_files), + _ANSIDECL(Boolean, unread_only), + _ANSIDECL(Boolean, threading), + _ANSIDECL(art_num, first), + _ANSIDECL(art_num, last) + ) + _KNRDECL(struct newsgroup *, newsgroup) + _KNRDECL(int *, stage) + _KNRDECL(Boolean, update_last) + _KNRDECL(Boolean, kill_files) + _KNRDECL(Boolean, unread_only) + _KNRDECL(Boolean, threading) + _KNRDECL(art_num, first) + _KNRDECL(art_num, last) { Boolean finished = True; static int chunk_size = PREFETCH_CHUNK; @@ -1613,7 +1829,7 @@ } if (! PrefetchedGroup) { - if (! stage || *stage == 1) { + if (! stage || *stage == PREFETCH_GETGROUP_STAGE) { art_num first, last; int number; /* @@ -1639,53 +1855,69 @@ * group is, if the update_last argument to setUpGroup is * true. */ - if (getgroup(newsgroup, &first, &last, &number, - stage ? False : True)) - goto done; + (void) getgroup(newsgroup, &first, &last, &number, + stage ? False : True); articleArrayResync(newsgroup, first, update_last ? last : newsgroup->last, number); - - (void) setCurrentArticle(newsgroup, unread_only); } if (! EMPTY_GROUP(newsgroup)) { - long start_time WALL(= 0), end_time; + art_num my_first = first, my_last = last; - if (stage && (*stage > 1) && (*stage < 5)) - start_time = time(0); + if (! stage || *stage == PREFETCH_SETCURRENT_STAGE) + (void) setCurrentArticle(newsgroup, unread_only, False, + (stage && ! FinishingPrefetch) + ? &finished : 0, chunk_size); - if (! stage || *stage == 2) - finished = getsubjectlist(newsgroup, newsgroup->current, - newsgroup->last, unread_only, - (stage && ! FinishingPrefetch) - ? chunk_size : 0); - if (! stage || *stage == 3) - finished = getauthorlist(newsgroup, newsgroup->current, - newsgroup->last, unread_only, - (stage && ! FinishingPrefetch) - ? chunk_size : 0); - if (! stage || *stage == 4) - finished = getlineslist(newsgroup, newsgroup->current, - newsgroup->last, unread_only, - (stage && ! FinishingPrefetch) - ? chunk_size : 0); - if (! finished) { /* that means it was a full chunk */ - end_time = time(0); - adjustPrefetchChunk(end_time - start_time, - PREFETCH_CHUNK_TIME, &chunk_size); - } - if (! stage || *stage == 5) { - if ((app_resources.killFiles == TRUE) && kill_files) - finished = checkKillFiles(newsgroup, - (stage && ! FinishingPrefetch) - ? True : False); - + if (! my_first) + my_first = newsgroup->current; + if (! my_last) + my_last = newsgroup->last; + + if ((! stage) || + ((*stage >= PREFETCH_START_HEADERS_STAGE) && + (*stage <= PREFETCH_LAST_HEADERS_STAGE))) + finished = fetchHeadersIncremental(newsgroup, stage, + my_first, my_last, + unread_only, + kill_files); + + if (! stage || *stage == PREFETCH_KILL_STAGE) { + if ((app_resources.killFiles == TRUE) && kill_files) { + unsigned char fetched = newsgroup->fetch; + + finished = checkKillFiles(newsgroup, + (stage && ! FinishingPrefetch) + ? True : False); + + if (! finished || newsgroup->fetch != fetched) { + int i; + + if (! stage) + return setUpGroupIncremental(newsgroup, stage, + update_last, kill_files, + unread_only, threading, + first, last); + + for (i = PREFETCH_START_OPTIONAL_STAGE; + i <= PREFETCH_LAST_OPTIONAL_STAGE; + i++) + if ((PREFETCH_FIELD_BIT(i) & fetched) != + (PREFETCH_FIELD_BIT(i) & newsgroup->fetch)) { + *stage = i; + finished = False; + break; + } + } + } if (finished) { - if (setCurrentArticle(newsgroup, unread_only)) { + if (setCurrentArticle(newsgroup, unread_only, True, 0, 0)) { if (! stage) return setUpGroupIncremental(newsgroup, stage, update_last, - kill_files, unread_only); + kill_files, unread_only, + threading, + first, last); else { - *stage = 2; + *stage = PREFETCH_SETCURRENT_STAGE; finished = False; } } @@ -1702,6 +1934,8 @@ } } } + if ((! stage || *stage == PREFETCH_THREAD_STAGE) && threading) + finished = threadIncremental(newsgroup, stage, my_first, my_last); } } @@ -1712,7 +1946,6 @@ prefetchNextGroup(newsgroup); #endif - done: #ifdef DEBUG fprintf(stderr, "setUpGroupIncremental(%s, %d, %d, %d, %d) = %d\n", newsgroup->name, stage ? *stage : 0, update_last, kill_files, @@ -1721,19 +1954,28 @@ return finished; } -static void setUpGroup _ARGUMENTS((struct newsgroup *, /* Boolean */ int, - /* Boolean */ int, /* Boolean */ int)); +static void setUpGroup _ARGUMENTS((struct newsgroup *, Boolean, + Boolean, Boolean, Boolean)); -static void setUpGroup(newsgroup, update_last, kill_files, unread_only) - struct newsgroup *newsgroup; - Boolean update_last, kill_files, unread_only; +static void setUpGroup( + _ANSIDECL(struct newsgroup *, newsgroup), + _ANSIDECL(Boolean, update_last), + _ANSIDECL(Boolean, kill_files), + _ANSIDECL(Boolean, unread_only), + _ANSIDECL(Boolean, threading) + ) + _KNRDECL(struct newsgroup *, newsgroup) + _KNRDECL(Boolean, update_last) + _KNRDECL(Boolean, kill_files) + _KNRDECL(Boolean, unread_only) + _KNRDECL(Boolean, threading) { #ifdef DEBUG fprintf(stderr, "setUpGroup(%s, %d, %d, %d)\n", newsgroup->name, update_last, kill_files, unread_only); #endif (void) setUpGroupIncremental(newsgroup, 0, update_last, - kill_files, unread_only); + kill_files, unread_only, threading, 0, 0); #ifdef DEBUG fprintf(stderr, "setUpGroup(%s, %d, %d, %d) done\n", newsgroup->name, update_last, kill_files, unread_only); @@ -1798,7 +2040,7 @@ regfree(&reStruct); #else # ifdef SYSV_REGEX - FREE(reRet); + free(reRet); # endif #endif @@ -1861,7 +2103,7 @@ if (! getgroup(newsgroup, &first, &last, &number, True)) { SET_SUB(newsgroup); articleArrayResync(newsgroup, first, last, number); - updateArticleArray(newsgroup); + (void) updateArticleArray(newsgroup, False); } } return; @@ -1901,10 +2143,11 @@ * returns: the question in a static area * */ -static char * buildQuestion _ARGUMENTS((struct newsgroup *)); +static char * buildQuestion _ARGUMENTS((struct newsgroup *, art_num)); -static char * buildQuestion(newsgroup) +static char * buildQuestion(newsgroup, article) struct newsgroup *newsgroup; + art_num article; { static char dummy[LABEL_SIZE]; long unread, next_unread WALL(= 0); @@ -1929,22 +2172,22 @@ if (found) { if (unread <= 0) { (void) sprintf(dummy, QUEST_ART_NOUNREAD_NEXT_STRING, - newsgroup->current, newsgroup->name, + article, newsgroup->name, nextnewsgroup->name, next_unread, (next_unread == 1) ? "" : NOT_ONE_MSG); } else { (void) sprintf(dummy, QUEST_ART_UNREAD_NEXT_STRING, - newsgroup->current, newsgroup->name, unread, + article, newsgroup->name, unread, nextnewsgroup->name, next_unread, (next_unread == 1) ? "" : NOT_ONE_MSG); } } else { if (unread <= 0) { (void) sprintf(dummy, QUEST_ART_NOUNREAD_NONEXT_STRING, - newsgroup->current, newsgroup->name); + article, newsgroup->name); } else { (void) sprintf(dummy, QUEST_ART_UNREAD_NONEXT_STRING, - newsgroup->current, newsgroup->name, unread); + article, newsgroup->name, unread); } } return dummy; @@ -1953,14 +2196,27 @@ static void handleXref _ARGUMENTS((struct newsgroup *, art_num)); -static void handleXref(newsgroup, article) - struct newsgroup *newsgroup; +static void handleXref(cur_newsgroup, article) + struct newsgroup *cur_newsgroup; art_num article; { char *string, *ptr, *num_ptr, *group, *gptr; art_num number; + struct newsgroup *newsgroup; + struct article *this_art; - xhdr(newsgroup, article, "xref", &string); + this_art = artStructGet(cur_newsgroup, article, False); + + if (IS_XREFED(this_art)) + return; + + if (this_art->xref) + if (*this_art->xref) + string = XtNewString(this_art->xref); + else + string = NULL; + else + xhdr(cur_newsgroup, article, "xref", &string); if (string == NIL(char)) { /* no xrefs */ @@ -1999,14 +2255,35 @@ newsgroup = (struct newsgroup *) gptr; - if (IS_SUBSCRIBED(newsgroup) && - (number >= newsgroup->first) && (number <= newsgroup->last)) { - struct article *art; + if (IS_SUBSCRIBED(newsgroup)) { + struct article *art; - artListSet(newsgroup); - art = artStructGet(newsgroup, number, True); + if (number < newsgroup->first) + continue; + if (number > newsgroup->last) + if (app_resources.rescanOnEnter) { + art_num first, last; + int count; + + if (getgroup(newsgroup, &first, &last, &count, False) != NO_GROUP) + if ((number >= first) && (number <= last)) + articleArrayResync(newsgroup, first, last, count); + else + continue; + else + continue; + } + else + continue; + + artListSet(newsgroup); + art = artStructGet(newsgroup, number, True); + SET_XREFED(art); + /* Don't mark read in the current newsgroup; that's the caller's + responsibility. */ + if ((newsgroup != cur_newsgroup) || (article != number)) SET_READ(art); - artStructSet(newsgroup, &art); + artStructSet(newsgroup, &art); } } FREE(string); @@ -2020,31 +2297,38 @@ * returns: XRN_ERROR - article has been canceled * XRN_OKAY - article returned */ -int getArticle(filename, question) - char **filename; +int getArticle(newsgroup, article, file, question) + struct newsgroup *newsgroup; + art_num article; + file_cache_file **file; char **question; { - struct newsgroup *newsgroup = CurrentGroup; - struct article *art = artStructGet(newsgroup, newsgroup->current, True); - int header, rotation; - int xlation = 0; + struct article *art = artStructGet(newsgroup, article, True); + file_cache_file *artfile; + int header = 0, rotation = 0, xlation = 0; #ifdef DEBUG - fprintf(stderr, "getArticle(%s)\n", newsgroup->name); + fprintf(stderr, "getArticle(%s, %d)\n", newsgroup->name, article); #endif if (IS_UNFETCHED(art)) { /* get the article and handle unavailable ones.... */ - header = (IS_ALL_HEADERS(art) ? FULL_HEADER : NORMAL_HEADER); - rotation = (IS_ROTATED(art) ? ROTATED : NOT_ROTATED); + if (IS_ALL_HEADERS(art)) + header = FULL_HEADER; + if (IS_ROTATED(art)) + rotation = ROTATED; #ifdef XLATE - xlation = (IS_XLATED(art) ? XLATED : NOT_XLATED); + if (IS_XLATED(art)) + xlation = XLATED; #endif - if (! (art->filename = utGetarticle(newsgroup, CurrentGroup->current, 0, - header, rotation, xlation))) { - CLEAR_SUBJECT(art); - CLEAR_AUTHOR(art); - CLEAR_LINES(art); + artfile = getarticle(newsgroup, article, 0, header | rotation | + xlation | PAGEBREAKS | BACKSPACES); + /* need to refetch it since getarticle() modifies it */ + art = artStructGet(newsgroup, article, True); + if (artfile) + art->file = artfile; + else { + CLEAR_ALL(art); SET_UNAVAIL(art); artStructSet(newsgroup, &art); return XRN_ERROR; @@ -2052,30 +2336,32 @@ SET_FETCHED(art); } else { /* verify that the file still exists */ - if (access(art->filename, R_OK) == -1) { + if (!art->file || !*art->file || + (access(file_cache_file_name(FileCache, *art->file), R_OK) == -1)) { /* refetch the file */ - SET_UNFETCHED(art); + CLEAR_FILE(art); artStructSet(newsgroup, &art); - return getArticle(filename, question); + return getArticle(newsgroup, article, file, question); } + file_cache_file_lock(FileCache, *art->file); } - *filename = art->filename; + *file = art->file; if (IS_UNMARKED(art)) { SET_READ(art); } - *question = buildQuestion(newsgroup); - handleXref(newsgroup, newsgroup->current); + *question = buildQuestion(newsgroup, article); + handleXref(newsgroup, article); artStructSet(newsgroup, &art); return XRN_OKAY; } -static int toggleArtAttribute _ARGUMENTS((char **, char **, int)); +static int toggleArtAttribute _ARGUMENTS((file_cache_file **, char **, int)); -static int toggleArtAttribute(filename, question, attribute) - char **filename; +static int toggleArtAttribute(file, question, attribute) + file_cache_file **file; char **question; int attribute; { @@ -2110,33 +2396,33 @@ artStructSet(newsgroup, &art); - ret = getArticle(filename, question); + ret = getArticle(newsgroup, newsgroup->current, file, question); if (ret != XRN_OKAY) { mesgPane(XRN_SERIOUS, 0, ART_NOT_AVAIL_MSG, newsgroup->current); } return ret; } -int toggleHeaders(filename, question) - char **filename; +int toggleHeaders(file, question) + file_cache_file **file; char **question; { - return toggleArtAttribute(filename, question, ART_ALL_HEADERS); + return toggleArtAttribute(file, question, ART_ALL_HEADERS); } -int toggleRotation(filename, question) - char **filename; +int toggleRotation(file, question) + file_cache_file **file; char **question; { - return toggleArtAttribute(filename, question, ART_ROTATED); + return toggleArtAttribute(file, question, ART_ROTATED); } #ifdef XLATE -int toggleXlation(filename, question) - char **filename; +int toggleXlation(file, question) + file_cache_file **file; char **question; { - return toggleArtAttribute(filename, question, ART_XLATED); + return toggleArtAttribute(file, question, ART_XLATED); } #endif /* XLATE */ @@ -2185,12 +2471,20 @@ struct article *art = artStructGet(newsgroup, newsgroup->current, True); if (IS_UNFETCHED(art)) { + file_cache_file *artfile; + /* if the article can be fetched, mark it so */ - if ((art->filename = utGetarticle(newsgroup, newsgroup->current, 0, - NORMAL_HEADER, NOT_ROTATED, - NOT_XLATED))) - SET_FETCHED(art); + artfile = getarticle(newsgroup, newsgroup->current, 0, + PAGEBREAKS | BACKSPACES); + /* need to refetch it since getarticle() modifies it */ + art = artStructGet(newsgroup, newsgroup->current, True); + + if (artfile) { + art->file = artfile; + SET_FETCHED(art); + } } + artStructSet(newsgroup, &art); } @@ -2318,14 +2612,16 @@ goto done; } - if (PrefetchStage <= 5) { + if (PrefetchStage <= PREFETCH_LAST_STAGE) { #ifdef DEBUG fprintf(stderr, "prefetchSetupUnreadGroup[%d] running stage %d on %s\n", mesg_name, PrefetchStage, newsgroup->name); #endif if (setUpGroupIncremental(newsgroup, &PrefetchStage, - app_resources.rescanOnEnter, True, True)) + app_resources.rescanOnEnter, + True, True, True, + 0, 0)) PrefetchStage++; ret = False; goto done; @@ -2387,7 +2683,7 @@ if ((! app_resources.prefetchMax) || (unreadArticleCount(newsgroup) <= app_resources.prefetchMax)) { PrefetchingGroup = newsgroup; - PrefetchStage = 1; + PrefetchStage = PREFETCH_FIRST_STAGE; (void) sprintf(msg, PREFETCHING_MSG, newsgroup->name); #ifdef DEBUG fprintf(stderr, "prefetchNextGroup[%d] continuing with %s\n", @@ -2471,15 +2767,21 @@ if (FastServer) { art_num artnum = newsgroup->current + 1; struct article *art = artStructGet(newsgroup, artnum, True); + file_cache_file *artfile; - if (IS_UNFETCHED(art) && - (art->filename = utGetarticle(newsgroup, artnum, 0, - NORMAL_HEADER, NOT_ROTATED, - NOT_XLATED))) { + if (IS_UNFETCHED(art)) { + artfile = getarticle(newsgroup, artnum, 0, PAGEBREAKS | BACKSPACES); + /* need to refetch it since getarticle() modifies it */ + art = artStructGet(newsgroup, artnum, True); + + if (artfile) { + file_cache_file_unlock(FileCache, *artfile); + art->file = artfile; SET_FETCHED(art); SET_STRIPPED_HEADERS(art); SET_UNROTATED(art); SET_UNXLATED(art); + } } artStructSet(newsgroup, &art); @@ -2491,11 +2793,16 @@ /* * mark the articles in a group that have been read * - * returns: void + * returns: True on success, False on failure indicating that the + * group should not be entered. * */ -void updateArticleArray(newsgroup) - struct newsgroup *newsgroup; /* newsgroup to update article array for */ +Boolean updateArticleArray( + _ANSIDECL(struct newsgroup *, newsgroup), + _ANSIDECL(Boolean, do_unsubbed) + ) + _KNRDECL(struct newsgroup *, newsgroup) + _KNRDECL(Boolean, do_unsubbed) { struct list *item; struct article *art, copy; @@ -2505,7 +2812,7 @@ #endif if (newsgroup->last == 0) { - return; + return True; } #define CHECK_CACHED(label) \ @@ -2513,29 +2820,32 @@ art_num first, last; \ int number; \ \ - if (getgroup(newsgroup, &first, &last, &number, False) == 0) { \ + if (getgroup(newsgroup, &first, &last, &number, False)) { \ + return False; \ + } \ + else { \ articleArrayResync(newsgroup, first, last, number); \ newsgroup->from_cache = FALSE; \ goto label; \ } \ } - if (!IS_SUBSCRIBED(newsgroup)) { + if (!(do_unsubbed || IS_SUBSCRIBED(newsgroup))) { artListFree(newsgroup); - return; + return True; } empty_retry: if (EMPTY_GROUP(newsgroup)) { CHECK_CACHED(empty_retry); artListFree(newsgroup); - return; + return True; } artListSet(newsgroup); if (newsgroup->nglist == NIL(struct list)) { - return; + return True; } #ifndef FIXED_C_NEWS_ACTIVE_FILE @@ -2550,7 +2860,7 @@ if ((newsgroup->first == 0 && newsgroup->last == 0) || number == 0) { lsDestroy(newsgroup->nglist); newsgroup->nglist = NIL(struct list); - return; + return True; } } #endif @@ -2570,7 +2880,7 @@ artListSet(newsgroup); lsDestroy(newsgroup->nglist); newsgroup->nglist = NIL(struct list); - return; + return True; } if (item->contents.single >= newsgroup->first) { struct article *art = artStructGet(newsgroup, @@ -2592,7 +2902,7 @@ artListSet(newsgroup); lsDestroy(newsgroup->nglist); newsgroup->nglist = NIL(struct list); - return; + return True; } if (item->contents.range.start < newsgroup->first) { item->contents.range.start = newsgroup->first; @@ -2616,7 +2926,7 @@ lsDestroy(newsgroup->nglist); newsgroup->nglist = NIL(struct list); - return; + return True; } @@ -2686,42 +2996,41 @@ */ int addToNewsrcBeginning(newGroup, status) - char *newGroup; - int status; + char *newGroup; + int status; { - struct newsgroup *newsgroup; - ng_num i; + struct newsgroup *newsgroup; + ng_num i; - if (! verifyGroup(newGroup, &newsgroup)) { - mesgPane(XRN_SERIOUS, 0, NO_SUCH_NG_MSG, newGroup); - return BAD_GROUP; - } + if (! verifyGroup(newGroup, &newsgroup, False)) { + mesgPane(XRN_SERIOUS, 0, NO_SUCH_NG_MSG, newGroup); + return BAD_GROUP; + } - CLEAR_NOENTRY(newsgroup); - if (status == SUBSCRIBE) { - subscribe_group(newsgroup); - } else { - SET_UNSUB(newsgroup); + CLEAR_NOENTRY(newsgroup); + if (status == SUBSCRIBE) { + subscribe_group(newsgroup); + } else { + SET_UNSUB(newsgroup); + } + if (newsgroup->newsrc == NOT_IN_NEWSRC) { + for (i = MaxGroupNumber - 1; i != NOT_IN_NEWSRC; i--) { + Newsrc[i + 1] = Newsrc[i]; + Newsrc[i + 1]->newsrc = i + 1; } - if (newsgroup->newsrc == NOT_IN_NEWSRC) { - for (i = MaxGroupNumber - 1; i >= 0; i--) { - Newsrc[i + 1] = Newsrc[i]; - Newsrc[i + 1]->newsrc = i + 1; - } - - MaxGroupNumber++; - - } else { - for (i = newsgroup->newsrc - 1; i >= 0; i--) { - Newsrc[i + 1] = Newsrc[i]; - Newsrc[i + 1]->newsrc = i + 1; - } + + INC_MAXGROUPNUMBER(); + } else { + for (i = newsgroup->newsrc - 1; i != NOT_IN_NEWSRC; i--) { + Newsrc[i + 1] = Newsrc[i]; + Newsrc[i + 1]->newsrc = i + 1; } + } - newsgroup->newsrc = 0; - Newsrc[0] = newsgroup; + newsgroup->newsrc = 0; + Newsrc[0] = newsgroup; - return GOOD_GROUP; + return GOOD_GROUP; } @@ -2732,7 +3041,7 @@ struct newsgroup *newsgroup; ng_num i; - if (! verifyGroup(newGroup, &newsgroup)) { + if (! verifyGroup(newGroup, &newsgroup, False)) { mesgPane(XRN_SERIOUS, 0, NO_SUCH_NG_MSG, newGroup); return BAD_GROUP; } @@ -2744,7 +3053,7 @@ SET_UNSUB(newsgroup); } if (newsgroup->newsrc == NOT_IN_NEWSRC) { - MaxGroupNumber++; + INC_MAXGROUPNUMBER(); } else { for (i = newsgroup->newsrc + 1; i < MaxGroupNumber; i++) { Newsrc[i - 1] = Newsrc[i]; @@ -2767,7 +3076,7 @@ struct newsgroup *newsgroup, *ng; ng_num newloc, i; - if (! verifyGroup(newGroup, &newsgroup)) { + if (! verifyGroup(newGroup, &newsgroup, False)) { mesgPane(XRN_SERIOUS, 0, NO_SUCH_NG_MSG, newGroup); return BAD_GROUP; } @@ -2780,19 +3089,26 @@ } if (! avl_lookup(NewsGroupTable, afterGroup, (char **) &ng)) { + Boolean no_group = True; + if (! active_read) { char *err_buf; err_buf = XtMalloc(strlen(MAYBE_LIST_MSG)+strlen(afterGroup)); (void) sprintf(err_buf, MAYBE_LIST_MSG, afterGroup); - if ((ConfirmationBox(TopLevel, err_buf, "yes", "no", True) == - XRN_CB_ABORT) || (! verifyGroup(afterGroup, &ng))) { - mesgPane(XRN_SERIOUS, 0, NO_SUCH_NG_MSG, afterGroup); - XtFree(err_buf); - return BAD_GROUP; - } + + no_group = + (ConfirmationBox(TopLevel, err_buf, "yes", "no", True) == + XRN_CB_ABORT) || + (! verifyGroup(afterGroup, &ng, False)); + XtFree(err_buf); } + + if (no_group) { + mesgPane(XRN_SERIOUS, 0, NO_SUCH_NG_MSG, afterGroup); + return BAD_GROUP; + } } newloc = ng->newsrc; @@ -2808,7 +3124,7 @@ Newsrc[i + 1]->newsrc = i + 1; } - MaxGroupNumber++; + INC_MAXGROUPNUMBER(); newsgroup->newsrc = newloc + 1; Newsrc[newloc + 1] = newsgroup; @@ -2842,7 +3158,7 @@ { struct newsgroup *newsgroup; - if (! verifyGroup(group, &newsgroup)) { + if (! verifyGroup(group, &newsgroup, False)) { mesgPane(XRN_SERIOUS, 0, NO_SUCH_NG_MSG, group); return BAD_GROUP; } @@ -2863,7 +3179,7 @@ struct newsgroup *newsgroup; int i; - if (! verifyGroup(group, &newsgroup)) { + if (! verifyGroup(group, &newsgroup, False)) { mesgPane(XRN_SERIOUS, 0, NO_SUCH_NG_MSG, group); return BAD_GROUP; } @@ -2894,10 +3210,15 @@ * * if sorted is non-zero, the list is sorted alphabetically, if * zero, the list is returned as it exists in the newsrc file + * + * The specified regular expression limits the list to the newsgroups + * whose names match it. May return NULL if there's an error in the + * regular expression; otherwise, will never return NULL. */ -char * getStatusString(line_width, sorted) +char * getStatusString(line_width, sorted, regexp) int line_width; int sorted; + char *regexp; { int i, count = 0, bytes = 0; char buffer[1024]; @@ -2907,6 +3228,46 @@ char *string; static int status_width = 0; int newsgroup_width; +#ifdef POSIX_REGEX + regex_t reStruct; + int reRet; +#else +# ifdef SYSV_REGEX + char *reRet = 0; +# else + char *reRet; +# endif +#endif + + if (regexp) { + if ( +#ifdef POSIX_REGEX + (reRet = regcomp(&reStruct, regexp, REG_NOSUB)) +#else +# ifdef SYSV_REGEX + ! (reRet = regcmp(regexp, NULL)) +# else + (reRet = re_comp(regexp)) +# endif +#endif + ) { +#ifdef SYSV_REGEX + mesgPane(XRN_SERIOUS, 0, UNKNOWN_REGEXP_ERROR_MSG, regexp); +#else +# ifdef POSIX_REGEX + regerror(reRet, &reStruct, error_buffer, sizeof(error_buffer)); +# endif + mesgPane(XRN_SERIOUS, 0, KNOWN_REGEXP_ERROR_MSG, regexp, +# ifdef POSIX_REGEX + error_buffer +# else + reRet +# endif /* POSIX_REGEX */ + ); +#endif /* SYSV_REGEX */ + return NULL; + } + } if (! status_width) status_width = MAX(utStrlen(IGNORED_MSG), @@ -2925,7 +3286,20 @@ while (avl_gen(gen, &key, &value)) { struct newsgroup *newsgroup = (struct newsgroup *) value; - + + if (regexp && +#ifdef POSIX_REGEX + regexec(&reStruct, newsgroup->name, 0, 0, 0) +#else +# ifdef SYSV_REGEX + ! regex(reRet, newsgroup->name) +# else + ! re_exec(newsgroup->name) +# endif +#endif + ) + continue; + (void) sprintf(buffer, "%*s %s", -newsgroup_width, newsgroup->name, (IS_NOENTRY(newsgroup) ? IGNORED_MSG : @@ -2939,6 +3313,19 @@ for (i = 0; i < MaxGroupNumber; i++) { struct newsgroup *newsgroup = (struct newsgroup *) Newsrc[i]; + if (regexp && +#ifdef POSIX_REGEX + regexec(&reStruct, newsgroup->name, 0, 0, 0) +#else +# ifdef SYSV_REGEX + ! regex(reRet, newsgroup->name) +# else + ! re_exec(newsgroup->name) +# endif +#endif + ) + continue; + (void) sprintf(buffer, "%*s %s", -newsgroup_width, newsgroup->name, (IS_SUBSCRIBED(newsgroup) ? SUBED_MSG : UNSUBED_MSG)); @@ -2951,6 +3338,20 @@ ehErrorExitXRN(ERROR_OUT_OF_MEM_MSG); while (avl_gen(gen, &key, &value)) { struct newsgroup *newsgroup = (struct newsgroup *) value; + + if (regexp && +#ifdef POSIX_REGEX + regexec(&reStruct, newsgroup->name, 0, 0, 0) +#else +# ifdef SYSV_REGEX + ! regex(reRet, newsgroup->name) +# else + ! re_exec(newsgroup->name) +# endif +#endif + ) + continue; + if (IS_NOENTRY(newsgroup)) { (void) sprintf(buffer, "%*s %s", -newsgroup_width, newsgroup->name, @@ -2967,10 +3368,22 @@ FREE(ar[i]); } FREE(ar); - + + if (regexp) { +#ifdef POSIX_REGEX + regfree(&reStruct); +#else +# ifdef SYSV_REGEX + free(reRet); +# endif +#endif + } + return string; } + + /* If art_num is greater than 0, format a single line in the subject index, and return its length (including the final newline). If @@ -2980,24 +3393,35 @@ called with art_num <= 0 before starting any series of consecutive calls to it. */ -static int subjectIndexLine _ARGUMENTS((int, char *, struct newsgroup *, art_num)); - -static int subjectIndexLine(line_length, out, newsgroup, artNum) - int line_length; - char *out; - struct newsgroup *newsgroup; - art_num artNum; +int subjectIndexLine( + _ANSIDECL(int, line_length), + _ANSIDECL(char *, out), + _ANSIDECL(struct newsgroup *, newsgroup), + _ANSIDECL(art_num, artNum), + _ANSIDECL(Boolean, threaded) + ) + _KNRDECL(int, line_length) + _KNRDECL(char *, out) + _KNRDECL(struct newsgroup *, newsgroup) + _KNRDECL(art_num, artNum) + _KNRDECL(Boolean, threaded) { static int lineLength, subLength, authorLength, lineWidth; - struct article *art; + static int num_width; + struct article *art, *parent; char *subject, *lines, *author; - + int thread_depth, ret; + if (artNum <= 0) { + num_width = utDigits(newsgroup->last); lineLength = line_length; lineLength = MIN(LINE_LENGTH - 1, lineLength); /* for the NULL */ subLength = lineLength - - 2 /* spaces after subject, or spaces before and after line count */ - - 1 /* newline */; + 3 /* spaces before and after article number */ - + num_width /* width of article number */ - + 2 /* spaces after subject, or spaces before and + after line count */ - + 1 /* newline */; if (app_resources.displayLineCount) { lineWidth = 6; subLength -= lineWidth; @@ -3021,19 +3445,71 @@ art = artStructGet(newsgroup, artNum, False); if (app_resources.displayLineCount) { - lines = art->lines ? art->lines : "[?]"; + lines = (art->lines && *art->lines) ? art->lines : "[?]"; } else { lines = ""; } + if (threaded) { + hash_table_object loop_table = 0; + + recheck: + thread_depth = 0; + parent = art; + if (loop_table) { + ret = hash_table_insert(loop_table, (void *)artNum, (void *)1, 1); + assert(ret); + } + while (parent->parent) { + if (loop_table && + ! hash_table_insert(loop_table, (void *)parent->parent, + (void *)1, 1)) { + hash_table_destroy(loop_table); + break; + } + if (++thread_depth == subLength) { + if (loop_table) { + hash_table_destroy(loop_table); + break; + } + else { + /* Heavily nested threading could really be authentic, or + it could be because of a loop in "References" + dependencies. To find out which it is, we create a + hash table to keep track of which articles we've seen, + and then redo the thread-depth check. + + We only create this hash table when we reach the + boundary condition, rather than for every article, + because creating the hash table and inserting the + articles number into it is expensive (well, at least, + more expensive than not doing it), and we want this + code to be as fast as possible. */ + loop_table = hash_table_create(subLength + 1, + hash_int_calc, hash_int_compare, + hash_int_compare, 0, 0); + goto recheck; + } + } + + parent = artStructGet(newsgroup, parent->parent, False); + } + } + else { + thread_depth = 0; + } + subject = art->subject; author = art->author ? art->author : "(none)"; - (void) sprintf(out, "%*.*s %*.*s %*.*s\n", -subLength, subLength, - subject, lineWidth, lineWidth, lines, - -authorLength, authorLength, author); + (void) sprintf(out, " %*ld %*.*s%*.*s %*.*s %*.*s\n", num_width, + artNum, thread_depth, thread_depth, "", + -(subLength-thread_depth), subLength-thread_depth, + subject, lineWidth, + lineWidth, lines, -authorLength, authorLength, + author); if (IS_READ(art)) *out = READ_MARKER; else if (IS_MARKED(art)) @@ -3048,267 +3524,22 @@ -/* - * build and return the subjects string - */ -static char * getUnSortedSubjects _ARGUMENTS((int, int, art_num)); - -static char * getUnSortedSubjects(line_length, mode, artNum) - int line_length; - int mode; - art_num artNum; -{ - struct newsgroup *newsgroup = CurrentGroup; - struct article *art; - int lineLength; - art_num i; - char *start, *end; - - if (EMPTY_GROUP(newsgroup)) { - return XtNewString(""); - } - - artNum = MAX(newsgroup->first, artNum); - - lineLength = subjectIndexLine(line_length, 0, newsgroup, 0); - - NextPreviousArticle = artNum - 1; - - if ((newsgroup->last - artNum + 1) < 0) { - (void) sprintf(error_buffer, "Active File Error: last - artNum + 1 < 0 (%s)\n", - newsgroup->name); - ehErrorExitXRN(error_buffer); - } - - start = ARRAYALLOC(char, ((newsgroup->last - artNum + 1) * lineLength + 1)); - end = start; - *start = '\0'; - - for (i = artNum; i <= newsgroup->last; i++) { - art = artStructGet(newsgroup, i, False); - - /* canceled and empty articles will not have a subject entry */ - if (art->subject && ((mode == ALL) || (IS_UNREAD(art) && IS_AVAIL(art)))) { - (void) subjectIndexLine(line_length, end, newsgroup, i); - end += lineLength; - } - } - - return start; -} - - -struct entry { - char *beginning; - char *end; - int left; - int size; - int startingArticle; -}; - - -static void valfree _ARGUMENTS((void *)); - -static void valfree(p) - void *p; -{ - struct entry *val = (struct entry *)p; - XtFree(val->beginning); - XtFree((char *) val); - return; -} - - -#if defined(__osf__) || defined(_POSIX_SOURCE) || defined(SOLARIS) -static int pteCompare _ARGUMENTS((CONST void *, CONST void *)); -#else -static int pteCompare _ARGUMENTS((void *, void *)); -#endif - -static int pteCompare(a, b) -#if defined(__osf__) || defined(_POSIX_SOURCE) || defined(SOLARIS) - CONST void *a, *b; -#else - void *a, *b; -#endif -{ - struct entry **pa = (struct entry **) a; - struct entry **pb = (struct entry **) b; - - return (*pa)->startingArticle - (*pb)->startingArticle; -} - - -/* - * XXX AVL TREE is the wrong data structure here, hash table would be - * better.... no need for ordering based on subject string - */ -/* - * build and return the subjects string - */ - -#define CHUNK (4 * lineLength) - -static char * getSortedSubjects _ARGUMENTS((int, int, art_num)); - -static char * getSortedSubjects(line_length, mode, artNum) - int line_length; - int mode; - art_num artNum; -{ - struct newsgroup *newsgroup = CurrentGroup; - struct article *art; - int lineLength; - art_num i; - char *start, *end; - - struct entry *pte; - struct entry **pteArray; - - avl_generator *gen; - char *key, *ptr; - avl_tree *tree; - char curSub[80]; - int treeSize, sz; - - if (EMPTY_GROUP(newsgroup)) { - return XtNewString(""); - } - - artNum = MAX(newsgroup->first, artNum); - - lineLength = subjectIndexLine(line_length, 0, newsgroup, 0); - - tree = avl_init_table(utSubjectCompare); - if (! tree) { - ehErrorExitXRN(ERROR_OUT_OF_MEM_MSG); - } - - NextPreviousArticle = artNum - 1; - - if ((newsgroup->last - artNum + 1) < 0) { - (void) sprintf(error_buffer, "Active File Error: last - artNum + 1 < 0 (%s)\n", - newsgroup->name); - ehErrorExitXRN(error_buffer); - } - - /* - * build the subject groups - */ - for (i = artNum; i <= newsgroup->last; i++) { - char buffer[LINE_LENGTH]; - - art = artStructGet(newsgroup, i, False); - - /* canceled and empty articles will not have a subject entry */ - if (art->subject && ((mode == ALL) || (IS_UNREAD(art) && IS_AVAIL(art)))) { - (void) subjectIndexLine(line_length, buffer, newsgroup, i); - (void) strncpy(curSub, getSubject(i), sizeof(curSub)); - - if (avl_lookup(tree, curSub, &ptr)) { - /* add to the end */ - pte = (struct entry *) ptr; - if (lineLength >= pte->left) { - /* grow the string */ - pte->size += CHUNK; - pte->left += CHUNK; - sz = pte->end - pte->beginning; - pte->beginning = XtRealloc(pte->beginning, pte->size); - pte->end = pte->beginning + sz; - *(pte->end) = '\0'; - } - (void) strcpy(pte->end, buffer); - pte->end += lineLength; - pte->left -= lineLength; - *(pte->end) = '\0'; - } else { - /* create new */ - pte = ALLOC(struct entry); - pte->startingArticle = i; - pte->beginning = ARRAYALLOC(char, CHUNK); - (void) strcpy(pte->beginning, buffer); - pte->size = CHUNK; - pte->left = pte->size - lineLength; - pte->end = pte->beginning + lineLength; - *(pte->end) = '\0'; - if (avl_insert(tree, XtNewString(curSub), (char *) pte) - < 0) { - ehErrorExitXRN(ERROR_OUT_OF_MEM_MSG); - } - } - } - } - - i = 0; - treeSize = avl_count(tree); - pteArray = ARRAYALLOC(struct entry *, treeSize); - gen = avl_init_gen(tree, AVL_FORWARD); - if (! gen) { - ehErrorExitXRN(ERROR_OUT_OF_MEM_MSG); - } - while (avl_gen(gen, &key, &ptr)) { - pteArray[i++] = (struct entry *) ptr; - } - avl_free_gen(gen); - - /* sort by article number */ - qsort((char *) pteArray, treeSize, sizeof(struct pte *), pteCompare); - - start = ARRAYALLOC(char, ((newsgroup->last - artNum + 1) * lineLength + 1)); - end = start; - *start = '\0'; - - for (i = 0; i < treeSize; i++) { - (void) strcpy(end, pteArray[i]->beginning); - end += utStrlen(pteArray[i]->beginning); - *end = '\0'; - } - - avl_free_table(tree, XtFree, valfree); - FREE(pteArray); - - return start; -} - - - -char * getSubjects(line_length, mode, artNum) - int line_length; - int mode; - art_num artNum; +char * getSubjects( + _ANSIDECL(int, line_length), + _ANSIDECL(art_num, artNum), + _ANSIDECL(Boolean, rethread) + ) + _KNRDECL(int, line_length) + _KNRDECL(art_num, artNum) + _KNRDECL(Boolean, rethread) { - if (app_resources.sortedSubjects) { - return getSortedSubjects(line_length, mode, artNum); - } else { - return getUnSortedSubjects(line_length, mode, artNum); - } -} - + NextPreviousArticle = artNum - 1; -/* - * set the internal pointers to a particular article - */ -void gotoArticle(article) - art_num article; -{ - CurrentGroup->current = article; -} + rethreadGroup(CurrentGroup, artNum, rethread); -/* - Return the first article in the current newsgroup. - */ -art_num firstArticle() -{ - return CurrentGroup->first; + return art_sort_doit(CurrentGroup, artNum, CurrentGroup->last, line_length); } -/* - Return the current article in the current newsgroup. - */ -art_num currentArticle() -{ - return CurrentGroup->current; -} int checkArticle(art) art_num art; @@ -3323,12 +3554,11 @@ } -#define STRIPLEADINGSPACES for (; *start == ' ' || *start == '\t'; start++); -#define STRIPENDINGSPACES for ( ; *end == ' ' || *end == '\t'; *end = '\0', end--); +#define STRIPLEADINGSPACES for (; *start && isspace(*start); start++); +#define STRIPENDINGSPACES for (; (end >= start) && isspace(*end); *end-- = '\0'); -static char * strip(str, striprefs) +char * subjectStrip(str) char *str; - Boolean striprefs; { register char *start, *end, *ptr; static char work[BUFFER_SIZE]; @@ -3337,51 +3567,29 @@ start = work; work[BUFFER_SIZE - 1] = '\0'; - /* A space separates the article number from the subject line, but - there also may be spaces in the first two columns, as well as - more in subsequent columns if the number of digits in the - unread article numbers are not all the same. */ - for (start += 2; *start == ' '; start++) /* empty */; - start = index(start, ' '); - assert(start); - start++; - - STRIPLEADINGSPACES; - /* * strip leading '[rR][eE]: ' and 'Re^N: ' - * only if striprefs is TRUE (want to be able to kill follow-ups) */ - if (striprefs) { - while (STREQN(start, "Re: ", 4) || - STREQN(start, "RE: ", 4) || - STREQN(start, "re: ", 4) || - STREQN(start, "Re: ", 4)) { - start += 4; - - /* strip leading spaces after '[rR]e: ' */ - STRIPLEADINGSPACES; - } + while (! strncasecmp(start, "re: ", 4)) { + start += 4; + STRIPLEADINGSPACES; + } - while (STREQN(start, "Re^", 3)) { - start += 3; - ptr = index(start, ':'); - if (ptr != NIL(char)) { - start = ptr + 1; - } - STRIPLEADINGSPACES; - } - - for (end = start; (end = index(end, '(')) != NULL;) - if (STREQN(end,"(was:",5) - || STREQN(end,"(Was:",5) - || STREQN(end,"(WAS:",5)) { - *end = '\0'; - break; - } - else ++end; + while (! strncasecmp(start, "re^", 3)) { + start += 3; + if ((ptr = index(start, ':'))) + start = ptr + 1; + STRIPLEADINGSPACES; } + for (end = start + 1; (end = index(end, '(')); end++) + if (! strncasecmp(end, "(was", 4) && + ((end[4] == ':') || (end[4] == ' '))) { + *end = '\0'; + break; + } + end = index(start, '\0') - 1; STRIPENDINGSPACES; @@ -3402,7 +3610,7 @@ artListSet(newsgroup); art = artStructGet(newsgroup, article, False); - return art->subject ? strip(art->subject, TRUE) : 0; + return art->subject ? subjectStrip(art->subject) : 0; } @@ -3420,28 +3628,10 @@ } -/* - * get the previous subject (article number is NextPreviousArticle). - * only called when going off the top of the subject string - * - * returns a point to a static area - * - * NextPreviousArticle is set to current-1 on building the subject string. - * NextPreviousArticle is decremented by this routine. - */ -char * getPrevSubject(line_length) - int line_length; +art_num getPrevNumber() { - int lineLength; struct newsgroup *newsgroup = CurrentGroup; struct article *art; - static char buffer[BUFFER_SIZE]; - - lineLength = subjectIndexLine(line_length, 0, newsgroup, 0); - -#ifdef DEBUG - fprintf(stderr, "getPrevSubject(%s)\n", newsgroup->name); -#endif /* search for the next available article in the reverse direction */ for ( ; NextPreviousArticle >= newsgroup->first; NextPreviousArticle--) { @@ -3450,43 +3640,49 @@ if (IS_UNAVAIL(art)) continue; - /* get the subject (and author) if it does not already exist */ if (! art->subject) { - /* get the subject and a few more */ - getsubjectlist(newsgroup, - MAX(newsgroup->first, NextPreviousArticle - SUBJECTS), - NextPreviousArticle, False, 0); - art = artStructGet(newsgroup, NextPreviousArticle, False); - } - - if (! art->author) { - getauthorlist(newsgroup, - MAX(newsgroup->first, NextPreviousArticle - SUBJECTS), - NextPreviousArticle, False, 0); - art = artStructGet(newsgroup, NextPreviousArticle, False); - } - - if (! art->lines) { - getlineslist(newsgroup, - MAX(newsgroup->first, NextPreviousArticle - SUBJECTS), - NextPreviousArticle, False, 0); - art = artStructGet(newsgroup, NextPreviousArticle, False); + (void) fillUpArray(newsgroup, + MAX(newsgroup->first, NextPreviousArticle - SUBJECTS), + NextPreviousArticle, False, False); + NextPreviousArticle++; + continue; } if (art->subject) { - (void) subjectIndexLine(line_length, buffer, newsgroup, - NextPreviousArticle); - NextPreviousArticle--; - return buffer; + return NextPreviousArticle--; } - /* continue on */ } - NextPreviousArticle--; - return NIL(char); + return 0; } +/* + * get the previous subject index line (article number is NextPreviousArticle). + * only called when going off the top of the subject string + * + * returns a point to a static area + * + * NextPreviousArticle is set to current-1 on building the subject string. + * NextPreviousArticle is decremented by this routine. + */ +char *getPrevSubject(line_length) + int line_length; +{ + static char buffer[BUFFER_SIZE]; + struct newsgroup *newsgroup = CurrentGroup; + + art_num art = getPrevNumber(); + + if (art) { + (void) subjectIndexLine(line_length, buffer, newsgroup, art, False); + return buffer; + } + + return 0; +} + + static art_num justInCase; /* old NextPreviousArticle, just in case the search fails */ /* the front-end is about to do an article search, save the starting point */ @@ -3505,24 +3701,32 @@ } -int fillUpArray(art, check_abort) - art_num art; - Boolean check_abort; +int fillUpArray( + _ANSIDECL(struct newsgroup *, newsgroup), + _ANSIDECL(art_num, art), + _ANSIDECL(art_num, last), + _ANSIDECL(Boolean, check_abort), + _ANSIDECL(Boolean, kill_files) + ) + _KNRDECL(struct newsgroup *, newsgroup) + _KNRDECL(art_num, art) + _KNRDECL(art_num, last) + _KNRDECL(Boolean, check_abort) + _KNRDECL(Boolean, kill_files) { - struct newsgroup *newsgroup = CurrentGroup; + int i; -#ifdef DEBUG - fprintf(stderr, "fillUpArray(%s, %s)\n", CurrentGroup->name, - check_abort ? "True" : "False"); -#endif + if (! last) + last = newsgroup->last; - getsubjectlist(newsgroup, art, newsgroup->last, False, 0); - if (check_abort && abortP()) - return ABORT; - getauthorlist(newsgroup, art, newsgroup->last, False, 0); - if (check_abort && abortP()) + for (i = PREFETCH_START_HEADERS_STAGE; i <= PREFETCH_LAST_HEADERS_STAGE; ) { + if (fetchHeadersIncremental(newsgroup, &i, art, last, + False, kill_files)) + i++; + if (check_abort && abortP()) return ABORT; - getlineslist(newsgroup, art, newsgroup->last, False, 0); + } + return(! ABORT); } @@ -3626,6 +3830,7 @@ { int ret = GOOD_GROUP; struct newsgroup *newsgroup; + Boolean unsubbed = False; if (!avl_lookup(NewsGroupTable, name, (char **) &newsgroup)) { newsgroup = 0; @@ -3637,7 +3842,7 @@ err_buf = XtMalloc(strlen(MAYBE_LIST_MSG)+strlen(name)); (void) sprintf(err_buf, MAYBE_LIST_MSG, name); if ((ConfirmationBox(TopLevel, err_buf, "yes", "no", True) - == XRN_CB_ABORT) || (! verifyGroup(name, &newsgroup))) { + == XRN_CB_ABORT) || (! verifyGroup(name, &newsgroup, False))) { XtFree(err_buf); return BAD_GROUP; } @@ -3657,7 +3862,10 @@ SET_SUB(newsgroup); } } - updateArticleArray(newsgroup); + if (! updateArticleArray(newsgroup, flags & ENTER_SETUP)) { + return BAD_GROUP; + } + unsubbed = True; } else { return XRN_UNSUBBED; @@ -3665,15 +3873,17 @@ } if (flags & ENTER_SETUP) { - if (app_resources.rescanOnEnter) - setUpGroup(newsgroup, True, False, flags & ENTER_UNREAD); + if (app_resources.rescanOnEnter || unsubbed) + setUpGroup(newsgroup, True, False, flags & ENTER_UNREAD, + False); if (EMPTY_GROUP(newsgroup)) { return XRN_NOMORE; } do { - setUpGroup(newsgroup, False, True, flags & ENTER_UNREAD); + setUpGroup(newsgroup, False, True, flags & ENTER_UNREAD, + True); /* We need to do setCurrentArticle again here, even though setUpGroup() does it, because if the group is already @@ -3682,7 +3892,8 @@ reset the first unread article (because it doesn't do anything if the group is already prefetched). */ - } while (setCurrentArticle(newsgroup, flags & ENTER_UNREAD)); + } while (setCurrentArticle(newsgroup, flags & ENTER_UNREAD, + True, 0, 0)); /* Need to check if the group is empty again, because @@ -3691,7 +3902,7 @@ group (e.g., our cached information about the group is incorrect). */ - if (EMPTY_GROUP(newsgroup) || (newsgroup->current > newsgroup->last)) { + if (EMPTY_GROUP(newsgroup)) { return XRN_NOMORE; } @@ -3732,7 +3943,7 @@ char *list, *list_name; int *count; { - char *p, *q; + char *p, *q, *r; int n; #ifdef POSIX_REGEX regex_t *l; @@ -3761,7 +3972,7 @@ #endif /* Now build the list */ - for ((p = XtNewString(list)), n = 0; (q = strtok(p, ", \t\n")); p = 0) { + for ((p = r = XtNewString(list)), n = 0; (q = strtok(p, ", \t\n")); p = 0) { #ifdef POSIX_REGEX if ((reRet = regcomp(&l[n], q, REG_NOSUB))) { regerror(reRet, &l[n], error_buffer, @@ -3787,13 +3998,60 @@ n++; } +#if defined(POSIX_REGEX) || defined(SYSV_REGEX) + FREE(r); +#endif + if (! n) { + FREE(r); FREE(l); - FREE(p); return 0; } *count = n; return l; +} + +/* + Returns -2 if the article doesn't exist, -1 if it exists but is not + in the specified newsgroup (or is not in the currently available + range in the newsgroup), 0 if the article has no xref header, or + the article number of the article. + */ +art_num getArticleNumberFromIdXref( + _ANSIDECL(struct newsgroup *, newsgroup), + _ANSIDECL(char *, id) + ) + _KNRDECL(struct newsgroup *, newsgroup) + _KNRDECL(char *, id) +{ + char *xref, *ptr; + int len; + art_num artNum; + long error_code; + + /* First, the easy way, using Xref */ + xref = xhdr_id(newsgroup, id, "xref", &error_code); + if (xref) { + for (ptr = strstr(xref, newsgroup->name); ptr; + ptr = strstr(ptr + 1, newsgroup->name)) { + if (((ptr == xref) || isspace(ptr[-1])) && + (len = strlen(newsgroup->name)) && ptr[len] == ':') { + artNum = atol(&ptr[len+1]); + XtFree(xref); + if ((artNum > newsgroup->last) || (artNum < newsgroup->first)) + return -1; + else + return artNum; + } + } + XtFree(xref); + return -1; + } + + if (error_code == ERR_NOART) + return -2; + + return 0; } diff -u -d -r -N -P 8.02/internals.h 9.00/internals.h --- 8.02/internals.h Tue Oct 17 06:44:15 1995 +++ 9.00/internals.h Fri Jul 18 08:57:29 1997 @@ -2,7 +2,7 @@ #define INTERNALS_H /* - * $Id: internals.h,v 1.28 1995/10/17 10:44:00 jik Exp $ + * $Id: internals.h,v 1.48 1997/07/18 12:57:29 jik Exp $ */ /* @@ -37,6 +37,7 @@ #include "server.h" #include "config.h" +#include "file_cache.h" #define XRN_ERROR 0 #define XRN_NOMORE 0 @@ -53,11 +54,9 @@ /* * kill file stuff */ -#define KILL_GLOBAL 0 -#define KILL_LOCAL 1 -extern char *stringToRegexp _ARGUMENTS((char *)); -extern void killItem _ARGUMENTS((struct newsgroup *, char *,int)); +extern char *stringToRegexp _ARGUMENTS((char *, int max_length)); extern char *localKillFile _ARGUMENTS((struct newsgroup*, int)); +extern char *globalKillFile _ARGUMENTS((void)); /* * routines for adding newsgroups to the newsrc file @@ -99,16 +98,14 @@ * routines for doing article management */ -extern void gotoArticle _ARGUMENTS((art_num)); extern int checkArticle _ARGUMENTS((art_num)); -extern art_num firstArticle _ARGUMENTS((void)); -extern art_num currentArticle _ARGUMENTS((void)); -extern int getArticle _ARGUMENTS((char **,char **)); -extern int toggleHeaders _ARGUMENTS((char **,char **)); -extern int toggleRotation _ARGUMENTS((char **,char **)); +extern int getArticle _ARGUMENTS((struct newsgroup *, art_num, + file_cache_file **, char **)); +extern int toggleHeaders _ARGUMENTS((file_cache_file **,char **)); +extern int toggleRotation _ARGUMENTS((file_cache_file **,char **)); #ifdef XLATE -extern int toggleXlation _ARGUMENTS((char **, char **)); +extern int toggleXlation _ARGUMENTS((file_cache_file **, char **)); #endif extern void prefetchNextArticle _ARGUMENTS((void)); @@ -124,6 +121,7 @@ /* get the subject line for the previous subject (and get the article too) */ /* only called when going off the top of the subject string */ extern char *getPrevSubject _ARGUMENTS((int)); +extern art_num getPrevNumber _ARGUMENTS((void)); extern void startSearch _ARGUMENTS((void)); extern void failedSearch _ARGUMENTS((void)); @@ -139,8 +137,9 @@ extern void initializeNews _ARGUMENTS((void)); /* query the server for new information */ -extern void rescanServer _ARGUMENTS((/* Boolean */ int)); +extern void rescanServer _ARGUMENTS((Boolean)); extern void rescanBackground _ARGUMENTS((void)); +extern void cancelRescanBackground _ARGUMENTS((void)); /* clear the "NEW" status of a group, by name */ extern void clearNew _ARGUMENTS((char *)); @@ -148,6 +147,9 @@ /* return the new newsgroups string */ extern char *newGroups _ARGUMENTS((void)); +/* check if an article is available */ +extern Boolean articleIsAvailable _ARGUMENTS((struct newsgroup *, art_num)); + /* return a count of unread articles in all newsgroups */ extern int unreadNews _ARGUMENTS((void)); @@ -160,10 +162,10 @@ /* return the subject string */ #define ALL 0 #define UNREAD 1 -extern char *getSubjects _ARGUMENTS((int, int, art_num)); +extern char *getSubjects _ARGUMENTS((int, art_num, Boolean)); /* build and return the status string */ -extern char *getStatusString _ARGUMENTS((int, int)); +extern char *getStatusString _ARGUMENTS((int, int, char *)); extern void releaseNewsgroupResources _ARGUMENTS((struct newsgroup *)); @@ -176,14 +178,16 @@ extern void articleArrayResync _ARGUMENTS((struct newsgroup *, art_num, art_num, int)); -extern void updateArticleArray _ARGUMENTS((struct newsgroup *)); +extern Boolean updateArticleArray _ARGUMENTS((struct newsgroup *, Boolean)); extern void cancelPrefetch _ARGUMENTS((void)); extern void resetPrefetch _ARGUMENTS((void)); extern void finishPrefetch _ARGUMENTS((void)); extern void prefetchGroup _ARGUMENTS((char *)); -extern int fillUpArray _ARGUMENTS((art_num art, /* Boolean */ int check_abort)); +extern int fillUpArray _ARGUMENTS((struct newsgroup *, art_num art, art_num last, + Boolean check_abort, + Boolean kill_files)); extern char *getinfofromfile _ARGUMENTS((char *)); @@ -197,5 +201,52 @@ extern struct var_rec *cache_variables; extern char *cache_file; + +int subjectIndexLine _ARGUMENTS((int, char *, struct newsgroup *, art_num, + Boolean)); + +char *subjectStrip _ARGUMENTS((char *)); + +#define PREFETCH_FIRST_STAGE PREFETCH_GETGROUP_STAGE +#define PREFETCH_GETGROUP_STAGE 1 +#define PREFETCH_SETCURRENT_STAGE 2 +#define PREFETCH_START_HEADERS_STAGE PREFETCH_SUBJECT_STAGE +#define PREFETCH_SUBJECT_STAGE 3 +#define PREFETCH_AUTHOR_STAGE 4 +#define PREFETCH_LINES_STAGE 5 +#define PREFETCH_START_OPTIONAL_STAGE PREFETCH_NEWSGROUPS_STAGE +#define PREFETCH_NEWSGROUPS_STAGE 6 +#define PREFETCH_DATE_STAGE 7 +#define PREFETCH_IDS_STAGE 8 +#define PREFETCH_REFS_STAGE 9 +#define PREFETCH_XREF_STAGE 10 +#define PREFETCH_LAST_HEADERS_STAGE PREFETCH_XREF_STAGE +#define PREFETCH_KILL_STAGE 11 +#define PREFETCH_THREAD_STAGE 12 +#define PREFETCH_LAST_OPTIONAL_STAGE PREFETCH_THREAD_STAGE +#define PREFETCH_LAST_STAGE PREFETCH_THREAD_STAGE + +/* CAUTION!!! + + Maks sure that PREFETCH_LAST_OPTIONAL_STAGE - + PREFETCH_START_OPTIONAL_STAGE < 8, because the bits are all assumed + to fit into a single byte. If this needs to change, we'll need to + grow the size of the flag field from char to int (or long). It's a + char right now to save on memory usage. + */ + +#define PREFETCH_FIELD_BIT(stage) (1<<(stage-PREFETCH_START_OPTIONAL_STAGE)) + +#define FETCH_NEWSGROUPS PREFETCH_FIELD_BIT(PREFETCH_NEWSGROUPS_STAGE) +#define FETCH_DATES PREFETCH_FIELD_BIT(PREFETCH_DATE_STAGE) +#define FETCH_IDS PREFETCH_FIELD_BIT(PREFETCH_IDS_STAGE) +#define FETCH_REFS PREFETCH_FIELD_BIT(PREFETCH_REFS_STAGE) +#define FETCH_XREF PREFETCH_FIELD_BIT(PREFETCH_XREF_STAGE) +#define FETCH_THREADS PREFETCH_FIELD_BIT(PREFETCH_THREAD_STAGE) + +/* Given an article's message ID, get its number in a newsgroup. + Returns the article number on success, 0 on failure, or -1 on abort. + */ +extern art_num getArticleNumberFromIdXref _ARGUMENTS((struct newsgroup *, char *)); #endif /* INTERNALS_H */ diff -u -d -r -N -P 8.02/killfile.c 9.00/killfile.c --- 8.02/killfile.c Wed Dec 31 19:00:00 1969 +++ 9.00/killfile.c Sun Dec 28 10:57:38 1997 @@ -0,0 +1,977 @@ +#include +#include +#include +#include + +#include "config.h" +#include "utils.h" +#include "news.h" +#include "killfile.h" +#include "internals.h" +#include "server.h" +#include "mesg.h" +#include "mesg_strings.h" +#include "resources.h" +#include "error_hnds.h" +#include "avl.h" +#include "dialogs.h" + +#define BUFFER_SIZE 1024 + +/* + Eventually, if 'h' isn't specified, only the subject should be + killed, but this is how XRN behaved before, so I'm continuing to + do it this way for backward compatibility. XXX + */ +#define DEF_CHECK_FLAGS (KILL_SUBJECT|KILL_AUTHOR) +#define ALL_CHECK_FLAGS (KILL_SUBJECT|KILL_AUTHOR|\ + KILL_NEWSGROUPS|KILL_DATE|\ + KILL_ID|KILL_REFERENCES) + +static void free_entry_contents _ARGUMENTS((kill_entry *)); +static char field_to_check_flag _ARGUMENTS((char *, int)); +static Boolean entry_expired _ARGUMENTS((kill_entry *, time_t)); +static void write_kf _ARGUMENTS((kill_file *)); +static kill_file *read_kf _ARGUMENTS((char *file_name, + char *reference, + unsigned char *fetch_flags)); + + +/* + Return true if the indicated character, which should be in the + string pointed to by start, is backslash-quoted, or false otherwise. + Deals correctly with quoted backslashes immediately following a + non-quoted character. + */ +static Boolean isQuoted _ARGUMENTS((char *, char *)); + +static Boolean isQuoted(character, start) + char *character, *start; +{ + /* + Figure out how many backslashes there are between the indicated + character and either the first non-backslash or the beginning of + the string. + + If the number of backslashes is odd, then return True. + Otherwise, return False. + */ + Boolean quoted = False; + + for (character--; + (character >= start) && (*character == '\\'); + character--) + quoted = ! quoted; + + return quoted; +} + +static void parse_kill_entry _ARGUMENTS((char *, char *, kill_file *, + kill_entry *, + unsigned char *, int)); + +/* + Parse a KILL-file entry. If there's a parse error, this routine + will display it. The resulting parsed entry is put into entry, + or it's unmodified if the line should not be added to the kill-entry + list. + + The kill_entry structure contains a union whose value is dependent + on what type of entry has been parsed. Right now, only "entry", + "include" and "other" (i.e., strings which are ignored by XRN) are + supported. The "entry" type contains a pointer to the compiled regexp + (if necessary), a set of flags indicating which fields to check, and a + set of flags indicating the action (junk the article, mark it read, + or save it). The "include" type contains the operand of the include + (which either is the name of the kill file, or the name of a newsgroup), + a pointer to the kill file contents, and a flag indicating whether this + file belongs to an existing newsgroup. + + Lines beginning with "&", lines beginning with "#", and blank lines + (or lines containing only whitespace) are ignored. "THRU" lines are + put into the kill-file structure. + + If there's a trailing newline in the line being parsed, it will be + replaced in-place with a null. Otherwise, the input string will not + be modified. + + If this function determines that the "Newsgroups" line of the + article is going to have to be checked while processing this entry, + and newsgroup->fetch & FETCH_NEWSGROUPS is false, it will add + FETCH_NEWSGROUPS to *fetch_flags. The caller should detect this + and react appropriately. Ditto for the other FETCH_* fields. + */ +static void parse_kill_entry(file_name, in_str, file, entry, + fetch_flags, mesg_name) + char *file_name; + char *in_str; + kill_file *file; + kill_entry *entry; + unsigned char *fetch_flags; + int mesg_name; +{ + kill_entry my_entry; + char *ptr, *ptr2; +#ifdef POSIX_REGEX + int reRet; +#else +# ifndef SYSV_REGEX + char *reRet; +# endif /* ! SYSV_REGEX */ +#endif /* POSIX_REGEX */ + char pattern[MAX_KILL_ENTRY_LENGTH]; + char include_arg[BUFFER_SIZE]; + char *str = in_str; + struct newsgroup *otherGroup; + + if (! strncmp(str, "THRU ", 5)) { + file->thru = atol(str + 5); + return; + } + + /* + Ignore the trailing newline. + */ + if ((ptr = index(str, '\n'))) + *ptr = '\0'; + + memset((char *) &my_entry, 0, sizeof(my_entry)); + + /* + Ignore whitespace at the beginning of the line. + */ + for (ptr = str; *ptr && isspace(*ptr); ptr++) + /* empty */; + + if ((*ptr == '&') || (*ptr == '#') || (! *ptr)) { + my_entry.type = KILL_OTHER; + goto done; + } + + if (index(app_resources.verboseKill, 'l')) + mesgPane(XRN_INFO, mesg_name, KILL_LINE_MSG, str, file_name); + + if (!strncmp (ptr, "include", 7)) { + ptr += 7; + + if (!isspace(*ptr)) { + mesgPane(XRN_SERIOUS, mesg_name, MALFORMED_KILL_ENTRY_MSG, str, + file_name, ERROR_INCLUDE_NOT_SEPARATED_MSG); + my_entry.type = KILL_OTHER; + goto done; + } + + for (; *ptr && isspace(*ptr); ptr++) + /* empty */; + + if (! *ptr) { + mesgPane(XRN_SERIOUS, mesg_name, MALFORMED_KILL_ENTRY_MSG, + str, file_name, ERROR_INCLUDE_MISSING_MSG); + my_entry.type = KILL_OTHER; + goto done; + } + + for (ptr2 = strchr(ptr, '\0'); + (ptr2 > ptr) && isspace(*(ptr2 - 1)); + ptr2--) + /* empty */; + + strncpy(include_arg, ptr, MIN(ptr2 - ptr, sizeof(include_arg) - 1)); + include_arg[MIN(ptr2 - ptr, sizeof(include_arg) - 1)] = '\0'; + + if (verifyGroup(include_arg, &otherGroup, True)) { + my_entry.include.is_ngfile = True; + my_entry.include.operand = XtNewString(include_arg); + } + else { + my_entry.include.is_ngfile = False; + + if (*include_arg != '/') + strcpy (include_arg, utTildeExpand (include_arg)); + + if (*include_arg != '/') { + int i; + i = strlen(app_resources.expandedSaveDir); + (void) memmove(&include_arg[i+1], include_arg, + MIN(strlen(include_arg)+1, sizeof(include_arg)-i)); + (void) memmove(include_arg, app_resources.expandedSaveDir, i); + include_arg[i] = '/'; + } + + my_entry.include.operand = XtNewString(include_arg); + } + + my_entry.type = KILL_INCLUDE; + goto done; + } + + if (*ptr != '/') { + mesgPane(XRN_SERIOUS, mesg_name, MALFORMED_KILL_ENTRY_MSG, str, + file_name, ERROR_REGEX_NOSLASH_START_MSG); + my_entry.type = KILL_OTHER; + goto done; + } + + /* + Find the end of the regular expression + */ + for (ptr++, ptr2 = ptr; + *ptr2 && ((*ptr2 != '/') || isQuoted(ptr2, ptr)); ptr2++) + /* empty */; + + if (! *ptr2) { + mesgPane(XRN_SERIOUS, mesg_name, MALFORMED_KILL_ENTRY_MSG, str, + file_name, ERROR_REGEX_NOSLASH_MSG); + my_entry.type = KILL_OTHER; + goto done; + } + + /* + We're leaving a character free at the beginning of "pattern" so + that we can anchor the pattern later if we have to. + */ + strncpy(pattern + 1, ptr, MIN(ptr2 - ptr, sizeof(pattern) - 1)); + pattern[MIN(ptr2 - ptr + 1, sizeof(pattern) - 1)] = '\0'; + + ptr = pattern + 1; + + my_entry.entry.pattern = XtNewString(ptr); + + /* + rn puts ": *" at the front of its KILL file entries; XRN doesn't. + */ + if (! strncmp(ptr, ": *", 3)) + ptr += 3; + /* + I'm not convinced this is correct, but it was here before, so I'm + leaving it in. + */ + if (*ptr == ':') + ptr++; + + for (ptr2++; *ptr2 && (*ptr2 != ':'); ptr2++) { + if (*ptr2 == 'h') { + char *ptr3 WALL(= 0); + + if (*ptr == '^') { + if ((ptr3 = strchr(ptr + 1, ':'))) { + my_entry.entry.check_flags = + field_to_check_flag(ptr + 1, ptr3 - (ptr + 1)); + } + } + + if (my_entry.entry.check_flags) { + ptr = ptr3 + 1; + while (*ptr && isspace(*ptr)) + ptr++; + *--ptr = '^'; + } + else + my_entry.entry.check_flags = ALL_CHECK_FLAGS; + } + else if (*ptr2 == 't') { /* kill timeout */ + for ( ; *(ptr2+1) && isdigit(*(ptr2 + 1)); ptr2++) { + my_entry.entry.timeout *= 10; + my_entry.entry.timeout += (*(ptr2+1) - '0'); + } + } + else if (*ptr2 == 'u') { /* last used */ + for ( ; *(ptr2+1) && isdigit(*(ptr2 + 1)); ptr2++) { + my_entry.entry.last_used *= 10; + my_entry.entry.last_used += (*(ptr2+1) - '0'); + } + } + else { + mesgPane(XRN_SERIOUS, mesg_name, KILL_ERROR_UNKNOWN_OPTION_MSG, + str, file_name, *ptr2); + my_entry.type = KILL_OTHER; + goto done; + } + } + + if (my_entry.entry.timeout && !my_entry.entry.last_used) + my_entry.entry.last_used = time(0); + + if (! my_entry.entry.check_flags) { + my_entry.entry.check_flags = DEF_CHECK_FLAGS; + } + + if (! *ptr2) { + mesgPane(XRN_SERIOUS, mesg_name, MALFORMED_KILL_ENTRY_MSG, str, + file_name, ERROR_REGEX_NOCOLON_MSG); + my_entry.type = KILL_OTHER; + goto done; + } + + /* + Eventually, multiple actions should be allowed in one entry. XXX + */ + switch (*++ptr2) { + case 'j': + my_entry.entry.action_flags = KILL_JUNK; + break; + case 'm': + my_entry.entry.action_flags = KILL_MARK; + break; + case 's': + my_entry.entry.action_flags = KILL_SAVE; + break; + default: + mesgPane(XRN_SERIOUS, mesg_name, MALFORMED_KILL_ENTRY_MSG, str, + file_name, ERROR_REGEX_UNKNOWN_COMMAND_MSG); + my_entry.type = KILL_OTHER; + goto done; + } + +#ifdef POSIX_REGEX + if ((reRet = regcomp(&my_entry.entry.reStruct, ptr, REG_NOSUB))) { + regerror(reRet, &my_entry.entry.reStruct, error_buffer, sizeof(error_buffer)); + mesgPane(XRN_SERIOUS, mesg_name, KNOWN_KILL_REGEXP_ERROR_MSG, + str, file_name, error_buffer); + my_entry.type = KILL_OTHER; + goto done; + } +#else +# ifdef SYSV_REGEX + if (! (my_entry.entry.reStruct = regcmp(ptr, NULL))) { + mesgPane(XRN_SERIOUS, mesg_name, UNKNOWN_KILL_REGEXP_ERROR_MSG, + str, file_name); + my_entry.type = KILL_OTHER; + goto done; + } +# else + if ((reRet = re_comp(ptr))) { + mesgPane(XRN_SERIOUS, mesg_name, KNOWN_KILL_REGEXP_ERROR_MSG, + str, file_name, reRet); + my_entry.type = KILL_OTHER; + goto done; + } + my_entry.entry.reStruct = XtNewString(ptr); +# endif /* SYSV_REGEX */ +#endif /* POSIX_REGEX */ + + if (my_entry.entry.check_flags & KILL_NEWSGROUPS) + *fetch_flags |= FETCH_NEWSGROUPS; + if (my_entry.entry.check_flags & KILL_DATE) + *fetch_flags |= FETCH_DATES; + if (my_entry.entry.check_flags & KILL_ID) + *fetch_flags |= FETCH_IDS; + if (my_entry.entry.check_flags & KILL_REFERENCES) + *fetch_flags |= FETCH_REFS; + if (my_entry.entry.check_flags & KILL_XREF) + *fetch_flags |= FETCH_XREF; + + my_entry.type = KILL_ENTRY; + +done: + if (my_entry.type != KILL_ENTRY && my_entry.type != KILL_INCLUDE) { + free_entry_contents(&my_entry); + } + my_entry.any.value = XtNewString(in_str); + + *entry = my_entry; +} + + +struct kftab_entry { + char ref_count; + unsigned char fetch_flags; + kill_file *kill_file; +}; + +static avl_tree *kf_table = 0; + +static void clear_seen _ARGUMENTS((void)); + +static void clear_seen() +{ + avl_generator *gen; + struct kftab_entry *tab_entry; + struct newsgroup *newsgroup; + + if (! kf_table) + return; + + gen = avl_init_gen(kf_table, AVL_FORWARD); + + while (avl_gen(gen, 0, (char **) &tab_entry)) + tab_entry->kill_file->flags &= ~KF_SEEN; + + avl_free_gen(gen); + + gen = avl_init_gen(NewsGroupTable, AVL_FORWARD); + + while (avl_gen(gen, 0, (char **) &newsgroup)) + if (newsgroup->kill_file) + ((kill_file *)newsgroup->kill_file)->flags &= ~KF_SEEN; + + avl_free_gen(gen); +} + + +static void unparse_kill_entry _ARGUMENTS((kill_entry *, char *)); + +static void unparse_kill_entry(entry, buffer) + kill_entry *entry; + char *buffer; +{ + char *ptr; + + if (entry->type == KILL_OTHER) { + (void) sprintf(buffer, "%.*s\n", MAX_KILL_ENTRY_LENGTH-2, + entry->any.value); + } + else if (entry->type == KILL_INCLUDE) { + (void) sprintf(buffer, "%.*s\n", MAX_KILL_ENTRY_LENGTH-2, + entry->any.value); + if (! entry->include.is_ngfile) { + struct kftab_entry *tab_entry; + int ret; + + assert(kf_table); + ret = avl_lookup(kf_table, entry->include.kf->file_name, + (char **) &tab_entry); + assert(ret); + + if (! --tab_entry->ref_count) { + char *old_file_name = entry->include.kf->file_name; + char *file_name = old_file_name; + + write_kf(entry->include.kf); + entry->include.kf = 0; + + ret = avl_delete(kf_table, &file_name, 0); + XtFree(old_file_name); + XtFree((char *)tab_entry); + } + } + } + else if (entry->type == KILL_ENTRY) { + (void) sprintf(buffer, "/%s/", entry->entry.pattern); + ptr = &buffer[strlen(buffer)]; + if (entry->entry.check_flags != DEF_CHECK_FLAGS) + *ptr++ = 'h'; + if (entry->entry.timeout) { + (void) sprintf(ptr, "t%d", entry->entry.timeout); + ptr += strlen(ptr); + if (entry->entry.last_used) { + (void) sprintf(ptr, "u%ld", entry->entry.last_used); + ptr += strlen(ptr); + } + } + *ptr++ = ':'; + switch(entry->entry.action_flags) { + case KILL_JUNK: + *ptr++ = 'j'; + break; + case KILL_MARK: + *ptr++ = 'm'; + break; + case KILL_SAVE: + *ptr++ = 's'; + break; + default: + assert(0); + } + *ptr++ = '\n'; + *ptr = '\0'; + } + else { + assert(0); + } +} + + +static kill_file *GlobalKillFile = 0; +static unsigned char GlobalFetchFlags = 0; + + +void read_global_kill_file(newsgroup) + struct newsgroup *newsgroup; +{ + + if (!GlobalKillFile) { + GlobalKillFile = read_kf(globalKillFile(), 0, &GlobalFetchFlags); + } + + newsgroup->fetch |= GlobalFetchFlags; +} + +void read_local_kill_file(newsgroup) + struct newsgroup *newsgroup; +{ + char *file_name; + + if (newsgroup->kill_file) + return; + + file_name = localKillFile(newsgroup, 0); + + newsgroup->kill_file = (void *) read_kf(file_name, 0, &newsgroup->fetch); +} + +static kill_file *read_kf(file_name, reference, fetch_flags) + char *file_name; + char *reference; + unsigned char *fetch_flags; +{ + int entries_size; + FILE *fp; + kill_file *kf; + struct stat statbuf; + char buf[MAX_KILL_ENTRY_LENGTH]; + int mesg_name = newMesgPaneName(); + + kf = (kill_file *) XtCalloc(1, sizeof(*kf)); + kf->file_name = XtNewString(file_name); + + if ((stat(file_name, &statbuf) < 0) || + (! (fp = fopen(file_name, "r")))) { + if (reference) + mesgPane(XRN_SERIOUS, mesg_name, CANT_OPEN_INCLUDED_KILL_MSG, + file_name, reference, errmsg(errno)); + else if (errno != ENOENT) + mesgPane(XRN_SERIOUS, mesg_name, CANT_OPEN_KILL_MSG, + file_name, errmsg(errno)); + kf->mod_time = 0; + return kf; + } + + kf->mod_time = statbuf.st_mtime; + + entries_size = 1; + kf->entries = (kill_entry *) XtCalloc(entries_size, sizeof(*kf->entries)); + + while (fgets(buf, sizeof(buf), fp)) { + kill_entry *entry; + + if ((! strchr(buf, '\n')) && (! (feof(fp) || ferror(fp)))) { + mesgPane(XRN_SERIOUS, mesg_name, KILL_TOO_LONG_MSG, buf, file_name); + do { + *buf = fgetc(fp); + } while (! (feof(fp) || ferror(fp) || (*buf == '\n'))); + continue; + } + if (kf->count == entries_size) { + entries_size *= 2; + kf->entries = (kill_entry *) XtRealloc((char *) kf->entries, + entries_size * sizeof(*kf->entries)); + memset((char *) &kf->entries[kf->count], 0, + (entries_size / 2) * sizeof(*kf->entries)); + } + + entry = &kf->entries[kf->count]; + + parse_kill_entry(file_name, buf, kf, entry, fetch_flags, mesg_name); + + if (entry->type) + kf->count++; + + if (entry->type == KILL_INCLUDE) { + if (entry->include.is_ngfile) { + struct newsgroup *otherGroup = 0; + + verifyGroup(entry->include.operand, &otherGroup, True); + assert(otherGroup); + + read_local_kill_file(otherGroup); + *fetch_flags |= otherGroup->fetch; + entry->include.kf = (kill_file *) otherGroup->kill_file; + } + else { + struct kftab_entry *tab_entry; + + if (! kf_table) + kf_table = avl_init_table(strcmp); + + if (avl_lookup(kf_table, entry->include.operand, + (char **) &tab_entry)) { + tab_entry->ref_count++; + } + else { + int ret; + + tab_entry = (struct kftab_entry *) XtCalloc(1, sizeof(*tab_entry)); + tab_entry->ref_count = 1; + tab_entry->fetch_flags = 0; + tab_entry->kill_file = read_kf(entry->include.operand, + file_name, + &tab_entry->fetch_flags); + + ret = avl_insert(kf_table, tab_entry->kill_file->file_name, + (char *) tab_entry); + assert(! ret); + } + + entry->include.kf = tab_entry->kill_file; + *fetch_flags |= tab_entry->fetch_flags; + } + } + } + + (void) fclose(fp); + + return kf; +} + +static kill_entry *kf_iter _ARGUMENTS((kill_file *, kill_entry *, + Boolean)); + +static kill_entry *kf_iter( + _ANSIDECL(kill_file *, kf), + _ANSIDECL(kill_entry *, last_entry), + _ANSIDECL(Boolean, expand_includes) + ) + _KNRDECL(kill_file *, kf) + _KNRDECL(kill_entry *, last_entry) + _KNRDECL(Boolean, expand_includes) +{ + kill_entry *sub_entry = 0; + + assert(kf); + + if (! last_entry) { + last_entry = kf->entries; + if (expand_includes) + kf->flags |= KF_SEEN; + } + else if (kf->cur_sub_kf) { + assert(expand_includes); + assert(kf->cur_entry); + + sub_entry = kf_iter(kf->cur_sub_kf, last_entry, expand_includes); + + if (sub_entry) + return sub_entry; + + kf->cur_sub_kf = 0; + last_entry = kf->cur_entry; + last_entry++; + } else { + last_entry++; + } + + if (last_entry - kf->entries == kf->count) + return 0; + +#if !defined(POSIX_REGEX) && !defined(SYSV_REGEX) + if (last_entry->type == KILL_ENTRY) + (void) re_comp(last_entry->entry.reStruct); +#endif + + if ((last_entry->type == KILL_INCLUDE) && expand_includes) { + if (! (last_entry->include.kf->flags & KF_SEEN)) { + sub_entry = kf_iter(last_entry->include.kf, 0, expand_includes); + + if (sub_entry) { + kf->cur_entry = last_entry; + kf->cur_sub_kf = last_entry->include.kf; + return sub_entry; + } + } + + return kf_iter(kf, last_entry, expand_includes); + } + + return last_entry; +} + +kill_entry *kill_file_iter(newsgroup, mode, last_entry) + struct newsgroup *newsgroup; + int mode; + kill_entry *last_entry; +{ + kill_file *kf; + + if (mode == KILL_LOCAL) + kf = (kill_file *) newsgroup->kill_file; + else + kf = GlobalKillFile; + + clear_seen(); + + return kf_iter(kf, last_entry, True); +} + +#define CHECK_WRITE(cmd) if (! (cmd)) { \ + mesgPane(XRN_SERIOUS, mesg_name, ERROR_WRITING_FILE_MSG, temp_file, \ + errmsg(errno)); \ + (void) fclose(fp); \ + (void) unlink(temp_file); \ + goto done; \ +} + +static void write_kf(kf) + kill_file *kf; +{ + char *temp_file = 0; + FILE *fp; + char buf[MAX_KILL_ENTRY_LENGTH]; + kill_entry *entry = 0; + time_t now = time(0); + int mesg_name = newMesgPaneName(); + + assert (kf); + + if (kf->flags & KF_CHANGED) { + struct stat statbuf; + + if (kf->mod_time) { + if ((stat(kf->file_name, &statbuf) < 0) && (errno != ENOENT)) { + /* Not a completely accurate error, but close enough. */ + mesgPane(XRN_SERIOUS, mesg_name, CANT_OPEN_KILL_MSG, kf->file_name, + errmsg(errno)); + goto done; + } + + if (kf->mod_time != statbuf.st_mtime) { + (void) sprintf(error_buffer, ASK_FILE_MODIFIED_MSG, "Kill", + kf->file_name); + if (ConfirmationBox(TopLevel, error_buffer, 0, 0, False) == + XRN_CB_ABORT) + goto done; + } + } + + temp_file = utTempFile(kf->file_name); + + if (! (fp = fopen(temp_file, "w"))) { + mesgPane(XRN_SERIOUS, mesg_name, CANT_CREATE_TEMP_MSG, temp_file, + errmsg(errno)); + goto done; + } + + if (kf->thru) { + CHECK_WRITE(fprintf(fp, "THRU %ld\n", kf->thru) != EOF); + } + + while ((entry = kf_iter(kf, entry, False))) { + if (entry_expired(entry, now)) + continue; + unparse_kill_entry(entry, buf); + CHECK_WRITE(fputs(buf, fp) != EOF); + } + + if (fclose(fp) == EOF) { + mesgPane(XRN_SERIOUS, mesg_name, ERROR_WRITING_FILE_MSG, temp_file, + errmsg(errno)); + (void) unlink(temp_file); + goto done; + } + + if (rename(temp_file, kf->file_name)) { + mesgPane(XRN_SERIOUS, mesg_name, ERROR_RENAMING_MSG, temp_file, + kf->file_name, errmsg(errno)); + (void) unlink(temp_file); + } + +done: + XtFree(temp_file); + } + + while ((entry = kf_iter(kf, entry, False))) + free_entry_contents(entry); + XtFree((char *)kf->entries); + XtFree((char *)kf); +} + +void write_kill_file(newsgroup, mode) + struct newsgroup *newsgroup; + int mode; +{ + kill_file **kf_ptr, *kf; + + if (mode == KILL_LOCAL) + kf_ptr = (kill_file **) &newsgroup->kill_file; + else + kf_ptr = &GlobalKillFile; + + kf = *kf_ptr; + + if (kf) { + char *old_file_name = kf->file_name; + write_kf(kf); + XtFree(old_file_name); + } + + *kf_ptr = 0; +} + +static void free_entry_contents(entry) + kill_entry *entry; +{ + XtFree(entry->any.value); + + switch (entry->type) { + case KILL_INCLUDE: + XtFree(entry->include.operand); + break; + case KILL_ENTRY: + XtFree(entry->entry.pattern); +#ifdef POSIX_REGEX + regfree(&entry->entry.reStruct); +#else /* SYSV_REGEX or BSD regexps */ +# ifdef SYSV_REGEX + free(entry->entry.reStruct); +# else /* BSD regexps */ + XtFree(entry->entry.reStruct); +# endif /* SYSV_REGEX */ +#endif /* POSIX_REGEX */ + break; + } +} + +Boolean has_kill_files(newsgroup) + struct newsgroup *newsgroup; +{ + if (newsgroup && newsgroup->kill_file) + return True; + + if (GlobalKillFile) + return True; + + return False; +} + +void add_kill_entry(newsgroup, mode, field, regexp) + struct newsgroup *newsgroup; + int mode; + char *field, *regexp; +{ + kill_file **kf_ptr, *kf; + char buf[MAX_KILL_ENTRY_LENGTH]; + FILE *fp; + char *file; + kill_entry my_entry; + int mesg_name = newMesgPaneName(); + unsigned char *fetch_ptr; + struct stat statbuf; + + if (mode == KILL_LOCAL) { + file = localKillFile(newsgroup, 1); + kf_ptr = (kill_file **) &newsgroup->kill_file; + fetch_ptr = &newsgroup->fetch; + } else { + file = globalKillFile(); + kf_ptr = &GlobalKillFile; + fetch_ptr = &GlobalFetchFlags; + } + + kf = *kf_ptr; + + memset((char *) &my_entry, 0, sizeof(my_entry)); + + my_entry.type = KILL_ENTRY; + my_entry.entry.value = 0; + + if (field) { + assert(strlen(regexp) <= MAX_KILL_PATTERN_VALUE_LENGTH); + (void) sprintf(buf, "^%s: .*%s", field, regexp); + my_entry.entry.pattern = XtNewString(buf); + my_entry.entry.check_flags = field_to_check_flag(field, strlen(field)); + if (! my_entry.entry.check_flags) + my_entry.entry.check_flags = ALL_CHECK_FLAGS; + } + else { + assert(strlen(regexp) <= MAX_KILL_PATTERN_LENGTH); + my_entry.entry.pattern = XtNewString(regexp); + my_entry.entry.check_flags = DEF_CHECK_FLAGS; + } + + my_entry.entry.action_flags = KILL_JUNK; + my_entry.entry.timeout = app_resources.killTimeout; + my_entry.entry.last_used = time(0); + + unparse_kill_entry(&my_entry, buf); + + free_entry_contents(&my_entry); + + if (stat(file, &statbuf) < 0) + statbuf.st_mtime = 0; + + /* + XXX Check mod time of file and update it in the kf structure. + */ + + if ((fp = fopen(file, "a")) == NULL) { + mesgPane(XRN_SERIOUS, mesg_name, CANT_OPEN_KILL_MSG, + file, errmsg(errno)); + } + else if ((fputs(buf, fp) == EOF) || (fclose(fp) == EOF)) { + mesgPane(XRN_SERIOUS, mesg_name, ERROR_WRITING_FILE_MSG, file, + errmsg(errno)); + } + + if (kf) { + if (statbuf.st_mtime && kf->mod_time && + (statbuf.st_mtime == kf->mod_time) && (stat(file, &statbuf) >= 0)) + kf->mod_time = statbuf.st_mtime; + + kf->count++; + kf->entries = (kill_entry *) XtRealloc((char *)kf->entries, + kf->count * + sizeof(*kf->entries)); + memset((char *) &kf->entries[kf->count-1], 0, sizeof(*kf->entries)); + parse_kill_entry("internal", buf, kf, + &kf->entries[kf->count-1], fetch_ptr, + mesg_name); + assert(kf->entries[kf->count-1].type == KILL_ENTRY); + } +} + +#define CHECK_FIELD(value, flag) \ + if (! strncmp(field_name, (value), MAX(field_length, sizeof(value)-1))) { \ + return(flag); \ + } + +static char field_to_check_flag(field_name, field_length) + char *field_name; + int field_length; +{ + CHECK_FIELD("Subject", KILL_SUBJECT); + CHECK_FIELD("From", KILL_AUTHOR); + CHECK_FIELD("Newsgroups", KILL_NEWSGROUPS); + CHECK_FIELD("Date", KILL_DATE); + CHECK_FIELD("Message-ID", KILL_ID); + CHECK_FIELD("References", KILL_REFERENCES); + CHECK_FIELD("Xref", KILL_XREF); + + return 0; +} + +#undef CHECK_FIELD + +static Boolean entry_expired(entry, now) + kill_entry *entry; + time_t now; +{ + if (entry->type != KILL_ENTRY) + return False; + + if (! entry->entry.timeout) + return False; + + if ((now - entry->entry.last_used) > + (entry->entry.timeout * 24 * 60 * 60)) + return True; + + return False; +} + +void kill_update_last_used(file, entry) + kill_file *file; + kill_entry *entry; +{ + if (entry->type != KILL_ENTRY) + return; + + if (! entry->entry.timeout) + return; + + entry->entry.last_used = time(0); + + if (file->cur_sub_kf) + kill_update_last_used(file->cur_sub_kf, entry); + else + file->flags |= KF_CHANGED; +} diff -u -d -r -N -P 8.02/killfile.h 9.00/killfile.h --- 8.02/killfile.h Wed Dec 31 19:00:00 1969 +++ 9.00/killfile.h Sun Jul 13 21:22:59 1997 @@ -0,0 +1,94 @@ +#ifndef _KILLFILE_H +#define _KILLFILE_H + +#define MAX_KILL_ENTRY_LENGTH 512 +#define MAX_KILL_PATTERN_LENGTH (MAX_KILL_ENTRY_LENGTH- \ + 1- /* first slash */ \ + 1- /* second slash */ \ + 1- /* "h" option */ \ + 20- /* "t" option and value */ \ + 20- /* "u" option and value */ \ + 1- /* colon */ \ + 1- /* command */ \ + 2) /* newline and null */ +#define MAX_KILL_PATTERN_VALUE_LENGTH (MAX_KILL_PATTERN_LENGTH- \ + 13) /* "^Newsgroups: " */ + +#define KILL_SESSION -1 +#define KILL_GLOBAL 0 +#define KILL_LOCAL 1 + +#define KILL_ENTRY (1<<0) +#define KILL_INCLUDE (1<<1) +#define KILL_OTHER (1<<2) + +#define KILL_SUBJECT (1<<0) +#define KILL_AUTHOR (1<<1) +#define KILL_NEWSGROUPS (1<<2) +#define KILL_DATE (1<<3) +#define KILL_ID (1<<4) +#define KILL_REFERENCES (1<<5) +#define KILL_XREF (1<<6) + +#define KILL_JUNK (1<<0) +#define KILL_MARK (1<<1) +#define KILL_SAVE (1<<2) + +typedef union _kill_entry { + char type; + struct { + char type; + char *value; + } any; + struct { + char type; + char *value; + char *pattern; +#ifdef POSIX_REGEX + regex_t reStruct; +#else + char *reStruct; +#endif /* POSIX_REGEX */ + char check_flags; + char action_flags; + int timeout; + time_t last_used; + } entry; + struct { + char type; + char *value; + char *operand; + char is_ngfile; + struct _kill_file *kf; + } include; + struct { + char type; + char *value; + } other; +} kill_entry; + +typedef struct _kill_file { + time_t mod_time; + char *file_name; + art_num thru; + int count; + kill_entry *entries; + struct _kill_file *cur_sub_kf; + kill_entry *cur_entry; + char flags; +} kill_file; + +#define KF_SEEN (1<<0) +#define KF_CHANGED (1<<1) + +void read_global_kill_file _ARGUMENTS((struct newsgroup *)); +void read_local_kill_file _ARGUMENTS((struct newsgroup *)); +kill_entry *kill_file_iter _ARGUMENTS((struct newsgroup *, int mode, + kill_entry *)); +void write_kill_file _ARGUMENTS((struct newsgroup *, int mode)); +Boolean has_kill_files _ARGUMENTS((struct newsgroup *)); +void add_kill_entry _ARGUMENTS((struct newsgroup *newsgroup, int mode, + char *field, char *regexp)); +void kill_update_last_used _ARGUMENTS((kill_file *, kill_entry *)); + +#endif /* ! _KILLFILE_H */ diff -u -d -r -N -P 8.02/mesg.c 9.00/mesg.c --- 8.02/mesg.c Thu May 2 08:29:02 1996 +++ 9.00/mesg.c Thu Jun 5 07:11:41 1997 @@ -1,7 +1,5 @@ - - #if !defined(lint) && !defined(SABER) && !defined(GCC_WALL) -static char XRNrcsid[] = "$Id: mesg.c,v 1.25 1996/05/02 12:28:42 jik Exp $"; +static char XRNrcsid[] = "$Id: mesg.c,v 1.27 1997/03/30 18:15:00 jik Exp $"; #endif /* @@ -62,14 +60,9 @@ #include "Text.h" #include "ButtonBox.h" #include "InfoLine.h" +#include "InfoDialog.h" char error_buffer[2048]; -/* entire widget */ -static Widget MesgTopLevel = (Widget) 0; -/* text window */ -static Widget MesgText = (Widget) 0; -/* amount of text currently in the window */ -static long current_length = 0; static char *MesgString = 0; #ifndef XawFmt8Bit @@ -84,51 +77,6 @@ */ static char InfoString[512]; -BUTTON(mesgDismiss,dismiss); -BUTTON(mesgClear,clear); - -/*ARGSUSED*/ -void mesgDismissFunction(widget, event, string, count) - Widget widget; - XEvent *event; - String *string; - Cardinal *count; -{ - XtPopdown(MesgTopLevel); - TextDestroy(MesgText); - XtDestroyWidget(MesgTopLevel); - MesgTopLevel = (Widget) 0; - MesgText = (Widget) 0; - current_length = 0; - return; -} - -void mesgClearFunction(widget, event, string, count) - Widget widget; - XEvent *event; - String *string; - Cardinal *count; -{ - TextClear(MesgText); - current_length = 0; - return; -} - -static void displayMesgString _ARGUMENTS((char *new_string)); - -static void displayMesgString(new_string) - char *new_string; -{ - long newlen = strlen(new_string); - - if (! MesgText) - return; - - TextReplace(MesgText, new_string, newlen, current_length, current_length); - current_length += newlen; - TextSetInsertionPoint(MesgText, current_length); -} - int newMesgPaneName() { @@ -162,11 +110,7 @@ */ { va_list args; - Widget pane, buttonBox, label, button; static int last_name = 0; - static Arg shellArgs[] = { - {XtNinput, (XtArgVal) True}, - }; time_t tm; char *time_str; char addBuff[MESG_SIZE]; @@ -200,39 +144,9 @@ time_str += 11; /* Skip over the day and date */ time_str[8] = '\0'; /* We only want the time, not the year and the newline */ - if (MesgTopLevel == (Widget) 0) { - MesgTopLevel = XtCreatePopupShell("Information", topLevelShellWidgetClass, - TopLevel, shellArgs, XtNumber(shellArgs)); - - pane = XtVaCreateManagedWidget("pane", panedWidgetClass, MesgTopLevel, - NULL); - - label = InfoLineCreate("label", 0, pane); - - MesgText = TextCreate("text", True, pane); - - buttonBox = ButtonBoxCreate("box", pane); - - button = ButtonBoxAddButton("mesgDismiss", mesgDismissCallbacks, - buttonBox); - makeDefaultButton(button); - - (void) ButtonBoxAddButton("mesgClear", mesgClearCallbacks, - buttonBox); - - ButtonBoxDoneAdding(buttonBox); - - XtRealizeWidget(MesgTopLevel); - XtSetKeyboardFocus(MesgTopLevel, MesgText); - XtInstallAccelerators(MesgText, button); - - XDefineCursor(XtDisplay(MesgTopLevel), XtWindow(MesgTopLevel), - XCreateFontCursor(XtDisplay(MesgTopLevel), XC_left_ptr)); - - XtPopup(MesgTopLevel, XtGrabNone); - } + InfoDialogCreate(TopLevel); - if (! (current_length || MesgString)) { + if (! (MesgLength() || MesgString)) { (void) sprintf(addBuff, "%s: ", time_str); } else if (type & XRN_SAME_LINE) { @@ -258,9 +172,12 @@ * If 'now' is true, then process as many X events as possible to * force the message to be displayed immediately. */ -void _info(msg, now) - char *msg; - Boolean now; +void _info( + _ANSIDECL(char *, msg), + _ANSIDECL(Boolean, now) + ) + _KNRDECL(char *, msg) + _KNRDECL(Boolean, now) { static char label[LABEL_SIZE] = ""; static Widget info_widget = 0; @@ -276,7 +193,7 @@ info_widget = TopInfoLine; if (! TopInfoLine) return; - XtVaSetValues(TopInfoLine, XtNlabel, (XtArgVal) msg, 0); + InfoLineSet(TopInfoLine, msg); if (now) { xthHandlePendingExposeEvents(); diff -u -d -r -N -P 8.02/mesg.h 9.00/mesg.h --- 8.02/mesg.h Thu May 2 08:29:20 1996 +++ 9.00/mesg.h Thu Jun 5 07:11:41 1997 @@ -4,7 +4,7 @@ #include "butdefs.h" /* - * $Id: mesg.h,v 1.14 1996/05/02 12:29:00 jik Exp $ + * $Id: mesg.h,v 1.15 1997/01/12 03:41:22 jik Exp $ */ /* @@ -43,7 +43,7 @@ #define XRN_SAME_LINE (1<<3) #define XRN_WARNING (1<<4) -extern void _info _ARGUMENTS((char *, /* Boolean */ int)); +extern void _info _ARGUMENTS((char *, Boolean)); #define INFO(msg) _info((msg), False) #define infoNow(msg) _info((msg), True) diff -u -d -r -N -P 8.02/mesg_strings.c 9.00/mesg_strings.c --- 8.02/mesg_strings.c Thu May 2 08:27:52 1996 +++ 9.00/mesg_strings.c Mon Dec 15 11:24:56 1997 @@ -1,5 +1,5 @@ #if !defined(lint) && !defined(SABER) && !defined(GCC_WALL) -static char XRNrcsid[] = "$Id: mesg_strings.c,v 1.52 1996/05/02 12:27:32 jik Exp $"; +static char XRNrcsid[] = "$Id: mesg_strings.c,v 1.75 1997/12/15 16:24:55 jik Exp $"; #endif /* @@ -136,7 +136,7 @@ #define FIRST_STRING "Anfang" #define LAST_STRING "Ende" #define CURSOR_POS_STRING "Aktuelle Position" -#define SUB_STRING "Abbonieren" +#define SUB_STRING "Abonnieren" #define GOTO_NG_STRING "Gehe zu Gruppe" #define CLICK_TO_CONT_STRING "Fortsetzen" @@ -148,12 +148,12 @@ * 4) Artikel ungelesen * 5) NOT_ONE_MSG wenn *ein* ungelesener Artikel, sonst " " * - * Beispiel:"Ungelesene News in comp.sys.ibm.misc 30 Artikel + 20 alt" + * Beispiel:"Ungelesene Nachrichten in comp.sys.ibm.misc 30 Artikel + 20 alt" * Note: maximale Zeilenlaenge ist normalerweise 200 */ /* The number of characters into a newsgroup index line after which the newsgroup name actually starts. */ -#define NEWS_GROUP_OFFSET 19 +#define NEWS_GROUP_OFFSET 26 /* The number of characters on a newsgroup index line, other than the newsgroup name, and not including the newline or the null at the @@ -307,6 +307,8 @@ "Article not cancelled.", /* < CANCELLED_ART > */ "Article has been cancelled.", +/* < CANCEL_TO_MODERATOR > */ + "Message being canceled appears in one or more moderated newsgroups. Cancel request will be mailed to moderator by server.", /* < UNKNOWN_REGEXP_ERROR > */ "Unknown error in regular expression `%s'.", /* regexp string */ /* < KNOWN_REGEXP_ERROR > */ @@ -314,13 +316,23 @@ /* < ART_NUMBERING_PROBLEM > */ "Article numbering problem. Marking all articles in newsgroup `%s' unread.", /* newsgroup name */ /* < CANT_OPEN_KILL > */ - "Cannot open %s kill file (`%s') for newsgroup `%s': %s.", /* "local" or "global", file name, newsgroup name, error string */ + "Cannot open kill file `%s': %s.", /* file name, error string */ +/* < CANT_OPEN_INCLUDED_KILL > */ + "Cannot open kill file `%s' (included from `%s': %s.", /* file name, parent file name, error string */ /* < MALFORMED_KILL_ENTRY > */ - "Error in KILL file entry `%s' in newsgroup `%s': %s.", /* entry, newsgroup name, reason for error */ + "Error in KILL file entry `%s' in KILL file `%s': %s.", /* entry, file, reason for error */ +/* < ERROR_INCLUDE_MISSING > */ + "No newsgroup or file name specified in include directive", +/* < ERROR_INCLUDE_NOT_SEPARATED > */ + " Include operand not separated", +/* < KILL_ERROR_UNKNOWN_OPTION > */ + "Error in KILL file entry `%s' in KILL file `%s': Unknown option `%c'.", /* entry, file, unknown option */ /* < UNKNOWN_KILL_REGEXP_ERROR > */ - "Unknown regular expression error in KILL file entry `%s'.", /* entry */ + "Unknown regular expression error in KILL file entry `%s' in KILL file `%s'.", /* entry */ /* < KNOWN_KILL_REGEXP_ERROR > */ - "Regular expression error in KILL file entry `%s': %s.", /* entry, error string */ + "Regular expression error in KILL file entry `%s' in KILL file `%s': %s.", /* entry, error string */ +/* < KILL_TOO_LONG > */ + "Discarding too-long entry starting with `%s' in KILL file `%s'.", /* start of entry, file */ /* < NOT_IN_NEWSRC > */ "Newsgroup `%s' is not in your .newsrc file.", /* newsgroup name */ /* < BOGUS_NG_REMOVING > */ @@ -467,16 +479,22 @@ "Subject search: %s", /* regular expression */ /* < ERROR_SUBJ_EXH > */ "Subject has been exhausted", +/* < ERROR_NO_PARENT > */ + "Article has no parent.", +/* < ERROR_PARENT_UNAVAIL > */ + "Article's parent is unavailable.", /* < ERROR_SUBJ_ABORT > */ "search aborted", -/* < ERROR_SUB_KILL > */ - "Subject has been killed, returning to first unread article", -/* < ERROR_AUTHOR_KILL > */ - "Author has been killed, returning to first unread article", +/* < KILL_DONE > */ + "Returning to first unread article.", +/* < UNKNOWN_KILL_TYPE > */ + "Unknown kill type \"%s\" in \"%s\" kill request.", /* field type argument, field name */ /* < ERROR_CANT_UPDATE_NEWSRC > */ "Cannot update the newsrc file", /* < ARTICLE_NUMBER > */ "Article Number:", +/* < LIST_OLD_NUMBER > */ + "First article to list:", /* < ERROR_SUBJ_EXPR > */ "Search for expression %s: no match was found", /* regular expression */ /* < ERROR_SEARCH > */ @@ -493,6 +511,8 @@ "Group to go to:", /* < VIEW_ALLNG_SUB > */ "View all available groups, with option to subscribe", +/* < SUB_DONE > */ + "You are now subscribed to `%s'.", /* newsgroup name */ /* < AUTOMATIC_RESCAN > */ "automatic rescan in progress...", /* < RESCANNING_BACKGROUND > */ @@ -578,7 +598,7 @@ "In article %s, %s writes:\n", /* messageid , author */ /* #### end may be not translate #### */ /* < NEWSGROUPS_INDEX > */ - "%6s %7s %*s %4d article%1.1s +%5d old", + "%6s %7s %*s %4d article%1.1s +%6d old", /* < UNREAD > */ "Unread", /* < NEWS_IN > */ @@ -603,12 +623,14 @@ "Processing KILL file for newsgroup `%s'...", /* newsgroup */ /* < ERROR_REGEX_NOSLASH > */ "no slash terminating the regular expression", +/* < ERROR_REGEX_NOSLASH_START > */ + "no slash preceding the regular expression", /* < ERROR_REGEX_NOCOLON > */ "no colon after the regular expression", /* < ERROR_REGEX_UNKNOWN_COMMAND > */ "unknown command (valid commands are `j', `m', and `s')", /* < KILL_LINE > */ - "Processing kill line \"%s\" in \"%s\".", + "Processing entry `%s' in KILL file `%s'.", /* < KILL_KILLED > */ "killed - %s", /* subject */ /* < KILL_UNREAD > */ @@ -621,8 +643,6 @@ "marked %d article%s unread in %s", /* count, "" or NOT_ONE_STRING , newsgroup */ /* < COUNT_SAVED > */ "saved %d article%s in %s", /* count, "" or NOT_ONE_STRING , newsgroup */ -/* < COUNT_MATCHED > */ - "matched %d article%s with unknown option in %s", /* count, "" or NOT_ONE_STRING , newsgroup */ /* < ERROR_CORNERED > */ "XRN error in `cornered': expecting nglist to be valid\n", /* < ERROR_OUT_OF_MEM > */ @@ -650,15 +670,27 @@ /* < ERROR_INFINITE_LOOP > */ "XRN panic: moveBeginning / moveEnd in infinite loop", /* < ERROR_FINDARTICLE > */ - "Valid article number not found in findArticle (cursor.c)", + "Valid article number not found in findArticle", /* < ERROR_STRIP_LEAVE_HEADERS > */ "Only one of `stripHeaders', `leaveHeaders' resources allowed", /* < ERROR_REQUEST_FAILED > */ " Request was: `%s'\n Failing response was: `%s'", /* command, message */ -/* < ASK_UPDATE_NEWSRC > */ - ".newsrc file updated by another program, continue?", +/* < ASK_FILE_MODIFIED > */ + "%s file %s\nhas been modified; overwrite it?", /* file type, file name */ /* < PENDING_COMPOSITION > */ "You cannot exit when a composition is pending!", +/* < NNTP_PASSWORD > */ + "Enter NNTP password:", +/* < UNKNOWN_SORT_TYPE > */ + "Unknown subject sort type: %s", +/* < TOO_MANY_SORT_TYPES > */ + "Too many subject sort types specified.", +/* < UNPARSEABLE_DATE > */ + "Unparseable date (article %ld in %s): %s", +/* < THREADING_FOR > */ + "Threading newsgroup `%s'...", /* newsgroup */ +/* < FILE_CACHE_OPEN > */ + "Error opening cache file in %s: %s", /* directory, error string */ }; #endif /* XRN_LANG_english */ @@ -671,9 +703,10 @@ * -------------- * section GERMAN * -------------- - * The GERMAN section was created and translated by - * K.Marquardt (K.Marquardt@zhv.basf-ag.de) Nov/23/94 - * + * The German section was created and translated by K.Marquardt + * (K.Marquardt@zhv.basf-ag.de). Some revisions were provided by + * T.Foks (foks@hub.de). + * * german version (iso8859-1), use LANGUAGE= german in Imakefile/Makefile * * values of the iso8859-1 characters: @@ -685,11 +718,11 @@ char *message_strings[] = { /* < BAD_BUTTON_NAME > */ - "XRN Fehler: Falscher Button Name `%s'.", /* button Name */ + "XRN Fehler: Falscher Knopf Name `%s'.", /* button Name */ /* < NO_SUCH_NG_DELETED > */ - "Newsgruppe `%s' existiert nicht, moeglicherweise wurde sie gel\366scht.", /* Newsgruppe Name */ + "Newsgruppe `%s' existiert nicht, m\366glicherweise wurde sie gel\366scht.", /* Newsgruppe Name */ /* < UNKNOWN_FUNC_RESPONSE > */ - "Interner XRN Fehler: unbekannte R\374ckmeldung%d von %s in %s.", /* return value, called function, calling function */ + "Interner XRN Fehler: unbekannte R\374ckmeldung %d von %s in %s.", /* return value, called function, calling function */ /* < DISPLAYING_LAST_UNREAD > */ "Keine ungelesenen Artikel in `%s'. Zeige letzten vorhandenen Artikel an.", /* Newsgruppe Name */ /* < PROBABLY_KILLED > */ @@ -705,7 +738,7 @@ /* < NO_PREV_NG > */ "Keine vorhergehende Newsgruppe.", /* < NO_GROUPS_SELECTED > */ - "Keine Newsgruppes ausgew\344hlt.", + "Keine Newsgruppen ausgew\344hlt.", /* < NG_NOT_MOVED > */ "Die neue Position kann nicht innerhalb der angew\344hlten Newsgruppen liegen. Es wurden keine Newsgruppen verschoben.", /* < SKIPPING_TO_NEXT_NG > */ @@ -747,11 +780,11 @@ /* < COULDNT_POST > */ "Kann Artikel nicht ver\366ffentlichen.", /* < POST_NOTALLOWED > */ - "Von diesem Rechner k\366nnen keine Artikel ver\366ffentlichent werden.", + "Von diesem Rechner k\366nnen keine Artikel ver\366ffentlicht werden.", /* < COULDNT_SEND > */ "Konnte Nachricht nicht senden.", /* < MAILED_TO_MODERATOR > */ - "Es wurden eine oder mehrere moderierte Newsgruppen angegeben. Der Artikel wird vom Server zum Moderator geschickt.", + "Es wurden eine oder mehrere moderierte Newsgruppen angegeben. Der Artikel wird vom Server zum Moderator geschickt.", /* < ARTICLE_POSTED > */ "Artikel ver\366ffentlicht.", /* < MAIL_MESSAGE_SENT > */ @@ -763,25 +796,25 @@ /* < CANT_OPEN_FILE > */ "Kann Datei `%s' nicht \366ffnen: %s.", /* Fehler string */ /* < NO_FILE_SPECIFIED > */ - "Keine Datei Angegeben.", + "Keine Datei angegeben.", /* < CANT_OPEN_TEMP > */ "Kann Datei `%s' nicht \366ffnen: %s.", /* file name, Fehler string */ /* < CANT_STAT_TEMP > */ - "Kann den Status der Datei `%s' nicht ermittlen: %s.", /* file name, Fehler string */ + "Kann den Status der Datei `%s' nicht ermitteln: %s.", /* file name, Fehler string */ /* < NO_CHANGE > */ "Keine Ver\344nderung der Datei `%s'.", /* file Name */ /* < ZERO_SIZE > */ "Datei `%s' ist leer.", /* file Name */ /* < NO_MSG_TEMPLATE > */ - "Interner XRN Fehler: Keine Nachrichten Vorlage beim Aufruf des Editors.", + "Interner XRN Fehler: Keine Nachrichtenvorlage beim Aufruf des Editors.", /* < CANT_EDITOR_CMD > */ "Kann Editorbefehl `%s'nicht ausf\374hren: %s.", /* command, Fehler string */ /* < ONE_COMPOSITION_ONLY > */ - "Nur ein Composition Panel m\366glich.", + "Verfassen nur eines Artikels gleichzeitig m\366glich.", /* < EXECUTING_SIGNATURE > */ "F\374hre Signaturbefehl `%s' aus.", /* command */ /* < CANT_EXECUTE_SIGNATURE > */ - "Kann Signaturedatei `%s' nicht ausf\374hren, lese sie ein.", /* signature file Name */ + "Kann Signaturdatei `%s' nicht ausf\374hren, lese sie ein.", /* signature file Name */ /* < READING_SIGNATURE > */ "Lese Signaturdatei `%s' ein.", /* signature file Name */ /* < CANT_READ_SIGNATURE > */ @@ -792,62 +825,74 @@ "Artikel nicht zur\374ckgezogen.", /* < CANCELLED_ART > */ "Artikel zur\374ckgezogen.", +/* < CANCEL_TO_MODERATOR > */ + "Message being canceled appears in one or more moderated newsgroups. Cancel request will be mailed to moderator by server.", /* < UNKNOWN_REGEXP_ERROR > */ "Unbekannter Fehler im Suchmuster `%s'.", /* regexp string */ /* < KNOWN_REGEXP_ERROR > */ "Fehler im Suchmuster `%s': %s.", /* regexp, Fehler string */ /* < ART_NUMBERING_PROBLEM > */ - "Probleme mit den Artikelnummern, markiere alle Artikel in `%s' als nicht gelesen.", /* Newsgruppe Name */ + "Probleme mit den Artikelnummern, markiere alle Artikel in `%s' als nicht gelesen.", /* Newsgruppe Name */ /* < CANT_OPEN_KILL > */ - "Kann %s Killfile (`%s') f\374r Newsgruppe `%s' nicht \366ffnen: %s.", /* "local" or "global", file name, Newsgruppe name, Fehler string */ + "Kann KILL-Datei (`%s') nicht \366ffnen: %s.", /* file name, error string */ +/* < CANT_OPEN_INCLUDED_KILL > */ + "Kann KILL-Datei (`%s') nicht \366ffnen (included from `%s'): %s.", /* file name, parent file name, error string */ /* < MALFORMED_KILL_ENTRY > */ - "Fehler im Killfileeintrag `%s' f\374r Newsgruppe `%s': %s.", /* entry, Newsgruppe name, reason for Fehler */ + "Fehler in KILL-Datei Eintrag `%s' in KILL-Datei `%s': %s.", /* entry, file, reason for error */ +/* < ERROR_INCLUDE_MISSING > */ + "Keine Newsgruppe oder Dateiname in include-Direktive angegeben", +/* < ERROR_INCLUDE_NOT_SEPARATED > */ + " Include Operand nicht separat", +/* < KILL_ERROR_UNKNOWN_OPTION > */ + "Fehler in KILL-Datei Eintrag `%s' in KILL-Datei `%s': Unbekannte Option `%c'.", /* entry, file, unknown option */ /* < UNKNOWN_KILL_REGEXP_ERROR > */ - "Unbekannter Suchmusterfehler im Killfileeintrag `%s'.", /* entry */ + "Fehler: Unbekannter Regul\344rer Ausdruck in KILL-Datei Eintrag `%s' in KILL-Datei `%s'.", /* entry */ /* < KNOWN_KILL_REGEXP_ERROR > */ - "Suchmusterfehler im Killfileeintrag `%s': %s.", /* entry, Fehler string */ + "Fehler: Regul\344rer Ausdruck in KILL-Datei Eintrag `%s' in KILL-Datei `%s': %s.", /* entry, error string */ +/* < KILL_TOO_LONG > */ + "Verwerfe zu langen Eintrag beginnend mit `%s' in KILL-Datei `%s'.", /* start of entry, file */ /* < NOT_IN_NEWSRC > */ - "Newsgruppe `%s' in der .newsrc Datei.", /* Newsgruppe Name */ + "Newsgruppe `%s' ist nicht in der Datei .newsrc.", /* Newsgruppe Name */ /* < BOGUS_NG_REMOVING > */ - "Newsgruppe `%s' existiert nicht, entferne sie aus der .newsrc Datei.", /* Newsgruppe Name */ + "Newsgruppe `%s' existiert nicht, entferne sie aus der Datei .newsrc.", /* Newsgruppe Name */ /* < MISSING_NG_LISTING > */ "Newsgruppe `%s' nicht im Cache gefunden. Hole Newsgruppen-Liste um sie zu finden.", /* newsgroup name */ /* < MAYBE_LIST > */ "Newsgruppe `%s' nicht im Cache gefunden.\nNewsgruppen-Liste holen, um sie zu finden?", /* newsgroup name */ /* < DUP_NEWSRC_ENTRY > */ - "Doppelter Eintrag in der .newsrc Datei f\374r `%s', verwende den ersten.", /* newsgroup name */ + "Doppelter Eintrag in der Datei .newsrc f\374r `%s', verwende den ersten.", /* newsgroup name */ /* < BAD_NEWSRC_LINE > */ - "Kann Zeile %d in der .newsrc Datei nicht interpretieren, \374bergehe sie.", /* line number */ + "Kann Zeile %d in der Datei .newsrc nicht interpretieren, \374bergehe sie.", /* line number */ /* < CANT_OPEN_NEWSRC_COPYING > */ - "Kann .newsrc Datei `%s' nicht kopieren: %s.", /* file name, Fehler string */ + "Kann Datei .newsrc `%s' nicht kopieren: %s.", /* file name, Fehler string */ /* < CANT_EXPAND > */ "Kann Dateinamen `%s' nicht erweitern.", /* file name */ /* < EMPTY_NEWSRC_SAVE_NAME > */ - "Dateiname zum Abspeichern der .newsrc Datei ist leer.", + "Dateiname zum Abspeichern der Datei .newsrc ist leer.", /* < CANT_OPEN_NEWSRC_SAVE > */ - "Kann .newsrc Datei `%s' nicht schreiben: %s.", /* file name, Fehler string */ + "Kann .newsrc-Datei `%s' nicht schreiben: %s.", /* file name, Fehler string */ /* < NEWSRC_SAVE_FILE_WRITE_ERR > */ - "Fehler beim Schreiben in .newsrc Datei `%s': %s.", /* file name, Fehler string */ + "Fehler beim Schreiben in .newsrc-Datei `%s': %s.", /* file name, Fehler string */ /* < CANT_READ_NEWSRC > */ - "Kann .newsrc Datei `%s' nicht lesen: %s.", /* file Name, error string */ + "Kann .newsrc-Datei `%s' nicht lesen: %s.", /* file Name, error string */ /* < CREATING_NEWSRC > */ - "Erzeuge .newsrc Datei `%s'.", /* file Name */ + "Erzeuge .newsrc-Datei `%s'.", /* file Name */ /* < CANT_CREATE_NEWSRC > */ - "Kann .newsrc Datei `%s' nicht erstellen: %s.", /* file name, Fehler string */ + "Kann .newsrc-Datei `%s' nicht erstellen: %s.", /* file name, Fehler string */ /* < CANT_STAT_NEWSRC > */ - "Kann Status der .newsrc Datei `%s' nicht ermitteln: %s.", /* file name, Fehler string */ + "Kann Status der .newsrc-Datei `%s' nicht ermitteln: %s.", /* file name, Fehler string */ /* < ZERO_LENGTH_NEWSRC > */ - ".newsrc Datei `%s' ist leer, gebe auf.", /* file Name */ + ".newsrc-Datei `%s' ist leer, gebe auf.", /* file Name */ /* < CANT_OPEN_NEWSRC > */ - "Kann .newsrc Datei `%s' nicht lesen: %s.", /* file name, Fehler string */ + "Kann .newsrc-Datei `%s' nicht lesen: %s.", /* file name, Fehler string */ /* < CANT_PARSE_NEWSRC > */ - "Kann .newsrc Datei `%s' nicht interpretieren -- Fehler in Zeile %d.", /* file name, error line */ + "Kann .newsrc-Datei `%s' nicht interpretieren -- Fehler in Zeile %d.", /* file name, error line */ /* < CANT_OPEN_NEWSRC_TEMP > */ - "Kann .newsrc Datei `%s' nicht schreiben: %s.", /* file name, Fehler string */ + "Kann .newsrc-Datei `%s' nicht schreiben: %s.", /* file name, Fehler string */ /* < CANT_OPEN_NEWSRC_WRITING > */ - "Kann .newsrc Datei `%s' nicht schreiben: %s.", /* file name, Fehler string */ + "Kann .newsrc-Datei `%s' nicht schreiben: %s.", /* file name, Fehler string */ /* < ERROR_UNLINKING_NEWSRC > */ - "Fehler beim Entfernen des Links der .newsrc Datei `%s': %s.", /* file name, Fehler string */ + "Fehler beim Entfernen des Links der .newsrc-Datei `%s': %s.", /* file name, Fehler string */ /* < ERROR_RENAMING > */ "Fehler beim Umbenennen der Datei `%s' in `%s': %s.", /* temporary file name, file name, Fehler string */ /* < NO_MAIL_DIR > */ @@ -861,15 +906,15 @@ /* < FOLDER_NOT_DIR > */ "Pfad `%s' ist kein Verzeichnis.", /* folder Name */ /* < NO_SUCH_RMAIL > */ - "Keine solche RMAIL Datei `%s'; Datei anlegen?", /* file name */ + "Keine RMAIL-Datei `%s'; Datei anlegen?", /* file name */ /* < CANT_OPEN_RMAIL > */ - "Kann RMAIL Datei `%s' nicht \366ffnen: %s.", /* file name, Fehler string */ + "Kann RMAIL-Datei `%s' nicht \366ffnen: %s.", /* file name, Fehler string */ /* < CANT_WRITE_RMAIL > */ - "Kann nicht in RMAIL Datei `%s' schreiben: %s.", /* file name, Fehler string */ + "Kann nicht in die RMAIL-Datei `%s' schreiben: %s.", /* file name, Fehler string */ /* < UNKNOWN_CONFIRM_BUTTON > */ - "XRN Fehler: unbekannter Best\344tigungsbutton `%s'.", /* button Name */ + "XRN Fehler: unbekannter Best\344tigungsknopf `%s'.", /* button Name */ /* < CANT_EXECUTE_CMD_POPEN > */ - "Konnte Befehl `%s' nicht ausfuehren, `popen' fehlgeschlagen.", /* command string */ + "Konnte Befehl `%s' nicht ausf\374hren, `popen' fehlgeschlagen.", /* command string */ /* < CANT_EXPAND_DIR > */ "Kann Verzeichnisname `%s' nicht erweitern.", /* directory name */ /* < CANT_CREATE_SAVE_DIR > */ @@ -877,7 +922,7 @@ /* < CANT_FIGURE_FILE_NAME > */ "Kann Dateinamen `%s' nicht ermitteln.", /* file name */ /* < CANT_CREAT_APPEND_SAVE_FILE > */ - "Kann %s Datei `%s' nicht ausf\374hren: %s.", /* "create" or "append to", file name, Fehler string */ + "%s Datei `%s' nicht ausf\374hrbar: %s.", /* "create" or "append to", file name, Fehler string */ /* < ERROR_WRITING_FILE > */ /* < ERROR_WRITING_SAVE_FILE > */ "Fehler beim Schreiben in Datei `%s': %s.", /* file name, Fehler string */ @@ -888,7 +933,7 @@ /* < GETTING_NEWGROUPS > */ "Lese die Liste der neuen Newsgruppen...", /* < FAILED_CONNECT > */ - "Verbindungsaufbau zum NNTP server `%s' fehlgeschlagen .", /* server name */ + "Verbindungsaufbau zum NNTP Server `%s' fehlgeschlagen .", /* server name */ /* < LOST_CONNECT_ATTEMPT_RE > */ "Verbindung zum NNTP Server abgebrochen, versuche neuen Verbindungsaufbau.", /* < RECONNECTED > */ @@ -898,7 +943,7 @@ /* < CANT_CREATE_TEMP > */ "Kann kann tempor\344re Datei `%s' nicht schreiben: %s.", /* file name, Fehler string */ /* < BOGUS_ACTIVE_ENTRY > */ - "\334bergehe fehlerhaften Eintrag `%s' der active Datei.", /* entry */ + "\334bergehe fehlerhaften Eintrag `%s' der active-Datei.", /* entry */ /* < BOGUS_ACTIVE_CACHE > */ "\334bergehe fehlerhaften Eintrag `%s' im Cache.", /* entry */ /* < XHDR_ERROR > */ @@ -908,7 +953,7 @@ /* < MALFORMED_XHDR_RESPONSE > */ "NNTP Server sendete nicht erwartete XHDR Antwort.\nXHDR Befehl: `%s', Antwort `%s'.", /* command, response */ /* < NO_APP_DEFAULTS > */ - "Die XRN Application Defaults Datei ist nicht vorhanden. Es ist m\366glich das einige XRN Funktionen nicht gehen. Falls XRN von jemand anderem installiert wurde, teilen Sie ihm bitte diesen Fehler mit. Wenn Sie XRN installiert haben, lesen Sie bitte die Datei COMMON-PROBLMS im XRN Quellcode Verzeichnis, dort steht wie Sie das Problem beheben koennen. ", + "Die XRN Application-Defaults-Datei ist nicht vorhanden. Es ist m\366glich das einige XRN Funktionen nicht gehen. Falls XRN von jemand anderem installiert wurde, teilen Sie ihm bitte diesen Fehler mit. Wenn Sie XRN installiert haben, lesen Sie bitte die Datei COMMON-PROBLEMS im XRN Quellcode Verzeichnis, dort steht wie Sie das Problem beheben k\366nnen.", /* < VERSIONS > */ "Application Defaults Version ist `%s', XRN Version ist `%s'.", /* app-defaults version, executable version */ /* < NO_DOMAIN > */ @@ -922,48 +967,54 @@ /* < OPEARATION_APPLY_CURSOR > */ "Operationen sind abh\344nging von der aktuellen Auswahl und der Position des Zeigers", /* < NO_MORE_UNREAD_ART > */ - "Keine weiteren ungelesenen Artikel in den abbonierten Newsgruppen.", + "Keine weiteren ungelesenen Artikel in den abonnierten Newsgruppen.", /* < SEL_GROUPS_ADDSUB > */ - "Zum abbonieren Newsgruppen ausw\344hlen, verbleibende werden als `nicht abboniert' gekennzeichnet.", + "Zum Abonnieren Newsgruppen ausw\344hlen, verbleibende werden als `nicht abonniert' gekennzeichnet.", /* < ARE_YOU_SURE > */ "Sind Sie sicher?", /* SUBED, UNSUBED and IGNORED strings must have the same len */ /* < SUBED > */ - "abboniert ", + "abonniert ", /* < UNSUBED > */ - "nicht abboniert", + "nicht abonniert", /* < IGNORED > */ - "ignored ", + "ignoriert ", /* < OK_CATCHUP > */ "Als gelesen markieren?", /* < OK_CATCHUP_CUR > */ "Bis zur aktuellen Position als gelesen markieren?", /* < OK_GETLIST > */ - "OK to fetch newsgroup list from the server?", + "Newsgruppen Liste vom Server holen?", /* < OK_TO_UNSUB > */ - "Abbonenent aufheben?", + "Abonnement aufheben?", /* < OK > */ "OK", /* < EDIT > */ - "edit", + "Editieren", /* < SEARCH_ABORTED > */ "Suche wurde abgebrochen.", /* < ERROR_SUBJ_SEARCH > */ "Thema suchen: %s", /* regular expression */ /* < ERROR_SUBJ_EXH > */ "Thema nicht vorhanden.", +/* < ERROR_NO_PARENT > */ + "Article has no parent.", +/* < ERROR_PARENT_UNAVAIL > */ + "Article's parent is unavailable.", /* < ERROR_SUBJ_ABORT > */ "Suche abgebrochen.", -/* < ERROR_SUB_KILL > */ - "Thema wurde gel\366scht, gehe zum ersten ungelesenen Artikel.", -/* < ERROR_AUTHOR_KILL > */ - "Autor wurde gel\366scht, gehe zum ersten ungelesenen Artikel.", +/* < KILL_DONE > */ + "Gehe zum ersten ungelesenen Artikel.", +/* < UNKNOWN_KILL_TYPE > */ + "Unknown kill type \"%s\" in \"%s\" kill request.", /* field type argument, field name */ /* < ERROR_CANT_UPDATE_NEWSRC > */ "Kann die Datei .newsrc nicht aktualisieren.", /* < ARTICLE_NUMBER > */ "Artikel Nummer:", +/* < LIST_OLD_NUMBER > */ + "First article to list:", /* < ERROR_SUBJ_EXPR > */ - "Suche nach %s: Keine Eintrag gefunden.", /* regular expression */ + "Suche nach %s: Keine Eintr\344ge gefunden.", /* regular expression */ /* < ERROR_SEARCH > */ "Suche nach %s", /* regular expression */ /* < REGULAR_EXPR > */ @@ -971,19 +1022,21 @@ /* < BEHIND_WHAT_GROUP > */ "Nach welcher Newsgrupppe?", /* < ARTICLE_QUEUED > */ - "Artikel nacheinander versendet.", + "Artikel nacheinander versand.", /* < GROUP_SUB_TO > */ - "Zu abbonierende Gruppe:", + "Zu abonnierende Gruppe:", /* < GROUP_TO_GO > */ "Gehen zur Gruppe:", /* < VIEW_ALLNG_SUB > */ - "Anzeigen aller Gruppen mit der M\366glichkeit zum abbonieren.", + "Anzeigen aller Gruppen mit der M\366glichkeit zum Abonnieren.", +/* < SUB_DONE > */ + "You are now subscribed to `%s'.", /* newsgroup name */ /* < AUTOMATIC_RESCAN > */ "Automatische Abfrage des Servers wird ausgef\374hrt...", /* < RESCANNING_BACKGROUND > */ "Abfrage des Servers im Hintergrund...", /* < ERROR_UNSUP_TRANS > */ - "Nicht unterst\344tzte \334berstzung: %d nach %d", /* transition from, to */ + "Nicht unterst\344tzte \334bersetzung: %d nach %d", /* transition from, to */ /* < POST_FOLLOWUP > */ "Artikel", /* < FOLLOWUP_REPLY > */ @@ -993,7 +1046,7 @@ /* < SAVE_IN > */ "Sichere in %s", /* file */ /* < ERROR_SEND_MAIL > */ - "Fehler beim versenden einer Nachricht:", + "Fehler beim Versenden einer Nachricht:", /* < ASK_FILE > */ "Dateiname?", /* < ASK_POST_ARTICLE > */ @@ -1017,17 +1070,17 @@ /* < POST > */ "Artikel", /* < FOLLOWUP_MULTIPLE_NGS > */ - "The default `Newsgroups' line of your followup contains multiple newsgroups. Please be sure to remove inappropriate newsgroups before sending your message, and/or to put a more appropriate list of groups in your `Followup-To' line.", + "Die Standard 'Newsgroups'-Zeile Ihres Bezuges enth\344lt mehrere Newsgruppen. Bitte stellen Sie sicher, da\337 unzutreffende Newsgruppen entfernt sind bevor Sie den Artikel versenden und/oder f\374gen Sie zutreffendere Newsgruppen in die 'Followup-To'-Zeile ein.", /* < FOLLOWUP_FOLLOWUPTO > */ - "Note that the article to which you are responding contains a `Followup-To' line, so the default `Newsgroups' line of your followup has been set from that line rather than from the `Newsgroups' line of the original article.", + "Beachten Sie bitte, da\337 der Artikel, auf den Sie antworten, eine 'Followup-To'-Zeile enth\344lt. Deshalb wurde die Standard 'Newsgroups'-Zeile Ihres Bezuges auf den Inhalt dieser Zeile gesetzt, statt auf die 'Newsgroups'-Zeile des originalen Artikels.", /* < CROSSPOST_PROHIBIT > */ - "The `Newsgroups' line of your message contains %d newsgroups. The maximum\nnumber of groups to which you are allowed to post a message is %d. Please\nreduce the number of groups to which you are posting and then send your\nmessage again.", + "Die `Newsgroups'-Zeile Ihres Artikels enth\344lt %d Newsgruppen. Die maximale\nAnzahl von Newsgruppen an die Sie versenden d\374rfen betr\344gt %d. Bitte\nreduzieren Sie die Anzahl der Newsgruppen an die Sie versenden m\366chten und versenden\nSie dann Ihren Artikel erneut.", /* < CROSSPOST_CONFIRM > */ - "The `Newsgroups' line of your message contains %d newsgroups. Please\nconsider reducing the number of groups to which you are posting.", + "Die `Newsgroups'-Zeile Ihres Artikels enth\344lt %d Newsgruppen. Bitte\nerw\344gen Sie eine Reduzierung der Newsgruppen an die Sie versenden m\366chten.", /* < FOLLOWUP_FOLLOWUPTO_CONFIRM > */ - "The `Newsgroups' line of your message contains %d newsgroups, and the\n`Followup-To' line contains %d newsgroups. Please consider reducing the\nnumber of groups in your `Newsgroups' and/or `Followup-To' line.", + "Die 'Newsgroups'-Zeile Ihres Artikels enth\344lt %d Newsgruppen und die\n`Followup-To'-Zeile enth\344lt %d Newsgruppen. Bitte erw\344gen Sie eine Reduzierung der\nNewsgruppen in Ihrer `Newsgroups'- und/oder `Followup-To'-Zeile.", /* < FOLLOWUP_CONFIRM > */ - "The `Newsgroups' line of your message contains %d newsgroups. Please consider\neither reducing the number of newsgroups to which you are posting or adding a\n`Followup-To' line containing a smaller number of groups.", + "Die 'Newsgroups'-Zeile Ihres Artikels enth\344lt %d Newsgruppen. Bitte erw\344gen Sie\nentweder eine Reduzierung der Anzahl der Newsgruppen an die Sie versenden m\366chten oder ein Hinzuf\374gen einer\n`Followup-To'-Zeile, die eine geringere Anzahl an Newsgruppen enth\344lt.", /* < ERROR_STRIPFIELD_NL > */ "Kein Zeilenvorschub in stripField gefunden.\n", /* < FOLLOWUP_REPLY_TO_TITLE > */ @@ -1047,7 +1100,7 @@ /* < POST_MAIL_ARTICLE_TO > */ "Ver\366ffentliche Artikel an `%s' und versende ihn.", /* < MAIL > */ - "Versenden eine E-Mail.", + "Versenden einer E-Mail.", /* < USER_CANT_CANCEL > */ "Sie sind nicht berechtigt den Artikel zur\374ckzuziehen.", /* @@ -1063,11 +1116,11 @@ "In article %s, %s writes:\n", /* messageid , author */ /* #### Ende des evtl. nicht uebersetzen #### */ /* < NEWSGROUPS_INDEX > */ - "%10s %7s %*s %4d Artikel%1.1s +%5d alt", + "%10s %7s %*s %4d Artikel%1.1s +%6d alt", /* < UNREAD > */ "Ungelesene", /* < NEWS_IN > */ - "News in", + "Nachrichten in", /* < NOT_ONE > */ " ", /* see NEWSGROUPS_INDEX in STRING section */ /* < DONE > */ @@ -1079,21 +1132,23 @@ /* < CREATE > */ "erzeugen", /* < APPEND > */ - "anh\344ngen", + "anh\344ngen an", /* < ERR_XRN_RUN > */ - "XRN l\344uft bereits auf %s als Prozess %d.\nFalls es nicht mehr l\344uft entfernen sie die Datei `%s'.\n", /* host, pid, lockfile */ + "XRN l\344uft bereits auf %s als Proze\337 %d.\nFalls es nicht mehr l\344uft entfernen sie die Datei `%s'.\n", /* host, pid, lockfile */ /* < ERROR_CANT_READ_NEWSRC > */ - "Kann .newsrc Datei nicht lesen", + "Kann Datei .newsrc nicht lesen", /* < PROCESS_KILL_FOR > */ - "Bearbeite KILL Datei f\374 Newsgruppe `%s'...", /* newsgroup */ + "Bearbeite KILL-Datei f\374 Newsgruppe `%s'...", /* newsgroup */ /* < ERROR_REGEX_NOSLASH > */ - "Fehlender Slash `/' am Ende des Suchmusters", + "Fehlender Schr\344gstrich `/' am Ende des Suchmusters", +/* < ERROR_REGEX_NOSLASH_START > */ + "no slash preceding the regular expression", /* < ERROR_REGEX_NOCOLON > */ "Kein Komma nach dem Suchmuster", /* < ERROR_REGEX_UNKNOWN_COMMAND > */ "Unbekannter Befehl im Suchmuster (Erlaubt: `j', `m' und `s')", /* < KILL_LINE > */ - "Processing kill line \"%s\" in %s.", + "Processing entry `%s' in KILL file `%s'.", /* < KILL_KILLED > */ "gel\366scht - %s", /* subject */ /* < KILL_UNREAD > */ @@ -1106,8 +1161,6 @@ "%d als ungelesen markierte Artikel%s in %s", /* count, "" or NOT_ONE_STRING , newsgroup */ /* < COUNT_SAVED > */ "%d gespeicherte Artikel%s in %s", /* count, "" or NOT_ONE_STRING , newsgroup */ -/* < COUNT_MATCHED > */ - "%d Artikel%s mit unbekannter Option gefunden in %s", /* count, "" or NOT_ONE_STRING , newsgroup */ /* < ERROR_CORNERED > */ "XRN Fehler in `cornered': erwarte g\374ltige nglist\n", /* < ERROR_OUT_OF_MEM > */ @@ -1135,15 +1188,27 @@ /* < ERROR_INFINITE_LOOP > */ "XRN Fehler: moveBeginning / moveEnd in Endlosschleife", /* < ERROR_FINDARTICLE > */ - "G\374ltige Artikelnummer nicht gefunden in findArticle (cursor.c)", + "G\374ltige Artikelnummer nicht gefunden in findArticle", /* < ERROR_STRIP_LEAVE_HEADERS > */ "Es darf nur eine der Ressourcen `stripHeaders', `leaveHeaders' angegeben werden.", /* < ERROR_REQUEST_FAILED > */ - " Request was: `%s'\n Failing response was: `%s'", /* command, message */ -/* < ASK_UPDATE_NEWSRC > */ - ".newsrc Datei wurde von einem anderen Programm ver\344ndert. Trotzdem schreiben?", + " Anfrage war: `%s'\n R\374ckmeldung war: `%s'", /* command, message */ +/* < ASK_FILE_MODIFIED > */ + "%s file\nhas been modified; overwrite it?", /* file name/type */ /* < PENDING_COMPOSITION > */ - "Verlassen nicht m\366glich, solange noch ein Artikel verfasst wird!", + "Verlassen nicht m\366glich, solange noch ein Artikel verfa\374t wird!", +/* < NNTP_PASSWORD > */ + "NNTP Passwort eingeben:", +/* < UNKNOWN_SORT_TYPE > */ + "Unbekannte Betreff Sortierung: %s", +/* */ + "Zu viele Betreff Sortierungen angegeben.", +/* < UNPARSEABLE_DATE > */ + "Unlesbares Datum (Artikel %ld in %s): %s", +/* < THREADING_FOR > */ + "F\344deln von Newsgruppe `%s'...", /* newsgroup */ +/* < FILE_CACHE_OPEN > */ + "Error opening cache file in %s: %s", /* directory, error string */ }; #endif /* XRN_LANG_german */ diff -u -d -r -N -P 8.02/news.h 9.00/news.h --- 8.02/news.h Wed Nov 15 09:22:00 1995 +++ 9.00/news.h Tue Jul 1 10:27:46 1997 @@ -2,7 +2,7 @@ #define NEWS_H /* - * $Id: news.h,v 1.14 1995/11/15 14:20:20 jik Exp $ + * $Id: news.h,v 1.28 1997/07/01 14:26:27 jik Exp $ */ /* @@ -36,9 +36,12 @@ */ #include "avl.h" +#include "hash.h" +#include "file_cache.h" +#include "xrn.h" typedef long art_num; /* easy way to pick out variables refering to articles */ -typedef short ng_num; /* easy way to pick out newsgroup variables */ +typedef unsigned short ng_num; /* easy way to pick out newsgroup variables */ extern avl_tree *NewsGroupTable; extern int ActiveGroupsCount; @@ -47,9 +50,18 @@ struct article { unsigned short status; /* ART_* */ char *subject; /* subject line */ + char *from; /* full value of the "From" line */ char *author; /* author name */ char *lines; /* number of lines in the article */ - char *filename; /* name of the article file */ + file_cache_file *base_file; /* the untranslated article file */ + file_cache_file *file; /* the displayed article file */ + char *newsgroups; /* newsgroups list (maybe) */ + char *date; /* date (maybe) */ + char *id; /* message ID (maybe) */ + char *references; /* references (maybe) */ + char *xref; /* xref (maybe) */ + art_num parent; /* parent article, for threading */ + art_num *children; /* child articles, for threading */ #ifdef ARTSTRUCT_C /* These should only be touched in artstruct.c! */ art_num first; @@ -69,7 +81,10 @@ art_num last; /* last article number */ art_num current; /* current article number */ struct list *nglist; /* newsgroup entry for unsubscribed groups */ - unsigned char from_cache; + unsigned char from_cache; /* is this entry from the active file cache? */ + unsigned char fetch; /* what should we fetch? */ + hash_table_object thread_table; + void *kill_file; #ifdef ARTSTRUCT_C /* These should only be touched in artstruct.c! */ struct article *articles, *ref_art; @@ -79,16 +94,19 @@ }; -#define ART_READ (1<<0) -#define ART_PRINTED (1<<1) -#define ART_FETCHED (1<<2) -#define ART_UNAVAIL (1<<3) -#define ART_ALL_HEADERS (1<<4) -#define ART_ROTATED (1<<5) -#define ART_SAVED (1<<6) -#define ART_MARKED (1<<7) -#define ART_XLATED (1<<8) -#define ART_KILLED (1<<9) +#define ART_READ (1<<0) +#define ART_PRINTED (1<<1) +#define ART_FETCHED (1<<2) +#define ART_UNAVAIL (1<<3) +#define ART_ALL_HEADERS (1<<4) +#define ART_ROTATED (1<<5) +#define ART_SAVED (1<<6) +#define ART_MARKED (1<<7) +#define ART_XLATED (1<<8) +#define ART_KILLED (1<<9) +#define ART_LISTED (1<<10) +#define ART_MAYBE_LISTED (1<<11) +#define ART_XREFED (1<<12) #define ART_CLEAR (0) #define ART_CLEAR_READ (ART_READ | ART_KILLED) @@ -109,6 +127,9 @@ #define IS_UNMARKED(art) (! ((art)->status & ART_MARKED)) #define IS_XLATED(art) ( (art)->status & ART_XLATED) #define IS_KILLED(art) ( (art)->status & ART_KILLED) +#define IS_LISTED(art) ( (art)->status & ART_LISTED) +#define IS_MAYBE_LISTED(art) ( (art)->status & ART_MAYBE_LISTED) +#define IS_XREFED(art) ( (art)->status & ART_XREFED) #define SET_READ(art) ((art)->status |= ART_READ) #define SET_UNREAD(art) ((art)->status &= ~ART_READ) @@ -129,32 +150,120 @@ #define SET_UNXLATED(art) ((art)->status &= ~ART_XLATED) #define SET_XLATED(art) ((art)->status |= ART_XLATED) #define SET_KILLED(art) ((art)->status |= ART_KILLED) +#define SET_LISTED(art) ((art)->status |= ART_LISTED) +#define SET_MAYBE_LISTED(art) ((art)->status |= ART_MAYBE_LISTED) +#define SET_UNLISTED(art) ((art)->status &= ~(ART_LISTED|ART_MAYBE_LISTED)) +#define SET_XREFED(art) ((art)->status |= ART_XREFED) -#define CLEAR_FILE(art) \ - if ((art)->filename != NIL(char)) { \ - (void) unlink((art)->filename); \ - SET_UNFETCHED(art); \ - FREE((art)->filename); \ - (art)->filename = NIL(char); \ - } +#define _CLEAR_ALL(art,free) \ + _CLEAR_FILE((art),(free)); \ + _CLEAR_BASE_FILE((art),(free)); \ + _CLEAR_SUBJECT((art),(free)); \ + _CLEAR_FROM((art),(free)); \ + _CLEAR_AUTHOR((art),(free)); \ + _CLEAR_LINES((art),(free)); \ + _CLEAR_NEWSGROUPS((art),(free)); \ + _CLEAR_DATE((art),(free)); \ + _CLEAR_ID((art),(free)); \ + _CLEAR_REFS((art),(free)); \ + _CLEAR_XREF((art),(free)); \ + _CLEAR_PARENT((art),(free)); \ + _CLEAR_CHILDREN((art),(free)); \ -#define CLEAR_SUBJECT(art) \ - if ((art)->subject != NIL(char)) { \ - FREE((art)->subject); \ - (art)->subject = NIL(char); \ - } +#define _CLEAR_FILE(art,free) \ + if ((free) && (art)->file) { \ + file_cache_file_release(FileCache, *(art)->file); \ + FREE((art)->file); \ + } \ + SET_UNFETCHED(art); \ + (art)->file = 0; -#define CLEAR_AUTHOR(art) \ - if ((art)->author != NIL(char)) { \ - FREE((art)->author); \ - (art)->author = NIL(char); \ - } +#define _CLEAR_BASE_FILE(art,free) \ + if ((free) && (art)->base_file) { \ + file_cache_file_release(FileCache, *(art)->base_file); \ + FREE((art)->base_file); \ + } \ + (art)->base_file = 0; -#define CLEAR_LINES(art) \ - if ((art)->lines != NIL(char)) { \ - FREE((art)->lines); \ - (art)->lines = NIL(char); \ - } +#define _CLEAR_SUBJECT(art,free) \ + if ((free) && (art)->subject) { \ + FREE((art)->subject); \ + } \ + (art)->subject = 0; + +#define _CLEAR_FROM(art,free) \ + if ((free) && (art)->from) { \ + FREE((art)->from); \ + } \ + (art)->from = 0; + +#define _CLEAR_AUTHOR(art,free) \ + if ((free) && (art)->author) { \ + FREE((art)->author); \ + } \ + (art)->author = 0; + +#define _CLEAR_LINES(art,free) \ + if ((free) && (art)->lines) { \ + FREE((art)->lines); \ + } \ + (art)->lines = 0; + +#define _CLEAR_NEWSGROUPS(art,free) \ + if ((free) && (art)->newsgroups) { \ + FREE((art)->newsgroups); \ + } \ + (art)->newsgroups = 0; + +#define _CLEAR_DATE(art,free) \ + if ((free) && (art)->date) { \ + FREE((art)->date); \ + } \ + (art)->date = 0; + +#define _CLEAR_ID(art,free) \ + if ((free) && (art)->id) { \ + FREE((art)->id); \ + } \ + (art)->id = 0; + +#define _CLEAR_REFS(art,free) \ + if ((free) && (art)->references) { \ + FREE((art)->references); \ + } \ + (art)->references = 0; + +#define _CLEAR_XREF(art,free) \ + if ((free) && (art)->xref) { \ + FREE((art)->xref); \ + } \ + (art)->xref = 0; + +#define _CLEAR_PARENT(art,free) \ + (art)->parent = 0; + +#define _CLEAR_CHILDREN(art,free) \ + if ((free) && (art)->children) { \ + FREE((art)->children); \ + } \ + (art)->children = 0; + +#define CLEAR_ALL(art) _CLEAR_ALL((art),1) +#define CLEAR_ALL_NO_FREE(art) _CLEAR_ALL((art),0) + +#define CLEAR_FILE(art) _CLEAR_FILE((art),1) +#define CLEAR_BASE_FILE(art) _CLEAR_BASE_FILE((art),1) +#define CLEAR_SUBJECT(art) _CLEAR_SUBJECT((art),1) +#define CLEAR_FROM(art) _CLEAR_FROM((art),1) +#define CLEAR_AUTHOR(art) _CLEAR_AUTHOR((art),1) +#define CLEAR_LINES(art) _CLEAR_LINES((art),1) +#define CLEAR_NEWSGROUPS(art) _CLEAR_NEWSGROUPS((art),1) +#define CLEAR_DATE(art) _CLEAR_DATE((art),1) +#define CLEAR_ID(art) _CLEAR_ID((art),1) +#define CLEAR_REFS(art) _CLEAR_REFS((art),1) +#define CLEAR_XREF(art) _CLEAR_XREF((art),1) +#define CLEAR_PARENT(art) _CLEAR_PARENT((art),1) +#define CLEAR_CHILDREN(art) _CLEAR_CHILDREN((art),1) #define NG_SUB (1<<0) /* newsgroup is subscribed/unsubscribed to */ #define NG_NOENTRY (1<<1) /* no entry in the .newsrc for this group */ @@ -185,9 +294,24 @@ extern struct newsgroup *CurrentGroup; /* current index into the newsrc array */ extern ng_num MaxGroupNumber; /* size of the newsrc array */ +/* + If this assertion fails, it means that you've got too many groups to + fit in an integer of ng_num's size, and you need to change the + typedef of ng_num above so that it is an integer with more bytes. + */ +#define INC_MAXGROUPNUMBER() { assert(MaxGroupNumber < NOT_IN_NEWSRC-1); \ + MaxGroupNumber++; } + extern struct newsgroup **Newsrc; /* sequence list for .newsrc file */ -#define NOT_IN_NEWSRC -1 /* must be less than 0 */ +/* + NOTE WELL: There are places in the code that depend on the fact that + when a variable of type ng_num with value 0 is decremented, it + acquires a value of NOT_IN_NEWSRC. I don't know of any systems on + which XRN works for which an unsigned integer type doesn't wrap + around like this, but if there is one, then the code will fail. + */ +#define NOT_IN_NEWSRC ((ng_num)-1) /* not a valid group (must be less than 0) */ #define NO_GROUP -1 diff -u -d -r -N -P 8.02/newsrc.l 9.00/newsrc.l --- 8.02/newsrc.l Wed Apr 19 18:02:40 1995 +++ 9.00/newsrc.l Wed Dec 31 19:00:00 1969 @@ -1,98 +0,0 @@ -/* Bug in some(?) versions of lex makes /{sep} not work in */ -/* the expressions below, thus the silliness with unput() etc */ - -/* FLEX (gnu lex) has some detailed, Posix-related changes */ -/* classic lex, implied below */ - -%{ -#if defined(FLEX_SCANNER) && !defined(YY_FLEX_LEX_COMPAT) -/* - * If you get an error on the line below when compiling, the problem - * is probably that you are using "flex -l" instead of "flex". You do - * not need to give flex the "-l" argument in order to build this - * file. You can probably solve this problem by (a) deleting - * lex.yy.c, (b) adding "LEX=flex" to the Imakefile, (c) rebuilding - * the Makefile from the Imakefile (with "xmkmf" or whatever else you - * used the first time), and (d) trying to compile again. - */ -int yylineno = 1; -#define YYLINE yylineno++ -#undef yywrap -#else -#define YYLINE -#endif -%} - -letter [a-zA-Z] -any [^ \t\n] -digit [0-9] -sep [:!] -%% - yyin = Newsrcfp; -^[ \t]*"\n" YYLINE; -"\n" {YYLINE; return(EOL); }; -"-" return(DASH); -"," return(COMMA); -{sep} {yylval.character = yytext[0]; return(SEPARATOR); }; -{digit}+ {yylval.integer = atoi((char *) yytext); return(NUMBER); }; -{letter}{any}+{sep} | -^{any}+{sep} { - char c = yytext[yyleng - 1]; - yytext[yyleng-1] = '\0'; - yylval.string = XtNewString((char *) yytext); - unput(c); - return(NAME); - }; -[ \t] ; -^"options ".*"\n" { - YYLINE; - optionsLine = XtNewString((char *) yytext); - optionsLine[utStrlen(optionsLine) - 1] = '\0'; - }; -%% -/* - * $Id: newsrc.l,v 1.6 1995/04/19 22:02:40 jik Exp $ - */ - -/* - * xrn - an X-based NNTP news reader - * - * Copyright (c) 1988-1993, Ellen M. Sentovich and Rick L. Spickelmier. - * - * Permission to use, copy, modify, and distribute this software and its - * documentation for any purpose and without fee is hereby granted, 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 the University of California not - * be used in advertising or publicity pertaining to distribution of - * the software without specific, written prior permission. The University - * of California makes no representations about the suitability of this - * software for any purpose. It is provided "as is" without express or - * implied warranty. - * - * THE UNIVERSITY OF CALIFORNIA DISCLAIMS ALL WARRANTIES WITH REGARD TO - * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS, IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA 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. - */ - -yywrap() -{ - return(1); -} - -/*ARGSUSED*/ -yyerror(s) -char *s; -{ - /* lint/kludge */ -#ifdef lint - (void) yyinput(); - (void) yyoutput(0); - (void) yyunput(0); -#endif /* lint */ -} - diff -u -d -r -N -P 8.02/newsrc.y 9.00/newsrc.y --- 8.02/newsrc.y Fri Sep 22 02:47:50 1995 +++ 9.00/newsrc.y Wed Dec 31 19:00:00 1969 @@ -1,180 +0,0 @@ -%{ -#if !defined(lint) && !defined(SABER) && !defined(GCC_WALL) -static char XRNrcsid[] = "$Id: newsrc.y,v 1.14 1995/09/22 06:47:47 jik Exp $"; -#endif - -/* - * xrn - an X-based NNTP news reader - * - * Copyright (c) 1988-1993, Ellen M. Sentovich and Rick L. Spickelmier. - * - * Permission to use, copy, modify, and distribute this software and its - * documentation for any purpose and without fee is hereby granted, 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 the University of California not - * be used in advertising or publicity pertaining to distribution of - * the software without specific, written prior permission. The University - * of California makes no representations about the suitability of this - * software for any purpose. It is provided "as is" without express or - * implied warranty. - * - * THE UNIVERSITY OF CALIFORNIA DISCLAIMS ALL WARRANTIES WITH REGARD TO - * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS, IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA 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. - */ - -/* - * newsrc.y - yacc parser for the newsrc file - */ - -#include "copyright.h" -#include "config.h" -#include "utils.h" -#include -#include -#include "avl.h" -#include "mesg.h" -#include "news.h" -#include "newsrcfile.h" -#include "mesg_strings.h" -#include "internals.h" - -extern int yylineno; - -int newsrc_mesg_name; - -%} - -%union { - int integer; - char *string; - char character; - struct list *item; -} - - -%token NAME -%token SEPARATOR -%token NUMBER -%token EOL -%token DASH -%token COMMA - -%type artlist -%type articles - -%start goal - -%% -goal : newsrc_file ; - -newsrc_file : newsrc_line - | newsrc_file newsrc_line ; - -newsrc_line : NAME SEPARATOR artlist EOL - { - struct newsgroup *newsgroup; - - if (! verifyGroup($1, &newsgroup)) { - struct list *current, *next; - - mesgPane(XRN_SERIOUS, newsrc_mesg_name, - BOGUS_NG_REMOVING_MSG, $1); - - for (current = $3; current; current = next) { - next = current->next; - XtFree((char *) current); - } - } else { - if (IS_NOENTRY(newsgroup) || IS_NEW(newsgroup)) { - CLEAR_NOENTRY(newsgroup); - CLEAR_NEW(newsgroup); - if ($2 == ':') - SET_SUB(newsgroup); - newsgroup->nglist = $3; - updateArticleArray(newsgroup); - newsgroup->newsrc = MaxGroupNumber; - Newsrc[MaxGroupNumber++] = newsgroup; - } else { - mesgPane(XRN_SERIOUS, newsrc_mesg_name, - DUP_NEWSRC_ENTRY_MSG, $1); - } - } - XtFree($1); - } - | NAME SEPARATOR EOL - { - struct newsgroup *newsgroup; - - if (! verifyGroup($1, &newsgroup)) - mesgPane(XRN_SERIOUS, newsrc_mesg_name, - BOGUS_NG_REMOVING_MSG, $1); - else { - if (IS_NOENTRY(newsgroup) || IS_NEW(newsgroup)) { - CLEAR_NOENTRY(newsgroup); - CLEAR_NEW(newsgroup); - if ($2 == ':') - SET_SUB(newsgroup); - newsgroup->nglist = NIL(struct list); - updateArticleArray(newsgroup); - newsgroup->newsrc = MaxGroupNumber; - Newsrc[MaxGroupNumber++] = newsgroup; - } else { - mesgPane(XRN_SERIOUS, newsrc_mesg_name, - DUP_NEWSRC_ENTRY_MSG, $1); - } - } - XtFree($1); - } - | error EOL { - mesgPane(XRN_SERIOUS, newsrc_mesg_name, BAD_NEWSRC_LINE_MSG, - yylineno - 1); /* yylineno stepped at EOL */ - yyerrok; - yyclearin; - } - ; - -artlist : articles - { - $$ = $1; - } - | artlist COMMA articles - { - struct list *temp; - - $$ = $1; - for (temp = $$; temp != NIL(struct list); temp = temp->next) { - if (temp->next == NIL(struct list)) { - temp->next = $3; - break; - } - } - } - ; - -articles : NUMBER - { - $$ = ALLOC(struct list); - $$->type = SINGLE; - $$->contents.single = (art_num) $1; - $$->next = NIL(struct list); - } - | NUMBER DASH NUMBER - { - $$ = ALLOC(struct list); - $$->type = RANGE; - $$->contents.range.start = (art_num) $1; - $$->contents.range.end = (art_num) $3; - $$->next = NIL(struct list); - } - ; - - -%% -#include "lex.yy.c" - diff -u -d -r -N -P 8.02/newsrcfile.c 9.00/newsrcfile.c --- 8.02/newsrcfile.c Tue Apr 16 00:57:15 1996 +++ 9.00/newsrcfile.c Wed Aug 20 19:44:38 1997 @@ -1,6 +1,6 @@ #if !defined(lint) && !defined(SABER) && !defined(GCC_WALL) -static char XRNrcsid[] = "$Id: newsrcfile.c,v 1.35 1996/04/16 04:56:15 jik Exp $"; +static char XRNrcsid[] = "$Id: newsrcfile.c,v 1.39 1997/08/20 23:44:24 jik Exp $"; #endif /* @@ -53,20 +53,25 @@ #include "varfile.h" #include "internals.h" #include "activecache.h" +#include "killfile.h" #ifndef R_OK #define R_OK 4 #endif -char *NewsrcFile; /* newsrc file name */ +static char *NewsrcFile; /* newsrc file name */ FILE *Newsrcfp; /* newsrc file FILE pointer */ char *optionsLine; /* `options' line */ struct stat fbuf; struct newsgroup **Newsrc = 0; static ng_num Newsrc_size = 0; -void checkNewsrcSize(size) -ng_num size; +static void freeNewsrc _ARGUMENTS((void)); + +void checkNewsrcSize( + _ANSIDECL(ng_num, size) + ) + _KNRDECL(ng_num, size) { if (size > Newsrc_size) { Newsrc = (struct newsgroup **) XtRealloc((char *) Newsrc, @@ -162,12 +167,14 @@ if (errno != ENOENT) { mesgPane(XRN_SERIOUS, 0, CANT_READ_NEWSRC_MSG, NewsrcFile, errmsg(errno)); + XtFree(NewsrcFile); return FATAL; } mesgPane(XRN_INFO, 0, CREATING_NEWSRC_MSG, NewsrcFile); if ((Newsrcfp = fopen(NewsrcFile, "w")) == NULL) { mesgPane(XRN_SERIOUS, 0, CANT_CREATE_NEWSRC_MSG, NewsrcFile, errmsg(errno)); + XtFree(NewsrcFile); return FATAL; } if (NEWUSER_GROUPS[0] == '/') { @@ -178,6 +185,7 @@ if(! (NewRCfp = fopen(NEWUSER_GROUPS, "r"))) { mesgPane(XRN_SERIOUS, 0, CANT_READ_NEWSRC_MSG, NEWUSER_GROUPS, errmsg(errno)); + XtFree(NewsrcFile); return FATAL; } while(fgets(tmpbuf, BUFSIZ-1, NewRCfp)) { @@ -198,16 +206,19 @@ if (stat(NewsrcFile, &buf) == -1) { mesgPane(XRN_SERIOUS, 0, CANT_STAT_NEWSRC_MSG, NewsrcFile, errmsg(errno)); + XtFree(NewsrcFile); return FATAL; } if (buf.st_size == 0) { mesgPane(XRN_SERIOUS, 0, ZERO_LENGTH_NEWSRC_MSG, NewsrcFile); + XtFree(NewsrcFile); return FATAL; } if ((Newsrcfp = fopen(NewsrcFile, "r")) == NULL) { mesgPane(XRN_SERIOUS, 0, CANT_OPEN_NEWSRC_MSG, NewsrcFile, errmsg(errno)); + XtFree(NewsrcFile); return FATAL; } @@ -215,11 +226,15 @@ if (yyparse() != 0) { mesgPane(XRN_SERIOUS, 0, CANT_PARSE_NEWSRC_MSG, NewsrcFile, MaxGroupNumber +1); + XtFree(NewsrcFile); return FATAL; } - if (!copyNewsrcFile(NewsrcFile, savenewsrcfile)) - return FATAL; + if (!copyNewsrcFile(NewsrcFile, savenewsrcfile)) { + freeNewsrc(); + XtFree(NewsrcFile); + return FATAL; + } (void) fstat((int) fileno(Newsrcfp), &fbuf); @@ -232,6 +247,30 @@ return(OKAY); } +static void freeNewsrc() +{ + ng_num i; + + for (i = 0; i < MaxGroupNumber; i++) { + struct list *list, *next; + + /* Don't free the name, since it's also stored in the AVL table + which will be freed separately. */ + for (list = Newsrc[i]->nglist; list; list = next) { + next = list->next; + XtFree((char *)list); + } + /* There are no hash tables because we haven't threaded any groups + yet. */ + /* There are no kill files because we haven't read any kill files + yet. */ + artListFree(Newsrc[i]); + } + XtFree((char *)Newsrc); + Newsrc = 0; + MaxGroupNumber = 0; +} + static int ngEntryFprintf _ARGUMENTS((FILE *, struct newsgroup *)); static int ngEntryFprintf(newsrcfp, newsgroup) @@ -305,10 +344,12 @@ (void) stat(NewsrcFile, ¤tStat); if (lastStat.st_mtime && (currentStat.st_mtime > lastStat.st_mtime)) { - if (ConfirmationBox(TopLevel, ASK_UPDATE_NEWSRC_MSG, 0, 0, False) - == XRN_CB_ABORT) { - ehNoUpdateExitXRN(); - } + (void) sprintf(error_buffer, ASK_FILE_MODIFIED_MSG, "Newsrc", + NewsrcFile); + if (ConfirmationBox(TopLevel, error_buffer, 0, 0, False) + == XRN_CB_ABORT) { + ehNoUpdateExitXRN(); + } } tempfile = utTempFile(NewsrcFile); @@ -316,6 +357,7 @@ if ((newsrcfp = fopen(tempfile, "w")) == NULL) { mesgPane(XRN_SERIOUS, 0, CANT_OPEN_NEWSRC_TEMP_MSG, tempfile, errmsg(errno)); + XtFree(tempfile); return(FATAL); } @@ -323,6 +365,7 @@ if (cond) { \ (void) fclose(newsrcfp); \ (void) unlink(tempfile); \ + XtFree(tempfile); \ return(FATAL); \ } @@ -336,6 +379,9 @@ for (indx = 0; indx < MaxGroupNumber; indx++) { struct newsgroup *newsgroup = Newsrc[indx]; + write_kill_file(newsgroup, KILL_LOCAL); + write_kill_file(newsgroup, KILL_GLOBAL); + FAILIF(fprintf(newsrcfp, "%s%c", newsgroup->name, (IS_SUBSCRIBED(newsgroup) ? ':' : '!')) == EOF); @@ -413,7 +459,8 @@ if (retval == EOF) { (void) unlink(tempfile); - return(FATAL); + XtFree(tempfile); + return(FATAL); } #ifdef ISC_TCP @@ -425,6 +472,7 @@ mesgPane(XRN_SERIOUS, 0, ERROR_UNLINKING_NEWSRC_MSG, NewsrcFile, errmsg(errno)); (void) unlink(tempfile); + XtFree(tempfile); return(FATAL); } #endif /* ISC_TCP */ @@ -433,8 +481,11 @@ mesgPane(XRN_SERIOUS, 0, ERROR_RENAMING_MSG, tempfile, NewsrcFile, errmsg(errno)); (void) unlink(tempfile); + XtFree(tempfile); return(FATAL); } + + XtFree(tempfile); (void) stat(NewsrcFile, &lastStat); diff -u -d -r -N -P 8.02/newsrcfile.h 9.00/newsrcfile.h --- 8.02/newsrcfile.h Sat Oct 28 14:56:38 1995 +++ 9.00/newsrcfile.h Wed Aug 20 19:44:39 1997 @@ -2,7 +2,7 @@ #define NEWSRCFILE_H /* - * $Id: newsrcfile.h,v 1.6 1995/10/28 18:56:27 jik Exp $ + * $Id: newsrcfile.h,v 1.8 1997/08/20 23:44:24 jik Exp $ */ /* @@ -36,13 +36,12 @@ #include "news.h" -extern void checkNewsrcSize _ARGUMENTS((/* ng_num */ int)); +extern void checkNewsrcSize _ARGUMENTS((ng_num)); /* return 1 for okay, 0 for fatal error */ extern int readnewsrc _ARGUMENTS((char *,char *)); extern int updatenewsrc _ARGUMENTS((void)); -extern char *NewsrcFile; extern FILE *Newsrcfp; extern char *optionsLine; diff -u -d -r -N -P 8.02/ngMode.c 9.00/ngMode.c --- 8.02/ngMode.c Fri Dec 22 05:28:27 1995 +++ 9.00/ngMode.c Fri Jul 18 07:57:27 1997 @@ -98,33 +98,33 @@ int NgActionsCount = XtNumber(NgActions); -ButtonList NewsgroupButtonList[] = { - {"ngQuit", ngQuitCallbacks, NGQUIT_EXSTR}, - {"ngRead", ngReadCallbacks, NGREAD_EXSTR}, - {"ngNext", ngNextCallbacks, NGNEXT_EXSTR}, - {"ngPrev", ngPrevCallbacks, NGPREV_EXSTR}, - {"ngScroll", ngScrollCallbacks, NGSCROLL_EXSTR}, - {"ngScrollBack", ngScrollBackCallbacks, NGSCROLLBACK_EXSTR}, - {"ngCatchUp", ngCatchUpCallbacks, NGCATCHUP_EXSTR}, - {"ngSubscribe", ngSubscribeCallbacks, NGSUBSCRIBE_EXSTR}, - {"ngUnsub", ngUnsubCallbacks, NGUNSUB_EXSTR}, - {"ngGoto", ngGotoCallbacks, NGGOTO_EXSTR}, - {"ngAllGroups", ngAllGroupsCallbacks, NGALLGROUPS_EXSTR}, - {"ngRescan", ngRescanCallbacks, NGRESCAN_EXSTR}, - {"ngGetList", ngGetListCallbacks, NGGETLIST_EXSTR}, - {"ngPrevGroup", ngPrevGroupCallbacks, NGPREVGROUP_EXSTR}, - {"ngListOld", ngListOldCallbacks, NGLISTOLD_EXSTR}, - {"ngSelect", ngSelectCallbacks, NGSELECT_EXSTR}, - {"ngMove", ngMoveCallbacks, NGMOVE_EXSTR}, - {"ngExit", ngExitCallbacks, NGEXIT_EXSTR}, - {"ngCheckPoint", ngCheckPointCallbacks, NGCHECKPOINT_EXSTR}, - {"ngGripe", ngGripeCallbacks, NGGRIPE_EXSTR}, - {"ngPost", ngPostCallbacks, NGPOST_EXSTR}, - {"ngPostAndMail", ngPostAndMailCallbacks, NGPOST_AND_MAIL_EXSTR}, - {"ngMail", ngMailCallbacks, MAIL_EXSTR}, +static ButtonList NewsgroupButtonList[] = { + {"ngQuit", ngQuitCallbacks, NGQUIT_EXSTR, True}, + {"ngRead", ngReadCallbacks, NGREAD_EXSTR, True}, + {"ngNext", ngNextCallbacks, NGNEXT_EXSTR, True}, + {"ngPrev", ngPrevCallbacks, NGPREV_EXSTR, True}, + {"ngScroll", ngScrollCallbacks, NGSCROLL_EXSTR, True}, + {"ngScrollBack", ngScrollBackCallbacks, NGSCROLLBACK_EXSTR, True}, + {"ngCatchUp", ngCatchUpCallbacks, NGCATCHUP_EXSTR, True}, + {"ngSubscribe", ngSubscribeCallbacks, NGSUBSCRIBE_EXSTR, True}, + {"ngUnsub", ngUnsubCallbacks, NGUNSUB_EXSTR, True}, + {"ngGoto", ngGotoCallbacks, NGGOTO_EXSTR, True}, + {"ngAllGroups", ngAllGroupsCallbacks, NGALLGROUPS_EXSTR, True}, + {"ngRescan", ngRescanCallbacks, NGRESCAN_EXSTR, True}, + {"ngGetList", ngGetListCallbacks, NGGETLIST_EXSTR, True}, + {"ngPrevGroup", ngPrevGroupCallbacks, NGPREVGROUP_EXSTR, True}, + {"ngListOld", ngListOldCallbacks, NGLISTOLD_EXSTR, True}, + {"ngSelect", ngSelectCallbacks, NGSELECT_EXSTR, True}, + {"ngMove", ngMoveCallbacks, NGMOVE_EXSTR, True}, + {"ngExit", ngExitCallbacks, NGEXIT_EXSTR, True}, + {"ngCheckPoint", ngCheckPointCallbacks, NGCHECKPOINT_EXSTR, True}, + {"ngGripe", ngGripeCallbacks, NGGRIPE_EXSTR, True}, + {"ngPost", ngPostCallbacks, NGPOST_EXSTR, True}, + {"ngPostAndMail", ngPostAndMailCallbacks, NGPOST_AND_MAIL_EXSTR, True}, + {"ngMail", ngMailCallbacks, MAIL_EXSTR, True}, }; -int NewsgroupButtonListCount = XtNumber(NewsgroupButtonList); +static int NewsgroupButtonListCount = XtNumber(NewsgroupButtonList); /* Set the current insertion point in the newsgroup widget, saving the @@ -691,11 +691,17 @@ String *string; Cardinal *count; { + Boolean newgroups = True; + if (CurrentMode != NEWSGROUP_MODE) { return; } + + if (count && *count && !strcasecmp(string[0], "nonewgroups")) + newgroups = False; + rescanServer(False); - determineMode(); + determineMode(newgroups); return; } @@ -708,7 +714,7 @@ static void getListNG() { rescanServer(True); - determineMode(); + determineMode(True); } void ngGetListFunction(widget, event, string, count) @@ -1011,9 +1017,12 @@ If it's not there, or if newsgroup is null, replace the whole list. */ -void redrawNewsgroupTextWidget(newsgroup, skip_last) - String newsgroup; - Boolean skip_last; +void redrawNewsgroupTextWidget( + _ANSIDECL(String, newsgroup), + _ANSIDECL(Boolean, skip_last) + ) + _KNRDECL(String, newsgroup) + _KNRDECL(Boolean, skip_last) { long GroupPosition, NewPosition; String new; @@ -1100,8 +1109,12 @@ /* * update the info line and update the newsgroup text window */ -void updateNewsgroupMode(prefetch, skip_last) - Boolean prefetch, skip_last; +void updateNewsgroupMode( + _ANSIDECL(Boolean, prefetch), + _ANSIDECL(Boolean, skip_last) + ) + _KNRDECL(Boolean, prefetch) + _KNRDECL(Boolean, skip_last) { if (CurrentMode != NEWSGROUP_MODE) return; @@ -1118,8 +1131,10 @@ * install the newsgroup mode buttons (and the delete the previous mode buttons) * and then go to newsgroup mode */ -void switchToNewsgroupMode(skip_last) - Boolean skip_last; +void switchToNewsgroupMode( + _ANSIDECL(Boolean, skip_last) + ) + _KNRDECL(Boolean, skip_last) { PreviousMode = CurrentMode; CurrentMode = NEWSGROUP_MODE; @@ -1221,6 +1236,11 @@ XtPointer data; XtIntervalId *id; { + String params[1]; + Cardinal num_params = 1; + + params[0] = "nonewgroups"; + if (CurrentMode != NEWSGROUP_MODE) { TimeOut = 0; return; @@ -1232,7 +1252,7 @@ TimeOut = 0; xrnBusyCursor(); infoNow(AUTOMATIC_RESCAN_MSG); - ngRescanFunction(NULL, NULL, NULL, NULL); + ngRescanFunction(NULL, NULL, params, &num_params); infoNow(""); xrnUnbusyCursor(); addTimeOut(); @@ -1240,6 +1260,21 @@ return; } +static void resizeNewsgroupText _ARGUMENTS((Widget, XtPointer, XEvent *, + Boolean *)); + +static void resizeNewsgroupText(widget, client_data, event, + continue_to_dispatch) + Widget widget; + XtPointer client_data; + XEvent *event; + Boolean *continue_to_dispatch; +{ + if (event->type == ConfigureNotify) { + redrawNewsgroupTextWidget(0, False); + } +} + void displayNewsgroupWidgets() { if (! NewsgroupFrame) { @@ -1249,6 +1284,9 @@ XawPanedSetRefigureMode(NewsgroupFrame, False); + setButtonActive(NewsgroupButtonList, "ngPost", PostingAllowed); + setButtonActive(NewsgroupButtonList, "ngPostAndMail", PostingAllowed); + /* The Box widget is managed only after its children have been placed in them because there is a @@ -1258,7 +1296,6 @@ NewsgroupButtonBox = ButtonBoxCreate("buttons", NewsgroupFrame);\ doButtons(app_resources.ngButtonList, NewsgroupButtonBox,\ NewsgroupButtonList, &NewsgroupButtonListCount, TOP);\ - XtManageChild(NewsgroupButtonBox);\ } #define INFO_LINE() {\ @@ -1285,13 +1322,12 @@ TopInfoLine = NewsgroupInfoLine; - setButtonSensitive(NewsgroupButtonBox, "ngPost", PostingAllowed); - setButtonSensitive(NewsgroupButtonBox, "ngPostAndMail", - PostingAllowed); - XawPanedSetRefigureMode(NewsgroupFrame, True); XtSetKeyboardFocus(NewsgroupFrame, NewsgroupText); + + XtAddEventHandler(NewsgroupText, StructureNotifyMask, FALSE, + resizeNewsgroupText, NULL); } else { TopInfoLine = NewsgroupInfoLine; diff -u -d -r -N -P 8.02/ngMode.h 9.00/ngMode.h --- 8.02/ngMode.h Mon Oct 2 12:33:30 1995 +++ 9.00/ngMode.h Thu Jun 5 07:11:42 1997 @@ -37,9 +37,9 @@ extern int NewsgroupDisplayMode; -extern void switchToNewsgroupMode _ARGUMENTS((/* Boolean */ int)); -extern void redrawNewsgroupTextWidget _ARGUMENTS((String, /* Boolean */ int)); -extern void updateNewsgroupMode _ARGUMENTS((/* Boolean */ int, /* Boolean */ int)); +extern void switchToNewsgroupMode _ARGUMENTS((Boolean)); +extern void redrawNewsgroupTextWidget _ARGUMENTS((String, Boolean)); +extern void updateNewsgroupMode _ARGUMENTS((Boolean, Boolean)); extern void doPrefetch _ARGUMENTS((Widget, XEvent *, String *, Cardinal *)); extern void addTimeOut _ARGUMENTS((void)); extern void removeTimeOut _ARGUMENTS((void)); diff -u -d -r -N -P 8.02/patchlevel.h 9.00/patchlevel.h --- 8.02/patchlevel.h Thu May 9 14:16:03 1996 +++ 9.00/patchlevel.h Thu Jan 15 21:36:44 1998 @@ -1 +1,5 @@ -#define XRN_VERSION "8.02" +#ifdef MOTIF +#define XRN_VERSION "9.00 (Motif)" +#else +#define XRN_VERSION "9.00" +#endif diff -u -d -r -N -P 8.02/rclex.l 9.00/rclex.l --- 8.02/rclex.l Wed Dec 31 19:00:00 1969 +++ 9.00/rclex.l Thu Jun 5 07:11:42 1997 @@ -0,0 +1,98 @@ +/* Bug in some(?) versions of lex makes /{sep} not work in */ +/* the expressions below, thus the silliness with unput() etc */ + +/* FLEX (gnu lex) has some detailed, Posix-related changes */ +/* classic lex, implied below */ + +%{ +#if defined(FLEX_SCANNER) && !defined(YY_FLEX_LEX_COMPAT) +/* + * If you get an error on the line below when compiling, the problem + * is probably that you are using "flex -l" instead of "flex". You do + * not need to give flex the "-l" argument in order to build this + * file. You can probably solve this problem by (a) deleting + * lex.yy.c, (b) adding "LEX=flex" to the Imakefile, (c) rebuilding + * the Makefile from the Imakefile (with "xmkmf" or whatever else you + * used the first time), and (d) trying to compile again. + */ +int yylineno = 1; +#define YYLINE yylineno++ +#undef yywrap +#else +#define YYLINE +#endif +%} + +letter [a-zA-Z] +any [^ \t\n] +digit [0-9] +sep [:!] +%% + yyin = Newsrcfp; +^[ \t]*"\n" YYLINE; +"\n" {YYLINE; return(EOL); }; +"-" return(DASH); +"," return(COMMA); +{sep} {yylval.character = yytext[0]; return(SEPARATOR); }; +{digit}+ {yylval.integer = atoi((char *) yytext); return(NUMBER); }; +{letter}{any}+{sep} | +^{any}+{sep} { + char c = yytext[yyleng - 1]; + yytext[yyleng-1] = '\0'; + yylval.string = XtNewString((char *) yytext); + unput(c); + return(NAME); + }; +[ \t] ; +^"options ".*"\n" { + YYLINE; + optionsLine = XtNewString((char *) yytext); + optionsLine[utStrlen(optionsLine) - 1] = '\0'; + }; +%% +/* + * $Id: rclex.l,v 1.2 1996/06/07 06:53:50 jik Exp $ + */ + +/* + * xrn - an X-based NNTP news reader + * + * Copyright (c) 1988-1993, Ellen M. Sentovich and Rick L. Spickelmier. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, 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 the University of California not + * be used in advertising or publicity pertaining to distribution of + * the software without specific, written prior permission. The University + * of California makes no representations about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + * + * THE UNIVERSITY OF CALIFORNIA DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA 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. + */ + +yywrap() +{ + return(1); +} + +/*ARGSUSED*/ +yyerror(s) +char *s; +{ + /* lint/kludge */ +#ifdef lint + (void) yyinput(); + (void) yyoutput(0); + (void) yyunput(0); +#endif /* lint */ +} + diff -u -d -r -N -P 8.02/rcyacc.y 9.00/rcyacc.y --- 8.02/rcyacc.y Wed Dec 31 19:00:00 1969 +++ 9.00/rcyacc.y Fri Jul 18 08:57:24 1997 @@ -0,0 +1,183 @@ +%{ +#if !defined(lint) && !defined(SABER) && !defined(GCC_WALL) +static char XRNrcsid[] = "$Id: rcyacc.y,v 1.6 1997/07/18 12:57:24 jik Exp $"; +#endif + +/* + * xrn - an X-based NNTP news reader + * + * Copyright (c) 1988-1993, Ellen M. Sentovich and Rick L. Spickelmier. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, 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 the University of California not + * be used in advertising or publicity pertaining to distribution of + * the software without specific, written prior permission. The University + * of California makes no representations about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + * + * THE UNIVERSITY OF CALIFORNIA DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA 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. + */ + +/* + * newsrc.y - yacc parser for the newsrc file + */ + +#include "copyright.h" +#include "config.h" +#include "utils.h" +#include +#include +#include +#include "avl.h" +#include "mesg.h" +#include "news.h" +#include "newsrcfile.h" +#include "mesg_strings.h" +#include "internals.h" + +extern int yylineno; + +int newsrc_mesg_name; + +%} + +%union { + int integer; + char *string; + char character; + struct list *item; +} + + +%token NAME +%token SEPARATOR +%token NUMBER +%token EOL +%token DASH +%token COMMA + +%type artlist +%type articles + +%start goal + +%% +goal : newsrc_file ; + +newsrc_file : newsrc_line + | newsrc_file newsrc_line ; + +newsrc_line : NAME SEPARATOR artlist EOL + { + struct newsgroup *newsgroup; + + if (! verifyGroup($1, &newsgroup, False)) { + struct list *current, *next; + + mesgPane(XRN_SERIOUS, newsrc_mesg_name, + BOGUS_NG_REMOVING_MSG, $1); + + for (current = $3; current; current = next) { + next = current->next; + XtFree((char *) current); + } + } else { + if (IS_NOENTRY(newsgroup) || IS_NEW(newsgroup)) { + CLEAR_NOENTRY(newsgroup); + CLEAR_NEW(newsgroup); + if ($2 == ':') + SET_SUB(newsgroup); + newsgroup->nglist = $3; + (void) updateArticleArray(newsgroup, False); + newsgroup->newsrc = MaxGroupNumber; + Newsrc[MaxGroupNumber] = newsgroup; + INC_MAXGROUPNUMBER(); + } else { + mesgPane(XRN_SERIOUS, newsrc_mesg_name, + DUP_NEWSRC_ENTRY_MSG, $1); + } + } + XtFree($1); + } + | NAME SEPARATOR EOL + { + struct newsgroup *newsgroup; + + if (! verifyGroup($1, &newsgroup, False)) + mesgPane(XRN_SERIOUS, newsrc_mesg_name, + BOGUS_NG_REMOVING_MSG, $1); + else { + if (IS_NOENTRY(newsgroup) || IS_NEW(newsgroup)) { + CLEAR_NOENTRY(newsgroup); + CLEAR_NEW(newsgroup); + if ($2 == ':') + SET_SUB(newsgroup); + newsgroup->nglist = NIL(struct list); + (void) updateArticleArray(newsgroup, False); + newsgroup->newsrc = MaxGroupNumber; + Newsrc[MaxGroupNumber] = newsgroup; + INC_MAXGROUPNUMBER(); + } else { + mesgPane(XRN_SERIOUS, newsrc_mesg_name, + DUP_NEWSRC_ENTRY_MSG, $1); + } + } + XtFree($1); + } + | error EOL { + mesgPane(XRN_SERIOUS, newsrc_mesg_name, BAD_NEWSRC_LINE_MSG, + yylineno - 1); /* yylineno stepped at EOL */ + yyerrok; + yyclearin; + } + ; + +artlist : articles + { + $$ = $1; + } + | artlist COMMA articles + { + struct list *temp; + + $$ = $1; + for (temp = $$; temp != NIL(struct list); temp = temp->next) { + if (temp->next == NIL(struct list)) { + temp->next = $3; + break; + } + } + } + ; + +articles : NUMBER + { + $$ = ALLOC(struct list); + $$->type = SINGLE; + $$->contents.single = (art_num) $1; + $$->next = NIL(struct list); + } + | NUMBER DASH NUMBER + { + $$ = ALLOC(struct list); + $$->type = RANGE; + $$->contents.range.start = (art_num) $1; + $$->contents.range.end = (art_num) $3; + $$->next = NIL(struct list); + } + ; + + +%% +#include "rclex.c" + diff -u -d -r -N -P 8.02/refile.c 9.00/refile.c --- 8.02/refile.c Fri Oct 27 03:47:17 1995 +++ 9.00/refile.c Fri Jul 4 13:30:10 1997 @@ -1,6 +1,6 @@ #if !defined(lint) && !defined(SABER) && !defined(GCC_WALL) -static char XRNrcsid[] = "$Id: refile.c,v 1.14 1995/10/27 07:47:17 jik Exp $"; +static char XRNrcsid[] = "$Id: refile.c,v 1.15 1997/07/04 17:30:10 jik Exp $"; #endif /* @@ -37,7 +37,7 @@ #include "config.h" #include "utils.h" #include -#ifdef SYSV +#if defined(SYSV) || defined(SVR4) #include #else #include diff -u -d -r -N -P 8.02/resources.c 9.00/resources.c --- 8.02/resources.c Fri May 3 02:19:33 1996 +++ 9.00/resources.c Tue Dec 16 22:05:11 1997 @@ -1,6 +1,5 @@ - #if !defined(lint) && !defined(SABER) && !defined(GCC_WALL) -static char XRNrcsid[] = "$Id: resources.c,v 1.47 1996/05/03 06:19:12 jik Exp $"; +static char XRNrcsid[] = "$Id: resources.c,v 1.60 1997/12/17 03:04:19 jik Exp $"; #endif /* @@ -36,9 +35,13 @@ #include "config.h" #include "utils.h" #include -#include -#include -#include +#ifdef MOTIF +# include +#else +# include +# include +# include +#endif #include #include "avl.h" #include "news.h" @@ -50,6 +53,7 @@ #include "error_hnds.h" #include "internals.h" #include "mesg_strings.h" +#include "sort.h" #ifndef XRN_APP_CLASS #define XRN_APP_CLASS "XRn" @@ -86,6 +90,10 @@ #define XtCCacheActive "CacheActive" #define XtNcacheFile "cacheFile" #define XtCCacheFile "CacheFile" +#define XtNcacheFilesMaxFiles "cacheFilesMaxFiles" +#define XtCCacheFilesMaxFiles "CacheFilesMaxFiles" +#define XtNcacheFilesMaxSize "cacheFilesMaxSize" +#define XtCCacheFilesMaxSize "CacheFilesMaxSize" #if SUPPORT_SILLY_CALVIN_ICON #define XtNcalvin "calvin" #define XtCCalvin "Calvin" @@ -97,6 +105,7 @@ #define XtNccForward "ccForward" #define XtNcmdLineNntpServer "cmdLineNntpServer" #define XtCCmdLineNntpServer "CmdLineNntpServer" +#define XtNcomplainAboutBadDates "complainAboutBadDates" #define XtNconfirm "confirm" #define XtCConfirm "Confirm" #define XtNdeadLetters "deadLetters" @@ -108,10 +117,8 @@ #define XtCDiscardOld "DiscardOld" #define XtNdisplayLineCount "displayLineCount" #define XtCDisplayLineCount "DisplayLineCount" -#ifdef REALLY_USE_LOCALTIME #define XtNdisplayLocalTime "displayLocalTime" #define XtCDisplayLocalTime "DisplayLocalTime" -#endif #define XtNdistribution "distribution" #define XtCDistribution "Distribution" #define XtNdomainName "domainName" @@ -139,8 +146,12 @@ #define XtCIncludeSep "IncludeSep" #define XtNinfo "info" #define XtCInfo "Info" +#define XtNkillFileName "killFileName" +#define XtCKillFileName "KillFileName" #define XtNkillFiles "killFiles" #define XtCKillFiles "KillFiles" +#define XtNkillTimeout "killTimeout" +#define XtCKillTimeout "KillTimeout" #define XtNleaveHeaders "leaveHeaders" #define XtCLeaveHeaders "LeaveHeaders" #define XtNlineLength "lineLength" @@ -161,6 +172,8 @@ #define XtCNewsrcFile "NewsrcFile" #define XtNngButtonList "ngButtonList" #define XtCNgButtonList "NgButtonList" +#define XtNnntpPort "nntpPort" +#define XtCNntpPort "NntpPort" #define XtNnntpServer "nntpServer" #define XtCNntpServer "NntpServer" #define XtNonlyShow "onlyShow" @@ -195,6 +208,9 @@ #define XtCSaveNewsrcFile "SaveNewsrcFile" #define XtNsavePostings "savePostings" #define XtCSavePostings "SavePostings" +#define XtNsaveSentMail "saveSentMail" +#define XtNsaveSentPostings "saveSentPostings" +#define XtCSaveSent "SaveSent" #define XtNsaveString "saveString" #define XtCSaveString "SaveString" #define XtNsignatureFile "signatureFile" @@ -223,6 +239,8 @@ #define XtCUnreadIconPixmap "UnreadIconPixmap" #define XtNupdateNewsrc "updateNewsrc" #define XtCUpdateNewsrc "UpdateNewsrc" +#define XtNvalidNewsgroups "validNewsgroups" +#define XtCValidNewsgroups "ValidNewsgroups" #define XtNverboseKill "verboseKill" #define XtCVerboseKill "VerboseKill" #define XtNversion "version" @@ -356,6 +374,10 @@ XtOffset(app_res,cacheActive), XtRBoolean, (XtPointer) &defaultFalse}, {XtNcacheFile, XtCCacheFile, XtRString, sizeof(char *), XtOffset(app_res,cacheFile), XtRString, (XtPointer) CACHEFILE}, + {XtNcacheFilesMaxFiles, XtCCacheFilesMaxFiles, XtRInt, sizeof(int), + XtOffset(app_res,cacheFilesMaxFiles), XtRImmediate, (XtPointer) 50}, + {XtNcacheFilesMaxSize, XtCCacheFilesMaxSize, XtRInt, sizeof(int), + XtOffset(app_res,cacheFilesMaxSize), XtRImmediate, (XtPointer) 0}, #if SUPPORT_SILLY_CALVIN_ICON {XtNcalvin, XtCCalvin, XtRBoolean, sizeof(Boolean), XtOffset(app_res,calvin), XtRBoolean, (XtPointer) &defaultFalse}, @@ -368,6 +390,8 @@ XtOffset(app_res,ccForward), XtRBoolean, (XtPointer) &defaultFalse}, {XtNcmdLineNntpServer, XtCCmdLineNntpServer, XtRString, sizeof(char *), XtOffset(app_res,cmdLineNntpServer), XtRString, (XtPointer) NULL}, + {XtNcomplainAboutBadDates, XtCDebug, XtRBoolean, sizeof(Boolean), + XtOffset(app_res,dumpCore), XtRBoolean, (XtPointer) &defaultFalse}, {XtNconfirm, XtCConfirm, XtRString, sizeof(char *), XtOffset(app_res,confirm), XtRString, (XtPointer) NULL}, {XtNdeadLetters, XtCDeadLetters, XtRString, sizeof(char *), @@ -378,10 +402,8 @@ XtOffset(app_res,discardOld), XtRBoolean, (XtPointer) &defaultFalse}, {XtNdisplayLineCount, XtCDisplayLineCount, XtRBoolean, sizeof(Boolean), XtOffset(app_res,displayLineCount), XtRBoolean, (XtPointer) &defaultTrue}, -#ifdef REALLY_USE_LOCALTIME {XtNdisplayLocalTime, XtCDisplayLocalTime, XtRBoolean, sizeof(Boolean), - XtOffset(app_res,displayLocalTime), XtRBoolean, (XtPointer) &defaultTrue}, -#endif + XtOffset(app_res,displayLocalTime), XtRBoolean, (XtPointer) &defaultFalse}, {XtNdistribution, XtCDistribution, XtRString, sizeof(char *), XtOffset(app_res,distribution), XtRString, (XtPointer) NULL}, {XtNdomainName, XtCDomainName, XtRString, sizeof(char *), @@ -428,8 +450,12 @@ XtOffset(app_res,includeSep), XtRBoolean, (XtPointer) &defaultTrue}, {XtNinfo, XtCInfo, XtRBoolean, sizeof(Boolean), XtOffset(app_res,info), XtRBoolean, (XtPointer) &defaultTrue}, + {XtNkillFileName, XtCKillFileName, XtRString, sizeof(String), + XtOffset(app_res,killFileName), XtRImmediate, (XtPointer) "KILL"}, {XtNkillFiles, XtCKillFiles, XtRBoolean, sizeof(Boolean), XtOffset(app_res,killFiles), XtRBoolean, (XtPointer) &defaultTrue}, + {XtNkillTimeout, XtCKillTimeout, XtRInt, sizeof(int), + XtOffset(app_res,killTimeout), XtRImmediate, (XtPointer) 0}, {XtNleaveHeaders, XtCLeaveHeaders, XtRString, sizeof(char *), XtOffset(app_res,leaveHeaders), XtRString, (XtPointer) NULL}, {XtNlineLength, XtCLineLength, XtRInt, sizeof(int), @@ -450,6 +476,8 @@ XtOffset(app_res,newsrcFile), XtRString, (XtPointer) NEWSRCFILE}, {XtNngButtonList, XtCNgButtonList, XtRString, sizeof(char *), XtOffset(app_res,ngButtonList), XtRString, (XtPointer) NULL}, + {XtNnntpPort, XtCNntpPort, XtRString, sizeof(char *), + XtOffset(app_res,nntpPort), XtRString, (XtPointer) NULL}, {XtNnntpServer, XtCNntpServer, XtRString, sizeof(char *), XtOffset(app_res,nntpServer), XtRString, (XtPointer) NULL}, {XtNonlyShow, XtCOnlyShow, XtRInt, sizeof(int), @@ -484,14 +512,18 @@ XtOffset(app_res,saveNewsrcFile), XtRString, (XtPointer) SAVENEWSRCFILE}, {XtNsavePostings, XtCSavePostings, XtRString, sizeof(char *), XtOffset(app_res,savePostings), XtRString, (XtPointer) SAVEPOSTINGS}, + {XtNsaveSentMail, XtCSaveSent, XtRString, sizeof(char *), + XtOffset(app_res,saveSentMail), XtRString, (XtPointer) 0}, + {XtNsaveSentPostings, XtCSaveSent, XtRString, sizeof(char *), + XtOffset(app_res,saveSentPostings), XtRString, (XtPointer) 0}, {XtNsaveString, XtCSaveString, XtRString, sizeof(char *), XtOffset(app_res,saveString), XtRString, (XtPointer) NULL}, {XtNsignatureFile, XtCSignatureFile, XtRString, sizeof(char *), XtOffset(app_res,signatureFile), XtRString, (XtPointer) SIGNATUREFILE}, {XtNsignatureNotify, XtCSignatureNotify, XtRBoolean, sizeof(Boolean), XtOffset(app_res,signatureNotify), XtRBoolean, (XtPointer) &defaultFalse}, - {XtNsortedSubjects, XtCSortedSubjects, XtRBoolean, sizeof(Boolean), - XtOffset(app_res,sortedSubjects), XtRBoolean, (XtPointer) &defaultFalse}, + {XtNsortedSubjects, XtCSortedSubjects, XtRString, sizeof(String), + XtOffset(app_res,sortedSubjects), XtRImmediate, (XtPointer) 0}, {XtNstayInArticleMode, XtCStayInArticleMode, XtRBoolean, sizeof(Boolean), XtOffset(app_res,stayInArticleMode), XtRBoolean, (XtPointer) &defaultFalse}, {XtNstripHeaders, XtCStripHeaders, XtRString, sizeof(char *), @@ -514,6 +546,8 @@ XtOffset(app_res,unreadIconPixmap), XtRPixmap, (XtPointer) NULL}, {XtNupdateNewsrc, XtCUpdateNewsrc, XtRBoolean, sizeof(Boolean), XtOffset(app_res,updateNewsrc), XtRBoolean, (XtPointer) &defaultFalse}, + {XtNvalidNewsgroups, XtCValidNewsgroups, XtRString, sizeof(char *), + XtOffset(app_res,validNewsgroups), XtRString, (XtPointer) NULL}, {XtNverboseKill, XtCVerboseKill, XtRString, sizeof(String), XtOffset(app_res,verboseKill), XtRString, (XtPointer) "jms"}, {XtNversion, XtCVersion, XtRString, sizeof(char *), @@ -554,10 +588,8 @@ {"+discardOld", XtNdiscardOld, XrmoptionNoArg, (XtPointer) "on"}, {"-displayLineCount",XtNdisplayLineCount,XrmoptionNoArg, (XtPointer) "off"}, {"+displayLineCount",XtNdisplayLineCount,XrmoptionNoArg, (XtPointer) "on"}, -#ifdef REALLY_USE_LOCALTIME {"-displayLocalTime",XtNdisplayLocalTime,XrmoptionNoArg, (XtPointer) "off"}, {"+displayLocalTime",XtNdisplayLocalTime,XrmoptionNoArg, (XtPointer) "on"}, -#endif {"-distribution", XtNdistribution, XrmoptionSepArg, (XtPointer) NULL}, {"-dumpCore", XtNdumpCore, XrmoptionNoArg, (XtPointer) "off"}, {"+dumpCore", XtNdumpCore, XrmoptionNoArg, (XtPointer) "on"}, @@ -685,9 +717,7 @@ puts("\t-defaultLines number\tDefault number of lines above cursor"); puts("\t+/-discardOld\t\tDiscard unshown articles when onlyShow is in effect"); puts("\t+/-displayLineCount\tDisplay line count in the subject index"); -#ifdef REALLY_USE_LOCALTIME puts("\t+/-displayLocalTime\t\tDisplay local time in the Date: field"); -#endif puts("\t-distribution\t\tDefault distribution for messages"); #ifdef DUMPCORE puts("\t+/-dumpCore\t\tDump core on error exit"); @@ -700,7 +730,7 @@ puts("\t-iconGeometry +X+Y\tPosition of icon"); puts("\t-iconName\t\tIcon name used when unread articles"); puts("\t-iconPixmap\t\tIcon used when unread articles"); - puts("\t-ignoreNewsgroups list\tRegexps of newsgroups to ignore"); + puts("\t-ignoreNewsgroups list\tRegexps of valid newsgroups to ignore"); puts("\t-includeCommand\t\tCommand to use for article insertions\n\t\t\t\t(defaults to the toolkit editor)"); puts("\t+/-includeHeader\tInclude original article's header"); puts("\t-includePrefix\t\tPrefix for included lines"); @@ -734,7 +764,7 @@ puts("\t-saveString\t\tString to use in the save dialog"); puts("\t-signatureFile file\tSignature file for posting"); puts("\t+/-signatureNotify\tNotify user which signature file is being used"); - puts("\t+/-sortedSubjects\tSort or do not sort the subjects"); + puts("\t+/-sortedSubjects\tSubject sorting order"); puts("\t+/-stayInArticleMode\tSwitch to the next newsgroup whenever possible\n\t\t\t\tinstead of exiting article mode"); puts("\t-stripHeaders list\tHeaders to strip"); puts("\t+/-subjectRead\t\tChange default from next unread to subject next"); @@ -839,6 +869,8 @@ app_resources.saveMode = 0; if (utSubstring(app_resources.strSaveMode, "mailbox") == 1) { app_resources.saveMode |= MAILBOX_SAVE; + } else if (utSubstring(app_resources.strSaveMode, "formfeed") == 1) { + app_resources.saveMode |= FORMFEED_SAVE; } else { app_resources.saveMode |= NORMAL_SAVE; } @@ -972,6 +1004,14 @@ app_resources.breakLength = 0; app_resources.lineLength = 0; } + + /* + Authentication + */ + if (getenv("NNTPAUTH")) + app_resources.authenticator = getenv("NNTPAUTH"); + + art_sort_parse_sortlist(app_resources.sortedSubjects); return widget; } diff -u -d -r -N -P 8.02/resources.h 9.00/resources.h --- 8.02/resources.h Thu May 2 08:28:01 1996 +++ 9.00/resources.h Tue Dec 16 22:05:12 1997 @@ -2,7 +2,7 @@ #define RESOURCES_H /* - * $Id: resources.h,v 1.26 1996/05/02 12:27:41 jik Exp $ + * $Id: resources.h,v 1.36 1997/12/17 03:04:19 jik Exp $ */ /* @@ -65,14 +65,15 @@ char *saveNewsrcFile; Boolean cacheActive; char *cacheFile; + int cacheFilesMaxFiles, cacheFilesMaxSize; char *signatureFile; Boolean signatureNotify, executableSignatures, localSignatures; - char *nntpServer, *cmdLineNntpServer; + char *nntpPort, *nntpServer, *cmdLineNntpServer; int topLines; int saveMode; char *leaveHeaders; char *stripHeaders; - char *savePostings; + char *savePostings, *saveSentMail, *saveSentPostings; char *deadLetters; int minLines; int maxLines; @@ -85,6 +86,8 @@ char *confirm; int confirmMode; Boolean killFiles; + String killFileName; + int killTimeout; #if SUPPORT_SILLY_CALVIN_ICON Boolean calvin; #endif @@ -103,7 +106,7 @@ int breakLength; int rescanTime; Boolean pageArticles; - Boolean sortedSubjects; + String sortedSubjects; Boolean typeAhead; int prefetchMax; char *addButtonList; @@ -112,14 +115,12 @@ char *artButtonList; char *artSpecButtonList; char *printCommand; - Boolean dumpCore; + Boolean dumpCore, complainAboutBadDates; String verboseKill; Boolean cc; Boolean ccForward; Boolean authorFullName; -#ifdef REALLY_USE_LOCALTIME Boolean displayLocalTime; -#endif Boolean displayLineCount; Boolean resetSave; char *saveString; @@ -127,6 +128,7 @@ char *mhPath; int onlyShow; char *ignoreNewsgroups; + char *validNewsgroups; char *domainName; char *authenticatorCommand; char *authenticator; @@ -153,6 +155,7 @@ #define MAILBOX_SAVE 0x01 #define NORMAL_SAVE 0x02 +#define FORMFEED_SAVE 0x04 #define HEADERS_SAVE 0x10 #define NOHEADERS_SAVE 0x20 diff -u -d -r -N -P 8.02/save.c 9.00/save.c --- 8.02/save.c Wed Sep 27 15:32:21 1995 +++ 9.00/save.c Tue Jul 1 10:27:46 1997 @@ -1,6 +1,6 @@ #if !defined(lint) && !defined(SABER) && !defined(GCC_WALL) -static char XRNrcsid[] = "$Id: save.c,v 1.26 1995/09/27 19:32:15 jik Exp $"; +static char XRNrcsid[] = "$Id: save.c,v 1.30 1997/07/01 14:26:13 jik Exp $"; #endif /* @@ -48,6 +48,7 @@ #include "save.h" #include "mesg_strings.h" #include "refile.h" +#include "file_cache.h" extern int errno; @@ -203,19 +204,30 @@ return(utTildeExpand(dummy)); } -int saveArticleByNumber(filename, art, printing) - char *filename; - art_num art; - Boolean printing; +int saveArticleByNumber( + _ANSIDECL(char *, filename), + _ANSIDECL(art_num, art), + _ANSIDECL(Boolean, printing) + ) + _KNRDECL(char *, filename) + _KNRDECL(art_num, art) + _KNRDECL(Boolean, printing) { return saveArticle(filename, CurrentGroup, art, False, printing); } -int saveArticle(filename, newsgroup, artnum, temporaryp, printing) - char *filename; - struct newsgroup *newsgroup; - art_num artnum; - Boolean temporaryp, printing; +int saveArticle( + _ANSIDECL(char *, filename), + _ANSIDECL(struct newsgroup *, newsgroup), + _ANSIDECL(art_num, artnum), + _ANSIDECL(Boolean, temporaryp), + _ANSIDECL(Boolean, printing) + ) + _KNRDECL(char *, filename) + _KNRDECL(struct newsgroup *, newsgroup) + _KNRDECL(art_num, artnum) + _KNRDECL(Boolean, temporaryp) + _KNRDECL(Boolean, printing) { char timeString[BUFFER_SIZE]; char inputbuf[BUFSIZ]; @@ -224,10 +236,10 @@ extern time_t time _ARGUMENTS((time_t *)); time_t clock; long pos; - char *artfile, *fullName; + file_cache_file *artfile; + char *fullName; FILE *fpart, *fpsave; - int xlation = 0; - int rotation; + int xlation = 0, rotation = 0; char mode[2], string[256]; int c; struct stat buf; @@ -239,11 +251,18 @@ /* get the FULL article */ - rotation = (IS_ROTATED(art) ? ROTATED : NOT_ROTATED); + if (IS_ROTATED(art)) + rotation = ROTATED; #ifdef XLATE - xlation = (IS_XLATED(art) ? XLATED : NOT_XLATED); + if (IS_XLATED(art)) + xlation = XLATED; #endif - artfile = utGetarticle(newsgroup, artnum, &pos, FULL_HEADER, rotation, xlation); + artfile = getarticle(newsgroup, artnum, &pos, + FULL_HEADER | rotation | xlation); + if (! artfile) { + mesgPane(XRN_SERIOUS, 0, ART_NOT_AVAIL_MSG, artnum); + return(0); + } /* * check a few special cases before actually saving the article @@ -260,8 +279,9 @@ (void) sprintf(error_buffer, SAVE_PIPE_TO_MSG , artnum, &filename[1]); infoNow(error_buffer); - status = processArticle(utTrimSpaces(&filename[1]), artfile); - (void) unlink(artfile); + status = processArticle(utTrimSpaces(&filename[1]), + file_cache_file_name(FileCache, *artfile)); + file_cache_file_release(FileCache, *artfile); FREE(artfile); if (status) { (void) sprintf(error_buffer, ERROR_SAVE_PIPE_MSG , @@ -280,7 +300,9 @@ } if ((filename != NIL(char)) && (filename[0] == '+')) { - int status = MHrefile(filename, artfile); + int status = MHrefile(filename, + file_cache_file_name(FileCache, *artfile)); + file_cache_file_release(FileCache, *artfile); FREE(artfile); (void) sprintf(error_buffer, SAVE_MH_REFILE_MSG, filename, status ? DONE_MSG : FAILED_MSG ); @@ -297,7 +319,7 @@ /* XXX not quite right, don't want to try to create it if not used... */ if (!createNewsDir()) { - (void) unlink(artfile); + file_cache_file_release(FileCache, *artfile); FREE(artfile); artStructSet(newsgroup, &art); return(0); @@ -308,16 +330,17 @@ if ((fullName = buildFileName(filename+1, app_resources.saveDir, newsgroup->name)) == NIL(char)) { mesgPane(XRN_SERIOUS, 0, CANT_FIGURE_FILE_NAME_MSG, filename+1); - (void) unlink(artfile); + file_cache_file_release(FileCache, *artfile); FREE(artfile); artStructSet(newsgroup, &art); return(0); } - status = RMAILrefile(fullName, filename+1, artfile, pos); + status = RMAILrefile(fullName, filename+1, + file_cache_file_name(FileCache, *artfile), pos); (void) sprintf(error_buffer, SAVE_RMAIL_REFILE_MSG , filename+1, status ? DONE_MSG : FAILED_MSG ); infoNow(error_buffer); - (void) unlink(artfile); + file_cache_file_release(FileCache, *artfile); FREE(artfile); artStructSet(newsgroup, &art); return(status); @@ -325,15 +348,16 @@ if ((fullName = buildFileName(filename, app_resources.saveDir, newsgroup->name)) == NIL(char)) { mesgPane(XRN_SERIOUS, 0, CANT_FIGURE_FILE_NAME_MSG, filename); - (void) unlink(artfile); + file_cache_file_release(FileCache, *artfile); FREE(artfile); artStructSet(newsgroup, &art); return(0); } - if ((fpart = fopen(artfile, "r")) == NULL) { - mesgPane(XRN_SERIOUS, 0, CANT_OPEN_ART_MSG, artfile, errmsg(errno)); - (void) unlink(artfile); + if ((fpart = fopen(file_cache_file_name(FileCache, *artfile), "r")) == NULL) { + mesgPane(XRN_SERIOUS, 0, CANT_OPEN_ART_MSG, + file_cache_file_name(FileCache, *artfile), errmsg(errno)); + file_cache_file_release(FileCache, *artfile); FREE(artfile); artStructSet(newsgroup, &art); return(0); @@ -347,7 +371,7 @@ if ((fpsave = fopen(fullName, mode)) == NULL) { (void) fclose(fpart); - (void) unlink(artfile); + file_cache_file_release(FileCache, *artfile); FREE(artfile); mesgPane(XRN_SERIOUS, 0, CANT_CREAT_APPEND_SAVE_FILE_MSG, (mode[0] == 'w') ? CREATE_MSG : APPEND_MSG, @@ -384,6 +408,10 @@ } } (void) rewind(fpart); + } else if ((*mode == 'a') && (app_resources.saveMode & FORMFEED_SAVE) && + (fputc('\014', fpsave) == EOF)) { + error++; + goto finished; } if (fprintf(fpsave, SAVE_ARTICLE_MSG , artnum, newsgroup->name) == EOF) { @@ -424,7 +452,7 @@ finished: (void) fclose(fpart); - (void) unlink(artfile); + file_cache_file_release(FileCache, *artfile); FREE(artfile); if (fclose(fpsave) == EOF) { diff -u -d -r -N -P 8.02/save.h 9.00/save.h --- 8.02/save.h Mon Jan 30 07:49:22 1995 +++ 9.00/save.h Thu Jun 5 07:11:42 1997 @@ -2,7 +2,7 @@ #define SAVE_H /* - * $Id: save.h,v 1.7 1995/01/25 03:17:52 jik Exp $ + * $Id: save.h,v 1.8 1997/01/12 03:41:22 jik Exp $ */ /* @@ -34,9 +34,9 @@ * save.h: routines for saving articles and sending articles to processes */ -extern int saveArticleByNumber _ARGUMENTS((char *, art_num, /* Boolean */ int)); +extern int saveArticleByNumber _ARGUMENTS((char *, art_num, Boolean)); extern int saveArticle _ARGUMENTS((char *, struct newsgroup *, art_num, - /* Boolean */ int, /* Boolean */ int)); + Boolean, Boolean)); extern int createNewsDir _ARGUMENTS((void)); #endif /* SAVEARTICLE_H */ diff -u -d -r -N -P 8.02/server.c 9.00/server.c --- 8.02/server.c Thu May 9 12:22:03 1996 +++ 9.00/server.c Tue Dec 16 21:57:50 1997 @@ -1,6 +1,6 @@ #if !defined(lint) && !defined(SABER) && !defined(GCC_WALL) -static char XRNrcsid[] = "$Id: server.c,v 1.91 1996/05/09 16:21:43 jik Exp $"; +static char XRNrcsid[] = "$Id: server.c,v 1.145 1997/12/17 02:57:49 jik Exp $"; #endif /* @@ -57,6 +57,9 @@ #include "xrn.h" #include "newsrcfile.h" #include "varfile.h" +#include "dialogs.h" +#include "sort.h" +#include "file_cache.h" #if defined(sun) && (defined(sparc) || defined(mc68000)) && !defined(SOLARIS) #include @@ -65,10 +68,10 @@ extern int errno; #define BUFFER_SIZE 1024 -#define MESSAGE_SIZE 1024 +/* This constant must be a 2^x times BUFFER_SIZE, for some x. */ +#define MAX_BUFFER_SIZE (8*BUFFER_SIZE) Boolean ServerDown = False, PostingAllowed = True, FastServer = False; -static char mybuf[MESSAGE_SIZE+100]; int server_page_height = 24; static struct newsgroup *currentNewsgroup = 0; @@ -76,24 +79,207 @@ getgroup((n), 0, 0, 0, True)) /* - * get data from the server (active file, article) - * - * on error, sets 'ServerDown' - * - * returns: void - */ -static void get_data_from_server _ARGUMENTS((char *, int)); + Get a line of data from the server. -static void get_data_from_server(str, size) - char *str; /* string for message to be copied into */ - int size; /* size of string */ + Returns a char pointer to the line of data. The caller SHOULD NOT + FREE THIS DATA, as it is static. + + The trailing "\r\n" sequence is stripped from the returned line. + + On error, sets ServerDown and returns an empty string. + */ + +static char *get_data_from_server _ARGUMENTS((int)); + +static char *get_data_from_server(discard_excess) + int discard_excess; { - if (get_server(str, size) < 0) { - ServerDown = True; - } else { - ServerDown = False; + static int size = 0, end, left, len; + static char *str = NULL; + Boolean continuing = False; + + if (! size) { + size = BUFFER_SIZE; + str = XtMalloc(size); + } + + ServerDown = False; + + for (end = 0, left = size; ; end += len, left -= len) { + if (get_server(&str[end], size - end) < 0) { + ServerDown = True; + *str = '\0'; + break; } - return; + + len = strlen(&str[end]); + + if (left - len == 1) { + continuing = True; + /* Only one byte left at the end, which means that we didn't + read a full line and the last byte is the null put there by + fgets. */ + if (size >= MAX_BUFFER_SIZE) { + if (discard_excess) { + char garbage_buf[BUFFER_SIZE]; + do { + if (get_server(garbage_buf, sizeof(garbage_buf)) < 0) { + ServerDown = True; + *str = '\0'; + break; + } + } while (strlen(garbage_buf) == sizeof(garbage_buf) - 1); + } + break; + } + left += size; + size *= 2; + str = XtRealloc(str, size); + } + else { + if (continuing && (len == 1)) { + /* It's possible that there was a "\r\n" pair split between + the previous and last get_server() calls. */ + if ((end > 1) && (str[end-1] == '\r') && (str[end] == '\n')) + str[end-1] = '\0'; + } + break; + } + } + + return str; +} + +static int wants_user_pass_authentication _ARGUMENTS((void)); +static int user_pass_authentication _ARGUMENTS((void)); + +static int wants_user_pass_authentication() +{ + char *ptr = app_resources.authenticator; + + if (! ptr) + /* Default to user/pass authentication. */ + return 1; + else if (! app_resources.authenticatorCommand) { + /* Fall back on user/pass. */ + app_resources.authenticator = NULL; + return 1; + } + + while (*ptr && isspace(*ptr)) + ptr++; + + return(! strncasecmp(ptr, "user/pass", sizeof("user/pass")-1)); +} + +/* + This function assumes that wants_user_pass_authentication() has + already been called and returned True. I.e., don't call this + function if you don't already know that the authenticator resource + is in the correct format. + */ +static int user_pass_authentication() +{ + /* + The format of the authenticator resource when user/pass + authentication is being used is: + + ws "user/pass" ws username ws "/" ws password ws + + "ws" stands for whitespace, which is optional in all cases. If + the username is omitted, the return value of getUser() is used. + If the slash is omitted, then the user is prompted for a + password. Neither the username nor the password may have spaces + in it. + + The password is allowed to be there only if + ALLOW_RESOURCE_PASSWORDS is defined to a non-zero value. + */ + char *user = 0, *pass = 0, *ptr; + char *buf = 0; + char cmdbuf[BUFSIZ], *response; + int retval; + + if (app_resources.authenticator) { + buf = XtNewString(app_resources.authenticator); + + user = buf; + + while (*user && isspace(*user)) + user++; + + user += sizeof("user/pass") - 1; + + while (*user && isspace(*user)) + user++; + + if (! (ptr = strchr(user, '/'))) + ptr = strchr(user, '\0'); + + while ((ptr > user) && isspace(*(ptr - 1))) + ptr--; + + *ptr = '\0'; + +#if ALLOW_RESOURCE_PASSWORDS + pass = app_resources.authenticator; + if ((pass = strchr(pass, '/')) && (pass = strchr(pass + 1, '/'))) { + pass++; + while (*pass && isspace(*pass)) + pass++; + ptr = strchr(pass, '\0'); + while ((ptr > pass) && isspace(*(ptr - 1))) + ptr--; + *ptr = '\0'; + if (! *pass) + pass = 0; + } +#endif /* ALLOW_RESOURCE_PASSWORDS */ + } + + if (! (user && *user)) { + user = getUser(); + if (! *user) { + retval = -1; + goto done; + } + } + else + user = XtNewString(user); + + if (! pass) + pass = PasswordBox(TopLevel, NNTP_PASSWORD_MSG); + else + pass = XtNewString(pass); + + if (! pass) { + retval = -1; + goto done; + } + + (void) sprintf(cmdbuf, "AUTHINFO USER %s", user); + put_server(cmdbuf); + response = get_data_from_server(True); + if (*response != CHAR_CONT) { + retval = -1; + goto done; + } + + (void) sprintf(cmdbuf, "AUTHINFO PASS %s", pass); + put_server(cmdbuf); + response = get_data_from_server(True); + if (*response != CHAR_OK) { + retval = -1; + goto done; + } + + retval = 0; + +done: + XtFree(buf); + XtFree(user); + XtFree(pass); + return retval; } @@ -102,15 +288,17 @@ static int authenticate() { extern FILE *ser_rd_fp, *ser_wr_fp; - char tmpbuf[BUFSIZ], cmdbuf[BUFSIZ], *authval, *p; + char tmpbuf[BUFSIZ], cmdbuf[BUFSIZ]; char *authcmd; static int cookiefd = -1; - int builtinauth = 0; #ifdef USE_PUTENV static char *old_env = 0; char *new_env; #endif + if (wants_user_pass_authentication()) + return user_pass_authentication(); + /* If we have authenticated before, NNTP_AUTH_FDS already exists, pull out the cookiefd. Just in case we've nested. */ if (cookiefd == -1 && (authcmd = getenv("NNTP_AUTH_FDS"))) { @@ -125,20 +313,12 @@ return 1; } (void) unlink(tempfile); + utTempnamFree(tempfile); cookiefd = fileno(f); } strcpy(tmpbuf, "AUTHINFO GENERIC "); - if ((authval = getenv("NNTPAUTH")) || - (authval = app_resources.authenticator)) { - strcat(tmpbuf, authval); - } else { - strcat(tmpbuf, "any "); - p = getUser(); - strcat(tmpbuf, p); - XtFree(p); - builtinauth = 1; - } + strcat(tmpbuf, app_resources.authenticator); put_server(tmpbuf); #ifdef USE_PUTENV @@ -154,37 +334,28 @@ setenv("NNTP_AUTH_FDS", tmpbuf, 1); #endif - authcmd = app_resources.authenticatorCommand; - - if (!builtinauth) { - sprintf(cmdbuf, authcmd, authval); - return (system(cmdbuf)); - } else { - get_server(tmpbuf, sizeof(tmpbuf)); - return (strncmp(tmpbuf, "281 ", 4)); - } + sprintf(cmdbuf, app_resources.authenticatorCommand, + app_resources.authenticator); + return (system(cmdbuf)); } -static int check_authentication _ARGUMENTS((char *, char *, int)); +static int check_authentication _ARGUMENTS((char *, char **)); static int -check_authentication(command, response, size) - char *command; /* command to resend */ - char *response; /* response from the command */ - int size; /* size of the response buffer */ +check_authentication(command, response) + char *command; /* command to resend */ + char **response; /* response from the command */ { - - if (STREQN(response, "480 ", 4)) { - if (authenticate()) { - strncpy(response, "502 Authentication failed", size); - response[size-1] = '\0'; - } else { - put_server(command); - get_data_from_server(response, size); - } - return (1); + if (STREQN(*response, "480 ", 4)) { + if (authenticate()) { + *response = "502 Authentication failed"; + } else { + put_server(command); + *response = get_data_from_server(True); } - return(0); + return (1); + } + return(0); } @@ -193,12 +364,11 @@ * request. */ -static void check_server_response _ARGUMENTS((char *, char *, int)); +static void check_server_response _ARGUMENTS((char *, char **)); -static void check_server_response(command, response, size) - char *command; /* command to resend */ - char *response; /* response from the command */ - int size; /* size of the response buffer */ +static void check_server_response(command, response) + char *command; /* command to resend */ + char **response; /* response from the command */ { /* * try to recover from a timeout @@ -211,10 +381,10 @@ * 503 Timeout ... */ - if (check_authentication(command, response, size)) + if (check_authentication(command, response)) return; - if (ServerDown || STREQN(response, "503 Timeout", 11)) { + if (ServerDown || STREQN(*response, "503 Timeout", 11)) { mesgPane(XRN_SERIOUS, 0, LOST_CONNECT_ATTEMPT_RE_MSG); start_server(); @@ -236,266 +406,405 @@ } put_server(command); - get_data_from_server(response, size); - (void) check_authentication(command, response, size); + *response = get_data_from_server(True); + (void) check_authentication(command, response); } - + return; } /* - * retrieve article number 'artnumber' in the current group, update structure - * - * returns: filename that the article is stored in or NIL(char) if - * the article is not avaiable - * - */ -char * getarticle(newsgroup, artnumber, retposition, header, rotation, xlation) - struct newsgroup *newsgroup; - art_num artnumber; /* # of article in the current group to retrieve */ - long *retposition; /* if non-null, return parameter for byte position of - header/body seperation */ - int header, rotation, xlation; + Fetch an article from the server into the "base_file" field of the + article structure for the article. + + If the article was fetched successfully, returns a positive number + and fills in the pointer to the fetched cache file structure. + + If the article was unavailable, returns 0. If there was an error + (e.g., disk full) fetching the article, returns a negative number. + In both of these cases, the contents of the cache file structure + pointer are undefined. + + Does not modify the data in the fetched article in any way, except + undoing double '.' characters at line beginnings. + + The returned cache file is locked until unlocked by the caller. */ +static int get_base_article _ARGUMENTS((struct newsgroup *, art_num, + file_cache_file **)); + +static int get_base_article(newsgroup, artnum, ret_cache_file) + struct newsgroup *newsgroup; + art_num artnum; + file_cache_file **ret_cache_file; { - char command[MESSAGE_SIZE], message[MESSAGE_SIZE], *msg; -#ifdef REALLY_USE_LOCALTIME - char temp[MESSAGE_SIZE]; -#endif - FILE *articlefp; - char *filename, *ptr; - char field[BUFFER_SIZE]; - int byteCount = 0, lineCount = 0; - int error = 0; - int last_stripped = 0; - long position WALL(= 0); - long start_time, end_time; - Boolean found_sep = False; + struct article *art; + long start_time, end_time; + char command[BUFFER_SIZE], *message, *line, *cr WALL(= NULL); + int byteCount = 0, len; + file_cache_file *cache_file; + FILE *fp; + Boolean is_partial = False, was_partial, pending_cr = False; - if (SETNEWSGROUP(newsgroup)) { - return 0; - } + art = artStructGet(newsgroup, artnum, True); - start_time = time(0); + if (art->base_file && *art->base_file) { + file_cache_file_lock(FileCache, *art->base_file); + *ret_cache_file = art->base_file; + return 1; + } - /* send ARTICLE */ - (void) sprintf(command, "ARTICLE %ld", artnumber); - put_server(command); - get_data_from_server(message, sizeof(message)); + CLEAR_BASE_FILE(art); - check_server_response(command, message, sizeof(message)); + if (SETNEWSGROUP(newsgroup)) { + artStructSet(newsgroup, &art); + /* Should I indicate that the article is unavailable or that there + was an error fetching it? I'm going to opt for the former, + although I'm not 100% convinced that's the right answer. In + any case, this should almost never happen. */ + return 0; + } - if (*message != CHAR_OK) { - /* can't get article */ - return(NIL(char)); + start_time = time(0); + + (void) sprintf(command, "ARTICLE %ld", artnum); + put_server(command); + message = get_data_from_server(True); + + check_server_response(command, &message); + + if (*message != CHAR_OK) { + artStructSet(newsgroup, &art); + return 0; + } + + cache_file = (file_cache_file *) XtMalloc(sizeof(*cache_file)); + + if (! (fp = file_cache_file_open(FileCache, cache_file))) { + sprintf(error_buffer, FILE_CACHE_OPEN_MSG, file_cache_dir_get(FileCache), + errmsg(errno)); + FREE(cache_file); + artStructSet(newsgroup, &art); + if (ehErrorRetryXRN(error_buffer, True)) + return get_base_article(newsgroup, artnum, ret_cache_file); + } + + do_chmod(fp, file_cache_file_name(FileCache, *cache_file), 0600); + + while (1) { + line = get_data_from_server(False); + + was_partial = is_partial; + + byteCount += (len = strlen(line)); + + is_partial = (len >= MAX_BUFFER_SIZE - 1); + + if (!line[0] && ServerDown) { + /* error */ + (void) fclose(fp); + file_cache_file_destroy(FileCache, *cache_file); + FREE(cache_file); + artStructSet(newsgroup, &art); + return 0; } - while (! ((filename = utTempnam(app_resources.tmpDir, "xrn")))) - ehErrorRetryXRN(CANT_TEMP_NAME_MSG, True); + if (!was_partial && line[0] == '.' && !line[1]) + /* end of the article */ + break; - while (! (articlefp = fopen(filename, "w"))) { - (void) sprintf(error_buffer, CANT_CREATE_TEMP_MSG, filename, - errmsg(errno)); - ehErrorRetryXRN(error_buffer, True); + if (was_partial && pending_cr && (line[0] != '\n') && + (fputc('\r', fp) == EOF)) + goto disk_full; + + if ((pending_cr = (is_partial && (cr = strrchr(line, '\r')) && !cr[1]))) + *cr = '\0'; + + if (!was_partial && line[0] == '.' && line[1] == '.') + line++; + + if ((fputs(line, fp) == EOF) || (!is_partial && (fputc('\n', fp) == EOF))) { + /* disk full? */ + while ((line = get_data_from_server(False)) && + (line[0] || !ServerDown) && + (was_partial || (line[0] != '.') || line[1])) + /* empty */; + disk_full: + (void) fclose(fp); + disk_full_closed: + file_cache_file_destroy(FileCache, *cache_file); + FREE(cache_file); + artStructSet(newsgroup, &art); + if (file_cache_free_space(FileCache, 1)) + return get_base_article(newsgroup, artnum, ret_cache_file); + return -1; } + } - do_chmod(articlefp, filename, 0600); + end_time = time(0); - for (;;) { - get_data_from_server(message, sizeof(message)); + if (byteCount) { + long seconds = end_time - start_time; + double kilobytes = (double) byteCount / 1024.0; + double speed; - /* the article is ended by a '.' on a line by itself */ - if ((message[0] == '.') && (message[1] == '\0')) { - /* check for a bogus message */ - if (byteCount == 0) { - (void) fclose(articlefp); - (void) unlink(filename); - FREE(filename); - return(NIL(char)); - } - break; - } + if (! seconds) + seconds = 1; - msg = &message[0]; + speed = kilobytes / (double) seconds; - /* find header/body separation */ - if (! found_sep) { - if (*msg == '\0') { - position = byteCount; - found_sep = True; - } - } - - if (*msg == '.') { - msg++; - } + if (speed >= (double) app_resources.prefetchMinSpeed) + FastServer = True; + else if (kilobytes > app_resources.prefetchMinSpeed) + FastServer = False; + } - if (*msg != '\0') { - /* strip leading ^H */ - while (*msg == '\b') { - msg++; - } - /* strip '^H' */ - for (ptr = index(msg + 1, '\b'); ptr != NIL(char); ptr = index(ptr, '\b')) { - if (ptr - 1 < msg) { - /* too many backspaces, kill all leading back spaces */ - while (*ptr == '\b') { - (void) strcpy(ptr, ptr + 1); - ptr++; - } - break; - } - (void) strcpy(ptr - 1, ptr + 1); - ptr--; - } + if (fclose(fp) == EOF) + goto disk_full_closed; -#ifdef REALLY_USE_LOCALTIME - if (app_resources.displayLocalTime && !strncmp(msg, "Date: ", 6)) { - tconvert(temp, msg+6); - (void) strcpy(msg+6, temp); - } -#endif - /* strip the headers */ - if ((! found_sep) && (header == NORMAL_HEADER)) { - if ((*msg == ' ') || (*msg == '\t')) { /* continuation line */ - if (last_stripped) - continue; - } - else { - if ((ptr = index(msg, ':')) == NIL(char)) { - continue; /* weird header line, skip */ - } - if (*(ptr+1) == '\0') { - continue; /* empty field, skip */ - } - (void) strncpy(field, msg, (int) (ptr - msg)); - field[(int) (ptr - msg)] = '\0'; - utDowncase(field); - if (avl_lookup(app_resources.headerTree, field, &ptr)) { - if (app_resources.headerMode == STRIP_HEADERS) { - last_stripped = 1; - continue; - } - else - last_stripped = 0; - } else { - if (app_resources.headerMode == LEAVE_HEADERS) { - last_stripped = 1; - continue; - } - else - last_stripped = 0; - } - } - } + if (! file_cache_file_close(FileCache, *cache_file)) { + file_cache_file_destroy(FileCache, *cache_file); + FREE(cache_file); + artStructSet(newsgroup, &art); + return -1; + } - /* handle rotation of the article body */ - if ((rotation == ROTATED) && found_sep) { - for (ptr = msg; *ptr != '\0'; ptr++) { - if (isalpha(*ptr)) { - if ((*ptr & 31) <= 13) { - *ptr = *ptr + 13; - } else { - *ptr = *ptr - 13; - } - } - } - } + *ret_cache_file = art->base_file = cache_file; + artStructSet(newsgroup, &art); + + return 1; +} + + +/* + * retrieve article number 'artnumber' in the current group + * + * returns: the cache file (locked) that the article is stored in + * or NIL(char) if the article is not avaiable + * + * MODIFIES THE ARTICLE STRUCTURE FOR THE ARTICLE BEING RETRIEVED. + * Therefore, the caller should not have the article structure + * allocated (i.e., returned by artStructGet()) and expect the + * allocated structure to be valid after the call to getarticle(). If + * the caller *does* have the structure allocated, he should + * re-retrieve it after calling getarticle(). + */ +file_cache_file *getarticle(newsgroup, artnumber, retposition, flags) + struct newsgroup *newsgroup; + art_num artnumber; /* # of article in the current group to retrieve */ + long *retposition; /* if non-null, return parameter for byte position of + header/body seperation */ + int flags; +{ + file_cache_file *base_cache_file, *cache_file; + int ret; + FILE *basefp, *articlefp; + static char *buf = NULL; + static int buf_size = 0; + char *buf_ptr, *ptr; + int byteCount = 0, lineCount = 0, error = 0; + Boolean found_sep = False, last_stripped = False; + static char *field = NULL; + static int field_size = 0; + long position WALL(= 0); + + if (SETNEWSGROUP(newsgroup)) + return NULL; + + if ((ret = get_base_article(newsgroup, artnumber, &base_cache_file)) < 0) { + cache_error: + sprintf(error_buffer, FILE_CACHE_OPEN_MSG, file_cache_dir_get(FileCache), + errmsg(errno)); + if (ehErrorRetryXRN(error_buffer, True)) + return getarticle(newsgroup, artnumber, retposition, flags); + } + else if (ret == 0) { + /* article is unavailable */ + return NULL; + } + + if (! (basefp = fopen(file_cache_file_name(FileCache, *base_cache_file), + "r"))) { + file_cache_file_unlock(FileCache, *base_cache_file); + goto cache_error; + } + + cache_file = (file_cache_file *) XtMalloc(sizeof(*cache_file)); + + if (! (articlefp = file_cache_file_open(FileCache, cache_file))) { + (void) fclose(basefp); + FREE(cache_file); + file_cache_file_unlock(FileCache, *base_cache_file); + goto cache_error; + } + + do_chmod(articlefp, file_cache_file_name(FileCache, *cache_file), 0600); + + if (! buf) { + buf = XtMalloc(BUFFER_SIZE); + buf_size = BUFFER_SIZE; + } + + buf_ptr = buf; + + while (fgets(buf_ptr, buf_size - (buf_ptr - buf), basefp)) { + if ((strlen(buf_ptr) == buf_size - (buf_ptr - buf) - 1) && + (buf_ptr[buf_size - (buf_ptr - buf) - 2] != '\n')) { + buf_size *= 2; + buf = XtRealloc(buf, buf_size); + buf_ptr = &buf[strlen(buf)]; + continue; + } + + buf_ptr = buf; /* for the next time around */ + + /* find header/body separation */ + if (! found_sep) { + if (*buf == '\n') { + position = byteCount; + found_sep = True; + } + } + + /* strip the headers */ + if (!found_sep && !(flags & FULL_HEADER)) { + if ((*buf == ' ') || (*buf == '\t')) { /* continuation line */ + if (last_stripped) + continue; + } + else { + Boolean stripping = (app_resources.headerMode == STRIP_HEADERS); + + if ((ptr = index(buf, ':')) == NIL(char)) + continue; /* weird header line, skip */ + if (*(ptr+1) == '\0') + continue; /* empty field, skip */ + if (ptr - buf + 1 > field_size) { + field_size = ptr - buf + 1; + field = XtRealloc(field, field_size); + } + (void) strncpy(field, buf, ptr - buf); + field[ptr - buf] = '\0'; + utDowncase(field); + last_stripped = (avl_lookup(app_resources.headerTree, field, &ptr) ? + (stripping ? True : False) : + (stripping ? False : True)); + if (last_stripped) + continue; + if (app_resources.displayLocalTime && !strcmp(field, "date")) + tconvert(buf+6, buf+6); + } + } + + /* handle rotation of the article body */ + if ((flags & ROTATED) && found_sep) { + for (ptr = buf; *ptr != '\0'; ptr++) { + if (isalpha(*ptr)) { + if ((*ptr & 31) <= 13) { + *ptr = *ptr + 13; + } else { + *ptr = *ptr - 13; + } + } + } + } #ifdef XLATE - /* handle translation of the article body */ - if ((xlation == XLATED) && found_sep) - utXlate(msg); + /* handle translation of the article body */ + if ((flags & XLATED) && found_sep) + /* I'm assuming here that utXlate() won't change the + length of the string. If that changes, a "len = + strlen(buf)" line needs to be added after the + "utXlate(buf)" call. */ + utXlate(buf); #endif /* XLATE */ - /* handle ^L (poorly?) */ - if (*msg == '\014') { - int i, lines; - lines = server_page_height; - lines -= lineCount % lines; - for (i = 0; i < lines; i++) { - if (putc('\n', articlefp) == EOF) { - error++; - break; - } - } - if (error) { - break; - } - byteCount += lines; - lineCount += lines; - msg++; - } - if (fputs(msg, articlefp) == EOF) { - error++; - break; - } - } + /* handle ^L (poorly?) */ + if (found_sep && (flags & PAGEBREAKS) && (*buf == '\014')) { + int i, lines; + lines = server_page_height; + lines -= lineCount % lines; + for (i = 0; i < lines; i++) { if (putc('\n', articlefp) == EOF) { - error++; - break; + error++; + break; } - byteCount += utStrlen(msg) + 1; - lineCount++; + } + if (error) + break; + lineCount += lines; + byteCount += lines; + buf++; } - end_time = time(0); + if (found_sep && (flags & BACKSPACES)) { + char *orig, *copy; - if (byteCount) { - long seconds = end_time - start_time; - double kilobytes = (double) byteCount / 1024.0; - double speed; + for (orig = copy = buf; *orig; orig++) + if (*orig == '\b') { + if (copy > buf) + copy--; + } + else + *copy++ = *orig; - if (! seconds) - seconds = 1; - - speed = kilobytes / (double) seconds; - if (speed >= (double) app_resources.prefetchMinSpeed) - FastServer = True; - else if (kilobytes > app_resources.prefetchMinSpeed) - FastServer = False; + *copy = '\0'; } - if (!error) { - if (fclose(articlefp) == 0) { - if (retposition) - *retposition = position; - return(filename); - } - } else { - (void) fclose(articlefp); - /* read till end of article */ - do { - get_data_from_server(message, sizeof(message)); - } while ((message[0] != '.') || (message[1] != '\0')); + if (fputs(buf, articlefp) == EOF) { + error++; + break; } - (void) unlink(filename); - (void) sprintf(error_buffer, ERROR_WRITING_FILE_MSG, filename, - errmsg(errno)); - FREE(filename); - if (ehErrorRetryXRN(error_buffer, True)) - return utGetarticle(newsgroup, artnumber, retposition, header, - rotation, xlation); - return(NIL(char)); + + byteCount += strlen(buf); + lineCount++; + } + + (void) fclose(basefp); + + if (!error) { + if ((fclose(articlefp) == 0) && + file_cache_file_close(FileCache, *cache_file)) { + if (retposition) + *retposition = position; + file_cache_file_unlock(FileCache, *base_cache_file); + return(cache_file); + } + } else + (void) fclose(articlefp); + + (void) sprintf(error_buffer, ERROR_WRITING_FILE_MSG, + file_cache_file_name(FileCache, *cache_file), + errmsg(errno)); + file_cache_file_destroy(FileCache, *cache_file); + FREE(cache_file); + if (file_cache_free_space(FileCache, 1) || /* free up at least one article */ + ehErrorRetryXRN(error_buffer, True)) + return getarticle(newsgroup, artnumber, retposition, flags); + return(NULL); } /* - * enter a new group and get its statistics (and update the structure) - * allocate an array for the articles and process the .newsrc article - * info for this group + * enter a new group and get its statistics * * returns: NO_GROUP on failure, 0 on success * + * When returning NO_GROUP, sets first, last and number to 0. * Only displays an error if "display_error" is True. */ -int getgroup(newsgroup, first, last, number, display_error) - struct newsgroup *newsgroup; /* group name */ - art_num *first; /* first article in the group */ - art_num *last; /* last article in the group */ - int *number; /* number of articles in the group, if 0, first - and last are bogus */ - Boolean display_error; +int getgroup( + _ANSIDECL(struct newsgroup *, newsgroup), + _ANSIDECL(art_num *, first), + _ANSIDECL(art_num *, last), + _ANSIDECL(int *, number), + _ANSIDECL(Boolean, display_error) + ) + _KNRDECL(struct newsgroup *, newsgroup) + _KNRDECL(art_num *, first) + _KNRDECL(art_num *, last) + _KNRDECL(int *, number) + _KNRDECL(Boolean, display_error) { - char command[MESSAGE_SIZE], message[MESSAGE_SIZE]; + char command[BUFFER_SIZE], *message; static long code, num, count, frst, lst; if (! newsgroup) { @@ -510,22 +819,32 @@ if (newsgroup != currentNewsgroup) { (void) sprintf(command, "GROUP %s", newsgroup->name); put_server(command); - get_data_from_server(message, sizeof(message)); + message = get_data_from_server(True); - check_server_response(command, message, sizeof(message)); + check_server_response(command, &message); if (*message != CHAR_OK) { if (atoi(message) != ERR_NOGROUP) { - - (void) sprintf(mybuf, ERROR_REQUEST_FAILED_MSG, command, message); - ehErrorExitXRN(mybuf); + char *mybuf = XtMalloc(strlen(ERROR_REQUEST_FAILED_MSG) + + strlen(command) + + strlen(message)); + (void) sprintf(mybuf, ERROR_REQUEST_FAILED_MSG, + command, message); + ehErrorExitXRN(mybuf); } if (display_error) mesgPane(XRN_SERIOUS, 0, NO_SUCH_NG_DELETED_MSG, newsgroup->name); /* remove the group from active use ??? */ - + + if (number) + *number = 0; + if (first) + *first = 0; + if (last) + *last = 0; + return(NO_GROUP); } @@ -553,17 +872,21 @@ char *newgroupsDate() { - static char date_buf[18], message[MESSAGE_SIZE], *ptr; + static char date_buf[18], *message, *ptr; struct tm *curtime; +#if defined(__osf__) || defined(__hpux) + time_t clock; +#else long clock; +#endif /* First, try the "date" command. */ put_server("DATE"); - get_data_from_server(message, sizeof(message)); + message = get_data_from_server(True); - check_server_response("DATE", message, sizeof(message)); + check_server_response("DATE", &message); if (*message == CHAR_INF) { ptr = strchr(message, ' '); @@ -630,8 +953,80 @@ return line_buf; } - - +/* + Return true if the specified newsgroup is supposed to be ignored, + false otherwise. + */ +static Boolean is_ignored_newsgroup _ARGUMENTS((char *)); + +static Boolean is_ignored_newsgroup(group) + char *group; +{ + static int inited = 0, ign_count, val_count; +#ifdef POSIX_REGEX + static regex_t *ign_list, *val_list; +#else + static char **ign_list, **val_list; +#endif + int i; + + if (! inited) { + val_list = parseRegexpList(app_resources.validNewsgroups, + "validNewsgroups", &val_count); + ign_list = parseRegexpList(app_resources.ignoreNewsgroups, + "ignoreNewsgroups", &ign_count); + inited++; + } + + if (val_count) { + Boolean is_valid = False; + + for (i = 0; i < val_count; i++) { + if ( +#ifdef POSIX_REGEX + ! regexec(&val_list[i], group, 0, 0, 0) +#else +# ifdef SYSV_REGEX + regex(val_list[i], group) +# else + ! re_comp(val_list[i]) && re_exec(group) +# endif +#endif + ) { + is_valid = True; +#ifdef DEBUG + fprintf(stderr, "is_ignored_newsgroup: %s matches valid list\n", group); +#endif + break; + } + } + + if (! is_valid) + return True; + } + + for (i = 0; i < ign_count; i++) { + if ( +#ifdef POSIX_REGEX + ! regexec(&ign_list[i], group, 0, 0, 0) +#else +# ifdef SYSV_REGEX + regex(ign_list[i], group) +# else + ! re_comp(ign_list[i]) && re_exec(group) +# endif +#endif + ) { +#ifdef DEBUG + fprintf(stderr, "is_ignored_newsgroup: %s matches ignore list\n", group); +#endif + return True; + } + } + + return False; +} + /* Parse a line from an active file (NNTP LIST command output, local @@ -653,30 +1048,20 @@ is filled into it. */ -int parse_active_line(line, from_cache, group_ptr) -char *line; -unsigned char from_cache; -struct newsgroup **group_ptr; +int parse_active_line( + _ANSIDECL(char *, line), + _ANSIDECL(unsigned char, from_cache), + _ANSIDECL(struct newsgroup **, group_ptr) + ) + _KNRDECL(char *, line) + _KNRDECL(unsigned char, from_cache) + _KNRDECL(struct newsgroup **, group_ptr) { - static int inited = 0, re_count; -#ifdef POSIX_REGEX - static regex_t *re_list; -#else - static char **re_list; -#endif - int re_index; - char *ptr, *ptr2, *group, type[MESSAGE_SIZE]; + char *ptr, *ptr2, *group, type[BUFFER_SIZE]; art_num first, last; struct newsgroup *newsgroup; int ret; - if (! inited) { - re_list = parseRegexpList(app_resources.ignoreNewsgroups, - "ignoreNewsgroups", &re_count); - inited++; - } - - /* server returns: group last first y/m/x/j/=otherGroup */ /* Is it really necessary to skip leading spaces? JIK 2/19/95 */ @@ -706,24 +1091,7 @@ #endif /* NO_BOGUS_GROUP_HACK */ - for (re_index = 0; re_index < re_count; re_index++) { -#ifdef POSIX_REGEX - if (! regexec(&re_list[re_index], group, 0, 0, 0)) -#else -# ifdef SYSV_REGEX - if (regex(re_list[re_index], group)) -# else - if ((! re_comp(re_list[re_index])) && re_exec(group)) -# endif -#endif - { -#ifdef DEBUG - fprintf(stderr, "Ignoring %s.\n", group); -#endif - break; - } - } - if (re_index < re_count) + if (is_ignored_newsgroup(group)) return ACTIVE_IGNORED; if (first == 0) { @@ -744,6 +1112,13 @@ newsgroup->nglist = 0; newsgroup->current = 0; newsgroup->from_cache = from_cache; + newsgroup->fetch = 0; + newsgroup->thread_table = 0; + newsgroup->kill_file = 0; + if (art_sort_need_dates()) + newsgroup->fetch |= FETCH_DATES; + if (art_sort_need_threads()) + newsgroup->fetch |= FETCH_IDS | FETCH_REFS | FETCH_THREADS; artListInit(newsgroup); if (avl_insert(NewsGroupTable, newsgroup->name, @@ -809,19 +1184,23 @@ } -Boolean verifyGroup(group, struct_ptr) -char *group; -struct newsgroup **struct_ptr; +Boolean verifyGroup( + _ANSIDECL(char *, group), + _ANSIDECL(struct newsgroup **, struct_ptr), + _ANSIDECL(Boolean, no_cache) + ) + _KNRDECL(char *, group) + _KNRDECL(struct newsgroup **, struct_ptr) + _KNRDECL(Boolean, no_cache) { char *ptr; int ret; - if (! (ret = avl_lookup(NewsGroupTable, group, &ptr))) { - if (! active_read) { - mesgPane(XRN_SERIOUS, 0, MISSING_NG_LISTING_MSG, group); - getactive(False); - ret = avl_lookup(NewsGroupTable, group, &ptr); - } + if (! ((ret = avl_lookup(NewsGroupTable, group, &ptr)) || active_read || + no_cache || is_ignored_newsgroup(group))) { + mesgPane(XRN_SERIOUS, 0, MISSING_NG_LISTING_MSG, group); + getactive(False); + ret = avl_lookup(NewsGroupTable, group, &ptr); } if (ret) { if (struct_ptr) @@ -839,10 +1218,12 @@ * * returns: void */ -void getactive(do_newgroups) -Boolean do_newgroups; +void getactive( + _ANSIDECL(Boolean, do_newgroups) + ) + _KNRDECL(Boolean, do_newgroups) { - char command[MESSAGE_SIZE], message[MESSAGE_SIZE]; + char command[BUFFER_SIZE], *message; char *newgroups_str = 0, *new_newgroups_str = 0; char *newgroups_var = NEWGROUPS_VARIABLE; char buf[LABEL_SIZE]; @@ -901,17 +1282,19 @@ } else (void) strcpy(command, "LIST"); put_server(command); - get_data_from_server(message, sizeof(message)); + message = get_data_from_server(True); - check_server_response(command, message, sizeof(message)); + check_server_response(command, &message); if (*message != CHAR_OK) { - (void) sprintf(mybuf, ERROR_REQUEST_FAILED_MSG, command, message); - ehErrorExitXRN(mybuf); + char *mybuf = XtMalloc(strlen(ERROR_REQUEST_FAILED_MSG) + + strlen(command) + strlen(message)); + (void) sprintf(mybuf, ERROR_REQUEST_FAILED_MSG, command, message); + ehErrorExitXRN(mybuf); } for (;;) { - get_data_from_server(message, sizeof(message)); + message = get_data_from_server(True); /* the list is ended by a '.' at the beginning of a line */ if (*message == '.') { @@ -1008,15 +1391,17 @@ void start_server() { static char *server = NIL(char); /* for restarting */ - int response, connected; + int response; char buf[LABEL_SIZE+HOST_NAME_SIZE]; /* Make sure to close a previous server connection, e.g., to avoid file descriptor leaks. */ close_server(); - if (! server) - server = nntpServer(); + if (! server) { + server = nntpServer(); + nntp_port = app_resources.nntpPort; + } if (! server) ehErrorExitXRN(NO_SERVER_MSG); @@ -1024,26 +1409,22 @@ (void) sprintf(buf, CONNECTING_MSG, server); infoNow(buf); - do { - connected = 1; - if ((response = server_init(server)) < 0) { - connected = 0; - } - if (response == OK_NOPOST) - PostingAllowed = False; - if (handle_server_response(response, server) < 0) { - connected = 0; - stop_server(); - } - if (! connected) { - (void) sprintf(buf, FAILED_CONNECT_MSG, server); - if (! ehErrorRetryXRN(buf, False)) { - while (! updatenewsrc()) - (void) ehErrorRetryXRN(ERROR_CANT_UPDATE_NEWSRC_MSG, True); - ehErrorExitXRN(0); - } - } - } while (!connected); + while (1) { + response = server_init(server); + if (response == OK_NOPOST) + PostingAllowed = False; + else if (response >= 0) + response = handle_server_response(response, server); + if (response >= 0) + break; + stop_server(); + (void) sprintf(buf, FAILED_CONNECT_MSG, server); + if (! ehErrorRetryXRN(buf, False)) { + while (! updatenewsrc()) + (void) ehErrorRetryXRN(ERROR_CANT_UPDATE_NEWSRC_MSG, True); + ehErrorExitXRN(0); + } + } (void) sprintf(buf, CONNECTING_MSG, server); (void) strcat(buf, " "); @@ -1065,25 +1446,6 @@ /* - * Calculate the number of digits in an integer. Sure, I could use a - * logarithm function, but that would require relying on a sane math - * library on all systems. The technique used in this function is - * gross, but what the heck, it works. - */ -static int digits _ARGUMENTS((long int)); - -static int digits(num) - long int num; -{ - char int_buf[20]; /* An article number longer than twenty digits? - I'll be dead by then! */ - - (void) sprintf(int_buf, "%ld", num); - return(strlen(int_buf)); -} - - -/* * Get a list of lines for a particular field for the specified group in * the range 'first' to 'last'. * @@ -1096,144 +1458,308 @@ * * Returns: True if it's done, False to keep going. */ +typedef char * (*_fixfunction) _ARGUMENTS((struct newsgroup *, art_num, char *)); + static Boolean getlist _ARGUMENTS((struct newsgroup *, art_num, art_num, - /* Boolean */ int, int, char *, - char *(*) _ARGUMENTS((struct newsgroup *, - art_num, char *)), - unsigned, /* Boolean */ int)); + Boolean, int, char *, unsigned, + _fixfunction, unsigned, Boolean)); -static Boolean getlist(newsgroup, artfirst, artlast, unreadonly, max, - field, fixfunction, offset, required) - struct newsgroup *newsgroup; - art_num artfirst; - art_num artlast; - Boolean unreadonly; - int max; - char *field; - char *(*fixfunction) _ARGUMENTS((struct newsgroup *, art_num, char *)); - unsigned offset; - Boolean required; +static Boolean getlist( + _ANSIDECL(struct newsgroup *, newsgroup), + _ANSIDECL(art_num, artfirst), + _ANSIDECL(art_num, artlast), + _ANSIDECL(Boolean, unreadonly), + _ANSIDECL(int, max), + _ANSIDECL(char *, field), + _ANSIDECL(unsigned, offset), + _ANSIDECL(_fixfunction, fixfunction), + _ANSIDECL(unsigned, fixed_offset), + _ANSIDECL(Boolean, required) + ) + _KNRDECL(struct newsgroup *, newsgroup) + _KNRDECL(art_num, artfirst) + _KNRDECL(art_num, artlast) + _KNRDECL(Boolean, unreadonly) + _KNRDECL(int, max) + _KNRDECL(char *, field) + _KNRDECL(unsigned, offset) + _KNRDECL(_fixfunction, fixfunction) + _KNRDECL(unsigned, fixed_offset) + _KNRDECL(Boolean, required) { - char command[MESSAGE_SIZE], message[MESSAGE_SIZE]; - char *line; - art_num number; - art_num first, last; - int count = 0; - struct article *art; - int pane_name = newMesgPaneName(); + char command[BUFFER_SIZE], *message; + char *line, *ptr; + art_num number; + art_num first, last; + int count = 0; + struct article *art; + int pane_name = newMesgPaneName(); - if (SETNEWSGROUP(newsgroup)) - return True; - artListSet(newsgroup); + if (SETNEWSGROUP(newsgroup)) + return True; + artListSet(newsgroup); - first = artfirst; - while ((first <= artlast) && ((! max) || (count < max))) { - art = artStructGet(newsgroup, first, False); - if (*(char **)((char *) art + offset) || - (unreadonly && IS_READ(art)) || - IS_UNAVAIL(art)) { - first++; - continue; - } + first = artfirst; + while ((first <= artlast) && ((! max) || (count < max))) { + art = artStructGet(newsgroup, first, False); + if (((offset != (unsigned)-1) && *(char **)((char *) art + offset)) || + (fixfunction && *(char **)((char *) art + fixed_offset)) || + (unreadonly && IS_READ(art)) || + IS_UNAVAIL(art)) { + first++; + continue; + } - for (last = first + 1; last <= artlast; last++) { - art = artStructGet(newsgroup, last, False); - if (*(char **)((char *) art + offset) || - (unreadonly && IS_READ(art)) || - IS_UNAVAIL(art) || (max && ((count + (last - first)) >= max))) - break; - } - last--; + for (last = first + 1; last <= artlast; last++) { + art = artStructGet(newsgroup, last, False); + if (((offset != (unsigned)-1) && *(char **)((char *) art + offset)) || + (fixfunction && *(char **)((char *) art + fixed_offset)) || + (unreadonly && IS_READ(art)) || + IS_UNAVAIL(art) || (max && ((count + (last - first)) >= max))) + break; + } + last--; - (void) sprintf(command, "XHDR %s %ld-%ld", field, first, last); - put_server(command); - get_data_from_server(message, sizeof(message)); + (void) sprintf(command, "XHDR %s %ld-%ld", field, first, last); + put_server(command); + message = get_data_from_server(True); - check_server_response(command, message, sizeof(message)); + check_server_response(command, &message); - /* check for errors */ - if (*message != CHAR_OK) { - mesgPane(XRN_SERIOUS, pane_name, XHDR_ERROR_MSG); - return True; - } + /* check for errors */ + if (*message != CHAR_OK) { + mesgPane(XRN_SERIOUS, pane_name, XHDR_ERROR_MSG); + return True; + } - for(;;) { - get_data_from_server(message, sizeof(message)); - if (*message == '.') { - break; - } - count++; + for(;;) { + message = get_data_from_server(True); + if (*message == '.') { + break; + } - /* - * message is of the form: - * - * Number value - * - * must get the number since not all articles will be returned - */ + /* + The brilliant folks at Microsoft have decided that it's OK for + XHDR to return multi-line header fields as multiple lines in + the XHDR response, even though that's never been done by any + other NNTP server and it's different from what XOVER does. + It's really annoying how they invent standards like this. For + the time being, I'm going to assume that someone is going to + show them the error of their ways and get them to fix their + server, so I'm going to ignore the bogus output for now + instead of actually trying to handle it. If they manage to + browbeat the world into doing things their way, as they so + often do, I'll consider at some point in the future adding + support for multi-line XHDR field response. Grr. + - jik 12/16/97 + */ + if (isspace(*message)) + continue; - number = atol(message); - line = index(message, ' '); - if (! (number && line)) { - mesgPane(XRN_SERIOUS, pane_name, MALFORMED_XHDR_RESPONSE_MSG, - command, message); - goto next_iteration; - } - art = artStructGet(newsgroup, number, True); - *(char **)((char *) art + offset) = - (*fixfunction)(newsgroup, number, line + 1); + count++; + + /* + * message is of the form: + * + * Number value + * + * must get the number since not all articles will be returned + */ + + number = atol(message); + line = index(message, ' '); + if (! (number && line)) { + mesgPane(XRN_SERIOUS, pane_name, MALFORMED_XHDR_RESPONSE_MSG, + command, message); + goto next_iteration; + } + + /* + Strip leading and trailing spaces. + */ + while (*line && isspace(*line)) + line++; + for (ptr = strchr(line, '\0') - 1; (ptr >= line) && isspace(*ptr); + *ptr-- = '\0') /* empty */; + if (!required && (strcmp(line, "(none)") == 0)) + continue; + + art = artStructGet(newsgroup, number, True); + if (offset != (unsigned)-1) + *(char **)((char *)art + offset) = + XtNewString(line); + if (fixfunction) + *(char **)((char *) art + fixed_offset) = + (*fixfunction)(newsgroup, number, line); + artStructSet(newsgroup, &art); + } + for (number = first; number <= last; number++) { + struct article copy; + Boolean changed = False; + + art = artStructGet(newsgroup, number, False); + copy = *art; + if ((offset != (unsigned)-1) && ! *(char **)((char *) art + offset)) + if (required) + goto unavail; + else { + *(char **)((char *)© + offset) = XtNewString(""); + changed = True; } + if (fixfunction && ! *(char **)((char *)art + fixed_offset)) if (required) - for (number = first; number <= last; number++) { - struct article copy; - art = artStructGet(newsgroup, number, False); - if (! *(char **)((char *) art + offset)) { - copy = *art; - copy.filename = 0; - copy.subject = 0; - copy.author = 0; - copy.lines = 0; - SET_UNAVAIL(©); - artStructReplace(newsgroup, &art, ©, number); - } - } - first = last + 1; - - next_iteration: - if (max) - break; + goto unavail; + else { + *(char **)((char *)© + fixed_offset) = XtNewString(""); + changed = True; + } + if (changed) + artStructReplace(newsgroup, &art, ©, number); + continue; + unavail: + CLEAR_ALL_NO_FREE(©); + SET_UNAVAIL(©); + artStructReplace(newsgroup, &art, ©, number); } - return (first > artlast) ? True : False; + first = last + 1; + + next_iteration: + if (max) + break; + } + return (first > artlast) ? True : False; } -static char *subjectFixFunction _ARGUMENTS((struct newsgroup *, art_num, char *)); +Boolean getsubjectlist( + _ANSIDECL(struct newsgroup *, newsgroup), + _ANSIDECL(art_num, artfirst), + _ANSIDECL(art_num, artlast), + _ANSIDECL(Boolean, unreadonly), + _ANSIDECL(int, max) + ) + _KNRDECL(struct newsgroup *, newsgroup) + _KNRDECL(art_num, artfirst) + _KNRDECL(art_num, artlast) + _KNRDECL(Boolean, unreadonly) + _KNRDECL(int, max) +{ + struct article foo; + unsigned offset; -static char *subjectFixFunction(newsgroup, artnum, subj) - struct newsgroup *newsgroup; - art_num artnum; - char *subj; + offset = (char *)&foo.subject - (char *)&foo; + + return getlist(newsgroup, artfirst, artlast, unreadonly, max, + "subject", offset, 0, 0, True); +} + +Boolean getnewsgroupslist( + _ANSIDECL(struct newsgroup *, newsgroup), + _ANSIDECL(art_num, artfirst), + _ANSIDECL(art_num, artlast), + _ANSIDECL(Boolean, unreadonly), + _ANSIDECL(int, max) + ) + _KNRDECL(struct newsgroup *, newsgroup) + _KNRDECL(art_num, artfirst) + _KNRDECL(art_num, artlast) + _KNRDECL(Boolean, unreadonly) + _KNRDECL(int, max) { - char buffer[MESSAGE_SIZE]; - int num_column = digits(newsgroup->last); + struct article foo; + unsigned offset; - (void) sprintf(buffer, " %*ld %s", num_column, artnum, subj); - return XtNewString(buffer); + offset = (char *)&foo.newsgroups - (char *)&foo; + + return getlist(newsgroup, artfirst, artlast, unreadonly, max, + "newsgroups", offset, 0, 0, True); } -Boolean getsubjectlist(newsgroup, artfirst, artlast, unreadonly, max) - struct newsgroup *newsgroup; - art_num artfirst; - art_num artlast; - Boolean unreadonly; - int max; +Boolean getxreflist( + _ANSIDECL(struct newsgroup *, newsgroup), + _ANSIDECL(art_num, artfirst), + _ANSIDECL(art_num, artlast), + _ANSIDECL(Boolean, unreadonly), + _ANSIDECL(int, max) + ) + _KNRDECL(struct newsgroup *, newsgroup) + _KNRDECL(art_num, artfirst) + _KNRDECL(art_num, artlast) + _KNRDECL(Boolean, unreadonly) + _KNRDECL(int, max) { struct article foo; unsigned offset; - offset = (char *)&foo.subject - (char *)&foo; + offset = (char *)&foo.xref - (char *)&foo; return getlist(newsgroup, artfirst, artlast, unreadonly, max, - "subject", subjectFixFunction, offset, True); + "xref", offset, 0, 0, False); +} + +Boolean getdatelist( + _ANSIDECL(struct newsgroup *, newsgroup), + _ANSIDECL(art_num, artfirst), + _ANSIDECL(art_num, artlast), + _ANSIDECL(Boolean, unreadonly), + _ANSIDECL(int, max) + ) + _KNRDECL(struct newsgroup *, newsgroup) + _KNRDECL(art_num, artfirst) + _KNRDECL(art_num, artlast) + _KNRDECL(Boolean, unreadonly) + _KNRDECL(int, max) +{ + struct article foo; + unsigned offset; + + offset = (char *)&foo.date - (char *)&foo; + + return getlist(newsgroup, artfirst, artlast, unreadonly, max, + "date", offset, 0, 0, True); +} + +Boolean getidlist( + _ANSIDECL(struct newsgroup *, newsgroup), + _ANSIDECL(art_num, artfirst), + _ANSIDECL(art_num, artlast), + _ANSIDECL(Boolean, unreadonly), + _ANSIDECL(int, max) + ) + _KNRDECL(struct newsgroup *, newsgroup) + _KNRDECL(art_num, artfirst) + _KNRDECL(art_num, artlast) + _KNRDECL(Boolean, unreadonly) + _KNRDECL(int, max) +{ + struct article foo; + unsigned offset; + + offset = (char *)&foo.id - (char *)&foo; + + return getlist(newsgroup, artfirst, artlast, unreadonly, max, + "message-id", offset, 0, 0, True); +} + +Boolean getreflist( + _ANSIDECL(struct newsgroup *, newsgroup), + _ANSIDECL(art_num, artfirst), + _ANSIDECL(art_num, artlast), + _ANSIDECL(Boolean, unreadonly), + _ANSIDECL(int, max) + ) + _KNRDECL(struct newsgroup *, newsgroup) + _KNRDECL(art_num, artfirst) + _KNRDECL(art_num, artlast) + _KNRDECL(Boolean, unreadonly) + _KNRDECL(int, max) +{ + struct article foo; + unsigned offset; + + offset = (char *)&foo.references - (char *)&foo; + + return getlist(newsgroup, artfirst, artlast, unreadonly, max, + "references", offset, 0, 0, False); } static char *authorFixFunction(newsgroup, artnum, message) @@ -1242,7 +1768,7 @@ char *message; { char *author = message, *brackbeg, *brackend, *end; - char authbuf[MESSAGE_SIZE]; + char authbuf[BUFFER_SIZE]; (void) strcpy(authbuf, author); @@ -1334,20 +1860,27 @@ return XtNewString(author); } -Boolean getauthorlist(newsgroup, artfirst, artlast, unreadonly, max) - struct newsgroup *newsgroup; - art_num artfirst; - art_num artlast; - Boolean unreadonly; - int max; +Boolean getauthorlist( + _ANSIDECL(struct newsgroup *, newsgroup), + _ANSIDECL(art_num, artfirst), + _ANSIDECL(art_num, artlast), + _ANSIDECL(Boolean, unreadonly), + _ANSIDECL(int, max) + ) + _KNRDECL(struct newsgroup *, newsgroup) + _KNRDECL(art_num, artfirst) + _KNRDECL(art_num, artlast) + _KNRDECL(Boolean, unreadonly) + _KNRDECL(int, max) { struct article foo; - unsigned offset; + unsigned offset, fixed_offset; - offset = (char *)&foo.author - (char *)&foo; + offset = (char *)&foo.from - (char *)&foo; + fixed_offset = (char *)&foo.author - (char *)&foo; return getlist(newsgroup, artfirst, artlast, unreadonly, max, - "from", authorFixFunction, offset, True); + "from", offset, authorFixFunction, fixed_offset, True); } @@ -1384,12 +1917,18 @@ return XtNewString(numoflines); } -Boolean getlineslist(newsgroup, artfirst, artlast, unreadonly, max) - struct newsgroup *newsgroup; - art_num artfirst; - art_num artlast; - Boolean unreadonly; - int max; +Boolean getlineslist( + _ANSIDECL(struct newsgroup *, newsgroup), + _ANSIDECL(art_num, artfirst), + _ANSIDECL(art_num, artlast), + _ANSIDECL(Boolean, unreadonly), + _ANSIDECL(int, max) + ) + _KNRDECL(struct newsgroup *, newsgroup) + _KNRDECL(art_num, artfirst) + _KNRDECL(art_num, artlast) + _KNRDECL(Boolean, unreadonly) + _KNRDECL(int, max) { struct article foo; unsigned offset; @@ -1400,7 +1939,7 @@ offset = (char *)&foo.lines - (char *)&foo; return getlist(newsgroup, artfirst, artlast, unreadonly, max, - "lines", linesFixFunction, offset, False); + "lines", (unsigned)-1, linesFixFunction, offset, False); } #ifndef INEWS @@ -1641,7 +2180,7 @@ * returns 1 for success, 0 for failure */ { - char command[MESSAGE_SIZE], message[MESSAGE_SIZE]; + char command[BUFFER_SIZE], *message; char *ptr, *saveptr; char **lines, **lines_ptr; @@ -1660,21 +2199,20 @@ #ifdef INEWS tempfile = utTempnam(app_resources.tmpDir, "xrn"); - tempfile = XtNewString(tempfile); (void) sprintf(buffer, "%s -h > %s 2>&1",INEWS, tempfile); if ((inews = xrn_popen(buffer, "w")) == NULL) { mesgPane(XRN_SERIOUS, 0, CANT_EXECUTE_CMD_POPEN_MSG, buffer); (void) unlink(tempfile); - FREE(tempfile); + utTempnamFree(tempfile); return POST_FAILED; } #else (void) strcpy(command, "POST"); put_server(command); - get_data_from_server(message, sizeof(message)); + message = get_data_from_server(True); - check_server_response(command, message, sizeof(message)); + check_server_response(command, &message); if (*message != CHAR_CONT) { mesgPane(XRN_SERIOUS, 0, NNTP_ERROR_MSG, message); @@ -1753,16 +2291,16 @@ } } (void) unlink(tempfile); - FREE(tempfile); + utTempnamFree(tempfile); return(POST_FAILED); } (void) unlink(tempfile); - FREE(tempfile); + utTempnamFree(tempfile); #else put_server("."); - get_data_from_server(message, sizeof(message)); + message = get_data_from_server(True); if (*message != CHAR_OK) { *ErrMsg = XtMalloc(strlen(SERVER_POSTING_ERROR_MSG) + strlen(message)); @@ -1774,7 +2312,74 @@ return(POST_OKAY); } +/* + * get XHDR information for a single article + */ +static char *xhdr_single _ARGUMENTS((struct newsgroup *, char *, long *)); + +static char *xhdr_single(newsgroup, buffer, error_code) + struct newsgroup *newsgroup; + char *buffer; + long *error_code; +{ + char *message, *ptr; + + *error_code = 0; + + if (SETNEWSGROUP(newsgroup)) + return NULL; + put_server(buffer); + message = get_data_from_server(True); + + check_server_response(buffer, &message); + + /* check for errors */ + if (*message != CHAR_OK) { + if ((*error_code = atol(message)) != ERR_NOART) { + fprintf(stderr, "NNTP error: %s\n", message); + mesgPane(XRN_SERIOUS, 0, XHDR_ERROR_MSG); + } + return NULL; + } + + message = get_data_from_server(True); + + /* no information */ + if (*message == '.') { + return NULL; + } + + ptr = index(message, ' '); + + /* malformed entry */ + if (ptr == NIL(char)) { + mesgPane(XRN_SERIOUS, 0, MALFORMED_XHDR_RESPONSE_MSG, buffer, message); + message = get_data_from_server(True); + return NULL; + } + + ptr++; + + /* no information */ + if (STREQ(ptr, "(none)")) { + /* ending '.' */ + do { + message = get_data_from_server(True); + } while (*message != '.'); + return NULL; + } + + ptr = XtNewString(ptr); + + /* ending '.' */ + do { + message = get_data_from_server(True); + } while (*message != '.'); + + return ptr; +} + #ifdef DONT_USE_XHDR_FOR_A_SINGLE_ITEM /* @@ -1788,7 +2393,7 @@ char *field; char **string; { - char buffer[BUFFER_SIZE], message[MESSAGE_SIZE], *ptr, *cmp, *found = 0; + char buffer[BUFFER_SIZE], *message, *ptr, *cmp, *found = 0; if (SETNEWSGROUP(newsgroup)) { *string = 0; @@ -1802,9 +2407,9 @@ */ (void) sprintf(buffer, "HEAD %ld", article); put_server(buffer); - get_data_from_server(message, sizeof(message)); + message = get_data_from_server(True); - check_server_response(buffer, message, sizeof(message)); + check_server_response(buffer, &message); if (*message != CHAR_OK) { /* can't get header */ @@ -1813,7 +2418,7 @@ } for (;;) { - get_data_from_server(message, sizeof(message)); + message = get_data_from_server(True); /* the header information is ended by a '.' on a line by itself */ if (message[0] == '.') @@ -1854,65 +2459,29 @@ char *field; char **string; { - char buffer[BUFFER_SIZE], message[MESSAGE_SIZE], *ptr; + char buffer[BUFFER_SIZE]; + long error_code; - if (SETNEWSGROUP(newsgroup)) { - *string = 0; - return; - } (void) sprintf(buffer, "XHDR %s %ld", field, article); - put_server(buffer); - get_data_from_server(message, sizeof(message)); - - check_server_response(buffer, message, sizeof(message)); - - /* check for errors */ - if (*message != CHAR_OK) { - fprintf(stderr, "NNTP error: %s\n", message); - *string = NIL(char); - mesgPane(XRN_SERIOUS, 0, XHDR_ERROR_MSG); - return; - } - - get_data_from_server(message, sizeof(message)); - - /* no information */ - if (*message == '.') { - *string = NIL(char); - return; - } - - ptr = index(message, ' '); - - /* malformed entry */ - if (ptr == NIL(char)) { - mesgPane(XRN_SERIOUS, 0, MALFORMED_XHDR_RESPONSE_MSG, buffer, message); - get_data_from_server(message, sizeof(message)); - return; - } - - ptr++; - - /* no information */ - if (STREQ(ptr, "(none)")) { - *string = NIL(char); - /* ending '.' */ - do { - get_data_from_server(message, sizeof(message)); - } while (*message != '.'); - return; - } - - *string = XtNewString(ptr); - - /* ending '.' */ - do { - get_data_from_server(message, sizeof(message)); - } while (*message != '.'); - + *string = xhdr_single(newsgroup, buffer, &error_code); return; } #endif + +/* Get XHDR information using an article's message ID. The returned + data is allocated and should be freed by the caller. */ + +char *xhdr_id(newsgroup, id, field, error_code) + struct newsgroup *newsgroup; + char *id; + char *field; + long *error_code; +{ + char buffer[BUFFER_SIZE]; + + (void) sprintf(buffer, "XHDR %s %s", field, id); + return xhdr_single(newsgroup, buffer, error_code); +} #ifndef POPEN_USES_INEXPENSIVE_FORK diff -u -d -r -N -P 8.02/server.h 9.00/server.h --- 8.02/server.h Wed Nov 15 09:22:02 1995 +++ 9.00/server.h Tue Jul 1 10:27:45 1997 @@ -2,7 +2,7 @@ #define SERVER_H /* - * $Id: server.h,v 1.20 1995/11/15 14:20:20 jik Exp $ + * $Id: server.h,v 1.31 1997/07/01 14:25:59 jik Exp $ */ /* @@ -37,6 +37,7 @@ #include "codes.h" #include "news.h" +#include "file_cache.h" /* * function definitions for the nntp server (in nntp/clientlib.c) @@ -52,42 +53,54 @@ /* Find out if a group exists, and if it doesn't and the active file hasn't been read, then read it. */ -extern Boolean verifyGroup _ARGUMENTS((char *, struct newsgroup **)); +extern Boolean verifyGroup _ARGUMENTS((char *, struct newsgroup **, + Boolean)); /* get the list of active news groups from the news server */ -extern void getactive _ARGUMENTS((/* Boolean */ int)); +extern void getactive _ARGUMENTS((Boolean)); /* see if the subscribed to groups have 0 or 1 article */ extern void badActiveFileCheck _ARGUMENTS((void)); /* get a single article in the current group from the news server */ -extern char *getarticle _ARGUMENTS((struct newsgroup *, art_num, long *, int, int, int)); +extern file_cache_file *getarticle _ARGUMENTS((struct newsgroup *, art_num, + long *, int)); -#define FULL_HEADER 1 -#define NORMAL_HEADER 2 -#define NOT_ROTATED 1 -#define XLATED 2 -#define NOT_XLATED 1 -#define ROTATED 2 +#define FULL_HEADER (1<<0) +#define XLATED (1<<1) +#define ROTATED (1<<2) +#define PAGEBREAKS (1<<3) +#define BACKSPACES (1<<4) /* * tell the server that the next set of article requests will be for this group * returns NO_GROUP on failure */ extern int getgroup _ARGUMENTS((struct newsgroup *,art_num *,art_num *, - int *, /* Boolean */ int)); + int *, Boolean)); /* get a list of subject lines for a range of articles in the current group from the server */ extern Boolean getsubjectlist _ARGUMENTS((struct newsgroup *,art_num,art_num, - /* Boolean */ int, int)); + Boolean, int)); +extern Boolean getnewsgroupslist _ARGUMENTS((struct newsgroup *,art_num,art_num, + Boolean, int)); extern Boolean getauthorlist _ARGUMENTS((struct newsgroup *,art_num,art_num, - /* Boolean */ int, int)); + Boolean, int)); extern Boolean getlineslist _ARGUMENTS((struct newsgroup *,art_num,art_num, - /* Boolean */ int, int)); + Boolean, int)); +extern Boolean getdatelist _ARGUMENTS((struct newsgroup *,art_num,art_num, + Boolean, int)); +extern Boolean getidlist _ARGUMENTS((struct newsgroup *,art_num,art_num, + Boolean, int)); +extern Boolean getreflist _ARGUMENTS((struct newsgroup *,art_num,art_num, + Boolean, int)); +extern Boolean getxreflist _ARGUMENTS((struct newsgroup *,art_num,art_num, + Boolean, int)); /* xhdr commands */ extern void xhdr _ARGUMENTS((struct newsgroup *, art_num, char *, char **)); +extern char *xhdr_id _ARGUMENTS((struct newsgroup *, char *, char *, long *)); /* post article */ extern int postArticle _ARGUMENTS((char *,int, char **)); @@ -109,7 +122,7 @@ #define ACTIVE_NEW 2 #define ACTIVE_OLD 3 -extern int parse_active_line _ARGUMENTS((char *, /* unsigned char */ int, +extern int parse_active_line _ARGUMENTS((char *, unsigned char, struct newsgroup **)); extern char *unparse_active_line _ARGUMENTS((struct newsgroup *)); extern int active_read; diff -u -d -r -N -P 8.02/sort.c 9.00/sort.c --- 8.02/sort.c Wed Dec 31 19:00:00 1969 +++ 9.00/sort.c Tue Dec 23 08:06:02 1997 @@ -0,0 +1,466 @@ +/* + + This file contains routines for sorting a list of articles from a + newsgroup, and for producting a textual representation of the sorted + article list for displaying in the newsgroup index. + + The method for using the routines in this file is as follows: + + 1) Call art_sort_init() to initialize a sort and get back an opaque + object for future sort manipulations. + + 2) Call the various art_sort_by_*() to do the sorts. These routines + should be called from minor to major sorting order. That is, if you + want the articles to be sorted by thread, and within thread by date, + you would call sort_by_date() first and then sort_by_thread(). + + 3) Call art_sort_done() to free the opaque object and get back a + string representing the finished product of the sort. + + You can also call art_sort_cancel() at any time after + art_sort_init() to abort a sort and free the memory associated with + it without getting back the string subject list. + + The newsgroup must be prefetched completely before sort_init() is + called. + + No other manipulation of the newsgroup's article list should be done + while sorting is being done. + */ + +#include +#include + +#include "config.h" +#include "utils.h" +#include "news.h" +#include "sort.h" +#include "artstruct.h" +#include "internals.h" +#include "getdate.h" +#include "mesg.h" +#include "mesg_strings.h" +#include "hash.h" +#include "resources.h" + +#define SUB_SORT_WIDTH 24 + + +struct sort_article { + void *sort_key; + art_num num; + struct article *art; +}; + +struct sort_data { + struct newsgroup *newsgroup; + art_num count; + struct sort_article *articles; + void (*last_sort) _ARGUMENTS((void *)); +}; + + +static int (*compare_function) _ARGUMENTS((qsort_arg_type, qsort_arg_type)); +static void (*sort_list[3]) _ARGUMENTS((void *)); + +void art_sort_parse_sortlist(orig_sort_spec) + char *orig_sort_spec; +{ + char *token; + int num_sorts = (sizeof(sort_list)/sizeof(sort_list[0])); + int i = num_sorts - 1; + char *sort_spec; + + if (!orig_sort_spec) + return; + + sort_spec = orig_sort_spec = XtNewString(orig_sort_spec); + + while ((token = strtok(sort_spec, ", \t\n")) && (i >= 0)) { + sort_spec = 0; + if (! strcasecmp(token, "false") || + ! strcasecmp(token, "off") || + ! strcasecmp(token, "0")) { + for (i = 0; i < num_sorts; i++) + sort_list[i] = 0; + XtFree(orig_sort_spec); + return; + } + else if (! strcasecmp(token, "subject") || + ! strcasecmp(token, "true") || + ! strcasecmp(token, "on") || + ! strcasecmp(token, "1")) + sort_list[i--] = art_sort_by_subject; + else if (! strcasecmp(token, "date")) + sort_list[i--] = art_sort_by_date; + else if (! strcasecmp(token, "thread")) + sort_list[i--] = art_sort_by_thread; + else + mesgPane(XRN_SERIOUS, 0, UNKNOWN_SORT_TYPE_MSG, token); + } + + if (token) + mesgPane(XRN_SERIOUS, 0, TOO_MANY_SORT_TYPES_MSG); + + XtFree(orig_sort_spec); + return; +} + +int art_sort_need_dates() +{ + return((sort_list[0] == art_sort_by_date) || + (sort_list[1] == art_sort_by_date) || + (sort_list[2] == art_sort_by_date)); +} + + +int art_sort_need_threads() +{ + return((sort_list[0] == art_sort_by_thread) || + (sort_list[1] == art_sort_by_thread) || + (sort_list[2] == art_sort_by_thread)); +} + + +void *art_sort_init( + _ANSIDECL(struct newsgroup *, newsgroup), + _ANSIDECL(art_num, first), + _ANSIDECL(art_num, last) + ) + _KNRDECL(struct newsgroup *, newsgroup) + _KNRDECL(art_num, first) + _KNRDECL(art_num, last) +{ + struct sort_data *data = (struct sort_data *) + XtMalloc(sizeof(struct sort_data)); + art_num i, count; + + data->newsgroup = newsgroup; + + /* + Figure out how many articles there are. + */ + for (i = first, count = 0; i <= last; i++) { + struct article *art = artStructGet(newsgroup, i, False); + if (IS_LISTED(art)) + count++; + } + + data->articles = (struct sort_article *) + XtCalloc(count, sizeof(*data->articles)); + data->count = count; + data->last_sort = 0; + + for (i = first, count = 0; i <= last; i++) { + struct article *art = artStructGet(newsgroup, i, False); + if (IS_LISTED(art)) { + data->articles[count].num = i; + data->articles[count++].art = art; + } + } + + return (void *) data; +} + + + +char *art_sort_done(data_p, line_length) + void *data_p; + int line_length; +{ + struct sort_data *data = (struct sort_data *) data_p; + int sub_width = subjectIndexLine(line_length, 0, data->newsgroup, 0, + data->last_sort == art_sort_by_thread); + char *out_data = XtMalloc(sub_width * data->count + 1), *ptr = out_data; + art_num i; + + for (i = 0; i < data->count; i++) { + (void) subjectIndexLine(line_length, ptr, data->newsgroup, + data->articles[i].num, + art_sort_need_threads()); + ptr += sub_width; + } + + if (! data->count) + *ptr = '\0'; + + art_sort_cancel(data_p); + + return out_data; +} + + + +void art_sort_cancel(data_p) + void *data_p; +{ + struct sort_data *data = (struct sort_data *) data_p; + + XtFree((char *) data->articles); + XtFree((char *) data); +} + + + +char *art_sort_doit( + _ANSIDECL(struct newsgroup *, newsgroup), + _ANSIDECL(art_num, first), + _ANSIDECL(art_num, last), + _ANSIDECL(int, line_length) + ) + _KNRDECL(struct newsgroup *, newsgroup) + _KNRDECL(art_num, first) + _KNRDECL(art_num, last) + _KNRDECL(int, line_length) +{ + void *data_p; + int i, num_sorts = sizeof(sort_list)/sizeof(sort_list[0]); + + data_p = art_sort_init(newsgroup, first, last); + for (i = 0; i < num_sorts; i++) + if (sort_list[i]) + (*sort_list[i])(data_p); + return art_sort_done(data_p, line_length); +} + + +static int key_compare _ARGUMENTS((qsort_arg_type, qsort_arg_type)); + +static int key_compare(a_p, b_p) + qsort_arg_type a_p, b_p; +{ + struct sort_article *a = (struct sort_article *) a_p; + struct sort_article *b = (struct sort_article *) b_p; + return (*compare_function)(a->sort_key, b->sort_key); +} + + +static void generate_subject_keys _ARGUMENTS((struct sort_data *)); + +static void generate_subject_keys(data) + struct sort_data *data; +{ + art_num i; + char *ptr; + + for (i = 0; i < data->count; i++) { + data->articles[i].sort_key = (void *) XtMalloc(SUB_SORT_WIDTH + 1); + (void) strncpy((char *) data->articles[i].sort_key, + subjectStrip(data->articles[i].art->subject), + SUB_SORT_WIDTH); + ((char *)data->articles[i].sort_key)[SUB_SORT_WIDTH] = '\0'; + for (ptr = data->articles[i].sort_key; *ptr; ptr++) + if (isupper(*ptr)) + *ptr = tolower(*ptr); + } +} + +int subject_value_compare(key1, key2) + void *key1, *key2; +{ + return (struct sort_article *)key1 - (struct sort_article *)key2; +} + +static void free_subject_keys _ARGUMENTS((struct sort_data *)); + +static void free_subject_keys(data) + struct sort_data *data; +{ + art_num i; + + for (i = 0; i < data->count; i++) { + XtFree((char *) data->articles[i].sort_key); + } +} + +void art_sort_by_subject(data_p) + void *data_p; +{ + struct sort_data *data = (struct sort_data *) data_p; + int i, tmp_pos = 0; + struct sort_article *tmp_articles; + hash_table_object hash_table; + void *hash_reference; + struct sort_article *hash_return; + + tmp_articles = (struct sort_article *) + XtCalloc(data->count, sizeof(*tmp_articles)); + + generate_subject_keys(data); + + hash_table = hash_table_create(data->count, + hash_string_calc, + hash_string_compare, subject_value_compare, + 0, 0); + + for (i = 0; i < data->count; i++) { + hash_table_insert(hash_table, (void *)data->articles[i].sort_key, + (void *)&data->articles[i], 0); + } + + for (i = 0; i < data->count; i++) { + hash_reference = HASH_NO_VALUE; + if ((hash_return = + hash_table_retrieve(hash_table, + (void *)data->articles[i].sort_key, + &hash_reference)) == HASH_NO_VALUE) { + continue; + } + do { + tmp_articles[tmp_pos++] = *hash_return; + } while ((hash_return = (struct sort_article *) + hash_table_retrieve(hash_table, + (void *)data->articles[i].sort_key, + &hash_reference)) != + (struct sort_article *)HASH_NO_VALUE); + + hash_table_delete(hash_table, + (void *)data->articles[i].sort_key, + HASH_NO_VALUE); + } + + XtFree((char *)data->articles); + data->articles = tmp_articles; + + hash_table_destroy(hash_table); + + free_subject_keys(data); + + data->last_sort = art_sort_by_subject; +} + + + +void generate_date_keys(data) + struct sort_data *data; +{ + art_num i; + + for (i = 0; i < data->count; i++) { + time_t date = get_date(data->articles[i].art->date); + if (date == (time_t)-1) { + if (app_resources.complainAboutBadDates) + mesgPane(XRN_SERIOUS, 0, UNPARSEABLE_DATE_MSG, + data->articles[i].num, data->newsgroup->name, + data->articles[i].art->date); + date = (time_t) 0; + } + data->articles[i].sort_key = (void *)date; + } +} + +int date_compare(a, b) + qsort_arg_type a, b; +{ + /* + Casting to char * because pointer arithmetic on void * causes a + warning. Casting to a pointer instead of integer value because + there are some architectures on which pointers are larger than + integers. + */ + return (char *)a - (char *)b; +} + + +void art_sort_by_date(data_p) + void *data_p; +{ + struct sort_data *data = (struct sort_data *) data_p; + + generate_date_keys(data); + + compare_function = date_compare; + qsort((void *)data->articles, data->count, sizeof(*data->articles), + key_compare); + data->last_sort = art_sort_by_date; +} + +static void do_art_thread _ARGUMENTS((struct sort_data *, + hash_table_object, + art_num, + struct sort_article *, + int *)); + +static void do_art_thread(data, table, this_art, artlist, artpos) + struct sort_data *data; + hash_table_object table; + art_num this_art; + struct sort_article *artlist; + int *artpos; +{ + int i; + art_num *ptr; + + i = (int)hash_table_retrieve(table, (void *)this_art, 0); + assert(i != (int)HASH_NO_VALUE); + + /* Circular article references are bogus, but unfortunately possible */ + if (! data->articles[i].sort_key) + return; + + artlist[(*artpos)++] = data->articles[i]; + data->articles[i].sort_key = (void *)0; + + if (! data->articles[i].art->children) + return; + + for (ptr = data->articles[i].art->children; *ptr; ptr++) { + do_art_thread(data, table, *ptr, artlist, artpos); + } +} + +void art_sort_by_thread(data_p) + void *data_p; +{ + struct sort_data *data = (struct sort_data *)data_p; + struct article *art; + int i, ret; + hash_table_object table, done_table; + struct sort_article *tmp_articles; + int tmp_pos = 0; + art_num this_art; + + tmp_articles = (struct sort_article *) + XtCalloc(data->count, sizeof(*tmp_articles)); + + table = hash_table_create(data->count, hash_int_calc, + hash_int_compare, hash_int_compare, + 0, 0); + done_table = hash_table_create(data->count, hash_int_calc, + hash_int_compare, hash_int_compare, 0, 0); + + for (i = 0; i < data->count; i++) { + int ret; + + data->articles[i].sort_key = (void *)1; + ret = hash_table_insert(table, (void *) data->articles[i].num, + (void *) i, 1); + assert(ret); + } + + for (i = 0; i < data->count; i++) { + if (! data->articles[i].sort_key) + continue; + art = data->articles[i].art; + this_art = data->articles[i].num; + /* We are keeping track of which articles we've already done so + that we can detect (and avoid) circular "References" + dependencies. */ + ret = hash_table_insert(done_table, (void *)this_art, (void *)1, 1); + assert(ret); + while (art->parent) { + if (! hash_table_insert(done_table, (void *) art->parent, (void *)1, 1)) + break; + this_art = art->parent; + art = artStructGet(data->newsgroup, art->parent, False); + assert(art); + } + do_art_thread(data, table, this_art, tmp_articles, &tmp_pos); + } + + hash_table_destroy(table); + hash_table_destroy(done_table); + XtFree((char *)data->articles); + data->articles = tmp_articles; + data->last_sort = art_sort_by_thread; +} diff -u -d -r -N -P 8.02/sort.h 9.00/sort.h --- 8.02/sort.h Wed Dec 31 19:00:00 1969 +++ 9.00/sort.h Thu Jun 5 07:11:42 1997 @@ -0,0 +1,20 @@ +#ifndef _XRN_SORT_H +#define _XRN_SORT_H + +#include "utils.h" + +void art_sort_parse_sortlist _ARGUMENTS((char *)); +int art_sort_need_dates _ARGUMENTS((void)); +int art_sort_need_threads _ARGUMENTS((void)); + +void *art_sort_init _ARGUMENTS((struct newsgroup *, art_num, art_num)); +char *art_sort_done _ARGUMENTS((void *, int)); +void art_sort_cancel _ARGUMENTS((void *)); + +char *art_sort_doit _ARGUMENTS((struct newsgroup *, art_num, art_num, int)); + +void art_sort_by_subject _ARGUMENTS((void *)); +void art_sort_by_date _ARGUMENTS((void *)); +void art_sort_by_thread _ARGUMENTS((void *)); + +#endif /* _XRN_SORT_H */ diff -u -d -r -N -P 8.02/utils.c 9.00/utils.c --- 8.02/utils.c Sun Oct 8 03:48:55 1995 +++ 9.00/utils.c Thu Jun 5 07:11:42 1997 @@ -1,5 +1,5 @@ #if !defined(lint) && !defined(SABER) && !defined(GCC_WALL) -static char XRNrcsid[] = "$Id: utils.c,v 1.22 1995/10/08 07:46:33 jik Exp $"; +static char XRNrcsid[] = "$Id: utils.c,v 1.29 1997/02/27 11:36:44 jik Exp $"; #endif /* @@ -51,6 +51,7 @@ #include "news.h" #include "server.h" #include "resources.h" +#include "getdate.h" #define USER_NAME_SIZE 32 @@ -339,311 +340,35 @@ #endif /* NEED_STRCASECMP */ -#ifdef REALLY_USE_LOCALTIME - -#ifdef NO_MKTIME - -static int days[] = { 31,28,31,30,31,30,31,31,30,31,30,31}; - -/********************************************************************** -This function performs the function of mktime as declared in time.h, -but which has no definition. It's the inverse of gmtime. -**********************************************************************/ - -static time_t makeTime _ARGUMENTS((struct tm *)); - -static time_t makeTime(tmp) - struct tm *tmp; -{ - time_t ret; - int i; - - if (tmp->tm_year < 70) return 0; - ret = ((tmp->tm_year-70) / 4)*(366+365+365+365); - switch ((tmp->tm_year-70) % 4) { - case 1: - ret += 365; - break; - case 2: - ret += 365+365; - if (tmp->tm_mon > 1) { - ret += 1; - } - break; - case 3: - ret += 365+366+365; - } - for (i=0; itm_mon; i++) { - ret += days[i]; - } - ret += tmp->tm_mday-1; - ret = ret*24+tmp->tm_hour; - if (tmp->tm_isdst) { - ret -= 1; - } - ret = ret*60+tmp->tm_min; - ret = ret*60+tmp->tm_sec; - return ret; -} - -#else /* ! NO_MKTIME */ - -#define makeTime mktime - -#endif /* NO_MKTIME */ - -#ifdef ultrix -static char *getzonename _ARGUMENTS((int)); -#endif - -/********************************************************************** -This (ugly) function takes a source of the form "31 Aug 90 16:47:06 GMT" -and writes into dest the equivalent in local time. If an invalid -source is given, the dest is a copy of the source. - -Optionally, there may be a "XXX, " prepending the source where XXX is -a weekday name. -**********************************************************************/ - -int tconvert(dest, source) +void tconvert(dest, source) char *dest, *source; { - char *p, *fmt; - int h, m, s, day, mon, year; - struct tm *tmp, t; - time_t then; - int doWeekDay; -#if defined(apollo) - int daylight; -#endif - - strcpy(dest, source); - - /* Parse date */ - p = source; - if (!strncasecmp(p, "mon, ", 5) || - !strncasecmp(p, "tue, ", 5) || - !strncasecmp(p, "wed, ", 5) || - !strncasecmp(p, "thu, ", 5) || - !strncasecmp(p, "fri, ", 5) || - !strncasecmp(p, "sat, ", 5) || - !strncasecmp(p, "sun, ", 5)) { - p += 5; - doWeekDay = 1; - } else { -/* doWeekDay = 0; */ - doWeekDay = 1; /* Let's put the weekday in all postings */ - } - while (*p == ' ') { - p++; - } - if (sscanf(p, "%d", &day) != 1) { - return; - } - while (*p != ' ' && *p != '\0') { - p++; - } - if (*p == '\0') { - return; - } - while (*p == ' ' && *p != '\0') { - p++; - } - if (*p == '\0') { - return; - } - if (!strncasecmp(p, "jan", 3)) { - mon = 0; - } else if (!strncasecmp(p, "feb", 3)) { - mon = 1; - } else if (!strncasecmp(p, "mar", 3)) { - mon = 2; - } else if (!strncasecmp(p, "apr", 3)) { - mon = 3; - } else if (!strncasecmp(p, "may", 3)) { - mon = 4; - } else if (!strncasecmp(p, "jun", 3)) { - mon = 5; - } else if (!strncasecmp(p, "jul", 3)) { - mon = 6; - } else if (!strncasecmp(p, "aug", 3)) { - mon = 7; - } else if (!strncasecmp(p, "sep", 3)) { - mon = 8; - } else if (!strncasecmp(p, "oct", 3)) { - mon = 9; - } else if (!strncasecmp(p, "nov", 3)) { - mon = 10; - } else if (!strncasecmp(p, "dec", 3)) { - mon = 11; - } else { - return; - } - while (*p != ' ' && *p != '\0') { - p++; - } - if (*p == '\0') { - return; - } - if (sscanf(p, "%d", &year) != 1) { - return; - } - - if (year > 1900) - year -= 1900; - - /* Parse time */ - p = strrchr(source, ':'); - if (!p) { - return; - } - p--; - while (p > source && *p != ':') { - p--; - } - while (p > source && *p != ' ') { - p--; - } - if (!p) { - return; - } - sscanf(p, "%d", &h); - p = strchr(p, ':'); - if (!p++) { - return; - } - sscanf(p, "%d", &m); - p = strchr(p, ':'); - if (!p++) { - return; - } - sscanf(p, "%d", &s); - p = strchr(p, ' '); - if (!p++) { - return; - } - - /* Confirm GMT */ - if (strcmp(p, "GMT")) { - return; - } - - t.tm_sec = s; - t.tm_min = m; - t.tm_hour = h; - t.tm_mday = day; - t.tm_mon = mon; - t.tm_year = year; - t.tm_isdst = 0; - then = makeTime(&t); - tmp = localtime(&then); - -/* ascftime is non-standard, sigh. - ascftime(dest, "%e %b %y %H:%M:%S %Z", tmp); -*/ - fmt = asctime(tmp); - if (p = strrchr(fmt, '\n')) - *p = '\0'; - /* Make this look like the original format */ - p = dest; - if (doWeekDay) { - switch (tmp->tm_wday) { - case 0: - strcpy(dest, "Sun, "); - break; - case 1: - strcpy(dest, "Mon, "); - break; - case 2: - strcpy(dest, "Tue, "); - break; - case 3: - strcpy(dest, "Wed, "); - break; - case 4: - strcpy(dest, "Thu, "); - break; - case 5: - strcpy(dest, "Fri, "); - break; - case 6: - strcpy(dest, "Sat, "); - break; - } - p += 5; - } - if (*(fmt+8) == ' ') { - strncpy(p, fmt+9, 2); - p += 2; - } else { - strncpy(p, fmt+8, 3); - p += 3; - } - strncpy(p, fmt+4, 4); - p += 4; - sprintf(p, "%d", tmp->tm_year + 1900); - strcat(dest, fmt+10); - p = strrchr(dest, ' '); - *(p + 1) = '\0'; - -#if defined(sun) && !defined(SOLARIS) && !defined(SVR4) - (void) strcpy(p+1, tmp->tm_zone); -#endif - -#ifdef ultrix - (void) strcpy(p+1, getzonename(tmp->tm_isdst)); -#endif - -#if defined(apollo) - daylight = tmp->tm_isdst; -#endif + time_t converted = get_date(source); + char *ptr, buf[30] /* ctime only takes 26, but who knows if it'll change? */; -#if defined(apllo) || defined(SOLARIS) || defined(SVR4) || defined(hpux) - if (daylight) { - strcpy(p+1, tzname[1]); - } else { - strcpy(p+1, tzname[0]); + if (converted == (time_t)-1) { + if (dest != source) + strcpy(dest, source); } -#endif - - if (*(p + 1) == '\0') /* Couldn't find any timezone */ - *p = '\0'; - - if (*dest == ' ') { - p = dest; - while (*p != '\0') { - *p = *(p+1); - p++; + else { + strcpy(buf, ctime(&converted)); + /* RFC 822 requires a comma after the day, but ctime doesn't + provide one, so we'll have to insert it. We check explicitly + to make sure the date begins with three alphabetics and a + space, in case the ctime format changes out from under us. */ + if (isalpha(buf[0]) && isalpha(buf[1]) && isalpha(buf[2]) && + isspace(buf[3])) { + strncpy(dest, buf, 3); + dest[3] = ','; + strcpy(dest + 4, buf + 3); } + else + strcpy(dest, buf); + if ((ptr = strchr(dest, '\n'))) + *ptr = '\0'; } } -#ifdef ultrix - -extern char *timezone(); - -static char * getzonename(isdst) - int isdst; -{ - static char *name[2]; - struct timezone tz; - - if (isdst) - isdst = 1; - - if (name[isdst] != NULL) - return name[isdst]; - - gettimeofday(NULL, &tz); - name[isdst] = timezone(tz.tz_minuteswest, isdst); - name[isdst] = XtNewString(name[isdst]); - return name[isdst]; -} - -#endif /* ultrix */ - -#endif /* REALLY_USE_LOCALTIME */ - #ifdef XLATE #define UC(x) (unsigned char)(x) @@ -757,9 +482,12 @@ return(0); } -char *findServerFile(basename, prefer_long) -char *basename; -Boolean prefer_long; +char *findServerFile( + _ANSIDECL(char *, basename), + _ANSIDECL(Boolean, prefer_long) + ) + _KNRDECL(char *, basename) + _KNRDECL(Boolean, prefer_long) { char *server_name = nntpServer(); char *long_name = 0, *expanded; @@ -791,8 +519,8 @@ This is different from tempnam() or utTempnam(), which might put the file in $TMPDIR instead of the specified directory. - The file name returned is static and should be copied (if it needs - to be saved) before the next call to this function. + The file name returned is allocated and should be freed when it is + no longer needed. */ char *utTempFile(orig_name) char *orig_name; @@ -822,5 +550,23 @@ (void) sprintf(&new_name[strlen(new_name)], ".%d-%d-%s", instance++, getpid(), ptr); - return new_name; + return XtNewString(new_name); +} + +/* + * Calculate the number of digits in an integer. Sure, I could use a + * logarithm function, but that would require relying on a sane math + * library on all systems. The technique used in this function is + * gross, but what the heck, it works. + */ + +int utDigits(num) + long int num; +{ + char int_buf[20]; /* An article number longer than twenty digits? + I'll be dead by then! */ + + (void) sprintf(int_buf, "%ld", num); + return(strlen(int_buf)); } + diff -u -d -r -N -P 8.02/utils.h 9.00/utils.h --- 8.02/utils.h Sat Oct 28 14:56:39 1995 +++ 9.00/utils.h Sun Oct 19 22:11:55 1997 @@ -2,7 +2,7 @@ #define UTILS_H /* - * $Id: utils.h,v 1.41 1995/10/28 18:56:27 jik Exp $ + * $Id: utils.h,v 1.56 1997/10/20 02:11:55 jik Exp $ */ /* @@ -34,12 +34,6 @@ * utils.h: random utility functions and macros for xrn */ -#if defined(hpux) || defined(__hpux) -#ifndef _HPUX_SOURCE -#define _HPUX_SOURCE -#endif -#endif - /* This is necessary so that when is included, it will execute a pragma which will allow vfork to function correctly when @@ -75,6 +69,7 @@ /* Stupid SunOS header files are missing buttloads of declarations */ extern int printf(), fprintf(), fputc(), fputs(), fread(), fwrite(), sscanf(); +extern int fgetc(); extern int fclose(), vfprintf(), vsprintf(), puts(), fscanf(), _filbuf(); extern int _flsbuf(), rewind(), fseek(); extern int toupper(), tolower(); @@ -83,6 +78,8 @@ extern int system(); extern int read(), close(), fchmod(), rename(); extern int putenv(); +extern int strcasecmp(), strncasecmp(); +extern void bzero(), bcopy(); #endif /* SOLARIS */ #endif /* sun */ @@ -113,15 +110,15 @@ #define MIN(a,b) (((a)<(b))?(a):(b)) #endif -#ifndef _ARGUMENTS -#if defined(FUNCPROTO) && !defined(_NO_PROTO) -#define _ARGUMENTS(arglist) arglist +#if !defined(NeedFunctionPrototypes) +#if defined(FUNCPROTO) && !defined(_NO_PROTO) +#define NeedFunctionPrototypes 1 #else -#define _ARGUMENTS(arglist) () +#define NeedFunctionPrototypes 0 #endif #endif -#ifndef _VARARGUMENTS +#if !defined(NeedVarargsPrototypes) /* * The number of symbols I'm checking here seems a bit excessive. For * example, it probably isn't necessary to check both FUNCPROTO&2 and @@ -129,6 +126,26 @@ */ #if defined(FUNCPROTO) && (FUNCPROTO&2) && !defined(_NO_PROTO) && \ !defined(NOSTDHDRS) && defined(__STDC__) +#define NeedVarargsPrototypes 1 +#else +#define NeedVarargsPrototypes 0 +#endif +#endif + +#ifndef _ARGUMENTS +#if NeedFunctionPrototypes +#define _ARGUMENTS(arglist) arglist +#define _ANSIDECL(type,arg) type arg +#define _KNRDECL(type,arg) +#else +#define _ARGUMENTS(arglist) () +#define _ANSIDECL(type,arg) arg +#define _KNRDECL(type,arg) type arg; +#endif +#endif + +#ifndef _VARARGUMENTS +#if NeedVarargsPrototypes #define _VARARGUMENTS(arglist) arglist #define XRN_USE_STDARG #else @@ -143,7 +160,7 @@ #define SIG_RET_T int #endif -#if defined(_ANSI_C_SOURCE) || defined(linux) || defined(__bsdi__) || defined(SOLARIS) +#if defined(_ANSI_C_SOURCE) || defined(linux) || defined(__bsdi__) || defined(SOLARIS) || defined(__hpux) typedef SIG_RET_T (*SIG_PF0) _ARGUMENTS((int)); #else /* ! _ANSI_C_SOURCE */ typedef SIG_RET_T (*SIG_PF0) _VARARGUMENTS((int, ...)); @@ -203,16 +220,15 @@ #ifdef NEED_TEMPNAM extern char *utTempnam _ARGUMENTS((char *, char *)); -#define utGetarticle getarticle +#define utTempnamFree XtFree #else #ifdef TEMPFILE_DEBUG -static char *tempfile_debug_ret, *getarticle_debug_ret; +static char *tempfile_debug_ret; #define utTempnam(a,b) ((tempfile_debug_ret = tempnam(a,b)), fprintf(stderr, "tempnam: %s at %s:%d\n", tempfile_debug_ret, __FILE__, __LINE__), tempfile_debug_ret) -#define utGetarticle(a,b,c,d,e,f) ((getarticle_debug_ret = getarticle(a,b,c,d,e,f)), fprintf(stderr, "getarticle: %s at %s:%d\n", getarticle_debug_ret, __FILE__, __LINE__), getarticle_debug_ret) #else #define utTempnam tempnam -#define utGetarticle getarticle #endif /* TEMPFILE_DEBUG */ +#define utTempnamFree free #endif extern char *utTempFile _ARGUMENTS((char *)); @@ -225,7 +241,7 @@ extern char *regcmp(); extern char *regex(); #else -#ifdef linux +#ifdef POSIX_REGEX #include #else extern char *re_comp(); @@ -251,7 +267,7 @@ extern int strncasecmp _ARGUMENTS((CONST char *, CONST char *, size_t)); #endif -extern int tconvert _ARGUMENTS((char *, char *)); +extern void tconvert _ARGUMENTS((char *, char *)); #if defined(SYSV) || defined(SOLARIS) #ifndef index @@ -284,6 +300,30 @@ #define WALL(a) #endif -char *findServerFile _ARGUMENTS((char *, /* Boolean */ int)); +char *findServerFile _ARGUMENTS((char *, Boolean)); + +#if defined(__osf__) || defined(_POSIX_SOURCE) || defined(SOLARIS) \ + || defined(sun) +typedef CONST void * qsort_arg_type; +#else +typedef void * qsort_arg_type; +#endif + +int utDigits _ARGUMENTS((long int)); + +#ifdef BSD_BFUNCS +#ifndef memset +#define memset(_Str_, _Chr_, _Len_) bzero(_Str_, _Len_) +#endif +#ifndef memcpy +#define memcpy(_To_, _From_, _Len_) bcopy(_From_, _To_, _Len_) +#endif +#endif + +#if defined(BSD_BFUNCS) || defined(NO_MEMMOVE) +#ifndef memmove +#define memmove(_To_, _From_, _Len_) bcopy(_From_, _To_, _Len_) +#endif +#endif #endif /* UTILS_H */ diff -u -d -r -N -P 8.02/varfile.c 9.00/varfile.c --- 8.02/varfile.c Sat Nov 11 14:27:15 1995 +++ 9.00/varfile.c Sun Oct 19 18:31:32 1997 @@ -11,6 +11,7 @@ #ifdef TESTING #define utTempnam tempnam +#define utTempnamFree free #define mesgPane(a,b,c,d,e) fprintf(stderr, c, d, e) #define mesgPane6(a,b,c,d,e,f) fprintf(stderr, c, d, e, f) #define XtMalloc malloc @@ -65,6 +66,7 @@ #define CLEANUP { \ (void) fclose(input); \ (void) fclose(output); \ + XtFree(tmpfile); \ return 0; \ } #define WRITE_LINE { \ @@ -82,7 +84,7 @@ { FILE *input, *output = 0; struct var_rec *this_var; - char line[BUFSIZ], *name, *val, last_chopped = 0, *tmpfile; + char line[BUFSIZ], *name, *val, last_chopped = 0, *tmpfile = 0; #ifdef GCC_WALL tmpfile = 0; @@ -100,6 +102,7 @@ if (! (output = fopen(tmpfile, "w"))) { mesgPane(XRN_SERIOUS, 0, CANT_OPEN_TEMP_MSG, tmpfile, errmsg(errno)); (void) fclose(input); + XtFree(tmpfile); return 0; } for (this_var = vars; this_var->name; this_var++) @@ -126,15 +129,15 @@ if ((this_var = find_var(vars, name))) { if (this_var->written) continue; - if (! fprintf(output, "%c%s %s\n", CACHE_VAR_CHAR, - name, this_var->value)) { + if (fprintf(output, "%c%s %s\n", CACHE_VAR_CHAR, + name, this_var->value) == EOF) { mesgPane(XRN_SERIOUS, 0, ERROR_WRITING_SAVE_FILE_MSG, tmpfile, errmsg(errno)); CLEANUP; } this_var->written = 1; - } else if (! fprintf(output, "%c%s %s\n", CACHE_VAR_CHAR, - name, val)) { + } else if (fprintf(output, "%c%s %s\n", CACHE_VAR_CHAR, + name, val) == EOF) { mesgPane(XRN_SERIOUS, 0, ERROR_WRITING_SAVE_FILE_MSG, tmpfile, errmsg(errno)); CLEANUP; @@ -151,25 +154,29 @@ if (output) { for (this_var = vars; this_var->name; this_var++) if ((! this_var->written) && - (! fprintf(output, "%c%s %s\n", CACHE_VAR_CHAR, - this_var->name, this_var->value))) { + (fprintf(output, "%c%s %s\n", CACHE_VAR_CHAR, + this_var->name, this_var->value) == EOF)) { mesgPane(XRN_SERIOUS, 0, ERROR_WRITING_SAVE_FILE_MSG, tmpfile, errmsg(errno)); (void) fclose(output); + XtFree(tmpfile); return 0; } if (fclose(output) == EOF) { mesgPane(XRN_SERIOUS, 0, ERROR_WRITING_SAVE_FILE_MSG, tmpfile, errmsg(errno)); + XtFree(tmpfile); return 0; } if (rename(tmpfile, filename)) { mesgPane6(XRN_SERIOUS, 0, ERROR_RENAMING_MSG, tmpfile, filename, errmsg(errno)); + XtFree(tmpfile); return 0; } } + XtFree(tmpfile); return vars; } diff -u -d -r -N -P 8.02/xmisc.c 9.00/xmisc.c --- 8.02/xmisc.c Tue May 9 22:21:16 1995 +++ 9.00/xmisc.c Wed Dec 31 19:00:00 1969 @@ -1,237 +0,0 @@ - -#if !defined(lint) && !defined(SABER) && !defined(GCC_WALL) -static char XRNrcsid[] = "$Id: xmisc.c,v 1.12 1995/05/10 02:20:01 jik Exp $"; -#endif - -/* - * xrn - an X-based NNTP news reader - * - * Copyright (c) 1988-1993, Ellen M. Sentovich and Rick L. Spickelmier. - * - * Permission to use, copy, modify, and distribute this software and its - * documentation for any purpose and without fee is hereby granted, 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 the University of California not - * be used in advertising or publicity pertaining to distribution of - * the software without specific, written prior permission. The University - * of California makes no representations about the suitability of this - * software for any purpose. It is provided "as is" without express or - * implied warranty. - * - * THE UNIVERSITY OF CALIFORNIA DISCLAIMS ALL WARRANTIES WITH REGARD TO - * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS, IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA 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. - */ - -/* - * xmisc.c: routines for handling miscellaneous x functions - * - */ - -#include "copyright.h" -#include "config.h" -#include "utils.h" -#include -#include -#include -#include -#include -#include -#include "news.h" -#include "xthelper.h" -#include "resources.h" -#include "internals.h" -#include "xrn.h" -#include "xmisc.h" -#include "busyCursor.h" - -/* XRN icon */ - -#if SUPPORT_SILLY_CALVIN_ICON -#include "calvin.icon" -#endif -#include "xrn.icon" - -static Pixmap -getpm() -{ - unsigned int width, height; - unsigned char *bits; - -#if SUPPORT_SILLY_CALVIN_ICON - if (app_resources.calvin) { - width = calvin_width; - height = calvin_height; - bits = calvin_bits; - } else { -#endif - width = xrn_width; - height = xrn_height; - bits = xrn_bits; -#if SUPPORT_SILLY_CALVIN_ICON - } -#endif - - return XCreateBitmapFromData(XtDisplay(TopLevel), XtScreen(TopLevel)->root, - (char *) bits, width, height); -} - - -void -xmSetIconAndName(it) -IconType it; -{ - static char *PrevName = NULL, *OldName = NULL; - static Pixmap PrevPm = None, OldPm = None; - char *name WALL(= 0); - Pixmap pm WALL(= 0); - Arg arg; - - if (OldPm == None) { - XtSetArg(arg, XtNiconPixmap, &OldPm); - XtGetValues(TopLevel, &arg, 1); - PrevPm = OldPm; - } - - if (app_resources.iconPixmap == None) { - app_resources.iconPixmap = getpm(); - } - - if (app_resources.unreadIconPixmap == None) { - app_resources.unreadIconPixmap = app_resources.iconPixmap; - } - - switch (it) { - case InitIcon : - PrevPm = pm = app_resources.iconPixmap; - PrevName = name = OldName = app_resources.iconName; - break; - case UnreadIcon : - PrevPm = pm = app_resources.unreadIconPixmap; - PrevName = name = app_resources.unreadIconName; - break; - case ReadIcon : - PrevPm = pm = app_resources.iconPixmap; - PrevName = name = app_resources.iconName; - break; - case BusyIcon : - if ((pm = app_resources.busyIconPixmap) == None) - pm = OldPm; - if ((name = app_resources.busyIconName) == NULL) - name = OldName; - break; - case PrevIcon : - pm = PrevPm; - name = PrevName; - break; - } - - if (OldPm != pm) { - XtSetArg(arg, XtNiconPixmap, pm); - XtSetValues(TopLevel, &arg, 1); - OldPm = pm; - } - - if (name == NULL) { - name = app_resources.iconName; - } - - if (OldName != name) { - XSetIconName(XtDisplay(TopLevel),XtWindow(TopLevel),name); - XFlush(XtDisplay(TopLevel)); - OldName = name; - } -} - - -void -xmIconCreate() -{ - xmSetIconAndName(InitIcon); - if (app_resources.iconGeometry != NIL(char)) { - int scr, x, y, junk; - Arg args[2]; - - for(scr = 0; /* yyuucchh */ - XtScreen(TopLevel) != ScreenOfDisplay(XtDisplay(TopLevel), scr); - scr++); - - XGeometry(XtDisplay(TopLevel), scr, app_resources.iconGeometry, - "", 0, 0, 0, 0, 0, &x, &y, &junk, &junk); - XtSetArg(args[0], XtNiconX, x); - XtSetArg(args[1], XtNiconY, y); - XtSetValues(TopLevel, args, XtNumber(args)); - } - return; -} - - -/* - * create the normal and busy xrn cursors - */ - -void xrnBusyCursor() -{ - BusyCursor(TopLevel, True); - xmSetIconAndName(BusyIcon); - - return; -} - -void xrnUnbusyCursor() -{ - UnbusyCursor(TopLevel, True); - xmSetIconAndName(PrevIcon); - - return; -} - -/*ARGSUSED*/ -void CBbusyCursor(widget, client_data, call_data) - Widget widget; - XtPointer client_data; - XtPointer call_data; -{ - xrnBusyCursor(); - return; -} - -/*ARGSUSED*/ -void CBunbusyCursor(widget, client_data, call_data) - Widget widget; - XtPointer client_data; - XtPointer call_data; -{ - xrnUnbusyCursor(); - return; -} - -/* - A Command widget is passed in. Its border width/shadow and related - stuff is grown so that it's larger than other buttons (so that it is - visibly the default button). - */ - -void makeDefaultButton(widget) - Widget widget; -{ - Dimension border_width, highlight_thickness; - - XtVaGetValues(widget, - XtNborderWidth, (XtArgVal) &border_width, - XtNhighlightThickness, (XtArgVal) &highlight_thickness, - 0); - - border_width++; - highlight_thickness++; - - XtVaSetValues(widget, - XtNborderWidth, (XtArgVal) border_width, - XtNhighlightThickness, (XtArgVal) highlight_thickness, - 0); -} diff -u -d -r -N -P 8.02/xrn-man.src 9.00/xrn-man.src --- 8.02/xrn-man.src Thu May 9 14:16:03 1996 +++ 9.00/xrn-man.src Thu Jan 15 21:36:45 1998 @@ -1,5 +1,5 @@ -.TH XRN 1 "$Date: 1996/05/09 18:15:43 $" "X" -.\" $Id: xrn-man.src,v 1.123 1996/05/09 18:15:43 jik Exp $ +.TH XRN 1 "$Date: 1998/01/16 02:36:44 $" "X" +.\" $Id: xrn-man.src,v 1.204 1998/01/16 02:36:44 jik Exp $ .\" .\" xrn - an X-based NNTP news reader .\" @@ -25,12 +25,12 @@ .\" CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .ds XR \fIxrn\fP -.ds NS `.newsrc' +.ds NS .newsrc .ds NG \fBNewsgroup\fP .ds AR \fBArticle\fP .ds AL \fBAll\fP .ds AD \fBAdd\fP -.ds XD `~/.Xresources' +.ds XD "~/.Xresources" .\" .SH NAME xrn \- an X-based interface to the USENET news system that uses the NNTP remote news server @@ -42,7 +42,7 @@ users to read news from personal workstations by accessing a central news repository. -This manual page applies to version 8.02. +This manual page applies to version 9.00. .\" .SH DESCRIPTION .PP @@ -67,9 +67,7 @@ [\-defaultLines count] [+/-discardOld] [+/\-displayLineCount] -xREALLY_USE_LOCALTIME_STARTx [+/\-displayLocalTime] -xREALLY_USE_LOCALTIME_ENDx [\-distribution dist] [+/\-dumpCore] [\-editorCommand command] @@ -147,7 +145,7 @@ \*(XR uses the \*(NS file to determine what groups need to be read. If the \*(NS file does not exist, it is created, and the -user is subscribed to the news group `news.announce.newusers'. +user is subscribed to the news group "news.announce.newusers". .PP \*(XR has four modes of operation: \*(AD, \*(NG, \*(AL, and \*(AR modes. @@ -208,9 +206,9 @@ .sp The news system is a set of bulletins, discussion groups, program sources, and other bits of information distributed around the world under the -name `USENET'. -The information is generally called `news' and is broken up into -`newsgroups'. +name "USENET". +The information is generally called "news" and is broken up into +"newsgroups". Each newsgroup deals with a subject or set of subjects. The subjects for newsgroups are varied: from discussions about particular versions of UNIX to movie reviews, from information on the X window system @@ -218,9 +216,9 @@ .PP For information on what newsgroups are available, answers to commonly asked questions, and newsgroup ediquette, read the articles in the -newsgroup `news.announce.newsusers'. Users who are new to the USENET +newsgroup "news.announce.newsusers". Users who are new to the USENET are strongly encouraged to become familiar with the contents of the -articles in `news.announce.newusers' before posting any messages. +articles in "news.announce.newusers" before posting any messages. .\" .SH NEWS SERVER .PP @@ -231,11 +229,11 @@ about where to get the appropriate software. The NNTP server to which to connect must be specified in one of the following ways: -the `\-nntpServer' command line argument; +the "\-nntpServer" command line argument; the environment variable NNTPSERVER; the \fBnntpServer\fP X resource; xSERVER_FILE_STARTx -the contents of the file `xSERVER_FILEx' +the contents of the file "xSERVER_FILEx" xSERVER_FILE_ENDx xINN_STARTx the NNTP server configured into INN @@ -284,8 +282,8 @@ the article (when available), and is the author of the article. A `+' in the first position means that the article has been read, a `u' in the first position -means that the article has been marked as unread, a 'S' in the second -position means that the article has been saved to a file, and a 'P' in +means that the article has been marked as unread, a `S' in the second +position means that the article has been saved to a file, and a `P' in the second position means that the article has been printed. .PP The top information bar displays information about the mode, the buttons @@ -352,12 +350,12 @@ be used in X resources to control the appearance or behavior of individual button widgets. -Furthermore, the button names are used in the `ButtonList' options -(see their documentation in ``COMMAND LINE ARGUMENTS'', below) to +Furthermore, the button names are used in the "ButtonList" options +(see their documentation in "COMMAND LINE ARGUMENTS", below) to control which buttons are actually displayed. Note that only the buttons whose names are followed by asterisks in the lists below are displayed in the default \*(XR configuration; the others are displayed -only if you specify a `ButtonList' option which requests them. +only if you specify a "ButtonList" option which requests them. All button names are also action procedure names and can therefore be used in Xt translations to specify key sequences that activate button @@ -377,10 +375,10 @@ .IP "addQuit * (`q')" Add remaining groups in the list to \*(NS as unsubscribed; go to group mode. -.IP "addIgnoreRest (`x')" -If the `fullNewsrc' option is false, then mark the remaining groups -``ignored'' (i.e., don't subscribe to them or add them to the newsrc -file) and go to group mode. Otherwise, behave as `addQuit'. +.IP "addIgnoreRest * (`x')" +If the "fullNewsrc" option is false, then mark the remaining groups +"ignored" (i.e., don't subscribe to them or add them to the newsrc +file) and go to group mode. Otherwise, behave as "addQuit". .IP "addFirst * (`^')" Add the current group(s) to the beginning of the \*(NS file and mark as subscribed. @@ -406,9 +404,9 @@ .IP "addUnsub * (`u')" Add the current group(s) to the end of the \*(NS file and mark as unsubscribed. -.IP "addIgnore (`i')" -If the `fullNewsrc' option is false, then mark the current group(s) -``ignored''. Otherwise, behave as `addUnsub'. +.IP "addIgnore * (`i')" +If the "fullNewsrc" option is false, then mark the current group(s) +"ignored". Otherwise, behave as "addUnsub". .\" .SH NEWSGROUP MODE .PP @@ -422,7 +420,7 @@ "*newsgroupFrame.newsgroups.translations". In addition to the key bindings listed with buttons below, clicking the middle button on a group in the newsgroup list will cause \*(XR to enter that newsgroup. -.IP "ngQuit * ('q')" +.IP "ngQuit * (`q')" Quit \*(XR. .IP "ngRead * (space or `y')" Read the articles in the current group. The current group is either @@ -437,9 +435,11 @@ .IP "ngPrev * (up arrow or `p')" Move the cursor to the previous group, leaving the articles in the current group untouched. -.IP "ngScroll (``Next'', ``Page Down'' or Ctrl-v)" +.TP +ngScroll ("Next", "Page Down" or Ctrl-v) Scroll the list of newsgroups forward a page. -.IP "ngScrollBack (``Prior'', ``Page Up'' or Meta-v)" +.TP +ngScrollBack ("Prior", "Page Up" or Meta-v) Scroll the list of newsgroups backwards a page. .IP "ngCatchUp * (`c')" Mark all articles in the current group as read. @@ -490,7 +490,7 @@ subscribed groups whether or not they have unread news. \*(XR starts out listing only groups with unread news. .IP "ngSelect * (Shift-S)" -Select a range of groups for a subsequent `ngMove' operation. This +Select a range of groups for a subsequent "ngMove" operation. This selection is cancelled automatically if the list of newsgroups displayed in the newsgroup list changes. .IP "ngMove * (`m')" @@ -501,8 +501,8 @@ Quit \*(XR, leaving the \*(NS file unchanged since the last time it was updated. The \*(NS file is updated each time a rescan or checkpoint occurs, as well as each time you exit from \*(AR mode if -`updateNewsrc' is true. See below for more information about -rescanning, checkpointing, and `updateNewsrc'. +"updateNewsrc" is true. See below for more information about +rescanning, checkpointing, and "updateNewsrc". .IP "ngCheckPoint * (Ctrl-s)" Update the \*(NS file based on \*(XR's current state. .IP "ngGripe * (Shift-G)" @@ -510,7 +510,7 @@ the maintainer of \*(XR. .IP "ngPost * (`a')" Post an article to a newsgroup or a comma-separated list of -newsgroups. See ``COMPOSING MESSAGES'' below for more information. +newsgroups. See "COMPOSING MESSAGES" below for more information. .IP "ngPostAndMail * (Shift-A)" Post an article and mail it too. .IP "ngMail (Shift-M)" @@ -534,16 +534,24 @@ Move the cursor to the next group. .IP "allPrev * (up arrow or `p')" Move the cursor to the previous group. -.IP "allScroll (``Next'', ``Page Down'' or Ctrl-v)" +.TP +allScroll ("Next", "Page Down" or Ctrl-v) Scroll the list of newsgroups forward a page. -.IP "allScrollBack (``Prior'', ``Page Up'' or Meta-v)" +.TP +allScrollBack ("Prior", "Page Up" or Meta-v) Scroll the list of newsgroups backwards a page. +.IP "allSearch * (`/')" +Search the list of newsgroups for a specific string. +.IP "allLimit * ('l')" +Limit the display to a subset of the list of all newsgroups, by +specifying a regular expression indicating which groups to display. +If the list is already limited, enter an empty regular expression to +restore all groups to the display. .IP "allSub * (`s')" Subscribe to the selected groups, leaving them at their current position in the \*(NS file. .IP "allFirst * (`^')" -Subscribe to the selected groups and move them to the beginning of the -\*(NS file. +Subscribe to the selected groups and move them to the beginning of the \*(NS file. .IP "allLast * (`$')" Subscribe to the selected groups and move them to the end of the \*(NS file. @@ -553,20 +561,20 @@ file. .IP "allUnsub * (`u')" Unsubscribe from the selected groups. -.IP "allIgnore (`i')" +.IP "allIgnore * (`i')" Ignore the selected groups, i.e., unsubscribe from them and remove them from the newsrc file. .IP "allGoto * (space or `g')" Go to the current newsgroup (either the first selected newsgroup or -the newsgroup on the same line as the cursor). As with `ngGoto', +the newsgroup on the same line as the cursor). As with "ngGoto", either the first unread article or the last article (if there are no -unread articles) is displayed. However, unlike `ngGoto', this button +unread articles) is displayed. However, unlike "ngGoto", this button does not subscribe you to an unsubscribed newsgroup before entering it. .IP "allSelect * (Shift-S)" .IP "allMove * (`m')" -Same as the `ngSelect' and `ngMove' buttons in \*(NG mode. Note that -``ignored'' newsgroups cannot be moved, since they have no location in +Same as the "ngSelect" and "ngMove" buttons in \*(NG mode. Note that +"ignored" newsgroups cannot be moved, since they have no location in the newsrc file. .IP "allToggle * (`o')" Toggle between listing newsgroups in \*(NS order and alphabetical @@ -583,16 +591,16 @@ Use \*(AR mode for reading and manipulating articles in a group. When you enter \*(AR mode, it displays a list of unread articles and their Subjects, or it displays the last available article if there are no -unread articles. You can view previous articles by using `artPrev' -when viewing the first article, by using `artGotoArticle' to go to a +unread articles. You can view previous articles by using "artPrev" +when viewing the first article, by using "artGotoArticle" to go to a specific article older than the first article, by using one of the subject-search buttons to search backward for an article older than -the first article, or by using `artListOld' to list all articles in +the first article, or by using "artListOld" to list all articles in the group -Hitting the space bar in \*(AR mode will ``do the right thing''; it +Hitting the space bar in \*(AR mode will "do the right thing"; it will scroll an article if there is more of the article to see or call -the `artNextUnread' action otherwise. +the "artNextUnread" action otherwise. To change or add key bindings to \*(AR mode, use the X resource "*artFrame.subjects.translations". In addition to the key bindings @@ -604,18 +612,18 @@ you move the cursor in the subject list, \*(XR displays the article the cursor is on. However, it is also possible to navigate in the subject list without changing the displayed article. In particular, -you can use the ``artScrollIndex'', ``artScrollIndexBack'', ``artUp'' -and ``artDown'' actions to move the cursor without changing the +you can use the "artScrollIndex", "artScrollIndexBack", "artUp" +and "artDown" actions to move the cursor without changing the displayed article; you can also select articles with the left and right mouse buttons without changing the displayed article. When you navigate the subject list in this manner, you can use the -``artCurrent'' action to tell \*(XR to display the article that the +"artCurrent" action to tell \*(XR to display the article that the cursor is currently on in the subject list. .SS Buttons in the top button box .IP "artQuit * (`q')" Update the \*(NS file and return to \*(NG mode (or go to the next -newsgroup, if `stayInArticleMode' is true). +newsgroup, if "stayInArticleMode" is true). .IP "artNextUnread * (`n')" Starting at the first selected article if any articles are selected, or at the article under the cursor otherwise, display the next @@ -623,12 +631,12 @@ subject list if there are no unread articles after the starting point but there are unread articles before it. If no unread articles exist, \*(XR exits from \*(AR mode and returns to \*(NG or \*(AL mode (or -goes to the next newsgroup, if `stayInArticleMode' is true). +goes to the next newsgroup, if "stayInArticleMode" is true). .IP "artNext * (`N')" Display the selected article, if any, or the article under the subject cursor if it isn't currently displayed, or the next article after the currently displayed one. Exit from \*(AR mode (or go to the next -newsgroup, if `stayInArticleMode' is true) after the last article has +newsgroup, if "stayInArticleMode" is true) after the last article has been reached. .IP "artPrev * (`P')" Display the selected article, if any, or the article under the subject @@ -657,7 +665,7 @@ that are not explicitly marked unread, from the first listed article up to the first selected article. Otherwise, mark as read all articles that are not explicitly marked unread, and exit \*(AR mode -(note that `stayInArticleMode' does not affect this command). +(note that "stayInArticleMode" does not affect this command). .IP "artFedUp * (Meta-c)" Mark as read all articles in the current group that are not explicitly marked unread, and then go to the next group with unread articles. @@ -673,52 +681,109 @@ list cursor to the currently displayed article if it wasn't there already. -When an article is marked as unread, a 'u' is placed in the far left +When an article is marked as unread, a `u' is placed in the far left column next to the article's number. The only way to mark an article -as read once it has been marked with a `u' is to use the `artMarkRead' +as read once it has been marked with a `u' is to use the "artMarkRead" function. -The `artNext', `artPrev', `artSubNext', `artSubPrev', and -`artSubSearch' will all display articles that are marked unread as -they encounter them, but `artNextUnread' will not. +The "artNext", "artPrev", "artSubNext", "artSubPrev", +"artThreadParent" and "artSubSearch" commands will all display +articles that are marked unread as they encounter them, but +"artNextUnread" will not. +.IP "artSub (`+')" +Subscribe to the current group. This comment is used if you enter an +unsubscribed newsgroup from \*(AL mode and decide while reading it +that you wish to subscribe to it. .IP "artUnsub * (`u')" Unsubscribe from the current group; exit from \*(AR mode (or go to the -next newsgroup, if `stayInArticleMode' is set). +next newsgroup, if "stayInArticleMode" is set). .IP "artSubNext * (Ctrl-n)" .IP "artSubPrev * (Ctrl-p)" If articles are selected, then display the first selected article. Otherwise, if the cursor is not on the currently displayed article, then display the article the cursor is on. Otherwise, find and display the next or previous article with the same subject as the -current article (besides any `[rR][eE]:' prefix). If there are no +current article (besides any "[rR][eE]:" prefix). If there are no more articles with the current subject and there are more unread articles, display the first unread article. If there are no more articles with the current subject and there are no more unread articles, exit \*(AR mode (or go to the next newsgroup, if -`stayInArticleMode' is set). -.IP "artListOld * (Shift-L)" +"stayInArticleMode" is set). +.IP "artThreadParent * (Meta-p, Shift-Meta-P)" +Find and display the parent (actually, more generally, the most recent +available ancestor) of the current or selected article (i.e., the +article to which the current or selected article is a followup). + +This command will search through all of the article's ancestors, from +most to least recent, until it finds one which is available. By +default, articles which are currently listed in the Subject index take +precedence over other articles. To force this command to definitely +display the most recent parent, even if it is not currently listed, +hold down the shift key when you execute the command. +.IP "artListOld * (Shift-L, Ctrl-Shift-L)" List all articles available in the group, even those that have been read. Note that this button does not toggle (clicking this button twice will not put you back to where you were). Note, furthermore, that this command can take a while when is is executed on a newsgroup with many articles in it. -.IP "artKillSession * (`k')" -.IP "artKillLocal * (Shift-K)" -.IP "artKillGlobal * (Ctrl-k)" -Locate either the first selected article if any articles are selected, -or the article under the cursor otherwise, and mark all articles with -its subject as read. For `artKillLocal', put the subject in the -current group's KILL file as well, so that it will be marked read -automatically in the future. For `artKillGlobal', put the subject in -the global KILL file, so that it will be marked read automatically in -the future in all newsgroups. -.IP "artKillAuthor * (Meta-k)" + +Executing this command with the Ctrl key depressed will cause \*(XR to +pop up a dialog asking you to enter the first article number to list. +If you hit Enter without entering an article number, then \*(XR will +"fill in the gaps" in the current article list, i.e., it will display +old articles between the current first displayed article and the last +article in the newsgroup. If you instead enter an article number, +\*(XR will display all articles between that article and the last +article in the newsgroup. Finally, if you enter a number preceded by +a plus sign (`+'), \*(XR will decrement the first displayed article by +the specified number (e.g., if you specify "+20", then \*(XR will +display twenty additional old articles. +.IP "artResort * (`o', Ctrl-o)" +Resort the article list. This command won't do anything when you +first enter article mode, because the article list is pre-sorted. +However, if you do something which causes new articles to be added to +the beginning of the list (e.g., executing the "artPrev" command while +viewing the first article in the list), you can use this command to +resort the list. + +Executing this command as Ctrl-o causes the article list to be +.BR un sorted, +i.e., regardless of your default sort order, the subject list will be +reordered into numerical order. There is no mouse button binding for +unsorting the article list; you must type Ctrl-o to do so. +.IP "artKillSubject * (`k', Shift-K, Ctrl-k)" +.IP "artKillAuthor * (Meta-k, Meta-Shift-K, Meta-Ctrl-k)" Locate either the first selected article if any articles are selected, or the article under the cursor otherwise, and mark all articles with -its author as read in this newsgroup. (There is currently no way to -automatically add an author to a group's KILL file or to the global -KILL file, but that functionality is on the TODO list of future -enhancements to \*(XR.) +its subject (or author) as read. Then, if the command was executed +with the Shift key held down, add a command to kill the subject (or +author) to the current group's kill file, so that it will be marked +read automatically in the future. Alternatively, if the Ctrl key was +held down, then the command is added to the global kill file, so that +it will affect all newsgroups rather than just the current group. + +Note that the Shift and Ctrl bindings apply to the subject-kill and +author-kill buttons as well as key commands. E.g., if you hold +down the Shift key while clicking on the "Subject kill" button, the +kill command will be added to the group's kill file as well as being +executed on the currently listed articles. Also, for users who don't +want to have to use the keyboard to kill articles, clicking button 3 +(usually the right button) on a kill button is equivalent to holding +down the Shift key, and clicking button 2 (usually the middle button, +if there is one) is equivalent to holding down Ctrl. +.IP "artKillThread * (`t', Shift-T, Ctrl-t)" +.IP "artKillSubthread * (Meta-t, Meta-Shift-T, Meta-Ctrl-t)" +As above, but kill the article's thread or subthread instead of its +subject or author. An article's thread is killed by finding its +oldest ancestor and killing all articles which claim to be descendants +of that ancestor. An article's subthread is killed by killing all of +its descendants (as opposed to the descendants of its oldest +ancestor). When an article is the first one in a thread (i.e., it has +no ancestors), killing its thread and its subthread do the same thing. + +Thread-killing functionality is not available unless "thread" is one +of the sort types specified in the "sortedSubjects" option described +below. .IP "artSubSearch * (`/')" Starting the first selected article if any articles are selected, or at the article under the cursor otherwise, search for an article whose @@ -733,24 +798,28 @@ .IP "artContinue * (Meta-/)" Continue the last regular expression search, searching for the same regular expression in the same direction. -.IP "artScroll (``Next'', ``Page Down'' or Ctrl-v)" +.TP +artScroll ("Next", "Page Down" or Ctrl-v) Scroll the article text forward a page. -.IP "artScrollBack (``Prior'', ``Page Up'', `b' or Meta-v)" +.TP +artScrollBack ("Prior", "Page Up", `b' or Meta-v) Scroll the article text backward a page. .IP "artScrollLine (Meta-down arrow)" Scroll the article text forward one line. .IP "artScrollBackLine (Meta-up arrow)" Scroll the article text backward one line. -.IP "artScrollEnd ('>')" +.IP "artScrollEnd (`>')" Scroll to the end of the article text. -.IP "artScrollBeginning ('<')" +.IP "artScrollBeginning (`<')" Scroll to the beginning of the article text. -.IP "artScrollIndex (Shift-``Next'', Shift-``Page Down'' or Shift-Ctrl-V)" +.TP +artScrollIndex (Shift-"Next", Shift-"Page Down" or Shift-Ctrl-V) Scroll the article index forward a page. -.IP "artScrollIndexBack (Shift-``Prior'', Shift-``Page Up'' or Shift-Meta-V)" +.IP +artScrollIndexBack (Shift-"Prior", Shift-"Page Up" or Shift-Meta-V) Scroll the article index backward a page. .IP "artPost * (`a')" -Post an article to the current group. See ``COMPOSING MESSAGES'' +Post an article to the current group. See "COMPOSING MESSAGES" below for more information. .IP "artPostAndMail * (Shift-A)" Post an article to the current group and mail it as well. @@ -759,19 +828,19 @@ .IP "artExit * (`x')" Restore the current group's articles to the read/unread state they were in before the newsgroup was entered and exit from \*(AR mode. -Note that articles marked read or unread by KILL-file processing +Note that articles marked read or unread by kill-file processing remain so marked. .IP "artCheckPoint * (Ctrl-s)" -Same as `ngCheckPoint'. +Same as "ngCheckPoint". .IP "artGripe (Shift-G)" -Same as `ngGripe'. +Same as "ngGripe". .SS Buttons in the bottom button box (Note that buttons can only be placed in the button box in which they were originally assigned by \*(XR. Therefore, if you want to include any of the buttons in this section in a "*ButtonList" option (see below), you must use "artSpecButtonList", not "artButtonList".) -.IP "artSave * (`s', `w' or '|')" +.IP "artSave * (`s', `w' or `|')" Save the current article in a file or mail folder or pipe it it into a command. This command pops up a dialog for you to enter the file name in which to save, and buttons to execute the save or abort @@ -788,22 +857,23 @@ If the filename does not start with any of those special characters, then it is assumed to be a normal filename, and the article is appended to it. If the filename is relative (does not begin with `/' -or `~'), `~/News/' will be prepended to it. +or `~'), "~/News/" (or the value of the "saveDir" option) will be +prepended to it. If no filename is specified, the article is saved in -`~/News/Groupname', where `Groupname' is the name of the current group -with the first letter capitalized If `saveMode' (see below) is set to -`subdirs', then `~/News/groupname/' will be used instead of `~/News/'. +"~/News/Groupname", where "Groupname" is the name of the current group +with the first letter capitalized If "saveMode" (see below) is set to +"subdirs", then "~/News/groupname/" will be used instead of "~/News/". If multiple articles are selected when this command is executed, then all will be saved as specified. -If a specified filename has a `%d' in it, the `%d' will be replaced +If a specified filename has a "%d" in it, the "%d" will be replaced with the article number being saved. To save in a file with `%' in -its name, you must use two `%' characters, i.e., `%%'. +its name, you must use two `%' characters, i.e., "%%". .IP "artReply * (`r')" Reply (by mail) to the author of the current article. -See ``COMPOSING MESSAGES'' below for more information. +See "COMPOSING MESSAGES" below for more information. .IP "artForward * (Meta-f)" Forward the current article to a person or multiple people via mail. .IP "artFollowup * (`f')" @@ -814,11 +884,11 @@ Cancel the current article. You can only cancel an article that you wrote. .IP "artRot13 * (Shift-X or Ctrl-x)" -Decrypt an article ``encrypted'' with the ``rot-13'' algorithm. -In some newsgroups (e.g., ``rec.humor'', ``rec.humor.funny''), +Decrypt an article "encrypted" with the "rot-13" algorithm. +In some newsgroups (e.g., "rec.humor", "rec.humor.funny"), articles that may offend certain people are sometimes posted. To minimize the offense, these articles are sometimes encoded with -``rot-13'', a simple letter-substitution cipher, so that users must +"rot-13", a simple letter-substitution cipher, so that users must take explicit action in order to view them. Executing this command will decode such an encoded message; executing the command a second time on the same article will return the article to its original @@ -831,9 +901,10 @@ Toggle between showing all header lines in the article and showing a limited set of header lines. This command is ineffective (and therefore its button is insensitive) if you have not set the -`stripHeaders' or `leaveHeaders' option (see below). +"stripHeaders", "leaveHeaders", or "displayLocalTime" option (see +below). .IP "artPrint *" -Print the article (see the `printCommand' option below). +Print the article (see the "printCommand" option below). .\" .SH COMPOSING MESSAGES .SS Kinds of messages @@ -851,7 +922,7 @@ X toolkit editor, whose command syntax is similar to that of .IR emacs (1). However, you can also use an editor of your own choosing to edit -messages. For more information, see the `editorCommand' option below. +messages. For more information, see the "editorCommand" option below. .SS Signature files \*(XR will attempt to read a signature file and include its contents in the message template. (Note, however, that a signature may not be @@ -862,14 +933,14 @@ .I inews to post articles.) -The signature file name is set with the `signatureFile' option (see -below); it defaults to `~/.signature'. However, rather than just +The signature file name is set with the "signatureFile" option (see +below); it defaults to "~/.signature". However, rather than just checking for that file \*(XR will first check for a signature file that is specific to the current newsgroup or newsgroup hierarchy or to the type of message being composed. -For example, if you are posting an article in `comp.sources.x' and -`signatureFile' is set to `~/.signature', \*(XR will check for the +For example, if you are posting an article in "comp.sources.x" and +"signatureFile" is set to "~/.signature", \*(XR will check for the existence of any of the following signature files (in this order): .sp .in +0.5i @@ -880,18 +951,18 @@ .fi .in -0.5i .sp -Then, it will check for `~/.signature.post'. In general, the message -types used for this check are and `followup', `forward', `gripe', -`reply', `post', and `mail'. In this check, a message that is both a -followup and a reply has type `followup', and a message that is both a -posting and a mail message has type `post'. If none of these files is -found, it will finally check for `~/.signature'. +Then, it will check for "~/.signature.post". In general, the message +types used for this check are and "followup", "forward", "gripe", +"reply", "post", and "mail". In this check, a message that is both a +followup and a reply has type "followup", and a message that is both a +posting and a mail message has type "post". If none of these files is +found, it will finally check for "~/.signature". -If the `executableSignatures' option is enabled and the signature file +If the "executableSignatures" option is enabled and the signature file that \*(XR finds is executable, \*(XR will run the signature file as a program and use its output as the signature. -If the `signatureNotify' option is enabled, \*(XR will display an +If the "signatureNotify" option is enabled, \*(XR will display an informational message telling you which signature file it is reading or executing. @@ -903,14 +974,14 @@ composing some types of messages and are therefore omitted) at the bottom of the editor window which do the following: abort the message without sending it; save the message in the file specified by the -`savePostings' option (see below); send the message; include in the +"savePostings" option (see below); send the message; include in the message the text of article to which this is a followup and/or reply; or include in the message a specified file (for which you are prompted with a dialog box). You may only compose one message at a time. .SS Posting restrictions -.if (xCROSSPOST_PROHIBITx > 0) \{\ +.if (xCROSSPOST_PROHIBITx) \{\ \*(XR will not permit you to post a message to xCROSSPOST_PROHIBITx or more newsgroups. .\} @@ -918,7 +989,7 @@ If the value of the "warnings.posting.crossPost" resource (see "X RESOURCES", below) is non-zero and you attempt to post to that many or more newsgroups, or if "warnings.posting.followupTo" is non-zero and -attempt to post to that many or more groups when your `Followup-To' +attempt to post to that many or more groups when your "Followup-To" line does not contain fewer groups than that, \*(XR will ask you to consider reducing the number of newsgroups to which you are posting. Since it is unlikely that your message is appropriate in all of the @@ -948,12 +1019,21 @@ Use the given list of buttons for \*(AD mode in the order given rather than the default button list (described above). -The ``list'' is a comma separated list of button names, as given in +The "list" is a comma separated list of button names, as given in the lists of buttons above. For example, your list might be: -``addQuit, addIgnoreRest, addLast, addUnsub''. +"addQuit, addIgnoreRest, addLast, addUnsub". + +Buttons whose functionality is not available will not be displayed +even if they appear in your button list or in the default button list. +For example, if you are not allowed to post articles to the news +server, then none of the buttons which cause articles to be posted +will be displayed. Similarly, if you have not specified any value for +the "leaveHeaders", "stripHeaders", or "displayLocalTime" option, then +header stripping is not active, so the "artHeader" button will not be +displayed in \*(AR mode. .TP 10 .B \-allButtonList list -Use the given list of buttons for \*(AL mode. The format of ``list'' +Use the given list of buttons for \*(AL mode. The format of "list" is as described above for the \-addButtonList option. .TP 10 .B \-artButtonList list @@ -963,16 +1043,53 @@ Use the given list of buttons for the bottom button box in \*(AR mode. .TP 10 .B \-authenticator command -This command line is used as ``AUTHINFO GENERIC '' in response -to an authentication challenge (NNTP response code 480) from the news -server. -If the ``NNTPAUTH'' environment variable is present, it overrides -the resource file entry. -The default (built into \*(XR) is ``any ''. +If "command" begins with the (case-insensitive) string "user/pass", +then NNTP "AUTHINFO USER/PASS" authentication is activated. After +"user/pass", you may include optional whitespace, and then the +username to use for authentication, if it is different from your UNIX +username. Note that the username may not begin or end with +whitespace, i.e., whitespace preceding or following the username is +ignored, and it may not contain a slash. +.if (xALLOW_RESOURCE_PASSWORDSx) \{\ + +Following the username (or following "user/pass", if no username is +specified), you may specify your NNTP password, preceded by a slash. +If you do not specify a password here, you will be prompted for it +when it is needed. Like the username, the password may not begin or +end with whitespace. + +For example, "user/pass username/password" specifies both the username +and password, "user/pass username" specifies the username but omits +the password, and "user/pass /password" specifies only the password. + +You are +.B strongly discouraged +from hard-coding your password in an application-defaults file that +might be read by other people. Furthermore, note that if you specify +your password in a value for this option on the \*(XR command line, +others might be able to see your password in the output of "ps". +Therefore, you should specify your password in this option only when +using secure, single-user machine to which you can be sure that you +are the only one with access. +.\} + +If the value of this option starts with anything other than +"user/pass", then NNTP AUTHINFO GENERIC authentication is used, and +"command" specifies the type of authentication to request from the +server (that is, it is put in the NNTP command "AUTHINFO GENERIC +") in response to an authentication challenge (NNTP response +code 480) from the news server. In this case, the +"authenticatorCommand" option (see below) must be set; if it is not, +the value of this option is ignored, and the default ("user/pass", as +noted below) is used instead. + +If the "NNTPAUTH" environment variable is present, it overrides the +resource file entry for this option. The default value of this option +is "user/pass". .TP 10 .B \-authenticatorCommand command This is the command line that is used to prompt the user for authentication. -\*(XR invokes this command line, with ``%s'' replaced by the authenticator +\*(XR invokes this command line, with "%s" replaced by the authenticator command (see above). The default (in the application defaults file) is an invocation of xterm, from which the authenticator command will prompt the user for authentication. @@ -981,9 +1098,9 @@ Display the full name of the author or the user/hostname of the author. .TP 10 .B \-breakLength len -Break lines longer than `len' characters into multiple lines. +Break lines longer than "len" characters into multiple lines. Default is 0 characters. If set to 0, line breaking is disabled -(see also 'lineLength'). +(see also "lineLength"). .TP 10 .B \-busyIconName name .TP 10 @@ -995,7 +1112,7 @@ .B \-cacheFile file Use the specified file to cache information other than what's in the newsrc file that needs to be preserved between invocations of \*(XR. -Defaults to `~/.xrncache-hostname', where `hostname' is the NNTP +Defaults to "~/.xrncache-hostname", where "hostname" is the NNTP server host, unless ~/.xrncache already exists and ~/.xrncache-hostname doesn't, in which case ~/.xrncache will be used. .TP 10 @@ -1004,10 +1121,10 @@ button. .TP 10 .B +/\-cc -Put 'Cc: user' in replies. (X resources class is "CC".) +Put "Cc: user" in replies. (X resources class is "CC".) .TP 10 .B +/\-ccForward -Put 'Cc: user' in forwarded messages. (X resources class is "CC".) +Put "Cc: user" in forwarded messages. (X resources class is "CC".) .TP 10 .B \-confirm list Turn on confirmation boxes for the buttons listed. @@ -1021,29 +1138,30 @@ .TP 10 .B \-deadLetters file The name of the file to save failed postings and messages. -Defaults to `~/dead.letter'. +Defaults to "~/dead.letter". .TP 10 .B +defaultLines count Number of lines to scroll the subject list in article mode when scrolling automatically at the bottom of the list. .TP 10 .B +/\-discardOld -If enabled and `onlyShow' is set, then articles earlier than the +If enabled and "onlyShow" is set, then articles earlier than the requested number of articles at the end of the newsgroup are marked read automatically. Disabled by default. -xREALLY_USE_LOCALTIME_STARTx .TP 10 .B +/\-displayLineCount When set, display the number of lines in each article (if available) in the article index in \*(AR mode. Set by default. .TP 10 .B +/\-displayLocalTime -Display the 'Date:' field of the article using the local time zone -(instead of GMT). -xREALLY_USE_LOCALTIME_ENDx +Display the "Date:" field of the article using the local time zone +(instead of the time zone in the article itself). When this option is +enabled, you may view the article's actual "Date:" field instead of +the date converted into the local time zone by executing the +"artHeader" command (see above). .TP 10 .B -distribution dist -Set the default distribution to `dist'. +Set the default distribution to "dist". .TP 10 .B +/\-dumpCore Dump core when a signal is detected. The X resources class for the @@ -1051,10 +1169,10 @@ .TP 10 .B \-editorCommand command Use an alternate editor for creating postings, followups, forwards, -gripes, and replies. `command' must be a string in +gripes, and replies. "command" must be a string in .IR sprintf (3) format containing -a `%s', which will be replaced by the file name to be edited. The +a "%s", which will be replaced by the file name to be edited. The command will be executed using the bourne shell .RI ( sh (1)). Examples are: @@ -1070,7 +1188,7 @@ The resulting command should handle all editing and windowing. The article being followed up on, replied to or forwarded is automatically included. -You can also specify `%D' and it will be replaced with the display +You can also specify "%D" and it will be replaced with the display name. For example: .sp @@ -1080,11 +1198,11 @@ .fi .in -0.25i .sp -If you specify an empty `editorCommand' string, the external editor is +If you specify an empty "editorCommand" string, the external editor is disabled and the editor built into \*(XR will be used. You can -use this to disable on the command line an `editorCommand' +use this to disable on the command line an "editorCommand" specification in your X resources, or to disable in your X resources -an `editorCommand' specification in the \*(XR app-defaults file +an "editorCommand" specification in the \*(XR app-defaults file installed at your site. .TP 10 .B +/\-executableSignatures @@ -1105,18 +1223,18 @@ new and enter \*(AD mode to ask you what you want to do with it. If not set, then any newsgroups not found in the newsrc file will be -considered ``ignored'' (as opposed to ``subscribed'' or -``unsubscribed'') and will be left out of the newsrc when \*(XR +considered "ignored" (as opposed to "subscribed" or +"unsubscribed") and will be left out of the newsrc when \*(XR updates it. In this case, only responses from the server to the -``NEWGROUPS'' command will be used to determine when new groups are +"NEWGROUPS" command will be used to determine when new groups are created. -When you run \*(XR with `fullNewsrc' disabled for the first time, any +When you run \*(XR with "fullNewsrc" disabled for the first time, any newsgroups created since the last time you ran \*(XR will be -``missed'' by \*(XR. To verify that you haven't missed any +"missed" by \*(XR. To verify that you haven't missed any interesting newsgroups because of this, enter \*(AL mode, execute the -`allToggle' command, and page to the end of the newsgroup listing to -see if there are any ``ignored'' groups there; if there are and you +"allToggle" command, and page to the end of the newsgroup listing to +see if there are any "ignored" groups there; if there are and you wish to subscribe to them, you can then do so. .TP 10 .B \-geometry WxH+X+Y @@ -1138,8 +1256,10 @@ matches one of the specified regular expressions is treated as an invalid group. For example, specifying a list containing "^talk\\. ^rec\\." would cause all newsgroups in the "talk" and "rec" hierarchies to be -ignored. Note that if you specify -.B \-ignoreNewsgropus +ignored. + +Note that if you specify +.B \-ignoreNewsgroups on the command line, you should enclose your list in single quotes to prevent any backslashes and other special characters in it from being interpreted by the shell. If, on the other hand, you specify it in @@ -1151,9 +1271,9 @@ .B \-includeCommand command Use an alternate program for inserting current article text when following up on, replying to or forwarding a message. -`command' must be a string in +"command" must be a string in .I sprintf -format that contains two `%s's, which will be replaced by +format that contains two "%s"s, which will be replaced by the include prefix and the article file name (in that order). Examples are: .sp @@ -1189,14 +1309,14 @@ .TP 10 .B +/\-killFiles Turn the use of kill files on/off. -The default is on. See ``KILL FILE FORMAT'' below for a description +The default is on. See "KILL FILE FORMAT" below for a description of the format of entries in kill files. .TP 10 .B \-leaveHeaders list The header fields to leave in the article; a comma separated case-insensitive list of field names (\fIi.e.,\fP subject,from,organization). -This option takes precedence over `stripHeaders'. -If the word `all' is specified instead of a list of fields, then all headers +This option takes precedence over "stripHeaders". +If the word "all" is specified instead of a list of fields, then all headers will be retained (This can be used in user X resources to override a resource specified in the global \*(XR application defaults, or on the command line to override a resource specified in either the @@ -1205,25 +1325,25 @@ .B \-lineLength len Length of lines that are broken. Default is 0 characters. If set to 0, line breaking is disabled -(see also 'breakLength'). +(see also "breakLength"). .TP 10 .B +/\-localSignatures If enabled, signature files are searched for in the same manner as local kill files, except that the file searched for is called SIGNATURE instead of KILL. For example, to find a signature file for a posting in news.software.readers, \*(XR will look for -`~/News/news/software/readers/SIGNATURE', -`~/News/news/software/SIGNATURE', `~/News/news/SIGNATURE', and -`~/News/SIGNATURE', in that order, and use the first one it finds. +"~/News/news/software/readers/SIGNATURE", +"~/News/news/software/SIGNATURE", "~/News/news/SIGNATURE", and +"~/News/SIGNATURE", in that order, and use the first one it finds. .TP 10 .B \-lockFile file -Set the XRN lock file name to `file'. Defaults to `~/.xrnlock'. +Set the XRN lock file name to "file". Defaults to "~/.xrnlock". .TP 10 .B \-mailer mailer The command to use for mailing replies. This command must take all of it's input from stardard input (\*(XR will not build a command line). -The default is `xSENDMAILx'. +The default is "xSENDMAILx". .TP 10 .B \-maxLines number The maximum number of lines above the cursor in the subject line display, @@ -1238,19 +1358,19 @@ .TP 10 .B \-newsrcFile file The newsrc file to use. -Defaults to `~/.newsrc'. -If a file with a name of the form `-' +Defaults to "~/.newsrc". +If a file with a name of the form "-"' is found, it will be used. .TP 10 .B \-ngButtonList list -Use the given list of buttons for \*(NG mode. The format of ``list'' +Use the given list of buttons for \*(NG mode. The format of "list" is as described above for the \-addButtonList option. .TP 10 .B \-nntpServer hostname The NNTP server to use (name or internet number). .TP 10 .B \-onlyShow number -Only grab the header information for the last 'number' of articles +Only grab the header information for the last "number" of articles in each group. This is useful if you have been away for a while and only want to see that last 100 or so articles in each group (and thus don't have to waste time having XRN fetch the subjects, authors, and @@ -1262,7 +1382,7 @@ displayed articles, then exit the newsgroup and enter it again, the last block of articles before that will be displayed to you. If you want earlier articles to be marked read automatically, use the -`discardOld' option. +"discardOld" option. .TP 10 .B \-organization organization Set the organization name in postings and followups. @@ -1302,7 +1422,7 @@ .B \-printCommand command Set the command used for printing articles. The article is sent to the command via standard input. -Defaults to `xPRINTCOMMANDx'. +Defaults to "xPRINTCOMMANDx". .TP 10 .B \-replyTo name Set the Reply-To field for articles and mail messages. @@ -1322,7 +1442,12 @@ no unread articles and therefore you will be shown the last read article, and instead you are shown new unread articles, or when you enter a newsgroup expecting to see five unread articles and instead -see ten. +see ten. Furthermore, it can cause \*(XR to check with the news +server for new articles in newsgroups you have +.B not +entered, if articles in newsgroups you enter are cross-posted to them, +which means that \*(XR may reveal new articles in a newsgroup in \*(NG +mode even when you have not done a rescan. On the other hand, this feature allows you to see new articles in a specific newsgroup immediately, without rescanning for new articles in @@ -1334,39 +1459,43 @@ automatically. A rescan time of 0 means never to check automatically. The default (unless configured differently when compiling \*(XR) is 0 (i.e., never check automatically). + +New newsgroups created on the server will not show up after an +automatic rescan. To check for new newsgroups, you must use the +"ngRescan" command, documented above. .TP 10 .B +/\-resetSave -Reset the string in the 'save' dialog box upon entering each +Reset the string in the "save" dialog box upon entering each newsgroup. .TP 10 .B \-saveDir dir The article saving directory. -Defaults to `~/News' when \-saveMode specifies `onedir', or -`~/News/newsgroup' when \-saveMode specifies `subdirs'. +Defaults to "~/News" when \-saveMode specifies "onedir", or +"~/News/newsgroup" when \-saveMode specifies "subdirs". .TP 10 .B \-saveMode mode -The mode for saving articles; a comma separated list of options. -The options can be `mailbox' or `normal', `headers' or `noheaders', -and `onedir' or `subdirs'. -The default is `normal,headers,onedir'. +The mode for saving articles; a comma separated list of options. The +options can be "mailbox" (UNIX mailbox format), "formfeed" (formfeeds +separating articles), or "normal"; "headers" or "noheaders"; and +"onedir" or "subdirs". The default is "normal,headers,onedir". .TP 10 .B \-saveNewsrcFile file -The saved `.newsrc' filename. -Before the `.newsrc' file is modified on startup, +The saved \*(NS filename. +Before the \*(NS file is modified on startup, it is saved in a backup file. -Defaults to `~/.oldnewsrc'. +Defaults to "~/.oldnewsrc". .TP 10 .B \-savePostings file The name of the file in which to save postings and messages (via the -`save' button in the composition window). -Defaults to `~/Articles'. +"save" button in the composition window). +Defaults to "~/Articles". .TP 10 .B \-saveString string -Use 'string' as the default in the 'save' dialog box. +Use "string" as the default in the "save" dialog box. .TP 10 .B \-signatureFile file The signature file to use. -Defaults to `~/.signature'. +Defaults to "~/.signature". .TP 10 .B +/\-signatureNotify When sending mail or posting, display a message saying which signature @@ -1374,6 +1503,9 @@ .TP 10 .B +/\-sortedSubjects Display the subject lines in the subject window sorted by subject. +Note that the "sortedSubjects" X resource behaves differently from the +command-line option, and provides additional functionality. See the +"X RESOURCES" section below for more information. .TP 10 .B +/\-stayInArticleMode If enabled, then a number of operations in article mode (including @@ -1385,7 +1517,7 @@ .B \-stripHeaders list The header fields to strip from the article; a comma separated case-insensitive list of field names (\fIi.e.,\fP keywords,message-id). -If the word `none' is specified instead of a list of fields, +If the word "none" is specified instead of a list of fields, then no headers will be stripped (This can be used in user X resources to override a resource specified in the global \*(XR application defaults, or on @@ -1402,22 +1534,22 @@ mode will always scroll to display the current article after operations on other articles in the index. For example, if you use the scroll bar to scroll to several articles that you want to save, -highlight the articles and click on the ``Save'' button, \*(XR will +highlight the articles and click on the "Save" button, \*(XR will scroll back to the current article when done saving. If disabled, then \*(XR will attempt to maintain the position you scrolled to even after performing an operation on other articles. Note, however, that this may result in some flickering of the subject index, due to -unavoidable ``disagreements'' between how \*(XR and the Athena Text +unavoidable "disagreements" between how \*(XR and the Athena Text widget think things should work. .TP 10 .B \-tmpDir directory The directory to use for the temporary storage of articles fetched -from the server. The default is `/tmp'. Note that the environment -variable ``TMPDIR'', if it is set, will override this option (or the +from the server. The default is "/tmp". Note that the environment +variable "TMPDIR", if it is set, will override this option (or the default value). .TP 10 .B \-topLines number -The number of lines to be used for the subject list in article mode. +The number of lines to be used for the subject list in \*(AR mode. The default is 10. .TP 10 .B +/\-typeAhead @@ -1430,22 +1562,31 @@ and/or pixmap as specified instead of using the default. .TP 10 .B +/\-updateNewsrc -Update the newsrc file when leaving \*(AR mode. +Update the newsrc file when leaving \*(AR mode (or when going from one +group directly to the next one with unread news). + +Note that whenever \*(XR updates the newsrc file, it also saves all +kill files. In between newsrc updates, \*(XR accumulates kill-file +data in memory and therefore its memory usage will grow until that +data is flushed by a newsrc update. Therefore, if you find that \*(XR +is using too much memory for your tastes, you might find it useful to +set "updateNewsrc" so that kill-file information will be flushed after +every newsgroup is read. .TP 10 .B \-verboseKill actions -By default, when processing KILL files, the subject of each article +By default, when processing kill files, the subject of each article that is marked read, marked unread, or saved is displayed, and a summary of all such articles is displayed when done processing the -KILL file. This option allows you to select which subjects and +kill file. This option allows you to select which subjects and summaries to display. The .B actions -specified should contain one or more of "l", "j", "m" and "s". "l" -means to display each KILL-file pattern as it is processed. "j" means -to display articles that are marked read, "m" means to display -articles that are marked unread, and "s" means to display articles +specified should contain one or more of `l', `j', `m' and `s'. `l' +means to display each kill-file pattern as it is processed. `j' means +to display articles that are marked read, `m' means to display +articles that are marked unread, and `s' means to display articles that are saved. If .B actions -is empty, then no information is displayed when processing KILL files. +is empty, then no information is displayed when processing kill files. .TP 10 .B \-watchUnread list Only check for unread news in groups matching one of the listed @@ -1454,28 +1595,133 @@ separated by spaces, tabs, commas and/or newlines. .\" .SH KILL FILE FORMAT -\*(XR supports a subset of the kill-file commands supported by -.IR rn (1). +\*(XR supports a super-subset of the kill-file commands supported by +.IR rn (1) +(i.e., some of its features are a subset of +.IR rn 's +and are compatible with it, and some are incompatible with +.IR rn +and you should use them only if you have no interest in your \*(XR +kill files being compatible with +.IR rn ). For compatibility with .IR rn , -lines in kill files beginning with ``THRU'' or ``&'' are ignored. -Any line in the format ``/pattern/options:command'' is interpreted as -a kill-file command; its components are: +lines in kill files beginning with `&' are ignored. Blank lines (or +lines containing only whitespace) and lines beginning with `#' are +also ignored. + +A line beginning with "THRU " is assumed to contain the last article +number previously killed in the newsgroup, and this line is updated by +\*(XR after processing the kill file for the newsgroup. + +A line of the form "include name" directs \*(XR to read another kill +file and process the commands in it as if they appear at that point in +the including file. If "name" is the name of a newsgroup, the local +kill file for that newsgroup is used. Otherwise, if "name" starts +with `/' or `~', it is treated as an absolute file name to use. +Otherwise, "name" is treated as a file name relative to the setting of +the "saveDir" option (see above), which defaults to "~/News". + +Kill-file inclusion is subject to the following constraints: +.TP 3 +\(bu +"THRU" lines in nested include files are ignored (but will be written +intact back to the included kill file if it is later saved by \*(XR, +e.g., if kill-file entry timeouts in it have been updated). +.IP \(bu +Loops and multiple inclusions of the same kill file +are silently ignored. +.IP \(bu +Kill files won't necessarily be updated properly if the user does +any of the following: +.RS 3 +.TP 3 +\(bu +Includes a newsgroup's kill file by specifying its file name +instead of by specifying its newsgroup name. +.IP \(bu +Includes the global kill file in another kill file. +.IP \(bu +Includes the same kill file using two different file names in +two different files (e.g., with an absolute path in one and a +relative path in another). +.RE + +Any line in the format "/pattern/options:command" is +interpreted as a kill-file command; its components are: .IP pattern -A regular expression which is matched against the -authors and subjects of articles to determine to which articles -``command'' should be applied. Slashes in the expression should be -quoted with a backslash to prevent them from being interpreted as the -end of the expression. +A regular expression which is matched against article header fields to +determine to which articles "command" should be applied. Slashes in +the expression should be quoted with a backslash to prevent them from +being interpreted as the end of the expression. .IP options -Interpreted by +The option `h' affects which header fields the regular expression is +matched against, as described below. If `h' is not specified, the +regular expression is matched against the "From" and "Subject" fields +of the article. + +The option `t', followed by a positive integer, specifies the number +of idle days after which the entry should automatically be expired. +That is, any kill-file command which contains a `t' option and does +not match any articles for the number of days specified in that option +is automatically removed from the kill file by \*(XR when the kill +file is updated. + +The option `u', followed by a positive integer, is used by \*(XR to +keep track of when a kill-file command was last used, for purposes of +deciding when to expire it. + +Any other values will cause \*(XR to display an error and ignore the +command. + +Note that the `t' and `u' options are incompatible with +.IR rn . +Therefore, if you choose to use the `t' option, either manually or +automatically by setting the "killTimeout" X resource (see below), you +will not be able to use your \*(XR kill files with +.IR rn . +In this case, if you use both \*(XR and .IR rn , -but ignored by \*(XR. +you may wish to consider setting the "killFileName" X resource (see +below) so that your \*(XR kill files are stored separately from your +.I ir +kill files. .IP command A single letter, either `j' (mark the article read), `m' (mark the article unread), or `s' (save the article in the default save file for the newsgroup). .PP +If the first character in the options field is `h', then \*(XR will +check to see if the regular expression starts with "^From:", +"^Subject:", "^Newsgroups:", "^Date:", "^Message-ID:", "^References:", +or "^Xref:". If it does, then the pattern will be matched against the +corresponding header field in the article. The comparison will be +"anchored", that is, the pattern must match from the beginning of the +field value in order to be considered a match. + +For example "/jik/:j" will mark read any article containing the string +"jik" in its "Subject" or "From" line; "/^From: .*jik/h:j" will mark +read articles containing "jik" in their "From" lines (but not articles +containing "jik" only in their "Subject" lines); and +"/^Newsgroups:.*,.*,/h:j" will mark read any articles cross-posted to +three or more newsgroups. + +If the `h' option is specified and the regular expression doesn't +start with one of the field names mentioned above, then it will be +compared against all of the fields mentioned above. + +Note that normally, \*(XR doesn't fetch the "Newsgroups", "Date", +"Message-ID", "References", or "Xref" fields of articles; it fetches +them only when it notices while processing a kill file entry that they +are needed. Therefore, specifying kill file entries which must be +matched against one of those fields may cause \*(XR to take longer to +fetch newsgroups, since it will have to fetch extra data. However, if +you're using a sorting algorithm which uses some or all of these +fields, as described in the documentation below for the +"sortedSubjects" resource, then the fields being used for sorting are +already being fetched and no additional data need be fetched in order +to match kill file entries against them. + Any line not in one of the already mentioned formats will cause \*(XR to display an error. .\" @@ -1525,6 +1771,22 @@ may wish to use in conjunction with "cacheActive" to prevent \*(XR's cache file from becoming too large. .TP 10 +.B cacheFilesMaxFiles +\*(XR keeps a rotating cache of recently retrieved articles in the +temporary directory (see the "tmpDir" option, above). By default, up +to 50 files will be kept in the cache. Specifying this resource will +raise or lower that limit. \*(XR silently enforces a minimum of 10 +for this resource. +.TP 10 +.B cacheFilesMaxSize +Although \*(XR will limit the number of files in its article cache as +described above, it will not by default limit the total size of the +cache. If you specify this resource, \*(XR will attempt to keep the +total size of the cache lower than the specified number of bytes. +.TP 10 +.B complainAboutBadDates (class "Debug") +See "sortedSubjects", below. +.TP 10 .B domainName Your internet domain (e.g., ".Berkeley.EDU", ".orst.edu"). Equivalent to setting the DOMAIN environment variable. You probably don't have @@ -1540,12 +1802,80 @@ This resource is overridden by the "HIDDENHOST" environment variable (see below). .TP 10 +.B killFileName +Tells \*(XR to name each of its kill files as specified instead of +using the default name "KILL". This is useful, e.g., if you use +another News reader besides \*(XR whose kill-file format is +incompatible with \*(XR's, but which uses "KILL" as the name of its +kill files. +.TP 10 +.B killTimeout +Specifies the number of days after which to automatically expire +(i.e., remove from the kill file) unused kill-file entries. + +Note that this is only the default timeout which is placed in new +kill-file entries created by \*(XR as the value of the `t' option. +This resource will not affect kill-file entries which already exist +and do not contain a `t' option. + +See the "KILL FILE FORMAT" section above for more information. +.TP 10 +.B nntpPort +Specifies the TCP/IP port number to use to connect to the NNTP +server. Defaults to the port number for the TCP "nntp" service. +.TP 10 +.B saveSentMail +.TP 10 +.B saveSentPostings +Save mail messages (articles) which are successfully sent (posted) in +the indicated file. By default, these resources are unset (i.e., +outgoing messages are not saved automatically). If these resources +are both set to the same file, messages which are both posted and +mailed will be saved in the indicated file only once. Both of these +resources have the class "SaveSent". +.TP 10 +.B sortedSubjects +Tells \*(XR how to sort articles before displaying them in the subject +list in \*(AR mode. Should contain a list of one or more of "date" +"subject" and "thread", separated by commas or whitespace. More +preferred sorting should be listed first, e.g., if you want articles +sorted by subject, and within each subject by date, you should specify +"subject date". It doesn't make mush sense to specify any sorting +types after "date", since most articles in a newsgroup will have +different dates, which means that a "date" sort will mostly undo any +other sort. It also doesn't make much sense to specify "thread" after +"subject", since subject sorting will to some extent undo thread +sorting Therefore, values for this option which make sense are "date", +"subject", "subject date", "thread", "thread date", and "thread +subject date" (which is what the author of \*(XR uses). + +For backward-compatibility reasons, "true", "on" and "1" (case +insensitive) are all equivalent to "subject", and "false, "off" and +"0" are eqivaulent to specifying no sorting at all. + +If no sorting is specified, articles are sorted by article number. + +Note that \*(XR needs to retrieve articles' "Date" fields from the +server in order to sort by date, and it doesn't normally do this, so +sorting by date may cause some degradation in performance, especially +when talking to a server over a slow network connection. Similarly, +"Message-ID" and "References" fields must be retrieved from the server +in order to do thread sorting. + +If you want \*(XR to tell you when it encounters a date that it can't +parse (usually because it contains an invalid timezone or a new +timezone that \*(XR's date-parsing routines don't know about), then +set the "complainAboutBadDates" resource to true. If you do this and +\*(XR tells you that it can't parse a date, please forward the entire +message about which it complains (including all of its headers) to the +\*(XR bug address given below. +.TP 10 .B warnings.followup.followupTo (class warnings.Followup) By default, when you start composing a followup to a previous message, -\*(XR will warn you if the default `Newsgroups' line of your followup -is different from the `Newsgroups' line of the previous message -because of a `Followup-To' line in that message. To disable this -warning, set this resource to `False' or the class to `0'. +\*(XR will warn you if the default "Newsgroups" line of your followup +is different from the "Newsgroups" line of the previous message +because of a "Followup-To" line in that message. To disable this +warning, set this resource to "False" or the class to "0". .TP 10 .B warnings.followup.crossPost (class warnings.Followup) By default, when you start composing a followup to a previous message, @@ -1566,6 +1896,27 @@ Followup-To line of your posting at which \*(XR will suggest that you remove some. The default is xFOLLOWUPTO_CONFIRMx. See "COMPOSING MESSAGES", above, for more information. +.TP 10 +.B validNewsgroups +A comma- or whitespace-separated list of regular expressions to be +matched against the server's list of newsgroups. Any newsgroup which +does not match one of the specified regular expressions is treated as an +invalid group. For example, specifying a list containing "^talk\\. ^rec\\." +would cause only the newsgroups in the "talk" and "rec" hierarchies to be +considered valid, while all others would be ignored. + +"validNewsgroups" and "ignoreNewsgroups" (described above) can be used +together, in which case the latter takes precedence over the +former. For example, specifying "validNewsgroups" as above and +"ignoreNewsgroups" as "^rec\\.games\\." would cause all "talk" and +"rec" groups except for the "rec.games" groups to be considered valid. +"validNewsgroups" is empty by default, which causes all groups (except +for those that match "ignoreNewsgroups" to be considered valid. + +When specifying "validNewsgroups" in your X resources, you should put +two backslashes whenever you want a single backslash to appear in a +regular expression, because the backslash is interpreted as a quoting +character when X resources are parsed. .PP Furthermore, \*(XR takes a number of specifications for colors, fonts, border widths, and other program options. @@ -1590,93 +1941,56 @@ A higher level default can be overridden by specifying a default at a lower level directly. .sp -XRN widget hierarchy: +The \*(XR widget hierarchy is as follows: .nf -...xrn (Shell) -vpane (Paned) - titlebar (Label) (optional) - index (Text) - indexinfo (Label) - indexbuttons (Box) - buttonName (Command) - articleText (Text) - textinfo (Label) - textbuttons (Box) - buttonName (Command) - -Composition (Shell) - vpane (Paned) - label (Label) +xrn (Shell) + ngFrame for \*(NG mode (Paned) + newsgroups (Text) + info (Label) + buttons (Box) + button names listed above, e.g., ngQuit (Command) + grip (Grip, not usually visible) + artFrame for \*(AR mode (Paned) + subjects (Text) + info (Label) + buttons (Box) + button names listed above, e.g., artQuit (Command) text (Text) - box (Box) - abort (Command) - send (Command) - save (Command) - includeArticle (Command) - includeFile (Command) + artInfo (Label) + artButtons (Box) + button names listed above, e.g., artSave (Command) + grip (Grip, two of them, with the one on bottom not usually visible) + allFrame for \*(AL mode (Paned) + list (Text) + info (Label) + buttons (Box) + button names listed above, e.g., allQuit (Command) + grip (Grip, not usually visible) + addFrame for \*(AD mode (Paned) + list (Text) + info (Label) + buttons (Box) + button names listed above, e.g., addQuit (Command) + grip (Grip, not usually visible) -dialogs... + Composition (TopLevelShell, separate window) + pane (Paned) + label (Label) + text (Text) + box (Box) + compAbort (Command) + compSend (Command) + compSave (Command) + compIncludeFile (Command) + compIncludeArticle (Command) + grip (Grip, two of them, not usually visible) + + Various dialogs .fi .sp -Examples of defaults are: -.in +0.3i -.nf -# -xrn.newsrcFile: ~/.newsrc -xrn.nntpServer: pasteur -xrn.organization: UC Berkeley XRN Design Team -xrn.replyTo: user@machine.domain -xrn.iconGeometry: +64+521 -xrn.leaveHeaders: subject,from -xrn.includeHeader: off -xrn.signatureFile: ~/.signature -xrn.geometry: =750x770+10+10 -xrn.deadLetters: ~/dead.letter -xrn.savePostings: ~/Articles -xrn.topLines: 9 -xrn.saveMode: mailbox,headers,onedir -xrn.minLines: 3 -xrn.maxLines: 6 -xrn.tmpDir: /tmp -xrn.mailer: /usr/lib/sendmail \-oi \-t -# -xrn.Gripe.geometry: +50+50 -# -xrn.ngButtonList: ngQuit,ngRead,ngCatchUp,ngRescan,ngSubscribe,ngPost -xrn.artButtonList: artQuit,artNextUnread,artCatchUp,artPost,artNextGroup -# -xrn*background: plum -xrn*foreground: red -xrn*font: 9x15 -xrn*border: LightGray -# -xrn*Text*Background: DarkSlateGray -xrn*Text*Foreground: yellow -xrn*ScrollBarMgr.thickness: 22 -xrn*ScrollBar.background: DarkSlateGray -xrn*ScrollBar.foreground: yellow -xrn*ScrollBar.border: White -xrn*Label.background: cyan -xrn*Label.foreground: blue -xrn*Command.foreground: White -xrn*Command.background: coral -# -xrn*Box.ngQuit.foreground: Black -xrn*Box.ngQuit.background: red -# -xrn*dialog*font: 9x15 -xrn*dialog*background: LimeGreen -xrn*dialog*foreground: CornflowerBlue -xrn*dialog*Label.foreground: NavyBlue -xrn*dialog*Command.foreground: Black -xrn*dialog*Command.background: yellow -xrn*dialog*Text.background: Wheat -xrn*dialog*Text.foreground: SteelBlue -xrn*dialog*borderWidth: 2 -# -.fi -.in -0.3i - +For example \*(XR resources, see the application-defaults file +included in the \*(XR distribution and installed with \*(XR (probably +in xXRNAPPDIRx). .SH FILES .TP 15 ~/.newsrc[-hostname] @@ -1696,7 +2010,7 @@ directory where articles are saved .TP 15 ~/Articles -where `saved' postings and messages are stored +where saved postings and messages are stored .TP 15 ~/dead.letter where failed postings and messages are stored @@ -1761,19 +2075,19 @@ .SH COMMENTS .PP The name (\*(XR) is a bit of a misnomer. -\*(XR is not an X interface to `rn' (the terminal-based news reading +\*(XR is not an X interface to "rn" (the terminal-based news reading program by Larry Wall), but is an X-based news reader that has -had part of the functionality of `rn' added since a number of our -users are (were?) `rn' users (all of the code is new). -Much of the `rn' funcionality that \*(XR currently has was not in the original -plan (KILL files, for example). +had part of the functionality of "rn" added since a number of our +users are (were?) "rn" users (all of the code is new). +Much of the "rn" funcionality that \*(XR currently has was not in the original +plan (kill files, for example). .sp -The user interface look and feel is modeled after that of `XMH' +The user interface look and feel is modeled after that of "XMH" (by Terry Weissman). .sp -The \*(NS file is updated on executing the `quit' command in \*(NG -mode, during every `rescan', and by `checkpoint'. -If the `updateNewsrc' option is set, the \*(NS file will be updated +The \*(NS file is updated on executing the "quit" command in \*(NG +mode, during every "rescan", and by "checkpoint". +If the "updateNewsrc" option is set, the \*(NS file will be updated every time \*(AR mode is exited. .sp \*(XR catches signals and X errors and will clean up on error exit @@ -1786,7 +2100,7 @@ for the program to exit. .sp XREFS are handled by \*(XR, however only articles that -are actually read (not marked as read by 'catchup' or 'mark as read') +are actually read (not marked as read by "catchup" or "mark as read") have their XREFS chased and only groups that are currently subscribed to have XREFed articles marked as read. .sp @@ -1805,13 +2119,13 @@ .sp \*(XR assumes a mailer that understands domain-based mail addresses. .sp -\*(XR notices that the `.newsrc' file has been updated by another program while +\*(XR notices that the \*(NS file has been updated by another program while \*(XR is running and informs the user (and gives the user the option to quit -without updating the `.newsrc' or to continue on). +without updating the \*(NS or to continue on). .sp Article temporary files can be removed and \*(XR will recover. .sp -\*(XR strips `^H' from articles. +\*(XR strips "^H" from articles. .sp The v{f,s}printf implementation included with \*(XR is from Robert A. Larson . @@ -1826,9 +2140,7 @@ .sp See TODO for a larger list of bugs and things that need to be done. .sp -Incomplete KILL file support: the THRU line is not updated, the only -actions supported are 'm', 'j', and 's', and matching is always done -against both the ``From:'' and ``Subject:'' lines of postings. +Incomplete kill file support. .sp See config.h for a list of defines you may want to use based on problems that may exist in your version of the X11 toolkit and widgets. @@ -1836,16 +2148,16 @@ See COMMON-PROBLMS for a list of common problems and solutions to the problems. .sp -Report bugs and requests for features to `bug\-xrn@cam.ov.com'. +Report bugs and requests for features to "bug\-xrn@kamens.brookline.ma.us". .sp Requests to be placed on the \*(XR users mailing list should be sent to -`xrn\-users\-request@cam.ov.com'. The only thing that comes +"xrn\-users\-request@kamens.brookline.ma.us". The only thing that comes across this mailing list is announcements of new releases and patches for serious problems so don't expect very much traffic. .\" .SH "AUTHORS" .PP -Jonathan Kamens (OpenVision Technologies, jik@cam.ov.com) +Jonathan Kamens (American Internet Corp., jik@kamens.brookline.ma.us) .PP Ellen M Sentovich (UC Berkeley, ellen@ic.berkeley.edu) .PP diff -u -d -r -N -P 8.02/xrn-man.sym 9.00/xrn-man.sym --- 8.02/xrn-man.sym Thu May 2 07:57:49 1996 +++ 9.00/xrn-man.sym Tue Dec 16 21:11:50 1997 @@ -1,3 +1,4 @@ +ALLOW_RESOURCE_PASSWORDS CROSSPOST_CONFIRM CROSSPOST_PROHIBIT DOMAIN_FILE @@ -7,8 +8,8 @@ MAX_SIGNATURE_SIZE PATH_FILE PRINTCOMMAND -REALLY_USE_LOCALTIME SENDMAIL SERVER_FILE UUCPNAME XLATE +XRNAPPDIR diff -u -d -r -N -P 8.02/xrn.c 9.00/xrn.c --- 8.02/xrn.c Sat Oct 28 14:39:51 1995 +++ 9.00/xrn.c Sun Jun 29 22:51:46 1997 @@ -1,5 +1,5 @@ #if !defined(lint) && !defined(SABER) && !defined(GCC_WALL) -static char XRNrcsid[] = "$Id: xrn.c,v 1.26 1995/10/28 18:39:19 jik Exp $"; +static char XRNrcsid[] = "$Id: xrn.c,v 1.34 1997/06/30 02:51:46 jik Exp $"; #endif /* @@ -36,14 +36,10 @@ #include "config.h" #include "utils.h" #include -#include -#include -#include -#include - -#include -#include -#include +#include /* so we have Widget */ +#ifdef MOTIF +# include +#endif #include "news.h" #include "xthelper.h" @@ -58,7 +54,9 @@ #include "compose.h" #include "mesg_strings.h" #include "InfoLine.h" +#include "Frame.h" #include "ngMode.h" +#include "file_cache.h" #ifdef XFILESEARCHPATH static void AddPathToSearchPath _ARGUMENTS((char *)); @@ -68,30 +66,20 @@ Widget TopLevel; XtAppContext TopContext; -Widget Frame; Widget TopInfoLine; /* top button info line */ Widget BottomInfoLine; /* bottom button info line */ int XRNState; /* XRN status: news and x */ int inchannel, outchannel; +file_cache FileCache; /*ARGSUSED*/ int main(argc, argv) int argc; char **argv; { - static Arg frameArgs[] = { /* main window description */ - {XtNx, (XtArgVal) 10}, - {XtNy, (XtArgVal) 10}, - {XtNheight, (XtArgVal) 800}, - {XtNwidth, (XtArgVal) 680}, - }; -#if 0 - /* See below for why these are disabled. */ - XtWidgetGeometry intended, return_geometry; - Arg sizeArgs[2]; -#endif + Widget frame; int sv[2]; @@ -99,8 +87,6 @@ inchannel = sv[0]; outchannel = sv[1]; - - XRNState = 0; #ifdef XFILESEARCHPATH @@ -109,36 +95,27 @@ TopLevel = Initialize(argc, argv); + if (app_resources.cacheFilesMaxFiles < 10) + app_resources.cacheFilesMaxFiles = 10; + if (app_resources.cacheFilesMaxSize < 0) + app_resources.cacheFilesMaxSize = 1; + ehInstallSignalHandlers(); ehInstallErrorHandlers(); - if (app_resources.geometry != NIL(char)) { - int bmask; - bmask = XParseGeometry(app_resources.geometry, /* geometry specification */ - (int *) &frameArgs[0].value, /* x */ - (int *) &frameArgs[1].value, /* y */ - (unsigned int *) &frameArgs[3].value, /* width */ - (unsigned int *) &frameArgs[2].value); /* height */ + FileCache = file_cache_create(app_resources.tmpDir, "xrn", + app_resources.cacheFilesMaxFiles, + app_resources.cacheFilesMaxSize); - /* handle negative x and y values */ - if ((bmask & XNegative) == XNegative) { - frameArgs[0].value += (XtArgVal) DisplayWidth(XtDisplay(TopLevel), - DefaultScreen(XtDisplay(TopLevel))); - frameArgs[0].value -= (int) frameArgs[3].value; - } - if ((bmask & YNegative) == YNegative) { - frameArgs[1].value += (XtArgVal) DisplayHeight(XtDisplay(TopLevel), - DefaultScreen(XtDisplay(TopLevel))); - frameArgs[1].value -= (int) frameArgs[2].value; - } + if (app_resources.geometry != NIL(char)) { + GetMainFrameSize(TopLevel, (app_resources.geometry)); } /* create the pane and its widgets */ - Frame = XtCreateManagedWidget("vpane", panedWidgetClass, TopLevel, - frameArgs, XtNumber(frameArgs)); + frame = CreateMainFrame(TopLevel); - TopInfoLine = InfoLineCreate("info", "", Frame); + TopInfoLine = InfoLineCreate("info", "", frame); BottomInfoLine = 0; createButtons(); @@ -146,46 +123,26 @@ /* create the icon */ xmIconCreate(); -#if XtSpecificationRelease > 5 - XtAddCallback(TopLevel, XtNsaveCallback, saveNewsrcCB, NULL); - XtAddCallback(TopLevel, XtNdieCallback, ehDieCB, NULL); -#endif /* X11R6 or greater */ + XrnAddCloseCallbacks(TopLevel); /* - I'm #if 0'ing this code out, because as far as I can tell, now - that I've moved things around so that initializeNews() and - determineMode() get called after the main window is already - realized, this code is no longer necessary. Disabling it causes - no problems under X11R6; if it causes problems for you under - X11R5 or X11R4, let me know. -- jik 11/13/94 + Be sure that initializeNews() and determineMode() get called after + the main window is already realized. -- jik 11/13/94 */ -#if 0 - /* Get the top button box to start out the right size. This is a */ - /* somewhat gross hack, but it does do the job. */ - intended.request_mode = CWWidth | XtCWQueryOnly; - XtSetArg(sizeArgs[0], XtNwidth, &intended.width); - XtGetValues(TopButtonBox, sizeArgs, 1); - XtQueryGeometry(TopButtonBox, &intended, &return_geometry); - XtSetArg(sizeArgs[0], XtNheight, return_geometry.height); - XtSetValues(TopButtonBox, sizeArgs, 1); - /* Let's do a similar gross hack for the bottom button box */ - XtSetArg(sizeArgs[0], XtNwidth, &intended.width); - XtGetValues(BottomButtonBox, sizeArgs, 1); - XtQueryGeometry(BottomButtonBox, &intended, &return_geometry); - XtSetArg(sizeArgs[0], XtNheight, return_geometry.height); - XtSetValues(BottomButtonBox, sizeArgs, 1); -#endif XtRealizeWidget(TopLevel); XRNState |= XRN_X_UP; xthWaitForMapped(TopLevel, False); +#ifdef MOTIF + XmUpdateDisplay(TopLevel); +#endif /* initialize the news system, read the newsrc file */ initializeNews(); XRNState |= XRN_NEWS_UP; /* set up the text window, mode buttons, and question */ - determineMode(); + determineMode(True); xrnUnbusyCursor(); addTimeOut(); @@ -201,7 +158,11 @@ XtAppAddInput(TopContext, inchannel, (XtPointer) XtInputReadMask, processMessage, (XtPointer) 0); +#if XtSpecificationRelease < 6 + MyMainLoop(TopContext); +#else XtAppMainLoop(TopContext); +#endif exit(0); } @@ -248,3 +209,30 @@ } #endif +#if XtSpecificationRelease < 6 +static XEvent last_event; + +XEvent *XtLastEventProcessed(display) + Display *display; +{ + return &last_event; +} + +void MyMainLoop(app) + XtAppContext app; +{ + XEvent event; + + for (;;) { + XtAppNextEvent(app, &event); + MyDispatchEvent(&event); + } +} + +Boolean MyDispatchEvent(event) + XEvent *event; +{ + last_event = *event; + return XtDispatchEvent(event); +} +#endif /* XtSpecificationRelease < 6 */ diff -u -d -r -N -P 8.02/xrn.h 9.00/xrn.h --- 8.02/xrn.h Sat Oct 28 14:39:51 1995 +++ 9.00/xrn.h Sun Jun 29 22:52:46 1997 @@ -1,8 +1,10 @@ #ifndef XRN_H #define XRN_H +#include "file_cache.h" + /* - * $Id: xrn.h,v 1.26 1995/10/28 18:39:19 jik Exp $ + * $Id: xrn.h,v 1.30 1997/06/30 02:52:46 jik Exp $ */ /* @@ -41,7 +43,6 @@ extern Widget TopLevel; extern XtAppContext TopContext; -extern Widget Frame; extern Widget TopInfoLine; /* top button info line */ extern Widget BottomInfoLine; /* bottom button info line */ @@ -49,10 +50,20 @@ extern int inCommand, inSubCommand; /* executing a button function */ +extern file_cache FileCache; + #define XRN_X_UP 0x01 #define XRN_NEWS_UP 0x10 #define LABEL_SIZE 128 #define HOST_NAME_SIZE 1024 + +#if XtSpecificationRelease < 6 +extern XEvent *XtLastEventProcessed _ARGUMENTS((Display *)); +extern void MyMainLoop _ARGUMENTS((XtAppContext)); +extern Boolean MyDispatchEvent _ARGUMENTS((XEvent *)); +#else +#define MyDispatchEvent(ev) XtDispatchEvent(ev) +#endif #endif /* XRN_H */ diff -u -d -r -N -P 8.02/xthelper.c 9.00/xthelper.c --- 8.02/xthelper.c Wed Oct 4 16:36:23 1995 +++ 9.00/xthelper.c Thu Jun 5 07:11:43 1997 @@ -1,6 +1,6 @@ #if !defined(lint) && !defined(SABER) && !defined(GCC_WALL) -static char XRNrcsid[] = "$Id: xthelper.c,v 1.16 1995/10/04 20:36:23 jik Exp $"; +static char XRNrcsid[] = "$Id: xthelper.c,v 1.19 1997/04/07 02:07:46 jik Exp $"; #endif /* @@ -120,7 +120,7 @@ } fprintf(stderr, "xthHandleAllPendingEvents: %s event\n", type); #endif - XtDispatchEvent(&ev); + MyDispatchEvent(&ev); } return; } @@ -139,7 +139,7 @@ return; default: XtAppNextEvent(TopContext, &ev); - XtDispatchEvent(&ev); + MyDispatchEvent(&ev); } } return; @@ -167,9 +167,12 @@ return; } -void xthWaitForMapped(w, expose_too) - Widget w; - Boolean expose_too; +void xthWaitForMapped( + _ANSIDECL(Widget, w), + _ANSIDECL(Boolean, expose_too) + ) + _KNRDECL(Widget, w) + _KNRDECL(Boolean, expose_too) { Boolean mapped = False, exposed = False; Status ret; @@ -181,9 +184,11 @@ #endif hints = XGetWMHints(XtDisplay(w), XtWindow(w)); - if (hints && (hints->initial_state != NormalState)) + if (hints) { + if (hints->initial_state != NormalState) mapped = True; - XFree((void *) hints); + XFree((void *) hints); + } if (mapped && !expose_too) return; @@ -214,7 +219,7 @@ } fprintf(stderr, "xthWaitForMapped: %s event\n", type); #endif - XtDispatchEvent(&ev); + MyDispatchEvent(&ev); } done: diff -u -d -r -N -P 8.02/xthelper.h 9.00/xthelper.h --- 8.02/xthelper.h Tue Sep 5 14:33:16 1995 +++ 9.00/xthelper.h Thu Jun 5 07:11:43 1997 @@ -2,7 +2,7 @@ #define XTHELPER_H /* - * $Id: xthelper.h,v 1.5 1995/09/05 18:33:11 jik Exp $ + * $Id: xthelper.h,v 1.6 1997/01/12 03:41:22 jik Exp $ */ /* @@ -39,6 +39,6 @@ void xthCenterWidget _ARGUMENTS((Widget, int, int)); void xthHandleAllPendingEvents _ARGUMENTS((void)); void xthHandlePendingExposeEvents _ARGUMENTS((void)); -void xthWaitForMapped _ARGUMENTS((Widget, /* Boolean */ int)); +void xthWaitForMapped _ARGUMENTS((Widget, Boolean)); #endif /* XTHELPER_H */