|
25 Years of Programming
An open source source for C, C++, OWL, BASIC, MDB, XLS, DOT, and more... |
Home Projects Up Sitemap Search Blog Forum+Chat About Us Privacy Terms of Use Feedback FAQ Images Services Payments Humor Music |
mylib.cpp - Borland C++ 4.0 libraryA single file to automatically pull library routines into your C++ program. I've used this file with MSDOS, Borland OWL for Windows, and the Borland EasyWin Windows interface. The date on the first line of the listing is the "version number". Starting with the 5-31-07 version, the #includes of external source files that were in the previous versions have been replaced with the code from the files that were being included, so it is no longer necessary to run around to other web pages collecting them. However, the old web pages containing the old code do still exist. If I have made any mistakes while collapsing code into this file, those pages might be useful for reference. The hyperlinks in the code below take you to the old web pages. Since the conversion, I've tested this briefly with MSDOS and OWL Windows targets without error. I no longer have any EasyWin target applications to use for testing. This file requires my.h, to which there is a link in the code below. There is also a version of mylib.cpp for Microsoft Visual C++, .NET, STL. |
/* mylib.cpp 5-31-07
Copyright (C)1997-2000, 2006-07 Steven Whitney.
Initially published by http://25yearsofprogramming.com.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
Version 3 as published by the Free Software Foundation.
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#including this in your program will pull into it the source code for all my library functions
that are compatible with the platform you're using. Using this instead of a .LIB file ensures
proper compilation regardless of memory model or platform.
I've used this with MSDOS, Borland OWL for Windows, and Borland's EasyWin Windows interface.
When you use it this way, it drags into your program everything
that meets the #include criteria, whether it's used or not, unlike a .LIB.
Not sure that's true: using #include mylib.cpp seems to REDUCE
.exe size by 14k. Including this file in a DOS application where it was
unnecessary added only about 16k.
IF your main program has only one source file:
#include mylib.cpp in your source AFTER MY.H, #pragma hdrstop (it cannot be precompiled)
OR
IF your main program has multiple source files:
Create a source file in the project, LIBRARY.CPP, which has all the #includes
that any of your other source files use, plus #include mylib.cpp. This is
the only way this file can use its various "if #defined" tests that depend
on #defines from previously #included headers. You can also put into LIBRARY.CPP
#includes for any other library or utility files (filearay, stataray, stopwatc, etc.).
*/
#include "c:\bcs\my.h"
#pragma hdrstop // you can't precompile code
//////////////////////////////////////////////////////////////////////////////
// EITHER DOS OR WINDOWS (always included)
//----------------------------------------------------------------------------
/* backfile.cpp 2-14-98
Creates a backup copy of a file.
Given the name of a file you want to backup before writing to, it deletes filename.bak (the
current backup file), renames the current filename.ext to filename.bak (backs it up), and
returns a reference to the original filename.ext string.
It should work properly with full path names.
Use: ofstream outfile(backupfile(filename.ext).c_str());
*/
const string& backupfile(const string& filename)
{
if(filename.length())
{
string bakfile(filename);
if(bakfile.contains(".")) // delete extension
bakfile.remove(bakfile.rfind("."));
bakfile += ".bak"; // add ".bak"
unlink(bakfile.c_str()); // delete .bak file, if any
rename(filename.c_str(),bakfile.c_str()); // rename current to .bak
}
return(filename); // return reference to original filename
}
//----------------------------------------------------------------------------
/* countchr.cpp 12-28-96
counts number of occurrences of a char in a string. Pulled from code.cpp.
*/
uint countchr(const string& s, char ch) // string to search, char to look for
{
int count = 0;
int N = s.length();
for(int i = 0 ; i < N ; i++)
if(s[i] == ch)
count++;
return(count);
}
//----------------------------------------------------------------------------
/* isallspa.cpp isallspace() 3-12-96
Returns TRUE if char array is zero-length or all whitespace, FALSE otherwise.
Could add additional version for a const string&
*/
BOOL isallspace(char *s)
{
char *p = s;
while(*p)
if(!isspace(*(p++))) // first non-space char causes return
return(FALSE);
return(TRUE);
}
//----------------------------------------------------------------------------
/* logerror.cpp 1/7/02
functions for appending strings to text files:
logs an error string to an error log file, and keeps count.
*/
uint logtofile(const string& s, const string& filename)
{
if(s.length()&& filename.length())
{
ofstream(filename.c_str(),ios::app) << s << endl;
return(1);
}
return(0);
} //logtofile
//-----------------------------------------------------------------------
// write error (if provided) to disk. returns cumulative error count.
// you can call with empty string just to get the count.
uint logerror(string error, string filename, BOOL beep)
{
static uint errorcount = 0; // # of errors
if(error.length()) // if an error occurred,
{
ostrstream os;
os << TTime() << ": " << error << ends;
logtofile(os.str(),filename);
if(beep) // note: only beeps if we're writing an error
{
#if defined(_Windows)
MessageBeep(MB_ICONEXCLAMATION); // a global WinSDK function
#else
cout << "\a";
// SerialCom.Send(os.str());
#endif
}
delete[] os.str();
errorcount++;
}
return(errorcount);
} //logerror
//----------------------------------------------------------------------------
/* lrandom.cpp 3-12-03
Contains lrandom() and any other specialized random number generators I need.
*/
//----------------------------------------------------------------------------
/* returns a uniformly distributed random double in the range 0 (inclusive) to 1.0 (limit).
builds as text because that's the only way I could think of that seems foolproof.
There are some pitfalls to generating random numbers; see a severe one below.
this is very fast, and works for DOS, Win16, and Win32.
this method seems to work properly, based on running the results through stats.exe.
*/
double frand()
{
int len = 17; // "0." plus 15 significant digits is max possible for a double.
char buf[18]; // must be len + 1
buf[0] = '0'; buf[1] = '.';
for(int i = 2 ; i < len ; i++)
{
// you cannot use random() here because in Win32 random() calls lrandom(), which
// calls this, creating an infinite recursion, stack overflow, and crash.
// the method highlighted here is equivalent to random(10).
// vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
buf[i] = (char)(0x30 + (int)( ((long)rand() * 10) / (RAND_MAX + 1) ));
}
buf[len] = 0;
return(atof(buf));
} //frand
//----------------------------------------------------------------------------
/* Returns a long random number from 0 to specified upper limit - 1, inclusive.
This is random() for longs, and I use it as replacement random() for Win32.
For a possibly negative random number, use this, and then if(random(2)) { make it negative }
this is very fast, and seems to work, based on running the results through stats.exe.
In Win32 only, Borland has a function _lrand(), which returns a random long
value (probably 0 to LONG_MAX), but you can't scale it to a different interval
by the usual method because the result of the required multiplication can
overflow LONG_MAX and even ULONG_MAX, and that's the largest integral value
the computer can handle. Borland's stdlib.h method of using % operator doesn't
work, in that random(N) has a repeat cycle of N if N is a power of 2, probably
because of coincidence(?) with cycles inherent in the random # generation.
*/
long lrandom(long top)
{
if(top <= 1L) // 0, 1 always produce 0, and negative is illegal
return(0L);
return(frand() * top);
} //lrandom
//----------------------------------------------------------------------------
/* returns a normally distributed random INTEGER from the distribution with the
given mean and standard deviation, using the method from the TI-59 calculator book
TI Programmable 58/59 Applied Statistics using the power of your Solid State Software(tm)
module, (C)1977 Texas Instruments, p.2-6.
*/
int NormalRandom(double mean, double stddev)
{
return round(sqrt(-2.0 * log(frand())) * cos(2.0 * M_PI * frand()) * stddev + mean);
} //NormalRandom
//----------------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////////
// DEAD STORAGE SECTION:
//----------------------------------------------------------------------------
#if 0
// keep this section as a useful warning about the method and any like it.
//----------------------------------------------------------------------------
/* #ERROR I THINK THIS ENTIRE METHOD IS UNWORKABLE DUE TO CENTRAL TENDENCY:
The mean of the aggregate rand() calls is 16k, and the more times (N) you call it,
the closer their sum will approximate N * 16k. So the random numbers are NOT
uniformly distributed within the range. This result was verified using test runs.
Notes: RAND_MAX = 32767, LONG_MAX = 2147483647L, ULONG_MAX = 4294967295UL
method: since rand() can only return an int <= RAND_MAX, determine how many maximum
rand() intervals are contained in "top". Calculate rand() for each interval and add
them together. Then there's one interval left: the remainder. Get a random number
< remainder, add it to the rest, and the result is a random number that could be
from 0 to top-1.
*/
long lrandom(long top)
{
if(top <= 1L) // 0, 1 always produce 0, and negative is illegal
return(0L);
long randomnumber = 0L;
for(long intervals = (top - 1L) / (long)RAND_MAX ; intervals > 0L ; intervals--)
{
long i = (long)rand();
randomnumber += i;
}
long remainder = top % (long)RAND_MAX; // the final interval
if(remainder == 0L)
remainder = (long)RAND_MAX;
// remainder is relatively small, <= RAND_MAX, and overflow impossible,
// so explicitly use the usual formula for random(top) (see stdlib.h)
// whose maximum return is (top - 1). We can't actually use random()
// because in original flat model, it was redefined and didn't work,
// and in my new method, it's redefined to use lrandom, which would
// create endless recursive calls back here.
randomnumber += ((remainder * (long)rand()) / ((long)RAND_MAX + 1L));
return(randomnumber);
} //lrandom
#endif // 0
//----------------------------------------------------------------------------
/* mystring.cpp 8-23-01
------
Notes:
Additional string functions, including fixes for apparent bugs in the Borland string class.
This file cannot be precompiled. #include AFTER my.h wherever you use it.
It is automatically included into mylib.cpp.
In DOS, you will get 2 "duplicated symbol" warnings for the string functions.
In Win, you don't get those warnings, possibly because Win calls library
functions from a DLL at runtime, and then only if a static-linked version isn't
already in the program.
//----------------------------------------------------------------------------
Bugs in the Borland C++ 4.0 cstring library:
operator >> and read_token():
string.length() is corrupted after input if the istream buffer does not contain trailing
whitespace before EOF. The text of the string itself is properly read, but subsequently
anything that uses length(), including [], fails. (HOW? retest and document)
string += string doesn't work, but string + string does (in another test, it didn't).
It is possible that these buggy functions do work properly when the default values
of paranoid_check (especially), skipwhite, and/or case_sensitive are in effect.
(This is true for rfind().) In most of my programs, I have NOT been using the default
values for these. Surprisingly, the string class appears to be one of the more recent ones,
with documentation that appears hastily prepared and has errors.
Static linked code for string classes is in C?.LIB files.
//----------------------------------------------------------------------------
*/
//////////////////////////////////////////////////////////////////////////////
#if 0
//////////////////////////////////////////////////////////////////////////////
// OBSOLETE. keep only as long as useful for reference or backup.
//----------------------------------------------------------------------------
/* 2-26-98
skips leading whitespace, reads a string from the stream, stopping at whitespace or EOF.
Causes stream to fail only if nothing, or only whitespace, was read,
in which case the target string s remains unchanged.
This should be an exact replacement for buggy version. No length limit.
*/
istream _FAR & _Cdecl _FARFUNC operator >> (istream _FAR &is, string _FAR &s)
{
int i;
string temp; // so s won't be changed if nothing is read.
is >> ws; // discard leading space
while(1)
{
i = is.peek();
if((i == EOF) || isspace(i)) // non-whitespace control chars can be part of string
break;
temp += (char)(is.get()); // append the char to string
}
if(temp.length())
s = temp;
return(is);
} // old operator >>
//////////////////////////////////////////////////////////////////////////////
#endif // 0
//////////////////////////////////////////////////////////////////////////////
// BUG FIXES
//----------------------------------------------------------------------------
/* 2-28-99
skip leading whitespace, and read a string from a stream, up to whitespace or EOF.
This should be an exact replacement for the buggy version, plus an enhancement:
It can also read quoted multi-token strings:
If first char encountered is a QUOTE, reads everything up to the closing QUOTE.
*/
istream _FAR & _Cdecl _FARFUNC operator >> (istream _FAR &is, string _FAR &s)
{
// so s won't be changed if nothing is read for single token,
// and to protect s from error when reading quoted string with read_to_delim.
string temp;
is >> ws; // discard leading space
int i = is.peek(); // peek the first char
// if first char encountered is a quote, read the quoted string.
// a null string ("") will empty s, allowing writing and retrieving empty
// strings from a file, which ordinarily is not possible.
if(i == '\"')
{
is.get(); // get the first quote
// For now, trust read_to_delim. it is fine as long as the delim is present.
// if EOF is hit before delim, it will return the stream as failed NOW,
// even if something was legitimately read into the string. Thus, in this case,
// while(istream >> s) will stop prematurely, the loop not being executed
// for the last string read. BUT this may be good, because if delim was not
// present, the file may be corrupt and the input invalid.
// Note that if this happens, although the last string read won't be processed,
// there won't be any warning about the situation.
// read up to the trailing quote, and extract and discard the quote
temp.read_to_delim(is,'\"');
// if read_to_delim has bugs, reading into temp might protect s from corruption.
// getline(is, s, '\"'); might also be an alternative
s = temp;
return(is);
}
// if it falls through, read an ordinary 1-token string.
// causes stream to fail now only if nothing, or only whitespace, is read,
// in which case the target string s remains unchanged.
while(1)
{
// on entry, i already contains the original peek
if((i == EOF) || isspace(i)) // non-whitespace control chars can be part of string
break;
temp += (char)(is.get()); // append the char to string
i = is.peek(); // peek at the next one
}
if(temp.length()) // if nothing, s is unchanged.
s = temp;
return(is);
} // operator >>
//----------------------------------------------------------------------------
/* 2-26-98
skip leading whitespace, and read one token from a stream,
stopping at whitespace or EOF. Does NOT handle quoted strings,
since a token should be just 1 word or equivalent.
This is almost identical to the earlier operator >>, before enhancement.
*/
istream _FAR & string::read_token(istream _FAR &is)
{
int i;
string temp; // so s won't be changed if nothing is read.
is >> ws; // discard leading space
while(1)
{
i = is.peek();
if((i == EOF) || isspace(i)) // non-whitespace control chars can be part of string
break;
temp += (char)(is.get()); // append the char to string
}
if(temp.length())
*this = temp;
return(is);
} // read_token
//----------------------------------------------------------------------------
/* 7-4-00
if paranoid_check is ON, the original rfind always returns NPOS for a multi-char s.
otherwise, it seems to work ok.
A better solution MAY be just to turn paranoid check OFF and watch for any problems.
That's what I'm currently doing, and is why this fix is disabled.
Paranoid check does have an inherent problem in that it will consider a string with
embedded zeroes to terminate at the first zero.
In a brief test, this version seems to do what it's supposed to, but makes no use of
paranoid_check or any other string settings.
throw() means it does NOT throw anything.
*/
#if 0
size_t _RTLENTRY string::rfind(const string _FAR &s, size_t pos) const throw()
{
size_t u = find(s);
if(u == NPOS) // if not found at all, quit
return(NPOS);
size_t temp;
while((temp = find(s, u + 1)) < pos) // find next occurrence beyond u, but not
u = temp; // at or beyond pos.
return(u);
} //rfind
#endif // 0
//----------------------------------------------------------------------------
// END BUG FIXES
//////////////////////////////////////////////////////////////////////////////
// MORE STRING FUNCTIONS
//----------------------------------------------------------------------------
// change a string to "title case", all lower case except first letter of each word
string& titlecase(string& s)
{
s.to_lower(); // start with all lower case
BOOL spacebefore = TRUE; // whether the previous char was space
for(int i = 0 ; i < s.length() ; i++)
{
if(spacebefore)
s[i] = (char)toupper(s[i]);
spacebefore = !isalnum(s[i]);
}
return(s);
} // titlecase
//----------------------------------------------------------------------------
// for one-step output of a string in quotes: cout << qstring(s). source string is unchanged.
string qstring(const string& s)
{
string t(s);
t += ("\"");
t.prepend("\"");
return(t);
} // qstring
//----------------------------------------------------------------------------
// remove the quotes from around a quoted string. source string is unchanged.
string unqstring(const string& s)
{
string t(s);
if((t.length() >= 2) && (t[0] == '\"') && (t[t.length() - 1] == '\"'))
{
t.remove(0,1);
t.remove(t.length() - 1);
}
return(t);
} // unqstring
//----------------------------------------------------------------------------
// for one-step output of a string in brackets: cout << bstring(s). source string is unchanged.
string bstring(const string& s)
{
string t(s);
t += ("]");
t.prepend("[");
return(t);
} // bstring
//----------------------------------------------------------------------------
/* fromstring 1-1-99
A strings-only override of my template version. Necessary because if this situation
fromstring(string,string) were in template version, the compiler, when instantiating the template,
would have to GENERATE the code for the line (below) "var = value" for ALL instantiations,
including those where that line is inapplicable because the types are not the same.
But if the types aren't the same, the code itself is illegal, a compile-time error.
(and there's no RTTI at compile time, so you can't conditionally exclude the code)
(am I sure there's not? Even if there is, probably not for templates.)
copies ALL of a multi-word string, which the >> procedure in template version would not.
*/
string& fromstring(string& var, const string& value)
{
var = value; // just copy the whole string
return(var);
} // fromstring
//----------------------------------------------------------------------------
/* measures how similar two strings, usually tokens (single words), are.
This is an experimental concept that was interesting to work on.
I think this is used in wtalk.cpp; some other programs have similarity() or
SimilarityTo() functions that explore a more generalized concept of measuring
"similarity" of two objects.
returns a value between 0 (no similarity at all) and 1.0 (identical)
Measure is the same regardless of which string is first or second.
Some similar and likely better measures have been devised:
See "Levenshtein distance", php functions levenshtein())and similar_text()
*/
double similarity(const string& first, const string& second)
{
string shorter = first, longer = second; // copy to local for use
if(!shorter.length() && !longer.length()) // 2 empties are equal.
return(1.0);
if(!shorter.length() || !longer.length()) // now require both to have length
return(0.0);
if(shorter.length() > longer.length()) // comparisons easier if we know which is which
swap(shorter,longer);
// calculates return value as a percentage of longer, so a short word that "contains" shorter
// is a better match than a longer word that also does. if they're equal, score = 1.0
double denominator = longer.length(); // save now; processing may destroy the string
//----------
// Version 3. NEWER version OF version 1! More restrictive.
// find what chars they share at the exact same locs. no displacements allowed for.
// you don't want "UNfastened" to match "fastened", which version 1 would do.
// e.g. run and ran match 2 chars in order, = 2/3 = .67 match.
uint count = 0; // count of matching chars
for(uint i = 0 ; i < shorter.length() ; i++)
if(shorter[i] == longer[i])
count++;
return(count / denominator);
//----------
#if 0
//----------
// Version 2. Still not convinced I've found a satisfactory one.
// newest version. It destroys the strings as it finds matches.
// it tends to overrate the degree of match.
uint totmatches = 0; // total matching chars found
while(shorter.length() && longer.length())
{
// in each string, delete all chars that don't occur in the other.
// those chars can't possibly match. Do it each loop pass because a char can
// become newly unmatched when one string has more occurrences of it than the other.
size_t u;
while((u = shorter.find_first_not_of(longer)) != NPOS) shorter.remove(u,1);
while((u = longer.find_first_not_of(shorter)) != NPOS) longer.remove(u,1);
// if either was emptied, further matches are impossible
if(!shorter.length() || !longer.length())
break;
if(shorter.length() > longer.length()) // test again
swap(shorter,longer);
uint matches = 0; // new char matches found during this loop pass
// find, tally, and delete any sequential leading matching chars (prefixes)
while(shorter.length() && (shorter[0] == longer[0]))
{
matches++;
shorter.remove(0,1);
longer.remove(0,1);
}
// start from the other end and work backwards.
// This will find matching suffixes in strings of different length.
int i = shorter.length() - 1;
int j = longer.length() - 1;
for( ; shorter.length() && (shorter[i] == longer[j]) ; i--, j--)
{
matches++;
shorter.remove(i,1);
longer.remove(j,1);
}
if(matches) // if new matches were found
totmatches += matches;
else // if no new matches, MUST prevent endless loop
break; // in case strings still have contents.
} //while(shorter.length() && longer.length())
// experimental final test (from below), untested even on paper.
// strings may have substantial internal matches that still haven't been caught.
// (such as when they're displaced by one loc). I don't do this inside loop
// because doing it multiple times looks like it can degenerate into "matching
// chars regardless of order", which must be avoided.
// test yet again
size_t u;
while((u = shorter.find_first_not_of(longer)) != NPOS) shorter.remove(u,1);
while((u = longer.find_first_not_of(shorter)) != NPOS) longer.remove(u,1);
if(shorter.length() > longer.length())
swap(shorter,longer);
uint highcount = 0; // longest sequence of matching chars
uint remaining = shorter.length(); // chars left to search in shorter
for(uint i = 0 ; (i < shorter.length()) && (highcount < remaining) ; i++, remaining--)
{
uint j = longer.find(shorter[i]);
if(j != NPOS) // if first char found, search subsequent chars
{
uint count = 0;
for(uint k = i ; (k < shorter.length()) && (j < longer.length()) ; k++, j++)
if(shorter[k] == longer[j])
count++;
highcount = max(highcount,count);
}
}
totmatches += highcount;
return(totmatches / denominator);
//----------
// version 1. seems to work better than Version 2.
// find what chars they share in the same order.
// Start a new search at EACH position in shorter, and use the highest count found.
// the first nonmatching char does not abort the search. internal nonmatches are allowed.
// #error DOES THIS ACCURATELY DESCRIBE WHAT'S BELOW? NO.
// e.g. run and ran match 2 chars in order, = 2/3 = .67 match.
// You could also factor in a measure of the displacements of the shared chars
// (i.e. they match, but are not at exactly the same locs in the strings), using a method
// similar to that for the Q-set deck, but it doesn't seem that useful or necessary.
uint highcount = 0; // longest (maybe broken) sequence of matching chars
uint remaining = shorter.length(); // chars left to search in shorter
// for speed, if there's not enough string left to search to improve highcount, quit looking.
for(uint i = 0 ; (i < shorter.length()) && (highcount < remaining) ; i++, remaining--)
{
uint j = longer.find(shorter[i]);
if(j != NPOS) // if first char found, search subsequent chars
{
// a test here similar to "remaining", above, is more trouble than it's worth.
uint count = 0;
for(uint k = i ; (k < shorter.length()) && (j < longer.length()) ; k++, j++)
if(shorter[k] == longer[j])
count++;
highcount = max(highcount,count);
}
}
return(highcount / denominator);
//----------
// in oldest version, this was the final step.
// This section is useless, *especially* for strings. Kept only for the method.
// find what percent of the chars in shorter are found in longer, regardless of order.
// this test destroys longer
highcount = 0;
for(i = 0 ; i < shorter.length() ; i++)
{
uint j = longer.find(shorter[i]);
if(j != NPOS) // char was found
{
highcount++; // increment count
longer.remove(j,1); // remove the char so it doesn't get found again
}
}
double anyorder = (double)highcount / (double)shorter.length() * .25;
#endif 0
} //similarity
//----------------------------------------------------------------------------
// returns string with each LF changed to CR. source string is unchanged.
string LFtoCR(const string& s)
{
string t;
for(uint i = 0 ; i < s.length() ; i++)
if(s[i] == '\n')
t += '\r';
else
t += s[i];
return t;
} //LFtoCR
//----------------------------------------------------------------------------
// returns string with each CR changed to LF. source string is unchanged.
// useful for making text from a TEdit suitable for writing to disk in text mode,
// (will prevent writing double-CRs).
// #error (where, if anywhere, do I use this? It would change CRLF to LFLF.)
string CRtoLF(const string& s)
{
string t;
for(uint i = 0 ; i < s.length() ; i++)
if(s[i] == '\r')
t += '\n';
else
t += s[i];
return t;
} //CRtoLF
//----------------------------------------------------------------------------
// returns string with each LF changed to CRLF. source string is unchanged.
// useful for output to modem or to a TEdit. (See mylib.cpp)
string LFtoCRLF(const string& s)
{
string t;
for(uint i = 0 ; (i < s.length()) && (t.length() < NPOS) ; i++)
{
if(s[i] == '\n')
t += '\r'; // insert CR before LF
t += s[i];
}
return t;
} //LFtoCRLF
//----------------------------------------------------------------------------
// returns string with each CRLF changed to LF. source string is unchanged.
string CRLFtoLF(const string& s)
{
string t(s);
size_t u;
while((u = t.find("\r\n")) != NPOS) // find each CRLF
t.remove(u,1); // only remove each CR
return t;
} //CRLFtoLF
//----------------------------------------------------------------------------
// 8/23/01
// These fns I've implemented as string class MEMBERS,
// and I have modified cstring.h so they are declared there.
// To use, you must also declare them in cstring.h (the string class header file).
// Otherwise comment these out.
//----------------------------------------------------------------------------
int _RTLENTRY string::startswith( const string _FAR &s ) const THROW_NONE
{ return(find(s) == 0); }
//----------------------------------------------------------------------------
int _RTLENTRY string::endswith( const string _FAR &s ) const THROW_NONE
{ return( rfind(s) == (length() - s.length()) ); }
//----------------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////////
//----------------------------------------------------------------------------
/* strand.cpp STRing AND 12-2-96
An "AND" operator for strings.
Searches a given string (such as "ABCDE") for a pattern given by another
string (such as "?B??E", where the question marks are "don't care").
The above example would return TRUE, because the target string does
contain a B in the 2nd position, and E in the 5th.
Returns TRUE if pattern matched, FALSE if not.
Used in CLASSIFY.CPP. Another planned use for this is in LIFE.C,
for matching strings that encode the neighboring colors around a pixel.
See paper file A-LIFE in file cabinet.
Briefly tested, seems to work ok.
*/
//----------------------------------------------------------------------------
// string to search, pattern to search for, the dont-care char in pattern.
BOOL strand(const char *searchin, const char *pattern, char dontcare)
{
if(strlen(pattern) > strlen(searchin)) // can't find pattern in a shorter string
return(FALSE);
int slen = strlen(pattern);
for(int i = 0 ; i < slen ; i++)
if(pattern[i] != dontcare)
if(pattern[i] != searchin[i])
return(FALSE);
return(TRUE);
}
//----------------------------------------------------------------------------
// version for strings
BOOL strand(const string& searchin, const string& pattern, char dontcare)
{
if(pattern.length() > searchin.length()) // can't find pattern in a shorter string
return(FALSE);
int slen = pattern.length();
for(int i = 0 ; i < slen ; i++)
if(pattern[i] != dontcare)
if(pattern[i] != searchin[i])
return(FALSE);
return(TRUE);
}
//----------------------------------------------------------------------------
/* gettoken.cpp 5-16-00
Token extraction functions and sentence extraction functions.
A token is a punctuation mark or an alphanumeric string, which can contain
hyphens or apostrophes. This is DIFFERENT from what string::read_token() extracts.
*/
//----------------------------------------------------------------------------
/* get token from a string
this has a minor unfixable bug: if the source string ends with a "-" (such as might
happen when reading lines from a file), it means the token continues on the next line,
but this function must quit looking because the source string is used up.
returns the index of the location in the provided string that is 1 char beyond
all skipped chars and the gotten token (might be beyond end of string, too).
or 0 if no token was created. If it returns 0, it also means there is nothing
left to get. You can use: while(gettoken()){...};
sentence to scan, start, token holder
*/
uint gettoken(const string& s, unsigned startpos, string& buf)
{
buf.remove(0); // must empty each call: it's a reference, and we're using +=
if(startpos >= s.length()) // startpos invalid index
return(0); // means string is used up
int i = (int)startpos; // pointer into s
while(iscntrl(s[i]) || (s[i] == ' ')) // skip over control-chars (incl. nulls) & spaces
if(++i >= s.length()) // string ended
return(0); // before any valid token-starting char found
// got to here means there is a token to be got
if(ispunct(s[i])) // punctuation
{ // identical multiples, like --, allowed. Also gets "" as 1 token
string t = "?!"; // allows !? ?!?!?! ???! etc. as a single token.
char ch = s[i];
while((s[i] == ch) || (t.contains(s[i]) && t.contains(ch)) )
{
buf += s[i];
if(++i >= s.length()) // hit end of string
break;
}
}
else // alphanumeric, and non-leading hyphens, apostrophes all legal
while(isalnum(s[i]) || (s[i] == '-') || (s[i] == '\''))
{
buf += s[i];
if(++i >= s.length()) // hit end of string
break;
}
return(i); // return new search-start loc.
} // end gettoken
//----------------------------------------------------------------------------
// get token from a stream
// returns number of chars in the retrieved token. if 0, there is also nothing more to get.
// You can use: while(gettoken()){...};
uint gettoken(istream& is, string& buf) // stream to read from, token holder
{
buf.remove(0); // must empty each call: it's a reference, and we're using +=
char ch = ' ';
while(iscntrl(ch) || (ch == ' ')) // skip over control-chars (incl. nulls) & spaces
if(!is.get(ch)) // stream used up
return(0); // before valid token-starting char found
// got to here means there is a token to be got
if(ispunct(ch)) // punctuation
{ // identical multiples, like --, allowed)
string t = "?!"; // allows !? ?!?!?! ???! etc. as a single token.
char p;
buf += ch;
// peek() can only return a value that will fit into a char.
// you should decide schar vs. uchar, but probably makes no difference.
while(is.good() && (((p = (char)(is.peek())) == ch) || (t.contains(p) && t.contains(ch))))
{
is.get(ch);
buf += ch;
}
}
else // alphanumeric, and non-leading hyphens, apostrophes all legal
{
buf += ch;
int test = is.peek();
while(is.good() && (isalnum(test) || (test == '-') || (test == '\'')))
{
is.get(ch);
buf += ch;
if(ch == '-') // if the char is a hyphen, extract and discard
is >> ws; // trailing or line-ending whitespace, if any
test = is.peek();
}
}
return(buf.length());
} // gettoken
//----------------------------------------------------------------------------
// returns TRUE if token is a type that terminates a sentence
BOOL isterminator(const string& s)
{
if( (s == ".") || (s == ";") ||
// (s == ":") || // unsure about ":". for now, it's not a term.
s.contains("...") || s.contains("?") || s.contains("!") )
return(TRUE);
return(FALSE);
} //isterminator
//----------------------------------------------------------------------------
/* get a sentence from a string
returns the index of the location in the provided string that is 1 char beyond
all skipped chars and the gotten sentence (might be beyond end of string, too).
or 0 if no sentence was created. 0 also means there is nothing left to get.
You can use: while(getsentence()){...};
text block to scan, starting position, sentence holder
*/
uint getsentence(const string& source, uint startpos, string& sentence)
{
sentence.remove(0); // always empty it at start
if(startpos >= source.length()) // source used up?
return(0);
string token;
while((startpos = gettoken(source,startpos,token)) != 0)
{
sentence += token + " ";
if(isterminator(token)) // end of sentence?
break;
}
if(sentence.length() && !isterminator(token)) // if it has contents, and no terminator,
sentence += "."; // give it one.
sentence = sentence.strip(string::Both);
// if gettoken failed, we might first have gotten a sentence before it did.
// so THIS function shouldn't report failure until the next call.
if(!startpos && sentence.length())
startpos = source.length(); // set startpos so next call fails
return(startpos);
} //getsentence
//----------------------------------------------------------------------------
// extract a sentence from a stream.
// returns number of chars in the retrieved sentence. if 0, there is also nothing more to get.
// You can use: while(getsentence()){...};
uint getsentence(istream& is, string& sentence)
{
sentence.remove(0); // always empty it at start
string token;
while(gettoken(is,token))
{
sentence += token + " ";
if(isterminator(token)) // end of sentence?
break;
}
if(sentence.length() && !isterminator(token)) // if it has contents, and no terminator,
sentence += "."; // give it one.
sentence = sentence.strip(string::Both);
return(sentence.length());
} //getsentence
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
/* tranctrl.cpp 11-5-99
replaces control chars in a string with their corresponding ASCII mnemonics.
CRLF turns into the STRING "<CR><LF>", etc.
returns a string containing the translation; source string s is unchanged.
*/
string TranslateControls(const string& s)
{
static char* ControlText[32] =
{
"<NUL>","<SOH>","<STX>","<ETX>","<EOT>","<ENQ>","<ACK>","<BEL>","<BS>","<HT>",
"<LF>","<VT>","<FF>","<CR>","<SO>","<SI>","<DLE>","<DCl>","<DC2>","<DC3>",
"<DC4>","<NAK>","<SYN>","<ETB>","<CAN>","<EM>","<SUB>","<ESC>","<FS>","<GS>",
"<RS>","<US>"
};
string t;
int ch;
for(int i = 0 ; i < s.length(); i++)
{
ch = s[i] & 0xFF; // mapdown into ASCII range
if(ch < 32)
t += ControlText[ch]; // control code
else
if(ch == 127) // special, DEL is out of normal sequence
t += "<DEL>";
else
t += s[i]; // normal char (retains original parity bit, if set)
}
return(t);
} //TranslateControls
//----------------------------------------------------------------------------
// TTime Y2K bug fix, TDate revisions
//----------------------------------------------------------------------------
// datetime.cpp 4/14/2000
// Fix for Y2K bug in Borland C++ 4.0 class TTime. Overrides library version.
// Could just change the code in time.cpp, but this override avoids recompiling the libraries.
//----------------------------------------------------------------------------
#if defined(__CLASSLIB_TIME_H)
// default constructor: creates a TTime object with the current date and time.
TTime::TTime()
{
//----------------------------------------------------------------------------
// one revised version, which works.
// struct time t;
// gettime(&t);
// *this = TTime(TDate(), t.ti_hour, t.ti_min, t.ti_sec);
//----------------------------------------------------------------------------
// but this version is more like the original. Although I don't use them,
// it makes use of localtime's adjustment features. And is more portable.
time_t ltime;
time(<ime);
struct tm _FAR *t = localtime(<ime);
// the following line uses the TDate() default constructor for today's date, which is Y2K OK.
// the version in time.cpp uses an explicit TDate constructor that is not Y2K OK
// because it relies on Jday(), which is not Y2K OK.
*this = TTime(TDate(),(HourTy)t->tm_hour,(MinuteTy)t->tm_min,(SecondTy)t->tm_sec);
}
// If you revise the TDate operator << below, you should insert HERE
// the entire body of TTime operator << from timeio.cpp:
// ostream _BIDSFAR & _BIDSFUNC operator << (ostream _BIDSFAR & s, const TTime _BIDSFAR & t)
// You must supply it here; otherwise, the existing library version of TTime << will be used,
// and it won't use your mods to TDate.
#endif // (__CLASSLIB_TIME_H)
//----------------------------------------------------------------------------
/* 4/14/00
Minor mod to output of TDate, so numeric fields are always 2 digits wide, zero-filled: 01-01-96
modified from date.cpp.
note the accidental *extra* underline before DATE in next line, to match the .H file's #define.
*/
#if defined(__CLASSLIB__DATE_H)
#if 0 // you cannot enable this until you supply the code below.
ostream _BIDSFAR & _BIDSFUNC operator << (ostream _BIDSFAR & s, const TDate _BIDSFAR & d)
{
// In order to implement this change, insert here the body of this function from date.cpp,
// and revise it by inserting
// << setfill('0') << setw(2)
// before the output of fields you want to make 2-digits.
}
#endif // 0
#endif //(__CLASSLIB__DATE_H)
//----------------------------------------------------------------------------
// trivial rounding function for ints
/* rounding.cpp 2/13/03
rounding function for converting the result of a double calculation to int,
such that "rounding up" always means increasing the magnitude in the correct
direction from zero: positive #s are rounded up, negative #s rounded down.
This might be achievable using floor() or ceil(), but I think I decided it wasn't.
duplicates of this may exist local to some project. also keep a watch for places where I
now round incorrectly or not at all, and could use this instead.
usage: int x = round(speed * sine(30) * x);
*/
int round(double d)
{
if(d > 0) // if positive, make more positive before rounding
d += .5;
else
if(d < 0) // if negative, make more negative before rounding
d -= .5;
return(d); // truncates decimal portion
}
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// WHEREVER MATH.H IS USED
#if defined(__MATH_H)
//----------------------------------------------------------------------------
/* cosine.cpp 7-9-99
Same as cos(), but takes an argument in degrees instead of radians.
*/
double cosine(double degrees)
{
return(cos(degrees * M_PI_180)); // radians = degrees * pi/180
}
//----------------------------------------------------------------------------
/* sine.cpp 7-9-99
Copyright (C)1997 Steven Whitney.
Published under GNU GPL (General Public License) Version 3, with ABSOLUTELY NO WARRANTY.
Same as sin(), but takes an argument in degrees instead of radians.
*/
double sine(double degrees)
{
return(sin(degrees * M_PI_180)); // radians = degrees * pi/180
}
//----------------------------------------------------------------------------
/* trigtabl.cpp 7-9-99
A sine/cosine lookup table for whole degrees.
*/
//----------------------------------------------------------------------------
// constructor
Trigtable::Trigtable()
{
for(int i = 0 ; i < 360 ; i++)
{
cosines[i] = cosine(i);
sines[i] = sine(i);
}
// set some exact entries to avoid rounding errors.
cosines[90] = cosines[270] = sines[0] = sines[180] = 0;
cosines[0] = sines[90] = 1;
cosines[180] = sines[270] = -1;
sines[30] = sines[150] = cosines[60] = cosines[300] = .5;
sines[330] = sines[210] = cosines[120] = cosines[240] = -.5;
}
//----------------------------------------------------------------------------
#endif //(__MATH_H)
//----------------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////////
// DOS OR EASYWIN ONLY (Assumes Windows without OWL must be Easywin)
//----------------------------------------------------------------------------
#if !defined(__OWL_OWLCORE_H)
//----------------------------------------------------------------------------
/* aborts.cpp 10-31-96
prints a specified error message and aborts program execution
"plain" version. see graborts() for version that calls closegraph().
Advantage of this over BC4 abort() is that this will cause destructors
to be called for all objects, while abort() calls NO destructors, not
even for globals (PG:147). Any open fstreams are closed by their destructors.
On the other hand, calling destructors in a crashing program may not be good. Oh well.
*/
void aborts(const string& s)
{
cerr << endl << s << endl;
presskey(); // prevent msg scrolling off before being seen
exit(1);
}
//----------------------------------------------------------------------------
/* presskey.cpp 12-19-96
says press any key to continue, and waits for a key. erases its message
to leave a cleaner screen, but moves to new line anyway so that
if echoed to printer with ^P, subsequent text won't overwrite.
The default os is cout, but can be, for example, a constream.
12-2-99 returns the char you pressed so it can be used.
*/
int presskey(ostream& os)
{
os << "Press any key to continue...";
int ch = getch();
os << "\r " << endl;
return(ch);
}
//----------------------------------------------------------------------------
/* yesno.cpp 12-2-96
Asks user a yes/no question, using either cout or a provided constream.
returns TRUE if the answer is yes, FALSE if no.
prompt is a string, not a string&, so it can have a default value (see my.h)
(Figure out and document somewhere why I decided a reference parm couldn't be given
a default value.)
*/
BOOL yesno(string prompt, ostream& os)
{
int ch;
os << endl;
do
{
os << prompt << " (y/n)? ";
ch = tolower(getche());
os << endl;
}
while((ch != 'y') && (ch != 'n'));
return(ch == 'y');
}
//----------------------------------------------------------------------------
#endif //(__OWL_OWLCORE_H)
//////////////////////////////////////////////////////////////////////////////
// DOS ONLY
//----------------------------------------------------------------------------
#if !defined(_Windows)
//----------------------------------------------------------------------------
/* ci.cpp CONSOLE INPUT 10-19-99
RETURNS A 2 BYTE INT.
use this function instead of getch() to read alt-, function keys, etc.
ASCII CHARS ARE RETURNED AS: 1-255(extended set), WITH HIGH BYTE ZEROED OUT
(You can only create chars above 127 with the alt-numeric keypad trick.)
FUNCTION KEYS RETURNED AS: LOW BYTE ZEROED, HIGH BYTE has unique CODE
enumerated in my.h.
Example: if(ci() == 0x3b00) tests for F1.
NULL CHAR (CONTROL-@) IS RETURNED AS 0X300
"Middle" keypad keys on Gateway enhanced keyboard return same codes as
other function keys, except low byte is 224 instead of 0.
ci() zeroes out the low byte for these keys,
so the duplicate home, ins, del, arrow, etc. keys on both keypads are interpreted
the same, and can't be distinguished from each other (usually what I want).
10-19-99 I changed this so it no longer translates all to lowercase,
and modified the few pgms that use ci().
*/
int ci()
{
unsigned key; // must be unsigned so negative is impossible
unsigned lowbyte;
// bioskey is a Borland DOS-only function.
while(bioskey(_NKEYBRD_READY) == 0); // wait for key, if one not waiting
key = bioskey(_NKEYBRD_READ); // read it
lowbyte = key & 0xff; // this clears high byte
switch(lowbyte)
{
case 224: // "middle" keypad key on enhanced keyboard
key &= 0xff00; // zero out LOW byte: 2 keypads now identical
// (to differentiate, remove the line)
// falls through to next case...
case 0: // function or alt-key
return((int)key); // negative possible? hasn't happened so far.
default: // ASCII char, (low byte has data != 224)
return((int)lowbyte);
// old version converted to lower case automatically, not a good idea.
// If any old pgm doesn't work properly, this is the return value it expected:
// return(tolower((int)lowbyte));
}
} // ci()
#if defined(__GRAPHICS_H)
//----------------------------------------------------------------------------
/* gputs.cpp Graphics-PUTS() 3-12-96
Prints a string to the screen in graphics mode, similar to puts().
To prevent printed text from being obscured by what's already on the screen,
it first clears (in either black or the background color, whichever is
different from the text color) a box large enough to hold the text.
(it's still possible for text to be invisible if color == background == black.)
You should first use ostrstream or sprintf() to format your output into "text".
Also, the (x,y) coordinates are NOT pixels, but text mode (characters,lines), upper left = (1,1).
(There is no test for whether the given (x,y) coordinates will land on the
screen when conversion to pixels takes the font size into consideration.)
Could add: when parameters x == y == 0 (or when negative?), it means "print at bottom"
(or print n lines up from bottom): use different placement calculation to align on bottom
line of screen. (must count up from bottom rather than down from top)
This function leaves the draw color changed, since I usually set the color
before any action, anyway.
Bug: I thought this worked fine in the program it was originally designed for,
but in a later program, the spacing between lines was too small, so line 2
overwrote the lower portion of line 1.
*/
void gputs(int x, int y, int color, char *text)
{
int xloc = (x - 1) * textwidth("W"); // convert to pixels. W looks like the widest letter.
int yloc = (y - 1) * textheight("H"); // it seems to include between-line spacing. H tallest letter.
string s(text); // local buffer so we can modify text
for(int i = 0 ; i < s.length() ; i++) // strip CR and LF, because they're
if((s[i] == '\n') || (s[i] == '\r')) // printable chars in graphics mode
s[i] = ' '; // just turn them into spaces
setfillstyle(SOLID_FILL,(color != BLACK)? BLACK : getbkcolor());
bar(xloc,yloc,textwidth(s.c_str()),yloc + textheight("H") + 2); // clear a text box
setcolor(color); // set draw color for outtext
outtextxy(xloc,yloc,s.c_str());
}
//----------------------------------------------------------------------------
/* getpixel.cpp 12-8-96
Some custom getpixel functions.
You must already be in graphics mode.
*/
//-----------------------------------------------------------------------
// sgetpixel() (SafeGetpixel)
// same as Borland getpixel(), except it returns 0 if (x,y) is offscreen
unsigned far sgetpixel(int x, int y)
{
// more correct but slower
// if((x < 0) || (x > getmaxx()) || (y < 0) || (y > getmaxy()))
// return(0);
// assumes VGAHI
if((x < 0) || (x > 639) || (y < 0) || (y > 479))
return(0);
return(getpixel(x,y));
}
//-----------------------------------------------------------------------
// getpixel() version that takes double arguments.
// Returns the color at the point, or 0 if the point was offscreen.
unsigned far getpixel(double dx, double dy)
{
return(sgetpixel((int)dx,(int)dy));
}
//-----------------------------------------------------------------------
/* graborts.cpp GraphicsABORTS() 10-31-96
prints specified error message and aborts program execution.
Use this version of aborts() in any program using BGI graphics.
*/
void graborts(const string& s)
{
closegraph(); // no mode test: seems to do no harm if already in text mode.
cerr << endl << s << endl;
presskey(); // prevent msg scrolling off before being seen
exit(1);
}
//----------------------------------------------------------------------------
/* initgraf.cpp initialize graphics display 3-12-96
Initializes graphics to the mode I always use (VGA HI-RES),
but could replace void with parameters, including path to driver.
returns graphics mode selected for possible later use by setgraphmode()
*/
int initgraf(void)
{
int grdriver(VGA), grmode(VGAHI), errorcode; // VGAHI = 1 page at 640x480
initgraph(&grdriver,&grmode,"c:\\bc4\\bgi"); // path to driver is system-specific
errorcode = graphresult();
if(errorcode != grOk)
{
cerr << "Graphics System Error " << errorcode << ": " << grapherrormsg(errorcode) << endl;
exit(1);
}
return(grmode);
}
//----------------------------------------------------------------------------
/* picload.cpp 12-18-96
Displays to screen a .PIC format file previously saved with picsave().
see picsave() for the file format.
console must already be in graphics mode before entry
if current screen mode is lower resolution than the file,
only the upper left corner of the image will be loaded.
returns highest color used in file, or EOF on error
*/
int picload(const char *filename)
{
int maxx, maxy;
ifstream infile(filename,ios::binary);
if(!infile)
return(EOF);
infile >> maxx;
infile.ignore(1); // the comma
infile >> maxy;
infile.ignore(1); // the \n
char ch;
int color, highcolor = 0; // highest color seen so far
int xlimit = min(maxx-1,getmaxx()); // use current mode's dimensions
int ylimit = min(maxy-1,getmaxy()); // if smaller than those in the file.
for(int y = 0 ; y <= ylimit ; y++) // ok to stop after last allowable line,
for(int x = 0 ; x < maxx ; x++) // but MUST read all x data for each line
{
if(!infile.get(ch)) // file too short
return(EOF);
if(x <= xlimit) // only process chars that
{ // are within current screen
color = (int)ch;
highcolor = max(highcolor,color);
putpixel(x,y,color);
}
if(kbhit()) // ESC aborts
if(getch() == 27) // (^C causes a batch file to abort)
return(highcolor);
}
return(highcolor);
}
//----------------------------------------------------------------------------
/* picsave.cpp 3-12-96
Saves current graphics screen display to disk file in the format:
ascii strings WIDTH,HEIGHT\N, THEN A SERIES OF (WIDTH*HEIGHT) CHARS:
COLOR AT X1,Y1, COLOR AT X2,Y1, ETC. (X VARIES FASTEST)
you must already be in graphics mode before calling this fn.
to do: figure out how to test for it
returns TRUE if succeed, FALSE if fail
the screen size string is written in binary mode, so what would
ordinarily be a /r/n is only a /n. fscanf() won't read them properly,
but >> will. See picload() for proper way to do it.
The advantage of this is that you can still type the .pic file
to see what screen size it uses.
*/
int picsave(const char *filename) // include the .pic extension when you call it
{
int maxx = getmaxx();
int maxy = getmaxy();
if(getmaxcolor() > 255) // char can only hold 255
return(FALSE);
ofstream outfile(filename,ios::binary);
outfile << (maxx+1) << "," << (maxy+1) << endl; // binary: \n only writes linefeed
for(int y = 0 ; y <= maxy ; y++)
for(int x = 0 ; x <= maxx ; x++)
outfile << (unsigned char)getpixel(x,y);
return(TRUE);
}
//----------------------------------------------------------------------------
// Text names of DOS BGI colors, for reference.
const char* DOSCOLORS[16] =
{
"BLACK", "BLUE", "GREEN", "CYAN", "RED", "MAGENTA", "BROWN", "LTGRAY",
"DKGRAY", "LTBLUE", "LTGREEN", "LTCYAN", "LTRED", "LTMAGENTA",
"YELLOW", "WHITE"
};
#endif // __GRAPHICS_H
#endif // !defined(_Windows)
//////////////////////////////////////////////////////////////////////////////
// WINDOWS ONLY. Can keep source code here, as I have no true Windows library.
// This section for generic functions. Other sections below #include only
// when a particular Windows or OWL header is defined. (And thus also Windows only.)
#if defined(_Windows)
#endif
//----------------------------------------------------------------------------
#if defined(__OWL_EDIT_H)
//----------------------------------------------------------------------------
// accept a string, add it to the end of the TEdit text.
int append(TEdit* ed, const string& s)
{
string t = s;
if(t.length() > 30000) // inserted text itself has a limit length
t.remove(0, t.length() - 30000); // remove earliest chars
t = LFtoCRLF(t);
if(t.length() > 30000) // check again
t.remove(0, t.length() - 30000);
if( ((long)ed->GetTextLen() + t.length()) > 30000L)
ed->DeleteSubText(0,t.length()); // make room for new text
ed->SetSelection(-1,-1); // (uint)(-1) becomes lastpos in TEdit
ed->Insert(t.c_str());
ed->SetSelection(-1,-1);
return(1);
} //append
//----------------------------------------------------------------------------
#endif //(__OWL_EDIT_H)
//----------------------------------------------------------------------------
#if defined(__OWL_GDIOBJEC_H) // TDib declared here
/* This #include used to be automatic, but __OWL_GDIOBJEC_H is usually defined, even when
I don't need SDib, and this includes a lot of code. As you encounter projects
that won't compile without this, add sdib.cpp as a Project Node,
or #include it into library.cpp or other source file for that project.
*/
// #include "c:\bcs\library\sdib.cpp" my extensions to TDib
//----------------------------------------------------------------------------
// Font calculation
int FontHeightInPixels(int pointsize)
{
TScreenDC dc;
return((long)(-pointsize) * (long)(dc.GetDeviceCaps(LOGPIXELSY)) / 72L);
}
#endif // __OWL_GDIOBJEC_H
//----------------------------------------------------------------------------
#if defined(__OWL_CHOOSECO_H)
//----------------------------------------------------------------------------
/* Custom colors palette
This array MUST be defined when using the TChooseColorDialog class.
These colors (all grays) were copied from COMMDLG.IDE for convenience.
Change values in the array as I discover default colors I like better.
Usage:
TChooseColorDialog::TData.CustColors = MyCustomColors;
CustColors is a TColor*. BC4 interprets MyCustomColors as &(MyCustomColors[0])
The values in this array ARE changed when a color is added to the custom colors,
but during any ONE dialog session, colors are added starting at array index 0.
I.e., to add and retain 16 colors, you have to add all 16 in one session.
If this file is included in multiple .cpp files that will be linked together,
this will be defined > once, a link error.
In that case, copy the following declaration to one of the source files,
and comment it out here. OR add this as a node to the project.
For some reason, DOS will not precompile a header containing this
or the DOS color definition above ("initialized data in header").
But OWL will allow both.
*/
TColor MyCustomColors[16] = // array of 16 TColor objects
{ // these are all grays:
0x010101L, 0x101010L, 0x202020L, 0x303030L, // note hex,
0x404040L, 0x505050L, 0x606060L, 0x707070L, // format is:
0x808080L, 0x909090L, 0xA0A0A0L, 0xB0B0B0L, // 0x00BBGGRR
0xC0C0C0L, 0xD0D0D0L, 0xE0E0E0L, 0xF0F0F0L
};
#endif // __OWL_CHOOSECO_H
//----------------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
Copyright ©2010 Steven Whitney. Last modified Thu 10/21/2010 02:08:04 -0700. |
||