/* -*-Mode: C++;-*-
* PRCS - The Project Revision Control System
* Copyright (C) 1997 Josh MacDonald
*
* 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.
*
* $Id: prcserror.h 1.14.1.5.1.11.2.2 Sun, 09 May 2004 18:21:12 -0700 jmacd $
*/
#ifndef _PRCSERROR_H_
#define _PRCSERROR_H_
extern "C" {
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include "config.h" /* So PRCS_DEVEL is defined. */
}
#include "dstring.h"
class PrettyOstream;
class QueryOstream;
extern QueryOstream prcsquery;
extern PrettyOstream prcsoutput;
extern PrettyOstream prcsinfo;
extern PrettyOstream prcswarning;
extern PrettyOstream prcserror;
#ifdef PRCS_DEVEL
extern PrettyOstream prcsdebug;
#endif
#if !defined(PRCS_DEVEL) || !defined(__GNUG__)
extern ErrorToken global_error_token;
extern int return_if_fail_if_ne_val;
#endif
#ifdef PRCS_DEVEL
#define DEBUG(command) prcsdebug << command << prcsendl
#else
#define DEBUG(command) (void)0
#endif
/*********************************************************************/
/************************ Error Return Types *************************/
/*********************************************************************/
/*
* PrVoidError, NprVoidError --
*
* The two classes below serve to allow functions to return either
* an error or some type, the typed errors are derived from these.
* The Void error types only return an error or not an error. The
* difference between the two types is that PrVoidError represents
* an error which has been printed, and NprVoidError represents an
* error which has NOT been printed. The following methods are
* available:
*
* Constructor(NonErrorToken tok) -- constructs a non-error value
* Constructor(ostream& stream) -- (PrVoidError only) constructs a
* error value. Assumes that stream is in fact a PrettyOstream,
* which has an ErrorVal() method. In this manner, a PrVoidError
* may be constructed with the statement:
*
* return prcserror << FatalError << "exec failed" << perror;
*
* Constructor(ErrorToken tok) -- (NprVoidError only) constructs an
* error value with token type.
*
* bool Error() -- returns true if THIS is an error, otherwise false.
*
* ErrorToken ErrorVal() -- assumes Error() is true, and returns the token.
*
* operator bool() -- returns Error();
*
* PrVoidError VoidError() -- returns THIS. This method is defined to
* help convert an non-void error type into a void error. See
* PrError<T>
*/
class PrVoidError {
public:
PrVoidError(NonErrorToken) :_tok(FatalError), _error(false) { }
#if defined(PRCS_DEVEL) && defined(__GNUG__)
explicit
#endif
PrVoidError(ErrorToken tok) :_tok(tok), _error(true) { }
explicit PrVoidError(ostream& s);
bool error() const {
#if !defined(PRCS_DEVEL) || !defined(__GNUG__)
global_error_token = _tok;
#endif
return _error; }
ErrorToken error_val() const { return _tok; }
PrVoidError void_error() const { return PrVoidError(error_val()); }
protected:
ErrorToken _tok;
bool _error;
private:
operator bool () const { return error(); }
int operator!();
};
#if defined(PRCS_DEVEL) && defined(__GNUG__)
class NprVoidError {
public:
NprVoidError(NonErrorToken) :_error(false) { }
NprVoidError(ErrorToken tok) :_tok(tok), _error(true) { }
bool error() const { return _error; }
ErrorToken error_val() const { return _tok; }
NprVoidError void_error() const { return NprVoidError(error_val()); }
protected:
ErrorToken _tok;
bool _error;
private:
operator bool () const { return error(); }
int operator!();
};
#endif
/*
* PrError<T>, NprError<T> --
*
* The two classes below serve to allow functions to return either
* an error or some type. The following methods are available:
*
* Constructor(Type) -- a non error constructor
*
* Constructor([N]PrVoidError) -- an error constructor, from a void
* error value. This assumes Error() is true for the
* argument. Itand allows PrError<T> to convert to
* PrError<S>, since by calling
* PrError<S>(PrError<T>.VoidError()). In this manner, the
* macro Return_if_fail below allows the statement
*
* Return_if_fail(x << foo());
*
* to return from the function if foo() has the same print or
* non-print error type as the caller.
*
* VoidError() -- returns the correct void error value.
*/
template <class Type>
class PrError : public PrVoidError {
public:
PrError(const Type& val) :PrVoidError(NoError), _val(val) { }
explicit PrError(ostream& s);
PrError(PrVoidError err) :PrVoidError(err) { }
#if defined(PRCS_DEVEL) && defined(__GNUG__)
explicit
#endif
PrError(ErrorToken tok) :PrVoidError(tok) { }
PrVoidError void_error() const { return PrVoidError(error_val()); }
Type non_error_val() const { return _val; };
private:
Type _val;
int operator!();
};
#if defined(PRCS_DEVEL) && defined(__GNUG__)
template <class Type>
class NprError : public NprVoidError {
public:
NprError(const Type& val) :NprVoidError(NoError), _val(val) { }
NprError(ErrorToken tok) :NprVoidError(tok) { }
NprError(NprVoidError err) :NprVoidError(err) { }
NprVoidError void_error() const { return NprVoidError(error_val()); }
Type non_error_val() const { return _val; };
private:
Type _val;
int operator!();
};
#endif
template <class Type>
const PrError<Type>& operator<<(Type& var, const PrError<Type>& val)
{
if(!val.error())
var = val.non_error_val();
return val;
}
#if defined(PRCS_DEVEL) && defined(__GNUG__)
template <class Type>
const NprError<Type>& operator<<(Type& var, const NprError<Type>& val)
{
if(!val.error())
var = val.non_error_val();
return val;
}
#endif
/*
* if expr returns an error, return the same error
*/
#if defined(PRCS_DEVEL) && defined(__GNUG__)
#define Return_if_fail(expr) \
({ typeof(expr) _E_(expr); if((_E_).error()) return _E_.void_error(); })
#else
#define Return_if_fail(expr) \
do { \
if((expr).error()) \
return PrVoidError(global_error_token); \
} while(false)
#endif
/*
* if expr returns an error, return the same error, else, if
* expr's value compares != to val, execute the following block
*/
#if defined(PRCS_DEVEL) && defined(__GNUG__)
#define Return_if_fail_if_ne(expr, val) \
if( ({ typeof(expr) _E_(expr); \
typeof(_E_.non_error_val()) _E_Val_(_E_.non_error_val()); \
if((_E_).error()) return _E_.void_error(); \
_E_Val_ != val; }) )
#else
/* This kind of loses, but oh well */
#define Return_if_fail_if_ne(expr, val) \
Return_if_fail(return_if_fail_if_ne_val << expr); \
if(val != return_if_fail_if_ne_val)
#endif
/*
* since errors evaluate to true, this is just to act as a reminder
* that an error(which evalulates true) is being checked for.
*/
#define If_fail(expr) if((expr).error())
#define Failure(expr) ((expr).error())
/* So that the error mechanism can be implemented with exception
* handling later */
#define pthrow return 0 <
/*********************************************************************/
/*************************** Stream Types ****************************/
/*********************************************************************/
/* PrettyStreambuf --
*
* Nice looking output in a harsh environment, or something. This
* class formats all inputs by prepending a fill prefix and
* filling lines by greedily inserting line breaks wherever
* possible, as set by set_fill_break, with true indicating that
* it may break lines at whitespace. The squote() modifier below
* single-quotes a string inside `single quotes' and prevents the
* streambuf from breaking lines at whitespace inside the quotes.
* It forwards all output to another stremabuf. Currently, there
* are three of these used in PRCS. The ostream prcserror's
* streambuf forwards its output to a filebuf(stderr). The
* ostream prcsout's streambuf forwards its output to a
* filebuf(stdout). The ostream prcsquery's streambuf forwards
* its output to a ostrstreambuf, since prcsquery doesn't know
* whether to send its output to stdout or stderr until the query
* is received. */
class PrettyStreambuf : public streambuf {
public:
PrettyStreambuf(streambuf* forward0, int* dont_print0);
void set_fill_width(int width0);
void set_fill_prefix(const char* prefix0);
bool set_fill_break(bool breakon0);
bool set_fill_pretty(bool prettyon0);
int set_column(int col);
void reset_column();
const char* fill_prefix() const;
bool fill_pretty() const;
protected:
virtual int xsputn(const char* s, int n);
virtual int overflow(int c = EOF);
virtual int sync();
protected:
streambuf* forward;
int col, width;
bool breakon;
bool prettyon;
bool new_line;
int *dont_print;
Dstring prefix;
Dstring line_buffer;
};
/* PrettyOstream --
*
* This is used for prcserror and prcsout, as detailed in the
* documentation for PrettyStreambuf, above. */
class PrettyOstream : public ostream {
public:
PrettyOstream(PrettyStreambuf* stream, ErrorToken err)
:ostream(stream),
_buf(stream),
_err(err)
{ }
PrettyOstream(PrettyStreambuf* stream, NonErrorToken err)
: ostream(stream),
_buf(stream),
_err(err)
{ }
PrettyStreambuf& ostreambuf() const { return *_buf; }
PrVoidError error() const { return _err; }
protected:
PrettyStreambuf *_buf;
PrVoidError _err;
};
/* I have the problem of, if a user hasn't specified -f (force, which
* guarantees no interactive queries) ask the user a question, and
* otherwise warn the user of some decision -f has caused. You'd like
* to keep the messages short and still allow help for
* first-time-users. Also, there is a -n open for only reporting
* actions (like make).
* In each of these cases, you want to output something different and
* take a different course of actions, depending on several global
* variables and possibly user input. So I have devised the a class
* QueryOstream to inherit from some type of ostream, which buffer
* some initial part of a message and then a bunch of omanips for
* declaring what you want it to print where.
omanip force(const char*, ErrorToken);
omanip force(const char*, NonErrorToken);
* If -f is present, it will finish the output on the standard error and
* set the given return value.
omanip report(const char*, ErrorToken);
omanip report(const char*, NonErrorToken);
* If -n is present, it will finish the output on the standard output
* and set the given return value.
omanip option(char, const char*, ErrorToken);
omanip option(char, const char*, NonErrorToken);
omanip default(char, const char*, ErrorToken);
omanip default(char, const char*, NonErrorToken);
* Otherwise, tell prcsquery the options available, with a message to
* display in case the user asks for help and a return value.
* default() sets the default return value for if the user just types
* enter.
omanip query(const char*);
* Finally, set the query text and finish the command.
prcsquery << "Subproject file " << squote(subfile) << " contains errors. "
<< force("Ignoring entire subproject", NoError)
<< report("Ignore entire subproject", NoError)
<< option('n', "Fail and abort PRCS.", UserAbort)
<< default('y', "Ignore this subproject, as if it were omitted "
"from the file-or-directory list on the command line.",
NoError)
<< query("Ignore and continue");
* If -f was present, on standard error:
prcs: Subproject file `dir/file.prj' contains errors. Ignoring entire
prcs: subproject.
* If -n was present, on standard output:
prcs: Subproject file `dir/file.prj' contains errors. Ignore entire
prcs: subproject.
* Otherwise, query the user:
prcs: Subproject file `dir/file.prj' contains errors. Ignore entire
prcs: and continue(yn?)[y]?
* where ? will yield:
prcs: n -- Fail and abort PRCS.
prcs: y -- Ignore this subproject, as if it were omitted from the
prcs: file-or-directory list on the command line.
prcs: Ignore and continue(yn?)[y]
*/
typedef PrError<char> PrCharError;
typedef PrError<const char*> PrConstCharPtrError;
struct QueryOption {
QueryOption()
:val(NoError) { }
QueryOption(char let0, const char* descr0)
:let(let0), descr(descr0), val(let0) { }
QueryOption(char let0, const char* descr0, ErrorToken tok0)
:let(let0), descr(descr0), val(tok0) { }
char let;
const char* descr;
PrCharError val;
};
struct BangFlag {
BangFlag() :flag(false) { }
bool flag;
};
class QueryOstream : public PrettyOstream {
private:
#define MAX_QUERY_OPTIONS 10
public:
QueryOstream(strstreambuf *base_stream0,
PrettyStreambuf* query_stream0,
filebuf *stdout_stream0,
filebuf *stderr_stream0);
/*
* Manipulator methods.
*/
ostream& option_manip(QueryOption);
ostream& default_manip(QueryOption);
ostream& force_manip(const char* message);
ostream& force_manip(const char* message, char different);
ostream& report_manip(const char* message);
ostream& query_manip(const char* message);
ostream& string_query_manip(const char* message);
ostream& definput_manip(const char* message);
ostream& help_manip(const char* message);
ostream& bang(BangFlag*);
PrCharError result() const { return val; }
PrConstCharPtrError string_result() const { return string_val; }
protected:
strstreambuf *base_stream;
PrettyStreambuf* query_stream;
filebuf *stdout_stream, *stderr_stream;
PrCharError val;
PrConstCharPtrError string_val;
QueryOption options[ MAX_QUERY_OPTIONS ];
int default_option;
int force_option;
const char* force_message;
const char* report_message;
const char* default_input;
const char* help_string;
int option_count;
BangFlag* bang_flag;
};
extern char const default_fail_query_message[];
template <class TP> class omanip;
template <class TP> ostream& operator<< (ostream& o, const omanip<TP>& m);
template <class TP> class omanip {
ostream& (*_f)(ostream&, TP);
TP _a;
public:
omanip(ostream& (*f)(ostream&, TP), TP a) : _f(f), _a(a) { }
friend ostream& operator<< <>(ostream& o, const omanip<TP>& m);
};
template <class TP> ostream& operator<< (ostream& o, const omanip<TP>& m)
{
return (*m._f)(o, m._a);
}
ostream& __omanip_query(ostream& s, const char* message);
omanip<const char*> query(const char* message);
ostream& __omanip_help(ostream& s, const char* message);
omanip<const char*> help(const char* message);
ostream& __omanip_string_query(ostream& s, const char* message);
omanip<const char*> string_query(const char* message);
ostream& __omanip_definput(ostream& s, const char* message);
omanip<const char*> definput(const char* message);
ostream& __omanip_force(ostream& s, const char* message);
omanip<const char*> force(const char* message);
struct CharAndStr {
CharAndStr (const char* str0, char c0) :str(str0), c(c0) { }
const char* str;
char c;
};
ostream& __omanip_force(ostream& s, CharAndStr cs);
omanip<CharAndStr> force(const char* message, char different);
ostream& __omanip_report(ostream& s, const char* message);
omanip<const char*> report(const char* message);
ostream& __omanip_option(ostream& s, QueryOption o);
omanip<QueryOption> option(char let, const char* descr);
omanip<QueryOption> option(char let, const char* descr, ErrorToken tok);
omanip<QueryOption> optfail(char let);
ostream& __omanip_bang(ostream& s, BangFlag* flag);
omanip<BangFlag*> allow_bang(BangFlag& flag);
ostream& __omanip_default(ostream& s, QueryOption o);
omanip<QueryOption> defopt(char let, const char* descr);
omanip<QueryOption> defopt(char let, const char* descr, ErrorToken tok);
omanip<QueryOption> deffail(char let);
/* squote --
*
* This manipulator takes a name and prints it on the ostream,
* with single quotes around it, and sets nobreak so that the name
* is not line broken. */
ostream& __omanip_squote(ostream&, const char*);
omanip<const char*> squote(const char* file);
omanip<const char*> squote(const Dstring* file);
omanip<const char*> squote(const Dstring& file);
/* fileline --
*
* This manipulator takes a filename and line number and prints
* formats an error message filename:lineno: error message.
*/
struct FileLine {
FileLine(const char* file0, int line0) :file(file0), line(line0) { }
const char* file;
int line;
};
ostream& __omanip_fileline(ostream& s, FileLine fl);
omanip<FileLine> fileline(const char* file, int line);
/* filetuple --
*/
class FileEntry;
struct FileTuple {
enum TupleType { DiffPair, MergeTriple };
FileTuple(FileEntry* work, FileEntry* com, FileEntry* sel, TupleType type0)
:work_fe(work), com_fe(com), sel_fe(sel), type(type0) { }
FileEntry* work_fe; /* or from */
FileEntry* com_fe; /* or to */
FileEntry* sel_fe;
TupleType type;
};
ostream& __omanip_filetuple(ostream& s, FileTuple tup);
omanip<FileTuple> merge_tuple(FileEntry* work,
FileEntry* com,
FileEntry* sel);
omanip<FileTuple> diff_tuple(FileEntry* from,
FileEntry* to);
/* setcol --
*
* This manipulator forces the min column to aid in aligning text. */
ostream& __omanip_setcol(ostream&, int);
omanip<int> setcol(int col);
/*
* Manipulators for class PrettyOstream which take no arguments
*/
extern ostream& dotendl(ostream& s);
extern ostream& prcsendl(ostream& s);
extern ostream& perror(ostream& s);
/* setup_streams initializes the three streams */
extern void setup_streams(int argc, char** argv);
extern const char* re_query_message;
extern int re_query_len;
extern void re_query();
/* call after command line has been parsed */
extern void adjust_streams();
extern void kill_prefix(ostream& s);
extern void continue_handler(SIGNAL_ARG_TYPE);
/* Error Type constructor definitions */
inline PrVoidError::PrVoidError(ostream& s)
{ *this = ((PrettyOstream&)s).error(); }
template <class type> inline PrError<type>::PrError(ostream& s)
:PrVoidError(((PrettyOstream&) s).error()) { }
/* Serious kludge */
inline PrVoidError operator< (int /*x*/, ostream& os)
{
return PrVoidError(os);
}
#endif
syntax highlighted by Code2HTML, v. 0.9.1