/************************************************************************** * File: modify.c Part of CircleMUD * * Usage: Run-time modification of game variables * * * * All rights reserved. See license.doc for complete information. * * * * 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 "spells.h" #include "mail.h" #include "boards.h" #include "olc.h" void show_string(struct descriptor_data *d, char *input); #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 extern struct spell_entry spells[]; extern char *MENU; /* local functions */ void string_add(struct descriptor_data *d, char *str); ACMD(do_skillset); char *next_page(char *str); int count_pages(char *str); void paginate_string(char *str, struct descriptor_data *d); const char *string_fields[] = { "name", "short", "long", "description", "title", "delete-description", "\n" }; /* * Maximum length for text field x+1 */ int length[] = { 15, 60, 256, 240, 60 }; /************************************* * Modification of malloc'ed strings.* *************************************/ /* * Handle some editor commands. */ void parse_action(int command, char *string, struct descriptor_data *d) { int indent = 0, rep_all = 0, flags = 0, total_len, replaced; register int j = 0; int i, line_low, line_high; char *s, *t, temp; switch (command) { case PARSE_HELP: sprintf(buf, "Editor command formats: /<letter>\r\n\r\n" "/a - aborts editor\r\n" "/c - clears buffer\r\n" "/d# - deletes a line #\r\n" "/e# <text> - changes the line at # with <text>\r\n" "/f - formats text\r\n" "/fi - indented formatting of text\r\n" "/h - list text editor commands\r\n" "/i# <text> - inserts <text> before line #\r\n" "/l - lists buffer\r\n" "/n - lists buffer with line numbers\r\n" "/r 'a' 'b' - replace 1st occurance of text <a> in buffer with text <b>\r\n" "/ra 'a' 'b'- replace all occurances of text <a> within buffer with text <b>\r\n" " usage: /r[a] 'pattern' 'replacement'\r\n" "/s - saves text\r\n"); SEND_TO_Q(buf, d); break; case PARSE_FORMAT: while (isalpha(string[j]) && j < 2) { switch (string[j]) { case 'i': if (!indent) { indent = TRUE; flags += FORMAT_INDENT; } break; default: break; } j++; } 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) { switch (string[j]) { case 'a': if (!indent) rep_all = 1; break; default: break; } j++; } 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("No replacement string.\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 ((i < line_low) || (s == NULL)) { 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)) { s++; while (*s != '\0') *(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: /* * Note: Rv's buf, buf1, buf2, and arg variables are defined to 32k so * they are probly ok for what to do here. */ *buf = '\0'; if (*string != '\0') 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); /* * This is kind of annoying...but some people like it. */ #if 0 sprintf(buf, "%s\r\n%d line%sshown.\r\n", buf, total_len, ((total_len != 1)?"s ":" ")); #endif page_string(d, buf, TRUE); break; case PARSE_LIST_NUM: /* * Note: Rv's buf, buf1, buf2, and arg variables are defined to 32k so * they are probly ok for what to do here. */ *buf = '\0'; if (*string != '\0') 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; } 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); /* * This is kind of annoying .. seeing as the lines are numbered. */ #if 0 sprintf(buf, "%s\r\n%d numbered line%slisted.\r\n", buf, total_len, ((total_len != 1)?"s ":" ")); #endif 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++; } if ((i < line_low) || (s == NULL)) { SEND_TO_Q("Line number out of range; insert aborted.\r\n", d); return; } 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 != '\0')) strcat(buf, *d->str); *s = temp; strcat(buf, buf2); if (s && (*s != '\0')) strcat(buf, s); RECREATE(*d->str, char, strlen(buf) + 3); strcpy(*d->str, buf); SEND_TO_Q("Line inserted.\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 change 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) { /* * Loop through the text counting \\n characters until we get to the line/ */ while (s && (i < line_low)) if ((s = strchr(s, '\n')) != NULL) { i++; s++; } /* * Make sure that there was a THAT line in the text. */ if ((i < line_low) || (s == NULL)) { SEND_TO_Q("Line number out of range; change aborted.\r\n", d); return; } /* * If s is the same as *d->str that means I'm at the beginning of the * message text and I don't need to put that into the changed buffer. */ if (s != *d->str) { /* * First things first .. we get this part into the buffer. */ temp = *s; *s = '\0'; /* * Put the first 'good' half of the text into storage. */ strcat(buf, *d->str); *s = temp; } /* * Put the new 'good' line into place. */ strcat(buf, buf2); if ((s = strchr(s, '\n')) != NULL) { /* * This means that we are at the END of the line, we want out of * there, but we want s to point to the beginning of the line * AFTER the line we want edited */ s++; /* * Now put the last 'good' half of buffer into storage. */ strcat(buf, s); } /* * Check for buffer overflow. */ if (strlen(buf) > d->max_str) { SEND_TO_Q("Change causes new length to exceed buffer maximum size, aborted.\r\n", d); return; } /* * Change the size of the REAL buffer to fit the new text. */ 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); mudlog("SYSERR: invalid command passed to parse_action", BRF, LVL_IMPL, TRUE); return; } } /* Add user input to the 'current' string (as defined by d->str). */ void string_add(struct descriptor_data *d, char *str) { FILE *fl; int terminator = 0, action = 0; register int i = 2, j = 0; char actions[MAX_INPUT_LENGTH]; /* determine if this is the terminal string, and truncate if so */ /* changed to only accept '@' at the beginning of line - J. Elson 1/17/94 */ /* Changed to accept '/<letter>' style editing commands - instead */ /* of solitary '@' to end. - M. Scott 10/15/96 */ delete_doubledollar(str); #if 0 /* Removed old handling of '@' character, put #if 1 to re-enable it. */ if ((terminator = (*str == '@'))) *str = '\0'; #endif #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 */ { char *tilde = str; while ((tilde = strchr(tilde, '~'))) *tilde = ' '; } #endif if ((action = (*str == '/'))) { while (str[i] != '\0') { actions[j] = str[i]; i++; j++; } actions[j] = '\0'; *str = '\0'; switch (str[1]) { case 'a': terminator = 2; /* Working on an abort message, */ break; 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': terminator = 1; *str = '\0'; break; default: SEND_TO_Q("Invalid option.\r\n", d); break; } } if (!(*d->str)) { if (strlen(str) > d->max_str) { send_to_char("String too long - Truncated.\r\n", d->character); *(str + d->max_str) = '\0'; /* Changed this to NOT abort out.. just give warning. */ /* terminator = 1; */ } CREATE(*d->str, char, strlen(str) + 3); strcpy(*d->str, str); } else { if (strlen(str) + strlen(*d->str) > d->max_str) { send_to_char("String too long, limit reached on message. Last line ignored.\r\n", d->character); /* terminator = 1; */ } else { if (!(*d->str = (char *) realloc(*d->str, strlen(*d->str) + strlen(str) + 3))) { perror("SYSERR: string_add"); exit(1); } strcat(*d->str, str); } } if (terminator) { /* * OLC Edits */ extern void oedit_disp_menu(struct descriptor_data *d); extern void oedit_disp_extradesc_menu(struct descriptor_data *d); extern void redit_disp_menu(struct descriptor_data *d); extern void redit_disp_extradesc_menu(struct descriptor_data *d); extern void redit_disp_exit_menu(struct descriptor_data *d); extern void medit_disp_menu(struct descriptor_data *d); extern void hedit_disp_menu(struct descriptor_data *d); extern void trigedit_disp_menu(struct descriptor_data *d); #if defined(OASIS_MPROG) extern void medit_change_mprog(struct descriptor_data *d); if (STATE(d) == CON_MEDIT) { switch (OLC_MODE(d)) { case MEDIT_D_DESC: medit_disp_menu(d); break; case MEDIT_MPROG_COMLIST: medit_change_mprog(d); break; } } #endif /* * Here we check for the abort option and reset the pointers. */ if ((terminator == 2) && ((STATE(d) == CON_REDIT) || (STATE(d) == CON_HEDIT) || (STATE(d) == CON_MEDIT) || (STATE(d) == CON_OEDIT) || (STATE(d) == CON_TRIGEDIT) || (STATE(d) == CON_TEXTED) || (STATE(d) == CON_EXDESC))) { free(*d->str); if (d->backstr) { *d->str = d->backstr; } else *d->str = NULL; d->backstr = NULL; d->str = NULL; } /* * This fix causes the editor to NULL out empty messages -- M. Scott * Fixed to fix the fix for empty fixed messages. -- gg */ else if ((d->str) && (*d->str) && (**d->str == '\0')) { free(*d->str); *d->str = str_dup("Nothing.\r\n"); } if (STATE(d) == CON_MEDIT) medit_disp_menu(d); if (STATE(d) == CON_TRIGEDIT) trigedit_disp_menu(d); if (STATE(d) == CON_OEDIT) { switch (OLC_MODE(d)) { case OEDIT_ACTDESC: oedit_disp_menu(d); break; case OEDIT_EXTRADESC_DESCRIPTION: oedit_disp_extradesc_menu(d); break; } } else if (STATE(d) == CON_HEDIT) hedit_disp_menu(d); else if (STATE(d) == CON_REDIT) { switch (OLC_MODE(d)) { case REDIT_DESC: redit_disp_menu(d); break; case REDIT_EXIT_DESCRIPTION: redit_disp_exit_menu(d); break; case REDIT_EXTRADESC_DESCRIPTION: redit_disp_extradesc_menu(d); break; } } else if (!d->connected && (PLR_FLAGGED(d->character, PLR_MAILING))) { if ((terminator == 1) && *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); d->mail_to = 0; free(*d->str); free(d->str); } else if (d->mail_to >= BOARD_MAGIC) { Board_save_board(d->mail_to - BOARD_MAGIC); SEND_TO_Q("Post not aborted, use REMOVE <post #>.\r\n", d); d->mail_to = 0; } else if (STATE(d) == CON_EXDESC) { if (terminator != 1) SEND_TO_Q("Description aborted.\r\n", d); SEND_TO_Q(MENU, d); d->connected = CON_MENU; } else if (STATE(d) == CON_TEXTED) { if (terminator == 1) { if (!(fl = fopen((char *)OLC_STORAGE(d), "w"))) { sprintf(buf, "SYSERR: Can't write file '%s'.", OLC_STORAGE(d)); mudlog(buf, CMP, LVL_IMPL, TRUE); } else { if (*d->str) { fputs(stripcr(buf1, *d->str), fl); } fclose(fl); sprintf(buf, "OLC: %s saves '%s'.", GET_NAME(d->character), OLC_STORAGE(d)); mudlog(buf, CMP, LVL_GOD, TRUE); SEND_TO_Q("Saved.\r\n", d); } } else SEND_TO_Q("Edit aborted.\r\n", d); act("$n stops editing some SENZUs.", TRUE, d->character, 0, 0, TO_ROOM); free(OLC_STORAGE(d)); OLC_STORAGE(d) = NULL; free(d->olc); STATE(d) = CON_PLAYING; } else if (!d->connected && d->character && !IS_NPC(d->character)) { if (terminator == 1) { if (strlen(*d->str) == 0) { free(*d->str); *d->str = NULL; } } else { free(*d->str); if (d->backstr) *d->str = d->backstr; else *d->str = NULL; d->backstr = NULL; SEND_TO_Q("Message aborted.\r\n", d); } } if (d->character && !IS_NPC(d->character)) REMOVE_BIT(PLR_FLAGS(d->character), PLR_WRITING | PLR_MAILING); if (d->backstr) free(d->backstr); d->backstr = NULL; d->str = NULL; } else if (!action) strcat(*d->str, "\r\n"); } /* ********************************************************************** * Modification of character skills * ********************************************************************** */ ACMD(do_skillset) { struct char_data *vict; char name[MAX_INPUT_LENGTH], buf2[128]; char buf[MAX_INPUT_LENGTH], help[MAX_STRING_LENGTH]; int skill, value, i, qend; argument = one_argument(argument, name); /* * No arguments. print an informative text. */ if (!*name) { send_to_char("Syntax: skillset <name> '<skill>' <value>\r\n", ch); strcpy(help, "Skill being one of the following:\r\n"); for (i = 0; spells[i].name[0] != '\n'; i++) { if (spells[i].name[0] == '!') continue; sprintf(help + strlen(help), "%18s", spells[i].name); if (i % 4 == 3) { strcat(help, "\r\n"); send_to_char(help, ch); *help = '\0'; } } if (*help) send_to_char(help, ch); send_to_char("\r\n", ch); return; } if (!(vict = get_char_vis(ch, name))) { send_to_char(NOPERSON, ch); return; } skip_spaces(&argument); /* If there is no chars in argument */ if (!*argument) { send_to_char("Skill name expected.\r\n", ch); return; } if (*argument != '\'') { send_to_char("Skill must be enclosed in: ''\r\n", ch); return; } /* Locate the last quote && lowercase the magic words (if any) */ for (qend = 1; *(argument + qend) && (*(argument + qend) != '\''); qend++) *(argument + qend) = LOWER(*(argument + qend)); if (*(argument + qend) != '\'') { send_to_char("Skill must be enclosed in: ''\r\n", ch); return; } strcpy(help, (argument + 1)); help[qend - 1] = '\0'; if ((skill = find_skill_num(help)) <= 0) { send_to_char("Unrecognized skill.\r\n", ch); return; } argument += qend + 1; /* skip to next parameter */ argument = one_argument(argument, buf); if (!*buf) { send_to_char("Learned value expected.\r\n", ch); return; } value = atoi(buf); if (value < 0) { send_to_char("Minimum value for learned is 0.\r\n", ch); return; } if (value > 100) { send_to_char("Max value for learned is 100.\r\n", ch); return; } if (IS_NPC(vict)) { send_to_char("You can't set NPC skills.\r\n", ch); return; } sprintf(buf2, "%s changed %s's %s to %d.", GET_NAME(ch), GET_NAME(vict), spells[skill].name, value); mudlog(buf2, BRF, -1, TRUE); SET_SKILL(vict, skill, value); sprintf(buf2, "You change %s's %s to %d.\r\n", GET_NAME(vict), spells[skill].name, value); send_to_char(buf2, ch); } /********************************************************************* * 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 we're at the end of the 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, struct descriptor_data *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(struct descriptor_data *d, char *str, int keep_internal) { if (!d) return; if (!str || !*str) { send_to_char("", d->character); return; } CREATE(d->showstr_vector, char *, d->showstr_count = count_pages(str)); 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(struct descriptor_data *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! Very handy. */ 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 = (int) d->showstr_vector[d->showstr_page + 1]; diff -= (int) 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++; } }