529 lines
12 KiB
C
529 lines
12 KiB
C
/*
|
|
* CDE - Common Desktop Environment
|
|
*
|
|
* Copyright (c) 1993-2012, The Open Group. All rights reserved.
|
|
*
|
|
* These libraries and programs are free software; you can
|
|
* redistribute them and/or modify them under the terms of the GNU
|
|
* Lesser General Public License as published by the Free Software
|
|
* Foundation; either version 2 of the License, or (at your option)
|
|
* any later version.
|
|
*
|
|
* These libraries and programs are distributed in the hope that
|
|
* they will be useful, but WITHOUT ANY WARRANTY; without even the
|
|
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
|
* PURPOSE. See the GNU Lesser General Public License for more
|
|
* details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with these libraries and programs; if not, write
|
|
* to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
|
|
* Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
// $TOG: autoNumber.C /main/6 1998/04/17 11:47:13 mgreess $
|
|
|
|
#include <ctype.h>
|
|
#include <limits.h>
|
|
#if defined(CSRG_BASED)
|
|
#define MAXINT INT_MAX
|
|
#else
|
|
#include <values.h>
|
|
#endif
|
|
|
|
#include "HardCopy/autoNumber.h"
|
|
#include "HardCopy/FPExceptions.h"
|
|
|
|
buffer autoNumber::f_buf(128);
|
|
|
|
autoNumber::autoNumber(const char* nm, enum autoNumberType t, int delta, const char* prefix, const char* postfix) :
|
|
f_name(strdup(nm)), f_type(t), f_delta(delta),
|
|
f_prefix(strdup(prefix)), f_postfix(strdup(postfix)),
|
|
f_initialValue(0)
|
|
{
|
|
int x = strlen(prefix) + strlen(postfix) + 12;
|
|
if ( x > f_buf.buf_sz() )
|
|
f_buf.expand_chunk(x);
|
|
}
|
|
|
|
autoNumber::~autoNumber()
|
|
{
|
|
delete f_name;
|
|
delete f_prefix;
|
|
delete f_postfix;
|
|
|
|
while (f_values.entries())
|
|
f_values.pop();
|
|
while (f_serial_nums.entries())
|
|
f_serial_nums.pop();
|
|
}
|
|
|
|
void autoNumber::setNumTagsSeen()
|
|
{
|
|
if (f_serial_nums.entries() > 0 && f_serial_nums.top() < MAXINT)
|
|
f_serial_nums.top()++;
|
|
}
|
|
|
|
void
|
|
autoNumber::reset()
|
|
{
|
|
// reset stack of values
|
|
while (f_values.entries() > 1) // leave one entry for re-use
|
|
f_values.pop();
|
|
f_values.top() = f_initialValue;
|
|
|
|
// reset stack of serial numbers
|
|
while (f_serial_nums.entries() > 1) // leave one entry for re-use
|
|
f_serial_nums.pop();
|
|
f_serial_nums.top() = 0;
|
|
}
|
|
|
|
void
|
|
autoNumber::push()
|
|
{
|
|
f_values.push(f_initialValue);
|
|
f_serial_nums.push(0);
|
|
}
|
|
|
|
void
|
|
autoNumber::pop()
|
|
{
|
|
if (f_values.entries() > 1)
|
|
f_values.pop();
|
|
if (f_serial_nums.entries() > 1)
|
|
f_serial_nums.pop();
|
|
}
|
|
|
|
unsigned int autoNumber::operator==(const autoNumber&)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
ostream& operator<<(ostream& out, const autoNumber& an)
|
|
{
|
|
debug(cerr, an.f_name);
|
|
debug(cerr, an.f_delta);
|
|
debug(cerr, an.f_prefix);
|
|
debug(cerr, an.f_postfix);
|
|
debug(cerr, an.f_type);
|
|
debug(cerr, an.f_serial_nums.top());
|
|
return out;
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
//
|
|
//////////////////////////////////////////////////
|
|
|
|
autoNumberNumeric::autoNumberNumeric(const char* nm, int delta, int inv,
|
|
const char* prefix, const char* postfix) :
|
|
autoNumber(nm, NUMERIC, delta, prefix, postfix)
|
|
{
|
|
f_initialValue = inv;
|
|
|
|
f_values.push(f_initialValue);
|
|
f_serial_nums.push(0);
|
|
}
|
|
|
|
autoNumberNumeric::~autoNumberNumeric()
|
|
{
|
|
}
|
|
|
|
void autoNumberNumeric::setNextValue()
|
|
{
|
|
if (f_serial_nums.entries() && f_values.entries())
|
|
{
|
|
if (f_serial_nums.top() >= 2)
|
|
f_values.top() += f_delta;
|
|
}
|
|
}
|
|
|
|
const char*
|
|
autoNumberNumeric::getValue()
|
|
{
|
|
char* ptr = f_buf.get_base();
|
|
int ptrlen = f_buf.buf_sz();
|
|
|
|
if (f_values.entries())
|
|
snprintf(ptr, ptrlen, "%s",
|
|
form("%s%d%s", f_prefix, f_values.top(), f_postfix));
|
|
else
|
|
*ptr = 0;
|
|
|
|
return ptr;
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
//
|
|
//////////////////////////////////////////////////
|
|
|
|
autoNumberCased::autoNumberCased(const char* nm, autoNumberType an_t,
|
|
int delta, enum CaseType ct, const char* prefix, const char* postfix) :
|
|
autoNumber(nm, an_t, delta, prefix, postfix), f_case(ct)
|
|
{
|
|
}
|
|
|
|
autoNumberCased::~autoNumberCased()
|
|
{
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
//
|
|
//////////////////////////////////////////////////
|
|
|
|
char autoNumberAlphabetic::f_lowerCaseLetters[26] =
|
|
{
|
|
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k',
|
|
'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
|
|
'w', 'x', 'y', 'z'
|
|
};
|
|
|
|
char autoNumberAlphabetic::f_upperCaseLetters[26] =
|
|
{
|
|
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
|
|
'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'T', 'T', 'U', 'V',
|
|
'W', 'X', 'Y', 'Z'
|
|
};
|
|
|
|
autoNumberAlphabetic::autoNumberAlphabetic(
|
|
const char* nm,
|
|
int delta,
|
|
CaseType ct,
|
|
const char* InitialValue, const char* prefix, const char* postfix
|
|
) :
|
|
autoNumberCased(nm, ALPHABETIC, delta, ct, prefix, postfix)
|
|
{
|
|
f_initialValue = alphaToInt(InitialValue, f_case);
|
|
|
|
f_values.push(f_initialValue);
|
|
f_serial_nums.push(0);
|
|
}
|
|
|
|
autoNumberAlphabetic::~autoNumberAlphabetic()
|
|
{
|
|
}
|
|
|
|
static const int base = 26;
|
|
|
|
int autoNumberAlphabetic::alphaToInt(const char* alpha, enum CaseType a_case)
|
|
{
|
|
int digits = strlen(alpha);
|
|
int i;
|
|
int offset = 0;
|
|
|
|
switch ( a_case ) {
|
|
case UPPER:
|
|
for (i=0; i<digits; i++)
|
|
if ( isupper(alpha[i]) == 0 ) {
|
|
MESSAGE(cerr,
|
|
"Initial alphabetic autonumber value is not capitalized");
|
|
throw(CASTHCREXCEPT hardCopyRendererException());
|
|
}
|
|
offset = 'A';
|
|
break;
|
|
case LOWER:
|
|
for (i=0; i<digits; i++)
|
|
if ( islower(alpha[i]) == 0 ) {
|
|
MESSAGE(cerr,
|
|
"Initial alphabetic autonumber value is not in small case");
|
|
throw(CASTHCREXCEPT hardCopyRendererException());
|
|
}
|
|
offset = 'a';
|
|
break;
|
|
}
|
|
|
|
int x = 0;
|
|
int expansionFactor = 1;
|
|
for ( i=digits-1; i>=0; i-- ) {
|
|
x += (alpha[i] - offset)*expansionFactor;
|
|
expansionFactor *= base;
|
|
}
|
|
|
|
x += int((pow((double)base, digits)-1) / (base-1)) - 1;
|
|
|
|
return x;
|
|
}
|
|
|
|
// Algorithm: converting integer values to/from alphabetic autonumbers
|
|
|
|
// The alphabetic autonumbers are grouped into blocks where each
|
|
// block represents autonumbers with same number of digits. The size
|
|
// of a block of d digts = 26^d. Now assigning a sequece number (an integer)
|
|
// to each autonumber in blocks. This number in fact is the integer
|
|
// value (internal) of the autonumber.
|
|
//
|
|
// block 1: [a, b, c, ..., z]
|
|
// seq num: [0, 1, 2, 25]
|
|
//
|
|
// block 2: [aa, ab, ac, ..., zz]
|
|
// seq num: [26, 27, 28, 701]
|
|
//
|
|
// In general, the 1st sequence number in a block for d digits:
|
|
// x = int((pow(26, d)-1) / (26-1)) - 1;
|
|
//
|
|
// given an integer x, its number of ditigs when converted to an autonumber:
|
|
// digits = int(log((26-1)*x + 26) / log(26));
|
|
|
|
|
|
const char* autoNumberAlphabetic::intToAlpha(int x, enum CaseType a_case)
|
|
{
|
|
if ( x < 0 ) {
|
|
MESSAGE(cerr, "Negaitve alphabetic autonumber value");
|
|
throw(CASTHCREXCEPT hardCopyRendererException());
|
|
}
|
|
|
|
int digits = int(log((double)(base-1)*x + base) / log((double)base));
|
|
|
|
if ( digits > 50 ) {
|
|
MESSAGE(cerr, "alphabetic autonumber value too large");
|
|
throw(CASTHCREXCEPT hardCopyRendererException());
|
|
}
|
|
|
|
//debug(cerr, digits);
|
|
//debug(cerr, (pow(base, digits)-1) / (25) -1);
|
|
|
|
x -= int((pow((double)base, digits)-1) / (base-1)) - 1;
|
|
|
|
char* letters =
|
|
(a_case == UPPER ) ? f_upperCaseLetters : f_lowerCaseLetters;
|
|
|
|
int y, z;
|
|
static char buf[51], buf1[51];
|
|
int i =0;
|
|
|
|
while (1) {
|
|
y = x % base;
|
|
z = x / base;
|
|
buf1[i++] = letters[y];
|
|
if ( z == 0 )
|
|
break;
|
|
x = z;
|
|
}
|
|
|
|
int k;
|
|
for (k=0; k<digits-i; k++ )
|
|
buf[k] = letters[0];
|
|
|
|
int n;
|
|
for (n=0; n<i; n++ )
|
|
buf[k+n] = buf1[n];
|
|
|
|
buf[k+n] = 0;
|
|
|
|
//debug(cerr, buf);
|
|
return buf;
|
|
}
|
|
|
|
void autoNumberAlphabetic::setNextValue()
|
|
{
|
|
if (f_serial_nums.entries() && f_values.entries())
|
|
{
|
|
if (f_serial_nums.top() >= 2)
|
|
f_values.top() += f_delta;
|
|
}
|
|
}
|
|
|
|
const char* autoNumberAlphabetic::getValue()
|
|
{
|
|
char* ptr = f_buf.get_base();
|
|
int ptrlen = f_buf.buf_sz();
|
|
|
|
if (f_values.entries())
|
|
snprintf(ptr, ptrlen, "%s", form("%s%s%s", f_prefix,
|
|
intToAlpha(f_values.top(), f_case), f_postfix));
|
|
else
|
|
*ptr = 0;
|
|
|
|
return ptr;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////
|
|
//
|
|
//////////////////////////////////////////////////
|
|
|
|
char autoNumberRoman::RomanNumberBuf[256];
|
|
|
|
autoNumberRoman::autoNumberRoman(
|
|
const char* nm,
|
|
int delta,
|
|
CaseType ct,
|
|
const char* InitialValue, const char* prefix, const char* postfix
|
|
) :
|
|
autoNumberCased(nm, ROMAN, delta, ct, prefix, postfix)
|
|
{
|
|
f_initialValue = RomanToArabic(InitialValue);
|
|
|
|
f_values.push(f_initialValue);
|
|
f_serial_nums.push(0);
|
|
}
|
|
|
|
autoNumberRoman::~autoNumberRoman()
|
|
{
|
|
}
|
|
|
|
void autoNumberRoman::setNextValue()
|
|
{
|
|
if (f_serial_nums.entries() && f_values.entries())
|
|
{
|
|
if (f_serial_nums.top() >= 2) {
|
|
f_values.top() += f_delta;
|
|
|
|
if (f_values.top() < 1) {
|
|
MESSAGE(cerr, "Value too small.");
|
|
throw(CASTHCREXCEPT hardCopyRendererException());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const char* autoNumberRoman::getValue()
|
|
{
|
|
char* ptr;
|
|
|
|
if (f_values.entries())
|
|
return ArabicToRoman(f_values.top());
|
|
else {
|
|
ptr = f_buf.get_base();
|
|
*ptr = 0;
|
|
}
|
|
|
|
return ptr;
|
|
}
|
|
|
|
int autoNumberRoman::getDigit(const char*& p)
|
|
{
|
|
int x = 0;
|
|
switch ( p[0] ) {
|
|
case 'I':
|
|
case 'i':
|
|
if ( p[1] != 0 && p[1] == 'V' ) {
|
|
p++;
|
|
x = 4;
|
|
} else
|
|
x = 1;
|
|
break;
|
|
case 'V':
|
|
case 'v':
|
|
x = 5;
|
|
break;
|
|
case 'X':
|
|
case 'x':
|
|
if ( p[1] != 0 && p[1] == 'I' ) {
|
|
x = 9;
|
|
p++;
|
|
} else
|
|
x = 10;
|
|
break;
|
|
case 'L': //50
|
|
case 'l':
|
|
if ( p[1] != 0 && p[1] == 'X' ) {
|
|
x = 40;
|
|
p++;
|
|
} else
|
|
x = 50;
|
|
break;
|
|
case 'C': // 100
|
|
case 'c':
|
|
if ( p[1] != 0 && p[1] == 'X' ) {
|
|
x = 90;
|
|
p++;
|
|
} else
|
|
x = 100;
|
|
break;
|
|
case 'D': // 500
|
|
case 'd':
|
|
if ( p[1] != 0 && p[1] == 'C' ) {
|
|
x = 400;
|
|
p++;
|
|
} else
|
|
x = 500;
|
|
break;
|
|
case 'M': //1000
|
|
case 'm':
|
|
if ( p[1] != 0 && p[1] == 'C' ) {
|
|
x = 900;
|
|
p++;
|
|
} else
|
|
x = 1000;
|
|
break;
|
|
default:
|
|
MESSAGE(cerr, "unknown roman numeral letter");
|
|
throw(CASTHCREXCEPT hardCopyRendererException());
|
|
}
|
|
p++;
|
|
return x;
|
|
}
|
|
|
|
int autoNumberRoman::RomanToArabic(const char* romanString)
|
|
{
|
|
int x = 0;
|
|
const char* bound = romanString + strlen(romanString);
|
|
const char* p = (char*)romanString;
|
|
|
|
while ( p != bound ) {
|
|
x += getDigit(p);
|
|
}
|
|
return x;
|
|
}
|
|
|
|
const char* romanCardinals[4][9] =
|
|
{
|
|
{ "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX" },
|
|
{ "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC" },
|
|
{ "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM" },
|
|
{ "M", "MM", "MMM", "MV", "V", "VM", "VMM", "VMMM", "MX" }
|
|
};
|
|
|
|
const char*
|
|
autoNumberRoman::ArabicToRoman(int x)
|
|
{
|
|
unsigned int len, slen;
|
|
|
|
RomanNumberBuf[0] = 0;
|
|
if ( x > 3999 ) {
|
|
MESSAGE(cerr, "Value too large.");
|
|
throw(CASTHCREXCEPT hardCopyRendererException());
|
|
}
|
|
|
|
char* buf = form("%d", x);
|
|
|
|
int j=strlen(buf)-1;
|
|
for ( unsigned int i=0; i<strlen(buf); i++ ) {
|
|
if ( buf[i] != '0' )
|
|
{
|
|
const char* romanCardinal = romanCardinals[j][buf[i]-'1'];
|
|
char precise_romanCardinal[8];
|
|
|
|
int k;
|
|
if (f_case == UPPER) {
|
|
for (k=0; romanCardinal[k]; k++)
|
|
precise_romanCardinal[k] = romanCardinal[k];
|
|
precise_romanCardinal[k] = 0;
|
|
}
|
|
else {
|
|
for (k=0; romanCardinal[k]; k++)
|
|
precise_romanCardinal[k] = tolower(romanCardinal[k]);
|
|
precise_romanCardinal[k] = 0;
|
|
}
|
|
|
|
slen = strlen(RomanNumberBuf);
|
|
len = MIN(strlen(precise_romanCardinal), 256 - 1 - slen);
|
|
*((char *) memcpy(RomanNumberBuf + slen,
|
|
precise_romanCardinal, len) + len) = '\0';
|
|
}
|
|
j--;
|
|
}
|
|
|
|
return RomanNumberBuf;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////
|
|
//
|
|
////////////////////////////////////////////////////
|
|
unsigned int
|
|
autoNumberListT::operator==(const autoNumberListT&)
|
|
{
|
|
return false;
|
|
}
|
|
|