OpenIndiana and Solaris port
[oweals/cde.git] / cde / programs / dtinfo / DtMmdb / HardCopy / autoNumber.C
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these librararies and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 // $TOG: autoNumber.C /main/6 1998/04/17 11:47:13 mgreess $
24
25 #include <ctype.h>
26 #include <limits.h>
27 #if defined(CSRG_BASED)
28 #define MAXINT INT_MAX
29 #else
30 #include <values.h>
31 #endif
32
33 #include "HardCopy/autoNumber.h"
34 #include "HardCopy/FPExceptions.h"
35
36 buffer autoNumber::f_buf(128);
37
38 autoNumber::autoNumber(const char* nm, enum autoNumberType t, int delta, const char* prefix, const char* postfix) :
39    f_name(strdup(nm)), f_type(t), f_delta(delta),
40    f_prefix(strdup(prefix)), f_postfix(strdup(postfix)),
41    f_initialValue(0)
42 {
43    int x = strlen(prefix) + strlen(postfix) + 12;
44    if ( x > f_buf.buf_sz() ) 
45       f_buf.expand_chunk(x);
46 }
47
48 autoNumber::~autoNumber()
49 {
50    delete f_name;
51    delete f_prefix;
52    delete f_postfix;
53
54    while (f_values.entries())
55      f_values.pop();
56    while (f_serial_nums.entries())
57      f_serial_nums.pop();
58 }
59
60 void autoNumber::setNumTagsSeen()
61
62   if (f_serial_nums.entries() > 0 && f_serial_nums.top() < MAXINT)
63      f_serial_nums.top()++;
64 }
65
66 void
67 autoNumber::reset() 
68
69    // reset stack of values
70    while (f_values.entries() > 1) // leave one entry for re-use
71      f_values.pop();
72    f_values.top() = f_initialValue;
73
74    // reset stack of serial numbers
75    while (f_serial_nums.entries() > 1) // leave one entry for re-use
76      f_serial_nums.pop();
77    f_serial_nums.top() = 0;
78 }
79
80 void
81 autoNumber::push()
82 {
83     f_values.push(f_initialValue);
84     f_serial_nums.push(0);
85 }
86
87 void
88 autoNumber::pop()
89 {
90     if (f_values.entries() > 1)
91       f_values.pop();
92     if (f_serial_nums.entries() > 1)
93       f_serial_nums.pop();
94 }
95
96 unsigned int autoNumber::operator==(const autoNumber&)
97 {
98    return false;
99 }
100
101 ostream& operator<<(ostream& out, const autoNumber& an)
102 {
103    debug(cerr, an.f_name);
104    debug(cerr, an.f_delta);
105    debug(cerr, an.f_prefix);
106    debug(cerr, an.f_postfix);
107    debug(cerr, an.f_type);
108    debug(cerr, an.f_serial_nums.top());
109    return out;
110 }
111
112 //////////////////////////////////////////////////
113 //
114 //////////////////////////////////////////////////
115
116 autoNumberNumeric::autoNumberNumeric(const char* nm, int delta, int inv,
117                                 const char* prefix, const char* postfix) :
118    autoNumber(nm, NUMERIC, delta, prefix, postfix)
119 {
120    f_initialValue = inv;
121
122    f_values.push(f_initialValue);
123    f_serial_nums.push(0);
124 }
125
126 autoNumberNumeric::~autoNumberNumeric()
127 {
128 }
129
130 void autoNumberNumeric::setNextValue()
131 {
132   if (f_serial_nums.entries() && f_values.entries())
133   {
134     if (f_serial_nums.top() >= 2)
135       f_values.top() += f_delta;
136   }
137 }
138
139 const char*
140 autoNumberNumeric::getValue()
141 {
142    char* ptr = f_buf.get_base();
143    int ptrlen = f_buf.buf_sz();
144
145    if (f_values.entries())
146      snprintf(ptr, ptrlen, "%s",
147                 form("%s%d%s", f_prefix, f_values.top(), f_postfix));
148    else
149      *ptr = 0;
150
151    return ptr;
152 }
153
154 //////////////////////////////////////////////////
155 //
156 //////////////////////////////////////////////////
157
158 autoNumberCased::autoNumberCased(const char* nm, autoNumberType an_t, 
159                                  int delta, enum CaseType ct, const char* prefix, const char* postfix) :
160    autoNumber(nm, an_t, delta, prefix, postfix), f_case(ct)
161 {
162 }
163
164 autoNumberCased::~autoNumberCased()
165 {
166 }
167
168 //////////////////////////////////////////////////
169 //
170 //////////////////////////////////////////////////
171
172 char autoNumberAlphabetic::f_lowerCaseLetters[26] =
173 {
174    'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k',
175    'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
176    'w', 'x', 'y', 'z'
177 };
178
179 char autoNumberAlphabetic::f_upperCaseLetters[26] =
180 {
181    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
182    'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'T', 'T', 'U', 'V',
183    'W', 'X', 'Y', 'Z'
184 };
185
186 autoNumberAlphabetic::autoNumberAlphabetic(
187                 const char* nm, 
188                 int delta,
189                 CaseType ct, 
190                 const char* InitialValue, const char* prefix, const char* postfix
191                                           ) :
192    autoNumberCased(nm, ALPHABETIC, delta, ct, prefix, postfix)
193 {
194    f_initialValue = alphaToInt(InitialValue, f_case);
195
196    f_values.push(f_initialValue);
197    f_serial_nums.push(0);
198 }
199
200 autoNumberAlphabetic::~autoNumberAlphabetic()
201 {
202 }
203
204 static const int base = 26;
205    
206 int autoNumberAlphabetic::alphaToInt(const char* alpha, enum CaseType a_case) 
207 {
208    int digits = strlen(alpha);
209    int i;
210    int offset = 0;
211
212    switch ( a_case ) {
213      case UPPER:
214        for (i=0; i<digits; i++)
215           if ( isupper(alpha[i]) == 0 ) {
216              MESSAGE(cerr, 
217                "Initial alphabetic autonumber value is not capitalized");
218              throw(CASTHCREXCEPT hardCopyRendererException());
219           }
220        offset = 'A';
221        break;
222      case LOWER:
223        for (i=0; i<digits; i++)
224           if ( islower(alpha[i]) == 0 ) {
225              MESSAGE(cerr, 
226                "Initial alphabetic autonumber value is not in small case");
227              throw(CASTHCREXCEPT hardCopyRendererException());
228           }
229        offset = 'a';
230        break;
231    }
232
233    int x = 0;
234    int expansionFactor = 1;
235    for ( i=digits-1; i>=0; i-- ) {
236       x += (alpha[i] - offset)*expansionFactor; 
237       expansionFactor *= base;
238    }
239
240    x += int((pow((double)base, digits)-1) / (base-1)) - 1;
241   
242    return x;
243 }
244
245 // Algorithm: converting integer values to/from alphabetic autonumbers
246
247 // The alphabetic autonumbers are grouped into blocks where each
248 // block represents autonumbers with same number of digits. The size
249 // of a block of d digts = 26^d. Now assigning a sequece number (an integer)
250 // to each autonumber in blocks. This number in fact is the integer
251 // value (internal) of the autonumber.
252 //
253 // block 1: [a, b, c, ..., z]
254 // seq num: [0, 1, 2,      25]
255 //
256 // block 2: [aa, ab, ac, ..., zz]
257 // seq num: [26, 27, 28,      701]
258 //
259 // In general, the 1st sequence number in a block for d digits:
260 //   x = int((pow(26, d)-1) / (26-1)) - 1;
261 //
262 // given an integer x, its number of ditigs when converted to an autonumber: 
263 //   digits = int(log((26-1)*x + 26) / log(26));
264
265
266 const char* autoNumberAlphabetic::intToAlpha(int x, enum CaseType a_case) 
267 {
268    if ( x < 0 ) {
269      MESSAGE(cerr, "Negaitve alphabetic autonumber value");
270      throw(CASTHCREXCEPT hardCopyRendererException());
271    }
272
273    int digits = int(log((double)(base-1)*x + base) / log((double)base));
274
275    if ( digits > 50 ) {
276      MESSAGE(cerr, "alphabetic autonumber value too large");
277      throw(CASTHCREXCEPT hardCopyRendererException());
278    }
279
280    //debug(cerr, digits);   
281    //debug(cerr, (pow(base, digits)-1) / (25) -1);
282
283    x -= int((pow((double)base, digits)-1) / (base-1)) - 1;
284
285    char* letters = 
286         (a_case == UPPER ) ? f_upperCaseLetters : f_lowerCaseLetters; 
287
288    int y, z;
289    static char buf[51], buf1[51];
290    int i =0;
291
292    while (1) {
293       y = x % base;
294       z = x / base;
295       buf1[i++] = letters[y];
296       if ( z == 0 )
297          break;
298       x = z;
299    }
300
301    int k;
302    for (k=0; k<digits-i; k++ )
303       buf[k] = letters[0];
304
305    int n;
306    for (n=0; n<i; n++ )
307       buf[k+n] = buf1[n];
308
309    buf[k+n] = 0;
310
311    //debug(cerr, buf);
312    return buf;
313 }
314
315 void autoNumberAlphabetic::setNextValue()
316 {
317   if (f_serial_nums.entries() && f_values.entries())
318   {
319     if (f_serial_nums.top() >= 2)
320       f_values.top() += f_delta;
321   }
322 }
323
324 const char* autoNumberAlphabetic::getValue()
325 {
326    char* ptr = f_buf.get_base();
327    int ptrlen = f_buf.buf_sz();
328
329    if (f_values.entries())
330      snprintf(ptr, ptrlen, "%s", form("%s%s%s", f_prefix,
331                      intToAlpha(f_values.top(), f_case), f_postfix));
332    else
333      *ptr = 0;
334
335    return ptr;
336 }
337
338
339 //////////////////////////////////////////////////
340 //
341 //////////////////////////////////////////////////
342
343 char autoNumberRoman::RomanNumberBuf[256]; 
344
345 autoNumberRoman::autoNumberRoman(
346                 const char* nm, 
347                 int delta,
348                 CaseType ct, 
349                 const char* InitialValue, const char* prefix, const char* postfix
350                                 ) :
351    autoNumberCased(nm, ROMAN, delta, ct, prefix, postfix)
352 {
353    f_initialValue = RomanToArabic(InitialValue);
354
355    f_values.push(f_initialValue);
356    f_serial_nums.push(0);
357 }
358
359 autoNumberRoman::~autoNumberRoman()
360 {
361 }
362
363 void autoNumberRoman::setNextValue() 
364 {
365   if (f_serial_nums.entries() && f_values.entries())
366   {
367     if (f_serial_nums.top() >= 2) {
368       f_values.top() += f_delta;
369
370       if (f_values.top() < 1) {
371         MESSAGE(cerr, "Value too small.");
372         throw(CASTHCREXCEPT hardCopyRendererException());
373       }
374     }
375   }
376 }
377
378 const char* autoNumberRoman::getValue() 
379 {
380    char* ptr;
381
382    if (f_values.entries())
383      return ArabicToRoman(f_values.top());
384    else {
385      ptr = f_buf.get_base();
386     *ptr = 0;
387    }
388
389    return ptr;
390 }
391
392 int autoNumberRoman::getDigit(const char*& p)
393 {
394    int x = 0;
395    switch ( p[0] ) {
396         case 'I':
397         case 'i':
398            if ( p[1] != 0 && p[1] == 'V' ) {
399               p++;
400               x = 4;
401            } else
402               x = 1;
403            break;
404         case 'V':
405         case 'v':
406            x = 5;
407            break;
408         case 'X':
409         case 'x':
410            if ( p[1] != 0 && p[1] == 'I' ) {
411               x = 9;
412               p++;
413            } else
414               x = 10;
415            break;
416         case 'L': //50
417         case 'l':
418            if ( p[1] != 0 && p[1] == 'X' ) {
419               x = 40;
420               p++;
421            } else
422               x = 50;
423            break;
424         case 'C': // 100
425         case 'c':
426            if ( p[1] != 0 && p[1] == 'X' ) {
427               x = 90;
428               p++;
429            } else
430               x = 100;
431            break;
432         case 'D': // 500
433         case 'd':
434            if ( p[1] != 0 && p[1] == 'C' ) {
435               x = 400;
436               p++;
437            } else
438               x = 500;
439            break;
440         case 'M': //1000
441         case 'm':
442            if ( p[1] != 0 && p[1] == 'C' ) {
443               x = 900;
444               p++;
445            } else
446               x = 1000;
447            break;
448         default:
449            MESSAGE(cerr, "unknown roman numeral letter");
450            throw(CASTHCREXCEPT hardCopyRendererException());
451    }
452    p++;
453    return x;
454 }
455
456 int autoNumberRoman::RomanToArabic(const char* romanString)
457 {
458    int x = 0;
459    const char* bound = romanString + strlen(romanString);
460    const char* p = (char*)romanString;
461
462    while ( p != bound ) {
463       x += getDigit(p);
464    }
465    return x;
466 }
467
468 const char* romanCardinals[4][9] = 
469 {
470    { "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX" },
471    { "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC" },
472    { "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM" },
473    { "M", "MM", "MMM", "MV", "V", "VM", "VMM", "VMMM", "MX" }
474 };
475
476 const char* 
477 autoNumberRoman::ArabicToRoman(int x)
478 {
479    unsigned int len, slen;
480
481    RomanNumberBuf[0] = 0;
482    if ( x > 3999 ) {
483       MESSAGE(cerr, "Value too large.");
484       throw(CASTHCREXCEPT hardCopyRendererException());
485    }
486
487    char* buf = form("%d", x);
488
489    int j=strlen(buf)-1;
490    for ( unsigned int i=0; i<strlen(buf); i++ ) {
491       if ( buf[i] != '0' )
492       {
493          const char* romanCardinal = romanCardinals[j][buf[i]-'1'];
494          char  precise_romanCardinal[8];
495
496          int k;
497          if (f_case == UPPER) {
498             for (k=0; romanCardinal[k]; k++)
499                 precise_romanCardinal[k] = romanCardinal[k];
500             precise_romanCardinal[k] = 0;
501          }
502          else {
503             for (k=0; romanCardinal[k]; k++)
504                 precise_romanCardinal[k] = tolower(romanCardinal[k]);
505             precise_romanCardinal[k] = 0;
506          }
507
508          slen = strlen(RomanNumberBuf);
509          len = MIN(strlen(precise_romanCardinal), 256 - 1 - slen);
510          *((char *) memcpy(RomanNumberBuf + slen,
511                            precise_romanCardinal, len) + len) = '\0';
512       }
513       j--;
514    }
515
516    return RomanNumberBuf;
517 }
518
519
520 ////////////////////////////////////////////////////
521 //
522 ////////////////////////////////////////////////////
523 unsigned int
524 autoNumberListT::operator==(const autoNumberListT&)
525 {
526    return false;
527 }
528