namazu-dev(ring)


[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Using s/pat/subst/ in REPLACE



濱野でございます。お誉めにあずかったので、[namazu-dev 100] と同
じ内容のパッチを1.3.0.8 にバックポートしたものをお送りします。

1.3.0.8 は基本的にメンテナンスのみと理解していますので、こちらの
コードは現行コードの変更点を最小化することを主目的に構成してあっ
て、前回のパッチと比べても性能的にはさらに suboptimal ですがあし
からず。

ときに、なまず、での字句の登録に関して質問があるのですが、
「replace という字句はファイル re_match.c に現れる」という以上の
情報、たとえば、「ファイル中の何行目に」(位置)とか、「関数定義
として、あるいは、関数の呼び出しとして」(用法)とか言った属性も
記録することが出来るような辞書構成になっているのでしょうか? 

なまずに現在する機能のなかでは、メールやニュースのフィールド検索
の機能には、そういう属性記録があると一般的なメカニズムとして使え
るな、と思ったのですが、ざっとコードを眺めた限りでは、フィールド
検索にはそれ専用にアドホックな別辞書がつかわれているようですね。

なんでこういうことに興味があるかというと、etags や ctags の出力
を索引エンジンに食わせて(つまり、chasen や skk の代わりにこれら
を tokenizer として使う、ということです)、なまずをソースコード
ブラウザ(global とか lxr の類ね)としてつかえるのではないかな、
と考えているからなのです。

また、もともと [namazu-dev 100] であげた CVS レポジトリ中にある 
RCS ファイルを索引する例でも、「この字句は checkin log に現れる
コメントである」とか、「この字句は最新版 (-rHEAD) に現れる」とか、
「この字句は古い版に現れる(が最新版にはない)」といった属性付き
で検索できると嬉しい、というねらいもあります。

cd /reb/src/base/namazu/src/
rcsdiff -r1.3.0.8 -r1.3.0.8.0 -u *,v
===================================================================
RCS file: hlist.c,v
retrieving revision 1.3.0.8
retrieving revision 1.3.0.8.0.1
diff -u -r1.3.0.8 -r1.3.0.8.0.1
--- hlist.c	1999/07/12 07:38:35	1.3.0.8
+++ hlist.c	1999/07/12 07:41:11	1.3.0.8.0.1
@@ -30,6 +30,7 @@
 #include <math.h>
 #include "namazu.h"
 #include "util.h"
+#include "regex.h"
 
 /* merge the left and  right with AND rule */
 HLIST andmerge(HLIST left, HLIST right, int *ignore)
@@ -448,6 +449,105 @@
     }
 }
 
+/* FROM_STRING_PTR and TO_STRING_PTR points to the original
+ * (replace-from, replace-to) pair.
+ * If they are for regexp substitution and if the target string
+ * contained in TMP matches the replace-from pattern at the
+ * beginning, fill REPLACE_FROM_BUF and REPLACE_TO_BUF with
+ * a (replace-from, replace-to) pair that is suitable to be used
+ * for string subtitution.  That is, when:
+ *   tmp = "ab/cd", *from_string_ptr = "(.*)/(.*)", *to_string_ptr = "\1\2",
+ * store "ab/cd" in replace_from_buf[], "abcd" in replace_to_buf[],
+ * and update *from_string_ptr and *to_string_ptr to point to them.
+ */
+void replace_using_regexp (uchar *tmp,
+			   uchar **from_string_ptr,
+			   uchar **to_string_ptr,
+			   uchar *replace_from_buf,
+			   uchar *replace_to_buf)
+{
+    uchar *replace_from = *from_string_ptr;
+    uchar *replace_to = *to_string_ptr;
+    int i, j;
+
+    if (strpbrk (replace_from, ".*")) {
+      struct re_registers regs;
+      struct re_pattern_buffer *re;
+      int mlen;
+      int is_a_regexp_match = 0;
+
+      regs.allocated = 0;
+      re = malloc(sizeof (*re));
+      memset (re, 0, sizeof (*re));
+      re->buffer = 0;
+      re->allocated = 0;
+      if (re_compile_pattern (replace_from, strlen (replace_from), re))
+	/* re_comp fails; maybe it was not a regexp substitution
+	 * after all.  Fall back to string substitution for backward
+	 * compatibility.
+	 */
+	is_a_regexp_match = 0;
+      else if (0 < (mlen = re_match (re, tmp, strlen (tmp), 0, &regs))) {
+	/* We got a match.  Try to replace the string. */
+	uchar *subst = replace_to;
+	/* Assume we are doing regexp match for now; if any of the
+	 * substitution fails, we will switch back to the straight
+	 * string substitution.
+	 */
+	is_a_regexp_match = 1;
+	for (i = j = 0; subst[i]; i++) {
+	  /* i scans through RHS of sed-style substitution.
+	   * j points at the string being built.
+	   */
+	  if ((subst[i] == '\\') &&
+	      ('0' <= subst[++i]) &&
+	      (subst[i] <= '9')) {
+	    /* A backslash followed by a digit---regexp substitution.
+	     * Note that a backslash followed by anything else is
+	     * silently dropped (including a \\ sequence) and is
+	     * passed on to the else clause.
+	     */
+	    int regno = subst[i] - '0';
+	    int ct;
+	    if (re->re_nsub <= regno) {
+	      /* Oops; this is a bad substitution.  Just give up
+	       * and use straight string substitution for backward
+	       * compatibility.
+	       */
+	      is_a_regexp_match = 0;
+	      break;
+	    }
+	    for (ct = regs.beg[regno]; ct < regs.end[regno]; ct++)
+	      replace_to_buf[j++] = tmp[ct];
+	  }
+	  else {
+	    /* Either ordinary character, or an unrecognized \ sequence.
+	     * Just copy it.
+	     */
+	    replace_to_buf[j++] = subst[i];
+	  }
+	}
+	if (is_a_regexp_match) {
+	  /* Good.  Regexp substitution worked and we now have a good
+	   * string in replace_to_buf.  Fake replace_from and replace_to
+	   * as if these matched string pairs were specified in the
+	   * replacement list as literal substitutions.
+	   */
+	  replace_to_buf[j] = 0;
+	  *to_string_ptr = replace_to = replace_to_buf;
+	  strcpy (replace_from_buf, tmp);
+	  replace_from_buf[mlen] = 0;
+	  *from_string_ptr = replace_from_buf;
+	}
+	re_free_registers (&regs);
+      }
+      re_free_pattern (re);
+      /* We behave as if replace_from and replace_to specified the
+       * literal string pairs from the beginning.
+       */
+    }
+}
+
 /* replace a URL */
 void replace_url(uchar * s, int opt)
 {
@@ -458,16 +558,27 @@
     strcpy(tmp, s);
 
   for(n=0;n<url_no;n++) {
-    n_from = strlen(URL_REPLACE_FROM[n]);
-    n_to = strlen(URL_REPLACE_TO[n]);
+    uchar *replace_from = URL_REPLACE_FROM[n];
+    uchar *replace_to = URL_REPLACE_TO[n];
+    uchar replace_from_buf[BUFSIZE];
+    uchar replace_to_buf[BUFSIZE];
+
+    replace_using_regexp (tmp,
+			  &replace_from,
+			  &replace_to,
+			  replace_from_buf,
+			  replace_to_buf);
+
+    n_from = strlen(replace_from);
+    n_to = strlen(replace_to);
 
-    if (!strncmp(URL_REPLACE_FROM[n], tmp, n_from)) {
-	strcpy(s, URL_REPLACE_TO[n]);
+    if (!strncmp(replace_from, tmp, n_from)) {
+	strcpy(s, replace_to);
 	for (i = n_from, j = n_to; tmp[i] != '>'; i++, j++)
 	    s[j] = tmp[i];
 	s[j++] = tmp[i++];
-	if (opt && !strncmp(URL_REPLACE_FROM[n], tmp + i, n_from)) {
-	    strcpy(s + j, URL_REPLACE_TO[n]);
+	if (opt && !strncmp(replace_from, tmp + i, n_from)) {
+	    strcpy(s + j, replace_to);
 	    i += n_from;
 	    j += n_to;
 	}
===================================================================
RCS file: re_match.c,v
retrieving revision 1.3.0.8
retrieving revision 1.3.0.8.0.1
diff -u -r1.3.0.8 -r1.3.0.8.0.1
--- re_match.c	1999/07/12 07:38:35	1.3.0.8
+++ re_match.c	1999/07/12 07:41:11	1.3.0.8.0.1
@@ -38,6 +38,12 @@
 
 #define STEP 256
 
+void replace_using_regexp (uchar *tmp,
+			   uchar **from_string_ptr,
+			   uchar **to_string_ptr,
+			   uchar *replace_from_buf,
+			   uchar *replace_to_buf);
+
 void replace(uchar *s)
 {
     int n;
@@ -45,16 +51,26 @@
     uchar tmp[BUFSIZE];
 
     strcpy(tmp, s);
-  for(n=0;n<url_no;n++) {
-    n_from = strlen(URL_REPLACE_FROM[n]);
-    n_to = strlen(URL_REPLACE_TO[n]);
-
-    if (!strncmp(URL_REPLACE_FROM[n], tmp, n_from)) {
-	strcpy(s, URL_REPLACE_TO[n]);
-	for (i = n_from, j = n_to; tmp[i] != '\0'; i++, j++)
-	    s[j] = tmp[i];
-	s[j] = '\0';
-    }
+    for(n=0;n<url_no;n++) {
+      uchar *replace_from = URL_REPLACE_FROM[n];
+      uchar *replace_to = URL_REPLACE_TO[n];
+      uchar replace_from_buf[BUFSIZE];
+      uchar replace_to_buf[BUFSIZE];
+      replace_using_regexp (tmp,
+			    &replace_from,
+			    &replace_to,
+			    replace_from_buf,
+			    replace_to_buf);
+
+      n_from = strlen(replace_from);
+      n_to = strlen(replace_to);
+
+      if (!strncmp(replace_from, tmp, n_from)) {
+	  strcpy(s, replace_to);
+	  for (i = n_from, j = n_to; tmp[i] != '\0'; i++, j++)
+	      s[j] = tmp[i];
+	  s[j] = '\0';
+      }
   }
 }