/****************************************************************************
** pDIRT: GPE (General Purpose Editor)
**
** Author : Peter Eussen
** Version : 1.01
** Last Revision: 18 Jan 1996
**
** This editor replaces the old 'writer' with a almost fully featured
** editor. Although the editing has been greatly improved, the interface
** has simplified, which causes some problems with implementation, but
** that's not of my concern any more, since it works for me ;)
**
** To start the editor, use the start_editor() function which needs:
** Player number, function that handles the file created by the editor
** Maximum amount of lines the player can type, and an optional parameter,
** which you can use to store data (use NULL if not needed).
**
** The editor allows you to review your text, overwrite lines, delete lines,
** append lines, cancel and quit. The prompt is automatically built.
**************************************************************************/
#include "kernel.h"
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "mud.h"
#include "log.h"
#include "parse.h"
#include "editor.h"
#include "bprintf.h"
/****************************************************************************
** PROTOTYPES
****************************************************************************/
void editor_reader(char *inp);
void end_editor(void);
void delete_line(int i);
void review(Line *start);
void reinitialise_editor(void);
Line *find_line(int nr);
static char *Edit_Header =
"&+w[ &+wpDirt&+w Editor V1.01 ========================================================&+w]&*\n"
"&+w[ &+w~c &+wCancel Editor &+w~d <n> &+wDelete Line &+wn&* &+w]&*\n"
"&+w[ &+w~q &+wEnd Editor &+w~r &+wReview Current Message &+w]&*\n"
"&+w[ &+w~h &+wEditor Help &+w~e <command> &+wExecute &+wCommand&*. &+w]&*\n"
"&+w[ &+w~a &+wAppend at the End. &+w~o <n> &+wStart Overwriting at line &+wn&* &+w]&*\n"
"&+w[&+w============================================================================&+w]&*\n";
int start_file_editor(FILE *outp, void (*func)(char *,char *), int max_lines,
char *fn)
{ if (cur_player->editor.active)
{ bprintf("Close other editor first.\n");
return 0;
}
if (fn == NULL)
{ mudlog("ERROR: start_file_writer() with no filename");
return -1;
}
if (outp == NULL)
{ mudlog("ERROR: start_file_writer() with no file.");
return -1;
}
bprintf(Edit_Header);
cur_player->editor.fp = outp;
strcpy(cur_player->editor.filename,fn);
strcpy(cur_player->editor.old_prompt,cur_player->prompt);
cur_player->editor.func = func;
cur_player->editor.start = NULL;
cur_player->editor.current = NULL;
cur_player->editor.parameter = NULL;
cur_player->editor.num_lines = 0;
cur_player->editor.max_lines = max_lines;
cur_player->editor.active = True;
cur_player->editor.mode = Append;
push_input_handler(editor_reader);
sprintf(cur_player->prompt,"&+w[&+w%.2d&+w]: &*",cur_player->editor.num_lines+1);
bprintf("\r%s",cur_player->prompt);
return 1;
}
/*****************************************************************************
** START_EDITOR
** Initialisation routine. Sets up all parameters needed for this editor.
*****************************************************************************/
int start_editor(int player, void (*func)(char *,char *),int max_lines, char *param)
{ int save_mynum = real_mynum;
setup_globals(player);
if (cur_player->editor.active)
{ bprintf("Close other editor first.\n");
return 0;
}
bprintf(Edit_Header);
sprintf(cur_player->editor.filename,"%s/TMP/Editor.%s",data_dir,pname(mynum));
cur_player->editor.fp = fopen(cur_player->editor.filename,"w");
if (cur_player->editor.fp == NULL)
{ fwerror(cur_player->editor.filename);
return -1;
}
strcpy(cur_player->editor.old_prompt,cur_player->prompt);
cur_player->editor.func = func;
cur_player->editor.start = NULL;
cur_player->editor.current = NULL;
cur_player->editor.num_lines = 0;
cur_player->editor.max_lines = max_lines;
cur_player->editor.active = True;
cur_player->editor.mode = Append;
if (param == NULL)
cur_player->editor.parameter = NULL;
else
{ cur_player->editor.parameter = NEW(char,strlen(param) + 1);
strcpy(cur_player->editor.parameter,param);
}
push_input_handler(editor_reader);
sprintf(cur_player->prompt,"&+w[&+w%.2d&+w]: &*",cur_player->editor.num_lines+1);
bprintf("\r%s",cur_player->prompt);
setup_globals(save_mynum);
return 1;
}
void editor_reader(char *inp)
{ Line *l=NULL;
if (inp == NULL)
replace_input_handler(editor_reader);
else if (inp[0] == '~' || inp[0] == '$' || inp[0] == '*')
{ /* Process commands */
switch(inp[1]) {
case 'c' : /* Cancel Editor */
case 'C' :
bprintf("&+wEditor Cancelled.\n");
end_editor();
unlink(cur_player->editor.filename);
cur_player->editor.func(NULL,cur_player->editor.parameter);
reinitialise_editor();
return;
case '*' : /* Quit Editor */
case 'q' :
case 'Q' :
bprintf("&+wQuitting Editor.\n");
end_editor();
if (cur_player->editor.num_lines == 0)
cur_player->editor.func(NULL,cur_player->editor.parameter);
else
cur_player->editor.func(cur_player->editor.filename,
cur_player->editor.parameter);
reinitialise_editor();
return;
case 'D' : /* Delete line or last line */
case 'd' :
inp += 2;
while(!isdigit(*inp) && *inp != '\0')
inp++;
if (isdigit(*inp))
delete_line(atoi(inp));
else
delete_line(cur_player->editor.num_lines);
break;
case 'o': /* Start Overwrite mode */
case 'O':
inp += 2;
while(!isdigit(*inp) && *inp != '\0')
inp++;
if (!isdigit(*inp))
bprintf("&+wOverwrite which line?\n");
else
{ if (atoi(inp) > cur_player->editor.num_lines)
{ bprintf("&+wYou still need to type that line.\n");
break;
}
cur_player->editor.current = find_line(atoi(inp));
if (cur_player->editor.current == NULL)
{ cur_player->editor.mode = Append;
sprintf(cur_player->prompt,"&+w[&+w%.2d&+w]: &*",cur_player->editor.current->nr+1);
}
else
{ cur_player->editor.mode = Overwrite;
sprintf(cur_player->prompt,"&+w[&+w%.2d&+w]&+w> &*",cur_player->editor.current->nr);
}
}
break;
case 'a': /* Start Append Mode */
case 'A':
bprintf("&+wAppending after line %d\n",cur_player->editor.num_lines);
cur_player->editor.current = find_line(cur_player->editor.num_lines);
cur_player->editor.mode = Append ;
sprintf(cur_player->prompt,"&+w[&+w%.2d&+w]: &*",cur_player->editor.num_lines+1);
break;
case 'r': /* Review current message */
case 'R':
review(cur_player->editor.start);
break;
case 'h': /* Command information */
case 'H':
bprintf("&+wThe Editor has the following commands: \n"
" &+w~q : &+wEnd Editor and save message.\n"
" &+w~c : &+wEnd Editor and do NOT save the message.\n"
" &+w~d <n> : &+wDelete line &+wn&+w.\n"
" &+w~r : &+wReview the current text.\n"
" &+w~a : &+wAppend at the end of the file.\n"
" &+w~o <n> : &+wStart Overwriting at line &+wn&+w.\n"
" &+w~h : &+wThis help file.\n"
" &+w~e <command>: &+wExecute a command.\n"
"&+wBecause some mud clients have a problem with the &+w'~'&+w char, you can also use the &+w'$'&*.\n");
break;
case 'e': /* Execute a game command */
case 'E':
inp += 2;
while (isspace(*inp))
inp++;
Game_Com(inp,False);
break;
default:
bprintf("Unknown command, use &+w*h&* for help.\n");
}
}
else if (cur_player->editor.mode == Overwrite)
{ l = cur_player->editor.current;
if (l->line != NULL)
free(l->line);
l->line = NEW(char,strlen(inp) + 2);
strcpy(l->line,inp);
strcat(l->line,"\n");
if (l->next != NULL)
{ cur_player->editor.current = l->next;
sprintf(cur_player->prompt,"&+w[&+w%.2d&+w]&+w> &*",cur_player->editor.current->nr);
}
else
{ /* Reached the end of the file, switch to append */
cur_player->editor.mode = Append;
sprintf(cur_player->prompt,"&+w[&+w%.2d&+w]: &*",cur_player->editor.num_lines+1);
}
}
else
{ char *p;
if (cur_player->editor.num_lines == cur_player->editor.max_lines)
{ bprintf("&+wWarning: &+wBuffer is full, can't add new lines to the text.\n");
bprintf("\r%s\n",cur_player->prompt);
return;
}
for (p = inp; strlen(p) > 0 && cur_player->editor.num_lines < cur_player->editor.max_lines; )
{
l = NEW(Line,1);
if (strlen(p) > 74)
{ l->line = NEW(char,77);
strncpy(l->line,p,74);
strcat(l->line,"\n");
p += 74;
}
else
{ l->line = NEW(char,strlen(p) +2);
strcpy(l->line,p);
strcat(l->line,"\n");
p += strlen(p);
}
l->nr = ++cur_player->editor.num_lines;
if (cur_player->editor.start == NULL)
{ cur_player->editor.start = l;
l->next = NULL;
}
if (cur_player->editor.current != NULL)
{ l->next = cur_player->editor.current->next;
cur_player->editor.current->next = l;
}
cur_player->editor.current = l;
}
if (cur_player->editor.num_lines == (cur_player->editor.max_lines -5))
bprintf("&+wWarning&* &+wReaching Buffer Limit, please end your message.\n");
else if (cur_player->editor.num_lines == cur_player->editor.max_lines -5)
bprintf("&+wWarning &+wBuffer full, line could not be added completely.\n");
sprintf(cur_player->prompt,"&+w[&+w%.2d&+w]: &*",cur_player->editor.num_lines+1);
}
bprintf("\r%s",cur_player->prompt);
}
/* A little tricky, but the termination process is broken in two. This is
* done cause sometimes you want to open the file in the handler, to write
* some additional data. But it is asking for problems if you have two
* links for writing to the same file, so that is done in end_editor().
* This handles the cleanup of the file, if it is still there, and
* reinitialises the data.
*/
void reinitialise_editor(void)
{ int fd;
if (cur_player->editor.parameter != NULL)
free(cur_player->editor.parameter);
cur_player->editor.parameter = NULL;
cur_player->editor.func = NULL;
cur_player->editor.num_lines = 0;
fd = open(cur_player->editor.filename,O_RDONLY);
if (fd != -1)
{ close(fd);
unlink(cur_player->editor.filename);
}
bprintf("\r%s",cur_player->prompt);
}
/* Writes buffer to a temporary file, and closes the file. Also resets the
* input handler as it was before the writer was started.
*/
void end_editor(void)
{ Line *l = cur_player->editor.start, *p;
while (l != NULL)
{ fputs(l->line,cur_player->editor.fp);
free(l->line);
p = l;
l = l->next;
free(p);
}
cur_player->editor.start = cur_player->editor.current = NULL;
cur_player->editor.active = False;
fclose(cur_player->editor.fp);
strcpy(cur_player->prompt,cur_player->editor.old_prompt);
pop_input_handler();
}
void delete_line(int i)
{ Line *p = cur_player->editor.start, *l;
if (i > cur_player->editor.num_lines || i < 1 || p == NULL)
{ bprintf("&+wThere is nothing to be deleted.\n");
return;
}
if (i == 1)
{ cur_player->editor.start = p->next;
cur_player->editor.num_lines--;
if (p->next == NULL)
cur_player->editor.current = NULL;
free(p->line);
free(p);
l = cur_player->editor.start; /* Update line numbers */
while (l != NULL)
{ l->nr--;
l = l->next;
}
if (cur_player->editor.start == NULL && cur_player->editor.mode == Overwrite)
cur_player->editor.mode = Append;
}
else
{ l = p;
while (l->next != NULL && l->next->nr != i)
l = l->next;
if (l->next == NULL)
{ bprintf("&+wLine not found.\n");
return;
}
if (l->next->nr == i)
{ p = l->next;
l->next = p->next;
if (cur_player->editor.current == p)
{ if (cur_player->editor.mode == Overwrite)
{ if (l->next == NULL)
{ cur_player->editor.mode = Append;
cur_player->editor.current = l;
}
else
cur_player->editor.current = l->next;
}
else
cur_player->editor.current = l;
}
cur_player->editor.num_lines--;
free(p->line);
free(p);
l = l->next; /* Update line numbers */
while (l != NULL)
{ l->nr--;
l = l->next;
}
}
}
bprintf("&+wDeleted line %d.\n",i);
sprintf(cur_player->prompt,"&+w[&+w%.2d&+w]%s &*",
cur_player->editor.mode == Overwrite ? cur_player->editor.current->nr :
cur_player->editor.num_lines+1,
cur_player->editor.mode == Overwrite ? "&+w>" : ":");
}
void review(Line *l)
{ bprintf("&+wThis is what you typed sofar: \n&+w%s&*\n",DOUBLELINE);
while (l != NULL)
{ bprintf("&+w[&+w%.2d&+w]&* %s",l->nr,l->line);
l = l->next;
}
bprintf("&+w%s\n&+wEnd of Message.\n",DOUBLELINE);
}
Line *find_line(int nr)
{ Line *l = cur_player->editor.start;
if (l == NULL)
return l;
while (l->nr != nr && l->next != NULL)
l = l->next;
return l;
}
void terminate_editors(void)
{ int plx,save = real_mynum;
for (plx = 0; plx < max_players; plx++)
{ if (players[plx].editor.active)
{ setup_globals(real_mynum);
end_editor();
reinitialise_editor();
}
}
setup_globals(save);
}
/* Since the data is finally written to a file, we can use stat() to get the
* filesize.
*/
long editor_message_size(char *filename)
{ struct stat buffer;
stat(filename,&buffer);
return buffer.st_size + 1;
}