streaming filter: ident filter

Add support for "ident" filter on the output codepath. This does not work with lf-to-crlf filter together (yet). Signed-off-by: Junio C Hamano <gitster@pobox.com>

Junio C Hamano committed May 20, 2011 at 18:28 UTC b84c7839170ab35342d4ff098083c780075f015a
1 file changed +169 -8
convert.c
+169 -8
@@ -872,6 +872,10 @@ int is_null_stream_filter(struct stream_filter *filter)
872 return filter == &null_filter_singleton;
873 }
874
875 +
876 +/*
877 + * LF-to-CRLF filter
878 + */
879 static int lf_to_crlf_filter_fn(struct stream_filter *filter,
880 const char *input, size_t *isize_p,
881 char *output, size_t *osize_p)
@@ -909,6 +913,151 @@ static struct stream_filter lf_to_crlf_filter_singleton = {
913 &lf_to_crlf_vtbl,
914 };
915
916 +
917 +/*
918 + * ident filter
919 + */
920 +#define IDENT_DRAINING (-1)
921 +#define IDENT_SKIPPING (-2)
922 +struct ident_filter {
923 + struct stream_filter filter;
924 + struct strbuf left;
925 + int state;
926 + char ident[45]; /* ": x40 $" */
927 +};
928 +
929 +static int is_foreign_ident(const char *str)
930 +{
931 + int i;
932 +
933 + if (prefixcmp(str, "$Id: "))
934 + return 0;
935 + for (i = 5; str[i]; i++) {
936 + if (isspace(str[i]) && str[i+1] != '$')
937 + return 1;
938 + }
939 + return 0;
940 +}
941 +
942 +static void ident_drain(struct ident_filter *ident, char **output_p, size_t *osize_p)
943 +{
944 + size_t to_drain = ident->left.len;
945 +
946 + if (*osize_p < to_drain)
947 + to_drain = *osize_p;
948 + if (to_drain) {
949 + memcpy(*output_p, ident->left.buf, to_drain);
950 + strbuf_remove(&ident->left, 0, to_drain);
951 + *output_p += to_drain;
952 + *osize_p -= to_drain;
953 + }
954 + if (!ident->left.len)
955 + ident->state = 0;
956 +}
957 +
958 +static int ident_filter_fn(struct stream_filter *filter,
959 + const char *input, size_t *isize_p,
960 + char *output, size_t *osize_p)
961 +{
962 + struct ident_filter *ident = (struct ident_filter *)filter;
963 + static const char head[] = "$Id";
964 +
965 + if (!input) {
966 + /* drain upon eof */
967 + switch (ident->state) {
968 + default:
969 + strbuf_add(&ident->left, head, ident->state);
970 + case IDENT_SKIPPING:
971 + /* fallthru */
972 + case IDENT_DRAINING:
973 + ident_drain(ident, &output, osize_p);
974 + }
975 + return 0;
976 + }
977 +
978 + while (*isize_p || (ident->state == IDENT_DRAINING)) {
979 + int ch;
980 +
981 + if (ident->state == IDENT_DRAINING) {
982 + ident_drain(ident, &output, osize_p);
983 + if (!*osize_p)
984 + break;
985 + continue;
986 + }
987 +
988 + ch = *(input++);
989 + (*isize_p)--;
990 +
991 + if (ident->state == IDENT_SKIPPING) {
992 + /*
993 + * Skipping until '$' or LF, but keeping them
994 + * in case it is a foreign ident.
995 + */
996 + strbuf_addch(&ident->left, ch);
997 + if (ch != '\n' && ch != '$')
998 + continue;
999 + if (ch == '$' && !is_foreign_ident(ident->left.buf)) {
1000 + strbuf_setlen(&ident->left, sizeof(head) - 1);
1001 + strbuf_addstr(&ident->left, ident->ident);
1002 + }
1003 + ident->state = IDENT_DRAINING;
1004 + continue;
1005 + }
1006 +
1007 + if (ident->state < sizeof(head) &&
1008 + head[ident->state] == ch) {
1009 + ident->state++;
1010 + continue;
1011 + }
1012 +
1013 + if (ident->state)
1014 + strbuf_add(&ident->left, head, ident->state);
1015 + if (ident->state == sizeof(head) - 1) {
1016 + if (ch != ':' && ch != '$') {
1017 + strbuf_addch(&ident->left, ch);
1018 + ident->state = 0;
1019 + continue;
1020 + }
1021 +
1022 + if (ch == ':') {
1023 + strbuf_addch(&ident->left, ch);
1024 + ident->state = IDENT_SKIPPING;
1025 + } else {
1026 + strbuf_addstr(&ident->left, ident->ident);
1027 + ident->state = IDENT_DRAINING;
1028 + }
1029 + continue;
1030 + }
1031 +
1032 + strbuf_addch(&ident->left, ch);
1033 + ident->state = IDENT_DRAINING;
1034 + }
1035 + return 0;
1036 +}
1037 +
1038 +static void ident_free_fn(struct stream_filter *filter)
1039 +{
1040 + struct ident_filter *ident = (struct ident_filter *)filter;
1041 + strbuf_release(&ident->left);
1042 + free(filter);
1043 +}
1044 +
1045 +static struct stream_filter_vtbl ident_vtbl = {
1046 + ident_filter_fn,
1047 + ident_free_fn,
1048 +};
1049 +
1050 +static struct stream_filter *ident_filter(const unsigned char *sha1)
1051 +{
1052 + struct ident_filter *ident = xmalloc(sizeof(*ident));
1053 +
1054 + sprintf(ident->ident, ": %s $", sha1_to_hex(sha1));
1055 + strbuf_init(&ident->left, 0);
1056 + ident->filter.vtbl = &ident_vtbl;
1057 + ident->state = 0;
1058 + return (struct stream_filter *)ident;
1059 +}
1060 +
1061 /*
1062 * Return an appropriately constructed filter for the path, or NULL if
1063 * the contents cannot be filtered without reading the whole thing
@@ -921,23 +1070,35 @@ struct stream_filter *get_stream_filter(const char *path, const unsigned char *s
1070 {
1071 struct conv_attrs ca;
1072 enum crlf_action crlf_action;
1073 + struct stream_filter *filter = NULL;
1074
1075 convert_attrs(&ca, path);
1076
927 - if (ca.ident ||
928 - (ca.drv && (ca.drv->smudge || ca.drv->clean)))
929 - return NULL;
1077 + if (ca.drv && (ca.drv->smudge || ca.drv->clean))
1078 + return filter;
1079 +
1080 + if (ca.ident)
1081 + filter = ident_filter(sha1);
1082
1083 crlf_action = input_crlf_action(ca.crlf_action, ca.eol_attr);
1084 +
1085 if ((crlf_action == CRLF_BINARY) || (crlf_action == CRLF_INPUT) ||
933 - (crlf_action == CRLF_GUESS && auto_crlf == AUTO_CRLF_FALSE))
1086 + (crlf_action == CRLF_GUESS && auto_crlf == AUTO_CRLF_FALSE)) {
1087 + if (filter) {
1088 + free_stream_filter(filter);
1089 + return NULL;
1090 + }
1091 return &null_filter_singleton;
935 -
936 - if (output_eol(crlf_action) == EOL_CRLF &&
937 - !(crlf_action == CRLF_AUTO || crlf_action == CRLF_GUESS))
1092 + } else if (output_eol(crlf_action) == EOL_CRLF &&
1093 + !(crlf_action == CRLF_AUTO || crlf_action == CRLF_GUESS)) {
1094 + if (filter) {
1095 + free_stream_filter(filter);
1096 + return NULL;
1097 + }
1098 return &lf_to_crlf_filter_singleton;
1099 + }
1100
940 - return NULL;
1101 + return filter;
1102 }
1103
1104 void free_stream_filter(struct stream_filter *filter)