nuts4_141/
nuts4_141/n4/boards/0002/
nuts4_141/n4/boards/0003/
nuts4_141/n4/boards/0004/
nuts4_141/n4/boards/0006/
nuts4_141/n4/boards/0FFF/
nuts4_141/n4/etc/
nuts4_141/n4/pubgroups/0004/
nuts4_141/n4/pubgroups/0006/
nuts4_141/n4/users/0FFF/
nuts4_141/n4b/boards/0001/
nuts4_141/n4b/boards/0002/
nuts4_141/n4b/boards/0FFF/
nuts4_141/n4b/etc/
nuts4_141/n4b/pubgroups/0004/
nuts4_141/n4b/pubgroups/0005/
nuts4_141/n4b/src/
nuts4_141/n4b/users/0FFF/
/***************************************************************************
 FILE: cl_editor.cc
 LVU : 1.3.3

 DESC:
 This contains the code to create & run editor objects used in entering 
 profiles, mail etc.

 Copyright (C) Neil Robertson 2003,2004

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

 ***************************************************************************/

#include "globals.h"

#define SRC_PROMPT "\n~IP~FGSave, ~FYRedo, ~FRCancel~RS (s/r/c)?: "


/*** Constructor ***/
cl_editor::cl_editor(cl_user *own,int typ, char *path)
{
struct stat fs;
char *str;
int fd;

error = OK;
owner = own;
buff = NULL;
inc_size = 0;
inc_path = NULL;

// Can't use the editor when on a remote server since any input the user
// enters will go to the remote server and not the local editor.
if (owner->server_to) {
	error = ERR_USER_GONE_REMOTE;  return;
	}

type = typ;
stage = EDITOR_STAGE_INPUT;
bpos = 0;
line = 0;
malloced = 0;
maxchars = 0;
maxlines = 0;
isbatch = 0;

switch(type) {
	case EDITOR_TYPE_PROFILE:
	maxchars = max_profile_chars;
	str = "profile";
	break;

	case EDITOR_TYPE_LOGIN_BATCH:
	maxlines = max_login_batch_lines;
	str = "login batch";
	isbatch = 1;
	break;

	case EDITOR_TYPE_LOGOUT_BATCH:
	maxlines = max_logout_batch_lines;
	str = "logout batch";
	isbatch = 1;
	break;

	case EDITOR_TYPE_SESSION_BATCH:
	maxlines = max_session_batch_lines;
	str = "session batch";
	isbatch = 1;
	break;

	case EDITOR_TYPE_MAIL:
	maxchars = max_mail_chars;
	str = "mail";
	break;

	case EDITOR_TYPE_BOARD:
	maxchars = max_board_chars;
	str = "board message";
	break;

	case EDITOR_TYPE_GROUP_DESC:
	maxchars = max_group_desc_chars;
	str = "group description";
	break;

	case EDITOR_TYPE_BCAST:
	case EDITOR_TYPE_NET_BCAST:
	maxchars = max_broadcast_chars;
	str = "broadcast";
	break;


	default:
	owner->uprintf("~OL~FRINTERNAL ERROR:~RS Unknown editor type %d in cl_editor::cl_editor()!\n",type);
	log(1,"INTERNAL ERROR: Unknown editor type %d in cl_editor::cl_editor()!",type);
	error = ERR_INTERNAL;
	return;
	}

// Memory map file we wish to include in buffer
if (path) {
	inc_path = strdup(path);
	if ((fd = open(inc_path,O_RDONLY)) == -1) {
		error = ERR_CANT_OPEN_FILE;  return;
		}
	if (fstat(fd,&fs)) {
		close(fd);
		error = ERR_CANT_OPEN_FILE;
		return;
		}
	if ((inc_text = (char *)mmap(
		NULL,fs.st_size,PROT_READ,MAP_SHARED,fd,0)) == MAP_FAILED) {
		close(fd);
		error = ERR_MMAP_FAILED;
		return;
		}

	close(fd);
	inc_size = fs.st_size;

	if ((error = include_text()) != OK) return;
	error = OK;
	}


// Batches work in lines
if (isbatch) {
	if (maxlines) 
		owner->uprintf("\n~BB~FG*** Editing %s, maximum of %d lines ***\n\n",str,maxlines);
	else
		owner->uprintf("\n~BB~FG*** Editing %s, unlimited lines allowed ***\n\n");
	owner->uprintf("Enter a '.' on a line on its own to finish editing.\n");
	}
else {
	// Everything else works in characters
	if (maxchars)
		owner->uprintf("\n~BB~FG*** Editing %s, maximum of %d characters including newlines ***\n\n",str,maxchars);
	else 
		owner->uprintf("\n~BB~FG*** Editing %s, unlimited characters allowed ***\n\n",str);
	owner->uprintf("Enter a '.' on a line on its own to finish editing.\n");
	}
print_buffer();
}




/*** Destructor ***/
cl_editor::~cl_editor()
{
FREE(buff);
owner->editor = NULL;

if (inc_path) {
	// Test inc_size because if error occured on startup memory may
	// not have been mapped yet.
	if (inc_size) munmap(inc_text,inc_size);
	free(inc_path);
	}
}




/*** This does the actual business ***/
int cl_editor::run()
{
cl_splitline *com;
int i;

com = owner->com;
if (stage == EDITOR_STAGE_SRC) {
	// Choose what to do when done editing
	if (!com->wcnt || com->word[0][1]) {
		owner->uprintf(SRC_PROMPT);  return OK;
		}

	switch(toupper(com->word[0][0])) {
		case 'S':
		owner->uprintf("\n~FGEdit complete.\n\n");
		stage = EDITOR_STAGE_COMPLETE;
		return OK;

		case 'R':
		FREE(buff);
		bpos = 0;
		malloced = 0;
		line = 0;
		stage = EDITOR_STAGE_INPUT;
		if ((error = include_text()) != OK) return error;
		print_buffer();
		return OK;	

		case 'C':
		owner->uprintf("\n~FREdit cancelled.\n\n");
		stage = EDITOR_STAGE_CANCEL;
		return OK;

		default:
		owner->uprintf(SRC_PROMPT);
		}
	return OK;
	}

// Input stage. Check if we've finished editing and that user has 
// entered some input that contains more than whitespace.
if (com->wcnt && !strcmp(com->word[0],".")) {
	if (!malloced) {
		owner->uprintf("\n~FRNo input, edit cancelled.\n\n");
		stage = EDITOR_STAGE_CANCEL;
		return OK;
		}
	for(i=0;i < bpos;++i) if (buff[i] > 32) break;
	if (i == bpos) {
		owner->uprintf("\n~FRNo input, edit cancelled.\n\n");
		stage = EDITOR_STAGE_CANCEL;
		return OK;
		}
	owner->uprintf(SRC_PROMPT);
	stage = EDITOR_STAGE_SRC;
	return OK;
	}

if (append((char *)owner->tbuff) != OK) return ERR_MALLOC;
if (append("\n") != OK) return ERR_MALLOC;

// Batches are done by line
if (isbatch) {
	if (++line == maxlines) {
		owner->uprintf(SRC_PROMPT);
		stage = EDITOR_STAGE_SRC;
		return OK;
		}
	owner->uprintf("~IP~FT%02d>~RS ",line+1);
	return OK;
	}

// Everything else done by number of characters
if (maxchars && bpos >= maxchars) {
	if (bpos > maxchars) {
		buff[bpos-1] = '\0';  // Remove newline
		if (bpos - maxchars > 6) {
			buff[maxchars+3]='.';
			buff[maxchars+4]='.';
			buff[maxchars+5]='.';
			buff[maxchars+6]='\0';
			}
		owner->uprintf("\n~FYInput too long, truncated from:~RS \"%s\"\n",
			buff+maxchars);
		buff[maxchars] = '\0';
		}
	owner->uprintf(SRC_PROMPT);
	stage = EDITOR_STAGE_SRC;
	return OK;
	}
owner->uprintf("~IP~FT%03d>~RS ",bpos);
return OK;
}




/*** Append a string into the buffer ***/
int cl_editor::append(char *str)
{
int len;

len = strlen(str);
if (bpos + len >= malloced) {
	malloced = EDITOR_MALLOC * (((bpos + len) / EDITOR_MALLOC) + 1);
	if (!(buff = (char *)realloc(buff,malloced))) return ERR_MALLOC;
	if (!bpos) buff[0]='\0';
	}
strcat(buff + bpos,str);
bpos += len;
return OK;
}




/*** Include some text in the buffer prior to in the edit ***/
int cl_editor::include_text()
{
int len,start_line;
char *s;

if (!inc_text) return OK;

// Count lines
for(s=inc_text;*s;++s) line += (*s == '\n');
if (isbatch && maxlines && line > maxlines) return ERR_INCLUDE_FILE_TOO_BIG;

len = strlen(inc_text) + line + 1;
malloced = ((len / EDITOR_MALLOC) + 1) * EDITOR_MALLOC;

if (!(buff = (char *)malloc(malloced))) return ERR_MALLOC;

// start_line only used for NON batch files. Ie ones that have max lengths
// measured in characters. I know, its confusing. Deal.
if (!isbatch)
	start_line = line > max_include_lines ? line - max_include_lines : 0;

do {
	if (isbatch) bpos = 0;
	else {
		buff[0] = '>';  bpos = 1;
		}

	// Put included text in buffer
	for(s=inc_text,line=0;*s;++s) {
		if (*s == '\n') {
			line++;
			// If we don't do this we'll have a lone ">" at
			// top of edit
			if (!isbatch && line == start_line) ++s;
			}

		if (isbatch || line >= start_line) {
			// Put '>' at start of each line unless editing
			// batch file
			if (!isbatch && *s == '\n' && *(s+1)) {
				buff[bpos] = '\n';
				buff[++bpos] = '>';
				}
			else buff[bpos] = *s;
			++bpos;
			}
		}

	/* If bpos has only gone up by 1 (ie user has one big long line
	   which is too long to include and we've simply hit then end newline
	   then get rid of '>' */
	if (!isbatch) {
		if (bpos == 2) bpos=0;
		start_line++;
		}
	buff[bpos] = '\0';

	/* If its not a batch include and its too big loop around again 
	   and cut off first line. This is for message replies. Theres no
	   point doing the same for a batch file for obvious reasons. Make 
	   sure user has at least a third of "maxchars" characters to write
	   his reply in. */
	} while(!isbatch && maxchars && bpos >= maxchars - (maxchars / 3));

// Unmap file
if (inc_path) {
	munmap(inc_text,inc_size);
	FREE(inc_path);
	}

return (!isbatch && maxchars && bpos > maxchars) ? 
	ERR_INCLUDE_FILE_TOO_BIG : OK;
}




/*** Print prompts & contents of buffer so far. This is for when we have an 
     included message ***/
void cl_editor::print_buffer()
{
char *s,*s2,c;
int i;

// Print initial edit prompt
if (isbatch)
	owner->uprintf("\n~IP~FT01>~RS ");
else
	owner->uprintf("\n~IP~FT000>~RS ");

if (!bpos) return;

// Print buffer and further prompts
for(i=0,s2=buff;i < line;++i,s2=s+1) {
	for(s=s2;*s && *s != '\n';++s);
	c = *s;
	*s = '\0';
	if (isbatch)
		owner->uprintf("~NP%s\n~IP~FT%02d>~RS ",s2,i+2);
	else
		owner->uprintf("~NP%s\n~IP~FT%03d>~RS ",s2,s - buff + 1);
	*s = c;
	if (!c || !*(s+1)) break;
	}

// If included message has hit the or gone beyond limit then go straight
// to s/r/c prompt.
if ((isbatch && maxlines && line >= maxlines) ||
    (!isbatch && maxchars && bpos >= maxchars)) {
	owner->uprintf("\n~NP%s",SRC_PROMPT);
	stage = EDITOR_STAGE_SRC;
	}
}