This is patch05 to PennMUSH 1.8.3. After applying this patch, you will have version 1.8.4p5 To apply this patch, save it to a file in your top-level 1.8.3p4 MUSH directory, and do the following: patch -p0 < 1.8.3-patch05 make update make install If you use GNU patch 2.2, you probably want the above to be 'patch -b -p0', not just 'patch -p0'. Unix (or cygwin) users need not worry about failed hunks in src/switchinc.c, hdrs/switches.h, hdrs/cmds.h, or hdrs/funs.h. These files are automatically rebuilt on compile. On the off chance they appear not to be, simply rm them and re-run make. Then @shutdown and restart your MUSH. - Shawn/Raevnos In this patch: Major changes: * Significant rewrite of ansi parsing and better ansi support for many string-handling functions. Patch by Sketch. * Rewrite of the softcode regression testing framework, and addition of more tests. [SW] Minor changes: * Store a pointer to the start of a player's mailbox in objdata instead of the connection struct. * Experimental rewrite of hash tables to use the cuckoo hashing algorithm, with constant-time lookups even in the worst case. (And appears to have generally faster lookup even in normal usage.) * Regular expression @sitelocks save the compiled regexp instead of recompiling every time the rule is tested. * Added %4 to @pageformat, which is the default page message. Commands: * Added @message, which makes it easy to use @chatformat or @pageformat via @hooks, or to create your own *format. Functions: * Added message(), the function version of @message. Fixes: * decode64() does better validation of its input. [SW] * Various compile fixes reported by Interevis and Kimiko. Win32 patched by Intrevis. * @sitelock does better error reporting. [SW] * Crash bug related to regeditall fixed. * @decompile didn't handle attribute trees correctly. * Compile failure in funstr.c on some systems. Fixed by Boris. * '@set =foo' failed silently. Reported by Talvo. * Fixes from 1.8.2p7 Prereq: 1.8.3p4 =================================================================== --- Patchlevel (.../p4) (revision 1119) +++ Patchlevel (.../p5) (revision 1119) @@ -1,2 +1,2 @@ Do not edit this file. It is maintained by the official PennMUSH patches. -This is PennMUSH 1.8.3p4 +This is PennMUSH 1.8.3p5 Index: configure =================================================================== --- configure (.../p4) (revision 1119) +++ configure (.../p5) (revision 1119) @@ -17943,6 +17943,8 @@ ac_config_files="$ac_config_files game/txt/compose.sh" +ac_config_files="$ac_config_files test/alltests.sh" + cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure @@ -18487,6 +18489,7 @@ "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; "src/Makefile") CONFIG_FILES="$CONFIG_FILES src/Makefile" ;; "game/txt/compose.sh") CONFIG_FILES="$CONFIG_FILES game/txt/compose.sh" ;; + "test/alltests.sh") CONFIG_FILES="$CONFIG_FILES test/alltests.sh" ;; *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5 echo "$as_me: error: invalid argument: $ac_config_target" >&2;} @@ -19033,6 +19036,7 @@ case $ac_file$ac_mode in "game/txt/compose.sh":F) chmod +x game/txt/compose.sh ;; + "test/alltests.sh":F) chmod +x test/alltests.sh ;; esac done # for ac_tag Index: Makefile.in =================================================================== --- Makefile.in (.../p4) (revision 1119) +++ Makefile.in (.../p5) (revision 1119) @@ -3,7 +3,7 @@ # - System configuration - # VERSION=1.8.3 -PATCHLEVEL=3 +PATCHLEVEL=5 # # This section of the file should be automatically configured by @@ -80,6 +80,7 @@ netmud: (cd src; make netmud "CC=$(CC)" "CCFLAGS=$(CCFLAGS)" \ + "SQL_CFLAGS=$(SQL_CFLAGS)" "SQL_LDFLAGS=$(SQL_LDFLAGS)" \ "LDFLAGS=$(LDFLAGS)" "CLIBS=$(CLIBS)" ) access: @@ -162,6 +163,7 @@ update-hdr: -@@TOUCH@ options.h.dist + -@sleep 2 -@@PERL@ utils/update.pl options.h options.h.dist test: netmud Index: options.h.dist =================================================================== --- options.h.dist (.../p4) (revision 1119) +++ options.h.dist (.../p5) (revision 1119) @@ -58,7 +58,7 @@ * when decompressing, and considerably slower when compressing. * (But you decompress a lot more often). Compression ratio * is worse than Huffman for small dbs (<1.5Mb of text), but - * better for larger dbs. Win32 systems must use this. + * better for larger dbs. * 4 - Raevnos's almost 8-bit clean version of the word-based algorithm. * Prefer 3 unless you need extended characters. This algorithm * can encode all characters except 0x06. Index: src/sql.c =================================================================== --- src/sql.c (.../p4) (revision 1119) +++ src/sql.c (.../p5) (revision 1119) @@ -427,23 +427,14 @@ break; } if (cell && *cell) { - if (strchr(cell, ESC_CHAR)) { - /* Old style ANSI string */ + if (strchr(cell, TAG_START) || strchr(cell, ESC_CHAR)) { + /* Either old or new style ANSI string. */ tbp = tbuf; - as = parse_ansi_string_real(cell, 1); + as = parse_ansi_string(cell); safe_ansi_string(as, 0, as->len, tbuf, &tbp); *tbp = '\0'; free_ansi_string(as); cell = tbuf; - } else if (strchr(cell, TAG_START)) { - /* Either old or new style ANSI string, - * We assume new style. */ - tbp = tbuf; - as = parse_ansi_string_real(cell, 2); - safe_ansi_string(as, 0, as->len, tbuf, &tbp); - *tbp = '\0'; - free_ansi_string(as); - cell = tbuf; } } notify_format(player, "Row %d, Field %s: %s", @@ -478,7 +469,6 @@ #ifdef HAVE_MYSQL MYSQL_FIELD *fields = NULL; #endif - if (sql_platform() == SQL_PLATFORM_DISABLED) { safe_str(T(e_disabled), buff, bp); return; @@ -490,7 +480,6 @@ if (!fetch_ufun_attrib(args[0], executor, &ufun, 1)) return; - if (nargs > 2) { /* we have an output separator in args[2]. */ osep = args[2]; @@ -504,10 +493,8 @@ for (i = 0; i < 10; i++) wenv[i] = NULL; - qres = sql_query(args[1], &affected_rows); sql_test_result(qres); - /* Get results. A silent query (INSERT, UPDATE, etc.) will return NULL */ switch (sql_platform()) { #ifdef HAVE_MYSQL @@ -608,24 +595,15 @@ default: break; } - if (cell) { - if (strchr(cell, ESC_CHAR)) { - /* Old style ANSI string */ + if (cell && *cell) { + if (strchr(cell, TAG_START) || strchr(cell, ESC_CHAR)) { + /* Either old or new style ANSI string. */ tbp = buffs[i]; - as = parse_ansi_string_real(cell, 1); + as = parse_ansi_string(cell); safe_ansi_string(as, 0, as->len, buffs[i], &tbp); *tbp = '\0'; free_ansi_string(as); cell = buffs[i]; - } else if (strchr(cell, TAG_START)) { - /* Either old or new style ANSI string, - * We assume new style. */ - tbp = buffs[i]; - as = parse_ansi_string_real(cell, 2); - safe_ansi_string(as, 0, as->len, buffs[i], &tbp); - *tbp = '\0'; - free_ansi_string(as); - cell = buffs[i]; } } wenv[i + 1] = cell; @@ -635,7 +613,8 @@ /* Now call the ufun. */ if (call_ufun(&ufun, wenv, i + 1, rbuff, executor, enactor, pe_info)) goto finished; - if (safe_str(rbuff, buff, bp) && funccount == pe_info->fun_invocations) + if (safe_str(rbuff, buff, bp) + && funccount == pe_info->fun_invocations) goto finished; funccount = pe_info->fun_invocations; } @@ -655,7 +634,6 @@ int i; int numfields, numrows; ansi_string *as; - if (sql_platform() == SQL_PLATFORM_DISABLED) { safe_str(T(e_disabled), buff, bp); return; @@ -676,9 +654,7 @@ } qres = sql_query(args[0], &affected_rows); - sql_test_result(qres); - /* Get results. A silent query (INSERT, UPDATE, etc.) will return NULL */ switch (sql_platform()) { #ifdef HAVE_MYSQL @@ -748,23 +724,14 @@ break; } if (cell && *cell) { - if (strchr(cell, ESC_CHAR)) { - /* Old style ANSI string */ + if (strchr(cell, TAG_START) || strchr(cell, ESC_CHAR)) { + /* Either old or new style ANSI string. */ tbp = tbuf; - as = parse_ansi_string_real(cell, 1); + as = parse_ansi_string(cell); safe_ansi_string(as, 0, as->len, tbuf, &tbp); *tbp = '\0'; free_ansi_string(as); cell = tbuf; - } else if (strchr(cell, TAG_START)) { - /* Either old or new style ANSI string, - * We assume new style. */ - tbp = tbuf; - as = parse_ansi_string_real(cell, 2); - safe_ansi_string(as, 0, as->len, tbuf, &tbp); - *tbp = '\0'; - free_ansi_string(as); - cell = tbuf; } if (safe_str(cell, buff, bp)) goto finished; /* We filled the buffer, best stop */ Index: src/extchat.c =================================================================== --- src/extchat.c (.../p4) (revision 1119) +++ src/extchat.c (.../p5) (revision 1119) @@ -3368,9 +3368,9 @@ if (!(((flags & 1) && Chanuser_Quiet(u)) || Chanuser_Gag(u) || (IsPlayer(current) && !Connected(current)))) { - if (!messageformat(current, "CHATFORMAT", player, - na_flags | ((flags & CB_NOSPOOF) ? 0 : NA_SPOOF), - ctype, cname, message, name, title, tbuf1)) { + if (!vmessageformat(current, "CHATFORMAT", player, + na_flags | ((flags & CB_NOSPOOF) ? 0 : NA_SPOOF), + 6, ctype, cname, message, name, title, tbuf1)) { notify_anything(player, na_one, ¤t, ns_esnotify, na_flags | ((flags & CB_NOSPOOF) ? 0 : NA_SPOOF), tbuf1); Index: src/utils.c =================================================================== --- src/utils.c (.../p4) (revision 1119) +++ src/utils.c (.../p5) (revision 1119) @@ -231,13 +231,9 @@ int pe_ret; char const *ap; - int old_re_subpatterns; - int *old_re_offsets; - ansi_string *old_re_from; + struct re_save rsave; - old_re_subpatterns = global_eval_context.re_subpatterns; - old_re_offsets = global_eval_context.re_offsets; - old_re_from = global_eval_context.re_from; + save_regexp_context(&rsave); /* Make sure we have a ufun first */ if (!ufun) @@ -261,6 +257,7 @@ /* Set all the regexp patterns to NULL so they are not * propogated */ + global_eval_context.re_code = NULL; global_eval_context.re_subpatterns = -1; global_eval_context.re_offsets = NULL; global_eval_context.re_from = NULL; @@ -286,9 +283,7 @@ } /* Restore regexp patterns */ - global_eval_context.re_offsets = old_re_offsets; - global_eval_context.re_subpatterns = old_re_subpatterns; - global_eval_context.re_from = old_re_from; + restore_regexp_context(&rsave); return pe_ret; } @@ -323,9 +318,7 @@ ATTR *attrib; char *saver[NUMQ]; - int old_re_subpatterns; - int *old_re_offsets; - ansi_string *old_re_from; + struct re_save rsave; /* Make sure we have a valid object to call first */ if (!GoodObject(thing) || IsGarbage(thing)) @@ -350,9 +343,7 @@ save_global_regs("localize", saver); /* Store regepx info */ - old_re_subpatterns = global_eval_context.re_subpatterns; - old_re_offsets = global_eval_context.re_offsets; - old_re_from = global_eval_context.re_from; + save_regexp_context(&rsave); /* If the user doesn't care about the return of the expression, * then use our own rbuff. @@ -377,6 +368,7 @@ /* Set all the regexp patterns to NULL so they are not * propogated */ + global_eval_context.re_code = NULL; global_eval_context.re_subpatterns = -1; global_eval_context.re_offsets = NULL; global_eval_context.re_from = NULL; @@ -402,10 +394,7 @@ } /* Restore regexp patterns */ - global_eval_context.re_offsets = old_re_offsets; - global_eval_context.re_subpatterns = old_re_subpatterns; - global_eval_context.re_from = old_re_from; - + restore_regexp_context(&rsave); restore_global_regs("localize", saver); return pe_ret; Index: src/malias.c =================================================================== --- src/malias.c (.../p4) (revision 1119) +++ src/malias.c (.../p5) (revision 1119) @@ -142,7 +142,7 @@ return; } if (!alias || !*alias || !tolist || !*tolist) { - notify(player, T("MAIL: What alias do you want to create?.")); + notify(player, T("MAIL: What alias do you want to create?")); return; } if (*alias != MALIAS_TOKEN) { Index: src/wiz.c =================================================================== --- src/wiz.c (.../p4) (revision 1119) +++ src/wiz.c (.../p5) (revision 1119) @@ -1028,7 +1028,6 @@ void do_debug_examine(dbref player, const char *name) { - MAIL *mp; dbref thing; if (!Hasprivs(player)) { @@ -1052,8 +1051,7 @@ switch (Typeof(thing)) { case TYPE_PLAYER: - mp = desc_mail(thing); - notify_format(player, T("First mail sender: %d"), mp ? mp->from : NOTHING); + break; case TYPE_THING: notify_format(player, "Location: %d", Location(thing)); notify_format(player, "Home: %d", Home(thing)); @@ -1447,7 +1445,7 @@ return; } if (opts && *opts) { - int can, cant; + uint32_t can, cant; dbref whod = AMBIGUOUS; /* Options form of the command. */ if (!site || !*site) { @@ -1467,20 +1465,23 @@ } } - add_access_sitelock(player, site, whod, can, cant); - write_access_file(); - if (whod != AMBIGUOUS) { - notify_format(player, - T("Site %s access options for %s(%s) set to %s"), - site, Name(whod), unparse_dbref(whod), opts); - do_log(LT_WIZ, player, NOTHING, - T("*** SITELOCK *** %s for %s(%s) --> %s"), site, - Name(whod), unparse_dbref(whod), opts); - } else { - notify_format(player, T("Site %s access options set to %s"), site, opts); - do_log(LT_WIZ, player, NOTHING, "*** SITELOCK *** %s --> %s", site, opts); + if (add_access_sitelock(player, site, whod, can, cant)) { + write_access_file(); + if (whod != AMBIGUOUS) { + notify_format(player, + T("Site %s access options for %s(%s) set to %s"), + site, Name(whod), unparse_dbref(whod), opts); + do_log(LT_WIZ, player, NOTHING, + T("*** SITELOCK *** %s for %s(%s) --> %s"), site, + Name(whod), unparse_dbref(whod), opts); + } else { + notify_format(player, T("Site %s access options set to %s"), site, + opts); + do_log(LT_WIZ, player, NOTHING, "*** SITELOCK *** %s --> %s", site, + opts); + } + return; } - return; } else { /* Backward-compatible non-options form of the command, * or @sitelock/name @@ -1491,16 +1492,18 @@ do_list_access(player); return; case SITELOCK_ADD: - add_access_sitelock(player, site, AMBIGUOUS, 0, ACS_CREATE); - write_access_file(); - notify_format(player, T("Site %s locked"), site); - do_log(LT_WIZ, player, NOTHING, "*** SITELOCK *** %s", site); + if (add_access_sitelock(player, site, AMBIGUOUS, 0, ACS_CREATE)) { + write_access_file(); + notify_format(player, T("Site %s locked"), site); + do_log(LT_WIZ, player, NOTHING, "*** SITELOCK *** %s", site); + } break; case SITELOCK_BAN: - add_access_sitelock(player, site, AMBIGUOUS, 0, ACS_DEFAULT); - write_access_file(); - notify_format(player, T("Site %s banned"), site); - do_log(LT_WIZ, player, NOTHING, "*** SITELOCK *** %s", site); + if (add_access_sitelock(player, site, AMBIGUOUS, 0, ACS_DEFAULT)) { + write_access_file(); + notify_format(player, T("Site %s banned"), site); + do_log(LT_WIZ, player, NOTHING, "*** SITELOCK *** %s", site); + } break; case SITELOCK_CHECK:{ struct access *ap; Index: src/services.c =================================================================== --- src/services.c (.../p4) (revision 1119) +++ src/services.c (.../p5) (revision 1119) @@ -30,7 +30,7 @@ #define THIS_SERVICE_DISPLAY "PennMUSH for Win32" int WIN32_CDECL main(int argc, char **argv); -void mainthread(int argc, char **argv); +int mainthread(int argc, char **argv); SERVICE_STATUS ssStatus; /* current status of the service */ @@ -431,7 +431,7 @@ /* start up the main MUSH code */ - mainthread(argc, argv); + exit(mainthread(argc, argv)); } /* end of worker_thread */ Index: src/conf.c =================================================================== --- src/conf.c (.../p4) (revision 1119) +++ src/conf.c (.../p5) (revision 1119) @@ -679,7 +679,7 @@ maxval); } } - if (from_cmd && ((!GoodObject(n) && n != NOTHING) || IsGarbage(n))) { + if (from_cmd && ((!GoodObject(n) && n != NOTHING) || (n > 0 && IsGarbage(n)))) { do_rawlog(LT_ERR, T("CONFIG: attempt to set option %s to a bad dbref (#%d)"), opt, n); Index: src/db.c =================================================================== --- src/db.c (.../p4) (revision 1119) +++ src/db.c (.../p5) (revision 1119) @@ -1219,7 +1219,7 @@ */ tb2 = getstring_noalloc(f); if (strchr(tb2, TAG_START) || strchr(tb2, ESC_CHAR)) { - as = parse_ansi_string_real(tb2, 1); + as = parse_ansi_string(tb2); tb2 = tbuf2; safe_ansi_string(as, 0, as->len, tbuf2, &tb2); *(tb2) = '\0'; @@ -1306,7 +1306,7 @@ if (!(globals.indb_flags & DBF_SPIFFY_AF_ANSI)) { if (strchr(value, ESC_CHAR) || strchr(value, TAG_START)) { char *vp = value; - as = parse_ansi_string_real(value, 1); + as = parse_ansi_string(value); safe_ansi_string(as, 0, as->len, value, &vp); *vp = '\0'; free_ansi_string(as); @@ -1801,10 +1801,10 @@ static void init_objdata_htab(int size, void (*free_data) (void *)) { - if (size < 128) - size = 128; - hash_init(&htab_objdata, size, 4, free_data); - hashinit(&htab_objdata_keys, 8, 32); + if (size < 10) + size = 10; + hash_init(&htab_objdata, size, free_data); + hashinit(&htab_objdata_keys, 8); } /** Add data to the object data hashtable. @@ -1828,7 +1828,7 @@ mush_strncpy(keyname, tprintf("%s_#%d", keybase, thing), BUFFER_LEN); hashdelete(keyname, &htab_objdata); if (data) { - if (hashadd(keyname, data, &htab_objdata) < 0) + if (!hashadd(keyname, data, &htab_objdata)) return NULL; if (hash_find(&htab_objdata_keys, keybase) == NULL) { char *newkey = mush_strdup(keyname, "objdata.key"); Index: src/function.c =================================================================== --- src/function.c (.../p4) (revision 1119) +++ src/function.c (.../p5) (revision 1119) @@ -45,6 +45,25 @@ * Utilities. */ +/* Save and restore regexp data */ +void +save_regexp_context(struct re_save *save) +{ + save->re_code = global_eval_context.re_code; + save->re_from = global_eval_context.re_from; + save->re_subpatterns = global_eval_context.re_subpatterns; + save->re_offsets = global_eval_context.re_offsets; +} + +void +restore_regexp_context(struct re_save *save) +{ + global_eval_context.re_code = save->re_code; + global_eval_context.re_from = save->re_from; + global_eval_context.re_subpatterns = save->re_subpatterns; + global_eval_context.re_offsets = save->re_offsets; +} + /** Save a single q-register */ void @@ -513,6 +532,7 @@ {"MEDIAN", fun_median, 1, INT_MAX, FN_REG}, {"MEMBER", fun_member, 2, 3, FN_REG}, {"MERGE", fun_merge, 3, 3, FN_REG}, + {"MESSAGE", fun_message, 3, 13, FN_REG}, {"MID", fun_mid, 3, 3, FN_REG}, {"MIN", fun_min, 1, INT_MAX, FN_REG}, {"MIX", fun_mix, 3, 12, FN_REG}, @@ -563,7 +583,7 @@ {"NVEXITS", fun_dbwalker, 1, 1, FN_REG}, {"NVPLAYERS", fun_dbwalker, 1, 1, FN_REG}, {"NVTHINGS", fun_dbwalker, 1, 1, FN_REG}, - {"NWHO", fun_nwho, 0, 0, FN_REG}, + {"NWHO", fun_nwho, 0, 1, FN_REG}, {"OBJ", fun_obj, 1, 1, FN_REG}, {"OBJEVAL", fun_objeval, 2, -2, FN_NOPARSE}, {"OBJID", fun_objid, 1, 1, FN_REG}, @@ -889,8 +909,8 @@ { FUNTAB *ftp; - hashinit(&htab_function, 512, sizeof(FUN)); - hash_init(&htab_user_function, 32, sizeof(FUN), delete_function); + hashinit(&htab_function, 512); + hash_init(&htab_user_function, 32, delete_function); function_slab = slab_create("functions", sizeof(FUN)); for (ftp = flist; ftp->name; ftp++) { function_add(ftp->name, ftp->fun, ftp->minargs, ftp->maxargs, ftp->flags); Index: src/cmds.c =================================================================== --- src/cmds.c (.../p4) (revision 1119) +++ src/cmds.c (.../p5) (revision 1119) @@ -665,6 +665,44 @@ do_dolist(player, arg_left, arg_right, cause, flags); } +COMMAND(cmd_message) +{ + char *message; + char *attrib; + unsigned int flags = 0; + int numargs, i; + char *args[10]; + + if (!(SW_ISSET(sw, SWITCH_SPOOF) && (controls(player, cause) + || Can_Nspemit(player)))) { + cause = player; + } + + for (numargs = 1; args_right[numargs] && numargs < 13; numargs++) ; + + switch (numargs) { + case 1: + notify(player, T("@message them with what?")); + return; + case 2: + notify(player, T("Use what attribute for the @message?")); + return; + } + if (!*arg_left) { + notify(player, T("@message who?")); + return; + } + + message = args_right[1]; + attrib = args_right[2]; + + for (i = 0; (i + 3) < numargs; i++) { + args[i] = args_right[i + 3]; + } + + do_message_list(player, cause, arg_left, attrib, message, flags, i, args); +} + COMMAND(cmd_motd) { if (SW_ISSET(sw, SWITCH_CONNECT)) Index: src/markup.c =================================================================== --- src/markup.c (.../p4) (revision 1119) +++ src/markup.c (.../p5) (revision 1119) @@ -1,7 +1,7 @@ /** - * \file strutil.c + * \file markup.c * - * \brief String utilities for PennMUSH. + * \brief Markup handling in PennMUSH strings. * * */ @@ -26,27 +26,17 @@ #include "game.h" #include "confmagic.h" -#define ANSI_BLACK_V (30) -#define ANSI_RED_V (31) -#define ANSI_GREEN_V (32) -#define ANSI_YELLOW_V (33) -#define ANSI_BLUE_V (34) -#define ANSI_MAGENTA_V (35) -#define ANSI_CYAN_V (36) -#define ANSI_WHITE_V (37) - #define ANSI_BEGIN "\x1B[" #define ANSI_FINISH "m" -#define COL_NORMAL "\x1B[0m" +/* COL_* defines */ -/* COL_* and VAL_* defines */ - -#define CBIT_FLASH (1) /**< ANSI flash attribute bit */ -#define CBIT_HILITE (2) /**< ANSI hilite attribute bit */ -#define CBIT_INVERT (4) /**< ANSI inverse attribute bit */ +#define CBIT_HILITE (1) /**< ANSI hilite attribute bit */ +#define CBIT_INVERT (2) /**< ANSI inverse attribute bit */ +#define CBIT_FLASH (4) /**< ANSI flash attribute bit */ #define CBIT_UNDERSCORE (8) /**< ANSI underscore attribute bit */ +#define COL_NORMAL (0) /**< ANSI normal */ #define COL_HILITE (1) /**< ANSI hilite attribute value */ #define COL_UNDERSCORE (4) /**< ANSI underscore attribute value */ #define COL_FLASH (5) /**< ANSI flag attribute value */ @@ -64,25 +54,21 @@ /* Now the code */ static int write_ansi_close(char *buff, char **bp); -static int is_ansi_oldstyle(const char *str); +static int write_ansi_letters(ansi_data cur, char *buff, char **bp); static int safe_markup(char const *a_tag, char *buf, char **bp, char type); static int safe_markup_cancel(char const *a_tag, char *buf, char **bp, char type); -static int - compare_starts(const void *a, const void *b); +static int compare_starts(const void *a, const void *b); +static int escape_marked_str(char **str, char *buff, char **bp); const char *is_allowed_tag(const char *s, unsigned int len); +static const ansi_data ansi_null = { 0, 0, 0, 0 }; /* ARGSUSED */ FUNCTION(fun_stripansi) { - /* Strips ANSI codes away from a given string of text. Starts by - * finding the '\x' character and stripping until it hits an 'm'. - */ - char *cp; - cp = remove_markup(args[0], NULL); safe_str(cp, buff, bp); } @@ -95,27 +81,31 @@ char *ptr; ansi_string *as; char choice; - if (nargs < 2 || !args[1] || !*args[1]) + int strnum; + if (nargs < 2 || !args[0] || !*args[0]) { choice = 'r'; - else - choice = *args[1]; + strnum = 0; + } else { + choice = *args[0]; + strnum = 1; + } switch (choice) { case 'i': - as = parse_ansi_string(args[0]); + as = parse_ansi_string(args[strnum]); inspect_ansi_string(as, executor); free_ansi_string(as); break; case 'r': - for (ptr = args[0]; *ptr; ptr++) { + for (ptr = args[strnum]; *ptr; ptr++) { if (*ptr == TAG_START) *ptr = '<'; if (*ptr == TAG_END) *ptr = '>'; } - safe_str(args[0], buff, bp); + safe_str(args[strnum], buff, bp); break; case 'l': - safe_integer(arglens[0], buff, bp); + safe_integer(arglens[strnum], buff, bp); break; default: safe_str("i: inspect r: raw l: length", buff, bp); @@ -127,7 +117,7 @@ /* ARGSUSED */ FUNCTION(fun_ansi) { - struct ansi_data colors; + ansi_data colors; char *save = *bp; char *p; int i; @@ -136,8 +126,8 @@ define_ansi_data(&colors, args[0]); if (!(colors.bits || colors.offbits || colors.fore || colors.back)) { if (!safe_strl(args[1], arglens[1], buff, bp)) - write_ansi_close(buff, bp); - return; + /* write_ansi_close(buff, bp); */ + return; } /* Write the colors to buff */ @@ -230,12 +220,12 @@ return 0; while (*p) { - if (*p == ESC_CHAR) { + if (*p == TAG_START) { + while ((*p) && (*p != TAG_END)) + p++; + } else if (*p == ESC_CHAR) { while ((*p) && (*p != 'm')) p++; - } else if (*p == TAG_START) { - while ((*p) && (*p != TAG_END)) - p++; } else { i++; } @@ -244,33 +234,7 @@ return i; } -/** Compare two strings, ignoring all ansi and html markup from a string. - * Is *NOT* locale safe (a la strcoll) - * \param a string to compare to - * \param b Other string - * \return int - 0 is identical, -1 or 1 for difference. - */ -int -ansi_strcmp(const char *astr, const char *bstr) -{ - const char *a, *b; - - for (a = astr, b = bstr; *a && *b;) { - a = skip_leading_ansi(a); - b = skip_leading_ansi(b); - if (*a != *b) - return (*a - *b); - b++; - a++; - } - if (*a) - a = skip_leading_ansi(a); - if (*b) - b = skip_leading_ansi(b); - return (*a - *b); -} - -/** Returns the apparent length of a string, up to numchars visible +/** Returns the apparent length of a string, up to numchars visible * characters. The apparent length skips over nonprinting ansi and * tags. * \param p string. @@ -303,7 +267,55 @@ return i; } -/** Strip all ansi and html markup from a string. As a side effect, +/** Compare two strings, ignoring all ansi and html markup from a string. + * Is *NOT* locale safe (a la strcoll) + * \param a string to compare to + * \param b Other string + * \return int - 0 is identical, -1 or 1 for difference. + */ +int +ansi_strcmp(const char *astr, const char *bstr) +{ + const char *a, *b; + + for (a = astr, b = bstr; *a && *b;) { + a = skip_leading_ansi(a); + b = skip_leading_ansi(b); + if (*a != *b) + return (*a - *b); + b++; + a++; + } + if (*a) + a = skip_leading_ansi(a); + if (*b) + b = skip_leading_ansi(b); + return (*a - *b); +} + +/** Compare ansi_data for exact equality. + * \param a ansi_data to compare + * \param b other ansi_data + * \return int - 1 is identical, 0 is different + */ +int +ansi_equal(const ansi_data a, const ansi_data b) +{ + return ((a.bits == b.bits) && (a.offbits == b.offbits) && + (a.fore == b.fore) && (a.back == b.back)); +} + +/** Return true if ansi_data contains no ansi values. + * \param a ansi_data to check + * \return int 1 on ansi_null, 0 otherwise + */ +int +ansi_isnull(const ansi_data a) +{ + return ((a.bits == 0) && (a.offbits == 0) && (a.fore == 0) && (a.back == 0)); +} + +/** Strip all ANSI and HTML markup from a string. As a side effect, * stores the length of the stripped string in a provided address. * NOTE! Length returned is length *including* the terminating NULL, * because we usually memcpy the result. @@ -347,108 +359,55 @@ } static char ansi_chars[50]; - static int ansi_codes[255]; -struct { - char flag; - int num; -} build_ansi_codes[] = { - { - 'n', 0}, { - 'f', COL_FLASH}, { - 'h', COL_HILITE}, { - 'i', COL_INVERT}, { - 'u', COL_UNDERSCORE}, { - 'x', COL_BLACK}, { - 'X', COL_BLACK + 10}, { - 'r', COL_RED}, { - 'R', COL_RED + 10}, { - 'g', COL_GREEN}, { - 'G', COL_GREEN + 10}, { - 'y', COL_YELLOW}, { - 'Y', COL_YELLOW + 10}, { - 'b', COL_BLUE}, { - 'B', COL_BLUE + 10}, { - 'm', COL_MAGENTA}, { - 'M', COL_MAGENTA + 10}, { - 'c', COL_CYAN}, { - 'C', COL_CYAN + 10}, { - 'w', COL_WHITE}, { - 'W', COL_WHITE + 10}, { - '\0', 0} -}; +#define BUILD_ANSI(letter,ESCcode) \ +do { \ + ansi_chars[ESCcode] = letter; \ + ansi_codes[(unsigned char) letter] = ESCcode; \ +} while (0) void init_ansi_codes(void) { - int i; - memset(ansi_chars, 0, sizeof(ansi_chars)); memset(ansi_codes, 0, sizeof(ansi_codes)); - - for (i = 0; build_ansi_codes[i].flag; i++) { - ansi_chars[build_ansi_codes[i].num] = build_ansi_codes[i].flag; - ansi_codes[(unsigned char) build_ansi_codes[i].flag] = - build_ansi_codes[i].num; - } +/* + BUILD_ANSI('n', COL_NORMAL); + BUILD_ANSI('f', COL_FLASH); + BUILD_ANSI('h', COL_HILITE); + BUILD_ANSI('i', COL_INVERT); + BUILD_ANSI('u', COL_UNDERSCORE); +*/ + BUILD_ANSI('x', COL_BLACK); + BUILD_ANSI('X', COL_BLACK + 10); + BUILD_ANSI('r', COL_RED); + BUILD_ANSI('R', COL_RED + 10); + BUILD_ANSI('g', COL_GREEN); + BUILD_ANSI('G', COL_GREEN + 10); + BUILD_ANSI('y', COL_YELLOW); + BUILD_ANSI('Y', COL_YELLOW + 10); + BUILD_ANSI('b', COL_BLUE); + BUILD_ANSI('B', COL_BLUE + 10); + BUILD_ANSI('m', COL_MAGENTA); + BUILD_ANSI('M', COL_MAGENTA + 10); + BUILD_ANSI('c', COL_CYAN); + BUILD_ANSI('C', COL_CYAN + 10); + BUILD_ANSI('w', COL_WHITE); + BUILD_ANSI('W', COL_WHITE + 10); } +#undef BUILD_ANSI + int -read_raw_ansi_data(struct ansi_data *store, const char *codes) +write_ansi_data(ansi_data * cur, char *buff, char **bp) { - int curnum; - if (codes == NULL || store == NULL) - return 0; - store->bits = 0; - store->offbits = 0; - store->fore = 0; - store->back = 0; - - /* codes can point at either the ESC_CHAR or one - * following after. */ - - /* Skip to the first ansi number */ - while (*codes && !isdigit((unsigned char) *codes) && *codes != 'm') - codes++; - - memset(store, 0, sizeof(struct ansi_data)); - - while (*codes && (*codes != 'm')) { - curnum = atoi(codes); - if (curnum < 10) { - switch (curnum) { - case COL_HILITE: - store->bits ^= CBIT_HILITE; - break; - case COL_UNDERSCORE: - store->bits ^= CBIT_UNDERSCORE; - break; - case COL_FLASH: - store->bits ^= CBIT_FLASH; - break; - case COL_INVERT: - store->bits ^= CBIT_INVERT; - break; - case 0: - store->bits = 0; - store->offbits = 0; - store->fore = 'n'; - store->back = 'n'; - break; - } - } else if (curnum < 40) { - store->fore = ansi_chars[curnum]; - } else if (curnum < 50) { - store->back = ansi_chars[curnum]; - } - /* Skip current and find the nxt ansi number */ - while (*codes && isdigit((unsigned char) *codes)) - codes++; - while (*codes && !isdigit((unsigned char) *codes) && (*codes != 'm')) - codes++; - } - return 1; + int retval = 0; + retval += safe_chr(TAG_START, buff, bp); + retval += safe_chr(MARKUP_COLOR, buff, bp); + retval += write_ansi_letters(*cur, buff, bp); + retval += safe_chr(TAG_END, buff, bp); + return retval; } static int @@ -462,17 +421,16 @@ return retval; } - -int -write_ansi_data(struct ansi_data *cur, char *buff, char **bp) +static int +write_ansi_letters(const ansi_data cur, char *buff, char **bp) { int retval = 0; - retval += safe_chr(TAG_START, buff, bp); - retval += safe_chr(MARKUP_COLOR, buff, bp); - if (cur->fore == 'n') { - retval += safe_chr(cur->fore, buff, bp); + char *save; + save = *bp; + if (cur.fore == 'n') { + retval += safe_chr(cur.fore, buff, bp); } else { -#define CBIT_SET(x,y) (x->bits & y) +#define CBIT_SET(x,y) (x.bits & y) if (CBIT_SET(cur, CBIT_FLASH)) retval += safe_chr('f', buff, bp); if (CBIT_SET(cur, CBIT_HILITE)) @@ -482,7 +440,7 @@ if (CBIT_SET(cur, CBIT_UNDERSCORE)) retval += safe_chr('u', buff, bp); #undef CBIT_SET -#define CBIT_SET(x,y) (x->offbits & y) +#define CBIT_SET(x,y) (x.offbits & y) if (CBIT_SET(cur, CBIT_FLASH)) retval += safe_chr('F', buff, bp); if (CBIT_SET(cur, CBIT_HILITE)) @@ -493,25 +451,20 @@ retval += safe_chr('U', buff, bp); #undef CBIT_SET - if (cur->fore) - retval += safe_chr(cur->fore, buff, bp); - if (cur->back) - retval += safe_chr(cur->back, buff, bp); + if (cur.fore) + retval += safe_chr(cur.fore, buff, bp); + if (cur.back) + retval += safe_chr(cur.back, buff, bp); } - retval += safe_chr(TAG_END, buff, bp); + if (retval) + *bp = save; return retval; } -/* We need EDGE_UP to return 1 if: - * x has bit set and y's offbit does. - */ -#define EDGE_UP(x,y,z) ((x->bits & z) != (y->bits & z)) -static struct ansi_data ansi_normal = { 0, 0xFF, 'n', 0 }; - void -nest_ansi_data(struct ansi_data *old, struct ansi_data *cur) +nest_ansi_data(ansi_data * old, ansi_data * cur) { if (cur->fore != 'n') { cur->bits |= old->bits; @@ -521,127 +474,127 @@ if (!cur->back) cur->back = old->back; } else { - cur->fore = 0; + cur->bits = 0; + cur->offbits = 0; cur->back = 0; - cur->bits = 0; - cur->offbits = ~0; } } +/* We need EDGE_UP to return 1 if x has bit set and y doesn't. */ +#define EDGE_UP(x,y,z) ((x.bits & z) != (y->bits & z)) int -write_raw_ansi_data(struct ansi_data *old, struct ansi_data *cur, - char *buff, char **bp) +write_raw_ansi_data(ansi_data * old, ansi_data * cur, char *buff, char **bp) { int f = 0; - + ansi_data past = *old; + ansi_data pres = *cur; /* This shouldn't happen */ - if (cur->fore == 'n') { - if (old->bits || (old->fore != 'n') || old->back) { - return safe_str(COL_NORMAL, buff, bp); + if (pres.fore == 'n') { + if (past.bits || (past.fore != 'n') || past.back) { + return safe_str(ANSI_RAW_NORMAL, buff, bp); } } - if (cur->fore == 'd') - cur->fore = 0; - if (cur->back == 'D') - cur->back = 0; + if (pres.fore == 'd') + pres.fore = 0; + if (pres.back == 'D') + pres.back = 0; /* Do we *unset* anything in cur? */ - if ((old->bits & ~(cur->bits)) || - (old->fore && !cur->fore) || (old->back && !cur->back)) { - safe_str(COL_NORMAL, buff, bp); - old = &ansi_normal; + if ((past.bits & ~(pres.bits)) || + (past.fore && !pres.fore) || (past.back && !pres.back)) { + safe_str(ANSI_RAW_NORMAL, buff, bp); + past = ansi_null; } - cur->bits |= old->bits; - cur->bits &= ~cur->offbits; - - if (old->fore == cur->fore && - old->back == cur->back && old->bits == cur->bits) + if (past.fore == pres.fore && past.back == pres.back && + past.bits == pres.bits) return 0; - if (!(cur->fore || cur->back || cur->bits)) { - if (old != &ansi_normal) - return safe_str(COL_NORMAL, buff, bp); + if (!(pres.fore || pres.back || pres.bits)) { + if (past.fore != 'n') + return safe_str(ANSI_RAW_NORMAL, buff, bp); return 0; } safe_str(ANSI_BEGIN, buff, bp); - if (EDGE_UP(old, cur, CBIT_FLASH)) { + if (EDGE_UP(past, cur, CBIT_HILITE)) { if (f++) safe_chr(';', buff, bp); - safe_integer(ansi_codes['f'], buff, bp); + safe_integer(COL_HILITE, buff, bp); } - if (EDGE_UP(old, cur, CBIT_HILITE)) { + if (EDGE_UP(past, cur, CBIT_INVERT)) { if (f++) safe_chr(';', buff, bp); - safe_integer(ansi_codes['h'], buff, bp); + safe_integer(COL_INVERT, buff, bp); } - if (EDGE_UP(old, cur, CBIT_INVERT)) { + if (EDGE_UP(past, cur, CBIT_FLASH)) { if (f++) safe_chr(';', buff, bp); - safe_integer(ansi_codes['i'], buff, bp); + safe_integer(COL_FLASH, buff, bp); } - if (EDGE_UP(old, cur, CBIT_UNDERSCORE)) { + if (EDGE_UP(past, cur, CBIT_UNDERSCORE)) { if (f++) safe_chr(';', buff, bp); - safe_integer(ansi_codes['u'], buff, bp); + safe_integer(COL_UNDERSCORE, buff, bp); } - if (cur->fore && cur->fore != old->fore) { + if (pres.fore && pres.fore != past.fore) { if (f++) safe_chr(';', buff, bp); - safe_integer(ansi_codes[(unsigned char) cur->fore], buff, bp); + safe_integer(ansi_codes[(unsigned char) pres.fore], buff, bp); } - if (cur->back && cur->back != old->back) { + if (pres.back && pres.back != past.back) { if (f++) safe_chr(';', buff, bp); - safe_integer(ansi_codes[(unsigned char) cur->back], buff, bp); + safe_integer(ansi_codes[(unsigned char) pres.back], buff, bp); } return safe_str(ANSI_FINISH, buff, bp); } +#undef EDGE_UP + void -define_ansi_data(struct ansi_data *cur, const char *str) +define_ansi_data(ansi_data * store, const char *str) { - cur->bits = 0; - cur->offbits = 0; - cur->fore = 0; - cur->back = 0; + *store = ansi_null; for (; str && *str && (*str != TAG_END); str++) { switch (*str) { case 'n': /* normal */ - /* This is explicitly normal, it'll never be - * clored */ - cur->bits = 0; - cur->fore = 0; - cur->back = 0; - cur->offbits = ~0; + /* This is explicitly normal, it'll never be colored */ + store->bits = 0; + store->offbits = ~0; + store->fore = 'n'; + store->back = 0; break; case 'f': /* flash */ - cur->bits |= CBIT_FLASH; + store->bits |= CBIT_FLASH; + store->offbits &= ~CBIT_FLASH; break; case 'h': /* hilite */ - cur->bits |= CBIT_HILITE; + store->bits |= CBIT_HILITE; + store->offbits &= ~CBIT_HILITE; break; case 'i': /* inverse */ - cur->bits |= CBIT_INVERT; + store->bits |= CBIT_INVERT; + store->offbits &= ~CBIT_INVERT; break; case 'u': /* underscore */ - cur->bits |= CBIT_UNDERSCORE; + store->bits |= CBIT_UNDERSCORE; + store->offbits &= ~CBIT_UNDERSCORE; break; case 'F': /* flash */ - cur->offbits |= CBIT_FLASH; + store->offbits |= CBIT_FLASH; break; case 'H': /* hilite */ - cur->offbits |= CBIT_HILITE; + store->offbits |= CBIT_HILITE; break; case 'I': /* inverse */ - cur->offbits |= CBIT_INVERT; + store->offbits |= CBIT_INVERT; break; case 'U': /* underscore */ - cur->offbits |= CBIT_UNDERSCORE; + store->offbits |= CBIT_UNDERSCORE; break; case 'b': /* blue fg */ case 'c': /* cyan fg */ @@ -652,7 +605,7 @@ case 'x': /* black fg */ case 'y': /* yellow fg */ case 'd': /* default fg */ - cur->fore = *str; + store->fore = *str; break; case 'B': /* blue bg */ case 'C': /* cyan bg */ @@ -663,12 +616,72 @@ case 'X': /* black bg */ case 'Y': /* yellow bg */ case 'D': /* default fg */ - cur->back = *str; + store->back = *str; break; } } + store->bits &= ~(store->offbits); } +int +read_raw_ansi_data(ansi_data * store, const char *codes) +{ + int curnum; + if (!codes || !store) + return 0; + store->bits = 0; + store->offbits = 0; + store->fore = 0; + store->back = 0; + + /* 'codes' can point at either the ESC_CHAR, + * the '[', or the following byte. */ + + /* Skip to the first ansi number */ + while (*codes && !isdigit((unsigned char) *codes) && *codes != 'm') + codes++; + + while (*codes && (*codes != 'm')) { + curnum = atoi(codes); + if (curnum < 10) { + switch (curnum) { + case COL_HILITE: + store->bits |= CBIT_HILITE; + store->offbits &= ~CBIT_HILITE; + break; + case COL_UNDERSCORE: + store->bits |= CBIT_UNDERSCORE; + store->offbits &= ~CBIT_UNDERSCORE; + break; + case COL_FLASH: + store->bits |= CBIT_FLASH; + store->offbits &= ~CBIT_FLASH; + break; + case COL_INVERT: + store->bits |= CBIT_INVERT; + store->offbits &= ~CBIT_INVERT; + break; + case COL_NORMAL: + store->bits = 0; + store->offbits = ~0; + store->fore = 'n'; + store->back = 0; + break; + } + } else if (curnum < 40) { + store->fore = ansi_chars[curnum]; + } else if (curnum < 50) { + store->back = ansi_chars[curnum]; + } + /* Skip current and find the next ansi number */ + while (*codes && isdigit((unsigned char) *codes)) + codes++; + while (*codes && !isdigit((unsigned char) *codes) && (*codes != 'm')) + codes++; + } + return 1; +} + /** Return a string pointer past any ansi/html markup at the start. * \param p a string. * \return pointer to string after any initial ansi/html markup. @@ -723,37 +736,6 @@ } } -/* Is this string an old style format? */ -static int -is_ansi_oldstyle(const char *source) -{ - const char *ptr; - /* First test: ESC_CHAR. If there's one this is old style. */ - if (strchr(source, ESC_CHAR) != NULL) - return 1; - /* This usually means a TAG_START appears, but no ESC_CHAR. */ - if (strstr(source, MARKUP_START MARKUP_HTML_STR "/") || - strstr(source, MARKUP_START MARKUP_COLOR_STR "/")) { - /* There's a
*/ - if (strncasecmp(ptr + 1, "PRE", 3) == 0) - return 1; - /* And
or
*/
- if (!isalnum((unsigned char) *(ptr + 2)))
- return 1;
- }
- return 0;
-}
-
/** Convert a string into an ansi_string.
* This takes a string that may contain ansi/html markup codes and
* converts it to an ansi_string structure that separately stores
@@ -764,42 +746,44 @@
ansi_string *
parse_ansi_string(const char *source)
{
- return parse_ansi_string_real(source, 0);
+ return real_parse_ansi_string(source);
}
-/** Convert a string into an ansi_string.
- * This takes a string that may contain ansi/html markup codes and
- * converts it to an ansi_string structure that separately stores
- * the plain string and the markup codes for each character.
- * \param source string to parse.
- * \param oldstyle If true, treats it as an old style ansi string.
- * \return pointer to an ansi_string structure representing the src string.
- */
+/* This does the actual work of parse_ansi_string. */
ansi_string *
-parse_ansi_string_real(const char *source, int oldstyle)
+real_parse_ansi_string(const char *source)
{
ansi_string *data = NULL;
char src[BUFFER_LEN], *sptr;
char tagbuff[BUFFER_LEN];
char *ptr, *txt;
+ ansi_data *col;
char *tmp;
char type;
- int i, j;
+
+ int pos = 0;
+ int num = 0;
+
int priority = 0;
markup_information *info;
+ ansi_data ansistack[BUFFER_LEN];
+ ansistack[0] = ansi_null;
+ int stacktop = 0;
+
+ ansi_data tmpansi;
+ int oldcodes = 0;
+
+
if (!source)
return NULL;
- if (oldstyle == 2)
- oldstyle = is_ansi_oldstyle(source);
info = NULL;
sptr = src;
safe_str(source, src, &sptr);
*sptr = '\0';
-
data = mush_malloc(sizeof(ansi_string), "ansi_string");
if (!data)
return NULL;
@@ -808,23 +792,45 @@
memset(data, 0, sizeof(ansi_string));
txt = data->text;
- i = 0;
+ col = data->ansi;
+
for (ptr = src; *ptr;) {
- /* Is this an ansi sequence? */
switch (*ptr) {
case TAG_START:
/* In modern Penn, this is both Pueblo/HTML and color defining code */
- /* Find the end. */
+
for (tmp = ptr; *tmp && *tmp != TAG_END; tmp++) ;
- if (*tmp) {
- *(tmp) = '\0';
- } else {
- /* Point tmp at the end */
+ if (*tmp)
+ *tmp = '\0';
+ else
tmp--;
- }
ptr++;
- type = oldstyle ? MARKUP_HTML : *(ptr++);
+ /* Now ptr is at TAG_START and tmp is at TAG_END (nulled) */
+
+ type = *(ptr++);
switch (type) {
+ case MARKUP_COLOR:
+ if (!*ptr)
+ break;
+ if (oldcodes == 1) {
+ oldcodes = 0;
+ stacktop--;
+ }
+ /* Start or end tag? */
+ if (*ptr != '/') {
+ define_ansi_data(&tmpansi, ptr);
+ nest_ansi_data(&(ansistack[stacktop]), &tmpansi);
+ stacktop++;
+ ansistack[stacktop] = tmpansi;
+ } else {
+ if (*(ptr + 1) == 'a') {
+ stacktop = 0; /* Endall tag */
+ } else {
+ if (stacktop > 0)
+ stacktop--;
+ }
+ }
+ break;
case MARKUP_HTML:
if (*ptr && *ptr != '/') {
/* We're at the start tag. */
@@ -833,19 +839,19 @@
snprintf(tagbuff, BUFFER_LEN, "/%s", parse_tagname(ptr));
info->stop_code = mush_strdup(tagbuff, "markup_code");
info->type = MARKUP_HTML;
- info->start = i;
+ info->start = pos;
info->end = -1;
info->priority = priority++;
} else if (*ptr) {
/* Closing tag */
- for (j = data->nmarkups - 1; j >= 0; j--) {
- if (data->markup[j].end < 0 && data->markup[j].stop_code &&
- strcasecmp(data->markup[j].stop_code, ptr) == 0) {
+ for (num = data->nmarkups - 1; num >= 0; num--) {
+ if (data->markup[num].end < 0 && data->markup[num].stop_code &&
+ strcasecmp(data->markup[num].stop_code, ptr) == 0) {
break;
}
}
- if (j >= 0) {
- data->markup[j].end = i;
+ if (num >= 0) {
+ data->markup[num].end = pos;
} else {
/* This is greviously wrong, we can't find the begin tag?
* Consider this a standalone tag with no close tag. */
@@ -855,35 +861,10 @@
info->type = MARKUP_HTML;
info->priority = priority++;
info->start = -1;
- info->end = i;
+ info->end = pos;
}
}
break;
- case MARKUP_COLOR:
- if (*ptr && *ptr != '/') {
- /* We're at the start tag. */
- j = data->nmarkups++;
- data->markup[j].start_code = NULL;
- data->markup[j].stop_code = NULL;
- data->markup[j].type = MARKUP_COLOR;
- data->markup[j].priority = priority++;
- define_ansi_data(&(data->markup[j].ansi), ptr);
- data->markup[j].start = i;
- data->markup[j].end = -1;
- } else if (*ptr) {
- int endall = (*(ptr + 1) == 'a');
- /* Closing tag. For markup color, this means we
- * close the last opened tag. */
- for (j = (data->nmarkups) - 1; j >= 0; j--) {
- if (data->markup[j].end < 0 && data->markup[j].type == MARKUP_COLOR) {
- data->markup[j].end = i;
- /* If it's not ENDALL, break */
- if (!endall)
- break;
- }
- }
- }
- break;
default:
/* This is a broken string. Are we near or at buffer_len? */
if (ptr - source < BUFFER_LEN - 4) {
@@ -896,90 +877,52 @@
ptr++;
break;
case ESC_CHAR:
- /* I'm getting rid of ESC_CHAR. Still, pretend it's
- * a proper "opening" one, unless it's normal,
- * in which case we ether close all extant, or
- * open a 'normal'.
- *
- * If we open a new one, find all old open ones that
- * it overwrites (rather than modifies) */
+ /* ESC_CHAR tags shouldn't be used anymore, so hopefully
+ * we won't get here.
+ * To parse these, we assume they can't have the new tag-style
+ * ANSI codes in them, as this should always be true when loading
+ * from attributes. Assuming that, this code is separate from the
+ * "actual" tags, creating a single temporary holding space on the
+ * top of the ansi-stack and playing with the colors there.
+ */
for (tmp = ptr; *tmp && *tmp != 'm'; tmp++) ;
- if (strcmp(ptr, COL_NORMAL) != 0) {
- struct ansi_data cur;
- read_raw_ansi_data(&cur, ptr);
- /* Close any we can */
- for (j = (data->nmarkups) - 1; j >= 0; j--) {
- if (data->markup[j].type == MARKUP_COLOR_OLD) {
- cur.bits |= data->markup[j].ansi.bits;
- data->markup[j].type = MARKUP_COLOR;
- data->markup[j].end = i;
- }
- }
- /* We're at a start tag. */
- j = data->nmarkups++;
- data->markup[j].start_code = NULL;
- data->markup[j].stop_code = NULL;
- data->markup[j].type = MARKUP_COLOR_OLD;
- data->markup[j].priority = priority++;
- data->markup[j].ansi = cur;
- data->markup[j].start = i;
- data->markup[j].end = -1;
- } else {
- int found = 0;
- /* Closing tag. For markup color, this means we
- * close the last opened tag. */
- for (j = (data->nmarkups) - 1; j >= 0; j--) {
- if (data->markup[j].type == MARKUP_COLOR_OLD) {
- data->markup[j].type = MARKUP_COLOR;
- data->markup[j].end = i;
- found = 1;
- }
- }
- /* Is it an "opening" ansi_normal tag? */
- if (!found) {
- j = data->nmarkups++;
- data->markup[j].start_code = NULL;
- data->markup[j].stop_code = NULL;
- data->markup[j].type = MARKUP_COLOR_OLD;
- data->markup[j].priority = priority++;
- data->markup[j].ansi.bits = 0;
- data->markup[j].ansi.offbits = 0;
- data->markup[j].ansi.fore = 'n';
- data->markup[j].ansi.back = 0;
- data->markup[j].start = i;
- data->markup[j].end = -1;
- }
+
+ /* Store the "background" colors */
+ tmpansi = ansistack[stacktop];
+ if (oldcodes == 0) {
+ oldcodes = 1;
+ stacktop++;
+ ansistack[stacktop] = tmpansi;
+ ansistack[stacktop].offbits = 0;
}
+
+ read_raw_ansi_data(&tmpansi, ptr);
+ ansistack[stacktop].bits |= tmpansi.bits;
+ ansistack[stacktop].bits &= ~(tmpansi.offbits); /* ANSI_RAW_NORMAL */
+ if (tmpansi.fore)
+ ansistack[stacktop].fore = tmpansi.fore;
+ if (tmpansi.back)
+ ansistack[stacktop].back = tmpansi.back;
+
ptr = tmp;
if (*tmp)
ptr++;
break;
default:
- txt[i++] = *(ptr++);
+ col[pos] = ansistack[stacktop];
+ txt[pos++] = *(ptr++);
}
}
- txt[i] = '\0';
- data->len = i;
+ txt[pos] = '\0';
+ data->len = pos;
/* For everything left on the stack:
- * If it's an ANSI code, close it with COL_NORMAL and i.
* If it's an HTML code, assume it's a standalone, and leave
* its stop point where it is. */
- for (j = 0; j < data->nmarkups; j++) {
- info = &(data->markup[j]);
+ for (num = 0; num < data->nmarkups; num++) {
+ info = &(data->markup[num]);
switch (info->type) {
- case MARKUP_COLOR_OLD:
- info->type = MARKUP_COLOR;
- case MARKUP_COLOR:
- /* If it's ANSI, we assume it affects the whole string */
- /* Sucks, but ... */
- if (info->end < 0)
- info->end = i;
- if (info->end == info->start) {
- info->end = info->start = -1;
- }
- break;
case MARKUP_HTML:
/* If it's HTML, we treat it as standalone (,
, etc)
* This is ugly - it's not a "start" but a "stop" */
@@ -995,13 +938,13 @@
}
return data;
broken_string:
- /* This stinks. We treat this as if it's not ansi safe */
+ /* This stinks. We treat this as if it's not pueblo-safe */
if (data == NULL)
return NULL;
strncpy(data->text, source, BUFFER_LEN);
data->len = strlen(data->text);
- for (i = data->nmarkups - 1; i >= 0; i--) {
- free_markup_info(&(data->markup[i]));
+ for (num = data->nmarkups - 1; num >= 0; num--) {
+ free_markup_info(&(data->markup[num]));
}
data->nmarkups = 0;
return data;
@@ -1016,19 +959,24 @@
{
int i, j;
markup_information *info;
- char tmp;
+ char tmptext;
+ ansi_data tmpansi;
int mid;
int len = as->len;
/* Reverse the text */
mid = len / 2; /* Midpoint */
for (i = len - 1, j = 0; i >= mid; j++, i--) {
- tmp = as->text[i];
+ tmptext = as->text[i];
as->text[i] = as->text[j];
- as->text[j] = tmp;
+ as->text[j] = tmptext;
+
+ tmpansi = as->ansi[i];
+ as->ansi[i] = as->ansi[j];
+ as->ansi[j] = tmpansi;
}
- /* Now reverse the markup. */
+ /* Now reverse the html-markup. */
for (i = as->nmarkups - 1; i >= 0; i--) {
int start, end;
info = &(as->markup[i]);
@@ -1061,10 +1009,6 @@
mush_free(as, "ansi_string");
}
-/* Compress the markup information in an ansi_string.
- *
- * This combines adjacent identical markup.
- */
static int
compare_starts(const void *a, const void *b)
@@ -1077,111 +1021,117 @@
return ai->start - bi->start;
}
+
+
void
optimize_ansi_string(ansi_string *as)
{
int i, j;
- /* Nothing to optimize if we've only got 1 or none. */
- if (as->nmarkups > 1) {
+ if (!as)
+ return;
+
+ /* If we've only got 1 or 0, or we've already optimized, do nothing. */
+ if (as->nmarkups > 1 && as->optimized == 0) {
/* Sort the markup codes by their start position */
qsort(as->markup, as->nmarkups, sizeof(markup_information), compare_starts);
for (i = 0; i < as->nmarkups; i++) {
- /* If start and end are negative, it's a standalone (img) */
- if (as->markup[i].start == -1 && as->markup[i].end == -1)
+
+ /* If end is negative, it's removed or broken. Either way... */
+ if (as->markup[i].end < 0)
continue;
+
for (j = i + 1; j < as->nmarkups; j++) {
+
/* Already removed? */
- if (as->markup[j].start == -1 && as->markup[j].end == -1)
+ if (as->markup[j].end < 0 && as->markup[j].start < 0)
continue;
- if (as->markup[j].start > as->markup[i].end)
- break; /* Far apart */
- if (as->markup[i].type == MARKUP_COLOR &&
- as->markup[j].type == MARKUP_COLOR &&
- (as->markup[i].ansi.bits == as->markup[j].ansi.bits &&
- as->markup[i].ansi.offbits == as->markup[j].ansi.offbits &&
- as->markup[i].ansi.fore == as->markup[j].ansi.fore &&
- as->markup[i].ansi.back == as->markup[j].ansi.back)
+
+ /* End if we can't stretch markup[i] any farther */
+ if (as->markup[i].end < as->markup[j].start)
+ break;
+
+ /* If there's an identical code within our bounds, merge it. */
+ if (as->markup[i].start_code && as->markup[j].start_code &&
+ (strcmp(as->markup[j].start_code, as->markup[i].start_code) == 0)
) {
if (as->markup[j].end > as->markup[i].end)
as->markup[i].end = as->markup[j].end;
- if (as->markup[j].start < as->markup[i].start)
- as->markup[i].start = as->markup[j].start;
- if (as->markup[j].end > as->markup[i].end)
- as->markup[j].start = -1;
- as->markup[j].end = -1;
- } else if ((as->markup[i].start_code && as->markup[j].start_code) &&
- strcmp(as->markup[j].start_code,
- as->markup[i].start_code) == 0) {
- /* i and j are adjacent and identical */
- if (as->markup[j].end > as->markup[i].end)
- as->markup[i].end = as->markup[j].end;
- if (as->markup[j].start < as->markup[i].start)
- as->markup[i].start = as->markup[j].start;
as->markup[j].start = -1;
as->markup[j].end = -1;
- }
- }
- }
+ } /* end if_indentical */
+ } /* end inner loop */
+ } /* end outer loop */
}
+ /* end if_optimized */
+ int target = -1;
+ int len = 0;
+ j = 0;
- /* Get rid of all removed markups */
- for (i = 0, j = 0; i < as->nmarkups; i++) {
- if ((as->markup[i].end >= 0) && (as->markup[i].start != as->markup[i].end)) {
- if (i != j) {
- memmove(&(as->markup[j]), &(as->markup[i]), sizeof(markup_information));
- }
+ /* Get rid of all removed markups
+ * "target" is non-negative when we've pegged a destination
+ * "len" begins counting when we have a target set and we hit a
+ * block of non-removed markup
+ * If len is non-zero and we hit a removed markup, shift the block left.
+ * The end of the removed string is our new target (it's removable anyway)
+ */
+
+ for (i = 0; i < as->nmarkups; i++) {
+ /* Valid tag? */
+ if (as->markup[i].end >= 0 && (as->markup[i].start < as->markup[i].end)) {
+ if (target != -1)
+ len++;
j++;
} else {
free_markup_info(&(as->markup[i]));
+ if (len > 0 && target != -1)
+ memmove(&(as->markup[target]), &(as->markup[i - len]),
+ len * sizeof(markup_information));
+ if (len > 0 || target == -1) {
+ target = j;
+ len = 0;
+ }
}
}
+ if (len > 0)
+ memmove(&(as->markup[target]), &(as->markup[i - len]),
+ len * sizeof(markup_information));
as->nmarkups = j;
+ as->optimized = 1;
}
-/* Copy the start code for a particular markup_info
- * For HTML/Pueblo, this inserts TAG_START and TAG_END
- * Otherwise it's just a plain copy */
+/* Copy the start code for a particular markup_info */
static int
copy_start_code(markup_information *info, char *buff, char **bp)
{
int retval = 0;
char *save;
save = *bp;
- if (info->start_code) {
+ if (info && info->start_code) {
retval += safe_chr(TAG_START, buff, bp);
retval += safe_chr(info->type, buff, bp);
retval += safe_str(info->start_code, buff, bp);
retval += safe_chr(TAG_END, buff, bp);
- } else if (info->type == MARKUP_COLOR) {
- retval += write_ansi_data(&(info->ansi), buff, bp);
}
if (retval)
*bp = save;
return retval;
}
-/* Copy the stop code for a particular markup_info
- * For HTML/Pueblo, this inserts TAG_START and TAG_END
- * Otherwise it's just a plain copy */
+/* Copy the stop code for a particular markup_info */
static int
copy_stop_code(markup_information *info, char *buff, char **bp)
{
int retval = 0;
char *save;
save = *bp;
- if (info->type == MARKUP_HTML && (info->stop_code != NULL)) {
+ if (info && info->stop_code) {
retval += safe_chr(TAG_START, buff, bp);
retval += safe_chr(MARKUP_HTML, buff, bp);
retval += safe_str(info->stop_code, buff, bp);
retval += safe_chr(TAG_END, buff, bp);
- } else if (info->type == MARKUP_COLOR) {
- retval += safe_chr(TAG_START, buff, bp);
- retval += safe_chr(MARKUP_COLOR, buff, bp);
- retval += safe_chr('/', buff, bp);
- retval += safe_chr(TAG_END, buff, bp);
}
if (retval)
*bp = save;
@@ -1205,13 +1155,6 @@
" %d (%s): (start: %d end: %d) start_code: %s stop_code: %s",
count++, (info->type == MARKUP_HTML ? "html" : "ansi"),
info->start, info->end, info->start_code, info->stop_code);
- } else {
- notify_format(who,
- " %d (%s): (start: %d end: %d) bits: %d fore: %c back: %c",
- count++, (info->type == MARKUP_HTML ? "html" : "ansi"),
- info->start, info->end, info->ansi.bits,
- (info->ansi.fore) ? info->ansi.fore : '-',
- (info->ansi.back) ? info->ansi.back : '-');
}
}
notify_format(who, "Inspecting ansi string complete");
@@ -1233,20 +1176,30 @@
int end;
markup_information *dm;
- if (start >= as->len)
+ /* Nothing to delete */
+ if (start >= as->len || count <= 0)
return 0;
- if (count <= 0)
- return 0;
+
+ /* We can't delete from a negative index */
+ if (start < 0) {
+ /* start is negative: this *decreases* count. */
+ count += start;
+ start = 0;
+ }
+
+ /* We can't delete past the end of our string */
if ((start + count) > as->len)
count = as->len - start;
+ /* Nothing to delete */
+ if (count <= 0)
+ return 0;
+
end = start + count;
- as->optimized = 0;
-
dm = as->markup;
- /* Remove or shrink the markup on dst */
+ /* Remove or shrink the Pueblo markup on dst */
for (i = 0; i < as->nmarkups; i++) {
if (dm[i].start >= start && dm[i].end <= end) {
dm[i].start = -1;
@@ -1264,10 +1217,16 @@
}
}
+ if (as->nmarkups > 0)
+ as->optimized = 0;
+
/* Shift text over */
memmove(as->text + start, as->text + end, as->len - end);
+ memmove(as->ansi + start, as->ansi + end,
+ (as->len - end) * sizeof(ansi_data));
as->len -= count;
as->text[as->len] = '\0';
+ as->ansi[as->len] = ansi_null;
return 0;
}
@@ -1275,120 +1234,147 @@
do { \
x.type = y.type; \
x.priority = y.priority; \
- x.start_code = NULL; \
- x.stop_code = NULL; \
if (y.start_code) x.start_code = mush_strdup(y.start_code,"markup_code"); \
else (x.start_code = NULL); \
if (y.stop_code) x.stop_code = mush_strdup(y.stop_code,"markup_code"); \
else (x.stop_code = NULL); \
- x.ansi.bits = y.ansi.bits; \
- x.ansi.offbits = y.ansi.offbits; \
- x.ansi.fore = y.ansi.fore; \
- x.ansi.back = y.ansi.back; \
} while (0)
/** Insert an ansi string into another ansi_string
* with markups kept as straight as possible.
* \param dst ansi_string to insert into.
- * \param loc Location to insert into, 0-indexed
+ * \param loc Location to insert into, 0-indexed
* \param src ansi_string to insert
- * \param start start point in src
- * \param size length of string from src
* \retval 0 success
- * \reval 1 failure.
+ * \retval 1 failure.
*/
int
-ansi_string_insert(ansi_string *dst, int loc,
- ansi_string *src, int start, int count)
+ansi_string_insert(ansi_string *dst, int loc, ansi_string *src)
{
int i, j;
int len;
- int end, m_end;
- int rval = 0;
+ int retval = 0;
+ int src_len = src->len;
+
markup_information *dm, *sm;
- if (loc >= dst->len)
- loc = dst->len;
- if (start >= src->len)
+ /* If src->len == 0, we might have only markup. Stand-alones. Ew! */
+ if (src->len <= 0 && src->nmarkups <= 0)
return 0;
- if (count <= 0)
- return 0;
- if ((start + count) > src->len)
- count = src->len - start;
+ if (dst->len >= BUFFER_LEN)
+ return 1;
- dst->optimized = 0;
+ if (src_len >= BUFFER_LEN)
+ src_len = BUFFER_LEN - 1;
+ if (src_len < 0)
+ src_len = 0;
+ if (loc > dst->len)
+ loc = dst->len;
+ if (loc < 0)
+ loc = 0;
+
+ if (dst->nmarkups > 0 || src->nmarkups > 0)
+ dst->optimized = 0;
+
dm = dst->markup;
sm = src->markup;
- /* End in src */
- end = start + count;
-
- /* Starting location */
- if (loc <= 0)
- loc = 0;
- if (loc >= dst->len)
- loc = dst->len;
-
- /* End of src's insert location in dst */
- m_end = loc + count;
-
- /* shift or widen the markup on dst */
+ /* shift or widen the Pueblo markup on dst */
for (i = 0; i < dst->nmarkups; i++) {
- if (dm[i].start >= loc)
- dm[i].start += count;
- if (dm[i].end > loc)
- dm[i].end += count;
+ if (loc <= dm[i].start)
+ dm[i].start += src_len;
+ if (loc < dm[i].end)
+ dm[i].end += src_len;
}
- /* Copy markup */
+
+ /* Copy markup onto the end of dst */
for (j = 0; j < src->nmarkups; j++) {
- /* It's possible, but not at all easy, to get this much ansi markup */
+
+ /* It's possible, but not at all easy, to get this much Pueblo markup */
if (i >= BUFFER_LEN)
break;
- if (((sm[j].start <= end && sm[j].end >= start) && sm[j].start >= 0) ||
- (sm[j].end > start && sm[j].end <= end)) {
+
+ /* Is it a valid tag? */
+ if (sm[j].end >= 0 && sm[j].start < sm[j].end &&
+ ((sm[j].start < 0 && sm[j].end <= src_len)
+ || (sm[j].start < src_len && sm[j].start >= 0))) {
+
copyto(dm[i], sm[j]);
- if (sm[j].start < 0) {
+
+ /* If our start is non-negative, start+loc is its position in dm */
+ if (sm[j].start >= 0)
+ dm[i].start = sm[j].start + loc;
+ else
dm[i].start = -1;
- } else {
- dm[i].start = loc + sm[j].start - start;
- if (dm[i].start < loc)
- dm[i].start = loc;
- }
- dm[i].end = loc + sm[j].end - start;
- if (dm[i].end >= m_end)
- dm[i].end = m_end;
+
+ /* Make sure the end position is within the bounds of its own string */
+ if (sm[j].end <= src_len)
+ dm[i].end = sm[j].end + loc;
+ else
+ dm[i].end = src_len + loc;
+
i++;
}
}
+
+ for (; i >= BUFFER_LEN; i--)
+ free_markup_info(&(dm[i]));
dst->nmarkups = i;
- dst->len += count;
+ dst->len += src_len;
if (dst->len >= BUFFER_LEN) {
- rval = 1;
+ retval = 1;
dst->len = BUFFER_LEN - 1;
}
- len = dst->len - m_end;
+ len = dst->len - src->len - loc;
+
+ /* Determine what old ansi might stretch across the new text.
+ * This sets backansi to any ansi values (bits, colors) that
+ * are continuous across an entire length of text. */
+ ansi_data backansi = ansi_null;
+ if (0 < loc && loc < dst->len) {
+ backansi.offbits = dst->ansi[loc - 1].offbits & dst->ansi[loc].offbits;
+ backansi.bits = dst->ansi[loc - 1].bits & dst->ansi[loc].bits;
+ if (dst->ansi[loc - 1].fore == dst->ansi[loc].fore)
+ backansi.fore = dst->ansi[loc].fore;
+ if (dst->ansi[loc - 1].back == dst->ansi[loc].back)
+ backansi.back = dst->ansi[loc].back;
+ }
+
/* Shift text over */
- if (len > 0) {
- if (m_end + len >= BUFFER_LEN) {
- len = (BUFFER_LEN - m_end - loc - 1);
- memmove(dst->text + m_end, dst->text + loc, len);
- } else {
- memmove(dst->text + m_end, dst->text + loc, len);
- }
+ if (0 < len) {
+ /* The length beyond our insertion that can *actually* be moved. */
+ if (loc + src_len + len >= BUFFER_LEN)
+ len = BUFFER_LEN - loc - src_len - len - 1;
+ memmove(dst->text + loc + src_len, dst->text + loc, len);
+ memmove(dst->ansi + loc + src_len, dst->ansi + loc,
+ len * sizeof(ansi_data));
}
+
/* Copy text from src */
- if (loc + count >= BUFFER_LEN)
- count = BUFFER_LEN - 1 - loc;
- memcpy(dst->text + loc, src->text + start, count);
+ if (loc + src_len >= BUFFER_LEN)
+ src_len = BUFFER_LEN - 1 - loc;
+ if (src_len > 0) {
+ memcpy(dst->text + loc, src->text, src_len);
+ for (i = 0; i < src_len; i++) {
+ j = loc + i;
+ dst->ansi[j].back = src->ansi[i].back ? src->ansi[i].back : backansi.back;
+ dst->ansi[j].fore = src->ansi[i].fore ? src->ansi[i].fore : backansi.fore;
+ dst->ansi[j].offbits = src->ansi[i].offbits | backansi.offbits;
+ dst->ansi[j].bits =
+ ~(dst->ansi[j].offbits) & (src->ansi[i].bits | backansi.bits);
+ }
+ }
dst->text[dst->len] = '\0';
- return rval;
+ dst->ansi[dst->len] = ansi_null;
+ return retval;
}
+
/** Replace a portion of an ansi string with
* another ansi string, keeping markups as
* straight as possible.
@@ -1396,116 +1382,123 @@
* \param loc Location to insert into, 0-indexed
* \param len Length of string inside dst to replace
* \param src ansi_string to insert
- * \param start start point in src
- * \param size length of string from src
* \retval 0 success
- * \reval 1 failure.
+ * \retval 1 failure.
*/
int
-ansi_string_replace(ansi_string *dst, int loc, int len,
- ansi_string *src, int start, int count)
+ansi_string_replace(ansi_string *dst, int loc, int len, ansi_string *src)
{
int i, j;
- int end, m_end, s_end;
+ int end, d_end;
int diff;
- int rval = 0;
+ int retval = 0;
markup_information *dm, *sm;
+ if (loc < 0)
+ loc = 0;
+
/* Is it really an insert? */
- if (loc >= dst->len || len == 0) {
- return ansi_string_insert(dst, loc, src, start, count);
- }
- /* Boundaries */
- if (start <= 0)
- start = 0;
- if ((start + count) > src->len)
- count = src->len - start;
- if ((len + loc) > dst->len)
- len = dst->len - loc;
+ if (loc >= dst->len || len <= 0)
+ return ansi_string_insert(dst, loc, src);
+
/* Is it really a delete? */
- if ((start >= src->len) || (count <= 0)) {
+ if (src->len <= 0)
return ansi_string_delete(dst, loc, len);
- }
- /* Starting location */
- if (loc <= 0)
- loc = 0;
- if (loc >= dst->len)
- loc = dst->len;
+ /* We can't delete past the end of our string. */
+ if ((len + loc) > dst->len)
+ len = dst->len - loc;
- end = loc + len;
- diff = count - len;
- m_end = loc + count;
+ end = loc + len; /* End of the removed section */
+ d_end = loc + src->len; /* End of the new string within the dst string */
+ diff = src->len - len; /* Total change in length */
+ if (diff > 0 && dst->len >= BUFFER_LEN)
+ return 1;
+
dst->optimized = 0;
dm = dst->markup;
sm = src->markup;
- /* Modify, remove, stretch, and mangle markup */
+ /* Modify, remove, stretch, and mangle Pueblo markup */
for (i = 0; i < dst->nmarkups; i++) {
+
/* If it doesn't cross into the replaced part, leave as is */
if (dm[i].end <= loc)
continue;
- if (dm[i].start == loc && dm[i].end == end) {
- /* Debatable: If it surrounds the replaced part exactly,
- * keep it, stretching it to wrap around the replacement */
- dm[i].end = m_end;
- } else if (dm[i].start <= loc && dm[i].end >= end) {
- dm[i].end += diff;
- if (dm[i].end > BUFFER_LEN)
- dm[i].end = BUFFER_LEN;
- } else if (dm[i].start >= loc && dm[i].end <= end) {
- /* If it's completely inside the removed area, remove it */
- dm[i].start = -1;
- dm[i].end = -1;
- } else if (dm[i].start < loc && dm[i].end < end) {
- /* If it ends inside, but begins to the left, push end left. */
- if (dm[i].start >= 0)
- dm[i].end = loc;
- else /* Standalone is inside */
+
+ /* Debatable: If it surrounds the replaced part exactly, try
+ * to keep it, stretching it to wrap around the replacement. */
+ if (loc <= dm[i].start && dm[i].end <= end) {
+ /* If the locations match, stretch it, otherwise overwrite it. */
+ if (dm[i].start == loc && dm[i].end == end) {
+ dm[i].end = loc + src->len;
+ } else {
+ dm[i].start = -1;
dm[i].end = -1;
- } else if (dm[i].end > end && dm[i].start < end) {
- /* If it begins inside, but ends to the right, push start right. */
- if (dm[i].start >= 0)
- dm[i].start = m_end;
- dm[i].end += diff;
- } else if (dm[i].start > end) {
- /* It's to the right */
- dm[i].start += diff;
- dm[i].end += diff;
- } else {
- /* Shift */
- if (dm[i].start > loc)
+ }
+ continue;
+ }
+
+ /* Shift the beginning if necessary */
+ if (loc < dm[i].start) {
+ /* If we're beyond the markup, just shift start. */
+ if (end < dm[i].start) {
dm[i].start += diff;
- dm[i].end += diff;
+ } else {
+ /* Otherwise it's inside; push it to the right of the new markup. */
+ dm[i].start = loc + src->len;
+ }
+ if (dm[i].start > BUFFER_LEN) {
+ dm[i].start = -1;
+ dm[i].end = -1;
+ }
}
+
+ /* If this markup ends before the new one, squish it. */
+ if (dm[i].end < end) {
+ dm[i].end = (dm[i].start >= 0) ? loc : -1; /* Or erase stand-alones */
+ } else {
+ dm[i].end += diff; /* Otherwise, shift it. */
+ if (dm[i].end > BUFFER_LEN)
+ dm[i].end = BUFFER_LEN;
+ }
+
}
- s_end = start + count;
- /* Copy markup */
+ /* Copy markup. Code taken from ansi_string_insert. */
for (j = 0; j < src->nmarkups; j++) {
- /* It's possible, but not at all easy, to get this much ansi markup */
+
+ /* It's possible, but not at all easy, to get this much pueblo markup */
if (i >= BUFFER_LEN)
break;
- if (((sm[j].start <= s_end && sm[j].end >= start) && sm[j].start >= 0) ||
- (sm[j].end > start && sm[j].end <= s_end)) {
+
+ /* Is it a valid tag? */
+ if (sm[j].end >= 0 && sm[j].start < sm[j].end &&
+ ((sm[j].start < 0 && sm[j].end <= src->len)
+ || (sm[j].start < src->len && sm[j].start >= 0))) {
+
copyto(dm[i], sm[j]);
- if (sm[j].start < 0) {
+
+ /* If our start is non-negative, start+loc is its position in dm */
+ if (sm[j].start >= 0)
+ dm[i].start = sm[j].start + loc;
+ else
dm[i].start = -1;
- } else {
- dm[i].start = loc + sm[j].start - start;
- if (dm[i].start < loc)
- dm[i].start = loc;
- }
- dm[i].end = loc + sm[j].end - start;
- if (dm[i].end >= m_end)
- dm[i].end = m_end;
+
+ /* Make sure the end position is within the bounds of its own string */
+ if (sm[j].end <= src->len)
+ dm[i].end = sm[j].end + loc;
+ else
+ dm[i].end = src->len + loc;
+
i++;
}
}
- if (i >= BUFFER_LEN)
- i = BUFFER_LEN - 1;
+
+ for (; i >= BUFFER_LEN; i--)
+ free_markup_info(&(dm[i]));
dst->nmarkups = i;
/* length of original string after replace bits */
@@ -1513,30 +1506,131 @@
dst->len += diff;
if (dst->len >= BUFFER_LEN) {
- rval = 1;
+ retval = 1;
dst->len = BUFFER_LEN - 1;
}
+ /* Determine what old ansi might stretch across the new text.
+ * This sets backansi to any ansi values (bits, colors) that
+ * are continuous across an entire length of text.
+ */
+ ansi_data backansi = dst->ansi[loc];
+ for (i = loc; i < end && !ansi_isnull(backansi); i++) {
+ backansi.offbits &= dst->ansi[i].offbits;
+ backansi.bits &= dst->ansi[i].bits;
+ if (backansi.fore != dst->ansi[i].fore)
+ backansi.fore = 0;
+ if (backansi.back != dst->ansi[i].back)
+ backansi.back = 0;
+ }
+
/* Shift text over */
if (diff != 0) {
- if (m_end + len >= BUFFER_LEN) {
- len = BUFFER_LEN - (1 + m_end);
- if (len > 0) {
- memmove(dst->text + m_end, dst->text + end, len);
- }
- } else {
- memmove(dst->text + m_end, dst->text + end, len);
+ if (d_end + len >= BUFFER_LEN)
+ len = BUFFER_LEN - (1 + d_end);
+ if (len > 0) {
+ memmove(dst->text + d_end, dst->text + end, len);
+ memmove(dst->ansi + d_end, dst->ansi + end, len * sizeof(ansi_data));
}
}
/* Copy text from src */
- if (loc + count >= BUFFER_LEN)
- count = BUFFER_LEN - (1 + loc);
- memcpy(dst->text + loc, src->text + start, count);
+ len = src->len;
+ if (loc + len >= BUFFER_LEN)
+ len = BUFFER_LEN - loc - 1;
+ if (len > 0) {
+ memcpy(dst->text + loc, src->text, len);
+ for (i = 0; i < len; i++) {
+ j = loc + i;
+ dst->ansi[j].back = src->ansi[i].back ? src->ansi[i].back : backansi.back;
+ dst->ansi[j].fore = src->ansi[i].fore ? src->ansi[i].fore : backansi.fore;
+ dst->ansi[j].offbits = src->ansi[i].offbits | backansi.offbits;
+ dst->ansi[j].bits =
+ ~(dst->ansi[j].offbits) & (src->ansi[i].bits | backansi.bits);
+ }
+ }
dst->text[dst->len] = '\0';
- return rval;
+ dst->ansi[dst->len] = ansi_null;
+ return retval;
}
+
+/** Scrambles an ansi_string, returning a pointer to the new string.
+ * \param as ansi_string to scramble.
+ */
+ansi_string *
+scramble_ansi_string(ansi_string *as)
+{
+ int i, j, k;
+ int pos[BUFFER_LEN];
+ markup_information *dm;
+ ansi_string *tmp = NULL;
+
+ if (!as)
+ return NULL;
+
+ optimize_ansi_string(as);
+
+ tmp = mush_malloc(sizeof(ansi_string), "ansi_string");
+ if (!tmp)
+ return NULL;
+
+ memset(tmp, 0, sizeof(ansi_string));
+
+ /* Scramble the text */
+ tmp->len = as->len;
+
+ for (i = 0; i < as->len; i++)
+ pos[i] = i;
+
+ for (i = 0; i < as->len; i++) {
+ j = get_random_long(0, as->len - 1);
+ k = pos[i];
+ pos[i] = pos[j];
+ pos[j] = k;
+ }
+
+ /* The old scramble did new[i] = old[pos[i]],
+ * but handling markup tags is easier if we do it this way. */
+ /* Scramble the text and ansi... */
+ for (i = 0; i < as->len; i++) {
+ tmp->text[pos[i]] = as->text[i];
+ tmp->ansi[pos[i]] = as->ansi[i];
+ }
+
+ /* Scramble the Pueblo markup */
+ dm = tmp->markup;
+ if (as->nmarkups > 0)
+ tmp->optimized = 0;
+
+ /* Copy the standalones (tags with -1 for start) */
+ for (i = 0; i < as->nmarkups && as->markup[i].start == -1; i++) {
+ copyto(dm[i], as->markup[i]);
+ dm[i].end = pos[i];
+ }
+
+ /* Copy the rest */
+ j = i;
+ for (; i < as->nmarkups; i++) {
+ for (k = as->markup[i].start; k < as->markup[i].end; k++) {
+ copyto(dm[j], as->markup[i]);
+ dm[j].start = pos[k];
+ dm[j].end = dm[j].start + 1;
+ j++;
+ if (j >= BUFFER_LEN) {
+ optimize_ansi_string(tmp);
+ tmp->optimized = 0;
+ j = tmp->nmarkups;
+ if (j >= BUFFER_LEN)
+ return tmp;
+ }
+ }
+ }
+ tmp->nmarkups = j;
+ optimize_ansi_string(tmp);
+ return tmp;
+}
+
#undef copyto
/** Safely append an ansi_string into a buffer as a real string,
@@ -1548,25 +1642,26 @@
* \retval 0 success.
* \retval 1 failure.
*/
-
int
safe_ansi_string(ansi_string *as, int start, int len, char *buff, char **bp)
{
int i, j;
+ int cur;
markup_information *info;
int nextstart, nextend, next;
int end = start + len;
int retval = 0;
+ ansi_data curansi;
- if (as->optimized == 0) {
- optimize_ansi_string(as);
- as->optimized = 1;
- }
+ if (!as)
+ return 0;
+ optimize_ansi_string(as);
+
if (len <= 0)
return 0;
-
- i = start;
+ if (as->len > BUFFER_LEN)
+ as->len = BUFFER_LEN;
if (start >= as->len)
return 0;
if (end > as->len)
@@ -1575,86 +1670,143 @@
/* Standalones (Stop codes with -1 for start) */
for (j = 0; j < as->nmarkups; j++) {
info = &(as->markup[j]);
- if (info->start == -1 && info->end == i) {
- /* This is a standalone tag. YUCK! */
- if (info->stop_code)
- retval += safe_str(info->stop_code, buff, bp);
- }
+ if (info->start != -1)
+ break; /* No more standalone tags to copy */
+ if (info->stop_code && info->end == start)
+ retval += safe_str(info->stop_code, buff, bp);
}
/* Now, start codes of everything that impacts us. */
- for (j = 0; j < as->nmarkups; j++) {
+ for (; j < as->nmarkups; j++) {
info = &(as->markup[j]);
- if (info->start >= 0) {
- if (info->start <= i && info->end > i) {
- retval += copy_start_code(info, buff, bp);
- }
- }
+ if (info->start > start)
+ break; /* No more tags to copy. */
+ if (info->end > start)
+ retval += copy_start_code(info, buff, bp);
}
-
- /* Find the next changes */
- nextstart = BUFFER_LEN + 1;
+ /* Find the next changes--new tags, or a prior tag ending. */
+ i = start;
nextend = BUFFER_LEN + 1;
- for (j = 0; j < as->nmarkups; j++) {
- info = &(as->markup[j]);
- if (info->start > i && info->start < nextstart)
- nextstart = info->start;
- if (info->end > i && info->end < nextend)
- nextend = info->end;
+ /* If there is another start, it's our next one; we have a sorted list. */
+ if (j < as->nmarkups)
+ nextstart = as->markup[j].start;
+ else
+ nextstart = BUFFER_LEN + 1;
+
+ /* To find the next END is harder, since it isn't sorted... */
+ /* Scan forward. Stop once we find a tag with a start beyond
+ * this one's end. Anything beyond that can't be the nextend,
+ * so we'll backtrack from there. */
+ if (as->nmarkups > 0) {
+ cur = j;
+ if (cur >= as->nmarkups)
+ cur--;
+ info = &(as->markup[cur]);
+ while (cur < as->nmarkups && as->markup[cur].start < info->end)
+ cur++;
+ cur--;
+ if (info->end > as->markup[cur].start) {
+ for (; cur >= 0; cur--) {
+ if (as->markup[cur].end > i && as->markup[cur].end < nextend)
+ nextend = as->markup[cur].end;
+ }
+ }
}
next = (nextend < nextstart) ? nextend : nextstart;
+
if (end < next)
next = end;
- for (; i < next && i < as->len; i++) {
- if (as->text[i])
+ curansi = as->ansi[start];
+ if (!ansi_isnull(curansi))
+ write_ansi_data(&curansi, buff, bp);
+ /* If there's any text/ansi between start and next, print it */
+ for (i = start; i < next && i < as->len; i++) {
+ if (as->text[i]) {
+ if (!ansi_equal(curansi, as->ansi[i])) {
+ if (!ansi_isnull(curansi))
+ write_ansi_close(buff, bp);
+ curansi = as->ansi[i];
+ if (!ansi_isnull(curansi))
+ write_ansi_data(&curansi, buff, bp);
+ }
safe_chr(as->text[i], buff, bp);
+ }
}
- i = next;
+ cur = j; /* Our current markup */
+ i = next; /* Our current position */
+
+ /* Basically the same thing as above, in loop form. */
while (i < end) {
+
if (i >= nextend) {
nextend = BUFFER_LEN + 2;
- for (j = 0; j < as->nmarkups; j++) {
+ j = cur;
+ /* Find the last markup that could possibly have a relevant end code */
+ while (j < as->nmarkups && as->markup[j].start < as->markup[cur].end)
+ j++;
+ j--;
+ /* We MUST have markup if we're here, so no nmarkup > 0 check. */
+ /* Print relevant stop codes, and find our nextend */
+ for (; j >= 0; j--) {
info = &(as->markup[j]);
- if (info->end == i) {
- retval += copy_stop_code(info, buff, bp);
+ if (info->end >= i) {
+ if (info->end == i)
+ retval += copy_stop_code(info, buff, bp);
+ else if (info->end < nextend)
+ nextend = info->end;
}
- if (info->end > i && info->end < nextend)
- nextend = info->end;
}
}
+
if (i >= nextstart) {
- nextstart = BUFFER_LEN + 2;
- for (j = 0; j < as->nmarkups; j++) {
- info = &(as->markup[j]);
- if (info->start == i) {
- retval += copy_start_code(info, buff, bp);
- } else if (info->start > i && info->start < nextstart) {
- nextstart = info->start;
- }
- }
+ /* Print out all the relevant start codes */
+ for (; cur < as->nmarkups && as->markup[cur].start == i; cur++)
+ retval += copy_start_code(&(as->markup[cur]), buff, bp);
+ if (cur < as->nmarkups)
+ nextstart = as->markup[cur].start;
+ else
+ nextstart = BUFFER_LEN + 2;
}
+
next = (nextend < nextstart) ? nextend : nextstart;
if (end < next)
next = end;
+
for (; i < next && i < as->len; i++) {
- if (as->text[i])
+ if (as->text[i]) {
+ if (!ansi_equal(curansi, as->ansi[i])) {
+ if (!ansi_isnull(curansi))
+ write_ansi_close(buff, bp);
+ curansi = as->ansi[i];
+ if (!ansi_isnull(curansi))
+ write_ansi_data(&curansi, buff, bp);
+ }
safe_chr(as->text[i], buff, bp);
+ }
}
}
/* Now, find all things that end for us */
- for (j = 0; j < as->nmarkups; j++) {
- info = &(as->markup[j]);
- if (info->start < i && info->end >= i)
- retval += copy_stop_code(info, buff, bp);
+ if (as->nmarkups > 0) {
+ j = cur;
+ while (cur < as->nmarkups && as->markup[j].end > as->markup[cur].start)
+ cur++;
+ cur--;
+ for (; cur >= 0; cur--) {
+ info = &(as->markup[cur]);
+ if (info->start < i && info->end >= i)
+ retval += copy_stop_code(info, buff, bp);
+ }
}
+ if (!retval && !ansi_isnull(curansi))
+ retval += write_ansi_close(buff, bp);
return retval;
}
@@ -1664,272 +1816,278 @@
extern char escaped_chars[UCHAR_MAX + 1];
-static int escape_strn(char *s, int start, int count, char *buff, char **bp);
-
static int
-escape_str(char *in, char *buff, char **bp)
+escape_marked_str(char **str, char *buff, char **bp)
{
- return escape_strn(in, 0, strlen(in), buff, bp);
-}
-
-static int
-escape_strn(char *s, int start, int count, char *buff, char **bp)
-{
unsigned char *in;
int retval = 0;
int dospace = 1;
int spaces = 0;
- int i, j;
+ int i;
- in = (unsigned char *) s;
-
- if (*in) {
- count += start;
- for (i = start; in[i] && i < count; i++) {
- if (in[i] == ' ') {
- spaces++;
- } else {
- if (spaces) {
- if (spaces >= 5) {
- retval += safe_str("[space(", buff, bp);
- retval += safe_number(spaces, buff, bp);
- retval += safe_str(")]", buff, bp);
- } else {
- if (dospace) {
- spaces--;
+ if (!str || !*str || !**str)
+ return 0;
+ in = (unsigned char *) *str;
+ for (; *in && *in != ESC_CHAR && *in != TAG_START; in++) {
+ if (*in == ' ') {
+ spaces++;
+ } else {
+ if (spaces) {
+ if (spaces >= 5) {
+ retval += safe_str("[space(", buff, bp);
+ retval += safe_number(spaces, buff, bp);
+ retval += safe_str(")]", buff, bp);
+ } else {
+ if (dospace) {
+ spaces--;
+ retval += safe_str("%b", buff, bp);
+ }
+ while (spaces) {
+ retval += safe_chr(' ', buff, bp);
+ if (--spaces) {
+ --spaces;
retval += safe_str("%b", buff, bp);
}
- while (spaces) {
- retval += safe_chr(' ', buff, bp);
- if (--spaces) {
- --spaces;
- retval += safe_str("%b", buff, bp);
- }
- }
}
}
- spaces = 0;
- dospace = 0;
- if (in[i] == '\n') {
- retval += safe_str("%r", buff, bp);
- } else if (in[i] == '\t') {
- retval += safe_str("%t", buff, bp);
- } else if (in[i] == BEEP_CHAR) {
- for (j = i; in[i + 1] == BEEP_CHAR && (i - j) < 4; i++) ;
- retval += safe_format(buff, bp, "[beep(%d)]", (i - j) + 1);
- } else if (escaped_chars[in[i]]) {
+ }
+ spaces = 0;
+ dospace = 0;
+ switch (*in) {
+ case '\n':
+ retval += safe_str("%r", buff, bp);
+ break;
+ case '\t':
+ retval += safe_str("%t", buff, bp);
+ break;
+ case BEEP_CHAR:
+ for (i = 1; *(in + 1) == BEEP_CHAR && i < 5; in++, i++) ;
+ retval += safe_format(buff, bp, "[beep(%d)]", i);
+ break;
+ default:
+ if (escaped_chars[*in])
retval += safe_chr('\\', buff, bp);
- retval += safe_chr(in[i], buff, bp);
- } else {
- retval += safe_chr(in[i], buff, bp);
- }
+ retval += safe_chr(*in, buff, bp);
+ break;
}
}
- if (spaces) {
- if (spaces >= 5) {
- retval += safe_str("[space(", buff, bp);
- retval += safe_number(spaces, buff, bp);
- retval += safe_str(")]", buff, bp);
- } else {
- spaces--; /* This is for the final %b space */
- if (spaces && dospace) {
- spaces--;
+ }
+ if (spaces) {
+ if (spaces >= 5) {
+ retval += safe_str("[space(", buff, bp);
+ retval += safe_number(spaces, buff, bp);
+ retval += safe_str(")]", buff, bp);
+ } else {
+ spaces--; /* This is for the final %b space */
+ if (spaces && dospace) {
+ spaces--;
+ retval += safe_str("%b", buff, bp);
+ }
+ while (spaces) {
+ safe_chr(' ', buff, bp);
+ if (--spaces) {
+ --spaces;
retval += safe_str("%b", buff, bp);
}
- while (spaces) {
- safe_chr(' ', buff, bp);
- if (--spaces) {
- --spaces;
- retval += safe_str("%b", buff, bp);
- }
- }
- retval += safe_str("%b", buff, bp);
}
+ retval += safe_str("%b", buff, bp);
}
}
+ *str = (char *) in;
return retval;
}
-
-static int
-dump_start_code(markup_information *info, char *buff, char **bp)
+/* Does the work of decompose_str, which is found in look.c.
+ * Even handles ANSI and Pueblo, which is why it's so ugly.
+ * Code based off of real_parse_ansi_string, not safe_ansi_string.
+ */
+int
+real_decompose_str(char *orig, char *buff, char **bp)
{
- int retval = 0;
- char *save;
- save = *bp;
- if (info->type == MARKUP_HTML) {
- if (info->stop_code != NULL) {
- char *ptr;
- retval += safe_str("[tagwrap(", buff, bp);
- if ((ptr = strchr(info->start_code, ' ')) != NULL) {
- *(ptr++) = '\0';
- retval += escape_str(info->start_code, buff, bp);
- if (*ptr) {
- retval += safe_chr(',', buff, bp);
- retval += escape_str(ptr, buff, bp);
- }
- ptr--;
- *ptr = ' ';
- } else {
- retval += escape_str(info->start_code, buff, bp);
- }
- retval += safe_chr(',', buff, bp);
- } else {
- retval += safe_str("[tag(", buff, bp);
- retval += escape_str(info->start_code, buff, bp);
- retval += safe_str(")]", buff, bp);
- }
- } else {
- /* Find the digits */
- retval += safe_str("[ansi(", buff, bp);
- if (info->ansi.fore == 'n') {
- retval += safe_chr('n', buff, bp);
- } else {
-#define CBIT_SET(x,y) (x.bits & y)
- if (CBIT_SET(info->ansi, CBIT_FLASH))
- retval += safe_chr('f', buff, bp);
- if (CBIT_SET(info->ansi, CBIT_HILITE))
- retval += safe_chr('h', buff, bp);
- if (CBIT_SET(info->ansi, CBIT_INVERT))
- retval += safe_chr('i', buff, bp);
- if (CBIT_SET(info->ansi, CBIT_UNDERSCORE))
- retval += safe_chr('u', buff, bp);
-#undef CBIT_SET
-#define CBIT_SET(x,y) (x.offbits & y)
- if (CBIT_SET(info->ansi, CBIT_FLASH))
- retval += safe_chr('F', buff, bp);
- if (CBIT_SET(info->ansi, CBIT_HILITE))
- retval += safe_chr('H', buff, bp);
- if (CBIT_SET(info->ansi, CBIT_INVERT))
- retval += safe_chr('I', buff, bp);
- if (CBIT_SET(info->ansi, CBIT_UNDERSCORE))
- retval += safe_chr('U', buff, bp);
-#undef CBIT_SET
+ int i;
+ char *str = orig;
+ char *tmp;
+ char *pstr;
+ char type;
- if (info->ansi.fore)
- retval += safe_chr(info->ansi.fore, buff, bp);
- if (info->ansi.back)
- retval += safe_chr(info->ansi.back, buff, bp);
- retval += safe_chr(',', buff, bp);
- }
- }
- if (retval)
- *bp = save;
- return retval;
-}
+ ansi_data ansistack[BUFFER_LEN];
+ ansistack[0] = ansi_null;
+ ansi_data oldansi;
+ ansi_data tmpansi;
+ int ansitop = 0;
+ int ansiheight = 0;
+ int howmanyopen = 0;
+ int oldcodes = 0;
-static int
-dump_stop_code(markup_information *info
- __attribute__ ((__unused__)), char *buff, char **bp)
-{
- char *save = *bp;
- if (safe_str(")]", buff, bp)) {
- *bp = save;
- }
- return 1;
-}
+ char *pueblostack[BUFFER_LEN];
+ char tagbuff[BUFFER_LEN];
+ int pueblotop = -1;
-int
-dump_ansi_string(ansi_string *as, char *buff, char **bp)
-{
- int i, j;
- markup_information *info;
- int nextstart, nextend, next;
- int end;
- int start = 0;
int retval = 0;
- end = as->len;
+ if (!str || !*str)
+ return 0;
- if (as->optimized == 0) {
- optimize_ansi_string(as);
- as->optimized = 1;
- }
+ retval += escape_marked_str(&str, buff, bp);
- i = start;
- if (start > as->len)
- return 0;
- if (end > as->len)
- end = as->len;
+ while (str && *str && *str != '\0') {
+ oldansi = ansistack[ansitop];
+ ansiheight = ansitop;
+ while (*str == TAG_START || *str == ESC_CHAR) {
+ switch (*str) {
+ case TAG_START:
+ for (tmp = str; *tmp && *tmp != TAG_END; tmp++) ;
+ if (*tmp) {
+ *tmp = '\0';
+ } else {
+ tmp--;
+ }
+ str++;
+ type = *(str++);
+ switch (type) {
+ case MARKUP_COLOR:
+ if (!*str)
+ break;
+ if (oldcodes == 1) {
+ ansitop--;
+ oldcodes = 0;
+ }
+ /* Start or end tag? */
+ if (*str != '/') {
+ define_ansi_data(&tmpansi, str);
+ nest_ansi_data(&(ansistack[ansitop]), &tmpansi);
+ ansitop++;
+ ansistack[ansitop] = tmpansi;
+ } else {
+ if (*(str + 1) == 'a') {
+ ansitop = 0;
+ } else {
+ if (ansitop > 0) {
+ ansitop--;
+ }
+ }
+ }
+ break;
+ case MARKUP_HTML:
+ if (!*str)
+ break;
+ if (*str != '/') {
+ pueblotop++;
+ snprintf(tagbuff, BUFFER_LEN, "%s", parse_tagname(str));
+ pueblostack[pueblotop] = mush_strdup(tagbuff, "markup_code");
- /* Standalones (Stop codes with -1 for start) */
- for (j = 0; j < as->nmarkups; j++) {
- info = &(as->markup[j]);
- if (info->start == -1 && info->end == i) {
- /* This is a standalone tag. YUCK! */
- if (info->stop_code)
- retval += safe_str(info->stop_code, buff, bp);
- }
- }
+ retval += safe_str("[tag(", buff, bp);
+ retval += safe_str(tagbuff, buff, bp);
+ str += strlen(tagbuff);
+ if (str && *str) {
+ while (str && str != tmp) {
+ str++;
+ pstr = strchr(str, '=');
+ if (pstr) {
+ *pstr = '\0';
+ retval += safe_chr(',', buff, bp);
+ retval += safe_str(str, buff, bp);
+ retval += safe_chr('=', buff, bp);
+ str = pstr + 1;
+ pstr = strchr(str, '\"');
- /* Now, start codes of everything that impacts us. */
- for (j = 0; j < as->nmarkups; j++) {
- info = &(as->markup[j]);
- if (info->start >= 0) {
- if (info->start <= i && info->end > i) {
- retval += dump_start_code(info, buff, bp);
- }
- }
- }
+ retval += safe_chr('\"', buff, bp);
+ if (str == pstr) {
+ str++;
+ pstr = strchr(str, '\"');
+ } else {
+ pstr = strchr(str, ' ');
+ }
+ if (!pstr)
+ pstr = tmp;
- /* Find the next changes */
- nextstart = BUFFER_LEN + 1;
- nextend = BUFFER_LEN + 1;
+ *pstr = '\0';
+ retval += safe_str(str, buff, bp);
+ retval += safe_chr('\"', buff, bp);
+ str = pstr;
+ } else {
+ safe_str(str, buff, bp);
+ break;
+ }
+ }
+ }
+ retval += safe_str(")]", buff, bp);
- for (j = 0; j < as->nmarkups; j++) {
- info = &(as->markup[j]);
- if (info->start > i && info->start < nextstart)
- nextstart = info->start;
- if (info->end > i && info->end < nextend)
- nextend = info->end;
- }
+ } else {
+ if (pueblotop > -1) {
+ i = (*(str + 1) == 'a') ? 0 : pueblotop;
+ for (i--; pueblotop > i; pueblotop--) {
+ retval += safe_str("[endtag(", buff, bp);
+ retval += safe_str(pueblostack[pueblotop], buff, bp);
+ retval += safe_str(")]", buff, bp);
+ mush_free(pueblostack[pueblotop], "markup_code");
+ }
+ }
+ }
+ break;
+ }
+ tmp++;
+ str = tmp;
+ break;
+ case ESC_CHAR:
+ /* It SHOULD be impossible to get here... */
+ for (tmp = str; *tmp && *tmp != 'm'; tmp++) ;
- next = (nextend < nextstart) ? nextend : nextstart;
- if (end < next)
- next = end;
+ /* Store the "background" colors */
+ tmpansi = ansistack[ansitop];
+ if (oldcodes == 0) {
+ oldcodes = 1;
+ ansitop++;
+ ansistack[ansitop] = tmpansi;
+ ansistack[ansitop].offbits = 0;
+ }
- if (next > as->len)
- next = as->len;
- escape_strn(as->text, i, next - i, buff, bp);
- i = next;
+ read_raw_ansi_data(&tmpansi, str);
+ ansistack[ansitop].bits |= tmpansi.bits;
+ ansistack[ansitop].bits &= ~(tmpansi.offbits); /* ANSI_RAW_NORMAL */
+ if (tmpansi.fore)
+ ansistack[ansitop].fore = tmpansi.fore;
+ if (tmpansi.back)
+ ansistack[ansitop].back = tmpansi.back;
- while (i < end) {
- if (i >= nextend) {
- nextend = BUFFER_LEN + 2;
- for (j = 0; j < as->nmarkups; j++) {
- info = &(as->markup[j]);
- if (info->end == i) {
- retval += dump_stop_code(info, buff, bp);
- }
- if (info->end > i && info->end < nextend)
- nextend = info->end;
+ str = tmp;
+ if (*tmp)
+ str++;
+ break;
}
}
- if (i >= nextstart) {
- nextstart = BUFFER_LEN + 2;
- for (j = 0; j < as->nmarkups; j++) {
- info = &(as->markup[j]);
- if (info->start == i) {
- retval += dump_start_code(info, buff, bp);
- } else if (info->start > i && info->start < nextstart) {
- nextstart = info->start;
+
+ /* Handle ANSI/Text */
+ tmpansi = ansistack[ansitop];
+ if (ansitop > 0 || ansiheight > 0) {
+ /* Close existing tags as necessary to cleanly open the next. */
+ /* oldansi = ansistack[ansiheight]; */
+ if (!ansi_equal(oldansi, tmpansi)) {
+ while (ansiheight > 0) {
+ if (howmanyopen > 0) {
+ howmanyopen--;
+ retval += safe_str(")]", buff, bp);
+ }
+ ansiheight--;
}
}
+ if (!ansi_isnull(tmpansi) && !ansi_equal(oldansi, tmpansi)) {
+ retval += safe_str("[ansi(", buff, bp);
+ retval += write_ansi_letters(tmpansi, buff, bp);
+ retval += safe_chr(',', buff, bp);
+ howmanyopen++;
+ }
}
- next = (nextend < nextstart) ? nextend : nextstart;
- if (end < next)
- next = end;
-
- escape_strn(as->text, i, next - i, buff, bp);
- i = next;
+ retval += escape_marked_str(&str, buff, bp);
}
- /* Now, find all things that end for us */
- for (j = 0; j < as->nmarkups; j++) {
- info = &(as->markup[j]);
- if (info->start < i && info->end >= i)
- retval += dump_stop_code(info, buff, bp);
+ for (; howmanyopen > 0; howmanyopen--)
+ retval += safe_str(")]", buff, bp);
+ for (; pueblotop > -1; pueblotop--) {
+ retval += safe_str("[endtag(", buff, bp);
+ retval += safe_str(pueblostack[pueblotop], buff, bp);
+ retval += safe_str(")]", buff, bp);
}
return retval;
@@ -1945,8 +2103,9 @@
* \return size of subpattern, or -1 if unknown pattern
*/
int
-ansi_pcre_copy_substring(ansi_string *as, int *ovector, int stringcount,
- int stringnumber, int nonempty, char *buff, char **bp)
+ansi_pcre_copy_substring(ansi_string *as, int *ovector,
+ int stringcount, int stringnumber,
+ int nonempty, char *buff, char **bp)
{
int yield;
if (stringnumber < 0 || stringnumber >= stringcount)
@@ -1972,8 +2131,9 @@
* \return size of subpattern, or -1 if unknown pattern
*/
int
-ansi_pcre_copy_named_substring(const pcre * code, ansi_string *as, int *ovector,
- int stringcount, const char *stringname, int ne,
+ansi_pcre_copy_named_substring(const pcre * code, ansi_string *as,
+ int *ovector, int stringcount,
+ const char *stringname, int ne,
char *buff, char **bp)
{
int n = pcre_get_stringnumber(code, stringname);
@@ -1997,7 +2157,6 @@
{
int result = 0;
char *save = buf;
-
result = safe_chr(TAG_START, buf, bp);
result = safe_chr(type, buf, bp);
result = safe_str(a_tag, buf, bp);
@@ -2005,7 +2164,6 @@
/* If it didn't all fit, rewind. */
if (result)
*bp = save;
-
return result;
}
@@ -2032,7 +2190,6 @@
{
int result = 0;
char *save = buf;
-
result = safe_chr(TAG_START, buf, bp);
result = safe_chr(type, buf, bp);
result = safe_chr('/', buf, bp);
@@ -2041,7 +2198,6 @@
/* If it didn't all fit, rewind. */
if (result)
*bp = save;
-
return result;
}
@@ -2065,12 +2221,11 @@
* \retval 1, tagged text wouldn't fit in buffer.
*/
int
-safe_tag_wrap(char const *a_tag, char const *params, char const *data,
- char *buf, char **bp, dbref player)
+safe_tag_wrap(char const *a_tag, char const *params,
+ char const *data, char *buf, char **bp, dbref player)
{
int result = 0;
char *save = buf;
-
if (SUPPORT_PUEBLO) {
result = safe_chr(TAG_START, buf, bp);
result = safe_chr(MARKUP_HTML, buf, bp);
Index: src/access.c
===================================================================
--- src/access.c (.../p4) (revision 1119)
+++ src/access.c (.../p5) (revision 1119)
@@ -77,6 +77,7 @@
#endif
#include "conf.h"
#include "externs.h"
+#include "pcre.h"
#include "access.h"
#include "mymalloc.h"
#include "match.h"
@@ -94,8 +95,8 @@
*/
struct a_acsflag {
const char *name; /**< Name of the access flag */
- int toggle; /**< Is this a negatable flag? */
- int flag; /**< Bitmask of the flag */
+ bool toggle; /**< Is this a negatable flag? */
+ uint32_t flag; /**< Bitmask of the flag */
};
static acsflag acslist[] = {
{"connect", 1, ACS_CONNECT},
@@ -113,31 +114,62 @@
};
static struct access *access_top;
-static int add_access_node
- (const char *host, const dbref who, const int can, const int cant,
- const char *comment);
static void free_access_list(void);
-static int
-add_access_node(const char *host, const dbref who, const int can,
- const int cant, const char *comment)
+extern const unsigned char *tables;
+
+static struct access *
+sitelock_alloc(const char *host, dbref who,
+ uint32_t can, uint32_t cant,
+ const char *comment, const char **errptr)
+ __attribute_malloc__;
+
+ static struct access *sitelock_alloc(const char *host, dbref who,
+ uint32_t can, uint32_t cant,
+ const char *comment,
+ const char **errptr)
{
- struct access *end;
struct access *tmp;
-
- tmp = (struct access *) mush_malloc(sizeof(struct access), "struct_access");
- if (!tmp)
- return 0;
+ tmp = mush_malloc(sizeof(struct access), "sitelock.rule");
+ if (!tmp) {
+ static const char *memerr = "unable to allocate memory";
+ if (errptr)
+ *errptr = memerr;
+ return NULL;
+ }
tmp->who = who;
tmp->can = can;
tmp->cant = cant;
- strcpy(tmp->host, host);
+ mush_strncpy(tmp->host, host, BUFFER_LEN);
if (comment)
- strcpy(tmp->comment, comment);
+ mush_strncpy(tmp->comment, comment, BUFFER_LEN);
else
tmp->comment[0] = '\0';
tmp->next = NULL;
+ if (can & ACS_REGEXP) {
+ int erroffset = 0;
+ tmp->re = pcre_compile(host, 0, errptr, &erroffset, tables);
+ if (!tmp->re) {
+ mush_free(tmp, "sitelock.rule");
+ return NULL;
+ }
+ } else
+ tmp->re = NULL;
+
+ return tmp;
+}
+
+static bool
+add_access_node(const char *host, dbref who, uint32_t can,
+ uint32_t cant, const char *comment, const char **errptr)
+{
+ struct access *end, *tmp;
+
+ tmp = sitelock_alloc(host, who, can, cant, comment, errptr);
+ if (!tmp)
+ return false;
+
if (!access_top) {
/* Add to the beginning */
access_top = tmp;
@@ -147,25 +179,25 @@
end = end->next;
end->next = tmp;
}
-
- return 1;
+ return true;
}
/** Read the access.cnf file.
* Initialize the access rules linked list and read in the access.cnf file.
- * Return 1 if successful, 0 if not
+ * \return true if successful, false if not
*/
-int
+bool
read_access_file(void)
{
FILE *fp;
char buf[BUFFER_LEN];
char *p;
- int can, cant;
+ uint32_t can, cant;
int retval;
dbref who;
char *comment;
+ const char *errptr = NULL;
if (access_top) {
/* We're reloading the file, so we've got to delete any current
@@ -197,8 +229,10 @@
comment = NULL;
/* Is this the @sitelock entry? */
if (!strncasecmp(p, "@sitelock", 9)) {
- if (!add_access_node("@sitelock", AMBIGUOUS, ACS_SITELOCK, 0, ""))
- do_log(LT_ERR, GOD, GOD, T("Failed to add sitelock node!"));
+ if (!add_access_node("@sitelock", AMBIGUOUS, ACS_SITELOCK, 0, "",
+ &errptr))
+ do_log(LT_ERR, GOD, GOD, T("Failed to add sitelock node: %s"),
+ errptr);
} else {
if ((comment = strchr(p, '#'))) {
*comment++ = '\0';
@@ -213,8 +247,9 @@
if (!parse_access_options(p, &who, &can, &cant, NOTHING))
/* Nothing listed, so assume we can't do anything! */
cant = ACS_DEFAULT;
- if (!add_access_node(buf, who, can, cant, comment))
- do_log(LT_ERR, GOD, GOD, T("Failed to add access node!"));
+ if (!add_access_node(buf, who, can, cant, comment, &errptr))
+ do_log(LT_ERR, GOD, GOD, T("Failed to add access node: %s"),
+ errptr);
}
}
}
@@ -316,8 +351,8 @@
* flags (can't register, isn't suspect)
* \endverbatim
*/
-int
-site_can_access(const char *hname, int flag, dbref who)
+bool
+site_can_access(const char *hname, uint32_t flag, dbref who)
{
struct access *ap;
acsflag *c;
@@ -332,11 +367,11 @@
for (ap = access_top; ap; ap = ap->next) {
if (!(ap->can & ACS_SITELOCK)
&& ((ap->can & ACS_REGEXP)
- ? (quick_regexp_match(ap->host, hname, 0)
- || (p && quick_regexp_match(ap->host, p, 0))
+ ? (qcomp_regexp_match(ap->re, hname)
+ || (p && qcomp_regexp_match(ap->re, p))
#ifdef FORCE_IPV4
- || quick_regexp_match(ip4_to_ip6(ap->host), hname, 0)
- || (p && quick_regexp_match(ip4_to_ip6(ap->host), p, 0))
+ || qcomp_regexp_match(ip4_to_ip6(ap->re), hname)
+ || (p && qcomp_regexp_match(ip4_to_ip6(ap->re), p))
#endif
)
: (quick_wild(ap->host, hname)
@@ -402,11 +437,11 @@
(*rulenum)++;
if (!(ap->can & ACS_SITELOCK)
&& ((ap->can & ACS_REGEXP)
- ? (quick_regexp_match(ap->host, hname, 0)
- || (p && quick_regexp_match(ap->host, p, 0))
+ ? (qcomp_regexp_match(ap->re, hname)
+ || (p && qcomp_regexp_match(ap->re, p))
#ifdef FORCE_IPV4
- || quick_regexp_match(ip4_to_ip6(ap->host), hname, 0)
- || (p && quick_regexp_match(ip4_to_ip6(ap->host), p, 0))
+ || qcomp_regexp_match(ip4_to_ip6(ap->host), hname)
+ || (p && qcomp_regexp_match(ip4_to_ip6(ap->host), p))
#endif
)
: (quick_wild(ap->host, hname)
@@ -433,7 +468,7 @@
* This function provides an appealing display of an access rule
* in the list.
*/
-int
+void
format_access(struct access *ap, int rulenum,
dbref who __attribute__ ((__unused__)), char *buff, char **bp)
{
@@ -474,7 +509,6 @@
} else {
safe_str(T("No matching access rule"), buff, bp);
}
- return 0;
}
@@ -493,29 +527,28 @@
* Build an appropriate comment based on the player and date
* \endverbatim
*/
-int
-add_access_sitelock(dbref player, const char *host, dbref who, int can,
- int cant)
+bool
+add_access_sitelock(dbref player, const char *host, dbref who, uint32_t can,
+ uint32_t cant)
{
struct access *end;
struct access *tmp;
+ const char *errptr = NULL;
- tmp = (struct access *) mush_malloc(sizeof(struct access), "struct_access");
- if (!tmp)
- return 0;
- tmp->who = who;
- tmp->can = can;
- tmp->cant = cant;
- strcpy(tmp->host, host);
- snprintf(tmp->comment, sizeof tmp->comment,
- "By %s(#%d) on %s", Name(player), player, show_time(mudtime, 0));
- tmp->next = NULL;
+ tmp = sitelock_alloc(host, who, can, cant, "", &errptr);
+ if (!tmp) {
+ notify_format(player, T("Unable to add sitelock entry: %s"), errptr);
+ return false;
+ }
+
if (!access_top) {
/* Add to the beginning, but first add a sitelock marker */
- if (!add_access_node("@sitelock", AMBIGUOUS, ACS_SITELOCK, 0, ""))
+ if (!add_access_node("@sitelock", AMBIGUOUS, ACS_SITELOCK, 0, "", &errptr)) {
+ notify_format(player, T("Unable to add @sitelock separator: %s"), errptr);
return 0;
+ }
access_top->next = tmp;
} else {
end = access_top;
@@ -524,8 +557,12 @@
/* Now, either we're at the sitelock or the end */
if (end->can != ACS_SITELOCK) {
/* We're at the end and there's no sitelock marker. Add one */
- if (!add_access_node("@sitelock", AMBIGUOUS, ACS_SITELOCK, 0, ""))
+ if (!add_access_node("@sitelock", AMBIGUOUS, ACS_SITELOCK, 0, "",
+ &errptr)) {
+ notify_format(player, T("Unable to add @sitelock separator: %s"),
+ errptr);
return 0;
+ }
end = end->next;
} else {
/* We're in the middle, so be sure we keep the list linked */
@@ -539,11 +576,9 @@
/** Remove an access rule from the linked list.
* \param pattern access rule host pattern to match.
* \return number of rule removed.
- * \verbatim
* This function removes an access rule from the list.
* Only rules that appear after the "@sitelock" rule can be
* removed with this function.
- * \endverbatim
*/
int
remove_access_sitelock(const char *pattern)
@@ -563,7 +598,9 @@
next = ap->next;
if (strcasecmp(pattern, ap->host) == 0) {
n++;
- mush_free(ap, "struct_access");
+ if (ap->re)
+ free(ap->re);
+ mush_free(ap, "sitelock.rule");
if (prev)
prev->next = next;
else
@@ -585,7 +622,9 @@
ap = access_top;
while (ap) {
next = ap->next;
- mush_free((Malloc_t) ap, "struct_access");
+ if (ap->re)
+ free(ap->re);
+ mush_free(ap, "sitelock.rule");
ap = next;
}
access_top = NULL;
@@ -648,8 +687,8 @@
* This makes a copy of the options string, so it's not modified.
*/
int
-parse_access_options(const char *opts, dbref *who, int *can, int *cant,
- dbref player)
+parse_access_options(const char *opts, dbref *who, uint32_t * can,
+ uint32_t * cant, dbref player)
{
char myopts[BUFFER_LEN];
char *p;
Index: src/local.dst
===================================================================
--- src/local.dst (.../p4) (revision 1119)
+++ src/local.dst (.../p5) (revision 1119)
@@ -46,7 +46,7 @@
/* Initial size of this hashtable should be close to the number of
* add_config()'s you plan to do.
*/
- hashinit(&local_options, 4, sizeof(PENNCONF));
+ hashinit(&local_options, 4);
#ifdef EXAMPLE
/* Call add_config for each config parameter you want to add.
Index: src/funmisc.c
===================================================================
--- src/funmisc.c (.../p4) (revision 1119)
+++ src/funmisc.c (.../p5) (revision 1119)
@@ -82,7 +82,18 @@
orator = saved_orator;
}
+FUNCTION(fun_message)
+{
+ int i;
+ char *argv[10];
+ for (i = 0; (i + 3) < nargs; i++) {
+ argv[i] = args[i + 3];
+ }
+
+ do_message_list(executor, executor, args[0], args[2], args[1], 0, i, argv);
+}
+
/* ARGSUSED */
FUNCTION(fun_oemit)
{
Index: src/speech.c
===================================================================
--- src/speech.c (.../p4) (revision 1119)
+++ src/speech.c (.../p5) (revision 1119)
@@ -12,6 +12,7 @@
#include