disable the unrecognized nls flag
[AROS-Contrib.git] / regina / convert.c
blob648e6a933924fbafa3d6bc16a9c04044bc1a26ab
1 /*
2 * The Regina Rexx Interpreter
3 * Copyright (C) 1992-1994 Anders Christensen <anders@pvv.unit.no>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the Free
17 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 * The functions in this file converts between the different
22 * 'datatypes' that REXX uses, such as decimal number, character
23 * string and hex string. Some of the functions are meant for the
24 * standard library, like c2x, x2c, d2x, x2d, c2d and d2c, and also
25 * the new functions in language level 4.00: b2x and x2b. The routines
26 * in the file might also be used elsewhere for conversion between
27 * char, decimal and hex.
29 * In general, there are two levels, the interface for the functions
30 * in the builtin library (std_c2x, std_c2d, etc) and the low-level
31 * routines that do the actual job. The interface routines 'only' call
32 * the low-level routines in the right order, to preprocess parameters
33 * and to postprocess the result. However, this implementation is not
34 * 100% clean in this respect, since std_b2x() and std_x2b() are
35 * selfcontained.
37 * When converting to or from decimal numbers, the decimal number will
38 * trigger an overflow condition if more bits are pushed into the
39 * number than the is allowed by the current setting of NUMERIC
40 * DIGITS.
42 * The low-level routines are:
44 * pack_hex() --- packs a hex string into a char string
45 * unpack_hex() --- unpacks a char string into a hex string.
46 * numberize() --- converts a char string to a whole number
48 * In addition, low level routine that operates on whole numbers are
49 * placed in strmath.c. The relevant routines there which are called
50 * from this file are (numberize() is only a frontend to the function
51 * str_digitize()):
53 * str_binerize() --- converts a whole number into a char string
54 * str_digitize() --- converts a char string into a whole number
56 * The high-level routines in this file are:
58 * std_b2x() --- converts bin string to hex string
59 * std_c2d() --- converts char string to whole number
60 * std_c2x() --- converts char string to hex string
61 * std_d2c() --- converts whole number to char string
62 * std_d2x() --- converts whole number to hex string
63 * std_x2b() --- converts hex string to bin string
64 * std_x2c() --- converts hex string to char string
65 * std_x2d() --- converts hex string to whole number
68 * There are four 'generic' dataformats used in this implementation:
69 * bin string, hex string, char string and whole number.
71 * * CHAR STRING consists of chars (any of the 256, including 0). It
72 * can have any length (including 0). This is the 'normal' strings
73 * in Rexx, but note that in the context of this module, char strings
74 * will often contain unprintable characters, while in normal use
75 * char strings will often just contain printable characters.
76 * To emphsize the difference, it will sometimes be refered to as
77 * packed char string.
79 * * BIN STRING consists of the binary digits 0 and 1, and will be
80 * implicitly padded out to a nibble (4 bit) boundary. They may have
81 * any length, including 0. Optional spaces might be added between
82 * nibble boundaries, but not at the start or end of the string. The
83 * first group of bin digits may have any number of digits, and will
84 * be padded with leading zeros at the left to make it a multiple
85 * of four digits.
87 * * HEX STRING consists of the hex digits 0-9 and A-F (in upper or
88 * lower case). It may have any length (including 0). Optional
89 * spaces might be added between pairs of digits to form groups of
90 * hex digits, but not at the start or end. If the first group does
91 * not contain an even number of hex digits, it is padded to the
92 * left with an zero.
94 * * WHOLE NUMBER must be a string which is a valid Rexx whole number,
95 * which is a subset of Rexx numbers. If it has a decimal part, that
96 * must be zero (i.e. 13.0 is a whole number). In addition, it must
97 * have a precition which is sufficient high to identify the number
98 * as a particular integer (i.e. 13E1 is not a whole number, since
99 * it really is 130 plus/minus 5).
101 * All these, except whole numbers, can have zero length, in which
102 * case they are considered the nullstring. When generating these
103 * datatype for output, they will be normalized, which means:
105 * * For whole numbers, bin and hex string, no additional space is
106 * added; neither internal, leading nor trailing.
108 * * Whole numbers will not be in exponential form, and the decimal
109 * part of the number (if any) is truncated.
111 * * Hex string will use upper case alphanumberic characters in stead
112 * of lower case. Bin string will be padded with zeros at the left
113 * to a multiple of four.
117 #include "rexx.h"
118 #include <string.h>
119 #include <stdio.h>
120 #include <assert.h>
124 * The following string is used to convert a nibble value
125 * into its hexadecimal character set representation. Perhaps this
126 * should be placed somewhere else, e.g. in misc.c
128 static const char hexnum[] = "0123456789ABCDEF" ;
132 /* ---------------------------------------------------------------
133 * Takes a bin string as input, and returns a normalized hex
134 * string representing the same data. The output will have an even
135 * number of digits.
137 * Since we are reading byte values in the range 0-255, we must take
138 * care to use unsigned chars, else we might get some surprises.
139 * Therefore there are some casting in this routine.
141 static streng *unpack_hex( const tsd_t *TSD, const streng *string )
143 streng *result=NULL ; /* the output string */
144 char *res_ptr=NULL ; /* ptr to current char in output string */
145 unsigned char *ptr=NULL ; /* ptr to input string */
146 unsigned char *end_ptr=NULL ; /* ptr to end+1 of input string */
149 * Allocate the needed space, which is very easy to calculate
151 result = Str_makeTSD( Str_len( string )*2 ) ;
152 res_ptr = result->value ;
155 * Initialize loop ptr and the end ptr, and loop through each
156 * character in the input string. Write two output hex digits
157 * for each char in the input string
159 end_ptr = (unsigned char *)(Str_end(string)) ;
160 ptr = (unsigned char *)(string->value) ;
161 for (; ptr<end_ptr; ptr++ )
163 *res_ptr++ = hexnum[ (*ptr>>4) ] ;
164 *res_ptr++ = hexnum[ (*ptr & 0x0f) ] ;
168 * That's it, set the length and return to caller
170 result->len = ( res_ptr - result->value ) ;
171 assert( result->len <= result->max ) ;
172 return result ;
177 /* -----------------------------------------------------------------
178 * Input is a hex string, which is converted to a char string
179 * representing the same information and returned.
181 * We have to concider the optional grouping of hex digits by spaces at
182 * byte boundaries, and the possibility of having to pad first group
183 * with a zero.
185 * There is one performance problem with this. If the hex string is
186 * normalized and long, we have to loop through the string twice, while
187 * once would suffice. To determine whether or not to pad first group
188 * with a zero, all of first group must be scanned, which is identical
189 * to the whole string if it is normalized.
191 static streng *pack_hex( tsd_t *TSD, const char *bif, const streng *string )
193 streng *result=NULL ; /* output char string */
194 const char *ptr=NULL ; /* current digit in input hex string */
195 const char *end_ptr=NULL ; /* ptr to end+1 in input hex string */
196 char *res_ptr=NULL ; /* ptr to current char in output string */
197 int byte_boundary=0 ; /* boolean, are we at at byte bounary? */
198 int count; /* used to count positions */
201 * Allow one extra char for padding, ignore that allocated string
202 * might be too long if there is spacing at byte boundaries.
204 result = Str_makeTSD( (Str_len(string)+1)/2 +1 ) ;
205 res_ptr = result->value ;
208 * Initiate pointers to current char in intput string, and to
209 * end+1 in input string.
211 ptr = string->value ;
212 end_ptr = Str_end(string) ;
215 * Explicitly check for space at start or end. Illegal space within
216 * the hex string is checked for during the loop.
218 if ((ptr<end_ptr) && ((rx_isspace(*ptr)) || (rx_isspace(*(end_ptr-1)))))
220 goto invalid;
224 * Find the number of hex digits in the first group of hex digits.
225 * Let the variable 'byte_boundary' be a boolean, indicating if
226 * current char might be a byte boundary. I.e if byte_boundary is
227 * set, spaces are legal.
229 * Also, set the first byte in the output string. That is not
230 * necessary if the first group of hex digits has an even number of
231 * digits, but it is cheaper to do it always that check for it.
233 for (; (ptr<end_ptr) && (rx_isxdigit(*ptr)); ptr++ ) ;
234 byte_boundary = !((ptr-string->value)%2) ;
236 /* Does this statement do anything useful? (Proabably, things crash if
237 I remove it ...
239 *res_ptr = 0x00 ;
242 * Loop through the elements of the input string. Skip over spaces.
243 * Stuff hex digits into the output string, and report error
244 * for any other type of data.
246 for (count=1,ptr=string->value; ptr<end_ptr; ptr++, count++)
248 if (rx_isspace(*ptr))
251 * Just make sure that this space occurs at a byte boundary,
252 * except from that, ignore it.
254 if (!byte_boundary)
256 goto invalid;
259 else if (rx_isxdigit(*ptr))
262 * Stuff it into the output array, either as upper or lower
263 * part of a byte, depending on the value of 'byte_boundary'.
264 * Then toggle the value of 'byte_boundary'.
266 if (byte_boundary)
267 *res_ptr = (char)( HEXVAL(*ptr) << 4 ) ;
268 else
270 /* Damn'ed MSVC: */
271 *res_ptr = (char) (*res_ptr + (char) (HEXVAL(*ptr))) ;
272 res_ptr++;
275 byte_boundary = !byte_boundary ;
277 else
279 goto invalid;
284 * Set the length and do 'redundant' check for problems. In
285 * particular, check 'byte_boundary' to verify that the last group
286 * of hex digits ended at a byte boundary; report error if not.
288 if (!byte_boundary)
290 goto invalid;
293 result->len = res_ptr - result->value ;
294 assert( result->len <= result->max ) ;
296 return result ;
298 invalid:
299 Free_stringTSD( result );
300 exiterror( ERR_INCORRECT_CALL, 25, bif, tmpstr_of( TSD, string ) );
301 return NULL; /* not reached */
306 /* ------------------------------------------------------------------
307 * Takes a char string input and concerts it into a whole number of
308 * base 10, which can be signed. Actually, the real work is done in
309 * str_digitize().
311 * If length is -1 (i.e. unspecified) it will be interpreted as an
312 * unsigned integer.
314 * If length is specified, the input string will be interpreted as an
315 * two's complement number having that length. If parameter 'length'
316 * is bigger than the length of parameter string, 'string' is
317 * logically extended with '0' at the left. If parameter length is
318 * smaller than the length of parameter string, only the rightmost
319 * characters of string is significant.
321 * The output string will not contain any leading zeros (unless the
322 * value of the number is zero, in which case '0' is returned). A '0'
323 * will also be returned if 'length' is zero, or if 'string' is the
324 * nullstring.
326 static streng *numerize( tsd_t *TSD, streng *string, int length,
327 const char *bif, int removeStringOnError )
329 int start=0 ; /* character to start reading at */
330 int sign=0 ; /* is this to be interpreted as signed? */
332 /* The trivial case, either the nullstring, or length=0 */
333 if ((length==0) || (Str_len(string)==0))
334 return int_to_streng( TSD, 0 ) ;
337 * Set the variable 'start' to the most significant byte in 'string'.
338 * That is the first byte if 'length' is either unspecified or
339 * bigger than (or equal to) the length of 'string'.
341 * If 'length' is specified and is less than the length of 'string',
342 * then set 'start' to the the 'length'th byte, counted backward.
344 if ((length==-1) || (length>Str_len(string)))
345 start = sign = 0 ;
346 else
348 assert((length>0) && (length<=Str_len(string))) ;
349 start = Str_len(string) - length ;
350 sign = 1 ;
351 assert((start>=0) && (start<Str_len(string))) ;
355 * Call the correct routine in the string module. The number will
356 * always be signed if length is specified.
358 return str_digitize( TSD, string, start, sign, bif, removeStringOnError ) ;
363 /* ------------------------------------------------------------------
364 * Converts a hex string to a decimal number using two's complement.
365 * This is a high level routine, which just calls the above low level
366 * routines to do the job. First the hex string is converted into
367 * a char string, and then the char string is converted into a whole
368 * number.
370 streng *std_x2d( tsd_t *TSD, cparamboxptr parms )
372 int length=0 ; /* the length of the input hex string */
373 streng *result=NULL ; /* the output string */
374 streng *packed=NULL ; /* tmp variable holding the char string */
377 * First read the parameters, and set length to -1 (meaning
378 * unspecified) if the second parameter was not specified.
380 checkparam( parms, 1, 2 , "X2D" ) ;
381 if ((parms->next)&&(parms->next->value))
382 length = atozpos( TSD, parms->next->value, "X2D", 2 ) ;
383 else
384 length = (-1) ;
387 * Convert the hex string into a whole number in two steps
389 * Note that the 'length' variable is the length in hex digits, and
390 * that numerize receives its input as a char string, so we have to
391 * convert the 'length' a bit. Also, that means that we have to
392 * sign extend the number at the left to a byte boundary.
394 packed = pack_hex( TSD, "X2D", parms->value ) ;
395 if ((length>0) && (length%2))
398 * The char string was padded with an extra zero nibble in pack_hex()
399 * so we must signextend that nibble. 'msb' is a tmp variable
400 * that points to the most significant byte in packed. Hmmm this
401 * is a kludge ...
403 int msb = Str_len(packed)-(length/2)-1 ;
404 if (msb >= 0) /* only if length <= hexchars supplied */
406 if (packed->value[msb] & 0x08)
407 packed->value[msb] |= 0xf0 ;
408 else
409 packed->value[msb] &= 0x0f ;
412 result = numerize( TSD,
413 packed,
414 ((length!=-1) ? ((length+1)/2) : -1),
415 "X2D",
416 1 ) ;
419 * Clean up and return to caller
421 Free_stringTSD( packed ) ;
423 return result ;
428 /* ------------------------------------------------------------------
429 * Converts a char string to a hex string. This a just a box around
430 * one of the low level routines, that processes the parameters.
432 streng *std_x2c( tsd_t *TSD, cparamboxptr parms )
434 checkparam( parms, 1, 1 , "X2C" ) ;
435 return pack_hex( TSD, "X2C", parms->value ) ;
440 /* ------------------------------------------------------------------
441 * Builtin function that converts a binary string to a normalized
442 * hexstring. The hexstring will be padded to *nibble* boundary,
443 * (note: not byte boundary).
445 * This function should have been implemented as a convertion from
446 * bin string to packed binary string, and then a call to unpack
447 * the binary string to a hex string. Unfortunately, accuracy would
448 * be lost, since packed binary strings are padded to byte boundary.
450 * Therefore, this function converts directly into a hex string. If
451 * more functions are added to Rexx (like b2d() and b2c()) in the
452 * future, it might be more effective to create a nibble format (or
453 * change the packed binary string to nibble array), in order to
454 * decrease the number of lines of code.
456 * The code for this is mostly taken from lexsrc.l, these two pieces
457 * should perhaps be tuned to use the same function.
460 streng *std_b2x( tsd_t *TSD, cparamboxptr parms )
462 char *ptr=NULL ; /* loop variable */
463 char *endptr=NULL ; /* pointer to end+1 of input string */
464 char *res_ptr=NULL ; /* pointer to result string */
465 streng *result=NULL ; /* result string */
466 streng *string=NULL ; /* tmp variable to avoid pointer chasing */
467 int first_group=0 ; /* number of bin digits in first bin group */
468 int cur_bit=0 ; /* current bit in input string */
469 int nibble=0 ; /* collects bin digits to nibbles */
472 * Have we been called correctly?
474 checkparam( parms, 1, 1 , "B2X" ) ;
475 string = parms->value ;
478 * Since a bin string can have any number of digits in its first
479 * group, we have to find the number of digits, in order to be able
480 * to pad that group with leading zeros. The number can also be
481 * zero (i.e. the bin string is empty.) The number first_group
482 * contains the number of binary digits in the first group.
484 endptr = Str_end(string) ;
485 ptr = string->value ;
486 for (; (ptr<endptr) && (((*ptr)=='0') || ((*ptr)=='1')); ptr++ ) ;
487 first_group = ptr - string->value ;
490 * If the first group contained zero binary digits, then either does
491 * it contain leading space, or it is the nullstring. The former is
492 * an error, so report it if that is the case.
494 if (Str_len(string) && ((first_group==0) || (rx_isspace(*(endptr-1)))))
496 /* fixes 1107969 */
497 exiterror( ERR_INCORRECT_CALL, 24, "B2X", tmpstr_of( TSD, string ) );
501 * If the string is a proper bin string, we need one hex digit for
502 * each fourth bin digit (after having taken into account that the
503 * first group might have a maximum of three implied zeros.) If it
504 * is not a proper bin string, the error will be caught later.
506 * Actually, this might be more than we need, since there might be
507 * spaces embedded within the bin string, but don't bother about
508 * that since it just takes CPU time to figure out exactly how many
509 * bytes we need. Just allocate enough.
511 result = Str_makeTSD( (Str_len(string)+3)/4 ) ;
512 res_ptr = result->value ;
515 * Initialize some variables, 'cur_bit' is the current bit within
516 * the current nibble. It must be initialized to the number of
517 * implied leading zeros in the bin string. The following
518 * transformation from 'frist_group%4' to 'cur_bit' has the following
519 * mapping {0,1,2,3}->{0,3,2,1}.
521 cur_bit = (4 - first_group%4)%4 ;
522 nibble = 0 ;
525 * Then, loop for each character in the input string, and perform
526 * some action, based on whether it is a space or a binary digit.
527 * If it is neither, report an error.
529 for (ptr=string->value; ptr<endptr; ptr++)
531 if (rx_isspace(*ptr))
534 * The variable 'cur_bit' is a number containing the relative
535 * position of the current bit within the current group of
536 * binary digits. After reading a complete nibble, it reaches
537 * 4, and is reset to 0. So if this is a nibble boundary, it
538 * better be 0, or else there is space within a nibble.
540 if (cur_bit!=0)
542 Free_stringTSD( result );
543 exiterror( ERR_INCORRECT_CALL, 24, "B2X", tmpstr_of( TSD, string ) );
547 else if (((*ptr)=='0')||((*ptr)=='1'))
550 * If it is a binary digit, shift 'nibble' and add the digit.
551 * If 'cur_bit' (after being incremented) shows 4 (i.e we have
552 * completed processing a nibble), reset it to 0, and and
553 * flush 'nibble' to the result string and reset 'nibble' too.
555 nibble = nibble * 2 + ((*ptr)-'0') ;
556 if ((++cur_bit)==4)
558 *(res_ptr++) = hexnum[nibble] ;
559 nibble = 0 ;
560 cur_bit = 0 ;
563 else
565 exiterror( ERR_INCORRECT_CALL, 24, "B2X", tmpstr_of( TSD, string ) );
570 * Wow, we're finished, we just have to set the length of 'result'
572 result->len = (res_ptr - result->value ) ;
573 assert( result->len <= result->max ) ;
574 return( result ) ;
580 /* ------------------------------------------------------------------
581 * Function that converts a hex string to a binary string. The hex
582 * string may have spaces at byte boundaries as usual, but the first
583 * group of hex digits may have an odd number of digits, in which
584 * case it is *not* padded with an zero hex digit.
586 * Just like std_b2x(), this function should really use a common data
587 * format, but it does not matter before more rexx gets more functions
588 * that converts to/from bin strings.
591 streng *std_x2b( tsd_t *TSD, cparamboxptr parms )
593 int space_stat=0 ; /* state machine: nibble or byte boundary? */
594 char *ptr=NULL ; /* loop control variable */
595 char *end_ptr=NULL ; /* points to end+1 of input, endcondition in loop */
596 streng *result=NULL ; /* the output streng */
597 char *res_ptr=NULL ; /* ptr to contents of 'result' */
598 int nibble=0 ; /* holds a nibble while extracting bin digits */
599 int count=0 ; /* loop control variable */
600 int pos=0 ; /* position in string for error reporting */
603 * Check that we got the parameters that we needed. Then initialize
604 * some of the variables.
606 checkparam( parms, 1, 1 ,"X2B" ) ;
607 ptr = parms->value->value ;
608 end_ptr = Str_end( parms->value ) ;
609 space_stat = 0 ;
612 * Let us allocate enough space, we could tune some space here
613 * if we actually checked how many hex digits that the input
614 * string contiained.
616 result = Str_makeTSD( (end_ptr-ptr) * 4 ) ;
617 res_ptr = result->value ;
620 * Check for leading or trailing space in the hex string.
622 if (end_ptr>ptr)
624 if (rx_isspace(*ptr) || rx_isspace(*(end_ptr-1)))
626 goto invalid;
631 * The main loop. For each hex digit, output four bin digits to
632 * the output string, and check for illegal spaces within bytes.
633 * If anything other than spaces or hex digits are found, report
634 * an error.
636 for (pos=1; ptr<end_ptr; ptr++,pos++)
638 if (rx_isspace(*ptr))
641 * We have found space in the hex string, eat it up, and keep
642 * the statemachine 'space_state' going. If state is 0 (end
643 * of first group) go to state 2 (at byte boundary). If state
644 * is 1 (inside a byte) report an error, since space may not
645 * occur there.
647 if (space_stat==0)
649 space_stat = 2 ;
651 else if (space_stat==1)
653 goto invalid;
656 else if (rx_isxdigit(*ptr))
659 * We have found a hex digit, chop it into four parts, and
660 * stuff them into 'result'. Requires that the character set
661 * value of '1' is one higher than that of '0'.
663 nibble = HEXVAL( *ptr ) ;
664 for (count=0; count<4; count++)
666 *(res_ptr++) = (char)(( (nibble & 0x08) != 0 ) + '0') ;
667 nibble <<= 1 ;
671 * Remember to toggle the statemachine between states 1 and 2
672 * so we can keep track of byte and nibble boundaries. If in
673 * state 0 (whitin first group), stay there.
675 if (space_stat)
676 space_stat = ((space_stat==1) ? 2 : 1) ;
678 else
680 goto invalid;
685 * Set the length, and get out of here.
687 result->len = res_ptr - result->value ;
688 return result ;
690 invalid:
691 Free_stringTSD( result );
692 exiterror( ERR_INCORRECT_CALL, 25, "X2B", tmpstr_of( TSD, parms->value ) );
693 return NULL; /* not reached */
699 /* --------------------------------------------------------------------
700 * Converts a char string to a decimal string. Really just a box around
701 * numerize, that only preprocesses the parameters.
703 streng *std_c2d( tsd_t *TSD, cparamboxptr parms )
705 int length ; /* The length of the input char string */
707 checkparam( parms, 1, 2 , "C2D" ) ;
708 if ((parms->next)&&(parms->next->value))
709 length = atozpos( TSD, parms->next->value, "C2D", 2 ) ;
710 else
711 length = -1 ;
713 return numerize( TSD, parms->value, length, "C2D", 0 ) ;
719 /* ---------------------------------------------------------------------
720 * Converts a packed binary string to a hexadecimal string
722 streng *std_c2x( tsd_t *TSD, cparamboxptr parms )
724 checkparam( parms, 1, 1 , "C2X" ) ;
725 return unpack_hex( TSD, parms->value ) ;
728 static void check_wholenum( tsd_t *TSD, const char *bif, const streng *arg,
729 num_descr **num )
731 if ( !myiswnumber( TSD, arg, num,
732 !get_options_flag( TSD->currlevel, EXT_STRICT_ANSI ) ) )
733 exiterror( ERR_INCORRECT_CALL, 12, bif, 1, tmpstr_of( TSD, arg ) );
736 /* ---------------------------------------------------------------------
737 * Converts a whole number into char string. This is just a wrapper
738 * around str_binerize(), which preprocesses the parameters.
740 streng *std_d2c( tsd_t *TSD, cparamboxptr parms )
742 int length; /* the length of the output string */
743 num_descr *num;
745 checkparam( parms, 1, 2 , "D2C" );
747 check_wholenum( TSD, "D2C", parms->value, &num );
748 if ( parms->next && parms->next->value )
749 length = atozpos( TSD, parms->next->value, "D2C", 2 );
750 else
753 * The strange syntax forces a check for non-negative first arg if the
754 * second doesn't exist.
756 if ( num->negative )
757 exiterror( ERR_INCORRECT_CALL, 13, "D2C", 1,
758 tmpstr_of( TSD, parms->value ) );
760 length = -1;
763 return str_binerize( TSD, num, length );
767 /* ------------------------------------------------------------------
768 * Converts a decimal string into a hexadecimal string, using char
769 * string as an intermediate format. Due to the use of intermediate
770 * format, an extra hex digit might slip into the left end of the
771 * answer, and code is added to remove it.
773 streng *std_d2x( tsd_t *TSD, cparamboxptr parms )
775 int length; /* holds the requested langth of the result */
776 streng *result; /* the output streng */
777 streng *packed; /* tmp variable, holds the packed string */
778 num_descr *num;
781 * Check the parameters, and set 'length' to the specified length, or
782 * to -1 if the second parameter was not specified.
784 checkparam( parms, 1, 2 , "D2X" );
786 check_wholenum( TSD, "D2X", parms->value, &num );
787 if ( parms->next && parms->next->value )
788 length = atozpos( TSD, parms->next->value, "D2X", 2 );
789 else
792 * The strange syntax forces a check for non-negative first arg if the
793 * second doesn't exist.
795 if ( num->negative )
796 exiterror( ERR_INCORRECT_CALL, 13, "D2X", 1,
797 tmpstr_of( TSD, parms->value ) );
799 length = -1;
803 * Convert the whole number into a hex string in a two step operation.
804 * First it is converted into a char string, and then that char string
805 * is converted into a hexstring.
807 packed = str_binerize( TSD, num, ( length == -1 ) ? -1 : ( length+1 ) / 2 );
808 result = unpack_hex( TSD, packed );
809 Free_stringTSD( packed );
812 * Since we used char string as a temporary format, the hex string
813 * will now be padded with one extra zero at the left. If we specified
814 * length, and that length does not match the actual length, we must
815 * strip away the first zero in 'result'.
817 * Here we check for length>0, since we want to catch it if length was
818 * specified, but not if it was 0. In the latter case, the string will
819 * be the nullstring, and we don't need to do anything anyway.
821 if ( ( length > 0 ) && ( Str_len( result ) != length ) && Str_len( result ) )
823 assert( Str_len( result ) == length + 1 );
824 memmove( result->value, &result->value[1], --result->len );
828 * Just to be safe, check that we did get the nullstring if length
829 * was specified to 0
831 assert( ( length != 0 ) || ( Str_len(result) == 0 ) );
834 * If length was not specified, there might be leading zeros in the
835 * answer that we might want to get rid of. However, the algoritm
836 * that is used in str_binerize() should ensure that there will not
837 * be more than one leading zero in this case. This part of the code
838 * could easily be merged with the other call to memmove() above,
839 * but have been placed here to improve readability.
841 * If there are more than one leading zero, this will not work. The
842 * situation where the result only consists of one zero, should not
843 * occur, but it will be handled, since the result should then be
844 * the nullstring.
846 if ( ( length == -1 ) && ( result->value[0] == '0' ) )
848 assert( Str_len( result ) > 1 );
849 assert( ( result->value[0] == '0') && ( ( result->value[1] != '0' ) || ( Str_len( result ) == 2 ) ) );
851 memmove( result->value, &result->value[1], --result->len );
855 * That's it, now we just have to get out of here
857 return result;