python update, 1 of 2

greg andruk meowing at banet.net
Thu Sep 23 03:41:32 UTC 1999


python filter updates for the innd directory.  Doc and sample changes to
follow.

Summary of changes:

- malloc, strlen, copies eliminated where practical.

- Method/function lookups are expensive so we cache them at load time. 
  Interned key strings speed up building the header dictionary.

- All headers now go into the dictionary and missing ones get assigned None,
  so that filters can use a cheap 'is not None' instead of has_key().

- C function names and calls tweaked a bit to be more consistent with what
  the perl thingy is currently doing.  Message ID and article filters now
  get measured by the timer.

- The filter_before_reload() method is there now, for real.  Also added a
  filter_close() method which runs on ctlinnd shutdown.

=-=-=-=-=
diff -cr ../dist/innd/art.c ./innd/art.c
*** ../dist/innd/art.c	Sat Sep 18 03:20:47 1999
--- ./innd/art.c	Wed Sep 22 23:15:00 1999
***************
*** 145,156 ****
  
  ARTHEADER	*ARTheadersENDOF = ENDOF(ARTheaders);
  
! #if defined(DO_PERL)
  const char	*filterPath;
! #endif /* DO_PERL */
! #if defined(DO_PYTHON)
! const char	*pathForPython;
! #endif /* DO_PYTHON */
  
  
  
--- 145,153 ----
  
  ARTHEADER	*ARTheadersENDOF = ENDOF(ARTheaders);
  
! #if defined(DO_PERL) || defined(DO_PYTHON)
  const char	*filterPath;
! #endif /* DO_PERL || DO_PYTHON */
  
  
  
***************
*** 2005,2015 ****
          }
      }
  
  #if defined(DO_PYTHON)
!     pathForPython = HeaderFindMem(article->Data, article->Used, "Path", 4) ;
!     /* TMRstart(TMR_PYTHON); */
!     filterrc = (char *)PYHandleArticle(Data.Body, Data.LinesValue);
!     /* TMRstop(TMR_PYTHON); */
      if (filterrc != NULL) {
          (void)sprintf(buff, "%d %s", NNTP_REJECTIT_VAL, filterrc);
          syslog(L_NOTICE, "rejecting[python] %s %s", Data.MessageID, buff);
--- 2002,2017 ----
          }
      }
  
+ #if defined(DO_PERL) || defined(DO_PYTHON)
+     filterPath = HeaderFindMem(article->Data, article->Used, "Path", 4) ;
+ #endif /* DO_PERL || DO_PYHTON */
+ 
  #if defined(DO_PYTHON)
!     TMRstart(TMR_PYTHON);
!     filterrc = PYartfilter(Data.Body,
! 			   &article->Data[article->Used] - Data.Body,
! 			   Data.LinesValue);
!     TMRstop(TMR_PYTHON);
      if (filterrc != NULL) {
          (void)sprintf(buff, "%d %s", NNTP_REJECTIT_VAL, filterrc);
          syslog(L_NOTICE, "rejecting[python] %s %s", Data.MessageID, buff);
***************
*** 2026,2032 ****
      /* I suppose some masochist will run with Python and Perl in together */
  
  #if defined(DO_PERL)
-     filterPath = HeaderFindMem(article->Data, article->Used, "Path", 4) ;
      TMRstart(TMR_PERL);
      filterrc = PLartfilter(Data.Body, Data.LinesValue);
      TMRstop(TMR_PERL);
--- 2028,2033 ----
diff -cr ../dist/innd/cc.c ./innd/cc.c
*** ../dist/innd/cc.c	Sat Sep 18 03:20:47 1999
--- ./innd/cc.c	Sat Sep 18 16:29:14 1999
***************
*** 678,684 ****
      char	*av[];
  {
      char	*p;
!     extern int	PythonFilterActive;
  
      switch (av[0][0]) {
      default:
--- 678,684 ----
      char	*av[];
  {
      char	*p;
!     extern BOOL	PythonFilterActive;
  
      switch (av[0][0]) {
      default:
***************
*** 777,783 ****
      PLmode(Mode, OMrunning, p);
  #endif /* defined(DO_PERL) */
  #if defined(DO_PYTHON)
!     PythonMode(Mode, OMrunning, p);
  #endif /* defined(DO_PYTHON) */
      
      DISPOSE(ModeReason);
--- 777,783 ----
      PLmode(Mode, OMrunning, p);
  #endif /* defined(DO_PERL) */
  #if defined(DO_PYTHON)
!     PYmode(Mode, OMrunning, p);
  #endif /* defined(DO_PYTHON) */
      
      DISPOSE(ModeReason);
***************
*** 858,864 ****
      extern int		PerlFilterActive;
  #endif /* defined(DO_PERL) */
  #if defined(DO_PYTHON)
!     extern int		PythonFilterActive;
  #endif /* defined(DO_PYTHON) */
  
      /* nb: We assume here that BUFSIZ is >= 512, and that none of
--- 858,864 ----
      extern int		PerlFilterActive;
  #endif /* defined(DO_PERL) */
  #if defined(DO_PYTHON)
!     extern BOOL		PythonFilterActive;
  #endif /* defined(DO_PYTHON) */
  
      /* nb: We assume here that BUFSIZ is >= 512, and that none of
***************
*** 1259,1265 ****
      PLmode(Mode, NewMode, reason);
  #endif /* defined(DO_PERL) */
  #if defined(DO_PYTHON)
!     PythonMode(Mode, NewMode, reason);
  #endif /* defined(DO_PYTHON) */
  
      ICDwrite();
--- 1259,1265 ----
      PLmode(Mode, NewMode, reason);
  #endif /* defined(DO_PERL) */
  #if defined(DO_PYTHON)
!     PYmode(Mode, NewMode, reason);
  #endif /* defined(DO_PYTHON) */
  
      ICDwrite();
diff -cr ../dist/innd/innd.c ./innd/innd.c
*** ../dist/innd/innd.c	Sat Sep 18 03:20:48 1999
--- ./innd/innd.c	Sat Sep 18 16:30:19 1999
***************
*** 923,929 ****
  #endif /* defined(DO_PERL) */
  
  #if defined(DO_PYTHON)
!     PYSetup();
  #endif /* (DO_PYTHON) */
   
      /* And away we go... */
--- 923,929 ----
  #endif /* defined(DO_PERL) */
  
  #if defined(DO_PYTHON)
!     PYsetup();
  #endif /* (DO_PYTHON) */
   
      /* And away we go... */
diff -cr ../dist/innd/innd.h ./innd/innd.h
*** ../dist/innd/innd.h	Sat Sep 18 03:20:48 1999
--- ./innd/innd.h	Sun Sep 19 01:48:58 1999
***************
*** 367,373 ****
  
  typedef enum {TMR_IDLE, TMR_ARTWRITE, TMR_ARTLINK, TMR_HISWRITE,
  	      TMR_HISSYNC, TMR_SITESEND, TMR_ARTCTRL, TMR_ARTCNCL,
! 	      TMR_HISHAVE, TMR_HISGREP, TMR_PERL, TMR_OVERV, TMR_MAX} TMRTYPE;
  
  
  
--- 367,374 ----
  
  typedef enum {TMR_IDLE, TMR_ARTWRITE, TMR_ARTLINK, TMR_HISWRITE,
  	      TMR_HISSYNC, TMR_SITESEND, TMR_ARTCTRL, TMR_ARTCNCL,
! 	      TMR_HISHAVE, TMR_HISGREP, TMR_PERL, TMR_OVERV,
! 	      TMR_PYTHON, TMR_MAX} TMRTYPE;
  
  
  
***************
*** 657,666 ****
  
  /* Python functions */
  extern void		PYfilter(BOOL value);
! extern int		PYReadFilter(void);
! extern char		*PYHandleArticle(char *artBody, int lines);
! extern char		*PYHandleMessageID(char *messageID);
! extern void		PythonMode(OPERATINGMODE Mode, OPERATINGMODE newmode, char *reason);
! extern void		PYSetup(void);
  extern void		PYclose(void);
  # endif /* defined(DO_PYTHON) */
--- 658,668 ----
  
  /* Python functions */
  extern void		PYfilter(BOOL value);
! extern int		PYreadfilter(void);
! extern char		*PYartfilter(char *artBody, long artLen, int lines);
! extern char		*PYmidfilter(char *messageID, int msglen);
! extern void		PYmode(OPERATINGMODE Mode, OPERATINGMODE newmode,
! 			       char *reason);
! extern void		PYsetup(void);
  extern void		PYclose(void);
  # endif /* defined(DO_PYTHON) */
diff -cr ../dist/innd/nc.c ./innd/nc.c
*** ../dist/innd/nc.c	Sat Sep 18 03:20:48 1999
--- ./innd/nc.c	Tue Sep 21 23:04:46 1999
***************
*** 483,498 ****
  
  #if defined(DO_PYTHON)
      /*  invoke a Python message filter on the message id */
!     if ((filterrc = (char *)PYHandleMessageID(p)) != NULL) {
  	cp->Refused++;
! 	msglen = strlen(p) + 5; /* 3 digits + space + id + null */
  	if (cp->Sendid.Size < msglen) {
! 	    if (cp->Sendid.Size > 0) DISPOSE(cp->Sendid.Data);
! 	    if (msglen > MAXHEADERSIZE) cp->Sendid.Size = msglen;
! 	    else cp->Sendid.Size = MAXHEADERSIZE;
  	    cp->Sendid.Data = NEW(char, cp->Sendid.Size);
  	}
! 	(void)sprintf(cp->Sendid.Data, "%d %s", NNTP_HAVEIT_VAL, filterrc);
  	NCwritereply(cp, cp->Sendid.Data);
  	DISPOSE(cp->Sendid.Data);
  	cp->Sendid.Size = 0;
--- 483,505 ----
  
  #if defined(DO_PYTHON)
      /*  invoke a Python message filter on the message id */
!     msglen = strlen(p);
!     TMRstart(TMR_PYTHON);
!     filterrc = PYmidfilter(p, msglen);
!     TMRstop(TMR_PYTHON);
!     if (filterrc) {
  	cp->Refused++;
! 	msglen += 5; /* 3 digits + space + id + null */
  	if (cp->Sendid.Size < msglen) {
! 	    if (cp->Sendid.Size > 0)
! 		DISPOSE(cp->Sendid.Data);
! 	    if (msglen > MAXHEADERSIZE)
! 		cp->Sendid.Size = msglen;
! 	    else
! 		cp->Sendid.Size = MAXHEADERSIZE;
  	    cp->Sendid.Data = NEW(char, cp->Sendid.Size);
  	}
! 	sprintf(cp->Sendid.Data, "%d %s", NNTP_HAVEIT_VAL, filterrc);
  	NCwritereply(cp, cp->Sendid.Data);
  	DISPOSE(cp->Sendid.Data);
  	cp->Sendid.Size = 0;
***************
*** 1275,1281 ****
  NCcheck(CHANNEL *cp)
  {
      char		*p;
!     int			msglen;
  #if defined(DO_PERL) || defined(DO_PYTHON)
      char		*filterrc;
  #endif /* DO_PERL || DO_PYTHON */
--- 1282,1288 ----
  NCcheck(CHANNEL *cp)
  {
      char		*p;
!     int			idlen, msglen;
  #if defined(DO_PERL) || defined(DO_PYTHON)
      char		*filterrc;
  #endif /* DO_PERL || DO_PYTHON */
***************
*** 1286,1292 ****
  	continue;
      for ( ; ISWHITE(*p); p++)
  	continue;
!     msglen = strlen(p) + 5; /* 3 digits + space + id + null */
      if (cp->Sendid.Size < msglen) {
  	if (cp->Sendid.Size > 0) DISPOSE(cp->Sendid.Data);
  	if (msglen > MAXHEADERSIZE) cp->Sendid.Size = msglen;
--- 1293,1300 ----
  	continue;
      for ( ; ISWHITE(*p); p++)
  	continue;
!     idlen = strlen(p);
!     msglen = idlen + 5; /* 3 digits + space + id + null */
      if (cp->Sendid.Size < msglen) {
  	if (cp->Sendid.Size > 0) DISPOSE(cp->Sendid.Data);
  	if (msglen > MAXHEADERSIZE) cp->Sendid.Size = msglen;
***************
*** 1320,1329 ****
  #endif /* defined(DO_PERL) */
  
  #if defined(DO_PYTHON)
!     /*  invoke a perl message filter on the message id */
!     if ((filterrc = (char *)PYHandleMessageID(p)) != NULL) {
  	cp->Refused++;
! 	(void)sprintf(cp->Sendid.Data, "%d %s", NNTP_ERR_GOTID_VAL, p);
  	NCwritereply(cp, cp->Sendid.Data);
  	return;
      }
--- 1328,1338 ----
  #endif /* defined(DO_PERL) */
  
  #if defined(DO_PYTHON)
!     /*  invoke a python message filter on the message id */
!     filterrc = PYmidfilter(p, idlen);
!     if (filterrc) {
  	cp->Refused++;
! 	sprintf(cp->Sendid.Data, "%d %s", NNTP_ERR_GOTID_VAL, p);
  	NCwritereply(cp, cp->Sendid.Data);
  	return;
      }
diff -cr ../dist/innd/python.c ./innd/python.c
*** ../dist/innd/python.c	Sat Sep 18 03:20:48 1999
--- ./innd/python.c	Wed Sep 22 22:34:20 1999
***************
*** 1,12 ****
! /* 
!  * $Id: python.c,v 1.1 1999/05/20 09:22:51 kondou Exp $
!  *
!  * python.c: Embed Python in the style of innd's TCL and Perl stuff.
!  * 
!  * Written by G.J. Andruk <meowing at banet.net> patterned after TCL/Perl
!  * work by Bob Heiney and Christophe Wolfhugel.  Meow.
!  */
! 
  
  #include <stdio.h>
  #include <sys/types.h>
--- 1,16 ----
! /*
! **  $Id: python.c,v 1.1 1999/05/20 09:22:51 kondou Exp $
! **
! **  python.c: Embed Python in the style of innd's TCL and Perl stuff.
! ** 
! **  Written by G.J. Andruk <meowing at banet.net> patterned after
! **  TCL/Perl work by Bob Heiney and Christophe Wolfhugel and a whole
! **  bunch of other people mentioned in the docs and sources for the
! **  other filters.
! **
! **  The astute reader may notice the commission of blatant atrocities
! **  against Python's OO model here.  Don't tell Guido.
! */
  
  #include <stdio.h>
  #include <sys/types.h>
***************
*** 17,213 ****
  #include "art.h"
  #include "qio.h"
  
- #if defined(DO_PYTHON)
  
  
  #include "Python.h"
  
  
! BOOL			PythonFilterActive;
! ARTHEADER		ARTheaders[], *ARTheadersENDOF;
! char			*pathForPython;
! PyObject		*PYFilterObject = NULL;
! PyObject		*PYFilterModule = NULL;
! PyObject		*PYResult;
! PyObject		*PYheaditem[];
! STATIC char		*PYSTARTUP = NULL;
! STATIC BUFFER		PYpythonbuff;
! STATIC FILE		*PYFilterFile;
  
  
  
  void
  PYfilter(value)
      BOOL value;
  {
!     PythonFilterActive=value;
      syslog(L_NOTICE, "%s Python filtering %s", LogName,
  	   PythonFilterActive ? "enabled" : "disabled");
  }
  
  
  
! /* Reject articles we don't like. */
  char *
! PYHandleArticle(artBody, lines)
      char* artBody;
!     int	lines;
  {
      ARTHEADER	*hp;
      int		hdrnum;
      int		i;
      char	*p;
!     static char	buf[256];
!     PyObject	*PYheaders, *tryfunc, *result;
  
!     if (!PythonFilterActive || PYFilterObject == NULL)
  	return NULL;
  
!     /* Fill in the dictionary */
!     PYheaders = PyDict_New();	/* Hmmm. Reuse instead of this business? */
      hdrnum = 0;
      for (hp = ARTheaders; hp < ARTheadersENDOF; hp++) {
! 	if (hp->Found && hp->Value && strcmp (hp->Name,"Path") != 0) {
! 	    PYheaditem[hdrnum] = PyString_FromString(hp->Value);
! 	    PyDict_SetItemString(PYheaders, (char *)hp->Name,
! 				 PYheaditem[hdrnum++]);
! 	}
      }
  
!     /* Store article body */
      if (artBody != NULL) {
! 	PYheaditem[hdrnum] = PyString_FromString(artBody);
! 	PyDict_SetItemString(PYheaders, "__BODY__", PYheaditem[hdrnum++]);
      }
  
! 
!     /* Store article line count */
      PYheaditem[hdrnum] = PyInt_FromLong((long) lines);
!     PyDict_SetItemString(PYheaders, "__LINES__", PYheaditem[hdrnum++]);
  
  
!     if (pathForPython != NULL) {
! 	char *p = strchr (pathForPython,'\n') ;
! 
! 	*p = '\0' ;
! 	PYheaditem[hdrnum] = PyString_FromString(pathForPython);
! 	PyDict_SetItemString(PYheaders, "Path", PYheaditem[hdrnum++]);
! 	*p = '\n' ;
!     }
! 
!     /*
!      * Checking exists/callable for every article may be a little too
!      * paranoid.  Perhaps better done at load/reload time?
!      */
!     if ((tryfunc = PyObject_GetAttrString(PYFilterObject,
! 					  "filter_art")) != NULL) {
! 	if (PyCallable_Check(tryfunc) == 1) {
! 	    if ((result = PyObject_CallFunction(tryfunc,
! 						"O", PYheaders)) != NULL) {
! 		if (PyObject_IsTrue(result)) {
! 		    strncpy(buf, PyString_AsString(result), sizeof(buf) - 1);
! 		    Py_DECREF(result);
! 		} else {
! 		    buf[0] = '\0';
! 		}
! 	    } else {
! 		buf[0] = '\0';
! 	    }
! 	}
! 	Py_DECREF(tryfunc);
!     }
      PyDict_Clear(PYheaders);
-     Py_DECREF(PYheaders);
      for (i = 0; i < hdrnum; i++)
! 	Py_DECREF(PYheaditem[i]);
  
!     if (buf[0] != '\0') 
! 	return buf ;
      return NULL;
  }
  
  
! /* Refuse articles offered thru CHECK or IHAVE that we don't like.  */
  char *
! PYHandleMessageID(messageID)
      char *messageID;
  {
      static char		buf[256];
!     PyObject		*tryfunc, *result;
  
!     if (!PythonFilterActive || PYFilterObject == NULL)
  	return NULL;
  
!     if ((tryfunc = PyObject_GetAttrString(PYFilterObject,
! 					  "filter_messageid")) != NULL) {
! 	if (PyCallable_Check(tryfunc) == 1) {
! 	    if ((result = PyObject_CallFunction(tryfunc,
! 						"s", messageID)) != NULL) {
! 		if (PyObject_IsTrue(result)) {
! 		    strncpy(buf, PyString_AsString(result), sizeof(buf) - 1);
! 		} else {
! 		    buf[0] = '\0';
! 		}
! 		Py_DECREF(result);
! 	    } else {
! 		buf[0] = '\0';
! 	    }
! 	}
! 	Py_DECREF(tryfunc);
!     }
!     if (buf[0] != '\0') 
  	return buf;
      return NULL;
  }
  
  
  
! /* Tell the external module about our state. */
  void
! PythonMode(Mode, NewMode, reason)
      OPERATINGMODE Mode, NewMode;
      char *reason;
  {
!     PyObject	*tryfunc, *filtermode, *result;
      char	oldmode[10], newmode[10];
  
!     if (!PythonFilterActive || PYFilterObject == NULL)
  	return;
  
!     if (Mode == OMrunning)
! 	strcpy(oldmode, "running");
!     if (Mode == OMpaused)
! 	strcpy(oldmode, "paused");
!     if (Mode == OMthrottled)
! 	strcpy(oldmode, "throttled");
! 
!     if (NewMode == OMrunning)
! 	strcpy(newmode, "running");
!     if (NewMode == OMpaused)
! 	strcpy(newmode, "paused");
!     if (NewMode == OMthrottled)
! 	strcpy(newmode, "throttled");
! 
!     if ((tryfunc = PyObject_GetAttrString(PYFilterObject,
! 					  "filter_mode")) != NULL) {
! 	if (PyCallable_Check(tryfunc) == 1) {
! 	    result = PyObject_CallFunction(tryfunc, "sss",
! 					   oldmode, newmode, reason);
! 	    Py_DECREF(result);
! 	}
! 	Py_DECREF(tryfunc);
!     }
  }
  
  
  
! /* Called by the external module so it can register itself with INN. */
  static PyObject *
  PY_set_filter_hook(dummy, args)
      PyObject *dummy, *args;
! {	
!     PyObject *result = NULL;
!     PyObject *temp;
  
      if (PyArg_ParseTuple(args, "O:set_filter_hook", &temp)) {
  	Py_XINCREF(temp);
--- 21,212 ----
  #include "art.h"
  #include "qio.h"
  
  
+ #if defined(DO_PYTHON)
  
  #include "Python.h"
  
  
! BOOL		PythonFilterActive;
! char		*filterPath;	/* this gets set in art.c */
! PyObject	*PYFilterObject = NULL;
! PyObject	*PYFilterModule = NULL;
! 
! /* article filter bits and pieces */
! ARTHEADER	ARTheaders[], *ARTheadersENDOF;
! PyObject	*PYheaders = NULL;
! PyObject	**PYheaditem;
! PyObject	**PYheadkey;
! PyObject	*PYpathkey, *PYlineskey, *PYbodykey;
! 
! /* external functions */
! PyObject	*msgid_method = NULL;
! PyObject	*art_method = NULL;
! PyObject	*mode_method = NULL;
! PyObject	*pre_reload_method = NULL;
! PyObject	*close_method = NULL;
  
  
  
+ /*
+ **  Turn filtering on or off.
+ */
  void
  PYfilter(value)
      BOOL value;
  {
!     PythonFilterActive = value;
      syslog(L_NOTICE, "%s Python filtering %s", LogName,
  	   PythonFilterActive ? "enabled" : "disabled");
  }
  
  
  
! /*
! **  Reject articles we don't like.
! */
  char *
! PYartfilter(artBody, artLen, lines)
      char* artBody;
!     long artLen;
!     int lines;
  {
      ARTHEADER	*hp;
      int		hdrnum;
      int		i;
      char	*p;
!     static char buf[256];
!     PyObject	*result;
  
!     if (!PythonFilterActive || PYFilterObject == NULL || art_method == NULL)
  	return NULL;
  
!     /* Add headers to the dictionary... */
      hdrnum = 0;
      for (hp = ARTheaders; hp < ARTheadersENDOF; hp++) {
! 	if (hp->Found && hp->Value && !EQ(hp->Name,"Path"))
! 	    PYheaditem[hdrnum] = PyBuffer_FromMemory(hp->Value, hp->Length);
! 	else
! 	    PYheaditem[hdrnum] = Py_None;
! 	PyDict_SetItem(PYheaders, PYheadkey[hdrnum], PYheaditem[hdrnum]);
! 	hdrnum++;
!     }
! 
!     /* ...then the path, done separately to get the info innd adds... */
!     if (filterPath != NULL) {
! 	p = strchr(filterPath,'\n');
! 	if (p)
! 	    *p = '\0';
! 	PYheaditem[hdrnum] = PyBuffer_FromMemory(filterPath,
! 						 strlen(filterPath));
! 	PyDict_SetItem(PYheaders, PYpathkey, PYheaditem[hdrnum++]);
! 	if (p)
! 	    *p = '\n';
      }
  
!     /* ...then the body... */
      if (artBody != NULL) {
! 	PYheaditem[hdrnum] = PyBuffer_FromMemory(artBody, artLen);
! 	PyDict_SetItem(PYheaders, PYbodykey, PYheaditem[hdrnum++]);
      }
  
!     /* ...and finally, the line count. */
      PYheaditem[hdrnum] = PyInt_FromLong((long) lines);
!     PyDict_SetItem(PYheaders, PYlineskey, PYheaditem[hdrnum++]);
  
+     /* Now see if the filter likes it. */
+     result = PyObject_CallFunction(art_method, "O", PYheaders);
+     if ((result != NULL) && PyObject_IsTrue(result))
+ 	strncpy(buf, PyString_AS_STRING(result), sizeof(buf) - 1);
+     else
+ 	*buf = '\0';
+     Py_XDECREF(result);
  
!     /* Clean up after ourselves */
      PyDict_Clear(PYheaders);
      for (i = 0; i < hdrnum; i++)
! 	if (PYheaditem[i] != Py_None)
! 	    Py_DECREF(PYheaditem[i]);
  
!     if (*buf != '\0') 
! 	return buf;
      return NULL;
  }
  
  
! 
! /*
! **  Refuse message IDs offered thru CHECK or IHAVE that we don't like.
! */
  char *
! PYmidfilter(messageID, msglen)
      char *messageID;
+     int msglen;
  {
      static char		buf[256];
!     PyObject		*result;
  
!     if (!PythonFilterActive || PYFilterObject == NULL || msgid_method == NULL)
  	return NULL;
  
!     result = PyObject_CallFunction(msgid_method, "s#", messageID, msglen);
!     if ((result != NULL) && PyObject_IsTrue(result))
! 	strncpy(buf,  PyString_AS_STRING(result), sizeof(buf) - 1);
!     else
! 	*buf = '\0';
!     Py_XDECREF(result);
! 
!     if (*buf != '\0') 
  	return buf;
      return NULL;
  }
  
  
  
! /*
! **  Tell the external module about innd's state.
! */
  void
! PYmode(Mode, NewMode, reason)
      OPERATINGMODE Mode, NewMode;
      char *reason;
  {
!     PyObject	*result;
      char	oldmode[10], newmode[10];
  
!     if (!PythonFilterActive || PYFilterObject == NULL || mode_method == NULL)
  	return;
  
!     switch (Mode) {
!     default:		strcpy(oldmode, "unknown");	break;
!     case OMrunning:	strcpy(oldmode, "running");	break;
!     case OMpaused:	strcpy(oldmode, "paused");	break;
!     case OMthrottled:	strcpy(oldmode, "throttled");	break;
!     }
! 
!     switch (NewMode) {
!     default:		strcpy(newmode, "unknown");	break;
!     case OMrunning:	strcpy(newmode, "running");	break;
!     case OMpaused:	strcpy(newmode, "paused");	break;
!     case OMthrottled:	strcpy(newmode, "throttled");	break;
!     }
! 
!     result = PyObject_CallFunction(mode_method, "sss",
! 				   oldmode, newmode, reason);
!     Py_DECREF(result);
  }
  
  
  
! /*
! **  Called by the external module so it can register itself with innd.
! */
  static PyObject *
  PY_set_filter_hook(dummy, args)
      PyObject *dummy, *args;
! {
!     PyObject	*result = NULL;
!     PyObject	*temp;
  
      if (PyArg_ParseTuple(args, "O:set_filter_hook", &temp)) {
  	Py_XINCREF(temp);
***************
*** 221,477 ****
  
  
  
! /* Allows external module to query innd if an ID is in history. */
  static PyObject *
  PY_havehist(self, args)
      PyObject *self, *args;
  {
!     char *msgid;
  
!     if (!PyArg_ParseTuple(args, "s", &msgid))
  	return NULL;
!     if (HIShavearticle(HashMessageID(msgid))) {
! 	return Py_BuildValue("i", 1);
!     } else {
! 	return Py_BuildValue("i", 0);
!     }
  }
  
  
  
! /* External module can locally delete an article. */
  static PyObject *
  PY_cancel(self, args)
      PyObject *self, *args;
  {
      char	*msgid;
      char	*parambuf[2];
  
!     if (!PyArg_ParseTuple(args, "s", &msgid))
  	return NULL;
  
      parambuf[0]= msgid;
      parambuf[1]= 0;
  
!     if (!CCcancel(parambuf)) {
! 	return Py_BuildValue("i", 1);
!     } else {
! 	return Py_BuildValue("i", 0);
!     }
  }
  
  
  
! /* Stuff an ID into history so that it will be refused later. */
  static PyObject *
  PY_addhist(self, args)
      PyObject *self, *args;
  {
!     char*	msgid;
!     char*	arrivaltime;
!     char*	articletime;
!     char*	expiretime;
!     char*	articlepaths = "";
      char	tbuff[12];
!     char*	parambuf[6];
! 
  
!     if (!PyArg_ParseTuple(args, "s", &msgid))
  	return NULL;
  
      sprintf(tbuff, "%d",time((long *)0));
-     arrivaltime = articletime = expiretime = tbuff;
  
      parambuf[0] = msgid;
!     parambuf[1] = arrivaltime;
!     parambuf[2] = articletime;
!     parambuf[3] = expiretime;
      parambuf[4] = articlepaths;
      parambuf[5] = 0;
  
!     if (!CCaddhist(parambuf)) {
! 	return Py_BuildValue("i", 1);
!     } else {
! 	return Py_BuildValue("i", 0);
!     }
  }
  
  
  
! /* Get a newsgroup's status flag (m, y, x, etc.) */
  static PyObject *
  PY_newsgroup(self, args)
      PyObject *self, *args;
  {
!     char*	newsgroup;
!     NEWSGROUP*  ngp;
!     char*	end;
!     char*	rest;
      int		size;
  
!     if (!PyArg_ParseTuple(args, "s", &newsgroup))
  	return NULL;
  
!     if ((ngp = NGfind(newsgroup)) == NULL) {
! 	return Py_BuildValue("s", "");
!     } else {
! 	/* ngp->Rest is newline-terminated; find the end. */
! 	end = strchr(ngp->Rest, '\n');
! 	if (end == NULL) {
! 	    size = strlen(ngp->Rest);
! 	} else {
! 	    size = end - ngp->Rest;
! 	}
! 
! 	if (PYpythonbuff.Data == NULL) {
! 	    PYpythonbuff.Size = SITE_BUFFER_SIZE;
! 	    PYpythonbuff.Data = NEW(char, PYpythonbuff.Size);
! 	}
! 
! 	/* SITE_BUFFER_SIZE should be *huge* for this, but be paranoid. */
! 	if (size > SITE_BUFFER_SIZE + 1)
! 	    size = SITE_BUFFER_SIZE;
! 
! 	strncpy(PYpythonbuff.Data, ngp->Rest, size);
! 	PYpythonbuff.Data[size] = '\0';
! 	return Py_BuildValue("s", PYpythonbuff.Data);
      }
  }
  
  
  
! /* Return an article header to the external module as a string */
  static PyObject *
  PY_head(self, args)
      PyObject *self, *args;
  {
      char	*msgid;
      char	*p;
      char	*q;
      char	*bufptr;
      QIOSTATE	*qp;
  
! 
!     if (!PyArg_ParseTuple(args, "s", &msgid))
  	return NULL;
  
!     /* Get the article filenames; open the first file */
!     if ((q = HISfilesfor(HashMessageID(msgid))) == NULL) {
  	return Py_BuildValue("s", "");	
-     }
-     if ((p = strchr(q, ' ')))
- 	*p = '\0';
  
!     if ((qp = QIOopen(q)) == NULL) {
! 	return Py_BuildValue("s", "");
!     }
  
!     if (PYpythonbuff.Data == NULL) {
! 	PYpythonbuff.Size = SITE_BUFFER_SIZE;
! 	PYpythonbuff.Data = NEW(char, PYpythonbuff.Size);
!     }
!                                         
!     strcpy(PYpythonbuff.Data, "");
  
      for (p = QIOread(qp); (p != NULL) && (*p != '\0'); p = QIOread(qp)) {
! 	if (PYpythonbuff.Size < (qp->Count + 3)) {
! 	    PYpythonbuff.Size += SITE_BUFFER_SIZE;
! 	    RENEW(PYpythonbuff.Data, char, PYpythonbuff.Size);
!         }
! 	strncat(PYpythonbuff.Data, p, QIOlength(qp));
! 	strncat(PYpythonbuff.Data, "\n", 1);
      }
-     QIOclose(qp);
  
!     return Py_BuildValue("s", PYpythonbuff.Data);
  }
  
  
  
! /* Return a whole article to the external module as a string */
  static PyObject *
  PY_article(self, args)
      PyObject *self, *args;
  {
      char	*msgid;
      char	*p;
      char	*q;
      char	*bufptr;
      QIOSTATE	*qp;
  
! 
!     if (!PyArg_ParseTuple(args, "s", &msgid))
  	return NULL;
  
      /* Get the article filenames; open the first file */
!     if ((q = HISfilesfor(HashMessageID(msgid))) == NULL) {
! 	return Py_BuildValue("s", "");	
!     }
      if (p = strchr(q, ' '))
  	*p = '\0';
  
!     if ((qp = QIOopen(q)) == NULL) {
! 	return Py_BuildValue("s", "");
!     }
! 
!     if (PYpythonbuff.Data == NULL) {
! 	PYpythonbuff.Size = SITE_BUFFER_SIZE;
! 	PYpythonbuff.Data = NEW(char, PYpythonbuff.Size);
!     }
!                                         
!     strcpy(PYpythonbuff.Data, "");
  
      for (p = QIOread(qp); (p != NULL); p = QIOread(qp)) {
! 	if (PYpythonbuff.Size < (qp->Count + 3)) {
! 	    PYpythonbuff.Size += SITE_BUFFER_SIZE;
! 	    RENEW(PYpythonbuff.Data, char, PYpythonbuff.Size);
!         }
! 	strncat(PYpythonbuff.Data, p, QIOlength(qp));
! 	strncat(PYpythonbuff.Data, "\n", 1);
      }
      QIOclose(qp);
  
!     return Py_BuildValue("s", PYpythonbuff.Data);
  }
  
  
- /*
-  * Python's syslog module isn't compiled in by default, and it's in
-  * an 'optional' bit of the path.  Doing it this way is easier to
-  * explain.
-  */
  
  static PyObject *
  PY_syslog(self, args)
      PyObject *self, *args;
  {
      char	*loglevel;
      char	*logmsg;
      int		priority;
  
!     if (!PyArg_ParseTuple(args, "ss", &loglevel, &logmsg))
  	return NULL;
  
      switch (*loglevel) {
! 	default:		priority = LOG_NOTICE ;
! 	case 'a': case 'A':	priority = LOG_ALERT ;		break;
! 	case 'c': case 'C':	priority = LOG_CRIT ;		break;
! 	case 'e': case 'E':	priority = LOG_ERR ;		break;
! 	case 'w': case 'W':	priority = LOG_WARNING ;	break;
! 	case 'n': case 'N':	priority = LOG_NOTICE ;		break;
! 	case 'i': case 'I':	priority = LOG_INFO ;		break;
! 	case 'd': case 'D':	priority = LOG_DEBUG ;		break;
      }
      syslog(priority, "python: %s", logmsg);
  
!     return Py_BuildValue("i", 0);
  }
  
  
  
! /* Make the Python methods visible to the outside world */
  static PyMethodDef INNPyMethods[] = {
!     {"set_filter_hook",	PY_set_filter_hook,	METH_VARARGS},
      {"havehist",	PY_havehist,		METH_VARARGS},
      {"addhist",		PY_addhist,		METH_VARARGS},
      {"cancel",		PY_cancel,		METH_VARARGS},
--- 220,489 ----
  
  
  
! /*
! **  Allow external module to ask innd if an ID is in history.
! */
  static PyObject *
  PY_havehist(self, args)
      PyObject *self, *args;
  {
!     char	*msgid;
!     int		msgidlen;
  
!     if (!PyArg_ParseTuple(args, "s#", &msgid, &msgidlen))
  	return NULL;
! 
!     if (HIShavearticle(HashMessageID(msgid)))
! 	return PyInt_FromLong(1);
!     return PyInt_FromLong(0);
  }
  
  
  
! /*
! **  Allow external module to locally delete an article.
! */
  static PyObject *
  PY_cancel(self, args)
      PyObject *self, *args;
  {
      char	*msgid;
+     int		msgidlen;
      char	*parambuf[2];
  
!     if (!PyArg_ParseTuple(args, "s#", &msgid, &msgidlen))
  	return NULL;
  
      parambuf[0]= msgid;
      parambuf[1]= 0;
  
!     if (!CCcancel(parambuf))
! 	return PyInt_FromLong(1);
!     return PyInt_FromLong(0);
  }
  
  
  
! /*
! **  Stuff an ID into history so that it will be refused later.
! */
  static PyObject *
  PY_addhist(self, args)
      PyObject *self, *args;
  {
!     char	*msgid;
!     int		msgidlen;
!     char	*articlepaths = "";
      char	tbuff[12];
!     char	*parambuf[6];
  
!     if (!PyArg_ParseTuple(args, "s#", &msgid, &msgidlen))
  	return NULL;
  
      sprintf(tbuff, "%d",time((long *)0));
  
      parambuf[0] = msgid;
!     parambuf[1] = parambuf[2] = parambuf[3] = tbuff;
      parambuf[4] = articlepaths;
      parambuf[5] = 0;
  
!     if (!CCaddhist(parambuf))
! 	return PyInt_FromLong(1);
!     return PyInt_FromLong(0);
  }
  
  
  
! /*
! **  Get a newsgroup's status flag (j, m, n, x, y, =other.group)
! */
  static PyObject *
  PY_newsgroup(self, args)
      PyObject *self, *args;
  {
!     char	*newsgroup;
!     int		nglen;
!     NEWSGROUP	*ngp;
!     char	*end;
!     char	*rest;
      int		size;
  
!     if (!PyArg_ParseTuple(args, "s#", &newsgroup, &nglen))
  	return NULL;
  
!     ngp = NGfind(newsgroup);
!     if (ngp == NULL)
! 	return PyString_FromStringAndSize(NULL, 0);
! 
!     /* ngp->Rest is newline-terminated; find the end. */
!     end = strchr(ngp->Rest, '\n');
!     if (end == NULL)
! 	size = strlen(ngp->Rest);
!     else
! 	size = end - ngp->Rest;
! 
!     /* If an alias is longer than this, active is probably broken. */
!     if (size > MAXHEADERSIZE) {
! 	syslog(L_ERROR, "too-long flag field in active for %s", newsgroup);
! 	size = MAXHEADERSIZE;
      }
+ 
+     return PyString_FromStringAndSize(ngp->Rest, size);
  }
  
  
  
! /*
! **  Return an article header to the external module as a string.  We
! **  don't use a buffer object here because that would make it harder,
! **  for example, to compare two on-spool articles.
! */
  static PyObject *
  PY_head(self, args)
      PyObject *self, *args;
  {
      char	*msgid;
+     int		msgidlen;
      char	*p;
      char	*q;
      char	*bufptr;
      QIOSTATE	*qp;
+     PyObject	*header;
+     int		headerlen;
+     int		bytesused = 0;
+     char	*headertxt;
  
!     if (!PyArg_ParseTuple(args, "s#", &msgid, &msgidlen))
  	return NULL;
  
!     /* Get the article filenames/token; open the first */
!     if ((q = HISfilesfor(HashMessageID(msgid))) == NULL)
  	return Py_BuildValue("s", "");	
  
!     if ((p = strchr(q, ' ')))	/* probably obsolete in 2.3 */
! 	*p = '\0';
!     if ((qp = QIOopen(q)) == NULL)
! 	return PyString_FromStringAndSize(NULL, 0);
  
!     headerlen = START_BUFF_SIZE;
!     header = PyString_FromStringAndSize(NULL, headerlen);
!     headertxt = PyString_AS_STRING(header);
  
      for (p = QIOread(qp); (p != NULL) && (*p != '\0'); p = QIOread(qp)) {
! 	if (headerlen < (bytesused + qp->Length + 1)) {
! 	    headerlen += GROW_AMOUNT(headerlen);
! 	    _PyString_Resize (&header, headerlen);
! 	    headertxt = PyString_AS_STRING(header);
! 	}
! 	strncpy(&headertxt[bytesused], p, qp->Length);
! 	bytesused += qp->Length;
! 	headertxt[bytesused++] = '\n';
      }
  
!     QIOclose(qp);
!     if (bytesused != headerlen)
! 	_PyString_Resize(&header, bytesused);
!     return header;
  }
  
  
  
! /*
! **  Return a whole article to the external module as a string.
! */
  static PyObject *
  PY_article(self, args)
      PyObject *self, *args;
  {
      char	*msgid;
+     int		msgidlen;
      char	*p;
      char	*q;
      char	*bufptr;
      QIOSTATE	*qp;
+     PyObject	*art;
+     int		artlen;
+     int		bytesused = 0;
+     char	*arttxt;
  
!     if (!PyArg_ParseTuple(args, "s#", &msgid, &msgidlen))
  	return NULL;
  
      /* Get the article filenames; open the first file */
!     if ((q = HISfilesfor(HashMessageID(msgid))) == NULL)
! 	return Py_BuildValue("s", "");
      if (p = strchr(q, ' '))
  	*p = '\0';
+     if ((qp = QIOopen(q)) == NULL)
+ 	return PyString_FromStringAndSize(NULL, 0);
  
!     artlen = START_BUFF_SIZE;
!     art = PyString_FromStringAndSize(NULL, artlen);
!     arttxt = PyString_AS_STRING(art);
  
      for (p = QIOread(qp); (p != NULL); p = QIOread(qp)) {
! 	if (artlen < (bytesused + QIOlength(qp) + 1)) {
! 	    artlen += GROW_AMOUNT(artlen);
! 	    _PyString_Resize (&art, artlen);
! 	    arttxt = PyString_AS_STRING(art);
! 	}
! 	strncpy(&arttxt[bytesused], p, QIOlength(qp));
! 	bytesused += QIOlength(qp);
! 	arttxt[bytesused++] = '\n';
      }
      QIOclose(qp);
  
!     if (bytesused != artlen)
! 	_PyString_Resize(&art, bytesused);
! 
!     return art;
  }
  
  
  
+ /*
+ **  Python's syslog module isn't compiled in by default.  It's easier
+ **  to do it this way, and the switch block looks pretty in a color
+ **  editor).
+ */
  static PyObject *
  PY_syslog(self, args)
      PyObject *self, *args;
  {
      char	*loglevel;
+     int		levellen;
      char	*logmsg;
+     int		msglen;
      int		priority;
  
!     if (!PyArg_ParseTuple(args, "s#s#",
! 			  &loglevel, &levellen, &logmsg, &msglen))
  	return NULL;
  
      switch (*loglevel) {
!     default:		priority = LOG_NOTICE ;
!     case 'd': case 'D': priority = LOG_DEBUG ;		break;
!     case 'i': case 'I': priority = LOG_INFO ;		break;
!     case 'n': case 'N': priority = LOG_NOTICE ;		break;
!     case 'w': case 'W': priority = LOG_WARNING ;	break;
!     case 'e': case 'E': priority = LOG_ERR ;		break;
!     case 'c': case 'C': priority = LOG_CRIT ;		break;
!     case 'a': case 'A': priority = LOG_ALERT ;		break;
      }
+ 
      syslog(priority, "python: %s", logmsg);
  
!     Py_INCREF(Py_None);
!     return Py_None;
  }
  
  
  
! /*
! **  Make the internal INN module's functions visible to Python.
! */
  static PyMethodDef INNPyMethods[] = {
!     {"set_filter_hook", PY_set_filter_hook,	METH_VARARGS},
      {"havehist",	PY_havehist,		METH_VARARGS},
      {"addhist",		PY_addhist,		METH_VARARGS},
      {"cancel",		PY_cancel,		METH_VARARGS},
***************
*** 484,523 ****
  
  
  
! /* used by ctlinnd reload */
  int
  PYreadfilter(void)
  {
-     PyObject   	*tryfunc, *result;
      PyObject	*newmodule = NULL;
  
      if (!Py_IsInitialized()) {
! 	syslog(L_ERROR, "Python is not initialized");
  	return 0;
      }
      if ((newmodule = PyImport_ReloadModule(PYFilterModule)) == NULL) {
! 	syslog(L_ERROR, "Could not reload the Python filter module");
!     } else {
! 	Py_DECREF(PYFilterModule);
! 	PYFilterModule = newmodule;
      }
      if (PYFilterObject == NULL) {
! 	syslog(L_ERROR, "Python reload: filter object is not defined");
  	PYfilter(FALSE);
  	return 0;
      }
- 
      PYfilter(TRUE);
      return 1;
  }
  
  
  
! /* Called when innd first starts */
  void
! PYSetup(void)
  {
!     char	*modulename;
  
      setenv("PYTHONPATH", innconf->pathfilter, 1);
      Py_Initialize();
--- 496,602 ----
  
  
  
! /*
! **  This runs when innd shuts down.
! */
! void
! PYclose(void)
! {
!     PyObject	*result;
! 
!     if (close_method != NULL) {
! 	result = PyObject_CallFunction(close_method, NULL);
! 	Py_XDECREF(result);
!     }
! }
! 
! 
! 
! /*
! **  Check that a method exists and is callable.	 Set a pointer to
! **  the corresponding PyObject, or NULL if not found.
! */
! void
! PYdefonemethod(methptr, methname)
!     PyObject	**methptr;
!     char	*methname;
! {
!     Py_XDECREF(*methptr);
!     *methptr = PyObject_GetAttrString(PYFilterObject, methname);
!     if (*methptr == NULL)
! 	syslog(L_NOTICE, "python method %s not found", methname);
!     else if (PyCallable_Check(*methptr) == 0) {
! 	syslog(L_ERROR, "python object %s found but not a function", methname);
! 	Py_DECREF(*methptr);
! 	*methptr = NULL;
!     }
! }
! 
! 
! 
! /*
! **  Look up the filter methods, so we will know what's available when
! **  innd wants to call them.
! */
! void
! PYdefmethods(void)
! {
!     PYdefonemethod(&msgid_method, "filter_messageid");
!     PYdefonemethod(&art_method, "filter_art");
!     PYdefonemethod(&mode_method, "filter_mode");
!     PYdefonemethod(&pre_reload_method, "filter_before_reload");
!     PYdefonemethod(&close_method, "filter_close");
! }
! 
! 
! 
! /*
! **  Used by `ctlinnd reload filter.python'
! */
  int
  PYreadfilter(void)
  {
      PyObject	*newmodule = NULL;
+     PyObject	*result;
  
      if (!Py_IsInitialized()) {
! 	syslog(L_ERROR, "python is not initialized");
  	return 0;
      }
+ 
+     /* If there is a filter running, let it clean up first. */
+     if (pre_reload_method != NULL) {
+ 	result = PyObject_CallFunction(pre_reload_method, NULL);
+ 	Py_XDECREF(result);
+     }
+ 
      if ((newmodule = PyImport_ReloadModule(PYFilterModule)) == NULL) {
! 	syslog(L_ERROR, "cant reload python filter module");
! 	PYfilter(FALSE);
! 	return 0;
      }
+     Py_DECREF(PYFilterModule);
+     PYFilterModule = newmodule;
      if (PYFilterObject == NULL) {
! 	syslog(L_ERROR, "python reload error, filter object not defined");
  	PYfilter(FALSE);
  	return 0;
      }
      PYfilter(TRUE);
+     PYdefmethods();
      return 1;
  }
  
  
  
! /*
! **  Called when innd first starts -- this gets the filters hooked in.
! */
  void
! PYsetup(void)
  {
!     ARTHEADER	*hp;
!     int		hdrindex;
  
      setenv("PYTHONPATH", innconf->pathfilter, 1);
      Py_Initialize();
***************
*** 525,559 ****
      /* It makes Python sad when its stdout and stderr are closed. */
      if ((fileno(stdout) == -1) || (fileno(stderr) == -1))
  	PyRun_SimpleString
! 	    ("import sys; sys.stdout = sys.stderr = open('/dev/null', 'a')");
  
!     if (Py_IsInitialized ()) {
! 	syslog(L_NOTICE, "Python initialized OK");
!     } else {
! 	syslog(L_ERROR, "Python is not initialized");
  	return;
      }
      (void) Py_InitModule("INN", INNPyMethods);
!     strcpy(modulename, _PATH_PYTHON_STARTUP_M);
!     PYFilterModule = PyImport_ImportModule(modulename);
!     if (PYFilterModule == NULL) {
! 	syslog(L_ERROR, "Python module failed to load");
!     }
      if (PYFilterObject == NULL) {
! 	syslog(L_ERROR, "Python filter object is not defined");
  	PYfilter(FALSE);
      } else {
  	PYfilter(TRUE);
      }
- }
  
  
! 
! /* Maybe some day I'll even make this do something. */
! void
! PYclose(void)
! {
  }
  
! 
! #endif /* (DO_PYTHON) */
--- 604,643 ----
      /* It makes Python sad when its stdout and stderr are closed. */
      if ((fileno(stdout) == -1) || (fileno(stderr) == -1))
  	PyRun_SimpleString
! 	    ("import sys; sys.stdout=sys.stderr=open('/dev/null', 'a')");
  
!     if (!Py_IsInitialized ()) {
! 	syslog(L_ERROR, "python interpreter NOT initialized");
  	return;
      }
+     syslog(L_NOTICE, "python interpreter initialized OK");
+ 
      (void) Py_InitModule("INN", INNPyMethods);
! 
!     PYFilterModule = PyImport_ImportModule(_PATH_PYTHON_STARTUP_M);
!     if (PYFilterModule == NULL)
! 	syslog(L_ERROR, "failed to import external python module");
! 
      if (PYFilterObject == NULL) {
! 	syslog(L_ERROR, "python filter object is not defined");
  	PYfilter(FALSE);
      } else {
  	PYfilter(TRUE);
+ 	PYdefmethods();
+ 	syslog(L_NOTICE, "defined python methods");
      }
  
+     /* Grab space for these so we aren't forever recreating them. */
+     PYheaders = PyDict_New();
+     PYheaditem = NEW(PyObject *, ARTheadersENDOF - ARTheaders);
+     PYheadkey = NEW(PyObject *, ARTheadersENDOF - ARTheaders);
  
!     /* Preallocate keys for the article dictionary */
!     for (hp = ARTheaders; hp < ARTheadersENDOF; hp++)
! 	PYheadkey[hp - ARTheaders] = PyString_InternFromString(hp->Name);
!     PYpathkey = PyString_InternFromString("Path");
!     PYlineskey = PyString_InternFromString("__LINES__");
!     PYbodykey = PyString_InternFromString("__BODY__");
  }
  
! #endif /* defined(DO_PYTHON) */
diff -cr ../dist/innd/timer.c ./innd/timer.c
*** ../dist/innd/timer.c	Sat Sep 18 03:20:48 1999
--- ./innd/timer.c	Sun Sep 19 01:47:58 1999
***************
*** 105,110 ****
--- 105,111 ----
  	case TMR_HISGREP:	str = "hisgrep";  break;
  	case TMR_OVERV:		str = "overv";  break;
  	case TMR_PERL:		str = "perl";  break;
+ 	case TMR_PYTHON:	str = "python";  break;
  	}
  	sprintf(buf, "%s %d(%d) ", str, cumulative[i], count[i]);
  	cumulative[i] = count[i] = 0;



More information about the inn-patches mailing list