diff --git a/refdoc/chapters/special_functions.xml b/refdoc/chapters/special_functions.xml
index 5c421eb6f7729058bc31ac202016ee9575115146..9272cc268ecfed4cfd11d41dcfa58e9284374669 100644
--- a/refdoc/chapters/special_functions.xml
+++ b/refdoc/chapters/special_functions.xml
@@ -51,6 +51,7 @@ to the following table.
  Note that sets that includes the character - must have it first in the brackets to avoid having a range defined.</c></r>
 <r><c>%{format%}</c><c>Repeatedly matches 'format' as many times as possible and assigns an array of arrays with the results to the lvalue.</c></r>
 <r><c>%%</c><c>Match a single percent character.</c></r>
+<r><c>%O</c><c>Match a Pike constant, such as string or integer (currently only integer, string and character constant is functional)</c></r>
 </matrix>
 
 <p>If a * is put between the percent and the operator, the operator
diff --git a/src/opcodes.c b/src/opcodes.c
index c36606ccf5425bcbcf54946cabdb5a6f5a1b84c1..78e29c1ade1c9742260468a74cbe7cf71abe2bfa 100644
--- a/src/opcodes.c
+++ b/src/opcodes.c
@@ -2,7 +2,7 @@
 || This file is part of Pike. For copyright information see COPYRIGHT.
 || Pike is distributed under GPL, LGPL and MPL. See the file COPYING
 || for more information.
-|| $Id: opcodes.c,v 1.136 2003/02/10 13:59:59 grubba Exp $
+|| $Id: opcodes.c,v 1.137 2003/02/11 15:22:28 mirar Exp $
 */
 
 #include "global.h"
@@ -30,7 +30,7 @@
 
 #define sp Pike_sp
 
-RCSID("$Id: opcodes.c,v 1.136 2003/02/10 13:59:59 grubba Exp $");
+RCSID("$Id: opcodes.c,v 1.137 2003/02/11 15:22:28 mirar Exp $");
 
 void index_no_free(struct svalue *to,struct svalue *what,struct svalue *ind)
 {
@@ -859,6 +859,224 @@ PMOD_EXPORT void f_cast(void)
 }
 
 
+/* 
+ * helper functions for sscanf %O
+ */
+
+static PMOD_EXPORT int pcharp_extract_char_const(PCHARP str,
+						 PCHARP *dstr,
+						 ptrdiff_t maxlength,
+						 ptrdiff_t *dmaxlength)
+{
+   int c;
+
+   *dstr=str;
+   maxlength--;  
+   *dmaxlength=maxlength;
+
+   switch (c=EXTRACT_PCHARP(str))
+   {
+      case 0:
+	 *dmaxlength=maxlength+1;
+	 *dstr=str;
+	 return 0;
+      case '\\':
+	 return '\\';
+
+   /* use of macros to keep similar to lexer.h: char_const */
+#define LOOK() (maxlength?INDEX_PCHARP(str,1):0)
+#define GETC() (INC_PCHARP(str,1),INDEX_PCHARP(str,0))
+      
+      case '0': case '1': case '2': case '3':
+      case '4': case '5': case '6': case '7':
+	 c-='0';
+	 while(LOOK()>='0' && LOOK()<='8')
+	    c=c*8+(GETC()-'0');
+	 break;
+      
+      case 'a': return 7;       /* BEL */
+      case 'b': return 8;       /* BS */
+      case 't': return 9;       /* HT */
+      case 'n': return 10;      /* LF */
+      case 'v': return 11;      /* VT */
+      case 'f': return 12;      /* FF */
+      case 'r': return 13;      /* CR */
+      case 'e': return 27;      /* ESC */
+      
+      case '\n': return '\n';
+      
+      case 'x':
+	 c=0;
+	 while(1)
+	 {
+	    switch(LOOK())
+	    {
+	       case '0': case '1': case '2': case '3':
+	       case '4': case '5': case '6': case '7':
+	       case '8': case '9':
+		  c=c*16+GETC()-'0';
+		  continue;
+	    
+	       case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+		  c=c*16+GETC()-'a'+10;
+		  continue;
+	    
+	       case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+		  c=c*16+GETC()-'A'+10;
+		  continue;
+	    }
+	    break;
+	 }
+	 break;
+
+      case 'd':
+	 c=0;
+	 while(1)
+	 {
+	    switch(LOOK())
+	    {
+	       case '0': case '1': case '2': case '3':
+	       case '4': case '5': case '6': case '7':
+	       case '8': case '9':
+		  c=c*10+GETC()-'0';
+		  continue;
+	    }
+	    break;
+	 }
+	 break;
+#undef LOOK
+#undef GETC
+   }
+   *dmaxlength=maxlength;
+   *dstr=str;
+   return c;
+}
+
+static PMOD_EXPORT int pcharp_to_svalue_procento(struct svalue *r,
+						 PCHARP str,
+						 PCHARP *dstr,
+						 ptrdiff_t maxlength)
+{
+   *dstr=str; /* default: no hit */
+
+   maxlength--;  
+/* calling convention: max_length <= 0 means no max length. */
+
+   for (;;)
+   {
+      switch (EXTRACT_PCHARP(str))
+      {
+	 case ' ':  /* whitespace */
+	 case '\t':
+	 case '\n':
+	 case '\r':  
+	    break;
+
+	 case '0': case '1': case '2': case '3': case '4':
+	 case '5': case '6': case '7': case '8': case '9':
+            /* fixme: grok floats */
+	    return pcharp_to_svalue_inumber(r,str,dstr,0,maxlength+1);
+
+	 case '\"':
+	 {
+	    struct string_builder tmp;
+	    init_string_builder(&tmp,0);
+	    for (;;)
+	    {
+	       PCHARP start;
+	       int len=0;
+	       INC_PCHARP(str,1);
+	       start=str;
+	       for (;;)
+	       {
+		  if (!maxlength) 
+		  {
+		     free_string_builder(&tmp);
+		     return 0; /* end of data */
+		  }
+		  maxlength--;
+		  switch (EXTRACT_PCHARP(str))
+		  {
+		     case '\"':
+		     case 0:
+		     case '\\':
+			break;
+		     default:
+			len++;
+			INC_PCHARP(str,1);
+			continue;
+		  }
+		  break;
+	       }
+	       if (len) string_builder_append(&tmp, start, len);
+
+	       switch (EXTRACT_PCHARP(str))
+	       {
+		  case '\"':
+		     INC_PCHARP(str,1);
+		     *dstr=str;
+		     r->type=T_STRING;
+		     r->subtype=0;
+		     r->u.string=finish_string_builder(&tmp);
+		     return 1;
+		  case 0: /* abort on NUL */
+		     free_string_builder(&tmp);
+		     return 0;
+		  case '\\':
+		     if (maxlength)
+		     {
+			INC_PCHARP(str,1);
+		     
+			string_builder_putchar(
+			   &tmp,
+			   pcharp_extract_char_const(str,&str,
+						     maxlength+1,&maxlength));
+			break;
+		     }
+		     break;
+	       }
+	    }
+
+	    case '\'':
+	       if (maxlength<0 ||
+		   maxlength>2)
+	       {
+		  r->subtype=0;
+		  r->type=T_INT;
+
+		  INC_PCHARP(str,1);
+		  r->u.integer=EXTRACT_PCHARP(str);
+		  INC_PCHARP(str,1);
+		  if (r->u.integer=='\\')
+		  {
+		     r->u.integer=
+			pcharp_extract_char_const(str,&str,
+						  maxlength+1,&maxlength);
+		     INC_PCHARP(str,1); 
+		     maxlength--;
+		  }
+		  if (!maxlength || EXTRACT_PCHARP(str)!='\'')
+		     return 0;
+		  INC_PCHARP(str,1); /* skip that ending quote */
+
+		  *dstr=str;
+		  return 1;
+	       }
+	       return 0;
+	    
+      /* fixme: arrays, multisets, mappings */
+	 }
+
+	 default:
+            /* unknown */
+	    return 0;
+      }
+      if (!maxlength) return 0; /* end of data */
+      INC_PCHARP(str,1);
+      maxlength--;
+   }
+}
+
 /*
   flags:
    *
@@ -870,6 +1088,7 @@ PMOD_EXPORT void f_cast(void)
   %n
   %[
   %%
+  %O
 */
 
 
@@ -1752,6 +1971,31 @@ CHAROPT2(								 \
 				    INPUT_SHIFT)(input+e,eye-e);	 \
 	  break;							 \
 									 \
+        case 'O':                                                        \
+        {                                                                \
+ 	   PIKE_CONCAT(p_wchar, INPUT_SHIFT) *t;			 \
+           PCHARP tmp;                                                   \
+           if(eye>=input_len)                                            \
+           {                                                             \
+              chars_matched[0]=eye;                                      \
+              return matches;                                            \
+           }                                                             \
+                                                                         \
+           pcharp_to_svalue_procento(&sval,				 \
+				     MKPCHARP(input+eye,INPUT_SHIFT),    \
+                                     &tmp,                               \
+				     field_length);			 \
+           t=(PIKE_CONCAT(p_wchar, INPUT_SHIFT) *)tmp.ptr;               \
+           if(input + eye == t)                                          \
+           {                                                             \
+              chars_matched[0]=eye;                                      \
+              return matches;                                            \
+           }                                                             \
+           eye=t-input;                                                  \
+                                                                         \
+           break;                                                        \
+        }                                                                \
+                                                                         \
 	case 'n':							 \
 	  sval.type=T_INT;						 \
 	  sval.subtype=NUMBER_NUMBER;					 \
diff --git a/src/testsuite.in b/src/testsuite.in
index 9600d4140e5e278205f77a719e9a9e76998347eb..27e48c43dcbf3523c55bd8132052ee7e48de5381 100644
--- a/src/testsuite.in
+++ b/src/testsuite.in
@@ -1,4 +1,4 @@
-test_true([["$Id: testsuite.in,v 1.596 2003/02/08 22:36:14 mast Exp $"]]);
+test_true([["$Id: testsuite.in,v 1.597 2003/02/11 15:22:29 mirar Exp $"]]);
 
 // This triggered a bug only if run sufficiently early.
 test_compile_any([[#pike 7.2]])
@@ -5434,6 +5434,21 @@ class Bar {
   return Bar()->foo()
 ]], "foobarbaz")
 
+// sscanf %O
+test_equal([[array_sscanf("10","%O")]],[[({10})]])
+test_equal([[array_sscanf("\"gnurk\"","%O")]],[[({"gnurk"})]])
+test_equal([[array_sscanf("\"a\\bc\\123\\d\\x123e\\\\f\"","%O")]],[[({"a\bc\123\d\x123e\\f"})]])
+test_equal([[array_sscanf("'a'","%O")]],[[({'a'})]])
+test_equal([[array_sscanf("'\\a'","%O")]],[[({'\a'})]])
+
+test_equal([[array_sscanf("   10abcd","%O%s")]],[[({10,"abcd"})]])
+test_equal([[array_sscanf("   \"gnurk\"abcd","%O%s")]],[[({"gnurk","abcd"})]])
+test_equal([[array_sscanf("   \"a\\bc\\123\\d\\x123e\\\\f\"abcd","%O%s")]],[[({"a\bc\123\d\x123e\\f","abcd"})]])
+test_equal([[array_sscanf("   'a'abcd","%O%s")]],[[({'a',"abcd"})]])
+test_equal([[array_sscanf("   '\\a'abcd","%O%s")]],[[({'\a',"abcd"})]])
+
+test_equal([[array_sscanf("10 20 30","%O%O%O")]],[[({10,20,30})]])
+test_equal([[array_sscanf("10 20 30","%O%O%s")]],[[({10,20," 30"})]])
 
 // Basics
 test_true(1)