/* ************************************************************************ * File: modify.c EmpireMUD AD 1.0 * * Usage: Run-time modification of game variables * * * * All rights reserved. See license.doc for complete information. * * * * Code base by Paul Clarke. EmpireMUD Project, a tbgMUD Production. * * Based upon CircleMUD 3.0, beta patch level 17, by Jeremy Elson. * * * * Copyright (C) 1993, 94 by the Trustees of the Johns Hopkins University * * CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991. * ************************************************************************ */ #include "conf.h" #include "sysdep.h" #include "structs.h" #include "utils.h" #include "interpreter.h" #include "handler.h" #include "db.h" #include "comm.h" #include "mail.h" #include "boards.h" /* * Action modes for parse_action(). */ #define PARSE_FORMAT 0 #define PARSE_REPLACE 1 #define PARSE_HELP 2 #define PARSE_DELETE 3 #define PARSE_INSERT 4 #define PARSE_LIST_NORM 5 #define PARSE_LIST_NUM 6 #define PARSE_EDIT 7 /* * Defines for the action variable. */ #define STRINGADD_OK 0 /* Just keep adding text. */ #define STRINGADD_SAVE 1 /* Save current text. */ #define STRINGADD_ABORT 2 /* Abort edit, restore old text. */ #define STRINGADD_ACTION 4 /* Editor action, don't append \r\n. */ #define FORMAT_INDENT (1 << 0) void show_string(Descr d, char *input); extern const char *MENU; /* local functions */ void smash_tilde(char *str); char *next_page(char *str); int count_pages(char *str); void paginate_string(char *str, Descr d); /* ************************************************************************ * modification of malloc'ed strings * ************************************************************************ */ /* * Put '#if 1' here to erase ~, or roll your own method. A common idea * is smash/show tilde to convert the tilde to another innocuous character * to save and then back to display it. Whatever you do, at least keep the * function around because other MUD packages use it, like mudFTP. * -gg 9/9/98 */ void smash_tilde(char *str) { #if 0 /* * Erase any ~'s inserted by people in the editor. This prevents anyone * using online creation from causing parse errors in the world files. * Derived from an idea by Sammy <samedi@dhc.net> (who happens to like * his tildes thank you very much.), -gg 2/20/98 */ while ((str = strchr(str, '~')) != NULL) *str = ' '; #endif } /* * Basic API function to start writing somewhere. * * 'data' isn't used in stock EmpireMUD but you can use it to pass whatever * else you may want through it. The improved editor patch when updated * could use it to pass the old text buffer, for instance. */ void string_write(Descr d, char **writeto, size_t len, long mailto, void *data) { if (d->character && !IS_NPC(d->character)) SET_BIT(PLR_FLAGS(d->character), PLR_WRITING); d->backstr = (char *)data; d->str = writeto; d->max_str = len; d->mail_to = mailto; } /* Add user input to the 'current' string (as defined by d->str) */ void string_add(Descr d, char *str) { extern char *stripcr(char *dest, const char *src); FILE *fl; extern int improved_editor_execute(Descr d, char *str); int action; delete_doubledollar(str); smash_tilde(str); if ((action = improved_editor_execute(d, str)) == STRINGADD_ACTION) return; if (!(*d->str)) { if (strlen(str) + 3 > d->max_str) { send_to_char("String too long - Truncated.\r\n", d->character); strcpy(&str[d->max_str - 3], "\r\n"); CREATE(*d->str, char, d->max_str); strcpy(*d->str, str); } else { CREATE(*d->str, char, strlen(str) + 3); strcpy(*d->str, str); } } else { if (strlen(str) + strlen(*d->str) + 3 > d->max_str) send_to_char("String too long. Last line skipped.\r\n", d->character); else { RECREATE(*d->str, char, strlen(*d->str) + strlen(str) + 3); strcat(*d->str, str); } } switch (action) { case STRINGADD_ABORT: if (STATE(d) == CON_TEDIT || STATE(d) == CON_EDIT_DESCRIPTION) { free(*d->str); *d->str = d->backstr; d->backstr = NULL; d->str = NULL; } break; case STRINGADD_SAVE: if (d->str && *d->str && **d->str == '\0') { free(d->str); *d->str = str_dup("Nothing.\r\n"); } if (d->backstr) free(d->backstr); d->backstr = NULL; break; } if (action) { if (STATE(d) == CON_PLAYING) { if (PLR_FLAGGED(d->character, PLR_MAILING)) { if (action == STRINGADD_SAVE && *d->str) { store_mail(d->mail_to, GET_IDNUM(d->character), *d->str); SEND_TO_Q("Message sent!\r\n", d); } else SEND_TO_Q("Mail aborted.\r\n", d); free(*d->str); free(d->str); } if (d->mail_to >= BOARD_MAGIC) { Board_save_board(d->mail_to - BOARD_MAGIC); if (action == STRINGADD_ABORT) SEND_TO_Q("Post not aborted, use REMOVE <post #>.\r\n", d); } } else if (STATE(d) == CON_TEDIT || STATE(d) == CON_EDIT_DESCRIPTION) { if (action != STRINGADD_ABORT) { if (d->storage) { if ((fl = fopen((char *)d->storage, "w"))){ if (*d->str) fputs(stripcr(buf1, *d->str), fl); fclose(fl); if (STATE(d) == CON_TEDIT) { syslog(GET_INVIS_LEV(d->character), TRUE, "GC: %s saves '%s'.", GET_NAME(d->character), d->storage); SEND_TO_Q("Saved.\r\n", d); } else if (STATE(d) == CON_EDIT_DESCRIPTION) SEND_TO_Q("Description saved.\r\n", d); } } } else SEND_TO_Q("Edit aborted.\r\n", d); if (STATE(d) == CON_TEDIT) act("$n stops editing a file.", TRUE, d->character, 0, 0, TO_ROOM); else if (STATE(d) == CON_EDIT_DESCRIPTION) act("$n stops editing the description.", TRUE, d->character, 0, 0, TO_ROOM); free(d->storage); d->storage = NULL; STATE(d) = CON_PLAYING; } d->str = NULL; d->mail_to = 0; d->max_str = 0; if (d->character && !IS_NPC(d->character)) REMOVE_BIT(PLR_FLAGS(d->character), PLR_WRITING | PLR_MAILING); } else if (strlen(*d->str) <= (d->max_str-3)) strcat(*d->str, "\r\n"); } /********************************************************************* * New Pagination Code * Michael Buselli submitted the following code for an enhanced pager * for CircleMUD. All functions below are his. --JE 8 Mar 96 *********************************************************************/ #define PAGE_LENGTH 22 #define PAGE_WIDTH 80 /* Traverse down the string until the begining of the next page has been * reached. Return NULL if this is the last page of the string. */ char *next_page(char *str) { int col = 1, line = 1, spec_code = FALSE; for (;; str++) { /* If end of string, return NULL. */ if (*str == '\0') return (NULL); /* If we're at the start of the next page, return this fact. */ else if (line > PAGE_LENGTH) return (str); /* Check for the begining of an ANSI color code block. */ else if (*str == '\x1B' && !spec_code) spec_code = TRUE; /* Check for the end of an ANSI color code block. */ else if (*str == 'm' && spec_code) spec_code = FALSE; /* Check for everything else. */ else if (!spec_code) { /* Carriage return puts us in column one. */ if (*str == '\r') col = 1; /* Newline puts us on the next line. */ else if (*str == '\n') line++; /* * We need to check here and see if we are over the page width, * and if so, compensate by going to the begining of the next line. */ else if (col++ > PAGE_WIDTH) { col = 1; line++; } } } } /* Function that returns the number of pages in the string. */ int count_pages(char *str) { int pages; for (pages = 1; (str = next_page(str)); pages++); return (pages); } /* * This function assigns all the pointers for showstr_vector for the * page_string function, after showstr_vector has been allocated and * showstr_count set. */ void paginate_string(char *str, Descr d) { int i; if (d->showstr_count) *(d->showstr_vector) = str; for (i = 1; i < d->showstr_count && str; i++) str = d->showstr_vector[i] = next_page(str); d->showstr_page = 0; } /* The call that gets the paging ball rolling... */ void page_string(Descr d, char *str, int keep_internal) { if (!d) return; if (!str || !*str) return; if (d->character && PRF_FLAGGED(d->character, PRF_SCROLLING)) { msg_to_char(d->character, str); return; } d->showstr_count = count_pages(str); CREATE(d->showstr_vector, char *, d->showstr_count); if (keep_internal) { d->showstr_head = str_dup(str); paginate_string(d->showstr_head, d); } else paginate_string(str, d); show_string(d, ""); } /* The call that displays the next page. */ void show_string(Descr d, char *input) { char buffer[MAX_STRING_LENGTH]; int diff; any_one_arg(input, buf); /* Q is for quit. :) */ if (LOWER(*buf) == 'q') { free(d->showstr_vector); d->showstr_count = 0; if (d->showstr_head) { free(d->showstr_head); d->showstr_head = NULL; } return; } /* * R is for refresh, so back up one page internally so we can display * it again. */ else if (LOWER(*buf) == 'r') d->showstr_page = MAX(0, d->showstr_page - 1); /* * B is for back, so back up two pages internally so we can display the * correct page here. */ else if (LOWER(*buf) == 'b') d->showstr_page = MAX(0, d->showstr_page - 2); /* * Feature to 'goto' a page. Just type the number of the page and you * are there! */ else if (isdigit(*buf)) d->showstr_page = MAX(0, MIN(atoi(buf) - 1, d->showstr_count - 1)); else if (*buf) { send_to_char("Valid commands while paging are RETURN, Q, R, B, or a numeric value.\r\n", d->character); return; } /* * If we're displaying the last page, just send it to the character, and * then free up the space we used. */ if (d->showstr_page + 1 >= d->showstr_count) { send_to_char(d->showstr_vector[d->showstr_page], d->character); free(d->showstr_vector); d->showstr_count = 0; if (d->showstr_head) { free(d->showstr_head); d->showstr_head = NULL; } } /* Or if we have more to show.... */ else { diff = d->showstr_vector[d->showstr_page + 1] - d->showstr_vector[d->showstr_page]; if (diff >= MAX_STRING_LENGTH) diff = MAX_STRING_LENGTH - 1; strncpy(buffer, d->showstr_vector[d->showstr_page], diff); buffer[diff] = '\0'; send_to_char(buffer, d->character); d->showstr_page++; } } /* End of code by Michael Buselli */ /************************************************************************ * The following modify code was shipped with OasisOLC. Here are the * * credits from the patch I borrowed it from. * * OasisOLC v2.0 * * Original author: Levork * * Copyright 1996 by Harvey Gilpin * * Copyright 1997-1999 by George Greer (greerga@circlemud.org) * ************************************************************************/ int improved_editor_execute(Descr d, char *str) { void parse_action(int command, char *string, Descr d); char actions[MAX_INPUT_LENGTH]; if (*str != '/') return STRINGADD_OK; strncpy(actions, str + 2, sizeof(actions) - 1); actions[sizeof(actions)-1] = '\0'; *str = '\0'; switch(str[1]) { case 'a': return STRINGADD_ABORT; case 'c': if (*(d->str)) { free(*d->str); *(d->str) = NULL; SEND_TO_Q("Current buffer cleared.\r\n", d); } else SEND_TO_Q("Current buffer empty.\r\n", d); break; case 'd': parse_action(PARSE_DELETE, actions, d); break; case 'e': parse_action(PARSE_EDIT, actions, d); break; case 'f': if (*(d->str)) parse_action(PARSE_FORMAT, actions, d); else SEND_TO_Q("Current buffer empty.\r\n", d); break; case 'i': if (*(d->str)) parse_action(PARSE_INSERT, actions, d); else SEND_TO_Q("Current buffer empty.\r\n", d); break; case 'h': parse_action(PARSE_HELP, actions, d); break; case 'l': if (*d->str) parse_action(PARSE_LIST_NORM, actions, d); else SEND_TO_Q("Current buffer empty.\r\n", d); break; case 'n': if (*d->str) parse_action(PARSE_LIST_NUM, actions, d); else SEND_TO_Q("Current buffer empty.\r\n", d); break; case 'r': parse_action(PARSE_REPLACE, actions, d); break; case 's': return STRINGADD_SAVE; default: SEND_TO_Q("Invalid editor option.\r\n", d); break; } return STRINGADD_ACTION; } void parse_action(int command, char *string, Descr d) { void format_text(char **ptr_string, int mode, Descr d, unsigned int maxlen); extern int replace_str(char **string, char *pattern, char *replacement, int rep_all, unsigned int max_size); int indent = 0, rep_all = 0, flags = 0, replaced, i, line_low, line_high, j = 0; unsigned int total_len; char *s, *t, temp; switch (command) { case PARSE_HELP: sprintf(buf, "Editor command formats: /<letter>\r\n" "/a - aborts editor without saving\r\n" "/c - clears buffer\r\n" "/d# - deletes a line\r\n" "/e# <text> - changes line # to <text>\r\n" "/f - formats text\r\n" "/fi - formats text and indents\r\n" "/i# <text> - inserts <text> before line #\r\n" "/l - lists the buffer\r\n" "/n - lists the buffer with line numbers\r\n" "/r 'a' 'b' - replaces 1st occurance of <a> with <b>\r\n" "/ra 'a' 'b'- replaces all occurances of <a> with <b>\r\n" " usage: /r[a] 'pattern' 'replacement'\r\n" "/s - saves the buffer and exits\r\n"); SEND_TO_Q(buf, d); break; case PARSE_FORMAT: while (isalpha(string[j]) && j < 2) if (string[j++] == 'i' && !indent) { indent = TRUE; flags += FORMAT_INDENT; } format_text(d->str, flags, d, d->max_str); sprintf(buf, "Text formatted with%s indent.\r\n", indent ? "" : "out"); SEND_TO_Q(buf, d); break; case PARSE_REPLACE: while (isalpha(string[j]) && j < 2) if (string[j++] == 'a' && !indent) rep_all = 1; if ((s = strtok(string, "'")) == NULL) { SEND_TO_Q("Invalid format.\r\n", d); return; } else if ((s = strtok(NULL, "'")) == NULL) { SEND_TO_Q("Target string must be enclosed in single quotes.\r\n", d); return; } else if ((t = strtok(NULL, "'")) == NULL) { SEND_TO_Q("Replacement string must be enclosed in single quotes.\r\n", d); return; } else if ((total_len = ((strlen(t) - strlen(s)) + strlen(*d->str))) <= d->max_str) { if ((replaced = replace_str(d->str, s, t, rep_all, d->max_str)) > 0) { sprintf(buf, "Replaced %d occurance%sof '%s' with '%s'.\r\n", replaced, replaced != 1 ? "s " : " ", s, t); SEND_TO_Q(buf, d); } else if (replaced == 0) { sprintf(buf, "String '%s' not found.\r\n", s); SEND_TO_Q(buf, d); } else SEND_TO_Q("ERROR: Replacement string causes buffer overflow, aborted replace.\r\n", d); } else SEND_TO_Q("Not enough space left in buffer.\r\n", d); break; case PARSE_DELETE: switch (sscanf(string, " %d - %d ", &line_low, &line_high)) { case 0: SEND_TO_Q("You must specify a line number or range to delete.\r\n", d); return; case 1: line_high = line_low; break; case 2: if (line_high < line_low) { SEND_TO_Q("That range is invalid.\r\n", d); return; } break; } i = 1; total_len = 1; if ((s = *d->str) == NULL) { SEND_TO_Q("Buffer is empty.\r\n", d); return; } else if (line_low > 0) { while (s && i < line_low) if ((s = strchr(s, '\n')) != NULL) { i++; s++; } if (s == NULL || i < line_low) { SEND_TO_Q("Line(s) out of range; not deleting.\r\n", d); return; } t = s; while (s && i < line_high) if ((s = strchr(s, '\n')) != NULL) { i++; total_len++; s++; } if (s && (s = strchr(s, '\n')) != NULL) { while (*(++s)) *(t++) = *s; } else total_len--; *t = '\0'; RECREATE(*d->str, char, strlen(*d->str) + 3); sprintf(buf, "%d line%sdeleted.\r\n", total_len, total_len != 1 ? "s " : " "); SEND_TO_Q(buf, d); } else { SEND_TO_Q("Invalid, line numbers to delete must be higher than 0.\r\n", d); return; } break; case PARSE_LIST_NORM: *buf = '\0'; if (*string) switch(sscanf(string, " %d - %d ", &line_low, &line_high)) { case 0: line_low = 1; line_high = 999999; break; case 1: line_high = line_low; break; } else { line_low = 1; line_high = 999999; } if (line_low < 1) { SEND_TO_Q("Line numbers must be greater than 0.\r\n", d); return; } else if (line_high < line_low) { SEND_TO_Q("That range is invalid.\r\n", d); return; } *buf = '\0'; if (line_high < 999999 || line_low > 1) sprintf(buf, "Current buffer range [%d - %d]:\r\n", line_low, line_high); i = 1; total_len = 0; s = *d->str; while (s && i < line_low) if ((s = strchr(s, '\n')) != NULL) { i++; s++; } if (i < line_low || s == NULL) { SEND_TO_Q("Line(s) out of range; no buffer listing.\r\n", d); return; } t = s; while (s && i <= line_high) if ((s = strchr(s, '\n')) != NULL) { i++; total_len++; s++; } if (s) { temp = *s; *s = '\0'; strcat(buf, t); *s = temp; } else strcat(buf, t); sprintf(buf + strlen(buf), "\r\n%d line%sshown.\r\n", total_len, total_len != 1 ? "s " : " "); page_string(d, buf, TRUE); break; case PARSE_LIST_NUM: *buf = '\0'; if (*string) switch (scanf(string, " %d - %d ", &line_low, &line_high)) { case 0: line_low = 1; line_high = 999999; break; case 1: line_high = line_low; break; } else { line_low = 1; line_high = 999999; } if (line_low < 1) { SEND_TO_Q("Line numbers must be greater than 0.\r\n", d); return; } if (line_high < line_low) { SEND_TO_Q("That range is invalid.\r\n", d); return; } *buf = '\0'; i = 1; total_len = 0; s = *d->str; while (s && i < line_low) if ((s = strchr(s, '\n')) != NULL) { i++; s++; } if (i < line_low || s == NULL) { SEND_TO_Q("Line(s) out of range; no buffer listing.\r\n", d); return; } t = s; while (s && i <= line_high) if ((s = strchr(s, '\n')) != NULL) { i++; total_len++; s++; temp = *s; *s = '\0'; sprintf(buf, "%s%4d:\r\n", buf, (i-1)); strcat(buf, t); *s = temp; t = s; } if (s && t) { temp = *s; *s = '\0'; strcat(buf, t); *s = temp; } else if (t) strcat(buf, t); page_string(d, buf, TRUE); break; case PARSE_INSERT: half_chop(string, buf, buf2); if (*buf == '\0') { SEND_TO_Q("You must specify a line number before which to insert text.\r\n", d); return; } line_low = atoi(buf); strcat(buf2, "\r\n"); i = 1; *buf = '\0'; if ((s = *d->str) == NULL) { SEND_TO_Q("Buffer is empty, nowhere to insert.\r\n", d); return; } if (line_low > 0) { while (s && i < line_low) if ((s = strchr(s, '\n')) != NULL) { i++; s++; } temp = *s; *s = '\0'; if ((strlen(*d->str) + strlen(buf2) + strlen(s + 1) + 3) > d->max_str) { *s = temp; SEND_TO_Q("Insert text pushes buffer over maximum size, insert aborted.\r\n", d); return; } if (*d->str && **d->str) strcat(buf, *d->str); *s = temp; strcat(buf, buf2); if (s && *s) strcat(buf, s); RECREATE(*d->str, char, strlen(buf) + 3); strcpy(*d->str, buf); SEND_TO_Q("Line instered.\r\n", d); } else { SEND_TO_Q("LIne number must be higher than 0.\r\n", d); return; } break; case PARSE_EDIT: half_chop(string, buf, buf2); if (*buf == '\0') { SEND_TO_Q("You must specify a line number at which to tchange text.\r\n", d); return; } line_low = atoi(buf); strcat(buf2, "\r\n"); i = 1; *buf = '\0'; if ((s = *d->str) == NULL) { SEND_TO_Q("Buffer is empty, nothing to change.\r\n", d); return; } if (line_low > 0) { while (s && i < line_low) if ((s = strchr(s, '\n')) != NULL) { i++; s++; } if (s == NULL || i < line_low) { SEND_TO_Q("Line number out of range; change aborted.\r\n", d); return; } if (s != *d->str) { temp = *s; *s = '\0'; strcat(buf, *d->str); *s = temp; } strcat(buf, buf2); if ((s = strchr(s, '\n')) != NULL) { s++; strcat(buf, s); } if (strlen(buf) > d->max_str) { SEND_TO_Q("Change causes new length to exceed buffer maximum size, aborted.\r\n", d); return; } RECREATE(*d->str, char, strlen(buf) + 3); strcpy(*d->str, buf); SEND_TO_Q("Line changed.\r\n", d); } else { SEND_TO_Q("Line number must be higher than 0.\r\n", d); return; } break; default: SEND_TO_Q("Invalid option.\r\n", d); syslog(0, TRUE, "SYSERR: invalid command passed to parse_action"); return; } } void format_text(char **ptr_string, int mode, Descr d, unsigned int maxlen) { int line_chars, cap_next = TRUE, cap_next_next = FALSE; char *flow, *start = NULL, temp; char formatted[MAX_STRING_LENGTH]; if (d->max_str > MAX_STRING_LENGTH) { log("SYSERR: format_text: max_str is greater than buffer size."); return; } if ((flow = *ptr_string) == NULL) return; if (IS_SET(mode, FORMAT_INDENT)) { strcpy(formatted, " "); line_chars = 3; } else { *formatted = '\0'; line_chars = 0; } while (*flow) { while (*flow && strchr("\n\r\f\t\v ", *flow)) flow++; if (*flow) { start = flow++; while (*flow && !strchr("\n\r\f\t\v .?!", *flow)) flow++; if (cap_next_next) { cap_next_next = FALSE; cap_next = TRUE; } while (strchr(".!?", *flow)) { cap_next_next = TRUE; flow++; } temp = *flow; *flow = '\0'; if (line_chars + strlen(start) + 1 > 79) { strcat(formatted, "\r\n"); line_chars = 0; } if (!cap_next) { if (line_chars > 0) { strcat(formatted, " "); line_chars++; } } else { cap_next = FALSE; *start = UPPER(*start); } line_chars += strlen(start); strcat(formatted, start); *flow = temp; } if (cap_next_next) { if (line_chars + 3 > 79) { strcat(formatted, "\r\n"); line_chars = 0; } else { strcat(formatted, " "); line_chars += 2; } } } strcat(formatted, "\r\n"); if (strlen(formatted) + 1 > maxlen) formatted[maxlen-1] = '\0'; RECREATE(*ptr_string, char, MIN(maxlen, strlen(formatted) + 1)); strcpy(*ptr_string, formatted); } int replace_str(char **string, char *pattern, char *replacement, int rep_all, unsigned int max_size) { char *replace_buffer = NULL; char *flow, *jetsam, temp; int len, i; if ((strlen(*string) - strlen(pattern)) + strlen(replacement) > max_size) return -1; CREATE(replace_buffer, char, max_size); i = 0; jetsam = *string; flow = *string; *replace_buffer = '\0'; if (rep_all) { while ((flow = (char *)strstr(flow, pattern)) != NULL) { i++; temp = *flow; *flow = '\0'; if ((strlen(replace_buffer) + strlen(jetsam) + strlen(replacement)) > max_size) { i = -1; break; } strcat(replace_buffer, jetsam); strcat(replace_buffer, replacement); *flow = temp; flow += strlen(pattern); jetsam = flow; } strcat(replace_buffer, jetsam); } else { if ((flow = (char *)strstr(*string, pattern)) != NULL) { i++; flow += strlen(pattern); len = ((char *)flow - (char *)*string) - strlen(pattern); strncpy(replace_buffer, *string, len); strcat(replace_buffer, replacement); strcat(replace_buffer, flow); } } if (i <= 0) return 0; else { RECREATE(*string, char, strlen(replace_buffer) + 3); strcpy(*string, replace_buffer); } free(replace_buffer); return i; }