Changeset 9e219d3


Ignore:
Timestamp:
Mar 13, 2016, 12:43:12 PM (8 years ago)
Author:
Karl Ramm <kcr@1ts.org>
Children:
ed05467
Parents:
0f5af62
git-author:
Karl Ramm <kcr@1ts.org> (03/12/16 21:40:02)
git-committer:
Karl Ramm <kcr@1ts.org> (03/13/16 12:43:12)
Message:
rewrite the zephyr scribelike markup parser
Files:
2 added
5 edited

Legend:

Unmodified
Added
Removed
  • AUTHORS

    r80c0fc7 r9e219d3  
    3232  Betsy Riley
    3333  Robert Jacobs
     34  Google Inc.
    3435
    3536BarnOwl is based on code from Owl, which was originally primarily
  • Makefile.am

    rca1fb26a r9e219d3  
    5656CODELIST_SRCS=message.c mainwin.c popwin.c zephyr.c messagelist.c \
    5757     commands.c global.c text.c fmtext.c editwin.c \
    58      util.c logging.c \
     58     util.c logging.c ztext.c \
    5959     perlconfig.c keys.c functions.c zwrite.c viewwin.c help.c filter.c \
    6060     regex.c history.c view.c dict.c variable.c filterelement.c pair.c \
  • fmtext.c

    rf271129 r9e219d3  
    549549}
    550550
     551/* flattens a ztext parse tree into fmtext */
     552static void owl_fmtext_append_ztext_tree(owl_fmtext *f, ztext_env *tree, char attr, short fg)
     553{
     554  char *s;
     555  int i;
     556  bool handled = false;
     557  const struct {
     558    const char *tag;
     559    const char mask;
     560  } TAGS[] = {
     561    {"", 0},
     562    {"@", 0},
     563    {"@bold", OWL_FMTEXT_ATTR_BOLD},
     564    {"@b", OWL_FMTEXT_ATTR_BOLD},
     565    {"@italic", OWL_FMTEXT_ATTR_UNDERLINE},
     566    {"@i", OWL_FMTEXT_ATTR_UNDERLINE},
     567    {NULL, 0},
     568  };
     569
     570  if (tree->closer == '@' && tree->content == NULL) {
     571    owl_fmtext_append_attr(f, "@", attr, fg, OWL_COLOR_DEFAULT);
     572    return;
     573  }
     574
     575  for (i = 0; TAGS[i].tag != NULL; i++) {
     576    if (!strcasecmp(tree->label, TAGS[i].tag)) {
     577      attr |= TAGS[i].mask;
     578      handled = true;
     579      break;
     580    }
     581  }
     582
     583  if (!handled) {
     584    /* continue the dubious practice of printing codes we don't understand */
     585    s = g_strdup_printf("%s%c", tree->label, tree->opener);
     586    owl_fmtext_append_attr(f, s, attr, fg, OWL_COLOR_DEFAULT);
     587    g_free(s);
     588  }
     589
     590  for (ztext_node *p = tree->content; p != NULL; p = p->next) {
     591    if (p->type == ZTEXT_NODE_STRING) {
     592      owl_fmtext_append_attr(f, p->string, attr, fg, OWL_COLOR_DEFAULT);
     593    } else if (p->type == ZTEXT_NODE_ENV) {
     594      if (!strcasecmp(p->env->label, "@color")) {
     595        s = ztext_strip(p->env);
     596        fg = owl_util_string_to_color(s);
     597        if (fg == OWL_COLOR_INVALID)
     598          fg = OWL_COLOR_DEFAULT;
     599        g_free(s);
     600      } else {
     601        owl_fmtext_append_ztext_tree(f, p->env, attr, fg);
     602      }
     603    }
     604  }
     605
     606  if (!handled) {
     607    s = g_strdup_printf("%c", tree->closer);
     608    owl_fmtext_append_attr(f, s, attr, fg, OWL_COLOR_DEFAULT);
     609    g_free(s);
     610  }
     611}
    551612
    552613/* Append the text 'text' to 'f' and interpret the zephyr style
     
    555616void owl_fmtext_append_ztext(owl_fmtext *f, const char *text)
    556617{
    557   int stacksize, curattrs, curcolor;
    558   const char *ptr, *txtptr, *tmpptr;
    559   char *buff;
    560   int attrstack[32], chrstack[32], colorstack[32];
    561 
    562   curattrs=OWL_FMTEXT_ATTR_NONE;
    563   curcolor=OWL_COLOR_DEFAULT;
    564   stacksize=0;
    565   txtptr=text;
    566   while (1) {
    567     ptr=strpbrk(txtptr, "@{[<()>]}");
    568     if (!ptr) {
    569       /* add all the rest of the text and exit */
    570       owl_fmtext_append_attr(f, txtptr, curattrs, curcolor, OWL_COLOR_DEFAULT);
    571       return;
    572     } else if (ptr[0]=='@') {
    573       /* add the text up to this point then deal with the stack */
    574       buff=g_new(char, ptr-txtptr+20);
    575       strncpy(buff, txtptr, ptr-txtptr);
    576       buff[ptr-txtptr]='\0';
    577       owl_fmtext_append_attr(f, buff, curattrs, curcolor, OWL_COLOR_DEFAULT);
    578       g_free(buff);
    579 
    580       /* update pointer to point at the @ */
    581       txtptr=ptr;
    582 
    583       /* now the stack */
    584 
    585       /* if we've hit our max stack depth, print the @ and move on */
    586       if (stacksize==32) {
    587         owl_fmtext_append_attr(f, "@", curattrs, curcolor, OWL_COLOR_DEFAULT);
    588         txtptr++;
    589         continue;
    590       }
    591 
    592       /* if it's an @@, print an @ and continue */
    593       if (txtptr[1]=='@') {
    594         owl_fmtext_append_attr(f, "@", curattrs, curcolor, OWL_COLOR_DEFAULT);
    595         txtptr+=2;
    596         continue;
    597       }
    598        
    599       /* if there's no opener, print the @ and continue */
    600       tmpptr=strpbrk(txtptr, "(<[{ ");
    601       if (!tmpptr || tmpptr[0]==' ') {
    602         owl_fmtext_append_attr(f, "@", curattrs, curcolor, OWL_COLOR_DEFAULT);
    603         txtptr++;
    604         continue;
    605       }
    606 
    607       /* check what command we've got, push it on the stack, start
    608          using it, and continue ... unless it's a color command */
    609       buff=g_new(char, tmpptr-ptr+20);
    610       strncpy(buff, ptr, tmpptr-ptr);
    611       buff[tmpptr-ptr]='\0';
    612       if (!strcasecmp(buff, "@bold")) {
    613         attrstack[stacksize]=OWL_FMTEXT_ATTR_BOLD;
    614         chrstack[stacksize]=tmpptr[0];
    615         colorstack[stacksize]=curcolor;
    616         stacksize++;
    617         curattrs|=OWL_FMTEXT_ATTR_BOLD;
    618         txtptr+=6;
    619         g_free(buff);
    620         continue;
    621       } else if (!strcasecmp(buff, "@b")) {
    622         attrstack[stacksize]=OWL_FMTEXT_ATTR_BOLD;
    623         chrstack[stacksize]=tmpptr[0];
    624         colorstack[stacksize]=curcolor;
    625         stacksize++;
    626         curattrs|=OWL_FMTEXT_ATTR_BOLD;
    627         txtptr+=3;
    628         g_free(buff);
    629         continue;
    630       } else if (!strcasecmp(buff, "@i")) {
    631         attrstack[stacksize]=OWL_FMTEXT_ATTR_UNDERLINE;
    632         chrstack[stacksize]=tmpptr[0];
    633         colorstack[stacksize]=curcolor;
    634         stacksize++;
    635         curattrs|=OWL_FMTEXT_ATTR_UNDERLINE;
    636         txtptr+=3;
    637         g_free(buff);
    638         continue;
    639       } else if (!strcasecmp(buff, "@italic")) {
    640         attrstack[stacksize]=OWL_FMTEXT_ATTR_UNDERLINE;
    641         chrstack[stacksize]=tmpptr[0];
    642         colorstack[stacksize]=curcolor;
    643         stacksize++;
    644         curattrs|=OWL_FMTEXT_ATTR_UNDERLINE;
    645         txtptr+=8;
    646         g_free(buff);
    647         continue;
    648       } else if (!strcasecmp(buff, "@")) {
    649         attrstack[stacksize]=OWL_FMTEXT_ATTR_NONE;
    650         chrstack[stacksize]=tmpptr[0];
    651         colorstack[stacksize]=curcolor;
    652         stacksize++;
    653         txtptr+=2;
    654         g_free(buff);
    655         continue;
    656 
    657         /* if it's a color read the color, set the current color and
    658            continue */
    659       } else if (!strcasecmp(buff, "@color")
    660                  && owl_global_is_colorztext(&g)) {
    661         g_free(buff);
    662         txtptr+=7;
    663         tmpptr=strpbrk(txtptr, "@{[<()>]}");
    664         if (tmpptr &&
    665             ((txtptr[-1]=='(' && tmpptr[0]==')') ||
    666              (txtptr[-1]=='<' && tmpptr[0]=='>') ||
    667              (txtptr[-1]=='[' && tmpptr[0]==']') ||
    668              (txtptr[-1]=='{' && tmpptr[0]=='}'))) {
    669 
    670           /* grab the color name */
    671           buff=g_new(char, tmpptr-txtptr+20);
    672           strncpy(buff, txtptr, tmpptr-txtptr);
    673           buff[tmpptr-txtptr]='\0';
    674 
    675           /* set it as the current color */
    676           curcolor=owl_util_string_to_color(buff);
    677           if (curcolor == OWL_COLOR_INVALID)
    678               curcolor = OWL_COLOR_DEFAULT;
    679           g_free(buff);
    680           txtptr=tmpptr+1;
    681           continue;
    682 
    683         } else {
    684 
    685         }
    686 
    687       } else {
    688         /* if we didn't understand it, we'll print it.  This is different from zwgc
    689          * but zwgc seems to be smarter about some screw cases than I am
    690          */
    691         g_free(buff);
    692         owl_fmtext_append_attr(f, "@", curattrs, curcolor, OWL_COLOR_DEFAULT);
    693         txtptr++;
    694         continue;
    695       }
    696 
    697     } else if (ptr[0]=='}' || ptr[0]==']' || ptr[0]==')' || ptr[0]=='>') {
    698       /* add the text up to this point first */
    699       buff=g_new(char, ptr-txtptr+20);
    700       strncpy(buff, txtptr, ptr-txtptr);
    701       buff[ptr-txtptr]='\0';
    702       owl_fmtext_append_attr(f, buff, curattrs, curcolor, OWL_COLOR_DEFAULT);
    703       g_free(buff);
    704 
    705       /* now deal with the closer */
    706       txtptr=ptr;
    707 
    708       /* first, if the stack is empty we must bail (just print and go) */
    709       if (stacksize==0) {
    710         buff=g_new(char, 5);
    711         buff[0]=ptr[0];
    712         buff[1]='\0';
    713         owl_fmtext_append_attr(f, buff, curattrs, curcolor, OWL_COLOR_DEFAULT);
    714         g_free(buff);
    715         txtptr++;
    716         continue;
    717       }
    718 
    719       /* if the closing char is what's on the stack, turn off the
    720          attribue and pop the stack */
    721       if ((ptr[0]==')' && chrstack[stacksize-1]=='(') ||
    722           (ptr[0]=='>' && chrstack[stacksize-1]=='<') ||
    723           (ptr[0]==']' && chrstack[stacksize-1]=='[') ||
    724           (ptr[0]=='}' && chrstack[stacksize-1]=='{')) {
    725         int i;
    726         stacksize--;
    727         curattrs=OWL_FMTEXT_ATTR_NONE;
    728         curcolor = colorstack[stacksize];
    729         for (i=0; i<stacksize; i++) {
    730           curattrs|=attrstack[i];
    731         }
    732         txtptr+=1;
    733         continue;
    734       } else {
    735         /* otherwise print and continue */
    736         buff=g_new(char, 5);
    737         buff[0]=ptr[0];
    738         buff[1]='\0';
    739         owl_fmtext_append_attr(f, buff, curattrs, curcolor, OWL_COLOR_DEFAULT);
    740         g_free(buff);
    741         txtptr++;
    742         continue;
    743       }
    744     } else {
    745       /* we've found an unattached opener, print everything and move on */
    746       buff=g_new(char, ptr-txtptr+20);
    747       strncpy(buff, txtptr, ptr-txtptr+1);
    748       buff[ptr-txtptr+1]='\0';
    749       owl_fmtext_append_attr(f, buff, curattrs, curcolor, OWL_COLOR_DEFAULT);
    750       g_free(buff);
    751       txtptr=ptr+1;
    752       continue;
    753     }
    754   }
    755 }
     618  ztext_env *tree = ztext_tree(text);
     619
     620  owl_fmtext_append_ztext_tree(f, tree,
     621                               OWL_FMTEXT_ATTR_NONE, OWL_COLOR_DEFAULT);
     622
     623  ztext_env_free(tree);
     624}
     625
    756626
    757627/* requires that the list values are strings or NULL.
  • owl.h

    rca1fb26a r9e219d3  
    6363
    6464#include "window.h"
     65#include "ztext.h"
    6566
    6667extern const char *version;
  • tester.c

    r21dc927 r9e219d3  
    2626int call_filter_regtest(void);
    2727int owl_smartstrip_regtest(void);
     28int ztext_test(void);
    2829
    2930extern void owl_perl_xs_init(pTHX);
     
    118119  numfailures += call_filter_regtest();
    119120  numfailures += owl_smartstrip_regtest();
     121  numfailures += ztext_test();
    120122  if (numfailures) {
    121123      fprintf(stderr, "# *** WARNING: %d failures total\n", numfailures);
     
    10551057  return numfailed;
    10561058}
     1059
     1060static char *ztext_str(ztext_env *tree)
     1061{
     1062    /* Unambiguous representation of the parse tree */
     1063    char *s, *t, *u;
     1064
     1065    if (tree->opener) {
     1066        s = g_strdup_printf("{'%s%c'", tree->label, tree->opener);
     1067    } else {
     1068        s = g_strdup("{-");
     1069    }
     1070
     1071    for (ztext_node *p = tree->content; p != NULL; p = p->next) {
     1072        t = s;
     1073        if (p->type== ZTEXT_NODE_STRING) {
     1074            s = g_strdup_printf("%s |%s|", s, p->string);
     1075        } else if (p->type == ZTEXT_NODE_ENV) {
     1076            u = ztext_str(p->env);
     1077            s = g_strconcat(s, " ", u, NULL);
     1078            g_free(u);
     1079        }
     1080        g_free(t);
     1081    }
     1082
     1083    t = s;
     1084    if (tree->closer) {
     1085        s = g_strdup_printf("%s '%c'}", s, tree->closer);
     1086    } else {
     1087        s = g_strconcat(s, "}", NULL);
     1088    }
     1089    g_free(t);
     1090
     1091    return s;
     1092}
     1093
     1094char *ztext_ztext(ztext_env *tree)
     1095{
     1096    char *s, *t, *u;
     1097
     1098    if (tree->closer == '@' && tree->content == NULL)
     1099        return g_strdup("@@");
     1100
     1101    if (tree->opener) {
     1102        s = g_strdup_printf("%s%c", tree->label, tree->opener);
     1103    } else {
     1104        s = g_strdup("");
     1105    }
     1106
     1107    for (ztext_node *p = tree->content; p != NULL; p = p->next) {
     1108        t = s;
     1109        if (p->type == ZTEXT_NODE_STRING) {
     1110            s = g_strconcat(s, p->string, NULL);
     1111        } else if (p->type == ZTEXT_NODE_ENV) {
     1112            u = ztext_ztext(p->env);
     1113            s = g_strconcat(s, u, NULL);
     1114            g_free(u);
     1115        }
     1116        g_free(t);
     1117    }
     1118
     1119    if (tree->closer) {
     1120        t = s;
     1121        s = g_strdup_printf("%s%c", s, tree->closer);
     1122        g_free(t);
     1123    }
     1124
     1125    return s;
     1126}
     1127
     1128int ztext_test(void)
     1129{
     1130    int numfailed = 0;
     1131    char *s;
     1132    ztext_env *t;
     1133    char *d;
     1134
     1135#define CHECK_ZTEXT_STR(in, expected)                                         \
     1136    do {                                                                      \
     1137      t = ztext_tree(in);                                                     \
     1138      s = ztext_str(t);                                                       \
     1139      d = g_strdup_printf("ztext decode \"%s\" expected \"%s\" got \"%s\"",   \
     1140                          in, expected, s);                                   \
     1141      FAIL_UNLESS(d, !strcmp(s, expected));                                   \
     1142      ztext_env_free(t);                                                      \
     1143      g_free(d);                                                              \
     1144      g_free(s);                                                              \
     1145    } while (0)
     1146
     1147    CHECK_ZTEXT_STR("", "{-}");
     1148    CHECK_ZTEXT_STR("foo", "{- |foo|}");
     1149    CHECK_ZTEXT_STR("@{foo}", "{- {'@{' |foo| '}'}}");
     1150    CHECK_ZTEXT_STR("@bar{foo}", "{- {'@bar{' |foo| '}'}}");
     1151    CHECK_ZTEXT_STR("@bar{foo@bar}", "{- {'@bar{' |foo@bar| '}'}}");
     1152    CHECK_ZTEXT_STR("@bar{foo@@bar}", "{- {'@bar{' |foo| {'@@' '@'} |bar| '}'}}");
     1153    CHECK_ZTEXT_STR("@{foo@}bar}baz", "{- {'@{' |foo@| '}'} |bar}baz|}");
     1154    CHECK_ZTEXT_STR("foo@bar{baz@(bang})}",
     1155                    "{- |foo| {'@bar{' |baz| {'@(' |bang}| ')'} '}'}}");
     1156    CHECK_ZTEXT_STR("foo@bar{baz}", "{- |foo| {'@bar{' |baz| '}'}}");
     1157    CHECK_ZTEXT_STR("@bloop", "{- |@bloop|}");
     1158
     1159#define CHECK_ZTEXT_STRIP(in, expected)                                      \
     1160    do {                                                                     \
     1161      t = ztext_tree(in);                                                    \
     1162      s = ztext_strip(t);                                                    \
     1163      d = g_strdup_printf("ztext strip \"%s\" expected \"%s\" got \"%s\"",   \
     1164                          in, expected, s);                                  \
     1165      FAIL_UNLESS(d, !strcmp(s, expected));                                  \
     1166      ztext_env_free(t);                                                     \
     1167      g_free(d);                                                             \
     1168      g_free(s);                                                             \
     1169    } while (0)
     1170
     1171    CHECK_ZTEXT_STRIP("", "");
     1172    CHECK_ZTEXT_STRIP("foo", "foo");
     1173    CHECK_ZTEXT_STRIP("@{foo}", "foo");
     1174    CHECK_ZTEXT_STRIP("@bar{foo}", "foo");
     1175    CHECK_ZTEXT_STRIP("@bar{foo@bar}", "foo@bar");
     1176    CHECK_ZTEXT_STRIP("@bar{foo@@bar}", "foo@bar");
     1177    CHECK_ZTEXT_STRIP("@{foo@}bar}baz", "foo@bar}baz");
     1178    CHECK_ZTEXT_STRIP("foo@bar{baz@(bang})}", "foobazbang}");
     1179    CHECK_ZTEXT_STRIP("foo@bar{baz}", "foobaz");
     1180    CHECK_ZTEXT_STRIP("@bloop", "@bloop");
     1181
     1182#define CHECK_ZTEXT_RT(in)                                                \
     1183    do {                                                                  \
     1184      t = ztext_tree(in);                                                 \
     1185      s = ztext_ztext(t);                                                 \
     1186      d = g_strdup_printf("ztext round-trip \"%s\" got \"%s\"", in, s);   \
     1187      FAIL_UNLESS(d, !strcmp(s, in));                                     \
     1188      ztext_env_free(t);                                                  \
     1189      g_free(s);                                                          \
     1190    } while (0)
     1191
     1192    CHECK_ZTEXT_RT("");
     1193    CHECK_ZTEXT_RT("foo");
     1194    CHECK_ZTEXT_RT("@{foo}");
     1195    CHECK_ZTEXT_RT("@bar{foo}");
     1196    CHECK_ZTEXT_RT("@bar{foo@bar}");
     1197    CHECK_ZTEXT_RT("@bar{foo@@bar}");
     1198    CHECK_ZTEXT_RT("@{foo@}bar}baz");
     1199    CHECK_ZTEXT_RT("foo@bar{baz@(bang})}");
     1200    CHECK_ZTEXT_RT("foo@bar{baz}");
     1201    CHECK_ZTEXT_RT("@bloop");
     1202
     1203    /* fuzzzzzz */
     1204    srand48(getpid() + time(0));
     1205
     1206    char tango[32];
     1207    const char CHARS[] = "@abc ({<[]>})";
     1208
     1209    for(int i=0; i < 1000; i++) {
     1210        memset(tango, 0, sizeof(tango));
     1211        int l = lrand48() % sizeof(tango);
     1212        for (int j=0; j < l; j++) {
     1213            tango[j] = CHARS[lrand48() % (sizeof(CHARS) -1)];
     1214        }
     1215        CHECK_ZTEXT_RT(tango);
     1216    }
     1217
     1218
     1219    return numfailed;
     1220}
Note: See TracChangeset for help on using the changeset viewer.