Implemented RTS/CTS handshaking (-C/--rtscts vs. -x/--xonxoff)
authorshort <>
Sat, 7 Apr 2001 15:13:34 +0000 (15:13 +0000)
committershort <>
Sat, 7 Apr 2001 15:13:34 +0000 (15:13 +0000)
Implemented disable of +CMGF PDU/text mode autodetection (-M/--smsmode)
Implemented additional preceding SMSC modes for compat. (-P/--pdusmscmode)
 - "count-in" is default, "count-out" precedes w/o counting, "none" discards
 - "count-out" needed at least by recent Siemens M20 firmwares
 - autodetected when not forced by user
 - hardware provided by courtesy of Radek Kadner, ATS Praha
Fixed: total retrycount - now really (maxretry), not (maxretry+1) as before
Main communication routine devcmd() fortified and made foolproof
"AT+CSCA?" is now allowed to omit ADDR_* type
 - needed at least by recent Siemens M20 firmwares
Fixed timestamps by telling 'not known DST' (instead of 'no DST' as before)
PDU mode SMS receive works now (fatal bugs, never tested before)

mdsms.c

diff --git a/mdsms.c b/mdsms.c
index ccd9d5c..932dc49 100644 (file)
--- a/mdsms.c
+++ b/mdsms.c
@@ -96,9 +96,29 @@ static int verbose
 static char *pname;
 static int dis_cleanup=0,devfd=-1;
 
-static char *phone,*device,*logname,*lockfile,*smsc,*maxretry,*readtime,*chartime,*cmdtime,*baud,*restore;
+static char *phone,*device,*logname,*lockfile,*smsmode,*pdusmscmode,*smsc,*maxretry,*readtime,*chartime,*cmdtime,*baud,*restore;
 static int readbody;
 static long maxretryn=DEF_MAXRETRY,readtimen=-1,chartimen=DEF_CHARTIME,cmdtimen=DEF_CMDTIME,baudn=DEF_BAUD;
+#ifdef HAVE_CRTSCTS
+static int handshake_rtscts;
+static unsigned handshake_stamp;
+#else
+#define handshake_rtscts (0)
+#endif
+static enum {
+       FSM_AUTO=0,
+       FSM_PDU,
+       FSM_TEXT
+       } force_smsmode=FSM_AUTO;
+static enum {
+       FPSM_AUTO=0,
+       FPSM_COUNT_IN,
+       FPSM_COUNT_OUT,
+       FPSM_NONE
+       } force_pdusmscmode=FPSM_AUTO,
+#define FPSM_MIN (FPSM_COUNT_IN)
+#define FPSM_MAX (FPSM_NONE)
+               try_pdusmscmode=FPSM_MIN;
 static size_t bodylen;
 /* --send / --send-mobildock / --receive specific */
 static char *body;
@@ -235,11 +255,13 @@ static void usage(void)
 %s\
 \n\
 Usage: %s [-c|--config <cfgfile>] [-d|--device <device>]\n\
-             {--send | --send-mobildock | --receive | --logo-send}\n\
-             [-L|--log <file>] [-b|--baud <rate>]\n\
-             [-l|--lockfile <lock>] [-s|--smsc <smsc #>] [-m|--maxretry <#>]\n\
+             [-L|--log <file>] [-l|--lockfile <lock>]\n\
+             [-b|--baud <rate>] [-x|--xonxoff] [-C|--rtscts]\n\
+             [-M|--smsmode <mode>] [-P|--pdusmscmode <mode>]\n\
+             [-s|--smsc <smsc #>] [-m|--maxretry <#>]\n\
              [-r|--readtime <sec>] [-t|--chartime <msec>] [-T|--cmdtime <msec>]\n\
              [-v|--verbose] [-h|--help] [-V|--version]\n\
+             {--send | --send-mobildock | --receive | --logo-send}\n\
   --send / --send-mobildock:\n\
              [-f|--file] <dest. phone> <msg text|msg filename>\n\
   --receive:\n\
@@ -253,9 +275,13 @@ Usage: %s [-c|--config <cfgfile>] [-d|--device <device>]\n\
 \t\t(def. \"%s\" and \"$HOME%s\")\n\
  -d, --device\tMobile on this serial device (def. \"%s\")\n\
  -L, --log\tLog all important messages to this file (def. \"%s\")\n\
- -b, --baud\tSet baudrate, 2400-57600 supported (def. %d)\n\
  -l, --lockfile\tLock serial port by this file, \"%%s\" is basename of device\n\
 \t\t(def. \"%s\")\n\
+ -b, --baud\tSet baudrate, 2400-57600 supported (def. %d)\n\
+ -x, --xonxoff\tUse XON/XOFF (AKA software) serial port handshaking - default\n\
+ -C, --rtscts\tUse RTS/CTS (AKA hardware) serial port handshaking%s\n\
+ -M, --smsmode\tForce SMS as: \"pdu\" or 0: PDU mode, \"text\" or 1: text mode\n\
+ -P, --pdusmscmode\tForce PDU as: \"count-in\", \"count-out\", \"none\"\n\
  -s, --smsc\tUse this SMS Center number (def. query from mobile)\n\
  -m, --maxretry\tMaximum retries of any command before giving up (def. %d)\n\
  -r, --readtime\tSeconds for maximum wait time for response\n\
@@ -284,8 +310,13 @@ Usage: %s [-c|--config <cfgfile>] [-d|--device <device>]\n\
 \n\
 You may need to use the following line to catch all of this help text:\n\
 ./mdsms 2>&1|more\n\
-\n"),version,PACKAGE,CONFIG_MAIN,CONFIG_HOME,DEF_DEVICE,DEF_LOGNAME,DEF_BAUD,DEF_LOCKFILE,DEF_MAXRETRY,
-DEF_READTIME,DEF_READTIME_MOBILDOCK,EXT_READTIME,DEF_CHARTIME,DEF_CMDTIME,
+\n"),version,PACKAGE,CONFIG_MAIN,CONFIG_HOME,DEF_DEVICE,DEF_LOGNAME,DEF_LOCKFILE,DEF_BAUD,
+#ifdef HAVE_CRTSCTS
+               "",
+#else
+               _("\n\t\t(Not supported on this platform!)"),
+#endif
+DEF_MAXRETRY,DEF_READTIME,DEF_READTIME_MOBILDOCK,EXT_READTIME,DEF_CHARTIME,DEF_CMDTIME,
 WORD_NET,WORD_GROUP);
        exit(EXIT_FAILURE);
 }
@@ -303,21 +334,25 @@ static const struct option longopts[]={
 {"recv"          ,0,0,MODE_RECEIVE},
 {"logo"          ,0,0,MODE_LOGO_SEND},
 {"ring"          ,0,0,MODE_RING_SEND},
-{"config"  ,1,0,'c'},
-{"device"  ,1,0,'d'},
-{"log"     ,1,0,'L'},
-{"baud"    ,1,0,'b'},
-{"lockfile",1,0,'l'},
-{"smsc"    ,1,0,'s'},
-{"maxretry",1,0,'m'},
-{"readtime",1,0,'r'},
-{"chartime",1,0,'t'},
-{"cmdtime" ,1,0,'T'},
-{"file"    ,0,0,'f'},
-{"verbose" ,0,0,'v'},
-{"help"    ,0,0,'h'},
-{"version" ,0,0,'V'},
-{NULL      ,0,0,0  }};
+{"config"      ,1,0,'c'},
+{"device"      ,1,0,'d'},
+{"log"         ,1,0,'L'},
+{"lockfile"    ,1,0,'l'},
+{"baud"        ,1,0,'b'},
+{"xonxoff"     ,0,0,'x'},
+{"rtscts"      ,0,0,'C'},
+{"smsmode"     ,1,0,'M'},
+{"pdusmscmode" ,1,0,'P'},
+{"smsc"        ,1,0,'s'},
+{"maxretry"    ,1,0,'m'},
+{"readtime"    ,1,0,'r'},
+{"chartime"    ,1,0,'t'},
+{"cmdtime"     ,1,0,'T'},
+{"file"        ,0,0,'f'},
+{"verbose"     ,0,0,'v'},
+{"help"        ,0,0,'h'},
+{"version"     ,0,0,'V'},
+{NULL          ,0,0,0  }};
 
 static void processargs(int argp,char **args,const char *from);
 
@@ -513,15 +548,17 @@ static struct {
        char **const var;
        unsigned stamp;
        } optset[]={
-               { 'd',&device   },
-               { 'L',&logname  },
-               { 'b',&baud     },
-               { 'l',&lockfile },
-               { 's',&smsc     },
-               { 'm',&maxretry },
-               { 'r',&readtime },
-               { 't',&chartime },
-               { 'T',&cmdtime  },
+               { 'd',&device       },
+               { 'L',&logname      },
+               { 'l',&lockfile     },
+               { 'b',&baud         },
+               { 'M',&smsmode      },
+               { 'P',&pdusmscmode  },
+               { 's',&smsc         },
+               { 'm',&maxretry     },
+               { 'r',&readtime     },
+               { 't',&chartime     },
+               { 'T',&cmdtime      },
        };
 
 static void processargs(int argp,char **args,const char *from)
@@ -532,7 +569,7 @@ int i;
 
        seq++;
        optarg=NULL; optind=0; /* FIXME: Possible portability problem. */
-       while ((optc=getopt_long(argp,args,"c:d:L:b:l:s:m:r:t:T:fvhV",longopts,NULL))!=EOF) switch (optc) {
+       while ((optc=getopt_long(argp,args,"c:d:L:l:b:xCM:P:s:m:r:t:T:fvhV",longopts,NULL))!=EOF) switch (optc) {
                case 'c':
                        if (cfgstacki>=NELEM(cfgstack)) {
                                error(_("Looping (%d) during attempt to read config file \"%s\", break-out"),NELEM(cfgstack),from);
@@ -540,7 +577,7 @@ int i;
                                }
                        chk(cfgstack[cfgstacki++]=strdup(optarg));
                        break;
-               case 'd': case 'L': case 'b': case 'l': case 's': case 'm': case 'r': case 't': case 'T':
+               case 'd': case 'L': case 'b': case 'l': case 'M': case 'P': case 's': case 'm': case 'r': case 't': case 'T':
                        for (i=0;i<NELEM(optset);i++)
                                if (optset[i].c==optc) {
                                        if (optset[i].stamp && optset[i].stamp!=seq) {
@@ -554,6 +591,16 @@ int i;
                                        }
                        assert(i<NELEM(optset));
                        break;
+               case 'x': case 'C':
+#ifdef HAVE_CRTSCTS
+                       if (handshake_stamp && handshake_stamp!=seq) break;
+                       handshake_rtscts=(optc=='C');
+                       handshake_stamp=seq;
+#else
+                       if (optc=='C')
+                               error(_("!RTS/CTS handshake NOT supported on this platform! Occured during parsing option %d from \"%s\""),optind-1,from);
+#endif
+                       break;
                case MODE_SEND:
                case MODE_SEND_MOBILDOCK:
                case MODE_RECEIVE:
@@ -812,10 +859,11 @@ static char state=-1;
        state=yes;
 }
 
-static const char *record;
+static const char *record,*recordend;
 static char *catchdata;
 static size_t catchdatal,catchdatasiz;
 
+static char *reform(const char *s,int slot);
 static void catched(const char *end,char edata)
 {
 size_t len;
@@ -833,14 +881,15 @@ const char *p;
                memcpy(catchdata+catchdatal,record,len);
                catchdatal+=len;
                }
-       record=(p?NULL:end);
+       record   =(p?NULL:end);
+       recordend=(p?p   :end);
        assert(catchdatal<=catchdatasiz);
 }
 
 static int retrycnt=0;
 static void retrying(void)
 {
-       if (maxretryn>=0 && ++retrycnt>maxretryn) error(_("!Maximum command retry count (%ld) exceeded"),maxretryn);
+       if (maxretryn>=0 && ++retrycnt>=maxretryn) error(_("!Maximum command retry count (%ld) exceeded"),maxretryn);
        if (verbose>=2) error(_(".Retrying phase, %d out of %ld.."),retrycnt,maxretryn);
 }
 
@@ -890,21 +939,27 @@ static size_t bufl;
 ssize_t got;
 char *hit,*s;
 va_list ap;
-char errout,extend,noconvcr,edata;
+char errout,extend,catch_any,edata;
 long alarmtime;
 const char *osend;
 static const char emptystring[]="";
+size_t discard;
 
        if (!term) term="\nOK\n";
        if (!strcmp(send," ")) send=NULL; /* GCC formatstring-check workaround */
+       if (verbose>=2) error(_(".devcmd(sendfmt=%s,term=%s,catch=%s)"),
+               reform(send,0),reform(term,1),reform(catch,2));
        if (!(osend=send)) send="";
-       if ((noconvcr=(catch && *catch=='@'))) catch++;
+       if ((catch_any=(catch && !strcmp(catch,"@")))) catch=NULL;
        if ((errout=(*send=='!'))) send++;
        errout|=(maxretryn==-1);
        if ((extend=(*send=='~'))) send++;
        alarmtime=readtimen*(extend?EXT_READTIME:1);
-       d8("devcmd(), alarmtime=%ld, errout=%d, extend=%d, noconvcr=%d, osend=%p, bufl=%d, buf: %s\n",
-               alarmtime,errout,extend,noconvcr,osend,bufl,reform(buf,0));
+       buf[bufl]='\0'; /* for d8() below */
+       d8("devcmd(), alarmtime=%ld, errout=%d, extend=%d, catch_any=%d, osend=%p, bufl=%d, buf: %s\n",
+               alarmtime,errout,extend,catch_any,osend,bufl,reform(buf,0));
+       assert(!catch || !strchr(catch,'\r')); /* we are no longer supporting 'noconvcr'! */
+       assert(!term  || !strchr(term ,'\r'));
        if (0) {
 err:
                alarm(0);
@@ -919,8 +974,7 @@ err:
                l=VARVPRINTF(buf,send,ap); bufl=l+(!!osend);
                va_end(ap);
                if (bufl>=sizeof(buf)-1) error(_("!Command too big (%d>%d)"),bufl,sizeof(buf)-1);
-               if (verbose>=2) error(_(".devcmd(send=%s,term=%s,catch=%s,timeout=%ld)"),
-                       reform(buf,0),reform(term,1),reform(catch,2),alarmtime);
+               if (verbose>=2) error(_(".devcmd formatted send=%s%s"),reform(buf,0),(osend?"+\"\\r\"":""));
                if (osend) buf[l]='\r';
                for (offs=0,got=0;offs<bufl;offs++) {
                        alarm(MAXSENDTIME);
@@ -940,7 +994,7 @@ err:
                }
 
        if (!(terml=strlen(term))) {
-               assert(!catch);
+               assert(!catch); assert(!catch_any);
                return(NULL);
                }
        if (catch) {
@@ -949,13 +1003,12 @@ err:
                }
        else fragl=terml;
        fragl=MAX(fragl,MAX(strlen(ERROR_SUBSTR1),strlen(ERROR_SUBSTR2)));
-       record=NULL;
+       record=recordend=NULL;
        wasalarm=0;
        alarm(alarmtime);
-       edata=(noconvcr?'\r':'\n');
+       edata='\n';
        if (!osend) {
-               got=bufl;
-               bufl=0;
+               /* "bufl" important */
                goto skipread;
                }
        for (;;) {
@@ -972,18 +1025,18 @@ err:
                        else error(_("^Couldn't read device data (ret=%d)"),got);
                        goto err;
                        }
-skipread:
                bufl2=bufl+got;
                buf[bufl2]='\0';
                s=buf+bufl;
                while (buf+bufl2>s && (s=memchr(s,'\0',buf+bufl2-s))) *s++=REPL_NULLCHAR;
                if (verbose>=3)
                        error(_("\nGot chunk of data from device: %s"),reform(buf+bufl,0));
-               if (!noconvcr) {
+               /* convert CR */ {
                        s=buf+bufl;
                        while (buf+bufl2>s && (s=memchr(s,'\r',buf+bufl2-s))) *s++='\n';
                        }
                bufl=bufl2;
+skipread:
                catched(buf+bufl,edata); assert(!record || record==buf+bufl);
                assert(bufl<sizeof(buf)-1);
                buf[bufl]='\0';
@@ -993,25 +1046,34 @@ skipread:
                        error(_("Found ERROR response on command %s"),reform(send,0));
                        goto err;
                        }
-               if (catch && !record && bufl>=catchl && (hit=strstr(buf,catch))) {
+/* "record" may get NULLed here after successful 'catch'
+ * but "recordend" will never be NULLed
+ */
+               if (catch && !recordend && bufl>=catchl && (hit=strstr(buf,catch))) {
                        record=hit+catchl;
                        catched(buf+bufl,edata); assert(!record || record==buf+bufl);
                        }
-               if (         bufl>= terml && (hit=strstr(buf,term))) {
+               if (catch_any && !recordend && buf[discard=strspn(buf,"\n" /* accept */)]) {
+                       record=buf+discard;
+                       catched(buf+bufl,edata); assert(!record || record==buf+bufl);
+                       }
+               if (((!catch && !catch_any) || catchdatal) && bufl>= terml
+                   && (hit=strstr((recordend?recordend:buf),term))) {
                        memmove(buf,hit+terml,(bufl2=(buf+bufl)-(hit+terml))); bufl=bufl2;
                        break;
                        }
                if (bufl<fragl) continue;
-               memmove(buf,buf+bufl-(fragl-1),(bufl2=fragl-1)); bufl=bufl2;
-               if (record) record=buf+fragl-1;
+               memmove(buf,buf+bufl-(fragl-1),(bufl2=fragl-1));
+               if (record   ) record-=bufl-bufl2;
+               if (recordend) recordend-=bufl-bufl2;
+               bufl=bufl2;
                }
        alarm(0);
        if (!catchdatal) {
-               if (!catch) return("");
-               error(_("Data requested on command %s but no found after term %s"),reform(send,0),reform(term,1));
-               goto err;
+               assert(!catch && !catch_any);
+               return("");
                }
-       assert(!!catch);
+       assert(!!catch || catch_any);
        record=emptystring;
        catched(record+1,edata);
        if (verbose>=2) error(_(".Returning data %s for cmd %s"),reform(catchdata,0),reform(send,1));
@@ -1061,7 +1123,6 @@ static inline void textconv(char *d,unsigned char *s,size_t len)
 static inline void smscset(void)
 {
 char *s,*t,*e,*serr;
-long l;
 unsigned char bin[2+(MAXNUMLEN+1)/2];
 
        if (smsc) devcmd(NULL,NULL,"\r\nAT+CSCA=\"%s\"",smsc);
@@ -1070,19 +1131,22 @@ unsigned char bin[2+(MAXNUMLEN+1)/2];
        if (!*s || !strcmp(s,"EMPTY"))
                error(_("!No SMSC set in mobile station found, please use option \"-s\""));
        if (verbose>=1) error(_("\nFound default SMSC in mobile: %s"),s);
-       if (*s!='"') error(_("!No left-quote found in: %s"),s);
-       if (!(t=strrchr(s+1,'"'))) error(_("!No right-quote found in: %s"),s);
-       if (s+1==t)
+       if (*s++!='"') error(_("!No left-quote found in: %s"),s);
+       if (!(t=strrchr(s,'"'))) error(_("!No right-quote found in: %s"),s);
+       if (s==t)
                error(_("!No SMS set in mobile station found, please use option \"-s\""));
        e=t++;
        while (isspace(*t)) t++;
-       if (*t++!=',') error(_("!No comma found after quotes in: %s"),s);
-       while (isspace(*t)) t++;
-       l=strtol(t,&serr,10);
-       if ((l!=ADDR_NAT && l!=ADDR_INT) || (serr && *serr))
-               error(_("!Type parse error in: %s"),s);
-       if (l==ADDR_NAT || s[1]=='+') s++;
-       else *s='+';
+       if (*t) {
+long l;
+
+               if (*t++!=',') error(_("!No comma found after quotes in: %s"),s);
+               while (isspace(*t)) t++;
+               l=strtol(t,&serr,10);
+               if ((l!=ADDR_NAT && l!=ADDR_INT) || (serr && *serr))
+                       error(_("!Type parse error in: %s"),s);
+               if (l==ADDR_INT && *s!='+') *--s='+';
+               }
        *e='\0';
        if (verbose>=2) error(_("\nDecoded SMSC address: %s"),s);
        if (!NEED_PDUSMSC()) return;
@@ -1509,6 +1573,7 @@ int i;
        tm.tm_mon--;
        d7("mktime(y%dm%dd%dh%dm%ds%d)\n",
                tm.tm_year,tm.tm_mon,tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec);
+       tm.tm_isdst=-1; /* "timezone" info not available */
        if ((receive_time=mktime(&tm))==-1)
                error(_("^mktime(3) failed for %s"),string);
 }
@@ -1623,10 +1688,10 @@ err:
        exit(EXIT_SUCCESS); /* cleanup() has been disabled */
 }
 
-static inline unsigned char fromhex(char c)
+static inline unsigned char fromhex(unsigned c)
 {
        c&=0xDF;
-       return(c<'A'?c-'0':(c-'A')+0xA);
+       return(c<'A'?c-('0'&0xDF):(c-('A'&0xDF))+0xA);
 }
 
 static int teldecode(char *text,unsigned char *bin,size_t digits)
@@ -1673,7 +1738,7 @@ int i;
 static void receive_pdu(char *pduline)
 {
 unsigned char pdu[140+0x100],*pdup,*pdue,oalen,inreg;
-char text[160+1],*textp,*s;
+char text[160+1],*textp,*s,*pdulinescan;
 size_t pdulinel=strlen(pduline),want;
 size_t udl,udlb;
 int inb,outb,xb;
@@ -1685,11 +1750,11 @@ int inb,outb,xb;
                { error(_("PDU length odd (%d): %s"),pdulinel,pduline); return; }
        if (pdulinel<2*13)
                { error(_("PDU length %d too small (min. 2*%d): %s"),pdulinel,13,pduline); return; }
-       for (pdup=pdu;*pduline;pduline+=2) {
-               if (!isxdigit(pduline[0]) || !(isxdigit(pduline[1])))
+       for (pdup=pdu,pdulinescan=pduline;*pdulinescan;pdulinescan+=2) {
+               if (!isxdigit(pdulinescan[0]) || !(isxdigit(pdulinescan[1])))
                { error(_("Invalid hex byte: %c%c on byte %d in: %s"),
-                       pduline[0],pduline[1],pdup-pdu,pduline); return; }
-               *pdup++=(fromhex(pduline[0])<<4)|fromhex(pduline[1]);
+                       pdulinescan[0],pdulinescan[1],pdup-pdu,pduline); return; }
+               *pdup++=(fromhex(pdulinescan[0])<<4)|fromhex(pdulinescan[1]);
                }
        pdue=pdup;
        free(receive_smsc);
@@ -1714,8 +1779,8 @@ int inb,outb,xb;
                error(_("Unrecognized PDU type 0x%02X at offset %d, dropping: %s"),*pdup,pdup-pdu,pduline);
        pdup++;
        free(receive_number);
-       if ((oalen=*pdup++)>10) /* OA len */
-               { error(_("Originating number too large (%d, max. %d): %s"),oalen,10,pduline); return; }
+       if ((oalen=*pdup++)>2*0x10) /* OA len */
+               { error(_("Originating number too large (0x%X, max. 2*0x%X): %s"),oalen,0x10,pduline); return; }
        if (pdup+(want=1+(oalen+1)/2+10)>pdue)
                { error(_("PDU length too short (want %d, is %d): %s"),(pdup-pdu)+want,pdue-pdu,pduline); return; }
        chk(receive_number=malloc(1+2*(*pdup)+1));
@@ -1759,8 +1824,7 @@ size_t udl1,udlb1;
                udl=udl1; udlb=udlb1;
                }
        else
-               error(_("Trailing garbage ignored in PDU data (UDL %d/7->%d/8, got %d) in: %s"),
-                       udl,udlb,pdue-pdup,pduline);
+               assert(pdup+udlb==pdue); /* should be checked by 'PDU length too short' above */
        textp=text;
        inb=outb=0;
        inreg=0; /* GCC happiness */
@@ -1778,7 +1842,7 @@ size_t udl1,udlb1;
 #if 0
                d4("inb=%d,outb=%d,xb=%d\n",inb,outb,xb);
 #endif
-               *textp|=((inreg>>(unsigned)(7-inb))&((1<<xb)-1))<<(unsigned)(8-outb);
+               *textp|=((inreg>>(unsigned)(8-inb))&((1<<xb)-1))<<(unsigned)(7-outb);
                inb-=xb; outb-=xb;
                if (!outb) {
                        *textp=charconv_recv(*textp,textp-text);
@@ -1942,6 +2006,25 @@ size_t l=strlen(device);
                }
        if (readbody)
                error(_("Warning: -f / --file is forbidden with mode \"%s\""),MODE_NAME(mode));
+       if (smsmode) {
+                    if (!strcmp(smsmode,"pdu" ) || !strcmp(smsmode,"0"))
+                       force_smsmode=FSM_PDU ;
+               else if (!strcmp(smsmode,"text") || !strcmp(smsmode,"1"))
+                       force_smsmode=FSM_TEXT;
+               else
+                       error(_("!Unrecognized %s argument \"%s\", supported only: %s"),"-M/--smsmode",smsmode,"pdu/0/text/1");
+               }
+       if (pdusmscmode) {
+                    if (!strcmp(pdusmscmode,"count-in"))
+                       force_pdusmscmode=FPSM_COUNT_IN;
+               else if (!strcmp(pdusmscmode,"count-out"))
+                       force_pdusmscmode=FPSM_COUNT_OUT;
+               else if (!strcmp(pdusmscmode,"none"))
+                       force_pdusmscmode=FPSM_NONE;
+               else
+                       error(_("!Unrecognized %s argument \"%s\", supported only: %s"),"-P/--pdusmscmode",pdusmscmode,"count-in/count-out/none");
+               try_pdusmscmode=force_pdusmscmode;
+               }
 
        switch (baudn) {
                case  2400: portbaud= B2400; break;
@@ -1974,9 +2057,9 @@ time_t start,end;
                restios.c_cflag=(restios.c_cflag&~(CBAUD|CBAUDEX))|B0|HUPCL;
                restios_yes=1;
                }
-       tios.c_iflag=IGNBRK|IGNPAR|IXON|IXOFF;
+       tios.c_iflag=IGNBRK|IGNPAR|(handshake_rtscts ? 0 : IXON|IXOFF);
        tios.c_oflag=0;
-       tios.c_cflag=CS8|CREAD|CLOCAL|HUPCL|portbaud;
+       tios.c_cflag=CS8|CREAD|CLOCAL|HUPCL|portbaud|(handshake_rtscts ? CRTSCTS : 0);
        tios.c_lflag=IEXTEN|NOFLSH;
        memset(tios.c_cc,_POSIX_VDISABLE,sizeof(tios.c_cc));
        tios.c_cc[VTIME]=0;
@@ -1998,8 +2081,9 @@ retryall:
                if (mode==MODE_SEND || mode==MODE_RECEIVE) {
                        cmgf=-1;
                        do {
-                               if (!devcmd(NULL,NULL,"!\r\nAT+CMGF=0")) {
-                                       if (!devcmd(NULL,NULL,"!\r\nAT+CMGF=1"))
+                               /* condition is _negative_ here: */
+                               if (force_smsmode==FSM_TEXT || !devcmd(NULL,NULL,"!\r\nAT+CMGF=0")) {
+                                       if (force_smsmode==FSM_PDU || !devcmd(NULL,NULL,"!\r\nAT+CMGF=1"))
                                                { retrying(); continue; }
                /* CMGF=1 */
                                        if (verbose>=1)
@@ -2021,8 +2105,18 @@ retryall:
                                        s=devcmd(NULL,"\n+CMGS:","!~%s\032",body);
                                        }
                                else {
-                                       devcmd("\n> ",NULL,"\r\nAT+CMGS=%d",(strlen(pdusmsc)+strlen(pdudata))/2);
-                                       s=devcmd(NULL,"\n+CMGS:","!~%s%s\032",pdusmsc,pdudata);
+                                       devcmd("\n> ",NULL,"\r\nAT+CMGS=%d",(
+                                                       (try_pdusmscmode==FPSM_COUNT_IN ? strlen(pdusmsc) : 0)
+                                                       +strlen(pdudata))/2);
+                                       s=devcmd(NULL,"\n+CMGS:","!~%s%s\032",
+                                                       (try_pdusmscmode!=FPSM_NONE ? pdusmsc : ""),
+                                                       pdudata);
+                                       if (!s && force_pdusmscmode==FPSM_AUTO) {
+                                               if (FPSM_MAX==try_pdusmscmode++)
+                                                       try_pdusmscmode=FPSM_MIN;
+                                               else
+                                                       retrycnt--;
+                                               }
                                        }
                                break;
                        case MODE_LOGO_SEND:
@@ -2063,18 +2157,16 @@ struct hexdata *hd;
                                d1("Lock-device succeeded\n");
                                do {
                                        d1("Reading a message for us...\n");
-                                       if (!(s=devcmd("\r","@+CMT:"," ")))
+                                       if (!(s=devcmd("\n","+CMT:"," ")))
                                                goto retryall;
                                        if (cmgf && !(i=receive_headerparse(s)))
                                                error(_("Receive-header parsing failed on: %s"),s);
-                                       if (!(s=devcmd("\r","@\n"," ")))
+                                       if (!(s=devcmd("\n","@"," ")))
                                                goto retryall;
                                        if (cmgf) {
                                                if (i) receive_text(s);
                                                }
                                        else receive_pdu(s);
-                                       if (!devcmd("\n",NULL," ")) /* eat last '\n' */
-                                               goto retryall;
                                        } while (datawait(1));
                                goto retryall;
                                break;