/* * New editor code * Author: Cronel (cronel_kal@hotmail.com) * of FrozenMUD (empire.digiunix.net 4000) * * Permission to use and distribute this code is granted provided * this header is retained and unaltered, and the distribution * package contains all the original files unmodified. * If you modify this code and use/distribute modified versions * you must give credit to the original author(s). */ #include <stdio.h> #include <string.h> #include <ctype.h> #include "mud.h" /**************************************************************************** * Data types and other definitions */ typedef struct editor_line EDITOR_LINE; #define CHAR_BLOCK (80) #define BLOCK_ROUNDUP( size ) (((size)+CHAR_BLOCK-1) / CHAR_BLOCK * CHAR_BLOCK) #define NOLIMIT (-1) #define RESIZE_IF_NEEDED( buf, buf_size, buf_used, added_use ) \ if( (buf_used) + (added_use) >= (buf_size) ) \ { \ sh_int added_size; \ added_size = BLOCK_ROUNDUP( added_use ); \ if( added_size == 0 ) \ added_size = CHAR_BLOCK; \ RECREATE( (buf), char, buf_size + added_size ); \ (buf_size) += added_size; \ } struct editor_line { char *line; /* line text */ sh_int line_size; /* size allocated in "line" */ sh_int line_used; /* bytes used of "line" */ EDITOR_LINE *next; }; struct editor_data { EDITOR_LINE *first_line; /* list of lines */ sh_int line_count; /* number of lines allocated */ EDITOR_LINE *on_line; /* pointer to the line being edited */ int text_size; /* total size of text (not counting newlines). */ int max_size; /* max size in chars of string being edited (counting newlines) */ char *desc; /* buffer description */ }; /* "max_size" is the maximum size of the final text converted to string */ /* "text_size" is equal to the strlen of all lines added up; the actual * total length when converted to string is equal to this number plus * line_count * 2, because of the trailing "\n\r" that has to be added * to each line (of course, plus 1 because of the final \0). * Thus, if(total_size + line_count * 2 +1) > max_size, the buffer cant * hold more data. */ /* Hence, this define: */ #define TOTAL_BUFFER_SIZE( edd ) (edd->text_size + edd->line_count * 2 +1 ) /**************************************************************************** * Function declarations */ /* funcs to manipulate editor datas */ EDITOR_LINE *make_new_line(char *str); /* void discard_editdata( EDITOR_DATA *edd ); */ EDITOR_DATA *clone_editdata(EDITOR_DATA * edd); EDITOR_DATA *str_to_editdata(char *str, sh_int max_size); char *editdata_to_str(EDITOR_DATA * edd); void discard_line_list(EDITOR_LINE * eline_list); EDITOR_LINE *detach_line_range(EDITOR_DATA * edd, sh_int from, sh_int to); void attach_line_range(EDITOR_DATA * edd, sh_int position, EDITOR_LINE * line_list); /* simple functions to set a description for what's currently * being edited */ void set_editor_desc(CHAR_DATA * ch, char *new_desc); void editor_desc_printf(CHAR_DATA * ch, char *desc_fmt, ...); /* the main editor functions visible to the rest of the code */ void start_editing_nolimit(CHAR_DATA * ch, char *old_text, sh_int max_total); char *copy_buffer(CHAR_DATA * ch); void stop_editing(CHAR_DATA * ch); /* main editing function */ void edit_buffer(CHAR_DATA * ch, char *argument); /* misc functions */ char *finer_one_argument(char *argument, char *arg_first); char *text_replace(char *src, char *word_src, char *word_dst, sh_int * pnew_size, sh_int * prepl_count); EDITOR_LINE *format_text(EDITOR_LINE * line_list, sh_int max_width); /* editor sub functions */ void editor_print_info(CHAR_DATA * ch, EDITOR_DATA * edd, char *argument); void editor_help(CHAR_DATA * ch, EDITOR_DATA * edd, char *argument); void editor_clear_buf(CHAR_DATA * ch, EDITOR_DATA * edd, char *argument); void editor_search_and_replace(CHAR_DATA * ch, EDITOR_DATA * edd, char *argument); void editor_insert_line(CHAR_DATA * ch, EDITOR_DATA * edd, char *argument); void editor_delete_line(CHAR_DATA * ch, EDITOR_DATA * edd, char *argument); void editor_goto_line(CHAR_DATA * ch, EDITOR_DATA * edd, char *argument); void editor_list(CHAR_DATA * ch, EDITOR_DATA * edd, char *argument); void editor_abort(CHAR_DATA * ch, EDITOR_DATA * edd, char *argument); void editor_escaped_cmd(CHAR_DATA * ch, EDITOR_DATA * edd, char *argument); void editor_save(CHAR_DATA * ch, EDITOR_DATA * edd, char *argument); void editor_format(CHAR_DATA * ch, EDITOR_DATA * edd, char *argument); /**************************************************************************** * Edit_data manipulation functions */ EDITOR_LINE *make_new_line(char *str) { EDITOR_LINE *new_line; sh_int size; size = strlen(str); size = BLOCK_ROUNDUP(size); if (size == 0) size = CHAR_BLOCK; CREATE(new_line, EDITOR_LINE, 1); CREATE(new_line->line, char, size); new_line->line_size = size; new_line->line_used = strlen(str); strcpy(new_line->line, str); return new_line; } void discard_editdata(EDITOR_DATA * edd) { discard_line_list(edd->first_line); if (edd->desc) STRFREE(edd->desc); DISPOSE(edd); } void discard_line_list(EDITOR_LINE * eline_list) { EDITOR_LINE *eline, *elnext; eline = eline_list; while (eline) { elnext = eline->next; DISPOSE(eline->line); DISPOSE(eline); eline = elnext; } } EDITOR_DATA *clone_editdata(EDITOR_DATA * edd) /* Returns a copy of the editor */ { EDITOR_DATA *new_edd; EDITOR_LINE *new_line, *eline, root_line; CREATE(new_edd, EDITOR_DATA, 1); new_line = &root_line; for (eline = edd->first_line; eline; eline = eline->next) { new_line->next = make_new_line(eline->line); if (edd->on_line == eline) new_edd->on_line = new_line->next; new_line = new_line->next; } new_edd->max_size = edd->max_size; new_edd->text_size = edd->text_size; new_edd->line_count = edd->line_count; new_edd->first_line = root_line.next; new_edd->desc = STRALLOC(edd->desc); return new_edd; } EDITOR_DATA *str_to_editdata(char *str, sh_int max_size) /* Converts a string to editdata. It will stop converting * if it reaches "max_size" and will return the partial * conversion at that point. */ { char *p; EDITOR_DATA *edd; EDITOR_LINE *eline; sh_int i; sh_int tsize, line_count; CREATE(edd, EDITOR_DATA, 1); eline = make_new_line(""); edd->first_line = eline; i = 0; tsize = 0; line_count = 1; p = str; while (*p) { if (max_size != NOLIMIT && tsize + line_count * 2 + 1 >= max_size) break; if (*p == '\r') ; else if (*p == '\n') { eline->line[i] = '\0'; eline->next = make_new_line(""); eline = eline->next; line_count++; i = 0; } else { eline->line[i] = *p; eline->line_used++; tsize++; i++; RESIZE_IF_NEEDED(eline->line, eline->line_size, eline->line_used, 1); } p++; } if (eline->line[0] != '\0') { eline->line[i] = '\0'; eline->next = make_new_line(""); line_count++; eline = eline->next; } edd->line_count = line_count; edd->on_line = eline; edd->max_size = max_size; edd->text_size = tsize; return edd; } /* * Removes the tildes from a line, except if it's the last character. */ void smush_tilde(char *str) { int len; char last; char *strptr; strptr = str; len = strlen(str); if (len) last = strptr[len - 1]; else last = '\0'; for (; *str != '\0'; str++) { if (*str == '~') *str = '-'; } if (len) strptr[len - 1] = last; return; } char *editdata_to_str(EDITOR_DATA * edd) { EDITOR_LINE *eline; char *buf, *src, *tmp; sh_int size, used, i; CREATE(buf, char, MSL); size = MSL; used = 0; buf[0] = '\0'; eline = edd->first_line; i = 0; while (eline) { /* ignore the last empty line */ if (eline->next == NULL && eline->line[0] == '\0') break; src = eline->line; while (*src) { buf[i++] = *src++; used++; if (used >= size - 3) { RECREATE(buf, char, size + MSL); size += MSL; } } buf[i++] = '\n'; buf[i++] = '\r'; used += 2; eline = eline->next; } buf[i++] = '\0'; used++; tmp = STRALLOC(buf); DISPOSE(buf); smush_tilde(tmp); return tmp; } sh_int get_online_index(EDITOR_DATA * edd) /* Returns the line number currently being edited * or -1 if there are no lines in the editor or the * insertion point is not positioned anywhere */ { sh_int i; EDITOR_LINE *eline; eline = edd->first_line; i = 0; while (eline) { i++; if (eline == edd->on_line) break; eline = eline->next; } if (eline == NULL) return -1; else return i; } void set_online_index(EDITOR_DATA * edd, sh_int index) /* Sets the insertion point of the editor to the given * index. If the index is out of range it will set it * to the first or the last line. */ { sh_int i; EDITOR_LINE *eline, *prev; if (edd->first_line == NULL) return; eline = edd->first_line; i = 1; prev = NULL; while (i < index && eline) { i++; prev = eline; eline = eline->next; } edd->on_line = eline ? eline : prev; } EDITOR_LINE *detach_line_range(EDITOR_DATA * edd, sh_int linefrom, sh_int lineto) /* Detaches a range of lines from the editor. If the line range is invalid, it * does nothing and returns NULL. If it's valid, it detaches it and returns it * as a list of lines. If the editor insertion point is somewhere within the * range it will leave it pointing immediately after or before the line range. * If the range comprises all the lines in the editor, it will leave the * editor empty. */ { EDITOR_LINE *eline_from_prev, *eline_to, *eline, root; sh_int i; bool move_on_line; if (linefrom < 1 || lineto > edd->line_count) { bug("detach_line_range: %d-%d Out of range (%d-%d).", linefrom, lineto, 1, edd->line_count); return NULL; } /* Find the start and end lines */ root.next = edd->first_line; eline = &root; i = 1; while (i < linefrom) { i++; eline = eline->next; } eline_from_prev = eline; /* We actualy need the line previous to the * first one, se we can delink it. Single * linked lists... */ while (i <= lineto) { i++; eline = eline->next; } eline_to = eline; /* Substract the sizes of all lines from the editor */ /* and check to see if the insertion point is within the range */ move_on_line = FALSE; eline = eline_from_prev->next; while (eline != eline_to->next) { if (eline == edd->on_line) move_on_line = TRUE; edd->text_size -= eline->line_used; edd->line_count--; eline = eline->next; } /* Leave the insertion point somewhere valid */ if (move_on_line) { if (eline_to->next) /* there's something bellow the range? leave it there */ edd->on_line = eline_to->next; else if (eline_from_prev != &root) /* nothing bellow the range, but something above it? there.. */ edd->on_line = eline_from_prev; else /* the entire buffer is being detached. leave it on NULL */ edd->on_line = NULL; } /* Delink the lines from the editor */ eline = eline_from_prev->next; eline_from_prev->next = eline_to->next; eline_to->next = NULL; edd->first_line = root.next; return eline; } void attach_line_range(EDITOR_DATA * edd, sh_int position, EDITOR_LINE * line_list) /* Inserts a given list of lines at the specified position within the * editor data. If the position is invalid, it will try to insert at the * beginning or end. The insertion point is not touched. */ { EDITOR_LINE root, *eline, *eline_last; sh_int i; if (position > edd->line_count && edd->line_count > 0) position = edd->line_count; else if (position < 1 || position > edd->line_count) position = 1; /* Add to text size of edd */ eline = line_list; eline_last = NULL; while (eline) { edd->text_size += eline->line_used; edd->line_count++; /* remember the last one on the list */ if (eline->next == NULL) eline_last = eline; eline = eline->next; } /* Find the position to insert */ root.next = edd->first_line; eline = &root; i = 1; while (i < position) { i++; eline = eline->next; } /* link it at the end */ eline_last->next = eline->next; eline->next = line_list; edd->first_line = root.next; } /**************************************************************************** * Main editor functions */ void set_editor_desc(CHAR_DATA * ch, char *new_desc) { if (!ch || !ch->editor) return; if (ch->editor->desc) STRFREE(ch->editor->desc); ch->editor->desc = STRALLOC(new_desc); } void editor_desc_printf(CHAR_DATA * ch, char *desc_fmt, ...) /* Just to save code... */ { char buf[MSL * 2]; /* umpf.. */ va_list args; va_start(args, desc_fmt); vsprintf(buf, desc_fmt, args); va_end(args); set_editor_desc(ch, buf); } void start_editing_nolimit(CHAR_DATA * ch, char *old_text, sh_int max_total) { if (!ch->desc) { bug("Fatal: start_editing: no desc", 0); return; } if (!old_text) old_text = ""; if (ch->substate == SUB_RESTRICTED) bug("NOT GOOD: start_editing: ch->substate == SUB_RESTRICTED", 0); set_char_color(AT_GREEN, ch); send_to_char("Begin entering your text now (/? = help /s = save /c = clear /l = list)\n\r", ch); send_to_char("-----------------------------------------------------------------------\n\r", ch); if (ch->editor) { stop_editing(ch); } act(AT_DGREEN, "$n starts editing.", ch, NULL, NULL, TO_NOTVICT); ch->editor = str_to_editdata(old_text, max_total); ch->editor->desc = STRALLOC("Unknown buffer"); ch->desc->connected = CON_EDITING; send_to_char("> ", ch); } char *copy_buffer(CHAR_DATA * ch) { char *buf; if (!ch) { bug("copy_buffer: null ch", 0); return STRALLOC(""); } if (!ch->editor) { bug("copy_buffer: null editor", 0); return STRALLOC(""); } buf = editdata_to_str(ch->editor); return buf; } void stop_editing(CHAR_DATA * ch) { set_char_color(AT_PLAIN, ch); discard_editdata(ch->editor); ch->editor = NULL; send_to_char("Done.\n\r", ch); ch->dest_buf = NULL; ch->spare_ptr = NULL; ch->substate = SUB_NONE; if (!ch->desc) { bug("Fatal: stop_editing: no desc", 0); return; } if (ch->pcdata->in_progress) /* Free the note */ free_global_note(ch->pcdata->in_progress); act(AT_DGREEN, "$n finishes editing.", ch, NULL, NULL, TO_NOTVICT); ch->desc->connected = CON_PLAYING; } void edit_buffer(CHAR_DATA * ch, char *argument) { DESCRIPTOR_DATA *d; EDITOR_DATA *edd; EDITOR_LINE *newline; char cmd[MIL]; sh_int linelen; bool cont_line; char *p; d = ch->desc; if (d == NULL) { send_to_char("You have no descriptor.\n\r", ch); return; } if (d->connected != CON_EDITING) { send_to_char("You can't do that!\n\r", ch); bug("Edit_buffer: d->connected != CON_EDITING", 0); return; } if (ch->substate <= SUB_PAUSE) { send_to_char("You can't do that!\n\r", ch); bug("Edit_buffer: illegal ch->substate (%d)", ch->substate); d->connected = CON_PLAYING; return; } if (!ch->editor) { send_to_char("You can't do that!\n\r", ch); bug("Edit_buffer: null editor", 0); d->connected = CON_PLAYING; return; } edd = ch->editor; if (argument[0] == '/' || argument[0] == '\\') { argument = one_argument(argument, cmd); if (!str_cmp(cmd + 1, "?")) editor_help(ch, edd, argument); else if (!str_cmp(cmd + 1, "c")) editor_clear_buf(ch, edd, argument); else if (!str_cmp(cmd + 1, "r")) editor_search_and_replace(ch, edd, argument); else if (!str_cmp(cmd + 1, "i")) editor_insert_line(ch, edd, argument); else if (!str_cmp(cmd + 1, "d")) editor_delete_line(ch, edd, argument); else if (!str_cmp(cmd + 1, "g")) editor_goto_line(ch, edd, argument); else if (!str_cmp(cmd + 1, "l")) editor_list(ch, edd, argument); else if (!str_cmp(cmd + 1, "a")) editor_abort(ch, edd, argument); else if (!str_cmp(cmd + 1, "s")) editor_save(ch, edd, argument); else if (!str_cmp(cmd + 1, "!")) editor_escaped_cmd(ch, edd, argument); else if (!str_cmp(cmd + 1, "p")) editor_print_info(ch, edd, argument); else if (!str_cmp(cmd + 1, "f")) editor_format(ch, edd, argument); else send_to_char("Uh? Type '/?' to see the list of valid editor commands.\n\r", ch); if (cmd[1] != 'a' && cmd[1] != 's') send_to_char("> ", ch); return; } /* Kludgy fix. Read_from_buffer in comm.c adds a space on * empty lines. Don't let this fill up usable buffer space.. */ if (argument[0] == ' ' && argument[1] == '\0') argument[0] = '\0'; linelen = strlen(argument); /* "Line continuation" feature to go around the "Line too long" * truncation forced by the input functions in comm.c (could be * done with /g too). */ p = argument + linelen - 1; while (p > argument && isspace(*p)) p--; if (p > argument && *p == '\\') { cont_line = TRUE; *p = '\0'; } else cont_line = FALSE; if (edd->max_size != NOLIMIT && TOTAL_BUFFER_SIZE(edd) + linelen + 2 >= edd->max_size) { send_to_char("Buffer full.\n\r", ch); editor_save(ch, edd, ""); } else { /* add it to the current line */ RESIZE_IF_NEEDED(edd->on_line->line, edd->on_line->line_size, edd->on_line->line_used, linelen + 1); strcat(edd->on_line->line, argument); edd->on_line->line_used += linelen; edd->text_size += linelen; /* create a line and advance to it */ if (!cont_line) { newline = make_new_line(""); newline->next = edd->on_line->next; edd->on_line->next = newline; edd->on_line = newline; edd->line_count++; } else send_to_char("(Continued)\n\r", ch); send_to_char("> ", ch); } } void editor_print_info(CHAR_DATA * ch, EDITOR_DATA * edd, char *argument) { ch_printf(ch, "Currently editing: %s\n\r" "Total lines: %4d On line: %4d\n\r" "Buffer size: %4d Max size: ", edd->desc ? edd->desc : "(Null description)", edd->line_count, get_online_index(edd), TOTAL_BUFFER_SIZE(edd)); if (edd->max_size == NOLIMIT) send_to_char("Infinite\n\r", ch); else ch_printf(ch, "%d\n\r", edd->max_size); } void editor_help(CHAR_DATA * ch, EDITOR_DATA * edd, char *argument) { sh_int i; char *arg[] = { "", "l", "c", "d", "g", "i", "r", "a", "p", "!", "s", "f", NULL }; char *editor_help[] = { /* general help */ "Editing commands\n\r" "---------------------------------\n\r" "/l [range] list buffer\n\r" "/c clear buffer\n\r" "/d <line> delete line\n\r" "/g <line> goto line\n\r" "/i <line> insert line\n\r" "/r <old> <new> global replace\n\r" "/a abort editing\n\r" "/p print information\n\r" "/! <command> execute command (do not use another editing command)\n\r" "/s save buffer\n\r" "/f [range] format paragraph\n\r" "Type /? <command> to get more information on each command.\n\r\n\r", "/l [range]: Lists the buffer. Shows what you've written. Optionaly\n\r" " takes a range of lines as argument.\n\r", "/c: Clears the buffer, leaving only one empty line.\n\r", "/d <line>: Deletes a line. If you delete the line currently being\n\r" " edited, the insertion point is moved down if possible, if not, up.\n\r", "/g <line>: Moves the insertion point to a given line.\n\r", "/i <line>: Inserts an empty line before the given line.\n\r", "/r <old text> <new text>: Global search and replace text. The arguments\n\r" " are case-sensitive. To replace a multi-word text, surround it with\n\r" " single quotes. When inside quotes, you must escape the single quote\n\r" " character, double quote character, and the bar: (') becomes (\\'),\n\r" " (\") becomes (\\\") and (\\) becomes (\\\\)\n\r", "/a: Aborts edition, terminating the edition session and throwing\n\r" " away what you've edited.\n\r", "/p: Prints information about the current editing session.\n\r", "/!: Escaped command. Executes the given command as if you were\n\r" " outside the editor. This is only allowed to imms, since it can\n\r" " potentialy crash the mud.\n\r", "/s: Saves the current buffer, terminating the edition session.\n\r", "/f [range] [width]; Format paragraph. Takes the text in the \n\r" " given lines and formats it to form a uniform looking \"paragraph\",\n\r" " by concatenating the lines and breaking them, doing word wrapping.\n\r" " It gets rid of excess spaces, respecting only the spaces at the\n\r" " beginning of the first line. First optional argument is the line\n\r" " range to wich the formatting is to be applied, wich can be two\n\r" " line numbers or the word \"all\". If no range is given, the\n\r" " formatting is applied to the whole buffer. Second optional argument\n\r" " is the maximum width of lines; this value defaults to 75 characters.\n\r", }; for (i = 0; arg[i] != NULL; i++) { if (!str_cmp(argument, arg[i])) break; } if (arg[i] == NULL) send_to_char("No editor help about that.\n\r", ch); else send_to_char(editor_help[i], ch); } void editor_clear_buf(CHAR_DATA * ch, EDITOR_DATA * edd, char *argument) { char *desc; sh_int max_size; max_size = edd->max_size; desc = STRALLOC(edd->desc); discard_editdata(edd); ch->editor = str_to_editdata("", max_size); ch->editor->desc = desc; send_to_char("Buffer cleared.\n\r", ch); } void editor_search_and_replace(CHAR_DATA * ch, EDITOR_DATA * edd, char *argument) { char word_src[MIL]; char word_dst[MIL]; EDITOR_DATA *cloned_edd; EDITOR_LINE *eline; char *new_text; sh_int new_size, repl_count, line_repl; argument = finer_one_argument(argument, word_src); argument = finer_one_argument(argument, word_dst); if (word_src[0] == '\0' || word_dst[0] == '\0') { send_to_char("Need word to replace, and replacement.\n\r", ch); return; } if (strcmp(word_src, word_dst) == 0) { send_to_char("Done.\n\r", ch); return; } /* Warning: the replacement of the word can result in the buffer growing * larger than its maximum allowed size. To control this, the buffer is * cloned, the replacement is applied to the clone, and if the size results * legal after the operation, the original buffer is discarded and the * clone is assigned as the current editing buffer. If the clone's size * results too large after the replacement, the clone is simply discarded * and a warning is given to the user * It's a lot of overhead but it'll have to do... */ cloned_edd = clone_editdata(edd); eline = cloned_edd->first_line; repl_count = 0; while (eline) { new_text = text_replace(eline->line, word_src, word_dst, &new_size, &line_repl); DISPOSE(eline->line); eline->line = new_text; cloned_edd->text_size -= eline->line_used; eline->line_used = strlen(eline->line); cloned_edd->text_size += eline->line_used; eline->line_size = new_size; repl_count += line_repl; eline = eline->next; } if (cloned_edd->max_size != NOLIMIT && TOTAL_BUFFER_SIZE(cloned_edd) >= cloned_edd->max_size) { send_to_char("As a result of this operation, the buffer would grow\n\r" "larger than its maximum allowed size. Operation has been\n\r" "cancelled.\n\r", ch); discard_editdata(cloned_edd); } else { ch_printf(ch, "Replacing all occurrences of '%s' with '%s'...\n\r", word_src, word_dst); discard_editdata(edd); ch->editor = cloned_edd; ch_printf(ch, "Found and replaced %d occurrence(s).\n\r", repl_count); } } void editor_insert_line(CHAR_DATA * ch, EDITOR_DATA * edd, char *argument) { sh_int lineindex; EDITOR_LINE *newline; if (argument[0] == '\0' || !is_number(argument)) { send_to_char("Must supply the line number.\n\r", ch); return; } lineindex = atoi(argument); if (lineindex < 1 || lineindex > edd->line_count) { ch_printf(ch, "Line number is out of range (1-%d).\n\r", edd->line_count); return; } newline = make_new_line(""); attach_line_range(edd, lineindex, newline); ch_printf(ch, "Inserted line at %d.\n\r", lineindex); } void editor_delete_line(CHAR_DATA * ch, EDITOR_DATA * edd, char *argument) { sh_int lineindex; EDITOR_LINE *del_line; if (argument[0] == '\0' || !is_number(argument)) { send_to_char("Must supply the line number.\n\r", ch); return; } lineindex = atoi(argument); if (lineindex < 1 || lineindex > edd->line_count) { ch_printf(ch, "Line number is out of range (1-%d).\n\r", edd->line_count); return; } if (edd->line_count == 1) { if (edd->first_line->line[0] != '\0') { edd->first_line->line[0] = '\0'; edd->first_line->line_used = 0; edd->text_size = 0; send_to_char("Deleted line 1.\n\r", ch); } else send_to_char("The buffer is empty.\n\r", ch); return; } del_line = detach_line_range(edd, lineindex, lineindex); /* TEST ME */ discard_line_list(del_line); ch_printf(ch, "Deleted line %d.\n\r", lineindex); } void editor_goto_line(CHAR_DATA * ch, EDITOR_DATA * edd, char *argument) { sh_int lineindex; if (argument[0] == '\0' || !is_number(argument)) { send_to_char("Must supply the line number.\n\r", ch); return; } lineindex = atoi(argument); if (lineindex < 1 || lineindex > edd->line_count) { ch_printf(ch, "Line number is out of range (1-%d).\n\r", edd->line_count); return; } set_online_index(edd, lineindex); ch_printf(ch, "On line %d.\n\r", lineindex); } void editor_list(CHAR_DATA * ch, EDITOR_DATA * edd, char *argument) { EDITOR_LINE *eline; sh_int line_num; sh_int from, to; char arg1[MIL]; argument = one_argument(argument, arg1); if (arg1[0] != '\0' && is_number(arg1)) from = atoi(arg1); else from = 1; argument = one_argument(argument, arg1); if (arg1[0] != '\0' && is_number(arg1)) to = atoi(arg1); else to = edd->line_count; send_to_pager("------------------\n\r", ch); line_num = 1; eline = edd->first_line; while (eline) { if (line_num >= from && line_num <= to) pager_printf(ch, "%2d>%c%s\n\r", line_num, eline == edd->on_line ? '*' : ' ', eline->line); eline = eline->next; line_num++; } send_to_pager("------------------\n\r", ch); } void editor_abort(CHAR_DATA * ch, EDITOR_DATA * edd, char *argument) { send_to_char("\n\rAborting... ", ch); stop_editing(ch); } void editor_escaped_cmd(CHAR_DATA * ch, EDITOR_DATA * edd, char *argument) { if (get_trust(ch) > LEVEL_IMMORTAL) { DO_FUN *last_cmd; int substate = ch->substate; last_cmd = ch->last_cmd; ch->substate = SUB_RESTRICTED; interpret(ch, argument); ch->substate = substate; ch->last_cmd = last_cmd; set_char_color(AT_GREEN, ch); send_to_char("\n\r", ch); } else send_to_char("You can't use '/!'.\n\r", ch); } void editor_save(CHAR_DATA * ch, EDITOR_DATA * edd, char *argument) { DESCRIPTOR_DATA *d; /* Used for Gboard and editor -- Xerves */ /* The prompt that the character is given after finishing a note with ~ or END */ const char *ssFinishPrompt = "(&WC&w)ontinue, (&WV&w)iew, (&WP&w)ost or (&WF&w)orget it?"; d = ch->desc; /* Modifications for Boards Code. If you got a better way, tell me. * I could use it =} */ if (!ch->pcdata->in_progress) { d->connected = CON_PLAYING; if (!ch->last_cmd) return; (*ch->last_cmd) (ch, ""); } else { ch->pcdata->in_progress->text = copy_buffer(ch); set_char_color(AT_PLAIN, ch); discard_editdata(ch->editor); ch->editor = NULL; send_to_char("Done.\n\r", ch); ch->dest_buf = NULL; ch->spare_ptr = NULL; ch->substate = SUB_NONE; if (!ch->desc) { bug("Fatal: editor_save: no desc", 0); return; } act(AT_DGREEN, "$n finishes editing.", ch, NULL, NULL, TO_NOTVICT); send_to_char("\n\r\n\r", ch); send_to_char_color(ssFinishPrompt, ch); send_to_char("\n\r", ch); d->connected = CON_NOTE_FINISH; } } void editor_format(CHAR_DATA * ch, EDITOR_DATA * edd, char *argument) { EDITOR_LINE *eline_list, *formatted_lines, *eline; sh_int lineto, linefrom, width, line_count; char arg1[MIL]; bool add_final_line, complete_format; width = 75; linefrom = 1; lineto = edd->line_count; /* parse out the arguments.. boring */ if (argument[0] != '\0') { argument = one_argument(argument, arg1); if (str_cmp(arg1, "all")) { if (argument[0] == '\0') { send_to_char("You must give both starting and ending line.\n\r", ch); return; } if (!is_number(arg1)) { send_to_char("Argument must be numeric.\n\r", ch); return; } if (atoi(arg1) > 0 && atoi(arg1) < edd->line_count) linefrom = atoi(arg1); argument = one_argument(argument, arg1); if (!is_number(arg1)) { send_to_char("Argument must be numeric.\n\r", ch); return; } if (atoi(arg1) < edd->line_count && atoi(arg1) > linefrom) lineto = atoi(arg1); } if (argument[0] == '\0') width = 75; else if (!is_number(argument)) { send_to_char("Argument must be numeric.\n\r", ch); return; } else { width = atoi(argument); if (width < 15 || width > 240) { send_to_char("Valid widths are from 15 to 240 characters.\n\r", ch); return; } } } complete_format = FALSE; add_final_line = FALSE; if (lineto == edd->line_count) { add_final_line = TRUE; if (linefrom == 1) complete_format = TRUE; } eline_list = detach_line_range(edd, linefrom, lineto); ch_printf(ch, "Formatting lines %d-%d (%d lines) at %d columns...\n\r", linefrom, lineto, lineto - linefrom + 1, width); formatted_lines = format_text(eline_list, width); /* count the number of formatted lines */ for (line_count = 0, eline = formatted_lines; eline; line_count++, eline = eline->next) ; attach_line_range(edd, linefrom, formatted_lines); if (add_final_line) { eline = edd->first_line; while (eline->next) eline = eline->next; eline->next = make_new_line(""); edd->line_count++; } /* Check the size! Check the size! */ if (edd->max_size != NOLIMIT && TOTAL_BUFFER_SIZE(edd) >= edd->max_size) { send_to_char("As a result of this operation, the buffer would grow\n\r" "larger than its maximum allowed size. Operation has been\n\r" "cancelled.\n\r", ch); formatted_lines = detach_line_range(edd, linefrom, linefrom + line_count - 1); attach_line_range(edd, linefrom, eline_list); } else { discard_line_list(eline_list); ch_printf(ch, "Formatted to %d lines.\n\r", 40); } if (complete_format) set_online_index(edd, edd->line_count); } /**************************************************************************** * Misc functions */ EDITOR_LINE *format_text(EDITOR_LINE * line_list, sh_int max_width) /* Formats the given list of lines neatly and returns the formatted * result in a new list. "Formatting" consists in wrapping lines to * have a max of "max_width" at most, doing word wrapping, and getting * rid of excess whitespace, except for the whitespace at the start * of the first line. */ { char *dest_buf; sh_int dest_used, dest_size; sh_int cur_width, i, j; sh_int start_word, end_word; EDITOR_LINE *eline, *last_line; EDITOR_DATA *edd; /* set up a destination buffer */ CREATE(dest_buf, char, CHAR_BLOCK); dest_used = 1; dest_size = CHAR_BLOCK; cur_width = 0; j = 0; /* set up source coordinates */ eline = line_list; i = 0; /* copy first line whitespace loop while space and while size is less than CHAR_BLOCK copy to destination */ while (isspace(eline->line[i]) && dest_used < CHAR_BLOCK) dest_buf[j++] = eline->line[i++]; dest_buf[j] = '\0'; cur_width = j; dest_used = j + 1; /* loop through lines */ while (eline) { /* loop within line */ while (eline->line[i] != '\0') { /* loop while space (ignore) */ while (isspace(eline->line[i]) && eline->line[i] != '\0') i++; if (eline->line[i] == '\0') break; start_word = i; /* loop while non-space */ while (!isspace(eline->line[i]) && eline->line[i] != '\0') i++; end_word = i; /* if current width plus the word is bigger than max width */ if (cur_width + (end_word - start_word) >= max_width) { /* resize if needed */ RESIZE_IF_NEEDED(dest_buf, dest_size, dest_used, 2); /* break a newline */ dest_buf[dest_used - 1] = '\0'; /* The -1 is to overwrite the last space */ strcat(dest_buf, "\n\r"); dest_used += 2; cur_width = 0; } /* resize if needed */ RESIZE_IF_NEEDED(dest_buf, dest_size, dest_used, (end_word - start_word) + 1); /* add the word, plus a space */ strncat(dest_buf, eline->line + start_word, (end_word - start_word)); strcat(dest_buf, " "); dest_used += (end_word - start_word) + 1; cur_width += (end_word - start_word) + 1; } /* advance the line coordinates */ eline = eline->next; i = 0; } /* convert the dest buffer to a list of lines */ edd = str_to_editdata(dest_buf, NOLIMIT); eline = edd->first_line; edd->first_line = NULL; discard_editdata(edd); DISPOSE(dest_buf); /* get rid of the last empty line .. */ if (eline->next) { last_line = eline; while (last_line->next->next) last_line = last_line->next; discard_line_list(last_line->next); last_line->next = NULL; } return eline; } char *text_replace(char *src, char *word_src, char *word_dst, sh_int * pnew_size, sh_int * prepl_count) /* Replaces a word word_src in src for word_dst. Returns a pointer to a newly * allocated buffer containing the line with the replacements. Stores in * pnew_size the size of the allocated buffer, wich may be different from the * length of the string and is a multiple of CHAR_BLOCK. Stores in prepl_count * the number of replacements it made */ { char *dst_buf; char *next_found, *last_found; sh_int dst_used, dst_size, len; sh_int repl_count; /* prepare the destination buffer */ CREATE(dst_buf, char, CHAR_BLOCK); dst_size = CHAR_BLOCK; dst_used = 0; dst_buf[0] = '\0'; last_found = src; repl_count = 0; for (;;) { /* look for next instance of word */ next_found = strstr(last_found, word_src); if (next_found == NULL) { /* if theres no more instances of word, * copy the rest of the src */ len = strlen(last_found); RESIZE_IF_NEEDED(dst_buf, dst_size, dst_used, len + 1); strcat(dst_buf, last_found); dst_used += len; break; } /* copy the buffer up to this instance of the word * and then copy the replacement word */ len = next_found - last_found + strlen(word_dst); RESIZE_IF_NEEDED(dst_buf, dst_size, dst_used, len + 1); strncat(dst_buf, last_found, next_found - last_found); strcat(dst_buf, word_dst); dst_used += len; last_found = next_found + strlen(word_src); repl_count++; } *pnew_size = dst_size; *prepl_count = repl_count; return dst_buf; } /* * Pick off one argument from a string and return the rest. * Understands quotes. * A pickier version than regular one_argument, it will not * convert to lowercase, and it can handle the (') character * when it's escaped inside '. */ char *finer_one_argument(char *argument, char *arg_first) { char cEnd; sh_int count; bool escaped; count = 0; while (isspace(*argument)) argument++; cEnd = ' '; if (*argument == '\'' || *argument == '"') cEnd = *argument++; escaped = FALSE; while (*argument != '\0' || ++count >= MIL) { if (cEnd != ' ' && escaped) { if (*argument == '\\') *arg_first = '\\'; else if (*argument == '\'') *arg_first = '\''; else if (*argument == '"') *arg_first = '"'; else *arg_first = *argument; arg_first++; argument++; escaped = FALSE; continue; } if (cEnd != ' ' && *argument == '\\' && !escaped) { escaped = TRUE; argument++; continue; } if (*argument == cEnd) { argument++; break; } *arg_first = *argument; arg_first++; argument++; } *arg_first = '\0'; while (isspace(*argument)) argument++; return argument; }