pkg://jmr-0.7.23-1.src.rpm:177179/jmr-0.7.23.src.tar.gz
info downloads
jmrsrc-0.7.23/ 40755 764 764 0 6623366026 12452 5 ustar jvuokko jvuokko jmrsrc-0.7.23/src/ 40775 764 764 0 6623366026 13243 5 ustar jvuokko jvuokko jmrsrc-0.7.23/src/filelist.cc 100664 764 764 37750 6623366026 15516 0 ustar jvuokko jvuokko // $Id: filelist.cc,v 1.14 1998/11/14 20:40:56 jvuokko Exp $
/****************************************************************************
* *
* * MODULE : filelist.cc
* *
* * Copyright (c) 1997 Jukka Vuokko
* * See file COPYING for more information about copyrights.
* *
****************************************************************************
* *
* *
* * This module contains methods for handling file lists
* * (expand wildcards, select file etc...)
* *
* *
* *
***************************************************************************/
#include <stdio.h> // sprintf()
#ifdef __EMX__
# include <sys/types.h>
#endif
#ifdef __WATCOMC__
# include <direct.h>
#else
# include <dirent.h>
#endif
#include "jmr.hh"
#include "filelist.hh"
#include "../utilib/String.hh"
#include "../utilib/List.hh"
#include "mail.hh"
extern Terminal_screen Screen;
extern settings_t Settings;
extern Window* Wscript;
static const menuinfo filelist_menu_array[] = {
{"[jmr]", 0 },
{"Exit from menu", 0},
{"Quit jmr", FORCED_QUIT_CMD},
{"@NEXT", 0 },
{"@END", 0 }
};
Filelist::Filelist()
{
menubar.init( filelist_menu_array );
menubar.set_text( MENUBAR_TEXT );
for( int i = Screen.get_xmax(); i >= 0; i--) {
dashline += '-';
}
}
/***************************************************************************
* FUNCTION: compare_name
***************************************************************************
*
* DESCRIPTION: compares two strings. This is for qsort().
*
* EXTERNAL REFERENCES
* IN :
* OUT:
*
* RETURN: <0, 0 or >0
***************************************************************************/
int compare_name (const void *a, const void *b)
{
String **aa, **bb;
aa = (String**) a;
bb = (String**) b;
return (*aa)->compare (*(*bb));
}
Mail*
Filelist::open_file(const String& bbsid)
{
Mail *mail;
open_new = false;
int c;
if (bbsid.is_empty()) {
for (;;) {
mail = 0;
if (open_new == true) {
read_files (Settings.qwkpath);
pattern = Settings.searchpattern;
} else {
read_files (Settings.datadir);
pattern = "*.[gG][dD][bB]";
}
// keep only entries, that matches with pattern
search_matches();
c = select ();
Screen.bottom();
Screen.clr_eol();
//Screen.clear();
if (c == QUIT_CMD || c == FORCED_QUIT_CMD) {
break;
} else if (c == LOAD_CMD) {
open_new = open_new == true ? false : true;
} else if (c == SELECT_CMD) {
mail = Mail::create (filelist.get()->get(), open_new);
break;
}
}
} else {
mail = 0;
read_files(Settings.qwkpath);
pattern = bbsid + ".[qQ][wW][kK]";
if (search_matches()) {
mail = Mail::create (filelist.get()->get(), true);
} else {
filelist.destroy();
read_files (Settings.datadir);
pattern = bbsid + ".[gG][dD][bB]";
if (search_matches()) {
mail = Mail::create(filelist.get()->get(), false);
}
}
}
filelist.destroy();
return mail;
}
//**************************************************************************/
// CLASS: Filelist
// MEMBER FUNCTION: read_files
//
//
// DESCRIPTION: Reads all names of files from directory path
//
// EXTERNAL REFERENCES
// IN :
// OUT:
//
//**************************************************************************/
void
Filelist::read_files (const String& path)
{
DIR *dir;
struct dirent *ent;
String *str;
filelist.destroy();
if ( (dir = opendir ( path.get() )) == NULL) {
String tmp = "Unable to open mail directory: " + path;
handle_error (tmp.get(), HALT_FL);
}
while ((ent = readdir(dir)) != NULL) {
if (ent->d_name[0] != '.') {
str = new String;
str->put(ent->d_name);
filelist.add (str);
}
}
filelist.sort (compare_name);
if (closedir(dir) != 0)
handle_error("Unable to close mail directory", HALT_FL);
}
//**************************************************************************/
// CLASS: Filelist
// MEMBER FUNCTION: search_matches
//
//
// DESCRIPTION: Removes all files that does not match with pattern from list
//
//
// EXTERNAL REFERENCES
// IN :
// OUT:
//
// RETURNS: false if list is empty
//
//**************************************************************************/
bool
Filelist::search_matches()
{
bool rc;
String *str;
if (filelist.first() == false) {
return false;
}
do {
str = filelist.get();
if (check_matches (str->get(), pattern.get()) == false) {
rc = filelist.remove();
} else {
rc = filelist.next();
}
} while (rc == true);
return filelist.first();
}
//**************************************************************************/
// CLASS: Filelist
// MEMBER FUNCTION: select
//
//
// DESCRIPTION: 'User interface'.
//
//
// EXTERNAL REFERENCES
// IN :
// OUT:
//
// RETURNS: selected event.
//
//**************************************************************************/
int
Filelist::select()
{
int c;
first = 0;
current = 0;
ypos = ymin = 6;
ymax = Screen.get_ymax() - 5;
lines = ymax - ymin;
++lines;
//Screen.clear();
sline.init( Screen.get_xmax() + 1);
if (open_new == true) {
iline.set_default( PACKET_LIST_KEYS_MSG );
} else {
iline.set_default( DATABASE_LIST_KEYS_MSG );
}
display();
iline.show_default();
while(1) {
Screen.refresh();
c = Screen.get_ch();
iline.reset();
if (c == ESCAPE ) {
iline.set( MENU_MSG );
iline.show();
c = menubar.select();
display();
iline.reset();
}
switch (c) {
case MESSAGEWIN_CMD:
Wscript->enable();
iline.set ( MESSAGES_MSG );
iline.show();
Screen.get_ch();
iline.reset();
Wscript->disable();
break;
case CODE_UP:
case PREV_CMD:
prev();
break;
case CODE_DOWN:
case NEXT_CMD:
next();
break;
case CODE_NPAGE:
case PAGE_DOWN_CMD:
page_down();
break;
case CODE_PPAGE:
case PAGE_UP_CMD:
page_up();
break;
//case CODE_CTRL_L:
//display();
//break;
case CODE_HOME:
first = 0;
current = 0;
ypos = ymin;
display();
break;
case CODE_END:
current = filelist.count_items()-1;
// next line is not a bug! It's the only
// way to calc first index of page...
first = (current / lines) * lines ;
ypos = ymin + (current - first);
display();
break;
case SELECT_CMD:
case CODE_RIGHT:
if (filelist.check() == true) {
return SELECT_CMD;
}
break;
case FORCED_QUIT_CMD:
#ifndef NO_EXCEPTIONS
throw quit_exception();
#endif
case QUIT_CMD:
return QUIT_CMD;
case REFRESH_CMD:
return REFRESH_CMD;
case LOAD_CMD:
return LOAD_CMD;
case HELP_CMD:
show_help();
display();
break;
case 0:
break;
default:
iline.set( INVALID_COMMAND_MSG );
};
iline.show();
}
}
//**************************************************************************/
// CLASS: Filelist
// MEMBER FUNCTION: display
//
//
// DESCRIPTION: display files.
//
//
// EXTERNAL REFERENCES
// IN :
// OUT:
//
// RETURNS:
//
//**************************************************************************/
void
Filelist::display()
{
int i = 0;
int y;
Screen.erase();
menubar.show();
display_header();
if (open_new == true) {
sline.set_infotext( "List of QWK packets" );
} else {
sline.set_infotext ("List of databases" );
}
if (filelist.first() == false) {
Screen.gotoxy (0, ymin);
if (open_new == true) {
Screen.print( "No files matches with pattern \'%s\' "
"in a directory : \'%s\'",
pattern.c_str(),
Settings.qwkpath.c_str() );
} else {
Screen.print( "No message databases available." );
}
return;
}
while (i < first) {
filelist.next();
++i;
}
i = first;
y = ymin;
do {
Screen.gotoxy (0,y);
if (i == current) {
Screen.inverse();
ypos = y;
}
draw_current();
Screen.reset_attr();
++y;
++i;
} while (filelist.next() == true && y <= ymax);
if (filelist.count_items() <= ymax - ymin) {
sline.set_percent( ALL_PERCENT );
} else if (0 == first) {
sline.set_percent( TOP_PERCENT );
} else {
sline.set_percent( (100*i)/filelist.count_items() );
}
sline.show();
filelist.first();
i = current;
while (i) {
filelist.next();
--i;
}
iline.show( REFRESH_ALL_FL );
Screen.gotoxy (0,ypos);
}
//**************************************************************************/
// CLASS: Filelist
// MEMBER FUNCTION: draw_current
//
//
// DESCRIPTION: Displays name of the current file.
//
//
// EXTERNAL REFERENCES
// IN :
// OUT:
//
// RETURNS:
//
//**************************************************************************/
void
Filelist::draw_current()
{
Screen.empty_line();
Screen.goto_x( 8 );
Screen.print( "%s", filelist.get()->c_str() );
}
//**************************************************************************/
// CLASS: Filelist
// MEMBER FUNCTION: display_header
//
//
// DESCRIPTION: Displays header of screen...
//
//
// EXTERNAL REFERENCES
// IN :
// OUT:
//
// RETURNS:
//
//**************************************************************************/
void
Filelist::display_header()
{
Screen.reset_attr();
Screen.gotoxy( 0, 1 );
Screen.textcolor( Settings.header_subject_color );
Screen.print( "%s -- A QWK compatible offline mail reader",
Settings.version_str.c_str() );
Screen.textcolor( Settings.header_author_color );
Screen.print("\nCopyright (c) 1997,1998 Jukka Vuokko. All rights reserved.");
Screen.reset_attr();
Screen.print( "\nThis version is compiled at %s" , __DATE__ );
Screen.print( "\nOfficial jmr homepage: http://iki.fi/"
"jvuokko/jmr/");
Screen.gotoxy( 0, 5 );
set_dashline_color();
Screen.print( dashline.c_str() );
Screen.gotoxy( 7, 5 );
if (open_new == true) {
Screen.print( " packets: " );
} else {
Screen.print( " databases: " );
}
Screen.reset_attr ();
}
//**************************************************************************/
// CLASS: Filelist
// MEMBER FUNCTION: next
//
//
// DESCRIPTION: Moves to next file.
//
//
// EXTERNAL REFERENCES
// IN :
// OUT:
//
// RETURNS:
//
//**************************************************************************/
void
Filelist::next()
{
if (current < (filelist.count_items()-1)) {
if (ypos == ymax) {
first = ++current;
display();
} else {
Screen.gotoxy( 0, ypos );
Screen.empty_line();
draw_current();
Screen.gotoxy( 0, ypos+1 );
filelist.next();
Screen.inverse();
draw_current ();
Screen.reset_attr();
++current;
++ypos;
}
} else {
iline.set( AT_TOP_MSG );
}
}
//**************************************************************************/
// CLASS: Filelist
// MEMBER FUNCTION: prev
//
//
// DESCRIPTION: Moves to previous file.
//
//
// EXTERNAL REFERENCES
// IN :
// OUT:
//
// RETURNS:
//
//**************************************************************************/
void
Filelist::prev()
{
if (current > 0) {
if (ypos == ymin && first > 0) {
--current;
first -= lines;
display();
} else {
Screen.gotoxy( 0, ypos );
Screen.empty_line();
draw_current ();
Screen.gotoxy( 0, ypos-1);
Screen.inverse();
filelist.prev();
draw_current ();
Screen.reset_attr();
--current;
--ypos;
}
} else {
iline.set( AT_BOTTOM_MSG );
}
}
void
Filelist::page_down()
{
int max = filelist.count_items()-1;
if (current >= max) {
iline.set( AT_LAST_PAGE_MSG );
return;
}
if (ypos == ymax) {
first = ++current;
for (int i=1; i < lines && current < max; i++, current++);
ypos = ymin + (current - first);
} else {
while (current < max && ypos < ymax) {
++current;
++ypos;
}
}
display();
}
void
Filelist::page_up()
{
int i = lines;
if (current == 0) {
iline.set( AT_FIRST_PAGE_MSG );
return;
}
if (ypos == ymin) {
while (i && first > 0) {
--first;
--current;
--i;
}
} else {
while (ypos > ymin) {
--ypos;
--current;
}
}
display();
}
jmrsrc-0.7.23/src/groupselect.cc 100664 764 764 12710 6623366026 16224 0 ustar jvuokko jvuokko // $Id: groupselect.cc,v 1.4 1997/11/17 17:16:59 jvuokko Exp $
/*****************************************************************************
* *
* * MODULE: groupselect.cc
* * --------------
* ***************************************************************************
* *
* *
* * COPYRIGHT (C) 1997 JUKKA VUOKKO. ALL RIGHTS RESERVED
* ***************************************************************************
* *
* * This module contains functions for selecting group from
* * list of all groups.
* *
*****************************************************************************/
#include "groupselect.hh"
#include "mail.hh"
extern Terminal_screen Screen;
extern Mail *mail;
extern settings_t Settings;
Groupselection::Groupselection()
{
grpnode *gnode;
int savegrp;
win = Screen.newwin( 0, 0, Screen.get_xmax()+1, Screen.get_ymax(),
WIN_SLINE | WIN_TITLEBAR);
win->set_sline_text("* Available groups *");
if (Screen.is_color_term() == true ) {
win->set_border_colors( Settings.sline_color,
Settings.slineback_color);
} else {
win->set_border_colors( INVERSE, INVERSE );
}
win->disable();
savegrp = mail->get_group_number();
mail->first_group();
do {
if (mail->get_group_number() >= BASEGROUP_NUMBER) {
continue;
}
gnode = new grpnode;
gnode->number = mail->get_group_number();
gnode->name = mail->get_group_name();
itemlist.add( gnode );
DEBUG("Added to grouplist: " << gnode->name );
} while (mail->next_group() == true );
mail->go_group_number( savegrp );
}
Groupselection::~Groupselection()
{
Screen.endwin( win );
}
grpnode
Groupselection::select()
{
bool quit = false;
grpnode selected;
selected.number = mail->get_group_number();
selected.name = mail->get_group_name();
int savedgrp = selected.number;
ymin = 0;
ymax = win->get_ymax();
current_item = 0;
pagesize = ymax - ymin + 1;
maxitem = itemlist.count_items();
pages = 1+ (maxitem / pagesize);
--maxitem;
// go to current group
itemlist.first();
do {
if ( itemlist.get()->number == selected.number ) {
break;
}
} while( itemlist.next() == true && ++current_item );
current_page = current_item / pagesize;
first_of_page = current_page * pagesize;
ypos = ymin + (current_item - first_of_page );
win->enable();
iline.set_default("@ENTER@=select [@n@]ext [@p@]rev [@q@]uit" );
iline.show_default();
win->clear();
draw_page();
do {
int c = Screen.get_ch();
iline.reset();
switch( c ) {
case PAGE_UP_CMD:
case CODE_PPAGE:
if ( false == ppage() ) {
iline.set( AT_FIRST_PAGE_MSG );
}
break;
case PAGE_DOWN_CMD:
case CODE_NPAGE:
if( false == npage() ) {
iline.set( AT_LAST_PAGE_MSG );
}
break;
case CODE_HOME:
first();
break;
case CODE_END:
last();
break;
case NEXT_CMD:
case CODE_DOWN:
if( false == down() ) {
iline.set( AT_BOTTOM_MSG );
}
break;
case PREV_CMD:
case CODE_UP:
if( false == up() ) {
iline.set( AT_TOP_MSG );
}
break;
case FORCED_QUIT_CMD:
#ifndef NO_EXCEPTIONS
throw quit_exception();
#else
Quit_flag = true;
// no break here!!!
#endif
case QUIT_CMD:
quit = true;
break;
case SELECT_CMD:
selected = *itemlist.get();
quit = true;
break;
default:
iline.set( INVALID_COMMAND_MSG );
break;
}
iline.show();
} while ( quit == false );
mail->go_group_number( savedgrp );
win->disable();
return selected;
}
void
Groupselection::draw_item( int flag )
{
String tmp;
grpnode *grp = itemlist.get();
win->reset_attr();
if (flag & EMPHASIZED_FL) {
win->inverse();
}
win->clr_line();
win->goto_x (1);
win->print( "%.3d", current_item );
win->goto_x (10);
win->print( "Group no: %d", grp->number );
win->goto_x (28);
if (!(flag & EMPHASIZED_FL)) {
win->textcolor (Settings.subject_color);
}
win->addstr( grp->name.c_str() );
win->reset_attr();
}
jmrsrc-0.7.23/src/jmr.cc 100664 764 764 201164 6623366026 14503 0 ustar jvuokko jvuokko // $Id: jmr.cc,v 1.43 1998/11/14 20:42:39 jvuokko Exp $
/****************************************************************************
* *
* * MODULE : jmr.cc
* *
* **************************************************************************
* *
* * This file is part of jmr offline mail reader.
* * Copyright (C) 1997,1998 Jukka Vuokko <jvuokko@iki.fi>
* *
* * 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.
* *
* * You should have received a copy of the GNU General Public License
* * along with this program; if not, write to the Free Software
* * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* *
* **************************************************************************
* *
* * Main program and signal handling
* *
***************************************************************************/
#include <new.h> // set_new_handler()
#include <stdio.h> // sprintf(), rand(), srand()
#ifdef __WATCOMC__
#include <io.h> // access()
#endif
#include <stdlib.h> // EXIT_FAILURE, EXIT_SUCCESS
#include <signal.h>
#include <time.h> // time()
#ifdef __WATCOMC__
# include <direct.h>
#elif defined (__EMX__)
# include <unistd.h> // F_OK
# include <sys/types.h>
# include <dirent.h>
#else
# include <unistd.h> // F_OK
# include <dirent.h>
#endif
#include "jmr.hh"
#include "menu.hh"
#include "mail.hh"
#include "filelist.hh"
#include "tree.hh"
/*
* Defaults for a resources
*/
const char DEFAULT_PACK_CMD[] = "zip -jkq";
const char DEFAULT_UNPACK_CMD[] = "unzip -oLqq";
const char DEFAULT_QUOTEPREFIX[] = "> ";
const char DEFAULT_WILDCARD[] = "*.[qQ]*";
const char DEFAULT_PTAG[] = "";
const char DEFAULT_TAG[] = "";
const char DEFAULT_FULL_SYSNAME[] = "no";
const bool DEFAULT_CONFIRM_RETURN = true;
const int DEFAULT_REPLYLOGSIZE = 200;
const int DEFAULT_DATABASESIZE = 5000;
const int DEFAULT_QUOTECOLOR = GREEN;
const int DEFAULT_BODYCOLOR = CYAN;
const int DEFAULT_MENU_COLOR = WHITE | BOLD;
const int DEFAULT_MENU_BCOLOR = BLUE | BOLD;
const int DEFAULT_SLINE_COLOR = YELLOW | BOLD;
const int DEFAULT_SLINE_BCOLOR = BLUE | BOLD;
const int DEFAULT_DASHLINE_COLOR = DEFAULT_FCOLOR;
const int DEFAULT_DASHLINE_BCOLOR = DEFAULT_BCOLOR;
const int DEFAULT_BASEGROUP_COLOR = GREEN | BOLD;
const int DEFAULT_SUBJECT_COLOR = GREEN;
const int DEFAULT_AUTHOR_COLOR = CYAN;
const int DEFAULT_ILINE_COLOR = DEFAULT_FCOLOR;
const int DEFAULT_ILINE_ERRCOLOR = RED | BOLD;
const int DEFAULT_HEADER_SUBJECT_COLOR = BOLD;
const int DEFAULT_HEADER_AUTHOR_COLOR = GREEN;
const int DEFAULT_PERSONAL_COLOR = YELLOW | BOLD;
const char DEFAULT_FOLLOWUPSTR[] = "In article <%m> %n wrote:";
const char DEFAULT_REPLYSTR[] = "On a message dated %d you wrote:";
const char DEFAULT_DATESTR[] = "%D.%M.%Y";
const char DEFAULT_WRAPIND = '>';
const char DEFAULT_RECEIVER[] = "";
const char DEFAULT_FOLDER[] = "";
const char DEFAULT_TAGLINE_PROGRAM[] = "";
const char DEFAULT_SIGNATUREFILE[] = "";
const bool DEFAULT_RMOLDPACKET = RM_OLD_NO;
#ifdef F_TO_ALL
const String DEFAULT_FOLLOWUPTO = FUT_WRITER;
#endif
const char DEFAULT_QUOTECHARS[] = ":>%&!/#";
const int DEFAULT_QUOTEDEPTH = 4;
#ifdef __unix__
const char DEFAULT_EDITOR[] = "/usr/bin/jed";
#else
const char DEFAULT_EDITOR[] = "jed.exe";
#endif
//**************************************************************************/
//
// CLASS: Jmrmain
//
//
// DESCRIPTION: Methods for handling mainloop of program.
//
//**************************************************************************/
class Jmrmain {
public:
Jmrmain (int ac, char **av);
~Jmrmain(){};
private:
void init();
void init_paths();
void init_defaults();
void init_settings();
void parse_arguments ();
void init_extractdir();
void get_jmrdir();
void check_jmrdir();
void create_rc_file (char *file);
void read_lines ();
bool parse_rcline (String&, int);
void mainloop();
bool check_argument (int&);
void show_help();
void set_textcolor_value (int& c, String& value);
//void set_backcolor_value (int&, String& );
bool init_all; // if this is true, then write new rc-file
int argc; // number of items in commandline
char **argv; // command parameters
Filelist filelist; // qwk-packet selection level
Groupmenu *groupmenu; // group selection level
String resource_file; // name of resource file
key_tree keywords; // Syntax tree for resources
// Extra variables by Yka
String open_jmr; // Name of packet or database to open.
// If empty assume normal jmr behaviour
};
/************************ GLOBAL VARIABLES ***********************************/
Terminal_screen Screen;
settings_t Settings;
Mail* mail;
Window *Wscript;
#ifdef NO_EXCEPTIONS
bool Quit_flag = false;
#endif
// This array defines rules for keywords in a resource file.
// "*" means that the keyword must have a value.
// "" a value of the keyword can be empty.
// "$" a value must be a number.
//
// Format of an array:
// Keyword, rule1, rule2, ruleN,
// "@NEXT",
// .
// .
// .
// "@NEXT", "@END"
static const char *rc_values[] = {
RC_MAILPATH,"*",
"@NEXT",
RC_REPLYPATH,"*",
"@NEXT",
RC_QUOTEPREFIX,"*",
"@NEXT",
RC_EDITOR, "*",
"@NEXT",
RC_UNPACK, "*",
"@NEXT",
RC_PACK, "*",
"@NEXT",
RC_SYSCHARSET, "latin1", "ibmpc", "7bit",
"@NEXT",
RC_BBSCHARSET, "latin1", "ibmpc", "7bit",
"@NEXT",
RC_WILDCARD, "*",
"@NEXT",
RC_PTAG, "",
"@NEXT",
RC_EXTRACTDIR, "",
"@NEXT",
RC_REPLYLOGSIZE, "$",
"@NEXT",
RC_DATABASESIZE, "$",
"@NEXT",
RC_QUOTECOLOR, COLOR_VALUES,
"@NEXT",
RC_BODYCOLOR, COLOR_VALUES,
"@NEXT",
RC_WRAPMODE, "yes", "no",
"@NEXT",
RC_FOLLOWUPSTR, "",
"@NEXT",
RC_REPLYSTR, "",
"@NEXT",
RC_TAG, "",
"@NEXT",
RC_FULL_SYSNAME, "yes", "no",
"@NEXT",
RC_CONFIRM_RETURN, "yes", "no",
"@NEXT",
RC_DATESTR, "*",
"@NEXT",
RC_MENU_COLOR, COLOR_VALUES,
"@NEXT",
RC_MENU_BCOLOR, COLOR_VALUES,
"@NEXT",
RC_SLINE_COLOR, COLOR_VALUES,
"@NEXT",
RC_SLINE_BCOLOR, COLOR_VALUES,
"@NEXT",
RC_DASHLINE_COLOR, COLOR_VALUES,
"@NEXT",
RC_DASHLINE_BCOLOR, COLOR_VALUES,
"@NEXT",
RC_SUBJECT_COLOR, COLOR_VALUES,
"@NEXT",
RC_AUTHOR_COLOR, COLOR_VALUES,
"@NEXT",
RC_ILINE_COLOR, COLOR_VALUES,
"@NEXT",
RC_ILINE_ERROR_COLOR, COLOR_VALUES,
"@NEXT",
RC_HEADER_SUBJECT_COLOR, COLOR_VALUES,
"@NEXT",
RC_BASEGROUP_COLOR, COLOR_VALUES,
"@NEXT",
RC_HEADER_AUTHOR_COLOR, COLOR_VALUES,
"@NEXT",
RC_PERSONAL_COLOR, COLOR_VALUES,
"@NEXT",
RC_DEFAULT_RECEIVER,"",
"@NEXT",
RC_DEFAULT_FOLDER,"",
"@NEXT",
RC_TAGLINE_PROGRAM,"",
"@NEXT",
RC_SIGNATUREFILE,"",
"@NEXT",
RC_RMOLDPACKET, "yes", "no","ask",
"@NEXT",
// IV
RC_QUOTECHARS, "",
"@NEXT",
RC_QUOTEDEPTH, "$",
"@NEXT",
#ifdef F_TO_ALL
RC_FOLLOWUPTO, "*",
"@NEXT",
#endif
"@END"
};
/************* PROTOTYPES FOR FUNCTIONS IN THIS MODULE ***********************/
void memory_error_handler();
/***************************************************************************
* FUNCTION: main
***************************************************************************
*
* DESCRIPTION: main function of the jmr.
*
* EXTERNAL REFERENCES
* IN :
* OUT: Settings.currentdir, Screen
*
* RETURN : EXIT_SUCCESS
***************************************************************************/
int main(int argc, char** argv)
{
signal (SIGINT, die);
signal (SIGTERM, die);
#ifndef __WATCOMC__
signal (SIGKILL, die);
#endif
#ifdef __unix__
signal (SIGTSTP, suspend);
#endif
set_new_handler (memory_error_handler);
set_misc_lib_error_handler( fatal_error );
Settings.currentdir[0] = '\0';
// initialize random generator
srand( (unsigned int) time( NULL ) );
int h = Screen.get_ymax();
if ( h < 19 || Screen.get_xmax() < 79 ) {
cerr << "Required size of the terminal is 20x80.\n";
cerr << "Your terminal has " << h+1 << " lines and "
<< Screen.get_xmax()+1 << " columns." << endl;
exit( EXIT_FAILURE );
}
Wscript = Screen.newwin( 0, 1, Screen.get_xmax()+1, h - 1,
WIN_SLINE);
Wscript->set_border_colors( DEFAULT_FCOLOR| INVERSE,
DEFAULT_BCOLOR);
Wscript->set_sline_text("* Message Buffer *");
Jmrmain jmrmain (argc, argv);
return EXIT_SUCCESS;
}
/************************************************************************
* FUNCTION : die
************************************************************************
*
* If SIGTERM, SIGKILL or SIGINT signal received, this procedure tries to
* save all necessary data before exit.
*
* EXTERNAL VARIABLE REFERENCES:
* IN : mail
* OUT:
*
* Parameters:
* int sig signal
*
***********************************************************************/
void
die(int sig)
{
//Screen.bottom();
Screen.enable();
Wscript->enable();
Wscript->print( "\n\nTerminate signal (%d) received.\n", sig);
Screen.set_origmode();
if (mail == 0) {
exit (EXIT_FAILURE);
}
if (mail->have_replies() == true) {
Wscript->print( "Emergency exit. Storing new replies...\n");
Screen.refresh();
mail->emergency_exit();
}
exit (EXIT_FAILURE);
}
/***************************************************************************
* FUNCTION: memory_error_handler
***************************************************************************
*
* Function for handling memory errors. The new[]-operator calls this if
* memory is exceed.
*
* EXTERNAL VARIABLE REFERENCES:
* IN :
* OUT:
*
* RETURN:
***************************************************************************/
void
memory_error_handler()
{
handle_error (NO_MEMORY_ERROR, HALT_FL);
}
/***************************************************************************
* FUNCTION: suspend
***************************************************************************
*
* DESCRIPTION: Caughts SIGTSTP-signal (^Z).
*
* EXTERNAL REFERENCES
* IN : Screen
* OUT:
*
***************************************************************************/
#ifdef __unix__
void
suspend(int )
{
sigset_t mask;
sigemptyset( &mask );
sigaddset( &mask, SIGTSTP );
Screen.set_origmode();
sigprocmask( SIG_UNBLOCK, &mask, NULL );
signal (SIGTSTP, SIG_DFL);
kill( getpid(), SIGTSTP );
signal( SIGTSTP, suspend );
Screen.set_rawmode();
Screen.refresh( REDRAW_ALL );
}
#endif
//**************************************************************************/
// CLASS: Jmrmain
// MEMBER FUNCTION: Jmrmain
//
// DESCRIPTION: Main funtion of class.
//
//
// EXTERNAL REFERENCES
// IN : Settings, Screen
// OUT: Quit_flag
//
// RETURNS:
//**************************************************************************/
Jmrmain::Jmrmain(int ac, char** av)
{
argc = ac;
argv = av;
init_all = false;
open_jmr = "";
#ifdef NO_EXCEPTIONS
Quit_flag = false;
#endif
parse_arguments();
Screen.enable();
Wscript->enable();
init();
if (Screen.is_color_term()) {
Wscript->set_border_colors( Settings.sline_color,
Settings.slineback_color );
}
// change to working directory
if (change_dir (Settings.workdir.get())) {
char buf[1024];
sprintf (buf, "Cannot change to direcetory : %s",
Settings.workdir.get());
handle_error (buf, HALT_FL);
}
mainloop();
Wscript->enable();
// back to original directory
change_dir (Settings.currentdir);
}
//**************************************************************************/
// CLASS: Jmrmain
// MEMBER FUNCTION: mainloop
//
// DESCRIPTION: Main loop of program
//
//
// EXTERNAL REFERENCES
// IN : Settings, mail, Quit_flag
// OUT: mail
//
//**************************************************************************/
void
Jmrmain::mainloop()
{
bool open_new;
String tmppath;
loop:
open_new = false;
mail = 0;
if (ACCESS (DEADJMR_FILENAME, F_OK) == 0) {
Wscript->enable();
Wscript->print( "\nPrevious session was killed and replies was"
"wrote to recovery file.\n" );
Wscript->print( "Continue killed session with these "
"replies? (y/n)? y" );
Screen.refresh();
if (get_yesno('y') == 'n') {
remove (DEADJMR_FILENAME);
} else if (0 == (mail = Mail::open_dead())) {
handle_error("Cannot open recovery file.",
REPORT_FL);
}
}
if (mail == 0) {
if (open_jmr.is_empty()) {
Wscript->disable();
mail = filelist.open_file();
if (mail == 0) {
return;
}
} else {
Wscript->disable();
mail = filelist.open_file(open_jmr);
// skip interactive mode
if (!mail) {
return;
}
}
}
Wscript->disable();
#ifdef NO_EXCEPTIONS
groupmenu = new Groupmenu();
#else
try {
groupmenu = new Groupmenu();
}
catch( quit_exception ) {
Wscript->enable();
Wscript->reset_attr();
Wscript->textcolor( BOLD );
Wscript->print("\n'Quit immediately' command received!");
Wscript->reset_attr();
Screen.refresh();
delete mail;
Wscript->print( "\nCleaning working directory..." );
Screen.refresh();
clear_workdir();
Wscript->print("OK");
return;
}
#endif
Wscript->enable();
switch (Settings.rmoldpacket)
{
case RM_OLD_YES :
if ( ! mail->get_packet_name().is_empty() )
{
Screen.refresh();
tmppath = Settings.qwkpath;
correct_path( tmppath );
tmppath += mail->get_packet_name();
Wscript->print( "\nRemoving old packet..." );
Wscript->print( mail->get_packet_name() );
rm_files( tmppath );
Wscript->print(" OK");
}
break;
case RM_OLD_NO : break;
case RM_OLD_ASK :
if ( ! mail->get_packet_name().is_empty() )
{
tmppath = Settings.qwkpath;
correct_path( tmppath );
Wscript->print( "\nDo you want to delete packet ");
Wscript->print( mail->get_packet_name() );
Wscript->print( " (y/n)? n" );
Screen.refresh();
if (get_yesno('n') == 'y') {
Wscript->print( "\nRemoving old packet..." );
tmppath += mail->get_packet_name();
rm_files( tmppath );
Wscript->print("OK");
}
}
break;
}
delete groupmenu;
delete mail;
Wscript->enable();
Wscript->print( "\nCleaning working directory..." );
Screen.refresh();
clear_workdir();
Wscript->print("OK");
#ifdef NO_EXCEPTIONS
if (Quit_flag == true || !open_jmr.is_empty()) { // --open-jmr means immediate quit after reading.
return;
}
#endif
goto loop;
}
//**************************************************************************/
// CLASS: Jmrmain
// MEMBER FUNCTION: init
//
// DESCRIPTION: Reads contents of a resource file and initializes Settings.
//
//
// EXTERNAL REFERENCES
// IN : rc_values[]
// OUT: Settings
//
// RETURNS:
//**************************************************************************/
void Jmrmain::init()
{
Wscript->enable();
Wscript->print( "\n*** jmr %s (alpha) ***", VERSION );
Wscript->print( " (compiled@ %s )\n", __DATE__ );
Screen.refresh();
#ifdef __unix__
// check for color capable terminal
char *cterm;
if ( (cterm = getenv( "COLORTERM" )) ) {
Screen.force_colormode();
Wscript->print( "\n\nColor capable terminal '%s' detected!\n",
cterm );
Screen.refresh();
}
#endif /* __unix__ */
keywords.init( rc_values );
init_paths();
read_lines();
init_defaults();
init_settings();
init_extractdir();
if( Settings.full_sysname == "no" ) {
Settings.system = get_system_info();
} else {
Settings.system = get_system_info( ALL_FL );
}
Settings.version_str = "jmr " VERSION " ";
Settings.version_str += Settings.system;
Settings.tag = "* " + Settings.version_str + " * ";
// set up wrap indicator
switch ( Settings.syscharset ) {
case ISO_LATIN_CHARSET:
Settings.wrap_indicator = 0xbb;
break;
case IBM_PC_CHARSET:
Settings.wrap_indicator = 175;
break;
default:
Settings.wrap_indicator = DEFAULT_WRAPIND;
};
Wscript->enable();
Wscript->print( "Resource file is OK" );
Screen.refresh();
DEBUG("---------- SETTINGS ---------");
DEBUG("zipcmd ="<<Settings.zipcmd);
DEBUG("unzipcmd ="<<Settings.unzipcmd);
DEBUG("qwkpath ="<<Settings.qwkpath);
DEBUG("replypath ="<<Settings.replypath);
DEBUG("quoteprefix ="<<Settings.quoteprefix);
DEBUG("searchpattern ="<<Settings.searchpattern);
DEBUG("ptagline ="<<Settings.ptagline);
DEBUG("tagline ="<<Settings.tagline);
DEBUG("replylogsize ="<<Settings.replylogsize);
DEBUG("datebasesize ="<<Settings.databasesize);
DEBUG("extractdir ="<<Settings.extractdir);
DEBUG("quotecolor ="<<Settings.quote_color);
DEBUG("textcolor ="<<Settings.article_color);
DEBUG("followup ="<<Settings.followup);
DEBUG("reply ="<<Settings.reply);
DEBUG("syscharset ="<<Settings.syscharset);
DEBUG("bbscharset ="<<Settings.bbscharset);
DEBUG("menucolor =" <<Settings.menu_color);
DEBUG("menuback = "<<Settings.menuback_color);
DEBUG("slinecolor = "<<Settings.sline_color);
DEBUG("slineback = "<<Settings.slineback_color);
DEBUG("dashcolor = "<<Settings.dash_color);
DEBUG("dashback = "<<Settings.dashback_color);
DEBUG("subjectcolor = "<<Settings.subject_color);
DEBUG("ilinecolor = "<<Settings.iline_color);
DEBUG("default_receiver = "<<Settings.default_receiver);
DEBUG("signaturefile = " << Settings.signaturefile );
DEBUG("taglinre = " << Settings.tagliner );
DEBUG("rmoldpacket = " << (Settings.rmoldpacket ? Settings.rmoldpacket == RM_OLD_YES ? "yes" : "ask" : "no") );
#ifdef F_TO_ALL
DEBUG("followupto = " << Settings.followupto );
#endif
DEBUG("quotechars = " << Settings.quotechars );
DEBUG("quotedepth = " << Settings.quotedepth );
DEBUG("-------- END OF SETTINGS ----");
}
//**************************************************************************/
// CLASS: jmrmain
// MEMBER FUNCTION: init_defaults
//**************************************************************************/
//
// This function initializes contents of Settings structure with a
// default values.
//
// NOTE: All paths must be initialised (init_paths()) before using this.
//
// EXTERNAL VARIABLE REFERENCES
// IN : Settings.currentdir
// OUT: Settings
//
// PARAMETERS:
//
// RETURN:
//**************************************************************************/
void Jmrmain::init_defaults()
{
Settings.zipcmd = DEFAULT_PACK_CMD;
Settings.unzipcmd = DEFAULT_UNPACK_CMD;
Settings.qwkpath = Settings.currentdir;
correct_path( Settings.qwkpath );
Settings.replypath = Settings.currentdir;
correct_path( Settings.replypath );
Settings.quoteprefix = DEFAULT_QUOTEPREFIX;
Settings.searchpattern = DEFAULT_WILDCARD;
Settings.ptagline = DEFAULT_PTAG;
Settings.tagline = DEFAULT_TAG;
Settings.full_sysname = DEFAULT_FULL_SYSNAME;
Settings.confirm_return = DEFAULT_CONFIRM_RETURN;
Settings.replylogsize = DEFAULT_REPLYLOGSIZE;
Settings.databasesize = DEFAULT_DATABASESIZE;
Settings.extractdir = ""; // this is initialized later
Settings.max_subject_len = MAX_SUBJECT_LEN;
Settings.max_bbs_subject_len = MAX_SUBJECT_LEN;
Settings.is_pcboard = false;
Settings.quote_color = DEFAULT_QUOTECOLOR;
Settings.article_color = DEFAULT_BODYCOLOR;
Settings.followup = DEFAULT_FOLLOWUPSTR;
Settings.reply = DEFAULT_REPLYSTR;
Settings.date_fmt = DEFAULT_DATESTR;
Settings.editor = DEFAULT_EDITOR;
Settings.menu_color = DEFAULT_MENU_COLOR;
Settings.menuback_color = DEFAULT_MENU_BCOLOR;
Settings.sline_color = DEFAULT_SLINE_COLOR;
Settings.slineback_color = DEFAULT_SLINE_BCOLOR;
Settings.dash_color = DEFAULT_DASHLINE_COLOR;
Settings.dashback_color = DEFAULT_DASHLINE_BCOLOR;
Settings.subject_color = DEFAULT_SUBJECT_COLOR;
Settings.author_color = DEFAULT_AUTHOR_COLOR;
Settings.iline_color = DEFAULT_ILINE_COLOR;
Settings.iline_err_color = DEFAULT_ILINE_ERRCOLOR;
Settings.header_subject_color = DEFAULT_HEADER_SUBJECT_COLOR;
Settings.basegroup_color = DEFAULT_BASEGROUP_COLOR;
Settings.personal_color = DEFAULT_PERSONAL_COLOR;
Settings.header_author_color = DEFAULT_HEADER_AUTHOR_COLOR;
Settings.is_wrapmode = false;
Settings.default_receiver = DEFAULT_RECEIVER;
Settings.default_folder = DEFAULT_FOLDER;
Settings.tagliner = DEFAULT_TAGLINE_PROGRAM;
Settings.signaturefile = DEFAULT_SIGNATUREFILE;
Settings.rmoldpacket = DEFAULT_RMOLDPACKET;
#ifdef F_TO_ALL
Settings.followupto = DEFAULT_FOLLOWUPTO;
#endif
Settings.quotechars = DEFAULT_QUOTECHARS;
Settings.quotedepth = DEFAULT_QUOTEDEPTH;
}
//**************************************************************************/
// CLASS: Jmrmain
// MEMBER FUNCTION: init_settings
//
// DESCRIPTION: Initializes settings with a values readed from
// a resource file.
//
//
// EXTERNAL REFERENCES
// IN :
// OUT: Settings
//
// RETURNS:
//**************************************************************************/
void Jmrmain::init_settings()
{
String value;
String tmp;
int errors = 0;
// first init required resources
value = keywords.get_value( RC_SYSCHARSET );
if (value.is_empty()) {
++errors;
tmp = "Resource \'";
tmp += RC_SYSCHARSET;
tmp += "\' not found";
handle_error(tmp.get(), REPORT_FL );
} else {
if (value == "ibmpc") {
Settings.syscharset = IBM_PC_CHARSET;
} else if (value == "latin1") {
Settings.syscharset = ISO_LATIN_CHARSET;
} else {
Settings.syscharset = ASCII_7BIT_CHARSET;
}
}
value = keywords.get_value( RC_BBSCHARSET );
if (value.is_empty()) {
++errors;
tmp = "Resource \'";
tmp += RC_BBSCHARSET;
tmp += "\' not found";
handle_error( tmp.get(), REPORT_FL );
} else {
if (value == "ibmpc") {
Settings.bbscharset = IBM_PC_CHARSET;
} else if (value == "latin1") {
Settings.bbscharset = ISO_LATIN_CHARSET;
} else {
Settings.bbscharset = ASCII_7BIT_CHARSET;
}
}
#ifdef F_TO_ALL
value = keywords.get_value( RC_FOLLOWUPTO );
if ( value.is_empty() == false ) {
Settings.followupto = value;
} else {
++errors;
tmp = "Resource \'";
tmp += RC_FOLLOWUPTO;
tmp += "\' not found";
handle_error( tmp.get(), REPORT_FL );
}
#endif
if (errors) {
handle_error("All required resources are not defined!",
HALT_FL);
}
// if resource is defined in file, then override default
value = keywords.get_value( RC_EDITOR );
if (value.is_empty() == false) {
Settings.editor = value;
}
value = keywords.get_value( RC_PACK );
if (value.is_empty() == false) {
Settings.zipcmd = value;
}
value = keywords.get_value( RC_UNPACK );
if (value.is_empty() == false) {
Settings.unzipcmd = value;
}
value = keywords.get_value( RC_MAILPATH );
if (value.is_empty() == false) {
Settings.qwkpath = value;
correct_path( Settings.qwkpath );
}
value = keywords.get_value( RC_REPLYPATH );
if (value.is_empty() == false) {
Settings.replypath = value;
correct_path( Settings.replypath );
}
value = keywords.get_value( RC_QUOTEPREFIX );
if (value.is_empty() == false) {
value.replace_ch( '_', ' ' );
Settings.quoteprefix = value;
}
value = keywords.get_value( RC_WILDCARD );
if (value.is_empty() == false) {
Settings.searchpattern = value;
}
value = keywords.get_value( RC_WRAPMODE );
if ( value.is_empty() == false ) {
Settings.is_wrapmode = value == "yes" ? true : false;
}
value = keywords.get_value( RC_PTAG );
if (value.is_empty() == false) {
Settings.ptagline = value;
}
value = keywords.get_value( RC_TAG );
if (value.is_empty() == false) {
Settings.tagline = value;
}
value = keywords.get_value( RC_FULL_SYSNAME );
if (value.is_empty() == false) {
Settings.full_sysname = value;
}
value = keywords.get_value( RC_CONFIRM_RETURN );
if (value.is_empty() == false) {
// Settings.confirm_return = value == "yes" ? true : false;
Settings.confirm_return = (value == "yes");
}
value = keywords.get_value( RC_REPLYLOGSIZE );
if (value.is_empty() == false) {
Settings.replylogsize = atoi( value.get() );
}
value = keywords.get_value( RC_DATABASESIZE );
if (value.is_empty() == false) {
Settings.databasesize = atoi( value.get() );
}
value = keywords.get_value( RC_EXTRACTDIR );
if (value.is_empty() == false) {
correct_path( value );
Settings.extractdir = value;
}
value = keywords.get_value( RC_QUOTECOLOR );
set_textcolor_value (Settings.quote_color, value);
value = keywords.get_value( RC_BODYCOLOR );
set_textcolor_value (Settings.article_color, value);
value = keywords.get_value( RC_FOLLOWUPSTR );
if (value.is_empty() == false) {
Settings.followup = value;
}
value = keywords.get_value( RC_REPLYSTR );
if (value.is_empty() == false) {
Settings.reply = value;
}
value = keywords.get_value( RC_DATESTR );
if (value.is_empty() == false) {
Settings.date_fmt = value;
}
value = keywords.get_value( RC_MENU_COLOR );
set_textcolor_value (Settings.menu_color, value);
value = keywords.get_value( RC_MENU_BCOLOR );
set_textcolor_value( Settings.menuback_color, value );
value = keywords.get_value( RC_SLINE_COLOR );
set_textcolor_value (Settings.sline_color, value);
value = keywords.get_value( RC_SLINE_BCOLOR );
set_textcolor_value( Settings.slineback_color, value );
value = keywords.get_value( RC_DASHLINE_COLOR );
set_textcolor_value (Settings.dash_color, value);
value = keywords.get_value( RC_DASHLINE_BCOLOR );
set_textcolor_value( Settings.dashback_color, value );
value = keywords.get_value( RC_SUBJECT_COLOR );
set_textcolor_value (Settings.subject_color, value);
value = keywords.get_value( RC_AUTHOR_COLOR );
set_textcolor_value (Settings.author_color, value);
value = keywords.get_value( RC_ILINE_COLOR );
set_textcolor_value (Settings.iline_color, value);
value = keywords.get_value( RC_ILINE_ERROR_COLOR );
set_textcolor_value (Settings.iline_err_color, value);
value = keywords.get_value( RC_BASEGROUP_COLOR );
set_textcolor_value (Settings.basegroup_color, value);
value = keywords.get_value( RC_HEADER_SUBJECT_COLOR );
set_textcolor_value (Settings.header_subject_color, value);
value = keywords.get_value( RC_HEADER_AUTHOR_COLOR );
set_textcolor_value (Settings.header_author_color, value);
value = keywords.get_value( RC_PERSONAL_COLOR );
set_textcolor_value (Settings.personal_color, value);
value = keywords.get_value( RC_DEFAULT_RECEIVER );
if (value.is_empty() == false) {
Settings.default_receiver = value;
}
value = keywords.get_value( RC_DEFAULT_FOLDER );
if (value.is_empty() == false) {
Settings.default_folder = value;
}
value = keywords.get_value( RC_SIGNATUREFILE );
if ( value.is_empty() == false ) {
Settings.signaturefile = value;
expand_path( Settings.signaturefile );
}
value = keywords.get_value( RC_TAGLINE_PROGRAM );
if ( value.is_empty() == false ) {
Settings.tagliner = value;
expand_path( Settings.tagliner );
}
value = keywords.get_value( RC_RMOLDPACKET );
if ( value.is_empty() == false ) {
if (value == "yes")
Settings.rmoldpacket = RM_OLD_YES;
else if (value == "no")
Settings.rmoldpacket = RM_OLD_NO;
else if (value == "ask")
Settings.rmoldpacket = RM_OLD_ASK;
else {
tmp = "Resource \'";
tmp += RC_RMOLDPACKET;
tmp += "\' is terribly wrong";
handle_error( tmp.get(), HALT_FL );
}
}
value = keywords.get_value( RC_QUOTECHARS );
if ( ! value.is_empty() )
Settings.quotechars = value;
value = keywords.get_value( RC_QUOTEDEPTH );
if ( ! value.is_empty() )
Settings.quotedepth = atoi(value.get());
}
void
Jmrmain::set_textcolor_value (int& c, String& value)
{
if (value.is_empty() == false) {
c = atoi( value.get() );
if( c > 7 ) {
c -= 7;
c |= BOLD;
}
}
}
//**************************************************************************/
// CLASS: Jmrmain
// MEMBER FUNCTION: read_lines
//
// This function reads contents of a resource file to a tree structure.
// If syntax of the resource file is not correct, then program is halted.
//
// EXTERNAL REFERENCES
// IN : Settigs.jmrdir
// OUT:
//
// RETURNS:
//**************************************************************************/
void
Jmrmain::read_lines ()
{
String tmp;
char line[MAX_LINE_LEN+1];
int linenum = 1;
bool rc = true;
tmp = Settings.jmrdir + resource_file;
ifstream rcfile( tmp.get(), ios::in );
if (rcfile.fail()) {
String err_str = "Cannot open resource file : " + tmp;
handle_error (err_str.get(), HALT_FL);
}
Wscript->print( "\nParsing resource file \'%s\'....", tmp.c_str() );
Screen.refresh();
do {
rcfile.getline (line, MAX_LINE_LEN);
tmp = line;
// remove preceeding spaces
tmp.cut_first();
// if line is not empty and it is not commented,
// then check syntax of line and get value
// of keyword.
if (tmp.is_empty() == false && tmp[0] != '#') {
// remove trailing spaces
tmp.cut_tail();
// parse the line
if (parse_rcline( tmp, linenum ) == false) {
rc = false;
}
}
++linenum;
} while (!rcfile.eof());
rcfile.close();
if (rc == false) {
handle_error("Fatal errors in resource file.",
HALT_FL);
}
}
//**************************************************************************/
// CLASS: Jmrmain
// MEMBER FUNCTION: parse_rcline
//
// Checks that syntax of a line is right, and initializes keyword that
// is defined in a line with a value given to it.
// If syntax error occures, then false is returned.
//
// EXTERNAL REFERENCES
// IN :
// OUT:
//
// RETURNS: true if success.
//**************************************************************************/
bool
Jmrmain::parse_rcline (String& line, int num)
{
int n=0;
String tmp;
// check that assigment operator is in it's place
n = line.findch ('=');
if (n == NOTHING) {
String error_str;
error_str = "Assigment operator is missing in line ";
error_str += num;
handle_error( error_str.get(), REPORT_FL);
return false;
}
// Then get word before an assigment operator and check
// if it matches with some of keywords in a trie
tmp.copy( line, 0, n);
tmp.cut_tail();
if (false == keywords.goto_keyword( tmp )) {
String error_str = "At line ";
error_str += num;
error_str += ": Illegal keyword : \'" + tmp + "\'";
handle_error( error_str.get(), REPORT_FL);
return false;
}
// Check that given value is legal, and store it
// in a trie.
tmp.copy( line, n + 1 );
tmp.cut_first();
tmp.cut_tail();
if (false == keywords.set_value( tmp )) {
String error_str = "Invalid value at line ";
error_str += num;
error_str += ": \'" + tmp + "\'";
handle_error( error_str.get(), REPORT_FL );
return false;
}
return true;
}
//**************************************************************************/
// CLASS: Jmrmain
// MEMBER FUNCTION: init_paths
//
// DESCRIPTION: Initializes directories for use.
//
//
// EXTERNAL REFERENCES
// IN : Settings
// OUT: Settings.workdir
//
// RETURNS:
//**************************************************************************/
void
Jmrmain::init_paths()
{
String cmd;
// initialize jmr's directory
get_jmrdir();
// Create directories for working files and for data files if
// them not exist.
Settings.datadir = Settings.jmrdir + DATA_DIRECTORY;
Settings.workdir = Settings.jmrdir + WORK_DIRECTORY;
if (ACCESS (Settings.datadir.get(), F_OK)) {
// FIXME: use mknod() or similar function instead
cmd = CREATE_DIRECTORY_COMMAND;
cmd += Settings.datadir;
jmrsystem (cmd.get());
}
if (ACCESS (Settings.workdir.get(), F_OK)) {
// FIXME: use mknod() or similar function instead
cmd = CREATE_DIRECTORY_COMMAND;
cmd += Settings.workdir;
jmrsystem (cmd.get());
}
// append path separator ('\' or '/')
correct_path (Settings.datadir);
correct_path (Settings.workdir);
// store current directory
getcwd (Settings.currentdir, MAXPATH);
}
//**************************************************************************/
// CLASS: Jmrmain
// MEMBER FUNCTION: get_jmrdir
//
// DESCRIPTION: Initializes jmr's directory.
//
//
// EXTERNAL REFERENCES
// IN :
// OUT: Settings.jmrdir
//
// RETURNS:
//**************************************************************************/
void
Jmrmain::get_jmrdir()
{
char *dir;
// get jmr's directory (where logs, resources etc. are)
dir = getenv (DIR_ENVIRONMENT);
if (dir == NULL) {
// if environment variable is not set, then read variable
// HOME, and use directory HOME/.jmr
dir = getenv ("HOME");
if (dir == NULL) {
handle_error ("Environment variable JMRDIR or HOME is not set!", HALT_FL);
}
Settings.jmrdir = dir;
correct_path (Settings.jmrdir);
Settings.jmrdir += DEFAULT_DIRECTORY;
} else {
Settings.jmrdir = dir;
}
correct_path (Settings.jmrdir);
check_jmrdir();
}
//**************************************************************************/
// CLASS: Jmrmain
// MEMBER FUNCTION: check_jmrdir
//
// DESCRIPTION: Checks if directory exist. If not, then creates it.
// If directory does not contain file jmrrc, then create file
// and tell user that it's necessary to modify it.
//
// EXTERNAL REFERENCES
// IN : Settings.jmrdir
// OUT:
//
// RETURNS:
//**************************************************************************/
void
Jmrmain::check_jmrdir()
{
String cmd;
String rcfile;
int c = 0;
// check if directory exists. If not, then create it
if (ACCESS (Settings.jmrdir.get(), F_OK)) {
Wscript->enable();
Wscript->print( "\nDirectory \'%s\' does not exist.",
Settings.jmrdir.c_str() );
Wscript->print( "\n\nShall I create it ? (y/n) y" );
Screen.refresh();
c = get_yesno('y');
if (c == 'n') {
Wscript->print( "\nExiting...." );
exit(EXIT_SUCCESS);
}
cmd = CREATE_DIRECTORY_COMMAND;
cmd += Settings.jmrdir;
if (jmrsystem (cmd.get())) {
handle_error("Directory creating failed.",HALT_FL);
}
}
rcfile = Settings.jmrdir;
rcfile += RESOURCE_FILE_NAME;
// check if resource-file exists. If not, create it.
if (ACCESS (rcfile.get(), F_OK) || init_all == true) {
create_rc_file(rcfile.get());
}
}
//**************************************************************************/
// CLASS: Jmrmain
// MEMBER FUNCTION: init_extractdir
//
// DESCRIPTION: Gets directory for storing articles.
//
// EXTERNAL REFERENCES
// IN :
// OUT: Settings.extractdir
//
// RETURN :
//**************************************************************************/
void
Jmrmain::init_extractdir()
{
String cmd;
if ( Settings.extractdir == "" ) {
char *path = getenv ("HOME");
if (path == NULL) {
handle_error ("Either key 'extractdir' must defined in resource file\nor environment variable HOME must set.", HALT_FL);
}
Settings.extractdir = path;
correct_path(Settings.extractdir);
Settings.extractdir += EXTRACT_DIRECTORY;
}
if (ACCESS (Settings.extractdir.get(), F_OK)) {
// FIXME: Should use mknod() or similar function...
cmd = CREATE_DIRECTORY_COMMAND;
cmd += Settings.extractdir;
DEBUG( "Exractdir = '" << Settings.extractdir.c_str() << "'" );
jmrsystem (cmd.get());
}
correct_path( Settings.extractdir );
}
//**************************************************************************/
// CLASS: Jmrmain
// MEMBER FUNCTION: create_rc_file
//
// DESCRIPTION: Writes new resource file
//
//
// EXTERNAL REFERENCES
// IN :
// OUT:
//
// RETURNS:
//**************************************************************************/
void
Jmrmain::create_rc_file (char *file)
{
ofstream fout (file);
if (fout.fail()) {
handle_error ("Cannot create default rc-file.",HALT_FL);
}
fout <<"\n########################################################################";
fout <<"\n#";
fout <<"\n#";
fout <<"\n# Configuration file for jmr QWK offline mail reader. This file sets";
fout <<"\n# up the runtime parameters and options.";
fout <<"\n#";
fout <<"\n# Parameters defined here are global for each bbs, but you can override";
fout <<"\n# some of these using local, bbs related configuration files.";
fout <<"\n# Sample for the local configuration file should be found at";
fout <<"\n# /usr/local/lib/jmr/sample.rc, if jmr is properly installed.";
fout <<"\n#";
fout <<"\n#";
fout <<"\n# Lines that start with a '#' are ignored.";
fout <<"\n#";
fout <<"\n# NOTE: Name of this file should be 'jmrrc' unless you specify name using";
fout <<"\n# parameter -f at commandline.";
fout <<"\n# This file must allways be in the directory, that ";
fout <<"\n# environment variable JMRDIR points or in directory";
fout <<"\n# $HOME/.jmr/";
fout <<"\n#";
fout <<"\n# NOTE: All paths must be absolute, and if your system uses drive-letters";
fout <<"\n# (like MS-DOG), path must start with drive-letter.";
fout <<"\n# e.g. in Unix systems replypath might be something like:";
fout <<"\n# /spool/send/";
fout <<"\n#";
fout <<"\n# And in DOS-familian systems it might be:";
fout <<"\n# c:\\work\\uploads\\";
fout <<"\n#";
fout <<"\n#";
fout <<"\n# Resources are divited to these sections:";
fout <<"\n# Required settings -- Without these jmr wont run";
fout <<"\n# Basic settings -- Defines paths, editor, zip command etc.";
fout <<"\n# Replying & quoting -- Sets up quoteprefix, tagline etc.";
fout <<"\n# Database -- Define size of database and replylog";
fout <<"\n# Colors -- Set up colors ";
fout <<"\n#";
fout <<"\n########################################################################";
fout <<"\n";
fout <<"\n";
fout <<"\n";
fout <<"\n";
fout <<"\n########################################################################";
fout <<"\n#";
fout <<"\n# REQUIRED SETTINGS";
fout <<"\n#";
fout <<"\n# You must set up at least these two resources in order to run jmr.";
fout <<"\n#";
fout <<"\n########################################################################";
fout <<"\n";
fout <<"\n";
fout <<"\n # Define your systems characterset here. Possible values are:";
fout <<"\n # latin1 for ISO-LATIN1 character set. (e.g. Linux uses it)";
fout <<"\n # ibmpc for PC 437 codepage. (e.g. OS/2, MS-DOG)";
fout <<"\n # 7bit for 7bit ascii character set. Some old machines (?)";
fout <<"\n #";
fout <<"\n # E.g. systemcharset = latin1";
fout <<"\nsystemcharset = ";
fout <<"\n";
fout <<"\n";
fout <<"\n #";
fout <<"\n # Character set that bbs is using.";
fout <<"\n #";
fout <<"\nbbscharset = ";
fout <<"\n";
fout <<"\n";
fout <<"\n########################################################################";
fout <<"\n#";
fout <<"\n# BASIC SETTINGS";
fout <<"\n#";
fout <<"\n########################################################################";
fout <<"\n";
fout <<"\n";
fout <<"\n # Shell command to unzip qwk-packet. Normally you don't need to change ";
fout <<"\n # this, unless you are using MS-DOG and you want to use pkzip/pkunzip.";
fout <<"\n # NOTE: Use unzip-program with option, that overwrites existing files!";
fout <<"\n # Default command is 'unzip -oLqq'";
fout <<"\n#unzipcmd = pkunzip";
fout <<"\n";
fout <<"\n";
fout <<"\n # Shell command to pack replies. This should also work in your ";
fout <<"\n # system without changes.";
fout <<"\n # Default command is 'zip -jkq'";
fout <<"\n#zipcmd = pkzip";
fout <<"\n";
fout <<"\n";
fout <<"\n # Set up path to directory, that holds your QWK mail packets.";
fout <<"\n # Default value is './'";
fout <<"\n#qwkpath = c:\\work\\download";
fout <<"\n#qwkpath = ~/work/download";
fout <<"\n";
fout <<"\n";
fout <<"\n # Set up path to directory, where replies goes. Usually it should be";
fout <<"\n # same directory, that you use for uploads in your terminal software.";
fout <<"\n # Default is './'";
fout <<"\n#replypath = ~/work/upload";
fout <<"\n";
fout <<"\n";
fout <<"\n # This is the wildcard file specification to use when searching for";
fout <<"\n # packets to read. Accepts *, ? and list [].";
fout <<"\n # Default pattern is '*.[qQ]*'";
fout <<"\n # NOTE: Pattern is case-sensitive even in Os/2!";
fout <<"\n#searchpattern = *.qwk";
fout <<"\n";
fout <<"\n";
fout <<"\n # Define the editor use for replies.";
fout <<"\n # Default is '/usr/bin/jed' in unix and 'jed.exe' in DOS-style";
fout <<"\n # operating systems.";
fout <<"\n#editor = ";
fout <<"\n";
fout <<"\n";
fout <<"\n # Set up path to directory for saved messages.";
fout <<"\n #";
fout <<"\n # Normally program reads environment variable HOME, and extracts saved";
fout <<"\n # messages to subdirectory 'Messages' of directory that HOME points";
fout <<"\n # ($HOME/Messages/).";
fout <<"\n # ";
fout <<"\n # If You want to override HOME-environment variable, uncomment next line";
fout <<"\n#extractdir = ~/work/articles";
fout <<"\n";
fout <<"\n";
fout <<"\n # Default name for file to save articles. ";
fout <<"\n # This name can be simple name like `saved_articles', when jmr creates";
fout <<"\n # it under directory defined by key `extractdir' or $HOME/Messages.";
fout <<"\n # Name of folder can also be relative like `../saved_articles',";
fout <<"\n # absolute like `/home/my_name/saved_articles' or `~/texts/saved_artcles";
fout <<"\n # ";
fout <<"\n#default_folder = saved_articles";
fout <<"\n";
fout <<"\n";
fout <<"\n # This format string defines how date should be displayed. Default format";
fout <<"\n # for date is dd.mm.yyyy (%D.%M.%Y as a format string).";
fout <<"\n # Format specifiers are: %M for month, %D for day and %Y for year.";
fout <<"\n#dateformat = %M/%D/%Y";
fout <<"\n";
fout <<"\n";
fout <<"\n # With this option set, lines are word wrapped at the end of screen,";
fout <<"\n # avoiding truncated messages.";
fout <<"\n # Note that wrap mode can also be changed in jmr using key 'ESC-w'.";
fout <<"\n # Default value is 'no'";
fout <<"\n#wrapmode = yes";
fout <<"\n";
fout <<"\n";
fout <<"\n # If this is set to 'yes', then jmr asks confirm when returning to";
fout <<"\n # packet selection level from group selection level.";
fout <<"\n # Default value is 'yes'";
fout <<"\n#confirm_return = no";
fout <<"\n";
fout <<"\n";
fout <<"\n########################################################################";
fout <<"\n#";
fout <<"\n# SETTINGS FOR REPLYING & QUOTING";
fout <<"\n#";
fout <<"\n########################################################################";
fout <<"\n";
fout <<"\n";
fout <<"\n # Default value for name of receiver when composing new article.";
fout <<"\n#default_receiver = All";
fout <<"\n";
fout <<"\n";
fout <<"\n # This describes how jmr will quote the lines of the source message when";
fout <<"\n # creating a new reply file. '_' indicates space.";
fout <<"\n # Default prefix is '> '";
fout <<"\n#quoteprefix = :_";
fout <<"\n";
fout <<"\n";
fout <<"\n # This specifies all characters that might indicate that the line is";
fout <<"\n # quoted from someone else. Default is \":>%&!/#\".";
fout <<"\n#quote_chars = >";
fout <<"\n";
fout <<"\n";
fout <<"\n # This tells jmr how deep from the line it should search for";
fout <<"\n # quote prefix characters. Default is four";
fout <<"\n#quote_depth = 4";
fout <<"\n";
fout <<"\n";
fout <<"\n # This sets up follow-up string. The following format specifiers are";
fout <<"\n # recognized:";
fout <<"\n # %d = date of quoted article";
fout <<"\n # %t = time";
fout <<"\n # %n = name of author";
fout <<"\n # %s = subject of article";
fout <<"\n # %m = reference message id";
fout <<"\n # %M = number of month";
fout <<"\n # %D = number of day";
fout <<"\n # %Y = year";
fout <<"\n # %% = percent";
fout <<"\n #";
fout <<"\n # E.g. next string 'In article dated %d %n wrote:'";
fout <<"\n # Expands to something like: ";
fout <<"\n # 'In article dated 03-31-1997 John Smith wrote:'";
fout <<"\n #";
fout <<"\n # Default string is 'In article <%m> %n wrote:'";
fout <<"\n#followupstring =";
fout <<"\n";
fout <<"\n";
fout <<"\n # Set up reply string. Syntax is same as in follow-up string, but";
fout <<"\n # this is used only in a private replies.";
fout <<"\n #";
fout <<"\n # Default string is 'On a message dated %d you wrote:'";
fout <<"\n#replystring = ";
fout <<"\n";
fout <<"\n";
fout <<"\n # When this is set to 'no' then only name of the operating system";
fout <<"\n # is displayed at the tagline. Default is 'no'.";
fout <<"\n # With value 'yes', tagline looks like:";
fout <<"\n # * jmr r.x.y.z [machine/system] *";
fout <<"\n # And with 'no', tag is:";
fout <<"\n # * jmr r.x.y.z [system] *";
fout <<"\n #";
fout <<"\n # Note that this settings works only in Unix-like systems.";
fout <<"\n#tag_full_sysname = yes";
fout <<"\n";
fout <<"\n";
fout <<"\n # Write your permanent tagline here.";
fout <<"\n # This line will come right after jmr's own tag.";
fout <<"\n # Usually you might want leave this empty...";
fout <<"\n # Tagline should not be longer than this!!!";
fout <<"\n # (---------------------------------------------------)";
fout <<"\n#ptagline = ";
fout <<"\n";
fout <<"\n";
fout <<"\n # This sets the default tagline for articles.";
fout <<"\n # This string is placed at bottom of each reply, and it will start with";
fout <<"\n # three point ( e.g. '...this is the tagline')";
fout <<"\n #";
fout <<"\n # You can also specify file of taglines by using character '@'";
fout <<"\n # at start of tagline. Example: if tagline is:";
fout <<"\n # @my_taglines";
fout <<"\n # Then jmr will gets random line from file my_taglines as a tagline.";
fout <<"\n #";
fout <<"\n # If file name is given without path, then jmr seeks file from jmr's";
fout <<"\n # directory ($JMRDIR or ~/.jmr) and if file not found, then jmr seeks";
fout <<"\n # from directory /usr/local/lib/jmr ";
fout <<"\n #";
fout <<"\n # NOTE: if you specify file with path, then you should use full path. e.g:";
fout <<"\n # @/home/my_name/sruff/tagline_file";
fout <<"\n # @C:\\misc\\taglines.txt";
fout <<"\n#tagline = This message has been UNIXized for your protection.";
fout <<"\n";
fout <<"\n";
fout <<"\n # Use external program to generate tagline.";
fout <<"\n # This overrides both taglines, 'tagline' and 'ptagline'";
fout <<"\n#tagline_program = /usr/games/fortune";
fout <<"\n";
fout <<"\n";
fout <<"\n # Set up signature file.";
fout <<"\n # If this is set, then jmr reads singature from the given";
fout <<"\n # file. Note that this overrides all tagline settings.";
fout <<"\n#signaturefile = ~/.signature";
fout <<"\n";
fout <<"\n";
fout <<"\n";
fout <<"\n########################################################################";
fout <<"\n#";
fout <<"\n# DATABASE SETTINGS";
fout <<"\n#";
fout <<"\n########################################################################";
fout <<"\n";
fout <<"\n";
fout <<"\n # Maximum count of replies to store in log.";
fout <<"\n # Default value is 200.";
fout <<"\n#replylogsize = 1500";
fout <<"\n";
fout <<"\n";
fout <<"\n # Maximum count of articles to keep in database";
fout <<"\n # NOTE: Database for single BBS with 10000 messages requires about";
fout <<"\n # 8-10MB free disk space in jmr's home directory, and 8-10MB";
fout <<"\n # free disk space in temporary directory.";
fout <<"\n # Default size is 5000";
fout <<"\n#databasesize = 10000";
fout <<"\n";
fout <<"\n";
fout <<"\n#########################################################################";
fout <<"\n#";
fout <<"\n# Experimental code and keywords";
fout <<"\n#";
fout <<"\n#########################################################################";
fout <<"\n";
fout <<"\n";
fout <<"\n # If set to 'yes' jmr will automagically remove read qwk packet.";
fout <<"\n # Setting it to 'ask', will make jmr to ask permission to delete.";
fout <<"\n # Be careful! Not Tested and probably dangerous piece of code!";
fout <<"\n # The default value is 'no'.";
fout <<"\n";
fout <<"\n#rmoldpacket = ask";
fout <<"\n";
#ifdef F_TO_ALL
fout <<"\n # To whom follow up should be addressed to ?";
fout <<"\n # Domain : all, writer, John Doe, ...";
fout <<"\n # Setting to writer assumes normal jmr behaviour.";
fout <<"\n";
fout <<"\nfollowupto = all";
fout <<"\n";
#endif
fout <<"\n";
fout <<"\n#########################################################################";
fout <<"\n#";
fout <<"\n# COLORS";
fout <<"\n#";
fout <<"\n#########################################################################";
fout <<"\n";
fout <<"\n";
fout <<"\n # Set up color for quoted part of article";
fout <<"\n #";
fout <<"\n # Colors:";
fout <<"\n #";
fout <<"\n # 0 use default screen color";
fout <<"\n # 1 red";
fout <<"\n # 2 green";
fout <<"\n # 3 brown";
fout <<"\n # 4 blue";
fout <<"\n # 5 magenta";
fout <<"\n # 6 cyan";
fout <<"\n # 7 lightgray";
fout <<"\n # 8 brightred";
fout <<"\n # 9 brightgreen";
fout <<"\n # 10 yellow";
fout <<"\n # 11 brightblue";
fout <<"\n # 12 brightmagenta";
fout <<"\n # 13 brightcyan";
fout <<"\n # 14 white";
fout <<"\n #";
fout <<"\n # For non-color terminals, colors are shown : 1 - 7 normal, 8 - 14 bold.";
fout <<"\n #";
fout <<"\n # Default color is green ";
fout <<"\n#quote_color = ";
fout <<"\n";
fout <<"\n";
fout <<"\n # Set up color for normal text of article.";
fout <<"\n # Default is cyan ";
fout <<"\n#article_color = ";
fout <<"\n";
fout <<"\n";
fout <<"\n # Foreground color for menubar.";
fout <<"\n # Default is white ";
fout <<"\n#menubar_color = ";
fout <<"\n";
fout <<"\n";
fout <<"\n # Background color for menubar.";
fout <<"\n # Default is brightblue ";
fout <<"\n#menubar_back = ";
fout <<"\n";
fout <<"\n";
fout <<"\n # Foreground color for statusline.";
fout <<"\n # Default is yellow ";
fout <<"\n#statusline_color = ";
fout <<"\n";
fout <<"\n";
fout <<"\n # Background color for statusline.";
fout <<"\n # Default is brightblue ";
fout <<"\n#statusline_back = ";
fout <<"\n";
fout <<"\n";
fout <<"\n # Foreground color for dashed line that splits screen.";
fout <<"\n # Default is normal text color";
fout <<"\n#dashline_color = ";
fout <<"\n";
fout <<"\n";
fout <<"\n # Background color for dashed line that splits screen.";
fout <<"\n # Default is normal text color";
fout <<"\n#dashline_back = ";
fout <<"\n";
fout <<"\n";
fout <<"\n # Color for text of infoline (that is line at bottom of the screen)";
fout <<"\n # Default is normal text color";
fout <<"\n#infoline_color = ";
fout <<"\n";
fout <<"\n";
fout <<"\n # Color for error message that is displayed at the infoline";
fout <<"\n # Default is brightred";
fout <<"\n#infoline_error_color = ";
fout <<"\n";
fout <<"\n";
fout <<"\n # Color for subject that is displayed at the header part of the screen";
fout <<"\n # Default is normal color, but bold text";
fout <<"\n#header_subject_color = ";
fout <<"\n";
fout <<"\n";
fout <<"\n # Color for author that is displayed at the header part of the screen";
fout <<"\n # Default is color is green";
fout <<"\n#header_author_color = ";
fout <<"\n";
fout <<"\n";
fout <<"\n # Color for subject that is displayed at the lists (grouplist, threadlist)";
fout <<"\n # Default is color is green";
fout <<"\n#subject_color = ";
fout <<"\n";
fout <<"\n";
fout <<"\n # Color for name of the author at the thread list and color for the number";
fout <<"\n # of unread articles that is displayed at the group list";
fout <<"\n # Default is color is cyan";
fout <<"\n#author_color = ";
fout <<"\n";
fout <<"\n";
fout <<"\n # Color for highlighting your name at the personal messages. ";
fout <<"\n # Default color is bright yellow";
fout <<"\n#personal_color = ";
fout <<"\n";
fout <<"\n";
fout <<"\n # Color for name of the jmrs basegroups at the group list.";
fout <<"\n # Default is color is bright green";
fout <<"\n#basegroup_color = ";
fout <<"\n";
fout.close();
Wscript->enable();
Wscript->reset_attr();
Wscript->bold();
Wscript->print( "\nResource file (%s) created with default settings.\nEdit file and set up required values.", file);
exit (EXIT_SUCCESS);
}
//**************************************************************************/
// CLASS: Jmrmain
// MEMBER FUNCTION: parse_arguments
//
// DESCRIPTION: Checks correctness of command line arguments
//
// FIXME: Replace by using getopt_long someday...
//
//
// RETURNS:
//**************************************************************************/
void Jmrmain::parse_arguments ()
{
int i;
resource_file = RESOURCE_FILE_NAME;
if (argc == 1) {
return;
}
for (i = 1; i < argc; i++) {
if (check_argument (i) == false) {
cerr << "\njmr: illegal option -- "<<argv[i]<<"\n";
cerr << "try 'jmr --help' for more information.";
cerr << endl;
exit(EXIT_FAILURE);
}
}
}
//**************************************************************************/
// CLASS: Jmrmain
// MEMBER FUNCTION: check_argument
//
// DESCRIPTION: Checks if option <n> is legal.
//
// RETURNS: true if option is ok. Otherwise false.
//**************************************************************************/
bool Jmrmain::check_argument (int& n)
{
if (strcmp (argv[n], "-f") == 0 ||
strcmp (argv[n], "--rcfile") == 0) {
if (argc > n+1) {
resource_file = argv[++n];
return true;
} else {
return false;
}
}
if (strcmp (argv[n], "-c") == 0 || strcmp (argv[n], "--color") == 0) {
Screen.force_colormode();
return true;
}
if (strcmp (argv[n], "-h") == 0 || strcmp (argv[n], "--help") == 0) {
show_help();
exit (EXIT_SUCCESS);
}
if (strcmp (argv[n], "--init") == 0) {
init_all = true;
return true;
}
if (strcmp (argv[n], "--open-jmr") == 0) {
if ( argc > n+1 ) {
open_jmr = argv[++n];
return true;
} else
return false;
}
return false;
}
//**************************************************************************/
// CLASS: Jmrmain
// MEMBER FUNCTION: show_help
//
// DESCRIPTION: Displays help for options of jmr.
//
//**************************************************************************/
void Jmrmain::show_help()
{
cerr << "jmr " << VERSION <<" -- A QWK offline mail reader";
cerr << "\nCopyright (c) 1997,1998 Jukka Vuokko\n\n" ;
cerr <<"Usage: jmr [-chl][-f file][--help][--color][--rcfile file][--init]\n\n";
cerr <<"-h, --help Show this help and exit.\n";
cerr <<"-f file, --rcfile file Use resource file 'file' instead of jmrrc\n";
cerr <<"-c, --color Force ANSI-color mode on. Useful in color-xterm\n";
cerr <<"--init Write new jmrrc file\n";
cerr <<"\nAdvanced option:\n";
cerr <<"--open-jmr <bbsid> Opens bbsid.qwk if its is there, otherwise \n";
cerr <<" bbsid dabase is opened.\n";
}
jmrsrc-0.7.23/src/jmrmisc.cc 100664 764 764 67740 6623366026 15351 0 ustar jvuokko jvuokko // $Id: jmrmisc.cc,v 1.3 1998/05/25 20:06:04 jvuokko Exp $
/*****************************************************************************
* *
* * MODULE: jmrmisc.cc
* * ----------
* *
* *
* * Revision : $Revision: 1.3 $
* * Date(UTC) : $Date: 1998/05/25 20:06:04 $
* * Source : $Source: /usr/local/cvsroot/jmr/src/jmrmisc.cc,v $
* * Author : $Author: jvuokko $
* *
* ***************************************************************************
* * This file is part of jmr offline mail reader.
* *
* * Copyright (C) 1998 Jukka Vuokko <jvuokko@iki.fi>
* *
* * 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.
* *
* * You should have received a copy of the GNU General Public License
* * along with this program; if not, write to the Free Software
* * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* ***************************************************************************
* *
* * This module contains miscallenous helper functions for jmr
* *
*****************************************************************************/
#include <stdio.h> // sprintf(), rand(), srand(), tmpnam()
#include <time.h> // time()
#include <stdlib.h> // EXIT_FAILURE, EXIT_SUCCESS
#include <signal.h>
#include <string.h> // strerror
#include <errno.h>
#ifdef __WATCOMC__ // Watcom stuff
# include <io.h> // access()
# include <direct.h>
#elif defined (__EMX__) // EMX-stuff
# include <unistd.h> // F_OK
# include <sys/types.h>
# include <dirent.h>
#else // UNIX
# include <sys/types.h>
# include <sys/wait.h> // wait()
# include <sys/utsname.h> // uname()
# include <unistd.h> // F_OK
# include <dirent.h>
# include <fcntl.h> // open, write, creat, read
#endif
#include "constants.h"
#include "jmr.hh"
#include "time.hh"
#include "terminal_io.hh"
#include "linereader.hh"
//-------------------------------
// for set current diskdrive and copying file
#ifdef OS2
//# define INCL_DOSERRORS
# define INCL_ERRORS
# define INCL_DOSFILEMGR
# include <os2.h>
#endif
#if defined (WIN32) || defined (DOS)
# include <dos.h>
#endif
#ifdef __unix__
# include <unistd.h> // getcwd, chdir
#endif
//****************** GLOBAL VARIABLES *************************************/
extern settings_t Settings;
extern Terminal_screen Screen;
extern Window *Wscript;
static const char* help_text[] = {
"@COMMON MOVING COMMANDS:",
"",
" HOME, ESC < Beginning of list/article",
" END, ESC > End of list/article",
" PGUP, Ctrl-v Page up",
" PGDN, ESC v Page down",
" UP ARROW Line up",
" DOWN ARROW Line down",
" LEFT ARROW Back to previous level",
" RIGHT ARROW Enter to next level",
"",
"@COMMON EDITING COMMANDS:",
"",
" Ctrl-a, Ctrl-e Move to beginning or end of the line",
" Ctrl-k Delete all characters, from cursor to end of line",
" Ctrl-h Delete the previous character",
" Ctrl-b, Ctrl-f Move backward of forward",
"",
"@MAIL PACKET AND DATABASE SELECTION:",
"",
" r Refresh file or database list",
" n Next packet or database",
" p Previous packet or database",
" ENTER Open packet or database",
" l Switch to list of available databases or list of",
" available mail packets",
" q, Q Quit jmr",
"",
"@GROUP SELECTION:",
"",
" n Next group",
" p Previous group",
" ENTER, -> Enter to group",
" TAB Enter to next group with unread articles",
" w Write an article to current group",
" y Switch between all groups and groups with unread articles",
" c Catchup. Mark all articles in current group as read",
" C Catchup without questions",
" t Tag/untag current group",
" T Tag/untag all groups",
" / Search from current, tagged or all groups",
" q Return to packet selection level",
"",
"@THREAD SELECTION:",
"",
" n Next thread",
" p Previous thread",
" TAB View next unread article within all threads",
" ENTER, -> View first article of current thread",
" w Write an article to current group",
" f Write followup to current article or first article",
" of current thread, if reading mode is by threads",
" r Write private reply to current article (or first",
" article of current thread)",
" y Show all threads or only threads with unread articles",
" t Tag/untag current article (does not work in thread mode)",
" T Tag/untag all articles (not in thread mode)",
" s Sort articles",
" c Catchup. Mark all articles within current group as",
" a read",
" C Forced catchup",
" e Edit current reply. Works only in group 'New Replies'",
" E Edit headers of a current reply.",
" k Kill/unkill article. Works only in groups Replylog,",
" New Replies and Tagged Articles",
" q, <- Return to group selection level",
"",
"@MESSAGE VIEWER:",
"",
" TAB, ENTER View next unread article within current group",
" n View next article",
" p View previous article",
" <, Ctrl-a View first article of a current thread",
" >, Ctrl-e View last article of a current thread",
" N Next article in history",
" P Previous article in history",
" f Write followup to current article",
" r Write private reply to current article",
" e Edit current reply. Works only in group 'New Replies'",
" E Edit headers of a current reply.",
" t Tag/untag current article",
" k Kill/unkill current article. Only in groups:",
" Replylog, New Replies and Tagged Articles",
" s Save current article or thread to file",
" SPC Page down, or if end of current article, then",
" view next unread article",
" ESC-r View article using Rot13",
" ESC-w Toggle line wrapping mode on/off",
" q, LEFT ARROW Return to thread selection level",
"",
"@MISC",
"",
" Ctrl-l Redraw screen",
" m Show message buffer",
" Q Quit jmr",
"@END" // This must be last item of the array!
};
//*************************************************************************/
// FUNCTION: read_tagline
//*************************************************************************/
//
// Function reads random line from given file.
//
//
// RETURN: line as String object.
//*************************************************************************/
String read_tagline (String const &file)
{
List<String> *taglines;
Linereader l( file );
// read lines from file of taglines to linked list
taglines = l.read_all_lines();
// get random line
String tmp;
DEBUG( "read_tagline: Lines = " << taglines->count_items());
int index = rand() % taglines->count_items();
DEBUG( "read_tagline: random is: " << index );
if ( taglines->first() == true ) {
while ( index ) {
taglines->next();
--index;
}
tmp = *taglines->get();
}
delete taglines;
return tmp;
}
//*************************************************************************/
// FUNCTION: get_tagline
//*************************************************************************/
//
// Function gets tagline from file of taglines, or from resource file.
//
// EXTERNAL VARIABLE REFERENCES:
// IN : Settings.jmrdir, Settings.tagline
// OUT:
//
// RETURN: tagline as String
//*************************************************************************/
String
get_tagline ()
{
// default tagline
String tag = Settings.tagline;
tag.cut_tail();
// read random tagline from file.
if (Settings.tagline.nget (0) == '@') {
String file;
String tmp;
tmp.copy (Settings.tagline, 1);
correct_path (tmp); // expand possible ~
tmp.ncut_end (1); // cut trailing '/' or '\'
file = tmp;
if (ACCESS (file.get(), F_OK) == 0) {
tag = read_tagline (file);
} else {
file = "/usr/local/lib/jmr/" + tmp;
if (ACCESS (file.get(), F_OK) == 0) {
tag = read_tagline (file);
} else {
file = Settings.jmrdir + tmp;
if (ACCESS (file.get(), F_OK) == 0) {
tag = read_tagline (file);
} else {
handle_error ("Cannot access file of taglines.");
tag = "";
}
}
}
}
// Remove possible ^M from the tagline. Seems like that
// usually tagline file is in MS-DOG format....
// (where is good old dos2unix?)
tag.strip_ch( '\r' );
return tag;
}
/***************************************************************************
* FUNCTION: clear_workdir
***************************************************************************
*
* DESCRIPTION: Removes all files from working directory
*
* EXTERNAL REFERENCES
* IN : Settings.workdir
* OUT:
*
***************************************************************************/
void
clear_workdir()
{
String path;
String file;
String tmp;
path = Settings.workdir;
correct_path (path);
file = path + "*.[nN][dD][xX]";
rm_files( file );
file = path + "*.[dD][aA][tT]";
rm_files( file );
file = path + "*.[iI][dD]";
rm_files( file );
file = path + "*.[mM][sS][gG]";
rm_files( file );
// remove files with exact name.
// Under Os/2, readdir() returns uppercase name, if file is
// stored to FAT file system. That's why in Os/2 it is needed
// also check for uppercase names...
tmp = DEADJMR_FILENAME;
file = path + tmp;
rm_files( file );
#ifdef OS2
file = path + tmp.upcase();
rm_files( file );
#endif
tmp = DEADJMR_ID_FILENAME;
file = path + tmp;
rm_files( file );
#ifdef OS2
file = path + tmp.upcase();
rm_files( file );
#endif
tmp = REPLY_TEMP_FILENAME;
file = path + tmp;
rm_files( file );
#ifdef OS2
file = path + tmp.upcase();
rm_files( file );
#endif
}
/***************************************************************************
* FUNCTION: handle_error
***************************************************************************
*
* DESCRIPTION: Shows error message, and if flag is HALT_FL, then program
* is halted.
*
* flag:
* DEFAULT_FL Show error message and wait for key
* REPORT_FL Print error message to stderr, and continue
* HALT_FL Print error message to stderr and halt.
*
* EXTERNAL REFERENCES
* IN : Settings.currentdir
* OUT:
*
***************************************************************************/
void
handle_error (const char *msg, int flag)
{
Screen.enable();
Wscript->reset_attr();
Wscript->textcolor (BOLD | RED);
Wscript->enable();
Wscript->print("\n*** ERROR: ");
Wscript->reset_attr();
Wscript->print("%s", msg );
if (flag != HALT_FL && flag != REPORT_FL) {
Wscript->print( "\nPress any key to continue.." );
Screen.get_ch();
Wscript->disable();
Wscript->print("\n -----------------------------");
} else if ( flag == HALT_FL) {
Wscript->print("\n*** Program halted.\n" );
Screen.refresh();
if (Settings.currentdir[0]) {
change_dir (Settings.currentdir);
}
exit (EXIT_FAILURE);
}
Screen.refresh();
}
void
system_error ( const char *msg, int flag )
{
String str = msg;
str += " -- ";
str += strerror( errno );
handle_error( str.c_str(), flag );
}
void
fatal_error( const char *msg )
{
handle_error( msg, HALT_FL );
}
/***************************************************************************
* FUNCTION: convert_to_qwk_date
***************************************************************************
*
* DESCRIPTION: Converts date from format mm-dd-yyyy to QWK style mm-dd-yy
* (and wait year 2000 happy!)
*
*
* RETURN : Pointer to c-string that contains date. Copy it immediately to
* the final location
***************************************************************************/
char*
convert_to_qwk_date (char *s)
{
static char buf[12];
strncpy (buf, s, 6);
buf[6] = s[8];
buf[7] = s[9];
buf[8] = '\0';
return buf;
}
/***************************************************************************
* FUNCTION: convert_to_jmr_date
***************************************************************************
*
* DESCRIPTION: Converts date from stubid QWK format to better mm-dd-yyyy
* format
*
*
* RETURN : pointer to static c-string that contains new date.
***************************************************************************/
char *
convert_to_jmr_date (char *s)
{
Time_handler Time;
static char buf[12];
char *tmp;
tmp = Time.get_date();
strcpy (buf, s);
buf[6] = tmp[6];
buf[7] = tmp[7];
buf[8] = s[6];
buf[9] = s[7];
buf[10] = '\0';
return buf;
}
//*************************************************************************/
// FUNCTION: get_month
//*************************************************************************/
//
// Returns number of month from date that is in jmr's format (mm-dd-yyyy)
//
//*************************************************************************/
int
get_month (char *s)
{
return (int) (10 * ((*s) - 48) + *(s+1) - 48);
}
//*************************************************************************/
// FUNCTION: get_day
//*************************************************************************/
//
// Returns number of day from date string that is in jmr's date format.
//
//*************************************************************************/
int
get_day (char *s)
{
return (int) (10 * (*(s+3) - 48) + *(s+4) -48);
}
//*************************************************************************/
// FUNCTION: get_year
//*************************************************************************/
//
// Returns number of year from date string
//
//*************************************************************************/
int
get_year (char *s)
{
return atoi (s+6);
}
/***************************************************************************
* FUNCTION: strip_re_prefix
***************************************************************************
*
* DESCRIPTION: Checks if prefix 're:' exist. If so, then remove prefix.
*
*
* RETURN : true, if prefix 're:' found.
***************************************************************************/
bool
strip_re_prefix (String& str)
{
bool rc = false;
String tmp = str;
tmp.cut_first();
if (tmp.ifind ("re:") == 0) {
rc = true;
str.copy (tmp, 3);
}
str.cut_first();
str.cut_tail();
return rc;
}
/***************************************************************************
* FUNCTION: get_yesno
***************************************************************************
*
* DESCRIPTION: Reads answer to question y/n. If user just press enter, then
* default_value is token as answer.
*
*
* RETURN : ascii code of answer.
***************************************************************************/
int
get_yesno (int default_value)
{
int c;
do {
c = Screen.get_ch();
} while (c != 'y' && c != SELECT_CMD && c != 'n');
if (c == SELECT_CMD) {
c = default_value;
}
return c;
}
//*************************************************************************/
// FUNCTION: show_help
//*************************************************************************/
//
// This function shows help.
//
// EXTERNAL VARIABLE REFERENCES:
// IN : Screen
// OUT:
//
// RETURN:
//*************************************************************************/
void show_help()
{
int y;
int ymax = Screen.get_ymax() - 3;
int index;
loop:
index = 0;
int c;
y = 0;
Screen.clear();
do {
if (*help_text[index] == '@') {
Screen.textcolor( BOLD );
Screen.print( "%s\n", help_text[index]+1 );
//cout << help_text[index]+1 << "\n";
Screen.reset_attr();
} else {
Screen.print( "%s\n", help_text[index] );
//cout << help_text[index] << "\n";
}
++index;
++y;
if (y == ymax) {
Screen.gotoxy( 0, ymax + 3);
Screen.print( "q to quit, ? to start over or any other key to continue" );
c = Screen.get_ch();
if (c == QUIT_CMD) {
return;
}
if (c == HELP_CMD) {
goto loop;
}
Screen.clear();
y = 0;
}
} while ( strcmp( help_text[index], "@END" ) );
Screen.gotoxy(0, ymax + 3);
Screen.print( "? to start over, any other to exit from help" );
c = Screen.get_ch();
if (c == HELP_CMD) {
goto loop;
}
}
//**************************************************************************/
// CLASS: Infoline
// MEMBER FUNCTION: reset
//**************************************************************************/
//
//
//
// EXTERNAL VARIABLE REFERENCES
// IN :
// OUT:
//
// PARAMETERS:
//
// RETURN:
//**************************************************************************/
void
Infoline::reset()
{
if (true == active) {
Screen.bottom();
_msg = "";
show_option_str( default_msg.get(), Settings.iline_color );
Screen.clr_eol();
active = false;
}
}
//**************************************************************************/
// CLASS: Infoline
// MEMBER FUNCTION: show
//**************************************************************************/
//
//
//
// EXTERNAL VARIABLE REFERENCES
// IN :
// OUT:
//
// PARAMETERS:
//
// RETURN:
//**************************************************************************/
void
Infoline::show( int flag)
{
if (true == active || flag & REFRESH_ALL_FL) {
int color = is_error ? Settings.iline_err_color
: Settings.iline_color;
Screen.bottom();
if (_msg.is_empty() == false) {
show_option_str( _msg.c_str(), color );
} else {
show_option_str( default_msg.c_str(), color );
}
Screen.clr_eol();
}
}
//**************************************************************************/
// CLASS: Infoline
// MEMBER FUNCTION: show_default
//**************************************************************************/
//
//
//
// EXTERNAL VARIABLE REFERENCES
// IN :
// OUT:
//
// PARAMETERS:
//
// RETURN:
//**************************************************************************/
void
Infoline::show_default()
{
Screen.bottom();
show_option_str( default_msg.c_str(), Settings.iline_color | BOLD,
Settings.iline_color);
Screen.clr_eol();
}
//**************************************************************************/
// CLASS: Infoline
// MEMBER FUNCTION: set
//**************************************************************************/
//
//
//
// EXTERNAL VARIABLE REFERENCES
// IN :
// OUT:
//
// PARAMETERS:
//
// RETURN:
//**************************************************************************/
void
Infoline::set( const char* msg, int fl )
{
active = true;
if (fl & ERROR_FL ) {
_msg = "*** ";
is_error = true;
} else {
_msg = "";
is_error = false;
}
_msg += msg;
}
void
Sline::show()
{
Screen.gotoxy( 0, Screen.get_ymax() - 1 );
Screen.reset_attr();
if( Screen.is_color_term() == true ) {
Screen.textcolor( Settings.sline_color );
Screen.back_color( Settings.slineback_color );
} else {
Screen.inverse();
}
Screen.addstr( get_sline() );
Screen.reset_attr();
}
//*************************************************************************/
// FUNCTION: set_dashline_color
//*************************************************************************/
//
// Sets colors of dashed line on.
//
// EXTERNAL VARIABLE REFERENCES:
// IN : Settings.dash_color, Settings_dashback_color
// OUT: Screen
//
// RETURN:
//*************************************************************************/
void
set_dashline_color()
{
Screen.reset_attr();
if (Settings.dash_color != DEFAULT_FCOLOR) {
Screen.textcolor( Settings.dash_color );
}
if (Settings.dashback_color != DEFAULT_BCOLOR) {
Screen.back_color( Settings.dashback_color );
}
}
//*************************************************************************/
// FUNCTION: show_option_str
//*************************************************************************/
//
// Displays given string using given 'bold_color' in all text that are
// between '@' marks. Color for other text is 'color'.
//
// e.g. If msg is "Press @Q@ for quit", is letter Q highlighted using
// color 'bold_color' and other text is drawed using color 'color'.
//
// EXTERNAL VARIA