Found in "gnokii-working" directory, some November-patches version
[gnokii.git] / common / gsm-common.c
index 54e63cb..72c1b8d 100644 (file)
   Released under the terms of the GNU GPL, see file COPYING for more details.
 
   $Log$
-  Revision 1.1.1.1  2001/11/25 21:58:59  short
-  :pserver:cvs@pserver.samba.org:/cvsroot - gnokii - Sun Nov 25 22:56 CET 2001
-
-  Revision 1.8  2001/11/13 16:12:20  pkot
-  Preparing libsms to get to work. 6210/7110 SMS and SMS Folder updates
-
-  Revision 1.7  2001/11/08 16:34:19  pkot
-  Updates to work with new libsms
+  Revision 1.1.1.2  2002/04/03 00:07:55  short
+  Found in "gnokii-working" directory, some November-patches version
 
   Revision 1.6  2001/09/14 12:15:28  pkot
   Cleanups from 0.3.3 (part1)
 
 */
 
+#include <gsm-common.h>
 #include <string.h>
-#include "gsm-common.h"
+#include <stdlib.h>    /* for rand() */
+#include <ctype.h>
+
+/* Coding functions */
+#define NUMBER_OF_7_BIT_ALPHABET_ELEMENTS 128
+static unsigned char GSM_DefaultAlphabet[NUMBER_OF_7_BIT_ALPHABET_ELEMENTS] = {
+
+       /* ETSI GSM 03.38, version 6.0.1, section 6.2.1; Default alphabet */
+       /* Characters in hex position 10, [12 to 1a] and 24 are not present on
+          latin1 charset, so we cannot reproduce on the screen, however they are
+          greek symbol not present even on my Nokia */
+       
+       '@',  0xa3, '$',  0xa5, 0xe8, 0xe9, 0xf9, 0xec, 
+       0xf2, 0xc7, '\n', 0xd8, 0xf8, '\r', 0xc5, 0xe5,
+       '?',  '_',  '?',  '?',  '?',  '?',  '?',  '?',
+       '?',  '?',  '?',  '?',  0xc6, 0xe6, 0xdf, 0xc9,
+       ' ',  '!',  '\"', '#',  0xa4,  '%',  '&',  '\'',
+       '(',  ')',  '*',  '+',  ',',  '-',  '.',  '/',
+       '0',  '1',  '2',  '3',  '4',  '5',  '6',  '7',
+       '8',  '9',  ':',  ';',  '<',  '=',  '>',  '?',
+       0xa1, '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',  0xc4, 0xd6, 0xd1, 0xdc, 0xa7,
+       0xbf, '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',  0xe4, 0xf6, 0xf1, 0xfc, 0xe0
+};
+
+unsigned char EncodeWithDefaultAlphabet(unsigned char value)
+{
+       unsigned char i;
+       
+       if (value == '?') return  0x3f;
+       
+       for (i = 0; i < NUMBER_OF_7_BIT_ALPHABET_ELEMENTS; i++)
+               if (GSM_DefaultAlphabet[i] == value)
+                       return i;
+       
+       return '?';
+}
+
+unsigned char DecodeWithDefaultAlphabet(unsigned char value)
+{
+       return GSM_DefaultAlphabet[value];
+}
+
+wchar_t EncodeWithUnicodeAlphabet(unsigned char value)
+{
+       wchar_t retval;
+        
+       if (mbtowc(&retval, &value, 1) == -1) return '?';
+       else return retval;
+}
+
+unsigned char DecodeWithUnicodeAlphabet(wchar_t value)
+{
+       unsigned char retval;
+
+       if (wctomb(&retval, value) == -1) return '?';
+       else return retval;
+}
+
 
 GSM_Error Unimplemented(void)
 {
@@ -64,30 +122,6 @@ GSM_MemoryType StrToMemoryType(const char *s)
        X(MC);
        X(LD);
        X(MT);
-       X(IN);
-       X(OU);
-       X(AR);
-       X(TE);
-       X(F1);
-       X(F2);
-       X(F3);
-       X(F4);
-       X(F5);
-       X(F6);
-       X(F7);
-       X(F8);
-       X(F9);
-       X(F10);
-       X(F11);
-       X(F12);
-       X(F13);
-       X(F14);
-       X(F15);
-       X(F16);
-       X(F17);
-       X(F18);
-       X(F19);
-       X(F20);
        return GMT_XX;
 #undef X
 }
@@ -98,3 +132,493 @@ inline void GSM_DataClear(GSM_Data *data)
 {
        memset(data, 0, sizeof(GSM_Data));
 }
+       /* TP-Validity Period handling */
+
+/* TP-Validity Period handling */
+unsigned char SMS_Validity_to_VP(int Validity)
+{
+       /* FIXME: error-checking for correct Validity - it should not be bigger then
+          63 weeks and smaller then 5minutes. We should also test intervals because
+          the SMS->Validity to TP-VP is not continuos. I think that the simplest
+          solution will be an array of correct values. We should parse it and if we
+          find the closest TP-VP value we should use it. Or is it good to take
+          closest smaller TP-VP as we do now? I think it is :-) */
+
+       /* 5 minutes intervals up to 12 hours = 720 minutes */
+       if (Validity <= 720)
+               return((unsigned char) (Validity/5)-1);
+
+       /* 30 minutes intervals up to 1 day */
+       else if ((Validity > 720) && (Validity <= 1440))
+               return((unsigned char) ((Validity-720)/30)+143);
+
+       /* 1 day intervals up to 30 days */
+       else if ((Validity > 1440) && (Validity <= 43200))
+               return((unsigned char) (Validity/1440)+166);
+
+       /* 1 week intervals up to 63 weeks */
+       else if ((Validity > 43200) && (Validity <= 635040))
+               return((unsigned char) (Validity/10080)+192);
+
+       /* Validity too big, strip it to the highest possible value */
+       return((unsigned char)0xFF);
+}
+
+int SMS_VP_to_Validity(GSM_SMSMessageValidity Validity)
+{
+       if (Validity<=GSMV_1_Hour)
+               return(1*60);
+       if (Validity<=GSMV_6_Hours)
+               return(6*60);
+       if (Validity<=GSMV_24_Hours)
+               return(24*60);
+       if (Validity<=GSMV_72_Hours)
+               return(72*60);
+       if (Validity<=GSMV_1_Week)
+               return(7*24*60);
+       if (Validity<=GSMV_Max_Time)
+               return(63*7*24*60);
+       /* Validity too big, strip it to the highest possible value */
+       return(63*7*24*60);
+}
+
+/* This function implements packing of numbers (SMS Center number and
+ * destination number) for SMS sending function.
+ * RETURN: strlen(Number+(*Number=='+')), that means the number of INPUT
+ * digits (without '+' marker) which is equal to the number of written
+ * digit nibbles (without possible trailing 0xF filler)
+ */
+int SemiOctetPack(char *Number, unsigned char *Output)
+{
+       unsigned char *IN = Number;  /* Pointer to the input number */
+       unsigned char *OUT = Output; /* Pointer to the output */
+       int count = 0; /* This variable is used to notify us about count of already
+                         packed numbers. */
+
+       /* The first byte in the Semi-octet representation of the address field is
+          the Type-of-Address. This field is described in the official GSM
+          specification 03.40 version 5.3.0, section 9.1.2.5, page 33. We support
+          only international and unknown number. */
+  
+       if (*IN == '+') {
+               *OUT++ = GNT_INTERNATIONAL; /* International number */
+               IN++;
+       } else *OUT++ = GNT_UNKNOWN; /* Unknown number */
+
+       /* The next field is the number. It is in semi-octet representation - see
+          GSM scpecification 03.40 version 5.3.0, section 9.1.2.3, page 31. */
+
+       while (*IN) {
+               if (count & 0x01) {
+                       *OUT = *OUT | ((*IN - '0') << 4);
+                       OUT++;
+               }
+               else
+                       *OUT = *IN - '0';
+               count++; IN++;
+       }
+
+       /* We should also fill in the most significant bits of the last byte with
+          0x0f (1111 binary) if the number is represented with odd number of
+          digits. */
+
+       if (count & 0x01) {
+               *OUT=*OUT | 0xf0;
+               OUT++;
+               return (2 * (OUT - Output - 1) - 1);
+       }
+
+       return (2 * (OUT - Output - 1));
+}
+
+/* This function implements unpacking of numbers (SMS Center number and
+ * destination number) for SMS receiving function.
+ * INPUT: Number of NIBBLES which is
+ * equal to the number of written OUTPUT characters (without optional '+'
+ * marker), may be +1 as it is hard to predict possible trailing 0xF filler
+ * RETURN: Number with skipped number or NULL for error
+ */
+char *SemiOctetUnpack(char *dest,size_t destlen,u8 *Number,size_t nibbles)
+{
+       int i;
+       char *d;
+       u8 *s,Digit;
+
+       if (destlen<2)
+               return(NULL);           /* buffer overflow */
+
+       d=dest;
+       s=Number;
+       if (*s++ == GNT_INTERNATIONAL)
+               *d++='+';
+
+       for (i=0;i<nibbles;i++) {
+               if (d +1/*current digit*/ +1/*terminating '\0'*/ >dest+destlen)
+                       return(NULL);           /* buffer overflow */
+               /* Here we advance "s" during first nibble read as when we finish
+                * now we must return the pointer AFTER it
+                */
+               if (!(i&1))
+                       Digit= (*s++ )     & 0x0F;
+               else
+                       Digit=((s[-1])>>4) & 0x0F;
+               if (i+1==nibbles && Digit==0xF)
+                       break;                  /* nibbles+1 is allowed */
+               if (Digit>9)
+                       return(NULL);           /* digit=A..F */
+               *d++=Digit+'0';
+               }
+       *d='\0';
+
+       return(s);
+}
+
+/* When you are so poor that you even don't have your own buffer...
+ * RETURN: local static buffer address or NULL for error
+ * Note: address of Number with skipped number is lost!
+ * Note: be sure that this function is called only once before processing the result!
+ */
+char *SemiOctetUnpack_static(u8 *Number,size_t nibbles)
+{
+       static char Buffer[GNOKII_MAX(
+                               GNOKII_MAX(GSM_MAX_SMS_CENTER_LENGTH,GSM_MAX_SENDER_LENGTH)
+                               ,GSM_MAX_DESTINATION_LENGTH)
+                               ] = "";
+       char *r;
+
+       if (!(r=SemiOctetUnpack(Buffer,sizeof(Buffer),Number,nibbles)))
+               return(NULL);
+       return(Buffer);
+}
+
+unsigned char GSM_Default_Alphabet[] = {
+
+       /* ETSI GSM 03.38, version 6.0.1, section 6.2.1; Default alphabet */
+       /* Characters in hex position 10, [12 to 1a] and 24 are not present on
+          latin1 charset, so we cannot reproduce on the screen, however they are
+          greek symbol not present even on my Nokia */
+
+       '@',  0xa3, '$',  0xa5, 0xe8, 0xe9, 0xf9, 0xec, 
+       0xf2, 0xc7, '\n', 0xd8, 0xf8, '\r', 0xc5, 0xe5,
+       '?',  '_',  '?',  '?',  '?',  '?',  '?',  '?',
+       '?',  '?',  '?',  '?',  0xc6, 0xe6, 0xdf, 0xc9,
+       ' ',  '!',  '\"', '#',  0xa4,  '%',  '&',  '\'',
+       '(',  ')',  '*',  '+',  ',',  '-',  '.',  '/',
+       '0',  '1',  '2',  '3',  '4',  '5',  '6',  '7',
+       '8',  '9',  ':',  ';',  '<',  '=',  '>',  '?',
+       0xa1, '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',  0xc4, 0xd6, 0xd1, 0xdc, 0xa7,
+       0xbf, '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',  0xe4, 0xf6, 0xf1, 0xfc, 0xe0
+};
+
+static unsigned char GetAlphabetValue(unsigned char value)
+{
+        unsigned char i;
+  
+        if (value == '?') return 0x3f;
+  
+        for (i = 0 ; i < 128 ; i++)
+                if (GSM_Default_Alphabet[i] == value)
+                        return i;
+
+        return 0x3f; /* '?' */
+}
+
+/* Function packs the '\0'-terminated 7-bit "input" string to the "output" 8-bit stream.
+ * UsedBits specifies the number of bottom bits already used in (*output) byte.
+ * RETURNS: Number of bytes written, at least partially
+ */
+int PackSevenBitsToEight(int UsedBits, unsigned char *input, unsigned char *output)
+{
+        unsigned char *OUT = output; /* Current pointer to the output buffer */
+        unsigned char *IN  = input;  /* Current pointer to the input buffer */
+
+       UsedBits %= 8;          /* just sanity, (0<=x<8) assert would be appropriate */
+
+        while (*IN) {
+                unsigned char Byte = GetAlphabetValue(*IN);
+
+               /* UsedBits specifies the number of already allocated bits in OUT[-1]
+                * byte. If there were no bits used, we just don't advance the destination
+                * pointer and we gain the full 8 bits of free space in the current byte.
+                */
+                if (UsedBits == 0)
+                       UsedBits = 8;
+               else
+                       OUT++;
+
+               /* Write upper (UsedBits) of Byte to the bottom of (*OUT) */
+                *OUT = Byte >> (8 - UsedBits);
+                /* If we don't write at 0th bit of the octet, we should write
+                 * a second part of the previous octet
+                * Write lower (8-UsedBits) to the top of OUT[-1]
+                */
+                if (UsedBits != 8)
+                        OUT[-1] = (OUT[-1] & ((1<<UsedBits)-1)) | (Byte << UsedBits);
+
+                UsedBits--;
+
+                IN++;
+        }
+       if (UsedBits!=0)
+               OUT++;
+
+        return (OUT - output);
+}
+
+int UnpackEightBitsToSeven(int offset, int in_length, int out_length,
+                           unsigned char *input, unsigned char *output)
+{
+       unsigned char *OUT = output; /* Current pointer to the output buffer */
+       unsigned char *IN  = input;  /* Current pointer to the input buffer */
+       unsigned char Rest = 0x00;
+       int Bits;
+#define ByteMask ((1 << Bits) - 1)
+
+       Bits = offset ? offset : 7;
+
+       while ((IN - input) < in_length) {
+
+               *OUT = ((*IN & ByteMask) << (7 - Bits)) | Rest;
+               Rest = *IN >> Bits;
+
+               /* If we don't start from 0th bit, we shouldn't go to the
+                  next char. Under *OUT we have now 0 and under Rest -
+                  _first_ part of the char. */
+               if ((IN != input) || (Bits == 7)) OUT++;
+               IN++;
+
+               if ((OUT - output) >= out_length) break;
+
+               /* After reading 7 octets we have read 7 full characters but
+                  we have 7 bits as well. This is the next character */
+               if (Bits == 1) {
+                       *OUT = Rest;
+                       OUT++;
+                       Bits = 7;
+                       Rest = 0x00;
+               } else {
+                       Bits--;
+               }
+       }
+
+       return OUT - output;
+}
+
+GSM_Error SMS_SetupUDH(GSM_SMSMessage *sms,GSM_UDH UDHType)
+{
+unsigned short srcport,dstport;
+
+       sms->UDHPresent=true;
+       switch (UDHType) {
+       case GSM_RingtoneUDH:  /* FALLTHRU */
+       case GSM_OpLogo:       /* FALLTHRU */
+       case GSM_CallerIDLogo:
+               sms->UDH[1+0]=0x05;     /* logos (16-bit ports) */
+               sms->UDH[1+1]=4;        /* sizeof(dstport)+sizeof(srcport) */
+               sms->UDH[0]=2+sms->UDH[1+1];    /* total UserDataHeader size */
+               switch (UDHType) {
+                       case GSM_RingtoneUDH:  dstport=0x1581; break;
+                       case GSM_OpLogo:       dstport=0x1582; break;
+                       case GSM_CallerIDLogo: dstport=0x1583; break;
+                       default: dstport=0;     /* double shut up GCC */
+                       }
+               /* I have tested on Nokia 9000i on GSM_RingtoneUDH that it will silently
+                * ignore Multi-SMS incoming ringtone with srcport==0 .
+                * Despite this Single-SMS incoming ringtone is accepted even with srcport==0 !
+                * When both ports were equal to 0x1581 everything was fine.
+                */
+               srcport=dstport;
+               sms->UDH[1+2+0+0]=dstport>>8;
+               sms->UDH[1+2+0+1]=dstport>>0;
+               sms->UDH[1+2+2+0]=srcport>>8;
+               sms->UDH[1+2+2+1]=srcport>>0;
+               return(GE_NONE);
+       default:
+               break;
+       }
+       sms->UDHPresent=false;
+       return(GE_NOTIMPLEMENTED);      /* error  */
+}
+
+GSM_UDH SMS_DetectUDH(GSM_SMSMessage *sms)
+{
+       if (!sms->UDHPresent)
+               return(GSM_NoUDH);
+       if (sms->UDH[0]< 1/*type*/ +1/*sub-length*/)
+               return(GSM_NoUDH);      /* error */
+       switch (sms->UDH[1]) {
+
+       case 0x00: /* concatenated messages (8-bit reference) */
+               dprintf(_("Concatenated message!!!\n"));
+               if (sms->UDH[2]<3)
+                       return(GSM_NoUDH);      /* error */
+               return(GSM_ConcatenatedMessages);
+               break;
+
+       case 0x08: /* concatenated messages (16-bit reference) */
+               dprintf(_("Concatenated message!!!\n"));
+               if (sms->UDH[2]<4)
+                       return(GSM_NoUDH);      /* error */
+               return(GSM_ConcatenatedMessages);
+               break;
+
+       case 0x05: /* logos (16-bit ports) */
+               if (sms->UDH[2]<4)
+                       return(GSM_NoUDH);      /* error */
+               switch ((sms->UDH[3]<<8) | (sms->UDH[4]<<0)) {
+               case 0x1582:
+                       return(GSM_OpLogo);
+               case 0x1583:
+                       return(GSM_CallerIDLogo);
+               }
+               break;
+       default:
+               break;
+       }
+       return(GSM_NoUDH);      /* error - not recognized */
+}
+
+/* RETURNS: Total number of SMSes, sms->MessageTextLength will contain cargoitems
+ */
+static int SMS_Deconcatenate_calcparts(GSM_SMSMessage *sms,size_t *cargoitemsp,size_t buflen,size_t messagetextpreitems)
+{
+int bits=(sms->EightBit ? 8 : 7);
+int UDHbytes=(!sms->UDHPresent ? 0 : 1+sms->UDH[0]);
+int UDHitems=(UDHbytes*8 +bits-1/*round-up*/)/bits;
+int lengthitems=(sms->EightBit ? GSM_MAX_SMS_8BIT_LENGTH : GSM_MAX_SMS_LENGTH);
+int cargoitems=lengthitems-UDHitems-messagetextpreitems;
+
+       if (cargoitemsp)
+               *cargoitemsp=cargoitems;
+
+       return((buflen +cargoitems-1/*round-up*/)/cargoitems);
+}
+
+/* RETURNS: Whether we should send the prepared (*sms)
+ */
+bool SMS_Deconcatenate(GSM_Deconcatenate_state *state,GSM_SMSMessage *sms,char *buf,size_t buflen,bool useudh)
+{
+size_t offset_start,offset_end,messagetextpreitems;
+
+#define CONCAT_TEXT_HEADER "%2d/%-2d: "
+/* This length has to match 7bit-to-8bit length of single concat UDH
+ * due to broken assumptions at least in xgnokii_sms.c
+ * =round-up((UDHlength+IEItype+IEIlength+reference+total+sequence)*8/7)
+ */
+#define CONCAT_TEXT_HEADER_LENGTH 7
+
+       if (state->first) {
+               /* INIT */
+               state->first=false;
+
+               if ((state->singleshot=(1>=SMS_Deconcatenate_calcparts(sms,NULL,buflen,0)))) {
+                       memcpy(sms->MessageText,buf,buflen);
+                       sms->MessageText[sms->MessageTextLength=buflen]='\0';
+                       return(true);   /* send it */
+                       }
+               state->sequence=0;
+               state->sequencep=NULL;
+               }
+       if (state->singleshot)
+               return(false);  /* done */
+
+       if (!state->sequence && useudh) {
+u8 *d;
+               if (!sms->UDHPresent) {
+                       sms->UDHPresent=true;
+                       sms->UDH[0]=0;
+                       }
+               if (1/*sizeof(UDH[0])*/ +sms->UDH[0]/*current UDH len*/ +6/*concatenation UDH len*/
+                               > sizeof(sms->UDH))
+                       return(false);  /* error - UDH buffer overflow - shouldn't happen */
+               d=(sms->UDH+1)+sms->UDH[0];
+
+               *d++=0x00;      /* concatenated messages (IEI) (8-bit reference number) */
+               *d++=0x03;      /* IEI data length */
+               *d++=rand();    /* 8-bit reference number */
+               *d++=0;         /* number of messages - not yet known, filled by d[-2] below! */
+               state->sequencep=d;
+               *d++=0;         /* message sequence number - not yet filled */
+
+               sms->UDH[0]=d-(sms->UDH+1);
+
+               d[-2]=SMS_Deconcatenate_calcparts(sms,&state->cargoitems,buflen,0);     /* UDH[0] must be already set */
+               }
+       if (!state->sequence && !useudh) {
+               gsprintf(sms->MessageText,sizeof(sms->MessageText),CONCAT_TEXT_HEADER,
+                               0, SMS_Deconcatenate_calcparts(sms,&state->cargoitems,buflen,CONCAT_TEXT_HEADER_LENGTH));
+               }
+       
+       /* It is important that "lastsequence" is not "u8" and so it will not overflow
+        * on the 255th part of SMS
+        */
+       state->sequence++;
+
+       if (useudh)
+               *state->sequencep=state->sequence;
+       else {
+               gsprintf(sms->MessageText,3,"%2d",state->sequence);
+               sms->MessageText[2]='/';        /* it may got overwritten by '\0' */
+               }
+
+       offset_start=(state->sequence-1)*state->cargoitems;
+       offset_end=GNOKII_MIN(buflen,(state->sequence-1+1)*state->cargoitems);
+
+       if (offset_start>=offset_end)
+               return(false);  /* no more data available */
+
+       messagetextpreitems=(useudh ? 0 : CONCAT_TEXT_HEADER_LENGTH);
+       memcpy(sms->MessageText+messagetextpreitems,buf+offset_start,offset_end-offset_start);
+       sms->MessageText[sms->MessageTextLength=messagetextpreitems+offset_end-offset_start]='\0';
+       dprintf(_("Fragment %d: %d..%d (buflen=%d)\n"),state->sequence,offset_start,offset_end,buflen);
+
+       return(true);   /* send it! */
+}
+
+static inline u8 SMS_CharFromHex(unsigned u)
+{
+       u&=0xDF;
+       return(u<'A'?u-('0'&0xDF):(u-('A'&0xDF))+0xA);
+}
+
+/* RETURN: end of "d" block or NULL if error
+ * Note: s==d on input is permitted
+ */
+u8 *SMS_BlockFromHex(u8 *d,const char *s,size_t len)
+{
+       while (len>=2) {
+               if (!isxdigit(s[0]) || !isxdigit(s[1])) 
+                       break;          /* error */
+               *d++=(SMS_CharFromHex(s[0])<<4)|SMS_CharFromHex(s[1]);
+               s+=2;
+               len-=2;
+               }
+       if (len)
+               return(NULL);           /* error */
+       return(d);
+}
+
+static inline char SMS_CharToHex(u8 x)
+{
+       x&=0x0F;
+       if (x<10) return(x   +'0');
+       else      return(x-10+'A');
+}
+
+/* RETURN: end of "d" block (no errors generated)
+ */
+char *SMS_BlockToHex(char *d,const u8 *s,size_t len)
+{
+       while (len--) {
+               *d++=SMS_CharToHex(*s>>4U);
+               *d++=SMS_CharToHex(*s    );
+               s++;
+               }
+       return(d);
+}