2005-05-21 17:39:09 +08:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2005 Junio C Hamano
|
|
|
|
*/
|
2007-04-29 14:38:52 +08:00
|
|
|
#ifndef DIFFCORE_H
|
|
|
|
#define DIFFCORE_H
|
2005-05-21 17:39:09 +08:00
|
|
|
|
|
|
|
/* This header file is internal between diff.c and its diff transformers
|
|
|
|
* (e.g. diffcore-rename, diffcore-pickaxe). Never include this header
|
|
|
|
* in anything else.
|
|
|
|
*/
|
[PATCH] diff: Update -B heuristics.
As Linus pointed out on the mailing list discussion, -B should
break a files that has many inserts even if it still keeps
enough of the original contents, so that the broken pieces can
later be matched with other files by -M or -C. However, if such
a broken pair does not get picked up by -M or -C, we would want
to apply different criteria; namely, regardless of the amount of
new material in the result, the determination of "rewrite"
should be done by looking at the amount of original material
still left in the result. If you still have the original 97
lines from a 100-line document, it does not matter if you add
your own 13 lines to make a 110-line document, or if you add 903
lines to make a 1000-line document. It is not a rewrite but an
in-place edit. On the other hand, if you did lose 97 lines from
the original, it does not matter if you added 27 lines to make a
30-line document or if you added 997 lines to make a 1000-line
document. You did a complete rewrite in either case.
This patch introduces a post-processing phase that runs after
diffcore-rename matches up broken pairs diffcore-break creates.
The purpose of this post-processing is to pick up these broken
pieces and merge them back into in-place modifications. For
this, the score parameter -B option takes is changed into a pair
of numbers, and it takes "-B99/80" format when fully spelled
out. The first number is the minimum amount of "edit" (same
definition as what diffcore-rename uses, which is "sum of
deletion and insertion") that a modification needs to have to be
broken, and the second number is the minimum amount of "delete"
a surviving broken pair must have to avoid being merged back
together. It can be abbreviated to "-B" to use default for
both, "-B9" or "-B9/" to use 90% for "edit" but default (80%)
for merge avoidance, or "-B/75" to use default (99%) "edit" and
75% for merge avoidance.
Signed-off-by: Junio C Hamano <junkio@cox.net>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-06-03 16:40:28 +08:00
|
|
|
|
|
|
|
/* We internally use unsigned short as the score value,
|
|
|
|
* and rely on an int capable to hold 32-bits. -B can take
|
|
|
|
* -Bmerge_score/break_score format and the two scores are
|
|
|
|
* passed around in one int (high 16-bit for merge and low 16-bit
|
|
|
|
* for break).
|
|
|
|
*/
|
2006-01-16 13:08:42 +08:00
|
|
|
#define MAX_SCORE 60000.0
|
[PATCH] Add -B flag to diff-* brothers.
A new diffcore transformation, diffcore-break.c, is introduced.
When the -B flag is given, a patch that represents a complete
rewrite is broken into a deletion followed by a creation. This
makes it easier to review such a complete rewrite patch.
The -B flag takes the same syntax as the -M and -C flags to
specify the minimum amount of non-source material the resulting
file needs to have to be considered a complete rewrite, and
defaults to 99% if not specified.
As the new test t4008-diff-break-rewrite.sh demonstrates, if a
file is a complete rewrite, it is broken into a delete/create
pair, which can further be subjected to the usual rename
detection if -M or -C is used. For example, if file0 gets
completely rewritten to make it as if it were rather based on
file1 which itself disappeared, the following happens:
The original change looks like this:
file0 --> file0' (quite different from file0)
file1 --> /dev/null
After diffcore-break runs, it would become this:
file0 --> /dev/null
/dev/null --> file0'
file1 --> /dev/null
Then diffcore-rename matches them up:
file1 --> file0'
The internal score values are finer grained now. Earlier
maximum of 10000 has been raised to 60000; there is no user
visible changes but there is no reason to waste available bits.
Signed-off-by: Junio C Hamano <junkio@cox.net>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-05-30 15:08:37 +08:00
|
|
|
#define DEFAULT_RENAME_SCORE 30000 /* rename/copy similarity minimum (50%) */
|
2006-03-04 17:03:53 +08:00
|
|
|
#define DEFAULT_BREAK_SCORE 30000 /* minimum for break to happen (50%) */
|
|
|
|
#define DEFAULT_MERGE_SCORE 36000 /* maximum for break-merge to happen 60%) */
|
[PATCH] diff: Update -B heuristics.
As Linus pointed out on the mailing list discussion, -B should
break a files that has many inserts even if it still keeps
enough of the original contents, so that the broken pieces can
later be matched with other files by -M or -C. However, if such
a broken pair does not get picked up by -M or -C, we would want
to apply different criteria; namely, regardless of the amount of
new material in the result, the determination of "rewrite"
should be done by looking at the amount of original material
still left in the result. If you still have the original 97
lines from a 100-line document, it does not matter if you add
your own 13 lines to make a 110-line document, or if you add 903
lines to make a 1000-line document. It is not a rewrite but an
in-place edit. On the other hand, if you did lose 97 lines from
the original, it does not matter if you added 27 lines to make a
30-line document or if you added 997 lines to make a 1000-line
document. You did a complete rewrite in either case.
This patch introduces a post-processing phase that runs after
diffcore-rename matches up broken pairs diffcore-break creates.
The purpose of this post-processing is to pick up these broken
pieces and merge them back into in-place modifications. For
this, the score parameter -B option takes is changed into a pair
of numbers, and it takes "-B99/80" format when fully spelled
out. The first number is the minimum amount of "edit" (same
definition as what diffcore-rename uses, which is "sum of
deletion and insertion") that a modification needs to have to be
broken, and the second number is the minimum amount of "delete"
a surviving broken pair must have to avoid being merged back
together. It can be abbreviated to "-B" to use default for
both, "-B9" or "-B9/" to use 90% for "edit" but default (80%)
for merge avoidance, or "-B/75" to use default (99%) "edit" and
75% for merge avoidance.
Signed-off-by: Junio C Hamano <junkio@cox.net>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-06-03 16:40:28 +08:00
|
|
|
|
|
|
|
#define MINIMUM_BREAK_SIZE 400 /* do not break a file smaller than this */
|
2005-05-21 17:39:09 +08:00
|
|
|
|
diff: introduce diff.<driver>.binary
The "diff" gitattribute is somewhat overloaded right now. It
can say one of three things:
1. this file is definitely binary, or definitely not
(i.e., diff or !diff)
2. this file should use an external diff engine (i.e.,
diff=foo, diff.foo.command = custom-script)
3. this file should use particular funcname patterns
(i.e., diff=foo, diff.foo.(x?)funcname = some-regex)
Most of the time, there is no conflict between these uses,
since using one implies that the other is irrelevant (e.g.,
an external diff engine will decide for itself whether the
file is binary).
However, there is at least one conflicting situation: there
is no way to say "use the regular rules to determine whether
this file is binary, but if we do diff it textually, use
this funcname pattern." That is, currently setting diff=foo
indicates that the file is definitely text.
This patch introduces a "binary" config option for a diff
driver, so that one can explicitly set diff.foo.binary. We
default this value to "don't know". That is, setting a diff
attribute to "foo" and using "diff.foo.funcname" will have
no effect on the binaryness of a file. To get the current
behavior, one can set diff.foo.binary to true.
This patch also has one additional advantage: it cleans up
the interface to the userdiff code a bit. Before, calling
code had to know more about whether attributes were false,
true, or unset to determine binaryness. Now that binaryness
is a property of a driver, we can represent these situations
just by passing back a driver struct.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
2008-10-06 05:43:36 +08:00
|
|
|
struct userdiff_driver;
|
|
|
|
|
2005-05-21 17:39:09 +08:00
|
|
|
struct diff_filespec {
|
|
|
|
unsigned char sha1[20];
|
|
|
|
char *path;
|
|
|
|
void *data;
|
2006-03-12 19:22:10 +08:00
|
|
|
void *cnt_data;
|
2007-07-07 16:49:58 +08:00
|
|
|
const char *funcname_pattern_ident;
|
2005-05-21 17:39:09 +08:00
|
|
|
unsigned long size;
|
2007-10-26 02:19:10 +08:00
|
|
|
int count; /* Reference count */
|
2005-05-21 17:39:09 +08:00
|
|
|
int xfrm_flags; /* for use by the xfrm */
|
2007-10-26 02:20:56 +08:00
|
|
|
int rename_used; /* Count of rename users */
|
2005-05-21 17:39:09 +08:00
|
|
|
unsigned short mode; /* file mode */
|
|
|
|
unsigned sha1_valid : 1; /* if true, use sha1 and trust mode;
|
|
|
|
* if false, use the name and read from
|
|
|
|
* the filesystem.
|
|
|
|
*/
|
2005-06-13 08:23:15 +08:00
|
|
|
#define DIFF_FILE_VALID(spec) (((spec)->mode) != 0)
|
2005-05-21 17:39:09 +08:00
|
|
|
unsigned should_free : 1; /* data should be free()'ed */
|
|
|
|
unsigned should_munmap : 1; /* data should be munmap()'ed */
|
2010-01-19 04:26:18 +08:00
|
|
|
unsigned dirty_submodule : 1; /* For submodules: its work tree is dirty */
|
diff: introduce diff.<driver>.binary
The "diff" gitattribute is somewhat overloaded right now. It
can say one of three things:
1. this file is definitely binary, or definitely not
(i.e., diff or !diff)
2. this file should use an external diff engine (i.e.,
diff=foo, diff.foo.command = custom-script)
3. this file should use particular funcname patterns
(i.e., diff=foo, diff.foo.(x?)funcname = some-regex)
Most of the time, there is no conflict between these uses,
since using one implies that the other is irrelevant (e.g.,
an external diff engine will decide for itself whether the
file is binary).
However, there is at least one conflicting situation: there
is no way to say "use the regular rules to determine whether
this file is binary, but if we do diff it textually, use
this funcname pattern." That is, currently setting diff=foo
indicates that the file is definitely text.
This patch introduces a "binary" config option for a diff
driver, so that one can explicitly set diff.foo.binary. We
default this value to "don't know". That is, setting a diff
attribute to "foo" and using "diff.foo.funcname" will have
no effect on the binaryness of a file. To get the current
behavior, one can set diff.foo.binary to true.
This patch also has one additional advantage: it cleans up
the interface to the userdiff code a bit. Before, calling
code had to know more about whether attributes were false,
true, or unset to determine binaryness. Now that binaryness
is a property of a driver, we can represent these situations
just by passing back a driver struct.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
2008-10-06 05:43:36 +08:00
|
|
|
|
|
|
|
struct userdiff_driver *driver;
|
|
|
|
/* data should be considered "binary"; -1 means "don't know yet" */
|
|
|
|
int is_binary;
|
2005-05-21 17:39:09 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
extern struct diff_filespec *alloc_filespec(const char *);
|
2007-10-26 02:19:10 +08:00
|
|
|
extern void free_filespec(struct diff_filespec *);
|
2005-05-21 17:39:09 +08:00
|
|
|
extern void fill_filespec(struct diff_filespec *, const unsigned char *,
|
|
|
|
unsigned short);
|
|
|
|
|
2005-05-28 06:56:38 +08:00
|
|
|
extern int diff_populate_filespec(struct diff_filespec *, int);
|
2005-09-15 05:06:50 +08:00
|
|
|
extern void diff_free_filespec_data(struct diff_filespec *);
|
2007-10-03 12:01:03 +08:00
|
|
|
extern void diff_free_filespec_blob(struct diff_filespec *);
|
2007-07-06 15:18:54 +08:00
|
|
|
extern int diff_filespec_is_binary(struct diff_filespec *);
|
2005-05-21 17:39:09 +08:00
|
|
|
|
2005-05-21 17:40:01 +08:00
|
|
|
struct diff_filepair {
|
2005-05-21 17:39:09 +08:00
|
|
|
struct diff_filespec *one;
|
|
|
|
struct diff_filespec *two;
|
2005-05-30 07:56:48 +08:00
|
|
|
unsigned short int score;
|
2008-11-02 21:37:28 +08:00
|
|
|
char status; /* M C R A D U etc. (see Documentation/diff-format.txt or DIFF_STATUS_* in diff.h) */
|
[PATCH] Add -B flag to diff-* brothers.
A new diffcore transformation, diffcore-break.c, is introduced.
When the -B flag is given, a patch that represents a complete
rewrite is broken into a deletion followed by a creation. This
makes it easier to review such a complete rewrite patch.
The -B flag takes the same syntax as the -M and -C flags to
specify the minimum amount of non-source material the resulting
file needs to have to be considered a complete rewrite, and
defaults to 99% if not specified.
As the new test t4008-diff-break-rewrite.sh demonstrates, if a
file is a complete rewrite, it is broken into a delete/create
pair, which can further be subjected to the usual rename
detection if -M or -C is used. For example, if file0 gets
completely rewritten to make it as if it were rather based on
file1 which itself disappeared, the following happens:
The original change looks like this:
file0 --> file0' (quite different from file0)
file1 --> /dev/null
After diffcore-break runs, it would become this:
file0 --> /dev/null
/dev/null --> file0'
file1 --> /dev/null
Then diffcore-rename matches them up:
file1 --> file0'
The internal score values are finer grained now. Earlier
maximum of 10000 has been raised to 60000; there is no user
visible changes but there is no reason to waste available bits.
Signed-off-by: Junio C Hamano <junkio@cox.net>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-05-30 15:08:37 +08:00
|
|
|
unsigned broken_pair : 1;
|
2006-08-04 03:01:01 +08:00
|
|
|
unsigned renamed_pair : 1;
|
2007-01-05 17:25:18 +08:00
|
|
|
unsigned is_unmerged : 1;
|
2005-05-21 17:39:09 +08:00
|
|
|
};
|
2007-01-05 17:25:18 +08:00
|
|
|
#define DIFF_PAIR_UNMERGED(p) ((p)->is_unmerged)
|
2005-05-21 17:39:09 +08:00
|
|
|
|
2006-08-04 03:01:01 +08:00
|
|
|
#define DIFF_PAIR_RENAME(p) ((p)->renamed_pair)
|
2005-05-30 07:56:48 +08:00
|
|
|
|
[PATCH] Add -B flag to diff-* brothers.
A new diffcore transformation, diffcore-break.c, is introduced.
When the -B flag is given, a patch that represents a complete
rewrite is broken into a deletion followed by a creation. This
makes it easier to review such a complete rewrite patch.
The -B flag takes the same syntax as the -M and -C flags to
specify the minimum amount of non-source material the resulting
file needs to have to be considered a complete rewrite, and
defaults to 99% if not specified.
As the new test t4008-diff-break-rewrite.sh demonstrates, if a
file is a complete rewrite, it is broken into a delete/create
pair, which can further be subjected to the usual rename
detection if -M or -C is used. For example, if file0 gets
completely rewritten to make it as if it were rather based on
file1 which itself disappeared, the following happens:
The original change looks like this:
file0 --> file0' (quite different from file0)
file1 --> /dev/null
After diffcore-break runs, it would become this:
file0 --> /dev/null
/dev/null --> file0'
file1 --> /dev/null
Then diffcore-rename matches them up:
file1 --> file0'
The internal score values are finer grained now. Earlier
maximum of 10000 has been raised to 60000; there is no user
visible changes but there is no reason to waste available bits.
Signed-off-by: Junio C Hamano <junkio@cox.net>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-05-30 15:08:37 +08:00
|
|
|
#define DIFF_PAIR_BROKEN(p) \
|
|
|
|
( (!DIFF_FILE_VALID((p)->one) != !DIFF_FILE_VALID((p)->two)) && \
|
|
|
|
((p)->broken_pair != 0) )
|
|
|
|
|
2005-05-26 06:07:08 +08:00
|
|
|
#define DIFF_PAIR_TYPE_CHANGED(p) \
|
|
|
|
((S_IFMT & (p)->one->mode) != (S_IFMT & (p)->two->mode))
|
|
|
|
|
2005-05-26 17:24:30 +08:00
|
|
|
#define DIFF_PAIR_MODE_CHANGED(p) ((p)->one->mode != (p)->two->mode)
|
|
|
|
|
2005-05-28 06:50:30 +08:00
|
|
|
extern void diff_free_filepair(struct diff_filepair *);
|
|
|
|
|
[PATCH] Rename/copy detection fix.
The rename/copy detection logic in earlier round was only good
enough to show patch output and discussion on the mailing list
about the diff-raw format updates revealed many problems with
it. This patch fixes all the ones known to me, without making
things I want to do later impossible, mostly related to patch
reordering.
(1) Earlier rename/copy detector determined which one is rename
and which one is copy too early, which made it impossible
to later introduce diffcore transformers to reorder
patches. This patch fixes it by moving that logic to the
very end of the processing.
(2) Earlier output routine diff_flush() was pruning all the
"no-change" entries indiscriminatingly. This was done due
to my false assumption that one of the requirements in the
diff-raw output was not to show such an entry (which
resulted in my incorrect comment about "diff-helper never
being able to be equivalent to built-in diff driver"). My
special thanks go to Linus for correcting me about this.
When we produce diff-raw output, for the downstream to be
able to tell renames from copies, sometimes it _is_
necessary to output "no-change" entries, and this patch
adds diffcore_prune() function for doing it.
(3) Earlier diff_filepair structure was trying to be not too
specific about rename/copy operations, but the purpose of
the structure was to record one or two paths, which _was_
indeed about rename/copy. This patch discards xfrm_msg
field which was trying to be generic for this wrong reason,
and introduces a couple of fields (rename_score and
rename_rank) that are explicitly specific to rename/copy
logic. One thing to note is that the information in a
single diff_filepair structure _still_ does not distinguish
renames from copies, and it is deliberately so. This is to
allow patches to be reordered in later stages.
(4) This patch also adds some tests about diff-raw format
output and makes sure that necessary "no-change" entries
appear on the output.
Signed-off-by: Junio C Hamano <junkio@cox.net>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-05-23 12:26:09 +08:00
|
|
|
extern int diff_unmodified_pair(struct diff_filepair *);
|
|
|
|
|
2005-05-21 17:39:09 +08:00
|
|
|
struct diff_queue_struct {
|
2005-05-21 17:40:01 +08:00
|
|
|
struct diff_filepair **queue;
|
2005-05-21 17:39:09 +08:00
|
|
|
int alloc;
|
|
|
|
int nr;
|
|
|
|
};
|
|
|
|
|
2005-05-22 10:40:36 +08:00
|
|
|
extern struct diff_queue_struct diff_queued_diff;
|
2005-05-21 17:40:01 +08:00
|
|
|
extern struct diff_filepair *diff_queue(struct diff_queue_struct *,
|
|
|
|
struct diff_filespec *,
|
|
|
|
struct diff_filespec *);
|
2005-05-23 01:04:37 +08:00
|
|
|
extern void diff_q(struct diff_queue_struct *, struct diff_filepair *);
|
[PATCH] Rename/copy detection fix.
The rename/copy detection logic in earlier round was only good
enough to show patch output and discussion on the mailing list
about the diff-raw format updates revealed many problems with
it. This patch fixes all the ones known to me, without making
things I want to do later impossible, mostly related to patch
reordering.
(1) Earlier rename/copy detector determined which one is rename
and which one is copy too early, which made it impossible
to later introduce diffcore transformers to reorder
patches. This patch fixes it by moving that logic to the
very end of the processing.
(2) Earlier output routine diff_flush() was pruning all the
"no-change" entries indiscriminatingly. This was done due
to my false assumption that one of the requirements in the
diff-raw output was not to show such an entry (which
resulted in my incorrect comment about "diff-helper never
being able to be equivalent to built-in diff driver"). My
special thanks go to Linus for correcting me about this.
When we produce diff-raw output, for the downstream to be
able to tell renames from copies, sometimes it _is_
necessary to output "no-change" entries, and this patch
adds diffcore_prune() function for doing it.
(3) Earlier diff_filepair structure was trying to be not too
specific about rename/copy operations, but the purpose of
the structure was to record one or two paths, which _was_
indeed about rename/copy. This patch discards xfrm_msg
field which was trying to be generic for this wrong reason,
and introduces a couple of fields (rename_score and
rename_rank) that are explicitly specific to rename/copy
logic. One thing to note is that the information in a
single diff_filepair structure _still_ does not distinguish
renames from copies, and it is deliberately so. This is to
allow patches to be reordered in later stages.
(4) This patch also adds some tests about diff-raw format
output and makes sure that necessary "no-change" entries
appear on the output.
Signed-off-by: Junio C Hamano <junkio@cox.net>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-05-23 12:26:09 +08:00
|
|
|
|
2005-06-03 16:36:43 +08:00
|
|
|
extern void diffcore_break(int);
|
2005-09-21 15:18:27 +08:00
|
|
|
extern void diffcore_rename(struct diff_options *);
|
[PATCH] diff: Update -B heuristics.
As Linus pointed out on the mailing list discussion, -B should
break a files that has many inserts even if it still keeps
enough of the original contents, so that the broken pieces can
later be matched with other files by -M or -C. However, if such
a broken pair does not get picked up by -M or -C, we would want
to apply different criteria; namely, regardless of the amount of
new material in the result, the determination of "rewrite"
should be done by looking at the amount of original material
still left in the result. If you still have the original 97
lines from a 100-line document, it does not matter if you add
your own 13 lines to make a 110-line document, or if you add 903
lines to make a 1000-line document. It is not a rewrite but an
in-place edit. On the other hand, if you did lose 97 lines from
the original, it does not matter if you added 27 lines to make a
30-line document or if you added 997 lines to make a 1000-line
document. You did a complete rewrite in either case.
This patch introduces a post-processing phase that runs after
diffcore-rename matches up broken pairs diffcore-break creates.
The purpose of this post-processing is to pick up these broken
pieces and merge them back into in-place modifications. For
this, the score parameter -B option takes is changed into a pair
of numbers, and it takes "-B99/80" format when fully spelled
out. The first number is the minimum amount of "edit" (same
definition as what diffcore-rename uses, which is "sum of
deletion and insertion") that a modification needs to have to be
broken, and the second number is the minimum amount of "delete"
a surviving broken pair must have to avoid being merged back
together. It can be abbreviated to "-B" to use default for
both, "-B9" or "-B9/" to use 90% for "edit" but default (80%)
for merge avoidance, or "-B/75" to use default (99%) "edit" and
75% for merge avoidance.
Signed-off-by: Junio C Hamano <junkio@cox.net>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-06-03 16:40:28 +08:00
|
|
|
extern void diffcore_merge_broken(void);
|
2005-06-03 16:36:43 +08:00
|
|
|
extern void diffcore_pickaxe(const char *needle, int opts);
|
|
|
|
extern void diffcore_order(const char *orderfile);
|
|
|
|
|
2005-05-24 16:10:48 +08:00
|
|
|
#define DIFF_DEBUG 0
|
|
|
|
#if DIFF_DEBUG
|
|
|
|
void diff_debug_filespec(struct diff_filespec *, int, const char *);
|
|
|
|
void diff_debug_filepair(const struct diff_filepair *, int);
|
|
|
|
void diff_debug_queue(const char *, struct diff_queue_struct *);
|
|
|
|
#else
|
|
|
|
#define diff_debug_filespec(a,b,c) do {} while(0)
|
|
|
|
#define diff_debug_filepair(a,b) do {} while(0)
|
|
|
|
#define diff_debug_queue(a,b) do {} while(0)
|
|
|
|
#endif
|
|
|
|
|
2007-06-29 13:54:37 +08:00
|
|
|
extern int diffcore_count_changes(struct diff_filespec *src,
|
|
|
|
struct diff_filespec *dst,
|
2006-03-12 19:22:10 +08:00
|
|
|
void **src_count_p,
|
|
|
|
void **dst_count_p,
|
2006-03-01 08:01:36 +08:00
|
|
|
unsigned long delta_limit,
|
|
|
|
unsigned long *src_copied,
|
|
|
|
unsigned long *literal_added);
|
|
|
|
|
2005-05-21 17:39:09 +08:00
|
|
|
#endif
|